summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSynthesis AG <opensource@synthesis.ch>2009-04-17 23:33:46 +0200
committerLukas Zeller <luz@synthesis.ch>2009-04-17 23:33:46 +0200
commitaf04e8194a12588cd86d70d32a7df73f511ef7a1 (patch)
tree89936c6ec5861bc9f0b58b298bfff197339eff67
Initial Commit by Synthesis AG for Open Source libsynthesis 3.2.0.25 and libsmltkopensource-initial
-rw-r--r--.gitignore32
-rw-r--r--AUTHORS3
-rw-r--r--COPYING10
-rw-r--r--ChangeLog0
-rw-r--r--CodingStyle52
-rw-r--r--INSTALL237
-rw-r--r--LICENSE.BSD26
-rw-r--r--LICENSE.LGPL-2.1510
-rw-r--r--LICENSE.LGPL-3165
-rw-r--r--Makefile.am11
-rw-r--r--NEWS0
-rw-r--r--README35
-rw-r--r--Synthesis_AG_Contributor_Agreement.pdfbin0 -> 43362 bytes
-rwxr-xr-xautogen.sh11
-rw-r--r--configure.in67
-rw-r--r--doc/SDK_manual.docbin0 -> 607232 bytes
-rw-r--r--doc/SDK_manual.pdfbin0 -> 1186451 bytes
-rw-r--r--doc/SySync_config_reference.docbin0 -> 1649152 bytes
-rw-r--r--doc/SySync_config_reference.pdf7749
-rwxr-xr-xdoc/engine_settings_keys.rtf142
-rwxr-xr-xsrc/DB_interfaces/api_db/DLL_interface.cpp215
-rwxr-xr-xsrc/DB_interfaces/api_db/DLL_interface.h119
-rwxr-xr-xsrc/DB_interfaces/api_db/dbapi.cpp2203
-rwxr-xr-xsrc/DB_interfaces/api_db/dbapi.h556
-rwxr-xr-xsrc/DB_interfaces/api_db/dbapi_include.h382
-rwxr-xr-xsrc/DB_interfaces/api_db/pluginapiagent.cpp709
-rwxr-xr-xsrc/DB_interfaces/api_db/pluginapiagent.h183
-rwxr-xr-xsrc/DB_interfaces/api_db/pluginapids.cpp2757
-rwxr-xr-xsrc/DB_interfaces/api_db/pluginapids.h445
-rwxr-xr-xsrc/DB_interfaces/api_db/plugindb.h21
-rwxr-xr-xsrc/DB_interfaces/api_db/plugindb_precomp.h18
-rwxr-xr-xsrc/DB_interfaces/api_db/sync_dbapiconnect.cpp533
-rwxr-xr-xsrc/DB_interfaces/api_db/sync_dbapiconnect.h234
-rw-r--r--src/DB_interfaces/odbc_db/odbcapiagent.cpp3895
-rwxr-xr-xsrc/DB_interfaces/odbc_db/odbcapiagent.h603
-rwxr-xr-xsrc/DB_interfaces/odbc_db/odbcapids.cpp4455
-rwxr-xr-xsrc/DB_interfaces/odbc_db/odbcapids.h689
-rwxr-xr-xsrc/DB_interfaces/odbc_db/odbcdb.h34
-rwxr-xr-xsrc/DB_interfaces/odbc_db/odbcdb_precomp.h56
-rw-r--r--src/Makefile.am.in139
-rw-r--r--src/Targets/ReleasedProducts/SDK/target_options.h55
-rwxr-xr-xsrc/Targets/ReleasedProducts/clientEngine_DEMO_linux/clientengine_demo++.pch50
-rwxr-xr-xsrc/Targets/ReleasedProducts/clientEngine_DEMO_linux/clientengine_demo.pch43
-rw-r--r--src/Targets/ReleasedProducts/clientEngine_DEMO_linux/clientengine_demo_x86_linux_prefix.h23
-rwxr-xr-xsrc/Targets/ReleasedProducts/clientEngine_DEMO_linux/define.h119
-rwxr-xr-xsrc/Targets/ReleasedProducts/clientEngine_DEMO_linux/target_options.h196
-rw-r--r--src/Targets/ReleasedProducts/clientEngine_autotools/systemxml/xmlparse.h2
-rwxr-xr-xsrc/Targets/ReleasedProducts/clientEngine_opensource_linux/clientengine_demo++.pch53
-rwxr-xr-xsrc/Targets/ReleasedProducts/clientEngine_opensource_linux/clientengine_demo.pch46
-rw-r--r--src/Targets/ReleasedProducts/clientEngine_opensource_linux/clientengine_demo_x86_linux_prefix.h35
-rwxr-xr-xsrc/Targets/ReleasedProducts/clientEngine_opensource_linux/define.h119
-rwxr-xr-xsrc/Targets/ReleasedProducts/clientEngine_opensource_linux/target_options.h197
-rwxr-xr-xsrc/Targets/ReleasedProducts/sysytool_linux/define.h119
-rwxr-xr-xsrc/Targets/ReleasedProducts/sysytool_linux/sysytool_linux_x86_prefix.h25
-rwxr-xr-xsrc/Targets/ReleasedProducts/sysytool_linux/target_options.h177
-rwxr-xr-xsrc/Targets/clientEngine_dbg/clientengine_dbg++.pch41
-rwxr-xr-xsrc/Targets/clientEngine_dbg/clientengine_dbg.pch35
-rwxr-xr-xsrc/Targets/clientEngine_dbg/clientengine_dbg_x86++.mchbin0 -> 4220900 bytes
-rwxr-xr-xsrc/Targets/clientEngine_dbg/clientengine_dbg_x86.mchbin0 -> 75468 bytes
-rwxr-xr-xsrc/Targets/clientEngine_dbg/clientengine_dbg_x86_prefix.h14
-rwxr-xr-xsrc/Targets/clientEngine_dbg/target_options.h205
-rwxr-xr-xsrc/Targets/sysytool/define.h119
-rwxr-xr-xsrc/Targets/sysytool/sysytool++.pch20
-rwxr-xr-xsrc/Targets/sysytool/sysytool.pch20
-rwxr-xr-xsrc/Targets/sysytool/sysytool_linux_x86_prefix.h25
-rw-r--r--src/Targets/sysytool/sysytool_x86++.mchbin0 -> 4269404 bytes
-rw-r--r--src/Targets/sysytool/sysytool_x86.mchbin0 -> 151860 bytes
-rwxr-xr-xsrc/Targets/sysytool/sysytool_x86_prefix.h10
-rwxr-xr-xsrc/Targets/sysytool/target_options.h176
-rwxr-xr-xsrc/Targets/sysytool/version.rc46
-rwxr-xr-xsrc/Transport_interfaces/engine/engine_client.h28
-rwxr-xr-xsrc/Transport_interfaces/engine/engine_client_precomp.h35
-rwxr-xr-xsrc/Transport_interfaces/engine/engineclientbase.cpp138
-rwxr-xr-xsrc/Transport_interfaces/engine/engineclientbase.h77
-rw-r--r--src/client_engine_linux.mk233
-rwxr-xr-xsrc/expat/copying.txt22
-rwxr-xr-xsrc/expat/xmlparse/xmlparse.c3927
-rwxr-xr-xsrc/expat/xmlparse/xmlparse.h527
-rwxr-xr-xsrc/expat/xmltok/ascii.h86
-rwxr-xr-xsrc/expat/xmltok/asciitab.h37
-rwxr-xr-xsrc/expat/xmltok/iasciitab.h38
-rwxr-xr-xsrc/expat/xmltok/latin1tab.h37
-rwxr-xr-xsrc/expat/xmltok/nametab.h150
-rwxr-xr-xsrc/expat/xmltok/utf8tab.h38
-rwxr-xr-xsrc/expat/xmltok/xmldef.h52
-rwxr-xr-xsrc/expat/xmltok/xmlrole.c1265
-rwxr-xr-xsrc/expat/xmltok/xmlrole.h99
-rwxr-xr-xsrc/expat/xmltok/xmltok.c1558
-rwxr-xr-xsrc/expat/xmltok/xmltok.h301
-rwxr-xr-xsrc/expat/xmltok/xmltok_impl.c1765
-rwxr-xr-xsrc/expat/xmltok/xmltok_impl.h46
-rwxr-xr-xsrc/expat/xmltok/xmltok_ns.c96
-rwxr-xr-xsrc/gen-makefile-am.sh16
-rw-r--r--src/global_options.h248
-rwxr-xr-xsrc/platform_adapters/binfile.cpp147
-rwxr-xr-xsrc/platform_adapters/binfile.h55
-rwxr-xr-xsrc/platform_adapters/configfiles.h93
-rwxr-xr-xsrc/platform_adapters/linux/configfiles.cpp240
-rwxr-xr-xsrc/platform_adapters/linux/ctype2
-rwxr-xr-xsrc/platform_adapters/linux/platform_DLL.cpp142
-rwxr-xr-xsrc/platform_adapters/linux/platform_exec.c92
-rwxr-xr-xsrc/platform_adapters/linux/platform_exec.h29
-rwxr-xr-xsrc/platform_adapters/linux/platform_headers.h37
-rwxr-xr-xsrc/platform_adapters/linux/platform_time.cpp82
-rwxr-xr-xsrc/platform_adapters/linux/platform_time.h53
-rw-r--r--src/platform_adapters/linux/platform_timezones.cpp173
-rwxr-xr-xsrc/platform_adapters/linux/profiling.cpp32
-rwxr-xr-xsrc/platform_adapters/platform_DLL.h45
-rwxr-xr-xsrc/platform_adapters/platform_file.h49
-rwxr-xr-xsrc/platform_adapters/platform_headers.h30
-rwxr-xr-xsrc/platform_adapters/platform_mutex.h28
-rwxr-xr-xsrc/platform_adapters/platform_thread.h110
-rwxr-xr-xsrc/platform_adapters/profiling.h248
-rwxr-xr-xsrc/platform_adapters/sysync_glob_vars.h42
-rwxr-xr-xsrc/platform_adapters/sysyncinit.cpp79
-rwxr-xr-xsrc/platform_adapters/unix_common/platform_file.cpp138
-rwxr-xr-xsrc/platform_adapters/unix_common/platform_mutex.cpp28
-rwxr-xr-xsrc/platform_adapters/unix_common/platform_pipe.c91
-rwxr-xr-xsrc/platform_adapters/unix_common/platform_pipe.h38
-rwxr-xr-xsrc/platform_adapters/unix_common/platform_thread.cpp217
-rwxr-xr-xsrc/prefix_file.h11
-rw-r--r--src/smltk-linker.map6
-rwxr-xr-xsrc/syncapps/clientEngine_custom/clientengine_custom.h26
-rw-r--r--src/syncapps/clientEngine_custom/clientengine_custom_Base.cpp64
-rwxr-xr-xsrc/syncapps/clientEngine_custom/clientengine_custom_Base.h47
-rwxr-xr-xsrc/syncapps/clientEngine_custom/clientengine_custom_precomp.h26
-rwxr-xr-xsrc/syncapps/clientEngine_custom/product_options.h125
-rwxr-xr-xsrc/syncapps/sysytool/sysytool.cpp235
-rwxr-xr-xsrc/syncapps/sysytool/sysytool.h27
-rwxr-xr-xsrc/syncapps/sysytool/sysytool_dispatch.cpp70
-rwxr-xr-xsrc/syncapps/sysytool/sysytool_dispatch.h41
-rwxr-xr-xsrc/syncapps/sysytool/sysytool_precomp.h26
-rwxr-xr-xsrc/syncml_tk/Synthesis_RTK_history.txt159
-rw-r--r--src/syncml_tk/doc/ReadmeMaintenance4_1.docbin0 -> 99840 bytes
-rw-r--r--src/syncml_tk/doc/ReadmeMaintenance4_2.docbin0 -> 107008 bytes
-rw-r--r--src/syncml_tk/doc/ReadmeMaintenance4_3.docbin0 -> 113664 bytes
-rw-r--r--src/syncml_tk/doc/ReadmeMaintenance4_4.docbin0 -> 116224 bytes
-rw-r--r--src/syncml_tk/opensource_license.txt42
-rwxr-xr-xsrc/syncml_tk/src/sml/SyncML.def151
-rwxr-xr-xsrc/syncml_tk/src/sml/inc/palm/define.h102
-rwxr-xr-xsrc/syncml_tk/src/sml/inc/sml.h328
-rwxr-xr-xsrc/syncml_tk/src/sml/inc/smldef.h238
-rwxr-xr-xsrc/syncml_tk/src/sml/inc/smldevinfdtd.h194
-rwxr-xr-xsrc/syncml_tk/src/sml/inc/smldtd.h455
-rwxr-xr-xsrc/syncml_tk/src/sml/inc/smlerr.h132
-rwxr-xr-xsrc/syncml_tk/src/sml/inc/smlmetinfdtd.h101
-rwxr-xr-xsrc/syncml_tk/src/sml/inc/win/define.h171
-rwxr-xr-xsrc/syncml_tk/src/sml/lib/all/liblock.c75
-rwxr-xr-xsrc/syncml_tk/src/sml/lib/all/libmem.c187
-rwxr-xr-xsrc/syncml_tk/src/sml/lib/all/libstr.c136
-rwxr-xr-xsrc/syncml_tk/src/sml/lib/all/libutil.c177
-rwxr-xr-xsrc/syncml_tk/src/sml/lib/inc/liblock.h81
-rwxr-xr-xsrc/syncml_tk/src/sml/lib/inc/libmem.h98
-rwxr-xr-xsrc/syncml_tk/src/sml/lib/inc/libstr.h111
-rwxr-xr-xsrc/syncml_tk/src/sml/lib/inc/libutil.h87
-rwxr-xr-xsrc/syncml_tk/src/sml/mgr/all/mgr.c288
-rwxr-xr-xsrc/syncml_tk/src/sml/mgr/all/mgrcmdbuilder.c808
-rwxr-xr-xsrc/syncml_tk/src/sml/mgr/all/mgrcmddispatcher.c510
-rwxr-xr-xsrc/syncml_tk/src/sml/mgr/all/mgrinstancelist.c226
-rwxr-xr-xsrc/syncml_tk/src/sml/mgr/all/mgrinstancemgr.c1106
-rwxr-xr-xsrc/syncml_tk/src/sml/mgr/all/mgrutil.c1635
-rwxr-xr-xsrc/syncml_tk/src/sml/mgr/inc/mgr.h162
-rwxr-xr-xsrc/syncml_tk/src/sml/mgr/inc/mgrutil.h180
-rwxr-xr-xsrc/syncml_tk/src/sml/mgr/win/libinit.c95
-rwxr-xr-xsrc/syncml_tk/src/sml/wsm/all/wsm.c1246
-rwxr-xr-xsrc/syncml_tk/src/sml/wsm/inc/wsm.h118
-rwxr-xr-xsrc/syncml_tk/src/sml/wsm/inc/wsm_sm.h103
-rwxr-xr-xsrc/syncml_tk/src/sml/wsm/palm/wsm_sm.c206
-rwxr-xr-xsrc/syncml_tk/src/sml/wsm/win/wsm_sm.c481
-rwxr-xr-xsrc/syncml_tk/src/sml/xlt/all/xltdec.c2635
-rwxr-xr-xsrc/syncml_tk/src/sml/xlt/all/xltdeccom.h214
-rwxr-xr-xsrc/syncml_tk/src/sml/xlt/all/xltdecwbxml.c1053
-rwxr-xr-xsrc/syncml_tk/src/sml/xlt/all/xltdecwbxml.h60
-rwxr-xr-xsrc/syncml_tk/src/sml/xlt/all/xltdecxml.c1234
-rwxr-xr-xsrc/syncml_tk/src/sml/xlt/all/xltdevinf.c1355
-rwxr-xr-xsrc/syncml_tk/src/sml/xlt/all/xltdevinf.h75
-rwxr-xr-xsrc/syncml_tk/src/sml/xlt/all/xltenc.c1565
-rwxr-xr-xsrc/syncml_tk/src/sml/xlt/all/xltenccom.c104
-rwxr-xr-xsrc/syncml_tk/src/sml/xlt/all/xltenccom.h120
-rwxr-xr-xsrc/syncml_tk/src/sml/xlt/all/xltencwbxml.c299
-rwxr-xr-xsrc/syncml_tk/src/sml/xlt/all/xltencwbxml.h112
-rwxr-xr-xsrc/syncml_tk/src/sml/xlt/all/xltencxml.c171
-rwxr-xr-xsrc/syncml_tk/src/sml/xlt/all/xltencxml.h86
-rwxr-xr-xsrc/syncml_tk/src/sml/xlt/all/xltmetinf.c362
-rwxr-xr-xsrc/syncml_tk/src/sml/xlt/all/xltmetinf.h64
-rwxr-xr-xsrc/syncml_tk/src/sml/xlt/all/xlttags.c697
-rwxr-xr-xsrc/syncml_tk/src/sml/xlt/all/xlttags.h232
-rwxr-xr-xsrc/syncml_tk/src/sml/xlt/all/xlttagtbl.h118
-rwxr-xr-xsrc/syncml_tk/src/sml/xlt/all/xltutilstack.c206
-rwxr-xr-xsrc/syncml_tk/src/sml/xlt/all/xltutilstack.h139
-rwxr-xr-xsrc/syncml_tk/src/sml/xlt/inc/xltdec.h227
-rwxr-xr-xsrc/syncml_tk/src/sml/xlt/inc/xltenc.h92
-rwxr-xr-xsrc/syncml_tk_prefix_file.h9
-rw-r--r--src/synthesis-linker.map11
-rwxr-xr-xsrc/sysync/binfilebase.cpp333
-rwxr-xr-xsrc/sysync/binfilebase.h146
-rwxr-xr-xsrc/sysync/binfileimplclient.cpp2772
-rwxr-xr-xsrc/sysync/binfileimplclient.h587
-rwxr-xr-xsrc/sysync/binfileimplds.cpp2081
-rwxr-xr-xsrc/sysync/binfileimplds.h935
-rwxr-xr-xsrc/sysync/clientautosync_inc.cpp1947
-rwxr-xr-xsrc/sysync/clientautosync_inc.h301
-rwxr-xr-xsrc/sysync/clientprovisioning_inc.cpp377
-rwxr-xr-xsrc/sysync/clientprovisioning_inc.h14
-rwxr-xr-xsrc/sysync/configelement.cpp1024
-rwxr-xr-xsrc/sysync/configelement.h285
-rwxr-xr-xsrc/sysync/cp936_tables_inc.cpp5568
-rwxr-xr-xsrc/sysync/customimplagent.cpp869
-rwxr-xr-xsrc/sysync/customimplagent.h229
-rwxr-xr-xsrc/sysync/customimplds.cpp3272
-rwxr-xr-xsrc/sysync/customimplds.h768
-rw-r--r--src/sysync/dataconversion.cpp148
-rwxr-xr-xsrc/sysync/dataobjtype.cpp972
-rwxr-xr-xsrc/sysync/dataobjtype.h183
-rwxr-xr-xsrc/sysync/debuglogger.cpp1519
-rwxr-xr-xsrc/sysync/debuglogger.h381
-rwxr-xr-xsrc/sysync/engineentry.cpp334
-rwxr-xr-xsrc/sysync/engineentry.h117
-rw-r--r--src/sysync/engineinterface.cpp1719
-rwxr-xr-xsrc/sysync/engineinterface.h791
-rwxr-xr-xsrc/sysync/gb2312_tables_inc.cpp2777
-rwxr-xr-xsrc/sysync/global_progress.h83
-rwxr-xr-xsrc/sysync/iso8601.cpp383
-rwxr-xr-xsrc/sysync/iso8601.h67
-rwxr-xr-xsrc/sysync/itemfield.cpp2180
-rwxr-xr-xsrc/sysync/itemfield.h679
-rwxr-xr-xsrc/sysync/lineartime.cpp403
-rwxr-xr-xsrc/sysync/lineartime.h195
-rw-r--r--src/sysync/localengineds.cpp6358
-rwxr-xr-xsrc/sysync/localengineds.h1125
-rwxr-xr-xsrc/sysync/mimediritemtype.cpp395
-rwxr-xr-xsrc/sysync/mimediritemtype.h134
-rw-r--r--src/sysync/mimedirprofile.cpp5120
-rwxr-xr-xsrc/sysync/mimedirprofile.h694
-rwxr-xr-xsrc/sysync/multifielditem.cpp1645
-rwxr-xr-xsrc/sysync/multifielditem.h371
-rwxr-xr-xsrc/sysync/multifielditemtype.cpp1007
-rwxr-xr-xsrc/sysync/multifielditemtype.h219
-rwxr-xr-xsrc/sysync/remotedatastore.cpp265
-rwxr-xr-xsrc/sysync/remotedatastore.h83
-rwxr-xr-xsrc/sysync/rrules.cpp2364
-rwxr-xr-xsrc/sysync/rrules.h247
-rwxr-xr-xsrc/sysync/san.cpp691
-rw-r--r--src/sysync/san.h256
-rwxr-xr-xsrc/sysync/scriptcontext.cpp4624
-rwxr-xr-xsrc/sysync/scriptcontext.h483
-rwxr-xr-xsrc/sysync/simpleitem.cpp102
-rwxr-xr-xsrc/sysync/simpleitem.h75
-rwxr-xr-xsrc/sysync/smltk_precomp.h40
-rwxr-xr-xsrc/sysync/smltk_precomp_xpt.h17
-rwxr-xr-xsrc/sysync/stdlogicagent.cpp84
-rwxr-xr-xsrc/sysync/stdlogicagent.h70
-rwxr-xr-xsrc/sysync/stdlogicds.cpp1452
-rwxr-xr-xsrc/sysync/stdlogicds.h357
-rwxr-xr-xsrc/sysync/stringutils.cpp856
-rwxr-xr-xsrc/sysync/stringutils.h160
-rwxr-xr-xsrc/sysync/superdatastore.cpp1244
-rwxr-xr-xsrc/sysync/superdatastore.h322
-rwxr-xr-xsrc/sysync/syncappbase.cpp3545
-rwxr-xr-xsrc/sysync/syncappbase.h654
-rwxr-xr-xsrc/sysync/syncclient.cpp1805
-rwxr-xr-xsrc/sysync/syncclient.h367
-rwxr-xr-xsrc/sysync/syncclientbase.cpp490
-rw-r--r--src/sysync/syncclientbase.h190
-rwxr-xr-xsrc/sysync/synccommand.cpp4307
-rwxr-xr-xsrc/sysync/synccommand.h805
-rwxr-xr-xsrc/sysync/syncdatastore.cpp333
-rwxr-xr-xsrc/sysync/syncdatastore.h145
-rwxr-xr-xsrc/sysync/syncexception.cpp72
-rwxr-xr-xsrc/sysync/syncexception.h90
-rwxr-xr-xsrc/sysync/syncitem.cpp98
-rwxr-xr-xsrc/sysync/syncitem.h163
-rwxr-xr-xsrc/sysync/syncitemtype.cpp907
-rwxr-xr-xsrc/sysync/syncitemtype.h262
-rwxr-xr-xsrc/sysync/syncml_globs.h77
-rwxr-xr-xsrc/sysync/syncml_tk.h9
-rwxr-xr-xsrc/sysync/syncserver.cpp1440
-rwxr-xr-xsrc/sysync/syncserver.h252
-rw-r--r--src/sysync/syncsession.cpp5592
-rwxr-xr-xsrc/sysync/syncsession.h973
-rwxr-xr-xsrc/sysync/syncsessiondispatch.cpp888
-rwxr-xr-xsrc/sysync/syncsessiondispatch.h153
-rw-r--r--src/sysync/syserial.h221
-rwxr-xr-xsrc/sysync/sysync.h91
-rwxr-xr-xsrc/sysync/sysync_b64.cpp321
-rwxr-xr-xsrc/sysync/sysync_b64.h26
-rwxr-xr-xsrc/sysync/sysync_crc16.cpp146
-rwxr-xr-xsrc/sysync/sysync_crc16.h23
-rwxr-xr-xsrc/sysync/sysync_debug.h321
-rwxr-xr-xsrc/sysync/sysync_globs.h478
-rwxr-xr-xsrc/sysync/sysync_md5.cpp400
-rwxr-xr-xsrc/sysync/sysync_md5.h78
-rwxr-xr-xsrc/sysync/sysync_precomp.h63
-rwxr-xr-xsrc/sysync/sysync_precomp_xpt.h18
-rwxr-xr-xsrc/sysync/sysync_utils.cpp2928
-rwxr-xr-xsrc/sysync/sysync_utils.h618
-rwxr-xr-xsrc/sysync/textitemtype.cpp325
-rwxr-xr-xsrc/sysync/textitemtype.h115
-rwxr-xr-xsrc/sysync/textprofile.cpp1681
-rwxr-xr-xsrc/sysync/textprofile.h197
-rwxr-xr-xsrc/sysync/timezones.cpp1381
-rwxr-xr-xsrc/sysync/timezones.h464
-rwxr-xr-xsrc/sysync/tz_table.h573
-rwxr-xr-xsrc/sysync/uiapi.cpp131
-rw-r--r--src/sysync/uiapi.h70
-rwxr-xr-xsrc/sysync/vcalendaritemtype.cpp263
-rwxr-xr-xsrc/sysync/vcalendaritemtype.h122
-rwxr-xr-xsrc/sysync/vcarditemtype.cpp288
-rwxr-xr-xsrc/sysync/vcarditemtype.h124
-rw-r--r--src/sysync/vtimezone.cpp838
-rwxr-xr-xsrc/sysync/vtimezone.h92
-rw-r--r--src/sysync_SDK/DB_Interfaces/demo/DLL/target_options.h44
-rw-r--r--src/sysync_SDK/DB_Interfaces/demo/sync_dbapi_demo.c941
-rw-r--r--src/sysync_SDK/DB_Interfaces/snowwhite/DLL/target_options.h53
-rw-r--r--src/sysync_SDK/DB_Interfaces/snowwhite/myadapter.h28
-rw-r--r--src/sysync_SDK/DB_Interfaces/snowwhite/oceanblue.cpp757
-rw-r--r--src/sysync_SDK/DB_Interfaces/snowwhite/oceanblue.h268
-rw-r--r--src/sysync_SDK/DB_Interfaces/snowwhite/snowwhite.cpp337
-rw-r--r--src/sysync_SDK/DB_Interfaces/snowwhite/snowwhite.h90
-rw-r--r--src/sysync_SDK/DB_Interfaces/text_db/DLL/target_options.h46
-rw-r--r--src/sysync_SDK/DB_Interfaces/text_db/sync_dbapi_text.cpp1347
-rwxr-xr-xsrc/sysync_SDK/Sources/SDK_support.cpp967
-rwxr-xr-xsrc/sysync_SDK/Sources/SDK_support.h238
-rwxr-xr-xsrc/sysync_SDK/Sources/SDK_util.c781
-rwxr-xr-xsrc/sysync_SDK/Sources/SDK_util.h197
-rw-r--r--src/sysync_SDK/Sources/UI_util.cpp249
-rw-r--r--src/sysync_SDK/Sources/UI_util.h48
-rw-r--r--src/sysync_SDK/Sources/admindata.cpp217
-rw-r--r--src/sysync_SDK/Sources/admindata.h73
-rw-r--r--src/sysync_SDK/Sources/blobs.cpp343
-rw-r--r--src/sysync_SDK/Sources/blobs.h108
-rw-r--r--src/sysync_SDK/Sources/dataconversion.h29
-rw-r--r--src/sysync_SDK/Sources/dbitem.cpp1003
-rw-r--r--src/sysync_SDK/Sources/dbitem.h214
-rw-r--r--src/sysync_SDK/Sources/engine_defs.h415
-rwxr-xr-xsrc/sysync_SDK/Sources/enginemodulebase.cpp191
-rwxr-xr-xsrc/sysync_SDK/Sources/enginemodulebase.h379
-rw-r--r--src/sysync_SDK/Sources/enginemodulebridge.cpp368
-rw-r--r--src/sysync_SDK/Sources/enginemodulebridge.h110
-rwxr-xr-xsrc/sysync_SDK/Sources/generic_types.h139
-rw-r--r--src/sysync_SDK/Sources/prefix_file.h12
-rw-r--r--src/sysync_SDK/Sources/stringutil.cpp244
-rw-r--r--src/sysync_SDK/Sources/stringutil.h51
-rw-r--r--src/sysync_SDK/Sources/syerror.h181
-rwxr-xr-xsrc/sysync_SDK/Sources/sync_dbapi.h1118
-rwxr-xr-xsrc/sysync_SDK/Sources/sync_dbapidef.h649
-rw-r--r--src/sysync_SDK/Sources/sync_declarations.h56
-rw-r--r--src/sysync_SDK/Sources/sync_include.h93
-rwxr-xr-xsrc/sysync_SDK/Sources/sync_uiapi.h94
-rw-r--r--src/sysync_SDK/Sources/timeutil.cpp157
-rw-r--r--src/sysync_SDK/Sources/timeutil.h52
-rwxr-xr-xsrc/sysync_SDK/configs/sunbird_client.xml821
-rw-r--r--src/sysync_SDK/configs/syncclient_sample_config.xml1399
-rwxr-xr-xsrc/zlib/ChangeLog722
-rwxr-xr-xsrc/zlib/FAQ315
-rwxr-xr-xsrc/zlib/INDEX48
-rwxr-xr-xsrc/zlib/README126
-rwxr-xr-xsrc/zlib/adler32.c75
-rwxr-xr-xsrc/zlib/algorithm.txt209
-rwxr-xr-xsrc/zlib/compress.c79
-rwxr-xr-xsrc/zlib/crc32.c311
-rwxr-xr-xsrc/zlib/crc32.h441
-rwxr-xr-xsrc/zlib/deflate.c1502
-rwxr-xr-xsrc/zlib/deflate.h326
-rwxr-xr-xsrc/zlib/example.c567
-rwxr-xr-xsrc/zlib/gzio.c1005
-rwxr-xr-xsrc/zlib/infback.c619
-rwxr-xr-xsrc/zlib/inffast.c305
-rwxr-xr-xsrc/zlib/inffast.h11
-rwxr-xr-xsrc/zlib/inffixed.h94
-rwxr-xr-xsrc/zlib/inflate.c1270
-rwxr-xr-xsrc/zlib/inflate.h117
-rwxr-xr-xsrc/zlib/inftrees.c321
-rwxr-xr-xsrc/zlib/inftrees.h55
-rwxr-xr-xsrc/zlib/minigzip.c322
-rwxr-xr-xsrc/zlib/trees.c1215
-rwxr-xr-xsrc/zlib/trees.h128
-rwxr-xr-xsrc/zlib/uncompr.c61
-rwxr-xr-xsrc/zlib/zconf.h329
-rwxr-xr-xsrc/zlib/zconf.in.h323
-rwxr-xr-xsrc/zlib/zlib.3159
-rwxr-xr-xsrc/zlib/zlib.h1200
-rwxr-xr-xsrc/zlib/zutil.c319
-rwxr-xr-xsrc/zlib/zutil.h258
384 files changed, 193589 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..89d6341
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,32 @@
+# ignore generated files
+*.o
+src/sysync_SDK/bin
+src/Makefile.am
+Makefile.in
+configure
+m4
+aclocal.m4
+autom4te.cache
+config.guess
+config.log
+config.status
+config.sub
+config.h.in
+depcomp
+install-sh
+libtool
+ltmain.sh
+missing
+compile
+Makefile
+# ignore Mac OS X finder metadata files
+.DS_Store
+# ignore Mac OS X ressource dir created by zip
+__MACOSX/
+# ignore all sorts of backup files
+*.orig
+*~
+*.BAK
+*.bak
+# no CVS
+CVS/
diff --git a/AUTHORS b/AUTHORS
new file mode 100644
index 0000000..fd7430b
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1,3 @@
+Lukas Zeller <luz@synthesis.ch>: core engine
+Beat Forster <bfo@synthesis.ch>: SDK, database adapters, time zone handling
+Patrick Ohly <patrick.ohly@intel.com>: autotools compilation
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..a942100
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,10 @@
+Unless noted otherwise, all files are dual-licensed LGPL v2.1
+(LICENSE.LGPL-2.1) and 3.0 (LICENSE.LGPL-3). For a commercial
+license, please contact Synthesis (sales@synthesis.ch).
+
+Files in the following directories are under a different license:
+
+src/expat : public domain (src/expat/copying.txt)
+doc, src/sysync_SDK : BSD (LICENSE.BSD)
+src/syncml_tk : BSD-like license (src/syncml_tk/opensource_license.txt)
+src/zlib : BSD-like (src/zlib/README)
diff --git a/ChangeLog b/ChangeLog
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/ChangeLog
diff --git a/CodingStyle b/CodingStyle
new file mode 100644
index 0000000..3cda812
--- /dev/null
+++ b/CodingStyle
@@ -0,0 +1,52 @@
+The coding style varies a bit with author and age of the code. When
+changing existing code emulate its style. If you disagree with a
+certain style, format code the way you are more comfortable with.
+These are all merely guidelines - use common sense.
+
+Here are some general guidelines for new code:
+ * Similar to GNU formatting.
+ * Indention is two spaces, no tabs.
+ * Avoid curly brackets after if/while/do for single statements
+ (as in the Linux kernel) but only if the statement needs
+ no comment lines.
+ * One space right and left of assignment operators,
+ none around normal operators (such as in "a = b+c;").
+ Other formatting is also common. Use extra spaces if it helps
+ readability in complex expressions.
+ * Usually all capital with underscores for global constants.
+ Sometimes also with lower case.
+ * Mixed case with first upper case character for methods
+ and functions. Inside the engine lower case beginning is more
+ common.
+ * Lower case for variables, if necessary upper case characters
+ in the middle to separate words.
+ * Prefix and suffix characters for entity names are used:
+ prefix a: function and method argument, may also be
+ replaced with other letters to denote special
+ context (e.g., m = module, s = session)
+ prefix c: constant
+ prefix f: class or struct data member ("field")
+ prefix T: structured type or enum
+ suffix P[P]: pointer [to pointer]
+ suffix H: opaque handle
+
+The following .emacs snippet can be used to configure Emacs accordingly
+when editing files in a "/synthesis/" directory:
+
+(add-hook 'c-mode-hook
+ (lambda ()
+ (let ((filename (buffer-file-name)))
+ (when (and filename
+ (string-match "/synthesis/" filename))
+ (setq c-basic-offset 2)
+ )
+ )))
+
+(add-hook 'c++-mode-hook
+ (lambda ()
+ (let ((filename (buffer-file-name)))
+ (when (and filename
+ (string-match "/synthesis/" filename))
+ (setq c-basic-offset 2)
+ )
+ )))
diff --git a/INSTALL b/INSTALL
new file mode 100644
index 0000000..d3c5b40
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1,237 @@
+Installation Instructions
+*************************
+
+Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005,
+2006, 2007 Free Software Foundation, Inc.
+
+This file is free documentation; the Free Software Foundation gives
+unlimited permission to copy, distribute and modify it.
+
+Basic Installation
+==================
+
+Briefly, the shell commands `./configure; make; make install' should
+configure, build, and install this package. The following
+more-detailed instructions are generic; see the `README' file for
+instructions specific to this package.
+
+ The `configure' shell script attempts to guess correct values for
+various system-dependent variables used during compilation. It uses
+those values to create a `Makefile' in each directory of the package.
+It may also create one or more `.h' files containing system-dependent
+definitions. Finally, it creates a shell script `config.status' that
+you can run in the future to recreate the current configuration, and a
+file `config.log' containing compiler output (useful mainly for
+debugging `configure').
+
+ It can also use an optional file (typically called `config.cache'
+and enabled with `--cache-file=config.cache' or simply `-C') that saves
+the results of its tests to speed up reconfiguring. Caching is
+disabled by default to prevent problems with accidental use of stale
+cache files.
+
+ If you need to do unusual things to compile the package, please try
+to figure out how `configure' could check whether to do them, and mail
+diffs or instructions to the address given in the `README' so they can
+be considered for the next release. If you are using the cache, and at
+some point `config.cache' contains results you don't want to keep, you
+may remove or edit it.
+
+ The file `configure.ac' (or `configure.in') is used to create
+`configure' by a program called `autoconf'. You need `configure.ac' if
+you want to change it or regenerate `configure' using a newer version
+of `autoconf'.
+
+The simplest way to compile this package is:
+
+ 1. `cd' to the directory containing the package's source code and type
+ `./configure' to configure the package for your system.
+
+ Running `configure' might take a while. While running, it prints
+ some messages telling which features it is checking for.
+
+ 2. Type `make' to compile the package.
+
+ 3. Optionally, type `make check' to run any self-tests that come with
+ the package.
+
+ 4. Type `make install' to install the programs and any data files and
+ documentation.
+
+ 5. You can remove the program binaries and object files from the
+ source code directory by typing `make clean'. To also remove the
+ files that `configure' created (so you can compile the package for
+ a different kind of computer), type `make distclean'. There is
+ also a `make maintainer-clean' target, but that is intended mainly
+ for the package's developers. If you use it, you may have to get
+ all sorts of other programs in order to regenerate files that came
+ with the distribution.
+
+ 6. Often, you can also type `make uninstall' to remove the installed
+ files again.
+
+Compilers and Options
+=====================
+
+Some systems require unusual options for compilation or linking that the
+`configure' script does not know about. Run `./configure --help' for
+details on some of the pertinent environment variables.
+
+ You can give `configure' initial values for configuration parameters
+by setting variables in the command line or in the environment. Here
+is an example:
+
+ ./configure CC=c99 CFLAGS=-g LIBS=-lposix
+
+ *Note Defining Variables::, for more details.
+
+Compiling For Multiple Architectures
+====================================
+
+You can compile the package for more than one kind of computer at the
+same time, by placing the object files for each architecture in their
+own directory. To do this, you can use GNU `make'. `cd' to the
+directory where you want the object files and executables to go and run
+the `configure' script. `configure' automatically checks for the
+source code in the directory that `configure' is in and in `..'.
+
+ With a non-GNU `make', it is safer to compile the package for one
+architecture at a time in the source code directory. After you have
+installed the package for one architecture, use `make distclean' before
+reconfiguring for another architecture.
+
+Installation Names
+==================
+
+By default, `make install' installs the package's commands under
+`/usr/local/bin', include files under `/usr/local/include', etc. You
+can specify an installation prefix other than `/usr/local' by giving
+`configure' the option `--prefix=PREFIX'.
+
+ You can specify separate installation prefixes for
+architecture-specific files and architecture-independent files. If you
+pass the option `--exec-prefix=PREFIX' to `configure', the package uses
+PREFIX as the prefix for installing programs and libraries.
+Documentation and other data files still use the regular prefix.
+
+ In addition, if you use an unusual directory layout you can give
+options like `--bindir=DIR' to specify different values for particular
+kinds of files. Run `configure --help' for a list of the directories
+you can set and what kinds of files go in them.
+
+ If the package supports it, you can cause programs to be installed
+with an extra prefix or suffix on their names by giving `configure' the
+option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
+
+Optional Features
+=================
+
+Some packages pay attention to `--enable-FEATURE' options to
+`configure', where FEATURE indicates an optional part of the package.
+They may also pay attention to `--with-PACKAGE' options, where PACKAGE
+is something like `gnu-as' or `x' (for the X Window System). The
+`README' should mention any `--enable-' and `--with-' options that the
+package recognizes.
+
+ For packages that use the X Window System, `configure' can usually
+find the X include and library files automatically, but if it doesn't,
+you can use the `configure' options `--x-includes=DIR' and
+`--x-libraries=DIR' to specify their locations.
+
+Specifying the System Type
+==========================
+
+There may be some features `configure' cannot figure out automatically,
+but needs to determine by the type of machine the package will run on.
+Usually, assuming the package is built to be run on the _same_
+architectures, `configure' can figure that out, but if it prints a
+message saying it cannot guess the machine type, give it the
+`--build=TYPE' option. TYPE can either be a short name for the system
+type, such as `sun4', or a canonical name which has the form:
+
+ CPU-COMPANY-SYSTEM
+
+where SYSTEM can have one of these forms:
+
+ OS KERNEL-OS
+
+ See the file `config.sub' for the possible values of each field. If
+`config.sub' isn't included in this package, then this package doesn't
+need to know the machine type.
+
+ If you are _building_ compiler tools for cross-compiling, you should
+use the option `--target=TYPE' to select the type of system they will
+produce code for.
+
+ If you want to _use_ a cross compiler, that generates code for a
+platform different from the build platform, you should specify the
+"host" platform (i.e., that on which the generated programs will
+eventually be run) with `--host=TYPE'.
+
+Sharing Defaults
+================
+
+If you want to set default values for `configure' scripts to share, you
+can create a site shell script called `config.site' that gives default
+values for variables like `CC', `cache_file', and `prefix'.
+`configure' looks for `PREFIX/share/config.site' if it exists, then
+`PREFIX/etc/config.site' if it exists. Or, you can set the
+`CONFIG_SITE' environment variable to the location of the site script.
+A warning: not all `configure' scripts look for a site script.
+
+Defining Variables
+==================
+
+Variables not defined in a site shell script can be set in the
+environment passed to `configure'. However, some packages may run
+configure again during the build, and the customized values of these
+variables may be lost. In order to avoid this problem, you should set
+them in the `configure' command line, using `VAR=value'. For example:
+
+ ./configure CC=/usr/local2/bin/gcc
+
+causes the specified `gcc' to be used as the C compiler (unless it is
+overridden in the site shell script).
+
+Unfortunately, this technique does not work for `CONFIG_SHELL' due to
+an Autoconf bug. Until the bug is fixed you can use this workaround:
+
+ CONFIG_SHELL=/bin/bash /bin/bash ./configure CONFIG_SHELL=/bin/bash
+
+`configure' Invocation
+======================
+
+`configure' recognizes the following options to control how it operates.
+
+`--help'
+`-h'
+ Print a summary of the options to `configure', and exit.
+
+`--version'
+`-V'
+ Print the version of Autoconf used to generate the `configure'
+ script, and exit.
+
+`--cache-file=FILE'
+ Enable the cache: use and save the results of the tests in FILE,
+ traditionally `config.cache'. FILE defaults to `/dev/null' to
+ disable caching.
+
+`--config-cache'
+`-C'
+ Alias for `--cache-file=config.cache'.
+
+`--quiet'
+`--silent'
+`-q'
+ Do not print messages saying which checks are being made. To
+ suppress all normal output, redirect it to `/dev/null' (any error
+ messages will still be shown).
+
+`--srcdir=DIR'
+ Look for the package's source code in directory DIR. Usually
+ `configure' can determine that directory automatically.
+
+`configure' also accepts some other, not widely useful, options. Run
+`configure --help' for more details.
+
diff --git a/LICENSE.BSD b/LICENSE.BSD
new file mode 100644
index 0000000..c7a0aa4
--- /dev/null
+++ b/LICENSE.BSD
@@ -0,0 +1,26 @@
+Copyright (c) The Regents of the University of California.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+3. Neither the name of the University nor the names of its contributors
+ may be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
diff --git a/LICENSE.LGPL-2.1 b/LICENSE.LGPL-2.1
new file mode 100644
index 0000000..2d2d780
--- /dev/null
+++ b/LICENSE.LGPL-2.1
@@ -0,0 +1,510 @@
+
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+ 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL. It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+ This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it. You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations
+below.
+
+ When we speak of free software, we are referring to freedom of use,
+not price. Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+ To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights. These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+ For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you. You must make sure that they, too, receive or can get the source
+code. If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it. And you must show them these terms so they know their rights.
+
+ We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+ To protect each distributor, we want to make it very clear that
+there is no warranty for the free library. Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+
+ Finally, software patents pose a constant threat to the existence of
+any free program. We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder. Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+ Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License. This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License. We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+ When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library. The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom. The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+ We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License. It also provides other free software developers Less
+of an advantage over competing non-free programs. These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries. However, the Lesser license provides advantages in certain
+special circumstances.
+
+ For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it
+becomes a de-facto standard. To achieve this, non-free programs must
+be allowed to use the library. A more frequent case is that a free
+library does the same job as widely used non-free libraries. In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+ In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software. For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+ Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+ The precise terms and conditions for copying, distribution and
+modification follow. Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library". The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+
+ GNU LESSER GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+ A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+ The "Library", below, refers to any such software library or work
+which has been distributed under these terms. A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language. (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+ "Source code" for a work means the preferred form of the work for
+making modifications to it. For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control
+compilation and installation of the library.
+
+ Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it). Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+ 1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+ You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+ 2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) The modified work must itself be a software library.
+
+ b) You must cause the files modified to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ c) You must cause the whole of the work to be licensed at no
+ charge to all third parties under the terms of this License.
+
+ d) If a facility in the modified Library refers to a function or a
+ table of data to be supplied by an application program that uses
+ the facility, other than as an argument passed when the facility
+ is invoked, then you must make a good faith effort to ensure that,
+ in the event an application does not supply such function or
+ table, the facility still operates, and performs whatever part of
+ its purpose remains meaningful.
+
+ (For example, a function in a library to compute square roots has
+ a purpose that is entirely well-defined independent of the
+ application. Therefore, Subsection 2d requires that any
+ application-supplied function or table used by this function must
+ be optional: if the application does not supply it, the square
+ root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library. To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License. (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.) Do not make any other change in
+these notices.
+
+ Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+ This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+ 4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+ If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library". Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+ However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library". The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+ When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library. The
+threshold for this to be true is not precisely defined by law.
+
+ If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work. (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+ Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+ 6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+ You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License. You must supply a copy of this License. If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License. Also, you must do one
+of these things:
+
+ a) Accompany the work with the complete corresponding
+ machine-readable source code for the Library including whatever
+ changes were used in the work (which must be distributed under
+ Sections 1 and 2 above); and, if the work is an executable linked
+ with the Library, with the complete machine-readable "work that
+ uses the Library", as object code and/or source code, so that the
+ user can modify the Library and then relink to produce a modified
+ executable containing the modified Library. (It is understood
+ that the user who changes the contents of definitions files in the
+ Library will not necessarily be able to recompile the application
+ to use the modified definitions.)
+
+ b) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (1) uses at run time a
+ copy of the library already present on the user's computer system,
+ rather than copying library functions into the executable, and (2)
+ will operate properly with a modified version of the library, if
+ the user installs one, as long as the modified version is
+ interface-compatible with the version that the work was made with.
+
+ c) Accompany the work with a written offer, valid for at least
+ three years, to give the same user the materials specified in
+ Subsection 6a, above, for a charge no more than the cost of
+ performing this distribution.
+
+ d) If distribution of the work is made by offering access to copy
+ from a designated place, offer equivalent access to copy the above
+ specified materials from the same place.
+
+ e) Verify that the user has already received a copy of these
+ materials or that you have already sent this user a copy.
+
+ For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it. However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+ It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system. Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+ 7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+ a) Accompany the combined library with a copy of the same work
+ based on the Library, uncombined with any other library
+ facilities. This must be distributed under the terms of the
+ Sections above.
+
+ b) Give prominent notice with the combined library of the fact
+ that part of it is a work based on the Library, and explaining
+ where to find the accompanying uncombined form of the same work.
+
+ 8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License. Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License. However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+ 9. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Library or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+ 10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+
+ 11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all. For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply, and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License
+may add an explicit geographical distribution limitation excluding those
+countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation. If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+ 14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission. For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this. Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+ NO WARRANTY
+
+ 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Libraries
+
+ If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change. You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms
+of the ordinary General Public License).
+
+ To apply these terms, attach the following notices to the library.
+It is safest to attach them to the start of each source file to most
+effectively convey the exclusion of warranty; and each file should
+have at least the "copyright" line and a pointer to where the full
+notice is found.
+
+
+ <one line to give the library's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or
+your school, if any, to sign a "copyright disclaimer" for the library,
+if necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the
+ library `Frob' (a library for tweaking knobs) written by James
+ Random Hacker.
+
+ <signature of Ty Coon>, 1 April 1990
+ Ty Coon, President of Vice
+
+That's all there is to it!
+
+
diff --git a/LICENSE.LGPL-3 b/LICENSE.LGPL-3
new file mode 100644
index 0000000..fc8a5de
--- /dev/null
+++ b/LICENSE.LGPL-3
@@ -0,0 +1,165 @@
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+
+ This version of the GNU Lesser General Public License incorporates
+the terms and conditions of version 3 of the GNU General Public
+License, supplemented by the additional permissions listed below.
+
+ 0. Additional Definitions.
+
+ As used herein, "this License" refers to version 3 of the GNU Lesser
+General Public License, and the "GNU GPL" refers to version 3 of the GNU
+General Public License.
+
+ "The Library" refers to a covered work governed by this License,
+other than an Application or a Combined Work as defined below.
+
+ An "Application" is any work that makes use of an interface provided
+by the Library, but which is not otherwise based on the Library.
+Defining a subclass of a class defined by the Library is deemed a mode
+of using an interface provided by the Library.
+
+ A "Combined Work" is a work produced by combining or linking an
+Application with the Library. The particular version of the Library
+with which the Combined Work was made is also called the "Linked
+Version".
+
+ The "Minimal Corresponding Source" for a Combined Work means the
+Corresponding Source for the Combined Work, excluding any source code
+for portions of the Combined Work that, considered in isolation, are
+based on the Application, and not on the Linked Version.
+
+ The "Corresponding Application Code" for a Combined Work means the
+object code and/or source code for the Application, including any data
+and utility programs needed for reproducing the Combined Work from the
+Application, but excluding the System Libraries of the Combined Work.
+
+ 1. Exception to Section 3 of the GNU GPL.
+
+ You may convey a covered work under sections 3 and 4 of this License
+without being bound by section 3 of the GNU GPL.
+
+ 2. Conveying Modified Versions.
+
+ If you modify a copy of the Library, and, in your modifications, a
+facility refers to a function or data to be supplied by an Application
+that uses the facility (other than as an argument passed when the
+facility is invoked), then you may convey a copy of the modified
+version:
+
+ a) under this License, provided that you make a good faith effort to
+ ensure that, in the event an Application does not supply the
+ function or data, the facility still operates, and performs
+ whatever part of its purpose remains meaningful, or
+
+ b) under the GNU GPL, with none of the additional permissions of
+ this License applicable to that copy.
+
+ 3. Object Code Incorporating Material from Library Header Files.
+
+ The object code form of an Application may incorporate material from
+a header file that is part of the Library. You may convey such object
+code under terms of your choice, provided that, if the incorporated
+material is not limited to numerical parameters, data structure
+layouts and accessors, or small macros, inline functions and templates
+(ten or fewer lines in length), you do both of the following:
+
+ a) Give prominent notice with each copy of the object code that the
+ Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the object code with a copy of the GNU GPL and this license
+ document.
+
+ 4. Combined Works.
+
+ You may convey a Combined Work under terms of your choice that,
+taken together, effectively do not restrict modification of the
+portions of the Library contained in the Combined Work and reverse
+engineering for debugging such modifications, if you also do each of
+the following:
+
+ a) Give prominent notice with each copy of the Combined Work that
+ the Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the Combined Work with a copy of the GNU GPL and this license
+ document.
+
+ c) For a Combined Work that displays copyright notices during
+ execution, include the copyright notice for the Library among
+ these notices, as well as a reference directing the user to the
+ copies of the GNU GPL and this license document.
+
+ d) Do one of the following:
+
+ 0) Convey the Minimal Corresponding Source under the terms of this
+ License, and the Corresponding Application Code in a form
+ suitable for, and under terms that permit, the user to
+ recombine or relink the Application with a modified version of
+ the Linked Version to produce a modified Combined Work, in the
+ manner specified by section 6 of the GNU GPL for conveying
+ Corresponding Source.
+
+ 1) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (a) uses at run time
+ a copy of the Library already present on the user's computer
+ system, and (b) will operate properly with a modified version
+ of the Library that is interface-compatible with the Linked
+ Version.
+
+ e) Provide Installation Information, but only if you would otherwise
+ be required to provide such information under section 6 of the
+ GNU GPL, and only to the extent that such information is
+ necessary to install and execute a modified version of the
+ Combined Work produced by recombining or relinking the
+ Application with a modified version of the Linked Version. (If
+ you use option 4d0, the Installation Information must accompany
+ the Minimal Corresponding Source and Corresponding Application
+ Code. If you use option 4d1, you must provide the Installation
+ Information in the manner specified by section 6 of the GNU GPL
+ for conveying Corresponding Source.)
+
+ 5. Combined Libraries.
+
+ You may place library facilities that are a work based on the
+Library side by side in a single library together with other library
+facilities that are not Applications and are not covered by this
+License, and convey such a combined library under terms of your
+choice, if you do both of the following:
+
+ a) Accompany the combined library with a copy of the same work based
+ on the Library, uncombined with any other library facilities,
+ conveyed under the terms of this License.
+
+ b) Give prominent notice with the combined library that part of it
+ is a work based on the Library, and explaining where to find the
+ accompanying uncombined form of the same work.
+
+ 6. Revised Versions of the GNU Lesser General Public License.
+
+ The Free Software Foundation may publish revised and/or new versions
+of the GNU Lesser General Public License from time to time. Such new
+versions will be similar in spirit to the present version, but may
+differ in detail to address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Library as you received it specifies that a certain numbered version
+of the GNU Lesser General Public License "or any later version"
+applies to it, you have the option of following the terms and
+conditions either of that published version or of any later version
+published by the Free Software Foundation. If the Library as you
+received it does not specify a version number of the GNU Lesser
+General Public License, you may choose any version of the GNU Lesser
+General Public License ever published by the Free Software Foundation.
+
+ If the Library as you received it specifies that a proxy can decide
+whether future versions of the GNU Lesser General Public License shall
+apply, that proxy's public statement of acceptance of any version is
+permanent authorization for you to choose that version for the
+Library.
diff --git a/Makefile.am b/Makefile.am
new file mode 100644
index 0000000..3a30b20
--- /dev/null
+++ b/Makefile.am
@@ -0,0 +1,11 @@
+## Process this file with automake to produce Makefile.in
+
+SUBDIRS = src
+
+# list all additional files which need to be included by "make dist",
+# check with "make distcheck"
+EXTRA_DIST =
+# extra files which need to be removed during "make distclean"
+MAINTAINERCLEANFILES = Makefile.in config.guess config.sub configure depcomp install-sh ltmain.sh missing mkinstalldirs
+
+ACLOCAL_AMFLAGS = -I m4
diff --git a/NEWS b/NEWS
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/NEWS
diff --git a/README b/README
new file mode 100644
index 0000000..ba212e4
--- /dev/null
+++ b/README
@@ -0,0 +1,35 @@
+Build dependencies
+------------------
+pcre
+sqlite3
+xmltok (optional, bundled source is used automatically if not installed)
+zlib
+
+Ubuntu 8.04: libpcre3-dev libsqlite3-dev libxmltok1-dev libz-dev
+
+Contributions
+-------------
+You are free to modify and distribute under the licenses indicated in
+COPYING. As many other companies which dual-license their code, Synthesis
+must be able to use all code in the upstream repository for their
+commercial offerings. Therefore please sign the
+ Synthesis_AG_Contributor_Agreement.pdf
+and send it to Synthesis before submitting patches and code to Synthesis
+for inclusion.
+
+By doing so you simplify your own usage of the code (no need to merge
+your changes with new upstream releases again and again) and ensure
+that Synthesis will continue to release updates under an open source
+license (we need to finance the development with sales from our commercial
+products for commercial platforms and applications).
+
+Synthesis is a small, entirely self-funded venture and the code that is
+now becoming opensource is the work of 8 years focus on SyncML. So we count
+on contributors to understand that in our own interest as well as the interest
+of the project's future we need to be able to continue our commercial products
+without complicating the process of developing them.
+With the contributor agreement we can avoid the need for forking our internal
+work into an open source branch and an internal branch, which would severely
+reduce the possible output on the opensource branch. The contributor agreement
+ensures we'll be able to share our internal progress in the project without
+delay with the open source community.
diff --git a/Synthesis_AG_Contributor_Agreement.pdf b/Synthesis_AG_Contributor_Agreement.pdf
new file mode 100644
index 0000000..90573df
--- /dev/null
+++ b/Synthesis_AG_Contributor_Agreement.pdf
Binary files differ
diff --git a/autogen.sh b/autogen.sh
new file mode 100755
index 0000000..9a806cf
--- /dev/null
+++ b/autogen.sh
@@ -0,0 +1,11 @@
+#!/bin/sh
+
+set -e
+
+(cd src && ./gen-makefile-am.sh)
+
+libtoolize -c
+aclocal
+autoheader
+automake -a -c
+autoconf
diff --git a/configure.in b/configure.in
new file mode 100644
index 0000000..299a91c
--- /dev/null
+++ b/configure.in
@@ -0,0 +1,67 @@
+dnl Invoke autogen.sh to produce a configure script.
+
+AC_INIT(src/sysync/engineinterface.cpp)
+AC_CONFIG_MACRO_DIR([m4])
+AM_INIT_AUTOMAKE(syncevolution, 0.8-beta2)
+AM_CONFIG_HEADER(config.h)
+AC_LIBTOOL_DLOPEN
+
+dnl check for programs.
+AC_PROG_CXX
+AC_PROG_LIBTOOL
+AC_PROG_MAKE_SET
+AM_PROG_CC_C_O
+
+dnl All of these libraries have to be compiled and installed
+dnl separately. TODO: build bundled sources?
+PKG_CHECK_MODULES(PCRE, libpcre)
+PKG_CHECK_MODULES(SQLITE3, sqlite3)
+AC_CHECK_HEADER(zlib.h, , [AC_ERROR(zlib.h not found.)])
+AC_CHECK_HEADER(xmltok/xmlparse.h, have_xmltok="yes")
+
+dnl libical might be installed stand-alone (upstream)
+dnl or bundled with libecal. Upstream is preferred and
+dnl thus checked first. libical is optional, so don't
+dnl complain if not found.
+PKG_CHECK_MODULES(LIBICAL, libical,
+ [AC_DEFINE(HAVE_LIBICAL, 1, "libical available")],
+ [PKG_CHECK_MODULES(LIBECAL, libecal-1.2,
+ [AC_DEFINE(HAVE_LIBICAL, 1, "libical available")
+ AC_DEFINE(HAVE_LIBECAL, 1, "libecal available")],
+ [true])])
+
+AC_ARG_WITH(xmltok,
+ AS_HELP_STRING([--with-xmltok=<builtin|system|path>],
+ [Chooses which xmltok/xmlparse gets used.
+ "builtin" = compile code into libsynthesis.
+ "system" = use header files and libxmlparse from standard search paths.
+ "path" = use header files in "path/include" and libxmlparse in "path/lib".
+ Default is system if header files are found, otherwise builtin.]),
+ [ if test "$withval" = "builtin"; then xmltok_builtin="yes"
+ elif test "$withval" = "system"; then
+ if test "$have_xmltok" = "yes"; then XMLPARSE_LIBS="-lxmlparse"
+ else AC_ERROR(xmltok/xmplarse.h not found.)
+ fi
+ else
+ XMLPARSE_LIBS="-L$withval/lib -lxmlparse"
+ XMLPARSE_CFLAGS="-I$withval/include"
+ fi ],
+ [ if test "$have_xmltok" = "yes"; then XMLPARSE_LIBS="-lxmlparse"
+ else xmltok_builtin="yes"
+ fi ])
+
+AC_SUBST(XMLPARSE_LIBS)
+AC_SUBST(XMLPARSE_CFLAGS)
+AM_CONDITIONAL([COND_XMLPARSE], [test "$xmltok_builtin" = "yes"])
+
+AC_CHECK_HEADER(stdint.h)
+
+echo "enable dynamic $enable_shared"
+if test "$enable_shared" = "no"; then
+ AC_DEFINE(UIAPI_LINKED, 1, [libsynthesis.a linked statically])
+ AC_DEFINE(DBAPI_LINKED, 1, [libsynthesis.a linked statically])
+fi
+AM_CONDITIONAL([COND_STATIC], [test "$enable_shared" = "no"])
+
+AC_CONFIG_FILES(Makefile src/Makefile)
+AC_OUTPUT
diff --git a/doc/SDK_manual.doc b/doc/SDK_manual.doc
new file mode 100644
index 0000000..3ba0b7b
--- /dev/null
+++ b/doc/SDK_manual.doc
Binary files differ
diff --git a/doc/SDK_manual.pdf b/doc/SDK_manual.pdf
new file mode 100644
index 0000000..f1bb39a
--- /dev/null
+++ b/doc/SDK_manual.pdf
Binary files differ
diff --git a/doc/SySync_config_reference.doc b/doc/SySync_config_reference.doc
new file mode 100644
index 0000000..2d70248
--- /dev/null
+++ b/doc/SySync_config_reference.doc
Binary files differ
diff --git a/doc/SySync_config_reference.pdf b/doc/SySync_config_reference.pdf
new file mode 100644
index 0000000..17c6b6d
--- /dev/null
+++ b/doc/SySync_config_reference.pdf
@@ -0,0 +1,7749 @@
+%PDF-1.3 %âãÏÓ
+1676 0 obj << /Linearized 1 /O 1678 /H [ 7898 5569 ] /L 1753396 /E 777607 /N 198 /T 1719756 >> endobj xref 1676 367 0000000016 00000 n
+0000007696 00000 n
+0000013467 00000 n
+0000013629 00000 n
+0000013700 00000 n
+0000013820 00000 n
+0000013978 00000 n
+0000014158 00000 n
+0000014291 00000 n
+0000014443 00000 n
+0000014572 00000 n
+0000014702 00000 n
+0000014925 00000 n
+0000015168 00000 n
+0000015347 00000 n
+0000015542 00000 n
+0000015735 00000 n
+0000015888 00000 n
+0000016067 00000 n
+0000016243 00000 n
+0000016414 00000 n
+0000016569 00000 n
+0000016727 00000 n
+0000016893 00000 n
+0000017058 00000 n
+0000017245 00000 n
+0000017408 00000 n
+0000017574 00000 n
+0000017740 00000 n
+0000017923 00000 n
+0000018107 00000 n
+0000018282 00000 n
+0000018461 00000 n
+0000018624 00000 n
+0000018798 00000 n
+0000018962 00000 n
+0000019218 00000 n
+0000019391 00000 n
+0000019599 00000 n
+0000019782 00000 n
+0000019964 00000 n
+0000020165 00000 n
+0000020287 00000 n
+0000020517 00000 n
+0000020646 00000 n
+0000020782 00000 n
+0000020915 00000 n
+0000021095 00000 n
+0000021240 00000 n
+0000021381 00000 n
+0000021522 00000 n
+0000021647 00000 n
+0000021772 00000 n
+0000021918 00000 n
+0000022064 00000 n
+0000022330 00000 n
+0000022554 00000 n
+0000022731 00000 n
+0000022956 00000 n
+0000023138 00000 n
+0000023289 00000 n
+0000023460 00000 n
+0000023631 00000 n
+0000023832 00000 n
+0000024003 00000 n
+0000024198 00000 n
+0000024380 00000 n
+0000024548 00000 n
+0000024832 00000 n
+0000024999 00000 n
+0000025217 00000 n
+0000025385 00000 n
+0000025560 00000 n
+0000025751 00000 n
+0000025915 00000 n
+0000026090 00000 n
+0000026271 00000 n
+0000026441 00000 n
+0000026631 00000 n
+0000026803 00000 n
+0000026968 00000 n
+0000027183 00000 n
+0000027353 00000 n
+0000027533 00000 n
+0000027769 00000 n
+0000027942 00000 n
+0000028116 00000 n
+0000028285 00000 n
+0000028493 00000 n
+0000028672 00000 n
+0000028844 00000 n
+0000029016 00000 n
+0000029207 00000 n
+0000029377 00000 n
+0000029548 00000 n
+0000029751 00000 n
+0000029902 00000 n
+0000030088 00000 n
+0000030246 00000 n
+0000030425 00000 n
+0000030584 00000 n
+0000030745 00000 n
+0000030890 00000 n
+0000031037 00000 n
+0000031225 00000 n
+0000031401 00000 n
+0000031604 00000 n
+0000031774 00000 n
+0000031976 00000 n
+0000032192 00000 n
+0000032313 00000 n
+0000032470 00000 n
+0000032645 00000 n
+0000032810 00000 n
+0000032985 00000 n
+0000033157 00000 n
+0000033337 00000 n
+0000033529 00000 n
+0000033708 00000 n
+0000033893 00000 n
+0000034088 00000 n
+0000034280 00000 n
+0000034495 00000 n
+0000034699 00000 n
+0000034891 00000 n
+0000035080 00000 n
+0000035269 00000 n
+0000035444 00000 n
+0000035616 00000 n
+0000035759 00000 n
+0000035928 00000 n
+0000036136 00000 n
+0000036297 00000 n
+0000036439 00000 n
+0000036614 00000 n
+0000036806 00000 n
+0000036944 00000 n
+0000037153 00000 n
+0000037373 00000 n
+0000037534 00000 n
+0000037685 00000 n
+0000037855 00000 n
+0000038034 00000 n
+0000038215 00000 n
+0000038384 00000 n
+0000038572 00000 n
+0000038794 00000 n
+0000038978 00000 n
+0000039168 00000 n
+0000039337 00000 n
+0000039524 00000 n
+0000039710 00000 n
+0000039878 00000 n
+0000040052 00000 n
+0000040231 00000 n
+0000040411 00000 n
+0000040615 00000 n
+0000040809 00000 n
+0000040993 00000 n
+0000041161 00000 n
+0000041334 00000 n
+0000041538 00000 n
+0000041727 00000 n
+0000041915 00000 n
+0000042083 00000 n
+0000042247 00000 n
+0000042414 00000 n
+0000042583 00000 n
+0000042745 00000 n
+0000042918 00000 n
+0000043089 00000 n
+0000043236 00000 n
+0000043404 00000 n
+0000043626 00000 n
+0000043833 00000 n
+0000044027 00000 n
+0000044195 00000 n
+0000044369 00000 n
+0000044532 00000 n
+0000044688 00000 n
+0000044859 00000 n
+0000045016 00000 n
+0000045173 00000 n
+0000045331 00000 n
+0000045489 00000 n
+0000045676 00000 n
+0000045891 00000 n
+0000046080 00000 n
+0000046276 00000 n
+0000046447 00000 n
+0000046616 00000 n
+0000046800 00000 n
+0000046986 00000 n
+0000047150 00000 n
+0000047331 00000 n
+0000047535 00000 n
+0000047724 00000 n
+0000047941 00000 n
+0000048154 00000 n
+0000048323 00000 n
+0000048488 00000 n
+0000048658 00000 n
+0000048833 00000 n
+0000048996 00000 n
+0000049159 00000 n
+0000049337 00000 n
+0000049499 00000 n
+0000049673 00000 n
+0000049847 00000 n
+0000050059 00000 n
+0000050205 00000 n
+0000050331 00000 n
+0000050536 00000 n
+0000050754 00000 n
+0000050934 00000 n
+0000051078 00000 n
+0000051235 00000 n
+0000051399 00000 n
+0000051581 00000 n
+0000051769 00000 n
+0000051968 00000 n
+0000052153 00000 n
+0000052321 00000 n
+0000052494 00000 n
+0000052693 00000 n
+0000052899 00000 n
+0000053076 00000 n
+0000053238 00000 n
+0000053453 00000 n
+0000053621 00000 n
+0000053755 00000 n
+0000053911 00000 n
+0000054071 00000 n
+0000054230 00000 n
+0000054379 00000 n
+0000054538 00000 n
+0000054703 00000 n
+0000054846 00000 n
+0000055007 00000 n
+0000055174 00000 n
+0000055324 00000 n
+0000055493 00000 n
+0000055649 00000 n
+0000055816 00000 n
+0000056021 00000 n
+0000056167 00000 n
+0000056329 00000 n
+0000056516 00000 n
+0000056673 00000 n
+0000056830 00000 n
+0000057003 00000 n
+0000057162 00000 n
+0000057339 00000 n
+0000057515 00000 n
+0000057690 00000 n
+0000057836 00000 n
+0000057999 00000 n
+0000058173 00000 n
+0000058335 00000 n
+0000058503 00000 n
+0000058701 00000 n
+0000058910 00000 n
+0000059064 00000 n
+0000059206 00000 n
+0000059399 00000 n
+0000059560 00000 n
+0000059724 00000 n
+0000059884 00000 n
+0000060063 00000 n
+0000060256 00000 n
+0000060443 00000 n
+0000060606 00000 n
+0000060792 00000 n
+0000060978 00000 n
+0000061150 00000 n
+0000061313 00000 n
+0000061490 00000 n
+0000061655 00000 n
+0000061815 00000 n
+0000061959 00000 n
+0000062126 00000 n
+0000062268 00000 n
+0000062419 00000 n
+0000062565 00000 n
+0000062732 00000 n
+0000062901 00000 n
+0000063079 00000 n
+0000063245 00000 n
+0000063403 00000 n
+0000063565 00000 n
+0000063740 00000 n
+0000063927 00000 n
+0000064060 00000 n
+0000064197 00000 n
+0000064378 00000 n
+0000064520 00000 n
+0000064667 00000 n
+0000064811 00000 n
+0000064997 00000 n
+0000065127 00000 n
+0000065253 00000 n
+0000065455 00000 n
+0000065632 00000 n
+0000065757 00000 n
+0000065881 00000 n
+0000066025 00000 n
+0000066177 00000 n
+0000066325 00000 n
+0000066478 00000 n
+0000066652 00000 n
+0000066783 00000 n
+0000066910 00000 n
+0000067047 00000 n
+0000067171 00000 n
+0000067293 00000 n
+0000067429 00000 n
+0000067606 00000 n
+0000067746 00000 n
+0000067872 00000 n
+0000068006 00000 n
+0000068146 00000 n
+0000068313 00000 n
+0000068456 00000 n
+0000068591 00000 n
+0000068727 00000 n
+0000068883 00000 n
+0000069016 00000 n
+0000069176 00000 n
+0000069397 00000 n
+0000069530 00000 n
+0000069663 00000 n
+0000069805 00000 n
+0000069981 00000 n
+0000070128 00000 n
+0000070269 00000 n
+0000070434 00000 n
+0000070593 00000 n
+0000070736 00000 n
+0000070890 00000 n
+0000071069 00000 n
+0000071192 00000 n
+0000071348 00000 n
+0000071483 00000 n
+0000071631 00000 n
+0000071763 00000 n
+0000071890 00000 n
+0000072082 00000 n
+0000072206 00000 n
+0000072345 00000 n
+0000072482 00000 n
+0000072728 00000 n
+0000072771 00000 n
+0000073857 00000 n
+0000074068 00000 n
+0000074123 00000 n
+0000074671 00000 n
+0000074887 00000 n
+0000075476 00000 n
+0000075617 00000 n
+0000110527 00000 n
+0000111386 00000 n
+0000113830 00000 n
+0000127795 00000 n
+0000165169 00000 n
+0000170243 00000 n
+0000007898 00000 n
+0000013443 00000 n
+trailer << /Size 2043 /Info 1642 0 R /Root 1677 0 R /Prev 1719744 /ID[<1b0c5acde4b3a254aa19b761e04c3766><1b0c5acde4b3a254aa19b761e04c3766>] >> startxref 0 %%EOF 1677 0 obj << /Type /Catalog /Pages 1654 0 R /Outlines 1679 0 R /OpenAction [ 1678 0 R /XYZ null 846 1 ] /PageMode /UseOutlines /PageLabels << /Nums [ 0 << /S /D >> ] >> /JT 1675 0 R >> endobj 2041 0 obj << /S 7064 /O 8142 /Filter /FlateDecode /Length 2042 0 R >> stream
+H‰t”kP“ÙÇŸ7x ƒÈÅR$µ R¹H\‰ŽT7[ËÎTdÍlW‚bݹí1!»a·ËÊM©.—ÆqZÆQ0RSHÈ@¤ÐÝæF¸ ;%„@i@¥zÂjÝ/}?¾çœÿù?ÿç÷
+“x œNö¿ù ï4¨Óà·á›&üdmW#ÜXõY‚ÏKIÞÐȇ¿&ï¬ÆÊ'Ãêà3©\d’îÓ}§¡Šx‹ê Kà9'ø— é X ÿ´Ñ}7¡BŒ|Ô᫆ Œ@vy¥±»#È¿aøˆ@N÷{
+/KIþ`Xõ“ƒµÃß÷ÁpoÞOódÿ ï`o
+ú)ÜMªÁp:YŠý\Cb@ƒE ùFÁíIt6—¿‹Jð.ƒ<±W(ü‰âûˆ o@Àñ=ä&\w;°fø‚C,̶lª„s'?øgçȹ;焆sßFßQµ
+-[s箳 ³kwÓÿvÍø““}\’î8§re°]yö}cÏþ·q¬A8«ï;ŸäQ™5Ì4tzqü´º}ƒ*ξ8øµjeã#ªªäÜÛ§;]üœåo|jCgܵá䩼ˆïM„Ž9ktìOea†c×Çk‡-Ë)Sîº~žölwKUÆU¯×Üõ°îÙ~[aw™Ïˇ²¥ ‰ ¡_…_sy|aÁ~UŽ6»¨%>(i´@kY~Þ&à)Ì ¦Sï'éstÙEÍËi}ãd k)ÓgýlYVY˜!J2Z¨]p<ìºX®âö.$¶¤¦\Ÿ¨ÕÙ¨g–VöÚXh?«/”še&C`}0K9—h0-”Íñ,7íêb={móž[Ì•fñL©ÎöÑZu¶C±\áŠôdSC ¡5ãyjër›Ä})˜'e'…;½(Ñ/ö\êZ‰]ÿL~6¡bÊ]þ´rКÀ/z#X£c)¸èGPɼÿü?+î*©9±­^74~Yf¥¶é†X=Y¼¬-Ñ#w•žíl«¿®/ÐÎñÜKΣK”çðŸ¼wÔ}Ažåz(ñÄ;ÂLÊtˆÖ£¯ªfQÎÎöñF¤sdÌY7Ì–²zZŸ¾%Tq[mÇ®Ž7öqy)¶PA±:oÍ<`TÛy#Üáþ)ÿˆÉxé|[šÝÓæ1V¢àRi©1’ Ñ›ÚY²–±g{Qí ¼6[E.-YÐæþ+
+¶4 Ý®/±Riû‚aV^R82ïñãªXr>Ž.V³eFÝZ©‚{€–u3D$7_4:죯JÙ®Ì'i5º†þó¼Tm´v$«Èh -·W>DæoÆÜŠ¬E%·¼tD õ³]–1ûØSÞ 7!32J².’/”)N:‡Ö<æ[>=º)PZÊZâ ~yÙRº³}æŠtÑS²ÀÊRç%en}¹0Ì•Y{Og´6öZºŒSö¡µË}YËEaU9½Ó]­þºg£yòEžÑöå‚Š¥·õ´vW¬ÌqQ'äKÖ(UùnmÒmä+hÑq-« AKÛ†Ö=?ÖÏÖf'˜Ò÷A<Ç{B˜©Rzü4;v‹ä–e‹-6ÖÚÐÃ, ßo¯nì;+£¥ë`k™IYÑÒø*žµ (v_dž‚Yd©³{CÓ$ܳ/²U¡4ˆ„ÑB=BâÉ{ݯ•greÌ’–—ˆ)
+³«åàV¡Ì*SœÊà?­ìaR7vG”Pié>_ðvp£œ]†ú¾1Q¥4_´8f»¿³‘_7\:˜µl¾é¼ÒØ——²ä¬ë/Wp·»<Ѩµv{˜æ"øù°½(C[ü« x–ú@Á
+h( ]Å8YTÌʆA±d²dì8¤Ì´ …ÕfWRð¨4mÌ
+
+DË£nkq¦™LY¦<E©"TV*“©<„ðr°3¼‘‚¢IÏ»Áfº;wïîÿŸó}ç|çÜk3stNæ¡ù×
+‰öF4bØýg¤@W6ØB³º‘4ÛTØ{½ÔÃuùºN­+h¯y›®:Á§÷ÀìÎ{ªï ëZºù, k°|J#[H!ºª;ùLÓ+HtCGʳr8`$­C­ëûcÈEL›¾d÷ü¥OèxSÉðÉg… C¦òA¿IQº3»fºZ-|éÚ£÷b’¡\Dî­Ï±æO@£?>I$Ô§/Ͼ;mŒš;]•Û¼RXOÄ“èsÇHê{Ÿg4ÅÖô_//x¸øVr'Ú¼ »Ïݘ[ŸÊ»z,ä¼-³Q=Ów#}rÓo¡Á¸|Oƒ}°d¥€«‚®¥óè3†|Išm ?ùüeúÓ¥B$¥T.#ªÕo˜¹rWo ×)»8{nð#½)¼J)÷»Øy®®¯kí}“Ú'ãˆÊ)ê[ÂÑ{Ý ÞØþYW,á=‰Â\RØýimäB£!)£U}0Â\B2s<µAŒI›Ù¡;z?©«Sƒ\<
+©£cPIÊäég±/ôÊrœ^^ÊhJ4 T­›¢:}Æz'-»jÝ̼ÒSQ¥¼|{!ñÆ€I™ý&/lY]/}»0¶Ÿe­w‘ǹv¦qÔ6iÐye
+_Ý0$«%º¦ßÄM)ë높 ð~îÍ•¬µñtÁçœ|e#ƒØnä˜WpìyœêÇ4ëØóXwÔŒJ«'m"y¿Ð>.ÿëT 0rŸÿà›
+ØAìVL8kR‚-´ü;úúÄçÐÛ1Ñ•%Xq¤Wúôæ$¿½š/lš„¯hS™9ý™@ŒõÙžÄö¾T·®%Lõ†c»™!Ç*ÿƒ똑°»Ä\ún§†3,š°, ,gh‰&4“×ÿ'÷öBakìöÁÚ»ñ¸Ô›Ý]ø‡œ“å]˜i˜6þ˜cRuÝà>Fº­3I;V“<é,vjBnàå{êŽqm£¹$­•$?¶­‹‹3c àÊéàŒ•SFõ¶›Ú¥xÆÎMV§„ə֙"’Ý øáäÈ«ûø›%ù¨$áâ´˜,ò«p^àú76sº1>VÔ51õÝⶴZõ)¬”—êÎð3²5LúU–qilj4­ògúÿi7L×»ËÌ-̤gM’á=®&²îà@µ7ü~n‹ç©Öa]éåΗÇ[û*‘Üæ•,œdÜ ’FðC–$´‡WÖ{$q£uNÈj̇˜c™5ý8”z>éf)`t,Ò§÷àG‡Zq–7NNÌ.éë#™.3’m<†m¢7` º=<ÅüÅâÞô~ÑÓ8Œ“ŒëÙ¦R´Ýߣó(VOdç›bRܸñÛØhPnìLìª|8”4Þ”Ò8Rå_”ÿ½U 1¼ø|Ö%·y„Ø„£ÀŽev §*µÞW:s׋’ë£ô ®]In6fNç.ßC©zÖ¼”1R„o
+“u4'7Ç“Ù*¸A†À'‡¾+lžãçð׬Pâfe±D9*3ÿšjæ³¥Ù5¸¢ Í.8À4»ÓL³3áo`ÛB.ö*u!…ôÕQ¨Z’ü™Ïþ7ë~Þ¯ºû²ïrNÈi‰ÖHæ±ô§Ÿ™²ª`ÒetåYèyo:Š-@z¼¦OÛûÆΈÌÁ`tÌ%/Õâȧ^¥?ÜH8ýÚò—øÊÐ2¶O©}ÓíÞñÛ1¼‘á¼ëÝÆw=ƒëØãpŸéä•*f:ÓpÈ 'ü× Ú곸îà¶Ö
+ÍÛЬ %U(Ö?¬;^©ôœK4í²‹5%4±%ŽÈð³ž
+ö9™ÊrKÆm” qŸ9Áí„e¦zÁ*½ÅEZ¦´ÁŸÃ?RnWÅ*ÌXtäÃeÁ.(;ÙÈvq.±ØóÎÓœóøÙ®à @î<ÁÃÁb¥X˳àÊ›Y]±˜b(û3÷x·Ü41öjƒ…fsa€ÀÁK S(,ˆ èåö°±Ðd«R1Q4¡|CÔ–ùWÓ*Ã9së ka<p<ðü¿ö1bÙçN&•Œ½† ò‚ QºŸõœ—¶lƒhI òͦ¨…4ø‡ìcžG²ƒKnšÍø!â7g­Â‰¢·-m:À'ãÆ®øJc,)o@6yhžO•Š;]âÛÃœ›Â'Èñ¼/lµ
+Y-ú)„}£@pxðx‰`m(â o/Ø/óHúáË_sPèfȨðþ“MsÏ;E6×쪵ùÒkÇ(µ#È9Ž
+J‘¾íó¸Á­¶MÈγyƒmM'¾A%„nþ|=/¢aÄ%P
+\»`¢Øé |ãët–9WSª6q4dµQˆ½'\íÁgãCEïcv© ÎÛà
+ñ²ÿxƒÓ·gÎÅ"|SA•Àoãlcú¨`kŒóB‹ÿ_2Y$‡-`Ÿ?ÛøþËI¹´6GQü×Ê<Sa@\JƒâR™•¸Ì'б‚h|Å"*Zm­}ÛÖI£¶hbm‹Å‚iQ©Ÿà.êÒM6î³rkôxOû ìb¸éœÿ¹çœ{ÿãWÉã693ÀGºRîc™/㶬òR< çÕ™ €1U·ñ<ÄÏÄK™Å«ò?ËK½˜'hq”L]¤M£·—p³ôâdéÁà2ÛÛ|Â/ð¿N^ªc©šÏã5ùƒ×à­ªAŽqu›Ãkó?2-aƒ;bþª¾¦ ¥æ)E½].%DÌm¹LÂVÄd¤<–­š Ï ›ÁËZ¯f;û€ ‘óOUmF,dY‚"nŽi„ÜXÒÎx'ú>‘ÚÙšA‚ˆøM“kÒª’1…ëm•5‚Ëϼ͈~Aà)…8)ð¨¥R:E&æ…À›g-ûHóÈ2ˆ›-›Ç†¤­(¿iu«ˆen‹ÔnÃkñMHUIƒÃ}l…U›‚³NØ6Ón"Òª†±*ûS’±JSÖ¾lHîi)ÆËqU ¥1¢A]S£›ªÖ5ßQI”ÒáÊJ¼¤cj>*ªgJ­_!.+ºq<Å,+SZQ`EUeMfXú&îËfL÷´‰·$­¤nyœ–97È+Úçãm#°­;¦ÿ]Ç­rC
+Nâä8‡ÙžÚŠ–q#;u.ÈÂEœge5/Uïqjº$1w5”^ ’ó~íU"Ü1Ÿé’H ÒòC_ê„)]tWyÃ^»þl¼ÍG‡zƒ=ñŽW‹‡ÛìkðEÎq°Öù›Ý52D‰ýôõØ_ÛµF&é8@X·¹íLì¸YÙn|۪´§hcœÿK&›]&õ¯ÐÑ°+ÔÉý¹èþn_Œ<üZü'À
+H‰|RËŽÔ0¼û+ú„âC<vûçÈ΢$`|@B d–Al"6³ ù$þ’rœMH(Jb»»ªË]}•Ä.%&Cé( “ƃ_£Ut &h…×Rº»ý©›æMS7ˆÝÍÁÐí$4¥.~ŠŠdú*ž%ñdZ±+„–AGlå¬
+Q;êÀúüÎÐõ(Þˆ«$Úœ–óÂú&_ógf­^jA½[ÔÇR,¢^£tз¬âÜdk¥u>ëD=/}Vþ¾z÷ê%íÇáxº}¸—5³òÕG‰K„êŒ-v§q ·ZbÕË|~”ÈzÜ
+˜ÔT“ôyr$P¼lèéÍÚåß
+endstream endobj 2031 0 obj << /Type /FontDescriptor /Ascent 1100 /CapHeight 0 /Descent -309 /Flags 32 /FontBBox [ -194 -307 1688 1083 ] /FontName /DNODBA+Arial-Black /ItalicAngle 0 /StemV 0 /FontFile2 2037 0 R >> endobj 2032 0 obj << /Type /Font /Subtype /TrueType /FirstChar 32 /LastChar 148 /Widths [ 333 0 500 0 0 0 889 278 389 389 0 0 333 333 333 278 667 667 667 667 667 667 667 667 667 667 333 333 660 660 660 611 0 778 778 778 778 722 667 833 833 389 667 833 667 944 833 833 722 833 778 722 722 833 778 1000 778 0 722 0 0 0 0 500 0 667 667 667 667 667 389 667 667 333 333 667 333 1000 667 667 667 667 444 611 444 667 611 944 667 611 556 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 500 500 ] /Encoding /WinAnsiEncoding /BaseFont /DNODBA+Arial-Black /FontDescriptor 2031 0 R >> endobj 2033 0 obj << /Type /ExtGState /SA false /SM 0.02 /OP false /op false /OPM 1 /BG2 /Default /UCR2 /Default /TR2 /Default >> endobj 2034 0 obj << /Filter /FlateDecode /Length 34817 /Length1 46800 >> stream
+H‰|V PT×þιåíZÁ"«æ®¸»¢â‘èv‘‡PÐ]5f—‡¢ˆ¬ŠV¬Iצ–æ‚m£¡c´$ÚÆ)vrMB´MI§IÚÔ¼:Òé´µÍĘÉ(LJ£ŽµÂíqÀ&=‡Ÿû¿Î¿ÿqf/€D„! |åêì9Õ¥¾ ÀËŤ]QÓ ½¹6§ 8C2;Y³§Y9°mÍÛdû 6…67Þ¾ùÈ@Ê'¹ló¶–MMOžjÒz€ïΨ¯ Ö¾žæÍ£xGéÌüzR$-ñ±ß!9½¾±yo{ùéß‘|ˆ«ÜÖTd+ÿx 8ÕGò†ÆàÞåQ±ŸÎg‘¿²=ØX§6¿ñ à¥Â3)Ô´«™pÓ:ÛgÚC;ëBY_¿=H&QþUÕm¨& ã¡Æ¥¼nÜ!ý“C€ S øßÕFôÖ0k|Éú‚S÷­Q~ǾÔI ýkìG¦ã$8íy,N¬Â‹‡°/“. ;ñ,æâ5hH"Ýcx"2H?9ØHçÏ’œ…BœÀ"ÔñÍpaO&#¸y¨ÀZ¼€s¸ÈúÁ0A4á(ÅxŽto£ÿf-¤OÆ,äa1–‡Ý³ï±|vKc> k° õhÇ%6…?Àß„Š¾ùt¦
+Û¡P¶NzÛ‚èY?¡ÓØA§à'ø9^ÄGl.kf{Y+;Ê®òdþ ÿ§°QÓÅÅš@1Ê.‹Ì" KàEV`%庆özBº Qåö`/Zð8EoÅ÷ðá;ŒS´;iŸ¦Ê½Dû·ø
+Äly?~L¨Ã|uj^“4º—ù š +üghúÛè®o7®ý4 i
+ãé¾–âðP¡;„í4Ñõ4ù%4­çéÙQåÎ]˜»`nΜٳ²gºœŽfMÏÌHW§Ù•¦N™lK›”úÕ‰)ɾ2Þ:.)1!>.6Æ"K¢Àœ^µ( è™]ÌT‹‹]¦¬I¥è
+©ŠÆúèJ ꦌõt“ç¦û<ÝÞî{žÌªä#ßåT¼ª¢¿ãQ•n¶®ÂGü!êWôþ(¿<Ê‹™Q!‘»N(ÞÔz¢³€âÕ‹öÔkÞ€‡âEâã
+Õº8—‘¸xbã‰Ó‹ÔP„-fQ†yó"1‰„J/U=^½Dõ˜t!ìÕË+|^Ín÷»œ:+¬Q«u¨ú8GÔ…Ñ×èr¡n‰¾FÙb¦ƒ6%âìÑÚ»­¨8jÕÚàŸ.ýæ;Æ;ô¥ªG_ºïãT—³›ý¬Ò§Çv3Tú^E©Ž”„=?yj‚WÓZǺ»œe«|v£zÛà*_¹³Ôlz½©3N¥NõššÀVEU Ôzmk€Ú¦éXÕbïJ+u¿j|ˆR¯¢UúT»¾Ä¦úƒžÉ‘dh«ZΔ¸•’±—3b?\ÃHÒ¸»LBâh¦îž-ÊEÝMŽP‘™ˆÔj¾®Ô(„ħê<#×üW— ­&—ÜhùÕj U& YóÌKVUÑn€Z¬ö÷Õïjä ë ˜¬9÷†‰ì#¼îpè3f˜3`)¤¦²ÅQyžË¹G/SCVE/£’¡ÜG‡üyÙTr»Ýì_[·Õ$èá
+ß°¬ ÚÖw¶Ã¯ó€i鱤T™–ðˆåÞñ€Jƒzæ÷LŠ“yïoœuâo}žÎ&þsÝ°.†W‰ˆR†VîË jm¶Ì€Öî§ÖÑ%Ó´"U)ÒZ°ÛW«ŠUÕ"eeZÈI©Û8×fÓ‹Úýº5PϨ®zÎpAô …>ÁÆý÷ Ä•­VË*Öùr£}ƒ L¾2HßR1—‡>úOÌ(ÔQ‹?-pw‰õà1ÁƒýZ¨’;P*õbk@¯Âx•qQhÀ"²?E¾•<ŒeôÜ+ÍBŠ‰Ž¹ˆV- ÚBTK´‘ÈôYGþ»‰ÖRŒ##įÁo©BPj0Äk°I)ðËL”ܘ-†±ˆøGåLç½pðNcµt ybâåN$‘-Mvc#ùe”€B/–‹×Œ;R'2(æg–£Oê@œä¦§ÅÜ÷ óî0zéY ¸Î;ñå^ ¦`šP…¤/4Ÿ¼ªE"駲^ªQ/ÅïÁ\zÆÑûÒI¯•¥
+×PDq2(ŸT¯\’íBq]ª2®
+˜Lñ,T¿ùB/³‰` g„òÿ™w”(o3ç‘œLü&¦/$Âv?±^cpÝ…m Eq¦ú`t šb¬¤ºæ“ÜLµ Æ¿¨ã¨ö^“,½˜Jù™µ2gäˆÜk¸‹s¶9D‡£Du¦sÙ;_: ÍÑLÑ 'Õ{ÿ—ó*꺢÷ýÝxÌ,Áƒ±!ì\˜P;ÄF¦ñ³jNÁÁfXÇìPLlS
+¸ÄT•HÚQXš¨M“ ‘•.ÃÐ’¥e¬**jUãJºØ(k³4R›ÀôÜÿg2­ºŒtþyÿ-÷½wß¹÷ýɧ"öÎp¦r”¾Œõ,‚¿Š¡‰<VÙDËpvwØ>´0m.@Def}ÅóêÝ´纈ÏÏ
+^;ï5Î:ËøùmŒë*ùÊ6°ؘÅ1úåuÞµ }±N²Ìçvô/¹QžÉûe}ÝåÆô ›GŒµã3D³yì»í?pvM÷1b›ãËáôßxÍðá{þ$÷†ºyOìãô¼q>à˜‚œ86ïeü¥³YzÅŽÙBŽ‹‘ ¶¨iäª3¨ã|‚8Éœ[líŽà¬³Ì¹‡ãŸ>®Î¼—Úñ ­ÿ·lç-äŽìùgµ‘ei-ΞÇ"ÇSf­‹ 7k›ÂíœsÕ·ÓZGz@Þ¶ÒêÊô9fq.ý½lÞe©cŽÞçóy°=ö;ûØŽ»LÎU¿ŠœÀÚç3ƒ.Õñ4ËÞo ÷ÎvÄò­–G.íaZÇã¹^þôv‡ä«T!ÎÁ_ÔÈuxß'?N.ù=i·»œ>òw¨Áî÷}äÖÕT „é9Y£‰vûuä±³ÈU7ht å߃ÿw¶y ïT®HÈwqGKl×ó0×Ùy6ë}‚¦Úšæ³ËÆ6t¥ÿ0}]ÿûÏÃ=òönaïÎ}õ|Åþâ±³ÒgÙ–‘O¤,¦©ðGÓõWÆ<Þöæ³ï'ØTßÏøJ‡ízÜÖmTjÌFÿ"Ìs¡ÿ<ã=´Sz0sG-”7#o\ 4ï²cL‡óqMäcÈ;ב3~N.»Žs¿Ãö}±
+öMú'¢dí‰ º©è·È ±—o1¾ o[¡íS@°àï¥I¨oÌÞ8r ßa h-MJe‰ ¥fRšr¢½Ò<´¥Åɉ5L“;*ÍpžôTJ2¥/€+Á¥ÒD›'&bÜ­8ÑÆ8{Ôl-DÑL<¹ÎLŠÃãmB¢¹ 4ÞÊß\h®ê°Ì³­gÛÏÆäÁÖÁöÁ˜ü ©H\¤í°W(.&¶›ÓâS*,@?áD¥y5)ÂÇ+Íê@Ø+”€a@!žñLLQ¶J…§?Ðoõ7ôw÷÷k»ïêWý‚.y.5\º4|IMŠ|Ëõ\…ù]à°8ì5°P{5°Ex–öÛQc-Þho¼w w¸W¥^Ooy¯ÕÛÐÛÒ«Q¦ÐŽvn5¨hÁ‹’C{Å#ñ½©½õxzÊ{dêyµg Gö„ljbÌVŒoùn<¹Åe|Ø\Æœ—©X ¬¶{
+pƬ¢>‘êèêîS¨¯¼Oâ’ô–øP¼C3`bã›­•æ†5mfURøŽG3{ßW¿v '3d候G*Ì#‘Yæ†H…Y½-r-¹V¸êp¤Ð¬NJ%–oÍdsM[©Yµ«Ûcóؔߚ‚“¯nåGÅ<1©.{5¥b1µ%ÖëŽEcJ,‚ö¤P›„å±ÖC‘6sP•ãŽ¸7Kmæi)"â‘Td 2Ž¨;·ÌBÕFÏÆ¡°K\À_ÀžB\€Jò°· Ö tÚÈ=9ž\O^ 'ÈÓL½L—tmªYåÖÊ´ÕÚí§Ú –Öô2m±& JtÂ$©ÀT%¦ÂdÍ*S.“%YÂÀZi«ôŠtZº&©4o‚Ðç5¬ù¤èH̯
+x ø1pˆ¯‡ƒÀà`°Ø´› @+šl»ómµ84Ρ‡Æ8ävh”CªC²ƒÀ€ÓÀ/×=ó+¼9Þœhx”8B:4ü²ý¬µŸe–¥G¥G_Ò£/êÑgõh·}Z®Ö£Ëô‰F‰04Æ…Fá7Æ>ÃcŒ6\Æ(Ã04C1$ƒÒê㩧©¾5ÿtIK{by\ Ö‰¸¯žê—Öˆør½T¿¤.^ªÇ)~%^ªç4DšŽ ñífÔÆ¥o%-m–¹j_QÜ7»é$Tô̾狘£ûžonö‡îÿÜû"êvž„æ6ÐÍ‹º¹VG]ýTE¹*zQÚU"ÑFmõOío™@÷ÿb’‘]ænXR‡¹šŽT×<{…ÃÇ¥ÜQXuKQqsßÓþ¨½…šâ‚ÝE§ðgã Ê 5Ç]ÁºxÀMÓÂÓÂÜ„€á¦Ñ¨vgš
+v×odš<¨öŸÔ
+Q—½ê´Ëö‹à²èè …Vu¬Ê¬°«ónùßo££³³Ë¦®ÎŽ.ôut±±u®³Ë¢“âòÜõIiìÜõqkK<œ×¹¢ SQœ¯uv:¾;fÅæ¶à~™»àœ{gDˆÿÉÄžñ,C~AË­Ü$ý¦ðâ©7ä“"¿¶w>ð|@µµxN/Ÿî-öN*ö+ôy@N}n©ô”,íJ$¨E”OÅÔpÉ”´¦{ÝÌuÎiôùeüa·ßòKä÷8€ÿ¬ÐÍŸöë~°Äë›Y¶ò™mWvÍ,ûºwfYçÖª½}µvzùZ°¤Tž\:yÆÃTVøóÇè’[hzæ“oŽRõÑyô? ¯Öà&®+|ïÞ]­^^íJ+­Ö˲%?µ’eK–-[lcÙ(ËòCÆS…úA†1ÈP<Ma …0”&<Zþ8!!aâ }¸OIÒ™wšÒN§IÚaèt:šf::#Ñ»‚´ý×ݽw÷Üsîê¬îw¾s.ßXé òBµ×׆ÎÊ.X)*ÉC¡Fï[Åww|.ÿsm{›ÛÝÓ=ÿr?¢¾ÔÀ ZbŽ2Àƒ»2s™À©â<Îs\9@,r _£O…Ä,v3ÏæÃaìoˆùÁ¢ÿú†œè<E¿ šêÚÌA¸!h Z¼ua]¹`õ¡úXk›X{ªøXyÐdŠ¶¶ŠC;ŠÄ?Y#á*ÍìàŸ±r•Zò4e·i9NF–[u< ÙdÐë ¦L©PÐ,»“ñX‰x~»žà±Be/7+-Wä œ ¡WÛƬMeÆ+­ÎÀÁr¼ž–!‚•ÑÈÇáˆ{d9Èq<WÏ–˜&$NGiu"‘H@aqn6+€ˆe©aUD:
+‘Èѯ,K2>)oÝ»ºè5J7Øh”Œi¶¤Â«_ O{ü_:u$µ€Þ…œz'ré¤æjèj VŒÃÍ™¡ ¼;”É|üp¶o ƒÏ§û×Áâ*<;?D·á•â„Ô2Å=™b^’Zž/î…ç0ä‹ÿ@íTؾ N‚+à̽çuTOµLSËO>‹±-ûv!O¶]¸H"ê“mmR‚×-QüÒµÔF$Oû_81O¨]†¶˜WkFòÀ8oIk4<ñÊ)p•<½SRi³ƒÝèôø|4<‰g’×^^†Î›åk–ex?Öµi-–L%'’3É£IJ“„ÉäµQvM“Y½öò¹<çñ\»ÃÆgâGã+q2ÿ,þyÅÿæËÅr©Ü™y/sâl¶0›Í‡E. pää¿ÈKk€‡¾ÈoÚ’‰uööt_}û­ëo´„š~‡­œÝ4Ú2øÊ™³ç^=aßôÌìÜ F“…Oºôúkw/>tp^=’"{éø u™z”"ÿ¹†9­€¡ÿ šÂyNGD㶠„Ù±r±´ä@²+Ù¶da‹ßWŒB—À±…ÍM!Á ÇA"üÀ±"Ø žF´ÇíñB ô´Á Ï44>õO»p„…‚ÍMîfÍ@Úl–&”¢N0è$ë`³Îjò¸]´@ã—ðž`;Ä3<ÒxlhŒ"–Ýøg 7°$aYCp³
+²öS¡ù}ZYå1š|ªCp`À­´(*Í:‡‚X,4A2åxɧU/LèµJ$ó’–]rŠ¨BŒazªRhˆ‰öœJeÊ}uå6i@Õïòg4„L"ˆ6F)5„+}½eV£«
+A‚†m]O+æ]ûûv^rÊy?Œ:H’€ ÕÙzÞãWrfS…ImèèoX•&÷Ø/dT‡ÅÕt·îù]¦ßQ·¹µšz†9Šh¼êN-!Ê4åœÏw7´¢UÊ`e«µ®^¡<}¢Åæ¥È:ut¤â@NŒ'|/
+Q}O~,{Ž2g
+Âÿ£žVuô¢ þô¥(+nºû¦êÖ.ç&¿ç±Íwu¿X)ÌÈ™ÎÆ É¶ÎŠzòà•ÜŽ‹ÕV¬9ì2Î(°&Ñ?ÙPÑ°AWéPý²†X‡ˆ
+[‹ï/¨péέ×öŽwgÆFÛÎ5$cû?¹1ã‹Ó—Þ¿õý}ã]Ãcc‘…³ ýѹOÞÁã¸=ù u›üÞã¦Á×`glªñîÀ·ªôáÌ”f2ô=ªÊ2”rÊdƒéPsUá¡1Úr×O>vB¥:‡m°Ùf3›Sõõ©“C“;'×'ÉÉëŠÔúº ʪ¯7*Ü6'²›ž«
+…‡(“¨Ócd?ÜŠ7¾ï"Ô¿Íô>üàáã[v»·¯®( O
+ﶽÀ ½ËðËoOˆ‰hbsb[b:q$q:q9¡Hàõ›•1;›Ç|Xx ±b>Ì=å$.,ˆJ«-d±ÖDãa6+à© .¨«S™›i·ï[n‡ 
+ÁgP–È•©ªrqê¾tÚ¿@'´ÕÕÅÒM¾:ì÷Y8 Ÿ1S‰À0
+ê2%ƒ)¢†&ß·º‘ó¡÷çHöë!ÑeØ q;±4<¨
+ãeU‡­”Õ_ë=á
+4¶7,O[2ºO^ºìäsÛÿ1ráã/7HÊ´Ö3ßíÜŸùË…ƒá\ˆçRü9]ÄÚQ›ô^H
+y£€Mµ©‚F'é$ŸP›Ô¦`
+Äv ÝCK¬†*@õ°´¡>D¾á©˜/²ý>?7ç˜sÔ¦÷¤žé{–_æSqã‘9,öÒ’ãö#鈩dƒ õ¡æÈÓÿ»zRØ!—a²MZ•$°AX£ÊãR`0æ$ÙÈÖ”QL„GLœ‘Î à›Z\\–Æ2VWØ´ÖqRRUbL±Å!@…"nš’eç^ÞÐø‹=Ï‘#Œ)+5̱^‡³TäJõÇ—x»‹.³,û'¼>Ó®žò%$áù³’Ù–+X—uûƒÂ–*“ìð°)™åÞ©ÓžTݬ)ôa“"æSö”A†Ï íÌ{Ìw‘€ZŒ(¬½Íà«œekzŒ7bÌ/cþü‚&t ÷„Ç‚É/ì. ?&…á|`Ö„QŒØúÏ¡¸± —Ê%
+ü3˜t[ôR†¥8™Ff{§ÄvÒèªÔi‘‘µ›ÿÞ¦t+}Ê°rY™TL¥M¡”x?¨ŽD”ì £,II:ÉéÊ=IO2Q€ ð ýãŠ5öUßÞVž.¤êÂÁf‰:sÄ)üfiŠoeicÁ+‘ ,ÜÇ-èç¹¢·é7x‹içév~íGô#2üÙ¬O°»2-ùÌ‹ÀfJ¢KjK¢u«7ÖÅãz<¾?óý
+û*°@BÏG˜¢¶ÒH¡iøZž¦éˆ.ï3T9î¨qCÞñ#@G¸銎ùú ì«_n„i¶eá¯ô0›ñRQ¿¾,ÿè]1ÙàÑæfÊlSÇJl¬CÏ£a( t}ˆ "«á6hhŠ‡K¦á]¥#üKDŠá-]|ßýcIkÚºÞºÃÊXÁÄ»æ»ömé‚W¿qQpõ.¸,VÈ|@l4 ƒ‡ó‚…³Æ™$@X•S¥‡ÅüîFmwuϵ‘¶cGWìºY9‡¥ECí顜⡡ CILÚÙvò=^2‚µß½†#¯·¼uíÐä¿;ÎìïÜv)óÓÌÌÂn\4'¼€(êO¬U Õײkê¶ ›ØÈnÄÖœ[eÅ4vM1ÀGá×iØêNÑ6i£M³é¶Yc³UU’„Îϧº ‚Ä¥­/
+$z_‹¡Œå¤Z‘|Ê—YÈ–6 ù%”`ˆ‹º©Âö¢%! ” EQ÷Ýf'g¯4×nÝdQ„<Gñ¦‚¦ÅŒVÉÆY%·\'l*ÄÌg/û.MHf‹Éæc«;MXª|ò­ƒ8Á.0¼L*0•qî[‹~¯—ÍÏF©ñè¬:¡ÇÕñÈx1트Š]QZæe‹,ÐϪJ4ªæå©‚Á`§JùÒ(Í•¾‹YÄáÛð?o_§¬ ö¬þõ
+¨È|Wÿ¾ù.@i`fBÍÆÀ4?WO ›Ä•|&\"«$8#:É„| …3ÎCúÎ&—¬±¿¨ÿÁþÝ [{^ÊYuf]æã?_i- æâ{×¼4s´gy[N"2XSÒ·ùýØyþˆÖ²é¹³¸õÍî_dj¹aWa¨ýÃK#ßoŽ”ø6®¯‹·îA @É…ô›“:´½§ïlnln>^”êjÔˆZªëïXï” ºÝ^m°åN,æE¬÷¿LWmlç¿çyîÅ>ûrç8ö9~Ob'Ž¿Çbûœ ”@$ÙÜ$ƒ€ +Þ›®P
+M·Ñ@Tº©*/kÔUt]#Ú®atjÖ±² mR?mS§‘i¬ÒÔEåÃ@l%°ç9Tîî9Ý}xîÿÿý/By™Dë
+>ª¯¯wÙì2¸lŠ²ÍÙ ÍÖ/ù¼¾„¯S£§| Р‹[BP+'5–½µ ”€,'–˜ Lj¾v­™T§¹GE%£]B·L2öˆÓì…†î’çÃÏÁa`_X£õ
+•}x[uÈ6å, `ŠÏ@ÆÖ1˜–¬6Å5 ºÔÝv}¼º‡ÞA¿D#úU]}ˆL•yü™ôD¦Gá›aŒyŠ ËáºðPøLx&<æ¢Ú õ‡AøFZZ)m”4åUbŠªlU>T®+R¸e2¦L*æ]M ´ÀX\ÄWlñ‹&¢žÄ (Qi¡(-`šÇB`dç§ÐRòÓÒàp—*HÏ€Ö32J¤ÃK½Ä÷˜÷!Ñ`¢µXÔhVn0†²O¶;u½:²|{óÑ^‡E'”Ù; \·+Vñ•»¡‘±˜-Õé5ò÷÷ºžfÜb¹6Dù'Ýv½óƒÀìaûÏFß<¯ƒ.“U,+üW/»akŸ¯±PeqtwâζçšHžzøÚGß¡
+Ô®÷m6+ÍúMÕpVÎLÆà#dbÂvZíBÀêtò©;]ü
+<šò„^\õ½ß_mÛöDV[‚/u¿þç_}Ýx¤f"_¿½k_âÅ-Mk«¬ŠÎÄ<Së
+îß6œ:³ï¹3Û}85 `EHS?T‡^qš2$a(©­D =Žÿ: cü`êd
+¦é^£¡GO€ xâÁ  -Óut†î ‡è3ô =Gë£Ú Ñ7<S±¸ß?§'ãâðÝ8ˆk¤±ˆOQeÌK‚ðÇâ-Í΀‘¢æ¦+` G6\󄤂[*Ú’÷Ó2G©ôðF‡740qö¯:#oñ÷Îïh?žíʬmOözËt¼Îh°*5Ï~zóµÖmôÕ®'vž~H]}f«^y€BÍí£ õñùO{Öìüݹo©,âEIÏóó—~þ¯Zâp<ü'èDvÊHÙ(EÕóÓˆ“¦­Z¼Lm4kyP
+ü‚F2tÃÐ4D€¥Ð/Á)ŠÁ¿môR Y
+DãÀZPõ˜ð
+©ÙôÅŒQ¼Ècs÷áæmñˆ‡çÕÍÔQ@4a²Wùr¾ Î×™òÇ7ÉIŸêÌ kÿ»ÊçÔ<ŽB¨‡å`i†ÕõÐØAÓ˜Cfhja•? |ÁQ@ƒç–Iߦñhxì·ú*ÿï:íHFUz ?þœ¾ú¤‘Ù‰7eê6£x ¬S›½‹ßc}ÐÓÔS{&¨R05£Ï¶`‹Å`sÿ|¹Ú}S&^§øQõÏêLÒù•Þ
+«¢ˆÀ{€$ˆŽ˜7e™\¼mmV¡;Úà3¸]ÝÎVÙ›WX’/]§†llƒ¥Lbh¿¥òè¶Ãf·à4 ¬Ù¸«Hær¤Üt‘o2QPda:k¹ð„‰^œÝ¼»B–£Öì
+ XÎv$Ÿ3#NÔÖPŒöEbz+·±²£«m*øo·.÷µ…ó›Îu_q‚=Ðbš¹åckä°[9Óuz»!¯7ÓÎÜc1m2³“c—C¬®åÆ^døí ·™F³ñZwqGÒ–g—Æ°×{xå«3I¾PlÙ_]t¨«–w™”TÛ?ÚëAåµÙ¯™Ï@McÔÀkmâòü‹h¯“ã¼^§I«¢ZHÆ¡¯«t8¡ÓBÞ<?Ì[ÄH~¿Š›ñ^ü~€Ù1ŒðV’¥‰–úÊËFËð;e¨,¬·^ƒéHjÂ’.ÑéAdnIz‰.T“ÎMY´ì Ÿ Kàéi—UFWEë±.ªÌ»f”Ås¦âk#«+7œèŽ»»/Üœ·jS—ß ¹‹ŠíböÐõóoOŒ´?sòWZÎ\g>]»NÌ¥ñÐl¾çFoíÌÜ;ˆŒ‚ïmd¸lþb=c•t½ñõË/|rýX®TK¼Æ9û€ý=+w¶P_NP-³/ÛÜñ–ñÙ¿_v¸ãq²+.—;®•—^,½â(½Qz#ùE©i• —W"q-TŠJáò£ˆAԜޱr‚rk’_$ï%ÿdŒÉIœÔr°ê‡RýN'¼Øíðbã
+<à²CÖGµ
+CÁð‚ò™r[a•q4qe]5"pè.Õ*þkjJ‡ØtºDÿƒÍ'å"N'¥‰t¦K`óK¨’9]\ŠbÁ )O$3Fںȼµa°6üTòØ` JI8t\DÒ¡*Dg¶R±¢0—(ˆèÚZ$a—°BQoìñD_©;|ëùgÖ¶=7¶ìýÁ?Ëez1F³kžÝþë3›žXà“Ì“ÑÞÇqPŠCd 9±;ç7%¢Æ˜Í- V™EŽ6”7}øùñ¡o.¶ùÌ<ƒxËUç$öZÒvâØÓ—š\œÁ-[¤ýɯ2æAÈõŠÙ²"ój”Wƒíwš´­wüýÍý/ôÐÿËþÛýûyy?îï÷UTøš£ãÇO| n„ø-Õ|6-—¼óBüŒ>üjûí$ÖNCpÕ’“­6¦»wèIwwï¯>zॗÞ,®ôW§‹‹ßL¦÷CTä‘rµ¹z4]Cdi&>¢ˆîe±´é©Ä”>iÏ“„|«¢|Áª‚$T Jä˜ÃˆUÌ!18œ. Ç“¢Òë
+âÆÎ —ì¬E ±Õ….2ç~Ðó‘¡Do¡,AVäâ‰9VÆ"$ÜH©G_ÈÙ
+ëræ+wÌdcM›-?*™…¾KU‚Õšcù/ÕÕÛÄ}Çïwß½¶Ïø;v°“˜Ø1NpL^$LLI¡\‚ ’Â#)-[ZèP†›¦labÛÐ:Æ*A[ЦuÕ*&Mê"Meû£ªB5´Në²òBÚ¦8ûýÎÛr9ç|þådý¾Ÿ§H’ŒHq «©îtFx¢ûBàÉ’îàoý`j¨ËfãE-’š£ôù×KŸvê7·«"* %½¶·tòßÀ7&ÛÛ±"'2ÈÑhH#ÿc8^ÙÀè‡$eÛÓÕ ‘
+ò·I1©=,ÛŸéÉøÒ™r–Å%®œî/ኄhí‰&gÊd-•B¦UBB²$1и+ɦ| „pòñ!‘¦t?H%Í$…ìCÎiCŸVÖaà˜ u'¥;ËŽ
+Ùr@jŽg#2¯P¾ÓJVÔ˜Wé¾
+I'1›¥¥w»E´Ë+Q…ÇÍh÷ÊçЈAb*ÓLP,Mwg†ÏˆD²Wõwõ;úuZ7ºù¿¢)Yª¦Fª‰Í¥R[ ‚ž  2t ü"SEI6i“t\ú¦ÄH‡ü}M};ûNöÍ÷1}·AÈi°q¢V‚{%š8a&™<z»dæTxc¹5]".çX¬Ê¨›  ‹Ns¾~ÌptØò,íhÄfOÁ¦
+°€XM•gR"ôöüvöí½3¿8¿«}(žo}À;]ç‚Ê6_ì|6ï ³4Óàì®Ñ ÂpÇÓ}çž¿³ü^¤íöï_ûçµOè_Ío®bæ磥?Ü}þßtÖÛÅ`ºÿÀÏÒ<…Ô¡
+„Æ*E œbæìù5Ž+$9Goî:_ü9ÈhÇ»Yù‚¹ÇðÄ[ÄÛD3é{÷ܹ«þnc3&MÖÀ¤é'ëŒvZ—á'÷ðg‡Ž• uˆ»h\Á7®RUzÓUN»T%•Ÿ(ö_(Ž3…=…ïÞ/,¾,¬X¡©@–”5ÅKsÑü̈»Hï)r¿Q€‚Y÷Sía%u"+]~|Õž'º™¤+S±°üOk ó®*‘v7¯o±4WÒdEÀ˜W&e4)Ô0LAO•ȪңeX¶+‘»­ 4«ã_TKÐU`Z4b^KóªYTajuY²M ˜À‡˜œLä6ö…9…:9GGÜÊ
+ÑAä3<i$ŒVÎa¸0äÜ~Ë!·ÖMºÆ»¦º(o1Y¬q!<i0
+c¢)oê÷#¢%Óô;AKãåYšÀI2ëR:[ƒ
+lv¸ªXÄÙH(êp^\ h4 äs4»æG"žüØ[¤ßm—¡@24)zü1w«JD«2ßúÂàI¡å/¿vªopW}³ÕÕÕ«È6™ƒUj
+b®ÀHÖÄ‹¥ž·‡ªÇ;=œHEæ9Ô,bîÊÐò¿È4Ú—-Ä3däü ý£6fÔãQjÉ@.—›ÊÍæ.ç˜è-p7S³qÄ_Ô²ÙÁ€Ö¤‘*j†MÚ«Úeí×$pQÌ VvÆ’ŒkhsÊ{³_^‡šeÚß,gæ.¥š#Tj=ÚŸ8æAËj†Å§,zvoŽ^I<6ýQHìw-ÍÓ65Ö&0ŒËÎ:ý>d-Èšè÷ „Ñ8mÞXHyÃ_K,V1´§ö¾>3Ùv—Þ”[xšÑ›c™ËÏ]úêá?ƒÉÜ6ACùCå$D’rÊ´¦ðîUÖd]ÈSÇña‘+ýíƱý7^º;Xm5àÖ±aåPgH"KÌ
+Bš«6Ü;ì’aÅRi'Ÿ3¾‚et1FæŒ"^:ý ãe¼Ô;Í… âT-T~ü´÷˜÷Û^ÊëÝÝ z;@Ç-ðÝŒMK4%ÆT"1{â䩳s§F¶oA9æ½sêv°=>ñi$íh|ËyÔQYJ§q`!þG*þW:«*þ†Êáy.æŠÒg´_ Ó› &Ì«Ûü‚›¡é`Éuå`²Ê
+†5É@”Eîq}A¸Àÿ¶.…‰'࢓-)”Š"ÿ«±('M¬˜Šëm•bvi­‚ðKÉÛ: ¬1ž±ÆöÛXÙnÝç/¹dÉEJúŒžT©t‰æ ë }ól@dPŠåSµ³·) Gַ˪à¤cN¼7²ü #ŒnБø¢C´JÑM
+Ý:³1¼>[ó¢Ë>¶‰óŽã÷<÷òÜí»³ÏÎÅvÇñ%Î É9NHb^b/qÝ„¦Iˆ³€—:¡PA@ã—,Ú¦Í@$Í@mIF)}ë¦Q´™VUZÔŽ²2Ú2ÚÐujUTþ`ë6-Ξ³ “e?ò½ØÒsßß÷ûùV Ñ³ÁmÃZB€7x[-fÆXd+qïòá³sñ¶ÛýõF&o`8À
+¥¤‹Ã
+m”‹< ra«KN~«À͈F¢,¦`ßù¢éÓ:ÞbdíZ·Z|/`H‹ "ÖÔ"Õ¬¹JŸKÖTˆÿ‘J£ZÊJŽµè…~«ÙÀAƒ„寙˜ƒ¦Á‘pÇo¸ùËc—÷Ýänò»µuðŽ1Ǿ8ŸëÛÇHªTRPíÖ¤'¥!)) K{¤)U?ôí¡½CdžÅèÐÀÌÙ™÷f¨qÖ[ìq»Õ ˇÃN55RDLJ“prv\™P5«ªjîÑî'JاºñW·it||‚5YYÖ4Ò›bM£îA•î±k«ã ½5½µÐ¥?]»·é~b‡‰øÆŒÏô´Çc±d=åÐsñƒúá¶C¬Lˆ}î>Ø÷ò–¾ÆéöéèHÊã¦XB «UËÔ Ú¦nT·«£êœÊªj8º)z$z7J…£Ñdôíè_¢·£Lô¸Y³™˜5‘ìU²ZB©] jÒR"iÇV“Ä.’/é*JÜ3ï;Îé¾Ý,†ï阭C‹éêʃÂþ¼æWÄkvøþ„~BZXX ª«±%`8Ùp_|õ²e-x`ÙÄÉæ-9ìÓ_¹sY;ÁÓ‡©MÆ}+ç&èùþn:%ú“ Ðuꃄ¯æLÁ
+dî*)Ö̽>dd)qV©õÌë¬é-ˆøÉÆÓÀ™šz”FœT„ / ’½ñ@G
+Þ-¶NAgµþå/é?ÓF¢€¨#¾VÏÞ@¢ [çÊAùž rÖuÚ]{þ 
+À¸+¸*ý5#§Žn;1xéTwÐUZ»Fnÿës“W§^l‡ç{ÎMwúËÜÓïõÚlxú$|zÛkóûýžÆ'¶·Æv<æ›Í½«nŠŸù$-h»¦þ¸ëÐäÖ×6¬IüäJgì“ã{Cx&K—¿¤ÖÑ"ÑO\ºH—o‡eżB»Ý·X"L[¼U¨H0Ö³#ÂöÅa&;˜ù
+ÚfÚD  &2@b­:¡}¨Ðj*íVjbê4µR«j%Ù{çPMš”<+q¬H÷¿ÿGL
+s.#¤õfÄ"Ž†®b„LK‘lóŽ#W¯‹«¬å6œ
+äÎÎ7³ß±TÕB}˜4×´~‚>þœbŽ·Áj†ä·Gr»=.ÕêöŒ¯ÌT£¾æ;çûÄGûÐùHŒž‚÷ „´Gp[ŸËÕÊ­3ýØM»Ì>0æ>_E‹£0º ™9³pI®ŸÛÏá~Ï}Êý“û7‡¦j¬L:Ø$à’ ¹ën
+Kj ý´¶•ÈEÐfWÅRÆ„,ÑnÓG×½¹E0#Z6Zù"¼p°¬<_„$YʉCžÀÝ X‹ŽíKœØ¹LÓGŒXô bÐ4ôŠòÔ`™1‚Äì8)‹AE¤CêôLr # •½ÊyâÌþõþb;SYþøgÞmG×xd“7M‡.§ƒ:‡‘Ñ)²U¹
+
+îd‹uûÕ¯Þ³Nž§—)†§”!„ªUTy^ì>w7{èNƒb­‡fÁT^¤HJköÊÎßµuzgY;~ºÑQË›qq‡%%–™VÓä^гE˜×¬X‡ ( ¤ŒÊöf7#ãÄ¿S¼ØŠÝ>·Íí³MGµbqÔî|§ºÓZwwÚ=š¦˜=Ž±ÜÖ¢o o#eÅ̸þwr¿¾w ôéýíÊ@:“íömnÕ[϶RÈ*6§ÜâóñKñâŒ?Œ³?‹ƒxüèhÁV$0
+˜ï>H¨w+á •–j• ÷bFÐŽT ý¹q6Ðý×6ƒD+ú¦b!Ñ©@? ùã€J†âW²\
+möªÔ£ï\Ñq›Z‰  @Õnbçƒs;Â%…Ÿö…´¨sßÙýÏ;ìÐ!n®÷ÙT…µ ‘¸Íþm“sþ­š´zÆ Aû{Ø
+šjYÙ&ÿ]Ö®8Qdç
+\'~4«ÕÒ„/mã$
+È §?ÙÒlíãT–DžF”
+F*Y2ÖCVmµöSx&ƒÆŒÍUdÇŽ£LÐÏàÚI¦5Ö³¼ž†\ƒÃÙÿø•õގ š™jðÚ­¼ˆliÛáp«e{Ç_oZm ¯‘·É.÷~ã7™Z¤ýgö¶ÿâ»nH‹
+MRÉ4p]kkk¿¼ÄÅ]jLj—E;—«ÿÍÕÔÆuFïÝ»«]!$­¤•„z"VxHèÁË2Zó0†¦@±¿„óªgƒ_©ëxì4Lð¤&ö´Žã6ã Óu2M]× ôWH'nlw<&?b;¤Œ›¿2¦ÌÔMÓƈ~WÂûçîÎÝ™;÷œóï|Ú’¼Šÿ¬ÓÃ?º<Iç+SjôŒ´óˆš´§Îãã¢Ñpà6éˆþµ.±ç£Ì¿w4:‚_ú©GÌ#6s´·©ÞY@–‡cô,ð?Ÿ)aÒ‡bh£ZÊŒ!Æ?0ãâ¾»pu/°ð" `-
+àª@ §,ô¥ûäÆ¿L¥ UF,œ=èô¬vÞÊÛiíÁÂ[(lÔÚ¡4=ؤ5Æ– Áêà<ɼ¾¥&4†Å´õ³0fi°ÞVÑ®8Læ˜&a9–a8HÛŒ™«Ø¿nàû w?¿Þ¥›zµ/óÍÁzñ¸ÕÈÁeŸþ½ª+(öààñß3ïýùe¸áDæ yƒ;ƒªUõ2À` NÂàs›U€‰™ÉN4Ž#êD
+CÜ¥î“|–\Ü,†‹'z™O·qgþ»üöóL1ÔêÑ&µÜ=æ2k0chéÝ®šî¢éÚÊr¿|Kþ§¬—ñ¼ŒeYMQèr¸­<5¼á(
+ VÇJÀ²íÙXx.CÕ(
+0>iFÃh ®iÜv{Wa0û¢ÔÐV<l¾0¾e*ú•}£Ñ¸g
+eg>—y“Ƴ!o¢Ôª;¥×å¨Ý*ll÷kõ"ÍsÊò}Á;ªü}…î"=¯-œÙéi¯°M]8d]Ÿ$É"ñˆ;+
+ÕŠuÁ¸ÍŒûW?'W¹fT…ZÐMuÝbSR½aH•^’îH¤ÿH@òhÌKfxT=äÓÉ:çìê²ZV਽ÆqŠ>Ð7L£}Êæºmu³u×êغÁçç”ó-ç¼ó¡S㜔gñÔð›Ê%…éTú•qå¤B?þ¡,+RDÅ«DUy¾¥Õ"W)kNº´ÁG¼¿’|:e"û•uÁX…"&áÙ^†R@çS(’‚ÔDºpÅd¯…Г¶$²ÓkÌf5×JÙ\9(KNH Fs0ÛêDu<˜¨
+Ê8ºÆ«ç"’_ØwŒ7 Ç0]úÖ´q¨³²ß^jð^Ú³s:Ïq±`ÍàИ˜]øع W&û7µy¦òök.Ïl b‘ËÜÞ½·‡syX›Ñ+¥ã»›÷)C‡ºšºpãà/_XPF·t/$?p–}×ùþÅ_Íy,Þ¿>)­w–QGi_ýŠxIùÐ/þ¨cË,T]Áä'„Dà~ûïy°gX`ñ·0˜êZp/¥©G,n)ê-)ºSÄÍ2^5¦Šb¿xP|M<+΋<±(zň8.²âe$ˆ‚*t
+ì²€UáGÂÏüâ7i0}JB:[;(µ4ñ4]›Z¹OA.±p±¿çÄŸõh¨…
+\ì_“{ñàös£ãW|æg­ë_4Y’,Ù¾¾ë·ãíGg6ni8|{tâúžHQÈ«Ôˮֱw¦çbnÖ¸ú%yÄ5¡jB¿Wk‚¢ÍQ+ŒéfôÈ¢°_å' IǘkƧkþ¡|T>%Ÿ“¿¿–¿•yZŠ.¹#Ê_óSáÕÕÐÛÝUˆ°u×L;öÍ›±y²%Õ›bRƒT~
+¹¥Ü1£âQÂJJ™P+åYs€ô
+ô¥§÷Óâª=Ñ`²˜sÚ£Và¤r“eVpT†AQÙ‰.’mVbá©š¢4>ÓUœÈº®*·(x…n 9fj¿t€72§c,“<©ÉP€=¾Ó^‹)ðëW;~³­.ì~1°Ý=Õë4²¾É®ƒïþ3·]‡wE V——oð᦭™™öG-Z 3f^à5Eš~ÎØ0:ÛßóNÜߨµ—êWN7~ö‡ô–˼ýa)ž}õ! *BëÑgj¼ÙŠïäß6ܶA `ÚÅFv•!VeçXâe#ð½Í”I4yM“jâLT˜ƒ z¬Ö‚ëjvéÑl9§†J¥©Eê‘F$N’6¨¡ÎPh<4âph1Ĉ!o(ßlèòˆööö_ZV;ˆÜ¢[uwºÙe7VݧÜçÜÄý,ùœ(dß´¸ÕŸ%%«ÒµTLíÿþ”áø'ŸPÿ?ÑåÓÖyÆñóž÷¼çøîã˱Á\Ž1¶±1cÛg|H¸†
+©4)Ó²ªm4Á6Mj—æC&ušPò!š”eÀÞ÷`ƒ­s°yžçÿ~ÿ@ÊxÚΊݠ$ ¼ƒVF·äî%³<
+—:ñ°lkî@¯[0pé”ÔÙÞË‚Æ‹=®Áž®ã 5ªú¶ÆŽ‘\kjhó¯M‘MŸÚì;¦;Fó©Öj4Í{~“ƒÐækõq*iĪ®jøãk [\!K…«ñVO³£8Ý‹µ?±½ %ÔJP?•ÑhBÛÌ6^M°Wél‚]ëýýŸú~çÕNº82$‘:=I¤-NÍǽ½cæ±#0cŽ_SÃævór vÍ&†ç ˜ÌÖ H.` 06º `¤&ž„Ϥ ¼ñÌÿ–2š”‘Ç¿>s›á¦9ßĉ´_ŠíÀQ¶e
+ÐßÔÚ ª2þâœÆ
+Ê-W¾wÔÏ }ånFk/GœžfËYùÖKï}j¶õ•Õ7Oí™?.{ÎîhÐë4"ËÐjVmf ­ã‹ý -I»)]m v÷©Ÿ‘»œ ªµˆ|WL~œŠìLjûøUSZ*IÝ”ëoUÿ´ꦜwðüƒÚ( tì°*…{¶À\$$Îí¯Éxðó5¿F½¬²åx®Áux"VÙ"N¨+À&'Œ”H>ªY2ÖŠØ?NV.¯êŠ:Z7Ûü,%ñÒ¼´$1²”’&ð)ÃK!é] JR‡¼ëíwñ*Ì=¿³7
+T²Pjt²pú4¦íò!˜‚…ˆ·$X/!Š΂²Š¨ñyIœ’~JÆ3EqŸßˆ¸=‰„ÇNƒäªÐq:ÌÉ8dŸ¢º=‡*ôß®®DZdaòN‰fQ€ùsM(&Šñð–«Êù½~„lÈײµ¾µê©2kUbÕ€á` ‡ *Z` Bp
+Nz2ž907w8þåÌ(™éñÉLŽÌô¸fqüC6-d@æEiÊz½Œ
+gÚ3-UqÖ:㞉ÎtÎ0]3^Xèð#bd(#+à_2Þ=©i]¾2ïË'óLuäW@ƒ¼ïBúíôûéëéµ4ZJ¯¦‹i˜þ¢ùEçbhQ^L-²‹³®áÂ2¦Ë%0;€¬Øá}Ä+¤:·´ÛÜ~ó.F{l„ø!Ixñáß#±<în}$76î>vǦÜ&}Bû Á
+–.¼p‹ä¡H];l±ÔE»Œîð?ñI¶
+`k´sÄÃq²ÏvúªPd'ìFì$£þŸñrmê<Ãøù¾sùÎÍöññå$Žc;vr’ljí`b'.!ŽIœd ÁÓ$å2BC„Ûº0˜J3M°Iƒõ²‚2m€¶Ž u”^´"„P»L£•¶1*ú´SµTF™&šdßg'£N¶}®’¿÷yŸç÷>yÙ^ Ø­yT
+‘¤­$=s×æÚÐç@ÞØg‡Ì£î- ¡JÛâd¤äõý¬Ëà*“ ª Ù×öµ¤X“§ÉÓÑ,-e¦À½+d¶uœõ Ö¹Õ…ÏN”hŽ}Vøƽ핺ÎX¥™‡Ñ-"k1Gs%ÀZ…Æ"ý7?­wdN’U1¤±}•é‰›Ö1ÐŒu«’Yá ÍB8Ú€ð3EuE{•ËDÆY2Þd-èU‘ed$rºx g¡`^´§¨ôè¯E9;æ3¬Iqq|Ä©•Ôå¸õSæ‹i` 5 Þä€9£Ýz2"-C¡x6p.
+ƒ±A882R22½qä“4ð¤ï¥áX¤ÉÙdDëÝ7£ôEãhœïâø1~’?ÅŸãÅ+¼‡¯Á/ñ3<L<ˆ‘MŠ'%ÈÕ`¹2 ÞŸËd<‡6¤s„_ónë˜+ˆáòÕ?p[ÇæHOk $ò–ç¥Ö cÆ£Ÿ=7Ä›µ<ãh^84)nZ_.ñ›`Äc aÜÄùÙ2“> >¿üü %ÆaXþw >`>ò˜rÅYnW9d°û$ßêd3ÇxÀo÷6&û¶Wg~Ô>Ö¶{=°ðá™CÛT͆:ÉPÑ É;:£ãõ8%‹š‹Y0×.|Ü=ê.áp ²l]¿îd
+²U¿ê: ®^]ãýøu¿Âñ¢
+Y“•†D¸9¹ø€ž¥C”‚Ñv(îáa„0 °ŸCæ~YÚiÛ^ ߷ݱÁ6Û´]…ž¸tM¸'@áüíbPLö­c¸E}IÈX¡j¨.Š¡¾ö( ¸>s8Óçö,Y@nš±„ò Œ9ŠÁ‘Z ÀÒxéóqGÛ“ {›7{çñÂ>8²»g?x{ýŸn~aSË‹Ïüüø©W»*©7Î=n^[øªçwÚÑ43}åòO
+æÿ…ÿÉ!ªžj£ÞGtgÄ «¢kš*¸x«:ì±`Qj rVjݶÃ=á¾íþÌýo7‹ÜÀâýºÌAÑo#L¦žSÄh¿
+ºã goèRUotZJ&2‰«‰÷Lbh»w¿÷ïï‡Þϼœ÷85ªŽÎH`Lš”NI3Ò—[#Å¥.i@"‡8)OÿóxAˆKÞŸ[Ž'LþßÐ,ñB¬Q ¶¡ÜÐI`‰?›
+ûö*´®ò¬ìM2ÑEj£'¦?d
+ß Óîp,œ …ÇÓῇQ8Ý3Žµ˜›7ãhRÞ'‚$«N‚ Wbyå±;d÷€: Y¬&@€Q³¢¼ ,%Ƽ²|Ú,‡GȯK˜p$^úú3&'hr« —‹î,ƒ$20’/…ã¤R£û!·ð q-Ãœ@f<R(©¡Ì{£gº·'}Þx:¦ ªCNu¬î>ŸJÐ¥]øc·§¨¼yï÷¨¡ÀŠ]\`-
+"F‘e…"VpÔÿþ¥ƒÈÛüîÎKÿÔ’y~WzóúÑÍý“™ÓÏàu,[œ‡èAª•º¯uü—êªmã,ã÷Þ§ïÎç{m_üqql'NÎŽ/‰ûjÇMR_§u?â¤M³ÖÁμ¬KÓî£çV´ŒÒ-ša ÔN¶‰†Bÿ™¨Ô  ¤jj§ý·Ð‘¡JE Cd¨Pai ï]RZì³ïõéõÉ~žßóûHôcêˆÅTq! ÑŒH*¸$&©êê¤J@5¬âßUßQñ»*Px‡Îµ3Ãå‘ÊQÆÌAã$ÔŒ÷°<Ìãù8¥·¥°NÐÙYôN§.‚Åç öï,>ÉžG¬ÁwZÞ߇?¢EY…GãP?Q¯?‡´/QßðÞ¨–ÇÍ‚Ã>°Dzf;9°&åQv3ËŽ
+[x‰A6/Ât“ìàGÒ¶ÀÖ‰<Ú™B3p%çu;:z¤Öœ| ¢ “ð9ÉÁ¨NÖv<><ŸEæÙa)VÐÁ£e ª¡Þ] õs'-ŒmõB6>¨v¸ý¬§V/}ÐÁ1K¢vóGsfï1Tûúúþ&1„
+ËÁmØî¦éð»o¤2ë½nž6²[3‡Ž³è¢¯Š¼a]\êSQ\3‡¬ŽÕÑ#kN•90¦œvZÜä6¯'†ZFÎ4êÄÿÈN3÷dÞèr+Ƕåb‰Äƒ÷öî~%»³Þ‹À:‚g††vƒ¿&ù•×2/?Ë!ýå<Èåû»úG²™§Š……-oúé0˜zj×`o$23þBjìký ›‡•–üvêÑÀú_ñ*qñÕaÝžêOéŠ%ÒwÔÛtCŸ ƒp¸õp”ïì4]Fˆi‹‹\ž‹=™Œ®÷
+Ð.æõ/tŸÇ›sÊíí•2AßH­¤ÖR÷Rdjé.¦r¼ã™¿mZ †P"|Ñ‹bÌ…èZÿ4
+¢¦QLs?B?áù Ååâ".ÅÅâyë,êÅÉbmós¥X¬¶͈r²ŠÍ —XµÈÿ~³žoÁ¦KUÕ\n”UwÚ›M…€ô¹-Š7_–
+#¿˜ö¢6!|Š– "IF®ñ £ K LŠ*_²àjv 9ED[í{Úå#~TsïôЮo&(„E ý¦)žÃ€“,cïb…að­©v/çÄ¡ÁìÑí£Èðä&ÌGE‰ã¸û{¯–ø+‡¶ ÷(}ß×BÞ‹qžDÅHhvýpmß·!AÑÍ·µñTĶò ›ÆÛJ¥_qrÒÍt¡l’œÂ–
+¶åá UØ“)+å$-”Ó;á{,Iлpzt¥
+ü… P•åñà°›ˆ‰Ž‹}£±—cx¬þ¬{0ÍÐîi¦AmR«iËu^Z\Õå–"†ìÓ2O@`|™©Kü*ÿ2T ðú{úøâ8>ž¨7«'ê¹Ü&år~4h¹\óåëCuÅø%Êb ¥>ŸyRͦW ×=K'$Æj ©÷¦|lÌ×fï£fKÓh¯A‘9 7öš "¢$
+¨Tfø¹•y0ÏO–·•Çho¹»¢éò eŒLœÌŸ<~’œ©ø5­äŸŸ3 TŒ)qŒ4À+º„—ò¥ÑÒréF‰2Jk%–@©.ë®o¸€;᪑¢¯¿âÿÈOú G`J^YV®(7*©Ê"Z¬(k
+ ­ yYò ù&y‘¼F®‘ùf@C7&šAaÆy7ŒEãžA ¨fuR_V‘3´°eBË$t%7O&ÆšÍÍÃ[Ýù([:ûøÉÄœºä¸iCO3 Õ«eÙ3øx-ÝËd£}„•3ÓÞt¡ iÂ#PzÝoæ1L"H¦³)S&ÒA`݇!hÆ[b©Dq‹‹&´»,ò6qGNÈOÏ燹óhŸ#wV9#rNšÇÅÈöo'Ï&üñ½­½mÛº\/î£öGƒÑMýÉëþmÈ YÔdÏÏÒ†~}ÙÅ9UŸ
+ÆrVܾ֯/«Àt˽ÎÄÚ;»He; ûœ•£‘ha 7å[_§¾C¸ý4|]ÏíØ}°b€ê.íÛ=>uošùtçÊçê—;¿Ì|1ö”)Šúµþé™ÙÙLK6Û\6=5åUf3™iUq©ªâmÎfG¦¼.Ò›Q§²³J³×´¾Ü6£¬uh9´ïuàC¹ £c,ŸÛGÚŸŽáÆ\uE.b V³ ǃgN0Šày {°y®ù¥æsÄzy”
+„
+¡+!šÁÐ'‹q#w*jŸ:¤^Qi5#n lCÛþ4I‹jIm^[е«ÚmÏh£Ú·´¤ã–ögû5mnV¢$.ˆ”ø „Že§@¡¤)(-œ,¼Zx³ðïÏ`á“{“03929=9?IK“ “h2±Q- “W‹IvUÉ7\Û %ÌßpæFHzøú¯õôŠÛ¥ëšùàì’F.ãÝ$: U%D›·@ÓÒF.VP“ԭФsÚÈ·Ft5‡7ª«ÇLSª
+™lÞˆ] xÿTlø{#îšÊÛb£D‘õ~$•TfÝݽ1¬p´ØWbQŽÔî­ŒôÕÅ¥uuî'}q’ˆ¨G+Ï|w}S#¤?ÄYo©'D‰Î”ÛvjKmªÜR6WIÚÙ·g
+EëÙœu` b=ÚgOå6…a8WÃ
+¹}9š}ì1œÕ‡© *ʘê+•¨r ¾ WG“-Éžä“É#I$aò¯Rþ†:žÇ¯àãëøf+ð4FØkŒA062†Æ »?“7”·HCw eb÷YgÉÝF¨åQᎋՙ!Ý•žV$Y°ªë#YŸ‹,ûÿU9g°Æ¢Ù¯k N.“>%„-Cyå"+\÷ƒ]1ûô!êK¬²çþ¬úl‡"²Â ¥‡LcÖFhh„4*JE(šÜb²}°»%FC9ÖêEDZÝ}éz_dÐ'²eU½d-­ûu‰“0ïÚp4Ï”ˆ|û¸ E9¿;%:êkÛrn7ô”Myf9k—ì \QˆÃ¬ӌÊ]‚²ýæ÷ŸÒö,ïÚpŠe¼“‰$"ÐBYÜBÔî6E}Ìš¾v¡´²—«ÝtU¨¢Œbˆ ûÞ „ÚaT…[\¸”æl„Íëÿ¥Ê©QÐ ~¤ï…¼/ÙÔ4 y„</À®&}+\Ü
+·n­u»;Ê £Ý‘Œ {jÙ¦‰Ž=]8okâcnÅ´üómÊ2¡SN)çVQb°cËѶ6{<6›Ž-ÄnÄØX¬÷u²ÜDžìöŒšX6µ0¿²LrŸQET@¢ I~ªwŒà7CÆÅRQ÷¬Y<Ƙ[ WE¹ûNŠ‹V™†+u¿.<’ˆÈÞQž”Û/
+be8Ô ŸNflû‡Ë÷òBMß‘ú`Y“wYº½géé`ýûÉ’.¢pÚ¯Ö]ñÔt:-“yÛê­NJ>ÙzÀþ̓y–XoÊ!”ÕDœä­ƒá©v[.ʼñ´Õâàkwýgí—VtŸÉ8 Qè]ë¡ú˜3
+Žéý~¿uc(»
+ÿILq 4ó žqš¶;Æñ5;X³­
+R–à²îµX“uÕ ›PCƒ/ت« }ƒuêPݪÐÅæfßQt½&wAßkô¼ª{F„ia^X.
+W…ÛÂ]Á’$±ºœ—VóóNJ£hÅI-"0I§A„;«wÈ+!l¤² ÑáP¥›œOˆF²ËI§#ÎtŠxJÂqhl€ÉRs<D%@º ¤‹›ÑBáwá×?þ`í'KKkþÇ~µÇ4uFñßí½-¥­m)\ Xú -J ´¥Œ
+íQ| Ÿ8qh·ø
+2?c5=Lêqž—íBã8®Û¥RéSEnÐ £EQBaûh£'=yödæGÍæñجÞH‡Çjõy­V/÷Šƒ46¯'ÃáñŠ­´Àqcèf\2ÎEÇ´¢FP³£Ãªt
+‹"^!k§]ÆvúÀÁ¢i×ð!¾ŽoâÏð—yY_ÍJWG«VÂóv[tG »Â€è`(ºÓO>è•ý®@«Rú€^&]®`ЕºÍ»CÕ!IS(×rsCµî ‰Áàž½ˆX&VÔF9ï
+%^=Œ¾Ë–Ÿbô f
+º{“ÃÐ7fÇÌÉ)Íf"è¨RÚ˜(Ò“ Ó,(«•MÊ^%wYÉ(‰šÁbíJ'¢òˆ—4šÑ5›èˆ^ênZ±âyÝÊ¥…¢J¼èÅ”qK—“¬5yÖ™ünŸ¹$'SpÖ¤œ&¡!ÉÈïÏŽìµ­cÜéI‰›9Ó“dm(®Þï¶Ö½œ­Wkâ3Ò]¾¸Qy³½6Zl±2ùa0¡–⟀l* §3^±™pP6ª@­"´áK@›Lh 3vYÝD ñ
+ô mµ[€T? ? ¤Ý ïéçc5`¢ÛDz³šð1`¹8 Ù·
+ûÄr,‰Ùa¡¡ç°Ì‘¬‰É2’ŒâæâIc¤œiX–PlSb2Kúژ̑¼&&ËHÞ]^YU6¥ÚYQß\¿¼qEÃã_QŽJT¡ŒLWÉ
+¢¦™°XÔ`1ÂXMü‹úÇ÷}–Öa&$íø%¸)E«EDM@òûð|çÀì¤9G’øv·Æ e6̽òwÊCT(Í4aPNfq)®Ÿ]c^¢ßs£¡mQ¦ä7Œ–G{RïZ8´¦Ìé¿Ó*ßG9
+ÄoÕÿ5
+endstream endobj 2035 0 obj << /Length 783 /Filter /FlateDecode >> stream
+H‰
+
+
+000RRR«««ËËËvvv''' GGG§§§)))ˆˆˆºººÉÉÉßßßÍÍ͇‡‡888OOOóóóÛÛÛrrrµµµÙÙÙzzz KKK‹‹‹eeeÝÝÝMMM„„„,,,èèèEEEBBB···ccc ………âââ"""€€€ààà---```¨¨¨PPPŠŠŠ¥¥¥yyy555+++DDD^^^‰‰‰¬¬¬666***///iiiŒŒŒ¡¡¡AAA333IIITTT...£££dddCCC@@@XXX¢¢¢ÌÌ̦¦¦~~~]]]FFF???$$$ YYY!!!VVVSSSwww%%%QQQŸŸŸõõõUUUuuužžžìììZZZ&&&;;;HHHWWW777444[[[:::ooo999111(((### ===\\\gggkkk{{{ƒƒƒ___JJJfff>>>‚‚‚ÜÜÜhhh©©©<<<NNNLLLëëëòòòíííððð×××
+endstream endobj 2036 0 obj << /N 3 /Alternate /DeviceRGB /Length 2340 /Filter /FlateDecode >> stream
+H‰ìTwTÓY~¿’žP’:„„ 5´P¤H B 5€€"ˆ(EQD¤ ‚ˆ
+Š# 6¤ˆŽ.–±ÌŒ:†Ã(#ë2±nÆu÷xv÷Ÿ™w¿s¾wï¹÷~ß}¼óz¹°*€HW‹D
+iC dñ gÈò‡B ((J‚$*†¶CÕPÔ u@=Ðiètºݾæ Eègè ŒÀd˜k†°9̃Ý`?8Þ
+‚!@ØL¨%tF · „·DE"‹èD %¦K‰Ä~âñ1ñ‰¤K²'“Ĥm¤FÒ)Ò5Òé5™Jf“ùä²”¼‡|Œ<Fþ†ü …B1¤¸R¢)¹”=”Êå;Ê+9šœ™œ@.A®D®EnHî®Ü y‚¼¼›üFùBùù3ò·ä—
+†
+|…x…­
+-
+ç*¬(Ò-Ók{¯+>£â¨†TOjµœz„:A§!4=Ÿ&¤m§uѦh t,EÐSèÕô“ôú²UÉF)\©@©Eé¢Ò,a2Œ4F-cñ€ñFYSÙMY¤\©Ü¯|WyUE]ÅUE¤R¥2 r_å*SÕS5UuŸê°ê5T­¬–¯Ö¦6¥¶¤NWwTªW©ª«k°5B4Š4ŽhÜÔXÑÔÒôÖÌÔlÒœÐ\Òbh¹j¥hÕk]ÒZÔ¦i;k‹µëµ/kÿÈTbº1Ó˜ÌI沎†ŽŽT§CgFç­.K7L·Lw@÷‰Q§—¨W¯7®·¬¯­ _¬ß§ÿ­Á€glpÐ`Ú`Õea¸ÓpØðK…%`²úX(F.FYFF÷Œ±Æ<ãTãCÆ·Ù0›ËNf·°o™À&¶&b“C&wL1¦ö¦ÓNÓ‡2Ç“ÇéãÌ™1ÌüÍÊ̆Í^˜ë›G›ï3Ÿ6oÁµH³è²xdIµôµ,³µüÙŠm%´j±ºgM±ö².±±þÉÆÄFdÓfó5—Æ àîäŽs³µ³Í¶í·]´Ó·‹³kµ{È£ó‚x5¼kö{wûû ö¯lrþæÈqLuìu|¶†µF´¦kͼ“®S¼S‡Ó¬3Ó9Îù°ó¬‹ŽK¼K§Ë÷®z® ®Ý®OÝŒÝRÜN¸½p·pÏv?ë¾Êwàoáy ÞU3žTÏ0ÏfÏï¼t½’¼ú¼–½¹ÞEÞc>?Ÿ}>š¡ G°ìkç»ÅwÒì·Î¯Ùï{¶¶ÿh
+endstream endobj 2037 0 obj << /Filter /FlateDecode /Length 13872 /Length1 21068 >> stream
+H‰œV{l“×?×ög›ð¨ó€ÌÃæ#´àihB³’Ö±Id³vÔ¤dIIX  4U×!œdS³R©j÷@E_Z36MÙJaÿ„NªXÿYµM++R™˜&:±¶Äûëû¹NÆVuV~ß9÷qÎ=÷wÏ=7$ˆh&õ‘•6=á_ÝTñm¾„ÞÍí±ÎG<‚ú["1Ðü­nOhÖ†ÍûÚûvw>Õ~k럂D–"-穽w¯¹Õ1ƒ¨
+õSRˆÕb¯(Bt?I±BÔŠ€ÐÄ3*>–ŸÓψªZSþ`ÙêU+ý+JK|Ë—=pÿÒâ%úb¯gÑÂóÝóæΙ]Ÿ—ëºoÖÌÓs¦9vÍfµ*FQ <2×ás{½ÞH©jÏ›Ü6¬Å®x Ês{õܲÌ,÷«ùSÚ ¦´fÚ *0Bz †=Pè¯å¢À ^FäK)£`K›ÜcÌ ´D£°¨Ñ]#ôw¿ŒEù™žÐ­9¥%4’3êth˜Û9"B©XBÁµ#rÎ,-1ò|†¥8Èh3ªû£PôxÂHþ#£©±ì!‚™©å§5a؆C®ëÙcTÇ ê÷Œ”Œ%F]ÔõÍhÑ[bO‚ºb!kq0ÞÈDѸǰÁ¹ü¸Ñã Æ= éÆ£øê5°ºg?º§ÂG¼cn#2häúŒõ˜±þÙÝÖD°h‡›‰Äñúæpö¨—¿‘H¤'‚:ÂY°í1l¥È_Z’Þ“" %ÚÆk¶Å8Î`›'Ñß*c1È©Á8LuìË&&Á=Øky,½@À¨n”‚·‡åÁ^MDu© ±É‘hMÄ›æ»~K8À±é±wúä3=QÕƒŽ 9èáêàÀð4{ ÚÖ1µ‚?­”h®ùãX5|aehÅ.Ý“ø„ ÕÿvsrOLõØ‹]Ÿ«!=M$Bº'”ˆ&b£©¾&ÝãÒ#õõ‰Î`«6„a5šºØï6B˵ Ÿ“ ´%¼Îí͘ͳIÈ*äÖt¹°€¿:%À25†½µ5qƒ§0ëÐÓ’s ¹[cV´1G­zJõz9AûG«© £os8ÝöP“û<Uû}8(Œ™#³·òHŸ9’1êXåñ‹6Ûp.ÍüÝçš“Œ¯5Äœÿ1Üš7òa«ÛIk·•µ.{•Qèƒþ€/Cøn¸|†¹«"W.ª
+Ke·ð£Í¾4'¥RVöþÅðsÄR™úÒõÙ(u1ñê€CÀãjµ˜ãRýÏaNÂr6uZéGÁÏaÖ³ìj­ƒô<öSYvt â´BŸ]Åð9œ½Yˆõ›Õ¾1Þ³¹'Ž_ù¾21šHÇG1~LdÅ6=S0h-“’}><Š<¸`§#<nßMkŽÐøSú®¥&ðz1ê¶!Êçœà>`¶M®}
+ùÐ U‡ØžÿkR¹rÜ” Î?`!P<¬Îh‡}+æ«ó2¡r…Ïá&t^“ïØ(d‹j¯
+!û½Ê÷†ù•÷Fù–9†s6%çÌÿ!êµúh™Z¯.Kž¶Qm†Î5%•tN ÓQ¾‹|X‚¿´ ÑQÎcÎÁŒü€Êå]U~ù®˜2kï~yw 5¢
+uzLir“áh(«~ýÈýuàž¯Â}¼ÎQh&xOM¶á€Ü„¸OM‘I†ãšhÓÆhÌ6Ž{}¾où=½È€~²û¾šÅÕdn§ðeJó\î!“Š¯<ÎKœ[ òãäFÎÈ*ÎÈsŠoë‘i§Àg‘—áüEŒuɺC­1‰ÿ,ÞËÑ.ŸÊw–ä5z¸–r=3óѺcjÿ\øNsMà¶x‰ì\+ä\¢Qû;dwœ!»ÍYü(&»uAz×~Žß^‰ú‘¾s‹y]æ‡sXꕲžÊºñƒêÞ%彸"¥_Îi‘ç`u£-ʾBÆÙNmM2ïe=—9…>í~®Õ²n=ÎãÖaô¥ùlTóžÃøçfMWãuÚY?Ù*Ñþ¶.Ùîq¸qÞ~šåh„~Œk³ôÝcÖ_îã=:T.÷¼9€³P5†ß}Ú0Y€¤ã]ÌyŸNhQšar
+a?ßå·Ïñ±X xL‰þa Xê\&þÍwõÇ6qÝñ÷î‡Ï>ÿ¸øœ;Ûqì‹ãÄ4\;>p0dñ
+…e ò ˆI!?š”$„X¡qŠL0ˆ&PHYÅTmë*ÕüNÚn˦NƒM4ThTÒ¨¶jeêR*„ºŠ†Ë¾—Œ¿&í¤Ï½ç÷Þ½ï÷ûùþð{'ø2ÓŒTj¤›Ñvê{(<,£ÆŠJÛˆpx®f)(
+®$®¤®d¯|v…–Úq{gº?olÅCÿIÀ1Àk€£€#€ `
+¶?ç“lž|¯Õåγ
+¢ÓÊ;r­\ŽÝb±Ú,&Öl10F IÑ8?Z8ǹn{—£Ö¢µäZéYf”¸¥F‰Œ%´Ä(%œåkPMÝò¬C»ayV‘kÀ9ë³¹&kJln:‡ñ«a4KÇp§¥Ðð+6mnÇn}:íŸMãTú¨ç¿íƲ7»§fCS¶Ï»1[¥wF½‘ ÏÀÀ.ùÿ<ø|}ª~eב§äùÕXžïÌõäÇËæÇæ~üÏ° ˆ™Ÿq=^5ue#`ãü‚s&ÝĶõËõË“~ƒ¢œUHÄ Ta÷Û‹ýv?…f
+ÈÉ•Fß¡j*
+“gÈ)¨‡QÄÞF؉Xü9|ª¨.üµ›UÞ›[ÌY3iVMlÔ ˜³“ª)×UQ
+ÄàPR^’“rr±.klö®á}£0ZŠ^W‹…;8\(ÉÒUTiĘ+âJOx¨ÊFNŒñØ7>ûµºÈbR¸0f.¢P°¤B¹JR#>ÌùT_‹ïŒòù\F¥0®
+¥\®Eøe|!‡¦•Ð´ (|,”쟶ó±˜=Æ;á%'Q¼j:> öØÜ3–¬ã~LÛ0ãL9^`` ‚¢Å‹¢•åÞJDraVtFq5 …!—´a!WT"•AÚƒ«Ýå ÇòKoW )Û·{_»­ýí¿MT}uÿßwŸnêÞ%iË×6¬ŽW×ê*_nþÐ+ĵ§›¶¯îâÝ3u¾U¥~µÿÎ3[G¾ÿã?M¬9ÜPßÝ\WÉíe¡'WWTkÁeu¡5;ëÛÂp¿=ƒ¿ßШXuR"IŠH'èŸÐD˜~–î£GiŠ%“Ó±Š+¡Š°Ã/~²¿®jÿZN¿w_û—èûôiëˆëà+
+«¾SÖŸ“Dè w›†M”Ñ$d<†N%Àá¡ä£H¨êQŠÇªd+crØàñ”v`iÅWÚïßü}Sûë7Ok•š;ð¯<ÂW!š é,HjDf8/ I¤êò÷imDG,È÷X:eyÚd0°&‘fYKºÓhfÛ ü ÎRB<4çcºIö\³ Wöá o¼×²«‘¾kç&n­?í^»—á½Ä~â"D_®Ê¢s$H•$ÈP…Ííà/#²ÄEísÚ@z„ð)XïQsÐY’'Ò$Iƒ
+9¯Èžÿ $jÏàSZ'|…gïq°ŒDeª ªqä‘$Ì0éF}c©„’
+D]¼j˜.—‡÷*8€{oh£Ñ7
+zfÖÍ~A°Ìת1†;H´·è[–oìdÔ¶Ê6Ž¯bª’\b%¼c$#ŽYÝyŽ³#,ÉúÙ^‘Ah.øåPR×6>œsV¿#ù#>Â)
+Ð) .Å9·É9z$×á2¼èøoö¬J´ûÚå‰Ï×,^†·n©Þ,·¶¶o"þŽßµk^jøéy­_{õÅë°ûíîõÆþˆ‹®][߆$¿º³¨j1¶~+5“˜dNrìö¨Á@° áb ƒ‰w‚
+V[”ÌsÑT¾ÍjeÇx&ÿ4v‰zùXz<gÃ{iœOæÑ.ZÄpbÈpb\IQ{ kÆfÃ>¬ó¦Ì3jAUH&å9
+•^ @¹å‹'?S\M(ä¿ýÔÌwK…¿àâ m­5µ­v­Ó•úçGŸÜ˼9õÒ†%ľK8þiúpõŠ–mûü¿üÝsÛþüòÁ?ªªR·f5økÊѧˆ9ÐÙa“ŠJÙ`À„Êz8Ô[µÄd‰ÇTŸòú³þIÿg~Æï÷€Y»)\:F1N—Ë3æpsã³ß^€`á‚…‹:u6Xè\c1ËôÍÂPÎP  €ÏU’aTNˆ2Œ/Ãqqn„#¹0×K  Æ®Œ3ä|\Wç9’õš
+U^±ƒëd+`
+"Nç Ƨe™ñz¹­#G‹”JgKtê¬Ùp Êl±+‘襌C|Ü5à;Ú ÃC,aÔÙU_¶fÝÁ™ëW¿8ñ>éj¯]ÿ|צpPèHt'V­ÞDñ+Ç?¼õVËK…ü²c©/·\ÊD_jÙÓÖÑçúÝÕÜÄuE÷½}»«ÕgW^ÉË–°- "ca ·^11Á®3&|ƒ˦ÆÆ|ƒ]‘£¤Äȯ “4”PZ „¦´ƒKJ›†vZJ·¸í „i$L Öº÷­äO2ÓY}Ö;ò»ïÞsî¹ç¡nuŬê…Ð_…à½:G-L›ê5[,û0kǘÐa ¼YiFfV0*,X³@ÄçÌãe#6ÒZ93³;Œûç·ÄeŒÀc£EÅȇ·ëdnòC‡½M ‘Å T0cÊýa(P™/®7=é°Ò®GîxùSrRÜ„Xkü!|­8sF{EkC
+šÏ6µk´ÏðèCÍì˜ìàAë'2QÕça<,^-l0&º¶ôz;²ÛÓt;{#Â5 ›Þ+9Øà o£çäX•­ŒÇÔ"Ô‚e`ì³'Ú£¶[Út¿ÏŸDW'<$ý¤Pžº P f
+£’T 'a
+S™¡P…ÇQ ×[ZJ¹Ø„`œ•L ty¼uuÁT]À·‹wBõ“ËŽuýyczá¢Ö–U/}¼õæ½mhÚ²ù‹–†æ†Ó6W…¾Ý2¯ª‰µûËë"gZ ¼½¢N-+=×¼ñçk§n¼òìΓ¨¦*¼$Tµ,/_ÞÞQ¿|kðvÎð 2œäíUÙáAm¦ƒÀZ+t¢N](å©${iU§Žc°,À¼©BRçG-M’Fd8L ¤Ãþu{)‰mzKAÊ^¦då2Q(‰¡4(é`iEV£ÖªàSÁ;p¸Oû|ïÓýÈøšö#íêñKøR[]MCcm]3
+ùT¬}úxʸòâíöí4¸…MZwÍ–oi§Æ E<ú c’ê`•ô29&(˜ßÃ
+‚–Ê è–J7”ƒå᤯JõVð&mí=°XtEêõ%f†š›\Ñ(‰™ä˜¤ˆ° ¬m”$+,.Kúâþ20Ö²QÓ8>Ì8ïH£%ý# yÌBB&Bú¹Ã3õj
+'vñõ|„gyVðÑ@ŒÎ±zn#³œÓß-Ú{Gµsôl·²údµÉæ*“n309¼
+â·¸àÕæêå'µ^ŠÃð3@U˜ý?P‹BºbD!Cª3v‰NÁ¹‹œ(;yÎÎó1ÞÉÀšˆ4µR«= ãKд<{…#l'å1FFBæ³h?;
+þSåϳªÏ‹¬è€ìô0XJ®tÚ¯p | ¢íM’4è²^(ë²–]HØö<°v.&™ñ¡9ÚTœ>Ãùšçè÷Þ¸s‡;{¿û°ÿÌ7Ó“㟣 t'Y«VÜ 5ãÍ<äg·Ú±=fÍ«ÎÛŸÇZóÔ¼Ëyl#|à¼<gfŒa1›1´‚Fetâî0囱ݹÝæ±5€ØÄËtÁ‰IM¸©y0\š°Åi£²«wÒ xQÊÈM[ýÇÕÖ¾z`Ý’ÒM½ÅCwЯûÖÕÖ®ùàFKý²ÖëÜÙ?Þž‘ëùéóͧJܲm=¦V½][1s>íÃåÃ×¹»ÀÍ,¦Rò®Ë £ÉŠ¬ÖÔ˜™ "½Ù˜Ìg³vÈ2Ó\¨;:Äl19ïiK0 º‘Ž£S"1Ö]$óä'†~BÉj´¯àá¾[ÚïûÑnôJEÌÓ"©éž³}kÆw;wíÁ ÛŽ4?‡–Bã®G]Ÿ<9+{Úô­Kú¾øÉ‹´ŸŽ
+-Ç,J>켃oÄòvK†¥!¹ÃDmiß‚ÇPÀW%J™¢ŸÒ /Žà7.~ùzeÃû<_†ú_h›}ð]îìúKÚ/ÿ[‹wC×Ê_y÷æ;´b úÓÝÄÔªÓb%˜Ä¬Bµ°_
+MH/ÅÑøýëÁ×AR_ƒ§Ç?ÐJ!pß0£]¿Eã.¾Áû/nȾÈ çU4˜BN8æ˜Lœ1Þö?ª«(ªë ŸÇ=÷¹ÂÞ½+, ¸¸ì¢®‚(BÒÜFmâm}!n”Q;Q¶*&Ù.¾‹£ÂVCÆšIœÖ4ŽÑ¨IヴJ|ŽMÚLc:µã£¦ãŒÆÔ˜q„=ô?wEfv÷î2çÞs¾ÿÿ¿ÆþloÔ²ü1 $;ªÔµŒ8Ð#Ö¼Œ©²ðç“I%pP±LÇ2¦š§l]Mþ|‘ôOÞ˽þÔ+}w¿yc6gíâWŽ¼·gÚ–ZbáâD[ë¢wñÂkwð¢ùÕsOlÿñOçv÷Üþxì¤-IädË©_‰Kv4jXÓ 0ìŠá‘”˜<Dn
+âB´ÁÀVVŠ47`DMð•‚¾Í#uK@ÚL·
+ªû¬
+Ü"'œ§}+É
+\&ó7IaâSþÑM§ —ȘÄç}¤îÿÀ±&6ÎÆÐPwJv0¨´ÔHH `Š&D7·Ÿ#gz›Ygï!þ*¬#¤XïB{í4YÅ^5G#š_ŸPªïï±=páÆ#¨Q‰úµÜR0¢=ïÁ'÷DZn)UTu¡^žF×´}Ã%&: « ÚÀuy(à Z©+-ΈÞââà0¦DÄD€dHË@Æá0XùH“•—ƒÅ9
+T\Qˆ&¶ tÂV4Ò¾¬(-)Nq¶ê)wâŸ31)+æÎÛ1ÅÛ.ü…ßæ³—ùlØÏg4ü Šû.·G€_¦¿ AY¨Ê.nU1ô™B.ªxë`»”ª4›Õ*K1ÍŒSÉŒëp~p~Ze£ŠIQ><5Ò±„!Ó-†#Ÿ…•nÇKpÞzŸøö>ÿà¯øܛۻ筶Ö?’ÿ@ˆkç«ù뼟;ö:žÝåôÉïºO
+´:j ¦ÃlŽ³³â ÝžÕ=éQi(´–¥Åt¯þPãS4›T!u)/`>Róä›ïîñ{üö}^Ž/4®_6?¶–uÞüו‰ú£ºê§"™:y 3A«íi’„—×IÕ°Úàb´+(m ¶†H #’rT ³<='n¼æD›½XE bZÀÌ …r%ÉõŽõB<Œ¦…Óñ2lÑãtšc¦àØ5´šøšòM™™
+0Œð„Ì°ÁœG—…
+Êý°³² žúõ‚Î`ÇUþî‡×pþ~oã¬ôÍøæ}{Û
+G—¿T]9¥>óREzÆp^óÿàOðJ¼ Çy3ßÁ?ÖxåäéOîœÖÊŠÿ`ø§ºSÚï8Î,”VØO–ÛòxÖšn¯iºÝ2²ãÈçöµøz|’χò㘚äÙda·aZYž˜iú£rú%?+vn ƒÁ„Er4ñ–€Â³´èY,äi¢)<DÒŽedÂ)­<*€
+f‰@è؉0~
+L(ó(„H1…‰ä$üO¢ò‘¨±z>Wð `z:zKÓá¯öß½p*´‡´Ê"C㪅ÜÔ#Ù ÅžXzVúò‡ÜýÓI
+r³9²
+Bù…4Âñ,ýý©Xh%.: h6 šPsÃÛ&µÕª™f–B5Ëá"_ kùFØ%ÕÑ™ÒrºX’JH›JžeRéb"_²™ä·;L3ÊDgˆk \€ˆKr»¢zÁpá­Ót/ ¤édËнÁ†b Q¹
+=ò7:Q¼@.w÷-Õ”Ÿp|Û;"£Ù*FUJ®;Ç‚éž½ìm¼1TDÛ$[ ÃW
+¾¬Í–Û¡SpBZ[Žu]¶Õj»Ú¥JêÀYż;ïa¡ípÞÈÃÃÂQŸƒ²;m!ö]Œïb©ßæßÃw9…´r…ËøŒÓøLbrÂ…?ç£IŸØùH˜Í¸ã:æÛe£Ô¹j«JÿÏvÙ
+:„0„¾ GÀYÄ_UL8Ë{ôÎB>¥AI
+˜ŒÓCšoEþVAò™‡àJÅ4„ˆÔëXÞPù‹íW?®/ ÛÜÚA§…kjì:ñî­… oÜ=²xVÃæàtT×¼f'wîìy»Jðï
+Úvùsr f~w‘æ_´bN¿h‰çùwìnhðy³…ÝÚÝl§Ç홨ž³n1©WÔËJËT6µJ5䬱`Ñm°“þ›:ƒÜ°.Ñ!S”+ÁŠŽ„ JÔÆæqFŒwóØ&¡¸œ#o!%R?Ðk_)±¡6ºƒ“Èp‚}Cê(4…ôúÐ`õ`ë°¶ó·}/®žÛ¨ÅÐ@{cݦ ó—w@'0ªªÝ×_Ö¾ŒMÛt;Žöî>±·§º´’ØZ»©•je%[!µ°«$f?…ìö€‚%=!‰ :•`5T¥P£ÐJÌnsQΊ ¡mòÜ›¢ªªb=/Á=HJ°L”BSS’,Ÿ>…bîoþãÊ¥O¾½¶²àµE/_G ’*´òº¦-}{Ö7mEo=¿ûGk^}÷ÜÅ­MûrJ®•Î›òTìÈ2RûÚ±{ÌÔ^¢ž£Ú¡öÌÓ¤ö"©½~cVè,,QJÂ,f%8V
+pŽbO¸‘û4K…Ùr%ì" }Ù„ÙQ)!w2Ä!#vÓQ Œ`Š°`¢È“©ê†±ª*&Á­Ó L €’ëm(O‘/FˆuŒÞ6Θ¹.òøÐɵÚèçü×[¨eÜA|¿¶ÑÔÞ¼ák^]Þ˜Ÿm xÏ{ÑWþ‹ÊúµGï'zÚ– Œ)ÀYz50^:Õªz%«Ì_±#»j°„ì +ÂÜ
+Þ¡_ÕX`0i8ÐÀ@øö¤Çl§T‚Ó|JËËfáÒ “#bpiXÖ¢®%ǽø›ÂºÂŠ€À2>íOI¢ÙolªÎøÉ›ï ž§-e>‚êO£T¿ji÷ `Þ²<ì 4´’‰ÜPEŽüL‹Å¬¯Ð"ן
+˧ t()¾? ‰ifl@BÜh2¦”
+‡%pñȸ‘è“L!øa!°Çí1pÃñb˜ëG£îí@|Ý~K
+ŽÓ4 %fh½ÄÐ45©Š%%$f
+ÿµÈõL­—ê¼Þrº}?‚šéWP%_¾ÃcV™ã>c„Þ´Š™^ÙùŒ¸x5M‹g^môÞð~楽>/Ù}ä΃ªâ;É*½Yɶ“w䪉†UR42Ù¶ÓÇûîRÙ&yö¢=˾׳ïõâõoîüÁM-ê*¨®mÑ¢¼Yõ/xA‹2¾Á Ë^X]ôë¿$·àHm^EtGò#\· #¿¤swòïS»³Q‹‡®þtÊàÁFÇÎòdà¬Ï¸UO\drýO\ïŠË}—&Žñý°sͯÞzÞša=f*ƒ
+ªîñƒ+èJ;´(%‡=ÆeqàsT&Õ6q\„ôŒè¤
+)ãv:;\¦û¦ºVªENTó˜ö3í“1êÍa4°cUK÷öæÕ;±ø9šy-ñáÚ/^@i¯9ôzÿá£:ui]¬VC’¿S5–ÑÍtŠºhR¹AY¢bFVH}ÒßAeAÞ5DŸôD:
+g…>¸ºaŠs!½²eØy å(¾$½„rL‚G gø‡a.‡†DRÐ0øÛ‡±l ëv¶‘+IÁ`(fYÑPL×£¡ÐØX²û‚Iˆð2oû£bv[b>ŒØ‹ÑdNÅ¥òý!ËŸG5KÉ£åÖ–`ŸÅÛ<+ó«Ç‹Ã uƒjW^Qâ4Î7ÅŒû|Ž%ã7¤ ³Ë*´FB$2Jxxêm€®„C"³…' :%ÀÕ”Üt±])&¥ñvs󥓀Ž')Ï<úxQÊ —í¡¦Úú‚ìIYÏzeuÇé7~px}ž³Ñ]T5«"/ï ïþ¸mÕN:ü…„ÃÕf§ŽÏzB¹;—´õÕNíx<\Ìœœ=Þ¶­@}_óâƒ+¾É=aÊðeÖ€¹<
+"¡I¤T ?º|p0É..XÞÛÕ÷ü¬;±¸¶;gœž*ÿ¸TçRÖüݯ“Ï Ç¸ŒŸÅ¾á¼ÆÜZËÀPÃ,J0‹ú‰Ï«¹1}‚ãÉèVmõ±‘xÍ¢“‹&REáRL¡ñè÷dIçÍ 6î=J¾:÷¡šÒûêM'~N_zûS ŸÜF†ù¿b•.ã¿*ÐÃN(ôœr•ü–±ìW¨&+J54‚$K
+ y$c!—JH(6nªHŒ€[V|L¦–T'QI!4¶
+ÞƒñAŒv" h‹wII°a2¶bñ
+ŽÅlº·œ×ïž Ç{°zãD^á wJÖ’E²(Ù{ò—äkÎ §W
+µÒ¯#@BùÖÛm(¿ W9ì?¤ûâ1îEB¢†\ÍzTI›¨ùxù=[ƒ„¬ :û\ªír©’,oÑtÕtFéC· J ÊÈ>0QC³S'TÖ™éŠkªj†éS6²œéZâêt13tbRªÂ*Tœ •©Çô%z§.é¨uBiTÛË5O¼ ý§b•c­ ßAàÌwÐ"˜21¦„M„Y 4äKûÏ’UƒûI³ÞÙyfÀÙŽZFwñûÖyºièsÜRb¸¹h)>{ȦOÑS”©0]h…úèˆÀâø‹ƒ/j¨¤;Za7×GëÎЉ®+ ©ýꇪ¤Ž¨…¹`Íu¡Ï
+ÞÛZ¡N Y5!)ö³÷'ï8;Éf'÷ø%gù<¶?gIž£ "‹œoÑ|”TÆÎç
+Jê_Ħj–äiDà%yâ¸/î€á€<äy§ÑN<5ÐÆôÉ p+y‡–æñù`ˆ£º|9 ÑØQ‰ÔIßFoõ
+1\ÈÀ‚ÁÏΔ€ â8w€Rôß'у™ðßH€+ÜázÇû¥jÜ—["áÎÉâp”·=:îÎñ~‰ñ@-“ÉÁ_½K¶9ÍÕµOœk|;³±½úÑ <ÁôØ­m?¬Z$ii• h—\Œ§9h û×éšZ£ÒV žP™ŠvÙ÷gêÆ‚KÕàv³e;ŸÊ\”ʆÜF5Í¥»x&Ç$·ðÁë-åC-#ÈEÄóaUæxy;7t™–ߺ@þêäKáß]p®ä?< etûü>DáÍ?òŒú&AØ “b¤ÕÜjî6™ÛÌ0+Ì:S2¡Ÿ <0°ˆLÐX¥¥¤°¥íWzŸ·Ÿ·nó1¶» æ62Œ
+£Î è‡Ì*èËî䃯³éòòÑ¡ æQdr@Ó¡jŠ¢iÐ/˜)QRA],Z#á­<- $W ùÈ™÷â÷>±¶Q@fuGN׿]Yóϼeý
+™0&@"ù&t°=3-s,Ö^;aȇ™bg)|%¾3|ñÎYƒ}ƒ} [ÿë‹ÝS&–'80ð$h io‚VÊá')i¸DL§)$ÃüÍð‹Å ZBº3A+H¿\5¿®ªrfd暶¥í•íK—­üwV 
+¹×á³íÁ{ ´¡-Ú¡ƒ°©¥¸v·¯
+p…± îÎé?ýFX‡î‚?ÁthFg¥¨_!”áÚf6#îëçqG•âo#3,§h]2zýã1Tà1<ùí*oÑ~ªîgk§A—/õ™EÍ­îé7aŒ*¾~eAÿ >ÿ½œµC_NêT÷ã«1â 
+endstream endobj 2038 0 obj << /Type /XObject /Subtype /Image /Width 501 /Height 554 /BitsPerComponent 8 /ColorSpace 2029 0 R /Length 37200 /Filter /FlateDecode >> stream
+H‰ìW‰TÕ~Ë ¡83!$!c 0ú¨mR¸ˆ­K7ßÝÞ½wÞ VŒŽ¶Q¦Sœ
+ú'…ƒ;hÄP·Ë/b ‰îŠÊôDBÞ@zÒØíHpÙ#wþž£Að…ñcwtòûî…çªkÒyèWÄB˜£ 7j|~'¿ïþâ~q—‡8f¼ÛýbÒ£å½QúÆr@‘Ã|)ŠÅ`² ôÐÃÇK9)¤ß5é“҃̕ SÇ#‘™ÎÔ­_† 'ÇW•q év¿˜ô6¤¯¾ûÌFÒ;¯`N<rO%¨T
+SSà)pàÉàÔzê"Ïéú(Ô!X!H[‰áDz9×:˜~¶}LÀgzÇè(Óošé–ñUÂñ“¡W ×
+—#ìJ»»çÀÃPŒ2"”ä°BÔ“Hg²Ž$I‘KxŽÈŽŸhJ~šåƒ¦#Ž‡zP
+îÀD»þC’(ýEDbØуR›ûR»P)E¸ðªs‘¤t¦
+ˆøJúlW{®w~æÀ ¥]PôÃ`OQ¨ü¬QŠKJØØ_ÂWh‘…õ"9Çd.Óa¿©!*±„ÓžËà¤$ßHºSæy0n"ª»D¢…fð*Ђt}‡¯Ýpgé?­rŽ`ù5´t¬*;0
+‚•©:âEB—9‚¢œPŒ1A¨L€mÌî …6$ù\ ¸-ÎJ ÈRŽO<²K‹8QZ€F éRîe™£ï¤ " s1GzÊÐ4 §ŽI8%p!Õ“ŽóYR¦”Q
+Ó(Ä€PgÄ ?²à\|hNºe@å¶5³=‚ô›†¬Ò+‡@Ci)WÆʸÎCÒ‡2ý#™óÃaÒI¾)ÕŸLeÎcG€¨g•«^úű ¿¼ðò+Ç7sHc¼ãÅԶ̓U/OþjdÛPúøÅÔ€@q°ù†™g~ý›K/¿º^
+õ˜K‘“Éô¿–y}ËÅ2-¡‹#ÛRýC™”Or×½úÆ¥7;óB¤"çDÉãØÝåy¿»ï÷§Þ?¼ÿmÂóĹLút
+¯c¯]éoý)»ÕJ„ª¬uÿãÛÓE½
+¾È¸©C­›ÑSAkž€Ä ì”ÅÝ“ Ö%¤d=¯[¾ÂÕxŒdœ9Òùòi]‘²Â¾
+l™ü0õAÛfÐD¸y@ ›‘^³Nò^#L(#kßéYvéñHÍ8pê!Üéñ
+ö 2ADÚ“œ0Éb„»ôî\´k²Ï|ºÇ1!b“Y~…7Jâ„ró\Õå}誽&õUµåˆ £‰½­î<vbu_w"ªA—ÒÏ0b«õÑî͸´ã–c“Â$ê@¾4ÿ^’=…Ù¾òô¦ÚgÛ‘@Yy7SWA°Fc“¡©gøðhE‹>³´â;ï=¼³öPO?òg8Ž]\4•ó+,å“7ú)¤@蟒w²we^·ãÉzºÞÿs€Ìó‘îU0xÂ@j6Êë¿F
+ÍØ&Ë*úƒ;Ñ~ 9mâDf¸0’$ûʘJ{~Úš¬‚åKŠ”Púèþ‡Fô’iv¶X|n¹® v´M†Q0Ú$oàô±©û «é–÷JIËÍ'ÚªK»÷X‚3îWƒN<ÞŒÑ>õ_bi›-z”‰F¨é¬TªqEQG#£.÷¬œ¤Ž,Þþû½¶”Ì-ò¾ÆÔ­²”?zhÎã3h#â(nÍw'+]È5þê:.¤£ž}Ò@Ž@±‰\wlÛ¢|‹|yU
+N²» JëáÑ‘Ü4ׇ>&•ºe,ü\»Ò?w¯ÇŠë]7[œãÀh+q:ˆ0Dñ €D Ô ‚ ŽÈ t_RÅV¬ªÄYX5 ç§Îº;x,ɃTVQÚ|2Ïðba›òºÕõ†¡‹™ç0mp °FQ¼á±W ƒ2›3Сbgkñy‘ïb°k<däšbLx¤¨$½ ?
+ƒZ; "!Gåõ_#’÷õšÃ}‘-€Ùö‰ðŸ¨à¤¤ó'¡n”QR]rlæ1_m¬tËg9šÛþóV3eEÉô¶‘
+êQÒdT< -ÏçYÛ椾
+]ð›ú%>xùá†iÓžjó”‡*=Ö­…"˜8@­yv(8ÆTJEW0ýÓJfHóÐŽç,Gyݶ]×vÙh£¼þ«
+ôšš¤Ö¯ýÖÔ·]bÕ5MιŒ1%¨Zý‰Àr°H*ËF9ã%“’,¹|ºˆ¸÷Ëçœë7
+_jÕ¥]€ šÀ ._º`ê÷ö²X·!-Ö‹žX|ÃÐ ;\6æíƒqÌ™[¸IK„ÿ›2™ŠnãÝ‚ÈãÔ½¤‡ä}….£(„¿ñmɸdÄahaÝÿ°_ö±Y]uoyÉÈK[Oc«Ëí?vf™g8ánNcr^ï9瞇hWžQö¨)z¹6Ï}èÓ%K!2 )/e %“ Åh t™¡n1ˆ¸„m1qi!ò0Òø‡ÙtþãïœÛ
+u­ÿ4¤ýµ}zŸ{Ͻ÷œóù½|+Û< ÚCO5ʉÛ8ÐgÝÎxè³g­Îˆè9ó½3+bÉýë7\p—Í2¥™:g˦]ƒ~Î4²gÇ Cå„‚g΀oÔ]MÒi“€.Hõ؃bÙé70PG”ÿvºdÚOe☈ Ê^š4t6Ï&óœÙ||4ÊÐ
++î{oƒŽ»’3½>튑–*^e¡í‹UÏ"&§åÄm賫gÍþ¤š^]ý,îx~ÿ]ë,o·I»¡rL÷Ù=‚{&Ê2¯v“ƒÕ#µb÷
+‚Pµ<ì„QѬ'D¶Þ Bùé®6¦° Ä0Übéh#Æ"5.:7i褾Ց=N½ˆÄ±ðž´%%¹½Ð¯FDf[¢§7;UbÌ×…"¢7¹]œ´5ݘ-K|¨ËßC’Aé·CïùHw´?)½WWýo‚6JàöÊ{N
+öJ¿Eâš­emÐd•¬ m±¡V4‹UÂØÒZèåB@"nu_…HE^.VŽAÆë›;ë<9˜ñïSjâJ0U„7Û» à.å$nÎ;w´Q{³Çõz…$DÀ=oÙèüLÁNhî©F8y›ôKöWÝ-Ôç~~íEˆAÇ9.<·|XëÅŒ>væa6‚
+fç?¡
+nÁ£6Q­4ƒcÍ¥uŠÆŒ”ÌÓH¹„‚½¥(…¦ 1†íIð,X´Q<š ˜DŒy c­”†·Sï‚q bgÅ(‹¨®
+à™m°RI£)&øØD"½úSwÍ~ !Ss¥!¹ãp’ K d’@X
+Ú'ÂŸÚ Ï5»ì¯txÿ_>xÏÚk\ˆÑ®“¿)]þÜüÖkÞ9sKÍÑ›5;ò]O”¸[“¢Wq*h‰ÔuÏÜ_z½"ë`ö.°"ÄN¼O|)r1+'—‚ª›àTŒÌ8¡—_3q\Ì]õjYÆåœIGȽ8Ð7Ï̾cf¾úšÏ)]©:X5<uþù»÷¿sâë•fåMÖC5-ƪÿñ˨ŒËÀ/ÌAW5”n” ìØO·ŠË´r×êX3é´þüÇ^þÞS<wúÇ»½N¡j\‡¯rzÇ„Î „lÖ¹r£5}vvõE·î…¸;Mÿ=Ÿj·¯Þ?Ú·¸¼âôZßT/÷Êà,îéáÆWtf”Ž•Ë¶ò:N%ÍåZlC&g÷<<(ƒ‰œtTÊŸ^{#kcu3´W:¾7—‡Î¥ÀÆ çiº³Ð7Ͷ*_-õ™½:šÛ>ú¥C™8+oK“R¯þcsµ™dþt)¡
+€ŸìB(&¡ ûJ‰…æt óqQ+TôÝw=|hn9§°´œÛÇ™ssÕÄ):Æn)¦‚Ûµ‘FnÓÚBŸÙô¯wý@¶A[›‘—É‹^§'ãŒÜU&ã2êó‘;L• F
+Î\5µRJøégW|l<î•{Ûs–AùËw"Û¼ž^Ö…V:zïúœ·Ý¸¸Þ•ÓíýÖc(ìkÙûÍÛßûSà†Lô&R1.Kå‰7Ž@¶Ó‚ôŸ,‡Ú&îÉï¿ö‡/œpû»9‘q{TSÁ©.eB.fuÿÏ«p±•–"ï B€Iya«rçJÇ÷æò2G9zÝG?½øÁ·¿r oQõõ˜ÏÖ¸ê]Ëw|MÚ0¨@²€l´…ýûuÍÓg/1w z¡ºK¸(åá¬Uu)óqQËœ|ÚczeeØOÎUÝ´Ž^Þ…~=…~gAJÏR¹çÔ:Wn@é;¶í]³¤o¹‘?+9]¨èÀ[ÿäÍU9Ï”BbiÃ1Jx%­jNŽ„3Çím—Ê+UÖÙ£]Ï7Œ[öLv¡cŠ„q¥=g´£Ž’'&ŒIâþºrQÇŽï%æœ2Þ8ºHèÿÐd­˜Ð×`–Œ(tªÂ€Èâ{ª¢VõO{¤5ey>&±Õ}gâ„ 4é3Eì«=^o£Akõ¥ý›ãŽag £† 0ôOÞS{ä˜\Fu :X2ÃHmº£Î"IÖ+J˜²†B /‘ 'r%¼Và‰8!G ÒqðÊdœ”tñl™ Ø”5ò€’Êš¶K¬Híö쬆Õã:/‡ã¢
+ì/Ü´œl{—‰‹çÀ°@LÜŠÏñ`ƒŒ¢¬l¡1[Å¡îg%}¿Ã„ËZ qAB+)Ëuä±B7yTΠ/UA!I˜ï„¥ä)Á’Ám¥ 6µÛš$ö†6/•>FDµ90ÿðŸ?¶à›7mšÝ±sŠÒg>uJØø.
+®öfþ¹ç~GØ$œ†u£[çj±²Ê¡Õ¨#<À¤\r¸B'H˶¶5÷¶‚à>¦è}®cÊJ7¹Fn@Sgä´®r¬)从'jXÐCNïE-æ8så!ÌÈLE 9H"Y!¹€[$ŽDbQÓ[AâÈq|ú`>?;û“ŠOÊüî3eéÅk¹AC (8W¶oa›Ð«Å¸ê¿ÃvRãRl¹;å¡ >Z×;ø¤)µM@ÃIY³­õ!· |@l¸
+îŒ~Qõ40°{íÀK ç6b3 )G\iÿ¬¤iy’+;5c>9pßí#I7m#™5î©Éyæ4(.b~l¥ –1;òp’Ž!Y‰ª³ö¾t²k©îä‘Æ/xz6|ĸ1wÝ´èÃ+?ÅÍ»Ï.§àF¤,„SëÀɪíjIXegôÛňJ‹ny}mb©ž›Ó«Úðµ~2ÐV,á£Ø(.1Ï€õ#ÉAÜ|1¦‘ hü°½È©O´1þúw Å?/'ßÇ.O€nè3o h‚ ñõ7çʵà@¨ŒËg,¿ÿm3¹æ9YÛT`Á±TyåSïáM†±e¯ Á~£æpý²½0bEÒ°GêÀúlèž;Ó* ¦n–ê`ÆkwPÐH·_º2Þ­sí´£)ŠÚÕÂ}JQµ«µˆiïá¿é¼©SöÌ'–1 ÖøÌ6óžZ§¤ôüªmï£2ä^Z$IFŠXC©iô‘Ìð˜«îz?#¦ˆ¾ú¹¦5­ù‰ƒPä»S&+œ¨óéÒì¹}|
+Xst XÖ0¾š7Ft×Ï°f“FÀ;–Ps1‚NéÐÒ_ ¥w°ûð
+yÃoZôao†¹áf™G¦x¤´°¡
+¦c“¬;s[E"D9qlæ
+‚“4:3âÞôQF°((¼2á< ‹ŽÀ¨B9&Fü%¾9 Î^nr,6ž”´­î%D@3RÄ5-¯T` -9ÀgŸ÷ŽX‘'QMºöŒy-´ªÉ³T`þUYƒÑ¤B_ëtt¢w¸(ßê¸ß„<ŒÍa겎Ʋ$ø§iVÚP%[s,¬t|Þçð÷kk¸
+¹­ËÕáø÷!ŽÖ# x¹®¾• "Ìnìd7•D³;(4zý£"¹B$ŒQp6¥ËŠ‡­bêòž}è–ºKÀ.´ÀóÞiýtø ìûõ?ߦé·üV]Fû
+%q:*3Sr ø<YY‹ÏʼÂ8•¯.È•ûˆøŽk,C÷~¬)±2ì±­µîß³è·õ¬h'ÆÌ oÏlKè u2JŠ×ÕôŠéÝ
+ÙKZô$•_$µ¬»§ž±Ÿ`g>^cY·?Óžžj÷Øÿ«ñ=;ý'šV»çPé£@epdá!ÃÓrªg­¦Ä’ò»8Òs׿Z*«ÖSÍ'<Ù\Ù*@9Ù†ãû”Œ—[øƒbÆ C³{ôí¡ÏMç'’ê+Yx(Ù|rAõ\µØg&2Õ–&ì_0½Ç§6•¢ìÇAÂÓ¨ 9Ÿ<ùëJCÉή7OEknÚÞcjI%q¶D®„•FZ²éx_‹6>ì,#süТ˲åH8o +ƒñŸ£ô„Ÿ_æe
+l59¯õ@zD³poBWÿoÁ{Áë¸P«ŒÂýï Úuúïn®Ùó‡?ûÃ.ÉHc6¼?&X°â ’äšeÇÇâ-¿š®ÈGnä ™››Q%jµÈjëJ„¨;Ù:I$
+¥…`Ô@%¾M—ù‹MÚ'…ˆ0‡$_éQ9]%ªÒN¥ø#µÍŠ6
+¥9WÝîotS¥ÿ[‡%1¨õ‚AjŽFË“tÓt»èþ–#OLñ™ÙU’B+-JÔ¦’Ï:®9ásùbì¦6<ÞÑEŽ†Ö‚EEQ-@<‘¸P‘ðFA«5¶³ ₦C}㤟âîè½âø¡E—§RÿXj˜ÑÆê sk^w¥tý£ž¨æø™Ò§H³ª"©skí
+(ú¿ôf+fI$T9 ™«áB
+r¬5að‹œyÎ\îyoÆÌsTú§u”]3öÓ›.gV`ÌÙè
+úÖ•ÂÝzz© qQ/½üý¢7RJm+¦Š.ºË 3|í’¼X‹<Ì,òÐf§€OÂÁM~ d‚–[ßÏD\ATÝ{Ü­½ê¢5* (
+À\Óæ["M’¡ HTØa7ü¿B
+u1/…¶iQˆÒPµ4¤¥)jTÒPõÿÌœMEÀ•ºÀ>ÏÍ>3Ïÿù?¿ÇÞ •^8S²2RØšIë:ñnŽ³U,ß.רOïxÉ”qCßDoOìd]”§}!ú¯J¯Ò[ðsÒy×>mvjS‡åZÞŒ±R¾uÓ¸Lû”ê;â@žüøýNà pÙëŽeÒ-‰™/æpÍgŽbìµv$„rp‚‡RHƒ—.}\¤½£äŽƒœü±ju|8~ó¡eœ¾ÓuѼñŸÍii1cðãu 6é
+áÀSÑ…+€(ÍÙG‡¶þ8*©¯Uó™¬k ¯äÊR&.mk2ï8m^ÿüwÿÇÄv¿¤× y"=z•êEÝ-¹>õ¡I—_LL“*>Ðx "ý[¢þps¶ÿV­ñȾV¨Ùwî›ôH­mð1U aÐJ=R£WúÞÀã"è«(3„(uK€3VO™j1Y]ç;5Ë Ø;=w*çÏÐvHÕ·è÷0nÞ¤Ê=MD—N *óC–µ¶¸Ÿh÷Ò2kûÉúƆÕï.ÆæDûK&¢©ÝeÝ)•ZÓfšní­qwáJ>K´zû“Ú9NÏϽWûÔææŒ/²3.kF-ªyÒΠϗ6©\O3>æÁ<OÌ€™¨]˧¸Þœã+”žï”šíØiŸÉ†‚ñ‚ΧB'n9êÃtƒ×ÔÅæÀsqÑÐS½êØ¿ÿ_zöèÄÅܨ}G&BÙ¿J œrÕtãÈ<™°âh‹`Æ• ‘™ Ñ“¥ áo
+Gz`Ã÷·GfE – ­ìÑý›Ù´YéTœ^^«eZÝ®´( ãZ0±Àh6‘ P7hniû#Ú’”éØ"ô¢k +»Y<º³
+öjakµªê+áç÷°÷ºº¡ÃÆ4 žô¡Cž ^êOz¡]‹²xõ| F­™½ÔúPT½×jt¥ÒçÚ‰ö믒QðæÙ~*“n:­’vMAØé`Ƴ)¡Ö%T?™_2Oð@ؼõP¹hî÷/æA™)kø¼:1êˆTû:a¤@]V w’¦8å¬ÉC߉š(6x”­ó…JgÌˬ?¥÷(˜L`}‚­làqÖ²ý ;SË\B”¯¡,×Û‚šõt}ÿ#9B¸˜",%&\›}û7‰Ð"ãèêÜÄìZóÛjZ - •œZ<ÇL¢šñÒ{½µ{OH€j<
+jUG×ãcÓ^øàÕµº\ªdñÏ^^¥…³UX2[Ñ¡Uè/òx:q±MÕ ùÕšæØòAq,¸;é%7(e:.t–MR«ýµ¹µËÊøÅS²6þ\;O¿ì=*\r ß­O´$ã<.ƒídzŠzzà¢Ã¦Cý_‚J7D ä=žeŽ½ó÷!T¡H®™,ÑÚfY›@îÕ &‡QRT×V»Ø ѺõWkh‡ç˜è¶^é°iÌ€
+x<vä¬5{wwØ3‚
+lg[%<Çñæl{á£Ã3ëQÎ'…p‡¨w1£Y®X©(ëàÆ=§òvöÁ›ÿ¸ÒóÍúæÉÓ±N0Çl¬ƒº™®odmH†gë@g‡Fœ<šLY0;cç?ðížÃsÇù±Ã#N*ˆAŒž
+e:"#y][ðÐ"D^Ì}ãîÊã› äXAHÑ›.z ¸€Ú è)‡ª‡ÞZ9ô/ð­Gÿ=}¹ú,);Fw'˜:(}æ÷…$‹´DIüð™™é ‘%ap=ۉË"®¿óþüÝgç‡èÅÆ“h–tÉW¾lž\LèTš]dÓbBÿuð]Zì? “Ù4KŽÆÓµó>{ðÛkqî܇ôâH§Y‘‡³ÏÿþàÙß~ò8‚ƒI@U…‹«¥{¿ž¤ÉÖ›}ðÑ®<ù4¦—ý.!m›Iš%Y4{ó³¿üñWß¿v+þÞ“„^Zapþî/Ÿ}ùƒó·ï<š„Ù$X©NºÔ´©w7߈®Å­žI~gE”ôwHdaGôŒt
+£-2N£<èé¤ ³,OË‹E:oÐ7$SúÚtJ'ÎbF—fišÓ0¥ç$-y:FÅAùÉÑÖ$鳈þixtÑ-ZËÒ}OŽãˆ¶î)­/cZBSú"Z¦ó<K
+:"´<$³„~§ñ0¡Ý4¦³Å8(—ú(K‚<NʳeyTÐÂ^È!ý•§‘0¡i 'ôzÅEô‹”G(ºî¤—(]mÐ¥½ÔhMJÂè€>£¿‘žzU‘XïdôCh$â¢8,¿Š~(};íôxtB9Ø™¥ôgÓ‹›öÀ¤<_ÐoK—³Ižfå&ÒìÓòø³ò@£COy¸“•[V•{!½Ó¡…žûò…OÑV'yœÆ=X”§ô;Ód|·vqòfo8ÿÄh¯^×IñƸükÿûIBÿ§è¼K øåS»ràüŸè~8;ìw.'}ÿ˜Æººï}É-]•|ÍYèKn™èRÉ…1Ð_oK]Þçw`yí-] å©šÓ;Зܒ'½¼©þš[êAnþÁǤ¿î–¾§×ôå–&Y:ÿüã{ŸÜ»÷‹wîB=×QvôÓö{ëöo¦Á«?ÐÓ8‰âÉa>I²¢˜ÓoñH@oJù,:,Šp\м§qQ¼ú#½9eGYá8O§Qšg¯þ@@oLQ^ã˜Vy:ÄGQú- è)ŸNÇAA‘Žã¬ˆ^ý‘€Þ˜Ršó8 ó<Ë‚"/ÂW$t¹*4Ðe®w¶¾¢›è\²™ô‡S¡ª³ô¦fƒþEoX·¾½©ÙMúžœßtY¡oœÎÎ$ôÑ[ƒ*9Л›úÆYÍ ½±Ù wÛ sÊ€.<!Ö€Î4º/ZÅæè,«G×ó¾
+ ÃfnX}k }"m8ôZmº2sËÇÊJŸL+ªô©Jmãõ%ôÉ´"èµêªú–­%ô‰´áÐg<âê5•úDÚpèÕj¥6ö%ô‰´âî}ªR™.¡O¤@Ÿ©a[ï”Ð'ÌŠ+ý¢•Ð'ÌJè+ÐJè+ÐJè+ÐJè+ÐJè+ÐJè+ÐJè+Ð
+¡×*Ó}Ð%ô ³èS@:]Vú„Za¥{ä3%ô‰´áÐk5ÈûÌïËJŸL+¬ô궼úh }"m8ô©þüx×Jèi—éÞç6ÜVBŸH+€îÏ­½µû}óš òW•Ð'Å
+ ×<ëkoîjýæÕ[§†”z ýjµ1 W6¯ýx­Z›*¡OŠ½¶yÝmC†”ЯZú4™ÿDµ6=]BŸGÞg¶õN•Ð'ÃÆiäЭª•Ð'ÇÆ©t@¯ôî%ô«×Æ^`SÕw$•Lj¥STP… ø`Rf¤U3"ª'ŒãNõ)¤ÖŸRV0®µŒ²V£Õ¿®˜NÙp‡q‡ÊPaG-MM¸V…Iid¢àaK+£¸pî+ªŠÆ3ŒV(î‡ác$Ö„Uø%ÃðGX£9ÆQc#$üÌ4äƒõÏaŒå]Ãí†[„ŒŽ,A„PÆ
+FËÆÓ!÷…#pI*IÕ}žÿÃá¾
+Óï ÷xÐ+•G(|§VR¸m,’MOWÜj©µÅcfëJk!­¼)àˆ£4ðI2¡‰¤IÑžI0˜`ùbú×5x
+®4a’#s aùñr ,—– ¥tƒ´')Uñ| U‚>󙡬O[ÊtÆÕ0ar·åákä²ýûÀ[0¤ ¾øôêÂö™Ï­i)Þ›çrÆLK!0B `¸ˆ$ía‡C1âÁ¡ :$~Y~e¨Ká“Ô kÅWZéºO(Kµ°ªAüƒ˜á*Q*ÑðqÇ,$
+Õ%[^ $²ë‹A gd-¶"*LßN#0Ê"W7£‚,„ÆWô2C@9ñëhñÐ*ôL8üÒð‚Ý•ö+¬ô©Ú·¼´iÕ
+Û€Jêл\Þ1%%æ}ò¸Ì­DŽ–wÎD€’<]t]ETÔk…Q@¤H¬!Pg™ð¢ñ‘%="ƒL5 q¨mC‰m–©§ò£¤QG~ob$÷Ÿ‡aƒ &ÌBÄ”g[Qú¨(B©° ƨõ4¨µ$ l6!A#ƒn¡K©#-Vp¢…³ØvT/-¸‹mÆêOVf®z¥úA%Eèç|øO{[z㯧Þ^Ïô^ò‡XÝ&Êo«â}{«¯sä«\Üh|#>ˆÉi–ËpÄê¡X€-³\gÛç]œ%ïš/ˆd11Eò®íö9¤"[¤tžÖ(»0$ÛçlÑ|œ°ùOA»»{1¥DYáwGïÛïÜÙ“÷üèyˆ¼NΘÍýüÓ††"HP PH¼¾÷…Ç¿ÑvéÞÃÿýÌZÊH
+yçðiöáœù[xo”¼ÏdÇV¾=âçý²—üÒc׋À~à„˜ü Þ?ÊǹÝ[|´¸ x÷ yÒz)ÝôwýÍwÙ*O$CÇÝ}»ôï˜Ææ¯2yF}•¡{Ýp1Ë|ÆuºA‡'¹5Ö²Ïæ㣸Úû{7ŽujÊÖøÃvŸä¥Öt'±]j)Ð×íÆ¢;ýGeËO½ßm÷øF)GFå•Ž™±“ ®ùºÝÝ’ˆs?}Á#_6®}3!ú”Si>íOýë£6y˜m ²›»÷>ë#’6}JyñÍÀ§½{›î臯%ïS >35û#áŽ;÷ù8§ÙoÜÍàN†ÏKëëV~voÛeÖŽÛß ¾·‰üëšM¿Üét1íwÏÌÕÙB˜CE©û
+üÍ zqÖ²0×½üúIŠ
+†¦±ÒŠ†KÑ4MPRhÅ`J­È¥ ÒTB¬¨Äøù¾Ù›ÝîܱÖòz»IgßÌ|ß÷óý~>ŸoV±TFu»ßŽÄöÄœ~-õè;.3®qì<‚ÚÓJ\ñm*>Â=©þÀÃNÃ[ÉÅsªÖÇ!U›;ÍÞí©²7k¼Ël“b›¦"ÎRúWzÛÎs½Aa!¤U
+ʯ¢RyÎ^cO½š®’º×Ó¿Í3`Ä擹 KÿNø>º#u5Òˆ3ÓJ[6]:ùˆš‡ 㿾Å.Ö.
+”ŒØìNÐo¥c=JGJžŠD!Îîýç¨îÚé
+óÆ+•6Úi$|z½‹P)î^|ÒáMÊ7Ý5}¥ „¢IíËâß];}‰û.ì™ru¦[8ÉH•„Ì!ebÄî?4(‡1ÝxæÐÚlf£R[0¡0Ñ⩦óÃ/{Ã’Ç‹rP3lj_Þ
+†o™øä1´¿yª:þÿ4r°§äU†Ò<'éÖPp9vΚÞ*ß³@^Å=ÉMÌ\C¶V©ÈÀ$$¡'횯b4&ðE0«t5ms•„(ÍßH2Ì!sÕ/“Ï_ùÆŸ×ÎA•d;ãºE’y±™Ë×4œ†î6©W*º«ÑÔ½s•w1rS.{¨·ˆ9èý“?oX2Ç ±É·v]ÎÔÂÅï¿
+ô•×W[FZ–éÈšG¿ÿ¯ _üö ’«G"h(øÏ‹;A7ËR©B³=~j»Ï£ç¶þˆåzOqÖuf‹öÛ¦†º•Øá¾ÚÙ\Ð×_~"!²Š™aøþ}— OnŸ{ü>jr$}Ã"íÝ4sŸÕé7,þLÏø÷)Ÿ¢£š²¿¾õ6T8JlËÊÔ¾”¤€¦\“ú5Wž)ñþ×$ô<àò:;d³ViïçB—tØïçIŠ• øëÐ…N·73TuyüºQ}ßÉ&&Šoò,”Ɉcç :eµý±t%åµFO½E4Ólùæ)Ãú™Ô¢æ¯ß¼ ‡¡†ù4¼àÞÅ(óL‘ª¯•”_üú;’ã2€Å-â+¬íÖ¶a7ÎŒ"™­óh¼†-#Lbb °Þ‰Ç¶\(->ÞµÊ6ÖاÏH/ZÒmd›9ãsSz{¦vî™Ú33sÍX#Ÿº64œ£½}-þòˆï•™B›cÀü§“Ëq©¼ãN?Âà¶h£»‹½ƒ‘
+¤’Z…ªý8$™xô]"”9íå¿Ž†…è¦æÂ
+½Z½枬ØG :+͵ټÕÙéN´žì.…+Å” V¸—aj±É>¿æéãjÃM ú
+;eý”ò¤ˆ ‰‘lgˆI‡+‡º2B®àŽð7Ö ˜æ÷çÛBß…%n~m\T’¡Jõm#Á(²Ïð<@2üƒ©/ÊõÇz{®ºäê½·w¡­ÒÄaëuûÖb#á© n¢sèm F@Åñ±[6õ¸ì©…£¹¤·@ó–‘j¹Mñýpy°j`§t~ë~oèu'.åôNç£na¦Œò1µÀ+¹‘…µHZµÑôÄ ë$+vœþÄ{e@X™gº‚Žœj–‚×Ð3)¡}ô6Ú&ßg ÖÃC ÛÌÜß<q­˜4!c0qZPn¸•ê×LÄÜ
+ç€;8ýX˜°VèôʱañtL¼SEÝË”I÷λÓWv¬­wß•ÌÐ]¤•M/vv:WÓ¾"ˆ ¬sÒÕÝÏ|ÊŒ!²èÞœ†ø}·í^ŒÁ®ÄYWzÇã´‘ƒ‡2n‡ö<ãÕtôúÉ/çņù4Zg3¦Â æ²Æñ„¬2>(ƒáR¢%•/¸>Üó‰‰@ï5}?ìú×ÿl›Gw9xôW1±xÅ µ¯·fµyÒn¦éª‚H7-@†*ß–'iƒÐsI’‘üÁuƒPÒ0Ðç zqQéU&Ùb\D¨«ºµO(Eb§2¯£©F¨±xýJŒ^]œ|×å*­[rACkŸýÉÌ&º•€Iüœ93®lð¤:M>Øü“9¿ƒ6øƒq%!JµöMMÆÕ‚Z5ri”)ý\@(µ/¼ÿ2_µ1R]exw›&€!é‚5“5Õ ¦ž‰41z0ˆ7!ú瞯{Ιs‰ e›‹vM·ã2wâ,J‰„
+»ÚŠÕ4@ÓZÛš´%´6b]äíþ( …Øb¨Õ"Ö6qù¾çÎÌnçRØM9ÙÍ~̽ç¼ç}Þ÷yž÷{­€vÃÈÖõí?w]ôù{Â{Ø2žRõRxj÷NÝ´ášìŒO[xìMÚ#ªvîýIþ”‚4ùrrkÆH5ͳúÄQN]«¦'.ûn)‘µVE\7í+ú¹G£$Iàý—¯Ì] tÞ·-“b_kîÉg~²´µGZ@2²o¢šå-~­ÀÍ/\¶Â 7èRmÏû™’6zL;˜Y”zO»ŒhSxivÙ°½ÐyÝ8Ç-¸2è7ÝÖÿ –žÏ7쇷wåwnR–h 3T(p8¨ÍE¾9|œW1ÒàÚ¸÷x£¢ƒ¿XC”뀴\±ëîô¸ey…,·¸÷Z~šŸvî2 )GDØ;ÕZYx’õ×w“˸ •2¯mÄÂfЩYSÌ6GÍ­é]ù¹E2
+éçiÜ·¤÷{߆:~2šDñCǶí>I
+0S ³ŸªÖvv_™Ñ|àŸY¾x¾hÇî3V'Ð{nï[W˧ïJ—`ÓºÉê
+ÐœÁfŽl¾ï÷*§^Û7ñÞ{6vx–BÖÔi3OoÑVËp@O⤦' ¶ƒû¹P–Z3ÂDÐ÷Ã*VÇ=+wñß·Žä(Ìv0!}²tIŒ¸Œ†fF±=B/LmGÏÒ±ÓQÛžðc­ìW°xï©L— A‹ò%Ô ¶{Gp_·x†Ç˜ÍN¿év¡ïÈýrGvû!¼r}Šžø¼™Ì†¹zðbMË°}ïýRÿO< ^#¦uDØBppç6Ó“½†\¿‘kÆÂô µ¹Nëk9 ¡'ùݲ«\"öºŽ
+ï^}$˜[ÑGšA:ÒO¬‡S+èUÊ.k»õFņeë|‘ÄU÷;­µ‰9‘i_šÄ²è/jíchðÑœºƒ3ŠhVA¤–¨Ü œ÷õ|y“³/CT;„ù–Ó‚$ÉŠ;Ìýt\ci¶½¼ÑbúÇaT|ŒgaÚd× z»åå£ t¤*(ã–•¸VД´TÄÆùª»›v+'E·Î4Vj¾¨tB45ƒfÕñ3%bÈ7A’þÖä‡;n¸Gª™­MAbÀ10®^šp5?UyÞjA?çÍ&bÙÜ€ÞÓõ©ƒ1)ÌòÕ¯âN ‘i™pÅe”sŠ¹dÆp#jÓ^‡bŽVi2”܃.[AO7
+%¬¤ËŸòµ4š,‰f½ÓÝèfl”6‡îhW"kZiËÀ‡ÂÜþçNúˆÑÊÄYA?
+£ŠXniER…¡Rìí_ñ¥Þ!Š%‡\e_0}ŸfЕæûR—y€‰Ddõ³š…vî˜ÌO@û0ö›šh€‘›#zŸ›¤–S
+VbÉ£‡«i]Ü‹Øô¯Ào0({I,˜9(VìËæ!˜T‡ÌÔh¿•Þ/P ÂR”_ù-ü ËíQÃùë½9™ùðžÍu5ž zMn‹-˜O ŒÙ¼`DðA*-~1Õÿàî Øs)–rkºø.W>iÑt°Ó&”¥P³Rî­É3>õ.ËH’þ¸¿æR†vÓfÜž.Ÿ+@«€À„öÑÄÛg©õòšƒ½S‚­Í@¯Ä£[ósz÷ÍÀвà£ÞmL½ ò£È[RȦ¶jÂ"dîM¬  I/ =äm@ZEƒýŒ°Á7çS NùA=Û vKì¥5mîtT¡âÙæçIhÉ00)¤°
+_w“¶ÓyVŒìrnZ¥ê¿¤~²ÁŸg¬P†s8ä¥ìsø.>Ï4äFÝÆ>Ðòv}jö•AŸ×ýbV# õüMm0¼z´q×ܨ¸÷ :ÔËýèâ´¯ìnùüƒ]“‚ "YRàK`<ƒ7Çõø‡ÿظ÷cPÀÁËšAÏYžo;3Å ˆžúȽؕ‰Kt>ouâ=Mjö¯>Ê(@¼“-{Û•1)<~Œ”"‘ ÊÚ\2]I:‚Þ Ï«ÃÞ‰Þ»6*p’02")1žo ~hªâšAØÅ+iVºPŸ;oÐ8i­Ì1ÖHš€= Ätc¡AÕ[@—)¦t<x.¥5ÓŸÕ ³Á ÖÅŽ ›àkÌFqÜÀ=Fˆ©©º!Ëâú?0?:ï w-·
+No¸x€4U“J'Ð{2Œ»¯úÕáˆèY¨LDXÄì­$ò=-Uï»Ó…$;Á”±ªî•tvC`1¢
+ñýlŠÂQ÷Ûƒ%–ûä„涞§öšÞ m>ÿŠ w÷|“DšçL)Šde¬Ôˆ´q×s¨çâÚAGå¦ðZÈXÓ²ñAƒj@´òcA¡7!aɠаJe©”Ôva3è"/`²Êš …\úBš§AW“ÓWO§-xfHLâqw[­ Y äÈØüø 0y(•aªˆ¶~(é@ï=]= ¼°_©Ó|ÊŠÏ/¹ó.q‡–œ3Q<’ÔƒšB­‚¿VС¡òá’“þRU/U7èº$¬)©/œèL
+«L%¸P©±“8‡­×1Rä÷ìóÝùuÕnkU[% ¹–g{q#!S3˜ ë:Öѱ• Hi'1bjº¡†nÚ`+lÚSa+­R`+ôCTžç½óÙ¹³ÝT•Ö¾Vâó½Ïû<¿çã÷ä*ëQKÞù«:
+ÿrH#J´´óŠA‡Þˆ(‰žHâìöäš
+,1¥ÒðŠ+`ÔÕÑ£h«:WtmÅQÊÒ¤ƒéÃ\ÒÊ€sVèãÐuÄÁaë‘þ7¢$õ˜¶ôhÆr B*Ôª"œ#¢Ë#7Èqïõ¬&¹‰ÄMWÁ
+ÛES7a£ñѵl—
+!zÂeï™IbB£ëgpeÛ
+ºf$ f
+8Šfê± Û\Ú–[e´ê¥‡ñ™Ð¼×Ó¡ýzŸY—V?‘6b:°…º]uH*d/ÁÒà3=j\‘ ë˦1ùâ#´ì„J¦®<ytizµó¹ ·ÄP§nÝ‘³ùˆM2_?Ý•Ï&uäažKm¬`MƒÏÝOj1âÏ8ƒž]2o˜Õ’JážaHT-æìè‡ס³=ØÊÔ‰O/9Kkû¼ò í¦q‡NU6¦
+Æ–ŸW·œïN\-BâëP0 ˆß$¹ûàO²oË _g
+ Bêâ |¶Ó~à?QmÝE ãL:/:gÖ¯}Kìàp]^vÀlV9!1Åá<!Ànêð¶£a»×.|G€}1ùúexAl‚Îü@¹ žg áÁsÄ
+Ö.iÖb@[DH¬µ‘ íÕ’ˆ„`‰ š
+. •B´)ý‚X©•V>Úsï›Ù}ï;·ìÎÊìü˜æÝwî9÷|Ÿëì*Œ§Ë!±v0Ê·\7´
+ݦñÚ…+a²‡düv¼±®Êãèpºœ6ƒZO…—Óí4yQp:Cº<QØÈêéM¸ˆ‚€Óé4®;ú@€Óé²®kÅò^øp•wƒƒiöïyŠÈ\NŸµûbqx
+譀ܮ Æt.yíô?—G
+Û`Þª£ðàÃÊÓÓûˆ‘wö¼IÉqFpà”Ï„ÎÝªë ¸ÿ€(“GäroPvqÒä'€“W:¥§¯q V™ùƒ‡Œ·jÊÇ1MË
+Ú"tÇ©(N7Í€æï}%$_ú´Ê}&t-{) ѼzH‰ÂdPBMI¥ýãÓ’qv óôP%!‹…oRDšãÀTzø¶4]v=ëÞÐÓbd/ëV>Ä)?z8¿Ÿ™­NV†U¤«úàÝÛ÷P½xˆy'ƒù—xîØbRXÜxXßTÅ•y
+SUª‡•³_~iè¼æèþýUÜ­rõzšU–D"û'xé2ˆGšã‘jg¹‘Á‰úÑWE£ñæ ‘Jïa}é3rîW¹åPOBâ¾ô^=¤&ñÉQßs®4ú=;2íô1cËKJËJ˼(ZeºDÀã_[‘E–AÅŠÒŠ¸'ðŒ¥%~ôÃËÊK+JGF¼ÆòA\ª;äÍß—–—”•ûÒ{ô­phi‰¯"…ª‡.KÖ´_µÚë뛣‘xԃȄÉ[;Ýz̲0Òì¥ëBUý⻫CÎSq¨­ó%Ç«èo꯱|7¤Ñ<ÞíV[FöWùÑgé!À˜Rßœ¥oÁëá‚œAzí9£»‡SLš¾Ħpu$ÕÿµÀ©–åpÕÑÕÑœ¢tÏÝǯ¤ƒ©^? S×ÅÀçê‚n Ì8}œa!8ó½Þ¦ù÷ý¦ÃhòQï…‘SÁ³x8QÀz¸…3ÈýuÁ¹nžÄF÷¿!0×liIøÞ(¸¤fÙÁè»! ¡ìâÎ-~ôÙzÐ9¹pz¸@¼‚ÊJ×úuÅ2%‚o9$ÖÿP½Êqüä×!¬¨GO/³ËŠjƒSy90ú2ឪ
+Ø–¡GÕVhd,ƒF¯ z¦dÒ~B.ƒÁè‡ÝñÂé­”™ÌÑ9§¦Ó9 C-*ß‘;MÊìT#¡“§¥ ê¿Šæ¾ÄÛĦ7JñZÑ”çÔ14ÔL¦AX1íäê¬ê‡”Áä™´´yú<Í”X„@iãL}u|º2HiŒK¥†´xÏgX ¥v y~#t†¤ÒjG/¾,3ŒL\ 'L€†5Qò¢´L¶Žš§6hÒ=OÝVy±qÁ¤°t´ÿšÑãÉ®dnzØðÀݶÑ@h£><»wK[2yhѼv‘˜Œ1˜;m̪¼SLS|uÇæ-{§?¥è¸Øßð`ãZüëš5 «h Äqü5mŸ2óઓ;Ö¨r­‚/,ƒeÖ6_:y|–HÇ[퀺OœÜ|âÝ«UœÓâþES•ËHÕ{†œ¸tªc©É.”á,ëƒq·Ü·sß¾é“å‡õàCôÍŒ?­úlú RˆÝü£<ïH=yEª"þm“5FH«Q×%“/‹øËÊPÄyL>Ε‡™ö‹e‰¯¼‹M·‘É5™ŠVë?4; ñ¦µÇèΠÎøß1Ù³0ñÒ$b»}5~ Êô]tà±kô°Û ê3lEDëÉwÉVª=‹a2AkM”â§Ì){“ôˆÉØK•J˜L^· >IÜ&Ã’Ë4nÿíKпÕ5*‰ ãÒÊOjUº¾÷ÝÖZ$ëÐ`WâšÐIñƒNTLZ;_¥Â—íÐwmÃ&K.¶åpëûâbs§*·µ×¾‰83]ñôGñôøYÖÚ5QkܘÀ(ùœ²Ô€Øó7uÚ¢D|îÃ:_œÂ9þ»ÉøÿZ¾cùp¯|þ¹õ;YÃu™“lN_'CC
+/,} ßéøÏ ÿ| SßfTDZ)Iòeìa¹ñoøõ[ŽàéçÚÎuÆäQÙ¨&…வˆ§÷Lß1ã4¹òW’R@ûy+ù‡¶G?z…üÞЩÂö Pç¹ÿÄ#Î-˜Oì¾ÃqõaðÈj´Ž}xû{ŸnÂcqªö“’¸k6Z/?¾éÔ:LÜA qÓu¼1™¨ÕÔa&¦Z^•6£¶iÁ± ^@k®¤"£í£´Ù¤ËÞ¡@p
+g«Î©³FLáºZIH¾ü¾ZWB\LáÙïQwÕ­@¤2-ÚRx³+'‡S¸Mæ|(‰³Ï¬¼Õ z[O[­¥ÊÏq-¸KV™Z"5—áMëʈZÓÛßAXRÊœçqåë²!ðý¸qCHÎ{0ùõúö&;ÔŒû“‰O¥Hó¹Þ!yjç,<èÈtYêÂpkÌzk©TXëœqÏST-jjz¸€Sòyˆo;Æ•›WcÉÙ¤¯ úåoáQ\¹žÜHŠ½Œ‡ÃcᔸÎàoÖ&ž
+m)ͽWšL* €!tv¹ÐE–˜Íý×íö8HvnïÕ»•Ø'òvWÿ¶-‘Eg;}•„ŽwÈ•ò_ïþß.û0nz‹·u<í_Díëh¦wB£‹ä)\îÚEï$9ŠéDÃXL¿>Ý&9Â?`š­¯98©×pZq§ rø’#LW˲DƒÅÌw‰U<bòTǯàR$»ð´)µWÆ{K»n¡\¯Jóñˆ?ÿ M>"¼ õlÁwÂÈ?LîP鬉tCR2øÇQݗƵß3;ôpŠëüÚ4”ÕÑ·RØ,Þ‰BÙ|6kU¡—ÇÛ8ã
+ »Vë&«¹Ø!ù~
+W0øð¶ð,µÑ)©äà‹  38¾«Ãmøâb¢Yñ`Ë\<Þsá¡€Ü3EŒ§Ú¬Á¿Âú´~’+¯GKAÁN÷S
+‰-"èì*P¢;Bn§6#±M?‰È
+è"lvŽê&vFü’Ézÿ‘ó¤ð3µOKÜC©áð'R$ìu@1á3•}•Úéu
+åøoêeèJWV†„Ø£ÖÐSNzÈMœF¦^ðwHµmôc£t.YŒФ»¾8GÙ¤"%%ú{~>iz&ü©X¡ÅŠ<Ê.l5Ø»ËÅNˆ ]k‡Æè$²o‹Ïû‰B¥3–“aœñw¼£Ë*ubQþY¯áDñø5—^Ћ1RgpŽXMdÄüQ¶Úƒ
+ŽÔwëëp¾g8û] -“à„‰qy¾éëg³P_³ºÇLŠ1ÿQÑϺ
+–Õ—b§Ñ5¿Ä¡H´Øsºi=âœWysŸâ‹8•C`Ù¾·™¥ ò™óziãš~±4¯*¯jiÅ6¸© -2ô…§‡œ3®œ+gcïZ —½›z¹TÞ¼:þPÛnäˆëd[È[Ã,§¤IÿžU Œ¼ è]ˆÉÀcdy{àaÈ HH6½xJk§X ‡k{9¦¦ÆO?Er¯©ÆJ’Ì1,ÖÖXRÐÉ(zHtª…î%p‘DM3MŠ0–áçœe´¥il@¥óy—¶gõe[ÿ¬ƒ‰!M×ÂŦ¶}žÇA~c»2oÀOZAs]1Ô×%à{n “`ˆued¿h8õx-ý â #™ü@K“¨ÛëÉûÞéÙSŒî‰mñ×n‹N+ðC¥™½IÎ $îü–Í9I̦›¿»°­Ñ.3EqÛ•]ˆ^IdØ*î
+*š¯#FC¹q,ºÚó( õ•œB
+nÈhß¿%sÊ!0÷± 'ØaçS–…TÙJªœ­Õ˜ó²U.ê„äVĆßÜ'ÃZaÛ*1ÏGoE¦Ž\Á™O·^9seojCCÃö†­åÌê9"ü½ì=qJ…ØûÑ©B¢þqÝ•î÷IúOœÊöü$`í2\ŒÜŸ‰> ³,éOy‰iÿ) þ›Åùc4"ß;ÆNÊžá¾"ìÙNu‹Ér(סè›ì`ßá° éµÔ;(WZÚåjïœPÑ¢ø
+Aå5Âÿ0¢VĽqì:Cˆ‘õv9Õâb&x›ºAo¥¬ æn&Î~®·„g‘ãS–,…oi÷eO º¹MoøýoÞGÔÓ¨Ïa*çÏ2KkMægjùUš¤Œ–AŸ µ‰ÝeÏ8½Ÿ\‰I¿œoµ%:quY¬à¶ø¼ ?=‡VYË°ÿßûÿÝ¥TÊ/—ª;¹xe¥ÿ‘°÷à2ë/VÐmßÃÎB‹‹vƒ³SDד“£²»Œ$+Á猥±Ãù“KqZh=n3sáÀ©KëͲ%c)7÷
+qJ³î ç†àIº¾u¤×îðm•7Nn(¬”Ô‚ðyá_8ëÄ$_³ND—,eÒŽ!B‡XŠ]AÝ×19±–8øŽ6kºw$‘ÑS€ÑÑs†ÄY÷™ÛÂSù"TkYAß¹Ûÿ›!¤¼mÖvQ kÄX¹éùªóssŸ_¡5•b’ª$²ÖýD³/eDÑ¿Q4H*Ÿè˜úD?à TªŸµƒû²NOHë¶ôQe‰&Û§ð[q­&^Ed¯ø^Äm*TÖ6àå ÆÆ“8Ò¶KÄtPÍG™Á½ÛΖTÀ*hñLJ1z!nq±Ó.›¸5‘lPK´5Ô­£¥ÿ—zèpJKZÆQœÈ¹5>/»çÓw$
+‚Šnü…]6½1Mt%&N+ïW|7¿M\’£T{ü†‚³™)‡Ž³ ’×r]BŠË3¸Â lÖmaä .Ñ0ÐÄSgQCe ¬¨þ6ím]¯ÁòçVÊ?Ê;¾Ð›Ì]"mxí54èjí
+Ÿ{ï»ó&"±A1D(?ƒ,üC"D¢D±
+‘3aÓ…1c1µ3aHCTŒ’&ºp,AêÂc”µ‰bMlâFS:QSÁ‰RYÔX4X¬ÕóûúÃÊ•­-sé{ïÞy÷¼óóïãî»åo†ú…šd(WÇÜvŸƒ}¯ùÐrÛÛÂs<`l‡˜O¬lXÊŸçw[=øcoðÓRÖÕqKLÓ>åÁ3÷¬¢”’/`.Ý8¾6\8‘~ª£y÷]âL;¶ëÊÌ;H‘ßfVp_–¦—HÞ+
+m%Òv<ûbéÚ†Yo'üãêöczZÖçScNf@Œ87áU:~m¼‰ùÃ%7½%•ðJ íö9ÌË}˜Ì³8áã§ò‹Ö瑵Xu5%É/‹Vn{ñ`‘ç‘$Ôd¹e6Rýç9ñøþÇ^¿¹7«(ê­ WRBÙ_EYž|+Ÿ?u<áž M¸Wç‚Žø¬¼eúwºŒ@s¦,5˜áÆGqù‚gß¾™wÍ¥¬º3ThúÃ2µ÷Kü’Ìa‰ŠôUµPì~!´èï\8ˆNì­‡¤†öÇ-FÉ€ø»Bò:ành7~6æN<ÄUäG8Å6Q\R5—¥ÑGxßÇDé<–”òÄp'sóî¦2·$ ÷wtäؾ“ÌEÞ%?nÞ¨`N+˜—(¡Ør%‘Ç 7qÏ
+Q=ó¡U#höJ¡„#ËœüpZ*ax!w×S˜ÖV¯ð97f:lOeh³p` ¸ âöKW±™:dÁ¹YZSi4ˆ‡tÝÞ^yDû/‹Jø´ý
+åòÆ÷ÇNË(—äÑÎÙ—û¹t¶s;g ÓêöKj,Ñ?[ºêÇÏÛzi©® Î{?9PüæÛ7ųú ºÇ©ê¢†ç“Nm<jwìµ"?u±ã!BWWÑÿ;:”øËÁºNê*küo•®>
+±wé[ŸçÒVcB+¥$i‚¡”,´A•´ö `Ý…Ù£÷FA~å0”ŠQ<€±¹ptJüå¦5ëµÍèÉú¹ô?¥Å¡p&ÎËÑZ²òb|ã áq¡B¦]GûUË
+AÆjhÒØØ1ŽŽ¡
+2—*EÒÃCxá8
+Y‡ª%š>IfBÁ
+endstream endobj 2039 0 obj << /Type /XObject /Subtype /Image /Width 129 /Height 128 /BitsPerComponent 8 /ColorSpace 2026 0 R /Length 4903 /Filter /DCTDecode >> stream
+ÿØÿî
+
+ 
+ÁÑ5'áS6‚ñ’¢DTsEF7Gc(UVW²ÂÒâòdƒt“„e£³ÃÓã)8fóu*9:HIJXYZghijvwxyz…†‡ˆ‰Š”•–—˜™š¤¥¦§¨©ª´µ¶·¸¹ºÄÅÆÇÈÉÊÔÕÖ×ØÙÚäåæçèéêôõö÷øùú
+sƒ“FtÂÒâòUeuV7„…£³ÃÓãó)”¤´ÄÔäô•¥µÅÕåõ(GWf8v†–¦¶ÆÖæögw‡—§·Ç×ç÷HXhxˆ˜¨¸ÈØèø9IYiy‰™©¹ÉÙéù*:JZjzŠšªºÊÚêúÿÚ
+÷®‚»®×s³Þ=و̈ŽL,ÏYRљ֜{E^÷ï~÷ï~軯ÿкv5ü½ÿ
+ç«6÷ï~÷ï~ë :÷¿{÷¿{÷^ëÞýïÞýïÝ{¯{÷¿{÷¿uî½ïÞýïÞý׺÷¿{÷¿{÷^è‡|÷ÿ
+ç«6÷ï~÷ï~ë :÷¿{÷¿{÷^ëÞýïÞýïÝ{¯{÷¿{÷¿uî½ïÞýïÞý׺÷¿{÷¿{÷^è‡|÷ÿ
+ç«6÷ï~÷ï~ë :÷¿{÷¿{÷^ëÞýïÞýïÝ{¯{÷¿{÷¿uî½ïÞýïÞý׺÷¿{÷¿{÷^è‡|÷ÿ
+ç«6÷ï~÷ï~ë :÷¿{÷¿{÷^ëÞýïÞýïÝ{¯{÷¿{÷¿uî½ïÞýïÞý׺÷¿{÷¿{÷^è‡|÷ÿ
+ç«6÷ït‰üÏ?Ÿ'ÃOåo¼öŸOvn;³ûƒäøÅÑg6ÿ
+Ìø÷×›«na¾\ü
+ùçñlî¹|XMíÙ]cO&:XV岇ÈUmÃ]GKa÷GlPnZÅŠ7nºÈ¶Ë÷ïaçWvvÁîž»Ù]³Õ{§¾:ã±vÞ+xìÝ·êf#píÌåø¼H ë)*¥ìàQÃ*ˆ~ý׺÷¿{÷¿{÷^ëÞýïÞØ7j ·În´•è°Œ¦f±)ìf–“AS_:Ó‰V©+LÚUš×>£Ï¿uîŸýûÝvÿ
+ç­xœ¯òšïîãþiýuóÿ
+gîï—_%>_üãùÒý-Õ[§kmíƒñ÷ãwcTlü>܇{ÿ
+s˜ê%EÓZÒy±¼jš,Igß½ûߺ#¿Ü/7;ƒu}1žá•PÈR8ê¨(£LH‰Šé©ó'¯{÷¿{÷¿t¯ÿÙ
+endstream endobj 2040 0 obj << /Type /XObject /Subtype /Image /Width 730 /Height 743 /BitsPerComponent 8 /ColorSpace 2026 0 R /Length 606987 /Filter /FlateDecode >> stream
+H‰ì–ýoÇÇÿ€þRµ€}»;³;»{ç3†@I  
+EšB µÁ`;$ˆBÒ@K)à0i ¼ƒí¢*"iUh¨újZZAi `Þ1”"¥¥¨©‰B1ö½ùãóíîÌôÙÝ»õa ¿DÍW_Ÿyæ™ÙõÎIχs!!¡ÿ{QÎlw2£Ô¶¸“2›KÔ‚?6ccþ²+F]»'Ñ\Æ“Ÿ³rÅÔrS;çàÁ¹Ê3l=Ë;öþ×wíÀ;9”B
+Ö±öê1‚°aªU3uCꃫT.¨˜?^ÅkK–nh|»µ¹åÇ-­à ç΃¯_û[úÞ@@#Y&ÉÀ¸S?|ñõ”/LHHHHHè‹'h¼´à çýÌéæ À#Í´ã\Ëd>íë»|çN[{ûþ#GvïÚõú† UË——WUM{ñ›ÇŒÆ U'Š!%
+1°¡ª`¢ŽÐµ‘aC
+ë²wZ–"º™+ËÒH¾}P ¦A™©é°„B²?…ƈh‘CŒXAEáÈì—J—~gÉúu ;¶mÿ`ïûm'NþõÓ¿t'SÖ`†ç¡‡Ï$Aüt.KHHHHHè‹©Û™Áëé+GNŸÛ½ÿÕ[¶U|ÅÔy¥3¦N%’‰Fá‚/—˜ˆH_…©ßý¡ÑcIÁ’djÐåe‚&›¦&
+ð‰1’.N¨
+Ë…0šC†`yȹÛ%Ë'RÄ@ÅD‰¨’®£0¨#
+tØ"B
+wÊ{ )åc ä³Ü×јÍ+ÃYÚßÀ½Û©Þ?¶ÿiϾýËׯÿVù·K&Œ•MEÖGiD.
+kÑ01T%D䉈.E ©„¨Ðz 5šª" £CW ]2 ”q™=ý¹E•“ÖÖLkÞ9ûÀ‡•‡/½zùµŽ[o&ºÞŠw­‹ÇjR©š¾¾š®Ø²Dle¬sEOª6•¨IÆjSñµñÎúîÄúÞd]üÎÁ©ØêžDÍÝžZp2¶"ox¨»{+Ê¢D—.À……‰î†îÞ†T²öQõ±U©žúx²&Ù]OÖÝéZHÕǵ±®e‰ø÷þÃn¹þÆq•aüàµñîÎý²^;÷€B% %´¤±…4NHÓ¢œ8µ÷6;³·\šPÒV\œ¢VH-è ´¤‰DZÄ¥ ¤r -QŠÔ6ÞsŸY’8öîzyg ®Š’˜GÖç¬Ïœyß³Gz~a xžâ,Å9(Œ¢”Íi•b(ÞáÌcÔ!ð/Vüå¯~õç?÷ìö'NŒ¹ù-{wߺíŽk­„)'t%1’6á褡”¡Zºb«)[•L-a¯hIŽT“3iÎ0­ ŽŠ­Xë‡7íÙ¾ïÉcßüÉ ?~ýW¿
+솪r9.Býç©õâ7œ€× GÊ<à
+9ï;Ï|÷oœ_\èöâ?–/@»Ý^á Àtån
+L.C ÔPá´Ê
+d´•2©…À0ȹáú>5-oK°†·¼ßuàLp£<,Ü…–WLiî}# ™èAxºƒÝ0)1¨9!¯
+|Úé.bÅŠ+V¬›¨v¸põ‹OÿyoÿÛoû¤‘14mȲÕuZ2 ±¢¦R†¢ÙLí´2lI¶)ŸØ´vjrlöÛg^Û÷/CøBîc¿Lýš uMN³ €hf´à7Â4àyìÏp\䨄HÓ<åEô§E&\f1ÏžE$˃|qv(žŽ6$3ÍpZh‰2ð „uH«ÀÃwCZÃ΂8!‹¦Ìw*·¬ò<ÃÓ„º×5CÕû&ÖÙ¦fhò°¤PVáÄ…ýo´~™:ú†±'X5bRn±
+¼Ê€v—ë‰Z&îѼnÙÈ/¢B˜‡€iç­b“NÁ7;˜¹,¨!êùØC¼Þ •À/Á+`g¡Y÷i-`U8x/E…€;Á¿Ø/ÛØ(Ž3Ž÷k*U%rÜíûÞÞmˆ“¨p)á¥(@
+Z …¶ïÕç;ŸÏæ5¶¤%­ú¡B¢ªŸÚBÒ8ÂP5"%jQµD”Rûnwfvöö ûönûì­k¡H Ñ&Q÷¯G£ÙÙ™½¹gçÿ…Ru ëÖ˽—3'ÿ´ùðáím çÏi&È7¸L
+Å·™Z†¸s4|‰æ‹C)RÚIJ©¡Bþ½÷:_yySûŽ…ó[d‰ùZHfÖ'rH%"±23•2a…o†ôàìŠ'–þÙÎ;å¤ÙxzŽV¬k5ûz­z­Ã#•JŽL2Q¿Yµº¬º&ïCòäÉ“'Oÿ«`¯‡ý|üüXÛ9S^¯Ù7`?¯kÔÁÃvšnÝ®S  ˆ=zct¨ˆÎ¼új4_õæˆÄ‰`¸ ãSY•ˆ–¼ÄøCÂô óÕéžåE²‰Uoö÷_çÛ ²‡â>ŠzÀõJ¸Û@Y‚ÅŠbÜAH.oïÚ_š¸C!…XüÀ@›ÈOWäFžvÞô˼(¢}çô¶bñùOÍ—>$Ý·~ƒRÀ»±*À“`Ò]Ô[uœ0i«)¬¦¡$Z²\êÂZ,ÕõèÓkÛ¶,8Þÿ$Òb:ÊPÜé°
+´H!-¡“N¨hZ‡QÚ‰ôgnõž4â”;¤¥š†™zÑÚMš
+@ëÿÈœ&žáùiâ©?o*h;áɦ¾øþø€d ±8t(,|å‡k>·åñÛЈ $uhL4 íXÝE4x=&ÝC<ÂfM¼ ~øD'I¬'Õž.F¼¼.Öñ­ù-",Žh s³.¨ð|DáØ€ ø9† ƒóæÍM§'þP³«5kÌäšU½q}¤NË5€J¥â&³U×Ýù¬<yòäÉÓ 6óê8üE`3¯T­«5kÔ¶ÆiW^{ýH4¾¶Q™¥ð³ÂâC2‘x&$0¢Ÿ 2Yœ’¹°À¬^þÍ_¾´öÝ¿nsÀƒÄ°žÇA$¥âvpXC;ž¨§4­¡<B9ǧH
+‘„¦Ç”D¡$¸R¯wƒQjjôîãÄäÒ-eTÔýÂþÕa¤‘ã:Z¦/‘b—ªmE8­‘tyx—‰ö·\¼ý·ÚKHŸ³V8^6â¨Ð1Ls¨7ÍÔ Ú†i¦4œ'49Tج¡í&íÑ‹û”/²l“º<˜ÑK‰ÿ°_f±Q]gêC•V K왹û23¶R"™&)Y1+P²@!…ر‹=ö¬žÍ Æ‹Ú,ê¦Vªª¤!m‰¢ªe©Ò6!•*¥Ë¦<÷Þ³Ý;cÀ‰=¾îwm‚T©~ ˜¶ó×_gîœ9sæÌ9ß9ßïPš Æ |˜Ybel;FœÄ_ÿ²/$Þ)Ý¥4èò|ãDVÂf“
+è"'K¯jüÊƦÞhϑ߶)ƒèõ€y–FfÃØÓ\tÏ=¸®;ÿ¨ªªªªªjá(2]q¡t=]G‘É©+‡¸Sc—ÿüËC/<½å«A}‰Äû^k<H|­ä÷«œ¤ rHñß·*˜ŒÞûÆ¡­€49¨dïFÅ=ÔØkY=¶ÏÚ, I
+ò223ÌŽRÚCi 8„Ð,³(4Í2û…Æ9ÈkУ´—šiváqâÖÒˆedÒÞ}#­j@kK»¢÷œ:Ý}âä¶S'¶¤pŠfú,•ëñë¢á¾ôÎñ.˜Rduœ(ZÉ÷?èypdÏ?}Ç~Ÿ6ÐhùZ³˜ü?ží”ªè_¼jyÐÄjg‰[Å~Æ’eš·I¸±Ä‘£íw×ûa}×·6Ì7NÓHÌ.P¬` ¬2%IFS÷0cƒÎ–
+%(k
+í! šc8ƒÍ”C3@§ÆX7Ä Äƒ]Naš¦öiŽ=²)Ú±zÍj1$Ý¡rK•€ 4M ‰~EDUV>H>×Ö~ìÈQË0H&&&®v•@ªªªªªþ‹ä~fOÓs(Bhñµ×Gw>»6¼K,QàF*t% HK%NPMU¨Y×Rÿ—?õÞ·ŠÎ(Å †òĤhÐK¦¤›8]&ëgd$Á@`Âú\‰Û8ÍPŠA‰ÓÄJbäeg0$DH^„ô2Öë8IÇîc$Oq~áqâÖÒˆÃE3¶åi÷‡´ÆD™‡¾°à«Wô=ÑÒî"ÎÅ€èD„;ÿñÑ1‹™t×ù £OllJ‚ÂEê¤:Íï«×>ÿÒ‹-_ʱrá܉ã›ÕÚHDå6·ÜGYŽ ¬Gh7¥ buQKXØ¿Á
+ÌÉî] K&p¾Ž®ú‹V®hö½û~{8,Hý¡û¹KÖNçZoºçaÎÇ…Äð}–íŠgßík]W«HŸ“å¥@ž_Y±èWoF7´*!%¢râÉÓq„¢6Äf?Á ÇIcIlA
+Ÿ.}.ýû‡è_¥¢Š*ª¨¢Yk¦TK;ÓåÃé™ÒbàÔôÔ$pÉ$¡C?:¼gÍšÅ^O)-ôJ¦ ü¡×Éus Í ¹Ò£Öulm>vlÓÇŸ%x1^ A ÅŽ ¶P‚² Ø«à)F#ØÚQ×µ·ûë¼fI#
+äpBÓÅ£úôæ·Þnç(n£0\5q!ʬ$C°ò©'Ÿò«R]͆‡—s'“·º›ýs\7»½‹uù³¦É¬çW5¿¾PŸ·¬Þíú㻣tÇꊦŸ¼sê D£½Ù¿âW«,YèI¤îzö鶎-M~ ¡Þ ÒnÉó¨à ŒÂ‡J;VÂá8Í`È壔¥1 c°íFÑ|®‡ó Iè(‚ž,Ëi„âã, ”Rj’&( TSþ±¸ðêKq‘}š³(¥Bz
+Å…L¦(ú'.<7òñž_¾ñð¶-w5únRkoñ¸aÝæ×U¹uYönE’ ][ûàšWvŸ¾4]B’2­_v`”²®á³[QEUô£É‹—¾ÈwÓ—§f¦
+(
+£pò
+Q†BãÅ$Éè§6FJ“bß鿧ŽëØþí^¹Ú¯zêu¯G’|šæÓÉU­+®zS »N¾ýÛ/¦/]aõBûWô^QEUTÑ®+Aoæ‹Ë“¿
+ql¥(ð
+Y“ˆw["ø·³Á—_Z÷ÀJƒ,{\ŠOöÕ+õZ“T£ût¿®*w·Þñž?;tæ
+ÀOM—P¤B#UTQEÿMO]š,…¾™ËŸOØ'ÞþUÇãuµÚÔuÞÂJMƒ¡ª®SsÚ +–ë{2Ëß?cÑ>æÄ (àˆ^Œ¢‚ENÂÖ1FÂR-Úƒó{
+ó¼¹Mg‰&ƒ¹ÌDtø¸¡0g îfmÅ4Š Z
+¡4BØÖ©üSp¦šm.¤½›Ü÷£ëol˜íŸ©É³d¡V—5i¬L£¿Ö§k¾X¬õ׎ŽG&ÆKÅÑ1®±³•/ÛØØX9ÇÇ'Ãóìèhéò凪ªªªªÿ Ñ}4I çÿS*AJ,ŽŒWrãXilg†½çž;°êÛsdQPŸ,^«ÊÓEß 8
+Så$Éqæ²cÀbÝkÙ¦ítò»°e´ ¡ŽK8>#éëæꊮªªî,±­HÞÍr» }
+?-óÉEhÙå+g&c1JLN³?@íݘìtœ–ÝKÝä‡ÖÄ"¯¿±åÅ~¸{×ÚÃGÚ05ó^òÝw2[7ÏÕÅ«I6jä hŒiAe±.ËA¹vðïÐî.N3Øê,à@ì)«õÅ߶Õ)AYœ¾qý2†# ›ÄŠØ
+û/@Á…¹Áõ"4â¹è…J œ ‹ëØaŒbðÂÛás
+¨` ÅÊLrA£åÂzžF>y{A€rcV,áò8¥ÇÙÁ°é ™ÄÊ;ÍIÒ¥ @,Žú)Ë8v¬¼Hı:(11ÍØ´9ý^~`Ð y5ôì³·=½3±ïæ´ ‘N—õÀØŽb;î2ÇŽ÷}siV«ÍµÎŽëÞzÛüç?b¯ýeky>¬õÐ+ͪ,H5×/Ö†‡ÖðLlÛæå²tÕ£nÏC·»^öä©Ð’¹J@\* ºâ7ä@hyÆwÝ»ÞuéK®þÉÞU›6/À®“ÒÉUËP7jçÚ
+<Ÿã±n˜¿Ë¢y£(ĀĜÈTþq™IqxÌåàÌ8Ä…íÄ ÍPÒ؃œˆ…Ã|¥©uûâçø|º¯¨^®üÓT)Pg,Œu¦ßüû›e›ŒÁóìq.<«ªªªª>»*G!ÛAcä£3㥳gNÿæЯ7ܶR×eßL]öÏ6ä€! YÖüêŠÅÇÙð¯c!šÃá[ õäY’œ2 ¸;Oz¡„}*î²n×ëÎÙSfïÏ›—p.æXíy7îXqŠîöØÝP¯/ÕøÈNÜqëÉ/†fˆµ…Õ‰¸®9Õï*h?>I £ ^¦xQyþ8íòtKhø!ÏSÄ
+QöXš:™2 ò ô«ËÅ™‚›µ¬6B[ Ùvu#–m2ׄ7Z¹m§‡;ç$F8ùæ^ïÏžÞdÀ¾›åVjÀ’›×ƒ¹>Lú{–tEµÛ74bþöË=8ªúŠãý§Ó?–G²¯»÷µ„
+‚kòNû)Îb#MH/£° ’@Vö¢c`‰8xy"Bp`•¡~€‹õƒ¸ÃHÇy“ìd
+˜!8ü›
+¨—ÐÁrlª_Ó<]ÖæÍv~q#lT*6$†Å®®îÃÔu¿<3à
+èÓÕgl0p”yQÎ2Œí&hg©”áf’pØç½WË0á;!fJ# ê%†WËŠbWG 2#è *Fq)VæCmnÅ Î׎=îü–ß[T%]žæq¸tYõiºâ•o¿àÑ‘GãP§“““.\ø¯²­ÒHÕªVµ/•qNGöî™ÛÔèõ¸5Žw¿Ò¦ùܾ–Fy åñw2ˆm+â(‡ï¬³qÁâpœ‚”ˆJ¯òH–ò¢Iû0g—šÄgŸ%pÿWNK›"cÐŒ‡}î¯4H³üòWÇÑžk5?ÈwÛ} Š¤)Þ ZS722û•—Ö¾z¤ãè‘uWôÂx€Ä–Qžæ,õ¹4Ê^­Àg/çÎc°Ä”¥˜H¤‹[!Âðô'¸È
+¥æVdõüéDïðÐê¹õzP÷»k
+
+bYq 0&â%…kÜ~ÄH Ò‹¶n"܇YóX‰„-º  ܱ´”`<mƒ‰€–QºÙ¤INûçúƒ>w“Ï;ãч®Uü˜F¾·¦ÉïHŠS“]ªgšê÷{Ý3t|E×¼ª&é>EWœãe‹ë×´.l_Õܺª¹³û®ÃmC;îÞ³÷Ž—^þÑ‘WÖ½þæ ¶ÄÈQÔo¿])Y6óF)lŠ¤i3CÄä ao Ð4Â)Œ³‚Ä8Ž#i±~Š³àe„™GãYAU(O2+‰D›é"KÁ6ƒô2a£ ¤Ý aÄ¢UŽ£¸‚†ÙÄâ
+Öi’¬i¤íàY¸aÒ.Á¸Ç© »•FFÄâ€U1Æ¢ˆv‹r6Åi‚ú*;Ê.1ØcôîdÕßôiµnÝ%ë’¤«Š¦¨^¯§a–6²wwátaê<Ô2`ÉGv•Ûu>uþüùJÅÛ•~©ö/\ïS¨jU«Ú¿Ù/Óب®+
+1Þf3c=Æ1›”4mº©š¤P©ŠEêb  ¥R¨¢TUTPÕÒ4ÆãyïÞû¶›‚Á¦çÙV~ñD~ÌÑ‘l?½7~ç̽÷ûN=΢OF§Ùù˜»5{c¦6ï)ÞxgZ*ž’‘ ]H\¥JªÈûbŠâÅë•×~Ö¡[¥òÇ®5j’~8-Ä`# ¸ïÚp¯’knIŸžšxPâ: !
++
+¾zEiGdET‡¢¼b„F^Ž²bÈϬÕŒ±‚*ŠàÛð°ÔúÍØîÑC‡žxéÅ-¯þ´íô™½ïÐ71©;I.ÃÜo൩&yèñŒ"FW­Ây5×JÙ4An“ö#Ž+IÛ€æ»vª
+Ž’…íP«eôJÊû®íÌüJðìCkŠS³{ª6¤O ƒ{PU³H¨æ Tô,< åÀ¢‚GÀâ ¡|£’5¬Á«•Âo´mÚð P¡í"Ϩ(&"?â$ŲéÔ¿¯|xûö ð9ÏL7þÂ~‡¸57W·‘zÔ£÷=¼³h~P‚‰iñ€š…c ´äöë3ÿ+_Hñ /ra8ÜË+œŒ|(ÄsmkÞyg_Ù vÆRצŽÖ¤Ç¸g, °úýv†{–
+U÷QPÒUu
+Gh—y-_Ö=ÇÃvß±ãm|ÉáãÇ6ß­÷§FvGÛÃ2B‚/"ùOåþ$x¥;xÇܱEêhÛ[¤m-¡§¾^ãÃAØ/Ä8Iñ±J
+*aFåP
+ Kk:ÉŽ=×ÑÚ„\XTIàLX•8a™ÒÉüÄøÕ… >{sqúXœAêQzÔãSÓÓÓÞùcʱìï½üݦfQ@>²€Y0*ÊüÒöÖ5gÇv•Ã¶=l$F9˜ìHe
+±b•Ü05ÖìþI©m¸~úÎyó´ïÖ™×O/½~vÉõsŸ«þ~‰{æó×þì+Ÿ•?8¹ìÜ÷—þáöÔ(ÿã^ßKû#+Ú¶HO?¡¬Žú#"# b0ÀH!•– ¢Â
+aËåxÅlj¼ ÈŠªðälDnT¹Ì
+ã £
+3`›C¶Y„Eh‘,0ÇIÿŸýrâºâø‡ª Äxmïkvç½Ûø 4 P+(`"žE`¡`cãÇz×ëµ×kc †„”W’Ví—V¥Ú¦©ª*öîúER*šV¢j*¥EiHÒ°;¯;wf½Øø==ùÈG¿øèh_3º{ÏÜsÎÿwD¡ÙLN’¤f¤@Šv›Ç!Æ$©GR
+`½GÕN
+J’ᆳ™ñNMïŒ#^SÉörÆí«Xa?ÕU=:¼éÁý»*YÊE‘ì`âБ–êÄJ Rß$‰|·ÔE[uµI‚7>ÐN«—å+J‹_<°Þã*XYh9×WõÉgõÙÉ°„%©1›m‡ÚÝèJTŽ"¥a"{îŸw
+J7¢k&í㈘#¹ (
+)X:Rç  [ 5d²2jMŽعÝçu³A’ K2´ËÇÑ>ÊM^üÑ1-ä!ŠLMM-hûY´E[´E3fffÌ·‚dåòÅK++*I§ XÄ c®ÓÉ8œ^—íÀžŠø@­¬Å@[Eñôö±±ôs1ã[»ªAvÑׯm½ôš02À…'‡'á·>éܱÕWÊå6]=6vB†ÀM&A­ w0ÚJ|.bHG y¢tkM’QqŸ$Ÿ’Qœé¾#ý03ÐPTLµ"­%%žîŽ®/ò/+"i*‡,ãɽ;7Q¤!÷|ÿ±… 6Ø^IÑ~@…"aô“óCysï~ÃH.´<c$sDÎüÀrc°`.i› æâ¤wÌþ1wöOcÀj :¦“Ö¹çýx¾1ôMcdÉdâé™Bú}ó’õúYú—µK_n!ëwçì~>gmá²rÖêsÙVðå°ŒŸ ýn¶˜õq.š£X†d(Šò±•”Ãç¶q<åó3¼Ûž[è±ï¨þnW´ê?ß÷ÁßÛ¾LB™^GE© øDÖë%í¸¤Ö‰¨²2 >dã½lnMÇ&7tlæ*ð ü¢ :M (Jì«»A=ÛšÉCñ1Q8©kYªL6T”-åùå^—Ÿ·Ú„»1Uíi9^EÛy/Ï\¾¼Q–»îuˆJSbäË?Mºˆò2
+¥añWš[ü^žpuQžBÊYìb§µ„cw½àPŸz/’béT7œ¾žiJ}Ù¨©!]¯ƒûÛ¶®öç_=[ýßÿ„20ˆ€AIka€1Ô$|Êê1Ui•¥À- aH6pAâLgÿáš]U+h?ï¶ù7e/" )/-{ãÊëMŸŸ›žž^è&´h‹¶h‹öÀæ©û“—.\,+)åYŽv‘†ó10œ2Å¿oGy2yED¥f7ÍÓB½EHÕC?ÌfzÓ©€®·Âç‡nª’‰"²Ô6–‰.86<!¾^˺,^{I Ÿ÷ñ§Í_Ü=®à€*ƒF´–@øuÁ
+L¡ÛÑ<Ex<$³®²ìqíôî`õ*‚cH’5âvcÈn$]³‰‚Gû€m>áxèFÒfI"g6±t¶ÿ)“U®Ù§ã¹ÓùSqÛì°{*é4’£¿Àè·qÂHšW?ÜO|k2NŒØf®Q÷†I%Îÿã§Ö÷^u¼Õ²$ü"õÒVûÆRK™ÏØ
+܆-˜7¿§§çA›PB %ô°)Fc±{uïe,l}›ˆŒÅ&ÂÑqk GÞï⼂<'YÊNá¸ÀpE9hûÚÂe¿{ÍðM7ÌYü†G­æ¬x=x¸ŸÒT0ø9R<Ó«ÈnMm[Lë2]nו.È,U­Š»}fVÂ' ›Û!ÅÉ««ûd½B3}`òW>Û8ðvᵿU©FU X!©•—.ù~~ìé榛ž~2“°¹(!ƒÈ¨”ÓʨbèÝ<agYrV.Ùµ{Õk¯ü ; ÐÁQ=onðk%§~½•O8ζjqŽa¶jZ¥iT#e÷ê¥.{:Î;ÉË—‹U³ ÚÓP3l‡¦U[1ªþÏŸÒ<ë×ä34ï †¡"ýx¤/9ÚŸíKû¿ªÈ…é¡séWÛz÷°ûKù­K¨Å³¸8«¬`"¼H;X‚cáEŒfvÏáJq1‡aV,:½ OŸØrùj‰„‚AÀE·¡Ö4ÊJ#R}£rky5;X©+¥8-(·iJ}@o44B•Öç¬7#åyEêÖQð¶Å0šÞšmC×½‡¸f¹’ Æc¹t>ŸF<µˆ|çb%`€µ,šÇSd:o_{u üÍk»y<;“s°¾e㬦Úå»hžL†)`åÒ,hIµ`‹ßh¹p©¸t{^]IþÐÀ$hO‡YÀh€c kCõiÈk°á‹·"W«¨>ÞáÞ÷sgwýø™9fIA`)šÄYÆš?
+òç¿ÿ‡÷bààqË
+v˜ºE#ñ‡ùÑ£"÷ljû,€
+( Yª1tëé¹V•‚jM©Õä&ëw¬ˆo‡¬Wýõ£ÁNi¤À ß
+™ç÷wÝí÷Ë•íùï|X6"u›šè`#ðUÕˆtÔ¤ÖP AWjâg
+á ž¦*À! †“F!þÀqâ>K‘Ý%àÞ@#°˜4së+òªJeÐð†ÌVÉ_ fj/¨JÓ]³Y‚ j1B ºÙ:2Ü~Kß««%þ›žsýëyœÒë çèBÝ">3Ö;?_Xµú±Òª•¢ÃÉRvùö‰“*Ñ _^vGd1tú ÝEkŠ¨>•ï¤’ilšÓž—Ëq2òüòèZΞΉS
+—qšÑ¡ ]ªZ!ùÛvm-à‰4žMùÍëŪڂP9<‹Š€¬¬Ä4õÚ‡•Fîž]õ&MôMõM‹õN‰õ%ÅN?26 Žö¥ß¹0õ«‹I×O¥ ¢÷•sÅË쮬Ù"›ÉS"Gòµ"ÍepœH‘âXó †Ó+K–ç´u|ÿDÏÚ/‡J^­ëCóMë#µA’öêtÕ˜g¨:
+ª˜DYÓhŒ_¯Ø‹ ƒM7¤s´MBuŠZ«µ’T zªÊç9V$¹š²| ̺!£Ü5{ªC¤6é·=¥B3ꎿ±J0šV/Î Žú†GjOö<+ßÉâf”lÎRõvÍlQZ‡¯¬‚†á "¹Ä0ܦY-A‡
+AËÛ€Á t±K ”!Í­Í**7‚ÕÀiËçÑ©Â\WêÑW7izù-cÏ?òd1.ËÎaí~¹J5ªv|ñ÷
+ÉhüëHÛÒ•MRœ=¸oƒ"a‡MÏbgqWŽ(fqÉ|Š#‹ä¤mSѶmv^ÿÒ{ퟓÁ\pP¶ÃGÖIZ§ÜwàÀ“™ìL:Í !ûRûª€¹Ç4kâÍ×AÃj
+˜5+Œ¦‡ÏÛîö§ `cýöÑ>ìÎ
+ B‚€MrÁN‚ã$vb'\Ê¥D¡jÖJh[Ñb—¦±K©`2ÊÊÚŽ4”*øøÜãÛ‰÷8l|cûÐh(^½>::~ßó>ÏÿùýãWâ} ¢W2¢Ÿë¢=úȧÚHל{çæþÑ“ÙV¢ÐÅM¸™À̸D1#Aä“d!‰™Œ4£cÛ߶ùõ³Åׯü$8T;
+ìÍ{Uá϶Š¼[U\ªÚÀóv.Y.EðÂœçë õ'QPj…D¾…îÏó­"Ð;|XΑ,Ñ+ÛåЉƒÖ˜Ór}ÿuÈ™Ãì¾sŸìAIk6ª]ýFl‘C^ø—½{^#I-™<ŽU!©†Ý[Wä$“QxصA‘<÷¼‡éÖW6Vuˆê»¶ªr£"ÖKR½ $áòAà\ð$,
+pL×Þ¸:¬¾Çq5p"ÀŠ¡`£ÀÂ1Õ½¬4÷Í›òÏùçMRcÝ©ÑîôXŸ&Ú«û2£¾¬©nl*@Ä»²]Ú„/-á[è¶Ä:3's£W燿Р^¦|ÇÑÓ;çü¸È²„Dr³À?%0% M1ƒ”Ñ€ZˆùEù¶}Ë.þ¦üÁ· »BB3Ç7‘ÊŠ
+’„ç û¾o$¿!
+#8ì
+`໣þ|Y‹k™*{SEñàÒ"€“BŸ~þÜöqáø;6
+µPD‰è=å¢Ød‚è>ÜÖ²®¨ÐbÐëf"õo½æÿÃ.‰;Êsõ’T u
+ïF2ynþ Í\Ð(A°A¥Cþól»¢8†ƒÿb¿Lc£8Ï8þ¡_z…#±×{Ì={Û`b Á€c§H7¦@iÚð®]Û»Þûë JÔ#­Ši5‚’¶!mlhTµ'‘‘"5r"nïîÌ;ï»6¸^ïîô\¥Í‡HHMb¥Ú¿Fï®F3óÎó~7{ß<ðÃ:§Ó¬'ô:3a$M¸…fõº’®PX‘dFòómqTÐWAùlnÎ1~}¢|ÉR'XD"IéÍ †U­Æ¶s°Ê­Zvƒí)>¤p~hQ$m(#߃rXÃÈlàý„FP¡Ù=™ŽÌ;Nü%ð¡Kì=ÿ—º‰E¡i‚žÕ‘cµ,n ôvš0T®1>û‹Çv7,#0Kì¤îƒ÷‘{7‹\`ßVѦÅ$VzêD"„׆Ÿ`X#cp>ºÁÆqÁk×[œc‰4šê+[Ü›7<TŽÈ„1Zì~
+*h”Ïÿ§ýgggçVT9m%—ŸÎ«“9u*«ÞΩè×é·Ï_xdý†4&Š&X3mÇ4ƒÛ̘}G]™ ÷AÅ@ ŠQóŽw;=ã)1&ñ½’Ð'ŠÝœàR;/¶Ê²Kà$¨ù?ŸŽ¼wÑýîØA‘÷
+R ‰ë´Hé指<U÷*'Aê­xƒé÷îÝ<v©>=Õ‹.~îOû˜ƒƒvÒ%ì[oíJríPŠV¯XŠà„5ÒgÏÉv?Þ¦A ½ ©©
+aÛÕ+VÌ@ÓKHË=IphJh¤1Œ™Bo‘ Î’¤©¤ÄF±ë×~-~í§ŠÒŸ.to~ØôÒÀ÷ÚôÆë5LÉh:„îl‡Oc ñ3³íçö>¿"4òEW~„B “úÖÌ™Eñß“oc=;t–}!1ÇHR£± â£h=f4¯®r¶‡«Î½]Ï)~4÷Ó 4ïAHŸ‚a †y„÷©˜,vÁdgš°‡"F™hx=²Ô* nžëæœö\½üùñ­›j‡:èÿX^VNጙԟ<±¦Zâ|ì£ ÿó/=îö¬s:X
+s0$C“:
+7.u.I^@°Ÿãš¥´ˆ> X!MÄ8¾S‘b¨;’7]iÉ/䤀÷‹ C;¡I–‚"ß“QIð)Ð/
+m€w¡G’=¢âùørÃÑÃÕËœVʨ#Œ¸™t PCÓÅ©»ð>Ê;¹lg¦o«ùÙ;õoKúĬ2™ÌÜI.—ûÒ]³ ‚
+úœ5‡êõøÜb>:µ¿vÈfÔܬVieÆÛêahC‘„´2f°˜Æî-µ°4î$ñ"3®;yr[œo†0†fÞyÇŒ»¬É´
+.À”e xà“å^.Ù¼Tæ»RððØ»»jª¨2¦|ïÎj9u .Ô§'£\Ò“žÜlù£ñ®­[œ ¶
+miKh”&iIƒ öúàHÛD"QcA$T*Øžó͵kp}nkG(òO@­ö§§Ñ›·z£Ùy¿ß÷÷ùö7½óöŽÃ¯­Å¶ßhEú^¤F%¥AVÃñD­ x
+Ò`¿,Ö!æ5ÊbÀ
+e9ªh¡¾¾j&׆c&7…ñ( ‹¼zî©ÕsiÖÍ>qüiàŠžXé_þº­ïó  ªv òµHöWÀU|"ç5Tp !$…§º
+Àƒð¦|¨†Ÿ`V€-p…½i¹;c²Ë2Ö•=ÒeíÎë1u[F:¬#'­£=90™8••ìúþäß¾§Ÿ{èüQü÷ÄšùÎù´ÍIP9…ÛY;CÚ왬#ƒµYs‰ì «\oþf w-|S ª’ Ñ8jF|H‘ÂCÆ.8ež«P‡#
+<¤h
+ËÝûòèõqX!k¾¿XI™uѳÙsúìVkV†“!×þh./UÃ'zdù\ÆnwáDo÷vI‚©üÓÅí»Wœîª¹Ñ_£ë¤D8T/è5 µÉå†æ›²œíN­ˆ**3Ôvè>ž ×Åã)ü
+^òŠ\ôžcÆMj=B<Þ
+:c ()!AÝ%£Ð'ÿ(Ϸر솦˜l†p‡ük>ú("ÊPbgÛž•n{¶ƒÌóEÖó‚¡j°¢²,¯|„¡h•ñ‡£Åºéçò'nr;Íóú>-UÄ]*òVV¬tÓ3ž-^Ü+•Ä:ÀøªŠÚ rûñ  AH}dE­På‘÷@‹¨PQ‚<ºV7¼†â×Q
+läš‚«,Õã =‹_:ø»R•
+à¤Ãn#4]€ãøâ%…‘ðü?ñý’êååRI-WÕZQ¬sW”:M¯’ä
+ž«…´Ñ”FM éÜž h-œ\ ·Hô© oGgíæâ%N‚ryt6Oá--‹P"zèWKí³`Yû»Õ”ÇØ7?¹èåƒÏ¸ÀtäàûÛ7 Ò‹‡^Úâ°>øøÚÊjq“¹NbÖÉS/ÈJ³Š€½ÿºÖâ)/ª­-ºr9l$D¥R4Nr3ä¿,Ö&t¿&FU¡M•[- ¡úÿ²_®±Mgß—µl…8ñýØçb;‰sáZBéDPÔ()·B–{'Ží8@R ]™Jµ“¦iÕTi% ¹@-—uÓ¦ublZF !¾œsÞ÷=çØ&!±CΞ$Ûć!õ‚nò£GÖ9ÇÖñkŸ÷ùÿL:Ïõì_ó\ŽÒÙ-nœ¦ »ƒÍÛ´qóåËONN>(\SSSê\€züš™ªT¥êÑÖƒÉâ?C-Ëòᶣ cq:²Ad,:M®ãéÎŽÍcAW0è‹Å:g=ÕÇ•€ö(Ä'Iî+ŸÔä°:ÚlÎd³¨ mOï)ê{â˜ñ%;*v‚6ÊÑýr¼öÓßW4¹V/sjVƒÓ±PÄ!ܸ¾Ð¹i]Ñê«‘æ(êÌû¤qïÈIñ~ßi5Ú,æü|ÛçcÕ²|„<#·]Krò8Ö˜Å>ûsólߺŠ2é]>üõÆЭ@\982R 6+è‡DôÎý™GÒ 
+`’àÞ“PG¿)Kà85|¸föó²
+,F{ÙöÒÑP3°ÁíÏ›Âb кÞnæ²èÅu•ÅA²O’[Á/
+mϯ´qf=Ke®}6³ëÈúÃÍ‹–jÌ8˲ݻ‹CÄ=.{@ÿò‹ '^<;¸Ì:&·¡j&©—xÀƒ=vC`„`‹×AªÅ¤Šð'ñ yâRmœÑQr‹Rƒ@\Xr¹‘ˆ“€…
+ðGrô¨Œo¬-Š}J¬R åDj‚-’à#7Xy<Ú$Djä…&áùDÂ]
+üŠp› Ë@Õ—ó|‹€=íÇaß_¯·pz#KSËó
+¶½ô³“Û.œ{“@ÚýW/VÙè¯[,΢%º;¡n,y@¼çÚ•šå¹iv*ËB166—Òfç²ùœaaÙËֿݘǀ'O\³ÿWh$yÖ”<«WÏ›`m÷ú¾5=¤IôkîÓÜÒ'µ÷ú3¦uS‰^:¤W{ÒžØ:´‰¾oÞúFrø™Ûfü¼U·c>;Ý@[uVZKs‹5“£ó``lúeßé9óŠt`Ù'`/ .
+y±àV”&Yò»~ÀF¥v,´‰<lN·$Õ ¼Kä"<0ŠìŒvíÙ®eMEÿèÝ]c±†c»ö®t°,Ë,>v´óíwq *]»’eÌ“.×a§(&Ÿ5e´Ô¬Šâ!ì¿5ÖÔP»¼bgÉë{#á @èˆè ó9æ
+?xØþ‰ã#ˆo”åÆ0ïçÒ‚¥Ï0E›ò2´Ùh²1Yí‡Úc±ØtMM€šÍÌ©Ü\ÍêÛÌL2…)©JÕW²æg3‘H¨Éü'gaåwŸ^Z]\”É:YŠ³˜5ÚB›4åûŠ¯þÖÅ“€ @œñƒ8<Ô€„V
+?Ö@V0˜Ekê¹A“*ÂAøX͈ ó­WÿÞöóãÏ55¬ÚñBe ©¦ï\}!lµEõÂb‘ †­a€¤ðºZ»›$X¼â•_Õðݯ܈ÚÊi‡ %¬V^E±nÂê&™Õµ–+W[Di». Ö; ‰NÒ¶ñ‰'Üö…¯¼´‰KPî„RúÞ¡[±sÉMW®5J {4Õ¡¨1M ³¿·ÿ×ë…F2ý &’ ¦úʧû-¹>ëtÂœ9[:ÓkšH”d’d’ 'ó'’ó—¾0oâ½’9£‘ _™€«[fΘ ¤ÔgÊ÷•æÎ.0þ„€sU§»Ø¬([lC„w°(]ÉÞ:BàÖ勱hóºkkII¡a£G"†‡ì!Š^Mi/À9ߦÉá‚+Ũ
+
+\G:âùFhv]é¹1Üø›—·ìܺ¾¡þ›ßZ[½„ð¸qìHO¬…Æ8ï­ÛÍ5•8ƒVã6¢»ëQM pÒ¯oµÆ@ÓSnÔAY±Hx ¼ø®Ä„T› SøTÙŸÖƒc#³Òˆì‡ÝæÄ'Aò)ZœK~ëÍUnÔã,#ívá&1;‰‘ñh×ɱÜôÌÝêLNNÂÏìý +ª¨¢þ+5>>^°íLúwfjúÒûƒ«×¬!0ÖåXŒÙP7MV0¦úíz5zG?S‹ö†¾p@Q#¢º{¶ôà¸fô(3ôàÀù]”Íîf*H´Â…›.^òòÀ§>´ÉBTÚ <¬©Ç‹­@Ù ÷ cöÆ?}Ç~ºÆKh’r î"-_úã©]š“ä=p])uäóÏüöåçDàç%¯btœxu+j¡Ú±þ17/úôóàÊj+e_•,Vå²±JÓøb'ý­«EiŸ*Eß¿ôà8CÑ8ZRA „Ú±Ù-Iñ1a·
+P-ð~ †¡ý¡au­s,¸òQãÛ§¿7:¥Eßÿ5vu;^~âÔ‹@n0R^mìˆÀwlù†“tÓ˜së •7n{= è;q/Úá£Λփ×"óÁY…vUmãS͆½«wÜܧɭ²ãÛÞ~Ó·ÂC¸‰r5a&ÊE-¥2ŽÜt¹\nN㶨¢Šú‚2‚†ýðƒË›6~·£8B²4M#8‹{^\sc¨hí‚Ü ÊUñêjð‘B{RZ¾U•Ï–@içR~YlOžó]½bqŒ& ‘°¤Ýúú©-é;‡p
+j¥·¨æèLµ˜3çþù¯©r›ýµ¨J&‚U²ìƸóã\kuØÊKiþòù‘8Uk;sÆm³˜i«•3¯_ Êr¼Õß­xºÒ´ºjY±ÙAþ-sϯe1„ÄF„‰—‡[âq¿«GÇ\DÌÉ,’ÝkER+B
+H‹§t;¤ÁvEŠÌ8ÌÒÈ[ª×˜ìÉKögO dÝíyr¢Ï”<U2Ú]45LÆŒðDöÄ_9µ;o²?Weë'¿:Së$˜ŠõhAªË˜:IëÝŒÞW ÷Î×»²ôè=673%zm‰&Ù“ŸìÊJÌÑ£Yz,ãêÑüжÜ*;Ç›‹
+­<Ã1&›^È銒ü½o¼týÓ6¨†¤“zÒÈñù Ê8@ÈD‹ï„€ v“ç2öÉàMŒöB¹  fI|½=ðœ2Ù¨
+Öúd}Mé­ë.âJÖ<UÎ2žËnÚõ2 9»NÔ¬\^^H³TSÎÛmÔã®^íDʧ»?D"ÏKÃ~ úÙ«(
+D'ù2#;eèÇR+Ä^¬x Ô!\§* q¥™Ð‹½3Ž³4òÀ¦wçé„4¢ ÑùãýÙ‰û(ÝíûÒƘµÏXªøŒõ+ÙÆuê Äcl¢?c¦Ö9ÙKMôäLöÍÓc™ÉXîD4çN,óîéãì^ë½^ËT_V²N2:ï^Ö½¨!Õo˜ì&͘Š´hÆõcÔ/½ìó 4m⬠m¦m»*æ
+ ÏTRÎk®\®†}šJì†A—"ûä"ň*·ÊØ¡ËÎtA„a¤9%)Œ5g‹ûeÖD| Wý½*xÞ9¼ÙΔ2Æ%K+¸KŸ¼ŠÐöÏ6”Y9–²óœ¥7ÑùŒÝ¼Äf¶.­üÊ•k­ÓÒˆDT‚,£˵»n¡)~e$H2Z“Ãd"v‚xàÂ¥ÈÆïTØÒS0Äa L²uK5p¦Ew6fãÿ;R„;’©¤žœ"þï–"¿‰¾ã•‹J(£¥¤¨ˆ1Ï·1yœÑqhÿrñv$®v#²ì&DA_¸z½ÂØ%I ªê…À­àðÈPË»Xc13Å«ÕH-_XqábkÈ˘BœéïÏ•~(óøÂ…Dþy£Éf1óT±Õh9ûa#üw°;­cRHSÚE±^F¬„öw½÷j•Ë1–ŠÅÙ7o´I# ß^÷B9Ox³måÓÌmißç¶F‚«vm«úÉž¯ÿýo„‘f¢Òd…i”"x&yþÕ\a‘äB³‚|’è&EJÁi÷ 6CÑ¥ÉA{d ù.E  VÁ ´!è#H© €†Û°ÔGQC
+
+›ù` DîO¿?EöQƒæø_² èƒsf«þËöûHẊÇ#Ç•XÍ1E¬ÉaÉeÌoóÚ‹—vB¥V%§?Ô4}Hò!”¾öÊ¿Ø/ÓØ8Î2ŽÓ¨*mêâØÞkvfvŽ½ç N[Ú4JHcˆ”*¨$ê['ÙË»¶×Ç®85Ò
+ø€€H­ÚÉï$JDQDÚ¡”&Ž½;3ï\{ر½^Ïìê,Å(UäG¯ÖcíîìÌ;Ïóÿÿþ>À{ 5+è
+sWWìüÿºŽÛÁ‹h‹ÙIØ]67e¦×¬X?ÐÛ33­+`~F›žÊAÓC™–Ó´› ”333ðšËåtÕœ-¼µP µP·­r“¹Â_}è²Y5¯å`áŸÃ‡:¬ "Œ‹&p£ÑM¬Ø¶uõ¥ßÖ
+Ý-42sÎ49tïd¿15À¾b×á÷T2;w_û%“;Ehý‹òçš<U1~BÃôä“.Iôá³ÃDî$ÛN»ñR–¹ÇE­.'(·}¹Ëìb1ÃqËzÊ­»<wj¿^¢õ™¦N[¦ú°üY³ÖS~Ç1ã×Ä)³2Dœúù .œ¦­.wºð•« 3ô=våý½œ¿6É*»Ui?Œ3Œ¶N¼RI»“ ŸÌwC7ÂÈZ«qrâÐØHƒ*8?´ÝåþœÕP¾n…õÆ8òÅóßr1VŒ$–»*ß½\—Nïçï£ß–M•,æaˆ’ß¼ó<Bí‚à“•½ˆ‹@—
+B
+ƒ59™Õ&
+ 72r­ººÇX—ÝC`/ërbL¬éɯ×ób4­v
+s{Ü&S¹Áíaâѧ¿²Áü½î'ÿñI„ý)µ±`Ä5Eùû/ø³G‘FIˆ€8ÂÒéڗ΄æÚ7€%pE®ç’~¸rQ éû,‡Æ®5gÕNÈ›pB>éŸÈF¹d"ëâ¯Óc2ªÊmÉ„Ž.K%€ÝC… €–Ȥ[Ò©Vn, Há»…F´ã­×¨ –M™†ŽZ «Ù€ÙI»{ Û03dÖN,Êô/™ZüÃpEp[Å+~ËO:¾ ¬Ð†–¼ í.¯ÉF-%1·ÅûˆãÁúøkóÁo—W¯}€ÄËM e­dW•–ô¡R%¹SKf{,Ú€eö¬U;CÞq̸Õý¹p¯vÖ;cš4]:f¬ÙTº32$UnsylË–[H·á¬ÿà/¾ `
+,VæL¢º7Á‡o$Ɐ[ ÷»X£biISæO<òçÿ¨ÍæŠ2ž™†¬öišššÒþÍ' µP u[
+r
+èÅÅ:­–ÝNK°~ËßþîGR T11¶€Š¤ÄýR¢£hôŸF‘âú¬iðIâ›d¡UÛ%ÔªÊ1À…æÚ7Øü‚€‡ Ï". í"ƒo§[‘P›NùHI1n´!£tÀ[ .·™MÅD¾1%¶È¨ °Nʼn5¢Ô,ð×Ï41Z@R<íÝA#'Í3=x®÷óÚÀ⛿ªø’›ö`´ »÷”Ì —i'èÉÛõÓ¦ÍK)Æ`]ÎPÏ>õvº”;NV­dJmF‹yK[vXGÏØÔÓl¯-?l,9µ¬´–U2Äj¬×ß²O÷›´^˜çæà}Ù¾%“}ØÇŒ[\j/–ë7j§ËµFíliv¸ìÜÍ{ŸºÏcÃör–4ºq·—ªÄ1«Ív(ðå÷¯ø8®IQc²ýW¥&kz耆`
+"rÑ<o*tßÿ*˜uX‘Á¾#ªZ/ó W¯îYSéaY–¡<¸ÁüÚѲ_î±MÝW¯ö(‰ã÷}Ø÷úm
+•ÿðÊûU›ªHÔmC=VÌl3kÚÔo¼±îæ@s†>‚%pé[ 9)†Ü ˜š"ŠÀTÁ… 3} f(?|
+øQd’I2™‰™˜‰ãJWWÇòe‹ŒZ½Ãä´â¸Å4kÝ:Õ¥‹5,Óê sCÂïã3!°3ªiÌÊQеéª,7ñ\#
+ëÀWž÷JQJŠ ?½#Pð`Æò–Xø©¢°Év/jýt÷’›Ó©PÒ¶ÃÏ8Ãl'œ´=öò +·V¿T½ÆE˜MZ“«°â¥§:j²r83Ð(3A†òeX\~IÜ/¥ë`©AùÐP5²lCqqðw €Ä&Õ¿˜!üø°iDäŽptlb_ßðØíTƒ$µÑ™ÃSÍ„YÞÏK ²}¥ßºÅVNòww~óô©ÚŸ½µæ£k±om_bÒ«ì¸ÉB<þÂV×{
+`0A ì„/ƒ;ä Ù>l|¦ê%ˆ-©Ã?sc îÉgWj¦<71˜a÷1|}&ëüCMÞ…»·§ÞÞfZZ¿d×ÙŸYµ|í*7€™_h't´ò?ì—yl×Çù£…Äø¶÷šÝ9÷d‰ ††Fµ¤„•‰[Âјû°½>v½öúÌa‡¤QÕHi‰¦)…¤QÖ^Û» ˜ @›ÚµU’ª¼;÷ÌîÚ^ïåéÏXBüCU$*RÅ?½½}3š÷æßßçkÖÛÊíª«{x¾†\‡ºVZaÏèÑ㿬ºkŽî{åõ*+VJèÐm;çÿßÐÈ 6>˜“Ô¦}9ѳEr_–ÉÅPœÐ/ø}[Þ¯å› ,˜IkÀ¬¿9«³•÷´×Lz ³Q±ñmbp¶âUÁP“þ‚‰~,ÔÄý³Rý†¤·$s&;1 ‚Òùé^$Ý_̽“ûáKßüô$öÀ1ã¿_¡â×g‚è¨/wâlÎ乂ñÞ|¥_?ÈûµI?šì×)~UÔ›wþebÓª<‚À0=F &
+±Ø°³Y±Lóê«Ï‚˜@]
+Ž’>À ÇNyia¹Vl ¹™é’¹=¡3nè¬[ÿt ¡SY͈ÞPèp¬¾ú×êWÿ§ n«!Û¨[€Œ{¾}Mh8ÿñÖeK0£áI\§rÖ- · ,0³[Œºo
+Ž»n9¹ú¥Ã@/"ç–ÙvvE`²óïŸܽí R¯1áfDƒIEØ»»»§ D¹Kfb&fâ?G&“™>5ÓWø«d”É4üRéÔX&5¡L*>ºö©uEḞÂ)RGو—~º–ã=Räž½í}+läMNÞ!Ë~Xa5`t(‰ÏV<NKYnäFªGé£Ó!KÍÂîõý"ë”ņÙ.µ¢pø䉪•Ë(«!»Ñ@Ñ‚%eY5ó?¹âŠI]L¨î¾Ñç¼…mÎi“ƒŸ®K\ƒÈ·3|3ã˜ä”Ã.!ä¹ãÀ ´›‹º†ù]‚äæ­d¶™Ž´]þKý¯Wô¾·IŒîi¥#!®y h¤I曦 1Ö¼åGK­¸ õµ_Wòò^ßÇÝè¾ÖqòÍŠŽ|ß±g-Ïa¹=’TfV ”Uo½x¦¥núE¸ÎJ¢Vª^þ-üØÑÊÖ–•‹­ˆÓ-Æ›Êè‘‘õ<õÄ<œ4áÆ9¿=í9pxEOwEÍ®ÕV€j  ;XD@ øp:<µF²äÔ3•}¾b4’ h2ýZ¨¤)þ¢¨O»åñ>Ϫ׷í4W~O¥ÑÛ)J½wYöøY­4 Uús>趕’$aÀ­˜Êµ«Tä*ÐÞgN{s”>MæÝÅ?[ñ©’§1åL^Ü—£ å*Þ¹Ÿ·./U¡”×él¾˜487kƒ/ZÆü:åœN>¥NžÕƃ9J^t g|È8æ¥ô(ܤIúôŠW­ d)Á¹™ÞüŒW¥ +~õ¤ì‰]Tz‹•Ò÷2¨I h½^Tï^• Ê`°áN`*“5iMÖ–›% úPM4¶ýF´ö:ÓƒÁ5ƒßù:QläÚ…–ï`¸v‰?-àM. íbén!RÍÉÛ8ÁõÝ5vðl~¦œùWÓ¨ÐÄòm-ík¦vjÜðô#p¸D®† íçE'͹i¦‰ã[¦Ìç¹ó°l»(vÝU8ø
+KT?ëYÁó5шçŸÃÎÇ«MØ‚…ó´,×€TõÜc&Ò`$¬•ë²â¡ãb„® ßIêLz4ßLå…éÆá/ÝeÖB·™Í8n'Õ0¤…&ʤÁKHÂBæ}þÙöp¨þ†5‰e€7' ÊWF>uÒ—?Ñ›•,Rºx@ÕUU„ º¥Æù6Þb$U-úèeL *âè™üÄÀ\³Š4 ¤Éˆ®[¡W¿‘ñj&üÄè`VâýÙé!D~M5©÷–}y“~ûhÿ%ˆ|ú¾ÈT¤§03aGŠ œ$uº¼2²°§ªXôÍR.¨'ßÕ¦úDŸ~<¨Šl aé^$Ò—“<«̘ÈKùŠ’]"€ûñX?2áÏOŸ›£x‹”`nâqVâ >1P0xx²7ûAÑH|©Ó‡Ô«ËŠôÅV Z‚¨…Àô„ÐuÇ–åŸ_icFÜ´tˆ‹ì
+§€¥³k$©!tÓ%ˆ-pð®ç…ý?@ 9 eAçÿù²+>ÖÀÑ»i©¶¼Üf1™Q•ú•_m‡1É-ómaÚÅ
+.ð/t¸ D`Ú¡ It3ìNQÞyWaà±pØà Ï Õ.)/0èŠÁYIˆýÑÒE—.„$ãJ:J+Ü)¹Ó(ò@d&fâ+Ód~›C€I ÇS“ñ̈(‰øä‰7NPi¢HD«1"ó0µê¹Jûµ/;Ã\ËÔñŒ[âð¼Ò´ÈuN%©ýh×:R VHK¢ù«–—_û¢Q–ê8z¯Ìy ÍñÒ=ӂĵ‚EŠJ­ÐËÍÐ>ÈãÒ¨#,ìäÄZ†¯ä)Òf”!uB÷‘F¦dšIn™­ÈFú7ûeÕyÅqR^g½ÛÜeVÛIí¸.q!´D,®)$Â)
+vJXLíñ:cÇã lêB)Mãä¡ $BŠšÒQlÏÂŒMÝ6Ô*iHÚF5Ò3¾û;6^Æ3sûM,Q©RPá£óp¿—ï~÷ÜïüÏïä±?r«Yæ;AÂl‡$¾róöO>û·#L7Ý‘ŽÈ\ƒ,µ½öÓ-&ÜŠiáu¥…wŒg;dÉIGê£R}Tè£i;Ç´8ÙÝh¯4lFBQz¶™ë±õŒÔûó7jpTC¦R[îµk€+Ât/
+/3c¶UV†ë`Ä—ì§ ª‡OþhGº>¢“æ÷GDGåÆg¬…¨ý¾mQ¡³r}±‘0PHźò¦Ú­'7ÿ°¿ìÂù>ÿA_pÇëµ
+\ ÏÕ‹B½ÀÛE¾…‰|¹‹¡Ûc‚GÝ‚ädxÇÕž];)¥%t…$JQ8Lð[gN[—Tæ’Ñ%Õ½kýî*ðƒÒÿGñ(¶øDOC»’\ˆ+¢8ßéö`XžÉ¤Ça„}­!{ÜÏÒ¬‹—ìrÈB{L²pÏSþ~eTnc‹wŠ‚ƒæ]¿1PR˜kÅ)!)ˆØ&ÆìR´¨
+ÏôHÑú{ÝŸcì’àHc`’höß7 Ò’'jæ™VŽvÐS-"ï¥.Žw߯ïøä„
+J‹4]îrNvÎÔïÜeºjCM­ög8¡+p©Ö
+*•Ü
+'SÿUZ ±‰Dbé9ÿ¿ÿQ<Š‡/îvÇÝŽ˜UÒ(Ÿº™Ú´q3h% ´8ª³†b«í¹—ºO’Zx¾‘™j„VYpò‘ŽE#<o—¥.žØÐ83ã 3 gμ`A! ^JÀpy±éÏÔ²BîQ¾÷ ³s¯à€oêàÀþ¬s‰ €j¹yG蔦œ眑;cÑv†i`ùfIn»4âCù‹—¦óî€æé&I8IÓ¯{§æãöÎL{®\©«¬P< ô&\W}Ûø¯IÇíãÄÁâ"ƒ!L¨ùÃOœSl›È7Éb“Äx¢´ œ6B7L϶ñRçæ5_-$a›"0Êj0¿qúE–à™.fÊèëo÷ôVC1åú‡_9µÛ‚¨Ð¼ûë<ßÌrþÐn¥1’úÍß´L~Ú-KÝ ÓýÞ_œª/$-D>~ãs; ”®zºXMB¿Sñt[{å7Êá'Ì6½T³:Í{üR‘àK%±ciù°ÑHb,;1¢O´ó>½Ò¦|z)˜ßû]Õ5YLj¬jèt“-"âÁ¬…ЊÅQXñ믚&ÖI-n ÚB ©©D?>›%\‚å‰ìOß1Ù®ùe»öN0_ ¬TÎgÌO¨‚¯ªZ·Pßß’µw Q³jkÎ8tUGY‚C&̸w=*…P€"R ¿ÔœCl–³mmæÛE‡6æîÞðXWúÏsËcA]ú>$5œ›òeÌû3ãA]rCË®¾»¼1äm„ÑÒ¸ƒ\ð¡ŠF”@&À¤?ž¢JJŒÀ±ßÂ’ñl}¼ÏÁñ<“Ù€ÁŠh¬$ù¦Õãáp/Ã7È1»,&i‰ .ÒÊOuK¼kFîš
+·¥ï-Ý,Š­ß>9éúð¯{€_
+hAn¹‡ýÞ¾­ÑÐ=wÔ•yˆ…ð~çѪ¤¡nœiùÝ™†:@¦üQ#P^…õòn×ÎG¤‹—û‹ÄU\øõ¸>þüÐÈÜðrgˆ½:ìÊ •8cÄ»¥ÎiR¼Û•ß?Bÿá'Ä•Ñ
+g€t†n¿6\æ ¸ŽÙ9FL3z³âNªŒ¡ê$Zd8 )o’ùúš²jr…HT­‘]o¿¢à1¨ÎÒ[ûéZ†áHžhë—9n¥°Ž“Y"¡.û9Ïg„…‘²¹¡ü…›•´\ÅKüZY
+ˆ.J(Y¡þÜÏ–-ŽWGVC}Ï%œ±ò«cËòÃ_uFn›qn¯¬¦Zæ9—8üªÛX}³hd¦4äºv´<|ÅÂ)h+¯œ(uŽ“™qwÿÓ•`,*+Ë9á6ÖU༢‹Ž…¾uîB»‘nVQKÚê™ú¨m&·l„-Üd[­€NÛ€8Ì@SÑ™Ìˇ~x¿‡(@í ;1šÈÎt8RÜž±útµÙ6ÛõÉ„möšV¯jvš™î”™™îЧnèb²øCgl èf-µgÚî1µ„ž
+!+4qºíÁ{««…€H*´‹“%OMÿìÙ³E±ŸŸ/Àád)–b)œO2I.—;vì˜È34éÆÇ ‚ûögŸúÚ‡çûTci<£„a„0«SÑ´Õ6äfш÷BE6Ô֌٠Å1“ ©ZldðQZåUxÊEúxn|<œJ% ô\u}Úïëz“m‡jÆ8Ì¿@j,mîµq§ev
+g”\Z™[ymTpŽß¾xtUþ=×µÑÒÜ
+Å t¹â&îf_ ç^p¯»CÉ¿ãrNËïÿ²ü7ÂþÇåC ÊÄklv‚uNÐóÃä#ë2!Étí¦Z2=Râ Ý4™?åÎ.wN,sW.ž(›)_'® Ò—Fä€cŒO¥T Ð0Û€(–Uy×2Duåꟾþ˜ªöNÀí0R] &
+éZ#ÐèÒÇÊÐ —hR‹[è…ÜtH›Ül®+ä';ÔÿàüÌkS© GÆM#j¢Ö“–¶Ó w8ðÍÚÀ­»¾½æ×o6\¾Ô7“MÞ(SS½étLGÏBnÏd{Õ©f°<ڛɄU½õ?ç÷ï~rƒ_ %º\â 'C9r¤ˆ"Eë·K±×cnnúÙÙYèóù|GG˲"çöp^UåeÜ^Üáf3³ÛFýꀛkè
+LMê£aì5íxc°žc+*=U`Eë7WYÓaÛjNëýŸúûFHí´Œ}Xï㓵@¸Út3jZíØŽ!³Í¶£ªZ0eØÜóY­ J3 È¿?xæ_ÿ|JM}K
+H‰Íæ´±°DCáÓÏ{IÞC{7n(׬8,œµ‰’p¾š|÷tƒD×),'Ò´Ïãil¼W76nÊàØe=±sÇ},Ô Ño\¿Î+Š|M§åù‡ÿr¶Ù°Û€št”(0êÇz(m…M³Í0“ªš€½µôöœ™(´ÿA²ÏÌ º¯­ZY¾xª<÷[Þ/q‰üØ-¹¡ç$›?q«óÞòÜ`yþ¤++›9IÏ,uÞ)sN)ÓÇ¿‚Ž¯îJJDŸ—¢ª‰
+…go•ÀpûÿË~™ÆFqŸa<R¥¤€ñ¹çܳ3³k¯±±ÃaZ % ¤„p" `Œmì=¼Þõ1ÅPTµ¨GÚBD’Ò$4‰ @};C"ÔãSøÐÐBS ìîÜ3»>Y{½}T¤VJ”I\E~õj5+͵ÿý?Ïû{¶¢CgÌ{ÓZ‚Y­Ü7¿¶ŸèDR]Èh·íÊI¢,o‡ç3xч'³RçŒ#« KXœÂiš% ¿h6Æ.G:g d¾VÅ_Ê92-¤±â¸‰¥1›i¾-û¹oqÚ)úæÛ³€s³í$ZµÅ6Ù÷cºh$Ù;+Ñozêǽ¦©žXÞDo¶gËÃ…4eÆXu>ýraq¬C,YÀa…,bÜþ|Á¥‹»uµU‘]÷Ƶ (¬I“›t­ 6¼ª4ñZ3¨Fv)R£;t¦«¢bC™(T”ëâ£miQ\ºxHk$õAlÿý>Ž€Çç;pdåcÙüT“ô}‚äŽÅ[
+!Å£(õ²ÚvðÀª|ÒDY;ÉA’¤ßïO$à·“““©ÿƒ35S_ûšššúŸãûBHþ§''’#㛟¥r.…Ù)”Y0ßòú[´xH’$fߧN‡ÿ·¾~«¡¦&aÇQ„¥˜'¶ó‚Gib]²Ø8ó‰B8•®†>cê}Q 
+E@šXµ¤üØŽdãVÖFPïœÝsífý+'6jz ÜU—‚Áæ…$a!HŽDØcÇž
+ó.EóÅ4·¨x«ªÂ… ¾èåW+$%˜vã/y=§‹F¾¨î>L¾T“ÛúlFÇ ¹§‚Ök¿%=xª'{¬ ÿ9'ËÌÙì BæÄÀìÔ…Üá.¬jsvÎj]š§F»ÌþMeËalƒþmMžcÆߥâ—sÇÏgOõd½t˜²™,6›6Û °¥ˆ9Ÿ¡ðyˆmÕrãQß|'A‘” År:òîd$ Óµ©.k¬;35˜™x×0ö^ÆÝþ€“+Çi§1×I¿ŒN¶'©Æïe›™"ŠÀ$‹ÁQ–.&©ü€omøïî°êç%ØÞ¨PÕ]­@:ž&ƴ褴äHÒ-zt%êÓcµªÐ&EܺZ 'hª(EŒy"òÎë t¯ùWkÊ}œD g13øÁNAóiÎ €c@ƒ "óíŠT-ñµ1H( k@}Ù l·JK2¼/ò‘Ü ˆ·ÞÚ¼¸xŽÅ)Ã1
+|iGÅV5*ó‚ ¤ôÔ={~€%÷=y"™œ†Q1S3õUÕ}&O&“÷i>’)ÀðíÛŸ¬]½CP†´Ñ˜âÛò¥æÁËõúÈA¹"ï ðO;f|Îäæ£?]Ã"ÂJ²6s©ÓpûV» Ô€íĵM
+òa·À7…`Ú~4¢Èn!ìÕäfQ¬®‹Fk%Å+©µbØ«H ÜLi»qõÈ徊úø“ÝÏmrPFÉcPÌN௼º×ª$9€csh+WVÌÜ Õ]
+‰‘¶Ñ¡ý¿Gö­[[€Y11Õí^F[Hà'mqGùµº”±j¸¹(x ÊéŠOÉükÀÃϾ³‹Á,€@…¤Ãnvày½•’P”z•¯eÏÊeóX)¢Éhd_LJQ?¸q„oþÝëÛi+K[
+ª¶•Iòt¡‘ÏìTWFª3+ÕkL ˜&{²¡S½æT§5ñnôJöÁJs‰%ÃN>B
+Š¨âåKòæa
+LX~ÏËp_ÖPçÜå ÂÙ³_¦±QœgO•¤ƒ½ØÞõsîìåõ…†’BÓDˆ„¦4QhA@CT°½>Öës½>
+¼V¸’Q_ì ®U@M’Ô¨*™ƒªâ OÕó|;RÝ=›ûq’´âe/Äñζ-ÿ¸xuÇW$UNâ“]<jŽ mD¼O–›@>Üd»"À  Õƒç€I¢ ðC³ã•Åö ±(ͼX=ÉÖHÒ‰Û·{÷ìüšËš"²YI¤TZ|ó£ÚBr!•NÍÆ!±Xì3Ó^¢b±Ëñ°#‘H,î,É¿c¶ïü+é p£“¦í˜ÝŽçÔÕl„BZ‹EðÛ¡Þq¬¡:éáW™ÿU"Ö#Ë £¡C”Ah“ÍF`¯æŒðCB‡¡(K’W®ó¡Ïg* 
+Çd¡‰Í`Œ’èŸV[£\#ËqåXp]¡Õ~yàU`?Yj“ÔÆ0ò|ø[ÿ76®-$‹,ú<+Uðô×K ³“¡²~tz‡Ä&NQ éày¯ªxáæús%t”„Ñöíç‹ïO½›VYOZifÛ ø­»T«ˆÇ$îˆ*ùá›>°ë^ð!±îëÛç©X»{§ëxÏŽß_ZkGB¸«,µŽâsÚÍFìzqƒõONÔÅã~èY®ehø»Üà$
+ö|§˜e;%¡a™FþK†Èä€)9¨™^•[•¸š]­]$µÁ/¥ qÀ1t?í!/öê~wÆTd²ØL_Áî‰ó9éë9·ÎåD)¦wŒ»ç5‹4ªWÌ^ÉÖúOšŸ.³ÑF§bž)Ê™¾j×ú³µ¾Çµ±¼¹~¼Ì•k¦I†6Ò&óÍ÷̳Á,íýÕsKö⣘6˜ìÏK]a—EýøöR M:ìÜnq¬Öicf­Ï0Êž¦´‘'’kF0-¤¿óìp«™i“V»ÝZæ`¨r}öôNùa1 b•Äõ(rKFà‚„êX\É@ˆó
+¬˜à)Õ¼|ì©' ¶¶À˜]ßüLmív»™d
+Èm›7ºÒE;·®³òÜ›r´>ÌÆ‚žðýS¬£ò[¥†
+ÇI•´A{{¢gÏÇšïZ¥Hj¬¾?l|çÝ­vš±“em-[¥Iºê pg‚p˜cýZˇ×|¤Eà‘E±.CJ\fÐH¤FQ|â´'錉Æ;3®g…¶îömÍÏ–•ä’¸“a˜“?ü&+t©±ŽryÎîºõ—@mXï¢ö¾\
+MF‹Å—¸ÜPå {w­ÿëx'Ô!¡B@ HðˆR•¢za‘ƒâ@øˆ÷©r³Äw\cFe²çÞTûú2ÊImØþô滚óüq¼j÷°CÚi‡NÍýÎñí¼è⪃CUEÌJ¿÷¥¿}ì“Õ*Àž)¶’EUª:òCN+ÄWƒaf&Ò¢H0‡)Ö#H’âW@þ\àìÙ×ÊDYJÂå (› ÿþÛ'$L-:3D2™\Âz±Ëñðb‘F—z<‡m[[…:ßF%ÔºµSßù]‚è‡2Ä…ëcJ·Èû¹*Ž"tE¦2-À’cÆLn²IŽñèÐZŠ­¸ª=IB?r¡o¯¤tñR¥(7©J{x²R}2:üЧ„¼àEÿ=pîÜÁŠCîàðØL/Ë5IÈ;8´Ÿ°4YøÒ‹åj¬‹‹ÔI¨YàŽ*Ò!0Õ0ûÖ©Sû]ÜbÄá)í ÈûÖÝùô¨ 5!T#^ž­‰É'àï·¶¹("Á‹BÃû%T §D阧â)s8Èõ6 µ©|Õûž{pÿQ¬ÏTv®>*·!¶A–ªÐbÑ#¼ü=N:
+
+ÑXQãé“[Î?6îŸÎ— ™{ø˜o¢0y#k‰Ë
+žkKÊ\¬Mä}ñÄž¸ä”SÍ¿»æ²(…Ô.·#4^j³’A®|
+ûÕo@úý"ãøý2wäÆu'ôIi³R†{ãÞ©„3)A×픤ï8Ö=SOµ\qñÂ7>¼uHº%É ®MÚþ`\ôÊ|wBn›HôFﺓr7´k)µü  ZŠéløƒ_^Uu¤ýµ?x£1ØU›$¸D®Ì&$ /¶q¼‡çûÀ
+€L&?dY^»v-‚ E™Q;X×~uéß>èâ.Д„Ø+p­‚ÔÅÉ-¬Ô e(²GRWl|_BtÏ9f|ÎÁó-‰„;:Þ'&ýÀõè2ÜŒYLkÖß=ùµßšœè¿ë‡çy‡Ì·ÝõtÞx¯}ÇÎå„'ч¬„Åb,u6=û¶ûDÿ:%€—.^Øf Z\<& ̸3Îwˆ¬šØ?ÙTE™ªÿȦ—_Ùi b¤3ZªèÊS‡¿”ä:‚pâÕÓël”Õ.ß¹ëA<&G]ðÉ$®0'|Y)¾ÛÉÇ`Ù$É!ðnh˜²¼Ÿcb´Oâàb@‘6Yèà™>à
+fü$ 't`H‰î;¬#1ÙuN
+M)vŸí¹Žmù.`˜S” 6뙧‘Ï ‘AL if‡‹§GŠ&#åSam:¤É «§ÃÈÌ€J*WÂ¥™ðâtdIáâ€VÐ)C¥ùÊpMzpa.
+µ ˜yÕjåSµ„¡ÚFUüñý—
+¹ZÔ†’¦ iZ
+2)†õ±Þõ®½»¾¹rH”DEý£¤¶j€’(ð ¡J‚H  §GDkŠwgÞ1ÇÚØØ^ïô­·¡U%\É¢‘üÓO£7£™7¿'½÷ý}¾&áÚ™k£rìŽÂìÂ7Ëçø;aJôdœÊïž3ÓõŒuZ&ÏÌKvdO´²)h<“1Ú¹h¤gÁx+otÏmÖs&›‹¦Ý. µgSætçOÏIv|Éø(óÜ×·‘Ã.
+Öâ(ä¶Ü‚¾·êʯ¢z¢R¹oÍă`Ôp€rUõAŽFCX­+M@ÞùÚ+k\Ü"å|ñ¹|€Zû_}ùYžæxÎÌÓ;êÖ\¿æÝ¿÷Y7#0æ"ËúÕ›Ïëj–½HöÚ‰Ê~0ä‘a=¡¸V­É;5POTH;ö¨Z…Š# ÚäjM¯èkÚ¸®P4°VJEʬztYoï9#acÿf’Qï)mO$fe6¾Adòs&¹d?§Hd¬÷Ó +–}ƒ5qù6ÎicY³¬úfÿµ×±²K>M Gª†õˆ®xgº›¤ $ÝÍñÀ‰‚wE£5Z< Ñv]«"dcCºG/g1¤4 XµŽ«A¬Š0
+Ê’¢BVšâ뿾û'?\)0Ï2¢•õn_!£²8ÞM<¡I
+M·þ4Š&!™®?Í'D⨲ƒ 䤪¸Ž<Ÿº¦þù¯5¥¥ùù“H»ÝÎ¥¹¦/»D·g¿|Å/É„[0(ÃJ(ÖÒd¨¹oÀ·¼ˆ¬ËÉ›®^ۢă@œ;ï_¾tQ]ï´1Öbwæ™®ÆHÄ}ìÈ
+rÖ,+Å°6ój7×÷ŽcºóOtå%;7ßãÆßÏ‚g¬ûÊíE,k¥sM– —ËÁÑB±ÛqühéM}‡ B­D‘›t%Œä€Š~T«šG{ƒ1y³Œ*dÐxìhÉšGî¿t9 º÷ÞÝ PŒCäxKAÀÿht°Ê58Þ¼þé•NçÅ- c7jã¸.®4È €”&òVʆ´0’¼ F¼IÂ
+Y)Ñ”: u HÑÀÍ¡ü‘;ôª¯je¶æY\.›ÅÌ,/^ö—?]0ãÆd’pÈøø8‘qb*Éulbb–Ffã iù¯›âêIãâÅ ùE™l\hlôÜÆÈSýabT=ˆ”jE°!MÈ?én2µ„(Ò@r›FdA¸ª~¢
+ )R=µJ¼æÄÉ-N>‹¥­‚ÕµÔî<Ùþ jTDjðÈQßtëO£ˆ,ùÿ!SK˜ZN
+K¬&Z„q…¼DÜ öaewLòkjƒYkC‘ßþnݲÅC:»K´°Ù®‡‹-û~öm 5Ä`#À ì±J$íþàãŽÉa™¼59‡â‘ë¨x%äíý4ôýuÌÚ'ø7ömüäb-
+À †w\ë 9úðCoq~ŽÀðÖþÜú%’´+®‰4]½^²„ç,Ù.‹û—o”`rÆQ¹Šý1PŠµ°ŠÂE@l«†ªãjHÓ2ôm+†!¢ºVCÔ)zÃG$hx8D(&GZšYì´´S`6ÞQoëè:h
+Ž±°”Ïs¯ÿN>P|„Tè‹£ªÿa äÛ4ŠyÁŸó•$…ˆàèaªmz ÄGÂM‡ÞZwè­'
+LÐÈèD»N´ƒm)$Ž?Ûy”„G71±R¥ ¶•jª*¶µƒÛIJ¶0`BbbÚTXÙD_ßÇï¾ì‡ãäîKhš[þ)?Y¿{}u~¿û8çûùâåfóâ'vÎjëNÒ ét¡„Üò1´_"9y×ÈáǽyîÖ•…=€’èNÐþŸ¿ûåVjm4V9– Át°ÈÅqnEòÑ §$´Ü/?˺d±q÷ ¶ð€|×®¹ª*JʇÅhǵÆ¿Ú"*‘ ¦yß\÷Ÿ{D÷P°$G#¼A¢e|°tJ:„øƒ}ý¯mÙ@X‰Â#· r#¨½s—Ð|üØV¬„°ùG¾¸}ãWˆb¼Ì\F`†Ê*ìø»/ t …NÞ|}-¦Ã[~ü“o_ipŒZœ"v°Œ‡]–¡`Áf9›õe ]7ªøã~äØ0O±@#ÿCãÆñ®âɸi2FÍD 5®éÊSût‰¨5;ðl&ž¯ödÏ.9_0z®p,fš÷ýœ[4-˜ê×EKfbÆ鮢lü™ìù§¯´9pJ¯ÕáV+®«:¾T=™asÍ?-ÎÆóÔóK³ç‹S±‚ѨVëÔØ—&cÚ‹'Wn|®3—™) T½ ·˜qâàç9¾““êDÅ•+ !è).ˆ>Ž¥ä&Eô¦Äæáá}¬¹0ؽ{UÿÀwy¾MägÒ¡ƒë¬D¹Ãhzÿ½×8!4’hpÿèðKGìøÅÉoüëÆÛ£Jk2¹7-uÒIÿà ×ë_ÞR}ýj`4ÝÆ£:è<ÛÄóNàíDœ ±ÈÿÁ¯ßx®ÜdÐhl6›Q_m%©d3Sê]ƒ©.0ÉÂxLFE2™ üÎ~À3êûï¢,$‰i,z+Ä
+GÁ•«^Nr*Š_LvŒÊô°GD>E ‚Ê#Þ~gÃAPrrEr!3¡:
+&?ÑNDujËÆžžúc!ŠSßÞhpà¤ÙT¢Å­55^pçã|5jœk~9fÊöXÔØ3™è™^íÄ'¦l¬P=«U»ó¦zñÏ>®pÖËÌÜb
+Á»Ñ @òå¿84Dœ½Hè¦;‚Þç)­emuEÍŽêêåË-X…é_ÙA1ô±‘aДÀùï—\‘‚pG²Ø|•Nûé„kbô âY¦ó…uz³^O%fÌ
+ob¹vÍ™F`ç9Ép<7Ø?#u\þ³3öQ-“ð¤S>QÜN¡õY)ì•í_–•ÃÉa`‰zo¡Qû‹›«-&« 3}zËI³õ²Üùá{ªËµ”ECY1;aøÚJÍG¿Ývår’ÀI5Ë|D[`¸È±~AhäÑ> Jx_ˆ?LG8&(IÞYÃ…¼jFÌÃx$häÁ‘ÍÄ5“gó¦c‹³½EÝÆLœ˜ê3ÌÄó&Ϫ½¤ÓLGó'ºò&{ò§úÍ÷~Ô¸>+žè)ÌDµjT;ÓûÅôÈÓ-v NáºüÒRërì 7N¨˜¦û—Á5sÍ?Ñg?•é*P»qµ§P°'Ó«¹ÓÌœ—Œö/úݱ꯮(%õEåD®§,&rÝ*ÃéÓo&èVQ<¸íIË~E𳉈"5ÕÉ°M²âFœK÷K‚?ò ýéûUUz[A Û7Û“(4̆wÖME8ø‹³b`t ¥frÝjÝ?†}ŸÞ
+nÛTYNØl¸áÙØ_¯F¸dŠt¶õñaY
+"”ó€Ð@üˆ ¤•&†o¸0¨\¾ÌJ-+%­¸ÁdÐéÃÍ¡Éñ `’©ÉÌ£–—…±0þ¯1==Ífa2~g,
+ã& P¤ÔjÓëë_ Ý‚Ð!ra‘ Êb€ç<
+¨?Ó(ò!8)ð!¨ &é’ÄÀC”œŽƒˆƒ”Ã!HybÄ}è/­]£Û¾uÕºªRQ0ti?«Ô_¸àÆ0#‰Ù)SÉoÖqWmƒP¼÷K.ñ>`‘ïä™6:¹w쎗i’ ä‘÷÷úI¬Äl±`fÁõ¹îAÂþ¹n~Ö@Á*w!$w °ÿ›7¾çªÛPIêìÅúßœÚÅÑÐjZY©ºfÀ^uu‚{Kâ#’àaQð³/ÌF¼º¬œSE©ŽcBHܼÕþÍ/ï²ýòT=Mw¦F Ú'òí2ßœ¢›’ìnY
+ƒ½J§ZYÖ sÄEàÅqBƒ ù‘èçG}À*ðÏÍ™²—X ‘ñdÜ0ÇáæS½EÙî%ÿf¿Ìc£¸î8.¥R0ì®÷œÙ™½llB¨Ý6д
+*„£ ©ÒŠ£|ïmïÚ† ©UT•$jSª$U)Rx¯Ù¥¤’пš¤RªV@0ž{fg×Ø»žñNcWT¥TT!þ#<ý4;3ûæ½7oÞû~??-U§¦MÚyídÝd²^Ë€MãZÖ©&Mw}Hg5g­äiIƒš»ïÚï¼O6×8p/îñmΗ5‰–S ÀHçï¸}-õ€FZ•¼µ’©×FLZÊ &“uj®ú-§ÍS#µZÞô÷“Ë·?æ³á„×çtX„ÝGX£}+ÿv%<^ŠC‚&³{en˜ÿ&ÓM qALpL_‘Ù'Ñ!‰`nxíÕî–‡ûÆך¯^ë¤øþ£GÖãV¬ ý
+/z¶«ýÐѧ6m]†¢vóó‰­›–ûí­>´·›ß~/ S, €äB
+ ¸)²0WÈLºxNÏ%9Ðja@ä"ð,%wmÝ´GÝ~ܸ¤q>ó§Urn¾–®-§­
+cƒú¹E¹…sŽ+ŸsTÓæé¤MÉ×S6ííãã‹ÝˆËîÃ=j÷lø²ëÆ™:-S3}UÈz5ýÀgÕo9[«’uĪe-JºV=ï”R­§"÷9\Ë}¨Óo1/Æ;,øºÕí.Ä(¹ØV>ÍÅ _à})Štç-¾ƒ¥öEp¼^áò{¶=~ÿ{vé›Bî[âµ45Ùfä¥ckıçKÌÄìêÚýH‹»±ÕÛâ!q‡Í ¯ß*Ãwª'Ö6»õ„Ó‹!Íæ GwW•²6¥éX2“{Î*?ü¨Õª2==wŽt¯|q ,<àEÑ—å¿h¹ªÇTe:¸p»×íÁÂí4®û¶Pd\~az
+p,’
+iTóú‘4•S Ó9K%m„PÎÛ&sæ
+i.§MŠ$õÕÔ-3ú÷†‰TýäˆIË"‰ü¨#óµÔýS¤CÉ!*DÖ(¢d¡AÃœãÁçJzž–uOå(díÔYÓo5,s€Å:œNG“™8qÀ&Ãç Í0ÿ•L­šþÌú­u@#ÊY‹–2”“óÊd]õb3{ÚŸÕµ® AlY Á0ÇÃˬ¯¿²M˜o©[Âv
+‘›×ŠÂa‘ST°±$$ú…±k‡eñyø÷‹]~Œp˜›—>ˆR"Ô<ÄÑ]27|ér¯ÇÞìEq¯Ëì±û‡â«xi€¾S=ùëGñ ë|„Ýírº0—… ˆp ¬©·@çEÔ™2ë³@2‡–t¯|aËô¿ d†á|ZQuQ”X$èoÂæÁFÍøú5ΫW‡Kò~`†ý!l(Ýmùn±¹Ûî{
+6¸(
+sF#3 q ‘X@²(ÏDd1.³d~¾
+ô¬Ù±½éémKvl_¾~õƒ7ËÒ%70Åp `=ó¢ ‚€1vI5T½,à7ÖÛ·\ëfkš›€yI!¡#`®zb1¢%§m©@nú®gç:eÇêúÃß#N¶Ê§¼Â¯Û§ÝF_}‹`¦j­Á…Vd^1”Ñd˜,FèÉ'àÄtœ*щùcñšñ„c¼Úg
+˜±„m,±°ü¾Ã¢­ ÎRôëãá¯L%”Γ…89³†ìSçíÅáE€C¥A[%â°ˆ9ǃ{œÅp5ÄÏÕY±¯UÎÓ¾'i‡M’EÌSìÁ ‚~¡¾wûáÊšÒ€}:Aß­q ±
+ ¼ÔqxeZëN¦ö™¦7gWÓ±¢ÿúÓì
+¤GƒG÷7¿ýÆÞ.õiFŸªyu­5ov™/Á¸#©CsE#šê53~Mm‡Ïs¦?j5v íV/d&2Ò½À*@&zÚ›3|¦æ«öÕê»2³ÇÇFSÁô­Î?ðýÁþ?=µêåÐÆgŸùæ¦MË/Ʋ[Fˆa(§„(…#]ŒÃÍØ=ðù±Ä³R@<GË@ˆöà8
+C 5† A–X§‚áb Ü¢(
+‹œ¼àÆ|Íó Æ‚ª—â8Zà!I“H’„8hFäZAÜQ–I’cQ“»‘G0
+xêXõ0Þ¼Vì~¶öÌAÇÛÇ苯
+—ßb~¢0P;ÿ†á­0kE™™°Ó
+Û­›®µ¢¶R¼n*f+ÚÁU+CDeÈQŠ-,DæY1[%V=UˆÕ¢¶©¨}l1êœs<¸Çi%„ɳ¼±Y ûÐ/¨fš–ÅfJdšQÍŸ^·[ ›5T[ŒØ*ñ3Qn"Vw·Æ- /,'ˆJ”.÷Û¬x5¼°WŠÔÀ©ò@½#_-iÀõ2K ÀÆ5Ü??lM'C¹ÌKóÀÍ‘Cæ¸_U[uÍÛÜÌ@9BšÍÌ|ïÃËH‘G%¼úÚæÑtovüô¥««ÖÈ’4Ÿµ‹Û7®øäÓP2µw|ì9ú¹zõyzbjÇUmïµëGžÚâvq.©A”(–%ŸØ²5ŸÍMŠ³L2ëåryn-é~üÆÌLuV*·qxƪL—sfvÛÖ'dFöˆŠ"<g?ÒöÈ•k¡Œ0u/ð@FèZ ˆ¡B€¾iú¾|w;îcŒåN@Ý12ê×3]ãZHOÕsÞ?mÝ´BÂD=%òøò'í¹Ü+°/ 휴È`7/ýä•-ZÆ—Ìu«°=3>#w4©>¯ôÝé|&3/ë©v#Ófdú~ýæ6¨_ÀæìY¶ØqéÊ‹iÛËvçŒ=Õ£©]šyð‹ÕÞêíûßCÝØõTJ
+fj²-g´Ñ>5Ù•RO
+eESjÁ!–IÔJ1Yr¿«›®„U ÖT•,•Ç“ŪÔÇÊbÒ!E=¦h•’LêÁþ¡“>w¶_o}ä¥X²bT?£é'^?³ *¸—
+xq48°KÓ#ñTêr¸ Y=ùAW^»×ñÄ>¯IM”‹1˜Š°¬Ö¾˜Çq É!–#˜ÊÐúÛ±°e¶dµ!¿fêu¢zä+²ä
+h^îdù€YMÄÊGG+’R0)UÿäÝm+Š‡òÊ,ÍlŠÚhh)yJ–¾ä‡rDVCº‚äfèÖ¹pAI ÂñBiD—^•âa]­ƒÛOŠ•Š^cŽEbÉRY«LHšyú‹XÍÕk~ún~CdcagCîj?ÏðˆbŠtí<탄ÉðF‚ßç¢HVp;0Ìíñ Žó»0?‰hÆO#ž Áò08B$ÃSre­âí/¬wïü>¿íiÇö§ms¿ÙOþ¸Ðsî\Ss~Ÿ§¹ÐóúNú\‘·iÝø2ÑTÀ6íÉ~k¯p®(»iÝîhßI´ív¿±ËÌcŠÖã»×/?‰^xÖ·n . ÅˆYæõRœ€†<át²$Ãfi.YCq .ÁK<ÎzÆI†"IÒ%ëu"ãIFÀ¨Ç¶5×y|‡£á(~µùŸü¦öÃØÔÀò™(‚=öôà#³Îö.Îô¬ÈôdÍõfÏöÙÒý¶çÁŽ±îGf®e¥œ—¸ Gnzíã˜ý‰ǧ¿&ç†ùÌe€-Õ³hªK,™îÍúºþ¦:õ-Ýë²@ÒƒƒIÒÝDf(Ûǜ͢#Óíœé]vëJàÀ‹4O;@Ë4–ãs{6ûÂùíñx•t»jL‚´YchÐÄE ‘‚Ò­…¯„á›1£!–]¼XðÔh5Ïx0×JÁÎÖXíóÿîˆ>Zl­n­BNV¨bͽæE:¥‰gÄxÄ0‹?ÿwiyÙ3>Q.àD“Ôæ¼M
+߈íßïê6"5ɉZ]©4ÍJY-—”ÈgŸÕýñO¥Wz^ÿkµ"ŸM$+uóLkë󽔧r=ÈñÛË»”Ø›ŠTš«
+w粎¥{väÞ¼6Œ2M
+J Iò1E< ¹×ñhZ}<Ydjg!E$’å½}E~Þ‰pŒ¥|<½¸÷Ê ˜(5RÅZMÇ¥»jíNórçã|Äc µbÜàèx1pëgïåoÈõþêRá­›ÑXbþ‚å¦Q ÔŠ(R< 8Ñ:Ö*FL˜“ &ß÷çrW
+Ñ{Ÿ
+7P‘7v³]û\W*=C!âBlaÕ¨ Gy#ÊNtp°o¥¦£h¬•‡ãñVb2JNu£œ“mÔlo6Ó£-\*ꛎ®ou¥šSY“-ž‰‹œÑAêQb¤ƒm'Ç¢Tªƒ€‹¤:½c~#úm±yÕ?Ý®$>Ñ—Jk)âÎîáƒ[éýé—¾Ë}œts<>ìBË# >ËóX€wù¼´
+ŸôªÃ\j›X6Û·(Ý»xº?{r[p<àÈ /ïvÝèv< h„ܼ7'›}»byæjÖèûÄì3ÓÏe³f‡ìS½ËAq_Ûÿ‘é>€dªÏ²Ð4ø¤Ï 1ÑoÏ <šéYPI]¶ÏôËøÐ×Zºníb±%òÑ$íàÁ57?…–ªÚ7C¯ŽÇNŽŽÔÈRp!‰‰/‡E½òï7JûºËjËw¸áüJ/›õó÷ãM£JKºÒ(Éwí2î–O¬vR†¤ÑÕ
+M+‘äÆ“ÇÖ<ø|¶‡÷3ë¶Ä*07ó"·…Ûæ¬ÐUmû¶üù—ÓêOiÿñÒ ©Q“ÊÆäS´V¢×ªjPOŒTÉò)]ŠÉ ˆ©7Üïê&'ÊL½\ƒ%ñúΖü kp¯‹„†ÓO1[r>¹^¬jûE©þGÖ²w3öÇ|Î &oÖG«?þËñ®·7'¥j]­ƒ9¡$*M-¢+aU?"ßûxDµL3BIñ¡œUÕSI±¡øhDûx½g— &ªMµÂÔª%%(«õ_‘=þ?þw¿Z)$+C:mJâírðRB¬I(g ŸfæII,ð$âu’~R›
+¬b_›ã?°– mrŸÅûË#î«Ù5Ñj;1þT0;)3J¦ºÐX;˜…ƒfgºÕ1%ç¢ôt >u>ãíÞé.ïX?Nu` ’ډ) G›k¼Ó–jõμ³"uÞ?iÅ`?Ù‰ÆÛi8Ñ‚M”MŸw¤[°¹vbÎjs¥Ú°T»k¢Ÿi³Í¶ÛgÚ1Cª ¥¢nãëô\¯ã®•S—~èzëöx·%×#а¾¼, áÅÒÄ—SaÑËx–`BŠæóÈ2 šá?g;·×Ù׈ߺäšþ ;3`›°÷/<pü‡ýrmë®âøÄhmÓ$Žkû>|Ÿ¶»M›>Ö¡i´ˆ­eÝPˆµKÓ4ï8NœWÛt-‹Šš8q›†6T!þA š6qÜôb€* „@¬¬LCKãû¾×o_¿.Ç iZ&Z¡]Ýë\ÿrü»÷|¿ŸS¼Ì7ˆ;jwÑ•ðúìݶ5yc“ñóÊâM“6‡'C&ýúã™+˜1g½_ÿ7ýÎ
+„Mz¸,{½<½°Q—C¦gÑ¢9zܘ7é![ôš=¶‹Ÿ{çÒÞ=µ6Öæ¡X
+§­$IØï~÷7ßV?Ï7+êII>ÉsŠÔ]èžEáÏ÷‰‰—¡qº÷9Èr’`FÎ×G¤^%®/©—:Zk•û¬zƒnK­0³‘®˜æ…s^mi|
+:ßÅ8« €DDð‚\.÷°=éQü?Ga%3ÙìêÉ¿#k¹B,«¯?‚  ËÒ A0¨³ßKÜûè x7øZ,ÖGEêSÄ5»à¾¹˜pVüŠÒ!G^•ù~EèÅfèÙeé•X¼•¿×ÍKÞãÇžwºÌ$âtØé’ŒS‚@wmõüþM1íÔÝ»m_¨³:´š`¿¸k‰;ÏÇ”xg„óqBçý+µ 4d¥Ô3¼Øù§÷Î<é®7©vW0HÍÞ>&È%„“ùö¸Øõ°¨`Í}æ{Kµ±ˆ­’Ô`©ª^QlçÄ“
+%ô(‹EøŽÙlÖ(ào (Ž;˜Íêp±ÞÎ/Šd¹ånIì„÷\¨æ½÷À]R’é~XUe©E;d±O”R M>wºoà º½Ããn?¾¯­iK•U;Ü,^÷òÁ]K‘ãñÄ©+³‡8é±ová/]zIT|à­Šx6ªݯ:aÆøÎR©RŸ: vpË烾öŠÛl5,òÄúΚÚ+-ûUîŽF þžÃ#öò<ânUî*ÃÀ~«ªÏ]‘½ eQõ´& Éü 9 G—OÅùÁ„è×x/¯z—–ºç~zäLÇ—ê÷î¤ìŒÙIWÔÚY±!&šbœìŠ¨!p'† Í2]Ûä+µUÏTL~ÓôKr÷5J.Ù:š¢RAü:Àsdztm—Ÿ´¥¦¬©i$ó&Ÿ"cAJ›À£A<1AÅÇJ$“¸èH\¤ãöøø¦Ä”)3NA‡>êŽ9¯ÃXµQ*6eIÌÑi2;EDß 3t:€òÓ,˜·Cf'˜ü¤+têã4
+4})Æ/ŠºEêÀ’x,¹ËkÉ]rÉí'+̓!-Ã/| †ƒåÇ™™ÿÿ÷­gºÕ\&9ùÎR÷ô™ƒOê
+r(Oxv7Ó‚Àð,ÂRþN6ıÝÞÖ/3žïÜÍ8øÀ¥£í qÉóûêñ½¶Ìš¼•i¯%û™°%_5ŒT¢Þ’ìÕw~Æ›‹{¶2wàcÚWž&(UŽ3…YœáŠqÁ”p+B6£¬#ñ¶ÌWF“ñLœ¨I­Æ4jÉûTà/*2[ ûíkÇÉbÜÈ‘—E5&Vföé2 cÇq3Š™ÆŒpÕ0oI|S%X“èB1dÌŠµ0d¦U>‘F6hd3jŠ0@8F/GQHhÄPÐHÁ‹
+uí—æ^>Ì~ˆÿJ€„×ËQ,dI‘¡G8ÂÏ `β,As$Iv‰èñGÐß±¾ºÏ^ÜÙL¸ìÒHàµy´¶ài®xê‰û>g–rXglÎ{í<i/·Üvü¸)곈9çj¾Õb¾ér–]…öeÌYq-mGðNŠˆ$z/Ï^ù¥Û^v[³øVyœ9ÌYn5æ¶ÙsèÕ˜ÝY]l)¾Mßêù› _p–èæåÅ?áÊT
+@Ð:øº™^]Û*äGa<•êɇÓÙ“W¯ûöNo÷3h×¾»^}åÈÕ¾˜ÎL•3ëk§Ôô©\j¤RÓóÏhÙÉ-õVJeFúNÞ¤ƒ„WàY€$:ѨÛÎÑ0¤^¯;7JÚ;íNûô ’Y«ÕªÑøèXÁ‘ëõ3<ä.ÐŒ¯-ÔוNÞ´~¸€"p
+]P(3âuη8Ó;›I œÒŒßˆ`uÀ•È+BÙoH@ju¦Y{šÓ£„ƒ„˜#a…8$AK1˜avÊ2Y”ðbË…ù \¹€Ty‰3£<
+­Gè’Â[3"DUáŒ(S•`É\-¼Ë
+{j2mÅ€šü0nFiXÚßÏ"s'Ý/=æyò~ßÝ,.´Ÿâü$#ú˜ Îú 0PàEFpžEâÀ—Ú&+çØü¤³Øî,ì¬Î·—i;á1íåWi±ÕH´Tç]ö,â̳·?n
+{µ—Zí•]Õ¹˜}n{s¥=óþ[](B3Ëuø:~zwþêÍý¡ÕÙ:uy[3ái,ºyŸó;Ì™ÝÕXòçÅ[=ç2m^þœ³ŒVßØ^Küü«÷ˆPòÑ´_¤¨ Žl?túàßÇ Ú; (
+6‘ª ¸¡jz,µ6©i’›MÀcpg“™î\~,•™ø`mü‘‡ƒpDò9ÊÅùðNÁ;9ѵ“׳/´1M ­¯uëzÿV:IukzšêÿåæÌ>ŽCiš 6í
+ˆàÛø
+Ø»;3;3;ë½mOŸj?ùC%#K͇Ñêç}÷}žÿï?ã:×ò§FóÓ—ð©+¸1ôŒ1’?y1F=éú÷Œ!aúÊøâÔE¶óçwßþšÃj-AÙ/°6Ž£í/¬°}ôÑNh@IÜ«ˆ]r¸æU®+#9ߧÂÔÒ›ÄH¥ªW¥6]m‰H•R¤6¢uÝþøèºõ‚tÒîâ­¯—-knÞ´¢¤ˆÅ­N
+=Öõ²(7Cµà%5¹99‘šgš1­F•Zàs!±±¡vm¡ªìEu5µ ÙlH=ŸÆÓ˜ÅH§ÓîXSC#† x)Êe#Ø7·»Â¡v]?ÓZÕp'p8\Q%R¯©‡
+Ï….Ù²‰ºw§ ´)8V+ɇr
+®MÃ59\—FµGLòdUR«/ür»‹±ÚÙ"†ä¾»m%/´£Ž—–Üþ{S8صv9jcì,Å®YëÜÒÙñÍRêàm6š®¯|Y6kZuH)«Šé‡¦ ~ÈœËËeÖêW•J)Ø ‡š¹V’ª¤p£nãŠIMáðþúºu%%´ÕIJ$¹öKÈxøí9ÇÇ LY4Z¯ª50¯ÔH³"•Æ(ë ç/¼ºwwñše,Ì|³³ÇÒB9|)bãp{!¶ÚjÚ÷êßËþ­Ùí·k>$áE ¿3yœËvóF€Iw[Ò^x#î±ä
+è—¶8¶¾"ܹÛ&†êcZsh¼\×J‘E‚7@ìÐÕš¸^ (¢*MO\%#»díHûÑõãÀpžaŠÌ{Þ^týF¹ª×ÈRͯ?(c10fšXÂ. ƒ¬€½lXçTõÖðXƒ¢¾%I]`7BRyX®P£M¡pCXl”¤fQl™½R߉Ê?PäªHdŸm}G 6Aƒß;d(-eH†Ç‰c]æ?Ëÿ°_¦±QœgÔ+€Ûx™wî™Ýµ×6ˆ„4"Ž€(mC”BEIÛ„ª!-Æ'>°M•#xÙÛFmÓªA=¢”ËØÆUùPE­RµR¥ë¹÷Þ½¦KÕ~©? ø«W£wf4ï;óÌó>ÿß_‘¢ú€¢t‚ƒ¿¹5zöýWûÛ…f§»–âjÀy4ð*Çq$M3WGÖÔ#vc-ÑûUúô#Ĩà©Îèä8G#ˆR¾ªì {júQb‚Œ…Èxʆx#Àe½Ì¢òg’~×Øs~»á³eýØ¢d˜!åEF€Y÷
+°K©|ÖçJ{ì…0ÿþ­uUõ"'Å:qF 9"I (¤B,NÝÛ÷<yë˜ÃÕÈ°D­yO’ßz
+Ѭ›¤Ý¯¯çÓ'†·,ã³'˜WŸd'Y†bXŽuð”ÀŒ@ÐNŠ&‰ÆuìòжêÛÎÔ$‘õ¡’1=ô½ÒH,„ Ń( ~" 3A:°dƒöEó ÇrA” Ú’ÁÊ„¿>ª…x&x6LA ²^!ç«[|ÄËf`ìç ¤Å€µäG…w¨›žÚ_¾mëØh{¦†$‘
+…»ƒ\.g˜¦Q0‹¹âÔgxœæé
+ž· ¼ñµ—+þöÇ‘DlLÓz4$DjhùA«aTÝ­€|‹°h+(».(‘1UQ¤®å­ë7G›œÇÐŒÂøÀä+’´v(IÑÝ?ÿõµ5¬Ýåà› Ÿj°=øüBd,í‘¥š²K–ÊË ôZ¿ªí x¾â1³»X–¬khüû'²:¢EßV¤( ªØfD×vÞ·¥¥6]íÖ”0V’´G–ÇÄ}’t0¥ êwzâD{AÞ-Ç©;éPbm²Þ-Š{¯ÎwwmßØTËÑC¢x’¤ ŒgP K:pK#ohnøá·-ß ¹2>+°DÚG¤ÆI3ì4}Ö´§:ÔbG­… ž
+ Ô¤3î«M„Í#öd˜ÉY)Lg}TÁKk~&ïç u.TG¹
+A¤’
+’ víø
+Ií]ÏìaC›H4TJJˆ±×k{–‡&UÒHU?’0öîìîì9{¯§‰J¥J|@¢¢RóêÑjv5ó¾3³3Ïï÷g­iaQÓ¯žgÓnf^¤S" “ç$3Ð_uU;’—ÌꈠzŒâL -4+˜(¤å‘Žl[äÜ^þóQ{3¬g×qeFX3ƒëö¬\tpi'œ^²ly%q¤†¤7¯1i"™…òP HO6ÕÀ?‡!讵TÈýPzX:J]8ØØö„E®š6Z™š®õBÄÓPr±9§1å2&D"%y7•“8ϼ›Q†ü>éa2G¹”“R%*%’ " •6ƒºdD¤è¢æ%6gÇs¢1ïAÁ…rÇðô0’õ§±è!Kž@ îÔFb"£JdÑjn¤
+c¨6 ª`ÔÆ ¥qÍËi>J×ß-)#šÏN,ÒÆÊwA³BÚhâk×™ÿùäÊâïø]ó£ÉѲ”17¾ä¶óx+çÏéfÞ]\C`„€›xnà'÷©“ØEÂkTÏPÚ¢ù+s©SmŒÉûË3pÈ¥ùØÂ(\/ZšÄá>§ué1}ÑG¤ÏT|mŠMžª,LVܱey©‚ï©3Hòyá¸móªz3mÂÎBÖr²a{ùóþLô`2´#,¿Œ´çöÞLXñè!%Òu#¸CÍô„äŸFƒYi¿x©åáFèçK˜—7PÓS­×‚}þس|9ÊÜ2žù퉶Àl7ĽHpDë€z1ª,´Ü¹¹ž[6òï<%w‚\¾òê÷[aá«­¦:ÔHñ<ý›÷Þ„”
+ ÷Ëœ p)
+
+N49‚âå_X®a?9L©#hÎmÍÛõ‰côÜ ·qyF‘M24KèÉXY q@}š@y?òŒ)íÁ5•p˜`*𜴇L»”ɉ<°>1dðlÓ38K1¼‰ÆÏ÷‘²„my©!õ¶ðùH´ÛlCÊH®N±ËŒKÝnK qQ{yZ¬ûñŒa(o~¢ÉZt2i‘P] ÚÓ ºŒ4/Ûº¶,ì©…³Í ;¤Àâ¹:†@¸«¾p2%;¢9±ð—tÓ ›”UÔçÅÊœ£ª4L¨N¼xT§Ú±’«² –k..3P–·Ãn:P‘ü ¡¾D¡(™T‘t" +I‘MIÌBy è˜„.(ÜÚHÒeʸ˜¬dÌ Víš×`¹7±¸›Kü•×,oï´lZÅÓ,båñ
+¾ž`q¯(E-@ÆæMúñ×É, sjiÂ_©ú…)}q¢:çÕe§‘ùÑÊâhUqL7ïC `£tæ~Í[u·ldþ´I›^\ò¯½¯ûÏ™ ¥g ¼Ô¼8ê´se¹–hSºŒ¯¢4iÆÛÍS­(ùª. ƒZiÁFáÈGoUýhzŒÖÎ/Í{‘è)‹<¦ù°‚š÷- W1Ihcdñƒ
+m²\›Z”ýº6]•Eµi>ë՗Ε秗&OÃn`,èÛÈø75¿)åýVé,–ò•]óÖo]k!\ pSËsÂúµ¦/.·&ƒT¥;Ùá^ä°ÜÉ%=0'ï!Q»!Ë\ŸiõŸmYÝh¦Œ¸‰àWÔ°.mW”]‘hÿõdËƧX[Éáú7?ë z +B{!¼f¢„öC,ºe#ÿá$²ÜõjßÊz—›Ø:ŽæxÿôÓµy­[ˆ·™LæqŠóó÷w_ÿõQ¼9`CUUí_Bò×Ï>z°~5„žgYŠ_Ñ€ž;Ûõ+JG8¼T$i¿ ʹ¶PhßÚÁ¶XdÁ…fg:ã‰þë³°èëÓ{Z›×Ÿ8ñÃ3{7ùŒî¿xñÙ¦úFj™º —Ÿ<µåÙ§75Ðì‡ï? íË=ÑxW@n“CðÎö…‚=ð oœ<×q¯lBG0ÐUº¡f ÿ Ï­bQ‚X†™·omš™{9…Ý:•Ènè ±ÈËwoi˜³ ––oÞXè9‘@·Ü}ö¥¤Ò›éøƒwWËsëÌ<Ca˜‰®¦iÇšX^ 83Bq˜Ð½ßÅÞÙÅ]ÔE]dv˜-¸˜¼HgìTÚÁgݵªD¥œdÂCÅ=TÌM©N¶$2šÎ#“Hn
+X”O0–qP©1&ãr.ËÝÒHÆE'\ÀUؼ슇[‘…˜GÒÜÆœ‡(¿J§1ædþ~¢ÉÑjûn=fåDh-´h¬VŠ“Ísä¶fÊÓAöNuiæÁübmjËÎÚ¹*mƨ-ò • ©Éùšì’)¿¨Ë.<t¯h¤x®º4¥+]¨<Ö¶‘!iŠnàEq{#–)?[­-¬Óf+2çˆü¢!‰¡¾Pýç«´Ynx_Cˆ4i~ÔR ©f4f)ejÍ{Çøæëž&zO/ÖD<”6U•Ÿ®ÍγsDnËÎVæÖzis5Ú¼.=]‘›[¯-V”fuÅ)ü®iäœþž›–rå>PZÒ]_”zvYy–æÐ:‰àë8Ë#›7,þnŸß?˜
+õ…‡Ëm-2¸ºµáÀu¸¼ÍÊ­Áp´óÄðVžÆÌ”ý›VâWö«êÞ¨r(¨t½ur‰ü†À‹‡_ü°-y^U÷)Á®pp |
+ o ÀaPQ~üßrH`ºh¤f¹téùFdL8FS$ÝÔÔð·¿~ @¢KðyGhîÛ}ûŸVZ5pòùümÌõë×›ëͲ1„ (·¾¡æÊïû`цƒ½‘ð
+[r!U6òö|I„dÅ+¢æð“èƒvüÓ!:ê°Á˜”ƒLù,Q^t×À-zRÆ
+úÌtM|Áô§_è'TßKº;Ä©aî_g¬ùi!3»¶pþm•¦¸âL%ø…ÙÊ»}®ô”.?mÂ)ÍVÏ´%:vÆXà´C©³A™ÅÖ7³¯´)åþÐ ºJ<6@Xî[Q_Q•£e¯ªý<ÐÛy`³™1œ:õ4ðx¸;úéäÄ' ¨‘eõOïjŒ¨ÇþöÚ~ëF·~z{$ܸu0¦FÃûoãÄ_m\ýwnÕpЋ¼riÿ k‹ÐHԆƦkWÿù bñ¶è|…’wßþÏ Pä¶ë$‹íرI Gsk7“¿<õŒ?Ü‚ öxÃ’»-ap•™ØÛàËVm%x¢ìD»¯}>ÐúCÁÎy#e«ç—ž¹l÷+?RcÝþàA¨ÿgž§$±‘0ñ"‡Dʲý±úO®^RãýÑðPp¹+ØQ÷ÇÝ¡PgÀßuUþn/˜:¶2ZZ¾qD‰ôDã/½~âIÞˆÓ&–G›ÉO®v9”3T_„3Ƚš
+ÏPP#:<<>|Ö ÃV~©ã \&
+–f$H[V¡Þý5·Î™³!¬0‰B5åP•~e™ªž¿ˆè—¤G¥‘TŸ -}k‡@J¢dš5½¶-\7Î]øš>U;… téRmqÂP"TÍ•e['›D~™ˆ2ç†ðð8÷G‡øLs­1’eÙ†FZr´1¹ë¬~¹pbýžÕË­ÄÊ­f+êIl "ßµòæ¯êSSÆìøJ=ÊÁZ}ÊTš@¶_¥+•A»É0?iÈ¿^²úEQ_zçºxj`cjy†&ˆ‰F}abDéˆ%ºïN¿]B¬.›1µ *%®öÜû¼;‘é‰ ONíÕýÊìQ臱Ÿ¿ÔÄ6ÒfÖžÏ?k½3}xzº?ìýìo?úõ÷>w©Z¿¢½¡ªmImP ÷@~,^ù¿YÜ4¥C C“À\èÿ}K#/㦫 1$õâw¶gRéùòýÿ1É“ö¤}¹‹E}a†À–L&wî܉ãxE¶xµ¯úY EÓ†RéŽðtœÒ‰xÿâ„ ÏvªÊAH‘Tò'Pà=¸•¶˜Ús9–8ýÉ÷$`¡ÃÉ“-w£ÝjêØÇc»zr×Øé-ÉÌÐÌlwLí<rtÛs›ùÍëO¼÷¢ª½UÂðUÂC1µR$ïR£­Ñh›í¨<¸ëÿ¥‘…/ºS‹L¨CyŠÖyóöÀs›Œ€2×[ëpîúàU= .pðÝ}»Â³íQ®NÇΞ}å…-0R "ä§ÍJóO2*Úéi€µ~ù]{c!`‚žqÓe¬;¹ü‡ ídk6€À8Îz¨J.»¥œWÊجJzø”GJ 3Y(û„˜ÛvcHüMß±•ÚÎÛÐêPß:ÍÇë#LƧG™˜·!XÅÒ$ÁÀ4#d±l:vpe–t097“òÓŸöH€
+¶±%ä C i8dë´±3CÛ´CÊdZ¦à ›44)I3í‡~h§M'™ƶ¤•V·¬Ëòöq™fú¡ýà™ð)y癕^Íj÷Ù÷}öÿÿ=ƒB& Ïñ¢—Ìû1Õ[÷ש†%9dæh:ømMb@\ð˜K^KÒ‡ƒøB
+<üÃw
+¬2! ¤•Ì$fÿK¼á$
+“„zsueB \´ä皪*þZ[«Z¸Q!òSÚÂÛ+3Sõª~ázUò÷“³k,”ÉÄ­apM-ÁÙp€dÚh¤#•h„3J/4_ÙÜ¡Èì3 —<Û‘Îv||·y­…F<¤L?ý$÷ÑGÇ@…àäx´M‘{Fn±"BÔ7H”Áq´öΞD²SŽü?îÅ'zM6΄Ž(W\îm Ë­?ûé. «åh¨a˜¤¥ùh¥<¯‰|N#Ÿù±°ð¿k
+ìfïö¡øEÛëûÅvÀM±š¿÷ˆ‘!R=§‹œ—¶­3êy)pZl?­&ÐŽuµ£/ª[Ì iqÁ+ɃÆB?ˆ¨öoÈú–•úms~Má"0Þg}Fõ¢±Ð´¦Qû
+øÐçã36Êåòo}±XP­,VEáÊå+VQ‚¾êz™¶–†Ð´0`zöp*é
+Ï8²©n¨çûíÂPÏᙨvE9˜Íºfgºåhûû·_èqnM¥ÜÓ‘¶7¯4ñ”ÖÊ®õ®¡-ë×TwÞøýOI¼ÝÂÑc{¢Ñ¦%SÜ–Nu)±¶{Ǹr¼>¡´$•cr,þØÌtc2Ù&ˇåXëý^„[ï4Yy’Âh11ÄòÉ›?ˆ)Ç*ÁI9›í°Ç¿á¦Ç¡¦LÚú
+-eVèc¥b]µ† Ú²i-Ò@È“GIG×Ñ4‰ÛIXÚ­Ý´µÕ´" ‰kÙdƒµU['µeHÄo_'Žíøuw¼Hlÿ"•¶]]{tž¿ûû~¾¹¬ÐÇ\¦lwU¡O¸|´òø¶¶Z·ŽºãJ;ã-›s™‹nîïbµ€Ìc%%êm Ò[ÉR%¯¿Ð.i}Vµ ´8Öj†¬läöÕÒñgÌk!Ïð`¬é)Pج Ìx•‡ªò<ÔȤTÃ3¨›È‚¶2ú*ö®_¢gÝ0Ö-ìXS&›ðjÞ"3F±”Å/^­žäÒN "˜áI*ÛWá‰ÓL>ï®Ôºav€™ê'>VxÜ,T“߶ILôSE§ð°O¯&,hŽ•Y[ÒÍÆ{‘úÓ& ^ X’µ&­†z”mßdD
+=bÄ¥?ÛÂ.Ç6ÒÝ߇?~tIr8¦Æ'6è}xڅͺk´×¥_>…ï
+o4&âûã±úD¼ÄÞÑèžiõÈùsõ+” Í7ì~p"Ü6myëWÛX?x¯øê+?ý¬c"ѱu{ kÂdnÑ'Ÿ T¸ejŠDê‘_˜šÜ]¡–Ì#ʾp ´ô„õhûè_V£­j춟C$rä`ëC6Q%VàÁê•l<Þ ”((n
+Lí'¨Pp/¤p¸.Ý 5$¢C“M*º¯XÃÔTêP•Ø)ÔxÆ·ý{OUÒ&bÂrN¡!DP檑F?^ƒhBkÈÅe]¹Þò´kQ²Ûêæsn9çáÒ½åªËžñ(Hª¦Ý:ÍI^#P{ÎÃGœæ¢3^q®¦»ôïî$eA'Q5f î¬Lz*bN ‰¯ê–7×Ð,!X-,Áﮀ8§G8´e%½Îk}h4ø›,97l½obªÍ‚Ä
+7ë¹\n¾’N§µ¢öñG—««,²™³,_?wíúÑp¤ubª.¯G2¢7n L4Ì$n¯£'ùYh…q’ÚØ¿Ø/×Ø(®+ŽW­ÁõswgçuçÞyÏ®_` IJiTÒR"ACBB("¥!Ðà›Å6i@@RÀxß^Ç„¶¨*5ªˆí5Æ.Ô¤­ÚªR
+iÒ€ Ø;ûÞµg½ëµ§×õ·~³T>µW£ÑHWš{î9÷þÿ¿=>>þ2!×þÎ&U+$Ù¦¦¯Æ²?ŒÄÚ’±WcŸ½öñ½æ‹?Ù´wOƒ©jÑöÚñgÆÆ\©Ä±…®›JÍõ ø6%Gcº ß)Œ4˜óã:†¢ædÌõàÞ‰ÌÑèx[*zbì^ëÃÎC6qìÃö××ر3*šC¢÷ÜúT¼ËH:Ý6[Ü…ãLƱÖ£ûâ‘Ž˜Þ’L4âEÆ\‘íFǃè.]?õ濽ñ¹zM!-hb­C©eXA¦d‡ÀîZGŽ¾BNôZ'¼åi·Í Ã[c”lKAÆŒ€4åå&ÏÛL7iº¥L7LûYó -ëátxû„3°[Jú,Ù¾åy·Õì­üÈËq,Åóõ’P½¶Éø”¸Ìú(£»æGÏ
+@¦–V¹ÚÞïÕr’ ”DÕ¾¾ó &™ŠÞ<’(ˆ#›k2n°R¦U$J<·®(xÔ\`›Ãñto,”°¯=!°‡–åÌ´Üd®»Û莳‹˜é#’gÊ.íÑö~ƒyõùåž—ÀûÔõc䬻¬à·^zý—‘ŠA‡¯xÊápoçm 7­TWÍaêT@´Õ­P+{^²LûeÃÃëîŠT·ý …ã5LH"‹&ÞrnÛtPy¯A§Fa¶AìÙm4¶u³FzÑ7kìuðˆVYûp+œKc%ýbú‚çpÂr^TpÓI/] v õx
+#ÖÛû‘ч©O˜t×ÍöZÿr`gá8Z@üÙ-ÖÉPCö¢ý¯çÐåæŠÀ6Õ¿MìÚDþ¶ƒùûIbC*ʲÆQ7›ø™3²Ù§\l—Ò˜9-/ulUŸt–892ò‡=£Îe3Èd»S^&ž3}.¢á[ÎèL\ýÒÈ”GÊ™Ü[¶—L¸Ñd·dúi3¸$ÞƒböpëU„,¼^`ì’iNaÀJ$ºž•>¾ÍëdñòÜÀ’ü““ù01®2•¹aëÔe S¹péìˆÍ`Ì[aðsæ¨ríL‰0@ÒdºªV$ov©ù!K!LL¢ÙwËêò¹+´yµÒ)ûcŸòÓ¶Gõ«Öé!ëì a¾ÍšWéâHÕôª-k(¬D¢ƒ€í÷>Âx¦KsïÚÍkD!üÈ̈-u…0†Ë/u@' $ÙúõÔµ>65$™#Ü‹FrýUSáGÌQjæ20‡*f®}¡pÃñ·Ÿ/ßø” ‰Mv2”(
+ñÅ&ç·'0T’!.²gü¦`øDÃ'§‚ ëå‹î23P‰¬¿Ik\4UÍQ·\öDh©á³Lw—â÷Î5P€,b9á§gk³þŠ©^hœW‡)«X%Â2Òqr8÷b¶ò "?hW¦Cäéç €lÃÞYg˜­_Á¶DÀ¨sç´³èG“^«Â€‚$‡­â­}R:Db—Ï„¼Æ}+²~­àå>dz©™¢È©€lv}>wMö*¦ÛZôðYüüjY¥‘$„¨U³%ˆƒ’øâdB&h¥Ì"T®ÈÞ™ä…*óWèeñƒsµóqh‘Š·É´-{AÌyKð~G]ƒ
+˜4±ñ}ƒc“&b¼÷§MkªöC$Ž`Ì•Ej¥|i*!%j(ÆöÎìììÎz{mïô5HùŽÚÍ£Õ£™ÑŽæ™gÞ÷ÿÿ=ô±« |=OÊRÇóÔË9êù§¾+‰ÝÒ¤n.ÿ¡zN]Ö%†òâ#ÙéñœÉ[ö}›JQjI¾²UUl¸lœI´L
+§Äƒñx ’h¤K6G亰ü–0Ù
+¶üþy–BÌðéJ9ºgRlêhÛÆc,°mÞ”?ùàhl¦fJxWº #Ä”öàôY¬‹)Ç‚Ópšk\2¹5"Cj„Óœ,×F£õ¡ êاŸï·ç3 (†Ã‹qÅf“¢( Ѐ`þBþß"N›¿H$òÊæ—  µ0fÖ_hÔܺq8nˆ5‰èñ©‰Úp°.<ÈÒ3ÑcБE±úI»pL®+¦…jI>=4¼[Qz„@­¢¼9)vn}±pÉU°ñšÁöÜSMccf<{£Ýt¢£LŽÏ(oO  í?îs—¶'„.©yi…šà΂p‹vJáö©é9Ú,„ÞX~©ZŠ„ÚŸt”P#œA¦‚-å[í,a¦à)¼²jMXê„åÁ‰’a`ª1,—Äv)Ø
+nvÎGι °©&Xœá3ÜÀ~]ÒŸ)ù°”ŸzpŠ"hœ)
+¥Í·8c)Öá<O²™R­¦y‹ýΉÂćö´/gñ=Nñ¬HºaItïk8i‹ìM(êY5ç.ŠõØþZÿt ¢)Bµ+‘;öSŠUüˆèÏ :‹¶Úµ±PFÁþ´—‹¹Mñ>ãâi$
+M¿†ÿ\ðÒq/¦xñs‡8ž I€’@
+)oA “¦,tF¤I¨—¤ÚˆtT6
+rÕÞ«M»ÊJøøüîž·³ ÎÓ¶Rø쳃3ñ¶S }XÑÚl‡“ŽhÏ´ ÓUp‚Ê õç¡fÖ.I¸ jÔù©V´‡åæ)±òöí&#a`1p3C”••É² =èŠ@cJ¥Rÿgü>þûñDggga~ôÝá•ÖÖV8.áÔ5Ðþa—®Ä
+%Ô55Q ·‡¥¶ØôÐìêᢂOÚ…CB ¼ŒØmfsºßÛd¹^ 4Ýû׉­/ØyŠ¢# ƒÜ&ë¿øǾ‘‘׿úª ¢B0t(n…7¹åqŸ ‰Káty!P OãÊû¾ér9¶9z¶ML·‹JÝĽÞo¾îŒÅkª'Þ±V"‡^ÚÍ+¬t ³)½}õP<z*ª …ŽÀÏ!KÇ$±–-…Þœn–"Ç/}rð™uì…áY€pTM1$f\Çœ»è{§-sDuhbn<éÇb^"â4-8Íj/•ö`ažv1‹N6Õk„¿YpÈ*12ï¡ §(^~¨Ñb†¥
+û¥j|qý³QDïQƒ:òSËKÞò蔡4³43q×”GÊ‘ð<¤\5¨¿5e|+²×ñ„wyq
+KzVª>ClœV}äF3QÉ
+Õ‹&°Ô{©3uB[ð‘y­z—ª&u
+ÆE }ÊúVÞ+Éz ©qM~‚,úðœoiÚ£ÉMB$'ê¸Yñ=à¿aÙó°•<ƒ€ã²å•Çnÿõ’¹ÀK ¥3äïΟŠH'¡”–1Ô‚»ýRï÷z¾ú•Çj-
+t”H£uÓzýïgKÆ…`Û鞧Eh2ƒ%­Íµrø,ÊII:‘[ÁöÅž1Ú„]Á@³>…âTu*ÑîïK&^ G›/ÿp·à<«G‡š$ɶ¶¶|>H”N§?[8~>>ýñÉÖg³YõFj·Û!„,kà©Z ´vvl È]ÁPS(Ð wÅåV9Ø
+6EÂ'%©±/*wßo
+G¤®°ÜõËwöŠüÃ-Zù£ d¹)²p&Ýsû?M[6ñ(œE`±þò¥R IH9‹7J†°¿GŒJ¯Ýõsåv„ø@ðˆ’hcòÝ:Ýð2!(P†k¿>üÁÍïìzNènÙ:ïo G ;
+w†¥n¤ÑXÏá}›9ÜŠBrâ¡Ý†ùõ¢îåÀ¢¤(6¡w_ˆÔ_óí~G p"eC
+HÇâ­ÞÀ–;öÒÿì¯DjQtÒ¥!&瀨?Í8a~á¦"áÖGF¨Ø0Dm¬âDñÂ1úMº „˜¼“‹^ÐÇ:@ÞlëŒehZž¡R
+ƒXÆ­Sú¸œsUÑmÉôùSÕßÁ+ƒ ~¡â挭OéªI´“B•…b€yeäê(]×nýŸ~B¤fË23_ÈùÊÕ«Ú˜¦½åªïÁ³ûô ÏóܲÁš6f&Õƒ\…Nx˜´o™:ÅܵŒÓù.ëÓ«žru¼<ëÕ¥§©‚—Jz¡:ñ@~W¯ê=Lj&’´Ø6ò`þ,cYfzia
+æ<¦ÒŒ6ùžÝ&M¥éK>w Ľe¥ëBÆ£Q=ú{e#ê¤VõPéÅ—ÅŠãZÕ«/]Ó*“†¬gii\S˜$þ~µî›O0€5VÉÕU€BBrçÎw•Ô©…ùá@GLFæ+ó‘ w+þž¨¿# 5ú/þáãÛ6o i’«kWÃë¿kÇ‚¯·šG®€éÜ™ósgRSTiò¤PGDî@B‚²(m]L¤hWÐß.K=~éD,ÚŽ:Ĉ܀¤¥÷ÌS"'P
+õÃ÷¶!ä9§¥ä‡Bnò‡•úh¨%"õüãßG·?i±RV'*骋—¾ž”ꥹSr¢þûgž«hxiÿ£÷j=R´Þ?‡Nhw<Òö‡HòÙoc=ã¢XQc]]-ÖY€E$ ¾©—ƒárèh,ܶÅã­ G$¤p¡nÉìÞ­§!èŒG:¤À‹·>¬_[¹ÂÆÔBÚÂ0à­Ñcñpg,t0h—§Â‘ÖÞa÷öÍV³
+©!6çâ•>SÎ…—ÆŒ Ç
+$Éa>ãÐùÇLÙAËoÚ(ç.25"¦ÝËSçÖì+cCba€žmá‚°™+6ZÄZ`D…›+·Yk•asl„*œÇÓ#?Þ‹™x›`•4çÜoË9ÍÁ‹†b6Ñ@þ—ý²mâ¼ã8R×ÀBpœøì;ßû«_I ¡PH36…¥+T*±!eÝ`ƒ$@yRÞ¤¼ØÆ/±!«˜´Uš¦Ut¥y1 MS»?ª1
+…nP± HbŸÏg;¶Û±}{²J•¶ÿÂ&Mýétºîy~~§û|¾ fÅÙRUy·ÒAŸ!zL_aexøEDt3P)VoúøPQÚ‰dm†íëôŠ5k +«¿ã\¦Øˆ¬ËàµmMMÌ9)Gò<ÁqàÐzR@)#ÎÔ®‚Û×!¿®%ïŸ(K{Ë2n2ŽÜ‹<-UŸõ5å+šr.r˜³/I\bj^0™%¡Ÿ®-=+~[‡~oeb m^Iüâ禰ݤx™äè~oõÓ-Oèv¹@Ù¶QÇ·2õ50C‘`à,Š9^/™²³ÊöØvL‡¤+Š?éF¶ò„G—ugZÅÍ uXËÐb-Ã4æ{‹ŒxHñ<ôÖ*èÌOtÒi:ç¡“®¢¨wùôu´·ü“NÖߦþs'%Ÿ­œµ©».æ1G.P9GaÎÉ$|H±,ìæ“MÆ¥]ØŒ Ïy:÷ÕõRÓnɬ‡ˆ»ð˜‡Júè)'špèg=”â"n^ôb±^äîÑe-ë‘r^‡`ËÚ‚éi–63UŽto/yü>¥ø¿­ –Ì-Èýþû/ •«¦9,4àا΂ŒŸÏ]Ñæ>P+#äl?š¾)~h¾èŸð«²ÃH~è¹Y?ù«.£À- XˆPݵdäºJé7*.œ.š¹
+t‹ßX¤\Cb”¤G‰Ä:7¬UühnNŒL«rÄœKôSÊ Uú÷H~X•¾ªÊöë”ëσ£)t¦_“|>sJ cOÛgl´ =¤W>”kK&û­?ª*&0T#ÎÇÛ×´b\jå# ÈDBûcG¥hk@j
+Ií±Hˆœ¡ðþÉÉ“Í{–‘›Öa·ïÔË‘®{_ì­Û±š@h–%) cI+‰ÑåeÆîS›ƒ‘àòdS8Ô)GÄÐNYš‹QyÎI¢á£‘p}p¢)=2þ¤!1Ó>1y¸eïK@ƒy‚Á¤´¿»ü›9$å”lJÉå€T€S_!+›Í~}ÿ¦þo
+øg.óÕÌ•l^¹}÷Ö²
+ÖBz9Ã?Ø"Œ=<‘Ä­©M’Û&CMÏÚF
+c“Àóìæhô㱶 5•Õ×U7ÕmÅwæ­ñx<Ú˜¨ŽïIFÎ>~Ô”÷Ük¬¨„Q‘§9Ž4ѨÚ骃§e±[DCÝQéäøã}S±Q¹+,vD#{ç«QnŠa±h8ÙuîÔ›¤f)±5ká±/w‡C;%±çÓ¿´7ì)ç0ümh=>‡$–ÕÓ|¥ïy’N“¢»<ÙW÷°‰¹4ŠR€ôšt³q;’ñÐ75ÇGiÌ Ïø,
+,âZ;r—(vzÆCÝ:Ákq ÂÖUðŒ‡Œ{õ³ñ‡ï”™qc4”þa”è+Q<ÅÞF0aÔŠÃ(Ž0ÚHXD/žvi6îâN2² Éàq¹Õœ²Ó)·C½ÛÊ ˜nµ€¾ù"Ö¹¿´ûÃìáY"ì¥}KE¯v"3nX$i+Mºðió?×ÿ´¯&ê0å\¨â€Âç¹*Š_ŠfÌ°oƒ:礇jÊÍ%]TÌE¼ü´OíÃ"ç
+òž¢©^öý&n£a¡AxG@úJ†«b u¨EC,Å¿· Ë»)ÅÎý¸º”Ñ#@6Ö›t€þÓ}DÌ»$i£3¾EÓïBwN³/™h5YfB1#ªµoáïž«/éÓ6,íD§ÎY”óLÜ©¥Ê4¥A˜hŠÐôÞ°ûøuÓ)›vö‚6á’nd¶›q°ñ‹DÞ]\(. Q¯ "Ýkž±3^]Ô®Myø´“[äzɤûð¬M—t2³iå¬8Hùö§ãhÏz`[:…´6S¨ÑLêà—ËŠïâä~(>XÒoßPÊCV+ Y9Öu\fÊ•*ý¥ŠN B ?”Õ¤F—Ì—¤®.Ê€õŒéyÔøzÅb¢HÎjAU®}ÏM\Ö+CßJàS%ʇP|X›QeÊ (CK½Ç¼Û¸ø¦U¡”Ê\]¨\S§ý‹’ýºÜ¨:?\E£ô#Óý…éZ$•!}~„Îø‹ò>u«`)?©\/M_ÑÍ qWÖnBд…€a0èýukž„·? ¶ˆ‡[Báƒ!©30¹_
+¶Æ䎉ñ݉äñÉÀ‘ž“¯< ¶>´?küþËœ' 4 TdçK}à KyžDÝÑR#ËÇÁ‹rèp0Ð(Š²Ô%:åp I<(ãÑCñæx¼- íÛÝ»}òW)6ñœ†!9‹¹ìæg7˜òÊ´’û7l}­%€_ÿ}h~Só^ùUhÈÜ s™L*9“ª®®xÜÈ œ¾|å
+Õ_?ëz4^
+6Åå·HÒžp¬ùYÛˆ8y8Ü ïûF¥QÚÿù½ŽúŸ­X3Ç-xêc‡nÑôÚf‡–1 cÄáMß­^½Z-P”™aÿöà°nB»æ«ŸÀD @"Ö“:Çÿñvrª'$¸u«qãÆ%Í4ÆtYŒ´MŠõ Y„¥²_ç±qTwÀ«*¤-vì½æž7÷ÌÞÎUR’¶¤J 4 *ÔJ))´Ð&NlÇW;‚íõìήXªúGª‰"'¶×RZÔR!ÔJ”ÑV©ã½ïõ^³ëé3é_ü‡”*ÿtô´FoÞûû~~=±¥£éÄ1hªXô`ìF_&ÙŸLì»eÏ'Ñ¿Úe¬j¤î|üÑé{¿Å4-Š¢L‘êK}òñÀ '抓`{+²à«NÅËtƒð•Ó17FÜÆ8SÖåq{)(}d&Ô˜PñÓ•Êa %?UR™J€ƒ©W™pdžó?¾C!'°»€ÍŽ@<„:™ªÆ–‡¥å)Ö¬×^]e¸C$.µ+%Ê,‚ÒtûNÊÐІÊäýŽÜ$ع‰v±„D…Æ®û˜úžŸ¢uÍóÀF–gQ’µË@É«ÀÐð¸F>¶½I àì‚Ðl 0Å5[ÖGýeÃIj# ×SÍgµå|”>…­¨æ¿
+ãdq'Øü8› ±é
+bòÌ“Þü°Ç Ëð¡F€X™pÔ‡°ò$_
+eÕ¬ÚŒ_¶½7 v+Ol#ß þpãùž-wÓ´D¯qËÔ•SBZûrL•6q&–v¢Ô{¿§ðèšUŠSrzÏû=Æòçj‡lÈÍ(Øì¶Ü¹Ý εC⺊C!¤ýÌO6YR²ãNwKvÀT£xÙäÈ÷(†/Ž±É‘›wÔj !Í”µT¨dJÃLE«*¨~*Ãå1 žVµ•UkUÃÊ!º¨ÑÅ-Ê“lÕ§1Š@×Æ¥¢Ê]{ô~תp4F{HÆM…ŠD ÷y,¯œØüÛ“bM’,Âxx’²}ôY[0Õ.›ŒY´>c[·–ß´”ÖUæn™FŒ«¶Â«-ÆÔ˜1¯„mL8·rX3iGðU`ouH?ØNïÞiuH0fMÆi¼‚é3짫j¹:äÞ“<î¼ÇÅN?Çe¼ËókWÂkWæ°ê ¥ÏâÆŒ^¢6éóhíµfã’ ¢¢|¡Õ˜Å>÷:§ùò¥/TÃxý2a„-Å‹&’Ç·µÒ”×)‘"Âmä×wøÆbnO2shµ^e» !²éî|º?›8œŒöÃÊ–ÊîÄú–R»¶o×}_·óƒ"vÆ:;×¹´Ø•ÊtÿñÝCmî0mvëûÀÆíçÙÔ1X¦ñUä³Ï%“pZ8a2Ö—Nô$û Fr…þtæäß?<t——åI”ÂH»¼þî¯m-– £X¯üõzýæÎÍ_]×ooŒþ»ÅÛ
+I½Ñ¨îÞý(Cq,)±îä­o¾ÝM-”zÓÉ.(Ø\úp&ÓKtü¯5’KŸL&ºŠù^ø¡ÂL_\<¶ç E¦pÌêrHX"ýb$
+9ÝÿëÝ;î±Ë".r@XR)û#ß󤲇³é®ldðV­§TêLÆ;£‹½©xo¾p Û‹œÞu/Ñ.öÀÂÃ~m>¼/‘êJ¦ö&rÏ.Eº K≟eRóé£Ð$™ô­ÓZüH:Ar0•èŠFÛÓÙã¿>÷”kæy+‹XU¶x`vãŒHa (Z¦›!–FdCEJ/[Š¶dŠ£HÉOÔü@°0J*¬ÿ«mi-DUƒLÙÏÂUPžt¦îw;̬YGÙR“ ºa 9L¤Ó¾nz€[ö‰å Ÿ
+X’gm”™¥ X‹bcŽïxPž‘iIð(xô”\µe2̦àGX`Eéü~Ö²&5С
+%8àäÑÐ*3ã ÀOíÂ/àß^ŸÑ6|!@5|Jq’[VѲƒÁWõ£ðÐýH=€ýÐ`â*ÀB2D\ójô’·Ÿº_.ùØœjŽ‘7Ž
+‘QvzÑëÃ@W• ²:!ü¸>lmLà…1¢®YV|–Â0^
+Hzƒ¯i9Då†HÝ×\‡çL q?Ûã ?R
+¬ÏitV³åòrhcFãRÚºÊ8ZòO=èO·’2ÿz¼—cd˜Ûku¹œ8ƒ£2#1ŒëØ,õYP“yÖ˜çª3¶j¸E_h©_6Óø­ÒHiÆb,´V__k,Æ V½ˆþæy‹X8Eá µ1§ÅÈŽíÆœµ:ý¥ZØ\·skŒ…–7TV_ÄhÉÁ Ýóí¦M;Œ+cºY¿ ä/µ·L¹ScÁÚ˜¡õð:ã­;ôK¦z+…-Ÿ[#àÓ05®ZŠ¯›‚¤<+F.¹þ&§PMkǬŽTºöoK䟎Åa×óL±x(è‹þ»#ï+d"‘_¤2Ï$³{ÿ‡§7x
+e8ÂYرk$öR)q2—ê¹k?Ü¿ÃÁJ,ÑzêŇ–Rý7÷æs‡ó¹ØÖÁžHìO§º“q(œãP#ù\?<gŽ';£?ù¾·¯vK$æà%§Äÿ°_ç±Q\wÀiZœØ{ïÜ÷ììá(R’PUä RB -ÁÆìr™Ô’`ï5»Þ5”Ò+RKT’â`blAm“¶ŠR‰FjªP
+Äöîì1{ÌÞ»ž>·R+Eí‘!UyzZí?»ïÍûÍ{ßÏ£ìOÖªŠZûO^üûóËö×òµJµ¿¿ÇLªQÀ[)íÙ³¤äîéÈ‹qÉy÷ζbþPtjo,ê’“Î/Z#ÑÈ®ÈTÀO,Ö àq:¼‘ÇëY„îè N ¯‹G^’£]™˜X×ßní]¿¶™a €Vš_³ºõÖÃÓÑÍ™„+Ý9Wó‘ãû£Ó/¤SÎdòh<q šØ%gÿæ·›8Bã7¬_×fç(ÖÜÚÞÔôñÍ=RÂ9s½ÿ‡ѨKQ:'ïnOÆœR´üp®æ“Œ‘¢»EÀw°‘ã2ØÅ'¿³ÑJ!ayÔJÀ4gqÄØ„1m°fßÓä}-¥!:ë6æƒT5ÈäÜhÊÏV‚0Èë’/zð‚‡,‰ÐHV„€R€FJ"Sði””ӖTØöD³Ía¶à ™ô+†…yæEÑ÷´–‚p͇dÃ&i°²KXäkùaÖÿ¬AÚ,eDÑŸl²”ˆ&*ÓGÇ›\CaÅq›Á+"\ ت^Óx·ÅN‘fÌ„±øÉÍšâ—€T7ñƒL3³ ·ÿñz)Ä(akÍ£¯¸‘´×žIzÊPrÃ9·&-"%Ÿ#l(yK^{Éo+ˆ@t.@dƒ˜Bsa$3„¤‚8Èè”HfNü`tüžóã3]9×tõ‡ô”<ÿ=x¤m…Q%~·Ï  `ÅA$$K§àÜ°1$²>vFÄ
+^$ç'
+aÔ7ç…«^3ýj
+¦ê&Ãu 9 d½çx–ùÀß÷ÀeQÓ-´ñ, :LXGr•—‰”[—ô€Å·^u9n­ˆˆ¶‚>”CV)Ô˜
+RŠ…(‰f`Ë¢VÌzy`B— ¢—ªzêU·iÆÛ˜t³© ±†fßÕ!²l2¼{€ÜÐNbÒNƒ
+‘ž¨«Œêfýœó¬ŽCå1“rñõ#
+¢gC;_pcïF1‹ÙÓ-”¡Q2M›åG›ùÛG™çVÁ&IÒ»^?ã‡Êak!(|ØÇ.£t5/ð?cˆû[Ô!,ç1åXUÔ¼†TÈRñl€Í†„ÔI“¢ŸíµçL;¨¿ 0êiRÔy
+Í]ÿ*UæiDËäÇ' X‰ÞuÉDž³ê™7ºÄÌ°¡îfà7ÏýˆayÁD1I¼Þ!•ec.@¦FÉïY4„ù[ÍïóÆŠÇ\>+*>­êmÉ¢ê0¯Ê€Š¦ú°PaÓ.¾< »Ðf¼L1(•B ,8ã™awð¯ÿeÊ8ÌšÛ\”fÜ⌇oøDfò^²ø¿§‘bÀR’õE/ T½ºTH_ ,ŠÈ-3¬è%f‚|E6–\FØŽêU7Êmy_Ò©!’½b^}£ˆA<¤Š.>FH2¯Ó ˾Æ̸ÕCb'­õa²î§ò£\Þ¥ŸÁA¤ƒ[VÎÀl6j‰m¡Ð
+‰3‡Y9âÄ6!ïm~s‹x‰M9$îqb4/±Q ñ„ÄKðüŸy®¹ì‡-cnt|©Õˆ/¶1ÍcýkïoÔß÷HÕaVõ£¤Ûœ #T0'c1YüÍò›Â<½žÛîÐ|<Äׂͅ!.´€Ÿ³>])DUÝ€gC5@Ô<\ÁÍ'tr”L‡ô¡Í|»h@h%Æó$k¶°‰'7,5]ä^X\¿ÆÎŒ-¨OèkWQá26s£e®4R+W¿\Ç*©Ê]é*®^ýºú»¯T®¥‹ÚäøcÕ› “l™#j0Œá,i£I^ ³ cIíSvüü~®~¸6L‹4«-[V±ÊÅ/©×Œ@šú%¬~}Þ„Ë°ŒäpšâXq…aÞûg¨ˆÏºÎúÉ ©Lè«ã@šÇÔ T'êoëŠ7›½Ó¶mõˆ¢! &Äüê—[§#é\\þ
+-ûÖNüÍ=žD”ÀiDó³m|äD‹i¶p
+£B*(sE7£zÉœ‹¨ÈD<(6¾ãA©3öêà™‘…ê0Œ©È|Ø«S} ¤å½úšÿí^z·ˆ±Ù¨CËž[L~âÒ«#쥗‘! Óô‘«ýÉ]k¬B/>…EÚ Îts?‚3Ÿq¡°Ÿ{’!Xì„h´á›×p+ð¯q¹Ú&Û f«þ¯æGM¯Fõ,Ìí£?dZÑã8…ôÂ’žäàCõa*xdQBn‚Ç50Æd=è¥([²2 è‹>CeˆQ]7*ÝÏXÌì"+Â9z!‹#Äýƒý2âºãx£¦$ØÞ{ç¾÷06ƒ ¤uë4QJ’B)%¤J
+N³°û¥òÛ®Ò.Í»ÿ8ËH2~²8fÑ.êµKÌäˆA½2§8>'Ñ05n»õV݆ՂÎì …jÁóú¹ï­2d£rddwBÝö<ÁP¶5O­o“Õ®°Ú´ùùj©¥ ³ pøq,Nb$Ç̹pas$x"!·ËWŽw.jÞÃÜúähB킬¶D ‘¶h´q¦d/ÙJC4|P v¨Jk(Њt~ðߎº•<m®¶³‹£õõõÑhô³vöY/û¢|¾Êdp–/?}¢MáTûᎭ$MÛlVØ~U4ùÎx[\Ý ìR•îMJôPPn+°÷Ôرpx,Ö u‡£-Á@w<r$Þ©*GŸ{š`­<.Ýø<ÿѧÝxÓÛ×ö¯Zlã öJæáü«m¶âIOÊÀõ}ŠÜ,‡Û€"âÑV9Ø,+Ý^×çmÅ`m±±4A2èþ†•Jú tŒi;Nu¬µ#hm5~¨»þŸv5v<ØŒDüÇ"³G#÷Ø.ýê{8ªcHÄD.©a„úÙÉ_3/-T]æL­ê%5Q?éÅUŸpßF)ß?bçh;‡›Iüñ _#
+.Ä»uM1U(oaÊv®w¬^ŽéXšF+ßmæ³"îÅžubv ¸¼EÿáI¾ •g½úÐÀâuv;c²,á,›–[nDó1³åæÙ>"-a)€ ™ñÑ7™-y.—-ÓB
+’,+} á@.—+'‹S%ÿÒ¾à‘Ïi™*Ád±PG`M’|' [;œئD^)Ùk‰N<h÷ …ö• $p8“> îǺwZbÑŽPhüPT>;6|·ýç·<V©ÇÍ,OÙ)Âøô7k4ƒ.dó\WçWog/åGpŒÊÝ2| J{,v"üÙ»×Ö¯AÖ®±±Œîå½5þÑí­õ¨Î ”áÔñgÂrçÝ;;ÓÉŽÐÝ#¡°çÓ@SàæÉ´zZu†CMÀW¡ ¤
+æ[pY&$L©˜—gÌ1%Eþ~Ý*ã6Gz=»¼‚£µ/Ã~ðSÇ'ÒÒîõF#Š"6òq ³›«(ê‡@_k1Mºª"½˜g»Õ†²óiF"?߸R=Kçz ùŒáw?@»«â/?á>”Vh¿Ær¯’³E#·)ï2æܦ‚hÍK`afU4%$s¦Çžr I7Ÿð1É^*ÑǬÑ>“êC`–fõ™Ó¤„Aßœ¯„.ËÀëO³MÚKÞä¼4ê)7 –šƒÉìT‘P%|Fq›R}liFàªIRìe³"•òài/‘‘¨¤Ox0à"¨Ì(„ Ò)‰™QÒ¥ZHyèž›i¯=ö*—–,%ù̯ d<H ÉDê¾×
+º°¤È¦{Ù¤D&¥{Æ{;Ý/D]DVâÕ¾ÚßlÇë…G Šr Çi’ÂLÜw꘿ºŒÚðÜ”ð
+Têé+Ëÿ<çßó{?ŸÏcÑÇÍÅ+ßÔÆ}lyjk}§(ÌMëׇÜÚøcú¥Eå++NwðÊ9hŽ$nÊú—ñÒ…ÚÒ„%z–ÞÔDÚ1Gyì$ûã§;çLú(βò8¹Puê-å1$w~IiÂPþ}ý?\ÿÝÕŠñ°¸¥ÎÎñ zío'S‡RѶ„ÜW|á™w•tçlìM¥Š oBé¾3uümí<ðâ‘®ç>üàåp¸+*wÄßÕßmýÑ÷·Œ²s3xrËL¬;™:œˆïV¢>px›oƒÀ±§eõî–hŽÄ:”äÁLÊ›mÍ(ï•ì´Æ A1)I9½<(ÓuµPüÚF¾¢¨È=!Ñça µ›7&
+§ÒκX+ߺk]<Ù©(û@
+ùR\œ 5toÀ7:—œÞ}ÑïÖýTFD
+ÖT>"ÿñþæ3N—µÆmk2›P«AíkšìäÍ^F)`x±gi‚ °žøó»µZšÔ”NYŠ5ôð„an„8ºuQ¸ ¯o`‰3oTû–-DÁ ó~¶,zæ$^ Uiyy+†ì´‘ 6&é7¥Ha€RC4Œä&Q›Ö!<Žë š#I« {¬ÉSsK"=¹ËÆüØ"ý¢aþsº<N«— f#ãˆ>Z[EÕI¤4aÐGA!V¤®šÏîgœ8ý¤ËÈ­²,uYpa¡ª un{Æ6½ÊH_3¿¶a±•ÿûuÅuǼÿPÉE¾½»3óæÜ=|R
+P©!!(‰€@C(!Ð*.c|Ÿ,Œ ±÷>Œ QU”¶¢MÀ.ÆGi¢þQªÒ–¨UŠ[%€íÝÝ™=f/¯§¿¥RÕò‘QU)£ŸV«ùc÷½7ïÍ÷óCfœÀ‘iC­éo`†¹©"ùfilR³8N,™F&uñkËrÓš…q¤Nb 7 ¤iË_.?gaxŽ^ŽH×­]ýÏ{ÇbBO(ØŽ‡}rèx$ÔòçM’ljܖŠÃ«xŸ>9;ÛiÞd¤X=eÔ3üÚzî­3Û;š6ZèN[ÅáËoM´…ù×¾ê•BR¸zÏàì 10(…xB %cƒóóñПîvmß\­'p'k̵5Õµ‚ø7Dr™¯9òy'EYÌ忽¾k/‹8ž4³_¿BóÇ;sÂaE郭%…Û" Ö'ž’Г:Eᘮ1;åŸíŸ™i‘£AW8Ü*†›""Dy›ß4 ´ˆrÿ\¨Ï/vÂ-óÁFEÀ–ÅÖX¸yɆ$µaúB[4|44¿þ]þúò6•“x™™^ydß3Áý‘@{TîþëÌ‘M›V(J•ÿdô%!Ôœï… $#5 BSÀßâŸo…•ŒH½ €Ö×ÈCàYo$rè³;G¡Äô–#*)æÝý(6jQ†ŠÒ.FöÒ†i7ý4½għ†èžm&œåiš4°æ+GHÕC¤ÏinŸa¸¢b–ÆŒæ
+†wn©TìlÄU®8õ³oU‰îoÇÝXÒ]¤U&}(áÕË6m>àFHÅS‘9oˆ cK¥‘¤ rœ…H6¤<¬âan:î¢bžügÜA*À
+púUùâM<9QšºUþ³¡z³Nkà)ž­5³†çžá?»×;Åp·î>슉'„¹Î¸|R{ò]á|SB:ý÷Oû:n6$4¹&¶nÃFÓÌç'gý­v]º´ÍDÃMò¥”˜wHcØß ÃàÁxt@ÀÛ²(ÝpÅ?×.†ÛB³üýšVU’f††%aIók»ö@Œe³iuñ«___éÊ.fs<é²ûÆ 'Œ(fN÷Ëw_•¢' .ý³-a¡%:.øBbÛ×H¨)èü¡ƒî÷w5¯i9Z78vG¥>Yj
+¦˜< †;ýÁç;"¡9t&4×´¥¢]‘@kèaûRŽŒ'hƒã
+ŠBe\I5Â.0§¬¬2ÂJð NKΊgtÚƒEÜ…Ÿ&1ÊÇŪS½_úÜ}FÙI+6"cC Í„“‰y™Ø“³“6Ì|÷µ{Æi˜¾VÇ"qÜò=KÉûgÉÄ4–¼Z Þ"“ãK•ò ׋ÔXr¬8sƒJÿåÆuêu6}õ[êõŠäX™ú!óÅωµUËtœ i¸cÕŸG5êDInº g»Ž£«5NWpOUn¿Sš¼®Uo–-ŒU¨ßT¯|C½ò/öË46Ž³Œã ”ûðµÞÙ¹ßygörœ8MœRš
+Q¥U¤B E¶BIˆ|áø¾r8 M|ìeoÒ&­"(H!ØÙµsôUJQ!TÀ‡P°¯wgfgöòz{x6þć|r„úê¯Õ;³š™ç}ç™çÿ{L³ã_Yª8³Ajþ*cŒZ2—WS™«¦ÌH‘qu…|ÿÊó´°Š!Œ$I»vUhÊ1Y®S”z¨“P‡cò15Ò69}PµFÕÓwï6|û›Oæ
+Èl‘Úˆ˜u~4©ì›ÍÔÆõš‰©–o=S9áDÄŸ?mP•Úõ¶5ªÕ¨zc8Ü5#·†¢¯éZ8‘¦ö@ý„VNQOÝoTõ†_ÿ¼Cd¿æ$ÌTÐ$îïï/xZ>÷¿öÕ/Ç3
+ 4&ˆ'@oæÁsfÄš%–I“•*ß
+’
+5—Yïá5>p€R!‡÷šµ1I•*ý£U©jŽ6@¤VTôR“´¨M£ãcwfvfïÃkO¿ RUUu%$Úü“§§ÑìÎ̧7óÞû~¿Ì¼{ŹS£‘<ß"uMÍônƒÚ'Ÿ$ÉrÊ ã¾×±cG•UOïïØ<Ã;Sb_bòä_kW' *½DŽ‰QÈð}BÌ3y°ík´eV­Pßù½‹ŸöHü ª3Ó.)Þ+.ÈŸ1¾?"uFø³Ï|Ý@QfØ3••öD"ñisõ³ößÈbqaá_•q~~^Y(]8tСѩ, ÕfÝݲàyd@Ý<ïŒF»%©âÈlwL쯣}qñˆ0=(FK«K’_ŽŠÞ©ÈP,霙<šýñ_½56²”™Yêý»ûf#'b1¬Üb¤_ˆuÆ„Á‡$ºÀ@f#½Ñè`\:^
+; D¼’Ð+D1Á z&™¸wo_µ·’DµÕÒ¾mMíJ-⬾
+0ô‡_t‰Ñ=÷þæZßÀèu&C:­’xïÿÚ.þ}EÑ)D] öïÔ¬sÏ.œÕÒ•Z £Ë÷· ÒˆQñ#Å ºäEMÚÿÐôŸ÷©³œ5"‹cê÷CÕuÖ'0œ²ã*ëc€‰ì%Kêüòù
+7¤3A6dä ?¡ÈP–ž
+SÐSA*Ã1òE*ÔΗ+÷ÿù”å'Ó¹çñ¶ÚªeÕj¨¦ØCcE±,¨d7⃊\b˜™¡lPÐÁ?&©$‘z¹uµuo5ÕÓhÿáÍßÿFõ•=ën¾ØðnGÍ;­יּšžæ„»1ínL¹ë“ÝkR®uÿ±gÜ OSÂÓ4ãhøpïÊÛ»Ëo½P~«ÃþÆóëC­•ç·ÔœÜºöÅ5¶íu¶­µå•¤ÆL“Fò!õ,M°à" ŽS˜Eg±èKÝ„›JVEÑ`VfL¦w™ht}¥®³EÇí&®öZÓ!6†0Œýt!dM̉a6`R>da”Îtà–%…Ðù!~™»À†É"GÌ…ÑLP j
+!"dB–d€NpDjOÃ%N™º€.j5<ËQI¿^¾ ûhà pÌ  % SÎ3ÓçضŠÏH+h`=óå;Cö„ËŒaÙF9[®„U_´»Ú´ÆB²†Õã¤Ð—ÑeIßl’†9¥pxÊÏÆ9ͼŸÈªxTÙšѦ9Í\X7Ò†µðvàfù
+ï”E~ºÔµµh­v•Ýdb–ÑK+óÞõįGmÙ›Ÿ“¯i•kÚˆrS.?.]¡Õx2×UÊÛ˜rå‹ñ÷¬ÏPP*â$£78…¿úúsS‘ÁDì4?Û‰v
+üÉó§[`¯ð¯«foü¼#ž:ÚÛj5¨`•îj³ž>ù\[ûpqŠÖÒ¤æÄÑoF…äÌ)(<£’ãî½}°ž-xµ eƯí\,¿A—“ÞßÞé^[NÀš7•¶3Ù¾ý@[±XÌ-,
+Æ›:8;Û—¼²ä˜ž>tæô:JK°D•ã ="ô  Р‰ŠñGdÌäèÃŽ„tƒº—N„ˆ6=Ù†$a .L»eñˆ,öߟ<(Éž?î?ó¬•fj*È&ÏL§Ç¿³Ñά`0µÃY#΋N÷ÉRßÍ÷ºêV©==­÷gŽÉ1÷§®ÿ¾[#=‚Ð-D<²pF+È=7ÞuÙHå3«¿>°"ÔåC ¥ õ°6’ jãA
+/I…ʲœ=9lLú 9Î ²
+ú$szx»ÅÆ)ŒòÕíßÄ·dh]9²¬'µ²½^ó«K˜ò3uþ­¥Ù[*þª6ÿŽA¹üDáú?Ø/óØ(®;ŽWj*‘ãcï™swfm|
+¥ÁÀœ›8ûµÙe2,Ü×wíÛБÍfsãED¼'}øþ¬ž‚÷Ž¥õŠ?aš¹üs=Bе\nÈ|]ÁÀ¡w(èöÏw‚ÂB?l»t±%z1Ä‚ œ™i…#aÞyýÝ–¿þy¿oÞꟳFø#ÿ‰Íõ%:Q¡SßøÀ6{Çks=¡€“çŽÂ†L#GDì™»c…>
+%ÀÕ«Ýï¾Ó:;cà¿ù©Ó:ä{9nï7Žÿ/¡Kœ1±Ï7 wj÷q­¾Àomíõ2ÂP†ªõDéÓFdv´
+ÞäI*>Ž>(€mÁréd¡4¦ºÙð !ydÉ  ˆè–gÇð”OxÈÔˆ>êYrŸx?¢€:#ªø˜ú¤I¸é› Â^$:¦J+SEÌ#ã‡e)‘”KžBiX%ô“׺ ýÏèZ°Š\=ERàÂ…йž$(2W¿©-À’“VAcŠrY_IîoR÷n£Ïýd͵óí›ÂÓ– ©vSÜÚŠv˜bVSÊiNuÖÆìÕQ{UÒ^·ã¹¾:f¯‰ÚÖ‹öõQgm¤sp…ho„¿¦{ÝR4²8íþLsÂÖ´7%­@°U¬³.î¨ÙŒI‹1Ù^•:¸.Û^qšDG}ØnÃD »p(`¯ý¤õ‡SÏWœy¢¬«¹ôÇU¬‘!iDCpR*A€ƒÓ˜Â 8a@q†D ¤Ö@Q ùÞªÑæ£>…ÏͤΕ$ÜÅ’W! ʤ“ű2:DFÇÉÈ.žÖ
+5./`u
+.Òevºèó«Æ‡Ã04ŠËJjJ‘\m Ú3¨ƒ„DÛÍ›§¾˜ûrVÿüXüW—§v™ÖÒ ¼˜˜:÷`1ÂÒVýé-{$|ôËÏì{~ެע„üýÄs>_v çüÜËK}ß‚B;çï2–ûz\&R­aiÕ(i’úøÃÒéô¢ý,ü×ö¾kß– xÿ"eRiÎxê‰'I ¡qŠF©í›ÙÏg­3wÀ^;ÄPßr¹!ç é䈈½ß°ñêÙV[ÔiÙ:3×-D»ÅÈÑpèðí‡êz5mª(;êÚzýZû|°ûµó?bˆÐ*­ËºÅÏà¹ã€ 1ñp`î××ñàtÔðÛE¾›ó[C|[8þËÛsû‚ü1žËýëìçÃÝ!þEŸ¸/ 1`Å$²ºµµÙÕ¹½D[¢S'·Í}5²Ë£õy{w—'"/{>—ÏßÅñV‘ïýæñãåçÁ Mvûïtð‘ƒßsëÖK%FD‡²¥X)Žâž=˜uèICúŒâiăGÆ°Ô8žФ<TòlI|¢ Ö_”òªE¢DDÁ€I²#ÄRû¤‡hØ*êÕFGQ°¼è<á‘g‡Õü0’ÖH^DrãÉ*>ÄdFh°›ðØêwëw’;jéJš&µ,‰0”ÖR«pRFâຠ…r/ƆÔ1ä³UÌ‘exríŸU ê¥<4„lfÎVÇ;ë#®Æ˜Ë Œé0Š]®ƣž³Õúm5 ðýPg}à¤Ý”h¯‹[Í1[cØÒ(X{³`5ó–zÞRu˜E›)cË’4YÄ À«)n©MZj5!k“èlìM¼½!ìlõ¼ÝínˆXÌ‹Iì¨ Ûª#£Ø¹Vì¬ ;ËRMq{CÔš£€`ßf|ãóØãk-ffKbÐÊ1†ÄHÓ—«u%¡'´:BK€}Aa‹S,†jôZ[ú§=È—'¨ì(ÏÅ[)Ìz‹’Y­N ¡9ðÆ’^íÝ1 D'íѦÜZð}n|‹x•KÅ79B
+Ã(‚´¶wó…™jí¿ñW«ÕŒÆo¶ÿã©V«óǵJ@rúä)à $I«ñ &ýnï…·)ˆGRIg*é^¬4„ä9?ìEa0“v'¦ú"ؽžni ´¤¢î‡»›î}9˜Üÿ¼ïù-¨7ép *#fÛ7‚ìÞºq$üb÷ÖvFÓ*åß>;˜f|é´3ËÎ$ñBßCëˆN§\€™Lr€Í8Sì~±pöç?{áBðåtú­¿ÿÓO÷¿ûÛbñÔdb`ÇS­ZJ¦ÅeÐEš ÍÅØKIÎ?ñça³’ÂS4²+5åá’ýyaˆc}É„'“~äüøzñƒðù§’ÞœI»á³5±üÉðØvYB¯FhѪ[2ù£4BFZ#å !AÅQåtŒbƒX"‚pLŠ5Ì„”3aB
+()PŠ¢¹À¬.¼UHñ—B˜#Úì¨.%³Q¼E²s£(Œ âiò¼îƒ†þ­È:Úd ´E«)RŽ!Ž –è&fBH-JPˆr¹zWSý›Û¨ßìjúç¾öD_'ãÚ,x·BÜg½]¢§“uw€.*nû´§3ï´gûí…¾®JßÉÑñ´²>K~ ³äë,¹me‡­Úo›qØ EW{ÉÝQôÌ•Ï:_y_ç,-æ(%¸Ú¡à`!
+Ž.ÁÕ%º-p“‚·*ïiϹ-‡½Øgyà°H{ºßzçÕ¶‹ÛýVÔÞˆ­×«õ¸R‹*L˜j 5Bc«ÖꮄÆÄ xkÀTϬ[uô9Åï7²€Ã1¼%jb&D”G¨RP[ 
+AE1¤®„°j˜¨„ðbÍ…‘Blaµ†¸ 0f3åQ|~ÿ RŸ R‰@먊#)Pφd•PC9`Ã+ÊaÎùõ¾¥&¤Q¥o
+‚w±ÒãÜ,ëb7hËsÃ9ñø÷_ó´Ð:NP*ã³Ýæî¾Ìro|61Šìþî6µž’™H3®Ò‘˜FGipU³‰Òcr™×±…Ïù3i:å?Ë:Z#,\åaÓBê4›:šÉ»û¾‘’qù/ÞÞyûãþ Ê' Æ›·zYÑûÓSß#f˜Û$RÿjoÓ$;ôÉ_~ܽ™2©Ön\küû—Cë“GS½‚èä2ÇyÆõèùñ¿•ÌxSïì?Ï qŒ—I;Æ™L{˜ÔÉîM„V‡ÑˆÌ¤ANôàRÌ$F‡ÕH-bžíyõÕ‹èô˜º8¦*U•
+!ÃE{™©ªbØZÿó®Õà„Å ^
+èå ͤ‡Þ@!˜†®¦ÔÇ_Ââï®S~P±0/‘ÂpqnÊpÕBP]G%_IÚ(J²K®CŸi~‚Í„Ê’Ãjùm]:„DÆU>bÚ¶^ebž!'kpMµ‰Z;ÞÇ$®5®}ñêÊü¥’”ª0­Z¸¼&ÿAéâòåJÿùéŠÌ¤ªp¡²p•ÎN­ÌM®øý°Q¯¦5z¢s˪/.˜ö71\ƒâFg§z¬[e`ŸÑ#Úol­Š_7,N–K×Wä&×ÂÄ–k>…É’ÔdEâZiá&R¸¨OM=½©ÍÜXû³à6Š¡˜úJj5C£Üž÷ò.Â=á(pÅ
+{£B¯8wL ï•øÁ˜è½ûY_×Ëf­†ÖÓ&
+5­¯E~ùk×gìqeàÞ]ohøÅû3vØó—Úß Õ}àQGgg»gf‰Ñ“ãßÔãU8ôY 7=u¥_„ÈËf³O:¿:þãXü²D!Ò¸¡!(ÅY¢bßËÖ°à„D„î”|,*öCx-Wò|°.ÎIHÇ$ñä÷¢ü)AtÿT»žD‚Öú:£æÊÕ!>âá#‡?ù«=àß½m ÉÒ+)DˢЮ5,ÅjÔ¼ÅÅCbÄ Ïö=¶F¢ýáp·õFïÉÑ7Á`/¼PB"I :ýÓêÔdÓZ£›¾Ò‘FæÞÚ¹£–QY´Ž@– pU¹‘…Þùñ¹3ŽŠüY‚dœ ;Š]€0ðÄùñ¿Ï+DɉØ
+Ñ>‘÷ÌÎ8@†±°sòýWXSñYXÌP‹ª{dz<Š<L$¤2>,í«L–åýpHƤ'ðLˆÊÀ¶ï'ä‘
+ Hþ]&ãg–º\:H@pä*èI“>ýÌëþq¢öäNã‹5˜!1 ¦)hiB­Ãh« (LC18É41L×ýÙ] ¿ëêÝ[îÑÑu40·9élSœSNkÆiÉ:Í9Î2ϵ)Ž6Èñ8·9ëܜ豊ÜÆ9—Eè³&À
+—*R7péjÙütÙæó¾záÆyª4w‘Í_Yÿ ¥\]‘Ÿ*‰}Hy_ªaT(i@aÉà«Þû髳üñ(6Yî‡x®ØHÎ HÂ[²èâg¹?Ü´¶âF-›¼ÅžÛ¬ùôoß¾?Ó7'þø–k»­~©;wîùþÈ’[œèŒ
+ÇDÁ#‘A82ÃÝßeÒÑZH7-ͬ¯«Ç¤G ÉçóO:ƒ¿:¾<róÙGy}ÿ÷€ŽZ’†Ül2Þ¹;Äc´G–<Bx?¼ßï]®4ŒŠw áþ¨04wÿÍ3¡æýªÝ9ÿýäüÞº:–Ä1%LŒêÌé ÞãD`Œ7,¹?ùÔ
+ìݱgHV‹1z?Ô½kŽwEăá9X'w>s|,手½Ò\¿(¼ŽvýóÞ©íÛš1þLxUuS=úáG=‘hó’àýûçö=ß2HŠtÇ07´š çõj$æˆ
+ q0>$ˆCáH?(=~òüø¯šåûb’wv¦'!y¢¼G ÄDøi÷&æ„¥{:[HºEt:jýn[yvyl„Ö¦ÇЬ_›öážõâ#Lü4)#‰ &á¹ ™ Êh%PD«Y2ützúVD§?:ÁÛlª-¥Ô•‚@ º--PDÍŽV¡ÑÔ ôžSèù Ú×~È‚œfH^ÙÞ
+N›Šp¶‚²žŽyK(Å*ÛÛRîv°AÌÙ&»mP’Ó’pXŠÌàÌ`B9.ËCKX“([Ê×´À
+>"{Úå^kÌÙP°7†í–‹ßièÛb|¶7âd5ùoöË-¸‰ë ÀLK†ìYÒjï÷]ɶ° Ô-¤†&
+mÄšt ‰MÀÙ–Àæâ@’R\dYÌP2-}h:ä ÷LK›LfhÊ„I(Ph¬Ë®´ºË²,m1Óé‹é¸%ÓÎüÚ9çEûïîÎ÷ý`G–P$ı@þ(’Äi’àhÌ,óÌüïª']oE’#@;»ôi‡!á “]âCJeLB€Œ-Á$åÀ’K4#©øHíÄ2ûðÄ!:îÒå;5’‡‹wSê/àÈA¡f)Ë3¢ CP¬øå2Á³‚Û[MWUB‚
+ñ¨hªQ‰l”Cµ
+l9´ýÚ•Í‹ç—‘Å°S®]o ù~lRwM˜Î![8Ú Ö‹ö‚µ`é°¯bi
+Ghš†hRsø—5²²]ÖGuŠÔ ¼õÙ'õmï¼`o_tâIJÛ_Zåච¿
+©E 7º™T<"YjˆH{d‹ä·…€*€ %YyÛèòk¶”pð]°yÿzu=GhÞˆ£Ü3축«NÜÅ'ìúÌÞï¤U>e‡h&j)q7–²³¹Æ‘N6éÐ&=º!‘ÚÏN3ÕÍÄìÈ° váNȈszÚÉý¥•g¹~‰Y'0 ‚²$ü\ £ÖLÁé[B²‹i¸yw|MU´~”û@$€$ŒÅC¸ÿ8þcÄ,‹–ª¤e^¶À·õ;Ÿl\phE©m®EE728KãCÑàl§I
+LM ¶æ[ȯkÙÛm<àu­vCw|c£á“.mÊ¡Qð°“Lt!Q'qãQÕIåÌxõ“:hŒ:±”›òµi†œÈßìÄæP˜€sFáHLZB¬€0ÁŒ<=»²dÐŽæ;4y—øi›f;e3#>eÔTzˆÔ³”( æ•s ÷ìFIÚ®K:˜ŒÛrÝ¿—w²nx¨¿žM»Vu!—›ñÕÏb¸gLf5¡ÓŽí,Ïj¯&ºh¤QÎë†Îg½ZµçiµoZîtÁH‘ñÂÙ¾©¥Þ‹çú loaöLAº¯ žŠõ0wMb)–@øï–ÐG¬å¿wNó}„ìûñtª›fOµ!ïEÕúÔ¥‚] %WÌ'b— ùÞÉÊÙ‚‘óÅ `)ªSO?:k:…¨S¥³ÂŸ3Ò0 –$ÑJqÖõ?Û‚r“¬X|¾–Pd‹pkLÙ+-!©9mü‡¿åè‡Õ•¥ß8öþë¡À;a¹öêß-ëjfþ”£Ì„Á(RÜÊeeÇ¿)KïJEjf*
+„†âmÆÆi‘ÒðЪ´¼'˜Ð´}èCHi 5P{æîw{ÛÜãåÅnÝDJúë×Õ}º÷è?çüß÷¿ …F¾©]…ÒzïKpjÌ#H˜fš_ÊÒúñÄàÊÄEJ»hŠ¯PGt3}‹£" µ~*1²$Ò_ÿeA‘Ã'Ž˜Áìùâvææ(˜(Ž(¹¹1¿$—s\¥ WH‘êûãõ7nzÇÆ8®yh`OÁj'lÊ´‘$S,[LftàÌÎm佇•
+ᇾ˜Ú(„TµŠÜóõ·pèÍc [m˜ÅlÈÎͧœŽ5wîÜÑ´Ä2Î9ÉÌãø<ý•Š¹Ú~VHæâÑÔ¬ìe7…3LS²¹ÔzãVÝœŠŒ?œÅº(Ôpa"ƒÝ\0mçËÐØqAª
+‡
+I¶oˆ¡²óXÂNšM?¯V‡#‡nß«þþþ×Y©| Ž¿°Írk´îÁ_D:%Kå‚X.©uç±ÃÁÚ#úãSÛC¡
+…o‘eÏB×7)Rƒ"»Âã¹|AOVÙXà@†úÞÚº‰vX͈q%ƒß;ÿZ(RsøðÖ¶÷Æ+øpETm‘Õ:N¬Yèc±: "á—×JB£,6Í=µR–]‚àæ97¨ÿã]ð]ù_ÛHH
+T'…ÿ6Z^º>‡ÄíºÜaÄOìõ³öøóL<Õ™¥©TlGj#Ñžìd7”lCSg°é.2Ùƒ+ATwôZ<Ýe[MÕÔ+Ïbv³©µ’
+ÉBYÖò*™‹éQ‹"œßEÒ×p¥?SøÕm(GºH4î¦s‰$pÒãŽÚƒØø‡púªI„´KOÅû¡©A6Ù¯_,™ì7D?
+˜bÝP´R[±ØiÛ,÷Ïbñ.ƒ@ä.ç|ç(DûÚJ1™°5ÏâÜI²¿¯+¼ß“m[*œf·ç€Nf Ù*3x˜b b“çÐθÇÌ0œKšQ®²cy¡Æ:Ý™±{a‚
+ÒÚ_‰M©GxªŠ¬Ñ4ÈЂðd=ÑFÌ·ž©.Ü‚X'š’­™S]L¸=ÿ~ô[OëY#+L’Ù´qC]ïaguéáåÚ°Në7¤/¯Ð®š’ƒúɾ¬é¾ ¥üT?pC¢7 P^ûB’X™X®õÁ“½è§–×uû7“í¯?5Ù÷õG#˜ÖoŽ,­~±`&‹™¦™µ ål,šï0ëv™Æ~mÒ>^® fjƒÓ™‹e#ê•¥Ú 1sùS—u©‰Îc,N›<Æõ ÏÚ÷rü©Zæü×
+šoýÓ]û²EO8”KAð«µE·ïví ‹ß;³VÓ”Ú`&¡%ó󾺷#8f 1û7½:߀Z”´m¯ r^ j1PmŸêz£ŽH¬‰\~ðÆË5ÚêrèÀþåwF<œàe‚»8¹á둃 LZ87êŠHp¶¡†ÚU(°øEE'ÜÎòN1îšê犂4öñ,ð¸Z€LÁŠc óà€ïÎ?ùqËüíR+w±Á¶×âv= º6l2ÀFP4ØjÔIIêPAÄO§üp¬Žùñ©j$únV¨–» Ñ·)¥WÞ3<|Ç8ìмQY<OóœÊ%ÀÔD‘8…C†€VcôJ=} Âüå–Ê´½&ÝXsTHžêq÷2ÙYÉ8ª8gµì^(±[ã®r ‘§ ùA#ÓR ·5겊®¥‚k™è­½Ë$÷ÒÛ*8æ‡ìe㮪xcEhÛbpS¾*ÞYƸ‹¿nY8°e^ý"¬Ñ(ä`!àRª„"HJ«£aC5‰ù9üe'’ò“É^MºŸ ä> Tì8‘ê#’=è¤éŽÔµ®"³)›‚
+;D¡Qæ=RÀËœ¼°ûäÉf½j]]éÍ›»™±v™ë
+ŒC怎<CT š IBOä_ðXþ¢¨ß8Ñ«ŠƒAµoÒ™q²RŽê'zðÌQuê]øn7Ò¿%oUI¡^c ‚™h Ө
+Òk±
+®Å1§5i·FKDOç[â2ÔTù”"?hdº*ä,9—„]WuÔ±<ÚøQ•nªJ´¬ƒæ;ËÆÝ‹âÎ…@#aÇ
+ÁþRÚþBªÁši.‹û–þËYÑÿÚ¼5¥ˆ^=K‹Ò„Š0’ZŒR«pà pt]z©Y{ï9ršŽœPK‡Õ‰#Ô£^t¢gÎdû'qL; ó~z÷Æüb„(‚ ."Hµ2b8Mg•ª©J-}w'‘927ÙK+½jùÞ±NoÄ,ÀBkaÀ†‹6¨¶8…pLGæ`Ž01óΟè©ô~rsù3Vcö©­ÅzçrÇ`¥{Ƥë9^$ö¨@…%Q©cyÉ£¹±ÃšÌqŠë¥»ë
+–à8‰¢8ª7š³hßW÷<?\~V9Ÿ§ SÀ!ÉáÙÊEjÊA!?1Ì ?—7~6+3”“œ“0*WóçžQþ¦ŽÌT³”ìþ_ uf¼€Ò¸Ç" >l/ˆ]Ð ŸNùfcê9y(EëÔï8ñÄДK3¥Alº421ˆÆ†f>‚Ògó&>ÉO}<“9g]½Ò„«1 ]„â‹,¦[·=RÄË
+ à
+ëÚmã½¢Ü*òÓƲö'cÝ$ç­¼C\AÖÅ1û×ÔS…”Ec0µ~ýúp8üMJNLL|W1ý=yd2åÿQž4üôéÓŒéqYKèYW¯¹l£Äu„8‡À»%Ñ J–@*µ
+,vÊüÛBðÀýfìáIÞ)HpS8ÁÅ'ðÓ×ÿIŠalrÈÎ3>™w‹œö†j
+W“@¬8±ÞZx÷ðÂT7”é#â½ÈT5’øûåÅuÆñ> "ÂÅx½ÞÙ™ûÌŽ×ë6¾°Æn!-4¥ˆ¤8¾ìÝw0w!°^{m %´RÚTÍC
+Œ¹Y”¨@”TQûPUjÅÁÞËììÌÎÞ׬§ÇAB}q%"£¾pô=œ9}óÍÿ|ÿßyŸN¿ÜKtnÈ®0"0n€qŽ¡p#Žs(LŽãKˆ?m.ò5Õ†[Ö¤-Õ1Ke̾DvVò–RÑYžì¨–­eQk]ØY'¶ÕJ¶%ñ¦ò‰ÆŠ¤¥*l7=±Ñç423á¬Ø¿<PÆæ%ÑƪdóÒŒm™ÐVÇ[Má–¥qs]Ò\#7UÉ–ªˆÓ”±›Â¶e^ëj¿}µÜº<ÞZÏ[jï5Öv¯1Õ‘AP–é’žC—²°ú§Ù¾ =8B)yúXÑMN2ÓéG:).UbŠ R_÷Ìy£JS€-dq´/Ba´˜(.c¡?ïF3¿ÒË$v\P¥ÜÆó6]¥_¨…7,Zèí/xÔgøîDÑÁ–Ëp‹#[ªUa7;áÂÒÄõs•žÔäàÅ,”Óû:*Ÿ.‰¹s¦Í§O;qª0ÝÏÉd%ÝêÌ
+CñîÝ+!‹à=$÷†BVAnö‹Ž1Þá ˜#’#ìs„‚ à>xìÀk1—% JWÀú·—þû~»w¬Uä{Ä@G`¼Y|àöÉPƒÜð:C¡°ùñ£›iØHÓZèÇïß¿ÿØ+ŸsÈ3ñ‰‰”’Q¥§&&[·ü…òi‚Ö#…¯¾H
+v)ô.ï·C­>¡c¦ÜY|~ ¥N!
+&µ:˜ (Ö“0ŽTp‡ŽWà/qƒªä
+u6›ÿxÌ’¾
+7”yÚ¯-Λ
+?è–øN‘í
+ýQ©t]Ð6#ÑÁ;1ŠPíÞ±öÓ»G@åØ'7› %i‘
+s%a|°g7ËH‚MŒôýÏôõ‡Ýß#þEØî˜Ô»$C‚=Àf#ý7oôTS¥ jÄMÆÆ.–0$›Éd€V>‰ÇE€zE9Ÿ+‹‹÷ïýãÙÕ&ƒ AêÑ_½»M#¬=ê«æ¬ñ‘©*l 'cƒ‘po8БˆÙÙP;< °ÁàÛm¼p"Ì L\Û×Pƒ£X¢‡M´ñõ7*ÿúÏn^ê‹p'¬oÖO^ß/ÅNòÒá@¸°M„í»‹ ô,·žP 7{GâŽKì
+œ u”ÄAèä"-¼Ð‡†x¶åÞº|ù%ŠY¸Õ.&Ú?žÙÏJ£¾~»~µ%Â÷‰b|¼(Rˆ‰p=ÛÉq’~ÓÎr<zü†¿5¢”Å(ÜL©ÿ4T—q¬~PôaÉQ$ë†2xØË:0ù4œóÁ‚»êÂϨ-k Ô€áN¡ ÷ÕëÉRÑÔd_cÝŸlÌô4:×%­“lÍ1ëæèáçã‡Ö§:š€FEë†o\FŸä#Épdž˜ucª³!ݺ&ÛZ—k¯Ì=ÔðÐö½_ÿdÝKuF M¢$AP4Wúºµ¨Ú¢­Ø·‰X°3
+Ì`dôxsü»cHðRuèìó-›š˜©Ñ W[ÌI§!}ΞÁ2Îu‰(;ªË¸´‹ŽŠ‚’=hÞ‹¦¼°4ªÉ ëó`¸4áÅ > Tn g]ú ¯V×@
+£^OR•FÊ@"ê+ǙĬZžYY×$§*Òsšì$,ûU
+8µÁ×åcŸ~Ùqîå]¼ó³ãŽ¯¥½y‘A
+§Üj*ÑFi4Ú‹—÷„pìQëˆ}‹wí"× ÆS
+¶m‰Ùk଴oØj¤ŽÚ‡.£ß¯³:kƒŽJGMÔ±3b«QZÒï:é¬õq[u¨¹êFcùÁ-F EnÅuÄ`уXÓÆíámôÍÞ"eL£Ô!Mx]XüU"5\ aêkÆ¥ÁÜo¢‘å‘‚à68IÅ'Éä
+l»Ø <³ê<ˆÍÐM~?4Bp»ŸëæîŸÃr·Äu
+J¡£§ TÉ‘ºœÏßÉT2àNnAÛÿ<ý³ŠÒ2]NfØL¯?߶Vœ.P=Ú°]ùŒ½‹«óY¡Yêæ$óÙe4yC{{£
+¨ã!V»ÏåéõQ}ˆ]Gº¬ m ¤H_ Èßß|Ž€Qm×OàCý-Þ{­áPŸ$—@¸v¤l¡ÈJ+ýî¹j¯ÿ%¯ïE^ì8÷¸‰ÈÔk‰/ï#§Éág§</Œ\Á n –(vÀWðw¥Érÿœ»©„ɧƒõì—ës×Àó!mb[’õØ÷®Þ¶°16ø‰Í+- „¤ÐhIL(à‡Þ¶‘Ág $Ä–dL[gÒiË@ú¡M
+Æ B3)´é´™¡iJ ÅØ’vWû´’¬‡±zùòÁ30Ìt¸s>ܙݑÎì=÷ü~GOÁ0<>6 Td.óØFʺ—ËÞË梂TUQf¦LZµ‘å÷toˆI= +L
+Ba"Q-z…¼û9ýŸz—d‰ùãê¹*1DEý0 þ7ÙHÒGÍú•³^UƧÏ“^ˆÜ©þdå|?”9j±á=i€d2ƒÁu/X øPeb@óçE|Ô'63¦‘•âfT¡Ù´¬è®×”>¦œ{ÏøŸ6÷’缋SýyÒûÊø€.ã+Íú©Œ_) h²ÃxÂ+oæN!àiÄ%†qz€Ìù¡´Wùåd]µz VL¢BY´¨¼mSÞÌÇdfì[¹ËEñ±Â̤b~”X0å'å³#‹Ó£E@HRd©ósc…ÙÑüÙÉÅ™óÏä.D'+woÄ1´ØˆcíÛ‹¤‹ªÔ¨:{¿6\ø]s¾‰R˜ŠÂ*Ú’BkÛZ•8å®<‘ù]^n OOœ>PÞTJ8·*¹) *¹•0²à<s@cÒçÔ³ãùñÏ‘æfÂÌ*W›ð«ŸïŠ$\ Ù
+쎵ƒñ-*:3½Ów=’ø.jÚò‚ ‡—Qdb¼%´&ÅwÎöï¯éaµ+é;²öÂØyñ hÚBØʆ젽ópžwpœ@„g{@£‹E=Áw€vtØV›1‚BPiKË–„fB€š×ÃX©l|Û7½E J‚tjbýjâŸ7{BÁædüìƉ8w
+öí{¾TG`J’BÔz¼¨ªT÷˳[è°;±zù'&’„zÝ“¿gHF…¦ÝQ¡+z˜˹hÖÁEö-ô™@¯î½#8'/º¶3ð°LÃ#Ýi×Ù_¿¬Ã
+2éŠwn¯ÉLÑζÖÕ$„*äâ¥=tà`4Üó°U„eÑ„$Ìt‚
+<êþ!vMÓîé;íåæÅzS+ÞÙBN "€îÒï\©…`-†ëKt:
+á-ËzµédnP›ú)Æù¿÷§üpbPž:EÞ:^Ññ<º\«úê=!Ëù)ÁÞ<jlÄ -¢4bÏXŒòß:—
+VéI $Wnßff˜c,Ý)ò.‘·Gøn–Ûyýú®m›-¯n1ïÛæÛ·PX¦C‰É+/…èæ¸àfŽ;S:TIjjцõšÜØɨàà'ú鰓ã‡ØVÐÉì;ÂNÛÐö…bÌvë?½Õ*=FaDÐÛGßžK=jlÿŸ®{¹{Ÿ]ýG´F=‚«e-ræô^6ê"66؈bÚ9¾›¡íßú
+h€îQöéG¥3&õ]Ã@S68²éP:O‹4”eLG¢gÉÉ·Ì %VÌk1É`!qÌ„Ã::ßh˺ô¦-ÕkRú`ð†Ð îÑG¬‘sâ?)h•!S WßÑG‡°X<Û§ ŸÌˆ¾gï[Ñ»f‘Ž2›ñçiË‚Š« ºøâÌͯ§†Ócײ×uÿ¹jgÇÒScèÌUbzì¹…ÒÿQ‘º?øb‚5z<Ó¬ÉÙTh’jˆ]͈ßÎôý,ccq®E—ŸG¡7ÎC›ìI¥S„VORVÊÒÙ”¿¥®kf.ãɱ% í7öaflT3sE“ÕÏ §%nþÛYç"=n°^À „Ì!yçÍ°¿M–Ý@$꾇ÿôRÐbTX[…ûÀ©ÐÏl¨¥,ः+K–½ÚTW^j£
+‡(
+§7­K»w«Yâ‹x¶÷{AEàÉ;^ð‚àxOóëµ&=f&q…^ZŸJ¦fggÿËÖÙÙD29ׯÊÿSæs‹ÅR_
+I8)=?4ØÖ±™&ó­ÚiŸïVä–ÇE1Ñ×%‹¬(íå9V•;UÅ+ÈQa
+†ºÉ3ákQmA¹k‚÷NÈs öÞÈî"XGeåà&‚G%”¦È•b.$µÝëldëew-P‘y™#˳¦" RL•ÌV¨L¥ÄØ¿ðÚ}ÚUb #n[ÈUqW(Žª€³Ni©bVE\Õ’§2¼ÅYj[`«ÁâÞꩽåOýCžÑ
+i †à#Y…J•ZÔK‰Š”Œñîα;{Ÿöô9HiÿA Š”§Ÿfæ4ï½ßoÞû}??±±"Ü°&~dß1ñ£í?XgZƒê«NW¢¥iU”AË^ª0ÿã $2d‰÷’€=Ò#Úùs
+ù¼:Ñ«Î Bé!mjP•ôjb8æÕ…½¨è},ÄÎC©ÓK¦Û‹‹´*DϨ˜Ârq«Û–óRmËV¥¤´8Q„"›ú7ö¨Ø–
+ÃTë5еæÂù!clX—(2¬õ)“ƒXz
+˺gîÈ×ÔÀΑZkƒµaWyÖ¹Vv”ËŽÚ”}SÝvmßú=f¥EŠ°b-jQRf#f6¢êŸ¾Ýÿ9šô ’‡ûiî¬"}aep@ $íA3Ib
+ŸƒƒÃºÇÑHÖ‹,xñéfÃæÂ¥f%HSÏu ^ta@7éÈ3SZjiƒeÎì5ß6¥Fj/"¬z†ÐSuk LtŽÌõèc#úä¨><¨–ÑÔ “îקϪç=ºh¿týDʃe‡±pŸ
+
+lHìôûXŸïh0Rq HþA±ç\Í{Bϳ >催§‡QÏà(J`hIñª™{÷s¹œü“<ºæ>{ø¦ýß–ÉdþÛYÓɸ¾öÊ«”ž¦uƒ»ê sB§ŸoK$›ø
+ ‰¸8®¼üðïGJkÐÙ¶TYbÒ aæ•h  Ó¯~½—‚)R[pqd?Ïw#´]~òyÛ‚œ[º€ ƒ.8 Àµï5íÚ¾jÛV3ƒZwï(å¢õ \R éãÿlK5HeŽP¿{û{82¡½ÁXÞ爆ÛE®9àwÀú¹&NhúªiT¿“Žˆ"â;%á„P$ðšÈµÏÞmy³»¡´b”0…”tK±±}%ñÖiçºHÚ\Ç–³"ÞTý¹¬ü¯=s™û¢V_\ÊSÍ•qvm̾.áØ"9wFZÊ$Çj@&gÅ{å­C¥¿Ý[ܳ<ZÉø6¤‹Å¶‰£uiG5×T’vÕ>{G¾žo¬»ª¸æ*ÞUa×%Ù5Ivu¬quÖUš`­A¶ì—ß-*1iP[$BcÂ1±T1ÔåW Y/ăCæÔ€1:‚'‡
+yŽ{LI/êA“ˆÃ¸ØoÊ UxP›Ä‡Ä=x¸ö#
+âý[/[HÅM˜^c¦‰¼òbøŸ£td¢/¿'//@[:?žÿ´h$ý®â£ 6ŠÒÐJʈoŸP¤ÆUò˜báÚsòM²·UUh\–OkŒZékþÃ~½Ç6uÝq
+¢J;~%qâ
+GP´5üéÃ=aÁÍ…Árê‹Èö¾¾Ÿ˜IˆBD›ˆÒ`% ÄŒ¡&ƒöµ_”}óà„ yY¡™ã’ä–$ïœ@þK‘y5"ño1ì‘Ù˜ÚÁµ{{)Xߌ
+éô-'ÈÐÔdòÙ¥úâ>²Ùl&“sÚ§ý„"H FL¨…†ÐàȾ
+œŸÃû¢‰À½O|^]e¦LHKÑ6àm8qjó† gUÊQ6å­:ªApL¶UË®uRóúI×æ9Š$­FR®šûöG+¨ VÔ
+VE\Gáz³ÂQ„@ #YŒaÅeÓãf=e¿¡}p_CÚQ™j^—hªYÉøªÍõÏüAi‹{ªîà-d[꧛·¦ì[@‰-å1G½àiíuiGy²¹4í¬ùÛ¡í{+Ð2Ô¶±*´ÀXf‰â"XîeB¾@'û5Ê NéWeû´ÉËdì¢Ø
+™ÿC}7qßq
+\/¸lQ²ˆâÀMçµ+û¶û_÷ºÏŽíÑ¡Ù…„†DçÎí 0],wìâ…Wµ gþõoØ eÚeá$èè¡(z@K•Ä^ðZ=i
+ «(H –ÍFÕ3_¤BöÚ„·>j¯—m  âkæ~ÿß*ì2,Ú[CÄÚ±4Ç›^h¯|°2aoµl«ô˜Wm¬£îÜž»Ê\¥TA(FjA„Qê¬V#1ã5Ü4$§ˆ•á¬Å¡\a—ÆÉ¥ ìaˆLâ± (ü.$R¼2¥FÖ‡‡Ô§w•éTå$Ž îF¥amp”zû]%Š«ÁÌÀ«
+Ý9Sê2}íJwÅÒ;–•šÒÊ“Êø !WÏ’
+°“M#d! æDÑÒd!ðÐ]-/tÐØUæûÁÉ ´ïÎ?zëªr(ŒÀ €dblü‹H]‰'žvȯ™#­‘Õ$hÚjbEÄÊÒrt
+Ü‘4w]M<˜f»Æ'_À g5êjJ]ºø}šm¾»Ÿù|Ý­8¡&P¥Ë Õ}ý[ï|r”ûC¬›gŽ²´Oì<oy\ÎËN‚aËí¹˜Ë‘M$Ë—D[à¾ÇçhÆZ-ªÑ붵P›Œ„Ë  µK?}z%ÎÇ…Ü!®ƒhº'Ät-„œë¯¸ª/öDKälL¨C’ߘ»aݵ½UB:¢…P¢¤pí6ŠüàåJÁk”\µ’ݳ×Ên“à¨Yô6HÎFÑÞ²èl:Ü5_ÆÊÚÔHÒÚ@{wïß ÅqA ”(Té1
+Op%ï3–¿ÕRùþ®Ê[¯î8Zç½-¢§A¶TÊÎzÞµ™÷šyŸñ©ßÈ-ÙÚ(ÛkEW5ï©e½fÎÝÈ9Á”«ŠX áî¦ÞMón3ë3I¶ QûÆ°¯yÉUñw{ÃÉ­e5ŠRÕj` ‚ɲÜÜÓ/~v²0q&?yF!¢£øÒˆêa‰ —%†Š§sS“:yìYiê›à›åQ":Uðñ
+\Ÿ¶Ö€¥Ã¨AaÍÆp…W©s_0UÎ\nc‚>‰ïâY'xƒ€âž´:dÆË îûœ…-c ó½AÚAK€aÎûóýï¼ým‚ȇT˜–
+D¸PÂ]·@lž&+é>g´}£ÒÞøÐ'ú¿jÄݘl¯Hùjß=ìØ_a„aÀ#fȳ@6E]vÍuoxÁ´¦áÔEmjT'\4Š¯ØÄakòœNÔ(£Z~TÿYJQ.¬ ž/:Td3f©õy¹hñ6;z°jå;Ý«?8…V£ÖŽmÙì8`†YüÑ*Ïf3jRå ˜ÊZ¼3ÝýÚÒM鋪ð¨:>dH›¤—Ôá!cì‹84EFàø”†#†È°N2D.å1ç¬ïþ*8F@FÌX¬‚òK7ä¼?nVnä(“k“w4ñ (3‰d&Ö,ÖIda
+–o}m©*ˆÝX‘¼¥Z¸©OL>’¹›ýëPš¥±X`kasqöŸ_†’og)· ± H™\¡üB¹nA-z#Øؽ1uêƪPÊ/5Ë¥#áÎc‰[&åWZeâ+Ák%[V²Á`V[­úÖÅžeCmL0¢ÛEª[š[zÑ$ò Ÿ-Ràè× *Ro®'Îœ{ø4ë¦Dÿ½Nt’Žl5™m&Äl„ÜŸ’H)ñLDÉ<ˆÚh>IÞ/‰ò?JP$GïÝûŠ¢F½C1`Ý+—[Eú¼0‹‹óÏ
+¡®àoˆ!8Ž
+çZrŽõmý뇤 uKR7Cõ ÜQ†ñ,×uyÉóÆOöc6@ÇO왥O†¥^aöH˜;¢éÞ@Vðƒ½ µˆ“%Ïö0AR
+û¾ÅôobðªP 2;À#ˆ¤3 r™Ø$ãaO•ì)“Õ"Q›è,M‘5@œ{ïÝ*¶sÄ&ª³¦6£°Þj.6[õo~§4BÖG‰zwþåPy³¸½y¶ØôŒS'šÁ OE6Q>×íevlm)¬+€×¶ä©fÜô¢
+jg5…èêC¦_µÏak-+@´:+l3¡F­¶Pƒî.(»¼£,ãq2=5r 2Òa—ˆ©ÏÅw¥Ü• ž­Œ»X&íI5ßáû] ^“
+Ù_Ëû\ ˆ“žª‡CŸ'o9ç­b¼Ûîw5*„3Üi û«ç»@GˆZ
+ ÁS)y+¿ï®‹øëD¼"N”¥{¬³8î®Xð7ÅȦ'Ê
+ó¬ˆÅ”o€Uç›1™tŸËÞš;6Wcf“ƒá+O”G=Õ,Q%\¡žæ±½.¦Ë²@ÿa¿\cÚ:Ï8mR»”¬ àÛ¹ß||ÀƒccLmBצKRu%MÛ\HCÀÆÇ6`’¦Iªli£B€¬­¦5ª¶*š&–A¹­™:Mš¦hŸÚªë–æÂÍ>ö9Ç׃pöÒ~˜”©R‘˜²{õÊ:::—×çÑûÿý„ µì·Eh[·ÐVí¬+ @÷eÁp} c´b3E¨FòM,I¡zX”ò?Ø숄*Å.[ÚkÏz=qΖ7j¸Õ\ÿn£e‡vÁŒµÛYf<]‡þµÉ&jS«¬ø•+ Ä#P“ô9€€ªI´»ÀL\i_e²­*¬ÿW½Ýn Q3 c%¤Š@!VÞÈ@= †‰f§ÂmŒƒ[¸ŠAï{ô•}hÊœ¸b2´qÎï™y½h¶cýŸ[7ì³8B24L"”©P¢ ÖFü±ÃÌ÷#³'U¹AFêÕ(ƒêù<ršM½[”ëC¾ÕFÎ1Ò)íü{„8H_çÌ»ËàRB…êóvÛ gû¨™¾<à!JòA³µÅiŒ¥QEŠËt+,E"E%³uí÷þqÜ”ÔeÎ’™>£Üg’{ðÙ>ha‘{tà!Ù^øIîší‡’g…$3hˆÿ\}¾*c‹Ôx«.2“æuÅÐí rý1ùw«Ò—×Ì}¬^¸Š)— €Ì]D—Jÿ…ËÐì%dþŠV¹¤™»°R¹ñ¸0Š÷ÑÓCªÙáÅ FÞ,t#šb\[
+iî²Ý½T07VøáqÊØ0¬Âb\3>ªUF
+–ËF”KkfG!ù"®<1ÿ þÓfÑ€¡Pz­ »sϲ+c2ê]ŒÍ%æ?u$·% Ä,h!}fŽïó^Ýr"pyøw5BÀ´Q[lQ›´zœ¢ iš„Uª×^~åÈ‚8ûoê~}üyx<
+‰eb&Ñ-ñ‘
+F `ž¬V_k ÅŽšqÎ1ã]›ð—ï+µXÅq3N~²§2wÀ#sõÊ~·äß
+ÇšÄHô‡½Ûèä +ô2éA4; ;ËdOc¹Ó¦o³‘Ì)Kæ$6Ëž‚å=ÎüY¯u¤ƒ¸yT/÷ÐÑA$yNõ«½…8V
+,|·J¶Â‚z7•Ô”Ò(n&4«ÃνJdNëå:9 “άÎô«3}¨ÔÇŠý%™l¦—ûàìm¶_+Àé~8Ñ ý¥ŸþÅž|’ÄiÊ€rhV¿´ž½ÿkhá¥ÜÈO=6?†äF`åºNÍ[²
+W (%5Ï»ò¢CÐÂ%øãžï[p‹ Ïßó’V¹ /—Ì]|RÃæ®â©+«”±'ç‡WŒ7»$f%P3ë÷ì*Ÿœòƒvr*K–}|8í¤(‚{D#ûEž£aïúâV¨i› #À0p‚Ä`„µ!ø5›Û ‘4L¥Iâ÷##ÊHÇÂ×ãQðþzÌɹYE©­¯§T¨•`Õ†¶’¡6Ñ4¢Ök´˜g£á7c¯Æ¤ 8½7%,rszÂ+ÆB¢°X¦H´#.t/ø¦;¢“­’”bo|#¥
+Œï»wçÈæ§Y
+31$f@¡¡ß6§2‡bSGDþ0põÿ¶D£ÞD4a>É]Ú^RlÄÀþ£( WiHÊ#ï{Šbû*“ŽØAgÄWívñ!‡à«\äMëSÁš´¿â‘cè;Ú…@A XI†}ÞäɵÚr-Ž¤¯"ÉÕ‚ ²íŽ´QEÄ ØH®Í ”¥‚Δ¯:ÕR#ûë²\,—ÂöS Ê`+Ñ4¾¥”LRÞ
+Éç”|®É–"‡~u1ÉR¨)¤Î7Zg¸ÊT¨xªÃ]ù/öë<6ŠëŽx© ÀÇ^sÏìÌìa¯OÖÇ®íõ‰/( ‚Ò(„$mÃzwöðúÀØ„B ÁÆЄ&-U$h«)>j” ”HUª¶©H)hÒ{wgwfïµ×öô¹H‘Ú
+©–Ñ?úô4Œ4ûÞìïé÷ý ë”ÈÃ*åo·
+,éSÌÁ
+B_ÿ¥*õë4éƒ4iIþ
+ž¿¼&ui½xõ±½µ8KÁ„^l(@ÕÂ$I@Í&&ú“Lé=YQC£ò žøêji$1–)MÀK—2æ&×IÐÂ/Ö¦®Ê'Œ$"Ó¨ 4 LÿìÇ{‚ÞŽ¨Ð3}﹕ö½©Ùç„P'ïíõ{»@äyGX8ö×OûnÉf X‹dé%K*Ô„ª¾¬øíoµ¼ÿ„ýXñE † IÜb±,,-¥R©………û™»¸¸øÅõÿã_Æ‚t¼¿ʼn<œÁry>ÒÔ¾“h (4C±z4KOe±T.«Ÿy¦à“û§îÙÄp»vDÛ¬0àòA±V+õxþÅ°ÐéŸõ„EýÙiG<Öã÷[ÿvïèØxË…w¾áóN:Àñðó«µnH<pwÊÑ{Ш'ÀWI).Ç ˜R¥“˜JOdïl.¿}Çí›õáoûýxå§ùLoP|‰=ïõ»îÍr‰TÛÍÎÇ7£j¬„@åRG+dÜwê÷Û#£—\z¦!ÖÞš?3Ó»^GÀ烽Ÿý¥§¡ìÐÏh~ôý<þ`€ô%kD–)òç[V«½”d ŒZnuZR#g°§³Ñv/µo ;ü\#ßZw‚´ÚË“Öª¤µ&ié\-¸Í>GáC¡ÿF#`îÔkõá€
+™ž4Þí(z,·­¥w¬Á]s˜—QÑVÆ;ËD{ÅìQr-?µ•Fì¥awYÐS6ÛVúÓot¤^K਑
+hÅÅ›D[a¼­(¾¯ú¡Wöß&à1Ï™ü\aÄSó˜@E‚öò°§F²—ø›=•}µº‚`Qƒ $P·JVC1_ÈN0R?ïO? ¤±Š¦cgððiY|„ˆÔ&_'–†2‚Ã4ÂŒuÉÊIs®–Ñ@2Çp—FàDÿiPÊ;÷øVFhïI$tšš?Ã.$S¯A±SCg"“ƒÄÜðò
++þj @õNM/7íh¢×ë}åÜÙ=EÙ™MƒÐ¤ÕÇu$UN°Ç¶_ÙÛúá¶ÖϛܿÙf¯”c S ¥&©×N¾.ý“÷A²´´tÿ
+ˆòpÃÿm„gü¹E› ÉB1J Ýõ¦7wWž²ÌÓœQB¡-1´’ÎRg®ÕÇ_­¾výeoðè]/ç ¶†Bnï¬Cä»W+õÄP+ýeiøÁ ]à=³Sî ¿]µÏ̺Ä`·Àw þÓ÷„Â}‚u×ËÛøÀ‘CÝ:B¡# Ðtj\‹³Û4·>íæ/ùfmáàaß½Ã<ÿòŠÓÜÔïïæùƒ¢xÔëíš™é¸~íÐáέåf’¡6°8£#òfIUcƒ6?›fÈìB~ëNOw½žh¸cú®Ûäþø'WAÖWÞùáã¢èðùž ‡[BÂÁ` ãËÖÈÌÌ+~ðÂÖFG@44š‹­-Ê@™´7=¡®rÐÌ}ÖjÑ^åÊ\UÂjN‚˜¶W%m•1›%l7‡æçáÇЃøq~q¿³vS6Sp¹+/w_eÎv‚R­Ý•3ÝÖsZ"­%W…Ï^rVĹòy[]l¿%ÎUüƒürmêºãø¤©­ÐXIœÄö}___¿Çy8vâ¼_
+ h) °1¶yøq¯í8a%´Ðiçl-ƒmR·"ÖQB^ MªÚý1MíºµB+-”Ǿ¾¶¯Û±Ä;€úÒ˜¨¨´£ŸŽŽ®tÏùý~çñýülÈ@ÜZ²5&:þ²³€†•
+D,“EÍv4¥z*{eØÖanu×êɧQ“P„¥ÇÛ ™îêWÖši
+’¡JJšwy‹)ÑUë2¥YSÜZ{uK±Çå­”Iú*d`•(»Úë0úXݲµäŸ?¯/Á¤$ZIµMÏÚËÒ¶ª°½æÌzA2Už‡÷7@^çÚ„sõ¼³"hæP•šÆs8­IÞÞnŒõè#ÖÒ0Û
+Çó†3{ká¾9Ÿë¡ßíÀÁÛ·˜è|?:tþÛÛžËWQ°ÓRDK’$„i‰Bgý¦+[w]ÛÒùQ³ã“5]××0«ßw²öù"Ãpëõ%¥~¿?sH–¿i[ù¿‹í—džP„Ép †À:IãÙŸÑï<G]Ü(o6œÞQ¸«ŽÒJ”˜H }ÔÀ8‚#¹¦rèøk}üq>ìþÚ»'˜·?2Õ»}˜õðÂ]Ìðâ}/ònŽ‰†\AÎæï,;½~‡ïTër>wHpÝöuú‚/MLZ÷u˜ÛÛ{:ªOŸ]çåúù0#^æý]Aÿ€á"/<ôü=ÝM[·\Á°ËÍ”¼èªòq½\èå·ÏµwY €~d„† 0ÂEâH¾Œ¼ºžó³ ä Ü«Ïßï ¸nÜ´òsÇ9o/È<ÈOÀ¿Ÿãß6œ9½µDýCŒ"1
+CI…«C%§;šŽ²HÙo7Äz VŸ`–TD¦¸£"Èš8¶‚sU†{*Š
+_Àößžc>¿i{ó××5j)£PTAª1Zª%¯iùð>–Û¼‚ãQùÃùíŽ ‡Ü!ì‹›ó;;Ï[ü¡^o_„ï‹p~€šBï§ÿî³Ú+i!qŒË@ÓPN§YþIGe¦S÷Øeå>‹X.›Aĺ:ŒY°W.vÔDíeS–´Ö%-Õ‚­Hp—FJ[
+®¦gìæ8hI:¢Ü|=³O˜y~–ˆ½.™bžF¿­îËm
+íR¹
+‡”(Žj©¬Ò@g~¼K¢£âÂ>w‚ŽÀÉבÌ(”=Zœ£Ÿ¤”¹“hzLòjQŽ6<7ù*›‘±Ì¼ 'ŽKf†d…c¤pZ<;*‰”Žâƒ/ 5$Š!4J³•ôÓ$?‚çŽéÃT~H’?g¼lÜ‹¦F$i/:ßÅ0N2^87"Í{¡ÔópxɱÖçT°ŠÀA´PöÄÎEñ¢Ü-Ij\47^”»µ0uM”¹A&Ds7Šç®‹òEßU¡;%™«%…ëèÕ>Z - YÖZ%:Þ…üx{q%XÏ`•ô"û6/Hÿvaáš(÷.â¿\|÷Ì‚øu¸p]œ¿ü|îý²§E”y¥\¢·¡]Û(
+ÂÔ, 6µVU(舆÷O?²‡øƒ‘È«¾`/öøý{C|ï|Bú"Á^wGB.ž·#n_h ìüY³^cÂP´–b˜@HÅ:…úôºíŸltµŠ{°ÒþÅî‹uŽÏWÛ¿\ãøs«ãÓµ=ß4so-ßÊBš–Q$Í Dè‘.9[˜+äÒÙo¹–&xv-ÍþËa‘š“s®ÅVŠÐ-Ó/:¿j3\ܲôÜfýåmºs/Týzƒé7/š¾­îP»¦½ª”,¡D”†‘kå4Ã0mÝÙðÁ‡îh¨/wM:£áþd`02y@ö‡"îÉ€C €ŠðýBhè÷û\
+Hž9þÃõ“—ªå0MK hX’$!ªueõ×
+û€@ü>`7 "€"0I(¸×7½;¶…ç­rp:ràöï»[›ô‰ÁH†T l Ê†[+'«ãîeÁ½ÿu{Þ8Hy‡Ô“¸Ë’p[Á”o ø!b¯‰ÚêÂKhÀq7$íM!GÝÔÀÚ(×Xàü»«C6Ó…µz˜“ŒšÔ© iœ$q‹?½^õ‡ŽÆo-é}ñ.M’3$]¦”³F°UÇúW=i<[ÓêMC ‰ ¬ôè*<e7=µ[}¬¿!Úm<–ó?0‰Q¬ÕJŒx‘-&1X†j¤XÉŶº„Í÷
+)*­W2W~Ôêiœ±i æv„"¥åŒb)Dw™¡¬£I謜+MZ„‚Å,­Z‚K7TV醑ÊhJFË@Ε±4C£j˜oÕõI—1Ìg–Yg=P–¿×”è±Î8³€(nðºõüž*o«Ì-‘‚tZ«R”^Ü
+”ãRL«Æ˯ »#á ðÎi_/qQ{зßuMóý“Ó®pØvjÁà+SÓ‡oÝ騱,ÖÐEj„Va*CEtqš8d^uycçÇ[zBþ²¢8äëí î¯w€+Ÿµ:ï·t=Xï~¿­§cI"‘2$Ca„ÓÆ‚ä3s 2—Ïçþ}Vÿï·ÇžÏç3™Ìã+ŸÜýX¥PR¸L³R9Tq¸½æÒŽª_¶Õœß¤=¿Yþö†¥ÚçÚ Útooª¼ÔQ}jS¹{fPA$Ž‹ ¬U²`eÔéKÝžæ»_ízÄ NŠ=±`·ïï Úü^äZÀ
+:?£Â@8ô-HþßÊÏ{¸n“R!‘JE®S*‹ï}îž
+ $¦Gb¾ÁpÀ8ÎÝ}S.p¼wo@§“Ä¢2_§±MÞw
+0²£ÒZ 0®v-I?>ã3q¼Ä^UâE%´ÔúÉzÞØúûÿß+µÅ3¬-ðx§AÿëÏj=ÀKñ:ü¬3àwø¼{Xik,Üÿèk×lxÏøÑ—Ë¥…´
+|§EŽpX«éië‰1¦’¤ \ƣĔÔ]#]têccÀ¦™sÃݵ~C¨k©DŒ*AÎÁH\Lsü»[Ãæòˆ º"ì¨ ôhNo)“bð`Dp$›—SDWêÀõ¾åˆ ŒÀ`/AœK;•ñ.Í¢«†uVw”p1’O d1gÍW+î÷êîõ–NnkUí[oxE'{ATSϽß.
+;õ ·µTÄÖ¡g÷®¢šKb.õ¬Cï·–gz+¾´zœ€‹pD(R\±+è/vW¤ºèk¥IƱ¬ ÷jCnÓœ]7åÖ-¦ »2î©yà1E_«Š9ÔIF9ë6Åš0£bÍÊ€M0ãgÔ!»:æÔ,ãõ³tÖ•À±A›á HÂY5m©|§U.G9£x¸T*Eùئµ¢ûØ/~ˆŽ…áÌ('4J?M#©1A aöðÞ“Ì #Ña(5Šf_ÿözŽQœ'Ç
+yWH
+pÞß
+=fFðw­”ÑèJªð§õÏG†rÙÂà0w~D‚"¿Ì ¿ƒ, –$ób#Âè=7JÅGð…8z €$>J~ñ–¼COã«C4 ­þøÇÜôyAüäŠÌy*u&/qneêŸà&'Ö$Oñ¿uýO=Ÿ¹°2}1Ø&= úµFĈ\B$…¹…ЉŸå¦/dÍœµ0!þÉóÄhP– ñÉÕpä<ñ4gá$”9+xVI[;µ&9 ýºÇ 1¸J%I‘ôÖu‹ß·Ç;»+²û¼žPÈùÕ£—"þ}SAæ1ËÄâoNåœ íýüZ׶M˜!Hƒ¸„)ðB¡˜è1N5m»Üö£›-ö» ¶;&óFÛõvçå˧M–ÛMöMÌûµÚî«möÛ­®ãu/Ã8
+Y µQ‡>b©xªFlšCõ$&– 8™G“uZÄ7j1]Zðñ„£
+Ð(fÑm/°'±ŒBHD€½¤¦S̺ˆ½<l­ˆ9u,Suhm^ L(á‰Ä8LÂidU… …%2É–ZÃÉé\_–4W¬º`¯.Á莴(p<¦‹Ä0nÑÿêkNZäóŒÜïX÷ÏJcð;¤wm£NSÆ® [Ô3 Áv=XyÂ^ÉÚtà7&<F¯Eís×Dúê»Õs.SÜa<X£â…I (ªÀÊ`˜×¢,¸Ö]ëÕ¥lÆD"Ø«ñÛ—ÎÈP½°«,ûp§òŽµiÖÙœa¦yØ£
+v)ŽOn¼›Üùï€;4åʆþ ?ÒhÔ 6
+$;)ör»æ_ÃK£Ã gÍ´SŸë1f]Aý±óãk4Â7¤}vЊ$xˆ`Ÿ1ç\|}ƒýÀ|öÈ
+½¯EM3pΨP¥ÕÔâ*‘2ÄkŒ)¾ ä_aK§
+¼ñaãV9µB^À9‚™ªÄIŸ5êh ñ¶ŒÃåë [›RÛMÑ>S' ‹¢
+¨äè2ëWuÊgJõ7%xCÔÛ÷Դɤ8Yk´$º¸†iÆ+kP–Ãdb’!pR.ý±W!_méz­1_ÓÔ€®Ø§M8;WÖÒDŠdÕ*±zM­ôÈRìò*­ÓTàâM5p g~ÎÛ>ݯ/l­I:,q _}Ò ÔÔ˜ù©.7`Kz›fóò¶ºDŸ)ånÈ÷§Ú°Çvñ'-¶jDUYá•4G–’ÌÚæSÞšî×M»Mø_ÆÈàâfS ¥*’hã~xm•ºèš_èÕÇuA·ùÁ’E¶@›áí ·=Å·D]m_ÅÝ1W=hF
+’ÇH€s²û¡™qiz/*€?ø9&GÀbBªXèI³ZtçÄôU\¸$.ˆ…‹âüéÌ9H˜üÞ7ÖÈY´x™Lœå'Ÿ&KŠ—¥ï&Û©¹ÏnªÈ^ŠWÐÌÑï¶KhìP1Š(¢\ÌZ¤hùðfù ÛÅwNq³c8=çQi$u.ž+N—e® ï¼P©’ÍÅp™‚d0÷ÚñÞPx8èw¦bƒþ»ÞHx0œè‰}µ3æöðK+,u"Æq‚ƒP‚`5(<·ž£÷Ù—Ýèò|Ôåþ{§çÖ¢á÷Ú‡þ¶Œÿ`1øêþh¡óÆB¸õNÏdzŸv¸ÁÝO»ÿÙÜw«Ã{¢e£– hœdiäÊ·Þ~û¿)
+Âÿ9FŠÂýÂ4h7¬[4BaLHè &ýÄ͉¥úW—h@‘vbuýÄZ
+íî%UÖj9L@ÊR8ÃP8ÅŠ1hÝSæ7ÿê„}A¿+Ý“
+‘+qfÏ""â3Äꧡè2føº˜$¬¶”Ó–pj6ž˜Ãðîf3€Ñ)*•µ(ò´†Ý a~þ´K“vÙs{Ú[øtrUJȘÑr5:÷³žö/†kS^k¶¿9Λ¼ülO{ÂÐ Äqâ²_vé®miÝ 5ʘRš+e$OÖqó| àO=ö\¿-é²fܱ:`ª¼«ar}ûj+C!R 8ƒ£‘W¦
+‡ÚªUŸ;uA¯.=`ÉxZ’.[­M,˜qXbsÔaK»­‚ÇñZo»êÞáõ·¢0dùò©Ö•z"*0ŽU‰d8‚[]•ör^ÐÑ’pZ^[iFeÇ”¡ÕV¢ô g“GcÄe {q×¢8_Ÿ°æ=uE¾(7¾Í˜ôÌò ŸFÌ)¯ ì
+^•[ ‚]Àa °îËo˜*|nu°W ZIPƤ­"é”DŠ
+ãyi${ž9ÿÉ‹©‹@V¹Éß®|Dš〠ðûàÚI/ƒðÐÍo8(%˜álh „£(QÈGpÉêœNŒë´>0÷Þ4Û¾4Y-î4ØnµØÆ7ØÆÍ]kpNÔÑmtŽ×Ó1S·M¶[u¶»­ž “ó®Éî¿m¶LÔQw×Zî5y.Ôí–!$?"`Œƒb¿9~‰!‹Ù…™¹åvÁòmÿ¡Hva±R£HãÀ(ŽrŠ¶Ht#ÛKÆšd#â±vÑX§ôôfÉÙMec-¥#Í%gšEgÛË~±©tl£èL³d´¥ôl£b´U5²©ltSñh»t´]>ÒZü³:M»‘Š  ˆ‰æ„‰EïÿXû¯¯—btÊÝO5öíYvü¿5Ú;ƒ^OÌ×ö¡¨ýú­î!ÎÇsylŒ`³ì=^ïPÐ ¹Â÷Ðɦ·Þj}x-·&–ðcxÈT¬û¹õÇ·'Y²M `‰Dm~ïþNu–"/2\´†Çâ ´˜ð¬‹SÚJ§«Â.mØ©ŠÓz°nÏ:Ê—ÿ•& @‘¸K´*#vûº(­iSËË Õ(Â`|!;÷'íÊ€Û°IRnÓ+ü5$Éू!´U ãc¶nÎ.NîUÜÞi,e¿ 긢|>ÊeÿÈ$~H¯ÿy³ì¨žxH³.eÌ%ó9ÕI§áYýIÓz¯‡áKåÞžªôÖŸøÝê4- SfÙS®²ŒG³HN·)¬„
+ø«×óH€a懠ä
+µ§ŸÄÕa)3Ÿ}É/-^—–~©/a «ß}E¬ëk*åg¿õ¹Òyì¡i䕾¶&wI‘¥+'Ç˯*Ĥ£pðQ +>¼µÿþœëÀZÌJ…àùN )¥Æ;Ií!Û–·7y€@ÞêÝóçõžÛ½0¨[]Î÷ÖÙßïÝu«Ûù^ûÇ~öín;øñ×~©µŽº¹ºÙ¿ôºßÙèúSŸcª‡ýÝzçGë¸míý&”QÃHÞz«uQ#À‘GM‚G´VX(¤O?ËȈZBWNÁ´3~òVÕ™¥•úõÍ•Gz•tQ®‡q=¦!%xA$*ƼNõê…aá…xÔ/î{Óöxx˜çÙÅtŽ¸ƒ!.Ì Á}? 99–¯B¼”ŒC‹´¸ÏÝ›ví÷õڟמzySpî;aadfÖŽø‘‡vRš›áâ‘¡XÄz#!?
+¹B¡PôX…Ä܊3ûM]¿ísÜé÷u¼µ~÷û}ìC©ßwÚµ¸œQ`4Ó$qæÌ@‘t±
+9Ôª ¶×Î lŽz›¢\}ÜÙ:e·™(‡a£Ajœýr ˆ³ˆ§)3ØþO_»I1˜‚B/›Ø¬K Ø€yŠ®æ×ûb_-0Œ˜RÊh ¢Ò´¶×id¢kÛ[ã.kÂÛ.¸m Îê¥ö[d-!Wë7[”}ÕÊ­z¢ä9ŠiI¥ŒÀw˜˜»®¾  Žßq/6ãšouÊgûa—A…cI«Ð²ú ó^ërßÁÕõZSlÓÍç[ÉÕ«5ª5V‰¹
+¢Š‚IF«‚RB„R“â¬ü#O–ë,z­³MVJJªÅÁ“ÂN>µèÉ„Ë6ã³$]Í;j0 M«)
+Á•¬MáÚçµ³>sÔm³Ý·=fÁÓõ´ÞÎÚ„ß" ÔÇw™#¾fÁÕ–Ç 1g}ÒÛçA=*ÛÓ{ÛbÎî‘—’$ÎÈ%­6àe?yK%©îªSÇÊK‰ðK²ôaìßÁþ«‘ÿ€–8'ðÂ"7* ”¿2¬¶èåFR#L.Í© @cÇ©ÄIþ$ö‡@ÕF“‚*¢F¡+oîÓi¤  ÄqY4€”Ž¾åDNµ~¥j%IÖàk%µ‚¢$SÇ4ÅK ÁW@/LóË
+WËs—ÊAÊgΗ®@ù«Ð’µpA”¹ŽŸûùõÛÔuÀœi݇5 yؾ¶ïÛ÷áGÞÁI¿pœ'IŒ]ÑT ñãÚŽI€B‡ª4,¡ÔNS¢Œ0VRpž&UhÝ(kÕ Ú­Ú´ˆ?â·smß•M•ú!R2¤íêȺ÷È:çøžãóÿ£¢ÃÏ 3“õQŸLð
+WÄÙÑB~¢(=!ÉþŽ™½/Œ3¹I T.µ}@‘äHAfT,LÈ2Ò…1qÊW †š¼òTød²8:U¸àC„H¸Ž~~ ¤$qp¥GÁË{‚ŒUheWÝônË ìr´Áýa§ý“¶Þ/×{–K#·»]GŒëq‚1¸»±…Oñ¢A)Âóüã¤Xò?|‡9~ø×H1ªTh(%J Ý™ç–K#•oý@wy‡fx›êÜÖº7w”»¬p Ba¢º"FM–IHµ‚QiÔ²ÝnÃí¿šŽí F39ÿô¾yÿá°ÿ%ÿŒg&ÀÍÆ<OÜ +­‘¯™dnΉ¸Caûôô®ÙY{(Ø”˜X®Ný3®G †ü½¦¹` ?Ú÷Gjf xwoW#Æ0†! S!¥‘Í´lò¹¦¨§!ÂUƒgÔeYŒ$v¥wõ0gIºš½¦ˆ½~Þ«ï3Æ\úØ‹u ®=`¯¸ôq»%é´UqOí‚Ãö4ÇÜ5Âní]oÇ~K9Hg¦pY•ˆ¡
+ùí^sÌcŒ{cýíî:’†iŠPRãгq¯aÎ¥xkç¹z—™")º”dT¸Ôi$ø^C´Ï˜á£=Ö;=?.E*P$‚‚€Ê¡b½‚ÞQAßÜe
+;õ Îq˜¢Îª%ÿd‡6æîx¶J®BñJœ”RŒÐrW#«Îw×Ä{[B\mpÀ\t )Γ¼-Làë8Ê°jiÑP[¥Ð³âiî³Î¹  ÷ô.Ë­í&"OFJ„’ë%4Àvˆ”Ä hNLT¥ºjÞŽX!Ý«»¸½N-“Ò,‹Ë 54þ™Mf9Ä5'÷VßÞm¬¢$LÊ#ן×͹-¼«.æÒ¥öëÕ­ï¦ .?£ûÒfJx- ½æ”½)ì4äöÖ‡=–
+R%~¥
+œ&gÕÑŸ’áÓßÎüŸD
+òQ·KM,AIJ,¦ b\YFÀ7’±cŠoÒHhPš~M=ek„lzH}¹w¬¼UCTb˜ŒP28\«¦§C
+×E¹+«s#DfÊøò3S H’>iv^ð-Y Âe†ŸúNò4ÿ."¼/FW-jäº\˜(âÇòRãy‰±Báš…xŸT.µýÜ8(’[䓈g¬ ;%ÊN!Ù«ß&Ÿ•™Q&7V´0Uxÿ·TGû:–ÀS“4 2P€˜j%5çŒÛ?ètÜÝЯÝu·ûs÷i»óÓûriäÎ÷Úl à$D à4DCÈÅóÃBößÍ.>
+dÙø–
+µœ¢H ˆN=Ž<àêCn#è(õbùP‰ŒÂT4LÔh舭äKØ©KÛB¿õO/N·k_^[rÔDœß¤¾±sÝ_œ-óæ·†·W˜CÞ%¿¤³®în-tþ#(T)#ëaè%«ä¾½•ççܺù>3x!`1»¤ð}·å¨•jÑ*ºLª­Ztx‹>ÙkXéy™é5$û›‚=k·eaOã½=ëÝfCä¡
+ KËr%Aãp%#/_GaZÔ!·5Þcˆ: 1»ÞeYD‹‚„ï××E<aNrš„ݺ—›KÁªÃå4ÈÁÎJ$boˆÚ ao-oÓþÓÛÔLÉ”X™þÞ–ò¼¿ö¬sºÀ€nnÀøÐeäíú¨£¬L€
+ŸX•»!&E™Ë«scßJø¤‘÷%‚¯8;Z˜»V˜{šŸ,Ž¿—Ÿ¹*Ê]-µý̨D˜Š$}ùüD
+) "Uj·BhH× B6u[¥jƒ®Ttc›@Çöõûý¸;!ÝTMãLÉvõ“¥sÿ8çžs¯Ï÷s:j/mV^Þ^>ñ‚~¤SÜ-ƒÀÁ•'Ä`!†aBXv!©æÒä¾¹0õȳ×ë%>Ê5g¦Ý‡Ÿ;VU#
+Çkq¹x=öÿµ_Ü¿¶‘Š`” Á¨‚‹ê+\ýJí±jÿNšŽu ¢nÜn
+“-«½«‡,ʨSOSµR±Ö¦Mnbݲ5hÓ&¬õq\ï#k\•×Ùr4‡lFÏ!ýœÝôvWµ\$ R`©³²ÌõqkËp‹üßy%.Ur
+'·×3ê¨æIQˆ
+ØTR,âHHÊGÊÑÒ}š¶éüOö?o¤
+þ“ƒ­CÜ¡#ëJQÅ I ,±©àÐ@‹ßªñ éÝx“µYÉã¬Q¢œj úZ=+h1Òöu4eŒš® 6z‰ºD¿:H™üæ§wUÂ^§½—Ô,w¾ª"計õ{ûh’ÆÇ„.æP'(ƒÏ¬š·ª s³>Ž|6]€ÒЃÆ
+1ÃÂК··pF·AÑQé³4–&NÁ‘7‹"§9QØs
+Ž ™óRϨ(sœ{¤+_W"â2 87rYc‡«ÂC‘©ÜÌÕ|fML³â7¿˜ùVöJÑrµÀ
+RCÁÐ~sÍ"ïŒyÍ1¶iÁÛ~¤ÏP ã‚ËpI£m T©À¤d#¦¢0%
+)~èÔüa ;°˜æœÏ˜¼;tÚgzX¹5h*ºY¿=Î7Å<Ú¿+èÊíi¼öÛ†…V·’R†Dd½«…ºyˆZ8§GP@‘ÿhdtY …±úÄøêèø—3#áe¹p)HòctÐå%‰p¼æö)êÀ&‘ºª7âteWrk„N†ó£ŠÒ¸(z|¹sa¬á‹4²8‚¥Gàü«xvNÂ…Ñ™1"quá}E¶ÓX¥¬…ĤL£Íõ’ß7f.)JçÞçÎÂÙ_—/=&œ¯X S¥éÊÒVš]%\Ä
+ç¿Vøe•p®:;]Vœ•ä§!a)ÎTç¦W¥ß}¬0+Yé÷ó3¢ÅsU¹éòâ…ÊÜLUþ¼´t\x
+ÿ9ømic£ˆÄY-¤"`9&&Å´Wþ¸çÙk}Ëk<—׺?v27œÌ˜¿®õüÍå½êb
+Á•8½^¡zýqcrOŠÕeØå!Š¬õcÞ®¡0†)LT‹É§6+3¬%»ÇP`Œ UÿÉt,q½‚G/0ê מô7Gئ ×½èëLzS¼.é·
+•) âÖ÷ÉȨH8QS:BFŽ¢©qñ 4q¼ö‹4’?
+-À£G¥é1H8ŽåIs£PþR<ÍeN’ÿ8L¬ÑÕÔB4©#É6í#Ÿœ¡…‹_ÎV•ÎÑé©UK¾^šZ±2—ªS3¢Ül%pBzª¼pÏN?šŸ-Ë_,[:_Y<-ž­]š©
+L&Ó`bβî†sø²ÓysÝå¾îÚó¡k÷.àö£u^
+ú:3þWJp08ÏÅãPˆù"±ÀŽsóéègûÒsæ"_}m;"a¥†ˆZ„è¢kN÷©Ã\gŠ±.x-ù€#Ã4?èÓ;ÎØÒþ®4gO2íi ÞãQ_GÚc vGÁc9÷^}¹ÁëTS'­1Õ0)©©¥HZ°hLgdŸ1ã³ÿq«7 T¡B)ºF&§1ªCÅÔF‹|j«.Éö¼ÕGüê™–Ov¶¥ÿâ¾^c›º8€w_¶ICð뾟¾~Äq^Nl_çé<yh´À
+ï$¶¯í8ÊVu­Z©-Û¤Nè‡ TˆC˦‚JYÙØÔǺN-+qâ8ñ;~åh_ø°©iª\ýuuî•eû]ûÿ;¦˜½>i«xZŸo`,_Æ`mEð[ëLóÕb 5™ÎÚ” ƪ'Ü+®l³n3,ºX©Pƒmª Å4B³J5Áâ Ô¡Ueí–¡~Ú^sšvX*¤j¾’ËYu›Auwo}ÄmÎ9+2]-N¾ŒeC1¼ÄÄÊÿa¯Ú­ã]Õ)GE ·>n·„ ŽÊ”«܉¸jﻃBÍ´Ãø¤ ¼9±ó€
+“à%>êªëu
+¿‘÷ÆÓl} ÉH•}¶ ¹ø#ö/½*ñUi̧~”RÀçNôC9í-¸õ§G‡)êJûÜjIl¨8â_25¼T¼ Í^*‡¤ÿ«¾«$‡¤â0”ý”»
+4õB™þ’ô1:ê[|ï”nsÉ㘢®”Rø²ôQ¥è#†û*[ˤÅ2œ…–‘tÑ2.—Ãz%|¨•ñ)Ó'¹ï‰Gi$ê£bÇØ©þÂØayÐWñ«ÝÊb¸`õjDÆa²“?ƒÄ÷$â¥Åñ«Hú2-^Ο/ˆþM_–%üpêªdz¨0w¡07$ûÑ쵂‰wòDÿÒð üâ³CB8‚kÉB A1ŠTD‡Æð[ë–?­n¯´ßZïš/ÜYåød¹ýã5ŽÛlwV /ZžDDI±‚5ÔÕÏ´¶(¦2™™þž~p±PŽéÄJ¥R3óšo¼&)-Æ0
++Å˹Ùtúé²óíü™ ús›¾7Ì‚äaŒ§ÛËÏ?czksiw=^Ž „‚"HFNà8L@rØ
+¢:ÞS±[B{ù´P'zøD'=ïõ½ÐÒ]u¿ŸŸt•'lúLWÙTWyL¨Lõ´z a§ñÕ¸ŠÄµ
+²•ZôewCÐ^Ûßä²è9L¢yzJÕQ®ö™ÇìæˆÛ·Õ&mewû·éŠ9„äpcäGÚøôÛt6NÛ áý­×~ªjS®º À‰PrÔÄvgÜz@PðÅ&<€7ÐÏõ:D»á¾†Ç’ê©ÚÛŽ7W™pLB ‰’(Á+ÕôÑa¯"wB26 ™:Ì$Žb /
+„ò `
+Áå*”RÒMçß>$7úšfÄ‹‹çK#Y^Æÿãô¥¼Ìe,í‡þEé+àŽL¼ø˜xIvÂ…ëJÔ(£…1…Rp¬ž¦ñFŒ{Ó¼öÃ']w–Û>_çùsÓ¾ùÒȧ+­¶ÿm­ë³åŽ[k×VṲ̂
+ô²šå
+F0Ò†X[ˆoŠ 8b›ClcÖm q„Ž‡L/”oÔJŠcP%ER!FKe7{¶dŽU発φìKCÎYq:>>ÒÝD ‹a©VTUIŠÔ
+Á>c9c­Ö“%‡3-UÒÂKζ˜³<7ì6exû2gõé‚ŒqnȺþõýl%ï4%˜úØ  $ê±&YkÂk‹
+-Se4QÙ]£È°u< Ï°Ïq«"ßiÙ!%Õh5Š.ºÒn)¸ )V l™°¿µË@A²CZÙôACá„=ëÒE\V?×<ïk‹sÖÖòßÕqHâà=g×|’Œ=ÊêýMÒc(0ö”³åJ§©œ09M©IˆPRÄ_¢#ÂütiX’ÁVAÖÿÓ¥Ò£¢Ü|-ŒËÓçÊþ}±vh[ñSÁó¢Ì8ÖÓ"–P0…ˆ+%B›Zt¦:ÛA÷j„¤d.§Ô°`Ò¥ŽŸ“<I#‰QeúBIæ"™òçK
+ÃÐ'¯Vì1~FÔ2t#„+šÔ‚¹7JEâ·Š
+¿*^/„ÞÅó·Q
+4òÑÔ4Žb‚VˆqLOè~Ü£¾ÞY{£½îZ—|b—ájÏ ‘U~€€k@ Ç8¹¹[qm‡æF‡áÚNíÕ.ãõýú3Ýh«RUF£$d
+T…*T$ao¥ÆÞî»ïçfR'gÂ|ÈÏ-úO$f†ÂAv6xdÝð”$8ÏF¾м/âÃ!WÐßöDæN]:½$`T‘4,!Œˆ|úsfÐïÓæ8cŠÓG<†¨×åã®ú¼Û¸Ö§tœoŠ1¶c3æ˜ËuÜ&0½FÝÕY_ó¢¯ñsϦfUY1„C
+U9‚+„åR˜ à®:2ÚgÊTÏö×Ûœ¯:à´ÆÝM?誔WHa„– q—Ãx¸K*„)Öˆ¾rÊ‚>p[²œ>éµ$ùƸ۶à4§ó¢Çšb)¦mÝëûË 1È‚KÌÓsÚãÎÆ×b­·#ì±%YÛS—ÐÅxíCWmÞÓ’í¯ûwk”2-¡K„REYY))î1xk¦¿.ÍhÒƒúן7—‹¿,†ÅR²¡J?ío)ôéµ
+’(cïÈZïC˜·¤Øú´Ûœt›cýÚ´×ðˆÛ|}{-"#ÄÐפ˜R‹ÕÜ$ gñÈ%Iæ"¾
+’•Œ «IA™aa@e\F‰åKò™zùœ$;^â?]©•%˜T¨ö–—>:_³t¹bæ2ùðeü V
+!ËÅP;VùÖŽ}Ÿ63÷wpê`§·û‹ãÛŸmõü½›në_/|Òêùs;÷á–¾{;Ù7;ÿº…¹Ý|Ô,UÈP‚
+XÀ¸‰VÜõÒñQjñþ˜"
+Š Å0„K1BCJIJ[IýHÕŽémôÝZÏM[÷”ÝùQƒkÊæ¾Ûê¹µ™ž´9ï?ãÌ–F¦lô­&÷íf÷&ÇÃF×d ý ‘05ªPc„|ïùƒÙVÃÚŽéŸ
+Ç[õ;
+.mûr4²pþ8åã»ÀÍ¥m…mù·jßîÔÿb·æ§-º @—äw ¨B–ÉBŽä,Á1UÎþÃæ÷§˜ÏbGýœof¶à$ë øªdÖÍÍ0Ü Ë=ÁÈ¡O½ðã3{Ø×)BII
+D\Š×÷›Ó‡Œq‡6â¬÷»,AÚóUϱa‡1æ4ñŒ5Òc\ë§tÔ]d*øžâ9Wq¼¿ê§öíݦÑÝ5:ªãlM„1ñÝe×:Jþæn‰ÑÅ»Í*ËÙII4Ï€ ¦Ô‡|_ ïªÀVs½@&útWuÔÓð§öší²A;üC;~uOy˜n›óÔÍôÅXkÌS½ä©»ÌqoY¬¿ÌÏ@‚ýFðñ¬×÷ÿX’´e¶$]¦E¦Œ§!—.ÊGØÂE·)É#té¼·2ì«úÔ[ï­Y8P:ë¬íÔËä¤Z„!Å"Á¾?ÇH’‘
+lc ÙuÊøi•Â
+LJéÐM×vù.K ¯"ÉV¼··'Är&Q¥˜€ BT,EåësÕhîð÷û­3=–U–c‹<N›’ž5¿Ë.ßi~ÄVÅ|
+î58ö2úaŸ8„$I¥ä›É׉Ð04?$N jbƒªé3B­</G„ªþm/õ$¤Ž Ó¯jB'Ä‹#8B…Få©ߊà= ˜{š+H
+±ÈÑ›oHR¿B²¥‘…+ÒåwdÑwr/¿”Ól"0©Œ$p\¨P£à(2 Š—­-÷ZöNÛºÿÒtô¦ýèíVæÎfæ®mE#Z¼÷ìô-›ën+›-ܵ{n78?nöLÚwZ\S-ÌÇ6潺ç¢0Ž‰!JF>|øp)“I¥RÙ†Ã6–——ÓéôêI¯×‡£Š}‹¶âüs†ñ6õÄæÒ‰vÝ…ã¹mòŸùÏ
+Ê9bÈä|†gK®Ø•Û’y` ¦$è«[èQñL1Ç¥XxWÙ¼·*ä,] M§9Ñmö³•ó=Þc¸
+A-9ëRkçU÷šëëÿ-!¯1ä5ÏÒFÎk öV™Jž®žwÖ„½VÎc°€+Ö¸«tÆc1åIºîú#%å`¢<Ï—ˆÞÝi™ØUI"Hª…Hò`•€DQV …!`j4oMŠi{˃^ë.Ë–“/!PœÌ‘ $¡–á
+q®TÕ-ø*HÂn+Xó@#Q—§Ík}
+¥›äRôÏGˆ'iÌ87 džò’CÒ…!ajŸĆÉôqé£c¤Eƒdà ,¢HdGé¦Åw×eK#™k_ûãèÆH„ðED2Ua¨‚@öUþ¦™¹oë»_ÛóIS÷¤í¹­à;Î{v÷T0‰{²‰¾¹™ý ™Î–F&›>×H£{º‰þC«ã£×­ÍÌßë{y!8)K|>_zyyµ»³íˆ/h¤2üç/\dGGQ“Aꟴœß®Ÿèx¬‚µÖˆnâÛÚ±íº±6ãD‡n¬]{¡³øòž¢ñ'¾_3Ñ |¢¿´©ø|»ö…b‹R$['—æà€@p
+UKaÄبzsßTèÅ×ÃE}ÿôû¢³‡# ‘C~&ð„GÁ.ÐÏÍö†ƒnÀÿolæpÖÙðß&èäüž€¿?:‰x9ÎÁqÎ`À
+ ßÜÑ`ï[o|'’Ê °i’e ,øݳÞWï.žg+Â.3ﱬy1ùô‡>ÙSžtW‚é¬1A\樧,î±òÝåºÊ´8 0li1‚©ÄJ,óV!Ÿ±µKt]ØY,qÌV¤Â`!¡$DIöBu}þÊšÖaÎlÕòl}†¶Y½¿×È»Lký»ânË¿çñõ(m
+ÓÆ c y 1ÖÄ{L º4á,I2s®2Þm¹,ÑQ_g±QÜwÀ[)}H;`¯wwvfçØÓëµw½ö޷퇱´V jsïÚ.m¡UšV‰„ q4Q¥( wD‹7ªš*/AU!R¥¾D콯Ù{í顪ú€Ò@QV?­ö˜ùÏ~ó×|?vçA*y2¬"p‘Àº2¬=r¥x[„îLNÙAÈ‚s´G Ý9²¾KŠó&ÃõZ ÚŸd)6˜ùwù3´h˜I’s‚†ƒÍ²Œ_ ƒõ
+¹Š!/hT~ÂYšôTè`òƒÊãrž4[?‹û;ZÁ”R!ë£ê[&äϳñ¦ÂÎlÈV¡í&áÜ­mZXŠ©Õ8„ì @1Ö¼úîÎv­Á•*Xe BcTK%¸iÜgC}1=P&Måqë|¿Q†ÊUD—~VÁá~Ç+½x¸Ïl1Èj’êU*I»»¹¿3w°' l0î¦LQÊ›šé‹ñý)~Sš€Õ˜e]IÖà< Þÿ¸×OžõUIs™óÿvW·^-AÄ‚ëp…"h¶Ü}E'“®ÎB•ãÍwÞBĹŽõ!Ì¢âIIô$ÁnmT+!9ÚfÓ}ëƒ_èÊs™ã¬KÓ 1HaØ0L ËoéJÇ$©*áèñø†üT8ªY›‡ËG‘õƯƒ*³ø'‡áŽ™N¡Òhär½ôWczqéùëxéR‹¸Ðš»öTá£Æò•âõ¦/«‹ÚÕÆêµ–âe¨|M±º$_Å…&q*]k(/Jk—%âšÄËÒµD¸"‹_~šöuᨠV锪6=¬7µ [Û,_¼10~³÷ôß·P·¶POÊÿ[ý¥÷@c€e’â©ÛË¢¸Z+
+©!\ƒ„‡©üÝaó2ß[f»£3~pßþ'¿c@/E4*­RgDTj ÷#êTâÐ.ýÓ+ÔVñ€${dƙ㟘FêîšôX¨@†$Ø@Œï…7¥xGšµœ I[…´–_¶¬Žwƒ¶äi°±/O‹tOêU¡û–isŒíÊr¶ë,3Î
+í„+N:öáUà=€4àôsŒý~YÖ–çìõ½(gt¤ȧÀº AEÁ¥ "Ó¾{aO4\çGnÒ^·&Igšvƒ¬,²A@”
+¨Nú+¾Gշ¤«8:æìL-±þ{áçÞÖ‚]#܆Á­­èÆ¥Ž"çˇ¼9f`q¿u´ÁŠ¶È
+ã{,êKßwDÃþ,iO0Ûœn¼è3Ê7”ÚjEá÷†zoOt¦½«ãÁ›û6o×Úrœh@1íϼÄíÉ:6J“>íHQÎmÍðVà“ .D7XœyÊ]¦}àÄ÷úÉÒ¶â}‘Þ ¼=äêBáfH©&T:EÓ UõÙ«pqVY›SdO4‹GŸÍ¼¡ZW 'T™y8?¯øÇk.'&Å¡ †‘†}6Ã6‹Â ÉÛàvÕëà–ã#úò¬´úš*ÿÝ·±ì<!Îi*Çá»'e™7uë_˜GKoÈc³ðј@µ0.5Ê$­xÓGshåOrqI]»ÚT½ª(^k¨-mxø²©^–¯^o©\¦òÁ·@
+ k ²Õ+r@ê›ÅëòÒbcöçRK-ïLá.%ªU6ðF£V*”2 äÓ›_õn¿±ƒùt[ÉfòÖ}|LòÉ6öínLã(Ö†iŽýòu±Vñ-þ—=¾~ùÏ«&VתÉbÖk¶)\…¼Ùòóíìñ 
+_»sïÌ^^{׋÷ðzïËGíÅŽC §Ò$¶÷˜™=ls(i¥*MJƒm0h…¢ †
+øØûÞõÚÛTVÿˆ¥¦ÁAýô43zÍ<½yßÏ{…*1„ÒÐiÚéoL°€
+ûWwM¬§Ç×w„
+XÓ^  HÚ§ˆ0uaÖ”ñÙRNíR¯ºqÂ~S¨Ï” è“ݵiÚ0³kÝõíõ­ò*‚ä•”¬JV‰qú-²;?ÑÌŽµÖJIà`JŒw(¨`¯1ÙS“cU)_ݤÛ|lƒ®š·—@ÐSby…ßX1Á®uò»,a¯ºàµd]† «ÿî5ò€"¬1ÄÖEéú„§>åÑfYCÆoLø ‘€>ÆhÁýP-èÀhc hÃtc”mžv™£^[Økû é=¶VsÚ§ö;¯š>Ý®þx“|ìEåG›U¿X7¶Eñµu}³üÆvåíÃ]ocÐß¥í —5ë¶Æy‚]•ô*“À'´6áÑÅ]  p^ 3Èw€œ„ÇwZ3N{¬ä¸ ˆèAbÒõVñÕEukÜBúD¯%H‚´бHÛÓ;[ÿⶱL©Ñgnn1M÷ê
+È帰†¹*~«C8±ŸÈ¿SZ| ¢~¶u…€(¯âK­HìPMáíòБ’ÅžŸ=Xš‡…÷‹Æ_©)˜Ç¯$ ãol&óàÄ/>]¼V^¼'/®,\…¿©F2æ®R™ó+
+,/ŽÁÅ+œÙ ¥Åëœâååó×Vþí„ÀÓŽá2!NR$‚ò0JTQÂM2í¹†m_´o6wÝZçyT ùþÙ:æ¯k™'ÎŒÿ±>[ËÞrô¶ŠV¥ p=÷ÞYà…Ba!ÒB
+éž&ùˆã?# 2YÀÉRk¤æl‡ê×l¿Ý¡8¹uÈ/lwø#­‹õ—¾ íÔœz^sbƒê„C=²¡~¤]w¼:ý#í¹NÅi‡äŒC¶·©ªQ ĹBA%I!^µŒ«áÁ¢j‡ä—ç:¿˜Þ9abÑ]÷ïz‚_ù²±Ý±iÿÔ=ˆò`¨ï‰sâ[V8¸sj’‰Å¡;=E‡‚~€“d²ÿã~«Q&!p=K`u˜à|‡tÆ×èQ¦™º|/ØóÖ}†XÀui3K¿Ì»ôqð áÕg<šüÎÆÛ=:ÿ&&…B’' Ë 9λék)ôiƒ½Ú¸¿ah]}5§„À`Þ‚‰ûfƒ¤È6}ʤS•õ™C´ubGóûëôn4ýk›äfÜ¥È÷)ÂnqÚ[—s2.SŒ6}÷y’kŽ2¦°Çöè"´>Âè¬TÆÛ_Üm-ÈÓcK³ö¶ª/üXqxäÆj§^Ò¾JÔ"—*0ìj…BñQ„ƒ )A`A®Ä(ŠG|}(…rù(W„TT!J6I¨…t³Zì4Tý´¹æH›ââ&åøËš»Œ!ÖoÉ°öHa—ärÊ«ÏzuyZS 5Ñ€TÄk|ø ¦8cMÒö¸Ûú¸Æí­
+\ûÛÿô²õݵdÊßœc´w}Ä£+8­³½¦h·*ë1æ<¶T%î2å A§¦è–ÿùAR.Æ$$ÅË· ÿÝóÊ\ÿÐgŽÖ}0¤¦ÛÛÌ"ÁÈšjÛ–<­Ìúµy¿.Í´2ì×E&
+4@ tŒ¡¥@Wml´j”ZKÛµ yÛÁŽcÇĺýÒLü±6¬@ÔÓW§‹|¾è÷óÝïû9þBæLúƒjdª_”ºÅ¿“ÅŸ¦þ˜•:#‚DZs?<º[EZ<3’”"™2
+Gi²Qì/^{}µ÷ã
+×Ge­×*:êïÕx?¬t]­p~TéºçJ7Ì¢3ã>sceûÕ*Ïk¶õb€¡hùV+ŸI}½}ïøñ?[ŠO]z÷]2K„3 ‡“ˆèzŸV©œ§ DÈãÔˆåðºô*œ­Pê·Tç÷¶hÕiÔä¿ù“…Î×|½×oÈ9VŸÝ[¯>Ñ ìkPœ¬Ïî[§émRjÈé]«>ݜݳ>'X48Ô:@|LNƒÈ ³´©vœoþ|¢s"¾uxÜ9tû¹è„;
+†G_þ|Ñ9ñ3>Ú‚£›pŽµB–„Çùñ`m™
+,‘a2(4FHý¾¿ë7ÞñÂú+Œº-cí¦·
+x· Îa¤³pµ2Ö®½ÛiŽ#.U¤Ë æMA?8QOi³V`˜
+a0‚N§qÁÔ™™ÑçŠx¯6ê4@ŠüÛ[ØšGJ¤ @Ä”k¤Æñ@þL‡.îRE]Æ)ŸaÚ¥»s’Þœ¤GŸðb®<ø˜<ͦ<¦¯}Ì]2éÍñhàËÊnùò7ç«Y’|BÌip9üw4>™ØËòÝÔ‚Z؃LíwöpSû¨äA‚ß&_Ç^f¢û¸ÄN*¶= žÀïFûÒNùĹ”œÆ0 FÑŒ\H€¶RìÓn,¶ÉL·r¡ëÏt§GˆS{©éƒË£Û4³OóŒÀûB„¤J5iƒg°Øù¬ø_2ù·Ó“oe<¨Fîþ퇱³O$ÿœÎ÷§'Ï-M^ľ<+=ý›åv½PÂBAJ)±š#¤3‰¨­öz¨ŽKu®*¥ŽëU®÷k=ï×ø®V Cæ›Rä´J¹î÷™OÊ]—+—ë6B¦€Ï/IŠPôŸçþúß6ÿžƒ$‘L9ZÛXš’
+àz,À‡{L‚‰¤¸˜è·'Q@‚ (‚dH††ˆ‡8a È ®osçásJ
+C~ëÁj=”´XÂQŒR€ZuœT¸e‰³âʳú¨¯,ÚV³»ØˆQL,T“Kå$÷z½!ìÊ›öçN;Ì“›ó>÷—¶¥nÐ[=â)‰øàõóc>Ä$ïHð¡ék¡$Æ1H£®Ò¸'Ò+´‡Ûsãný-‡±V'ƒïJ‚`R)÷ꆥüKº…´0µ ã÷
+"ûÈÈAEòd¶Oî‘ñ;‘ÉÝ\ò\r7Çïâ>yl+WËÓä*LÄÒJT°ŒÅ)%PIBÍ*¥äÐÎô…®y‰Xë&B;–𯒉™—~­- ÓX 4-ÁdÛZ–‡úÕü¹Ì»zrömáƒj$ùÖ
+þ,qvYüü’ÈEÑùƒè¦*J(Æ0…
+“IÅ$hŠVlÒ¯ê¯h½Qâ¹^⸹Êq³Òs©ÖóAâäZ™óFõ\¡ÏSd Ú3P醋ΌûÌõÊö+%­WªÜ¿2¬Vfá"ãvÝSMÉdr¾Ð!Hæ KfggOÿÏöåW£(pd#"FŽ[<›st½®¯nÞßÌ£Ö÷»†LN¤G9ÉJ½ùðFÙ©ÅÉ5šSë:_¼^Û;§íñ&ݱuúžµÆ7Öšzšôo6iÕë{~¬ë«Ñž.×ô•È{Êáu,GÿÃ|7qÝ
+Z‡ë(ˆR+ˆGôŒ¦J½çPÕO÷gÄP40ô>lKü×1ã ŽwÇ£/Œ%Æã=·¿èÙÛe6 Y,WF©Ut^ÀŠGüe‰Ž‚¹Àú)Á—loIBtÆ»J ¨ø€"‘ÞUϺ3B°û‹‹•
+ã´¯E"Y„¤œDÁæÅ ˆ¤`bh˜aPšFÃh¦%¯!Z(M+Ê<‰°~’ßÏ2ùé¼çXþ9Fÿ/ãyuÑÖ,yÛP¦‰`ªhuAª!I*(\ÉàJSiP¨F£ÁH5°crW>(`Š
+ŒJEP,…»4ð®Büx£îêÓ¶ äˆJl>'zÍ‘n;pNX*OJå ¶9_Åß“=q¯3ÒeKû«ƒ]ÎŒPï,í5…E@;ç
+>ã«Hv8c~WD´­vvÊf¥’XwõÜîÒEÉ:í1%®9Á÷DD]¸»(¨˜}ÚzG*Ž‹&p°Çvã)K¡‘C˜¢!užËl w•¦}Üd·cJ¨ˆùÜ»ìx6‰© ä`…úåJä³} €;þÉnSx¯%&Ù@1±dÎos‚+p„|¶ðÝ~’=&E½Å É9ï­L‹å)iP°üîIóëtÕf AQ 2Œ£*PÝÁ®ì œÄ…h”Œ&Q\À¶“D•š9@q?§u¯jÌ¿æ,ê79×ç
+iʾÊ+ q]_õÆq–)|‰1ôPÜ]K Å ÅrqLv¸,Ø…¡Ë¥!9Éa4¡Ø²aøQ+«j³_ÛÀ^ù¾=ÞUñT%½e
+CYBy¤qíØa,õŠ"ӧܩ֒*9L[¹uÓÏËb}P48€ÍAýèÊù½Ú±V‹¡ë`@ik©I–~‡š;Ÿµð^öÒåo%Ïã U󾑺$_¸ŒÅ/f¥/+Rï*—ÑßÊâïÉ.e%.<’|wmüÒºÌÅGã¿!{UÅ„œE *’g“)×5©u/ÛÿS½÷º[­ö\[ï¹Y¸ß*ÿ·:éF­ï“Zá·øq£¤^
+×:>ªûÁ>Ýà»Qëýs£çj“÷³jÏç5žÑúÎÕÖäÈïÇnœˆ:ª¥ ÍSR¿ªyjáÎRzTy
+Rp"™'“)‡Êuóãd‹…cÌ“þœŒãu庫ÕQ¶ PdY#`¬gNÓMΨ· Âê£~C”5G|P£Œ¼½96'ñ½¹S¾âÙ@iŒ-œnÐõ7ÚÎm2ìÌ"Õb>DÂ(‰Ñ¨@ÅOÁŠ@Å"õ‚1EAÇE¨ãW‰È}RõòìßÊ ïÉJ†h[ˆv|-³?”—öA¥mHY:¢,N(B–ÿ¬4"(- J‹奃rû²|XU”—÷ÉœŸˆ×¿O˜¼¸¢œ u$,¢„`­ Db%HP¢ \"–b‰
+!D&áY
+¢Þ"¾¼M3äËš dÇü9Á¶œá€iÌgœô™¢Í9‘@Ö´W¿à1Œ3º(kšóç?f,óÞ¼ÇÞœYÆ8ÃV¬&wΓi2EÚŠÂLîÛòávØ6‚¥Z„
+Œ8jË
+î„$ö6Ä} œ9Ž/K}šbo˹÷_ú4 4"©<“RDô½K^*tR´ðKãÔA!w,iì´$`C^3¤ÿá Pdþ¾¬‘e$Lò”þ?û©ÂH¦‰q:Iˆ©°5‡1îúã7Rž\çÍ_×éÜ5ܵô¥„ñ«éO®I¹K©Ü5wŠ_YÇ„ÜÄæ/!ÜMÁÅ|[ö ˆ
+–R<8 ¶«“oï±q>Û¼Çj6³¦ÇÍæiwæj¯¢ÓK¼¾pا› h–Üy!výïwšë ü X(â d<^&,Ä`JI½åÔ-ùJçöærodî65ç* ž@I@‘*… ?ÖæD=%Ó»U½š¨;;ê+Ž·•Ä›ÔÀ!˱Jù/Õç=SLIÈ—7åÕÇX#øG!Ö:È™`McLÖ$£{³ïÖç­4•k)Ô= "'Å8!%0ÈA DâxŠ½
+QG`õ§DÁ?%Îtý]2.+~$K`cˆ¶ ÓfQ©5B['eVpwT^òHQ @2´l’gaYþ8m™
+EJ½WFÏùJž—Fb>SØ] XæXË…ºœLA T~º7‡yŸ9¥áŽ)ÂïðO¥ÅÞ}ªF–NAOŽÒ“Çáõ/gÂÐ6ï«ãÒùCâ…Cü؉äÅ“Bîx ž&‚Çî
+(?IüŸ™;Müj„¥c2‚xJß{(w}íÒMhá
+Ÿ»–Ê}¼h„û8éÉo^˜»¾fñZ¸5w9‰û(…ëY³tóå{ŠW‹Òi{(LðQTH`¡Þ%1Ü°×öÙÜ_Ú™¯lì@©çN™ûv…ïn;à`VZåÿ\á¹SáMhÄîé·¹C@ã÷}W' rØ›î‚F`’JŒÒg÷õ:Ù¿–ùî»üål¯ÃûEsÛåYmôÙšú¸ìž»v÷Ÿjü[DZÇQœ@a¤óBDzF¾ãá¾ÁÒ2H@Î ã å§í Ž¦éZŠs¯¨/T©{ªMç«V[# ¶Û«ôUÒî J€ÖRü¢\
+UYÑ€Ä×ã#P˜6µo¶©ƒ†cGa)GAÓ€•¶LÛØV±\Úq&!qlÇNœÄ$'Þߣ­´¼ˆ¢ÖWù±=ÏOúÛÏïóSP•â¿kýà†'>Â%áÑئ©Ø–XÄŸåÆ’¡ÑĦH,‰G“áñd0•äž—F
+ ƒP‚@4Nºø&×– ؆ìSA6×iˆʆú4gýŠ=_³äéâ$2§ýås Tš³§B6p8³AõÀ[?Ùá¼·Þº…ºFÏ/Æ J ¾”B(\,Ei¹H¦àK”%dÑ¢žþcöPºsrÇ-Um\Y7¢¬¼£²^/3Giû°Ä>È8ú••ª¼7bòꄬú¦Êr[i¹£`ïÊÙ¹øaDLâzZ”Û@î«òéWYóQ[4Ö¨œf,ý²Š~†íWÚ˪†TUC
+×€Æ5È8ïR•'­o¡ª¡"„¤XŽC(Œ”Â$D2bBFA¸Âä$‰Š×˜Á¿Üz÷5Ë£NS.`ðöèã~Әߜo¯ù³Žûí£œ3ªï¾Ä¼Óàš>#`aÊ­šðk/ºí#Îð
+´è¸-CnÝp¨22g:µãnu*`NxL¿uÀmë`ó.
+¦8՘ϚÜlÓó IÂRLY… N¶ÛF¸e÷<®Mub¡˜/"hX(a¨¢/×U?/Lú,“~ýgŽúôºªö6`L ÅHƒqHü¦ Êtã±m‚l·bìWÔdÿIZÈì%ßÂsïH"Ý’ëÅÑå™î‚t•Û'vS{(pNúí¢é½ÈƒÁ£íßyœ¯òõá“®Ÿ~Éô®±b¾ÐŠW× ÙSèÌÙ‚¹³EÎ.˜þ¨hüTá£3 sþ sî{S¼˜ù°ðÑûŹ¿.<Qò‹u¤ŽBJ‰ïÐ"9Œ1@$°l)©?Á6Þjêø¼vÓµæÐ¥—Ü—\W¸‹Í ‹=ë¼7Bóíòÿ©ç€@úê}À×ë1W€.š|ŸUýè
+0Fs ¯ÖwÓÅݪ ~âòžfØË®9V¹öTõºVwÜ® 8÷«æ­ ùæ³wàÖKžËužËµÞ½¶6†!¤ÓQùpz&—ïð¹Çþ[ûÊd2߬À!¹Ù¹KŸ_ÄQL
+‰
+°)ÆàR0²j1j%©Ü.Ñ—™‡ä–»
+ GDf‹Òlœ¶DiKDnVÙ¾PÛn(QEpÈ0SÕÏTÞ’U)YˆÂQ
+œ¤E0E R:¢¸M©¿Ù}µ–»Þ~Ö¹Ò
+Ó•
+È4&±Þ/Éc|Ã8eÝÞ*Ñ}D«v#Â&·QŠ¡ çqpƒ ³ †DöW*¾yÍö˜g:ôóå¼W²à’.»•™'ÿ¿–|ºE`Å{Ìå˜n7,ÕK^ùŠWŸ 8×\ÚØY¶«l¥Uµäw¦‚eÓÞòÌk²¿jÉ£\i“/ø”é€zÖ§¸ß¥ˆ-Çl´Œ]€£RœÍ{ÇL¬å¸+ž>¨›i•/ѺŒ ¶À¸šyüg¥‘H§*åÕ¦<š•`ż¿|®]6åvüLI   #x ¢Ø9$LddO­¾Ï}œ’ïdÏb™nVæ4=šŸ=,¿—?{ÎœÌöÍŸz>{ÎvSËGÑ¥3Üä)
+ dC#ù~÷q9Ïä­ŸyáØN¯ˆÀ!—)DŒÈ;{™½ò“åÏŠW®1²Ÿæg¯<—¹þÃä
+ÿ|kµ¡hÄÁØ8ÅF؉IfAHYõy­ûvCð«šà ë‘áêÎaç¾Õ.Îw«½#6w®j|wœþ[5ž'Mù{µþ›5n
+’ýÓKkW¡‡W ³×_Hÿ‘1rðîøþ˜Íå#…Y¢BX¾…]M–~f}ý¯[½_Öyon l=rÏžãÇpµç_Î
+ÅÐêR½¾¿I6Ø$ìmP m.E@©{†Z¤}»€+J.6RƒµÒOø—/—]ÜVöñ.ew3¶½´-”Ã(ýêë4¶‰ì
+G˜TÔ™Š¸£_$þÂ4‹
+Qü-›rŽÕÍú©ø‘Ò´·fÞk^qêò.ÃD¯*}İࣳ‡Œ¹¾z`•¸ÿ?m’F®0 ha5ô2ŒÂ±Å%
+ãaU|\L½_f|L™gE¦¤˜ž’k'eºiÊ¢*ÃTUH
+ü@OI“¤q’¢§dô¤œ䘤ÖbÜJ¦É†¸!,´†„5€+€
+óS=!5­MGVù%ez"1|)3NHë
+fñyií0¥þ¾€b…;p„ãBD$ã%¸|“bn‘ƒä¼Ý( öX²þÊ—r½¿KŒ1eèr~-uÚ«Mô*R~uÚi‰û á
+ËNWužµîûÏ-Û¡ íÐÝ7í=·š=A›wÜÆmžOìž›ÍL°Åú·›<àáz«ü}›0ænƒçóº5Ø\µÐY5EÅŠsQÚAVüBÛúQ½ó¶=ð‰ƒyXÇÜuøÿÖÐ}§ã(x×ô·ëVÐz#h÷[ÝÁF÷¸Ýt0
+#
+#
+áAÛIˆÇGE0—c’bžXÞ¦Ûœ<Š¦L(Îff®‘ â 'Ù‰T`&Ú—˜íKĘpâõÍÖH$új<ü“Hì‡Óñn0z$Þûä W½R/ƒp)„0 ̾^»Ïöª²líFíŠy·:ä7$X#ØÞ'úªcþÊ%§ùöÁÚóVÕš´ªKòk„U&}šŒÏŽ¨1¿*Úože«dàaØöœÏg3¬iÙYžñ¨³þº¿v™(tK1"†ÉϚȈ»lž1e˜†UÕœWï¥7*ÿ2í3&ØÊ Kç¼Æ9!éÕÆÊi¿<‰³ºÄã3½x¾Ò¡jׂu€!¨
+å-0,á”ðð‚„0_Ä‘p1IΓ`/¨Åö·ÍùÊ—üôÒaã’Ó˜bµ F=ÏÒ/ðš²ý¼_“óêsÞš9 …9çÛ8E{5~»N²…(æpDd‘`ë‡æ´¯&á6ƒõù™kç> Ž¢p@Fqy:,ãÒEú ánj5P™ôÔ}ÜY1ÃÔ®:é€Ð(éVçz-)_eÊK§XcÔ¯‹-Å@ Æ
+N ‘s"Ÿ¦¸(Ç#c‡æ‰?ã«W‹¯œêõß!`®’ÃG0 ŒÁ
+‚ߧmü»Ýûhg`¼‘ùÌÆ<n`4î:ØG5îÛÍÞk»™`“s¼Îó¸‰ýÀÑó¤‰ ÖwßÜÅW<¨_·F®µxï41OÙ{U=×ÚÙ7Ji²&B´¸ ™,;_³ïSû°Ö{¯Æu¿yÔè»ÛèºäSßó/;Ë}Ëæ-¸Ýl
+0:nì·N÷Ò9¿œÓÿf¾Îb›¸ó8€¿mW‚–†¶çòœ>ÇvìØN|ÅgN;1!,Gè¶Ò$¾/ê¶{<tw{ @i(Ý­ U  ºÒîCµÕJÛD»ÐâœËŽDZsâý‡öuºJ£Ÿì§ÿ<}¿Ÿé1®Uú%Æ•Xx ]ñÖ弚¦Ãv\M<¯ä¾XŒI˜âÙÛ´O™j}5‹žªh|¿q®Ç´·‚À1†¦Œ*¾°«"¬¡èsQS¼»¦Ï ¢ˆ€d|ª½|&X1Ð¥ýŽYh$Y3¤}–LH“U¤|ºœG·PÏ…ôñ`ÝbP1®XŠê3݇n©YÉ 4ÃÁ
+§€áù|>D!àí³AŽ¡ª[´ó’Ür—u$Eu)c\d}B˘ÈøÔðŒÌn[ï*Ìä–)ií¶îŒÈ£å¤°„"Êp.à‘4!à“ ˜Š(Á¦ýñW»ÀÍ7- ¹éÞõ½>ÍRȘ ÒžªÙ 9±ÄWÝhXŒÙæ»×ˆ"@§C:Xÿ^XÌ/"1±„K³èF¯ý×b ^l”Ñg`å“R¢dhOy6lškr‡ª3]õ~½ˆK½pªÃ¸Ð+Ëvë|æù5¾_3¶eÕ… y~Ÿ
+æ'„MÀ!àÁ'øƒkµÿ’W7µ¿a#ÁSŠð0Æ—Qe½„厕¤úÙÔÛ¿(“䎢sGñÅ~4„»ÞY|«dü¸,ÿV8RT8úü­?JËÉbˆ`^ÀqñËw;¥í¶ç¤Ô 0#ÆðV– Q±ùou]—›B7ë#5žË à‹.ÏðÖðõZÏß}Wä’Ó{BÝü²ÂØ$’Ÿ¨Þöe«ç
+€–ŸÛòë}#à\«ïMC+¡ÄŸ%ý–Ö¦®ms0À¸Ôìû¶Ñs§Ö?âŠÜiŒÞÞûÒ±w¸ý PÓm«çFcpÝ5ÒøÓ
+•rá2a¼kÀyáA×hª/?ûÚÔd$ïËNün:q(™ˆ¬·F’“áT*œžñ¤'_ûç¹N„2$Ì%D0Š00™¨5’.†Í)_Õã`ÕLÀ¶féÝ]5ufê¯ö²¼2Ÿ‹ÊX‚)†!’íT 󽵋aÓLH‘õ—º¬_;±%Âú³z–ÃP")F¡Å¤C‹~Þ¢ý®Ó6à®´à œ V&àþd¢ï‡­ùˆ*ã¯ÈÄV×^Ó>ãZí?ÓkœŽ“ àš³Þêt´:q z<`þ ^kQbŧ @™‚19J˜¸ð!Zù5ë×Æ…¦Q‘aLbšZÆÄö1¡m\d$³?PØïˬqQõ´ÀüÔðŒÌ-UÝCÖ1)2OŠŒÅúĺqa–±ÄeÖ)ýÀþ…Ðô
+!”¡ÿe¾Ü‚Ú¸Î8þØ‡Æ—Ø iµ÷]]VZÝ%t—’Ô\
+M±=Nlƒiu'}«ãÄq·nÓ™:Ø@ˆ§îC’´“6mãØ­±!&ΤÆØ!0Qå5/™:gþ£9³Ú™svçÛï÷;ˆ¦ÔFD±b!†
+W΢+gö>ù5=z{U$w)œD–ÏÓçŸr–X9[üà¬\CËH –Á˜ˆ ¤R)ƒ’†™ êu[Õ`ä–·`÷vmr :vÇ×zÛ{ìVUä‹`Ë׀šØÝÊøÍÊX#kÞ)B)‚¤pJHMâ0’iÿŒ|
+ùÛ¿/å|‡ï¢ÝÁ£ºÝ(I‹”8õ[[ý` ›Û¡è@%÷¯*îF ýnel¤29쫤Þ+mjÒÚXït…n…Êbw±ÿƒ ¸¡ÿ½
+M!ªŠÿ±â¨B$B#¸„–ÌÎd€Œ¬¯¯=kïøî±ú-««…åËWûq!%çÑ@£h¯¦¤§QùAôr ¤·Öø{¥àÞгí”7ö××ÄÓ“%Æì"CO“çâÄ&EB…O\~ægåïRêQã‡/jß«Ñö5ªú4½ ªK5ºîzS߆–H{·Ýšt—ê€öèûëÔ}!u_µ¬x¥Qe¿éU/m! A
+ß)¤ ZH+Q…#"BGJÈë­¥ù¸è ùb½q/´;òQãJJ»Uõ°¡
+ac:jx’(ÍG—ªUé.9üœœäi !\´ÓÄৼšGœc!¥Í'¬éN}wµ‰¡á"‘@ÂÛ+%•¸èô~קmŸ¾è¸Ÿ¬Ê'|O¢¦lLn^>bå,¹„d˜ðÆr|ܱeõÜáÚx„¨c1j¾¶ß®ðX1ÍÃp¾€8é£Öß‚žžÂ—ÎñsgyKo£óç¶ÝFVßgÞÜ[x«¨ð6–ýñ§’ N¤Aø2BŒña¢ÀçNá’6­÷/ÍwªÛ¾®J~UÁÝö‡oVqÿ…ï¹áJîsËH(þçàÑ}ñqOêw‹f/,  „k¸GÒ”´ bWñkúÐ}‰•ñïKù›uí7«Ãµ”
+ÂqFZÏ—¾ðоðíªÈ€·mЬIÔ$ÿQÍ]ó½ô®­^'–k R†‚»¡j©æãÚèW5ÇÿYÞv Å"›6²9×CñŸ3zZ²!¤ºzõjá)Àþú³öŽïkß6d©°\ê÷Èi™ŒO
+H×`{kÕýõ
+nq‹î·x21_¦3NS†|Ô7ù
+)õD\¿˜(¶ µz2ƒìþ‘aQ¢ˆÂy0MZR Ã1žO÷ã‡aïBW8Bæc¦\ÔTÌç83hæ[µÿõvð´ùãŽÅ¤ó£zMƒŠí…Ä c)!$Ç… L8ùÈEZ5¦pɽ³
+OFR:-¶¥¥¶ Ö9®,×Ë<i¹kRâ˜Ù§$®)élüõ¬5à’´Ô9É”M°îGr÷ã~lD˜–Wfľ)±{Š)g¬åÖGZ×7÷
+Rò<Ÿ/Â H
+†¡A5«ý°²ùV 6\*?|=Ðv/”ös·üá;•Q`&÷üÑáÿ2_gÁMÜg
+a”@œ™ ™þX­è(ö0·WëÎT*;ʹ®
+uÀÉŽwyåç%òÅ¡Ed¦R“åÔ^:H+2XOjOWH¿|622»Ÿ•?RyNg8ê»+5`ùépÀ’ÎSÓS4¢=]–s¦JßV¤ëŸ—1m%ªöÂÜž
+í{…t•  „D&%ÁITNA´Ä,Ó¿í<y©alòÙ™æÖÈøLl)}ì£ãã2Ñ‘8âe½_¬ÞLš&â¦UÞ½Üè\OØæRy ¯›ç[¶­z¯ó¦…”÷ & 'DÅe ñ=*ñÒOÝK‡Ý‹¼î^ÂœNh¤ÀÕs×Á`³mÔW~nžæ] õ9ß6Ùnôý2Páߣa!'ú!GJ`1jx9ã„—ë‹»Wxßo˜ä.Çm`íý¸m5iŸ™æS¶íʺ1¥%ú×LRTŒ¡"Jeá(‚0N˜þQLêŒsjû›7Åø†ä¦QÆz+h¬Cœû–Ü>N;˜¼9Æ”ºê—7¥ôNrž1Öu—q<w¼ 1Á:GÔyC:ïmwXéWúFžAÖÞÜæœ*ûˆÚ9£pÌR¶I™}Xá˜TÛo²¡7 “L&É2€ Q1a,„¢™ä]Ÿ¢¿Þ>wØ1Ëâó‰üíÚkõ¦õ¤óáëÆÕ”yô ÓDRû0nOǬ› ûH±Úl[¯Ó>Hy7x÷Rƒ½³‚C%(EbÄIX”Ä$T†A"‘ˆdRˆË¤åB䥺ìŒ;IÏl£a&aX‰ÚWš\Ó ÆYÞ žG€ù˜€·+ÿqS:’ûàL ÛhÜS«%Y‘
+P¡TjF…¿¥—~Ï®}¤¼ÿhõÁNkdñ¤>ý>ùV%k—âñƒ,˜ÔS€YÉŒï9 ú‹õ…\(øñå@í‚æÞ¢äe_ÃÕP¢·(u)?Ò_ØøÂد9‡ ¤@ Â$*ÿ‰Ö%Ý%
+äæOÐúÝ„ü-•÷ß¡è…Ý ç
+ëwZ# ®ù-)ùÞ
+&a»58¸ù‚bä±’<<öæ‚Bh ¦@Qy•FÕY¡é®Ò·–›NíÑŸÙ«î©f?/cÛ‹ŸFt]U–SRhD°É;2%V«5Uôq+%˜]²ìPŽ¢«TÞ]¤n+Ö´—¨ÛJ²»ÊUmeÚ¶rKç^ý'E;žgk¡±«RÛQ­h«VvíWwVgŸ)Ë ´ïÏ鮩*ºJtg+•'ü¸_NÃ9LÑ
+ó Ê
+nà¬Ü1MÙÇçPœÒ?¡ðßes·~Àº¾–ùâp6˜C1š"$#%`$…²ÂLF{©&tn$Ík¼~»öÃJÂy?j™h6Ï',à‘I7å­
+—fQX¶þ«·öi[ü¿Š¯S¾Øm?ÿM(ò÷pýÅâäßl5û³ÍFœ€!!B 8(ê8`8)-i?v½r!œè %.£—ÃÑþð³ H_8þ]<¦HßnDo r-œ¨19Qf2% Ï£oÿfóÛ笎ïxôèÑ“ã“ïL&“ §q‡ñ,Å»a]gx§»¹öŒWÙSÄöìQµ–?+Ïí®Ìî(5tVH{ÂÁO÷1a€ÄIX!ËIDJ¡¨á“C" Ó‘4&ɤ²LG
+LmûeÅ;çSÿ¯ÎJ]{ENg•¾£„¡£JÝZŒŸt3ÿ%¿îc›8ï8€ÿ³Vm*'±ïýÎw~wçÍñ['Nb“8`hy[FGÁÉÙ¾;;„׊mt[»B€Ilb*/µ¬ë4í •ÚZƒ0tt-Í qìœíرHöDLÕ:ÍT"㬟N§ÓY¿Gºçy¾ŸG‘ER9I¢J¤$J0˜B¬íöµïøþxgçhlo4¶=yllw,IˆìxŒMÆù´¸]Œ÷Þ‹ñÑ$7‘
+¤Ç^,¤Ž‰øîxÌ?1½/Œ'q<‰Í©ÑWíµ`¥â¨‡Pi‹æ “[[jª(×ø§ï”B*X*#u0Y¯+ÆXos”³¥[ž·LwWNuf8cN°€~dWÍT·~’¯Êó¦xÄ44%„ÊDDóØtZèVO†M¹q6dÊøA/ÛLÐ9ªIDªfzLé
+
+8È'qŒ±¦¿­S¤ NJ úÈ"Ýg°ìÀ³™ƒ%ù}²¹>dvŸtæ0žÚå`S}P!]Ìôc¹~"Õ‡N @¹_@ùýÒ©×d󯣙~eª/ä÷ÁiððˆúâžÒͦbµŒQÈH%,Wà‚@j)Äj-çš·J[ “k+Ø¡–®!oèvûg_GWMË0Ç52\S>M•ǤÅE4MIõ_Z×;Â
+”8ÛYy‡·Í ‹®‘I¾~°™– ÅZJÉȘ¶U¶»"Å;&·9ÄuŒµˆœ#Å7¦BöTÐœá­YÖ‘ínûÌßþᆦ6Õ™5UYW–µ=®ñ¤"`+¶æºMàðcl›-!X³Ak’/5Œ„l©žÔÞôW­­GdˆL‹ÉÁçAe„Á„
+ ©Ë.1ŽÏ5MO<ŽŸòV5ÞÕ9>Ó×ÝSYD…#ªv]ѸÞÖ8šiºŒ€±âq$&y¶VýÌžfj8hŽw.ŸïrÍq¥Ñ øs>àH³u¼qR°L
+uyÁ‘åì £ì‚T¿î¼Êm™ EŒT%ÃU§Öiø ªÐJ5­F—õšâœ'å7fys¾×ºÜêu^|¡l8Üøþ¦FMè :P§;êÖêáoÓ%ˆfÙVMý#â™
+×ÏtÙFÃŽÇ5ÿÓl-gŽ¯
+Áƒ0L3Q¾Rî»â úd&‰Ä0Ä.Q|âa† LH
+ Š¡$!Aˆ«Ê?ma¯7tN¸wª8À’ µ Þ¬ãnÜS¸äao×ÿÓ®%e«P‡a):¨q Z›G­{Î:÷Ÿ*ß÷~YSÐPñ–Æ}Ä\×­«
+ÈíUU‹Tógûvid¼²c¢:ø…Û÷SG#Ž¤…`X˜ ?qÈ–
+¶Î_›FoO±d#éeN^ŸâÑd¡òh“î·»egš^tË6Œ´éN´Fvë>n$O¸Uhµ¼»7·‚Ä{ìúÛtg÷¶˜z½²–åçj°<43Òůك­Úß·S¿k¢G½šSÛn§B{ò»à¬Û.ž€D7ºKyª^yÂ#mÐœ}Eù3]£ Å8*€Õ¨²€Ã(JêRÖþªñƒÛû©`t–‰ÍTžöD#oÎ>ìžžéŽ$²j$a"³]ñè‘™GÝóÑÎéÈÁk×{êÜ
+”/¥HšÂ°V¹8tÇü¥ëê÷ܪ\¶ˆ3ÚÓ-e$8D˜Bóo0/†õ i†3§9C’Ó'Ø™&YÇZÐQ«ÊG¨BD€`T1çP­¿Z¶vð™÷ªÙbÙo^<äœ —Í°¥™@Ù:kIK#!]ŠÕ/p®¿µ»¼jEò´RÉpHN‘°ÇG°’$Y‘ìœ*rÎ*ÿ—n{;þ†Ç´x笸""uÌ(ìSJÛ¤ÒzOᘔ9çdÎ8]qY\Þ#R+ƒ1*—ˆ„Pò¹å·|¶(뎇œ©®Ò‡¡²%Ƹ²$ÙÒÎœÛ
+Sy"‡ßÔ¹>wû.y¸/=k5 Ý³×½Ùkeࢗ½éå.T¾qÙËÝñtöêQ"aPH\ªï¯fîÕ>gÙ'Á1
+Ga>Æ/”b°Š"D"@Ýa$…< ’ø÷t–íÒ˜ümOè†'ôWO‡ F„!Hq‘j~~~K#[*øÚòHÖÖÖžü,ùãés<D“ h*•º±võ‰Å™Þµõ£Íš‘FíÉfÝÙfÑI¯þÔ>×[qB #Šv«ù£W6ok,k5½ßªékRÿz¯ú½ÝÀ0êÑ&åXƒr¬Îtz·éãæmçÇÿ‡j¤ 
+ Á`¹D –æ* Ʊ£¨ò_²Š)eå}™uZn–X•ÛÞŽ¿á1Qä¼+·OÉl¶)•}Re»_dû
+@±Ø|·È9—EÞÏä®’†…E„T†Q
+ŒD6©ø\›$Û£ãr6í7%ÖÍt;
+=5ñ¶âñ)°z,ëé;Ô“·Åé÷‘ô vêo=,Ç??Ž<}/ûñQÖìqÖåcœ6 V ÏæIPš#4W ™_@§Lu×ýß)j†€À¸Z-^³”LüõRõJ9ÿsɹÿzs¥,0â UE¿§+… Ø0AuóA³Ë–Mð9ˆä#8†š'4\lŸ<¯ß±ç²‹¹Y»åi…þ¹êh-c†=Ì¥òШ'rËŬ×ïßÊB7]‘‹Î–¡²–ûNö‘ZŒ$%F€£$©¢Ä…J½Ka,ë¼´q‡@·CbTàÇ
+ø?6Wl–F®U2£®ðˆ3t³4ú®ý[4Ÿ\ `úC„d$ðŸ¿/áÊà'ÓÝÊÊJ¦Ç
+P‚ÀXl F´T«úªåýÕ9žÚš¾e¿_Ó_§éöåô×X½[û–{‹ÅÁq”ÀP—_“cêÜ«î¬ÉíòªÎU*kä}^ùÙJ]W­¹»ÎÔåמ­Ñ÷Öm>?þG#À‚·ªžØÐöú¡Fà|szªM§½¦ÓUÚ¿ì\­ §¾ÙÉ*¹_§†óæ¢l(%æ…¢Dñ¶þ¸ÿöøéÉ#³ñãóëidìad6yx6K&ZÇþñO‘˜ââÐ
+`Ae½%–$îi‘cLd¾«´>”ZS¢ü¤¼èŽ4ÿ®8ZîÞô8þŠ×4?/!)x$sLÊ“Ò¢G4,û]¸ ΟØà“)¹}œÎýgNþg9ξ½^¤RóØ$Î¥y¸ðY$¿˜Fÿ²Ët?R˜<è„ I‡rW¢–4cNµæÍDrŸ;åc…©pAâ@^",KGôóGz¿i®M9Ò.v¤BÖU¦p.h†yòfÉ|P7V|Öê.¡I
+f¦®tœ‹PjœàÃuϹdÖ¥FëB»}¾É”<X2zîñ¬WóóbÌ
+AO™' á\¨‘DÈúž ÍÍWõFRŠ’Y4÷*<æ—Ü;®Y9±õ‹wÙËÇ 'ñÔ tùj=,¿G¦JR'ã§8} ˯ÂÛ„8)Š
+ˆ/AÛ9ýu@zkµ]>x+ôÂꃷ–žZe÷.8/e·Wú7ߥ&ŒF>¶E†È‹d“(QI{Þ/=u=x;[šlZO#‰¹àää¡™©¶d"ú£ÃE4B\@PÛ””ôj0o)y0͇e+1Ëìk–¹vÝFk$4~Ê›;w›ÿÅ~µqßq
+D1‰
+AkêƒÊäYΗm¿\ÑpÕå¿ájºîZàÈÊ'}ÅÅtW†º*‚½¥ÌmGh±T?¸ç`/»|×Ê蛥¾® Á+öÆ{ÎðÖ<[–
+£^“×}PõqÿÛC;†"ì“èÇ÷LJ±¡æèЛƒÃááøñ‘Ÿ~~¡N„òÓ„
+ÉÌÂ…—f tñæ’xÀdŒ)ÌG†XH·ÒÚoŠûr烅×ì[”ÂÌ JJIÅqŽeá(vdP(‚a‚‚¹ 2!D sŽ¯W&ÂöÙ°vœÖGç’Õ,IÑÚ© Œ½ÃlÑØ^Ó[8(ùAAÄ/ŸcKþè-4’P:L
+H¹ˆËOÇÒ-÷ *¹EY'äöÇ ö0ÉK^xì~WËR#ë¨ÄþIa#‹¡Wˆ—Šˆ”#B7k±; ÎùsÕ= «ã¬a2dû÷2L2F ”ŸduÑfÝ㦂¥®Ãh8?rDXë¤O5éÓ.¬É&Ë'»4¹ÈË!Â/äðj¯T|×çiÊþUµç¾de^ÜlŽ†¬£![Œ±M²Ú•îÓDSA2`m2Ì‹¶@€žCâYöÜôÄÅì/ÒâGàùw°g-ðØû¼™V*ÕB¤Zág­œ¹Ö̧‡áéÃèü¡ôoZL?ôð²‰ï¥^Q$#¸BŠ4ï#“÷’;x£‚Y
+(.´[\ð'玛¥ 0F7 Gy€çZEsŸ3Ô¿¾éëRú«²`WÙî«®Æ>§¯Ïìr4Þv.®‘Eª¿8p³2ÜåòuWøz»:¿7o3ŠrDÐH6Ÿ_%S׸]ÊôV2]¿óꦋÎÆþò¦û¶Ð•ôgnÿµÊà?=+®À?P_9ü_Vѽ¶ÆNÃk|„/‚ ãõ­À
+JLwÌ£8ãѶoÌ=Y«ìØ°â)Üæ¦ÎzÉNô”à'çLµòŒW{¢Z}zSîÕ­ùH’ÁBˆðHò¹ÒQÎ
+¶~¶ýËûoFÑOâ“ÍOÂc‘}‰Q&:¼gt¤yè»Ù©äBi"B#éu
+1 HtŸ-ÞdMÍ©  DQŒË6s-Vq?ØßL@É}Å«76¨øb(=C@‚LÀø8À¹Bd˜@‚. 2TóI+¼ö¸K2ö£õcÁÂÁzej¯m„^²šR´&ÎXbÍŽØCÜ—7Ý ŸaL£aU"¤æ+ûY±&¾Ïˆ(#2¸
+¯‡„')ËuåbÝ‘á aÝ“}§‘ÿ‘º+5Ý“ÊJ¿;’jMÇÁ¬cd±&½„ä\¨3Ï0æ©}Å1°xüꭇ̠¬56Çim„V'÷,½öèb>s,l™j2ÓÆÉfë7»­›¨WD‰â)á`
+îS}ºÞÙÍþÙ¸TÎ
+É—Z^ù‹õ¼>çyŸç÷–É•8!„|»LÑZÿ\~
+蔩nÈ¿åM\²~³¸ª˜’ÊxR‚ÃBÞëž¡*îj;änúâWü[®‘«~𨙑JæÛòæ±`ûÕ
+ø0zÊù $úÞz}ÿ®Â0XÊôO6êöÆldOÕ©’õªþ½šÁW_–F
+R … zj‹RuÚÁzEªFy®ÚØÆëõçv•¾QI»¤"|Š`X©jA#„Y½µìÆÓÿjº?Ñ>?Ý97Ñ9;¸{3Ê(pþ(\±ƒÇ?a+αº4zŽ Pd!fZ²1 èrSqóVw¹,§Ê°æ9®l‘sf 8ÃãNßða“Uí’@R!Ÿ@ †¢üýFéÛ»œ¹Òɸc†ueŽxÀ­p¦E;ÕÏr›îÆiƼ|Ä–³ÓR§9s´hœµýsÛ!Ð0Òˆ4:]Š g Œc:÷´Ò7.sÜ—”NÑö'jç˜Êö=m›¡Ü/}ŒþÏ%~ C´ý]ú@ážTV~E;~G)äµV@JÁp™m²Â#žù×cœkUW”IX¦¸’Ù„Pv¡­h³uŒ±-É€f7T3Åè¿ÜoCqZ†æo“"8Âo+_ªæ٢ɸ~ºË™m×ÌFm9Æ›‹š3q}&nKG]ξÕçtŽsÍ2†ÙvËSp}ˆ™æk&ÚÝ69‚á@-fâñi2{‚X=ÉÏžÄ×ßgN‰2§¡‘7$‡œÜ¡¦,O †±ΓìVÏ»ÃÃÀÖ;•#ÞÈÍrf¤"
+Lr£*6T¹áÛ~îyMÛËè­*öKoÓ÷¡ccžøoHtÉ×âWÉ„0OćH‰(!(b¸Ú‰q0Pv`0§¯õp·}ìHeìÛ@ÛfóÞ
+¶_ ²×\-hý: ËÃø
+!¢‡ÄÝæÝñ÷]{Ô¥•a$‰ârB’‡
+Á[o€”ßÔu\ö2c>nؽ鼛+‰ ˜keÍ£•ña_ìŒu7A``WbŒxuÿ¯V—Wž¡ ·¼üÂ4òì³²²²Á’µõ?¡H1
+#rG4|ã™]©PQo=P¯ë ¦B[=…©jI_¨$Õ¤=ä‚ >`rH,ऀRñqJ ÂaXMÐb”ÀN!Õt×úÃ
+Óý{^–F”½õ…ý†Þ¡»Æ
+é>mPöÕê>k4%÷iºÊ03‰
+"
+‡©X.Þ.$¤0ÞV¬ëŸùí½;š÷Äù‚<Pô棵œ3WÏǬ€"9Î(’Ž7úO‡ €d«»\&¡ŸŽO¶‹¡g®Ó´ÐV¼1f㦉¸}¨¥ô¯ûŠ/¾f¹Ëºr¶µ˜fQ€–xŸu>Ž8£%K\ñÓvK–unZ#]®Å˜a1ºáŸiÆ0u|Òè°R±
+eg_×)àª$„Rr²ø/­Ð“àÙ#/?þ@rÿ·ÈØ{Ô{»HŽE(§a crÑ<4þ{[-È÷¡€0è‹^õóÿð·\«J „… aT_8q%,€p¼ì‹oTÚ^ˆð7‚ÂÅP´/´Æž[Õmÿô·e*©T
+~•
+Áu0¡‘bŒ,cÕ&Œ¢œ#R‡4Å–÷úø+Õ©á@¬àÖ¾ø5¿pÐìÃ)!‘âÎ’ÔaÏîŸé|.…
+ä‹[qH$'():jP²Œf©õVÆGÂ탕ü·ÁÔHUv³5Ò€Öz«b×€%ñ~_ìë o%å QÈä3Ó9 `@‘Ç««ÏR#ËËËk/«O¾øìs‰HLâ„BŒ";ã©f®#bèÞ¥îiÔuñOQ\O×Ù\öÇ}[¬¨â
+\"ÇH!Î4˜g²úù„åAʆÌ_
+<°6ÒŽÙäÚq³§ÜRܳ”qç3ŽIÞ2µ/$Ü`êæÓ°ÓÌÇ-9Þ²q‡,¥J—â†å˜a–·N¤K§Ò–UÁú„7çxÓDÂ9÷Úw4c˜i_£×¾t"î}ßgÔ£¢­ÒW¤¢$åÛ0I‹\yƒsim£j×<îW{&X÷„Ì1FÛFåvU£*Ï÷Åžç£/jCêëV:FXû4㜥íwå¶Û,
+ŒÄ$0!›”Æ£¾½ßT%‡ªÛ/ûø?˜ëU¡”1£òˆ¹xéš¿µÐ¾Ã~áݪ&z[‘JŒPA‹`?g’!(ŠÂàþ4ƒ˜A˜Ø­µŸt¾~¶*z¡6 s©2Ö[›<lí ò›­‘þpÅÏÿ;Ð2äûIªÓûåFwÁaôϧÏ,?|´úl²îŸu¬>^9°ÿ-@$šUòr§j´õ‰&cG£æX5×U¿Ù)¬ï®Õw4˜G¤Aš©`érFáã˜2Z£¦8ûÌê×,Ú7ÜÞRY«Ñ|´ùÿoÖ¿„8¨BûšOïaWgœ² bØVݯöO^}^ůUwÖë?Ý£ín,înÐœlPuïÔ}²Sûi³á³fó‰%৚u]ͦª õº—d/#X‰RF’Œ"e…¦ÄHŠ 1„a:Dœ0pÚ@ O'óéµ7yÁ’OÙæ³N@‘ûQˆþÍžr“•¹¨i*©ŸÏZÀ˜åsiçÝ”é~zmë·˜:Òç£Ú™¸ü°|›ë^²r6åZ Ë|ÉRÊœOÚ§ÀÆ0S^pë¬õf‹nñíÐh,tÈÊÈ0h+†²&ƒvÀdÑŒeº¸ú–Òq›6ÏÉËÆ”ÞIÂv[áWyòLENî¹¥tÝPÚÇçsѵ!u“+¿L¢*›T:ïÉíàüyKS~UkŸS8¯3¾WE25±Ð6S°ÖOÁFLÃÙòÅvûœ`É's…kd&íXäÝÀ3³|I>í;‚•˜y¤ÝÛ`¦Ô[ÀÞ ¡Ñ-íe[Æ´“‚e&ã~À¯9|! Ö©ùaÔ8Ÿ0MgÌ Óf¯ÓE`­¸RK>mšnµL¶¹ÅLC|…-’a3‰ª'¾Ê¨ö¹·;X+‚$?"1RIQ2éöýÅÖ¯C±ë5ïœ $ÎWð×½™+ÌÅHf :ÕWÙr5œì¯ú#©°0P»Rg.פ6*m½ñ>ìÛHúš/6RÁª½LŠ½„‹}Û<øÉpÍÁë^á?žèˆ? â¸ÓóM“âíb•w˸¿×
+ò›
+÷Wr÷¼Äµ u>Y÷ÝF_ë¥hVâ¹+u}©pÞRÚïÈm³2Ç‚Ô='v/‰\eUdÎÛånV$ƒ ‚‡H”%èAHf‹~áÇgÓÖ¯Ó–'¤e#í|7¼èw¸š6/uXÁ¾Ë±Æ9Ú3ŸrR–Öð Ýõ–MŒCž&÷*eXx·òYÚ‹;Ö×išIëÓö­¸;G9º\ù¤k¯÷é+ß"Ý‹]ž…”f›ÖåºËŒs™6m&@ÒÕ
+á"Aù\T.Sª±W!o@0!D•D± †«=Ç®D˜+æR09HÞ´O†N\ ž
+¥^–Û^Š°×}ô¸Ÿü,Òq3À|Qß-- £H _üÛ:êvýÉIWâZ€®M]¬OM“#µô”%*ú)¸ðûöðTMòEǽ GÔ)¥¯Œ‹„ !*ĸX„£ÁÇuUƒ¶·&=ôMu-D_ ÅGëÙÑu#ÄNUÅïøéaÿ‰ápüºoÏidÂOO™É ˆk4«£ÿHŽ…."PáC.<
+Ù ¸@$Àp>Vz”°ýª¹<Û*ˆi†šµ} úÁ&y¦öe¹- ˆÿ(ýÙZÝO
+y˜šIZtjÀ'õ’lTÛÔdÂåÙ:u¶Aî˜u&Z™‰=/¾:Ó VÓÜì¶
+ˆ@ ±‚‡óø5¬(‹1®¦Ý¨=×æ9Ý\ÞÛbÊ4(²µšl›:Óèhÿ(å9ëÙÛlíi–o–% ¸áÂEB –—–¢˜„àãB¬´Y/¸ËÖm1îGÉŠ<kZN–Ó®åTÍ]½Ê‚òGùŒ5>Mý“F@Ý÷¯Úëì÷²´™¶-$-_ÓŽuZ¿@ëVت<åX#•Ê=ÞáiÔ!%E uÊAq§€8A~Ob¿/uÌÈœËÜ»Úw£|­}Ô,@]Õ-µã‘Ä1-­ú™Píà—p…%•‡ ªäà“f<GVåÓŽÕ¸y;eÎQú<mü˜6ç;­yÖ²š2þT*‚EÒpÿ¸j‰2®uÚ€ïƒó
+³çûn‘*Òiʧík)ë*i]e¬‹¬y©Ë¼Á)û<åEÊ¥}Æj
+[Û›…­-
+L\Ó[¯ÉDÙú½¦Ý…¨4iyжP„éù²_†lçZumå·(‡š€ÔC
+±˜ƒó
+ó!X†‹N™êÆ#] Ã¥Zê‹9öMÿ‘šÄU?õÀÛ}Rî"€
+DØkjO¯ȱš$8a¯EX0Áç;J‚þã¶A®—ÁhL®?ëh ì,Èå:f$D‡Ù‰ÚN@,—Bä_B©-MoŠ4Êûv¨Šþ1øÊß=æ£÷kÝ>t¶ Å%B‚r¡ï·w6Á±µ¾™E4²½½.Û…­Õ|EùßÙ¯×ߦÎ;àÿÀ´š8¶Ïýâûýn'¶cÇNœÄ—ÄvlçÆ:
+ŒÆ±Ïñ ­´©Ó$6í¦VšÐ¦m-àÄhQ·»h+… iH€²ŒÁh ‚CÈH!{¼H¼@ʤN ÖWGÏ9:>ÏóG¿Ï£  C<düiÈTìW”b2 „BÔi†CÊ_ýתçj‘•ÚÑ]v‚F(’GµÖá~õ¾°¾¸IUèWúÔùu>¦+vöhËEaU¬84”ÿ¸¢Ï÷I‹êီ3ÒŽéÞË¿6Jii¾ÏóÖÈ" _„щz£å%"jE6ŒDQ
+c§³é5_/  È°Þ³ИI˜æS–yÖ8·C_É:oÅíŸ'MÒ¦™Íײ‹/5¾¢#Ìu0ÅC…‚S¢ H!„ýÚýõ nÊ—9áÙ>Ú:0Ù–¸Ðœ¼äbÖ«ªNµU1ÌŒµÄ§¼é|ŒOÔâŒÁ;U®ÉÎÐÅiïàxËÀ¹VælGf¬ d/¶ïþž¹K‹ q ¡PLa:íg&‚Ù _j¼5q¶­:Ð8Ó–\­ß‰|ÉéæÔ;®—ß÷$
+Ì'qТ‰¨ùжà)ÿú¼±•óg(ˆ/Â)Á¥jõòÜ ‘åjžÆtsùáÒââÉ£Ç`*‚q’€p—D_êQcòRLUŒ 1s!¦*FÖ\#ú‘^Ë›ý"ƒa®8٤꓿Ó#*E´b†án0 S>¢ÍUùNÅpXZ ¯ZKÑj/åˆb(´r
+XbÜߧ<œ0#ºBHZ ê~læQu¢3˜µ,g¡y„0$ÿ~D·/€!¨€ƒÈ„¨n¨_^K½Õq–|ëÎ'¢,DÕå°eȧ~Ë'Û×#h^Àkh.&ĵ
+ Ã2.Î…PÇäPù2ãÄÇïÝ´ëk~˜¬¿‘4ÝÚe¿7 Yɳª‘”~~‡cf°”†;Yý]Vwt³ÛFñ7r…Jìåp”$öÆ›b÷5•­"p^“5­”¡Ç,yŽ“ÿçTh×ÇJÇ”Æ~]f¯lWEö«’¦9™÷´ØൗI€H8¨
+PÖåå5׈|O€¶eõXD’‰õ½@D’‘¨êpLV
+J ~M¹C?Ò¥-w©KQM¹gµçëŠQð4C¹[3Ü¥
+ëK1p
+Ú†ý}ªÃQ C¡K;”‚ºv k!'õq·uÿ&ŽWÀ•†ÿ2ª8ÖÉôb¾€”b’B¯t$l,ô¨„´ÅÀºóã‰è ÝÆ|ÐR
+ZKÝšÝͲÂù2.àmØ“ä³]¸J,ÁøŠ x$Ëy­Æk÷º•·›çrÆ9Vù(i¼—s¬PäÙÕÈâ€a.a¿n¸Ÿ6,¤­oG5&Drp)Àø¤pÃÆ}ÍyÛ%ÊqCî¾*׬€ä¹Fžä’ØqUéú«ÔzIb½®iº.o¼L[.‹­³’¶sB{a¨¸N"በÜ©‚/l7Uv9æ3Æù„€d&e»“¶ÿŠ
+c^ëõ2ËX³Î
+cý.îtT/&- ËRÊR´Ì°õÿØiŽHÜ‚˜‘ ¶3 Š
+p±’¹´z Ì’$¨à.Zü^Û¶¿ùrçÛ’“ÞÁ=[“gýéu«ª^àf4ÀŽû“ÓžÔX;óMM3ØáÖ<')<Ò¶õï.äxg*ØöºÁ+¥I!Ž _äÍQ¨€¢ ¾¢ÓÐw¼-~&º˜ô¥@
+-J!áÖ’
+„¡YÑ…%¸S÷y*ÓêØõ4ð€” jsàöÀï¿E# &Ê~ðmn—FJsMº¡6Ã@SÙpFXZù$Z¤b%JÃ8"¾ù|ùËA^êÆyRØÐìm‡}¦þpÉ@«vOáùqÿxöÅÝaMn%•¶#$Dá “2IQòëgÅIâ÷QéëŒ;7K[Öð¾*GyB g­Ät•ÿ×sΫ½Öë13È‚FòàMUp]<ìÛ5e¿
+^ìÜ;Ôjj‰©Tˆ
+‚Š*b¥2ÚN\çøšK|õE¥õŒÚýM -jd1 U¶i®ò*g›j.‰Î%Çyµ˜ä´²ú²¦ö]u…·wŠ,NÁL)P%‚._ÍË
+ê+Ù±v ²ioô¥¡øË}¡U*„»„Aì
+qŸ·ó¼¯Tê„?qÈÙ5éOªUx““ô„;vÈ·öOt,8ê· Uª@ˆ2A´ò¢aA $©€é’b”‘CJŠnW[†­“®äIoj} 0<˸'vÌðÆFÆÀÉ}±]wÌ:éí9S—øÄÛón8nTñË—-¥YV‹²¬ÃAÉ#H%¥è58÷8Vƒ[uGN„zŽû£5]àx4˜8Ô?| vu
+r˜d Æ1€4Ç!Â<¬ög6K“1–†BÚ\øA矿ۻYÐøf¶×iÕÙ&íH£1Û$eÚJ[ ƒu¶QÿJ€•ãÅ " …‹œœRÐËIr)‡‚…VTR)•e^ÐæBú‘@u¦YÛ¿‚ßýÃw–á°ð,³š*ÁébT‹r”R­òso­×¿û“èΘúw½e¯o¶Å/µ( "G`"F2„&h”A?ÚhÓÏFË4Þ‡ )80*·ÆK)ËaÉDA
+€d¥á‰kLséšk]e³=UùxÙÿ7{ï]q kð‘k¤×2›2þ"fº1ç×ÕŠäìKyžca¹„â,BâNØUÐljy¾¿çÕ\bËߺ^y§óçoG1~Õ¡,Q ˜‚fIH¾Ñì;JöDú'ë×ñ$
+Õª‡}É oâxmä°?2鋆=ëë}ÕÙ@AÑ Sã8C³Lp('1ä‹båÛµk&k'úxç3æ'
+Õ+9Æ#üoë5Cõº=õ¹fã®°)׬Ì5»ÃÆÁFC¦¨FZ ™6v³C
+D0Áq\F³:H!,cK`DQ¾EoÞÕ¢ÚÔîiÖ÷·>èüÆL8¹4èSï Š{WTüùé–Ë a:tÆ->ó®6iwX ¨s’Á`ùP“©…D„S“bii)Ôr
+ÈùÞŸX^k(zJ9ؤi‘†æy&fC…R‡6´€ÑÛRïn³Íæl+FU6¬ôèG:”q—©˜ddH‘ŠÀÕ”ö͸z{êF»£—í¬Ç´<Á<‚qø`(Vòm~ã'‰Ú|Ú~=e½™²ÝˆZnÅ+nõêf¥3qûõdÝõ„m~+—šßUJŸ'«g"¦kió§ÉR°³Ë'­³IÛl´âˈmÿS5&F
+šPã0\AÿP:
+^s‹ù^gZUs…µ^VTŸ­ÓJçدÓØ&Ò3
+ÃyÛ^ðàÕZ8mÊEÍTÑ׫c—ê—èÏÝíã^ê’7|ÓÑ1o'ÿ^G^öP׫H –)OÈçJUçlm|ÚÙù…‹Þéõ¹QM×F/X÷‰JÀÁkìU›ØØØxöìÙö8?}úôÅsdcóîÝ;b>f01Œêð’VuλãI´™ z¸QšöªS>Mʧ΅Äçštg\¦¬_“ ©šKÿЪìªá¤˜è5†G &KÀ,íöêGZd©Z]& O…òåW 5>O[¯Ëx§•¤ƒE°ôEB&Îf‹Ù˜•'ï(7œê†ßTfBòþ}æ ìg•‚
+#vA‚ÝÁÀäÒd
+QÐBÞéS'óiDý~D{úÕo(ÞÞ*H€#Œ"‚ qœÀ‹THþÐ.D5K‡Õ ýfÄv7ìXIV¬&KQíƒhÙ*i]%Mkq]¡ªëbÔ¸FÛ—¢ÖGG,k¤áeº·n$ìiuh„ßS3x0Ÿ c˜õ©°lV¹§àíìU¼ÔñYåm¾u^4R¾ ´¯]Ù»y
+ÌÁ‚Å£NǾþ–y3þ懑òEªì>@r¶³œ¬P[x.Ô~ù&fXéÔ/¦{Ië}`Òô,] ›Ö% ¤úéá=ã­¶}z ‹©f¢ ð`Tá~cÅ/ßN¤’½©Ž÷F#GÏEŽåÈ£úpÈÇácg©žÂïÖ­80|-î®ySú‹Š0h¬çÇw¢ÇR@ƒÃÂ8ƒ+9nmºèŠþÃÏ7²†$˜¨O¹I[žèdþüc>z̹VK–ÜðÄfÝ—ñi7rÙÙÑWâkiÄš6ƒãw¶æ«.jÞœØÓ6VÓ6]Ù1ìQ«!/»ÂÀ<€4[i]$ÐÈdU$Ùéõ”ºZÿ›‡r`"6Žðq®„+˜Ÿ»½…mìÜñìÉÓ‘?]@ŠX"—¢(áUÓ­Ò¡ºo¬>ÐO%9Ÿ"ãÓ ô™fez/ÿÃFKP~ºA1ÚÂË…#!õ·d$¤üÈ/ŽÛQ¯äGn´üä›òL³,Õ`)Ò¼ÉU¿!0úÍ}AfƒŒ…±»pœÅÇY<.çÁˆ@ˆ=2e·[›Ýoî9úß
+K`ä‚ëí+ µUE.Õ„ ÎïÄgÎ6;ƒãáËÎÚZÇíí—Ü‘ñÚè¿<‰¼qE¦=Ño€ÛÉm$ü¿Ø¦Ëõz¦šœ²·Ïº¢ÿtÓSÎŽ/]ÉSæ Cð‹™
+‚`Â,~1[Žïëfݱ-ÔÓ“®Î1Oçl%9Q«%'êiêš‹°®<‡ÊN¯xí«µ±+u4%*eà,Œ‹1w}rþ“  pþ6H¶_ä±±y0A aL†ÄÿÔ¥hVçíò/*tÙЈ8Ý ¬S¥Cå£?)=ÑRÒíż*ØH0Õää+(³ª¯ž?X\aɼahVõy#‡ÔCM¢\P–óë>lÈ—_:Ð`i2
+
+ˆÁc" B˜¶ËÔl­öd›2»•InÖg¼ªl³&Ô§ƒ€"ÚT@ŸmUfšdé¼ó®ùº¥·jÓmúT“6éÍQ$ãS y+R­ä3…óÁ
+ø))­B”a^ÿ豚Ã,s"¢;&Qos db&i’„¥4„¨1Šð“>ßs±Ç²VÝìkžW^·jw]Š™tå œá?í•_F=nÉz
+d"M‰@ Ãýâò›Œë¾Ðþ¥Êz]e-x9{‘ç:×ä5·Ä¶y©í†:’/”U7äÕ7äÖ9©å[yínXa0C‰éRZ,ÄöXDó\õýžªåNùJLs/ažg«îÆ-Ë¡‚id!®½×Qö0j[‰›—£º%Öò§í–VÊ'R˜”0Aò!¾€$DÛjêÿ¸ëµl÷[#±}éhßH¤ðcŒÝ7Û§C}ÙèÞt,—Lè@#gÂ{ÏDö½»#j«ÆId|è#gÇÇÑ‹ÎHÁùñTfì!€éF”þÏ\¡}‘¿¹;çkØ|×L9Ã9±¬4À)èÌ;…'6½%þiC8 䜳sÒºäá|«~=
+ÀF£S,`JaL½ •@(´Ul˜ðÄ/9"`,¸3Ò¤# N¯8¢—ë"3®(pȧîð…†ïSÐ3‹+¾ã´3ú‰#¬Ä‰R‹hƒRóèÑ£'y’gN‘™ÙÃA £a~© 3ï(Kµ¨RÞ5/¬ÙÓ ¯b8À¤ýº‘WÔÝvB 7ÿ)ŠIôG%´@P¬çYwjNn'jýPP›ò©ÇšDÃͨÈ-
+ XÅC Bj1˜ïe³-•‹{œK¬n!ªXI˜ÃúBí®‹ íÝðÆåˆu%¦»ÝÔÄ ¨DÊ´´”A¼wÕesR;(ß(êæ%µ×$–‚—³y®sM±qNb[[Hæ䶯 pÈ7âÜÿj ·
+Ÿ=¡™öóºð„½}jsô¢—û«»#¯^\¹ œ
+@ˆ;ÇÔüCºé&mýVd½%·ÍØnÒ¶:Wð6È1èeJa†×qënu;ÿ¯ük\NØnsUË\U¡ž—¹=ÖÅPùW‡üú
+1F•Ð ®@ T mñ!“ª²'°3ÛùÆ8×›äöž½ý‡Ø[ÿc¿Î›¼ï8€ÿ41rÙ~.?Ž¯Ç÷DZçÄNâ#>b;Ç
+ŒÁºØ~.Ûa¥ÚÔBR¨èÆ´Ò6¤ ¥ÛqºMýe¿¬ÒÆX9BŠÇP c-Ò$†B`_ƒ„*¶ü MÅzËú>ü|žGßÇÏ÷óz€4R‘í‰ðëi¦{4º d”ꢺ†én0£s‹½•&wdèî1zç{›:µ|1qE,,*8bÛ4錭:?žÊ%Gì„5tÆFžsF?³GNׇ&ô_<Ë«É–À„;z¬1 Ovþל¯§ÏÙ¨‹ìYyºæç
+ o;Bîì í"w F• oÏt¼ñ1½ó÷Ì.0
+c9ˆ¹h0ü®FÁ÷ýû÷ÿ7u,~Ç1K=|ðpiñ¾
+ÚÂ0L 
+õR
+Š9`yÅD<´HŒéÖrG\¡óÞm“V
+é„3rÊóÜçáÒ:fÜNŸpÑ“ö¸*ƒp1BpÇøwç€.=xÂ’'8Yégéq––žT
+ê“]¢Ù0è¥Ú†!Ÿîm{^Q>¸#r²¶d° 6
+L¸Ñ¿ø@3/p„\'fêo'Ò}Ú¯O¸di"(;ܾìyQD9èyL0Èi$Õ®Úl’Š*à©aY›^»Ç#8²Ñ8Ð¥)§"í3¥^.}ß[žnCGì«Î§¢úÈ«;²Q“ð+Gýe™ ±/PdFa…!@m‡(BÖê÷Ť=Œ¬weQôå’£H_Lò>­ézs á¿»E¨àÅÜ|ž*0¤xíKÁšš’‘¶ò{±ú{ÚRŸ}Õrƒ5ÜcËYËmšbMÀ!Ù˜ež4χMwÉ¿ÎQ•×:Jæ@ªd>j¾N–Þ}µæNX;jú{s¥Fºâ®ÂŠ¢6WÔ¯z{z‘ïU.5ßHknˆ+¿$*¾PÕ}­l”Tjá<„/qHœ¿¶àf)Û¸aºÓ4C²1ó4e˜aŒ9ŠtZfYóTØ
+ž È£åòÙ@øt0|²¬õBIä|‰|*𧑱¼-çK•±2ùl¹òi…Ü›¿Nræûh:ä¦Ç¦AZÎyñb¡2Z*/·þ‰âÍFŒ`TP†*Ü<VÚþ'ÿ–ýÞ*ƒFOˆ¡hžçŠEr Í"„§%g¯A‡¼µŸä7_(‘/ä/J#çŠZžö8œ/ŽÀ¿)—ΔH'Ë"z'´DÓˆ G>ýËâí;K A<&BÒÈÒ}Àuqqœó«·~I°‡"1cs®³§NŒÅžJa áIuÏôx5PÄ é®{ªœñØWe׈=AÛÑŠéåæÁÕz\P1©ž¹)?Iƒ­Ö¤º£ù®Î°1ÓFkÁs’Ëña£q°Î1°ÖÑ OXeˆ×Øúê¡à‡ÐSW+È$¾ÖÚ²Ôù®OÍã0ã†W1cĬTcƒ3cw0ÿp½µ»BýQHßÌì ‡‚+·"ö×Õ
+‡ƒæa@]­®=±©jšƒ L‰÷Û
+ˆa|¿ÝòŽÌw)ËÖHg0#tÅ€%àÃ~@b;Ø¡9ÖБ¾»I—뙩bIéUi$ÎR\Ú&7=ú£ÂÅŽâùfû¢”1!y¦Ú2&eì7ÂÞy9g!š=¯øæ”eïºàÉHÖBÔ»([gÂö ¥p¢5c)fùǦR¿ ‘(‰¢Sà¼ç¥™Ïù¬i­ÅÛÓó<S¹˜^pQȽ¢Ïr¾â}3zÿWbe'kÆYB­ZÅa ”´z±¥pÎtÔsµ-óVÔ7‹Bò€Õ3<æÀu¹ëâêöüëm™sQçl‹er³p+–5·½ðJ8ãF³øm›ûÚ6Ï̶üî2³‡ÆH^ƒ“°Z¡¥Ðt²*ƒ3n­ÚðadÇQeÏqi÷CylkÝ Õ¥{ëšàtªãPŠÊK~_úÊ¥Êiëh¥<êo®
+ýî³²èŠóã¡|V=ë—Ï”D
+6ÖëDÇ„C#¦`ØR Cj–ãpBñþXÜz*رÜú犇ᜄvæ7¹ü¼¨%97°<GPˆÀ"àDÉ“ÈDÑ"ÍvØŠæ6ü9(Ÿ‡T´5“ΕJO{FK¤3¥Êç¥Ò™¢ÖÑrå™Õðl4¢  …{‹w—þ%‘ÿÅ$‹÷î}_#ukkfˆ¥h:ÅúæZ±7äè¯z²¹¯‚*ko•{°.xµy 2}¸ÚÖ_cêm°6˜†ÊÁ x@`“1ØcS6Yœë³ÄF/E¨Œ«ñaßWm\g¨µ÷Õ€FlGjA#æþУ ÔWó€"€±?¡[¼Ör(”êK3®YcOc9hcÌ1µ@™h–O†cƒ ëíÖŸ•;ãlñzg÷£ê¯LŽ…„¾
+a(äh´ªg
+´,Eð*&³~S¹é½˜î]I×…ˆû¢¡‘ôýŠp (&€m ŠØµUüìx¿Cß%³rˆsZÔ©¤†T3ˆÓ'«’Örk~R¨ÿRöOÇ
+¤Üé˜c&æžQ<³ræ¼ä…íw\q^ÙêYî®;uÞnËÝâZˆ¹&[Ì·”¼9ç[ŹÑaáˆdP4A±v\ýoJïçòW¼==Ï3•ËBÁ%CÎESB#3TÆ7¬÷rzÑ5mQ”×­AÚU$¥×¢ÃeésÍž©íù³ï-Ù8Ÿ•2¦[]‰IÞž5]Þ¢€\Úê›js.´ï´Úî´]“²Ç›ÒïFMKQ/8çÄ:gÀÀP˜Z u(%gi5I›iÍ Y%á×nýù‘–I»׺ LòEþ]&ËÕÈñð®£Ò®¡ØO‡›wìÞžæ1-l‡;
+Ž8ÉI[`Â8å)›~<‘Ó%Á‘Š¦NC•ˆÆä2Ø_›Á²Šd¡ëÁ¢åC¶ÆS¥ÔIg舋8a NØÉô«<Ñg_!ð1>þb¸/@6†Ñhv¡¢}‘ëWE+GìÔ”-|Ì<î ÙÈq 9ZÒ09‚§Jƒg¬Ï]#p§  Á
+™6V¯ˆWé^u«ˆ}m•O7 mŸOu«“~°‡6Y«‰ùuÑZPŠîÃeòè°
+|[X¡ e˜"VÎæj²:Œ‹)äruÚÜÞ2EÌ+‹{• 6î¨â­*éý†C+c^pÈ#Š€ôq¿2jËéó©µx_(^#{Û¡ª×ò¥YbƒŽ±$\‹µ`Q[¥$¶D-ÓÅ\óÎ'"I”«n8#Ýžz™MÁÄà™ÇP*©³Kv6?@Üí¤¦+¥ˆt)r({šU=Í
+™ùé¾uo4çܦôw#ÖËÁ‚›M–ÙK¯G­g.ò$ ÙLœ!ø G÷…|ñ ±é‚¢âœÒ0ïåée^¨|.µ~ÁËŸÅ‹ÎJÍJÓUYá /ÿ´Ì|Ad›9°ÑQ—±-—`§W›fš oDÏ’&
+(
+M©ÑÓóvyå±òEƒ~ý~÷¼óã‰è{«ðxEN¼Ö¸ÙƒŠ˜6Š0‘L¢ßB
+÷7Ë;×Ê6¬äX8,˜RdÒ8(å!8NÿÎëå‘W3Açíu3-¦ÙPÞ]"mÀ¼ïzÄt«Ér‡È»N™7”ðitD dÑ¥ ¤Š%œY¯Èò>“çŸZ.)Kæ½<½Ì •K2ëy±ù‚Ä
+,9-6&Ì¿Œ[Ï«­ç%ÖܼßâÆ\*b B¾\üÃŒU¼sDîµõE3M‹oRù
+Óeyá§bËEáeqñ¾õ‚´øž7žÕäÿUaÙÎ5°!ÆbqØ?XÀCÙi
+IÎ%ç–û…$$!!wÂ- !Aˆˆv]ëºBÎ9É `;µ­ÂººÝéCÝ^t½Ì¨„tËv¦}h§íLgÔP@q­coÖew„*Ø݇g|ÐÁ¡žùÏ™3™É÷}çLrþ¿¯õ¯¶mR¾FQ
+ò¥[Fª¢#ÕÜßÜ ËÆ·ÅB®âÊï
+þà~çEÇÞ¸à÷[¼ñs¡øåZî3÷ŽëÕÍ úª¢—¼Ìp ~ÙÇø¢ºß=çk Çk˜€d¤’¾êã_=änT°@>@#`%@Aý>ö/eÍ0†`¨h˜hä)!_Ž"_ƒdqñkÏ,=^˜›‘Ôò¨ˆRä«RQ¶ÞÔQfjL'ƒªž+Õž®›±3AC¯ Sg:´•a(†©ß/3dȾZýÉ õT£æô÷Œ§"¦aÓÉ-æä&K:\ªVfüÚÞ €–dÓsÕÑ.L…m”=!]:bL6žjÐ$ë5=Ap–§ 2]¦ëõ›’Ûˆ™Å…¤¨®P»§Òx¬®0³¡ ['Í¥ö"ä‰×8~Ñd6«ºCúTðlÕùñL ™ ¸Á‚£[PÊ[O¡0
+#Š½Í/ªŽ•
+p‹ôPLz8®;ÚnüYTÒä $ŠˆÐ|…`La‚R9r<`§=Yç4c˜mssN$\sœu†1ζ:&cÎ)Î0ÙnßežH˜gXËkïêqÆ<ÅšÀ»w³Mp¥ûü…ù(OÍ—“$©€Ö|"uM‰¼ÿ‘•ü[åü\渭tßR–®z=½Îë€üWí¸©²OÈÊïRžŸJ¶ƒQ1"–Åç¿_”‹iYÍÆ>Ó^>Á˜r¬qƒµäâÅSíÖñ6ë8W<É™ï'Šïq–9®8Õƒ?ÂT«ëNÌq—sL·Ús´ã:íé(S*5yŸÄH)ŠHÐ|ŠLÎÎM;»£{úbû˾ìÑ{1Z¼Dž:$Ívf£}Ñw“ôÞܲóø-³kÀÇܨbF±K•-CÞÿ; W±ÃÕÌ5o|·®D†òQT ɶË[•%:X"Ä ˆÀ0
+Ç)ÞkóùhÐ×ç½;‡B‰Ëà‹~îJulȳbZõ0}ôß«¹Á@àZ% 4òÒ8ÐË 'ú¯Êøõ
+¦¿6®ozâàÙ®Ô¼C•€Cìh 0Œƒ‹ AÖMR¨
+,àC?’¨‡ÕÕ2Ï?•®[*û;¨€[ŠòU¯¡×yåÈ·Ž1¥kLQñ'±}I­ÅyL,1ŸI2–(ºµ,$L·[,‹‰’…˜yºÍ
+(’cŠgiû<íX`œóŒm®8Äžc,ÚœšMië\{É—QólÜ~¼ÒPE| †ÞÀPr 4¦
+Ò!ãG´
+w{óñu•¡ØJ¨X‰U™T%Diø©
+·âA<ìNÐü0)QVÜQ{Ákÿ¦Æ>¦tŒ‹ì_ÈJoËÊV¿†^çu%_JÜc2ǘÊ~Ÿã
+ω­BBCׯEˆ÷JU³Œ%·Ns¦©˜ý[ôULw7Q|ŸsÍÓîù–’ùç cb-“?tOq¥wš-3œá>+Ÿd ¿ßj«•å£"p£pRÉç‹T¸*h:ØüƒÓLçYnÿ'ñgŸP¤—îTÈ0¯Z#`"0 `O_lÿ™·ú;îý‰ÎP‘ `I’!ÿ“÷ZMtýgåôˆŸ[u~<ÛþÕñÁšØ`ÙÎ;e‰ UQ'&H\ˆ"üì×Ûs×ð? ÍõUZí®vW÷ËêjK¶,Ù¶,Y7_„- 4­¡X»Ú]]€2eŒ í„ÜšbbB ¶, _`Ê3íC3™Clp %¤”Tl \Jlpã™>0“>¤JÝv¾³£Ñß9gvôû¬0¼‚æ¡žO®é.óœ¬~ ¾âd&ª©¥®]»˜qL>gk=ãµÔ˜‡ž®¦¦ªèkNä´«x ጛZZª“úÌÏ]ôr5ôXÏ ÄM팹h ‘Ë5áñÚðn³P‚a ÃÞüÕ‹ÏŸ/,qda™ßéžYRÍóŶµë€v@ TÈS¿ÝP”h2õ7«RÍd²^—®×'²¦]ºYÛ_§J6•¼±ä)ù_˜«ßV«jàQžÜ¨Ý^%Ç!)!Aø"e®àšÊÉç›E¦wƒ²‘Uʯðü§*© v Ñ˜hP'=â´W;ÒbÜ
+Lò³öH“ìX½6QGö“ÇÖ®8?^ˆ<áÓïk@9X*)S½GIߧVJ#šƒ1²7ª<À©{£äÁ˜ì=¤èÐvÍÝû´4äj$r%0QO˜ãZ ôvúóóÜvÅ£¨ô)e¸C¯¾µµx&n¹Ë–Î…J
+`u.‚ÁB
+…Z•¼>¿!w>Øn™݉êïÇMàÏùqÔò€+¹Ïšïqæ9Öô8^þQ Âˆcb1,ƒ
+áuÅMIÙ¬¤üï2ËM™%£°Ý’—ýMa˨«¾”W®|z™—QØo(«¿”'³ä •éº²ô‰ÌyEäø1Fäà°’H Ŀšf(ëBÄ”‰–?¡ ÷(ó k[n5?‰Ä ³±¢EN¿2]k/ïvÌ„D Ëq˜€‘<)"F eRÕ[-›ÏíJ‡1ïîØ9Ù;ÌíIs݃ìn „‘p°ÁÿF# @>€C#¡ÎdèõT¤»TJò¼ð„‚Êg½Ì_tÜZq~¼¿Ô°5ô¹@ôS7Ýë\‹£†¥~mörmºàf&Üܸ3ü™ƒºê‰MU…ÿà£/øcÀ0ÓvÂË© û¸ÉÚ¬©à¼—>ç£ÿ\ÛqÑËM:èi' V8îbþêŽ^©¦'\̤‡»ä 
+’éÀ·Í¯ð“é >ц©ݪÑ5¦ýÍ?0æcÄr\µÑb~Ýk>ÜZœnƒeé€&í)Jךþ€:ÑLþ®IóV-ùZ…ýÍ–òCJO´KÒD£MÔ“c*kçµó<´/Ïᘀ‡ "‘qW»ø §í‰­”F=¬|?£î‚Èzi/';Š½qòÃÊàKNûÁvm÷ÌoÏJO …¸Rh)<ÕV6ósûBÈöt‹ùQ‡ác¸5ÎEK²e_Óö딽Z%" †æ´M š‘Ø2âò/¶ëdyFiŸ•VÜ•Ý[3ÊÊ›+݃^æe–ó¹buF¾ú*Y–QÙ2Ò²[²ŠÛ
+ן”V“
+/°Ê¹à0^”Ÿ9””ᨒ /Ÿ´¨‘ÇßÅ#ósssOž<I%Š¦h„ÝPœŸYeì÷æ'…ÌJ1
+FáW‚ìxØ;Óæ»ßî¼+¾Á7ùn¬b¶¹b«M'C¥®Ð¢d!JŒhìS¤þ¡
+À㊦bF¨úŠ«¼Â—ßTZ
+ÊVT<A \.Âr,©Sõµ½›¶dK#ªîˆö`bq×ÓjèIöÆò»[õ=!u÷&î@ˆ{'®<á÷‡óDÔ=-bOœÞP‡ôxÙå2ÂáLúƒ·j4Ñêû‘òÿ4‹À$3‘²³¯ÁC¢…—å0Œé÷’ü%½é–¶jZQô%k½¢²]WUNñU“šÊY¥íSzI÷R#/óÿ­õ ®ô¶ÂqCY5©°}©¶*­(šìš†p9ƒ’,ñ©±Ûqó­DþxÁxéíH齈ùA¢ü\KéN«Â$ù!‹Ð4I¡Œ‘CPÕóoVn:Ú6˜èL7¿Úú·–×[¶&;†âÛß­óx¸ëxhçŸ[v,f8¸ð ù>42Ût”ŽîLÇ»úƒÛ
+"ÐnÞ.¨wÇþéÞ˜u~<•‹®8èé—«"§­Çu6»F Æ”R‰CÐZy­òÛ~ł΄a# µŸøZ/»ÚGlÍ@##žà…ê%SÁÅê6ps+€œ ¢(BB£†åRˆFšû‘/xÉûÄ:á·‡FíA`†¥š÷³šÐà‡#<Q=뜌ÔFÇìÁûzÃrh…‘-‰ŽóO4òðù02?ÿäÞ£ÅÏÍ9ìÃt„RFH  ÿ3K§‘@^& ðmR%Ê—i%ÄrIÑOX\®dQ•J£Ì2}§ÅÐW§ëó«W©÷¬Vr$±2–TüÔ`\©KûÊûž­£t#ˆn >¯?`L6,¸â  Ý]¢÷èàb &säÌÊ0ŽT„R ¥1¦pˆ0Ò\c¾z»ÃØë×÷
+
+U|wDÕÛjø}XÓ›Š<GèC1íáͺÝ-Ôj»\§ ¼à,Žç!rX.±ªé¾Õ%_µÌGó§¢îzõr¢%+—þh½‚¹EUÝà,×4e“|ñÒ5ŽòiÁ:¥¶©¸ÊWN öiÞ1¥²_W—\Ì“¼ùšP2­¶Ü¬·¸Š…5ûmîÅäªP2¥)«žQßäÌW´¥W5åWKÖ ûÎ)Ÿá-7¹’|é´ºlš3_W–
+í±øÁ¦MXƒ@ #uñ“µ‘3Žà ßËFZ£0v£¬²r~nþ°ÄãçÕÈüý¹ÿR_¯±MgÀ¿OíÖ6„Ø>÷sìs÷ÝNBÛ‰_bÇNì8W’j ø“2¦Ž‘ÁàT¥!@(¨8¶.ÓV©Õ&UÛ˜ZŠ:B …ŒæJnmakwS Ù›¦Û7&‰¬JE–eŸóœG‘ÿ¿wñk|ù¥‚DÀ¿: á„–Zs$ª¤_LV6¯c·!…Ô‹ÒxŒ’½”ƒhp6d2&›¸Áš‚Á¦¢£þõ%ÄzL¢ÎÁ¬?äק‚ºd•&óX¨€CÒuºÌ2€F
+2Åš
+üà£2™'SÖæaWø€šóD@nxã·} ðf¶42ä }ì
+Ví¼âj»áŒ]³…v3†@k
+@#@©¦Âuúý^y¨q*”A½òÇn¶·I¸Ø ¬5
+æÚ^T#NáÊui¢ ½ÍšÞ*M*@Ö?îúÊd ø»¢m2¨ë úë
+Ó…Í|ÆOüªÞp¶¶ðU/£–³„œ”!´™Š„X'—“øz’0%Êdî”|ݘi
+LÆ+ †ÄRKs™•Ryžwˆ6|Ê8'Ë kJ0öÕj‡ ¡d’/™áM œyž.YP-‡-YݳœsŠ±±¥àŽãªÒ15èSÖëu2ÅÛÆÛ8W6!–O
+¶IÚ:ÅÛI²>Ø3æS¹}œ±Ž3æ¬å/\ékc¬÷W¶æYPšþÈæO(]~ŠH&ó$ß kGv8¶$ô=ZÁq$‹‚E(t–ÛÛ3»ö·í=Þ÷ËöƒÃûÚ:³ÎŒ'Ì…pg¦}ß&{•%d0L£H»¦ôFÕ2<@¥ŽxãCîÈG¡áÊ
+JFQ"…‚ eP!©<¸ew2Ü™
+u^ˆìÿyôÀÅоÁÖN`’ ñYgÆæRt_:²·{Çn\
+ãH BüÆv„@Ÿ‚b½Y»V Y–€'{qQ„>r¶Öþð¦=<R{»&â’õ¼¾»0pÍù“·ãº3|»"l0æl®
+yZoVFF¼Ñkîеêøî¶ÕšçVeôƒŠí£ÞÄUÛ¶[¾ïº·…U¦ÓΗïXC£Že8­Ì<â‰
+V¼C‰Ä*…R)ÇZÖ¯öäé«käé¿
+%àlyaj2sá©° °  ¯J#š!1U’;àSåmsRZŠAB*nïFÝ(Ј¾o { Hö…gÆcüe8Šê‹ƒ€%`~¦¯>ÔNíWÑÛI æmoAi]-Åi2K!ß@S”ãžÖ1KYøÒiµ ´ä ïX®vX$
+é’yÞrŸ5ƒÍgÕÖ¯éÒǤm2Ï“%‹ªLæiPÊæ»lÉM¶Xðz]¡œ¢JZ0Ü‹ã>%íÇÈ&HÑD(¨òìu£5ÏÒ%³”m†uÌ2ó*Û#Ò<Çš„šç6]ú•Ú~ƒ3ßØi3Â""K¢D)ÍwAJ0Q½Å|ô[v§c=C[ÓÁ'c{‡ÛmÝ';~Ò¿é§#{gÆ3Úº-ꎊxÖâ2„ ð”±^S<á{Ч—ªÂ ï&ªÂBiä¼½õfMÇUG´ü˜'hqɘvÆÎU®Uw\­Š}V¶a§Î^ÇXf(eÔ`î5Õ©Ø r¥"8Y6¸^\®y®UF'íA°kŽ…W)e¨t5œÕ¬7y£ÀãU¡Ë®0 À €Üy÷·qß¾pŠd8„Á¸2™LÏ2 xúªyN~øxÅ Nâ"2¢úÑÆ•Öˆ>î1 yõÃu`­MÔiâ~]ܧô±ƒNUÒ;Å7
+v»‚ßMf'`ãoIó¸ÖuW_ó€sÌж9Ú:Ï
+Ý­+–QÆÁÉÅY¨DL rAÇdÝjÁZ{¹r…/¾KZOZw°y;ÔÆÏÉÒ‡´ý#˜*ïñåsHá4[|›-ú;íxO¡ÑÈÄD.'Çw6n:Õù~zóöãÑ=©h×PäÇ#á]ÉÀöthg*’YŒFvœ$ÛºgÆóIp{ªsÇð殣.JáŽ!hª<SÙ
+:€häReh²*^ÁZ(L8C×Ücå­8ÛÏ96ßötLZ6Ž9ƒöE»Œ3ÃBD…ÂJ‘ òU¨4'·dÛ?¯^v†¯W„n¸;Ç«—mþ1gøwEë6rÅ´LŽAP+ÃÌ((2æ
+fç
+ÅŠŒ;˦ —|;çËÛÕ¹oȳH”
+ÀF(èÓutº6¿ßk©×$êáŒÉ)CË1$P›¿/ÆôupûBšÃüሺ/"83–˜ <zc€Rlo”ü(JŽê÷E
+~Ald~,þ0ñ4)’¤B“%ù¹<ÿ)QrŸ5Í),Slù¤Î¾¨²=Á-·˜Âåj‡Ý\Á;r…éM%¡,'0F•°â 5*Ç$bq«\9Hǵ¶Þúˆú¿oç—å7D'^cRðgKKQ%Ž@ð›=xŽàƒ½fî3¥]¸J ­FIóQé$¿¨.jžYÖò˜6/¨M×5ùsjóMÚY¢Äe8*RC¤ [Òm='#ÝéÐOF[»~xï…@†Û{mÝ£á]'B»FÛz> íœKÕH`[d¸Š“á=N>_‰)0)D`è‰ü
+P£ÆUè–úÓîs_¦±Qœgÿ\Uj@f½»sß3;³³—×'>ÖöúØñ±¶w½†@Ai {O¯ Qi#0GZ•lcH"|¬×Qi?´Rz¨ù’pÔ˜Ëæ(.Å$1 ªš6%Ä}¾Q!°5«¿VïŒÞ™÷yŸyßçÿ{ãSÕ‘©ºèGõáåŠç]ÏÆ\‚_Í¢,IŠ&b5ƒÛxùDî† UᙚĴ¶Hq@&@t´ë•+žø:Ó¾ÄV[‘I¤DŠe3ÑÏ~üõ×_='<\ø??
+¤ÖåŸlÊlr¦­C À¹CË-­Éã®ô¥Óû
+³JÆqDd½¢u³‡(±žÀð®Þš÷Þz½¨ãir²S”tP©+ìm±†Ð8nþ¶ÏûY„?–pßaïnçúÒ‰÷ÛúcÆ%‰AäbO\îK*Ý kwB>qôuºz:„ãqt[à–‘fDŒðØ%±r¹\`Î^uUZûÑó7¹øsµæ/¢o„-Q9” 0š&áÇ „¡dŒäq Å ÅbL$©Lļ–åÏ•]—ô·×¥º°RxÃQ=+VÜS¼órÅ-®bV©¾/?­ÿ¯ù† HÊ, $…‘‹mŒÜËÙtŸÈ j7ëb(£‚J Eáˆ5Œ ezÅó‰ì½­–ÍK%€%×ϼT{ÚRÆ`ß¡ ƒÕÆpooÙy:º§?¹k¢cÏDdî8ñ‚ìIHE»&Úººü¯ñkœ¦`»µ¸ò¦ýç*¶_«Ÿkˆ^Òbµøùº¸^4ò4]®moä]$Íeð¬Q óqjsvɵ¸
+‚àÁóüokZ§µèGÚv˜Â³¾ÿãopbº²íj]ü|eëŒÖñA]òÖr¡xš3@ñáhÞ„·qy¿©Þ
+¤LiQÐåšøbÒ|‹©»R›Xé<œ¯_ÓÚ»] P- 4
+H êP³kôU[*´lC§Z\éõÒ@Snzsé/B¶:;ÂÖp&çd# 4b¢1£"g:$ ztÇÿË[³£?±©§6Mc2òË
+ÁHš ’…K¥ÀHX(¦l¦)+)â(â@3OX
+t·×%j.¾%”ÍÉ5sJ圵êŽêŸ±xÿC>­ÿËJ# nb)ø²8œeIPB!i¯¨ê_>kó~j)¹/ÏY
+ˆåŸ*Õ£–\’ÈTL8P6„ïÙöúØŽ}£­oŒ·íÕ'^P§bûSÑ®QhDö '÷û³Šhd‘FyËÓ<[Óy¡*|µ66Yžô'.UëO¨%PŽåíœtF‹Ü­JLúc×JÛ®h‰„³ÖiDZm…@#SZøBMüYߥ2zι¦%Îháö,`23…QÔ‘žAPðk@èÊ·þ)»Ry,xö2 ç‹\\yŠƒÙ]©mÿ£^{‚¤qÊë){ôœ,²
+vù! ê/[ÔqMwüxRé} É5² l$ܨ€âœ™€…àøévÛñNþXÂv4á8ÁÊ{“ºcÆÑ„8{;=IKwàDì‰Ë= û¡§Xàl¨˜YŠÂÞã
+þ.zçÅÜårJéM¥ä¾àû]lÁ3Œ„ÉJò4Æ`àÀ8ÅQ ìz C ÃpáP
+hÄ‚PN‹”À…#R|É IÓÝ^—¨~!'‹D€.XÓ*ÎœÐ(K›ö1ÊÓú¿¬4ROð­1”D ¸Œ£»·^ñÜ*nÛ*îÊ‹4rW.š—ŠHÞ›ÏA…ÀHV!,®‹uŸÿDwœxAMD÷Å÷Ÿjs¤u÷Hò@gã&É ¨O‚¥}WÍŸÑÚ/øþÇ}½Æ6up
+e¸*g4´\ã×ÙŽUšGCÜÉ*îdµi¸Öz¬Áx¤^h´ ¯w®Í=^ÇÆëØÑËPÄ:P•z~|?Æx8I¸£ÕB·'ÒE°8)çB„H‡:ÄþvØææ·£ÆþNþ@Tív4gÛ³è„ïR'J_ÌÖ×)lò£ |ÜPK²wÄŠÛ|áM-w¹Z`FËŸ3ºoód]¦¤§d„f2H†fqLOËÑ Gh<%Ò³ $Žã/ôðÑS(ÛÀ<cÂmÊëõ ó>U&£@G° Ár›Aò(fø%þXÝ=¯9!ä*†ÆD9
+$Š6“éÿàKR5ŸÏ…575÷¼âZ”³šsF· ¹+¸>T<…Š$•É2z‚8ÜØoßûþ¶})çÄL¢uÉXô5xoíîkÚiÃ9’
+š­ä-[ön_þ@£ãt#Ü»\ã*#¡¬DÄš¨W×Úß¹vù¯úóŽl.8¶^‹‡´!¯6ìcß«•km¿‰ØãëRÎG52X›•¨µ­er€áQ‚ Ú‘ÄDØàÛ;L}É~‡ß<C‘ú¢–ƒæÞv¹7*¼Ó5’} Ë¶¯ ¨¢Ž¡ˆL”å°1sOð,ˆÎk¦ÂåjbîuÕý{±„#=‚ƒ•K ÆB·ÚG€7ûŸ¤Ò)pYð+ÁCTîzFV
+¶Ú‹‘ô$
+ÕÓ
+Šò.wNbÃr«Ô*'…}~-`Öa«~Ìg¤iH&X•RĽåÖ>Fª²GêmÇjµpÊùñH¬ƒóhÈ~8Ìd±,Fs®—héõíI{|WîýßûøL¤/féë0öÆÄþvöp»Ôß^ðv'Øèa3 ˜*€Œ´Í,7Ç»g¥Âyµd^p/W Ü“‹gÔÒn1‹!1 ’}KR€ê׃A±àSÍsK-½¥ºa_ü[(ýVòMKî^ÎÞÅ+)®ÏLå‹ ç«3åõú„9)åiÒ>b,¢×ôC¢{õqç?¯¹f„uÏI¾3Rábá-Å7/»—Q¹KÍM©
+€ÌÃÿãâòT!€ p
+̯¦¤S»ùÓíÊÉÝÚÏöP'ZÔÓ/oÿ€„{}7v¢ÙqòEp*³ƒ¦Jà ´H QÆ
+–yÑ?·Fô Üy‡ì“I#E<J±y$œÐŒq?]8®GÕÀŒèÍ÷§%×#Ö5«º&$ûE¾˜%!{*ÀqXu"!ÍiµxVw-ˆÞ ÖÿàîíïqîI¾r‘Œ^xwMŽRiZvÞPsB`L…Öï^ä}04ýIÁ1cñ.€âÅ:a*úÜ쑼W¹ÒsBþEÙ:ªx²åÓ¼ÿ˜U<0È︧8!õAwžcKî™Üãš÷/”ÚŸøÀÖw×äü+íI«á~¡àg½%¹'$'\8)h÷_º`öÝÖŠïÈöŒXä‚“ü¦19°$F'9gZpg´Ègªg™q¬VüÕªÏi©–Üo´R ]gF˜40âéæ4í¿:=½·åÀ[íGû’{š_êk;¼æ˜ñÕÓzp uÒTë¡Î݇ÞØw8ÄY4ŒN„
+,½Er9‚¡HÁ!°‘ðb“
+%/—7mu  Iâ$ŠšU=K///ÿ4?þ÷4²ü7’ˆu‘cdŸ`}³Aê®´ $VË=­½õEµÖ³[`X±¿Ê|®FØBT˜:ªmzwÖY=·If4q#ÇçÈGjŠÞxÞvöÙü—7#N‰2â,Fé\íç•…=õÖÞZ½kËJdK×æA#_@ÈZ¡¶Æ
+Ùöw€Î-ø–]Ü_Åý(JV
+f±tÔ˜;ÿïhDï¯(8ÛÈÛEŒ€5I’4­Üfy­mµ@>ž’_kÓOíÑŽ¥ à/I¤c«F;_&ýx›ræEÈBʉ6ýd»åDûúB#I@J,i8Æ™ïê>èqs¼Zê*ö¤÷´ÒŒPV
+ù›×@
+—ÕðŸ•ò¦$#eqbZóAØ€f:%:ï¨Î;÷„î›’}S‚ç¦É{Cñ¤åZ’ª¤A##TaÌVŒo¡ø.Ú¼(ùÒ\É}Á»ÈúÇdï„ÿ…oR(…ã+‚¶aji.ÆRõ çA пec¢c”÷n£…8+x¦Á(%
+¶9AK• U˜
+N¨á¸w„U‹¿Zõ É0-y®š}Û 62R8+ ÌŽàæ_î>ºB#ÐÙû“᤭㫫7•Ýð
+œKîh}©cï‘mŽo„‡KÀvà'Åñ›‘¶áÖk‘ä`<¹æøñ„†ÂMƒá–ñèÞ÷Ãͯ8j“šçYT/çô×ÕøÛ¾íC±=Gr$”òï€~ýq´éiã_Ù²÷ÃЮ+åmŸDÛ.ù¾s¼¤¦]÷4pÊß©¯³Ø¨®3
+~ºŽ¢œ
+!ÀþÆõºi³zΨº'Uß+³¨‘9½tÖ¬MY^8ŽÁÂpqìj a$²dI#ÿYBUº­UÝÐKfuÿ´Z1ã©žö”Ï¥÷µ²y¥üªRqÉ ÍJ¡9£é×¢ŸâNÌIH´3 /ŠζJ _*‘krÉ]³z¡@B M½ü†TqG­¨<ɵå ü2` ›™%2:#Çîhµ÷äFÌ:—ÞE8Î¥PX„\ôs9ð–lGª£ìˆeiÞ"†KR–2HC+âŒwâŠ/ÄØm3rU+¿¡×þRX«…Áx©Ì‰ EX#p>«²V^(XhB@¹À¿cÚŠ#z[ }  2%# ÙZamÔŠöáR§sXg!ÒhIÍã°EWPÎS£TaþÈßÔjï(!˜®/¤
+]X¤8Q"Á+6>’·Ut.Ö~)†‚ó+¦ôòÈ5¬²{fݤYs”÷¯Æ‚NÛܬ°pB$¢ÌÊËKrI4d¢Ëž™çñŠž×Ã7¼5—ÍÀtAÕ©D¢ƒå {
+˜µÁgÑÄp¶ÚÏÖú\ ¯^=ã©;Æ—æHv…!Ååóê¡žM£ÉþLÏæ“t½òn¢jú"Nþ/b8ÕŸéÝr¤wëÑž­0„®M™tÿ««»á£À>€™î”
+Î6l8K}é=ßðôùñu-4Ç'Ó—jS—Ã}'ÂÝç"ésÑÔGm‰©pâTktûlm|¼>þ~å³Ö¾8ÖÚ÷mÛ¿^ˆ+õ}S‘ÔD¨{¢¶w,–kHŽÇÒ¿_ñÂkÖõ¾Ê°æ1yÛÁœËñJQÝßãÍ©s±äd]ÏßSOzÎÕ÷NESãMÉOS?ñVB`<y²hŒGyôäq×ÑÑ#~uV`X›öãâ¡5ž¨zYÓÔôå‡ÚK3Ö`sÁpGñþVÿîÖŠÃëüC«|¿íж ?#­2Lgž/³v4«#-Þáˆ>R#D£ûÖ»xÚãâEXZ
+K™ ÍñœÈ:>$UÙj?[ës^®¹noé+®J‘ˆ*¹SC<KS/ÇÖ ¦·A5/Þ?Úµùh|ÄH÷æ§ÎŒÿ12Ém™¯ìt¤{Ë{‰P-<ìÝâUØ8°\=û—XjÜéH/Ô֧ίÅ_›ŸDº§BñÉæ—·§×w}^ŸœŒöL´¤Çš’c¡®ÝÅM:å¢iÇ žò©êo­‘ñH×éÆøDSb,Ö3‹OÄK?üŸ‰$.Ô%§êÓgÒ5ô …ŸÛUÑÑå.k­ÍV͉hÏ8à-?Z>Òó0KLÖ'OE{Çëz·úˆ‹’^dñ>|øÍöøïëÕ¿b¤Ò˜œ›cž•æ@³g0k±FÁ!-æp›1ÜQ>°ÎÝ }´T Škä‚EÛšŠ÷¯]þR¤!†·—$W¸G;¼ƒßïÝG;üÛ;l¢ò eòÞCÿ"¾Îc£¸Î
+ª`@éÁÄÎu»Ûû­Ÿ®-”
+ì·³
+JÖ ¶Ð‹£ dö5Îû‘¿yÂ[›w=­ÕÎ:M—­†Ãb¹¥0_KJtµ„è62)`Ï-I_î‘ Nõ &œ’àÙ V`Zm¼§†§­|iž6£çph¡ÉÉlà‡*Ž‚tR “!2‘`gU}¢YŒŒH9O(µ·ìØ¿ØŒRuûø;ºËQ¢@æ¡“¿;â9®Ujþ‚iDmœt#·Õšš‰}zÍò‚)ah?£vùÞÜÆc¹­G{7˼~8½ "?˜of|ÎíÛ q$»– “ä[“ Cˆá‘Àî‹®<ÕÒ>Ñ&ùÌ«êÓÆùÖø¼ɾ³8õíªhoeè‰WÇ’«ÎEÓg¢éókO´õðdTgûÇâëžvþ¿¶õŽ-I%{Ï&Ò[³]‘9Ó’팷fÇcéñȪ ÑÌ•æÁóÍ'›VŸêü]ªÿƒÖÕWÚ×Á.´å.%ûNžõÌ·"‘>ÏŒ5÷kØß°Rö°”bY@Û¶m{dŒû÷ïÿçáñàÁƒÏÖÈŠ—z°ˆ!•a­¤t{Ê7ºª¼ï`G¡ªgÕ®Eû;Û‘e¡¡WÔå•,å ¢—È/`Y*Q…ç ­wˆ¬È¸+­²Ýû»ýï§*vuíåÇhšPúrMåÈò²áî|9~y¼ßù„€@æ(2Ç’ÀP—o‡H°^¤˜ÔPûêàBæáeþŸ÷T¼—”º¼Pû gÓf«âàR÷H
+.þ™w~<¾ŸCß3ÃŒŒ¥ýo ˜»ÖJ08ÂùIùžõyìÎwlþ'…¹+òqw–¾»¦ÂEòòÈB$§ê³¾¶›jõ”[wÝ©»£5\„¡À*ÛTºê„>õ¶Õkâ?/ ¾‡ŠUt0:¡²£" гa¥ø‹2»J·Ž£E×Hd¿YÚtË Ý°?Ñ÷Œ†?[Mº(8È+ê¾&j:#E0þ›¯yV¯¹êV]ó‡&½Ñõ×ôXLW {â5BN‘8 B­ÂÎhÓv|ÆžUc&T^§Hóq" ˆA", ÞU ,–`zq1œ2À"°^ªJ<–±, Ì/qè®»èF7ªAçI g (/ø¸…"!CcäïKå_HZˆ‚†˜ãvã„Û<ãþZLA³s^Qt!³ŒGMŽèˆr²Œ1%YdÌP„ÌÉ2Ö5XAÑ)obÊiº«UÝqë'åšYËoÈâJ‰a™ç]Ù ’j0¬V‘ÅVEyÃZ´Ù®n–uX<¼§ˆÐç0rXX?aþ¤ÔÞ³wÕÐtÆHl1J‰\ÂÑÏ©§¸%
+Ë ŽŒ˜&eZN“J’À+_‹ð 9<X2ˆ•³È½æ ½ÎÛ"ð“L AãýVjþB=Ÿ3Jã”™0ªîØ‘›j² ó”Ë7hÐ?nþZæÈš7¬Þø³¾-Ã飙Mdz[çŸ3õm~4
+Ž¬zz‡–‡;‘óPgùî¥B»É«,á$†¶}œ-3feI£Ø„ mXðÅ*ïŠEJÄyÆ
+q%¥¿ iT·ª’´$„W!ƒŸ ãJ$QU„zLÕ~@¢
+’©WgGG»š{Ï̽÷üÿ‰²ä•òüZÍÿê‘Àa™¬ØŠøŽŸÕÓ#0Õ5§~ÆH¼ï$t@ŒuG%bi‡eÿ.ûÓWƒ‰,è¾D" — ÍD" Œ£ 0eUü)ûñ,
+ÅUy‰é•½<àŠnAÁ²þ’º¡7L±ô1}Q_¶Y m7+~¨•3ÉÝ~ζÁöürÕÚ¦†¶¸Õ[ŒÊŸ™eÛ©ó• ñ#ZD <ã‰"ñ~ÀEã°¨QÂTi­¢oV‚ëuf@`´”1€Æ'ØH}ÒVyé[¤úRýx0þ¹]ó¥›»@ÒY,ÃCÙ" p*tŠ¼aMr#³Fö’R}KMΘùßè!¦xUÉ”ZÈ@²÷§šsɨŸ&f´Ä‰`n5†ÿ`3AWƒ<ˆ˜áñ Oáú¯XÓˆ›yÕ,Ë‹ªÈ’cfìªÓ8«¦¦•ðHYòä²$ì¨H¸‹q:O( @<à1›¯ñçkN°E¯“Nl–å¶k•2òQI‚ÙËö—¶½Û9§ã]Û†Šsj¾à˜ñ j†
+yÅ”˜+‰Ð‘$• ¢è2—×òpA‘I*âÖ:¡ý+*úÚBýmåƒOpæ\]6Ðz?¢Ú÷ÙP¤¼oõ¿rí—)Uòè2q0ò¨^0FÃWp90}ªnáÈïÖV•¬éüC+?ò|½M8‰ªÙ…¤³wÙÛ]±ûùù¢
+"ñEïÓŽáón Tß0Ò£dxÒNï6«^ïb˜–hð°’Fý2ÿ.«¿m€0gœèx0qhÍL¿D*‘¼ÔGA•ô*ÁP'©‹.9¹‹¡š)§þ¢E-/Àr…ŠQAÄHjS¬–5Oû›ÆXä6K]7Ž¸‘?iÕçÊR³fò‹Œb7ø×j,Ä.b!Õgp2@ )#MB²É¿ÇjæküùÚŸãNn”ÕTÔMÚáQ#ý©½BÑE"ŠÁHß×þ‹?”v¦õì 9Ô¾eÁ1ãÆï;·îØ:оùíîýÅ­Püáž] –v„ —Š²Š“ÄÙâHºô—ÅÇâxóÏä:€vm‘ QŠÁ°êh²¼H—TNEÎ!òpýâQ§h*œnêü[¡t¦©ëLcq¸ÐŠ4û̯ªrß±CõŽÓà8ë­Ê_W䥟ýdeé\¾ôq¡ãlCû…|÷‡ßî†A.fÿ{kê8uæ»ÎeŠçó›bV€2h„Rz÷î݇QÀɽ{÷ ¹÷ŸäÃÃÃÐd(Ò5¡Ól ¬Xãµ<nU­:Øb¿’e?ß­´¢¦Å C§d"PU<a‡dg±DLÙÏS>¶ý£Ž,
+Šùy0:eÇÆ­ÈÝò§i …UòŠ
+§)Š%URd…˜<0Š¡ù%¢ÁK èMkÙ +yÝß0â$@§&ŒÔ*ÙíˆI–‰(7xž$¦ç”‘™ÒÒvzÊŸšÑ£ª¤QåEYT&S²A±§ìÈÜu7<­§&­ ÆX•0Á²) Š58!Œ_úg¥ö²¿n4˜œÑÆÌÔ ô2VrœW!À
+]BÈ•4- M‚ö{©/Ðò"j²H³Ž¢Ç‘}n>ç|?‡*Ü°a\+˜ýßIÙ;¡ïhîO{ ÁDј"ù
+¢wˆê´™Ö
+&µü‡JyTt„IhDRð™=Xº§UŽÛüð\ᄵô¡]Skééð9!““ M¬ï}ÿ–ÝÃzþ¸\‘ÎlHÃaÓr`$+A½)»ÆÕÒ)Ù3«}+ƒ¦¼³Fá¿Ï]£pDòÌþaÕòœ2|0dzaø0 00!ð “³Rõ¯ÔúœS oÛýÔ²»Jѵ`Z)Ú+IaŽ'[+w\<ðZ_ë±®–_~Üòúïö]sfüÈ’ŠÙUd\‘ Núkï„ykÎÊ›êëÔ<Ž(@5AŠ¹‰a0°gŠYp¹…æȶ¼à`yhß‚¿`†/Bf‚ߪ9dâa‰úËš®E[FüÍ×*Ÿ'"/ÿ>°gwQˆä
+cŽ1w™YhvQþû £7ÊÛFB­Ã¥û¯on¼h¼±9q¥|Õ52‰_­0[Ý 6ýÔȃ`LB¿zþìù³ùï@È¢:–z]¾|4Âc Nqg¶µ'–›Úî<3ú«W;mó»jrÎnu|3Î×[»w9»~æx·ž=á_r¥‡lƒŸtÈR&‰}²|¡a¹õj•:¹g;Ë1³õWk©Zk_ƒÐ_}n‡–ð¯³[ˆŒõ M"µŠX­‡ƒ®Þy JMmq÷×ç¤böÞUŸ‡¥Jvw]No=µúàì«Íî‰9Sæÿ‘CÁ,‹Šè8±ÏèH* £=¾R*:`›ìºÜÿ ‘A8Tµ1&ý6‘óNÂy&)u4;NÇm æýY+Óè¢F¬gÒ‰]
++ 4†¥¸Bœy=»tµ52)ë~ðì컫*ièÞïÄèßÅP˜¿WÔ Á²žÛHÐ*4`9’ΔH™>ÐYü‘R0±PÛ˜Õ7§”ÜS|'$ƒ¥ÒX$ËkXBˆÝø6ý@~mO+ùŸê[à`‘I’D$±‚ÉóJÁRI‚¦Xð‘‹Å7?ÄߨµhN(”}“ÖÂ1¹¤aÎFš(¨HF§ÄúwrÉ¢F¦äÀ¸î]¶F„M<² ,AÛ0ö…áÐÕ˜¼A.ß×ý ‘I=8¢mú›”‡XfQ#pD/j¤^P&5S#÷â1)äaD ` FµAè”râo8×QVþ•¨d¬¯
+Ö×8ë+’|œw¨ì˜hSæÅ[fÝ ÞÓÜ3ª÷1[YßfH•`a©l¤PG™²´‹föÒ¶“RîÁó@ªÒ}åÀ´=0§Â¤ygï?%?üÐKid¥ê_±õ©xîj%ÓJpJ)žÒ=Ózñ{JžŒ ›‰Êùà—ZŽuµ¹Ôr¼¯éÿA#‡7702-"œÈ+½]Ö:X¶öüx¡¼š]†H.$MA?  .,$U[1Ë#Rá˜m†ëOìj¤éó²¦[Ñäõµ°É
+sð’ŠHphdB÷‚Ffä °d¹ù4BÁ•@dhVdÁ‡ãŠsÜZ8¡
+ÎÒBtvœùþéüü“§€§OŸ>_âõäÉ“gÏL²œ:u
+n(p dqýºÒžªÊê®8ú–þË-öz×…mÙ½UÚ¹°Ñ_‘uq«³g«;U«ŸÛ¢_¬a?ªä/ý‡úzmêºã
+¼?î.9ú]~ Åâ3YŠQ­H±06 w5,²ÿàeãÝA| Ûû΀m¨Ïî_¯~]ÃêpFÅòš4$sįd?äûÓÖH>Ðqø¶-º`ß´UÍÛ‚Kö dʼ£tÖ¾)ŸJà¯jì/FbF ýšõ‚h#ïAdÈ
+*Équ7kóßVýwõÐmѱl_rZ8 œ/±2-"Ž—‘õC˜®^4Ëj8!Ú†d
+n4FBÁ^Ùö@‹®ªAz*Œ””ô;f|ɨ¼i–/™áE²¯lÙŒ%,°’H³!0 GK(ÐȲþx:ÿ©F>’7©\~0~¸ñ)ˆÙ©oäMó/êU‹ù5,»åŽbŠ€48.¯…bšyyN‹À·sºÿc©‚ `»`YL’F<iá h‘EJ7MµÒE˶°:#+¢ýš4ÕÈi ½E[õŸ=‘OíÁ&P±áy ‚dÂú2ƒDxQeM€ñdQ­¤…#JDn'¥ÿž/»k^µÃ€«—µÐjY¯ö×k.àò{Fèš­jÙ¨šÕK–K¸vç,à Ìò µ#’>½}7Pdºw6óú3çÄY¦2{>èÞçÔM@)ϳn†;SÓq9öìùñµòap[B2¿møz‹Â5v/aUâU~ë\"ó§pf6òÊ•DßÙdb>²
+œGmz¼o÷¹¸ƒ¬-cX3ËópÛ Ä;uùÕ6óÈ€q¨×1< Z·~ñPFx}«A¢B™n8fÖ_ÖO[#‹Ø[«˜3*¯›P WÝÕü ZÅ-³j ïkÁ;j`^÷ßÂÁ%z€#G…b™¥ŠsRˆä)ÐâD7IBL,àÀ}¥*9³rI žÑƒ6±Þïd$H´Œx PÑGë_i5Ÿj%˜`
+y0ˆ à¡ õ<)ÓϧeöKG4gDîk!ø¥VÓÈzµ¿^û3§”/˜Áë¶@û—”²E#0ïˆQ"mÕ8¶À|ªcú¥Ý?ëÚÿ~÷Ó]ÿ÷9Ù»÷tçqŸ¶= Ûž&ÇÂ[/F»ž9?¾VnÆþxùJ´w&9¸·¤†# UŠÚW\û»Tæ·µŸÕt^¬É\HôN \¨ïûbs×ÅpÇÕšÞËñÌÙx×¹ºÌçµ=«µ¾¦ëR¤óóú¾™êžíJ‰ @§¬Bo–$>Š¿t~s×õpæF¤zù8Ù9Tžæ «* $YØï ^ÜÜûE¬ðs)Öù?ÓÈ¥Ú ÔßaX$¢™¶–VÐÈ£¿=\ÈŠ=V*ÿ^Åb2 FŒÜ£mÞñ„{ª1¯‘lêi§­íT“m²©,Ûîy-&„4S oadRb­Ú«!ïÛŠ·–·ÂHì“ÉÙ†5kç­íb¡F°"'*4%r„U²²…6òrÌf¶û´ž2}oÈ1”²¶àl[ñÈVûx‹>Õ`L§S-EÙVÏX£c¼þÙi¤Á;’rOn‡xÆj]ï7WŒl“|èQ&¨Bwý¨Ó<Ø‹ÀÓ|g}H
+Õ¡ê^Tà ˜ê¹bªOÎÉ•×ïQÎ¥—CY(zWuò—ÍGº3Ò/<uN|Êv:s¸/}øø®œj¤1øDhl‹f
+?}~<ÖFâÙÑxë`$ý‡HóX,7¾yÿÙОáºÜhuj2ñ|Ÿwg‚RPcq e$I\åùþš]ƒícáÌXM>¸Ï*¢-£«+ë/µÍ f€alÈ©,´¿â¨Ÿˆç.„Z.SãÁ44àÍXmúb$ûú¦&¶¤„ãø¬úƒ_å@ãñÿ…FÎGÓCáô_CiàŒ[Á)ðíã(¦Hò?Üt<F‘•“•ë÷îÝ€aD’¡)Îä6«Û!S=hÄÞݸÖi»¡7éíhB}l±±X£4ÎÀ # +QÇžEvj4ù…ÏéE?Ýf3éèÜú¤ý‹û½õŒ©5ÀÂÈÁ’ƒ",&a¼,X QØ–Q"Oª¡@²`¯´}«ËõÚ6sÿKOƒ³û‹¶×ël=k>«5½'æìjÒOoÓz’›úê…Ψù;õ°…Ä(\¤i)P±ŸhW:2ægåW Ch¥?i·ËH'²ìÏr扮R²LDM’ I¬£Œ˜(Š­Å ¢Ïj\ê›æõ¨ H¸J]V¢Ë:ä¬s­5²$y.kù<ý€ ýšs»x#‹~öjY0»A)Šë–è¾-TÞ’üsj`ÊØ+Ë4ŠÊ¤j¤”*–qRaøT‚¤¸É{—xߌÉ;iª¸«F%
+âÊÀaë9C.1 NѵŽâ<FaJNÉyÀ¬¦–BA#,BÆxqI‹ÌINÐÄå¬âŸÓ\KrMœ‘DJ€e‹ÃP’…œ§òß™Ó+nȵ jåkD«`±b‰”àÛd(âžä€\º,¬‘¹rF¯™Õ]5¯š'ÍPk„lj&VœQ‚ š @ò‰ ¼–§,<8Xît EC¦è¢%¸,ºn ¾)K ¨é:˜²ú'xçyzäÙ;o­†Q–4Lò²V9iöÌKî¿ þ 6Ï´ÉuIsÞá\ÓjåMɽ¬y@°³JÕQ©TG¡QÁò\ü<O©-$zÉVZ¸)—¯¦‘Bõ_¨ú\˜<7×-Õ;n÷Ψž;¢oFsý^‰WQε(Y%ëÀ×_êj©;󯑮–C}m/÷ï=¤¡€ öqÃDzOµ‹U©w©ñHvÜ¿o!Øöß—¶ðúqgÝDbÿïB{Û­>ÕˆÂZ͉gÄ%#™!,¿mh¾È&¢­Ã‘–±hv¼ªyµþóÂñîŒå:+·˜á©"ÂxƳg(–Œ6%ZÞ¥ $Z†"ÍÃÑÔP8ËðŒ(Âash,Ò6KUe­ça<Ø¿™e§ƒm>ÙÂÀû _Z¼öðÁÃÕŽ“ÜÿÇ=¸ç½wÞ…õCD…ÂÉíº²¯»AïݼáÔf{w²P©jïlpt'ͧÐgY_“µ³ÞÖY•hù+ÛO“YNé'’úX÷ °Ñ(;¹»$Âü“øzmêºã
+“Š#Ž`i’P(Îd`;xõk%<©TL9kg Ö¨¨8Sîª¯Ô ¦°ˆÒZTH‰¤xƒâ;‘0«4>Pü÷Lÿ ¥jÒ
+̨¾{rÃ*$»e‚q˜›Úá1­ÖB·uÿc¡|Z͉µ§%Ÿ†Øž%9@bì$Í©²kJÝ´U}S­¼g„€U³jùWõŒâ?%yiW(4Å‘8<W)XV½/5L‰þ›®ª[îj¨Î³JÍWF(‚svÅS¤‘LœAÓ²ÿžV:­yIÕ*•Æ^cp‘“m‰ ð7§¶!÷e%üDéU\7*ÙÀ%m]‚’tÖÖ/W<’Zî(j>ÀÀœü†«} zo›5szãœu+qÇ'rè¶ÉÌZ ÿæ|¥àgÎÈO”„Ùé„jØ•¶ÝК!Ô gD€ BA41!ì«ê±ï[`+åPH’ Y™d­Þ’Ëîú}jé9ñÛñÔnHq|Ë®¨§
+@ËP´VHž
+'/5§/6¥.F’PõÀ
+, [qœ€Ñ (¦y¸/É¢³jíŒR?§ùï¨5µÐu­áWyËôΨA†)‚Mb1†àBŒÂ5KÐÌ--4«ûç ¸Ê?m†æÔêûJ`l¤Œà \¢YŠáxŠ˜U‚·%XɪY©üs+8é¬ýHªTÈB³(Ï*(¦ÛyŒ&è„Ð'Ÿ*?k~\§$Ϥ^7¥„ΰ5°,Æ1¬€±œäÁQo±ÅàŸ9=<§ûn¨“ZÃ]½î®QqBÉy03°Ë€Kb‚r]‹ÜÑ*nÈÞ'Îæ‹J(Áð4³¼
+œP«C8ØÈf—‰ÆÙQ§ Y5ò€¯ýè‡â@(˜'„˜öUõ¿Ø÷í-µþÉ… ØÉR¨ÛÌìÊì^rN|Ç6šÚ 9™Ü½Îe
+@¢Ø©÷ý¡4?7@f¾ÂÇ|µm^Š@»Ð¸i*Ö3Ö´e,šü¢©g‡äIFäÄ÷ª:Ï­L×sª@Ñçê7ýÊ/³1<æè«ŒODÒ›»ÇbI¨Ý—¢©/" N^Že®´n=K ,<>c÷Ö¯…«.?_h «‘ü™ã¡î¿…“Z©6=Þ¦•Œ5¦ÇbéË-©EWY4Ò ¿H74ÐÈž²&š%ÐÎ9;¯‘¯§OŸÂû·Ï_p³oŸîûí^ЋD* msn¯/êp&@#щ’áÎWUUcmî‘Nk(áÌÅçÛü÷æH‡¹¹Z&mߧ¾WòÁjkh­#$É8‡ÚYéÎmnG4el(1sq÷‰õ/;®ûÄmgPªå¬PfÙ"…_ÆðäàWy¬·Úa8ëDB?6F›Ì\¬|°Ý¿oMõÑõ %g®⻇WÃT‹ó‚Z˜Cùi#kþO|™ÿFu]qü/@ àyó–ûÞ½ï¾ý½™yãY°=õÆx›/Ä;˜&%"RlÏb¦RÓ%¤8U‰Æ,^0©TðC¡J©Ô¨R~¨ª(ÊRkš@ã…%ô‡ª ¤gL…"UD¢"}5ºzºsî=ïÝ{¾ŸSq¨ø]‡úpÔKIaïèÑŸܧ‘"QLdû3J#öþ"
+ÛßßbOçÉ!ÿÞÁàÁüÃÖ… 3W&2
+a¼Ï˜q9ºZÅOŒb%”À¯‘*€DŠ(B#À/I›¥àæ}XØa–.)­‹?o7ÿØŽª\ Ƙʊ ¦*Âa¬±“ë¾g¯êµ`è·´ê/´º‹Ný¢›÷­[v‚¼Zâ±,˜¢Îk„Ý âÏýWš» dÁhX6ª¯™õŠÆ("Â,¢ V%Ø É _ê¦Õpê½nÅþbÃÌÊsNv¹Uo²*Ü/$–xL®a XT¢ÖdÕFâÛ
+¤IAj ¤ É>®øOúÜ.ñÍ3phy$–þ™á³C;Ÿ:N|Cɼ@òëÌ®‘Ž-
+#
+È$ùÄÝöaTø¼)}:9pæÙÜûÉD
+z¯µ˜Üj²ÁÁxì­±KDC/FÝBŸúrŒ"ª¢ÿ»õ‘Ù«ÐõÈt¤'r칊ÂÖÀ®V>eŠ²×ÇI>F•-bvF‚?ï²ç6ºÇ{Ü£mö‰Öà‰MB{x¦=Tè(Ùží Ïv–ï|Š4RV豎o\w¨+z¤74Ù#, åE6æVì+ú¾=‘Š°ÆÓîxö‘id¢ˆ©|p,ãNåalNæ``Ld̓ùÈôßXÚ[à²EŠC¾ØîáÈh¿<Ô«t[Ϸ⪰¨RM’±Bu¯È!}§Á€þ‰,|躆BüoÀBîøÌ/Ý“•·$ ²¬ ü( }a%/ù*oUKfÝ“®ê—üuoh¾RaW•L¤Vш çËè*¡ÃbàšD©ÉÂa@0WÐ=Dh6” ´þf ñ°ø7¦?èë#˜U¼^¸¡ÆðÒÀáSBr8¨–-Ù)ð¯‹Vå_õÚ¥êR°aѪYÔË–}IN\‹YDà‡’(pÌv…^±šÌÚkFÍ’ÐXr٬낫 †È’
+ŠÎIÐÑ@O„Pñ@ªH„r]¼ÑHü#RÛö³©ÜÉ"|œê?ŸJŸl9Ö°U@ Ðë»F¿\;wî|EîÞ½{
+;“ÏÈàŠ(
+ÎKí@¾ñ\`2÷54«&‡é"Möçü“Ã廳ܷ/cŠ aíïÿM|™ÿFu]qü?hží-÷Þ·ïïÍØ3Þ°=›gafŒÁã1˜`ÓŠ¤m
+¡)±Db8óáE"üÇ(9ÝÍy5E`x—Èfyñ#ÔôU¨uZ ®ÿ®_S/©šårÁã\Ác†#L•ä½ª¤¯éñU-1cDni±Y½áFe
+ ñŽØ°ªl‘½p+:ÇqUŠÈ(2MÚuQÉü]m^ÒÂ7­Ø¢š˜÷GgÌD'œ#è,œ‘$S2üÄ¡è›ZbIKÎ)ѧqÆHΛÿRÒ§á
+a¡×+6•ÐÕ $ˆQOÛÞÑæåú;úG>*!/
+Á».º 
+1xxj^ù´¥t)Wº”î…¿m)^Ìõ
+˜Ä¬p(tø{”^ bL'5çä¦Í¦'6mEõäzwõ%­áž¸mfÿŠÃ9«^—"‰.ÂR¼|W&„£$H„ ÇRÝÆÇÓ*G½ÎÕ,(-ójlN«_ð×­ÿŽ½§¦Çì†Ú Ù3Ì $"ŠÃ2źû%å–¨˜±c·­$
+Î×æ‡ •cíÎxœ=8ÔéLt× ¾ ¿EµÈ )Ç ƒ˜ÈÂi
+’r8Z9Ôáœ-
+ÊpÞÏ׎¶=/ ï¨+˜cmþ±v8±# !
+,
+«˜˜4îÚruL+­˜ö“ø¥´š I€œÄ8JÑÖ_FÊOÓ6iŠ´ª°hÒŽ¿²ô>Ïó}ßWï÷s¦ÕmeV´=¢%`†i®<GZvcÂ[¨¦M6ªF[§PnRãTiFc™eÍO)_–³L“å­4¯ó~ÍTî#u^U!Ž¯ÔÈVòpí+__‰¼†áM¸úŠhã·×hsIÖ’t“¯ÃÑfŒmSsXÃ}Î’£9Áö€±æï}Þ<ËTt®¢›P¢Aë•Êf%¾aßÅ´ó˜Ðešµ>c$hfpHãø«ÊPŽ;¢ÔO»~‰%„¤*Àø®¤år“»qU7®RÙ&µîI½ Ö˜mN”ûMÅCÖ<ÏKϨê‹hÅ{¬ÎËp¤ Onr%lµFU¢"l¹ γ%_é<óLÍåJÓEèyŠ³ƒr@HË $Iû²ò¿ês›áÍpT€KMªBD†²…4'£¿Xvœø‰q±ýýT´ûd¬ëÓð›*}À!ŒU È)kËÕÚð¨/rÇ¿î å­üz™hd°&–÷bxÌõ†Æk;/¶¬W `%ˆRAÊ[0íe÷Û÷]±[¾Øçµ[ºËjn¸ÃSÞÄ`MøfmdÄÓZ –tyOxÕñ[¾ÄžòZEøUrm\…j5bb¸FÑ5H{˃§?wm¹æ }S$îú·Öv¦«w¤]áÏÖW½·½‘<Ô„&¥ÐÈ°?:ꉕ …R®`ifñ/ÏÿB~øÛó÷àwñ¾k<EaV“žÒXáËrU1Ù\œl-9ÕLí­Bv›ùžê<ê¤Zõ§ëÙT]y²Uèi E(V(ƒ†
+Qø .Xi8ò†î\#šòWõ®eN5þغ†¼z!Õ¤I´’ëøÝNv›Þà3¥œœÃžâ29£Ì/‚ EõïHÆ3mL_°èL©·™N5°ý Ƴ ËE#¥§×
+É¡¯ÑxªMå¤(•/DQbØ—`{â|Oœh„ÿ¸“ß¿¤û/I#ãÂá8q<Ï@#ìÑ„qâ5u¾
+¥»+–O±ž,ãÊjà ˜³¬i–š´sÃ
+dõΦä)mš¢Êi|Bš¥Ý€[×xÓ%¶äUúi™a] ˜å gû³.0.ØÐΓãliÑ<M¿.¢Ê@ÈQ ‚Lû²ò¿ês à1i~Äx6°‚
+#¸„R¾ûƛˎ?•F¶víú$Þõ»Žî \w‚ Sbèž"׫·Žù£ã®ð­@ì›@x¸Œ/¼\42PõD&}ñiËh ]ÝöD¿ &Nš7FŒžm¾t´OÞ•BWš£igôrSgÚ»cª*r=Ð>Xñ†òdå‹.M;ÑkŽ_øž3~Iz+dtî*­=ìyó†ãí!ÌŽùc7X€Ç¼{£ƒþÈMod´*<ˆíkön½Ú¸í¿C#7ê#SRh8Ð1ä‹L¸v^ tw:
+çZµ$U¿T~mÿ:®·®¸·Ñp¼¡äü:õy/s±N¶OnÒþ:Èǽ\‹7iPÃU(¡@(%AÈqJIpµQ!«1d5ºÏ!ž¨+ém†!ŠS-údSQªy¹h¤èl³!Õ 9WÍ÷+NlÆñµ!¤¢˜ãD€"Š÷Ç©C ˆ<Kô,IàþÌ‘˜p$!B
+ŒŠFë4´ÓD¯÷j"ÍÜîŸñ¿ AB¨HKÀÆyú±t8ƆºqÍñí0Ýp0¡"Õ€ÄÜjŒÆ &iž±Þ-)KFcDÚ^õ­þ²"ËT‚¹?dìBì3œsŠq<¡\9JšÒfŒÐÕ""S(W*NŽ12r®<Š™²¸õ±ÆöˆvMRÒ„Îñ˜)[v!ÿÿ‰
+I`9 ºôR*»dûAÉ
+$*+ÒW£ç7ó¾óÞŒõ¾Ÿ©KœñD¡gª&zËž¸f @Q—˜pmû]06팟wF®;¢ö Ôô÷hÄ!œs‹\¹^ÝvÙ/\rÅn•Gn:Û'kãÓžÎÓP÷} À„[8[½ù¦kÑ<ÿ÷ðÆ&œmSó&A}i‹|æ5) AaRjîWwî>ý0òíÿRdA& pöóÏÎ áP%»†7ŽŠU¸(Ù¨ ¾f øt]aHõãb9Íè—)ù÷VŠòñ2s_H7Ü`®7%ýÆ¿).L6™S!cÚcNû-ƒ¡‚dØœ¬‡)-ª‘þ)[o ›Sak&¤ÍºVö×+›L8“‡“y4!Q&•®H–¨„¥å8Jb2‚”Êä™™"zõ¦ò­> ˆ%-"Ęª_àŸ®Ï­Ft#>€bÁþF’\A#8Pt¹EÛ5²¿4Âõ&ŒÅÁߣux@¢ëI€CúÙÞ˜ö€`íë4€+ºcæ½m¼Ð€W[¸f§.Ò ßþ¶æÃCy0Áˆ«çÍ#2¦·ä£ìî¨{EŠ@ðÝÜjºãºÝ›1£ÀHFFØ•ÌŒBÔÈŒfÕ}VÔÈ×lî·ý—ŒÇ\åmMù]]嬪bÆh›Ñ•=ÒU^וü]QòoÖ½“2Ñ’GKÕð€à`= Š×TþCSõ@µJ¬,œí&h„]™ó…¼‰%³lå=uÅ]}õ)yM/£q\.ÅWóC±®±6Q ÐŽí„ÆxdgZèÊ=3^Z#£Â®ñØ.˜vOëÏ58ƒÃÒ0Ò¥2Oz¶]­&ÝÂåê-üñsöÖéšh®ª0X„pÕÛ>陈ä˜t~âm½V?YùÓ‘ªï¸¶Zl?áK‚*cµ\¥Ï“ç¡'ªÞ™r “Ž¶éðÖ 
+”a7ZE¤ƒÖT£fXÔˆéõiD7è·Ž…
+?ö3o›$r™:2K•Z)Î (“ŸOQËY·ÖüK¯)µ–ô¸õ©uÚLX?„Z\˜ Z†€IºÑEó[Rë­™zuÆË'æ~¯~¸Îv¤™‘ËhT"GEUÊ"ƒç³,Â[äô )Õw¹¬}k­ƒëÍ–ä:ëPx"€S:‘CŠ,hÄ4€åð ®_8IlБBi6P ®Pô%ÌûEf€
+ô „Ū¿nžªnAs ¡;ØGÌ
+sS¶Ôa€TGpé»j묲ú‘ºüWÍÚ9ßö_2þ¦ªº¡.¿ÅÛ.«K¯iÊ*KÿÉ•ÝኳöO46žDƒÓ4‡NÉäŠv\5ÅÛÿ¥«ša‹rU³jûmMå#¶$ç yK"ªm”e_ñ¶lM)(e2'8}¸õ=± ·lv¥£;@#¿í^B‹îž?‹îHt±:Š )f•Q§= ÐÈ%(âµbu1àˆçª
+$¾ðD"ó…8:QùCÅÁ*d( rš"Q”%H†–Ë~Èá †$+›.úâSöÖK5[®xg|‹jäJ­
+óé:]6À§Ö°›K@SIÓ£y§jº¢7nØŸXpBÓ×±xõA„
+þÈŸg*n«Ój[[Ü@Âg«„ÁAþ
+jªÅ˜|9®,žUUü•-¾­YùX_3ËUÏpå³Üêœ/äM,‰˜UÛî+WÝ×–ßSÕl!
+i>l†¿¿¾ªùx[E´uAYŠ-vg„ÃÑ.pÔPû.»¡HNÒPÚÔùèqOäKG|¢62éŒL¸…‹ÎèdmŽ(ârÖ7ÏGìœ#rÍ.ŒÛ6"’·´„œ€×@’AÀÌagc ¤-X†-_wÖÑ:íM\u
+“¶ÖóþÄ¢±Ç>÷Ä ÄO¹¢W\ñ³>á½"G±”Ä »‰‘( •å7h ®ÛÚsöœ‘‹žÈ”K€ö˜ª=zÑ-|Pì‡ÀªqÚ½oÿ7ÿyü
+K²¥¨,MÈÚ¾rí7¥íCV¡B²}fìoÎœÿÏEêa¯y Öq¡1ÿB‹ó\PkYÖPÄ5ªSÄ–B¥‹")4hÉZÛHmv²UªÖL˜¢YÓœvè­e,3Îí}ñoш­7fèi¨)N€ôoõÅœ}Pp b:×ÁŸ‹óg£ÐÖÓÛéêë„ëæÞ¨n³zgÝ3)Ÿ@ÃÿÒ\|YØÎÆÍ}aIÖéXÖ‡q2[…E(ÐXCÓjù}Ù»`.X’ A#³*P¤(íÛþ*ëJÑ’¥ô–Åß„YX/2Q Œìz¦”ï¯73<Å -'2DF³ï!^Ì™µ.ið;·äž²i^ymôµVé­Õ;k.¼oÊŸ³nþïáè ØÉižß]Z3ÖqôräèXè d2Ü=™’IÚ™±Z¤ìŒÅz¶”IˆƒWƒäýØ·}¦,¢k¤::íèñGÓ®pÅ—Ño*ü:°WR4lh’ Š4 46˜ã2x¤Ñ|»Ûw¹t×-üJuô÷¾ÞtÌxÛWêîú¢2 )Õßù]E¨Òd§Fà 'œÄ[Y^ÂÑÔ¯Š÷¤kfÊÛ¯ø÷_÷‡§¶„¿¬ŒÂP§üÑÁ‚&ˆ`=…YþøOÞ{öÜ Ï^hþ>|ÇGÁÝž®nЋ0B´ãD#È®ÕF›²’)Œ6¾ªTÍlÞ0±^g›lP&êÌcuÙãÍîDcÖX“ãý
+á-å¦y J0ÒfÎDâu¤œaÈeОlÏ…fHäìdš¬Ôß9Ø䮲Œ×h‰`Îp£ëb“íP©B°ûó¤
+_®9×ëIÔë S±ÕêyuóðhÄ:Rç­÷Œm 1™ˆÁˆµo (ÿ¶˜ÄÒý¼Ð
+nÁ@>úûbz‰/[å¥JˆÉhÃpÛKˆX$X·Õ,½hò/¨%³æüEµÂ42Iû¶¿Êš“7Þ·ún›«ò06ÒTf¼
+QlH˜X ±(Š|Z7$å>P‹ˆKæây­dV.¼'—,Z½kY«UÖ‚¼iN+¼/çÍÚ¼Ÿ›¼,7’ÂïÓ<C±®ç‰uOD{.¶y42Ñv$Ñr©­ ­j(IJ,‡Qwnõti0]K 2SÙ‘®þjKäJ@à™ò0häkü3ÿ¾VKöÛ9[ŽäTllü4oûTàÀtý¡K¾=q.CP¹Øt|Këòèµòö¯Þ¹î ݨê\9åà‘¿TÄ¿(oƒ|ÿ¥÷°P<LYbµ¿ã.ßYTÆddr%`|Ø]™®y¸åü¹2ÿ ˜Škð隯ýÓÂmàÚHÊ‚têäÏþcÿ¡È“'ORFy¶íVÐ@ŽdHg“c¸Â=TkitÔ§4²bú¿lm¸Pë :’M–dÐœ¨qŽ×CiÉu¤Â2Ù`›huŸß‘ÓÕ¬Vn@e @H†Î«¬E?oµŽÕÙ5çWìïirŽ×YFkÉ­®d³c¨šïô¬wdª¬AB‘<Bcsˆåh„y’±fr£Œ‘¢J²„`ȉVgè±'uÀÉsŠ¤U#ÚhPŸÀ‘œÙ Ðh=fTÅlí‹A¦ƒ”31ÈtàÀ
+DZ"ÉòΔEfüQˆ¿eá«[B×üé‰à©TøN¢7ñ©²ÐUâ²ÐTÝÛ¿­l›ñ…oTħ«:®”µë׫ã
+vLµ¨Œ4Q¤Ù¯l ÝÚºY™öG?¯\±ÿõòhçŽ/|µ" ¯9Vð&o
+È3ø¦k´:'Yoov%A#ÚhÓ«JU°‡’¨µŽµÚF[³‡·z†šƒuÎÑFOb›ãDÀÛ¨4XøŒ%‡ÅPœ 22¬@ÁkÎ?×bú$¨LÔmH¬¨ˆlëD5ðÚÍÚx0g¼5ï£Ýöó ¦S⻥R{±TïQJìªMäÁ¨fðT†Ls˜—Y†7ÑÄwåïdÓ5²L‘¬±Ô»§(¢%‚éÒHÖp½6^(rí Ñ:™b2y6Ûíx˜{£ ST§Â™N+¦¿–:*½ œ»û:=ïGÈP50¿:`ÁpÍ‘úbÀgäÀ(XbDP<McEàrR €Ø_IwÔóÝ;”Óax:<d{yXzÃâùŽ§âB„ß@ˆv9ˆïS&û?MewlºFæ­Þ9±4ò@-Lû¶¿Út°þUÙôÞ›¿ÇpH£Y‘$8Xj˜5bʉ9‚%áb!f¯iþ¹øÛ¦;ÖÒ9yó’¸ù¾Õ¯<+ç§ý-Öêu©EÕ7/çݶoX6Þ³ò%NÂXÉD²Ü¿¸ïÝK±c‰È‘ñȿد“Ø6Î+
+."WaÀÄÃ`0˜yÿ›Ÿäÿ¾¿çT¤w\xj@r:Ò7"&Ò±ÞÓÁn0ÕþàÐ  ­Áº´QóÒ¼[œ­îºî§}âeŸ˜5@ÿ­
+͹ÂS®dOxÆ%ܪŒMÕŠ|!ˆYoøªG˜«ìšóGþTþ
+MР-‚]¨èš÷DÎû…lÞõøüþèe_äóÒ®)¿8妊;9œbIÆ©V^õÇ/ÁОØDqKá°°”òúlMµŒFü"¼ÔEo„&½”?»\–¤Á$ëÛ×-þsà±°°ð"KßFæó`q©©¡èÈy™åvÃX£m´Ýn·Ö[Ó€‡U명É&ͱ€eb£åøã¡]_£fC‘¾PÍñ$E 8’C0ËJÈq4—§0„&IB†r?T¯Så ´“Mš±zÛȪÕS°ÇoÙé!_{™mv(ªŒk­ÈÏûöoÊ–:æãmŽñFuº.oOLÊ"*G+ VÚýuýQàŠd˜Á80Æz0ž÷!§ËÇ0Ná8Š(išE I#®%XÆÑ"”FNâ4…â8O2rŠ qB‰f³‘ëðƒ1Ó~Ñp(ʉ+­G}XPÆqbx»ÕhÔQ ÁÉ«Šü¿( A Зïj˾P•@ÜSeÙÿŽñwÞ=««Ü¯4ùQ‡>‡12˜PCsYŠ  Uî‹®œœ¯ÿÐRzG_ù [’õ‚ŸÅS7uÎû\ÉW†Êë:ç׊Òß1z9ñ‚ãh’ê×Çú&»ÁÄX´7Nœ ­X#£ÑnxN&ÂÝ'c{R‘î¤Ø=)öA6ˆ3‘>ÈüµŸŸØú&Ü<í~/Ø{&Ôs\”MwÁ#§V>.À)Sm*Ú“0¢å`ߤÂé<îŠë´[¸RÌz#ç¼áluaè¼søtUhêñE/xBçjÅ6µ¹…9O¼ôa ø«¢šFcÊ¡ Ž•1Ȥ³CºÁVýüŸV‡>óŠ³U]×\p>ZÒË/ɲE,u½a+Œ{¹"8^ùc•åÒ‰ÉÏ»¡’È5otνÏúÂSžðg^© æj®Z„+Ó^a>‡“ï{~ô‰¨‚¡[}5‹ËËÿX^’L²ô¨FA#v[P„!(…•ÊéЧ¬Ã­úT›-Õ i$Õ¸Z]Õq¬Æžn5¿å—•¡8ùKÈ0…\Æçªä*çAŒ
+‚P¢r}XO:h]S¾}kå×Ö£­Ží¶T£y¤Î2Z¿j~< ;Ñ MµÂ[­¯<¶QÛï7ŸjÏ:? Ëp«-U§NÖZvÔPxn)ã \Ýæ_i÷7ŠƒF”¢¤‘¨¹?n?¸E¹k£>Øhìô|%ªëéU@JŽñŒ‚Z›‹äÊ Šœ¨P
+| K¬r(Šr ¤èG¹ðhD31ˆRƒ1­RÍ£8ŠÊu8 ¹©v‚FngÖÛ§M#7¸Â»šŠ/ÔU×Øò³\Ù$ï|›ËO°Ö½ŒyD]ô‘ºò:çºÏ{þ¦ñÀmŸkœY/øY<ÕqÓPrs~©-¿¡q~¥,åì%Ëh¤³Ì—Žõ‚F&…ÞñHÏ“i,‘8JLlÛ{,¸ã¤ÐóAtß‘ðÎdì[*ˆÝÇ"»ß¨Ûð“òšúü²Ým¯Ãˆ'ƒ‰‘XÏ ±w¬k ü?Éø§˜ÒbÉ£¤‰S^ôÅf\¬O¼â Ãž¯²¨‘tÆ#dzýe_ ð~éÆŸ9¼]¶²F½½˜àô2D‰¢K¼È r³Fžc"¨OÝÁuÛg}Â…òÍ—½­Ø
+GP
+£îÕ¦'Ðùþˆz0®>ÓöG­ƒÛÌoG顨⠠< èþ°MÛ/Ü'*ª
+H’¤ci…ÏÉ·¸(‡ S0†irI#ÝZÁE!§­‹a(®=$®´Í`T?3 Ĺ·B
+ŠáÇå~V ÉP䖺䎺$£‘»êì/ûß9Šîj‹¿Ô–~­­¸¯sÝS»n++n©\·ùâ{ºò»úŠÌ JwÂ{)K²]í³xºã¯Æ’;J'ü¤ÐTüY]ÆÓrgáê28RñÞ‰`⌸gLè–4"¬Œ’
+"=üí1ñJ¸ËÛôËæͧ7íLoé“("$à8(ìðêÌZ9ü‘[Š]0Öxp·!¡X÷œŒö­t\Hr*,@ÐHZìñhí°3‚…BMP¬F ‰Ïy"Ða/e‰"ÿÖHµThdÊšò‹âi{3Š$A)pšÏ!8Î!$(âG4¦#舡xÞ™®
+]ª੹@üqù¯V —jDHÎ9W-|ìqr©epòf—`wm6•®ÓÀ‘AI=Êê¤ÞbÂÒN¡Ž…M¥Ÿ¶€Fæý±‡É$Ÿò„¿ïùqk­,”BàŒ,ì‘ÑÈÒÂâCü&Ëp•#Ͷ—.áŒ£í¦±ÇH«&ÙbNÖYRµ¦dËjuUå¸×–l6n¯XC?Ï£ó4ÉÓ§úi‘ýÍúü‘NãD«f¬^Ÿ¬³Ž¶ä'×kßmÒ¦jGkìÿb¿NŸ›¸Ï8€ÿL
+µ-í½:öÖaÉ·l˶,ɲ-ßÂ8i¸¬•V’ m 6G’픀Oð$ƇddÒ™Î$¯šIs'@&$Ø`nhÞµÒ>k3„¶Ã 1N=žAóÌÎOë=ž]¯öûùjÈoN7›cõb¬v±úÉñÙ'ê„xåðÓp½å¿u¬ÎzxÑ®w±Ê<Ö`Ÿ¨µN4ò­9†ét0Ž•›’M¡'$õG¸Þª‘ÁÓ"°†=…¿?hî ;~±6{(Š~#"c¾’µ7š¾?jëß"EVãfÞ€‘FZoª*w¼
+Œ1ïW@#LŸ,&¯¡?,(–>…ûÕ:ø‘‘)RÛÁØæÄ’(2ÿ¾U)Â.›Ô¾É\gWŽ«lþ5&ŽÉ¿e,¸Ïý/¼Á9¾a³/qÙ—%ÇUÑy“wÞcŠ—¼áµ¬ëS!üX挅w WÄ¢¯¹Š,ƒ"q"ƒ`†£;'Ûç5èJ$é…UöÚ´½%·LGP0{]çtØñ.x㨼ëÃàn°J¢óí6§ÇB1†gsæxçÛp:uÇÍÝÉ»Ç;€Ï­‘ eWbEÍ9¥ðj‚i,­E¦*^û²"€x…œ=í-•FNE<jg*§*ÚÏz•ioð÷¹­A`ÿ†Ô±”œ”BhÒKHÛ2\—o˜ñv«ØpʧÀ%wËÏ:þ…2ù3Ÿ|Ñ­œòN¸ÚO»Ã¦n¾„kÌJ‘Œ–´jtEá$dM¤!˜A§¥IN‡õ+‘ÔFcÆgÕ[ÎTÊÐ',¡à°°Šü4âfÓ ´(ÀãªETx,|~ÿýÜ»s 7ŠÂÚÅKcæxuÎX³k²ÄjÓ!úO#æ#µæa?¿¥èg I¢¨¥qŠÓR+t?1z¸Œ_–eõ×fÄëÌS~6áÓ%¼Y‡\¶X•eªAL4ðàåñÛ¸ÑúöÛ&[Ùx™\kž\-Æá–œÿ]ñ&K¼.Ÿ)ªxiÔÒµ>Ùôçç5àÂü(Ó¯°óeÜ€1=Ü¡ŠdìÞœ"Ñ4­Ñhd6Ö˜÷acf(ª}_Dm¯û…7hÄ´/d<1õ(ÖÞˆ±7 õ%­®7,ô…žv®ÍGhÕE¤ô2¯ j@/PÆO]uÃPtÓX|›)ºÃß`aMá¬PxYÈŸ›ÿz—/½/”ÝV­U0Ã|+.yÃ/jY<ZªFXç}ƒú˜Ý`*ü¤ÇQÇy Ñ/¿±~DÞ&™ íz
+/„»šòžÐI·üç²õšÊ눎­Ím¦œîôò‘¬†O
+ÚŽ­
+ž®é¼ìŽBþâÙt²*tÁ™ö>³ÿó®ÐŸªä‹.Ø
+¯sêà6“ÿ5ï¸e\µ•”PB 0`Òðë1eçT{7äûs¨àp û¨¼ 4¢x[ é`Þê”ìûƒÛ¶·¬ÿE}›ìiªË,r$}*J`8NS¬† UELmxë#yw<ôƒ(’ÕœöMÈjÛ[k_¡qÒˆS4Švø.•‡¦½ó‘q- ETT´Ÿ÷>ÎôsUáÓîàIW
+ H“;v øùçÇ y4uðèÑÿð)<B‚©ÞWó¤ÉfsÌ áǶ¸ß6ºh±N4ƒv¤1¿m¢Å:Ñjý U ;9'½EÒha`h&m%“J6X2÷ÔdÇÚr†[ó5e ×
+ ÆœoÀqšLÏš–ÕýiÜàeÓ4–(QìâÅ‹i‚<ùR Ï5òGžÿùÓK2œ
+âbM7¸âŒüªúÀšÒѳγŽ•}NeŸV»®k¬s´íÅÁ•Ì)‹A¡w9ÛmÒò€´ÿµ/ҦϘÒ/×=²t€Ç”.&èÛCÒ¸Ò:A:H
+©¡
+9ƒážòòÀa»`PivçØƼM#¾È@™rñN~8rÖÝðÄ>©Þ4¹L-ÕÉèmõS=à„)GôS?¿ÒúàQŸpÅuà6o9b׃MôìíŒll¼†¼ü¸?šò§Õ1äGƒ‘¡
+DFHŠlœÂ¥¨Yn>µ=çlU~oe~WˆíË<¾éè«Ôv—ç¼W!’dÑb9!•àž|Mûª§ÿJÁ‚(Ž¤MBý¦)ï /*ÉÁÄb&Å$¨J¡”áüB1*ƒXPŽ"b"hÌïh&:ø®îPœ;WvÆW­ ÷ãÜÑÙú}‚ƒF ©—‘3´3ãÛûKÆ$i†a÷9휧lS LFÌÄE•mŠ³O²¶&¦e¶.°6Ë4›ù‚_QÌ«J¦IÜà=µm–¶A7жEÊ
+¢xaL3%‹¤ñsÒ:GB[lз)Æt›*š¤Kï«ÌSŒyRc[¤Í³Š’ÛŒá.kZiHñàY:H
+©¡
+)\ž\¼.[¤ Ö)q)©´Pº2½ñ»×ŽÐ[•;Ö
+ÇêßêÛûóµÒH_ÝÛ ¡å\´Ò/¤ßOGßVŠaTÉdfer®¹„”/ôñÔÈ` :à|‘ý¹ •j×Í”ébˆðó7ý 0ô¡<àÄTÙÞ?X·×¼.Ç`¶‰P)‚5Ó0ƨûwÜÂpYÃJëÃ
+ ™qwtq;Ðœ*kj7WÚÅO•Æ³æÚTÙž[ž†”ƒ‡>€[Ëøq¯0ꊤ<±¦Ao¬Ë¾í{Zƒ";;‹±ß~ý<ï'žÈ¸/ž
+ÄÇ oÞ´VqÂ^ƒÉQ0†R,íøuûß–-?]þJ#à/Eòtùà/~ —É0Þµ­ö¢î­êžMúD%hD“¬Ð$6åu¯ÙTq«ÏoÓþÈ•w«k‹Õ6
+û'õuúÛDz
+B¢”–æÞA³(ëÙ5‡7H£~(É<Ò`8Þ”y¼Þ0­3Žú¥=> Ë•0àÆ5X•ýÉ•Ò?˜>ëû\_Ôxh›hB‹ xN A„ž‰² e(’eàö,³.Û{y·iûÃúɼž¸2ä-»8Wê‹ÐÉ&*ç0ŠÖ";XüôÆ?«‚:Ò:WZp@½)ÛSŠ úã}²ô[Òv±-öyè’¬mž²Þcÿßøë]*îÜiÀƒdM˶Yh1/Z)Ùñ˜‘ŠõŽ¯Ç]F]"°ÇMÅv[o›“³¼Vð¦. h1X¯+KÎ) ¤ƒ¤Z-@´©Å%ЩÓbé ©ê¡P’ñ…zÉH ®9±T ÜW¼uQ¶ÞÊ-¿VEûIxûgÁΓ¡Nèéã¡ËUA‹¥Ò£˜7¹ªƒ¾F'kä³PJ½ç® ï<ß5ÛÂ3|ÛóYd׉o_)œŒtO솿cÑÎOÃ]Ñ&J`qŠÄˆ"Š½ä‹A >ï OÖD3©‘ÊÐÅêÈéµÑbVÈ¢´¹fG˜ßUlþkE+@\9ë ÎøâçêâG]Í… Çf¡Iìë‚×*+“ÞðRóƒC®VÆ®U'®–‡¯•GÿR ›ßAߢ €‘õLÞ¯V×þÙÑúeyòJE|ʾâm›tE>·´t™kªäÕ$ç ¹Ð`Þ, W½ÚŸ˜¬‰MzC°túU¯Ï°ëû,îFÑöüú£>|ª‘çyþzô4è}¦eO¹y¸Y7âäøA#ú1þðÊuÕ“ïæ¬×è²³Hô-àù®€3(A#…±8¢Ö h DrUõö;–ö+lhMÑÐy¼4R0ì—‡ê3΃×Ç›ÄÊIPL"þÀ£ôo[)ˆqº'¤lç»CºÁ¤r MŠ¿‹”*y(C¯ÒÙ†¢á…kPJƒéµ¬œÜPØ—“À}_ÜØÕíÑÑåæ5 $…þûÃ*RƒÃÃ9Bá¼Tžñíýe»0gI e7õVè¤ÐòRìµå)¶†²[²5õ´EÎ+î´Ñ}K¶ßàË2^ð+ŠÛÆò[¼eŽ/[ËRzË-ƒeF.˜ç\/ŒY}Vq'
+î&Šƒ4ÂçàýŸ
+uN„:@# ‡åª`$Úq8¶cäç¿ýŸoar1'>øh4°x9Ü óÔñJi$Ô ù!F#@‰ä‡©ö
+-NèQ|º*6õT#Ó5±óÞŒiäJUü’«õOî-¨&[KÑÏEW»§<[¯®mEœó†&×Ƨª"ì­ “Ó ‰d¾[Ä)œc1Ds¬²åKOà‚7x±*¾Ôü«ž©Øú_–xcg*yî2šþ 8ÎÓ,ìÏuórµnµ,K4M¢„@0Ž L Ï•:†ï.ªû}uàºm°Vç*‚ç«c—kêà¯Ï¸u“G(- û|ï¾î=yðxòøÑs†<|øð™Fºó£U© !îræ­—G×åøóŽÖÇ×FŒCþ•j©Ê‘FóÞF6'Ë”Ëë‘Ï!ßb Š`$ ¡)M®”“[ Ô™ÖB}WEñ'ßwj):´>ÿä&a´N©/„¾<Ö”q¼îòç7q±RC„AQ¨•ûÚVJ#ƾÓÕlËë‰+=1ñ@œ tþˆ¬Íq 4!f!B6¢ÕóÜFOÁîÖÂþvøœ Q5Ò—z£ì@l¹yá\a Î×;Hø½…eýš—øÆw‡”ÔQö¬.JjÌ꬇ÎÈ•Ýçmßp¶4WzM.»n²Ïl÷á’3]ð+ŠÛ¢k–³(€ :˼T6G—Ýåœà±Æ=Öæí3¢-¥¸Dë]ºôkÞ6ÇYoÁé²ýžè€XÒ:×"eý†´/5¤€D’Bj(
+ÕÐ$¸ˆìÞ»ïßàŽÀÿiäÁƒê¿ÇO>ܽ‡ÂµZ¬¦ö:MÃòxƒi¸Þt¤Ö8î†WL#úãÍÊ^6¹Š¶¡¹ t5]Pj¤Bæ¼®JËàFÇÐfëpsá‘zãprÂoÚ`5ê'tÇü £ü¿éèŠÕó¦DÞÐ:ÓX#½%ŸF4,Æ‘$¡o[¯[9
+ûš™IŸ¹kÒæ*o±×SÙ]
+Ú»kÒ4Í¥M.§òRñ„º‚ò(^½û§ÉÕG¿ƒwIÓÈtÓ¦ä›Éï÷ËÎ÷ûavŸ¡¬ Œy^›W™¹@eþÊ_ÆÌ_Ñî{ªÌçÆ}¥u™¶Þ#sïhrâêœ%Êô%kùBsW]îÒ4ÏæÏè
+àÖ}IZîkl«åPÊAQ( @ÐÌ"› D¼¯-x@™!`
+C½ÞvÁ|àŠ‹s
+W܈µ²Y}7=aèj˜d­¼îæ_ôýù(óMyÒV¸d
+'kkŽ=ZÇãÑÈ“•¿Gßüã‡"I)@К®Åmˆ–³1ØôKÒÎxA#l¬D)Ù¨]5ãÃâÔwÉ¥ uh§¶ÎžÖU®ïy==ò6
+a:x°Ó)¨ºÂl‡
+.ÒFKÒbe çÁKã¹RЈêí4Ј,Gdó£}LûÑÒÈ3r€7ÔÝÕ@ mGØØ"lk«Òµ ºÎ*À‰ª•?ÙÆkZ8C[˜=US}{ØÐQ¥ëª&[ƒúÓ5ß~f-¡o­Rt 4ËH¥R\|[ؼÈýh¤Õh:(Ãu›7¡ÒÍ€GøÊ$$Ž0˜”`$‹ÂûSJñƒ Ù+M„z„µ%¼ásê‚UÎ,›³Hf/« ÿl°Tà
+
+Ù¤@dÏ wò÷ÿ¨ÍùŠ²Ä™¬éÔÜE:ë!–³ÀZâªÌu<qyšÎù;i›ÐoùÞjy ‚rPJCÐ4SCõÉ[,Y‰Ê `
+‰ë²*ó£ôN‘IP”Då‡r\½|ìé¢FkVãÜ‘Ÿ€F€.íñÝAŽÙŒ;E$p¹Ÿo>Ï5EýuQ_íFQb((ö ˆŠë£|ã`eýP¨ù ÅË x
+M ;ÊGÜ¡gy »êj Ü´ú “ ¹BFh
+ú™zÙåw‚V(b Žúa<¾’ݤxUBÉLJ‹¸k¶Š[¶ÀgÞU5™ÇÝÜ%»o¬ˆŸrS¹¾ ÷§RþŠ«bÜãwù®:ü7<¤“¿a ~QºmtɸâôÝ°UN9üîÊKÞŠI¸KŽÀ5»Ò-Œ[|pœpòc®®¸;ÇFŽÊ_+){ôä ÀãÑwyüX<ŧOKKvIž.*Mø¶ûÿxn¤ÆŠµÑýŒY ÿ&2Iš"Eµ'9eKhÍêèDH€=ZCäé0õ³#tí[šŽ0Ù±]G™Î*ªk%Zy}g±¥ÊØY£ê¬Ö¶Wg¾_þs^Ù³Ý5ÚÖð¶÷yy7w Í/C†SGÁ3xך5Bwðº¶*D"Ã1‚’I<„ü/:Ç"ý_£‘ieYi™ÑdÍhwÌÐTÎÏñüC”F&ÅÅw7a’¶êP”@%h²„N‘K%ò$ŽbRønj$ð“ŠbrÅv™¾Ú7º—hómz{\ošg²î)s'  ¿ÀuƼ2oîÙ‘Í™WæÜQîÆpA(ø‡£²WQ‰„À4[e2×n9
+j;IfÌ©ÌqÍÎYuöeZÒþ•ÉÿšÌ¿­5/“¹°¸¬5Í+Í1,SŠ‰§À‰p:$TÒBr(… ])ým͸•K0ÇA¿R) `
+‹ ¿QëŒFŒE*wžÎ…Ëœeón*³FérI"ä ‘½Ù…°÷„ú}¾úµª``唈¯TpØì•nMA¤R…ÿu¸¡'Üë}¾:ÈŸœDÃMƒÆ!_Cÿáã Ÿ>¡9Â5
+x?W=¾(ñþÂð+Žb¨D–TŽos{5Ä/UšŸ„âÇêÛÒuþO;Î4&©À) „.]Ô Ìücúî}î›dé’f"XÖ…mÚÛîóx`äíoÌÞ·ÂáwÃ3‚¦Æa
+Z&OxªÈVg#Š$c·F‹ò¤À\mn¾PZÈ
+±o6©(’[‚ÒwBRk¶¶Éó¶iYb0Âq¼m¸·E-ç¤x>úÎö¼míлEè œmÁW\Á[é®ícÎ
+(þwä’/•’ÉüÊß“y+EÅÃØò3ˆÎQx§8ŽAˆÅ”RI"ðQUeëU™':–X‡dG\~þnfÆïWåMgé5gñqÞßià¯rUÚ/ð ãa!0
+ïPŠ¹ca‰’†`nPº e8ήРŒ µ’²Ù%o¶kÛ ­?ªíNª;ÌÂ퉂dªçãŒeÿ$,bYqö’rU«ú?ÒÈ„«âºîÿ)¾%ù&Ýu«d(™ßÄ /ßkµH ¢DdY”eJ1ƒ˜Y„õ2*<?&ký#:xUf>Pæg×Üv–_Ö‹/g{o¸|·õŠOoÚ/ð ãa"P ”˜f0©T˜ˆ‚ #~6Ê:á¶2å
+|ᬘpú?w .ê^h²“Zé—ÿ”³|²ƒF` „áÄJÅH ÉŸ5|îô[;÷Mâ \w—ý‚›¦±<KyÐHMþ<èæÌž#ææC‘ÔH
+k4–{Û©&Õ(Á<à‰S ß°õÖ&°:›fOÖÇ?«Oþ!¸îDCt´:mçÿ´ã£S†[ _LïkdøñÏ»ÿ£‘ééé–æ¥ÃBÓ¡˜¤½í>Çù¹û¾íöyà5TÍ¢‚gGÜÕo¦ÚýsûãúÎvÇ@Lß½!wgÒÓZK9ŽfÚ` ™U»IaK°ã° X•Á¨’ÁO€Šªª‚ÍA’½$;ÐÿýœIЈús+¡•ùpÊÙa:´šðD戊9Óé™Ð*§¥i/﮲±ßM¥ø²«â \L€áU;f øh•‰Ä𼳋œMǬ,2ß ð© À‹É£¬’uT*¾á ÞQ«ÆÕÒK9¾Kù¾I—ÿ¦§òJvEÚ/ð ãaá±%˜o°È•‰¥kØ&` –Dü¬·”ü¿8‚àÛª‰±ìrˆqÍ7¦—^S}Szp§XÀ  –@DÉìCBZHþ¬idÜ]Sqü?÷üëȶñFp¥ìðÀ¡øæw#=Ãm©ªàprËÑyÄè=º¶ëk6Uç}Ë¡¨
+4a ÷G¨Ÿ”åsí(îñXoœª1ÏךpòŸ6&Ón†§õ q
+o-ÐZ—.Ÿùjæ«»3wg ‘æÅK€"ÐtDBÓßvŸÇãÀü‚}+]%:¬%D²¢E¶ÇRíþÎÁ88${0˜ÉyÓp¯ ñš”%ñðÖÀ2sÇ`‡ÂaãyžN²–«%LE–W2,‡ˆ6ŽòBhŽ{«¡ï²ò
+ÂCù²c8Ü mDq(UŠD{ö]{#GÌÍïGûöƺ¥ I(ª
+ŽƒR!*ÔZ°@"‚R4¿ç}pÄ£çàú $‘îÇÓÈÁ˜uÎptРèõֈʼnÀ³~5§ê\­y<4N×¥M#£ÕÆéPôt0 4:Ó”ø]mÛÖ‚ú*$ÃÅT¹ÁrÏ)ˆçRÇ€¯ù·õÆhCâãPüxÙÚ?×µÿ‹ý2mò¼ãøÿûg-Gbû½ýÞïkûõ‘|qb;q Ü¥ 41‘ÆN|$¡•&šÈTvHrŽŠ6!¥k«µê:­ª6µG„³$ä‚•ueS!û™H]ÿ
+àÿlcE8èL* –¥æI¶6¬wü²?¶íÝØ/R(Û–îIA0ÚTíýGtES4þ@¡o6N•$NU)Š,ÊXš+‹,Œ\ Å•G‹£gK'K7¼n_“]KKPPÊDs”¿×±êÏűs¡ø`jÀUƒE)Ž:[‡ñŸ/IfžNb,¢[›@‰UK–ÏÜ…ç̽»Óß¡Èw4RRf´ôý•ñØ}¦ÊÐSf>ð"Æ« µ)Œ¤­f]Gr–ҒЙZ“ÀÌžÔ7†×«¨Î8ßžd:k¤–„¹r¥–að¹ÙóÌ‚††Ž:¡£†Þ”›ŽäzFàñ,5ÎÒ®¦ˆqï&ª%ªÛU­oOÏèÁ¼#íñÈmQ]ÍjŒ¢x”Dˆù ”aŠŒê~042!ú&'DÞ›¼U™£z^–Mjz.—M¡œÇ
+äFÿ¿„à7¼Âb\.˜ÐÚ¡jJ
+ ˜CsZç·Jñh»IÛGuž¡œüËf?¤É Á7Å{GgÆ'ø˜zp†(Fá€|–f1©PA£„€Qjõ‚y‚óè]C²RuŠq§°DvŒÈ…Ûi3I¨é–ý€A!”ƒ X!Ø‚ùÓF#“¼€ ÖL€$õ¥à¼‹)=›Lc1¼'ÞØoìÔ÷¥O=‘†ƒñNü`Ókºç4²ŠP²ijF'¯(y Ò<÷ºÀ¢Ÿ-Ý,%¼¦ë¥Wß®l|;²å`´ñÐÿJ#½‘¨&Ÿþè–îªÆ_½6ˆ(E@#ûÇBôSÅÑL¥íñòäÙ@
+-Ž„£‰ãþŠ#E‘¡âäÉp| ¸ú¯%ñÁE5¥ñ/B‘/Š£'–ÔœUFΕ&#§Ë’Ç
+*à3”gž˜9îPøÛ\yö™éû4rïÞÌ÷
+Vú+¾ U],ª>Š¾ôé’È¥’ÄYïÆʼnϖÄNÇ
+ºH|½ËžÊõ6”t½(¢4UTÔî=i}wÓãÒˆ{[’½ÛêÚ•vPÑÇË’Ý~Ÿ´½YÛ™öíluíY£ýnmáÖ„à·‡ˆ„¹^{GÊØ€IŒwZ½ÛR4õ“­‘;¬ü²'8ä)íÖŠ5GîsLƒQYµ\†ý„ßbøÔà]+~ÛŠi¡ëFpÄò„`;êŽ~ê.ò…á~áUØÏÂ]JA>rhbžÍ;1ËqIœ_Ì[Aè otÌ]zÝU#Ü´â7õØ]-4n/ô‡~¡ÚÄÁ„Œr\h–ÉϦø¹=Šo æ2JÀˆŠD·˜3 ÍòIĸ ò)ï¢íúüÁ©J¼)ˆŠˆ !²,;©ÓBÎe<=húãWݱaÀ€;2n–ŽØAh\v‡‡=‘ #ô…^~ÁÛjùË©XÈå¨NF¼¢(~A)šå<N¤œË -ºù7#<<gá¨7v^Ÿ7jD¨éXäRÀl! 3 ;>0ïQ§"Y’ ª2D rvH.ÐÎsÁ¤+tÁÂíÜ0+;ˆmbQ•a øÇSü*qýQÈÈ!S”¨&
+À›g™ÉBjÎhDÇrOêÍô'6>¬
+
+à3ƒ…–šiÐÇ7Ó_ý#Оššúæ~j¸Ò.òùº—?)ìYGï
+_ϼÝËÝï.Q¶UI¿Œz;jžÊ¯«™û³¥HU,1oþîŒúöc¢È® 8DÝ›¶w¦I±Û%)¢$Aø%akïuGÊûÖ+…?o¶bc ËPPõ<×ö$h¤h[ŽæÛ­¾íi׎G
+ᎲÐ+€ǨÉŸiä1kä`ËæÞÆ G2›÷­Þp8½ LÒ×ÜÖŸéì[»å×/¶¾¾tes|ékKWþæåŸv¥ÚöµtÌlêoÜx(ÕÙŸh‡Ž2›²×'ÚvÞÃôߌ0Ã’®Õë¢ð‚åh¹V|¼¶õRyËP<s>Ôtª¶ùŒ®ú¤¢é\Udòq¼Îœ¨‡âÛ|²:ñ,ÁÊÄ`Uê“êô`MúLMj :y2Þ y\Úù°>³Äš£P"È’HðôW÷¦ïMù­F¦¦ïÍhdjš‰Hå³û¦3ØF=1þ¶­c þÕ‘Û"BóžÊp:Ïùu'`†U‡}ÅÛÓÊÎG¨þŽþÛÖç·µ€F´•5"/(ª
+›Å)rsÜù‹*üuQÉkÁ´Q(ÊâÖnlðîn±öd
+w´»RÖÞÿ®l÷ïšFƬа'4©íŠ:…å„DâÍ£ð$º"bä!Ó)Z˜ò
+±÷És‡ôø]½úŠò…¯ÚáÏÕЄÖ£z鈫üªU÷ºYˆ §Rf9™ÏÉÕxį'ùãr%Ðås7#óËÆÍЈ=ÅÊóe,å ÒQÖD—í )êqWMºÃ#j¤ZfÅœù=Er~ã.JŠ½äÓ-¨û¹²„ˆ
+½{ÖÝ:»zžjìoêÙZ×ö¥\ÃælÝ`RèKò›âÂjîb}Sœä¥ò Aõõ%G&™•Hi2kj0k(W)Ï]þ÷µå—>¸qícŒÜ¸qmié:Ž–n@#M÷/cŽbâ®s¨pòÿ7…DBâÝ“ÉfX—z‘ `RªmyéÉÚ#»WJ#úÇãMß ŽŒm3ùD~.ƒò•W>ìáU_hšø\Kä0ós/ _Ù¿<„À$prG—¾»5r%.½™m‹:r÷ŸÔ-9»f²B7í°»)mw¾4!–+qªÏÊx¤N9Ã&Á7ŽËsqy>h_ðÚ“Îù õb®k.,ý6jOLU•!QµvÐtµ®æv›oDýWs=ÿòÛ/fÛfò-¼¦‹¹žª‘*[(Í¥r6FPÍ3¯Fé©.ä6\ »ûbC¤´• °YH¡hövHi‡­k„e”§L(•Ë©qS‚÷ W$*
+,Ëme5ÌÕBXU8Í- Bˆtoz´úYS÷Ǥg¶Ðq>ižóºJ± /¹ Vq´T<½;Ö¯Ý W¢§L–ù*â’JáàÊšgXr5;ˆû<µþÃïûŽŒm““¥P*U…ú®ÎÎæûé·FÌ!LCA.N‰o°ª‘›yHÇ>ª$Ãcdž÷™8»ýÀ™Ñ‰ÛUÁé'Ÿ'Ní?7’¢bjÇä“<Ú­sáÀ'–7å< Ã@”ŽQø-þzùÙögG&™³;ž¿œ?> ŠœÀIF'ª|aˆVX« V›í
+0Ûà–|oD;BÚ ?±ÓP±C®O´¶%b°;˜ÁÖ@íëW$Ž Ž¯´[0Œ,_¿±üÑòÿ^ÐÈuä¦FPBLqŽƒÒgäÝŒB®¢žpÐ/]NÍHE¸"èž÷d ¯ªñ¨GfÉÌ£¥ºúŽ_ܹR Œ_˽<cŸþF¦ršbÏu]P$UŸ‰t]›‘ú¤¸ûë ‡w‚ñK£9 äG;’#éI’ÃcŸ6ÌGÝç£ÎE¿ŠxϘI³Ë>£îÃ@Šv¬•Dî+Ü:&cÊÖj31FÈgËÔ:5¾]è}«¶kÞo¹ìµ\Êu\ZÞ÷»óÐ,3yËpãø\xâŠÌ©°u:ê|7hús±q!j¾@½ÛtàWÕôô sIÆ(Ýìó¿$å¿ùrÔø¾_þ¢çjbH4kã9Bdêú51ªf ³" Êk‰…›pŸ ¹H£½¬rœ‡Cw&×·´[ß ;®Dýð76\Ænt|F”% >E8¾OuD‰tª\E²Ô1FÜG-O¹ÏHó†éœ/öÍD³QÃ\Ôûeé‚
+åØ3×!·_÷ZÎGå_„mœhDIîí$Îß²—ƒÎKqÇtØ×K)jš+s%ªVTãü>hùk±ôº×Š{Kå‰TA†¨U¬¬F¦FöO=±÷̶ýøø©ñƒ‡ojÝ jSúªCÇ'5ŠçTlò²/<>ÀœüÖÞ³ãÏݶ„xm趯ûÚ𦢑3£“Èñ¡}'‡'°r0.QJq 3ÅRö¸2ÅÕ-#èÇÁÃO‹‘Š™›Q«AÊÅ2
+Ez£˜ÖÂõÙÊõÝòÒÝnª-Ì )A®//ýçÚ'Y^JßHYbÓ©m©Š=ÝYwWD–ŠºT';òº£Öí*ýŇKëé¶n¸'omô·vb€umî.:ëÑÊ‹/íZ)
+"SÜPêU#•<(CÀµËojdâÄè›$ØÎ<³}âøŽÉG6ôJËF†>WåüúG;Š-yáY–E<M,û ­='v<µm_Jˆ‘ âÜðäí^÷ìÈ$ÎP9®€8Á©ðìP4°5 ç&Ç$„ ǩŶcì–Ñ2Å-‚ì;¬ŽmHas¶¤`ÑFaJanc¶n0[?˜ÔmŠÓ<­LÓë†.¢©ÑP>ü裊@>~-ݸ¶´TÑž,%æ‘\S}ýO·ÜÉOm®ÿ/ûåÕuÇñÿúggj\öqï¹÷ÜsïÝsîcï. °o`Y@QSŸÁ8i&*,,, m’Ž ’0}Óp4m䡉IlûWÛ?ÓÌ'5¶šZAžB˜¶ñ/‹ö·ašÎtìL°Î3ì|çÌÝû8¿=çÜ=ßÏwxSîÀ†¼¡Mk7zOÔX§6'7d~CõæÖ¼ãÏ)2T À'«h}X4¢õ§¤îô©mÉ>Ò”Û»Oëi.îjT^Þ©ÖVi;ª”æ-úOê}Gšò{÷³¾¹/ âíou÷4êÝ™c³/õ@¥o·Ê§YôºœõF.ško³²i5þ¾oŠ,,Ûx§[@«TdsKÌ)2.K†"*°eKˆ±,¡xUÖó*½î)Ÿañ1­tš†>•ü ZUM˜6õØ ¦©¨qàÕ<ãFl^‰Œšá)Zú]û-ù ÊQËΈ5§ƒJö÷X|žÅnKþ š1kÂ*D<܇S@vÉÉc^Œ‰Êˆ[È8µgAÉ¿¥W ‘µzB´«*Übׂ¨¸4îÛ·”õ[¥@\`åM?•UO€è8—Ä­V^Ľ$å³’%zź–ý\-¹SÒõ+´üÎxâfì Á²þŪ|Š(º=cŽ.™§<ÎÉ’¿£Ø~M ®š¡¿©•;ôBÃÅKN—é”Q]%Ú±
+8¿¡§°SAdÓ9pœÉÙ;Xp^ LêþßÊÅ6ÉÉ8aD9¤"¶B#KªéP‘ 79]×q*q`¹T
+¬ñ ?½¢¯¡r†6fl¨–­ñšé½‘FŠzÚ|ÝÍZO°DïO±×²{R¹Ý)=bé”Ú—r÷·€´Þ(êK·f÷ï7»SÚëMFpH+.–[×›nàw”)˜0;dç9OÉ‚Z6mù6þU¥•Ì¹Ã7Õà -ž0üSft•ÿ—}æ‰\V s3Á;1ÜHQíŠ(Š:GVaQvðÑa؃ГЃ'~Ó]<é‹Î+PU€ø,Y†´)¨‚D8›Hù¥‚1cý§4Â\¤QC,‹Ãq–‰ ²‡”DÎ+2oVŒkÑìÐ Û@X?â„’ )œâ ¶ß¡ìQ£xÜ(™¦àÑ¡Y#x•EÚæâV‘(a¼B\NHMç5ÿ‚îŸfE7Ìâ9WGÄp Lˆ“LÌDäzÚMFµR˜„)%0k–€éO±Ðœ±4-á/1à $
+Àˆ‰¨h. Øæõ\7b ¬lŠÆ­à¬Rú”D#a@„`že—Œdûûrᤘü7Ì™¡Q½äû’! »Œeðh•—!¡
+|t¹ª¹ÌëƒÍJáEÅ)ü7,Þ]\’»‹áµ…ðçåE^ÍÖ¹í®è¾ÊÞä;±—Èqš@ä¢|«¯ÞL·<,¡é¤§7•ÛÛêýY"秉¢#ÍÖÁçéKµæ¾/´'Ho{FMn_¨ævUrÛcø™
+Z»ÎØZI«£rI¡̧Á‚åÖõ¤“4±)C#F’=ó>ÑâSFà‘oã_Qczà†žR$
+DAD¡Ð…_±*Ààžÿß
+\^öØ]©E«äܶ虊C™(
+ã¥ÅáÓmááýOK# HôÔ«kØ¥î©/´™Î˜†(SqºL}ÐJ;(S¢bDå2‰œ¥H2e¬g1EäÛnät¹¿ ™Œµ›B‚oÑâ)«&c•/û4¾Äš°+ÆìظÌÚ5ÓNõ¬š²*ÆÌõ£ÑTέBÊg±I·â 3v)fÇþ°.ÑoGÃAÊ\“t‘#s‰ƒ,øk79©•OFÒY/•1Ëî8éßÛU  æ*¦q1,Ó`àÕÎY5_5{¸b2áÄD0ˆè¬Ðkz8ëÖþ¦szÉ…sjíV®1Æ,™Ã±Uõ
+e9 ðVÄ —}à-±YÁ
+`ã©EU%$ašØ¡«ouÓVðð)b%²VÕ”V™Õãsvj’Wä«‘œ‘¸^”Žó üDd¸H=P£Na4Œ“­T¼e§²^Õkg¢°ôk%ã Ìøïš0«ü²Àžî€KЛË>ð–X›ˆÅ¥à Kìlב_´‚F.tôå«‚‘ŽÁz† ‚Kmýïv^OLœӠؾ¹ùÔ¾ƒïõ9ÙrðåôL‘²¸]üóÎ>8äãöA\Ì\êz2@ _kÏ°¢‘g®>ÝÔåŠX<ÉåÛ7ny° kdÞßààÁÇ»w4i ËYöØ]©E«ôüvhDELIÁ<€ä‡FÂÃûŸ–FÖžèq‡{ŠÞ9îmêÄüŒû¢ÉÔ—‰BÈÂ<N)Ej\Å´óè*2É·]w¸‹¾¾Û_óŠ„ÑÐVé¨S—s—_ªFìrjÜgŒÊ,«œ oüKɦ«zÙ_Ýš§þ®]Y! g¬ô”Qý¹‘þ)›Œ~{3’\"á
+ŒZŠøOöëí'ŽëŽøP)jÃîÜÎÜÎÜgvv—sÙ { 1`®vRù¡Jk0° ŠÔ¦±±C©•Õ¦æj'l/ ¾æRKUûÒªR«äÁQ­Fil`YÀ;nÚäÁÅÐß8Š"U~0*µjé§e´Ìœ9sæìù~ÎßÄè’š2"óR匕øT ϨÁkzd¨È"E$hÖájAÙ¥˜ CÉ籊 ámËrntC ç<‘œ^6§–ÍiÛŸ%Å̓F
+Íë‘Ûr
+–R¤5p)œÀHú­Xú# …‡'½aÄsJdÑŒÏZ±qñub½™Õ* ‘‹xÇÍ+,t…ái,32ËÇ †-f¸÷¥È”'² ÄA#Ð h
+4Â#ê àÕ¬kÞŠ)O|ʈN뱜ƒ¾-ªŽ÷6}â=dÁ†ÔÍ“‚ DÌÀÅN¦w™hyÝ*è8’¹ÐûêÛû¿Ý~d¬óG O‘Jq°~8ñ8AÀ¤€- L×z«ô/ô]L=·ï£ˆî Ý“ûnˆF^mÝûD#W]ÙÑ£Ò$¼/ØÞ>×ÔæhäÞê×¹·voeõîÚÚÊêj[S‹ÂÂæÁîcÓc÷I=°
+2þÓ­\Ô«PÎ å(ÞÙg§6J#x¬[=™’;}o¼(ÿ¼Ý3’´îE;Þm&½Ã½¾¡^Ï`RìQ†zä±|ꣽÆXŸ6’Ò‡“æh/”1²îþècIóX'hD„H»¿K ÓVõ‚Þôeü!kFMÌ™‰œ\r[)…Ô{“óÆIú˜”˜§IæGjôŽ]0C3Vp^ Þ–#Wq¤™gÝ®oÊXtq”ȲJ>x\ƉÓFå¢UzÍ._ÆU³j|ÙŠƒ²FÉi~›K¤’(7ð\A ·Â¸M‚C$ƒ$4âÏç>¶›rj1hdY
+ß4"KjpÑçÌš,énA¡yV`·² fÌä;QC3_idI Í+‰£Ø/~¥°’H¸eVxK.Y´‚Y½ì3©ôª\á3Ò„Ï휃HŒ8>_Úzù ¦çÕò#6-‡²¾Š«zÙå|ü'Oéz5’Õ·/)Ñ)¥ZUx@‘ˆ'ERÔI™ óXL2<GøŸRíGvø–Tùh‘Ì;¿"G#C¾ÌÛ—•ÂÖ»²÷WØþàýƒTxU]3˳F4«=6Z†ßT¾älH+=Å÷;ù~©>û׫‚óûû'RéŽC ‘w÷O K¾6ü.`-€ š‡€hš.óÆöý`29p©k
+@òqZkïÏ´J'_¹Ð~joEƒÌpSš%χw°£ûÏÛ;Þ¯íü°jóc÷I=°®Ôt n—¯Œ wïzneuíîê½µ»_kdeå¾PþµÒÜØ„H²
+¤"özmcÀ"R›öŸJU¥üÑ*mQJklãƒJ[©ŠBM¿e«ªˆ«VŒTìO«µµï›yßÎ|ßo‚¥"óVÀ3k„ïÐê×øB‹a¼ )Ê
+1èÑŠ<Ü/ä KJÙ´»aÄgíø,©‹°y_Ð\†jðœ$ñ4À) “/KH9Š\Xpq&çiq$éÈFõ=o›RJ&íêoð²íá)œaD%ÄŽÙeÚ¥,/j¡ lÒ¬:¯„
+â ^€®ØÒÜ
+
+ÉâãþƒOhdùþ?v=µž8r žk)|¨_&kƒ`V#M«¨z© ¤Q2¸µô|³õrÌxv}ù÷¶–ev›™ÍöhÔzs@(0ܯ¾LcAf§op»ÿÍVÿ@s` ©h¸¥è¶‚‘ÿCl¨m…Ïé$ï·¿nühßjiÄ{¦ÇwÊñŸt
+_|öW#0$9Á¸ÇQÈրЬV{Ý[RÃ3JdˆNX‘Iä–Y0ëv J™,(Ãh,QMàó–´Ê)£xÆŠÌXáY;´h”Ý¢ýbAöù–a‚yA¢D`~ª–Ü¥¥ÙÓ
+{yϨ² 
+f²Qèi]­©êm+J7eãü6ù™’<éKªÅ‰1*¿/>ÑJ­pä«öH›5Údd6ùF·”¤[׶” ´”¦[JÒÍ…CMþá­à–5çÁçé¯ø‡ž¢{6h<Aö>¿Ë:“\-è§ßÏÿø[ÁW’r¼xA•°”@7ƒ€‘— è
+6fþ`ÔLÛÑ5oïŸ1þ®Æ¦iùÏ *êU {òB*|5Ÿ€
+/
+¢âÉå]xöëÿ›Ù0e”Ïi‘;öÆi;1mÇnØ¡9-4ï­o•L,ª"‘l7VYI Vp-Ùõ·¬è¢·zʈÌá;ZdVõ‹A ÆX„/šC<¡àÌŸ¨¡¿ªñi36ã­œ3#7 b“jü­È¤<‹$®
+BZH[†Ãö¡P
+(”ŠsL.õ1(×øÎVeݣȈ<2ܲÀËò:…ÈØ}E /šUKztÁ|ì´ °œ5bófõmµbÊ Où¬^yZ @WÇ„Pw×µ wQ€F
+/+Qæ³òa;]ûÿYâºU9§U-Ù›®3å–å“9Êb^XÄ‚LþÉ~þFu]
+"‰À€Ç †ˆ´¡›˜¦M0¤ È4‘ Æ6ã/5JÔ~è‡VM©ª*K Œ±ÇöxÒ*­*ÀÐ3¸ªZ…u…b*1:=æž{ï™7ïü.'òƒþ‹)F°¶0ˆf¾%ÙÝ,ÕIë_âÐœË#á ¦È„#rM &¡ßAדKàórà/zùJ€ðvè´v†lDzb†%ºLý7§õØ´¸n„§ÔÈ(N‘@JõMR‹­‘dÇЂ
+Þ•W#ùgjdý‚F :{7>ª®j$6
+GÓz`Fñ'=µΑ9¨A<¿ÇæwðÊŒ^5¦–Lk!>¡&Iè–Z:c˜SzèªÜÆ ¢åYˆÉVè,Ùž9ÎËÛ~ë6ÇUsB©ºEª€"`ŒOÕÈÉ[`³8²³1k%ÌS«Í…¨„œÍ0¯jÑ\~K‰ÁzþŠK¡¹' ˆX7Î_mCŽåË{¦»3ÃPœLó²)ò(¸M ¿QÌ9½:M·”+²™À²Í†ée*“ŠXÁÐï*…_¸K“zeʈ¦ˆ–ø¾ ¾¤€ŸÎÖlËu{VnV¶ÃþÔVžN)‘ #t“njÑ b«®RI $„´¶ ‡íC P( §^Õ3u `ƒp›Ûjyf³´]AÖ‡†ðh`¤¶÷)…_ºÂ3¤$­–,ù÷µŽê¡IbÎJÁkºŽÀï[Ç4/lcŽnk9¿»-#„xÛ¢)ÒØÞ½ãe`
+£¯íRßÙ»Øy'›Ä®¸¾u5t1‰Ç
+N)ùêÿFæœÑ1ìOj%)Ã×#—è•Õ¿$£rÞ‹¢s“¤ä²4Oga”ã¥,=+ˇ‰ë}ÕŸT#sFlJ7S$8¥„RZζº {‡@…þ’6ÂÓ¤ôº«$-ç9cßÜàBKð†þ"0œ Ú‡¤’Zù¸VœÒƒÓjtL/†¶;'E¡­9Ì?a³ßSØmø.’’‹FñE¡¨˜=¤pÚ~…“t1¦`ŠYÃüzW)º(ø´âcÅ{b G Œ‘ð¬¼î0Ór†:ã"äLëá ¨)tD>Ö£?s›gD_;ïy†“Ö R=6:Åü÷qø½òº˜5*ÆÔèï ?¬íoRdÐáÿPŠü‡G´Â ¤è¼ì–}£bFSSZé¨bŽkY¥*óg}ÕâÆECºoX žvù~¥ÏQ¨ |Øö¹33ÂpH© !¤…ä°eØ8lŠ
+ðÆY±øÖ¶H”. ®{;À`•)^e­ ”mŽsÞæ*oó[îJÕs,ÔÍÊ{Dù"W>'Ûøây¦(-¾bŽ«@ÖXÇ*m[cìK”eFcº+•Îi³:ë}É4Í–Fe.KE©5‚ê²Ó§^#ÞâëWpÛ‚l»ÃZÖÈÒ©xIp¬h³’kF(ý·}¥+…~Ó­÷HÛœX;Õ=ä
+ÐÆ[Ré<m¹-šnÊ÷YÓ"c#=Ô–ƒ‹fyã#ÂySt}C–,1¸v…kÍ‹²åºh¾K[Whçm¾|™-{ÄØo±ÆyÁ2Ë™¡ ÎŠå «% k¾Ogôeóté‚Öñˆ¯˜çÌ_“¦yÁ~G¨\bÓBñoý [s›sþUcY`Œ°«”sA*Ÿc,€´;¬sq­hÁ
+m¾ÏV\“Ëàî,±¶e¸×\eƼï 3]dhû´X ›¦«êÒ
+8V`œ‹t_ 
+t§Öéç™Ü{r!ôÁ˜ÿ½›«è,%4»™D)'U(¢‚‡%I2¥¾9áÞý$‰¶CpÈ P÷G]ð§eª9×vW 8°òZòrcäzeðº3ðûúÐ+üÏ&VÜªÖ   F~tì8¨ãoßÙã{¯§ŸL$ST7œ„wq—™5
+©šÂ„O7êÉ{øT“.±uÃ
+ëX}^Ò«Oøäq_ÁxKQÔW4â)mßÐe‰HŽ³Ê´OBµ?Ð*b®F¥!ä"¢‚ÀµËQÕÑ…‰ÆDËzû54É©fzr«8Þœ7Úlˆ·äÅÜc|")åÖ§<ú”W—ðIÉ!ÖR2êÓEÝÂ…Vv ÝŒ¸óÍLÒ—)ˆ ·>Z—7èEUÙÂÁiDtšÈuWÿÿz(´kúCøP˜ë ™{öt© ~x?ÕÎ?ÝYÐÛIõ…¡.¡7¢
+WÌ}]ò©°n°C:á7½—¯·_éôâ\ÿî[L¶vN†íÑ
+k¤-ãÛûË®&‚ãSú­
+ÅsUF ÊÍ8¢U ›zIižµÏ öyܼ*W¤Ks¦'ò*É<_¶¤µÝí328вÂ9ïÒu–­€mS9h}´½{ªíÈù¶4 ƃ‡×«‚‘Ž÷'ýG⻞
+àsPV†³PU-î¨lØår×,` N’@‰F;?¸´§;ÝcDZѶwA2{­·ßdàô D÷wƒI`$ŒÓ*®®bÄ?n ]®ܨnÿ²*x¹.œñ²û’ ûSmøjMè«ŠÀ5§ÿ‹ÿÕúЪÀ'ÞÀµ²·?õ†>sížÙ¹éðÿ¦)tÍ•1­ýÂù:¯@pµAT'{zž}ûøɳï@òÏyú|òë_}L¢8X
+Œ*ÑS}¦þt˜=ä‡ö±ƒ¾¿ŒAŸlcÎìCÎD¸sï;¹¶)n·÷ÿTú9ˆ%Âwq}a¦7Èô§õ¢?‘zÃÜ`ä4BŸíâ>ôsG*†5Ôª&óÛûKÎS6ËÚ–´¥‹ë´\vSïXbJçÐ’EÒòˆ¯¸%UdóXÏ"à ŠfSj×ðŠÍ ö%]ù´hýšrLs¶yÚ’ñ‰¼JFY¦ì³‚mV¶¬2%‹¬ãŠ¦
+G7«0T£Bëõ%±öt5aăG
+1Ÿ¬6èǼ OZ#±Ö ,¬y±t Ö‹±!ÙĤ©T¿Û„à*B‰².!/ê“SÍù#­ìþ¤@ƒ!j¥I”ãÀ*eTþ9p©~ÝÕ<ÞjŠ·”$›¹±j>Õ Å ­üÄ6}Â'ÅZÄø6yòMqj›låqÒÃM5‹Ñ&@‹™pëâncb«͘F¤˜GŽ×Î6«i%…И
+Õ8y°k£4òœRDìk`PƒiŸNK:Þl& „£¹‚ªº8ß|M÷ÑÞ¼áýê€ö—`‰n¨“é HûþÎ~½5uçq
+
+! Id±;ã¡jít;­•R,¶
+! (VÛî>ìÓngZu½_êª#­7Ô.ÓÝÎìv­º?–Ngg_bò 3ß9“ûÿÿ?ùÏÿ÷ù%?t—f¤WØ’Õj%p’Ê#±;ª•)?Þ—8³lþŒ®øžÚöXyE.;)¯º«sý]]7+WßÔ׌sÖB‡~!Y6¶
+æÄÒ[BáMêò") hdV±->TåßU¯ü”)"± ‚¡9_WR$
+ŠrßË›¦6nûxãŽO:úá·{‹"Ãc!bôžÖîc¾Ã]ýãþíñà
+ÿyWètMàËÚðµšÈÙò®TÍssv Eúù‰ ‘ï=ú©Fž<y×þõèÔ‰“$Šó4 ‘[,ƘWŽW›'Œã^Ѐa5"ÇÌñFËD½5êÉI¬6ÅêŒã®œX½¶¿R` y*Ú5Ù£kø>’‡ˆY¿ )D¯$aç+•JJG«7æ[¦Ö‰ªdÇÍ·J‡\ºI·1Ve9
+‹òäF[óÞjÐõ•óksÉ25ϱE*Ö­3ô––´™Æš q¯>î1D]¦D½~rµ!Þ`‰6§J#Ù“^Ó”'g¬‰41,Áá(†s„áíšȼF†#` é€a_H
+¨F#æ½!nm%NS°+4Çá ZØ*K³u¶¼·Ã@áý°v$¢Ù‘Fæ%“ì¸Ú÷º C½ìh¯&×
+§@$‚°$ù‡Ï>[ÐÈãÿ§‘×®C‰ñBÝ=Fs´YŽ×fGW›¢M 1M4.VU5NµêÇêMcn ˆw«£µ†q€A“f°*‹ÊR“¦ã– ,ŠqË z%Î)32²Q¦£ hu¬5;±Ær0ù¡GÝúÃ^ÝñáP…9êÉm2õ­RÚ)Œš_¸cu
+V«dÇé,Deˆ”š´¦Û̇ÜÖjõñ:1æ±ÄMeÉnš¯1jdljŽb ŠbÊòfx±4¢ñC¸ê‡æŸêöõ\i2t:°1À!ó R„‚¥\)­ušööHû‚Úá°ðnP핇z’W74½³‰:Ð+:  ß¡^‰g~*¥üx_âüE,½¡/œò¾‘*­@¢(«äh¥D¡Ydz‹È(MaDAPe*(lF¬zÈ=P<4–]–ŠþƯ¼ûüÝ·ù1³¢ý¶h»+Í Å3RYÒÐ$†Q$°aÏÚ@¼»Jù$8$˜4 €™éÐÎxÇ–÷Û7g3ê4ðBăã‘ÁÉÀÀ´oGlÖ±ðÀ/íN 6ªµRê‘ ¯ïÙŠˆú':·NunOt÷?›F&
+%Îcòj«uØkŠ¶è7k'ë³'êrÆë´n~*iè&œ†„ǘh±hãÚVp"I§# Å'9Lɦ+èe—NR™$†ÀL²–¥aV
+”¬8øoöëô7ŠóŽø©`ï1ÏÌ3×ÎìÌììéÛx½>°×°‹í=lcãB+NÛ{ú"%`cRªªä
+Ó½Wµ}®.¹èƒFÎ{cYçÁs®Ï¼Q˜ûß°«¹”âWÚš„G#St.ƒ>ô÷ªÚT¤A-¢åÞ® Ù»?^ƒ ad¤Ž@3òxœÑÈÃGß¡.
+y4f‰™‡cê‘(„»2–Éw8.Y#£ÝôHH=Þ/‰”Š‰®ƒ¹b«x$l=Ö'Kãqþd/ ™¼.ÈG
+'‚&ñÄSKÕâD[^"è8Qo>ÝhK¶K;Êt¹?6é¤[%ÔY,ûëíévËl‹š¨Ï;ÝP–j²$êt“2ÑXð;N5À`”¤¿xjãRûu$Õé€ód;ìD)±F]¹àLl±O4:R5ãœf¸²åTcyrcÑx£iµ £)ZƒØRœ÷î&øÊœl eK#pIŸšðIo)RC¬‘àغÈqe$’L-æ;Äú24œ0ŒÇ„‘œ;^ — »V
+Ží2 ‡å£ÝêÛ} “¼cýr±]FØD`-GI‡:ÍcÝ–_÷+€+ËЈ2Çbêhœ­,âIZ 0ü?3ð¸+–Þ5–Ý–\W•j0ɼø?«‘ÏÕÒëjf{;/U%Ö|itß0­»jYSõAÝ=×…ÚÛÆ5óÊšy©ò+¡,ë~Qß«ºfrþCp!W~©”Ü–œ8‡ GC’ÞødúL…—§ A:º¤k÷:Kq'©/j)!‡
+„åL‚¹ 7à‡@2@ÃüÚ^…#Sˆ
+<FTn’•$0Ìœf,)¦f©þ’¶ä}¯òRåodå×T#¸4’ÑÖ¸Q%h 4¢8õ“!Ð4;h$¹¥0I®*
+ešb|‚I"ðS HA1…B¡”¢SÁw#ƒc¹Þ&|Å7ñX#ýãSûƒ„Çd¨BwѦ›ÖÀÍfÿï[} (>ØÍ;^pn4ö|a .UwnÃ$
+)z±¼ë‹¶Cn ‡¢¨‹6Þ¨éý´5 A1c[þçÖÐ ÐðžP@’}(ÍþßYû³^EqЈÓîxøàჭíÿLòø•GÞ}O)W@à¯àNìÖGÛtq÷ãn.á4Ä=ϪU;Œ+Y
+WÀ2³ËÛ Þ¸ÿþ·üø/Eîomptê£SYÄâÀ9¸IßÍ‹¨`èAFl×GœÏªU©5°“»ØÆÄ톈Ýt¶mgÄmHzt—iŽ3â5Š{¨µn"ñ†°¼¯b~oÉH³~Ñn¸æÑ%ÛJ#öR±“»ÙXûSh¤Dô‚FP¬ X©"‹$†3^nµ.ÅÇ즄·$Ö n6áaW½ÆÑ6ŽiŠ”˜ì5ÓñV!ÞiŒ9MÑ–|i(áâm%Ñn……ÒHq­Çq„>Ù Š
+‹´¸þÐÞò…Cä´Ÿšòe/~>ËnôM¹»V3¶_fKaîô ?åÏYAsAÝ\H}6¤¼ßÿºÇ0L…`»PÉW|[š¨Yg*¡¦S\Ã7JsŠ±äýkÿ9å.QG[»âÒVg´–ÛË]uÕ]eå&U³AXÒdU†¶@`¢Íî•F^剤µæÝQWlÒU¿¢« ¡dê…¤…1Ň&“}£‰qèt°$6˜³FÎF#á#±`ÑÞ‘ÕÁ# Â5ßÈ'½ã—z_êêÀ ‘QqøX40 Nœ Œ~ÜsðxW_2<ïy/g^íO€yÇ/'}»œ¨)¨*ý¨ÌþH#ŸÙ|ÜÒÈõ|ÛàÅç/¶ào›zÿÐä·R¼´h; v¾\/W“
+¢•6þÌT£@Š<»¨üeKÿçÖç®`!Päfs–%7¶4òYËÀjÍpLròÄ÷þõï‡On÷îÝûßøŸÿÇÒ¢¤¨ž²ÁÙPµ.nNì`vÃÊ3Ó±ê2E<ú•Nfm¯AÜo8ßÅžë çÛ©ÉVâ`±¿Œ´q¤Y«Ô¡˜ºŶÿ@(”ö¸x;›°•DÆXs2ÑÖ\ïkŠ:¸D;i%‘B©R…o/¦‚ÕtÒ¡O¸Øe›>æ*= > ñòI¯ºI‹ã¨QFbÅß7-yÑ-¬8ˆ5[¾4Ró¢H¦»)O,”˜B…¢šwº¡ÇéÅ0q&,0³Í\ÎèçÂÔ\€9;Ì- óar¸SRJËúÚ 3!z>È·Jѧ|ÄŒvéó…÷{™¥!ØUÍ èÏf¹Rv:”ëM6pej1LÏ„©à¶,|Ô2œE
+~£kÎP ime†4opuw”Uéï®FÒtÃ-ªþ+ª²Nצ˜º U—!k3LSŠªß ëaœe‰ºjC›åYÞ'ü*/UÒ¤å]›ÖTܦ,P)†)Q †÷Õ¶‹Á# ‘¸<韸òH#ÁÜH
+cíÔmÁy¸J¶“Ú´NÚ&m¬…®”´!еÏý3i›V’ìkЦ©x"¤Û8}uºóÙ¿ï÷îwò÷ó;!ír
+NÐ ‡˜¡€:ìoG¶'L2PK¦¡.v8È=L'†ƒDDÊ w%›×0¢‡P¿*Ðt¢Î ´
+Á)tÃA6‘©¸¯E_Ⴖø¶¦(Á’µþÛ_¥¸Ì\á ¡/r%w´å÷è²ûšÒ{ê8½Î]Ó]ÕÿQW|…/¾$_KÖ¼àgñ…
+ ÈM¶ô[
+U£˜†Aä¯=ï{{
+Þz©÷Ä‹»~æß 9Üúíן—z_ÜÓÜö½ÖÎuìíê›Ù¾ÿ¨ÔïØ Y¢¾ÈE@jPÊÌC™Ài²y:P3üÆ„ã
+ÆM
+‚ňßTv\°A#ïÖv
+-B“J%Ë2S[0ã)KèŒ#t©Ú»Úõ€CÎUûf«½ ‘¹*ï'Öài›·?Ó
+Fâ
+424 Þxðà¸ciié@$”²¼òî©Ó X–ª¿-Go&£n~ÊÉO:LãŽ'ÕU…ÞÚõÚu$¾&IM
+¦ÃX¡X9É( F*qŠ
+‡¨¡DåÄHÀ0Ô­þfÁ!p,­‡2ÜT—ýYS|•Í¿JÜÑ–.ÒEkþ·¿Jq‹Ë»Åš!3ô”ë\abÉŸ·À™!µù°ì½Ë”ÜÕ”ÞQ?Óȳø—¸¦+»Í–-ð€v‘zŽU0i¨Z”+ÆB{¢í»À Q)¡‘Ÿ´õ@gÿ4¨8Ö½ÿP°gsaMÅÒ«]h|
+Z¡wÚ6wì÷õý4°oºm7à'.%,3W@)3Á½±@rI!¢Á=':öÀÈ ¸3Æb´‚'•g­ÙBUúNÛÚç«| ؤ5çÁSŽkÕßZ[.Úç+½sΗÃÅÍv:“ÆÔjDª9º7×:WÙùN]÷…šÀ|EËj×3o•`"@#³µhäcKàmkû~±Ö˜@
+“F£rÀ›J†~ %Siœ@ ™‹¤Rµ¤v«O”Æ)9žÊ—ð™Ã[LÓM|´VŒ9Ìãn0‰6žt=tÔšoàã.nÀ“¦ÇŒëä tôR&gâý‘Mb¬9+Öœ{Ä;æácMÄ´Û<±‘þ~ñ—^›"u™S›¸¸“‹6¤ÇÝk¥‘ô ˜‘Ó¤M?n×E6ÊëYL©‘SŒIÐtóC!õÁÐ/ otôÉ‘ ¡‘HH–@Æá.m$ðPÔ~ý@gÆ«~c»Gë(W¶:„½­¦‘D$¨ 4MU ð=Û´á øÆ˲AéN6¯éðè†CYý!|Лõ?¡ŽhP‰Bî³–E¦à†±ì6UxY[¼Èý×h䶮ð–&ïŽP~M¬¸Æ–ýISq_]¾ +[d‹“Š»ÊÜ«ºü›@Ú|E›O
+s<ós=óœóÌÈÜ\ƒ0Ì Èåma.Ô=uÕ¦]Û¸«(ˆl+ 3ȺmúsûC›lbí"ñZª.÷¥ÝZ“¦QpÛÏÀÖ6&Mv îl¼óäyH†çóåyxÞ¯'á†ë¢ÿ匒¨QB) –Ôž€FÈÞ(ì@­3ç"ÂùötUÀôDùî(0†™t·˜®6mw”~{‡”׈eˆŒ%E8*ÕÑt[½±çMæƒV‚"1  ”îè>íÙvîâa݇i+hítã™6å…ˆötP®£•(ª”ÊY2ª‚Ǭ}–µÍ’ö9Ê9“égþ·Ï cž& f)Û$i™VÛ4Î €Ê
+KH+³\ÑŸ…ÂiƹH9¦ûCÚþXãøªl‰0Oh,¬æ\ ¬Œe‚³ÎR–{‚m‰´Ïh Çu… tá#Uá¼Ò¶È83>ðk–EÒ2E™ç¨¢TFµ8[ÿ@ÖZì} Àörøø 8h~oW±gíõIXJ$‚#©R©”KQZ&/áM§÷D¡cW릑+Áã0 €d¸õèO·=2™ z­™Ë½^‡|æ ß, €FþT‘y|÷ùܸã ÜÝrø³ÚC“ŽÖ«åÍ7ÊB÷]¿ßrà®;:V¾Q¾e?p»°õª'8Zxå#¹‚7+"ce­7KZ®y‚×Kƒ£e‘í” n¸jrœx¸ô
+s´uŽ4ÏSÖyÞ1ÉZ§ë<eO+ ¼ëK¦Zf‰Œ94ö¿ª *kÆøâzé9æXç¢èh½ÇšïrXï"U8O¦~ò@QðHa›¦÷(ÇcÒ•ñ_³ÌÓæ%¸=è²2œfãr…‘ý¬©õ%4í?9±’ç9»ÿ0)ÁÄ8
+/­´”Ð"ŠR}¾[_@c
+4K,Ax1ý½%¾xÛû#ÁuÓÈ'¡ãÃáãC-G@#‡ÊàL„É‚8a*MõlqàZeJ#wJCŸ»Ã×3mƒ h¤pß„çð/¬óÍŠÐ]û¾p…ÿà~`ò½•Sz&¯ú·îà-ÿaÐÈ´3p£ì•kd´¸e¬,xÝÕr۸挹B£îhÁ¦+•a2ôŸ_Ãg«ùoŠ¬¬¬¬¤â­ô(å
+ªÌÖdåömç¼Â`h
+°k C„ÈV!
+Cô;+¡Ó5Ým\W;ßmªÜÓIw;|v¸ÞƒdWê—O‡³÷oAQT-F(—‰ÃUl]Îh
+™âÓ¼sFmû‹Òñ7¹ã2ç‚Ê>ÎYÇëg}¤,x¨²Üã­ø5Ë™»D]V™å˜%P)†–çÙ> ¤ßþ«üx¾¿v89Ñ\äSÊpDAYHWËÛƒ£C-G~>ù›‘_¯±MgÀ¿Oš&
+ñåÜÏñ¹ÛÇwçBÇ$΅ĉıCÖ¤Û
+ÈÅNìÀ ëÆ€„Ž¶Ž‘+E@.ŽËغI“Ф©ZË´µ@hÊ*¦MJR¬íÃÆJ’=NV¦!MªQÀZwôÈzϱ”çy“èýÿN×w{;l¼^GÒ 8zwì…§Ú÷¿V¸;Ör(2ÑÙS"Úà£H§Q¶óƒUÌz³eíïo ÍTþ?jäRU[·ÃÄi¢Ž5üÚÝüZn os‚ˆ¡¸†ÄIÝeÌ}³6|ÙþmUëÓžg¶<™ñ´F®xZg*Âoù: Jã¤ÈòžòŠ•O²´Zk×ò§,I.`¹¼ràëû9Ëá"Aª¬¯úìñ ~¢RØó[âþuKÕ±Zs,(L5ˆS5RÂc˜(Ñ2#®ãQ
+TàxÙcjpŶçö5à…£4z­¡69z¼¦±€«Ô9F@Õ©ö…d%¶é'j•˜/ë¬wËÄ X‹3¨'1š£xQEI*£Tê2aóɯZÆü¦ñ:)V£ŒÕcÌ#>G"`Š×¦K#`¡¤©Æƒ–DÐ êzPZ‡Ð4IÊò×4néKƺ8²Fô§£ro~Ž2åûB¦¡.{oDòq8%1œŸ ÒÈ>Ù¥ÿŠ—É@Šcvx©¾|tµQã`$zK–éTD:•rߤzÃƈ4Ø ˜Ü:Ø¥uYXJ§C1„Uÿ”wÍ)%·¤‚91ïH#‹¼û–X4/;çŒÎE9ÿ_¸ ”ýÊR</•¦T³E·%÷ï­Åפ¼åRˆøEÙ=/»Ò¾ÁÇKtÞ–] ²s("Ýa·Ü—¶.Ê%‹‚'®äöè¯ –Kbîm}ñ}¥èBVúþ|Õ‚œ» 凜B0"yuU7M·>‰
+Öòˆ"kr×ÂKŠã¥–œÑÖƒ? ½°çÐmÝoìJ~ºm9ŒÞdá­N{¦ùÀxhÝ4r!Üùtöœ ´áAQ0D>)¿ëi‡à{ok{R#åÉ„(œI· ž}]©ê¨Ög"Á©°©²í7*"6½LQƒ¢ç)JO’`¸7+ZÊ¢Ïâ÷SÚ&¼ZÙv½¤LrÍÓqÑÓÂ
+:B-(„à“&“±ûã5땪Ö_öù@æùzûpµ}Ä“3Ýs̯¦3(†Ña¤ÏÀm¨P l`3Ô”–¥ r“Fíb­¯o3ŒTÆD]æ°P‘rë„ß ˜GëŒ1îhиϹѨ¦4¨%y§Q”Ôj9$Žh9ÔXœµ9¾Í6†½`û¸$3N¦‡"IĪ…öX½i"˜ôá™/ã2Âh)Ç9«i.õ…A#rÈ$UðC©ö¤Fú“*
+n§µÁЊšìÊ©z¿"üNeû•ªðµòöG‡içÁ3®ËžÎÚ  :}>RИ) „†üzÉZxÕÙÁYsì ̦ßø"³eïVv>õ‘JÛ.—·]󆮻w_¯
+ƒFÎå…T§qò{ÇO
+o},”-«-+ÊŠe±,)]áŠ`Ç&(ZF‘CÃ)voA†˜œ 1#_2[†¡£Y¿e·%Õ%U?ËÓÍ›TÃŸÊ +|Ù*_„ûRUöŒKûþtyÂ[àºÄ—/
+©@c½ó…y$­²/p¶Ç\ù’¶ø±ªè&cc(<‡•³H6Fçf‘ÊP<š{Z2ýMQ’¶ª‚9ùòe˜Z4'E ØfUX g†¬ðf?Éÿ;/{œW-‹|áºÒ§tÑ3m‘3Mz+JÖU¡¢‡Ð*i6£( kwÔ\ë<>ØÙ“iõHÄ}ÑPÊ$ŸzN&w<ìÜC­½pƒŸ¼MWplסáî“BýùŽ³’‚f Ü3êw¼ÐývÜ× CXà
+ÿÄw<è½è¿|ðx¦ë¡b¡·®¶÷¹‹l<Jc"Wâ‰ím›UýÇ«½_T'íÞqgð~…÷Qe ¥ ú®;Ždª:tÛá‡Üqfªƒsµ¡‰Zÿ}›oÖÙ=]å›´ûæªC‹fÿ¼Õ¯*ŧ‰*Ïlm*88
+X5]“±¦&ì¡6]1á,JA)À†ÉF8„ü¬êàƒÊp-äd «¯ Þî×½ôçsϘ® N::gÞ»u¡{#Û$ŽQÉ2ŠO¯]ÿæïßdÝëž@'Pz/'1Ø00„Â)ÉfÒÅÚÔq‡.…¸Õ4´aUUŠ:ó‡\…±VÝØNM¼Å8ØTuK1—4úcÕ;õìþ"ƦfÌ<åÒ(ºK ‘&a´–O4Aý­Ò¨KwâMº¡ŒU n6Æ\š‹Î‚¡íqÂ䂤qhWÑp³)VŸ?Ò¤;תôXgrr¨jUþ'»U£yQ§1êÒŽºõ±–¼ø†©ìЈ>êLý;b-RÌmˆº¹öBr2(Ú$½¿FŠt)"¸*Ïtg¥ölxû¸m‚ ‘QD+³Rßy5ÆÐYrDÃéßÜg:÷s:ÌèÖž @„ê3Aˆx&,Ét^Î]™Ú|ä‹äž¦ KBå’Ƽ¬ü¿×a2ŒÂH0?‹SÉ$ŽÐpxq8ePZA
+
+ùŒ-¡ž*KÕ¶ôU¸$É—>Ïs–@ÖÚÞW¾²VÄ¿½çíôª™—Š¿b­K\%ˆeI]˜ä‹ÿ ´¢²­¤ e1\Di£Z)ý(«W¥YÒª>-€¹²Å¡"5õZRX[\¿C5Ϋ–$W´ 3?aËk*òæ¯TÀ­¾ìÏ›;S"$&‡“öÎþö«Çâ¡·3­þ:{bÞž‘®·†;Ž%:N\ôîwå•ÃÏœÃ(Q‰ÍMµ TR¹`P¡$ŽãEJ†¾×:®üìñŸ½8 ƒŒyz=©v oÈs<áí½8•éz
+€FnÕîTvÎÙý7ž)»wÖ曩òÏV!wèo€CÀÓ6ÏtMpÚîŸsþË÷yïó–î?ÖzçRDñNUûa4ÐÈŒÃ?•ùzæw„mtjQêhŽšPð”‚FÑ}RѸ­ã PÀS4M1UJÃŒ=|Ïê¹Ußþ²ŸÏ¬Ý¦š¬ñ€Ffj³¶@»¾4´ qâæ?ý#%Žoþ $Ï5òŸ€ø¦)p4Bç±yÑ݈J¿~d§a¨i£ªª&ñš.êÖº€%Âpƒ)æÖÅZ˜h£k4%Ú ßt®9?êÖ ;…¸“¿àÔ Ù Çv®õ¸Ä‘:.Zï”â;3W™hÞ®5w 'ì'g(šT‰†Ø.͈“IØU‰jýØn¾ËFRrx23Rr~”hècMê‘í`£a¤q³4bŒ¶ÁJÄÑxzùCÍR¬Y{²ŽÆq(pð’ïÈ—>
+s‘  iR× Óˆæ]¿¢ª˜øa6RÙ ‘Í’p82l%xšeDˆA4=Àýæñkº¿òkN4†!úHXû~P*?ÎX#ªÞ½B.™ËP”<»†Âñ;´Ö§\Ù¦—ƒï™ta(^Š“2ŒF% Xã8”<MH
+gœãüšÊ¼ V>Q˜—…Ê´óŠ–Uµ5)Z òBZ”, jó_¸’Œ«_ã<­«œ’jÀ°é«öCMÉSÖ²ÌÛ¾ä, B!Üò ±2/'‹“ÑɨVŽP ªÒü´P0¯I«©gœFX‡DŠjKR²®+býáúëÙ¨q^µ¬²æ¤X±*X@tóàLÉú@]¶¢*¿®5ëd[9˜R®Ð¨¥‘𱋇ތuõgZý/zzGý}ñîþ¨/Å ht¾aãŒJ<…8‘4 !0\‘*‚ÖgÁ˜„S!ò•%¡šÖ3{à ™sÁž±®þ«¡·/yû.øú
+÷ðGbÜÛó‰]ºc­\gŒîŠÁHæ5IT©ëHy4LrŒJ[‚)—¨.†²~Gxöåà¿ŒÅ4B"rÇ\)(h•’–“8£$Ò¨4:[0d¤ °ð3ÊÒO«-×Åâ;L>TØÅú½ño°}ÃY$·ÄˆñÔ«íùÚ¡­RpO..߈w™b`
+S „Ê“S°WÛ¼c°i7$l0˜ï1ž÷‚{“ÛvÜVÈ
+¶ «ç3/:cÏJ#Pack%?@¤qÖ¶õjiä¢3:w1xAªÂsk#Î& ËgžÈ%GøŠ«eÔÝjEŠk"š?tl¹T8õF!ç¥ÒÈ$Õñ\µoû¼"4Rºí£â—»,¾¤¥ú}à ½ãÞ¨g”q̱5ó„Æì/ÿYÌ­CèóòæBŒ¡0 E
+Ã^~Ð+V‹ñ*cO1Ù éõúë2OU›Þð_-Ó¼beªÍL®ƒL® ä™{õ=å¦~Ÿº/å~ùÝ•†„O7X—Ù»±â¸|¥>MIå’¦×ÖŸX_йNlÌeW®de¸ŒãÅЪÕݵbŸGNÖÀ ³ûk€IÏJ#`B ;\iˆû­'ýì Ç”Ü€çÑ 
+#à
+Ç…G ç!³€Þ'4@ivß Ü¢óZª<Ï[Üaì7™â›bñ¸:ÿ&›ƒŸä$Zp‚”ËT¿’xcC´;ôê™h{xoªÕ?’ 
+î}7¸oxKÛo"†"íMmƒ‘ö¾–ý¿Þ²}ëZÿcŽâàT¢À0B¥”ÙÇh¥
+GÐ ™Ô
+Aüž.ÿÅj÷˜-ø¬42âŽ:%]ŒÚÀ ¥Á1[àO®æ«…‹ö pâ3Oè¬7<R[º¼&8êüÁ›¯8¢gíA^I¢r9Ž£; ŸV„¿, Ã#Ð8_¸¼&|¡ü{(kÛ…µÁse¡Kî–+e±‹öШ;ö‰ã•Ë…kŽæ3¶¿/z麳õ’+vÞœ»ùé®ÏŲÈöÀ]’Ê
+¨P£¢­yù³ ‘™ÙÇ™™™y¬‘©éihLÏ}KŸ™ÙŸïúh6IØ3³ö¹õ>}¢ÆÜS­…ÆUUÝ€[HVè†jô'ªòNmÈéjÔì/ãZ­ì¦Ò¥&Ì©\Æ¡+Õ˜‚Ê@)œ’+5Æ ¢Ú°jU¼1{¨:«Ç›¯Iµß¬„oU·WÓïU\o•y»-]HãÓ
+¿Ê&YûU¢ä#²àcÊú…
+6à`mÓÉ/-$¤B“I§¸­:fœî<£yµ^½ûî#y¿ŸÝóoKv=§ࢭ¶ÜB˜ŠMI;Yõãk•‹C(Èý«ÎPB•>Ü®Rn9#¿¬]¡.ôWèJr¥Ê>º
+Đ
+0Æ—O¿
+E0‚ ÁEéx~†–Ly%­Ž¸õc¦h]æ`Ò1Åê Çê…K–‰qwáá&U³~ ›Æe¼† (‚Sã<Jj5-h˜7
+³£Mº‘FÈÛ|Ü-{Íǽ ù„{±4bnÒ»L£pùžìa?Vkˆ5;,F’(Ž¨è´Bèó}!©7(ì÷/”FèèFnÏ´W( ê lÖ÷†…þ0q È÷Eøż;˜ù“eERé IZ¿¶^r–¨Íür9±£Ew0"ö‡1ÿƒFô=4G"QÁp^£Öý¥Æ|ѧ£Ú4DM$~o$¡®Ç±{bÕg´uZ.|aÝå‹!LJeSbé”d{ÈZ‹ÅwõEóžš-¹ÏZgøÒ¹|Z.»­+¹!•Ü6”%½~Î
+¤™’J&uEP0H ‡›WƒÓB"ßçt%3|AâÔÄ[þÌ—\Ï²Ì ewøŠi©ú‘\þX(š£sïϯèØýD÷‚?Ü•KoÉÖ«²åW8-?‡DâHþÌxÙó|ßj–òY¡¹‚i©ø1Wq«icxpÃP°.ýFyÃ;Ê®þŽ“ÎdÓ2èõw
+0Î=Ò"¯¶Ò„6… h5NàtA2fΨ”çœh1Œ5³'ÌcËLCõÆ‘æÌ!¯éH8^·X1x¥WæHƒ9ê1Œztcã1Wî¾FáT íË7Iûñ`D8¨èzL#™»ýý[t½!ª?¨?ØnÞíÏê ³ý!󾈼/$ ln4n~=…Æ„„´EBFá•t ƒgnyïMÆØ“ôyåþˆan±K(“¡!Lãg¸Ymñ¢ÇÁYói„¡HT7kʤ¦´Xº‹RM™+ðÌûº§+že‹æD맂íŽTñ­ø“P1ÅΫ‘‡œíAlŠLˆ–ß1Ù¿¦L°YI§ž¶Bü¦Tü“xûoúÇXþ‰XüG]éc¶ð1o½+[o™‹§ù¼O¤œÏhË_ت)¹rZ›?ÇæßÑÝÓÙæï[IB_\B
+9¼“¥ÝõÚð†N¨¾®SÁ`àà$ìz××}Æ¿ã ]àØ {Ž+] “VgÓ[}\é>nñm?ÚŸJv1P1¥c %¢FÃ!Ê+Eã¹êÀšð¢iÄî¿ZåOncøloÒü•«•W¥ ¨
+GÕ †q²‚ÖKù?ÏsEm+/Û['ì¾óÎÀÅjÿ¶ì¸œ¦xœÏ
+Ì…GWéŽWÇì<&Éó.ršc òq~Ô+ »ÍƒÎü¡Æ¬!y›jÏÃך™Ö¥rWUö€Go’Æ—“§ÜÙ±Fs´jÈŠ5åÇšLQ×Ò¡SÙBn¦PBC§ãj–ÍüPAÁ”̽‘…Òßö¶-íÛ¢
+_ßòHoƒ¨Ézl™u¸ÚÑ_å<Z¯VÍ;?ž ¥Êó$G2$æÕLƒmg;Û›ví\÷²4bôd¬=8v´æØàݻ޻u±ºÆ)àe‰LR4EQ ÃÀ~EÑ4o&°…0)ìkKÖ„m;3¶i÷Þ·l{:Ùè]gï]gëÉzIß•ù®¼öí Çï;…XÐ0192)¡E{¹‚
+%WŒàM=0«oi±)>­OÏwøþáhÖ"p¹+ŒŠÈˆ¿`”ßÐʦ”ø cR+œTãWÅ¢/¥’/´Ð}98n„&”¢‹ÖðFYÓi lÔ*ÁJ‰iR¡Ñ8ŽþŒ&4lº5†`YMiTÖŠàCƒ –Qè/¢çk½ø¶¾lÍêÑ;\`ZN‘ 5zOHâò3&<"³; #FPÍEiÐ :°ôW®p»äU‚Ï1[͈¤sÛˆ¤Já-50e/ÍÈ‘•HƒíWB&d­$RàIüe=pM[|M‰¼ÏÛ 2W£›™•¥[rIö'gøŠ•¬ÈcÇA 0V(‰É%¬¹9»h…$ÿܱ&n«Áëz趛–‹néñIÕ?-æ?TK>ud÷‡ySóª³0Éè´ “ˆæ0T< ë6[)D÷•’wDo+…WµÐ]­ø¦Ñ oJá [œ6ï 鹸®GÈqx¿§E®«þ«ŽÒ&ŠƒÚGÓ´fa:“ËF:»F¾©ãPÍÇÚæF‚ÿ« y3ÐblÝû¿YÕaä ÆL"Cj+°Ð„Já;M«=
+žH«}“«|®y/”g õùdæ³Dû™ò4¸âDy[·«„&é|3>i:S»›+É_õ§â7Ë(³ÂU0k8N´].i?øÁççlY;éd²íb" iÒóaq²ˆ…mjzrê}|ýøÉ“g5ò,K¾‰w±[¥`1ܘó`ƒo°Ê:XéèiÕÓÓW©®µÿñçž}u¡ýîÑ&éHµq´Öµ£ñUm!"ÊaÙ}ø§Î„ûh¥6š”Ž¤¸F‡Æ`3EòÕNc¸Ñµ¿Ö3¸b®ymGR ×P½í`*o eŒ¤„¡*ëØJÛ±Z8†\ý ®þFßÇoËÕw‚BÊiNà‚ݯkcuò¡
+Ïp>œ²5Î;?ž µ£T rX–å/¤Š
+æÈœo‹¥lá)­`VÝT"wäؤž´†æ½L|OÀ®••u‚åh:B³ÝlÞ¬³Kxqlí[¥¼_³ÖSRtˆÞP×ì…3Zñ%GÑÛªU¤Íà ˜x™1Ecz­áÍ 9Šà ¯ 9É 0a)† DîÇ
+¢s^©×…q. r˜tÄàš_*1
+Y¬‰²î¡²Ä<ƒé“d É‚`å®Õ b·´ÐoùçvlR,)›6˾°âgŒXÒôë¼ËD‚’­ [SEMç³¾Õ" !ÌæÖ¶Ý#ùÎ-£ñ+µjB ÍXPáŠf%¬Ý§ê¶™÷ôPL8Õ7Ì*Àï);<cVÿÆV…\`EIÄ kg÷óYMïÙ:lp$±a®Tp<±ilÍz@‹ýÝ/>ÛÔå¸àDKpº‘Baã*Õ‰‚a)=¨X³;Âñç–ýp(ù
+—i×ÇÚ”±6fqÑH›3ºÃy«¡`]¥¹6Á¾Ú·¼8ÓFO4n ë¢í>[T॔ZÇwxEÁÎlÌFüf:‹2OŽvøÆûǺBot=¹wiᶖ‚çcƪR¾FÎóæ`ü„%äšT~=z©ÍÿVga¦)<Úaïm^:ïøñPx_éP¤‘b)JI³³ÏÙ•¶vô?*q†Sæ`ÂÚ=P¸çYû'Ës‚àt¹
+ –#["LÝ¢¨¤ËŠÉ+^Š "³s¨"¢¬I‘9Ÿç½è¼æÄ>Ó*/ÚUq¢Szå_µø¥È6Ðcb~P²±
+Ò{+ô‘¶@¦©äͦÒѧœ7Ï;~<þ#ßWK%ŽðÀ2UìÍ«AÁAýxw¥|ƒ½¡ÿ/ÊŠ:„¼n ÔyÃF Xþ†pÉOW¾ÚÇVÖƒ^ø=˜ 襯¥;úý¯§Ì]ý¾ÁP‡5”Vw§•=ýÚýÎPÁÎ~ßP? þ³¼ÚžDáöNÂ/åeqÕ%€‚óXì¡úŒÙ8kU]õ•M˜¡«Nå-µbÊþ¿¡€ŽL$T»D@‚9Qàä¬}øÎ$¢z\¿Õ#³fíu#”•$V3¡×l’ÓÃc¢(‚,aÅxdÓØãŸK<$‘§œ ÍÇ „€O¬Á)«1Ù”·nF M™á«Þê‹þè8©
+©a7`­žÇ€jtUÓ°$ ÙI$ 1FDvO†TYuâx‡ SŠr™ŒI»¬O›ÑÛZÙ¬Ÿ2Ë&Íp»dd°ä$¯,`L%|]‹Mš5ÓFxBmQ‚:BTU¾!ŒCðÊf>—üU^²Móp¹M C•T—("& åøYøTÎÚ±I5ô¥^9a.¼iÆ^L#†Hp¦À~ç^HÑ]MéÞ—d_\mäTE¨Ã"¬ˆèaܪ˜Õ«¦ìŠ+v–F¦ÿA}ÆFqÝ
+c’¶é0w€âõÉ‘&j>ôCÕªRl°¡Êa°’(BêíꈈVuDåvõ×èÍjFï½™7ïÿûÞZé´R>ç é©øBðÝÐ<ŸI%SZ`XôåÒ6HÓˆæ×µ:„Z·õ½–NÐÈ@ª«'Þ1[¤×½q2n ·e{:ÖÑ—ÚÙÛ¾ëh|ûW%¿S\Q,"ÅXpŒb qš q—”Â'£'š; ß¾hÇ`bç7ÐØ£?Ú1Ãh@À?k«ÏÙIÄáXÇK•cþèpØLvs¥@Èx(>R¶Xr¦&9Z•¼ˆ_+Oœ­Iœ GM6c0¼«ɱò–QóŪ¶sØïB͇¯v¸µ‚Q­:át¶ý‚ Ë„ããUÉ‹¡8àç5ñÖl&K"B§ƒk>ð®1¾Öw6œ­ØЕ[Ëë2Ž¬ã‘¶ËcWªR#Á– ¡ØûùŒ‡àèñ\uüÃÐZ–À Ö'óʲ¦¯>šÁÆÇŸ©‘Àÿù;üî^'5Be(\祲–¸ûž›FŽGœéZ#]kž¯s¥—æô6åõ,Ïé_ÍhV°7NJ߶[ŒCXYÈT8æ¹YÅJ
+qw·¹“— g8Yi…Ùâ÷–±‡R ãDÖꦦ5S)/Ȥ±muöOãî}íÂá6û'™÷ó$ðC8˜BûârwŒÂù7áöÇrv'¥‰ìýõ7VJ’d¦rĸö¡ ¶âÛz1”±Æ"Ø™ïÙÿo4³ ÉP
+ÁÈ$ÓHcDHˆÔ¶ä³C¦T™eÜö¡äý\ôMË §¤â»rÙ¤VÞ):£²9<
+!Èø@ŠÂ"4Ÿ¤¥6V^E :nË`1†¡iÜ´,Úh—9*•}i÷Hn;|“rèMÊeá, Iò0$ 2pV ) ±
+)™¤a=9¾s%y£Žåìô|…„‘Q,ˤ ÑºE`YnÞ¸½|RöNÛ=_ŠENï0ëQÅÊQtŠ!ÁaÞb„Ÿ—¼Œšq­ôšè™Ð*×*ªÈ`@AÉÂ-@¥Ô^5kÒ¸§zA#S*,§’Ûj)LgÎÒSqŸ÷~âð]—
+8ª;9'OÚŠå¶JË?ÖnàÔ†ŽS±Ê{ã;úc;f«‚Óí?
+Lr&©MÎ[.Wµ^ ÆFÑáÚÔåšö ‘Ôo_Ž]«lm¿Ðé¹°9ëñÊèR v¡:yзÔJàNŽy½°ü}ï
+‘ç(ŠÊeøX±?ÌÏ´~I|\…{FWªfÝï¬#\ ÄF+ã‡< $Âa£¼ýÃýý«ÇOÈÇG?ÑÈ£G_CeÆ!O4ò§~Cù`“Eœdêîcu ‘¯ñð\4Ò[ïN×åô™<pô×}uzo½Þ×`况¯y‰ƒšeEÓ´ÊÑIÐ$eÅ°,œËd­URöáƬõÎÃ!½oÖã1†ê
+Ž7fõ6:ŽEòÖØëóŽ¬à Ž¦y¨ùP†E^@"9ìÐÃYtkc£súšEùÇ^ÉêY’=P§¦k`ÀsΧ"çh´=ÈÒVšã$ŠSÊ {ÂþøóÒˆ~°ÕÕNè¿Œ ++pÜ&a´…GÂæ&×úÁvøt¡Û\´«EÍqbu%zj©kwʹÇD‘tdcvç:í»‘œæ†¼Î×
+¤?ÍFA®äpB'X v ¤Å>3H$1C#Ë ºpR*šÖN …7²»JéNN³Û,𥃢yŠhÚ •¹ é×…ÀwðJŽÿºü™œ/I¸É0VÂæ\@ã CÑ™ï+Å_H¥·ìyŸål°„BPöb,³€£ –‡=ä.#Ogß‘óþ,øo¹«®e‡ÿ"•ýÆ^´^°gpV©UÂh’àBu¬zK-ƒá”`ÊÓš¿ i°&eÊÆQ¼Bšï¾µiÑ?e/T‹n¨e‚ ÑP sŽ•Éˆ±6+ÚyÝÿ©¸­ø¯ªãj>ì²ÅäŽ –µ}.ß„´è¾á»'9ʶ ŠC–çY‚Ð)é[ôüJ‰–}c}w„⻺ïx y'XÏrUpŒ`y‰•+edbK8ꪽòS©ü¯º×Ä­\ü‰ì½©ýÏiÖÕ=Ís]-ºh÷(IœN*[kW NÄ:"C p:[
+£QÏ>ÝcÇG¿ùç× ÆXÐÈ¿¾«oÉ£YØþñÕ=’áÕ–<h@°j´o2gÿWéiʬ¶ 7秛©€5ÓhJ‡¡ …qÔoyIö;Ì ½\1à‹É*)•­ý«¬ÇV±#ªT‹ùÈ¢ûò£M–¡zûÈÊüᆲáìWsf½cM)Q)k/•nñ;VšFV3­¶Ì*ËU•)ô…çLËŠß ;µÚ…APpì’óã±*;Ü¢ µñ„øÄè RáÕ]áÀSS~±%íë4v ƒIe¨×Ô³†9˜Xbw+ƒÙ3öØû{„£
+GXˆCŠ†uX˜‘®ª¦¥’ÆêëªsZªhAENsh ¨"p‚ÆX¿+UM«çJf9ÏÏi“ˆå¶XHå<Ç4sƒ­ºo¨x@Ì«®9ÖuVóü”u,. Ü©#hRJ‚(›2•gÇYrMëd*Ϻ&IÑ8eCÉŒíª\=g¨º¢9¯©îû\ùUCÁœV=ŠÛeBÿ<‰²à7Å„Þ;­øçYHp{—-y VdÕ·Ô鱚6Õ\
+<)V3 /èa¾€1eº¶·o9Íša¤³ïh<›ï ˆE«
+„
+a—AÈ>±pšF rç˜ÁuGòNižÛ‚s^(‡Lïã ¦GišC)
+ eiŠ!Q”DZ·)íŠRrÛV~ÑèžUjïÑ®›æÒYÁ}Sr6C óE–á!I²™f”Ê;rÉuƒL2+W4"¼ˆX¼Áê 6GfĪiÙyW)»%Umç, ¦#(šÅ²úB,E˜¯©Å·T׌\}S.…Sýšu8†S¨„3 P„¢hŒéã´›jɬ±â–Rý‰PKã8Cr<FÃ;ç6oy”±½îdò&Ù¶ðf¸Í”m‹àhm¼ד4C󼞆kã²ßà˜WÝ÷Ä¢ë’sNðÍÉ5SæÒ¥š?Àì{bù´ì†€YtKvß‘Ü3RùMµèžÑwK¨o`dŒÖ›ü'²5üÊb£,Ò—îØšéÙ‘Nn˦tÛ‰Î7G:¶Ž‚(^{§¡ØÃÓ,‚ *Å5:*z«Sñ,0À$°Û/ÖÆ Ú³ùÔñÄÒ@뱎7Æ’;@DÇÞ„ [ã®åpÚˆÏsÈA{hª&þi rº¾ûbÍÒPêd(1YôÅÎbç|‘3áÞ·œMÉÂhd.x‚(>HvBs8õZ‘ïd¸ûËò¤GÐôÅÑÄo]ëþ²"¾Ø¾ý‰ñ@'Ô„?y®&:^×q¡.v¥*2U±áL8ö'ÿÆsuÑÉÝ©â¦õ¢=¤Z¢œíwÎ6 Óu±ÏCÀÐȹÉg=>à´‰`×éÊŽ³þ®
+ÕDÓ$ƒ<Ëý7u<U#«Ja Ž¤sÌ»›,#­ÚpøY§ªi¸Ù”®7 „̵ýj-TajíðZûÈ:Ë¿‰¯³ç¦®3
+”ä¥Ó‡N›~23Ì4ƒ3˜Zsæεì{ÎwŽÏý¾ßÛb 7šGãÍÙcYgëô¾z¹§”o²1Õ¢õe¯=ÑbO6œ_ó¸æñêœázëh£%Ù’u±ÑœlÑ&¶ÚÎlcìl-–bð€9ÿìsÖd1R“5Ò¼þüøßæ­s¾³I û12§9!“â·”au° ê~ªâAƒânœìÒ
+ñÛÅt˜Â©[¸…!I™F žQAR׌À¬ê¾§¸çí%sZéºóã5Âp"E@GRdTÄmæm_‹¡›j”ø‡6È]¹|Jq/˜‹oƒC´’{¼ï†^²šFbδ¹4²,ùnË)ÌXýŠk5,©nÐH§ ò†(Z$yj4 ¹•ø£
+Ƕ­;?~¨‘ážx6ï7›1jT £ ‡ª tª'ãæÁnðPDïO•uËP>°6Š@ÕÈ)ÏHG:Ìç÷Úúãæ7veíQFµþ¸1´ÇvöÖ#1¾½ó8M„‚Â!$™ˆL«¨ýªÍüëNÒŸQ8P„ÞZæ<ûÃСõÔî””VLòÐцºáê<Þ hÑÎíq½6¹ ñP•p”vZvÝ}3Zþt–òöºóã5¯MP°J
+ŲÞH‹‹rå’^tGñ=´-Iù÷DײQ¸¬¸„<èsIôÜ2—¯¦‘ßÉ0(hdAñ¥bÐ çÍþ?¨îÕ4rGvÏå-<xÀDÑpšãID‹üuÚ ­ê[Ù;-å~'ûoêeóŠoA.X2N+®%½â² Œæ^@èIiz›\ó–¢™[ÑÈ%1*ïð4 ÓHiÄì¾'Á"d,*E¤³4Lâ!HN!xŠD¬H€÷‚4­œ^9œàá×$.1ˆ7á
+ÁBC„é-Þ1«ø—5ï7føö., á^¯ý3¯ÂÄ=·
+Û™Lû›ÓN«#Q  ÆÇHĘ֙¦?4Õ~SÛÉD§ëh`Î0÷Þ½÷œ3ß{Ï÷û9T„çŒâH–5‰,Æicº^1áâXRàN‰HÀ ÖÀ-o“m׌ş«î)µX¸a/\¬ïçæBŠ@›]Ð@2¯ל¡g•¡Y”ú×ÿh ¶5Õê2²c¨±mdóÞc-»O$v ‡;7n;ß1Û>ؼsÏêð+E,ÞÈánÀ€%!ã),ùá] £èÈoÕÓÒŒ;œØ S:ß hÙ¹Ñ_­â êDD«Í=VÑ2æ\ñÆÏ•FÇK‹¥`À_ãõ`ëŸÝœ²"0žgáˬU3‡}.z[.7]­ØúÁ‹ rÇP˜Bc˜Î+ZHWÕžíÄ'½áTÇ,ou¿:H€ÄÆýñ3ÁøAWmˆMƒ5 \ä$€m%PȔٙ_}yåæK^€Sd"Ø<Q–ó„?òE/ø" “gŸI|,¿Pé™ Ÿ° á?Û»ï -r9‹pŽ€¬I*¶ìÞÕö£µÏºªšŽ‡,á̮*kØa©Ìᣒî9 G²ñ8/!U UÈZ²Ti!"\f)%ƒåפeõÖXFj-U©Žk¯/¤‘AÀÎ4Ï ¼Èpðfåï³:ÆL«Ué9]µ–÷Cƾ²ô#•Ú‘g‡T›y Ú4X “×fòØ2äš•×l› âkâà t0
+FCÞà—þ•ÑhƒÆ#©8Ƕe­åt"u5Yö'Ò6KoE Cu &Ý>F#I±t6ƒFlï´ÀüÙÎÜœñf‚Ȳ0­!AO.éUr?“‹?•
+fL‹V5RÕz )Kòt™Â'ëŽÅ~K-úÖö‰É3§•ÌŠî«ŠëšVtS*øÌX4/8©.oFv=ÔÈ´’ §pñ1™Ñ< ¼$S8d6Žæ@#*E«”î=ÍyWö¦L®±àZš{^+¾¡dÝÑVL1¾ ¤°4 …‰LPÌSÓÉŒHyÓÆüOTçÜÒ
+@#¿•<½ š@5½-:§5÷’·„~ê¦Ñ
+¥_™‚€‡yÞ1m)žç
+_ÈþpW:ËËõ‡€£¬œ§¤˜ÞIÙášR ‘)Õ5ktÎ*…ÿ«†¤jäS£{FvN©E§U7E<Ÿ,^xR#,Ëf!æM1ï¶üçÿ«¸#–Ï+æTßuÕ3m
+Üæ½_HYÑ3'ºïòî ÿ¼\ú%ç™\×Õ‚Zá KáÛ¢}?Óê°•Bh€9©Ù=­õ«"¹‚¥7 “fîYß:i‰¤€øŽîØO׸ü6‚ÏÅ„ž¦ö$HÂíCÑŽ‘ØŽ“ñ]'¢;†Û‡£Ûûc}MÛ[Cëàæ²Íb`òS(ßóƺ–‘æÝOM#‘Ž¾h;p(9ntû/6¶*Ã1,K^;]–õ…/–Å'K£ãPaK‹¥‘q_ølyl´$š0¹œBÃ+¿÷5–Îù"ç=ûWÍp ~€VŒTVt ÂÏÆ™så‰?^ ¦¬‘Ñ@ø|01é_-Iœ*¬í IêUVÃa]Ó
+Eäƒöx”9×-a‰Ø…4R˜Ñ4†–‹kXJ(’‡è†%,UÚƒ-ÜËä%T"¹ æ‡'iÓ]gAñ¬8~|GaðÕ,–Éápú
+©Ÿ'kþÂøçÛ·ÚW¬kVïážÕygxçŠg\o›4Ø–£‘ß
+V
+áî‰%÷%' à..G#sr!ˆþï§Ì`€IJÁDEŠÖãØQ.TÐ%_ˆöƒXhÚä½,ÿ˜Iõ­¼
+Q±¨
+%0\Grmµ䶄ö&£{“¡ÝC­;G‚{.G\ï6HvÂ1ùÓ#‘}À gÂ;“ñ}n}gdSûs£‘Ö]á]ýb`Ï…èþ o ´6ðJ05–Üño¹êNø#7ü ‘±Ò£È!¥¶mVF¦Ô(PïÆœ’ɲmôGkãWªÂ£ÞÀ­²ÐµÊð­Šè”;¼ËTšAàãÚ$åß* _÷µÞ¬L;ˆûIYËuopºòÍa÷¦l5¹[òI›?r¿>îÛö®}}µ.‡!0'g—_­Møcc•ÑÑšÐÈxyävuüÿA#ÕÑQOÐNs4úYQø×?¾þ~4òï¥CókÍ,FÂA™ÜþrËPã‹VÕ‚Ä:¢¿:'ÑL×É$›ÅÛ´Ò«1ìŽÔz×X×k ƒ¦d•)Qg¨5Ž¬1)Û¢‘Êû•G;Pmj(ìM;.{±6ûOéé׸äÈ÷ùD…e¸Ê2à/40~e¨NIÔç$^Éé­³œ©6&ëV?þËrÎ6Içª
+µ¦Á&¥ÌD³N2b’¹½A(d<¾EèI=1åD›ÒÕwÇÓ¥ùD›¡#
+Ë 1ôD˜>+8€ÞS@I!è|3l‡ Ç·™Ç„SmÒ¡€®#"tE€dÌ'·ñ‡0PNn…K´àçøÒ¸;®ûùt¦‚‚¡;
+Ó€ˆ
+ܬ_õ…n—Åz]ÍL–ŠSBën{C7«ãcÎÖ{åÛ?p¿ábu/iT<AG„ÜßW?+‹^õ´\©‰
+*nîß ¿WcIV¤×<´6w RJú-ýµº‘5y§}Ùƒu|_£®ÚB"Â*¼A,ŠTqç×ÎWêV?¾aµ†³k­M|Ò§Û©Y%"‰RAŽpæMíÑ°¹g €Ý6vFôÝÑtid9c}Vq£Ÿ>°)ûXˆÅr0ÌvGøÞ6òd\ìŠçum·‰›{¶ÁX{j»Ð™b Xeèi3
+AJTWæë¥
+Ü2$¢bH–djhü1ïœRlàsJÎÿ’õUS‡ñjŽj%ˆƒÑtæŒàû³Ù=i²=æJ÷FŒF) ©Í¤ òG¡DP(( J„2B1G0’
+Ç9 …M ¨ÀŸ ÀžÏEë¢h}•\RþC|™?µu]qü/H›EÒÛïÛŸô$@ì@ „
+Xä?Ï?>s–C ã"È %î{Ý®j<Þ”>ºÊ2^cŒ{óâMŽCë³4ÊDzâr>¨³†Kõ5iȽ/)UJ²¬µËk S*¡ÅÔTš4¤fÿjMþÉ&ÓïRçM?àË­1z¬Û«­c 9#Íb•I'&‹Ð;u§M¢DÌÜå2­J?Þ¸Üìñë×XF3FV›&ê2}x)ãÞeÆ Ëï·è÷ÆÀ¾Ã1yOT †B¯ŠF¬1 ´_¾“¶;Ì …²†;Í{;¤þMÙëjPq6[heìY|½SioNÛUöv¨ƒi(lÞß ù戴/p²ÔyjÌÃ\K¸!+r™Tã¿W¸ÒRጜ?§·Ý1”ÌI¥¥ÒCÑr¹ÉŒàx Úî*¹OŽ–p“ÓJ¡Œ‘<É,)àCÓ8‡ÑZšcÈÔyÙ5c´-ª®‡|q—¨
+4Ž^Æ9Hƒíƒ¤#&åŒyÙùP,¹§CÀœÂEø  ’¡
+¡Y4ÚoɶEƒûœâ\Íñ4‘¢Gñ°3¥
+ž²n'ÁÊáðƒhJ¤H†MZàËïÉy7M9 ²£›7é$Œ`Ybµ …($¡@. ¤AFó4WÈ“Ša$šb)Š¥Yž"ºq^NÞ‚Rð¹\|C)ùcɤÈ71-Íèà†ÄyX7¡"¢Œ¤×R‚$`iXmŠYƒHê-6•ctocÊ= ¯è™7”$bYiVòÈXv[_´ ØîsYðÿÄkÑjAjq*É FÞ~ø¨­÷d 7îy™ËºO…vL{G£}@§ÚzÆ‚=»6„y {‚¢Œ¬Ø½¾íDlçXÛö‰È
+·X•‡¡s¾È…ªàŸÝÁËÞ—ÓNEÀã²;tÁ&™,{ÛEðÙ8µ^ÍnÏq°7uƪ[íœb é¦´¼+ÕWœ uQXÕ«ZëMV.E›Žó?µV|\óJ7*4êM¿~}
+ä3­Î=\oiXê¼æ±Õæñzv½Y§JB¡Ê¾_~°Q¢8^—,A'æµJ6`žAÚ7›âKÿuGF¼Î2Ú¨­³ž\“}tPŸ!h0Ð
+¸]ÙX­?øŽþÃ0˜8
+á'H€4H†(„rä®Á>§:žèKsΠ|—ÀàÚ¤$P!É„aq dµ8¥ÃhÂí¦º7 ó°[aiB&³52ÿPp>bíõÅ‹²í ç.—–¡¡/Áú±@B ÜsŸIù_òŽ~JOÑ)C`ˆ†×$¡@.-ÁTrnBFƒów\Gi€Ž A‡C
+!Šƒ¥Ã20iNÁ`;HœOÒ]Ñ@
+1ÀÞ"L
+rxç\Á@×Jn;ÊgµªyGàžZ±\n2íðß×¼`‘ózð¾äÿB­þP¯
+Ã!È´Zñ…˜S½÷Eï´ø½æ9&$’ƃiŽ£Y†„ÃÀxÈ úc1¸;}"4J–àD2
+'sH°ó'`ƒäò(^¶ó’djV Ûìëñ·F ÈŒZ4k—E‘妑ϵê[Îòi˜¤ô±~…Õ,É"™EŒW2ßëê;¶õ•ñx0ÆX¼÷\þhª0`#KÑž“±¾£Ý{"ù^X2X;¤§íÅ“‰þñwŸÜù³C©Ý„\¡œ:ÂÎ1Çä6Œɦ"ßpº÷ýäžÛ³¡à½Y~xN4òA¼ÿÌ%Ös`Ë.Í[‘]ÂB.ÍŸŽDÁÁ'@#`©W 0ës ËF#àïg“SáÄ/‹šyÚ‹_HáãM–&'|×B]müñ1_{Îå.fÒL¦ìSþΛáôéúød$ú´ø"‰K‘äPj"ýcxû›ÞU•“#XŠ8¨¾8’†ƒ$BêcybÙ;N8Êß©ZÿjnM5«‹ÈÑáÂ@¦©š8ÈêẢº¼Yr9î¼ÙQ¦P†Ð)«ê£Gþûì4ò¯…,üpC;¥
+œA¼­`OÃ’»ê±uæh³#ÓâÞ$7x°…pÙ0Ðâó„çB-Œ¶<ó'ÕžÁf}¤Ñ•i-ÝP<Ô*·Ðã«ò¬5F¿¿h
+j-Ì´±> ùæ<oVºÇ[` AŠe±}åp;ÐŽÀŠ§8¶Œ­[vüøZóŒ¬q¬Ö2Íîá&çXKþ¡fÖCÁÖ…¹@¶Qû7»íqîM‡ÒêÁäó¢×àu0÷F‹÷ï,ŽµYUŽÍ±ºY¬Xi¨^¡´¡E Iø'VD Ål
+çíK  m0íÚ—2¤Õ”~°k±ïU§I}°[Ü©û½
+ÍŒhAt»$Lª•ÿ}3Já]­ì®ê›vV-—•diD÷=½à’sÎð}-p×ðW
+MÂåÞ¢-b 0<‚T”3éªþ‹Ë?cÔM›Á~%×
+Æþ¨bò”c( !Š<ÏCq§ÓH'H/e}MÔÎj!Eæ0‰dš‚Üi¢ ×ÀC-|G÷ƒ‡N+þšA%ÙJ`X‚V)^cˆY­vÞQ}nj¡Î4hp¿óã9¨5@"
+äÑ@:d13z‰Î’:!è¤9V¢%‰!w+®ÛFõC±b^¯¸ТßvW~f¯þ#’o=-ó´>@c8…æ²µ"9«…’1…³›3O`{‚I³bÎYqG¯¼ódá
+JW(zÖoË$úN¦_ß·u—_ÉeQ4o§A1‰dtA‚‚BB¬€ÊOÛc™xïx´÷ƒÔ룱žÑäóAh€I™Î×`:éÚV…BvŽ†ßîò5éKÁØ…pìü¹Z“{=Ó°<(íJ0>ÙÔ5Y“ø¸&ZnšØfc1÷=ÆÎ"r“»tÈßq<¸eoÙÚ„»²+ßEZãáüÖ(æD}קUQ ‘?Õv~Cüó‘xö-
+ÉÂa´ó캕¾ßWn»\›ú4ü¸X“y¡. BØ¥Ögª>u:ƒÚ0­?È‚È?#,|ùdøÂ[oý‚Àœhå%ul-]jWõŽmÌjvþª±øÈF3´› m·B"Ö
+Yiµ[Ûô¼½®øh‡ûH³çتÿ1_g¿Q]wÀÿ„6Øž™»ïÛìãÝx<ãÙ==3Þhq!BìÙ¼
+E%
+€®f¾#ö¼4bú Ék¡»“æÎ6±ÜŠQ¸€âaEAŠ¸¥·~eܵpžEÔ5‰ÿÜÈXvnTŽme:ÚC Ј¶«•ïX¶Ž¨c Ë‘Vº#®ëÞf~{#C³‚c…ã«z™¼EÉ=¯@ˆ¤52-ên]©4¹-»pE7¥5s’sž·Ý ?s·Bˆf—Õ"6„âu·>KØ„2W$ë‚ì|À•O*öC¼©¥+ è@FH*È°•”ð9Ÿ ·{¬ðîú6ø
+'át€nІÀ@“ÜS\sbÉ=ÙúP´NKEO8ç “JôLpå=hþ&„/ÀA¹ªÌu8÷6›ý±ày"Vˆ@%K¢Á(Œ$qb#Å^Wœ‹ŠgV*™• g%ûLöŠ\€&ËE¶
+gê1qÉ^<Ó:t›“<=¤eÊÔ#l ÂTÐ((
+Ê ¥ƒB¡˜'µ54S‹õ$¨0!ÖìAÉtG,mç¬iá…‹ìÌ#¡ôk]Ù©¤GÈnfe†³š UQ°žàˆSyXü·¢~Š-¾Ç?ŠÒ…0
+ÚOG#stÑ¢l›Qlwdÿk8‡#* ¥Áž•Æ¢Áí¿~œ‰ï9™hŽï>™Üóaì™)ùxHg}üèé‹í¨ÉµãYØ­0îÔåìßܶ»vs™>ŸÉD æ(‚”inÝßÓ¼ëÐëÛm‚‘@0„&áj£³j iÐL{NIk$žVÓñ–­™Á0‚&t/©Ž—½z݇½XÿÒŸ€TMù© ¬˜F®¸â—+ZÎb©@óÒH£¼œ±
+«Ÿ Fô"èÁÔjÐ5’ñ²—‘Þs×¥Ê[.ù›S®èå1³柨ˆ]ðÇà‡+®Ø´;1lž´¿ñ¹7zÚñêAk$n*-'K&Ü ÁhT ˆW0•†&Õ
+
+ûÊà'ƒ˜
+«·Îix[q@”,ˆ+¦‘Ù}Ÿ·Þ×Üá9ë¼¾à¦ÑúGƒ{V\^û‡˜Ñy ’npþ”äx$=–¬‹Œc -y@9®Ñî»ÚÊEÑGN‡û-Ö5%ßOçxñmÙ
+ à+œ„KкAga8LrS.ùJr.HöYÁúcKÉÖ›¼õŸ²o^vLó¶k²ëºÁUë¿*ºoJÞyÅ· ùþM—ž£òp"ƒF(Ža†ªQZ£ÞI)³¢c†·ß•Kî*ùôö¿jÝ÷Hï]Òq_ò,JžÛ¢/%z¾£}8û"Uø„±Ï ΔÎ;/òþEÁ{—.…A¡ \P4(ÊÅü†±_e³BÙ=É9£uÝ’üs¤û1m_JoÉ% ‚mV r°>¦‹AðÓÏpÙóÊš­}Jq\V|_ʾÏxÇiºð3Îö7¾t‘s€Cnˆ3BŒš69€mO ÷“шTüDtÞ2x»Å"CÜljÀÉýõM£Éö4švŽFwý/ÊO'÷>+å @‘“ÑÝÀ8þ8Ú]µžÓà¨AIB•™¥'8i5B¬ÊâyžÃ)^7VÔ´îùðõ0C,¸’Eƒc2Fo. ¶´·ìjÜÏ
+kTS:”Æ0LÅeö@^à|¸í²'q>¼èŽN’“¾gªà¢·qÒÓ˜ò'ÏVľp¿1ái:iNù›a¼>0æš+ÝΗÅok8jô7È9V^!@%1M.‚Ÿq­Âßþ#oìéäÑ®W¬­ÐÓ|ÞÝÓùöÇYdié[ÐÈ¿
+á]»Ònßuçö­Í?^S06ŽÖˆÃ!ñT½4T“=XmêX¼Ë½¯q 7Z´.KV©Óÿ 5ÂDͪ‰ÅrF6ˆ½Õ¦Ö0c¦Ä÷}¹ý+Ï4e¬Ú2Ìë­4 Õe…èqat½$ˆ°zP ƒffÊ{7¥¼³Ew8®íz>I·£-èÑX^×vû¾¨ÊÅ ¨ÖU¤t´*ÝÛ¤Þ7ÉΤñwoZÛc*ƒ(hsŽ»r¥÷š@#Ú®VxñP\×ݶÜûfÞJvÄòº·ŠGblGTÿ~n’þË~Ý?5‘Ÿ
+ÒIƒÔA!LØ*xcB°gYØ&\t~Á–Žk­ ‘§jרà¼#8FEGV€çî†üL²v¸þ˜*yÀ8²Z'Ü~Vm-cBÁ¤¶(˹¦ïCµw–óÍ°®º06þ
+!ÿ|‚SÜ\,šFDçc„×…"2 #H–Vàµ%ö3±ŽtÃOz›vj}·góÀ!@‚LÃÎ×úÓÑ9¤í0êl¤ýtSûáÆjž$É¥8ª`i™L¡H• Ÿ£À¦²šTt×G{~éèkÞ³Î]F+ E5±ÙUÞo;Þ´³?Ñq*±÷ddÁ4;vߊ(p‰Ê…¾[ÁmÀ‹Õ (£·Kçêø%_#øäj™´X
+H ‘ápâš{Ëu_ãÝ•-g<[ÊLv¢D•P0qUŠËPhhq* 6 n
+rò“sç¾þòÅ_Áßè†}ùâÅóçÏ¡í÷úàã Ó’J¼°û{¦žr>ÖRk
+Ò5bo…Ø_·PUæ4¦««õ©U…©*M¿‡96¦ÖZÒ›¹ V†D8°¡^N9<‘_ï¶Y+ÔSý•†T¥õX©·ÞØWkê«œ÷Ò™S¦Æ|¬“¬oÐËôK”KH#á8¢ZkÉ?²J— ›2UBO{´Â©RuÆãµ=•¦t­©W§kõ}Õæã¡ÅÒH~ª&?½Jè­ÖŸX£ë­µôÖ™U嶊‚¨ò4Åbž"[W+vp+t»ðËäBiDß)ñ‡š¹}QCWòM\fDI2¡6† ]­â>èTjQŒ;öm—éá‹ û!ôœ°_RŽóq ˆ±»•ìŒÎw]í‰ý Á}ÐHˆû$ód¡´v©R¦†LódqžÏͬÊ:­qÍRžÅª&ÿ+1$–NQöUÑ ãœU‡ºUÅV…âcºb„-¹/¸ÆEÇ}ågžâ­÷Åâ;¢åëù”s‡dÄrÐÅä8K *ä»rlù'”÷íelÓ¼êø e›ÐÚý¿åqKg¢õ<%“jç_:ɖ̲®‡¬cDS8•j#ó±Üœ¹c‚Šs|ËëÔ1¯øÙ†˜ÓP2J‘˪"HDÉ#D³·ödboÿÖðÿhÃŽ­?õÐz
+ÅòT8“ƒþ|}´¿q×@¼#%µeš;ú£óÖ
+×®çLo›Ü§œõׂ µŸóeM°™ÑÒ(å\(z+œüÌßp­ªe°¢árhNh7ÃÉß„«šïânÝ×Cq âõ•±›Á8ˆØ3Xýs¹t9ÜZ¬b
+ÝIMw3}¤E<7uÎ[G°"P„9( šƒq]wKAÇV"l£s剋
+¢FI³¾QÁ™¥m¢wÑËÍ·<ž¨]Y}šwd)çeÊ[D råRN™s 1|JÙ§¸à_˜•iOV]:Íù²¬ï<åõÃÙ
+F˜—)ä ‚ø)ò’Úó„õŒp+¦´%¸’¬ºäÿù1ª-yL;AÎwtNHÚ3µû¡ÖyW³âoœ÷0k²äå2ÎÈ1NIUëb ¥‘´´ûu‘ʇ*¡8'Ç­8·mÕÆMmǤ¶ÞøKfl{ç-§$Uy(P¿ˆâFwžŒ¶ƒú“{ûb»šæ¯‘hû©ØžLSÛ©ÄÞ¾—ö@|ØÔ±­v…“DÀG±š1|Š M#·‰Át-òF^iä¶/2T.ýÞ·¥ŒÞÒ¹£7ƒÉϼ[GÉse‘ÛÁøuot°vûï*¥óžÈ0LMËŸÊj?ý‘±°_g¿Q]wÀÿ†ì¹û:÷ν³Ü™ñ,^±Ç Ø3^g<‹0਀Íì61$E †4ˆ6Å`  1ãYÍÒ6U¥(J¥>D„`C¡˜àÒ¦yèS!ÐþÆIÕ§*222ýtu4ºWçœßhÎ÷s›_…ë;µ]¯ÈöNÁ4dª¶Ô¿_Öu½mà*LçŒ}Öºs®ªÿv}èRsðE÷çª+ ¹îŠ@]s†áÀÒWøkûôÁ’¤ˆ ‹ùöñÿLñ<yöìÙ“'Or™,œ9@Žb„Þb{¶[›n*IŒ©N%ÛnI¸ ©Îe ÖdÀšñjSmï¶T@I¶”žéVE³,§BãXœe5…IC`*_-o(±§z´ç½ð 5é7g:µ©ö%Ï;å±Ì¤¬GNJO÷”îi·”ív:ƽՙõ¦œ_Ly홉žòL—fºQ›ô)¯aºÙ¸'ëL2ÝnI®˜Fl 4´Ë 1À á+É®/NtPYÍ“l!„3„üâdØx,&OìX.NÅuQóÛCºñÓºCTfŒÃàÐÚÕ-ò§ôg^+z#¨öÔ›—HsMÓ®2ó[ã¯#êc!öÌ 2µŽ-}^ðÏD|QV⩸úAãÉAÃp¯(‹ÇòÃÐè(­<Ò9¿’*ïLè¿Ékj+nèÊætõ[µÊOÄ@³šÆVÙir'Š³ššœTw„/ŽZ‘"u(Ž£…Š5`t©EMÏ–‹îÊu¤÷ôå rÕ}©úBÖ±â|ÙK¬z$:î+µ ‘‡Rå×bÕÚÊy¥rÞÐÒNÓ«Y”§95J¹Ì%©Ø¾\pÉéÿÿ
+
+àÉFöç5²}øBtpª9:Po­ Y^61ùU™ûJÛÀ×ÊPj®!|©9 ¹¶vûõ¦è g>v?t‡6‹%<Ëà¸jª®g¶>‰|¯>ö§¶àç ñOêƒ5‡/7 ~Õ0t«64ë ýe]x¹ÖóyMôrË@¯RZ„ÓZø9PÄŒ’4sc.ă¶uËÚ.Ö½rýë;m{á-ªÝhÌ[’ïOc¾E7êC“ŽnÈkš$x’|}ÏÏ5ò Pñ|yú]=}úøñãÛþŒÄ  U-”d6i3­S~}ʯϵÛãòiĘ
+˜¦[”œW™Yoœî(Ît–¿å/4ÑÆóMØX¶×^zȧÄ×j\&‘E¢J‹YÞhÒ§Ýæd«%åÕ%}Eé%¯§(é3g}ÚŒ[“qe;Ês›ìg;ÍSû¹niÊ«?ÒªÛ\jðËÑ:ñpKIf³%ÙeN{Å©zÓL»!ᱦ;ÍÓ^C¢u¥4b¤°CÚ &˜S@;hÅp. WSb [N½ª_Š@iNFäñð"KC
+eVF(©».ÝP°Õ-—Ùt­Ã^P ÆüꉨéÄ
+WÝñèwa'còé "Í&§Àò$B ða¸Šj×Ý–m+7/wÝQÊÿ®­úZv&4•8ƒ±£+ H–Ây¢Ä!T4…¨YJ¢HÁ )’£¹"ZC B“:‚Ó¬F-‚ø±¼fA[ó¥¡|ÞPöHªº'9îék%+¾Á—¼„ê/”ÚûR4mA_}W®˜—«¾4ÔîæM&#8Ž¥ /Þ{¿oïÅÈèriä\xžÑ0NöïI÷ Ò}{
+!ÔœCc:²e qnû>X@
+è5j#B|àì¿Ú¿ì
+­”F X/5æE± i{Ëû¤56æðªqFD©U æcõ;·ßlŠÌÕõÍ6…n5DßutýÂÚœ°uþ¾¡ÿ#wìZëÀ­†e[Ïͦ¡ñÚn«$!H!œê<MK ‹Ð¸„3BÊC¢¸ÄÐKÕ][ï´D_xœ‘›ÍqÌ6Fòfs†o:#°ß6'C‘E€ÙÒ3¹û| ùþŒyúßñ“=6èô<ÍBÄz²øt·’m/?ë×-j¤xºÝ”îZ®TU2Æ©&cÎ+%ýú„ß–ê´ínBIŒdøÕjÆö‹½rÐÒé8¶Ae-Tz”PÙbÕÅï¹­g[”i·!Óa?Xº‚|ƤǖöYSnýt³”jUf: Óò½-„ƒ)dVQp³jÃ5j¬)9ê­ÉmRRneÆ“šÏú,)¿1ݾR1'Ü–$¸¨Í0ÝQ2~`‰’ô—¼ÝêV <£F’fJ÷nÕMä|¹4"N„•‰˜8ÑŸ(úe˜÷8†Êÿ/( 9…qp²ð8Š±(!R,[jTÆ¢ì™Áï é&5“K^0]˜ŽÇ `’‰¸öD 
+( QªâHEÏKëþ!U®xܼ䵠¯9|ª©+ÃT«pLO굄LàôO)‚dYŠ¦Y¥q^
+$’“IšÃá•L,$y‘P79uå7bÍ_µkîë‹ÊiªhjïëþØÿ,±ú¾©nA]ñP5¯8´u º†ß 6-‰°(&Ñ’ᶴtÌì½Ð·
+½°qéØ4­¦nMØãfß«tFáC¥GGﱎß÷õsŽÏó{ú’åEŠL“‹bF VŒrÑâúËê‡ j¦ös½2Üb.@„Ó/‹ER!‹ AÁÛá‚ã(=¢º’šÅÚÀ¢{ïsÏOM27(l»å Ý©U«y3É
+¥au™3+–¬y¹yÁãS¡ôk¹ê¦Rò±$DeÙ(‘µãПò$F‚•st‚Ve‚C)Ê
+ M±c„„fùò]ƒóžTþµqÓßôŸ(IÎÇâÆ'BÅ_ùÊÏLEçšÿÀ<È¥9ç—ré’ë‘°é¶Þ3ÀØ(<‡ÂµA¯Ðâñ®ýïî9x^šR/gL#ßE*0”ôGÏ«G.ô¬`>Àç; «]zËÀ–îs¡a¸&¾w
+^z H„$IŠ1C¯§Ââ7»3µŸ_Wíf5Ù2M$Š;®ºÕy.èšYË'nõ²7PƒòÑ2»qÛ½òðíªàÇõ}Ï;?W7oÀÝ©òßñ„àNÁé¼Á %4Ž¡GÈ;v¤ òÍ·ßü߀äÙò³.ÿ(
+‰‘úKA¬o8b^[¬!£étL7Íx¡¶J‰Ó9ßÆW[Hîl=óRÑpéíúÂé9Þdo/V(¤Ä£9ôî<éÒ6yÊ[”l“ öĪU`ŽÕÛε[’2xæ|»k·÷»q"[O°Z ¥¼ùÆã­ÖøÖÂW½øxÐ`;šœRÚ1Ñ!§Z‰–ܳi“õk¥K
+4UomÌÌ6Ùf| #G|³4¿M*Qd‹ü]„°9È”FìãëH[OFȉ<OFòFú…_vK»gžRîä;=úèNËX¿y4¢Œ„À ÊxÄp"HwÕàÞi$œXíº0ƒé­q4ÌŸîã&ÓZáüf4"õȹz”ÅNÊÊ.ˆ5/7/x<Ëo‹ù_IΧBñ•Mï+î ¦xGê1’Ä¡aI’'gIœÃAdÙ:ŽK
+âË?UJ?³V<`ÄŠ[×_D×Áõ„ßôw¾ês¥ì¡ü£F¾'>7¹¾dO×cnÃ?ùš¸è’M6ƒñ„–ÕèÂÞí³þè%õð´ÿ@ªøRÏ‘LidÞ?œXP€: ÀÀŒ‹á#gB/†µ÷8aR=8<Ì÷D!.©GÁñžÁKýÇàbøV"8´êuCðôùÞ!X©ððdp Mq£& B ¯;}¿óîT‡n»ƒ×׊"5Á[µÁk5½wêú®UùÓ©Š/òa,™…!Eü¾jïÃÊЕêî×u$N°ÿe¿Î‚›¸ï8€¿ö¥2ÒjwµÚ•´‡nÉò!É–I–eÉÆH–veÉ@’0פíLSØø ز.˜L&}èL‡¶tÇ6á,g€†4Mi;} @‚NÚ™N¦cÆ<DóͤÝßÿïßÈú~„’2‰¤ŠÔì(õÔVF䘶
+|9ªTQšw¼æL›fÂͦ½å£ëNy‡¾¨§^™°ªßw—d^-\+ŸjÍu_nÂkH6›âí”J»ÍñW9_9„™§$V™nÔÏM®)9ÚoéFÖS2¼PŠKey¦7lúévã°Û’hS¥³)J¸5)“t&}êD“.íÓ%¼ºïR)…Kû#mªíÕßU,SŠñÇ8¹þp@×eû:Õý1ÍAÞ0¼8AõòºC9«à›JÝ…nÚžNf0F÷w‚U ƒQeLÑU¿¾ž(Ñe‘dñæVùPs$bئéí„‹ávÝ»œM<,äº/ü]øPwißÚ WÑŠŠ T.B6ä%­ësÒök›çJîÒæ?æ[KA/~-¨jËTžꃘò
+‚ºòOœ1 ¡D¥hÔà8ßøÆ/‚‰e0¼ð¦šKõÑ3Îœù¹ÏÇþ¸A8ífk§Ýá9kÇù†®΀£ð EÉÅó=zôŒ
+ù¯G¶4{ô¸ÞQ ÁÅRR^¨9äQf¼¦¤×”ð©3>MÒ³Xé©žðê“ÍÚ—>ãg3~mzõªømw• Í—(ó(¾Â0¾®áŽÕ3MÒk˜ôkÞk+Ž¯Õh%ÓuÔ ³t|ƒf¤)g%›t >áqÇ}†áÕ’5|`fc©%Ó¦ª+hfÞ÷˜†|Š*–’ÈáƒGo4±ñfSÊä`¦ZŒ¯f¼Mù˜d ŒE•hè“-ÆôÒP$[i_ñX«~À¡ͤ˜ÈgHmW‹º'Bõ„ÙÁ˜ê]ž;Ä˲lÐ^4нwl;ôí
+(r‡®ücý#]uCi¹ÆVܤ+
+îvO 9÷ϵ¦‚»GßN
+{ž®§·ýØWTI¡R
+‘Š„7Z×>W—͸ùÚ0„òRiäœ=tÚYhèü¸6|ÖÕy±Fø™}“VŒ°#¨D"‘aR(XÀwšND¶û焳Öàµú®Z‚Í+Ì£
+/•³Fæ\ÑGø”“?ÛÀ_ut.4ò¿qO9¶œ¬ ›YšÁD[Uóµ|+ÊBês+$ôÎmZ«¼ Æ£+ý3¶àɺŽSžèóžÏùZ¸øQCð\=̇‡#}T/”J))†ƒFà¤ÿëß¾¶ÄÇŸÙ!ÿ¾÷‰Fvïê&p)–Í´å\¬’JùŠ>ý˜·8ÓF'Ý‹•žìx³6Õló@ÄC²s)_I¢Ý2ÒNÆVJëDúQ¿qr|´É4¹Z=Õ¢uP™FcrƒjGCѱuLª¹l¼]>â4§|¹îkL4«“^Mº™mÔNùµ#­x«‘’ 2)θUÅñù´‹É8d“uÅGÛt•Œ¢@‹
+˜×ÊØxKÑT;=æRL4’n6Ѧ·•d^1¦Û
+LjíFÛH´/Ñö“1÷h¨7ìJE{³U¾½3×ö¿Ù1Ø  tƒI–eò^¸/ÕÞ™UÛàß™™`O:´ºÀí¤ÛöÀ}½Ëö¸šœ”@$Ba^Õ†YG|¶:0ióOÚƒ·­¡–5¢Ä†¶½Y埮 €F¦+ý÷ñí­NZ+Ã0‘ˆ„ÅEóI’ÿººøÏ®øýêâ®à.m•„J)‚Ç[Ÿ4mɽåƒ0q¦Ú¹Q·Ç«Û?6±sÕ±¿TúïÛãwªY`ÏùŸ–­Ç9|!–Gphîßhž°°SµìÕ¥Ý[íý¹îÁeÜ°²söдyǤ3²¯ÈIòù '~ñóƒ ‘eE,¿ÍçÙ³gÙã—_ÁšÿøâïR±DH
+ >4à.lÓú4©ý[i\±k†#hDw¡A9äÖŒ4©F)*Ó"Mn¦µ97ä Q¢º}>å ¯ èË$Æ-Øn.-dܲ”#×¹† nÅpv:ÜŽ*Y§ÌøJŽ5çr$ù"FPF…nK™q§sëò ¦Óùä:¥9Þ$öœŠ€(zÈ!ÌØô Gq"+"&ãV']údƒvtåö'Ǩ2
+c®ÀP™B1œÄȒ©À(ë‘{?ˆHù÷:‡£û
+ð8Ü©‰É/ÿù¯e‡<þ|E4YN•¹RÈ'( Ã%ˆñP‹>¹I3Ü HÔë+ÖžºL“bÐ¥Kz ÒªDƒ*åV {”éå…fc¯OÁáÒ„ˆáQð‘£;Úh|Û›_-bò9<a¾ð°K5æÓÕå:W;äS©’>ÃP@‚N9‹‡¾Ï„J$$)æ$ˆ
+M“MÞ·]t°]ê¨ÐpHŠòPëFý¡ út}$° Ñ;9_øLTõKVÛ“ D4'£Ì‰°öDTÚZ/ÅÅr.)…×™×-Õ|"·,ȪžŠMk^O/xæ™JÈ‚Ì ™gªç5UËÍ tù7YÒH¶XÓwù¿y$¯xÀ”?Tš–(² )ƒ­›×TMHª¹"%èõ˜ ¡4”t`k4ݱÀ0Æö@qg–²ŒŠœêÏŒDû2±¾T´w(Òs>´7µsÿùo¥C=ïÅ,³4ƒ’¡îÕÖÈp¸wÌß ¹¸ûm¶¾EŒàF(Í>Uºî Ý­ ÌÚØI+{͘«É¹ÍW*ÖölÛZ‚ÓXÇ[µ¡©šÀt¥ÿŽ3>éŒ\µ®Zü7má¶ø\YûGµÛÿ`m+‰QG€ã8ŸwÈè¼UãÏuîŒÅË…ÛŸ°°÷jÂM±kµl›¢„K!v©òrùV€ÊŸlí³uÿf¿ÎŸ›8Ï8€ÿmâ€mI{¯v­Ý•V»’lù6ËÖ}û¶p ¤C¬û°9Ò–Ò4 I)6>0`›•e›«Ói¦“éL§“I§ñ;n§š™ôǶ€I_A'ù©“qk™NvžÙY­ŽçÑ»šý~”X°&æM‘?Öv\2…>´Gï¸v\6u|j‹_qFo,½ï£Kv}À¨2Çþà‰Q˜FPZIýóïÿøz1K‘'O·ÿ…"ß¾ý™FŸÜ€V
+”J%N¼Ht%Ý>ݸWÈØu£^í©Æ
+isE èu¿ªP8¾¿žhÕešKF›xɳZa¤0ž1íRI.!½I]'ä -‡¸³®¯“>žTuGµ}©åÕv2I ‰myJËY«ÂpˆÂrp9ê^§Ü¿…îO©û:…íÆA¤TD¡×Êò¼pÇË`âHPßßÅuÇØÔRû
+ƒÛ˜ÃAÝàv¢'ÂôÄ´Gãºc)`-¥½"@ÁMDQËbÊË}ö{|GļǬêø¼ `ãgú«ºö>[óF²Ï> د‘ï,`ÏÁ*©7<`ªï)+ÁÃ[zÓ¾,…hpz!7Ÿ%™’<ü'Mï…÷Já=Ï‘î>Ÿ|°d<¸d]±Ôôï
+@È7µê?çõ¥ªú º
+Pܧªî
+¦«Úò†0.Gp%'S ÷…èOûS¯½ßqp$¼;“808²;¼ÿ|$‹pf©é.qHŠî=ÔÒQ«Òã9ù$IÊ)âEFp¿‚LêÂ]¯Œ&œàVZ#
+„¡¿ `§Ílº‘¢áÒkuFãÉv½@¢Dä”JuiŸ 9uã^~Ô#f|úQ¿8â3¤´#þ¥öå3-© ‡ïó‰oÔ,Qä*<mÓIžb) Ë7ÉÕ°}„ 9*>— H™¸ÙX>Ôž­g2n}zÕ4R$9ëx©¥PjÎÔsC^¹“3ä*
+àÅP]C"
+óhJAÓ  ido:±lê î>ËÚi,¼÷Ùp0Ùw>ºg0µû½øÏëøb˜ÜŽòw ŽÕRÇóV3–Ð-kò7¦Í<á
+¸L„Ã(¡¤‹+ɬeÎÚ5gK-˜"Ó¶ðá+ !à‚î/v^3‡oØ¢W\Ùý5ÇŠÏyÍœ6/¹²h¼cÙfÒˆ‰r0AÐÂÂÂÇÁþñãÇË®‘F·Gp•œ’2ñmÚ«óê¥OUjÂo8Þ¬Ô@J*Ø\\|> Ž¶•{Ê)œÄJquÈ$ƒå”UhóuCÍÜX½öl«f¢™M׫G³Ñ¥ë ™Æ±_§¿QœwÀÿVŠ
+”Ò*i“Wô‡úK}Q©/,­cŠ}5Vóvæûyò­k[–Yç‚J>`Êg=¶+ÝúÅβó!ÓG»¹x]FKá+ïC1 BjY1›ÛLú ‹ÍêbKIfݵö¿bMûôxÐeK·r;M‹í†øvÕÒÎb“”‘ ;ÓÍôÛ'R…jÓô
+¥@KæÒ mÖùcºC•d:,¹9×
+Õ¤Ìû,ÙÀZç–,´(™&.ß`ýU›5,;סß_gñÛ¡?Pøál7 ¡ Bâ)hÈd~»Á1ÚZ™Ûe‡"Êøát[¾}£4bÎø•\›9ÛaŸ*Y¯z¥Í1ÒŽTÐ: Kiq:„
+F#?Š0‘åÍ(ó~GøRòÄxâ]T FôEÄÁ–7¡FŽu÷õÔNuG.÷-ÔÜU~@Š¬jâduŸ‹ùxߧ¥”@0ŽaQT{¨Äu-ðCèÿE~çî¹íM~éMŽT6(õ˜!C”Àp†¡IPDG@K* Ga˜†£Ë5àjcï wlÉ¿^þÚ“ú²!ºîñ„¿hŒÜqŮՇkvÁ.Iâp…¹{÷îU0<yòä?x(¸Fn_¿†“M0+CkcþuË\°$Û¼Þ­ê8ßÎÏ{Ż԰›•e„F9a\£%eL°i%P¬ý±á5Û?çÕ_nÝšï( ÚÏJ>òY2AËb«šöçw®u®ñlƒýÓ.!Û\2”_·ã6 ¥!å"NÆ$ÁZ¡qýV^ŽT›§»³eéœ ˆ™JÆW’ Ú²!Ýlp£4­hʵ›Ó­ð9˜}âbcYºS¬æ´¨ÀðrC™Ti*iœIèÇ¢òtªPí¯Ž% E䨱0Ô?“§’æÉ~îoqÛÊŽ+&0'e†‡¯˜€QX Ê|à ÇÙwàÍÀCÀ+Àê~äé0;7LÆåñ¸Ú!lF‹$FÃP$¶å k¾%ÕÝW·ßQuTçŸtÎ ¯­Wy)ógÅöÀTó¦ösÁ]-°4Rpq¸2J:ùF,ŸX)ë¹ðáùøÐÅÄñ‹}C…"A²y—@1€¢;Ëër{^M¼Ûåp”
+ǵ(‡"( +U .¹Â7šb7ݱÛu‘õ뮑ëî¾%Oô«ÚÈMw<i¯…àQrvvÚãéӧРpÿøñãÂ:du{öô_6›$
+Ù/´Áº_ïV-Íu[ó>ã^ÁÊ›X• (‡â„Õ°ZÌh¹®2ÇX¨|a—:ç7äƒrv‡šñÚ³kÖ¯äúœö²}ísÍŸ¶[Ï·nv³ MXH=‰€ä —4Z Œb›-üÉF9‚¡jÖg›k2/ä+Aý¥€)í³Î­hDÍul˜F2Íj¦ÕšÙY²èrj¾Õ~®0ÑðÿlÐò¬ÉfùÌO+§÷ƒ™‚iF™HŠç .cêd
+ÒB¹ ƒ†ÄŒ„‹2øy”QšÖKxOSÙ{1ëHRŒEt3ýP#ºÑ˜ej `:šŒ)3qÝdDžŠ›>ˆs^'½çi'h”*:Ì›Êîo8ç}Ññ7cÍ=]Õ†×Ö«¼”¹§/ÿ^®ýB®¯cY
+ªb}5zÆžñó³ß|?³°°°ˆ‡gÏr«‘…çOÚwíÆa <Ï¢Xž°‹IlѥꗺUÕ3MDÆÌ]p«\¥ëµT&QÉé*×YÎ6ò©fÝ9oñžje3[xÌLýØËf]üT½!ëagÜJÑΦêMI×›î«OÚK϶hœE¨ S`ìZ)ËeJ$Ÿ¬S²=åºñ­üôö‚™½è¤D‹rÖjL;t¢Nצ\¦¤Ç zѳ\áÓfÊiÌz )§Z„ПnÔÅjÖɾÇHH
+HFÈ<9)G {õq÷ú“=†±^  ÍH˜_DÐ?–3ƒáÂSaEÜÇEè±(7Ô”®§òI%„# K@'1Ãß”[¾£ÍÈ ™¬dIòw¢òZfV¢$QäÃ$D¹ÇÞšŠõŸïÜ9úA¶óàlìÑw(í;t1Ø—+ ø-éº|LA–ª¸ÎÞr­•Ã°
+W»3ᾄÿ`!ª–£ÀùQoûÕîœíû23¡> ð‹Àúbä8íoì€aXƒ+ðµÒÍ}ÕÒq§Òw§6´ì xKrúUEÛúCuø“ öJÝ\áœ3ÈqËümmôRÅν&KƒÖԮߜ¨ÚvùpînmèFM×¼5ð…¥ûšuÉu7o Þ5ƒißÙürDª5ˆ¼º¸ìž½x½4IÎ5òÝóM‹LP+`Ê­/L·±)ÇR·ªö¼G¸äÒ ±êUtžÊÁ1=eüD“ꜳàâ<c/]Ê}e(¼ÂóU͆ʡº}5P»^wÌbJµqYÀ†FnÒþ¦ûj²vj¶U{ vbÎW ¨nçzÝ[—pšÄÆõ©&Ἃ™rqÓ^nÚʼnv! ÀӰȘ”Û˜òŠ^°à3o¬ j„KÖƒ© ¢‹ŸtÓBÒ£M¹M‰ÖuEFŽZ#T¬Nøé±}¹ÔH<
+Ž úµñ°r<¢û4È5[¥8ªT*Q‰œ(ÖkÞm£‡"üèf8F úÀÅüxï+,Þ;ÎÕ<º±½ºSQð媑
+)â¨0_ˆ¾hÿPßd×ûWöþHܽºó  H&p$WØWÛ
+ža£a¢”ÑC2¹<_ºE¿áÒžûûZ»qŒâ8/Åãm‘Ÿør¦‘Œÿpöů
+BWüCm~À”.{m­äÿ2jA¿f­L¾–T‘yä-«¹à?”ž ôˆÑ¾)ßÁË¡cWüý³Á~1œ€Œwî3ªY©1’È•‚#ÕC{Ïvþp -ÈÉ ™šOµõE哾
+Ñ®šu°™:}¢¾0ÓDçîOÈaŒ¢«ä|kž>_…a¬î­Ðœ
+BŸÙ N ú»Q[)w`¸š#A0$2DŽ‡Õ§co:òhLÌjT°ïI@#¸ñc yL—=dÊh7þQ»é¶â[uÕWTå²×ÙJÞª<¤6½ÊŸ5›QåßjóH[v_Øôµ¦ä/ªMRWüUiýŠ¶ÅHŽã†¢8NKÑí…•  ¯D?LuøZþõ¼z?:’ñží>r¹ãðOƒÇ3/ýûõÛÔ}Çüm«´M„Ø>÷»Ïñõø–„„\¸ä'vìÜeH|ML€Iš4PÕiHÂ-øn®ëº‡íu·îF)tH+£T´›ªi•Öe?“ iHy
+jxPU4‰âÅPUÄjú,ëÏ»ïÖ35çí
+ÿ\…ï|Ùf^…q%Œ~£½}~áñËÁÈ7ó °Õ­›†¿4¥D)ZI”sæH4;MgŒ:/%ËW Îô gš­‡1ÞP˜j[Û» w›¬¸h&R–šWñ$AÑ$¡§i‚@ Áš¼£ESn[¤ÉuÃMÖF­1‡9
+´pËáz[¨àÖÆ\Ú„{©}ÅT£%âÎ;çÈ7J©hp“·ä\‡ñÂI‚–U(03òíwµ*‹b{NÉĈ.bÀcnsvwdžÏ(kÖnˆ:‹Ïo'JEWHA½gZ*@#ò{ݺ>íTH}:ÝòpiX&Š@òT#‹I+eÌõ£~p˜ˆ¢9é·h&wëN…ôã!]‡“§Yx”ñJ\‡’¦|ë‘.Ãd¾&ŸÛ£™è6>·FL“=ì)?{p+A׫9œEž#ÆëçBå¿´×~¦-|¨/~Ä®û»T–ñú{•ÐPä¡~ý"Hžjä¾üŸlñ§šüû¦âÏ5Þ h:Ÿå†'v­aÍ™`"4|¡óÀlpx¦kà4Iÿö Hžj䃎Á‹ýáø\[Š%“·Ò}Þsà²oèòŽƒ×ûŽN÷ oÛ`7(HIIðxÚ!Pjœ«ÔØŽµûà>³žÁTW¼ëÐrinõö'}ƒ—<ƒW|Cžýë$`FÀiÊôN}ï«}²?Úè™Ë´VNþPëým…o¯­*›D4$R/èv˜ÖFV«I’!eVÌV*I–Z•ý½ƒñ×åÔÈ­rÐT"k†Ra‘È…ùùo^ŽFþ/
+J‰«Åìu®‚ÄVí…zkªYŽ§Ác
+Û3Îg•u¥Þ4m7¥^×ü¸2ŒŽ²
+NÐn·Œ‡Ôãˆv²G
+àɉ`˜gádG£hv®Æpx§î\ŸfÔ§?ÓËŒûòF{ž[#'ºuS!a*P´o›N
+³Z ÇÙ%ÃûåÛ§]]&F ¼S.-¤D‰eµ$-2Ä9ÿw=ÓHUàF…¯Œ‘xœ@I‚&©¿Þ»·ðxþåhÐóøß_/,|ðyŽ€Ö¤ÂØWk;Ó‰6Zãn
+Ó~Ь5ç¼ÉÒˆjÒ«|–Ä®r:Å”¬N©'¼@&ì¬rÒ3íÅÃì9òD;¯”œ• E½-Ã)ƒ õÊm³ ™€—™öˆ3½?£ží}Ü´W3 8ä áe\†Ö Ê-4Ì“Ò³¸ø/{ .ú«|ÇCÅ®ûš¢”·¿7Ù\á ÉŸEÈ €CªŠîªr¾c‹î**Ëâï²0F‘<J™È]΄ëh¸«ÿšstç•žSq“8^¹û'ø±1Nì®zG–ºû[
+*)ŒHƒ!9Ë•ñú¨oøº{œ û†À¬s‚ãKþ¡ˆcà²o4NÇàe÷00R¨óxB8I €VÈ9
+"-ml§ù&ÿR×_Ê=_u¬5üêvù<Ï­ÏÍ¢nAÈÓ ±¼š[%Α¼ZvœÃ°k%¾ß”¹E†ËÀ%¿šk¿»'euÞ.wÿº´G'Á7[PPð(áɓף‘ÄZOÖßøì”ñ0I£°l¯1+Ò`×'@bˆÚž;ä,IB÷ŒZÀVˆZôa3 £•éøV&gà­úÖ|ݤYÝË^«Q]±í8m…URÞÆïUf-ڳ㨓-˜ÄÓåì”%g¶!;ºOµª–-Ú)kÉ’²‹‹/UÓö fp;ÌŠY6 áZõÉJF
+qR·_'ÌXh›Aµ åI8gá—ªË-²ô·–¹‡5Î7è/ÛUK5Ú Å°lƒÖ”óã…äΛ¸+uúˆU´ëNÕ 
+ ƒà$Γ,¥t×+Î÷)~qÜ«òËg|xHn‘Oz@€FäçÜš™°.>å”Ï÷fÍG;•ïTKBˆ^J RR‚Àé&³íÑäÒ-aæ|àZM \¥šëvzå&½ìŒ—˜v¨>vÞK ¶<§J#0‚¤%R g4k\ñ÷ŠòïÙÝ÷U»RßþÞd3%N¾
+Ã]-jþ}•§–ÖÐݬÞ~·¼çÛ2±Þ¨ÂðKÚ¿­öݬNÙsû¦Ì5’»G¥ñŸA=rlýéúÓŸ^“FþóÃë러ÿø·ïþ¡f²­(„"„œÐ\0Cõ ˆá: uت
+š“Õ=ÅåxëBC°N}Én<g“¨2 !\?\«ý´»T%.TäFìù¡fúÈNzÎ*.ï×.5*£M†»Ê¢i&¡»³
+Û„¨[®b&CÈœ²ŠÁ—ªÉ°XkˆX¹¨%;¶uü‡&–€0ˆVï3îXn>*Ç)œ•bL_!ß•£‘Â2J+£Å 6íj½1ëC ì](i*KVvžoàVAUfí’);Ö˜6S‚b ‘žI…ÁvÅl7åÑNøÔ^~&ùÙÐH"²@|W˜ŠëìfMöfne«óå$gB<AK1„”sï>õîü!r¬ƒœõj.Ιè¨À¦]úéÞW­ ¸…/7ë&¤!å`+¢f00“H ,í(¥]cKþ¬-ýgª{ß›l¶ü®0NPä¾|×=.ïsº(_N8DKPT²„Ìš[ñ[ñ ~„»O\ñŸºê*uõÇü#?[#ÿïûÎA ˆÝ9y,F"[Ò-Y»çÜý‘ÞѦ{´Ù²L”GI*]šË¨zªWúN¯º†®¸†?q\:Ð扸O‚ƒ Ç;Ž^ëû Y ºÀm®8OŽuô( Ã0'hœ*«¿eé»Wê¾Yíø²ô½{æƒ_w®Uö¤œ›$¿«êþ´ø]Å S]í
+!ˆ#yå7+]_U¸·32†Ä
+šþXæ¹SáK™FÊÝ开’Ò8õÅç_¬ÿ´þš,>
+Íà[³s—[øpÕö˜M³d£ö”óã…ƒûŒ;Ù«»dU_4Sû*!Š•"Ž¨ÛM@ èœG?á7Œû˜¹ä dÒ»A‘DT3~&à=Ê zÚo ”÷5B;5*EQ<Tð†!(ž+Š'>z¾×pÆ#œ?è"žõh§ü
+qßÈè>µ‡³k¶h0ŒQÈǬ›g¬±ßzÃKæsm…H2w™4bDfŸÌÎ='™››ƒqvvÆÓ'O‘81/³Ï"$_5'|bÒ^œñóçý\¢.?õÌ”ÿ_ŧ¦LC~Òð`HùJG6êwTÿ¨`i ô­«óÏøLYOþÞi>»AˆU°6£ˆ¢º\x³Örz‹¬H¦•J8DùR@n;ÙdÉlä“õúlƒ˜¨3§ëõ¹g~ÿçš’õyIŸ9ãÒ¾‚󛊬g=F„X®V(X§¨}ÏÃuÙxBÁ­$€P ôǶåÖƒâT‹˜òfæaW^Ò+d¼‹MÆ„¬­`£
+í](?©X_º£l€À[6ò”sb}cÁcý=ë_£pJKª¹ ÓP("‘”Óí·þäî™pÆÿV¾î^¯m»j¬0uF¯ºc—]‘ñÚŽ›kcKŽ‹¤öØ5{øjupܾˆ~ìh›¬m›p¶ÿÙš¨ ^\ø¥»õ–»ûwö`‰\%§hNŠ‰2’Â0¹
+3Kä¿v§jÂëÝçTUÇUO×åêö‰ÚÈ_í¡kîØ»bn¯¬Ï—Ò¹MñxüÉ<~
+ Ïçzüøñ|ñÅ›jFE`xH,tÁéM‚†4䯲;‹ÉK[1ÛdNçh$/]ǧêLéfÛÙÍE?õòc …é@^¶žK¸Í]Ê“\ÀÕÁPƒdé ™­Lo…èUî¼¢÷ܩͦD“6YoÊ6š€4ÒõbÂó¬ÏÍK ÐÔÃN.é·e¶ïò,/RH†”K_¶·e+‡¸Z¦0:*¯ÏQ>¼YŸjÒ¦½£üá:€í³ig¡[]mm‡©v Št@LŒç›aEç¼…Â2}Â…&>í7¦6X÷ûø5Š.W„R©¤q’‘ ð·EIPCpõUǺèvã`œ?[tìƆ­ïÆ «‹(#US
+S)p9‚ЖílÒŽšºÅ#yGâêSݺb†c]º÷ÃÚ£QÕñy"
+|ÂDÊq°G|'¤)+¥$Ø
+ §ßüò+¥8zIUô™Éù/~íŒÊvG´Ýçmߪ+f¸¥ËZ\qÖ»l錮ò¡®jš±N+sP:­µ~­¯¸£¶ÜåKoë+î©Ö¼ÃšiL¢’*%M ˜WEMC{F£ýcá¾  ý DûÛçc{ÏGû¡†1Þ3Ú5øj7‡
+‚¢YD)G©U DY‚æà15ê?ôŠé–Òáæ¼áFÝOŸj0&üàÓ”©×§}ÏNù†¼D=Ј.ãU&]ÆÑëЪ³„Ts4!c¶Z
+‡¶Ñ^«’¡kUº÷ë-‰fc¦™Ëøu£þ¼”?Ø e]¿Ø4’c¿Lƒð#‘[ ÔóSØÓHn™BÆ ,g;(Ú :0l*‰z—­*$4â*Z-§H‚"Yèˆ@3r)ÜRZz6‰gÞÐj!ý™F
+u o¿&-xŒ&娜"à‚®%”r{ZUt§·ë†Þ1ê$‚^ʵ:ÿ@Xsî €¼=üÑ(;S‹.”õ`Xu4,Šª]åì*T¥¤Wh`o$r‚µÞÖÖÜÓ?à
+hKþ.TÜÓT/}\¾Ðbê6o»«){ÈW>äl÷Ù’¯Åò~°E_iò¿ÑÚî‹ÕŸ±5mœŽDV¢$!ã9¹Tf$Õ=ž–DÇî‹áþKÑ}9`ˆö.`úgbý@ Ép/‰H/(ß÷_öë,¸‰ûŽø{_Ú°¤ÝÕ^Z­Ž]iuX–|1¶eÉ’eݲ±9†ÓÖ--\i“ƒmpâC·,s Ó§LŸ:…!ሦt(’¦t:M'p Áô‘7 ¿°ó›ÿH³»¿•þÿý~Ö¢ª òPÇq0.Çe‘ZGÎ?pÒ¿AH¨„/`ýw#¥"CËÕϹða€"€t`
+!BÂ(ˆä¯ª†È.mÃêv^m
+ÜjŽÞ³FWL»¯5í~`‹®8ŠU¦ÐÍ–ØõÆ@¾nk3©Pbb%x — IóázLúq…ýŠ5´híþÊÒs·6ðÁzo¿ÁrªÚõûúÝíûÀá_XÂ_˜‹®‘Esäz`O¤€sø®1|Ã9UÑBBÂ
+^cqB‡jæ:Ø„C‘wf]åsnEε\i Q–i+˜kÑ$mšœCs
+w—ˆ æûŽ¥w
+-6…®Zv¯<ŠS×ÌÁ?#“:»§„¾šÏS4ÅÇA CÃdá­Ð.âÎV¹î5w?ܺվھn ÿÕÒ÷UmðFc঳çsS°Ø}Ü°¾ƒ·¡?›bWœ=[h-È <àÒ…‹/|þòYÁ!ÏÞŽE^mÏžýÿj/
+e26.F_UõKžt1WEÜ£)œ¶/WÚ0(“ÎÒ”§,í¥Mع«&Þ¢špÿ¬Š·þ9MóÕFõ‘Qžw©g]Ô‚µÊS ‹}ìÅYªEù©]›jß²i‡*aWÍ9Ài9à„£,ã{³FÜʤ½,ëÑÄí@úœ°¤4í”λ gH8Õç;ÄÓN}¼ÃؤJ¶ëSvCÊ¥‹;JgÝút›.Ý
+ίη[#ªWà&)Íx_SüVú´O—õqéVöCíQá4_ñ!ΈX«|~ !%]¡Žàôж™ƒ3"Ó#‰£ë7Y ¨Ì1
+ÆWq´ôDgùh¤Ø‘º—{?€—qRžª,Uöog&z¤Ó{ññˆòìí©n´[ìØ@ó„@¿b•”@KsÑVîL/;Ù­‰r#aj¦w¹úQœ «ÏöÐ!f2$c“aIÿ6RË1BÃ… ®‚Ñ$Yù±ù;ªfÅãò]·¤µ`ÿwfý?$Õÿ$«þ-mø–µÜ7&Ê9X þûuúÔÆ}Æüu_´“Öáv÷·—VÚÕ}r`c #¡ Ç®ƒm¡k%!l1·À`l'¾
+¶Nΰ¾û׺¿ÞÿîÙ¥¼¨95[}ˆÃ°Ÿ`¿PC‹0"!hi
+
+Ç:ü¤j±I•hÒ.Ûd›Ž¢Dsñœ5«o•-Ú ³¶’…u‰&CÒI`XnÑ&òEøõF]2c*{SÒ¿LÂ
+5¢MØ^Sþ8ª9“z²EÙZÎ* </—ÀŠ¤QøÏG0‚ÖnôMö\<¶ÐëŽõ{gzNÍ ¸æû]×Î0…
+
+§”ÆH4êͶF¸K¼z¾GõÉ1±µº|, [覣!åÕær¨ðz79ÁU—R€Q´x—04\5à4A‚ìѳ!GáT§"ÚÁ\ógªñtXy9Xp5¨˜á‰9?y3È] hÇ|x…&ÂW24|n¨I´W¢ùk|÷ãòÇd5lÕ72’þF—~+ªü£º~Eµ§e’/GpBD“õHiÎÛ»âHÿdÍ3¸ˆ¬„G<½‹îÞ_:·êÈÔôO9hh)¡e¢]-¤ZŠö^ñvCöÜòGR®¾¤§ú†œ'9ŠdB€î•é§ÜÝëÁ‘5÷
+1©HtÂPugo볺À½ïž YÊoj\•$‹P¨„ÄDX€ÓmºŠpAM·²Ê
+™šþ¶ò}jR¼MÂÁ"„—|vÈ‹ÆÛ{W;Ï®z7¼Có§"p¸£
+Q±€Æ\H•J«F̽­ç†šÆÏ›/žužj;?=g:7ätî9È`, h §0œÀᆡ¥¤xT_÷4ûÓö]¥¥°Ò“¾€¸¥%wk¼Ïj 7ø{ ü—:ô Ò|‚$ HF «´Ü7z×{Ÿý«Ýk¼ö{ŸÔùŸÖf½O¨‘Gu°´ÿ‘Px¸?Ø!){2p]+@7×6^½zµµ³ór{+ ’­·Ä‘­-Xsg{{ûß14“€†ËEÕ˜Mo.Š› ‹<•9¼!ª+d‰fÎ$Û°”.QÖpL¤ˆ…"‰$à¨@‰ŠÄ€ƒ[úq=ÔK¶ûÉv
+f”@}ÅÒ˜S‘: í¢KZŠ’Nh$Éš¨hé£ÒH³´L&H^hp€4}Ÿ­T›Î´¹#!ÿ7­óý'oø¢}õ%(²‹Á
+NÖj]•GM$NHà³ ÃD¡ ’O‡‰¹€dÚ/½”ÝI.»å³l”W~æᦃðÈ0âhYv•—_öKg~°^¨Ù*æÜT)B!œDÀ ˆdŒà*cø§¸æ_lÅŸ%_ÿ‡ýºÿmâ¾ã
+—¬ÂlaÓU«ïϦ¦±ŒŠÄóEÒfmÆZÍ¿cp±Ês³Ä¹Ì{±ÌK#(JáGyðàÁ£GVTðÏÇŸ<—/d‹ùç;89rä‰2°6i½Ù
+ì3¥®9[è²Åý±Å½X¼V´öüx*™…)“k±È9Wâºbæ‹=ŸTìºEŒÈB؈Ù>[š*rΚœ7‹œ€øÔ´Å÷—"W•\Oc˜LŒg“굚Þäš*s}bvOºfŠ½ýÙur '„cØm[¶®0
+Š¡UÓˆbÀ£ôôC!nȧ8HÜÝðs=ÿ¯pó8’Å*%IÝëP.N´(ùeÃ!õ®-d¼$NÉÈQ’£Ê¡L¨ï÷úSš_{ÔÇBÔa7ð†>êU ùá+øAlÀ­ðFÀ?ßwΕ c$¦¾ã•×ˆH”Ä0Œfi’âh©IŠŽsë¿ÒZ–yã׊œëêÜ¿ó…ßÈ ?SïrÆ/eù· >W¿ÔÈÿhn©
+ï$-i
+ïªw™÷”YUä|ÉgßP”µ³I*>{…†uEPRDªæÝÖ×O9~u.Øû¾œõtBÅGW¯åÏy»Æ{Þ tÇãl‹»ª3
+ åy‚öåÛÎ7µû:„ÚÍr’ÂÑ°HZª·žöLø:#ÎÖI78('Œ`ãí\­ W³:N7î>½³Ó^l•­AâÅ&0F•ñú7önìë)ß{ ú@wy÷[µ}ÏÓÈ~Û~àJg}gS¡=ƒI“!4PÇ$$IJïVÿP☭üåtQ`1/ðq‘c¾*ôi¹÷’É1kñÍ™½WL®Ù
+ÿšóã©LÕ„,þkežÅÇL¥oºÌû§
+¯RÆ&b¤ˆ',Jý‡Vÿ”±ñ3‹ÿVEðbÑë—ÌŽ«yŽy³ÿóÒææÔb†Ó(ÎôZ͵T˜ª
+§HšãF]Öiû‘ÜÝ—ùîŦöÙ¦c îä ƒ¬„X·”w‡æÝÁK-=ñS¾ëtc«
+"PO%eK­‘ËHU3õ}8D‡³e„"b‹ 9BÖYÇù€™÷=ñ®
+墘7§]Ü—ÝW›‰I˜Fp: </°QR($åïÔÛ'ÎhÐ5²Ç“mÍÑ6Çt_¦n¢³yª³i.X7ÝvûP9É`˜!hBUïÜ:ä— ñì¸O=âQº×M#g<Š©C̘W=âÓ xtC¾Ì³‡µÁzðØ Š²ROûØQ¿êŒ—àÈè¡ôÖ¨g„AQr&ÿŒ²õÙVõä!jÀ©ñ+Æü’Ø!eW½ t
+Lh²@hŒÊ¼+-¸«öȹ/ÉúR}O£ÿ‹$û•½á±û¶^Xß(òþ)ÛñIÎ×Rý×ÒÂÉÎ •¢C Í,
+ „rŒFqŠ%$nCÕgð'¾pÈ%.tÑs|³a‘ ‚Ó%׺¥ü9>–]pu^ä»Î{wïqH7A J˜T³|çÈ–tT‚a‹¡Z×Xýát©¼J’$ø‚D}aÙEøRݱ„FÀ\p@­£L–<‘ù¦ö)oG–\‰Á" ƒ–„0¼;¥º«:®ì ›#§ÊOôWœ:QÑû2ôíî •{J{ú­§z¬½á]a¿ÙgÖ”Òb08 )$ Ñ.©v&w÷ªÉwÝÒzÍÏ÷«…ö›Fþ–Ñs½Ð¹áüx®nïŒ+÷³ë·&þÃâf­`…EÚ-P$ÇöAÞå2ïj Þyg÷ËRîN‘÷ŠÁ½bk= Ì”Ó$ƒ RµQý_5sŸ—x®î°ß(iéÍ(!M“ÀÀ<çþ6®€8BxôäÉÓHâ{?~œ˜
+%Lrïîš Y ¶±IÒMyÓÓfªÞ@:§œ³)gm©‹•ºXå&å„Ã’±á«ÓÏWÊJ²ö¨:+ä4­0$ÚpN¼fm]¨Ò^ÚÍÎWhçöè‡÷Êö¦m–}¥r!É0¡h„“TÁ~Kc¬Ó5vôàb¨a®³)ÚáœìpO\S@‘¦ØK5⊅ÆÛ]s] 3†hÀ ››voÆ’@܃Á‘TßÇ Ù•.ÝÈúP$^§9É„Ÿ™ðiÎúÓýÊ·dIJ^{´­5fœiM›xrS­ÙãÇd;ŸAQCP ±¸Frâ]§fÈŸ>àÓÔ[ÑŠÜ”SnM¸ ߦÛ
+’J9R_v¼E5êO>Û¢äEC¯¬z4þÕ°WvÆ šL>ãkÂ1_ò©&©¯QËd›Q5̲I¨Bª—I.+õ÷T†ûRýŸÙ¬?)s~¯ÉþR–µá±û¶^Xgõ`²¾aò×ä% ò#͈Q)Æ0Ià$,B‹déïÕxÜÚ à1×ðC 
+k Ä.ËŠæÕ|TƯ”µ¬ù»ÙÜíÏÇ>“ (ÃB»,{Ãú·xo»?5p¿0rFƒ(ÁÐà.ôóÿûäéÇ¿Éã§OŸ<#Á›9ž>;¾“IbòŸo6×Ö³‹‘ „oÚ~Ø ž­RO[ÿßé¬[,UÏÛRf*5SâR–DqšŒI«™«K›Ü¥›¯ÖOPšÒ)CD*#7œ¯«‘ikÊ\Uf´Vë/B’1)p[LÑŒ±°8§4wo¿—‹†€=öÏvÔEÛšcÇt'ítÇ‚ÎhÀNçB/ÓHãl¨i2`o³O¶;f‚ŽÉo츼8ÜWÑÿدÓç&î3à¯ûª“àCÖÞ§V+Y·mlã c#_B’-aLGmŒîÃPð¥Ãò&Sð…/À–…/`&äèôE’¤™¡…ÉA’–4oÚ) ÐGvÓiÚð"­Mg¢ùŽfgG»û]Íìïù,†
+(.É ƒNáÔaÕd@9êÙ,&:ø1/F¼fÄ¡Ïyá›?„˜ô+†œšé£ä˜W?þŠ¾«EV` ¥N’JŠKG¥”\–Ýy@1TŒÔ- EÁNÚ\FçÈP’“`‚ýÈÀòÁFáŒàÆ}Âĺ©¾cOq2)%íh@7}D5ÙžTÓ˜—œ ð3~ùˆKiªKèL„ÅH‰œ}űô´†êãtk²ò‡ã=±ä¾ð·ÚÊ”Ýò­YS”~¤ªý…XÑŠóLVš“Ò$…c‹bù˜Ì_V¿â‰®úc³mÇ—‚‘ËþP
+NŠs Kæ«‹{Û 3G…õÎü9¿l2 õ}מy§½ê©jÒÇžuiƪ /¹1PN´+¦Úá7¤Ïš¦ã‰´ -Â0””Hg½XO"Kò¼„ŠGlå粪”Ýò­¹'VÅhe.–¯®F)¤$à…×jsO
+Ì#½¡D ²àî½âéYut^rõÂL¿ì
+_v†®¸ÂWÜô gϦMywhŕ̆–½p•«m½Ë‡{šó+IœÈÄQ“¦p¥µëuoìô~?¼‹i’H«?ÚxhÁÑ,æ
+ÃQpø¢«wãœP;áèÞ¬ž³­Ç·³jAâR)C2&X¶Z‡v÷Dë¢'NÕõ›ûêûûm1{?äyŸ <ÌÑXC¬¯>t²<i?±™N²Äz-Çì
+BH0’f¸ä #DVf--N”ÚoÔyîÂÐ4ºRÎÉÛ5î{Fß[ƶ›5®»uÞ7k=o˜3;š›Ø­r)K–LJdgà°¬e"R§TzPWt±ì¥fN‡Rž!E’¸^êHUÿ[Õž58ÊÝeØ aq\døð‰î$EÖEðøñãh䫧Oÿ ßëçéWϾü⥓ˆš”#’ô¢±faŬ_°B4óí’M™hPÌYrõs ›7ëu‹6ÍÜ.üÍ0Z/Ý£ÊÀ_ÌMG<ƒ@³(‚Ç%¬Z"ð$-Ù:¼'Þ¤JXTWšdófýr½&nÒÆ­†xSªt!,×(—Lš„U·P¯[°©“wdS.Ù5sMŠ›¸bWë” UÚ‹fÃù&õìËù5%O­Á¬¥ ”¢xü³9Þx¢¹u²Èá˜íu]Ã7Äùrüi8hË %(‰ÉÁpMà'ü¸[7î5œñ‹#É ŠÈó SAnÔŠØ,¥
+¨*~¥¬X$ ŒB F<Ë14—œæ¦ÍšÚ «žèåÖîå¶îq_—1¯(|ÐØXu$UsÉ^u8C¯ùb«îpÜճ⋂|¾ð¢/² ~pö,xCq_vÖè
+¡&Šã¹„쨥9hߧ¤8RÆ©%$œç5gd±µsÉMxBpàfõtA¥9OÏb
+˜¹êŽ@O¸D]N9 S“BàÉ$2lI´!3 <Oß5€“Á†AËÉó(¥£¦Ý¬®Un‘%ß50’¥X0¿˜E4‹‰òæ;¦àûUîÛF÷»5ÞuÞ·«¼·Œž÷«ýïÕx×jaçzû×&÷›uîwªÝ7«Ö“¢)»Ú÷zù¾†ÊrZN¢¨”"P†BqBŽRMN`õHg1‘W—]¯v¦ªç “ãæN×õ:Ÿžàþp)ŠýææԪ㹟'I#j݇S8“ó,•(×,6h/5è—ìê+D{ÉóWŸ°ëÿF}½ý´‘žq
++€[ÝÃ÷cp9‘1½ÃéêË?šô ‹Èéºç‰Â™S}ãG[5r%Ǥˆ”ÀI¡óVvEëG~Þ2•.Œ6ŽM6L€F®8®\izoÂ5öNã@OUGžºX‰ðˆCiX›Éȃo•f*|ëŸÊ:EoÔøVx?«<<ø¸ÚóiEïcxY ðQåRéƒ1PäQÕþi¤&W2ý±Ö?_ì>ÅdggŠ“4™É†1ɲÌá 4~äÔŸkƒûÕç£c^¸u)ûi%Påh¶ ¿P ýÿËíûÄýïÿð;–¤RŒ¢0I!j¾ÕaªO5K´«õ9 '”~¥Þð#KÒ“æ)§iÝiŠ7ñ:]ÊeÙ8k¹æRõkuœœÈ h !$Ý‚nÞ‘½^o[>¦M6˜o;³fê-g”ñ:uÜ¡[mÊNœÜ/äÝre/» I·>åÒÑ;õ9+õ–8éhV¼ˆ’·tJˆ”c¹´Bü·ÉÕ”B!GX—b™Æ
+˹÷ú»–¢m …;—^SpÈ¿¤I#í‰ñö«oójZ “Ÿ\Æ ˜ª,O?ãcnù?·áBºk^ÓtPKH¸þ¼¹þœéi>’3Þ­¯+“S˜”@GQÎU/?íÓÞ*§zM7û…kíò
+ChD)Ak³
+¯´ãÁèz÷EðCºÒ<ˆnv ®t¾ ÀÀ•´T*ÕJ­œúà\ä~dr«sh³gøVx¤¡Ð®U°Œ uÈYŠT(.H‰¶’c+¾¡û¾Ñ»Ñ±Ö=JË¥2«”£(tÎ(è£ËL×À½àX²ãgÕ;´½›¾þ“þ¨xBotËÝò^ÚˆŒ¿ëj/du„,ƒ†[‡*8‰êDQkÔ9uDÓE‘×o5r¹æ½Ëµ—/7NŽ·Œ_rGš£'­gs•føÕPVžIË$˜Œ"™< z­Àñ &ð¸Â÷ Òóè¨ÿaµï“Zÿ'UHÿÏ+¼_Ø=P"Hªýú÷+å?>ÒùY•çË
+ÿ—å¾'ÕÁj ÛÉICEŒÒ!$+G‡H=Né>ª
+K‹¶«n>Ñ $Á úxcÎZ3”f¥N›J›F4I'쳓Múd¯Õn¸Ì©Ÿš—ÜÂZ›!Þ&Üv­† ·f½Á´ÑÌ/ËšjQ:u‡,™ÚwÊrÖ܇M eúúù_+ÑeXqª7[øÍFݚôâÐÇëØd­n£‰_mÎ~¿YY¡Á C*0g(F‰‘¸\®ÉÑÿYgmâäò`Ûò``uòš"€ Èë±x0Mi»5ØŸhì;C!™˜帄BUÇ+ØYŸ>3p‹aÍ͈H‚)Ÿ°NWÊC¬gÅúà´Ü\À´4=Ö%«/Šh>ðgC|,Ä/„Ê…`Ö\D8S³7j9‰Ð”V"®A&båé²¢ðfÍuŸy®>ÈÆ‚†)Ú42ÔÍ…@# & c~zÞ·E3ënG´
+&¶%Ù’­ÓG >dÝ2—qldK²d›Ë€|_Ø–|à
+["éÒHÄ>ÈhÄ2tÊ|2b4GCæpÌ4|ºi¤»Æo‘Wç#R”‹æ`H–Ã8—[#¤¦Êmë›5ž•mwë¼7Ì0 ÈLÂÄà|¦ÏL‹gRç¼]ÛySßb6ëüj½ŸV9ïÕuMmkl& %>'{K¬Âº©s?Ö82Uç}ƒ÷7ÚÖb.†çprQD&$¿~öÕw/È0;þÇõ"•zùËS?Æz‚|ì¸l!’+i,.ZÛ)O4€`†%{Ñ’]¾l–®ZÒÕÍ¥ ;½lU.Û
+’6åš]–´P‹fù²µä’Y¾fÍOZ4—lê¹2±«à\‹¬A-A8.›ËÏÁíù¥³Íôº¹0Y¯™®Í”FdkM`3ÅŠE¾h tÊU³òj“tîm’î-Ëgç",† Yˆð²Å”D»Ï晶-„Îôv%£sïµÏ÷€@ îÅ0Œí3} ‘ÎùþÎ4Qâ^í_8Ù¶)5jhVžˆËÉA¸>&9ÜR4Ö½X|Á¥˜Pã>¦‘Ρ/x
+¦«›¯{BËîàRgXbwáÛ$ËaçñæM¨¦‹æœ½ñöc4c b6J!ÌÛ+d#pa¾Ëe«8xOãµö>`ÒjGßŶ£në¡½n(SsGÜCÙ°
+Ül8ÒVÿ’'˜ì<yͧͻúviô"CqOÀä!eHÉqãñ¨%6l95d?¤K#±£1sì”åÔiËYÜ 3÷±HMÆø®3Gª½•Ä[â<€‡G7)@ö+J®T¾³Y¸kðÞÓ»èÝ÷u®;zçï Î?¼–ÉgZgÆ4Rë¼­ë€qÓä»[ï¾®k»nè¸e÷Ý®v|bt}l ¬×QÖ\¯÷Ü­î¼nÊX·Œž³ÅõÙ]AÉïìÜ“z•ú?µH*õ}
+$’‚|ùä „‡b<TÊGYwÛ\sQb'•
+ê¢>Š&}¢Ù
+‚l­6.¸ŽAÿ¨kø*8¤ãä5Oäòk<$á´i¤3¸ÚÙðE>r„ §›ò·JE\"\ÒXZÙQ×öà²ØE¸´­Ò<ëê…Õw•òI>z¬˜•“[,U\íŠ%tè èa–»ÂPù¯@Ô†k éèƒ%VœŒÞ÷EÀ'éª?éî_uᱬôÖ露xŒ< ŽŸ\ŠC¶U8Ûrz¨n$f:5hŠ˜ÂiÓˆuDMÑhýPÌ4<d ™ûÖHØ<6ŽÀ—Ѧ¶ª}…ˆgÉPŽˆ@…B‚°²
+qüDYͯ«:6uîÇzߟµ¾‡Õ®›:Ç ƒó–ÑýiM¦(ÂhäÑsßྣí`XRç¾SçúDÛú¹Þ ^ºoôÿQï¹að<¬ñ=­öÂÏ2UçoÍÞfBÅF8b>Ž"ü÷7®@¯ñï—™vÇ¿¾*½J¥þõêE*e¨«çgçÁù^TªGS¾Ð¢H6K6`ƒzÙ
+`­˜ÓÖЗ
+!.Šrxb +Ûªnö¿Û™ˆîO†‹¡Àü€s¢§c¶×±<Ð6×ë™þ¤Œ€¸iŸéƒ¤K#‡fûÚ&OøV£­s½»C.Å ÌfÎÊÉjËŠCí…“GÈ n2î‘Mø¡5§«Ë+ã~˜Y9^p+gRSX%Ì_0êÆÝÐåU]ÊàÁ_Ê2 ç³ò²P.›áÞÆü™cÄ9gñ¹€z¦›Ž9ÀØ„›÷€jàß4P$Smšê”^ôÀl0-=î—ûÁ<?E~ѯº â^|ÊÏzeÓ~EÜ#íPvaÅ<”ð°<¡0[€láfL$yoÂ$×$o?¦ŒÏèÚ'òÊG´æYšñ¶þ3Ís¹î©¤òc²ò¬t«Ž 4ç?Ô×éSçð¡u HÚ[»ºVT“À˜KB  ‡$<N
+ZÁÂÖ^ðÌ
+…À$ ^„”_kÒ^ê)]îÞ+@ÏÊõ®ÒÍîòT›¨]†P߃¾(LÊ B\€á$)Ñ*›]o;–ÜÅTÿ¹°÷Ü(·4~dáøÑ̸c%äXôCõ/A Î¥+ÙÕPĽÍ—F¸å ¬Ø7?â[ôÛ.øÞ’$ \¨A(Pï4ʦ8ÑœGuÆ Y/šÏ›F¤g=@ñ9ŸxÖ]<?(q©Òƒò7;? <åÜ3Ý°Õ%ø| Š—ð(Õ‘ŽÒ³Ç˜·bþ˜<=$?ãÍùJASÍ´` Kû@5Š3 N¾úTÍ €C@8Ò¹œs€d»àQžTN{‹§ÜªYN‘v‰ç²yWqú·©fìªcÝxUàjP2ž€¢i¾b…:GiîËë¿VêK«÷<ÖÿOë²ÊÂT
+)EqŠæ‹hL†Rx4HhÚ\sðÃ÷<×ÝÁëý£¶Ñ¬7¸æ eœãë¾Èº'—à×\9|ìÈ[šoì:€!;ÉØÇn &§mÃ?ThŒ$Š |,·ÿÀ´åy²â€+@GK¡VmüL,dD<Ì^ßyq0’³MÿøU_ ÜrÑ€ó%OnZ€
+8d•³Š+˜¯þ?rä¨vÞ~¢I¡E x$CÓ”P‚’û¥ÕÃ]à k$eˆÅÛ“ óéHG2Ù™'Š“ œÉÎÉxg"Òšc‰®dÌw„Oéâ“13TbÒ<9¡OFÛRIóô¤5<dð5j›hœÁq”‚'>N0%B ÞU•g¾õëç½w.juܽFç^¥üçMîûMî{:÷Ýæ\3·Z]À’[í®ûÍý7;·m›|¿jvþÜ2p³»ÕâØ«>G¥0
+‡??‰XꨕûúÕÈîñj§½—ú³‚•Ã§Ñ/¦øÊS‡Ê—»TY£vŬX³À dµí¿òe3VLZ¨,d0‚%1R,Ø'©cØãõ%YSÅŒ‘2°|Áwi‚BÞ”6Ì÷¨×:óµ®lµ]žígMÅë]åëoiV,êeSYÖZ¼nRn˜ÔYKiÆôƒŒ±lͨ\׫®ʳï–MYØ÷kx*Øbd8`ã1¨á4¿îVÇÙñ¾ÅÀÑ 'œ«AûÂè·‚!=â[ »VC}K£}™±þ%0÷è_ë[8bq]z—bötÀ½µÍ‡Ý™Dïù`ÿRø(¼,í[·-ÁüΕÀ{çFúG{/ŒÚ—ö…qpˆs)л<êÎœçÇú.ø›©#gG+5òÑ‚&aC‡d × ÉËNyÊÎ °³.ɬ !ÙµDÚ é§šöä+ýeça õüPÙ)Öú‚ðe”°€D”ej¸Î¤=šÙÉ4W67gez@¶ð£Òá#ìa=Y£¥ö—›šöö0gìÜ rÖ 2)Mv9S¹a p cw çâ¹|õ¯N²³Å“äCÝX½þÙe<TÈcH„æáä>Q(BËPt×ÜP¶%®yÁÖ>fk³UÏå5OeU¥_*+³µO¥µ_‰kÿ ‚ª—=—U?‘WÿNÙðHÞ°Å6<aëŸJë¶dµOu[Êú=çÁ¿Ô“ósÙgÒÜMÁÝ=R¾ùHUõº•Ü’ÁÅšglõ 6w_Ïd•O$u[Òz¸©lÝ×’Ï¥U[l%üÊïÕÚ/K*W¾ø#Óü™Ò0#¯6!$%%ÀPGh
+’8ŠI„†Ô[ÓGe½á‹Nÿ¦;|ÙÙt gó•Ú«\`Íá‡9¯8C›6ÿ†ÝÙκ3sApÂWø*^<<¤ D¹V)
+‘BzâzÅþ¬Ãÿ1Yô3}'®{bÀ’ž¦x¢b ”âj O:
+D$ÆG³‹ÕŠºó½À Ñ9›ü‚3÷¢'à4ëz.@ò™J> › .˜’¤?³}ú ^õ›‰ö
+AÉ”rp5BÉÁË"¹¯È0~‘=)ÓüA^óXl¸/Þû­¬Èñ)úVRôµ´è+YÑײÒod¥·Ò½ü>S~›-€Þ}OV¼%/¸+-ºËÞa
+^:?~ß±%·ØÐÈ–¤l[ºç‘´ì1»çSºMí_mI*À`pÂw¢Â;â’mIù6[
+±Åß~vï°÷$%éâ[âÒÇ¢ÊÇtõo™²"å«_ ä "öD$IRÛ:2‹_H+\†öY×H´{h£b£7 l°áˬF }_õ†9©®‘«®ñkÞ 
+ÅÁÿÆÎO2ê¼çLS
+ÆÀ†L埞Ðæ‡e$Wl>|’ö‰wÔYÕª0éÚŽpa÷”ƒH,@‘1 üxîL'Cõ¡ÓæS† À>9Óz.lO·© †›¦ÂÍã¡é–©pCødóÉLi$Ô†™A>  !Óð¡Òƒ¬žà`$F¨8˜”ËçXÔì»^íüSã‰/Ëí7}Ô;>Ú×ó—èÈÎj{>¯r¼tüÌñÇïõZÇ}öÏ«íŸÔöü¾Úþi­û¢¶Þëj ÿzòOèòOž<y©Üø¯çùáéŸ|ô1Ô<,¯æE÷«#uÊÍfyÂ-[4ÿÔ]i*›4È:s™iÈDu¥^2¨×Zˆ+m¬¨ãˆ2Éqúˆ–YkÎÔuñFm²M¿Ò¢‹7iWšr“Š£4i,Y?¤Š›ä‰:õ•FæJ³b­C;Þ.,•“GAì8ƒPLNqQ]‰¾cÀv|ÖïLLv/]ôÙãÖ¨Ï2Ò}‘F¬Ë#¶8 bÔµt/‡ºüÇæGlËc®ËÏATÇpîVÀçÈ…™Í¥˜Œ"Q^6.B…jºÙÛÑùxV&,³CÝK£G‡íÉñc ÃŽÄh¤si¨;æ÷Æ‚¶ˆß²0bONºÆ;ÇÜ9r ÍFQTÈP¯å)'{°ˆ›™qª/¸•ÿ9ÏLÂ\vgª›Ãüùó'DíÐÍUÓ6¼Jq¹:„‚Ý÷ä)|G4³ý`bÞ­·rÊr%dzïIíâB1‡ÕsjB,ËdÉiÖbT]òÈ£o)Þñä_ð*ßvQ n ÈN쀠‘ÃŒ¬‰j®_éÃgœøœKígúè·¹Své-giû8Œb”
+¶è‚´CØbˆ»’’—ÎÅ=y9¼ßzfHþ>[r_\¼-*z$«„ÃoÄEib)+¶÷¥å%å_*óï)
+¿—”|O—>`öüUZù•¬ò¦´ü]F¨+ƒ•Â_A0Ž˜Šy•–‰rù4W°O©h>¼hÙpoØFSž±U÷` Ýj5G
+ö‰
+U̯Õ9tÆB¶Xd]
+8R¡g
+
+v.ØãAç¢ßu&Âð­eÞ÷æêézïA–& øÃÃÍZõê¿™¯óç&Î3àÿBg°u¬vµ·vµZÆ66>å36øec›p6²NK>BoÉòŒß–ñi;ù¹3™¶3Mˤ)dJÁ`âƒðc‡!ô‘ÍtÚLøM‰»óÌÎήfßG+½ûý¼šQ'3îÔþ‡ !Ÿi,¾ Õ•èDù®FâB ü¸K5çÓM7ðÞJü AºoŠÈ…IEg…iÐAO¹¹I·±,•É”J%/Ç%E'™Œ'JÇòP5 ‹P2FŽÚm5.žç§êãÆw42íúo~€F ¢Õù›šò
+“>äÑLû`\p´
+Ç䌋Ÿ­7L¸5GåÙû%4h7`Œ(åÕRšQÆ2±‚2æ$Añ‰ÿ`ÒïsæGšÜ'PB$øW*ˆéô§ê¬Çš¬¢ùžÖüPÌy"ä|­ÊyÎäì9?~T`ŒmÁüX“ùHÌ|¤5Cmi³žŠY´)PÅ”MõÁG|
+Ô¦¶)d<Ô¼§=øO!mK•½ÅæÂe÷ÐF ü¸ˆþÆ(Žb$A1 ƒ‘´åd¸QÉVg Ô6¬5÷~Xßsí\û†­ã–+°êêZqv‚C®»ý»á rˆV”CÝpt9àþ»m¸ý7aÐ3-ë¶v¸
+'ëZá< ¼qó\Ä*Ð@‹õ%…ÉÉIbŸ,QoòV6[ß+OÉÑRª‰.iæXnѺ7ò"=Û;a ¨~¢×?t~Ó×»bkƒ;ï6|­18pÔžÄë`6ÉHe JAjs:-­ÁâàÛT°ô î·ôGÊG.Z/^¬î*ñ÷•Ë‡*†úJzŠûz­½ý]–@´4·*¶Œôì;<Ð_6Øwd PtYêͺ¥ã±ÈÒLI¢«Qò¸1y#ïôí"ߧ‘üõÝ=ìýcAÝ_òí{΃_Z#¹®¿yþTä¼]ìºg¶Ã£¸™}Z‹¯~ñü{ÈzHùW¯_ÿëå˽UÇÛ¶]¼zõê%tøê
+¯•ë¥ÂÕÝR±nµØtóøþ+Õq§3YÀär,Ûìb¤1Z‘qìpÍX˹ŎS Íu«NεÖ] 8Âû\{¤:À$põmqÎ\p.t‚΄»\«ý¶ñ¶ìãÅRNA+”<A 8ÅÊ |9†‹‘H •
+¥I©\Î*IX6Š A„Câ,éî±vÏ\À>ï¯]è<;×
+
+²ÍµÙVý5ómÐ h$ÒI¸»öªLr.(8^¬& ¥D*P¬S¨ªrã'|šP=;å¡B.HX
+ ö˜L/CcHŒÄ ^©”*#&É7%PX5ëh]r¶¯Ú;®Û:>rtßr÷ܨï‰d««jÃ
+ÏÒ¥”YŠ¦†FHvHùHÖïÔÿ綫‘7{pÓ¯¿yöµA§§"€¢dqƒå‰á#qâª%.\ùók¤:}¡"y¹Ì°\ž<^%֤˓œ•Š2NKè• Iâò¬Ö‘›±t\¿^µqÃGö/–'^µšÖ­ü†Eõa…þú»‰+'«“sÅ$N¡T(I†R’@ŽÄ05&WvÚêî¹6×Ü×jÇ©¥&÷²¿n¾½öJ‹}¦Õ1ߨ™kµ-u¾]#mð±³síu‹š± Õ…£Ä
+‚8I`DvFVOWà‹;w_ï˜þQw>ÿûâì|jB‰`AJÁ±JƧèÃ-
+ MûçÏÇ:®ÊÄP#@ˆž÷3îd!#‘ã< oxŽfá­Èg,Yì„G¿ðk…½4r^‚¢:U\ Ÿ«WÍù@¦ÐÎS>¨ÈÍŽ|v÷Q)nÒ Ø€ûCÿü¨C=yJâ”×0áÖ„Üì¬G5ãÑÅ…šLSMºÉMó»ì¡ThžAà×ÄX­d4û ªPÈ*9)¡ˆØ \î#Ù ÕÏÄÜmun„"${úW\Ê3îàw\ò >eïùñ¿õX@Jÿ’O{¦Êø†Îü–1Çæ<Wå>3¶E°GÖS6kS÷…öOÔæ[DbÅ™•J "aé¿™¯Ó˜6ï;à¯÷fë´€ç~?¾±MC8BCB 0ØCB’fËÁåã±ÍÑ€ïsÂB¸Ì’tj¥iï¦iÚ¦JÙÒeiºuZ¶t‡º*{1©åš÷{ju/ºF“&7Ìþ鯇Gèù_~žïça1Òn%#!¾Á b¡¨@ªr3Þ:çZ±õ$\î»Vÿ}[à-‹ÿ^«w£ÍËç¬Ã¿àð R‚ó©@OWšƒ1
+Þ¡”ˆ’«´Aè÷Õô ú_¤‚9:pêz¸2­ ÅŒ€ÀÍ«SÉ$Þ}ת'Òà„xK{GÌÃqC,j§M#æPÀä÷ÁÚP¤.Ššc‘Êx¨*2rr dì©Ï7)¥:°½Jˆ M`8 ¿Fª[+:ó¸Êõ°ÜþÛrÇžóà%ׯÊlï—ÙVayPa{¿Òõ“2‹–a"°J|ú‡T܃C>ÝÜü¿ÕHò ŠÀgw{4”bPþÕÃD²Æ‚Â…³šõ:ÝbM΢éëÖHÖj³¤WÜ©ÍГ¹†fŠJDKÊILümö¼Q§î3¯7k§õùwÒ7ž…úܯí’A±bÈ^m|mêì‘X½¶H%T!´ž™„”Ð Éàj[u廎±žk3aiÎ}~¾»mÞÛ>ëo¾Ö2}Køl‹`
+œ¦æë"m¹öÓ´D¢“¨Pwõ}“SLwhnØTÃÙ°†Òˆr”K$k‚@”ãí­NÍí.ÕO5(bÆ%›éȺٞ5îÌúü?¡Õ»ÀÒM:£Vi×)º4n+å>T+¢å"ð àGI ¥àH$E*TP…¡J6-+ø¹²ô™¢ pòTqø‰²pÏùñ¥z–Uô‘¢è/ÒC ‘¿*JŸ©^ÿMVé#Més©þwÒ?’•L²6F}Œ å˜@„b»É? Qx® "Ãä0Š¦býÔ¹HRˆþ$µïíÏj«{Ãâ»oÞ³@×kßÎ PaÃ懂ßp¡]±z—-ž´¥¹ÍݽŅ ÷%›wåj¬çts>!—â`p¯!ηû:Jf1Ÿ…!­´-=ïpáÅÖÞ†×^—Âã‡''¦ $MúúĕȲçÍÛ®(´pqž
+Ð L'ÕÝç,I×øášà¨;müŠÍØÝçŽVáߤÖÞ8˜LüBɹ`m`Ðt=R‹š£cô+UЫ÷GëkGÌCþo¼%ö÷Ÿ'w’ÉÏ’Ÿ|ð·a׈Çä9s#PŠâQÃW_äª_æh_C´Ac(lˆðç‡Â¦x¬"r½¦¯ÏvUq¹åR!#Á(‚¢ù õšë5-Oê.ÿôDÛžóà%×ãrû{Ç­*-¿.³=Ö·‡ €aÈ*Š ëLµ"ÛÛÛÉT’Ànmí¡7þëgww’ngk;¥‘÷~ùhÏ
+q1Ë <þ¼|¹&gAŸ³üµkäÕCþ|µfÍ”?ÿ†ð £’J±Å V„HOÔŽ™÷/4XlÈZ3«×MÙ‹•éêW¶Ú ^®Í^4ê–9+õyãuŠ‹¹ßR“@¡`g ‘Qš¹%К3õÖ™€u!xaÎÓ”ð·-šoûZfƒÍsakÂÛ2ß{qöÚÅywSÂÛ<ën›ñ:ç‚/ÒÈ©…nËjÈ>ÖSÖX‰BR(‚·EFˆVWèŸ~ø{þ§³½ ÙÜÙÜJî~š/noîní$w·þÉÿºžü‰õ{—dB\%BDœI %%º Cov,Ç.L\¶Í{¬3|/à%€ÎØæÜÖÙ^ëœ×9åmZ ›û:,«D œÂ•"ìùR
+‘ŒEx©Qâèa k"¥×Ùœ Õá=çÇÔ¡?É‹þ,/úHuômÉ5‡–dºaŠ-“À;„€Ãm"„Ý‚rRŠIåb–ŸAƒœA&¹rÕ¥’Š3­ g añlXü÷lÁ5+¤3äi`­Í»â ,;üKœo…ó¯sHð»VÿF›7¥ÈôT‚¯X<@‘»®pºÒœïÝâ[mêË[ i¸~l:F^üj~\£D(JIPBA³¡ó åŽèÚÅîûí‘YÎSSPŒÓ»+Æ*²ò笽÷¬þï[yÀt`÷!6ï.rT5¯†=
+ÊßGK;œ- ™¥“í
+>ˆtÌÍ=ï‘œz‹–#âP„Bã¡`4°x¦X2}‚rΑwÖ#s ŽUq”{¬2÷Ü;ؤ“{¡þ›3æáD=ҳάQ'>ÝÆŠ¶I§<¼Q›hæ„äŒæHÌ´#çììhr
+l›Ž°éL>Ââ"
+X4Îíàf̱sË>tSòˆ+ßâ–oñJ?å—m¥›ÄÑMâȆ xSxø3~Á6¯ð!;oƒ“³!,\—ÝÞ'ò?ç}Ê-|Ä‘módKdŸ²/ÐÂÂC[ÄÁ-¾lp] Û¯KŽüEtèhù‚‚Ml ž†þ×yÅ÷ðâûÈVØŽJ5žMC0*¤§H2úᜯFŽBÒŠ"»…ÅbñÓè9^qЩ4žipƒ/¯¶ö\2{÷Ê…¿«®Ú‚ñ†ŽZ{¯8‚+-§ÁÁ»(`
+xyÂÓþ÷G_˜ FJ:g±!u•T¶Nz›£§- þ–¹.Ëì®Ñ/ÿ´Ì'Õ<ãµÎù-³>¨mó¨ÏvÛÇO£6“±ƒ
+fZ%Wþm{x#µRåkô|·¤n¦ö
+ëC¦ñ소ѶŒq7wæ„èd=Uȃ€,u0KlæM¹³'Úñq'/Úž$¨ónΈ%I ã­Ù;ÑÁ&n_/bŒØ2'\“®ŒóíøYpoê…ÓHá°3F>™D/a¨‰°êp…Œ%äÐaIAƒÎ„@’$X;
+KŒ1l„¦“pF:¦¿¢~Š#ŒàÒeNÁo8Å·yeŸð•e[BÅ_ àÊ}¢´É¯Ø&”ùŠø¥›"åºHqWT¾)?d—¯ äŸãòù6Wù€«ÜÀÊîcå÷Ø1K~Á-‰cÅ#Œl7Sh¸Ål”ÇJ§0÷ ›ABiéL¥2™ŒœOaK³¨¬N¢óÐ2)¬£DXù@CÛ¸åÔ¼;°ÒÛ“¾ 6ú²h¨cõ¾i{W¡iÕJX¼
+Ë?àô o‡ú=WèWx¶áäðÛmÙŽ¦àJv—K*¬Jk±§_¬T '-^öW‡‚êþÁÚ!¸ ©Ãýšþ°*ª B;R{žì¯3N
+y
+ AŒ¨ô—Gï–:¯—™oU;oWXnUY×”ö
+óªl€"7å¶ëÚ¶?j=kG­/'¾?\SZï¨Ük
+W ^@à8Ä ”BD")§HYÆ7²ÚJyþì‰$3“™Ž÷'Èó|UâKñ²úEÓˆ ^•³¤ÎXQ¡ñjâ]}æ¼&ïÝcÌ+ƽ¢‘Ü“(nÌ\Ñe/« UfBW°läÅtÃuÿ¡¾ÜŸšJÏ8þtº«@È9ï¹_BHHP]°C€¬—բܒW+B\D 䢠ufwúK§ý¥ÛÎnµV·3kw¦¿ÔÎÎv§?ì:ÝUQÓçÀŽÝéŽ3í,Ží™gÎ<9'ç=Ïû¼ïy¾Ÿ‡0ë¶qo*”A FwÓŒÂb±Ü»w/ ø<ñû[·E^ ô²l2—z¬­¶u~жà¯í>}¹³~Ö[?ë{Øæû
+2¬‰-Œâ!Ö#U3•ét™LêÌù«å´—Ò»8íA_Á ¥$mà„žÓã2ž¤¤RIù›4ASˆ"qšA¬@ˆ"!ò˜È¦ò,ÂÀàíl] •ˆTb„VÎäÒÊ’Œõ¦à1Ûekç¢SRö•F諾dtÙÖ³dõJ<àÜJuþ¯l¶É;ÚÜ]’÷6Üf8²æ
+\³ù®yúAâ-€—æ\>ËÎ|ˆ—`ì–ã?³öŽ5t¼¥Ö#R:Ò9Q/¨¸|-€1×{
+ó“¯ݽ}çÀþ|é«€9#R.—‰<§D¨1}÷ûÿXâü Èz¿Äñ¡Áþ‰±åîAûo‹ïš<;o4ü®ÂýÚqâûZ¡ýŽÁñQ‰ë½ý?IKÅrDÊ0… >xð`S&^Ç·ýÿ—ãYâëP4$Èšà•,iÒ«ç«Ô×*^5¨VLÙqSîB¹zÙ¤½nÉŒ³ËŸa«hD\1çÍ–ïY¨Ô,•WŒšÕ£š‰ªŒú<Y&¹JVñ¼£Ù’”SùûÜX½™ØÐýõ€¤»³ hdÃäš<]ËÅ^ÛloÍlWÝ‚¯qÞgŸó¿ŒFà–5æ³Ç@ qÝœœÆyÿÉ3 ˜ˆ ªCÑ#Rä¿ùõûðƧOŸ¼l]€=6·Ó “˜dãJt0Ì,!œ"0‚ÅŠÌ%îXÐ뫹ÜÕ¸Ð{jª£i¾×*áEÀQus^ˆPÊ>ÝÛ63U™O§Ê¡ÏMÃI¢ÈÊ}Ú‡bÜ ½©¡šÉ按o€ü-Tç´I·ê’G9Ú¤s)'ÝšX»Îwz{n‹“Њ"ŽqŠ¸”:CÖy¦Ñ‚²Ôªöã™SíôE‡vº]Q} Á‰rRê˜öèé¼,–ç…¼lUËQåt+(¾fÔ Š¥ô×@»›h6I&dŒ¡Èm™¼æì ÍT‹b•¾u”õ2@8D7Ñœq©xŒŽy
+Çó,í.MÂyáa¦ˆb%ƒüQ`CË(tSji¨Ð†
+ÙYqI#¯mdõ†'8Òt¶H“­Àå,‡!
+)! ×¹ ¶PEpÈ WL‘`ÅÐ@et°b \)H؉VEåý`ýÁó¦<º±Åý‹ñ³ñ Ù®"ã¯îÿùÜ/@!'À6‹Qâ³þ>5è/óEŒá¨q8j:7|tÔ{¨g«h$h Šœ+ ”j
+OŠ„´9¬6êŸ>¾ÿäÑã͆qyq)K§'p¤æ…d”Š‘8É C²‹ï\Ûÿ|À Î-£ ü~ý“9
+ÐHílõJ .ÖcÝàýÞPñ¥Ö’`δv¬olõõÇÿÉ~ü+¶Ä†ó<áhhPÒ ‹á4–ʸ¹ù¤k"ôŸž÷ÖÎtA„4Ò!Õźâ¾Ú¹Î†9égÓlÀ6Õ›]¸“%‰¦Æ),%åŸÔ—{LTÙÇÿjÚÿ»
+3sç¾ïÜ;¯;Ì0"ŠV òD¨*ó¾óR„yÁðP`˜qxƒ(¢®µlÓ&&›6»IÓ´»6v“¶i·ÿènûG×(ò˜þ.li»ýg1¶w~99÷œsçžó;çþ¾ŸŸª¾D“ôr@ q—jÄ%¨$Ȩ:!€m•:ƒ(«Ç½TÒ ´
+¨ìªˆgPaSn&%Wôu75n!{ë(¯Iq²€+ÊSäòŒF¡ÂhNN*d@„¥8Œf1œÀâ
+OÑ÷zjg[oX;A…oÛ:ïØ7„ð¢+,ª¤# Ò *y³¹DZ6•t ºì!Ðл®®7E#‰“Í·”¡pNÁÉð2þÚoò2É”÷NÚ/ÁÌïÙÂ?vEïØB0á|íNð "•U(\°tÜsuG …@#
+Ž#¶e¾­0\÷vÍ5µÜº~è肅À"
+@%@Xï±þžª>àŠØÙØ÷~ ‘eåYZ „×:ñ£èÑ© ]q,ÿåsh‡xõ ­¦Wÿ–žÞ
+UõDËû
+¿.v>,qÿîˆóÃëÃÃö?q¿qœøªVæ~ð¶ã½Bk­À09änðuðó÷7ebS#^ŠÅË–ÿ—K<eëk^»‹¤S¨p¹®å€q®öuÓHö|Mî5Ñ ×ªù…Jõ‚I{à ¶U4¢Y21Ó%Ù·ò.Ï:¨f‘ '!ÃT!r°M‚ðçG¿O¯®­¦—ÿž~¶º±ƒ+++/=36 O¢(¥p^næ»ÏLµX';þ;´Ì†ÎLuXç#–©€0©Úe¨rU`ƒ’ÿìñgpJVD(Y[߸þe_6[–——_mé­lŒß̪ÍU4ˆ2A²ÎU§ƒN÷B_ãô%Û|È9±\‹
+Œ¤%h¸=´¶ávñý+/¾ìÀ¼úÑmLíŸGk]4‘JÒé'Ož ٠ĸÁ$*
+¬ÉNxµu.(òÒTÇ™Ù@ËtÐ1²Ít8f;-“-Sí-³Óãíµ¾SC­Ú¼,C(Š€|âpö™JÍeûÎñs
+…P ¢¬KøȤÀ§üycçÉ‚=à@,SFâ„„£øê¢ýƒ^~êk!Mù šïæSý-
+§E‚t%H'Ã$&7ð  øÿŒL›ÖŒFÎÛ¡Cæ’,…\"^e2
+LÃlƧ°d´w D„²ùP0eˆXG;lô˜÷ÐÙãßB#¦!OÓ€£iØmõ‡ÜÖsÞ¶¸+3ŸÐ9DŽIî~ôéfÀÅ%Âø7¨ýÚ3I¿Qé)=}Ó`¾óO˜y.Ùµ_¾£8âr/[¿ÛÕ C‡vrÌspØÙ8è49çÚɦÁ“ô(Ã*@,‡‡½ôh¨ÁeÂò¥‹y«Ù|ž”=ת )èk•÷Ði‘öP +¥ÎÄ MqŠÚ¥ócVi¿ ï6æ“Ä͹=ð³U2ÐZÒÝ*Yñácy]tnoëÆî¢ÏZ`l
+·$i
+8\4yA:Óz:µdkE# «—*ÓñÅ‹ÈÊçB~n.*j<1Ûì¾pÔD1nö
+»
+©XR2ºoãØnâ¼:oª^1Z¯H6”Œjò&«V›R–³ü„¶(Y_4¦SŽh”“õ¹ÓõYãyRSz®Îä×ÛH“ê-£7ÿTƒ–HÓ±@‹
+(w õ{}ýü‹ÿuevkµÈO
+ÂÉÛà*`dØ–£ÄÌòÜŒ¡…¸yµãZ}³ ùÆ (º#¿ä a­àÛl‰g €,:>N¹Á’´{ÊâKàÀ€Ñ•¦‹´ˆh¦% \@ãF'\>L9Ç­^¸„¹g³‚±øönÝhŠàê_Œ` ZÁçr£kÈäkóH€³îÅ2 È‚°…È:”›6~>ßWZ¾Úë0 dhrO}e°0X„ó&ûŠœQÊ©Ù²\7ƒ„…×)kìÚö>RE:4ºàr*ï×yBj°2УïVqíu_M^ŸKýÄìîµ»ÑÚP—ºÓw°cñ³TêP©-@ °© vè£nu  ‹ti»œ{Ý×Go¤¤Þì¼l†ô¡ ÚV/;îrÈÐ…€LºpXªüQ]$PëkßëØ.Û.cKq¼¹¹\öåÿ¥þk]½ÿ~0äB*CP4ƒ-&à\Þ6DÛùò»5-ïU‘ïï"o•>
+b™²y"ìAëš¼çjœ lh/t¸Åê¹hjë©mVcŒ(QqŽ@Êcs¹<üœw(ò€.?0{7`ž©šÖKöPouSG á Bæ±a8*È`£zõ†‘fïKÐH<É*˜&Ì*g“sŠ\ ž•o,b88JŒ¢Š`ßF¶É7ÛµõíåÉ|×vtè"톎g¦¿Îé6vwùZçùÎ Àf=\LÜüéo[u®^mWGQøH…çw¿ºx²øŃÙáòP¸8Èñ•¶ á’p‹îpàüH(âÑyýz¿¯ØÔ–« ˆ¯$Ô…€"aC¸CÿÅß¹'²ë>%W‰­â"1†!~ø£'ó ‰'_ka„­T`]½{÷®Ñh€ÕLë?a¡\ Ž«Òxû2Ö_Þ^ó-uMk»¶µî×ä'%öwXf·‘³…õ7 (Iê J´Ü(¦WJ#W‹èO
+è[;¨Ÿm¯»^HÎW4æwZ¢›ö­KCW Ps6‘qï޽䜟#IþOx}©÷8|jBS€bpÆ7ªØ~Å„.{ªD:£ ¨bù”a¥4’1¥SNê×N–ªNëÁHéq­ò=mn´(}ºTß½~´By K¬ÂP6,526ÑýÆëßýèÇ—áGÇ|¥Ë×ì< G†kÒVaJ‘µó€¤*Új9 ðpÖŒ´ZÇÜ–Q„{͘“÷ÕŽ¸Ìãn:æ­rÃ^¶ˆ@¸0ŒÞ½ó·Å…ÿþ´ž$_n;??gØ
+‰«%lvšX€#|¾ 3]~¨"»ß.k(¢3REvÒa
+ß‚ÍoÜU>cr½oõ]¬œ·§Í®ó&w¤¶q‘-Oã«Q*5o7 ÓÎi»ÿ…«,9 S(ÏyÒ šš©qÀIíY·çB®ò¾
+nhè_Ñ×5tr“x |… °Þp'¬þæ美Ö^‰#õúR/ôáýJ¹‚ÇE :…8Gá*V¿W&ïÊŒçÆK•ñ݊ɲ•ÒHÞT™2V QOÕ“†ì¸Q1³;}Ƹn¤"“ÞÈQq0Þj‚ƒ¤s° œXÍbeÉ2{»{ž:d)‘*KO–þ‹pÿ­ ›`ù‚½ Oˆ(¶å5÷{kÇÛª'Úª¢NÓ˜‹Šº,#NÓ¸»jÜiŽº-m ÓˆÃ4ä¬ ÚW‹Ø„¡þ¦o>É—žürÿØTåÑ£GBTˆ±¸‹…gtŸÛó1cly¦FFÔ„Ç:æywèhíé
+šDš7Ø"”»%GÞi%†ࢬ‡”ôÛäI?ˆOÕƒõª>»$\B(ÂØ‘%ÃDÌv2ÅÓ›öÊNÐ9ѸMtÜ*8aÍ9(ì§`jÐV:À°|¶!ºÌ™É ¾âåÕÐ $U9K2|‘ò]"}-L}Æìf.6˜ÛLN¨ß˜¡½ñ:T±Ô9áÓ¸Õ=ióô™Wn.R ¥(Ê(d®Ï}sÀr”r¦10Z{ä’=4N:·gçÃA0žKŽ@:Ñà™¬>:J:-š²öýÔ¶ to
+ QŽîªÆ1»çì¡vð Œä%hdæß™ž1¹ÎÕ¹> 1Ú]¾±
+ah¯`›°y„ÇÆø¤™Â¤HÂ~ÌüøñãÄKó3¯.ûÏ%a)ÜþøŸ‰¦‘.‡ý7e_ýAðW;â­‹8 QŸeÄÝÐ׬jÑL¸Õƒ§LQOãnð”%âÝ£.Àð¹8|ŽÁ³]ÖÈË¢x¥-Ù*¹ÅbËÿW¬”'˜8£ÆeÔ9"­†ˆc%4Œº4ýÍP LUuÖù }.¥©Š€ÆbÈ ‘ñIÇd'g ç…-ÊÅç °@|'é1Cù+6nÏì²H5J©§h$ [Ó/˜E½K·î6ƒ.DçôdÄ?‰Š$I² ŒbÐþ“;,kU󊶡;rSû±!+à*ûØáÌÕéuV—.²‚S
+m B÷SÛJ Dïœ4¹§-^zÉ*48A/Wž)MËU êO™=³³YYC
+ø°Ä(.Žq¸.—ÅE(œÜ”– g¯éÝ7ëÝPTï8£¶‰R.E²IœÅbmgº+5%9Û âmË¢ZpßK/`pÍä ÝÐ{~Ð÷]¢ÈtRhà.½Sµe|AŠ)1+{GúŽCi‡$=@ AÑÌð©ü
+b:Š°¤l„Ëåi¹½:½Ç–uñ¨¼·4%=ÜY›¦HØúº³ª¤ËHž×Iûmé½ÖÔ^š@ è€(l6ˆ»Œë{lçLÂFÞ‰# œq¸ á—ïÂmÂA{v@ŸÙÕ( Óäà×—08L°+ GÞ Âeöƒ¹A}nØ.¹h¥zMâ‹V¨);k\kKü\4rYÛJkÄè™6º'¬žh£gÀäèÖžðVÖûª¼‡tþjC·îäøQߤÅ£
+Á`tŠE¶ò¶º”ÍþW 8¥]Õ0(  e¥ìïSvUP­µÐ¨
+4í?õA߇‰ï ‹ÏæA%àñ¹ØMw?øÄSæòªüû/8 ÒOåó+|ÀžvE;˜$¤
+•A_‰®ìU‚寪‘`i›¯ÈwZu–VMeȦ°mmæ3@ 0aQµú]u±Ø÷KoâÛ‡Äå
+IœÀ>þtÒ„ÚïãsKäzð‡G¥JO(d²9°40&‡ƒ!^AÉnhïí4|µ×ú§<ëìNý<ÝgÅæÅ–Ù=kæ{ùº™ÓÃ<Ãl¾ù“|ÝÃ"ÛíG2Ù(ÂÇ9(‡BÑ¿}ýubi\±ç‹? µÿÓ-©(f£ G1 áQ¬·ùå©YÃãûäcå²qeæ„r­4"‹gL–çLWoî®ÊPoCä(“õ&Ž Å>6mÜ<=:‘xF/«.½ž$B
+Û½BëlŽv£häv>L­ŸÍ3܆f®iFQoÌ܇!p XõÑVU/E£ #óË"÷?#ño777·Ü`íÏß~G“Òw³è'â•àò‡Û"šŒÉéTÑFшèšz+@QãnÎnîfú‚@#ÀÆዘöÓ'ÿ+0ð³ø§yöR²c+Lgæ7Ù –<£ ‹p’ÿ8{')Š‹‰0"»9§RQ5ØR7å×¹õçZô#-uÐHm¸É4檱—4Us)„‘4NÙ,V–ÕçØçÊJÞ€I^‘Ìëù8ûÛ›7IÈ¡‚¦ñ½‡ Œ£žÚq×1XÒ*4¢wn>6î0Dܦ°½aØU{®¹rÜ~tÔ @"×&I”ŸÀñ›(¼þiƒ
+€D6ÔÒkŠÓˆ¨×˜<d]oUå÷…çl)![|v aÈ*ëµJû­IçêaÂ0‰ØTžˆ$J9CP<šáWɆNJü:Y¸=oM¨—u›yçm² gO½#]š·›NA&ãR ïþl·$©ùˆ8TÏd9GÒ·á,ñÿB#—,mg?±²Uûé©’Ap‚¢PÎŽ’NSÐT 7s¶ã¢ÚÜPíkVïÄÑ&Ðîa}‹R¶ ßœ@ñy¥»ö‡Mö_˜½—´ŽFÕ‘tZŒAЛʕn3µN›=WLî«õ¾pÝÏ»Ž˜3(!ƒ1MÒ4lÔÉÁOL,x˜]ÀÀ$0>,oÒÐ:iq›×F&MNøFWŽ·üÚèÒ·ٙǢCI¸J ŠØ*Ê´«,^…Ó¯înSô/Ã@;@ˆ§Äë/ ÀÓ£ð›ÕÔ¿­ÄïQùýJ¿_îëP´w(:ƒÅ¾â€·Òóýƒ¿-ÍC¶õâyìˆrOüc¨¦§KÕ(j÷(»‚å½.…;ÀÒŽøÄ£€¹U>˜(Å)oPñÃÖH#Nek燧]…nÄ’oÍD3I†3>Å ÷†XtXˆ"Ap{±4]F^Ò×ã!Ø…e!`G€®ÀXàØÜâò«Å…¹h—¿]–*òá lp£)EÞ•ÓÉgÞ/þ½Üð§âú™ýº™}ÚrëFÑÈLîÎí—Æ›…Æo÷Z¾ÈÕ‹I
+£pH „(~ëÆïbK/¹ëÅü|t­*òßP^z÷âÅ©S§’¹8… 1”‘§eM~,¾\šQl?ô¶èbÇXÑŽ‰âm“ª÷/¨eÀ9Eú¤R6U"»x(cJ“1Vš9¢–«3.¨ÄS…Ió‹ZkÈ=RÍ« P‹  ‘#«ªª=z‹½Äø™|CðXeOâÛ¯„B!ˆo8Žc„L¢5 ÕõãÞš;Èzݘ«æ¼Ó8á8±n2Žyw–äôBbäôôôóçÏ_­íõ»³¦ÂÒþŠ{q2a[K±žá <KF`¶÷õW:jFZ­ÃîÕhd5;:é¬jlóVÚuL:ƒ&𠌇bE¤Udu[RúÌT¿.%d˜SÎÔŠ-Â~¼L°¦ X@Ê¡.´­·Ú¦‡`^dÈ<Ѹ¥õ) Œæ"$Šòs· Ïè’GNÀ߼款zæœUÒgKë6§?EœÕ§L5À›ÔÚÒM ’—Ä%ÓvfÁ˜’.ƒ(l¤Ý&p‡¶ÐC&Á²ƒÐgÁµ”nƒ´×"é1§õ[$á`!°fÿt™6¸'tö)[[¤®åšÎùËúÀ Å^wð£ìÌí@š—È娢 "’4‘ˆðx<.\5Š
+!²–Æ%„
+g²4’"ÊH@v(Z¤˜ Š;„Fã`äƒ žAá¶Q¯­ÌdµBu=x.//C<‡g„b<„¤\@²ÒQâ0çbIýíRÛçæÛ…†û¦å÷ 7‹õ àÇ­"8äv‰à
+PäN¹õ7r[Aòeþ¶Âv«Ä´\aú´\ÿ„ÇEpºqãÆæ%w}Þl{¾Öâ/vg#§WWk8Û&„
+‚‘ªÍÈœÙ/YPIÉÒgA—ºP#ž¯ÏhÄSêôXUæ\uƬ.gAÇ_PŠ•Ò˜R:Q•j/`J!œN#™ôTç£x*—;22²6úÄËÊÊÊúàÿíÿOÚßSíálj„Ë`¡Û”€]’{|ÈÝxî¤~ªëÐX›a¼Ã<å6ºÝ6œ…Âhá+,*ÞXí—N^·}ób›Ö“x}¾ú«å_b(>A’BÜ2è4Ä|GÏ»ô£¯­ÛpgCôdÔ»q.xlÌ}ÈÝÄ–rp„®Þ#ékŸsç¬Â¡f€‡x°I2`Ÿ1ËÎX€ëTØêl+°d Ø2£Íi}VщZ²ø;(Ÿ½mÛ;üê÷¥C HðGì¼~Sv´El åË ²ÀX$¤'HU ¨0xnÉ{‚@ƒD[,$¨Tœý.ÂÈê·ã Yc`"‚~³`È&q$ÈqÆ $í·r¢‰>„Àe0 àøäMÛã[ ‘E‹gÚ´–…MȼCÆö<<I,¸¯¨$_vø{åázÛÔq?ð`Ñ៴¸z6W¦ç ¶£€F:
+Ö@Š²vMغiÿÈè¹hñ.Y<óK‹Æ`
+û•>¯ÚÓ¦é˜ôO?ùÃSªà€GñgOŸÅï~öÛΪ®îªH¯¶ÛUÖÒy’¥‘påi˜‹_ëû~~-•‚ã(ƒÃa×®[]kAróÛ$¶'OžÄ_Ävø—«W¯fåí‚ÃJa¢t:G( ÙMgµfìýi…égróçÅÆ{ÅÖûŽ›rËÍRÃÝ#hXPpÀy²4r«ÌrOnÿy©áŽÜv\¶›@kŒ¥Õj㛲a¶ÿ™¶¾)›ççŸ^ÿ ›@°˜8Êcìî? šW æ
+“¥éŒN«Î˜©ÊœÖdÍh3cÚ´µdRÅúPç;ÎëÒ=ÅœŠ†¾C TMÌÇPÐÁhkm}ôèQ"›¯&4òÒÝ߬oýæn¯\¹ÂA¹ŒbÐè(…UÔiZƼ çÇÜ 1wýX§}Ô»[]@â—‰‚FFGÆ`I“ò”ý cVãÏV ž‰ï«®m¢ `#´<Åwcô·~ÂixMŠ$42â´Nyê&]?ˆ¹ë¦<Q秓Q`òT”%ù6 W¦÷;xgmëùZ8l“@²†r4oë5"v¤‚ÎZxÃ6éxkZÄ ¨“{wˆ½õ²é6jÀC'ìì³¥¨òèôFâŸIÀ] &"Ú–ØD*
+#p:4ÊËáï:©8¨
+`Úl•õpªv+€@ØæË{
+R…S"F$¥ˆ™Šò#É'µHªR$—€^ƒÂë
+BLáx4@&LjÊÔÑmG§v›ŸdZgÛ§öšîiax8d¿ùÉAk¤häó}e¿ÝmxzÐ^•´—™Á0Á ½#ˆ?ü:«åûÿ}„½"‘áÁäBhñŸýRŽR4.þ‚Pb©ÍºôÉÂHÑȦáÜ”qj\#;p]›r³(uð°úJžúd·d^ŽI•Q2ÉDK±Œ›®ß¸!°zhiZ8YBØK ßý\Ẓ£ˆçÏŸ‡o©ÿÛógé[ÑTLR…K·¨Ù6téqrþƒ†¢( JÓb9&7ô*Rac_mõþ…¹y8;`ÃC §Àó𹾇¬Û_’Çö8(és¾œæ#®ÒÞKìGt˜úk‹»«Ìc¾â6Gⶔh *ÇIE*Â1b{JŠË¸©-!¯Zà&ñ
+T×Å­µÚnl³%uÚ“;ËÁb¯°t ¸(íê¹ØÁó 2ÕU{\›EÑfIo¶­£Qy4
+g™LKÜÜlg†?¦‚vªÝ¢ºx
+—3ŠŽ£ ,õǧ”DžÔÂ2»A w±ÉÄà”€–-cIÌŽDµ¥
+D„v—Xƒ¦Kžã¦di*¡¥·Ý£Þì<aba×å>ø/¸
+4Å´Îäx‡0øA’$ÎH†Æ°\´nž¡Nª.a:
+•„aRT‚£2%ɳÓójóœ,wKî倶 Ä¥q„
+ìØ/óç&Î3ŽÿNˆ-iµ÷®V—%ë0ÛÛK¶$;ŽC‹Á²%Y²d0ø”o|`Ç'‡ï3 ¤þÆ´3)%¡C)G[Úédš63iI É[’åí³VPÒ¤%qŽ™vçÕ»’ö}ßç=¾ßσƒÎ#¸4†’håL…n˵¬ò+é%@# x¸á¹‘æ\+ùSjéål÷k ©¸!‚3 ³'ïÅÐâbd~¢f½æN÷Ý^Ÿ‡«È£m8üö/ÆÒ j$*£:‰j(­h$a¢@;žk˜~!i²@_—ŽïVˆe1´8%hVJËÀ]I¼Äëúýï cZ´\˜_^=]!8IáOácu;Ecù<™|ýùá®~t¢ ‹™Ù‘Ì]iÀÏ*L…–ààw€8!f)aQLFQÉÉÉáÏ û_­EÇü‡ºÂ—WßyW.ã S#X%ãj<t`à¨c¶ùGï¸Fëí§kž”FàE(ö3ÇJFkì#ÕÎñ:ûÉ*ÀçéúŠñ–¢þ£›­i´TD"bŽ$Õbì)#§Úo2v”Å xÕýå@`…€ß´ÛÊûÜì™
+²ße¬Ôu»á‘,×ö{?˜Ó>y+~¸’é+Ó÷û”e6† )d¨R©Ô;·0;µ¦
+–³î
+3 ¯ì•åïD5
+…”ÜC¬Ã%2» úbûÝõ÷E¾w42å¨ÆPV?m?:[Z=í¨˜+©t‰—P(C‰h‚I+²
+&ÊjÀ©Ïí?èØ
+®¾ó¤áF9!Úô(ÔWB|0$ÀÙ²ÐÇž?wá'Ï¥¤ òI8%•À.•a(‹‘„#Y„ÄÄ,*¥©Œ¡Ú[Wçvx„="»¸<¸¼ŽLs(ôX%$@{PøJ8\‚0ÃA4|ø£¿ÿm ½CŽ¨f&1RIsùÞ¢¡*Çh}éH@Ü¡<)Š|I)­-­)÷—×=·/[Ä¢$‚Äã,ƒ¡Ìß•Ø]îÜSjðp¯—‚Gk|ª>O´"ëu} ”¢ô*z4‚ºº× ¬¢îtþ°2•³Ç‚‘á± }!–Ò,+"0AŸJT
+'CÄ¥f¹q.­èzºûæçïv{ßM/þ•Éq#Ë{˜dgÙ]îk;]·2Êogz¯¦–~uÜ4ùn¤¹nî*»žéþMºóNºóöó®«YÎ[é>a›pd!^̱,Ã\ü/€Ç¿»–=~¸òó·!BÀi"—h;3æ^ÐŒf&ÎXu£Ùúñ<Ý´Í0c1ÎÚâƲ´fý4ÔóÖÏäé', “6Ø~ÒÍ[çlÉã–Ä3&Ý”U>kÛ>P¨)Þ$Ò‹I0r0nZFIP‚étº‘‘‘ˆeG÷Id¿---E–#òÙ]!á4ño]üé¦)¿½q[ØÀð¼´ü•®øg••0t³
+M·Gq­ë«Ðµ8ÝõëÍ /~À—\ï@’u±®Âh h9A«2NJ¡(*Ù¬{¥Ð8øOöë=¨©ì
+¢[ò~bWy¢‚,‘÷[]ÿèLÿén;îè*¸kw}ìÎìhwÛé:³uUH é—Ä¡ÖÖZwÖqºã™CæÜsî9÷žû}¿³÷•Fþ{2Ú«‹ÊèhŒCR†Åá¼Æ"tŽkkGµ5@‘}í¨±î¸ñÀ&ñJ@J2‚Y˜¸qÐR7P1Ö1;ƒSÛã:RbÙ«*®È)ñ¼e>i¬ž´8A/k^½eUgöx†´Õƒ»öOêÆõv脹Lš]Ŧ â|”€D#âò Þf (òÖ¦ç–ú¦Ü†&eT—ÒíÈv=o–we7¸s<n•«9·Ù£ôÜÑô—«_AðiÌÝšs¨p¶µ²íÛ™Ù`ñùƒû+o0
+19«ù –4PÄï®I¯ƒ4ÝœsDÑ v4h*g“º 4R£®™hf,À߃ÀéƱŽ}íþ™`lñÏ~è®<qØÔ|éÜ¥ÀÝ@àëÀñ·ÃOê•Ž®¥»9ï[åk¹óÆ4ýFá:)KD°Ɇ^OØ/àÆÁ©5‰«Íå†ÖŽÃ¦Îߟ½·܃.øüÁ=ipÇæÿgÀ‡Ð'bø‰cQ&á¿ÿÙÙ@²à›‡k “@#ÅÍ&Áç!8‰8e)€.–Ï’Œ7’‚9Ÿi¸œa¼™¤»‘¬›R蟦‘ †kɆ›MW’´7ÖË›u—Ó ×6K¤o@H]‹„Cæä
+6 »,p×ö³0P$¨‘v‹¤Ã
+4"h5?»‚m€·ËL÷V`öbR½åà’JaOqT'9Q#¤³×sQ ”"`’Ùqd‹>æx¹ Ã&}Ç&íª
+:9fCU†­Yãq«œ¥Û­>’µ;ËÙ¤r=o–oP5¸s<.µË‘鄶+×mßn÷~5ûÀ ®7§n~ùÙ—ðVç^àì;¿±4h@ U¹UæŽúûAMScVã‹ÖHÐZ*7\Ý• èò4¸“U¹óÍíRRL384‹â¡$Ü+ÐÂdPàXÌ°–QœÆ9kV¬.)ØÖq¸óâ{ïÍü=ì„°IwÈììl¸±˜;ïôz½ÿ¾Ïý¾ù9¾y¸Ä¿}}äÐa&Nb<¢1xROœLe‹ÚUð‘Âôa–i:Ãôy’ñó Ú†)¥q:µôi™R¯&ë0о’i¾²®ôšÂzzã6!^ ç<ÿãû€ ÌÎûÞçMk?
+„Á?þøWmZ:`¨¥‘)Õº»Ötô=å"0+è™øæßæx& žøCk††×¹]ä®\DD‹DBŽAŠ–Keï]¿½2Ÿ¡ôÏÐûbdÙ¡©Ù|m¾Œ`i!"ãB€Žá8JQ„T#Ë9¼Ç:ê*ŸtÙö8<r
+Z@¨Ñ¯Ÿ'ŒÔWžn, ™h¨˜r–Ÿn<<Öhrò7jÒR8—‰1V|I¬S°LA†´ß
+0 $èå€gƒÁj¹ßú±&P ­Òoã'éC&‰¬äô™•U šÄÞ*Æ^’Ümƒ‰ÁRj¥½fάçHR*BAa°-q›½–Ø`fè(¬Á¬juBoUl Zí5ˆT¬‡ì– $,§Û®j®P ×ÑÁêä6+±5Î+N"‘ø#¹w½hq]04uŽòW˜d(ŠÂ…âc…gë¼cGì挂íŠDH70%(Xñ-E‚cŸñ\­—G“Ö4ÂSGgÒÔ8m:uΡ«cºš€/k]çŽxf§.˜g+N#õê´)ÏKA>± ,K²É%’h·5ä7uå··f{<Yž¶‚ζân‡ÎëÔzZó½Þ\çýº|›Úf—ÞíÊs{µ­Å'´õoN½*³^UYS…”çãð¥KN½»#·«]ÛÑYÔÙ
+ £sµ¶zs¼ðËæx‘3Ë4ÒUÐÔ‹m/juç»›ö5L;˜.ß¡©˜ ´œ”p4 –‘Ì4‹Q 2$><™°$yP^Ž¶áxý¥ï¿²psÔuiaÚ¨€ß©ç÷Vû»=ú}^½$óŸÜ|ÿ2(—¢IP$ŠK7¢2° ™/‹›Í.ý¶ö
+pÅ.ËõÝÖ+éå—³Mw£‘_eZßÎ4¿³³Päµóµíæw³kËã·‘(Ê’$nIA ã|äìÂK÷ZÅÿrYåñy)r¢p"7®]g0LNÑRBÆJÐxwÖ¦¹=‰gò¥ÓùI3ÅQVÊ&QOèUcºM“E©S/ÐSE[B/m:–!NFPñZ%FÊP–B9ŠCUq\GwË
+¼$zAxú óèûïˆ(DÃì'?~ssr
+K3ÀË.ip #™õ­@¥ê„#D¾Î<´;#b ²ÌÀ!‹ãŒ˜AXqñ¡¯wv
+úÇžèûª/ôdï¬ 0#œz<0þTÿØcþ‰Çý³_é9/è™y¬d]©AˆÓ2t=D,Ì€äòÛ¿à/ËòÊ¿ß—hä_½zÕår)4ÈàÞq8ÛN£(†Ä ®Œ“˜öY–¨£b¤Á0ÖXvºÞxÚq›
+Ô5
+$ªÀ‘hUôÛT~[¬¿J dâ·r
+¸<B’ ÃÒb,’z^4MÕ¸a9,.hÏç¬Nðý©‡
+â™@DhøˆáëáÞáb”!(¨Ð?°¿äõW_‹f| ¥à&óóæÞ‚Šp$Ñ‹vncÆíGÿ±¬„?µ$þ+ »Ê›# ôÊ«?ÒgkA€”0 ’c„J(6&¦ý §ò×ÖwÒM¿Í¶½›QyWù¶ñr–õ—»Ì?ϱ½¾Ût#³öâöR ¥0L‚á*Œ¹òÆ[YYŽÎæÿ²¬,,ó›°¸›°9Ž›B†PP¢E–lÛP±znbºpÓ”^=™›*L8S7]7W¬šÔ'Œ<zak /¾!Wœ)Ûˆ?ËlˆQ#ŒÒI%¯³Ú>úèÏ7à @#áå¥ðüRxaq%²Ýpâ ·¦±²r›K÷ÞûÚœ\È’xi")'@¦¾†Ë×fé…û7°‰
+ú'‚¡ 0!èy¼gôéžñ§»‡×t?9ð=oF0>ö ‹+AcÁ
+凿ÿàVHó/[öç~öÓ·ŽØª29"ØOã$ŽÂ~¡b–¸{[ñÑÒ£ƒvÓP£aÂQ:ÞT>Ú`kªž4‡œ@&Qö
+Ï‘F,ÃðɲáúŠñ&ã„^}x´¡rÒaª7L;Ë&ìY潜ZŠ¢b–¤9 ‘˜<5Va-ŠóYÁâk
+ÈÆl2€‚$µVAoóXÔ50>üM¼/§ðøŠbÏŠ„Ç‚×`, Š„‘çO’~D#÷®à×€3f{}^‰ £Ä(*ÁÿÉ~™µq_q\i“´™NÄ]«Õîj…Œ@§ë£®Û1—@HB`ðE|`,¬qÔõÄè¾vµ¸Œl0â0‡€8™üÑÌôδnS§š4q2Nÿ©SROíqgZcNõ-
+43=ÃL<žiòӛ߮v÷ýÞoßï}??À&_*@å„–
+¦§§ïß¿¿VÞ“µ} Q’§kò Wnþ<ÆÀ^Nçàox²§¿/G™IËy
+k_ÄçoF ÿ˯—™ÿPl¾¡~$ü±°á–Ú2]·Y>Üg½^b>š¹-U˜‚að*Q‹É
+.`Ø0–‹æNÿÏ â^`¿ÂÜû©fçæþvû¯
+fL5¸Þ²‡ìc­›'Þà|Óè¶y†­.xU¼Ù?fõŒ48€OâO/.˜íË'zhG¡L
+
+”ÓTu¨­ÒÞPdÚ¿½º$»d³*€D‚ˆ¡` @¡ÒA£Ñx÷îÝÄ*H¬!G$¹råJòtvvöóÅÿÛÒªÃü÷δYX^
+ÃË+Ž’xùøHɬ[Z^I¢ÙÖ„à›d¼¥‚Ü]åœHŒsqüÑ¡ç»ûŸ¹8².ãœnˆ²$(W&I•ˆD5ºC,ç.Ïýùö'•†j9š&M§ p(ȸ/ÁÑø†¬Wò ͵Æî×@úƒ S1Çz)¢®×GÓ€ 襾ß~jÐ~rØ<s|À^sXc®æ˜§ñŠÝÔwîôÓxÕy|Ð[7ì;ÚÎ<è¶Åœõ=g†¼ÇGüz¿¥ß Øs|ÀylÀ ƒ|5ܲ¥r÷w±B僺
+¤ñ„"E«vg]l’ô7g{êT'´ÂYˆ BŒÄ·©²¿¦‘ÇÃ5“çms vâì‘—ÕH
+O&““£PØQb<·Y];Ý6iõu\3{ŸÖ8'Mn˜Ã «—E³+nóM6
+EGGÇíÛ·Ù‘aazWFfú+ì\f¸ ?Žå0é½‡Ë Ëó¿üUn~ž)R
+–w®\‘×Wás•8m…ÉÙʬœäìƒÒ·Nî;AW{\%n¨–èÅ6XºÄÓZáÝè¼@ð«”vT;­%Öò½eo3‰Œ'€‡¹Hjbl|u‡Ï¯(–IÄx"GD‚0¾Wóù?>d´²Ò—V6’-®~]M«µ YU
+“’KË?ýÉ|Ρ,”Ëc¤ŒªaLâð/¤þY¶öw¹–ëùæëùõeëÿkþ$OÿóçoØ~›chÛY˜ÊÁ…B!dAFFØfÝì«Aùx¬.ûòštD()æ2%-áàøñÌ1åt•b¬jW \xP&
+Tº‚Š)>AææÍ›pîƒV‡b‡[¥ó§Üg¨ºü§ÏoÕÖš¥ ¤
+çråœ×üWqŒâ¼•ûÒömá‘çº'h£³[úâHï$Ò?õBO éôð%2\„b‰†ŽÄØÁYJý“¶ÿêXZ •Å%¨Äf±
+xP¿@£ß.E#[C[zF‘ðÒ3ºQ ñ­ÝÝ;rK LŠÊIìÛ¯(ø$
+W ÁñE(ª8~DßÕd‰zôÑ„@Ê:kã.mÌœ0FZ6ª‘šHxC;b
+‘ ÈS“Åîb:墠?¿Í_€Œ‡¬ ’Ýï«ò·Vú<•7´*7“Ý+˜
+¸XNêÏkï(í‚ucV£ÒG£­GmÇ_?žÌWÂ\àØÀQ‚ÛôAœÍMÏqqžþà¾ÀH §p’‡fªÓ;‚—îܹÃFÀâšö÷…Ö'p.›ƒÐg;ë²c5[ØA Ά#Ñ×SÓ 50Oš˜€8Õº¿è×E¶ë?;bùì¨ùz¶æFQÃ,ã ŒY’$žƒî\¯×»6¹þ»ÿ_:Öj„ýàøòÞ*¹JŠ+J&ʼnӻ3GßÛÛ]™~bïËÒW)gÁ•S\J$¢ög훟ŸgGcv•”,AÙÎâ£Çþ>‚×/¾ºçë ŠQ‚ÜÎI–* °•ü¨D oä¾h²n@ǑОFÂHGéBú&Pì¹0$~äÅZÁ“Èð"J˜¤Tݽ}‡ü?;Ø+e®ziùö—¥R*–H˜Ÿ²ÍéC†fþi¤w Eaö kdpjKxòùöwO.ŽbŒ#‰DJA ù7QŽî'Gl<?ÐÇܧ†5Ãv Hݘ[3N×9µ1hĵoT#@pEÝp Œ
+$bg(=îÛ$MaR Á0Aá"R±,FŒÚ»{ÏÞ| ¸¿°¸
+×»&·'|ËîMQ"°'zºº¡f>Ë#oÔj]æîY
+ú¼#¨P5Ä QáÎýÈ¥
+(¯7ÕëÀjBQ}ù<؈jtm*(ý†šQÝq¿îø˜±zÌXë7WûŒÕ¾vÕˆñÄiúgo|t÷ãÁŹO> ÍñÂÇÁÿôüx¬!`=6Úþý¡sj¿î\Õvüœð@Û”—Û•P2Ê¥
+˜M¢¸lù>åË psweGÁª@wäv:ÊÆC†® /3™
+Íæ\k÷—€/ŒîÒn}ŽÁZb³—ÛUÙ*yT…ÃJ'Á
+°%Œ
+¾ŸðÆJ£"cd2\^Rþ¼Ý¹¡ožß@\þuüë܈gqÏ"}3aÓˆÛÿM×ø3}ÓÈÀdpt£FË¢q0ûðúÅÉb?üÃùOV¾mü˶0ÿàå—2%b´V"olnA\“È¥ka½#È ÿ¹ 0×äZmäÙþ™u®Y¤k ñú_¨©§éMÉH%b1]Ó¦=pj®ó·Õ·Ô\jSNØ´^£ú²AíÕ©Fô` >}íH{õå–µÚˆÒËiLݨþ¸ßpÔ§;æÕ×z*¯åôHÇÕ÷~t;xçAp~iá>ô–Ph ÎŒ³î#ƒ- 3˜ØHÍàêy}mu>¤¨÷¶CÔ@#}úc£úê‘Vͨ±Þ«WÙ*uÊÍÛ’%Ba ŠÃb“R°[šžº2ÙÝ$÷4I¼'Ÿº~<›<MqC'â=Iýr—„$´¤_ا•÷ká*œ2nUÚÐé]NmRuqêž bW
+‘‘“³ƒÐËGÏB%[\M_ÛÈ“# 6L© W´æ×­ Óõ:Àýk ÆÉFS@Ë9À˜²<dFcšÕ˜žb;§´&°0ÎC”†‰º6hùL³m¶Ñh>T·S¾™„w›fÁÄ"ZB°2œÙ)Ýz2¯ÉQÑc+u9ËÜÎÂŽ ûm ¼
+«¥Âj,4Û÷;zJ/X Ìÿ+±åÙ:KPÿEó6fk'¡ÅÐZ–fWþèo‡o˯~þ y\‚ "JÆJÅQD½„ÁQé°æÛ»Šz`Ï«ÚšÂTl´°ÙJÁI,æ" È #ÔÑêÚ*ZSˆäX\ÂâÀz\LÒðIOaXwåÑ9†~ÝÔV±%EB Ę ¥%"Ú ëå\kë»w¹áC
+…`@X<M‘ÂhcRÖlnm]âNFÄ`-eçæBjžDÁ
+ÄËÏšðý³­ÂåAà
+æç¾óËw‚–ô
+"cq2†’ŠàÃV¼yów! yBÛ£µñ¦ä¹’„?ø§Ös-œ7Š0x[„4IR",6rGκ³-ˆÇƒ øÀCžïôq2<‰\œ@ú!&Ï4rq6l
+=rRežn2袜U1ÓË9­æøU``µü<«µL6Á@‘HƒP$rØÐjß,̸&phiIœ/åKR1!hqÕ¶wÌïX<•.ËnÆRlgdG™ÝUb±Ë‘ì&™P$‰%Öb³MfuV;˜jSíÎ}ùdÿ5‚ñ„‡sHFAsEò y# ö ¡iŠÏÇ(6ÿ éõn½ê©ªí)u;ËŒÜ(b/qwïéqÈf™éyшó-—³ÒyXÖþýÔ­ˆÇ"œ‹ËÃà1(61$·nü¦`Ófã
+(LÄá•Ète' î2/ÁVn_ñ¾á³µÔâ®pÚ¡EJlîr·£È GK…¥]Þš•’™†‰pœ-àãÕÒœ§?®óÌ·~lu÷×Ó’,\˜Æ%E8pð¼¼¼Á¾þÄ|"‰‹‹šÙåÇ/
+%ÒµXéÒÉØò{–Þ>ÑçìÜJgBSÆåa!‘ùëy;Éô. ‰ã©”èÚW“:['×LŠæcúÿsss"ü?6CWÆ V Z•ee<ŠKx:!Ü Lݾ}Ë»ÏÏ'â ~ö9ˆ¬ñ$Ì$qqiÁ™ûÿö¸Ü¹Ù9<œBÕ$íç`¢”ì‚ï¨[¾10¤ñbÿ4¢ŽþB‘¾ñ%™Zã›Z{jìÅžaÖ€ŸÕfùÇYÁ VOäß9`•u}£¯´ëó)‡¯ëÒ{“_ȳGÒ+Hý…sçCHú2_–—¾Úãg….°¼pcuŽ²¦Yþ•ÓH Ä:d L²úÏ|ëTèÛmíRÓ%ØkRžˆ¿‘VLõgŒF:šCzõ˜íЈmØx ¬MoÕ7"Å?‰€d…4´ 
+•£†¦aƒó½¡ÿvó“ÅOç!?Qxãyø{ûî_Þ½rù?‰x ñIâÒ¯®6õUc¶†‘“ð *Ì
+w6›F͈±.lÚ?Á(ÂfEÀ ³6Ž0{Ì
+‰†z
+å•$É4JIÇ·ç¥uì[uüxÂrú´é6éèáŒ>µ´O-´ýê[ê€FÒ¯ÞhO;ÕÄ’ëk÷5ÓÃZjX“׫ÝÔÛ’Õ§Éô*³[U²:›¾¦‘gÛ´ÂxFi
+¯_¿žÄ€¤Š}KÓÔR4G¯‰:ßÅyt|¬}p}>Žªë½Oï·;*$ˆ,ž@ò]&àQSQ–9_Hî?p
+2ºŒ=ëoròõX>’ñ¿sçŽ@ Àq<##Ãét~>; ÉH~t`±yHF® ¼L$f‰Ï!?èa¸<7Ÿxøþ¥+?xc'—³.ƒÂÅÙ¤D"a5œ|…ñ½ê;0c ÌŒ
+VW¾ôöÕ ‘/›E4 @ѹf…ë!¢ÐO¨MZfBuüŒÆ8©±œm¶ûëNØQ,¥H¶ˆ+à³ùd
+%æñy¤„›^]ð60FWQçÓÔÓ*w˜J­ž¯µÒÊÈÍî
+—³Äî•y¬•&m±º€ÞLœ„ÆŒ )$Ž555W¯^M,+\IÕ™ûûÝ{{«ªq.€‹Ú®4ZBSBåîfÛ^‡­Øá.w¸jL)ã”yŸu
+Nq &‹Šæ‰$‘²®ñã»&fç¡„"À@"·¸\>¢±Ø†,$ž2ÓK’Onܸ±oß>PÏíÒÜ|Qº„ý_ö˨©ûàOѶÛuvm%^’—¼¼Š¶jѺbµÖ¢ü©tvzò?„Z×
+!I ˆ !ŠÓy®»ºr¶uëõvkÏs·»µ®w›[¯þåD@·ïËÃè\]ÇÞæï¾Gò ¼|ß÷÷¾ßÏç d„öÂ_ÿFss.qý¿¼"Š?U*Ür.\ nIÈ]Áø mÃã4éÂ~xöܹ¼¼¼±f“„D@BW³d¢¸èÍÛÌOš;µúoó!ŽÃˆËG«ˆÃ?ÏÞ7[ú/nhæ&¾ {v1*`Ų‚ÝEÔuƒºI§31 ÒÜe~RÌ5§³¾9\õüJ’ýŒãqI9¨È…m}-Hà½E«o¶ù Î~¤µs!(³g¾½ |æ©Ò·%<T ¸À/ån*v×ïñiË}ZevmÄÿ»“ÁC`ã@µÆ&i%¡­„:9ø~•³¾Ä§Qz4'.ÓJ?E¿ò—·:tE^]aŸ¡Ä«­ìR+<ªRïÜÙQ{}µ[ŸWõ¦49–Ï瀌alT*"á“X·¤2WÞ]#t•Kl%Ë:ß Ir¿%i)¼.wï[ÚR‘ÔR‘à®!Ü•šõ<²‘û P‘~…æH…n B‡Ç+ ¿Pêú‹êŽTj*4¾2Õ¾ôŸ¦,I†žÅx)+†9ufa/Ê_Pl-×ä©u™
+¥"¢©Á4=9ã6ÿ¥ÿV¤A¨;€N…«Í˜0.8´OøÓÓá€_Q—/]yw-‘b.æD“àÿT-æ¬ÏX¨Ò¬èXÐî›×æ¥UÄé߈rõG9úsöÏšþmþ§J«—byÂgXbEɾÀ¸˜¬"I2o¦Ã/“tóLzæ3¾H#`ÉâEëRs€Š¸zÁ"hpöF9g—ÌŒ¸<ó:üˆ£iñF9ž0Äé[]€ ±¤—ž) ŠºêJ<u{ºëæŠþu–¶,Ø‚‰ìÂt0¼Az¸]›s|Ø_äV·òÓ‚2š ¨¾³Rï:¬)ìÕºk•ª²Cµe=s“ m#~C‘§¾Æk*4Ö¬|m &¥/ÎãÊ¢8Ž³<~ʲCaÒ់[ŠÉ¥´U‘ìÙŸØ^kWÄ·ï?‘9”I5šõ<²‘û pÚC*t¥Æ_ª(×W莕¨*t–Eþ‹ä@6@›'ð%8Žq‰84¾ e».·VŸ®2dÌ;lµÚ{ѳ1ÛdɱÓŒ¶¬ƒY}NC}žöU;¥,Y<RŽ ðT\¸çêå+T_Ъ· äÙg†-À+Dù}½Nk‘ˆÁVVÔf°l3k7káë€ÔðEse#úlCC†Áœa¶¥ÛÓL Rúízå+•‰üx9_*âò Bøò+©—®^¢çÌÔ¿ÌØ;yá´º ×ÅbŒà‹Rc×w@Ÿ¬¶YÛQ†‘¾Àì&ZE2Má
+ÓfbÙÚÜX`ݾ:WŒÉ0T Å…ñBô7eû‡ôÖ«u¦kï6€ŠLhl`#Ãjó5uÓE­i\g îo
+Õ·~£i±f篕1$Ã\Šãâržºú'¿~ÿÔxpjÎTžY-¹½I÷XÓá511ÁŽŽŽÒU‚›
+}úÉoÇÇnB¹îÄë£õŸWävš‘[{ú7- «bh˜š B¹CÓvKS‚\ÎG¹á!)‘8γWþ¨¸ú‰V?â:NóºË‡€Š@8½@ðùN?ÒêC콈cöôo?†¸º¿š+ãóQ1KÆ!—þãË?R·Ú™É?òÄW¿ Ú:]´ˆ+æ‹9䢢2°ÄéC\>¤Ýtà²Zà
+Ì:W
+;U{æÎF~v¸¶Ø¯Ý*ëÖÔx ¹Õ;cWÅ¡$'CÄ°E„T
+ôŒ‘Ëw¤Åµ”É<{Éîš{yâAår÷¾ØÎÂ] B²ÂYý YÿðÇ#¹Oé/QAUЇ}Ú¾*¿J«Ü·J,Ç8,LÈòÙ¡P‚“(_·±bK•>WoØbhβ™2Lª4uÃó=š (o´e6Ӹ̲€Š¬{#M$D* a·PŽÇÝ3DOÍÛãÆPì.-aÆ,`ñ“>^š˜$`ŽláƘ—MyFH´¾E—Þ0W6¢ÍÒ›s-iMæ4«yk“q»qoæÞ2F:Œ2 C¥rÉW_ÿyj*†ìm‰ ˜™½£G—.IŠ $±¼å–r8çló÷
+¯H©™0Ž?}zpýÚûÇä’%`ËDD"Æ?™û&bjE:ÚôFuøgˆß˜ß˜çèEì=ˆÃG¼½wÖôoóG¹9¿Å͉žxúi¹”„~y}{>“í×Âô ä
+Ò´iý«pÿá<6‹/_d´Òù¸Â9ÀHÏÑYAn³Î\‹±g?m#-È¡žèKÀÓPÜš¥ŠNí®n•Â«QÖ”Î!ý»ê*;5ï}vÂóQ™G½ÛS§ìù'ûåÛÔyÆñCl­ºJƒÇ>wß’k®©2Z P ƒ8Î¥¤¥1Hâ\ÈR.N|;¾;qLÇqâø’2º0©Ò¤}ÙÔ¡Ñh›¦îËVuK'˜J !äæxωƒM£"(|È«G¯Î±||þÏûúyþ¿×`éú. u4ûôha~*ÌÑ>Å?Æÿ]éÕTÙS}êò>
+  PèÖé¯?ûTåÂ,Çáe=*ÖW£½7êeKóô |ù‹?ÜÄQ expº‘mUíQZ‹m@#¶‚VÓ*¡«0€û·æ·™ZÚJvJsÅ<‘XÌð[€.nÜü<#
+A¬ë EQdç
+Ò;ô,4â llFÜÃ?º¨— Ói~’T@&BƒÁy K1å’âþXáÛ_#J¡-À÷·¼¾'Îéå „ÉáÍ"Š9U+Ãé鼂tù‘n`›!ÄÙ¿®ûÚ+’F"HÍÞó“ZŸÈ¡: ­êS—ùšWËý«¯èkúšÎxÕu=ªrŸªrH§ìo®ê½Ð~Ý{ïÁ·¨®ïÁ®ME¦¯ñ›Ó>MmP_ám© ²J¯®.dS «F#¾&øq·Bì ïEÈôLÈ /-16¦çdã8_(Bϧú[r"*Ù—³ý\izw#ÝS—ìix­³!óRmÚó÷ú?ÖhäYâÓzãeeóPµv¨Á`9V£ÈÙB2|>_LÐ$”*A PZB¥Þ‘wö`}[!k?liÉo1€éµqXrÈd?d|’{Ú60Ps¡ÅxÄ|<÷x† ƒÐ$‰“""5=ùË/ÿ χçæ9YˆÌ/ü—CbcÉË–ø¢cæÑ´ÕlI·€¿§Ð’]²Íù*ðh 38õ*Ñ€1Ï)´”´4).þ,ë (¬š@ ”b(¿tMMMF¢$µ°ÄK±±dÁ {62úõ(ÃÐÐxŠ@r¾àœuå:ꀎ¸‹b›©À¬Ïg­GlÆbã‡o¾/&Se‰„„!$ü„ßÕ^˜1^z 6N4Y£ñ@á ǽ&37³ÖZËC•iRc¹«³~«·N°-U–¶óŽ¾=øQÙ[¤P*"ù"2‰D)
+¸„Ü–ýZO·‡£àƹù'ybl×bóáÅ•š™›ƒg¹ÛåL²6žb,/éééH”O
+kûtå—MoUŠ¤”C¥$%†\ûq"/™fN¼·ÝÕ˜Ùóqr{MFG]vWãóöú?ÖhäYêj£±½â“Ò=‡2–À#MRRè(Nâ’’ܲ·Ë­…ÆVg‚-r‡£È ­Ëc
+¢Enq8žh ù­¹™=bRîWn#³©LLÓ-ÈÊHÿóÿÄ5%8ÌÎÇæèøð€ëYh\o£@øν»cY™´ˆD±->]ý^%«0˜å66ϲZ4b•·šä`úºæb•|ûaèêRŠCŠ¢ÜA/ φ£²¡†¹ÓN8²Ì‚cé„q¥ìçe„@˜H|˜ûPÄJõØ­QÀƒdMEfK‰åÜþsY¨§1!LEê}î³mc*ã´¶ÀbBkŸÐ·Þ×Ø¢(2mpN¨m€(5-“MÖImˤ©í¶ÖtOmÕ8^´ÿ­É¤—çËø›$$*ÃAb’˜fÀàöï{÷Æç¿_ò¾§±uˆn(‡1‹ŽÉÁçã-ž[c’§1À‹}²t /ÉÜÌl¯§';3‹Àp¨b(4™
+¨Í)™›O•¿ììDºBH×õ®ÄéE<ÈçSÌÚ=
+ËHŒ·xæBÉ ¦Ätº<¨H¨ èÊ!V‹F|lå ©¬ßpÒ§…Ûê~ƒ2¨?ДªÊû4öÿ8Gö\|5öp€Ê)¯ªz@_á,Ñõܸv'2qóï·VMO`qöªk‚,
+lfy‹Qî0+.™òÛ½Þl/6Û
+Œæ|£%ßÈñ$÷4ç9 …ÖOäç*Ê&Rš&p^F¦ô¯ÿa¿Üc›º¯8þ#iÍ:µ’øm_¿ó¦dZÐRn-y‘„°®H@ÈÛyò$$ñûýÈËöÄvb;Ž¶h£ÿŒn•Vu©ªÚQiÚ´¡µÕþkIJbãGìkSŠ&)ŒŸŽ®ÎýùúÞsν¿ß÷s>ùŒX†Éµšt/%vÔ(±A=)b“ú·R@’šI üeDcq§b9<&ó
+òø§‹OþlP]«–W)T5jÀe¥B[£Û¨zê+t²’7öÉǸ´’ã–}þùŸ7k»­®”SóÈ<ÁQí5­Ò*Ùx¹U]­IÙcÆ©*×èª ºZ=$ BÉ ÜAZ+?uð­Š´˜F#aóèÑÃÁ`ð>%ÇêXoÄã¸*–ˆ:tˆœKæÒ9Bÿå'áqÆãFíëz}<]«¨‘ÃQW¹nGŽitÇ5ª:µ¼R4rúÈ[B:Æfr)tJ)5÷º¨ó®T¿2 ^V wÅú ÙŠÄ°#5€³¢¹}UõÉéöƒÂ]T š&‹Îásh$ÒK%¥&½æö·ÿ‰%Ö I$ƒ ‰<ƒÉèÃõy
+H<¦ŽYq‹s°+ <3ÕÞ¥þËÒ°]%
+%>ºùÇëÿvì=×y§ªÇ¥èpÊšgÄmsòÍŠ§Û%n™hq‰â¦iÉÅ9m‹öÒërit!›Ñéàð™ì<*%[ˆ±_;ÀÕ4ïÃ/ï³öåÛ„æÎÂéÞ[wÞEpÞ[j»
+/‡s¤èpOU¤\¢«×iOè€FÔÕ*0 ‘¢è»èp[YˆQè&F'SýX,²Yûv0¸2p¥ŸEc`L‡Æ|­ìÀƒ±|ôñ9ä~œÕ8*+UŠj¥¦Ž˜QT(5•ºájÉ…Š‹¥y¥ùtBÙ³§leePäam}4Š$¾£‘h<êóù8,›Šñ)ìýÅûÔ'TÊr¥©jÌPi„"k굪*¥¾J¿^œ£Ç Šc2ÃÏ ð^4µú¾c—^á탾%p„D’¼QykX³*у} 6¼aY•—ź™häÎ
+Lšó×ÀöíOÚB þN_×È—×…êSø¯4:öû†“““I©”¦Ão¯@DŒ§ùÍ’Üü•v{„ÓºÞAŽ+ÈæCn?”éÕ­Q¶~Ô1°P5Ê9„àîAÔÝû”ÓiFŽËÈùÞŠKþg+.Šwîb¤´J„'ФŒ‘Š·ï{ºüÂjkrôFuúáªU—à^ÃÈ}9Ú=„.uE8½útûQ§µÑ.‘ÇIh9OÄñRj¯öGŸÿ}f˜{{ðmP.ä “ñ/D\r„ǹ8Š@Ààíèvz"\žðD@b£
+#1”šæ_Úª6œ
+¹J÷É'!Š?Ì™`pz¾’TVV2­ e°¯ÓÔûM¯AA¦}æ¦ÃV½ÖÆ©K77g5 ûYæÒ=¥,……ÇÈS‰4æ?^лš&jÇu–ûÕ˜qÑ«±@
+Íq ›ú|êû~pãOØ°™ÂE +ÿ>±º ä{öAäð£®áEk¤c
+¾‚œˆH¶jŽ·¼^æòúL¹½:
+ÖLëch$ûÅl®¤IŠæÈââbØP¦`§—*oOM=‚>ùî{,MÑ +)õœl[õ‘šÅŽ34ý¬p¹×ÍY‚ùhýÙÝgvàÛy’Qp<tN’øGýzœáC& †JÌÌìì­[·HœPò
+9Ŧ`êʃåÂáÐ"ƒ…@€8J»à:›YÌiæÖCVS†^»!]A(Y ‡Wn{þõö/jZÆ«­ MSµMU°X@€C@#`è@Gˆ/ M oKÆtM3:Ûƒ ­}Gs·$1±d,XHÆr”b%''_rÚam~URç[0¸dý;ß¾±VÁp MÎþóöç…%bJBb5Ã&ct|Œ”£1»v¯¨=ÑÝ逊<„º/#Û`¤{
+i ¡„Š”ºþ&õ† ¤`ÁÄJ†õàôÔäúNÛµðø_§¹ÆijgϘR
+]¾ü>ÆbQ!æð h/i Å&õ9-}Ѩƒù¼@#%4]@wÒòBÙaá.Ê‘\;ùÏUM tÏŒŒž×t<’éíÖE¹q^iØ(Eæeô¢º@2‰€d­Í6£4ÿSÛ1¯±Íµê)szë×­sñ¥lV" ÛMf>?SÔñ³§îÝ»«N¸o¨Ÿë»êæ½÷ÿˆ $Dgggm¦Î”½{’ø>… XœTT´‹-N:plWmënc÷OÀÎñWz&·uO ý~dp8ªg42èGÆ®‘èN°Á 4â ½#¯Ú§b¬cŒ(ºíñVwyœ†âûŽÄ^•"öÄ=²]ßwù#Ž(“HH"Åé±\‹ëö"ƒ“€`‰sé…|
+†,&3N¢|>WŒR¿È>­9§Z¬çÇãd}ÀÍÂW`$Vu²(Âcc”
+ ”™J 4
+6j\òJOKåðm°ÁĽ»à°ïWú~ãÛ4Š
+´íùhˆÆbZ—¯hÄÏhN†°[7ªFÀÂ
+4r±$Ì
+ÿPîÿõþGÅ—FQ‡â`<6P^<+·0N¥Eúýˆý×îÚÏ dÐ×5ÕéB\#Q]ã;¬¯WÜ€Š*à&“Rc ¦XÇ$bó!cQ67Ò7‚ô{b;¼;ä‹!Å ”ýaM¬s±»þM~™À´ußqü’,‰Tš6qñõüž1—!PÚ¬k²UU»4]ýe i—ñÁµ•Ã÷ó}pÙ~>
+³¦Vc>eÕVZOˆ,T€, ì­º0§¶.©l‹}¦e¥u…\PÙ—ú¬ëýsË]µêy­ÆÜQšÁæ5¶Û:Ûm‚êYTÛ–T`ðȜ¾UzYê1/kîhŒsz맄ÙSuú§Çesrx¹|:ïû¥eWÞûܮ˛ ë©õ‚ˆ&¡ âOl,'ép%%*ùûv”$šRå¡o‡ø¬$
+\¡KVìl†l/v«ˆóI YŽGÂ÷ˆóÆ;J|Ý0d=ß)-pÉ
+ÝEžÎCžÎoš1¾E4ìnÇÄš)©,*ÑDÄê¨HuY¦ 7ËCRÐòN³f¦I5#&&ÄÊžÚ7~Xr$åŽòØLe‚žfsá~Àx¹‡³JO>wü§ݪZ…á”ÁTe4œ4€´Yj¬–šÿ-Š€ P>—ÅâaÌW_y$˜º{I6âÛuŸSú E| ê÷¯]ÇPƒ<‹–öÕöªUCÄk:¢‚
+#˜±Âf(7ëϚΗ](¤f3²àq CF.OϤPä+BùP&ûß
+ÎïݺûñÏ&ì±W€Þë`ñ8'Æ,­ƒ‡[ü¬7hižP6GÔÞ_Æ–aX2ÇûË??~÷ƒßÎÞøõÈe_ׄ©Õ ¬¢‡ pÈ^'Ú&kõõŠƒ*Ѥ¶1 nð+EA}GØÜ6ª©l=SpôÐSh€ƒeÑÙ\›róØ<4+‹ù|iž¨öé¡váh×ȯ· 6®3
+K"Y)$
+a§ð¶‰ùÜc¹9ápx†YÖøjYÿú\íaË=úiÃ@‘øÊ®¼TsQ*‰¸<¨ï8|nÚw+”C Ä9àÇ©´OS¬«i/t×?vBéÎvÖ©"Ö³ÞÚÞ®MÆ瘧™_¾¿gŽx]†Ú°ûO_~]žÃFÁ!‹L ·ýrö¶ëZWuØ^ÒÝP¬¿;35%^”Ù90½¹øÒ
+Yb~qÃ4äQu›+BzTUÄZÒU¯èÖ­—FÔ}†Š͹žúsýúÒ!ã™>í»=Ú VP_Ýc>Y~ß¡,œäÁK”˜G²QGùzß-¡dÓ.¿½7Pƒ·UŠ»kÒÚÕ¢–
+©§2³­fO ZâSJÛÔ5@eW«*ëL÷V‚FÒÛÕÏÚÏ‹F†Uf0˜d¸L7Za¯4Aã÷.˜®ªèQ¥©¿\ªÔ…ª^E­ñ-ÅYû¥<
+jO@@(g’(¤Qr/-<(É~÷µÓuòzsMç8ìG\ö|§-ÇÆ4ÈÎp‚þ$§Ÿ¶FgNã‰àÁ0>:<:[`|¿_Ïš÷axYŽÿúWð¸(IðE•EfÖ\¶Èl —Ü 1šÌr‹£¡¬I£¬É~ÜùSYí©C?¡vb45á<4#CòÉ'7—™Z%®Ýe-p=Á±–[W£âÚeùþ/àiI‹á-âí©eÕæcf†"r«Mæ´ç7B@˜ηºòìö\+]h;ÿ#…”’²¸,’$„|Fl?œ‘>^V=a Ìjšb î…:ûRcÆè‚6¥wLií€YcãŒÎqü`rMšœwMŽÛÛ==©£§õ¶9ýq52Õàœ6¹'ŒŽ»ã”Æ<]o¹§±NZgŒ“Fw߉ÓûÅT2Éâ‹8ƒˆDqQ‰HÔÝÝÉL~9¾[ŒA˜ýz… ¬ØøäÿòXÝ<€ÕøüÖßÃN%’Äx2+eÎÂp–tûëÅÛh÷KMHèçˆ'’Ð܃t„ŽÞžÞO鼺^Ið÷lô!þH"ÀÀFº·h4˜Pº KNÊÈzÁàDú®nò!a¤}
+—‡¥’ý½ÔT¨DX{3$&ƒö³Ï?e>` ±‡UÀ*9þû<ºÓ
+JQ 
+"i]iœð¬ñ¼hdPeŒT™
+Ý1¡È`È7ÚåwA³#×íÎoi*hvËÝ@«Ìf•C¬ÖBÛÓÖ HÊ–`(r¹>Ÿ™Ù¹h|ÝÊÞKXËñùÙ¹ƒÙ(>I`\‹(xUf>n±äÚ ›ô9ú(m)¤A#@2@šEn®À9éœ4!ŸÂ¹<.›%•¦ß¼ùW¦œ‚ܸð0p­QäÉL²¡_“ “ƒâ###<…ÅÛŽa .’&¥ŸØ\Ÿo´ÙE.:Çf>BÛ \ö"cËwŒ—rkŽHÞ”¦ˆ¡îà!É#9ìd>³NMJΓìqüZ3aþ'ùuÛÄ•
+
+å°C-eYVrØ„„pʼno‰søŒ8>b'NBaEÿZ­vµ•ÐjWýkW•ØUÑ.H¨»r“„d¿±W)¢BÚTT•ÊÓÓäedgÞ¼÷}¿Ï=£tŒ·š¦ÔVpÈ„Šk5LªMÓZÓŒÎ<©¡ k­ã:jRß>¥µ€F&ÛÈÕjdºÕ×3´£5=P­3¤ížÊ°¨¦î]ÔÍšœw•”u—´CsØLž0–T›Ùë+ÊeúÃç°`.ÕëJ[™ŸA#ôrZZžomQ\\Àãó8x^f†–0!~yëÏ-—SaÄLw]cPA¤'†ô ŽFw8ÉMêL†ÿœ4‚x‚kìÃÃ}ÉA¼ƒiÝîŒ]2 3k=ÊE!}ÝH¥Ùûi38AA£ˆ3ÀpÄž+ˆ{éÆ\IwŒ Îþg^ß#Ã=€8CðÝ$OñfúûPŠ=–Öet†<ã Ý]p Ðq ÑãÞaÄ?´¶Ý³î2™ýöölq>++Gˆfåâ9(ÆÆ…‚BÙép4691;m~ñ1Ì,> KÿÛ°P¬,­Ä`e­œŸ¥‰baañB}£C—6Ê"w“ðø¥šæ>Rá×ÉÃzyX[ ñ+W›ÍÏ„ô¾ðuðF?Ôýû›þã—‘÷«~U]P+Ž5u#¶zŸÞûY¸ã³þó~S§­6¤«µÊý:ÛµÞ™åùøöYüòë[¦AW³ÇØ9ì»y÷ÖÂòc@þìòÂÕ/~Û2œjšS}ʆ°ñyi¤6¨–‡4gBôýà Ÿ hêúÕЃÚêˆö“°ê8ühP>Ú®èVIŽ
+sÁ$,xMœ'äCš†¶‰Å'˜,N.ÁÝQ*©)/¥N{Ï@xž:€Gž¯Iâm„Åq¢@Šßà¨û±ñÂh¤^;T¯»ZoøMÝáæ!ƒ
+u䜡ûTsýžÃ;Å›
+sp!Ê…`ÉËEqœÃÅ8<” âÍDiE™ô‡†JR'3èeÀ†Yf60’2“õp§y¯Å*µEÈ
+YAÆb2JÉZ#ªÊÖÍì7 #óu‰Hx-v@2·ütyò½Û£x¿o0Š¡LcÃz("ò«Þ?e9B™Ë)¸à‡©Âj™,‡­¶c@‘}›öå¡nxóY\\˜ Tt†\¢ãÕS¥Œ¿HžL²ßhñ¿¡H€/ÆQ4^.?›çˆöˆ÷žÜ^¥ªl#?&ÍGHýG†–ÊÖÆýçvä¿[Œˆs¹,—™-æÁ1S€1qB™«ÖÆ¡h‡¨Ú¾+xðÈïjÏþCg¹G¹]÷Õ– µuFk{ÔÖ1ßj›S¶Ï)­³mÔ´Ú:©1kÍ«ÕPgBm™ÒR3z\dê²a®…\PšµQ ÚŽ õµù¡Þñ7yëù·ßÃÑ .΄2“ËápØ‚VÓ‰šÛÿ*1‹ñöäŒ}·Œý‰5ú—–½žžÅ%ô^Æ ˆÖ8›“I^l|µê4Ã׃¸}I¾‘$÷0Ã9‚#ˆ;œf‹¦uŽ =£ˆ/Æp‡^éò?7¸ÃÉNи"ð’+B3 '’ެƈ’,Œ`³˜¬¼¢ôíÒ‡o÷JjÇ@
+PÁ>„ôFÓÜ.¤Ë†„bˆ=šâ?óúN  ’$W˜á §8Â)Ý©]aÄKvÇRÜ‘dOñA‡ÇLêŒ"}A0öâ ­5Yßøðh†¸DÀÆ8l&_€ ¸9ü̬"\TWÛôÅ_¿š}ÐuÄòâã%PÅ ¸b™æî“ 6ïS&Y9Ÿ
+¹èfœ+å}ë]ß¾£ŸW_¼u¹ýŸ†Î»†îº®)Uç´’šRšÇU ëj5ò@MΑÓêÁ%㬺}AÛ ¼™o±N+m÷”æ)cÇ”Ê2Ñjºoh¿£·Ý¨nú
+FBƒW}í‘“æÄîKu§v'w "Ž¤'ì@Or!0ÃH’Òtñ</Ð`ð€sb {ošw
+X¯q…É>'âÂÇRá$ïµt}· ìýŸ•nc8¼Hï•5]¡g]<Õe¸à'¢ˆzâ)F÷UTã !=!ÄD|ýˆ'ˆ¸Ó£¯vîâMö}úò˺ûXlnÑú×è<Æå‹rˆÒ¼R²½ãîÄ=› @;ðÃÂüãyù°Ïæ–èãÒ·yei9'Wj§j„ÄçþK~™7qžq\6P’6C,K«]I»:|˧¡4¤%àâÛ¸$Ällë–|pZ–vW‡>d[’m]–|B‡a¦ßú¥CÚ™BJÈ—3Ò cÀ`l£>k%-œši™¾óŸW«Ý÷Ú÷}þ¿"Ñ‘áQ‚'HAq&p¹lïŠBE?-X+ÃduxÙn®X5a›bª *„N)†`#HVEèꨀD A5AãÑ(YhT«C5‘&¹¿éü§¿e¸‹™åÌÇ»¢´ Ÿ:m¬Šjù›ÏùÞãGs“±- 2L34iƒäJш"l«
+PÊ°U4W kB ¬ƒ¾¬ Z€*ûOׄ²R›t£2`PûZ?¥è2äi¥¾½B
+…8ŽÃ¼ø߸ù#aNÉ–‚ÅuÍe´­ÀÚZàtõ4çõXŠZÀm4œmö¼[ž Œ¸µÌÙRÔ b)µ˜ËÌÖýf¨·´ØóÏšFœÅ-'KÄ<1.à‹QLÊìݵûÎÝ»+Ïç¿U¼Ä½þ“ËWp„ã*àˆ<_–ß\ÜÖœkwæw´î³ÛË\u¹õ;¥;ˆW…B.$¤|aï}o÷×7o1Qhfn!é`8ÂÄ$SO°Äwf’oƼÐf¼Ùظ/3¹ØÕËoBQìc.ΆÁ QTÊ€pø<!B
+§:hu(Â$Øã©MÉj¿Iî7iBfŸ*1×äù™T&áñ“Q.[‚ &Aø
+]ü5¢¥Gôæ!5 ¸2½yXGGÔ¦!-5¤1Œ©›&ÔÆójÓ9%9¡4ÑcÚUqBþnÙ;Òœ4”¡p 8wŠpˆ×P<ŒU‚â©Iâ×ÙóÒ÷Vï¨\)Z°Sæ"²¥˜n+¶¶åÛ…Ž¶ÂŽæ}®ŽÜfG~«½Ôa+h°q:©}-tiÇrÛoγ·°Wî:’’,¯ƒ PŒ‡ü|ïž›woÃQš_HRbçgg¦cL¨ž‰-É_ž¨ÇË¢U-¿-ív!ÄE8ÁGPœ#xCœS•Wu⃓šýªÂ­ù›ø2bƒ@Ì#¾Ãx{öützzziL[†g]žè&1V&“ÁfŽK‚‹ A™ÜæE$>|øÒ¥KÿÚLáÚµk>Ÿ¯¤¤Â5p äw,ÜA\ÀÜyH†@›žeÍ-þ¸þÊÜ3{²eÖìüº‰¾O·
+#@Ž9êv»ïܹÚ!œ
+’…¡k%ŠøL—Ư>›‹8ZW?Ðâô.[Ú"íO8`ŒqtFÚYN@lˆ½ i¹ŸG9-Z‡ó¤ñ¼Ü‹H‹;\4²¬©ñ^E\O6v!ÖŽˆÖ눱晽‡†G“Q"Œ/¥V]*lô=Õ4ˆ´ ÍCËš_° .kêG܈»±w¬hìZ꺫œ=+Ü]+¿—)^+b¡ÅZÒT×Èyƒ…»K¾xáfæL ê!Ãüdd$))I{‰æÑJ+(z“ôÔ•¼ü.jóÛ´ùmÁèÜ©Îô–…‹FÎtWœî¨Èm×÷¿óÓ©À, :8¾¿üíN hŸî¾ù#Û»úÞ¸><;ÆÚÌÜܧ³cž_öµés»*sº+Yx𨗺.° ¨–ÝÊ~ u® ì’ã¹ú¶kÿ íœmSà ðaa‡>Ï«…p@rÒS~¶½²¤Ë\àÒ¦]ÊØ–¼SºAÎâ•.áó!¦ ErJ,âŒTâbM’bdIÏÇg§nÖäl®+M°_ˆsý@Œá,–»JX i.q•{
+´°" ʵŒ¼QTõj¡~`r5C…º×K¯•TÁ‡-ÅjkÖ…¢G÷mÜž@I¥‚2Œ"ˆgI±€”`(à ER8%F%Œ@ümbDZ-Ç.§üPÄ OÓë“ æKuju¸h¤ú`½ñ%Kmrù Õ”fV¥i̹µ·_{oòÞ $rsãñ§~Ñü3]J…!I[ý²u©óפÖé,G¬e)åûã÷Å
+•A Áe½I‘ð“¡`Wþ™Àì4vfÀxoB¸fˆFü~ÿB/´¸C <y•‘‰
+‘T qÅëkëÀ´ç>:ác£ŽC8evrÍ©©©…› 5qÅ0>f _MNN‚vœLONÝ~÷÷žwIQqÒ‹/ÉqZŒÀ9$‰cŽŠˆu_Hãñ^Ñ »>05Ý/³Î”Yüºúa•aÔ&Q[‡ËãºÚQuÍÇ—ŒZÇ=“«åxænF$#E`ÑMCrD$H•žöÖ¹¹ÖñΰGR
+á¿øýÿwå›oJO‡scü8–H+HžÇñ¯§§"V+ÒÒ8V@ìv^EZ†¥7€‡ÅG/âè´õG8{Oï*Wëš+•¢˜4.WR$œäÓÊç‹#\4‚x;‘Z7bkGl=°=Äå^»{Ÿ,š/®YO’‚o½°ÌlAêÛ‘æeÎNÄîBjšwÒÜÔwGØ{‘æ.ÄîEìK]7¢Þ»ÜZ·V¶ù:ž’$Ú×{u6h !Bà* }Å/!ú}¤V}ð ët†Œ¤DB‰ ZÈÄJö_<UèÖ•tš æf{59íêüÎÊ<¯*\4’ÙSqÆUVw½ýÓ‰qFfü3õ×ÛnýñÓì®üÓ.©‚÷?øû×Û¯xŒ@GY^ȹvuŽ§âsÐ(O` @-<À6yZÐ1ק™Ù§Os¶mQ}ó;ôÜT0I> 3Ü%݆ì6UF«*Û§¢+òUe5\NWeoÝÿ¼l“£ÊgQ„MD$FC‘à\oè¤$brÛIúne^ÚFûù„Æâ؆B€Š8gi¬³¨`½­HƒkÎ÷8Y‰³ƒÄÉš
++À-_éÍ×€ôC½Ô
+U¦Œâ¼½é·Çábœ*NR ŠHš†&ÆQ¸Õ)$‚Bi™P¾…Ùzä¹ÃÓ.è’ ÆTp‚1ÙÄÂCJ­%¹Ú°Ï.1¬³¤ÔS,ºtcuNí[ƒ·ñfÙK=´sÖ,'íê6SZUmrÍRç70[ÕX­iÕ‡*
+¡8VHïÜ·çí·nqæˆñ±Qbx‹vêÌÑ“Ø:T*–<¡\È<ƒÅ|mÇÁ§´ºÕuÎåÎþHçµåö[ÒØÑÔ·(@X‡ˆßt5²º?¢¾qõ,·yWšW¦¤I¥’ 2üözÙÖ•Å—žlŠ°ëzzŸhð®
+Z$ÁpMBÄ*‰½YéçlªÂs†Osª¥¬À«)ñªÃE#ù>ÕåvãŸî¾ÏòÐ$ë®®¿÷kˆæª>ÇŸG>„L.
+BD8›Íáckó7•·+kú-©Fª=Q£™·áøÏÜcÀ"áœâó‘Ê=ú®ºb¿îPóÇþd†~˜M}9sõ¢jÐQÚZ'ï1–ûT•½êòn5DüZ|{U„>= ŸV
+-aÆ »h•úh=ÉN©[1˜…
+ÝØ¥ªèVWø50BP™ÔVÚ©*wk¤]új¯¡Ôr0½tûÒ +ã …±H‚EòqŠâÅóùŒ'ŽåP${„Ï‹£È—Ä\ÖòDÁÆWRr6Iêö¦jK–Ûd+Úhü´USí2êD5åV:å´œ54®8ÉÎÚ×g…F
+gcbJÄá°…«zãÆ/Ì­:k´hä¦Îòo­uªž†œ1uToÓZ&ŽÆUºQù¾Úv®üÐQåP\*R w–¶di³¥éö¿¾üz$z&
+ì:y¢Ždq»}qcÆ–VÄõó9­ýˆÝ…´»÷ÀœŽÓH‹q<‘"^:>ðœ½i˜çü­í‡%±ÉË„/‚s³E|R²|Qƒió.è@Žw"ÞÁ¨Ñˆó£E¶žE'úæ[Oγõ/:ªA1‰
+tåÊ•”¤dðç)”€„ÍE×doÙëô˜Š\G*zµÑ¢ˆÝêÓ-gþráNpd44elƒ ^ܯÚ×y°vÀ s©4½vÛûnǯ{«œÍUe ƒ¬O_ê©ûJõÿG¿
+§ºÆ©Rtj^]µGSéR<È|O¤™_S% `¡²G_Þ­w«ËºéÙ•{
+,Åév"^€‰SðÔ×ã׬Ú}xs­>[iÉӲ̖Ü&ÜÆ £%Ó|,×jËi6¿e´äšMYC¦˜ÈÄ”c7AÑ¢GU[àz ŒÂ9¿3þQû‡ê|t§Ì­ï·ùCÓ3Áñ[ú\USú¬írõ†lÌ«9ÇÖ”mÕgÔÚÆM‡rmIÙšŒ&`1¬‘ˆÄ1 5J`(GH V¯\UU)ýÕ/~9zo„Á’ˆÛ‰äAO'‡ñàí¶¦fîܼ{éÂgÏŸ;w.7ÿqszb:8ŒÄ5°¾åûâG  ƒƒƒÉÉÉ°àå„<
+®8›#‚»ãÀíÐгããã~ú_a@ë'E3Ìwæ?Ä—}PéÇWPGm¯wà!„Ýlv“ä5â *¾áyˆ‚¼´(V@ÞžŽ"!›÷   !œmçj;mgzÓ™›¶3éœý£3ÎÍÔj¯ÞÝ(jÒߥיrSoâõ™ovžÍî>»Ïïù½|ÚŒðƒûæý¾gÞôÑ­[I|> $,6Á!0t«øð±‡rK°hdž2ÌÉ3”ãK…îD5Kuø¨ÎR¥Ojœ»¬ðR¦ÇJ³©à‡i8¶B;pÙœ¨m‘™‡Üúèg>ïÒë_Š`7¯oÉë¿÷·ÏR£9á±ÑQ‘1©o
+›³E=ëÌ®æÑ«˜@†n®Y-vdp v¯×›ßT1q>7<|;'ŒÎÀù¥!Úž }Sëz§ÖuyB-S›L®`ÑH¨i³m0 öéõ–)¤¿;˜Ž²"’Qbëî}.”GfæaÑ\Hxi€[l’Y_·ÅØ ßD,?¥Ÿp ƒŽ«çÕid«¨†¼…Åmã]S«ü ‹¾åçË/›ÿ¥?û¿áAlG«<¿rî‡@ DÍìlcü÷þþù‰ì“ìH4žÅábLŒ¹ípÑ» –6‘GUêü6
+Q“uÍñk†ÚE#2G¸r¬¶K/I.PÂQ%ü#¦†e X£íFéKÕz.NiÍ„¬Æ)©¾Þ`oƒ{j²*·&{ÞzCÐ&<z6/íp:V Ί¸Ðg
+4úB-HW £¯Ò2èòÁ¢Y‰ü÷S¿ƒ:´Óïûôן*Š®ÜnCNoç™® õ8]¢|þ_z~£*QôœÐ¿êø
+ €D[¬UäÊ5§tFA—æTHŸ§VÉ.ç5žÜEF0Ýx¬Êeƒ¡“Ç!Yœ †mCwîÜ TáÕäðÍüðhî¡eÐœ{"—Ëä 6cs`5°hèH?(k“ß¿{€Ä»²Ç ~ûŸ[
+RàKv‰È)6ÙgÚjvߢ‘PÜ!$!5ÑûG&‹Àð@Ž©v,-¨l­oî•6 )jí”Щ¸è„¡ÚAB§ì¢½­jDøt ¨iäF°¾¿aPZ**ÏI~o7m;‹G° „`(gq Ö`iPˆË`òxFÆ°èŒ
+ù„$I(Ö ¸;†›šÂy/=±èÝøsÙÉõ%‰×Êbeå)¦Æ¤>ñ;Ýâ=-üÞf¾¥%eðjâà•XksLs‚¹>©¯1©¿)y@œ4 NìoŠëo¥õ‰a 0@äð3Üõjw‹Á%Rßl”O7É=uíî:É”X>Ù,w7JÝMÔd½dªQꪽ1QßîËàŸ ±ÂÕ@iΊ¨’*ñ©3Çr ÷:òÎŽT7Ž… ‚ã/D0A$ c @wt˜x$:$e3ر ^
+“Ÿ{¤lOéÕ¬y¥ÎS‹"Ö’ê”V_b”ç«UyÊ΂c¾J¿h'[kô¨Oh“:O§Í×js•j]‘¾«¬Ã÷Ø¿¸8¿èŸ›óy»k ZŒ ’Øf<‚À›ÿ§÷¿úÓ·¿úxâcs^Y$"Òö¨ ÊB- e*ze:RTEšb°ÊÕœ+e{Ëq2·ò «8Ô]8² i“8‰£8TçŒ]{­²Ïþ|g™Þøžûçüsþ@Z¦Ôòð”ïþ½Ï.·ˆ Jš .Êè
+«’1¼(-Îö×:üÒ¼þEÈ0ÞT𢾿ÔëÎl~ÚìÞ'~ïâÒÒM§'–àÀt1ÃÄØÌmïÉüC…xèÌù¤öÛ$“‡Çì<"ú¯·ÿ3]ðƒè‰Óļ¦Xx>ŸuÇ ·â „ظO®)Ÿ·w/HÔ”ª'mªY©aFiœ‘h‚E)ké·mgwìác$+
+%y…&3ÀØ2)õdæ1½j_cÒoÔÿc§
+|ÛÓ§O3öíÒÛ‚ooÔl²"½#ÈÐDh3ÄêAzÇB­®5«s¯“®ìýŽ·Š+ÑH’ÉårÒÂT
+d̃húÖw ‡ŽÜÜØ5¢›±Œoés¼)ÑlËÈÚšréÔ#ÛF½¹µÎ<øÊ4Ò?‰ô¸7Ò¯v#@ÃãoX]oª­aפC²Ã"È3~kiŦËLî0ugä¾ã›͉<^¼î_Ì—yP”÷Ç_S'‰Ñ•Ý÷>ö}w—åPÔxPD’ "UåQ´¢rËX9ö¾X¹–cÙ…L;é1Ó&©Ig:Mgœ¶SÍ4Ók2©FkrØ>/«ÄNœ ÉožùíoÞÝý½Ïïxžïç17!î¤Óƒt ‹3¸G‘Τ}iö#žñ¥ÿ‡¾ÆW›¥„ £QiqAÑ\ý…h}´<ù|î™°ÈB{ôšÁkE.š›?|(D §xŽ¥×ÉÏzí'|Úo3¾ªùŒ}ÚÒ]i]Å€¶ržh‹|º
+¯¡¬»¾ÐV%ÄGa*’ä )ÐÁ  2sF d@ ”ä)T © ÂVÍ+ÎVvi_ï3žôhËü¦ã=µ
+âå(*6†IØÌ¥lWdîä$s‡SÇRÃk+Îåñgrgsùs‡Âµù1Öâ­Í§â›OKXQrP†JŽÙæ«ÔyOéÚò«l9ŇÊÀ¬‡J-Kê4ù§“²ËÒŽîÊ<²S“¶51y}\Bø–ÍtT4¦ _Ë©qNiö`9÷€h))Š# §ñF` Lã83Áª^… 2ÕF*&AŸ²~÷±WTî)=¿¿F@§Ë23Mæ ›-Ãñ´iÄ‘î4î³Ù²ö¬F‡ÆnN5šØÎeé y:ýaÝEØñ¨¹!×
+4 `F¼{p^€ô
+Î
+æ1|ñ–y`
+XÅìµ?ŒÙ¸‰…„Å‹Ç1ì/Êk>ѵÜ6¶L˜:Yv6^¡b1%IǧïÞ335=9?;$¦™EUáùÀĽû·þõiRâNÅVy¦xcì'uŽ{µæÛ Æ ã?u¶;zǤ¾éiÓ`ÏuC«;¯(Iņ…R¤”ãá2ÈYš‰ŽŒêru‚·°™¢"|ëœûäää;o½ÉP¥ 
+Ú{—¹Æ` K<( •Òϧæ }~Ä5Ò;†ô!íPwlESûʼR#H^Í®F±œô]=Ï·"®>¤{ü‰i¤Ó´„´ù—‚«nâö"îÁ—©n“¨7 D(/D¬(©AÀÿ¾#®ɶí2%ǯY©d¥Ea±;WÛÛDŽjƒ•Ž/sŒ }o.i\î}®ë"â~ì{;¼KÛýH–ÁT‘,š'Å[†\pèsbÝð“@›~VwàK¡÷ÆÛ·ÅAüF‚„”äÖ÷-šÚ> é©+÷
+zÏ• 6zê€J}¦ª“¦"o-+Uóœ€â°ù‘*µ ÃF”rzÈ AáPLÁC•Á'Ρ´D¹%"9w_y{0I™ÇPê3” Š<u€"ÞEó¿Ìo8éÝ.ê×ÂZÀ
+û ýé~Ke·¡ÈY}°¶xoqîö¬]êø4‚¦ZF£$øÈJ–VS Hƒ%8Nq<
+”ÂÀX.#‚=,SNÐ`ÉÀW‚ŒTHI5Îr@+ðbÐ
+GårV‰b[á½òš‹ûóZçíӴΨï¸\|v=L°<‹ãxŸ' bÈBI¸xeáƒÜ¾í¯ýé*àF‹!-YóVù™Û Ö;çõ÷µwµÿn°OÔZž6Ü©mÔ;ï:>Ð:ì»ÒbqB
++W2´ä¤]W®\ ,hýÿ„é[ØàÐàè| '{?\`@ë©$,1éìGº
+d4™À".› \Óüm1ž($¹0“G,B…$Š\O˜ˆ 9àeˆ´P|RÆË8’ ~êFQö®ÔùÙòóNÖª½XxÞTl6™ÌfZmq«½¥Ï¥ðù•~Üã8ètrÁŠ§ àTû
+@ðD§Ú³Ð}Ú”V`0ÉWr»ò]^…ߧòÛóíz¹N_¨ÓÕÖª,Þ¬Þž–#M¤
+… h¡Ò6}«þc¤'
+î¿"0ßq ñö/˜F®ô"íý,z—†®ÅÁZ—¶Ût.mïD|mHÿPœ§ó;ÍÁ%!¤«—L^‡ó×.ß´ i¾òÞ™’âA·Ü@ZÃoyúðçËœýȵ!Ä׎tö½xí÷E‚W‘–~¤9ï½»}•ÈË–Pø*äþ $Ì+ ‰ÍßÈ`;¶™™‘§Ïe)æ¢ÐÁÊv®kè6}ã˜ñ_ª<l¨ï¥+®h+¢†³}0ñ ÕÇ’”8‘ËåóÐ$JF „~·ïÉ߆c­
+ÌÿXÀÌÎÅ*ç½ßÞkoiû`×#„RJˆ%¸<çsq!&^/UV¯iÑmoªì·œžÇ†7¢Ê¨®ºGWLÖVºuÕa}M·®,¬•Gt•QPJY¸©¬›}he¯¹´Û
+¬›ó-V… dWº*wÌ|m
+´Ø4¢Q^Š¢wòk–7&bæ7=Ñ437ÉÚß3 šaæò,s'pÇ®¤Íj«é°q죳ìÚô(3ñìËá¡æOíE–¥Ï¯p›U†¡æ! ‘
+›4¥Õª¶Ò*º©Hw^}¾p‹:[´A Ça(ÀdÎ=vì/Ž’¼›°F–"KçògdÝ©<wï‚å¥!Àè›ç.¹&´¶gFëcÚöÄ`a,ÁÇxÄ 1‹‰‰$Š’p¸Pß>¹>8þr Ò2== æÅ
+ùðëõX3sscSãÌ›k ÿñ•Ù`Ž~ò4#-|“ü“°ö7ë”É÷Bc‡<ÓÚ¦õöŦ‘i­õKƒí‰=ðXk1ØGMîûÖ€G} Cù±î#%)ùáÇ̼-@~ý£;fþ8Æ<ÔítM‘*N\‹®Û‚8|Ë:†–#Hgi»þZ0õ°Ýy}Y(€fçˆPž€¯Í{…ž^i±'|T+X—‡bhÁ„8àˆàEº‚´E`G|û'ñÖV$Ôövèæ‚i¤5×1
+}"* øüÕ¼5ë÷nª h~pÍq*Üô¦öºO_Úo(íÕŸíÑ•Et
+X±"j:Ö—E½Öò(}6b•G,=4`•ê+¦²fM‰¥î¨¦¼äRù‘ÆS‡Î8©úþ %è@‰rßQyžrÏƶfänÈÚœ™%¢¤\>!Q¤$®LÂMiT‚4Mͤ²·¦älKËÍMݶ-%g‹l³rƒJUP¼±øèæãå•Ö쯩?X^uѤ6™çaædáÁ!·¹o¾“.°Ú
+ìöB‡³ÀåÈw9ån·Ür)< X±€­«,tM¡­E.Kâ²[T¬™ÂÝìj‹»pÁ®½Pýjð̸‹ë£ì!™ø;óåÛÔyÆqChËÔÑB‚ïŽï„0n´(™(ê4–Ä×$4+Ý:n&Êe,‰ísŽís·}œ8q;q®Ð T:Ê@]WmÓ˜TíÒ%ëÔ}ئNP. ‰˜Äœ=ÇÞX? ‰HAìä¯ã£s}ò¼ïûü?q#ñÑùß~8ðók®}ò«?¨ÌÞŸÝŸJßà»ê"O¨†{?|U –éd’ŸºýÉMêM»k/kc¿Á°5Ô/‡¯š¬™æ‡±³À!d™—±R¸äŸkœ„ؼòß"ì~Ê`l¯‰ðUà„™„”²çrø$h
+â&‚Ýí'w“®Ýî=¯¼¾É¸ÈSg„Y. ¥J¡,PHT+Õ«dÞWËo‘ $8……¦°à„‹#CÈ 9Bñ „K½åméÚëX­•çÊrµZµB"Û¼qÓGF¢þ¼×·/zeÖžÀ@ýAN!‘k ‰óCÖê;žÐtý9Gh€¨i'žtá3—Àü óΑ“¥’<q®QoØQ²=- ušŸMÍWé )ñÿé4¡Âœ<~|SªÓ«—ç?T?îbShhÌÅŽ5“ù¤i„wqÿ@¨Ï .‰ùï6bÓ^ÿŸÜxIAÀ' :nnyíÿó–µ­ÌŸššNLL–¾¼])—éÔÊÂ|ùóÕß~Áß)8u䬨ãÑTÐ5(
+÷tÅŸ«;ªÐlÈ[©/”Èu’â|íê<¥A#Õ®’éV-Ó*×ïXrüÔ3gÎå´´/Ž-¢C²—¶ç­-µ¶‹"½s¦‘f ˆ¡…íÒpNd´ "5/ê¾ôlkÿ²½û¥Jè슖ºš¾L¶ëTú ¹/ªJÊD¡ÎEAnéñK jq[§¨åŒ¨3Q‰«êr:Ât¿÷ÈÔqNÔu¼î¾Ñ½d]IBÍt£ÀÉÿî÷O¨fBVSó¶
+æ:²3éôèèèÚâ5EjR,–j¥Õ?Ü(Š<uÌxLÕöyGÝu ¯£Ïs$æÝQS&“­€$Cuà¿~ýz¦Qåg2ò?±?CeÂ&Ö#ŒH2ué¿Q³G!U@©6Ê WªÊ†’â÷¡ÚîyËÏÞ^d8±¿'$qÄÑ‹
+Ã+Q¢
+£ª<¸ ¡l([å¥íØ(m!i M•L¹€°çÌ]lÑê÷XX¯™õYü 0GebÈ
+$x¥5°s
+‰D/%8¡‘K·Èäç›ôwLü
+ÝVHæ·¾ËqÃâ={ö¬F©–Š%+ z£T|ºtçMOp Ž!¾[Nz òhˆo`÷Ïî4G˜»žÈëk6õ¥T®“iÏKÁh§ç“Ff2¦™õwøÊ•+:V£2è”rô[åÐHã({Ç÷Äiäž‹š$Bã.:íòóîo._S¹I£/”Ke@#Û·½<“ú/;=-?züM¨äÐ>>˜äò¥÷t…A£—U©‹r1RÔzaAóQgô‘îÜÞ· 
+fW¨£®4²¸íG¢ð°¨mÞ&ê
+IN ¿éìL~8¥æ*Ñý4T¹Lé€ÉL:5û³k¿Þúµ­ºòb©Ò •ÊÕ+¤/©÷±'ç+þúnïѨh€¡.ŽîÀ=¾ÛÓp ß}p
+>iA«7Go5$Ò| ëÁ+˜PÙÜDmÁ`Y0li&̾^¤˜c†ŸâS÷îÌv’6sT?Ý)L¼Ðk¤'ÿžhx ¹º:ý™`ÍS|"ÉOðc|äT+UM{w‘¬•ñš(Èê\ãl6‚&?$h F€TC½:#XŽ°f®fòLT’€‚œ©…+kálaÔŽî,~¥Hf0*¡&©e±iíÆé g输™r÷PìrÚͦœLÒÉÜsGÙ»¾Àšl$ïctÒí™vÓ“XøÃcX©X趠ÄA#àgجYÌo}[¸l³Ö922ÌX /4ê%Jqeñº¿4ÐIW@@ŸwÖÕ Ç7涇sÓ·)î3Œz€pS§¸áº“Jq¾V¥1(t›¶lÉ ô¼¹ðL&Êl Ù€!N¥\ñ/îË<6ªãŽãom.'´iðµçÛ·‡wm¯qŒi•Aœ¤'¾ ­h›ð±k\ Q°÷z{¿õµö¾öô®m@iˆE¤ÿ4iD«þÓH¨mUuUJ… Ø€½w³ÏÙ*h,ÙDé觧ÙÙy3ó~3¿ß÷3&
+¯¶= WøuÐD t°5¨>äSÂëŠ1F
+†ð£Ê ¶ÖÔñ0ÓUk߸”œG¬±\¸ò‡+†FƒmÏ€s¯ÓVk36´Õ$bª˜?ýè/ñ$R±Ä| àªìÑׯ}|5èÿD“ ‘»áä=0tü"Éù«‹>¹¿{° 𛵡‹&4KµZë‡a‘{ëiñÉúnsM7UÙm­ï9[¯Ú÷íW¤xÎÉËÆs”•zVSwÏœÖ΂|“]S·yuלÊ:¯¤ày[i™7Ùþ©1½UÕÀç²Äb!çìm»yó&¶Åä|<ºR}¡³„^:ÝѲ°”QèÂÖü}ú²Q‡
+™lönœ˜ê4E;¨¥aFKE:ªÎ•e¦Ã²SJ|œÇ‡”ñþ¥_'#(—>0õò;ËJ׿ôn<AS™€OHXÒ6·jGÙ5XÞYjÚÐsìYPRkM#sgzîé{æ:ýÀíguÙs|!GŠçs¹ÜÖÖVzÁtI Êר\š‰D›Íd‹¸‡Ú±a/æ˜DºlGâËpN ]vŽcŽà:e—7Ó\ç˜Ä@²>lØÿ §;›²­·Z7êMëmY®à¦¾ ØðElȇV²%¥L¡'DxŸ){vs‹"³Ï¾Áæ_-aôO
+‹YU?Ùdļo#°qÁv°ñÌÁsë†Î£wŠ8ÆhYçÏ
+y5PMŒ>òT@·4“¤ -´ôÉT8 >‰¥¿æË´ÃÍ¥¬PV̱ ~>ö[ÌU~àçcÓpg“OuÔ¯“{4Ç‚FxõkÛCúCÃgÚÆȶ É–[­pkÚÝZx"ójåî…§³Í§jõ©å~ <AX›×Z=åUs@ Ï£^M³³³tG™˜ÉðøŸýùÈ
+7…ø¿nMï¥K$.›ÅcqvâK';¯ë»ïj­÷tÖÙNSXÝý°õÇ4Öš®¾ƒ¯ ™¹¡ôF2ñEj¥+t‚ýï4ûeJZÖà ‹0òï/ÿò†ˆ#¼Ö=/ÜÐYcêžiuGcS¯=èm3g a}÷õ“¤ýà¡Ò|N.3·PÀ+//ŸššZ¾æåŸÿµ)±¤ip
+Y%OUÔb¤ döŸ°_X-Ùà:Ïèóf:ü˜#„ÙÎoz'§×¿±ýßܾƒ³}×–×ögê»6ÙÉp¿ݲÎj9@VÌ$„Žx÷î,ç z³‡°Á64Ž ±G(Ã5 @‚Ú÷Óˆk€ ÍmVäIe|ž˜ÅÁebâµ—+®Ýü7×xt‰ÿi£ú£w u_ +´¶Â» ¸i‘ ‡Ã©–Çy¦ïÌ’”ù™íå¼%|q/br .·ð¹Òª“o(FuM5ØѾiTu<d:2ÔÀïköª“†Ãcj¤˜^m›G{ÔK¦ôWÓ à×¾9ajîPŒt÷“Ç:µ#£MåcPO4‹O ÈÔ¨nÁ%<8“<`ß÷÷ÂW#-H<à¶x:Öè»@,z_Ÿ´÷Òeqq1™ŠÓó“çD!Ј
+ Æ|ÈØÞ~«5ƤchÓ }óÍÎîj¥}û´ïû~ºgÜa&wÓT¶Ó¶Å¥ßN¶ÚºÆ"l„€k>jÿÚIS[Ûv»a·ñ‹ß|1ÂF— 0÷Ùë?ý#F§ÑX,™Vž"€Ã8`L¦MS o2ƒ¢44£qÒ™{cÓXéL“9W»cýn%¾P'%Éd8–ø£¥E"§ì£zgIßÔ[ú.؆+SU·^¨¡FÇ $PÿuÎ é'«ß9r6« ™X(Z¹råÝ/¿{–R>y±ÀÏJ>Ýq3:š‡ØÑ ÞR\"*‰å˜8¼§àÖ¦×â¼e"ïiÉa½óÎÔU~ ‚ü‡Áþg­s…SÁÚE©LÆä¹qƒOüàwþ3'ü7ý´XŠ¼{åH8¦ÉtnN–ÈõVØû “†çKˆ›z2bp<0¹>×Ù¥ÈaU•L(¥Œ¢(èäàààäÒóâµa¶Þ‡Œ E"&—‰’ ñü5‘«ù»P]7òtÎô´Çø¨®ù:fº»QS+ò†âªÛÜ]±µÜ¨6ŒÜÁ—<—»#ÁÕ9Ã×ørI©:muŠXºH$Par"õyûŠâžoª¿ˆš."Whº4‚|`†`tÉ߉
+dž'É–®ã ùdY³å@“îH‡µ  -hª€‚XØPY2Cpäõ‡ZÅMzpHiÀ•—?'„ù-Ú‚€¾¬,nÒîsŸ,l¸PÚf. >÷ªZ2µhõGšõ ²`]¦–àA8h†|^_ŸñQîS^#ù‡=|Ø s/{t ™0[“%Ç 0‰Høºæ@nI4RÐf
+ÝœC"ì~þYm©ˆö sh 8„§È8Ìxªi¦O}ðSÀ¼,Æ™íŠPé4€“Ù:ý.í†Ô iÄÂy†‹Ó¤’m ÿé¼ù«s$PdXOß:­Ò3ƒ³OËLUÝnë¨{&çíJÓ!³ ÎôUR·LžËW$ ©L, ø›"\²zR*›ª=VÐ'%>nòå¥å’D±œP$)”*)Q½#¯òô0Þ72_VGÌ®»çÉÛ¦)5uß@Ý4ÓÃZïÙÛqL¤ pY¢Xg0Lî
+½¿cú4*ˆz©¶%¶®ƒCœNÔ‘ÿr½S{µ4¼tpŸlá¥x® ÓÖÌ:s [¼xμD¥rsLU3‚¯{‚\ø‚Qf
+ëáIaeÍƧv˜§¯‘ @À<·èÿF¬Ù[Í…ÆFf’¶t+“IÑÚ4ÓNiö,§=§ŠÎu}[¡3(ߦ<˧W>eD_†ûA÷UÈa›¥ám?“Î@W-{̽ˎ =‹ÀÌa¦rSP„Nw‚L™ÖŸ5þ¢ö”O›g²fSp=¹¤²H[Ž™ÞÁðf
+ 5®HUâI
+Ñ&µú3ƒkDçè5Q=$GÖä8GÞ×3OÐH–‚bz×耀zzWk48ïkéˆÞݺ+?E*T)
+‘lÝú CCC#ìèTyì© ¦ÞÄÊ f"$¾!vôxù‰åDÊ"E²@*JÁ°cë7ÝÔÛîèa£sPGõW’Ó«¥n[Sõ¶×È žqüî„1Y*„é,W*—.^÷â)ûøŠïÚDY쀀¼ )N¬RàŸŸ5ì=zjÌà†~ÂÎóÖÈ=-9l¨¾Z|— à¯QÉäêDâãO~ ݃?ˆ¢‹vökü{7Ðcð–ÒUÈpA2&I¯¥®zÙb›Ñ
+ŠÛÕÇûÌ¥Œ¯èÕ$”t7óiž¶zw«Oxµ¥>®“¼)%Y$’BY&–·˜¢Q½8xaKÚfHP´IH Aj ¥b©‘8ᇲø=»v›Œìõ/®ÁƒnLŽjff7¤®‡Ä’„B VÓi(ój‹|j˜o•Ÿ Ú“úÂÉ<¥<ß4rCr¬ ÂðZcËÊç N³Mæ³9ÛÊÑH¦Í¼×nÊ°=+±çÚÁtYLËñ–аӸ\7úýˆ-ÇdÏ5ëvªÍJV›¯ùò³ë«„‚p¼þé5&Ÿ±ÉmòF6“›ˆ]Þ¬ÊÕ™
+¬Æ,3»ÇܬpÂ]&SÃäÒ0_@0ø º€4bÈ5ð€† Ö¤0³Ø“{ç¥f'¾"&¨„h1”#¨$fy**(«º­vŸÕ­ßц Ú(2ª³þGe ªªnc:û°Ê4 ¢³ß‚N= (2®²
+§„x xÐÇV…n÷ô}6d1™1!
+yKÝ<BÛÇë*ãí
+p
+7]êQ—ÏÑH…û14qæÇJSÕk„>$pÊyži$›£è°r“QÁêz:O§ÎÓh•:0BÏÈ}ž(Æ
+;\×ehÍy¬%ßïŸgè
+³Úl]í®ÚÊ­Gm|¿`íþ‚×÷)Ö+ó_Ëߟz 8­¨zû):KeV.=¡óŒÀBàª:WíRµq42—îƾ¾Ó©íúæÊß ›™
+A:‡ëÁñéÖšvFÉ6É[ØÝ&‹Ü¬ËÓér«ÜaÎa9Ë3±9F0›Òjͳ2àŠÅª°“
+å¾¹ªÈbY@ÿ†·±Îg)8P,§ER‚¢
+§¦ZõÎÛßÿ胵š-›_쉉 d^Lˆ2è×—«; ݺò[í7ðx£ê5Tú´
+S2Q(JKÿú±¢t ³«QLBT_ÐVôzO«CF@¯ŠÞYi¤,`TuÕ^}OSÞ§ûŽ¯Aí5ýdððM#Âøô“£>ëžSµAýa˜Eé1(}FeÀPåÈi´«
+é5Acy@« êùÀ”=©@²`¦À*º5ÀH5Ý N#.E³­Ð ªj+°€òÂAgí…-æo»šö´Â¬9ßb.2Û÷Øë_;]´½àÕÜí/¦®O#¤bT$æR(‘ƒ@0
+»!CS±ñÞ/ïB=ŽqÂUߣøxŽc“ax‘¸›è:Ù½Ðþpž¾ƒ9v;Á+ˆ*ŒÍ
+›!ßè,̳9ò-€O¦»UÑ䆰³%'^–n I¨ )’aPÅ$“ -ÍLAUÛ¶Ÿ6\X×¼ ŸÆ1npŒhíG¿±UD bœåÚΩØ$ó±ècÕ›‘¼™¯&ü›hlrú´ áæºÝX"Òßß·†Ê‚”¡Ñäýësn×kâ–³O™FŒsö jœu}¦1E`pÚ~®p/”<ô
+*JŠÃ‰h„û´17Ÿ¢Íà\4›šF¦tá6;‡mÄã“”å)$
+yžÆÈ0š¾w\?Ô`
+â@Hë
+ ¥§§Ó@^Á ò´/m/^ÞÙ³â\iíA¼œ¸ÏF`*€tô Ý àþeçýÇ~Nå—¹ú—3t†Ž ÒXÜÖ;_4‚\q áíG.…ž±:’whNÒ«ö+Ÿq·¬¬:&”¬CŤ4E@IÄ”˜â«I‘\˜µc‰ÁŽtõ.i !í!¤ÍÈ´ï[Ôv iïãvtÖ@Â!JkpqK{ LÕxîb€di¡‚ÄRqM¥Ä(fäÈ~sóT$öð9Ä}<æQ„{Ž'ÞùÞÛ¹9ëi”’´ £ óÃE©ô‹»¾^R_©j׿é7ÏU•€*ƒFe¯¹º¥É–Ð#bŽ­‹†#°(ë †Öde‹0òG…ÀC/I/l­vŸV{9©ôê*}úê$ò§˜¤¶×8_4rü¦O4¢ö˜ÄjZˆ¿®(å´cd4gm:ÑŒBKÂÒ
+kôÇ?=ÑÿÁÑkƒÊ+wÊ/ß©¸2X}嶪ÿWÊàí×ë®Þªí}ï€Õ·íÀQñ†W¾,”ËPFN‰ÐK’$ ³¥Ù¹˜A3Iz5ƒªÚt%U[ãתz`¬›•š‚ÆÊ.M§¡¦W_Ö£­èf;~<†<-oã‰)Ó[Æê€æp·®Úk®ô²Ê€ BWíÑð™kÛ4µ]úÚ€¥ªS§îµUuj
+#@–Ô’ŠCBÆH€C …€I(œ”‹¥0†¤ÊL’o—l=²óˆcŸË”gvîv;Š›, ,ý`–“¹Èb/rÁÖ ùúköP§ÑØŒâLCÉDâÖ»·š*› E¦…ö â Âc O&à›£Àl+²
+e}á®H$÷‰‰Ç¬{ýúuÇSE©)ÿä¾Îƒ¢<ï8€¿Ê£Ik–=Þcß÷Ý 55Ö«IFQ¨/DX`Axpì.{°'×Â.ì{°\»€&6flÒNÒFLMcKJÂØÖfÆ?’V‘åØþ^V¤3f†)þÑî¼³ó¾Ï¼ûîó>ïû<ßÏÏÃÉO¤§Ã[×´ vˆÝ+³ÕÈ|›T^á]Pëžow!• áRµMæ2¤Þîxoa•±6#Xuûâ*çœiÄîeÌ`óÁ¿D8¼K䆅ÉG—&§ÀE66q¨WA\Ë94ºj{tNÁ’ó1»¶c¸@EGmO@jëÃë:g'ÒÔií
+“÷ì ŽN$'ý§1aûÓ,S{AÇï²»>—vüéxûŸû¾Ìð}‘éëÍîüLÖy3½ó;©¾Ûé]·sºûO¶Ý’Õ”n~wkRÖt£(3—ù<R€‘³jÑkbsë ú5pk2gq–«:óÔ”oVÀ99®©[?¹ØZñ‰!xŠãPNLØ:>ìÊr+¤MsÝÊl·VÖ¬Âe7–6|äÿjìë¿Þ½ñ×[—oþÚõÁ¥ªKîóõºB—ñ¤£4»AðÈhSÁd{T0ÎÙ`?0Œ›iÉôÌz<g«Í>=cƒx½9ÁX1…eñºòýeûUÊ䢣›n$Ö‹ØBŸ†‹Æð9BT(Æ…"BHb|f#˜ Ãx˜À!IS8ß°- k [ð*öÊ¡‡J“Ô†d³~¯Q»û™G¿.^ÃÜu‚‰ñU¼Fy@>rgˆËL¼‡ÓÁ‘`ïµ^ë)«z¿Z¯1&˜Ÿy
+p.Úèhx%OêÙÿýÏÜgÞ¥Ðë$•Jñh\鈲qÉúHƒ©i ¯nGl—f«ÄÞYÓfñ 6ÒÐØ/¿XáŠI8(غ3Bv
+©o™ïð#Un¤ÞØ}‘Õ®9ÓHc[˜ÅûB•/ÒÐYëhò!Õµóêꑺ_>ÿ–†EŠp*½‚T[êýˆ£5ÆXÉÚ¶C ÐÑ¥qt#¶Žyฅú¶p{ǼÚ¤Ò C¤œÓ2ÏÖDaÚë[g@âAjH]û|KCX–”Âh
+åBêñ0ŒâG».f
+O§'gJ†ññàã"ßí~gÅàl¶€"Q‚‹Qè†;Žhóó]Zi£2ÝÉDU¦KžÑX4ë w+ ×òjÁæ— šÏç ±”ÐëõS$øxö=êÌTðæõO·mØÂ欤ÅB ç²X$¾ôÚº4uÞ·ö„GíQf¸‹ —O4ËçJ#RWQN“ü˜³$ßUF¯ d†­[¹¦·ç§ qÙ{\Û–ÝÑ›æûCª¿ï˜¿/³ó¶¬ó/2–¯7ËwKÖýÇ4¿ÔÿeŽ¯ÿdG^[߉ö>™¯/Û×'äZÞÙ¾û0MPK‰ÓË0!\yÝÎ-ç\†9Ù¬Ìr=õ¾d®¢ŒÆ 'ÚTR§"·AuëÞfý
+@ 8 À@N½ß"u2&iÖÀ@嶨O7ë¯|ö›ÉµÀécÌ/¦Æ‚S#Á‰Ûw;?¾ªë´¦Õž—¶–涕IåMšsNÝ)›ò¬GŸÙ
+l%Å¡h\
+O ¥ZÍÄÔ4L×Êš*à4ÔS$‰ýVvÒð¾Ú8Z2gV›
+µ5UcÅ•öCÇI>›fs KLØZǘ¥læ3õý( •Z¡a–—i0z
+ܯ­vÙÛžËÉ£$빘`F ¹K„´àT~ÁøØÃàô÷Àt°­¹EôoîË=¨©;‹ã×U[uk»> ûÌMAD«XºµÕºív}ì(ò«¶ÛZyä%‚BòI
+ù$‡•µ¤]mý>ßö(Oø´'QdiäªVó+-=
+ýý|{aì&”*à þ %W>»<ª#ýAÐú© ȘàW<&[øqîÜ™0ZH )1ŸEгv+2çÓöŠqÓˆã4âh@²Õ¡¿I$W¬}&aÛs*ýÏÍö¹I›HZˆsyóß•>}¤9R5¥´
+9Ò0Q42£¸
+9T ?”Õ#EµÓÊN2\Tî©È'ÉHÅynk"r°)®›q¤)k˜·v3MFΓg#%5È!R]3¨ã`R
+h­Ü5½¸)¬ax&OË,ÂQ,áI.•}×u7X«
+õú´71¦‹¢ðÃ÷ïÝ7Ä ?3‚,:š
+ࢱüL¯ÑÀÆ¥šâ³Õ÷2DjÿöŸç[>Æ zý9Ç ²J¥Ô¥JuÁ‚J¹[›]m¾Úù/¸ègÄä³o:nt}õ0Ð÷}À‡û ¿ñuæThån}z™êbó'€(þ€¿'Ðkÿƒ#ËmœlÑÅé­I6ÓF
+5Jb$'„&iö—géŒ3K+ÇM#%u³´Vnôë/Ìæ/@y,wβWfÈZ\’ü‚ñ£–ÏÊ7"‡kŸ)ôÎ(uN<u@bÄ«£Þ)ež)%µSŽBx²óÀèâ5
+·X°9óÝ}•¦tÇøý‹šœ*Øò{.Ðzm’ø-Gq’ Îâ!¡á´hå–ô4÷ßSN_—4\ˬk»¯H½M;O´HšÓ<_H<Í2ï5y}‡ØÕ"ñ¶‰O]O>Õ¶½¡u‡·%ÍÛ
+·RO|.oh‘{¿ü³Mrîvjí§ñ™‚'ŠÀx8Ê ã¾'•UfyM©Ž\`¿±üü÷åÅ}ð­ÏјŒàîöv÷îö@+hµ¦Ó45Èû¢m4*ï·Š Tî}·÷¾ãQäy܃7­McÇiœ´5)4 †Z£iši4ŠD9¸ãàú]L;8à ü“¿Y~Ü¿ýíw¿ûý|~Gm§AÞ±ëoÝû^¿Ç3â³¼ïªùCh ø†Çï·þÎ^à8eÙY!8‰­¢°™ú×ÄœŸž˜¸q÷V©C_l“œthš>èýÚ3F>îuû=u—Ú³ÚÕŠ¾ôÓ295æ÷–·è2š$‹m#p
+<D§1§™¤ÉÒ¸¨}áx8Ê@E
+~eûN³Þé€á„o†ˆt©˜œ-†t>û}žYý  ÈŒ®À`ÖI|þ¯|Ä!q&;r(’žõ“ *M½Øô·¦
+ç3©QQCjÓ„Ì<Z¦W[È °ò'2¨È„ªò‘bÞ냑=TµGny¬4=TÓŸ\zçôΠ`!Ža\ˆ‘0&rÛ'ù«Çï{. gU¤¹±iSD$lÁ`#Šñ„A!'öüì¶Ê8*Ñ«, O , `# «"ÐGå:¦r¸B÷•º:}k46ÂÄ9l"..î©íYü¿{í™nÁ¯GïÇîü!Ê%ÃØ<ƒóâc›sÞ6ÒÐ*zóˆ(˜Á&XÌÈ0^˜€‡âB~Ä2EËªêª ‘›ÂÖ0Öä!ç—7¼Ô×.” glKêÚZRïBš;–Õµ/­v"uÎ%µÎ•g\ë
+N‰øa¶.” [C6ïàsñP,(”²&>qYiéE¬µhô>äÀ)’åõ0•ngÅ™nZ9êºV×÷ÐBÒ؉À©ºv0ZQÎt®j
+ª,ÐmÞôÏh)/sh±$T®p\ÈeqàùNú½ ¨lk$6pÄí¦«ßLáóù¼ÓÓ¾¾¾wÄìæ²yBx7q.Š†0Eì-q»2 ¥ e#9IŽMò¶KžçÒæž.¦#ÂáÀåؘ BÈÛÀÆ2«zrzoô~žïú´´çFÖÙ¡ŒžkÇÎíÈìÌùõ`~×`Aç`ö¹› +¹Ý׋:KàØs-÷lVÏÕ‚®›ù]×sºŠÎÝÌv ”vVÖuuëáŠ(Wˆ,’µqot‰]ÖWØ¡Él–ιÎ øÒ{ïóA|<ÀˆÉK×þ”eWjÞktû½ðå¬ëo¿/q*ó츩âV9ØÈ §‘vºM^ýâ¨MrØU–Õ¡,rh;._ôLx¡Lºý“¿½ñÇŸ·ÊJ\ú»w|S)®WfÍmG e#ÀG°c¢Þ ÖeìΈbm&PbÁILN¾uûö$ƒßërÃmOúûŸ¢1àÕ3}FH@bŸRéÛxòÓi§TÊ * 9‚ nÒÖxù~ébÓߘ@Ó,²$ŠJ·ê“Múx½æ YlU‹õªJ™¬Q'SZøf²É˜`6'Z{= "-1í7ƒŠßu"‚I²@/˜wƒ@ıè¨m•ºZp!¸ |Ó4.Ç=^_@Œpx$“äóجæÊU£*ÓÚˆWaUWV=¤,cà9ŠÊ¼W_GI؈€ä‹“ShvÏg€³Î`ÇSÙ·oß&ŠrP‚Àø¼„ïÇô—S "Þ
+êµyDn|¬¦[¾‘™Iô " Tó]ç…É#7»5•÷ËÔ>¹õkµq˜Òû+ô¨ê¦CG#qðPB( á¯ÆÑø<ðMû¿üâŸÒßd³Ð@¹æsxL.çøž„¯”U¥Å§0ŒWh'••¤–ÿ·»UÖƒñá»
+·®N™’¢¡ÁäsA†††¡?p|üïl@Mzû®\ÙÈñ™ ¶Â—ø䶃bÄ~a½Éµâ ðºsiu×ÊæN¤Ê†ÔõÎe«­]/< ç“DkDêº?=†ó˜[^Y]”·6”Éd­};it¬°Ú–Õ:VVë;‘Æ6¤Áµ¼¾kEUR ¬Ÿsþùö¥¿:¿¢ñÜšªæŽ¾ôòv“³(Ë_/Ä‚¡zo߃T7#µ]/˜›Ð¨×D<V8¼.vïŠÊ:ÄqaieèÇ°š†v¤¹ipÒ*RÓö¬ªê^ZÓ†8»çºîú×¢h6‹ xk]ή1XÙÜPƒÎ1’$ö½uÒE§rœÊbû¼é3WÏìTeªŠI›„]Á9tð-óO‡ÑÑQ™D
+…Ê'`¿ok¸0ô¬ÔµŠãNm^»ðzÄþË‚6y¾­ Ü)ϩʳ+ µjrìŠ ›4¿ƒšs©mê¢`¨2³UYlS£|TÄÆIW²dLl~ǵìîþÜžOs{†
+Ûoäö̯çt_ òÎ~ø3«s0³c
+w竤ŠeQ¬BJ¦¦Ý7;CUÌ„Æ4B›Lì
+­Þ¼k_ßÕ½ý):õ§Âþ¿–õþíhä!Ì\ö^ y6¯4N.W‰„b¡8¥"ow«ú`3¿Å̾SI‹¶¼Cÿzkea¾¬—ÙӤѻ¹ðët*»òkíø%õÖ µÛzóΧüÝPøSÿPQ3UáÖvë
+ÚÔ@# g;ü£Ðäxàê­kî÷N7\袻kÛß{óŽw( Y<ò‡ƒ5ow–µSå­†ÿpp:Ç2t ^ßë®þºi„Í°Y39ÓóÎ5;äÑR•J! 6¬]ÿEþ'¾¬€’þ7“'ù×`ž¥Ž«D²¤hå˳¹Ζm³fÛÍVS:kÉ´Y2,æt³9“qäÙMi&6å29ø›óE‡>UÿcÆWíûNŽËµ1Y n`ÒM¶t{MV=œXv°†\æXVeꪴĘåË1.Ba7¸B$Œy)ïÅ/_áámò‹¶)xòÒ¥K°¾*¥
+D¯
+Þ)?ì£í·iÈÑ
+9FÛP¶!­T”n®ê?LY@7}¤ÈaTk÷UÛ?1pô¶­Q¨0A©Œ O%®¸?2NA˜N‚Vð$öÃáöÍ[Y[Sq±X& 8!j6oÿ'ÉNœ¾cÌ}­å[7W{µ¦0Yë}Ã8Ä؇ulØ\Wg¼ÇXƒU¬æHZznXc$»£3OÐÎÊ䧸úº³E‡7¨Tb¹'”
+¡DŒ¢AlKÙââœuþ]ý>}[šB"‡zD ‹T¸@)‘ —¼º"ù7eÕ€">S—¶•{á#Õà€ ðϨÆ<l`G).PÅú(ç5­%=)~‘<J‹'H0¥D|l{úÇ:Û Î öÀëcçH• ¬½[eð¬
+[Ü¢.j<v¤Û8›Ej†FŠ:èÓ–WÓpL¢Âdr)|Mɯ*9s­àÔåâÓ.èý°âÔõG£‘Ï÷<C !‹‹“¢
+‚xöåç¥*<æüfð¿‡C7YÜEîî¨ÞÓMíiÑ–µÐ7>þˆ—ÈÁIþwjj<<9ž¼æåžL„„CåÍ ‘º´‡:Ôaêÿã¹
+òÊD(4žACØgS“7|··˜Š=äA·i JÌi¹zóú¾6
+èî릞rlšLí$Éq¸JŠK ~ðþ3K?::žfŒÿ„Áì äÿpebb‚d¦ON¼{îíD¥‚ˆ¯”ÊŸ{êiWf­m Ë¥Û­™è¶4.á¦9ÇbLcÙ—jåÒ,ö,ÖF29Æo3¾Z§3)à`.Ãá̬³§×XÓ
+;åÕšýz딺rBWwnóöt1-Ũt”É^•õáÇW¢ß øŸ¼"(:|Ý .,Üøëõ•¬LŒAd
+SΨ›7 N 赪‘6øñ<â^V7”XןÐk^ê^VߟT3°è¼µç»."õžïWê1ñ”‘RF§‚ ¿V¯+mÓ‚oW€ôw«œì2elÌb0TJ‰€FFG¾
+¨”Üv|ß«µºSmæ’Nmq‡&~¾:Ñ£«èÖ•·+ïA#¥mjÐbøô÷GÊ2”2(T=Š¡È
+WÑðg@#e®é»V~á/÷M#Åý×âñ~‰ $D*JÒtÖ‹YeÊònsQ»¾¼=F#ÝúCmo”ôË{¸SMú·®¼ïA_Až±ìà³$âÁ"ŽÎó­"ÄÈþó ‘²MQ‡²¢U÷›ëñ’ é4YÄÚÈ@^ƒóh„'<šê¾ü6ÌXÔ¦úI§
+{$…_ÞÿìJY;¨ÿi±îqéó¸Ší§d©+‚Fqtëö­ÿ†ñgøXrC²»¾þ1N#Á8ÃÀÇP(C*4ÌBtnvîÓògž\µaMÆS`ð°>}öuÛ
+7=³[¡Ë×Y
+L–¼JÓNÎuÀiÚ»¨ÿÿmf=`šü¨Þ[oßá2í°˜ò-†îÈs…kÉl©PLNáAR©Ø3éëœvÇôÝ)ȬHìt@Ðî"± ÍçFFFV?žIb$Ã02 Û–¾zBa›TX|Zç”Êâ78}œsLe˜2Z—ªþÀ! “FǔξÀUÏŸµ4î9®fàXyfJ²PB (V,”lÍûb4™÷Fƒ_y'ÕJ•ŒdaEŒ` ‚Å„æ-/ýsŒUÞÓF¿Á6ÁÙaÀY¥c©þÌií³z[
+²Ö =9ÆÕÌpæQ­f¶Òæ×Ø}7 Ê\¥{L¡Ÿ<g˜7¸¯+ÍŠÜíºŠ Ä(&"!fN7Ffˆ¤,ÐuæÐ36]ý±V:­¶ø8û„Úó‘Ô;€CÀ¿Î1­¶ÎèÀi
+˜\Ãŧ×Q,FádÆ
+ˆzO#Ÿ?¡ÿSá+g(ü/DF[ZZ‚’4)0ÔŠGN¼¾¬®©
+¾½»¾ÖÛL¥ª£¹jÕŽŸF³ã]Íûß¼÷ÿß«0ÌTZÇ´hzG Ê4Øî°† sÍ'\Íϳ÷¤¥Ð4·N&Å(™,H"þ(Ѧí»rZ3Ë5Ìêj€sSU.ðØäE7Ä>ViŠ8ôg„³Oj¬ ‘q£y±Ü9¬s<¹Y‘ÒúœœÖÆí}Z‚a‡6oy21 4ÀOÀâØ©ã¬sVïW›'Y xɯuÁÀ‚ý˜ڬ°øžŠ5iß}”Žá"L&•§gfòÙæÆÜÿ«F pX ³|î Áf0¤T) ¥%8‰£Dü–‡4Ö¨ú¾ˆfߪº¶û—«ÂQÞ Hk'”i¤ígèL“(ˆþ`×j“ñ´ ¢^R “`I!IÖa9¨Ùõ­¶žÕu}«ÝHÝ Òz!²±o¥4‚€
+zª²;ؼ®•¡´G¹Bb~»{ôÙL"ü‹ýeåËGè³MîÇRþ¼~ãFqq1Ts)F*H )‘¼7å˜6ÿœ×¬òrÊ.NÙËeuª³»ØåúÉ;ħ…òß® œê0%¥>uŠ†=)yÁÜv|ðϹúñÀÕœþk¹﯈F ±ôQ†a8Jn?²;¯£RÙÊ *»•œô²ùÝ8¡Ìg½róÚBx<UöíëWO·s½úü.“²‘=ã5yÑÿÚ{—Â<9xE¼wã…톯:¯«êL3743
+#>57óÁø'E &U“~^Ø·S^³²ž-èàr:*szøWïøy;ÔnxEÁpø•wßT:5ÿnØžrŸ3íNÜ {1Š$Ä"ôÝw~þÌ­P÷î^|ŽÒe
+w+?Í+ pM³eîEmõh…qTo¾×ê?£q€CÆ ÎQu„µÜ©²Z+@bÔXW²3U!Á Ž‘˜ '™Dñ©â¢âéµ¢‡·É© Ù£lõ¼Æ1VaœæÓ:;Ôî93À9Gî]GÃc@c›Öײ{0<ž’%Ʋ¥$OágŸHûHc×8gµà[p‰Rã
+ã™>.;Oææff!uÌ‚}ýïÚ¡b |¸‚  ?$óO „IèÛÄÑb|E&"’“ˆ ¸€p3^,BJ‚’$NaÒǘmÙ»²¬Ï-Ûÿÿ¶V•a¬Lg?þbŠl«LÄ0˜„–EòVÇð)Û/žäW€m~fn)Ý~®ý»Ã;37úŠõ«xžÿLªÀl ¼´„¡h‚ ¸Ö;[’¶ÿý
+ˤÖ4z†ï]#AÖ>Î9Æù‚k›V[F8”]( Î5UaÖ9ìû=²~}²MÀaŠmˆÃ¤Q)Ù{bóÖËó-ÖÒ¹zǸÆ: ¥VTpYÛk5Úîµ?ÓzkPãúØÐðô–­()ÊHÚò²²(+uçFF.
+èؘò=ûÿf¬¾­wŒhÌÓjÓ¬Þ9¡wÜÒ[ýÖú‰2Ëë ¨ÍS:Ó|™uQ놠&ÕÖ)-H.ì~µ#\†±ß©²iL`ÀfâÕ9'*-SÀb‡óH¥ "ŠÀƒü•üƒnWÙÿ®sjKSvÖ8r¹¸¬èä—kPTçÇ¢Òfbg¼
+Õ¦$š°Ô
+
+¶¦àÄ:BxÙÐܯ|^4òFõQð/™L‡A\ûÑÁ·Rø_õk[rjp|Ú3)ûh_^1¶¸¸petä•‚’FÏ Y¹ v&J¤2’¹œ"AyƒCÖì×ðûWTÕ7E#õ^I3`‰[^ë’í¯+_¢‘4œàUH{jFï5Œ|"ôß:5v¯~tu(²À,ªk¾ #¹4nÉùZ¾WÖâR×z/
+\¡OYçÕû”|¯JèÑŸu¥#öK£V`§¦€â¤¯Eà5ˆ›<bA@^çßøÇ…8ˆÄBd1,ôhùeO­ë…ãPœù~sCè ¥ü°GƒWN1pš[Ôè =—^ùÐo¯-e4>õ~¨íŸòʾj1¶¨ÞQìÍÜÅ™l%é(åDcOÁcDãÏDÔ ¶ÿõ¯n––!q&‹ @X MÅ
+Ê×[½ íCHÿà¦7ËÓèøv&Vtqøz¯"ö+ˆ}<©}ø…®±Dk ¡'@­Öã‡X°ò:ÛðZÛðšî
+¼êJq(#¨ÀB÷¿{NpùäÓºÑ;5£w›·WK#µÃw„ß6ÝnøàNµÿã–ñ»õþOªÆ?Ëõ
+…žrÝrá@2ŸUYñx8ýs•Åæ–MÁÓk—×¹~ýzNV6ÁÄ8‹ÅÀ8LœÇÀ3˜dIáñïžüÃyePi›[b—LQ±qZªQ~¬6O¨Ì“è
+Ó„B·ZŠX5u¨­
+šºæ[TÏmKZ#É´X¤°Ä2#6L‹ôayÛg*ݹ."6~.îx=ow/ëÏ"å´F4/2=RXy´-ŒtN.w|ïl”ÜÂNý}Ý釭êPœÌ`TÙžäƒT«,SR}XjšÐÚfðp´#ð‚‚‚¿“_æÁMw_Ÿ˜&`°e?=齧˲I
+6“%@Bl &4!²ñms$$øÐiݲ|é²dë´$ËV(¤Ngš™˜i(-“ é$é1H9„Á¶eõ÷l ´SÈ8CòGº³ófßξßÛýíoßÏF"‘÷ýÑ¡òÿX†Ç×L|•ýdf^Š¡Ù€@\¨Ã‡aÔJèðÎl8<ÑOº …x’F’̤ïÆy¹D
+ÉÊ^8gëös'ò™­îœ[°-‹ÍI!ç%¿¶!Î`GæÞøî@r«•æ°ÓæE–²õ#K
+ÃM©ækI³lÍÁÌ#ˆS5 Cº+MŠÇFGêaUë$0èà9¨jr€Î˜¢ãÆ!8­|Ýn“‡Ú["bÕÕ&ý€¢}ËÂE³€Äyd¶@Àb3v,^(‘ÀÂmíC
+…gÎœ¹!]{êþ!”hìÜ_ææ,椥“F♌µ›­ÈÞ“dêC–0²I0÷&·¹M¾8SÏ]±Â“¦àDk0¾Ý÷ÄÁ·Ù 7Ì«zuÛ‘;ˆlAÔae•Îà
+01&ƒ1¯p€ 2‡“[œI;²ºÔì„â¬}‰¶¾äÎêðÐÒdÒ‹L>dêI´‡h ±ù§M#GÉßÝ‹Z‰ÿ¤Úù¼gæâ|Þ’Ybdr¢®cÈæJÙSNPÙl"•ÿx~ÉþD`$[(ž†.À$/,ü¡öíþ¤6Ïì–žD )G^‹Å;zÚ-øs«ÒŒ"¾UvÙ>Ðb—rºêS
+jîU”{eÅκJ¿¬Ì-©è–ïЀdÈãp9$µ{ç®ØdT?vÀ¾sç΃¯÷sãÉã'^X¶Œ‡³„$E0Òyl6Ÿ`1IŒ•E½²{sµ™žá^—´Â¯,óÉö:êAÐ+z¤•n±ÈUWÖ´ÊXœtŠ"8Loñšƒ*h ¯éŸŠ@-ë»T8Sþ¤²ÿÜîà…²Ÿÿ¹Ü÷Ñv©‹C¸! É%kóöu7‰Àù@#.Åtý¿Óu¸*¨¬v+÷wkJ;ÄÜêýUU@¾ÇQgû]ðø¥S—®}õÙØ×Ê_Ø+\’*¯øavxµç.ÝŒÝ9äP•w5V¹§;`ŠM°™$ྲྀ@õhÑnjQ*¶b ––AP ¦Ü§g|2þWÌLGôÞÖC’„§LÄ>8z,•Ã#p*§Xc)Ÿ¥}ýåÏÛkn÷G»bî=1ç®h×[.ј«dÈYúí®ô–ļ¥ƒ®õÛW ØDFññÔXËeERåFUËÖÕ&µ²@ùýЈz‹^U UoTòuÍzh
+ÇÓù.ËŸ{þHø}¢ÿSP¾ñ0Žßp&ÆÁVôÁCý`yŠL¦.'Ož|½hÌ®džIr16 ã<"ƒÄxÆzáéÚüþµ—.Ëš#Š–[òæ›bmD¢¾.U^‘Ê"ríM…ê5©v@i¼)o8Q´~×42"3FÔWÅÊ[rCLan0ý¡N»†Ê¤°ù¯fg½¶ø>ËÀX&±îÙÜ/ë £2ð´ù/b­½ èDí;CŠ–Û M74šÇFGcDl
+ûo ˆ‡ÿPâ¿yÖÿÉéÁ«¼¿NÍ8°£ %8ÇÏ)6KDèHd•®tü <Â~yž­±¸_+v«A†ð EÎ
+;®
+8m
+Í:•[ç×
+mDž†•V¡[)ì“2Í#=å¨/qkª-êÄÌTHD(‡ Õÿã›·"áý‚Éa{À’ öÁ…Eæc04ûh¢e‘iAÿ ?}ÇvE)”O³É$¦›… Ðm2Ϩ+ŠÌR
+õ„\w¿Þ°›¤7s œ¦Uqè2)1åm£õêUÛœ¬õ¾R7.ÑCoSò ÈX8ƒ¥áošÓ[·á”&øJoIK{ø02›°EÂö/8o;6X/ͧC¡GK‹Ùû÷Rèz|m—’¾º¡.ªÏÓ5€}
+¬T#
+Kâ¡D"—Kál4‰LËÚUb“õ7æêWZ}D.¹Ø!Ù%Åýª3I™KS¨¬Äø8¢i’Jß±“¡B¸¦D‚üE…c¥P[KkEYù?îÿ=ò‘Y†‰q‘áëÒ äãPÐãóìßývª`™€
+pŠGÆáQ8Ž%lÚ·ýhCQe¢Ê,Z$ùnU¹Sû^ÞaÅ)Šâü‡ýzjêJ
+Zl€  h<!äE
+£)!hÄ|ÂðxSëÌ)ÃÛÕûc_¡P^MCÚê»Ö™„ó™4:sUmá¹~ç_ø4‡ºõEjï+ÓBvIÄ–;Ü‘ò]
+µK‚ígÙA‡8à–ϸdSvÉdG.€d!Lºs¦:Îv–L»$‘æ³_w”ÆâëP^ŸÄvâÛ*O*ôÇ æ4³1Íðýh¤êh%½®æ3†¦wŒ¥)Sv¥$°ã)—'À)ƒ¿»÷¼èñxf—Àÿ,²™`p ío©ûÖ秞™«¤¤$òDþ»Êˆê%º• ¬¾áÉÑþð‰ªFýÓƒ èahNð˜O’Â)x[»)%~{K^ñ—
+ô©Õ_iòW覴Æ!~Tky‚Y~Á®©Ö˜FËkÆ•æß”–mâ
+ËŒ¢öAe}ëiIŸ 8Ÿ³¶ÿŒ$ ¸úM¹î¾©îŸÕã– e㓺Ÿ UàÂ4¨2LiÍSïéü•æq]ó—u…ûö÷Kåÿª6Ý«6?TXnHKI\°=õò®º–+ZÓ´J?TeQFÕæ @™ÔÖÁ3‚C`'€dPi©I;‘„cB.Ib4ñûþ`dή
+ÍÿûC{ÔüÓ0³‡"‘épxø/KNNàsq!%·p6¿zhµ²
+i÷ ÍÝ+¯ö̦à¤ìѳ¬Ù‹4z
+›Ô‡O¼÷/$ÉXŒûÒkQ\¸vSÜ÷åb5"î½%íýºÌw«Àõá®#™\”¢l
+çR;Er«Ræ­–tªÎwWƒ¢„XTÏwkÏ{ªÏ{´ 0 ¼yg£‡¶ K®£R
+{º´pHêTK¾~®Sß-p©á:p¾Ô£–¹µr›r :â“|‚à‚F4¢KÓ/˜muÇMõÇ,ºmöá\
+¥!郴o4¯ÍO›ï¦?xËþ©éh¢4Ôèq*äÑ"’.~c׸3?âÈ :e#îÒ1‡|Æz:Üq.dËÚÏ…b0‰ß.r̸ ÒȈ]ðäµKƒÎüÉ–ìQoÙÙý"¡PHðY"6-;˜W“¦7¯­9¦–<湞`7§Zªß¬1Ÿ4©ÒGv¾‹Å¢.ŸO°X›’’šê™Idæ83\áG<ˆZˆYKªpÑ€æë¿ÿãRéEøEd
+#(Šˆ¾š¨pæNÁÏÖ¡¹=pn`<òÏ­C¸¿ðà½=}}9ÙÛ„ñðÊh’£”-[…†)_?úi™f´ªqôŠnºÊ4Qc¬
+GtM#*ã„Z?ª6ŒT•u“j lD5Š «LCJ£/¿,žÍÄó¸ÆÁjTU0…Ì4Šøýþèê›·å£*à‡6צƂSÌÒ‚É>øäSIpIÑV>›^OnJÍD¬FW½šŸiòE5‚4u! n0 ¤oæh{78d­ÁƒtxŸjoZS A…{7”T0’±w­(Óáñ0.+g“GP<b5œ~zy­ qÞ@š~±&óÒó¤ÄÍY%€„ÑHk'bëA¬ Ÿž‹×È
+k/bïAš=ˬ]O[ájNÄæy¶±;¦Å‡´µ>g1svìc'ìŽÑj‘ö¤ùWË­¾•-Þåõ.8ŸñU{?R×µ
+µvlï?m´>—‘³)i;²y ¾%y<\˜”DmKØHdzH_pöŠ, ÙÇ¥ºàÒtTH]ʼ®ª¬º2*Y$ä @èPR11ê[Z/%$.Ð îA30‘™âAL;°ÿ'¾Þ¾)ÈQ*g‚Ìšž]^0Jp,2í‡ÝáÀ_ÿüÕåÒR”eÐH2‘ã· 4Ê%1k+Êùq|ÂÁ—qÔnPw’‰çy*Qæ½)ñݾpí³\ßW‹Õˆ´ÿV~ïYß]YÿÀÏ.7“I"”L6I/ÌÊwUgyÔ¹]à¥Ü±HÝUŒ1ª‚NÜàx“åPÉUòN]^§VÛNe±W/gίZè:ðÓ@Y§2ÇUží*ÏëT9µ%¶EÒf‚K ƒ‡alÐÍÆLiúÇkDŸVkI±SôW2 t"Má ey‚ëׯGƒ!ÄÆù™3W²6£‰/yãÐë|‚‚Ç^ÿ±¡xÜ^äw‰Ç’gáŒMðd¸/>t;ä B¹ ‘)»dÚ)_H#Óž¢Q[FÄžnË›êº0è*òŸaë¸ÿa¿Lc⺮8þ
+vÒ~±00ËÛgyŒÙ¼à¸%‹kbÇiœÆÀ€mêZµ[Ç,30ˆšeÞìû0ì‹`Ì ®ª(_Ú/#KÝ[/MG.‘Ç-6Ë°ÌÊô<ÆE|0i‰©RútçéÞ§y÷{Ï=çÿ;¢­R·#Ï 7š³¬@#¶#vs¶ù«FS¶àÇšeQ¼ªØíÂøP Fä:üùÏ áÀ"l
+è¾ÛœH€DªZÈ›>ß—¨laÃgM768·§¦¡aEDx=yòäÚââqP
+ßÍÞWœõ×XÀ’™Û]•ùa½9 q.Õ[çµöiµi¦V?_gôh,OÍ•mAg{Äê=jý¼Öúq½Ñ‘"%6ˆÃÅR>WIaxÂÎ$æÌ ™`!ìÞiâuÓ¼Ú6¥7X+,ç¡¡ÉSo]`í@J0ç, Ö:
+ÈXb£êÿ?×Ü,‰‘"LHc"À9CžÑ˜g¶^WµíÙ6¹S—eÔ3ñ‡‰q1À%H–%{f8˜ ®hjŽA °Ì «oU
+CËœ¿0h$3™\t—øÜEÁþ"O§2Ô£Xp®Gëµ@Wq` p¾§Ø×U4²ä*¾k=
+OH¥ˆ;ÒÒðx\ˆ.¾’‘~ì‘P0ô/¾ðã_dgL04ýðQUù Ê$\ *[±÷À’¾TØ£2…XÛçš «¿Ÿm\Ò4zê­>ÖáãôÝ2ËZ¼zç”ƺ¤i
+æ?˜¦³}ûÿö”*iCmfÒ $S“¶s#PB§„Ò„…ær¤Å™WÕ³ r:M×u! ç!¿/¨ë_ÓDÈ×uΑ#‘|¡”,Þ•ŽÔw -—šúVô,JJ#ñx±0— 1K—ðÃqnZÆ lUXóù E*Z‘ºÞ°ê¤¼s^㤱ã™idÚHp
+xFFßþÍoKŠ•Û¶lÑHñ¸eË~®É‘$¡¢õ;ò\·³z>U¸î|“F2z?Éìù0çÒǹ—?‘;?T8?>uñ^¶ëwywä}×—m?K¯âa¤
+GùÔäÄäøØ4HæÜü³‡äŸ‚‘üåŸ,¥˜ã×~n¬rëì>;©dúusÖˆ[ÅŽ«¬¾R‹[iÑZ¡•2#Ë}3TjÐ[‡ŠMn¶öjvÁz)Í‘p ì¼‹~Q„­"зç¾Ô9†Jí7Ωâ)BBD¶e»¯·€FÔψ"ë¨ÖúE‰aÄ\ù¨Ä8®µûJÌ0òÁ2‹WU(µ?2X•Û7Ç IÓ8÷Õu«ï)ÿ¤Ò{-Õ#ZÛ€’}PVuåðIÖ$Çæ$ ˆWW9VÌLèí¥†a½cDëð0C*ö!cðºn²ÈÞv0KMcT 
+¯g &@òïïÕßžs5[n3ÉåÎ7VÄ !FqnãÄ¿¼°¶ iíAê/…Õôͯq‘ÐЉÔv†Õºž«êyœÖ§ûßSm÷¢CGP>Ëã}/óðw€Ž.¤Ñ9¿º}iŠLÄK!sCœ¾pøÐwíÕH£ iêDê/
+(H|Q˜ŽÀÄ(•ñÓC°u3{p–3@áP KñÊå7 à
+i
+z¥0]«8ÞZzÜü-Dc¨ ¨ãEH–ræ%lÚpøì©Óͺ³­¦lk‘ ^
+÷E$¿lÅÀÀÀÌË
+þÊW=0×Wqv͉I(£6®ß@rÃ)É¥¥?9pª°ïý³Î÷vÈK#âÖ
+(r«4êaYM@cï/±Ü(Ð8ÿ\eSYÜj‹[cVqk-#FÇ€Ö<¤³Ž–Ù†5íC£õK­}PY®Û±KIEJ©(EEœ—Äøyn¡!`¨ô•0㥖Q¥Õ­©ìÙæu“SeÔš50TÛh±îÆèe«=ÚÊkyÊuBz))ŽŽ†õ¼/MšüÐÚðÏý%~›,ÝP^›i¢©½ç| NHy8ÆçFñøß%9¬ªiêEê{êóóë»Ãº‘º®`¯îžÉò¡D§¡\ÿ|¡ž/^F‘/†‹£Úk†ËH󹿱_æÁMÜW(3 ÉбZí®vuúÄ\s¸ ‡ñÍÑB°oƒqbcÉÖ¹º±|àãS²e;¥“Ì´ý£ÓvfZ’à„’
+ï}õu(ôBƒ0 Ðëcîÿ»s²üÂ"Ãd¡"ӹ׳»ûŸE#Y®ÛE½·ò×ò»?É¿rû­®™ÎOK®Ü>¨nDðX‚ D$/E¶ì}3½±<¯]UЦÉn®
+pxòtÊFKŽÿb†»-o¬%g¼¥x²)ËÝœ>[«ËmÉkɃî@#æŒÉK¹»7®€‰(!…„Ri@:™Ö%ëaÙÏz/xKR¥-¥Êœh5&˜ÍA7î6š’†D= 1MoHÕiÕô^e h0%ŸÙe 1î5êõô>ƒ!Õ¨O Ó”Iåqò8ˆM
+“[o{¨£?È=¾‰ ¸[wô)Û![ƒ¸.Œ`óª÷¾_ÎÐËŸUºsåH9ÓeH¡/7ŽiÌ•º1ƒýÁiz\U9©4 iL£çlwΘâär\BJ JÀC7oŠ
+¥ÁŽýHfk!ab´)X"ÄN< ¤/9Eñ¥|’çý8auëÜ&ñVUûüZל:« ˜¡#Ä34rhZ¯o\€ñš‹2rÃJ
+¤äúhÁRŠcòµ¯œe¨¦ÚÉjêYPÕ:¯º†eUv°ª€"¬ VUë„"Ó‹©uL_ÛÌÍæn–½UçœÓÑù=- ÂÆù$%àó82!Ÿ"[âã:αaŸoòxÐóMx¾©@0}øƒÊdè(ú§\mT&\± •€”øÜ(
+“£ÆÃù’èW6%¢‰G‘øD±‚`‹å¤DL
+„ìE˜å p-ÊQÈC
+ý[6ÌZ_}y7&*Z&’H”@å‰o[ózþÑz+Ãu/½ïË"×Í’îÓÍáëâ1D-ÄE@’‡S"’:UR
+ÝCã0ûl@ŠMJì ›ïÀŽuõôŠ…B9ÆÃpq|VEŽã£‚¾›Ï¢‘Ü®;…ÎÏNt|¼ïæÑžÏ3/Qünþ…÷Qk0\*# ¢äJWK‹êU¹íš\‡:ÄY—ÊÁgÚºÿ=NüwiD—¨³¦Z@¯AÙM{-údƒ&Usü‡'b±h)Oò„¢ÜØ•1ý7ú}ž Ei$d¿}
+—¡K¶J^í(;0è,iʺ›ºø£‰–ìñÖRwSAàÒÁ÷µ9+9óe˜
+«WiTêGÔ&`’!µu¶*PZ û Ú<ª0ù´öÇj›G_ã-3{T6ÐýÇÚÊ•]³cWD7—mIÈôUеá¾B1®ª*Û™*
+`$0àõ˃ŒÁA8l!&JÙ…Š%ÕM¬‹í¬Ç\»‹Uãb5:YuÍOÓHH¦5}/Yí¼×·E¢+D¨ ‘`\9'ŠÇf qö²7^.U.jèfÆiì kp͵7Ï©uέïšÓØ^ïaUµ±»^Ô9ÂßúNÆŸ\.0;æµwÏ«®zéÐ[ìek¡¢8!0*åarËNßÿ›«?ö…jIÈ;nŸÇë÷2À´@05MŒ¹á424¸ƒ§ž×é‚z4†'’#CI’/%I—½\X° ¾™Åxc˜ÉÆ;”GŠ×aKQDŠ\^¸D‚¢%$a%}]=0Ò˜ßúLOCÈó³å³Œé;ÅГB§ÙDz@z#1be|ê)絬Î/
+þÊ~Euß
+ÚNÒv’`ßÛ·ïØ –C1ˆšàˆI´ÕaA¼k¹A´TaÙewYö’EnXŽ]`YX´“ÔNŽé´¤MO@ÃŒÑ4­ÚjŒÜ×îöûØ”ÆÌP‡N&ù'?Þ¼YË›ßû¾ß÷ûýüz?É·K¬™ö᤾Á<û@¡ù½¨¬rNÀF6žˆ  ç
+'0 3îœrw“'Rj‚Iy(½=¶W_~…í‹B*ûú¸"(ô¹W‚±Ž£»î·›
+ÒB×^*”Î(«¡õ(*Ge•#%
+í—
+ÕJÕ¤Ôø‹°õ~Bÿ»}â¬bT®P¬}P¦y¨ÔÞ)“( _žZ²Ž;&Kk¯«
+6lø]QÉg%•Î²j—Ìø°´ò†BIâ(â×~)»]Z¹Ž0Ü”x(c{L€êGþlßœµ/Þ-ªt¨ªïÊcªÓ@/˜$ÈíA©vD¦›TTH €w䆒bE9$E`LåìïêfŠêüâq—>¨Ž…FðÃxÒp‡nn~¸¯|õazzj”ÓõùýFmÙ&âá$ÄzÚ3dësÇåM¤±wymÿ²š^RÛòuŠ<v´ö"5mH[ûó'4^;biŠË&q6ˆ†myæ´1µƒjV4Ú<k;F3ÒbEáÎíHSR×îy¦ýÇà‡&+ÒÐù­id! ‡ûŠùœ—²ŽˆŒgqýhÌ'Ͳ9Pÿ1ŽÏñã…·>¹õzŽaˆ z-fÆ5í€LŸÌ Å5É„Ï57Ŭçcþ›®ùJåêï6‡ãE
+ù<ŒÄq,!ݳ©žyØFÀ[Ò`EZÚ¤RzGî¦Y"?Œ„ ,ß'ÉUAo¿qÞ1=ëÆ¢»³/Ðñ‰ÕrÑW;/'3½?þþ|ŠK`8œY•|º]Ò}9³ÿÓÛÅ\û_r»?HþõPZßÍDÛ¤¾Yý×$M¿Ý´W‚³É`6Ê£¹  €Îɇ“>üóŒp_` cŽ Š¤ó´hËþÛåôÞÅ4r¸k0÷Üõ̾+Éö¡Ô³7%—¢2Õ,THzñ|}¹¤06'-½SuÈzJb>•×\Io—¥µJ¡ƒKLÌùHW…Lò½sâ»Õˆ^lÐDiÑÊŠ=je¬J%®ÐG ‘Fèà ›ãV±iŒàñhŒƒ­Y³fèêƒY¤ÿYKNWlÌ.X P` ëc]ÚdKö\[ªÓ?cÊ™iNÔ‘³T8M9S© ‘9Sö¸9¼9ý¶>‰Kâ ’ƒGm,‘ƒF4{5 m”~±çÒ‰Uú=*itQBøÁü—üQŠMS$-€Eˆã£Hš`ï|œäå ‘í-~TDVª£4 08͸«F½M«‰Ö•Ç+^qŸÍÐ|š¦!ézzz¾éÅ#§ëÒ…‹ ‡â! ‰€p<’FéÕ<œ»E@ôäïÿÂ|r¢%ÃeNu´Äš²f[s%–´ sò¬IâlËŸ¿˜tµ:w5ú Šqi~ „åݷλëÇ’ÌÖÔÜ™w
+&>Ê´ð¿#ŒÜ cRøÜ0"ÓŽËõñ‡ƒ)¶MpI¾?›wüXá4ç_„c~éÌ9—þw !œ°{
+ E}Yn¨7ºé)ÃiO
+D8)ä¢D
+HN@°¸8@J(Ž‰yëw˜¸1Ùj›—ܬ“9;'FÓ¯¾1_ö ¿ð<ûŸâz‡ÓHFÏ û@¶õJºíFjßP–¶]@ÒÂY”oDÜk9-ò ‹,­­¸À\v¬¥(’i.ϲ(˜‹–²T³ôßì—{PSwǯ¶jÛQ äuoî#o´E[]Á®V”÷£¾f´I
+;Äþ¾ÒqÛñ £ìwÕY‚Ç‚©1 "/%C^X 8D“­Óæ4¹dœ†l£!Ç ËjÐekô9*]NðoäoOÚÆ¥á:ʆQŽL&`R|Q °1F
+P&$Á^,~BqÇùS×Ů‹BÏßËÞ»R:tI2|M<Hþ¥Ô5&t_*ö\~Tî;5rñç¥MÃIéG)¬8Ey`&eDÑ)‘l0ÒÔ×ÒΞ9ð?|o`& ð'Z°'7Žà²¨t÷S÷ŸÄÃ7¢éÈ ‘{´dpPP¥ëo‚WÓéÑ4 B‹ß²þT§¦¬GYêÔ]ª·ìµ’E¹]%±*Ä}2‰U^ѯ^nWHlµ%6@‘2 $B;é%ýŠRÇ÷ßœF h »ú\í·£]¦QÓðó¼êé‡â±8F$•ËfqXXƾý^¯œÔtÔ»Ïn}JÀ0—‰bk'7òF‡$<p<Ð-
+õŠBýeã}‹CÒíÒµl¶O´K§úOLÛK‡+ ‚Çb±¢°‚Íù … ¦¼¦Æ ccV£>[ßœm6e´ÙEž¢¶°FšY¾/9c=œ(@x„Ô–Çd‘4…a€F
+øªÑ«2NÊ SÊ&¿Úx[Yÿ¹L§Ù“M8`FLÈãqxkV[ó¡2eÆË5š])ø À! ¡ãlœ 3¶3pKÆŸ±Û/ûÎhäLž>µùŽ\}G­»§5M*A…ù®¡ÙyàN*XMÛÎá;»§0>¨mú¢NTSv‚…3Q&aÊ;ö×wë÷nØ’°zíùªÚO5Æ)y¢Z=®5ûj Þjí¤®ùA™ŠI¹ðÉT½ùÃêšô˜$ÎKQ$í`pbB|pb2ä÷ÏM†?Øwo³áÇóÀ| ¾7r¦3h<ÎdÐÁH³¿
+Ùìx4
+erAWþì²Ó<e]°~v&ZÀæ_{†á™uâ0ÍÆQB°ºÑDÒH·ü d¢,sÞqÒr‹ûG.¨ç 'kŽŸD^ÙŠ³Q!è4&‚ãLüpá‘Ïo~†£Yß Púàlp&
+™—ÙW0øÖ˜Iˆ‹áÁÑ
+•ànH;ZubhLâ-ü xh¸Ð=&9sEê¾*uJ‡/‹Î\ü³tøú[§?,u~ðöðh‰ëJÕ™+âÖs›³Šhœip‚È„Ã(L…áMÛvtôÚÿsï>Ør&4öõ¯&máÃÌu8 e%·/q__ˆFJ\c'
+–ªO{¶ô–”ÙÎ<ëˆf{J I0$IÊ$r՞˫-®¶æ@µ-ÑbŠ3pI&Ãzÿé¸MñQÄ % J‚"LÒÅ€è§e(,Aqqáøø¨ó¹Yÿ‚F¼3ž»ü¥NcRd#¥Õ×$ØË*¸c\ùÁ
+.ÁTg²²Uª1äLIfãѲÜ]¹ëáðH\Á MãQ›"ûz„ ðóPˆríÚ5¥‚@q<Å^[÷2—º÷vë)Þ‘Ë;³f;”¾¶Tßù“³ŽtïùôiG¦§3wÑñq¦ñá;Š\êD)¼šÂe
+i˜bCÔ“'Ož]qzzõB™´FâAýÏOÍx
+Ô§)Œd$Ž£2GDû¢Öÿ2çìãŠÆ{Ë\iõRS~º”ùëÕVͬ •÷u懥æiHóŠIM™§¼Ò_Xu×ØtpË60Â8Œ½Ê`UÇNþËlŸÓY<Eº)wûGº‚=»6“0…ÓräèöïþB]â-kòkª|%/J#3ÆêeF+xé¾ÉzO+ül‘ù±®òAIÕÀÛÊ×XáQ2ÌHEýÉ9“åÍÿÖržÓ¯³
+ ñ –‘‹è-–®=ùúÖ»¥ÕJ«À©žèÍ÷
+Ã07î‰;*÷ð»åã瞶ç¤Hàc |æ+`â¿=&aLˆhRºe;ÔÚ" Hs×ò¦>áÆ›Lú 0ÝPc·ðgs?dwu\^ÁÙÅû’(:œJÃPÁÅ`)¦pœwúü “ÒÜïñù| Z˜¬Vs ÛÏm~¿pƒš"#ŠÂ‘ º‘ã‘;ÎôýQ龑9pCuq\yq,Í=’58–=0–Ýw3«çO*÷HzßÍt÷{ª‹w€I2úGTCãi½#J÷_
+€ê¶3å,À¨YË°ˆŒÆHXp.Wäe«‡/üô7x·k _ΠŽÁÊ"b{lAÿ»©—?XL#ªÁ‘‚¾÷2Ýcim?ÇâYLª#щ!+§Á¹Ñô›Þ|ýû?L̳+[5ÙúW™ÊeÌp–žì(IëÒ*ûŒù­Úi8Ýi<Õ©,Éu”
+¦b¯ÿì,ûU{1MàùyP 1÷ô¨öù‚ô`&%9‰†¢Tˆ*jê€Z¯,®×êšÞ5µCãPÇrkû+un¨ehemïòFw A]÷r[ïêZ÷
+»ªíª{Ö8©g™p<ìå2Ó*[ãò¸Œ
+E™ðÕœªí†ì?YYwj[E@‡ûW¯·öBÕÎeíƒAzÛ¼¬þdˆˆ¤à˜·¶ßŸœ‡sy^˜ƒ>èϘ±$œ,x¦­µ^Ž,Ö ”ø4¨Õ Tï ƒì]PC—@
+ý¼ç;÷Ï;ÿ##@1J‚×ÐxXÒ™Úìá[ÊáÑÌ¡›` (¢Í»pKåUõŽäŽæ ŽfŽg Œd¿¯z?sðVþÐ ¥ûÏÊ῞轕Ú5R8tSÝxå€Êùí7DN0ˆ$S°XOLÉ)\LJƒCe(‡HhfK²ê쥱¤ÞñÅ4’54’Ó;–70º-£˜Æ)¹øU ]J²a” ^+
+ÇhLþ‡ý:nâºã
+œêüU®]ñ­3ã9[Š6# b,Æ€FÊ÷é̲ò%S$Îd€~ËTWo‰åA¢LRÅmÚ/ æ$8C
+D E‡¹1ãy2ã™ß¾m'…S,%…­‡.ñõI{þ”5sÖY4g=²d´çŒ·ç{Yð!г®‚„ÍábVŠŠÂ®ð`þÉø“G¶‰‘"![ ˜à““!Ã$"6 KHHp»Ý††|Š¯Q û¦¯O~&''«««9Kç0ºpw¡EVi
+y*D©W*¿æ^¤¾s%Œt©pÕÁƒ¤ÙÅ›F 4rÖ ø5ö!õnž+ç*ƒ)`ÿN…Ñ’Ø
+ % ƒâèé§Fï?|¶{òx<¾ÝÓÿ ÿ›zç’âÅ"†£Å ¶îuY±ûJjÏ_2zodº¯gt
+Íç¹ëÓܾ[Ù½×Ó»®‚FrÏÈ{n¦»?Ëè¹™Ù;áv†ë
+E¢`œA™õâ ;¢÷¦'déOŸ5Èú…¦ƒ–éÔdt(}í[gÆs¶Ä’T'Y
+gaÜHJ»O 1Ä›—ªÝ~a¿ÑüVEõþ:Ý^£þ@¹"Y)‹Œ A¥ád(€Ä«Ó©!zaö\þõï(\Ì¡„˜”Fúp,vÌyzÂqÊkK]*E¦ÛäC¶<o[úHkŽ§-}´-{#K
+¥°Ð‚(6\ÄI‚%bŒÃHL@s”P
+Š E„ @»{o{kÛý¿¾Ð±¹Yø[Ø1}Ý!ÏöM0ó}ËÁ`0pB§„4I¤½‘f”UXâ*õqå<ÉdúŠfS¼@bØg†÷Á*ƽ&½LgH6œÚb½0‚ &Ca}à "êDaÑÈÐ0À'³ð
+Ái©”}…ä„JÍ¢ir½ÐÛ±¬Ñ‰´º‘æN¤¡}¹Õ…4ö -¹.ÄÚ¹¢¶íÅ
+ëÊZ«_« iéBšZ±˜}Qß héË'õHmÿªת¢B½†‰
+Ú&Cl6¤Ý4Ù×XKÖˆÕ4»_ªïCÎvúëLÁ[ˆ@‚#q#?¼x‘ÿš<tç§4âõ,|ï§öx„@m|vÙ³¡ƒÚ¸uó–a1*@*Y^[³ºþRßågåá…æî8ù‚AkèäûÙÒ†´
+šé6íô²ÖÌÔ#Å#Ûô¤‰ ,×îº}^6!¶S3ÁIÇò›g~óλû¾ïï}Þßóý~’—Ì}õà®TQN±ñÔ‰6M¹]%l‘ òòNí3ÇŒoYâ<#9¢,‚¨0¤ëž†F2Uºƒ:eª
+üטnÖК߲(ÒU;×îä,g'¡\¨‹¿zõCÊÐ……@Mä$üˆÃøQ8ê:°ΗFf\Å#®ãaWÁ˜Kî(ú¬1Œ#Ø(I `ú<
+V3& @ž(¹ö‡«”üÎFÿ«*OqX$¤Žd`Xs%‚og î£¹S›¦« “2Ó Â0,§î+ Ÿ+A±y¾.?¡¬WÖ Kô~™y\l
+»®
+zî÷Þ-î¾Qæ¹”ViÞU|rgÞ‰4þé7*MåöŸWø®çöÜ8æ½UØûD)ðõ zgköq.|*’}jw×ûTß Å?¡
+ àÁˆÒ<*1MT[¾-ù—B–ÖÉZ³7l-ÜÍâ~¦© Ÿ6OéG•&`¿Ö:Q¥«Öß×[úßof'Ó ¤Ï”vxLÖè—ÕIk¦ducjðX=¬·„$5SRó§*àÜ„Ú2¢T$†kåU;àSÐãˆn<{uåºOÿþ@RØŸÓáG ³Ð†fBáàSûíwãiUé³åÿÁÅŸ*b <‘@0ú†M1–ší|”Õ]ç‰@HtcÏ© ÉöB]­­›Öܱ,=‡½œA&r^Ø»‡Ö裵v-9SKb«Hªd}¯º2º¾cqc3­¥ƒÖyŽæh¶´-´ú¢›.<Wë˜?øh6'­É $€íËJ¤ãЂaóâÅ‹s­Ö~<M~þ#‚yé7¿‹äáñËItifîóg<Ôš=‹ë:æ»þÍ^šµcqë9 ²÷‰#WCÄ#¨~$%%ett4ü5…fuúÊð—* tÔÖÒÊäqúŠdœD’·;.½Óû'¾¯¿¼ûú“¨àYßwï½®+)¯g0Ê8lîàÈHà¿6äccàÿ<wÖ—ÿNÞº5k1݆]
+’Nu¸1Ç'°cá˜K²Ñ$2eûúÙ©©‚ìÃ2Qi›ª¼]/tkÊœJ‘K™ßR-ð(øíR˜…ŠB§ä˜[)pH„N1ßQ%쒵˄nõ1—¦¨MÌp¬S#ð¨øv‰À!+u(D-Ò¼vIA§¼¨Sw(vËŠr¡S^âR¹¤‚v9Ü“: ÑAÝŠïÃæDèVŠ<š’vU‘C–%"Ž1 ¸Å5d©Li@ÿf¿\c›:Ï8~ÚŒVݦQ”Æñíøœøœ8$t…M]é: hß’”¶@Éͱs§„PÀ—ãÛñÝŽrqâ;¡$ÊØ—}Y'µ´ÝÚ}˜Äèè&Uš¦iåâ$¾$v¼Çq×j¤ múꯣ£s||Þ÷9ÏûüÅ.µ«Ì2ËŠl¤4/«Äa“:A.‰×)pŠÝŽ½.›Øe—¸Á£óL²ÏÔ/=\ͨâãñà@½ôî%(µ°>zÿÃrŒ‹38 .ÄQyMÙgAC.ràV°<4R%B­Éh_Úß³R®æ¶‹!Åb 9î
+=…¡|
+—ƒÓé;YxPÞð/½eÎ
+­¨BxõÚ•µæçwãQåÜR:ßžŸ>Õj;ô¡¥ìò§7mYŒ!#¿*žx|4Š'‘¡àªT0:±nh>ƒŒùaÇQ{£ˆB§o®^ç !#“ëüSšØ°ï0¬¸®ñ_xìÔ42üë¢óOú"'À»ÈðE$paÍ422Œ†Š
+KÁk»õ-ï|ª<ÿ…jòšòÿFM}Þwö¢—Ä<.4fLZI ¬-sÿá«°@ˆv¡uM%’¿ÿèc³Ñ´sûŽ<“`e~`i6Æäà(ŠÒÀhlf1VÂÝ,
+)HV‹O6¬X·/ÄzRU&ؘ
+´Ü™xûŽZ0Nx#Ž³h €QçatŒ¶‘É~¶˜y|—ô½£šë_Âê™ÕÀ óF÷j®R;òÒ¹ǜڶ@¸
+h;»æùžF†§
+ï-ør‡
+“¹·Â;ÚEµ £ðiXÕÖݽÓ6_¼Ò9ùyÏ™kmÓŸýÏñãµ]øâØôùÏïâq¸8eƒƒÍÎÜM/e¾Ių3}+è«Í.9'æ.¿ÿÁ€Û#“HËù‚Ò
+D€Ëæ@©ç3°r4„t#ŠU ¸cC2³¬õÔõ%¬LÄ޲煃Dg_Ħ7´ÅLŠpž:Nªº3ªWÅŒp@h8¤+fê>mΓIH§ˆê{£DWH“§‘°N¹Â­¢)¢ë 뻃º®€Nz"z Ðá˜ñkné‰Ìà\9®†ã>uP›ŽðÊ*mõ6»Øk­óÙåŽUd³É¬€% 8ë¶z;<n­P±æDnÚ=ì’xÔÒ“U”ÊJŽâS]]‰”\N-g“¹lº¿ï……R!hl\À ½¶U4ÖäB͉±7s‘Î¥¨j.Ú’ž8¼šÛ¦ÃmKÁ–<„TÉ`Û©æ—9T: " Öï~ûÞ¿Ù/Óম+Ž‹J–Ž#YÒÛ-–äݤ$,i'I Æxc f5¶$ïÛ„R%½gí’my,k±lö&¤éôCgHg’à~i3ÓÚd:e:!_ŒmŒ%yQlp€‰gJÒLó!gþ#ݹÒ{ï¾{Ï=ÿß]X5xÜ7¡‘øƒ}w?+æâN»ƒd¥†)’”5?¯wxÛ
+;¹\Óɼ†7Ö¼ú’,‹M&€1XJΰ*šQÃNb`›b*…$M·©ÌRÈ»¶z=Å^~{Ó¾W÷ç9*\‰J%$ƒjÒUŸüíz‡»b) ’¤¦È,¥ìžª_y<X1yºt<¨TNë&ƒµSáÊh¿€dº·l©ù‰ÊÆúô“gáÿ3~íTÏéþ#£þcÛrh1+&•!S4)”0)CF–¤å¼¯?~Óìµv€áÞ=aNŒ=K¹öƒØc²¹u‚s59î1£3rÂ6ivÆ€§Ccú9O”k™lrÆ8{ÄÜrÓäùÌ`7ºâ|O®&!@_¸Xœ­bpü .&PBÉ*9 Þ}‡sÁ%Ñž¹&/ lÄìü¶iÞk¸ÉrÞ‚kÐS
+‚¦œ`2VvxWt^\Ös^Ðøž˜F–u<ï½,8}îY£]‚(9¡J(B~õêÕG†1ÿèÇÌ::¹qý AÁˆSi'Õ{Œ½UW®—ÿæ†îìÇunT^üûÿ?“öÒ§G‡Ön;HÒ2‰h(ôTg'Lé—°÷ø%’ó1yÀ$‹Ÿ
+ñº^7ج ËBMÚ°I?ÈiÃ\Y¿¹<ÌjBü¢ªƒœÞoÒõA€7 né3Tú
+5«’*±®R°8ƒ MЊ :ýX^ƒc«Ç³­Í^`¶ïrXP-RgªÒ¤R‘\Nggf]{ÿƒ ëÒ䌗$Mg’â/ü'§ÛwÇk§ô±`ÅLÝD_Õ¨O@2ÔÅûÊ—šŸXßÁ{~ýT¸aÌW5¬ˆõ–Æ|ÚX°fèÄ>FžDáêTB%ÅD?SkÞ)?2ìè¾{²%fò6L™œ1ÞDÖ<¶4L4·ŽrîÛ¼{¸Ù5ÌÙo›-fÛïü‚w íãF'˜ò0çs´TŒìcFþ–¥ýxÁŽýkÖŽ\
+„38&›[ÆšÜ#Ö.Û¦\€U•"‰ºÃ ‰âž¨6ßÓÈw(î›Ý|ÍŸ‰M¿ÝxTJ'³´Z3r„|®T/híYÙ1¸¬ëÝ%© #$8=(8t÷ÿ çÒ³m¡g´5?ÔdÂUHiN
+ŸÃÓhVõâjW몮÷V¶÷-ë>õôIËŠz³ µKà ºÎ |¿}bé¸ h?³
+žÞÄÖ¿.“Q(I(%äøøøÂ{Ám‘%¶¼ÿ2à`6ó
+)ñ£¼}GÞùKéÙ¿ê/ß(½ðçÊË×kÏ}çhäÐ…ë —>.jt‹h ‰â)ªbÙîŽîEÒø<ãÑ,}Ø×W3Ò8mf.þÏýûÃkCíÝÝ5‡íÜ©P«%
+†$Ãi™Ó`L¶\MaB tªr‡A8hÓÍ•!S]ˆ«ö*ƒÜ¡ 1Ñä€C*úŒ•~´¡G4Ágy_SEÀ\×o«ó7×ùø#~KƒÏª ñ•ÖŠy’é¡=hÓ…-€% ª´ÏTÌS :ßät‰0À¨TΨ<»lž‚Vk¾ÍQèþJy
+= G¾Ëžo·€¬öB[BÛí¶ Y·[,ÅÍ|1ÏqæBsÅýæÌMYt&#¥”r…X,ÊÍÝ2;=42=;_}ažgcGêÜp \D’º¼ ÃÁ£3¡ÒÙÞƒq_u$Ü°”ÛÞó•ƒÉLõ飡* °°5Ã~ôÁ‡ +˜HчÎ;_#` $¾$CqaÐŽ’&º1óE5­ŽÀ–ˆˆ!09A3R%ñB%ÚœÆî~)s{FJ6•Ä`p LJ¼Â®sîtºòŽb¾¥°­¹ÈRþÓr¥P–2«Hrra^þÐÐ
+Š­¸²®(¢‚]o”# BÕ ¹ï È%$
+(Ñg–F"óŸ}4‘8B.Ý–ÇèpÌoêg´^|­¥—Ñzé¥i¤©—ÑÝ[vM$(*–½ô­·–ßûá?Ïî9r@³† #8F‘ßßø$&*:Ç'¡ÈÖm¼Txõ®ðò÷¢ ß»v¯°ÿ+qßßrüx.Šû¿.éý[õ•¿éyL2úP
+Ax]°'ÿøôY*ÃÉôSÏòØÌ¡L¯AœYZ›ýûT`úÌàžñáí°ôúý_{ÿÆÇŸ
+ñ‰^ÃQ»äx¯Zl—•u)Ë.haŽ@H™S¦‡â¸Cu²K-±‹5•;Ëögæg¿•¶rÃê5Ö®ÉHÍü`ëæC¹[Žäm:œ»µ¤ ¿êèѾávmÙæa·¢Ä¡†YäÔÛUùµE4 &wm4É¥ëòNiö¨ä{ê”ùª†b—J•¯RïÕ¨?PÕåËÎì8S‘-n*ÍMßž“öëìտܘœ‘Æ[³[žÌby‡§#qŒ$Ѹ¸(š¦ [²L«‚´
+
+Á™8ÅŽ~¸c•Û^ì*÷õœ¶”ÎM#Å@#>Ç XfEhJãË/n…ósrê¹óz¹ºƒ£œšš¡ÿXdÞ°.>Ÿ‹r(NŒ€Çc£qÍDi’ËCØ·=•ß\±óvsÅC‡ØÛ#ï)ýXq4+ {A$c‚Ò÷Ž™÷šu»tºl£i·$ÝŸþ›å¬D›‚$ÄQ¤V¡¸Ü×G4Å6›d¢kSþÝ&ô÷žœp”‡Ú
+H&œÞ®
+¿]lŸ›F:Š'ç:DAGЈ§ó(lÆç(ô;Kï™+øܨ8ŒI Hɹ!þ(TÛ2¢4 ʵ
+ð¦Þ¥³†CnšËµ]r£Oekv×èýµÆq™% ·ø¥–Ôê—ׇȌA‰9TgWŸTš\Ó™m;<c@Aæ²Û¥•$šµy¤ÎòØxîfQ…ý@QÎú fÂG¢ÏxTa®Õz¤:ŸTï—›=R#ÀÏÿœFdF` ¯²Þ%U»ºï æM\ÁÂ÷­\ñ@a™P6¹%úqY½Ob}ôQƒOéPZ]²ú Â:zZíW[Kµ.µÅ+·z䟲~°N7¡kqÕZK’I¨„ÂHŒæüjÛÈàTÁÄX0rÉÿ<þXmð©MGnõpG3y2š—µªWåR.™ÄV¾ÙØ9—«.èèe4ÁâÊëm=Œ&;ãÜ%FGÿ’óÎíŽ%'%‹JÅ‹´†EÆóKÚ~»¨±ý÷÷ð9pƒÄ%D-¡çs—±Ø+VÏSziií{­©s~ëuFëµ_TV",‚ÇK©ªªši¬žŽW¸C3ÎzVƒNÍ&ø‰ÐoˆEŒ¶®…MW-} [úç.¾ôþ›zæk`¥¤Ç#d<…3¹˜Qo˜˜í
+ŸùÞYÏØôÐà“2½`'É<(?uýîþ ­¸ú²¾o„×ï^¹#ºxç'ÇçiäòÝ’+÷Ê/~%lú]Œ`=A%á šZH³x.–ºVZ+ùîþ·aƒñì©E€dƤ@ŸéDÖÃ3'5ýcbl<²€äž^L†ñf|r"0ñð‡‡:•&#m]\T …Vlz^Ö‰ý1§BèT
+þç[_F(N*Òã¼âxŠ"Ÿÿé³’¢â´wR!Ï (;&“BØ -Êâð9œwqæ*ºgÆ]M¡ÛYè9í· ¶¢‰®"oçá`ïqsÁ&2–M
+&{oÚ}¾ÎÛ`ÙÙ`ÙÝ ÎÕ
+“GfñȆ¥ÖÇòÆ'†öG5ºqm³«ÚàVX}õmÿ’hþY£riG´–GÕõÿe¿Lƒ›º®8þLØÚΤÙ’Þª'ÉFÆ,†¤„x”é€Ãj CH¼H–W‚ i°ÑjÉÖfY^0^dK^‘¨i›À‡ÒÉ´00uËZBC†i!qjãE²,K–^Ï“€¦þ
+”T±ŽÂƒÎæ). ”Ù†•¥žÒŠa¥FQ}* €Ê€®êO…Ê% ÄÀE EŸ·>9yàÞ=(f˜{üSÁÉÀóª?´ï¼Dò9ó„ÞAð3ÃÃÃkV¯ÆN’>‡Ä#y‡§UÕÚV¤öÄK5¿:æDí³êOÌ©9‰Ô;gTººÄ~|–Í…4õÁ›hy6dsT qÐèòÒY‡•± ¿ÀçÓØÊ5ϬæM®™5MQu§ºç–›pž(ðhTy÷ï߸Ç0n}³»giÁ°‹˜'`fçö8
+ÃcBTôã’"¤¶eNMr´m.,¦ÆõÌë¯ï|y{&I-\ËÅc9ɯ½1æg<,Šøýþˆþ>ÞÂÃ]„³´Åd‡Bð1‰8>ÏqQÞñ÷Ü“7dǯç¹nåwÝ”¹.e¼ôǧLÞ{UÚ{+«÷Ÿ…®kù¦vIÒZ1-†lC¬\{“–.;x ðÌ'§ÇÆÆž¢ÊÇq‰œÛÈ~Ì$,ÈE² ë¬ Ë!ð9ü†;±Ñ Û…þ¿J$ ã'¢8–@f׫²:õ™E.€‡S“ãÐ4ªs›5ò¶’ô5
+Bk!? &¬CölÛMhIã\:å}ïMÐtŸ#m:µõ9³}ͲQ{ˆò¸]4BrQ¨7Aötu³Ó±ØÈ^
+nÄQ8#µP ¢8±Ë…ÄûÛÞøÔ”þ ã€·Eh“O6e3BÍrÀ€‰æ<¯£€iØ~ј“€‘Ñ2šúÕâµú­¥å)õ¦­&CŠÉ´ÅbØaÔþZ»6~M<ìàˆPz[j*àìï:]8OH\šŠå‰ë¨9
+•åßÞ·4iÇÏVØwïP—‡Šl}Í‹ŠÞY&ù(iÅu…O¡ók*î4Æ%‰<7mÙJ¯ÆæV›¿2Øö®ZÍÉñ«N¥ôëêF5%þ¢Ê …mLe+µ|¥Ôê¬nM¥[Qù¢iÄ£±
+0ŠËj.‡f¦íki¬¿{û¡Ë0Z„"œÁň¤ú¿º§Dþ>ü$ìÆ];S{h>‡â¹Ž²Ì6ÝþfµÔ©ÌïÐÉYQïo-Éwj2[U-¥ï~(ò\-„oÙÏP,‚"‘7 bA%L,`(é oàz‚Õ<îÁ–dˆo â)‹@Z¤gdÆ'„)à=¼\¼(hywê.«ÕzöìÙ¡¡¡oŸåi/€{ÜûË·Þ“8…SB’NŒ™×Q°i¼µÀk—›ßc:¤£öl_ËÁ†,Æ‘åvæ›vŽ;ó½ÎCºŒÉYŸrÏR‚‹qÅBW+U‘XøØ ®ždž`"ÆâaÀ?Å#áÇ-À–¨¶ã /ô÷«Š›×o¤9üRDãbŒ/q,nqH.Ç[JÅÊÞ”|üÑ·cÈ‘¨“2Îüé¨`²¹
+dtÂCúxSæD[îtë™h•YŒ3§%s]EÅòø‹°Ø%Â…·º/Ë*˜âŠ¥i\W:;¬1M*ÓÒÈ£©üZQ«ÙJóyOçS|Šü^¶oBidŠ*¯«Ì+ph*‘•ì•Þ×V­5Tå½9pf¸t|-Û²kTgÐZþœ‘{^V
+ئàaÈXÕšöé!Ž)2”†»zãåCê/hEEHSñ™¶ì覭-ZòSÞµ SªêÓH©Õ[dô±ŽªMJ­ƒªÿ²_îAMÝY>qw»*
+Iî½¹y‡€¨Rµn¶Ö]­+ÐQ»‹"Þˆ"j ’@x!„]tÖÖîºÓmµnW¥>•ê:ã»;[‘GÞ¯=!”µnÓ™í´;³3=óËo~7wæ÷;÷Ü{Î÷sä.I½CR÷¤XöDª°–×:
+I¡
+›ŒBÃðϤ1qÒò(6·ñ•3‡â‡³žuLiD¶ödW§$~²=ÍŸú;4YS­?‚sh´5¬˜Š¥,VV+“ñ++vÊNªøÊò„òkùT
+¤!:¯””ßWd4¡ñ€.ŒNÅAô£Ñ ZÁ–Ñ®CO»ÀÔ–bR§Y4™Nˆ’Úëƒ[ŸijK6kRüúÓ!˜hºtyc]Çø«T:ƒAg³HHÑæ-¶Í ÑÎ'ŸHå“¥Õ’*S‰ÒŸš[Ë«–W½¹*Çq*‰A ‰@ˆå¤‘cbkeýäÙÓªæâX>'ÓÒËízÚñ1±Ì*.3•*÷­[0è<ŒCÄ‘ ©¢'ÊÆÑ’ê1±Â"k-–ùvií÷M#ž’ºñŠ¿I•†]É‘(™Ë¡Ü((µÊÅ'?.ðÈN^äˆTöw «Š–‡Ë9D
+`}Ñ/cŸ–ÕËÇ+k§ÊjÌÒ:Siͨ¸Ò^ÙðôhÅXIÕ”¢aBZe+œ%µwe5?£Q€ÚÉx(°(“Õ~À‡"³Iñü_˜kÚ|EjìÊè—½M$&F!2#ÿH<`$¨º½@Ò¢ö§Âª>Bk7A¥ ¬ë T%4«ìu<„¸tûžÅõªàÍ»0´™LdõêðÅ!нÌ+‚;æÕšÛ›Ú~,L¦F¾¶@\º¨¶sN[Ac hí«ê!4wÌU óº½4R¯ŸsÓ©—bÁL6N¢’ƒ×®3O@„ŽÌf±úÊág6èß< rv· šºì#IZH¨ÙÄŒC áéšô Néæµæ6
+2NñV¡H
+p’¥•Š´¥IUùK° €Щ¨HÞðÐØÁb2û²Ããrÿ{ñuk@ú¯üïöÌpËF¾?–}k?cvMÍî—³P4sË5S`ÓöB¾>ßáçvLüó‹7^‹„s‰‹¼‘ÇúKS¶¥+ÃÞ‘déÌ´hóœí"»:yJ Ê+´j…f-(¬ÐÑ–üLs06,§²iN!“ÛÛZ½|èõÆcsxgÃæ¶NyÑãxô×ÇïðA™Dº}ÓV6u aÓ™lF†°PŒE¥³0ÇX$¨5•ÁÄß\¥ÏNx\'òtæ;Tû=jw´piSÌšä ÀÔéOýÝY¶ö”ͯ¯eãt(4aäÐ~ee¼Lɯ’%Ƚ4§¨ÞQ]½S‘›M‹b 8Ž`{ttÔ™÷~{žF ƒX’ûv´Y›ïiK¶w¦xúŽLi2žµ
+­º
+)(–†iØ\_pñ¯‚pHÐß»ßlÞ¸ô|yN|Yݼ²$µÅv™¢Ï[¡n;*‰´½ÃÑ}N¨Ç
+é­Ñ]£QícNÇÙ”¦Šc°
+t Em<Zrrø³òÑk€JÇoÙ'ä¶ÉBûôE#ò±[û„Ô6Q4z£xìvÁèm±ãoùŽ¯¤Ž¿K7¥#×d#—ËŸ—?}¯B“únNš€ `EÒl˜bqI¶fb(Ž"IðÖ§¥çéú‡®|yÝåø½¾gξdÐá+aí >^Æ%wŽ8P;ÇáM{¶•šÄæÙ ˆLNfR”eC5€FŠ†T»‹¡—†§:ÚÚ—Sx\îåDaHxÂ/çáŒ}†ß˜ÿý÷d˜êñÿ]bžg‰¾#´ßÛ.Q¯?ä÷Üœ¾‘”¶¡’HJˆràö#ÛZOLÒ…~±ÇTî7Ê=ƒùn“Üc,õÅ~›|^/õ÷çûåfy6ÂX-$y
+Š÷oÿ}ÕoïôŠýú¼!/hz òÐPq`¨Ä­/|d,ö˜K@% F‘Ç$Žäþ}¡Ï(ykëÁ)œ›€Ä)Ö7R7ln8Øõ~usvSë¯5uÔd%¾³a°+ ·X,'ê7=u=9Oñ,¡¼„Dmâý[Wê2æ?ìÉ ˜
+½±Ç [’pÒ!ID1–úÅ®¹¿ÿXp¤,wçúµ0FØ›slë6O]ï]Eýœ²nN©~¤lŸStGróÏ$½Á`­Á˜ñ8bÊ‘ÎÖv?¬j} ÔzT=w*=Ú…3unUûÅ
+å.B È.½;-uòTÍÅò3é z™mI)›“…Ÿßo=ïUj½§5‹Ê¶™jÍÚ–9EÓÿ›Fî©šïU7.ª;¿>Srw6Ž£“µ!=](
+cœÏD¨5ŒÕ;ÞùE“ae¿ °ÆôY£;-1:;ÔmYÑmW¢u–¨#¤‡ºœ1ZklŸq¥¸8U"ŠËИÝá  ñ«ÿá4 õ‚ÏíùºÛšh3Æx(±f×¾¨¾þ¨n4h{IkŒéº
+.÷³O?–åS»Íf œJ¦)6™TØó'ñÈ é'·#º¿s:ß~E4>!qNœ°_{óp‰KDX$F"ü”íæ¨úvJÏrS¶8Égÿ’Åáî>ZôÑðçRÛ”lük‘}Jæ¸&w^ý±hD
+Ä1U`»Zd»R ê—2û¤Üy¤“8¦ÅãÓ¹£“9¶¿‚š‹œ“eΉË•ƒ
+sÆáò¸ôLŒ I æc\ Þ}%GI­ŽàÀÔh‚¿7ëÝãeå†Aý—.ÏÜðÂ;ù‰Sƒý¼+ó-Åã0X,œ¹·ôH™¹1è\á°ªÔ¨”"Cu©¥¾Ô ’ T—šê¶çdñ(œ`p@—yñâŧÚõü;ó?¯iàtùÖ ï¾`—-O‡Dzæ[Áw
+÷,ÒÙµYqÏbm?NkÃé:R¸"f¥â×Üؼ×Õ²êÄÙ×
+Nàš­8ÝÅäfýª‚£,)£SYdxk®Q½¨Ý‚ë²â:MÉú¾$°ˆÉ‰ë(Ò‡3Ú—´˜q
+JF£þ0k+çØñZ‰lÀÓ?62†@6û«K©+W±h 0“½i ¯­¢ÌVwÜTÁsHJM5¥ÖZMVb• á›jÄVùÛûÉ1D}åÊ•ÿ|U~Ž4æ§é)Ū➴罇_ks{¹|ÃSñ<eÿe(öxX8
+hƒÅ$¦Ó¡Õ(‘†Àd,Ñ°Â$4•BNÛ™—ïÛâ*?0ÖÄ÷÷”‡-"Ÿ‘ë·
+|AÔ\3òÂÚü¶ n.ZÏyõg¢æã@ÄCFNÄÌÂz~DÏ‹›¾^ú4¨ÆZBEÓ0„ n˶ŒÈrªÜ†ú\umNú R™£Pg75d7*rêêòjh
+…DƒPhïÆÌÉÊÆ{²†ñZ•¿R5S­YHÍ×2™©´†ˆþAp>RÕê=+õÖ©Ç% “R€"jµ*TßüàTÍlMë}Y›ro.à–„’è"pà™ÉC•²ÈÊ™J¥OÑú/©òÞy™W¢þ°¦áÛ¦‘éZíC©"p¡"$«‰Ö49ñP%cL
+íd§Vi|²¶iÝƒÊ ŠúøeDÒþÅùzŸT3#i—(ƒÕêxeãTâaµòA¥b²J
+tšf»i=‘€¥c«,<-wæÜ2ÝÂ4ÒÝ·¤Ãž¤ a_ÒÙªmq­h1§
+ø¯¾¹ Ÿ-£­ÅõÉzÏR•§s&wØ’ÔÍl(%“SY 85[“±ªèäâ6KBýõ½¯v¹+ ‹»ÀÊvœÎ‘`’¶¨$µÛ“´žEº_¾¤qàzËm/oÙM'3@!‚©Èëëß»>ù*#(â…ýþÂÂ| ‚(2ÂåÛ¶/mïZÒéÆu¸1èz^êðàº=‰à8‡$
+hx)dlçŽmÈÈÐ&€ÑÝy?)s©öTØ+˺«Df©ÀVË5V á[$B£ô§…¹d">ƒÊ„H¤«W¯ÆŸ‘uPÖ€”ÏçÙy67úì+Ϣˋ²gùä9
+ïl,ò¸J S„@èà¾íïâÆM<ŸYàLbáM‚°¹ dLâ(ÀgJÏ2òã®÷ÿ¬,åoco",M'Q<2ÐA@‰k0”I&ÐÐå´eÙ›éþ¬Ë§Þל÷Zx¾¡Ï,
+DA}qP_1ÎöpâzA\/˽–ÓSö3ÓÖRŸ…´ä‡ ùq[QÜÆ ]Ç#AÌ"Ž˜Ä ©ÀZ6ÑÍeÐI *‹¡ÛӷɲUMûërTʼUžB§¬ÏV+sZês•ÊCŠ­™¯c0-†as5РƽXüú‚‘sgÎ2Hšö“΀êÑÍ´!íi
+FÞ¼ÍÞòÓ±r¹W¬R(aÜbÃäð†ÿ‚Ö+®þF¬†ßè-ẄÕóŠZÀ§€²nF¤ýBVS²ë (:
+¿´´xqÉÊ °x»·\tË¥ŒŒ'P…?ŽÇ6€? [:©‡j†è
+}S:FNLÆR‘Êê•õ–çê­ÏKZžt•ª}UsolcïŠbê~ªÎŒóI› ©·¯hq &Äâ$—
+1F•’OEIDì¥õ›o þàÀ· li3& ë¨T*‘ÄX¹>=¥H€˜;W`Á!ÍÀHÖ§ëH³íà®hûÐÒk¶ÇÖX‘×3"U2! ü2‘~™ú—/GÐ=<!o
+‘€ÑSöæñ˜=Ÿ]¼Ã´ õÉë/èÁí¾ßý·ÓŽsì7€pŠ\#ðg¡s”e{\4³_ã9¯³œ£¹Îñüþ»L×­<çM€¢<Û8Çq ðƒoá9‡ÙýƒÌk§.~.p8†
+·täNuð'Ê®8¶óäÞmY;_zûg/ggnÎÛ“¦ÎÙý‰”ußTî³¾<ã1çøºrB<o{¾ÏÌñvq<fü"dæì)3{¡‹íëdÚs‚¦÷¼­¹ómœùŽÂiËÙiS!|58»¿“á5ΙÑÜ¢?,} EãÉD˜¼oË>ýÑÝ/t@#úc:Õa)Јä Z™U«ÊÒKI÷ìÈd’V¡Ö._¹âõû#bè ?”èJÎ¥’“é$J…ÊH"°_OÿÊXî)t7
+ô”ÎY;Úzü­Ì…Þw'ÇÓÅŸoç…[Oͼ–âT
+ ¶;5 ÝžJûö|¯²úBç©¢¹ùz”„1hôD´û]îd…fV®qK”ž”Þ*Õ”Xõ@¦+¹ð §èO%g~Ÿ“çW{„õ·ÊŸž>ûϲª¡2X© Ÿ×-Ó-Õxd…n¡R(2!×y¤ÕÁrõ“¦‘ù
+¥¯R7-ªmÍáa$Êê
+™@°à3÷&1€àw¶¼p«Bég…53•MRõ™z®R àt_¡wKu^‘fZª™k
+ã6lI§’ðbÏØö÷±!_È Ê
+ãù„R6¢9‘9èr>v˜ÇÚc4[èÉéŒÄäT
+F‡ɇ•Gû]Ñ"¶¡1ÚÖß Mæ¸Wv¥a(Ø! Káóù^/¬'¼ì#KžZÄÙ÷ˆ´f¸{1sYT2}i-–DÉ8ÀdÙ/³~=Æê,¶²ì×£¹¡í3¾ëîû¶ñ\×-…F'¬I%cYªîû w`ŒÓ?šÛ}M0p›i»™o¿Ãv}•?p›Ûu]Ð}5pó8`CßM®c híæ rûöÛ=c‚‡ÙŽ«Ž¶m´Àu‡Û÷vß]–ëÇ1¨PÜ03XèØ®öÚoðœ7€@àm‹j¢þÞÎO+{÷ ä¯å¤g¾A¢¯'’èäÄT1…˜´‘ŒÒ“Ö¦âa (ýE
+³Š{¶KÏ´Hs,çø½ü6«³ªÐ*ᘅp‘]ÉI¦  oàP
+‘Ôâ™ ²ˆ×h?’NKf±ÔŽ-åRØï ûÂÁ@ØëÏE?Lü¬~ÿBÄéÂðä¢qÉ좎ˆ”ñôÀˆ¤„?’7¡` ’*x|xyü0ûð}ÁÈR}a|5ÞÈ÷à3ÁðÒÅR@ãøñå¿þñÒ¥šÚº¼S,ðY0n@50ˆÍë?×°ç-œpûÉÅ6¦×$ð´ ‚&~4·ý_‹P÷·&ƒLb$’hÄu3³ ‡[‡dڃͽ,K ¡=ªÖQÊ©P½­=ôú[Œ: ¥
+&yyW^^¡ žUwëY§­[EDPÇcë‚áH
+‘“„„p#÷Smgÿ莥íªVäÑŠv»Ûé¬VE @Gö÷’Õmÿ°Sgì´Óñ7¿ù dÞ¼÷MÞï÷ý~¾
+L³ÏXçY ¢úßW,ˆ8Ü
+mpŽr—J¦Î
+–)‚幡UHvV°bû›ïù²ùÞ$·Ï>k~’!¦Ÿ:ôà™$%%ÁƒœäÂóÿž¼Ë^-˜j<:VB?ôyã¼I ¯ó0Šb“kªíXê0°‡$g(] J?¨ï£r=ͱò±$Û–‘c: \Ê`ƒ
++gW˜ }9TQëQôLÌ*ªJ¡bÃœÂêÙyePUÍT
+s1û <¾§ ÊC_ië ãœâ(C¬ÞÀ÷b€gyÁ—¿xÁ¦­¯j
+æŠä5BÆWôUót•ó*Œ¥FH n{~ŽÞ5AUæ¹gæGD1}—R ~0ËŸAâ0‘W^1îþ"Ž!Ón‘¸öí´«uœšv 5U—Ép&ÉœçO¡‹x+_‘ifÕA¥ÏG0=ò­¾lnqì¿ä>ºÐ)rÿ…ƒnsݬ4JìcNW{k³ÛÁúæÚµÞ8FÁ,æ³õýc'MW¢šº†ëóõHCW¼ñ™U^xîv¸áV\mw¬µ óÄQ„@øaéU15Ǭ‚š.Z–뇫ÛD–Î8KGŒ©ÿƒÆ‹~[B¸Þ«6…†j£¬½ –;1çþ i茳~%4
+:¸¯J!-ÌÏk¾^ûù§¾ho¡!Ñ~õÓÏ/µqµ­µ½¥¥¥¹¹ùG)- çdsóŸE^ ŠCqP‚‡ÁôÄÙ®‰rØ,.bl&£8I¢\ÁžÍ€ ÇÙ˜7û’ÆXH"°DÌF`º¨q0I‹P\Ø$lÏå$C°có<‘½>Ñq&ÜYi+‹²ÕÄÕŲû‹3ã'Îéê8Ùþ \çc_‰Ø&ÔîÌËÞ­ÒìΖ»4"Ý©ÈRæ†èT;Õ²àì ËÖsF‚ÌÖÙÙ199A—0W‹
+Hnîû•„€(>VíP´­ª[EñÑÖZ…@J}" $<„„‡„Gxò*»£µ³³ÿlgv×.vAå¥ÕºÓ?vv[ª$<²_–µ;ÝwêL;éôÌ7wÎœ{sïɹ÷œïw®Ì+±úUÔ=5A×X‘j;rÉæWÕ–@Ì ôt°Œa¸(Ï„Ø>@*ë˜?76!Wƒ”ñË0iˆˆÄä‚À.G*+ˇHYý<s+¢Ó>¿nã ë"租W[TÛæ]´#•­H­ ¹hCÌö9—jü)ˆ°U”#IRÂò<N¼½gÏõ~ï™õzz'G`é†åv»°ê÷vu½ûV
+Š²$90 á)³Gš¤x‚a1J Y)ÍI
+f•„ß·j͛뗓4Â/­c G•Ë1¹óK¾+<Ô!€"Ðè’;»úæνðÈPüEnÁ`^áº`ÉDNéXV±ÏNé…G_Žäx,i6m,¨úkªawDèó º ><|ôqŽa,ÛèÎ0ËØd<
+§õÃi…c9µ†Áì‚ù†ñì’¯Òò€"pñD–q\g‚úß3óOE½‚áKe4ÎsÎìØôÚÈø˜ÇûïmÝì+›}w°Ò~ÝòSùq”oÌJ¨Îx­-­RABQbÅ!"YëŸSàWÞ‚”6ûÕØÿFæ\r"Äl[XnCÊÛü.Tþò`( Û4ìÕ-HiRgõ/®œ[÷빩z†“,‘ã˃Q $¤btÅ&?mRßä_l]¼ã€„g—á¶þ¬° ©´"UíÏU6Ì©nE*Û
+çœò6¤ªÊÿì*,JŽRXÊ)”cÅ°|}ûÖj³ù£O:½“Óð—úúmVÛ»‡“b\D¡Á!TŽ1$·D¶zAºvaE3Üp~€ÄòÌ©v.Í/FQ.„Åø
+RÐUוñ'£:;M¾1Y<£põÌŒ{¼!RY¨À¡AAG¶'%Y?Vwøð níQwô)]=Im=OËΑÖÎã®{
+[ÿ;ºL& \ÉN2ÿöœýzJ[gŠí–Æ> °ĵ}ªpÞ\¿W-AI)E¯{ýð馕Ö Ê/ÞLÙ™¨Kª¸’ìPwÜoºžlïQºîvÜVtÜKpÝQ9z“àøÇ×™þDÇ$×ÝD×íøöžDGœ¨m7¿oh\Àžîø¶.~ì= ¶^% ËuûŒãfrû-…µ7ÎñÙ‘ö;ÇÚ{“;úÕm»ÎâÒ0†"ŽÛvloJ£.¡)Ce1$[²õi*kV|}FR­î@ª"ˆAy‚†+) ãBƲ4Ø#àíEIhh!>4‘ÃEršð¥ ÔAø~B3&†Sr &† ÂXBNcàL†ÂƒQtêéón
+¾Ž™ñ™ÉÑ?_û ®àCy‹ Ä,ŽÃÓ}¶¡YÀG±V
+™òþg!ý:aýT~|eúß¥©Y ÏÈ(LQá‹R4 ê[‘ªŽ§fçʦymsËÚ‘šÖùU.¿²ÄìðÏ-
+zñe˜Ý˜ Y N}®ÌîWkó×æR¡k`Ý¦Ñ ""ré·ø²_/@MÜyÀ#Š:w½9És³›Ý<
+Ê™F¤$@Œ5da!qǹ:²­é¥&'×h}a\l#mMg`J,‘†:’“f&˜›ß¾…vŸÏ¹Á!o(\z¶ä†`ú*êËÉE¶Û‚î^ÏÃ\ë€Ð>"^ôûÛÐ|é,v rí7yŽa®ëÓ“^Ë.@iX,i^±7ïH]•´—ôjªØôgk€ýîgWˆ¬±dj4„åè‚ÎÁ“ŽóG
+bwó7Gê¯fÚ‡ùö!a×
+]÷¹¶»"×ýZ#p®a¾ktpb óììÎ;<KŸÈ~_àúœí|˜cæö å_pú„§iû%DCQ”‘öäà~Çï”ä[4f ·½œm©àš%rž¾l% bRaðØ0
+›M|êf2T¼#åÖ™j¯´Ù[¢öW×~U¥ü¾4 4Q¥(W{«kEFÊ”O+T£òóaI£¯²~äœvºúB@ªw—©g¿¬Ðù*¥ã§eÿ–ÔY·iFÄ£ƒ@AbK’ö>*×Uurå˜D9­ÐY¥‘iCç´@&“*ý¨\7^^ªªõ”©ÜšÑJ€ÐXµÆ]©ùBváôÎ]1Ä(:Dc2XÀØ»¶îôOy`Qœµû·BjnšÛþÜ~2-ø ÌæìJ!î0]ŠGã&˜¨é>–Áæ,­oŸ/6Û4w/m²ã.
+×êÔd29ŽÏD(%on5
+š^øúxL¹a«hÔ(´sRWÃ$ŠÅãɉtìAIù˜J 0^1 €)‰hdD¢ž¨ÐKµîsÒ™2Ùc¥~°\ý!çÌUQñyEÿR5ý·Rç•égJk<íXyÍ„L÷}iÐh²Z4(ŒôD¢šÔ6|Q¥¼W¡»Ê/jN{ûCAÑ„²|:S­{Z¥|"—{«ôÿ)oe䎼ŒGR7n¤l U6L®~*‘MUiGË5#ÒÚÉJ¿úø§#•ª'eò€\?U¦
+J&JÕ“Š:p|â¬bZvþ‘Z(Áa‚¸¡ÀûRöº½Þi¯TPИ«£¹úúNý¬‘Ÿ\óßÉ\ó{} ^?þèÚ†¸xŠŽc@ht4ÌÚð+¾tÆ5týý:jêÊ
+a™Óã™ãÎÑÓã=îyy/¼¼÷îûßÿï­;^çZ À›ÁšüSµ¶j'ÀÛãtv€Ÿõþ䜅ÄB“,mFù1€ûúO
+|’Ï·£Æ …LJCáí»öÒÉ!R,kåæî ¼Ö…îëBÏ-~ûí÷å£þNžýZÞéï^Tóý?p¼ž«ŸµßÜëø¾ å
+äÁö*Wöò1zä~S1" Ã’²–w]º;³/EÐ$È¿¼Ï•ùλ¿ü—Àñ =ýwFÁÔm"¹ åÆ'–sé«vÂÊ 7a³–ää–4‰\Wa¹çzDgîpÝË„uos«·Õ·‹üWøþk‚–›…þn¾çÕR†°¥‡çï‚!h¹íÁõö@#qýw9Þë<ß5aà×q‰ïºRÔ9Tཻ,‡‡‘秧 TqË]ÇñBo9ßR%jþj¤À):$xY¡U–[¼EFT#¡}Áî£5‡]ßÎÜþ$ð½m?r[osZz`Ÿ@¯›ãï⺠|¼¶;yîëùޛ¶[|ÿu(¥"ÿU…þžb×e±ëÜüe›z&ƒÊHE±ðø 92Ž®F/œ?OÆÐDó‰oTæ<jâLX °6ñ&ì¢A#wÌZ´`@¨ 5ó‡œ SáÎ [„!|ØÌyjæŽ5sÆÌ\øAÓ‘!SÑ÷äØ° úõœ ùÀ8Î5òG›…!k1üò qÿÿhòÕö—ŠnÅ®TÒ\8{D4’¾L•+SæÊjs¡Fª7)•[*6Èj·+Ër­d¿Ç$Ò²póæáÈ`„"õk"’ÿ.dÑTýyÏèDßУ ‚ÈÂÐ%ŒÙÍ%ãæ¼É^ç}ßc£0l=8`û|ýoXTl>“L…ù…¯Xõ}¹ì±"R£‡¤ P#Á
+Üî“U<RTM”ÕŒ—6JÔCâÆ~ic°¬>$U=ËÉ•?É”ý•uAÉË×ÔΓòÚG²HçGÓ©l$93y¶|ÛÖïªU*BÕu%Ša©Ö³C°
+¬n 2Ç4»€®ÏÎÔ[vlƒOŠ"‚ŠŒÌàÀ ô\U=w{T ÑMŸÑðø×g¾Ì@èD‰Ža9s¿ñ+ŽïÎ˪ÚÅž‹{u_/?¢{‹£\s çá9èØqê,2&†Íe,,°^ÜsæX@s)jVÆÉ^Ãûãõ}-WWì9ÁJIƒ¥P)d4ƒB¡SWn?XâýVä>_àíØÓþOꢥo’æ¡dfN…™ç½x¤ý×}‡ï»•ç”ì–.HžçŠ(ÐYäíæ¹»^µR Dž+‡}7!Nö·öð}—ŠÝ<G›•Á Ð©()™‘|àd‰ÈVÁ³–Ãà[¤E&±À.åÛÅp'×"jÄ(’”ŠÌ£Ñ(höªCžaë]¾ûR±¯ƒ¸1Ùëá:ùÞkE¾Î+rRQ„ŠR™ù…í鳩v"|î¯ßÒ) a·¢n÷ª_¼Ü¿¢ZŠ†m¢H˜#ᘭ8R²›¹#VΰM0b;8`:Øo,uÚ„ƒ΋Îs^œÇBçÀU
+‹<ïÏUa|ÒyjÌÚ‹†ì‚p3ïüñ­Y¤_#Œ4
+üÃ/«ªÆËë’*ƒU ½•ÊqH‚—¤‹ªCZû‘ÊSA‰êá5ý•ª'Õ'¡”Ö„«u÷ÅUÃåŠËkî¯ù}ö;Â"3h l”FJüŠh¼ìdP\Û/oè/«ªTT|´žÉb%S°…$Ré6Ãùœ„2ßOË8[\:.Ö„?«UÔþX.…ß׋ëK«ŸHªûåu÷%²Gµýå5ƒðöËcRÕµÅÖ%ï¡L:¤H‰’ED×­[×ÛÛ6£ã‡_Ããu‹VØKûû¢ß.Le0a‚¥m^ú‚7>Ü2]ïˆAãˆ3ºbá¶Éá‡Ö t®¨áQgŒÁ‰ÑEÐzãÔ>€{gèqÂûkèIôt*š°ggŒV«o‰·Tš¤¬µ)‹&™ÓÊ*cͧc –éÊÚÙjsœ&
+vue­Ö«‚$$!€ZL I¹Àp‡#9AéìîÌδÝîÌÎÎîj[@.µíÚ=f«ˆÊ‘p“}»lg:N‡ù«¾óLæÉûe¾7ïw¼ÏïÚÿó=dÁ㣘±‰iyùž[®çW ??ßÖå¸uÞùY®»3ßw
+krùu…M\O¡³K³½Yõ¿álÝÃ&cQtÖ›WRm,hëLØ~p'`IÂ÷ÏÈj™[ö®Ç6"?K9©ÿà”¾í`®út¡eFÓ"Âј=<{¯ãë4Ïsξ<OW¶«/ÄG'Ôb·›ëéÌðõd´-/E DÞî,_ŸÀÞéíç·Úû.z:w¦J™(ƒŽ` eSâ¶KŠL«Th—‰ìr‘UqÁ"‚L¬’H¬ò¼&~M&P©TöžÃZoeúî¨à* ¼=/4ò|#Д ™´dƒF
+ËFJõ÷åš³œX ¡o 'ŠOÈ~}w¿X=­ªk†JʆÕÆþBI"“²–± ¥~v©tLjj>™Î¡`ë~z˜Ê¼[¨šTV JäOõÄÊ ±vP¡ (ŒS2ÝÐeé˜B;\¬™V™ÆÄ@MLslÓ&6‚Pñd&Úêr¹ç)²xG¾¿h?è6_dïÜá°£)(%˜¹žÊŽ#'Ä™pMŽÕ .\WeÁ…ð
+Ú{øž®åÖHž¯“çì;þ7‘·—ïëçù×> ³â˜DŒI¥h„´bQv£4ÃR ´ [d¹V9hDd+lò MJ2HEI ‘„Ä£ùΛ<Ï¡§œó…FžoLYDÍÂñ&Á{š2&yöóÃf~°Elà-¢©š³~37`=ë<ÆÓ{©ÔH&-ŠƒDÙÒˆþXyYŠ¦øÄUþ~Þt{1†AŒP0
+¹|ùòÔÔÔÜÿÚ÷–¶éÙÙ™éÙOÿr“†¢:‹E£)O¾9cÏ[ê|gš2a"€«)s末àOšs;hkéT›B"#mé9êkþ"娼|F¢’–[#£2ã“bM@YñTR6\¤»ªš–ëG‹”ƒŠ2¿\e•†#ï2Háá üó·çr¾*Ñÿ³D;¢©zxU5.Õd†A©îR÷»‹E)ô •þ«HTþ.UÚÒyÛè”êTî}…î±LûX¥ýFY6*×ÍHô#ÊÊ2 p+¨2®ª†•Ú§%š Äð¥Êt$6£‘€‹1 £‹òrüÁÉ…•3çéù†îEû!·…g`áªíàƒ‡Io@2Á¤E„#L””x
+«kE¥W{=¬Á÷ê¥K4„M§±€"p5þðûO‚ßÚf-¬u‹+ÞâFìÑ£G;6$Ð)$ £’9sÕY7n íüŽç¨¾§—çº *Èp÷¤·v ¼} ‘4}}˾#©"J¢`‘:¬Ô]ïðs=]'¼_óÚ¿à™<¤øm‘„ƒQ_Ýþ6Oç(ðvfú¾Ø>}ý¸B¦€Fà^ ¤&ƒµuGÒÛÜÂLûÇÂ_÷ƒyÒÛ?Ooïâ¶÷r½ý|ß½t{·Ðq;ÏÓ¿ÜÉñtƒ»ž˜/†³ýusò:!œƒÑÉdBÌÞÍ—šUÜÆ«B§"Ó&˲Hù-Rа$Û!»`‘ñ­²ìF‰AcƒF6ì>pÅu‹ëÈôݸoo^häùÆŒE8Ý"˜jæC2gA@_'š.Œ7fÁ!}ê˜9mÖžt¾?j~fõ×݇bxb$”06/™/=*$žûEì¡X‡g1#iôH,†ÅA‰™L2t‹/ãw“g´ÐÑ‘ÇOa»ÐX‰,x#æiCÎRç;Þ,šlâÎZA_Y£µ©3ž‹ê;©"¸ý11ÿV˜ž«‚%å#’ò'Rãrkd¸X7&¯)Õ+ts
+ÃØÅx±òA‰¾>.ÑÝãC
+…(†1èÑH qÕÁLHk„ ½+$ tbb•üFô"ƒo‡p(ã•êÆ•
+¤uƒ¯ŠT˜—×ä@
+
+Ã×íÏÀ³êÎ÷ ßu“ëìç{yî›
+<C`ãL÷çYâι’ìz-eS2è} b[õÇÖ϶”ô d4Ùxê_'¦¤3*J¥Âñ›³ëm‚‹ÏrŒT8úòJÅ0…‰( £$NÜ™v¦÷R®ë*ÛyGÐóÏñʼnÞ¶éŠÐ9rÂs¬+ßþÜ5±ß:áâZ®fYN}úuš@ºŽ€ÄÃ1t&ÓÉì†ng5 ®Q\Ð]øQdªš$B½˜m®v7$lÝ€ `Í(Xò)S×y›ë¹gïçz­Ä%üpÌhsçôl iÛ§åøuÜऑèb?Òæù»²gmãÀ$þDoªSð½ÉÜGÃQ*Šƒ$o n\Ë
+QùÕý:ù\eý©Ü+j{Þ—´
+…e¥Ò
+ˆ¡r.S:!…R¹ uÏ¢5Òa”&Ha…ôÖU e±08ŽLظ!ñ›¯ÿBûu̇µ
+Š%Ó™w*îIê½ÒæÉš Ï[#Þjù˜D6*n˜®•UÔ{ÚûJ«[2Žý»¼<À}i«ôý£ëNg¥oy¨ˆÂ×xááYÙƒ*é¸øÜqÓ¸´mBBæ«kÒðIå£åuSÒÖû¢Æok[¾­8믚Ê+wi«möÖÈÇDMS5Íÿ5ü§ºÞ_Ýâ«UþîdÅ.:ˆ§Ä’qZ&ªL?ZØ wÌ4Î~׆–Æ‹<æ'µÔçó…Nf§gÓ3¥§Ë(0þŠâ•JE˜[ÃkDrãJ¥=H@µ  À…ÒØ¢±¨,W9¡ &¨Ãì懲Q*31#—JÙHã½²ö°.gD»!êi$˜Ê3
+3Vs¹á§*™)éDœBù§«“ßZ]Ó
+u؃æéèò£ ÈÇìi€vº¡N¤^
+œW9½ƒÝï“ög®%%P°x¦ÁD,–@KÜ}èPYkñoú{úŸ·Fò-Cü‹wÛ®—:.ÅïHʼnp(ñœ±s§©”ÓUÍ·Öå›D\mU±½g¬èÅ£$¤‘\SMž¶&-ë0x¡,8–L&§òkŽA®s¤1ß5°¤‘g>-gJŸâ3
+G»x^`ÊR:a’Øxi[È ±aGwÑ?è©
+´ÿýrj*½øå¡e·nÝQ„$7777OŠ«nwguÝnëÊÚQ׺u¬®/ˆ@Þyè.
+!! o „< /ò m¶Óç´ì–UVÅgÛÝéZ_˜„@B·_¤KÎê”íL;œ9ó%óÍýžç;çüÎÞ'M¢ÏßJƒ“™…N%Â(Ê@™0%p•JÙ²%»¿ßãóùbµÀtì†g
+ƒÅ$ €÷4ݺ‰é0°pàãÑȬ½…ÒAãÓM¶ ÿ÷^ìâ_§×X;¼5ŒÏDJŽ–‰©™KM!°ii(ª< €d±
+Êf!)LDh黎ªÁpљ뻎7ƒ˜Ÿ “Øk_/3–ôÿ…ëâœgoã¤!X!…
+/OGV¬&'¯„SáTÒ
+öë(LÇà󦑢ÞK9î¿æ Üzí@Še 0J £TG&)1ÕðÌr­Zl©*2W‰M2¾Iε)
+DM…Qã¯êHFêR:+“Af2–/-—NTÖùÿ ñW5å `¹€\}³Q›ÍZ<¾p󦻲¦PUóÏA˜¢±•4ôJ©ôau[øDóDeã}UÓƒÊÚHEÓ²ú½«™:–™’ªÿIÎ…:x¢)\Ù4)m Jë•Ê©ºÖ±ŠÚ¢u¬u¢Î'k¼¯h¼«¬‰V6€žî}_EIt˜Ì¤¦Hô,"ÅívA6^
+(F£P©d
+¨1S¸y‹5H玩Ög°%´›Oºâ4}¶?¾³?©ÝûB»;ñT ‡ÄV+€„ÄN;Ôæ€ g Sor¡„Æ`“™dŒÈDÞÝ‘tÒ ºXçH8^Aƒ1*Bc-C“j?>cmd°ÅP$†%öøŽHçŒkí…̉jÛ¼©£ÓÐì<±ÓטÊ
+é- ÚžEmg ­ëE•Á°t”t„ù»³O» àV‘)ð;æó¿ùÆ›t2Ê Â4"å]^Ïù§|ç Ð3œï¼.ôÜXÅžË<÷ÈaÇå<×5®ç&×u™ë¼ q?ïlöÀu_x/
+ÜŸîkq½µ¿ˆ¹f#Š°RWn\ÏùPâ9W`?_îzûÇ… t0©Äõ±Ä~>Ïñ×ñgž®ÿ;l%³aú÷%úbÛP¾ö×oì.bXkRà´5Ù…®+üÓ_äدìà}eûÁw„Uù¶ó¥¶?ð½7„î›±ƒ»GùîK<×E¡÷ßþY™ó3nß À?`càø ³ û®ðÃç0×y^4p™ëº(òŽ\ùö¡#ÞkûˆÐö9ß{Bg! aÀtl3çGsõ¼”ÆbQQl9…LA¹¦ß䀵z‡D½7þWh$`É[ó"Æœˆ© hOš‹qs!ÞÅtŠtåF»ó¦MÜ°!?bD­…AïIóà]qs>˜$dŒF^ ºó‚–â iÀV‚wøÍ<Ü\Жá=GÆ»8Q;Òœ7iÈ›1IB†ØÿqÛ¡‰nnä†ô¢€åØC“8jáD¼@×Q¿õèh½è®IüÞ¦5lÂR C04¥të†Õl„f KQ:‚2YÄÂoE»“F
+uÛ÷6ú x¹rLÖ<^Ýz¿ºåžJ¨nŽo˜”ÖßQÖÌ—:îÈêÆÍ~i½_Þø@Ùt[Vë“5„eÍ3Õõ·• !E;ÀÀ$aY‹4{+ f¤¡AY[ä£@(™¸‚Æ`(¹oo
+ÊÔþr%.¯ T•ß“WâªÖKeŠ BÊ26#k9éâÊ´-"mòUÕŽÂQ¨gäÍ«®»]Õ0£ÔW´Œ¯’7MÈT3'¿T©q²¨•ˆ¤¡,JG`²×뚊Ép8<gš9 .È‚ü'{0àá€0ƒŸî€‰$P]Ƙ„Á$™KvîIÔ:ñúþE-½Æé{ =híÎiz¡[\‡|ðöë<¨É3økD´Ý©ZÉùæMÞ$€J¨hQ««–î"­7ëîŒÚz@BÈE8¹!$BB I w8:³ýcÿéÌvwêl¥(G(îvg»u×VÀ@N û¼àvÝÙ±Sfêñ™g2ï•÷}Þß;Ïûý¼
+Ù¿*뿾^ ‚ÞWZ÷uÕÕjÄ]Õè)—û
+Ïrñe{Ù¾o[
+ù½XàŽ9ãbÙT%šDcÒP*™†O<öj[ûº&àèk:»! …Ré =Ôn€° ·AJ;&¥ÃC«×i}­ZN¸,$oß.“Aò¨ó#H£¡$‹ú)J§Òf,T\‚S÷o”™Öf^!¾}fÓá”0AÖ&iÓš¶®ðö^Ha ¯é€:´aJ
+µš¡Ý–‚«1dv4•cã LÝõ<¿0^ÿS¨|15ÏfÓa|a‘Èô$IC^ßøEÓ˜¨wBhº# 0;…g¦uDd¾#2 fÙîf;Æ„Öa¾e;æk»®Dÿ½4ëýTÛƒ´ÞܾûܾIôéÖ‰‹ÝN¡ý~®åö®”4<‘Ä&¼NcÆ'§JÎ43znã\‹¡P2|”_Pàÿ gäÒ‡ËéuÆ>¾°…‚¢ç¤ËdžúcLdS"h[7ƒÇE!3@)"!noZ½¹Àr;Uæà4X²Œ·Ò­ãYVÀ
+™sARÏ·s:Ñ‚–¿¤æzµ™Óíü`O>Ø 42«â¸Ui¡žçŸJ-é³µ"_{ª·ãò‚Ž·Ø#ôh¹!EšW—í7ä~RzI™yªæìþG
+®·ëRH3ß!šéÊtwg.ôˆ=íi!Cî¼6w¦+ç‰>Ç-KB]éÞÖ³må)‡ß@að†ˆ##2^)II:ŸAbÒ™H m“[_2þfIõþ”FdÄ ÔÈ¡êó=&(ŸV0ßÉójøà¾ü]|p
+ý{“û÷½…"€"”A,|\â+²&¨½ê0áÚŒa7 ë[tÚô8•ýVë†f0¤2à”Hi†ôvHg\£Pm¬©ß
+\+OÚJÅï!‘ qûq…a;Ôj‡ Öøš´Ò˜W«\« ì¡ÄÈ´Vik±@m6¨Ã®j#mÛMB˜[)(q½øJ üŽ9êâ `SLÀá $"‹N'Ò¢~~Jb½#î›äZ'3,™æQ‘yD丗juq,.QÿTf¯KhÂXbXG…öÉ­ží–À>(² ‹-£b³Kdþ™È°Žð¬÷þÍ~½5•Ýq
+æÁ6œdXúÓ:F“­MÃbûƒäÎ!‰ýî~lŠÃBA‘£DäJe–òTí…åj$¾>3€äc(IÁ¾$½IªþBÒ>,°vÿT42£Mõ€èoŒñê„®úh¯.Á«‰]7Ùt]¤W'ºðš“ç 2‡:áMý,´ˆ=êÄ…fW+ò¶Iæ4’©©C›â1É'ô©Ï›Î$„ÐA‚þžåm>ín,Ó¦µÒy½xºñô3•Ø ü$1”ÄòÛÀZ{ê£mJäŒZáÕ%δÄ7K÷ÀlÄãÒH_EŒ[—Ý_.ÁØû½ûß×ÏÁ ÆØìOÃŒ„1šâöM©Dî6ù¼A>Û*—³E8«¹µ)#µq8 ôÆAЭ[ÃÇ¿÷xܯâ Ô%hÿS&ßOºW9N°‹ ¯üx³Îgƒ®I<
+¥e¥Ò¸ò†É§Þ´dó*¥Í§Ñ²RmúEƒqUR Fè†ÁGs RjÞQßZ“užÃÛÒŠàS<¸¦ÑU×±7„s™¾²vŒÅÃü‚Y,ÆÎ=?¿vRš!5˸¢Ù Õë—«¨Þ!ZW© +–¼ÔµØ ~õéXÌåqpð]°1pÓì¬cÚ;ç}3GÀì,,zÿö×;“É' ÁÖq7'Uh„–{ýWŠ®Ç˘Â6”iïO2t'ßvþóŒi$Þ4$déO¶ÉìC"sÿ­qǽ$[·´ý+…¹'ÅÔ«0÷*¬})ö‰­Waé—™€F­}©·ú%úT^òÑÛ9ÝŸDMaQÁs=¥öË+.Zn'«þ°;ZöòsÏÛ¾ô#´†žc hirÃ1a†ùî˽ð˜4åòqŒògaX‹Åå£x
+ƒ4>‘¯M·ÞÙF¤ö~€œ¤ö>™}liZÚûR¬C2Ëa0åó¯S-ÿ8x®
+ÔIŠÏã²XAlÖ¯ã#2í×âÕ9rÓ¥åjDdÌ ;°šfùÒ0v2ëzŠu$Þ~÷§¢§I<¡Mp˜RŸëRÆ N]ötkŽCsq¦-Í¡‘Íë’´"w‹`N›ÒÜ¥O~S?CÊ´.è©‘Îëómi.mÚ‚þüLS̬Næ5ò#w‘åoVžÙëÒ¥Ý.ù¶IàÕœu7Çß«J<ºÁY,„
+& f4ÁG዇w:ZÎ7é¾Gq ? DIw[ê¼*~Ê”}òÃ-$ÌÚˆ©Ñ³íOµÂ®ôãL„¶PÔž€"àMj¡«MºhL™k“¹´âyt®YîÖÉ"Ãi’
+êž–Ö™Ï$
+Ç媧—«
+¨ÞµX¡ÖNHeïsÝ ²Þ§É5Z¡ Ô ÷©7CMöŸ5w¼[ß]·­hè\}RÀ&ÚAo _s]ž?&‘Ë%yþïaÁ!k£EPVÖ»G#PfÒþ±qþQòÕ5ÍÖºhGe\¶F; x БO½j¶­lÑ¿S^‰óvørI“AðH½Jëš›[:Ho>7à3 Lщã8ÅE1_˜Ü%˲tËn€Œ–[†“¬c²ö¡²ë`r™¤îRsOjטÄ:$4*ºÉì#"SüæKpÉl‹dÖQ¹ý¬cThë³öÆÙû’ w2l=KDé‹3÷&ÚSo}¤ïÍ´ßÏ2Þ¹ðùÃÓ†1‘¹÷˜ô|0X 6fãþŸà‚ÿKÁþt
+;Žú1ù¹)25Åô¥ôæˆÐ>"±õÉ-÷¥Ö‘ ÌÌHzç˜Hß ¦HÚù(½cH|Åo £I
+…™v(L¡+«óÎéKÏþ—ý:jêÎ
+;òÙŠkuÙK(è“ÍB¿Aè5Š_ö;ÃMiã&à
+ø²¿Iìk<4Q¿? OóêeÖ¼ûÆcy{“¸ô0NF‘áqô`ƒuŠÍ™§9{C ŠÇÀ(3žÞ¾vé'Ëx†-¢²˜ìš]«ý¦,ÿùƒ ¬0&Î`S±E¦ôû†Í
+MJH,:~l뚉‰×˜ó·²Tœä›’({X/ò›à†Aó›eà4Ú¤N
+bUnã!!L:
+ŠiwíÊåçËÑsfüÐùñ”¿À’Ég1=95³vù'6ûªV’Á¦ÑèI„lýÒ6*½fá¸Y<Þœósi$pAô¤Eâ?¿ßs!kó‡\¥³hÎQmÜô°R;©9÷8W=RZ>TpzTU1”?;Š€æQ•ŠÎ>=~Æ_XwW]u:yÇ:ND$= f,â”[EŠN?Ò”{—{Šª»ŽgÀ(+nK V<RiüE5ÓuW×tÉó “·ñ×­‘|°¼&yg‡òÔpqíੲ±âŠá“šñ“%¾Âª¡¢Š'Åà8VPù$¯tüôهNjŸ¨ªþ™»ÇazHR FÙG*” =|ðÝ+x`;™ùá1þk¼ŽYÅŒFž½BÏóú߯ÐówjÈ{¡ò‹ÓàpP›’D4Ì™Ÿš17¿tŽÎÖÕ9ÞÔÚ F3To@Èá‚ÒÛ!8i…´V¨V¤ûíÜrcÖ°@&as?œ›WÕÚ ]ó"öûñïÍc£tø0Tk}«±ýÝsÊî”Æ¡ñ¸pdtðª-PùY
+Ã$‚÷²"ÅçNˆÌµLŸ'µÏV#RcÞ¡Æ\‚Í$‰0‚Œçk¿’¸o¾*ñY•]5rÁZ',$šÅ¢†Ã$+À
+w&~¿ëA]úPS¦rC,‰3P„nÉÜ1­=hàÿ‹ý2jãºãøb ¶›8¾@B»ÚC»+ 0g ®ã£>ëɧŽãÔ±$t€ÄeH±â÷! ˆC×J ’ ‰O›Ô“‰Çí4uœ Û“6Nb7qbÌ%#PŸ ÉòGfœÉtÆ¿ùÎŽv÷ÍÓû½÷ö}??A2jÊߊ |:}b°Iäi;ÊÈŽ Hd=.ˆß½e\—ä±åŒ²¸|"€
+‹@974G'̉“f…I´§Dtð‚*aÌ ™µe¹©Ë(ö½
+PäÖ­[F£Q”"ÄQŒ`…y­ƒ¹4A"8–}`Ç]ö“â¶${ôB^4ÍL2ƒøkKŠË˜âf2NÄŲ2¥
+ǵøþ›YöORÞ¼šf¹¢´ß”:n$ôŠ™‹;Ž¤­Ch%0§ŒŠŠ=œ_“iú[Ÿ%:¿Ú¯ýOýÔ”ò¨´ó¹CNâAØKåâžqÿ§Ç”M „³3©kPrîn’õãØ-8Jc0ý»†·„ÖäýŸz÷ ülªýÅkŸ%Z¯‹ß’3g‰Q<—Æ9 =.l+”ØÔéÖ2©Q)î,Lî)ZŠ:2•ÌT ³¨d%s‘ÌR¬0—d2eÂ^¥¬4‹Äx$ÍG ”‡
+bŸMÌp^;¯KC2ÇuÆ‚$öO¤ŽŸ‹F&;¥.³bŽ‘N›¤– ·1ÉcH·ä¿¸{ÄÇé‚ «è7ßêdc&1@svœ=ûù1sî “îa’&-’Û¶ÊÔ ?®?éa§Ñ@Õ¡vmÛ¿)öèŽMcà/ŒÇÏäDH8V$Æð u1ÁÔçÂi]úÙS‡®ª~»+6’¦ "s©ì÷ÓLú¤1eʤHÚÃÇÙ,R€;²ãçMY3f‘§C8¡Wî¥¨ðˆ€µï¨‡»Ò†ê¥‘ðZ {š‹s¿èȳ¥Œëdy»CÂØ+Žmæ/Œ¶¼1kÍ›e.}êRó0nV¸ÍR·Q8mÍ5Éö?Í]ÇEP˜
+PˆhÏÎíeÅ…—.]üêëM/Ô .çÎèøÅ÷®üéÏïh_>t8:Ô[b0Bâ$† pn ¥Y<šƒ„ ¨÷óvÝoK™4§» R#
+œ+CH. tð¦=‡ò5¹Ö¿{ îÿ™F¶Ä+ØáϬ¡£¥Gšs(­çêŽca4Ÿ‡ ÏÆËåÎ'º¯Ëto“4ÍeÐüðEIï».é½êmï?¼)Û>TØoJ€zß9,A2Œ aŽØ#o8ýªC“bT¦™
+r,%rKI£\ŠF–¢t³
+@tFÀUb*N¶œ>,G06›M’RëÉ_ÉŒÛ
+0Æ}&/.„E®çDS¬+ÅGÆõñžî¼Ëuò[Mù_jófmJ-oN/œÕ gÉlgÆRó
+†Äþ˜¦Q I|ñ^ãŸ-•@U¥|õd=?Ëï÷¾Ïþ½ïç(tœ
+Ø”‹¡¥^«Äc{Á6¾* ˆr´"BK@.Oìg‹sF, Eù¼_(œ©ûüa¥>PÓìÒgj.¾¬Fæt-3õ¹Y™`6p§œ=”û¤º9ÐÐ4«nŸz4-s•º6·Úènh{¬itkº³H<c7ý1ª¦qZ£óÕ·„j»–›¦ZhlŸ­mé[ŸV5L© ÿÒþ©3IÚ”ŸÈ% á¢hVRÚ݉;Á×_qVb%þ7‚Á  ÈÒ+xë÷û§§§+**0$'y±ñ|Œ\‡ bÅJ¨½'ªÿ ¨Ó
+™0`8HÀäˆêµ-²Áì„Ì£Pý«îÑ·úÇÞhl[_V¾!ï0d³c$ñ¶0a&‰Áì”4D¸™M¤!Ffìî¶}Qí4ÔgÿeKǪý¹Œ”Ôu$+‘ÃOؾ2ÔA¦Ñµ=WÖ‡ ~ë²év¾Õ;µ˜¡žáµmÝ°`8¸€Pii6‚ŒB¡Ðsk=kÁpxéíRî _P_ò>>Êe"Ãtóž¼2çw’Ñ2û’+—ŒÜ–þi²düæb]½[⸣¸t§ çj,ÌàðÀq3qð,#$Ù<ßr ÷y›
+ÎÚyàE@0ƒhd±ôŸ›PŒÞzõü™Am0ñDþü¤FzʪÓµÅæ víi«Zi©•Øþ?E›m‘"rZ Т´k¥vØ®-¶kTƒºw²·Cò)‹›ˆq`25¯¶íÅžƒ5,uÀª~dÿ_·FÂf¥ÛªðÙŠýtqÀªZ0ôÚÆ;X0Â!ðl^ü´ ô²§='½…Ç&ÊfͲÀ
+ˆÜý%
+Gpzppö¦˜ødvLFâ†O³…_Tü¡K
+ôŒJ<´\Ý3 õZd`Äo“½*‡,µ-ñÓª Uì3ÎÙT·úÎîç®å‰Å1fç‘cþºN—¦9Pi˜×_V#3Ã?*›`‚ˆÉrýB]°Úè­jðj[gª.úëÚç«çk 3U ‰~悱hçn€ˆ½Âwm×Óú–Gê‹ó•MË^BÛ¼Pmð\Ð?R×»ÚæjZ¿)WoCPàap~áàü"Ñìöøf]á`(ðúëÎJ¬ÄDèY,õA¶˜8ÂA8‘
+Ad¥â¾¾|ŸíôCK¹·¿âÂÁw2ù™o³xBðã°ã¸JáŒEæd ï÷Ê^¸h›ôa¿üßôgOµsfiÄ.öömJ¯õ¿½(Ëe-ñ=«þ@# R/«2‹lÁ¦ruFJ"Ù-ž¥Kô*5Å{n%“Ñõ›Aqyè¢8 'Š „‡q8 ,Ì0c³(äÃ_§×ìø‹¦ÈÓw6bRÌ%~›Lè¹ÍЖ( eÞ±Ï"ñYD¯N#" Ÿ½ÔÝ/,“ÏØË/Éwq`©ü8‘ÁÅž7<ª¬÷iZ¼µ­/«‘`Uã5Ñ9DÀã1 ïÑtL© >]›[Ý:UÓ8«5zô­ªôSúf—®%¢íø¡¾ÝT(¡0,™$œož«5>Ö\ºe©é˜ªªŸ×BÕÍÓ•tþÉt60-Áá`C™ªòRÏœËïö‚?ÿ\xÅ#+ñÓ(Í ÿ àç£áÈÕo®¥g¦ó˜bÁà¹Á)fFƪê‹kZL«{G
+‘Ÿz¤Ñ³`ø‹ [`‰Ä5"tÝW{nóÒ‚3’³Y\– •[k¦ŠS™Ì^©´U«,5Jkx Š€’ŠXªF”æj™±J¬?+·Ö‚ú©äÌe<
+´‚}SùD*5ž sðoý€Sux߃®S~“4Ü#öòÂfQÐ$1Êfõò½sä…LbŸ6gÚ$˜±È¦­êiSN!“c©u˜è)šÖÊ0›2Ô-
+AèM3
+ƒ«\ÜÞ€ßû:bæÍx3þ׿è…Èöû#$ƒ/ß a>>¸ç'É@ÿi„ä´w$y«ZÚ—uY–·»—wõãºúpmÎÕÀ'Öå\› gtÄhÚðÄ‘hñ·D+NâJÏ®=š ³×GXâ’_Ñîĵ÷ã:l8ÝÀ[Â|.N¥ÞÞlåù‹±yE)Dmæ'ëpÚk«5®¥4²¢Õ¾¬ç*®¥•ô£tð³"Á0OÌúøH €Ee½è[H™Hƒ S‘GMóE&¡Si„ÎJØZ`üDØw_áz4"p,8n)œŸ«ìcJ÷WB70ÉÔ=‰q±ë
+wë¹õº°ÿ±Ô3–oû]^ï§YWþ²Èÿ¨oÿg)m£EW ®Ï³íƒèÀ— åìÞÉÕqÅÀ¸È1"²˯>.´^—·¸_T8_Úì¸@/ÀP`C20*醉áð
+žA¦ÇÑàM{·ˆ/–æjÕæZ©©Rf­”˜+E=•¨¥v)(¬‘’šk€@ÀS°c‘¥^xé;á]†©$*$¸894˜A¡€S)6Ü ‘:‡¤žéÀ݈FÀ {—ì÷µkD/›3€(Ï™ïù,!ƒl¨*ÚŸLa² H²#óT‡»Nøµ¢)½ì¹EÖ
+§ ª £dV/ uùS›8…Aæ"„Ÿ2çÏu0›*çÇÛù "L¢]~³d²ýf.|b-ŸèTß:½ßZ”IeÄCÌõIäµ^-:Ý ,¡Ú»Ì3–ú.ƒÅ£°ã‰1_kK0c.¦•<é” U}øIYÖkOüÍT8Ñ“´)BúL_GnX_áÕåô‚€Å Ò9#
+^ôêr1“$¬“û»å*h.˜7(‚¥/b€ÿ¾OMEs&•¯;³ªÂöâH%fÌñ÷dûL"ŸU9mTÌö(AÖ‡ hP+ð™s€ßÂvÙŒ.Ç«êÀºÐô ƒà¹Y<i–NÄ`¡€m
+‰áä^ùkÜn<5Q{:¦mØÔòy}Ó|ןh8÷qùáÕ\œƒó¸|4…M¡èúµkF†FÀ‚žõÿ@"áXä¿í€çõ¼¾¥š³ÇD£ ªÁbŒÄ*ÇÂÓ³^pÀé衸ŒEe‰éhH–Åëw~ïðqÈhƒ€I.Ø!yÁyj³@.èB?ÔÕ ™»Z;ö‰2ð¥\6‹ÇA—¤¥ä¤apÞ»P«2~ôJ«eÁáø÷®X•XYµ·¥ýøÍLtŒ,I×A]žWÚz^ê` #é\ójDÿëÄ–6$w ‹/ÈaqùK{´*¾µæÈ‹ùýþ¹© z¼Ë€ÿÁÙh8RY^Á#)$ ‚,wåëU®Oåžq¥ã–úâ¨Ü=ªè‘÷ö«ÜwŽª¾áòÞìM2 !áUo¼YíþƒÄþWIßß$-}@(I$µdÙj©ãú\.Ç“Ý=ò$ÏœÿcKú‡e½CeîéÏg
+× Êq³ÜyWä¸]íPö (<{/ÿCâTzî(í#Of& zŽ^À'ÒÞA@¸íô Ù™h2
+H,G€à…*7sƒdGqë!•­Qci(3kçÕˆ±hDf׊­ c]‰¾qwEá"n’
+RxÂr0`’E̲ѳJ.‰P0? Ê¶-Zù»D>»"¢“N›*ÝÊ-Wn›dÊc]û¶ŠüÙÀ^DvýÛ¼ñNÕ'…y<*!SˆìâüŸåÐ9C¿Å"SP"C±—ã(‚f pªK¶k²§<l, 
+o¦ šGÖÊ ©jÆw΄x ú¾µÊgTɤ¥ÔkGÚß¡
+Z+¾0E\š Nî³*£LÉ´µ4Ò¡š2Ð1ÜoÓLé•ÿbä^»jÆ"™e
+ųFeP_:lÔÄÌŠ¨Y ô•FÀHh0@‘X÷¼ãyj0J@#`Ë€^³WLê^³Æo-›´ßmÒd‘)8‘ÁA©¥hªS.myZ„Ž}8QÛ|«FËÃár¹‰\r//û‰Ü[{òþ‰3ÿl>u³úhµpœ¶˜à Và¹w«N>ªoñ×™ÔžŠ9s¯ñÔçõÍ
+…B·]‚d§“hjf[ªIh·B]ýßis½tÁñb‡êpBýÐ…‹/Ÿï]Ðf|¡Óúª¶eáÒw.Ü´;‰®~õôyÈ`Kèè…ôÆ„#µiä’¥,,i«0áä H«_ÊNÎÂÓ“3×.èÔC
+û®ï½<^⹶zç~„B¹jõòkŠ«W¬{gãö÷úŠ{†Ôî?«Ã¢¾Qµç/
+f¨Ôý'±çÝ7®°Œì¡Ýƒ÷€¬ï}0`Lâ£{ÇT=Ãà'Ïœ%_³‹<×Uý#oUd¢(å ŸÏå¡(L`8E ØJ羶ù]%Å%­ J“Võ˜r¦QnÓÒÝÇ%öz‘µ´ÚÕŽh,¿¬¸Ð¸iÏ6ŒBI € T£ å!ðê(œÄa|æ‹*<®A©kLÓ3ªq Ë]7ókði5ÖR\·Ñȧ¿
+s­[™ú7ûuÔdzÇüÅU·Õ IÞûHá´µ¬» UÑõÚª¨tv9
+s…Þ¥ÊèR®w.×ѯ—©Vm òÂhO‚B¯º¢@€ÿe- {A#ó/knÆñ!ˆ›ã!G=H”b»1|Ï­ÁG("¶ö‹ŒÝ‰–>Ð@€$ÙÒ¥ëŽ5ö\0Þ¤}·ŸLàd@ðþÓÍ·Úâ¯Ä)­$ߟFØ4‚3¨¿;Ò8ŽAq@‚@·Ü“\ûæ”i@tíq´å^rGo\ë­Ó×´öD·;â2NßÓÚ%2‚pìO0ö%šú“Ú¼uN¼aKìè‹5t ‚Žø$ÉFiRøÙÕö°È0†Ï縳<FÈ0›‘,ÀÝùè†}ûEÇ.g¤WÉRkåÉuò”†Üä&Efs~j­4©ìbHZ$½YÈ¡`á=ŠÜ`xpÈó«/ÿ 2l¶Ù-=mꌲvÇYÔ%š»ÁSH|õ8_W# lŽÉ4wýê@#`:ðiì³´£¯›bÿ¬—|º‰ƒ¹»b´¤ƒ'â.€]=q„AxÅCizþ^§"®}[Ÿþ>µÎg6ìK|î:yFìÞÚÄŠð½>ö¦óÚOFµñßÕ¥ö‹¾­J›hΘÔJ(2Ý,«‹jL
+ô£H!9¸¹á×›w}rpoôáíaû÷…ìèï±QÈÁØ
+ÌئªÊëpE ˜Â¹7²Â^7Å&ê%+ÓÒ÷löc³„(Ec|’ôq‡…‚KÐ|_¿.kN˜lŽÖ8p%*#ôƒ˜Lî‰-_É"_4Ÿ¯Ø«“ìÚ”áº[‹ÄÖ˜b«O›©K›«O±UŸšÓÆ,PdB+ P€d²^ôÖ³û§×æ«E#M’ÑÖäÊ“» `SÊË‹ûS~çdƒŠ’‘óùC
+åXvñ|¶jP^4’Uð*<Í+˜«f/)Ç/¨žæ–×…Çâ˜3Ì°ÙBÝ
+€ÄaŠf0L
+ÆÐ/6öÄ[FzņWþŸ¯«p»iÇÄšV•ùƒ‘‰qþ"|ݳ·ÆŒÕF·¦þ^žzøý½^Þ4×ß“
+@âÿÃ~™Ç6yÞqü ºjÒÚæ°ýú¶h`´Ú¦UX[¦¶”è€q|ÛIp¸Gˆ'&ØIpâ$¶ãøŒï+=þ[¥M¤m °påäê BTrùöëãÝc›¢i['!˜²¡üôÕãÇ~籞ßïù~žwtÖ:¿‘Ö}€Z˜Q?®å¥Œ‚¦*j`‡ UI§Ào¬œ3îjƤ_Óp-;ªc„t{CæJ
+Š1„"<]üÆ›ñ6ýtc»_ªÕË’v€"óÍÿE€æN*¦›Ûï×·†$³ÇÛ4wþMÚ1XÉ’oÞ¡x·fËÞ3u3²¾9±<ØØ•œˆ?åw
+‡¡àÉx<(@.›373›øÅXŒÿ«
+ù)ìk[i0~Œ_$Ü/Ó4žJ¢±T&Á¶ŸnQ´³«ûÓ¢RK/)yñ9¶ólïÍ,{¼“ M›”k,‹%"÷¶g’3x¯ÿ˦ªC¸µ¯1»>fúnì´_«vž©øƒŽ…Ò*Þa9†ƒã|zçC÷h•c¤fðªÈ|¦ì×[p…äU„b,‰R—®­ØÅV}¶ï“[|ç årûÒ^Ï$Ów÷Ñß7Î÷ŽeFŸd»'žTæ¯~rëçï Xl1Ú‘s_FRß“a2l}~vÆëuoß±B§©d,ž„©0‘N Ó*"
+vçî.%bÛ`vo§ï‹±ÿ
+öülÒ>L]€Ö ‘h MZt%$ÔŒ¡i/æ¿üês@ZC®Öõ»r»MËz­KÕ¨×
+”«v¬PÛWô ¤¿ª\iésA=¶¥
+„-¢á–†?zHÄh&’é$J§P MFÐøÐð°¢K¹å½÷_^µà±GÅJ $*™H`›Áò¹}ßÝ»ŸÈ<ô! èö­Û($2ŒÅ ai†¾q–{Šg4Âò2çÛ¯>5I&Â`ÜT¼ãd'†a"–J(üsýŽÇu±°‰52Q/a®
+«¢vßÄÈ‘°VGLûc}TS‰èªfÌ"?@Ž~^ÜPè…1 e¬EM5qéã¦ô Q¨™à}k`ÌÛ«&aX/Lë
+k¨“3Õ„•¬Ð€pW©
+x#§Û õØ¡>+dpåª\yJÔm[ÖéHÿÞï…úΩlàqHkYÚëT–
+“Šqø•%«Í]"Ûד&€lÇ ¤®s”ç`û¦¸
+
+.€tæ—Z +õ/¶!-øÕµšW´ZW´ôEeÒi†Ôf
+1ù¤áϼ‹ß|Ô`GTÒÆ}{% 'L_^¸Ë¶Ž²­#@#ù½ý‹Î‰ÿ2y¶¡\Ç·Eæ?¥n} |Ÿid2 AKJa ˆ6ôô;
+ã8ªˆ.žø.Û+<r™`y=Á¨…g¯ÿî;zùòe°ÈDxÊ»;wå÷ ×Íi$Û~3×v;ßúõÿP#Áè0 @, %îzÙ±…V±°ží7ð½æÂIµ  åFŒ·Z8m8Ñ ¸nƒÈ£ÍXø®¶Ì€EäÒs$€R0m5߯ût"@š@§ ¤G43*AD_„iEoÚç7@ÀƒÀõÑ'jøø½|?gé3J‚šãA}΄ö€êȶ(NDP ÃTiO
+ó‘´6"­÷•(#UÍßȪçÓÆ“ŠZwUƒ³¢vBVç”7L•VGÊÏ>Vœ}\¢ U4a¥užŠZ²ñQ©Â]Qï)mð)š•ÉR…[ÑôG^1'mKÊš8 eÐè(!€¤Z!Çßx‹Ba¼)‹6³GØ,H–b)žËøý¥Ï¶lÚLJ Ò©4„D¡RŒ.Ïä¼Ú¢ŽB¢Í(©MP›6¦Íò³v;¤²B­ÝÑ<ßsκ¢µëÅó²¶î›1f¨Uu^€T6‚²&”uø55‘B¡\½zÇR(š{ãæ›ä•+WÞÙ¾Ÿ$¨_ ”FG¨k“ÞIEê+Ŷ~Žy@Ø{Gà¸ꩨo(×6 ª›È>$èº)²Þ¨ØÇ
+Ì`‘ëÒ‹^¤é²‚ê£m¦_'òë 0 ³ÿEž•±‘ùz
+r]žé×J"ª#î΢€Q4e`cÚ¬ Žuˆ¿,;Ø|8Cÿñ±ûu_÷1—X+7¢áÏè?ñNuÜ€†í5I:¿¹hJËw넃ģåùô< A·‘1{ÛÙßgâ¹ô|—šÑ‹#‚`Ïm>c(:¸•O#
+Af]³•×ÿæýȽò³ÞÊ:weítù¼™/ïÉ”«êÜM²– ´Å'­›®T:•
+LÛ—4=”µ7~ÈIA¨"‰JO¤R©D"1==ýÚµkø†ë(—b)~"-áÈÃû²3³h(5ZëÉ0БšûæNBe5¤±@*ûŠ:ûÊvÔ¡iÖA*#¤µ@:ëò6kL‹RY ½m>@çŒ/hPk×Ëmæå¿ØIJ¤' à½;räˆßïÀG>Ÿï?Ouâá£Æú†´õˆ€&’HÉHúŽýgš$o€âži
+aïË‚·FEÂ!‚U„]veï]V¢qÒéL§´ÓJ*",‰!&©MR<¸v—…íž1Ö‰Mù˱å;ßyóÛÝ·»¿y¿ãûùÍ499†w8Õgž¯v=g°rŒM£…S]Ï9Õ0ã´•S >u>Q#µ
+‘°ŒÐ''ÝkÒ…ä“|°N3lÕÖeŒ7ª†ë´SíOÄ¢5+ƒ&MÈ”2åŒ ÐH¤³k¦Lðƒ¡†¼H½&\¯øcIÚ/VÆÉP.L@°XFÓ ¹\šÂ%›&þŽÙýùž²ÈѺðÁß÷• <2\|| °âjddÿÑðŠqࢊÛÅ· ;pâEUÍLÞK¨P$ÆP¡Ƈ¢¥2ƒÁðpÕŒOÆ”7ñ阎g<öÀ$]þW7l ù).æÁspDâY‰I3ËŽsêlß«ñqNÙ8µvŽÑÆ©±Ì<Ù8«Òú\µã‰1y8UöÙ•nü≠á
+1!^ZðûìêcKUä»ÎlW'X2iýë…×7o A®˜Æáh.A!qñI¿ÜRbÖ´\Px¯+<WÎvf¢fu¨š{Ug®°ù¦®M²Dáý§ÆÑþÚ®"J“¨Â%8MÑÐÜháh„X¡,к{˜æö˜EIb%PÞjeéËßvx:’s (N„Hœ˜ålÍ>óa¾½UW{~ËÛ–´ÊózG[Ž¯WýÎgÞn@#­³#×å×9;uÞn¥Û/o¹úÔùñX2î•vvKO†ç²êL‡Þòé’µAŒQœBx¼=ùù·nÞ †¾™9cc,)ª\Á;gû4ãã›=^&‚¸³‚Ç%d9§ßKwõNkäÙÊ¡ÓÊY ª¤A3Z›iß5k¾´ï«ÒllÎ\p³V?hÌŽ˜U·ù  ½°ž‹ófŒìY¿bkÒ<Cá"œôžPêÕàëFÍý&&Ш5Ë%¦ÚŸa«bتhPÝ7kG,º°-'ܨ1¦˜á¦ìqSVÄ,ÿÐÖMË"!x
+ Ä$Åh>)ƒÉyêÌË<|,\V}iÿîÃ#Åå÷ŠËû‹*î:8x|ªùêPÙƒGF –”Ÿè/ªúxÏQýËkæqù(*ÄÄ4‰2‚^Ÿ¼¶µ³]Dì~øàäõ]ûátLÇÿ^€%ð`þOx'LR^vÂPJ$Ž‚…(‰aBÆ$ÌN‘sªOs|œZ
+#bq
+úȃRråò¥õ¯nâÑ4&[€R2¾‘Åp—®ü~AÙ_0:'(bpqê<£S]ÿ$Ì<m}¾ôW¶H6Q¸\šh²9#“ð
+­nY»[Ô~uŽFž-¿z¸È.Xa+XþÁÁÜègà/o[F=0ʧ´¹6u'1cWqá I5¡+56cÉc½¾ ñeã¦ßNš‹¿u-N’0Ž+W'y êp³$ •øM¢I]á¤I>ÛõLU>£Â¯Z}SSçÙ AÞa0Ãâa”d¢¥{Ëoß¾M‡½
+‡09¸bõ­êúÏß©Û,p¨v¼ª È· zÿ‘ÑŠƒU[6§â› %bœgÃ?‰ßÞÞ=†‘šÒÒCûŸ”>ŸÏ÷EÅ gLæ9›³ï“EwGQ€i4 ²(ŠÆq 0œ Q±ï§[·¿P[Ó`ŽÑžLó¾áGzëë[Ÿ«wÆÔµÅ4¶Ík4$/M'¹‹Å!¨=Jà0¨3BÇ·´(ÒD÷ïèèhYYC
+ ‰lˆ‰#xü|”¿|S¡üä9`Bðµ;FmÝ
+Ä>­”6Â͹@Á'ZT~£Ä¯N6+Æ\ŸQè5€N±§)Ÿ6 BF_+kU„´ÙcͲ?W‰ëóWiòÖú› Bæ=!Cîc]ɘy÷XËÛãÚ]ÁV¥§1;¤UÒzeàT.mTÜi•zšòhsÁÝÕ–ÕK`.‹'òx
+ãc,>‰¢‰äÂÁZ­Ü×íÎ`£87b¡–ïð•÷wð
+-T7Îeé‡@zÑ #zö.· S6§Ðê”Ú9݃æ›ù†©3–Yåq[öÒÙ뢙1ÂÅYF`DÆ 4>B‹ea8´ÀÛ°e[Z™V~îJa÷ Ò6širNˆ€sÌß™FdV'ä?Ox~EX‘¼vã÷€FÀ}m
+à ‘%1‰F>£–º Ù †,Ýy­rN+ØpésüQ°™ÒH=š kÈ÷´AâÒH½ÍÀ*Ù‹íŠI­hJ-ö·åúÔâ 6ý“Š±ŒHNí±|OgÞÌy‘K'Ý÷k3\-b_[¬åïTxÔY.}^x­év¹¿]ù•F%ÞH²Ù, ZÍ|’ýC’0É6“qN²- >ìî(8’úF’1NîÖ¤Y]Ö¬!Ú±‰ruöÎõ
+_Û¹6]ÉŒc,8œl2>!áìé3Aÿ¿û»pNé¡òµl˶lhz8øöÅ17ë^HHBQ&Çâ’‡òVqãé[_~âÄ©§šÚ"4Æ'[zVTwEhºVTT<»z3Š¢ ç솚:@‘Ðl@8ÿãMî¸×ëývà÷ÃÓã:0É~Å‘h„À9à|)¢q“7Ì“5ÿ>ÇxKe I›¸Û!±õK-·•æ[À$â.»Ð4*ê¾'6É{F¥6§ØØ/³ (»JË€ÌØb*j¾ºù@~LÊöŸ½ŸcìTÚRÞPa̸ƒõ
+¹VìÕJgôù3-B—F4gP.¶«@å}Ò]ZÅ¢>ËgȆÎ }:•WŸ5¯–{Z”@#c­ÒÄxLJ b?){kÞ@M¶Ò(‚ÚýAµÐ×)œÕÈ;rî7uòÝ%{6œ~{°ŠG¯¸Y%‹c< Ç8aœ¾2©-qJëÓ·Ñ ƒ³s-×­—ºµ2{…€ÇŠ}_‹ Ÿ«óç ™”¥ñé?brÉGþÞ$žmÚë·äÿ­5ïsu.,Ôgøšßò¨0b¹ÕBØuhû‰O'óëå¡/£•Í«%S%ð’ÿ|†§]<mMê >,Í8¼‰%œAÇ0çpHn ŽA’§È½wïÞ’Ö/Õ±ðH Á€Ï~÷Î8‰ÐLIDwfPS
+„Ð ZÁ y:~ž_qa<Ê# ¯
+-ƒ@ž¶c;~Cè¶>”jl¥oâÄŽí$°M[ÕJ£!'~ûöu¼UUÕhBê?òÓWÖuºûÝé÷û~?ß?+ ŸkþÊ㶠|Uóç‹òòŠ„¯WDÖ‚lÉB<Ob¿-³ ­Û­a8—#X_ñ¥eTuýKqŸWØj„63‡•‘Ë*f(ÿ¨énèû¼òÈ•WÅÇŠ«ö¿©:Až·6Yo©œ^Òõ×F(ÁæPˆëmcJ³OP±xµæ‘ªë~¡Ã-·N$fŸÈœ²Š¢oN]h,îz`€Å+6{ä6
+æA­mx®ë. 0€Â1ªt'ÄWGD×<•Ö‡"à™Ë«vyU¶!€œÒv_é|¨½î‘šnÉM·Ió=ÒôPi÷ËíãBs Ò¨sÕZ<"ÛȾ'Pnû¢Q:FNÌéÏb2!û³PöβŠÿ@ †
+';Ô`‰çÝÂX—œ2«§.–¨Qê•M ÏÛÁE䄱qR§ õ¨]êX‡2Ü©˜Ö©žTT¯æiwÝ”Q1Õ£é×l)]Mä²6€nv6—Ïg2hHÖ¢ýš'_üŠÌ¤³TÿøìøNZKû$I±ä½sçQ„ùŠA Å+–>:ðNôØo¿jnž¸0yøtèh¤u¢¹õë–ÖÉ·ß <7}êì×Í-á'Ÿ7·†O]ð¾u²z]Q.“. £g±p¡½\¸Ò¤ïMFã
+^Y'~¦Ûãl(Ý‹àôÌ\œKdaŸÏcç@¯ƾ=š¯¾xLQ¡DÃ÷ë4í“tƘ=Odz*øJÑZh0”c´³›·Ç[®<9~vâðiêè{S‡Z§Þ~w²åìÓæ3¡cg‚‡N>=|bâø™hó¹'Íï^¨®^Ãæ/Éä2 &‹%Àxt--/ ÿIy *Iå¢ïdÔ´Læq2?æÇÿ°wÒÛ–úfÅS‘˜‰Âþ
+C‡ÅQà8ÁFÑLÂ[º¨x3»¨˜À²&À üV•CÓJ b³÷Jýü ›ï{»
+är|ÄN›Ðªî¶+G $ØIÈÁ¤1•v IðùÆ~÷³ ÙØi•øcòè'ûyåã}~öïù~?O™×Rå7¤ÏîŸi‘Ó— €Cf[sí*À˜Ô×ZÊ´‹™¶¦¬Ò@[™·C>Û®}tNÑ\¶m-ñŠ
+„mÞí£½‘ôÆŸ®í|#[€ˆØ8,¢`Îb“‚á‰
+Ë°Ì씚ò^§€øq.9ÄýcRÛ¨ÒtOÝíÐt jÃÐ2(íÛï˯L”Úî•Zª+cJÛ7’ž!mï8€MïP„%\2ÓøB
+†Î´è®~=‘‚`
+Á×jw2ÝŠ‡çd<,–O¥(ñø#…§]ìíVÙõo%A±œXhw&6gª¿xp=ñ~ÊGó×ó>Ñç´ªömß½Šy
+
+ȳ«³7wÅPÉÃCöæGÇߟ;Ñôô¼Ç~óä至ÇçŽ4¸5.:Þ9Ù¸cŸˆ—„ËàóD<†ÅÃ…|næ  ù½aÙôûýa-Š®à 5,AÈR,ÅÑV]p|Ð> ÁAy£EGýÝÿ”ñt~Ô“¾|†Æ‹`ƒ Ga(Æêêê˜øæéðGB‘ ú¥UíèpZHÀ]BóÁ(x‚þ@À•˜QØ°À&æA>ÿðáêÊj#a”@Q˜c‹ƒàTæælÕIMËç*‹¨Úê”™ï&(·G6˜‡•¶1©ið ùV…ePeø¶mªôÊ}©i@rù²wRsiDÚwWÜs£ºoJlú³0y…‰Dñ° Ž#$•øÚ¦­ü´t'y1q|29ï„Qf¹Ee„ ±Ù¥GÀ­÷ôO)?UTÓ,Â`®L7ti¬ûžãb$[˜°nKAõéU¥Ë7äíz·Sb•÷|µ©@˦)ÛŠ4‹¹¹l¡ëÆMùF^Æ.é/U'öÖ[ ÖE©@múFmU؇¥ýCâ~§ØvWbsHí.EÏ ÊâTÚ‡@.± %·…!°Ôr šÒî(ï¹­4»ØÆU]#2@šQ™m,iïEï(ø¸¶×õƒiÄâTÙ\åf ›•ÀìÉÝË„ü‹”
+¦¥x®»¢S³‹ãÂÎIC¿í¬dÚŠ˜ö²¿výuÖjg:]².ÐVT—³&‰c¼åÍ…)€y<ݺÍ+¹|Œ øäå槪ÙvéDKe
+;†O¦òÐecM…³Š³ùù›Â„³ýã½ RI4oÓÖâsñ¦¼ Lk)€wÛ~w‡œ¹PÈt(|åÞ6·³bÖ(óvÉé6m Uã3JæÛet¨¤ðz[J¼Ò§FùÜEiÀ\ñ]Oe_õ®¢×¨t$ž€ù$ORŠc…âܵY>niùþÉtäÿõ†|ð4µxÐúð.Št,ò‚Ÿ¦DÌ_@®ý—Cà„
+åð0hcIñÊÌœ´-»W'¥Çp9jù®çÕG¸Hë0@)­yPcu‚‚‹­ãŠÞI©ití¯JþÁ~7qp
+W¯éôhE{_¥XÕ=Xí½üM5
+íïÑùFb†’;¶íÛËxåÏD#¬U4«Cv Û¦šl)™rj
+Œ‰N–­ºçÙëÒnÄ0 ÅånÉYv”Gj½†B8NÑ9Ya÷>¶µ8b7mËâðD$ÆãuÕl šËï¶×,ZQB‚‹ÈQð)™í¬ÝrzOÆGadφ´¨SûHTA›Šu2¬]5í©#ãšrÕ­†°U7cgbVÅL«:faf¬ê9–Øé˜Su
+3… /ä¦
+y$å"òeXüèI:j)c}ÚY'}·™‰Ù ¬M=ÖfºÔDoMáP¡€»Ø]³%æ® ›`¤ C°ÄzöLXkcNkQ±-¥¬¥"f¯·Ußwébzʪ³FÍ»íÆm[2E˜‹@ŒcŠðQb®@hFjúÏùòÖè¿Àãi:d¾AÐd4vüXø¢•Bð+0qñíÆ@©®@$%1.$‚Ió*Ÿ-O÷ûý¯i¡PˆýŸYå²ÿÏ€)ü$ÑÈô×,™ùvÌʹÕ㫇'§æ‡ðñGç^Û^ˆÂˆ€&!˜”À)˜O‚e›Jæ§,ÍÚ´KÝØ^ëø­ÉßKw —zéÎ!] Ã๮ö2þa­¯lµê°_®AM¥gÿØ™ýÐqBNÎýä$'Y@EA[A­—ê¨H r!„ â¥ìÔÖªnwqu–%$$$!w¼ða·ÝvVk»Þ; /³vv‹Ë,·@"§oÈ,ÃìÔ/;vþóNò$9óœ7çœçùÿžÖîÃ?o}–R8¤åVÙn–yƒ²Ëdî`•gHçÔ¶;»4Ÿ>’;úõWª-×EÙ»h”àL
+É-eu
+
+Î( ·hºÏªÿiæ:!P
+¥¤|ŒÀIÃ¥”lÞ¼Ùh4~ýÍÓ¹…»»ˆ /Ä‘HäEµ‚0·*ÂeoÊÅ!&@¤<x»4D8NBÃpÓ¢úS?ú ÷{<[P,É Üϲ–µ¬ÿNÿëÉå…(ÖÁ¸…¦±8Å,Ýj,¿>yòD¯«KOË€aˆ"aåc| !I8tJÎŽ=ús¬ñ:ß`¥' pöV£÷*|4íªœ]€Lôî¡*Ó­}††5{­=T[ëë×´?f}+œAÖ`Û¬=
+µæ.½ó­CW°F yÌ'¤TVJÜgõÅ_“ÍÚjÇšÊ9‡ŠkeÿeU?n:ÅH΀G`3*ÓxÂά¤›gKBm†pc)à°…k>ÌÙ¨§Z5!»*lS†LeÓfÕ„½.øauGíþ]ëédô‡b8¸¼§(Tˆ%¢«„
+IÒÖ”W¼ï5x{Tî€Æß[æKá¼Ãzº—GËë\ÝG=7u®;†+£Ö^½¿¦= òõ•ûzÕ¶¾ê«£ÀÊuž
+0aÉø°xQÍa~>²ð͹¿è
+ غmYÒJ2ÒtšÒ6-ÓÒàË’l& ™N‡q_:,iû§ž´¾tè4Íø?Ïì¼³ûî¾ÏîÎóþOÓÜÎÃæ@˜õ…OÈÌE`í¿ü`òê‚­Z®£:دs¶™Ý­&o›Îß«ôÜÛóó«Y%Ò¼ùzO»éjÂÛclê4xÛjšû7Ö^’bù ³–lÈ¡›î²þˆ¦e@Ôp!¶¹nÑ{Ø–!ƒ`á‚Õ(.c”„”ec²¹ÄÂoIä&“f‘…R-šÏØnt’¢|’šCP”Òò*0 ¼x}ÿY‹¡ù÷@ª`7 
+jåê‹ŠI°¼%ÂLD ó$x¡H”µrÅ¢7Nýì“!>ŠÇ¾`$Ÿžä'MR™HdÎA㘮_(Ø©Vbº„Ÿ&Ÿ¬žY!žžŒ©=(âLùO‡2éA°*
+I;Ñ ¿}lP®€*ÆҶC|=›v2 ËÞ{õÇe†Hç y¢§èÔ%&m©±U&ì
+¸u˜£6eû»5r
+åyÙ¹8ûÚ„».mQ<-Ï1»1î0¥œÚ´†ii‹*e¥Ó€–Š”Mµ«Gì˜S?á0&­€%†q;sh!¢VMÌƽcŽša{-IÂÁ>n`>¿¤óWmV­™_H YAà"1I‰„8¡d ph•úÆñxæ¯MaÆt/ð?ÔT&SY wttÌÐÅŒf4£¯¯Ò_ÄçŸÝ?÷ÎÙ’eË1ºZ2Ÿ‹QœÈEÅÐüy³q¹xÙ+¥ÚÊKÑÎ[l “y?¬ºÖ£âº"L¾°!ÐÃz»Xw—ÞÞÐx{hoHíË€¸3ãí„
+Í’QRÖùGÖ׫ö÷~Ÿ>,
+òÄâå5žÎÚ_1—?\°t.ÉGá;`¹"RR´¼´¬ÂŒæ/ÞTúfðì×yLWð«ý£Ú ‡¹g >0Pm„¬¢() 4Ûn›³
+))ØØãñŒ1Ø¡!Ûv·i’¢®Ðª«F 3¾À7W´‡V9ö6›H —!æHJÔô­V-I
+Æ~¨ª(;ÆZm Ýa‚óLpVç埬ڙ¥>³`7.;Li—n±‡)WÀ*Eðëgëã6]âÒ1 ¡[êa.êö© BA ;HÚÍ&xfÑi~½¢XA
+(ÿÜ«‹—¸pçhª>X^š+òH¸F•{¿“ì4€Ä¢Óuh'·™„ÞKîfgã+nög·; Ë}VÁÓ´Ú]¿i©¼Àiî–˜Ûºìh^v4EæDÏé„Í*I84q§6êÒƒyÄ7 ÝÇ×ìº%ž}ȳ«뿜Ϳ?}¨­ZU*CåD rˆ’aJJ¦Î‘¢"ŠÂô'•;ßÿù{³‘°^«ñïVÐSÉ\ÿ/ïæÉ $ý$Å$“É--*›l²Éfó<¡ÈGZȘd9‘æ}û÷WIÀyœ‡"" ƒJ0)ð !!)yEYuÛ֋ºÎ«ÖPXïšlY`‡ç|“ÚÀ$ ë›h L°Á0çŸâ¼aÖ3 º<˜Ð†3É8 $3ÖïfµýáFðý‰SÞºO‡jÏûZzol¦‘’]U²î¤7Ø8ÿ°Ñ;fòŒ±ž)ËÀ,;t¿>pïÝÁ0Q¹,
+C*
+)«Ø €¤Ü3æÔ{Ž”â
+áµúØÐŒ®Ãár’ÉIõË{×ÿ:xð—6EYuL)Q’„Åoµû¯Øžk%£à|\^d Lq¾ÏÕÕµh¾HˆajŒT*`XŠBDQyÃÙnëàÇœÎaAý¡ñçE# Å/Ù¹d¯4î¸Ãëk:Z©À1Š€$ÃÖCñ®:Á]·êЀî·çL•<EUÊüÏ×ÕDùuɬð´éÀL ÁRYõKå¥ò—s±¥ &s^T²<ßÅÔ
+G0‘BnG9 Ã%„ä¼€Kò*Êwͧ¯_½–q{*‘Ì,–Ì5húOS$ƒ-̆F2Ó-×Q6Ùd“Í÷'olY™“Ôw÷S8G-ìË/ï··_¨¬¬ÀÅq“¡"¤PJ*a)Áa N*Ô{ªŽ´ý–¹xÙêù‡98fŽ0ƒó:ÿüBŒèB3thÞx2‚Cp’Žr‘ŒC þ0 iñL¶ /Ôûæ´C ôåZºK‡l¦¤ …Ä8¥ªýEp…aðŽqà;0äcÜ¡ý­oºÍûw‰²˜ÄÄ8"~ç³?0Á‰×Z{FÀ9wûžÃ†¡y}ð~«oVUAâ|ˆ4v_§}w^Ѿ¯@sŠÊª¶ùþF¦ÿÄ©žá<L¦D) k.]d:Òî“ŒIU;wé|ctp¼øÀ L¼MˆÐâòƒ–öƒÜYTU®¦ÄHº÷˜‰õÝÒÜ¥½3æPDã¹õ¼h$Õßs6¯Ùµ1»i‰gVœ´i_)ã
+TòÉñ=+žÓ_÷[¦í§ù¦c»Hƒ±<¬¸†Ìí0D]͉þæ”ídljݦ)Ô˜4GAÁ0
+§r¨BË’HQnÔ4ünd0µòHHÅ’ÉäÓÿ˜>Óè7(²µ y¦*PÌ3•g“M6Ùü¿%³Se6ϵ'Y¿ ÎyIpêÖâ©t|}œû°]%WQ8%“É0A11ŠH(·XIá³_çÏMœgÀÿ€NÛClI{ë°1Æ'G8p8¤¡b˜:Å`lkwµ«Ã²± MIÈ@šÐä€é¬ÓÈÒê²I§0Ó !”I lÉ–-Ù8éù ÓfX²N[}dQÓÆÓ™6 }çwÞ]­¤WÚ}÷ù,Ž*–—n|¦Ž{QyêW:‡ŸÆ•Âçj/cL)Œ)ãY“xð‡w©=c:$'ÆÐö µ¹Ç9÷$íž =AÚí½äøÊƼãÙ{äTÿ*Ýó;Ö7ó®
+ÅHT\Óío«Ü¡\ŠJd(¹~)]ã¼+¨u}²ìéȊĸ¤Dßwƒóúë_ëÅyq¾ªv›j`´Y”oª_
+•mo;Î÷Ýèöú71/+Ge"lÕSû˜Þ+Êþ0cv¸Bªàâ‘i³*jP§ŒM ³.bÑ'z™wŸ §(öôŠŠCÏTׯ“/—•¢âbŒXZL ÞŽÝ)[ç=‹öK#1øøxS©¸PQô­*ô‘ÝëËŽ6lhÛzûÝñ3|Ê ÉØ»â=lÊÂÄ,ì=3›ìå“&.iRÒ½mð¸"nh‰Z#í³n¡yF-긕º¤ÍLÒÔšèiM§Y9ÐHv§‘»ûsÖÿÊþS-u;VËpÑ· —KÄrñ’2™”’.-(ÂQ‰x)!¢HK·¼î×_ü5(O¦ ƒ…‘=/
+E".‘)H1!CäÅ›öè]ŸªÜÃuìK„d1FÔ2‡Z\èpŒTm®GÐ̾×½ý3kh#Û%#"|ÙúÆ.­ý:í
+©¼“Lï`»àX4±e%6Ò‹júÊd_÷­Óª®Ç¿ûz}•aÿ†ׇO4Muq³zƦJ˜ÔBÒÄ%Œ*賓4³Ð@,³&fkƒµh#mÔ¦±ª£&ÞïanÆ
+VQÇ,ì´ Žç¡Ålêi+?e¦£V:ac§-­1[×èIÆÞQ¯Ù±vu NI
+å(QŒÀUHÀ |ÊõÐàÊ\½rÕ±cÇ.]º”LfÿÔ\ŸO>ùä“ÏÃ’œF Ï=pHà©1•HN„Çßøéëk_÷üÜýŸ,ÂKPªŒ”“"ablYQTlY÷¼¶ñÅÓݦóÝö+m}×4žÎ7ÎxÇÕçþȸnÑŽ,9Ô®Æ1¢µ3Fx_@åb…!Nh]£m®°ÞùwöË5¶©ó À?÷oc!ŽÏ9ö±Ø!ÑnLÍÂ2 $tRË-ŒØ±ÏÕvì\HÃFÕÒi-í6Ú®äâ»ûøØq(ÚXÕUÛØŠ!ñ-‰
+ab±TŒ–%r© $ðä¶Ö#o]|3
+ƒ¢R©áT4›‡‡‡‡ç g½¯ I©P½=ŸÍ¬w—ÌÃ#G·J9*@j!XÕÔÃP‚!©@(Å0V_»·ù@G¯ê‚Ýhÿ°›½Iy£:n^ïOè¸8Ã&ôܒηD{:v¶Ë1Å€·Þ;ZÏ4雥œ å‹è º@‚ð'4¾5ßÌF(Ï‚>xOÍFÕÜœîÊ=zbé÷ŒúÊ¢Ž éÙÛ½c3¸ç&31wš ©<w.¤ç¦üºï>€Ÿ?{á@§±Z¦Àખãú³¾)Ò=ó½Žó_*ª±ºàçL£1Üs«zg“°† åÉw‚†‰E½ª¹ã „5RE‹¦¯ÏûW‚Á¹á‹R\h«ØHÎi*ºMÀC²VmÞE­Yˆe3rö€ÃŒES°Q9 V 
+i;Pr³}VìIžr“k.â_NâKñOù;¾ê¤“NÄšIš©¬EŸ³òæ®’õDiøE (åË]]Y·!mÓ®\¶œYxƒ êŸýYëÓmõð.a&‰Q)&®CáZUbXƒX¢A("ÊaãΣ¡ëÊÄägŸ>
+`YuínEÓѦÎþç^Ò]ëòý÷Ï09aÆæèÀ<Å-P¾yÒ» õÇp.†û#$¡}!êòÎ=³›Ùp˜Nvšàf˜@„ö‡Á%ÆÀ¼ÆïS£w¨Ñi­^í_¢G§vÅh
+c"¡¼ª¯êP¬‘H †(
+¡à+‘‹ ©
+çááááy’]½’T„$ÿˆu3Éd2{~©P*fó`-GLÑR.]J­æÞ¿<;гï»{1‘#’:±¬–É L)”Ô 1HDXP¼C$F;÷îÛ÷cêG†óÄ›£½öºÙ)¦<ÐãÚ±%-Çý M Ñéƒ
+!J‰ìèƒo¼üÚ&®~žÊ§€i”í·”/®×]ññ[X®ÕǪô?ltž' $›5íõçAÏ/ç…hñåÁ/O…òš4-ÀÀ-dóùôƒÿ7ûeúÜÄyÇñ¡i±±¤Ýg]>€4M$¡´a Ó™¦l0Á$>¤=´’%Ì‘6Óp$)-MÓĤ´`Ë’l,kÉΛÐf¦%Í0|è´|À ˜¦oÊ°-iuõ'«xÚî;^0ÝÏ|gçÙgµ‡žç™ý}6‰ðœcãó›1aF "¤Ñ€¬jDd#†×tV}]F &„ɦMM¯´líz»ùý ¶ßü²'x½{ä+Ž3R”Q¦¡âƒ¨¬f#Ýò­éöáÐœs8åPf;FbŽÈŒKJ bÌIÙ†ÇY9ÅÉ3‡Ã)Gïè »ºê­M4iE:¬G&Álýζ¶c?£îÐ4+Æy)æIt‹³Œ2g“¦ÝÁÜÂ5:w@IrÊtg8æ‘v1Æ„Rò4دÌòbJÓ¼”`ái¥è“b#à¥Ag1àÊû„œvyÐHÞ[¯}ÑÏÝ÷Ú¡T¤äV»NÙk+¸¥ßw-ô9†Þ¾xgæ¬ëò‘×ß?°£ëåõÛÍuÏž²êk,$IÒæ=…a¸…0šõ„E‡êNê¾i¦ÖXpËŽ­;O¼óÞ_>¿ö{‹¹B9SªŒšª–‹¥J*R\Yo¥*å‡*R¥*$Ð
+]=ª©ˆ†††ÆA6›ýGW^æ+/ÿ|Y-@í¬¤R–+A±TÈWÜdùÓ5_‘–Ê^.{n61 yŽº7nÞHQ‰C”U!#ŽWíê.aøº-M?ؽ­ãHóÉs¶ Ä9ƉÑÕläàg“íÊ8¯L±J”K¼%O¸ÆRÂð'ÇlJªLft<wHÓ¼2Ï„“-½—ZNžî?t\wŽ&;–eÞtF&@„ÀI:F§;" î‰2'™èM¢ct\¿âÄ©žPÚ®Äà²N)î‘âÝ¡œ„‡a#©'ÅFTP/“9Ï}Ý9¯ð Ë^ô<8ÊÚ˾.u ³p‘[òÙÕ!!ÛϨý«^gê,{éø¾OÚ·þdצ½[7 o™ µ4BàzŒÀ š¤¬nD˜‘",n1#‰€ÞyöÙöÎÎsgÏ}yõZ©¤VW)_Ìga[.ÜfË…L¹Y*–óå¥\9[QaXˆdE9TU­öT…deÅV°Ò©¡¡¡¡ñÎÝ»weY>vìØÎ;BP¥@O*~‚hTÛŽ,z¬',ˆ" È G´±á…—¿¿«¥í÷ég†‡¾è‰ÜäÃö±ä[cÓGÓÝrÜŠu+IAIØB“ ]JÔ‰»G®`Ü£¤¡(ø'Gy|ƒ ßf”9›4È3œ¼)ÍË3à]‘¤=œ]aåÈ /O rÚN@x%zÃK ˆCN:¥¤ Æ `#.9%(•@?íϺ¤˜KœÆè È@ZææÝ?.–2«LNÍÁ•?9ókJ_«£š°šë'^_ðñLnÀ®ú˜ìrr~ö_s`Õ€#3Àæý¬ê³eû:ò¾®¼·SâA*¼]?“õÛrûrlÙ lyЭö³y¯ppîb¿½0ä\h+Ø3¾vuоØß=K^®0Ô½4ÄÝóó×NíŽÝõQçþ‡›[¾÷üÓ¨Ž0à8"Á3!F‚‚6®3)º:Å+“[MkkëñãÇÇÆÆîܹ³" š-hhhhh<VŠÅâJ­ÉçóÕÏÕû÷ïÿõêç>ýí¾–½Ö­¯
+ /h ­71ÂD)Ò„!
+šmÅñ=fÕQO£u[6lkÞÞÖ³ï𯘼oŽÛBqNJ°b|À%'øáI—>›·Ë©N)e“gmòœ]¹ÝºÅŽ}í¾æ
+Ý<¤LyäÉnq¼[žt+S|ð†32ÅËãñ¦Sžr+1—4%„&#㬜`¤8„S’¼œdÅJ8)®ÆÂHÉjX)Í(3lxÖ6švK΋aaÄñ}-Í0 «ŽO> CT,•Ï~ø›z
+ÇŒ Ýš?ÿlOÙË?2jÀ™ó YŸ£²õ ¹€¶Ø>yà|ÂRÀ•rç.zÀ^Ô>¦äs”]%¿÷ry¯¢öóK aÁkÏ
+Y¯­ìgK^[¡¯³4ì¹wž™û¸óOﶴ|ðæNáµ—ö¼¸î»ª é,ú:˜ #A›ŒVDXëôF0Iƒ@ r‚ôXƒÙ
+b¹¶¦Öj¶ìy}÷©'Å‘P*‘,æ °þky<Þõ§¡¡¡¡¡±LUHþ½îTJR ”J…b>§þýo_ÿñò•“ÇO´ìi6‘$8 Ô2d@˜£‘¦Ì˜žZ‡¬úº·€±”‘6á“fd ŸÛ¸íGÛZ˜W§^=ÖÛ|FrK_¹‚q˜r†=‘´KŽ»ÃIfø†K‰òJŒ•¢œ·+‰Ž‘˜MIÛGou*³Ý⼜ᆧ#3|hÎ!ÎóÒ¼ Ü9¤Iœ ÄJœRŒ§x)*(qW$éM: ^‰Ã•ÛBñCÊDÅ1ÔTÑ)ü@ë\¾´úðT†lä£3‚¬Å)“nÍçïµgöG&çå³ý\Áï„dúØ%/ZY80ï·LÖo[°f,øE?—ó²K˜Å~6ãsfîLàТ¿çÁ€ç^¿'õËö«ï¶Žmþ¸clj7¶ó¯½¸ç¥Æçê¿Ýˆ,M´Õ„fºµõ4iÄ §hÂ3b5$H²¶ÆjÐ5 ®3Ô›,à$úÚ:-7µµ¾qæ翸réòlz&—É¿ƒY©ÌøÃa¨ø×CïâÓÐÐÐÐЀú³Z¹)ýG `U£
+óóóW®\éííÝ¿wÿúÆõF›3a’´Ð4Òë1½&)œÇiì&´Æ„×b5OAé·FÚ@Q:¨œþÉn™ÿFqÝü?hB!¶wæsìzÁæH Ð$4…¤j! ïÚÜ—Áfm“&=ÄQ5Á9l6ÐÐBHh*0fm RÕVUZÒ
+Urû>þúù;ß÷v<óÞJßÏ´YófÌ-»´îɪ­K^<¸lçÑê7~¹©õRí‘‹5­=O·_«íøcUÛ*Ûnlh¿ vQÓÑ[{êZUGïºäå5ï¯9yeCÛ•êäUˆªã½UǯU'¯C@’踺±½¬TwôfCòúÊŽ›‰Öžº£=ˆð(Wðy±Òr{ä–k»?Ь]»ÀF
+dåìä6|r¨vØȼ•H½^i¾Yeÿdcÿ¡õé7«¬·€{¤Ôõý´î¯Ößhˆwo_عu~çÖ§š%¯¬[ðÂêo<|õÜK¿žW„Â÷EY¾*‹Ri'J~(,Ó(QØG1&…dŽ)¡"Q™ɰψIÒDMcÌ)„AÌšñðŠeË_®©#Ù~ùRϧâšU žmº¦5xÊ`žC¿`ªétúÞ}ã@ h¹Ð‰ü1(ú•ì¢·G;†ax½Ìùèoÿè|ïÜî†æçŸynñ‚Ò©Ñ)\":Q4ÌUÄŠ‰¦< b®È„Š W*
+UÕ/‚#…!ÎÔHDÆìHaªäiÓ”is¢³¿9ýÉŠ¹K7Í_·¹´nÇšm¯•¼’\½ÿW5GºjÛºkÚ/Õz?q¢gmÛyЕšŽíZÛz¢ªãfõ‰׵ݨj½³±írâø•D{ï@$¯­J~°)Ùûôñ«!ªRG˜ºbÉr÷}FÀt›t?,hÞ½W§²M‘gÍiY1oب^ðè²9Óⳋ*™{¤¸ü±)¥³&>QŒ£벤ÊHE”cFÇ l$¢‰Ëù
+EîæȘkQ¦ËÃ…XÑ%
+{HeB ÃœçS¡É÷ƒ¨rIáBØÉDBTg…}izy,¾±:±gßþŸý⮋½ÿìÏ€C€`øâaæ]½ôô 4öÞN3ȃ£ÑT@ ¸§ í89ú‘½Òí_ÐàlÛòfÝþ}m°»y± #õ—?ß9óÛw÷ïkÙ¾åù‹K¢S'3M“¥,LqD哈)æùay¼<á N9…V¬` ñ(?®ˆ±‰Œ*’ [‘)“yD)T
+´0‰j4ÊP$¢O™>ã±Ùs¾>óÑ'fÆ7}uå÷æVnùZå–ùuõÛö­®}勾|ªlçÏWî·êðùš£—ëŽ]1q¤ç»­·ë¬;ôžŽJ¡ˆªWÄ*¬”9Òþ¤@Dl×Fw63)1*ÄyãaCcŒÊ2CH!„S¢r¦)œb¤Pa®`^(!]Æáb,M”BQ$M¢4‚Ï“x#¡T†P†$YWt‡©ÄÀ:
+'ÏzjñªEe+ëZ^ÝÝr¦««ûÒïûŒ~× Ýí·3fÊ1RŽÑçXýÇ4x‚®Cf‚ÃrÉvÓ4³/}!bŽ¨@ð_úQ¶œ˜†åd²§ Û®„8f&#„å…᤽ѱ§?m]ì¾ðö‰ä÷4½°ù[kâ gNÿJQ¤:,EcL)f* ¡N¸"H4¦¨”sL´iLÂfJW5ªªf!aÆ õƒ`Y¢&a9ñÝ
+2&BÊG-
+¢‚BDe,ñh¸¸tQ|Aéê5ëŸÝñÒî½ûœ:ùvçÙ3Þ¼n¦>r,s ÒýNÆ¿°Æ͇?p:ï0|ÙÈØFjÀFÀ(rô2çX}ñp<9rVÞ£¯’@ Ÿ´!¿õ mRÁ¥ßí2Ö@#³Ûr,Óh„¦}·)ºæÒgZ;NÚ4¡3zmÑm–®¢ô¥RwîÜé:×™lmûQóÞ›·­*«(‹—O™<ɘ1FA’Ì8Q1†
+AÇÝ\ ‚1Šíá>qŒTÚ2LH 2–í؈«Wz/v_ø]g×oÞùõ±£­ûøZËîæM{š[_møγß^±lylqYYéb—”W”Çâþ—ñr¨ÛvgT_)HFìì‡÷B"ÁØ"ã14÷ ”À¯Ãhš¦_€Ü¯ŠɾƒaÁe*•ýy`q¶‡@žmCÉž…ÅBE@ [­<3ˆ_„¦sCÃôfs*ÃæþJL†_L1–¡À“øÏ6ºŠ8Yš”})`¬ðŸÙHŽr õ“ Ëòõ#ð¿ò¹àÿ‡@Brr˲‚)ÇsƒlÉQ¨XP÷oešfpÛàVA2,†ad/ðï6ÊúìYX,lD ‚1J Ý<ã©´e˜@d,Û±3nâUüâ@}°2bxŸõWÂmQ}áß”–à±Ï:ÒÝWÿ‹ük
+endstream endobj 1 0 obj << /Type /Page /Parent 1643 0 R /Resources 8 0 R /Contents 9 0 R /Annots [ 2 0 R 3 0 R 4 0 R 5 0 R 6 0 R 7 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 2 0 obj << /A << /URI (http://www.synthesis.ch/)/S /URI >> /Type /Annot /Subtype /Link /Rect [ 156 670 280 684 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 3 0 obj << /A << /URI (http://sourceforge.net/projects/expat)/S /URI >> /Type /Annot /Subtype /Link /Rect [ 206 617 400 631 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 4 0 obj << /A << /URI (http://sourceforge.net/projects/syncml-ctoolkit/)/S /URI >> /Type /Annot /Subtype /Link /Rect [ 181 571 426 585 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 5 0 obj << /A << /URI (http://www.zlib.net/)/S /URI >> /Type /Annot /Subtype /Link /Rect [ 240 502 342 516 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 6 0 obj << /A << /URI (http://www.sqlite.org/)/S /URI >> /Type /Annot /Subtype /Link /Rect [ 252 456 365 470 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 7 0 obj << /A << /URI (http://www.pcre.org/license.txt)/S /URI >> /Type /Annot /Subtype /Link /Rect [ 170 421 331 435 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 8 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 2027 0 R /TT4 2032 0 R /TT6 1604 0 R /TT8 1605 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 9 0 obj << /Length 3401 /Filter /FlateDecode >> stream
+H‰œWÛrÛÈ}çWLùiPE@¸_öM–U.mQ»ŽÉM6Yå"!k`P2ý!ù‡ýÇ<ät÷
+{ Sµ+&“·‹!‡!îËhŒ‘v)"ðrñ;WhÎËsÜPŽŒßúx&aL/c@ƒ¬hÊvëÒ²± ná6j“×û¼’6eyÏg
+c¢sÛVÏV„mîø—'gº“æSе™û`Ù±nа“>>,y]í°vËjnÁýDÄ“zy+ãf˜­®ëG3¸.Ô_-;•u0ÍeS«À§|G&¸æéyr¸¯á|ˆ˜Ÿ8~ÚbEa’x»
+~ycÀí8÷‡`¶&€
+¨RÀ‡ì—÷Eûmé{y;SmóÐ=ç»BÝé9'ÕÅî©Ø©f§®ª²¨»;K­Šv¹+ï‹S—µ*;Es–Íö` ’Oþà“7úä‰O»òqÝ«©è×*¯*Å--p×ÒŠ+G½â¶#»$±^õz“TÝtê¾PG~ ±ñÆØx&6ð¹$7ŠÏ8¨D/‹-öÒZ”ŸªÁ*;+D3 æ#%Ké(ÔVš›§r…( ‡f/MJ†4–‡\{°B€Ï'¹tbHU岨5ÓÔýêÅg³J! ò[Y”̵5žm`xï:ñãYZË®¬ÕéQØãYŒG¡{¦*„Ÿpò‚ªžjÃ34#°÷›^wÝö‡‹ "r-¢¡­²)KˆÍ&gåáDw1PÝžŠìÐ÷='ó‰}â|ÆLð“ …Tˆ“à(zþ±Gèë;ËáÄ°ÁX^|„€‘£Î‚20z_Î0¶Ý5«ý
+¨êÚ‹ö@…Œ6´©ì#»vô©ìF¾IQ—‘aâ™ã‡!Ñηñú!Ñi|ÿ?|E }¬|ü`ñq±f­æiä$‚§R‚8¸=}†$åR<:¾y­Œ^ßoFÒײ!6ÔŒC>AýR95k˜Ñêåä'ËF”ú¯ªÙš73
+2×Lë¡’šè%Nâ¹GHp£~£~Ôo´è¹õ¦æTˆ¨nå])»x*YüTÙ½qfv2w¬2x‰lHV2°åßmß3 ›P LÊr~žøŸ8Q
+‡ÅAq@-#Ð#¸‡]"<Ê#õý]ª1¼ÃÓ輆sõ?I³gÞ€‘ó~@¯>^Sª›Þï$dò8(ûσ”ÒMeŒQ|#„(Ô¤ÚÙ.wÌs&*1G%ETèþ¤åzY8Ýgâ t½Š!â(X¾Ë¬c¹ô{¢å§K¾ÿ½¬ð'ý%„CÙŽÊvÌyK^§CÙ•vBùœœfSæ$¾ŸÑ<׿ˆÞ@½z*øøÚ¦[j+"¡äª×ènw•oîM¯4¯‹“Ü'éF½Ú9ÑÆ_ËÌ NÜ#=Ø æwØ5–£«S»”T¹<ËMaBbùgÚˆ/¶ñpµôÆšÖ_`~iÍLØšÝÖ‰
+£».·—–ËõžÞEžaωžÑXimàU$×îâÒ¹­T;Òúr0fwà«DŸMZRQ%F:¯eýÒvõ¡Ù3‚dqÙ̹Óäô›9o I䊎|Ü·köMuã `Cn–]«VÍr¿)꼆Õ]¢˜Cyç¢ÅQuªš£ýäÐÕ •'ÒtA3·³Fh> æ
+áøá£àúz>Ÿý]½»™_Í.onÕålvA{¸ÞšZøo"ë/EP„µ´ÿø‰î‹›kRK Š’kJ‹!¸-Õÿe¼Z–S‡aè¯hifÚNË£ÜNW!qÀ3ÁNcÊÿÿÈ=zú`Ñ ‰YÒ9’üÉŠKøàå«üPŽãP÷vv) ±ÕóiWK7ó‡ 3wéBÜW½É FoL!.áçªû†î£"Œ_K=ˆJª6XÓ•ƒ¹ú+åõ&¹´D°ÛlwíØyfPGg6ñ&aqû¿ÄƒgT‘°‡O(ÖÏŒiÄ(õt4‘mjÕÎA·yìd" C(Õ%ýÚQø–ñÎÔ³k[ G5ªú÷“ý|ʬKŒÍÄÆ^]A¥xq¡åÕÊÍC•Ó8ÛÛî¼¹Ä7–µóTBðhàZLñ‘ƒþK¯ˆŠ¹ëm'é>–'ä›ÈW¸´àKþ9åÃ|Þ2ò|ÍH¥F¾Dd1‡Ì ßyäÌ¥kvƒ§>MŠAøÅð·•Çí¦e¼ÉÝðÍ,~ùúÈ}ì—‚ ‚‡…Ž6nl£¯r^þ1ÎþÓH5¨—J<€¾ºe‹»H·.fÿ1ã<œ@]slö>£ŠC¬5ÌsƒvŠƒ™+Ô s# !x„aŸ×îhé(¾»ÝŸì7¶ |£°6=~"3‘À3žii"˜ ïmËNŽ ²•Å¶‹ö‰‘ËL«²0Åé¤(˜;_KлÑ{HÙö÷dç|5Q«@gŬØóZþÖ^҄Ȥ>é
+BŠèŸ‡tòÑu@[j›SgÎœ%D•¢
+OD[‡H_‹ÂØ<›¥ofexžHu£ÌpÔêºRí©iõL1‰n1ñGnUvܤb“™™û¼Û^y·ÕÈ\Y³6ÖPô{áÞkõôUÃ3 ´Äíb£ž°à¬zkàÊy$²±/!³í Y;7þ\‰0p¿åooÐ2W‘Ù±à#hb¨p9õº>7Ü÷.(¹
+endstream endobj 10 0 obj << /Type /Page /Parent 1643 0 R /Resources 13 0 R /Contents 14 0 R /Annots [ 11 0 R 12 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 11 0 obj << /Dest [ 385 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 301 655 370 669 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 12 0 obj << /Dest [ 385 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 422 655 434 669 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 13 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 14 0 obj << /Length 2204 /Filter /FlateDecode >> stream
+H‰ŒWÛ’ÛÆ}çWté%ƒ*Â…ɧ”lYŽËVÕ2¥$R@pHŽ`\–Å|ˆÿÁÿ˜‡œî€+’ŽT[µƒžîÓݧ¾[O^®×1E´ÞM¢˜Büág¶J‚E¦´X̓t&´>N^~ß.)oeOHm^N^þøѾ„Aòž|â—§ÉGõ>óü$˜©½æßXQB޿׬ÄÀŠa°JqH² ÒО!ïÇbŠ¯æ©µô{ŒU?=?J‚T…KÚœéá\v¬ÄJ{°¤ZÓÒ«§ô¯ÿzþ÷ žÍƒ¹2^¨òÔN¦ûn
+1’¨¬ÜzþJÑoò_“è%²8 Rܬ_cM<8N—‘jùTÍÇÂ4Ž p"¸Ü-ÄÒëID†&ñ< ’4¢d,—”FÁlFˆq¶¤FOv“ïÖ#I„-á—h\x¹þÌùš¹|Í‚pfS&Wž‹™dÏš¸JÍ܆ô¶ì¡Ty+D°íóÎxKÄP•{¥1§A¸üä|Y˯ŠÂfFÛ|ðMþî'ª›Šh§”F—]KYK'Ý™—âôÖ› E­nžtƒ‡¦MÖê-Á™ª‡§)£ºÈº]ÕÉ”[í»¤ùQ%’+Žpq va]ª56—Ýà‰.÷¦ô¢eãÇÁRäE¡Z{s qð"eZ¹´û4 ·yUî̾oàRo—L¹§ŒÜµ‡#Uî MÿÀ!v7íL¡§t:˜ü@Çì8χcù½ùXxC<Æ0¿IX£ëÂäYÇgV ;Á c³s€
+Zœá~ðÅ8&»Òy±Ê
+€gÄ…Ê-öœÝKÁ\ëBtƒì˜á±—¶2ž¹nN‡æ ]ó¾õüšvçEøO窧¬ht¶=Ó!{ÒtªšG`Šæ<
+U|Ϋ#zmëÌbw'Çm-áÇ®ª¿¨¥cÕvcVž±ªÃKÎr ¹áâ†fÈMoU1€£ÌE9PØ’[êªÛé12’ÿœ’6½)P=½„QLEÇÁQ;c©_ëÇÃë¿ÝRžðÀl´Ï—lÿ—²8S]±(CLJŠ W<ÄS,¡º¶9¹·¸l“ŒÚÇÌs ¡؉eÂÌTÎ؉]»‰Q” ²?=^Š 9!qì2ð^ÎìÈ(Ž¿<H&>@ÈTŒâBìáî•wb¿Ú@@ŒˆÈšwIñ¦oËŠnêò“\·Ú×ø)¾dΑíÛRBBiå©’]u@%O¦Ï'IKÌÓVñ²ÝÂL”=e n+ÿ5ýAÆ•ë2è¼Fì¾xÅæ<žÙlÉM0WPØPˆüãÜ“Ù‘éЪڕ[Yêuƒ
+á®|ºH¶…’–Lmºyû×uÒu‘^Ús\©*<Ö <¡çpyÐðFË”æÅJ&®t•¼8² ™Tü¨MvRuîæ‹'Ìš õ #Þz¬5¸ ÑЃµÞ9dÍôò׬5©²=ª'£OH¿ìðïµt)¢abÃkÓˆê×Nà ÇÐ;L“¥ ¯¬0Ý•ÁnJupÚæD®ôOÅï îzk‰Ý]öàlîNÈ:-¿“m³º“ZÅ»\®²ÅÖ¬,y¢!
+m­NÐý¥:ióBM¹lŒ}~¿R/t::’Ú²¢Aº ‡=_jtöJ¾l´J6D‡ñ·OÄÌÁ´Û净>hŒ>y‰'>ol¹¢Vgê”[fb¨HÍ]yŠsÔé–;†á°&:¶·T[?cXZÔ…¾êgþ˜i‡Éµeð\#Šúƒh»Ælz+Î9×ä·éŒ¶ËA¤® ί¨…ÿór#æV’ÞqÃß :ÕÖ¿]Æ'šÖAOgêÍ,ZMån‡M£
+endstream endobj 15 0 obj << /Type /Page /Parent 1643 0 R /Resources 64 0 R /Contents 65 0 R /Annots [ 16 0 R 17 0 R 18 0 R 19 0 R 20 0 R 21 0 R 22 0 R 23 0 R 24 0 R 25 0 R 26 0 R 27 0 R 28 0 R 29 0 R 30 0 R 31 0 R 32 0 R 33 0 R 34 0 R 35 0 R 36 0 R 37 0 R 38 0 R 39 0 R 40 0 R 41 0 R 42 0 R 43 0 R 44 0 R 45 0 R 46 0 R 47 0 R 48 0 R 49 0 R 50 0 R 51 0 R 52 0 R 53 0 R 54 0 R 55 0 R 56 0 R 57 0 R 58 0 R 59 0 R 60 0 R 61 0 R 62 0 R 63 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 16 0 obj << /Dest [ 10 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 70 722 527 736 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 17 0 obj << /Dest [ 15 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 71 706 527 720 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 18 0 obj << /Dest [ 385 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 71 688 527 702 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 19 0 obj << /Dest [ 385 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 671 527 685 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 20 0 obj << /Dest [ 385 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 658 527 672 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 21 0 obj << /Dest [ 385 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 644 527 658 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 22 0 obj << /Dest [ 397 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 631 527 645 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 23 0 obj << /Dest [ 412 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 617 527 631 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 24 0 obj << /Dest [ 436 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 71 604 527 618 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 25 0 obj << /Dest [ 436 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 587 527 601 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 26 0 obj << /Dest [ 436 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 574 527 588 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 27 0 obj << /Dest [ 449 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 560 527 574 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 28 0 obj << /Dest [ 449 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 547 527 561 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 29 0 obj << /Dest [ 455 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 71 533 527 547 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 30 0 obj << /Dest [ 455 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 516 527 530 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 31 0 obj << /Dest [ 455 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 502 527 516 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 32 0 obj << /Dest [ 465 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 489 527 503 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 33 0 obj << /Dest [ 465 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 475 527 489 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 34 0 obj << /Dest [ 471 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 462 527 476 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 35 0 obj << /Dest [ 471 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 448 527 462 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 36 0 obj << /Dest [ 475 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 71 435 527 449 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 37 0 obj << /Dest [ 475 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 418 527 432 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 38 0 obj << /Dest [ 475 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 405 527 419 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 39 0 obj << /Dest [ 493 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 391 527 405 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 40 0 obj << /Dest [ 505 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 71 378 527 392 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 41 0 obj << /Dest [ 505 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 361 527 375 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 42 0 obj << /Dest [ 505 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 347 527 361 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 43 0 obj << /Dest [ 511 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 334 527 348 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 44 0 obj << /Dest [ 511 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 320 527 334 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 45 0 obj << /Dest [ 511 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 307 527 321 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 46 0 obj << /Dest [ 511 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 293 527 307 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 47 0 obj << /Dest [ 511 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 280 527 294 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 48 0 obj << /Dest [ 515 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 266 527 280 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 49 0 obj << /Dest [ 515 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 253 527 267 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 50 0 obj << /Dest [ 523 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 239 527 253 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 51 0 obj << /Dest [ 523 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 226 527 240 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 52 0 obj << /Dest [ 523 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 212 527 226 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 53 0 obj << /Dest [ 529 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 199 527 213 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 54 0 obj << /Dest [ 533 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 185 527 199 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 55 0 obj << /Dest [ 533 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 172 527 186 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 56 0 obj << /Dest [ 540 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 158 527 172 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 57 0 obj << /Dest [ 540 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 145 527 159 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 58 0 obj << /Dest [ 545 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 131 527 145 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 59 0 obj << /Dest [ 545 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 118 527 132 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 60 0 obj << /Dest [ 548 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 104 527 118 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 61 0 obj << /Dest [ 552 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 91 527 105 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 62 0 obj << /Dest [ 552 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 77 527 91 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 63 0 obj << /Dest [ 552 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 64 527 78 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 64 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R /TT10 1606 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 65 0 obj << /Length 2289 /Filter /FlateDecode >> stream
+H‰ÌWÛrãÆ}çWté%@•9ÂÌàúä²vµ§Ö‰+¢íT\y
+\^$Ž¶j‚€sN÷éÓw“Ñíd"€Ãd>âü‡¿ÜH²@8>‘ÇüБ0YnßmBH6ú6I6ºýøÀa±9Ìqèšd4®ŸG¿Z?ÆöX2×Z(ú-,pÁþÇäO£Hß ‚Àa‘‘!óó ý÷BߊŽ<ßÜé?ÏŽ…c¹d¾å„0ý_²òÏKÙx'k“nà»ßÀßÿk=ü\àwó¬Ôv¬äñxxNËßT±Ô7‘VœÍìqdÁ¿õÿúÅ4zL8ÌÇ“÷xN¿Áóó3³%ãÖ†žªè±xk|$ÃCÁ%Õ©G}§ûɈC
+#áùLúdÀÂ|Î\£B¡FóÑݤ!Cr¼ÄÙe£eÚ!Fð-'ÿ$½ÜJ/—9®‘LÕ|®V¯%Ô‰ôŸ¿Ë³ReåFߦ»ù3"íèà·’F†Îàû¬,òÙ6)Ó<C],‹]æuŒðîRSŠ¸¹c€›:‰‘è%9c}Ñçt¡F¡ëêWëÕ‡°r{ì[ýWbý„XE»Ÿ7¨(J*.…®‹Ów,÷Ê“Á<#˜ < ;
+—€Õ‡É9=‹÷Qúç ”-JÞ ä5JQ£D#L~ø÷Ù"Í`C’¯Öq¡fPÚã
+B8O[,ê4§Nr1¦p|.<”Å–‚'g4ÔÑê# }€r/¿4w©›ßŠýЌØÃÉ6%mÌÅÚôå•U®á©Mscª—=ŒQ…±&s~`.:"}‹~¼ ß5KŽ¢Õ†¦<<¨âIUAÀf­’tŽ-@$m71-Z‹áU#À ô-%èkê@’U5»l§˜3xŠ‹4ž.ÕpÃÜ’ÍRãgdÑ·8ú“îÕzsÛSM@Öæ7Í
+Z/³8Xá!ß ¾ê</ðÅ—[<ÌçP½cûÇápª;m¶Á<0ü´I³E‡nã&©Ž¨“XXXfòBYºö•·â•ñJ¸QŸ×Xr7—ÖÂF¿±Òé¶T:%×1š–8ŽÅw…Ãúºð#®jŽ ÂŒ:“">s®§êm>8Au–¤Æü¹Õš{Ê­ó7#î(ì5ˆ½±7éߦsB'­¬ûáÆ&}±Üiœzø]Zù ~h•E:5b1Pµswü¹´D/¿Zë=È}¸Y/ãMÊè¹ÂºÖMe®ˆWZUŽðtŽ@Å ÷f3GÖÏÃ`ĈÎ&P»Ç`’®ü–g
+piœÙ.VïÝ »Út=ÂÒB*z9~¸±ƒ/¬álÊxµ†B­qÍSº}¹UVC1 /¹ôé4¿âk+vD¡!jÁ°HKõ¹Ô9½éb¨^"Ü/ãkýH4EQvª²ÎmZ?‚ŠX/:mt¡F–ðßÖw~ÛwmÛU¾ã3xHŠt]RŒøg‹­Ž£rÀ ¤aXb¸¥²'“ÝöËc\Bg0E¡4"5ûÖ ÁË'‚Z¨á–ª=ܯ¦j6#¥4B=òlÖq± g
+ÒŒ ‚î4ÿ—¼Ü!/Á½ ãÍj¥2²èj²åE1|Å¢(¢ÆE§Ò¸õ&E)Ö†–> wË<ù%gpN Üð’¢zðý Q gªâ÷ÒÕèé1§Ö³}Wóš¬j_w˜¥‚¯•Ù‡÷qCùe­.£å9Dx^C„]"„í†.÷° ¼“Xý+  Öúí§´TE¼¼Æí00DÅZPé3M;H »…
+ïw2ñ
+¼ˆ:íîí´»îvA4ôêÜãûx5^·ZZÇõaŦ‘wF~øÙÑXbä«!Çw)ùÛεø1¤pE ¼Êù:׌)ÁH›"µÓkžÆŒh˜i‰iyð)Opk}²#Z_‰• aÅÃcÈçƒÍkkN¨b<Só4S3˜o³Ä,N<‡µ ¼%í·ÒÓ¯>zï€Aœ]>¤j9ƒ'l[?·ª
+.MYŒKî…=ìUmpþõÚ0¨—ùÑ™õÑ)N…¸ð]QÄ_r¸&
+5W…²æ[YB\!Uâò©^S#XtŒôEtè“Ã_’#X$Ï ûVî?¯ µÙTÛgxµÁÙÁé _Éâ!ùð\Áúø°ÌŸõH-ò%E†«9gûÈH•}Ô'ŽÔ#š ø!NŠ\»ÃõåÞ|¤Jùê‘zظÀ™úžAš-jJ¸wÍ„Q›àÇÌ€íúAɼÓÌòð$ANüDc2²ˆ•¦R̹ÞO=A:«R‚tqûãÃ9Í!ŽÖ*$PÈú?ݲ¤Ù²z$|mË:P{[Eb«è²1=Až416ÖŒo¥ZÍQx1Z“"™<Ò;Ý"ñúcÔ"c ?î4=[|Ph¥ËrœfÝÊáþuhi©y…­ø§ÚÊ™,wáã2ŸbJŸn+j([ÈÖ ò’àG¬"â
+þªsF2Œò‚9Ÿì±§Í³Ó?'Œ©tJx( ²ÏyGÿ«¤ËéKû<TGj`¯‘ÞOFÿ
+endstream endobj 66 0 obj << /Type /Page /Parent 1643 0 R /Resources 119 0 R /Contents 120 0 R /Annots [ 67 0 R 68 0 R 69 0 R 70 0 R 71 0 R 72 0 R 73 0 R 74 0 R 75 0 R 76 0 R 77 0 R 78 0 R 79 0 R 80 0 R 81 0 R 82 0 R 83 0 R 84 0 R 85 0 R 86 0 R 87 0 R 88 0 R 89 0 R 90 0 R 91 0 R 92 0 R 93 0 R 94 0 R 95 0 R 96 0 R 97 0 R 98 0 R 99 0 R 100 0 R 101 0 R 102 0 R 103 0 R 104 0 R 105 0 R 106 0 R 107 0 R 108 0 R 109 0 R 110 0 R 111 0 R 112 0 R 113 0 R 114 0 R 115 0 R 116 0 R 117 0 R 118 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 67 0 obj << /Dest [ 558 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 757 527 771 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 68 0 obj << /Dest [ 562 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 743 527 757 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 69 0 obj << /Dest [ 575 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 730 527 744 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 70 0 obj << /Dest [ 586 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 716 527 730 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 71 0 obj << /Dest [ 596 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 703 527 717 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 72 0 obj << /Dest [ 620 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 689 527 703 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 73 0 obj << /Dest [ 628 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 71 676 527 690 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 74 0 obj << /Dest [ 634 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 659 527 673 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 75 0 obj << /Dest [ 634 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 646 527 660 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 76 0 obj << /Dest [ 641 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 632 527 646 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 77 0 obj << /Dest [ 644 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 619 527 633 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 78 0 obj << /Dest [ 647 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 605 527 619 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 79 0 obj << /Dest [ 654 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 592 527 606 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 80 0 obj << /Dest [ 658 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 71 578 527 592 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 81 0 obj << /Dest [ 658 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 561 527 575 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 82 0 obj << /Dest [ 658 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 547 527 561 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 83 0 obj << /Dest [ 658 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 534 527 548 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 84 0 obj << /Dest [ 663 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 520 527 534 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 85 0 obj << /Dest [ 663 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 507 527 521 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 86 0 obj << /Dest [ 663 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 493 527 507 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 87 0 obj << /Dest [ 663 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 480 527 494 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 88 0 obj << /Dest [ 669 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 466 527 480 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 89 0 obj << /Dest [ 669 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 453 527 467 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 90 0 obj << /Dest [ 669 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 439 527 453 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 91 0 obj << /Dest [ 669 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 426 527 440 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 92 0 obj << /Dest [ 678 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 412 527 426 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 93 0 obj << /Dest [ 678 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 399 527 413 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 94 0 obj << /Dest [ 678 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 385 527 399 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 95 0 obj << /Dest [ 685 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 372 527 386 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 96 0 obj << /Dest [ 685 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 358 527 372 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 97 0 obj << /Dest [ 693 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 345 527 359 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 98 0 obj << /Dest [ 693 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 331 527 345 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 99 0 obj << /Dest [ 696 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 318 527 332 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 100 0 obj << /Dest [ 696 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 304 527 318 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 101 0 obj << /Dest [ 696 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 291 527 305 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 102 0 obj << /Dest [ 700 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 277 527 291 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 103 0 obj << /Dest [ 700 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 264 527 278 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 104 0 obj << /Dest [ 700 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 250 527 264 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 105 0 obj << /Dest [ 705 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 237 527 251 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 106 0 obj << /Dest [ 705 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 223 527 237 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 107 0 obj << /Dest [ 709 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 210 527 224 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 108 0 obj << /Dest [ 709 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 196 527 210 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 109 0 obj << /Dest [ 714 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 183 527 197 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 110 0 obj << /Dest [ 714 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 169 527 183 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 111 0 obj << /Dest [ 720 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 156 527 170 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 112 0 obj << /Dest [ 720 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 142 527 156 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 113 0 obj << /Dest [ 720 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 129 527 143 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 114 0 obj << /Dest [ 720 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 115 527 129 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 115 0 obj << /Dest [ 726 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 102 527 116 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 116 0 obj << /Dest [ 726 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 88 527 102 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 117 0 obj << /Dest [ 732 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 71 75 527 89 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 118 0 obj << /Dest [ 732 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 58 527 72 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 119 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R /TT10 1606 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 120 0 obj << /Length 3132 /Filter /FlateDecode >> stream
+H‰ÌW]oÛÈ}÷¯˜Ç!1?Ó @ïºY$ÝE­E‹>ÐäHæ®D
+$µ¶óCúúûÐsg†eÉrKi7X‹?ιçÞ{îûéÅëéÔg‚MgÂgþá'H¥û^Äâ4t£Ä“lº¼xý¡MXÞê=kóêâõÕµ`óöÂs=öä“þðîâü—Ì™H7àsE¿>g!sþ9ýé"ÕHYì¹i„—ÈÄ<ó}¿¯EGadžôo«ßs&Bº÷vóÀ®ªî+>WžÄÛ²e¼zÅþþgâ¼ÁµÐ yéx<¿}Å®ïÊî‹jú!’gUáLRÎþ¥ÿêÓè52ßs#œL/±¦¿àîîÎu¤+xKoUôZ<¯tqè\n—nõ“~˜^V² ?Œ\ &c7IX$Ü `À$¬Q³‹÷Ó )°ÅÛfcôGŒà+§¿Ž¢”†¿8ò\üonkB ¡ CúüÈ볿88
+g3Σô»¤Ñ6ÂMÓCé´ÿ1+ÒMÄ>V’ž•h,•É¦?ŽÛ#X±B™—Õe¾)WIÃÇã¿ -è“/vðÑ÷oði~A26 e<€y€Ð€Œ‘úÜEf?ÒQ¹èý6º:
+wüØ·6! ]È·M\v¥*Õ  W‹ú?Ú~¨ òÚàd?¯zo†r…qK@`¸cŸ¾¡½l49‹2A{y»(sUµªÊ–êÝ+}ê$­åh1ïÞ°Of*Nâ/©Ä¥`c´o•äN¾A]ìí2»Ïë*_7 êl«Ú–b@›EÖ¯²E¹,;ú²øÈ8õx’ã‘C%5že;oË/•eæ¤Ç=•Ìüó'¶’lnFMÚòÿ¢ÊukHõͯ¤ûr¹^2¬¨±Á*ÜúapªJa+}¸ë¾¿µÒ‡¬°‡²·¹®eÑv |8 ÙÆžðûŽu5+uç§éóÙ3ÛqzD]?ÕO¾ä(vÑ
+šÅ¨‚“(RÞeˇ<VÝŸ—åÌhC¶XX¥ü¶¾c£Û*;ØbNYYG ' ŽN8í¥‘<9Uñx";0§™Œ
+/%àÙò[uÁœ˜¿¥.Pu¬È eß™}[•ýESA¬’QþñB8-ÏFA#ž‘1_7˜Sºs=P꧆ÉmÂŒv›Cûc×OOG¸sȳ6<ÞT[*»õÊšoÿ:¤Lz*Åìµ
+³•uª*ºZÝ—f€ °‡Ä€“ô
+ ¡ìÆ>†ÚYÿ®š»¦ì3†ãõ7Ø»©ÂÚIÄÜjNÌ_pE³¥‘‘ð¾¶ÚŽ­¥ÿRbž­%4HR«]¬mV,ëBÙ!k»í“í6ØøÔ v;cN% /Ú´ÒhŒ^˜”‘|}cÊ%ÁG u)-g,ƒ
+t¡¨™ÙG'”J p3Ì«uÇfM½dZÊöYÐ>Ð8Ôèi’4âˆG„8âÞ‰âí%ÄÉ1œ'EpÕ`Ž$1Kí;­¡–A¨„|† ¡S÷Ë:v£}ykVš#-3b†žÌQð\°¦Aã^Š[>‹–²¬
+2tÕœ]C.Ê·PÉi‰àÞ(¶6ëªÐ6
+·ãFºz³¨óß(©Å³(ŸG.N•áû§,¸Åûå¢k²ÊT¥ªRÖ™b6Ô;sI ½Éöœúßß>—Ƒž?N3^í°àü—² Ÿ3Í®qÙjL=q.ÖpŽoX±vtþ-Wìú¡Êa’{
+ +f„90œÄ0ã/¹fW ¢Þ<ÕÝÍ‚µoØÜôöJ5ЉíûÊ\Ü÷
+>“¼do+­| gJEf+ìÏ´æz?x2—( c(Á=µjFÊ!&€LÊüÒâïIú/ëÕ²Ó0 ï|…顈<7qA•à
+ý#ºÆb14žGµç’=eCIôty‚˜R‘³5PÜ¡ó†æGIÅ÷Vüfø±®îoéb%žHÉÞïøk*hå×Î ‰Ò̶à׳†‰ç<¨ìKñûœxS¼Èþ1
+¢ÑÞš½7%øÔ¨TyNb´áeMX¯lt¯1ñ|)µB¡Ò˜è
+£‘,i:8)Ú]jUò¸ã§%ûØ‹û˜þMöIzõ#À
+endstream endobj 121 0 obj << /Type /Page /Parent 1643 0 R /Resources 171 0 R /Contents 172 0 R /Annots [ 122 0 R 123 0 R 124 0 R 125 0 R 126 0 R 127 0 R 128 0 R 129 0 R 130 0 R 131 0 R 132 0 R 133 0 R 134 0 R 135 0 R 136 0 R 137 0 R 138 0 R 139 0 R 140 0 R 141 0 R 142 0 R 143 0 R 144 0 R 145 0 R 146 0 R 147 0 R 148 0 R 149 0 R 150 0 R 151 0 R 152 0 R 153 0 R 154 0 R 155 0 R 156 0 R 157 0 R 158 0 R 159 0 R 160 0 R 161 0 R 162 0 R 163 0 R 164 0 R 165 0 R 166 0 R 167 0 R 168 0 R 169 0 R 170 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 122 0 obj << /Dest [ 735 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 757 527 771 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 123 0 obj << /Dest [ 735 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 743 527 757 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 124 0 obj << /Dest [ 735 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 730 527 744 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 125 0 obj << /Dest [ 738 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 716 527 730 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 126 0 obj << /Dest [ 738 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 703 527 717 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 127 0 obj << /Dest [ 738 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 689 527 703 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 128 0 obj << /Dest [ 741 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 676 527 690 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 129 0 obj << /Dest [ 741 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 662 527 676 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 130 0 obj << /Dest [ 744 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 71 649 527 663 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 131 0 obj << /Dest [ 748 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 632 527 646 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 132 0 obj << /Dest [ 748 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 619 527 633 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 133 0 obj << /Dest [ 761 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 605 527 619 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 134 0 obj << /Dest [ 764 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 592 527 606 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 135 0 obj << /Dest [ 764 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 578 527 592 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 136 0 obj << /Dest [ 773 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 565 527 579 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 137 0 obj << /Dest [ 779 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 551 527 565 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 138 0 obj << /Dest [ 796 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 538 527 552 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 139 0 obj << /Dest [ 801 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 524 527 538 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 140 0 obj << /Dest [ 807 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 511 527 525 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 141 0 obj << /Dest [ 819 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 497 527 511 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 142 0 obj << /Dest [ 824 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 484 527 498 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 143 0 obj << /Dest [ 824 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 470 527 484 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 144 0 obj << /Dest [ 824 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 457 527 471 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 145 0 obj << /Dest [ 829 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 443 527 457 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 146 0 obj << /Dest [ 829 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 430 527 444 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 147 0 obj << /Dest [ 829 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 416 527 430 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 148 0 obj << /Dest [ 834 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 403 527 417 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 149 0 obj << /Dest [ 834 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 389 527 403 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 150 0 obj << /Dest [ 839 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 376 527 390 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 151 0 obj << /Dest [ 846 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 362 527 376 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 152 0 obj << /Dest [ 846 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 349 527 363 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 153 0 obj << /Dest [ 846 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 322 527 336 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 154 0 obj << /Dest [ 850 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 295 527 309 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 155 0 obj << /Dest [ 850 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 281 527 295 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 156 0 obj << /Dest [ 850 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 268 527 282 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 157 0 obj << /Dest [ 854 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 254 527 268 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 158 0 obj << /Dest [ 854 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 227 527 241 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 159 0 obj << /Dest [ 863 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 214 527 228 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 160 0 obj << /Dest [ 871 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 200 527 214 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 161 0 obj << /Dest [ 875 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 187 527 201 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 162 0 obj << /Dest [ 883 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 173 527 187 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 163 0 obj << /Dest [ 888 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 160 527 174 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 164 0 obj << /Dest [ 888 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 146 527 160 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 165 0 obj << /Dest [ 895 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 71 133 527 147 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 166 0 obj << /Dest [ 895 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 115 527 129 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 167 0 obj << /Dest [ 907 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 102 527 116 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 168 0 obj << /Dest [ 907 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 88 527 102 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 169 0 obj << /Dest [ 907 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 75 527 89 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 170 0 obj << /Dest [ 912 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 61 527 75 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 171 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R /TT10 1606 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 172 0 obj << /Length 3282 /Filter /FlateDecode >> stream
+H‰´WÛŽÛÈ}×Wôc°hÞ/†``=v6â¬akEvó@Q­)P”gf?$ÿÌCNU7/3Ò\¤áx±Ã[³ÅsêTÕ©óÉÛùÜ®˜¯&®'ü‡Cúvì9‘ˆÓÐŽÇóÍäíÅ.ùŽ×8b——“·?wÅånâØŽCkòÉ´=½žü.¿fÖÔ·y©èèI ë_ó¿MRÞ ±c§~ÄOìÈÑ¿Áï{¼…‘Þé¿îN=Çšº¾I'‹[ñý¶l®pÇ“ÊÂNrWìÄO?¿ÿüŸ5 q]ãYh‡²°™_½߯‹æOU¯y_fåÒš¦Rü‡ÿò‡1zFæ9v„‹ùGÜã/¸¾¾¶-ßvåŽ~UÑÏbkü¤SàrsëŠwú4Ÿ¸¢/Œl?r…ÛI""׌A"j5YM>Ì;2|Kœ»lôL;ľrþoŠWr?^‰§™Œ#ÇÆÿ=•NØRIg$µ=1[ìW+Uת©o#×Åw—»kU¿'ô#±ÎvÈø¦XU KjJ#;‘ߎeSjgMã@Ú§ÿCŒˆ¶(b¾
+èhVF´Àèl¤ö¨!ÚäŨ½GQÇbV-Ô ªÈ ÿòÁŠ¡ O¿ sO”ÙÅÅóQ(Gƒ8¶AD»þÔv:ì/f›ì¦¹ªU¶¤h~Én BÒŸàêéË2f‡RjÒR.½‘Äb‹…­‚ë*ç@û>>ûX¡#£ä5–2(óÅõ¾$`òŽÀá®!JôÀ€;*¦ %°P§'ztÖ£ñüStÛí4|A‹/Öøü¸Ãç%}ˆ5@—|B"}°-Ê{1[êËLІ§·[}®ô`Iò—Ý2só{‹õ*s±*J}RðÏ¢Zë˲ßØ;§ñ€2½Cz@\|Š0zâtcöUøWÌV…Z/©hQê&+–Y“ ~Âõ Å*>¡ö>f*cg4å¤3:à–
+Á*¸Ÿ²¬«Œ‘†LÝ5äž›J ç7¹à‘´= *wØ—BT®¨ƒuPa’6ÅF!QWÅZiÀÔ>Öf$ v.c—1jI‚Þá–È鲨57[ãy+¼à>VÁž„îËØ; º×BìaÚ§ÚC'!÷Àëªj„¹„P&çy)ô;p ,|¢ëuØ~—62öïSú–¨x˜¿Û·#Áp
+~O‚? ‡‚s#ÙûÔÛéBÕçAByÀ w–;º ?@ü<AX¤3q4-0¥p¨\R¸MnX¨ù¾d1ôJ¸S-ã±ò„cî·EàÐÒœù‡zæ“mEb†%$À›WeSW„Ø“k¡Ÿ‘кwÖ šGkµU _’‡s‚³ƒ=!ì¾<´
+û‰Ò(Ê+êýhK.xW$:nòÃÚ
+³†?uýssü"¸ ;ù^í [–ÀÑQŠðÙ§6¯gÇ°ƒ"‘} ×ëêZm¶Í­EÃÝ{¢Æ—h”,Oâ¯PÁÑ%F¶ À!'¶Í<€–€Uë¢Jh£ÏI)1pBgúùRgý•"ùôøÓÑ\Ð+Qðdð#m S¶†Í-›
+x @mßÇŠsGCç & sëÿ4À˜*ãc¦Ýuýýð£jËE}”w ®"™|ùD…Rñˆp"§ãèÔŠ0* þãV9¤þÿg±Ý¯l¼ï¢(I±(u5C’ç2[¬ÕÛe¡]¯ñÑ‹5·e†¨-7J•Ùš_Ê͵ÙÖJ¯¸ç±ÿeUNõ¼1œÃ2RÝòK0zçÉj]4jc ;ÕŸkLa>;t\›‘ÇüÜýµƒväÐOôwÅ7äøê0äÕý@ZdU¹V?ÔÚ„úâáe‚×qIcÙ¨»Ry$\†ŠþR©ÿRny’sÆMFl§ükãÒ†làÇÓÃx¹Ï³OgX2PšÕmeÊꦛÖ~Z¯-® Ok×b_ª2¯–j)zÜ tbF²¾E)r‹‡Ö²Q%åELÖåežõnq:àaÔÑ-„ÿšíË‚0û’qj¥…’jôBqÅ%ZT‘•ïy†{Çñ¿–­7Eøù!(â
+ÃÂ{¹w¦ð ©èó8éó8P‘Ð[°B™×Ŷ >óÄJ°\”@T]në…îÌz)ôàËÖ¥Q³šêû[TéU‘ ³!©&–´­`¾nš³”€‡)œ‘”áÄ=ñ€Ž”è@1aÙËKBZÞpÔgÕ¾¹¬Ð¨‡Þ‰‹½¾¦6ƒª³‰SSîßÛÊ<­Ë•>×™O4OS‰©—Üí°ÐùG u$]à^sDz:*ôðk©ûHe볞SÖóA
+ƒh÷¤é„=›|Vp¨J÷•UÖI-4ÙjR×h'¼'ÛeëkÙ³¼Ds¶¦¸¬…ÉK¡Å8‚çö×!ôÆ*ó}•y„‹:9ºm¦-BÜÞÒ, ¸ÅÒP3´˜Á‰œß*ô#2n˜à칡‚ _¦þXCÙq0ŒlT}i%4–iÐGV„Ù[4&£öÀë®÷Ú«e§aÞùŠ]‰"’8I#!$—öRàPjÔˆ<ŠPø{fm')
+ ôÁ¥qíDòìÎîÌ6ì?½Öñ 5<ÆOÕçwØWóIžæbI&
+sÒ¿˜Í§ã;¢¼ËÌÿÅhÌÉ@ÈôHP$’Àk2~tL¶µÎgÁ^1˜41·©a±+ƒÐY,žfSt7‘Áçeeò
+JPŸ"›¿ .YÜ
+çF¹òçõY$yP
+Öëú.¾mᶠ#ivç3Çn;ª†ï÷À­õˆ~KYé›ÿTÆ8ˆ©G÷! egˆ~¼¥·¨’xó°“µëOŠ4B*]¼BÂqУYéQLuñ—†úôô¬7ajÅÖGÊ|¬lb '9ïÉqðº
+endstream endobj 173 0 obj << /Type /Page /Parent 1643 0 R /Resources 217 0 R /Contents 218 0 R /Annots [ 174 0 R 175 0 R 176 0 R 177 0 R 178 0 R 179 0 R 180 0 R 181 0 R 182 0 R 183 0 R 184 0 R 185 0 R 186 0 R 187 0 R 188 0 R 189 0 R 190 0 R 191 0 R 192 0 R 193 0 R 194 0 R 195 0 R 196 0 R 197 0 R 198 0 R 199 0 R 200 0 R 201 0 R 202 0 R 203 0 R 204 0 R 205 0 R 206 0 R 207 0 R 208 0 R 209 0 R 210 0 R 211 0 R 212 0 R 213 0 R 214 0 R 215 0 R 216 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 174 0 obj << /Dest [ 912 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 757 527 771 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 175 0 obj << /Dest [ 917 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 743 527 757 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 176 0 obj << /Dest [ 917 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 730 527 744 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 177 0 obj << /Dest [ 917 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 716 527 730 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 178 0 obj << /Dest [ 921 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 703 527 717 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 179 0 obj << /Dest [ 921 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 689 527 703 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 180 0 obj << /Dest [ 921 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 662 527 676 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 181 0 obj << /Dest [ 927 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 635 527 649 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 182 0 obj << /Dest [ 930 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 608 527 622 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 183 0 obj << /Dest [ 930 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 581 527 595 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 184 0 obj << /Dest [ 930 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 568 527 582 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 185 0 obj << /Dest [ 935 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 554 527 568 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 186 0 obj << /Dest [ 935 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 541 527 555 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 187 0 obj << /Dest [ 935 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 527 527 541 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 188 0 obj << /Dest [ 938 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 514 527 528 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 189 0 obj << /Dest [ 938 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 500 527 514 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 190 0 obj << /Dest [ 938 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 473 527 487 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 191 0 obj << /Dest [ 945 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 460 527 474 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 192 0 obj << /Dest [ 945 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 433 527 447 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 193 0 obj << /Dest [ 945 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 419 527 433 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 194 0 obj << /Dest [ 950 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 406 527 420 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 195 0 obj << /Dest [ 950 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 392 527 406 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 196 0 obj << /Dest [ 950 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 379 527 393 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 197 0 obj << /Dest [ 967 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 365 527 379 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 198 0 obj << /Dest [ 967 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 352 527 366 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 199 0 obj << /Dest [ 986 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 338 527 352 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 200 0 obj << /Dest [ 992 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 325 527 339 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 201 0 obj << /Dest [ 992 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 311 527 325 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 202 0 obj << /Dest [ 992 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 298 527 312 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 203 0 obj << /Dest [ 992 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 284 527 298 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 204 0 obj << /Dest [ 997 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 271 527 285 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 205 0 obj << /Dest [ 997 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 257 527 271 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 206 0 obj << /Dest [ 997 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 230 527 244 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 207 0 obj << /Dest [ 1002 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 203 527 217 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 208 0 obj << /Dest [ 1002 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 176 527 190 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 209 0 obj << /Dest [ 1006 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 163 527 177 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 210 0 obj << /Dest [ 1006 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 149 527 163 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 211 0 obj << /Dest [ 1013 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 136 527 150 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 212 0 obj << /Dest [ 1013 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 122 527 136 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 213 0 obj << /Dest [ 1013 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 109 527 123 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 214 0 obj << /Dest [ 1013 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 95 527 109 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 215 0 obj << /Dest [ 1025 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 82 527 96 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 216 0 obj << /Dest [ 1025 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 68 527 82 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 217 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 2027 0 R /TT8 1605 0 R /TT10 1606 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 218 0 obj << /Length 3299 /Filter /FlateDecode >> stream
+H‰¼WËnÜÈÝ÷Wf3EÀM³ŠoAà±g‚ĈµÊ Š]Ýâ ›llµäÉ?ä³È¹uùÒÓŽº=$’UÅb{îãÜŸ.o//µPâr½PZxøÅ%H}7Ö^$â4t£ÄóÅåvñö}›ˆ¼µk<ÑæÕâíÇÏJlÚ…çz­ÉËáö°øM~Êœ¥ïrc誥ˆ…ó¯Ë¿-R»A*bÏM#|ÄOÜÈãoØ÷µÝŠîˆwúÆèR{ÎRùn$½D\݉ÏwUw-ƒd[´âÝÇ7âŸÿu–!žÌ…n( Ç“ùõñùPt_LSÚM|™U+g™JñoûßÌ¢·È´çFx¸ü€1{‚Ãáà:¾«dK_5ôYlOº¸õ
+Èγ}WWu•›‹3ñë‡ÐI@“°bc*Ód]QW‚ Ém½ÜP›ûúìàÉ4µÆå1?r“ÑÎ9û!Ì¥]´T.€b!ãR#.5âŠÅy^Wm—U݀̀a¬Ç×vMQ9˲q–:„¹f±©(u©<ï8|þ„Oøôˆ/çÜI¶N±,¶»Ò€Èë}kš‹7ÌÖy;ï0@[Tx|
+´ä´D3-̪¦ÿ ez<Ê‹Oyg*ηû²+ºëÆd+@xW–õAر¥³Ä.’§ÌJ˜[Cq—–{òXºO¤¯#1öNJáS.ŠMË’iAeÀTFG/ª¢kó¦Øu䛑¼à™3ñÙ´-#-Ã
+?>Úk-fí³ãªPÊ
+KœñÄûq ‡
+n½¥`¢8Jå
+#J–Y9‘Õ†¸´4ð ¿Ðá°QòRÌ2¾¼hÒžÿ§éSL‚G{\Ðâû3òRÌÛ@.;þ¦œÌ”ŽJù3JýáBîC¢#œš0CQOhB„"äÓ?îmä”'8æ¹iï½9r›Ø ™M,³Ìrñ“%ÓÙ·=« ±=”r0¢ðí?L ùƒ`Ü’
+=!tÅŠÜfW—E¿ñ)ÑGZßš‰kF[<âŠÇ HqñÅåjÕ?%T«Ò>
+Wæj¿É¯÷ÕÛìÖV¨˜“'$Ôj×lºƒcÓK±l6æïW¿›ŽOï —ùbDYl¡ƒ×–b,i;«JTbŸç¹Îäñ™NÇt8Z$œ˜NÌ¥¡n@wý0þuH ¶_C~°+L)$;fuin NìÐC"Ó¦÷Û6Æ«OÊúSÁª=q¾§FFss‰Ù[óP­ììl¯ùc ˆëŒXg(¹5‚Q^T¯UûCÀ†'£ñ‘"ˆŠi̮ꦫ«¬,‹¬3ÛvBÏ—6 !-ÕÍPÕµ­ê‰äÀúiÇœŒüä9OýÈV$ zÆk2‚NfÑœ Í¡Ôýøòº>ä]žívM½3V¡"¦Ÿ½àË¿&†wX÷Ðð™r5Œîw»zØÀ¡€‡ŸtYQöÓèx1pSäæ™RΤOQ¡Zl™•pÈùAr\ö|õk¤²¨çÇÕ¶õ°ÜÝ—@HñÁ\"}½7å`\>ŒFÝÖRJõá` ª»ÛÊšEe)S\µÅ8§Rrlª™´ŠŒÝ—Råzd÷Ÿ±—/«5²ŠŸR.-Hª^tªTcª`&ï‚áb»3%u ¬ K)ëhøMµßö†œ’N#àHzgM¶myí_Îxað’i2$Þ«ùXrŽÂ¶4ý¦éîÄÙU脬í-²ÝýÞs})gÞdåÞ´"kGv®²­é½–{ðýÅÜ8OAÛ$t–”ŽÅyæP²Ïs³ëPn`n(j–mïìhV!¸QyVôÓ¢…#Z¯„ERRº/aFÇ£™±¦GÖô„&çe½Y#ÝÓáŠzÎ57xÄ,´È-¤IAía¢^:ökyÖXãïªQul±š*»*{®žÉõ Û¬Žpyœ6<R¥>F™òkÚ%aF©jt r"rª%$º–ÚO•C
+S›U•~>|ÂGfëáTÕÜPЫ¢¤XîỂá6w>”VÄêÍ"õ»âÆXu@ÙHdAY8d“Å’·"“…d2X‰î¬^UsÑ[C{Ö¡µÆä3sܳÔjC°#ÉU]•w0Á0ºZ:ÊÇ @œXÄ#V¡fUHMÑ)âž5Ô)}COÖÐsk@ÒÚˆÐÒ”$0Éãd‰ˆ€[KðS"EÐR†­!Ÿšbex%LËÉB1-pïdæ±69¥<gèÇ®¹Ûïz¿´+2Ðý6Üh¼&ºZØWøa2´å6»¡¶@…9Î+­3XÂ?‘ox餣ӹ%"Š”]ÝtŒˆ8Ž(W’®É*Ø¢ §@&æ«ÈÙÖ1S0XJÑs¼ÀÐG#ŸúÙpBΑC‰B‡QO{ ¶zÚ±»3ÍÖð3ÿϸñÝPÍÜf·Åv¿hó®ÐZ­ÐhPÓ6=ØÐöÎZ8}¿¢y|$úV$é¯/Zò?O‘ný±gÖ¨Ý)[ý·çTJ_â¹(RaßÂU<qϹ‚ž¶EHåhK¹Â%–ž ÿox¨Zý÷ªéQ¢÷þ
+ßKm¥,$UÅi«ªç¶§r Ál#¼ñ
+…­úïûžÇ_K ",'Œ'(óæÍÌ{ÖÕ•m4•ÁÁ­ò×_u†ë{5K¾üßgZAšUÖ*‡êU]ãZyA­ë”_/ón§˜Õb!³r¦‰ÏÈy©bñ¡LÿUEÙW2;®dQ^VÉ“›¹ô•œÐà’æ®]Ú¦îÂöâæá¿ŸÊ=„¶ÜY÷[¾ ¡ö¢§>ü]øÛ¥qP­CH× ±b Ð ÞdI[UògR‡Â`“`‡É—%vC*¨]‘Þ5ê„Wªä!6Õá 9§’ôCUùÉïÊIA-Ðlž¸J(5%Õ[97~Èúp³ð’œ;3•y•Ì Du5J]4 p Nn
+«‚»SN[Y]ôNi²8¯2Ö…/(ø¨Ï6°dË#pðfù…üxÙ‘¤´$b#Ò»ec;³–ÓT|ÉGeÚàÒæÖpOÊ©–kìÌûoŠÆFž…½‰ÞxLn-µKÔÚ˜ÐOA6“ï%øÎ;z*6‰i}î‡"Tùǘ¸HKv.‰”Vz'‰ÂòJ$þ/±ÓÌä\¸?NêÚŸÌS'EþòË/MDL½Âê­Ý#²„ˆ
+€<î …ámÆ`l8ÑpÖíѺ²‹yŸ>e£”´9’'s9¨e?GsÏG¿/pXC®ŒÉ—tÂ_‡˜˜[d†è”3ÖœGã›>Kž…Ѭ†MAÒœSa€Ï1D3žÍÝVÆü‡‚a .‘üæsƒn„¤ÇÜXI³Ü"áeí5ª·+I\õš‰#ZLÃhÀv‰ÍrŸ¿¿ù+À
+endstream endobj 219 0 obj << /Type /Page /Parent 1643 0 R /Resources 265 0 R /Contents 266 0 R /Annots [ 220 0 R 221 0 R 222 0 R 223 0 R 224 0 R 225 0 R 226 0 R 227 0 R 228 0 R 229 0 R 230 0 R 231 0 R 232 0 R 233 0 R 234 0 R 235 0 R 236 0 R 237 0 R 238 0 R 239 0 R 240 0 R 241 0 R 242 0 R 243 0 R 244 0 R 245 0 R 246 0 R 247 0 R 248 0 R 249 0 R 250 0 R 251 0 R 252 0 R 253 0 R 254 0 R 255 0 R 256 0 R 257 0 R 258 0 R 259 0 R 260 0 R 261 0 R 262 0 R 263 0 R 264 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 220 0 obj << /Dest [ 1025 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 757 527 771 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 221 0 obj << /Dest [ 1035 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 743 527 757 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 222 0 obj << /Dest [ 1078 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 730 527 744 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 223 0 obj << /Dest [ 1078 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 703 527 717 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 224 0 obj << /Dest [ 1078 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 689 527 703 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 225 0 obj << /Dest [ 1084 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 676 527 690 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 226 0 obj << /Dest [ 1084 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 662 527 676 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 227 0 obj << /Dest [ 1084 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 649 527 663 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 228 0 obj << /Dest [ 1094 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 622 527 636 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 229 0 obj << /Dest [ 1101 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 608 527 622 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 230 0 obj << /Dest [ 1101 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 595 527 609 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 231 0 obj << /Dest [ 1101 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 581 527 595 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 232 0 obj << /Dest [ 1110 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 568 527 582 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 233 0 obj << /Dest [ 1110 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 554 527 568 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 234 0 obj << /Dest [ 1116 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 541 527 555 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 235 0 obj << /Dest [ 1116 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 527 527 541 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 236 0 obj << /Dest [ 1116 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 514 527 528 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 237 0 obj << /Dest [ 1120 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 500 527 514 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 238 0 obj << /Dest [ 1120 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 473 527 487 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 239 0 obj << /Dest [ 1120 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 460 527 474 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 240 0 obj << /Dest [ 1125 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 433 527 447 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 241 0 obj << /Dest [ 1125 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 419 527 433 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 242 0 obj << /Dest [ 1125 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 107 406 527 420 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 243 0 obj << /Dest [ 1155 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 107 392 527 406 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 244 0 obj << /Dest [ 1165 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 107 379 527 393 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 245 0 obj << /Dest [ 1177 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 107 365 527 379 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 246 0 obj << /Dest [ 1184 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 107 352 527 366 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 247 0 obj << /Dest [ 1184 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 107 338 527 352 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 248 0 obj << /Dest [ 1193 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 325 527 339 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 249 0 obj << /Dest [ 1200 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 311 527 325 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 250 0 obj << /Dest [ 1200 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 298 527 312 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 251 0 obj << /Dest [ 1200 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 284 527 298 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 252 0 obj << /Dest [ 1200 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 271 527 285 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 253 0 obj << /Dest [ 1205 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 257 527 271 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 254 0 obj << /Dest [ 1205 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 244 527 258 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 255 0 obj << /Dest [ 1210 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 230 527 244 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 256 0 obj << /Dest [ 1210 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 217 527 231 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 257 0 obj << /Dest [ 1210 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 203 527 217 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 258 0 obj << /Dest [ 1215 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 190 527 204 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 259 0 obj << /Dest [ 1215 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 176 527 190 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 260 0 obj << /Dest [ 1215 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 163 527 177 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 261 0 obj << /Dest [ 1220 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 136 527 150 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 262 0 obj << /Dest [ 1220 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 109 527 123 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 263 0 obj << /Dest [ 1220 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 95 527 109 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 264 0 obj << /Dest [ 1224 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 68 527 82 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 265 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 2027 0 R /TT8 1605 0 R /TT10 1606 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 266 0 obj << /Length 3439 /Filter /FlateDecode >> stream
+H‰ÄWËŽÛÊÝë+w“&`Ñ$›¤ÈÁ@@nnp“¬xV±³à­‰(ÊòøCòùÇ,rª«ùÐHã‘Fº‰ kÈ~©ëÔ©ªS??L>><‹‰ÿð'L•; ¼XÌÒÈO‰‡õä㶉ȷf'¶y5ùøë'_<m'žëy´&ŸL»Çýä³ükæL•Ê'M)áüýá/“ÔŠ™ç¦1~D%nìño˜ý9Šž¢˜Oúw€Ñià9S_¹±ôñø,>=Wí#ÔN’Ûr+~ÿëñ·ÿ8Óï æ"7’¥ãÉ|ùA|Ú—íwݬÌ!JfUáLS)þe¾ÍÅŒõƲÀsc¼<ü‚1sƒý~ï:Êõå–~UÓÏâhü¤‹GÆåvhiNúãÃÄ¥˜QìªØjæ&‰ˆ}7 l ÑèÉbòóC†ò±Ä;Dc@Ú#DpˇŒ¼”†Œß,ö\üç-@Í\á[Wø¾«|×OÄýÚÁ5aý?õ&Û:±¤ÿ '„eåªÕÍüN¬1'ÊÊÁžXæõº¬žDÙê5­ݦià‡Ò}ÿh¥)œäû3Xè{LC¹I}Î`ÓªNÍ¢)L
+ÌÂZŠû‚¬Õ­¹~[7°8o¦\WVeKÃ9¿òXƒ ›€¼:'òlµÒ…xÔŒ`ídg‹Ì™Œ¹îÀæÍÀ´ÀmÍ}ˆN ­>µä:ÔTZ8 ŽQ <‹ZhQƒ}Œ[ʶù†6±'”UÉK–€`&·9/ä¹DLç¼óp¶ƒ1ãÁB3}
+ø Î+D½° Ÿya… !=¼.TD¬¿šÍêm$”asÌlŽåJ7í‘ÑóS@t±Ý¾´ÝÀ’ÑAΔâÄ%ò¾—Büæ:Y´iôæ(ž㨠5•ç5ÏWö…7T„yöÙÎæ‚ç“_.F]LϘø
+šž,$Á¨t¤gïÿWJÞSHÔ
+É+~ŸÁïcÔEV®(÷‘«ª'¸‘^=íZ1¨nãüJk­­GÇ ¡Óꊈ~yç
+Ëèâßk«-ôüÎHBaGµÉgÌ=´—d /¬¦D‡—WÑ› Þ…®bwÀá†,­w-\N¤ˆ$üί¬pAünC#,Vœßiñ…vž²šhÝJ$$C-LìöÛ=8 FåKË
+®¬Gú°|ÁðÚÉ‘?‰‘·hê„<› j±Î6pµ523
+n³!ÆîÔĘU¿³¸ÚÚkž)—÷¥×Ä}yŽBu6<½œ=¶ú,‡G¢íÕz•º>·mƒá$Qøú°E
+È: ¸¬ÙzÅ’õfžk™Ýýh‹×ÁRx4/‚â$á%@œHþ~—ü{  _˪l)ÿçM¹¡à5 ¬4•<FD•µß!¾r÷V…RŠŠÍÿ NTfWdžض8×Õ>”êí'uSÂäFg7 9·#šMmے馩sÍ/ü-(÷ÚȵœµÚ‹ÎfFg h_Tp+¼–Ü~Ô¨\zßÀåÜÄ|ƒ,Ikz£7Y£-2å¬å¼°†9•ÎΪ}à+u#/Ýšï¿48¢¼WÒÜEemYWüx@^óÍ…zµAS´û’z®´ï¹üwÓ¼oÇTxó`÷Ž‚=fo/àV^R .9ôM½e
+Ð]Ü]n¢=¾6ÚGá~I‡E<&9×WûÔaœçÄ&‡¡w"í¢2eKòöçõú ëŽ-ks4)5bÍË‚¤„¶«$þ8Ó0¹œ¤¬‘åUtdlü¾ölå#Öƒ­0wÿ™TF½d›•–ó°öϦD¶ÇT¾ÚQéKdWÆrýãµÔ)ÄhZ4D&xôm" @*¾Vc!à¥|º°¸²y2:dþtŒÈP˜dKô©ùrQ®P
+à&­âÀô#Sßã§k¾¢(7‰YIÝ+üò«®²µ6b~~µ1çYl \dÞ# Ž?jw –ý º¢r‹ûU¹.M¯¹R˜¶l¥«';²ìë!QôXl!wu3ɳ¶æ-ë¦íúº„³~^+~F%ï¸í{+^iviàªÖë±]Ég¶ êx[Ú®fΣ
+endstream endobj 267 0 obj << /Type /Page /Parent 1643 0 R /Resources 314 0 R /Contents 315 0 R /Annots [ 268 0 R 269 0 R 270 0 R 271 0 R 272 0 R 273 0 R 274 0 R 275 0 R 276 0 R 277 0 R 278 0 R 279 0 R 280 0 R 281 0 R 282 0 R 283 0 R 284 0 R 285 0 R 286 0 R 287 0 R 288 0 R 289 0 R 290 0 R 291 0 R 292 0 R 293 0 R 294 0 R 295 0 R 296 0 R 297 0 R 298 0 R 299 0 R 300 0 R 301 0 R 302 0 R 303 0 R 304 0 R 305 0 R 306 0 R 307 0 R 308 0 R 309 0 R 310 0 R 311 0 R 312 0 R 313 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 268 0 obj << /Dest [ 1224 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 757 527 771 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 269 0 obj << /Dest [ 1224 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 743 527 757 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 270 0 obj << /Dest [ 1229 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 730 527 744 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 271 0 obj << /Dest [ 1229 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 716 527 730 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 272 0 obj << /Dest [ 1229 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 703 527 717 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 273 0 obj << /Dest [ 1229 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 689 527 703 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 274 0 obj << /Dest [ 1235 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 676 527 690 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 275 0 obj << /Dest [ 1235 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 662 527 676 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 276 0 obj << /Dest [ 1235 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 649 527 663 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 277 0 obj << /Dest [ 1241 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 71 619 527 633 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 278 0 obj << /Dest [ 1241 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 601 527 615 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 279 0 obj << /Dest [ 1249 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 588 527 602 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 280 0 obj << /Dest [ 1249 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 574 527 588 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 281 0 obj << /Dest [ 1269 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 561 527 575 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 282 0 obj << /Dest [ 1276 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 547 527 561 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 283 0 obj << /Dest [ 1286 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 534 527 548 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 284 0 obj << /Dest [ 1293 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 520 527 534 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 285 0 obj << /Dest [ 1293 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 507 527 521 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 286 0 obj << /Dest [ 1298 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 493 527 507 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 287 0 obj << /Dest [ 1298 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 480 527 494 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 288 0 obj << /Dest [ 1298 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 466 527 480 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 289 0 obj << /Dest [ 1298 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 439 527 453 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 290 0 obj << /Dest [ 1303 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 426 527 440 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 291 0 obj << /Dest [ 1303 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 412 527 426 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 292 0 obj << /Dest [ 1303 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 399 527 413 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 293 0 obj << /Dest [ 1311 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 385 527 399 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 294 0 obj << /Dest [ 1311 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 372 527 386 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 295 0 obj << /Dest [ 1311 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 358 527 372 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 296 0 obj << /Dest [ 1317 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 331 527 345 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 297 0 obj << /Dest [ 1325 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 318 527 332 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 298 0 obj << /Dest [ 1331 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 304 527 318 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 299 0 obj << /Dest [ 1339 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 291 527 305 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 300 0 obj << /Dest [ 1339 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 277 527 291 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 301 0 obj << /Dest [ 1346 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 250 527 264 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 302 0 obj << /Dest [ 1346 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 237 527 251 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 303 0 obj << /Dest [ 1357 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 210 527 224 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 304 0 obj << /Dest [ 1392 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 196 527 210 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 305 0 obj << /Dest [ 1392 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 183 527 197 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 306 0 obj << /Dest [ 1392 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 156 527 170 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 307 0 obj << /Dest [ 1404 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 142 527 156 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 308 0 obj << /Dest [ 1412 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 129 527 143 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 309 0 obj << /Dest [ 1412 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 115 527 129 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 310 0 obj << /Dest [ 1412 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 102 527 116 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 311 0 obj << /Dest [ 1418 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 88 527 102 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 312 0 obj << /Dest [ 1418 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 75 527 89 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 313 0 obj << /Dest [ 1418 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 61 527 75 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 314 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R /TT10 1606 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 315 0 obj << /Length 3360 /Filter /FlateDecode >> stream
+H‰´WÛnã8}÷Wy¢€¶"R÷†'Àvg0À¢3ƒx_¶±ŠLÇš‘%G’ãx?dÿaÿq¶.”å\:‰c'݉%²Hóœ*Vú2O§Z(1”üƒ õÝX{‘ˆÓÐÏÓåèük›ˆ¼%O´y5:ÿåJ‰›v乞‡6ùhÜ?nFßåï™3öÝ@ÞüÔR¤Âù×ô6HEì¹i_â'näñwÐzM[áSñNÿÕ0:Öž3V¾I/×[qµ­ºŒhiØI¶E+þöË'ñÏÿ9ãÞ˜ ÝPŽ'óÅ'qµ)º›¦¤M|™U3gœJñúK#ô„L{n/ÓK£l6×ñ]%[üVƒ_ [ÃWºðè¸Ü-h§Ÿ§#%
+1Òaäú‘~ì&‰ˆ”0‰hÌh>ú2Ý‘á+0ñ²10í!#pÊéŸ{^Jæ/Ž<~y òŸ®HùøJ¹>œÞ“yÝ䦬¡Ckà3+»biœ|uñYdå†3œLå¶u ¤àÃ+ª™À%¢¨`¿eÖu%2kXÖ¹Ý<†Æ@‘„»/ÿ 5š©@…—ÊãÀôC7Ùy#g—0CðŽÉh¬\  ¼R;ðq‡Àò°î…'Ì6.!””Üà‹ÏІž0ôàYè2³Ö{ø\@ðõkðŸ0¡ŽgÂß11\Nå?`"“®q‚(³`DrÀ€ƒˆ\Ë>4
+HX—†‹_Þ«þ$œÈo* LiÕ‰®æÞäëŠè\àb‘­V”C•,øÃ[È&J—ù8:,¨ªÆIÐs3'~´ã$ÞqöwD»Ìc)&­ièzßÙOÑÑÇÖÁ¹2?:
+5z×M|.T
+4´ƒm×Ö^%ozÔ;ă™@;¬o½nÄë-øbTËG""ÒáC<ü*k[>*ä ¬Æ†ï¯ÐfS73gÆo9é[}Kz?Fón¯Dbb[YËyÖQâH@àú¨U÷zÝÖt”ŒÀ’W9
+‚È:
+6 lQ_ƒ
+œø¯Q`õ´£¯ävôv*вr
+§˜@ì"}o“úªãÒãÑí¹0Ú¡‹öIĉ¸NÄ”Ìp ’óÎ4Ö±-OälÉxÁ㟹>ˆ+êvú^f&6 CÞ†’¹2qùeÿ‚vã==¾ïð»¬W¦23ÛËž˜í·» %1Ž­°öRÔ|—ì´Êæ3NÓ.€œöî+—ŠI×dU›ËzyJZ(!4§Ã[—>±Ÿ„µ ÑôpÔcàES'»hÏkØt²n£%_7mÝ”Å5ÀZ·ÙõœÓ<ϘÂL“R¦‰eÖlñ"¾³‚>Ÿªö±ýWº/Eù~Ò™ûn™­.> zÌÖÝ¢^eðüYPbÁ´µ AÁ6cQÕ¢¬«›Þ㈚™ì.+ÊìºÄ:—Jëägê<ƒL?Þ ºòpB•ÃÓ ˆnÚËA™ŠÆ×]µ0
+^4Ø‘!¸_~~±äí%ýKƒˬ‚4ˆ}ÛéÔáÛ¼FËÿØÄÙÔïsÊ€äö—Ù¶·%ÄåíÚ4[1‡œO­LÖgÆ…ù¥êŠË&? ¾çÝÌ ’+øPA©@Q–õMQå “ÿÕ’dœù®ÜRÐŒ )l]ð¾± `Ó ÍSx2;LÁ€ ´#
+â¶Ë–+öæ’Áw¶jÅ’»
+à FÔšâ_Læu93;Œ:17Ë-À/úBöÆIX(âdG¥\°Íšl®ùÅ”¶GvS>.q±ÙÓ§ šz9º¶Oh!Ø?
+ë'Üè—Åp¢ë‹–Û
+.¥BM
+rþó(F­–%|ªÏŸöqÅÕÛlì’^K<2[¯€Q³g³·ƒý᜶Îã× »ÌLÉÂÅÐ^’“ÍÖÁÊ_å‚X8)PHîÔ’õAŸÆ?ì#•rINä#/Ät²ï#)ÁÈB±ü?ëÕ²›6E÷ýŠYŽ©°ñXd!u‘lªJ];f–üªe·Íß÷œ¹C 2‚Ù01÷q×¥[–¨+ˆ~®Wl¿HSUÊ´³â2(= ƒJ†Yê}š 3õgP?yÿÄ|ôq“†ÚǾõS Š?Ôþ0|˜qÖ\§ŒBYÖ›mnŠ ™‘K_âÏJaŒwÎÀ•;n&r
+Á>eÚ¤î[áñó¬„_^¹K-¶-‰vsb÷Z\Š
+§'ñØÁßÕs©HŽRñÚË—w²î™‰…Eäÿä\›–DÒâOIˆ±é¿3eÃöÿêkú/p’„mœ÷ßÕhð,#?‰SŽË="©µ¦ÍòuÙs~‹ôᶌ¯F9ÈÒ„gÙ~çrÉ´ù’¶¥»‹Ü%Á‰!i ñ‰):#:#8L F„}Þ̧3*”¹3-ƒ>ñ#º/%Kœ…0îýü¶’BýyZèµÊ<Žzd”
+endstream endobj 316 0 obj << /Type /Page /Parent 1643 0 R /Resources 358 0 R /Contents 359 0 R /Annots [ 317 0 R 318 0 R 319 0 R 320 0 R 321 0 R 322 0 R 323 0 R 324 0 R 325 0 R 326 0 R 327 0 R 328 0 R 329 0 R 330 0 R 331 0 R 332 0 R 333 0 R 334 0 R 335 0 R 336 0 R 337 0 R 338 0 R 339 0 R 340 0 R 341 0 R 342 0 R 343 0 R 344 0 R 345 0 R 346 0 R 347 0 R 348 0 R 349 0 R 350 0 R 351 0 R 352 0 R 353 0 R 354 0 R 355 0 R 356 0 R 357 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 317 0 obj << /Dest [ 1418 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 743 527 757 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 318 0 obj << /Dest [ 1422 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 730 527 744 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 319 0 obj << /Dest [ 1422 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 716 527 730 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 320 0 obj << /Dest [ 1431 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 689 527 703 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 321 0 obj << /Dest [ 1431 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 676 527 690 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 322 0 obj << /Dest [ 1441 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 635 527 649 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 323 0 obj << /Dest [ 1451 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 622 527 636 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 324 0 obj << /Dest [ 1459 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 608 527 622 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 325 0 obj << /Dest [ 1485 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 581 527 595 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 326 0 obj << /Dest [ 1485 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 568 527 582 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 327 0 obj << /Dest [ 1492 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 541 527 555 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 328 0 obj << /Dest [ 1492 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 527 527 541 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 329 0 obj << /Dest [ 1492 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 514 527 528 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 330 0 obj << /Dest [ 1498 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 71 484 527 498 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 331 0 obj << /Dest [ 1502 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 71 450 527 464 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 332 0 obj << /Dest [ 1502 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 433 527 447 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 333 0 obj << /Dest [ 1502 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 419 527 433 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 334 0 obj << /Dest [ 1508 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 406 527 420 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 335 0 obj << /Dest [ 1508 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 392 527 406 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 336 0 obj << /Dest [ 1508 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 379 527 393 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 337 0 obj << /Dest [ 1508 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 365 527 379 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 338 0 obj << /Dest [ 1508 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 352 527 366 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 339 0 obj << /Dest [ 1513 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 338 527 352 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 340 0 obj << /Dest [ 1513 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 325 527 339 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 341 0 obj << /Dest [ 1513 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 311 527 325 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 342 0 obj << /Dest [ 1513 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 298 552 312 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 343 0 obj << /Dest [ 1520 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 284 527 298 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 344 0 obj << /Dest [ 1520 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 271 527 285 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 345 0 obj << /Dest [ 1520 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 257 527 271 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 346 0 obj << /Dest [ 1525 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 244 527 258 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 347 0 obj << /Dest [ 1525 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 230 527 244 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 348 0 obj << /Dest [ 1529 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 71 200 527 214 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 349 0 obj << /Dest [ 1529 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 169 527 183 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 350 0 obj << /Dest [ 1538 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 71 156 527 170 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 351 0 obj << /Dest [ 1538 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 139 527 153 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 352 0 obj << /Dest [ 1538 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 126 527 140 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 353 0 obj << /Dest [ 1538 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 112 527 126 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 354 0 obj << /Dest [ 1545 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 99 527 113 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 355 0 obj << /Dest [ 1545 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 85 527 99 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 356 0 obj << /Dest [ 1545 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 72 527 86 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 357 0 obj << /Dest [ 1545 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 58 527 72 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 358 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R /TT10 1606 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 359 0 obj << /Length 3133 /Filter /FlateDecode >> stream
+H‰ÔWÛnÛÊ}×W ÎÓ°ˆÎðn¨šË9ÈiŠ¦µžMQ2ŠÔ¡è8:¿Ñ¢ÿÐìCמ^dÊŽe+n ›ÃáæpöZk_æÕ|òr>—L°ùr"$sð‹‹E¶9 cß"ÇeóõäåëmÄÒ­²qØ6-'/ºlµ8¶ãM:™¶Ã›ÉGþ!±¦®íñUFWÉ™p˜õ÷ùÏ“X­³Ð±ã
+›#è°¥-ˆí‡cú°Ò§íÿÉÚ©‹ý MòG~Èh*â{A ì)ÂÑ¡âÛaÜÉ£%¦¤©ëÛ‘ÂÁíp'4pâ±añ¬ ÏÒ&_
+ÂGâc<öþqŒºÛ–h‚ˆÍªË&±BTÛ)!Q"…-›¬&]ûºdPƯÒ"™æeQ6(ÄE¾ÐFè>ïD¿€œë5jQ¾¨Jm›Ñ³^­÷‘3Ûeö¹´]Okt“¥9}RÓƒ$KkÎô>¡àÚò°ÏF_²æZÌSµÖLo<ì6Sñ'Õk[ôXxCTØ *F1ö‘W—Ÿ¡#Jäh‘ÖI™¬²u6Àóý´áø‡Â©¦xLÂ?U0ÅhQ,#WA3¼©Ð
+¶ õVGÛuV5Bl\óô^ª A ¨äSÅó”µ9Û®Ojb2ônÓØÞ±1ٳNjd³²Ê&û|M€’$ ¤è!äÚ¡BÛ7hkCl7ôàa¯I”òקîLqw¦ …§iMQ¬H…ª£îKÕÂ(àûVÃc„éªQ¨|¿ßê*DµU¢ïÐÕuk\õ¶‘/a<­o8ÉO«alï^ DìE„±üæ9Kz*‹-;žÛéÓ’ªà¨Ó™ž-Á@ª†…¥ÎdI©5®hzrÐ
+ãô…Ĭ6 ÛC³×$•ÿ'áØžÃÆð<2;A_†‚=ŒpÓ PÇ+yãjhP“r+ª¯Ù ?'ºu5¡Í‚K¨lKÅ“Jæ_p´^ë_¨ýsƒNqçŸïÿ\Å–Ùiˆ²ÓseºìTiØd¿ÿÁšFðCÏ¢}T×Ååˆs½@Z¨A®þS£¦WÙÑ»ã·Îô[s‹ÊIû`¼ž ]«½ÊxDË8ÉÒ·ŠLØ+º
+Ò0ÝnÍôÂ<½0÷õ—v Ò £SŽé×z¡"7–¥¾bãä kêªÖ«†úŒø àW#¥€êéõ ½žmù¹‘!7ÒäFŠÜ¨%ä”"ǬUŽÿ%Qëkb‘fñ§IZR7{¶gdûtÊášÑùïîdòqÄæÌš¶#æ†6ÕÞ³%è ƒ 9š85³áQÌF-³ÉLaŽ«ÌÍÈP+ئ
+%Ž}(®©R®^]ŽÄûFYB4PÙo7Yš/ót˜wc*1÷ø ˜ïpé¸$ú Êå ‰ÁÊá‚¥+¥Ôn$ˆŸ/t<çE É÷l7¯ ÑN·È¨®ç9B’èo­²Äß«²ÈAâüm&‡ÇCóM½xwÔ”ËëÕ²HVV[y !ï™zP½=€Íëm''êÛøh€+–b4œ
+ž¢œ"tNyщ+©-¼èjyÅ\‰Ë‡­6mÔ¥Žøv>ìœßÛµ4v´ÅPgy׋MëÜ1˜iÖ¬
+ù…®´Õ¸ÐnR~Z•Ë|u]'M^•¬IVÛ8uc•ÏåHºá=ÒjØ9!ºR×QJîSÅö(7¨[6³¦RÕ%
+g^.•®ÕúGž4Wçú(Ü°eU³MV“Ü\ðÌÚ±:`U*H NÍg²ÊMpÄJZäëÌœc̉‹¦XR.X²@9åè% kò0õÓ™’>}÷´¬Ga›%ÊñiŸ¦:éã¢<D²¼£0>ÛÆ\`SÀ¨8å¾®Ök¯È
+zv
+­ïØXÀØNßNKÐaˆ º÷ªðat::²Û*²‹l™\ÍvW¦ëâ i)ø/ñU¬Û ÷~…G3d€
+endstream endobj 360 0 obj << /Type /Page /Parent 1645 0 R /Resources 383 0 R /Contents 384 0 R /Annots [ 361 0 R 362 0 R 363 0 R 364 0 R 365 0 R 366 0 R 367 0 R 368 0 R 369 0 R 370 0 R 371 0 R 372 0 R 373 0 R 374 0 R 375 0 R 376 0 R 377 0 R 378 0 R 379 0 R 380 0 R 381 0 R 382 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 361 0 obj << /Dest [ 1549 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 757 527 771 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 362 0 obj << /Dest [ 1549 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 743 527 757 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 363 0 obj << /Dest [ 1549 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 730 527 744 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 364 0 obj << /Dest [ 1549 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 716 527 730 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 365 0 obj << /Dest [ 1552 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 703 527 717 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 366 0 obj << /Dest [ 1552 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 689 527 703 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 367 0 obj << /Dest [ 1552 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 676 527 690 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 368 0 obj << /Dest [ 1552 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 649 527 663 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 369 0 obj << /Dest [ 1555 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 635 527 649 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 370 0 obj << /Dest [ 1555 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 622 527 636 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 371 0 obj << /Dest [ 1555 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 608 527 622 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 372 0 obj << /Dest [ 1561 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 595 527 609 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 373 0 obj << /Dest [ 1561 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 581 527 595 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 374 0 obj << /Dest [ 1561 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 568 527 582 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 375 0 obj << /Dest [ 1568 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 95 554 527 568 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 376 0 obj << /Dest [ 1573 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 71 541 527 555 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 377 0 obj << /Dest [ 1576 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 71 524 527 538 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 378 0 obj << /Dest [ 1576 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 507 527 521 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 379 0 obj << /Dest [ 1582 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 493 527 507 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 380 0 obj << /Dest [ 1589 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 71 480 527 494 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 381 0 obj << /Dest [ 1589 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 463 527 477 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 382 0 obj << /Dest [ 1598 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 83 450 527 464 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 383 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R /TT10 1606 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 384 0 obj << /Length 1576 /Filter /FlateDecode >> stream
+H‰´—ÛNãFÇïós×±„½3ã3BH{jÕŠJ«%•ª®za‡¸ 6µ Ùé;ô{Ñÿ7ãÓBHgAàñœ<¿ï<oæ³Wó¹b’Í—3©˜À/^9" cß "á²ùÍìÕÛ:bi­çV§ÅìÕ—’]×3áAsÒ™Ý5·³OüCbÙ®ãñ댞Š3)™õûü§Y¬wˆY(œ8ÀWÜÈ „ùˆÞ@齨åf«zm%,[ºNÀEÄ®vìrW4+ô(žY؉×yÍ^ÿpÂ~ûײ}¼WóŸç–àéê„]nóæKV­õ&.OŠ…eÇœý­ÿëƒi|¦„àeþ}úÛíÖ±\Gòš¾šÑg±5>é )@—¶]+½ÓûùL²œÍ”8n ™:QÄéx£±*›-goæ½0\‰)âki ¢$œrþ),º¯°HI†pð7ˆRÈN”Ô"iÙ0Bp"v¶.Ód½¸ÚÔYu~B¯–$q¦f Ý&u½-«Åù)» å/µÄ¯ó‚5%Ó°EÒ$WIM¢ ôÖhVŒÚãDæc¾¥0ˆn€vzH2î‹ÁÖ“°‚ÀD{0DZ5ÊÀ‰ÙYQ¶´ë‡χ*ûœMËðî Ó8Œp¬Ã ÿK
+.£‰LîÀ¤z&Õ3aÓ3 6Ækr²ïŠôfi¹È‹kËŽÐunFNÉaÒŸ/X7Ê–eu“4ü*ÄøË¡Äx:¤VÜ`¯öW H)ø,ôsVmª5÷1»)›¬c2#ì—°ÏÀCˆ˜¦8PGRÝ 9û¾ê”V]hTçr×z!ü cß+µÃYvˆÙðRŠ©Õ1@{ù ˜°àê2 ®fð ƒÏËôÏzUÖ !ÜV-Å
+Éïvº—ü­¼ÛµçÄšMÖUxéIÎõ0P‚Î#
+¸ÓŠA
+Ûðw·)Ç€%’âÃSˆ¢#)I³£e£V›|df$E¾Ò¾úÐ%¢qÌØ–äURÔæý¶¬šMÛ† Lã„B¶ä‡Ö@<æ¥}lé{1'™ŽSFÙ““ÓbqiQjì²JoÙ:±nµ³»”½7HÌÎT€m'XÙ´}N×uâ¸UÓ§c~â#•«}*WN$G*RêÛkÉöSÏøi@i¢ÊþÚdÚ'?Zÿ)qc‚î$ù£ÐIY¢ÓzÝ”†ŸÖH¡·ëðYv­:Rÿ~TRŘӑì ‹%uñÒ¬Àw‹+MÏÒ¢ÔWmù†œ*7²•ïL¡ÖÍÆ¢1ÍSË!#U­‰r®Ð( ÿ9Âè§Iã+q ÉH¤+ƒrAR<;×Õ
+ž)îG:OÖä¦+»k²Âè?§
+D8@‡cè …V: ó"]oH³Ï?gZš>×.£à6¹Ç0…’s¨]äžp½D<²“lj©ŸEìuÄ¡!váΉÃZäÐa9Z¹dWœ‰®D’Û(1tåÑä7Ù—²Èjºñɉ7>0¹¹r, …Ï@òd¤FA­»9D{_U°@ øœá2GÇWrZ%}€)†Òe,ŸÅô @ (Љñ1]£\P¯ºl”ì­ Üi׺§ *W—({X_Xz ·gu{VÅ~,àâªV¤&$4÷(å¹øÌ1Qe(
+endstream endobj 385 0 obj << /Type /Page /Parent 1645 0 R /Resources 394 0 R /Contents 395 0 R /Annots [ 386 0 R 387 0 R 388 0 R 389 0 R 390 0 R 391 0 R 392 0 R 393 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 386 0 obj << /A << /S /GoToR /D [ 0 /XYZ null null null ] /F 396 0 R >> /Type /Annot /Subtype /Link /Rect [ 197 625 331 639 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 387 0 obj << /Dest [ 412 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 145 652 170 666 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 388 0 obj << /Dest [ 412 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 178 652 335 666 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 389 0 obj << /Dest [ 475 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 238 555 246 569 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 390 0 obj << /Dest [ 475 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 458 218 466 232 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 391 0 obj << /Dest [ 412 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 380 97 406 111 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 392 0 obj << /Dest [ 412 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 413 97 534 111 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 393 0 obj << /Dest [ 412 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 89 83 137 97 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 394 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 1607 0 R /F4 1608 0 R /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 395 0 obj << /Length 3358 /Filter /FlateDecode >> stream
+H‰¤WÛŽÛÈE^õ ¿¤ H4ï¤òìú›¬k³@ìÀh‘­w(’!©‘g?$ÿÌCNU7I]Æk Æ
+<gå‡n"½LlÅûÇzØc$Ú%Ù—½øîÍRüã¿Î*Æ{‡o±ËÒñd¾_Š÷§røUw ¥ª gµ–âßüŸÆ×ç«ž›àeóc|‚Óéä:¡ëËžvÕ´-LcKn—Û¡=[zµYø¢‹ NÜ0ñE˜ºY&ß";F™èôb·ø~39#ô1Å»ôÆìj<‚Sn~¡€E6`‘ëE&fü4ú3B7 Î
+Ç[‡úæ6+~FPbD$ç)ÇOp‹ÁIÜTþ¾Õ§?§Œ»DnféÕVÉ:ÅëÙVÝÊKƽ|'ƒãȤ(k1짃â±ú¨*³Ëó×7(œöHÎ=BÖÉ°ü;ãµõ|j6yi4hŽO¡$Ì8õ-N7N/øÀUÙãQä{Õºs"7:päA×….„rR
+9MQâ_Ç2¿̓÷PârC#è«Æ¼Ljáøž<íÕ ŒÙz¾>½šu
+=¨²êq->JLJáJðB&K³¤­(«|<}tø³èÝödeP÷š~m’Ò5‚ùëéksc‹ƒZ¨ZèÏðöŠŽ?”53•w8=ü_Ö㎤ª*5”MíŽ~ჳ­'*Ág06b–x›ã\AÊü?±ÏØŒÍU¾« Æbƒ°¢¨à!D CŠÀÊâtÉ^g6ë¥Ó¹?È÷Î>xÐ Í?õ±@Þ•íð)‡>íªæä¶Ånâ±u¾òE`¢ð¤u@–L6§l¤nD™ŸÄ{7,öT¡Ï€‡ºθi¡z]í–b{„ê‘r½nÕŠü·ò×.˜hB3Û
+f³¹*ƒóÝË×¢hòãA×v-Ú®)Žù€4ÉïÕƒ,üèˆ~ßœÊúNœöe¾ÇiÉ!=Nöîo“Lþè4‚ÑSyñ%°"šºzÄZÕélrý9 6MÉÅàà ºž„Kd“kŠG&ÍÑì ¨Næõ‘âÉ:·ïÚ¼Ÿ¯(™;šz ðbd¶EÛ m‰ˆ£50m ¬Éܘ4§1ÆÚÁþH$ÉÑÞôËy•ÈÕ™¦°øsX|–‘Mi9ïL›¥æ2¡Ü9Ñøx1^ç†Pse† Çgª+0W‚–Ò‘‰«7@ %­2{åçvÍLÄØvŽTày€ùFåysh+NºÀ~Ø(‘kc´V™¦²k )óc?4‡²g¢«õ÷î5‘¸ñL#ß\ì¼™P¼‘P885¸Î9,ŸVð
+°Þk¥Ûf’k ˆ Ý[ræP´øµ©uÏ%xí©$ìu¯9öªÀ.1•1YŒÈ~*/ e¯fÎf*úËB×we­ÝñR¶ôÿœU>—ãg .B¨ä× ª9V…!\”+K"[.ó¸Ø’(%o:,èä¦
+è%…è‚—N›ÆÓ¦¬á½7ºÖ¼ Ò»ªÑ:ÐCÿUB‡ÆÁ·hÄø,mnk 9äï ~ÊÛúxØêŽjRS? ¶Ð‡r D5'¸c×5ñCY?‹{ÝÕºšW}tþp­íh«àJ“À léø±(È
+a3”[ªC—‰©(Η¸mÈ8M ý «†réÀUñÁÜ«_"[P/êQl)¡H”ŽWáì'%F¿H-Ôœ
+¡µ‘ë/“¸þ™ÄšL®v¨÷ä
+KòÁüG%‘Ìö+e{lÕ?Õ:ñ`È×~0³Ì7Š?¸WÉ
+h.1Öœåw¢vaªfhVÖ(ËÔ_úÒ
+JJÝvÚÎÂ-njH§QFù|)´¼ŠàKa§\ËÕ¡½¦É œÐô䊷ð:¢áP.ZQb” ‡Û_¶už
+£¿ÃÆ/©0šó)²xºoDÅÞœ´â°QŽû8|€8¼xPQBÄt‡KŠ“2S„ªNÈÂõ˜[vØØ´Îhí›ùjÖÍSãO}ºõS]>ØÓ˜©8ùJŒIÀéEÓ˜ EYC=†OJ»[wªª¡¾
+ð34©n<‘m»^S_V -ˆ¸K±ð¸A™ôgPøÛz Y±µKô’¹·y²¹¦5Éa㜸P›¾åšI©BëQàê)\
+Ú$"Æþs(w%týÀ’8`¨<ð¹ÀjT¾×³Ž.‡k)
+épÞd\‹yJ jî•Ó0ÂrIælƒ•Œ-gÏTU-Å}ÆÌ/†%–ÔÆ9ë±}ƒQ ¿jLûw²¶pVn?4}³©hŽyIÞ·ÂNõ-”A?Š|àŒ±D°›Âé‘'ÔFAèz¶«Æé3æ>áóæ•Ô‚"Ê~ uu‚œö]s¼Ûsõ¥–è¶D‘ª$Á¦ µZXs­Úª‰™™’€ÔFy¡[h5rC§ z»÷ðXHJ
+¢]²ˆ‡î+s´Væ‹6éÃ- f{èõ¸k94,W×9dŽ6 $o¤2e—²ê?úrjûÁŽV›ÓŒšÎîC2LÕû«ÒÌ„î}WMoÚ@½÷W¬8 P -G‹nU$Zª8á*‚±±„mdã
+¥|SOΪo²ä¯ä©L‡|o¥>œ¾Üô0
+—F¾¶µêØòdSW‚³“9ÿ)çËiO¼>Š›¯€µ@kÜl ºMùe‰¦Œoþ£î³üÄ x‡Å¯¸T
+endstream endobj 396 0 obj << /Type /Filespec /F (SySync_script_call_flow.pdf) >> endobj 397 0 obj << /Type /Page /Parent 1645 0 R /Resources 410 0 R /Contents 411 0 R /Annots [ 398 0 R 399 0 R 400 0 R 401 0 R 402 0 R 403 0 R 404 0 R 405 0 R 406 0 R 407 0 R 408 0 R 409 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 398 0 obj << /Dest [ 475 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 350 610 358 624 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 399 0 obj << /Dest [ 575 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 201 542 231 556 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 400 0 obj << /Dest [ 938 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 331 517 358 531 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 401 0 obj << /Dest [ 575 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 89 501 120 515 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 402 0 obj << /Dest [ 1101 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 391 462 432 476 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 403 0 obj << /Dest [ 1125 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 244 379 293 393 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 404 0 obj << /Dest [ 779 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 191 352 221 366 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 405 0 obj << /Dest [ 764 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 356 339 386 353 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 406 0 obj << /Dest [ 1184 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 307 284 356 298 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 407 0 obj << /Dest [ 558 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 89 230 120 244 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 408 0 obj << /Dest [ 552 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 456 108 478 122 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 409 0 obj << /Dest [ 779 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 344 67 374 81 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 410 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 1607 0 R /F4 1608 0 R /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 411 0 obj << /Length 3708 /Filter /FlateDecode >> stream
+H‰ÄWÛrÛÈ­¼ò+¦ô’A–„p#n9®òJ–£,;&ìÊÊÊE¬A€… úCöòyÈéžHQò&©¤’òÖŠ˜KO÷éîÓÝ?ăó8ö„+âåÀõ„ƒøD‘íDÎD„Ó±=‰_ÄëÁùE‰´æ3Ž¨Óbpþf抇zàØŽCgÒÁ¨û¹|–ïkäÛ|PôדÂõ…õ—øÇÁ”%LEèØÓ ^ñ#{âèGX€Ç²è×x¢EýÍÃêÈs¬‘ëÛéDb¾³}Ѭ°âIeA’¬³Z¼z3w·Fc|WØÛc™YŽLWC1ÛeÍWUå,Ä—I±°FS)~áÿ³bl>›æ9öñ%ÖXƒÝng[¾íÊš^Uô,DãI?X—š¥Kz\‘‰7žØþÄ~hG‘˜¸vØD¢Rƒåà‡¸ÃwqÄyŒÆj‡–ñÏä°À8,°
+™HÞÀ“R¨â!+”(ÊH¶I–'ó\‰¤ÖGDžÍ«¤Úk­Á<±Ã©ñ ÉžÂÓÄ”øEÌš¤j2v` ‹
+Ù¸¤x€}®l“€ lîåÅ9¡ëé ñÝwç—*߬2¡Ë%PRûÞ"(qx§*äbý ¹?²M _ ·‰(+^v¡û$¡¡f08éM ¿ý‡LNÐL¿ÈÒ×mT±P QS„†²Ýl ÑˆÜk(•È—fsÏÄÈõ¼ÊBç£\+ñµ,
+ÎØ#^B
+ ¢lDYä:OŽ“Ü (Z'²±ƒÜ
+ïîô5Þã­G˜üoÁ”¡üDdáÊ“ëgf\@²žH:õÇgù±Ó•Å²ÃÁ•½2ú]³ÚÁ?¹hQ
+?{Û;Añzvù*6çÞÝÞüt,ùÛ;ÏÖ¹ð@0¡ÆþÚ"iïiüyå–Ñ#2¡ŸqzÞKdO`”´ßª á©2}œây¯*Ñ2ñƒg*Cö0øtÙÞQ÷ ‘Áº¶¦¤|ÑjŠPšÏ4—h>‡ì—°ÜœÑ„KDìPQãý¼õQûžQ…?èÅk†î-‹|}‡|b\¥q9ˆlß=qÖÓjÐa/²“Ø”uQ¿Ó”(îµö>w=‘|(¨eRˤEÁLHZ“)‚Z1Œ€´tm ,sµU9ˆ¥ÿÖ¨¹MR4š¯žëQž™ÙÒ<S¸²PÛ iG±FÁK°R
+*05—òF|Œ/† Üj¨0ÝœmÀ±T+QoTš-QÕPK
+}Ç©ZàXfï¤üZ¢g4jÄE¦§¡B >Eù³ü¤ÖZ¿åØæŠÍoku*^]#¥=ððJ¨^RÈ'zº€a/´NíÜ야ƒ:M°þþlKʳr_‰n&HTufQþ’”õ¥8F¾œJGw
+0ŸõÉÑlË”-r[Í—'„‹«Á¿K¸Þó„õ„NF'š/¿3Óôƒæ¼§¤ÆQÕ5¢\StÖ4ÈøìQàÜ$YAI^©<!ï&º)È3""……–*’_8ž8P?*ÄÎô[SCâsÕìÝ E×£ödM˜O°±×.GDl³i@èf¤ ÚIùÿ´3ú¢kF›ªDæ6ÔtpžM7‘’šH~ÖtýLS˜ìVP‘68§kÕÔ"AòÅB¾—»‚hq®ˆ!þ¥ ùÌaÉ=…a ‹±Ö­ÑöauhE–^ÐXÄ&Ó÷Øúgª¯e›æ¥‰æHv”h‘ìÐ^u½icAô}…Õ§KQ0ØG'8ÄZlP}ÛíJ•ª(‰DYÔ‚¶ééïÝsfÆÆ$R¥íÜ™¹×÷ÎÇ™3=<á%ðËÔí·&ä3Œ¯Î@TZí[Dšõ­öø`ù}+±Ñ°=ÎB @Ü`ƒ§0jضTT¸U0™ßµ•%î¨Rxœt-ƒÕ¸·[ª^-ÁÁ›½Š3‘„ °Šæz´îWývW´”ðNC¢7óüó~3—œÞè@›²e^¸qúþãuVŒÚOÈóÄ_éˆwÓ–Š~“R\Î&™Â^F­„ÇÐ=VÎ&‰”¶b×&ÔÒÕÄå_«Û¤5¦¬PQŒóÀ£™½ßd±1Û+h¸~¨nÝný wUú*r_¶kv#xð!V6Ë‹M3ô¥ÃNºÜÑBuÛŠÏ„UýÆRy†a°TXÆu©Ì¿¢¢ƒõíf%­¸[ d+å`‚ÿ×ѳÛDõ¸ž¥â
+tö´ m!XóÿÇî¦Eš•µÈ~c7iu8­¤ЩFLác»åPÜc¬ï‹Ÿ¥“Qs±¸Š
+endstream endobj 412 0 obj << /Type /Page /Parent 1645 0 R /Resources 426 0 R /Contents 427 0 R /Annots [ 413 0 R 414 0 R 415 0 R 416 0 R 417 0 R 418 0 R 419 0 R 420 0 R 421 0 R 422 0 R 423 0 R 424 0 R 425 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 413 0 obj << /Dest [ 562 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 93 724 123 738 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 414 0 obj << /Dest [ 658 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 182 689 198 703 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 415 0 obj << /Dest [ 663 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 218 689 235 703 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 416 0 obj << /Dest [ 714 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 389 581 424 595 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 417 0 obj << /Dest [ 709 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 93 568 129 582 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 418 0 obj << /Dest [ 586 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 93 554 123 568 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 419 0 obj << /Dest [ 685 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 481 499 511 513 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 420 0 obj << /Dest [ 586 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 449 472 478 486 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 421 0 obj << /Dest [ 455 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 198 378 214 392 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 422 0 obj << /Dest [ 471 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 397 378 413 392 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 423 0 obj << /Dest [ 465 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 437 351 453 365 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 424 0 obj << /Dest [ 471 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 127 283 142 297 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 425 0 obj << /Dest [ 779 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 140 92 170 106 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 426 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 1607 0 R /F4 1608 0 R /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 427 0 obj << /Length 3344 /Filter /FlateDecode >> stream
+H‰´WínÛÊEÿê)F. ‰á7© (àؾémíä"VÚ´qQPÔJb¤,;’wè;öGÏÌ.EÉVÚ¢hÄ"—»³;3眙}=½˜Í<áŠÙräzÂÁ?üIb;‰‰xÚQâøbVŽ^\´‰ÈZžãˆ6«F/ÞܺbÕŽÛqhN6šô»Ñ'ùKjM|;+E¿žn ¬¿Íþ0š²…©ˆ{a?±#GoÂ<¶EOa¤MýÃÃèÄs¬‰ëÛ‘t1·U·Æˆ'•K²Í[qþf,þúOkâ½Á·Ðen92[Åí.ᆰ¦`#¾L«…5™JñÿòÁØ}vÍsì/³KŒñ v»mù¶+[ÚUѶ0-m<:ð.3Ck¶t5¹"#/Œl?r…ÛI""׃D4j´½žíƒá»˜âGcµCÁ)gŸÒ„,%e)rlüç%Ÿd§²®}±R•j81F„y*¬©,& óö(Ô½RU]ûRœ___žÿ…>%òš˜ñÎráÚ‡·³;yg¹*oÎÿx¥' bIú]*¤Ã— ÑÕâô1ú &è׆7k¤ÜMö@¢GŠ½zØ Y¢±B¬RÙV?è¿H.NR­Èä!b_¬‰x)Þ_]| A_¾¡D^½½°bäçw¿_žÏxê•vï?οxg¾¿Õëô2½R‡îø½;NÌX&G0=²ÝÀöï,›3ùâ'Ã<¬¦bâÙ^d ‡dÿJO ô`Æ
+²’ƒ*LíÐÛ3!ëé€LFX|”ÈÃ#M]½F BØ󙞈zþ mþU‰"/sdL»döDÌÝ—iqYZ°{£DUïžDÂï9;–éÈguµÌWÛ&êx+`èp«pØ*4žn­ q¤e‡s íU™>”úueF¿ªßu&^lñ€O’¦Ãa3 %¦‹;c‘pV%¶/ÒÊr]lµ‰0H8¬ÏQ§ã€ŠÎK²Ï‹aÚ¥šoW¢¨W«¼"·#¹Âc)TµN«ŒeO.
+Op^L“!’÷“þ‘Œ㔵 m ×õN?ˆ./•~ì,¸&ÓrC‚l¾B²×¢Ì‹"×
+™„˜µåÊbÛåue‹×<RÆ}Yg_ŒÀ阕ƒÈô®ÓÙÖé=4Šóq¦ª Ü÷eQ·Hú™àܸ 0•÷ù*¥}ßê /Ø­ól Z@d‹zÇKé¡Ÿ·å†p9Dås…àV4P7ˆëBÔKdþ´£…U†9.cq^ÀU‘Wly“:!Hæ·þÉÖ?IñIñ^t²*"ž¼¦rŒTPžyÀü¤¤ª1Ú±02([<…ªù²Ü!w®áè7m<dã(k÷*Ñ"­s…ÔnT3iUÛRêæ)u  ØÂ;þ‰Ž†ª6"•ÜŸ9#ÖRáAbE ¼fk³§v‰ä˜ïmÖä›® e¯lÚ]ÏÛ­S. EÜÛV5ç"ªÅÉšrBã€šÒ ªÂ^'ê>Ï”€M
+=Ö—%˜Fhwæ„f-ýüÐÌÌ*[kkum[ØâÖâ&bõ
+ÐŽeÉ\ÕkVÈ£¤p¨%6Ñ$¤ºøêÁšÒÔ¢Ó¶ÒJÏ/¸î¤DðèôŽ}o6 a%°7
+Í `ešÌ±"ó.•9êÓ(Öb¹­2¢©þ"n¯füpƒz8•·o´Ä]~¸ù…ÍöŸ?0Jýú¯濽½>×CWÿm­T­ÿícB= Mb}â–mô=‘ߥêáÀ5wàZV ¶ˆË™‹rƽž‚B¯4,ŠY]–Ü…ØsLÚŒk€"\ÐÄ-%-«ZY‹ Ê„!%ìMO…ø¨¬PEÈÉ×´(Eû%ßlˆ7¼û‚`kÓpÄHŪ`X’÷6O»€UµfQ½í6ÛŽüIï뜫E¹…´/T—æ]<jiá
+2öä¶èôyg¿Õ1BÖ£wƒ•Ú‰3t²ºvÅRÿ¥»‰†g,Ï`˜Êo½áÌéA*Š¸Â…H©®èAhÊÄç;µ©4Çcc ôe"yûæâ{íÝ[VËÕ@!qyõúÛÛß¿ûóϳ«ÝïSª`ûO:[ByËŸ3Öµ¾ç'}ܶ*ßgÏQj÷½3‰î ˜
+«.³ô7°ê$Á5ê¢Ý¾/Nò„ž4uijGY’¤äë¶å놿]öÕöNR^њஈTSsÂèÞ4õ
+j"î­D#S‹ 'yB­ ¹£ÄdÝ:YÚž_Y(NTØÚGF›^Ü’³÷|€™Ú%Üâø g/tõ1LImÚ3>síÓ°
+#p‡îàzµäPÝÚoo;t±—«I2W“d¬&áÞ7ÃCÇRHÇ
+1çU"Îw̘ÖßLn½—ÞÇÛs´äQvR{Ò©öøÐÉYÖí÷,Ú° ÿ º=.§a ´
+뜀'ÓƒÏú­ó’'úP"¤©5ºDóR6U‰öÀÉ_ ?]Ó2Ûì"£3×[óRHL듽¦Åê…»‡/ÿ
+endstream endobj 428 0 obj << /Type /Page /Parent 1645 0 R /Resources 431 0 R /Contents 432 0 R /Annots [ 429 0 R 430 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 429 0 obj << /Dest [ 1101 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 110 192 152 206 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 430 0 obj << /Dest [ 1101 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 110 178 152 192 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 431 0 obj << /ProcSet [ /PDF /Text ] /Font << /F1 1609 0 R /F2 1607 0 R /F4 1608 0 R /F5 1610 0 R /TT2 2027 0 R /TT8 1605 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 432 0 obj << /Length 3407 /Filter /FlateDecode >> stream
+H‰¬W[oÛÈF_õ+¦zæ˜sá-p dwámâuâ-
+ZÉ\H¤@R”÷ý ýý}è93© IÅí"ˆEΙsûΕ?LFד‰ œLæ#.ˆÿàGEó"/ aì³ ò$™¬F×ïˈLKsÇ#å4]ÿø…“E9ò˜çáéÈÝ?nGôo‰ãJ¦èB㯠„ûÄùÇä§Ql$Ä$ôX€±À³JŒ
+Ïq¹dõ"ò´#_vYõ '‚j$Ñ2-É»¯È×ÿ8®ïÐ|æÓÔñèôùŠ|Ù¦Õ7],I“læ¸1%ÿ2aÆ}ãšðX
+=š~˜4`HW¼c4Z¨=D¬œüv&ˆR¤0JÇà¿eÔxÐÄ‚×
+
+U àçEÈ’Û‹é"Í’
+L UŠÈ¯4ù–g€Î2‡3<d}†`€@È­gs4M–’–sZ«ßôêŠt¡Ã[txÎô9ÉÖŠÊáhÓM“t fAVPCš8X;à2èýZBz÷õóÃÁ ˜4PmK“4xylüÂ_ð3†…tŒ‚Á>´úÆpÓ2-ìêÖ³7ó©O´ÈZ=Ùâñ :Td;”Ì‹›²œÚÚ´Uqýnï¸2Q"!Þ{ý¨Üq¡¨h–¬ôÛñäþ“ñil«Ý˪€pÔGÓ|!…ÓL¿èb|}»7¤n•14.[·ø  KrldA ï¢5 û€‡õÎI9ËÿA ør4dåA>]𽈠Õ˼×ăfû?ѸšÜo\ÿr éƆÊóÔ©kdè­­2f›kÆM«¤P¢iVé….ŽSº´I  ~U™áMˆ~®l®ÒZ *i[ºbÂ’é`Ö¤P
+šŒòu|Ëp»R”IÙÍuïíØÌ[\#–Ð@–d³îÜo¼°;´žýJó X;ÃÊÍÓºÈçéRÛë~‹éìðô—»‡É˜ü~k–’16Ï·ã΋%ÃûÞq}÷ÑÌðéÝÇw?ø‚WÀðßѽÞbòFtü«s¾Ñ<šÕt¦—º²—¸iqŠQGÍËÆàS5ÒÖ*À¨BT˘šÖýu Ï‘USÆ  öžSÆoê¢ÙOí~ìï§>>r—{h~´;ûq9Áô¨±ÁKQêûw ãæ|³Z—I•»·c>>ëþ~ ð26ØšÜß`{ø/[í~;„ùYí!7Üj
+&BsßTòŸìe¯@„‚ús×òN&Qæù–bô„M$ð#qSš‚¦+
+ZnŽðXÚ;6 bs³~B#ªt¥aÀ°VŽÁO²Zoªé­=#5íB(i¾YÎÈ@úœ8.Ç fæ|Iá³wgæ,© (è®Á"ª±ù IkéoN<’)Ùb Ýdo`Í%9jÏÚ¤HZZP\ é¶ye`n·¼˜ËzvEž6•IŒ/(Ƥ’ÓO‰vB|´.¥™&ÛÿV^m»iAôWö­ëŠ"l0q*„„R*!!Q’‡*/Û«Ûò%ˆ~H¿·gfÖ8ÒØÛÌÎåÌ™uf­" _éCqT†ï<š*Ïò_^ŸÔñæjFADÌX|®ŠŠW<?Щ7S5W!ö–†bꃮzȧxE Ç>èÁS¦òð‚S­ÛKØ#3òÈ­+ù?tÙb)¸©Kkb'õxâ<çMõœÒXd‘:›ª^_žO©„”½Wö÷zûƒÞþðèí¿~ð†À/ªkrÍÕ5}âÿ/1¹}Ü¡›FdÇÛ*r°Á_r„Êâ|WYÙ¨=ÁÓ×m3ô#ú<"‡A„Ê
+F*¸<=¢EÀ<Ÿœ¥r¤âB”#ý§E$$3¼Xp£^÷Èé^í¥ÿJ³k+וkõhjÜ1Ý^ƒ‡l×[â
+Y‹´œ65:©Å» ‚;Öè0¥Œ‡#"ˆšûúEß÷7ô üi³ß)È2ÐÖ#ÀœàÜ3©ïøTeÔú‹˜ÏÄ-Å`»BÌä×Ëc&8ô¤bd„Z  C4adÇ»+fÜ3 Izœ÷³¸x*Ó)ªb™eòÜ)½@7ó³úr±¤iN%%ñŠ:>®À¦7¸ ÔƦyB.%f(¿Ò¸yxÇ4IÝ5”fÎT±É‰·eš!jFÞNJDS¹NÉPm öÍR»¢t‘"Ó ñÙ mÌØ·¢&Õ_½‘óú#çšR
+endstream endobj 433 0 obj << /Type /Page /Parent 1645 0 R /Resources 434 0 R /Contents 435 0 R /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 434 0 obj << /ProcSet [ /PDF /Text ] /Font << /F1 1609 0 R /TT2 2027 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 435 0 obj << /Length 664 /Filter /FlateDecode >> stream
+H‰ŒUÝNÛ0¾÷SœK["®íØŽ#´‹RÚÄ
+OEAZ`ßü’Ç9d‚糤Ž[Ñ$‰TŒVÆ6¡þ*ÜM”`‰L¹¥ÂÁõ/˜üZ<Ý㎢ÃH´ž×0<=€¯ÿXbðý1à 3AËû˜,çO¿«Ç‡$¥³Å Kr
+â<X,?–¦·øâq/ž`¹\r–rIëµ
+i14¦ä¸X]ù¼u#žH˜QÆòÔJH3îXɵ¬Q;x¬È-9ò-©Dñ’5Õ"0‚§ôßÉàD6ýÊynrÓ¸7K)B¶Ð7ÌÔ ŒêàEÇÅ„%V„s¾[-F/‡ãbJ‡¾¸8?û2E³³¡ÿ¹N>ùÑ”úáø´ðüØOpå§lÊÚ"E(FB–Iœˆ•mÔ÷8·-¬EÆÕ #Wº×¼Î»Q‰–zÛ»½ÙÁ°‘ܨ†á~æÆÅI1.ÎGÅ&ySvûØ
+cÛUì?çUŠþo²®qVtÝýpd½w°Þí¿n³›œ›–WL©¶Ü{pä}}¹œ« Š,•Üéõ@#´ºnÂ2(´œ-àºÂ?f,ÃÁ.«›pMÄQ}“hðZj[4Ïú£r¶¥òZ(JµÃÖÁY/ÑýêôÞnf´÷Š¤ߣ‘¢e‡J:dq¸=Üø;Æ«Ž\5ð²ºý÷Àmv¹qãtÑÕƒupW_Bæ]¿’¼À}üµwÀ
+endstream endobj 436 0 obj << /Type /Page /Parent 1645 0 R /Resources 440 0 R /Contents 441 0 R /Annots [ 437 0 R 438 0 R 439 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 437 0 obj << /Dest [ 658 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 313 228 321 242 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 438 0 obj << /Dest [ 732 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 504 139 511 153 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 439 0 obj << /Dest [ 744 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 299 111 313 125 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 440 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 1607 0 R /F4 1608 0 R /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 441 0 obj << /Length 3170 /Filter /FlateDecode >> stream
+H‰œWË®ÛÈE¶úŠÞ¥ˆ´øIA€ñµgÆ ¶VgA‘-‰1EjHêÊšÉ?ä³È©ª&)ëêNÃð›ìG=N:ýj={±^ÊWëíÌÔÿ𥩷H±JVK/N¡Zf/ºTåÏY¨.¯g/~úä«]7[x‹ÍÉgîðxžý¢ÿ–9nèEzgè7ÐÊO”óõ_g+Þa¥’…·ŠqJ˜zñBá Þ‹ž–±lõï
+ˆrR¥^±'°‘Çd¨<ùúg$Fºƒ!#|
+G:ˆ)Òúòņ·å3bv0_âE…uŸ5>:n„dŸP´uMIíÅJILUFTOŽmÓ;nŠA“7HŒu°ì6GS«ÒY!{µCëŠS×·Õú²¶`@`cF&øcK>Óب ÿoqô ÎóÊd­êŽ&/·eÎeÊ[jÂ/ Ù4§^í›óàCní&‹Õ&©QåáX™
+ÀY‚¼Bµ§z[*± Á/Ë«à­ÆdÿBÙn
+œqy•+“G„FEäöÜú]vx ±nˆÑt"­šÕFQNõUqÒ ¦U
+~­jc
+Ønç5H\;9,›¾#FNôè<s&ÆŽeoGÅÈYµ½}¸ô‚+Øøásï[Lº!ÒMÀâüNt.§”/%boê¼aª!
+YOˆDÏÈ÷mS—¿9 ¥ AŠÑŽö×èNݶ‘…•´•P_áû.4£ÉÎHìDDMµ¥³ ”ÿ®F~@ÆbaÞ×d Ð^_Èn¢ÿ A××GCKæÏ™bÍ£Cv ž`Õ±-MŸµ—¹ÊÞæ`Z·0Û’·¿Mg!»þ“ØåOvYÄýÜœ (}NÖ²/ÂâÙHSÅéSw
+õß/, @|X>YWDÞY?¬§š¦ò'›3‹cÞ®±¾£kŸŸ`Ü@~q›
+rº¸¡ÇnYÃ&"µ!ó& NÄZد½ý3 žÜêˆÔBNÖRãõíêýí îÞXêü
+å®û‹Dü"³SÝ}ù¿û¸Á èT•µ;êÓMÍ>Ü(‡E¨‘cïK‰?VþÝxꃴ²§ºe¶QÛ96*Ì¡Qt?]é¶cÕOrU÷„áž¹D
+ƒ–$IW\b2GÁš†ºÃ-­°öA‚hØóA¿¶§ê¬¡«€¢0WDà^¿zp7¢¹Øݱ­Ò-éZY–‚œÜ®/­¦Û ÀCP©ßÞ‹ÍÓ»ày_æ{Õš_Oe áh]Ô–ð±©øngä6ømì"%³l;Û–¦*@\Çciï‘;©AJº@T‚Œpˆû{¬ÂµÇ™ö†J²}4}{;#ÞRMÌ0ðWÄ%=ÔšL.C±vÏM;<2ûm±¯BQðXˆÈ2Šx/Ôà j#.K­^£TñŒ/$m#žI&È’ºic/)Áצ£w){G*§ïA£,q™ÆC„–ƒ3I\Om¥‹®»a@,=Àëµå8h¦2Èð0M” “…·ŠÑ€ƒUêÅñÃá ú­'€xG=p€S+ˆâG¸ð‰T.‚Ü¢
+<ѲNeƒŠ¾:ÍnάϳÞñ:%ï”´¬dkŒÝÌ:3^ÛìF÷È_NÑXZ}ÙPéÙ%X—°Áá^jrN>³!ûj’7òúåSZy®[òœ þÎF9©;÷ZÞY©2%Jè  ”—·"3öÒàZc¦£dIe·ŸL[.ñÜR«]Õl²j”ˆßÂ3–“aÁÔÀuŽ‹DçQ£ÒÏŽ’Á‰¯" ØLƃ£¤
+3õçÂlN;ÙRÿ…ãbÊ™3¶M;2;UE=[qw¦²JÐýßÞ˧'a ˆâw?ÇöB(PÄxÒ³Æx“K³Th[Ó‚Æoï{3ÓÒ?à·B·ÍÎÌö÷Þ £
+çÔ»T.î*ZX'úäIi^BN9s©oQ‡`MŠM«? )«ÀçÉ`öXnÏ«;ûB¥âÚ·
+C~¨&{“¥~ȵNû‚ÝQ÷<àÈ—*C•§QÓRk{%”¥iƒ%ѬÞ52g‘Ð;Äâwù’\<u·2†óIcÂ}4ST:¼¥‰„? ÷vÏ%^Ô'qŽ:‰®Øí>‹@±€:µcÈ¥@j¦ZðìÊ0¢9Ûü/D?•<ŠÕåé<ø=jyËŽãë@<ª½‘<~o©Î>Ewk°$Q×Õ¼²ìmŒv0é¢Ñh2 !
+endstream endobj 442 0 obj << /Type /Page /Parent 1645 0 R /Resources 447 0 R /Contents 448 0 R /Annots [ 443 0 R 444 0 R 445 0 R 446 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 443 0 obj << /Dest [ 895 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 428 673 442 687 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 444 0 obj << /Dest [ 1241 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 463 673 476 687 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 445 0 obj << /Dest [ 895 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 424 658 438 672 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 446 0 obj << /Dest [ 1241 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 458 658 472 672 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 447 0 obj << /ProcSet [ /PDF /Text ] /Font << /F1 1609 0 R /F2 1607 0 R /F4 1608 0 R /TT2 2027 0 R /TT8 1605 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 448 0 obj << /Length 3017 /Filter /FlateDecode >> stream
+H‰¬WÙŽÛÈE^õ¿¤4ÙÜIx‡ƒ12ë)ã `K¥3\4"ÕrçCòùÇ<äÜ{‹¢¶¶Ñ@‹dUÝåÜíÔËÙäz6 U fËI*ø‰óÜós?UÙ4ñÒÜÔ¬ž\¿êr5ïx¯ºy3¹~÷1P÷ÝÄ÷|ŸöÌ'îð¸›ü¢*7òb}oè7Ô*È•óÙ_&S–0U™ïMSh‰r/õE Y=%©ˆúoˆ¯nè;ny©ösu÷¨>>6ý
+_BmHÒ]Ù©ï®Ôßÿç¸ Þ7XK¼D—Ž¯ç«+õqWöÿ6›Š…DºhŽ;Õê?üŸ c÷ÙµÐ÷R¼Ì^ã[°Ûí<'òÝ‘VCj!*=<úðnn?­XÒ›Ù$P¥š„IêEi ¢ÌËs•^+øçjc&ËÉËÙŒ(ÀÿjŸ•³M®ßžÅ áb ³dJqã³ú²9–ÍÐi²RÆ°{~l]âA°#=’ëmcT»qb
+µ(ú‚7µ|VäÔNˆE¯î
+9ÂVG8Ð6lëÞök¡}~C;í¯L¿v\¤]ÿ™ê&×ê“Y[€aŪ‡W…h\Hl2þT¹„þSŸcªM³(6ŸUëª(EA"=¢B™º(+ZmÚÞtŸï8ó =!af+d.eò;sîBïœ;H7B»\Sº¤
+ÍR‡¥¹µtêE‰˜*f¤ôĦTb 7ÐÍ£jr©$(`Ú>ïÅZ
+ÈnkŒY`÷­Vg±¸Ru»(—|þQ>^],,?BÞ™MÏ1X˜ÊôF
+½Ð€Í•õºÝôEÓKµØMé{¸Ô¡Î~=|°‹mùÞ¶\l;˜sѾÎWŦ˜u°
+*lϦÜCÆì†É.J"È®1j7Ê|¦ÑªÖ=ô”Gí
++²¸\õc|ßQŒÑgï¼¢8·Q—o”“ˆl±Pö¢»¯K7ö˜\cìï­å•v‘Å}ržŸ‡óñë·Á0Ö…$´ Fþð¹®@}_[.ñçg˜üÏäÓ·,÷Íl’GžŸ*Š}/ïôb\ùÿÆL–__MÒýjLùh=ñs/ŒŸ<þ•åÝDXx5à¡vúÒúËÙ$ "í+üä¸ ¤ÏêÊÏ©Pk¨{$‚÷OI» `¡¢$Uäü‰)——‡ÃH„üK§ŸX?r$ðq³$OÂ<#—"òDß,ÌÝöþ–òa46œBnþ´¾§Öõ™—±¾,öb_ô¹?º®àÅšåÑR6yqÝSðÂ<ö²pÔ—ž¤ÁSëOøŸ‚Ú‡ƒÿ×È‚ƒÌ¸
+ùyºŒ®1'8˜ ÌD3†¢b) Æ3‘D¤öÖü”ø+À
+endstream endobj 449 0 obj << /Type /Page /Parent 1645 0 R /Resources 450 0 R /Contents 451 0 R /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 450 0 obj << /ProcSet [ /PDF /Text ] /Font << /F1 1609 0 R /F2 1607 0 R /F4 1608 0 R /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 451 0 obj << /Length 2584 /Filter /FlateDecode >> stream
+H‰¼WÛŽãÆE^õ½óÔ F’¢$j±`ìx ë À°‘Ý è¡ZR{)R&©ÑÈ’È?æ!§ªZ¢¤Ñìzí  ’}©{ªújÚ»™N«é¼'*Â4ËÂ(‹Fj<†£,¨éªwóu“©¼á3‘jò²wóí}¬M/
+£ˆÎä½þþuÛ{¯ÿf‚þ LõÂÒ3Ñ*ž¨àÓ¿ô&La¢ÆQ8Ë G‘0a Ó¢·áHHý;Áj?‰‚~<G:ÊÔÃNÝïÊv‰•DÛ
+ú^Ë ˜[UÉI²Ÿ’CìŸLüãÉžrþ) J
+Ý)ÔJPŠ"ŽÁMÍTsŠLÖ%`eÚÖΔ+IˆkïwL¨¼É!£Z³XàLkŸZ%Tw°<Ñßɲ¹¶ªj®ì_
+ÎøÈß^û¼ØÌ( ~p嬢ˆLõVrŠ×î® ,4Ð3„dnJd
+%#íZÎLb Áý. 0egOöÎÎ4*4hõ¾bŽ±™Í8Ô¯÷ G¹gö/Ç Ú퟇ð{m¶‡ólÊ!±¯zü—’Ï5sçTt¿@ƒêÙm=ÖjÊ_•ˆþèìv~Ø´ª¬Äð0Î)¥3¨ùŠÜ—e¡#|VHµXK\Ä”Q\¾Cã„*ªš4`ûÁC1IB¬ÙK-ªZÛäÑ~PØL[û êjÛØZî£Uo]mçÕSø)8eˆñ1€wâ%xÉÀ |8ÿ©Y,ÉHjáá_T Ù*ïm]Í69ç‰2‘`Æ0,½1øý´i
+¦iòX‹y@GNs¨xÅ]0I_´Â›véšÛSàX:¡ÃÃÁo4‡&C×F­82Š­Ù5Tò,<]ÖÆÕÍkuÚ©w2zèѨëÆK¹vÛ•ò|°íÖÚòÍÈ/ÚþÏx¡bPÙ¼:Dš°ù€Š,!ýÕ×—ƒSŒ|ÀýŠ`”µ'\†}~µŸöÖÀ¿io»I„(“ °øS$¿ÃÊÒqø¤~ÁjfÏÈý­ §£&îK#r¯Í‰TÓ?^nŠ¤©¸É!žC_ŽÙÔòÍøpԀ稊4*¦Š·÷¢õëW(tWújqp³¬(o[´:Ò }® m¼«Íð±|çQj©3T/Œ\8ןT9Óùæ¥4ü=®»ÐîN)1Iò¥yôíwÛÖˆn¿8 %
+÷ÿtõhŠ½òË%¼_L®nϺ¶—ÜyrlN¦©ø¶oEW›“æWE¶à‡hŽ=hΪÍCÁ-a$.ÐûÏÔ(9µïz.Ö¥N®½`hÞïw%tj\Co¹º·õ#RšjéHsy-À'uü`W.“Õt´-AEµ*…B<3!¾®USIoõ+Çé\=޺ŲUEU}¤:
+˜Z²#èxn85eÕQTØw’'4u±O&o 1¡ñ–ØÊ1×RÕõéÃÉBVådá—Ó,%åD?QvÄšÁ†ZjUH Ê®™‘„ׂ
+,öimu)âC±­ æSÜÛ.4½69úÌœ°iYÕvU=B—9†#^AfÜ…Ÿ*0K`ßS;!Tî‰^óº*T¾4µç
+endstream endobj 452 0 obj << /Type /Page /Parent 1645 0 R /Resources 453 0 R /Contents 454 0 R /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 453 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 1607 0 R /F4 1608 0 R /TT2 2027 0 R /TT8 1605 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 454 0 obj << /Length 1366 /Filter /FlateDecode >> stream
+H‰¤VÍrãD.®~Š._UÅÊè_Ê- ì,Ña‹5YšØbeÉXR¼áAxÞ‘ý3¶œÍ&¨T¤ÑLOOý}Ýã›|v™ç>xßÏ<4þá+LSW§:†$‹Ü8ÕäÛÙåmŸBÙ³†¾lg—oï<X÷3íjM6ålqfÔÏ…³ÜP­ ½}¾ç·üûYÆ2H´›ÅxJº±–CØϾhÅâêog¾v^àÆJ§°z„»ÇvØàŒ¯ŒƒžT_÷pýö~ýÇYDø½ÇµÈTíhUn.àîPš}ÃNU´•³ÈüÅOŒá34_»1~äßàGp8\'p=ÕÓ©†ŽE×x¤‹CèJ;µaOßæ3j˜ùQì±Aâ¦)Äž†€Ãöfv?»ÉOÉ<4ÑO³1¥ZSF0Êü÷3š<K“X»ø/{0mÞD†gÉhêÖ€i«Þ‰{'tSe`g4kdü`*îĨÎñ0ÛpؘîqÕ³3b¹u|Jã0Ôí0'ðÞÉÐäG^üÍËžQ6•Úº4Íãl‹ <\s<ëë“T»ª®ýz8³ð\DL|¼IláS«½)>B?ì1–êvè`;6õ®1@È{Œ¯ ßtcSÁÊ@×Ê,U?–(z¨ê½)‡nÿ»bØôKÇåt_¾±åD†ŸXY”¢ õ•˜…b†äZå]X_©­/W‡²"‘GG•ÓýÜ.î8~x(šÑôWv·=>vãÌ*’h&†#ar'ÆÔm0ÛÂ(~(X–¦7R”mšùÆÂ@ÁbÂxVRTˆáŽßÖI…©Â"A[$¬n™ë[ÒN¤º5= am»¥èÒÎi®]Åu0µ(={Óº`PDmpö\ºçÊkšî@šº‚ër‹FHÄÕIÛN‚§^¯ÛÎŽ« Xÿ¹ÇZäŽÁs%”ÅŽSuTã£iŒK^‡²ha%3GÁŽl_
+ïsÅ~PuK¨=1©gÈ‚O•Æ’(SÖ©ÜbQ”ƒ‘‘aîéÐDá!CfÅØ£!⸒ &íÅ&'ýùá$áår€§r ÝðLn¾ÕXMœta1# XU„*RÝ®·’YÞ$B&=¿æ–¨(ii#vvy]€‡âÕA¶?ƒÂ¡!–$›°ø', vþópÚùELÏn,…÷Bb‘1CÃí/ÌSñY=¿ˆqf[x5µ_|Í";Sëç(5‘3±ž¢ÚAí¼›ð“î"µäf/7¦Z:GE¥¤(Çó§>+³/g8=Eé‹åÓû÷¯cò'Lþ ÝÉ„ŠÞ.þ¢hŽ&ad‰bÛ¸“S%àü¦x .ƒHU}wûºð•<˜†D•]… 
+ÌÃàÜŠ
++•ÞŽwùG±hÄ¢¯Í±`HiáÉ¡M÷tEÙÿ¾Oݤ9-¥wÓuA¼tgýí´×ÊæNŠ¬ð=š¨ùÌOÀ7 ý¦¥_Ó|×â“´8÷æüæÛÁpý’ û©l÷\mcïcî™Î™OƒÞÚŽ|ŠeÑ]Pš8tXb#Ï9Y¤
+ ô|ºZƒÿ¼ZÏB%<ÿg§˜ðP5lg™ÄŸ¸ÿ
+endstream endobj 455 0 obj << /Type /Page /Parent 1646 0 R /Resources 463 0 R /Contents 464 0 R /Annots [ 456 0 R 457 0 R 458 0 R 459 0 R 460 0 R 461 0 R 462 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 456 0 obj << /Dest [ 465 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 179 571 196 585 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 457 0 obj << /Dest [ 669 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 288 499 304 513 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 458 0 obj << /Dest [ 465 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 329 457 346 471 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 459 0 obj << /Dest [ 465 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 227 280 242 294 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 460 0 obj << /Dest [ 669 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 278 239 294 253 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 461 0 obj << /Dest [ 465 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 199 185 215 199 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 462 0 obj << /Dest [ 471 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 419 104 436 118 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 463 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 1607 0 R /F4 1608 0 R /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R /TT12 1611 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 464 0 obj << /Length 2926 /Filter /FlateDecode >> stream
+H‰¬WÛŽÛÈE^õAšÁˆæM$e ¬×ñb7»‹F‚xò@S-‰YŠHJòøCüùÇ<äTUó2’Æ
+5 ±ƾ
+7MUì»Q¤`c”ªÆÌ6³7«Á¡-ÞsoŒ®öÈ#Ðrõ/
+Xd¹^$1ã§ÞŸIºI0q¨çÁñÅžÈUßÕÕ¦pRØ´=’¯p¿Îü„ºs°9Ñòµ®ÔÉaÛ2‡,m
+6Å®üZšV‘/SWyͿպ芺Êø¥ÄjÅO›bË¿G‘sq'Hn¥ä`[Sj²mþèXæGƒ‘ôxžé_ÌY•BÔ^³çFwFðÏÞ¼1;¾ÈHQ µ:e0÷ltpF«G]7JžwuÓ©»^a¿×©ZуRô”ÚT†‡(
+º bP'¼pÂ<ÀrãP~#cñL×:%ö…W"w™Êkßë»GGe „.µQ™‚>Ô„ÈýüœÓ%>Ê%ÁžƒCK<ÔÉ¡z}ÌÍZ¡Jv‡yºðôÜwý°7¥7¤R*÷¢ÈÇTùÏ?)Sm ¾.Â}ðZW«¬,ë³:dM¶7]S|ê·láM*ÒÚ¡·½ÃË£‘5VÌ‚Ç‘ÏÔ¦( ¿ošz¯îèòúØÉbmîúJ&­ƒAëdÔ:± Ù8¨*cÖEµ%ñÐ)@ŠÍÕôº‚åGbz㪿É
+ÌáÎ
+„¢±÷Š”— •dýDùÑå—Õ«Y©<«Ô£Ž-b¶AV®‹ÍÆ4¦êTkºã¡% º¦.K|ZæÓäÓÚ}S"¤qš\
+¥¨Ñ²Qwœx“> ‚r’ס†D˯­ïzÜ6ˆŸãŽþGà
+ø ’¦Ü —8UèûÉ4-yÕ~½]”¹Ô9ù::_ó‡#Åš ’´Ï*¥:4õ f ¥Íku*2~ûüµßƒÛÀ¤ŠjI(
+õb¾\¥ðM&ò^ge]±ž5⟹ïþ$<@’ÑrÚ„øb$j¯ÖÝ+‚r>SlO¶áËÞ;VNž\ÑQÅ[I¿™'uÞ™ªÆ˺ô1ºh¥ï5'’|]«Œß(?d’˜OJøÝÃßÈýÚ¦,Ÿ¡»›Ä'F„>íÞþU}û+ö(+P¯ÏÊÖÀääÁÞcéRgøܺÎY“…²6]V”ôõÅ:ù:9Gꃌxœ0ð¼ |’Žÿgî‡ìöPs(‡@&úYæ@
+\%Ì$N‚ 4×À# Ñ€¥œeeˆ Щ£Ýµ&ô]/*©áëT_áðô"—ÎU!-o2‘ë!ïŠ&i;íIN¸ØèeËÈŸË}èY® Ê¦hZ³s­Î[Ë,²Æ € LD®2Ç!½‘ºõHÁb\öɃER&JŒ¡•w]•Oª:€ÎCÊMö¸ÕZL;Á¦ÞRQ‰ôÁTL-kñ(µ<Ü¿g ‡-2>\uœ°h_8KØî:
+h¶XåM¤sNô’°ÊÌÛÇê`¥·NrØ£&hãD«ó#,Úè¿Ðp‰†÷Âh\èT”ñèÈœèôía•áÒw#oÊ*ãUF=« š8A»™q×Âcš\3þ‚‰Õ'D¢Á«4›þ.“ cð“hd’7²ð»ÛáFÆ\1Mt·c :2 Ã4.‹Î4YÙöþò‡v<Š‘è#ÛÇxªæWÉ8V;pêã``ÚÃRä_Â’þ㣦zcãqÊKòÀøðÝ4ÂQl–Ã\â$ÃyvžŸ„täùYÖO# žá¤ç†‹+…dî˜È’IJó§ 2Ãô)1àBA%Ö:<`¶`¢ ¦Z=À+ä²é¦­3§æfìVûÛØ%íäV–jìŽ\~jÊ°D[)•¼±üO´”ö‚×Àc{±ÝÄIÀïöp‹Ûk;*\ÖžßÆû7½*›bÐÎnLn;»²Çe¨ô^î®uˆ¯ã&/‚°þÃkY
+ûU]¶m¥Ù(û6Ìâ–6Ì"äÜ4¬“Ï ráÆ6_çãõ“Û©ºì£*ÚþF›èÈãåä vÞ]öê0ðÃt41ea]½­ŠOÒNÖÔS¡ &vŽÂ[°?;•g˜ô¸ô 2ú,›‹.ßAÑEî~ê&ÉE³»î*cõßIì¸|<p©S:‘:¬ÕBÛ*“ß)}˜%p³ ‡Éà&€\õßùØà§ý±ÿ;H;Õq«-Ý ·I& ñ¨=¨IÀi‚N?{hPÜ%"ç#UFJe’2ÓEs&Zy´ŸÁ•.a-ÀéÿîxôtlÃ-Ÿje ¹¾W»úlN¦±]˜¸…D.XÐ$õ¬Bnð{ó17<Xª-?GຯóeZfúG´m©¯9ü7BÄ©Âî­
+™ëˆœ²òhHPf[Û•²ÌMX.íèˆ&-uV@V?Œõïeé.“IƒIóûbhy¡Sú=ﵿ엵L7¼p6e),Ù«R+^¥!(œêb­,M^”79L¼] ¾¾ïñ
+qÔ „G´‚hmà‰“¨x`’Ûïœ{Þ†âH¼ëwÏÏä‡
+h0
+"ɬ„y—õš 3Ž£~RôGÔBà¨<$~}d­w¬
+ª—;\²sÞîÕ÷`
+endstream endobj 465 0 obj << /Type /Page /Parent 1646 0 R /Resources 469 0 R /Contents 470 0 R /Annots [ 466 0 R 467 0 R 468 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 466 0 obj << /Dest [ 455 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 345 725 361 739 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 467 0 obj << /Dest [ 663 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 199 448 215 462 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 468 0 obj << /Dest [ 663 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 220 394 236 408 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 469 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 1607 0 R /F4 1608 0 R /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R /TT12 1611 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 470 0 obj << /Length 2575 /Filter /FlateDecode >> stream
+H‰¬WÛŽÛÈE^õ !@šÀæMeÄ ìÚëÅvÖXk7@<AQ-‰/
+/#+ïù…üCþ19Uݼ̌<3<Ù—SU§NU}·ž=[¯}á‰õnæùÂÅ?ü ãØqc7ËÕ‰b7ëböìe‹´á5®hÒröì‡÷žØ73×q]Z“Îìþñ4û ß%–8¡Ü+úëKáûÂúÛúO³Ÿ°K×YE¸%ˆÈÕ—ð>ŸEO‹Hõomßµl/p"éÆbsïÏe{À_* 'É&kÄ·?\‰¿þײø]ãÛÂYÈÌrez¸ïOYûOUç|H “rkÙ+)þÍÿ306ŸMó]'Âõ+¼c§ÓɱÇ“ ݪèZ+<º°.5¯|Ò÷ë™'21ó‘Dž–N‹ÈsÂPÀÆ0µšífß­g–¸·½1ºÚ%
+sõù˜ðc¹ +†IÛÖž–rÃ:¸8–­ÒŽºDšábEäÃI7Óußæ¹h“}#ÚCÒŠ¦;«ºiUî²}W'mV•â&©³d“ãÏ•„ªl²
+ϸ»ײQ
+Öú×–H“R’` $ñKŠDÌ­ëø×gk% 9!“ØšMømÕ‡Úö/à_¦/€µu•‹l'p†8T'Ú L'’†ÜøÒ{Äë]â9‹ÛäùýµÔf¼2),;„_ÕµuÇ—È5Ïì£,ùúG¼³B„¦²|ÝÀØ¥ù?³<IN»IòNéWb‡µ±Y[[”:Ì`=2f¶Òª¤¶8GÔscÆkƒ…R'Z’“üÈX’jsä¯ Afcì@ÏÞš½‡ãÑÃ1Ÿ3/«¹x.¶•(«Vhlúˆ„³`ËßÂ$ 'çMVîsE8”µmlO2ŽªLñ[]‰Mgq~·=ж>"кZ5]ÞŠdŸdå=þ¦è}o$„gП9Ú±Ôø!°€È©¡!ê‘Ti§ôÊìFåg^é]½ò=QT"éþ4·ýÁ,Ò“C¼«ÕVí²’©­xYéGâd ûlçW¥ø5!¡A>i±q¡±¹j׈äP›&òøxb¯Jìª<¯Nˆ½È³†E¨E
+œû»®psÐÉ3Ÿi²?MHcœÍ/Ø#«µ/ê3Œlض[ò11Mûbäß-<Œ¤?"iDàÛ›¬8WŒHß¾-Š†ú*8s³¹WC§¨&©v}/ŠäSU_‰¢xQd%ž ±qªi^4ÝÆ\ ¡Ý¼ØtY¾å~*ñÄr"@ERv»$m;˦¦æ† °£©_¿äž·ýîZÕ¢i9¾Üªd„¡ ¹bæM‰(Š®ÌÒ„¸ŽbP«¢BsqLê6ƒvõ¥ànŠ
+î7ä¡`lÕM–B£#-3hË~,‘ï>”Ê`J G¼†|)–y¶©T¨{É3ˆõ Ût¯7$!8+eX@•7£ÂÄ®ÑÎPâ/‰d‰t_ÈÁaß µÓö¦O»èxä-jÇøV&_
+±êøÕVåLï`Ú£§§Ž~K›Ì,І¡ã0›#‡h7wbŒ˜xòv ïÚGŽ¡¿S.ktIžÒ1-¤}1xÑ<3Ý
+ø½H~!†èþ€äÀ…“üòMÿšCè!„—‚çôõ¼>¿IàÐs+3Ÿ˜²^4IYSÕH<c°œ@Phx˜ŠÊÙK|æ­½&£ï©QNÄOïÇÒy-YÐþ’•ÛêW½ÉÊîó•ÈÞªRýôþ_`Ä—ø8Êœç_Æ2þK©nñ¿þ‡A…ú%$eÇ ]úCFÍŽç€m+é-N¦<!ÞÄ–}^mÒÝþã1it탚I¨ÃÑ€p< GsnZUاlÛSÀ|´8™ä¡KTi^>¿¾Ö±°H¡‰KHm$Å3Õ¦0©9S2åU:Å>‚Âça?À`TôÊ{qÔ-\B¥9–<P.¥¶€1
+¨mVólðúo§¿)ýófe¿ £1åIn51íWÕµd ¶Íü?x†s[8µSµÔí Z_Ö*m+=çÐ'¨Lµ‡,Ò\D­¦
+endstream endobj 471 0 obj << /Type /Page /Parent 1646 0 R /Resources 473 0 R /Contents 474 0 R /Annots [ 472 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 472 0 obj << /Dest [ 471 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 206 712 222 726 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 473 0 obj << /ProcSet [ /PDF /Text ] /Font << /F1 1609 0 R /F2 1607 0 R /F4 1608 0 R /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R /TT12 1611 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 474 0 obj << /Length 2337 /Filter /FlateDecode >> stream
+H‰´WÛnãÈE^õ>5‹æý2° ìlö‚`wÀ
+dœZjYL(J)É·ì?ä󰧪šuñz^v°ØÕ·ºœ:Uýy2ºLB¨É|„ÊÇüÄyîù¹Ÿª¬H¼4÷#5YŽn¿ms5my¯Úi3ºýá1P/íÈ÷|ŸÖLGãþs?ú¢ÿVºãÈ‹õ‹¡ßP«0Rî¿&|B¡2ß+RÜå^êË%|@ÈgÑW’ÊQÿ !‡¾;"/Õ~®žßÔã[Ó- µqq’n«V}óÃúçÿÝq‚ñs‰—èÊõõtq£÷U÷_³©ùH—ÍÌZýÊY16ŸM }/Å`òÈXƒý~﹑è–n5t-ŽÆ•>}X7µ¢ŸôÝd¨JÂ$õ¢4PQæå¹J/ŽlŒsµ1£ùèóäàŒ(ÀÿÔGWûäh9ù7,¶‹=?Æ‚Ø‹¢<S2ìš%‰—…öŠJx P,6Å^â0I9Õ|fæ·•›#\óffX ÁÜQ%zºjfUW­XÞ”µ*;ÈÝmðêêyÛ™V|x O‚pÅ:GÛ R´ X;ý‹Ù«ªQpù'6ûè‹ØË"Z^wb×ÄM’…B5ÓòÀ@c(›À>Q–¥ªfweU»Á¨ ¼\?׆î7ì“
+*Ÿ|
+º=Ò°eWŠî
+´YÐ3$ú«‹»@™mW5/‡„¬K#8?¤ÞÅ#¬Kp<û*&jÙ¹‰¸*GÂÆD¾H¯3º
+6$¿k<… îÍ]NhfWöÅÌ%Ï87äªK0R’=üMy¸Á¢umL¬±32¤ÓÑ_/â¤^1 D?>£z-èUyÅž?±kàøëv©%Ñ™¶š6«N™×Š%D-U#¤ü±(Ž©ÀÀØÏŽ*e–º¸=ôKŸ|¢¤g­;Ä5ó<ì?.ö§û‘Cÿœ'dÏ#ÙUÉŽ<rï ÅB ›}œt÷æDïÊz+‡(*`•x”–‘fVɘU"a•ì„U"½\—›A(†¹‹à…cý䄺 dö8ŠhÝIðB&Ï/ >*O%éâÃí²9 ’y™Í!1þÑiÇð6üл[°¦Tõb½vQ¬§œ*BìOÊö‹§¾áúœézµ·Ó™ =˜bédÎjmDÄiˆe%CY ìzTÍ{¤ E¤ Ò¦ŠoSÒ¹ë?þ|/ó7=®É¦CEFä œ;n„îb~ºhA模†¢ýAϪWÈÚß¹ãÔº5´öƒ¯™xƒô›šu§ö
+}ò‹A#í®PÝX¹Ô¥‚g«†»Öv{ס£žˆŠ?‚D8;» Ç,®yh )‡¥q‚Hʲ~÷–=€®”±²Û ýºìv±ëó€®5=Þ2A0äbm]hÙ’a¥¬nŽwMÁÐGÔ]£Ä9€üuŽù/:ô
+ÑiìHõH^1^1T•˜Fåɲ™xiO›(è½>1ëSXÆ'R=æ&†îôÚzI&ÕSõŒ· çèlF|T^£0ØÏßp¾„f]ËûƒÞ4¸±à¤¢{¹[¸w^__‰‚
+TÉS„"Æuú2èôjÞö-=”06Þe­ôŠìЊöE’î=ë+†íàŶ
+JU©‰ø•Î¾j¢ÐaTÜpŸ¬œºj¶¯\ŽU2H–¥TûÜÆýÕÃýY0>* )’t—F*“­‹¡~±Kåî…ž?ˆQ˜œÄ7MÄ(µÄ—!FçMÀID£Áá!"l•Wk)ø䗋¡êO€°„Ÿ€ŸèQsl5óàŒâ/ YßeœÖYŸÖ¡­l8°Çºð@¦¥˜Öc}Tˆ~ϘxÂoŸ…KÊ2ãGT­Ð¯Øï­üŠ»í75S1ªEÝ÷¿Byh Ñu×]µ®9¡|Á5&ÐÔ[Êfî
+Q¨Ð¾øú{ì0¯årMUš@gæyû¢êÕ ³P¯Àv|Iü¥÷i‰mÄã\ç
+zªBeøœcX -A@›O—:¦}Å‚ˆ‚‹H¡ò\jH”%^îK ù¢ï ))ŠVÈ÷uü½M‹‡o?==aEûôÔ¢‚,ë»[»þïÿn2Ê#Ï¢l„šÊÏþ»1£ùdzIz˜C~ 橇ñ»Û?˜>Ü nIílAŸg›¯Nž\:.M¿ÂqLÎÃ-K¾ºýÈoY¯xq¦×ùÄ5’ì+4"òjI¥õ¦Ú•ùjÕ$dø®[ßåÊìï„ôúö¦û»ÓÄN!güó­×&
+endstream endobj 475 0 obj << /Type /Page /Parent 1646 0 R /Resources 476 0 R /Contents 477 0 R /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 476 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 1607 0 R /F4 1608 0 R /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 477 0 obj << /Length 3370 /Filter /FlateDecode >> stream
+H‰¼WÛŽÛFžê+~jbEš7‘”ß&c9ð‰ÙœÙÕ’¸¦H¤F£|Hþaÿ1{ªª©ÛL6~ZØ‘ì[uÕ©S§~˜^Ïç¡
+Ô|9
+Båã~â,óüÌOT:xIæGj¾½¾í2Ut<ÇW]Q^ÿx¨U7ò=ߧ9ÅÈ÷£_õ?sǼX¯ ý†Z…±rþ5ÿÇhÊ;LUê{Ó§D™—øroò^ô4Id«ÿ„øꆾã‘—h?Suw¨û5¾„Ú8ØIwe§n~«¯8îï-Æ&ÞD—Ž¯‹õXÝíËþ7ÓV¼I¤ózá¸S­~ç¿l_Ÿ¯ú^‚—ù[|c öû½çD^ ;:ÕбØGzxôq»Â~ZóN³ù(P¥…“Ä‹’@E©—e* ¼8V¸cœ©ÖŒ–£æGgD¦ø—Þ8¹Ú'ÀÊù¿)`± Xìù±ÄŒŸ¦qä¥á™Cý£Cý@®3ñÔ¼Üõ›“y™nj£ð
+“hŠžŠŸ˜é…S™zU¸w¬œÀ×¹*šÍ¶2½©œ)ì;8)&ÃeegªgËÉh²¦:A€È¬ðÚ©cjUÒj×ÆÔÅ`Ä¡$»¦'§bâÆÔ=íºÎ{•WU³ïœ YµÙk6gÓ´F-+óT>”UÙhjƒ–MK׈?7ÄŠ±‚9¼êa#kåŠ §‘é°@.‘Ë4ù‹¥¦2ï˦Vg77HN7°é±g'Ã+´G¤{'„;òÍV•5lÜÈ^‹f_«¾á`ȼÒ!(êzUáz%û:Ñ U™GSyê™÷Ct àðÚ¬çðsÓ›7ê]ÓªÎô»m§ˆÿª6”w^ªl0|À>µ@ ;kã$ˆÿ^Æ 8°ÉƒYçèGө宪Œšü{ÃÞ—¸÷~ ¸ìÄ÷
+âhºÖDrSÞLIÀ.|à|K‡áM#³q`.S- íä:erM‰\eòå[û&SQW„6ù7s¶áÎ Äú{IÀ¡DŸ‘“ýáfþ^Rêä‡äu"}KéM6]CUÔ;–ò+å€d‹²ôJ>Lâ å\>LŽò!äC
+74ÊzŽ>VRyk(žÅ•R¤‰µ>obí­õ4¶„Ò‘ùFίÑXÙjž^Ö'ÏÕS^ôˆíQYÀ.>¶¬·»ž¨ˆr}J²x•· ÐoBQou¿Ë+=V³RùL
+z×@ 34)gHõƒË~
+‚++¯"ñ,}—¦5‚ÓïIžß =TÀ@ÌÊéðb{~þ@Ý\tÖ$øp*Q œ&”ôaçžµ¤0Éï.M\©ßÉ}¼‹®ýÿ«6y[•¦«cB†ë ‰Ä†“·ö«Ú‹Ì!ã©ÅD€+û)§O©> 3 ³v2#Ø‚êaíz+jÇ»ÊrðàäØt¾ÌYnñ0%Û§Ñÿ¹®#ªOnÙ›§þò°Ô›ÄgtõÛÂÛò
+œÜx–ðÄ€Ä@"; ©™Žü‡~¡iKÀ ïIó·›P74³ë£)–¡€ýø*ÇNJÁ·J6ê ¾œH ©wÓ–…jD—.—h]ìINRToïuU~Cg¨u³k•ÉAaÈ< ɶ•å[Àªã>Ç<囵ù ÞÙR?†5_ÿhi³ª)Î{Ö‰¥(Dâ(vؽ3&¯€ÃYîPš«îÀ]&t^Sá&”öÖ,Î $
+oòRBüIÃ$]ÇΡ®h/Á‹üP•«uo;Û¸<‚kå„lHEƒñ"nS¯ l
+d$¦iÞëÛÙ<½y,Èä5Ñö-w¶ªûÞ˜íÚfkrÞªfVV²ÞöA8îêfáÿ€Á­¹ØµæÓïv¸$NNuË2gˆ-0ó<ðäwñè˜yFGŸ¯½{žQœLÔR黪kÒºIsD|9Ñ#[ïD$lûÖ¯óžÛ¤PÝô¤fé»üm˜ØŠ2ï‡~7W2²åS”ÒÊ ’@Î’”·sK
+;¹vzx¤1¼~ ¸M2Ë¢«»f1?9;í¤È[Ö¤3 ØÉò´aŵdiQq¢ð=WãAt‹P=Hséh4ÙGŠŠ,* ùI”"ŽÒtˆRÌQJ%RáJ£5 BäpÊ—¯t™ '|ùŒ/Í€Q9µ;˜mXŽ å£s˜žùöÔ#X˜Î¤>!mõ ©ð΢jr)É:+ÃÈÐ'˜tæƺÆ6 Èm§º†*Ïs‹˜†¡Eµ[ˆ<#¸Be2®J¸fBš€VšN*½&ùgùé%Îé~Tñ¬ÝQq^}}EžyÏ®¾sˆx>f 4ã3R¦oùE´×B±aÊX dÀi°sªýëDòx?Aýë%JýóÖ >…3¶(mˆåQ€PŒ
+‡ÄLI¬µìs½â[DåB„ø×Ò>;mšÉ¦ç÷˜è
+Tfæ«3-(ŠN¯µÊþÊÊ<ˆµÕŠHfAØ/Á½&‘=†˜lnw›ï“™ùQ·‘,ã¤ïã{›Ç¤œ£*YYHößÓR]!2œèU’
+endstream endobj 478 0 obj << /Type /Page /Parent 1646 0 R /Resources 491 0 R /Contents 492 0 R /Annots [ 479 0 R 480 0 R 481 0 R 482 0 R 483 0 R 484 0 R 485 0 R 486 0 R 487 0 R 488 0 R 489 0 R 490 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 479 0 obj << /Dest [ 726 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 489 739 511 753 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 480 0 obj << /Dest [ 1125 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 347 653 396 667 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 481 0 obj << /Dest [ 1101 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 128 599 169 613 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 482 0 obj << /Dest [ 1101 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 480 599 521 613 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 483 0 obj << /Dest [ 1101 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 434 586 475 600 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 484 0 obj << /Dest [ 938 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 411 400 438 414 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 485 0 obj << /Dest [ 575 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 235 387 265 401 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 486 0 obj << /Dest [ 967 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 158 360 185 374 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 487 0 obj << /Dest [ 1331 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 305 360 333 374 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 488 0 obj << /Dest [ 1125 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 89 124 139 138 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 489 0 obj << /Dest [ 1125 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 259 111 308 125 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 490 0 obj << /Dest [ 1110 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 401 84 442 98 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 491 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 1607 0 R /F4 1608 0 R /TT2 2027 0 R /TT8 1605 0 R /TT12 1611 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 492 0 obj << /Length 4243 /Filter /FlateDecode >> stream
+H‰¬WÛŽÛÈE^õ ?5ƒÍ›(*°XÛ³ ë$ÈÈ ²ž àP-‰1/ŠØyü!þ‡ücrªª)Jy×X,Æ–ÄfwuwÕ9§ª^ÍGÏçóH…j¾…‘
+ð‡¯$Ëü R5Mü4 b5¯GÏ_w™*:ž¨®hFϸ Õª~Ðœb4îîGô_roû‰^úŽ´Š&Êûçü£[˜©iàÏRìg~È&l b[ôk’Š©ÿFG7c?ÕA¦îÕícc׉´ñ`Iwe§¾ûáJýô?o<Áóï&þD—^ ‹õ•ºÝ—ö³ÙVl$Öy³ðÆ3­¾ð'Œ¯ÏW‹?ÅÃü ÆøûýÞ÷b?Ôíjh[˜Æ–>~¸]á†Öléf>
+U©FÑ$õã4TñÔÏ2•†~’(Ü1ÉÔÖŒ–£Wóƒ3âS‚So ®È#8åüßGaB”²„¢”>þËx ¯úXÐO:~ç‘G½q„›Ëƒõ"ªM­òmëÜ–E^U¾z‹·‘{«ŠÜ›âRº7ª}0[/Á¨|­˜·X˜†‚±£e³Rpzá6t[™Ú–µùÜ6æZy!ÂÑzaHv—^¢ËÕN,â m£l¾ê£1}ºÀAX6 ,£[é;Ý£2?Lî<Ÿ}óü{‡e¬Lf´~–¸ Iý;™—È<„ÂÅYœ 6dŽ ~ÈÙ2í18L¾ÉmÞy㘰6ÒíÖ(º¦¢{*\q<Vk>Y_ntÌ5?‰
+¡•#H“xð¬ dÒ_Ž~ÑAlIP™èHÌmN”ŠtíaþDo:ÕÙ–NŠ±­GÄ,ЉbªÜÒ†Ázf †Ά؊õ½{îøµO‹peö
+úle×ÆÝ´3ůiž•««å¶­a`› °Mœsצܪv[®Ê†52¦3Aq´êiBð¬L>ʶ̪Da¢ÄO§Edà}àx/À 㥰Ù]¸Ï<gØÌüY|d4Œ:äC –d’ȳR„šÑ²4„¢Þx
+R¬Yš¡2e3\aAÅ Ê1ïs²—kÉþúÐŒÓFAxä]~Å÷€&EZFh÷HçõF!«¹ íFvIîõ™q•ãøe½©Ê¢´Õ£²[±ÉÑ
+®œÝ!ŒÌºó<zôŸ„FjÄÉIÚÄ1SýTýéì7fÝÀõqÿ“%lA‰ÿ¤©%Ë $šNá#Åa·Ò{
+S&a%j•4¦ècÃóMQÊrÂ\LÖØꡲ"3}š
+ß´Tü@¿HšïwV5­ªÜX³2[è~ÑÖµixÿEp:q2pì19»9?G•ZÌŸâ 0èø<H*¸Ì§#B…}éÄxi)À)_À…yB80ÿÙ•yEÌ …] ’¼øøì ê—ïç¼ì57d=‹êí/=â¼»ÎÒÉ6²63ý©$ ú‚JÚ =;¤½¼ý‡ ÜJÙ4¿‘Çw
+ÚŽSì5ÖäÚE¾¤g¤"pR`lKÓõ®/àù ×ÃÍ|õú`Ÿ² ¥Â¤]ê{s¯ºÒ®ø%áâÃÍÿµ‚ʵ(»Må2úãåÚàJ¹FÚ>l´s#èq™ÍŽïИ=¹ÅË$!ÆÔõŃêà
+Ý"ä kcäCS:@Y-ñÙJùÔÃZ0±v‘¢}PÄOÓ3(O‡-¦¤¼ –nŸˆ Á¬rc¯¹Â¾^Q¶š ]Zb®LEÖáÔ˜Ô¦ŠõbmŠ—gðÑ¥†SJcßPíŠÿmÉ;=h2ß^¿µ¦þ¦’7;ѧÅúžzË…•T
+êÿ´W[kãF~ï¯ò4Ç,_$XÚÝm·Ð¾……®ó2±dGT¶\ÉJ¶ýõýÎEÒÈV¼”v!ë>sÎùn¹àœÞ/ã
+Ç §V@,é³Q OL»«öqzc}”Ãr£–bà7ØJ½MNúIµ™ñ>›ÙÕ4YzówÉsX-n ž@*IÈ4‚6XSÍmóÆ:Z^¯WÞ9¢ü^ÈÿδâGº=yk»&ÿ7 <§)+lð$‰Â¨*ùº5 ›` õÖ {ëÈú†$a«Ã’¤í=ú_¿KÎñŽCiºŒª6bU‚çõKŒ|ÿKDí Ž–ðo`Ê)U»HrHò€’%H£àC:?ªwçêBQfdzfÓÅì[tdm§æcùZŸ˜œ˜a8p6N.ùD¯Jã}\…çJ–8¹sÌÚ€XòU¹`²/ðéE ¥d‡_—è]¶èkþYÎ}ÿþ‡ß "•³ÐþüQ.?Ak«[<ìË{¥[sî·¢Þs°c¨ãébåA8ê[q?òQ†;ïGäó[¯¡j„q !,?ªE‚Œ=SÙa³þš°F8ÌìŒ#î­šC´bñÕ("A© h
+{–;J´³ËmküHYi÷R?Sä ¾ïÄÑÂ1R'õœ$¾µYvŠ¤1î§<+R#ÑŸÊ=K«®fØ#¸®¹×£yߣ¹¢þY˜®†(á{}â&oNpXÔ7f[•{N|{ŽÔ´¢!ÞóumSwränåÖªlvOš
+”‘’!èÏó/À|M'°SÑWö’Õx*]]—È@%¥I4z4¬\²¹0·:+ÇC§‚#„¬FL÷¹ÂïßøŒ0{?ˆ|è;AÒèÃI©\F‡£à̾£}¯¸@¦0Ec`ÃÎ|Œ]²ëöl&j‚EÅ¢þ5à˜èûî
+ Í
+•á¼ vðú3JBžðE` ¼}ãR«ÞèѨy›Å×¹\üe“Ñ®­yÓˆð
+=9i¯Ê¨àïî!˜z¶Áo‡'+÷€Ác“§[ð~½©r,§p‡]ãvôe›UµyvU^6u߶aáà,HTjZŠÛl²ºæ³wì-"¤¯[ÐiSÀFŒrÆÄ+{ÈvLõ¶£UßÁÛ~j½¡½4kãrqu`?Ü÷Ï
+endstream endobj 493 0 obj << /Type /Page /Parent 1646 0 R /Resources 503 0 R /Contents 504 0 R /Annots [ 494 0 R 495 0 R 496 0 R 497 0 R 498 0 R 499 0 R 500 0 R 501 0 R 502 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 494 0 obj << /Dest [ 575 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 89 612 120 626 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 495 0 obj << /Dest [ 1573 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 359 598 372 612 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 496 0 obj << /Dest [ 726 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 242 552 264 566 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 497 0 obj << /Dest [ 575 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 494 508 523 522 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 498 0 obj << /Dest [ 726 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 269 446 291 460 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 499 0 obj << /Dest [ 575 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 218 433 247 447 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 500 0 obj << /Dest [ 575 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 257 392 286 406 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 501 0 obj << /Dest [ 779 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 198 328 228 342 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 502 0 obj << /Dest [ 575 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 89 224 120 238 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 503 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 1607 0 R /F4 1608 0 R /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R /TT12 1611 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 504 0 obj << /Length 3065 /Filter /FlateDecode >> stream
+H‰¬WínÛØEÿê).üë0i’¢(*],8Nš"N‚57ÅfS4ue±K‘IYQdß¡ïØ=3s)ʲ’ šE‹¼¼_sfΙ™géè"MC¨t1
+Båã~¢$ñüÄÕt6ñâÄ«t5º¸l•·<ÇWm^.^Þê®ùžïÓœ|äöÛѯú]æ¸c/Òw†~C­ÂX9ÿLÿ>šñ35õ½YŒSƉûroò^ô4‰e«ÿ„uCßqƒ±k?Q·;u³«º%FBmì¤Û¢UO_ž«ÿuÜ Þ|›x]8¾Î—çêf[tŸMSò&cUsÇiõ;ÿ勱ùlZè{1^Òçãl·[Ï{néTCÇbkéáчu¹ZòNWé(P……“ØÇO½$QqàE‘‚Q¢3ZŒž¥{0ƦøÑ ö Ü2ý79,²‹<?„È“©’×ÔédâMÃTñÍ¢ê‡bÓÄ; ®¬ÒbeÔçº2ª]›œÇŠE‘g3¥Çºj•
+¹8N ¢}DÐ#˜:1„?öÄ–‡TžUêÇóÛÚ ¼©6y±p" [8-0sUTjnå/übAƒ—YÿRuj›íd§'öÚ/ì­}åÜ!~ÍŹú/ ë ‹*β®ßãX›nZL(ÆCÄÛ0}¶SU¶ÂeÆtF<9‚ípýcƨti(¬óë×ÊTw²ÌZ•©ÛMQv.Ì/‹¶SõBm릜»ÛbnT×;¯UÛ%Aë"_öˆ6fáò\DG@æÃeöÁà 9ß4¦Ê3ÈU9A@$!L§p–§þaÊRýfÇëmÅsÐÍu¢É|’÷u‰+eQg?§—gç
+wÓgïnR<ž!°B}Iº0ÕW陧^-`¦%!]rü˜÷8ólW:32únÙ©6»/ø­ºkÏUÑÀÀÆáÜ€"ÉðCANŠ4øÜÍøUm]Èšù
+¼ppqh}Ü`YLçÒñ‹ž8ôòà â@½Ãט. ÎhaãŒØˆcsÕ9¤ÄòcÈ
+»ô¯po‚ßK`Á»`b¢É;pcGd_±É.@
+›G€(VCTc“i¯ pQ„;2ÔòQ5›ÒØG¾¾½:Y‘‹LüëÚ|*òZŸÉfÊt¹Ç¦¦¼rFRªäí•3Ñ×WÞ¾¹²W°;~já¸1Žß8ı*g}´·1_`îcFΈ¼è£½è6 Ò±þ¡33uWȶ¥ˆ!´cµözÅ·I# ‘÷¥DŸr8³¸òm½»Çþ
+ OUw«²_
+x¿6[&ïì{nßíwy‘¿\=º®+
+ܽ´Ì ZéOöýÚûô4…Â_“Ï×+I`õT£ââšü¸—CÍO¿žÞ˜í¾Mjû^$›Ï‰ã>/0e JÛå cm[½MK)b7.™eâÇv½'Nqü.êf•u㌓~ñ4 ô•ÌŽâ2ãÜU’PPã#mÚO/.UEJé²Ø_};@4ê’ œªlœlÝË]^W‹ânÓd¶@ûA®Œk[OE,T?ž«ÖpšäT…»P—4ßUtéH¯†óÛœ¨ÝZ°äm úÉÚ/œXgí¥ ÖôÕ&ÜpíhƒÄ”`»,Ä“¹MKƉ
+ ÁWÂ
+ _Ù%QxðSÁU¤ÍB2dOáï¬û|CQ¦fÕ\ͳó¾,î–ÝÙ·Ñœ²Â½iZ"Ȫž›–”ß(¢-±Ä;
+‹ÀKÆs¥ð»bÎÒÄa–xJõ~ ûlŠŽ×4EÊ.ìéž—x@çK}ì>¡¦“¯ÖHÃQiJe<zørg*´”” ¥ÈëÕÊØæsî°úÍÏUÖ]Ô{¡i{í\f¨‡"þ tcÈäÍäT¼%H±^­€€C²#å£éÀ•Rí 'DHY#Œn‘éòeVÝ¡à1ÿ+¼Zvbà½_ÁÑ‘
+=ˆJ¿ßÏæ©z@HÞÍ®×ÇŽº•]ÊRb=ÇÌCC!¤ÆºàÁtÀÖ‰Öœ9Äf#?Лð]DÿƒþâyØɘ²«΋IÑrË3}Šñ"Ÿ; ÈyåN¢ìØ—]‡R€ÁÒUíjhÁpê€áþ’Â~;HÝu©üÑ~xå¹úiGªV› ôïzL8Q„T_ó6¥õ¿˜\hט«ú%½°|h"U$¶µ²(5þNÞ_ÊFÿR' Œ"›²¦ ñ…Õ:, Ð~ˆžëë23¼\`B3ÓT‰í-.éÈ—‰½3$¹½æ+HÑ£vO³Ó­h}ce¯íÙ‘ù˜' ȱ¬:|ÞsˆÙ÷Y×B}úŒæT¬(»NÞ±h­Õ
+-¶3Å7&¹ V[Úé)†)¤jR~t)ÊÉÇ5„x;»€‘3&Çšo1–C…†2‘ÙæC£O–r©9–隧ÙZž>î~¯ë‹
+endstream endobj 505 0 obj << /Type /Page /Parent 1646 0 R /Resources 509 0 R /Contents 510 0 R /Annots [ 506 0 R 507 0 R 508 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 506 0 obj << /A << /S /GoToR /D [ 0 /XYZ null null null ] /F 396 0 R >> /Type /Annot /Subtype /Link /Rect [ 77 383 210 397 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 507 0 obj << /Dest [ 796 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 71 668 102 682 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 508 0 obj << /Dest [ 515 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 436 451 452 465 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 509 0 obj << /ProcSet [ /PDF /Text ] /Font << /F1 1609 0 R /TT2 2027 0 R /TT4 2032 0 R /TT12 1611 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 510 0 obj << /Length 2782 /Filter /FlateDecode >> stream
+H‰¬WÛŽã¸}÷W0óP€­ÖÝò¦1Á\7YÌ&‹Œé´DÛÊÈ’aÑíö~Hþ!ÿ¸{ªHÙn·=³d.Ö…%²Xuêœâëéàf:D(¦óA‰
+c?“A.f{ñqߘ%ÞDR{˜IvU'^}?ÿúÅ¥xÞ`,õSYy,–CñqW™Ÿõ¦æIb©šÒM¤ø/ÿ²c¼}ÞZø¦oñŽ=Øív¾û¡ìhUMËbj,éã6Àî
+÷jÉ3½›BQ‰A”f~œ…"ûy.²ÐO=&¹ØèÁ|ðzzFÂ$xc¨Š¼œþ‡–¸„%~Øœñ]Ïqûãè$ Á! Ah·“ùâc±©Ö¦j⃇%ÅÖÞ`'cìt¡mT.!ä°T”R¬·—a0]jñÓß½QâåßăÞtUÛt¢ ÄQPô$gRSþpWüøAèfQ5ÄØ\+³Ýh¡Äl[ÕfT5C±¬ËzÏÃz>¯ŠJ7f(ÞŒ:³¯uŸÊQè‡1gœý[¿:/8÷Þ(òsÙõ(ì›Â¾éƒSS\Ô‘I3‚ãÊýhtSÚoļÖÕ¬ª+³UÞ)o,K
+Q*ã<…>–3e'íW«Õ¾Ýš~!µ9Ý`Ôo0<>tŸé}Û”bGÎV—I±néBÿQ(¢µƒ7¹{6KÎ ÖF¨—à®ôBPúø‘çh_üÅU×íŽçŸc,”-L3ií%µZcl¾À
+y¦;›:.ê´š°èJfàX6z£jßÝÙâï[ÒsÔ[)JÚØJ5{€¥j`A›$D_*ªìèš#jò¡h›yµØne‰¿íL»ª~Fzhb.Žn­ Ã,€Ì”5Õª@ ûZ¹P¨¿—(E¡»Ž-f2Å’¾ºX ǘ….fª^048ÿ˜Ö,W”+„B›ÂS¾_¹ÀŽÐb]«Õ1îß ”´ƒèȾc¶ˆåéÔˆ¿ ë[%a²ó•},û y‰¤8ñ§–Ç:‚Un¹R_]c†ÙL{¹XNªÅ•ËBë9j”AKØg.”DÖÊ`FÊŒ­ÒD½k;3i³IN…áâ¿
+û
+G¡øaÔ|O‹éòO_ådZMNõ?<׈2iꆨxSµÛŽ´¡cƒ"# X¢”Vl»žU¼uÝ¡¢Ñ7!Î+¸×›–
+”ŠyQ”‹b¹i›ª³Øi× Ÿ‹½D$ôŽ´eCS8™´8µïXº'²)ܳ¶ÏöÁþV‚5EIzO)Wàq’Ág6V¼‡ðûŲm?¿
+“ˆèUÕÝÒFˆç·õ#ôoµfRHQ
+!öhJ€f”qµÑ61€6££Nx³Î}ô
+'‰‹Rž(mØ·½tbUDPóÒ>‚ÚÉwË
+Ç¥zÐB¹£íšD
+ì^Tè:Vš™T5Ä®L›`ùL~
+endstream endobj 511 0 obj << /Type /Page /Parent 1646 0 R /Resources 513 0 R /Contents 514 0 R /Annots [ 512 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 512 0 obj << /Dest [ 748 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 213 226 235 240 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 513 0 obj << /ProcSet [ /PDF /Text ] /Font << /F1 1609 0 R /F2 1607 0 R /F4 1608 0 R /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 514 0 obj << /Length 2491 /Filter /FlateDecode >> stream
+H‰¬WënÛÈÆþÕSLýk¸°hÞI%݉7^¤X,ZX@FEAQ#‹5EjI*ŠºØ×è;ôû£ß93¼X–oQŽ†s;÷ï|ó~>¹™Ï=áŠùzâzÂÁ?üIb;‰‰xÚQâøb¾ÜÜ6‰ÈÞãˆ&+'7?ܻ⡙8¶ãОl2톇É'ù§Ôšúv ýzRx‰°þ6ÿãdÆ7ÌDìسRüÄŽ-„/ðø.…‘¾êßf§žcM]ߎ¤“ˆåQÜËvƒO* 7É&oÄ»®Å_ÿcMC|×X íPæ–#³Íµ¸?äí?U]ð%¾LË•5Iñ/þŸcóÙ4ϱ#|Ì¿Çkp8lË·]ÙTEbq5DÚ:°.3S¾éÃ|âŠ\L¼0²ýÈ~l'‰ˆ\;l Q«Ézò~Þ;Ãw±ÅyêÁÕyZÎÿA LÀÛ °!°}?‰…þ윇¡{#¯Æ½WcWÛYÓXBÿ.õÅ- <YÑä–Æ~÷£hªlé^Ö®:—6½hoFé3ˆv¼N4Hô|£`WïZ¡Ê‡¼Ä×~·«jIìX6V
+ºÜzM†G¨-gŒ: <
+M†Û•ôÜ·i«83DÊ3¥Ee(WO—ù«ï‹ŠGÙã+²=B¶ûô¢‚%Ùɉñ)¼¢óÔ#àÑš 9yR(øA‘} wèÉœÔóäv‡œ>w¶5¶ÈüDæeÊðaƒïεú$G5gUQ•b!ß.,Qù GzVÒ²¨²G»ÃG¤%ŠÉž!y´½—õ)º.ƽ5W³Û}J%Ì;»b–vÒÌ ¤\Qé±Z‰œ’£ä¿pýj‹ŸªVéWÛ¹õø•½#C¼¾’½!d Ò¢¨¾YUæ9•QÌp’²¢‘;«²8ŸÏü3”¦®À;·+@]Àíú$on
+#$~ЕSDnD4¾[Zä™·§A‚#˜›ÉÒÁÖ»ãaüÂÙs‹çUöA ¼®e³Â}XûPò'—Å© O¬ zþÂúyUÜ6kUä/”x#A
+o,úãá:¯›ö7d» IsFÛóËç• f€4¯S6{¢ìꬲiY1*¿^]ǣľÆ ëgÃÄ ]}Æ€7]vÌ¥õîxÚÁ §Ï/Ÿ×2˜õ^íRžðøø²¯áä%%.¬Ÿ×Üí«·'¾BeöN†Z—W 1Íêeļpü+Ëì
+lž+ôn‰àT>ÅÎã 4á“,÷Û¥ª™døTug©yB@åªûr…QVÕ0—]´_Xö3³5ó8!mN4¤A¤\*|ÚDô°z”‰DNÐÃe¦éi w­Ù–ÖJ
+©¤§+VXÐùH~¶èq‘æ•Œ Wg6ècfJ«­Š•¹ùu%/˜RM@Æï‘?Ū@üA¬Ô:/sª<Îc”·RhËèÜk$òJµ|/˜¢¡ ~WbEÍtkîÔá3“5ÜMLžÞMØK V#Zõ€_:Œ››¶&¾4çµ:ÿ„ƒ¢A®9jiÙ ¡Åº¨ø<iéº]1ð·™2¿œ\æEÒëÉ‚jd&›Š"bÌ°;`v³Ë¹äí|‹0èuÞ5
+Ñ5·Ö°/¯-z-HÞ¼4À„’ñAEÍù—Íój,«C)Öuµ·}ᦟu~Ÿ zo‘öúmÊ؇}ìæi^Y«.}[Ĭ¯Õ¸«ÕØ«õ¶Ãó›szàƺræ+Š×õƒ†SŶ»ÑP¼>/L«G.•H7h7fDžnx+•@$ÙÛ
+pâJ= F… )¬(J Æ}^f#xô†KÈè~gTçyñÉ3ñýL4.Å1p¸ÅaxÒ= Ù×X™5F¨„Ñ@"ˆFqU°fS*m­ÚSWÆ ÉúÆ )ó†Úbµ£2†™Û¼Ü£g楸š^¡‹¢:¨•XÅ*ÈÑ÷®dS ¯lûj¦G”£®4‰ãr䚬œx.ñ›Á§7Æacä¥ég´#B\Á¾˜z ÑþõQopeURbºýu€ÁÈÌp0Ó´ŒÛ)u(eêÉ#ª`X^)+–1PnaüB:_l\D#“&ª@Y }øHe›~„ð„™ß3æîv½)–®Zî[®¾*kõÕ£› ËÈYSV»/jVØ FÈÛuO. §¿h‡:5øô4ûpUx’€ÿsîù}îFùç}E¦q²%Œw`­$äÊvAŸŒÉÙdì¸É(Lþ3ÖúF¼“jÕ—V4ûº®ˆéì«ö^?“ zv¡-ÀPŠ€=-ª²0Dlª…=AÌ3-ÿj±¸¢h¥Ù£î„EÚŒX
+endstream endobj 515 0 obj << /Type /Page /Parent 1646 0 R /Resources 521 0 R /Contents 522 0 R /Annots [ 516 0 R 517 0 R 518 0 R 519 0 R 520 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 516 0 obj << /Dest [ 515 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 333 319 349 333 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 517 0 obj << /Dest [ 748 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 164 249 185 263 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 518 0 obj << /Dest [ 552 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 207 206 237 220 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 519 0 obj << /Dest [ 748 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 483 137 505 151 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 520 0 obj << /Dest [ 533 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 209 110 231 124 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 521 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 1607 0 R /F4 1608 0 R /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R /TT12 1611 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 522 0 obj << /Length 3492 /Filter /FlateDecode >> stream
+H‰´WÛrÛÈ­¼ò+ºø’A"À­Ú­-»¼µ«uEt¥¼V†àPÄh
+ƒ0¤=ùÈï_÷£êñüI«;KÏHA”÷ù£ŒOÈ`Y‚·LÒ å> â³èmšÈQÿŽpÖBÏד Qa
+‹Üªn3‘²ž¤Ú¢…Ë7ðë<ŠãצÁT^¨òõÜì‹îŸ¶)ù‰2ÕÒó3ÿâ_ŒÕgÕ¢0Hp0…s,Á~¿¼I UK·ZºÆ+| Q»ÜM­ù¤«ùHC£hš“DÃd¤)$:ˆc@ã;Z~˜1Ѹ%|hÁÔ!Y¥œÿvâ&ôÛo–„þ—OÈÑà‹HÄo»¦¨î`Wm›:·mk—°ªQs”¸ûÅl¶¥Ëz·(íç]ÝyZ3Å®›(¨@­aaòOmiÚ5]kËU
+U Î0çO¾ÍÊéÓVÎŽVÎD¶Wc  ]±AÕëªE#"±¢ó=?%…écL±XƒûÂ`À9´Nåd(*xëÅhûñá/i‚
+õ –uÜcYÇ=ïûdæƒçGxûfQ—E~Ô÷ ¿³ IøêÙQ1zeøÞ›rlj ~”ü­6{¦ÀäK9Ïÿöþê!IFAš|© À‡ª®›z×" ›€)ªÎÞ![Õ+ÐÈzÙ;ÆÊá9‰%f~T¯/|?¡Æ„x"“«3ãOn\­ð µáËäYÜME««Ÿßy3„髽t™f'ŽL ¹úò; ™`ŒhgÑò
+V ÷RÔÞâ…ï¯éj­.©Ñ˜)D*Á•Ê…žQôu,GŽž¤3aßÏÆÔç¥' ­+œÅIzT´ÁMÞÛŽX“ÇýÒµ$C¬à¼;¶|Ç+&q0‹N®ì߸¢¥€Àâ•JŒiÿÀfÒóÕ2Üv-4»
+Ü°Ú¢º=¡g»µ´›?q[VîÉsG9õPqNdÙ1–]ÊnIiW¾R©…v R u?Ó-{š|‡˜ÔN¿
+Â!„.Ðùã # ¨LwØråîÆäê1Kc»<€KhX o1CÌúöƒ2Ä‚*Õzw·îˆxg0ì›ïº‚ËdÑV÷ESWjUÎcÌHxèeQJ.~[D?ñiyÀÀ*Úº4ï A ¥èªÉœgrk‡¶ÐÔŸaùÚ³êªæ‚ rÛÔ[Ût…mÏ ³ðÛ’ÿ£–ôIrü¥²Xo3ãG!¨{ÇqF
+ÏÒ­æ²*kèoŠˆf\زFeY9ÛüÞ'G¿Q7=™Ú/è*”wdÑ=ºTöcÞ¨ø<ö™PªÃWx–‰Î{ ÅþeÑ8ûô÷6–\køûºÀÞLüÕžh°§¯úðÛ¥¥=  lÙÎ󿌾:R—€Åº3ÿŒ®6wÔ9
+2òµÙv¶i š’)Í4v…‡VÈþSíL
+èL”žd†º*Ä-Œ|?7òAŽ-zþ“ma[šÜÏæOý¤Ci¤^:ðÞ›¦rú0¹O¿Þ.ÞŒ¥]´§SvÑðÒ®úxF÷cÑ‹ƒ‹yÚSQÀiÆ¥…-]0(Ü>kMÅA·§ªaγkÏ%–×r« „¤Ê§5-Ë.‰ðãβvÙ‡5|èöl0R&Zæ¦<%¬D+aÀÇ”ÆCDÑ)gq®©÷2yn8ÝÈdž[9B~­´¨,HäHœ¨¯Þ%kàhOFLÄXÙ^ßÅÓìóÃÓìu]aÜל1 Û×j¨dÜÔí×|b6>eœt@XÊ';Ž3dN‚8:ùî$ké¾–¹„%ÙÓµ…‰¢ƒ šky´ý\MKë±Úº‘Ô´Äü}ýk]ãSQ•P/~³9å±¢„ݹ~P'Á49k×XR‘‹‘ª† ÀBŸNKÙÝê¿´WMoÚ@½÷Wp´¥€ˆMmÚJUT©—Þ¢ö.ŽY+ÆŽ
+æãß÷½™±×šôÒ ìïì̼!ÿŒvÓUPY8T]×HOŒ²N‡íïBÊ
+¶6ûHtÛþ.+]%é?G"è&˜,,ú^Ü^Ýf×{0Z‹×J½¡"¤=Tˆæ=%^Õ²™¡òDߘ†UÓ+m0†à^ÌÊoHÏJ“Èç÷
+»Úºr­nßQ$-dÇÕ=%
+«m¶ÐsÉ;Jî!8&Á½0ZyÞØ
+endstream endobj 523 0 obj << /Type /Page /Parent 1646 0 R /Resources 527 0 R /Contents 528 0 R /Annots [ 524 0 R 525 0 R 526 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 524 0 obj << /Dest [ 511 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 496 712 511 726 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 525 0 obj << /Dest [ 545 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 299 203 321 217 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 526 0 obj << /Dest [ 515 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 317 106 333 120 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 527 0 obj << /ProcSet [ /PDF /Text ] /Font << /F1 1609 0 R /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R /TT12 1611 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 528 0 obj << /Length 2809 /Filter /FlateDecode >> stream
+H‰¤WÛŽãÆ}×W4üDC›wÚðCÖ›5bq€ü?PTÏ cŠTDj´ò‡äò~È©®¦HQä c#’Õ—ºœª:õa½º_¯}!Åúi%}áá~Â4u½Ô‹E’EnœzXïV÷ß·©(Z½ÆmQ¯îø,Ås»ò\Ï£5ÅÊéO«럹ính=+úõ-xÂþeýã*Ó'd"ñÜ,Æ-AêÆ_¢ðõYôÅ|Ôÿ||u|ÏvdàÆ–—ŠÍY|>×Ý ¾ø–²q’Õ–­øËwâ_¿ÛN„÷d‘Y¥íYÅËø|*»ßÔ¡Ò‡V^om'³Äõ_­˜6_›æ{nŒ—õG|ÓœN'×\iµt«¢kq4®tñèÁºÂ|zÑ'ýu½’¢+?ŠÝ –"HÜ4±tÃPÀÆ0µzZ}X_œH,ñ®½1¸Ú#@Ëõ¿)`¡ Xèz!„n¤‰à×Þ©I¹‰?ò*dÆ«žÏ6ÅnæJ;…Òâû¦îÔ—Nè·ŸóC™ë§­Òªlè/ë—–}5‡›ËÝ~Føa( ÙôHw¯qd g0T«_”Èv À(ñjžJ["ù¦2K.WKswr i ù‚­²C«¨pPè&½l'z#ZÙ°™œÒØøG”µhj%š'#‡rßµxÎá(DÝÚ¨ª©ŸE׈ÜHEÁŽtá> êM§x}^UdUéh%22Žte@
+èkeÖH'åä„¢ a<€ëãPEC„awl;±1¯7žòý?áš‘[åÅ1Òøe£ž[J$ÑA᥄Ž­Mªª­+4|6• )Ri¢e¢@ #2Êšjl$Ïæõ¹wð&ç…:aíßæ]Þ÷ÊðhNRJÄnüh³ÑNêÂhò¥Q9tY醬EH¶ç:ß•…ÈùÿjëÜuÒ"eø»jžðëÓÍyuÊ{9
+É‹ÖOíöÝYð×ÿÍ‘Úh¬Ú˜³‹_UÇKîÌÒ=` +Š’O‡½ˆ`‘weÀ™éøܬç¿)QjU^tìK×LÂé€æ”P+µU[QkG!²9!"+œXÛGû›ña¾)7¦ú|’&ë݈V°>âôû{ÛAÙ¶XÚþ
+‚7õyYJ½ÁH—{ÃÂöwÄýÝaºÜf…æÇ®ž>G>HSÌžä®;nÈó\µgš4jEV§ †0o ž·ìPJfxL’ä…zŽgx,8«í„ k4è‡ ýdz$
+=Êæ­Uw†ÿ1§ð„&z¾Žùãv@ ­²AA3ºjÈ Ëã~ÞCzÁo„â-—Pn´˜$}‹?Úîì<#ƒZß
+{‚M™•s8ÃWu8”ÛÛТ-DÃýÜœqK€íõðNañ‹s¥FøA÷‚qiDÚTÃn@ ßîr4¬˜vÆ}†ÂK1Š¬Ë‡G›.±¾¡‹¥iAŽ/Ý4áeh<‚ÝüIöU+¢edÒÇqÎ)åÚ KÍû™E»œ_.~¿&vA–ÐÕ äjYJÄÎH—‰ÝÂöwÄ£»Ã½!o6ψgÉq ”nBŽó»Í„w§æM*¤þ õ̤±$ï·Ç™ë/“þñ¼Y!xÜÍU7‡]^q15 Šˆ /«° Ÿ×!
+¬÷óBÂ3ÈÝ¡ ›ñïo˜*xZ3æÓˆ•wÄØÁÀt˜˜šaøj›¢Ì1íÞPKØ•e£ b”ð5Û¼c^Gsp™'ÀÙ;¦­S*¸áˆJŽÆ C$9­J+
+(H½È7 ¾È/wÀ³‘—ñJLŠú£–C™œTØ ¹KcŽ¡’æÕ”âåÄ7å$çq€å¿Š˜k`ÑԪߡêýÿŒug!‚Vh© X
+cOLÀŠ
+
+endstream endobj 529 0 obj << /Type /Page /Parent 1647 0 R /Resources 531 0 R /Contents 532 0 R /Annots [ 530 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 530 0 obj << /Dest [ 748 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 92 217 115 231 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 531 0 obj << /ProcSet [ /PDF /Text ] /Font << /F1 1609 0 R /F2 1607 0 R /F4 1608 0 R /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R /TT12 1611 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 532 0 obj << /Length 2636 /Filter /FlateDecode >> stream
+H‰ÔWÛnãÈE^õ?57&Íæï
+ ¤Bzª›ú´kž[X¼?ˆuù"ZrñâÝÜ’ȈŒrÊÇž“îÔ‰QXÑñÃFtø5¢7žA)Ó å^÷…ÈãypC(+1Ú¤2Áÿ/2ÿ,»
+&ÐyBuÍþçTW[âÑ5FCJ›¦-kÇàÂÍEƳüŠÝ”Œr~5NuPBfzø_fzSW'ò„e17iN¸ÊúB´CTtÚ?·êB1dC;ŸMµ’)PIK7ÞÄIõÙ³ä Ã:7™2b-#Jè7 O%ÝEŠD^2D¹› zLÆ0ã•.ÊèÝWŽ -—ò¶}Þa–o²v©Ç²U¿u£~5xê¥/OòíÝòÓòퟖêå¨?Ćd!thF’²Ò™Àˆ¼*“^AMÎ䱟MÖí¯ˆû»UÖI)‹°RNvÏËÊ „ø£$$I”zi É6Ѷ=º3€AIQžÐÐ~Ç+ˆp~P¯Uù£øZ=~u74Îd5Sj!Pµ-r³=ŠPbßn‘Ï[ìÑ7Vë0ùæÌÜ_OÕ!õ‚áüdê3‹Ülò\³Þ"ŸU?Î'Xw£äètìïøÚsí³ÉçÐÇ&Þ•áV; ·J£îÌÍéäNÑñÃQÌ͘g‘Ï›—æH´MLBæÚb*Î}¬RV§Øäf{šH†cÓÚ&Ÿ×:†á"™)¨ï—ž%´â$ÀÙî†En¶G@W‚+Û-òy+ 9E‘±Â„V¹¾¨¸Ü<tGwØéõrh~ºŠ”õç¦ú,t\=Òu£K…xỽ©ÉÇ­˜Öß8Œ½4¹âZ‹|Þ* ÝiÙí!†M1íÄaÓ”&XÃ.à÷
+â¹ÙËâk³ÈgM‹2¬ðÆ4ã¨GúñýŸM¼Ý¿ùÃ÷ïÌË££Vü]ýÜ9`Ÿö<jW¶ÍNtÛ
+‹ñò/8â5Sam#–€ÛxŸôªÍOΤd)_¶Ž4efΘ™pE%ÀžN¯rzÒ0…DA(ÕÔõ_’Ó'_ÆD DÇ•Ñø$Ÿ¹^¾‚h\‰y¬xÙ;Ø-¨Ú$Æç–Ò
+å0µå—Ÿž¹^§®$Oê•?
+£³ã2ÍŒ/s´«^™t^‰¢âÞ•Í´]†Ñµ‘Á.ÅR¬¥öRlÙþŠ¸¿ûú¸b“Ïv»Ç•
+ëFèöÍow'ŽyüðÔ`…Áùh1å6¹ÙZ…‰½_Ûä³9®ë –v¬¼ú†+íÐ<ÉÄÿÄ$¨oWˆE<¯°Ü|ψ«ÔL_¸3cáíÎ4œ(d×›|^Åd<ð<ͪ˜NU̺íUã|púEÔÚ¥˜1ZjÏËöWÄæî4¼š16¹…¼˜¦D¸}L^&lEºúâ#VßßÖ„¯V%"É?ôx(©í^%¯°ù4"= ‹¨$#ºN?·P[LðJ!4tˆq"ÎK(WϤ*Ûn¾
+1­Ð#–b*FÆ€=™kKìy0vؼù
+kx¨–åö'<ì@ÐÒ
+\eœî延H®oä,åв`|W_ÕÙ#jt4Ы:y‡l
+3/Šìj-ßÿWv¦W5z¿";£”­ôJ÷µÉ-ªAôdÙuÕ‚Ÿ­Z\÷Š#çÅÅ `W\‰Š…?[±€Bv3‹|6¤s˜ƒ"Õ{éý5¤} ã×Üd‘ëíi>8ñ"C­B¬ Jh¯
+ó›¯Kõ½yäEö6</ýÏ
+endstream endobj 533 0 obj << /Type /Page /Parent 1647 0 R /Resources 538 0 R /Contents 539 0 R /Annots [ 534 0 R 535 0 R 536 0 R 537 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 534 0 obj << /Dest [ 511 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 398 507 414 521 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 535 0 obj << /Dest [ 515 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 89 491 106 505 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 536 0 obj << /Dest [ 529 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 127 491 151 505 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 537 0 obj << /Dest [ 545 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 248 491 269 505 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 538 0 obj << /ProcSet [ /PDF /Text ] /Font << /F1 1609 0 R /F2 1607 0 R /F4 1608 0 R /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R /TT12 1611 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 539 0 obj << /Length 3626 /Filter /FlateDecode >> stream
+H‰´WÝŽÛÆFoõ“‹6C[â’Ã_9v
+ìˆ3¶zÓåb
+å̵¡í:söêã®ÂÀÏʢ zàÍ‘2PEtdœçõå%•dË}–7,©ÙèÍA
+y¢–j¶Æÿ y’Ayn¸œ!Pw5¤mØ”küyÑîr»m¾Ù¦ýŽiß%—‘š”²Ãç‚!¢h]^ÿì!.¦FRÀ–ÕMÍX1_[‚/auV\粓×Ùþ¼'Ð n7²Úô£ŽÆ,+¶ÝçM¶#ÉÇ^"Ã:ÎJd=”y´ïëŽÊuŽïQMy-QvíÞ b%E”êpˆ.ÊÚíyäu]ò‡TÍW êêqdÇ
+FÝ<\Ä)BEa|Nå¬vu5îÒíEÐ*o½L?ø\L-(r©±¿W9'Ÿ ëÛ["¿²›ý Ê»±HÐ×ÆIœkÎõ_™6Í&iÔ0hŒ>R‘5mÙ¾7`—§]aGgI0¥Šî¤A=1W?H½Þ¨i¤°P* ½‹ˆhDDt‚¸! r°¯c–;‚A"|&ÑpÇ™†«Ä[Mvp0òøp°yŠäßÕƒ¡¸äF©¼"Ö
+ˆ >¥`Ó†Rï^d0’Õ v@üš*.â©’­™²æ÷!\—üo²ÝRyð[¶ÍŠ=ÑÄu÷ýp5/¯³Ôü–³7oØùz§ù©@è<¾jÏÅݹeÖ?¿¡ßLViNÿ›Þ™@+ïáÖì0Iûƒô/šsÝBë#òn•}ÈV_ÚŸÊÓùÛrµÏ
+ÄÊ]Æ}úÚÌ°334÷µÕ
+¤½bÅ~»”DÁ+*£Ô¢K¢Ë“Fø¯vÔM…Ü";“˜eõ~ÙT J\ð¡Méf¾Ûdë†årM<}Ú¥®k‹Ú­cðÏ ‚_ohß·ß>¤¥g®‚/ËíÎ\øÌu·ºöè; îÄM©?Í}¦¢ »ª ?²oŸMU7{Šç3uc|FÇ<ÔùWÏüÁ†¿Èš*«ço¾ƒ¥"Žé½»ççé¿ì øûÛŸˆÑ¹Ø?¿³Ã?¶C›á“¿=t†9Á]JΪSæm8ÎÇãw2 :ðö'à7£ŸÔº½‘Þ»¼)©)Q×ROEôù1•»†=f…ĸˆ´ 5É*K Q¿^kjˆ›&S“m%nÛÑjT¬jp“u÷¡£á(CÄYŠ~É“
+ªlZ…SŸ·ìÕæóŒ6êo)Y¶ÝåYš5èíŠÚ*ÖÑ…èHªÁ=TÁæÛžGLF`€!Ú¯sÜ4h\lOfˆIrð׊‰à¿Æd§ÆÜTªöKËá'„ߥ˃ÃðèT8ÂŽc­âÐCgý&º
+|íw»²j¬Y¬È‰â Šh”†^°ugL™³­L7I‘ÕÛzÈœûe\<^yó­^##`Å*kÐâç@‰n¿È¸MÔ5®m*\@œX8÷5':n ’ìÐa/?AÅ5
+×¼ÜWEÝ^
+3n:ðÙÜï:äþiOŸÿo½jšÚ†è½¿BGe&q#ù»=µ ‡N™¡Ór#'8M'b:ýõ}+ɶ,;P8`bízß¾·o…Ÿ:=—#§‰n@ 3 Øåկ˱ŒB Ï“¼XÓÈ°õø CºƒÓ—”—<ËÇ0›r>–œü?>Fc|Œ,#
+.„lxôn„ô
+a:d¦g+ ÌŸ0FKUÌ^ÁD"$u’ùà0NÓ$˜KçõEÚPOØ'ÆÆ-c÷JÛjl1³¿t.Êü[“ÓÜE§LaxØ=™+ü’¹™Òχï/PÉaO™õö¤á-ºG¥ÿJ(pðèà0i0R ¸óesÂ%hN²ôÚÞ8¯,çaúšMò½¥%Ä®ð&ið'%£G>¯-©U¤A"zvp`®æMó´ —cý™{ŽÛL##zòiI”Y-ys)¤WŠ(•æñW•"Js«=Þ‰¼•Y´É,]Ê™¢i˜w>Õtk¦#í´Úõ
+»ANaD¤ò¹Âø9«Á"É_œ#û¼#ážwA"·Mà5¢†ŸØŦ€ã$œ••‘Æò³6›Uqœ#ù’8ê+D ½SO
+endstream endobj 540 0 obj << /Type /Page /Parent 1647 0 R /Resources 543 0 R /Contents 544 0 R /Annots [ 541 0 R 542 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 541 0 obj << /Dest [ 678 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 424 730 454 744 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 542 0 obj << /Dest [ 678 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 236 634 266 648 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 543 0 obj << /ProcSet [ /PDF /Text ] /Font << /F1 1609 0 R /F2 1607 0 R /F4 1608 0 R /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R /TT12 1611 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 544 0 obj << /Length 2987 /Filter /FlateDecode >> stream
+H‰´WÝŽÛÆFoõƒÜt¬fÉábˆÓMâÔ±¯‚
+–më’­Ê¢ÉòBŸç'Æù±ðc{alóBísõoß½uBÜpò×æù—}Í.xB{Ý~×ëx«›¬Q;U45kJöÛa·§_˜’-ÕC^yñÀʵþ°-˽`‹’í+5'˜ä0ø|î ܺUt*ôN× 2V?½aªÀÁ¼s‚'•› špÿUV+Z])†Àur'E´­_çEÞ(öæÝ»Ÿim'Tù¾¹bäm„γ5›¬iC‡ÐùGtq‡.6èšì£ªa¼€³]Y‘jè$´Åa·T]Þˆ<toWÞ+ Q÷„ᙥÉwª<4_³;n+–\Ñ4 Ãúì°mp#³?ì£ël—t胎ևêß9ŽD–°lYVN€§Æ|×Í#B^ÂVFœ;sJƒ½3GTòÆlÖÖROjuhò²`NÜ­l6dtev›¿¥”öYЪÀ&æõw6Ìpî"c››6Jùt8˜eØiÓ—â܈–cœú]¦ø¶jýúƒ“ð×on`a­Z§›/b7‘”sÒSêÒè†T#»ª…o]Îsºs¶ý O$ü á\×øÖ«ÓX†!ŠÃØ‘:Äá‰.ï; 2òm>¢“—î´hø”Kî`Ÿ)/šJ¶F”'Fôºj$dØ+Ñшž­×oyLÁùH‹Äû²«¯æ„@Ä=7xiwBjNX8
+ýa$9‚™^±ó¤ì‡¶yq ¢øXµ¾€!(ûc%‚Ñ/B¾C‹àT.e Áï•1Ùå‰ÔavÇ7ùjCC&ß©<Mý®ô™ÊW”Õ.ÛnŸQD¬};¸¾4Ê惬Lez
+µp©Ð‡ö
+rϲ…¥‘Êž‡¶¥Ó ¢ÐxÜÄ<=KŽÈڛϕC Ћþh|LU}l ®SËàí¦:(´ý²Áèû#€6;ÒScÖ˜As_
+$ƱTÂÛË-L‹Rx’(m=ãz]x&HØ.{žh³è½ø’ý6ûêý b“ãPyã> Ì@^ÌëžrÓ ÉˆÙ­i«¬¬Ž6k+"”þÏ­ßÿlë?5j0:…@ Ñ u/¶fGhGƒþÙÕ×Ö”‹”¡»aKˆ 8`d?£ÊÐ
+‚½(z…oª²ÿ_âiºÈßß,~yÿö´Ø"è4ï8y†Ùë„œìŸlÓÙž¯iܹ‘4]H¸ÈMDô¹êØË QlÈK˼+\°!«ûÅó´YRßG9Ñqés±e‘æ™Â6l«ˆžiÐÂF†£ò ã˜:<2²Èt @«?†KlR.äf ‡û¶þc\ zÝü͈Œ˜x:¸Ù¢Ìþæ`^ µwþ^P«|1Ñ
+dbÖ¾:TFÍúßÙÖ<. Ì\Á´§äãhCÚÔ¢mñÜÁ›/—héú­R(¥…y^gÛZ C*ˆ²É´ù&äíö0¼°wL8~Ÿfhx"8æ)4?ÄvêáO—L:!Çáad
+&³" —Œ5!×åúÔÝí»»þcxA ™8!>QéA•O:ý´PŽ„Ì‹—n2ËJeÏÂÄE¯ . ˜OÀˆ#Øéhælî í'1~§:%5´Ñ¡VÛ?Šb_\¸Ö„¸Ý¦ evOÈÇQú!= R
+,ÞNˆø®Á@ø(̼Ʈµžß•zC¶“#É·#+ˆ¬t°YöS¶ªÊš ¦„Î%ÇC=”A›>\þ6/VŠ}ý&*% Z.âÙüŠx;HtÄ-ádªx@`èà%«û}Y54`*af8 1«0Ž©'Hïø¡>èÑ
+쮡˜¼›vèRƒŽ¦¬d=ûBcaÀ DICfFd?+i@ 8Ûa!ðæUo\ï\™7#Ê %Å@Ø?׎›æÓƒ»g .aNÃ8cTZ@¨„PóÙᆦªV—Õ¢ÍõµU¡VzÞiqë-ü£ÂÎv¾3“žªV)ƒ*š³
+¶$íµª=ca ÌŠgë(¶|fE×Å|§5ùy@,h²Ó£“Óà×vd<Ô°ôÚ:šfÎìS™ß³ûÃ~›¯2{9VÓøµEˆ4›g3ij85Râ÷C^™apôâ~‡Æ· *Äf¶e÷ùz­*¸†©]–ã.ò&kžÿËxÙì AøU§r!€$QOõæ p¨ÚŠ
+¼¿3Ý
+¨1zú³Ýé7K{Õ]<zᯥ<£$s¯}Ê@³y#[}4Ö8ë;’•q¥Z×Úa3+õÛ‹ËÉß_®­{ Sñ
+
+endstream endobj 545 0 obj << /Type /Page /Parent 1647 0 R /Resources 546 0 R /Contents 547 0 R /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 546 0 obj << /ProcSet [ /PDF /Text ] /Font << /F1 1609 0 R /TT2 2027 0 R /TT4 2032 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 547 0 obj << /Length 1917 /Filter /FlateDecode >> stream
+H‰´WÝŽÛ¸¾÷SœEAµ6MI%m'’l³@‘ ‹Ž ´;“ Ìk×–¼’'{ŸWØwè;öbII¶eÒN(‚Œh}"¿ó_-&óÅ"
+?üÏ›Eø»F,¢)<FòÕn÷Eû‹¬×údåÒ›¥~Õµ`Z}­ZÀ¨À‹oñ–`¿ßS/¤>i«T´x4RR\2Ô.ï^­ôI[L|(`D‚†Â‡0¦I§œêȨåäqòj1#ôñvjƒ©™²J¹øñÈM½ýbÁ(þ?½ÕJ‹_´+<¨.¶m3…]S”O€jÀ¿ßy3Acònþp÷Ú›q´Û·//ïàóû÷/à¡Ö>LQ;Ôƒü$[è^Ôž²aµ+—úœá3|ĤÝekØôïê
+Zù±…m-?Ȳmz[Ï|ŠJ(£ >âG-•ÌÛÚSâTk¹iß'`ÔÐë­ç#“Ì ¤B-Ô—oñ/†Y¾ÊÌÎ,o¥YuÛ×ÅOn¦:Ì'/
+·^Š1!屃à Vó¨}Vigªó•âò#„îC9ä7çÑ¢í¦N0ù0ãw†¡‘þ*U_‘£ž÷B}… ™„h%ÇDC?©$<JÅ«h$”Ëâ1±„ܹý
+|ÄŠÅXHÅùnŽUä>SGE
+™t…äŽÜhGa²3F”·ž?{÷Ÿw/_ÿóÏ^¨Ì×9ßW®^œS>°D±`§b`9‹µ(èAŒ¢lå&¥–#›>Ló¿ŽYƒÀÍjÁ¬Œ ¬ª”cÈÜ‘ìùƒ§xÏ(1–”ÌNaÓ( anô|nŽÆÌQš8™mØÈÓ¡ñt‹™`Ù¾™kgâ>J„›Ë‚p¡nºÔDâTäfîʱ(v‡Ž ë· ·ïmØ+ûø2ËÙPM5ô‡šëëò*,B5äUùX`GÌžø #]¢£Pe`$—î7‡âðg*Ù@¥ÓPÑÎU‰&àØWú 3…ð¦(‹Ö8cì UœØ@GŒá‰ãÿØÕŽ•Êt'• ´SñÔ¨†Ü]>Ïÿâ[³7R©î¤µÖÈæqz`ÕÓÖÍÜiUGjžsÔq7ªzH‡º{ˆcû¸çVUˆŸ÷ˆa·w¤R?bp ©dÚ3q˜9º¡}áE8¬ôlŽÃ•úýSþ¼+>dk¡­ôiÆ ÕþÍ'X;õ†Æÿý3€Û!¿ÐˆÝ¨v A/8оý
+|Ä}ipáŽ\òý¯ê¾<ð؆ÙYÃ4ýòîËYꦴ`Ê$þê“Ù†9˜U>Ž•u—«0‰Ü¤ÌZ¬Bžj{©X¡”4 \ñæFU¬w¨;ÖÛ¯À=w÷N6Þl‡¯”ª§4
+L‹<.UºptåâËîlAŠü'óæ‡`¸³…^B#oveÞUù÷µ@¨¾¸¯ùþájä›sßx!^¯vX;Ò‹U¯†új«.^y¶^ãelgÞcÝD1R¢Jia>Ñ—²)^°Bu…Å '‡ œºöf†8u¡ÍfšÏœ%·I†wCXæm •9
+XUhA6²•æE'"Þ%m@Ë­OÊJ”¸‘ =2 Þ.g˜÷ȫ샄²RœÙÆÃb†÷Ko†£AÒ­1âÇ]Ó¾hW 7ÛölÍ 5S_srÄs[mäYC¹#}-Û]]BØ vhÁýªÈWHSÂCwë5 í‘µ•·µ‡Š¦kuÄ7ç¸Ò°n¤8©Ðëqí‚ú•MnTer‡º3Ù±ý
+<pêØ.W‘‚wÛqkuó±öÆAß´š¶VÑ­-ÒœYÏg¦snçM8ªØó¶ÅF6m¶ÙêvLí§\:-FsáýösÛ-¸¶Ë šöšÏÀ}@w⯲֬Lx7Ý`:Ò»7åÒ,VY÷MUvNBí#KˆX°[nW†ë¾Ê2ë“ŽóâS»º'ͽwæ(<
+0‹7x¿³ òÛ@»ð>6„ÿŸ'ÊêŠ#Aù%G8p».,À†l8qDYíï‰Å ê’w)¨x·§ÆaV°¸Ð[…Ç™,¼âÕ+ÌjSÕrðMù…A£š¸`jlVø4 }ñE¹ÄxŸ>ûølÊÎ ÅžÕd'¨ÚÝÝÀ¾ù2ÚñÆÕœ­Àÿ6
+endstream endobj 548 0 obj << /Type /Page /Parent 1647 0 R /Resources 550 0 R /Contents 551 0 R /Annots [ 549 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 549 0 obj << /Dest [ 523 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 309 577 334 591 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 550 0 obj << /ProcSet [ /PDF /Text ] /Font << /F1 1609 0 R /TT2 2027 0 R /TT4 2032 0 R /TT12 1611 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 551 0 obj << /Length 2615 /Filter /FlateDecode >> stream
+H‰œWË’Û¸Ýë+/b²ªÅ&À§jWÙ㙩d‘¤Æò&Ý^°Ùè3ÕERÖ(©üFþ!ÿ˜EÎ@Š/ÈÎTWµH^
+J–ª•DÚg” AÜC
+L‹ùù‡-˜.p>O¸óóŸÁ(ªF}kU5dú~
+•›Ü@Ñ}ó°)pæô°XÂâ7”°ö^Ò×[bÒŒ"h¢æ8`^T4çJÞ}ƒë¾¬(ý}X£º6:ƒTÞ»ÜyiÏŠËžúÈPqÐ?¡èHYW©\UéŽTÅâ>ȇJÒûþX¶ÅKÙÅíQLøô_ÅÆÉû}ÆI'·HxÓ;/%ž\²M\t^BK%¿{¨ DÝË^ºD'­t¹&-’Õ×¼ÌÌbâC
+îtÌ’P…Ž‹zÞ‰‰^º¾hZžnUÿæ±íó룄¥Ã*/=p|DJ%I6¨::¬ªE-¼ˆ‰¦@NÐœ¤¨ìÅèjÝ3½®9íŠ|§šá^š…•š¤šÙ©=±NRxaÄÐ<MTŠ*¤Ý2/žqÒœ2}T:O;Y±ÜMt™R—eAæaÑL®7Ñà—š‘2Î5ÇS²ÇΡÖäŒéh‰ŒP˜3Vò¬d_\ú¨t茋 Ê8šIËH!¿h¼aÚK# ÕlpCw”lxœÚMK´™°‡ÚÞ {8¶ìñ mÕ¡e™Þ¡ÿcbE¾RôÆèid ²Û7´v—Z‰Ô{î]ïÿiU—Ö³öž¿¼Opöù¦ã|7 ºÁ1HS|mT'¦¦’•wÉ{Â)u»Èj8†ú·ç©WpS-Ÿd-«\¾R³˜žÄ¨j™Kµ]ÿt©$»fß7žå1}ΪÀÕ|šé90Û¿ mL5¢Ôt‚Y¥s$N?LæÔý±iYó‚ÌVSYì<ÕˆR*©fs}Ïë!IÜ£®q¬*bqˆZ }Ô3ñAþ
+ãáÎC¢&l,ÆÜ 
+ÔÉHù©'Bëö¯ˆº1oj)Fƒ8œï^’wÛCŸ®:FL€'Û-ò÷Û§Þè3êË>šrŒÉ*䘚CŒNÛýjpc}ó»»ï?¼Û¾»›8U`"ˆ¯è¶ÈǺ©_*Ý
+püàËb îƒ ›“ý![óï&nŠ0^ ˆMnQDXMmeè&í’Ç™'`»~輨¯µÝʬYª?.NS—…9Âî3‹Üb…ÀžvVhPÒwSÅÁZZ2[¸Ð*MBÄS/ ¯¨±È—ÙNlp݉MF|þüöÍmÏxµ~è¥é•Ì°ÈÍv±Aƒ²ƒ¶ˆ-˜cÜ/Å·2´HjvÕùrdDÐÃo`è~x»ÎÒ"ñk÷³Mn‹Öç÷DÕSó„˜?gfAç-‹- DìEb™—E_ƒ%Eùr©
+La›Yt.¼¼ÌÆz~}Ý,2 ¹S×jÒW [äËùfã¡ÍM Ÿ:¶£»BÇ€o'È/IÔ3è«¿>³l²È-XÓ¨ïú‹íƒoâQ¿žºÆ&_Ξ(½Á ò”:ðMù2£ð(õbÿXÇÔ@íþ´É»íQ4jŸ3Ô¹uÀé!ü6ä!µ±+º-rKlðÎÒÖƃíé0IØf’¨\PÇ»‚Î"· C3a‡®ik܆̼P<ïÚý¡?77fôÒb5cEÎÃÑà0 ¿E¾Œrã_Zï„9ßôZ°,òåºE‡óg±RwSÝœŽEÙ®‹j)†Íñ~2Q4A”¿¶u–Oi1¥¦lGl/ŽŒ0SÀÕ$‚=¥Ê( ¿>ì ô @Œ®}Í® VawÂP„Ð…ÛíòæëR£7¡qb~³íöZÄÿ
+endstream endobj 552 0 obj << /Type /Page /Parent 1647 0 R /Resources 556 0 R /Contents 557 0 R /Annots [ 553 0 R 554 0 R 555 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 553 0 obj << /Dest [ 552 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 144 574 166 588 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 554 0 obj << /Dest [ 1598 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 89 532 97 546 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 555 0 obj << /Dest [ 1598 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 275 436 283 450 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 556 0 obj << /ProcSet [ /PDF /Text ] /Font << /F1 1609 0 R /F2 1607 0 R /F4 1608 0 R /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R /TT12 1611 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 557 0 obj << /Length 3292 /Filter /FlateDecode >> stream
+H‰äWÛŽÛÈE^õýØL›w9†{×ö:X8ÆŽŒ
+#ªÑO: €näA®ÕËmUw~Õ(ܦú5…ÝCþéjÍëM+¼sŠƒ;1™u|
+¤­˜Ô8+hàìÒnÔÏúp®W"ïÞ̨@¦ú'ß+
+¾}ÁIˆhdmÜ9Ì6sª,]õk6Ž¦k{‹è^yDC4ν’ ýD¼WçeÓ¬œ­ûªÃ*,\XôC4è¸`©Tßz47bÈcM‘¥“r-QÆ<_ýnYCÐÆn6å.PZ«.ß~|u¥¹÷Fj$Òç¶-.
+äWz>Ýç@á"öÂ×óíå[:E¡iÓ/åæoâãâ’)í½t´Iðä.E„A{÷ù[:Ú1ÎGtñ¥$ؽ#ö†Á_öN$EÑSÄ[ž …ú!AÉ8ãâÁì[ãÁ ã$¤uit'…–¿¥Tþî‰ô5 ƒ¬0‘P‡˜Ñ˜àhÛ55 q\Í }mkKÙHO“”Éz.‡Çé¯õ²ƒ òæ<*-Kñb'peWp0d¾ùï"æ½;S×2ã¼ÔQ¡´fé^ÒÌj4’˜\ÀâHjCÞ1Ûàøͺñ9ïµV®zP®¼'‚.8Ù¾&‚ëPjEX†6äˆ5Næ‹#NÔPô«cÂå4§f‹6ÓƒH>*Å oÿ1š¹!(÷Á¡b1vÃ[ÖDo@šÄ§†…ÿYŒ4€¡é@‘Ed # •á\ÄïHyš´²Ôk$½ÝPúý"™ŸRIs¬’LÒ©þH&RѤJ„a¤—Ì!Îùô\30d’“­ÍmEEÙx‘¶CÜI«½ÂCÉ7R£¾?äÖ®`°R×رB¡‡Å‚I.Ó¦¢Ï¼(r;rÜÚvÌåŒ9HOZêî—"w?ÿõo¯~ùáÅ%É ‰Þ7ã’Ã×ûíNÉX‰Y ÍHÛDäe0() bdU)#G]ûµ´S}ï:ùÙx4ÒÍåƽݺ5´'î¥öoàÜãì÷²…o¼Ù¯££`HŽåŠ8B^rùæGEû΀1÷ðÇü¢hƒŸ2š)ÐŒ)µ>¼ïåÈA>öAˆ¿ñÕhõTÌXn±å«q
+„HÒk )¹±^-»mÃ@ðž¯àQ>¤h)•z ÐÜš(z*|a$% É„È<ü÷!—²]'==‰»îîÌY|¼Å³µ²vnãW–9xRf2'ð׿ü”ÿøQj½«+)¼š£
+ú¯+*ÛöN‹¥á}¼Òñ5ËäOªŠ¢ï€÷üˆ,Ø@Û²¥3t^§^`»LñcTAÊÅ¿¡š'¤Š¦:ô…ó(ö·Æ©Ý4샚2É¥ êu¢»Cñ×)ÆtŠú±g˜„h¹bHi’|äPŒcU",2%¿/ï¹+ĘŽ ð¤Žuz-…ÓïãO+“N¹ç6·4 NNH:)ší²÷ØêœÅ;¤"oL€såÚéB«£À¬ÃÔá]ÿJƒ ü’X2í-r:á¢É=-دBëDlF¿h4s1éÒ@lVòïY'á¬i¡»p¹qÄ‹î@6ž½àì¡ÿ:ÕO:3ß“|ëjŸ3»ÝM"
+-SŽ²Ç´žJ¦Nëg.‚óØž6h«má{~b-®ùæÏ„Kð±9ˆÁæÔÉDK`Åφç?†Š úÑú½¢=tòC ÅÍÝÊÌf%L}óóâ·
+endstream endobj 558 0 obj << /Type /Page /Parent 1647 0 R /Resources 560 0 R /Contents 561 0 R /Annots [ 559 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 559 0 obj << /A << /URI (http://www.pcre.org/)/S /URI >> /Type /Annot /Subtype /Link /Rect [ 287 328 393 342 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 560 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R /TT12 1611 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 561 0 obj << /Length 5625 /Filter /FlateDecode >> stream
+H‰ÌWˎܸÝ×W^¤)¤%‹¤”/ìø2öÀ]F‚Ø^¨«UUšé–
+’jÊ·äòYäÞKJE=Êãd4Ð%Q$ïå}œsør½zº^K&Øz»’…ð?‘ÖA¨Ã„¥Y$:Tlý¸zú§V³MKsBÖnªÕÓ··‚íÚU„!ÎÙ¬üþñ´úÄÎ=_ßø+9S)ó¾¬ÿ¼Êh‡Œ¥a%`Eé Ú@Ò^ø'f«IõeèùB 5»ûÆn¿UÝF$/<؉·eË^¼½fÿ·çÇðÞÀ·8ˆyé…|³¿f·§²ûGÑ<Ð&ŠçÕ½çgœý“þ“ct|:š ƒ^Ö¯`Œ<8N§Á[´Z YØLðÂé6vhO;½^¯+ÙJÆI G´f‰¢ˆÁ#Íšbµ]½\ÁP¦„ãhœCbDÀËõ/˜0=MXÉ4 ÁŽÊȆ2Á'<HYuÅ®hØ›ÏO ;ï^}æ­ X×”p‡¬<Ï´3a!;*HC-јHõÜS13vÚÎK ÃzòSd"Èä’£Âú \ίY ÞT;:ñľT"ˆ§›X«‡¼ó| iè<˜Ä‹¦šx “4HC%úªK„²>ô±ZpBA éh!¶x‡ Dæyã%ÕŽ|Yj8¥àûRHEbŠ´Íßgïka;ű!k°y ›oö…f[°ñÚÔøar3Ý$v¼ÅÆLyÇj ì±9šæ¿5Pm
+V›Å¬?è
+¶¾t¹è0©»Ð®ƒNÑCõ•í+ØM20Š°;RCÕöµ?X6Ë âhš%c'Ù9ËÈqN÷°’Iã!T™çGT-Áó%Å,‚—«:µÅh|³g9"ˆ]3‹ˆR=sm(‡íaø æõÄîÊ>õ"´(È>óðù¶\K1yÛìi?û¡ƒöeƒ?PmEóÙ Lê?`ƒ'˜Púrlª–}|÷âööæí»×fÕ+fš?:‘ˆÖ³T.Éÿ!Õ¾
+ƒ4:gÕ?Ó…ËUݱm}¬î¯Y aoNe[
+óŽÎqWìʪ‚2ûìy@Nä,¾û2öÅÈ¥ÙÙt虘 úËŽo<¤·L9À;U)b|Gu“
+îbŽî2¸d¼v\ËY_Ëð˜ÙŒ)½äŒ¤•"Ó=Ŭþð|ŠÏ?]8‘“E
+eÜ{UõeØþÅŽ:§ÀÞ×¼YtNÉào¬‹$ð=
+ˆR”Z {¥ÖKª¯ð…W`F-ËHCÔˆwùæד½‡:H¹ý°õPè§Ü|µ#%n«ì˜™ Ü@¬€#>9ï(Óϲ/ô9Mý0ƒ¨ V.ê`…haÛÞ²H¢ÎÝÖKˆÈ¶æ
+1à½ð~†ôUk@ë÷a>
+RñÿóéæSæA]ñÔÏA>F^€ü@Âö< MY—œ”ñíÇ—†Îo×(ã4’<4˜¶­>^€°&øÎC]²G8_{ØCÑ€L2Z›®tõz˜.: B"ä8»ƒ²›t&¸¸‘ÄŒ°ð RŽÙîåH{¼k ð7GZÊ= O¼,°~O
+ë|Ñ9Ò¹O×T1Ø?aN‹j)…BM–6u;h[><Í8<*SóðàBÕ+8•X÷ü åÙŸOž—®Xö:U+ªÄ’4YE³vÞ…lFI|!-ò¬>y}€øü4H§ÅuÊ4þst(æOž|ùb$×@}n}aEÇ3f˜Xb' tNlöBcKÇ«Iü›r}ôà‚Û!¨¥†_¡ïúR6Ú^lJ,/³C鈻ge5§`9¢`©gåívä F Z ¿Øæ^Lˆ+ é-E§èXê!RÒeACV^FyK1«'˜Þèöôe³Tâ`Ö¸v^D—Ém#\­,›ªV¡w’Ál1&WïÌXAhB $Mm·DÎìæŸ3:FjŸ;T¬‘‰Nݯ¶³In„>ío¬Ø燐‚{D6á*Ót³½L$Ž¸ƒø
+“”¢ûäˆ$€ ŒÊt~±¼+X»¯OÕ5"ŽáJtˆ”>8w…o¤öáÒ Šº1Ê.8æ;í¶Ã.Fÿ6° ¶ûkÙíÍ
+h„i঻rÙEëú€Z#]Bj5ãËë‹|9 VØ#VTP#ÖÙ–å \C›b‹ ’á+4ÀÝ7Šxήü+ÂmÐ!"º{RÓwª+Ðhû‡‰
+[·›¼+ª‹”³%m„ØDp—DLúî?¬WÛnÛH}ÏW4öaÐ$™÷Ë
+¼‰%S+”˜‡À2ž
+Ö¾ŸÄJ.­q
+`coØ£½”6î0ZÕG
+A/gÜq\†¼ðüö·*¯î‹*_JT;Zê3cR_‡º>ˆþ¸«Ë²>‘ð¢ Èáñe_²¢dðõm™¨szpE¿Æ̬¢™‘!8+ê=aê¡+jÈy½XsMeá^ˆšz C
+@¦9%ÔÕæHrQPçzº{ËÌ¿ÈQ͇qÀÍ”5É¿¹ñúLõ ËðÍMlHÈD®ìq©ÌÆD„©6ïÄKšÅèu"Ƴ~ƒùbb=Ò^¡^‰p ¨êçµ÷ûÚu7ê7ª{]3o×þÔ#´f±/ñ}/ç¿ÑÅòqÙ,¿.ÿ´í™ïnkõª¤¢°(ŸLQOç_;!7¦«á ’šÚ[½¿+ù¶-E6œ {X„tX‡Ü­ÚåeñX ]ªLD)}Âêìê¼á¶lZЇ¢—kgýØ[»Žm»8Þ—yÖà¾cØîó*?d%Ö%ëèK¡&
+ÈZNßm}ìç˜îsÛpÑɵ¢’•(ôšKÝѽ
+õÇRq&OnŸ'Ûѧšˆ)Öÿp•ÓÉu.àËQð .±ü$NDþ¦ÓærµV×üù° &Êl-’Θ ^ Š:vîCgÀið0•=Ã/üvt/[‡5.×)í[Çx†÷LÁ¿Ñ<$úr£NÅ‚rζ%Ò²€5<RáСP5§ã|pZ«²Öî<WͶzáÆë$ à¡m÷¿^@Àê‹Óé´ÞoÅ}†z]ËÕ½€Â/»¹†nEL«|ý)× p©p°ÍôâîÅ?¯|â§ÈZBñIç{Da›Ã±q@þ€IDî ™ø7K›F}rfƒ¨gcÆ\þF£ÄÜc. r|½Ž~m>?ÐÝH¦4»ú_oȨ¸úÝï7(ïŠÎØ;¯<¡ÚRa¾C§²óP¿7´Q¬·úuŒDÛD³K$öŽ‘ÐKájTÄÎs‚Øwv2·¦w&ÛÈ]&;<LLçÑùÄ›7»RÐeÙHOõqÉ-(‡½ÏÙ+F€ŒÙSˆúqæ&6-z.B\ 
+òPGa°9“:Ðj¡tÓ'èè3ÆuÈ°tCàÇVîG¾•Ñ ô†‹Çû“–ØÒð'L¬ÀP¨
+̺übn ¾ØÜjD_ÉáWì[<’³fS×M̦õv ¹åò_Ê Ž\msÒç4|’»sÎAź®§+Ø?"0„ò‘}õç´L EïY‘Ž·˜ ý—Dk;|¸'äúIJ`Ó ïjnA:7™•–Æ
+Óao¸aøÕ5ÔLѽh·UyóVŠšŠÀr²•?”ÔIÏ¡¦n(ÛKöo('à6WÒ×®W IòÙcYÁ:¹l8!ÚÏüAEêŸ s¬ºÜ¼Þ|øüÇ«E¬¯û7ŠŽ…5‹
+i¹m«¥ñ¹,hÐ\Ÿ dϹI‘Ösóh¤b'±ºÉ¤=¨dIØ:νš }ú&A"Áš¢ÜˆCæÉ#’µ?ç[äA ò-úH0ûóÇóº½3¾'=léwúñ“‘L‘œüÒe£ßõA„h¹SþG;2"W>.¨>bv0Q0ZDôÑ”ìü
+oðï#pü̵¶,D£à›%A)'ó¡JŠ7žµc“{QpSû –ËÆk¨)LÛ«ÝŽŸÈÊr‘ê§%½ÆÊk’8ý9T:Qßiv¸Æ$6HŸk9,L¬4B91‡ìI ï"]”¥ÚÖbÂ"Qâ :F^Y Dßꬥ"›¶8Ð!eÖ)²A"Ì¢Ù£h­ÊËœ<ßRñ“dñ°ž¯wjonÌð¨‹TÔ5bSüV<ÞîíLáà ›çÆÈ”íûÆõ\ xŽÛ L–ãm“ÿ÷ˆó(ª_ KH´ ±Y«7w*CÓU+É87Ü>kx4­· õãõ6Z!L•µêKv(²Û2—¶ïÑ*0‰ùÐ:ˆÄpY&|+ .C}_!ø¼`Ñ
+šâ¸¤-Y:bü%Ô]ÇÕ,&ÍÛhˆMbpŒ©èÆè‡0u(6Èe}«;zW£/ªÚÀz³Ï·ÅÝ“ôQÛ’Kcpoªf ÚC3Ò¸4MÀ,Â=Íhì’ÝS7Çš›26-:{Úä̸}/¸½`mªø˜öt(y™g¬®r{T,ÉrQ–íÔ"Ú!éÕ"/i¤‘üP,ªK:”§7D¡~m>?ÐÝPçê«÷o)…±~ƒ}}C¢/$÷È…g,˜U¾#ÀE÷GÎ)ÈæÁ™]"±Ç±ÚÚ(ª}µèt1‹äÍlXî(,RFêíADm}@Æ¥·¸”ç±ú)Ž;Ž•tƒa€ÜQË/óÚñÙ¤ œŽAÛæ\ËI(z{z²Øf‹¾ñ}\ªÛº^ujkDn¿ÚNÜÑX*†è`ÞÑII|ÉG@æ¾}Úp¸”¹Àðp3.´ïAÌ-pèúÓT5Æu,À…ihUãÕ¾?Í PßÏaA§ú0YµÁ%ìNÒÃë„WAHPùó’[!0¬ÂOª¬"tí_ªå-Æ_#q5yÊ2gŒµá:zöXÔ34·¾æíÆgŠÎÕå÷´ÀÊ
+¥kœóIjPÙO j^ƒ"ý° ÷–3ADD¬ÂCöšŽxÍB~3ìùÔI˜v(Óca`µöܳ1õ±LÎ÷T±H ‚ÍZ³ò—+ÆJ~J®«QÂ΄ 3·vëP…üHDåX‰n¨¸œ„ ¿cMÄ4”ëw¹5³
+ÁÁ-qǽ0MÏ:Š‡¡§Ã¢sÉSJMÀYQž6œüÒþš£8‘žv
+é¦~¦3F
+m2+P×ùRupÆ3
+3 D05©|R,3ÖùZµ§ÚŒ
+ctJ`È!ŠÓ^›!ëäÛqA¢t»ÍåÒl##YƒH¡4¡›oóÑ æ;¸ÀœÌ¥eVþ´Ì@ÙGr#•åe·K¨üŒùìt‚k©¥n;kC®„aw˜×X›³-\צ,ÛPÜWtĵœnsýâÿ
+endstream endobj 562 0 obj << /Type /Page /Parent 1647 0 R /Resources 564 0 R /Contents 565 0 R /Annots [ 563 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 563 0 obj << /Dest [ 779 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 327 377 357 391 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 564 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R /TT12 1611 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 565 0 obj << /Length 4253 /Filter /FlateDecode >> stream
+H‰ŒWÛŽãÆ}Ÿ¯hì‹›€Äåý²vwÇÎ^ gí1Õ3¢!‘IÍdü!ù‡ücrªªIQ— »±Éîªê:U§ªÞ-¯^/—òÕòþÊ”‡ø‰²Ìõ2/Qi»Iæ…j¹½zý¾ËTÙñOue}õú»_=tWžëy´§¼šOW¿é gº‘~0ôhfÊù}ùÃUÎr•znž@K˜¹‰'JX@À²è)NDÔ¼ž3÷C7Ñ^¦îžÕÍsݯñ&ÐÆ$ÝUzûÝLýú_gcÝâ[ìƺr<]®gêæ©êÿ4톅„º¨WÎ<×êßü— ãëóÕÏM°X^ã[ðôôä:¡ë뎴R ÑPéâÑÃíJûjÍ’Ë+_Uê*ˆ7L|¦n–©Äw£HáŽQ¦Zsuõn9:#ô±Å;öÆÁÕyV.ÿ À²SÀO¦‰=ö0A‘ˆ'ý,ä]ß:n_9¾›éúAý´ønñþ´øщñúƒ3pÉ·N¾wæn¶¸Õ8’œ囲¡›zY@–Qîú§¦HXDl‹5ƒB"×û»?LÙäðe‚ ¨E ×É=3ÕõÎ<ƒÉmm_±+ 27½(ÔŸ˜µ+úÞ nR\³>1+L¢s'‹YÙhVv0«­j²,шÎT_´*òhÑ)p"£5» ÇV¬‹ÒlM}ꧨ'Ù%ƒ‚`0ˆžH˜úm¦*––ãbžî9'c››1åJç]4ÒRŠHè‰à—à]6O‡ÀBpdºhDÛGÄI¨O­öc t*M¬¶èz~ŒVsV…ZUµ˜œ[“319¿lràû«S%^0ÚL6EO95 ¾(´ÁwrÙ˜/û›þýwÇ÷ô­Ãj'ä ´„1²!=å©\åú‰R+Õ– w€Ú©mÑ—kü6÷jè[‰t"18
+Säg¤)Vë;à¬pr*í°ù¯@‚ëª>ÓÀ_rn>¹€…v'´ðЀ燔jõã‰ÖtÔN´^ЇSþTeëPT'€:v?mÉw©}]“1±îÝ[¢‘·Æœ‡¡ ›°t\OÓÞõBÿP­v*p¡U¦NFéz»nâø¾SÃn‡ôìjTâ38ê «í%쎘œ‚=Æ+)‚LNŒ*Ü æÑëlžé;*„Áx/Ã]€-a»Õ+s_ì7¸&Uõ­“Òwë(*Ÿ>[ÙÔ}UïMÑWÀíùAK˜ÁL…Ú8dåN~ŠS#‘Éñ42£!¸¼!¤ûÊ!žÝ" HNX=g)s°>âšÆ8‹FçúVÂ=ø
+šÉM¡ÆmÕ·Pø´®ÊµÚ\9G³Ð!å|­ŠÍF4e¹§rHmFÄwÀa_×Hj»Ñ¾WGßË—èÒ¬n×áÎaôXd/¡&À
+Ÿ¤È€äBM<øêq.m^½a¨xµ¬¶Fá1Ö÷ûºì«¦îFå_í)0"jÔ¤`æVcDO¤±‡ÜŽb œ„b—èb»SŸ>ÿ"}Úºúe¢ôS`ƒ!#½`àÌÖô{[[ØŠÆKÁí-j¡"L›ø¬©ÌÙÛh
+ËTgÚGÓRÖü¼|O©škWøB}jà’~]H5é¹H±äÕðŠr[Û
+<ûˆ‹Ø¾õábÚjØaOLuOa'½IIó£ ‘•øÉ Î>ãœ3Î×ïÈêH"{3ý™.ìkÆ>ì£sìs7ö/”©x(SoWÂÀsk
+|úâ®…JûÆ~²AAäZÀ`èùë{wļW%˜ü/g‡Tp¸9Ç;ò îä ÿ@¶d<U ƒ²ØwL¶†y¡3°^$Œ‚ÆY¾ÓüX”%S€]ËGù;#ßrY5ÉP£øª—Ð)‹—iTñØT+µ3í}Ón 0Z™‡¶€þŠîâë¦~ÎC³âG2¹ Ì=a{óåf¹øˆÀ¤<®X(þ´zÛzê‡Y>f/ öžý^–5ßJî4$48Ú}Hp'dH±ñFnõlw”ùÀÙ0ø8%=J YQ<Ù÷<õ}ïHfû5J" u£ãºá1íÁø,·øoD“Í'Š°7NNšP86f0ÒÜ †‰ÞÄ…’Õºi%Iúrß«û†úYnkùÔ$±Ïš– °¼§Í)rŽ8æÓßÂè4½\~þU–öëb —O§BÎÂ5³Wòé^Ñ_&ÏK{æã+é~°•œØTš«¶» 'CCÄE…éÕ— Äësžš2~šÛ¹iÕ0}®‹GK©650ÛïvMÛ+¤/èªïÊN”sy
+bdKOœÛ|OÐIÏÉí›XÏ.‚|zËëì´3Ò4Æv³j$)Ñõÿ9ÞµÛ#Vpa¤Êi?·[{ˆ+ƒ<ïâKŽd›'†œ…ÝáMqïšÍž¢‡Ý!6ÆìP*fªk¸Ý×¥ žýžWyÜ:Jÿªº¿7íËŠëPñüIieν~»\|þôAÒóË´‡ïçÝÌC…±OÛà >L}w2ë&è
+““S27²=$>aþàAéÕÚÚ‚#6tUu*;.´@*_˜wÑ¿¥“ÑmjòÔŽx2±øÙЉf¶eTµßàªÃ°š#îCTBð.'3Fš§5M3¾®
+ÏÒ®,í˜ïN*ÿ˜¿ÑД 2¨Ûq¤3>\Ž%lî7 hˆ:Œ1®Ú…òÞxôßv¬5;L‚Ô_›'ƒvuÆV÷fÎæ^bL~ÂÌR‰\j] S‡’8f™’;4"%3ÎÆáåƒÝf§ïr¾B˜7Ê_ûýxÄé+*ÐÈùØl¡[úÄ%ÒÎìUY÷VñÊžnØP‘¡Ì¿æ—ˆÚ¨.É,ÕTu „EGS±-ÔõÊÈZ
+rœú(“9GÏ Bb<* h†/†Œš_dîZ,þ&S*ÁC=ÙÛšiÎ8;¡QÇÛs£ýU¤Â K>ÇC⽩ÿ‘^-»mãPt߯ðR´†D=L ÐÅ`Z`´é"΢h»PšÈ†-£ÍßÏ9÷’z:­3³²õ Eòž{i¾´v2êw•BÎdv5Œ.¬Ä&&úhÔªêûm)Æþ‰y %îo˧is<ÉÎ]q8HææiVçÎÞ¼ Ð
+´®.‘« €…ýúöpl¸œ§ýZîx)±ØîoCdüöqËñ‹år) 2«9Ä…G žâtc^-ÕÜå0œC3q‡M¥+Øú„ÙÒˆt\¾ƒ ßØ
+å¡k†;¶ žJf:65,Ëãøèkàl÷åì–oVÔ5Œæ¢ãH–Á&CiŽ»µÜÛŦl€ƒEÙlaù÷òIèmµ«´ßàÜwe{‡âõ]yS?Ôm]é·¨Öd~ߥ d½è`ÎLçe<šô™;y*œKµ‡’Ü] ?Ó ¥MÇ .k4·x‡ñÛ[ Úî‡ãM»/7´Dwû-sD2ŒºSÆ.mxÛA
+ku(Wz÷+Gi¥Ÿ‡slՌęØ[ÅdOÂfØ©™ÁŽÒé°°{÷ìî̈csBuÜÃká¸÷}S>ôp–wã)€_GÅÒ®ÆN²#¼p$ànÈA]èÉäcEóY@6ܽÀØÆJ,#µþãÕКŶpÀÅG
+céÇšš–“žhY¢C1mÝ;@»ÕWÄ K­8—_7°?9*ÅY !Ó&`Õa?T^Ùº©õý£è Bn
+Aö†|Yïo¥B:ÙÉFý_ýÑ·‡éLàÄæhS|âŽÑÒ YðöÌJ{ÆJwã
+endstream endobj 566 0 obj << /Type /Page /Parent 1647 0 R /Resources 573 0 R /Contents 574 0 R /Annots [ 567 0 R 568 0 R 569 0 R 570 0 R 571 0 R 572 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 567 0 obj << /Dest [ 779 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 268 587 298 601 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 568 0 obj << /Dest [ 888 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 506 457 527 471 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 569 0 obj << /Dest [ 888 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 308 355 331 369 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 570 0 obj << /Dest [ 888 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 216 280 238 294 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 571 0 obj << /Dest [ 888 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 424 204 446 218 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 572 0 obj << /Dest [ 475 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 356 115 372 129 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 573 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 2027 0 R /TT8 1605 0 R /TT12 1611 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 574 0 obj << /Length 5638 /Filter /FlateDecode >> stream
+H‰¬WÙŽÜÆ}ï¯(è!(MŠÅB`, Œ$ŽHí‡Ä2 »f†R79æ¢ÑøCòùÇ<äÜ[Å¥»)ÙNAÓ,²–»œ{v›ç»] ”ØÝnT |üÃO”ežŸù‰HóØK2?»ãæù«.eÇs|Ñ•õæù×o•¸ë6¾çû4§Ü¸ãããæ{ù·ÂqC/’wš~)Â\8?ìþ´Éy‡\¤¾—'8%̼Ä7‡ðïEOqb¶úW€·nà;®
+½Dú™¸yoŸêþo©ì$»ªW_oÅ?þí¸1Æ-¾Å^,+Ç—åýV¼}¬úŸu{àMBYÔ{ÇÍ¥ø'ÿeÃØ}v-ð½ƒÝk¼c ='ô”ìèTMÇbkéáчw¥}uÏ;]ï6JTbĉ&J„©—e"Q^ øe¢Õ›ÛÍW»)¡Âÿ4s¨}Š¬Ü½_¤I)MEšßóaSs2”MFç$²w/“Åñ¢oDy¯Ë¢ºu"8&à}Ð0í¡Å«LšqÝ‹¢Åáà"
+
+®Ô9TI@Å;¹R)'þ„ ä6Á¶6”txâÎ1ô“ùæ+ûp
+ìÄ‹óÙ Ú.¼t¼—”î`4 åÁ ÐôèN;Šf¼sœP.‡‚ˆÄ…D’b¹âà¨17.hœá'²øI3+>¾Ûmh&$üL4·”¢w›Ô ƒÕT›Ð&4‰ MÊ¡áTdrŽPð™%‹“ÑÚ|Ì÷KÑ·ƒ~çˆÇ
+9½!pÃB æ£n{Ó$b„P¢É%ñ3¦d²©5f»&0PHypÚT:‹
+Û¨åÇð'@ c.K—©›ÑL :ŸÀ0C#Ê䓸ACãOu[þ ¥F¹ì¸º½©~2ã8x7e›Ò|´)»T’X“þrõçë+PK"¿ùæµ£èø«¿#=ȲúŠ‹ùèh:¿,hüàPË¿ãsYf!5~FBK
+Ú‚)½ÃRôæ ¹â3*kf_®§àJ³‚XÁÎAÑÕË îy…X´xV æ…Ùo_˜ÓL,Ÿ±§˜C#•Ë™€ûHdÊ Ó3"‰Gµ„õ3¦Hîâ­Lk€æØìG}&qSv††Z Ò¿^ôΡÎJFâêRâÅX™µ–É6ª‹vü\üÝEÓÑ“mì¢9V=q:t/º;Áð÷/}#ÁöÕí­nu]jÛëPX*>íuÁ²-ÝèþQ“§Ž}©]¼8=GÝÈ*'²4\ÀépðËzûó¥å <r@6¶n;ß›rÂL€ò%%—ƒüIýTµíæ/£Lù3ä)w_±7â•/VÐÌf¶Ét$휋\ãí"Raz‘ëà ¹ö­ùœc“X{Ñúƒ¿å[-›ÌJÙ\"G=ÒÉÏÐNA1ü@WQM
+üŒ#áð<ÃGì‰È”\!‰t%ßÙ˜ïŠêʹº«›Vï·œ{ç@0ØŠóøaÏì¿®ö8™<7R€â ë»þž.€<,‰£”ÔU‘yG<¢da>Œ—Ö +Qôáÿƒ¿MzHì°ò‡¼=oIVV{ŠŸhÿ¿Ö&õÛQ¤ã4`š´Óx]âWâIаæRHzÂåì÷3\ñÄ`%Ë‚5¦<™ =š¡7GÀºÀé³»¸u§Ú²×FnoE«Å±Ø£3Ø›…u¶@ÒçÅ… NÉí–8EQ1ãVÊ'ðøA¼¹~õÝ›7×ßòð•©õë__í®ÁØ‘í˜G¤FU­ë•yi!ƒ´Ã„ÄÄoÔya¤Œ”Yîà.ùI…*•^×·FøCìß­[”Íê~Kún©1äHžüxÛêŸÎì"‰•Ÿïc"ŒÆ°%8À½¾Óë/J¢ËmŒ9KYa£b
+Æ»ÀÝmÝEj›eNæDM”“L”CR
+䈾ÀM“Æ—Z~©Ô’(ÊÆ¡ì’oçM º­JF½B©·*¤à Ë(E
+aÞj¤‰ìÐP©¼–n»9Kr%VÚxè
+ Û/+–øòeÌ1  ùȧŽàÌEï¯a`2K˜¾¥Ë‘’ƒÞ®Ð «ºå7„qºñBÙçñÐÙNÊÖ”ø-ÌÅì@ÏúÿV40­}¬:ž·f&
+i)M~µ…Á¥P ŸM&¢#ë¾²ùæ£Ñ+4Îv8èÿÐ^n=nãH~ß_!`€¤ ‘ºç-Èö‚Ífîô¾L€…Û­î1ⶽ²žüû=UEÒ%§Ý‹Ù˺•Šdñœ¯X …àÛÊÒ,^íHÁXaEÊØñjSDî%ë”–€YùWh&z{²¶Gf‹.alDÉš‹oïI¥J5üŒ}kCn\‚/ÚÓwú&˜]ÌéÍÆnÁoµúx-O%? "ø|vöhç„Âøí=H
+)ÜA^m.È=~·uyÑ?Ï;V!b:†Õ€±Î¡*¨¨ N‰SïñT­[ÊoêU7ÅE¨Š
+ŸŽ¶Øu´®j/f0V w“MñXU\¬Ù™ù¸Xó¶„Ï‹öóâê{^i'µ‚`W“Éä)QYB]ztš%ãRÜôèz<® xV[ ~›H•Që™±jWg–xÙo mŒE1û²‘ñd‘«1Á©éC—¥¾u›±Çô2¹
+L~ÕÊbÀ\Õ›`!ä¾½q8Êé†Á¡»÷±èyÉÙ*|‡æˆC1¿’“î–<÷,'6hò`çc=‡Cµà<MŠ&àS_?cJe%¥ªjµp(¹+Æž¬í= ¨…ÏuçžGHÁÌáûÝæÞ^èC€;µk•ç}kòDsªBÙÃ-±e äL~ÚÐ@(Û7µ0a
+bŽ #NÜq½ý'úà°zõ7¤‡k´Æ¦áQ±ˆãÿÆ`<Â’ ?Y¾#ËVž"›¹ ƒØ" GÑ8 nŒGÈ{aȉ=¯G¸íÝì²Ï•þs¥É>¥^vˆÄ,álÏÁ ;)Yã^²Ù&tÔ,¥*(¹îxÏRÃ"{ié
+'Ž8%m/€ê¬~! ‹\I±}ÉšŒ +ŸGuÂhÂ@VM³sÞÔððÉ€¡iR$Ž“+fèîMyfvµñYŸW v‚²:…ô5éÙùé–Xçi¹Ìri_Fï»n.k³<Ȩe+nÕ•8&É`¤‚Vx‘ÏÒµ.í
+›¶¢FÎ÷o’÷©› À¾©‡¯ :,ÏZ—ª'Xª‘úL±—¶HF´Šš!ƒ¦O›8û—±Õ*
+lk•ómò¾ï g{xë\ÖÈàÒ …>ôÛ'ËOЕÉ“»õvù•”xñÔÙåA!–ÕPÔ|¥UM#)Z˜#Â!È‚
+W½2)âøÌ‚)ÅH+bÇ4Ç‚Á9·wsÚ°@ˆf©R×ȉcìþØuKÀ vó¸aBU°xS|1Xš°¸ Ÿª7'ÂÇþ_×4
+}òtÜ@ã¾* +pœþ@˜³Ûb›'Vlúnyì™~˜ÙíeŠØŸ' ¢­5Ôt¯\üëÔmýwŒñŽ›%)Hï¹ “ÕÃh'æ¾·ûWýÊýå9Xnë{1/à1%Ý].;9•ßT¦QÇõú;öo“ÖÞ@—’ÈM®‹B­íu)&~ò™îhe ¤÷ð×DM«]hë©à>ˆnÜÐf,Õüú#ÙG©Þ‰r|¶·ÿ%;÷š¶V®¾(&¹~ØN’»W$ @™›N%§û
+endstream endobj 575 0 obj << /Type /Page /Parent 1647 0 R /Resources 584 0 R /Contents 585 0 R /Annots [ 576 0 R 577 0 R 578 0 R 579 0 R 580 0 R 581 0 R 582 0 R 583 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 576 0 obj << /Dest [ 475 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 356 743 372 757 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 577 0 obj << /Dest [ 475 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 509 694 524 708 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 578 0 obj << /Dest [ 475 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 509 611 524 625 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 579 0 obj << /Dest [ 475 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 481 562 497 576 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 580 0 obj << /Dest [ 475 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 509 514 524 528 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 581 0 obj << /Dest [ 475 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 107 313 124 327 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 582 0 obj << /Dest [ 1573 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 476 255 490 269 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 583 0 obj << /Dest [ 1573 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 320 184 334 198 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 584 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 1607 0 R /F4 1608 0 R /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R /TT12 1611 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 585 0 obj << /Length 3251 /Filter /FlateDecode >> stream
+H‰ìWÝnÛÊFoõ _ËÂb¸ügîœD6\øç bRœÔ½ åµÍV" ‘²á<Hß¡ïx.:?»$%ˉÑ$wE‘\ïÎÎ~óÍ7³ïòÉ›<÷…ùíDùƒðÓÔõR/I¹qê"_MÞ¼oS±hiŽ'ÚE5ys2Wâ®x®çáœÅdj_'—¿Î4pCy§ñéKzÂùGþ×IF2‘xnÃ.AêÆoB|²…oQ̦þãÃèÔ÷œ©
+ÜXz©¸~󧪻‡_j,ɶlÅÑÉ¡øò‡3à{ ‹ÜH–Ž'÷‡bþXv_õzIFYT7Î4“âßôKŽÑñéh¾çÆð‘€1òàññÑuWÉwÕ¸-˜†-]xõàt 3tO–fùD‰RLü(vƒX‰ qÓTÄÊ Cg S±Ö“ÛÉ»¼#P0ÅÛFc€ÚCDÀËüŸ°t7`É$ö`³C‘0’¾—™ ÌgùÇٙˣüÔ‰ÝD~v¦¾›ÊÙ•tBøìÊ<C€ÜŒeÛøÉFü™F{nâ¥>nìû™«ü9ˆm»Ê®EÇw}‚c×_:â•cÎhHé¹AdázJ3ä[B è¸qÖOò<Õ:ãC_ÔbYWwz-0X²E~ørƒGôeÓÔëN߈²‚s©žlYAÄÔÈtÖ›VlÚŸZ’DnŠå89ËéëØAæŸ]Ê'WxªiÃL³©¯Üx \<χ­HyT³®ÊÝ
+ ›h‹•fço7Õ¢+ëªX–ÝÓ¡¸Þt¢Yê¢ÕÀ¨âFD®Åu £¸ìNWz],Åâ¾€ã·äC(ñ¤–©q)ÿ y¡Â>‹ñ•ˆ_< ,ŽýãCf«†?E±vd6ç'’IëüÅ“t5À+xÌ…Ïå½BO>â’d—A ’„êÀv
+IB°"k’’ ünˆ¯âìòýÑÙé—™Ù™ÊGóOù{à8¤ã> î– <[ÙÇùÀó܈8
+Wú‘ˆƒ<¬ÍrJ^h 7æËìVueu×Ó6‚ˆ“âmÅ•4s4æ
+@OÅÀêÿ“C$WÅ¿ ËNÔ×m½ÔvwtxmƒcàÇ&JbWn¦àåäTGá÷
+å£BE¬O>éä3Ôr(1ŸAå”$.ê $#Á>{‹z¦Xàb?}}QGYôÓU¯8ÿ‹¶õ-ø–¾½ lÉió³Ðòp[ÛÊÎQX¾ž ÝTO3h%Õv­VÆ’R©Mnâ®Q£Œe -nib­³*ìªìI#u`bmäì6æËl¶­r©œ²Å ³Ô½Ô ¥þÇÄͳRe~ßU~¹¼ ’9»<v°ëæß9•V(’?Öá„^r[©‡Ó÷÷˜aŠ—XD©÷úzëG ™á¾Uʶ±"JÉCàO§©=´&ÆÕ>àdxfÇÀÀ|¥‹Hí`GYéúö3Dì»áHœ&Q„ýý¶½K“€'õ—/>ܾ|¡»Jüù†~i´kê­~7‘›)çG
+2Ó³ÙÞ•ˆ2#¥}ÞàîïmÃЖñ]rhs¸xHÓë:XröÔpô_ ­iÆ1Ôi›cTzÛS³>´¾\´}¤ì¢™Å}iÖ<™w[]ãV÷{½ît\€½À0ÚR¤ íGs#÷”Õ
+•Ð«¦{Ïṙü[n€‰k¾²º'òZKHLU6£êœHžQrv'
+\wpL¡3'„‹œ¨å´ä”®™'¨ï8€ Ó ÿjÜ:t6mÆxÂö"Ñ È¿ °2V JP„öµ#{¸L¼’¥ I¶¬‹¡tEoEW·f¸Jþ"ZªA T´EË–.ŒP•
+…©V¬ëTcj­°Zcþ=Øìõªè~u†BDe•àC2EÄ:Ò!DU±2A <Ù´
+|²kÜðÃå"7üj«á_q÷ ½>,§f¿[cÌŸuúYüÍ0é¢ÂüÐŒ5u.)ô ÊàRF»íónXÙOÊÒñ9ÿ[{ë4 ÄЯȘ’Kš\º¢ L¦.ÐÒŠÈ _Ÿí»kBZ±tê)Í9¶Ÿýü|L(ØU™?œ‰uŠÚI:É7^¥Å•§”#y‰.JÔ@O±Ð dë„3^H5ÛA0ê÷‰ø¼âµS²ò¾¿´‹mp±U±36âŽTœ?H]CÒÜ?8l·½žeƒzëN;
+d.FôOŽrÿ¥7ž’ûµoÒkÔp=eîÌÇûA[š©¶¤h­æ/UML?hÚ¿LîãÁíÇWç ;D\|ZB†
+ò?O(¼Ü<&1–ŒWyy™- ªæ5ŠÃÐýÇ"ró%Êa£ÌmÖš‘ Y~Ö³a- Ögàr¸I8Ufo¼Í¨Ðö¢×u‡ÇýÆç’†yùKmÄ\Eü°YDü6Œü¨bm1ö2ÍF@ÏË6® vbÛ4*­{§Ê>îg%ó/ŠjÇ[Wò ŠDì:Õ]B4/e“.z»”Ûȳw¶3jUfEMˆXƒßjQO‘4 Ec†÷N¹˜âëyg)#GH¦˜A†1n8 iÓž¡Ã‰
+endstream endobj 586 0 obj << /Type /Page /Parent 1648 0 R /Resources 594 0 R /Contents 595 0 R /Annots [ 587 0 R 588 0 R 589 0 R 590 0 R 591 0 R 592 0 R 593 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 587 0 obj << /Dest [ 475 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 448 611 463 625 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 588 0 obj << /Dest [ 475 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 241 549 256 563 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 589 0 obj << /Dest [ 938 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 231 535 258 549 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 590 0 obj << /Dest [ 685 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 464 232 494 246 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 591 0 obj << /Dest [ 523 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 326 184 350 198 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 592 0 obj << /Dest [ 685 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 417 157 448 171 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 593 0 obj << /Dest [ 875 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 436 143 472 157 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 594 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R /TT12 1611 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 595 0 obj << /Length 4272 /Filter /FlateDecode >> stream
+H‰œWÝnÛȽ÷S rQ ‹á ÿÛElê^lâb­hâ½ %Êb+‘†HÅudß¡ïØ‹žïû†¢H)H°ÈÎÌ÷{ΙŸç/çs«Œš¯.ŒUþðe™dA¢Ò<ö“,Õ|{ñòu›©EËߪ]Ô/ßÞõÐ^~Ð7‹‹YÿøtñIÿ½ðf¡釒~­V‘QÞïó_.rÞ!Wiàç N 3? äÞÀò^ô'²Õ-Fg6ðf&ôdêþYÝ>×Ý#V—vÒmÕªWo/ÕÇÿy³ï;ÌÅ~¬+/Ћõ¥º}ªº/ånÛ„º¨—Þ,×êþφ±ûìš ü/ó¿aŒ-xzzò½Ð7º¥SK:[ãH¼[¸¡5ït5¿0ªR6Nü01*Lý,S‰ñ£HÁÇ(S»òbuñóüŒÐà“`!ÔEVÎÿE ˦ ë#™&Îq‹?K$Íbñãöj>¿~wå™@¼yïÅ°ü
+ñ¹Ó…ªó"]mñ"¦³ÜOuëÍ,ˆq¬·¼â‘Hµú»ÊVà=È,™bØj`l §Õ$l Ù¡‹®Ú–äÍàƒMB?ž:!e!=‘—ª«(mÛòKCvZ]—íc¹Pç,
+£˜ö;gÑ`З¦žØ¦Á©#’‡;Ï%ÂuNà‡±«–csmž‰¹VmÙµ
+u£ÈoE‡©f%ÖbãöÉ€a“ÌûDCÎPÕ[j&ƒô¸µ}óú™¬$ÇL;"z¢õªkä¸Yhüܨ™A8'õ™³˜Lm=ê¢GÏ ïå¢ZQmxTêå’=ðÕû¦ƒGë¢c×6͢؈ƒŸ‹Í¾TTd -;qÔ?ÌOm|¦2F¾™£°Dc:¨eS¶^‚¨¨ºéÔb]Ô¥ºÓOëj±öŒ¡Zß-B—¾Ku¹AÔ\lK WDEåÍÈ_Êœé<Bκ÷{÷E³ÙS(*/£&¡
+™oüP¶êžÇŸ1./ñN˜5£°¯V宬\,4Ùl–Õñ6KUó&å“Dßa¹iE™kýÎó'h‚SK‹Òü›?‚Ô¸ØTuW>”;uMë[²Óê7¿z³¿7¯hØê¹›~ÿö!C£Â¬¶+¶gÔF)ŽF§%yægÑ1F×NðÐe“UßêOÉxæ¼ÊWîwû]Lƒ5RÀŸ+ß;mî<~ªNÛV›IŒÍ<>;ŽŽÏ6}Dé‰#Ú*&JÐjÓðc¦»ª~ d¸Q³ÏˆöA™Ý2ß=¢Ä+mèF9ù3)t“ûa4nò :bS%#Ô2EÛ6‹ªèÐ٠ɵ*œúf¡ˆï~œæ‡/ô¯7¯æ×\
+d<©4¤ÓæGx}'^r³”ÃpÉiœ…ÖnN °\a¯Ý^¼qé{qç]*H%4c'ýÀ50YU¯šÝ†4µZíší)ÜCþÙ0ŸmKm6ä¿Þ·Ämñoê”éÀžøè…ƒ‘‚‘¸‘gGÖó ®žê^êâ¾]áßéI—‹úùC¢öD;Œzgeš”å@_i¾#öŽlÏÞ-P,BMU¨·HÃ’·W¿‘ìûxãA¤¦ú=O_±êã'bÂŽòÕcVdó¼oÆK}ì4Š´È›Å~çF˺SûL1ÔÉnËRáD®)‡i°Ð}D’^ÂœúñDŸ Ðe¢Ì0ã
+4šüã÷k®‹wâŸ4
+„ØäŽ+§èy©¤A¼\[âÝ9éÁrUPÁMzia9IS«{ÚªzØïJžY¢)CU° 7~®ÏêBc†Örü÷‡3“&‰¸Iv294 µ²|S*ìÍêÓz”ê
+ ¨ù‘N±ý§¸#Œ>ú(+oÞ»a\ìèú1:*›½=¯Z@ïy‰K<?¥IΠؑFI¹mœ.´‡|§. Ò‹ÎmåšÓ5Ž”tãq jaþ‰XIí©7´Q~•ùúô`y’eý½¦[säÔbò[¨ƒ.‰'¢Ä­·™s¼]7»n!¾’\4Ôä©ÞQ$xàܦ\‘hcº‹ Ãÿç¼jzÛ6¢à½¿b¡XªøMA
+ à D&šð­K÷77$ .ýÄ‚õCKÄ¡…°g³àˆ€^ä“QŽ”úÄÄû(B(€²…Ñè)¶4Î9Íü4²ÌýeXš°i.?‰NÏÍñÀn»ÞòfnåV·dŒªãþûRÓPבÊ À±åryŦXéTnÉ_ :“ÉóûýÏ÷ly~ø÷_oM™ºz+2B*ÖwÈä¤-ãMìïLœÛßµö&SúŽXüsã üæpݸ‡´ë–SŠŒ[6cW,j{¬²¶¨Éú”Óë–OÓ4í’ºµ*ìñþz½¾»½_}ž[I :ñâ…¨
+K—Ûx!bi y8áK–a46šct(Å|ÖÏÇßíP3_ p¬ié úfLÂœ»Ù‚%Áºk½|w>¯~{¼ýºZ¯¯oWONÓ
+A¡0ãEv¹&ŽßÌçðÝÙãhc‚>I]Ó¤;=êjø{㼃h¦¹A×ÍÞÀ¡S´šøpAE¡Î½†ƽµï5gŦá¯t‡½ qpôHÓN’Ø
+–‘šQÏÜœÔn®îë–»Sb(“Jì? 83ÈŠlòñÇdq–,X“ä·ÀÙÑÑ«I…ͺ*ÿ!up^ê·j*é"™(álɦzTa¨†
+°PDÈÉKÝNTý
+ô˃=ù‘W€Þà:Ÿ;éÕZ«„r‡ëÌ1Î#Ó‰èYp!ºš]ò²n¿K?—:ïˆqÙdžÖ ,2æV‚ÀrýeJ Öóín³ú
+‹à’t<CóBGü_^F©þE=À[¸óEÒï2ãü
+Z 7¸Á4fšßð®~`"Ø"±¾éƒ4|’¾„¹EÒ')‚RRf TñÚÊŸV¢ZšeZ~´Õä&\Á<mSèïq^)Z{YêlÖ‰¿ŒÂ×"XvVò‡LƒìRæ %Ä\o :t4™hðQ.‡…T†™¿Ê¢i¯Ð4j¤¢9F’Ïi_¦Ü¿û‹Ì«žåg-®žó RUÙMÕö˜+?Zw?ŽJ^·#ì\jxŠªm¨sÍbGø˜Š¡hÍ7ö‰z$zéh7¤%zl‹Rcÿ
+6‰Ã ª„ÔÌ(-¿)ä
+y3ç‡Þ-ÇìÅ’>dÒ[1Z Á~lÐìùy´jˆÉÄDT…ÉZ…ýá-VKC kyTÂœ|šÎÕM}05d»
+ƒÙð&æ7‘63¥X
+~%1ÃúòðL™ÝÎÿ*Q,t䓱Rû‘–G® ø:´Ø1îƒ'Ym~ùw
+endstream endobj 596 0 obj << /Type /Page /Parent 1648 0 R /Resources 605 0 R /Contents 606 0 R /Annots [ 597 0 R 598 0 R 599 0 R 600 0 R 601 0 R 602 0 R 603 0 R 604 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 597 0 obj << /Dest [ 678 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 128 730 151 744 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 598 0 obj << /Dest [ 685 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 215 606 244 620 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 599 0 obj << /Dest [ 967 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 324 544 351 558 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 600 0 obj << /Dest [ 709 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 390 530 426 544 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 601 0 obj << /Dest [ 967 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 204 468 231 482 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 602 0 obj << /Dest [ 714 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 323 454 359 468 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 603 0 obj << /Dest [ 748 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 128 154 151 168 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 604 0 obj << /Dest [ 854 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 390 73 420 87 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 605 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R /TT12 1611 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 606 0 obj << /Length 4435 /Filter /FlateDecode >> stream
+H‰¬WÛnÛȾ×S öjXX 9ÃãÂ04†‘î: V
+P4Ù ZÉliRåÁŠ÷Aú}Ç^ô? ¦ä$ŠÖœùÏÿ÷óf½xµ^+á‹õná+áÁ?ø ’Äõ/qºQâi±~X¼ús“ˆMC{<ÑlÊÅ«›•/öÍÂs=÷lË~y\|–3g©Ý@î þ*)%œß×Y¤$!±ç¦hщy¬„(’…«0bQÿVðv©<gék7’^"îžÄê©lïá’ÆI²ÉñúæBüí?Î2„ç¾…n(sÇ“›û ±:æí¦.Hˆ–Y¹u–©ÿ¢¿d¹O®)ÏàaýÞ‘ÇãÑu´ë˵T ¢A¥ K¼ÛØW÷$éz½ðE.*Œ\ùBÇn’ˆÈwƒ@€A"j³Ø-Þ¬‡`h¶xÏ£1†ÚÈ€•ë¿cÂ’yÂúHÆ‘z&¡ôúPÆœ™—­Ù›ZÜ\¯ß^¿ùtsûzõËùÅùÙŠjÁMØ㧽\a<À~Œr Û®.A!ÁG±áßÎY*ˆJÝo+!<>¤®uÐ}Y<ÙíþFÒžjóG#¶öÈ]·w–„\T‡6¯@Ë®ª@S++½Üp^—XÎb黾¦”b`”õH¥ {Ô˜¦-"kð0hÕJÜå­è#öˆHªéPjÓ˜²Í˽»"Û£…PMð½&ÛÜ‹C2ï
+ô ¼A?°EKÐs!cؼõŸÀí÷†$.DJ¬ï¡œáט]Wˆ¶ŽïÉÖ<ª:«óâIlî³Þ¶½°ðÒжA…-/ée•TçN
+Á:´â‹ô{5„&²¡‰Ò€-Z]¯Ì~ë`
+¯ßÐç›ùõ;Žö‡÷+M%^ØM•æÐŽ"k¨0r
+±lh[ëàëªF‡Œ¨ê|ï$ :/³ÜYNÌÕCqz!ÙKåM»³>"ŠŠÓÁGÏg}`üù ZË/C£Jê4íÆ^¢°Ý”pœ¢$tÕ´ßü€å>`àÚ?ÆóÔ©*ð]œ;J'O;€È·H„:ã¡l-ÊŠæ¾êŠ­¸3P*¨3’f+ª
+jâÚb43®@f(©pJSˆMUîò}WgWÄ1Û&c- ø‘Ƭ#, ðóªk@I“=‚¾]M…ÈHº–ÏàҢ度̰ ‹·y“}Ï­òûQ€+Ô_FŒà
+&’ø
+K¡…Ô9žEqŸVÌ qGyKeU˜¬<[ì:Ð<\NåMS;øC–è8võYK~¬Öû¡“¦6÷¶L!à
+c9Ð#ò‚òi„ùg—?f…ƒ>ÚD8 ªÌòXÈpí+Íêùˆð{Œ‹ƒ—ÖK@hyz#|Œ?åP¾u(©?8¤Ò!^ŸáL@c‘Ì&©ö¬=ª\ˆÒi,â!½ë4j’Ö °—[û³ëh.bn £1pb3s<jö§6—Nä£äwŒöÃ!€áht„Tpöj™ñè.ø%ßÂåy‰±M(—´0à•±H¥4æéRù=RéÄ" Ë•
+tÓÁ*+NàOçDüˆõ=à§áÄ”‡µ¼â þv^%JªW‘Eul8ƒü­ ܱÝÜãì€0H ¾•ÜíıΑ‰’7
+„^-Ÿ¨çü c‹År¤›èÜ?oÚ‘%} Se'Ã4…¬'–È€à<æ¢<ýX³Aˆ-®X©—_
+
+=}/ð‚¤dÖ">kyŲ0t/
+Ÿ°·Ï°"°ì"`£
+p²ÌágQ›¶«K‹M˜˜ íVÈÇqh4U,†÷œñ¥n0åhþ”ð=’Tüž°;hétBéõ äš6ÉÝT‰]Mƒ¾z‡QWòæ=¢g 'QGæ];Ø烟@ùxÉ÷u4BùètºtBç¼ÄÖÈT€ßÌ“ô)GÇ|°#˜K#†cŸi[¾gÀ-y
+¸Ua ›äÃ4‡´ÓýË5Ç»?Y÷&ÓÝd–Éœå«ÈLì-iÒ uF!ãÍP1ÖbÜÂàý¨#Æ.â°jëƒ)Lï×XÖ%?Nq¨HŽ|
+"MÔ¢Îû`b*»Èªö"ò™sω—œëËáÝ·[zõ'Qòæã×å@ÏGø„Â?Äð
+p–辨ó'ÀF¬òžßÕIDêùs\îü™¶FãÌŸÎÎ&âÌ÷À.É©†~ ΀(MQ ¶V¦ÂÙ§#bu ¾`ÓäxÙ ã¦nÙ´¬ûß[‚I;¬ažÝ›b½Vf›=fesx›õì«ð³Û{–—O_îï–wHx]>ÝþÓJæKÞS!‘T×ÓA:>Ȩ¦LNêÆ>§åz„ <֛ТE½y‡z
+ @Âô}Ëœg„MwJ¶šS-C&ñ#¹‡îÕHh@.{¨àë^+Èýˆö©yŸø˜–p4= SêrÔŽæ‡Â…Mø¤ò|TMX<ñÐ+¯•Ó6„ ¦:óóùLRAJ¡õ¤¸`‚b²V¼W›©·%À/‘ÎhÛ$¯ƒÜÛwHûŽÝ¾CÞw,×C± Q]:ý§5ß{Gfx=hÁ@‚9]Ýqz <Ñ0¿:kvFmøtóùêË´È•Á¾1™si†2Š_:6ŽîÄÚÅ0ÐïØÓ0 ÆÓ$µ/NÇ3ƒ™¢{§g}ÞDß. ~Ÿ½Á‰IvD€´,®ñ¸¸¯Âl‡²þะm½ãv ®´CYóo“äãkB›7NŒ§ì—ó…2Ö‘cy‚UÏv!ý¶÷|ñKöº;¨.³/Æ×
+3÷Wl³µŠqÜZ‘‰vzbŸç!(†
+îÏìÁnF±›i'Ù›¼³ÍÖ春ûšv2· Â$@ÈÌx& žÇÄöéí±|{ü™¶á@.Ƥ&;‚¯²ž“QõÙ€²€—ðƒ9ÔP¤ºü,ø£!]ÙïK·Æ1oVOlx6>Ü 
+:ñÚCbÞ°·ª·vNá>)±†Gè2—÷•&•™Pב‚H…h A7³rl¦âz#Q”Å9m
+.«™>L=´Xð{•²}-ò’;§IN¡:&‡Ìö…“¥=óÀé§VÊñ£Ÿ —EŸ;§0cv¼èøXÒ[v›'è$PÿS•Z-×NË'âïÛ«;rJÞ]ÿu»üà …؆ˬ­¡ƒ
+NåT´È­áhŒR“£ú^dÔ{ð=ü&eÁ€4¾$f¸(›ú¡<ùš +: i®tª!ÅÁœ¼¤@bÔ‰XÓ1áŸU1ntN›XÞÿöß
+endstream endobj 607 0 obj << /Type /Page /Parent 1648 0 R /Resources 618 0 R /Contents 619 0 R /Annots [ 608 0 R 609 0 R 610 0 R 611 0 R 612 0 R 613 0 R 614 0 R 615 0 R 616 0 R 617 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 608 0 obj << /Dest [ 907 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 272 695 295 709 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 609 0 obj << /Dest [ 907 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 256 646 277 660 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 610 0 obj << /Dest [ 1224 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 375 611 416 625 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 611 0 obj << /Dest [ 1224 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 308 577 349 591 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 612 0 obj << /Dest [ 938 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 372 541 400 555 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 613 0 obj << /Dest [ 1200 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 483 463 511 477 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 614 0 obj << /Dest [ 921 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 147 388 169 402 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 615 0 obj << /Dest [ 523 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 385 388 409 402 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 616 0 obj << /Dest [ 465 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 418 142 426 156 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 617 0 obj << /Dest [ 1215 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 487 59 523 73 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 618 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 2027 0 R /TT8 1605 0 R /TT12 1611 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 619 0 obj << /Length 4571 /Filter /FlateDecode >> stream
+H‰œWÛrãÆ}×WÌ[©%÷Kjk«d™väZ­6•›•JAàPD
+gBáIä¦íœæŒ¶ó}ÞXö¼šþáfz=»8ý³ƒ(ëÙù…ƒê©C˜è[íÄجrÜ4ƒ¹7m=⸇nêey„‘pŽ±›E{ôõ™¾?êUñ4ž^±E³3ÃðiîzÑk–¾u~Lj泌ë¥B˽À>ß·97,‘¼'’/ÖuÝ>ö
+ÑëÍÀ\ñã'x|ÄéR7%ðª+ʶAà‘R¹6â2g[ÎH†;$}‹¤OW´ioºÓ©~Ùnê¹ìÞÑÔºkKcs¶ï‰0xÿ¯égB
+h+Ãù(¯):àb¨N¤Ax$¹Ø‡¢ªåóÿ›‹çŸ™d3fä9m™ë‹)¸èÿ2úÐP)ISèÍ>•bKF¤. U?=M“1"¾fáçȈf[d¹ ¶$¤ìE\9JÅ–…E7T‹ª¬ð${¦²S鎋EªŸ•C<Fu¦_·Moúô[^±-/i™çrŒ;è WM;(StuEZ‚N#P¾u$wƒQì·c(j̆¿ʉír?÷Íl:(' &'€SrŠ/U±àp À‘8Î$–KJ¬ ¼à¥ƒCeóðeQ.!Žôµ,eÍDäfKuþzbæ»ùqÀâ]ŶU‚OC £ï¹
+™±}GQ/bŠ.êâþ¡¨7æ°L’ÎÆÁkß½Aí,ÛA Í©î3ܳÚ¬Dr™èò`ÕΫÅ3IaF'#î ª²ïAŒ¡kë Þ=së°Oî8:ÄÊßiÚx
+÷ÂÀ[ 毀ìhÁ8ŽúÁ_ϾQÅôE5‚bs¬ËŽ`Ìv8frˆÒl†’aƒéhdøÝbGZøÁ¾aùH~|Ç:
+ÉÇ5…ÿlû‹vmäBZ“ÊÈ·c[BœávFèrkû0‚hCž_©Ñ]ˇ©âÏ|› êÈRý­}úÝ©ÌV7ñg¤å)a"" uÓxöÅ®µí5xMQÎGÅ—N 9ñg8•§VA‚ðר4½ò]4Rê˜QÉ›ŒRoSjŸLþÏÉç/,“²j/’¸UG–‰ýr!D2Íf5—¼7Ò5.ŠM= ç^¯‹®XõR ¶ä
+ú\mú]<0P³G³ÑI„ó†ÔíJÏd'ƒ)¤m^¹Í N‰Èž3N“m"RjËi¦ið$Aètêjzq9›^Ý|šÊpñ™ž
+¸¡§NçÖ9Š*î~79v±qf»X‡waŒ#‘ˆo!]¸#îÙy¨)0ǵ 9 = ¢”Øψ¶š°5ª&Xh¹§’b$³5Å¡~;$<|Þ:ÀÂñ£os×Ï^k(q涟œ^|™1Sþ¢ªúpµßŠXY qÛä$Ï[Aë—¬ár)¿•…À”-–}2š,Q™îÄŠØÜX;s%ÄzµZó9V¬tDž¹m—g¿ìÚ®À&ÿCUb …Êo¹¦èš²Z09*˜òu‰±v)Ù^HcûPµ›ŽuÂuˆ_Õ8ãT5fJÑ<ÿ}x^u=½¾>¿üüÇÓ+@6t$:¯7Ì‘4¾‘¿k|™eážÞ?8ød8w)"ˆÇ‹1s½ìÈØWˆÛ üÌö>WFØœ2›c[¿ur¢s´cA.9Úuò[!žhÀ µy¹±PÑ‹â®&´nsSýbôãf;Øñxr4Rx¨Ò…$RPó-¦#g"}óQ:‡®·7KúÑ®)ˆ¬Üékó|Ý[%ñÀ“äö°Ë…Ó$-Ù'¼“…àô‚UĆòP™§‚øÍ€xuÛª©†¾´;®ô•’ÞìÇ~z§YjuÐI¬BäTOf¸ÙAíŠÞŒ5Wâ¢,iWþfßÁÊ‘V_œÓ¬<l=…ë“Ø,ÆYW@ôh†Žæ³ùM?tö†'ÏOŸÝÆÉiѵ+…L’‘ ®Ãkì_4sÑ?CÍàwmS?+^>8¨p³í¼ê6MCYGS e|Ãd£úB$!]&‰¨v¨rå¥/gh#û…
+Ò4Ù;ûNNC{ô,·‘§m ;&‡Qø§þYÁï¾nïŠZAUAØ„d^V¹ê3X¦€Ãf¤í‚]Ø'{“ãP2“QÝ3Ëä‘š¡m‰øê±xVãõøÚÞbi¤å—`LtÉ5ÑžìÖ¢¨sAkh uçÔ$ã[ù3H Jºê[{ ~g¤½,äe¹¥àsˆ»Å¥ØÿêÕð•8ÈÆJLþc©¶öæàMSrq5¶@ÒͶfË-òÍýR­­…²]­+êô€¥ó?Ϋ­µm$
+¿ï¯Ðöi ‰ÑýRº…’šm!¤…$4 }Ql%•%#K¹üûýÎ93òxä²°/±äX3£sÎwã1Ñ‹¾Ò}åƒkwé 7´ÌV4ã±LóX®ô-x,×\ 1àɶÀ™©D¡¡·éøe@Îm7€jj¹äÃpH˜w. gþïöêÔìï«Õg 9Á‘ŽX0±ýg˜^P]¯nNɢɖ*úÐ
+‘÷Œª(þåÜ\m¿K¬ÜòMu&òä
+dJ@±×uãÕI•ÌS«gÆwúed ¥€'®J)Ÿˆó0OBeRX‘^äc6]‘µMàÎMU3RT fó
+Ús{ÃRÁ|)ëA;˱êfËœÓỸ©E…> ãNé+ü¼=6·è4߶üøäZ«YТteŽ +8`ù ª©-ê)Þ‚C·JÛì½f{Û±ëÇa%'¶'ÈËÂéMM<‰ ¯ç@øsÃIkQ5`-=>)UŽOk" Q3Ÿ ¤"~æÿ½I¤0Ùƒ¿ÚêÿídQ¡Å)ÜMIQ‰ui²I~bø
+'öé—‰c3P
+±0ÃD}763hØ `;}wæFœé§„¼wLö/·ïÐãA®k n÷’iAâ?õãUåÅ?¢ƒ¡CÒÝŠå‹qVCj¶éâ9¿Ìoòcbòã~Ý×»aîÑ©cìÏ©?ðPhàƤFyÉÛ={)»< æo p#„ @oÙ×ÀKŠ ¬ ³ŽøȨqg^×Ó–;*Ú½ ÇÝ=× —-d±åJ jqÚîAZð@MRb»Þ«ÊõÓ¤CKo6Ó1|µ`ت¨ëŸ¾Coç¡7ð³eD^¼@XL­Ì˾ïtköw$‡ã™ƒ$óm­`9^8øSÛ&94úĶ‘µ-ošÒ¦‚ºŠH¡Bç
+3û%Y=„±º¼§«D5U8ß3s½5±‚ìb&,f"gÄBéÙí÷ÏŸnV—_WW7
+endstream endobj 620 0 obj << /Type /Page /Parent 1648 0 R /Resources 626 0 R /Contents 627 0 R /Annots [ 621 0 R 622 0 R 623 0 R 624 0 R 625 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 621 0 obj << /Dest [ 1215 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 484 743 520 757 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 622 0 obj << /Dest [ 1229 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 395 708 436 722 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 623 0 obj << /Dest [ 1229 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 336 673 377 687 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 624 0 obj << /Dest [ 685 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 71 551 102 565 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 625 0 obj << /Dest [ 1502 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 178 241 192 255 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 626 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R /TT12 1611 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 627 0 obj << /Length 2785 /Filter /FlateDecode >> stream
+H‰ŒWÉŽÛH½ë+>%‘æ¾4 Õv¡Ç/K=×(2%qL‘—R©?¤ÿ¡ÿqKR¤–š1
+(1ÉÌÈȈ/^þ¼œ½^.]áˆåzæ¸Â†?øñãزc;QXal{b¹›½~×Æ"kiŽ-Ú¬š½þeáˆM;³-ÛÆ9Ù̳oò!5LÏòåFá¯+…ï ã_˿ϲˆÈ¶’vñb+´y2à’-|
+B6õ— oM×6LdzBiÇbu‹cÕmá+•–d[´âî—¹øç 3€qß+…aËl;‹CÑý¡š’Œx2­rÃL¤ø“þ“ct|:šk[! –ïáyp8,óÙâ®
+·Ó°¥6œ.Ó¯¶dé~9sD!fnZ^è/²âX„Ž1€3ú±hÔl=ûyy
+†çÀû<c¨mŒx¹ü7&,¾LØÉ(´a^,}x·¼_ÜýÇý‡Ï‹_~[üþùÝ£\Õu©ÒŠl‘ÏŠìØE3nâX¶a‡sê ßäºL7† ʧ´ìutÃóB+ºôƒSêÒŸ†3hÐã±´¥w±-ÇI8êݶhÀŠ¤H˲>èç]kà !êGÃÄ ÕF@V¹[±„QÚ‰ÂplÙ­"_M'¶|G˜Žåx¸)9ç8¼[VW]S—¥Ê^hïM¿ÏÓ΀Ć
+(>M>.?Ð _~ÒÖ
+;EÔ×û»%XŽ”Ùâ×å”@neÖÖ &6ÒÙgÖ­ðæº3 9“ÌFš:üÄ;ãÈéU:‡<"I S/)Àp*|÷”U ,ï"«Ñ˜Õˆ÷†t‚=lL03–”Ùì÷3ÄD"OLèÇý‰òJH˜Q\N5„”WzrÂâ
+7š*>¡“-–­';dO6˜%´'Ç/|¸Çöäç;âúí£¼
+öâ ¹ŒÕf>k (
+©Éó‘úx‚¥Ë(’ªÚ=J2ÆD«‘„‰å>Ô èbz0dê
+UAh°¬vµžŽ˜€FTT&ÜDb™óömó
+õ>àq®ébƒ¸Å­>já űîEó•øÛ~>}ä÷·àsƒ%Öu³K;L1 ±¥~5§Sós†A('žæ—5i;É— À;«Ëº#›&…>7 ¿²ƒÕÂï¸wT3lö±Ëb¿K}¥ž•99…Ë#ëÑÙ¼WXXkCÇG‘ä½pî±ll*ùº3ïß·¬'àêÉ©qRÕÐay¼¼±Áé¼)ÎFž×7]äÛªT…#¶ýFadY¢® –‹II¢lŒä’+]~ú°W©y6<`Òð¸S«L÷ZÏJ¢©—ÎÉK­ZçbL–m-*¥r݇۲Øl;84 V‰ˆ`<ðE´p3-—T9#$¡ª ²à®o;ñ]©½&erã<£€\™ØjkBUV礧´ò°Uc°hÚxŸ 
+a=×8˜­HS]QÇjôs´˜‚íݾƒ¨|..Ñ5‡9aŽ®þ®ªâ0€±&PP%BÀ§8S2ù4Ò‹?Ò ùž‰»-§Î91™^#ŒÖ¨çº+²K=½Mî'ºµRÜ0f‘p3‡í‰x¸ÙbíEš¬éõø ÊrÎú9Jðkê{Wò©åE’ÿcI¨¥Á"À~ÃQWϪôœ§¡a‡(ϱ1ƈE“ÌU—¥ø“ës ¨z·SUÎ)Û´É!¯©ÒºÊÁÍô¼Ìpwa©ÑMÑÔÑp_µ^Y¡ªì8ÇJÖ×bŽ\¯:ÝúS¤}K@‰ D˜–æ¾oöuKÂ*wÅt/ðõÙªL«MŸj1‡ÌÊû˜ƒ[`4h,È#ˆYZrxÕé`óoDúê $éoW
+,ña¥Ñháxšvn€Ðè°·äÉ%ø°ØÊ"# ÁÁÈù>F(Ò‚4ÆÚ$ÿ5šPoøtéöÔŒ_;Ãq5|Ë©†ZSCU¤#S¸Y@s<c_ÛØÖ}™‹=¯W€ùãm¸Äâ7Ù(`X}çȤ{>i
+H<#ÑÎä_Zbaõ©õ¥s‘˜ ãOEÎIóœ¹v]ªçbU”Ý‘_3ɥȯ?ÒÐ@îCúˆ‹ÏÝPÑ1ß#4aFYQP@(O-w–cƒIª+¤Î^ÉÊF¡ÁGZO^En"ÛŠ®e‰6Y®­«£ÜfÊKÜ‘œ^;:Óªd¯É¯…·”K:”`‘§]ºÒ”¦Ql”Lô˜1—ó¡ÙáÖTCÝk01¶xR]r çÌHs©nú0×¼Ôó­¢É?X˜ z@ú[ôú)uº.j’Xq<i ÜzÆ΃{ô@hâÎ0@æƒ9þ€Ÿ5µô!t H’á6ÑVÍå('qÆ&tƒ·˜g°Å0M2 ðñá+nÈ/XyŸu­ŠézV”Lµßªs€¾bpR
+±DPˆ™h”Aú®‰Ð¸w€Í½¼Ô`‘
+nĤ*
+endstream endobj 628 0 obj << /Type /Page /Parent 1648 0 R /Resources 632 0 R /Contents 633 0 R /Annots [ 629 0 R 630 0 R 631 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 629 0 obj << /Dest [ 644 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 128 568 145 582 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 630 0 obj << /Dest [ 644 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 278 455 295 469 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 631 0 obj << /Dest [ 1412 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 399 238 434 252 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 632 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 1607 0 R /F4 1608 0 R /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R /TT12 1611 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 633 0 obj << /Length 4433 /Filter /FlateDecode >> stream
+H‰ÄWËŽÛÈE¶úŠB¯Š€Äfñ-#pà§<@Öj¬,Ød©Å˜"’ݲæCòùÇ,rî­¢H=l`&‹Œ-¾êqï=÷œS?¬f÷«•/”XmfÊþá'LS×K½X$ËÈS/«ÝìþÇ.yÇßx¢ËëÙý‡G%ž»™çz}“ÏÃåaöYþ5sÊgM¿¾a$œ¿¯~š-y†¥H<wc• ucÏ,Âø<]E±™êß>ž.|ÏY¨À¥—Š§£x<ÖýO|©Ì$»²ï>ÌÅ/ÿqî[¼‹ÜH–Ž'óí\<ÊþWÝV<I ³ºpK)þÅyc>‡æ{nŒ›Õ{<ã× \%;ZUÓ²˜Kº¸ô]nmy¦?¯fJ”bæG±ÄJ‰›¦"Vn
+Ħ¢Õ³Íì‡Õ)Â'Þy6ÆT{”ìrõ*Xh º^hjÆWC>“0p’P/êE&œÄeÕkäÈ)w&·Àpšz 4•ŠNWvÖ•#[G¹©ÄÔ‘Ô"3?ý–qqÜëŽ?ÍÆ Qñà„Òì 5¯úmÖ‹<«Å“/æ™.ÞØ=>Ø-zb¡\?±EÊM¥ä8G6EHµ­ã)yé$@äÉK}Šˆ7Q¢˜i8‰rIS–klVɼzáßÕK‡€ý
+ø…’·7&N¡’~z1§ÉW³iiGûP!—ÿz#¦†§rRºÅJï°›Hî5Ä|!ö‘™Ÿãåv|¥\åß
+1 iûí)M[õ1•‡ª¥¿îé
+ñv]ÙÔH@*»7—ðIÜejò¾`wÀÕÝJ­# À‚Ø0D¡7e­;‘7uQöX¡›¦¥Û¯±P©‹m"ý*ç?1Æ@>½®{Ñ7 ¾…² t»²ïuA/øºp×ôZìù®ís‚®pŠe7?§†æHÝȆÇkú'ø}–eMI ‚h/p/rãƒR'ƒÃ±®!#X”5ö’;,¸‘š$U 1+bÊE§{;è´X8­C|Ê“­´XKZ ã¨e¯wÝ‚
+XMk2ªo±ëàô]`”QŠ¦ÙS‰ê ™`î¶ç¢
+Ì /”(d,•Üˆç¢JQq—ûñoÖßÆ” Û½v re5»2qùÏþOÆ `0*_àW J„ŠäÎ![UÖMÕ<‡à¸ºV9ÌHó¯‡+â•Å äÔU¬'ɳªR®ªŸq¥:uÿ Š«Ì ½}[¾+ÌMÑB­™Ú‘g:‡¡J“ɵ\½{X;WDMˆnLÛ ˜sNŒ5EÝ^çå¦t–¡d$¬e
+§Ö.²>{Ê:ÍÐÞgÀ8Ð6ÎÄ>RëöÙ׫Âî2ú}ŽêváÓ±ðéXø…÷Ñ*Cá‘A*}L¥÷9Â*ë­×˜£˜Ä¥ûõ-ãâS7<ÊúF¦ zÎ~“‰ùE†tŸq¼UTÙ]ÖDºK¶z|ÑšB¦ [æ F\ªËÖØ+YŠ+kÙŠÔìAœEJÂG $²`å°•¿”Ùë–·ÄŸÀnúšŒ_Jn”PO—ì%UžPñÇ6k&eôú-ïëê1\<)6͸7<ñÖV‹Þ~b7(t¥wØÍž¶”Kœ„£ØÓÞ®XuÌþbšþ5 @JtR¿APÁïc§ïÛþ(J ÊdÀÎ,ú{#
+'Ÿn}=ÜŠw1hr@»tô8\~êñ‘ü/VIÃolm œ"Vj"ÒŠÉŽÏ¢)ÁXuôLbo­_§—
+©îÐ*ðÁkê÷?žù®Ê8¯ÃûñÐûâј¯#I9vi®ºÒ¼è.¿ÈÍïÏŸ`‡ÌeûŠ:PO ÷l£»[æ6H¦Äqâ"Ï6!R,€€êÌܱÇTì¶ÈsˆK÷p]Ý4™ðòuËD…o<Á¤£±)ª"1oÍí®¬ø˜Iìܰʬøí;ÚS"‰Q^Ж×Ô’lï9:Æ­œ
+>%âÿ Ä' ªGWv «°k{:#2‘‘œª sªË[dmáÞ4¢jD¨²lý®ú•¹-¢ÓëgÎNªj¢¹H~ÇŠªü¢Å{oÍ&Q] Çâ¿÷|ŠÜ”kù~Å‘@àoæö­èÅåÐ3<þü²vîÄa[æÛÛžô0ÆñÁ’ôm™÷gǪ”‰K°} ˜(X={ódÃA¤Ÿ^*òó¨öÑžGh«WtŒtü?í¢?´-d s(¨h”¶p°$kH<)d
+—’i÷ådÎQÙÈ3fÆ2¾XÏYXÉ%mäqŸÐ8ž½¸¸¥s7ìœ=ÚørÚ¸t?´ [5~Bù7jÄÑŸˆã#[@ûYc# ò¿v¾¡ÌHú²}=-f~p’t "ý°Ã^§û½³@ãʦíÇ^¾<Ë]·±]’\üN÷Û¦ #$L/Wå~SΉ|&5U°OÀÞ’‹úMÙÿÍÌÿ}ÙäɧSØ ­ kòcŸõ — Eö‘‘ôÖ¨kÉFÙ$JhVÌjŒ¾¤ƒ"qùÇ)ÄÎÜÊø0³ €nÊgS€D¿¹àv•¸J}÷Ø`Æ>LFN4«ÌMãxÖ(Fß]ÝÖ¦ê08qQ•ô×l·8ù•Ì/Çe—fÙ}¥M«DˆgÏ$X•=ˆ<ov;öü9 »”šÈùÎ8ä1i*ÍP¦UÖÌv§_1†|sBzØ›Oîè“¥1Ùw}f~±ï6‘ånoÚZ¼–æ ÷ˆ’O%¶u¼- áÈE¡mÚ¦îI÷–È ]ÅÇßÞ™7Âüè¼1ë)ql3´/8,çÏÕ!æÅžg«dåÄQZf´åb°]›@½®
+Ê^(…]–áÓ÷rÈ߉B¿–¹9"ú:aZ¥åzT~-íüÈä•Þ|–ªªùÆD¼Ó<«9 åÚ‡°³C‘·(KÅNoxý¤¯“6„”™íió´‡ä‘ó³UgB䀂MÁ£Ÿô{¤Y®›—þ6{]c—Ìè|½Hû\‰¥˜þK{µì6nÁ{¾bŽ`âK”‚$@€ØC.‰ ä°¾Œ$®È]ŠdHʲ$ß›êÇ”(I€\lŠœéytWu•Ð6HX
+2“~Ë£´ˆùÆÄ„_ùˆýdüjÆ… «q}'˜¤±*ÊFÊcVšB¤,ÿ³÷ÄrÕ›KM?Ý(M0Á•ÓŒ—ÝÕ¶žÐÉh<—j<]«b銣Ù5¸òÈ
+4 ,ÃÞ|=Á* Ñ5Ê**°DÓ]Ú –LqeP³y‰Ø+š¿L-*–Ç7“åzb?M§åI¡do†gàûlgj|m%
+HÈ}ÿz’ Œ
+1+Ÿu8Éz¥«ðya
+š ÎUæ®™ˆõ—3 Õé€+£Ñéªqä ²¸ ànKùï"I¢ÂÔO‚+\›A|i}"*2£\1RéëÄîIÄn—±ì’fR?@jŒ½ì2t¡w2ÁÅ}ŽÐŸmÇ“SÏ—×s2Žüh‚ 1õË› ù
+P`ß5Þ…àö˪®A]ìH^²)Øg­Ý¢•µeCâ@¡‘Ù;&ÁácåGñ>FÑï4/+/¬J=àˆTTDW¬ÛVÞŸ§Âýä®=t͵{#Ÿ_(=¡—É/Ú1Žx.¨­ã'ðtÄ Ty –˜¶ËÐdPÏÂ<CÌZ†_[œAà«ñ´+•‚9± ¦²ÖÄ àFèÞŸê3ÌMËÍãÎÒɾE ñ§ÕúËøÿ`øi7ŒÅéÅlë ÕráQ4CT (Òô­­dDI×AàˆÙ%EeÍão¿š?>=ü¾ º~˜ÐfrU£b"gt-zŒf;Ä•Ð%4œÕ5ßKàÕòãh+èÓ†îs^Ùò¼ 
+²ÅÕsI—Š•§cöGÚç7Üä1¢C0Ék™z8É+ˆsPx}±ŒFèaXxNâ§ZVP_Ôçb ¬
+ĵwÛ†ãÙC®ŽmFÁèB³½]@T:ÉêjîÇ^ÄVb†î`R 7Ĉ*¾ÌA4XQ Œ“£R+F%ÈTEìHÄš-w³cˆÙâR ·û¼v¿ß(\z¹…úÛåEøPj<^ÖŒ
+6ëw9Þ|ik@¥P2/4.Þ.R¯,¥øʧ@ªÌÔd]ÎE>·É^ïoÞó\‡6¥Eæ Ѷõ }àFfŠ'½Ð³ÿMðÌ
+endstream endobj 634 0 obj << /Type /Page /Parent 1648 0 R /Resources 639 0 R /Contents 640 0 R /Annots [ 635 0 R 636 0 R 637 0 R 638 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 635 0 obj << /Dest [ 863 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 209 743 244 757 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 636 0 obj << /Dest [ 634 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 308 590 324 604 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 637 0 obj << /Dest [ 641 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 380 352 395 366 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 638 0 obj << /Dest [ 634 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 228 280 244 294 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 639 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 1607 0 R /F4 1608 0 R /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R /TT12 1611 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 640 0 obj << /Length 4471 /Filter /FlateDecode >> stream
+H‰¼WÛŽÛÈE^ç+F4#šwR†mÀöfØ#ÖSvòÀ¡Z#Æ)ˆ”å òù‡ücrªªyå±± 6ðx†—fu]N:ýzuõtµ
+”¯V›+?PþáO”e®—y‰J—±›d^¨V»«§oÚL-¯ñT[ÔWOøà«ûöÊs=ÖW‹þòtõ³~Ÿ;‹Ðô½¡¿VQ¢œ¿®þxµd K•zî2Á.aæ&žl¶EWq"¦þàé"𜅺‰ö2u÷ ><ÔÝOmXÒmÙªW?\«¿üÛYĸ?à]ìƺt<]l¯Õ‡SÙýÝ*6ê¼^;‹¥Vÿäßì‡Ï¡ž›àfõ=ž±§ÓÉuB××-íjh[˜Æ–..=DWØG[¶ô‡Õ•¯Juĉ&¾
+S7ËTâ»Q¤c”©ƒ¹Ú\½^ É},ñγ1¦Ú£ŒÀËÕß&eêó—&ž‹ÿò ²æûC-è’Ü/¶¦øhÖÊñ=ßçNŠ /ˆ§nØÍt§rµA€K]V98Ò쪿Û:ð[—²PíÊûm§îŒªYä|ÕðùN°ŸØG»}e>+ùVvìŽyW6v_uÚÚÝŒ"·"}á†êw®›Î¾¬Øù\ÞU¦/ÝÂw‘Tìgmêæx¿U·Z>4F=Ÿ•7(Z0¸-Þí»—ª¬j»¾wë\«»cgýß6ÇjÐ9<•jJJÀ ù¡HMm>õ.ïùb7|uÅYô\d{.r½ÕŒÜ0ÌR%·C]½ÈMƒIcøQß)Õ5u}8kµ2-JX¯Õ»ü£Y8 v}Ÿ·­Ú5kÓ»^tz¿U’rÇÛÙ·í|ƒ fC)4Ê\u¤[$}‰ö Þ,Þñ«·øíkeêû²6”ÙîÔˆWŽŸègœëßuíS9ƒÔ6`!]¨CøzõË"Û£Òˆ/³ñIRéc
+`9Ʋ”X([ð `{׬ÙA¢rç<Q  yÁÈq6ÿjµëà‡‚®Á$¨ES±>ìòŠã¼æ²ìL^c!%ECÑŠœ{ŽªÌçýÁ´-Ú‚Œåû}U"™]ƒë¼Ë%A v‡p’K‹1¸il%az©;‡xq Ñ…t·Ô×ÄÄÈ÷Iž{2ðù‹Â>T÷΂Rb²&à]¤ÔeŸ«³½T³QÝáhߢýzWz'ÔžM ûÚÞÄ­£xAnú­6vMÅ„
+ZwBØãF×MoŒW2MŒÝ:® údˆ\@–üZ “N$ÇÞ£Mí8Âíhàãt´hZ$Æž©ÍÎÀ"/w¦½fÄÙ®ÚÑü;l§ÚãÁà A Hª­:¯ª”4siNr&jsÈ;À«ìÌÎ"‹÷Å'£v
+ŸÊªBf‰ÓB-¿Ïº}-HvÕhñmi_vµ‘¢‰×g˜Ÿ+FÜÖúØô¡´F{¾)²¬SUùÑÎÕwÖg¿ï ÏŽ@¢"ɱ'Ë […Ǫã&x¯ ?Û²¾ÇBä’¦»ÞÂS·ˆmþÕ'^¬ZCØ$¿€Çöb(iA%áQ’`Áä[
+•Ê7d˜2';¾¢æõ #òýéÔ«õbÞ‹‘ݦ䶦٧nØa€‹aAÉ»ûC³oXbCzÏôÅ[ÕvèãÜaµ5âUŸË¾·‚h"å18vûæú´«B7& õ lz7%á&¦ÁD¨ŽÄk4¸p‚hyÌ
+^hÙÿNÈŽIŸhŒ€ó§_ÍK†8›ƒ$ô–=rû+C„”š›õæ¬`8ôDS[cTäÆ
+ä°l»Vðå—«S³ºl?êÝåÔf2ØdaÑ»‡ÿ»yKáк}Ãø-÷ÕÅÅy)™ƒt1z?Ý©}¬û›‡¸j‰ñU³ˆb¨ÍC¦^NÓïÃÜ
+ú¹%ì@Íô}Y°h]Êc’Ó{¹‡\#æè„-š‹QÊúŽ¹r%AƒæMpžœòÛXô/ÀøH8o‰Õ–gá$}ŒçÁÄrhx,œä ‚ã^Ù~
+¢ôÁÌ”ZåÿÙÝßÄôÅD‹²i"G…ÂM-ÌÝ{!H½3R¹Úç¤â3©d–TæC*}ùVA/UCãºrŸò5"u†V'ßê (‚ëÃùyɳ:M%. Iø^Ì»,ûJ£c"5Ô®H‹¬,B”3š Ò` ´Àž”Šj<Iì©%"Íó>$¡@c­lA»rœmöòFZœÎ2ÔÀs·7þÜŽÏÓ•NÒU4˜]œ$¥:÷RLøWÏØÄøÜ ÃûWÅ}8â>œ:
+­æ¨
+lΰ¸…Š€¼­D?Ö8E"͹X€:˜‘ÚÄqö€·5¹“`ͬÀJ{TŒÐœfÄDGŠs—Ûãç§7¤ÒDN@Éø%GeŠÆŠÙj¬ÔŸoøäó&È@¤fG3†Ñ5«9(tG w|f×T«u)_›¢CHöš°ãë n‚þæìM]€÷:YÁÂd{ÐýòÕÒj-»Ù§²§2ÊÝs»‹ªR–u/]œèjËæ¬m«Vß j¤%
+jAÄXLÇ—8däª>îî 7þþ`ÖfSÖ@µ¬ÙóÈ_ø‹ò£†Ë¡ü²Â¥Éoº¼´–íÇl©^иïÆ#† ‹?6¦ l¡ßD`¢wrÃ}ï 1øB Á@ AO ~O ¾Cô…q™Ž"éËÇBEYFáÀ†{àB®€žãùÑLøýdqýüåµÂ‹½ç¸|Ñ_ã\‘ê3Ÿ2×ÿÚÈrÕ
+ÿÝyõâaàöîh¤ÚÙ e‹¥}f–A¼[ãŽ;PÐÐ1‚xÖ2^QS9‘¯‹q×KÎ¥-6ÇúÉYÆô/ E®»ÇCO÷„Û´©%žNöÏŠž.À/=ÓY“‘OJ€\2ëãÔù„8´g-ŽÄö &¬ž›DëÒ¤þ¢DU-m5Ú^ÚåÐñ:Žz®fÜË”C{ž®LïP­ç¤ÔAú2Ç̇ը_K9«³Ó­è‘ê¼*CþÚ?‘¬ëáµ9Úë_X‹úøš#¿@ëÍSû[{}\j±Ì;ÝÒU±*‡"V&“Áç½jM#oî¾4݉OÙáà–tÍ¡à´EôBƒµMc×3¶ÕD†A†Æ=&‡K̉׌Â(Z
+Ò•{ƒèk Øɘ…"²JzqKâ”á½ðeèUnÝ*²@¦ƒ(±m¢—2ÕTÎDîü5ÌÑc¿æÿíÐ}¯Ú8=qü¢¦¸k!@ŠmFhôeÖþRüˆ¢èTŠ;‰„rTœŸ¢„úàït>°2¡ÅÐ;ÝØ££`–E'Þ¹Ìñr:3ï€ôk)í¥ÿÞ [Ð'H­ËV¿ú£Só †™œýì»f<Æ ÃáHuœeçç’&ñœÑÆ‚Ê‘×=ªüX˜¿/`<H£SqDÇÓÖ»¸L3úqïcFßôçv<2y¶Û—øÝÕŸ­DL|ªK…d/ù·˜_ ®ñ‘wTèÉÙ¤hÆœ¥.
+ˆ‚û€ª,Ýógæ Á0á«IN•¡@·æ©^uˆ#{ÎS>=ŽT†ŸØïÑ#"CÄ+L$‘Ç’Þ…Õ¹ù8$¬ÝÚgüËó4ý8¦XÛïóü,Wú÷“j÷ÉÀ
+Æ}M`gBDi¾#]!P²$7Åv®D€8~bÔ¸)]hÐîÍ»Š;ª%·7D¿Ž§ðh‚s½ç[‘:õç¤_ÌÎÅ‘šq-ü¨=½(Uå'Ó³Á¦ÕŸ ¢
+ƒ (4dyîÒ ©D}K ¢÷À‡»u.eZ#ú!X!ðŽÈ <\[}Y³¥-ù±/Ò.“Á¨ëZJ(V-F¸Y 3Êâ êÄí³P}#!lzaæDï¥7+)ñ“”nÖB7 O7^¡®+…Ú”è¦ÁS©è›žªžEU¤9Õ¾½f”ùlµiiî¸Öܱ7µàiì0Ô´¡z Z
+(j"·<ˆµ·F23²Õ›R>4‘2µä]@u):~b¶—•¸IJsØÝc¬ãU‰È ÂÛšm,"TT
+ÿ«BÙoFžº¦:§…TÍw%¼œ›ò­ºçt1[Â=¡wɯ+|óè7ú•·øg
+endstream endobj 641 0 obj << /Type /Page /Parent 1648 0 R /Resources 642 0 R /Contents 643 0 R /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 642 0 obj << /ProcSet [ /PDF /Text ] /Font << /F1 1609 0 R /F2 1607 0 R /F4 1608 0 R /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R /TT12 1611 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 643 0 obj << /Length 3857 /Filter /FlateDecode >> stream
+H‰¬WmoÛÈF¿êW,üiy.ßi4$±øE¬h/mAKTÌ Eù$ÚŠ‹þŽûýýÐgf–¤dKVƒ XÜ}™—gfž}=½˜LeÔd>2òñ‡Ÿ(Ë<?ó•æ±—d~¨&‹Ñ‹7ëLM×¼ÆWëi3zñöʨÏë‘ïù>­™ŽÜîs3úYÿ©pÜЋôç’~­¢T9Ÿü4Êù„\¥¾—'¸%̼ėKø€€Ï¢¯8‘£þ`Ö |Ç5¡—h?S×êê¡io0èÒÁIz]­Õ«·cõ·ÿ:nŒñ
+²Ø‹uåøzz3VW›ªýW¹ªùPÍÌqs­~ãÿ¬›Ï¦¾—`09Ãk°Ùl<'ôŒ^Ó­%]‹£q¥‡OÖMíÔ Ÿt>U©Q'^˜¦^–©ÄxQ¤`c”©U9š^Ozg„Kü]o ®öÉ#ÐròËèÅ…y/„‹™Æ¹—²W_\ž¿;;ýáå9m‚>Yˆ˜•¦¿ŠuØÒä¨4NziD@Ŷ<ö3/ˆn?"îðo–²f"}F³ýÛˆ{¯0Xh"Ï›µWüzr!XYDÁŠb/Ml ucú ¢OÝ{æ
+››
+¡[L©Ê¶,å;§ECv]SYŽ|Í£ýcÂ$ãïf¹iºCÿO¯
+³ˆô– jUgŽ+›¶aç¢#iOG"Т¯·vÅ¿/É70æýoþ
+N_F»\"ñöƒeå ”+™HŸ)fû·Û»c"<Q¾YJšYéaÍl?"ÞòJ”&‡äG
+nœ¦^}3›0D©ñî#‚Öh¤…†‚THÜ…[¬Uô<¹pøý¸Þ¥ÕÅ~ÌGƒv‘hlj%œÂhù˜St+Ô “¨¯"ÁX]ßµbȲ©¶mµçRò– ÇmÓd©«–¸jµ±ò5#tŽmžRB"ÛÊõºâŠ­Šìlö ÚØͦjo–Pe±œUóªtEã°³¥ðuy8™D6¬nŽÃ,U2Ä£,O(Â
+òS¶Îï5Ð\—5§8ƒ àµV43¦Ëk‡#ù êÏzˆ34š•óª¡vðE(üÆ ÈÚÂ'|ÖË…¥.+´ÌA©®ª
+ráܨ*¤ =IÑ
+™§çÜmÐqk¼YÈý{‚‚|ŽFñŒ¾Cöå}Xò®1Ì+(lj§æ+ñërÁLìñÑ
+//ý£ Ns‰ˆÞ,B'7 LŸñl]f ÂàßzŠV?"ŒqšY3¥$ ˜PÝ.¸66R+øwÕµ¨nI•+·î†,Bf2éÞGèöüÔÂÿ; €È€.)ÙŒ¾«Ué®A è†Æn«².ˆ-/e­ZÜÕmå"Ø™}ù5†Þ[ÊGëÐëµ*y׎àÚ,R¯Î>A»SÈ„pn6‰mR“áY· ïÎñ¹:¹üEÆž”m!QÖ5†wò‹ŒéùuÀR&ä»8VµÝJýðN亣q72MÖ78RgÃãtZ!dÕŒÚÚ¯Ö'g¼Ÿ|åCEÈ»÷CW-A½æÕW\Ç­.àVG&ž\x'B¤`_xþyqùñj­}ò4S/fbîùߣ@F}&Fµ ê²™«Su5yEÙeôG±f2VçΞ°vjÊþ3ÜEýF1´Ä¶ãÔE]vŠXeÁiNÏ`v¹ ×8+:ÿ@#ÖgO}‰gjr”SÀ°ïPÕLÒÓÔdÇ—h3êâ±JU!——ô“èwÂUÿ*²±z{ù‘ŸKßÿðÔÑèàƒ£‡ç¬‰;Úˆ£‰ö®6’•FË
+ºWöD“‰¸[Ψ¯oW˜IX!ÕQц§•J(TY¬žÙ[Ðo¬:Kû„ @0F䯰n—{eñÉ[ZµX-ásqŽE­n{â–©…=k%*p¢®[s›îegÃm¶9@‹cˆxÄŠÆAã¢F™^B·}%V´cµXqys„Æ“i¶ö   0îfÄÅ È7O2Öl¨¤È©V`Ão÷Ô/ÿg·|‚ÉܬÏæ›·j:#]™Œ_*Àº¦éßø%¶ ½BÂ[±)Ÿ“¦§ÇNR΢Ø>[O ‹ztÀ—Pƒèw¬Ö´HN¦p‰¾akaôø¬§Æ>LNÓ¥âÔX"ü3Æ㑵‹™*‡0VšXËt0$üXpø¼‹žŒ‡Ž«SôfsÓöP©FrqaÏ/xµE­ôˆÞFuò4»S£A Ñ2NИº-~éÀëšòØÓ‹ùäÃE:T§ïfWéOÑ}é!ýxžiØ´MImÄÈÀÐaæ@yaðØÙÅ;dð]\¢‘jÊuÔz¹Ú²îUØÎÍŸ‹Å{^œÒ5ž+eª£þìúk¨nw HqÐ7u¦vLFLMc¸M×Ã'ö§ùÈÇm&Xn§øåBõýÌ]c~ý&O¾õÛL*Õ­!3ÿ>È<ôâíÓÙUi4²Œ»r/) ä~''³í²,T9çÞÅ+” ÖdÓ+ÔÿÞÝ‘ûõiA^U·.:n¯Å/”¥’ÙÛbfå}|Ðö§‘°øè Z,‹{*52<¹ÚdÅý &Èrõ•3Q×…g pnÔ+
+æìIúéÌP ðH•3z{rþa|&LnžúÞµ›ài—Ü9¬Ú'2H…O´ÐŠ
+endstream endobj 644 0 obj << /Type /Page /Parent 1648 0 R /Resources 645 0 R /Contents 646 0 R /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 645 0 obj << /ProcSet [ /PDF /Text ] /Font << /F1 1609 0 R /F2 1607 0 R /F4 1608 0 R /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R /TT12 1611 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 646 0 obj << /Length 2916 /Filter /FlateDecode >> stream
+H‰¤WënÛÈFÿê)F×Íû¥ñpbÇpáØÛJA·
+›!bcB¼nžh×wtzNOë¿„¿²(ÁJŒ²B”ÅìY<(±²\F^‹¼ﯮY¿˜XÌgµZZ›-
+_¿àYoÕÖ†ÐPªº:¸éПÏ)X_»"õeÙ¨ËM˜®ÅŸ
+c³¯á¢OwÊâ5±vê#û°¶@á’ôe{3nãÿºæ4îñ‹¬‚ߢª—TåDäóÅLÍUa.DÏNƒ­ 1èâèøÖZGµP£|ó/Þ‰rAŸ*r,5×n•]üº¨uw-(štä@ž–b·á»v¼À((7 :]t,°}?‰…~mXh§v¸Ho­ká¶êŠ—ˆ>îG0ˆ%ÓùŒ—¾Ztoæ‹­¢1õÖðÚYï>ôש*ÄžgKÅ ÓÈ¥$³:{È*|&ÊL7Šz*&å²Á²k;ëÐF2O¨·í£n7¹f z"c Õ"*1 ó%÷PŽ/y†ºzкÄÙ‹zYsW$ò­P32Ì—)¾,`„Þ>¢Vèɼ<–‚‡’;!ÁÏb¾"e5HBÙ$FÇ„„NjÙs‹:iV׈Æ,ÿ§2¼Ù—ëŽy›]ë³\dËl®¨ÚA@Ç a&:ªxÌ €üÁ£¾ëR×ÍF#M(u%æeU ¾bb0W6RQ.j­D¡ÙJÎ@È–Ù Íæ^¢Ø&«ÙÞ€ï™@9àˆ–â’DÈÓÑ¥;¢fäË«•À`8ã|‚Š[Í(¶ t'ø6-Ç®¬1-ŸÔ@e>)g(¶Óø¹¤ýÔ\3䚉 0d¨ó˜Ù$§Š¹vÉ›TÉŠ3Ê:¯sCº9Ÿ$ÝÌÌóÇÝÀt]”KxÛÌí«ë³<¾üóŠ{;ò{<x‰ïH}­ßË—0(Ì/6Bs¡ß`ä…x½²Žs¨¢\º(täßYŸ¿­Úò‚5Îùy.ö
+b¯†ÝøoLé»è¨ãÊã’ œÊte¥94òÆ7×øæjm®öÍe}Tà…<Ôì½ß<j¯hl
+aKŸz
+;šÂÑ·£K6dÝÚ¯=æ'²"–æíׇryuÙæòí÷è­Ï9 D¦uè‰!þýv®Ýo&»³¤ Äå+åÈäöÄMHâ.è¦ë„<]ê`à‰˜—c±[~Ç7z‚}Ã=…À߳3S^¦È6ò‘r øW“ÎB¿ýÊ°±Æ‚ÇŽ'„…³ Ú„Xê¿›&lFF¼. {Šî†!á9ˆù¥ i!AúõÍ´…Hƒà[Ø9¿æ}ç¾Áb@>“æ¨M¾ö<‘úï.’ÖûDÛHóök½¿í
+âìí†÷DFÿ_ŸÉ½[xÚœåÇ­Ûò5¬òâ °ø4èwù}þí¡;ÇïïnÙAÂöwäZ”à®+Á¬ Ik†[ª¨^J¸x+8mÒG’­T'qËâÝ5ñ0«wQë‰a$ÍÛK9ÛC‰oS>ó{úuÞq©* Œö×øÆij#Hš¾ ì—çmÔ9¨T¨l¾x#ŽYCäÐEÿÂe:§MêÎo/ÌAº"¶²>×NÒvûÝ_(h ÿÖÞ8ò
+{ d
+-ôå>xî.áæÜÉŠÍ…qwàöMæÕ±å«q’#™Žâ6n.ݹ¹yY@¦Û™‚ïN¬œ£gæ-±í®ÚofcÞ5†“aÔ´«þg¶ìׂ«óWN¾ªš§šàOöƒ"5éß­KNÿÕÜÕd&µ/z® BÔüb£Ã¬¨¨¦5ïÖßõu b>DéÂj«
+J©ß_ÏRY=Í¡üäRöI-*2•AŒCùnü[•¸´àfŒ‰È
+endstream endobj 647 0 obj << /Type /Page /Parent 1648 0 R /Resources 652 0 R /Contents 653 0 R /Annots [ 648 0 R 649 0 R 650 0 R 651 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 648 0 obj << /Dest [ 863 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 468 569 498 583 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 649 0 obj << /Dest [ 641 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 238 542 254 556 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 650 0 obj << /Dest [ 871 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 376 481 412 495 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 651 0 obj << /Dest [ 1035 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 190 232 231 246 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 652 0 obj << /ProcSet [ /PDF /Text ] /Font << /F1 1609 0 R /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R /TT12 1611 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 653 0 obj << /Length 3331 /Filter /FlateDecode >> stream
+H‰ŒWÛnãÈ}÷Wôc3ÑlÞ¹ð\v0AÖ³ˆµ@°v(ªeq—"‘²Gû!ù‡ücrªª)É’<;sɾVWÕ9uúÝôêz: •QÓÅ• U€¿xÄyîyª¬Hü4"5]]]¿ïsUõ<&P}Õ^]º3걿
+ü  1ÕÕd|}¾º×?—Þ$òcýhéjÊû×ôoW¯P¨,ð‹»D¹Ÿ² /òZô–¤²ÔC´NÂÀ›˜ÈOu«ÙNÝíÚa‰–P[+é¾îÕͧ7ê×ÿy“ßô%~¢k/ÐÕòº{®‡?ì¦áE"]¶soRhõþeÃøø|´0ðS|L? -x~~ö½È7º§]-m‹¥±¥×
+÷ ‡\¦H[ŽþЩ²iºgµè6ªœÏë¡ö
+ݵ˜¸hÊÇž½N}idRÈ&Qœã{Äbݼ^,ìƶC]uû¨fvx¶°±ÐL'F·ê4*…oŠ#Ë¢’<ökõ;bƒ¨¼Ì AîpN3²ƒÍV»)ã6Æ/£9ÑaI€³MB?‹ŽíŠNA6fš? $çZÃKÜ#=—]Ø¥qr’_÷úz¾A#^™žð’ŒäH‡ytÀŠù1„Ì93‹ Ù—ÄcÂÜNçE½Ù<ì`"â½ÓátþPL,Þ$ÑჃ…²SâJ/YÇÁÕÞ %LB?NÜu¹4M—¨œ×‹i^%Yµ)ÛGJ±\ƒ¤0Œ´ºy¹{CD±áL<#ƒÀ7GI¦G›YÄÔèE‡eS`8æü}É `¬ô(Ÿòƒµ¹Kìß+ „Xƒ´ô$FÏß¹„u†Äí/Žn^$çãy&ÇñHcåb°^ˆ³oPæ}J^ÆÏÂã6û6¯\í KÝÛËeS¶™Úr½ó•g}ÛÁÝÃÒ5ž¡
+ã¦Uô@.l]/QNÚ€¯ â;êTõjÝØ–"¶7\†p@¥­õ¸D?z”à+›0•›åØÒ7éKf2ûhìÏ9”½—0?S½§˜€K%–öHôæ ë·ÖÎ¥ÕuV®³^³ÅÚͱêíÈ
+þÔ“•ªüÒUzÑM¤)GÐ>5d¶1gSêz˜ `=wí0,Å.zü¼
+AÍò@ß±[2©ó34
+îM1ºŽÞÈÛ/7tŠLO§T‰„¶”x¡&¼Ó¨Éu
+ëæ©9‘sߪ$zT8ìÆ ý$"N˜fÞÓ¯ÖœA7 Š6KP9ý§Æ‹ßúúV^&gKgŒ…\¦9»,`†Ô̘švHMê[ÅfqJÍ,šnó9jšø?¢¦_¾W.Þ}èQÁE¡á¡úCBTSNÐc3܆í9tñBŽtkД’Qš†J!•&Á6&´„“oÜ#- S°ZoÄxÈ0åâÖI4B®Å/¡A’×JR‘ÂÍNjž¿@\[—ߎ¡g%™HáECÓ8¢&=‚q‡ >æJRákäá†aªOoý
+ÞÂBÇ^Ýfé°°Þ_mϵVD䎖ÁO±;eM4â^ò^“µ)ÄÖÀ­6{JGõËùPŸ^ïôTFK–´Œ ž‡A-|Ü<üønî:ÙÃö&OPB˜Y’it
+endstream endobj 654 0 obj << /Type /Page /Parent 1648 0 R /Resources 656 0 R /Contents 657 0 R /Annots [ 655 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 655 0 obj << /Dest [ 634 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 474 674 490 688 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 656 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 2027 0 R /TT4 2032 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 657 0 obj << /Length 830 /Filter /FlateDecode >> stream
+H‰dTÛNÜ0}ÏWÌ£-ãÜ©ªThA­ ªDž
+}0^ïnJHV‰—°ýþCÿ±{²—¶ ˆ=vfÎœs&up^×1DP/ƒ(‰¿øHËRÈRæPT™ÈK™@ýœ_Ž%èÑß‘0ê.8¿¾‹`5RHéîè Ü/§àž}Q<LDÊVÆ=c™þ­þT>C…UŽU’Rä’Šø±ÏåVYN©~Å cÉÃ(9“%<îàn×Ù5Fbf8fbc3»ë3øú›‡î<ËDÆ.™^ŸÁÝÔØfh}’„©nÁÊÁOÿßóíûÖb)rÜÔï1æLÓ$x""6ºªÆ•ÅÔXRàRbwz­}¦uAAœå"É#H
+Q–G"M{LKL° .êI„Wäßl©–ŽDY?‘iÏ_‘KôŠ >jüϽ5`×Ê´nZýv€~ê€G’!úötÛ˜ÎÂfè[mGxV;·yi¶£ é¬–Jó\ÌÀ²0£1ÙÆ6}7žíI #èwˆ Š`ÜÒÁ·v0õOQË':ú
+lF` 1¨Çþ…WnÿÀÅ©S’ƒSŽFÙ›¶Cs-Qz”ØÕ6¯Ö œ1×—yÆöÖópAÑ¡!k äÖõC»9g%lÛ.ÀûïY=íûô®ÈØœñ¹^©¬ûv´nHO˜š²ÍõNˆ¥Ë«møïÜÏ–T®Òh\q÷CDÈÃÂXÕ´äÉ3˜ÝjÈÀ†¶šÌ{jqß=QTE´ô’Ð=7j5Ï iR²­ÆY›co”Öfc/ž Ò[0V '˜ûÙÙþ0
+endstream endobj 658 0 obj << /Type /Page /Parent 1649 0 R /Resources 661 0 R /Contents 662 0 R /Annots [ 659 0 R 660 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 659 0 obj << /Dest [ 658 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 71 475 88 489 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 660 0 obj << /Dest [ 658 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 439 283 455 297 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 661 0 obj << /ProcSet [ /PDF /Text ] /Font << /F1 1609 0 R /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 662 0 obj << /Length 2294 /Filter /FlateDecode >> stream
+H‰ÌWÛŽÛF}×Wôcqx•¨1ˆdgwáM‚XyØ͇jIŒyÈæÈò‡ì?ì?æ!§ª›G#eÀÙ]‘¬îºuÕ©Óo–“ëå2¾X®'~ <üÃO”$®—x31_Äî,ñB±,'×_¶‰ÈZ^ã‰6«&×wï|±i'žëy´&›LûÇýäGù]êLC7’E¿±/œŸ–Ÿ,XÃBÌ=w1ƒ•0qgž1Â
+ÖEOñ̨úO€¯ÓÀs¦~èΤ—ˆ‡ƒxw¨ô_©h’mÞŠ×wWâ_¿:Óï d±ËÜñd¶½ïö¹þ¨š‚•„2­VÎt!Å¿ù/;ÆáshçÎð²ü
+ß؃ý~ï:¡ëË–¬*2 Õ0éâÑCt™ý´eM_/'¾ÈÅ$ˆgn8óE8w“DÌ|7ŠbŒѨÉzòf9$#ô±Ä{šcª=ʼ\þBÙ‹\/2gÆO}>çQè΃QB!³ õNâŠ;U)JQ‚ ÒBÜõCê$ˆ üóe]­sNÉM7¬sü¹Ôx‘FRWâ[aûrçÀ’/u^W­Ifï]ä†a2?qq¶˜ãuìâ|p1î]ôáÊæU‘gªjU•–lTÝ^á7>
+²zÅ+Õí¼5ßÈWrZàƒx’‘~ï…oœ@tšWj%òê†Oz.µ¶of*^£ÂlÕ:3x|°?Uös†¬:ª3ßÜžú7eMSß…‡Ðfò’ yI¬Ki%2ã–ñ(~Á#ï葬j½Í«M_HÏ ‡ƒaF»~”¯©™"©u“?tZµôêK²ì£‰^°Ì‡
+£•ºh1èû¾5ø˜æN
+Sìãíb×5Ù6ÅrÀÒX°rÅw…JŸ.^çE*î‘î¾í¹>FÛ~ä¬í/ÎÃúa&jo¥tÁ¡îÄ6}T@2xLfB™?"`Z¨ÝŠ>×€æDvõÊê5'b×Ô«Ð iF[~+Úm½¯ÈIZ1r4xZQb£ëº(ê=¥‰â$MY`¼Ë'}ýß"ƒÓª ¢UhÛ#â¨Û_j„D–(~͈‰a$ñõ‹¬.wiupñûêz¼¥Iˆ1ê‹8^¸h'†ýø¿(gƒ4
+" â±<ö
+ªh@˜©xâŽÑùh(No!ÃÞ"ß…ÃüÏ.½êwžÇ/3ä¦ý£á9+¾©5͇TS d€&JÚ?ì
+Ã÷ÒpAºlÒÌ}8
+Wÿ¨¸{®Nõ.ŒE^œHkÞ^’²ôÉ]ƺë­ÈË]¡Jä1í/ ì®>ßyÎñeèñÎL[\5@KûÑüM]A—
+€.[>=àŒäŠØ¬¶›5Sw«Îjz®£QEJ\~m.©îzÓ×lK™dWG£èmnå„Œûçä~ÔÏÁÐÏv ÓŒR£iÑíãC^v%×Å„øvhîoªÚäÔ,†Í“¶3Ëû1_öÀm«µLAàoé*ðÙÑò¼_JUÖÍw5ö”OcÛ!mŒ­@iÂH¾Ùj”E×ÚÂ#~C G[E»­q7Ù(;¾^N~
+endstream endobj 663 0 obj << /Type /Page /Parent 1649 0 R /Resources 667 0 R /Contents 668 0 R /Annots [ 664 0 R 665 0 R 666 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 664 0 obj << /Dest [ 658 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 489 631 505 645 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 665 0 obj << /Dest [ 465 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 446 276 462 290 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 666 0 obj << /Dest [ 465 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 71 99 88 113 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 667 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 668 0 obj << /Length 2025 /Filter /FlateDecode >> stream
+H‰ä—ÛŽÛȆïõߤ ŒhžI Œl¯a$ˆƒ
+«–Õ(~A#{¨ùÔd¦éõFwtŸWƒ¦[Õ õ
+ÝvMŽï©Õ¡×»[çiõ‚£z~rvÚ·àpAß™Õ
+Î`†Dƒ„‚íß0²ÍF:¦ÜníÜ\S¯¿ð5°M‰û%V38âÚ™GÐðÀ¬–÷ñrBŠ‘fƒï0!ß*!WRv9[”¯¨`×yt¾ú¹ ‰çÛ朋ãž7ñíâÛK'ì³
+XùÜ^L[šþBÄùû ñˆXL™6•rŽ±I®hmêkÆIº“¯¹m®$3 0<Œ+Ù€^½oOÓÿ¸)Ù^+‰|\Î2‡»G ?
+wqV$h‘Ç.ät[&äê“?DêÓÂEˆÐ{Á+÷×”Õ‘ç»Á–?V&r ~‡)«›aÚK1 »[:\3M¹d9R-9Q ¦Cì Û—J“±o™$¤ձä’îeÔ…iôðûê;AÝ7«½ÿ3Ô]ÖØÿ£òúT]cÃûÜ ’#Uå+‡µF®fŠ]¿pæ“'­+QOÑgù>Ň)·ŠO¿G)»êòîQ;’
+á) ?]‚ÛïÇã§äÄâ
+endstream endobj 669 0 obj << /Type /Page /Parent 1649 0 R /Resources 676 0 R /Contents 677 0 R /Annots [ 670 0 R 671 0 R 672 0 R 673 0 R 674 0 R 675 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 670 0 obj << /Dest [ 455 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 348 685 356 699 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 671 0 obj << /Dest [ 465 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 92 587 109 601 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 672 0 obj << /Dest [ 471 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 168 409 184 423 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 673 0 obj << /Dest [ 465 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 190 382 205 396 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 674 0 obj << /Dest [ 548 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 276 133 306 147 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 675 0 obj << /Dest [ 548 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 141 79 172 93 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 676 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 1607 0 R /F4 1608 0 R /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R /TT12 1611 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 677 0 obj << /Length 2256 /Filter /FlateDecode >> stream
+H‰ÜWÉŽãÈ…¯úŠDaI£ÈfrW¡ÐÀ¬ ÌÁ0ÐòÅ]†AQ)‰ŠÔ0©Zü!þÿ£~™$Uªr/ÆxE%2—`dÄ‹/¿Y-Þ¬V‘Pbµ]¨H„øÃORAX„™È—ia,V‡Å›oM!*ÃkBaªvñæÝ{%vfaHkª…?>>,>Èß–ž‰Üiú¤HcáýaõãbÉ–"ƒe†¯ÄE…ö#l b[ô”fÖÔ_#ŒúQèù*2bý$Þ?µÃ#‘Ô,ISñõ»kñû¿y~Š÷siÊÚ eµ¿ïêáϺoØH,ËvãùK)þÂÿÙ1>>-
+ƒ /«ï0Æ<<<^(iè«š> Óød€Ç§«ÜО-}¿Z(Q‹E”fAœ)çAQˆLI"pƤ½^l߬¦`Ä
+KÂçјCRDàåêO”°Ä%,  ’ Ž‹\Ø×1¨yšytÕ0"d^äRÜV]»­w÷%¿öooÄFóã¶nµ¨ø‘Vx…Üúrðr Ð[׊û²¯Ëu£mÜàRq‰¡ÉhIXšýPcr•ì·];”øâFÔí 3—“YÍ,ÈÇt>¬ »ýÖxYPÈ'÷ÓV¤cy
+y&…ý9Â%tôªÞÖ µ#i!®îqøLRD¯¦ôë`²A¡C¬¡‘ýìš*¤ž~<R­Eãpk·€®'J@ŸSò
+’f™þƒ‚˜
+endstream endobj 678 0 obj << /Type /Page /Parent 1649 0 R /Resources 683 0 R /Contents 684 0 R /Annots [ 679 0 R 680 0 R 681 0 R 682 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 679 0 obj << /Dest [ 540 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 346 712 376 726 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 680 0 obj << /Dest [ 540 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 115 658 145 672 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 681 0 obj << /Dest [ 545 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 481 644 511 658 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 682 0 obj << /Dest [ 533 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 366 508 388 522 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 683 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 1607 0 R /F4 1608 0 R /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R /TT12 1611 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 684 0 obj << /Length 2159 /Filter /FlateDecode >> stream
+H‰ÜWÛŽÛÈE^õý–& ÑlŠ¤(c`À^o6YØØ
+@g³˜Pd
+$õRù
+‰Db¹Ñ'÷K½öÓþ5¼¤©a€ípëžuŠéÊŠ2R* æÈýP¾
+ÀÿFÍt-EWº÷¤fZV†­ôY1º°YYŠæd‹[‘€Þ‚ðAÅb(_“> B÷ºíxš+ƒø}£éUƒ¯üý;¡ëÕUáö™*£
+ó(²='øp<eªê-VÎá­Æð†!Óo# qnm=Ä…$WJZ¹?ð¼ÌÚi
+endstream endobj 685 0 obj << /Type /Page /Parent 1649 0 R /Resources 687 0 R /Contents 688 0 R /Annots [ 686 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 686 0 obj << /Dest [ 465 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 254 617 262 631 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 687 0 obj << /ProcSet [ /PDF /Text ] /Font << /F1 1609 0 R /F2 1607 0 R /F4 1608 0 R /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 688 0 obj << /Length 3017 /Filter /FlateDecode >> stream
+H‰ÜWÙnãÈE^õ½L1°hî¤ Ç@/Ó3 &ƒA¬¼¤4Y’8¡H¤Zv>$ÿÌCνU\,y4ú)h´U¬åÖ]Ï=õ~5»\­<áŠÕzæzÂÁ?üIb;‰‰xÚQâøbµ›]~h‘µ¼ÇmVÍ.¸uŦ9¶ãОl¶è‡ÇÙgùKj-|;E¿ža(¬¿¯þ4[²„¥ˆ{á?±#G_Â<–E£0Ò¢þãavá9ÖÂõíH:‰¸·U·ÅŒ'•I²-Zñî‡ ñ·ÿZ‹ß ÖB;”…åÈl{!nE÷/Õ”,Ä—i•[‹¥ÿ濬›Ï¦yŽácõs¬Áñx´-ßveK·*º¢q¥¡ë23µeI߯f®(ÄÌ #Û\áÇv’ˆÈµƒ@ÀÆ š­gïWƒ3|[œ§Þ]íG åêW
+X`ØN€ íûI,ôgïÔ8 íØ›xÕ¼ê¸Ú¦Äv]ÛµpŸ×e½Ù§ÝöæJ`"”‹FñJÖÕÍ£ %½°®+¦9/«ûÃFà¬Y,JÕj_BÍä4¯ݼ%åר›Û«f4ûPW]ZT*EuÅŽå Ö¤kdÇ}ˆ(gX–×9)uÓ;Ëh±àý ìÓí’dpIb.N+‘éËõ½á÷ºÞ÷4$k+ÎȲԎëj‘À
+EþäÑ£8n•™¤­EIÛY&ê~ž<Kòȯ¼~êÜÑ,o0Ëg|–ï¨ÙuMqè Ÿ®$ã\ÁëÆ91§]UWê7xÓå(ÊjÊîÊ0‚ÞD°;¢… ¹
+øNrPŠ÷Û¶ŠÃHþÓa¤‘ #OŠÏ-x§àaÈ‚ Èr¥Ù¢gË5”È n"l›óMi/Ow <æ>•ÛâK¬YY=ËÊýÃHÞ²Òï~á=zJ
+.se+˜ôWkAvû—<<<ˆC•ãÀÄÂ1‚1m!ù$0ÉY\oT\(yIN×ÞåNuۢ°n~<B‡¥<’ª®\ ,K‰pCBÚ¥óAÊÜl;êïC5')-íÊÞ©äÞ¦úVaÎ*º¤YƲÌ1½Élf@9ó¡ñe9àËR>Úç!ŸàÂÏuÇ¢Þ#Å\‚:ŠÕ\Ïì9ˆe¿ØßñJ"µbfÞÀ
+‚Õ*%˜’™¥JÜ+Š¦ËH–3ª5@º®×ªQU§¤í{qÛVà&S¢®Îº#: (˳dtc~!{OºSmt°Ä¥¾µkmñŠOž‡ ß?¤;äü•†ÁO®élvH»½`ôYöÔZ ‹K(Á®ûÃüXT¾7¿ùpuw
+‘ÝÝa_{}Ù·Ýž5$>:£+Â8¶ÑÚ‰(LèÂ[‹aÔ/ŠäÉrè$Pô¥Ã¯¯÷FöÒ3«kœ~~,gä
+ô}!ZºÔÜ)£«‰M’õ4­4K*Ñæa;)GífÐbÁ-»Ú”š;˜
+g¦y£É š÷uN”AêZGÛ÷0™ò‹nóa')zÚ•'`cˆäí‰+­E´ŠÒ‹\éW¦L>y¹¡{rùïtÛ50 ¦’Nµr5>5³Ô…:WMû¡€‡LZ–¾ízÓ#Þ
+¬«¨x(îõ‹³-×s†a’F â'~t†û—öÒ5×´OÒÇö¼'!ìã¿ð‘1%AóòW†ÞÓp9ßÖÝü)VzÔb'8<Ü1ÞWbW[ ˜!ÛN»}Ýt)eÁÀî!z=ŸÔøtGÀA/?]¼w–­ß+ó¼*E*bÔ+%b,Aüs‚º;©2µï Êzšô¸1aìxgV|>ã>U¯×º´¸À  ÜÐÿˆ¨v!ÅŠ’°|$¥€ †Užê¤ûI«ªéØB
+ù–2Ǥàémq0bJ¯Ÿl͹ÐpH3%ìCŽÕeñå4aFr“!“K5à ÐrDø+‚;RÄÖò©?xôîm[(m‡(—LNŽÔÀñ‡ëÃb—VéFí
+í<¼ÄL"‰ ÿ\k2•–¢¯æjs–Ï!¿mzpý6Ñ0›æ»¢šŸ’Ò·,ü ø¨Y7ÓmŠ†T‹¼šÕТíêµÜ© u¼ÈÓ.rÊ%ýB•m딬K›ê0Ø¥{|€è0$›œ²U´VäG€J,÷•¦ñ(À$A7¤B}
+=ïcdý7u±ö0Y|ÖÉ–Þiö /£= ãð,š¸y d(iÇA¯cÔfÒ“@;šêû_Á;õ¦; ÔÀG5½Ë:~3€•ƒ„FŸ;èŠ}[[z'/„hÔ×PFt¡ObŒ*¯ÊGQ¬-—ùÇ»a?ô 4XzNàõ pÓl•Âá’«b)wS°É•oG¸oõn`à Q»ºS`Vèuž\[ô–¨O)÷z·»+nv[da@xÍ…§„þË)ƒ~0Á]°æ#ºõ!qá Ý«sE(jZ"¡
+·Ë'-’4 ò<×YÔ,¿3âsõ¥È”AäWoº0YÀFž<Κ[{ÀC1î@ ºCƒ”¨Òºà¬Â‘Âû?ÎË&'B ˆÂ{OÁNH4Ä Àx2ôD‡1ÓãÂÛû~hhQ㎟¦ª§¦ú½¯jà 8»ˆïo$‘0Þ%_ÇìI.ûÅ
+endstream endobj 689 0 obj << /Type /Page /Parent 1649 0 R /Resources 691 0 R /Contents 692 0 R /Annots [ 690 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 690 0 obj << /Dest [ 720 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 368 661 403 675 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 691 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 1607 0 R /F4 1608 0 R /TT2 2027 0 R /TT8 1605 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 692 0 obj << /Length 4122 /Filter /FlateDecode >> stream
+H‰¬WÙrÛÊ­¼ê+&zÉ %ÂØ¿Ù÷æ.);qJ|Š•JÀD,
+gº‘ÞzZʼnrþ¶üãMÎ'ä*õÜ<Á-aæ&ž\Â|½aõ¯
+te‚.ÈìÆV¨gbÞzØ:„ŽîiP{~+à/:/…Ì 2iÝòüFÉ,ŒOoŠÓ± CHJNUä È1¯ õ03({ˆª.îx„Ì¡õÿB[@瀄[‘Í0Лޘ–œpŒš£pNj1C–Øe.ûìÕv5!Ké‚4µàšßÈ’ÈèFzâï£ }׋ä‹–ðîÛiM_Œæö\0(ÅñŽ˜ð,&^«å¶fô"v`‹L«aKn˜&[„U¤×]¿+ƺkYØÜTj씽“üÜÆMG/U1
+«Õèø>аåíÛvÝh¬…Y*2p8K–%KE²}ÑÏ®zSŽ‡¢Q‡Áôr¸ß>Õ“ÃoxWAbó V†¥oU×6ÏPÖ¬ ê–N nU·gM)ð[ êÉ4Bpp\,l¸ð¿?e£ÉpSülqåȱR!BÞ;‹„£#ÀÍJ¬öb ¸~Ž ·¿‰HHDnîŸ8ןoBÁ±U)"á&Ξ˜ýÃyñ vÆ®±‚¢ÉG:ž¶Ö-ÌÚ?ªUs0“ר†¤Ñ ¬O¢á
+Û‰ÇßÜ=õíØí@ù<ÖûÛ !C7Oã!šM¿g@”¤¨ P/H{àÆ&¤ó}Çh÷í×hØŽã^/«Ôò;’Â×TÙív‡Öî¢XÊK»œüðà°'ìâ¥n v‚ýWw
+åûw¬ý²ëšÇz„+s¥dêù~ÿ/-ýÈ¢¸—˜Ëõx‰a”Û“èzÁ¿¯Õ›öYuð’ Y6çž›ÜMUÕÁm7ª5T„Ï Úºf׫îVu{2JF¸E=ÖåàΉÒB÷0»ÐýBÅ«ôxuú{3u£J¸aÓõ54?slJp¾¨’‹é•!/Ñ‘®c·¶ðSè­êî•Û²ZcGÅÉ¡.eˬ(¬5ÙH¼$
+0ÒNP²LÈ+séEµëTÙ8 êÚç]ýn@¾&ä1îȼëw‹LõÞ¿°\v¼8—Ó wªª‡bÕ’q3ÁÇIQÇ€¡gŽÜ¢¥Xeö¦­˜xHN«Ð…“ê±XƒðáøuQš;Þx[­Š}}ËïXEIß¡zÂÅÿŽX
+DFýšPjÒÂvfö»cíøšyˆÌNZÞZö'GL§[.{zÇ-(üÙl±|päV×YÑ#úno8ñDh{Ò±¬¶Bï*k!úï¤*Ù¡e úL»SÝnèø\_³þŒÂ57Í„ð£ÃJ®Ñ)² ªUáDø©NYÔòˆa‹®öPú
+å J3ûß×íãÙ¿ŸºÙÿHþ=7¸¤ˆ¿:©¥’Ô$ð®²Zž6
+ád7zã¬F ,,ü<” À %l;©ÎÇ­}ö¼v´£ à™æô†üK .¡bb—ÙúÂs¹%ä7˜ÏêyÅHˆï{}-ÖV®ßâRr¢™n±7ôœ ¦ÉÝ×µ¼Â"uSæÈ
+yþ_ØóGʦD @
+ÂÙ”²Cù~VÄðYbœL$ºõš
+®‹&Nðù¼xj]¾P+Qâ±ù„Ò²«¸Â­zß×ÌéW /üJö\/y¹âÝ]$€)fž?yÄÒ+nþQïc6ºˆµšd_r¿‹ ñ–{CqòáÁ0¦µ =Ú&‰Os%2ƒÆþ‹˜g}Áå½e‡þŠ®DC¢ê,2j¯(
+…’Ôé/FˆýÅŸÒð§Dš$À†ö«"¨ãƒ&6×ñÀÄÞh_±É³–ßN[×dE)¦¢ý”ü4†ÝÈÚÏ:ÿMe&ì¡ó®šàH£A ‡3ªvT>¼T "A3î1+[i®€`¯†ýeH_á?«¯ðuvýQ/eQçý®wGé/¤UÌ6•ZKã^ÚFš
+iHõú½fG¡#“ !v>EB\HËä‘=1/ç!-GÊ]gGdãEñ •º·wUÒ1ZÛ·jîô 7@£]YäMPÔƒ7jòô%Ö!UWÔ鸨q¶ÔÎM%¬ CÎUÙê ")òƒ«÷½ÀSÜÙ€T‰—ˆâ-V/Wdßa1Ø°i«qm‹‘Ý/Æ[
+Ñ“ ûÓˆI¤Ã‹mèŠËöûQ”ìvEXâ ½–Õ^¥%âìAÒJUhäŽò,Üë/ùŠô\Û¶?ÔøWýœþÞ4zo ÿuЛò—™Ò
+íã\(q"*w¨jçÉ
+‚Ž[9Ú´ûÛ”áC²Ú¦s‰~ûP¥UP+§_ ·@÷$z`2òÀúÖ`ç
+Á,r&9pMLhS”EsKð°ÏFÞ ¶úÙâ-cët`ªÚàøü
+ÕVÐêWNÃD°õ@€KqRÆÿ.iS ’ñ]\`^²j.
+endstream endobj 693 0 obj << /Type /Page /Parent 1649 0 R /Resources 694 0 R /Contents 695 0 R /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 694 0 obj << /ProcSet [ /PDF /Text ] /Font << /F1 1609 0 R /F2 1607 0 R /F4 1608 0 R /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 695 0 obj << /Length 2541 /Filter /FlateDecode >> stream
+H‰ÜWËnãÈE¶úŠ7)b,šÅ·N3Ó=3iÌA¬
+„?üDYæù™Ÿˆt{Iæ‡b¹›]~Ûe¢èx/º¢™]~­Ä¦›ùžïÓžb6>ïf7ò¹;½Hn4ýRÄ©p¿,?Í|ÃB¤¾·HðJ˜y‰oá ¾‹¾âÄ\õ÷
+;µå›>.gJTbĉ&J„©—e"Q^ èeâ gëÙ7ËÑ¡Âÿ±5&SûdH¹üëìò»'þ ™Æ òŸ•¿1›#³oZ…ì-Ëef¯ñüȬ°ñUBOÝHgW5Õ.'k)XËaµ¦`‰¼ÄžâC±±Ò;ñéØõÂ!„pË<–‡CËWòÀÓˆƒÜ,7˜Ç°v~ÛöÃóØ è<ñ̦¼4µ)Œ[þ;¥Ó´hèؽ£fþâD³tŒkú4J’ËåÒMÜ[7UÇŸ¢B$A/;ê]
+zZ×¢Ôk7Bp@ó@k³t¨a¦æȶ½³gØ‚÷8ÂÑW‘U.ýoÍ-”¹Ù”càB"'/á´a°·û!€ÙF_?ç˜%=g;Ï#O‘uU8( cÉ¥wm¯«fÝ:žXn‘h›êVw¸L´·úp[é;q·Å³^*ó^`}›ï÷º©ÜÔi6¢<*üäéS\ˆÕ±M+J:IÆ%û¼ª;–K6I¬€=zìôA@ãÜ{œØ-Þ48Llèû^7¥.ŸDÇ"=‰Ž`ŠŽ`ˆŽ¥Kèk0ö $2„M óÃnæ1ÇÑð€yÝ^¢sâW‘×»ÖìëÝ
+“±ôž¦Z”²5“7²f`̹Ëï«çr-ÍN¬9…Šk~¼eƒÆP¼ØÂ\ms“‘ú Y»GÇ’68“A”¼G
+î™p=R¨¼iqKM”»ó†ÈKN™è|ž)bÔP¬F¬„Òqo>Ì$ŽáªÍ3oñŒjù-UpHߢ’ži‡d?M &8bí83œƒÝ!©‘“–ôœ ú¤Þ=,#%„+Úæö)ë8²É7ƒðT¦R*ST¿v
+Ä8$ÃsÕYCÿŒIN áÛ„ø˜¤è· ‰º´ü¸‘D%ñEÈj§A#˶ѨòدE{(A»½.ªu¥Ë ѵ¨©æ-PÔQíç ‘†M±r7ƒüÄŸœä¾AÅÙWhl-)‰&ØÁlxñaD5x0¦ë(Ï?à’+#‡;Gó&Û}lúÝD"/ßma¢ES"Š|4†‚[Á“†ðÕÕ8W£
+}!ÕHK–EàqÅRqƒHGš¤ë*À²7ÆÕ¿9Ÿ" äœp 4W^qµnërÑ`C>”
+¬ã‚#ÝÉ}ëü@P8"Ð
+îEŽÙYd+†°"ÈÑM¼S2§&zžÊ*•ÕmUINOü¹;#Ø“a§ú9dIèÜ|õÕ­›ùü‹CàÚ· ®’w+¢5”ÿPm¶ý…È…ÑGL¥%«‘¾UÿÛn0;äã]1®){+MŠŒG±íI/0vc#I¢ÂÖ¸lAsaÆà>ͽדº*~±
+ûÏ
+endstream endobj 696 0 obj << /Type /Page /Parent 1649 0 R /Resources 697 0 R /Contents 698 0 R /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 697 0 obj << /ProcSet [ /PDF /Text /ImageC /ImageI ] /Font << /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R >> /XObject << /Im4 699 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R /Cs11 1612 0 R >> >> endobj 698 0 obj << /Length 2027 /Filter /FlateDecode >> stream
+H‰ìWÛnãÈ}×Wü’&`qxefÇ‹Í»À£¼dœšjIÜ¡H…kœÉ?ä÷!§ª›"mÇž‡ »Àb0Ù·ª®ªsNñ»ÕìÍjO«ÍÌÈÃ?üDiêz©—Ðb»Iê…´ÚÏÞ¼kSÊ[YãQ›W³7?|ðiÛÎ<×óxM>›ÇÙGõsæÌC7R[Í¿¢8%ç«¿Ì–rÂ’ž»L`%LÝÄ3Fä€@Îâ§81Gý'Àè<𜹺‰òRº½§÷U·ÃH ´ƒ“T[´ôö‡súû¯Î<Æ{ƒ¹ØUáx*ßÓ‡cÑýK7¥ª¬Z;ó¥¢Ë_qL®/W <7ÁËêcâÁñxtÐõUËV5›ÅÑ0éâÑÃír;´““¾_Í|*hĉ&>… 7M)ñÝ("Ü1J©Ñ³Íì»Õ)¡%ÞÃhŒ¡ö8"ðrõË$MCü‰çâ¿l1k`þŸ³~y‘,N6¬Êú$Yº›È‘Õ÷ûˆ®ëÙ_giˆœû´ˆn‚]ìãÄÓgãä4ªf:{©DÏnaÚÚN’ÔåËë,{fgŸ÷ì™í/LO¢â'v6]rÔŸÜëKóHÿÓl&XÓlz4÷Ù:W#@áŸæ›Ú\9±»@áù¨jM—›º\ÕöŠöõZÓZwÚY 0›}QéV–*˜ŠªèŠ¬$»œÌTçpg¾ ³¼.KÇ÷p`ê`VèõíÍ‘Yu:/ë(+Kº-ëü“Ì=ÀŠýOþTá[ª`KÎOpú9Éáš­êó@ÕkÇçÛñX¬ŒýTì'ëYãDÖ¤?xCb\Ÿ@™[9<¾¦³¬ïê3:|E]:ü—vÙFàø\EŠ öZ°¥­?çÓ«RƒôXÐR×mßIhCU Kí5j3`þLÜS©«m·»?'Þ´©ªêfõf¥Æ{G†{–jz†Äj©*Iè)¸®‡m=ØÕÇŠ†X˾µkîH
+L’eî‘dÌOÂë¥ÎZg v}Å~i“®,fDµ)*gžr¨ðHѦ¯ò®¨A¶jeÆŠŠnÁÒ
+Ôx—íWì›ÄÝÑ%¿t(5„fX º« Â# v¡a;2±„`0"LàÜ(Dû‡ЕŒäâر
+¡lEîF.z*ŽZhèŽF´¾««%Œ0Õ… b¡è®ôH#¬‚Ñð¥`æê‘í¹¬‰ ße y1nìÆ/ØõãQüb«u F­è.+{V-ô³IytfÂXÉé
+Õ[æ6.ð¦€v0!Å Jv‰eóë.쩪®ô+b`R¦®õ&ëËîÂn°ƒ,u=/hCè-QKM¯éF±ÆÙ’1¹w¸—>Ü8ç´±CeËŠ'\ÿü†amyãX|ÌÅ—‘dÆ|Ozª¯qâ
+0ÚälÊ åÉ©nê’Ù@aÏø‚Ð@*t?'8ºqx•! ƒ}b¨—†<^I†{éÜ"eþ
+nõÉŽ â‚EƤ0!,›©]Ý—k&¥C£7ÅgÀŸB;!›¼oÈ­ÑòÉmø@á¥Ð~^öÒîÑF|4mþ¥žÎdz>M5bµ6[ÎM‡ƒ¨9üõF‡2ëÌc>
+a/¡0øêØr7Ý^ reÕÑáÑ)e²²²]ùë ìûˇ\ò”P– ‡®—.‡~!ù:à}¿šýw
+endstream endobj 699 0 obj << /Type /XObject /Subtype /Image /Width 700 /Height 148 /BitsPerComponent 8 /ColorSpace 1612 0 R /Length 6568 /Filter /FlateDecode >> stream
+H‰ì— [㸆•PwPnà„´a»¤]
+Ã03;t[e!Éøÿ?©:GwYväfg}Žmé\dóúø˜ Ífó@‹Ÿ4Âv¬þ•O§’^KMPvà(ÃÁWJwÔ+¥Kz“j-*{ÙšX0Kø&mWYõ{ÍšØØú¤p|¿Ëî!é{Q£·šß=©²Éýë»wïµø‰5—èM*QÜpkj‚÷ÂWÇâ¹ "ö› Ûãò’Þ¬lpãZY¯)Þ±yh8EUÀj¤NuiN¥7É“!·nkµÇ»Ð›¹®%ìRJÕyémÑŽª=iwºT:ôzºôXGQƒj_j¨BöyB:cÜbз&©
+ÒëñŸšŸ )q…3–¨Rå½(ÈS’m3T%*µŽÕh4*žžn?.wRøe¼ÜúôˆÂ¨5Ç pIï˜ÿÇÖ@湕²KÈþ ÿÞ£Ú듾ÜÃ}L†Ú(âZû°¡ÙѶlKŽM>;Ø ³“Î?¹%ÒðŸVu-ë¸×dk‘‚C[3•ÑyYµU›ÝŸJIÆSûŒšÑCâÈýXUÅÊwºÀÖb7ó}*ØítÈ?~ûŸ'dÂ({ÚÄvÁ;ÎÏ
+œ’
+±[¥óiå´h8ÿù/yº#»úhŒI¹<vø_.¸tØrv±âý[>dÈ®˜hÛõUÚ
+ãÜ$vC’#:8sb̤ Òâ¥'ÇìUÿäU¡cÞõ=Û¨å×6­WŒŠÏä+ö²êª.»§•öݦ6Ú™]Õz4Ü®À‘ËnC£Ã.î;™ø`³Ø=¦|ÑFõ ÚÞ‡*dÚ„Q/Éa§KB춠6‰>‚ðuä'ÐØö»àG-ÚŠXèÓµ ç<¨ìU”nÂZÍAG*›4PÈ röt;-½c2É“IŽ¯®cÓ’Ç^–Ô%¹¼Ä=îÄ™5r©„ìž_FWç|îýH
+úàëéèšøǧ8ËO‘ù÷£é¾ùxŠ†Ÿ°èJ#¤…Ë®ìº9ÀÝXžÁÎe¬³‹ì¶i_8ÙìæP'å¶lѸ0bWÚìòr|†Ÿp‚]èwûvQg9¥P£iû$‡¹£IOTYÚžÐ\Yk,Îh>ì´¡²MpZû™MÏ¥²¡Y—e•¥¼c2©fæ´ßר‘—%$
+Â÷ZÓcWìÿa[•Lç`WšebXM¬<^Œ˜:TÁ.·mÃaN‡reQÎÙœxÙ:´dW9Äd2[ת»±—%¥i½$»bÚéÔöÑ©»ìÞœŽ¦7QÍ. Ö(²+Šª,­Ñì«P›±
+vEÏî°ËÿÇä¿à‡,M$S“@Ý•Æî`ˆ]ÛÐ 5 –mÏšt‚@£ÿä¼g ³ËßÄ|;ê" ² Ù~VÏ@ÂÒÙÐì^öEvµwD&û¤ŸÓ¼ã'ÚrYR©!vl&o¦£é‰`—+è!¦§:þâ+þ%ë.QMC»…T‡VwB\vñóàŒôÁ÷·ü¥Ö¡Ö^jH±ØHcwPï†ÐÚªP½¼5$­¶ìõŽxÓ`çqØÍhÞutò£ö±ûO>SÝè
+Yó WY±ðng7à”ô£JÀ›í]ÊYMtEf¯Û.ÈóÉRÙM2’ÐíW{EÍ­ù4‹Ýöv³{I$½ Ið²æN­Ñ”ôצ*óy¯fW<e©cH²¥èåøÕä¯i¹ÖÏ›Õq™ºIž ‚Pc
+p£©ëíîoòø~Eu%;¥IzÛjØøJ„›Å2Œƒ™gº[³YU²Êª'WŸi …‡³YØe6_ïØüžïïÁcnÆ“Þ–
+øFiwpQú9(kWL[bŠî‚±…8*c
+ðc2躷ÝÏØÆzDJØõ‡rÛŽOìùiÆÖO%ìòƒ>·K^9 káBëªxXÜ«qnÿ1É®¼Ž {€üÚ:±Käå/¼›»qî‰E±ÂuïQ^žþÏ~Ù.É­â`˜ŸÛÉœìæ«*UÙ¿¹a 6”±ïÿ’Vâ·=Ý';ÉÉôè©j#°¸ûmYT¯aŸ2¥ t¥Ÿv/»9·>$ £ràhÝg×î+ åÅíÎ ­ üç®öS׺Þ×mí¦YÝ…×)Š·Ä¶
+SÊc)£Ã `ÔØjš1Öþ3œ„LkÆë~36B#Ù˜Íõ6¡EŒx±Âæ`Þ iäL»¤¼LI²b&cªý+l±ÎBÒ¢#L$]'ZÀ„‹Í–p௷©ÕÒzJØÈÓßø 1ÊI+¸ŒåD†Šƒ¥¿ÇøƒÁÃE![¶Xê]¸ša×j› rÚÌ:Ü^y~jÿ^íc¤|ãÒô¯w¼Á«v=Ì¢ªÚ»WÚ]—jÇ›3fmw˜ü™äL»©Ôtå óÞÅÌ”å$Õ
+²öka€SÑS¦jT+Ù…k-­íbµ*u³vûšÇ&iŠ=5&^eÄsó8œæÝxr‚4ƒjk´YRé°¶(Xk¿ÈÆV®É`p² 9‚Ö%OW튿¤ðu¡ÝYÆâ? ÷Ĩcø±vß7´ÛzL ©ØuAíŒFí>²Ñ®Ð˜Ÿ­Øç]E¹+CÕ“NŒÄ™GæNíÖÔÖ¸”Ù¾@z}ª]uœwé3‡«Ø)YúLŠcÚûR]$Ï»ÊuæQ8Ô®Ù%GuNJVÔ¸ëG‚O´«=øRB7Ú¥B hKˆl°´¡il‚(ÞêÉÚ}ˈ'¦^h¾ÅÝZ] U” ZHo°Sé×Rŵ;ŒÚÝÚw{¨]]Юwhly#´h\/ µÝƒ5´žy7±ýÒo‰ùŒh /Ì‹Ø(o¢š¬X±ÍýF»ƒò$I¯t¬8ºÁB½ÌL½rµõ~«¾s©WÆ®jéö˜<›ÚFqúe~6ÈÌÿªð¿,0Ãx:[ ò¦ßϱ̷}æç°³îln;¶”ªããOîŠa†a†a†a†a L)]¤Œƒ‚Qc«iÆnMð|ô“qQ˜ u´O£øÍ@1¦°Š%Ç)6„Z§¿÷€Ìãb
+ƒaLP„Ýi—*M·VC%æ€EƒÒ[#lÔO_ïb‘µ;ŒÚÝL·hèM~J]ª‰æõMTƒnȲÅØè5H[n¿økaþ| #ZƒÄ sç"6Ê›XX°ê|K´Ú”§òÕ+3r2Xrh"Š B]K XÐÖ{’d11´óZ&©9¨]õùža~%6hï¥ÎY|^c~¡<äM¿»XæÛ> óBX‰Yw6·ïa|™0 Ã0 Ã0 Ã0 Ã0 #‚)屋”ÑaP0jl5ÍkÛ8þÜê
+·VCä€EƒÒ[#ìTÚ}½›?ÃX¢lé5žûU_1ô7íïO~J÷\Ôk+Î'aO1/Ð=Bˆ›Ú~ñ÷Æüã@F´É$&¹â"6Jž(1«
+
+ÆJ‹Nf3,.×½ŸeJµRöñ7Yiõ@÷I÷6JceŸYË5îàúRÏ"ïè× ÊZ__øþ‹Çr_ØÝŸI;äÈ#^÷3®s.«`¸óœ8aw!ß¡ev×õ[í—è#§y(èwD.•ë1»oŠÙý1²û‹àýÈaä3Q—;z`?VÖ »,Ö¯³Ëú«ú:è§OÄb½&f—õWÅì²X,Ö§ôo
+endstream endobj 700 0 obj << /Type /Page /Parent 1649 0 R /Resources 703 0 R /Contents 704 0 R /Annots [ 701 0 R 702 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 701 0 obj << /Dest [ 700 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 397 479 427 493 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 702 0 obj << /Dest [ 700 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 321 295 351 309 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 703 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 1607 0 R /F4 1608 0 R /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 704 0 obj << /Length 2155 /Filter /FlateDecode >> stream
+H‰ìW[ã¶F_ý+æ‰,,®(É’< d“ yh¬Ý)²MÛj4’!Éëþü‡þÇ<ô\(YsÙKƒyÉ^,‘:<<×ï#¿^Í^­VXXíf6‚ÿâ#Ésæa
+ÙraÒ<Œau?{õM—æc™ºM={õý ûnš0$™Í,^ϳ·ê§B±IÔÞÑ3R°X‚þçêÇÙ’5,! Í2Å]âܤ¡lÂ
+"ÖEo‹TTý'ÂÙ
+u`c“ª0‡õ¼y¨ûÎDÊiÔ¤º²ƒ¯¾ŸÃ?~ÕÁÇ-~[˜…*u¨6‡9¼9—ý¿][±’XõVK¿ð/Æî³kQhR¬^ã[p>ŸŽUíêh[T[| Ñ»Ÿ:°¦oW3 %Ì¢EjâÔBœ™<‡Ôš$ô1É¡u³ÝìëՌآHø8—P‡´rõ¯Iš†øeihð¿,y9«ƒë¬›¦rE »ªØw°iê¾m*(w€A]Ü;hvй®+›*v,S 7U^¾(kÞ= oZÁÌ•+zZ#_Ë–•™!¨5h-ÆRLŠ†ìÒ÷·²?à:L_s$5óG6X .Ù`Õveå:(ZÞ›ìØ5íýõtŸØçl#‰“Ef¹¸XÁ['ÐcytïšízCRØ ¹tBb¬õÙ7ÞÞ·êÖ`ª9$6²Vçje¡L„QÄ6\š)3IzQa^G§StçïòG/± ñi½ÌU¯cu_‰7ä÷"{â’x<wÍ2N¯Ô;u>¸V'˜;¬ÖûxÏ;ŸPvz{Ru†B c/+ù-9ø˜^ßií½g‘õš§í¥uUã3';²¦R÷Çêj`í«‚ÔfêÞm¡*v¿!»V¢=f÷m•ù°crå)öýrL^â‘01X !$&Žó d8t[Å&‹&p%µ4-èÜP>q‹…‚›®¬÷•ÛWÍºà™ªjø¹¿}¹cœn¯§0%QŒ¿TŽc(0IGäoÚìÒUöé"x\ÓÏac‘e&Ê'ŽØÁ_BßHÃcÊúÚ£<©ó‘çI«ˆ7[·>íoŸX°ü@ÈÇøå~cÄ(6²ïâ3ûJÃ+é°î}Q4ƒˆu<3&±éèÂ[õI¢ú¾-קë‡V]3(ÅŸ1)̸cë¦v_i`õÚíŠSÕ_û^1’h$Š'ô›pyt‡öLªªÎ‘…¹2D¸CÌ^þÑî Ÿ
+À`ZÇ–„T!™¢²“)â3´MWÔî|QâßM üzsÅæà÷îuD»ÈÇ©ŠW0_‰
+Ó=—AÌtötö߈€
+|Ž¦˜ÿ £h‰å:ÅÀçtȉ±!ßàÍÙU§îÀCÊá­˜„GuW¹MOöŠ(ºŽ/ÄÖ”uàc¾JΔ<Bœ^.0V¿ÅÿW Ë.‰ÎDÇÕúD\ˆ‘á_lqÊf‹u ƒíÕ®ä3Óž`»FŠfL»¶‘ÚˆdÕÈg¿ðêãý>ÿ&H´Ëé!ñŠ,KÕšMÕi·sbyëŸ[/ð$£ƒä)ÒK ¤²ß.mB#ŒÂ¡9KSp€gõT1&ˆÀpó¸0ÀÀµßwÞKÞ6JŸXô'Åw¾yñhàC¬ž2¾tõH\ÓRO;Ga°D‡Ç9KLšLrvAžÐ#xáè»Bx†ç¢Š• (2„ÿ2:8.E„ (hÒ ,ã•Äµ5B Sá[º+í4ýºv\¾;UÕ|—ý8GÏa
+ùÔ:¼g=à½g‡xýs‰ú€e,7—§ûòáxu@aüG.®]7õ†ÎÐ5¥/hG<3ØÏÑV¾Í1VˆM«?Ï. LQaÓrÐêàäÉÛN¸º÷3|}ÐANÌw ‹¤N€BÔÍôB(ªªaAÍ
+‘}ªrÃÇIa¤gûÎ a‘¢eDämÕXçï£/’Q|Á(
+ß}(6=e—í©ÜÔðôîo8!—Æ\¼šÍæ$^’î?‘Úø»¥–ëU ŽSxÇ¢‡šÃ‰G@˜ÂGïAão«È2”52:ôÝþíjöß
+endstream endobj 705 0 obj << /Type /Page /Parent 1649 0 R /Resources 707 0 R /Contents 708 0 R /Annots [ 706 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 706 0 obj << /Dest [ 693 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 439 238 469 252 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 707 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 1607 0 R /F4 1608 0 R /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 708 0 obj << /Length 2459 /Filter /FlateDecode >> stream
+H‰ÜWÛŽãÆE^õ ½¤ H\ÞDR‹Í¶×±ÄA€Õƒ‘Ý è[³9áe4“ñ?øóSUMRsÙØÎ[2ˆì[U×íÔᗻūÝ.R¡Úa¤üã‘ä¹äAª²íÆOó V»óâÕW]®öï T·¯¯¾yªc·ü  =ûÅz|½,>è?oû‰>ZzFZ¥òþºûÃb˶* üm
+-qî§(aË¢·M*¢~Š0»ŽoÆ~ªƒ\Ý<¨÷uÂL¤­Iº+;õÅ7+õ—yë Æ-Ö6þF—^ ÷§•z)ûÚ¶b!±6uá­·ZýÈ¿|16ŸM‹?Å`÷s|ƒËåâ{±ꎴZR ÑPéã5€u{7ubI_ï¡*Õ"Ú¤~œ†*Îü<Wiè'‰‚I®Z»8,¾ÜMΈCl {cvu@Á-w_¼úý³xŽÌ6[ŠŸÕ¿‘͉l†Ng“²ÛåNŒ$²Âη¤êƒ^ª¡#G&ú´d›æLrç:N¡§Wò–*;oãçZulšBí›ómë%]ã­aŸ>» pd êÆökkµ¼póV˯[–£nPxaˆ8,i”Á˜0 Pªeskë}ÕŒr3œXújÇÓÏk«ªæÈDzYÙ颟ìm/I°fëÖеSr»Œ$m+u3ô,³¬M{6}ÙÔ"1ÖŠýGo'[¨¾QÈu •æÐÛVQÚh{gÛ–p¶²Y~¹rBTÎJÉDîì·Åˆ¬ u-se­öFv±‡0Õàx—ÑdH4’̆$dˆÞ·¦CmÐå*ÓõtÎ-‹,ÏóÕš¡*0¯îÊ®¼!×Õr ·'»ü'ye~–^%–èžUÐòÈ´T÷Vš ùãâ„d3ÆCM‘D‹FaôFÂ(Y$DYÅõ<:©sSXuq›Ài¡ß_Ú²§Í¸¿ÛLQ2²X#
+¶£VÉ—ØY(sD1‡Ú”5Ý2eJÉQ©vYkø\ÖÔÕƒLš;SVÀ0A
+†«Å³-šáê7ïp9 °*òC
+¥K‘0‘LHü8Î3%ñähŒQrÕ«¯ªÚ)È}ÂçУà¨7ÝpÓŸZk
+Ã~ZŠ}¨ß¾V% Š§à
+”4§%þ-PQò·<Ã
+ØÁÕÙñYP]ð…ŸEÿš| ~©­fºågNŒIf |6©NxëÙÍ•Þ–i'´Ç;÷n TÖƒ˜°o¤WÏC]Ž{¸Gãkì$ü²*mÝwT¨³;¼ÓôùYôpìLQ=ÿªØf„ ¢§Ýè»ÃU¢únB‰ðG nBZç
+_ÍUG¡ÌÐoÈ÷câZóŠMj,¢ÆÁXKå[”ÝÞ´uw4´ºé_ÎÁ™†‡É˜ƒ)u4ø{Órƒ&!2A)ä? &ŒóBQ†”PoDÄXÄ®ÖÀ/&³y]ÉCøp8:@ænœ$@_q¾»­´ó–.Òk·Qr¿·î«Š.âØëÚ»›h(Y¦ŒÏÜqeÍžr õÂG^©“¹N•é#L.4èEŽ“ÍÝ>› :aÒ¨Ï6x3²àRÉLùê…RŒ‰èÏÝiù-IyL@,„ø)§­QË¿ÝãoéNÌÔ‡òÀF êw.MÔwŒSïØjJ[³Öow|øû?>¯Ó—ÊÑZ€› žH9”M6‰V¥àGý‰C@ß½²$ùL~ãk!}uðìmS¢ ¯¾ððÅQbŽëà=g»s}3]WAèżŸ¿ ÂÍœ÷DèÁfA&I>@iœo.²¬¬—K®Eî
+*ouÃ#{$¸æWÀÆgXígø¶Å ô}&ƈ?ôµ–P)v?Oi£ýPÚ9–‘s©Ñ"½X¸Q¢{ýïV<WTà*
+(}Õò‹èIÔ\P¯d~`°âžå&<¢%%õ=mè“òдJ–,ie÷âo*nè9Á2AvMÿ°çöZÅÕô~-]f#ÒÙžTìùÖ¶V=4S£‚”;ÄÛê’RÐãfæ¦ï{¡ŽhôeÝÙ¶çNÊo¨Ò˜5S¦¦åÊÇ(1l«Ü0|Å4ÝQ¸_È¢Gr jªã
+endstream endobj 709 0 obj << /Type /Page /Parent 1650 0 R /Resources 712 0 R /Contents 713 0 R /Annots [ 710 0 R 711 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 710 0 obj << /Dest [ 586 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 92 128 123 142 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 711 0 obj << /Dest [ 967 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 319 128 346 142 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 712 0 obj << /ProcSet [ /PDF /Text ] /Font << /F1 1609 0 R /F2 1607 0 R /F4 1608 0 R /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 713 0 obj << /Length 2651 /Filter /FlateDecode >> stream
+H‰ÄWëŽÛÆFÿê)ûkX¬hÞIÆëÄN]x]#+ÀÙÚ…AQ#‰)/
+9ZíöAò}ÇþèwÎ )­v¸EÒƱŹۜùÎw^Î'Ïæó@øb¾šøðð?Q–¹^æ%"Ån’y¡˜×“gßô™(zÞ㉾h&Ͼ»öźŸx®çÑžb2>÷“ò}îLC7’kE¿‰/œ¿Íÿ<™±„™H=w–@K˜¹‰g”°€€eÑWœQÿ 0; <gê‡n"½L,îÅõ}£7˜ ¤r Iöe/.¿;ý—31î°»±,O›sq½/õ?TW±PæÍҙΤø™ÿeÃØ}v-ð܃ù·˜c öû½ë„®/{ÒªH-DC¥‹OÞvjÃ’^Í'¾(Å$ˆ7„ßaêfàF‘€Q&:5YM^ÎÇ`„>¶x£qµG•óéÂ"{a‘ëEعa˜¥Â ‡ ¦qì¦ÁQT½1ªžo|Ê\ßwýÐB)^”ÍR5º×]餈^³¾x.°'1Õ¬…nÅ‚·"ÚRìzŃ% Vm'ì)’¯DÒT7
+Ã@Lá»ç§RcNÆæ%rQµÅßûÁ»ì4G—¼™dG.ùƒGÖ¡oÚFçe£–¢lžó½¤öfYž¸ép³”j#^,Õb·¾8±bÊûa<ì81Ý~±â¼…QnôÆ¿¦7Ÿ }’Œ¼¹)–JÊ.$è‚YŽùÉèÇGyIéIk[ì´êièK²‹¤þ²]^ʉִúŠ@ø|ò[µÊw•~nXÁxúÁI C Š˜ÐûV8¡7ódï@´Ü:=ÊB™¡1sÊBF_½Ãm±uÞéÅDãÅDFÍ|Pà¤R;Ø,9—1‰^RNSÚ3" yµ¨UßçkÕ·É©$ßrô¶ìËEuïŠ×Èù?ͯÞ:SÊv4?\½U»îÏ…&µRÖSÄ0é[˜„¿rÓX(±ß”tc‰Ü"¤:ÇcŠe J9-Âà
+\´X9>[x™
+$2Ìر–¥ËžÏÉ@–ª„ AËÚ¡ãæ æq`IÓ‘çÁ˜Yñ¡@ÄÆó²éU§â\Céºlº›v%ªÒ™Áë÷Ð6ˆዺUݽѿtø…åºlQa¡rǬþhŒSxQŠÔƒŠÉqí2Ðâ"@ã]]é.oú*ç ­.žãZ¤ý¦Ý ,­VeÁ˜ÊÆ™R90ÐIqò|ÃZÅëßÿÖÂßÖüÃ%ûö’m[)ȹͫƒ ¢ZÑ‚ÿ¢…ÿ)¢ML› ŽÜ]WðeàQá òôf…GKK4!UÛœ#Š¶¦WBxÓîôº¥P {é/S®”‹â“žÀÓ Øc˜êŒÔª!lY”MŽ7õá¥ã›TÅ;«sýÉû²ªžzÈ&Dc´ÈXM( ‚A+tUŒI¹6
+üƒBsѱÂ
+mŽ‘ÂÌ( )<”ãG•˜Ì}cƇê~Éøæ_Û§Äûƒô åüÔ^[øg´Oxä¼C“bp}p79Ü—mÎÈõ3*D1a§äz]®JÕq9áÄêÚå® ÚC·»3'MæcÇ5­ØˆÜŠ:³2ùùr[~n—‹âŒÅ} Š•€þ¶{»û 'ò5UƒH^¾7ç“îÀ= $¨u}Iïýìn«ÔÕ½ÆCÉ+Â3» „ãl[nÕÑÆKð‡)“ !0h?PgÃüTI8ú™^¹ ìRÕí?¥ŒŸ’ÉWÀ --<"÷SIVñ‡PºpO.zâ“û7Ïç¿K‚à€žEÏÊtjOø'–7Ã/åŒÒìÄÈcH3ï&*DPÓðb^ :`Wè“þ–ì=ÈÇ$w(¿uÂCú½œ|Ü0œ™þ ã8×Õ‰3”Bá„ öÁzN€R™µ*Ði,-keÍ¿öÀZ}ɱä7qÌpö‘¾“c€#bè”ì×÷MÂD¦M#¶*?ºhvõoùw úÈÑ‹‘¸“mWWWd¢C\œHöŠLËäO;ûÑæ×ÉR€C*Ø>Lv‘îs±7à -•ö܆ü¸ë5UûãíölM˜œÙ‰¥!ÈfpK|>~¨çÞZRÛYô8dÿÚ…ÁNðXšmÈ¢ºøÖqY¢ÙæÄí¨‘º¢,3I¤oIdnê
+Ê8}IJÓ%™˜É‚ÆÀÙÊ ‡e¼-bÖʬö½ÝÝ6®Á]žÎLõpyŽ‘4\Ø“V¼»Š¾­íç`È0îÅøÕS[燨HKFËkÑÝ°Çþ¢SZî†E°œ…õÖÎò]Âôsë[ߊºĘÏÿxLMЮMn3ÎHCäûA°—»zk’ dŠ4úÒP!»ÞÛ“õ0AoîÖNV»a¶.×Íà`gîÀ"YöÚôìlÃÍÜØuv†^onž,åš(Öíó 'Šü¨‘<5ïÖNœæÛ¡OÒ!*Aj¢BÁOµ”o qþE.Ûy±*)Ø)hêÔb M÷œ^3ôHŽˆÎ#†ü}×jEd*:Ø-÷©hwÕÒ@FCÍ,ñë ¤T¾[P?ä÷M˜œ7xàu¼O)o•¹ŠÆð™Û²k›»ÎAQD©=ȲS?íÊN¯DÕj0‘/—%ÙƒÂV£lw÷ç(ßë._b,à )“qRV€@UŠ[ˆ|9æ°ª{±ß(êëé#öpÔ¹m+¥¼¨óŠDÃS@yŸ¯‘F™!"` Ü•@n%µtÅMØ6p˧ŒoÜÇ®‰©L ~ßo©(‹¯#ô¥-)EU"tTO@§éýÄòiFýør/źm—ÃÅ₨R½ÿþ/ãâÕ‚P*j~8»Þ‚r€O¹ +FF æ´P•â«E~&?Mu—×EÅ#”÷ƒ'!×Á-OÙ¡‚ôpÖ„)G
+#˜¬!P%¦°H
+endstream endobj 714 0 obj << /Type /Page /Parent 1650 0 R /Resources 718 0 R /Contents 719 0 R /Annots [ 715 0 R 716 0 R 717 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 715 0 obj << /Dest [ 709 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 262 379 297 393 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 716 0 obj << /Dest [ 586 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 71 285 102 299 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 717 0 obj << /Dest [ 967 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 298 285 325 299 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 718 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 1607 0 R /F4 1608 0 R /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 719 0 obj << /Length 2767 /Filter /FlateDecode >> stream
+H‰´WÙŽÛÈE^õ…~I1hÑÜDQFm{Ƙ`Ú1bžN;0(ª$Õ€‹†K«{>dþ!ÿ˜‡œ{«Hª/d¼ˆµÞ­î=uêåròb¹ „/–›‰ñ‰’Äõ/óÅÌ/ËbòâU“ˆ¬á5žh²ròâÍ{_l›‰çz­É&Ó¾y˜ÜÈw©3 ÝHn})â@8ÿZþu²` 1÷ÜE -aâÆžQ–E­YlDý;Àè4𜩺±ô±ºïïËv‡‘@*’d£qùæTüó?Ît†~¹™;“Úñd¶;ïºýUÕ9 eZ®éBŠßø— c÷ÙµÀsct–¯1Æ× ]_6¤U‘Zˆ†JMÞevhÇ’¾[N|¡Å$˜Ånû"œ»I"bß"£DÔj²™¼\Á},ñFc µG•ËŸéÀ"{`‘ëEX¹a˜Ì…éöAÏfî<8Š*ÆlT±Š}J\ßwý™“ â¼h¶ë®Ø_œ‰5tü[8ØË=E;»úQ´uÊã›ÎD[ tfr£sgŽUŠ‡²1Ñ„¡Éã̬ ”a£u~oœol{U•mªKµº<ãÏå Ö&lìÎûC¢¬a ´[ž¯ÕªÛ^ôá²VLyý.‡´Ç%‚’XÅi)2£Üè}E¯?2Ÿš$cUU¹‚œÛ4ïÄú8$£1Á`Œ.ÜÈKJâH¶m­W]«êú’Lò鸾h’7çt)«R}C |Ž½|­6i—·gvƒŒŒ`SšåÐõÔ";EµÙk¦¼vpÉσðFµÃONü‰„K¤hä.eTKiv¯ÌØ©(TcšéÖŽ‰Ú‰°NeJß"_Œ 3VeþP^ɳ\;¾w4vÐy.VJP @Šï, åŒ:>g:–Ù:ï5ëR
+uvlZøù£ †ô3Ú³}‡?Ÿšëëk $~yýÉáB„¼}ûöÓÕ•3åÕ§ªk·•¶ETnÝÃê®ÈÅGYÕÂEë£óÄ°ß©H—YE¾Y÷deÈiy#?ìT ñP¸ßÛzàõA<ˆ’âzùÞB2 ÝÖá
+ý¾ÃF®X®tMŽ‹_òg&¯û/åŒjÙ‰pJêÒô‰†
+;Ú4lÿ–PcXÊ£%ÙÒäÜnºä
+µ(*t1²—ò.†Ìš¿Èi;.6š‚>—¹ùØá†Ó¬÷ö€òMÉ<™†×±t(KvQúÐ^Êtš™ ²2Íó{±#ö à.à«p™y¨Ø âf—;tûãy&÷á%ýD÷Y$’Kb¥Ë´¾?µö¬8Í”í÷*X¾6<Igˆ%W¼|MÔ©Pí®2Àò„(Rê¨í[ÔþéÊ¡cþ¿üèp™¸b‰»ÄÜ;ˆpÃ%²´ü3q4q«ßb‡žõ§¸¨üÀ0JÖ»V¨µn«šeÃ묢`Í‘jôkjάµ²‹Î°Ö´Í%êãˆiEÚª *C3Ʊ)Ü©x`»x~óôY¢÷ôÈ[‚K"s[1¼T¨ Í!ÃQv&”z‹£Êû“RJ$æØiY~Nô2áwUÌU#Àä‹®ÔYÊ´ÙGaEçæ<Szz)Ž
+áI8¥É™ôÜzóÑ[@Œû¤CårâîRÊOJIB3Óq€(-ÝÿåAe” hÖ“Ó·U‹‡@¾Õž]Íj…££ìΫŒF4êƘf®>0câ ̧¸>öQhS¶‘+m]ýê®3_^AEý“oä®+×µZ7b®Ñ¨¦¿"CŒOA˜÷ªäŠ©Ì‹7-ð¸##Öi›ÒZ6UC†û|!/ß¾.ŶBéYçµûÎ!Œú»Cõ7ËÐçÿ…ÒaÁ·LgšŠ#ÂM
+ &™i'Mòç*kõ-ã:¢ó^~Ä(0“FPwi±jU%€K>œýRèÈê®Ac¸¾¼×ê<'î13‚:jüÛW}xõ
+»†ÌmAˆ<
+~·¼zÿæõ?®Þ‰&«õ¾5ôkÓ•ê#ÿùÜö@6ÉP×xá Ê¨¾ìTÄNÅ£Ë6òó¼BµâoÛð‹"£ðõ~ðvl/Èž†¨HTs"Cª;_z¯×h¨%¨0Žy(ê7LæÂtçž»ˆ½X!t{D¶†C ÙÒM\B„Ø8·¥
+Ù×Ðõ’غ*þ»J Ó,@¤‰VOL£â÷WiºZÅC›š â¨Zíë€:sÀ …\gCØ M] H›Ý,
+endstream endobj 720 0 obj << /Type /Page /Parent 1650 0 R /Resources 724 0 R /Contents 725 0 R /Annots [ 721 0 R 722 0 R 723 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 721 0 obj << /Dest [ 700 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 461 706 491 720 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 722 0 obj << /Dest [ 700 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 385 528 420 542 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 723 0 obj << /Dest [ 720 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 270 283 292 297 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 724 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 725 0 obj << /Length 1999 /Filter /FlateDecode >> stream
+H‰ÌWÛŽÛÈ}×W4æe›Àˆæ]”3`/†á}Hmd'(²)1æE!))ʇäòyÈ©êæe43ñ,°@ìõZd³»ªúÔ©SÝßmï6O¸b“/\O8ø?AÛNìDbµí(v|±©ï¾ïb‘v<Ç]Z/Þ}|pÅ®[8¶ãÐœt±Ï‹_äO‰µôí@îýzRD¾°þ²ùq±f k±rìu/~lGŽvÂ<¶EOa¤MýÛÃèÒs¬¥ëÛ‘tb±½ˆ‡KÝï1âIeÁ’ìŠN|ûñVüù?Ö2Ä{‹o¡ÊÂrdº¿ç¢ÿ§jK6âˤάåZŠñ¿oŸ·æ9v„—ÍãÎç³mù¶+;òªÈ-LÃ¥G»KÍО-}Ø,\Qˆ…F¶¹Â_Ùq,"×=±hÕ"_|·Áð]Lqž¢1Aí"ˆró7JX`ØN€ íûñJè×ÔUÚ+o†*@3¨:ÕØv]Û]Yø•â®S‡Nu]ÑÔe³ëîß‹ß7¢lêjEgab,‡CÓö*ã¿Ã~iݱS<;ð}O`4pÜAÇn]öªÞuE½+ÕàÆZ·fw_Ô]¯’L›y‰ãžœµí:º#6zwî¸;W;ÛìÁ‡>Ù üÔÓV° $»á­ˆ¢T-…#|ûöHmÚ h#ÑÈp×
+vNIy¤Â€þ\C2ãO…Í*á[’¨@ö}[l=¸‚WWRH.xð¿CrV,uS«7`à2öò•'DzoÃgOž±Æ·õ áT9Ì’§Ž;r¦tp Î5òÞˆ¼§-}ÊE§zÑ70yKf5ÁP—\¡ç¶è{UÛ‚Ë);²UiSUªÎÀ”¼iÅ¡m²cÚ'%T}*@ØX¶M}w+’Ž'™žb1[Á%ŸÇaÁeŠÌR%„è*Kö{>©ö"ʤÝ)ñ÷ca­AØôsy¹åh%dŠõ ŒFUÒ“¤¤Ï;ò¿\æÇ’`èÛ$ý<–ÑûEfÍÜbwÍyYªœù:˜µ, ‚m‰!TjÕY!±Žh#YÒ'dµÖäÕEØP¥->Õ"ɲ‚ÓaæP¬k
+cö%ÝIW\CH¤ÁÁŠæ,¶*MŽ4¢DÑ‹Š{‹`IyN€P(/4(̬›æ ê´løöèÉqW6TX‘ÜYKêì¹ :´^·¯šLÝkÝUÓu-¡]°‡
+|/%e¹…x°Ÿdüù9ò‰™2òƒA§a€fÒ#ý_è]ׄšý®«y£”NÑ\Ê݉ºî(ås_Üay±ãä_AÆѬ=H ‰<·/*Õõ ϨNWó;otýmCäEùLÈžk{à‡O[Ío íOÏh…1“„~êô¯ò±–‰þkBû›ª¾6±œÛ tߘtdøÄüx 8=ù‡8ÂÞs‹N¦à-
+JêkÔ˜¯(*-òBe·¬(Öøш{ Õç¢,ÑÉx\ÿ{d™©HÇ@&ZYšï¤¬8‚C a6Õ-§Éyʼ¦÷f:ËkÙ{…îPeT°ñ˜@QΚ½&»Þd×—"¥-þTôûæب Úšƒn;ZIá»®f{>šqžCqcÖNO¶H¥ˆ¢J†ïWÂûÊ}€N¸5’uôœ–/§èî$ñÝ
+µ­ÏT¯Ú
+ú%Î{Uϱܛ%ð7O½H‡Ñšõv§2ûYgá(¦s¤FÖ¨
+ò†´öƒ3ØÅ}…ï.ÀV*:qô@ƒ{T§º'¹HDZ8QïÅ~i^^låÁTŸæŠp<P<™¸ÉÔ Ç í˜ò,Qû¸ œ÷EºçœTήnìóÌpñ=!ÑËô8'Û•ÒDŸ³8ÌoÌ:b4=£×Ä/z¹èUuª3>øÛ*v@=ÁcÁ]±à®G/C‰Ì 8_ÊŒ;õW×PÅ´|n<>7[<(]±II´‹Ùq€\ãpEî#y8ö7ZsOÁ`Â#ukÿ×^è¼ €êÍO«‘ÏáxðÑÀ½!†Á=å8$§¦ÈÄO?oˆ[:²/·wÏ¥ÃÅ××Þý¯éRçý:³ÿÕvæëëæÛ:3/ð¼áÎé™›"èšÿÚKç+;ufoÖ™½YgöhÓ¾ä‹é¼A™ñ©=qÍ?áÎWôÆUWªž\ýn2‹™È%Éšß w #ÑEþò]ufÇsÝô¢;p§M“’2AJÔ}F>²’"}k “PûŒ:ÚÇ”DUáf:JχÍâ¿
+endstream endobj 726 0 obj << /Type /Page /Parent 1650 0 R /Resources 730 0 R /Contents 731 0 R /Annots [ 727 0 R 728 0 R 729 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 727 0 obj << /Dest [ 493 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 284 712 295 726 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 728 0 obj << /Dest [ 1573 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 428 487 442 501 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 729 0 obj << /Dest [ 475 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 221 447 229 461 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 730 0 obj << /ProcSet [ /PDF /Text ] /Font << /F1 1609 0 R /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 731 0 obj << /Length 1999 /Filter /FlateDecode >> stream
+H‰ÌWÝnÛʾ×Kt{®H ¢w—ÿ:N
+š¦,4©CRÑq¤ïÐwìEgfW"MSrQœ‹&ADrvç›ýæ›ÙÝ÷Ñè(Š$,ZŒ„dþ¸Çüе¼€Û,zÖKjÃY££W‚=Ô#nqŽc’Ñxû¸Ýͱm9ÆCŠ¿Ò`žÃÌïÑ_F!y™Ï­Ð;°<®@È$_øäzÊÕ¿$|KnŽ…myØÝ»z*š%|‘Fj‚'£Îjvòá »ù·9vá½›k¹Ffr#Y¾aW›¬ù™V99±¸¸7Ç¡ÁþIÿS`´|Zšä–/ѾQ›ÍÆ2mK5¢¦ ®Ò‚G«Kô§%yšE#Á26’®gÙž`¶oó„å8 Öè¬JG‹ÑûhG†-`ÎFK5GF Êè˜0G'̱¸˶Ÿ©×-©¾ëZ¾ì°
+ß4«0ŠÖXÂ1 ‚×Ou“>6Ùcú³¤OEúnÂÊiUe÷ô–Ìqh®ìIœ35‰á,åægY¤ŠI2è«j™ Q]mdb˜Pq–EgEzϲbBôúÆέ«gùÛ¡b” 5ý¸6=+0žôO‘ü=)‹…é€$²‡wýøÆäi,,a£7EU°£*Ð!ÅKTX*"÷•ˆãm=hº‘'EQ½J“l‘%`hP•Ø`- öͨӔý雹?J¹‹RxEŸ æ£iªìnݤ5¾
+c˜™Ã±ú¤®‚²§v€A\ßÓt¯óFѼ![:¤ŠX«'/QJ-7qÍLÁ*…•¤?@‹ª-SV®Ò*n²âa+<Bì1Ãwœ¨jámô;©;Z‹L((Zd)«é…%ì»”­Õ+„ðÍÐÏkµçù«@P.VýoëìÙ;D Í"0Jò«L ³‚N‡à:Ê"‚\7%ÓPjvCËUŸžÔ§îL]rŠ.˜ë¡+Ce6µ=5R7·çòiËnË9¬H!T‡~+LìqLg%+Lêy¨+kzYÒ.t®´4!<‚·vJùoÚ˜Çan·Ylƒ…æåB×áߧ èôÒ`—ò ìcfh”}ÄV†càE@ccɺnLŸÒƒ«Øu°@w°˜žk`q·1XŽmK$ÐáÂßQh»
+ü›còó™¼În./f¯7C×…fèüÿ5Cù‡6C·­~·ß ){µ¿¬`_ 1>U¥4Î?+eõ7P…Ø
+ͲØE©žð¸Ö÷¦±BaÐÕ>ðV}¢=kzκ"èízJ5æE—Ýs,†>¡u rÖ'%ß¹’õtù
+×y?åÙòau åCÍ6À®O쮟P<qÒd?€…¬H^&jÏñQ„°9Þ¥Í&¥c#]ª`;c9FR¯ À%‚ ÞǸ ·k̈·‚Ž0¬Lšò.­&¯¦ùèLè½Ërq”tq”¡7]uc€ËÂñŸoO§'ÑÉ-ÎÛO`Ãî=\ Õá§sïyÕêz;«#¸ƒví.,éìþŠy‹í‡x%SVH \NŸÏÞc‡+[»‹¾ÁÂeËÓ»¸ñ~öáüb²+ó!ìlÎÖ­ìa‡ŒnΧ“›ëÙüüôc͵  ‡ÑlÑ¢©å]E'Ó“ù´‡çˆxCÆa<¶xÓ°æÑD„ž/¸ #NzÀp§Ü<dæ~ <Ÿ_šMÎ泿½ý|y}üôõ×ó‹h6ÿròé­¿¾ÿ:=ùúv,®®{¡ÿ@(CÆÁPdèv3|yvv5‹Îæ—Ÿ'\¾X=?$ª!ã0d`¿„Œ.'\ôex@WƒÆa@¿£«ÙÅtªdp@UƒÆa47ì)dðÓù‡QÏ; ¦Aã0žã·x­ŠŸÛ{T,ÝÒ4Ûnô?ªX:$5hEÚm(ÏUüRTö!Q ‡!…x ‰*~Q6RÒÕqP„]¡Š÷¨ŠRÕq-ðŸ£íÙhDx@KƒÆa8NWRÁÝß¿¿;>2ÇçFoÿ§cDܷ÷î·â¾¯­û÷ý=Ó_1o±ƒ®váàÖ§fØiïo-O€¥x‚æ²åIœ€Œÿ
+endstream endobj 732 0 obj << /Type /Page /Parent 1650 0 R /Resources 733 0 R /Contents 734 0 R /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 733 0 obj << /ProcSet [ /PDF /Text ] /Font << /F1 1609 0 R /F2 1607 0 R /F4 1608 0 R /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R /TT12 1611 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 734 0 obj << /Length 2638 /Filter /FlateDecode >> stream
+H‰¤WënãÆFÿê)Nýk˜49¼®g³I6HRÚ¢Ù¢ $ZbK“ŠHÚ«>Hß¡ïØ=—!EÉ¢"0` çr.ß¹5Ÿ]Íç<˜?Î< .þáO$Ž›¸ÄièD‰ëÃüivõ¡I`Ùðše5»úöÁƒu3s×¥;Ë™Ý/_f?«û̲}'Pëœ~µ‚(ëoóïg)SH!v4B.~âD®0ašiÑ*Œ„Ô4îÚÚµlÏw"å&°ØÃþj7¸£Un!%Õ Ü}{ ý¯e‡ø½Ã³Ð Ua¹j¹¹„‡—¢ýW¾+™ˆ¯²jeÙ©‚óŒÕgÕ´ëDø1ÿ÷X‚——ÇòO5Ä5'¶HY:¸tQ»¥ÙÚ0¥ó™Ìt9~ä;I‘ç ŽA»|ö8ûj>€á{xÅ=Fã
+ß‘ÈÕƒDêf»«ÛzY—·—7›¶Ý"âímoóWbèA /"?«;òû@µí®XtmÞЧ§H­õ¶»ˆj÷Ûœ9ž¨;ò?÷À]Í7sm¶†¼Z–u“7•%´;q—ÖÞåeÖ¢}ØbÝí²-ïŒÈø=äZ–s‹¢xcyb$Úd¨š(V¬" _ÙðԨé/äÃ)z$»š9Z<<uò µ49Ë&{Ρ®PeÏU5Ú^7Тf¾eY¿Õž³²Ëåéµòƒ#+¢£%±åïØlߘHň7X1Ü'] a:¹ø²m/à±Þ± M‹òf% ×ä»ç|Ÿ»EDñÎSóÙR½(EÈ¢ÄLíÿ€±fÄ ˆ‡Aàj[\>˜!J0,ò'‹ÜïaÔÝ=fËTÉ–ÍëE&„ØJžZÁѧÐyÆǸeˆ~V.ªUý"ÑPåž´>‚ÿXíßd ?¼Øäy§‚»m¶Dïd÷Ç»¢›°=Æ K;¹@:>…Ð3åD£tLJÿ€œŠªûr ?ò–¡´„?>À_z
+™»·Ç)â©‘3IMµz)P$#ÂŽ„-þ´-÷°À$¶®ê&¨ÏjaÙ Ö¢m‚à÷¸_¢P…¼„eÖÉŠZv¾Ûջϖ´"˜éç|ƒ2'öFqg"ˆc„eXÖ&sFJpØ×Ý+¸é€V‰ñ›ª~A*èLð
+^‡á«'ɦ(ÅA¬$YOÈ¢ÌÏå®T|j9œJºO&'Hj1· ö)†µÀ‚Ô­PyîsèA†­
+:·Ü¤f†6Ég„»Å±dç Ûw!qš
+$yFnƒ²;Àõ°à«©ºT«=´dSJâïyÓ³±YPŠ•F¼“b1ãþ½Z*jyëU'~z¸»ÿ$ .PGUNEƒßv –¬ž:5«Õád´ñtßTaÝ!oO8 hµ2_ãBôX0ßmPOw /¦1GÇö¨ñÛÔ]ËžQåè§-GY¢à©^–£—ì‰lthJ<“±ñNFéX'£†-«ö¨ŠÏÅ;QRò¸¥Ám=j­b£›—š‰½Bê;9ᣌZŒ4)ʹk(Z’eÅaA’d[ÒŽú®Q¾Å¦­· ‚ÓÐ/5=»WmÎÙÌì±êã— Ý6¿6%ÝëK“”¶p`9Nwh×åŽíÜ:Üö“GâcÚñ ˆÑ: ð´1š9Þ= £á4Ðü3:ÝÄÑÁäówŽG¼µ9ÄÑ%
+^?>sŒÃÒaHð\,=4%V¥@¦Dç÷¶-¸Ô[ÎóòñÈ‘‚‹®Ée±Øø6fãÐo™oöuYÛö+lÃqêe$ÌNT˜8ŸÐÁÇTl&ÑðÝ|~s5|’kŒ$Þbîp‚·Öý†÷0v$îÍÕðqÂÙ×4Æô£SÛOœñOò|=û«C-?áé%<®L*<qÞ?wÃGy}þø¬À~‚Ôüíl4r—ð*ýÔ#_ž”bêü¼Ýü(F»ß›zò¾Ëûq‚¿ÓfŸ:Ÿ? ðvo÷æù‹b%UøvŸ77W'{ÇþàGó”çÏ4pPS‡ü¦ú¡T¦I–çýs?vô[èMœŸ/èämä¡å• ©Œ½¡ÃùãóÔiêÄú×: t(< GÌš|õ«ü{Z÷-à'Î'¤N°Û‹z¿{ʾ´›]ž­š[Ìw£¯cÛë4"ê“"LOˆkU=ˆÐä µ »®jnµ+‚Œ÷N„I°™×Óž4u~Ö“tˆ ýnúÕ­±ž*çÓ§ÔJ˜ÓéVbâù;Ç=ïˆÊÿ¡YõÉë‰sDûÆÀôY{Žï'1Ègìˆð31ÁfûJ\…ÒW¦Žg%Ôñ‰Œ?)sáÐ*L_×@Õ°õƒ!©ñ¥jh#Ì`¤^­Õ—h– AúžýC3]Qa«]T×<ƇÎÚôÆ×ÇCã:ØC¹•þsÅæWCÃ*/“AýÄ°Çyp)"÷ð î,vH$ì~I4u]æHç9+;šip=æ Ìa~ñ¢Ñ|pGcQ ÚvW,:´èÓS$–ìwD£âx‹cΛ0WOzñ»ç¬ÀY$Pe¶À)ÍW%Ž¢6~’³94›€ ™éú骮p|a¤9Ë ¾Î³®l¯ÍÃÏw‚Ñ ä3Ü/‘A°ÂŠÇÊtÃ÷Ï 7§3Ìó Ñ›ãü„k_4üm†Ó”Ywò˳ äU¶Àù +M°2—pÐļ¨èè;¡'ÿï-)r.(¼ +‹çü®Àò\…[b‘Êéu ¶£èòHšê‘@Ú_ÈÚNøæ|²È?䌄r GZædXsÓAsZ’æ4!ÒS#A¤ˆz ž-JU*—/s£ MO­«¬” ž/—e‘Wâò-;aTÕ-Ô†2ßcP"%ÿ ¼¹Dß&¦C›ÄÊÆ
+•Gr†yþlÄ؃üIéº@ Ãçø–Íœ:ËN‡«æŠºª-Ê1Vþ± @9ÞÚ!Nûj ¦°¡kÀŠ’ÊŽ¦8 u÷$çÐÒd<-dª9À±4P€»dAè‡ÿ
+endstream endobj 735 0 obj << /Type /Page /Parent 1650 0 R /Resources 736 0 R /Contents 737 0 R /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 736 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 1607 0 R /F4 1608 0 R /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 737 0 obj << /Length 2688 /Filter /FlateDecode >> stream
+H‰ÜWÍŽÛÈF®zŠ†/i-’E³x½É®N0ðÈ—¬s ¨Öˆ1ErÉÖÈÚÙwÈ;毪š"G£±cÀ9$0ÆjöOuý~õõ÷ËÑ‹å2TZnFA¨&ø‡Ÿi’ø“d«ùbæÇÉ$RËÝèÅë6QYË{&ªÍÊÑ‹ou׎&þdB{²Ñ¸F¿è›ÔGþTßú µŠcåý}ùóhÁj>ñ1n‰?žÈ%, dY4šÅ"êŸ!fÇáÄ‘ëI¢VGu{,í3¡6$é6oÕ«Ÿ«¿ýËÏðÝ`mæÏtîMt¶}®n¹ýÍ4 ‰tZ®½ñB«ßùVŒÍgÓ‰ãcùæXƒÃáà{‘è–n5t-DãJà ¬ËÜÔ–%ýi9
+T®Fá,ö£8PÑÜOþtª`ã4QmFß/OΈl™<ôFïê yZ.ÿ1zñçGñB¸Ø‘óÙ‚âÆgõdóT6ãNg“²\&NŒ?™Ê
+;âœóiD¦ßîëºj¬7Nü¹ÎË;õјzœù½Qy ³§˜WùŽ6¥ì…P—^ ­ÚTÎ9Â>ÚvôÆä¤2Û6UIŽÏ#‰ˆÌVíºÝ«¼0jmîsñéñåSN†ú]!îÃt‘~õVœŒª5¥ÊÒRU5twâ'º8ªTù.·f­Ê½LîV¦Q¬‹Þx%nV•¥q–e6¯J¾|£Kœ¿ƒ#Jes˜C§Ú óæؚn+•Û¡’áII¤±Ó’F¤f΂#­Œ¸v@ K›§…*+«l…W5ió²&UXQÖ}Oú4ÊÜV(Ð\3Ù_Dæ[ªØИ_÷ÝuöÎõsµÚ[µïî†\7D%QðîŠ'¼>9n‡ŠP–]‹*S²×tcTkÚ»||ÊqÈŸÑ%aìj1“‚ürv‡çÙÍjM{”šŠ†oJVåUfø«H Umx¶5 fú„÷ð¢¡<B‚Oà*„beÈí¶©ŠI\âƒG/•Y'âÜ_¬BÜkŸüµÉï$
+|†Ü%•ŠÖç=5çvSV¥9kg>à[92¸ð>Í=Bë"]yÔ` € °ûåÓwuûÐEåÍí+ÑöÆ£vðE@ßÀ²{b3Ý}
+:Weq”‰ÿÈIA"?˜Mº/ìˇÎö¾ˆ8<új좣ŽðfB7Ÿ_Ç'{çôÒ <‘˜oÐ <UÍð™»ç9S¦+ ÍsÁ=B‹¡æ…«L^/R¤sÌ ß®M|Pyî èïDÖ†òJë`{êúÈq¸Ú…jŒ…%õÃK@ôÎÀºñ¦P¥º“_np$1æ?”Mq^âk ­cê±ãÒ¢¨Îö¸;+‚îqÒÈ›äPú˜w—kÀ*ì¦ææÄ r°Ê[ähOjƒTT„1—æ>Ž\Ÿ]q±Ñ'„ ÎKF££¯ò4DIÖnÐPS q_&{ö…|vËá/@UVѼeÐÚNÒ…'‘ýSí„•>ý&úr'š÷8 —†ëÉñ4döÔ˜õ>3ªnªU
+š–#¡Èì#ã|ºe¿‘ûäCþçD‰´0?'õ°5ÒT¡»‘˜Ž ­G×PÒ«:uËòƒà€tÙœS™¦ÏCpêãAûàYм£—” »ŠÉ'%€)ïsŠí úî
+²÷ƒç«Ÿªs*Z{Ni@Äg•Ì~C到[É'7c·©Åõ±Dð¶Ìî‘ÒG%‡Olw òìbÜ’>n®ÿˆä8Wuˆl¹s“]²Ïºk KµñèM¥,ÝNÏ¢v—EpÐBªîZvTˆÂŽˆgèvø´Â_)eJv¢PPB®’ð@(<b¤†¼ âÂË%½®Ä)ðÂxTD“A_ËŠýº#Dü4ôîÃÞBp β-ç#ÈM•}4öæ5×êMZìPˈ¢P¬]ZUM Ê?Zÿ²›ÓÆ¿VÖˆÆäƒ@èjԊͶ¢].óª30K÷2c:Ц±œbØfJÿë>N›jm£ ˜|T¤2':¡,–wfW‰ë‡´²‡›ðDJ¤‡›˜²Í¤¡Ð¨tÉ’n3Ýœö"®SýUÜ2J®‰íz4qühÖqËHšç•ùdMS¦ª`¦÷M>Éä2ѵÉòÍQ½÷Va/[ÃgÖ”hSö4Ë
+endstream endobj 738 0 obj << /Type /Page /Parent 1650 0 R /Resources 739 0 R /Contents 740 0 R /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 739 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 1607 0 R /F4 1608 0 R /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 740 0 obj << /Length 2181 /Filter /FlateDecode >> stream
+H‰ìW]oÛÈE_õ+.üÒ!`Ñü&edlœìÆÅîÆØHÚ¸9²ØR¤¤¸?dÿÃþÇ>ô~ )Åñ6 Š.PØÈÑðÎsÏœsùb>9›Ïða¾œøxø‡_Q–¹^æ%Îb7ɼæëÉÙE—AÑñº¢™œ}ÿÖ‡›n⹞GsŠÉt¸ÜO>¨«Ü™†n¤n4}
+’œ¿Îÿ4™q„¤ž;Kp•0sOá
+~áONŒ·Ï[ <7Á›ùKã öû½ë„®¯:ZUÓ²—tñÒÃÝvhÅ‘^Í'>T0 âÄ ÂÔÍ2H|7Š
+áp¸ÎÕ€ŽÜ2\±‚’¿–¹<½­å™ì@#ñKè6<± xi¿ùö5f׊àWïœiâòóBâ\;°iMo8Œ±‹¹6c®Ñ$ÎJmÁ ©ú—BèüŽÂÉ›¯ÞŸ]þ,HžœdìMû÷LS߶)u 益4ûíž“Q¾pÙ#n˪Ñâ˜÷&tºÝálÞ9†©«®×MÕÜÀÒ´‘q;~ÈÛ¡4‚Ãá $£Â4.úÊ4”‡­še›·º„i{~2½–5צëA¬˜éâáéêiÁ¢®tÓwX½ÓÅtóÝUÅïQ:¥óA­ò†Å¶ª1¸&V«4Íõ¢‡n»¡è„?þ
+vÕŽ’r¦)ªuH¿x½c¼®Õq¹œçHÍ®÷1j%ÿ1µQY¡Öüâj`þç€û¹EžÔ¥Yö;äïø>VwE-µ /ôžf@…_M­»n͸HƒwÜ%ò¯jŠ%-ïŸÞÀ–¹’0W"E<ª:"‚êÐ ]óÌ’°El
+ÿ@ý—z™oëþüSE@I
+ŽˆÀÇ1DÛÏ,Ã!óì‘♇-ß9SG¢Ì¢¥"‰4Ǭ}lh|d_º>¿¹Üð¸.ª%R, .‹@¦¡ŸÈãÔü…êêìÒ¡²ÒIÂvKˆ‰.bIÂ7âhÎpt;NÑø·×S>m(þA–ÕD~ß>µÃ„pȆ°ëÁ}š±¾áßâGêÇ!A•äíO…wÑÊ`<EÖv/—Ð0¸b‹¢CÔŽ·1ïô=Pö"*ƒ:ÞnöukÐn?Rk ë¼X¡^œâ0šåA]‘Œ ,‘Ñ sã,4øµêd=dèô¾-—y[Šn 9-öÏ=ù™´„WŠT ÐèêÁ$LM&h=º¨±aAgáv`]ݬzñìÁµ^;Ò9@^`gÐÑžd—Ýo5:GŒšX4'”QBØÇã.]ø{ŸÁ
+OÙo,š+3˜Q]"øí:¯±áZPÕ“U%±çr³5Ïßb5Q@ý±Õ$¢&37qˆ[ð¬ÚäeÙâvÑ`°G#ëEÂë†$‘Ö»DËá ¾ÂtOÛHäQµ~6|-yð…ä€[÷„Æÿwü$øô“ðËýd°“™u¤?pBoæ©kõMîPk\×$WŸÚ̃­ÛWÿÐÁúáh3l2 ›Ìh1l0É`0>LÈ3Ú‹˜KdÍ%dGÀæŽØd2™ÅggI”Là@$zò Me‘ìïG"O“ Ÿã„± ošA†y…}Õ¯`½­«M­xë¡N0L)ÁEA>yåÐÞœ¢²‘Š‘gv
+<<(\[;¿¤ŒXeÙŽƒHø3óÇe)ùX4,€ˆ%£wláf°UP¦¤[z ߣNŸ
+°cÐଜÀJÈ]ŸTâ
+endstream endobj 741 0 obj << /Type /Page /Parent 1650 0 R /Resources 742 0 R /Contents 743 0 R /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 742 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 1607 0 R /F4 1608 0 R /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 743 0 obj << /Length 2109 /Filter /FlateDecode >> stream
+H‰ÌWÛŽÛÈE^õ}j"‡w‘ƒ]ÞKYÀI
+&ÝnèeZ==\j1Ó6õÓo»afd‡EýŸÍ|¤×zAçÕL¯ø¢W,[6±¯ßŠøï2*ÅBä<ÿ™N,ôb0që÷f[ëáÞZyÒ$/š eJÊžãòÂéÆþE.»Í¿Åd8a2AëHs(G2ïÐ2F»¶f €öP¯r%Ô± E˜Ýø½æO‡Ú(K½"\ëîبb7Cäí@Îf¢P`à]Õ(†•îµ·ÂtT0´aØW”<RÝ<:n Ô2&R
+ÄÞ¾t'šÏÇùŽ‹LØù¾ïÜÓÊLÏ_2ÞÝ­3xMÈ<¦ÿ _,ƒè,õM˜ÂÌק-Ïgâ6©þF>!<Ö/J’Ï
+ª&VOý…Hõ\ÙMˉN®%°uešâ-Ô4>·Ýö¥d ^s´{9ü@[¡>¿\5I¥:/TG¢'Ûú4fœgœHKò§-§ ÄTÉ•ÛzÙsµxˆ#nW d›vî*ÕÔ ]²÷§µ¦Q
+Þç Ü%­æckšãÆ™´¦6MuGžjÈW™¦&•úÓ©ô„ï¤<Ùøð$½jqéI“Œ{ÒÀ¿m–ü«Üy“ÌžõŸQy¡ÿeöŸ3PüûÏðƤŸ¿ÿ¼0Ø—×άñÅôŸÑMAö¿í?ñåà|2†ó?m¹…c P¿’wŸz´@/éM#ù€
+
+ö•V³!xV¶©}A/©þH1fÐÊ·`H©l›ì{+µm”)(°’œÖtD‘±>‰>×¢¤»´ô.U¿kµHÝ@(U=`$EJ´„š««}ZU­*‘Û4"½pÒ£´g­±¾±ÆK™.˜ÕË–•¨ TIA§ò¥RMVß$Se\
+g\ÝÙ®-ÖSËÆù</þ`Óœ*ymÔ—ƒ”}ÐïJ˜ÌQâK¥ÐL5%n.˜¸ü9漢²$B³âÔVJaÍp,ê ÿR)û¶R¶cmŠ=Þµ”„Tþ¡°…ûVºôX~Y‘ÑzCNäÔM .i)Ï•¼šš±‘(X/þ3
+endstream endobj 744 0 obj << /Type /Page /Parent 1650 0 R /Resources 746 0 R /Contents 747 0 R /Annots [ 745 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 745 0 obj << /Dest [ 748 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 334 358 355 372 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 746 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 1607 0 R /F4 1608 0 R /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 747 0 obj << /Length 3094 /Filter /FlateDecode >> stream
+H‰´WÛŽÛÈE^õ¿¤ Œh’’(Ép˜;N|ƒ­8Øxƒ E¶F\S¤L6GÖ~Hþ!ÿ˜‡œª&)iFñÆÁ. šR³ºêTÕ©Óß/‹ˆBZ¬aDþác<›ùÁ,ˆi:Ÿøñ,Ñb3x|UÏ(©eO@uR ?Òm=ü à=É`Ø-wƒê­ö†#¬n FŠâ9y_üi0 sšþ<Æ)£™î1‰-^Mbgê_¾F7 G~¬‚-÷ô~_Ø5¾‰”ñ`IÕYM—Ï/èoÿö†<WømâOTæ*Y_Ðû]f6U.FFJ©7œ+ú§üÇ$| -
+ü‹k|'ìv;ßù¡ªùTÃÇÂ4Žô± ]Ò~µK7‹AH ¢IìâFS6£8ôÇcBŒãUf°|¿èÁ…Øœ¢q€:`Dàåâ'NظMØØÆ.g²êðœŽGþ4:4˜t€v¹ËÞÔŸ*zšj/Œ–…ÿïôi¿5õwOèÚ›ý¤i៙ډں6Ø0V«¬È,Ì2äCÞPµ>ÏîYïms±¼ ;gCçëUYX&¥¬x"hOUo¶­ÝØŸvùâr&ÜëOk/fgÛ"ùGR+oŒP²Ûïîû7Küˆ­9øf=|³Ö%]PâÜrM~Á£pÚ·/Å/öP™<Í3vn¬ìwôt“m̶ò&¨©Ò‹T»+gˆ#ÅRmµõBn)äç+D}a|È%·³U¶l¬©ù1TGˆ¶øzÄ¡Š²0RŒ÷à:*Óàž ‡´vy]¬Ñ°µIlV”T© ½%ÈšÂJ–k_kKºÂ¡a šZjЊɺ*‹¬ÖlçWem™’W/)É3ب»Æ>¸Ø÷CßìŒ-S½§Z`o<nèí¶¬,Ý]é*¥Õ©ÜRÚ.my¤È TB#?øÑ“ÕÝ~™){sl4Eª«³(äWüc£DÉÕ1’âüGõfxšª6Žþ “GLïM…#¨nÄo æ gBG’j°_×´3yîÓ˪ žaÿe–y¶ž*¤¢¤M“¬iSVòv¬ñ¡:ÊøE(#åªÉó='q•Ý6•^憶ÁsÇ™ê±'tyë ÁÛ…©´-+Záÿ«¯n†×ìõL½xÇ_m´µÈ4—OïË–ð¥Ûd¤Œ”3Ú²{8A#AŸÞ/€ã†hWìõÒ¸> T—[¥Ì±¢{Ä)-ƒ2cweÀŒÔðúÅ;€½Kíš«·áâà‡“jÀÕ ¤¥Œ”voç@kë™ µ+~©hM¥´Ýe[›YŽÌ¸3—e#<Ýùæ\““bÕyø'F{ØŽ…£²f.E:ÏˬöeC¶¤¤á'ëE<6ÞgMö³!óE^°YqKÌl!8ŽÍTXÏÔû1˜­XʳO†î<ÁéÊ) í¶¥]›q@:G‡yB]š34nmá(؜ߩ=­ËøV ¨™BY­æ‡*=¶AÂð±§e‰ìK¬Ü `¢¼´T®èNW™f²IÍÞpphïsVV¸BfŽC«Væs“U&å4¼(H§iÆ?]œ¯Ò`tns\ÔB—ì4ÂLp¯¨ÜâV×Js„ΉçJ@i2m:oêo.‚3}¾@š×º¸5„ºL*£­¡Ôu·pÉDÉœ
+‘ú½k×­iiAhpd&+@Ç5Ó)8Òrn9[gùí÷õÙ¼¹v÷³LIÌL &Y³+ ‹‰Ó¸ï íÊÊ yùIx’ÇðHù²†Êá.sæ­öÝ-ìº]FV¶Bh¢ Z6Y.E°ÌËä“ûí¢aíë…‘^5)r‚Øå•SŠ½7­‚.,¥ÕNTsˆOÎ'ó+ͬB¿Ó’.ó'nÊ;,ìEƒ°ëŠõ
+±`Á3wø©ZûÑüHõ<dŸžy\°™›K­-r<âzU˜¡ìCA1²Sð÷VYèüäµÒQ ªÓcêx€«¯¥˜þ,ÞÜ9pÌ;Ä£PV{Ú–¼ëp¯;7†7W›¤¡„zÊá¡GZØD>\°ŠÈ@«€Dpš\dŽ†Þ\:U0æœr½Ìßz]6
+Áêz›k6j¾X§¿d+X°æj7࿽‹ @,HE}2Å,µ Ð¥[8ûä0œ¡‘b.<~NÏ:"ßl‘pIÔÿFí9ž|ÕZÎòú‚ø(3ôèõ#bLMe÷95nb2}AHù^fUWö¶Ëˆ¬4‚…ºqỀdòDˆñö–PÓé«ÿ•çÓlµBúAŒ…Þˆ~²,VÀkÈטÚ^ˆ^G m²4ÍQ/¹®Ýn¸P7«UöEÆ䶂£_xÞôÒ컢º„]Ì.æXsžUƒùµy‹wCß5똹 S°];”›;D»Fœ)Gï<¡d—qž%Ú >©\I:ô\ªHFkæŽÁ5 ìNäƒ
+ã3+’¼I%åÌRÑ~ÎÎy}8Ùedº2hsêF»Ì,&fsLì-&›ƒËEÏ}Àúš8 è)kr³]óͨh6KŒ2n’sùQɨûo•…z¼Ë™¾WZ k@£q:ûeE<ÀN´˜µ‚0äz
+=G–êÈýscŠÄàú"³–+Œ#÷û«]‚÷’²¹4> ×)']Ö|Wzë1·½{CŽU™MÙ»Z9ú¬µnˆÅ—fK©<„¾M•ªèYßYÝ
+ØÑô^~'P?·{üqì P]:”ü`|¤N¨¿ÞwäxÂåŽÊ¡_jÂì)õÌ#ƒHð“ÞBÖ‰ËF0¹7í­8‹{V'áu•ö+DøQ%E;G²†x´¸yùHnnÇô
+z‰çHP\nÞT½ôøöuP6ì7Ïþý\GæBÑ‘ºùÃ_߈è©?3Òc¼Ó‡t$=ÏŠƒ‡ ® ¹ÄÙl‡—”µnûá-Úw—YœdeÖt‡û¿qÆ*uÝ“5'Z˜ H±¶2¤še»£îèÃâÍõQYn>ܼ^`Vš“}N_×äïF®._Ê…åæõõ%®‡õÎñÚ/c¥†a ˆö|Išx’!žÔt””4ÂDŒ‰™Èž¿goïdÉv`èB°¥Hº[½1“iå–¾qY®óªÊE=€ãa]ÑÊGzÔ΢E‚Ð@c§ª“ˆˆªæméNoá}8“Ûä–Kî" 1>ýOýHqM{À஽¸«¥ûD ZgØoÛ3H¬Ã'Ý Iýb"/ö’ðF%w³Ap é§/*…oB1„=s9ùdè$6I:ž¹w³U–ÕÆVán‡“^øBGØbÙN@/ådíTœ ±{ýðM_Ý‹]žm§³=¡X/!2!;™K˜
+'Gï@¥¦1¬j¸Ÿón»Šúc8Cº¤ÿéB+~;ß‚²6Ù³«Ô‘çøí 3íÝÿM{›zl€Ú@ÓA$&‹_ïxÞ{ž·iÈ’ò¶û"-–ö(õŸÇ$tlW”( 8ªÀNàñk€M‡´¦y}¡WŒ‘Ì¡G`LMŸù?fzÄó‚Z qà,=@Û#ShBÜtêø¯Ù«`ìσ€V²Áq]‚“|iO*×tîéõ´HKTQr‡Õĵ“úNP$³À bè¤Ük„ëlj«ìÇç»Ó;
+endstream endobj 748 0 obj << /Type /Page /Parent 1650 0 R /Resources 754 0 R /Contents 755 0 R /Annots [ 749 0 R 750 0 R 751 0 R 752 0 R 753 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 749 0 obj << /Dest [ 1006 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 469 716 510 730 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 750 0 obj << /Dest [ 761 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 77 553 98 567 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 751 0 obj << /Dest [ 1125 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 218 553 259 567 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 752 0 obj << /Dest [ 1125 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 218 472 259 486 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 753 0 obj << /Dest [ 475 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 123 64 131 78 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 754 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 1607 0 R /F4 1608 0 R /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 755 0 obj << /Length 3101 /Filter /FlateDecode >> stream
+H‰ÄWÛŽÛÖE_õêƒc‰æ)J8¾.š6°U hÜyfĆ"’eú!ý‡þcºöÞ‡—Ѩ1š".âŒÈÃsÙ×µÖùz;y±Ý.”QÛÛ‰Y¨
+°'Ö­ü8,Š;, …
+Y¹Y·u
+·
+ôãœÓÄ…ˆœÈ¥}.Ýò.çô2¿H~ƒV0N+쓃$¾ä,#JBcÌHÁÛh{ÍA“¼›ÇÎB”+<B…‹È1Nä`jöß`†xÉJó$ t ·éØtǴ溜 Ô[’šI0«ôˆ…Ñ/%D• ´ÏS_Ä9ù§¾GÌ'$ž;ô¥˜odƒFùïоó­†"\‰õÔæ3êß%ò"©çôÃŒÉãÛÖœ
+F—‰&šCkîÚDÓÃ录Õw”bÒölÉ%nýPzÚ$[®eÒÏŒŠ -jZgGm³˜]8œ¢‰¯+ŠêDA&>å¦r‘£ÉW.bï\ÀØ¢E|fìo8?ï\¡®]Pu—8·ÚHÑ—éã”b‘/rç]ôäñp#•ÊSP›^Á?èü#?fIf2­£õz¾'1I<ž;¤íqeP
+WA"¡*€$“o=ÍôãEö¬ñŒ|ÑçÔBi0ìøêWtœÜ—¢š©ª‘WP&§¤d¥sw& Çœ3pÀ$Œh\©z%é˜Eìíüð.™_ës¿FiCöÇ¢Íqq"Ueti§g†ƒËÇ鶓%îÍׄQ½ùŽm@œe†Ž¾­j"G‘ûâfã
+И;aÕðuŒ9V¹A´…ö
+Ë’Ú:m³+øþfÆ<øÕLý4Hȹ€Fzî«•£Ñ~[X-ßG¶?â…Å
+©ÍØèàÒu
+M>æŠ 3r6ŠÔk}y×$Ë>uN8»‹µ¥sæ‘]ŠûŠŽe] 1€ü¹Ÿ
+wôÃW
+0
+endstream endobj 756 0 obj << /Type /Page /Parent 1651 0 R /Resources 759 0 R /Contents 760 0 R /Annots [ 757 0 R 758 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 757 0 obj << /Dest [ 475 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 146 730 154 744 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 758 0 obj << /Dest [ 475 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 164 661 172 675 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 759 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 1607 0 R /F4 1608 0 R /TT2 2027 0 R /TT8 1605 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 760 0 obj << /Length 4040 /Filter /FlateDecode >> stream
+H‰¬W˲ÛÆ­lùS\ R„ €ÚY’årqÊ—•ªD7 ^¢èÉ?ä³ÈéîÀËKɱKeùóžé>çt÷›ÍìÕfª@mö³ T>þÃÏ2M=?õc•¬W^œú‘Úg¯Þ¶©Ê[žã«6¯f¯~xÔS;ó=ߧ9ùÌ>ϳúo™ãFÞR?ú µJåükó—ÙšwX«Ä÷Ö1N‰R/öåÞ ä½èkËVÿ Ñ놾ã‘k?UÛ‹z¸TÝ=¡6vÒmѪï~X¨þ×qWh7[y+]8¾Î õp.º_MSò&‘Ϊ㮵ú7ÿå‹ñóùi¡ïÅhlÞ¡op>Ÿ='òÝÒ©†ŽÅÖ8Òç×å¶ëÀ;}¿™ªP³p{Q¨(ñÒTÅ·\*¼q™ªÆÌö³7›ÑQ€)þskL¦öÉ"¸åæ—+7~,LbßÃÿ²†<°šœ±’ûÃ6?>ü”Æ~ ußš}_ª}ݨ¬,½@çõq[Tf§vYg”ƒHuÅѨOΚìE^ŒtÙ›V•úô6kvŠæÐp¨ß:î?Y‰ÖJ›
+;x©ÞeÍ£ã©wƵv7'»ârÁšÀ‚ædª]Q=©ºR0³Êëª3Ÿ»…ª €ç;'Ä®Ó8KüªÖº¤®Æ±\špSˆ;l‹f›ªËº¢®Z•Ù^Õsÿv)‹Fýü~Nñžç¥aH%s˜cV”4•ô`ŒºzQd‘’ \
+~Ä ƹ*:•g•Ú•µmñd äôv~D‚c+o wêÉ^äŠÅZkMäIuïðY@ÁC—Uœ͟åžÁtÏ`¸'xØv ±¦1§Æ´Æ…Ø_b>]ÓMÉœ"ÓÓ[Ap0ÁIÉêÍ»ïþ¡
+Ü ÞpÈÂ|"Á=J߉ïÌ=RÃ#©þìª5ñͽg°¼&ZKd½b*IN¢ß³
+ylÑÖG+Œ.ŸÿÜî/±QÇ¢ƒGeCu*%ÔtdÝÖYAk!©þ¢%“odIÀù¶¬·7f-Ó¯Jàku-mÀ+ñÙ
+Î(BjÞg¢mUÚ¯'Çõ9ÕÛ_LΪ±†¤’™s'Á7ËÂR³¾ô²ŒE´
+èЂÚJ&Ö]
+l4r/axôòYṗ2ѶÄ4jÖg—“Hé£Tš‰z+ùôÓã¡šy'³Ô¹
+€s¨N@1¥9jR
+ÛúÈ‘œT;oLÖÑ="=ÝpןÀ»¬ïzšëF°Ë%ÁqºaUªÏåað*£Üš<ëm·„sæ '­=ó…@~Ðì"SÅx}­+žhzÄØ_B¢96É„£ã&8Š ÃTùxý­éΆ‚)Åž!ñœÖÚÁ(¢ˆ&½yô° ¬• Cná.j2ØßûÆäuƒ SàJ§y%o‡Ê…l€› ¯Ä­‰Þ£†x72 ¬¢«ÜSÂÆmȈ=ÿªØüöñ‚£ÂE¸›Ì8½Î Ó‰ké”ïL¡aEMÁ2¢’~Úî^”FZCÈ M¶ëað/QÕdÇ5j2ªD [×F¼Jsž,ØÒ¾ðÁ²ˆ
+L{]$±'/ÑcJ†¿™„“\Qà¡4˜‡”Kҹ͗) –1ÎÀÿG{µì6nÁ{¾‚àIlÁ2mZÚÛ"v
+­ú夤oâÜôÊ[•ó¨iÏö? ÔÆ4â“iò¨(LTÔÙæµÒ™[HDÄ?Á‘^‹D˜s!ÉTXö2Šßú_Q'…3‰,3LeñíªÎj§8˜™övŒw±x™ ÆEww³XÿVÓ]o»Éé{.ŸŸàÀgUêu÷ql&›<Ü ÒqN€”g©[CjEè˜4J¶l“.¥‹¬•Ø:¢ú&”keáÔ†µÌG‹?NØz¥°ò½/Ò‹1Ž½?Á˜èïû»h×¥¡™Œ›#*h7ð(¸Œ)e6u‚¸ÊyÖŽæ°†¦1ëm*Çö¤x Æóèéû/ÿ
+endstream endobj 761 0 obj << /Type /Page /Parent 1651 0 R /Resources 762 0 R /Contents 763 0 R /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 762 0 obj << /ProcSet [ /PDF /Text ] /Font << /F1 1609 0 R /F2 1607 0 R /F4 1608 0 R /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R /TT12 1611 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 763 0 obj << /Length 3025 /Filter /FlateDecode >> stream
+H‰´WÛŽÛÈE^õ ¾„ $ï"M
++ž)-h2›¼¯¿‹¿ýÛš„x®! íÐÌ-ÇÌVcñi—·ÿuÁJ|3-Ö$1Å?ù“ c÷Ù5ϱ#<Ì~5¶`·ÛÙ–o»fC§J:ªq¤Ÿ¼ËôÒŠ5ÝÏF®ÈÅÈ #Û\áOí8‘k€A,j9ZŽÞÌ`ø.¶8§hôP;„¬œýr&׉€ÓȱñÿÁ@OYß®RÂdj–°_¢¬ÄB/µúÛP_¶–å:æOÖ$ÂÒJ½”Cbô!ikhŠvÅkå‘NÀ¯U‰Fï(Þ0W
+NÞmÚšwÇf«—ž¬I€“÷z¿ÙÙ»®šVdJ¢éÎhÆÚb ­Ô–‹Ïî 5+Òʵ+­˜wâƒÒ
+¬"ŠpþÅòAПãôç8ú2˜à«ðQ³ÉÖê2J´Älmöæ5»(9Ržg{ÑiB»nï„«”˯ 2
+—b¢Sl`rbªONé
+ üÓêØ¿©1»oˆM]md·Àvòë¦È³¼µà„k"ßštH^ }Éu:à0å%qn+ ‰ÍŠr©Ü®ç²>O#é0=J£°@Èåß%ÅI­{¶ãÞÌ ÃB!½;D¨¯2Ÿ `OZk´t"LÑöûD Ä• 'ŠF+<(‘Ž1Q’¼æ€ô¾ÙùÓõsÇ/©:® ôªMXå^¶g¥áS;ƒÙk¶øT1MN|Ïöƒo2ƒ®¤FfJ”-‰"àiõ…o+Ñl7D?=qOQ9 .ÊPŠX}’-˜…€GÃÉ‹”Sa^ ƒw«¼P4s­©ôà„]S‘b™×„¬–P„!´:²o?­¨É‰m¹KËÔ;‰dé 2%µ¨³?©ö–ælRÙ©#í]r ½hyfÚʵDWKÅÍ×ê¦ÜQ€7²\
+5ê2<íy6jÃØÉɽ`µ†&EQæóÆtmë¬;\ šM È–ÄY¥F÷r‰ÐêVäƒh±ØR‰]Úª°à–tΛ“¾j‹–Žf†ÁP?[.˜ ›ôŠ–`
+íÀ¹ ÌO>þéeÀxÓОzÃù2$
+endstream endobj 764 0 obj << /Type /Page /Parent 1651 0 R /Resources 771 0 R /Contents 772 0 R /Annots [ 765 0 R 766 0 R 767 0 R 768 0 R 769 0 R 770 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 765 0 obj << /Dest [ 764 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 324 743 354 757 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 766 0 obj << /Dest [ 834 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 284 688 306 702 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 767 0 obj << /Dest [ 748 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 410 674 432 688 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 768 0 obj << /Dest [ 764 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 437 496 467 510 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 769 0 obj << /Dest [ 773 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 421 384 451 398 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 770 0 obj << /Dest [ 779 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 436 67 466 81 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 771 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 1607 0 R /F4 1608 0 R /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R /TT12 1611 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 772 0 obj << /Length 2642 /Filter /FlateDecode >> stream
+H‰ÔWÛnÛÚE_õzÚ,$†wQ†kÀ‰ÀEã‰à‡$} $ÊfA‘IÙU>¤ÿÐìC×Ìl^,;‰pr€"pDrßfÏšY³æõbôj±ð”«›‘ë)ÿðıíÄN¤fóÐŽbÇW‹íèÕ›:V«šç8ª^£Wï>¹ê¶9¶ãÐœÕhÚ>>Œ¾è¿'ÖÔ·}›Ò¯§ÕÌWÖ?Íy‡¹š9ö<Â)~lGŽÂx¼=…‘lõ_§žcM]ߎ´«åA}:;x:µ°“®³Z¿›¨Ïÿµ¦!Þ+Œ…v¨3ËÑ«»‰úô5ßÒ*çM|kk:×êßü?Æ×ç«yŽáeqolÁÃÃmù¶«k:5¥c±5Ž´ñèàv+óéŽwº\Œ\•©‘F¶¹ÊŸÙq¬"×…;±ªÒÑfôzÑ9Ãw1ÅyìÞÕyV.þ9€©õß,rlüñ’/ú\½¿z9½¸ú¨vU¹ÉòT­ÓMó\]¤µ‚õ*-š¬JUÝTûU³ÇS¹Qtô‹—I®Õ:i’æ°ÃÜýêN%µº“TëÖ]S׆ä%€åº]Ð#y­¬¬
+÷P:Êñ‰ZÜ!ÀS£–ˆ‡]ʉPäÓO¶É/M©hsöÄqÚêªP§mDY 9“µš_§â`6 HAG öÐ)5öpµâ°šëTí僘a>R¸•íµ/FŠUªµ“o¥Ì£œ¡âÍR…ˆ
+»êƒl¥ýjÐŒ/þÍ×yV[Ó
+•ä• “¬ÂsrÜÚšÐ{Ä3.y†7î3aAÓ·Ãà1šNŸ\ŽI."äºz¸ËˆfºÐ¡»ÆÈ+b L[Z†æŠâk6vÙêDè†z:KøF5bvƒHÈ µÅTmVciÖd8·îÌ]üy
+ì÷ù×b2œ¨b¿ÝBÕ$MY˜kŽò|ÍûQZ.˜‰"àñš_PtoMÞµ;ˆ³FQÈ#æGòp­ô5—€6£bäuŸSf+Nñ•7Ù4OïÓœ*CBŒ"žÓÏp]gR›²«tá2Ucó*É!k–ÓÖšp"k#xnÞœÿíòú✥ÒÜÈ€ŽKQ.Íu‰‹ÀŸë§bƒLëK˜cJØÍåÍåõBõaT3—Ê”„³–á™9!NgÓ=‡aÈà5ƒê´h!o|$€^ n¯ç]¿ }ö ‰›ˆÓÐíåOÔ‰|•,ˆqÁ%Š[=•@¶÷Ô—¨ IÓ¶hšŠÖIzîDM;¡CÄ „m²cꤪÛ"ôúòÝÕu&ϨgB¨rJ9[½-+á“Þ¤¶¦o¥‘~sþñÂrI,'j#óIN¤üU*kÌBJQ_Ê¢û|Ž™žÇ”àc__Z„—¾ÆiTäÎ?Ž-ÊkÛؽԋÜ_…,ÔaG…̨ž®O(5˜öU«ÓKÙ„+xÜQ5A 6ÉŠ&½M+ZxEò·VwåƒÂÁ%eÂbÜpßBÃ:*Œ|dÏ€zà#±¢%°MZGîLŠˆ+é–
+PmÚ 3QÈ5ÂË ³‡r¾LŸËi¯E†R¤Nêê\ŠÐ†ƒƒÂwÎÉÅAˆ´Z•{y•æí¥\‘há àÚ,4âdŽZæ!Ú 4DÆ~ÑìÍ69v ˆPôŸxSi;(â¶h/÷1¯•Û*o>¨
+´Êß?æ)©ñH”àN
+Š¼ÜŽY¶ŠŠŒ>ÜeÈ™¬XåûµE• íŸ)l¸;ÐgT±¨KnyOÓ>vN?œ}è·– Ü»]Yq­H¹î({# Ÿ¹OÞ¤šÚê©ß$W䆚ÛIË`-VîÜãŸ%“ºœx1Z¨%/CË1½Á}“m-jWÒoÐUõÝ䤎nÝžn]C·cìàl’Ã.‘YÍ/|}•ì^Òd«„1R+" ÅU5ÅF´À‹JT»Däs;JXÜÈYW¬³Þó$æFăGÊËì¾2;­™ŠÌz=0mÞÝš3%E¥ Af^+À¿<˜«~»ÅŠ ‹OnmNXoÓ&Þ*ŠÕ–.3œYÔŒ–Å_ÆÍ·l=f¡,‰ÒþÁud5dN
+endstream endobj 773 0 obj << /Type /Page /Parent 1651 0 R /Resources 777 0 R /Contents 778 0 R /Annots [ 774 0 R 775 0 R 776 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 774 0 obj << /Dest [ 888 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 227 701 263 715 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 775 0 obj << /Dest [ 839 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 284 701 313 715 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 776 0 obj << /Dest [ 764 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 392 688 422 702 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 777 0 obj << /ProcSet [ /PDF /Text ] /Font << /F1 1609 0 R /F2 1607 0 R /F4 1608 0 R /TT2 2027 0 R /TT4 2032 0 R /TT6 1604 0 R /TT8 1605 0 R /TT12 1611 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 778 0 obj << /Length 3390 /Filter /FlateDecode >> stream
+H‰´WÛŽÛ8žú+?Q‹6£»ä t÷ ²›d‰‘6ÙÙ¦»5#K†$·Çó!óûû°§ª(ÛÝî ‚ mŠ,‹U§NßÌFÏf³Pj¶¡òñ?qž?÷S•M“æ~¤fëѳË.W‹Že|Õ-êѳŸ?ê¦ùÆ÷If1š ÃÝè‹þGáM"ëK¿¡VY¬¼Ïþ6š²†©Ê|3MqJ”›Ô—CXAȺh”¤¢ê?!f'¡ïM‚ȤÚÏÕ|¯>íëþ3¡¶4é®ìÔëŸ/Ô¿þëM|·XKL¢KÏ×‹Û õiWöضb%‘.ê¥7™jõ'ÿeÃøú|µÐ7)>fW˜c v»ñ"èŽNµt,TãHƒ¡Û-ÜÔ-kºžUªQ˜¤&Je&ÏU˜8V¸cœ«ÖŽV£7³ƒ3¢
+Aúꩆf3Ü5"UO›Ðy ùÊ#w”UÎ"£î.±”±‘ñ/Ï™cÊÈ]rçdXH–PšÂ°eÑý~ã2Ö@D|̵ {pÕ\Çäð/¬’iÌf:Yº)@v–Ä€ÉSi¡ÉLƒ0¤³¿èz»^“S{€÷u» v*SO¹´ïàÎÐ¥Ãs5óR\ûØtÙñ‡äÁ®i=*¿¡ŒäÚ­îwie@ü‘,½@ÏÁ–+aV)+ 3o’1nèS–D IðêB‰6hLÉ!3|p0~,ÓüÉ LM”ÿÈ ¹èUi«åCyztº?=fîôÄíè!àvŠZÙñ‡j6}ÙÔE¥Š¾o9‘Êù¶·jQÔJD6,oå
+ù—é=Á¹ xŒ
+Õ쯧‰×Ù®£ »Â› ìÕo5JG1o¶¢˜ÚL…>oÅn»Ù4-Kj¾—l±ím¿zF} K«jïÝm³EŽP¢zÜGú)¥®òöpÌÒJÁ-¶U/¬C¯Åû=—g×€Ú¾z™ †H<A5ðyýж‰¯ ô7;¤ç¦»¯9íì8
+ýXŽ{ü v|5îÙÀÖN… ˜,˜Ðq©˜®DhäG
+#„Çè@$ŠˆAµ
+ÎJÔõïTž]¬7Üw¿Éœu3}‹#X<!:"(@©–Ù?$K–tòN®¨—,A@åÁFZït÷„œñÈöŽñÿ°4|¾d×ï®?ðàJ>=òõG£Ä²xÏFÝòƒ9¬îô8w=gd]ðïÚÒw¨ÕßßžrŠ•äàΣ7sñ&±À¢aôtá·F-úÆ^®g”ƒ¡þåŠ~‘ q¶F>éºþLv^£ |`¡—$ká»äp$ÔKwÂáz0±º„So¬[_ ñ°3Cxǽ“-pk@sWnÅg¯†Zš{9«ó"jýƒt°erè¾È#Ñ9à}€‰¾#LETQ8¤§1Ή$kun‡Öž£àÎéÏ~
+Þ—j<dÆŽá]U¬÷rüù’`rõúãØÍn×k
+Á.;¨Ñh>g+òï÷HÚ–ãXÙì&™²øDRÔ{p2OTƒ¹h*Ý-EÎ0ª‹à­¿¯¿m,‘. xbãÃYì—ðpb†¦XMšbËO$é×ϸŠ« 1F
+Òb·$3¦þ!(o•NÚК#›ýÂk–QáÀi¤±®wjñf&ëW}Ñn¥žÜZí=ÙòûÌïtÛÞ’-fDÔ¬Õ7øÎ2&òÃ0K•LÓ¹„'È3?Š$<B0´H”8Ò6ˈÐmÍœ— uàOGy’÷<aH4OÀéëS
+²ßÉÏÝl•“ÉP!EßΘ;¶zuï÷DqH“Ô»ÅÌ æÃm[~
+¡“]…WêâÑ/X[æ %hz¤_–nÑ0 2”Æ6Fíñþ=ûí©˜àìHÙ=“2+8ã¿ÂI»4´¥ Ak;U2\ºª V2ƒyÁ”vw
+8p{9FMäþðШÕatîHZDN'/YiÁz6jƒ?ä¥!«jÈ.høáp¬š¦ÚNgWql\Õ÷e—äÍ+ˆQ`Çd ™~8¨=u*_[åبR=><®ÑëRDz5 œ‡gÀ'Ý åê«»~Ü%¦Qû‹Ú-d^}€Ž¦RÕ§jÛ˜–¢†lJÔª_ ?¡÷ˆWžIçÁ-©¾”ˌؽ¢DâJvª’Ò9# òUû×Òp2‰6P´©ïˆãœ<8ÍÈßy©ÞJò’;,QÜŒ-vÞß÷õîU•H:ÿÐfž–.T÷ü`
+endstream endobj 779 0 obj << /Type /Page /Parent 1651 0 R /Resources 784 0 R /Contents 785 0 R /Annots [ 780 0 R 781 0 R 782 0 R 783 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 780 0 obj << /Dest [ 888 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 227 700 263 714 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 781 0 obj << /Dest [ 839 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 284 700 313 714 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 782 0 obj << /Dest [ 1200 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 309 686 337 700 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 783 0 obj << /Dest [ 779 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 245 450 275 464 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 784 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 1607 0 R /F4 1608 0 R /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R /TT12 1611 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 785 0 obj << /Length 3585 /Filter /FlateDecode >> stream
+H‰´WÛŽÛÈE^õ =5-Þ$Êp xíY˵³° ˆ'=TkD‡"’YùüCþ19UÕ$5Òd½d1ñÒÅ꺜:Uýýjôlµ
+U V›Qªþp‰ÓÔŸ¥³¹Z,žÎ"µÚž½nR•5,3SMVŽž½ý¨ûf4óg3’ÉFÓîö8ú¬6Þ4òc}oéjµH”÷·Õ£%kXªÅÌ_αK”úó™lÂ
+BÖEwÉ\Tý+ÄÛi8ó¦AäÏõ,Uw'õéT¶[¼ µõ I7y£^½¨¿þÛ›&x®±–ø‰Î½™Î¶õ阷ÿ°uÁJ"mʵ7]jõOþeÃØ}v-œùs<¬Þà[p<}/òÝЮ–¶…jléãvï2÷jËšnV£@åj&s?š*ZøiªæÇ
+>Æ©ªíh3ú~Õ#
+ 2{!Ô3Š¬\}=ûá*_‹H¹H–”7þVÿA„cÆžÎ!§eH»?‹ÝÊØ­¤ý7¬HZB6¢4;ûXCèGN§?HðÆÏÕj‹ìà±S¤AU¾ß×ÕÞÖíÉwMäÒ‹ýT+Êï wß´[Ó*³ß{‰µx™ºQxMI™CTÝÙû¼,óòž¶àdLCЛ~‘‘ŸµQïß½¿™¾y÷Ñ›ÆØMUw_lÖNTsȶÊ4j¼ºùéùXmªZÕÚÂî·•lZ”ÃîÎÖ>«F‚.
+±?KzÌdœ‹”MC
+ý`ÂoƒþmaÁ
+&ºH×
+µtë)†Ê‚½…ªo›Ðx Ô¶^
+%Ü[s¼M1qyHµ„vžXê2ÞÓŽçh4ÒiPEPŽæÔGÄ©˜{+ï˜ Ùf `çÖ»"fÉ<þ1Ø2þ&“ß8>~f£X{trwï.\f‰ÞíÛSç‚Û„†èó©%èCv1aV4/¦@5*Ɇº«ªÂ¢<*R#A,H3dܽ)=žã‰»Ý°ÓÖ@}£zjå\˜LóBÏs±øf •öÁ!š7@ôXU–<FFq>ÙáÌÌ­z00”¤‘VµÂuË+RòËU`ûT…ì*‘Â0Áóqkç…g‹µ $>yJ“ýjvèWØŠæߎ>Ò𿮤ï|F7-ÌÉ®÷8PýĽã
+FÝ—ÿ¥Œ¥Óð«Ï
+ˆc
+IÈ·UMéóÝήsÓÒ<O1ŽdV–ä7gN,ïŸháÜ_~spOýß«= g7íÂz1Ñ÷Ýë鉾'ÿ½c|Ó
+vÚM¼
+ã*”òLG&{*‘&S…Ñ.þ[-"8ՉȪ‹Ç®ßž…G÷ò”å$˜h€MŠ’S[¸xµcúïúÓ >¸Ö,š—ƒiÖ€QÄIDæÉ™õWåawƦ“È9Ÿo ¢‰Î‘œnÞîyÞRf„¦l¿à³!¨£ÚôG=Õžö–  FjâIz°$öÜüåÍ+b¯ì£Ã"£ÛY2øJ$)²‘f:ƒü1G‡ÊÌA­’Yá鸖¥Ô;jDåºàv+äˆ&çÖ)/T}+ýÅ#k:Ä5?Ž¬
+¥gƒ³IßÍcó ÂÿÿOfá§[ôYÓ¢ éš“w·/‡|,nG@dð ,z+¸/s>È<ü˜ô¹9+}‚ÂcB {ùŒŸ0ÈMÒ"`åÍÞˆB¦1¼*y”L³JQP±>·ØÍhI|Ñ®ÇMFÖ”ã³>+c¤Ñç ôCUÓdvšð„²+¿ÎÐn²ÜåYU`&E¨Tn½‰B˜ º¹ þ×D6láê;ðúÕêæíŸ>Ò¼<×ïØþ›O4ÙVÊ ¾vG(ŵ”®„ šú?¼WMÚ0½÷WD9i¡H€BªÚ^{Ú?g D`ÿ¾ïÍ؉“mKUµ=ÁrÆã7ï㔣¤Ø;C£u˜¤¹ãØ7ßYÞ¼¬Dë`mÀYͲ‡ëÔ¬ùL€)ÆIbNu8ä£~p ãÅ¿„³t&°[Vën3ã…ÁØ—¢4¦SòÜ‚W¶ï¡ó0*M"U‚­-*ô@éé¶ÞT¤—–5ý™Ä!ßD…„Áb©çØ(†ætÆ„Ë\OΓõ#ôM{¨:µx¥›]žý¢özQRjôÅêC7iÎ{Hm?ÂÈdÙÜ3[ÈÂ|_£\™]†§ø§¼×寻X3Õ,Z¿(³Õµ@'£Éïc$DG‹ ÅÅ~WHï;*W¿V—ýVFÏYêZò–¸jk‡Šp›¹KNo» ²FYƒX͈—”ófÄÀÔQ|>],Úü­Òë`©µC¢ëÈ…Ýœ—ö7V«#,®„šÙ¿Ø@3žàl°ò±&€ä«ìMJ
+endstream endobj 786 0 obj << /Type /Page /Parent 1651 0 R /Resources 788 0 R /Contents 789 0 R /Annots [ 787 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 787 0 obj << /Dest [ 764 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 376 406 406 420 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 788 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 1607 0 R /F4 1608 0 R /TT2 2027 0 R /TT8 1605 0 R /TT12 1611 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 789 0 obj << /Length 3840 /Filter /FlateDecode >> stream
+H‰ÄWÙnÜØE^õý2—ˆšæÖ\ŒÉ
+ºû¶D‡MrH¶”ö‡Ì?äóSU·É^¤‘'ŒGÉ»ÔrêÔ©ïçgOæó@ùj¾:óåá?üŠÒÔõR/VI6sãÔ Õ|}öäy—ªEÇk<Õ-ª³'¯ßûêª;ó\Ï£5‹³éîÏÛ³ ýçÜ™†n¤¯ ý´JbåümþdzŒOÈTâ¹YŒ[ÂÔ=¹„ø,úkËQÿ
+ðvxÎÔÝX{©ú´Uï·U76NÒ]Ñ©g¯ÏÕÇ;Óž[|›¹3]8ž^\Ÿ«÷·EÿÅ´%ê¼Z:ÓL«_ø'Æî³kçÆx˜¿À;¶àööÖuB××ÝjèZ+]üéÁ»…}uÍ'½œŸùªPgÁ,vÃØWa⦩Š}7Š|ŒRÕš³ÕÙ÷ó!¡%Þa4ÆP{X9ÿ¼—¦Ì—ø%±çâÙB ˆÇ\Ø
+³Ty‡­SÛ©ïÂh
+)–6fbãî
+•—·ù¶S×ù ̨ßÇå×EÚšË]>èõwðÜ r¶ÄIÈž¼²kI@– -Ô¿“Ï‘|FblÖí¾±6\/²_&öK:ìñåËtŒ=y_.HwºÛÿÉS5wPo€y\tü ê¦/ê*/UÞ÷­!ŧMoÔ"¯”,ix=¿BàÇúzË™ÌÕÁKX¥ªœ3·6êRËG9¶æcÖ
+ð>ÙTZkz%kQ
+ op@8à”0¾]kÓ´õª(Íw—Žº½6­9ðÂãºmM×ÔÕ²¨®6¦í·Õ]×8Gìë–€ûfE®àðBU«¯Dá„S7áhs Ï9 K¹_B~··EYª])HÀœ€BÛR1Tô?ŸdÖÍ°‡žyßžC”ü1J¾Ž¾2•¡`§T~D:(T9çÚTlný‰Ê&ÖŸÍ¢‡ÿpt´)âMvÙ–Þ€¡i+-6ò,Ëo<œ”]€ý³‡Ê.
+­˜®ûñå_Hâú½CÙÑoþônd¢z¥,‡vª,þ=ÏÛ¥ÂÖ»FЊ¬, d‘OrŠY"ÕíIÅG´}„Î1jÀzác–ºÏë
+¸?<!p½lŸü!Æþa‘ã]FÎY ÂÍ¥Éñó¦ƒ2ª›í¥ã»ò²«yëÒ¬òM f]I~Ð^&Ø>Ý5»ÙQ'#b“!ße8ÈONTÐò«qlKDÂRŸ]§"q“è¡*—E©x|Ʋ´4Bì1óFÑW1¯M
+‡“a;á 'zr®ÀÀ\r+m.®¼ëŠ«JÈ•Ûv'¥›£é³=îÀ¹éqJ‚1%¶I|eÌ!3_™å[£6òh ½)é:{?¿–Ÿ”£@_U–ƒ#û‚˜!±KzKÄZåö©´—õ§½ÃNö`oE{ȳàNšî¿î"÷zj0f6àÌê‰z*óÊ0Äø#qPØǾÅÂ;sË’€`x:e²?{h,*ÏÕOH”J¥)Gr©xüÉU}‰ª_7‚Z”ès¹–Çw¼¾Ç­(«K"[ß En@Ó=„þâýv]T¤êÕÊ™&l•³é-•“Ò
+±¾tX{.dÑAY¤Z
+€ïTIcŸì‚ªgÕ+ÈðÍsæ8òMn‚‡¼A•w÷©ÓqñYç}G½r4ˆtv !1ƒ
+?Ô¯þPÿ@:ö>ÝéJ{ü‰¦¼ ÏiKÛà2ŠðaÁŒþyÑ^ùŸ° Õµ0áÿQw­¡{r"“‚ð5j½À¨i[1ßtŠ …^QfT—ß
+b-¤ÏR ß¼þaNkœiŒø¯¯w4ÓÔ=8ò„°ç±FÁší¥Åòø€ØÿŸ!|±‡aÑJ¿ÒC/,îBr±kAò•Í³ÖGÁ´-‡ ^ÕØU ø Ò[
+¯7}³éi’
+{0ZØ^_£G£}B ÷…é
+Ûª[QL"èªiמúgly$d̃7µÕÉý¯ï5ÍŒÞRq†è^„XmÅ„bŒÌí‰
+¢‡Ò.tåu]…®oÙNåÍHº£×ÖÚ².4ñMC1?Úr9‡Á“jÇ;Ett)BÜp•`‘ 
+© cÈ^™¯Ö@)=wNë®t»‡¥¯û°Dx^^×f9«´gè‰
+Ͻ¢ÂKÕÚ÷†»ä¤$
+endstream endobj 790 0 obj << /Type /Page /Parent 1651 0 R /Resources 794 0 R /Contents 795 0 R /Annots [ 791 0 R 792 0 R 793 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 791 0 obj << /Dest [ 796 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 440 757 470 771 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 792 0 obj << /Dest [ 888 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 503 478 525 492 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 793 0 obj << /Dest [ 796 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 167 280 197 294 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 794 0 obj << /ProcSet [ /PDF /Text ] /Font << /F1 1609 0 R /F2 1607 0 R /F4 1608 0 R /TT2 2027 0 R /TT8 1605 0 R /TT12 1611 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 795 0 obj << /Length 3861 /Filter /FlateDecode >> stream
+H‰´WÛ’ÛÆ­¼ò+¦ø4H‘n@•WUZy-o,{US–c¥R 9\Â
+7MUì»Q¤pÆ(Uµ¬ç³{g„>†x½Ñ»Ú#`—³_Âä{Sq`{.þ˸Í÷ïƒA¯´œV;7%wàm¤²Ú‰àL£nÇ™D«uÕŒ¿×v«séTÝóFŽi™|'“ÔÎÚXÁGS½+T^ªEUþ²+m^•jï$m—^«¯L¹Û¼°&?j;ÙàDnèN>:#åøžÎì
+@B"­ÕGpÆÂýü›ê€«ß__Jþ½¹ŽÔ~mj“5*VgSïxÅŠ¥:0ˆ0Ðîçy;>é¿ž\|K.ˆ­ú£`ÈV÷ë|±¶'ãs…8!ÔÂ:Ð[ûÕ8@|Ùª!9žZ.àóÏ—´ý‰þ0$üÇŒþ˜ÐkµÈJ57€¾Ø¸‹„ù˜Ó@<Î`8ZÒ"¿9ïe‘cÉâîÈÓ㾇èmÚª6jöÓÛ •µmÏw­i¢D5ºuÆ©›h&ÕDf»®Jc­ªZ™M–NJ‰“-—ðpc@R´2¨Þ·\Dn4½'¸…°œð˳oì˜q€(Ni·ABõ¤;’n°˜¥H;¯/$®Ùž¡íIïçøÒ#DϬ¶Ùm¾ÉoAL:¦‘œAzø\(°çE¢ÕûQ~Ôã$âý`öDE n™Ù3jä&a?åï©DaÆùE8ð…'–ñÌ wj[íM½BìAuhNæ™@ƒô6ÕÒ`õE±[3´ˆ3´Zíã)ââO”SâѬÈÁ>@©‚@‚B–Ëe.ChÔ@Ø(8‹¾¦üjÔª®6ت±¸AâÞ!P²’
+ÅIFˆûÅËÒ‰C]qù•jfܼ5uV¨›¬ØiÆF@±¾5–?ä[|Þ õ˜
+<“ÕyV¶8 I2„Ž6+äÇ¿%”M†­Ó²HÝDÌ÷‘îjIô‘jv Y¦áMÕÕ­“d~\©úòÆZŽÈ׺qi³úD/Ôˆ{eÖg0ኋ“d¾+ëd€›ôLtàãß_)‰þ©ë]a›˜è¤7?h¦ÿt¡Ëìõo˽f‘g‡r¦».vj„§n^e…áû\És–Y­Þ½ûó› ÐTÛÛ½aÊ‚Íæ| !ÍS•'¹ ¼&êHÒo?±•Tæ¦bµ%ýN6ÏäÅHû–Õðf‘ñJI×\bkŽ?Á|¨úaíЛüîxç…Jˆ¹éÃtìäþØÒc­þºË|Û-ÉF×N 3y=hî)YQKA¹²ØAò+öHèó§å•Ù?W3?¸V,÷—Ukw¬^^‚yÃR„"3`^ËÀÖ¦jæVcx¯@¼sèòÆÒÅ׈ª5ï.l½Z|rÕ{Ôvæí¹ño½F‚–¦.¦Ø£œÂ-¬§£ÿgNy 6=/ªùßæqttýHÒ*èqܧ•­<-$`ÃNãJ#u&“ù¥r3Á-0k…µ]q¶ú¨w2fÇ
+—Õ{¦Î¹ 5G_ÉëGG kPH„¼B¹Ö&ÚHŸ)‰=!û°ŒÝ
+ûþ6ÿ¿š´‹‹D93îo¶.ªf1²c<ÌuM‚Ê0¿Ûù¾‚­7ec?ŠÅw“bïM¯o²idDÅ£¢­Âg»¦Ý¬•H)Z5h•ÄÊ´k?Ü° …fby˜;ßr¦p0R(g]¯Q?†Ô‡þe£hÛW!*ÊÝdzøW”b±|ê´ ä7pïGšµ7%ÕÊ1fÉóÝÕ!O®ù½sЬˆÓðÐÿ4/ʽß
+”vF³ÜìT\šáI¬vG3Í=y×xúŸ2nb¦#¼MÏ*ˆûê™Ûo@Nîkîk鲇{ˆ
+çÂÇÜ®±PýbÛ#;r] ï“à!á×ð¸¨å"HË#@¬óàÁïä°*›öŠlûŠBÌžÞ%ÇM<Ãw!éÀ¡N
+ÛÍÓ!ðÞ˜òOtù˜ÁýZÉÅŽ"x
+•¢Ñî%îÑÛTÑEA?:J´üv±ïÍ#3åfw`€™Þ›{ÑÈ…v¿…‘›¿…{ú06U˜a¨ñCôÅÃoŸåéLžôv’< Eäâ>¼rµrrï…ô•#W¾Ú“ÿ¾6'‘¯ß¬Â¦Úl]Ì 1ýÛNPüµ"áD¢Â¦
+q+óQ€E†çÉý-ÿ#oÈø6ü_ÎEÕ–‹=Ï`[²þÌf;ÐÕLHðwUù÷N¬öZ̃@¿!…Mµ!?¹ˆ9HŸE|êTØTz©q–><&aÃæ­Aé5ƒ¬
+`½X¢ÂÖ¹Á#Z7…­·³ŽfSÿ°1Ëq¤äcX©ž5¦ÎI_Ì&EHñ ”oëÍVÔíþnóÂD_œ&EŽ£Aifå{”C
+9K TÄ$ÐQ·ÖˆªzÑ v~Û*¿÷Ê4`÷ÄWcþo
+endstream endobj 796 0 obj << /Type /Page /Parent 1651 0 R /Resources 799 0 R /Contents 800 0 R /Annots [ 797 0 R 798 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 797 0 obj << /Dest [ 807 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 467 351 497 365 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 798 0 obj << /Dest [ 779 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 346 97 376 111 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 799 0 obj << /ProcSet [ /PDF /Text ] /Font << /F1 1609 0 R /F2 1607 0 R /F4 1608 0 R /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R /TT12 1611 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 800 0 obj << /Length 3319 /Filter /FlateDecode >> stream
+H‰ÌWënÛÈFÿê)¦ú5DšwRF 'Mëì.M-,îJ¤ RQÜé;ôû£ß™3¤(‰Š]l¨ˆÃ9œsýæ\^N“‰/<1™<_¸ø‡G˜¦Ž›º±HÆ‘§n &«ÁÅ«*ÓJãŠjZ .Þ~ôÄ}5p×¥o¦»YîŸåO™eN(ï=})’TXŸüq0ÖÆ"qq )AêÄ. Ñ |Í‹VQ̬þíc×ö]Ëö'–n*îÄLJ¢^`Ç—Ê'Yå•xñv$þöËŽð¾-r"™[®œ.Fâã.¯ÿ©6KÍ$Y1³ì±ÿÒ¿Z1m¾6Íw/“×ØÓìv;Ç
+OV$U‘X°†HKÖMÍÖBsºž <‘‹ÅN{"Hœ4±ç„¡€a*6j0¼œ´Î<|âzcïj—<-'?.Þx/Ûß‘‚!=¡ígùlm²•ªÌYàåj8ùëO×CÞ˜©y¶]ÖWÃU™­jQîøýy£}
+ Òþ æŸ &?¨!#›k,TbiWu¹Q³cãÍÏ×C;0.v2.;Æ-s¤Él¹|8¼­}:ðµcÁ«r>¯”1×;vÍÑ幕W´óîõÇ[ëìurãófõÐúbÝ~ð¬Ê ²Þ¨y›¤OG:„–çÜœb+'gõì£õê™zOÅÖ2»SËóðzµFV7ô‘ý€?0Zš`6
+ÒxWK†#ÌéÓü)¯¯I\$m+ÀËkËÉ`*<"Ê»ŸÕ´°wºh VCB—È!³}ó:Ð2ØBá ˆ Ą̂i~„£aƒéTëXEóu½€Ã Êt SÖ”ÂJ‡3³Ë¶#2ü^æXjÇRqÌH^ãÌXû'$g²(ŒC¸œ¨©âì7Àî>À® °Ì,#¿E²6Ï;ó¬pIq•ÌÛà÷ñk“©·–sê¼Gnì)ÂŒsY'H{-—彋Gë{$øéHCÀä$:tirÑ“Š
+$Rꇧ ¥fø-kr×ÔY«iŽ¹f6p2m.²/ÊŠÉ
+“æPíë
+5¾^ˆUþU͸ÔôAÇyû´ _”…Ý9ÉN!f(ÎyÍ‘íhÅ]×ÉeðÐÇD]oL ç7¼ ~ 骜&ÒtýžÂlLªL(›*kêDÌu¢qƾö¶•v­‘t} ,†ò˜HÎó¨ÚR©ÙÈ1O>]êï©j™í[©Ó’brÆ)i»¬Ûn€/vLJð;†µnt6»Í6¨ÿ5–ËŒiŠv 8ëד’¨ƒ·Q~r]Òì)ä¬$ëMVTPê(Œ?Z'ौ\CÞ%Ǹü 7á]z`½£+hr{þò
+Y©˜uG ¿C`vñÓ_Õ<ÙÜåHcÎioQ ö¾÷¾[_ìªh­§±m¾)WÍ( ì×j$8 p›K_á8.”[î©ÌËmŒÄ8Ã<祹ñ^ü˜Åbéî„ìæXçÌXóŒe]
+Nö ”œhÒ÷P±^X6ÍOJ«ÈÙ7á¶Nç@êåƘ›vˆ#n+õ’¨S ô«!_”„/ʘ.Šnþ| ^C]µnenX|ƒÅPËz‡šã¢d2±ú
+GEœ.PCø1êô"$ÌP15òâ \Í„ÙeK)©—”öM!*˜¸´ÐãS†nëL'«:¿®M}úÝL»ˆìiã"Ç ;<-}H"턘>螀SC’°i
+Ñ 8 :Ñm=…e°G%ËÕqkV-*UŹSð ’JÀÁÝXãî‹!QËùyÀ\n¥@U‹6ún¬½Â'È7ø"– Ê©žÜª¡™*>Ía®Á·ý~Óz"ÚžÿÏp3SQþßÚËd©A ÃwŸbŠ±  PXæ`ª<xñ¢¯ÄLŠlúöþÝ=,ÙÊ“—,ÌBÏt÷ß_gl~\Îã¿Ê@¾cÊ!­ÀsiÆÛ‡ƒ@7Ë8›w~ÍG Ô/R¤Â熀h/Ï‹‚H@Œë"з°ß ã‰sO{ªÕÆië¹v’ ˜´1ÓÆB¯­õ°ÆPJtYo~.º&ú÷Ú±kêF¯òïcׄC ˆzßDoˆ{q
+rÃP´TÍaëÆ#/Þý1‚^H:Ë!Q²
+ÁU:¢GÌIð­Ñ,a‚ <O=¯Ôº’tmt‰–f‰CØåvnÚh1äíú®â+@i%r¸F R+¹–[¹„›ÌS-jHHZÖ…¾çJ9"Wó§aô´p:s,ýÊ’”1¿ÂjÇYrýÅ9gÎœg8RëÏ+ZÏ(Ë(taPÐ^x3ÜÎq6;t7/ÛrÁ‰¸2êÜB«m±ÅJØ2ZPŒÄÙèÈ7âøHÜ“|>ŠÃÁ%S}GU2ƒ÷8×ÌRΜ١yË8§Q»3 ˆK-­Bì
+w¤žR¯üT .pG¢´5½¯d\¼A"¹w¾Ñ‹{3c1³ë7`U¦1tP´Œ$w@#W•¨Z9šIõQ™¥—Y¸
+\$­·šƒBæO—sÔò=½]ý Ü©‰
+endstream endobj 801 0 obj << /Type /Page /Parent 1651 0 R /Resources 805 0 R /Contents 806 0 R /Annots [ 802 0 R 803 0 R 804 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 802 0 obj << /Dest [ 807 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 116 275 145 289 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 803 0 obj << /Dest [ 888 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 227 163 263 177 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 804 0 obj << /Dest [ 839 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 284 163 313 177 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 805 0 obj << /ProcSet [ /PDF /Text ] /Font << /F1 1609 0 R /F2 1607 0 R /F4 1608 0 R /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R /TT12 1611 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 806 0 obj << /Length 3100 /Filter /FlateDecode >> stream
+H‰œWínÛÊEÿê)úÓe!1\~Ë×1à(J®{mÇ°™[¤IÐmë^‰H*Žû }‡¾côÌì’’eÑ‚ Ö~q÷ìÌ™™³o’Þ«$q…ÉMO¹ÂÁ?üøql;±ŠhØaìx"Yö^«XL+^ãˆjš÷^½¿Râ¶ê9¶ãКioØ4ï{ŸåEj =Û—·ýº» ë_Éß{#Þa$"Ç…8Å‹íÐчð.ïE­ Ô[ý×ÅèÐu¬¡òìP:±¸~Wy}‡Wfv’Õ¼ÇïâŸÿ³†ú%æ;sË‘Ó»¸ºŸ×ÿÎÊoâÉ4ŸYÑÿá¿ Œ¯ÏWs;D'y‹1Fpo[ž­dE§ft,¶Æ‘6šn75Cw¼Ó$é)1=7m/T‹ì8¡²}_àŽ~,ʬwÓ{“´Æð–8­±1µCÊä-75ö‹BÇÆý ¬¦Të jüÉ÷t¹Zdâcev,S+"Ï
+ôï2q•ðä±þùx%ÒºÖóóëu½Ùþ÷ññ)·&ço/Å<Çf8ÒØž°¸lò}|’X•yºùz™•ó),¸*³*Ëë´žùGïâÇMµ÷´Ý^½S†çv@«\ŸV}–‡«²Xeeý` aw™§Ëìuÿ*9N>^õgÆB‰È/|ÁÜr㋳AØÎú.ìòh>pb€éüü…é­³Uhf•O+Ÿ|½o ÜJ9 f”Ø®!!,ô-]¬3mž›y¶˜½îŸ<›\žŒ¿vÙ‰ÏmN£ûï é˜Œšp¤€·E“
+Ü€äíFp:­àtŒ^ÃáW RLŠÃUZ‚@5$Q$³òè@¬JnkÃ
+Ls׬x*Å,»™çsèÞÂHÀ‹w¥vƒÉÅІ[˜TÉ :ó<›A6°æŒd»­Qð!âÀ¨V’ѪU@¬r ÎBÆÌÚ"%ýp´ mÈ›´*JÛ'ní4mSHƒ ^
+nç#v¬µ«9wEíF˜7nMîðj©Ó[͘¬"=œ6·"±Š&‚mq¡‰˜Y!\Wbm™‰j½‚°_’ .À›ïäA¹´HÛ³6Þì3ÜÒÚžÖ¿Ìáû»ùô:½Îò _=ă¡õ}[áðg&i~»N­_v¢(Eý°²­Éô{Ûð«€±,5€ª.ÊŒ^¾œaC%1Í«šÛýµÚûPþo˜6_d…ÕübY.<c‹Ý+m½•v7 6£ÀØ{¹¸£ÂÃ…"¡TÿÔ÷…X‚ßb]ñšLÿÐcÆ'Š­xÞ¸.zäêê5†ï ý†í0ä/úUb2 h(jž+-y•Ne]#·È‡Má}´®ðÎ[Wð\8Œùz¥¥¥HÁ™Ù¬Yø-e"Á¦F3Àƒδ»È=Kíó4ºe뮧V'£n‚\ûÊ6//s“aóô
+7~ú9+OOºÚ
+­ôœs©QãÓH‚êŒG°‰örO¢@îYcóëL;gf‹ã\dßÓ%¢_G åÜ1ùt1”–¥)]pœ ÊdrJ>´†# Yqh³;q—oã´œìµ5špý$–"Áe"ù‰¬Ë ŒrBH¾þLJËß|l¾¶¸b-¯Ù¨¶¹wÉ@,ô.uú©9e¢O9å“¿ZJÒ BÈüÌ)pš¼¿Ëò'ÖÕ6uÛzfÈC{ý뇳I—€Š›œdÂYl$O¿ÒrääMàƒ¨ …|M J,×U½ß¦›@V&ÓÅ}ŠªÈä)îÒodm‹ñ2ZÓ*Ê8GT5x™N¤óRpY÷dvô eo2ÕJ¹ |¼Ÿ/t›5wgè×w|_2Õa ›RL%i/3à 3Cšr
+0
+·¡Û“ ¹{ôC$S“¬È¤þŠÙׂ–k­þž¶¢œ=Üi”<ÕÞ“]ª¬&7sÛ,AÎ !¹˜™>iQÓ¬8 kÊÃpS>3lR²Y?Ð5³Ì*Èíi]=’£tXö}Îï ¿Ù(¿*3-¢ñàØáñžwÅÙ ¾ô¥Žz
+êþ%!@êþ"ûðDfßZ¤ÔlºI¤PWŽi™'Q–(;àQÕŽ.²|¦…wJÂÛ“¥@.@Ö-t­-9vÄË(U*p—:)ŒUD6èÏe—
+®°4ˆ}.{¿±`fq-‹‡üO´½Þ/âí€e·¢˜ÝPaT5ëºRÜë±´ót©!Øe,µ´¥U2íd~"ðyV×s:¡Âos¤¦^H,¦ëÌ'u6nä*ÍΨ ”×1'Sÿ±Ã®3‡)â쿦5fn/O=Ô9•‰7ØÄ „vu”Vil\›‚Íì„Ï©}ÅÍÐlAsÖÛl«Ö‹_rTE©ÜÇ2?Üç%•Ó\ò*å``CåWèkx¼móÙn0Ç»wÉÐaÐÂÞnÎ4æÝKz)¨‘-ÀZ>6÷ÒuÇø˜ÀwÛÉÃÓÕ·
+endstream endobj 807 0 obj << /Type /Page /Parent 1651 0 R /Resources 808 0 R /Contents 809 0 R /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 808 0 obj << /ProcSet [ /PDF /Text ] /Font << /F1 1609 0 R /F2 1607 0 R /F4 1608 0 R /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R /TT12 1611 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 809 0 obj << /Length 3187 /Filter /FlateDecode >> stream
+H‰´WÝnÛØÆÞú)ºÙC@¢ù/*H 8‰c¤²‹F{Ñ&EAKG7)”µêƒôúŽ½è7sEI–œô¢0,’çoæÌ|3óÍ›éÕõtOÓÅ•‡?<¢4u½ÔKh<‰Ý$õBš®®®ß6)ÍYãQ3+¯®ï?ûôØ\y®çñšÙÕ¨{Ý^}Q¿fÎ(t#õ¨ù(J=rþ>ýóÕDN˜ÐØs' ¤„©›xFˆÈYü'樞3òC7Q^J;ú¼+Û%F¥œ¤š¼¡Ûû!ýí?Î(Æw¹ØUîxj¶ÒçmÞþS×…ª¬œ;£‰¢ɯ(&×—«ž›àcúc¢Áv»uÐõUÃR5‹ÅÑéâÕÃífvh)'ÝM¯|Êé*ˆ7L|
+ÇnšRâ»QD¸c”R­¯Wo¦{c„>–xÇÖèMí±E åô÷7M|c¿qâ¹ø7[Îûb0ýë¯wZgu¶Ò­ãûnª¬[ê†UMk˜ΪqÉTUk]·¹n¨È¿iLï>k·ïÞýÅEîX †ÔVÔ,«-Á4+rg£j'Áö²¥í2ké[^ΩZtÆù.´f›²fI¯¤õr« ½^V¥¦r³zÐuãÄ8òÎMÔº2_mVP6Ÿ×Ú|š_Ü$`
+*«öð>¡ÅȬJ£qçUUkè‘•”Q“—¾Ø4±*ìÝ(_ˆ¢ö¡\м‚±ùèYU¶Y^bhÎKxfÅøÌÚ¼*]z§Ù¦hŸTañ`‘¸ô©jExk©Ö¼—–YCz±Ð³–ª²Øuò­<èHëºZä…¾n6öU6É…»Ãû²Ó‚Þ_7oiÀˆÀîu3`|t:ñ~$¿Ë4ˆ€É>¼¬é º¯ßÛ5#`t2a™A"WüÉLGf'Ø
+`•£éØKÝ º´ùåÙ¹LÐdÖGÆžo>7"Øó:ßcøÅ1ÅÒo˜Æ@\ì">°†¹~f™$ÜkÈ|ùôç§/¨Nöt”½Ó¹ôÐ= ‚ßnïϸ(š¸“ :¬É…ùcU@ŒÇ¢J ­Ç‘ÏM2A“ óŒ‚莬_Ôëë½Qn8M 7}Aæùé#‘hÄ„âG¼½D$'£Itš'9$Ìäå8¿ùåÙÎ
+Þ‹zÈ'щ.Ì¿‘ÞÔ–zŒKE±Q Íg×?Fq´ïF}t˜ÃPÙBwŒü‰úöÚ&ªòæJ˜Œ•òÛÖUAœí2ù|DI7„¯C%ë‰[F9¢Ökm¶äè&Bêj¥½÷Š‚Ïx‡®ßéiÕ|k*ªdÛWÒXŽ=£!ã„ÇÉS2»awxi¥›ÛÝ 9pe$Ci
+¹5jí’Ü#U§ºDʾ2«¦{«¦V]pKŒ¶ñw´µ–š‹¾bÉmÇ1?ë‡=IK I»ef©ŽL QöKö¹={YrØ×µÐhö0d
+E†Â˜ð)™dHœÞ ue¦²ÖVäå¬Ö°ËBO8$PmáYK;4Ø)·8mžžWÙ)µ6{4=€°TjyCê¶y»¤¸‡µ•Ý™€íDìů ù(𥮳J61VÑ>:Üñ– ‘z쨖ðÏê<8mï¾(P:n.øÊyŠ9æ9H#Jd¬vü´ùJø­nÐKÔš `‚ø=ÇèdbC3 œÕ^š¬˜zS@eëƒP5"¯'²§ÜPpÒëì[mk33ÊK4&ªp¸£Ùèr¦1"||ËŒçrÛî€Þ0ÑÁš¢2›¨B¨Æ¡ ¡ã¶(Oêg&iì‘4ÏÚŒÛ!ù0³¸2;õœmK_ÆÃĽ¯ª`ÄÐEÚEçE§=+4Ë̇pGX¸b¥”ÆY”‡Þî7è5²†V ë9¸+Mï>Â&ʨÁ.L=½Íê9U¿kéa…Ç#݃ r葳!p`êS+tB¡Më’K«¢ëÀ¿ÕõbSØöjÈ
+êƒÞû'žë«¶vž_¤Ê–?~…i—öcË‘(Äì6Š·“Î×üºWjð ŠBˆg]˜v.LÅ…Ì à"Í+×çy=“R*.³¸0Ò|UŠg;a£³þzNCŒR]Õ¯JÔí¾
+f¶|Ë5fÕ¦l%„²½ƒ¹œþ¯™÷´6¨'É-UQT[ÎÙžo½2 xo1€RóÉÁøDèOfYÔ-‹,S' ò §ÿoÛåÒ“@ E὿¢™UIFâ
+endstream endobj 810 0 obj << /Type /Page /Parent 1652 0 R /Resources 812 0 R /Contents 813 0 R /Annots [ 811 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 811 0 obj << /Dest [ 801 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 334 285 364 299 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 812 0 obj << /ProcSet [ /PDF /Text ] /Font << /F1 1609 0 R /F2 1607 0 R /F4 1608 0 R /TT2 2027 0 R /TT8 1605 0 R /TT12 1611 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 813 0 obj << /Length 4000 /Filter /FlateDecode >> stream
+H‰ÄWÛ’ÛÆ­¼ò+¦ø’Aj‚*ÛU¶,9ŠÊ^•Ä²âhó0 Î.a
+}¼2­—™+ùÝxˆCÛ¼¸CFæ”_äÅÊ;…ä(Óu7E¡ŠÛmgÕƒ)·ÃkÓ©õVî:o‚EêÖª¬w«lÕ)±]óVò’M±©eMÑuõ•êje6›rÏ}ý(ˆÒCD鑃vKàr½±rÑqe÷l=TK÷þÒu ^`ïˆý DÑje1ëMi¯d»'/¢¾A|ÀL.ÀÑÈÝ—ãÅO¯Ÿï®ß¼Ÿ¦~~”÷éàåT¼TäBÄÙžI¶§Àe{ª­¼CÈœ¹¹ÄB”Ó¹Þ‰N9@z(þú“~RŠâÃæÃÞ²õÊ< ŠŠüöQL)£…-ä9˜G”*#ÉRõ{3¸÷Žz}¦¯ßxþT¿Â'¸{þ ú|¹èáBB~LÒ³œ}~äVu÷ÙÀÛCõPÊÙH90ú)tz-é –¢`þch‚˜H ÑDw‚ÎÙ:g Ð èiYx@'å>ÓçE6)2äpúýõ·Ï¿¿âË_ÿõ ±QdG)H?ŠÐrŽÓ²ç8fȺRš™c0>m‚t·Wk³WðŽX(#¿»¨êFÁÁà”iÒªf ™ÿKº9;|…<'ßëv%¼°k%ÁC€ÃdXä:ò?U‹FáL †›²ø`Áu|¥ðŒ{-7½mí’jIÉ«U»¡´î•Y.c¦
+Ù¬êʪj»¾µM«ŠN-kd¨ª1õ±(Ò ó­­Z{œÞ1RšèÉÝD_¿|ö|¬Š
+’B LêFßÚœoP!F¨)K•—œµÓ<U×¥!ÿCŸG®Ñ;sïÂ#g)Á‰æÌ4åñ÷+?[=9P†•7ž
+›=å•ÿ%ãœÖûNp/—‡ònå—6¬UnÊ|[–@‹QKþŠþ[aW}¹©Ðå=¯É“ngQœa Dà &ÛçJp08OŠê²¥s³»(Ô{]‹‘Ñx"K:ÁÄY;<'2Mšë IÐǔ͒Ü1 þ/¨¤¦È2ý•6¶ÊVyY“‚ìFvp %‡¡ŒæàÂA‰e,™œ…;º±<hSýh‰Ãd€b"üFM© s9†I‚ôBØÍÑ ;g£è„.¹‹I÷R,×?œm¹¦ðáÔÕ30yÝ÷݆ºd¦e±ËPâLì¹\½êꃉ²`r4s&ÉI4§Á`0$ÇÌñôW,bÁ%˜è•OÈó´\¯µ ZÌÚi¥3=YGãF·µ«vW4€|Yç(FFÓŽ±Ô2/ܹg­h£îÆ ÔKL¾fi!
+’CnœugM·mpöÜ×[UY
+˜b´û!c¬vr+.BerÚ[ðŒv!4a´Å}ˆ€`AGMFcÝA_ÃELGñ¤#Ð:ïñŠÃ¢ ܳßñt÷34š‰$oHsé ÿ+}R–_êÄKYN¥GÇ®á1zt2\΋5élb³ŒÉœš•'–VN¼›(Fš\KH\$Ž4íGuùÑù¯?
+„V¸âŤxf‚x)È”Ç
+ãL'4È{;D_²Vþò‰Îhdêì#( ®h%ìÖÌ’ÉYiçCiç½ØãýRò[.˜S) ð‰¦§Jߥ©ëR÷)7€ëÊ”»rÊ]ÙëzÌ­5ðàVׂf. MØœëÊ}kÍ’œw“?¸ìý¶u2†ˆŽ8Ù§¾ZŠÒ‘gó–ÔXO/8qfÇxÉzëý˜|Ij1Gð!oS3›dºé °ýžbNøô¤³OϨMx¤_£A DN ü‡ørém ‚ð}ÁÈ„õ¢m ØC
+=Äð_7ø•Òóõ+Ø)hfúu6¤Äí_ H“9‡ÛC¹u´Sõ ±ÎFéÐWÞ#Y‡é5ˆ·Ë×'glR;5ø×¼5«#"AAG÷LT!èÌÀ¶iýK=¹Úñ!Ù \PÈ&ur(ÁÚG*b‰Ò刋¤K×C?ÉõŠBª˜ÉFë;–í³Q>ƒxgûY‰ìÕšK°þ$„Rt’UÀSñi«òƒ ÂÙ2NIÐöÄî¢q¡ŠÑhLI±qÃI×N<ʺÐoƒŒæ’ÒÍÍ"šŒæ
+…œ½½
+HÔši ¼œ%AÒD;p³âÞ@KܦRÊŒ…b©ûÖ¨jJ3›‘»ëªmà<µ‘“b0¤úuHõ?¤k/Ìåà!΢¸Ž¶•/>7œ}!<Ê:¼I\žìL!c˜…¥Œ²‘Ý(ÖÓ‘»ôe۸ꓶ:žôáï¿èkn“{3|Qßcd²ÌîÁâ„Æ€¶ ó‚¨ý$ _kR…VDŽQeëñ˜É¾´j‘š(ýDÔþ=[(Ñ“} ²½Z+®Çï q™ÞÞ ªva%j‚)õPò 性C‚ˆãg>XBcðÂrFÀV†ÁP!\î•„tÔµýöW´iĤr_s¶Á™J†<§kŸXb©7ÕçÉ°Œ§çV°tVþ—Uw$LC̬ĸÁe?èÇýŸñæÌ<ê*| H^vÌõ¾náSUiôAƒò
+ „#Š™•ÏVîþsÙsÄJ”š MUqÅû4_`‡7êæë”pí­hóš ¨F¦äBxLWW‰]2Ëd[9ÁK+µµ[r„²C¨Çžs•(õKé(Ð1¥½ßZìî~û6
+endstream endobj 814 0 obj << /Type /Page /Parent 1652 0 R /Resources 817 0 R /Contents 818 0 R /Annots [ 815 0 R 816 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 815 0 obj << /Dest [ 773 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 116 743 145 757 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 816 0 obj << /Dest [ 1210 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 338 730 373 744 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 817 0 obj << /ProcSet [ /PDF /Text ] /Font << /F1 1609 0 R /F2 1607 0 R /F4 1608 0 R /TT2 2027 0 R /TT8 1605 0 R /TT12 1611 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 818 0 obj << /Length 2717 /Filter /FlateDecode >> stream
+H‰´WÛŽÛÈE^õ½„ †v³yÑ`<€íµ7Nl؈•ì&v`p¤Öˆ†DjIjåCòùÇ<¤ª/Éir€Ã#’ÕÝUuêÔ¥_-g×Ë%#”,73ÊH
+%óìiU‹¦É‹r[”Ò‰‹w$/Èm%ö”ê¸wpH$Ñ0ô#w!a9móÕ–”ð*ª*_‹šÌë#ºè Ñ•4bF1mFÍbMæû¼P¢myšKÕ§-Э?–7d•õg˜$’
+JOÈT’w•r×^à:À¾èBÉÉ÷£z@$a×*+~×-Do§¸br”. î ½:7‡@¤ I¾º¾þ­<‹Ð=ëê©Aw~ƒÅëú-WËÀ ]`UUôSƒ~À;諸9óJdë²ØçzµV±ï­f&Væé YnsX¨rÌÄú 38/ ×l¡qJh²‰4yYLHd]‘@-9ÖBõU5æß:k²+,€ò«¬*w<iS?—4^(»²Æ¢*H,+¹–/nyÒÏêç 嘻 ƒOù挧ìËJVá‚”…€ÂéJoKµM:t'=ÒÇ)jêr«¾ex^Š¤yÌvG½°o˜Î°åï•GñÅ#ݤÙ.*rÊw;ÒTgÒ”$;
+™ÈÇòx\Š5DKÇkÈÈögÄF÷¢Ëaði°Ù.(:·’”$РÀCÊ5(ÃvÒýÀ&®“o0l žÇ
+\Õf°_§Îëò†%› U͆Ëf#»KJ±‡-p&‡Ägg/ªúŠpœ`
+Ù˾£f̓¾êŽðLÀ*¼3rÒ¹s†—Yž¶³¼ìëŽseŸ[¯%G³¨”êàB “¸SϪÜ#ë¯îÿoÍmk2µÄi½Ýgmp/¤£Mf\*É©¤ä´oFÜêŽ'ܘÜÞ_xÀmƒãpøX©°Éì„ÐÍ/,ß¼ÿö‡Þ臯?}üóŸ:¯>¾z÷¾+ûòç«¡e·Ì"±, –½üËÏmó5ïlðÞùÀ¶0]ŒÚf“Y l§í9L’q™ÙGãÛ,2»eœ?מ™'Í9ŒÆ¹d“™m|<Ð6Ù §Æª§†¬èKy1dÔéPËH^™"#KH?k:Ëzõ£5ðM0Â"³aN&Æ›§=?¤|±È̶`‚/™Ýn–JÂð'àÿíÓ
+endstream endobj 819 0 obj << /Type /Page /Parent 1652 0 R /Resources 822 0 R /Contents 823 0 R /Annots [ 820 0 R 821 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 820 0 obj << /Dest [ 824 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 244 133 274 147 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 821 0 obj << /Dest [ 834 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 235 77 256 91 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 822 0 obj << /ProcSet [ /PDF /Text ] /Font << /F1 1609 0 R /F2 1607 0 R /F4 1608 0 R /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 823 0 obj << /Length 2319 /Filter /FlateDecode >> stream
+H‰¬WÛŽÛ8žú+¸~’
+f˜Faî…õMÕÛ‰Ø$QQO`4ÿÓ%Úu[½B=“BuF½B£ôÿRhÎ(Ï(5ÈSka7~Nž€ç²±Ý,J
+ÕÛ‰±AcZãÈbBý9áñ(y.Åo(Õ°ÝòÅfƒ”ju‘‘AÖZãˆ:‚¾¬ÏRÝ–Ê !ÏšÍ^.žä)a§§KÜENBƒ˜µÆ¸~_Ìõ‘lèŽn2²­­uù™¨Ñ}“JuÆ‘è^_¥Ÿ¬ú±£DFï²w iÚÒÝŽT¤<®þõî¶;c)0±I¸:£f8ïd³y{sÿãk ®¬~“'¹}™“á‘//×ô0ÐÞ'[ê™
+Egšö åD²Öd¼I±²ùŸÁ55ÄÎ ¢5Ž`LÎÒÝáî¦yk#Á÷ʈ\l!ç’«¤1kò²:/΂Tˆ‰ Å 5Ž`\è¾II}8«ë.û¢Ü'H›ãÁ}¢k äKSe*`Ó½OkôË¢¿Cw f‡ºüÆýdÐÛŽjWmºsj# wÎ{"±<‚TÐ÷Š[¹þ•lšYÛ©·mWñ™.–Zã¾ÁÅò·²Žt“éâ¨5Ž`\³º¦ÙJJrZ°ãíõԓljcE>Cw®{•¦À2]µF=¬`pyì6Q’›§NW-¦òHª†’Z¨L;=¦K¥Ö¨?ÃK¥r ëÀx+–LVäH2¸ð†ê!0Ý/µFý!ˆûÅ@‹MErHÝõ´EÐKîű00ÝUµÆv¢éª¨5Ñ{pô½«¢µ¸:fU–“†TÊM'0Ýø´ÆA8™àªwácÑ„¢žÔ`@mKüÿ`å£Ö(CÅ{V?ýs{p•dß”Ù#v ãjµ
+F«Ý3½
+Ü®^ó‰¾p!¦/¶Y“5OGhG±›ZK‘Ãç:ØÅ›/ÈL[ 쉃È
+´@†è °Bpá´ÉV‘gÇ%ð–X3à ﳩ¬3 w6t]ë'Û
+eÌɉx¢{ãlŒáai;>haÖG´‹<9}¢¸×MùX@PhK•ÁÓèh‘ÃÁj²¦É6ûœï_ÊCqd||Æ^ç–Ä?ê37vbmöŽJ&'Ÿó Å"ö×ZÌá¼s¡bˆž
+Z¢&{ZPâÄz¹‹?ÙîE›Ð³˜šØ x#)m åÆ‹v´ÏDõ#8‡#öUØá²Ã»Ky‚ƒ:i‰É2ýl ™ö;™uŽÂ€a
+endstream endobj 824 0 obj << /Type /Page /Parent 1652 0 R /Resources 827 0 R /Contents 828 0 R /Annots [ 825 0 R 826 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 825 0 obj << /Dest [ 748 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 410 757 432 771 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 826 0 obj << /Dest [ 819 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 71 559 94 573 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 827 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 1607 0 R /F4 1608 0 R /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 828 0 obj << /Length 2449 /Filter /FlateDecode >> stream
+H‰ÄWÛŽãÆE^õ…}"—MR5 °^¯ ˆadõ”Ý<p¤Öˆ1E*5²ò!þ‡ücrªªy‘f»‘ÝÍ:uú»åäír‘¡åfb"
+ñ‡Ÿ$Ë‚0 Sš/fAš…1-w“·ï­²&¤Ãªš¼ýñ£¡ÇÃ$ Â׬&Óîñ4ùäýšûÓ8H¼GË¿‘GYBþ?–?O²Ã‚æa°HqJœi¨‡È‘ìÅO³T·úO„ÑiúS©fôp¦çªÝb$ò¬¼Cq w?ÞÐßÿëOgxo07 f^á‡Þj{COEûoÛ”²IìåÕÚŸ.<úCþ‹a⾸…AŠ—å÷ N§SàÇñ|ªåc±5Ž ð»•ÚÊN–CM¢YÄ©¡xd¥&H‚IFl&ß-û`ÄKÂËh ¡9"°rùÏÉÛžå« ä|¶à¼É·Þ_tq¢‹q¦sÈí2¤=7óÆÍdý7Fg$-†møäm
+[®ËâàO¸ÛŠ»ÃnQìÇP0*ŒòÍ--·ÈÔîxhéÁRëO3€d‹&ž¥*ßYª7”W”—õS¤;_Ÿim7…¿@ü+»¦Î„–>{k 0Ÿý@7&ñ‚TijÀBoؼθÙ`ÜL{È8½¢Ó¶Xm)§Öúsï÷–ÚóûàvÁh¶†`!
+×Ømlµ²z0{æѦ©wØ»tÎÝX—9¤- .q &Q$AgsÒ×Xi¶
+‘ð({TbQ=R-oFPì‰÷X‡"ü>Š× î8º¨Öɬ]‚$Ž#ŽTšyÏØ2Çë¼Í%§âºl>ózϳ×ê(Mbx?r×tÞ:gß×U›3Ó[bºª„4˜§C%˜lÆíŽß7ÈFíÀeãs8Û{ÀºæqºcÚ³Ÿ!t¼ÄÞßè1¦g¸Ž+.“è9uUž9œ´ªwû¼-Š²h}c™3`ÔnéÉ6
+jDʯ¤¯¸»®aÓÅ%Ì@£fÄí(t1_qëÒgÚ@-„¿è->‡ÎõÑ=q’i~‘Dè.ÒVGt÷r1à ÏðîvçÓènŽ÷ðë©`</—&}wt–ê.÷WtGA:E;Ø,2™êŠ—*èºnžãI4H›?²ú®®…C‘¯|æ:ˆŒ
+¨l‹=*$v¡¸g¡tàcq—·¼KîÔ…[(/ÓQ>à§ã[éúôý¥< ªˆ—¥7²ÍÊ;""–Y™§Ë!0þuD¯o]tÚSNŒæ}t4|ß\r†Â˜*9¯·ÈFÝáô†{»*ð©ª@–À>0;Ž_§“ìH6ªø+Do@ë!y{ë¤ÕU—½©«!Äe ¤ÆŒòŒ¸ÎÙ`¶ô×w.¤X>ƒð‰ÀN§1¿_RCþ"*Ÿk€%ŸPºž •|lpv£òÛ¨T½GqZÕ-më“ØÇf#rJ´ÜtAùS2‰pB4Öi¯
+’‘†ŒÐ¤é¾»ßßÒ/GÛ=ÀàZE;‚b´t0hëŒÛ^¼
+ÔÕÞ,
+_S"¯ÃÖ dj:2Åv ᪙Š\&”[õù¨¿Ê²Òdu`/_pùû‰wæ6
+ø{`›ýY_ÙNë ]Š¯›Ð¬c6ß7Œ£þrÑÐut„ž³Úæ•ë@»ë›ƒ[õöXuëÛü·nÓ¯LؾÌWÚuóc[ãžS¬ÐØ4¾g,£ªñdOCÀ8Lº—W}ÁeQÈÝE¶òy0/I¿(\ÒÐOî­"—^qîb®¼ñ<çjeé>å ‹‹–“ÿ
+endstream endobj 829 0 obj << /Type /Page /Parent 1652 0 R /Resources 832 0 R /Contents 833 0 R /Annots [ 830 0 R 831 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 830 0 obj << /Dest [ 834 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 262 187 292 201 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 831 0 obj << /Dest [ 834 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 428 146 458 160 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 832 0 obj << /ProcSet [ /PDF /Text ] /Font << /F1 1609 0 R /F2 1607 0 R /F4 1608 0 R /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R /TT12 1611 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 833 0 obj << /Length 2610 /Filter /FlateDecode >> stream
+H‰ÜWÛnãÈE^õyjÍ;)Ã1°;3^L€ì.fˆZjÙ\P¤BR£U>$ÿÌCNU5II¶fŒÍ
+6F™jÌd5ù~>8#ôqÄ;öÆèj<-ç¿PÀ"°Èõ"ˆÜ0ÌR%¯½SÓ8vÓàÀ«Ø³^õ± zGnädÐZ]çeYïÌzÓío®¯ñ‹ZÓ¦\ª§œ×+þçypGYTâ<è•&Ò L0£„•ñ{]|Qåm]uyQ™¥*ª+öhª‡km~&nÚÇ„>Ìø
+ú\_C ³Î77½ƒ¬"Sþdê»~HŸ‰²Á ™•Wj!òEtü5ÑñëôHw<ÔuipÏç¼Ü:¾|0§^• e|0âN¿3«|[v¢Cö@r§W@Y¢ËÖPÒ÷_~=Þ¨û ak±öa¥º§¢ub`DÕ›®¨+U°N‘¶«ò‰`Ö]¨º2J²…¡vEYªÇ'PÚCêÑT¦É;ñ±˜â?bM¢ŒBä6ˆüª $ë%Ë™µãï¸Bp´ÙÙmsÑ#v0ònHX\C§"-šü²•·Î ¨
+=U<J"× Äîž âߘ¼£TFEñ¡ ê̯ª·ÝfÛ¹C$^ƒ»ÄGªG‡¸ bÌ‹!é}ýdò¥iºœ_»þñja“_pB€—êjù:´Å3ú¿D[ðMÑŒhãÂ¥ÅoªÊ×æ€ÿmã/b6­HЉ…Ï`°ŠFXE"yL pjÕ]‡z2é¶T]­69:VB-F:Tê{ú£3
+u©[T¨Tæ(~TåÌcQµ¨4Ý_~ê§i€{œa:\Oâ.Ä<а‡ 9Qÿšß²QY‹± UtRH®\~åA];Е¤ÜK
+ƒúÍN]!ÀçÊzì7ß…ç¡z'½b¦7¨ã`‹båDDÈÈuT}^Ô .嫲ì Ë‚Ÿ„L}j HÎý`ͤ.Ó9³0tŒ—î®_@§«þBØ
+Aߨ¢#ç|„­Psw¨®•[‡¨õ† 6@ú„m½X`|Üe¥ Õˆrá¡
+m(°eaÌn·å6D ~朙dj0fj©äGÚ¢/µèË^Ì"ÿ‹œÆU?Ö(<ÝSŽ„¯©ñÀ%×È+ê­“ÀsZ0
+±…£à#ˆ+Éú _,œÌ.Ò±%gC±Vo‘TȬïÃhv – 9ˆ~ëP°7\ÿÌBÞž±óuÙ±â[¬¼—âÈÊ'ГøI#„Bûæ–’Ù]»oŽ\Á<I>+,C˜£ÿH Î.]Q%úefRÜnýaœL]â8}óEMö<AäÞ|zÿã»÷ßÜô£G‚mû*ŠxŠ¡iã`æøÚfœô›JWr´{(äѹ¿¼{ —Êï‚:%Ñó_ÚǨ42ß$‰êD˜ðüD¨ŽÕãÍmS¯¯®/Çòég‚˜î·BÈæ%ÎìŸQÂó‡‘M_•H½éš­¹¾^O4 ¾|Aƒ3ûGd™¾=çÔ×—=?;–â7 Î…åü.åƒÝ=Ÿg>ÿÊv/{–Á‘çSâÜ>|ñJRFh‡ŒØ+rOŠ™š'\+Õ5Íq¦Ûo ±ò=M-±+ž¸zó“ôÄìKžåå¡‘& –óÁËG¾æÛÑ‘½¦ÈmªbJµJ¦9õηeêÂRlX^KÔ§#8GÔó²¬w­ZÕÛ†Ò€€8ã<àíöÊjuk•âË©aÕóßI=·yŠ,³ŠëS+ <h6r®ø …c&‡–sÝcj\1o‰ˆ«è²»w®8º¦êZÅ;1z1&Ä«1öl‡Ì³›-µøMiߘ6b
+«ÈÅkãPW¥„M«°ê3$D _Í>gá£ÇÏûÚ=-ýÕó¯•~ô‡ô¿/ýRu³Z€*¦§ÕvXýƒ1ªÁ³êÃûRÿÓ¾þ§cýç].9©~K‚¤æT‹zY/ ©^ºjÎç±±Dõ¿Zõ’´mq-J•B1 õƾÉ”¥}G˜ÍNâñ´4¬ª«éwŸÞ~ø Oy“/:Ó´Ô\J“[‡ZõÛ€ªeÛíKsP3ŸŒ“÷\:”e>‹'„û/ÇÓ
+endstream endobj 834 0 obj << /Type /Page /Parent 1652 0 R /Resources 837 0 R /Contents 838 0 R /Annots [ 835 0 R 836 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 835 0 obj << /Dest [ 819 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 118 658 139 672 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 836 0 obj << /Dest [ 1006 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 98 85 139 99 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 837 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 1607 0 R /F4 1608 0 R /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R /TT12 1611 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 838 0 obj << /Length 4071 /Filter /FlateDecode >> stream
+H‰ÌWÙnãÈE^õõXLš,nEÃ00Ó™tIc=¥J¢l\‘²[ó!ù‡ücrî­â"ÉN7º'@`€&KµÜåÜsOý¸\\/—Jb¹]JøøÿHkÏ×~"Ò,öí‡bY/®ßuZ¬;žã‹nÝ,®¾Äc·ð=ߧ9ë…;¼¾,>ɹã†^$ ú¯¤Ð‰pþ¾üÓ"ã2‘ú^–à”P{‰oá ïEoqb¶ú—¨«|Ç B/‘¾«£¸?6ýF”,ì$»²?ü|%þöoÇñ½Ço±ËÒñåúéJÜ¿”ýožâMB™7Çͤø'?Ù0vŸ]S¾—àcùGŒ±///žzìèÔ‚ŽÅÖ8ÒëïÖvè‰wúi¹D)*N¼0 D˜zZ‹$ð¢HÀÇH‹}±Ø.~\ŽÁLñO£1…Ú§ˆÀÊå?(a‘MXäù&D^êT˜Ï!¨i{©šE¿Ù¨úÊø»#/u4¬¿¾§•ÕÙ(ó²ü½âg»9Šv×—mÓ™pÁ}ñx•„¦ãƒáôÀþ®mú¼lŠ(›Ža*Çm-"/²@ £bôJ›ÜöÅç~·w"¤»ul±Å»’eU܉y»É{YÉ$ ¨óþ¸+œßwWæ¬`Œ0 ÈÂé0:Ì·‡µMuÛv/Öm½ËûrUVeNàK ëI<ûΉDH(/!eˆ<˜Œ1G„ö–ÎZP7–„xA8ºÏü$xÎKG!=U¾rÈdqóvôŒc†”øø+ÕJ ÿÊÖ“áì%ïäòdƒ²…0˜ËÆ3pžŠêC§ÍIi{Ø÷¡b1º(&µªÚ—²y}þh~â¥ØbƒèzŽô!¹À ÒÊØ”ûbÝÃT
+=¹µ3ó>G’
+„“°,r€øÐåúhú}[‰]ŽRk¯‹¦Øç=WÝV )ïâ«wR=8`d—<E; ~$B†O«vSØ»ÙðþùÎ=‡®ÜU¥9ÔòJ`6Ã<Šú“¬P]µl©åî TÀÚ ¥#HS³C‡„äD(ªÊ¥®(†ïgh
+ÎIâð43†åL›TY ÿº0ÃæÉ`Iäcq¦‰ˆ|'7 Æ
+¿ ÇߌÞp=Þ¿ê’h]Ö$<`9ÏÙ"0™4xy Å‘oúqd^¢çýøm'Ššðkk²™}Ã8œ`LNÁ†$ž©GVF°’fNVÎ
+ÞP6J–ÞR=n.c˜/fJþW9û-T:ÓIÃ`¥©Èê(î8WÉçv¾?6ë_þ<+Z|zÚ΃©„[C÷| Kå³ãKû
+Loĺ*mÓ&·ZÑ›ßòÆ ÕÜòËÞ\Æ„¤æ©Ð<©å›‘aUùÖ˦Gq#Ã÷üŠ–Ov„ ÆؘÑh¤hµ‡‘’\–ÃJÙKƒ°lb&ûïóVUû³É*ƒ?º»ººê­§Ú©Þ
+»iæû#ÜǾÔ{î/½Ï!Õ'ž
+Û ©3IÏ¡ÜNÉP¹2æ$hùdÜo =
+9Oœ°üYÜ—uEÉåÎèÜÔcõGe¿Ý1ó'¾j~µº˜)ž_4*é¯ávyɦ @ۢ܎ÈcTâ½¥1y;|_YÊP¹_ˈº8Òémá¨M±•´†“ÙæIWù ¢îÌ"±ý ¿ß¿1r¥þ× $o‰o5/—‰Òݤ8]þÂsÜÈ#•k«Wúêã±úd0óáÈT %- "bö3ø¿¨Jµá& ÂÑ5ó»0Àžš£Ídhü¸ç€AÅvì¤Ã»qÒ•Ïž8ý¨úÌE}VUþ­ía*Ðt’c*{ñlyDMî³›“ªþ‡a—TýœR\Åçþ/b·8Îy*îwdÙË‘¾\tmläÚØ'EJ“²M›¢7çÌ[ˆ
+¥T誹ùLwÛ ‰È‚ò.ÓÍ]ÄmúiŠ›Ø—+GÊ¢)†¶Í¿³Kµ¶ÕHˆci5#ÂhÚÔùßùÑ+3€üÿ(Ó’0Än Ï]I ËCÄRБ/ïåZHÚ¬°¿Ãyo
+e:Suqö»´ÈÛ`Úa6?à[¤ŽÀ(%ýä§óz×°ê) 0Û‘ÚÙÃ~¤j»ùÜi©æñ¿è©¦ÿCSõ±5õWìA8h]Kû¸yŠ2,L€OÒqÆÀð}@€KÓ{cÄü‹TÎNÀÖÊCLâ—7ÕyÝ úiÜᦫ¾ŸŽRô±G*û1%•Ë;Œÿ
+Ü+BHŸ¸Ê¡z—µ®sOUµò˜A@Azo–€…µ÷¤ž/Á‚D¾>áÈš¥¯·û
+endstream endobj 839 0 obj << /Type /Page /Parent 1652 0 R /Resources 844 0 R /Contents 845 0 R /Annots [ 840 0 R 841 0 R 842 0 R 843 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 840 0 obj << /Dest [ 819 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 468 612 490 626 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 841 0 obj << /Dest [ 761 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 452 374 473 388 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 842 0 obj << /Dest [ 819 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 160 361 181 375 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 843 0 obj << /Dest [ 748 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 272 361 294 375 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 844 0 obj << /ProcSet [ /PDF /Text ] /Font << /F1 1609 0 R /F2 1607 0 R /F4 1608 0 R /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 845 0 obj << /Length 2834 /Filter /FlateDecode >> stream
+H‰ÄWÛnãÈE^õ =5‹fó.Ck`ÇãYÌ;
+ÄÎ-µ,)H”=ÞÉ?äóSÕMR¢DOd Æ"YÝÕu9uªúÃ|t=ŸûB‰ùj¤|áá~Â4u½Ô‹E2Ü8õ1ߌ®ïö©Xìy'ö‹rtýËW%ž÷#Ïõ<Z³MšÇ×уüSæL7”Ïš~})ÒD8›ÿ:š²†©H<wã” ucÏÂ
+|ÖEOQlTýÓÇ׉ï9¸±ôRñô&¾¾•õ_|©h’û|/~þåJüõ_Î$Âû²Èdîxr±¾__óúw½+XI ³réL¦Rüƒÿ²aì>»æ{nŒ—ùG|c ^__]'p•ÜÓ©šŽ…jéâуw ûiÍšîç#%r1ò£Ø b%‚ÄMS+7 | S±Ó£Õèü F °Ä;Fj"+ç]:ËWÈ$šRÞx¯üƒYšÅ8Ó:dµtiw½ÐJÆV’¶{”‘˜´ølD™mô©ß ¬Z¦¢ ôHÁ‹½#F[G¹©Ô‹|å„È
+ûÆì,”1P@ÎÇÇ™¨fÅÄr;ùî·¾‡ï¡ñ`¢ýÊ Ë<j£×$÷|…OæEÔ•¨¶u¾Éo“_‰ÜhÑÁÁJ¶,±Äç3ñÁœ“3%òÙ¬¬Þ dÏp; /t¹„õ'ZB׋»µ~×êý ƒ±“ ÷Q2XtÊT¾Q@ú„l-Ó¾ÜeùŒ4ñAVº’}ßÑÓî¼A@ÊÞ7Ø?Q¸q?L“æñb?ëRïò…øò¥ññóoD†©£Ë1˜¯±¥¡›=yY#(ßUµ3¥L¨7U‹ÐUíƒ\öuµ¡•›¬ÞÛƘíÐ:«šb z(¡…¢ &ÜE"¹ûÿõ3ÊZ«û¢é^ $BõšI£ tCE¤râ(b '^ÑPÎ{ds%Ì¢ƒC8¤
+_‰WYÿÑ rkšƒT;KIzœÕž¦ÌiN1(÷éWš<ˆ—Ù7ÂR,óªë=:½~×M¬©ëGG…“v…“šChl©3ƒõYöµ1sY¶½EßÙ;4ºk1
+Ô.êâíªŸ0LQéQ‘¨ÎO›¯§C-Þª<,¨â•,¸SŠjñŠƦ?&ÜÇb€ ,åóžÙ$¢KažkS!f]Ý4_#ã …VÇ‹3±Í°¹8YpM]_5…q%ÌbDl@G¤oS½äÆÈ&ï[èƒÓ^Jϯ”j½™½RðD¿¥>Òïš35c²Q½tÐ3¾Û»G¡o»ª¶LaBÂ
+Ô7VqkìuVï”=Ï/Íü°ÍÈ>¾¢k#
+w
+2D¥ ñ63åaC¦ìoA8íóiÔ4vÓw’1$8>ÁDÙ&#/×:[êÝí*+özvݾ÷LÀ¯?€ñ€Æ«–k²¢¨^õf[¿ÝÖ»,8úг!ž—1 ¿ FdNÃxý#£¿ï9 8·Sß{óû¿œÃO…¾ùïä@>~…˪?o~>uÈw‚0 8Þ ÿÍyä
+½ïuÈë^Ó±¯ºçyÙ#¸ 0
+endstream endobj 846 0 obj << /Type /Page /Parent 1652 0 R /Resources 848 0 R /Contents 849 0 R /Annots [ 847 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 847 0 obj << /Dest [ 850 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 429 88 459 102 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 848 0 obj << /ProcSet [ /PDF /Text ] /Font << /F1 1609 0 R /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 849 0 obj << /Length 2342 /Filter /FlateDecode >> stream
+H‰ÔWÛŽãÆ}×W4ôÔFÞE dÞõØØÀƒX ’ɇjI4(R!)ig?$ÿôƒOU7/ÒH'‹5ÙìªÓu=õa9º_.=áŠåzäzÂÁ?üql;±‰é,´£ØñÅr7ºÿXÇ"­ùGÔi1ºÿá³+6õȱ‡¾IG“vyýMþ)±&¾È¢_OŠ8Öß—ÍXÂLL{A‹Û‘£•°
+UAZDaâá‡Î?Ë„\ î£Wíï*[£,ͤþ«èP +U4âE5'¥
+q„Sãn‡e본‹^<[¢U@’'•çݺ
+žàÈvÉVZR‡ HP|¹ãü¤¢œ>±’Ñðâ§Ü>A]“¡i¹ÛŠ,MŠ·gÜ'%¡E®«/ÉŽ²Ê“û\Ý1šÔìé¿*m
+¾‚Jf£ZH]´iµº£ôæeÍÍ¡@ ‘“,O¸”¿àºæ€c϶¸òºH˜HŽuPÃhcÜd|ðµ2L˜BÆä›úgZÇÊTÚ;‹*¦šòlÀM[p„‹4›3ÀæÚºÔ:!`ólG£>Gè]3éo왡Cíûýžé›žI]¥F,6‹;1G.ÎlÄ?t4[ºi>}zzܧúša¾ïìÀq§}¹íÑwV_âf£ BºˆaÿM{uû.â†W¬ Wpùò؃— Ó^Ñ\w™Å­
+#ƒ u†y¿KÔ˜ƒŽ`Z™ZüÕ¼Z“
+¦Hä°~ Í6¿ìS†
+à‚ UG¸€scÿ †XÎñ¬m`v˜ßŸ¿º€âMÉà·¡ÜØ¿ ̨ 1…<бC³¯Ê5E•YŽïßDlOb[mÑ¥—o쟡‰cÝ왃[»ÜwåáÜ
+œÛ»´f÷vÐÞ8þÎvk "A”¡waŒû0Æo¤*^AÓªD]÷ T%
+~&ƒòbaíQ¯ûUVãœ?B¨÷*Í’¼§*aÌTÅuúþ¦y Óñ|LE5UPf<ÏhWXLÑ`áN9©›„ØàŠß=[ÔHQÓ³ú¦Ô&ðwG“^¢øò[óœ4—Íö-rg3$ÿÀ"nGþS2äM/ʬ0þ¡‚;#nCcùègBð
+endstream endobj 850 0 obj << /Type /Page /Parent 1652 0 R /Resources 852 0 R /Contents 853 0 R /Annots [ 851 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 851 0 obj << /Dest [ 846 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 485 479 515 493 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 852 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 853 0 obj << /Length 2302 /Filter /FlateDecode >> stream
+H‰ìWÛŽÛÈ}×W4üÔ$o"©=€í½`»› –³‹xòÐ"[£Ž)R )ËãÙÈ?æ!§ª›”,=Þ ²ÀÂðˆdwWUW:Uõb9¹Z.#ŠåzF"À?ü$yîyŠl1÷Ó<ˆÅr;¹zÙå¢èxO º¢ž\}û*wÝ$ðƒ€ö“Ùðx˜¼‘VÞ,öy§é7’"_ïïËï' –°Yà/Rh‰s? ¬±,zš§VÔ?#|E7 c?•A.V÷âÕ}Ýoð%’Úƒ$Ù™N<ÿv*þö/o6Ç{‹µ¹?—Æ d±™ŠWÓÐmÅBb©êÒ›-¤ø•ÿ²a|}¾Zø)^–_á8|/öCÙ‘VMj!*}<¸]á>mXÒ×ËI(Œ˜DóÔÓPÄ™Ÿç" ý$¸c’‹VOÖ“ËÑqˆ-ÁÇÞ8º: ÀÊå?NÂ4ø/Kÿí
+@vŒEfÍßµºóæ†ý û#?—å­'ìãa{,?hѬî'L¯·‚DžBüd¯|±ä½/”ÆSUb£v;] ³ªöÂP–¢©«{z'ISñã¢h¶Xó3‰üÝצP½ip̉2õ„Yèãvð½½B8 žè2?¿ø×M»U=T–VÙšËQß_uÛYñâDÓŠ¹ÛèÖ·Z
+êaÓFÛ R]Y‹¥]³íö1]oeð¬nl+aÁŠ~ôôT„ÏÐA|ZÕ,AlFV˜
+²TÑ–Ômé‘™|˜Ó&Á¥ðâõJ£¾Ôê³Ícîþ’qáx5jcB9¥ ¥°;*»ã€Z»·_À ‡ :EòˆÉrôDÌtÃ-Æê2(UýÞi·="0ajÎc…¼+áR'Z ¦UšÀÄÚq뛽/¡î$¿“SêNǤLN¨;U"žÂjÕÞïÊØçò9µzb_ëš÷M©ù¡v3w¹´‘2—‰%ÕñGI7¡±'ø?$ÝøwHºÑ“tÃù1{\»¹jšJCÎ;Uíi䡶ø *Àÿ”~£ÿœ~m3ÛSk„þï48œ:‡Ç9GŸi…¿[»fœúÐuÆK§{nÑÚ½>éD• Ø*ꂘÀó,AC÷uS¤ZŠé¡ÇÌ@t¶„Á½»ƒ ¸ÉuhøtÍ­ÕÃSE|ÞÅQK]èýÔ,¡f’`ãšò-ë=Š=Ít!ïÄò›—ø•"Ê1Jh»ÇTÜãÚý,
+a.6ôm a0Ø~²²¬k0ói;ÿñ1fœ-uÌz½6… å¼¾L1;
+r‘ã‰ñWA_
+Uaô­ª»5:"bâà~ý Ï|†Ç±ërâ:­†1ꣻó¼E“’²Bl‹¨±õïÁë_†È Y
+»FÔÎMgPŸÊz‹bã©böà€uLºÐ%ÕM£TŠ)EØå”oÜ¢-ǯ—Øó iËeŽñÉ ¢:g¹Ü³÷ÃiÊK>Wz ^®|™fµXÖw4ÒH† ©tJ¸¾ñã¼è‹^©¨ƒê*«Ô„ƒ‡"ÉŠÝsÏC ]üëåäß
+endstream endobj 854 0 obj << /Type /Page /Parent 1652 0 R /Resources 861 0 R /Contents 862 0 R /Annots [ 855 0 R 856 0 R 857 0 R 858 0 R 859 0 R 860 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 855 0 obj << /Dest [ 854 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 374 452 403 466 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 856 0 obj << /Dest [ 863 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 142 439 150 453 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 857 0 obj << /Dest [ 871 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 278 439 313 453 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 858 0 obj << /Dest [ 875 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 426 425 462 439 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 859 0 obj << /Dest [ 883 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 166 412 202 426 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 860 0 obj << /Dest [ 1346 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 256 133 283 147 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 861 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R /TT12 1611 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 862 0 obj << /Length 2389 /Filter /FlateDecode >> stream
+H‰ÔWÙnÜÊ}Ÿ¯è·4‘&›ë‚
+Qu´&]Õ,Þ|¼ŽÄ]·ƒ0Ä5ÕÂï__ä/¥çÇA"ï4>•ËPx_ý~±$ K‘‡Á2ƒSâ"ÈB>„(’…oiÆ¢þ©`ÔW¡çGqÉ°ëGqýØØ-Œ(©=$;Ó‰wÏÄßþåù)|`. Ri¼PVÛ3qý`ìoúP“X–ÍÆó—Rüƒ~I12ŸLSaÁÇê'# /"Ùá©Ñpd
+’D€I!zq»¸\ Έ#Xžzctuˆ-W¿NÂÔû/ÏÂ
+hVyèGSÖgnÜ-ßh÷£myx·íaçù†Úò:Q›¯ZüÜ~5åïÜÈŸ¯®?B¸`­¸¾ºvòY^S A‚
+–¸
+– u( ˆq‘ þì³$Ëó W“2Žiº4ˆ^
+ÄQîJNÅuGRV~³”ø`gñ´RÆÅQ Š‰qQŸβ÷ ¨4Þ@ÄÞRÉêÏ@UÄ=¬Âù ÆyÁ4)é{í-áy–£$§ O¢NÅEé‹|w_kP]®=¬Ã5€PõöûÚøcƒ™ö€Þ/ÄRË?ˆ{}è gÜwÕ‰GyÅ ¯p.‚Q±›ØCé "ñÄC]…%$•ãù‰ÜÛW\Â:,–,åšð&\Ø1CÉÊd¯„k&s7Ô€¡ž¹F èä:5ªÀ·”e+ß¡¿A’=˜õÑB•ó1'QÃzEC”lÚF?Áóœoz¼ü¤oËcmÙ Åká3ú·<óŽSè‰}“D£ép²'}tEjKí»$?*î¬Ü®¸EÁ¡
+¼¶VZ¬5÷À–¤ñÆiÆú
+¡¢&ÊþþГöI[]å%h#zñTg]uÇTÚÜ›æ®ïäâ„@·„)<>Ÿ¨èr+dÈ\_œ!kè1è`[ukåošÍÂ6ôËÃl {PáÚ©G€‡†µÖU r„WvbßvYCßÇ–P"¬®>‹Nw˜úxÔ\K›‰bï"±1ì#þÕÓö¾°ÐòÄóÓ ÍyØ婯1ç€ {¨2&'ÆIÐßÊÝ »nI¸ÏÁ*ˆHÔ½‡0zyœ³qÞxgäUð ?šåWc¨¡àñ9Y¡ÈŠ„­pvgÎ
+cõŽ‡xðlN…ÛiEæñ ÿ–¸½p" ”bTîd
+èJéd!לQ„F'Jø¢¬í¶=Þm¦®!ÈAf‰ Ê«r¦ïÚŠ+êÕÉPmÍŸHä±³=ø+Eb¬»‘«»Ì ÐõÑÈSàèA"ìë*
+.L”¹»Ê .wšçâNn‹*8üã‘Ѩ2žɘH-ùŸ°î8Wx­°îô)ãèzHu/Œ= SXª—Ä›vŒ€žËçË€ª‹÷Ç zpûé´öGÆĸ£0žãélè9n. GQЉ:uÒŸ2¨çÔ<V¾ 9ÙFÍã‹š«€šÇÿÔ\ýo¨yüߢæêû]÷¥ê¶¢šµ¥>çê2ïÖ9×6‡Ž9󙆩ï]å|$6H©/~u=É1¬)yŸu+±·D¢°ß»²†ÑÍb~²¤É%o¶_…jŽê3ëŽvµÁ“ÊÆÄؠBȘ#Þc¤rˆ1¡›‰&T>=J!ù>(Y2¿´N.*×÷õg‡V`{.«öÀ’[®çIh4Þ"W!X£7ÎIè— ÉqÈžCW2!‰%F&Š‰ë¾|™)˜I÷ôü"xNÿ_Q:ª•Ž R¢‚A¤ˆNI­ÔH€R©§¬®Õ‘'­#éq7ÝlÐZv~Cg ¸CR–ö”LI'|­¡"ÄòAë0DdÅí,k*r’§ëÍ< fM1TÀÉAïºÓ {)-^¡J, ]*7¤²»bmúEýüÝ_Ö<ª/[ÚÔO{Trìîlfl‘‘+
+Ö¤“nVLªXÓ‚áp}RK¢
+endstream endobj 863 0 obj << /Type /Page /Parent 1653 0 R /Resources 869 0 R /Contents 870 0 R /Annots [ 864 0 R 865 0 R 866 0 R 867 0 R 868 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 864 0 obj << /Dest [ 628 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 515 611 523 625 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 865 0 obj << /Dest [ 644 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 437 598 452 612 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 866 0 obj << /Dest [ 1035 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 311 221 352 235 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 867 0 obj << /Dest [ 644 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 208 208 223 222 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 868 0 obj << /Dest [ 1035 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 323 128 365 142 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 869 0 obj << /ProcSet [ /PDF /Text ] /Font << /F1 1609 0 R /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R /TT12 1611 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 870 0 obj << /Length 3636 /Filter /FlateDecode >> stream
+H‰œWÛnÛH}×W4òÔ,†wQF0€gb²ðÎb ˜xÚTËâš"5"eEùý‡ýÇ}ØSUMêbO’]±ØdwÝëÔég£·³Y¤B5[ŒÂHø‡Ÿ$Ïý 25™¦~–±š­FojsU´¼'PmQÞþ|ªÇvøA@{ŠÑ¸Ü>é_7ŽýD?Zú´š†ÊûÇì/£)K˜ªIàO3h‰s? D ˆX=¥™ˆúw„·ã(ðÆaìg:ÈÕÃ^Ýíën‰7‘¶$é¶lÕÕÏê÷ÿxãë ¾¥~ªK/ÐÅòBÝíÊî‹ÝT,$Ö¦ž{ã©Vÿâ¿l»Ï®EŸa1{wlÁn·ó½ØuKZ-©…h¨ôñÀ»Â½Z²¤ëÙ(T¥EiæÇY¨â‰Ÿç* ý$Qð1ÉÕÆŽ£gC0â[‚ÓhBPD`å쟔°Ä%,ñƒ?Žó‰’eÔIšú“è(ªÁÕ Ÿ`wꇅZ½[”Ug7ÞÄŸè².»¶Ø”XLõºÃO¦¸p[x;bË!Ǧu÷Ã%oTw¼?˜Öζ¥zΛ Ðb?‰ãH‰ œp€É¢„m³œ!9J§¬åœÎÏ«tð4˜úQ~äiØ;êüü©©;SÖ0ª¬/9]=ˆuÅŸù“>áT“#{ÞÍMç%H±ûÙ¯­úŠA®Ï ³(8¸-ú¤¯žMéEð«2Õ`å!Ú^þ¹5ç©s½UÖê×Tæ±þ›z¶›¶lêV5uµÿss⃼|—»™Z&‰Púé¡ßS’ájuÝm75%î¡i* ¹Ï¦ÚÚ¾t_’È™VMÅ*)(6Ì~î¼1•„˜—}˼ä`^"Âæ G1*Q`©CY´†¨^(øžÐ…ÙQe\Q ©Û”Ûζ´ ÙŠ¾a!—˜®›ú{bÓWÐ{»0Ûª“ ä_Q1>èèŸv¤M¹4‘Ê3ÿ‚Á3šà´ˆ¥­Æ
+XßÛªÖFŒ¤aeû/ÎÆóÊ‘úŽ†v•ƒ„l‹¥2­º›]ñˆôìýÕìú^ß{('@ƒ¾þå}ÿ†ËáîÃï×·þúÁCÏc·ì¤sIâM0½vø­fNðoת\¼žê—É"ß_¤è«é¡Ò£Ï.’(½ÂTÒ$Rλ Ÿ}v÷ªhÓ»R«f^öñ&©”bwÀÌç<ó9'Ôo‚ ÿDŸ¤Yg¸.$•9±îEgÐBŽmi£<jR^vX\¨’æ^@
+(ÒɤBÞ°7¨F«äÛÆþ±-7.‘ŽÓr÷éÚà},Ðl^â)fľ6«²¸=%FënYbźR-xéÏ©Åc÷¸U×àvƒŠ>}{¯«ò ¹PoÂ@&^¬+Ú·hBXlâàõqv aÚ³½2Ô‡467ðë ¥ÎÒóméÊÍ:ø@ð%›å/.º` Ç £¤£Ç‘&1üPwÔÊ–åÑA®“„gaí{lÚcô£¶Cr>¯DAçÁÛ®,\¼0Öœ VîBóæ@¥_p)ãŽ0T„¹~¢: µ,ú¼Â… åIV44V1„ÞP”
+m•Ÿ­G”n‹ÁÔý¥ªt–ÄȤtFÍS[¾—í8QTt• .Ì}ׇ úÐé/y@²:%®÷°k~ñz½$Ó’Câ\+§XÈõ“4µQËí#MXuª7¨§•!PEbvcéñ^ïJG½—
+]yƒn¼æÖˈW“õÔÔ¡ž¹Ô“/P1†Éí=¿ÿièªBO"sY⺴@ i‘öø1Q' Å¥îö€8ó(¡ÒnQgÿ„Њ!^;ôã™#_L')iÖ ]<чù•'øŒŽ® çìû“f4@Våã²£rÈ8‡ê-ä瘯Úf…,lš‚”— ¨"›c«n*àf–ócÑü­
+x×Ó{ÁëP žgš°ù ³íæêöîúÂ!'½]ánè¹Up79ÜúMµµ4ÃÙQaæÃ'êU# 3„Å¢'W‘®*Ì ð¿2ã_g¬‡ Æce¾šõ|5Z,'Î!ƒ-Òò¢dÌꙪðTô5ÐÀ‘Õž§¢_)YŽúäD}äµåíF(SèÄb ¤‡\1ʘ¹Æ\¡¹”kÖ/zq¼éãeÍžH
+ɳ”å‰ÐBÖ÷"bB­°rÍúl`?{š|F²€RN@ôá–wÜqŒ®UƒC²s'’8dÖ§Â85ä»î³ù¡¦s—:2ª©Àˆ¹Y¶uÁSš§³y6ee*î-{Ä*)±Œ"°’ýš0ÍÔ„g}g? ˺ü¦‰hÿÜQ8Ú”¤§<ŒéË AË©@%*ã¿Le€š)óaQ¨ì/ ¬>$TAƒH©(@=dA¦¢<óóœxÒê¨ç{rɧ>N^à0Ê{ïòœfá:SÏ=QØÕMCõ…:Û¨X¡J( À
+±h8V9`ÕÑ Ï©±„öküøh\¤½9Œ0ÈÝŒhô Þ蚸n(ÌV–Ö½5Àq/šQ§þ]·'j›ã;‡»ÊÐ=JrmŽ¯£´èâ&Û亴}\ªÿù&æuH3’§Àd:Øì2¹AeÍ Úy”"ë>S±¥‡;çŠf\”STÿ\žM»©?ÕH”8Ma䂃ÈÒ…tªy®²”\´¥°×/rií¿UòÖ}\¦ «Î‘zÌ¢åáðb'‡ÝÛ¤Ù)æéyP|*ǽu:.Y—<kÑ…ôG¡1<’eƒÛ×öZæ¾
+i|`ˆUÚƒª\\Îí‚IFIÕµÄùòW~Á½–7Ðð¿”WKoâ@ ¾÷WÌÞ‚ÔB$Rµ[Ú
+©í¥ôÔ^™¶Ñ¢d¨h÷ׯíÏ >.âL˜±ý=¬G¢¾ãGéºd½©dZa«½"%ÍTóúA> ¬d–Äâ2SÎÐFõèÖ Kblzru-vtŒ >ÞJóΤe¤KY¹B‡zJ"øu']<Åc c“¹T„XC¤A¦ÎOÙ82šŽ
+ë&_úpاH#îër3½÷;eTÇ"5¬;¥ÐÀ^÷µÇm,dpRyˆòÊ”Ÿ¦ZBâKÑÇ{â3¤¿­ùT|:Ú㧊™½Ôš°ry¦Â=DK»Õ
+ÌvM]jàˆÅQr^U<­dY¬é¼:÷DOà źÈð5g ÖO()]­ ^¤jTxÊ`òÈÖÕ»]­ŠÜ
+7Å¢OÔ ¾Y+Ï»<\þÃx†#P#ʬ&ÖAâÎÿº8PÖY† ~L|Ø_'¾M[£Ê/nO~›‰Œ³ë܆;ç6Ä"ô:uùÄPa\_ÕZžëEw4_Z pý:Õ²œ½¢.ôyÍ9Ý\qÔ 'Œ¿â9ç¨ii²</˜ÎÅBˆ úµfA•!Q‡l¹Ñ5ízÍãA¡¶©åm¨Nüª”v¹®`\‰C$ tæ€ò’kËöÙ|×9ž Ru衤¼ÉcŠIÚMƒg{²_+KÏW[\Ô.°n…g:X@Ž.¸ñRGo÷ø <0ñ´pd݇k e¢
+ÿÊE’x)»ÿ8fòÞño üÛ¶ï‹UA)¿øñt9ÏÆOüΫÙYxlGäƒ ¡ž¨N>Wöìå³Xk,$ˆ;ÑhXúap÷Ÿ¬2L–í‡~Íö²’&´;ÊJDì"+ON¿O꟎œÅ›]üÆ%W¾·ߥµ9®6¾‘@)Î.[Q¼ÛÂÁÖO9_žNØñÅGõãèdÊŽ„þ 0
+endstream endobj 871 0 obj << /Type /Page /Parent 1653 0 R /Resources 873 0 R /Contents 874 0 R /Annots [ 872 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 872 0 obj << /Dest [ 863 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 307 209 343 223 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 873 0 obj << /ProcSet [ /PDF /Text ] /Font << /F1 1609 0 R /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R /TT12 1611 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 874 0 obj << /Length 3273 /Filter /FlateDecode >> stream
+H‰”WÛrÛÈ}çWŒý°5¨2! î°%WÉ"åõÆ+;"œªÄt¥ p(b ,WÉæ7òùÇ<¤{f@\@NÙ%€è™¾œ>ÝÓó.œœ…¡I ×fþÁÃö}Ýð —x£»¾a‘ðarvUø$.ăq69{¿`侘ºaàšx2­^“¯ôs¤M-ݦ÷Ÿ&%I´oá/“@hˆgè V,_w iD(0….|s\©ê?&|š†6e–îRÃ'wOdñ”•øbR®&Z$¹|ÿŠüí¿ÚÔß;9ºCÍ ñæY’ò|—
+%²•6 (ù·ø+á‹ÐLCwáG8ƒoƒÃá k–ÎhV9šÕ`R‡W¢‹Õ§Ð4'Œ$db:®n¹ŒXžîûÄeºmˆÑöÉŽOÖ“wá ‹Á£F µˆ€—áo“³k&ó9²Åñøºƒ`z.xé6Ð`~¥nÂùûù-@xôf>Ÿ]øÎoßTîú¤œÏcð$Âņ£ÏJ÷(µMHÓ”;†¯›öàögÄGÛ"'Bt7žˆªM
+¼±ø‡é6E _“«}Qj|Íȶ)–»²{²ÎwDd<¶ð¤ ˜INJ/Li;Éâüºä‘ÞaTc¦îR©‚ôU¦È:<Ž‘¦n™ÈX“º¯ò¬Œ’Œ¯H’½' Ý«ÕšR­ à€Zô 6šžP!·Ÿ¯`<µu“ªÇÓ–k@wúVkþ‰ƒS¡
+‚eV¥Ž1Y—Q¢™€QÝi–nÑTCÄøëao$HÖ1UVù| u:>‘Gàw’gɳôiسÖW§ÞWE‰%L!ç„ §N¡ƒ:¨,1 e¹ße˜ÿ»<O9è}ŒÒ='Xzƒ I=ÁÑ«@zµ*Ñ1þ; ü>€%ÝsŸsϮݳ¥²Ü>óR<mZB…j n-?sÌ¸Ä €¦r—À…ãiu€20ôŒ‡‚bp¼g?‚MÅ _Gû´” ø#&¦µêí€ÖˆJšìÄg#£Bl´I,ËjZ½büá&)*…‰p) Ô8ƇKÜàÈ_c!4× ÀÝ
+¾Ô^€\õ—F Çj™6Ë%ZÀ†ù#ÏÊ}” žK7 ˜-šiHª{uNèD„ÿÑè6bNÊœ$Û”?€i›Â5[E; Ƶ$ßïšîtŠÿ˜Åƒ˜
+Í”öñ†Dò•ì
+âÓêeBü:!~UŒe„ˆ* ÕÀ
+—Ë ”c {XÂk1”%âtOª‘)x ²½úHdÿk¼jzÛ‚轿qé®d#ƒ ,Ç*ä`ÉŠ«:=Tò%5¤EJJB±*ÿû¾7³l§R/|,»ËΛ™7oâtR¿áŽ¹ƒ;æ1Óüà…OkqNrÄymM¢E>ÁyÌÒØg)âûÏÏ™ÏTü®1>TU
+3³ªªÂ#ñ¤¨@î&&œaEü`ÎSÝûA˜_GWš$΢LŠv^¼Õùdåë|–¼a8§c{‰±ŽñºûfçÄí³ýL3s³—ˆ:7nš†TêwO]ê-ý‘G9î±)
+¾ÃÁqb^µÎîí’î{ÆmeGÎR¢8òÑ/ž½å^á ¦@”:hkt+˜è 4ÒªòBTysZùQX úá5ä«(O¸Ô ˆ,Gñ»ô{ ÊpÂ\B
+”_”‡ÍYÚ“´r¯¾D,Õ± ¾êë‘jh<·UóxJ¶÷q©dVL¤Ðs­KÎ6­ U&êðI‡‘¼ïüûªèO»7mYÌU÷ÜÞø º6L
+endstream endobj 875 0 obj << /Type /Page /Parent 1653 0 R /Resources 881 0 R /Contents 882 0 R /Annots [ 876 0 R 877 0 R 878 0 R 879 0 R 880 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 876 0 obj << /Dest [ 1002 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 128 646 164 660 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 877 0 obj << /Dest [ 1035 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 128 633 170 647 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 878 0 obj << /Dest [ 992 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 486 549 522 563 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 879 0 obj << /Dest [ 523 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 220 176 244 190 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 880 0 obj << /Dest [ 863 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 157 136 193 150 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 881 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R /TT12 1611 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 882 0 obj << /Length 3886 /Filter /FlateDecode >> stream
+H‰ŒWÛrÛÈ}çWLé%@J„q#¸\®ÒJ²J[Ž¤’¸q9Q b$a—¸
+wœ9æ?üËŠ±ùlZè{ ³3̱¯¯¯žyÓÒ©–Ž…héáÕ‡uszfIç³Q`J3
+'‰%‰¦^šš$ðâØÀÆ85=Ž~˜mXâï{cpµO–³Ÿ)`éaÀzONßÃÙ ÿé6*ôJ†´ÃúÆ×DNå"D.yÄÜžÿízv~yvïàkêÜ»üxÏ 8ɼh¢¾!¹ápD(G˜[Û­3…ð€ÜäR< üfæúA~áÇW);»ü‹®kì²î¬¹Ä(tÎÜ¿žé•PÛǬÅ8ð¦i"À 7¢ÄÝùLÌqS`ðÒÇ,k»¦¬žD;0ò¦~’ƒ,#×ÃQæ…é"ƒ˜ý/Š•…ap~O4[·É®{÷½ÆMlâÀ]µIF ¼³]Û»«±[÷l=s
+‰£È±â ’ ” ¬±H– >´ƒ–)ô‡+¸äÛ A‚…C† ¹ª rz}ÅÇ|â¤û|y:»›Ýž¸S¸uv~á2©}¥˜BŠ9¿c;Þü½T
+cœ#'’Ì'Z‰4`¢ø ä h[²,rž6ƒLI±ÚŠ“Zö9fö“ Q˜ì&™Â=Êúò4äX]=.ÊygZÆOìtöi°Ñ{±^á[.ôÑql-8>’¹Ê¾Úf¬iyÙt-êr/HR%àײž;:6GòfÅ/ú³ƒ÷ÖÍR[ÞÔ¥2Ün á>$cdïé?¤`ƒªhê^ù-9
+»Ãâ…%ɹ c½èv%Üš+Ãd©–ñÖMØ‹!WgÂO®"ËWÖ‹PáEœ÷âƒÅH*©u–¡ÛiãÇcr
+²õƒHXÔ¯ò²©æzâv5‹|"”¥Îæ£ÁFêøùA)e­nìÊ¥ý=­¹ø³¨{Ýb匷©#$q‹G@3²¡ª±v tÁÕ‘sÊ¿¾úô™!Á˜'ïøèXG·ô)vNdt~ñõžÂ‚ùPä@-y4où[W7¶¬ÊNFs²f•«Î“Þb}bCö9pí–GÔ‚
+nÕ Ô¹R°¬Ž ÂqFû¥:ßéóÁÍ<ÎR´‘…µ:¬h/ÎFÇJ¼æ7šzÊ–çü…3ˆOÙÆUSû-ŠsÒ¥¢ò»lil[/Ö•a^[¬ûG%KïÔ¬³sÒÕŠÐ8:šI¿¯£/Œ™Ø¹º{ 'h²ãæUÒRMHsÅŠ/6ÇL
+¯ÏÀ³8Ï¥3!ÜåÜP/ÍÄEÑY-rä z#kê•Bï´ gë•Íb°™x~Þk°'E>æs9Õä:ñšox‚y0Ò°¡
+·ëúþúcº1ò?¨?€P—'ëj!­’.ÐôU^‰ Ñ먔¾`¼
+KÚö^`»K•¡'*ƒsŠõ“V2N(%£
+SE¯9Í9•sRÉ´D2-î3ÂP¯¬|â6 H-kòŒšF¿ºy@$žªZåbb¸¨ÿ¬™ÑåS€ü~ÝB;ˆÕOKi¾dΉDù)
+ˆê%ëtÿtÂ>C¿šlB…Í›}³Ü$Æý+©+4MÝ ÞÖ.žôçz‡0uµØQ<§AÙ~=bâîÑ44õ(V*8×H[;_wÄ‘ÔJgÎ3εµSj ¢Ý#‡XÈ!r(†´‹õn{>%CìEI†Sôr ZÊ(ɼp÷Šë÷÷(nkùÖà{èB
+Piç<n@€µó„óÌÓïÍ)ˆMhÉ´ÖÑ
+/—Íe‹
+}À
+Lžèy(vU©&ie¶º¡FAʺªÛñ•Ê„–øå=aÈ„•fBª™
+endstream endobj 883 0 obj << /Type /Page /Parent 1653 0 R /Resources 886 0 R /Contents 887 0 R /Annots [ 884 0 R 885 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 884 0 obj << /Dest [ 523 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 320 242 343 256 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 885 0 obj << /Dest [ 863 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 157 121 193 135 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 886 0 obj << /ProcSet [ /PDF /Text ] /Font << /F1 1609 0 R /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R /TT12 1611 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 887 0 obj << /Length 2926 /Filter /FlateDecode >> stream
+H‰¬WÝnÛȾ×SÌöjX4ÿE¥N
+­€ †¾„–Ë¢Uï|T„lVÈ3+fYïüzb³§¢g™–Egf½~µÜö¾òϱÑwM? úu8zÌøý©7”†l`™Ã
+Ùã+›¼få;8ñ")Øåõû댾ï h¾éóÄ°ølyÆ&Û¤üMlRÉÄåq67úCÎþ%ÿJ`òùòiŽeøˆ®°'l·[ÓpM›$UX°†HK ¯›é­¥ä4Žz6KXÏñÓ læÌ0dmzýmDoÑû9Ú)õqÄÚ×F­j‹4”Ñßfªô7,ÿåuÆb}ÛÄ Â5ÒJ‡–ôžÈ - [Ïå:Móm’=1ñ#^­SC>M°BηjÁ°beÎ+‘•,fÃ3°DßnÓø‡˜3Üæ³|µŽ-Q—ó¬!NQØË(NE6×Ù‡ÒW†»}mzŽ[=OÞùšvÈ Ôó¸$Åågè`•å%+“•Þ%ÐñKžÌY¹Œý–gê.ÔA~”,J¶Þäxš¾µMÒ”=‰LlÀŸÍŸI-._§Él'%™f’’d¼oËØ¥)”s¤s+ØÅ«Ñ ál¦~‹©ñÎè{œ<»¶ÿù/¶ŽWÓ—B:À/”¦E1Û$ëòÃÅO_GW—ÑåWºo ]D´ —AüxLz`ÃߤúÁŽê9rB“î[¡éxG¯¿AnȶMµiïðv!TG„C.…’ªøÊoî¢ñõø:¶,þ0žü±ŠQ-;ZÄOóvöw«‹ƒðÄÅ.â¸H5Üós…tž«ß¢Œ)6æêKÛY}Lù¯oFÕǧ/“H­îî£öƒàÒ.⤞×ôæntûåj¬>®¢Itù_E㻫Ÿ¦F‰ï@ÒE<‚ÄuH`U%ý½úÝú|ù0þåf|{5™ò©q`sÏ9£‹x†c2ÝZÌ’8M_ÕçFÈÌ¨Ï yh“.Åì;òW¢{Ê-»ˆG âJ 1YT®½oÁ@òûméö)ßî"îKG=tØåÛ”àWËE¾Ñj‹WZ5i>‹Ó¶¾¦i]-)…º˜uÊÙ»ˆG ‡]Ξ/öü|Êß_}·ðÝ_Ýàò‡'\¿“x×ÀéÀUjŸK²ÙFÄ…ÖU²ø1I“Rë›*_­;µ¢X—…¯…<<-Ä#ȃ®hyÑ*.¾Wá‘?—mƒñÐI< »3GKœ"
+]&Ôœ{R7•¿\‰Eüœ–J á[Q2ªÕ–¤1•»¤ÈÖû¬Ý˸$[û.«ÌÓoÚ'2|Xb —uxRȦ~`~?„‡Ç
+ ›¼`§zìYBø•9£§ðdµF&2ÑÄR†rR…äò„ƒÐ*´…óqú”ï1/—+uÏdŸä¾h"w±Ç*”5)~»Û4ï)z.Ÿ–¾`XQª¶ê"E8µN¥xÔ"Mfeqƨõ„D&¹—ÛœÒÚª`™P'E¬©ËŸãEÃ<Wij%ËË¥ØÈ3XUaÈ0X!1§2ÕåÌ/qV6‘Õ ¦Ff;U
+(ÅQÙÜÐG;/UçнÀ'*Ñù\mÂk‘å>œ©°­"­柖ì¡úÿCŠƒZÛz:‹³yUWìúÒ ¾d×Í.ÔùÝñf¹rkºã€ƒËü9ÕýÓÜ­2„øûsœ"Ó,u«Öwmè^L:ÍvŠRŠ¯•(¥ÚR©¾T*•ŽíâȆ·Öß®!²5¼r<tʘCvQú$ëNÆJ½ÅIÈUMÓú¹»«×#ºnóu —ª¶ßdíkÍájJJh™ÆÍ×PÜ°VsÃ&U­GÇ4B¡*ö“qtKb}~?¹Q!
+í~e­!W˜7ð«dƒ4ðøªìøšÍ>Ý2— dR@GOOeê{–oY²@&Ù`Z[çÙœ*¸ª¶ƒg”ä§õ8MÌ6b•—¢³”ª.j×P å+¥×x½NUΉKšSáCá‚kñ”QÅ}Œ ñ¿g©Ãê}X·s¹Na#zœ*‘kEŸ%qJiT•ej¥²Á¥kdóaÃæ(ºè
+c¡ID^KU[Ò¦Îój]jÅ©rŠî,—”Ì}ŒFU ,Û’û]­TG‘. ÷¿dWËnÂ0¼÷+|t æQÓ Š M¹—{AÅÐHDQúÿÙ]ƒn‘w“¬=;ãY1/ž•’øõ²K}°¸\W§3æ³:DF±£N_`ýhNÙ`&ÓÌ.<,ØSúõ#ö3Œî¬•¤*Ü[ŸUÒ46E•—sÔëx„/…áÎ}£²ò©|Þ1ÒâÊ)Eá¾ XAÞÕÂîIöõœíù2<h5žBmý
+-nM¦—wCq_ˆ”­«£txç<?8ºWžuÉ…DÖéšÓÚEyQx]͘lCHQÕ¦Ìøx*ˆñSo§Ö¯“NÂ0µqÉ'ˆPHPCßÙÒm¡âRƯå»ÓŸö2¡îÉñlc œ¢%Mé.j¾žþ
+endstream endobj 888 0 obj << /Type /Page /Parent 1653 0 R /Resources 890 0 R /Contents 891 0 R /Annots [ 889 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 889 0 obj << /Dest [ 779 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 458 358 487 372 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 890 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 1607 0 R /F4 1608 0 R /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R /TT12 1611 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 891 0 obj << /Length 3151 /Filter /FlateDecode >> stream
+H‰´WÛnÛÚE_õzÚ,†÷‹H''E’SÄ
+‚6êMnKl(RG¤¬ªÒè?ö¡kfoŠºØ NÛ ˆErßfÖÌZ3ûÕtôl:õ„+¦÷#×þá'HÛIœHÄihG‰ã‹érôìºMDÞòG´y=zööÖóväØŽCsòѤÜŽ¾Ê?eÖÄ·9WôëI‘†Âúëô£”wHEìØi„SüÄŽ}oàñ^ôFz«yø:ñkâúv$DÜíÄí®îøâIea'Ù–­xùöBüåßÖ$Äûc¡ÊÒrd¾¸·Û²û‡ZW¼‰/³º°&©ÿä¿l»Ï®yŽáeúßØ‚ívk[¾íÊ–NUt,¶Æ‘6x—›O Þéf:rE)F^Ù~ä
+?¶“DD®>‰X«ÑýèÕt†ïbŠsŒÆ
+¿³¹]£žx­&.ýí·ö)N´wkEH‰E³%v£W%²cglÖ˜oø"ÊN-[±5CJ,›Â,AzdUاpQ\Ø„80‹µ{nJOdBYwj®Öâ‹Ý;Áýƒpˆ
+C”DSçÈÊÃü…§L”šR#Fòº.ò‹èZ ›råóQ.=çt?®–@H“Ó
+Ê©Vuœ$ù‚ðŽ¤I’š¥z®Ä}eòcNABS
+õ‚Èì"RÞ}¸™¼~÷ ‚\ó
+w™Õ¹:Íð!Uö¦D©9uùx¾_7u—•5R¡¬/¹½‹¥8-‚7B­)wÏ‹¬ËºÝ
+ý%qB9DŽZÅRÒ¨•È«ñ²$·B©=嘦úòÅ[ùÔ½6j“=Ž¦á¹ÎjB…\ÐÖ‡?²>¬7]Ÿ©Š12 ‡2Éè@v5 䇬‹LaÖ“FúCFFiš®[—w›I…W„”kµÿS6UÖM­˜'ˆ´ÉÎI1|tŒÓ. ]6GÛ¿P÷¼èÐ,!d ²ƒ~q‘=”ÍfM$ÏÄóÂâŒàðr5[Ñ´U¢­õ²  5 Ã\Õj]æblòzÜß8Ž[Ì|®!%‰-¦\½Ü—+ê\`ÃošYjE¨²âu£Zž+r¤•I~[é•y©—Rωò[PfF`“ÆüM/”d”ïÕZõ7A/¦ ËþV²#Ljòø辂5î¡LEújb ãK\F™Uœä
+”5ß\Ž%‚ø³÷jˆn/¦œA4*C`«¿‚ß^UçMÿ]w%вS.¼}É_nª®œXTîi#“ËXûRU"!±7ýäÙŠ¶G"ÉFÏ¢âNwèDÞ)ýD3чFp½ö¢ŸÃ5@tºïDcÀ¡òñQ0DSBI1©¨'ôå7´š«µšì;0Vó®×È
+ÅÍËÃ5·Y¥(K<±û®ÑŽƒÍqüÓó¹å< Ú£Àwô²Ì¾Q’h[ë†Jh'ÏwA²E[¦//”;TE»"2•yÖ•M”iêeÛE³©
+¢Ý£ì×5‰»1·Òr·
+)Õm¤oäöBP¶CR4Ž åqïaϤiP2´î+¡J—¼Ó¯@~ÄVo¨¾ÙŽQ®ÌáÅÓwgPN†–aè$cO›`¬ý ×êˆà ÁJÝ¥ÿC÷zƼãvV¨J§æJ 9Â{ò”IåzÔ¦ð
+*…Yó
+¹2²H€®n³î絑œéPKÒÞx¸“ñÇ Ø+Qü(qWŠÕ+îíJR¸¤×—zi˃E%X°³Üš¯‰¯<FmÛÇC~x¢¿ÂØÏÓK-Õ{¥ñLÒ£—4ŽS쑖馨
+Iq"õï;û2!€z)'ÛëñîìŒe÷Qˆ˜ˆa•J ¼zðÌ"YÉSUÛ‘t.~3ùðÚýÚØú±0u“ç–—[%rˆÂ&KDIÑ£Þ‘¥ÔO„jNXˆ˜ï\’À,éJó›E„(…ÙŸ$hQªblºÃ‚nàè€Brð&fº¢nûlÓgrR³'¢uÉŸ@Î<BÛ[­+,XêE—G"[öQ÷Î8†ƒÐÓøª)«{9V3¦ álòŸ¤l¨(›ßùYgü¬‹ûh0í£0z<ìQõCpŠsqNR>ο²`Pxjäön¯ïvø,p|<K†Ø…3(Ñ XS;xxšiï'ê Í5S~‘¥W€ÛgI^ž†ãºÄGÁÇv/Û<ø·P”õS9.Qí:•=Yî\© z
+ZÙw쾸6É®X¡0E£­/O4—‹r?ʧ5Ó¥¡fªÕãì æ¤Ó¨ii_Ô3WjסÛ3†fIƒlpæC¤1#Š$s6f
+él¥“¶êè™
+endstream endobj 892 0 obj << /Type /Page /Parent 1653 0 R /Resources 893 0 R /Contents 894 0 R /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 893 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 2027 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 894 0 obj << /Length 911 /Filter /FlateDecode >> stream
+H‰tTÛŽÛ6}×WÌ# X\Qw=&Í"EQÁ®òÒ$²LÛl%Ò•´QÝÉ?ôûÐCÒ^ï¢- ‹·áÌ™3gø¶îÚ6%Ií>’)%øaÈëZ$uRRÕ¢¬“ŒÚ1ºûa®©Ÿ½MBso¢»÷’s”ˆ$q6}_§kô™}ìxœ‰œ”SFMIükûSÔx U‰hJDÉjQ&!ˆwz_nV”ÁÕ_)vã4á±ÌDÉ’š¶gz<›åˆ”)OlÖ3½y¿¡_þæqõ„³BLó„õÇ =®zùSMƒw’±ÎìxÜ0úOߧ–&¢Ä¢}‡=`]WÁ3!Ùì¢*®R`š »þ²uôžîÛH’¦(-J‘•’²JÔ5•Rä9!Ǽ¦IEûèmûLF&a’¼fãFuâÊö×eºòW•‰À?\qhnµhüRH)ÖrT”àT²QÍ~¹ðuêÆí‘cu=VÃnC½5K§æ (5BòôêÖÄs Ùp×(³è°¦RØ’ú†=ú–£·é:,ÇnÎþö^#2=…sµ»€²\‚æ`Ê%EA±`ÂÕÉe[ݯBâï\Â9{ Û‡0Òi²'5-ç/\Ðë°(Äîº
+OJáH!=ž5r R$Ú˜½Çõ@9©nˆW;AÝé4èžW,Ø
+ú8¨nVHs¶ÈäüW‰¥|†%/DzqÁ„ˆ[m‚K€«Yh(<MapÂG7üþ„úŸa½C@³{}È+|/£Ýépˆ÷Ï5s°¡îbKß|Gw¡?½~ýoJÿ_žâüÆvÒzTx!:G«“é^ÐIœç¡pxBi±4ÃêhWêèÁפôÕض‡ŠöiÅq!íÔ^×fçKr‘2žÙ
+endstream endobj 895 0 obj << /Type /Page /Parent 1653 0 R /Resources 905 0 R /Contents 906 0 R /Annots [ 896 0 R 897 0 R 898 0 R 899 0 R 900 0 R 901 0 R 902 0 R 903 0 R 904 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 896 0 obj << /Dest [ 1241 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 332 709 346 723 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 897 0 obj << /Dest [ 1538 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 506 709 519 723 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 898 0 obj << /Dest [ 1241 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 420 599 433 613 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 899 0 obj << /Dest [ 1502 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 326 584 340 598 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 900 0 obj << /Dest [ 1502 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 470 571 483 585 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 901 0 obj << /Dest [ 1520 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 250 557 271 571 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 902 0 obj << /Dest [ 1529 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 327 528 340 542 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 903 0 obj << /Dest [ 1538 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 138 486 151 500 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 904 0 obj << /Dest [ 1538 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 134 178 156 192 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 905 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 1607 0 R /F4 1608 0 R /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 906 0 obj << /Length 2701 /Filter /FlateDecode >> stream
+H‰´W]oÛÊE_õ+~Z&Ã¥(Š2 ‰\´pnzk=5.
+š¢$öR¤"Rv|HÿCÿczff—ú°E/‚Xâj9;sfæÌÙwÓÁ›é4RFMç©ÿð§i¦a¢Æ“Q¤áPMWƒ7×mªò–÷„ªÍëÁ›ŸîŒZ´ƒ0CÚ“|÷õiðYÿ%óüaëEAŸ‘V“±òþ>ýó`Â&j“§ Ó å6±-ú6JÄÔ¿#¬úQèùf$:LÕóº{®»%V"]x°¤Û²Uo:Wûçð¼Áo£`¤K/Ôùò\Ý=•ÝoŦb#CÕ3ÏŸhõ/þËŽqøZ ¦7XcžžžoÝÒ© Ó82À×ÑåviÉ–ÞOF•j’`˜5iªıBŒqª6Å`>x7íÁl ÑØA"ðrúOJXla,9ãoÏq< ÆÑ áØŽ$co oÕe[
+%É¢rFźÀŸ$
+E QûÍKÐ;
+'LæxF]J]'Å\P—òR.Å|аüƒ ëËI5dâÝi±œ†_£µ™…˜Gs°èDg”ì¡®ÕC¡¶džàj£=Kc÷³@ÓPÏEDó²Q¬=ÚÏ+2eögø”K©¸+Å…ÕŸµlç¸LÃõ';é“-Åñ?f<œôÌ7‘àg»<ÇR߈‚C²kk:ÚR'Ú¦ØÀoJñQv! +Âïà-Ù5È.aXØj±ÀQ’ "—dCI"ã)áú’s
+¶jWï~¹åÅ7XŒõ§›wlñšt’c)_>äº(r8Ñ7r,q'òê*ºHß–òØÄ}¨…‡LäPkfÊ}Û<Š@sŒ(Ó¿IPI¾aNÉå×JÌZëöе²æûýv‡}v¯/囤Ž¿;7/âO+VWìùÚž¹]HÊ,¿ØèD`ž¾Y F$Ó?ž–¹úç¦ -³ŽW謖èe„êæýÇOp‰é»nžT†˜¶‹²öQÚ¨•¾l™’d„›Ä¥ºÄæݬ‰¾3k„»KQTõ¢*ÎÕö¬:Ÿ˜§øÚùó²*è`¢43…ÁÌ
+qÑ©ÁðÞûf›¯6ÿÐO9ÛÖpè{¬€"ž¦§˜{’–©ž…Ѻ¢  ÜÛø)ÿ(ßo)‘‰óÏ°­5RP¨ª|£6 óTg›çã
+‹™Ý&% iYœì@bŠ"ŠO·Ê&Òù½kà…PæhVž?Ö+§f]E’Ôñ4ï˜>9ͤSêêùܵl°M„ÇÜT ê¯Å—ma_ìŽ3<Æû¾õÂÃÝ+Ð#¬w@[0*ªGeͶs£œ,¼6É/¹„ú•,ûSä9ꇴêñ|…È;ä`‘—žå%LÙÚÔåo‹ƒSuòÁ,sCLZ͇¶žØÁFâ_¤ƒŒ)d¹è#Ú°?øüS7poÈñ꽫ŠIÑÒ¶Š,L»*:Åq'<ˆF‘8Sd¾ôi}UZ‡­¿‚Ñj £Á}ø[ÚŸfú“Ç÷®7‹êZ¬ËÍÅ!a¡ãr:PT´ðF}
+ŠÙÛvÜ_Ç¢ù›+:n®]ýúûüA&*)Ž7ø_vÅ›OÂz7ï®%¢Õ¶í¨Òpÿ£š;kfù4¡:k¿Tgÿ?>8ír´s9:tYFe?Rï5S]…á=<c‰geÎÞ{ç'b¢x‡`!Mó31ëâê 3úvaîÝ5ãþfb óËJöF=frݬ¶v©•K¬]Þxö®ËõÍîq¯?Ú/3šÏV Í·ÝÖmTnÃÁB׸poëýù¬œ®žä©¤bDý/ÕÍ; Ú¹Msëk.χq4{±BñÈNVSF¿¸7î ü¬÷‹Z7Fõ]S±§’ˆÇͶ‚ I¸PÀÛÃt¬äqŒËlá3Œ'IH‚­lfzel Ÿà%BF^®²¯-ØwUѽ'¡1
+ÆÑ~`Gq]Ó<Àp$z½
+;Ùø¬oŠy¶­:A+}툗 ™U•ƒªUåj]+ „’ t²MájŽV˜ä!øË}cŠL!· Öª%Å0†îÄ•çȳ¡ÛÛ~…™”$ýº‡@–õz_µËf[ͬ«¤¯‚@ýÜlVêŽ œÔ£{£Ý ˆonCÍ·-hžd±¿]ðÆ*µ†Ž¶W,
+endstream endobj 907 0 obj << /Type /Page /Parent 1653 0 R /Resources 910 0 R /Contents 911 0 R /Annots [ 908 0 R 909 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 908 0 obj << /Dest [ 1235 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 317 305 358 319 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 909 0 obj << /Dest [ 596 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 365 224 396 238 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 910 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 911 0 obj << /Length 2774 /Filter /FlateDecode >> stream
+H‰ÜWÙŽÛÊ}Ÿ¯è·4‰&µ”1w
+ØÌlF3˜…Ói–*YšÎça:9ój”½ÍŦ8'AF‘¹tÖ¹¢©»¢²MÏﺫ×jÉËNáÅ\¯›6Há7Yõõº¨ ·å³v¥¼
+ë7·Í^• "s£¡HQ™²<@ã¿è*}Óvv5R·}À9Û©ºé—ÉÙuT+¼<cú¨ÎýÚÊÚÉ
+µŒ3ºz_”¥ºµê³Ý¡ãH›²¸³GÀzòh2xqrL;¸‚'[T¯œªlÕ´‡P½YwHÆn ̤¢R1˜¹ÙvŠ!|¡o›¶ó'¸ÌU×
+Ñ•4åK«ÏÖî°K^OôIGñE …âjU±~â6NŠ“j±W wµÁŒ`D!z‰F(Um»®QklÄÚoÃxy èM4mçD÷ò>Á«› òÿ[À{žfèçà}*x
+[w–€«§4* BnÛôåŠ$úXA,c Hæ tBÅw? O¬¿Ö^Å¢Ú: îöœØÊtæÖ8BåJèÝ´¼]÷Õ-¶‘Gà™9ô ƒŠØ@Œ&C¸b+G•¦ÝØPýT«gÁvqRh1 uÈÜø'¬DËbäËŠ`kvÜ¢ æ]jpØÜÉ.9‰_ËßB\ ÈÈÙ‘ùl•l‚_V}¾å>#t°÷ü#Kû x5p!/ âE3 gíE}Àüó¹^7zÝÄçšµ [ o™Ž­‹V¸Åù¤L% ÅIÞ­.'LÇÓO¿kïè³¹>.¹§»DVε8*á—¨Úßîåð¡ÌJÏF3>•AìMýsÑmüðäø(-8J)yîŠÉBðt";ªsj×8yÂl„Ý[Àê¨@;ÎMÇÏôíy˜R„i&\u!Ù1Õrý/$Y)[o€øÜNN¬i'›tC2~U"+ kºÇ¨sýOÅX¹ú‘Éhrœ:Î=”ö&š%Õ|ÊöP{ˆÀ¼#î1½ÊŠÎ9bçZø¼´÷9Û牱+Ét®qþˆXäœY$Ô „+ÏUëð4UÓ£
+;q¢ñ9)b«³s-çR@vßQ×¼ô _ws®»é jÖÀðDË¡¦‚k)7*Û}w¨ŸÁº·\N)64})IÈ ÿ8ÿŽ‡ªý“¶=Põ%NÜè#$œÕ³"J…Ö³€¨aæHðÀùLÁÕ]ñŠþ?
+Ü÷õÇôòúðiùñÍ_–?}¤5ºÑ4M·]Þ»N­{R@»r?•9k‚< “›€r…Ý“Õ4‚öÒ ª¡]r¯|lÆø”‰½)]C®¦I²mÊ>^·P‰* Ç+ÑÌ…ÃØ4“± *¡…E
+@9ÍR%ËŸ$jœÆaÆ^©NÝ.F!ºa›!€ê¡ï5¿  fRTW¯w4ÄMy ZöSò¢áÓû€g£U³¯ýPˆPEø‡ŸA¯Ùjžé5xÅ;åa@û¨çËÀ˜¾00Nã³áöÒ±f
+Í¿w‚½Ö<ºÂq«^
+y…òwrW)f~S;ðôßH´5RÑ5ДDõÕ—m9¥\œœyæ Íhí][Üö ËX¿æ9eú’E) Ò5:Õá×]³ý×ú½]ÐqZö½NÓœtf0*®~dçKtïi(–Û‚Ç^%äXgk"Jkbb„+{Ûo6„»¾ýÏ€u*0㧖@[¨™àf20t«R‹òådi1$$aíuhË:_¡<1GŒh–Àr"ÈòÔD`ûØ ¶­…#´ðV69À:
+endstream endobj 912 0 obj << /Type /Page /Parent 1653 0 R /Resources 915 0 R /Contents 916 0 R /Annots [ 913 0 R 914 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 913 0 obj << /Dest [ 596 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 281 730 311 744 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 914 0 obj << /Dest [ 1325 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 374 298 401 312 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 915 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 1607 0 R /F4 1608 0 R /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 916 0 obj << /Length 2923 /Filter /FlateDecode >> stream
+H‰¼WËnãØE¶þŠ‚6¹ ,6_¢(Ãh`fº3èÁôLk6igASWÓ©!)+·äòYäTÕ¥^v·g†Lò>ëqêTÕ·ó«7óyD!Í—WaDþðH²Ì² ¥élâ§YÓ|sõæ».£¢“5uE}õæû»VÝUà¯)®ÆÃëþê“ùSîc?1+ËÏÈÐlFÞßæ?\Íä„M–â–8óÓ@/‘"9‹ß&©õï£ã(ðÆaì§&Èèá‰îžê~‘ÈX'™®ìè›ï¯é¯ÿñÆ|·˜›øSz)Ö×t·/ûÚ¶’Cb“× o<3ô/ù/‚‰ú¢Zø)>æï0&ì÷{ß‹ýÐt|«åkq4®ôñ@»Â ­å¤÷ó«JºŠ&©§!ÅS?Ë( ý$!è˜dÔÚ«åÕ·óƒ1âK‚skM°E åüï'nì7M¿£ÃÉ`@~cñÝAdÈÛSÎƉLÛ–Þ˜å}äGfdzáëÍ×n¶óÄsϺ5­eq¥+¶Ð›—ô un¨Ùó<-ôY»Ý“|ÉQÔ©8]é¬ÖÔu;™[®Ý…=üG½“ßÉå]1²¦¼öÙxÈN|'—[óùDcÚÈ%±©wî6·Ö=Dæ8VgL0Ê£#à#5rþд}Y¯ 4c•Êšˆ¡M¹XTöšeW4um YZ{@ÏÄHˆÄ¦ß{)ÄnÚÏdû§•í;Ú68í¡²>ýÒñ>ìD¢Ø¡´ ú–ù½ZæÏ¿¼¿›üðÓüÃÇ÷÷æÞSq:Õ¹à5Ó–Ûž–»55Ý›ÎZJý0ñÓ{ïÚËؾpÍîmöc8µ¦»ÎˆEÍÎó£¥"¯)¯ºÓ”oÇ/-<-Ti·UiŒžÂ¶}ƒŒ× 0ì5‚;1Õ$«ÊÏ–vË'j³ÄÔ 6
+$ßT)6´s5[Ö§»÷|RséÇt‰$ˆ¡ÄãlJú9DS:úÓè$š‚C4 ̆þ†I ݶ¡Õõv‘ËÀŒÄÏ·–˜k™.[+c‹ü|ö†¬øø£s¦ŸÄqÄæJ‚pzéÓo°ÕÖ½—™²È¡Ž™Á2üÿ \vIãbÄDvÊzÐÓ¿kj6=üPÖ7ÂgÓã±.;
+4yE uÒÁ_|†³n®¦S»Õ´ñØ{„e³ :ßØ/ „ ÓSp%¦ïÛòa×[ ¨Ð°¨!báQ§BæuS[!ô¯Û&K2ïì2ßU½Z#{íŠD®Ø,&rÃ…:'ÉäxwðªO~úyþþ†)íζp2á½C¦”PÍLí¾Á
+G_/ð2Ê[‰×Š8çê2»¸yU”7<…u$9ÖüNg\a€„åXô@Cs¤ÏŒ.ˆÓOB5v??!òƒÙ‘¡Ãc%4ÐψnÔÈÐ0S4%­ô ¡)WÖVQÝô¤ƒn=£êô[NÀÓpÄxaäˆfÔ¼h©ìÏn­–µHp.£º¸tw–'wç
+ ¹a…pÅ#"NàáŸÃ‡Ã9¼º,W;rìqúrY0)fLŠ¸’F|{ŠêR¶Œ ¨ïSsZ@Y)›Âz¨2®IŽ¶HJF.¹<¤áËM
+:L´G€H9Ô2’l½€×V¶¶-s¢F¹ôQœ1‡»YƒmÞuû¦]ð!+»CÀq×7‘ŽÝMû´å<Øp­"âÌÿ0½Å¡†Cù´Ú‚BÏHâÿçÿx¨Õ.÷g'Ý…Ðp÷ÍihoØSÉR(•Ý×¹¯'ðuÌᮑÆØD
+aŸtÑ;/2(ä7ééVâê K7š…l]è(ÌìN·žgšçÅù¹ ™öù šÏX æn) (up$ü¹l›èÅQ«¢ô-TÅ£ÔÆÂättí 9ù
+þÝñYŽZH=cª¾G¶Ò‘k¥+H^äçÀÓå¶ÒìÉaÎデRñê õª»8w·ˆžî’ç…êtÃç¢èzVõŠùù7pùw+Î…ôZ”N„^ë'¸cˆb ¿)Á¿àê‚Î((û¤$eÏ=H.ˆ´2Z¤Ü
+&é2znt”y1)‘^Æ,oüàZǧ΋ðB^èS)cѯ ºƒEe©?€d_¥«‡{cÎ5Ö¡¶–x#®z0žqjïhËi¨8q\›ýãNTx€?†ä¤8ë:TñL¿q`SÒ.Xק
+ [á´ú{ O^5†ØapÖ°•ïf;yÏ e
+endstream endobj 917 0 obj << /Type /Page /Parent 1653 0 R /Resources 919 0 R /Contents 920 0 R /Annots [ 918 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 918 0 obj << /Dest [ 1325 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 367 335 394 349 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 919 0 obj << /ProcSet [ /PDF /Text ] /Font << /F1 1609 0 R /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 920 0 obj << /Length 2634 /Filter /FlateDecode >> stream
+H‰ÌWÛŽÇ}çW4 è–£¹p.4 È–dØ
+ð~yä/Aª²eâ§y«Õvöêû.WEÇ4êŠzöê‡_BußÍ?Bгù¸ÜÏôŸÍ½Ua(oõ÷Ù’™–* üe
+Áqî§ÈežˆÙi•¤ÄýAÿ+Âî<
+¼yû©ru{P¿ê~ƒH[’tWvêõWê¯ÿöæ ¾[œ%~¢K/ÐÅæJý²/ûضb!±6õÚ›/µú'ÿõþ¶úiÆ/æ×DŸâcõ{¬Á~¿÷½ØuG·Zº¢q¥eàGºp[–ôv5 U©fQ’úqª8óó\¥!,©ðÆE®Z;»›}·:#A<¶ÆdÝ€,¢Ùz“gFûeiàãfù WØÿŒêv¶(M¥š]_6µ"c-ýL›^MÝ·MÕaϪ[»1e3´ª¹S?¿I”°]÷eaˆÏW?öªÛ4Cµ©êl¯úFAž3Ù<ô¡ YŠÜNžÅrC½¶­·€ÛTÝÈb ¥ŠRÖ°âKÛÎKü\÷^„MSV¾}µâßÛi«z[ljhV©[/¤-Cü™.>Ý‹¼F¶„D®ë5,Áüx)ÞMÏý™ÉÞàžPŸ¿U ±Óic·öô•ññ•ÉôÊĽ²óRD•2>/  äͽËÔ¦ªìfÀóAïÅTRO}O¶-낶¬q2‰Ó‘Ûb˜¨ûY‡¶7|ý²äÒévokKl9ÐY/×kJ(²ÎÉ•DóàQl3¹ÑáÁ”êA,¦ø%»6¤ Ä‘bˆÇ¦¾Â XlÌngëŽI¶†L_U¦Ãað,ÿÝÝ•¨hÛÛªuc;<¸¿ì¡§q(®µâZ†9ZkŠ; !²XHsáªÇëBÉPþ86S÷_’ÀQW˜š3‡ÉvÈEy‡r²t”
+§ ?ïÚË8-èïé5¬íêÍýG|Ðùy™¶”ÎU³ÿèIâd±.åŠow¤b‘ë­;`³$Nˆr{k W@ûÕºzµ-ï7=½fpDôŒ\•’…ˆ>$«‘i f´(N¼Ô-…dÇ|R¯¤4$’† JÃü’‹£ÅÑÅ´g0fφ•Bß¾ƒ¡¹-\s[øÁ5táÇqž)ù«iŠZå'í(HÆv4F8*Læåp£º¦Hïð–~ìÍ·j< •>wŽ8è[2ŨS~ÞpŠ ¯$щ"ᨇ‹èïQÅMY#xÊú[nC™>Šu}<Egp Œ±ˆök2ONÉ"ÔÜïGýIŸ«7gALjÃäGÃäN#Dz!Z‰BÉ
+ÑT>"F
+.)Ðï.éuÓ“½¦Æ¿Ð=˜«p?>CMšPp½ IÆ-×Ûg¯ž.õåƒ~cïÌPõòØü…+i»Ýõ¾ãìA'Mþ¼ÊNålt>·y*Sk:ª#T=¯ÇòiGŸÆäÓR
+¦ká q%C”†Áã0áíå.(›K×É‘Üh#4Z$ËY£õ†²xÒQŽÎÔ£¾&ež÷(e³cd&ǔ͑h×]¹ÝU–ôáA¾³üÕÞ\M¼ãÈv{ät‹Tlh!†Û)°¶ÍÚ¾œÏ‹zÙâ—Ïñï•Ïó)R§@EHض6[{¥v¦ëöM»þšôúfuü?fõ„4D_N±ÇMMz#õQ8—¥½ú<ðÏSíiþ¿kZŽs2±"]1ôÙîJ6Ê“Ž}DÀ bc#‰n¥2˜5a¨è‚øÕîLkz°ÄÑBß"îïpçIÏ/N
+µ`E£
+šû}‚–‚¢Tê0´@S7¦Ñý(®?Ö£[6â„#æô~=<Úl?ÙƒlüVݨ#J·ê›ZìQ,|óÑ}j™í,kA3
+á<F÷Ì'O^„G¡Tù·Ÿ õíoÅBïB׈ý„¨£d‚X'€ÒåM¥¿~u¶IBÞ®fyìy¨¢<ôQô3Ôœÿ¶vv÷âa’Ž‡‹sá£ã$
+endstream endobj 921 0 obj << /Type /Page /Parent 1655 0 R /Resources 925 0 R /Contents 926 0 R /Annots [ 922 0 R 923 0 R 924 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 922 0 obj << /Dest [ 596 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 365 604 396 618 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 923 0 obj << /Dest [ 1084 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 465 229 506 243 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 924 0 obj << /Dest [ 1094 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 71 216 113 230 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 925 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 926 0 obj << /Length 1394 /Filter /FlateDecode >> stream
+H‰ìWÛn7}߯˜G°h^ö&Ãà$F>4A$ô¡vV+*Úb½
+Ä•çCúýÇ>t†Ü‹¬øÒE›aírIÎ gÏ!_ΣÓù\ƒ‚ù*R$þá#εˆc™B6MDšKó›èô•Ë¡t~ŒW6Ñé›™‚.’BJ…cÊhÒ¿î#ö¾øhAI|þk4õ“¦I1MÑ°ÉE*ƒ]?Gûéô–¤4ûŠý®ñëDK>QF¤Læ°¸ƒÙ]Ó®ñ‹f–£%æ*oNàç?ø$Áöû‘°ŠKV®O`¶¯Ú/v[{#†Í’O¦ ~ó¿ü—ù‘_±_–"ÅÆü5~óì÷{ÁPÌ‘WKnÑ4ºø*…fe÷ií-]Î#D:I…I˜Lä9¤
+3 ¸Æ8‡­VÑËù £pˆ¼Ÿ1»’2Â|ö°FqW£XÈĘ<ƒÐì“š%‰ÈôAVå´ÏªÌš”JòœÁ¹³ÎU›¦jªÖ•ÛŠg˜ŸO§¥¬}q³Ð Øï»Z\+.hì§6äÃÊ¡3Ä¢§B汨>"yµiÚ¢jì]œù„fl0Û!2Y_’#\Èȹã³[žˆœÝvÏ©f'p^ÖÇfç¦}qøÄ»À^mp£B¤ÁÁÅmQqÜ ¬.œ Ts,(³g‡2?àYv¡V ¼ÿ@(5ì`¬”]›¦¾{<&=ÚË{y—¿¢2ä0¤/y"}Þ†9X—Cì¦8 ?‰Ù×=ÊË1š¦ÁÊ,€Â°Ÿ!9¦&“> ¶7{ºèðØëqÿDNTzX(JmÌÚv[-v­uÔT>ÀÓÀ¢Í[­Ù4öQ·c>®Øk»*vuÖ›?»ÞìþVDoÃ^B—Gë“ÃÊÈ#ˆ“í›óyHaâ+‡¯@?Xc°ßòãCJ¤ÞV|Š{ͲŸm‰ÙZÂ~M;%e¶±´hÐXÜû9Û{k6X£Wúöˆ!-++h\À[žx
+¡n(¸ ;os í–¶ô~jo²- [ý´UAû˜º¾Øž´ÈÔ»-¶U±¨­Ãi€Œ =¦0Ñî¸vÅFÄÂ5Û¯«r %§^ŒÜ`f|xèe‰0ÆÕvs´¸íjFPÇýÖ™q¸˜ªùè]ÄgîãkrÈóËÙìí»ºøà—9ãT’Kô…$ψ‰ G-vUÝNp]«]ƒ¥
+µÅô‹ø'žÖB*PÒk.`
+ðÌaÕ®»;b܃+„ÛÕõÝ5ÇÊÞt}¤xjí|7(0uíû¶8ŒÓkAµYñ˜=HÐ!½½ôüÒã‰ÊÃÜ= z#o^ÅHßfHºJˆftÓ ÃfàǨþ˜ì«Í%w^íÄzö·Žþ&‹…~ž»õÀÝM[µöƵE‹œ¬ÙÎw¸.D$¿­-m ë[»ì‡îÜχèElŒ¦%ÄReqNw¥ëU¡FpÔ bR—Öè^Ñõ¬F—1#1}V"Œö·§ø»D|—ˆïñOKD2JD2Ü0á$x8w¾a!<I#P!Ï»tÀ Ÿû£~ÕtÃý°ÖoÈp>?‡×ð¬ DQtÖö¶…%ò—2tr×Ãô–㽄¢±Á@àg®X+ܨI8ßãÉûÁóz:ž×Ó<i^zHÞþRÓ1øå<ús
+endstream endobj 927 0 obj << /Type /Page /Parent 1655 0 R /Resources 928 0 R /Contents 929 0 R /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 928 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R /TT12 1611 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 929 0 obj << /Length 3596 /Filter /FlateDecode >> stream
+H‰¬WËnÛJÝë+zÙ,†ï‡aðM|\#’w’YÐTËæ\‰4D*ŽçCææg1§ªšK¶“Å ˆE6»«ëqêTÕoËÙ»å2P¾Z®g~ <üÃO”ny‰JóØM2/TËíìÝû6SeË{<Õ–õìÝÇ…¯îÚ™çzž=ålÞ?>Îôuqg”ïÊYþs–ó¡\¥ž›'fnâ‰\>ðqzŠ:ýUÿ'Àê<𜹺‰ö2uû¤Ouw•@’t[µêâã‰úûyŒ÷¾Ån¬+ÇÓåý‰Z<VÝ¿ÌnÃBB]Ô+gžkõoþëücù·[ÌÖž›àeùk¬Áãã£ë„®¯[ºÕе+]<zn K»tÏ’.—3_Ujĉ&¾
+S7ËTâÓ
+6F™Ú™ÙzöÛrpFèc‹÷Ü£w=òˆfï!F‘Qäz6Dnf©’×Þ©i»i0ñ*Ö¬W±‹mò}× ¾Pgå¾íšíéœÔôÃ^~ÛrW=tç'ö;ïÝšz…ïmÉoüÝñ=}êÌ)Ö‘n†šÃ^ÏOÉ_õû)ºJdÔªY+<Årè‘kÜN-?R oêB¤®÷’«º÷o¡ZNákå28$;íà/wƒlâ¿w‚/>xßÔ]QÕf¥ªú”C™êA¬Í…ÄM{0 è€JBÎZ'ÕfçÄn¦¿Ûßs,Èu›ŠL µ‘¥º;?T|ÎWà+T·×ø¢©\pñ½¨¤¡Þ·wãÀµÚœ¾®ªÄ|È$ϪZÕêú åG¨ÿPе­šºUM½yz]§p”— ò2ë¿¢V¥øPÜ¿á>–NìˆpM 9s ®û‰_D‡|Ð!) F)«a~tÎ<ƒkD™ägÊP*L“B·¦%—ôÂúŒ{Ã'~2 ¹6Ò]·«n÷iéÕge
+!¶S !S{ùhÅ}sT!OF=4ÓÓ¨bðÐíÆ :<šÍÆU‚×c1‹Æ˜ECÌ îžõ6Ìô¼@ŽÊ5×¢{^+É_‹†¢è_YÓ•ùaJ ~¥¨ ©nÍ]Uÿ¥ºæ+(öÍà]Ú`íÉÇ#ïM=Å
+)ŠÀþÿùC2»êÈBY­K‘%Œë
+48èm6¤úKK ê ò‘O§BÖ?³e¬¨¼)J÷FRL—ËÅòby³ø¦qK®êÌS³o¥s‹ HzY@}¦Ÿ…n ÑŒB´T¶Ý»Dt8WB~öÌ
+§V{[ÔÑØø“¾¡n–ÛqI- ’‚áJè[ß«Œ§§m&2Ÿ|Ìù¦÷íA+.=˜¶}+÷i„\YdISŠã­±À§º@*­{¢aÝè%æÈòÝδÝ¡4Ó †Qé9
+ á¡é™_£WEáBAÃÙIzÄ$‰L¨$˜rɦÒ&UŸCšNÙ+fç9é|ÒCJÞ #"’~̦0õ €^¦W”üZô<Ž<‡Ü›lÝ?qòÑäFÓ–‰¼¢Q5Dµc0]Áeú³lû€¹/é?ñó7vùsÃs×OÞ6ü ŒÊ{£$/#ÎË@‹½ì’³EWœ?óñdÛ¡¹|é¨~~ Ô˜6ÐË •ThQºh'Я5‡C|ê'ÐI—À‘®X âÅc¿VîŽ3ü ÞÁ?˜ÛÆTÛ–ñœ~ˆ¦Ö#®lËk‡dœ ÛñÆò ÆvbjÙ¨hè¹¼Ÿ«g Ç<DÊÇ/Qþ¼üÎÿ|‰öäÏkPa.Þ¦ÿÜ  ˜÷Ïè_¦3™-r ³Ï¼n€µ³%/?=Ðx’’+i’åù)â´”á&¥éÉíç'ò¦uà™’†ÙóÞrD&Ë÷!±ˆGa7QÊ%c4²¥9‘HõUŒfÒ[iþ¼·ê;´­íUºÂæÃÖƒ6-Ê·G¢~ ·>®dÙ¶s õKn?Ë×Ô4úÈã=ºm+åò†VjJ{=ôv•¤H}GŽˆõQB ˜v€Â÷ð 3O¥çó؉˧y0˜³dlIŽó…Æ“CšöbÞ{µXÜ\^ß,¿a l6¦¨Õ@0“À#HIFÑŠ9Ù”œXHAÃÉSÇ~gø)ÌÜÄ{é¤g{4ç«>Qohä¾ÅË¡Ûë‰ßëfG¥zK%&ÐÀ ƒÍýK‚~ xž§=ðÞSæ
+ä`äÒEšKn¬¤BS+°eý÷áîÒ´¬ z±õ )’Ëývi+B¼ðÌi“0
+ñ÷ÑGLB½ Ý͵ÁtÕYnµ4úg;N˜_ôÌ‚ÆsÿøHͽ¸[äµ(r½&¢\"7{å¿çøà­}*• Ãn¤Èïñ»÷à ¿áï%xçdkƒ(õ°e¨ZLSªÌýÓ™ Í"œ…¸,£¸T—JâR0.ja²OÕ”ËYÔ¸Öi¿ÍîÖ ³Â5aÁ;®¨‘}·}°…üh4ð+þS>zTïBØëÕë„õXr5¶%{'a³l®çÛ€f©Y4Ëhu
+endstream endobj 930 0 obj << /Type /Page /Parent 1655 0 R /Resources 933 0 R /Contents 934 0 R /Annots [ 931 0 R 932 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 931 0 obj << /Dest [ 927 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 320 461 347 475 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 932 0 obj << /Dest [ 927 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 387 252 414 266 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 933 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 934 0 obj << /Length 1918 /Filter /FlateDecode >> stream
+H‰ÔWÛnÛF}×Wìã.`Ѽ_ C@’ºAŠ5lå¥Ihje±•Hƒ¤ìºÒè?ö¡gfy³,ÙP¤. Kär9;—3gŽÞÎ'Çó¹+1_NWØø×»–ïÛ¡ˆ’À
+cÛóÍäø]‹¬æ=¶¨³brüþÒ×õĶlÛÁžl2í.ï&ò<½ÖÂÁ»jþë$á—ÙV°[¡mìò;.¿NWAHo–¹Xº¶š:žJ;W÷âò¾hVXq¥V°$ë¼oÞ‰ŸÿVÓ
+q3ÿkìÁÝÝ¥<Ë‘5ªéX˜Æ‘.mË•Y»´bKgó‰#r1qƒÐòBGx‘Ç"tIýXTz²œ¼÷Éðl±fcÈ®M‘œ=ÔÈokä[¶ ¾åyq$Ìm—Ô(¬ÈeÏÚ¬Ú®‰Éq,ÇW1œ§Ù¶nÊ͵nTDA¤¯/Öºª³*¿if'xKñn[+ÄéÈñÊr#†­yq-J¾^*;ÚœZ¾ç¹bŠÐm'¢Œ\8½7‰ñåÌ~ü(ÞëFd%¶Ù Fµ1ƒ¨ã]dö¡Ú‰å¶ÀdÛÆ໲hҼР‘'\¥HöÆZ˜‡VÔÕ¹^A2rZ«HêJðç¶ýžÁ*~„¬­s…,zR›¥¢™íº;å#ð·Ç´±›Þܦ¹B‡Éuz¥—k…TI}rØUSξIìÖÕ¼ç}Oþ$àk—E-Êb}Ø'o°÷öâ6iJpMú‚'ÒÇ6¼Q\5"Ä+U®¦¾¼ižÉË´ƒÂ(|ì†þ‹‘ãLøœ3#¼ûÌAµ®)%±®™žÈ‰Ž E©õeÓTùÕ¶Ñ5Ý:ì ài`Ù÷oQúà±îNo|–ßéeº]7&ÞøÙx£>Þˆã-Jaz—܉Ïî#3¬bïƒhï Å?gì¯yÍ7"]¯Ë»öÚ|Ýôfù`N佚¶‰ÒvGf–*pDHÜŒ$ÂŦ$&߈¼Ñ›ÖdiŒ¦ÞÿÑ,œ‚%f(¤aÞl©„žìó2¯´Øš[mŒn×ç9¡Ü ‘˲ùæf­7ºh(C•b¹-²xJ×yso=JâÞüò1HåJw¥ÙÀ˜ ËJ7Ûªó [òәȗÈE›•Z`èPÀ ‰š^‚þoªr±Íð<-îÅBÁNª¦˜®I)§ÛØ ¦õ8b÷`Ä =øà#Gºüfd‡N¯u±Ð
+É8 ø‡Özâ«ç«—È?bù3’+Áî|#OE “+B3PM¡#‹æµé.Ö2³-Ô±+~™€Jß½^ñ¡W
+“%êFŸ3ÆÙÃNÚS£ã:Å„
+tBp£;V¸ƒ
+ ¡¶0¿OïÒ¼A/ÖMÊ ©„“’oXÓÑ/ÏFó}UmoÚË…áQÀâ’~ʵ{Ä~GÏ»ŠñÞAz.cÇv¢=yv‰þ%+P ð~V:dü?R€ÿšÚ⃆­¼îZð6]oÉ+—~›</»¾©ÌqwdΠr…gùî#µë-º¹ƒñÕ7¨ïž¯góÉ?
+endstream endobj 935 0 obj << /Type /Page /Parent 1655 0 R /Resources 936 0 R /Contents 937 0 R /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 936 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 937 0 obj << /Length 1997 /Filter /FlateDecode >> stream
+H‰ìW[ÛÖ~ׯ8‡€Dó&Š2 8vš´°‘«¾ÔÛŠ<’NÌ‹JRÚ•HÿCÿcúÍÌ¡$¯×qyH¯Eò\æ>ßÌ|³š¼X­"ªÕfF*À?<’,ò“$HÕb9÷Ó,ˆÕªž¼xÝgªèùL ú¢™¼øî.TÛ~øAâL1™¯ý×|kT$Ê[ý8Yò¥¥Zþ2á8óÓ@èòˆ¯ÓÛ<¥Ûïõ¿#¬Î¢À›…±Ÿê S듺;5Ã+‘6(éÞöêÕwSõ÷ÿx³9¾;ìÍý¹¶^ ‹ÝTÝ=Øá£é*&ë¼)½ÙR«ñ¯÷Õ_&¬1k~ŠÕ¬±¾û¡î‰«!¶ –>^?Ò…[Ú1¥oW“PY5‰æ©§¡Š~–©4„%tL2Õ™ÉfòÍêlŒ8Ä‘àSk\¬E4[ïâ™Ñ~‹4ðñ_®ÍËùCåÍý ²A
+ f¢1Cp¨²5=çmgfìRÝ ÈoG€ëŸG‡åE¶¥È–o.ÕsNtÁËT¯åÃüó`šA09ÈMµlɯ¥È N.ëSý ™¢0ק vúPÁ
+;,¬x
+D$"uO˜!?¶¶Tåa_Áhƒ£ÇøSTÒ¹…Ï0ú*ôËÖY¬²ùŒ¦;4ÈŽíjäìÍ»|«,#ö\›ºW•Ù ªE"ªM×Ö#ä1°ÍÅ!ø/õ½…Àé9|Ök³µMC°ÓnøC³»å«?AŸ}×îÑ$p{Q¼{KY 5áÑaT•±T ×fŒìÿ‹÷š—È
+9òÄpïý<\žÔB\ãrrœè‚ËR8sSšõa[ìüÙ|¨‚óÇÞ~4@æ·y·åÿ?xdÔõ¦€Âç;ª·Œ‰ªlŠ)¦õ“8æÄL‚pq­3‰ÀÕÉôWq²ùW=™“šÿW`.­h_àÀšŒ£{ÞJMÕ œì1Yj†Û_ ò/ˆ/êü‘yÁ-,LßRî¢cƒ_àùh=–š(ÐËK9AÄ­mE óƒ¸P³>!µ"vøŒ›XfñÛ(%â*%K—Øóâ` ïJ´¹ò˜×/©-Ÿ7œ_«-˱é|¦¶43 $¨0:Gز—€xHJCÍ]î:Mì÷{SØEð7‡z]`س5&½Èèº4¸œ1~áns-e,!°DS`PX¾?¸Ãàдªn;sE¶ö˜š¨ :‰dÑJ‹ôÑH=‰QOˆ·©¼%Pऎ¯
+íè÷óà&~¯‘Uö3³Jô zÒŒÆøÓ{©þye+ZŽµª¾”DG>»š'–(:Ü›QíAõB‹‚ÌáÅ-£å·(]râ{xµr5nLd³áÃH´/¬/Œ5æÑöÄ”ÚÅyݽ|E Jgú»-_L,¿tbyDVìWêÇéÇ3‡’x>_i®Òz¬4™ŠÚ„¥þ4ïÇ–•×¦#âÆ-€P#òsI&jÎh47Jˆ¶,žÜ¶"Y uçÜç€ô4ßk
+™hÈ——“S™ ùîµøãÎc>ƒ»*xüÙÔó´%OÜ
+endstream endobj 938 0 obj << /Type /Page /Parent 1655 0 R /Resources 943 0 R /Contents 944 0 R /Annots [ 939 0 R 940 0 R 941 0 R 942 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 939 0 obj << /Dest [ 1573 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 359 712 372 726 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 940 0 obj << /Dest [ 575 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 341 631 370 645 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 941 0 obj << /Dest [ 596 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 369 202 399 216 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 942 0 obj << /Dest [ 967 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 485 189 512 203 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 943 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R /TT12 1611 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 944 0 obj << /Length 2163 /Filter /FlateDecode >> stream
+H‰ìWÛŽÛÈ}×Wô[šÀˆæ”1À;3Ùõ± œ
+C”
+Õ4büÖ€æ·ÞƸÌqÛíÞQ†$rúz±´¡×Pz"ä;èsŽÅ–mãñWè
+Ù³òJs¸¼u œáØO"kØ~›Q[œÂ›ÆÁ,@ÒÂ$hû°±¤€ oáaãÆŽ7Mà
+žúx Çc<Î×—J/kbù?f„^zÓœÔH^.%¬|ÁËèÿfÛõƒj±ìzAAB:R®û®­ïëv% MkˆÔødX+øºmGQzìHÊÞÇylv[2/š®T.<ÌV—õ².ÕPw­A<õ
+¨{²ÝŠ/óË[Ï”º5bÊ}Ɔ ‡$žCÝÚ§•ØàTõ¶±‡~ê×;Eéöåæú³ ³ùû?][¶~¡Ÿ™üèQþ}pƒ·mʾނ­][4—™K0]:º[±°2¥é=“„°C'Œx×S^=å’Ã,9Õ©Äqéa¹"H(ôXGA¶€\øÄŽtüAgÔ%ìªÅb(ÛÒ½kûn_ì_k©kÏ€”vl´ÒÖ”¾Ãì6v´Û¸³V»–e·kÌÖ×T…4/P|V…p¤ ÈNe!„¨”~$øçjðêZŃMS# 鉔?‘f©êFWo…î{„ÔšçP]
+lk¡ã±­G®b~·B¤ièÉÿk…ÓS`¦ÖÆ¢ë ;wªÙ*Äüÿ¾ìÓ§1Ÿ5F?¹£Š ÿ…xóÎщ:§sÚÁ5RÍ ádÜÀ‰„ÒáE”â*;C9/…Ó¥®ï(ŒA¼ûÜþíؘ}¦\}<@UI²qíícDÑHtè‰B?xX´jN–PèU6tX¦éö‚ªÇSaëbìYGŸC I<z½é-¶,œ8Åp º„ 4u5µ4GˆþÇúœÂ0p¾Ñ=2NaáaðB+½à
+“Êê^Ál&±í»E£7Ô¬ü–"‡ºJ=(rccy cÐÈj̹Ywûr(áÒ¾ã¡-]…´AE£)Á—že­›Ê6H=EÔ’„ðbõèq?‰SΡ$ ž6Gã£ËçJßÕ¥ \—lvfÛPª×UÉ8pg$¿WÉß«äoºâ<»E¸+Êâ†o9NŠ›7O¾±@ÚÝMƒ¤”òÁ¸«ÌºÛ5» hvÍI[9БѱÍðœ¤jIzÉ1nøÊÓ½(™/(ù£Z†*æ‹,½ÍÁ)³C( ¯\vù&àJaº¦N‰Š.8…EKÎƪ³ªï4ê•š†3T\ô+äý=àQȯ63p'ƒì Gˆ´êLTµQ .vôN¿\
+endstream endobj 945 0 obj << /Type /Page /Parent 1655 0 R /Resources 948 0 R /Contents 949 0 R /Annots [ 946 0 R 947 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 946 0 obj << /Dest [ 596 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 440 279 471 293 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 947 0 obj << /Dest [ 967 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 143 252 171 266 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 948 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R /TT12 1611 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 949 0 obj << /Length 2491 /Filter /FlateDecode >> stream
+H‰äWÛrÛÈ}çWLé%ƒ*Æ K¥*ÙR6JY•M§*±ò0‡$b`
+óÚƒšÆ±›G¨ziª›3ù¾„NçÅE»¬wÝ~­Ûâ]8)Rå]®Ö¾wéL
+åÞfDcŸºœ=8I§«îuDYÑW¶bµ.õ
+xÛV䪪êN0¼öÄ¡¬f%|í8¨;.oT»tÓ¦^³×˜MÑd kãáÙ^·g=‘6UY|E•DE(÷¯€˜+:MÀq’2Cùd𠀊/ŠÏ3ØÌÐ@«/Ïá9ªÃdìÑ™Õï³ÚÖd'>ðáƒ=cS*$ÐøÁüKNÊ jA«9O6uNYôÀ§T˜’È¡åNíy¦˜£ãøô}ûÓ >‰´9Nj׎Í+‡­¬Sü¿Q8wK0[mÃ]”êdÀ&LÖåÖ¡ÆNõ_Ú!H ¤
+r2áÝ¡2þH?Ž}d`„C?>„ ‹ 9B74”°±¼ÐÕfEÛá¥5œÈnÝ0–õz­µj/ß
+˜±ÍJ7V_¾ßíÈŠ¬uS¯uÓíÅkù”%tXì`i^A…?´¦Û
+ªjÅ0³:ž¹ÒüÖþzWRŸ´Ïï°«¿zÚ®ÙÀ‰ÏÅ\Ñr®í?Ô•gö½7âýÛoüÑÐøm^0¡é˜¾9fˆ!ù ùS 7]½²£C½¨ÈÑ0þO¡`v"чâT=@%sï3Œ3†©w)qw{w3¾¾ýh€<äѤR­ŸÙD*jObú·û.á?(¦¨C7Ôº"ùZÏö½jf"p}œ–Z.)Ü?ê]Õ7¦ḫ¸âvNíÏLå®FOÖ’[ñ\¨²ëšÆèÿÂt*—T4Ï>9šº›ZrÃ/ ÆJ—}É
+ðçÉ%î8&i\%WʤòþQ?§öäk(?“ª‹äôO×7†z½}O‰›H[n¯ éUŒÌ‰jøAÁïO&ÐAG¢ÖÜSk
+®ØF(¸U®EÍ–s£…YÂXª’'~÷"`zôáj$[\ùÖž»—ÂÝ‘9ÁïQ¸ãVùX×¥Æ:|A¢ðJýÛWådÆV”g‰=ªØõlP]?.µyçɺ‰Yï–ûøÜÈ
+endstream endobj 950 0 obj << /Type /Page /Parent 1655 0 R /Resources 960 0 R /Contents 961 0 R /Annots [ 951 0 R 952 0 R 953 0 R 954 0 R 955 0 R 956 0 R 957 0 R 958 0 R 959 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 951 0 obj << /Dest [ 950 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 359 593 386 607 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 952 0 obj << /Dest [ 1339 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 320 566 347 580 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 953 0 obj << /Dest [ 950 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 71 416 99 430 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 954 0 obj << /Dest [ 1339 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 249 416 277 430 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 955 0 obj << /Dest [ 1508 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 495 416 517 430 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 956 0 obj << /Dest [ 967 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 317 389 344 403 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 957 0 obj << /Dest [ 1331 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 365 389 392 403 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 958 0 obj << /Dest [ 449 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 455 238 470 252 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 959 0 obj << /Dest [ 986 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 462 116 490 130 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 960 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R /TT12 1611 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 961 0 obj << /Length 2465 /Filter /FlateDecode >> stream
+H‰äWÛŽÛÈ}×W40Ò$š7Q”ᘵËÅfƒÑS<yà-‰ŠTØÍ+’È?æ!§ª›¢FšK¼,c,6Ù]]×sª¾_M^¯V‘Åj= #à~’,ò“$HÅb9÷Ó,ˆÅj7yý^g¢Ð¼'ºh&¯?]‡b£'!ö“Ùðx?‘?ç%Â`!¼Õ/“%ZŠEà/SŽ3? ¬\>ñqzš§tú‹üg„·³(ðfaì§2ÈÄíA\³Å›H*’¤®´¸ú4þ—7›cÝáÛÜŸËÊ d±ŠëûÊü]u5 ‰eÞ”Þl)Å?øï/«&l1[~ŠÅêÞ±÷÷÷¾û¡Ôt«¢k!Wúx üHîÕ–%}\MBQ‰I4Oý8 E¼ð³L¤!<)`c’‰NMÖ“ïWGgÄ!¶½1z7 HöÞ™Á‹4ðñ7:0Ò©}õ“èuÕl”·žS䯟{½½ªUgT'LÛÖ7ž/>¯Ù$×mg4ù3‘­GvîTÏuÛˆ\hÕÝá Þ¶}]ŠjT—×õ¼~Q…; }h Ú¬X
+Ò)¨•½¤»óf±O!µK/D¼EN«T"tûà›q«Rèƒ}j
+û«§$*”0¹²µù™î»Úˆ['B JøB¬^jϺ ¾“ÌÆ¡hW4‰$ˆMâÇq¶vyŒRø‹è"J³Ó0…¡¥^FJ¾­Ûͺª±¥z÷F\Æ[@˜‚ü‘w•9lF}…“3dÞq7Çé¨[v^ЃBé"óƒìD¡cÖ8mÞ·É«F•¢jÞpš/F±'Rdž+”c勘„¼ÕÞBªÎ›Ã¨;÷û âT¼-êʃÑ19–^5æݹâ3¾‚ò'>æ
+Jr0ÜJ鼸ðè¨ØIb§¬˜•wå²ÉtÕmo—Q(I=²øõ\ñMÛ(®úçýrèäµÎûÚ¼qœàØO¢sÁ1"¿töÒfÆ{&Gc,ôãµã­GHÖŠã»Ee&²Ò¼&߈b pcO1çŒàWøå–ÛL^d‘äµ7ò~[[±ëµ’ ƒ
+Ü*qßy +“ßÖŠ ëÑBË×ÚoBå8[ÒE!.ZPr«•ÓÚ}
+¹}×Öwœä`õ(™}‘@>/•R)[ðJÙ=ÒH1n›) £,R¡ 3y9}ç3{F0UTk|
+Ýh¢ÚZ
+/$µƒÏh0F4p…BÚ´âXU j—³¦•Ã½¦§ÃEeŽÌ˵œ€Bfâ.€¬.ÉßÂóIz
+Çø6€\4Ò±ž³C5¸þÂÅ%1¾p ¼¦:2„jøH~Ë?CØŸc†yøYòÿÊ á|,ø¹•q‹ÖHAÎ]^÷¤Rû·Ï³±@Ë¡„éú_EÖõ³SßÌ™BKsKœ¹Ê½
+þ|RôLÊö%o~™Çbø9ø-ñXôßâ1«à1a"—0äc{”fC%…îëF¾÷ ÀRδq@§I5è6’ÚKé¶ßxQÏÍBñÿ†ý¢_Ç~áyb
+X .„§PÃyWÝ©Í>3´QG­©ÞP!ˆ÷kD˜j „gçÌ?Íœ#ñ¤#ñ¤#sÆ`Nâ&G8‰¢Çàk—½ýµÍ=…ŽjœüºÄ@CnŽ£G¥ìfß…ï³9Š±|Ì}3¨Ô6¼‰¤‹,ç½uÞdqà>ß Ûß/¥p°+d¨GùôÕ‘™Ș
+endstream endobj 962 0 obj << /Type /Page /Parent 1655 0 R /Resources 965 0 R /Contents 966 0 R /Annots [ 963 0 R 964 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 963 0 obj << /Dest [ 1200 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 145 708 172 722 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 964 0 obj << /Dest [ 678 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 442 165 463 179 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 965 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 2027 0 R /TT8 1605 0 R /TT12 1611 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 966 0 obj << /Length 2406 /Filter /FlateDecode >> stream
+H‰¤WÛŽÛÈ}×W4 iÍæM”á ÇÞÍëMà‘÷!v8dk†ENHj´Ê‡äòyÈ©ê¦H]f4ÞÀðˆlvWWºz¿œ¼Y.}¡Är5Q¾ðð?aâ»aèÅb¾ˆÜ8ñ±\OÞü±MDÖòO´Y5yóãwíÄs=OaO6™õÛ‰ükz§…òá,ÿ1Yð¡…˜{î"†à qcÏÈå3>§§(¦Ó_å|¬Î|Ï™©À%äÜîÄÍ®êî±âKí@’l‹VüáÇ©øÛY„÷ß"7’…ãÉì~*n¶E÷/Ý”,$i•;³…ÿæ¿Îß—ž°Ålï¹1^–°Æl·[× \%[ºUÓµ+]<z®/3»tÏ’>.'JbâG±ÄJs7ID¬€¤€a"=YMÞ/÷`
+[¼C4t=BD2zðQrì£Éyì¹ø?‚2¤ƒ_åëâ³3 1tðsè† k(¶{ƒã¼ÀþY¯ëN‹ú±p€-Óâúƒø&¿|¾Žòd½Àæð¶Û2=›v“:1î+ËHÅõ§×¢Ú¬ou#Vu#à·…ʺÒí7g¯“µkÆJÍ”;_bÆÇ„Ñéuõ™\¬.X£æCÎO­ÛædN•®¸n.É«n"a\±rBøR¤^ß5NóIó¸)õïEÑÂÀ{ÊBç Æö¾ÈîÅ}ÚïrG)mV2s¾xèŠG]áV™:3_®!Î 4€?{Žóè«|H¨–ÈÎññw
+ìI)èÏ}½ågòÇ»œ®´QéKsÈÞ›Ò}EEßæ¢&“hGX$cïÁ¯|È”´$°4_f—ªŽRy¨Ì~¶7è¿R0¸cÓ‚}¤ÅC¤Ù$¿Ñ(8pˆäºK‹²^|Yˆ¨q„<"Bhp!Þ£A‹ÈFåIDrù…Ê#§
+åÍÓñgù{ø6 :†aÔôk›Vd@L].×æ•“„²_ä8ÉþXÛ´ØÞô¿IO+Ñn¬ ûc?·íjSNEˆžºª¹—¡sŠ®XÛïõ¦›
++»,EÍÍ]+ú‡¦nZAG¥Ý× þ‚þ‚“ðž µy\šo¨dÌ厀C]ÉÌ履DK¦˜!à[Hè#é‹u‚„^ÑJ\‚vþ3}¢oý¨XJÔK±–зÆ@H»íž¶cøX¨HQÝ*sŸØŸ­=¸Þ/é/Ú^Nk… ç×öñŽjÔBÞ±¸·Ò©Àôƒ/ÚÃgëV^¬Vt„s+¢ºlqó)’
+5NQ¯C±ÙPã´ÇìÏþ*D´90µá’ϼ$,‘³ÇŠí‡Pç
+(~Žg¯íØûg³„ð)mˆÙ²DQG•ßj‘­ ·Ôr‰Òn˜èf™6¯æ/7‰Ðèìœç¼Hí ‚eí.5KÔë©.8¡!$´†{ª«Ùå ÆRÒnCeFÙOÇÖ¨>ª£úHj÷ËIxãþý9܇—J"„×®ÊPÐq»:«KñhZ ðo¨)~“¯”ë¿š
+ü¨WÞ~Å|T‘;ézèä¾|Ÿ¨Î*Q‹'tçßêÄ„E82á”è çÉŸ7kÝ™Èê\s@²MŽ"öš:*ÍyÌ…`ã[¡®”ëM…5Á•3ÇØ{ÆèrhKmYßØqè•>e¢¤¬øÄô°f²˜[ð®ºmmÈà6ÝM…"¾seØ a˜†QÖë ¬UÊ¿:\qK³ôˆThÄüòB-[ØÝ0žÿèCv»6Ë¡`¿ªˆ¶•SuÕ–ÌëíúMÛQÃ-Lö®j‰4›1µðsŽu:ŠN¸í‰ëYÈpÖ^ÙDšâ²¡h¦î“?Ì€‹wPÞËôÚpÝ ®?¦FfŽ3RMä3RYg)z9
+ª}WX[ØüÍ1æ;Âàv÷]hž•¡½ÊH‘À<&›ÇðíA c–º‡Krö¦7†s „ÊR˜Z€öDfʉ]ž‡5¢BQ¡ˆ™wÌ5ÓæŽ\ ƒ‰ 2ZA%ØYq éŸÙ4¡ä·R#O‹´4+æ´HÁ* ý
+%22½-5Ÿ{Ë+ÇqÃ6†ó!Ãq̆„]lš§õáF7
+¿7 FuÅ®ËõíæÕïn…b2Ũ¦Ù“0@½,èy2ÍœP}M¿ñsfЩqá°N»ìÕ¶¨˜$
+C/6Ó£Ìõ1@Ï®¿“tŒAZ¬Ê"ëZ±Ž·;—Ö û¬&CŽ˜~‘æÄy-Ž§¶KZØQà29(DÙõ¥úþÄyNÄN]>RLA"\Ýò;9Ä
+ÒŽ‚Š•Å #“/é­’‹
+—“ÿ
+endstream endobj 967 0 obj << /Type /Page /Parent 1655 0 R /Resources 973 0 R /Contents 974 0 R /Annots [ 968 0 R 969 0 R 970 0 R 971 0 R 972 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 968 0 obj << /Dest [ 449 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 295 568 310 582 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 969 0 obj << /Dest [ 1325 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 158 289 186 303 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 970 0 obj << /Dest [ 1331 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 324 289 352 303 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 971 0 obj << /Dest [ 475 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 182 195 199 209 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 972 0 obj << /Dest [ 1276 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 401 168 431 182 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 973 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R /TT12 1611 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 974 0 obj << /Length 3036 /Filter /FlateDecode >> stream
+H‰ÌWÛrÛÈ}çWÌËVU"ŒABåR•íuv7±cÇ¢_bå†"VÀ
+½ÄÓ6…7Èá¶CcH›œ²gS”†ïÒîLåfgª¼U¨ÂÖ4ˆÙ3D õ¦ðȵÉVÍùÎÁ5Þàòc£\ˆJk'FÀ·Ž„(Z~Q]z£äqÇë&+6\îhmˆ“±ÛReãWTêÕ܉ô%pqa7¡ÒåLÍ"èÃC ‹lÆ(hpwÛa¥>°Œ®3•J{}¶FM -áâ
+[¢4PÓMðâRɵ§ ʇ«¨Æ)jÄU§<”!ϯÚ[Cäľ^öú¸nC¥#w`!ƨ3‘^S!±­d´A½Ki{J=ʲÛAS¢ àañ‹ÒéŒ3ÍP/XêÏ$gIâ¨=ßQÃ¥ýêÒ×h,™ïAÑ…àõ[(z#ŠþºVГx“.v³ÄËÜ$ñ.þ¾!ÍÐþ«¢Ý¶HÚwƒý
+¥`¹©é5Í°-
+Ë+ÐD05ˆtÅ[¿CžÙ)>S‚Än³%C_‘7u«]™f\ì†p*eÔÚí¡{ÙgäüœI†,íì\Ù_ž©]*»ÇB |GL"èU°÷^o²ÒeCÇÈ6T(™o9ÙiÔ
+Kä2d)\“0 «‰tÎo±nréë’—EukY}“c=84¹¾¾uFEÖ™ˆ\22µÁ ý˜‡½ø¸þùãåëlùÉœ¸^4MŽ¨Þ¹bÈãŒP€=˜K¥ly•ô&$k
+@5;h‰ˆS^-ss¨;_Kº¯âùzMŠþåÅÛ×W½ö¤1ÅS𦷂â
+<x*’'`µËÿ^>- A¿û)¤ DÐm¶ùãM°*’zë%Ø
+M”D¿¿ïÍì&ÛUðÖC'3Ìüvæ½ÞqP«#ûžÔvãÐBý2½|"“èI³ê<ZjøÕž,àÂä¾#S÷5ý¹Tl
+^"[ëÄ)û5´zªzÞ
+:’só~0˜…zZäõKoÅýz‚?rü,ûL'C=w —…<ïÏn„{é'ù³ÛC«xÇz!¾Öd3¥û„;×ê³Õ‹1+}öÍ5yQÇuR:Ûw%¿ß´$¨˜˜ëê¨"7§4[õB;íÖ?S&/OjeîÕ¸<Â9$Ï{Ñ/ºbúÆyÉDؘ
+=&6ŽHá´ÄÔ> 0aÿ(žé–(–ô1Bk”E_ú!ÏÄmðDï.ÓÊE.8™ 4zƒBaAÛã!„9A²rIVjà
+q7Ž‘÷`”ÖRîÁFˆ6$2DvßÌ
+endstream endobj 975 0 obj << /Type /Page /Parent 1655 0 R /Resources 984 0 R /Contents 985 0 R /Annots [ 976 0 R 977 0 R 978 0 R 979 0 R 980 0 R 981 0 R 982 0 R 983 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 976 0 obj << /Dest [ 992 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 233 603 268 617 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 977 0 obj << /Dest [ 1035 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 128 589 170 603 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 978 0 obj << /Dest [ 1325 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 245 498 271 512 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 979 0 obj << /Dest [ 1317 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 284 442 312 456 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 980 0 obj << /Dest [ 1276 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 299 318 329 332 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 981 0 obj << /Dest [ 1101 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 317 215 358 229 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 982 0 obj << /Dest [ 1125 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 344 139 394 153 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 983 0 obj << /Dest [ 1125 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 473 64 522 78 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 984 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 2027 0 R /TT8 1605 0 R /TT12 1611 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 985 0 obj << /Length 4538 /Filter /FlateDecode >> stream
+H‰œWÛrÛÈ}çWLí˪Dƒ;\öVÙãUìµ·$jSŽ•ˆIˆIP €Ö*’È?æ!§» HI±“Z¯‚3==ݧOŸ~=Ÿ<›ÏCeÔüjbBà?|ÄyèÇqª¬Hü4"5_Ož½isµhyM ÚE=yöǫ̈ëvøA`°f1™öwýkym•1ò柼©PYà) G¹Ÿb—÷„¼ž’”vÖÿ
+ñvÞÔD~ªƒ\]Þ«³ûº»Á›P[–t[µêÕÛ#õ×{Óßü–ø‰®¼@/nŽÔÙ]ÕýÃ6+6é²^zÓB«ò_ïoó?OøÆ|›0ðS|™ã{pwwç{‘otK§Z:¦q¤ÇÀõ½ºaK³ùĨJMÂ$õ£Ô¨(óó\¥‘T¸cœ«ÆN®&¯çC0"ƒ%Á~4vÑ ("š£‡å‡9ê#™¥Îq›}¶ÂL3¹EÛy‘ÆKý q‰áw7¡¾Æs¢Õ«óùÏ^†ßÎæ§'°*×o½P_è ¯5ÂGàGF"Ä9 Œ;*£':빚ßXUzÓ¹ØztkÝÝ(
+ühªúš#iÕÚºCB]r‹è¬7Uz±²l"‡Ó,¨ÎþÑ©Û’#çíݦYª r¸[ÂGürœ¨e5e·‡¤PSã{‡Sv˜ÑymÛîÂ;ˆî4ö³6eã€öärIcŠP.YÕ½¶Í(zßù§_ùsF·Î>üž?Œaá#/Á`<Œ»l=ghœÁ˲ÞÔ÷ëͶEHêZ4vi9=5 Ÿê®*Wí…w¤ÌËÛÒŤª¯áˆ©ðå™W Ú÷M¤}ñË{Ï ú¸m‘ìBÄ%CÓ#ydü`ÚÝßZ%ÑEÜ=0r¤">!áؾ‚µ½å^,ý¼Ûç+Õ‡å0ðyïIhOB3 9ÆÅØÉd*èo" ™AĹ 
+n cüðöâá.ßµpÕÑÓxêüûÞ4ƒ±ñ(‰úÃ1Cªqoyñ†2žéÙcUùI:®âx8'î¯ÜØnÛÔ­šŸ­Æú|Ff›ê
+7çâÝìªá¦T“œ‡NuÕÚÒ
+¯ä^÷QXì”7µ+ÒÜÏã}š]ÞË{mˉWòÁáŒuÃøJE‡ž ßü'Ñ%e-×w\p6›ŸÎ^üðþÓÅebf[2ÈC¢Ü0À]ñG¥h4µ/I–nl¹ÜÔ«ûa/3u˜DïûÛ„Ý‘! —0l2‚cèR“£‘¸Ôœp]y±niú̉¥Íq<\ð3ó¨=Â;ÐI.î£"ô³ÑÁAÖ3{áÊ@qôc‰~,ÑÏ)á¡Þ9¿>%˜D€ËÃco­|‘¿å+ÒDá(aàê®Z­Ô%õ€r9EA*jˆ0î¸Ê
+—D/êeÉŽŒD.Ç##ÿ[â²½Ä%$Ž3ñtê¾+g"üÓâ>œ…ºOÖ“9Ú¥ùGš/à@Óÿ´íOèD÷©Þ¨8Í
+tÅW»„ô¢
+Îz|-W[Òƒ™L—jo6ÛÕ’:=P±Te«~x b’Œå»P9(í;8r(dHê4¶}5Ýq'¯M…Y<Œ DAƒ¼F<bä<bä¨WÖ¿2Yp*°$}l¾‹Ãq†ý z²0œ,–Ú(E±ïêbF/r¦+¿‰Œ:yãz½¼}'Jj&»?Éî^i<‡™ï€BL¾ÜNeâxòóŵí°ÕˆÚFà4ùK˜O¤è·DoåÃ-ix4º—/R! U°o’GJ"ñÓð©’qµx¿$2Á2‹¤
+±Ð.±´SÖ¥4€à©T %
+Ë  =DVS‡öÔ‘Ä8 u|Hòý`ÛÿS$ÕÈÕ!fÇÅ‹#ÛmÏëqyÙ÷”GäšË®<d»'ùnEMð¸‹×úÚÊh«ÞüÌ3ÙìÍ»WçsyFB¨’ZÙ@ë
+Tl<•”0÷3&1²“„‰krÌb4鄈Øáîþ)O“É'94,BÑFc#»¨I˜[»@]í##
+{@=8}˜QLäÎÿ/â,ÊÀ“Þb¬òăª]/“}7bHú0xÄÿTªæÂ{ÎĦHËKuAÚ“×ü¦jÕÕ¶^ðÀR®¦Â%hQtÀ&NßEY?²®6w-ŸªZß®ìÌGá_lè[g1-¶² <2½Yc:³‹/U-¼ríaë†ÞEÃEn—d#˜3[K·'™ýbµ¹®êé£ÓOì9ÉÓÍUGüdô‚hà ( À·ÝO¾:áiµ#n´¸%T‡h8Eo›æ4ŠsÅÈ9^'Vì×Ì샳]o:Û·uÙá
+yql”V›RG%÷¨rO•žtüÀ&C~¦b”8zž’ˆÖ^±Ù”‡NÑ Ì®)•ÿW¹é”%A’$t:÷«Çƒþ9ÊÁV’ﱿ Pôl~Ê-§MB‡®ïKIa^úÿ‹÷OüUæߢÑtý³¸ÒàüÄÞ؇Gâ7dÚÄ¿üŽCS×jý¦óí˜k»Îݣ͹ï”&ŽâÕ"¦ãñ…C=˜ ˆµè„f!
+ðÊHGiB-7±ŠIÜ[lÞï3?”[]ƒÍ*.ìãètq„ ‡“ë\Iý¦ºß×ÅH1ÇY257Kãÿqn©Ò¡éOžÛ“?EÊ옡³ºÑr‘E#©¦ƒ{£b`‘1ÙÛ3)õ:I&ªL*¶±NÊÌPO`K'”‚ÕÒWôî`ëPöf©Ú‘0¨0ÓÙ§‡õÙÍ(Î霼몙ppGŒ©)ðMþûRûZ’œÅæ­”J¿fÛÕM ðC 3_xå.9ž|F çN9>íìPþ¨L†°ê‰ô iP 9–dD¹òŒßÎì'ÛÕ@xfÁ&ãG"‘p¹ T¼Šn«˜Îô岿íSTl8·5V}É7y+xQÑ™ Œ`ò†³©däM·Ã[NuͱœyL`-|2-ÔUÄ<—§#XÎìzQ>À©™ZEÄýé}Øí÷Tœ÷»%%$¶TA!s€µš$]¡ ¼Ì&uà˜ÎÛÈI¨ÚŒ_TŠ«Bàø„ÀqÙ]£K>H¶
+2Ì3”¨‹ z !µ‰Þ×u‡}Þ¹µDê{UݹMu[%‚~;}6´’Öt…¡3]êOÕ!¹CÒ3y›¢"œÅWmgÞ0YWð›û]K£öI,êàbæ]
+‘úÓPN§ú=UŽ7^YY:XAà›¥Ì3Iƒ´˜Àð’ctæ/p ¸¸dˆU«dŸ j‹–íZ‘2=>¸âOˆ‰“U²4®JôŽ3µW™Š8XJûKJ¤¢^DÔZQ^!P`BAÍ'V VÏpè´ {>bkÈP^òXy¦Øò ­ŒC+@”„ûD@óìÞ
+»`³\ð»%ƒƒ¸v)’ãOq;8)›«Û¯þ
+endstream endobj 986 0 obj << /Type /Page /Parent 1657 0 R /Resources 990 0 R /Contents 991 0 R /Annots [ 987 0 R 988 0 R 989 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 987 0 obj << /Dest [ 1346 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 216 630 244 644 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 988 0 obj << /Dest [ 1346 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 116 441 151 455 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 989 0 obj << /Dest [ 1346 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 120 319 147 333 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 990 0 obj << /ProcSet [ /PDF /Text ] /Font << /F1 1609 0 R /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R /TT12 1611 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 991 0 obj << /Length 2464 /Filter /FlateDecode >> stream
+H‰´WÛnãÈ}×WtôÔLšw‘ÇÀÜv’`7 0zÊ:ÐdËâŒD$eÙû!ù‡ýÇ}È©êæE·ñä!0,’]ÝÕÕ§ªNU¿[ή—K_xb¹šy¾pñ‡G˜øNº±X¤‘'n –ÛÙõû6yËs\ÑæÕìúÓgO<´3×q]sò™Ý¿îgòŸÙƒžç kùu–ò¢T,\'¡8HœØÕzyÏËé-Šiõ¯òw£¶ïZ¶8±tqÿ">¿TÝ#¾T4ɶlÅÛOWâ_Xv„ï²È‰di¹2__‰Ïû²ûM5VȬ*,;•â?üký{ù·Ÿ˜Oã»NŒåŒ±ûýޱǓ-íªh[¨Æ–^]Ç—¹Z³¦Ë™'J1ó£Ø bO 'IDìI3†‰hÔl5{·À<LqÑÑu ÉèžñüM…gb×Á¿^Ø€ÐK}mþßëN‰nu¢ª;‘m6¢Èºì>k•X•jSˆîåQµ"Ï*q¯Ä®UÅ•¸·ì0î
+p
+ànp2›J´Ê>—¾7áhóre…ÆåS.ºì")Aœê§úŽ¯·t‘”‰t~³ƒ`}KÔÊ®kÊû]§Zúô$!çQ¨|9ºvÿ
+V¯²­ºbê`f:g‡?Ú1„j•í6vYòÚÆã¾z5q¹d!áØ­¼
+hð¤ÒRNf"R±«
+Õ’|ͱÊb¢4æ AJìK°‹¦8bíàˆÈü1OLó‡Ž.Ës²AE½©LÍ/1—/‘íÄÐ Yo'C`$Ô¦D9â'‹ŽA™ùB=sÈRß%Qâä\¥ÞG臱 £”šo_:¥‡;´ZÓ§|à…¬õê$fÑ’t6œ,’yNä+éŸNð‰R‹ƒcîPåôäµÞ€ÎÔôRÃF}4ú"šDc¹_w<æšq9ü˜Ód<‘¯gÖm†‘—g›
+>ŒF&°°¬Þk™(tßéPÓ˜%ld¬1‹u€%Ì]= Eå2eKÃÞÒXöóé8™Ñ!²¢8PglÓýy!6å·Sߌµï‘v*h×Ó"Å»GÜJÌ/È–ƒ™ú³M[ƒ)ž’Æ^"‚ñ„"¨&á#oM»›¢`:{è’{SNpÄ_‰­Zܺ®£ÔíÂ’€éó)«ô-%GŠT”kV‘¤ ¿ü,TõPšA³ªÕÎ1ýfO’d„ìWÉ75¢}¶¥z
+³k¤‰h»ŒŠøi!9ÛSÈǦ.v(LŽøK½Gj®Äj×íà§^ÂU)µ×Ûò74pÃø¶|XãΪ0FÖ?ô~
+÷P‚×éá.Cý¸Ï¦ÊûkÍô;G|&Þ¥õ©ôMë» ”7$ññ½ƒk“çnõ«0C¼¹?9Ù(o-eNöïÞS
+Ò]eú‘g*…Úâ¤ÔYÜYàmOJ‹}¾VÔ¡¥Väø¶u!g/HîxûsC­÷£.úÄ'ßþüN'À<ƒêÂí(#—áâÔ~»VDƆÌൎªÝ2 ¨æ ¸³Þ¼¹?yÓ,?ÔÔw3R».—•?ÏßkSæ·¬öãr–Ž›xÂQËÀ
+NþäßFÍV¯K£x†>í@¹ ̹¸üñdïÐ=ìž.>#~·œÁa.þˆ!ÝØ b7~ˆ‰!Õ´-!ô'ÛÖØ”U«šN¿ÁF·ª¤LÖßkÕKlûÄÕ3î!pŽl½ ?06Iœ”mõc ¢m•7׃Y·äôÉžÁÂñG•ñ1¶çÅýbFøß1ø‚ü¬Á^šþÜóág.½§ñç"©¿Ýùy?{I ­ÿ7?{i<ÆÜ©­ÄçA[ ó_õ²'ƒO2䢒S /çæùÅß—öû&¸|'5/Éÿ;
+endstream endobj 992 0 obj << /Type /Page /Parent 1657 0 R /Resources 995 0 R /Contents 996 0 R /Annots [ 993 0 R 994 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 993 0 obj << /Dest [ 967 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 252 222 279 236 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 994 0 obj << /Dest [ 1035 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 105 208 146 222 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 995 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R /TT12 1611 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 996 0 obj << /Length 2051 /Filter /FlateDecode >> stream
+H‰ÜWÍ’Û¸¾ë)ºöVhþJ”ãr•wǵådcoetI<9P$$!¡H- Vy¼CÞ1‡| €"GšxìÊnâJ¹¬!€F£Ñýõ×o—“ËeD!-ד0¢
+kŒµ#ý„VÇù.ýeâhºRšµúé I•²Öj­dKõÁ[
+û«¸dEbÃ……Ͼj Ä»]Væ†eÀò½›ví¹ Hṁ €øQUm¤&o±eÏ„ˆ<€ZÀú8¤=*·ÿ†NÍá*†ò1×øX·H-ä~±mš·ªO¶zdCõÐ[ÀϤé»[ÿŒþÏi R4AòAÔ7ªÛWù©ÎwòõK81*eѪ½í Œ›çâA$°Ž0Ú¢G½µfƒ´­ÄóC
+;¢¯¦qˆ~ÑÆÁõ¸v{‡’Í\ƒ“½i2Þý©*<³Uø×®ûѯU÷ã'ëþôééP÷#SŠ3[÷#AÍž‹5¨¸jŽn
+©c¿
+d¡É²ÃŠé3±µb&I¡Åæ%ÇZš6TÊ
+è·Æe$q$ÎwûJr~ÒÉC ÖSݻھ#%¿Ÿlo,×rŽ¢ý(ép2*e«àç¼£o˜ßC†I¾Á/Ò÷!¯òËx9žü
+á,›º:áÁÆÄYbeGòziS–p$l„;ÿhÇóTÇÉã¢ñÿAÅæÍ„ÎÏ«¦©$ôpœpÔçòé5ö°²-@Æÿ¢Y¯¿Œ•Gù>pòÜ4™±åÆù5'Ï…Ëu;àN/efe~C6•rm ã–™¨J\˼—Ú µùÁ¡d›k4Ý!+µ’#Æd®¼â‚QœÐÜJ[¥¥¥[(sù†ÎÌ{´½Zîöºc‘\Ö+8š‚:Ò9ñEÔ¦†8÷ôžg65
+A¯?þèA¶WïC´
+endstream endobj 997 0 obj << /Type /Page /Parent 1657 0 R /Resources 1000 0 R /Contents 1001 0 R /Annots [ 998 0 R 999 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 998 0 obj << /Dest [ 871 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 376 730 412 744 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 999 0 obj << /Dest [ 992 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 236 553 272 567 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1000 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 1001 0 obj << /Length 2380 /Filter /FlateDecode >> stream
+H‰ÜWÛŽãÆ}×W4ö© Œ¸¼‹2&Ø»dž³ 0
+ Ä“ŠlŽèP¤@RÒÊ’È?æ!§ªšºv7Fâ‹‘ÍîêªÓU§N³˜¼],å«E9ñåá~¢4p£ÈKÔl»Iê…j±ž¼}ק*ïyŽ§ú¼™¼ýöÉW/ýÄs=ÏÇœ|2÷ý§ìÅ(/ÎâçÉœÍÕÌsç ‡©›xb—×¼œžâ„Vÿ¤ÿ`txÎÔÝD{©ZÔÓ¡V ´q`I÷U¯¾þöNýåŸÎ4Æ{‡o±ëÊñt¾ºSOûjøÅt5 uÖÎt®Õßù¯ó×Å÷Ž˜£ <7ÁËâ=Æ؃ý~ï:¡ëëžv5´-LcKžèÜ­ØÒãbâ«JM‚8qÃÄWáÌMS•ø@R!Æ(U™”“oG0BS¼K4Nèz„ˆfôN'3â7K<ÿe ažœà·
+¼QžTäËÆt¯—šMÇš¥'ò§r¦!âèÀC‚ÎmMo@m™Ñ{‚’YTFâã ¡ÈÒPäz²=rÃ0)yó>™ÍÜYpF^ÀNÈþÀ9ôÝÉ5Óê~èÛ ’À<@Qr‚›âá+…ÿ–?aÿ…7…áGPp¢T‘ÕoTåpdç ­é%Ðý1€ôšG^Ǿë¥g^û#r6UßµÍU)"_1»ÌN¸XzNPð–ŸÜË°ï)Ó©H=ü~¸öjÊë%àOг'ø.£rfgÄø ~øñ©îb±±lÛÚÀÎ.«·”ú ëO;sJ'¡0Ò×Äç¨À¡«–ÛU2¥Ú'—|¦ŠÏ¹ä͘9›¶1ÌžŸÇ@ˆã'ýÞ”Ù¶$êô3[Hî3þº-ËOnqY|<n ѳGh{ÚÑPØèÐEÖ¹ï{ÐY˸¢GïtLžõajcŽPýÆäUVƒÜ™B:TÊ.kp@}PeÛ1—çYV(…ÀU^W3¸¾nÇ2§$ö¯šËæÊì›4 ž¹Ék~tÊ H\c>z¦v­î aOæ¨=êw®¥Eø np-ø£i%#FFÀÛöQBôì¸ê‰©z®w•­}a?…e mÔ’6ö0´µ R!1–‚×i³¤kv5[5:.CÓ›êežB/TÙµkF] »S(þŒˆ—¨úVU¥ŒÈþ¥|­{Õbj;~+«z
+gÚ(n×w"n×ɉâ<Kqt¾Â¯\¡Hª[b¼~Õnk𠣇ñŒû‹¨( åª_¥˜¢ #ú¼bJ¬bê̦톣&"JÄÁ†šõ0YWƒÂÇXfžÄéØ;«ˆ wÅÔ%n—nð›‘DÁoI…ÿIü‡’Hz™†€øuÊäum<™%„­H4`«LiošöT¼|Úòi³éŒ¼Û’Éd$_eh#vlÛAË£¯ÉÛ®8}å[«¡<~]Ègg±Éºá
+:ò½³Â®ØÚO¯à€z:4ùLÿáeù2ÛÞÜ “k¶ý_½wŠcÃ1Âa
+zMêÍ™iWý¹ßÒ3+ùND<\P±ñˆ2Y´%¦4dÖ ‹ñèú6qÍOþÌmW;:‚JI´üÍDǾˆ®M„H|ý 8n½¥
+endstream endobj 1002 0 obj << /Type /Page /Parent 1657 0 R /Resources 1004 0 R /Contents 1005 0 R /Annots [ 1003 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 1003 0 obj << /Dest [ 748 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 452 137 474 151 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1004 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 1607 0 R /F4 1608 0 R /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 1005 0 obj << /Length 2998 /Filter /FlateDecode >> stream
+H‰ÔWÛŽÛÈE^õ ½l3Ѽ“2 ^;k8ØÁŽ‚
+I¢|ÈþCþ19UÝ$%Í؃
+ø˜¢Õ³ÍìûÕŒ8Ä”à2StŠˆäè!G‰ËQâ &$~¹°¯CPó4õóè,ªA:D5ˆ­OaèÇ¡_x…ŸHñJÕGuê:ݬkSò˜ª«õë—‚†Díå~.M©øKýáÀC*?Ë÷úð¡üìy‹C¢j„›åáàBÚ¥.Â~Ç‘X A˜S|­mÉh[dm3{ݪ¾2|JӉϲ±Ï=â,íᦩOB­×ÝgÏ·Û#6Å5~Ç€K?*Îg†öÈ·¦éUÕè5<xÉY¬žÊ"óó´0›pîúj­zÕyȺìM륌~ ë”WvXÛÑ÷ÿ5O/Ÿ½k/|¢?ÆXc¬
+g¸jDi·v§ÏÙNv§v;cj}T}ð
+6#)ûIc¥„²iùÅ *þɉ ;R­µ
+ü*°àíü¬$ÃhŒ«#þ­®½%Òº÷ÐkÉe°z0Gjß×nïð¹Ö»îÊò뼧
+
+]‰ãV7Īž¶È¨ŽÐGZ/AÁh;`ýÿ8"J
+ÝpSWÈNŠZëz( <“N$í%ïyÖÉ#øú}¹6GȺ’'êûƾ*V ©W›ªí@Ì,tvÚîÛA@ao1š”Ã$d£3õÁ­
+e¤_
+†eÜŽ¿ ֲǰTP¤Ó=W¼™< ¢[Ë$LÄ®NHjÚ9Ú"›é™oˆ[õ
+ŠÍt4õIÉŸOVåÖ**:0A,–´9¿ó ¾¸=5ˆÄÅ÷ý–$}Nظµ—Œ­r_ö\¼9Ôð ½'C6Zõ‡Öí⸇Z^ivT»¹D ÜÚzµiÍÎ’ê“÷‚Ç40p4W²D · œdÇeTÈ÷]Ū Ã@tïWOöB\B[²un)Ù’%E0ÈV9ÁùúÜ»w¢¦ÂÒEç÷Îwïºt-I†ÍA¨MQEû÷È©÷{ô ý¯jI¤býøRrWýªŒØ
+jN×\·>4X9ú À=Þ;i6õ¾ÒɼLE‰M»ËÏÌöˆÔ:S¿»öª}W
+K¨ŽgÛ¸X¹¥$ZA¸æ­D×ÅÐäx~¥³JúÃÝûöáS[™r
+endstream endobj 1006 0 obj << /Type /Page /Parent 1657 0 R /Resources 1011 0 R /Contents 1012 0 R /Annots [ 1007 0 R 1008 0 R 1009 0 R 1010 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 1007 0 obj << /Dest [ 1035 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 195 730 236 744 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1008 0 obj << /Dest [ 744 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 89 593 103 607 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1009 0 obj << /Dest [ 834 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 307 347 328 361 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1010 0 obj << /Dest [ 1200 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 475 247 502 261 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1011 0 obj << /ProcSet [ /PDF /Text ] /Font << /F1 1609 0 R /F2 1607 0 R /F4 1608 0 R /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 1012 0 obj << /Length 2710 /Filter /FlateDecode >> stream
+H‰´WÝnÛÊFoõ Ýty`1üEŽ‹œ4 ržnÚ¸(hje³H¤¬¸Òwè;ö¢ßÌ,)Š¶ì\äÀ°HîÏììÌ7ßÌü¼œ¼Z.å«åzâÊÃQ¸QäÅj‘ÎÝ8ñBµÜN^½k•7¼ÆSM^N^}üâ«Ûf⹞çcM>™u¯‡‰þKvk”ïÏ•³ü×$åM©ZxnCp˜¸±'ryOÀÛémÓî¯ú¿FgçÌüе—¨›õå¡lï0hã@’nŠF½ýx¡þþ?g6Çw¹¹;×…ãéüîB}9í¿M½a!¡ÎÊ•3Kµúÿ:ÿXþ2áómÏñ±ü#ÆXƒÃáà:¡ëë†N5t,DãH¯žèÜݱ¤÷ˉ¯
+5 æ±ƾ
+n’¨Ø‡%î%ª6“õäçeoŒÐÇïÔGëzdÍÖ;z¦³ß"ö\üËXÍ;šß Eý_«Ö¨ö.kñcT–·ûl£š¶ÎZãÄn¢oÔ¾1+µ®j•)Ü1Wiš¢*ÝOçY©²MS©£Vf]”Xë`¼(U½/ÛbkÔµ^™)WEy«h›tæ»Ðvœ5*†SkÕ¶ØÖàx>­Væ[¶Ým̵CŽÞ7$“ÿò~ùîÏ¿~øüéÝòËò¯äæP¿]¾ÿø·k¥M^»V^ï˼%õ¡!í¨²[I‡ôh§TÔ¹\emÖ8
+¿¹ø®šƒxì£äè£Aírâ!Ô`ÍjG@+ÂЋ\ >%Ó0Õ®yA¥DÂ}œ±íPŸ)“b÷UésJ§Ý ’N÷òEUï«X²œ¨äarcKÊ̲LfP8ôO_Ì·ì>./à¯)Å:äØ'r,xã´‘™Ö>I_+IË5±>
+KõÁ>¯)¼Hä xhpÜ¡?+DWQÌÛCû©ÒèV;T"uRÝs€ú̓^˜M÷©àT 4 ¨z\q• p%D/àŠ-¸¶{YÕ:ÔÑ<v2m `rô\ÁVlCYA}iÞ‚@¨ˆ?X¡­™I®0ev#>Ù
+Žìâç9@f–³„†º¸´A‡.£ ɹ|Þ=Êl/Ó…Àœë3zHŒx"„é¹Xk'Ð<B,Nϵp½Ö2"¥¨¼Û]+‡à kݸq{TIãDýÞÙEÛ/rIÿÅ#ÖdÇ¢¨­÷³ÍÚüŽŒš DNË©ù)™öl“¤b¼×@Iè¢Åp=\iF`Iõüãx…B7o#B¼<nø0p_ä £®ÁÛqLXt+s¢NâáØ6ú|‡“<)‰oQÚ¥I*Š…§l[ÚPÒžÖä0b,1ÒT圴‰Èt) žRöFrù Ù_Öf[µ†Ì{%s×ÒS6¡ð0„ø¡~ºe ¬‚qj‹n„ßg™]VgÔ–m
+8‰ËÊPx:Ôt¤¢3Y{!~UÛâö®UÙ¦©úñPlV9z¨ºýD㩆”4J­FAj ïð¹ñJÿi-š¨#¤¦Š†ÑðrI.ûšú:ÐȬ¸‡Æ½­.x¬%'ìäÂH•A>»ŠTßô‰ÌÇG°þ©·Xc
+è¯Âot•¢lZ“­Èžt¼%Ä%a²k ˆ‘éìQ„À¡IüLM¨9.Ʊ˜ÃX zN
+D¡
+u~]p†æsKjèŠ[fGR[g%Z¡z‚Š4š
+<çÆcœ™?£Ìì%ßaÄ
+~­z}"x3}Ïã?=¡(@ôœýžž>Q3A£ÂZzhK­«õå«óØO sè;7G¸ç¹ó¨r볓 @Iô âÏÌÿ
+endstream endobj 1013 0 obj << /Type /Page /Parent 1657 0 R /Resources 1023 0 R /Contents 1024 0 R /Annots [ 1014 0 R 1015 0 R 1016 0 R 1017 0 R 1018 0 R 1019 0 R 1020 0 R 1021 0 R 1022 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 1014 0 obj << /Dest [ 644 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 507 658 523 672 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1015 0 obj << /Dest [ 647 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 456 494 472 508 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1016 0 obj << /Dest [ 641 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 221 481 238 495 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1017 0 obj << /Dest [ 863 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 276 467 312 481 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1018 0 obj << /Dest [ 1492 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 71 454 113 468 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1019 0 obj << /Dest [ 628 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 251 357 259 371 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1020 0 obj << /Dest [ 634 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 289 276 305 290 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1021 0 obj << /Dest [ 871 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 470 208 505 222 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1022 0 obj << /Dest [ 628 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 251 126 259 140 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1023 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R /TT12 1611 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 1024 0 obj << /Length 2190 /Filter /FlateDecode >> stream
+H‰ìWÛnãF}×Wôc°h6ï
+ sq‚YL2»k=m¼Õ²˜á $eöCöòyØSÕMJ–{ò°›`°ØdwuuÕ©S§ß.g—Ë¥/”XnfÊþ𦾆^,’EäÆ©ˆe5»|ק"ïyŽ'ú¼ž]~w«Ä}?ó\ÏS˜“ÏæãããLþ5»×B©X8ËŸf ^´‰ç.bR7öŒ]^ãórzŠbZý£üÙÇÛ¹ï9s¸±ôR±Ú‹Û}=lñÆ—Ú%Ù½xóÝ…øÇ/Î<¸÷Èdáx2ß^ˆÛÇbø—îJ6Ȭ^;ó…ÿæÿÎ?—™ñ‰ù4¾çÆ,ßã{ðøøè:«dO»jÚ¦±¥‹GÏõen_mÙÒÍr¦D!f~»A¬D¸i*b…H
+œ1LE§g›ÙÛåŒ@aŠ÷4‡èzÉÑCŽB›£ÐõBLÝ Ha†cP“(rÿ(ªøf£êùæLJ¹r•ï¤ð[\­{åoŠrp u×_#4ª³U©)Üù÷Å{¼òå-b‘J¡\_` V¤r@
+öõg†æuèãgC#€¿Ï–‡r§/°VÛi{3­Î…®ï Š‚òd–çÀm8ÚhkyÄù-£Û¼p Ü£
+®¾E…èîÚ~hNg^lŠ<Š¦¶FZ ì1²Ë! SĦàÑÑ7MGvÉß
+Šº\wwLÓ´\ò`H
+ü¬]ðcÊEÒ»uoÌD ?4ðkØfƒÈòa—•eK΋j×b"ªÚR³^ e…ÓB`Ô*i×Ó ²vþ•ÄÚvMÛ…+€>ïŠv¸žÈE6ì[MAA<ˆPIˆ«¼;‡pЀ›¯L€NMPrPK·ûhןF då»”~„ #ÓtZ‡»YV–{Qe$ŽÕ“lõb³«sÚ2+]q ¯¨]Ѥa 
+CìüE%>¸O
+ÒÃ!®ÍYŽËZ±´UDƒâ·ˆ-•Œ °:0öÔ˜­ÎŠÀ;Øîªlò¬\¯x4
+­Ä­tZ†/òâÕëA4fu:_šYŒº.:¯Ê.â>Ãç?eן²ë+»ùdfYÚKC§³µØt¶9r0£®V `ê ½†vÚº†`^Ï4.4ÜuƒËÄÑ bêy¼ZâkÚÔ„ë‚:ácÇVÜ. $ì³7|ˆèm œh }ß“k¾¬óm×Ôxvx@s|]LÝ,gÿ
+endstream endobj 1025 0 obj << /Type /Page /Parent 1657 0 R /Resources 1033 0 R /Contents 1034 0 R /Annots [ 1026 0 R 1027 0 R 1028 0 R 1029 0 R 1030 0 R 1031 0 R 1032 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 1026 0 obj << /Dest [ 634 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 289 757 305 771 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1027 0 obj << /Dest [ 628 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 251 553 259 567 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1028 0 obj << /Dest [ 634 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 289 472 305 486 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1029 0 obj << /Dest [ 628 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 251 349 259 363 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1030 0 obj << /Dest [ 634 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 380 308 395 322 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1031 0 obj << /Dest [ 628 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 251 171 259 185 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1032 0 obj << /Dest [ 634 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 380 130 395 144 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1033 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R /TT12 1611 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 1034 0 obj << /Length 2717 /Filter /FlateDecode >> stream
+H‰ìWÙŽÛÊ}×WôcÑÜIÆ
+«²*; „¾È¨’?”ᮊ§Ý&GÒì©~Š*Ð2¼æõ'8ÕÔ*W;Ç÷{dĺ3
+À®
+ïN_Ñx 7Ä´ANƒÆÚwjKþQ»
+á4Tu„?çZ.t‘‰ÉNÁyˆbäâ…¼G +2ð‰f¬.sæ! üMð×{þd=kÍþPY«Nx¹eV;˜›^¡nžµÌ+䊙¶{Æ:
+,ØdCâ.0<
+…Á\Ñm¾6Jý‚I1èI‘‰Mzo:Rb* 5œRbj÷{¼Qkºä‘½èÄ#ýÖÄ!Ø´/•”XÄœXs²}wךyªk‘Úš¼2É»«lrQ¦§¢7ÕjBn(MØvç0=J{î[ä ûór6yB…‰4Q=(ãL„çL6ó=¥Úì÷õÁæÜ-žÓ؉9¨´ƒ…»ðÏÄç%¿.™:7Ž¯{ÝÀ!SÛÇÌ
+äJ>[ªm©æï[ÐuÞ“5&±f“yÖ^Øc° æQÈ
+Ð8ûÁZV’¢õÀ>çé¾z½Â-c…Û™¨Ë×j•ƒøR]|¶*P¾[¡q¢Íäˈ±bˆh•ŸLS•ìØÙ9./ø!BÐ^‚)}N:btBèësA+Ñþ¾ÄÙ‰®(Z¨*ß{N/kHFÌ¢Z§¹¨"¹–ÒúoS»Ÿenâý¤öŸÔþ“Ú¯vcâ|ŠD øŒÚã µûBí1¨ý•L¥n’jô`¹çÖóay”xÙ¶§%á±eó%$ï/‹žuåŠÈ×?n*ÂãÔÒ"ËÁò{ÎäÃþO0":Ç$PÚ…pšÊ=!%!Ñ;F¢ÕBVq&«™ˆ&õ^ðbÑ]ï[÷ÎK^ܵå6oK`‹‚bO(¯Þòù²HÙävÕ9£•UÙ=r£j›ê«4I[ôÙŒú,N˜0|~LÄBÃvDzÛ4¨–ÜZ‹5AoM…Ê®”|˜ÜR[(Ë{›a@…Ç'@ >é6êõÛ_Y)0¯nW8®»Êï—'àÀÊŒÈæLÀZ?èÀ€O5\reó7ËÙÿ
+endstream endobj 1035 0 obj << /Type /Page /Parent 1657 0 R /Resources 1044 0 R /Contents 1045 0 R /Annots [ 1036 0 R 1037 0 R 1038 0 R 1039 0 R 1040 0 R 1041 0 R 1042 0 R 1043 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 1036 0 obj << /Dest [ 1200 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 482 571 510 585 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1037 0 obj << /Dest [ 596 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 272 557 302 571 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1038 0 obj << /Dest [ 1078 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 443 530 485 544 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1039 0 obj << /Dest [ 644 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 220 458 237 472 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1040 0 obj << /Dest [ 628 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 297 299 305 313 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1041 0 obj << /Dest [ 644 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 251 224 267 238 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1042 0 obj << /Dest [ 628 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 212 154 220 168 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1043 0 obj << /Dest [ 1013 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 349 92 390 106 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1044 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R /TT12 1611 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 1045 0 obj << /Length 3981 /Filter /FlateDecode >> stream
+H‰œWÛnÜ8}ï¯àÛR ·¢»(c0€“8†™$°{_6ÞYM·5#K IŽãýý‡ýÇ}ØSEêÒr' ¬E‹Uçœ*¾Þ¬^m6ðÅænåÂÃ?<"¸Qä%"Íb7Q^(6«Wo:%ŠŽçx¢+êÕ«‹k_캕çzž9Åj=ü|ZÉOùN ßWÂÙü±ÊxQ&RÏÍ•›xÆ.¯ x9ýŠZýYþ7Àè:𜵺‰ô”¸}×Ïu‘@j–dWvâìâDüóÎ:Æ{‹o±ËÒñdq"®ŸÊþߺ­ØH(ózë¬3)þÃm~[ñ‰ù4ç&xÙ¼Å{ðôôä:¡ëËŽvÕ´-LcK?=7…ºgK盕/J±
+âÄ _„©«”H|DRàŒ‘­^Ý­^oÆ`„>¦x‡Ñ˜¢ëQD$G9ŠlŽ"׋0!rÃP¥Â¼AMãØMƒYT½€l|–¾ï†¾ëgŽrS)~Ùæ}ÞõM«KGÉôeÙwÎáLôÚ–ûþ×S¸ÑÒ”}/Šœ×V•ÞŠ[Í¿ï`ÀÆÐÂ0kÕóSŠ Ù=rê¥&¢yQè®+ë€ N*aTÞæ5ƒSª%Ç£y™¨ÙÑüÁ¶oL¿iê>/k¸WÖ§œŸTŽf-À72ìÚØØT›8˜ ‡ ¤]zµæõ8%ü²6|ßøìK^:¾*¿ui•ƒ`H}úmLˆFØ{¡ñ¤¬Å§+s(?Š/ºíʦîDSWÏßv'œì©Ñž²qÉkQ˜Ø˜°Äß Ûgqé
+Ÿ{d@IN£8´¸ÞÜ~4ªýÄ.ƒ—âp+r6
+Ñö“Y¨Î(i‘ìû¶¼}ìuG¯>äûÑ1SæzÝÔz¹ï‘
+Wá#ïvW¶œJ%{Ñ—òÂfÉ#ëIGuҦ§SñÇœ–ÂþfÕ}3,D">”uÙõ­ìË/C@sÚâFVåŸ0kßۓ î»ÁVM¶`RÌŽ7Ê“—Lð¶eõ!ßwÎ} U«óí3Œÿ©k±¯œ |Ì -r•²IçuÅ2n/Ç3æŸþ[8RîQdwM³…m2‹
+g$ò2g#›×“Ìzk¯}¬@HC,»4¤â<PMçQõÈ3a84ì7‹Áõ§5¶¡¿W”—Lž“ˉüýãæÜŽüá)ߟ8ã$þ~~#‘‰»Çºè Ú0qQÐ |±iö=×}ÎàD'DÑ*ªLù« B4‹{Ä3‹ S°Mß¾6RõñÓæò#‰s,?0ã®É¿
+¨3Ü>›m
+3LýáÇÌv¦0ÍTàS8^„~&›Gu3û–ÀyCPŽ¡@=¤’‹¦›K. ïéÐÜÖ¢Ý%Ö“Ù9ö¡ÍÃh wªô
+ «Œ ûh€J>™ÏÖ|½3%º½>0j¿çvñ1¿ºf°·ƒÈû霫ßÍaãIQ<Bwèü̆Èbõâ|óæârsvu¤Ã2ÐiC‡Jä;‡Ü¸4^¼ç»‹‚ˆj1lø¶Q9^mlënˆ%ªJaž„Èð0­î_h’2 úp˜©›’½~Ø7mÞ>/•(Hçuz@H0¨Š•KP–ØI@0@ãŽÈ)02pb<YÈq¼ƒt™=(q±¹
+HÝhªa¶
+oBü…s;ÀØ©¾Ùi$¾u_Ü'üÔͽi6ÖŠÄnš©hÔ‡.Htå–•d¡
+T;H$
+àL^ƒÀÖJ/Uƒ¼v,JÚ0´ï¬Dü¬¶c©4í¬#2›ºìÞÏw[ñЗxñ!UÐJ/7C`Gp2#s“Rh«ÍKZí“wÉ…Œ ™<ÍÝ® Æ=jÛ¶å¥ô³|jËž»¿úzãqKâq,yxl© ¼,®#’Õá=ÌçŒ$ÙTQºl\:Ô¢¼'èAÉLr5 LýåêQõ=Š<\‘¼ˆ âC!ÔyÁ€<FÙ=¦BÆ êþz
+ööþ
+ôÆÆ3ÏS‘o·D°”{CÄ’Êž›‰¡wÌ«#=
+^"çkb ¶d.Ëjp% †
+ÒMË6³áØ̆TE(«æoI^@ed
+´D3ÿOz¹ì¸‰Dax?Oz…¥´e @%£(ëx7³¡m#ÑîˆËtòöùÏ¥ ¸¸Ó™llQUuêܾß,M´0y”†¿n'u/”m»±ë¾ðóÞGùŽâž¼Þï¨1¬6÷¾¼Ë‘È„zŒ£jfSÍ€ÿç5C+m”Z2Ž$ä‡ë‰ä^PýÓcÙק²é†Ø)Â}~1vUÛwÁײ-¯¨;é5d+%Êþï «¶ìrÝpº½ˆ £+GòÏáG¹,ˆ¬Ë‚Vƒ}à¿’Q>@aγ@šdð\7 ð/èÛ¡‚’lpüÏÁÓÌö±Yã_ë#Yq­ž)ßÁ`ã÷Þ9=ÂHFÄrÍGÁýuKómDðEKœ¾$ZT®¼$TŠ}â…’Kr,[L:iL9Aå˜t
+="ð$©ïÄ
+OÌÞFC=ç礥Òp8’–ŠS1§L
+ Îï–©9†a‹ŠìUý54èÐ÷§­ $,­#Á‹;‹‘a¦~§¡qjj,÷Ô/©þîÃGD_ÏN¨¯âbЋG/¸Ä‡éOrùÆ»üT]d&mu¶Øfû(G'2œ—yMèã–i"YVm,.@Pfññ«Ú˜ÆbZLü¤dd(ºåiÎOT7[W=é­•»Sq·qîÎGw£"o
+1zÇËLT,¤…2^±J¬ž Ñ=sÈemÇHN]nŽ-hÆø1—àÝ–‡8áÂâ£×ĆcX4þÔCœn$¤KÉ­n j¢Üƒš¤˜õÇ+…± K¡¥G#,?Šð$š$X!oä Øb<Z<!Ã, 9­{Áž,”_.ø±bÃÌŽ”Ê7΄{³m®W¦>Øäó®oIK”_5¸^7ÿn+ÑY¥·.¬…báŸLr‹Œsìì£OˆÏ"Ô©sMל„2%¿ <î } úQòYÛ{ú בõy–dßûdÅÚ±unäVºÜ4Ÿÿ6öøBÉÉL”uÜA‘ kì¥ÚyŒè¡ÙáFï¡y™%àdá©tØ©ãÜûÂ4ÊŒvœR¢{к
+N’¾¦ó
+ûTR‚Flp§à'™
+ôÍ«ø©«dX¸J>kõÿʬD³î-$- pßžgOÁ|%cç;úÓ#
+‘ÉBý¦¿¸Yrµ ƒqË ¥HâMµCß™ð…J`B.pÆúï:ë¤DK’šÜ9–?ºeá:ö\§TÄgÝÀ:
+endstream endobj 1046 0 obj << /Type /Page /Parent 1657 0 R /Resources 1062 0 R /Contents 1063 0 R /Annots [ 1047 0 R 1048 0 R 1049 0 R 1050 0 R 1051 0 R 1052 0 R 1053 0 R 1054 0 R 1055 0 R 1056 0 R 1057 0 R 1058 0 R 1059 0 R 1060 0 R 1061 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 1047 0 obj << /Dest [ 644 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 439 743 455 757 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1048 0 obj << /Dest [ 644 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 316 611 332 625 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1049 0 obj << /Dest [ 644 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 183 562 199 576 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1050 0 obj << /Dest [ 644 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 320 528 335 542 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1051 0 obj << /Dest [ 644 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 183 479 199 493 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1052 0 obj << /Dest [ 644 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 435 458 451 472 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1053 0 obj << /Dest [ 644 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 389 436 405 450 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1054 0 obj << /Dest [ 644 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 128 401 145 415 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1055 0 obj << /Dest [ 644 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 469 380 485 394 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1056 0 obj << /Dest [ 644 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 151 331 167 345 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1057 0 obj << /Dest [ 863 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 351 304 387 318 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1058 0 obj << /Dest [ 644 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 446 282 463 296 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1059 0 obj << /Dest [ 1084 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 351 164 393 178 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1060 0 obj << /Dest [ 644 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 279 137 295 151 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1061 0 obj << /Dest [ 1002 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 267 67 302 81 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1062 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 2027 0 R /TT8 1605 0 R /TT12 1611 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 1063 0 obj << /Length 3996 /Filter /FlateDecode >> stream
+H‰ÌWÛnÛÊ}×W ò4,"š÷KÐpl‘Ä Ð“ºŠDYì±(C¤â$Òè?ö¡kï=CR²réKQ9ùí½×^{ÍëbtVòU±ùòðÃ#Ê7Š¼D¥yì&™ªb3:»h2µhxŒ§šE=:»ºõÕ}3ò\Ïó1f1Ûק‘~;¿/•ïçÊ)þ1ÊyR®RÏÍ,fnâɺ<'àéô'4ûoú_zÇçŒýÐM´—©_Ôí—º]£'Ð¥ƒ•tS5êüê¥úðog£½Ã·Øuåxz±~©nŸªök¹{àEB=¯—Î8×êŸüïü½øˈ-fkÏMÐ(.ÑÇ'xzzrÐõuC»–´-–Æ–.^=7Ð Óµæ•&ÅÈW•q↉¯ÂÔÍ2•øð¤‚Q¦våh5z]tÎ} ñ½Ñ{×#höb”ÇÈz2M<ì3p%<Å®ôù iÚ]Uß«Ë×7o‹©3ŽÜLß\ßÞigœàõÎáã÷8ÈÝ87Ž år™<÷e¹WŠì‡¯#7BÆÂÓî¥Y;>Ó8€VpšÚ>¶Õ¶VÒÓ:À•™X98­¯q.^M<òôrQ­àøÔŽ)—{Zí`ÏͶuü o+̦ésjffÙò‹ÚËäZèl{G†ót¹%Ucß%ø^Š÷ Œ óF]\MÕã|7ß”m¹SUÍk-çíüã¼)ñ¥]«;Ý”¥r` –© MÝèÎq•u¶ (¶MÚ6Í­Ç»\ÓÔЉØ"
+ENú88J–4ÇC FIÔÈèÉ–käžù}çd†¾à'ø¸šÌ¸ïmÁÍß~¦NïÉò·“€zW\Jm8; á??ë™ oYòö(ÜÉ{±´Ã(/ Hß|@àÅ’y(ð|6NƒÒJ‡½$ bŒŒŽ¦ˆŒ”EG$#—â=K¼‘Z a¿ ¢Ä1€–.¼HÅÄ€Ø{̨’jÁÝ5Ã[“‡_›-±å5á׈:@LÐ…"¥¯@©ž’4µÙN 3¥?èTiÔ3Qt ÒD1Õ|±Øî–àÄÖT|U¿Ïè0 ¸ )û×=ôZŸþ'Åùz>9‰õ¬äiGFg7üÄ8ÐjrÇåå9ã%ívD=pË0b¶î„'‰jÅBJ© pVde:{º0ò†Z±%TØ{'á
+ùd:Œždœô$ãÀüƱçö¢<ÿ6ãX²±<£š!·0±äB†Hèp/áEv¡r(ÿgŒ0(=Q0‚9Р–ªN—±ƒL÷‘ét•‹òäð*÷íL8Á§üW™î™ØHf¯Oñã÷‰}Ö”Ã'RûGií t@ýGGèøÿÎê>Î…1¿Ñ9«º-ïq'¼¾9'­£Pqù6îÌô…Ôæ_YbÍD§OH~$úÚŒ¼=¥;P%‚˜Øí½ä0ý¹"#ªx'«¿ŸÐ²! ŽJ=7W¼Ø,;ðqÅ)õór’ï–A\
+
+«2üá_¾2ÂÉ3®¡7﯋»Ž N¢<‚‚%­f°}ˆr¹ahœj±Ý×íÒóP’c8íGH?¨ÂIÞÅ­!6(p!_U(JLF{ª?0"á "*á &œ\?ÏšÂy06"µä˜îûš,<¦mÎÈÙmÊàî”÷‡Âß°cÒŠäC¨B¦›Ÿ×ÙS¢°W-¡‡.! þ7ÊWÕ¦jû4°gBþ òR
+I‹$ôð÷ûªÊ@3ÚÃ^ ‰cÇ®zõÞ«–¯ÂØ» ³Xc­EL^•ÅIýpvã›Ú°ntoé9Üà›ÿ‡ÄÓ …KWÐÅ<¦«Îy îæA=eˆÇúÏ/ıž=OÝLF/æè8¹@¸ø@Ì(>/wJ¼l¨åœI9ºP†4×BqÒ„è ºŽ
+/ k.0eÛ¦¢\®mQœžÔ$ •¹ZŽ>¾nåæñ–G¯tÌd~Ϩ±G›@“ѬZ±^á,óç¯clFдQû‰î¡ÚpÐê¦:¸úÁw±FÏd‹w{ª;iT“/ÓÅ˃~Õ6`®Ç[úü…5Áü~B¼)cº:_9埮@d#M‘¯˜ä‰#KÉmNXmùh¢09Ìñ¥µÉŽ¢ù+"¼ð`ŸI>¾nzS²Ñóoâ¯ÒZÎÆ÷ Œn°‡`snºLç,8^GkŒ´D²a%!Ï$l­S9Áè-B
+{B'P\ÏRK¾EP?)c¨Zú±îÃÌ@Fïrtâ¡–_·ÔÍlåŸåTSn Dà.ãÜ69ßµ—U¤çŠß,öDø /œiuå\ÿ»<Mz.¤ôº<'Þ¤B uT K©Pÿç_ø›þ†¢DCTñlLÃÉ]8b§Q¿C8«_õ:ß!¢†­#Ñëí¸JCãË´WŠcoóCÝ<JWÐäHâ€à‰T£aÄEú!²sövtömãù, /LXM¦Bó— hb†fI,&;ç—?^Ñ‘×Í'åàâtßÙ%èR(½ª<ñ&íÉ‹ów¬ Ïeƒ–{GíÙPïN—%Ùó ¡€¥·‡8ò¾ï÷[‹’˜žsòŽÀ ²Ìç™>Ñ¢XS9ÃÕç¥×õ¹å–8(„-Ôqb?ôœü“pƒUªf0º1]¥å,‘( ¢ƒVHÇlb˜MFZž)÷Ó’ÖagoTÖ#1©!tŒª ø(ªÝŽÕYh­•…ŸNÕé—ÇÇ|íž>XRH'}¼öÙ[ÎL•@g¾A:ãKJÕ>ˆCÝQFzĔوšVALyuAœˆäDLÇ J9ÿ‘°fD ÂÏ&H=TáM\Ïìáýy¡‡ˆ²œ¢Ò¢J¬ àür±–:?ä+d“}¡>‰i=óÑyYþöï
+endstream endobj 1064 0 obj << /Type /Page /Parent 1657 0 R /Resources 1075 0 R /Contents 1076 0 R /Annots [ 1065 0 R 1066 0 R 1067 0 R 1068 0 R 1069 0 R 1070 0 R 1071 0 R 1072 0 R 1073 0 R 1074 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 1065 0 obj << /A << /S /GoToR /D [ 0 /XYZ null null null ] /F 1077 0 R >> /Type /Annot /Subtype /Link /Rect [ 398 96 483 110 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1066 0 obj << /Dest [ 1084 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 419 743 460 757 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1067 0 obj << /Dest [ 1084 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 430 694 471 708 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1068 0 obj << /Dest [ 992 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 229 606 265 620 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1069 0 obj << /Dest [ 967 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 149 592 177 606 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1070 0 obj << /Dest [ 1084 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 453 544 495 558 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1071 0 obj << /Dest [ 967 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 443 530 471 544 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1072 0 obj << /Dest [ 1084 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 218 433 259 447 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1073 0 obj << /Dest [ 647 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 206 220 222 234 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1074 0 obj << /Dest [ 647 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 431 145 447 159 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1075 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 2027 0 R /TT8 1605 0 R /TT12 1611 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 1076 0 obj << /Length 4600 /Filter /FlateDecode >> stream
+H‰”WËrã¸Ýë+°R›ßS]Såi{z:Õ”­MgœMBcŠtHÊŠò!ù‡üc¹€e'3)WY^
+"É¡ÄõÇ+ñç«e íÞÅ^,kåËr{%îõøÓ7$”E[©e.Å?é¿úËê Ú1íFû^Õ ôÑ
+‡ƒ§B/ÎjpZ Szðè{Z–¶kK‘nW‹@Ôb¡ãÄ “@„©—e" “öe¢7‹õâ§Õ”Œ0€!þ<§ìú˜IÙƒ3Ê.ÏÈe2M|˜ç,•ÐG©„ÔÛTÖíh6¦×jA>c®By{·úðíæöA*ÈM$Ô´3<@B"ΦÀ†û¾¤GéGQv•Q Ä*ðåºëé]¹ï{ÓŽ2Ynû®­‡b¬»V<ÈÁ<ÕòËg¹Lí©àaj± <ÄÒ áÂO-Bâ<äÙÃ<£Òpp²E_ œ¯2cQ7üòAyK®¨¹UÄ”E+Øó³Þ7¢nÅ{Ú
+Ü‚Œ…}ÝŽvf¨%ЯÜu£Ï÷!Êy´8ÅunÜù
+ ¿¤ [³üªä;b‰†áåJ‰ ’ ïè˺­ÇÚñÆÙ[Dµ=ìucßRqhÜûIŠ*tIIúoS“Ÿ»cy€¨?Š±Ø\2% ~!7 ¯.ò,G· f–y1 jõÜásy{}¸ÿŽXë}[’æ]2ÌÄǸ¤‹5ø$áÿ­³S…èI§$¬ánšx’=ƒKÕóSÏG›§Ñ…‡ÊI±\v¦oYçbæë³ßÔ¹¹€çL(mÛÚbm›RªàÀ–p@§õÍÙJ$¶y¼˜b­rèRYe”%a£20 €Î,ÃÚY© &)öÝ€w‰Yc øµMƒ±biÃÛw4 èiŽøí8 [mö*‰yésEÐÊ`¬}kTŽÝ¶·‚/å£LA[þiTžË#¶¬ÞZç*O2>YH .!R ðgù)Éšñ;&ºYuC]“ ꦩ(?`}° “¸ÌT€w¸{l©n9PUŒ?‚VK#[Cú“ÉÉAÎŒ¶:£}6QŽûÉ­wT{
+“Hr—d#Pž‘kŽÅ6,ôò®ç‘äÚÉíXcNÈÖ†çŽ9ä“MÉSÀÅ-´v –î5:nªP;°B=³•Ç#Ñ>CP;LÈã~-›„Ø ý3,ÂØÀv¤(ªÊön)´
+èRÅÃJåh*ªi†“èÜœ¿"åI,²sÉϨ>ä‡æ>ÇW“Õ4•[ÊLþìX‡Šxײà;Sc¸‰ZeŸÞP¬Ø ’3Íz-WQp&W6ÁAœ'“\YÚ&±ÒR#Ûå,R_
+¼7î{ävìö„¿ªm7Š²1EoÝÂ6Ž=è€ßBéu·¦8½P8»ÈšóÓy౩@;F!Ÿ”òu [|6*%Æc?ãÕDáK~žÈ™ZŽ¡©qä @Ö´“Â~` <‘Ïiê'Rjã&Úñž c:/ÿ‚ÊóI¶ýüdºž6A(XsÀmnðÿóŽ—˜?åÛ\6pj§‘ûp‡U…w&#ªÎp
+ W°î2I N ë:ÐJíí tÀW¼µƒÿÉÄò±1?\j^]ç÷£_åõÍÍêúî#¸˜vÿ·úñ
+ñ!`N¹½Wi‰
+2t#>Á‘˜‡‚
+Çëœ`¨CðÍvßsä“Z$¤™²«Íå©l˜\s—9=‰;ù‰bKÈrà92ËQœDJ/}<-åƒöãí$h-ùÿä'ƒþ¤št##ÌF)ÞõÛxÉz0 á+v~Pxœà}¥¤ÿpäiõkïÕåš")Ä#ך¸\qìdJÞ0ËùÐÈ{@6yÙç üÁ öLD‘Ïwj«m4íoúz¥À ç
+<ÙYåÉ £·3yƒÓ0‘Ñž_TƒR½YâŽü¹Û|kÙ»b)&“Ó+ÊeUç$úL´L®[þ¦w=Çz ,Õ8+[Ah Ÿ®h뜾!ÏÞÏr#ŸÆÿ'd‚sy|®
+TÖBt5õ Ù¶ÿO©ƒì鹃Æ%úçéñlœ[[ûlµ$5ªþŠ¢0cö$ྻÚ!æ`î|¼áøÊútÏrýu[R¢%‡|3œôÛ„M¢~û±§ûŒˈë-pC–Çhš¦Ò“wRd†©½”žlt»•,™D„Z}¹#ï".ÂÙXMȪggúzõfÉŸÄ®ê7!÷Úpù€É=0Ù q°kbr™ú8ádÔw•
+\ç÷´ó•®“¢q—Ÿïn–0¥Kø‡À¹ÿÊ¿ßøÐÈüÿç>4,ˆ¤]/"·Ö#ù¨‡^Ãl½@Ð ¶Ä<Zs3äš
+â+ñ†î|ç7ÆÒ¥¹{%-ñ)eù@ù ¨÷¥¼y%Jc÷-"°-˦Aìe‘ïòƽ¼R—Å$!ÎÀ­G·•Þí$¬ä–% ¾œ Á×j—o¶ ÂDK[çØÝH2}ÐeFìLÇ€®$§\Zè¿
+endstream endobj 1077 0 obj << /Type /Filespec /F (SDK_manual.pdf) >> endobj 1078 0 obj << /Type /Page /Parent 1658 0 R /Resources 1082 0 R /Contents 1083 0 R /Annots [ 1079 0 R 1080 0 R 1081 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 1079 0 obj << /Dest [ 1084 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 236 726 277 740 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1080 0 obj << /Dest [ 1155 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 246 265 295 279 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1081 0 obj << /Dest [ 1276 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 321 265 352 279 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1082 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R /TT12 1611 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 1083 0 obj << /Length 2137 /Filter /FlateDecode >> stream
+H‰ìWËŽÛÈÝë+jYZ4ßÃ0жdḕEâ΢š*Iœ¡HE¤¬î|Hþ!ÿ˜Eν—¤(µÎl&`¸-©^·îóœ[ï³W‹E |µXÍü@yø‡¯( Ü(ò•æ±›d^¨ÛÙ«÷m¦Š–÷xª-êÙ«Û;_­Û™çzž=Ål>ü<ÎôÏfm!ÌWÎâ—Y·r•znž@p˜¹‰'rùLÀÇéWœÐéÏúŸfççÌýÐM´—©‡'u÷TwÌÚ:¤Û²U×·Wê/ÿræ1Æ{¬Ån¬KÇÓÅæJÝËîïv_±P›zéÌs­þÁŸÎ_œ±ÅlM๠‹˜c ŽÇ£ë„®¯[ºÕÒµ+]üôÜ@ýÔ†%Ý,f¾*Õ,ˆ7L|¦n–©Ä‡'lŒ2µ·³ÕìÝbtFèc‹wî“w=òˆfï"ã)mEdÏÅ9·y©8ÐòPÔ·u§l½.k«ªòaoöOê‹Ý·NìFºljW-øçÆñ5¼ˆ¿ƒ¬ÙÕ¡Rd¡^5{e ;Ï×[ÇwS½«¬êšq¯l„wpǶì|5ì®!v)“Áàé¹ïÂrð˜0pc¥¢ðʉp¡C¤lµlD«²V$œ~#
+l[ìKxõµâ¼§#of¨ª ^/LUÁFN;óˆÒ 1KI¸8ÃÂ8#D¾Ž>ñò­¼>wMQض%@''E’eúÁ´v´=»ÄØÑ`TtL òhðüû¦î ª˜ï5#Ï Ý ºø¬Ç.÷Ücâ#'!Û›½Càhß^j5çóçÙÌj|Ö×_L‰ôteÂÐ
+õ†ÂýmÄE# {=¡l~þD0Ꟊ
+L:ºïehø—|Š¬CU=®p‡ˆRO÷ðÄÒUw SÉ\Ãæ
+ƒP+ܲ¾z‘ ¢ýPö½pAð}qAøpAð?å¹8‘` » Žå9ønL÷£¢(**" BýHÝ#²á™Åô¾É/Ñù÷$…à÷"…ðÛ¤0€wzܼØàÅÔ ,ûJl ÏGÌ?nŽ=PfìS•¡EÆ-Yg¬Âánoºò‹Á
+™U²˜çí÷=­õS’n“cýâÜMó~zºDfûµþ}7T‹?t‹Ø?m1Ó“Eé‰ædü[úrðkàƒÆÝebu"õ d9–;æ ŽsLí”Cqfó0t“à"£Ÿ×ÝêP”Ñ\k÷\r‡b£L+½TCyñ4*ÉÒ7·)hÓʦM%D˜»N®ª=$l³ç8ϸ•ùéÃ;}Oýæ7lÀ6oÚÆ$#EgmL€Æ
+o´ð2Afë¥hV
+"dÇËm†ŸçP÷G›ñ£ÍøÑfü¿¶ô’A}6À eàä/Áøý–T³³{¦þvì9ÀfÒŠ 8 ¡F¡ZÛReàÝb´í³æâ³wÔ(°:ø#Féµâ¤÷$' ~»Â;9m¥ÌnWÉ GbVKzÙ¹*‹©¾Ýж=´
+endstream endobj 1084 0 obj << /Type /Page /Parent 1658 0 R /Resources 1092 0 R /Contents 1093 0 R /Annots [ 1085 0 R 1086 0 R 1087 0 R 1088 0 R 1089 0 R 1090 0 R 1091 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 1085 0 obj << /Dest [ 1155 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 246 730 295 744 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1086 0 obj << /Dest [ 1276 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 321 730 352 744 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1087 0 obj << /Dest [ 1035 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 356 526 397 540 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1088 0 obj << /Dest [ 1084 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 321 499 362 513 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1089 0 obj << /Dest [ 628 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 366 301 374 315 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1090 0 obj << /Dest [ 647 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 501 301 517 315 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1091 0 obj << /Dest [ 1084 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 299 261 341 275 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1092 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R /TT12 1611 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 1093 0 obj << /Length 2124 /Filter /FlateDecode >> stream
+H‰ìWÛrÛÈ}çWÌ[U&Œ;@—JU²¤UœÚ²¶,æa#å‡&²$ÈF’•É?äóÓÝð"ÉŠSïC¶,ƒ˜[O÷éÛÁûéèít©PMç£0Rþá')"?I‚Lå“ÔÏŠ VÓÕèíiW¨ªã=êªfôöâ*TŸ»QàAˆ=ÕhÜ¿>ŒôOåga‘ò¦MøÐDå?É 8.ü,¹|&âãô–ftúZÿ3Âì8
+¼qû™
+uû¨®»ÀL¤Iº«;urñFýå_Þ8ŸÅZꧺö]-Þ¨«‡ÚþÝ´Kë²™yã‰Vÿà§÷×éŸFl1[~†Áô s¬ÁÃÃïÅ~¨;ºÕе+}¼~¤+7µ`IçÓQ¨j5ŠÒ̳PŹ_* ¤‚I¡Z3šÞO0â[‚}4¶è„ˆfô¶žéñ˳ÀÇ>"{5}ì ýÉ éÖ©ØóçfYÿbÔѬ´^0:/ƒvÝz)àGzî%
+¹Ifܼ]³ÑºpŒr?ÉÅ!×:—·’? {EUkÑ©+ã/vá$¤~:ú oÕ»¶ór?ׂÂTõ‚
+µEÆ8†Ä‚ûóÁ6ƒXÄÌïšÊÖ릃­êFÓÏ]µP%íb0óž6jfèÆ°¹­oÍLÕ
+C?ÆßÄ÷&:òÆÓjÝòJä#tԣ˳÷8zêH»ñüÁÎÄ%nâ …÷â"W2ì#%›„~Tì¤Z0¤ZŠ¢D{C­ŽÊ¥imWµõ†'ìñ;à#€
+ëKuoÚŽbF­›åãËêÄ[yÅ ¯p¸” ¢Ÿ±XÒ¯À2~ü GÚ…$~Ña2è0)W•DÔ0_àýЈ2Ù+>Úqç5U¯0ñ³¤•¶-<Phv#€}ù‘ìÊO¶‰šð-½–jÿ*R6‰Ð³¨NÈi‰¶ {g‘ÁcºžÌDʼffμY7æðÞg
+ø¸9BG+yÝl¢4¦¯¶‘Ͳ¬ µÁºél¹\ªê®³¸ôÖ
+¡¸P}%¸ Ìf2ž¬‰&„ÄŸ€äGÉ·r›,8Hv¹Ínÿ壟Ÿä=­Ù´†‡›žßäÂo\3㉒kq9yk@˜0^\Ĭ,¢h!)SÅ$ âç*3Óo½KŒöÞ^”ÐOð«ò¢gj`O˜Q™9""Ž`m!Ž‹Pt ûò¾}þ· *<¤¨Ì „;AW”[T–eð-1Zʳ0¬ýÎ
+Kr5pÑþäìlJùê“OÒù/Χ§ˆ$½³šºÙs Êi{éfÎ~øðãôÜ dû³¬0J{£Ôâ)Ñ4”\==;‘/ÛŸeìV?Z©vkátŒ.Ð7‡ƒ|'mF‚MÝ>wêB¶÷7xÑVn?7ýðñ‚‰æw£5ßÀf„ïùˆ¿•ÒDQä§Ù.¥ðï?¯I=J uJbkkV éšè;^è$`¶ä†±Gž-ðy³ìé θí^<4°§”æZWë™é]¼¹%D冤Xr²~Õ=ùn{ÂlÂ_~ѯËl¶u!Ìö™ %߬Ž•{`¼bdxï~aX¨ßI9f’¾øö‹å{5Dþ±}Ð}úžy䊰!.ò‰÷’N¿“˜ßIÌÿ˜ÄœOGÿ
+endstream endobj 1094 0 obj << /Type /Page /Parent 1658 0 R /Resources 1099 0 R /Contents 1100 0 R /Annots [ 1095 0 R 1096 0 R 1097 0 R 1098 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 1095 0 obj << /Dest [ 1576 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 336 645 358 659 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1096 0 obj << /Dest [ 1101 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 227 562 269 576 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1097 0 obj << /Dest [ 1120 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 436 521 475 535 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1098 0 obj << /Dest [ 1084 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 483 94 524 108 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1099 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R /TT12 1611 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 1100 0 obj << /Length 3753 /Filter /FlateDecode >> stream
+H‰¤WÛŽÛÈ}×W4æ%MdDó&^ŒÅöÚ06Ûð(I&²5b¬!µ$eíäCòùÇ<äTU7EIƒì"áÉî®î>uêTÕÛõâÕz©P­7‹0Rþá'É#?I‚TeÅÊOó Vë§Å«†\UÏ ÔPµ‹WîBõ8,?BÌ©K÷x\èÏ壱Xyë¿/
+^T¨,ð‹†ãÜO±Ëk"^NO«”VÿUÿ+Â×exË0öSäêáYÝ=·ã_"m<XÒC3¨7nÕ_þí-Wxï1¶òWºñ]moÕݱÿaú‰uÙÖÞ²ÐêŸü×ûÛúw ¾1ß&
+ü/ëwøÆ'8¾û¡hWCÛÂ4¶ôñø‘®ì§-[z¿^„ªQ‹h•úqª8óó\¥!T¸c’«Þ,6‹·ë Œ8Ä”àº!¢½“g~Yøø/Kóúrøµ·ò3œ,ĵ›_”üàÔtèÞK,PûQ5t#m§™ŸMuM­ŽXNëMk¾™¯JkeôÈH9ì¢ ÊuÇû]L5²a;ªºKÕŒæI¹#ÉTS™æ›WÀ€[ûêc75nË~ùUÙ:.C@‘ rÀ–X†íÄzÏ2U³ipÙ®UݸÅnFÕü›êÒ[’ûÇRæÓ•CÝõFí€ÇNÝëc³Û)ÌÕU9V[妅4ÏBš®—‹Ðõ/MuíŽÙ”èç{Oa&ÎAGy G¡åe9»`<]°8]° ÒìÀà#öhÏO[yö*Én®éÐ%>Ê
+>xâžÂ9Fö]+f¸÷ü+œ%€#À!=1· áá<ä.Æi¨(N3ÝÛ!xîé0ŒØI¾‡Þú²Z]*üp%)g®ÛûÇ6üè–95ó£Ü†3as’§ –¥ªÙ€‹j[j,¿š–‘(«±Ámù°½ùéÐô ÆØaV[ïŒxf$„TÕÕæV1¦?6ƒ‘S³'㓇dÃÄi=ÑÖØvØv‡]‹ó•[ØiÚGÕ1Bp›ˆw…Dx’("š€Ð›r7’Š9
+d©k¦”|]øHéÔrá3ŸaÊúlugëèi¹;·©í³³*Ù¥i«Ý¡¦Ä ‚Œ,zGJýl+ª
+ú¼|±ì‰NºÊéX“Œÿf˜I7Ýw€iQÙé?‹˜ôŹJŸHÿåýÝû|³wÌüP?tÝθì’ô|NÍÄ*Ëý<™‘Þr^Ä€Ð[¦½E¥úᣔ–ŸÛù5„\VH‹Â~Mƒ\DšKGæ˜dú¥nßÔ’ëèE‰üþ`¶å·¦;ôj0#ˆ™È…HŒ~±,ûΕS¦­7e³ö߃íöZ%0 6Ó!åÏDûQF!êµ7ý’Ëæ‡ò,ÅÊ‹´#H2ÌÄ=3"^v]@ ÇÉò™œË1­gÉ6ŒÅþ(2w449].qÅÔ Û8”'=€"u TT&49T¤Ð$ý~âøöhK‹c„¢ô"¥S]DSýþTö_á-}€Šã"Rèìȼ15>„‚ýŒCLïZß´¹®«2?,æ°äg°0 Kîi®p‰².¹KŽ…=+P!­¤GÀ6|
+*Ødä–„¶k—Ÿ~¯¦.lŒ(û8YY-´ZÈÒs¾9! "'¤¶šƒ*œ³Rªå/ui‰}Æ`­Ê‡NÞÆIèr™„DæØp_4ë`r–+X’eæ*uF¡̉ö|qhÏwêïžJNíHÎ_Iê°ëúH?y”W8XYŽ1¼Ï9ea89P¼ÇNˆ¡,Ôu=«c×µ#àîFÇàn¿KWÔ£¹9 ÃwòÒ{Ô?¹¡ÃÓô´ßwýø½]q¯ÝÎoÜ@óŸ‚7Ó±¼ÄålW[ä7Ü-×êªÑc gSaw­Ð‘(é›·Ÿp+ý…vˆôúÝ›5Âk¥ßÜ­íÀ{vHúAж íÈyÉ<òðd5 Ç…G1‚7‡
+"Ò}¸QÄxk¶áu-Ú¢G×=˜ÚWŸ;h߉@ÅlE¹«ÞvL9.Šãòk Ñ£¿•;¬G€*íÄ×ê†S3%h” u}s«nìØ~WzÈ0†>áÁN‘ÁJvØ6ߌGØþ¶6;3ÊóLÄ*Ù¬ÛH´d&ËDk³fFɼÞz\Ä^ÑŠ=:ñ#+t7U·¾á2‹þæé?ŒWÉnÛ0¼÷+ˆœd V­Å²eŠ´çhní…‘éDˆl ’œ¤ßy‹V;i/¶(qy|Λ±%9ÓØ»êÁ+¥Åþ‚¨!Dë•‘æj᧠*%”“®UWdxïñ~yd!p~ÉŒ.áÈö¾ÍyÏKo_ÓÂ8d¢Ä:«ò²¹Ý˜:ãÔ"8<q áâ d‹~-™Ðã`©,f6Æ8OËÀ©¡ÓPR¾IƒáiÉÄp©¨§÷¨—í~ÃUØÛ<¿+w¨Øf–4ÿ† Åꬨþ’AeH:F§Göb"d"bò
+·FŸÌ|qÒ|ÑÿÛéÎÏ7Y‘ƒöźq7ÇÄCS¦Þ(º)MpH—$»ôåÅæ3Ea¸X\ô
+[ Ïxu{=å­L¾#Kùj0¸ÀÕÞVÏ­xS,´ºÍÖÐGîñTØêJÄ;(5
+°}R8ÑŒÐH¤ ÈJ¸$FBeätƒ%¡×Xq
+ÚGÿœüÕuëï¸ijí¼ï^”n4NWÐn…´.°;ôe£/I4è8ûFƒà–®s ä®»A¾Þ³Ÿ`V…ÐœÍajÛ*áw]Y‡ßÊ·kÉ<ˆb´mÈ{þÃ=–VtéeÈ8_ŽÞÉ9~»ÿôw
+endstream endobj 1101 0 obj << /Type /Page /Parent 1658 0 R /Resources 1108 0 R /Contents 1109 0 R /Annots [ 1102 0 R 1103 0 R 1104 0 R 1105 0 R 1106 0 R 1107 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 1102 0 obj << /Dest [ 1120 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 147 544 186 558 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1103 0 obj << /Dest [ 1392 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 209 544 244 558 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1104 0 obj << /Dest [ 1101 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 401 373 442 387 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1105 0 obj << /Dest [ 1339 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 389 252 415 266 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1106 0 obj << /Dest [ 493 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 284 150 300 164 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1107 0 obj << /Dest [ 1101 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 173 96 215 110 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1108 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 1109 0 obj << /Length 2375 /Filter /FlateDecode >> stream
+H‰ÔWÛŽÛÈ}×WôÛ6‰æM$5 àŒ °`´‰'-ª5Ø"²9òì‡äòyÈ©ê¦H˲Ç@ dÖt³/U§ªN~µž½X¯#ŠõnF"À?ü$yä'IŠlµôÓ<ˆÅz?{ñºËEÑñš@tE={ñî6÷Ý,ðƒ Äšb¶þ<Îä¯ê^ã°Dxë¿ÏV¼i%²À_¥88Îý4°çòžˆ·Ó_Ë”v¿—ÿŠ0»ˆoÆ~*ƒ\lžÄíSm0Iíá$Ù•xùn.þúoo±Ä¸Å·¥¿”¥Èâa.n¥ù]·KUo½ÅJŠòÿÞßÖž±ÇìMø)ë7˜c ŽÇ£ïÅ~(;ºUÓµ8Wúø3ð#Y¸©>éízŠRÌ¢eêÇi(âÌÏs‘†@RÀÇ$­žíf¯Ö'0âK‚OÑÑ Éè!F‰‹Qâ $~ç™°ÃÔl¹ô³h‚jpB5­OaèÇ¡en”âºÕ®·;Åò*ëû›+/°uAŸ¾,åNÑ7^togJ£÷(kQkžþhœ~Ç‘XÀë ÌL›)ÂyÑé®+›zp,?O¾“7ÁÊò‰7áàŒóåuSUÖz ;®8$™ ê˜Ó©Ÿ A¥,;Yñ^^o•Q‡Ò4­GY£oNÛU Þo`×à 2q
+·Ë\"_¬F(¨¢šk}»¾.(¨ª*Í“hvâQ·L ±Ì(]€›S¸[:ò·Á÷¢Ù#<zhZÕ> â¯\G»miß8øpêþš£P-ì¹sîõÀN¹uMoîÚ«÷¸Ø¦I¡ê Á>
+˜„Ò¨êô( –9Kƒ0;Œã¦€»øÔguA²„ éwÕby€¥nê…Ä…v€Â$µ
+€ËS¼­K®F0VŒJgˆD(ûKB.µnQ"Z- |hîõ×äÓÅÎþQà\‰ãƒ®¿2iKŠ(•r})?Z­hÐr=b¡„{Ò.Ý“6á'í|xeöe“Y$°“ù¤©!•=Âþ¯ ã–„ª0½Ç-TUðÕ…¶ >9nØÓLî;Ga1i1™Íf³R\ˆ®rЗ‡†  ß•›Š`Nøt–cÛr·Ó-½@FÝ€
+$:±C lù³†u[ׄNUÅv¾@íöŽªéÑ9 /&F4†)²¦s<>r`Þ5ù½²>s>³>v>;ü\ º•1v1)íœ<¾mmó£þÎÝ})ÝIª¢˜Øp¬ä''¹#Xâ*»"ßcLìöéš *fmz/Òˆ¿\¦ŠóT5eÑWª“äc xç.ã¬@^HÉpÅ"‹FQX"ÚéÊõ”U_'…–.A^ý¯ôК4q˜ŸÞ-ß(†#ztES1Ÿ7ã¡“@Ó‚Ž†„ò]H³6Žå•°m‰—A-p
+endstream endobj 1110 0 obj << /Type /Page /Parent 1658 0 R /Resources 1114 0 R /Contents 1115 0 R /Annots [ 1111 0 R 1112 0 R 1113 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 1111 0 obj << /Dest [ 1125 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 218 716 267 730 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1112 0 obj << /Dest [ 1339 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 389 649 415 663 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1113 0 obj << /Dest [ 475 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 471 485 487 499 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1114 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 1607 0 R /F4 1608 0 R /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R /TT12 1611 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 1115 0 obj << /Length 2845 /Filter /FlateDecode >> stream
+H‰¼WÛnÜÈE^ç+zqÐP¼_A€$dž‰m@cˆ•§GÄ"'$G—ýüCþk?crªºy™‘Ö6²N°^ Ùì®®ë©S—‹ÙÉbá W,Ö3×þÃOxv8‘ˆÓÐŽÇ‹ûÙÉU›ˆ¼å=ŽhójvòþÆwí̱ÇÅž|6ïgòSv§ ,Öâï³”¥"vì4‚`?±#GËå3§§0¢Ó_ä¿=¬Î=Çš»¾I'Ëgqó\u¬xRY$Û¢ïÅ_µæ!Þ| íP–#óͱ¸y,ºŸUS²_fÕÊš§Rü‹ÿZ[ü4c‹Ùϱ#¼,Þb5x||´-ßveK·*º¢q¥GÇödn–6,é‹™+
+1óÂÈö#Wø±$"ráIƒD4j¶ž].gø.¶8ûÞ½ëG${oŒLï¿8rlü?:Ð {Ò©¿(îUÛeÖ<€ª÷pW(·P¡Ý•™ßâ¹Û5ÙHø…6aªX™cýñ%ÿú²UÂryñéZ”féY5âV~|{yE!𤨱-wwEuk ³§±|©DQuªÙâVµ»¶¨îúÌ]¦‘ãI}oH$z$KH£ÖˆŸëJ‰v«òb]¨Õ1나òG6Õ‡ ÷– 5¶­èj±Tâ±):œ@ E|YÑ—ÞJ:¡OFl%ýÂJóŠÛ±ÔFçu…,>X¤°T ¢…¬ÚÄ°'í1©½TëºÑš‘n¥zÊÕ¶XÕ¦X!Ôì,·d÷[ý*ÔÓ¶,ò¢+ŸÅ}¶%UR¹å*€™Ù´.댅îô=’©GV„k˜È#q_¯”¨·]QWˆÊ«apƒ±žƒ1 g¸ú\œˆ³µEyQ9–t-WôÕŠqÓ±hq#œpmÿRÛâ¢qo-û¥£8y68á•uѪ|¨;u*7ªÒ†ÆäX‚u
+
+„òI't'j2:‘"g«¬Ëôî®n (Õù1‡…‘DÆà$»±®àîL‹ÐUÞ™íä,ïvdW,³/|u®ô†=qý™zrß¡t’C!¿³ÚeÌ\´µ(:|ÛÖ B
+½¦#OÙo¹Tñ‚î¨QüT¶,)+)&:©Ü“dDt›¬™Þ®DE‚B#£fY(3Sí™Øšó×ÊýEªvE¾+3øzL>‹î=6·³È½ùF˜ @ælÕW.YGañèd¶Ìö²JG÷VZ܇t‚"®ê °Qª>¡© ÛnrPxÞk…66ØH@G æ~ ýÚ·Ÿ0ìØ›¶Ÿ¡÷v›²w,ô6uת†0²Þu(’Hnwü¡;?XêßØKÏÔ G*4rAg'
+˜ &DøMT”‰¡éâö®ƒ¤—VqUŽ´Ì@ý¾ÓcÛK'|ˆPw€(pGFP# b–˼‘›´Á:VkPñ’Ì}ÙxGúF¡cÌ6bŽ ^Ë?ó꟨;±h…‡mäpíɬ3ÂÊâJtä•&‰Y©*Óƒ2­ª>hq¥xÇ$r¯3åÙkÝ|¦ï9qO0LW,À§Á0^%Ìãäåø}ϹW”¬õÒàݺ©‰©DL}i0·%îwmGlìœwkZ=+Z5ɶÎÊVÙCz|O£Ü“Ï´Q‡ n…˨¯IZ¾ÉšVñ5~ 5°-½Jsž©¦\µq5tEZ®ô b8Úavàûy-pÜxÄ£CH¼Ñ !דÕw¡öÛÇ}Ømf¸ßÓnÜzÜä ë„¦ëøL\Ïô’Ò+æ÷œ¦“˜é”Ë#Aϧ¾§3ü€65©òÞ U¨×„J%ÒÃ
+åÉõÍGj¸‘œ'I˜Î]Ji¡Ï}Þ>5è¬ÍAC­–Z#jnú©²ßÆ¢±ò#­Žúç®x þÒS_²ugJeÆ‚ç[<j¶ÿ¨—È_t¦éºüMöF¨.·ü«ù~/úÝ!`•=·ÏyJ–DRæ#?\ù2¡;)•x4:îc@À†l„OÑR«UFæzH?¶Ç¢ZÕ &¼ÌˆB;¥g"õª‘aÖ´¨C„6Âþã²Ða‚.$&=ŽöÙ*mºýeß>ײ/o(ƒcùq’eÜûšè4r¢ÞÍA?ÈœK‘=ŸïæÉ)`„SH3׉zS>/¸½£,HåÜ" HöM¡F¦aë…1!iùãbÓ'çûKLcîKï0-áwo:v„C' {‹n
+ÔY±.ˆ`húG™¶^$=ž‰±j"A¤,¿VŒü P!ÍrNœ}¡:[|¨;Ô6ßÔÒ°oœÏU 'mw[0lÐÀhî5Ð02j[3Fì²²š)”GÇÕ©æR¯ aâA+n´é E]é‡ÿ!¼Lãwõ‰ç…Ô^ÂJ2IÈq"èiæ^ø®È‹!BG|¦@
+endstream endobj 1116 0 obj << /Type /Page /Parent 1658 0 R /Resources 1118 0 R /Contents 1119 0 R /Annots [ 1117 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 1117 0 obj << /Dest [ 1357 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 463 106 499 120 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1118 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 1607 0 R /F4 1608 0 R /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R /TT12 1611 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 1119 0 obj << /Length 2428 /Filter /FlateDecode >> stream
+H‰ÌWmoÛÈF¿êW,ü¥KÀbÈ¥HI†aÀq^p‡Ü]qVQ´qQÐäÊb#‘‚HEVHÿÃýÇûÐgfV$-ù.¹4h1"r9;;óÌÌ3³/gƒ³™Q¡šÍ¡Qþág41þh$j<ýdDj¶¼¸©'*«Y&PuV^¼½ ÕC=ü !“ †‡ÇÝ@ÿ)}°P–(oöÏÁ”7MÕ8ð§ G? D/ï1¼žâ„v¿×?¬Mà ÃÈOt0Q÷{u»/›VŒ¶4麨ÕõÛsõ·Ÿ½aŒ÷ ¾Å~¬ /ÐÙâ\ÝîŠæ_v³d%‘NËÜNµú7ÿïý}öí€=foLà'x™½Â[°Ûí|/òC]Ó©–Ž…jéã1ðÎÜÒ‚5½ž BU¨‰?JBýÉD%!Tðq4Q;˜^ÎZ0¢"ÁS4:tBD3zˆÑÈÅhä#Œü(šŒ•¼@DZ?6=TñÍ¡ñ) ýÆ›Ànu™§Mº,J˯¶Ìë« 6ieˬʋòA䪹∊¤"YpEépôGQdÔîá˜P”\h ˜ˆtâ}Z[U7›gCÆJÑW'ÇéØúL}ã²Ñ)'•7UÙ¤p"WEyÁ¡ëV™ËíÄ‚K']²:»ŠÚKà]Sm¼Ùb¯ÎÕ¥,YYùè~¯p
+ÁÞÕe¶,lÙ\û0äs! /“cLnÒReâ„ØÂþ ní§G[™®,…‰‚9DpÔªÊí!wNŒ‰ºø$lŒØqME4Ò "s¿mlM¯¡&“Brò×Msº–UùËÇv„œïõ+;O·ËF¼ž|VÔÞë³¼’¨œ‰ü‘_½º ºcõl®Èí
+ùßf'„€èÖã)]â…ά<QwØSõÎs#óÑ@cŽ§L6[5Tu¥ŠSë
+S7 z5ÕOä¢~‘~´*/æs»Á ÕæŽ7…í5;­æÕ†$¼ád0GU%Z„Û“j_Ñ¡¿™ß´Mßè.>Çו÷º¢ˆžI#Ÿæv6á~¯Ã½¶óÞ9§3ÁÊ›DJâ@)* J‘Ýĉv:PöÀŸ Oto7oki#Œ…ð©²j”{0–å9='üRµv¢Ä7]dÛ¥[8 G)ìr·ÅÏÿ-‘9º¾É@ÊOÑ“‹F„ûúÌåv“lº„Õ˜/æ…åõ%"¤ñáŠéô˜ìÒ}­XÚ£X[† òýôWzèìj¯_|U0½ù¸½#ŒÝÊñ—GþnÊþ
+#ÿ34{_UK =Óå–ØîsîÿÓ‘¿wÓø­#¿Ì‘ìšc Kô²¶dàaבw'ƒÿ¯Uû7|‘s%χ²«ÖMQ•ª ¢K%öëš¹P
+kkÏÕQÙ;Æð‚y¸lfà„ —ð˜y- ¨âÂIõpRý—«t}Å›ÜA¹k#¤“˜†;곕~Ò…\É\3ɽPû
+P¸N!Lí¨žØôi!ÿEÇc²û~À¢',|¤žfdøªxX4b¸•FÕ,*·Íi.¿e.pg`÷à$w رå‰)[xœ¤iù`sÿ„å¥åŠžã†¹¨¶8â&Ûc§&­Sn•8;rî(Ûï«dj†6í•Ã•›©kØU鎴™]W1!%“¤ÍS„[=üÆ^æ•ëÍÜž¥óÓ9_æö-Îë5›Ò·Í˜¹ÝYY$|ÊÇòF¿-ÊÏi&@}Ò FÇó€k#>œ¼œoªC : µ]U _%-2‚ó|_oÉêæêBÝn×¼‰^y
+PüzÎî(þ6ºšº.G‘!„Ð º˜ ›8xè|µÁ¹Ð…!)6$¥õ¾ÌÎ>ÝdL€‹®ù½4ó{j2Ñÿ§É˜ÿ¶É0þ
+endstream endobj 1120 0 obj << /Type /Page /Parent 1658 0 R /Resources 1123 0 R /Contents 1124 0 R /Annots [ 1121 0 R 1122 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 1121 0 obj << /Dest [ 1357 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 351 517 387 531 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1122 0 obj << /Dest [ 1357 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 429 232 465 246 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1123 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 1124 0 obj << /Length 2272 /Filter /FlateDecode >> stream
+H‰ìWËŽÛÈÝë+*^$E@¢ù&e Øn“`ÆIÜÚ$ÓA@‘%‰1E*d±…žÉ?ä³È¹·Š[j{œõ l´ÈbÕ}ßsO½]Í^®VðÅj3óáá~¢,p£ÈKDºŒÝ$óB±ÚÏ^¾ë3Qô¼Ç}ÑÌ^~¸óŶŸy®çùØSÌããq&ÿœo„¥ÂYýs¶äCK‘zî2à0sÏÈå3§§8¡Ó?ÊÿX]ž³ðC7‘^&Öâî±Ñ;¬R9$ûªo>ÌÅßþë,b¼wø»±¬O»¹¸;Vú'ÕÕ,$”yS:‹¥ÿæ¿ÎßWœ±ÇìM๠^V·Xc ŽÇ£ë„®/{ÒªH-DC¥‹GÏ da—v,éýjæ‹JÌ‚8qÃÄaêf™H|DRÀÇ(šmfoW§`„>¶xO£qŽ®G‘=ä(²9Š\/Â†È Ã,æu jÇnL¢êÅcT½Ðøäûnˆÿ±“¹‘¯áY¡«½êu¾w2yȵjÊ›Wâ÷íQh'…o­³ WE©´ê°É}Õ(ó±|¡+¬¤rO{b©lDÝ( ±€ãžŸR</2øÆ–vã,*)ê¼×‚LyA ‰Í œÎ. óä©·tƒlâ©?
+·²ßµÎah)ªæ§+=‹µõž¸é˜p²Š%˜Ó¯Ë\罃 R·C¥n.­Zðy¸ »F'Q¥cÀ3kHÞˆÂcìˆÆ?>7Rld¬Û¶Vó׃ƒ c¨Ÿ3&<ã'—ÞPƒDRë®ZZõôêK2ɧ|Õ$/åRlZJ¼)ǯÄÀç\ü(oÕ&jm¼Î¾¢Â”Ç5±aþLzÁ;«;Û¶ÉÕ•Cžî_V=?ŠMoMSñÚÅÕøN‰Ô
+%j´ñRæ´˜Éþ©"wjhpJ‚-§m·çs©¬ëÇ9 $îwíP—Ð+jµÑB·ßu`4 Ä=¥øÞ™‹½²« < eÕl±ÛŠÔŽO}ÂÙ™®Œ‰r¬Ñ©<½!ì|ˆ3úq>î%:ô$Êkµ­šù%¿X~.åHöN„ [ò^
+«…籊-Rëv(vÐQÅ&gûv]‹„B9-Aͱ2÷ ¾†—èÿLé[8s/KÕÖíáÞ„ukòŒb
+çT?•ûj»Ó¢i5åóÐ",+Öµ¢Ôö
+{Úç<îÇ´j—®³‚“Y-uÕiòÖ ‡’3»”¼bc6À,$©íQNl¥¶»Ö£ÍÂ.ШG&ŽöõñôlM¢‹¡ë¨­8­¶Hëq—^\Æò<HO3•Œ6¾#AøS þИ&(ò^ͯ€Å–)·ã~è9¨:ÿŒ0½ùnõþ›DÔ¶ñe1¨±óá†z‡GŠKõ¿2Pý²¸’ ¼ ÉâxŽŸ¦f§ËÌ|
+Äs•tnšÅ´k>¶ZQ¿j¤Yüéöí;±†ß%S™IEt÷—ï4hµW uI"u/Pz½"^è‚o(¿1 ©m¤Hv{ꮂ’Of?_GÑ©Ž"&œœJ‚!ã¬{1«.œº"¬pÊ!¾ ÆµCÈŽ âÇþÆ27/¤Ìߊ,Ð’´eëиà@ó{ã6âŠ{è î
+Vi–sZ9ñŒ„gÝ`«´|CØÓq5r™oøÑHAüùÍÚ ²"‡é羚ÚU«°+³[zEiYò!l4†©§•Uë~ÁËnÉS£ áSÏw¸ž1²”DPRÍ
+ô-d>
+BºLÈ|x9º,™O"(9î;4xU¢áhMBuý¡ë½Ý™¢ÇStß› Œ«ë‚‘IøÅr™C@¸‘{CØãË{ç µr]Øüø²ÀÈ— …$6dŸ·Žˆøüa2¬m ±^ˆ
+Iau¥ºŸ¿)„1t%¿Þ~Ù7…/—|9Àï¡kPd%¶ƒAFË&Á«gÆ&ìï4^Mùu]eQI¹À@ß(&Ÿ™¬o®f,Èž9kêzïPˆša¢.'²5Ò#"–ÆŸÈ ÷¸ÈÆLÂÍøekÉLG’ûhËùHðå`;ñ Ùbm'Ôsûçð.¦ñ¥8½<Ô¾RÀ3( 27 qØØb[¶C]xfã:ï¶07/€ª×ƃ⸸å"¶E!e
+òÌÍ$’…²,ÃÖˆ.Ýý†eò㻬Þ|úð~u ˆî€Ì”Œöƒuí¡«Àoˆ:) 9ŠŸ#Ï´‡Ðs¡òÏ„jœ!t_Ìøê™HC™´Y¢duÎdºä%Ë?šÅŠxXnž·T­¾DÄìð'~
+endstream endobj 1125 0 obj << /Type /Page /Parent 1658 0 R /Resources 1135 0 R /Contents 1136 0 R /Annots [ 1126 0 R 1127 0 R 1128 0 R 1129 0 R 1130 0 R 1131 0 R 1132 0 R 1133 0 R 1134 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 1126 0 obj << /Dest [ 1357 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 188 716 223 730 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1127 0 obj << /Dest [ 1120 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 392 560 433 574 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1128 0 obj << /Dest [ 1357 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 408 479 444 493 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1129 0 obj << /Dest [ 1459 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 196 363 237 377 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1130 0 obj << /Dest [ 748 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 385 322 406 336 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1131 0 obj << /Dest [ 1125 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 258 255 308 269 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1132 0 obj << /Dest [ 1451 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 382 255 424 269 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1133 0 obj << /Dest [ 1459 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 340 112 381 126 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1134 0 obj << /Dest [ 1451 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 345 72 386 86 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1135 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 1136 0 obj << /Length 2464 /Filter /FlateDecode >> stream
+H‰ÜWÛŽÛÈ}×W4ö%M@¢y)Ê ÃÁb½ñè%Ù -©%1¡H…¤FÈþCþ19UÕ$5šÙ±ñSà‹úVÝu=uøv1zµXD*T‹Í(ŒT€?ø™f‘?©šÍ?Í‚X-ö£W7M¦V Ÿ T³*G¯>܆jÛŒ?BœY&Ýð4Ò?›­Åe™òÿÍYh®f?Oqqœùi ÷²LÄâ4JR’þEÿ;Âê$
+¼Iû©2µ|P·e»ÃJ¤­‡›t“7êúÃXýí?Þ$Á¼Æ^â':÷½ÚÕí)o¿ØºàKbmʵ7™kõÿïý}ñç[ÌÖDŸb²x‡5Öàt:ù^쇺¡W-=‹«ñ¤aàGzå–v|ÓûÅ(T¹EIêÇi¨â™Ÿe* áI§™ªíh3z»è‡8<öÆàÝ€<¢Ù{Cd:ÿÍÒÀÇ¿ÁAïÀ õmÓصºÓ›ªV¤°nl}oëæÎSX1­bOÚ*[®UµÁܪRyU:±û-ÓEnË–ÛäLQT§Ö4Ç}^nU^¶¶®‡ÏÁU«Î±“ЇÆð§dEÖ)H#R°±´XãEoᙪÄ{!ùXdËm´»êت¦5´š Ê¡?Ó­Û,·ÞdªÅÄêÞ›PÄÜÍrЗ5¥>–dm¸‹: ÈúOïÞÞx“«jÉÛi¿½Vî<²'Ä‹ûîeµv'[÷;vOqºðÍ´º•ã•3?ysäŽ{ë×Å5´ÇêgšgúÃ{9´p2ª³yYÈBïàã˜s–"?ïk†äãý±ñXA­ÕÎÜ[Ž³9joŠ¥Êý榵jƒtŸ‘Óòa±Y…²a¡Û¿üÈÔùÛSvÈѶRrŸ%uSmÖ,}rà 4Ãí²°%‡ÆÚ¶Ôª’ÁZÊ[.½0biŸd§Õt@ ©XoÖ†³’žLõJ.Ÿãò”)·Å½ñR²XåqUGYï¢;Ï—§
+ƒ„ ’dä8ÞŽ‡†ÿ/(h84×øÓ8ŽÈ?Ó œõ5L{ˆD§¶6e³ÏÛ–=W­ ‚fûFy›’tªQç¢K^ò«{$Ú?¯÷NË.;Kï)x"ÊÎ<öè ŠÝTekò·æåkÆÛÙp­kX) Ð!¶ÿØÕW0ÃP`:}s©Õ„å£TУTàPêÆ
+fÜKʪ´ÜO^öAȱøE¿³s,Z±:{á éàì]m6üÄ…=gÍì¢lE·I7$SYºóB7<T›Â Ï¸IU¢n÷ð'j`s,T¾ábâMT”H!õBÒy|$ÍÛÕéÅÍN6¤Fgwž‡Öi_}Ü úkÜA×–îøÁDéØ%*‹YIŸS.li¨ "½¤ªtaÏÚ)JR6KÙl6Ö5pt©ƒÛ«ÛÜ
+8ã‰U4•—08÷ÏÚN†ƒ 51n¶m›—â˜à8¢:#°¦çµå+à„öù¦>”mW·µý×rjª½¥¾Iüa½Î[°
+žÍuÁm“Ö[÷K~ª7nzÕ í¬Xí„W´n^omë«÷r¸ñ¨}ì*ô̵»^ÁaD‡Em;]¤»9§ÑJéûìÙÆš9êèé¦6[ê{D ÂBéB‘ºK¥Åz~“ê!$ˆHQ¨ÒbÚV¤âœ›š6B!à_-xeÇWå²­º_£–Eµú'½¿ÌKS?tš>×%P“óH™z™C[6ùë«kõöÇOoù–¥i@r0
+E÷Ή\EÄSਹþÃdx~4}þ"|ÄXæ¨,¨wÅîäñÞÀQöGÌ?P€®µLà㇃å៚.
+ðà‹Üdpæ2âq*6„ÈÓoàñ<¦ÏªÿKn1”MèœtE±«+€8Âœ
+endstream endobj 1137 0 obj << /Type /Page /Parent 1658 0 R /Resources 1145 0 R /Contents 1146 0 R /Annots [ 1138 0 R 1139 0 R 1140 0 R 1141 0 R 1142 0 R 1143 0 R 1144 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 1138 0 obj << /Dest [ 748 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 71 743 94 757 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1139 0 obj << /Dest [ 1155 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 261 647 310 661 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1140 0 obj << /Dest [ 1165 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 314 647 364 661 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1141 0 obj << /Dest [ 1177 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 367 647 417 661 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1142 0 obj << /Dest [ 1184 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 437 647 487 661 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1143 0 obj << /Dest [ 475 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 306 255 313 269 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1144 0 obj << /Dest [ 475 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 416 172 424 186 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1145 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 1607 0 R /F4 1608 0 R /TT2 2027 0 R /TT8 1605 0 R /TT12 1611 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 1146 0 obj << /Length 3974 /Filter /FlateDecode >> stream
+H‰¼WÛ’ÛÆ­¼ò+&|ñ µ„p#¨ä­ŠÛå”JNJt¢M¥@p¸„M4
+÷¾7ó®5K¥5»R²úßPžZJñ5ð sR}ã ¾©v§ôì»ì½`õ+ÂVl–gs¨6S ˜h„>ž9¡/¹qÅ ýwòHmŠ2£!âA²ƒaµùÁä-jÐd˜p9Þvy(¡cƤZ~b6ÏìfuÊ~:ÑtxR³âí+ž:ìa}{uŸ!›†A³'Ö¤½ºc4µ(3í£¾‡¢qßaÊʇ©7ýU?pO(NË"g&_iË“æÞI¨!àŒW¶5|ôIsY»/èb5Úœeúäì1¡Ž.-,Cp&Ÿd4É/YƒÒ‚ŠO¨mrê/0ŸŒ0ÏÃ<·1@z?5ýøþÈ„’ŠWç¶N:ä
+g⦈ÆXò7wÔpz&MI$0E†˜º®jY"•ª<?×WJÀãZõrìLBß¡^J't½“Ì“jnÈÊÚpñÊøÈZöxæ}«¿:žÚ[—!¥„µßHŸrÝB=©%ùƒ×ß¿zõ1üšåY3ÏQóñˆçü'yŽ{– {Ÿ£7äæH­™¢Ðvrí=É~ì×0Vny¬"a½œku< ϵ|Em"·>ÃZv<Ñ@‚bm8x®däÔ« †à·)Ï<š4JHŸìo¤µ›ö2p%;2fGr3Ë|hÛŒnƒ_ú½GÃΣ¶ƒºð¨]³å†uaŸòe뼚ø¿QQ^õ^Ež>Æ,4^=‰Ù1$ g‘r?Å…T~¬y‘„
+»mqÈsº~)?ÒÑú v\ºArY°½x`Ǹ›R,›¨Æœ²šr€<ól4 Œ9Ð¥‘ãÛR˜8ÇÊ•Êkê*€…GR*½ÆèÀN
+"—2€X²-[=ôÐÅöP´§MN{X( %Á–“úßwæ=êÓë¤[,¶@€XI‘oÞ›™Wpæ)#yæŸ80Ë¡šå,9ùó®‘ÀlÛï¼°[ñ9‘=©­-=‡©ÝðïúÒ1Ʊ­üSk걸ýÏ;Õ£É~©ãWW 7a–×ä,y#K g4غ½§¾ÝÎÅã&‚GûѦû.z‹áJ‡“¹Ú•Œ«©ÊƒêIÎç³KwÆLÕdÙžÎD¦³äÆ=ù µ’XáçÐæ,ct;ríOWô| Åkƒ×à]¦!Ï´
+°õ[´%#ÇkŠÖÌ°dàj¼B,eùfªKaü¸UÚ÷¹ô“½40ø„³’P&êÁLH6„ró¢Gmlíï¸ÿ³+ÝÔ!Ô°W>ÀŸˆ¥n0F¯÷ì ä>ÿwI™Lúš Hd ¦.
+‡þfB“5˜`é.©ü\_p÷ný¾NÝA¯îœ0—¼n¾ìª“Ú•ƒ»™!Ö 4P˜NÞçe; –S+ñÀ"½HÀ’ªxûÀ·¿Òn‹ bë»Aש²Ó)Õü3•´tÝ‚°'ôàÜßn@¬cä7 0ÉAÊSÉ …çS¸‹Ì'4N± ß…¹k^B¶¶Èþ£‚!w^“Cvô•ßž3¾ƒÙ½~1˜ÙJíj£¤õé\¦ýi\ùœ—pè§FÎLzÑVmeó¢0ÉK’Écq“Ú((&­ö52ù1/ò欴©Í#ŒíÙ_RNôÞH Ô}Y™¢*Ÿ‘¯¨bÑV´‰ÙC “Ã5*¡÷t<ÐR”d?Q姳o•´LDmX+8ã|* ’¦}‹ø§.\ã
+\ä6hi• ¸«:ŠN¬ò€D‚Piƒ7Þ”ºŠ šd"²—n”<V/ìx
+™sɶ¡ÄjPY«®²VS¶RêÜ,=…TË@ΕR‰ìÜ'ž|¢¥ëóÒ®ùÓŽC½¹W-ðrcZ‚E3£Ò¯†\JF§öÍ#éO$J=»*š_Ä%¼Á¬ðÚËx‹pÝIU[ ™4wÑ{¿…Úþ½Fª%ÕHH!Xê5ˆÙ¿æÖÍ·rk8åÖ÷º
+6Úü‘”§äp6Á-óÓûé™YmpŠßäéƒx†¯d%}ãÿˆo8À—éß ~Ãwx|ãëøÆC|W½€uïbºRDéÎ(pýB
+¦pΞ˜—–ðÊ30Ö‰=¼|xÙØ|/CôÍ ŠþÙS™ÿÍb€Y™€º(øº/ˆµÇô«hv8¶º—²|ÀGÌǺB|ï1>°Úx›£K=TðCS:¥PË:ÓßóQ1ã Þ,FE#qûñ5³‡w?‰/J|õ¦Eb€?ÁóÃ|ˆ¡1¤ø¬ ÁíX(êY(òÝÊÇûŸþ
+endstream endobj 1147 0 obj << /Type /Page /Parent 1658 0 R /Resources 1153 0 R /Contents 1154 0 R /Annots [ 1148 0 R 1149 0 R 1150 0 R 1151 0 R 1152 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 1148 0 obj << /Dest [ 1269 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 126 650 156 664 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1149 0 obj << /Dest [ 1101 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 107 393 149 407 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1150 0 obj << /Dest [ 475 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 416 379 424 393 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1151 0 obj << /Dest [ 1184 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 184 337 234 351 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1152 0 obj << /Dest [ 1269 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 418 96 448 110 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1153 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 1607 0 R /F4 1608 0 R /TT2 2027 0 R /TT8 1605 0 R /TT12 1611 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 1154 0 obj << /Length 3995 /Filter /FlateDecode >> stream
+H‰ÄWÛŽÜÆE^ç+h;ïC*†¯.FÅ2²ã<D
+.§w‡‡$†äή>$ÿÌCNU5‡s‘í$°lh!rÈîbWªS§®W³g«U |µº›ùòð—( Ü(òµÌb7I½P­¶³g/ºT¯ñTWÔ³g_ßøê¾›y®çùXSÌãí~¦¿ËïòCO9«Ì2Þ”©¥çf ‡©›xb—÷¼îâ„v¿Óÿ
+ðtxÎÂÝD{©º}R7Ou¿Á“@–tWvꫯ¯Ô_ÿí,büÞá]ìƺt<]l®Ô;ì?š]ÅFB×kg‘iõOþßùÛê›{ÌÞž›àÇê%žñ öû½ë„®¯;úª¡ÏÂ4>éâÖs]ØG¶ôj5óU©fAœ¸aâ«p馩J|DRÁÇ(U;3»›]¯Á},ñN£1E×£ˆhŽÞ³×¥©rg®gÒ¿‘Å‘,Æ7­CÖÊ„´ëEöÍܾI{|y#PÆ|ˆ¡.ûrkþ>t§†b7ˆmÌ°Ú›rÀ %†sõ\Ýä[£òN¬tWêvèUY«mYìšØiS4õºLî.–®Ÿ¨…ïK‹K!àüüžzÙÁÓuÞ㌧v"7ñ'GãÉѲîͽÙgg©ÛéLÝç}ÙÔª¹S¹"»‹¦®žÔC^ FQ
+Ý}‹ÃZµU^˜MS­ìòêwðȈ!1{%»•ü2Ð 7|ï¸jÅ6ˆ‚Z:.SLŒD _ÎÚ³# ¯ÓJRI¡˜Ëã=žÐRU7½ý`+Ö‹ò(keÛª,ʾr¸³?ÑGCíª?±ùÒÚ¨"¯
+D,Ñ»;ÑANœpô'ESÕìÔv¨ú²­ µF¦©ªf_Ö÷ê®ÊïU±¡ƒ¤ à¼èÍŽ¾ óOËf}®ÊÏH?ˆïÉÏ ££ò¼ÌÞùóËLSS†Õ°F¼)iöȾåˆv­l²Iäó5…‚4Öh–v?Y•uÍ–?3Õl6nUï5Ø켈Y3äúï~CuË_•ºôÜe|Ö]—“gKñÌ<šbèé\tú\ݼzƒ£Äú•“ê+tÈHssP§!GÙl¡
+¦|›ÒxGçØ!pT[û®º~óöÚY ŽÊ>ß!ÿÚø³¢!²Èþ|êGWt$eZþ[ë±Xï74DFÈ‹ƾìᢸ<—u-G¤çb
+ÁqRêåòóž²9Óï+NgòÿBhT‰?ÀÙUƒ;fd" Qä0)/T)¯0ÂÜoÆÛü&
+ùxéÞT•«^³¬'Y·´™¥h³TˆQpÞÎYÙ8È•ó?±áÝæ|#‚'s‘#[³8R†“î¿lz#Ó ¾ZlòÚNÛßÛ!¤áAÂ.i«áÔ:Ž)'‘Ñ Ç›+’uIì„Bx]¿a“o¯ù‘ */ ´™^8Tˆ[˜CØŽÏAeŽL> 8H{k.´¯d«ÈVöPß¼ü£Z7EÇŠˆ«÷í¦wJv*ñp’{!—ô½¦ ´ÛS]Qc]vD
+¹ª¿…Ô&¡ëÓ>ï°/"¿xß;d?äs,L; 'wÚ„Å-bNjæ­Y·ÉùUSƒ9rNGxeÃ{ qB•í–Â$èÄþã3yD†äI¾m­ yº¢—2±†vјûãèŠnœ
+¦ºbW¶ý—ÔæqT)ü0s!é³t[Ñï`ŸŠ›:KbÌ\ÝÐðÈå'jegr™{RÇõ‡Ž÷{a¹ûëU×Û¶ßû+Šœ‚X%YAÑ—¶h ¤m
+û¥H€‚¢h‹)²ü€"ÿúÎì.E‰RlíƒaI¼;ÞîÎÎÎÐfn(M/tÅUKáË8•àÑÌU\7EÕ—SžðÕÚbÙ×÷ƒ*yŠýY°˜=; Þ©¦¦\^ØÌ”€ãYr·­Ðáç$k²a@‘ævM¶nÇJ®HE6x¢VVÚî+b’Õñ§2Ò.$o¹qr j®TµØŽã YÜöˆ÷Ò
+Ó}x`ÕY4B"D“û<Ùˆª]“eã²Ìá;öÛ˜ƒ2JÌiJ"GW0D—®ªK`½Òd¨ªê¨Ï°>}¦*k›«B P覩Gî÷¼Ž¯å€ëPû‚*wö¿@yÅ
+ɵ'hÁ„»œ€iÞá–Èë«ßɴУ_"¨›ÙXžQ& ßú¹ üQ Ýús`Œj k+‹È¦½Òvd862Œò´É>\QbM>‚îþ|G_$)E V¾ÁÊÌO.Ƀ@Aˆ7lþˆ8@ä@ÞÒ7î8yõË_Áx& _]§‡Uw«•ÞŠog°ªg`JƒÖðæÖc³dÍ9·tµ‘Q½¨ÚžøÓc@[ŸbÔÏÖœi]”ï©íÎBgÖʧÀu›YœPMþIg›­[r¶]Q3¤«Ù¦°›® ü .›‡mÇ0—'pšvùœyŒ9™`Ž‰)IÊØB]_v9ÅÆ&~1…æ¾ÁNk$Ù q5@¹“‡cת7$b*Ön—×<JAˆ›­Î1x¡2jéØftUq´ÿýßXÆDœübêÀDE.›õ\":Ô[Ä[¿‘ ïEGè»b“îôB"|…ü2ÕÈS”ë[6¤Ò,ļHÌ÷¯¿ìµÎÝ•Š_åC}@v–GÀ¶”ZóÈ}S5¶†ÑË£HrõAI=ñêÚX.€ò×hõ£¹ëΓ¯±<â¹Zt¦C—£øvŠƒ*y¼àå1v“Šxa`eœ‘l yEY Iž8ˤv6EˆXÈ #•1‡ƒ eu¼éeBXIn"8IÝ„×õ2Ð̼bÑ5+t­õ›YŒy»X­|æìFÖŠ*L&Z¼nXeà‹s—¹;ì¢ßä—wüúãë]ÈhÔ(.DUê
+ÁÝᣩakì`éd‹è|p*‡Z«ƒÑbñ‚‡ôïÓæÕ-\çe\½q½X£ñâ+ò…::!’™ïŒâ
+¼º‹ñ¨-Ų}ïâ²r¹Ÿî¿ùw
+endstream endobj 1155 0 obj << /Type /Page /Parent 1659 0 R /Resources 1163 0 R /Contents 1164 0 R /Annots [ 1156 0 R 1157 0 R 1158 0 R 1159 0 R 1160 0 R 1161 0 R 1162 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 1156 0 obj << /Dest [ 1451 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 149 742 190 756 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1157 0 obj << /Dest [ 1459 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 338 475 380 489 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1158 0 obj << /Dest [ 1125 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 400 381 441 395 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1159 0 obj << /Dest [ 1459 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 404 327 445 341 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1160 0 obj << /Dest [ 1276 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 71 178 102 192 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1161 0 obj << /Dest [ 1184 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 386 133 435 147 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1162 0 obj << /Dest [ 1269 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 248 85 277 99 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1163 0 obj << /ProcSet [ /PDF /Text ] /Font << /F1 1609 0 R /F2 1607 0 R /F4 1608 0 R /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R /TT12 1611 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 1164 0 obj << /Length 3348 /Filter /FlateDecode >> stream
+H‰¬WÛnÛÊE_õ? шæý$’Ø9Ç…sœ }h\PÔÈb!‘ªHYQ>¤ÿÐìC÷ex•h§@aƒ"ç¶÷^{íË|˜M.g3GØb¶œØŽ°à~¼È1=Ï
+DûfY®˜m&—ËH¤%­±D™æ“Ë_îmñXN,Ó²lX“N¦õëa"¿$JØ®-ŒÙ?&1mŠEh™q
+jxÀF§aBÊth ÔFLc3&á€qƒ°EÚ8µ6Œë´ ìõ÷d³]+ñ —™Z/6€ÈĘ"À[²-› þ’uVV
+,gÃѸ·‹¼JÒêâ]@èÙ"T\pن΋³~ÐÌzŽ™«;ï[‘éx£Û_˜nd[fäéYÛ3}g¸ûü<D}»¶Éƒ7ˆ—‚—™Ê¡Ïß^|¾»ºùts}uÁƒ;µT;•§ª|{ñõú¯Ì‚8hõÆ”9°kl¾¯¨ï“f¡«kͪãô©²*+à¯VhS,`t§¿Êì|Y—CgPL¯ ¾¯ØÈüd~d†Ád·ïïg¿½ÿ|}²ßþÀÉÔLŸÏ(72?‚šçCFé£VBâÊûz˜îÐ|¹3®×Èüh.do ´O7_ŸAfO`ó ·Ïmd~6¨ƒqðÍùϹsd~6Ë3½Ñðœ]ßþñëÈhüÑèé |Î¥#óç1ócL4ÿ+f®}3 jÖs¾™?™†°z ³/¿ÞÍîÎ`¦ÇûŠùQH5m °±ùÀ·i²4^XÁÚ
+êÖ]‰œ¯‹y¿.B;ÌsßdÞí
+Î}JÖ{5¸¸ âsâF«˜µºç#Q1õ½â;«¼¤žÓ^xÃkc`Ÿ\¿I °1E«áEÒÒèuâ›dáfæÊï p¶:ñhíÄGØA—èO¸”ÖDü´É^Ñ—ì ñ ™ùÏ Í¤ü&¯Ô2Ù¯+†4zFÄ´•Q¿PšÐN§klŸhÃ§Õ «½àÖ¼¡P\B`€w«•Ò‰Èð1ÓP6Uò(2‹l§Ò
+¯y‹7PtDr(1}ðKK}› € ¡|„8çc0ž9 1,‹ŒW°¯–¡…ó™di=CPYsÖ6&lŸmRßU
+¾\ˆ‚ô‹ eŠ¹ÂôäÊ°)ù¤4›lØ–¼?æég½¥UBå1ù˜Ž¬ W&¸¥âoÈÄ©2"É_üÌ _@<P§fš`1I¾i¢ÕeÄ^Ï.DUˆ…J×$@uM¤³†Dh"ž‡Ž¢Ò
+AŒúRºA9kÞŽH m|ýÒ‰ø„p %{;$š2Úxtü\ flJëtʱ’`Ó‚ë*""ç±¾ä6 ÆœC‹Ÿ­¨YžRyêÕ´-ƘÔe_7 ¦ù‘Ó(µýOàH3Ù{Ó}ž+µ
+r+šUҺϗäŠQÓÝË´5GS^Cf®ônÝà«€ÌCµ
+endstream endobj 1165 0 obj << /Type /Page /Parent 1659 0 R /Resources 1175 0 R /Contents 1176 0 R /Annots [ 1166 0 R 1167 0 R 1168 0 R 1169 0 R 1170 0 R 1171 0 R 1172 0 R 1173 0 R 1174 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 1166 0 obj << /Dest [ 1269 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 278 743 308 757 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1167 0 obj << /Dest [ 1269 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 272 681 302 695 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1168 0 obj << /Dest [ 1269 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 494 549 523 563 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1169 0 obj << /Dest [ 1339 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 219 514 247 528 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1170 0 obj << /Dest [ 1459 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 350 400 391 414 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1171 0 obj << /Dest [ 748 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 362 292 384 306 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1172 0 obj << /Dest [ 1459 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 261 265 302 279 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1173 0 obj << /Dest [ 1125 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 131 170 181 184 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1174 0 obj << /Dest [ 1155 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 472 130 521 144 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1175 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R /TT12 1611 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 1176 0 obj << /Length 3502 /Filter /FlateDecode >> stream
+H‰ŒWÛrÛÈ}çWÌ‹k)w•JU¾È^'.Û‘¸å$V ’@
+7MUìÓ
+w SÕ•‹ëÅËÕäŒÀÇï©7vÞõÈ#š½‡¥û1=™ÄΙ¹2ð­+}COt‘ÞYÆpáàà{¬»ª¹QÎœeˆ—ÏÎ2Á ÿdÝ'ƒ¥ù™¾Ô—Î1_m‡‘Ô5‰8‰¢ËQ^Ú£Ôy9l;'Æžc(BNÇ«\5åƒúgù¨.õ¸¥?í M{zvO»]Ý:dP‘×ï^ïYÇ»Šoí1ŧ'Z{é8ËÔMàADK_·BíË]Ž‹ê~(;µÆc
+Ç"ŽSL'õGõ–„M "ÅO´(ì`Ç8¾,ŒÊ~(æÊjUž\‡‚
+¸“µ3Z=(IщÝò+’**HMì8 '?¬9µ“Ë­2CÇ‚`/ΩÜ/É„»6:‘=8y¤hR¤ùšP²Ð24²÷LTEX[°JîaðàÌCà€„m7°’îU~“³Œ@JÁ•È<«F;ÑDŒÐŠ-Œžçôò¾läðß>$¯_¬xâYmØ¥¹êGEKv‘DìÎ.¨†_ì¾ê$§qú?·+OÕºDF¯ÇÕ»u²ä.ä\ígG6>ÛÝC”ê¦Ô;½n®Cåh¹þ]™N k7»8[‘ÈrÊyóŽ2Y¨åueÚ+Û9½ÄHkýÀ©ÏáÚ4óRC´Ÿ¥T8Ç*Š¡¡”߈ _'×ø©êÁ!}))Ç ¯í¶ãRÜ Ô7;qùZÚú]øè¹ÁœŽ‰ÀÕ¤½à±úøšÒv _:„ÆW¤~\©ÛÒuÅ÷ŽÑ\!…¶”£*Žpj)R-ºð;°Á‚²`*GšH™!WŸ=;gµ=SEoe1ÄyŒ/hf{ówZ*•@ùmÓ•}_µ Êâ4!ˆ7(-aIÞ«MÞ ª½fÜ?{ñF¡SÏ>¿Q›:/ÊÛ¶^—]2¶´ÀÜø»à12ÞøôÄÂnCþþãÛ‹ß^^¬& ÔíÍP~ ÔêYº J(­¨ñ¢d§‘\"o
+>š6VjröØFÎÚ 0š7&éÓ~†RÜÍÀ$jîoÃA[ãg3€Œ¦™úÙ[í!±dïÁðT\(Þ§yÇ]
+宺"Í!˜2¤8ï¢:ÕwtÄɃ´C ÙÖà+·=õ)•[óV)1ÓÙ¥# È–§]Ù—¡¶TÒ±Å÷|#-Nk‘ºaqŸ÷3ù@6£ds§Dþ¤š‰kXV…S=ª\Pi¢ìkâ¹Y R†9Ð’rVÁÑäüËêÝ€ÿ@¯©‡¡“¬÷EW9)l¥Ãé±Ú´ý°Üt-¨í|jÞPåå%_óuP&`ŸDÛ¹\ŸN²é†AÀW YF¬Ä,"ýåxKÿNp¦«y³xvµ©N”`¾jjϸó9fÌ%;À
+â@ÿx†¸x§àR"œj u,ëõ]¾9U”èz'¸—oeôhú•¡G f¾þèPiôš4-ЯXÏŽ¸ J†b±äÙ¼gŸ&ƪ÷yåPêüŠuºfh•Çß¿ªuw0µr"¿HÍŸÎ)­ú£º£ i=›û}›‚-²Oìhsac`ÝýÀý¼G0»W_PjC~®¨GD™õc¿X²ÉÑŒ‹‚«[2ƒäÜ µ5&þ ¤ÕeÆ&÷àèÆþÜ™±eÕ,T#ìɬ›™ù|3*¥š5‘Añ2wpcXm²}åŠçX°EÁ
+U67UcƒŠvÐE} é2ýRŽµ8㬠`ÛËwqKIõ)PvhÙÏvÄïž÷Î6’Ý1ê‹ížêš!
+98]i·öΗ<õšù„ÖO”µ°Z§jíøPBÿJ#ÔÙ—æNçk+ùúßó&ZœxjÛü$Ó[ 9`wÅŠüÖèB}ë£ðo¡ÁþúAÈsæ)Þ{ä¹EN‘0ûù÷²Q rŽœ>æ]¸wa´^$ex7•z%S‰óšF_­Êºhÿþ˜s
+ÉÕî5ÈØÃ,´|ç;RÿÜ.gâ:šU±/¸i¥8™+Ñ©%:·Dç–h;ÂLKÆ2g9Á—oF® t*/öŒyÊH\Eżú'öÚ&bõÑ/¡ ²õÁ–k}Çí°ûŽUJå†Ò@j¡>,E’éªå©M¥ÛÞWø"àJXK0«y=ƒ
+'ðiªhePìt˜«ª“(˜J/ƒI¢Ñ†P1´x€­µg¡lPИÁpö¾7uE†þ¶Æ` ¸:pàÏk5}KÐY(6¿ØT¨²hxc¦ªÚw @.ÆêÿšŒô¦8°Ô’;ÉÛB¥÷Q¾Ÿ™±dì—ÏjUÐ0‚<è$ôG÷ùçµf3ÕÒlÚ^6ÔRX2d²Þ@#Ûï£ìíè"Øï$Ìjª
+’šTÇ\—ÃJ»µwÜfFÙ³ýHЗmï¼P>qÓ]úXŒw¥»Ç  pâuò×ÂLŠ”Á¾ëÕMÑ>¬ŵ(û Î@ví ôFÏy‘Óc„¥GFÃô2?T}Ï åkKwu–xÛU©I=5™¥fx×lb%F”ân‚ú`—§y ìp7;Á©ÌÅû=!ˆ3!›·ªM¼â›–˜Þû{óŸFÙ¬½øXA­4‘j1©vhJ(e[šÛu?z[ÿš3§±sD"%Ñ€<Û}~¿Ÿ¾ý`
+endstream endobj 1177 0 obj << /Type /Page /Parent 1659 0 R /Resources 1182 0 R /Contents 1183 0 R /Annots [ 1178 0 R 1179 0 R 1180 0 R 1181 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 1178 0 obj << /Dest [ 1459 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 350 706 391 720 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1179 0 obj << /Dest [ 1125 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 131 530 181 544 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1180 0 obj << /Dest [ 1155 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 71 463 121 477 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1181 0 obj << /Dest [ 1459 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 275 436 316 450 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1182 0 obj << /ProcSet [ /PDF /Text ] /Font << /F1 1609 0 R /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R /TT12 1611 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 1183 0 obj << /Length 2998 /Filter /FlateDecode >> stream
+H‰”WënÛÈþ¯§˜õ¯!`ÒÞDe½'›u±m‚X@»µEb%´eïƒôúŽýÑs^DQR‹ɹË÷}ç̇ùäj>÷„óÕDyÂ…ðÄžn$¦³Ð‰b×óõä궊EZÑWTi1¹ú|¯Ä÷jâ:®«`N:±›ÇÝD~M¾k¡|_XóNf´h&¦®3‹`c?v"—÷¥5-ǧ0ÂÕòß|µ=ײ•ïDÒÅâMÜ¿õ|ñ¤¶`'Ye•xÿùRüý?–Â{ c¡ÊÌreút)îwYý‡.sÚÄ—I±´ì™ÿ¢¿Öïó?MÈcòÆs^æáY°ÛíËw”¬ðTÇÂÖp¤®ãÉÔ|z¢>Í'Jdbâ…‘ãGJøS'ŽE¤ ’| bQêÉjòaÞÃW0ÅÝF]#")z£Àä(pÜ
+™kC¹­oÞ‰m©·4;)µØ™ñ:+¾ X@kœíIQoL<À÷=aƒÛ®šb4Ù’ˆ¼Y&u²H*Ýx‘׺âÎ/î¹¢|(NÎí¦¨“¬ÐK‘ï(S“ÑБ3m2Š £¡c×++p™é|¹N¶7bSZ!ä[\ƒkJ&–íÁã¿æÓÛx”–Bl|±<'–?Xty+6Eþv)*zÓ
+öT=FE™ ÂÎm°€Ù d]—Ùâ芯ŠüjŸówJô+6Åzc‘f>Èz•<ç5‡4>—_>£yÚái c”!<r
+Z‹•EQÙ‚J
+ƬÑ08“·#MñË*(øMÒúüàmñX»çCàΉƋZÞܲ±êÖæwa~+íFät&U—IÕdÒÂZ¾A¥®èEðOŠ¾ø ¶"¬,œí%›fîž4k‘>ó·ÚÂlÖ”f$Û‹™Âüèo°ÐõNS)Ó|\Áë@"{r`C¢=®3“!k˜ÙP.–*7Lð¢6'Œ‚¦‡ð4t¡E¶Þæzg‚´À6KG`„b¹!±Ë Áê’¬[A``2à”&2ì
+­—hšâhÆrËÅ"Í’œ‚ba)2‘ä¼Ï È_5àq»*=J蜞Är:=¨£X=©ìQÑCà™ŽÃQ€
+ÍMND¾®Š˜ÑTӲͦ
+¿AEȨðP†èwj†Í#ƈœ¨_xUÐöÆ¡
+H@ER¼5–#œ$R쀿†ªðZbÉTPSh–#Þ”®‹üC¯J(T¤!›$´#¶^oë7.´¶Ø·ÙXwÂr¸?áTç0&£Lí€2í€2í˜ S%«kˆy¾ÙáÙ؆ms‹®“š´ænvü
+ð‚P,ÿ0ÉŠRMZ Ê¹ëcZú `sš×»‘ø²×x_kÞŸ«.Ð9›§¥^Áõ°yK7ë5Ô¼5]V5L€‰/`r4pu|øŒ§Pݘø W’ DÈ>΀mû`B¬èý9¶}3´Šn›…ËLJG-o™ãvÌ‘,ãdÄÍõ·ßÏß? {& Ê40R©}ŽŒ‡ÏuÙ
+oÖÊÕUË =žè¤eo]ö¨‡Ý{»úÞæùi“w<~.;¸à Çñ¯ßî¾|»›ÿÖ!z˜÷4zƇϸzŒû÷óowù<´éóO›ÿ80ƛđᓀð¦=@üþûÍõUûXðâÓX86Þ,ŸžŒä‘áÓƇý@î«'-Ç$npybî1/RÖ¼a·×/8ƒtE§Ó5>|Zy¼€òe\6÷8íŒÂgŒ@è+‹¾Ñ{èo¢QõC1ô+<„#ãg<óû¢0Œl£ Æáã¢é§a6>|f^3y=øcÊéù§“?>|F8T?ùw?7QxÜÏûO?}úó×ùop] ‚¿ÿØ|Þ§ä¥}.Úº»-¡IÌê·a¬½3@82~ÆK·„O¿Þj‡ã_üúå¯#ûªNãc|ø´ÅjÖסÿÕâ_î>ÿ2f²:0Ù= ¥ñá3&Ç}(õMníð†v¨Ùét?I.5í²ÍhH°}^©ødþŽ 7‹§ÿ-½ZV†àïx’nšÆŠàÝø ‚ˆ{iý5¶dÓ}U¼ä2„ÌîÎîlÔL
+°Î¿Â‰üσòÆôŸ°L(­ÇÔ‚ %p}TƒÇºÌ„Ï~Hq&Ÿéï·­ú‚¥/$ÄJ–x¿ÿb¸v|¾x]{<¬kÏ%í­Zò³¬ JCnLÀ¢àŸèK߉8šÓ,kN¯.L‹_ÆõÖI„£ç —™‡uÂ4«9¾­Ì7:!\jpr-yøŒÒñt>ìö„T£VGÕVÙ .º í–¼IB“Þˆ'ö¬Âzç×!ƒ'öüeß­‘x
+ð àþ-Ë
+endstream endobj 1184 0 obj << /Type /Page /Parent 1659 0 R /Resources 1191 0 R /Contents 1192 0 R /Annots [ 1185 0 R 1186 0 R 1187 0 R 1188 0 R 1189 0 R 1190 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 1185 0 obj << /Dest [ 1184 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 129 580 179 594 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1186 0 obj << /Dest [ 1125 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 331 566 380 580 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1187 0 obj << /Dest [ 1177 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 427 526 476 540 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1188 0 obj << /Dest [ 1125 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 266 377 316 391 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1189 0 obj << /Dest [ 1155 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 71 242 121 256 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1190 0 obj << /Dest [ 1459 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 350 160 391 174 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1191 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R /TT12 1611 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 1192 0 obj << /Length 2899 /Filter /FlateDecode >> stream
+H‰ŒWÛnã8}÷WóÒ«EÉ’å h /‹ÁìK7¦½/;ÙZ¢cmd)ä8™ÙØ܇=UEIŽãI7¦'&)^Š§N*~ZÏޯבÒj½éH…ø?‹,
+‹0UËU¤Y«õ~öþs—©¼ã9¡êòzöþ×ïZÝu³0C9ùl>43³JÇ å¯ÿ=[ñ¢•Z†Á*ÅÆq¤¡ìËk"^N­$¥Õxÿ0:B®ã õÂLmžÕ÷çºßa$ò¬¼®ìÔÇ_¯Ô?ÿçÏô[|K‚Ä+ýÐËwWêû±ìÿ´mśĞ© ¾òÔø¯ÿ¯õßg|c¾M):ë/c ŽÇcàÇö::ÕÒ±ØGh†AäånhÇ;ým=ÓªT³(Iƒ8Õ*^Y¦R $î¸ÈTkgÛÙ§õF¬1%|‰Æ„nHˆxŒÞ䙿eøŸ—Èøqáü¸Â¦/‚8ΖJºãBQv‚|Ñ9xZ1þ­ü,XzAÂ?êf[Ö¦*;Óû0Õ+›ºË[4—^ùиVÛÒÏ<šñ§•Ç–FúÞÖªìelïÏ ¬Nð†™Ù9ÝÛÒ•’èÄ6=B‹G>7uoÊÚª¬¯Ù KoÜÖ±8.δoÇõp[{óða
+Ä"+&/Fòå¯hâ7ŸIö;î}U¶íÈ ª©«çsH¹IL{‰ƒ³Ej1Œ¦V¹@)(&o È{Ä'Fu }Š%ðô|á=ô¯Œx‰¦Ø°mXÉ.ßób3ìSïÏ3 *Ƥ?pixâÒÂôæ¥/uêÃÁ*öãÆt°±lÏý¬¢ÓùÑhh$†Š‰>bÚ{‚}`Ü듬Î`×é fIžˆ‹¶ÜzÛQWó}r?ºï’… njû6oç§Äýb·æPõiö#ÿÊC‹HÚ€“ì!:r:(~IQ‘žðeì†óS×;5þÕ=ؼ4Õ°{¿3ì|€€Ï¹©*·¡q\V‡ž­ Õlƹ{èl«ÈéÊä9|²„?ç–]g»+Õ–w»^m îà§û²mZË:Vv»¹“üé&#b#xLq?fà9E~ë/ÀIdx ¢§nÝg‹ìæ43Hoý@ýæ“S{ÈEJ·áIÕš:‡l¦žU[ÈÈÊkXFdkeÔÜz†hR´cÞR*³˜ö CBW'*k6@ìƒAeíL’%=èö><œ^zòÑ$Ka"—ÆÞ¶¸õ¯*êRnÉ•õ—žéhÌ’/ÈVRuCZÚá|ØÌ D/@Ñ·¾ÚùL?C¾ `Ô/O¿¨m哇Ìé[Ùwjßg½ò¨'¾¹
+^ñnJ–gî陟jÏo€fåÕpAðM¾#/Á¢Ô}Nn¾âKÒRaAê1[wFƈ}Všn
+ßWjã92½I•Wêõ–¬9¸v²C\ïm¾3µëíI š£û.ä}!… ô..ç~Ì ÿ¸ïÁ•é‘ÒJUe}/;]]‹'³¨¬:î +…eYç;¬ï^qä/’ÙСÝÚÖÖ 2ÈÛ€ -‰ÛN2êMwOã…êêp²Zxœ»±zz˜qnYغ/! ­ã?'LŠ‰üöe 2\yу‹Éƒ ‰òIê,
+†(î€5í5ùj&CL¯AÍîq`ƒ@£À$ÚÐXßÌ™`ÞÈJ§eutkeë¼øÚ"x´ÛÞ<+`E~¹¯›c zúš7u2¸âøA_ýôþÌ+>à\øN*+pƒõEŽ¤dƒ2ÍýnÜ/¨tëmlÎÝŒJ ž>»Í^j«\ÎôCuAU·r[Õ>)uo‡ÓBü<qaç¢ÎPèd^QX‰¡B!ßÄÃpïF[UÒh-Ŧâ—'ÃiÀšÐÔ﨤ÍUÚæŽÚ‘×süù’Ê"Po R ýl
+ùÄrû,|&È=¹…€{%ÜCÆ[²«Êx¬ýR0­¸6 ®hI².ÑÉE$å'SÍ`‚ìÛó%eŠd²A8·Y—ì^$ºÑ(ó|E*uQ—²I—ÜËɉ™ËÉ ¥S»&•²Â®ŽÞ¹áGS¬æË ÅÊ{çЪ=2Wï—KÄ `\îúVúÒ‘¿%á!K Ž–²†bž”ûX¢n•Òí]zçjòž¸ÕQuÃðAYI±"ÏÕ¬¶ÒÌPÐêkÍ‹ñúH–ÅÕÅb£”¹|ü`
+©ü¦Šë¹œ%†øö“õ;W
+†lZ
+ú -Î d(hgÃåÿ†Œ`Ð\¶Sa—›Ú•9®^ÌÜÛ!—]tÌÏ'ø¢*÷%es§àf߀AD~1÷‡N„˜j>¿·û¦} GÖÈãÎ ;$ÖN®(_ ßÖ¸.amúËŠæJq×B¦Þ
+
+’›o<ŽfKòe,+TîÙªÝã
+Œ‘fdTXiªí}’Q,—FÉêNYÀ‘Ö¤tµN#æxzáñÒc,#_èÍÛ¶å#)VqhGáz¦€–¤BOÇKuètE—ó·à§“TÞ[*iWÆ˳ʕÏN*ácÉýt…›íHX<ç8Ê BÛåðqŸ9ÜËÕvèo$âË¥ôôÈÔÉPJ§à=å‡ÌQÔÎ0úâ"‰šˆ¤DÂhgdÄ=»¨éœ8 d –‹d¢Ë!'Ž"0µÇ%fN< :ûÁÛ§^ñ¨œì÷êÉÑ=ØÕo®¶‡:ç+¨Âv
+=sº†p
+endstream endobj 1193 0 obj << /Type /Page /Parent 1659 0 R /Resources 1198 0 R /Contents 1199 0 R /Annots [ 1194 0 R 1195 0 R 1196 0 R 1197 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 1194 0 obj << /Dest [ 1125 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 410 757 451 771 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1195 0 obj << /Dest [ 1155 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 293 676 342 690 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1196 0 obj << /Dest [ 1155 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 457 649 506 663 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1197 0 obj << /Dest [ 1200 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 407 364 443 378 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1198 0 obj << /ProcSet [ /PDF /Text ] /Font << /F1 1609 0 R /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R /TT12 1611 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 1199 0 obj << /Length 2988 /Filter /FlateDecode >> stream
+H‰¤WÛrÛÈ}çWLô4¨"!ܺdUÉ^ï–ÛÙZ±r³ò
+øaƒy¥¦FmH~3¶ÕjV+ù¬Òti/ö—ô÷—ôå’ÅÂäÕf¥¶5Ý(ÑdLˆìÜ@§ T}«P>°ŠÏÇ]øРÓ19=RÝÂA¾|$z*c9;¡1ìsóÔv"¬LÆ6Î…÷Wíª[¸?_à¾|ÛD«+@­¥ï‚.‘iÆyÊ·+dXÍ @Æ]¹‡gz€¾§€ûýŸh?9\y,dP¯×ùîš?iÇP•{´ùbÑP ô1Ö&Ÿ•Õ½ª×j».[z= íxoÃX´Æ‘jíÄÚ5=f0RÌx‚üš jV0§YúË ‰Þ]«’ÐÞ/.êåjaZãª%ÀÖ Ic©¬13‚k¬•œKÉøLežrÚMø•¥´›2lÔj³œ^ÙétN¡ºY˜¥©Z2* `çøŠt>Cb¹ùãç7xzó×2º¯¿üôá/wúÎö4¾AúÏämYWC%‡I2Ò3Jô 5Ý
+µ€ÎE¼Q„p@"º)9¦ˆâ¾2ìíš=XÆ×S«žÅsêïãå´ L®Ððö•C)Íå¼,Ô|#–)„­¼p.O‘˧B\`j˜>ÖnÐÅ4
+*Î~ѨP-ê¯æEÐ 7 ³TÉgWéâqâ¦ÁUðzªàYH‘üÀÉ85›•Y“yš¶æ¡5ÌDóôõ
+“i)õªÏxAŽ)^¾6v„#ð{IHJéþ£®zãf§Ô©W;¥Û¨íwZ[¥ß$¡7L(R}ê3Ü<Ùû,ØçàÀæà«å˜B˜ÿfŸ×8 9TWÅ¢¤'|ðPÕ^Ÿ*>b=ºG{lBã=‰Mš¢jüŠª÷âì+»³¹îÜþLvÐË–ÐúªoˆÏEºm2Ôþ†>}MÂé>¯ç<««œª/$žÜñ€W&è}X˽o LeÎÌš@ £šÖ2È)$Ö2¨r;ˆLóÍî"* J™“viv#C\{ J„1Æ&8J»­• –Ç’˜”Xè¦!œPc÷…LoFçÒ±·'Wž%WûhY› $ž¼íÊU6NN 0
+îMIim–ukÔÊa¸æHX°v§ò·nPøBœø8W}®›VÕÓoe½iÀ–Fc¢ìœ Jɳ´+¦ï©Œo©LN·)´P™^(,‰YkûÕ8D1I´,C„ÙÆv¼ûª(*ì±ösg·ÜÊ"ûYòüüI5ìoT™¨‘Oa>0Ü‘F¤ìªf¸’…e—o¶²1}¬ÌŒx¶\é‘6€cº²±Ó¯–…Kà Ï÷ô¶o ™·6èäCòÙ²}wzY[Ü- u‡ß©…£(Ñ#4i
+ùGÙ4=Ô/d&Ž´]²@Õ@@V³îL»¥…ªc'Á>mœœõÁsÆýç²} †öãkÉLsÆPrSb3S&î }=t¸°ŒÜäLO¹;l/@jIW™¼è²ŒT\>Ûœe¶DÝùJö³Peø±™vì^Ö›•ÈƒIŸxË·ŽL^èŸó«ÎÛÄŠ0Ë–$#Ò+H+õ±,¥î‚‰ÝsÝ"•ÁxŒs;­}énÎ8ç,9Ü+¿gV¾-Ÿ7L«cr1,²ã282CË
+ù¨ê6e—n€y¹9\¡(Ýøj>îóŒ)bþ†ã^߯¨‰”¹ÊÇ[âÓò}DÛUŽZÌd òPm,‘.„H?pÁ&N$2bi·'.aÊÏÕæM53r$oÅÅÏvU.cÐ%:в}¯ˆÖô8X‚½Â(¼-Ñ[¡ŒåÈ$è /åG4
+ô! ¦Î]iªë% ¿ÝUÅgøäP×·(Uô%²qsyEô§†)Ž(n‚.näÔ.¦HNÍþ—q–eK>y‚CpâÆú²µ’åqçüïÊs»Üþù*’ARÝ™{ÌæNÅÜ)á¹9ï( ó »KmI¬†\ØS=ÞÅñcmÕÙOz´Ð!&H“æúÈûÜzÒ‚Ûˆ¡Y7í^ÈäQŸÉ-—IæõmŽõ%Ç6ªl‘“ŽË&ÚúÞÀk=¯×Ke—+é 8]جŽ³¸+êt8…sæþBtˆ‹ÿ~»–eQaëÀ‰6?ÈÎà€–@<3sÛ*XÜ«æåýfS‹¦æ%œ05sjchÍQ'´>“Ì~?^ºW)µ.Ξ׋E½åDþ”ƒÓÂDš‡zKD°Ý­J†U™¦2s¡¸ GH…dÝC‡b2:ô±„‚šDÃû ,|Äû¹Ša«:ߣsì›gÚÿËŸ}É{Àa<Ž¹y³¯Y& \"uÙ¾“[®H:“»:£Ä£
+¿õÂïšûìì+ªd^§Úý-{„a¿K'¶Æüµ•€‰…'@ÍÀ€@áÿíI)!i°ÏŒí'7ÎÝÕòußžœŸž•]”ÉSÖÆ[ÇcÅ:­-Ê`]~2)þ<”¨KqÛTGýpâúyœŽ(¥€±§T¥R
+qÛnÖ%mTc±%®t7O†$c8ªq<UŒ›¥Á—òFslCñLÑOàJãiâwaóË„M§øéè.qØïžÜ°ÿcÎÓêÏÒP:ÁòXÑ€Ò„¿ïÜ_5·o¬8 p%ÁfàÀõls–Å-ì6dŠ@ÇÖwÿ¾è¢n³í²xÚsQþ¨NPu–bášAèp›ÆãOqƒE0ÛxX/«tf”Ù®>l½2ñó?;œ»Ý±‡òîÈcú9·ŽVù+û-qPÊÓ—
+endstream endobj 1200 0 obj << /Type /Page /Parent 1659 0 R /Resources 1203 0 R /Contents 1204 0 R /Annots [ 1201 0 R 1202 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 1201 0 obj << /Dest [ 628 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 251 568 259 582 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1202 0 obj << /Dest [ 634 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 161 406 177 420 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1203 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R /TT12 1611 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 1204 0 obj << /Length 2354 /Filter /FlateDecode >> stream
+H‰ÜWÛnÛØ}×W䥇€Åð"^<ðÈL‚AŠ¶SÀB 4éM[l$R)_ú!ý‡þcºö…%ÛIM‹¢0 “‡gﳯk¯óÃröz¹ŒLh–7³02þðo‘Gþb¤&;Oü4b³ÜÌ^ÿØå¦ìxO`º²™½þé*4·Ý,ðƒ Äžr6ïgö÷Å­3aœoù—Ù9 ›,ðÏS(Žs? D/ËD,NOIJÒì?"¬Î£À›‡±ŸÚ 7×æê±éWX‰¬ó ÉvugÞütfþôOožà}‡o‰ŸØÚ l¹:3W÷uÿ7·[³’ØMåÍÏ­ù;ÿz^þzƳ7Qà§xY¾Å[pï{±ÚŽNut,TãHÙR—V¬éÝršÚÌ¢$õã44qæç¹ICDÒÀÇEnvnv3ûa9#±%8ŽÆ!ºEÄrô£…æhá lXøqœgF^‡ fIâgÑ$ªÁÕ ŸÂÐ#?ôp 5eÛôEÝt—߬$ö}S®½ qÜWΦ*z¼e¶èúvçD¦nðA"ç/â82s8„Ù$nÝ~ëv%1V˲‰¾ä§õ6:œûQ>q ìWóƒ];¾ã,dµZÆ©Ÿ y¤Âb "}ÑyøhÉ<ª„mä¥yNl+ËîòÔÒ9넧°z%¶ùÛ\+£Û’/Øö¤g`bUÃ"»-úruã-P|õº‡¹ ?·—gæâv_WÛÒ;*2Bê6ôCl>cz<š¦“ ¼¡ÎYؾßÕ×ûÞuôZr
+»jù°se/
+ͬüÒÜjóQ_3ÙGï¿ÛÒ÷kÓ6¿°›ƒóCmº9ånŽ¤›Q ˆl Ê~³Ç0ƃ|§îÅâµ4nÊIã²\¨ý_¬×í=wî•wŽ6y$<LmSþ–5ÿ†%ŒÃ[؆õÜ¢€¸vë©v*y3TnÈû*Šµ¹Ö1Ñ(‚IPÆj›ä~É ”Zù¥P*g?S«¨_a²’rù¾FåvëdC;C{äÈá…õÍ{L>"v¥îÒ¯¦¥ÐŽB¹»sMÿzÐóIûú%³ÎXFćŒ¥”±éæi4>W åÊ•ŸŒæ‘ÑæÀ3^pª6pæS˜»wx÷;©%t†(hµzHÏØܲeðf(&Ýê¾%úïÈëØN뢗ý¯Ðkƒb+Gþüöç' ÇÒ&”²Â¸v71 °Žƒj*&”eg*]¾ðùþÕÉâ§NW>zþÓà0mÒ„ÑgÅW”­á26L.ŒÐ ¥%â0¾Ôwh-€¾’
+ÙØnا#©M‹áµ-dâœÛG#B­Gîî´˜S”r¨…œs!ÇRÈ©–ñ¨òŒCÈc”ú bZY~ –´ÐÙZ¤.‘3b[½H˜è*Ë(òÀ5+`£ÄÃqtîWuÉ«á÷²f#[ ² ±Yá²Fõ÷þS y6»ñ‹Så4·43ŒŽ=®@K§,OÒ—°¹ §f”¬iΙ“ðÒ2
+jã\å*ÔN­“o¯c®2Å0 ÍVõ’ªú½œDø0üNÇðxÇÔnÐd 
+…ìÿùU'ãëýÛŽÔRòPS9¸ 7«J5¸aŽfñž=‡ífßÔݱhÐÜz§ó«šìVM“<:žu³æJéRѽLóŸðÕ£Ü8rÜ8˜’¸f¸gL‹¥©4dRZTã¥à´–Í3 ì=ã÷[òëÿ"üÏGÅoIM4êøà‹H_ìqƒö~í
+endstream endobj 1205 0 obj << /Type /Page /Parent 1659 0 R /Resources 1208 0 R /Contents 1209 0 R /Annots [ 1206 0 R 1207 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 1206 0 obj << /Dest [ 1006 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 110 674 152 688 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1207 0 obj << /Dest [ 773 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 167 661 197 675 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1208 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 1607 0 R /F4 1608 0 R /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R /TT12 1611 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 1209 0 obj << /Length 2681 /Filter /FlateDecode >> stream
+H‰ÄWÛŽÛÈžê+ó’æbDóNj Là]ÖëÅZy‰'ª5b–"µ$5cåCòùÇ<äTUS”4ãØ Èf_ªNW:õÝbòb±”¯«‰(ÿðeE^¢ÒYì&™ªÅfòâû.SEÇs<ÕõäÅï}ußM<×ó|Ì)&Óáñq¢ÎïòÃT9‹¿Of¼h¦RÏ%Ø8ÌÜÄ“}yMÀËé)Nhõý¯
+Qê­épá@ã]ß´æj‘{›¼/Ö¼øèÌôÇ|6îj5ï÷[J?˜ßí¶Û¦í¯ç°ôús¼Õý3Lôq[à>Ú÷í›·7ÓWo,ªê.'ïÙà=Ú7th¬4¶m³5m_â“øW:3\íФ{D "¾À„[?‹S2Zf‰½eb
+líP•XUIO{h*·Nå"€&º¬ò»êå6¤ÂŽƒŒbd³+çÚ×ÖE65HÏÔì7ŒèëèÐæZ´:ml}I“OWÕù&¯wdh¨Ñí`(S+YLš7ÐW”#äZ€>ì#½ >ZGTŒŒ±«é´¤§Ž ÊüKù2²º¾4KS]“Ŭ¥8µ1æpì1³R܉&bv¬?mgòeìÅ°oÅð¼1Xùîæ-sçWÆ)Ï·ù<§{KtÙr µyÌåÁÀ&ûÅ!­b?;žs¨ Q[äÓ
+endstream endobj 1210 0 obj << /Type /Page /Parent 1659 0 R /Resources 1213 0 R /Contents 1214 0 R /Annots [ 1211 0 R 1212 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 1211 0 obj << /Dest [ 950 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 357 577 385 591 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1212 0 obj << /Dest [ 1339 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 405 577 432 591 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1213 0 obj << /ProcSet [ /PDF /Text ] /Font << /F1 1609 0 R /F2 1607 0 R /F4 1608 0 R /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R /TT12 1611 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 1214 0 obj << /Length 2271 /Filter /FlateDecode >> stream
+H‰ÜW[‹ãÈ&¯þ•·´eÝ-¦an»L`C˜1Òµ\n)È’W%Ûëüü‡ýûïœ*Ù²{4=–0C»T§.çú¯Þ­&³Õ*¾Xm&~ <üÃO”ny‰˜/b7I½P¬¶“Ù{Š\óO輞Ì~üâ‹g=ñ\Ïó±&ŸLûáq"ÿ’=+ᇩpVÿ˜,xÓBÌ=w‘àà0uÏœË{ÞN£8¡Ýò×
+1TÓÖz/èK•F¤@¤wm“+­‘2²+öZPØQ!”§²~æ Òªë0Ö_KTÃܸÙóÏù6 œi£(ÌHqJ\|'ÔAÕ¸¤ÑJ‹2/„]Þ"Þ¾T"çï…Ü?x2²’
+
+ɶY«
+–2ümYÒªGÇ+6rz‹iAhllÈ©ñc$máÿ™ÎF/2~~ìÍrTѱ€šÏª¾’·žÈrœ ^PH w‚h>Uólöß {ˆ`3ÔIiìI¥OÖúráò çý´‰ÁEâdˆ6á-´´‰€,PhY•Û²SkþÚ”ª‚žh¥Ì¢™ç®Ð÷oúœ(x.C’i¡‹¦íĦäõªZë×*F@¯ðÿ¡L8·
+fVMS)œsȪ½Ã½Q+s \Ö¦ÿ9z´ãOT¸Å¬’
+­Ci•åð¸ÿŽ9 ¼  nº ü0=SÔ‹±¿È´°y((×¢B¦-zyMYx'à
+µbf#6 ’=kí£4©Í‹LµQÚ(á¦|F *^  SÒÎyØ@}5
+tí3ÇÆSqűÙã‹"óD¬5¡vÓ©œÎÛ>Ÿ`žÈc{
+¾±}D>¢¿‡ö`ß2²§(\sHîOJ/g_ÜX回8äù•ZijøK°°¸×j6Z¢6œcõ¢LÆ¥T¢V:^¢#Û_÷îðè÷R‚(ÇkwŒÈᎠËxIì‚9ÈmdÓ­›}[ BЙ+¶šœ™G4d«1ø
+c2Ò|oÀ|ÂùÂãe§p¶¡¸ßAp}8ünnø{"¸ÁŸà¾Â«9òƒÚdûª3V§ß¸‚w
+endstream endobj 1215 0 obj << /Type /Page /Parent 1659 0 R /Resources 1218 0 R /Contents 1219 0 R /Annots [ 1216 0 R 1217 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 1216 0 obj << /Dest [ 596 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 218 517 247 531 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1217 0 obj << /Dest [ 596 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 456 252 486 266 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1218 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R /TT12 1611 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 1219 0 obj << /Length 2153 /Filter /FlateDecode >> stream
+H‰ì—ÛŽÛÖ†ïõû®›ÀˆæA<(0 8Ž¤H‹ £«Ú½à[#¶)ð`Ey¾Cß1ù×Z›G#ÇhRç"°1"¹Oë¼¾ýõfñb³ ”¯6Û…(ÿð³Jwµòb•¬#7N½Pmö‹oºTåÏñT—׋ßÞúê¾[x®çù˜“/–ããq¡Èîòõr6ÿZ¬yÑZ%ž»Ž±q˜º±'ûòš€—ÓSÓêwú¿¾.ÏYú¡k/Uw'u{ªû¾Ú8ØIwe§^{£þñ³³ŒðÞb,r#]:žÎw7êöXö?™¶âMBÕ…³\kõþëüsó—kÌÚžãeó ¾±ÇãÑuB××jèXl#]<zn sûiÇ;½Ý,|UªEÅnû*LÜ4U±K*è¸JUkÛÅכɡ)ÞCkœ­ë‘E4[>ZY­\o… +7 ÓDÉëhÔ$ŠÜ$˜Y߬U1‹uò},tc'…5ÔËáPd½É+'…KS÷eÍ#]Õ¡tþê+%ST^a˜Çz( Ý¡LÎBX>ÁßB^:k[w†Âü•ç'dÙå9TøÉ©Š¡-ë{eeirÙš§—Ñ9©ë­Ý ©ëÓ¾ïô›¦î³²6…*ë¯ØU‰žv³±»ÉèlZŸƒ×†ßËÖ‰àz³oz#Ce^©FžÕËΗµú`_‘mYïµîaÑ{y†0ŸX•Ù#Ua¶ÎÊ]éÌY0õPõv ùÜ8>‡: bw”-äEþ"`Ʀv–)…°l€tØ-mÜcŒi-@ña-0†J3ô*ƒ›Éª…ö*;…r|O7-ove†³†Œ4­ße½*Ó©ºéUw0y¹=© ’ÄSò‘¡Í¹\OȪÁ¨-NèwÈïCÖöe>TY«šC_6õ{gL
+Köæ¤h‘N¡ŸŠ>o²Zåѧ¢":GE${Ü5Me°‰èø”æ2NÏ„çˆ'íÞé×TªVºïÛònèMG¯¾&‘|rÑ“"y …º©Í3làÛ„øÆl3‘h>q„”â€Ïh¶[>âBŸYUº ¡àB¨úÝ–]ˆ¨Ex‹óTi¥°_åñKõ¹§$¡ˆ²õš“quƒm ÿü¯ß?\Ñ"‰ˆU¥úö¤úfV¯‡Á8­vØß=2'áÒÕ´…€£øQ4’þY[§:Ãçá„L•[DÓÁYvЪۡtIÄ“¨vâ.C%„º:+Š’´Ï*YŒ&á2]¤õEÚ•®gi³Ë>œCÌ·ó.ƒ=š‚Ý*
+1Z„¬•¨º"h–Û²ípd¹§ ‹pUT]EÕÞU£D†²¸§bŽ$á,ô1ÐE&¿TØ”}¦ö
+»v¬]}‚9ìø¿¡ËA¾Ú©v wè
+ ªÒ‘,B‚ÞQé­ºfœËò-Y¬‡)—œC4)Ë…s/AèP1ÔDR›(ŸD?æD›Û9½C}†ä1?rÚdy_nìhC¾è‡¶&—s>Ó×1ŸñŸó/§Â¾Öƒ=Uí‡|§†zy5µ—ŠÚ˜=#¯ø|b›Ê‚Ì|žõÏhŒ$½M:±¦’'HÈrBÃÖа}r—–Â|€}rõj|Ä°ï4íˆ:‘êÑ<–&ì‹Ya(Ø…Ç ›óŽ¬îÇH-û|¥`";I>Ó6hºâ$*i˜'EK°‡I‘@³¢™|joê½SΞ¦eÔþ‘SÄ\d¼œä2Ò­1× Û¥õµÄZÓ¶¦;4uARm‡:—z]«ØgÆîç è*a´¡èzJÆdŽ¢‰“N *U³d’¬3¨-ªDšlâ0¨hŒÂ0ÃÎ(eìô½ðWÇÎUÁã?°óÀÎð÷€Á¯‡Oõœÿ;v_;Ãß
+;ƒ«ØùT/ùrØy1‰ú~{7Ѻ®ßÂÎ ÈÁà'ySz!ófV†…ú8çdŠ\
+ôë7o‰BöÔAPˆ¯2Ìc›ÊìÊEuÄá˜Ê…\µ¡‹ÿá¶2KQÎ-™e43ÖµðJ]€ý9é¹…å†d“Ãþ’˜0ÏTµÔeI\ž½0w.Rͨ*`*kCéGk˜¢ÇÞ¸Ú@€BÚæ®2ûN¸Ž<÷Þ¡«Ho)À`}È1›è]3T50
+endstream endobj 1220 0 obj << /Type /Page /Parent 1659 0 R /Resources 1222 0 R /Contents 1223 0 R /Annots [ 1221 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 1221 0 obj << /Dest [ 1215 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 358 757 394 771 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1222 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R /TT12 1611 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 1223 0 obj << /Length 2491 /Filter /FlateDecode >> stream
+H‰ÜWÛrÛÈ}çWLù!;¨aÜ‚ŽK)¯í8Ne×[1_+#p "Ñò‡äòyÈéîHI´½[IåaË21מž¾œ>óýzöt½ŽT¨ÖÅ,ŒT€ø$Yä'Iªåjá§Y«õnöôe—©¼ã5êòzöôÍûPÝt³À‚kòÙ|lfú'scU˜Ê[ÿ}¶âM+µ üU
+Áqæ§Èå=o§Ö"¥Ýô¿"ŒÎ£À›‡±Ÿê S×wêý]Ýo1iëA’îÊN½xs¡þúoo¾@¿ÅÜÂ_èÒ t¾½PïeÿÙ¶ ‰µ©7Þ|¥Õ?ù×ûÛú3¾1ß&
+üõ+Œ±‡ÃÁ÷b?ÔjéXˆÆ‘>šéÜ mYÒëõ,T¥šE‹ÔÓPÅK?ËTÂ’
+wL2ÕÚY1û~=#±$¸o£u²ˆfë=3Úo™>þ˲yx4(ê7uuç«›Þª~kzü”·ð3­š[Ûz Œ*¿0.¹±nöù°ß˜ÞæUé…4‹#tÝ—µ¬¨x}sÞ›Î/Õ•–! ¿‡~ûÞ
+>I¯<eœì²W¶€MWZ~mÞ—·¶ºS×UãaOB6%qݶ,B4t1ÃË¡óîKØÈ…M}祧›Üž¹lš‡>¤ˆ{aX¹e§6C[Ö7ª«šÃŽÎ/TYçÕ°¡Á¢l»~Þ—;«hÊgg@pâ&ñƒÊ$¸k¶TÒ<$þ2: ñàè¢À¹H¬´ò2¶{yS7­ÝØÛ²æ‘b‡O¢Í§®ül/Ÿ)šç‰Ö*|zg>•»‡vÐ x«ÚÈ$mr!î'qÑý“ \NM"Ѥµû¦í-v®ôPy…v¤Kªûï`){û¶.&ëfQc¼yºÀÎìäæá˜ÛîÚ/›º7emé gœKËGNKÞG?G'n~Þz”âv‡È–æPÙˇjÍYÀäs¹l6*B-ÖÄঢ(²ø†"áâ˜g ‘qÝ4•…œ[S ÃÀ¦/+OÊ„éÉ^|%ºïÛòz@TR7Ô¤"ä*KŽçº©íŸ_±
+?«®¤¼z¯-n§Û2‡®©Þ*ÉFuh€Ð‰R“]Ã䆉(U
+Ð8E78_•­B¡S×4¶w÷Nò•çE¡D' ;–P­ÁYì'Æþ?ðð»õ;õPõÒ%eÞ‹8’¿[ÞQÊ :ë¬cz.=Í
+q@ Ðýê*ñI</Eã¾’Q\ÔuÓo]Uei jj4™çªöI†=,ÝÑ#’¸fùÃ&†ø-I ¨P˜
+k\á+6- ï¶M…ñN
+
+ â ^èö£i›èKUpK5ó,¼/:¹Ûëv(bwÊÐSŽDÀ †$‹`Šó9iJdqߢ©*yÇ$ú™31c5—±˜iÓNûÜøœPçr†âT>ÙÑ´òZ„ƒŒÓk¢nzE2ÆÒseâLõ:‚á®C±ÅõW5Õ0õd,–O®¼ …¨u/Ó9
+G}rìTßiv<òwÜ{Iln¥_¬½º¯ß¼ûó_~SâºÙ8õîÇßþÞìÊê:¢HÃÊ v`×Zýc°miÇc¿XU>8’vqä-1º ²‘NYÌzµPîib¢„ºž‹†iFÙEˆ jñ "à˜ '¹å;fwˆÀ›îÝWœ<vDî9óŸ0 —þ,LâoÉñ·Ðc¼q¢Q"N1'ê¨ñ{m‰q›¸K}Gć  ÊâôáTZ¸ÄÁf×…)‘¼dk>a\wË^z Zw⦱n~1P’žúÇI½3=@€s’Ulzp)NUð‡Æ›¯À.”­ÌuÓ#ÁÛuEDÑN ÏzƒÒ"Ò r»ï,é RíñAÄÕ‰
+SöL–çäŸõÔcÒ'>}%5s: è=Æ…\uæ§(‚ ¡oªe ÛÑä¬<ëÍÜp`†ên±j©£ŠBòê¦3<›Ë)#³©EÄ¥œ)xNÍ$°y¨¡‘TW#ÿ$ÄMQbÿˆüNϦ|bAü€jÇ4  Ø`%â’ˆuÎS.ç:AÜäžÝàÂý¬Žñºø¢çD‚¬ˆ4?XOʃA_mË›-9ÜæÛšr€àÑTÒ`%9èE'O&OÊ~júqµKrl*¡2TPÇ÷î1›ŸÃhà @2Út2grц8幡¢¶ƒ±Ìå¡Öö‰ ®¸ÅS52Ø­0š
+Hí"cR',õŒtT¥ÜÙŽ¸ÊÜ~›«† :ûŸrUáuÅ›(kx¤¬¡PÖFÚê9y UUº·î{I3¿ÒnºïÒ&ß}—qGReAV&’D•'B†©vÇD'QDHG~)¥ñþ½Ë°Î 8”ýv~¶V'Ghu±Ò wFjÆ0zÕ!
+6œø¨ä$ìÌ
+fV´¬ß¦Žáˆ¹\öåQ‚~íG¬Ü2_B‚wœôe>T¦u„’èÆ8óמ•¿øáIV¸8÷p~Æ+æÿúnˆÿËwCÄt^7EÁG<¸ûIž¯×³ÿ
+endstream endobj 1224 0 obj << /Type /Page /Parent 1660 0 R /Resources 1227 0 R /Contents 1228 0 R /Annots [ 1225 0 R 1226 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 1225 0 obj << /Dest [ 596 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 71 309 102 323 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1226 0 obj << /Dest [ 596 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 196 126 226 140 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1227 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 1228 0 obj << /Length 1979 /Filter /FlateDecode >> stream
+H‰äWÝŽÛ6¾÷SœK
++ú³¤)´iQì")÷f“½àÈôX,
+’<Žû }‡}ǽØïR¶Ç™L²Ø¶X`‘Œ,Räùç÷~·œ½Z.Ši¹žÅ Eø‡Ÿ¬LÂ,‹r*®a^F)-·³W¯‡’ªAÖD4TíìÕw1= ³(Œ¢kªÙ|zÝÏÔßôƒ¡8‹)Xþ2»–M×TDáuÁiæ‘“+{ÙÎo‹œw¿SÿL0;O¢`§a®¢’îtwhÇ feHRC=з?^ÑßÿÌ÷ø¶ª"Um®èn_¿š¾!©Òí*˜_+úMžÁ?–‰ÇâM…9Ëï1'ì÷û0HÃX ¬Õ°Zˆ†Ê¯Q˜¨ÊOmDÒËYL5Í’E¦yLi–%å1"Ið1+©7³õì»å1iŒ%ÑÓhœ¢qD”Dï”™)~E…øs[ž ÿ;µ rX#võ€WrOM›úaÓh4Õ¦­+ÝÐÄ9â9OàÒ8ÖíCHwvk¨jê
+qUæŠlK»võ}ƒ@ض5A»ª±¶íð>é'ÛouÓ®œÌ“¹édnŸR;s‡`1…+Sìi®Y#lt#ÚÛ]³"7ðë~1Õ%µÛŒÜIÒiŠlÇfÑV0~FBòEeºªL7>ÕÆIÌ'C¢­éž’¹œ3çô¸Õ´±{ó8I}6uÙ)u™‹Lì Š颇îu;Ãõ(êM×ÛÊ »ªÙŒñë;‹INKÝ2ÀÙÍÇqfàYæñ, ¡,¢,LÓ² 7œP^a‘œ!PtD Èg+Ž±N%»uSÙmט1(8Xë^~-pf‹ïŒ‘’¢²­¬o·ß°yVF{ò7º1êÕ-[a›X˜¥iÂaË¢¸àÀ9³®fÇÀÑ q>·ÎÎ)2&á÷ueŽq(/qýè|‡Iyæ|<)ñ®¿FLuÝš‚ü ]¡Žb=]ä
+6âÑ‹pÓ6ˆ…-Ä/щp÷‹pŽÛ`^2 8`”Íü+kÜîFÔn/ Þ“î:ÂJJÐÞY\ÃF^&ÇájCgªz} Kž´ÔÓVå¸K=êfgˆÁQ‚:Ýuµktï±â} ¼rª‘¹dóèó¢<–^éËB·rÜôT‹/UÅâT 'ãÞÚÆ@›àˆŽ. ödLz4ÆÕ—gæo™í3U_ßïF€ÑœÉ&Åœ¢Mâc^mmk¾"Äß©ïÍZ£ˆœ×å *ΰ:DÃ…;g¼~YAÅ©‚Š‰ÄÙQи°Ï„ÆåW’ëûº©Ç¹iAñRùC# ¹
+úø )*ó±c^ÑÄ:wßç¶m.¹{ÖŒ4)Κ§q°·ý&ÐjÄÎgYðÓƤ©?0ßÈ28¾gò?ø6£¢1ˆY¹Ÿè@Ç~Æõ30_š™+
+?Ç䧜D>'°RªV
+é­n!@ÂÉw PoßœmâZº×þðüÊñ¬¹‡Üb‰–xx)|CÁ£àvã"íû{ÈY7ú¦û‘ã'ˆ;ŸNÚÓNÖÛ~…û¢¥mí-6õšûæŸ8t¢S,–›Û‚Ì0ž@ Ý9zªGt—è=@Ïd5–¬¢/‘û×saMNaõåѳBô I\±ðB±$´³œŸ~Ä5Ñ0UPh×uV.vð Ÿ—®²_ÃAVÌ×#ÐA30üö½:Û®ø
+±Þµ•3¶}§\÷”‡À×<œÚƒ¯à/iò5œCðNcåÀ–»Ü±"xôxª[÷…ópGêjHD ‚Gì]”‚½q”žcï&ô-Á¯Aß$IÃò÷½,ü7è›þ/¡oòg¢ï Ý㟂¾Ùˆ½é ^¾>‡)q
+ÿG¾™¥è
+9Óréã&*t`Òö
+’âÔ›
+endstream endobj 1229 0 obj << /Type /Page /Parent 1660 0 R /Resources 1233 0 R /Contents 1234 0 R /Annots [ 1230 0 R 1231 0 R 1232 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 1230 0 obj << /Dest [ 596 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 495 611 525 625 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1231 0 obj << /Dest [ 596 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 143 414 173 428 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1232 0 obj << /Dest [ 1110 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 301 168 342 182 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1233 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 1234 0 obj << /Length 2098 /Filter /FlateDecode >> stream
+H‰ìWÛŽÛF}×W4òÔF4ï" À3q²x‚`G~Éz(ª5bL‘Z^FÖ~ÈþÃþã>쩪¦.3šµ‚0f ²›ÝÕu9uªúÛùäÅ|(_ÍW?PþðˆÒÀ"/Q³,v“Ô Õ|3yqÝ¥ªèx§º¢ž¼øáÎW÷ÝÄs=ÏÇšb2_wýs~o”Ê™ÿ:ÉxS¦fž›%¦nâ‰\Þðvz‹Úý7ýï
+Ã@Ma²çϬ'-(¼@(ëUÓBzéä” dºVy§*¼º)r(°Á{ÌQ¥Ã éc ,÷27HO,÷GÃ}9öº©û¼¬ÍR•õKßì(Öâ?qg#
+ÉÛ§máI6œAÀ¼d|õÚ HømeD'"Öüc0ñóbÏ r8¶ëóz™·Ë+µ rjx3ÑyÝ­ 2Ÿøæve}1ý/ôß÷싆’©¬¥Ù5BÕ)_"áçT«ñ)]sÃߨ.¡î,œ)õ\Ò[ßßïÕÝÞÊ*¤%¿åuïx'q2rZ•˜ŒÌF‘C]…åOÄ¡W…ˆ¶·‚µjìQ«Æ‚Ï7ëüT½6õå«Yp´7{·$Õ¹à>;Óò[2¥:\wf ­øÿzß
+þBN¿B©Ù¢ñ]ó0oAíì4Âø}IT¯–f•)ªá5±¥¯i—:lX± ÜÓòƒ·}–ý
+†C)bk&'b´â–×½"*­Zhœéù=fC=åß”ÏwÕ_š ßÃxT7xt|¹Ø¶?­}‹á@­žP+U´Ûw§„
+à‚I7#—vB¦ Í…P%èõýüûiŠ:Ò¢ƒê¨Œp"°˜C³kÚDó)'†¾¦§…„úeõ槻‡ÐÌ
+endstream endobj 1235 0 obj << /Type /Page /Parent 1660 0 R /Resources 1239 0 R /Contents 1240 0 R /Annots [ 1236 0 R 1237 0 R 1238 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 1236 0 obj << /Dest [ 907 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 497 501 518 515 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1237 0 obj << /Dest [ 596 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 423 283 453 297 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1238 0 obj << /Dest [ 921 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 257 256 284 270 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1239 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 1240 0 obj << /Length 1722 /Filter /FlateDecode >> stream
+H‰ÌWÛŽÛF}×WÔ¾5Í»¨aÀ‰ÀÁæ‚Œ°ëÙÕuB‘
+Ù”fò!ûûyÈ©êÖÅòxÇ’ ðx†Ýì®®:Uçtñ‹ÅäÅb‘PL‹Õ$N(Â?üÉÊ$̲¨ Ù<‹2Ji±™¼ør(©dMDCÝN^|uÓý0‰Â(Š±¦žLû‰ú¾º×g)‹'sÙ4§YÎ NË°ˆœ]Ù“Èv~Ê Þý^ý/Áì4‰‚iœ†…ŠJº{¤›ÇÖ®1“(À’Ì@¯¿º¢ÿLsŒ{¼ËÃ\™ RõúŠnöÆþ¢ûFŒ¤ªj—Át®è¿ò;øÏâë‰D,Ñ$QX`°xƒ9ñ`¿ß‡AÆjàS5 Ó82Äc&ªöSk±ôv1‰ÉÐ$É‹0-bJgaYRIBŒYI½ž¬&_,Ž`¤1–D¢qB7bD” ‡e>GYeX…iZÎÈ  Îò<œ%g¨F³ªQîbŠcl ãyPÂzÙëum[Ù`|ÆáÕ5áU®Ü Bä5Cy à†¢¥Þ™Z;øàYyY=Gw’y•gîÄobçÌ—]k+Óê%™öZ0©£Y_”E8;d…ËD,¸Ý/û€S­7Õîqlô«K·¦b`‡\•o<,å–Ò{RµT;oœ#ùÿsäè„B=Ößü“Àq€…¥>äë£ã“ãñqÁÞ«×\³™²¶7w£ÕcÅç#IÏ
+Ž¢k§K½ªÆÆ×Lj˜Í¸¹Œ˜}†`‰BìR‡·jÐèRÃÓ þXbø¥F’šun´Ô˜±Œæá8±|Bi˜Ò*˜ÎTpR¤÷|Çu
+
+ÌÙ¯LÀÍÊÔ—©ÿ€†úLŸTÁ'DÆxh 3f³e•+¢`E]ü  øÍ’=†£+$qH?¦þ©yü}¢‘¥¨ÿâ\4²c‰'hF,ô‡f€,CÝ›­}u2âF³–5º¹û`u” @†]²X3`¸œªí¶1ç3Ãó½I•Üzý­dAN=Ýv• Є«¦º ørjè¡ÒןöÁA~죣Թ9øþWùßØÃŒ¨kQA«ôôÕá³|«øråzÛU½©îÀˆ¥ÆZ¨º©œž ÿ
+{«±­…Ù²éÊ[Å%\„¸NŠc§z™¥ô”%¯lߢB®ù›ß.àtÛYv¶Û;ü¸K^j©4M;ùq8¸9Æ(õù,$›/Ý ¾–‚2ä×á~"Æ)g8vÕÅ~.ÝÔaœ(±å.íª®Å4ÿ÷o—´yh¡î0‡E—)yÿÛËC‘±÷/ÅÐÁ¢;_P­±<>÷öÖ)­MA}»˜ü6
+endstream endobj 1241 0 obj << /Type /Page /Parent 1660 0 R /Resources 1247 0 R /Contents 1248 0 R /Annots [ 1242 0 R 1243 0 R 1244 0 R 1245 0 R 1246 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 1242 0 obj << /Dest [ 1241 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 275 689 289 703 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1243 0 obj << /Dest [ 895 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 332 689 345 703 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1244 0 obj << /Dest [ 1538 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 368 689 382 703 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1245 0 obj << /Dest [ 1286 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 381 539 412 553 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1246 0 obj << /Dest [ 1339 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 293 191 320 205 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1247 0 obj << /ProcSet [ /PDF /Text ] /Font << /F1 1609 0 R /F2 1607 0 R /F4 1608 0 R /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R /TT12 1611 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 1248 0 obj << /Length 3098 /Filter /FlateDecode >> stream
+H‰´WíŽÛÖEÿê)npYH\~‹2\±;.R$Å
+(и(®HJb–"‘ZYy¾Cß±?zfæ’ÒjWYÿHaxIñ~Í=3sæÌÛùèv>”¯æË‘(ÿðˆÒÀ"/QÓYì&©ªùftû®MUÖòOµY=ºýpç«U;ò\Ïó1'Mú×ÃHÿhV…ò£H9óŸG3^4SSÏ%Ø8LÝÄ“}yMÀËé-NhõOú?¾NÏ™ø¡›h/U‹£º;ÖÝ_]8ØI·e«¾þ0Vÿø¯3‰ñ{‡±Øuéx:[ÕÝ¡ì~-vojSçÎd¦Õ¿ù¯óÏù_F|c¾M๠~Ì¿Á7¶àp8¸Nèúº¥S :[ãH¯žèÌ~ZóNßÎG¾*Õ(ˆ7L|NÝ4U‰$î¥jWŒ–£·óŒÐÇï1't=BD3zðQd}¹^$nâ·ÏiºÓà Po
+‚•ôÒËpó7HÏpó{³,jس3e]䪬_±ß§zØÖ&NâNûÈ¡P–-dùëÖIÜTí£Îþ•5õÒ‰p—rõæÒ¾ ï¨`á
+e*ymŸÐñý±B¦)?Qò}ëø4Pd%Ý<°»g¦¢¨‰tÅ?Je9MeðnÝÉ.Ÿ'ê9ÌÂ3œ|rÁ×D ‘îº]¹Øw¸-~úšó²/ Ÿ‹-r6KJØèê`4;n0ò×Wr;Daÿ®òõRÍ“&»?zÍñ²f³5]¹(û¥ÝÑIa)'œ‹Ô"0Sv—¾ùäˆýã ×þðNøðU‚Ó­¹ÕÜ!ú\³eË?Tÿ<¬Ùùp ðo!ßK¶ËÎÁ@Æy1«xÚ¾ª P°É1XÒ5)ÿûãó¾¹êuhdÉ=V+ðönÙ¿ƒë§«ìtTÅgk]WÖ+êÀ†gTcaxprN4¹é qŒ«>vàä_öå®hÕ²Üm¥“¾¯›CUä¨^pƒ6‹fßÑ culÈ/‰Þƒ+ÍnUtªßJµÇ¶+6öÇãÃÀ™M'ü/kåOº2G:0äbˆÀÌ«W3aGh'š’JïdlÆÓè?Å–¯÷UutŸEÁ?(çýˆÂSæéÂS9AHNˆû_KLKû2B6Â@%« Mõf[âWšÏž¥¹æ™ð`8¢ûÉ Ì­ÐÚh+s›|ŸÐRö%‡÷'R1GæÒV[g’žNÍîÍ
+„É({ ²c3¹„] ¨”Ã^ÃêNì&“ÙZJK^.—Å®¯n­j–ƒW[÷,­¾(ã¦\Ì¿ýl©WRÔßû¶*¹1Í¢a#ªÅîAÊž×Sjë›^i¤¡ë¥¾Š“ÔÙ±º8Ó/ŽÆÉ0$Þùxì¥0æêò†‡³§´­Œ"Ë“èrõóãG§*í{$¥P¦ã(ä Àùãd"ÈlL}”·¬¯Ó2Ô·±¼¶EÑ¿däaùA"GÞ–KyN&OÐCV[b#¡vq‡+ã4bR.‰\A¿¾ÿ¾¡@8;¾H£«^ewÊèo¸óùå/ ÷g‡3útÝWÆß>ß`ôò+fkA¹"uQ5Ãtz¡w£)éês½{’IÉ w}°Ô
+¥î]g@ДÑ,·»ìÙRñ¸
+
+†Ž¿6
+t ÿ«5iœ{ õÃ3" g€åÙ0Së+þ»fטšÞ¨us…Dî»Y†Ê-Ì·QH~1bcmMµ·£ãj>'—
+Ž:é‘ÞÒW6»ÞÛä⃃éEÅüƒ”C›÷à«ÀFèSSáº6ÿºªìMC–I0¡s³{‘ÞËSD§%ÒŠe6 å@™mÝEKHs+Vì¢ !­¬ßK'€¤Êö§ƒ›Mù«¡øzŒÂ¤¯ø¿Þ©½ól{7_–fàœæ¡D“§Œª÷”„¾Þ,Š±SÏ|~¿mèB¨x'½œjÃú‹¢¨rè
+Ú8D d׺©rúY0HDèê¢&p/û«4þMÓÑKõ«9‡3!Á½ª¡Êªª9¢†û|¨+löUWn{Œ'ö
+.ÄïÓ@AN‘MF}mlÝ‹fŠÅ:˜ÿ’Ç·•ÉЇÉÙ<Ji‡å Û¨I`PØpX a :~È™HIô$©¹âx+>ºÒÜýÿƈÚ·@Âç0:+j´
+séJÁê_¡ ýàcüpÍŸÉï“ó³SâÌä¢ï‘ “ݨü¡Ú¢/ʪDéÀýc¾ŒuÝ@X?Ò%H“øiÍN µ—NW~샼™œ¯=V}ûäP_f{(W}מ´`WR±xà["m8c苪I„Ð[ó3âIòeÌõe y–U<«^~9kq{ø9奄·Ÿ6ÌK™Ñ•’ºPGÄì2ÑUs~öi N;;Ä^V.6ÿÓ5;õ’jS®Ö*h§öµy0Ø `V\ “ µöÝ~Wœ£ÐÚÒ ûJtE;ºÊ
+ΧÕ\x’nŒˆ6«Ù®Û1åW¿{%”šOÚîä½Þ`Ê{ð_¡6fwOu¾Uí>[CåäjCQNåëšçÖ%FŸõtrò´ä?`DAÍè.
+qR¨ù4ǧW ³cú/¨Î̘€‹ q ”EÝYªì3êR,Jâ\˜ãÏãÀãùX
+µ2Û•0Œ@c%‡*‰xðÏ21=íœÚ*Y!稺 *²2bÌ#£ì¶t ÜÖé¹ãÙžíÿ´Û2•™šà[qÃ^ ¸e`™KJÀÆ+Bº [¹e}\ŸÖ†gèݲócyÜBìpy°™?çi–ñ‡Â1¼ü¯íj×m‚}¾‚KŽ%œ—-GéÒ¦J78 qƹâï3»³¼é8X¯÷ö13›e…Ø¥Jœq¤¥UíÐ5×ÞV’£Ñs2=¸ª.‹,b
+­D¨îxÞœp §]©Xþojó”¬)l£ ”7šéôå´F 1~Û¢QJ'(èv‹éXœ¡9†Þ`k|8Ÿ³&ãÚ±rOz@·@ØAKóнÚxÑ tV¸n\ýò=†­I[{ÖÓÆÜ^íD¹»dmòmt ^·9U$ŒRù€é•CKSwu(&çlÂV&¯Î \ˆ~Tñ(héB•q»eüML48ÌÖ]lgieëªjÃð')3ˆ–E‘F ƒm´×[~¿VÜdTd7ÞbÎó×|t?kº¹*·-2% ”ÊÄPgoýEÅÀ*s<5Hu6þ…
+? ´üÑ5$ÿ¼ûã¬
+endstream endobj 1249 0 obj << /Type /Page /Parent 1660 0 R /Resources 1267 0 R /Contents 1268 0 R /Annots [ 1250 0 R 1251 0 R 1252 0 R 1253 0 R 1254 0 R 1255 0 R 1256 0 R 1257 0 R 1258 0 R 1259 0 R 1260 0 R 1261 0 R 1262 0 R 1263 0 R 1264 0 R 1265 0 R 1266 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 1250 0 obj << /Dest [ 1325 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 146 708 174 722 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1251 0 obj << /Dest [ 967 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 146 694 174 708 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1252 0 obj << /Dest [ 1331 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 307 694 334 708 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1253 0 obj << /Dest [ 1317 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 195 646 222 660 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1254 0 obj << /Dest [ 967 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 207 632 235 646 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1255 0 obj << /Dest [ 1331 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 368 632 395 646 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1256 0 obj << /Dest [ 967 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 125 597 153 611 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1257 0 obj << /Dest [ 1331 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 286 597 313 611 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1258 0 obj << /Dest [ 596 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 442 562 472 576 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1259 0 obj << /Dest [ 1276 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 167 508 197 522 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1260 0 obj << /Dest [ 748 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 399 433 421 447 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1261 0 obj << /Dest [ 1125 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 431 406 481 420 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1262 0 obj << /Dest [ 1346 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 195 191 230 205 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1263 0 obj << /Dest [ 967 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 230 178 257 192 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1264 0 obj << /Dest [ 1331 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 390 178 417 192 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1265 0 obj << /Dest [ 1357 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 195 129 230 143 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1266 0 obj << /Dest [ 1441 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 150 67 191 81 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1267 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R /TT12 1611 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 1268 0 obj << /Length 3742 /Filter /FlateDecode >> stream
+H‰ÌWÙŽÜÆ}ﯨ ¸LSÜàe`È–-YÓ1àhƒÃfÏ0b“í&[ãQßÈ?äósï-.½h¬
+C¡^5üe«²ªR—?½Rm—u­-ìé¢îZqlJóf0Ä mwjˆ²!bãüæ’ýF&;HMÈädÈ(z¤•[ ÷Fèçžëyk-Š8Æ
+¡RùN>î-¨»á@ל˜Ø±+'Ê9ñÄÌ][ È-ŠûKQÑh© –zÆ϶ZX1RçÖ¢“‹£ƒ=Û‹:ølN‘bìºcѹ²¸$/$Z5×]VÖÅR¡nÙ'ßìä{#`×Dú…Õt/ß~«ž(yê`x¤ýuÑûJ+IÕ äAŠÇ ÅbÁ•¦] ú‘mÑ•¥Pw›­Úɇ²¾aƒ./üþ²!Ñ—o¿·PW¾øåJc•ÌÎÉÜDoËM§V»:ïʦVe­¾©š›²Æ_w4ï‰Ø»øóçòHL ÙD$¢ƒÃI³mŽrñšó/;)0krI&™^ŠûèÈ鞸v|eÙêǦ“}p߬ãK%‚oÓDH†D0ypo
+& œ µEEµ´Rò˜*ÌÕŒ#pмT/^\È‚ŸÉ¸T¿|n!µmF¿—²5“‘AA–ü$œÌ"Ø/ÑwÆuèÄ!'‘ˆŽ â#t 'è’ø² ¨ô8ù¯Ëþˆ¬" Õ ½_¾yKrÊׯÕGhòáÀ×0›mѲ8…eÐX®;¦ïNóµáT_ƒ”¤´5}®înK‹RÔ`ï­Ê³š]s](SÜò¥£P‘Ãö@qaü‡·VoŸ4¼ž‰>^’9t ’åêáØ`Âcå]òDJZ-ôøåë×?ð6O_2´þÈiL2Tﳟ9 Öúš2ú5FSÔ$zÍ<Êú/ʧiµT^¡þx¥y%ë>ߎÄ#ñ'­B¤Â8µÃȨÙ2Á'[b²Q,ºð¢.à¹WÖ>éá6ÓÉÊ1dW„£ßàº~Kfº’¼ð‡w¤'iÌ£œ2‘D $õ$ I$/Æútʃ¼)?ô¥Ôƒ^ÑrýäMÝ¿wØ‘ŒC&–æáZþU¯Åa©¢Aˆ'7›,)U=æM­‹¾Yè­‹lgjœ?U™dIÌ:pÿD%<¤0í<÷=2窃TO/xJIOu×lM=¨ªüP°Å×»²êæ¨Ö‡//./%Á_ÿøóÓ·”äçj(•$a£:I3BËrQQ™†÷¡±a|rmØED3 -ð¨ýÈõ´©Ì·¾-Rm¹Þ@
+nYR:§Ì)ñ’òT¿r¬
+ •jB:£±” 4É‹3™µ—P†ø«S`èõiÀa'*+?ûdäíU³ëŽ|Þ[ÇÈG9Ž`$†\Šß7¢(ª2/;à“)p(þä@!ŒõâúC4¨S£v@báê{Ê!Oä"ÌÜ­kE³JsŸ
+%³‰À–Ã×׿d´>Ð\v<`3YŸ®àh¤fÒÒ_÷™ôÙW<ÄËPØ€äydÔv+;–¼Û‚S’8m¹„£ºmÖOØð„,/Ì;wÈvn4{è¨ñ`¾Óõ#m^X¨€d˜X“`™<à뼞l˜HÕ­ŒbV%*­'€w9Zc ‰ -éH0æÍšZ(a‹ê>3Óþtö¤Ö}½¿Eï€"Æ—dAX˜¼ÁX‰óÑÉkŒÊŠ9/AS•L„Ÿ‰&=rEVœêœð¦Êúf£UùjÕð—ÿ°^%«mCQt߯Ð&T.ĵ5Z`¼JZJ âEK²QdÙ‘¥Ä–Hý÷=wü$9I !àØo~÷Ý{†=é!Ø ‘ Ü„¢Ó÷˜
+GtØ;l{«rŸ.ú2ö$ÛúEæ <·/¾Þô«ÏõÍ\óú—¡×˜E{ «µÿȶ–Ø]þW)â¹äËð»83"»éGÆåvê­bI%imŠ;ý“&€Þaï!}ª²IJP즹 „qözÅ~ªuårNVäRΈ²¾Cž‘%$³ÌV•ƒ¥Í¢BÖìómi«Q “Jt/ƤD°1œt JÚuïüÚV]ªáûå§óº…]Õû‚ideA³¶ê`Ç9[ãú]¬ÕFö)ÛkUÀ t»gÆQ²}Ô_Jx]áSÆÏZ:;ŠjÀ³q85vŒLXb.8b•°œéŽýGžA•^{hGÓLÅ’lËP‘•èØé`­ß=90ªÝœ#²C™35cÖWf·°IVy_ÅYAbÔcáN†ëœc5_—º>Y™£ôžòE#Ôqø¾NÔ(PdPm$H·à‚";uˆÔÕê3tO} 4å½–Îå—›ð+mùIjϳ¿ö‡öõoi½ƒ¥´ +×u]$¤¨rÏZ°è•HÎ;ÏËMVà³&!,´ês.‰9«…ª¸@ô×lwÂ
+šGZ²N²M¼±g?èjºN EHÓàIEWç‰CÖwŒ‡uú©éhVœÉG×à¥êw5À³Eà@ ïÒáÙ¬ëœÕ+Éœz«PÉs´â5t†zÖ£ÒÌN’T~ÊçzDª +pÆüì}öx-싪˸ÿ*F€Ü Øâݱ¯7éU`<Iï7”¶‡ÕÙ7 ß#¦NokÚ”ê;<Ž‘G2³]D
+ œ
+endstream endobj 1269 0 obj << /Type /Page /Parent 1660 0 R /Resources 1274 0 R /Contents 1275 0 R /Annots [ 1270 0 R 1271 0 R 1272 0 R 1273 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 1270 0 obj << /Dest [ 1125 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 286 592 335 606 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1271 0 obj << /Dest [ 748 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 478 134 499 148 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1272 0 obj << /Dest [ 523 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 266 121 290 135 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1273 0 obj << /Dest [ 1125 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 272 67 321 81 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1274 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R /TT12 1611 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 1275 0 obj << /Length 3733 /Filter /FlateDecode >> stream
+H‰ÜW]oÛÊ}ׯ˜£ËÂbø%’*Ò
+"eÇý!ýý}è™™%)ËNbô±0`‘Ëý<sæÌÙç‹É“Å"¡˜ëIœP„?üdefY”S1Ÿ…y¥´¸š<ù¹-iÙJŸˆÚe=yòêCL—í$
+£(FŸådÚ?ÞLÌ{{é(Îr
+ÿ˜ÌeÐœŠ(œç˜8-Ã<ÒyeL"Ãùi–óèOæß Z§ILã4ÌMTÒÅ-}¸­» ZãÌdÚª¥Ÿ^ÓßþLgxßãÛ,œ™*ˆÌrsNnªîŸn¿•IRcëU0ú—üþ¾øËDN,§I¢0ÇËâÚd777a†±iyUÇËbj,â1
+³ôM™éåbSE“d–‡iSZ„eIy $ gÌJÚ»Ézò|1€‘ÆèÝEcD7bDŒ ‡•§1ê‘,òëA™ÎdàÙW?²n˜¦þx…ëVš&ó8†YXßi‚#îü[äÆÕÿ° «†» ÁGb8úaKôÚÞÒ¥«¶Xž(3[Q?3Nm–¾­lÌÐë`†Ÿ˜/5!-6•¬¤ÿeñ[Vv‹™²•5Çæ°¥ª¦m€h¤ýB·ÍA·)AAøršÆ!ROÅÃÉùQ]ujjœÊ¶Û#NØÝŠÀjݵÛÛ-­\g«-¾9ÈÈŸ[b&Îñf;ªŽ×5táh[Õ_‚\‚)ö#íLد {[üQC‘¡ÈtC &PŒæ‚Qf˜ÿ S&0e€IfA„3á,Œ2„8C¤Ë‚ôµ§I^arœqhóùUÁeÄ{G<ÞoíÒmšíÊí[BÓ̬ù²§}C+ÛY²Ë¥4¹¶¥¶³»† 9}j…Yš&Œ~ÅÅ€ÿlÁ{Š©@ˆÄ
+/
+9ˆ`Wõr{à¤âxKæ!{ÍÆ^;²}}ç`$c0â1_u{×îšzUÕ—}¡=‰ga™ v
+(d²ßÔ8§uråÖö°í†¼׋ˣõt‡XôhU¿, øȈHø?—4Ê$ rU/ÁœÀsm«œ ®ª›Ž´i'C‘“:«bln{®ÏÂ$»[]ÕWèæÙ'œ€ŠÉ
+Ý»î89‚–NyGD棈Ìõ˜¶ëöÕÅ¡s
+s.0#E[yÁ9Å¥ÅLuß´nöä¾Ú«ÝÖqQv_Ýò jß9)iV{ŠùÁ€ÃŽ«SÌ¢Ø7AT®¾ŠçPÒ°LG’=H„e UcfžúçLt䑳ˊVa˜†–Vviô¿,<3:{'äxçÅ«Tñ*üÄ^¼HüÌæ‘—ú> !îñüááý,
+$ð5nÝôE–7ÆUuª
+o­= ;)ÆNW ¿ZBœU®?`ãÂê7?pÒÛÄ–¤æZK}jpz‰ã˜¼³1†Þ¥ÙkØ^{ÁÛ/3—¨ïFß]-ȹrB|Ù kwÃæÆ[163£}1#Ÿ6°ÔuÅ ˆïv-Eš‡3ã?“OÑqÓÁ[Û·À³}„o–@ø¸>=àù_¿À\Z]¾ÔÍMMvÍÆœ—ÅM2éÝû¾£…Õ·_°e1ŸƒðÛ$›$;âØî
+×I]ÆâtþˆË˜¥ñ"¦ŸY£SS_N»Û#܆v‡ŽT†ÛØ•ëüõLé1\Þ*½K]»á¦çi,—6åòRø;Þ×`!âÄ{3¾•É0uažÝ•Æh¼ìô×ÛR*Ç
+<L_Ç^HÓF,J¥ß©ÿ=°%›ƒS¢nÍ—Áõýi˜Œý&þª©y`Ú÷Pµ|ýöÃË_uµsBQQEŽ½"?”,HÃqÞú<…-ñÑ,Ýêà‘TÿŽ”tþc-UìHG.]íE@‚7G±ZÝѲ}É«}]€’ø¸¿Ò~`iŽf)
+à"é+Ñ·X]~‹ÔÕ)©‹ä¨¸]0ó‘Ô³$2­UyåÈöSˆ#™¢øs­®j)áPwéö'|>¶Í<UÇÌ«ÑEÜÛûY*û»[
+ÒÙp¢Oæì-£0§·Ï“’p_KönÉ9d œ„SÇKðÂzK^¾yùó‚ø¹ö”£¶î¿€4ÓœÕ^’îAÖãê…®~³¯˜ä粶oÔ
+ëþ(Àk¥Ñoï_ü´¦—Ü…üç€ ÆÀ,퀲mîQä‘Ú¦L:&‘èLÅÚY•O¹y‚-,Ë 1€©ÖTõBÔ:‡sƒŽpîÃ$WÕå¦Jºn,ä%Ñ-bî!±¸´­ qvÛ¢lÕÍþ
+ô¹…Ùbvq#:¼{ñœËýÌü¬U„ë¤eä1Œ°òŒ-ËÞ_RbP2Öo¿Y¢V°ÙÓÑùT‹x¡–qα·]ÕÔ¸ŒTÚÒéÏ9éïAŒ´úNÏåçM™wšiÚÐ>Ò—œdëG>wù#úÞ/ÚC¶~<ÉÖGäé\ò´ø^žvÓ êQ‚D>.QE¯ÍÙõ7þtT¤cA:Qu9a³fnN3¡¦f ÖœÈˆ¢óÇ_™S©Y¨s‡ÀkŸN*;2’½ª•éþÐÒÇŸÞü&ã|Uc×rü¸©[nª Xð!ƒ­:¡_4ÚöÈÛv­<¬"­ú‹vnnM,Â~ööG¤Kž¯ûöÿÈQöÊÈòëăhb‹V10€~6£šA,u daþËxµì¶ Á{¿‚PQTl¤d=€ @QôÐs{kz-¦`G®ë4Éßwv—²(™
+$†(ñ¹œ9ýµÈL[‡c;e5Fê’6…Ö3˜/õdÖ¯‰yÆÓ Lz>° o¤A®KÖ#Çõû©›´É,‘Ïä®=C±ç¼t*ù Váï]ÊoZ¦Þë•~Q×´"ø£ËˆDÁ ÐW-øz[ËpµwéB—!!‰ú²®
+4±)9}»™@É´‡Û±
+×JL°ëèúÌP9é‹zèŸ릊oo¤4N  ƒ×4š±øFºË5SÝ$fÜR)[a¥"%ÃB¼àª!án±Ù¢è¼&ÿ„(8‘d;Ÿ…Q;Ø!GºïèvAmI8ÀFF6Ÿà¯ÖÌÇF>ƒ,W|ðER$Èɘ#) Í D ¿æ±¸ä,¸ä¬š€„,ã†Ô̆Êæ|ËÍV>žô¯GiF@Tó› ˆÌ
+endstream endobj 1276 0 obj << /Type /Page /Parent 1660 0 R /Resources 1284 0 R /Contents 1285 0 R /Annots [ 1277 0 R 1278 0 R 1279 0 R 1280 0 R 1281 0 R 1282 0 R 1283 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 1277 0 obj << /Dest [ 628 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 446 503 454 517 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1278 0 obj << /Dest [ 1412 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 146 409 182 423 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1279 0 obj << /Dest [ 1412 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 290 347 325 361 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1280 0 obj << /Dest [ 967 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 485 220 513 234 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1281 0 obj << /Dest [ 1331 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 205 206 232 220 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1282 0 obj << /Dest [ 1155 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 89 193 139 207 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1283 0 obj << /Dest [ 1125 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 326 121 375 135 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1284 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R /TT12 1611 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 1285 0 obj << /Length 3920 /Filter /FlateDecode >> stream
+H‰ŒWÛŽÛÈ}×Wt H4/I-‚|ï&°7»²ÎE¶FŒ)R&›3ž|Hþ!ÿ˜‡œªjê6rxwÄ&»«««êœSýj5y±ZE*T«Í$ŒT€ø™g‘?Ÿ‰J— ?É‚X­v“¯ûL=Ï T_4“?ޅ꾟~„˜SLfãããDÿ’ßÎSå­þ>Yò¢¥J™ÀpœùI vyMÄËéi‘Ðêßô¿"¼E7 c?ÑA¦ÖOê[¼‰´ñ`I÷U¯^þ8Uý·7[`ÜáÛÂ_èÊ t±ª»ÇÊþÃt5‰uÞ”Þl©Õ?ù¯÷·ÕŸ&|b>Mø «7xÇ<>>ú^쇺§] m ÓØÒÇcàGºp¯¶lév5 U¥&Ñ"ñã$Tqêg™JBDRáŒóLuf²™¼Z‚‡˜œGãÝ€"¢9zÇÌ„ÑÜÏ83IàãYƒ°é!þôHþ× ;ÓUÅwê“~ÜVÞÒOµ3yÓ{ ®•õàÿC[…3ª‡¼Œj7<ØT¦.•Z»U•[ŽˆD~¦;kJ~³7¶åuw¿¾Süvæ"< }¸ŽÀŠ{јjz"GÛÁª~èºvhʪ¹W_†Öšþ“ç+
+´^m‘eüëk£Šv·Ïmµ® ïÁ®Í6¨Ú¡#ÏoJx¢Lwº}Ìy¥=—Ç8-e{:M‚#âe¨ÛF†°¹i;£"?¤‹©zD ¤8vçÍéð¼1ï
+,¯
+gF”kõûÉðf'Œg«¶fb“@V+Zy(ÑõøŠ„
+ }¶ø9ß
+!wîGÃUÏTɘL¸ñ
+õö 6½Ot»ãl£+T¤|©¨B][·R‰%Æ;ÊÒ:YÌ$î“Q)ë‰/ãþ{ª¿m-ÙDŸxÎlóã¹/oÛZ\©BË!1Ž
+Ä24|º»EÌRp
+‘å­xöz%óežå–pßq£Ä_˜ óZ¬é°óò pô¨¼©r¤_| JK$w!uª9:£¸[\ S·#ñkú ‚{ÞÙQع<ðåö˜8×2ñ%¦§¬‰(ø6{èª”à ‘fn*ÊÖ3#_vt{Þ<ùסû\GépJçÑ™• Dõ©¶ínü\´óƒ—‘‹óܦCe9¯XDz#…ãZñV9¡kDûÔݯïÔGé~’W·2á×à­l…Dœ{Ñ{áœt‡µ¤Vé~k¯gà9ª©~l»ÏÜÝ!Ò3~,Q^jøÒªqiÇÌ¢¨œÎ@BŒ¨\¾®ê
+ÈÇ‘Üy¥¥ú Eß¼âŪ6¦Vƒ&š¸yùV®'YW}|+Žé¦+¬ ¡©3O”f“¢µÜ£¬æ渞˨Èݾ†]WݸNŠº‡’´kö¡”t5ßpŸiÏÆØbKÇ€îÌH)ÐZ{ÖFÜÐl ‹€¨ÖƒÝ}8„žcö­øUwƪ?”kp¸þûI°™ÊPæºT?‡Þ'w/mÑU±B§F½Dƒ®$½æÂDgÌí!*kâ€h¼9Sn§˜MøÌöÿOÏ{.RE¯ÿ—H=ï˜H¤\+sví©(‚ ½«êœU‰ˆÊ›R.螃${+“—Ô¸@Z¦¢_œ˜"Ç·ÓãiÐ,‘òÈ5ã”ü€ÿkpŒŽþGîªw.&éÚõ>û¢'R§ ýQRP{yûzù _¸¾l¡üº¦¦GíIöä§ðwÉ?%åÆfá1Ú§î+š±͈¿½LàˆŠo¸½*Kn¾Ðå=nùcsän0k¤ÿk8Ÿ—ÃQ ¨—ÉЃÂ\H®»pˆ#
+¦ì bn»›Ï±çR‚ ZD À¸q–*¦X˜€Rã
+þágt%JqëˆÄÞÿØ=7¨?6ê.ê¥þ³zÑâŽ]äÉÝ^Þ¿û`
+`QƒM]›×}‹^{ì²ñÜÑõpßå»î
+wÊØÉÔYí¦ÝÖ”¼*=gÓ>"ë}„éÊ5ó*݈H›¹ÝÁŒl?èm5pN¯^J= Ÿùu|ű»Gß(†wÝÔc1ž­þyÇ&hÌMxLtÌD»t–MÓþg«zh×’êØÕÿŠáw41wF
+aY롽t»µÕoðG"ÛÎ%›œ£4O¯Õ£ÝpB{—©Å¿Ó»•12=¾ ühþäÝL¢ÈDÊgäØÖ_I{<îW=Õµªî±»>–r à ßÐ@dú̵Eª›ë ”;®£Vg‰×9Aã‹•¿õ[»{,oF<7ÓØ?àe`ð’ Úá¯ðÃPµËKqj?ZøK2CpÇ“(šƒÐUíÅõ¨_ß0üÕ›»ËûÕâñ£W¸Ë'W˜÷\2íù0‹–…u1O Õ>ÏCâ` .b
+N#¡h]èÊ(äñb§ì¸Ä±0ŽU”Ž'«Ð -9×HVÄ£#m‡çù 8
+Qv‰*;d´÷D2„“ ‰­íX
+ŸÆÚÊ…4dfe£ïüÙâ5v< "³ÀÁr ¯‚‰V´—GÙ7WLìŠ
+XÈ„ >­tDËâ¥>-µíDðôd¾mÚg
+endstream endobj 1286 0 obj << /Type /Page /Parent 1660 0 R /Resources 1291 0 R /Contents 1292 0 R /Annots [ 1287 0 R 1288 0 R 1289 0 R 1290 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 1287 0 obj << /Dest [ 1125 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 224 633 273 647 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1288 0 obj << /Dest [ 1293 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 206 391 228 405 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1289 0 obj << /Dest [ 967 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 365 377 392 391 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1290 0 obj << /Dest [ 1293 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 202 106 223 120 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1291 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R /TT12 1611 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 1292 0 obj << /Length 3726 /Filter /FlateDecode >> stream
+H‰„WÛrÛÈ}çWLíCja
+ý#wÃØLßÓø_œ1üQ?:oÅïa¸^lOÑÙÓÄI(§ùÁ äDX_,ÊfàDF`¨ºü½W­3Á¨Æ¡A5·¯+•ÛsÛvYbÑ^öJ^éï cÏPÛÉF«;»(ËYU?;)}å‹Œ7˜°‚á
+¾±`JÉÊt…‡·X0%S€é×ëéç [&Cû¼È—KWÝ ßè9WµƒíCÜŠ~Õ£^5tŽH—Ž’–|Út¼šÊíg•E€äO2}‰kâ쀣=Y4Ò×r„éŸî±«±uBÝ;Œy2\Ï_jÞÇnã]¨ÓØæ™ ,bl$5 ܉n7ô[uü¢*¸‹[‚ãîbâaj¬U}C­!LŸË–¯…ÀKô{gL޺Φ÷·Ÿï><îFðòŒbµ—Ž•j˜ø@nÇ Œ2ô&; ý¦‹fY®Aõ èÝ-Ätx¡›„'kÐilË´Bë\¨¯€¾¯süÆ0*,‚'±=p5Çž#Ñú§×ŽDÁù‘½=à¿æËuyLXAê¹&zí–çö¼
+t§8b™g‡ 4ö0¸z²'*Ïì/LAÂáqtxßÎîùÃöÔ¢ýÇýì¾\¯jð¨ªwÆ ¤VÀUë¶EÎXnÕ¼$Ú›©¶ÙÈÈøéÖ .æ@B•!¶î‘'úFnÀÖe¿#Š:ôÖ×¼­ò§e9¤Šýuƒƒã>þoM¼žêåšh¿<%s7J¸œwÑîéÊž;1ûQva‡< ¾òĆ.µuHR¼œnüDçià †´aIý…3BYTBéP&Äs6…Pö˜å}þ”ï3
+õªMש9TcÄؤÛÙ!D”I–òj'P˜Jë‚ú£s ¥®÷:º5$.w|{„(s^h…Il}¾ZÃ' QŸ˜à‰tSÑ—‰.ÉYìÑ
+K¥ÐbqùñÖ¡QgÒ̉žcÌ?–f"º$ð­ÆÀC‘]/·oÕÒ>¿ÖÎ^‹1¡DnXn\©ÂÎÈÄÉÔK%”"4œ‰""þ¦´/ëô\2(Yæ®é‡ëO™cD3²¯b¤Ä><A‚OÌ1„Ô ºý
+/‘
+D¤^º…=×Ä›‰–®RHmØûZ`=htè¸P¥ŒG&JtQå}ÈV[µY”,ß}F²{ÆM&YD/mÈô(|MHÀq©¸É-çêêÎÚáõáL¥Á )ŽúzKçŽ5™„r¥)ÎwtìD@(rîãŒ#ÃÁOH9êç’<8@ ‹ð |¢ñÉwüz{)óNDÖTÞ~’„'˜@=ꎡ: †šBý\¶HI+¾&é¾€üšË¶H>Ëå“}¡`×_À@Vðð‚¢Ø×Rƒð·µÄµ
+åœf4EæÜ^Æ°ƒÐêDAÖáµ¼¦Š×œxýx…nÒ/JÕ<Ù½ÚâgYlaµÔæq.^Q,Ô#€·Ð-ʶoóD¿pA}‘Fo©÷“Ýžª…—Ü—›­–W^ò¨“þÌ[ ¶*±‚ë²Æ†d6ß/µÁ«µ&ŸÁzö#Žú¬ÐtXFJŽè<¢Q#FkQ,8;Z¦ëôZ?,¶>~}÷ï
+endstream endobj 1293 0 obj << /Type /Page /Parent 1660 0 R /Resources 1296 0 R /Contents 1297 0 R /Annots [ 1294 0 R 1295 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 1294 0 obj << /Dest [ 1293 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 202 631 223 645 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1295 0 obj << /Dest [ 1298 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 167 366 189 380 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1296 0 obj << /ProcSet [ /PDF /Text ] /Font << /F1 1609 0 R /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R /TT12 1611 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 1297 0 obj << /Length 2375 /Filter /FlateDecode >> stream
+H‰äWÛŽãÆ}×WtüÔV\6ïœ0—µá
+Öd“iûz˜È¯é›ÊO„5ÿm’ЦDDŽ„p°Û¡ÃçÒ—¶ã[âî_å¿]˜ºŽ5UžJ'‹£x9Íf\©-8IÖy-~ø þökÀ¸Y`2·™­?ˆ—CÞüSW:Ä“i±´¦‰ÿ¢ëïó?OÈb²Æuìóg˜# ‡ƒmy¶’5ÞªñZ8®´áÕ±]™™©5ôe>Q"7m/T‹ì8¡$ØèÇ¢Ò“ÕäqÞá)X✢ѣë "’ÐùÆG¾íø°À·=/Ž[P£ °#w€ªµ¨:ÛúzV º‹ûåb_ëjv'þúühE
+”¦Ù4Ë4¯âb¸©x|ä$œ'yè>t’àu|ÄÏÈ.\Ó[èvª,dÅ0s|Ù4U¾Ø7 
+0³¤hÒ7´OîhNg9â¥ð(/\Ê’e4@#Ì>`i#ø<ž_Š¦l–Ý!.Cë÷а!‹geÚnã,^Ü.^\6姲Ñx5ž—ÈÆ" We%ê”]¶óæ)ò
+M}ÓÈ9{J}P9&¨vi]‹mYi$«Ô00lo¬)æÔÚ–Š*Ì“ÐÅÄ0’{!~uU@üsÉ©Kä~.hh6^!õÄ28o‹ïÚõ?Òí{ƒ&Íb»Æ,‚¢ê†A(ÿr$– Z®ÙwÏó‡Ç‡—/ß±þ]
+`“ÑÐp\ºØhSE¦£è$=: £CÕ&+Ѿä˜Ùmñ3raHµ
+hÞhpµ»E½.B»Ô‹ý›Ø”o5uw•^éJØ(Î×ð¶BV8è1ÖtÂ^ó½[Q «³r öõ „­zÆ.4ñEI™Úœq¼²¦zŽûKM­j£ÑJ`7r®’ü¹ñ¼àÉ_¸y|6Áæq°%]#æἦ÷¾Èâ€ãÇÃø&"dB†¼%xÁŽn5=bÈ=bD‰IOö@ÍKâS^ô©˜‡xýÎ4büoš«¾±ÓØÍÐÍaîM)tþól Å©íÎøÑ¡0
+ €›NZÒhÐ’¶$¹oÊ-ÄeF½’Yµ¸Nw»Ž\Q©byQgúðèík ¼T°fæÃeÁ!Gd)jÍ=¿ù¾éªÏ«DìÏcË O‚‹©œ(]¡‰Ü…tqåGƒÅý×®´qeÛd2B“3¨dL—´S7¶}÷¬Ó¸ÈÜž“{ºRQßnøPhiÄŠ\QÂûfS0ˆšuE÷ƒë±n"oDX*kÚ%R#5;÷vEÆXàµ|É­ € í!¶Yº¹3ð}¯Z8Ò295
+olŽkÆ×Âþ’»hZÝoŠ|7ôoh:&×Ô÷†šŽ”øç—Ÿ>ÃìkôóÏŸÑõŸÎõ
+n„Ú¨p\/O ôj»òÏõ±È¶£:ÿBÝsuü!8*WGýáto…à˜°Ý¨n…à˜pTc•¼‚e±9^ FC¿ÐýñKÕvbÚ„çBOÏ¡vnEä˜p\ñx‘؇h¾A>”•¹ÿo6­&ü¤>”_só„ʺʱ¸]I"•ÜÖQá¸Ê‘úß&‘ŠoDí¨p\¯ y7‰¾þòü™ùÓ·ä“f‹ýkEïº ®‘^/¸W¶¿#nï‡9…sg»¯Èÿ;
+endstream endobj 1298 0 obj << /Type /Page /Parent 1660 0 R /Resources 1301 0 R /Contents 1302 0 R /Annots [ 1299 0 R 1300 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 1299 0 obj << /Dest [ 1293 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 353 658 375 672 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1300 0 obj << /Dest [ 1276 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 188 87 217 101 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1301 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 1302 0 obj << /Length 1948 /Filter /FlateDecode >> stream
+H‰äWÛnãÈ}×WÔ[š€ÅáÔÀ`=^,˜I¬§Ìä&[³©ˆMËÚÉ?äóSÕMJ–gÆ»¹ ,ö­ºêtÕéÓ7ËÙ›å2¢–«YQ€?ü$Eä'IQ¾Hý¬bZngoÞ÷U½Ì ¨¯ÚÙ›îBzèg!æT³ùøy˜©åƒ¦0 È[þy¶E Ê‘Áp\øY`íÊšH–óWšñêOêozçQàÍÃØÏTPÐý‘Y£'RÚƒ%Õ7=}÷ÃýñïÞ<E{±ÔOUãªZ_ÑÝ¡1?éýFŒÄªlko¾PôWùïýiùÛ™D,ÑDŸ¡±¼EŸxp8|/öCÕ󮚷…iléã3ð#U¹®µXú~9 ©¡Y”f~œ…ç~QPIBŒIA{=[Ín–qˆ)Ás4N茈ôpF‰;£ÄLHü8.r²ÍÔ<Mý<:C5XŒ¨¹‰ýõ
+E×õý®ìûwoéÃí—û¹zïa FêÒ”÷e¯É+&yaˆáþÐík ¼).3fr!ZøAqæB8zZÞw­)›V×Ô´oÇ\Mf]"f~>žÄ”S–°‘ëÞË•Þ{)~t¿ï` grE×Õ¦aŸc¥mWkÞ]:>—-0ʹzëÀ*&°
+çkÙReýµ®¦ßpÕ:8šˆ¬…]Éy ôú¤ŠA\u{rý•íu?Úþô}ãÍcÕ>xóî“d<Ÿç#ÃieŒ\í ÷K}7쟛ûzìÑäw˜‰ãÖéïx]¢ŒÙ7÷ƒÑ=7CõVœˆ_9,Î3dmÛµZ2÷賜N.|‘?>©¥—¡´BÓã“Lù@ü±“>]5+/c¨u>k;e­‰S6Sð:QÈYL)Tí‰ç¶X"Àú—¡yÖ®ÉtTV•¶«1®ŽŒk#"RâB'. Ö@¥ 4³æœs«ž£í(µåV\sbªÎ©P9ä©lŒl€1δáÊ •” î`g‰ß™â]Î~nÐgÕٞˮ`>R¸Õ•iºÖ.4vFÓ>¼»"g L ÊÿìùôÑãP7ÞtªËg[Ѷüýœˆ9oaà?º1N4ëÒÈöØ{~»÷'õ,®rͦƚ¤¶3î,¸iÿ»é÷˜fØ-»­lÔ ÆM«5Çî`¨µƒ£õæN£h ŸuÕmw΄tn›çÁ»Á­u”x>úç¨ë¬ÜTç£9ú/O|ª°_Äéé"BóœÓ“‰¦¢‰Ó3!kºÞíõ£n P—ŽVËOeJ3“ýNÚ|K*ž(-C½6Ù@•´9[Ü2¤Œ‹ÃOâ´à`’08£ÌsÚ(-a`i¬À¯ßi’>¿š»BbLO„—Z÷]·Ñ°óXnö
+ð¿OÞI¾#y¯!ͪ’Y¸¥{Í9ÆEgöƒæ_†H¹le¶àyFÊ츣|r9½ÝašÅ¤Ã˜³× p>•SààðÛ!œÛÿ"Ÿ#p‡Qn6`S®ol¹µ7¿w·r½oNvœôSc¿Œë8¬›jkFª}i;цŠÌ96¾ÌþÈE0¶]ݬŽ. L0^¤ž#ë™/¹ôŸd•$ŠÜY¬ž
+±E±4_Dõëðï-ÿoÆvÇ[½*‡±
+uíæøs˜ï_¤e±ŸÅÕWœèÉ k†Ý7Ø÷,W“î!w'9)nè'ü(ëLö*EF—¿ä’ZàÕÄ/T€Ó;£uÈ”ø ™)+t´EyYƒl…|öþ«jò?r\ð?™ öÞ&'cóóÛdyBq)I˜°äsÜÒµîðíV×MiXA¶|á*a…Æ[ ‚AÑ¥NíIEó¡*¡O-Ü‚zT¬þ˜ÐQ
+ͦÇ÷ËÙ?
+endstream endobj 1303 0 obj << /Type /Page /Parent 1661 0 R /Resources 1309 0 R /Contents 1310 0 R /Annots [ 1304 0 R 1305 0 R 1306 0 R 1307 0 R 1308 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 1304 0 obj << /Dest [ 1502 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 279 165 292 179 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1305 0 obj << /Dest [ 1520 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 71 151 94 165 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1306 0 obj << /Dest [ 1404 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 290 124 325 138 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1307 0 obj << /Dest [ 1502 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 117 97 139 111 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1308 0 obj << /Dest [ 1508 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 92 84 115 98 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1309 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 1607 0 R /F4 1608 0 R /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 1310 0 obj << /Length 2618 /Filter /FlateDecode >> stream
+H‰¼WÛŽÛFžê+~j#šñ"c0€/IlÉb´//ŒÕ1¦H-IÍxò!ûûû°§ªšG3v $6 Õ·êªêSuª^­fÏW«H…jµ…‘
+ðŸEù‹Eªl™øiÄjµŸ=ÝåªèxO º¢ž=ÿî:T7Ý,ðƒ Äžb6~ÞÍôÏæƪ0 •·úu¶äCK•þ2…à8÷Ó@äò™ˆÓ¯$¥Ó¿èÿF˜G7c?ÕA®Ö÷êú¾îw˜‰´õ Iwe§^~w¡þù?ož`Üb-ñ]z.vêú®ì³mÅBbmê7_jõþëýkõÃŒ-fk¢ÀO1X½Ákpwwç{±êŽnµt-DãJ??Ò…›Ú±¤oV³P•j%©§¡Š3?ÏU“
+6.rÕÚÙvöj5:#±%xè“wòˆfïáî~°À†…Çy¦d885K?‹&^ ’Á«A,6Aߥ—û ­.ûÖÔ)ú²á‰z/ß½z¡VÚ—ù™vTÙ5•ÁþZa"Ñûfà ð yCü Uós8úEK?È'ú…ƒz¡h÷º©{SÖv£Êú;9;‰u(Mýlx¦7#„HÈeçeÚ¶^âçúÖ}¯ *^¨Ë¢*½0¬LÕýÕ¹âs¾«ä7ΓùèÉÜéjjUˆ¾¢jò)Uù4‡ÅÉíS‡Â—vxêGjD£aJ‚~Ñ/ î Ý÷m¹>ö¶£a¨I2íÓ. 2VÝÔ¿ñdxÊoìÖ«^LÍïUrèÙÆnáÿHÓQ„ÒR?“ƒg¶M œîgeÃñ}¬vúÞÜÀ{êØ+}£pÁa;‚U}ÂÍØ¿¶|ÔWß÷ªÀCbÜÙ^™Nm›ªjîºÎÀo}¬V”ž½å_ØߺØDŒ;ŒQ;dV ZZyöÐå8ÊŠx{9¾ÍC ‘,'O˜œ¼’°&ÏÔ U7Oj0a¾`²/mƒ„
+òæÜŒe41#:™‰/è]”;{nN³å§ýéÍ«×jÓ–îs ý<þ:F†á­5›ù±.š½7Ï@Sû²ïÇÁ,Ÿ¬–££‚4Ìn`Yë-¾ò~½Qm¼y„ f(ËŒ’µÊT•*v”Ð2쿱n}üøjŬb±t+×üXYã‘Î2Õ+¹ÖÒuá0IV…ZV@ªÈ9ÐãÖº(Îý(?‹×üätz3ÿ,ÅD~šM±r"¨D\ñýVÝ7Ge?l[Úº€šMñž¢Ü¬+«m³öpo¬+»ï.Ì¡‘û= SÖ70 ‰d†\
+
+a~VA-ÇP_Ê5?#¡rYH Q Y7=øygzü2ï9ÇMR¾;¡Ü÷x84m¯¨,Ñ´a0ÈdÒpŸjÔT«kÛ÷H$2Œ+èÂr|ºÙ5K° ípJ': ’X‹½¹G¥ß1Õ×¹Ž+eÛ¶i;uÉ–{ {kkÒHÎrœÂ
+“–˜nÛ6{u× ÃI¡Ü{:bØ9þèýÏé9zêz 2b16aàQbU— àâØvM[•k4'ÇŽÚ90ðOo€¼ÏÊ6…}-¨$¦0 ðiï¿X¤Àmð§6q8†•´”éZÄcÊ
+oÙ\ºÛÔ£
+lŽ=Ø$ÓÃ4OVÎAƒ@‡3Ò_ÄqDæ,‚0;½áâ<ƒU |ß*skJ¦§ŠË³|Ê$eÌ(Šüeô§2f4%Lg¤zíWå^ýQN9â%ÙD¨†MÜUÒͼ8W!dú¢~’šæSÈÿUÞRJñXs Z«Ø¸f§ Eê
+è+‡ÛLBÀgß„v‰qGö)w\B×D<ꦿãTâìæ(%'ÚÁ¸7"Y&3%{÷ývÈ(¬cÄQD›¾YÍþ?
+endstream endobj 1311 0 obj << /Type /Page /Parent 1661 0 R /Resources 1315 0 R /Contents 1316 0 R /Annots [ 1312 0 R 1313 0 R 1314 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 1312 0 obj << /Dest [ 912 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 89 602 112 616 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1313 0 obj << /Dest [ 1311 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 448 259 475 273 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1314 0 obj << /Dest [ 1311 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 403 232 411 246 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1315 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 1607 0 R /F4 1608 0 R /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 1316 0 obj << /Length 2619 /Filter /FlateDecode >> stream
+H‰ìWÛnãÈE^õyÙ&`qxe ÌŒg»ÈlXOñä¢Z³©”5·äòyÈ©ª&©[<‹ä!@0Åf_ª«NSõa1y»XÊW‹õÄ”‡?<¢4p£ÈKÔl»Iê…j±¼ýئ*oyŽ§Ú¼š¼ýáÁWOíÄs=ÏÇœ|2í&úÙ“Q~(gñçÉœÍÕÌsç 6S7ñd_^ðrú'´úQÿ=Àè4𜩺‰öRµ|Q/U·ÁH ƒt[´êý7êÿp¦1Þ|‹ÝXŽ§óÍz8Ý_MSò&¡Îª•3kõ7þïüiñÓ„oÌ· <7ÁËâclÁáppÐõuK§:[ãH?=7йÚðNŸ_jĉ&¾
+gnšªÄ‡'1“õäÃbpFècŠwêÑ»yD³÷£ÈÆ(r½"7 Ó™’×Þ©³8vgÁ‘WñÍzÕ äN°×œF«wyi²¦3_ùµÃ]ôîpw«øuWfE¥N>ª]Ö¶m y³nV
+“V™ÌÉ–ò£5âYžk°4˜»^zd©ßê‹몃†N¸ewÏô°­ÅkâÎú€aa([Èòw­×jÓ8 °ólŸwÅAŸ›7妾KÀ½·žKÏ¥Ö¢¬R¹X%Åß0ÈÇŒˆee]Ãç•zÎʽãû€Ð…¯Fc‚Á?9ºÚ{Bz¤»®)–ûδôêk2ÉGL^7É›1¦ªº2=®^ñÏ}Ô÷fíËNn¾r„xŽ3Yןpv#L{ãiz±A"×»®À²/º«Ÿ ®QÈ^gšr†yt¹wÛU¼oMCH¼S­)MÞµjS„ Ñ~åêÁ4Ï1Á!E*ÛãsÕy—Ѿ›>ýGÛlšØ˜õƒuñ)‘PÇÏX/í³5·Öß[gð~Ál¸+Mÿ†ý½Ía`Ð:lÈîž%¹{
+é’K°RµÈºê m¹ t4ϼž|qÈ}yVæûÒ™#‰
+“ãÿ:Pá_d$5],ДÚÇ`8ÓEaïªî’¾‚s†Ø‚ó|cò_ÈØgÏ…ywzþp«]Ì2Âdt¿iÖu³¥Pð9BÍ8¯”4½E}ð—}јЂÈŽ§H$,‹J¶¬×ì7"Á*Ûšì_âyžhX§È$BtfWxÚ"b¼ØEA'A 9ßx²n„ýIÈ3zaš~Þ 
+‹„|©Y€Œ1ïR¹`$.8 Ÿ’~d©N&ÈÒg*Ð|!€Äæ:е“÷zg?”ÍzéwÈíÁv+;Ëh(±»“ƒG§wjU{XE„LBåó¢`{<l‹Ó‹ýÊ*¢%+‹îÅ…<uiˆ­e ,3.R˜ðPÕì–Ÿö‡½lµDNœ3\ôA°¬fDT†R­°ÿA¨Œ®“V2hÛ-CYí ©{#DÅóS³ê%I¸¥ÓHj?ÿNA@sôûú3<åköÕ&“=”<jæÑòÆ’q
+I˜:úô†nqÛ+oÀˆŸ¾ã†óU—é¨ÀXg%•'­1ÜÍEÄ‚Ôó„Áôªà_¶#ùÆ6
+{«±‘AÒ4+Oj0¾£>3eEH´T)W¡¶$7640]lf¼;éRè²7Ô«qÑSó¶Ö “Áç{^$#®ZPÓcføžûþQ?ðä9Ê`îGå…Î:ŸPe,©‹ŠŠNødÔ[éaܳ]Z0"dÒ‹tVÀ¿³ûäãÓL³¢”ATxØåó‘_ÎŒë§7Þur”fMÊ™Ñ|}|f÷B”)Š•5'»¬€,HNÓ Z·Ï¡_#q~Š=Òc‰‹F‰‹X½” ê„Œ¦ðs1Ëg«tmG5µ eühQ 'g’VëL% ‹¥…a@·‹<6Þo$q=$ð) _ê¤âÀˆîòüÓÉ ”UkhzY´€ÓøxÕk÷¼è0~6(tq+ñ99ì>G0†.F\ õT¯ƒ3½þ´˜üs
+endstream endobj 1317 0 obj << /Type /Page /Parent 1661 0 R /Resources 1323 0 R /Contents 1324 0 R /Annots [ 1318 0 R 1319 0 R 1320 0 R 1321 0 R 1322 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 1318 0 obj << /Dest [ 1311 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 456 757 484 771 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1319 0 obj << /Dest [ 1241 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 236 262 256 276 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1320 0 obj << /Dest [ 1241 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 155 185 175 199 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1321 0 obj << /Dest [ 1249 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 294 135 324 149 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1322 0 obj << /Dest [ 1249 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 234 71 265 85 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1323 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 1607 0 R /F4 1608 0 R /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R /TT12 1611 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 1324 0 obj << /Length 3737 /Filter /FlateDecode >> stream
+H‰ÌWÛŽÛÈE^õ‡Eš€ÄåýbvíÙìx X ž<p¨‰YŠœ•¨ÑN>$ÿÌCNU5/Òhf×@
+9“~åâ×vo̯|b"$ºS]«ºa߃™‹ˆ]ª[¸hùBîääN%‹U¡Â`iÈRÈ»/yÙe•ƒó}ݬï)ˆbE>auµ-jB¦êìÎ×ÚowÅÜí]mTÙnº©¦X맞‡ƒçÙèyf I(‘k€àZ_;ìb¢H…X7%¸ÿè׿ƒ³¾–çkî„ÚUﱚ,
+õ‘ŽN…±¶Mã3¡VÛÃÉëQÓ÷V¶£ˆÛ¬hø`õ5OJ»XGÊ4e»ªšµ+ÎBu"«:‘ëE 7v ³TÉkOó$Gô²‰N¦4'ÅN¦Õ˵éVæ¾*Íþ—úÕµ/syèÕ\½Ü÷¦i :¾i¾ ‚…qF‘ˆ|c!Ç&C02_ÎåMªæ¶¥Ó^¨7|˜‘ÁÝmÑ8éÍÚðïÖ4Ýàwv®¶ƒ³QäfÑÄY¿÷Õùºmº¢j8»^0Ú©¶µ"ž@¬Š‘¬úƒÝAowYW0ç KôœU€8 ÒÔ òÞÛßWÁ Ï _ðW#Y§\}]4 ;Û+¦Æ¿aª|eyËôeÂD8$¯!0eàiË‚Á2Ÿ#gý+’5ÈT·«n¢W_“}>ó¼}¬°´1¬ÉÏâ3ðõs[êN È>ñˆ3&áL+è{_|ƒQýsRÊî=¿ÕkyT%³ië•"Œ­êåœÒécÌ„°‚ÎTË‹Áz¿ïr7ÈGûG²Y®A¹@rj ÔæÌÈW,ë?êÇ%#Œ \ß;©[e<ç"e¿+ù%Ó©á=Ô‹ póè´¦<jU>jÚ®i;µ2·xó©‰
+I ª1ÒRñ {µA•+ ¢oqL]??cÝX›}[›7ÐDø¶" ¸¼
+}
+]¤ Ê4¬uÕ¢“€¢Ô3˜‹kŠâO k–)£ŽU ñ(j'Diª{OúÕ0aÏ›W2½Y£ô‚2ÿAQ|©5¸5à¾ÇˆêÑWª²®„HFæݭæÇ$s‹K½Ë…¬lÐh ªÄ(›–Äñ{Áå^ÃÈ›û¸3Ôl*ÏQ(³ $Ú¦ v C…žJJ$%ºP¼
+]?ŸpfW}Ït!$ó'Ðq3›•–b¸ï:D]¢˜/Kˆb–NÁ¹Pº+©¨0¿S?7í±Q¦£®¨ÿ0-ÜV¥,zý(Ó&Š™!Õä÷B¤t£Ìö®{8"‹œugOè—»jÉï-­*gnšžJ¿(r¿ª«¦¬+F!H8<€ ¢Ù§!êˆú¢”ùpõ–3êŠ|óõë%ëöŸöü£~úîÊÉô_¥]©²–+òÒöÌռLJʥ*)ØÚÏY¹W²A£ž•]ýç¶#˜Ða£V-lÝ1b‹ u³p$ûGý…º«‹ÒlÚzEõ[T ”<q‡¦V6±#½"ß}Rë@òÕµ¯T›ˆbÅÝ]ý 6ýŠùãã+bÝÊ£ÿIòYG>ê— ë”9®ä—‹‡Ô-@ñ: t:Ù#£œ¥ë¿´—Ks¢P…÷ó+ØL•VE‹ËEÔýd‘M™,³!Bª"ÄdòïçôƒËÐÔL&« ’ûè>}úkî¾Í”»É¦£B€DÐ>Fþè×›J2XëzÂóÆ
+g–2«zcç¡o}ѳr-7›ˆu» ÝÚË­f´¼è8wþø¥©·ËâEm?—|Έ×ãÏÛvï@dï¢JkOTyÆ‚wyÊ} Á­]vžôØT°…šƒ ãÂÕ Ù>qj{4ªÔü’ÂAŒhÔãòÕJÎÒ&}Leu"\ɪ؆ H>ЋØ$žôßÞåÀà‡.ì1l'­P¥U²ó‚ëD+òÌ2™`&Jå3’µÍ˼N›ª¾Òwx·dòrÌÑ„p ©‹íVÀõ–0Xç]]?(ÜRòÀÌm&»=Hàæöçõݽü.¿’×ÇmP×ñXWÆ8‚iY·
+L 1Ùq -W3ŠËó@Tгý"Ç›rŠ ÍàMéöÁ@¯Kl±ø)]A=˜ÇÄ@9bĈMüÿöœ˜N
+'ðQó™š0VÒ£t²GlÕ
+1™›1ùaBÛ:gþ;e§Q“%¢ÖÃ8´r81y^ú0e âNù8$ïÅËO‘kžfr1o9D#L5JÜÉÜ.Zäþ›ô †î¶=¨€Öº­[®°M¨¢9ÈTnÐDz¬hŠª¼rN0]KÀˆÆ6]I2ç[!…4¾¦Ï‡J¢ƒ•ú˜ˆ”é¡»–²p… \x•þY;¤¯y!&CcÅŽ}À’yèeØé)ŒÜìBm¾Ú7‚ò*ÓçàдC
+«Z5ŃÃÀééCÖÂ
+k˜P úyÅZF«t#_eºÓ§ÞâŽß\qnÊEC$:Âm.šQÀÝoéM€‘t¿%Éü©Ès‘ŒgzfÑ>£^Ýv77zs¬ÉÓUÒ’+g 8€,Å?Œþ¬b¤ÐŒ¾N…Fái{¬‡^Ä[ï{í!ï;w` £·b&7-V˜ÅÛTk±NMn[µ ¬˜qªNæ&Óµë°×>¿†`þANý®â‹ƒ@ð½¼;¹#¢š]‘Ú%†üœ&DqjÀŒ3#µ
+endstream endobj 1325 0 obj << /Type /Page /Parent 1661 0 R /Resources 1329 0 R /Contents 1330 0 R /Annots [ 1326 0 R 1327 0 R 1328 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 1326 0 obj << /Dest [ 1249 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 208 430 238 444 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1327 0 obj << /Dest [ 1311 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 284 241 311 255 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1328 0 obj << /Dest [ 1346 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 171 170 206 184 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1329 0 obj << /ProcSet [ /PDF /Text ] /Font << /F1 1609 0 R /F2 1607 0 R /F4 1608 0 R /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 1330 0 obj << /Length 2645 /Filter /FlateDecode >> stream
+H‰ìWënÛÊFÿê)‚,‹æýøplùÔmâœFJŠÔ.him±‘HE¤¬¨Òwè;öGç²$%™TšÂí¯ƒ wgwvîóÍ›Qïx4r„-F÷=Ûüƒ/rLϳƾD–+FóÞñy‰qAg,QŒ³ÞñÏC[<=Ë´,ÎŒ{ýêsÝ“¿$Jؾ'ŒÑßz1]ŠEh™q
+¦;ÐnM?ö/=é‘ÝTìÏšÚÝŸ;®‡\ÙÃrê^ƒn£½!4ïi4ï±Y<Óu£P𲂾³c,¿Âß–ËÐ
+­À'q²*Ôò‹Ú í^‹¯´¹R˸ϗV¾Ä"Y rV¦ã„ˆÀl™æ#//ÚŸ6j¡œ¨ö£¼J&›E:ϳ2I3
+>;I`bIvæ óé̲7Zì™ÂjLaU¦¨4ŸÀ@F¢ T>áÇMH ˜Ó ¿Éd¬¦ùl¢g6£Cî²@5Ó R²Él¥
+XÁ
+Ž‘´1Å ]“IJš¢MÚ¦­&mÈàAe”w¨·-g‚gÆ ê4
+cƒqø(¦„Ô­,”XXLûÖ8ðlÌèÉãh\£…aã­m‚ ’x)á•É#†I:Kîf
+ý÷dÄâàn%”;#d¶±ä˜mp›Jà^ª3/lÒ,¦øù‘'Ê:‡@ǸÉ!»©M¶®Md”'zUÔüAnÌ™–>¤˜e¥ð¦È’9(®Ok³„ĸÛƒ—æy !MHØ
+¬¦]»¯ä¾ŽUj]ý¸è<_bÑ\¤ó»h ¯ràgF‘Ò9í£ZhÏïît=¢Æ]l›pš¯Å Tƒ»øLÑ*z‹¢
+K‰§{Œiô¾#+€‡ÁÆ@Á«úŽVb+Â&ªrbôà[`ó,ç ö8ªÙà™­R¥¾¥|*(D¾š/Êàó”謆?M³RhžL,)­õPO®™ A8'Ý}SÆA Ú0·‰ÁŠ×Îs¯¤Õ®û?5›ùÄß.íèӀ܃O±gÔ͇GÔ½ô_ö╈gè=Üõ ¿u¡ßºÐ3w¡Á¨÷ï
+endstream endobj 1331 0 obj << /Type /Page /Parent 1661 0 R /Resources 1337 0 R /Contents 1338 0 R /Annots [ 1332 0 R 1333 0 R 1334 0 R 1335 0 R 1336 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 1332 0 obj << /Dest [ 1346 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 171 757 206 771 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1333 0 obj << /Dest [ 1346 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 171 631 206 645 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1334 0 obj << /Dest [ 1331 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 275 563 302 577 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1335 0 obj << /Dest [ 1325 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 389 174 415 188 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1336 0 obj << /Dest [ 1311 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 455 147 482 161 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1337 0 obj << /ProcSet [ /PDF /Text ] /Font << /F1 1609 0 R /F2 1607 0 R /F4 1608 0 R /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 1338 0 obj << /Length 2993 /Filter /FlateDecode >> stream
+H‰ÜWmÛÆF¿êW ^Gšï¤×lßÅu;®¥ H}E@Q«cŠTør²úCúúû¡3;KQÒ‰º¤É§Âð‰»³3;ûÌû«éèÅtê‚ÓÅÈqÁÆøãÇ®åûvÑ8°ÂØö`º½x]ÇÖêŒ uZŒ^¼™8p_l˶<“ŽÌîs3’{ N€1ýi4VLcˆlk¢`/¶B›å*W±ÓW÷'ñowM×6LdzBaÇ0ÛÂd[4KÜq…4P’¨³^¾¹€¿ÿÇ0\WH ¬@d†-ÒåL6YóOYåJˆ'’bn˜cÿRLÿ2R/V¯qm+ÄÅô÷”›ÍÆ2<Ë5Ý*éZWZøi[®HõÖRIºŽÈ`ä¡å…x‘Ç:ˆ$àý*9ZŒ^Mw`x±Ñèѵ ¡Ðë-ãØ!…¶…ÿ™a³ÝþôIú/Ê|.«;Qßp' 3FjÃG˜$ƵðÁÎaÁt‰(>$†éá[òV.›JêFÎA2s·‚º©²âþfm©Þ,`&¡\tg
+
+Äæ´Ž¹1ÇŠÄ––žXKh–݃uLÇÂg£QÌþ•ý#Å2©!¶HV³ì¾-ÛZk…X¯+YË¢Iš¬,,㋯µ§£Ð€$»‘6wÊ6àc>C£i`3ì…ŠeûLawjÑa¯ü%°|¯Bz–+|úq„‰~
+M,Ðp•òªäÕ\çðî&`^fšg†é¢î ÓGIúXÝÞý}ô~>WèƒðŒH‘hiÛµ>Y]²FëDM$êz£©æŠ%act¨Q|ß°¢Þé"~ø[ô‘H, e䢄¶6H¤¬p߀ñ¸$eá>{ Ý$È/j. )@®ÖÍø¼$•cÁä–œÉyZ&tn¼ˆJÃ&£×¯ðÉc|†oÐóÉAwÂ% å5:«s‹ûØWÂßì+'ó$A`, e«B"B
+­XÀUšË„¿IùXÈ/Ízc`Ö×lL@LðõÝùÕ<@ÀC¡¬ZÐÿ&¡–ˆ6‚¨,<iä
+Që÷@ŸgvF*&¤U4\’6ùÊBî‡ëÎ=œ kÀo­Œ€l„26@Ê)€vé
+Á;hhü‹vÞ,ÑGHÇ´ÔoùKíSâ@“&]J} ÷¯ýƒäkð¿n ÊÚi`e÷š«1<]ÌÑi‹T¿gúGvó°ws]je3ÂÈœ—Éåºo^n2•üT¸*´0)¦ƒÙ£×¥eÞ®ŠË#³Ogëñgµx¦2b‹¢‚Ïrû =Ê—0U”%¢ï‰ŒA÷›`… Ú@±V­’ÍžÀºÊVIµE(Ò<ycV4¥‚¡»©‚&™åãQ™ª-²Ÿ[‰ü']¦/cŽ.cÙ2Ãð ªNÆ$±cÑ_1fwØ|›e–.±©JÔÑç@"ZÕ6ðq²>·cF¥øëèšø‹YgZb.ÓUÈô)•hq ÎsM›'Mò¨œý¿×lïwªÙ»¢Ó£æ÷¨ù:ƒ.öb>-Qn¡D(Ä—µµ¬tøß b¾Ù‹õ¤’øÕÀƒ1FÃ'ü¼<›_ð‹Ç69²r‘-åÇæôS9áä;*¶”7 ‚7PU†â“'V†IXcÁ£ð麥ÇŒ@­¦àWnÕ Ž€|à‘Kõéâº>ºzè Ýû²‘—ðý²K–Ž K\åå=å/²*æÕô3“RJŽ
+ٱ
+U¿FGe2»¨CþãˆèÎàÃiŽO
+¤¨ä–"VopÄ}w뤕—i’cd‘Ôéê-‰J3T
+Ûɬ1h ‡\("* |q dÝP¢Ó%ïÏ%ÿîKÊôÖ\.²"ã0˜*‘Ku»f‘Ì¡¡_»£ê@áªeuä¾:ŸÉçC¢†«d 3²¶ßU‘³Ñú7 zŸ¦$ˆÏ[r*ÏÐ04$uÛ…@­ÊƒC;‡PD”C ”µÄ7Ÿ°›QB›•TgTWj«ÞçÈâŸÄ©
+–P1Öß[ÜÕ>ìÈ×±§¢«‘ôK­9qRÎÙ=æ\åÒlŽPuBH-Ì¥Ê:ûî‚Ç÷Kç¿-|M©ëÆ‚Ç•ð(’½:î½:f¯¾ý’¬Ö¹d³Q­#¹>]™{ÊjÔ@¤bsM­Ÿ¥/
+¿f׈8]+Á}op¨Ñ'qEéÛ†]grûÍíë©aâø*¾›Ü~üñ¯·?\|x9™|Û_üöM~xÿúG:3áõ÷¾ýx«®¼Žbϲcü1VLv-?Sý­ähñ45wTßE·9 vl¹þ ûä½»}_S¼ÂÌ}Šþj:BÃÙøâ؇ØBú³ÑÖ«‘‚ííÍŸžõÝó«{Ð’á÷ ±Iµ¤*d˜z™ÓìO»»ct³þá{Ä|šŒ¸ìu­;„F(z„öbÀ;3G=Ž'Ç!EÔ³Íÿû¿Úÿ±É¸yûæv2=G~ïÅÖ0ÀCDe[E<cÚ“Ìç©{÷zî— ŸvyÓOÈý¤c‡ÚI÷?BÈ«Ñf@ÕAªÂˆ©g@:Íþ¹»ÛõHì0Lô'"Àû¹xÜÛ<Š€Bâß®ÍÐ%ÁQ%vh
+UIÀbP”Ý1Žî¨Üòþú qºÖµü ¿hÔtØHnôp„5^õ°ìÜF¬ä® 8Kr“À[æ/œ—$õï4»P+oÖ 6õI5‡‰êäÿfP§ý ,Ú"UãÀ³W¡Áùl×=ÿöb÷¿T9/Œ¹y1‘†]øã9ÚRò`ö¢ŸŒqÏGŠi(C—e¨ï¶î.žõþù±þî\©ó(¯„ƒRHL=ƒÓiö'ÈÝÝ7ç'‰ä¾v2_¥ð-¼&^vñîb¼.ƒgösî¾³Sß1uWÝÄ¡–Ø]¦U†ÁŠ5Žzc­®/!Å9µ\:ÇÕoFäH †fÁ¸gד-*e&7Â~Q§rN?»©—õ|]M’ÑP•—üÔzwbu QŠÕ‘í9û!W+U0m…˜ôï5iî‹cõL%èÄÐ÷I¼|H2ƒòRžÌTó›óu9¬ ïí€×öõ>&µÑß*Tc2©¡,òí°:^//ÞÉÓÝúkœyR‰ñ žÀÇų́¹€,†Æª$Î\1³²Ì%Ê}HòVªz1Ëï´³VIŠaM Î¬^ø«ÕãyH‹Ô¦ÇÈ ÷à%A天²YÛ`EÀ¥£T¡‘ê U"„e¨ø% t®r#I›7üÚøÌfG÷µ¡Û@Ûƒ®<zßà 3{i<Åú4Åò «Ë¬ÏÃc,ÔE™RÕú€ü"S„j‹²‰.º…ªÜh÷ÀíÙ®Zªþ¾æZe`µiË·&J \ó_¶«`µa†þŠÌ…:%[aì¶Ã`ÇîÜ&0ƒ·ŽÆ0Ø×ïÉz6©×“mY’%EzRFé“ogùÛ=…„̺ÄpLh­Uœ¯£³Û×ífM6·Î6áÕ¥Q]ú‰)™ã Ј9†XV&\àͽÅ<S’[%;ß”8S½Å«ég‰b‡o¨å [ ©+ª—“n
+¥$]î$„AYK@_Ô>Ñ~L8
+endstream endobj 1339 0 obj << /Type /Page /Parent 1661 0 R /Resources 1344 0 R /Contents 1345 0 R /Annots [ 1340 0 R 1341 0 R 1342 0 R 1343 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 1340 0 obj << /Dest [ 1101 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 221 536 263 550 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1341 0 obj << /Dest [ 907 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 348 282 370 296 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1342 0 obj << /Dest [ 950 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 464 241 492 255 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1343 0 obj << /Dest [ 1346 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 196 76 232 90 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1344 0 obj << /ProcSet [ /PDF /Text ] /Font << /F1 1609 0 R /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 1345 0 obj << /Length 2416 /Filter /FlateDecode >> stream
+H‰äWÙŽÛÈ}×WÔK"`ÑÜI¼t 3FÆ­y‰{(²ÔbB‘m’jYþüCþ19÷Vq‘ZêžyM`¸ÅZîRw=÷Ýröz¹ô„+–ë™ë ÿð$žN$âEhG‰ã‹åvöú}›ˆ¬å;Žh³jöúã­+îÛ™c;Ž‹;ÙlÞîgòoé½n kùÙ‚‰"vìEÆ~bGŽæË4“Ó@ýUþÛÃîÜs¬¹ëÛ‘t±:ˆÛCÕm°ãIe“l‹V¼ýøJüý?Ö<ĺÁYh‡²°™m^‰Û}ÑýPMÉL|™V¹5_Hñ/þký¶üëŒ_̯ñ;Âbù{¬Á~¿·-ßveKR‰kˆ´ñéØžÌÌÖ†9Ý,g®(ÄÌ #Û\áÇv’ˆÈ…%Þ$¢Q³õìÝr0†ïâŠslѺYD²õà£Àø(°
+çÅrñ‰(xÞ¥‚íØÌèY½E刖ªF/¢;¢³æT[kŽ?QªôQi+k¢]e¾z‹öh¥²tg•6Q^¬×ªQU¦{eÏc¥º½RWò'‘ò•£Os¡€s¥@¿fë ·à¨ã;ì´,E[ðdEZs
+̧ÄçÏuG4˜$ 2”ïýòåæ3eùé盫×G¦#NLEÄù’ŽOÙ8úôëœ'ḗ ’gìsáüÝùñ«ÇÍð|Ký‘A  i'šcàûã °@Õ&H¼oŠN•õ=™Až æót¥-¤_)“eT:=ŒEw
+¢5tmk(꪿¼¶3úBC-¿'ú=\6ýkÎó1ÃûW
+‰,ï¬ óÐXASA~¥Œ„£Ó²<¼êÁM$Eÿ‹ä§‚úé3JWÞÞ|Yê}N“a0ärQAŸTÜB]Èz…±‡*k—.ÛOAçeÈ3Ìù¤b~²0ÖXœåÖÆÚMbù[^ ½$ÅBR !"<ªÛ¯ž)”¾‘¥˜ñÜ~¥!4+À¶*cZÍAŠ¼'Ö·Pƒ¸€‹+è´Ø¦¨²wæ²ñSÏ>”;ÃàòvGÐèšG\"ð,ªåeYï1@LjFSJ¬€y)ŽmbŽ‰±û ø
+?ÂÓàcÌ9•XÑ{‡CšW±È™üVTJB 1!à<UEÁ¢0€:¢n­·j
+‘Àœ°‹²>Ñ kÄÉD}Òÿîó)ËÎÆm4ÆmÄÑF"`÷7'µœ‰ƒh,¥‹IûøS¾Ô ä´Œv°˜ôÚ±‡¹ÜÃäí$f“'ƒ ©( ý©¼Žå=í™'ò&ÑîŽòxT¹ /ž § °¿ î̬7Èë‡ÅF­UCI`ŠñP”¸þSQK›¢­«–F#´"Ôèl“V÷*½«h c ETØ5\r$PV : ûšô‰Ã§>3_¤÷†5âÔv\¢üûzS!ÓNáÄ`­EÿöccY1,iмl±aœ;Æ¡l1X«éŽì¶ß¨Š )º
+.¤ŠãJO»Ë‹ízWr
+endstream endobj 1346 0 obj << /Type /Page /Parent 1661 0 R /Resources 1355 0 R /Contents 1356 0 R /Annots [ 1347 0 R 1348 0 R 1349 0 R 1350 0 R 1351 0 R 1352 0 R 1353 0 R 1354 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 1347 0 obj << /Dest [ 1357 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 194 756 230 770 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1348 0 obj << /Dest [ 1325 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 188 735 215 749 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1349 0 obj << /Dest [ 1317 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 197 714 225 728 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1350 0 obj << /Dest [ 1346 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 240 607 268 621 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1351 0 obj << /Dest [ 986 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 458 553 486 567 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1352 0 obj << /Dest [ 1404 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 323 472 358 486 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1353 0 obj << /Dest [ 1303 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 177 133 205 147 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1354 0 obj << /Dest [ 917 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 418 133 439 147 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1355 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 1356 0 obj << /Length 2737 /Filter /FlateDecode >> stream
+H‰´WínÛVý¯§¸Pì%`1üEÞ
+5Ʊƾ
+'n’¨Ø’
+>F‰Ú˜ÁýàëÙŒÐÇï]ÑŒb”<ŽQ‡ä$ö\üõP†cZøAÕÂ<ߺÙG9r£©u“bõa‹Äí6Ý8°\8N´iÕ'³WuãÄÀ ¦Ãî»@,øè°&Žø„‘ï&tÈ‘IÖ¯¶d¢ñËâÞ2›[2!ÔfãŒaÉ‘Y¡˜åúñ æL¦ÏØ“ÿ>{¦½=SY™›‡"3êÁëƒ5±µ††'ÚwèctdXdC¹^„ý#7 “‰’×.ªñÄwƒè(ª^܈gã„P ~•§mÚ´õÆ( Œu»_þò×aó¹¾Öù<ãáëK™r÷Ó;…rP?Þ~}c+ÁÂqBHE>|‹3nû6k^o²Â™ÀŸû"Si[y«ÍcØŸ&jD”í½K~ç‘/ÝÔU›•ÉUQ]r‰MžÄ$F®÷1ñú˜x6&WÙ<˜"¬÷‡©:£
+·"‹-bòãìbƒ€™² TÕ…*±ž¥•ÊÄ;qlü%Ç’Þ1»ÇuYª6]4ñRÙ2]·ð”ò
+Zd©ÀM<"y^ÔÎ(•
+]ÏÅß{Eä,Ò¤è3`T²Úpú£`dF-õ#/[ßz]Û£mEå…]J¨L¬v™ØÍUšeF&ÚéüŸÞ­±)̾žä©ôþŒ$™<O£î‹Ò4ˆEÓš4§Pm€{ U6ŸÆCäí”Íîºê:_!‚ô|TÁ Ôw?ÿpóËŒ¾¦üÿÛÛ7òvù÷ó„Z"y:Õ¿|ý^†o~¹}ÃÔówZóíÍ‹&úgLÕøþîÇ·w”Nd¡`’J>­Ö¥!6¿/Ý<ªà#šèxbYï µ%èU¦Œ-ŧ%‚
+Ê£›d{¦¼RK›@*š«yâ£n·­ü¢Ðߘ†ê9%"4Ý&¡ôCZn‰Åˆ]Áù­ÊR¾ TDQýz&'z¦v;EùʇÑYú}Ú¼U+6·ÖÒæ`S³@`¡¶¯©GYZÒ5Å'Z™"…^ÎÈ›sýùƒþ†§£}Ð…n;Ï‹‡¢Ù4
+K7HW’cÔÿÒØ–ü ÚŽù-¥®wa¿©U±X¶j™>
+‚ØE‚P–rœÅC4î'⦱®Qš úfHQcúÔzf94¨n%¥Ô´ÛÁÛÔ+eÒl©jØ}ÅßuQxZA?
+Œ•C]šW…’¤6¤ÁY4âB–³ E/ {Û¡Pž/¹œÝ·ÂHôíDƒt–¤ë<µµ@$Š|ZÛJ’®Ý.‘R¸!MìùöðC†ñQ/ˆ˜C×%wÉ„þH« @YëJ›Š"àPGÖž#.âwè¾êÄCÖÞ(5t¹©¶¥Å¨äjhC
+&ôPíø·(KÊ•4Ï7F¶8±"WÖª'ÕùAýÊn=äc]jøÀã䨯öãbŸ±Œ}Úœ®WEK°”æ¾UÂÙÅýqÑäµ9¨Vª¡kn„ÂkL¥¢rÕ·ÕùXMûXM»X1ð¬Y*ŒS¬/)ã½p+FA„,ßw»þGe¶¡ÔK•L,*Ôæâ`›óÂ9rÙèN!Âÿˆ»êÑ—ÀŽ’à¡ú=û–Æyi¢é&sÕšßÚtÛ._cWñη
+ßGëç »²«(ãi…݅팴Lz-/O&¯w§øØŸò†þOD'tpX!ST·¹MGŒ€å1—Ê/êÊ?>_rg¨¡ ”DìK½ãK\Û%Õ-ýÙ²—º¶/U[È‚TBA×ùžÕòs˜-ùXráÞÇM„‰JèGÍ÷‡.,KÏt¿³µ¼Ç#³dHXJ0DJ
+]µfÄ )3³D®’KþiÀmp€eQÉ1‚q9‰Ÿ ©‡õš±¶¢é²Å^ö»|,ž(>P nG½þüêg”œ‘ÉÓÇ2ð Öi!i}ôÚ¾á¡Iê8W"à@®Ä]*&܉è¦7éV£®áS¨eöI‡K¨ÃÑ ô8E;¯y;éZøDñåhPÛóG”•Y¸D1ê×-M'Ì“g*ãiö“ÿÍBC)FS3¾àù-”L`9Võ­¦HÑŒ;õA“¼k
+endstream endobj 1357 0 obj << /Type /Page /Parent 1661 0 R /Resources 1362 0 R /Contents 1363 0 R /Annots [ 1358 0 R 1359 0 R 1360 0 R 1361 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 1358 0 obj << /Dest [ 1325 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 262 743 289 757 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1359 0 obj << /Dest [ 1249 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 228 286 258 300 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1360 0 obj << /Dest [ 1346 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 257 256 292 270 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1361 0 obj << /Dest [ 1325 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 262 220 289 234 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1362 0 obj << /ProcSet [ /PDF /Text ] /Font << /F1 1609 0 R /F2 1607 0 R /F4 1608 0 R /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R /TT12 1611 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 1363 0 obj << /Length 2919 /Filter /FlateDecode >> stream
+H‰´WÛnÉE^ùýb¸ÇsçPð.`Kò®7^˱¸6V°’-qbrÈå E3’È?æ!§ªz.¤HÙȆÌé{Õ©Û©×£Þ‹Ñ(P¾Ýõü@yø‡Ÿ( Ü(ò5Æn’z¡-z/.ÊTMJÞã©rRô^üpã«û²ç¹žçcϤׯ?·=ý!»7ÊSåŒþÖò¡¡xî0ÁÅaê&žÜËg>N_qB§?é˜ížÓ÷C7Ñ^ªÆ;u³+ªfmܤ˼T¯~8Sù—Ó1^c-vc;žžÌÎÔÍ6¯þnÖs¾$ÔY1uúC­þÁÿ;ýÔcY›Às F—˜c ¶Û­ë„®¯KzÕг¸OºøôÜ@OìÔŒoºõ|•«^'n˜ø*¸iªH*è¥jmzw½×£ŒÐÇo]ÑŒl”Ú¨Frxx§eÓÁOúÙâùÐZmí¹ÑÐ*JÖŠZÃE¢øÚ‰€% üÊŽJ'ÁÈ•|( ¢6vR¶¨ÏfçªÑÑy•wŽ=dóÁ—ñ:«Ô6³«{/Wm3Sr
+ˆ~sýîòêão¼úÕŽ?^ÿ,_7¿¾¿øM–oìšüütýö}]¾ÒÐõR_%Qà¢ÌPÅêÔ­¯-ÆI½HCw9öRèqêðÓ«Í»pŽzÕÇÑááãë(·mÍLS©™‰Ÿ¸¨›\3? 6®>Z¨>ÈÏ5(Ä{;ã
+rÀõ»7î!ȯÞ_ÖÛ~¹‘M(½üÖÒXÒŸX?.½ºWKÿç¯>^Y“ZÙÞ^~÷üÙ›ç/_ì9ÊHñ0%~rîÓ«dg»zÚÐ'Že¹‹
+³-yYµ…aÀ,Ãó ™r,о²Y!Mñ—ÑE(ª¦ì¼â€bÑ®©™C ÐYFzèsâFÈTàÕ“öÕ8åW}/lR? dX[yU-²"c‡šYSM3ORÕh03î
+†Y¥¶6<Ôž@ÕF†…Ü=UãÝÑ<JkŸm^ü,¸û^u£ÅR¹ÊæiI$-ûä»È&£H¼"m+Ù° ÿæœý3ù?âoƒ~ø£óqÆ—$¹ÿè–Ð6Ü–
+–‚™’[¨¥ðÙ, Kh¤”|UèîüŽ%?´y:´æð“'ÍÐZ!ì¡#<n„Œ·âǵ˜Îd€XàjøÁõ K÷¬À§bœo5wäy!;|Ê7¡›äsÒuÒ)j<Q h}9Ðh²ä1G¶Žuu¼<·‰ð+:¶éÔ÷[G³„/GcîO)ˆ5¥ÖtjrзlÃK‡[Þ9¡„*r2ôú™¿“IË0Á!ñN4F¿|” n‡ââ›Tü®ªHU*'§U=Ú«AU³Z›’´bE¾ç¥Z›Å•›ÊÝ[Ùß±ý-ZÕK3VÇI¡_N>ÍѨ¦à0ÌHïúx Âa ¯´roŸ2kë¸þ^úxGœ'ѺÒ£>æ¨{„oeG–÷‰Q­Ñª|ñïÒËX‡A¢;_Ñ•©‚±òýJe« CþŸÏ41K„:F±œèélÝ-¥A“ÁT·ÂV>¾ïòÒœcb[1øYèÞNViwUî¸8oOÞ»Ãò.ÓˆTwk%dmvÒØcáUå÷¹2C)‡3åæÔ›ØÁ¸&®I £!@Ðvh³·²×É¢¿AÂo61Du¿ý1ûÉoØ)Š€p6À¿ð”Ø¥„jÀxÚCÔEÈ8zÁ¡‹¡Ep0æWs
+endstream endobj 1364 0 obj << /Type /Page /Parent 1661 0 R /Resources 1372 0 R /Contents 1373 0 R /Annots [ 1365 0 R 1366 0 R 1367 0 R 1368 0 R 1369 0 R 1370 0 R 1371 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 1365 0 obj << /Dest [ 1120 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 470 743 511 757 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1366 0 obj << /Dest [ 1116 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 238 638 279 652 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1367 0 obj << /Dest [ 1120 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 377 560 418 574 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1368 0 obj << /Dest [ 1120 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 470 525 511 539 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1369 0 obj << /Dest [ 1120 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 383 447 425 461 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1370 0 obj << /Dest [ 1120 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 255 326 296 340 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1371 0 obj << /Dest [ 1125 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 404 305 445 319 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1372 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 2027 0 R /TT8 1605 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 1373 0 obj << /Length 2213 /Filter /FlateDecode >> stream
+H‰ìWÍŽÛȾë)úb X4Ù$›ÍaÀŽ‹ÞüXœ²“‡âÌ0HE¢¬È²ïwÌ!U]M6IqFšu‹E,²Ù]¬úªê«ÒÙ›4ÌgéÝÌ̃ð*ᆡ'YœD®T^ÀÒõìÍowŠå;½Çc»¼š½ù~á³ûÝÌs=χ=ùlÞ^füOÙ}Áü(aNú÷Y¢%,öÜD‚á@¹Ò#»úŒÐÇñ*’xúGþ/«sá9s?p%÷»=²Å±j`EðÂK|WîØûï_³¿þÛ™Gp¿…g‘ñÒñxþðš-eóµØ®´‘€gÕÒ™'œý¤ÿwþ–þ~¦#ÖÑÏ•p“~„5íÁáppÀõùßZàkÁ4¼Ò…KÏ<7KÚÒwéÌg%›‰HºôY»J1é’ b Û³»Ù‡´#ða‹7Dâë!"\£9RãµHÆÒƒ÷ô ô}}ðÕâª=kòºabÄmÂ&LPÀ['tc@v.
+u²¥NµâTƒ}–Ì‘µ/N—yÕ˼êg6`J¤9‚pK›yÕϼ4y‡Ô 7¡'¦¸ó«Æ`žÃêÛ…[cc~~±VqbX¾qôkÝÇa³ÕD}Ø>#ßÆü“3WY€0€Ÿ—Eð¹„s$³UΠem6x㺆:Þˆèì‘Œ-F€--ÊþæSV9W§ÈN¾Ôä$‘œ$æ°GD]Ý·TDk0ÍÀÛ/äLqb9$¦Ò4ã[ú¥·“©µiâyÏ]ÑæÄó»œà%LmÄMîö›M½m4‹ƒ¿P¿íŠÆ ¦Gù¨­˜åg,…%ó‘_ý"Ô¤óC .žE8ŽföŽÏGÌó38GC‘áY(lŽ|ßBah5$($õjûF ™ßfºD÷„Ô&Šàpàn„#4/"BQ €@1¶–UÔY8leˆ°GùGœD8bHÄÕ hKÿIÁÔFw9›äû>]ÔkV.IÝ[èo°ŸtÛ‰ŽxOX-½ñ
+ íëšÈLÏ•wìm˦ûu{a¤Ô4O²ŸØ˜Tcˆv¦€ÕÖ[ # f½ž‹ÄÂqùÔd‹ë÷WÃú¸ÅÙ+Ä3Fì`#­ï¡‹å•­L…4,¯—ÅsjÃé*nŠGǤÃaô:iq}¦‘¾Qyµr ÓÜŸ7#5&ÚšP#Åéz£ÐòX˜Á'—l•™69%Ï&”ÙeJ5ˆv€^á¿à§]ÇÌ' Ó1ó ëP)ÑõF—Ì
+ß
+ Œ¥’Š”¼-±£ÄLe [ïûÙ-MÙÎ@ÄoWÚƒªøg3næ¦ò¢ØVÅ*-F˜nw¤2¦¸Pè±
+4¸Iyu}MâæjD…P9=*$Õß}
+zŒt,8ÄGSÒCñz[øhlà⓱™YL7ˆÀI`e>…zR±ÂDïM^—c¾mJseülê¦[Ø•_;ŸsW¿ˆ"ÁÑëÅÓì8 ËŽÁÿÖû*¯+ì˘—´ۋΕôùõ²d$âép¢ŒÏ†Åß"í ÅÚ?âkPeþÐ}HjºÙ¯oÍ"3hÄÛcSt[€ÎZ‘!F"ã”åj…D×'>HnMÝÔta€@Ì(‘ÕçvLb(â—Èg1èŠï±-bž¢Îˆ&æFÜÏ‹•Ðžý@ ã‰.úŠˆ¶²Œ}øôLjÙÜî—²Z–9l]²[Ò"Gl'˜·P.ªÀƨ8\ú
+¹ñ\i”ää—lµ/(y`¯ª¶϶Ù
+„QYѲ.©úÚëó>ò‹?b´ óâñkõ5»Ý7,3V3 :[`×eäÛp€x¶ô ù.ýg
+endstream endobj 1374 0 obj << /Type /Page /Parent 1661 0 R /Resources 1385 0 R /Contents 1386 0 R /Annots [ 1375 0 R 1376 0 R 1377 0 R 1378 0 R 1379 0 R 1380 0 R 1381 0 R 1382 0 R 1383 0 R 1384 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 1375 0 obj << /Dest [ 1392 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 107 577 143 591 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1376 0 obj << /Dest [ 1120 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 278 563 319 577 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1377 0 obj << /Dest [ 1116 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 296 493 337 507 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1378 0 obj << /Dest [ 1392 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 352 451 388 465 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1379 0 obj << /Dest [ 1120 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 429 424 471 438 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1380 0 obj << /Dest [ 1120 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 273 342 314 356 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1381 0 obj << /Dest [ 1120 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 283 245 324 259 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1382 0 obj << /Dest [ 1392 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 461 188 496 202 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1383 0 obj << /Dest [ 1120 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 273 161 314 175 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1384 0 obj << /Dest [ 1125 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 463 78 505 92 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1385 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 1607 0 R /F4 1608 0 R /TT2 2027 0 R /TT8 1605 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 1386 0 obj << /Length 3411 /Filter /FlateDecode >> stream
+H‰´WÉŽãÈ…¯úŠôÅ ›‹HQF·{0Æx¼”NîöE¦JôH$‡K4âð?úà™$µT× ìA/’r‰ŒŒxñâåWÛÅ›í6TÚîA¨|üÁÇ* ½ÕÊOÔz{IêGj{\¼ù}—ª¼ã5¾êòjñæëû@=v ßóý
+_¹Û.6¼i£Ö¾·I`8J½Ä»¼'äíô-Nh÷'çß!F—¡ï.ƒÈK?U'uªú=FBG»°äte§~÷õúûÜeŒß-æb/vJ×wòýº.ûu{`#‘“U…»Ü8ê_ü¿ûí|c¾Mè{ ~l?`Œ=x~~öÜÈ œŽNÕt,LãH_}/tr3´gKØ.UªE'^”*Z{iª’
+#2¨JY¦²ƒ!'P$ÅIeJæN2Tå&-kN %…½Z©·žÅ^ø«Ñ!úJµ”ºB³>ß—Õ# ¤Û;µ«}ú©Ìõ7îPÇà(²ž±q27A¤²N«&ë÷  r§Nº£µýÐVä',´jëç9 Æ\Ï
+ÃV(aOa õ3y”ׇáH‡@#JÕéÔg í÷™»$öé9é
+×Ðí¯?»oMª?šœð¡ar½_öÞ|\™5ÞÊЋ€r†‘š`âo,Ò7ØL¨ïõÉì3§~ë ²m9íûä¼Å8Ép½dÎ’ÊÁÏL=e‡A+µ=Tåƒ>œTY
+É÷ jÜeŠÂ5Ó÷À_£ˆVÄ8¸P/‡•ÚØúlWkõ ŒÏ®bhÞi<áKm+ŸjÏ7µ·ETLdñ¥o‰ •uÔ¾ÔîÔÃЫ<«p†ªwÀô €Ðc¤Yõ§Fèzµç=C•JwC¹pS\mI)¬‡ÎXûóµºiu‡„f}YWž`ÎbÆìšùó‘Ê—N§û§rtJ—©ÜƧ{üê>g±Øóçؽ& Á®¢0Rì(Ó
+Š–Gåg5:¡Sæ{ZÚ3'× ˆÓ<éÖSÛ=óJ2q!â:êì’VUöibYwûz8àšcè7mý„RýŠÎÅ®ÔXƒœ®|•ï³Þ”?2\1†>ùBši->ö@sˆv9GÀxÅ D&ÙŒ5¨=>× >™™VÝÀ#yŽ¸lHät²D†.-¡ÔÒš‹L«sú
+RK_Å[kd¡¤Z§² éüó_& 9”Aù&Òƒ¥ÿìÌx•Û3Ó>Éö'ê›Õ»÷£Ð–1Wš|>˜OkFñÌŽ›fÇ7=—×í÷¢û6Ò6-b¸ÓTRãÄ{tAÕ—G=ß”‚·Ë"s`¡kæw]©w¦9C.L;‘bˆÑõIì¼7t¶!:»b‰ÛÅKÛ!fˆ÷h+
+X>X@Þ)j¿ïdpv8ôؼwUuµ³ÿR­È}bÇìsI]Ì
+
+5ª¹Ïƒ ‰÷Rç¨MÌ>»ÞK•´þ¹7¼YS×¹üsE]‘uÓ 1oH¬ÐuUœÅœ4màá
+'ŠmH7ŽÑUFj!0Ù¥j áUt­g^i¨E36YKA¯x¢g™O èc—<-Õü|•¶:B[`1>É|yÐ ™"=ù|~IôÍdFç×¢‹ôa‡ãÇ$¾O‘àÒ„‘
+gôhDæì8ñ•ð:ÝviYGŸAûF"/ªË
+ßQ ³ðɳÃêàU¿Ú¤-©ò„™³<6ÆR&o ™<œº[^¿çâ4½HŽ1{Î왶d*ZàeV¹¥{ƒÙƒÐ¤àEòqY
+¨S>!±4˜ÁïЃnM-WY9c5¤sHç©Å¨P‡4kD
+iy“›®å@g­Oø JÌóÆô‹lB’1cÐðWK(êf”«:°„¢ÞíÚú(ïCÝÐ4uÛ3ŒÜ­˜6ä²Ù…™uMdn›.5öˆä
+Ar—\$Ì%‡á±4ó ësÞ/Œgá¹|M‘„Aÿ&Š"“xèº$e€}¾‚oâÀvÎÛÍÔ/}Ë“½Ä·S;øŽÂŠˆâŽÄުРÉ|RÈ&dFÑaB1èåâresÙR澦ǰޒcÖ·×Ûø`zk!êo²¦ìŒ}‰ÙMùDU”×°PËÐM@¢´æÇ”YTè^£V„|ŸUÖ09-_K—è½Ê(³·Ôe}ò Âx¬RDJX™%¢½ì.[ª&´EÊ’Ä«¯º¼º’
+×¹ûN?s£›w.=f(ö‘翵žYS©FsDÝÒZúXhÖ¨`iÛ—{TüŸ¾Uî¸EÝ]ƒ¦¤~£þFe:Äþ¶
+ê3j£îÌÃrÉǾʠÄÚ{ôõÜò_öË )aˆÂwÇ0£Œibh¹û8zA›Qf 8µþwßnR UpÆ“ã­4,Iö½ïÑ"™bÇúß^‹¿Û%!8X2%2#\  ¢ݹÞMÛnùªm=áíb0lo¢¿qú
+èQ¾3wÚÒŽdûÄ;»ˆ‘LDÍ©1l¸yܲÞñ9ö‹BÜ-…3­àÈ®
+ŸªÜÂûŸ8ÕÉVý~vEl}ã¯"‹=ÁÃœY°`:ª [Wö«¾²Ê i¨A„#pÙ¹òPâ•Ï‰ÎçDçS¢óšèü8‰'CO‘é|ÊtKmA3]0i™Ù ¨½ô“D$7ªNøŸçDz¿Ðo›nFTŒ„uQ#«Ää‹ÂØÌòoœd±Ét5ž÷\ƒ^h.^<¹l GöQŸk Òµeýo#¼ ²Í?a¿„·€“ò
+oZi^1 Óbäj$Ý}‚¸ô¶d9KU!“€kP¶#=Ág‰qPýƒa*a½ NyŽÉ÷G´ÅÖ5/´ i׸¦ÓÁ2¹%Ù8ù¨þKÞdˆë v‹?ãµyC%< ø2ºM`[õ‡°Í•÷X¯0#Ž\ãŽÝÞ™vÁ{R—ôV¡7÷»ô†×øR(Z¼Ç.(ìÓƒ¢߈ão·CÜž!ëLTŸŠâ^IjR^wã}¯ƒÔ•xF¦\ã)þì`Î(®ÑÀdd1åj–ð%<ðÑ‘A¡ôÀ”wÇX†a‡ec«{w¿¾ú`
+endstream endobj 1387 0 obj << /Type /Page /Parent 1662 0 R /Resources 1390 0 R /Contents 1391 0 R /Annots [ 1388 0 R 1389 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 1388 0 obj << /Dest [ 1392 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 134 460 169 474 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1389 0 obj << /Dest [ 1249 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 266 433 295 447 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1390 0 obj << /ProcSet [ /PDF /Text ] /Font << /F1 1609 0 R /F2 1607 0 R /F4 1608 0 R /F5 1610 0 R /TT2 2027 0 R /TT8 1605 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 1391 0 obj << /Length 2981 /Filter /FlateDecode >> stream
+H‰¼WënÛÈFÿê) ! ‰áðÎE²€l)‰[Ç2,z‹]»]ÐÔÈâF¢l‘Š£}¾Cß±?ú™¡®”³M³Ek8g.ç|ç;—9IZ¯’Äaœ%ãw˜øñ"Çò<;`aì[Ad»,™µ^–ËJ¹ÆfeV´^½rv_¶l˶9Öd­N=|j—é½`<àÌL~mÅrSÌBÛŠìFV`«såGn§‘Ðîã_f;Žmv¸k†±»®Šj‚Ç&N2ʼdÝwmöó¿ÍŽïd¾å¹i٤͆Oyõ›XLå!®‘#³ìŸò¯ù÷ä/-i±´Æ±­
+6®ÓàOÓ²2=˜ÎÊùD@Ö€™`×WgßK{7Ä ¬hƒ\¦à3XÊÊj‘÷lYŠ92@™±R,>‰Ï€¤\ÎhÑCº¨òtÊòJÌJk×êN@Y‡[N(­ù:3mgm& ¡"Yɪtq/*e–Ú½¶Ê÷þ`« Þÿb•²Å¯#‡FµUt!Viµ,Ì
+ù(ßÛÚ÷°(/¥·ñܘ-ÕWe:Dˆ;ÁÒŠMEªg:w9î,*qoF–‡àì8–Š±m“.MöpýZ㣵ñ‘Ò~q6”êCun…FZ‰}憖ço·ÎB¶»›!_ÀÖ¥‰Ux ³ãɃƒÐ ¯!ã"ôÁ¢-PB(Pd 5€û|Pµ¡²¢šW5$%tƒÊHPé·@‹G¿—³%Á5=:!|ÞÄ×ù½üQm¾)üXÙ¼ç Ò–3“ô@ YVÔ„BÉ=Š¬Þ¹…¯oá;´Y®©ââT"‹§ÉBßD—@Ñ…ˆ±“8Ćã¬Õv”Úe5_˜Tm"`KÞD‘X':ÎÌuwbïwEx*<BcÑVáS
+v«÷ {§ó§[óÐ[–ÿ-_ûJY°“çö2¼oqþLÙ»Ñy}Ûu¼v;9œÔfVDr–l2ŸŽ~Ó|¶H+v7gá“ÃÅwyAs
+~yûüv¼Ñ VŒÒ*m34gö0¯à]˜bÆ@tºbÎ?\Ǥ¼‚êZ :0Èåj”SæˆI‹`G:Í«U»ÎJX©4{ʧSVˆOò ™ncÁÄçL€lùÚ”6K?ç©zòç-GÝ îÍÖt#}ÙüîW‘‡åòáa¾¨ÄÈb'ËŠ)•8Æ„uåéìa*d fi>-•iU¥ÙLâŠê¾N·%ßt#J 9ϤÞÒFnÙI¦{\BÂÇÀ!Á ´6š™wË)[ý QgïêƒVþÆppJÇl#1´»œâ dETYgp¢œyPÒ,Ë2—°¨ØªIMqN…ï n©_«:¥ËÕ
+,…E¦ºµB<iÁãô‡½ô„V:ØêmùÆ–:7«†&’ºƒ0y¡>%Žž±PYYÉRp艩)‘ÍÕ` 9í«æ*'Éí+µ`•ªuŠUFÄ¡s©­Ò»©hKf>Mòl Ë"\
+åÔ•ò4‰q^¨î](2æñI¿´s$>å™8ë!”>gˆ/•í +pOZ
+4§Õ¤f‡NWšüpŸá‡ûÿàߧ‡¯èá+`œ5=–ˆW¡e²E; IlEÞóŽ¬Þê(–"›|J§KQ²±¨¨pzý‘7&Ê
+F¼!x/š6Ùëì÷8;¥¾ GÞÇ5ý(šþÀƲÍM2M-£×‡\ó '5ˆ¡Q¶1ëó(KÖê#9pe1¶çê¯LÞìjy×N&Àëʤúž™ª—§¸OM2«6Ö%0”`$—
+ˆ=cªç„ÜX>JQ¼àùr)™08NPDŠÏËŠžwå
+ÞuÌS/Ä&è÷»Ž!£
+¿Ð²™#DÑi¬9‡…oݺ«¦' ¦‡º!@zk¢# àP‚¹z ©$D`°‡B€-GYÌ3è<PñsaRJg½þy?‘ûû¤*úàÓîð¸:F·×—›Ñ±«Ð{ ë2ª¬Ÿ·-Ø`n,Ðou)ú
+h’T~³érDޛ͡¼j Þ¼k]<Ò…2¥Ì힤¶CÔ6©ÒÏäk’H®K@®VÁs÷hjdæ–jãTôT#:ùŽ.^i›°çÞ&Ò<ýT¡XGØ@U*3/$—ß™TeLÉ”*µjFú›¨E†Z¢'•»å¬›Õÿ*4p£¯¨õ™r¬-dÜLz• [1“-µx•òbêNmVfÞ!ßî
+‘Ë çǂ܉ÑDEJsãõ«/¥@;¶¢gèÛ,Ö›8 ªtÔ€còFÄuÝSäßÎÜô¾<PÛ‰:íøÍGäÏçGÇ-Û«~vÊéüxv‘ Ôè¾·šµí¢ê¯5sÚ{Îw‚øõ›„ͨyþÚÛ72¶xkª¹»ç×ýµêizñËËzøb\Fzû¸Ð;Θcòfí©Vè(3(B ä®Ÿ=ôTÏùùˆü !ÂC+j
+‘f®9¸ëšÅõfUœÃuDÞ‘³½ÿp^Æ:Â@ý”«6@A¢t9ét6UÚû€Üÿß8@0öÎW!í6»Þ·›ïÝöïó<,8¯
+endstream endobj 1392 0 obj << /Type /Page /Parent 1662 0 R /Resources 1396 0 R /Contents 1397 0 R /Annots [ 1393 0 R 1394 0 R 1395 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 1393 0 obj << /Dest [ 1357 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 455 516 491 530 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1394 0 obj << /Dest [ 1125 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 275 325 324 339 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1395 0 obj << /Dest [ 1357 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 293 74 328 88 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1396 0 obj << /ProcSet [ /PDF /Text ] /Font << /F1 1609 0 R /F5 1610 0 R /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R /TT12 1611 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 1397 0 obj << /Length 2577 /Filter /FlateDecode >> stream
+H‰ÜWënÛØþ¯§8]`ÑCÀ¢yxWpb'ë6—m¬¢Ý:Å‚’Ž-n(’+R6´²ï°ïØýfÎ!ÅÈRœ
+[¼OÑØ¡ë"’Ñ;}©öM”¦È$š¸oÎʧtÞfë[Ý.t¡[Ýü\<£ [¸^ªD’(ü
+–g Õ£«Qܯ†~§®G^êúáÑã,÷o³xQá…pÿðÁeÀyú2:ŠNÃ¥ :×òüâõÅôÂkùòý»7æëꇷ/~œž½u1½23ÿîâ½ÝfæüËÅú¶íÌÜ Áåz‘¢=‰¬Cä’rVÒo_½3ïFä7gߥ¼!žøŒ¸‡—àãî§R7êÝïô ü¶óºÀ±Eö>^üŒó<üùÕî]?uÓð3¾wdýùaúìH.ñ7´Þgy: íîÐõp£Ý Ha†ÝÁ8Ƈ? G¯§GOrñ€°Ì!…‰û|¥›6[aÊ—5ðÙqS­;V®ÿµ«‘Ä´h±€(Ц9X7"/͆6ã‹É–&Ý0ˆHy7T$̹•K±\Ì÷m6+t§ez “š†é@5Õif{Q•m–—zaž0Ñ&–ªwPÇnÒQ5åŽ^†kùt‘µYã`ƒl¡;åý¬?n¥óy袚¤=©$+ÅÜc䈑CE»™;fUUhÜs—G)FÆß ÷ÂØlx挛¼sÆ>R– 6ìøä¸LC õž#ß?!ª²ØŠ²¬Zqõ××y«?8{–;„‘R¤ÏõM¶)ZMú4Cs-·ºa÷4»÷T$<oß:~¯…ož¿¼íÙž:à·’E6ðÙ¶xêDؘ0[V ZÃf\l4{<r» 
+»ÚeÆ®,†ÏâøJ—m@íS(H™7}øûj3ÜûÐL_Ÿq¨­H 2*çój½ ;ÀÑØ át-Æn_BóaŒ»†\hܼ#÷¡àŸYÓ®ªnr],Ú-lÉk ŠÇJ`v
+¥]ç³
+$*I
+PŒ?¢@ Y;ck­¿‚¨©çZîˆ b•…•í©ø€µY‚k95ì|+šZÏ ¼†‘|ˆ¸›†é(ú`ÍP»â%ì€è>åäÕ]Ñ< "£AØ{Jh0çÅÿM³a…”,þ¾1ŸDíJî´UrUÛ%ð‹
+j˜áöB5?Êý]d¨leÁ&?à’£ìSV&LÌù½˜Ü#S† ±rÖ“O] -:š¡g(Xìn±Ê>ößö—Óf Ën(õõ¾*#ù 5DÃŒï“ÉMœ3 ÆmÞ¼]euÆEA߉xš—^·+î.²š×ÌJßM>wžJE®Eø馦"€z¤óWàÆTÒ ¦ÁäôÆÝMHë”}‰‡žˆ7Y ú™ñ†‚6$T»ðö¬ÌníI”çDßÑòýæÄ]ÏâÙž…X“…__HñÄuS¿§Æÿßdâý¾é+[¦ý'¾¨YâׂñFÕ©Ë%ø%–PÀ”l*žÚAB²;§ ÓEañnº3~l›€TRʦ{”ML8õÊ(«JÝØÒç^ÌT3òˆTþþ—çAKN~‰¬ÍRÍõy^ššûö!^¡<à÷/øö‰œ*YRvF…lš¹Ÿ­iìzUº¶Ü›.5m€¢ÍÖ|”ón®¬ïÌÞnèPâï?rAŸ6z»ÆHÙH(µ^P´ã= 7§€WRs«#¨scÓÏ)\ÀݬoÙd!›ŒºOšúNw«ÕŒq4÷nIá d_šåŠHŒùlþ Ì6Óí=sªÖ%×æ 6Í©;‡j1ûw_ÔŠevkÍ—QeÇr!ŸBî9òúø`-°+½»äy—ϵ+ΌʾQ9êT&'ØŠ"/?ç ü!7eþó怣n8ÄÍ0Æ‹
+WA®¬¸<ßó¼æïN©p' MtBŒ.½ÊŠš o¨!DCk&óUf>¶â£æt·¥ê…J¸®Þ%'FA²9_n`/Ø4 h}HÅ."äzз š\Ãô»ZzlúP î_¯uSW%7§`† z¼ d8 Ì`
+±|¤/©
+Œä>zˆ›d€ùd'áÄ¢G%XÂþês JëhQçàêR­ãZ<ÌF.db¹¢ªêj[Îßðìk¾° 5
+7âZ:£rŸ%#§æ¼/÷"™]$ÇBSHPÁn¶›g×ý{9ñšû "Iò-’ŒÚ­Õ¦i©ê§JÒt­al»W
+%/æÞIÿŽ5ksˆÖÒôúV·û¤ R„Õøÿ MH£ 0¥¤O=ßr1ýg
+endstream endobj 1398 0 obj << /Type /Page /Parent 1662 0 R /Resources 1402 0 R /Contents 1403 0 R /Annots [ 1399 0 R 1400 0 R 1401 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 1399 0 obj << /Dest [ 1249 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 318 662 348 676 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1400 0 obj << /Dest [ 1120 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 384 534 425 548 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1401 0 obj << /Dest [ 1120 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 332 206 373 220 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1402 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 1607 0 R /F4 1608 0 R /F6 1614 0 R /TT2 2027 0 R /TT8 1605 0 R /TT12 1611 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 1403 0 obj << /Length 3658 /Filter /FlateDecode >> stream
+H‰ÄWÛŽãÆ}×Wt6h#.o"©…a öŒíMv7±G@€ìäCµ$Æ©%›3£|ÈþCþ19Uݼ 5^’Àk ÙÍî®®:uêÔ7›ÅëÍ&¾Øì~ <ü‡?Q¸QäÅ"Y¯Ü8õB±9.^Û¦"oùO´yµxýý­/öíÂs=ÏÇ7ùbÙ?>.䟲½~
+gó·Åš­Eâ¹ë‡©{f_^ðrzZÅ´ú£üG€Ñeà9K?tcé¥âþ,nÏ•>`$ÊÁN²-Zñ»ï¯Ä_þé,Wxo0·rW²p<™®Äíc¡ÿ®š’7 eVmåZŠÏüëüuóûߘoxnŒ—Í5ÆØ‚ÇÇG× ]_¶tª¢c±5Žtñè¹ÌíÐwºÙ,|QˆE°ŠÝ0öE˜¸i*bž¸c”ŠF-v‹o6ƒ3BŸxϽ1z×#Höb”ÎcÔ{2‰=ÿ®ô’Þ•ôDùPk%ô!Ó‚ —Çì$T¥eŠ;4N( Õ:Ë/â˜EUkq¯`l™iµ5Kt-Èï).«Ä6Ó¦óºÙöëŠL”už•oi ‘×¢¨D&±9׸zé»°†së®ÜŠ<ëìJd®™.rsäV•Ju%ê°¶Ÿ ˜]`#ògñxPN›Úç,¹åE¢@È)zÖ`Þ·»ëGºü ²~vbnHæ¿®¿®-@
+EâÄõÍ;`,oœ%RGnnD^W¼]$u“•¾s~#Ø;k©aR “ì¼PöÁþQ´‡/+]d¥u?ùÐÜš.`Ë¢_„›±¿ê©ßUÕ^d;­㾈ÝÂ}ó`˜¤í/ç›»Á{9b%OXȺښcR¹GòÁ§½£9 BY#é,$0™5jðò²¿FÑÏúÔM³û3GÐNžÉ¡¬r{o\T@rµÊµ@>ÿB¬¢!V‘¹Ï©©OÙˆ¶F‰š
+l±B:fa0©yêÍóšàÅsÃq^²¶zOü[\àèåKŒöÍÓ¼ü~”$ÞVÄÍyC2býD áã´91Ád¨iœ¨.WÜryX\Ç?N’9GûT–TÔÒ÷N¢o,4qb“n»Ý®È)WáY»¨â·,-›#tf;ÒÇúWEÞ„öIÞÑ«íRúºH´õ4Æ-—À‰d3Ye†@'èvsÖaïyš…ãõ­@E奶†fº3Ë•Y~%¶¬'Ÿ4°ƒÍ³uüÅZæXfL€E†; Û`Aß«uwÞ–¢FÎÖ”¯Þn×ÜJXùN(Ž” Y¥‡>³¨ò’5p·µ(ãžé¥5¾aºr“
+Ù eN ‰œ³E»Øä×DÒBD+=ÌGÉ3"=–DZ?ò½ö¼’q•_IpU3cßL6ó\LÜtºY2׉ÑsЋ$sŠÜb™Xû•</þüΚ½ÒPçç´ð¥?bÌ·¼ƒr55¿ÔÅiЄAÏÞ“VÕ^h"º(ñ‰¯F°ryäöÂN/)   ÿ#½ü"II£ƒ;Åy7óÞê9Qs¢$š$–Lä†iò $ù .åÖP=Æ¥\ZV‰˜h¬¦3ÓVšC²%½,.; öjzçÕ˜h&ðKºå2ôÜxž»U^ <àOQÄ[.ö]lû:wN¤¢doÒ‚@˜_³>ˆLÊ W¡ìvn²jR<c:{V<ùÃWOoþO½b¸š
+b_ 2ã$5Ñ'Ú!¡X[Ç‹Ô“6¯™0²dÅ™íyÖ Õ„Ùhzš,Ú‘YÏ–Ñu8kî.Y®Ðê8¦Nªïßõeï¶k©%ûíOªíŽ$.C£Ès‡~FGµ}È*NùK»(Ó¦²ƒ³úp¦
+G8[ü`&Z}ÁÐ º«;Ø÷ã;Cë­FsrdùÏïT.»ç£~ qÙ20›9ÔqA0µ€LÇWÙ–z\éÊÁY(ÓÝ ½¬™ÞªÒY·ÕêE!}¡le %^ªiwÇúÁK—‡ø˜ÉKŠñ©cCEËF$r`né)…Kë ;v2_ä…!£‚Å äJ|e©¿T¹†O gF?•_S8ÙW
+³Le´O õFûÞÏfͺ‡¥²ìMéWŠ¼®tVTÔ«˜Ø,Ü°÷fÜÔ?±Ü£(‚«UÒ¶Jó„€òŒ­ûüœÓoG J*³²Ò—õȸf¤$û@‡¶H>†*8‘î)„ë/†;YÕÍ1+¹3Eu¶FÀoþá†ÿ“QÃ7èe Œ¾J³- EEÉɼì¶äºWö"K™ŸÎ%Ñ ûÄálhÄ\³×ooH˜¿ãßoÉ1fPaUWöI]ÿëeóÛ&DqõÚ¿b{©¨d[aVÕS›cUoÍ…Ø›‰@jü!ÿ÷}3³k0ë *ö%òÁËÌ›y¿&mê VŽÙHþ¯tÙxéúþf_YLT·‡ª©v/u‹ZÒk¾§&® T³™ZjJż•Ù|zøbæÞ~˜…§Óθ>È%±3ÃØný¡£…³»¸çRÿsÕ¾ €,ì3ý <2•<%ÞiNÄÄ(¨éÐW#”Ÿ‰:¬KJ<:X3Ä<bù) t†q*T-[?5¢•åÒ©/u%FÌz_T;ƒ€d^é6ÚÙvÍJnä·GÎr'ê'’§/Wä%4Ó.ðuá|ed¯v¶FýŽO»ù˜—fk8^…ÁwÞ«‹!Ù£ñHÓ)¾v=g÷òï/«älYe½ÖãL W¬lr+OÑYÞ »N…y_ kUðʃCqmÌ,ÈÌeäÅŽBà‘à/ùJÔ϶!ǾvYc$áù@~~š/î½Ù´L2%D ³pÝu÷Ëf:ê+ØÛs÷¸ WŠ)‹»«Ý½RØ%©]¤õYý¢6’˃P Ž9Á“Žû$ퟺˆ¸]7­¡¥´…$÷F}Ý'ûÃß°EI®«‰jQè " "¢9,=J¾L¸ÙOMU5:(1¸¤dÕkÔwJ€ÒÔEEr™!äb"úðÂdIa¢7†LòHáÚ‚N°nBøŽΦèƒrg…,RIƒ#‚•Yx#’d£#¢Þ5#Yÿ$:†=ŽÍQ‚UÂÇüpTe Å0äa ›T-Û%zå
+0w¿êñøbº®ê׫ 2ÁÌ Þ²œJï0o•)äͶ—±pmÎhJ;ša½
+c7ð*Ù€Ro6)ÁtŠ)ÉÀRÞ¢t?êŽÒ]$Fw¯#ô9óù\øœôSrÀ³|^Öt…ÍŠ„èôö/€Ç²¹».nÝUò  ÕFɥ͔9¸7éSÏÅD¶Ò‰òütíèÙz H‘ L…§‘å "ågN5?Áá\2’À!cပ¢± ™ÓJ±UHŸC>Hd7Â#ùµ!Ír}3fVx:wÊ‹ÞP^×?iD·Ì‡»×]LºãH(qÎË…rÙÅ• ÁÑ-ê$ýˆOk8¶[Š˜Á&õ\Ú;:~.÷Ú0bÚUçD£;‰j÷tUA§I7ôåò–ôòÊðÙK;"¨¿?þ`
+endstream endobj 1404 0 obj << /Type /Page /Parent 1662 0 R /Resources 1410 0 R /Contents 1411 0 R /Annots [ 1405 0 R 1406 0 R 1407 0 R 1408 0 R 1409 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 1405 0 obj << /Dest [ 1357 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 420 127 455 141 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1406 0 obj << /Dest [ 1392 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 459 127 494 141 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1407 0 obj << /Dest [ 1317 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 71 113 99 127 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1408 0 obj << /Dest [ 1325 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 103 113 130 127 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1409 0 obj << /Dest [ 1502 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 318 73 332 87 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1410 0 obj << /ProcSet [ /PDF /Text ] /Font << /F1 1609 0 R /F5 1610 0 R /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R /TT12 1611 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 1411 0 obj << /Length 2797 /Filter /FlateDecode >> stream
+H‰”WÙrÛÈ}çWô‹jUd €Ëv-ÑMhÉCÂI9bj
+"A 1jH²ò!ù‡ücrzÁB€ârYD÷íåÞsÏ]úC88 C‹pnÜ"&þáÇñ-æ8¦GÆË<ß´Ix?8=Ë|²Êä“d«tpúiÁÉm60™ir¬Y FÅçó€~‰ncÂ=‡á?ܱÉÛ>óLu®ÜcÉíâËõÄîkú ³#Ë4FÜf5}róB/i~‡‹ÆN¢Y’‘ɧ!ùû‘‹ñ2—¹41Lºº’Ås’ÿ+Þoå!6Òµ1
+(ù·ükü#üm -–ÖX&ó0Ï1'5x~~f†Í8ÍÄ­±¸GãJ†O“Yt¥§îäIÓpÀIB–ë1ÛãÄ3ß'’6:>ÙǃÍàCX‚as,1ѨÐ5"T¢Wy¦Àoì™ ÿ彆¹dÄÖ Ú<rMF€“gÒÐð 5®1Éðói”Ç÷qšË™%]%Ã
+·Ï;òV€í€W¤Ðýî仸‘«sM'—çÒôî$nV$“YGë{J‚ƒÖ£Ûš6a?Ñíñ¾.‰ÞÃÛ3Çé¦j—¼Ø>¶Ù¸Ç]òvO8ó‹ýí¬KiOâÛyi»¸Ÿè6ª®ç›Ž"ú9Z—‚èm-…ÖÊ}7 ÆÛã»™ÓV
+lÇ«ú·6ÏtÈÛ¡…UŸòÿPÒ¶­ƒ.ääù+ÄDg`j”AÌ>#ñ”Ì9j_»¥¢uÖÒîÖ¹cû+âân°÷ãιC¬žcŽÆAÊ2x” Àˆ/3ËsPÛ”¯Ô»ÄokŠ Ô3|<¡È[@–äxZm ŸÆïßÅï3Œ "¯1ŸF7Q†—šÏ\ºI°±4ºÇŒp34ñ»†– ;üÚõ¼xwqõì:Û¥y”¤ñO«7ƹêÇê÷¦'ñ+ž’Vù®ºF\Gy”X@óÝÞp¡_ü¾©ÕHî/Ÿ£￳(%+¥ŒÒÃíÑCá–g¸êŒ‡H< -šß‰§¬ÆЊz:R/Ç=¤@\¼V³G-Ûn_ÈþÎHü#Sñø4Ì(aÙúfitÛg•º¡u­Pš#—94Ï÷ÉÍcgbÈ©°’‹—n/Úx¥Š§iºKcõ„í…•sE®óx=ns¤ÿ“W4ì1¥TJ̦­Ò–2õbCò»$#j›ôVùT ¨ØÓŒè ÿƒåwClŽ¥»äXÿf VL³8“âì!^%›Œ=pp±Ã¡2VäÉ –¬Vq–]Ø+ÓdÒ†Qñ)ŒxÌÀä€Æ{ÃÑ 8NÔ7NÚ²ü´Õë“ô–\ßgâZ0Xh î–“ê/‰„â†C‰¾FœiSuà“a÷²ººvé,»TW|
+u/w
+GÇ{¼VáB§îžíZ+—¨Kº¿ó¬M³­f4ŠÏg„N”®‹Ø-u“žbÃkÙBPÃ;ŠZì ª=-¼]j†Å1J
+°9*Yc”‡C†©R¦ ÜàÊS¸·4X-T B+‘ªˆƒ{4oôXp.‹óLJa±b ®WN3pzþ¾„t'"äH¹0…«Ÿôpoˆ`‹Ó\'o¹d½Å7/Äà¦X0]tq:j+·r›®&·(‹$ɦ– $ºÝ PD~“S¤ø§¯¢Ú:\«G5“ÿošFù¹
+É9™@Bu$•fæ”(”g‚**ˆÓ€D.A¦P…PGC4S|RQbQ¥‚>FÆ‹ÖvñVM@{sK‹K@¹¼”b- Öƒ¢TST‰Am–U‰°‚Xç”} ô\qjª&0È M¦)(Ù
+endstream endobj 1412 0 obj << /Type /Page /Parent 1662 0 R /Resources 1416 0 R /Contents 1417 0 R /Annots [ 1413 0 R 1414 0 R 1415 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 1413 0 obj << /Dest [ 1502 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 0 72 73 86 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1414 0 obj << /Dest [ 1502 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 71 757 325 771 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1415 0 obj << /Dest [ 1520 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 274 743 295 757 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1416 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 1607 0 R /F4 1608 0 R /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 1417 0 obj << /Length 2558 /Filter /FlateDecode >> stream
+H‰¼WënÛØFÿê)ŠCR$E©Øq6[d/hÜud±¡I­HÚq¤ïÐwì~3çºn²Á‹ÝX<·9s¾™ùfær6x1›äÓl9ðòð~Â$pÃЋi2Ü8ñÆ4»¼¸ªÊjÙãQ•ƒßÝøtW<×ó|ìÉ£îóq ~Nï4ùqDÎì_ƒ©šÒÄs§17öŒ\9ÈqþŠb>ýQý7Àì(ðœ‘?vcå%4¢›§²Ya&PÚ$Uç5½únHÿøŸ3Š0Þ`-r#•;žÊVCºyÌ›ëM!BÆ*-Îhªè?ò×ùçìoy±¼&ð܃Ùk̉®3v}U󭚯…h\éâÓs•Ù©•Hºž |ÊiD±;Ž}OÜ$¡Ø’„7† mô`9¸œõ`Œ}lñöÑØ¢ë1"JÐÛZ¦Ão{.þ™#@Í›ôðó'«ß<­õ_ÏÖEë@ÙPÝAYF¦<»8§ŸféÒ‰0N‘¼Xxrà&jA7zó 7TmèªÈuÙÐUU:¾¯–€‡ïÎn—frfåøJӼ͋f©þÜŒ–y©ô@(4»ÃŽÜLwÖ€:x&=)Ø:U`^Õòö©jÓ¢x"û-®°tBùÓ³iDMØ›²´$¼UÍ5Ù³zA©ùAÿ‰=afèÖ~hønèŽùa¢Â$´aº^k`uœLÈ {»x¡; vkÖ±=û¸ \mâ$€Ž^Ö¿y£çmýÔä÷Z&«¶‰nþþ ´He®IŸ¦eîÀeà,Íü=þúŒ©‡Ÿ^Óä0 ;õâIâzÉŽz~§o”ƒa›4/Q^ž‹›O¶b-OÄð<(®¼*ë"æå"m
+½¾”#VB‘ÒôÓ&Í
+=¤ïA· )©½1("yH?Üœ¤uƒáhD\š€­/I¡1ƒƒŠË¶MZÔAž°x½£I4©}7&“gp•ÐºÊ¢jçâ$9ˆÙ¾ú;áyøL¸Š‘a­Ñ“úü}
+2ÒàЪÀÝ
+Oã"qAWïÑ— ß½‘M±š½2„|iÇ—7F;¼½å꥞ñ%´/ó/F€Øº£Üy§áü…ø$
+&ŒF‚î~(m“j—Ø$Ê#ej€[õƒCO\²t!t[á¯ò¿æUÈHÈ!ÏD½êŒÓÖ™Ýi¯@6݉ŸmWÚõtçôcÕ¥Û!úÌVõ+6)÷ªe…Ê’ËÖ{$ V®» ²Ÿ?¹^®ó-9v[ Ô¢Ê>ßC½ÐE~Ïa*S†’‚¯GD·Æö~\U"4§ñiÈGþ˜ÁÜ&Mڇ鰇ØÖUF­ñšsÓ†‰eˆ…¯¥þÌaŒ¢ Õ ¼‚‹w²µXd{ÊPzÊ¡¤,™$+ÉöRðHöJóWgÝÎð¦YÓ:R¦…é"éxÞ¸îLµsßaæu5n¶WíŠÚ 'ÌÕ÷ȱkÃѪë|‹A(ÒWéØ6_.õ•ã^áVKÒÛ®¹Hã
+²k—¾_Rkã—sT`1ÝÑrk­ãúÖqúÏ‚74yÉOœ*ñ›‰¸ü{a¸„*3ìëÛ¡qUsÄì1ðçfÎk Çg~ãæLz®HYIiÁðä§jO’ѬÒ—l×T² ÈÊ0Ú¤;PHš–)­í£$ØÙò¤É³¶H9s óÀ櫱¿bD÷›:¯
+endstream endobj 1418 0 obj << /Type /Page /Parent 1662 0 R /Resources 1420 0 R /Contents 1421 0 R /Annots [ 1419 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 1419 0 obj << /Dest [ 1422 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 89 87 130 101 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1420 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 1421 0 obj << /Length 1807 /Filter /FlateDecode >> stream
+H‰ìWÛŽÛÈ}×WÔcÑìæE”1`m/ 6ØÖSÆy ¤Öˆ»©-k'’È?æ!§ª[”4#{`71‹Fì[u]O~3½šÍ iš­FÚP‚?üd¥‰³,)h2Íã¢LRšmG¯Þö%-zÙ“P¿hF¯¾{¯é¡%q’hìYŒÆÇÏÃHý¹z°¤‹‚¢Ù£©šÒ$‰§§e\$^®œ1rœ¿ò‚Oß«ÌŽMu*)iþHï·ÆŒQ6‚$Õ×=}óÝ ýõßÑ8ǸÃZ窎µXßÐûCíþa»IUÕ,£ñTÑ?åô·ÙGb±Xc’¸À`ös¢Áápˆ£4Öªç[-_ Ѹ2ÆgµSk‘ôíl¤©¦‘É‹8-4¥“¸,©Ðð$ÁƬ¤ÎŽV£7³Á©Æ–äÒ'ï&ì%ÞCŒ²£,N2lÈâ4-'ä‡G§Nò<ž˜3¯&ùÑ«Iêm‚¾°S'QgŠnmÕmív•ðΔÊݽŽ&ñDO×ß¹¢Ê‘mäÌ’ð“«vűXüéûàÅ8KSCc›è ûÐß?îÏýý[Û÷œöçź‚$£ŒDŒ,Ÿ&â`Y2Myf™>
+Ö^îÛ¶qUÝØ%ÕÍk ÏD bC~ñä`Î8‘àOß.+Wõ6(×vg½{ªÕXÎÃDè5X–A‘ªçD¯Gþ‚:?NpÒ¼m7r>V›}¤5œôÌE'eÒA]œ™ô D¦œëêùÞÙž‡Z±J…ðy•|ØTÓ6ö˜~Ÿñ–XÜ«wvUí7Î[]¾t…¬ö!T®Û[ú VÕ¦·"é¾£¶ëë¶é ¥Kœ­µí>D¢Õ$ƒñ¾f’§¡Ræ˜3X‘[@ÚÃT‹oPíQ¼¡Þ:r-¹hŒâ°¤ðæÞÞàˆÅZŨŠÆ),etÁ¹ƒ §ê±÷¥ƒìž*×»6´ß-Ã
+0à×É sÊ&ãÍ´P&MÔ#¡ú"†Iÿ‚½¸¥âf
+ê,m_?4ÿ]Ó6ø<Cˆä¢_§&¶í´
+­81g\àÊ x KÎ
+áv k*[ëqÄ??ûòKŸ&©Wõ¢rB
+
+qPäyöbêÝvǯ¡g\qU[٦¦³C0ÐÊÊÝÍ dUGÌLyòCÄhWª*«/2¼ /ØËPÒ™Ç#ÑëM5ße}ý=õû7oQ9¨ü@BGïÿò}ílxúý†Ø/庽ýä«öZó”ª 1æeêú] à
+Œ1é^¢‹4
+­Ðäs1{¨ý<…eÞ\(ÿ^<.í96™
+;–^q<{â‡ov<TI?$•‹™5:×{‚Œ¯BZ¹Ü»_äºÈð¶í.\z¨ÝšnËçå:“n—~ñï
+endstream endobj 1422 0 obj << /Type /Page /Parent 1662 0 R /Resources 1429 0 R /Contents 1430 0 R /Annots [ 1423 0 R 1424 0 R 1425 0 R 1426 0 R 1427 0 R 1428 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 1423 0 obj << /Dest [ 1249 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 263 484 292 498 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1424 0 obj << /Dest [ 1249 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 252 471 282 485 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1425 0 obj << /Dest [ 1269 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 306 457 336 471 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1426 0 obj << /Dest [ 628 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 189 403 197 417 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1427 0 obj << /Dest [ 1125 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 432 175 481 189 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1428 0 obj << /Dest [ 1269 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 241 135 271 149 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1429 0 obj << /ProcSet [ /PDF /Text ] /Font << /F1 1609 0 R /F2 1607 0 R /F4 1608 0 R /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R /TT12 1611 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 1430 0 obj << /Length 3217 /Filter /FlateDecode >> stream
+H‰¬WënÛÈFÿê)¦? ‹áðNà àXò®ÛlÜ,¶v± ¨QÌ–"½$Ç}¾Cß±?z.ËhÙé…aRœë9ß¹}çýjövµr…«íL¹Â?xù±kû¾Š( ì0v<±ÚÍÞ^4±ÈZãˆ&+go¿»Qâs3slÇQ°&›Í»Ÿ3ùçô³*Œ„µúÛ,¡M‰ˆ; á`/¶C‡Ï¥=.mÇ_Aˆ»oå¿\»Ž5WžJ'ë'qóT¶÷0âJmÁI²Éqþ݉øË¿­y
+•~]òYVåË× (ýV.ô6Ý-$þWLôÅ¡3½®÷e¼auŸ#ܱl,ò‘¸á¯1€bùA @b
+¹W4–ü4'–-ˆ9ºõŠVóBœÌKÍ *ó5Ñq0p8Ø$›±â>öè’—¬?õjð湟мª›°AX«‡Æêijr«{duîo]2àäQôNlq<v½W(ãò+$KAYA¢ 8ê(úåQ.™šâ€G:k!Û­ŸF„Òøä¿óyOâ1oïtyb9Ì·q€
+_‡§™K6Ú [(W :ò7b‡np›z#'_·' Æ”5&VbÜn, w’É–¯
+Ǧ[Ï¿_Íö}$]ÇvB¯ãÅŽûHÓv€úÍòÃòbßqäÅõÇÕùÅêæ—?->ùázquyµ\ðÔå§ëø×ÍÏ/~éVò:é’ Ðó¦Sn?÷èäqaÃx$ìåõ‡ÅòH÷‡7[¾úÍùåôâ(~åâc“Ç/Âþbyöö¿ò/ððàŒøò,ú™}Ùu^Øþéîî0ë *Nv¿0Ð
+’=îÎH È„‘l©ZB~z7•jNû»ˆÎ.õÅF´ìš.–#ø†ÎP-S-˜±z’Šš‹´´£ª<ð²d£¤Ü÷#Ž¡çØ
+ùò ³0ȹéª-¤àgÍæDÀi7"¯ï/¨T‚ 9”êÃÆèN¦;ê @¶­óõ¾…26Gz‚h)Túua"jÒʪ|ùÚ¥Øqz›î‹– ÿÆ+&úp—xŒÓ<k¸QÔ¢EÜ(êG†VDu‘^u­H·hÏo$_ÈÖ6ĵwP`¥EÁ}“YMíU­3ðàJ6Þ!—oàDÊoÈ„òÖ,î”WF{7¶Á®ƒúcSëPfiqµ˜€æR™êwyƒî³¾m qŽ¤ƒkNHzÞÄ5zò`¸Ã:E
+«Žp™Ë7}€YÌÒH­\y
+^üÀ ŠQ¯®¨/…ÝÞC‘þñ2n \ùL÷œCSt‚4Ú0†ËæÅK+?:ÓWg1‰¬—³E6æK´Ñ»ÏÂi£m¤žM~îš*ï,Š¼|ÊæêØ )€¤…Ô`tµ,Ô{¾ñJ¢áz˜'²ÏCÝ'n%ÒpB7ZÚ«Ò•³±ÞäTkü‹T ŠÅÒønÍꔵiì¤IX+”[_Eè‡4—[õ‘ à–~þÐãœX,pA#±£ÃèèŸüØÚÇEÇap Ù½L½D+I}Âcºù·Ê¸*ŸÓŸ½qkö
+
+endstream endobj 1431 0 obj << /Type /Page /Parent 1662 0 R /Resources 1439 0 R /Contents 1440 0 R /Annots [ 1432 0 R 1433 0 R 1434 0 R 1435 0 R 1436 0 R 1437 0 R 1438 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 1432 0 obj << /Dest [ 1441 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 215 584 256 598 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1433 0 obj << /Dest [ 1269 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 320 557 350 571 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1434 0 obj << /Dest [ 1269 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 350 463 380 477 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1435 0 obj << /Dest [ 1125 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 171 382 220 396 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1436 0 obj << /Dest [ 1269 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 350 341 380 355 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1437 0 obj << /Dest [ 1422 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 218 301 259 315 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1438 0 obj << /Dest [ 1422 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 158 247 199 261 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1439 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R /TT12 1611 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 1440 0 obj << /Length 3055 /Filter /FlateDecode >> stream
+H‰¼WÛŽÛÈ}×W4 hÍ‹DRÆ`
+~T’r¢5˜¨e&òŽOƒÌÃòTæµ³»e
+Õ´©²\v}¤ìNµãq©–²ìvlyìçã²+§z
+ug@Õ¨©Mš•>¤N¬mÝw‰ðm@O
+}ÑRð [Bß´Sa[/U™3aµ÷BWÔµADÅ/Yˆ¥»‡z±[²‚Z¦=êÆìü_\âø×üDZTzÆöf¶ÐN¡ûYõ`&µ¦ ÊŒâŸr‰±å°ó£"S¯lyyÜÒŠmšËɶçö‹Ö$Xôó' GúæxõÅ.ÒÇAâî!”0׺M•êëË
+qÎj=5qpÏ©ÇŠ£Ú³ÄÚb¬HÆ*¦:«‚æ7áýÚÀÿ
+ $ÏçºWTö+¶~Í;¿Æ
+W
+çß›l¬vead¦µÛN.Êáy›I|OW¹)2i¯E‰äQãÙ’m½3R(3N÷¾â¶²Àµ«[Ç'º_ó4x‘ÖÈk áÄ”è1™õíÞÙ°¥¥H¼À•Å¼P)_'axN!¸,Jèj“n¯‰¥éÒÈGáwήîB~$¢þ1½ØÚ’j°Ê/ãã÷‡¼Ú¡H?\⯸‡–röß\µÂÿ±| ,ºÊLa¬b]¸d̓HL{„M{™j•lAÊdpA˜k¾~X=ÊaËA>¦«iHxº¨FÀlPtªFÑïQ#d+~¶6ÎÔÈï ªNïc8?äȇgj4³ãGj4;¨Q$j4µj4;V£žYÚuWDÝcE¼°<« AŠ¶}â6ˆi4MJ@y.æòó(ŸéB(všÚ®í§Æ.XÊ«ýn—«TVHdLƒìïÂþR{-O-ùtÆlè-îÞJbNLÓ–¨`"t{X%Ÿvö´ÌI¨¥96¡©NŒŸu;ªŸ Yþä†Þ¼¡Îkö£„Èæv¶úß^PIâ[«ƒ)+Ÿ\5Ó{ªF‚ŒÕ'éÜbîÜ‚®›'R”Ç_aMÆFŽú²×;¦ ™ÞnMzt £¬
+:“36r¡ # »V|ý´ ÔfÚ rßhw5ÝÁÐBòÕÊÊýEº¾Pc…Y¶¸Ê–¸ž™@M÷±ƒ S•‡ÚÐ_L©:þ
+hõÜ5h3p)ø&ÍMºeÉøÊrÙ2«Ésó•?œ]`Ç{Àyáˆ}¡Î5å5k8¤ÆÛ´¦ÄKÿÁWª¹'ÏYòü((—k ®2ÿ†T¨£ûm‹tRÙÄ–»ƒº$6ë ÌYSmãv
+>~-¹<‡¡ÀKz%€Lú„7 ÝBnÎ;-¹y¢¨c–T!éˆþP‡sšA”bÑ"ï|Yt¤²¹h¹¡=8ž6ò(}oªn¹É‹ˆmiâOüõk¸Z9mmïy2áëÎôæò™›À¾—ðl/ñ¢6«Ú4kµª«M·55§/TóX.UU‹ÕöË~-ý)¬|Q;~H Ö3E4¦~0µ]O}¢€ÚŸÓºìñ%&½_«1DbÑ9Ù–ß·|¥ÂË©ÿí”Ö¢÷­ö9£DˆÖ¥  =ÊWQ`‚ÒèÐ]ƒCÝêL§2!·ÅÃ÷4Þ§¢–BOB0„EüÈ%;í"”s˜ÄJ^cP@„žÆb7˜R^6}e#“ØlYàÅѸ6¿ÊïËÿ^-;Â@ðîWpìE«`8˜ô`bL|Ü<!¶‰ ¡&‚Ä¿w·Û@Ñ'MËt:ìÌB«“ëwR‚êÅMߪ©êeêlqœÚVçtØU›áÈO¡Â°ä¶{Dµg!ò»TAp2UÕù½ÒÊNñ:>ì˜ÄVýKìú‡¬Gø{Òåý#lCЩ¿ÎõRàC¶b»J‰­ \‰O@òy/@Nk\•*¬óʱú!þƒ úÔeÓ• ØÀ4hüðè3„„‡äi–X…J2
+ဌíÂÖBæMYÓ®#ŸÐ³‚fIˆ 1+Ÿ:茑ní“w:0„íE ‹ aœ‡½W‡.¤ 7 nÏÊí›óì#À
+endstream endobj 1441 0 obj << /Type /Page /Parent 1662 0 R /Resources 1445 0 R /Contents 1446 0 R /Annots [ 1442 0 R 1443 0 R 1444 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 1442 0 obj << /Dest [ 1431 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 448 743 489 757 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1443 0 obj << /Dest [ 1431 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 202 141 243 155 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1444 0 obj << /Dest [ 1269 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 381 127 411 141 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1445 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 1607 0 R /F4 1608 0 R /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 1446 0 obj << /Length 2795 /Filter /FlateDecode >> stream
+H‰”WÛŽÛÈE^õ‚4‰f“%ƒlÏÎÂAÖv<Ú—x‚€"{F\S¤–—‘•É?äóSUMI£¹ØÙ…Gd³»ªºëÔ©ÓoƒW‹E¨ŒZÜL¨üŸñ,ôÇã VÓùÄgA¤ëÁ«wÍL¥ Ï T“–ƒW?_u× ? 椃Qÿ¸èOÉU&ž+oñÛ`΋æjø󆣙b—ׄ¼œž&1­þ¢ÿbtÞÈD~¬ƒ™ZîÔõ®lW µõ`I7y£Þü<Tÿ¯7šà½Æ·‰?ѹèt5T×Û¼ý—­ 6é¤Ì¼Ñ\«ó_ï‹¿ xǼ›0ðc¼,.1Æl·[ß‹|£òjÉ-LÃ¥ÇÀuê†Vlé§ÅÀ¨\ ÂIìG±QÑÔŸÍTlp’
+{ÏTm·ƒ·‹ýaDS‚‡§q8Ý€NDóé2ÓŸß4|ü“%tæááøC ÿ½7AŒ·ÞX«s•ß•Uíý©¶ Ñ?›¶6K«®l=lcª/¼QˆCRyãÑY*ù±2ÚÎ@]ÿí¯XùÙEþLWÛwUçJDéD«ntÙ­—V\©Š½ËKÅ3¶Îöà (µI©~ý„ãžêË7 žôyžìó42>mí[ã­Ì´¢-FúƒG^ÿôyáÅu¬[ŒÌtÒÚµ-Ûv„/9²ówÞˆ#vFàq Ë`¬åó¼¤5V^Û,izÿ½7SçÝèðÈÌ~ÊN¡ñ Vÿ¡\ÑmâÏWäŠ]ITG»‹|Ha0ßg“)›ëünÕª¥U¥µb†I½]ÙRuÏ$þ-§L¶?î·/C*¡à¸U¾ÞÖ›ây(ß’¸iÇ*·$SyªR›u½í*OWJÂ*«öQ–$úq_ÖôDûhlK úLõ:&ÑþˆŽXTiU×@E±óÅ&jìjìÃL Æ~ͦJ^ûŠˆ§SQJ`‡hĹdÃ̼ç¸Z¶I^æ¿&•ôm‹£A6ë¼äÑÆÖíÅÐMňÑeQñ—4)ò¬A¶‡nëþ8ŠBÚÿ80Óà 쉭â<³­­×9…6Ï*rëÔ’£u^Ê«ýÖrd±¦áfcÓœ<òȺâŸÌ;ŸÌع ")‚0¢¯mÛñìºlzƒHüTÃjQÉ.ÒÁ
+9qžbs঴Î7­b#‡ÒÀ1z^ªOÌZ¡þ¨îmÝäU©ª²Ø¡¿< ™hïÍÄGÀ{#ì׶u¾ìZÛp 5à—3e]@‡ÁéøR¹“|Ñ—ö6é
+4ƒ±÷ë—]˜ÉALL\©ôH£ÆMrÀ‹¨9BiQŸôC– šè HC¦%ÔÂC]¸9Ofü ér >¥–@ðµ²ëS+»'tÔ Ë}± T z$"‰gBñˆ´™îùâ4¨Có0=oÓV'Üà)? Wyp_mòÁ>{‹ÅÛìáîû€¹*¸ ^+x¤-úïGeÁC°s[‡%lj·xFý#êþèýÚ}GN;ìêS
+ijýˆ¢ÅÊ6Ð(ö²¥MîÅÁßîÔªÚŠ´·$èñ”ªk[£ÈXæ•@ûl©yS¡:
+ ’Iº¶Z'mîøäÑßC8©~öõžå>%/v(²S¥£çñúE§]Ý ùÔ®P®äIåËÉ>/ ‰mRgMŸ¾° Óê—kº
+i\îŠÔ°¤™Î´ü%º‰ô²Ø)ZSð⥭nÖTjRŠ4£â[®¯®<:ysˆé1Ã
+éÜ;7;g…>mx:õ6Ä>qÞžîi͉^öH
+H ;u<×.”VZÈâχÊLÇ5š%”AÜ$ø7Ô¤ïUÓ¿¡¿é~» ŠÀ€Q#R»›i?]ÅÐkœ…´*ûU9}ŸèR9Û½½è½®éÜ|ª_÷¨Þ c‡Q”û!‚D³Ÿó$ý<®u¹÷Ò5²ÇÏm.Ú¼ @!Š²ª×¤Ü”£ƒžÄÿøUmŠ$•ù«ªÈ,ÄÈž põiŸpAô‚¾}xq „µ"T_¹,Ôb–ôÎåq<Y*egeðLåbßm™õ0ê†O"—9¤bœ¾>öÇŠ}›ø†d ]ïÜM y# Ãf sdÏIûàpaëÕAßšè[²»ïàûƒ\ëÜ­ÇúHºÐ¸ÿoà+F#80'³XüCÛ/ÀØõVX† M >0ÀËhÏ4`Í¡ƒ½Z@ï}Ek e ŠJ@n†pÀÙÌ*
+°ìËÈ/ÍI×BåE@Wâ¢ÒÆÎ
+ñ>X
+hPZfNq5­Ff‰Bb°Ê,(Ê7ŽôÀÅ»k
+endstream endobj 1447 0 obj << /Type /Page /Parent 1662 0 R /Resources 1449 0 R /Contents 1450 0 R /Annots [ 1448 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 1448 0 obj << /Dest [ 1431 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 449 757 490 771 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1449 0 obj << /ProcSet [ /PDF /Text ] /Font << /F1 1609 0 R /F2 1607 0 R /F4 1608 0 R /TT2 2027 0 R /TT8 1605 0 R /TT12 1611 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 1450 0 obj << /Length 4135 /Filter /FlateDecode >> stream
+H‰¬WmoÛÈF¿êW,òiYX IQ$’Ø9øš»¤±€;4.ŠZI¬)Rá‹õ‡Üèì‡>3³%[½‡Ú€¸\îÎîÌ<óÌÌÛùèå|(_ÍW#?Pþñ“À C/RñlêF‰7Qóíèå»&QYÃk<Õdåèåw¾Z7#Ïõ<k²Ñ¸îGúSº6Ê=åÌÿ1šñ¦™Š=wAð$q#Oäòž€·ÓhÑî/ú_fÇçŒý‰i/Q‹ƒº;”í36$é&oÔ›®Ôßþ팧x¯ñmêNuîx:Û\©»}ÞþÓÔ ™è´\:ã™V¿ñ¯ó÷ù#Ö˜µ <7ÂËüs|ƒý~ï:×× jèXˆÆ‘.†žèÌNmXÒÍ|ä«\‚iäN"_Mb7ITäÃ’
+:†‰ªÍh5z;?câc‰wnÁºYD³õ^¾æ¢ÞñtæzÖCúO²8”Å8Ó*d¥Ìç‰ãz¡|‡Íè¨/ú1„ñ0鲑WS·µi;˜6-úÙ|ùšµà»Ó‰µÉô<øbO•¥¥ZÕ8S7Ç
+ü¿3…Nz1ýhȦ¶H-»-`C -甉ˆor‘T*ŸXìc
+ÊR+ ?•û\«ÒGõ`²ÇU釗ÖQR•¡É*,I½HÛ=ö4"Sªµ)íÍR ¯™ª¬^^fv-•ÂÃ9I õï†6´]˜ 1ã±Dáꄘ,Í22'Ä-MóÐV;E4¾HadµßäÙÔÈlµID<±
+`»2[£k*¾"@›Ù\ ظè¤Ý–
+
+Yx¹êbÄ9°§µþiõÓ÷‹ÇŠH<ÇVø¶02i?Q½Æ¥%ßK½Ú!$}Õ¤±°LB5)ÛlUW[^z¹E²ˆ×g)!º êÑ 0`ëOÈ$©Œ”<Zn§Òú4¹€ƒ™úÒc"»½–Õ.eLè3ç7ê5gTà¤P[±¹}vTùÄvöÈíO]>Êí¾‹D º}ýšr
+@ÌRl0´à/ÿÞ¹bã, ÓW–vötª~.>BZ4àÌÒÚ3ÓC¢¥“ƒ}ÿH¼´G…j«ß`Cr
+
+`ÛÛœä D°öáu|/¨é³'få¡äQ±ÆÛ§¢ÿÐ^­­mcAô{~…È'i©]ë¹”Ònê@h›²kZÙºé
+lK‘ä¤þ÷{fæêaY²óa—@li|Ÿ3çÌ9ÌSóÔË”¶š}„¶IPÖD6D§­"®kWÄF P p9Kqu|Ç\§ÏºÜå
+&ÿ£rnT"$U‘±‡EsŠÖIÌß7’MyP]Šõ<2…HXÚ¶[[½0Ga\‡ýÞù6.é™AV¨]œŽrxÏ …b6ÔZÜòR‡Âo8Ø—=¤k\^Á­ëñíîöûHÊ=ú^™lÔ½ <'òÒ w’À³HÓ nFHÚ ~d£8ù•@½Ò&éhä-˜v¥”ë颬·Wôà$SÛXÅcÖf!ë8]°¿’§ªv#|¹gÂdq
+õí¼Ú%·QÐT1)Iš(RΧm>kòú‡-  í7ÒÚçjô…&]¦Iô~‹„
+ó=}U«”>bF!!`õE¿DAüHEÃãˆ>}3cÿF2Š«ËOÕ]C±_8°‡ì#ÈÒØGöuT‚­¹¬Ð¤éƆl_æD‡Ké_Eýn繂NÝŠ?{ES$ºÊ+=¦m'-ƒwˆâ㲤9Ý g9bðÄ+c·“êUˆ¡ã‘¹€BÒ‡¤ãQèÑ"ƒ‡ãΓ6ÀηoMLGìP\mìz×D$
+¬so²ãªÈeEÌ3²°€4[ýJâ;‹8t]¢Ü¤e·“%çù!ã¹R¢$ŒôI7p­!ømÌ2»éáŠÈE«ÛKVêu{3zÜU˜ÌZ›ãÂtÍ<ÉÊwÆH«^ûཱ_—†n>–c’Þ]jëå˜êa·&ƒ¶Šô3Ó¿Nùà*Y®²,ž¤ÛºÅäêq—ä\÷/åkÔ•Òx!Tw+‡»¢e²­ßŠÚÊÞø'}æ˜zV§{tÔ²nk
+`È\ ë„­++ˆ.bqúU‘7ÀŒw¹D±ÇŠ³»ÛôEÐLǾ3˜Á(W‚DOTBÿð3áÖÚïD% Ä{z˜óeÈýC>nþþú¥Ù©7>ØþhïnP•¾æónö}ñóúëÝâÃõâç§Ù³Ø  E‡îl(FÙâØp®z‡ž ê5C4ÿiÿ;
+endstream endobj 1451 0 obj << /Type /Page /Parent 1663 0 R /Resources 1457 0 R /Contents 1458 0 R /Annots [ 1452 0 R 1453 0 R 1454 0 R 1455 0 R 1456 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 1452 0 obj << /Dest [ 1125 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 152 563 202 577 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1453 0 obj << /Dest [ 748 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 71 523 94 537 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1454 0 obj << /Dest [ 1125 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 260 482 309 496 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1455 0 obj << /Dest [ 1459 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 421 262 462 276 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1456 0 obj << /Dest [ 1459 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 89 138 130 152 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1457 0 obj << /ProcSet [ /PDF /Text ] /Font << /F1 1609 0 R /F2 1607 0 R /F4 1608 0 R /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 1458 0 obj << /Length 3962 /Filter /FlateDecode >> stream
+H‰¬WënÛÈFÿê) ¢Í!)’2 ‰×Yl›M¶µ
+t ŠY¬yQD*ŠöAúûŽû£ç2¤dYNú£{±8çÌœ9×ïœy3]Ìf>h˜-FÚÿÅŸ0ñÝ0ô"ˆ§7J¼
+z2 \/ч!þ{ïÀ‡ßäN¢ú!¦ð!â%®¾xüìáî€Ò…™oŸdcð÷±LKá%–ÕU3ïÒ¢.›,-‹¼ýT^ßݾ»½™¡ÿ=Oýøú÷êæÃûÙë›Ù/¹ýùÞù“ÎÛ¿}ø±ÏÃ^GŸnèµ ¯)ùÿ´–Þ…ÚŒ»ûùýÍ/V»«‹g*Ek2üŠ§Ù'µˆ¦˜^¾Õâ
+ó¦6_º"¿Öÿsu±'=Õ!Jb×)˜/2)„ùr>üunïO»Iø•,zÿ†a;´õ†iŠ{ tƒ ‰A–=NDaŒ`„„h‘ÖÃB(°€†Ð§§ˆ‚«*]]_ÂÝ_ßA»b’ÉŠE‘ wQþ@@%øÍ¡Jy½²‘¨¢~€…I»ÍÚ‰é|kb6KŽÍ &jûý@÷ZjQò¦©)ÅLE}Éð«A¬í_"¢ð¡£0í[
+=S±Ÿâ]Ù%¬q9¥Žáã¦4Ÿ—Í\|*ÏáÑììvôðqÌ'ࡽöf÷öZm©üÃ`ïiö’?èLŸ¤óªÜ´Ðv)õ­‰ªrunWëì×à™K¨±ß#Å2*sŽe´0kƒ §=‡n‡Ö’ n„:‡ªBŽ;ÛâWüÛ­7uÆç#Õì’­1ý
+æ3̸wÜç=ÊØcõõ^}‹W³¥!(•„Yqõ'dý@¦°bdV}^Ù_6¦…>ÝjXòW¬>£KšßÊÒÁ¤RÍ– »ÝÚn»qH}t
+Œ±ý6òh¬Rv_¬*\ayÔU£jXa-v9"ÓeÜ$šºÜq1+t5í úô¹|ÙÄ1[vÔ²&C˲E¶X7 "ô Eäwn[ãÂTn/ *­•49áÔ2À®ennÀ»MEFÝ«M»±{JT~Ø~+8àÒ>:š–XøݲW‰®\œ&êÓËRñé…½%Ìâé>‹§biSî]â[_m_†K–‹8Ÿ*ÒNBʬ)7Uí…>âÐûz,³þõë
+z2!kc@°Âä2#rÓŸQBy áPøP}éãÞ°„wUïYœDJ½‡m¡f‡¦Mñoy–¡Ò‘‚Ì0È|V s‘Si»ÄîP“>‘2¨¿ ¯ËB°éQ¼Q5rŽ²6’N8i½Êzæ˜L’KE¡úÁêCFâŒCõ·ËF8m
+fHÕ·ÙfA§jpàĵv.Ü5GpÇ0&˜GâάŒ4ŸŒÝó<´¦Ø*Ï`e#ÿ™=±©ÉR´Ñ²ÄñiÎ(IdË,’2Ó4'g´D ÕO![%T›úñ 5»åÆ®Š:³òŸ–ŠeW”ãÓžX5²wgËɈ‡ý,rls<<Nè“ÌÏ
+Gw¬ ·œ iŠÿD5²¨RYÝã÷ÉÔ{àó§É{
+!‹¦Ûi*Í\
+³à¦nb·DÄâÞXê†s_*8QŠ óÅd8
+qxdI“§¾+“¨|NK!m¸‡™9a ?¸|[ ãðÙËï
+gQŠÉù@DP8Õàõr
+÷8ò²–-ù9¯¶Þ´‘(üž_1BªdV„ÚŽ1v•DDÅ*4t‹¥ªÚJ‘fƒ0KÜvÙ÷ýß{nc|Cw_¸Ì™9gæܾ>†-F2“+¶ê‘›¿¦?Òô.†-¿¶ “UÖÐÞŒÙî·W¨7w0`Æ‹ä)}‘Y/Á(@·ëªcS^¥{5œb[#¦ »ñŸD‡òxGc
+ÂW$ËŃu§TQÂÉ¡»®¿¯—u]€}eÖ[6û‡¸ NIÃŒÿȪÑãç„9ГÙ
+§Á•ô%v&þÛ·ö{Y?à—g1ôjÄg—€µš,ÞÑ›ÿovJ¡U}¤ëOÚøoAˆ¡™Ž0ÿ&˜û>gôFha§DßÚ:œR4(éÔuꇔú¥~Gñö”L—‚ÀÌ
+ҪǯÔcÜÊ5Íb¼f5œaØíã-C`÷?Åy ëp\
+q•3d¡€U•Ù9 `eÀʱ{xu®Ÿ`é‘®E(SAÇ°×hÏ 2™pÎc£öˆ µþ¼ \qT€%¥z`! C1þYm‹æ¯›±¹`Þz·ýVÞÙãÐwŽ? ~ÊîÿdÖsëÊÌf½ÀàŒBÐâÿðç†5æËxWX•D9îr”2‚ 3 *Ñ…HžÛ¸~Km°bÂhê<M2lu$kîuÆ£'…b³ß/¼«ÖìÌÒ
+endstream endobj 1459 0 obj << /Type /Page /Parent 1663 0 R /Resources 1468 0 R /Contents 1469 0 R /Annots [ 1460 0 R 1461 0 R 1462 0 R 1463 0 R 1464 0 R 1465 0 R 1466 0 R 1467 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 1460 0 obj << /Dest [ 748 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 116 502 138 516 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1461 0 obj << /Dest [ 1485 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 483 421 525 435 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1462 0 obj << /Dest [ 1485 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 92 407 133 421 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1463 0 obj << /Dest [ 1492 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 480 407 521 421 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1464 0 obj << /Dest [ 1422 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 402 259 444 273 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1465 0 obj << /Dest [ 1470 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 149 232 163 246 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1466 0 obj << /Dest [ 1492 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 260 218 302 232 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1467 0 obj << /Dest [ 1470 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 387 178 400 192 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1468 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 1607 0 R /F4 1608 0 R /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R /TT12 1611 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 1469 0 obj << /Length 3483 /Filter /FlateDecode >> stream
+H‰¼WÛŽÛÈE^õ}IÒ¼“c {ãÅv ;ªe1Ë‹–¤F–?$ÿÌCNU5)Ž4¶ß‚4d³/u9uêô›õêÕzª@­·« T>þð/ÎC/ŽýTe·‰—æ~¤ÖÍêÕÛ!WåÀs|5”íêÕOïõiXùžï˜S®Üéñ¸Ò¿ŸŒ
+²P9ë­nyÑ­Ê|ï6ÅÆQìËkB^NOIJ«?èÿ„uCßqƒÈKµŸ«Ç“zjÇFBm줇jP?ݨü×q¼÷ø–x‰®_—»õþX_L_ó&‘.ÚãÞjõoþuþ¹þóŠ=foBßKñ²þclÁñxôœÈ ô@§:[ãH¾êÒíx§?­WªÔ*LR/Je^ž«4@$|ŒsÕ›Õvõf=#
+0Åst}Šˆæè!G±ÍQìù1&Ä^å™’×)¨Y’xY¸ˆªŠ'°Þ!–9b¤îŠ¾/N÷¯ÕÆðÀ¶j«±Âc¨»Vu[…ÇD7Å0š^¹ò¶á™ôHEUÃ)»~céÅQ*þúAFa2N,#bûC9z3L^å—È›]ño½0_¸LøÄ¥·] ;Z³QUûšó‘ÙŒžzÙ”QZž-¹ÛV¦Þ4Åþþ—×À šm×ñÑôÃSQ9¨ ]¡©và«6¯¿~¬D`†¶‰íU«~ý6Ò¿¨'ÓUתkëÓ¼Õ•9Ñy¿|Þ/·±(ZUJ<$ BáAèë» Û2dé’p®Y ”¦øÜ;T@foŠÑAÑ$ú[áߺ›?1$
+ÏôXµ%¿ßcÆà¤4ÔÙ™¦Ù§ó¸AÄmJ»q!³ä×îv’™¿×´jcŒæ›Si¦³§p&Ížçoáåäæ]Õ¦MmÓŽƒœÖv•ÐéÑ4öªþû‹lTu×™eDÝL@²F‚öÒ3;¦³ ÕH.Dº$¿]íG²ãÑlÀêdÔí×Ѽ4»É®x óøŒ>/;ÉÑp [„Z~‰Ø¼\Ú>fRë|"Ärfm+™·³Ó§‰î-lÛ;ˆ›U™PŠÓ³]k#£íýבÎñ•¨Y6~ ‚‰õ8öÕãbáRïþN †¹Ôð
+—íîƒ^;):A@ÛÅ"ZEú`©MœîÕX|R˜C“x0ÖVè.ŠÂèŽ7¨ëîXµŸT·A E­
+¸Í[‘ïè<?óλ qda˜Ív .ÿÀ°|O¨ŒmXô%K_™CŸIÀ~à8#¿_ E釋0Ç^_2¬;=RP^Ã7ÆFO©C«dõhÔA^Áßc§äyïϘ²B(ô‰Âƒ¼š
+Á!ßAïXYÈ£ürS}¢Ø1Tæz£>j»ù5BQƒW»–¢ŽÝÑß0™#¾1ÜÉž-¶F
+Ø» (YjÜqx½€_¯M¶£¯o¢íêŽ:îªr§*Nõ~Åkœod]ã¯èf¼/ͨ¸Ó~s þðâYØ]
+4¹ëŽª)Ú;êÅ
+kœLÕ†‘Ž8öÕ8šÖ³8GÎÁ{‘îü3ÝM"桵y/Ô¾;š~{¨Ucp(Ð AëQ0œ“Ïäýú±… f”Bx2V\†nByØZ…‡H²ñÚzã¾”p?9›˜ˆ‰5vF3 î!¾ Ûn5vT9¥jÀá5Ø ¡ëx° &T¸Ö%wŠ¹Í–
+zËÎ0‘×áÊ<´–³yñÙ¼X6õÔ;˜e>;67LŽ…µ@ ·øƵ‘Î- @Ü6X]ôMÁ5Pv÷
+ôÓ÷=Û“Ì,a/ †Œ±ýü³ÿì<ÎècÃY?¶úˆX©8g‘ÿÏÉÑH‰H DcqýÖ,E;﬈ ¥ÕíD
+ÅÀ¢\ì/~$
+ÙT$¡:|Ñþ“ÿú¾_3wÓïÿà…¡©ÄøÏ‹•‘È€2’l¦SÒ3ÝÜ9 ëÚëÖ$4·hˆÜ„Ë€Ùú;¼¾˜\”RÖf3Ò†¦0Ã÷Yü)òûr#³[í ˆ]¬;ñï˜Å©^Nƒ4œ£kŸê¯hiÖŠfk„VöÛœ*ÀªIÈît²Ó›>… åáŠ,¢Qõ¤·waÍ´í v÷ܸœ
+(‚›±Ï*é”Ó¡B¨ ô‡ÁS Ü.yãºs‚°³tYµ=‘r#‰*1½ßL4úvLN.—D›ÏÎTT£Þb¿ a¼ –sýAÒ2Jã×ÍëÚV+Å ]˜[ˆøŒU»µ¸×·IôÜû†*£V Û²åu¾½•G—Šà)ñl)xJ)âý½†Ü÷¬ÚÌ©â­'ÅÛHþøZh‰ßöªl[gO$«#1>§ßáoÁaíØÌCè;sOÊná¨ñ"ÍLM`Qj\¯´rôŸõk¶XV“h­L´Æ8÷p·è¾x Osw•­LrOšB´ZÕŒÒÎx»@5·K½ËCèÑÚÙ,—[8:K‘»É¥-‘£W¤HëYßÅE=A¹Fºª²¯†äòZCê.uÓ‹²¤æí´fŸ]éy÷ØéZઠâ)}@í´#½OáfòwcP/—q­±‡X“?HF’žÂ‡ “Ž=Cj°K<9ïŒfÀbv€70áÀô„«—gÓ›OîæênÕyF â¦Q³–LÆuªèNé#DoîHÑýHüÞ eïZG
+Ew þ ¯|²Ñ8©i0ÕâIßÊÜ*P>Ší¦NþáöCŽf,‰ÆóðÊfÞ³ Ñp#ÒpÔ
+endstream endobj 1470 0 obj << /Type /Page /Parent 1663 0 R /Resources 1483 0 R /Contents 1484 0 R /Annots [ 1471 0 R 1472 0 R 1473 0 R 1474 0 R 1475 0 R 1476 0 R 1477 0 R 1478 0 R 1479 0 R 1480 0 R 1481 0 R 1482 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 1471 0 obj << /Dest [ 1459 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 428 689 436 703 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1472 0 obj << /Dest [ 1485 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 419 649 461 663 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1473 0 obj << /Dest [ 634 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 254 635 271 649 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1474 0 obj << /Dest [ 1431 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 163 527 205 541 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1475 0 obj << /Dest [ 1485 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 149 500 163 514 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1476 0 obj << /Dest [ 1470 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 267 487 274 501 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1477 0 obj << /Dest [ 1492 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 89 419 131 433 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1478 0 obj << /Dest [ 1485 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 89 365 103 379 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1479 0 obj << /Dest [ 1485 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 201 325 214 339 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1480 0 obj << /Dest [ 1485 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 234 284 247 298 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1481 0 obj << /Dest [ 1485 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 278 257 291 271 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1482 0 obj << /Dest [ 1492 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 275 244 316 258 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1483 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 2027 0 R /TT8 1605 0 R /TT12 1611 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 1484 0 obj << /Length 3763 /Filter /FlateDecode >> stream
+H‰ÔWÛŽã¸}÷WóD¶FÔÍrÐi`7—Eò°0ÈtØ6=­D–´²<nχäòyÈ©*Ê’íî^Ìc0˜¶D‘ÅbÕ©S‡?>Ì>><Äʨ‡íÌÄ*Â?ü¤E¦i”«å* ó"JÔÃnöñwûB­÷<'Rûu=ûøÓ'£¾ìgQEsÖ³Åðxœé¿Ø/N™e¢‚‡ÎV¼h¥–Q¸Êa8)Â<»¼&æåô”å´ú³þOŒÑE “„¹Ž
+õtRŸNuÿŒ‘X»
+c½öCÏlé3£J5‹³<Lr£’eX*7ˆ¤ÂÓBun¶ýøpFb0%ºŒÆ݈"¢9zcfŠBâ·Ì£ÿe ¢†¡!ü‘à¶kv
+~«»méªMUîƒ 1èïç<jƒœÖÙ“*ë{Qþ»*»ÎUî«­{UnyjÕ¬m¥¾+
+a° t¥}ªN´u«CÉ@8bü¸`‰ ä'×g£uÓ+[+Û-|Ô&ÄqlL>¤ßxï;KMô‰~ŒV_ý;6–‡'?• Ù"`\ŒÓ0OÎy\K2%Œ#Ô‘½LæÈ—RV°O /$G²Йø´ öÎ#ý§€@¶ R:ÚÆÉ0‡ÁÚm$¾wV†û Ñ® 2Mÿ¢XÛͱgøÄ<*+Ûþ^5Þ¦VY
+l¬O÷NZ©\Pè`Ü­½Û¨ë˜²Óéè*þƒÿ°b
+Ôéã+f¸¥B„kÈœÑÂ0ꆣ|
+ühÍô â”Ï~%ì Ò-sçÀáñÈá·Dêyê^1i™¾ý*£Ããä{<¶2ÜÜ‘§N8ÙEš6ŽBVÚëCø-Py;Û
+-¸mAïÖJêOñssœsiÁÐ.‚@Ú5’ê!õ®­\ïÂÛ&wÛݽ˜¸±ý˜ºñ7BÓŠ; ¸¦&i2ì@î±+ûÂÜMµÜ¨¡Rž¬Ÿâ¤)K¼·MU±(Dk8Ò‰žQw®öS£®ðÿë­ÚL;ׯfûÉ7>ÅÇ!†æy\‚öÝ$s›i§0}'Ëf¤ýA¼ÅS¡‘p˜á€ÚÙ=¿÷¬ªI‘qùqëF6Lß²uB‰àQÎÆurid\ä•ÑDíËœw½+k?ULõ´\Fˆˆý¶êîBC‹ÏîrÊýöÝ‚^ŠwŠF{Ö ¹¾ej“ƒ±˜®ßj§77ªÏ:™¢"aT˜†Q‘LP¾Âö¹¤¼˜¤ÜLÉ©Ðò׊Ìðää©© …‰¸h„$T»öÏ2wZðû°-•Ž`¯ §ƒL)äµ–)lek+úusQX…(¬œ?ús}y+ñ˜‡˜éàB¯0ã\ÐÒšMºèÆ©lü¹ 韱…I þJu|VbÝ^^˦VàN¹ àNÁíË·8½*|ºÊNê+…™»«‚=Ú± }Ë ¿ÈPÈ)RM]ÔÀýžö=¾€9t×3Â>3…s|±\,|·qĶ·ÈÈô‰æþB`¨8!"?Òô#™v­JICÒH#•üFîé[é!ðE ¨<
+~ÌÃa÷6¦ežì&ÃÄç‡öæªùYSm+[§w“éÚä qܤC¯DDà©¡þþE ’àù±§èA£&Í–•òl¼ö´LÌÆpg€DļWWWäh9Fr^AÿÙLp”«$"0fS(f>ê›—oq
+pƒE2¤³Ûðʹ|F+^œq²Œ¯èX!â‰UÉ]A¹£ÚEEd”3ò:¡öÚ•†K>,gŽß×åMž%ì²Úÿòå¤'ë²éÃúy€'[&5X[ÂñuÃÃ| Bá"?áUí%åCí]I‡E’‘нlÊ#­˜XŽNé7&""åv»Drªi¼ø(÷”V5+J‚à|¨5àpÉåkT¿ éa+[¸‹òw±EmIž¨î–C·a~Ÿ²û…‰Ì³;;#²îN&ªó¶ݲÔã¥.¹CIÙÓ… ¬„ l=ª_]òov½Æa H%a8WÑ|N‡z¥ –¹yxXNäŠWE<êÉPÄ€ñ’[E ÉøÉ}u·ªs(‡Å¤¤¥Ç)]BÆæÁ¹&Nà
+Ђ^À–oNà† %dI‡8’ðŠõÉ”õº:lœãØPüöôCòy  1½xU§ÝZ˜iÉÌë3J7äh­ÖòÑY6Έ"=‚GœŸz,ûgiÏ™±ä<!^é›±ogcßöì– l+€/½ñùÒ[ÊëÀÛ’OÙŸ? W^ºÎz
+î³”¥åôæys3èݾ‡ðvÄŸ+ 'aUêÀ§¼‹=ëUº±WX¾i@LmÇkOËö{(OÂ@ìòWå ß<DúUÒí Ö"ÌvÎÖüÀ`¬›3<¤Kî Î}""Ìw}§jç6¢†FÿÍ7Ûaæ;jí¬ÂØtÿÚý ü;ȳKý»;Åk>àãŠWM½˜Kœ±`®«­\ì*QÊR‹ë:?z’YX°q/€ !ìý/“Ò7²=l^¹w¾žŒåwió§«@íl"¾ ±–úNñ.ô.¼„üNåÞ’ÍÜj6‘ë·rmþZ÷ûlÛFöå,Ï“«ü1ùaP?Å4ÆŸ'-&t
+)Ž©Ê­×žP/ÍeCÖµÅbSÀDé¯ï›]lp¤\0öîÎÎξyó«’Éxšõä‚궧•lAÙ4 ò¦ l¢îN¸µn¥Oò[³@ð+Ñ€ñkÆBòå4Y!+Nnî°8%å¤+µõKXu†^/hÎت ¢ÀÍØËçÔÔ&"~Õ úÄ#öHX˜HkYä5H>ã¼ðïxoú:ê´ñchEóD¢K[3>ªE<׶ëÈ6$9‚„Íâ.*q„ÁÙW{2J57£Tt³$ÜœŽª½–ÛÄ­«Ú73°ê­F omß9'‡óÇUà5†JàNɽåðÊîÏnwÞéøºÙh‘E»•¤ Nt(‚ÆåÆWFÅƽ<·¼ÝÆì¼]#Ã0·j}}”<§løƒŠ|©ÉW¬‹+Þαq¶ëöª§oO¬l¬·¿p e”{ž@eŠ½Eâ» À­>Ó³î<ýûJ?ï褶 Ä2~ ÅÙßB„šš¤hL„9Ñ4“œô49³@‘0ÛÍÄ+4˜VÌ ].Ý”f3ÁsÆÍ­!~ùÈCk Æ¨ŸdãNž¤õ˸²J<iÓó†ìËhñ¹®Œ\à¢v‘ª‡KË9¥¨!D©k&\
+)I Ë2qr±õ~ØœlC‘ä©rJ¢È¹Jü±¯q±Zº½k?kÁ þ•‰'mÚ£aªºÒœ“Hå=&—öm.ítÜIß±­üy¡Fpžx§¥áÛjµ0X\Þ l,Ì4Z–ÀôãB$·K“\.ytp;‡›½ãœ$P‡»¥úŸàå¼
+!Yj‰C á“ãÙ[g›`=ªÑhìÜ°å΢ƒZœ%3+‹ 5_{ìÒÉ‚bný`mM¸Lþ9ð&Úæ}@pìç6¶÷ÎŽæˆÍ"(n&BCÏH¾ýXÝü`
+endstream endobj 1485 0 obj << /Type /Page /Parent 1663 0 R /Resources 1490 0 R /Contents 1491 0 R /Annots [ 1486 0 R 1487 0 R 1488 0 R 1489 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 1486 0 obj << /Dest [ 1470 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 425 703 433 717 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1487 0 obj << /Dest [ 1459 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 273 451 314 465 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1488 0 obj << /Dest [ 628 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 251 270 259 284 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1489 0 obj << /Dest [ 1459 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 71 189 113 203 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1490 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 1607 0 R /F4 1608 0 R /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R /TT12 1611 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 1491 0 obj << /Length 2554 /Filter /FlateDecode >> stream
+H‰ìWÛŽÛÈE^õ?5ƒÍ;)c,Àkï.v‘Ë" ž<p¨‹YŠ$jÆÚÉ?äóSUMê6¶ÉCÖìî꺞:õÍbòr±ˆ(¤ÅÃ$Œ(À?üIŠÈO’ £|–úYÄ´XO^¾ÝTídO@»ª¼üþ}Hw“À‚{ªÉtx|š˜ŸÊ–Â<!oñ×ÉLÍ(üYÁqágÊ•3‘ç§4ãÓÌß#|F7 c?3AA÷zhû¾DÆzdvõŽÞ|Cþ‡7Mñ¾ÅZꧦöS­nèýSÝÿb·‰MÙ.½éÌÐßä×ûËâljX,ÖDŸáeñßDƒ§§'ß‹ýÐìøVË×B4®ôñø‘©Ü§•Húv1 ©¦I”f~œ…ç~QP““‚¶vò0ùf1:#±%8÷ÆÑ»{Ĉ÷£â2FE¡žÌ³ÀÇ=ÌÞŽˆÔÇrë±OX×ò&%¦±wž—†/ÄWÚuT÷XÉ Á¥v·³­ìÓouÙ>uÚËâΛFðª¥Ûµ—Àû姭ü…«¸m#›(Sêá9u¬ˆµÒezq*«þÅ>È»Jê¼)\dT¼n$'¬×õ=ë’:E-Õª7Ý–º®¿¥Çñ:̇˜OCŸóÎù)’ŸØc}GM½®{âHŸÚýšuI̽ÝR÷@¥Ä>4Ûmy Ûصm{Ø Uéi _NSÓÛ–úNÒ%„ݳ,ûò¾t;-˜_«ZÇJŒ2ù>äb¥ ©©0r?-Ä€XÖYëäòD CºÏ³ð2\½B”Õ;½U´òq½ð“Dá€Kû ©à…|¯• -Ûi—\‹lÍ£'–¹cÍÞ>˜%b˜ÀߨBX×b;ÊpE·NÄÆzùñ®²çrìn<X^SxçùWûLŽ#jð’Ö£þFÜîÅddv'_K}Ó_Q"5'!d¡®v•ÕúkUã%ûE¶5(}bT*¸¼Ú«[Å(ñE$¾¸?Àü\µ{¡Ç%ë§R'¹~¡–ãù"ØáÑv—­\13¹!4¶Y`ŽUƒèÂè/.ƒfú¼|Ô]UŸƒN™Yb ÑÖh­µœÇ,Ýèž²Ÿß¨2V•eUgw©Àö#U]‹àîÝ"ê–rÎé`Uô¥ŽO"šÍ ºÊ­qä­ûÚ"4’ðúÇem 9Pw#9ßÏ%X˜œ|¶
+Ž–eŽ” æó;=cZVö½;Ê
+
+v߶Ïš;N¡åÒôv;gÿe Æ…p¯¡q2Æ#à¢ÛŽ£˜y[&7™ùúP(ªÿeÿ¡ìG,
+I°¥g’ý´Ñ'é7¹Ñ_¸í¹s¯ ùÿÞØuyÅs£Ð³SŽãt S›Æü5×&&¤5Eµ(“÷µÜlšZ‹~Ð.tê¡ŽâÓ|ÊÆøgŽ[Û]OkvcöË 0åæh/ }<ø)fœ«V¶úY˜„èÕ*Ð_ÐÖ\›„Ë:áË%X8oe„côˆà\fvµa‡fÄ4bœq=‘Ÿ%§¬vS*¿t,Ty¥¾èl"O2“(¥rwfnä†÷‡¶ú¼ÿVÖɶt&w
+ÍÚŽ‚­*µr»
+endstream endobj 1492 0 obj << /Type /Page /Parent 1663 0 R /Resources 1496 0 R /Contents 1497 0 R /Annots [ 1493 0 R 1494 0 R 1495 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 1493 0 obj << /Dest [ 1459 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 452 625 493 639 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1494 0 obj << /Dest [ 1269 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 441 196 471 210 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1495 0 obj << /Dest [ 1155 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 71 129 121 143 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1496 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R /TT12 1611 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 1497 0 obj << /Length 2064 /Filter /FlateDecode >> stream
+H‰ÜW[oÛF~ׯ˜—3€Åð.*0 ¸‰ÒfáMÛˆEuöaD,n)R%©(Þ²ÿ¡ÿ±û3£«íÄÝ]ì 6D9sîçû¿Î/ò<ȃ >þp‰³Ð‹c?£q⥙‰|5xñªËDÑñ_tE=xñÍ4wÝÀ÷|?Àžb0ÜÝnò{}gD0J„Êÿ2ó¡±ùÞ8…à(óRßÊå3!§»$¥Ó·ò×O‡¡¯†Aä¥ÒÏÄì^Lïë~‰'¡4
+’dWvâú› ñ§ßÔ0ÁºÅ»ÄKd©|Y,/Ät[ö5mÅB"©ë¹Ž¥øÿª?ç°ÇìMè{)ùk<c ¶Û­§"/i5¤¢¡Òíp–,i’QŠA˜¤^”"yY&Ò
+¥îÍ
+y¶¾(›²ã€_“ƒ±ìû¶œmzÓÑ2d_@j>o߈»§nj³ë Ïd— ×f¡7UoCýNç‰?´¯Pwÿð8þ¹JG°MšŽFØ+âHÈuˆ$R£[#6n!E·o€/z~!lߊ¦E™»=j¢ÖÛ^p§ êÁ½0OL 7¢—üšWü Â!†/è 󴺣£œÙ>îT
+¹»è=º’ÔÄ„«§
+ÝúÎý[Ñ$üÇÑähº• ¤?•UgÈÀÝ©óÌŸcË¡±³n¿]ˆ~YR«£"K2Eº…½06
+Â( aòŸÓü àͲH$ﳿšl
+Þª*­Ü;E\p˜µ“ŒgíÀ?çLù(76Çi ÷ .Š·ðˆ~ÿy‚ q“—‹ÒTó•^_}~û?d4–q<!uøjL Æ1
+ãYûxoÃØ Û¨+œ%3Ì'àv†ÐXcÒgqÙpw‹ìÌu¯gº3;iÏ¡¸ÿ‘9}xÐqä00¼³ñ{jn·eûØðþå€nÝSiùƒ~Šcö(¡>Ô•4«,K *´SS–AgŒã¾zIÒ E€îÅi¾;ê[§¦'HÁ¯zý³a5ëJ]<ñV%<ôÒ&¶rÙlÀdÅÒ?3×o˜øœÔf=<§¢s¸qøÛsCõKMHDCŸ1bUÞ-{ÐyÇå9FÁ=k™k‹•½»;bŒX˜¾Xbn““.s±h›Õ£GfNU‡!¥ÛKážw0%7ͯÁ,™|o+3}OìòƒºxÀ+·ròî5m²¯ß}wCÂ0 Â|ò[åË?NÞåSÚÀ,Ý•«²Ò-‚º j/;±nìtÕ•³ÊOw¦6-èû÷7 f0@÷ ¨O|Ú¯³’h$” ±²F¿CÔìèÛèZsùüôíä½¢q‚Ï ÍßEÐtò vk#¾º~Þ|õÓ®³lª¹â†Ú}0ºÏMûˆf¦àP:ìóÂí*w æŸÃVxNL­¸ Àf:É”7o Cy“Sª9á”…œ'Ša·6E¹¸g­6~* é”0œ­oM¿ik‘»s?N8#œ ›PÖMgÅïsó GxV´ÆeÈõºmÖmIˆªÁÒÖ2Á…ÂÉjÃx„)¨ß™õæúf:AŸÂ
+Ûú˜Åë;ïA¸Ÿå?5f7}XÔ`øoŸDŸ+±Ô 2xAcÿòð58 @Qã#˜~XŸÄ\f–:è³,x‘FÇ"§ Œ!ÿH^xÇ¥!m¢ËB,6uaen(°3[åŒÔ!¼?†Ç¯‰oð?öB÷OòÁß
+endstream endobj 1498 0 obj << /Type /Page /Parent 1663 0 R /Resources 1500 0 R /Contents 1501 0 R /Annots [ 1499 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 1499 0 obj << /Dest [ 1502 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 482 689 496 703 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1500 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 1501 0 obj << /Length 773 /Filter /FlateDecode >> stream
+H‰l”ßn›0ÆïyŠ£^ÙRpm ª®RÛmÕv5©h«¦‰’2QÚ,»ÞLÚ;ìw±Ï6¤i7E
+pìóçûc_ÁqQD¤¨X*"‰q‰8–†Ò<&“šŠûàø²Ï¨êÝI}ÕÇW׊–} …”
+{ª œ^·ûP.kR©!^| rç”S*EnXgÂH×ùDÎݾ%Æz߰߬a$y¨´0Lft»£ë]7ÜÁ±š#뛞ίfôé|o°–ˆ„5\²ênF×Ûfø^oZD³²›ó0gôËýóÏÅûÀ)vj") >Š×°¹
+¶Û­àZ(ÖÛ¬µM‹ÐH)ð*EĪÑtç"½)E Qb„6Št*²ŒŒI‚Æ8£M,‚‹bC+l‘Ïi<Ñ•–sôУxìQ,dìÛäÞ&ži¬E
+Sì‘G°A
+dvëúÕÑ€}Æ(Áã›_æ·<ƒæ#€fg3:­ürÛÔÎÞy§)B͵ÙÿyŽ”'`ǨàF¤¦ž„ &Uj›NF5Ú«yÛ´5]”}=§k[Al{oìL@Siõ†VºlÔ•²ºÈËìå¼ï)J#¢ì€¢šÒ*ŸõrÕ eÓ!iÓ¸)HÙ>ìxŒŒH§9²ƒíCx÷ÓžPÙ®úR­ºQu³<{Y_è"*£)í†?– ÇemyË-Á–ƒ«O^£öãô¼,ˆŽ«Ü»žºµ«n t% 67=¢c±’ȱ¾YuôQ 9Ã@¯Û²[®,úßB?\CTˆ>|C÷Òº}X6ÝþÈùšÐõäÕ¾¦È—4#K
+Ý«í¸{m¡w²Xô^Mþ¤&QÏË¡\`HÖ\)Œ^iKËØpwF¸èôÞšsVŽËngiW«²³r¬Ñ)6ŒæõÂõ"'µ7Oì‚EërSÞ÷´Záž8˜á§ÆýÛ¾÷«ù(ÛSþÔo=ÞBãàøK
+endstream endobj 1502 0 obj << /Type /Page /Parent 1663 0 R /Resources 1506 0 R /Contents 1507 0 R /Annots [ 1503 0 R 1504 0 R 1505 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 1503 0 obj << /Dest [ 895 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 332 689 346 703 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1504 0 obj << /Dest [ 1538 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 369 689 383 703 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1505 0 obj << /Dest [ 1520 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 111 154 133 168 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1506 0 obj << /ProcSet [ /PDF /Text ] /Font << /F1 1609 0 R /F2 1607 0 R /F4 1608 0 R /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R /TT10 1606 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 1507 0 obj << /Length 2453 /Filter /FlateDecode >> stream
+H‰´WÛŽÛFžê+~jÅ;©‰w
+8E„ßÞò&8 ÎFçÙŠæ°Q}²±;ånQTOn-Õ•C‘àÛÌòà²PzPÕü†Ò|XîôS|¨ŸX£
+œ’s.r"ÛKz¹-@.ドš´¨°vQÝèÇò8­I‘ÈŽ[Žiy
+þüimE8ëÁüTÙC†YP.·çûé™
+@†mªP+Aaé>-ÊtVZ: Å|½Õï2MéÝ6mŠu%Ös‘Š §Î ¹~E2Hâ=åÅÖÀh÷òÊlõ:!8 u¬S›ç-––bµ&°Â®ÔŒZ”ÿâqYdKñX”¥˜ñ
+਽ÔÚ`F²‘µ¬NJDõ¡ªÀ¹øFÕÕu˜øàJ¨W˜ó»ŒÚ4kݦvYr™Æ³]Q6#õƒTö¾ŸO³&$î%©t+1;NWóìhÒ“þ9M$¿|£ cÔ+0„iÉ2Ñd $Ñd¹|¼‚¶¹R™1ÄĤŸí)ŸºTxŽ\þ`]à9š˜Êãý!<ƒª†.Ÿˆ+Є–aUJIË-È?„ÿ„`5Bp÷ý÷µk»‹1õÒ|Äü4ãr:*Þž²tÆù¶þœ>8%pý s^˜{“( N swc(^›‰VäQ=y¾SÖ3“xgfƒaö®ë:dýÍ9š“¸'↠ߤÙÇtº÷BwæÒq
+ ƺ84Ì”ü•6¾uc‘E"Ó»(×3”ˆZ5öôE“çÓ¾n„ƒ€ú“?ÕSÆ·ù4éÙáˆK\ ÷æWœ˜­ˆÍ–Ofëv(HÿŸ²™¡qtÈõõÑÿSü3ütçðåSȃÎu{­æ1”Ç‘µªk2U»fy{U€Cv íø\í©’›Ri¾BåRîÜ^uv|ºã·›t›®êÛ3/sÕ@»¼êš§»²a ’¯
+êµ&Å’Ïu×[~·ŠªúIo e ¦çÆ`ð­Ë ´µö”¢Ô8º7¸…f>pñz:>M3<!¼á¤/¼GÁaÅA÷7¤>&­/ØDly ÕúSÛtøD»"¨+ól›rµÈ¸:|T¨Öyˆ&P!--6ƒ½º†Äu;u¦ï;,~¾
+mžc“.çb÷Ô¶n;Jx=JxÑÙzÑþ6h½p`ÔS^ø4-qmRv½œkz9B’µ5G—Ecí€l•èârŠ¡/ÓM£m$é6"òù¼ê;§)×·Ç­(Ÿn«—ñÇ®U~á½·¹‘íèLô⎟ÿÛÁ¡ªÇPDlÛqnÊ!‘ÏZÆžy~ôzQ?y‚ž Q‚QëþO%¡ gmØ1é>-ÊtV…NÓ Àn²Yƒ&9¾ŸÞß¾ûök c3ÎUýQQ,¾Þv0C¼ULrͱ{Vú;&º†‰iI–ÉÕ-t µÌ?#Þˆ<þÿ{¯5­Rn\öé˜ïÿöw}”¢L<ÃÍ =Ìû&¶ôâôÈ÷óœm)r{‘é³Ö~B‹·ï¨‚Oä æØZ „VQÚÉËéà×
+endstream endobj 1508 0 obj << /Type /Page /Parent 1663 0 R /Resources 1511 0 R /Contents 1512 0 R /Annots [ 1509 0 R 1510 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 1509 0 obj << /Dest [ 1325 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 269 712 297 726 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1510 0 obj << /Dest [ 986 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 438 159 466 173 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1511 0 obj << /ProcSet [ /PDF /Text ] /Font << /F1 1609 0 R /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 1512 0 obj << /Length 2095 /Filter /FlateDecode >> stream
+H‰”WÛnã8}÷WýD±,R÷F6@'ž`€A?íd1P,ÆÑ‘ KNÆó!ûûû°u¡.–å8ƒ
+\/€ëûI,¸Ù€‡¡ëª
+?+›‰nH£R%[8)Rˆòz6ÒË@±&>ä­QºPQXŸ½*½d £Æè
+š} [ó{,æSá÷X,@ 0Ì[±4Ô“å¯Eù ‹¼ÀÕ}KºüO: ¹
+ã‹Ñ†NÜ0˜³k"‘°{QÑÄÚÑÈ ƒ•›riÄ °Ä*øJ¬ —òœ·uñj˜ ¨Vß JÏ…HÞêH¦(Ÿ7 õÖ³îâÑÏ!K¤E¨M-‘¨Öu†Ìl ¬„·ÙY†cr»ÿtfvo8Iê
+Qƒ„]Þ}Ã2ga˜4¹ç§¬^§­té)›ïºÊ­ƒÕd–x—ü€HµUÁã*0–WS›—˜xÝ÷k Ëh›qÿ·u™ Û˪„ù 8ÔT -Êö—mAöôЖ虵)ŠiìB¶SÌ)ïe•ÈDUïˆ6§làŠ_Äߺá²j¹+¶}þØW&Hù©³b]áÈ1&V>VVì
+„(Ü®ƒJ”¯%áL<À½¨†»‘Ž“?ý™½n×ækoH7ûŒ°JŽhŒhÀT”X{ÙŸæy+’‚µž'…3Ó/˜{{¼RØw:{̤ÐU¢‚÷Y̯Õ?¨åužÕÙs±6Û Ääì-ÛÍà2«ðL\^ÏŽ¬Çpù)¼l†…õ<5¹“Fð@ob¿çÍì£òLΊ̳Æ0jŒçOg|òÇÖf_øíE
+l7˜|ÆþyÂö} Yta-aë–°502(-:ϪÞì@IaG}ØúøÇ‚4A"Ë/7_éCÜC™¡òç<“š; 5(àjK-Ë­´—¬ø!ÆãÊó[žf‚åú­XVCf?¹4hl¥{á ÙþnS•@QEùé%é ÑÇQGôºãImºFÊI%ñO,ßì¯@x”d)hQŸ_ºûÌz=¥õ[¢™vÔçñòßP, wÅÓ¾6V;¢·p•]ðV‡„]™¡\Fßèu~èô¤Í«íï²R,4Æ+¼äêέ¡H¾nN8¶ =yÛ¤œÕÍñ U;9:äàá¿<h³’7ö92ºx4²xó^:ƒaï,G4çw£–\B­;6ƽÜÔôƉI’øT6ŽëV&?ŸTãB¼ÝvÿéknA/+”JÚšX¾d[¸oáçŽ%£ j;š×LÛÊj‘op{K‹q売Øâk*‘¤ž2c߇¹  è= !¸IòƒÞ’+A‘®ÈÀ§éùCœ6ŸÙ‰Vi K
+ú†dàÔŠ$©á×eivÙÚmÿ Í*ÐÅ¡îëâQšu•%ÚV‡¬‹ò ¢¤eQÓdà%y ê8I:uÌÖªsÜûð„Ç,(8 ðQšÚ7£Å^#öˆ<<ü"8©ÜÁÂo^lÝeå£ß濉™øÍ™FÈ]xf÷
+ä`'ÖI¨)iGà 1Y™‹{¡åDÚ—ß)Iíi'vοFÚ=W°k  þ?
+endstream endobj 1513 0 obj << /Type /Page /Parent 1663 0 R /Resources 1518 0 R /Contents 1519 0 R /Annots [ 1514 0 R 1515 0 R 1516 0 R 1517 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 1514 0 obj << /Dest [ 1513 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 261 730 291 744 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1515 0 obj << /Dest [ 1502 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 141 661 172 675 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1516 0 obj << /Dest [ 1508 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 141 578 171 592 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1517 0 obj << /Dest [ 678 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 97 346 118 360 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1518 0 obj << /ProcSet [ /PDF /Text ] /Font << /F1 1609 0 R /F7 1615 0 R /TT2 2027 0 R /TT4 2032 0 R /TT6 1604 0 R /TT8 1605 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 1519 0 obj << /Length 2492 /Filter /FlateDecode >> stream
+H‰¬WÛŽÛÈE^õ}jRï—ÁDÀxm/6𮱞²‘Ò0¨H<ùüCþ19UÕ¼ŒFZÛHlØ"Y}©ë©So“«ÅÂWžZ¬&ž¯\üÅO˜ú& ÝX%YdâÔ Ôb;¹ú±IÕ²á5®j–õäê§OžZ7׸®‡5Ëɬ{<NôoùºT^’)gñIÆ›2•¸&‹qpšØ•syÏÛé)Ši÷ïúß>¾Î|×™y‰µ›ªûgõé¹nðÅ×¥ƒ“tS5êö§©úÛœY„÷=d‘‰tå¸zù0UŸŽUûÏr¿áC×…3Ë´úÿïü}ñ— [ÌÖø®‰ñ²x‹o¬Áñx4N`<ÝЭ%]‹£q¥Á£k|½´Ÿø¤w‹‰§*5ñ£Ø±§‚Ĥ©Š=xRÁÆ0Uûr²š¼YôÎ<,q_zcð®KÑì½!2ÿ’Ø5ø'[à5×ëÝO¤þ›gÕ”m[ÕkíU›;³À$z­n7‡uU;°!Ôwuk›v·/íÚb[ÕsÕîÔsÙL6h:éìe_
+ÄÅÓØW5í¾[Xíjµ[±rwçû™g`¹œòÆRȶ»â°)Õ±ÚlÔ}Éä˶zÊ[žO3q-®Yd »ƒ†mÞ8”­CËv{'¤Õ9äxb6‡I¼âæªßE_çtf¤/œà$‰_Ø”O*å#Öd¤tÌ)-†üúqAêéwÎ,ÄÏu·ÙÖ^`üÌ&…0¢Éêgò_E:ùH
+$ ^mòµê¾Èüèv*rvw¾Ï·v "à…¶‰?Écõ®%¿.wµ¬©Šr_¦7-´È7„z¡ ‚4QòÚ¥bœÁk騖!³µìÚ0òµ¾“Âu}öÑË9#¡(óëÁ™QòlJ~ü
+Ÿú«ãÐDþ¨
+ÒÁo©\ý~·WEÙæÕ†,ŒÄQ‘.K²ñú>S}¯cS£ÞÔ`dj
+$bHCÏ6“¤’:-*ØúkHH?:”ñå²ZUË=bãû=öy, U¶å¾Q+¸)‰\DryHá1pn ç×cÞ4e´ÃòºsËäæÜ(qÁ,1@͈àfC<³sñŒ%žqÏðûr7
+2㇔°œ»áÅÜ-Êû¿¯W`Cªóõ7ÅðÕC~o¯_w`Ù<J„U|£–N \Êñ¬kroQ®ªþ¥€Ø-EÉ‹îkµÙ­ñemƘôN¬½~fx†úH ˜ê@-¦L–”ê¾jåQ}ÖxáfÔè¯$…ÑŸ•d"¹Ô£}€š|ëš³~
+¢äF«ÇzÅt9\0ÒL°û}"0ÞrõŸ,²w(bP­GØ’@•VoP˜Êª” ÑC=ˆÁù–ê-9á¬ü>;zÇÈ rC˜"=¹{³ù£ØMϱÊG$¶"ŽÑ-"œ’§bªn…d~ø0Â9.几N¤Þ$§ ÐVt•
+ï‘Z±¶»a²<´Pî—þ
+Ðq8Ù}^Nè+×x®e&`nßhc̉7|T¤oÏÌèø—Wž_¸16ßè"–Åÿ®oÊ:¿Ç0‹]ª„Ý#EÿÏ]\Í9eF
+Á°ÁHóÔ…äì°!“†àzpÑe'œŸ½±ËH?yýæê\"bŽ}• —¥”ˆVz9/lÿŠ¸»;EýL"^’Ã#ßÈÓqJ?x¹'}ÏRô,<SôL(z`ÇË ã%S£Rdy±­ðîÁŸçSÞÉ2û„~ÛÜåü½à•Ý¶ùTÐË„(4‚£Ðõiñ^šØ¦yN;20­à£ÀH#ÐE¾:<xYørx8EL4ë–Zïãs†Ì
+°WùZƒ*ªÕªÜƒ;u:`ÍÃGOØQ¢½Ü;jBóŒœWWM»gö5};H•xÐ*¶Ý顬eZ¡#É5BW‰Gl…Tï"\¤G¨žKÃÁàˆÁ'oÊ“éQ˜Ñ$ÄçL}Î5KoÔÎm?=&/ éBM*µùí<Rеæªë­YQßÍ:Ï4l°ª]½yîÍ’PÜIÛ»Aºú‘ÇÎz-¨œšóróœø Ê®ýò9ˆ+Ñ2ï#ÜYu6„Ù ¯MhNЂûg&9‡ºŠÌ>æÁ‹€9îQFRƒóoòçRD<?U24EØ >bª¡‡‰Ò9З ’v P ^õ–ãûÖ„ú¯TNžþ¨ä¤ðÈRÒ]zŽýQ&„CR„bàm­vO¼/ÓOUyäÙŒf¶uõ$ùÀ!-hb!ÕZêWpᆢÝ×_Í TwÜ“”¸kq_áiœ–g傈 ŒÁ
+„{Û'$)ÜVßïŒâ¸'ò'[·Ð:¢†Ê)"«3ã>Ÿîø.ßÊ] mòN‰Ì;AÝ þÞìÛ­Œ_ÎŒ (A•Ê¬4Ö¢vPéá¶ON“ñ#†›Ú¼{»À)Dªo=ÔíÛ_~æs#ýkgTÔ^”}ˆUì÷<“œùþéð‡þþß
+endstream endobj 1520 0 obj << /Type /Page /Parent 1663 0 R /Resources 1523 0 R /Contents 1524 0 R /Annots [ 1521 0 R 1522 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 1521 0 obj << /Dest [ 748 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 164 277 185 291 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1522 0 obj << /Dest [ 1513 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 292 86 322 100 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1523 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 1607 0 R /F4 1608 0 R /TT2 2027 0 R /TT4 2032 0 R /TT6 1604 0 R /TT8 1605 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 1524 0 obj << /Length 2730 /Filter /FlateDecode >> stream
+H‰¬WÛrÛÈ­¼ò+æ-ƒ-Âý²•M•e­]ÎÚ±+ææ!VJC kРeçÙàTþ!ÿ˜‡œî
+#ZýNþÛÃ×¹çXs×·#é$bùI¼ýT¶·øâIea'ÙxòüLüí?Ö<Ä{±Ðea9ru{&ÞÞí?T½áM|™•¹5O¥øÿµþ¾øÓŒ=fo<ÇŽð²¸Ä7¶àþþÞ¶|Û• ªèXl#m<:¶'WæÓ-ïôãbæŠB̼0²ýÈ~l'‰ˆ\ )àcˆZÍÖ³‹Å ßÅgˆÆ]‡‘ŒbcäâhŸ ŒÃÔö=³
+?Žlý‡òéÄð ƒd˜OÑ>Ÿ¢a>¥Á
+ßÒRÜmv7E)¶x d•ï6ÄJô¡/o/ºnÕÇ6çyK‹´ê?5A§Ô~wzO Éy÷H‡/ô®‹•XîŠMKª2€àÆ<W–{£´=Ÿ÷¦,¿ˆÌŠµhö°GÛðV"3¿Ø"_Š¢lUÜ@®¹ee+%ôSŇê1žý9G7CoSÛêK§åç.:KøiðUPõy®š÷mu'V›B•í™€'­ÀUš¸Ù‡¬ØdKX?pÛÀ!œ}k¥ppóy)p“oþbÍðZèmÚ—î!ž|憟ÃÍþ¯áAèù hŸýÖI °]Ä6–âY±Q¨Ö:òK«xäc ´c™/{¢¦½Æ®rawä+Ç=Ÿ»ˆ®j+”*ká6‹PäÅÚ"û>ú«xBÙ
+ýJÆòÌïÁIßÔõógÆ2Ÿ›˜qWþŽ/@Ï lˆ…¹¤íoF‰ñKãÙŞ˸\\^˜Yæ φK< $LÓ@»w½Ûí®ó¬Íš¶ªU™m-×)•Ý~lÑJ]R&¤Wðƒ™æÖôƒùÝ!ú?©O#g渴†¿¡CZSË‹—×cžÛºwÒø 3X Þp'm-Oj•ËkD!”E«¶E~½.Ô&/ò±ûí}øÖx0åå=ŠGt˜Å Ë1qÉóÜÄÀ!ðuºP`pŠ\}(Và=8GU&”Óq‰~˸$ñ'¤X\ùj”m˽
+äÖå Ù؆ƒ­ÉØÖo5Sçë'oŽðOÿWû”K­0‹GßõÛV¿ð.P™¶Uƒ*»½«jkN*ªÍPªRQå¥ÊÕA×9àñõuh›Òúú;²gèL@¢à@&ïPÆŒ¥2DEèLÜß«[±ªÊ6+ʆïHºÁcžEí&ävƒ*«¨u+žŽT¢®¤g([[?wáX¯ õª6!°ÆÙb]ÕÛ¬·™öºA‚’Ò[<·õnÕîj%Ì ÍU<N¶Ð_¡k¯1û}ßï}~ òÊü8y¼Éä}Lèm4€nѺ­·€x³Û–±nóœ¹äÙóŸ_XÔ</±°¿’Õòµ?r´‹b°¡ ŠŒwRZFè¡41è&^Y]|»S³’…ÅM·S¹Û.»g4ù1€'8L‡ô 1y£¶°–+l"Û^ÉF¿§²n‹òFtƒp‡¹Ø͹²ìqQ×Fuö+kÒ·ÅË@úçã
+G-½o—0±w;cq„»õ™Ð¦ëátJ,7mÁ‘.Á$_Þ0Ç©§W[ñ’_+=X”Wr´9µ°3Ú
+ í8 õ`׈†Ã~6)º¢›†ÝöCA
+¸KzÛÁy‡£–è£Ç½Ä†Ö=„Ö5¡}Ã%tc‘Çd”JÄÕšÔ
+4I€²SðèŠ
+Þºà’ÙÒTa¾ávKM½´·„ºª~¯xG=•]äæøß™«F=#=P•k¨a¬ L-šÐöµk
+endstream endobj 1525 0 obj << /Type /Page /Parent 1664 0 R /Resources 1527 0 R /Contents 1528 0 R /Annots [ 1526 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 1526 0 obj << /Dest [ 465 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 456 541 464 555 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1527 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 1528 0 obj << /Length 2016 /Filter /FlateDecode >> stream
+H‰¤WÛnãF}×WòÔ ,ŠÍ» g
+‰çNc¤nì‰\ÞãóvjE1í~¯þããèØ÷œ±ÜXy),Žp,›5ŽøÊ8(IÕy —?žÁ?þëŒ#ìïp.r#•;žZ®Ïàþ7ÿ2»‚…*+WÎxªàßüëüsþÓˆ-fk|ϱ3¿Â1Öàp8¸NàjUÓ©†ŽEÑx¤‹MÏõÕÒ­YÒõ|¤!‡‘Ånk7M!ÖèI@Ãvfô8šÍ;g—x§Þè½ë‘G{c”>QëÉ$ö\üï]é%¼ñb•5Ùc^˜mÖ¬ßX)6Ò¤ 5•6ô¡ó$tç0_ @j\9ûð-,³–ëª6%Ô[³ÌóeVGÞ>B³Æh4Ù৬X™Ç¼4«3D ¦Œµ‹ê·gÇýÙ6ð|¢a¸à(ÝÆñ1Õά kð4ÔÄIpdPÔngÊÈX¨yÁ²¼½S>¡.ÌYÈšq“Û¶Å'q×Ø‚” Zå´î1­E¹"3gìãgÒ5Ô Õ¶È–¬yªøüGÏTU"ÀîÛ°F: âEm˜PuîóþªXAcÐÄX}nV W
+aJ!L$ÏQ& K·`Õ Ö¤«Ï€ ¬Ž¦ÖÑ퀗>^Ò/Û,“büÃÒÜ>áT†bÏвbˆÇñèwxDMÔ &ɼ ÇcHÎTíY¯²¢ß‡Ã•´hwrÊ‹Ö_AÏ‹Ó–CfÅHɲ ëi¤KZ1¤Ä°#D
+„¯,¢ÿ(¿Nê÷ÕÆ@až0Í(lG{zžRÇ‹¬‚’4-«7°—)Cúm@GØT'—Â+·
+Ôíäê7ãÀ/2Ž8”Ó ùQ­FÚ;UréfµµZžY]nY—¥ìº£C´º—mÖ€ç Å/~çà—Däçûª$s#á`Ê,0Å‘xB¦N}á[_„/¬'Ä~ëöBë_|à‹ñÁ¯ä­i§Ëª:t“΋p Ã/ñ-ç­ÐfoÖH³bbÚ1ÍQž…öûmq½6œËžlÛó*—X–\3ŒW‡1+Ù€ÜäpqL©ÁÉ©ûE²¦‘~¾Ø7ewœ—¹pJBº%¡ëÏÙf[˜ó!¥"É㈂ËÆ[e^!¡ï¾9äeàƒD©7Wç[™®>še#öðàPàñé_LNKŽ|íÚÊ=t½/µÐ ‚4é¶E|˜¦nâ‹øž¥<ËR¸#tR*.-9o°‡F¬ölFê÷ÞÞ\ßÍ~âUˆ¨ªE€ ›ÆQúZÕ0ïjÜŠ¹øýXF³XŠ7èæàÕâ#òÜjÏZúìþ~ùJÁǤëÛ‡äŸ\âȀ5¥µµ­’¸¶i*Xã]­»ÛK¸ºí:SÔÄç»P;¬H¹Ä|±Sáyc6_-áì‚ÉŠW!ˆwNgÂÔ>]ëeï㘨ÍãgÌ …m%ƒò›Ó”Vd >Q×”#ùNêÙe% ùEK Ì6æ :á°¢ ³åIª0c¨p±IJÉ).ÜW­ü\†:ð0nÎ {õŽ%<ìZñÉ‘NT¹„OyFÏ,©p—2ŒO®®¦­¤!¿l³O6»ðUÆûB)I-ù·»9Þ êšª˜óçDR¥‚”=ܵ½9®óòÉ’ÿ1{H„ÆXò|Äç@ºó±ðbâØZ—ªßöyÛDê“ÆR>¶W×t¡¨]^í:îŸH¦HˆyyF°+ ¿'2‡üf³l̦ý v=®²¢±EUÉD‡Æ'Ìäëâ(%mÔ–h TÖp]^ÙêÍæ
+endstream endobj 1529 0 obj << /Type /Page /Parent 1664 0 R /Resources 1536 0 R /Contents 1537 0 R /Annots [ 1530 0 R 1531 0 R 1532 0 R 1533 0 R 1534 0 R 1535 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 1530 0 obj << /A << /S /GoToR /D [ 0 /XYZ null null null ] /F 1077 0 R >> /Type /Annot /Subtype /Link /Rect [ 75 465 160 479 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1531 0 obj << /Dest [ 895 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 332 689 346 703 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1532 0 obj << /Dest [ 1241 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 350 689 363 703 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1533 0 obj << /Dest [ 1502 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 383 689 397 703 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1534 0 obj << /Dest [ 895 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 197 662 210 676 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1535 0 obj << /Dest [ 465 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 71 384 88 398 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1536 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 1537 0 obj << /Length 1696 /Filter /FlateDecode >> stream
+H‰¬WÛŽÛ6}÷WAR€­ˆº;XÈ AÛ(°~j¶h™¶Õj%â»u>¤ÿÐìCÏ )Ëñî6û,°¢†ähæÌÌ™ñëÅäùb %뉊E„?<Ò2Ó4ÊE1ϼŒ±¸<Ó—¢êùL$úª<­Ä¦ŸDa)œ©&³ay7‘¿èªŒE°ø}2çKsQDá<‡â¤ óÈéå;1_§U–ÓíòŸÒY3•„¹ŒJ±<Šëck·ÄÒÐ$ûº¯ÞOůÿ³ ï{ìea&ë ’Õv*®ïjûÙìV’HÝ®‚Ù\Š¿ùðÛâÇ {ÌÞÄQ˜ãeñ2¶àîî. ’PÉž¾jè³PO†XFa,+/Ú²¦w‹‰µ˜ÄY&¹I–¥Èð1-ÅÞL֓׋‰Â‘èK4Ft#BD2zˆQêc”†QêÂÄ«Ï"MÂ">Qô€*ç p
+)®ª NÃR6µiíËÑ",€èl€Âêçâ ïÚMÝÑàz áð’p.qXóM¼ÁÍ\E×6¼Ck/.¤‘*W6Œì©Æ%™VuíºÞ%Ì:f Íx$Ò’QôÝqèZau0+!ßôN?)/³÷„I”‡qyŽÉ$oºÖj¸¶uû‚cZÈ“Z_yX YAg£ñ™SrÕ‘Õ'çÄKÑ­…×Á0ƒY"Û
+ÙPÖˆu·½±¶nñ™¶CâýÎTõº®„íÎòŠˆÆÇ¥ãR8s=ÌÆ'ï
+ªøÝÕƒ‹n˜&YÉÔ¡¢dÄ(;Ù”8›N©fGJ•€Åÿo]9!=WÚê¯sE–£?ä
+áàÕQÜm
+0–t»òAÍP¼¨VPTJ|P“ “^ß­‚bÜj…Wé„+Ó“ÕÖ<©ÂÔY…½5k}h¬ƒ¡üWßo4»F[¤æí÷Da¬J4]¥mÎÂÌc™qrøD32KèÝ®©ý‘•r*pý¤Ê‹FO1g±5®ÿ>܈·>xÞ:ôÝ4G|õWu·¦m¶by¨;Cšßê–¬Cg4·tŸÜqÅ7{0ÇjÉâÚ
+->ú£·£— ÚOÂí‡9©1ÝTtûnëÏ®ôݬ‚xy¶ÛwàÓ?ã€ÓgVï7¢B|#>“3úW<è^v0ÎÞ Z ùr¼9EÂH±k Z*¦°5ÜC#aMo«®:f.È÷ÐÃz#]\‡‰3àü,߸Hhü„` \ánµ¦  ‹S iŽ:H¦ë$¦™0?„##©ŒÒ
+ÊÒ¼<Qò8>Ò,n‚P,¨Si BT(ÛÙ¯©ÐMgMÂ&ºt«›Ô0Ô«ÛºaïÏÀ˜¡Ê¾ŒŽ“Yy ˜g›úOœ=LûM·¡¾Šr­ýÓ=0a·£y.gÅp"¡»PÊsñÉÈ\É;¿'2å'²œÁ°(&3nVÐŽ‹ôÑÖŽ·¨gažÜYùÛ·†!HóÓ4°"ºC3Ï 4êÁ/¦PÈ8³hpc²&Þ”•:t†­%×|_T y÷A$²sŠªŒ±0†ÁlË~a‚kG­„Áئ. àþ$ûÃzèìŒ{̳œÇ©vŒdpä/¢ÄÚ6Gá¤<›‚;Èí‘bÝ…gwÃXàä4w{&ØöœcO×N]ûÂ{5º¯¼ÿÜÂàæÎ= @Ưۊ}™ûäÃóàžˆð~Õ%.'çCßBu°½ÚßñÊh3‘Ãg¼TÑŠ~š’óßÝHøB¶ÚQÓÌ…v‹q­êœŒÒQÖñ§všðc.ƒiÍÔ›aÌÓb&A+P|¯Ý\t"Ð
+endstream endobj 1538 0 obj << /Type /Page /Parent 1664 0 R /Resources 1543 0 R /Contents 1544 0 R /Annots [ 1539 0 R 1540 0 R 1541 0 R 1542 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 1539 0 obj << /Dest [ 895 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 332 709 346 723 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1540 0 obj << /Dest [ 1241 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 350 709 363 723 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1541 0 obj << /Dest [ 1502 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 383 709 397 723 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1542 0 obj << /Dest [ 895 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 197 682 210 696 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1543 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 1544 0 obj << /Length 2164 /Filter /FlateDecode >> stream
+H‰ÜWíŽãJýŸ§hͯ¶”xݶã8«ÕJûquíHÄB§'18v°;;„áxG~pªªcg2™Ù{$4ÒÄnWW×ç9ÕïW“W«U¬ŒZÝOL¬"üá'Íã0M£L-–ó0Ë£D­v“Wú\•=ËDª/›É«ï?µé'QE2ådvz|˜èß«Lž¨`õ§É’7-Õ"
+—'y˜E¢—÷ļžæíþ¢ÿcuGÁÌ$a¦£\ÝÕçcã¶X‰µ  I÷U¯Þ}?U¿ÿg0›ã½Ã·y8×Uér;UŸ*÷7ÛÕ¬$ÑE³fK­þÎÿƒ?¬~5aÙ›8
+3¼¬>b-xxxƒ$4º§S- Õ82ÄcƺôK[ÖôÝjbT¥&ñ< “̨dæ¹Ê "©àcš«ÎNî'ïWC0‘èq4ÆèFÍÑCŽRŸ£4ŒRI?â¹H“pŸ4qÇda°@ŒÔ›2ˆçáBוmÜÛ×êC#6-B¶Ûá1¦0©ºj¬ÂÛ\ŸKCA¦g-Ö3ÝPTørTeÛÜW¬|s äºðÉhÙPù ʼÅèM/чcùeñ .ÅYåg.™“GÞ¡mã
+˜¹VUóšS²ÐƒZ_ÓY¸8%•6ÎÇ"‹’7}Áå£ÿiÊ?’3AŠˆT›·ª•GUÀÇÀ˜0GŒ†ðp5ZÙ‰Š£âàgú ¨XÿÌõ'QSr/}Ÿ±•3Rç|ôùˇüåÞÝ¢¡@“ËâíüoEG<èˆE% ¡4õjmû²«î8zÊmÑHåÖÝÃäÎvêV÷Ö*,§š² ßúV3¥<yᆅרGN²ºo;Õ[窇4-: BNõ{[V÷U©\{j<r8>¥&Z©¡G2¶lÏbȤ mS-¹>
+Õûã9T]¸÷EK㹩 g%o«ÍözBàpz¸Õ€TU8Ñઽ Bõ»­m†>ªšÊUE]Í®¦Óh| ‰~%i¸ÈI¿0b…Áh‹veÁMŠ£»ùUÄ°ÊV{ƒ‹ƒkw†3QuzÆÒQÉ^ÆèTÏÜ\@bò¡b®°ç&òÆ2býÕ©º}ðç?µì¥Tøh‡ê× š¬ºWÇö l¸ ÕƒGᆓ¿ïìWöƒ¾*zlŸ{JÞp*e·¯­³õѧ¸ôÛuG3”&L\Ú8GÉÙk¶†á³¢)fCúJÌ›éPý$ºHÓ̲“ ÌÎ)LÙ—#SЈçÉí[ò"Ó¯ù™! Á‡€ü«)ãüîxºÛòªÚY·myy- ßœåRhËþ£³Üÿ öeŽFŠöš hÎÃ8‚èòÍa €ZrÀäµ]¿V7<.uÛX0ÁÍ]AC#¦Í5š–vë9e&Õ7?/ ˆ'ú¦!S—£÷ø6ò¤?8_„t
+”@ðf’¹pm­=Ä3À'—
+Î2`M=Ù+ê­í]CWˆmQƒãs]ÛfcuÈÎþåPupªæL¡ÅTÑÌÔq3_N<ÂÇÔ|AÁÝ
+F%OveÛ­1€ð¸
+¡Ú7"^ÊÁ¿ÈÉyòõ‘a4ïÊ*&¼3Q9é(¢ê©’?óeQù½Ë[ØZù•-š'ŸÀs§³ªkÒéÖ´ÏPl@´ûÀÐgj¬µ-ÛuqWË-‹2¸  ÅYÄ8¿µ<2AF
+/?'##dÄËA÷ò3ÌA^üÕ…jmù#œ¬ÐF%«MÙò¯ü·)N1ÃÄÿo„dž%$ž:m#1•ÛŽ?3U ]IHâæ.Kÿ;Ô³”ëž7çý¨‹Å ôL/¬¸þ P¹»¸ÚågÏkÄL uQN!Cx{Š—‰–7tÐÉøõ (.ÇR$Æóú€½'¾+GF;7Aö‹hn$¸«ü5N#‘O¾ðÔ8àSB—tÙ‰ñC×hN–Wù 隌3=¥•'Û¯°\Í*¼Œ7`Cp˜é+¼wÖžò²4»ÏÉ”‘ìÄŒè7&$O\#%’G,Xž¸Ðyš¼Î†+!8æßJ8í'Ž<¸&ö»JuñHu~Âaà}¦H
+ ‰O¹®æœõ…¶äÚœ„hè`>?Ñò.U }²dÉä‚;Ž)–|?}·šük
+endstream endobj 1545 0 obj << /Type /Page /Parent 1664 0 R /Resources 1547 0 R /Contents 1548 0 R /Annots [ 1546 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 1546 0 obj << /Dest [ 1549 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 152 343 180 357 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1547 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 1548 0 obj << /Length 1851 /Filter /FlateDecode >> stream
+H‰ìWÛŽÛF}×W4ü’&`ÑlŠ¤È`0@’ ‚YÄÈ"#¿¬ghª9ÓkŠTxEùüCþqrªºIjäñ%ØE 1Vw³ºîuªúËÍâÅf
+%6åB…"À?üDièGQˆuûI¬Äf·xñU—Š¢cš@tE½xñÍwÝ"ðƒ@¦X,Çåa!ÿ™ßi¡ÒHx›ÿ,2¾”‰uàg ¯R? ,_¾òuZÅ Ý~- qº o©V~"ƒT¼9Š›cÝßã$”Ú'Ù™N|ñÍsñ¯ÿzËûßb?–Æ dqÿ\ÜLÿ“n+f²’y½õ–™?óÿÞ¿7ÿX°ÅlMø 6›+œ±‡ÃÁ÷V¾’IÕ$¬!ÒÇ2ðCY¸£{æôõf¡„‹0NüU¢Äjí§©H<)`c”ŠV/ÊÅ—›É+’à±7fïäÉÞCŒ"£È"Dþj•®…ÝŽN]DZ¿O¼¨)(´$£TâG^ê§R\lu™6Jöù
+SÎÞ]ˉ­KÏÄ_ñ¡„aœhEetÝ_Ž®rj,ùÂRù”WÎ é(˜V,9¯Ea¥[Áñ[ÑÄ#²<¬úF eÓŠÒ´%ä.zy…dƒ³bQ5w¦¹·ŒÈã½Þíûs×Í:‡“<¥&S_Ë+ŽYoUM?â#•ÑÍ×òYqßz1txæ¿_àÊeþIÎ9¦Á¬Ð»åIð6Yzï)T˜éx#Æ_ÒS¶ºÒyÝ‹¦®ŽÂ”H=O)iM¡Ì»œèíjêª'Ÿ>Ûmcè}*D×õèh÷ö¨0¥Aúسޣ뭩ïÄÁT•x£Ëy+òQ5[þ“ícKÔ|Š
+ ¶­QB´—¼¼òÂ9’6†¾¸öbÔ Ñ£ ì•]^M"Ÿó}Ë|´3¯Þt9YšÊãSæÃa€¼ÙRwl<P Ân§¡±.9±iŽW8,´+îóªÒõ—‘v@E«WFú÷âm ã¸HàJÖÄ=ß"‚ö¸Ð6‡#¶:@½Ò‹dc×dòÈÒþhË«ï)(ã§Á}£›CeÓ£t,ܽ½Q8´×öŒÂ»–Äè<†§ÊØЙ~È{ÓÔÿ[4ÎBlŸ¬7€£1"#.j}èt×AéžPÒ}{t¾ä¢ÖLxŽêúJŒ„‚(î>¡qû¡ƒÐß 8Ãÿp²Æñœ‡±åñ¦i* >y5Pn¡­ þD´vðœ!ûvжúHx’isZ¸x\—œùÂ@E£r9A.ëþùë[¹…ôÞ[¢ëg¨ßRW Ô\c½%ŸdÒTC«o½‘ 8EðÒ¶©5° Ý8#jGŒ€ìö•î5êÅJœõAÜ0Õ‘Hôô—ßR"R{ŠœzÈIRÊ”%J~HäòHÞÓù=cV‹k×èn= "»}`…¬¨%Ønà:£¶*3Æ×ÅH®"tæÁ2†?Üéa–™ë¦N›ÚôÆ]¨¦nëQ0–çp̵`Ûñ­ìaz
+^†æÄ„úÒˆ—Éy³B×€9ô‰‹ýÐoõƒ©y[æ}W5¼<tǺ
+endstream endobj 1549 0 obj << /Type /Page /Parent 1664 0 R /Resources 1550 0 R /Contents 1551 0 R /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 1550 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 1607 0 R /F9 1616 0 R /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 1551 0 obj << /Length 2209 /Filter /FlateDecode >> stream
+H‰ÔWÝnÛÈFoõß„Lš¿"eÖ›dÑÂÛ]Ä
+6h¶9’XP-¬h¤ï°ïØ‹~çÌP”l9Î6E‹…‹3œ9ÿç;oæ“«ù<¾˜/'~ <üá'J7Š¼©Hf±;M½PÌ7“«oÛTä-ŸñD›×“«ïî|±j'žëy>ÎägxÜM¬³•~ {þÉŒ/ÍDâ¹³)‡©;õ´\¾ðuzŠ§tû£õ[€]'ðlÇÝ©å¥b±wûº[c'°¤ IV[¶â›ï.Åßþe;1Ö ÞÅnl•¶gåëKq·+»_eS±ÐÊêÂvf–ø'ÿ·ÿ>ÿË„=foÏb1=¶`·Û¹vèúVKZ%©…h¨tñ蹕›­5Kz3Ÿø¢“ žºáÔa⦩˜úˆ¤€Q*9YNnæ‡`„>Žx§Ñ£ëQD,Žr™E®á@ä†aš½‚šÄ±›GQÅž‰*N±OþÔMí¡/+•gU±è[Ù¼²}Ϻ´
+©ÀëØÞnùpÖ¶;Õ¯®Å­Z•v‚|Ô¢S¢âG:i‚éFa>{~r¥Ud]¶ÈZ98“>,¸ƒÞÌ Ò#üÁ_Ûÿ­ª»¬¬e!ÊúšÓ˜DŽu<u“!‘TY,Íx™W¥¬»WÌpøŒ†!¸¤#—"—ÍY-r­]+Ž?£Xˈ2bÖO¡¾[Í'­|ÿ`üGëµ\f}Õiåé3^{ WO­ê‡Aàíi•YÞh›Œ½hcæ6µÛÚö©ÙZ^ #K;²ï5›¬+U-†w½þEÞ¨lPD¨Ÿµ:!Ø°}º…Bªln­½Ø¡¶jd¬º5Ÿ6e¤%ÙN€ÊtÅYKÌo-e• ɲòl°‚¥™ö#qH»sœw@Nþýí`iIèR>³¸ j©÷:›ì!÷2±AžJ‡µÅ–lœÑp,/Å¢ïÄ®¬*aNôp{/ôóžD'V¯U—ö ™þU
+Uã
+r¯ñ
+ G¯Å¼×M—gü‚PÍòúÆ4æ
+S#
+#dPHƒõlJQK—×Ìï»È“ÙøF÷|0~ Žžo*Yçª(yÚ×+ÐU3¼eͧè]½Ò—ªÙN-ä÷Y¸ SŸhúž”>wŸ6Õ…@jq÷b·ÀÊfAÏÀÏW2Ô€oí_MQÏ·9$"†¾–øXbÃFͳæ]•Ì;!KSø!·€ØVñUù©?[DÞ4ü ,veFÞ†§™ *íg[|øžÅÜÚô ‹V¤Å&iù›` U¨Žômú|}žžŽÝå"¾Qr¹,sª"Í- °×ý&«FfE¶@£/4ƈÁÏ÷â§Û§nþ€¡nÈ:W¼U Ú/²í¶*sMáI"°°õ¸ã’_ÈE¿B“¯h€
+µ@sšº.ôÔ‘º[—hq½ØêA—ZÃ^}úÕ1ÀÔžéÎB¢ªV”š¡&–åªo¸‹ukë2kñ—_Âp´ëÑg.ÓÐ Y£ |Ï_
+ïèc ,ä<G‹gF B–2ÈPÆ ¡×`0Ò¼ÚÚTQ…’{Hlµ,+ùÄÍKj`tAÝ謠®¸ë@[5 i5]¶:þÀ@ÿYÖ–Œ•–˜ ¹Ó¢[…Ã)š"Vœ]Bq}æž9˜Ô Ä ù(p!ˆºSßRv`sm2PÕ•æ«{FÍ…ahÑÀãb¾edgFÐ[¦|b¦€‰•?k™û0×½øè3ö
+]{}uuòo;Ln¡–z—7¿Ì÷$>Ì©¯òøHÎÌ㇛7àqÊzÙ “ q•“¢àVálÇÖ˜Ø µŸ®Ë¦È¸V/8D
+b`oæ“
+endstream endobj 1552 0 obj << /Type /Page /Parent 1664 0 R /Resources 1553 0 R /Contents 1554 0 R /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 1553 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 1607 0 R /F9 1616 0 R /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 1554 0 obj << /Length 1406 /Filter /FlateDecode >> stream
+H‰ìWÛnÛFE_ùƒ<í&½»¼ˆ2 ±ã¦—6jšô¦¨ˆ­D*$eIýþCÿ±™åÅ–¤i“ ÖîpwvæÌœÙÙÓ‰s8™Ð0™9Ú€Â?ü bãŠ`4½(V>L–ÎáYCZóuZ8‡Ï¯4¼©å)¥qMê¸ÝpãˆËäM:Ž@N~uƼi #å#TìÇ^¤¬^Þcx;ˆv¿”ºFIWû^$T ×;¸ÚÍ%Fd5‰:¯áéóøù/é†8¯ð[è…"—J¤ó¸ÚäÍïYµ`%¾HŠ©tÇþàÿò—Éw{ÌÞåE8™<C[°Ùl<é{ZÔtjFÇ¢j<ÒáòŒH[Ñœ5O 98&Œ<?Òà¼8†H#’€>1T™3sN'=¾Æ%ê.ºŠŒÞá×]ˆO6J<êà…#œZ â+»el·àÉ‘­c­¶"Ç÷Q4ö.¬4$X.N%A|þÒ-o²ÊðìLº>â Y‘\/²)¤‹<+šš?‚ýYcÈB±Z•Uˆg+]Igiž,àIym¶Û­c´¶Oà'ü(Ý
+Híéöàð=»]Pn‡GP, CþVøšº7þ•x–Í’õ¢±‡ÇðÚ¦†(Ê"{¿·¼X dœÅÓ›$_Ëæ=Fß=H›¡(khZ.—Xaq‚ë„4ÆbêÌ1ôiÀqZ#†4B»¦wéaHÔÉ=“Ôž?áàOK *!«(!Ç”F†äŬÄj‰¥$K’&/ è>®í/&Z#]Ê~Æ|a ‚,¶©ÄÞà0·õ'H—9Ò¥½U‚•K|<ßÃ
+endstream endobj 1555 0 obj << /Type /Page /Parent 1664 0 R /Resources 1559 0 R /Contents 1560 0 R /Annots [ 1556 0 R 1557 0 R 1558 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 1556 0 obj << /Dest [ 986 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 378 644 406 658 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1557 0 obj << /Dest [ 644 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 71 386 88 400 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1558 0 obj << /Dest [ 1561 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 338 130 373 144 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1559 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 1607 0 R /F9 1616 0 R /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 1560 0 obj << /Length 2718 /Filter /FlateDecode >> stream
+H‰ÜWÍŽÛÈF®zŠÆ\Ò,š¤ø'c2íÝ56°ƒ V.ñäÀ![#n(R©Qf$ïwÌ!_Uu‹’,{&‰/Y 0"›ÕÕU_}õÓo“—‹E¤BµXNÂHøÃOœG~©Ê扟æÁL-Ö“—oû\•=˪/ÛÉËwCußO?BÈ”“©{ÜOôŠ{£Â<SÞâ§Éœ7ÍUøóŠg¹Ÿ¢—÷D¼ž’”vÒÿŒ°:oÎüT¹º{TÛa…•Hšt_÷êõ»êÏÿò¦ Þ·ø–ø‰®½@—«êã¾~6Û†•ÌtÑVÞt®Õ?ø¿÷—Åï&ì1{~Š—ÅwXc öû½ïÍüP÷tª¡c¡Gúx üH—viÅš¾_LBU«I”¤þ, Õ,óó\¥!Tð1ÎÕÖL–“7‹³"Á)#º!¢=Ä(¶1Šý †@ìÏf€V^¨Y’øYt„*@³¨Õ0õÃÔËᔺ†[åÖümgúáæ•Âb¢ÿ(¯jè¾²\©
+UCÑ¿o
+3V­wVŽÁW'ÂK/v'ëmi¬ç8uþèÀÉÊ
+8 zÛq3A9yeäMí6 š€ÑµÓ#÷£ÓÄ™ºGp`Yßï¶Å€* –uc|Ť8C-˜Ûæ‚Ú$Q½ü×dkªõ™ßÈ°XߨÁ é£5×÷òQÈØ¢Aùð2ß”‚ÓTÈE—¬´åjÛµõÏ„´= XiÌõñi`ŽÕ
+«ªö`u¨Û{l–{œ¼œ‚K˜…ñH™Xœ¿Õ«nowªˆ¦€')º¸tÅšëND 'ìºxDð›GuGÆУä¶Ä~HUø\ŠìÆ*·‚FåEþ-àu{ëù‡”NçM´ùã†wÔ¯lÓõCT|꺒~ðJmx¡ðnÒ€‘½[ÃëëéЛ탼n Ï\ÿº?jÆÙ³šqÅ~Ûfœy²刬À“3–– 鯴±oÖ¨EGrБˆŽ­QܪêNòªG{ä–ˆ’vÿ¬¹û5;™ŠµYo†Ç¯7Ôÿn=§ÛE5>DÄfùb…‰}@¥!f Ímlsã
+»±ëˆÑ°rÏè!V±k×®göCU¡6Ñ>²YD›…$‘[ö—ƾPé°úC§¿_u»¦¢"r±\}® õè:ÐJÚ—ÖJáÖ±b0("2)­‰nÔQcbÀ]wRu…‘–;öã¦Ã59ûoZhm#ʘ¶l:ÙWƒì×Öì“NÆñ]½Æwã«?Éû­åñ4Ö–!îWzÂÜöÓõƶ§aK‡6õ_á
+äšsz^–¤CGè©ðõº·W4ôèBÂGÀ»á©§ÑŽE[Â.VÔºiÇÓÝx~ü²»1{“%Óê¸öݾà{ï#¥ªR} Ú̶4Š­åÂìBA 4áo%{I.Y{°¿¨áÓýëìéQ"·Þƒ;9Üyê‚ø ëîϸö¹A
+5 ,Õˆ6Š©L {L”×õ}‹2pÁ¸ÊW»µ굑 Hþ¦‘c+uµÛÈ´SñÀV —ÔvmCT~.7 M?ú¦Ü8QIilnIB{™93iI¡ðÎ-)víŒéÒŠ€ä79*¹æÍ]
+ãŠ".íz*mV³ÈXý_{ˆ$Û\’m”<K7B?ãV0× Ís>ðâ¥gìW¡Íˆ ÕÇÓaŽd2ó✹e„ì«È bTĆ tQûÁRÖ&G>ë[O_á~Ûâ+.l)´w›çhÊ4òü$i¼kKCåjg¯QM·w÷©ò†½¾µ}
+íKFÊÙ­wZt%$rÞ®-á
+endstream endobj 1561 0 obj << /Type /Page /Parent 1664 0 R /Resources 1566 0 R /Contents 1567 0 R /Annots [ 1562 0 R 1563 0 R 1564 0 R 1565 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 1562 0 obj << /Dest [ 1555 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 452 617 487 631 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1563 0 obj << /Dest [ 644 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 473 413 489 427 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1564 0 obj << /Dest [ 644 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 445 222 461 236 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1565 0 obj << /Dest [ 628 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 92 208 100 222 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1566 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 1567 0 obj << /Length 1931 /Filter /FlateDecode >> stream
+H‰äWÍ’Ûƾó)ú–™ª%€
+êíd3y½:;c–@$þÜïÆì%ÞCŒ²£,Š3dÑlV,È/G§.ò<Z¤W^M’Ñ«ñÒÛ”Ì#üŸi\¬èåÐtGW¾ú†¾ïúÒ’!Þ"Þó"b®/žRä|gºŒâÀ¹Òßó¦k©[[QÝ~#îZ\”¾Í£ÅèpfÀB°úã/\Ÿ)Àèõž¶ÿ<X¿åt†õ«§À¦¢lšDL¾·ÞzPf´¾¨LK¥GæAå_•äVç^Ǻë =¦9è$ap_“žÁ„PxoíÆç1_Áà͘^Ûaô4Ûš-pÝ.¬]]âi¦šF/¡öD•u¶ßIþ·¿Dyr¹G¢¨¾}4ucÖå ŽD¼í§ôâ§Ôc,»ÝùF ®§µ4ç§e 38 ´©mË.„ :A¼ÌW¼Ÿý¨ž@ŠŸØs‰]b·Ú¢^”ÞÚÒa€W\GöÓ¾©ÁvU»æ„e¦¹ëDˆèžý™«°¦Â?Ûˆ5 UZˆÄžœ¡®ÅKKbžKÙ¡Ú\ì¸í6>ù™¯úGÛ“é-€–[üõ`«H4ÿЪ;vÓ£9…sæÚÄ`eyòjÚòŽœNØÍ[Í*,lõ)¥£c sàÆphë.\FÝF ‰1l¶®Œú2~Vƒ+*ãLD¹k­@þ øŽ¹Úh&pßí(Èv(ÑØï5Úð¿ž(´ÄâN°ÂdÕ!(¶ÆfOC]Y註þfn„Âýeê}›j¿¶8}¢ÛǾvζð™Û
+6Îãä;¨ó0äl­§L’Ê‚MÖ¢òÊ.,Øu?öÀúòœâÎÍ瀯ªÚòiMßu=Ðfð
+òç¸dðšC\ç
+aŒž›þ<‰~MƒÉst³âªÁÄç¶=†Ú[é‚Ãö²éJÓìCéž«­ý䬼hÑž
+Oov±ç€æª9Pc€ßI¥ÆwIùã{áZýY÷ïÆpî'«ßù‡úh‚Àƒ’R }K§
+ïNÔZ?×®9˳Q_8´‹¥ð̤8B0A÷Þø»¸Ìyò¹çsžTÅ—Õš›Í«HÊãÓñâÿš,f)2?cŠî.I(1»ž,rÍã½ìmÙõ•,6<î Ì62]°ïˆÂp%Ï\4lÌSÞ{O(ƒÄŠü±
+d‘JÇŒùú¬‘.A¸ø÷?k\±Ükøœðîf®äܳöþI:ï® &¨ú­çŽX¦Z|NüÇŽKmO²qìàÂËcÇ‚Ç^ÈØqþæ•-_ üó^dý(Â_$Œ‡ ÝsmÈÆ ¤ Þ/Õ{‘¢;ö“æ¶Z6AqýhÉëÙ±<³cÉì`¡ ªY;øqîád'€ÂY/;bpæÓ†‹ôÌòÌBZóƒmm#|I·5kiܾ5|Э0Ù¾Ñd0<«¥‹-üìš` ÷ó߬–¿>ñ­: Ò¤3ÅåÆ4ñ,‡‚K³ý˜+¾þ/<r™üFÄ=`üöùn5ùe
+endstream endobj 1568 0 obj << /Type /Page /Parent 1664 0 R /Resources 1571 0 R /Contents 1572 0 R /Annots [ 1569 0 R 1570 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 1569 0 obj << /Dest [ 644 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 443 638 460 652 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1570 0 obj << /Dest [ 628 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 92 625 100 639 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1571 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 1572 0 obj << /Length 882 /Filter /FlateDecode >> stream
+H‰„TÑnÛ6}×WÜG¨’’(ª(
+´ÍV`è€ÖÓÚ>È2kP¤L’ãf²Ø?îa‡¤ì$n‡ MÒ—‡çž{î}_%WU¥IQµK”&‰?|eV‹,“†Š2ÆÊ”ªÛäêÃd©™BŒ¤©é“«kE7S"…”
+1M²:- û­¾q¤lI¼ú#)Ã¥’
+)JàÔ
+##n¸£Ãu¿Ê¿ý™ý£qºÒ’¯T* “–6´~èç=N4sHlj'z÷ñýþ/_åØø-9k¹dÍþ­íü—»
+9)#ðo¸…ôftÍ0nÃfÇWdn»Ù…½W)¶}¶PÐë}˜Ú{÷ö5mÝ®åâ{GË¢é¾DͯŸqE–¦š€”IUi=—<r¹^ž Ooh6n) æ $ƈ€Ìí¥;ÏéÊRhû$]uÊVÅ> ý\·½ÛRÛ¿5+Øv1½Å©êÞ†E€ˆ×ßL “1ä3r~îσ‹G3Ï°{IqÀ-H0–ÀžK`VuOMdIå/R~†+óPªDpÝ}»‹+ðƒXüôM0ôÿO2=“TêIÚ×nWº9r³/p“A0Ö0ÂâÔ‹‚3‰©°w÷uÛÕ›Îù
+
+endstream endobj 1573 0 obj << /Type /Page /Parent 1664 0 R /Resources 1574 0 R /Contents 1575 0 R /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 1574 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 1575 0 obj << /Length 4437 /Filter /FlateDecode >> stream
+H‰œWÛnÛH}×Wð±¹Ñì{óQ±5vf&šE¤,,-Ó1gd)äu’ÙØ܇­K7EݲÀ"@LRdu]N:õf6¸šÍT&³Ùã@ª¬„ðÇUSºÌW¶p¡ÔÙìypu½ ÙbKï”Ùv±\ÝNeöy;(‹²”ðÎb0L—¯ñ×ús“ɪÌòÙƒŠ>ª2_•Ã:®d»ô¢ÏñÊ:üúïâß
+žU™¥.œ(Cvÿ-›~[ížà‰M–ĶÝf£ÛŸ²Oÿɇî7ð›-¬hóR,ž~ʦ¯íî{³Y’-êÕC>¬Dö/ú?ÿÇì—ELѨ²pp3»gäÁëëk‘ëBŠ-žÚà±`Ž,à²,”XÄGOdi<ȬÍʺB;™i_„9 ™Ì F²M3x¼™uÉÐ^)³±Ïn‰”=¨‘‰52Ei¸Lt•òé.¼ê%~‹ -‡‡åpžÈ~k·»lý˜ ²<@÷/ír7¤ßÛ x±Êv-Ý?7ßá/Ö«fË9;™ÎåŠ2¤XÐC
+#ðwÇpðP5é~ˆ‡é—fÑÖPIô%[ÕÏÉ›2Ê<àÂå—´Öø£”v?NgãwùÐ{Hì bþ¿x3š6¾5û ýª¨J?ÿöûhövrK§ž9˲‰ÓñûÙ[<Ë
+82ˆñ§ß¡ pò$jÈõ8¸ˆ.Ë¡ïü>B`ãU—ªXé $‡Õë5mêTuh„ìà ½ðâ~Ç|öŽÿ¯\ª.:%ñUWXe ;0züÌæŸ  mˆ«¡çÀ¡8&…¦°çNí²e ïdˆö~ͱ<ÓÙU¼ºAŒ1˲ZT9RÄùê)v·*»›|¼Â,X1}Ì%^€Ffæ ¸?†þ(½Žž,sˆú¸Ž6âÏ"òhÛ¨Ê ÆvW¯'«¢²eJԦΑ·î‰Q¤ÈôI¡ÒËã×±¶-$·ø$PZÉ€é¨
+-OÏÙ¸ OÜÑ™½:(r×Ê”U4ƒ¸™å@%’CVÂ@Èö|7A§Ü ÷VÜF¹¤tc-Ñ?È›ªb”‹!Ý{±cô.ëÕ®]âÑ-sâL¯ dHV>LçÈ'
+ÛÀ8T$2ßnS/³*á³ë3¿÷Ò~/Åš«Ù0¶ÁY^„€åœzí“ß
+}œrÉ©
+Fuö×¹!ûÌ7p­ºDA{
+¥ðMalí^£5I2'0"çH ’¡¯Ä $WlñyKÌjÅb-æ4ôL±bò=SŠMiñŒ]D³id
+“ê.öbäÓ¬íÌcÊ w:ãHhÒŠúˆ=$¸ ÑW}ðªê¹ìÈeÝsÙ‰7›ú{»l©×f+R`nßô¿:¶YYéTò1Ö@%žWX•žM¡<H™$`]Š\:Ž¦Rì‰w±ù?]á…îî©Ó¦ŸrI£U^:Aɸ~É-m h¬
+€=†nÅK¿lÖ_hºYŽ®Â‘êÿ0HªÄ**Ú‹N;vÚJåzgh:Ãõ*¥Aþ/ÚǨBZæ6ÖËÀRVKqµ´*“çS,”ãBYqÍ3§Ân¯€Hl7¨OÅCˆø4Ò¦™s}…¡W0upèðYbFhö80§/I?V¡ 8÷ѱ&Ǿ^Ñ4ÝåÈk›šFR¦8%5KQvœêtgÌQÒ1ƒ;Ê` ë°=yHñ+_Iq´ðtA÷kì~Lñªý¯V,9lH„uýD_µ4¼Wt]g!§è qØLâ7õ®Y¶«´Áûê"YÄ6T!qÊCöyìÑΩúaÖ­’ 4i¨#ß:4«ß̺±›&6ûߥ™’Nç³%’¨%%‡¨Øs©ùÑX-MšŒãñìŠk?¯"3eê|(:I"«ýÞ'síøƒÄí wÖ†[ 6}Ït‚%¶[YEY2¦AKãNÉD(Å|
+<È
+/Ä­»'º™žqŠKã["Ðh½õ$Eˆ+ l^èü2Aò@ÕNžL95°ØÜ` TìF 2S%µy7ÇßÆp>¶°Íe§U. EX)Õ>»,ÌxL\8œ•*ô‚‹¤1þÌÙÿöe×Ë÷Áºã+[jü'´Ä¦M«Íý «ÓM\‚²s‹No˜b\š¦?·´iw“@”¾*«@ ‚õsŒ^òR¸ßY¡Ãmš· IlÔ‚
+ªŒ
+Ð&v{f÷íbÙJWu&ªT^dº8Mü8~ë}iêíùפ†š··&G"_¢ÒËÍ=6rJ–Îg¾—}
+&Züe½y`ÁTSkwÛÓ%êŒ>¦b<¿®c( æÄF4\u)*ç9Uˆ$\C´ö•w Šx­;Þ£PoÇžAmz¶Û
+d«"[Øu´³Í1G>š4ÂóÝ!¹D„EÀu"'I(‡eÇΩ™GÌ3$ì¼XÖ«]» dA‡ç†2å ÊØjï7»ú𰤿ÅÏC-KÆ9)%)ì½LõÜ?a(Ÿ¶„~J¬W»æŸí‡Ô¬©c;õ7ø¨ìþ{,Í‹¢ödÏ ¯X¨a°øKü­ÁÆbÒdy…¹3Ðàïs©Y
+J¨ˆîTÛñÉí»€Ö$_VôgWs…qpjD²å:c&=<“Gk_:m*”*ÄBš$:R4Þìêv5g¡_j¾¶ Î"jcžV¥‘?ù–—ÐúÏ5´ŽDõŸ¹câ`º©á’j›0ÝŒQ"Þ´xŠ:îh½äO–LÔX*n‚îh<‘ï §]’ï“šˆå¹½Ï-õ
+”úI0ðBaÊX§Ió¥^RSà ÝuD’†)¿2iõ˜pñ¦ûWœ‹ÒLŠOM møU”³Êw?Ø Þ¤0`Lpv8^cH)LTAø±t}xeD¯*u/Âßxq £5D˜&[ä轥님‹A—>QÀQÐkŸq2ƒiŽ™.³áÑÐè•ÉªK=2Yç°Ä$m‹(mÉU€F=SØ|ÿì¨TÀ.á@äJ•¤eÒ\“5±Z7LW(¡Á>Ê’¶ÎΑA! ítÒË´©¶#‹9“Ótijá)¯–Þ¶ |ï¯Ð‘<Èྸ»GEaê ±ªZJ”ä®R¨n¬ R
+ôßwfvfE‰\½ùÁ}Í|ó=lÁN°Æ[/H]²ÛÕÕÂÈ4\"­aJŒÞgZ’·ÀF.¡‰˜HŒØ3cýŠQô+ß©ðAp.g$”[Ôsýv4çó»D2 æ˜$+¡€øì8µ”õ>yͯb2-E
+lîŽI@¼?²ïqŸÐù#…ãqÏÊgÏ*¶Wtô|‰Õìsš•®6ä[Z‚š«R* ¬×ŸŸYP@–¤™æ–H«XË¢c_Î p½é]ÓƦF!߉ã8·`J#pèÊ8ܵa0L˜†MÑjûõ ) UéAŠ¡ä|uŸzg‰ÂT±=E…m’ü¡À¤;pïp-¼×þéÏ´ÙöÛáûŽJrê‘Ûp㼶Ì«Ã* ¸œ/ß÷É"Y_è!¹À÷=½&Vo·OTSxäl)¹ëíþÛŽæá™ÛjN7^é¼ìø•‹þT£GÚK J_³6­c”¯ÿ&-WÙÁæ.s…ÁEù:W8éúD•ó«QTš{ýnÅÕéHµ·G›"q*
+Zž]Æû¿·¤aÚÿs8ž¢Å2j ¸1j6»ã ¯”\‡BÊpd\q¶O`Jr_.õņdy¼
+L¬›š˜pB}$|ªaÖçøûÜ­&c êHÞñóì éþyOy¨1—©š¿Töû.jnì¦#w ;m:²»±Z?‡"a–§Œ?×!éW¢tä¿)òD4”›\uø¶£Ǽõexˆ;Ó^niyKƒ5Ff=îsîK“Ï4>^u
+Þ¹äôö°çË|IÙãfŠ
+aק•Ñë¼¹m¢¬zîÒrj° £s.çAêÞ¶?=Uäeþ^©äTß;Ušª¸¤ª‰©ê/'¶×Ÿ¡ÞPù5U³ê¯Fz©—ÝD.{÷2Mö:§™-åÅAðž8*¦tÜè4 ëÖìÝ‹ÖFq9MJ>0u¥ ?ÞHGT¯&í(¿ *÷+º»¿1¢qQÊ°Õ-Êõ±t*×ÀYžâÛÙŒæ7…\5Þn~ׄ¼l^Ç„nœ}×›}ixd¶±ÂM·Â}¹¢¨³M>Ù¼°C[M¦Ú o8½`Z/V]’š®|ÏŒ‰Íå·9YxOÏ2€“‡v@#âæ}eØnáÞÌèÌÐÁJŽ»­é^¾Rµ®njGì ÿW=>R=Âx=èvQ<«Ê65ÚC¯Z•Ù§iòÇó ÄÎÈ ^`ŸÐþ8¸Ù–JïY, ïé÷‰]¸UN¼H}››¦šžÕžâ-ëRÚJ×è¢o$Ž~¾¢ŒÁ?òãཫ¶<ŽN _O"T³0]I;Ðúi×+Õ“løRœˆæ Iþ(Í(þ6XKw©Þ’›ãRù;ÃäÇ+]x©•hä$ÍÝ¡¥B)€BÁ^
+&Ù^óŸRbx%ñ,˜þ"|nnpô±2Ž~.Ž˜°hT‰û¯ÐÞ äðÌ5àÑtän,V ê ®oÛà‘züHÜÄY~¤–/þÖÔ6ñÅ93"BpçtUjs©SA:Àm‰ñ%dÖiÞ«%¾%Œ½{Ø—ß¹/v¤/ž?Ž60v¤VÍ’¨pkàŠºH%Jd§óimùç垃̻‘OûôÅáÉy·žc˜ä6—Ü.d¢aP¼§â´Àaö™¸›YF›äI«MR·‘랶- Ȧ[ÕSKŽpª\?³ÂÐêóWüï¦t ‘¦¨4ß~cò¼Ê½Ëœé4ÑJõ¾P…¢ t¬‹ÔÖ
+^îÙÒÔŸO“?ìÖ¿ü7
+endstream endobj 1576 0 obj << /Type /Page /Parent 1665 0 R /Resources 1580 0 R /Contents 1581 0 R /Annots [ 1577 0 R 1578 0 R 1579 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 1577 0 obj << /A << /URI (http://www.openmobilealliance.org/release_program/ds_v12.html) /S /URI >> /Type /Annot /Subtype /Link /Rect [ 378 653 758 667 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1578 0 obj << /A << /URI (http://www.openmobilealliance.org/release_program/ds_v12.html) /S /URI >> /Type /Annot /Subtype /Link /Rect [ 71 640 388 654 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1579 0 obj << /Dest [ 658 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 339 598 355 612 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1580 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 1581 0 obj << /Length 1980 /Filter /FlateDecode >> stream
+H‰œWI“«F¾ëW䱈x¢ÙAsó[ì°ý<>´<—ׂR«l(dõôüúɬd‘Ô‡”  ×ïû²>®ëu
+ìD@»“ ëºB/"¼KÆÑn»K[ÈR UfEv$²®†'¡«ºL‹âªQ›B‚Òæý¢²\_~n ª¡â›Y¥›ªO–=Fq–V ÞOâ³ÜF‰o}@œAoH¡;fÐÅüÄ_uöËWxlÓ¶kà“ñüjÆ¢0²“`ʘ»3FK“1óÎò°N²1$'Ƭ!­­ÕË-f1Ê -sØX.½õŠ5¥”ã¥yá«Ù ü•ÖÂ@Eªóþk¹åâ7lø7Ø ô~‚fÚTüÚ‡þu)9Té;/´Cü³þŒ÷(c‹
+CZKM1Ó®(
+•êLÚØ%Ô±Ï|í÷œ¾Ð Ï¡ìyOõÌWü(r~údz´kË„ðe½pAÁÂTÈ…(ðlßûÂK
+xV_l÷„ÛÝlòÜÁI/â*»Žk%ÔgèˆÆŽ[byLV²>ÈW ÃâÅ+—¡/vÃWE æ C3¥j±li¶“9µ¬Ù&!Á4„¦}ðž¬Ñ¡ÞÿeÄ:ö)<Äê9Îî¹ÿˉû&ê¿þŒ¦»,Cÿ¶]ØàujXð4iƒQoõŽÕoh–èÆ¿’¼óv?¶²„4Ï‘î0‡Öâ¹ ÍÇ'¶rz¶úTé-r âPe˜sCŒ‘)O„\‚ÆDq@O^T»ƒRb<àÏÄTå¹JÌy™\ô2š¼Œ&/ÙAI^!1Î4^-!+”Äþ'¸¼(ÍîõhuÑ£xò(~×#â\—ýÙ¼BÞí Âl,2Je"°qô3(*êU¿¼ø¸}\ÇBV .÷«Ü(xôâgIdÂ5«:¢
+#ÙN‘Úzâ@(rç+çϸCT_éfÊ–?u3bÞ{u­§ÏŒz7äàú®C:Ô­Ê·¬Fè}K:;*P„
+õr»R?FÆ9£¿õd™iè.±®ˆÅ8UöhûˆCÓJµ¤U(þîúEÓÞEEßÐöÅ8Áùo:Å.¯Ýê’ŠºIsÈøŽÌ :8ˆb;§%dÅ ‰¯1dŸ‡7=¸G=úö¼@=Š²ø¾ª7
+õK£?Ò~¶jBƒ+Ö}ßB[ÁK­hšªø úƒ‚Ï{r‹¨iif ¼ÚR‘èwløÈÐÿkä;³€ñt–ðÔÙ9§]â×ÍŸxè1¤µ­:}« ã‡Ã‹JN %)Kä)<U/8ëJjàü> £yörH{¢ADêV"Ôqt$‹M·§ùq–“ý7ñ]ž0Þ*å ×f<–¬‹wêä?u/ŸtÅ-Œò©yùÄÕ¡Q[¬Êü 8—£ÙɃQÕlÖŠý) ˆqÜènF1Ú¸<KœwÀ8J|8š1·xd»W'ƒ›f…wtå‡Jã±£–ÈY uBÕ÷>lÕZÈÓ[&Õ¯PVõ ß™k7Iø›Ôü¨‡c{&w¦$¼f— þ›ž€PÊ\¥Ð¾î¥)ñT…’W¦³p6*‘Õä–¡mŠÖí© '´¨-ž4DjøM ÿ‹ªѱ“/wW`u‹O¡»îÉa5"F+‹mbƦàè€Àwû-¼ÿÀ’ä<
+Š*#†Ûu
+endstream endobj 1582 0 obj << /Type /Page /Parent 1665 0 R /Resources 1584 0 R /Contents 1585 0 R /Annots [ 1583 0 R ] /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 1583 0 obj << /Dest [ 1576 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 447 670 469 684 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /I >> endobj 1584 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 2027 0 R /TT4 2032 0 R /TT8 1605 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 1585 0 obj << /Length 2311 /Filter /FlateDecode >> stream
+H‰œWÉrÛÊÝó+zÙH‰æaÛÊ‹;ϲ*¯beM±c Ñ̇äòYäÜn $%’ Êeht÷=w>÷Ãrv¿\:ÌfËõÌv˜…øñ"Çô<+`aì›Ad¹l™Ïî?ÖKkµÇbuZÌîYØ칞Y¦eÙØ“ÎæýãnÆ¿%ςٱÌå?g±:³Ð2ã
+‹«!ÒÄ£e:<í–6ꦇåÌf’Í?0ÝÀfnhF lX’AG/b•˜­g–ƒ1\[¬ckŒÖµÈ"\Y>ŠN}Ô[2 ,ÿõa²dHç¾sßö Â;=G7¦ß©I¾êvLŠÔˆ “È2±ŽxTÏ´<òL×B¦_
+<åQ…‹“îöú H+¤­`Ïzg!ª¤A
+DH¶¿c0*e.+ÊÆ@¾Ù}—¿ÓötF{vN­ ªên— ++˜"äLÿˆTȈx‘ [hÇí»­©2ìWµ æãæ¹€pÊ„CaŽƒü® æõ±gu~ÿ¬€µÅ¢Ü¬©’¢Þ,¤Ä¼aÛªlÊ´ÌÎFe<Ä”{•„Ç!½ìËhÅ£/c}ô(wžµˆ7ÈÏ-™ÌãåS&r†ZºQñe=e%Vrɲ¢HTzóýJŒ/ïÀîNÁ>&QoLT0+·¢`i™çm!Sƒ ©‚Ïã å@ŒÎ€S/‹w ó¦ Gd¡*H°Z „WI“œTô rý ríQ®Y¤B68*ê隣¬h7ëLÑ?õº07¨¾&:ÕWprYÙì·(µú^îs øqu²1<¦Ò9Äy‡I¡óFW¶þX°¯êÔgü¸~~Ð7Î "
+˜*m¥Ý¾.[Tjð°;Ê<ŠaQ”íó†’ jý«5‘ôšª•4“(jÕ£÷ w§ £ë)èÊnP£ÍKbO‚­ ›2¬4èVRèfDÞD#ùêõ™Šù.~ne%VÚ€,Ã*º1±V‹Š½èÌ|3*R*EC\v=ˆÆJ —ƒUjŸišƒœÖïM¿Îú&‚³©L±Ú+Ž@LpŽ /^c§Ã)çí&¾ Ö„ÀMžÊŠBÔ½­Qo•MQ÷5ÑüÜõ·L :×ßß“9ñ5º+‘¹$M›J‚ؾˆªV4_Nï:Jœc½‡žRž"Uµ¢‘¹(1dÝJ+CÆöu y± 6A—tdªjäZ¦‰jÃXröV‚à¼sÞx›×Â!6,Ž”èVŠàL(^2ÅÚ@ÆÁjp5x2 ª¨6/sͤ리DÝqƒ;VÓztu#ÑT€ý.4: ðwqŽdèž1˜â¯ˆªr J™—Õþ•XuÍE¹Á9¹gB«—{àF™oK˜ ó¢¢O`Øp¡I} Í®¬~°ä%‘øñ,ÁÞ7Œtmxík6Ú£}¨È”õ†¨y:BWÓë©©âyŠ‘ß!Ïô±ˆ]ÌJŠ,L+²PÞo«÷i]ô·}âe‘‰œæ'YÂ:ÉhTŠuÞ€Z¯ˆ‚x|ŒÑ;hè‰9ØŒ@~Ѧ¸Ü¡HvˆŽ ˜yx0f:º5ÕÝ7’Êr/z¡ÙHª÷¨cO­]éßš’‚c§VcÌ èÆ ãö†VÑU¯˜Ã=…€—Úís•¬0_ig¼±´ìØ)Øf|дæª$Ï„e|¶þ»“êÿi5áë¶ÐQFôLRm ŠÕ±­éfÿ ,ƒŠ®ÑÐ 1¶ãôÑäÚŒÇÓ14w„æºd´0YR
+öí¯ÊY¿θ'Qùw×*F®Évw' ±â•ˆp´öÃäYW»öZ;JLJŸ[È—M¶g:ÍZƒÆý¼ÅsÈE±ê¨¥dÿMT'˜ð QrÆë¡é;çƒñZk{“üu÷PšÇ$To”„%Þ×¥1‘7½o!žw[Ÿþ|¿ÍZ”¼S-|3<§…ã˜Î-¼IZœ´= 5?ŠrW°º}ª÷˜Çò›•LÉåÓƳ2ƒ:¿ÊƒJ$«ý±üØô‚s^õÍ88op’=ÂÁ¡Ö¨'(”Œ¼Þ¨i“ ¸I“T4þØüïVeÎê£kº¹N¹Ñ£½®2å³Wƶr«øQÞ2U½Tµ\ `Ä\ ¾`ùi„yf½mчåìÿ
+endstream endobj 1586 0 obj << /Type /Page /Parent 1665 0 R /Resources 1587 0 R /Contents 1588 0 R /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 1587 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 2027 0 R /TT8 1605 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 1588 0 obj << /Length 923 /Filter /FlateDecode >> stream
+H‰œUÛ’Ó8}÷Wô£Ä$_b?CMí[µUc^(ÊãȃGÎÊ6 |ÿÀ?îÃvKN²d¹d¨TìÖµÏ9}ñÓ2º(K
+Ê&R$þð•äZ$‰Ì`Y¤"Ëe å}tñlÈ¡ü Cm£‹«kë!’BJ…{êh±7·û«ZPE ¼|þPK)Š /Žs‘Ép¯?£ýq²ÒŒNß°/gZò…ŠEÆd·áú£ïpF3Ãñ&6´<¹z ¯þá‹Ç×R‘²–KVß=†ëm;~2®ó—Ĭ²+¾(|öOþ¦ü#òŒ=-E†ƒòç<‚ív+x,È«!·x5ºhJ¡Y=OÝù›ž—‘‚"f"ÎÄK‘ç)Tc’ƒ3Q=-bÄ
+·È¯Õ8ª+IæÕÃå§1Ú+¹Ì¤À8|Ä Cùâ‚#N53=:i Â¥÷Ò“EÄo§¦Aº‰X2cßÃp_¡¾¬ë é{ææ O³1Œ³+>T4.X·ßq€1sYÜ{ÍÐgõ"‚ŸÉcâÉ8%ϱHÈ3Œn²õ<Í
+é@ÓŽÐZ4ðvÖ´¦[}™.¾ Mñ%&ÔñÛj›ÊU÷f4î$²gøÔçÈ¡N}8Y?Ð7à*‹õø`Ÿñ9>Õѧ
+GÍÚªCý+;lz7BSµÝä ¼f¶‡•q8À{Ûo-FŒ]·–|%XÚ>Z¯ù/„%9®J­* Gë®x†Yž`ûO Šfë6ÌŽ<fÆÏÎ s‚ç
+jÓM˜=
+endstream endobj 1589 0 obj << /Type /Page /Parent 1665 0 R /Resources 1590 0 R /Contents 1591 0 R /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 1590 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 2027 0 R /TT4 2032 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 1591 0 obj << /Length 2882 /Filter /FlateDecode >> stream
+H‰ŒW͎㸾û)t¤€¶GÔ¿sØÝ‹ `úd°Y¢mfôãHt{<’wØwÌ!U,Jr‹t¯Ñ€õÓªbñcÕW_ýüºùôúzÜ{=lxèð—8wq¤^V$»4"ïµÝ|úe̽jÔßÞXu›O¿~áÞqÜ» àðMµÙN·× ûGy/bÏý÷¦ÐF…—»"ÇQ¾Kò«mBmŽwIŠÖ_Ùï!¼Ý†¿åÑ.eAîíoÞ—[§Nð&dÂOl”£÷Ó¯/Þ¿þçoxàÉ.aÒXuzñ¾\¥ú!†F;‰XÙÕþ¶`Þõ¯ÿÛë_7zÇz7a°KááõOðNGp½^w~´ãlÄU. ®aÉÜ»UæÕI{úóë†{ÒÛ„Iº‹RîEÙ.Ͻ”’ì1νAl›Ÿ_g0"ŸïÑXÐ ¦Ñƒ3ŠÍÅ» ¦cÒwžYí²Ð¸
+í4#§Ùätî480›†g5RÍe:õ·k®ûƒ\ê9Œ^þ¸ÔÂÔM˶
+§4 MšÁÄ;Ãc‚†²(xnâÒpàΡéXÓÄv©ˆ¥ ¦Ír³Y¸¢¨ò¼ì¾p¤×¾*;#À5K%žEî²°jô]'*%‘¥©µð$u2´M—õþ\!Ð/Ú>h¸–E[u‹Â½Ú’o†Üë=MQf‡ÌµzØ
+ÙÌ;VG);„°;`à³@Nßëc›‚GälÛt°E_P„ͼõl÷X¶0ȸÎÉ—àûKëy9·¼/DŸ‚;ý&ÇÇM×”§wÏ8çD»9
+˜sMæwZ"–Í…¡êÓÜ} >)(‚ÌÉOÑ¢¿otV‚b!ÃçRB ì<["@’‰ó
+µM
+gõÛÈ÷0œ5}U6?îÚ--ÚІ…#Pœ»ó{7; };ˆ¶W¢ïšÛx9ŸûAÝÓÈJ:š©$
+ï¹ä­{¸tZ’¯æG#ÞQ(Ýý ;öñ'pÑôû²iú#u;¨Ùgëx‘õĺº@´f½“™ñ†3—MO˜·²Ö-ÆO¢„<S%$Kþäž”2g–OiByìô|›@/ÃæŸ3ú…ÑIÔUétcs7p‡ÒšÜj¦>Τ`DðS#¼~èD&•ÒÅ}$V
+IPü-ÐX-TøpþÁ–Þ©i
+endstream endobj 1592 0 obj << /Type /Page /Parent 1665 0 R /Resources 1593 0 R /Contents 1594 0 R /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 1593 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 2027 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 1594 0 obj << /Length 3385 /Filter /FlateDecode >> stream
+H‰¬WËŽÛÊÝë+¸l#™Ý|/²¸7 .U
+?Þ¢ùU6¸ å
+p½n‹S)«B½wÒqxìMîïùô¢×˜ºhÉ¢ÉCUæ²\ð‰É¼-@Øå Ž¼?˯«`‘‡Œí­ÉjТ9äÌjˆÍŸ².•,N.–Céî1àP%›³º«àÛf˜—.úR]žUÙÈ:ë'‰uŒ×,‘ð"hó¬*Ž§²R²‡“„¸“[
+¹à^®Ë†áÚöH¦õÄp‘ Ë$:¢wXò{ŸõFÞæãÚd£ ,PÞÇrØ#e§°âMàÍ÷]†ÕCjn
+RW¶ ž?ðö,›ìXÉ‚J…sñ\ŠQ¢‹åɹ5¶¶O•‰ÏÊ)ÁB ÐÖ3s=>³íë B¦/Ž6Ör²¥Îe“_dþmÈMD#ßšV±ˆŠÙè²9•M9¸$äB—I ]¾èá‹ ðO ÷¢[ ì9ª²£¬°/Qή P«b=å+´*kÙŽà𩬲:Ëûvõ¹>c¦|F+ô¦Îý}ö
+\âgy; §ˆXE…ØÁ§Ïz—ƒ „^æÓÅ–-6ÕÙ7ùVh‚ø
+
+´È³6üøë"¼ÈB¨gmy‘Ìa™®)×#z MRô. iƒn這ïR¼v„Õ:
+ ;c+  LæôJmšØƒ±E2€¾¢ìi&¥›³3ª³²ZµäOP%º¾¥áÛQvƒåuÙ4ò¦ÊÂËAkÓ¾ –Ft_-£W%᪥\~–zMe@ÍbÁfèÁ÷8O•Õˆxq`-1ƒ–‰Ðp.F”Øipj¬T x†s8d­f¥½Qnsb ¥¨€ª}ßÌ-{q4âת€ÑÑœÀ4kâøA<¦$4òŠ2@®p@`uB¾W¦,…)–Ÿpt nŒ¥ÍCò¸†’“‹C±í]„ú‹›À€¢§w$ƒø š&Huj«¢lΘèMMÛ3Ö´²î`[é[˜ ª”ØÏEv¹0uÂøàÇf]Ëig!=KæÁ›Ø)Ùv€7­áÚšT¡‹0€Bf/
+ùòYD;€l®kÜT¨ÃhY±Y´r#Øå ×ÌçÞ,bɶ“7ÖUä²ÈNJp ’°
+ÿñ˜$éiýе¹Å)^·¾¿Ê(ÒG¨JЙçŠxNò$b´} )ͪ±/é÷RõS <”G=·P¤Ó1Û5í ~›_2šµ½^F©LaÛè
+±¼{’‡3
+gIo®µÄ+Ss…ʵ;ŠwÕÞý«Ç6¡ìŒm’$OÝ/‹0Ô,|>¶:’ά¨u$gm_W±$h:mÏÿÏ\›õ–—D˜Fh´˜%Z3d8·´Ô³Ÿ¹ÏêÌÙ/<4xЃÃÔe}VÇ7±Ñ¾?3!V:>Ø!,v,á5r†fg#._<Ùt¡C»v(±} žžÂ':€6`w#s[½œ}·(4K¶¹¤ï>lCˆÊú9§¯¨Á¢'±há6‘µ²7ÙL hôÌÆv„‚a@=؃6ÁíÛÛû¥°÷ÓíÈ¿K!~ØeÃpmûbýñÝ3N,&ÝÞÇA2ˇ{/&d™Ø„‹ëBFK¬ÂÈÇ4Žð†ÆQêCí=Úp7\åûØ*€F"²<¶VÓ¶Y‘µM…xËíƒc Ä(%ŒÑ7 ÎÊåfå
+lZ6m£w¯›ì&ѲšÕ³ãÂ8ŽîFÚ]28.Ýh‹}v&^è8!¾N˜¾¤Ì\8f3‰Ëqªp‚÷z«£¿Eå¾ÒÆ'Û.žw‰|^0´já€U¨$eæ)§‹Bè X‘á#Â4^a½ün¾6Ñç,Æ‹ ·lôÇNå°Ê E%·`9i
+Iå©”ýŠîÜÁþ
+ cÃ{¬_*” ŠJé _Tg]VUôšv™GÏ©Ž—€!r7”âƒÄáÙ¼®ñ B¤tÓ¯ã@%"L‰Ä¬ƒÄòe@õ›Nõñ¢AùwM½Ü})}o.ýmÆ|õoš’
+èW RÒƒ£Þ;ù'ì{nŸ&Û‘IãS¤§'ªóµ:£¬-ŽùF¡x8žV9Ç£R&é–”׋‹ÝÎu”ImBj­6B¨B‚"é‰4+ySÅ5‡vÀ3èŠð®° Tôàr½[b_ÛÄWÄ8é›ôLlÄoî4; ¥>d ó=öXöiðG¥|4˜ ‚kí¨™v qAÐ3CIõØ6æfºbxøLâÀ)Æà §¢/:ÂFebR#…ËbøÇ%á¸1ÂÝTæÆ@80˜ˆš3ØbøïôR™»zFü %WÆ•/|±ï?Ã¥½æ*Ï:³:•D¾<+YÚÇìB(ý¿Æ«-·Uˆþß•¸­Â#8, ;èuRëšGÁ(JVßÏ*bS¤H„
+0:>øõC5´õ‹$B‘èáÏÿærîðuEx/L¶—ãAÓ…>«¿!“åa›“ M:çkL,LÃS©ÍvÀ¡u5ݤ¥»"NJXÎkgûaâ¡ïðæt9[°
+endstream endobj 1595 0 obj << /Type /Page /Parent 1665 0 R /Resources 1596 0 R /Contents 1597 0 R /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 1596 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 2027 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 1597 0 obj << /Length 1076 /Filter /FlateDecode >> stream
+H‰”VËr¤6ÝóZŠ*ƒ%–™$5UY¥Ê½Š+ †–mfh ÜÓþüCþ1‹It«8å)—èÖ9÷êÞs®ø´ î7A8Ù<\†?\ÒBÄiÊ$ÉË,–KÈfÜÿ¬ Rk»†]wÁýçNžuÀbÆ8ÖÔAt¼Ýô÷êY^Jn¾¥•$gq)Aœ±dŽ×b„…›»Lô#ýGàÛH°0âI,)+È—y8tÓ ¾T…`¢ºÑä§ÏwäÃ(Ãóˆß²8£MÈhýrGöÍô¦ÆÖ’$´ê¶aTRò·ý ÿÜüØÛÝK<l~Áw6ƒý~‡IÌ©6Q• j„ŒqËbAëå«Ëôë&à¤!ÈdœHN’<.
+"9*I°Ç´ £
+ž‚O›S1Ž%ì²¾ºÌT„ÚêùÎë—KãßAP5æËÏ—þ¤¾OCF 2œ^áYqÜrÄcp™šõÂC…íœEŽýSÓ*B
+×@J (ò}<:5;µÕJë¦ïÚþ¹«vJ"ÙÚ‡”>äÒn×a†&M¡@Í«ÝàÐ>ãäç^j—àÜ‚3®ÚöÿƒáÝã_ á2Y̓•ž§¼ä‘–'5<óTƒ"awàIWó¹Ú%9¢®‹oƒe>næcÕ骞Pû]¿UK³/ y›z—>(¼C;h™ê¡'”-¿ÙîjÏ]\*m(©Þ÷ã–8ß_mÕ% N ˆ³R˜)XO¾épã65Þ%ï*ש£ä—«nûz¹mM€Â–¤ébL†¬$æóÄërRØÂ+ÞœjסôS­Hc S(â0ÛjR[Õ*|‚!,ÁÆMj -Ö÷yƒÒÓØtÏ„”|µÓt™×b.V•Ås/ÅÜ¥=wM I!gÌBA+ 1ýRš`gô#‚‰Ý3<ùÔ¨vë¬jä-Ö÷ë³`6‹_·ê¦¦CK÷˜É(¿ŽW±À,Œ§™]ñ«
+—”fl vrŠ¼0
+÷ƒ”K„ÕˆsúMܳ£|Ý+©¯jêª
+Û—¾õq*´6o\a`ÒϦ®Ñ×5VEN_«vF ¼¸Žóè~2ÖAQ9£ÅŠÝOeÅj5j¸<§ÄΙ™£ÄûŽ^5Õ¾j¦'˜² =D’P3_0ïÜ9XM³»s¿¹u浨›ÔèVÎ0pN;…Γ¬
+†ç‚ù¿£ôû®µÇ‚=>[¸ÊúùÉ}ãyêÀoÕ`MÑpzrâ‡DóÖ u¿ÆåD½*ÀK±*Ûé ô ¶_šÎ„73ÎLea¦rºÌ¸Hä1/×úî^Òðþ÷ß
+endstream endobj 1598 0 obj << /Type /Page /Parent 1665 0 R /Resources 1599 0 R /Contents 1600 0 R /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 1599 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 2027 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 1600 0 obj << /Length 2773 /Filter /FlateDecode >> stream
+H‰”WÛnÛH}×Wð± D
+›w>RbKæš /ŽÅÂH<ÎÄ‹¬g{àýù‡ýÇ}غ4IEl1Ù-vuõ©ªS§·Ýê}×¹–´º/+éZüƒ‡»ßwB+J‚M;žÕýkõ~÷[wOôc=Ý=®ÞZiýò´r6Ž#á›»Õzx}Y‰ã§_î-™D–Ýýs•Ð¢ÄŠœM‚a/Þ„Û¥5.-Ç· ÄÕÿuaví:öZz›P8±õù«ýãñù+̸âÞKâéáÉJï¬ÿ³×Œ¿ÃoÁ&¶#î¾¾³Ú—‡çÿÜÿFF<ñéñg{ëOúkÿ£ûÛŠNL§qMƒ.ƒ9òàååec{)žp×{ÜLÖxu6®¸ÓS_É’êVÒz°Vnn¼PZ^´‰c+”€¤gôcëûýêËjÛ`x>q~DcB×AD¡7EfÀ/
+ üç%€š#GøñÝO¿ýöõÓçûç‡;+üùþßÖ¯_¬Ov¸ñÅ·oÖçßíd‹‡oÏë‡GØîûÃoÏÖ—ßïž~}| YË ì‰ˆ`ˆÆ\½Á¬E¢n:zf¶'R~M[~Ö6¢Þ( \ONzƒQëèw<šÅ¢Y|¶ö±Vm›×•eùžÑ9éOé矸'E{¾†öæǤs<½&ËöyÑ©ÓÉè·?ÛÖzî°Öà°×¡È2ð×;vùÎ^G˜{<E$ò‚RÇ‚Ÿa6ŠákØ—ùÂÅG |ý·»Tû|PÝù K&Èm§°}ðRQDC¾»$uF ÌÞÈÑé@ø
+@ -ìé%§”Pèd)LùÈ]ËJÌ©åNuƒ“"QÁß
+=uhSx>± ™à•+aÁS).’®AC
+U:°^hnÉø½¾w@ááéH: Uyv^¼¯‘¬^ž0eùGfYâ^ºT‰¶ïô'™º#û6wNûlr oø6‘“`ksåÀ^ŽNþàcÄGŠ&'‘±”~¡Þ»ß·zÜ‘^戽MBõ¡í·”“-‹É¿4\^·yuÐ,âð 0
+ææèNÖOøA5;¹=mGßDœs¡^’ÎÀûdÚmÓô…-ýáÎ!CQkŒÖLh•)_Þ. Å@èaQdúí†çÏmž.#L6‘ç®99súÖ¼péÚéÞWÝLHNžÊ‘d‡nQ*šº=ÞrÖ=¹·ñF˜ëQ‘µç8 äè¯3ø›wä¼U;v³®ÐÆ’Lžpèe]A
+‰ ¸X´F¬BY©DpÒ›+¼…Ü«€ßºtH=„B@]Lè^Š%‰~î;ØjÊH
+j’ ì(ª·çSÑИçÁpD\¢ç™Oý…Þ⎊qØ ß·–Ÿz–ÁzØr#ì±0Î×ø+ø•—úÀ3ÖãÇ Ï·nWgF%`°zRD3õÛ°2ÄŽê÷•òq¦DxˆÚŸÀãÚ×:àD¬Ó[Õ¶y]]¥ +¼y” iß*Nk2صUÃã]QVæ#{c7ôt7lNº½D`•k Ïán‹ïþ¤Ã o‰"¤ÃQ4,< 1ø¨x"Ö‚Üá\Ö—?Ì5=¿ ðá¤Èl´¹¥wºæzàX¥vü«¾/491ÞRd0?¶ÙÚ‘È(mÛu“áB›®ž9Ø“ô¤Ú  |2©RmiÐ@ƒ19÷‚Nwxyj&&×$›‰ÁL™¶— ®™\æ%¨‹@g—é—-YÕõrÀD^8v9Þ*½L·Ÿï
+îBÅ,h7:Á
+endstream endobj 1601 0 obj << /Type /Page /Parent 1665 0 R /Resources 1602 0 R /Contents 1603 0 R /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 1602 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 2027 0 R >> /ExtGState << /GS1 2033 0 R >> /ColorSpace << /Cs8 2026 0 R >> >> endobj 1603 0 obj << /Length 1798 /Filter /FlateDecode >> stream
+H‰”WËrÛ6Ýë+°g"†
+î–5@ m„ˆÕØ–A[¸ªj–EU®ÀoJ{¹"ûyÏ#lv®ðP«
+•ÔRŒ?þíè´ÛŽNû8:T#Ûº)ÐR,ÛÖ5–Š‘—èÎÈ-MŒeÝâð¢[b#Ç
+Ÿ‚Ë …±kÒ‰"oPgjÃfS4jÂLÚm€ÇÕà-ÉcÜ71vìBh0¥íáP6õu±×±×µªóñŽ0¡À)n!—w´¯Ôõ¶WÉÓ ?XQ6@¹ãÁ!"ïªu ;Ë æÿÑB´ÈT?—{]˜ÌüÉdK¦NNÂ
+zû+`ÒÞb¢h9djàÊƶb²¥ÑV]l-" W ᯟßjÈX•Œi4†¦¸ Ü5µe=óh=Ó¸°hØä‰Ï·¬k§Ê¨W Ë¯*roȤ#EN™W „7LØmÕÒý–Òý†Ê‡à¥Ýì
+Ó(ïþ†\W:µàÖ,õ~¤õ
+ÏñêÙ|0üÂî¥åxªÊ5nÜwɵÎf”¢1Û&u‰q8ö‡¯‡ðuï¦8yZⱂñbGÿ{¾µx
+Ù
+”²—Ji&5¬4§Ãm½D±H¾-Vxb¿1ÕiÅ»jéTa»-‘Z1^ú*q© U¦Ê¨òêeö®²7vy¤ûË(ØÀìUÿ#oáºp©»¼Ü7§Qg‰šô'4,ÞX–ù–@\²7fPuÜÖgæFž÷æòÞÜžS´©ª‹‚•qÉòålŠ±ÌºbÏßpScù€”8=e„WÊãžó‹ ¬kvÅjê…«^f˜¾b6j$X
+Ó•èÂuY®ðÒ0\€g|Ã~/EOÅŽK•óRÚ·c`󶺶ûƒÓíó;ö¹
+‘û…ÍK¨7¾-Oº#ˆí¶Fòyònªî†Í;´HÅ( =.P·ÓOÕ ÞÌDlW†\+ºÛlê&óéNÎEZ°*·î뢫¸nk¨,ï'¹Ña›îò‰¹°2¯0ÓÑ$ã\uÅo:´ë¾~Ù©äZò³ÏyÊÙSæ癧åJJ#w–‹ _LòŠc}ÇÎ^ꕽ.—ܶ\¢c1’C
+‹'#¿ûAwÜ­ŠÖÒc®
+ðQÚº-kP|zƒ¡Ç„èÊ5åD?ºeºk† C}Ãø\(Ý-ó..ÂsI¶c*äcØ=ŸÔD-ì‹g™{.}(PD(úÀ!„z?uÝ É©Î&‹ÈÆWÞbž{òèʹ˜R!s¥7ô+’|ìj‡ÃÞVŸ* —\zþ’‚žSÊ]×-=K*Õ·”¢ÿÑù#ódíÕŠª–"XÀÆn§å'êK¯Ž '®¨JXŸË®JÅ‘¾J=kMT‡§}IrýÇmº6&£9»ˆïøn¨z©- D³^\“og
+endstream endobj 1604 0 obj << /Type /Font /Subtype /TrueType /FirstChar 32 /LastChar 122 /Widths [ 278 0 355 0 0 0 0 0 333 333 0 0 0 333 278 278 0 0 0 0 0 0 0 0 0 0 278 0 584 0 584 0 0 667 667 722 722 667 0 0 0 278 0 667 556 833 722 778 0 0 722 0 611 0 667 0 0 0 0 0 0 0 0 556 0 556 556 500 556 556 278 556 556 222 222 500 222 833 556 556 556 556 333 500 278 556 500 722 500 500 500 ] /Encoding /WinAnsiEncoding /BaseFont /DOADPI+ArialMT /FontDescriptor 1631 0 R >> endobj 1605 0 obj << /Type /Font /Subtype /TrueType /FirstChar 32 /LastChar 150 /Widths [ 250 260 552 667 469 833 802 281 354 354 490 0 260 333 260 552 469 396 469 469 469 469 469 469 469 469 260 260 667 667 667 417 0 656 677 677 781 708 615 729 865 396 375 677 635 917 844 792 615 792 698 510 688 760 667 896 688 656 667 365 552 365 583 500 0 479 552 469 552 469 302 542 552 281 260 531 260 844 552 521 552 552 344 417 313 552 458 708 500 469 469 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 490 490 0 500 ] /Encoding /WinAnsiEncoding /BaseFont /DOAFCG+Garamond-Bold /FontDescriptor 1633 0 R >> endobj 1606 0 obj << /Type /Font /Subtype /TrueType /FirstChar 32 /LastChar 46 /Widths [ 250 0 0 0 0 0 0 0 0 0 0 0 0 333 250 ] /Encoding /WinAnsiEncoding /BaseFont /DOAHLD+TimesNewRomanPSMT /FontDescriptor 1635 0 R >> endobj 1607 0 obj << /Type /Font /Subtype /Type1 /FirstChar 1 /LastChar 1 /Widths [ 460 ] /Encoding 1639 0 R /BaseFont /DOANAH+Symbol /FontDescriptor 1619 0 R /ToUnicode 1640 0 R >> endobj 1608 0 obj << /Type /Font /Subtype /Type1 /FirstChar 32 /LastChar 32 /Widths [ 250 ] /Encoding /WinAnsiEncoding /BaseFont /DOANAJ+TT11CEo00 /FontDescriptor 1621 0 R >> endobj 1609 0 obj << /Type /Font /Subtype /Type1 /FirstChar 32 /LastChar 181 /Widths [ 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 ] /Encoding /WinAnsiEncoding /BaseFont /DOANDN+Courier /FontDescriptor 1617 0 R >> endobj 1610 0 obj << /Type /Font /Subtype /Type1 /FirstChar 32 /LastChar 181 /Widths [ 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 ] /Encoding /WinAnsiEncoding /BaseFont /DOANGH+Courier-Bold /FontDescriptor 1623 0 R >> endobj 1611 0 obj << /Type /Font /Subtype /TrueType /FirstChar 32 /LastChar 122 /Widths [ 250 0 417 0 438 750 0 219 271 271 0 0 219 260 219 500 0 469 469 0 0 0 0 0 0 0 0 0 667 667 667 0 0 760 563 625 729 688 573 0 771 323 0 594 635 823 781 677 531 0 625 500 583 719 802 0 0 0 0 0 0 0 0 500 0 406 406 271 406 292 219 323 417 229 208 521 219 625 427 354 406 427 302 292 250 427 344 531 500 333 417 ] /Encoding /WinAnsiEncoding /BaseFont /DOANLJ+Garamond-Italic /FontDescriptor 1637 0 R >> endobj 1612 0 obj [ /Indexed 2026 0 R 70 1613 0 R ] endobj 1613 0 obj << /Length 228 /Filter /FlateDecode >> stream
+H‰
+endstream endobj 1614 0 obj << /Type /Font /Subtype /Type1 /FirstChar 32 /LastChar 32 /Widths [ 260 ] /Encoding /WinAnsiEncoding /BaseFont /DODGPG+TT11D9o00 /FontDescriptor 1625 0 R >> endobj 1615 0 obj << /Type /Font /Subtype /Type1 /FirstChar 1 /LastChar 1 /Widths [ 1070 ] /Encoding 1641 0 R /BaseFont /DODLIC+TT11DBo00 /FontDescriptor 1627 0 R >> endobj 1616 0 obj << /Type /Font /Subtype /Type1 /FirstChar 32 /LastChar 32 /Widths [ 248 ] /Encoding /WinAnsiEncoding /BaseFont /DODNPB+TT11DDo00 /FontDescriptor 1629 0 R >> endobj 1617 0 obj << /Type /FontDescriptor /Ascent 629 /CapHeight 562 /Descent -157 /Flags 35 /FontBBox [ -28 -250 628 805 ] /FontName /DOANDN+Courier /ItalicAngle 0 /StemV 51 /XHeight 426 /CharSet (/Z/at/slash/P/bracketleft/q/B/T/zero/r/backslash/C/A/one/s/exclam/bracke\ tright/D/two/t/a/quotedbl/G/asciicircum/three/u/quotesingle/x/I/N/H/unde\ rscore/v/four/E/dollar/J/w/F/five/L/percent/y/six/d/M/b/ampersand/z/seve\ n/O/c/braceleft/eight/e/Q/parenleft/bar/R/f/nine/braceright/colon/S/pare\ nright/h/semicolon/i/U/asterisk/V/less/j/g/plus/k/equal/W/comma/K/m/X/gr\ eater/l/hyphen/o/n/Y/question/period/p) /FontFile3 1618 0 R >> endobj 1618 0 obj << /Filter /FlateDecode /Length 6510 /Subtype /Type1C >> stream
+H‰lUyPùžž¡§‡k8šFèÁ\-¼@ð"jÁUq¹o.A9äÔõ "qDäX9t9™n@YDT4º“¨›u¡¢ÆòzÍþ°6 [ûW¶ªë«ê×ß÷ê}ïýÞ¯1‘X„a˜™×f_/ß…žÉéšØHÍTÈWˆx;ŒŸ)ægJøå&J3‘ôšð3M¬“Ñ‘ ÙDÎh±ŸËÊ~AR͠Ђ³³ËµI0L–‘ãì¼ØÉÙÙÙ39%K“f?/|¾½‹Ûr7GÝœ§Ñe?®Ó¸t—Mãr{ˆä]‘ö[²RÓ"Sí½“Â“5)Éš°´È'{{„û€©Ì©ö‘©‘š !ú« &c"\,2\&Zc'Š‰Ô⩘h©h'F`…bs±‡8]Ü,¿ÿ$±•¸Hª$½ :pCü~Eú;i­ô)aHÌ#Ö1Ĩ¬PvÏ0Ñ°ÇÈÚ(Äè„ÑãùÆŒ?7!L6ššÜ1]`úR¾NþÂÌÊ,ÊìœÙ[ó
+j0¡}îyÆá<pJ]ÛKs÷ëásW÷¾t’Œ©YX "È Ñv"¡*¾2æ¨ì}àÄÆÒù‘³éR"½Üÿe CnËÒÜ+ù6IVŸ«ÝI¯rÉöÞ¶­â›h†¬õ©+K)/­™ó®’¡…8B`pòæíïhy^0ØM¡åGn°ï,÷-K~&àPÃá7–¬ݼÁûšÿ£Q}ÿ&°”ŠKÙžpþòåºÆŽ+µqQ ùÜ'­¨á»MàöÜ^«ú îG(§öé']‘Ke0.Ïãvr°„…xÖrŒ;ÆA
+˜’Ùü-k™š‹uOwžl·}Â"Siؾ­) ™¾€¯ð’¯ã/ë躆–~Ý¥ìôFæL7^±ý¯Á
+·à€ÈêØšeÙñü£Å Ùmï_¼4ö¿¸øÇžm»ÈÙ-!8Ù½qœJOMJ
+T„„¶ŒÜín½tZ)_0‘ƒŽ“ð»&ì(•t-‚£æ!‡F/Uÿ˘Cƒ¼í£þ ó‹ ’“À]ÐQoƒÀq-`ŒJZ„ëçµÍBrÍñDŒz™ øa`.-‡?gÀ‹L ªnKø½ül*{Í :N¸§ôrB½{½`VÔbd>ûŒ"£Áæ%`ŽË!€Å Ž•ð@±ÈüTÈù«Àù±àGÈG0ð†Ux¦”£ÔÁ‡¥rØÍa „~ì¦8ô ν1¹ÀHxYõMrcxd|J\bCRû…–s͌͞x†ýÈ;Køò‰•”Z;‰}¾Õ15->[HfÀA½PH\§ºJ¾fnÄì¨_¯ÈÌ<x K¹\íœï&+I8øU.½ð
+ƒÕ°é8$Œ3ªª€pÈF³‘kÊÃsÅL^WQO DX‚¸C—¡eê‚ù–ûÊäe^hR³0Œ£ÂapçÏho0H€W+>¹y &N2‹½f¡V(Fc –*/TC€;(ñÏ»¯=¡
+sÏ1Ã1îõ¾
+Ÿ” ¥KŒÜoèt'tž’œ9L^ÜJ¯Ý™( yÙ-Ìã AxXKÞŠÝ£'Çøp¢Þ®jŒ=Áouõº†ÇtSAmþ¦=UÕº^±Þ/-v«²zþ—³7›‡Ïu[ƒCÂ7Ù+VIɱ††CEõÊ–‚«‰´÷µ“ÿæêêP&GÝšƒ§ŽŸUŒÒ®l|âàæU ¾Ìî±æ}ç¬ ÐaôßuöH,øóZ
+ÝD±ÓU
+›òafhïÁ_ÿYlWçeE1 zƒ]~iH_vJñ†õÙèé«šµÉ·Z¨ÌÄ3oïQ€{'ÈF”ò\
+8PõY¾báì½
+°`Àlôј’|å ZÊ'(Ì3ÖµâÇ7c']Uû ÔjTK¹­JñS/<õÃëÿœnÿnx×ZFžÛÞ}P.ôÝ…f¡ïGà õ`ï ÿ^¦1.´*T$«—-rpÓ·êôMÊýhÙg¬0)®$®¨øp‘"(`DzI÷õIŒÆA~ô•³Yí¥X¬æFïs=uûbk•Ú¤ªíåá2yÃœ©mÝJ oCÔéìË[£„uoJ¹Ôz¡¶•AUÿ“Ãá)Ñ‘)‘ío‰‘¿ð£ò®ˆ[Rù™ l¬KÂç•R>ÀÜÿ±]­QQ]WÔ{/I´s;hçš{PZi@QH”FÀà1¨× ˆƒe|Ј€¥Ú¥"ˆFPÐy
+
+:1PDM jµjÓ˜&šv‰î³²ïžÁÄÕ]ëÞggŸïìïÛû
+àë]<lˆ1§”í$I·–>XÖã¶×z6»XfúXÌõ4ªWy܉9ä°”Íê]Í4ƒÝ4™¾ÂýûöØŽYm ~¿ùq
+šêÚ<à°Š±XËÒÿÁZõ…fò|}º›ž³E}ÄHMÍÇ èOp <áp‚zA3E³ã’…Ôtˆ‡ñÚH®o:’+ÐL‹†n܉ñ/ 㾓
+xŽîâÐ×Y,ÅÝ]h8uÚ®—ÚxçDµ…¸Öú.‹»uúoW@šðCôcƒRà JLÓ¿ e5Oâlp4zÌd5 þþà3jp憼d"vàiì ¯•ŽÖ?Zù'Ëxì'5Ԧ͉0š¿vÝÖ^§”UÖ×T´ë·þQ^¾."w±„°Ž¶¬¨Ó¬-Hcmu$Ûq”XÒé¢khgÙŠvÚ gô]YÖVùzbH7ºH~ÓW•4cæ’¹±½o¢ËÓT¹"‡-V«ÅjÎ(Šs¢¹²¨Ej®Ý˜]¯ˆöÝGly6©Ú~¤¬\ÙÔÄe¥§mO“¶oß·‡"ZŠããKc¤ÌÌܼL¶Ô–_˜_XàVÌØ—ç©°‡FãÛ‰¡~8¢ "Ú ›K«åþ†>{‡ÔÞŸ:õ¼Rš=¤çH¬<aÔ«Q±ßƒŒ’É¢‡k.º]+xÜ
+!ûƒ;ŒòÚº+Ù©µËf¯Uê«?ëì6À-&¯‹4‰èƒ­ô!šÁ,Þd“õwCÐfõ[ƒ´$ ¦IÜ7­ê—<3Rw—‡Xÿµ÷Ys"Ô•²†2Œâ't`(Шþ ˜4|^pñ¼xÝbétî)Áhµ™ï`Ý<plyÙÔØ>ædCWë1–ù-‰‘b¯Øï´ÂpgC'ö‚¤pÝ#Õ.E A„p· Æ©=<øa
+'>Á0Ìãæ«°žøð0b`Æp¯mÀQ-ä<,No %»\½) 3¬ßÂNîK‚Qê >Ä ÎÀ_þ—êrŠâʸˆ==•²˜u:ƒqZº5.²‹R–(ø€ ˆñÚ¼•€¢@Ôøàaª,>¢(¾`ÔQ)|€¨ǹƒ®ƒ
+ˆˆ]5¥–kÅä4žÉÖžcªü‹ªáž{oŸóïþŽŒÒO¸!pŽ{gY:;8¨ƒ÷{B†‚#°‰W9ugsúál;ñÙ1ÞÙÙÌ|ù².­o Å_ùÒYÆ.Fü¾’&»îÑçG$&§[׳–«4Êqï¢VÑèzÆ×Ôµ#0þn@#Ù¡š {Ü^ªÞîê:5Çt†Ü’X7ï…>V—64^:^?‹o)ÿñœÃq1òÚì–EzÜxÞmƒúœÔ‚MçÏc5%2æ
+øòu‡òÊ¥Ë⎌ÃÖ/™&—Fq–ŠåMbåѵه價ŠÒÌé|ñœ™ÅDÔÑÃ#eÁ:±þÓW­×k/0IÈɯ'¤¬Sã&$Œ§$ž:^¾kßÞRù`µ}ãYñtMÁšùí þÃÈç¿Ùá)ò«z2Là‹ÜïþÓ'/Α`/z)¼ç, PÀ‹¬×ùÜ’š‰ÐK„Þ÷ '¸ÉŠ§÷¨q#ÆøÙÁðúÁµû ³ %Ñs¯n7<¡´¾fZʸ¥‹ÇN%â'¤«ÆG7î‚®þÂÊùµrõÏWÌŠß=MÄ0ìó þ ܇ƒñióÑ3—Þ»(=ŸÏ´TÝñd£Õë8]¾8‡o˜¾+JDÑÏM” Ô·!Y`sã‘‹'(Áü¿`1%£X¨v|øÓ“ëw_Òï V “µ‹Ža`t¸]©‡_h¢:zÞ”s›3vlv‚˜0{Ëyòüýö æ­êPBŠ àŽþا¿JøÁut‡qåUÒDH3á+Ý¥­—®'‚T1p±f-ÐÆÀ‹T”èpWw©]¦Íi¡[“Åì¾> [.ÌÛ°t…9¦?yG[Eõ`˜mNlü™oN€î8+( 9£íš9Õ°Ï™ñ´ÿ6¨R²ž:¦ñÇS'M@9¹Á¾·úÁ°«-¬§dìÅÍÀžá{vFKùQÜ׶ûÙ"ôºí ªÑƒ)z:J&ÿ±}¼ƒ›:ßkxýª)®wÒ•p׃°Ÿl®]Óf1eY ê?H™+eå¾KY,
+þ§Œsí±½èX#wìXW°M>ú冔¹f<î:µÕAb½V<òî›Zõ¶ñB]õšÓeÒîeé%ñbØ„ŒY±²Ðv½ftbëÛ:ÒØÊŸ©iªº,Ö:¾
+“q«VJbnomLí«A"yÄÃéð1*„_=†ã
+–ôXç/@¤ählã>""pèØæ×ïB7è]‰ºiïceð³Ö.ímbª¥“!yXG8_“q"P ;/ YÎK_’bFñùßÁ£¡©¬þ¬D¨?DáaˆÆ4:¿É#®‚ûÅJÐAoèÖ4z¨ô¶°Iv#Ä3z««ÕFxcZ»·pOþn}Yî¢méâàI!ÊDY¸zœ @/w/Eý€a4Ìâ…ê_ [ÝQ›X¶¿ µX~›]ãS–`‡*{º&ù›j ô6uF~¶Y
+Ûì·vÖa}Þöü={Ì Kj%¡±œ¬9Òâ’ùòÙQ%3hLí¿¸8ëÛùB|[Ñ­½ðèt¡­¸|ž~óš¹æì¾:e’òmu¼tIÁ VÇiµ.½#ÒsîÞ^ÿÀuG$åÔî;O›
+ìÜò˜,³•fÉ™–ÕUfÈ'xÆ`ŠBÓx¥KxBánÙ¶[-‡Ë¯h¦ÿ¹–£6>‘ðVhÐHíízBê°KŽ™ó™˜•¹Å²LÎØ·¦ªÊ G´ÿâF>igÒ±,iIʼnU'Ŷ-7[eaÅÒ²=m™j`F dÂ
+endstream endobj 1619 0 obj << /Type /FontDescriptor /Ascent 0 /CapHeight 0 /Descent 0 /Flags 4 /FontBBox [ -180 -293 1090 1010 ] /FontName /DOANAH+Symbol /ItalicAngle 0 /StemV 85 /CharSet (/bullet) /FontFile3 1620 0 R >> endobj 1620 0 obj << /Filter /FlateDecode /Length 251 /Subtype /Type1C >> stream
+H‰bd`ab`ddäsñwôsôЮÌMÊωXüfø!ÃøC–é‡,óq–²<b¿=~ïüuíW«ÜÆÿÝÝ’‡ý{¸À÷(þﱂ3¾÷1032r$gêX8çTe¦g”(h$k*ZZ˜ê€Hs0i "- À¤¹‚cJ~RªBpeqIjn±‚g^r~QA~QbIjŠž‚cNŽؘb…¢ÔâÔ¢2  Ô© ŒÛJ˜Y´¾÷ñýHøÎúýãÞï˜è}ß)úÎꎆº••Fœúm«÷=rïîÜ~/ÏW1ï§é¬ß±3Øžs=à0
+endstream endobj 1621 0 obj << /Type /FontDescriptor /Ascent 0 /CapHeight 0 /Descent 0 /Flags 32 /FontBBox [ 0 0 0 0 ] /FontName /DOANAJ+TT11CEo00 /ItalicAngle 0 /StemV 0 /CharSet (/space) /FontFile3 1622 0 R >> endobj 1622 0 obj << /Filter /FlateDecode /Length 82 /Subtype /Type1C >> stream
+H‰bd`ad`ddtñwôsôÒ 14tvÍ70
+endstream endobj 1623 0 obj << /Type /FontDescriptor /Ascent 626 /CapHeight 562 /Descent -142 /Flags 262179 /FontBBox [ -113 -250 749 801 ] /FontName /DOANGH+Courier-Bold /ItalicAngle 0 /StemV 106 /XHeight 439 /CharSet (/F/W/L/K/parenleft/t/underscore/M/parenright/u/T/Y/O/A/H/B/k/Z/C/x/quote\ single/o/R/D/comma/percent/n/d/p/S/N/G/E/z/e/U/equal/I/f/r/V/P) /FontFile3 1624 0 R >> endobj 1624 0 obj << /Filter /FlateDecode /Length 3025 /Subtype /Type1C >> stream
+H‰TU Pgîž¡»A`Tšî3Ú3EWHžÑU”,ac¼ˆGÖP@Áˆ$1ˆGE£îŠ#DÁhÔ 4Š²BDTtÉ–Qé_+ž…¾6o’ì?#nÕVM½êÿ¯ïÿÞ{ß;†e< ˲þ‘S"&G½81myFRBÆÒRâ]÷ãu £÷eõ~]1êþ}XÌò1ÖûxèV9ÜübÝ‹NÙËþQTôÒú°©'4ô>Û×Wòc8–õL_U°%8x¤<1-=7#iIb–mHÜŸmÃÂB¨ vÛan;ÂmG»mˆÛ†Ú"âÓ%Øbr3³R3mÑŽ¸´Œô´Œ…Y ñªÍ‘’b›îâÌ´MOÈLÈȦ·ÝyØ\ytÿ_j Ëð ãÅ2¾ £0Ì
+CÑ`L3V¿÷àñ¶Ç§¹Þ\×ÈGñ¹ü9þ…0NøX¨.zzxþèeõJ÷ªéáß#¾Çž÷¼'x§yo6%·‚­µÃeüN·‚©5ÎeD„ÇzštejËð‘“¢ÆGœ¾»Aéhnì°N”çÄ-Ÿ›vèHåþçê*’+âo°Çi®L£àw(ø‚|é%8ž‚|€‚Ë]`¼…’£Éù&Nªx—3z‘ÊBñC£ž3Jš}'dh¾uàqù9¥¬î³½<‡óý§¡ }ÍSKÐ †(/<4B'–.¬
+ùd_ÞÌÁ*¾<Š UùÝ°UÂ`¾h¢E‚ ô#~ÀkÕ9M»ôz-ÌýfQ™"êKK¸ªŠ=Õ'Ì—æÝÁ!
+WÛòX:x± z^j»muì [¤Iã ²ô‡~ЇoÞ·$Õ*vÂBiò;yC!<ø#À…[Mk4=¸“­'pS3êM×¥˜fnùÒÈoZPžù´‹
+a8s¹þÌŠôZkyö…ÂöÕ³¿
+µàp°þ…p2$ß’–kVš€.¶Kƒý”­NK÷“»‚N)%ñó?µàaõ†S£Âv†ÀkÍkµ¯—¢r&N[¬d'%.°„é2p<xäâºUQ©í«½Fåµgäéu2$¨è‰ô,šâ¥
+â£èþ0
+è½ $: Õë.G®ªÀPRV}R(Ús­’ˆ?‰¿ëgá7 –8óÕ6§<^…Z‚=ñ'úüoš þYvzŠ¶Áë°†±‡@EzF ©Ó¯ú\ê %´*0Y"xúÖaÀp>©€ûÐá(Lµ¼7óŸµ ÖäªÕ ßšÁöóš"v5ÎN=eµX­ÎÊâ÷¾eÁ ôA_œ„Ó _
+Ed ØÁö±îl]êöûFý3™àhªæhâ W!Ì)cØK9¦dC:Å°ø@ŽQo•I€u<£ÊQ›S>¿!o7„èךæ¨Õ<ŽB;öv‚›º+·‰Œá\Ciú„Ï—õ92£òÝÒYž¯h°‚õ³2|¡b$® þ}6ª÷rÐÿÂk#ð5ÍJ×3©€³d°j Tq8G½Ê@Gä=Ö“ ¾ZcÝR]ðõ.láT×äÌäót™ë&º«ÁVJôÄM´K%ø€ëõþFukó9
+Í<ìpq·õLÞ´¦S·¶ò<è4êßC•ýGÀ0hø`„vœ Jø?„°VX®o‘B磈þ(LþèÞy0
+·zƒ¿ ^¤ëzÇ«¦¿©A1£T†Õvìk8¯ãþå‹iøÁBê—¶{¤`ÂMgØ"ý£þË[,ÿ˜ªÊ0Ž‡vÎA(Ìûvîç’skC9¬Ù
+åòc,pa‰ 1™.9ÅäÖÓé¸Ôˆ‚òÃ+(†Ü-c©]XŒK9rWbC×Ús¶‡?zNöï{Þ½çùñ}>ßÝòðòâ.³œ›9œã¿r‹—ÀΜƼ‰iŒ²tµ0³u§‚ëq¾hc·põÒ«12ÜÚ{]eÎ"é8d: Æ€ªâ„´;³³ãsÃc¥ymôµ¡òôÁƒVå_ü!¸ËÁKQŽ}/ïÛR*’ *ùáÚ ›±Ft}éîë°BÈÚ9´‰¼ýŽ;ÿGÏà” 2ÞÇÛ'ºÇ•±;ΗLí€[_Ðk+-¯“rýF¿±$3žØ(ÔpÔ9”œÜc‡Šm¹e'³²¬6Ä, {ƒf„sÌ7dë˜Ý'}íîœòVTÿ{sò„5º0>'¢Ï˜$Ì»mŸÎ»
+/éaÌx±A» nÜ&
+l¸#Q3ûÝ<`è1ø ¦#P&ƒ° Va­-ÌŽÑÑÛIÇX‚7UÍ-GÅíßy V<œ}ÁÞ¤t5·òYš¹[”ÔíE²½
+™= âY}Q;3–ìSÙb&«’‰–×’Üœí#ú[ød{sJŽÝ’‘œ³®ž§?>×~cúÞî7Ôÿ“¦¦pæ3îÁ¼Ì.o;ÞªzjJR•Ôôœü,óMâö1W¤fX8¦ÁF‰ ôÕù›®ÀÆö‹Š§¾ú£:s âÞë”ñзr•.T½–“¦$ºiƒdB?Ö@¸†áïj¸Kˆ§•“3þÞËwU¢ºÝŒÅ²ô'ì×Ùc2”'Àc³aT^Ü#9Š KÓ•¸“ßuÙ ã [¥}õ‡ÎUßéš)ŸPn{Ï{lìˆCz–”å<8ÉdÏA 惓ù “³¡<N¢òXæ‡ rà­M|?ã½1¡ãaí7‘(°G”0a“N^&úgf…ògqëÿèà ©¨ …·cb1U‚.èî/¯ÃÍú4¼%a'^4'<0j
+½V³\c¥á“QYGŽ
+°R ù´Ùð5aT D7>'BÃË.é“Ã׌ë[`¾Sjá³³Oj—ûÝ«xv½b,¾ü/“جƒ
+endstream endobj 1625 0 obj << /Type /FontDescriptor /Ascent 0 /CapHeight 0 /Descent 0 /Flags 32 /FontBBox [ 0 0 0 0 ] /FontName /DODGPG+TT11D9o00 /ItalicAngle 0 /StemV 0 /CharSet (/space) /FontFile3 1626 0 R >> endobj 1626 0 obj << /Filter /FlateDecode /Length 82 /Subtype /Type1C >> stream
+H‰bd`ad`ddtñwqp× 14t±Ì70
+endstream endobj 1627 0 obj << /Type /FontDescriptor /Ascent 0 /CapHeight 0 /Descent 0 /Flags 4 /FontBBox [ 0 0 990 730 ] /FontName /DODLIC+TT11DBo00 /ItalicAngle 0 /StemV 0 /CharSet (/barb4right) /FontFile3 1628 0 R >> endobj 1628 0 obj << /Filter /FlateDecode /Length 144 /Subtype /Type1C >> stream
+H‰bd`ad`ddtñwññtÖ 14tqÊ70
+endstream endobj 1629 0 obj << /Type /FontDescriptor /Ascent 0 /CapHeight 0 /Descent 0 /Flags 32 /FontBBox [ 0 0 0 0 ] /FontName /DODNPB+TT11DDo00 /ItalicAngle 0 /StemV 0 /CharSet (/space) /FontFile3 1630 0 R >> endobj 1630 0 obj << /Filter /FlateDecode /Length 82 /Subtype /Type1C >> stream
+H‰bd`ad`ddtñwñ pÒ 14tqÉ70
+endstream endobj 1631 0 obj << /Type /FontDescriptor /Ascent 905 /CapHeight 0 /Descent -211 /Flags 32 /FontBBox [ -665 -325 2000 1006 ] /FontName /DOADPI+ArialMT /ItalicAngle 0 /StemV 0 /FontFile2 1632 0 R >> endobj 1632 0 obj << /Filter /FlateDecode /Length 21071 /Length1 39816 >> stream
+H‰\T xMWþ×ÞûÜÉM„"ÉÖ¹Ž¤äQDÕs"$7tyÐÞ*WI‰giãU%æzIUkTEµzb„aF[mÍGÄhGét¼ÚúUeæûÔLÕ=³î¥†9ë;笵öÚëñïµ@8–@"{L^ϔɕžYÀ«¬ÄèÂroå8³~°YôNá¼9zMÂéy¼ö%`ë5¥²¤üÄ‚ü:Àî`yFÉôS:­ÿ$H9ŒŒ,-öÜ¿ýeóž§JYѾO{‹V±Ü­´|Îüˆ'=i,¿8»O¯(ôÒ™›£…gYN*÷ί î¨ê.²½>Ã[^|§ï¦i@í)Î'¢²böΛŸZ3°^9«¸2×ôð8ǵ´ƒˆáשíDŒŠG4`]á÷jàï/³®Öñ-ïnº÷õx—Êð.þ„¨•w½‡hÄ1D![P…¨† ãYókä2i¬ßH1V#zbç³ Ílû,á :Q´u ‹±\~Ê»–3Ò]1Ù¨ÀeÍÅ\PËУ0•´ÄòXk­ Ö›Øò˜uap¢©Ùú^;k}‰dÞ± µ¸@ÚìCGY–¯aêäDEV‰õ#gàÂóœƒBšéˆHdïŸBÑT%ÓÙËvË´Ž²UgLD)êpúÒpáÒ&XYV3:qŒùìµ{±Ÿ© ‡ñ9´VëM«1HÂÓ\O#NÒ鿳Ô?„Ó¥À+ø#>Á)2è}Q¡9´-M{Áú Ðã8Û¼ótK,bZ,?V™Ö0D0.ëhã#\"'õ¤1ôŒè!*ÄV9 !±7SÊïÍìý<%Ò~á-r»Ú­nÛõ_´"øDâ¹m_ÃûΕê4›^¢3ô•H“Ä«â²Ü¨v©Óv/Wýʱ»q‹ÚSÊ¡_Q)UQ5­§Zj¦StU cÅ4qC–Ê™ò°Æ”§f«eÚ
+m•íªßã?êÿ‹ÿ–•b­@÷ÃRÎ~¶reЂsLp™4
+£&\4Ž^dZDkè·TO»¨‘£œ¢ËtþE7é¶
+—èÊdˆYây±Ql-L§Äwâ?2Jv•‰²¯,óegU-×1í“—”Sµ(‹qNÑj´×µzm·öÖjsØ_
+AȉŸ¶ßI¸sÞÿJ¯¿Ñº„Ž|†NF¡ sö^¦©|Þ5ÜqïáSr0vNJ TÅÈL¢©4“æ3’/Sí澇1JŸÓ Î9\tæü„è+†‰1Lωb1S¬D£8#~”v&ÛÊŽ2A—e±œ#ÈiÊòïò²üAþÄd©PÕEuUñ*Q W“Ô\µU]QW´ Úqí[¨­Ü¶ÂÖdû§ý){ª=ÛžcŸhžßþYHwç‡Ø‡ßã‡.Ê¥Ò-÷a­è£bÄIq’ûyŠd–àNõ´R,¤FÑM›o$Ñh´ªxÆúcñºøA ’Y4’ò0Uô¾ëÍÖA½Í¿ÁêC\W‡¸¶“ìy¾ÍA‹Ä ›{ b
+Þâ[ÑC›aK°u¤?‹2åP#„ÚÅÕ  n$µx™&Ê:Û qsÑ¢Bq^¾ÃÙ·ˆ=2Kµj¹TÊ7`!V`¦µ 4:M%ô âÔEžnU2E¹ø¿˜§Êžiûùvä90Tf±&š;g÷Å8žuL›yN(î 2¾ãÏò;‰FÛXÑ„-‚xê
+êi¹ÿETâ1¾9çi”–)Z´L+YøÄ9‘'j>_F;Ž¢ñ-ÓRµ?À§>G†X«­¿rwwç [‹Éø%¾æ*¿ç#äôñ V¦¬äz/ ÇÚiu¡P”ZÓ1‡°Ã®ÁkOLK7vhÚÔ_ 4p@ÿ~}Ÿì“Ò»WÏ'’“zt<>®›ÑÕ¥wyìÑαΘè¨N;<Ò¾]dÛˆpGXh›»MSR’ÜFfnƘ*Þ1"9 ^VxP˜:«2¶1õ‚ ™þ°e[Nù?Ë´»–i÷-)RŒÁÉIºÛÐÍæ Co¢ñ9æ×dùºy=ÈgùuA>œy—‹7èîèÒ Ý¤ÝmfÎ+õ¹ 2Ø]CXhº‘^šœ„†Ð0fØ3£ŒÊŠJ¥ #¢ÜBÂ9)Óid¸Í##)ãÜÞ"3;ÇãΈu¹ò““LJ/4&›0†™mƒ&H†1mé¦=F/ TƒUzCÒßê¦HL.HtEÞ Szó1Ú%rÜ 3ê…¯£ÿ'²óöéžêWc¥Ï]¦DŸ¯Z7ßÈñ<¸ê
+|óóÙïq™¾L½šA™§s4±<ßcÒr©* Tu·¾bÃÐLÕÍ6Æ0£Ô7µ€Æé3‘»Àµ×éL;`]„Ó­ûÆz —9$ÖÈ÷ftnè
+©ò¶.…¤ÎëèÚÖ¥Z:Ÿ£S¡–. uüÇgLvq06zdJ–§ñU†/§•Ã(ÛGWNÏòIMìŸ6l¿3lDÔ×èË¢šüÀ:Ïtú–^JA¥Ž*Ô6ÚÍЦS@œ¢ж òàî‹ö%ÀŸ@)0ÍÖ­¶
+«€D P}™vŠ/’=¡Æð±ŸþÜ.¶ÓÈ
+ì,Ò;é0ËÀz‰jóª8JÍÚeZ†ºï‡°Žíð7^¾êG´Pý¥3iâk Æß Ø•ñ°Š1ÿp†1T 4`®8~bß@Þ‹}݈¹>ñr ·Q°ûîc{0ÿBö9ï»R:¼mßF›M è?/µsLrî±fÚqØz©má׿€àgÈ8³º1ÎTÀ
+;¯oµ‘ß´sç0ö»Ì4\ó„ÑižÔ&™'Å(ÿÐÍX÷ÃÑ;5hÛ÷é\ç.µôt“sê´Ó>ÏŽËóæú¡¼GK¥}cŒÓ´G¾㠔ö6Û9Âî*Q Ÿ¦¬cªV‡|„ØÄ>‘{At3ß |'jág¾‹©F{ïî›Aå}±ŠÊ`ûy©ÃÊÌ:½ŒZAZ,JpÖöÒvÞ+^ÛÃ{ï}Æ{ý8'úi‘ø)Úøi,Ú5Kè„Œ î[Eľðl#bvÚðx-²O€&Ùþ8.}!ûã-ÂñžÀ˜†Ÿ6Ê÷Ä ýH/¡2äP‹'D-F rÎO'1ÆSèW¶ ß4y_¤»‘_õ8›êqæŒÿ
+sHkÇzƹh!ø¨nÖCða•\ûa±uœ?ZÍâ1âæ÷ÄA
+‹y”cTQ#t:ÎIÌ{
+¹‰*Ä
+ô¯§¯‹ÍT­u ö~KcÅ=ØkôÓ¿‹8IEÿ0® å-ªÐJ‘[µ(lžâvrŽN³Œ!ò(Mö‹´ÕËfµ
+[§qä•/àŒ
+~’hèC”€¾ß‚©ŠDj¶ß•S¡û¹Ý×kWdõúÑ•ËÀi«ÿPp/Êï üGðYpÚ¿‹~ÏYõW7C~xò äû€ ÊßûÁó`úbð{dÄwè¯ÿýq£Œ7Ë6Ø9ÜÞíþ†¸avösvk8û?ëö·ÄH¶ü€o¦7ñî‹Ä~û|Ú7ŽÃØÏáXü‹õªmê:ÃçœëØ× Á…$%Ž¯Ä¦Ä@‚ !_{Z]g) ‘t‚Ê ‘¦ .ÓІZbÛ˜Ô 4mÓ*Ô{ÍœP)™²ukV ZÓh¡´Ûõ T¥à=çØ@Ý|ý¼ç}î9ÇÇçž÷½†©IÔ”³xÍkY^?‹ú1£Åû›¨c1.!s²ó1óú•×μ~…æýÿÀ˜#æ³óêóÊäûÏVúy°Å½ œÛl~êr“gêMÔš?ç¹ç5
+d%Ðt
+È;
+‹3†½ÀŸd‰ß^ $Ù?.Ÿr2XÅ.`ì:¿@\@3Р쌰.ºH4à(pÐ#îÁ›#àbãÀÛÀER¨@3 ³wâ&ÉÎǽ J°€c$…XÔ³ìOB¿ÍÞúÏìB¿í„goÆ
+ æ!NpÚ
+]‰xû]¢Ü®¤‚66‚åQ +
+‹“…þ9%u§¢zWc¹¸ð®X ¢ßÕïeª÷øOárá=r Þï¿ ‹ ï·ÀâÂûÂ^X\x·ï„Å…·­Þ¦XIöÊoËç+5M»¨+ha½X¥^¬R/V©—X/¿ÈmŸÛÏâX±ªoA…¢ Sí ª­§Ú)ªuQmÕP­žj›©æ£šƒjNª©T;C—c)4ªþæ·V-¢Ú8ÕNS-F5/Õ<T+§š‹Ö¨I掯]*TX¨D?WÐ+Wù-˜£+êƶvã±<¤„§‚ä*M“Ÿvr]š¨¤ýÅ+ü»ƒkØnÃß0F>
+¾´YÉ.pˆAËœ¢Ã5IºC]h:nj55™¾bò›šÜ&ÅTb*6Í‘í²Už-Ï’seY6Ê™ÉDž“L]U}Ý£•+þòA‰AØVÆ%„8רÌȳDJŠ°È†ÑG·‘ÈV—þù†²$Í]צç”5PÝ!‘–}¹/’4¥Öë5¾ˆnjþFë
+]$&8±B×ýœq8àhd\pÆ 4ÎÑW ŠÃŠÓ!(tqŠƒÎ”M÷(•Ê¡)Ê!1’DïqiNþÕ,'ÿ*8¾ÿöÓÕàóÑD]t[{¸«,ÜYî:õ—öî(Òµ­.×À¶(¸tÉÛ¹uÛ®·téѲ®¾­,ä¨kD¸‡ëÊB¤=ÜÒ:Юv…âuj]¸lK(šhl®®y`¬CScU7?¢³fÞY5«±æánäcÕð±jøXj£‹ˆ=ÞÜ: “†èêö´N°¼\ì×Îbw´¡Àºg•Ø¼uî¢}ÅÃ(H~Eò|Q}VYƒžðТࢠá™â¡Ùh¶dBEûêÜŨÖ3!+šme Ä×Ýë!Eáo†Òß>hêîá ž–¾Øã>ˆ…uuK(ÖMHD¯ØÑëÚZL&´vòŸ¤¯È¶åå…“©Ñtãb4®à’4Eämõ¼ÍlÎþÿ{2z5
+4v&AU'í&±¨¤;#- GAK~k{[ë0Ê%žbQüÀõÑX¶1m’¶ ÿ½Yt÷d¬Ì:tgtú.ÜË.ÇÔ‡¯?§p^åàBr1ⶹmœiäŽK½£æ—aL²YJ°ÞœaóHÏÊþ[‰ROuN2uK-õ.¨Î3æšr2ENŽ1ï³,K#&¹>×bÖÌÌŒuQçæ[ªÍW¨d¨gTÍ·UÓ§g½øË"ß׬Ÿùꟛ¬·Núž¯Ÿ¬'z+®Ézj³×Ör,©¢>ßSÒ²¥s¥¥BõŸ]tyÉÙ*)A ¯_¿ûqZòÃ÷;wױΜw‰•¬Tsç[p2ÛÿCzµÇFqœñyîëöîöÎwçóÁ×ÎƆøqÆ`8×Ñ€JzÈ¡<b—#I&¥1PAÕÆnˆ¦Ò m¨A<l*Û¸âQ«UE\ÒT‘¶@S Pq„äP°×ýfϨ-ê­º·;ß·³s3ßüæ÷}ߌ¬FNt ]¤å“wy!jP“Rºß÷ãMáRc =4120|]†Kã8ñUM®žœdø Œ/nûmª±·emQma).µŸèÅw±çæÇC÷?hظýØ/ì|Ûü·ñ—Xz1)6ˆªùUa¶‹bp¨Xäé¾Õid(w;½^G¹Üév;Ê Ë«idž×“ï!žýþÅÒ=dgN!òUÅá—冂j¬ÆÔ­kémL±ŸÀ—ðŸ{»·olüðþÐÇ7íÏm¬|w‘gÉ
+X÷ V^i¢$…S„àBD"¼ 䱦7ÄÚ\NWQY*SQŽVÂ`“
+‚’ñ¸ëèQuP?ô2Î
+“$ÒHò)ô<z D¬ ¾·±Ý?rìNƒ½ÐE–®§¿¿ßI’ß’À‰¢¯u#:|áH †t _°Ì@Í›º‹„S˄ЈJ‘F¯!r ðÛƒ³ŽuÐsÒ¸“1²Xlà”¦¿gô LJKƒ8ñ¾VûÉ<~ã^@ð(‹×³¸Ãô)–É8’d•HIF“Xb`yªCD¬ànűúNz%ô\ý;4tX˜#¸O7L‚6ô÷¾“Á¨ }¦1±Ák´ôÍ´™Ê%ª~Œ4B%%Gˆ%õàzð¸z+ˆÚq»ÉHDaI,|Q^ÐèÀ”ÌÌ6P^Y$•+1²ƒçÂÈ°ÇAŒƒMôô M iÙ‹wtØ}öÉô°Íd3#Œ`*s",
+A4i€!#^ösìñ»aQ?ïQî»%¡Ü±t]š§ê¢äNYf”Ë”åêÓÆk´Õø ÿ•t¸e¸Þ€ç“zc¹ëq[¿í¾íQ™ÎÜÌC]šÊÓÝE’etEÒeà•ˆH^]'ó)ëøD(uAQGM¦à_jŒs%&Q©‹4Y*RôëÁ„ô`ì«\–_7љΩggØEF[f°w´\õú ù¢N[u¬‹wÃ+Ÿ‘ÉKr³Lä­Þ³ç²¬Éƒî0@É32®KF2u—“FnÁÓRàé†GÂŽÌƸšš F_Ÿ§¯oÏJ ñã‡\YbY:™—*rÏð-v§ÀÕ€W­Lg#y!0½Ðœ/’dJ¿#Oþ©}èíÝçñßÞš1ft‚÷Ü›{í/“F¼½û;o¼.¢ývð ë°R>8”àœnÄ`Mfº\Ò<ÆfÎ/\Z¸Z}E•¾y‘7©«]/ó—]RQH¥á¢’X(ªª9þXIÉøñht4¸åÇb>¤„ã’.â™Ô5|ÕJˆ€&ùE4“$¼¤ˆÞ%g­¥€à4w\\-þ¡k¢.x­ôÈ„hÌt|ÃßaMœé(¢-(÷:EÎ*’PnYšè¥K§-þ”H$•ÙÎK*sg$ý‰1”¿¦ 2O²¦Ì' v˜ŽÓÐEÂWP
+’,J)Ä•“««'UÅã…#+'‰dõí$¾÷ôê¥Ë^ݼ ùä&{+®m™2ëñ?Øiÿ¯XŸÞ8uî¶MöÞÓнdÑ»‰¢Þæe‡Ÿ® s|¡¥©¯<?þ~›¬OynÆœµ—Ê×@TŒ¢ß]LžÜ5|­S`ó»f=%4UºƒÇ¿mF¯D[ÑÞNßqwÓN÷¯Ý ËÑÛQŸÇõE£´D*ö•Œ6ógºççç-çÏE¿ëÝ¿ƒ¾åÙ1z/þ)ÙëûÈ“ƒ(bŒøHq ÙzbqáE˜Ê‰étTŒ©FÜ; ÅMŒq$?7n*XÑ…5J^lñÂlO§Dü‚òŽ÷u_n6q§Ó+ÐR¼
+çJ¬pÌX
+žwøÒé‰m?Üc¶å˜}}c¯ˆï;!ö4£½€Ý«ÌÌÇÓ•,;}FÌ‹0YÅù– êJÕ£Ô°SãPÏ I‘ü¨ñ_Sï‹Ô»û€z±‡©7¢§ÿI¹Šòék­j:J†“%‡³%“ò‘0‘\øF¥`(Ê QiÍ-À~aeti¾Øýž%pµà´`h.ä|0@€Ÿã
+*«³-VîÄooü~à «g¯ÛÒÿª}×ly§â±Ô›ßš}À~Ÿ÷£_ý†}¦ï=ÛÞ÷LåêŠÇ®¿{õ‹’ÌzD†k€£ m³‚)Š,#Êšs!Eìˆþ*y.ej¦›h7SÿwÕ§}=K ÐRŽÃ¦Sw.—>짰‡ð Fž=lìàNZ:ø}…÷°ëöÛî‹ŽCÑs èý£˜ HÄ`sÇ”Ú*G&ª²rbyVÏÊÂqYee8âH«ÄmT™¼•䔚`6£6t±2d¡ztÝBÜoBe+¢Ns—3áð7
+מÄÎ2±3¿d©þ‚*jŠ@‡^ãµð®ŒH&dÔ‘':ü!QÁªÍƒ1Ç«UÔ¼¼Z vSšªhœ2fr-À9¼)¦,A–4 qÂ0‘]
+R4J\°#é"S-o9Çmü?Á/qÆg)¢ÎU.c2ê!™Ê]d½¥»Ìÿ•Xÿ`¼Z`£¸®è¼7Ÿ}³ó_¯½_Û»öÚ¬½{Á ±âI“Ò
+-8-;äµE½'úà×ùÁø î`¼Æ½
+Ú3%«ÖË9°ìþ<G­ƒ¸µÄ\ ·º„ Ió—¡*€U®‚aÕB.« »’v1̧
+L¥AMÁ÷Õl|G Þq¿¥Ùðr7N•fr»<“)&­ðŽù
+†òd]ˆ ôÊ€gqâx7øMÎÒ!±êñ ?HPÊáç·Îe¢r»¡.2…»¹—óÓ1áí¨ÅÂ4Á¤­úný<„R_ª/µÄ&©Áh1׊륭Æ6sÁ4*3×Xh®¤ËŇ[a|É ¤‡Äl@|% „¨ešm2…j§L7Œ6™dú£Ö£Ä9͘Ô€MÓÆuê õ‡hh„™RN±a2ß êj0åé;5¢À$M¢Á: "\µ ­Í6±‡iÏ™”Ü'÷Ë@±t`й83Z{¢Ð…<ã:püÎÁXT7„Ážõf†«ï=;¸ø†˜š»"û7‚>3 9xŒÌ®±—ÿZ‡kY^ýÆÌífÏBãá;Ci×lI»Æ0ÀN×lïäðô\8;·T®½ Ò¡FÍ{!ýIUda'I;õ©'ÎA’!ëÛªb È"Ÿ-ö¼V\+LÞzááU/‹SŸ/‘Þš\ ½7‰Åx˜¾*E%;N„4l1ÁŠÊ<‹êUàgP¦1°9©
+_½£Ýâ½ÂÒíÊL®Þ;m¡£]pòÄ»ð8ÙK¾E–üòTqèw—Š#çIõÕ?“Äö^øcñ*½@6‘Ÿœ+þüÝ¿zú<Y÷Û⊗Hž$‰öbño³£Ý`Ž„¨PG¦¼tH3Ihar]í“lS-˜Ι| ð1Ȩ̈làLèe •AhxæƒÁP<ûñÁº9y«çäíÒÞ*íáúµÁêFÿ:|ß.íñº·@ƒ¹,¹,õ5íÉMɧÔmævë™à^ë%ã˜5l}hþݲçSŽv˱t5” éxUP 9¶¡ËQU­ŠÄc5,¢>Òu(¤…hÔ²LVÓhVpQ± ”rA)¨ÈêpŠ‚3V
+©ÌæLFÌÔE¿°;—*QùŸ•X’èŽÖöÅviéccQ4)H•%Ńk]n+´^n9/'ƒ6ÂÄÈÍþ%åê™g¹–½Ø -Æ‚![8WšPwñ˜ë@e†`3½¤kƒÀ±ëja»SjÈíUU•a%
+70Ä ;¸³
+'“¿çÉÿå9Áö¤¨5=f?&(¼.ÒÔx3¾öÈHX<p<“ç`‚GÀ»Xg€øƒ2åªÊ Óõž9ÇíÆÿr]ýÁM[w\O²¤Ä²,É–%Ë‘£Ä¿°KHl‡„3D@( ~(Ãù ¡œ ]{„£0lÍv×^WÖ‘Ê(¥K(´…Ùàø£ƒKzîšÂàÖd£¬)\²âdï=‡‘ÛÅŽŸÅúñù~~„ƇÚCt%ò»l¾ö1^”V⸒ï¯ìª$Û*A¥Š®m
+úF5èö•Î1Ý i0U Éرèڰ⺱ÚÚÐÅ08]3v¬¼":9SZñ$¹À9†Ñ%<ˆ
+åc…y&Üß\³/\5é“vŽŸ†Î6PFaàÄÒ Pq#ÒA†ñûBñXyùüAÅó1lÑd2
+µPU\.YQý!Šaí$\FËÐATbå™Æö³Ó_œ_ß»D«÷lßæípoèÙ»çX½˜«úÎêêò —–½°níoCÞ 5ïïš½c¶lç= uÃS“žM»ÓûjÍe3Ç5ßû~פ
+p£X‹g•ÌHý`ΤÁ‰Þ '%N‘ð‚ó
+G<¹F^zÀžƒõÉ—ï/À…
+9¥wWè«ÎZ¿³_¾«þSëÇÌS #ìAt­õ î²ãÈ
+¥èB«Ø&’¢hÉÓ­,ÑICûXͺ às!;^wBö¨&,R×qˆå³µ’A‹;Ÿ[46ÖÁÞcÀ­“ÁP }šSÍÀP¢b€5cc‘*…”Ž‘ÊLÇ)/ˆÔhML†geúf‹épx0¶qÍ@‡ìÃäJ&2é§.Ä
+ÒM)–­£2[¨ ¼@a›(õ\gäÛ3_Ýòõ«Àݶ~¸kÅþL/9×V±pïËï…ê¡SÀ€boÅCúXÐÞ¹¼¾{ÚÚ#PEœÂúsB¼™/çA+ÑÆk¦¶I;`û5ÿŸãá‹ù­K³hèy{Œ˜7‡§l‚n.2,;-CXÊ@vš5h!(ò—
+«Ç,(\0f»¢pŘT¤%Ò[t»ð[ÿÝ"IU×iòÄ©bÝÉb' `ýC>ÒBt=°ž&_1Ëh]¬Õ>ÝfU\Ñ`Ôt»{T ª¦šR[TK>r²!‚eMŲ¦þOÖT,kª‚ÿÑÈÊ:ŠAÛYYSQ(˜‰†^Ý,€ á3ç„ná¦0,X ¡J˜3Fð lú6AGß$`m°¶ Z8²¹É[xö(y»? þŸÂeúa³èCüéCŸ Ü‚ )©ª¢fdd ™Õ95•dB£Änu;W6mó+{Üv°µãË{>ûÙÙ—Ž¬ú²íw~uä•—~ðRóÑÅž¹Á²•K&tì‰o°ÿÍ–Gÿîn~ŸûY×¹Kç/žG ö'A݆®%ƒeg¾KQ¨¶àx´Ä©jª“·à]U-¦æH6I¦`Ëtš•9«-˜û_¶«=6ŠãÏìÎ>f÷î¼÷Ú[ûlß™ó™6×T€;vÝúÒB Ø4$¾’"hËÕ å‘"(iRœª*rQ#šª-&Bi©1©þ#i¥„(Ð4U›Š—ph%²Z䆇íþ~¿=CËcgvnwîf¾ù¾ß÷êòS’HîRq ƒü]cÄ`Æ“äíd>£ã>$2†H,06~¯´.øùqÜ[Ùî"ù†ü ;æ*=î€;èN¹ÂUbYâkÁß0ëai89™@ª‘ b§ –ú¶Òįf¢ÄÐ[¾d
+ÑR!ËÙo[Q2%Ô =´`¤˜åiT°Å·ƒM<â³3¤‡ŒlH$yÐ^2 fîY¤æ¹zß%ºn<œ Œz<Ü7¼gdûo— oÛ´âG-` ÿu xäçk”Ã}»Vîß=ñ;àä
+}DŒ …‰´8wBø^YyBÌì› }~« eÓÊ&Є㞠$‘…{(ÚÍûwo ìÊXëu*øü–Þ\ta}\…]yaxxX\{÷ÝÛqQwûõ©—'¿Â›iÍö~a±Ð²ÚçD½¶OÓ¦¦B(B‹2´5aÍ6p…¶nT†ËúAÑ `e0kYý6OÙ­v‡­Ú¸¢F\‘íP2¡ `S¦´«)™pQ¶I™„¸m—Gc¿©i›Íjb1dƒ–vgñúEWzYërÌ°ªHÓÌúÂõõ}ŽÙâŸÓ)«3+ÉeÈH2ÿDð"®7¥abP|ßðä7ç4¤†ë>¸D\={öæ®—BKˆ®Ûo,_‡|…³ ~ûb+_+$uß[éOêOIµ,øom\We
+ÏÈ.„&ôFÙPèZ«¬ê6ëõ²n¼¢óŒ^gdÍ&ý!ÙìvŠN}•Ñ)w‹g´—äô?‰¿è£úUã?úM3±,MU…¢ë†”&ÜHÓÌzÌ0tUˆ¬fÅ4ͲàÀ
+“ñÔtÃÆ2Kœàe© ÄF›câ]MšÒC¶¥¢ eJ²"ã­¬˜dz0Ÿ¸Oˆ3BœÑIfR
+ƒë™ŠrÇ:“åýÙ¬’ eûÃ<,(±y”Ö°šÚ”Øb¸È02: +”ÛHÆÀ–Qf¹5“ÞJ)† /ÛŸäIš.93]’¦ƒû aœ.IU2iátIäçd
+„j=òYy.›Ü–MnËžq[6‘ÀžCOà“Û²ÉmÁýMP›Ü–nŒäÐ&Cgsøg·{Hº
+t^Þ˜§ôxÞ 7â OUêã.qs|8ö™÷ÿ —uŸá
+Ï2\¢ÄÄ‘Bä~מpÆ‹½w1Þ vÏ(ü¹jŒ> jðŒ sõ°´LË°TÝ© ë¡$/³"%xí6!BýXâˆû^ÞvîéÃ+køMný•¨;øÚâžå vOlUö}«ûá§'^‡ÂM}$æŠAVÎ7{¸’(0‘HV†”ÜŠ½rú bXå6ýQóI½Óü†¾Á4óNs¤Ù]è-v–E–¹‹½.­K>æ#E÷1¯[ë–ëœîH·»ÎÛÁãRׂ«ÕǵǭÕÍêzm½µ9`%*…ɈÕ&)û$é˜r(ûŽNitl÷Ÿ:ä°ƒ8P‡
+(‚­Íæ眎‘6TcþÐ_RÉσ~¨–B{#Dç
+6iy¥û[Ó†îcÂ’œBháYËàf[ßæÑz¥>’el\àƒ|Œ‹oå\åXçH9©1'5ætBx
+vó‘@*ÍìQ°‹ú°ÄÌ¢ðsŒÃ­÷_ÇœB4¢ï)
+ÞÃHš"ò[¼óÎ}ßž2f¹et|Ûs‹%RÓÄ>ÜÈ!ãbÉÖs¨Ü1© ,ò"3¢xùà¬Î²å³~¸y¹?"¤Þíž?ã½ô¼ÂŠžÑË…Âø×
+ÇijSÒIMBÑT©à®."ï;1»
+’_aìVûçö‡ö%[Òí
+{­}Ã*ìÝ6±Ï°Ú”²¾xaVU?\n`3£‡ L0è| yÙKÌ<ÆÕíåkÊIyY©,ÀcÀ"Üæ†ÀñA…²jwE‡¶ã ækay Œ,naZì
+é¯nÞ²±>¹ïÂ[­³’}cñÖ3mæ1m]ç–®@ &¼ýãýK:/l½ô9~¨ìW=«æ>&kÝÖ2oS¦"7ó³Á'ÚŸhŒ—•ûÔDÝì-ímïüì(Ìibü’ßBúì4RYÆSy
+•Í‚>#¬yTìBƒæt•Q·Ë­1ÃoRÃã²ò}d…ü¼Ü'ï‘Ä”Óù˜|V–%È°J.’5¾Ì’‹~l"à¨^TÐEMÜÏ"iBšU¥ügÒ…‚¸a`õL*£ß¦[€ð#›uuÆE°­¹\Ò‚ü¥êÍ8Ã÷F†dqÓ©'Fèñ™¿XSµ}ûñ'|¹LäÝwŒY«’•;±¼fìõ£ûV… G/1,»)¤ØÝ[O£Ë -µò$ê äuxÚ:¯?Ÿóá„â hØp307YšP] ´ÀN„¸W±¸K±¼
+lCºãl'öìŠÏ©Ì£ ÛpÝIcÉ<
+° ;ºæ¼˜©Î£(ÛèÚ4”¡)µ Õ«óÑ<u ^B–*OÓÕx5éT:éF´o ›”tƒÚûÉ+®WåÊkôwèMú†zTÏ “ò€zW¯¡+êWè õúV­b¯£Q@Í ”Ú¨¶"G¥¢ã äEÖ*ù‰¿;e﯎@";:”QEC!ð—³þ+EÍÍÊVs=ÇrþC¹¡ª)x)ÃN£*+J’ª~JUä"„ ?ÆìAT&Y…,É*u!,ÖhX‹)ŽãÐ>Jè)>áˆ}"YäÐ(qpÌ}÷ÐM#!{´c´#¹ÕRÔFa&Àen¿Xëßz®¿:»¥L€ärÌM5½¨ci%®ó¬†F_Æ[ó—[ÉŠ`î«ÓcÏ ©ÑíÏ®}r=ÙÁšƒu‡„x’u‡W(/vÇiäeÊÑGâD%MxŒËƒš‡Sì&QYdFµâ‰³ƒ%?Á¨"ÓáǪéÂHcjK:ˆG“` 4ALÌI 3ë W‡ŒË¹!è>H00Î-À0„ÙúqV˜¦’ÇÌeæ.ÓeFyýNßä(L&À­¨Ìeåi€ë¯“‰¼ iÔ'…©í$Hnê.Q¼ò¹ür™v—3›”³J®$êåJsÉÜÿ³_­±Q\WøÜ;»óž¹3³c{×^‡µñcɒ؉1fˆÞÆóp6&""ZšpDIPªÚüHLS©U›*ñ²c"…R„(iRZQPi©¨ Må(•ZJ½ÛsÇk;HQȯöÏÌìw3wîÎœûïœ+Ì}iܪÍdsíùÎJ¶ÄY/="?êl·J›å·Å~Öë|.ÞPÒš†´Qk¦Y­SçN&çiù¹Kx]ßCöÒ½Ú[zôŠýæ©È9ñÊÕÈUö±3(þ[Ij"c=(­ 4ƒ’¥S m™j²ˆ¶,ÉÕ«6ù6ΔƒèÕF_þœßÄUÊ@öÝìÕ âÆDU³kÔŒÝY¢®²7ØöK¶j«ä"_Žá…3u6Hkë2ƒøã}ë2?‡£?þÊ|WˆFQ°¤¨¢ª2îQT˶Qß[GÁÁœ¥Å_«23õ+[’S’í8™¨äF£’‰ë\m˜®a˜2nw2ªìâãõ Dr"2³uÓ^ÏA—eIâ®ã0fš º×,ƒ¬6ž4v‚ÑGöøjªM%Õ*Uûèý¾Òf“ö›Ú¼§YQ²:ú$:—€Îµ§‡\‹][¤D‰ƒÙlóüq'ËÆ?õ,«p⧓‚×ÙA¹kÁîæ
+Y¹Ë´NH¦ÕÌÁÛ­Æ-]Þm¤ô}' sÚK`æÏtC=K9ÈQ2¥pt´˜´=NΟ9(Õ“àBÅÒÖ ‹WW/”RÃW¼z[p'êÅTçFµ:sHªç3‚)´øŸF'}®$xÎÎ_:¬¦")à7P6ÈÌUÁlg{&"ÐÁÆ<ü¢ŽB0û—ž+âV0”@Ob%\TÆ µiÍíß7=Ò°ïí7ÞÛ»?×}tß„ó(0o\¶OÓ'†ºÞ{Ÿ®½qvöüç·¨4 ãÐ?Pi,òçB*bD#T©h #Y‘³ºL@J»$ˆ?Ì!¬2á‰<þ,Jx+Øk‘×äÝæÙ±è1ñ˜ôS˜_ì•
+1¥È(µÉTíò=M®sˆtHÚróuÒ¥viGhŸ~J;mþƺ üAùñ'ëŠêŒ8—¦ƒc³¸‰…ÈÕÍä-&5@U)ú^dʯÊüµ¢(H²¢QT¢S>†ñÜ Œ–†I54A·T‘Q¦Z'á¤B­jP\
+85ŒÈÂ1DqNéû
+€ªêšÚ4v'ÂuõwÝ ÐMà š³fÏ™;¯e~ë7,l[´xÉÒöû—=°¼cÅÊUÙ[ÿùÿâˆ@–)<#A]  ¦B3LƒÙÐm°Úa<[óùˆ4ÜQqŽh±&ŸÏøUga ¾ún9B†µ…™0±$…/1ñüï l6 KÓ,œ@-(› (îe3ÅC l »
+endstream endobj 1633 0 obj << /Type /FontDescriptor /Ascent 861 /CapHeight 0 /Descent -263 /Flags 34 /FontBBox [ -147 -372 1168 996 ] /FontName /DOAFCG+Garamond-Bold /ItalicAngle 0 /StemV 133 /FontFile2 1634 0 R >> endobj 1634 0 obj << /Filter /FlateDecode /Length 35776 /Length1 48848 >> stream
+H‰|V pTÕþÎ9÷ÞÍÓl€@ w¹É‚Ù 1áaH²»ä!„ì¸ ‰îæ I)b )PŠ½„±¥<ZŠ3–ÑjI+wixŒ“NÅ)m±¥)¶
+ãh[¬h+Ȉµdûß›GjïÉ·çÿÏÿ8ßÿŸs' @2: °léòÜüÚóÁÍÀ«%´º¤nm¤5<Zœò¬½nãõk'›¾C¶K€äolmZûÊ£yS
+5ø
+21¸>DûÔà ø:¾m8… ¸ÁâYs°)¬}È>ã£ø¾],”’¤‹Ä‹C£Ø<xh—Uw
+T"€X‰Fª°…øï·*x‡p]8‚Äï,sx ânàí&˜Â’ØX¦±9l[ÉÂ,šX3[ÅÖ³'Ø~/°ó<‰OâN^Æ—ój^Ï[øf¾•àù‹ü “¿Ãÿ"òD‘¨OŠ›’Ÿx1«ÖñÔñÔÓêU±\Hýàa‹å#Ô¹õÔ¹6íÔ¿-ØŠíØïb7Ð8@üÒ8‚(Žâ8ÎàuüŠÆïðü—éîüãSü‡ÙX*õ«„•²‡h,fËXˆªha­¬ƒme»Ø÷Ø!v’õ²±~®P-E|.ŸÇçsoâx;ßÆwó}ü0?Fã¿ÄûøG‚‰D1JLª˜*rDµXM£]l¯‰^qEôKÉ/µKÏKG¤cÒ9©Wº*õ˲W.•ËKe]Þ)ÿD>­$+§²OyA¹¤üÓ6Ζe›§Q¥‡¨ÞÏ=Ì&E°EF#»$ÃÃßÀ/Ù{ô{œnªø¯(ƃ¸‚"YÇOÍ[·éM¸…—é­x|Æ£Mb;¥ÊC õ¿àdÓÙXl3«c ø;ݶå4NÐÛ¶nÆKÔsxæÌ)x`öÌùy÷çNÏq»²ï›6Õ™•©Mq¨“'Ý;1cÂøôqcÓÆŒ•jO¹'9)1!>ΦȒà nŸæ«†3lHN­¤$ÇÔµ-DF,„ •–üwújØrSïôôgã]žžOÏ°'³«s17Ç­ú4Õ8çÕÔn¶¢"Hò.¯Rk–¼Ø’%§¥$“âpP„êKoöª «>ÿ±Y÷…½”/š˜P¬7$ä¸MH$1‘$ïµF™³î÷F9â’‰•Q¦y}F©æ5)"Ë©7–U}Þ ‡#”ã6XqVk@[h¤¸,[ÛJ±a³¶QW™å`§u÷èÝvÔ†]IõZ}¤:hˆHÈÜ#Õe,ҼƢÍKÏqw³Wøân†ªà ”Å:¢¥^oÈÜmTqpÇH÷ ¡ûÒW©¦ªë;TãÙŠàH«Ãü …(iŽ»¼2è Öš¯S5˨ ZPR–žK$Í5³Ì‚4Ÿ¹^­ñÚB­Y_¦Ãš ¨Üä8:¡Ìs"ö.Ê|ª^ÔÆü -ñNŒŽ^¹éåRZz§%ǵ§t:zOÊ ”<Rh¶Y’ånJÄz¨ÕÌd¤•Ò1Ô:•˜5ƒg˜? Ðë
+Èž£Ž®¢þ…u{¡yr–]Sõ› ‹ ]»zçJdpEɲ߄)š×eøÊ‘}H6\.#;Û¼)¶b:Zb6ÏÒgå¸7åZ«]5Ê©eX¤ Pa.µÜá0Oyg·µ¤Á]EmÆQxr]!ƒ‡MKÏ%-`Z:†,Ãáa®ó1˜Ÿ7iFœsø/Å>v´¯¹Ð`cÿ¹aÀN¯OJr–¾,èŒè;3œa½3DGã§WQ×ýšê×Ãz¤;ÖQ«©vM–—ë­¾ðPIݱ“;3 gÈ°‡›õÕ˜1ÐòåZyÅŠ êÓÃC¯Äèâ ÈàƒÏ¡a¯ËÃdª\¾}™¾•¢ý½ýoÅ-î#?!ö(ÃHé†@@ô BéÂ¥5܃N~ÁBºèÃB²‹|kx Æ<Nþs„ 3ižJ¸—GX@xà'<L˜Gþê(ÇSCÔÙZP-{bïË-(%Ô(äË]È”ú0W¾€G”>x(¥@ìÛdŸ-§aª-€û”ØúOM~÷›³¼RåŽX¿’†L9»bsÅúå$Pþ·i¿އψsÍÙRöIˆ}LµlPOëÙÄs)ÍKh.á´—@>éNêÃ$âî–<˜L³öΦõi’ åRKì6ÉKy2©ž¼s¬\]±ërWì}ÄŽ=±¬~¸ð¼`i^I|t«îÚkÌš‡j2ù›œ¾ÄÑä7ÄÏ1ˆ$BÊ0·»Ñbñþ€IÄ¥È<Câ&½ŠÎz1ÕÍ™tOŠLÄ!v›úÔGð™çf Äž³zÞ…<óNvX >‹.̤<ù\Ê^L—̾»(¾ÿå¼üƒ¢8Ï8þ¼ïîÝîÊrAPö€D[‰þâ$Y,z6E=…x$ŽXI‹@“Ø ã¨g;Lë$32íÉŒm\ÏNœjnll3Ódd¦Õ¦™±:­jÔØÖ:ÓiêõûìÞ¡ƒþbøìw÷}÷ýõ¼Ïó¼{Ék\±¨óY{5Áž]ÜóY‹þî÷cO§¡(։α¦lÅãjŸÑ6øÆrÞ?Ìí|³Ôà½`)÷§Ü£
+~ð6l“ f«ØÛ¬“èiöc´Y‰öët“L{ÌsLí`ïQ
+ìU
+{ŽS5ðÁKà8Öï&ËMÑr ñ[öYööOööŽاֶ'ÖÀ>Î~–´³k¥`&bµ´€RØd!Ç û-Ïs¬oøûMJÙÇ9†ÿ— ÐO¯—ýë1×–çŸX‘ŠSö¿ÿ¨É9WŽmŽ¯”bÎÏI3qçζ§n^Ó
+V¥˜”? úàg=¨›º<ÄJ-Á¾‡°Æ;v9¿ å2;ÏZØŸ‹X3|ÚÞ»Tlï´O7ô}Xÿ9¬émÛΟðZ9£–q[wšc/}1‘z‰*l›ñ{NÇ^I;ŽÙŠó<Û
+}j“hÛÊnóWÊð¼L¦§ï[Dz‘3¨òDQ81jŸQó±>J¸»"¶Ü˱ÆÝ°Ç÷ âÁ>{ôä3ç}Väûì8Êír¯¥|6©o¡n+ö+N†{÷·gþ åqp.¸_‡þ%q—ÇR×¢m“]^oŸu|.šäå³ÑõÄ]¬ãÀ<Î[<n öz>)Ÿ¯)w#Ùùù›¿;ø{ý|u—8ã\òð¥÷ÑÓî:|WåyNÂç?‚}“ù,u83걶£ãô{ŒvQìpÅ)?ÿ%ڵϞ‹²H½(\˜Ï]ÇeCêþQˆ;u¬Ì£uÿkùÿƒ¼¤ðËo3ò7f°nÒ>¥ÀŸRÔÇ’ô™xï9Ä« |Ö‹Ü~ìr™tA/¸Û=…òÕ©³yæCЇü6›êhÅ0™ÒˆMÎ)NÄe^Ì“V6‰L5†ä”XÄoTåÈL™A›ñ• ] ÍJêĤNHªÇôto6®¶Ým“WO‹›tWÜü‰¤6o›¤3xô)ÓP¸Ë0vÆ𛙊±Æ£_ÍܙٙÉe9JæšÌpf$³_íu÷jòî#h³ñEì嫘m¦‡ Œ­k
+Œ®ð\ÌT1‹;¿dÅNÄde¬>ÖSz×Ï4ÒðÈ°4† LJefÕ³âùA)0AèQ ÙO­ í‘ÒA·k W/h=ŠÖ£x?–FQ?J®±ÒÁd©Š‘FíR˜@Ü<U€%TÂq0®:BSJÇJi—6H¾\h~ׄ}ç•®˜ðì:‡¸•åTeHŒ, `/U4T© ô(n4U©7ù^Œ€«R¹=t·Âîä.
+¸Ó"ÉcI”a¹À  Dâ Í~‹K%U"Ôƒ H‰SFÐ_•%XT‚ZÀ{à¸>  ‹„9«s‚,½l^n¸ÜzYmØ'Þïþu·ìÞglh™g,4´‰9¢ŽI‘ª<qŸzA •ŽáŸÝßÇz*…ŸêA PàU5óŒàá˜èŒl1Ö€`CäBD"•‘÷"Êî–c=hy¹Ä8¿>ÏXx+<¤“‰x÷F÷ʾ½#{åœëŸ o›d~¹¶±7<ÏØ úóŒ ð`XÜMÒÒ¥àì,ݬ󉽱º¹NGv9²Ó‘—ÙáÈ‹ŽDÙäH¾#SyÂÜ
+ý=¸
+®€À/ÀÏÁÏÀ9Àï¾ ~Á›`
+5V[ KBð•V°$dyÖ7âP3J-ùÆ Æ&líÏ·²—4 #c-Ø0ŸuëþƒÍÍä+yü/wìN„^Á‰.6ŸÒŒ×´’’Ð*<Fù1ʹ"Ö@ƒ¡´N£ÑÏÃ?ñokÇުݾªC6Ô©ºyIØÑS2= SoÍ/l®öyÛÛëXT˜Û“ÉâJ/i¶2Š«­ €«fUͪâ*7\5řɪܞE…ù§Å;É*/Š³`Jjí%'Ípm«Õ_{Àê/®qæÓ±'53Ôó³]ÖÑÞÑŽ'ê{¨ëìúöøGÝFûÊo(þ7kØQ;ÿ‘¥Ôn³üÀ<Ðjù‹k, ÷EÉç¢âQ²gOG•<´,B™ø‡,–†T­ÀÅfý”T!øŒâÇYP=ËOäÂUgˆ®{¯Se%®sJçdf=U˜U¨Ò~%þ~@þüj}ö$îˆc®¹”CEô¼9'Íð¥Ééiei8N'
+ÙwØŸŽ1¤ÌêÒ´i]~_©Ïôóð¹.ø„Ï÷dqVvy`÷†ÛåÛûÊÝYå\ªüÇÊòò9¥´{·pkÊôÓÌ.Á²ys}9“5Å­ÌOÞ»‹‹¦‹99Áò‚4—ª.(Í׃!o‰÷ƒ Ê5Ö?k®¯I!:>‰,.ЦU<ãrÿ“ïªmêºâ÷¾÷ü‘gû½gû=?Ä_ñNŒ?qlƒc?CBHHpc'8%FøJŒÂX»¦RЦ´VP:¶l´¦µ0iÀÒmhÑÊ$¤j-¬ÿTkÿت´«&¥ûUêV›ç„®•¦êê~¼sϵϻçw~ç¼­›:®Ôn=WûIÚȶËHÎ÷ÈßBu§EÔ{³a?s’3H¾"ú8uE¯ÓéF<îÒãwôX¯·YWÌ›Íu£¹ûÿ[£H¶šEq{H0}Åì¯=R|·y8×¹ÙZ½çÛre3qO&2ƒ£QÏÞÚ4~jóºìà#m¡GeÏ•¼«@ŠqdCNôžtP­Ñ²§R8½^I5Ú<Ob«Å$„Šãë•<ÈIž?,<È0¥P7Ðg£•¶]c!¯ B0 ,£Ó:ìÚ|E‚Óó‚JIÜ9§wé£zR¯hfÿQÒ‹<A’‚`“Ä~‘E·K¾€J6\5 o]ðÞÙ¬Üa+»$fê*0ŸR„[žànŸ
+›ë_Ž·aª·cQ£Û˜ åž<¤[p“£Ü=É„1ˆa£„¥òlŸ˜-Ýz¯»koœ+ÕÏ=rëýîÚø×Ýø|7ùW|¶vXî³µŸÍÖŽâgå>‹Gk/ã@þÖyòIøË¢¨}(Y 5ƾ¶^*m³¥Ó…NðÆ=\˜â4X3+RX;¬‹ÇâÑÎa4Ѳ:§jŘ۰©×Çõ{ú
+mšŽiòí$‡æñf‰·Ï)•ÉøP,2훌É2m>:Äy&ófó¦¹æy¹ÁMBnÏÏãf‰•’ýÉÉCÉé¤ân'V÷+ÕûÕL„[â¹ÏäaQŸˆ,qƒ +7¸Xùrõ™ ,P$_ÉWe-î>œ”µáâ3ƒ(ÇPÅgô—jÍ&;æE“ÜêÁÆ@)ðNì @ R²Ä²n"î Œ­É5 ¼É˜™¬(â×ÎôòÓs3"‹·a‚5)KÑ&WH4E6øB×Xì7M± _y#âó%›zÖ“¸k`ôBï‰Nâ;ê†öËß}ÝH’}ئ1Ó4Ar[²=þDó11¬p¾pÑÚáßq´kæž•TÜÂ`^^Ít*ÞQØT¥µm¥j“ê;=}S‚Ÿ
++#“AËáe:SAó4 R&ÁäĦDn>
+þ¸¯Ê]%u²ZˆÇ²ZHCY±DH
+ÿû*Æ$¹˜õ•Ä¤èmº9·í™ˆ·‰M¯¾´ÑzѤ[Îölèš 9š –/>Îz˲šûÆ7©AŒVüSi¡>Aƒh:Tü-t &Ñl±Ú“­)ÍW×é¡ñ½ûö88Ô_.pïP:}¬ó½½x(4†° 6z¼Éôþ Mp¼Ð»îXô@Ðï/°AgÇ
+…±uP J Ý ÝÇúÆÕƒjPêêËô5DCíé¤ËÛè{Ç÷SÌ0‚ð¹kÊåj÷z< ëÅ^o;ãÜ…w}ˆ(Ž"(jª82Ç0íì”sŠ˜z @tWÒºŠÑ¢Tì/ž+*Š
+­– !Ž%J ‚ª_–xõNÄ»ø(ßÏSˆ—x‚ç-æ•]’i*8Cf&Q Þ¢7ˆ k?dñ‚Óº-¸©#3ÆwŽ ÖݵO_º„ÍŠ°ÍVi;ôËZõwçkÿj9rvÜjb?þö[Çeì–-4ƒ…#`¡m”tbIIJ$+ 'Ûf§w"Á%DRvׄ5-œ¨#hGô•Jµº(ç°#!ѤL²qp3 ¡
+䃌Ïz3«­º}ݧfí‘™”ÖëŽ8=JCëÓnê?ŒÀå7ý೑ Ï?óù'¥Z·¾~S??úÁS&÷™ÓM`éz|ƒü…"òè„ŸŽ“¹j‰gH¯ÒÍZÔNFDÀú¢3âNf⬅2§sjµ”Üó8t3Y2—Ó%–’EYBÈ…v Chý
+-  ÀZpç‹÷«Ð—ós½
+t ¯®ý‹ü{ŸÖ”& xw r·ñW a¾‡ï"¢.Óú³ñƒ²ÀI —k«‘kÜûßPç}ú˜¼Î5ƒsH¢aÍ7bž4¿~Ëò¡…k=c
+‘Lœ$D§‚
+Vqè–ŸòÃQó"Aûå<ºªùCô®À«©3@g 1W+”Yh[¢Ó™Òe‰Þ,ë=Æ
+a£D#.ÚHè=*M61rZ„Çjý-³}›¶Wn¾õŽ'#Gg&‡Õ˜øû[ZµV“øÕBmdwÖ{þåκ&^«s½¾öÕ7ÿläR ÞÞ|ü×?šÿþÑŽØác·ûνûâ¯z=Ÿ?.¦Låü×
+ÿñ=Ô‹¿×:{z˜îrO/‹üÎѦÑ(b GXŽe<#Ñ?Ãù-§Äš-Ä€¬í]ùîL<›KF›bDmÈg´\Á‹¡.ÐPuÁ^EØ[QF?—°ÜnÅáªÎ°WkAª¨†Ô˜ª©ÇÕ³ê¼zS}¨~­>Q-th
+
+Ù¸kåÒþÕJ¶¶ÆDÞdŽ@ø)°ˆÛbv›Í¶€·´íC¥*å“„R¶`±ÙªKAIÇÓiƒófjZŠȪ‚¹ÌÕ# ýIkT>Î0NÆÅlIfSö„Éʉ@4b]]*ÞÞîËšæ8 —Qg±õ³ø\bŸ\-¢Á·ˆ'4«Â,¨} éhŠ{B0eän‡(Ê6öP 45u„ñ°ª›B¸ã˘¬Éò¸|\>+ÏËWå›ò}™e,ë€X¡º „\rC•AJ MÀqùÕ•%ªïòº'sô§û·õ¯Þ*=AÆõ$ÉŠQN\¨åÒŤCÖSöœ|ód< ]³é“‰‰ˆTg,9¼rÝ@´ê¥_jï]8·¯»ù¨V˜ðFªzHï+¹ùG§oÇÓ¿;vçÈÎŒ‡ŠÇ 7BSÆ0¥… ×fŠÛ~RÒâåÍ#oÿáR.½õÈÓŸ2,Sß%†X¶Ê(Æ^†<ž.‘³‡4zGãkÜØl¶3AÜ^ºJáTÊŒþK¨™°]sÙ[Ïí\PðÄc¶°òl€ßv¹¾ˆã8e³æ›÷1W}÷} 䦬™ÉWdF„€3ßéÕéÊê4ÄwÚN°A;ë.·²œT“pÔõ^µ  ŒÊQåºÑVh×› ……î•èTÜ_ë¼ÓÑ°oË©ó±ÊîÂ7Þûñiiîúþµ{·Ærí­Q-ã¯Ù$¥O”fË)f<ê>7”îÔÆ>ONFüoì»°ëϧ}9ßÐÝÜðB=ïÜúñ±Ùó”yãÀ¼ƒ^@ñ[Úp‘3«L¼Ù"X­…ʾѽ#Ã{vïÚ9d~þ|plìºw)J¼±)žn­JŽ,ÊQ…”B\u˜`¿UDÉ)8‰TD瀮]Ÿ˜‡ƒf
+•æPhŽÌAÓ³k}KI`÷Íé¶ölGž4®ÊTdyc¼Xúvô¸·—«¥Æ¦áÑ1Î GR/ó|ÎØÖ†Û3™B{;Îuvj=ÚTaûTÏ"~´ 
+Ø'w­ž))¼
+œ[´@‹J+P”©¹
+·$â5DQú5ÕM¥Ò©ËÿüÛs??ÔþÃí-g‡XŽ³sD»b~É÷ð!¾ÅüB_3qñ„™Ð»n,]¹»y¡wæ•i¨»~Ü™D“¾AïþõàáêWÇÏ\ Ú4&[ûO{Þ;‰yqº¡iÚoÊ&½†ÕZ N‹4†¸°Êá°zuf4Ý&åV¯$x½I«uHÄ¥öfsÖ‹¶PŠ!(—ÞÕÒž ¨,¢ ™ w"–wë ›€ Ü&< @ AµèÍV§C­“ToÙ_¶äAÞëšÝšyÍiÍ-´@<Ï/å‰<¶56¼aµÈâè¾ï!öCt#PAÃ\}ã¬;s ‹2ùxÁÞrž²61úA׌ÑÐïÃŽÆ9Ç+i‡9ÄÓHÔ6•Nx Pùæ“é ÞJ›$ÃÀ‰íº–!Mˆ³õ1œÞNúL}ïFôºqOñ7ÓÙ€ÁŠö<ªÆx°ÂkiúáÀkîžT0ªuç&ÁÅò"+ç ô1ülÚc½4A¸†Dí£gÒbåPè
+Ëh%
+R*nÑ®UUÄ âvÆœ²“NƒÿyÄI;­-ø&Pã¯Ý]«]chÝ/òJYµ8U*åk}®°Lú¨§7”.«©‚ÆêXìÙõVŸ=™Þâoã‰Ï½áÑâÆ~ÂÑ «£9g")LìH!ÒiÓ¢Sï­þ
+Þ ÷¡SG@¿¬ãBÖ/·¹ö»¦]¤kåÆv‘T13Àü’
+ŒÊz®âvIJƒt8¤¨Bè÷Vå$ n
+tpIåí1>ùlA'E''Óu)qW<%w¦^ÉÂP9V1ê»Ã*çêqS¦R0À²g†{æ%s HïAkŸ‰øGj´sEš#jHTž$õšæd2mç¸-ß/ôÿr*wÜ•h'SŒˆ<{ÌR7©s€Ûe„% 3 €ÌLÐ5A"/ÄcË4dèe7 Š4BêÍË“ÜhDKc…ÿ #€d³®æP6#ĈZB Ǯ֟Úc^˜‚b¤n>~ŸÚôd‘œš…×_…×ß©¾Y}ÝìI8DÝ$?Bi§™'/
+ªÖ…5ô «’šo›9ô8ImÂÿïñûäG窿¾SËÙû¿ka X¦
+Õ‚"™&P!²† H7BiÌŒ«ùÅðP„©¨|;9õd‘:WM½ZM½÷ÃØÝ{×WIH'A
+Zaë
+l•;uñœî!„Aáæ7¥&(iÁ©¿44„ãÜ7v…‰ðL<n»à¾â&Üî4ksÙÎÛ.Ø®Øh›4†=„É¿N_¯Ö"víž² óŽ)Ë_ :¿O‚ÉtÆŸÆe­ q €Bõ~ÁïÃ^[Œá¡YKB_öð™\v¶Ïw¶=×9¼ósh8qÁ—·l-¶ŽÂ™{ Ήl™ÉåR}Õ¯®íÿ]¿IËhœ—†;óÃ#…ü Öïƒÿ2]nÁM\g?çì}%K+i%y­Ø²uñZBÒÚ’mI lÙ¶XclGx+É2$(6¾B( „.m¦ iÓ)éL/)i ¡qÈ´åi;L!3}Èd†i“:i=“LÚ‡b÷ìÚ´Õ®¤£½èa¿ÿ÷ÿ~†ÚGÅ@Pa0{‹2Qf‰•¬gL_1Ó¦4lÓ¹Ö¸ª ¨j”åOª¬©Ñ$ŠSQUŒFU–¦g⪫´)7 R|¿gI¥q}³D“
+Èâ+³ˆèL‡g[Ù˜oxÜ•WcÙ¬ÎæÝÕª‘ÛÒýÜñ'©L'‹>)¬&ü½†2&“¶54,Ëî%=еº pÕÉÝú›„—ÒŠÚ»¡a
+
+Òÿs{ý´Œ]ºIlõuÈÑ:ÔT{{©©!ÊýbøOôÛƒ"oߦPP}k`ع˜Ž³<Õ2¼|ÊÙë°¸{¸.ˆº> ©}ðÖ'a©a¢ÉÑœµ6†lè
+CDôVêÅ¢3Si,Œæ W 7 ”P€~§ gçP¥Cë¬×œh¬2CÛ»µ/lÎá¦Ç
+þ´ºí4iB L¿jÙÄð®äÆ!1æ»>Ý›ïŒmìy’GèSzúìļɽîéïðl:VžX?’䎞¿/`.¯|N…©8ˆ³}¿@¿&¿)“<ÃúG€ë–¼¢ï©»é®­DÄŠ/B|èƒ>Pn/I’’yAf¤›qU‚Ò"¼uíý ½ ÿ\®*fPÜ”:y–»…µ:ÌeZë®2ÔÅo<9Úö_HÒ뢧´6‚Q‡
+ivh_„ç²AΚö¦§Ó'Òdú|ˆ ±‰ØLŒŒ—9½[Ù·d-r¥§‡,k»µ,‰Š‹°å·OlVÉdI‰=<ܬ`
+ƒEÎ’ÒÏÞ8NX$¯ëËj±}›çp½Kˆ—ÝvŽaëØÃÃ6[œNk{ßJ¶†_Ï<Ó›(Ú,ÖGl!/<2ûúì1Êj˜nFÀŠÍ¬¬W(8 ë²­vWM0AÄd5Ël/Œ¥Q[ÑñÌÞ"*‚XÅeõŽ¢ÑK¥¼rBå.etqƺ*¥’kÈ‚<#òR£æÐ\hZÚ«ýƒCÛ ÄH.‘ß7EíŸèÔ ¦5Éñ$eª!ÍúäµÛ™CÓ¾1«ßë?á¿â'ýþÁŽäþC ;Qây»‘.K67=¨ß̃a¤2òþ9òü%`±_ÄÞô¦Ë7¶‰óŽã÷Üÿ³}>Ûw¾³ØNŒç¿c;¶qp°%$Ð?lɶI!‚B#Da5a„(% ¡t´DëˆeÕ²Ö"$Zuh¥«öbÕ&Tm/6ÞD›ö‚m/²jª³ç9;”œ}Š}çØyîãÏ÷û˯!çØb(xQèÂʲŠÂ –.›n)eCSÐS«0‰×žæaë«kùUÄäs=RNcĨžÊá–(¬X°CµˆE‚У8†8‚ % ‘ñ„ì³1Kn*_v6/ˆÊ¥ŠщƒGJ“¦~ò˜o/@7xªOv“/…ˇ£¢¹¸Ij3§ÿY˜ÃRý¯MA+ ´ß*™hký– Õ¨ÑÎÃÑ;O×îMò;EÉfÊXMJK4²^ΨT´a†VcFjòU„è½âø"ée¶uQoª­§ÿqàê
+è5›IÝI™ ]Mçc…âèà¡
+¤¾ú²ô °WaØ?…EÛ²º¾ªk úæ[ ôèG¹†tSVS)¯ò@oWÏ‚[%ÛsyèJ÷O¬U¿°r¢_ZXÆÌz-CwR¯@?m+ðÃ#2eflLƒç%o[vÇÐûÅ‘Ïg·u ÛïÓÜ 뫬þÉS0ÖŠ¥i¨,þõá…ßunþNä#`Y”šÎ<&ºdÀ<›ƒîâãâŸ/¿yÇÊ:,Um£G‚gpÀ¶M›4‹â‹pÙŒfƒ‡b}‡O)» ®rgCýæÅüÌôõùcwŽPíðêd0Ž~6ïß‚eü–G ÙÛxpyáæÅH¶ñÕì`vßµìåÛÈÓÙÙ/9²Õ7Àp[ÂvCH]ÃÂÒäÒ“%âÉXZú´–”Z$ ãamŸ6ŒÏj—µ‹èñ%L»¯}ÊbÀ«3Ǧ'^;{úøØ™¾×ç{{nôì{›X¸¦^iŽD?¸ý«{LË/éåŽT{Û
+OMfœ^šu·Ü­¤ÒGŽÌ¼®ŽMOÏ&n¨ gÏN^),LLL
+*P ýêñ\ïB®77¦Nbªû}u…Uªy‡šVjÛÝÂJ[n%gÖ¥Ây'Arò+wA/;ŸY¾^}
+ë9,ò(ÛÖô|CzâéÚjÚŲ‘|¨ ¡Ç©R*zâ•òNß+/ìŸy> À?¾ZÚC2OžX/aJ‡²¼ûIO Y. jb@(£Xk°D× $&dˆX´dˆ£
+¥:ã;ß´ÒÉÐ’“¤7É
+þ§,2ê
+³“w…*~–-þoö‹Và)Àx$£p7áÏæŠü3Ø¡^ÂNe !o¶Ú­µdka[2.²:ÐO>êðãÂÐ\ßØÙÜêrT±ŸÀ
+ÎûkGj,´æÕjU¡ëa¸À=ÐBpqQ^+MXºòXe–n$¸æë‘p‹RnÇí µ\`¡bp9‘6¡UzÑpåP=‘ô¯5Ü$TWôY6Ö
+O9\뵋B¢¼@S@>ápÑ<uðráÐi§ÙZ»”˜P$¦jêÚB{nìê|¥;Òò¬Sd¾=[âáJ‘Û²Œylµ¿Üß¾K¤ŒðÔAÞDŠ‰¯0
+i÷4\¯ ßzg˜¿zm.šŒ'Ì—lœQ0H'É, ŒÄ‚ Ÿ%úúFû——¬jÌ.a…K9&ªÆÆWFõj.‘£(RèW»žtáp𺕑ûû…ñôøññ[ãwÆ©«ãOÆñ[ã`ü>x/Ã}9úïQÜ;
+F€‡ !ù)ì¾'òë'Oäa«9‘‡™ô S¥€B¿é÷ÿL—}lçÇï¹÷³Ïç;¿Ïï/Ä!±IB †”»@;K iIHn±ƒHà42jBJQÜјJëjZ%ªIõFûÛ4¦‚FÛ?Ʋ¬hë2)«4VS·öi´Ú¤‘îyî’¬gßcë¹ßé$ûûý}¾?cŒ2dz J­ JZϹб´Ê-…µhÃÑÊÊúû‘øDnF§Q–Òó Éóââ|ƒ7IÁO 7c’©éBƒ§‘z¡·ÃÌ ÉTk(ùéГ^›ÀÖ-.ã»z<cF¬fðÚ¾c­©‡‹‘”q´nt$q¦v­' šF÷ð Ì¿A#Ä¢ªÏÝçâe`ßÀí5Fr<óê‹G{êB¼M$õyÖ†£ä”;»3j+×ìî°»©H1ðÏ–N m=Ûß?}5äôG9Ûòì% ‘:pò„³~Ôý=×p“ÈŠ™Ú󜇽¸J’
+GDèÈZŒ¤ìT›Àþ­ÅO\øþÀµ÷¨®.,Guå}Y¬ûØÐ8>œr ‡²(áµv:Ü÷q>¡‡ô(>®çõÃì©¡òPe¨:tcˆêÊý¹­õ]iõp‡Uà9ŠõH?FøŠ{ÛQ[ìÀ8¸2X\UŒ«€©àÑÍŒ¯$ì@a¦¾”þ
+ˆET‡•j¶ÙÄDÛ–m{•Í«‰•[„ºžò½™IÀž&X`eë¡Jê0Œ©¥ìeüÛÚÊqP…o¹ê­*U_Õ_ TƒÕP5\T£ÖjìZüŽ|Ç{G¹ã[ô/XßÄ’¼ä]R–|Ëþå
+l/pô(´0R¾xd~C1M¢¢ÀƒòŒsÈ_Æ¥y¡!É"PóÂùE¸áM
+
+=7}Cš5569ùÓx
+þ"©"8 Ë¼âÊ<ªDG›q`&–’;±ã(V˜6Æ“~
+ÂÀé‘C¸I'ôX&mšÍp%c¸-kœ!`¦HÄšù?/ Æ|m¤Q¸Wk.øwŒIuÄù˜‡·Ùñè©€ˆ×4ƒ‹£A’jž²þ²Ú#qQ°mNì™v 9¿Å½­»8ÇZ˜NGZë$ÂFXpༀÿþ¡7.;$¼®£ÉC¾Dÿá2»/ÄŠû2Þ]1'Gò
+gmÚÄOº}„
+ÊbP[ihm;åi]ìm´¼F5A@bâöMú_´@ØQìXŸML˜¶8ŸçŸ œá_¼ø9õ3å%­jNX?N’5e†ʬ2ššKÁø’^–5@sÁ½t©ä¸Atè¿œ_E=°Šê»šYÅ$A”¸æ¶4Q¨ zÂå–öSæ àÆAÌt'åJňÇðdÂ3(&¢žCï_zø*ˆï®®¼:Ú×Pî8±Ö™à«c¹Q"Go0:;œ»^Ø¥¦IÆ÷üöƒÏ-ÄO~í÷+©<¨Vè¬Tzq$²o>;Ü´îâã|GŸû°3³5lùâ'…%Názùu¬õ(APçP[G|˜[7 ÌÞzÒÍ[ -Éûš&ÛÜeÅqëiëYë²õªõëß­ aÖ‹CRQE¶ÓŤ"3Z…|S ÷6FIs8àïIç»Èþ,
+znu÷<f»˜Ì|–„‚ó[Úâ2²9¡‰G¡
+£Ÿ'ähâáÄ#§'?Ö šf‚Œgæ8’Q<†:£Š‘,IvÔ(Æï©÷øq+üÏrp_˜,Òs<SïA7
+Z
+oŠ¸œÑÑ©Ó²¼R7ò§}¥Öå«I M§Ô
+dpUšŽmy%dªI…*·¶zLƒA«DÝN†„ù~ߦ_ÍhMиöžA©77Y䀒±2jx><›™³ûy–æŽìˆ¬A"½ýOò*G˜AfO¨ªét­ÙœnIÿ¦®ŠåYkf-”ÏØ•…fŽ»‘ùtZ¢^¬ISºK·ˆÛÓô^ñùô™ô;â…4­mâ¯Å¿¥)ŸØ&v‰}iª& Ä´™äZý~­›%j ÈÔ1G&ž$(‚èÔF/»™ËÚr'ÙZv+—×€4'š)[.:'(ç(*yš°ÛyðvÖ– Ž ‚þôñÜÙÜ•Iäô¹]èt9GçðìèÕYš_M˜nHÕÒÌ ù¤<@„)ÒXß-%
+ s•îqgš.-R
+lk5Íþ»ž‚îøÑÚö4CÖ°Úƒ)þ¡OAK‡–¥˜°Š
+WG äx¡ûÓÇ«¡Ä±øàÒt÷*øÃ^ØYÁÎßvþM¼ ÉcùD6Z¿1Ä¡©T"½µsIҾ¾®®‰‡ÌÆ35mð’?‘Jwõ2ˆ¹œ†¼M U¹ú¥^iB3FEM}[‚ŒH©4V;ÄÚÎwõÌ}ðCAjADf#[>¤¹ˆ`E8 ‚gPž°fº×—*§zÕà¹ëí=ï†úGdFòK¼¿˜Hó‰Dz©A¬5ÀkJ­OVa¬U¯a[´Åé(• ’4LžB”ƒ«z3Ö
+Sf³jâÑÝÂR˜J…Ðàw™±„ÙBÄ´ðIyþ€¿ ·Õ9L¨?0pÚïÞ{®Œ0HÎsÀÒ`'qûÛƒg+Á%§Å\õâE{­H„é:gˆæ*LÎî³Ã[ø
+q¢T':öÆŽ¿zö°²l\^1®,6çèeJ™î‡.$[º”ZÁ*¨„•Ðô/ …àIÁKÇzÐEãÌÉy¬˜C?ÆŽŸÄLô & × ÷ 8LƒC…<›èHw,’r<S'“Ø#¢ð „ëšZ£W—WL A…›äPš1Û¸$‚@0ؘÈ6lÁ]ìeÓß°@
+lÚë©6ÂLšŒ%­ íæ]é¨ú€³¨jh鹩¶¤u„Ós³ºª}ÐÒ”FBjãÔ-ªi· ÂfR¬JKWÕQµ±ÂË-‚ŽC t‹ÚM÷i\¤iN}H[‹ÀUm˜@iºŠêº(“@øJ³¥H£Ô_Õþ‰²ð9ú£0‚¡+è`Óe
+5pöMÒ6Ø)[¦o×”Óɇûp_û€pãz›­Rr8Œl³»³‰ÅÚ•@1ÿFÎåðÙQ sÍK¸<c£^}· Å*j­e}ý¨£¶†5ñÃ?ÿÙ1šñ§Ü6^Ÿ5žØ~Ðâ48 øãø?
+9^çNbaK~}Ê*œ}›‡Åo¯½¿öË5ì[×Ö6ðÙMäÊ]dÞ:ÎÏÍnÚ÷®\Ù¼kÝÓ²Õ±º¹™¿[7†½a4<ç‡WeW,Ö{3ÿfþ^Ëϯ~Ö ¬½s½hïÖÝÆg…Y'Ÿ=-<ÑÓ©>vyB÷&KÂ‚ï º&¡ ­OžªŽõ(AuÖ×5º,YWIoÏëkÉòÿ+pP=2ÀÃgý“þ“x¤šÙÅ tÁpA:4³RI…¡V•P)·B™E êœþfvÔÌbšàÍ
+uz¨ŠFTÍ1ÐíZ#:öªæaVwN/(^àÐì.ÂÛA$Úö•C]­ÒíTµÕV&èÙ6ám?qù›VeÉ£Œ`]òaSl±žÁ1ž¡¼‘ &ã! ÛIš'À×pì–Ëof£±_÷U¹Ob–R f‡!ù“G{š,PbκžsÂj]ù™®ú˜6Î;ü~܇ïŸïü‰mŒ?Á˜3Ø8c0Á1˜ˆGb£ ²¤$ °Ò±©A)¢kµý³Ei¦e²þ±¯¿R5ÍDºJË–ý7m•¶µÓ*mÕÒŠmjÙ&iª2Ì~g‡$z}¯_ß{º“ïy~ÏïyìØÜô¬ÂŸ¨½â[ªzÆ[Õöƒ™ ­û™¾\¸/$%óîñã˜aà±1 Ö˜ƒ<Q©ÉjºtôëÊÛƒAQP$°\˜·‹¬W0EfWDg­½Š)Ž#ÅÊË^›Ãα°&‚eâ±Úìfž—Á9ðô ï½To“h²ªa®•ÜOÄm‚
+ªåºÆGãΑW¦tMöÈûÎ6GX‹ÄÚŒžýbÙ9¨Ï¯â¡Ü[Ja4>zp&Î(=JFÉ+ƒŒ_uy•VjQ9/¡Qw4î¾°ºb_u¯®Ä£ÑS£ûhÔ=Z°¦×ÑUûÉÂèØÑ…•UóT×Lfjf6?™ÎyšL‹äÜôìôœiö•—
+$ÞžÌØÁ™Ùty4st¼°ZÔ…%¸Å¹ +Ü2áVטy48˜ŸÏgfòå“*V‡Ðüúòñ¥b131¿¼T^žÈlàäíñüå<Éo`ý͉§–6p:gFëõÜ:}cýî:äSÈjPT›BZuÞ*?ÙäÔ'Êåa4³föl<ä7ã³ ^~K­^÷²©]c’ÒZ¿jz+—)ôggú¡õƒµ£êã,Äézr
+—(‰rOBþ»‹x?ª1p{{ì,@²] %«d³5~ M&¶Œ½Ž¤MvÖÂ;ýØèd.¨[»ëqÕ65w§£éh·­Ú—Ò0=èÁs–»e7SÌðàÙàaÜ8ßmghå0,©Ñ `b%: á˜"Å…‘O^¯ÿþýCWg.5Š³ÛlÙlVHˆ?ýãgŸé$›s-žVö(›ñÀZ©|B·Ù+ˆ¢xÎŒ¥ç¦cŒ6ð{?!Æó¾‰[ÆÉþ/ûˆT¶N^ùyÿ‡ì•Ï¦jj­tãO!Ïõ¡·s©:Sœ£çCÁ`H$¡:§¯Þãr7©ÚÛ:ººÖƘÚiœ³«N¿KÕÛ:6ðæ[½\)"ƒ;ÜD68TÔs/qdœó ?Ì>”À›9kÜ㛌‡JñnF0Š|!€ÒÄNÖÐme/\#[5T亮è%e5bÃ#»¯w
+çôîz—N 4ŽþÓš„naXx¾ë¶È~¥û^3É!v!0û8Kl6v1v3ÆÄb¾ì¿ÿš•B ,ä
+Laƒ9ÙWNñf í Œž3c9—;6¬Œ¹¡ÙoæâîIý·ÃØ?NÓá§E+2€Lñgú»ú®NzQ¿ Kf@ÇúþW.ÐrÚ µ74rR[о¡}O»«±G´Ëp†Z´„F´ÄÒ"¤Ë%#å&Œc«¼¨l}¬d÷èU£Øl ÖâöŽq• ŽN¦‹å&p¾`jáæhuîî2DZ´Qô PUU6RfØ`¢±×™j? ù´d›Œ½ê6»»èw] …õã½}¥°öÚâÙ9ßþ‚´ÿ3´§RÇl2ä5ËYÈÿ'ºÚbÛHËèü3ã'öŒoã©ã[ìØãÄqƱãË$NÆÍ¥Nã¤Îºµ›¤qšÊmµnÒôÂRmRò@·ËòÐ ‹«ŠvA- Xm@*»‚Fì‚ OA¨ð€ª} ¬x0­PµÒJ¤áÿÇÞ Û£oä±þsÎwÎ;o©•µ™‹QwXX½çÕå†ËÖïÏHIËï8Ú ›Ý_šš>šï S®“!ézŸ'ä†zh«—v¶ J×À9Á5xéû÷¯x»½”üÜ:íˆE2¾Hâˆk÷îsÒ¯‹bIl{+®ØŸ:¡‡Z’uìÑéc-©‰A‰•|á‰5 =Xõ±fSci‚b:%+÷D"áäºl(³é
+[‘¹1ï>Vo…’.LöÉweâCù¡Œß”ïÉ›2±fNr—;*ôF¤z(ê¯TQPjN*‚'aNð/ÑÄ¢ŠQ«´•Ní5z èrê ý°¥@‘›ûQ4ï‰x«N$û{!ŠTwÙw½ÇlPqî˜a¾=¬­¯„D©Çwy*~eåNÞÖÇÄu€Àiœ=¦Á¾z¶gÖ0·ÓÌè•|Ï7È|myÜÍv½zû¦/@G(6~vihV¹1Ö·è²–½»db\˜tòaüïË°t¾bï¼Ú9]è:‚±©ô~1÷Bq÷ñ=b‹`‡sñ·˜¿2ø?™/;ð§Ø?0œÄ‚â oÕ\£-°`vmÓ0û†¿gaÇ팿ÆuXÿE£
++¦Y«Š(p òˆN¬
+W5ÙÛ Ù7Œ'âè¸Ð}H£o“™è4‰Y^ï0µoÝ=ùé@4šùÖŸ®~ðv:Ö5î°¸\Ò⻿º¨„÷EØ!تwòö‘ñä{©ý÷oWê©äP*×k·½ÿŸüÞÈxô »íþ‹œ×e°
+VÇþ“;Å/ƒ§g©Î=(b!]S
+sùñ¹µ™Z¤gX¥>£.©çÕ •|¨nª[*¡ªã$-€qÖŸTÉ¡¤/{c¢«?¸°¨‡h;A—·£#ìîŽEÃå¤é*íú-4œ$FÃëA,9è^¬¬ ]Mbgø3ø™Zò–· Ôr±|¼|·L–ÑÁ㿨^h@n7Û-+ÚÎþ߃´”Öª {1AÛȇªÕ†¶2,J+Ê)ªèÔA@rB¦BÏÑ(˜°4©k·9ìšA¡A»h£à¶½`±ôàŽAÔ†#B“¼¤q=áú¾c‡3çNB sr‡5{Ô_óÐ:&|5P6€¡
+U©/t~·ô­ÀÂ6·d8
+ îmvA7_š™,RéÃD’i3RfÞ)¸Äu°n42T²Pœ_/QAÁ1Pn¾T.¨å¡\ÈÌ…
+ÏsÒMiK"rÒg.=?Ïy1Ñ'>_ˆ$‡˜H`↸)n¡û-5k€8Aš©P»´Ä¸ »“²§Â½ö¤µ«õÆ‚©6ZI2ÛüZû˜Šfåi :„¬æÀ¤Œû¥@3@"8U(ZͺÑzDª…x› ‘{Aº%\šŠ
+iç]fú?«îL4Vð…GmÜqc[lþú²²¡Mom§Ì:
+ñFPÊ Gü3XªäÕ ÕÅê"’síáEF#Ì
+2Š
+rù\؉ÄW
+—Ñ=³º†/«æЂ…m×TºÉÄ”ùGJS“!I‹ͱ¹xò Mîˆ&\3Uø?  †–dÚéÜÚ
+R²\”G@ÇõùØ'µ`Ì™ùä½á¨½/”U’Áv›èá)÷X—Xž|ýõ1 –ýæ@—Þ”œe³æ¶çKÇG/üíÍkÉöNƒ;£ÇËÓ†H¬Hmæðè±wKý‚Ò™8òæV†o7üÇ ;VÓÔüéSzÒbòùÍÖ¯¦#&/>>|ðš”&H“ÜÁ 6´ ’
+Vš×éi$Ñic¨¦ G“;ïåö÷­ªßù3GPEŠkrø1œp ¬ÙÄvê­Ÿ©k°ãÞõ\Ô4îi‚%éæÍŠb€¹ æ{ FãA*‚5a}Ø-5’0ûëûµ3üÂ\,© Z†Ç²ˆh°£=ìtÙü~Lð¢ pbp„0v’¦ÃÉbB(šm(X$g¢.™Ü='èßr‚-ç§T¨ÛýPçÖ ïì©œu¯Ñ¨ÇÏÖ=#.%gábw<{ñ”ë–L„`W†ÐÑzno¨§ÝN‰0Í`
+vîFÀm*Š™°$vUÇÚMuá‘…ážXÀœMͦ>JÅé•-R¸-€5y½Š-†[ZhEAÉ,£J¨†R)ÖT^ Ãþz+àCEëBE{ Ÿ‹,ÖövͨسôDe+#’¢‚!`>«­ N± ¥Ö¡
+-G—>øÉäðU0c>
+\’Q ¬~®þU™P*JÕWû[õ³ê¿*L4°9òƒ‘GHS@ä'š¦ÍyŠ¯ÎWq¡b«âÇ+££¹jµRB¯5x¢ŽVÕZk0xQ­9TtÖõ¨\©ÖN™z0B›&VêE$œÇ‹y=]Cõ^-©õÔ
+ÚaâèÙ ÚEm½‡^ç=Øa>ØÑ™ìRsÓË!¯ÅK…ÑùóÕÕ DÝÛì¯yNð”hs’ìElÃáØˆÇ qþáüßç?ŸÿÏ<%΃ùùüùÅ¥Öbóïaýð{ ¦é?Ô6æ|«[êþz²NÖKïä·òŸä?Íÿ3Oayÿr£±TbP[_…º» Ò²]6,jï1@j[Û€Íý묲Ñfý ­7YâÐí¼–µÀWËŽœB„¼6,ÀÌûms *_­TÖs£Ž\nô
+wEÍ𒆡_a-ØWØý쀞4¬¼Êå²Î=øeO÷Z{BA]µ`îêä´#2’ÁQ}GEXM¡ÅLjY ½Ç©†v@…:׳÷
+”å˜Éi1IBÒlÅwPƒÒðÍ7[δeœf†ä$3ÃK€bqÅÛÜv{â[n·kúÇÇÅæfŠµ@À‹¬Èˆp ¼2pøþ¹ÚÍ$MÂm£LA Õl‚Oƒ¸Dű4vYÍã‘zÜZ—¹±¸–ÖR|¶w¶÷£ÞÏ{Éx¤³ÍëòÓ‚U"x»÷µ¦‹˜¿ØÊé¶b+]d;­%¬±ÅcA¾²hª20O!`ËÏ k1\»?ñBn„Ý~
+‰ò,)ÓVx©A„Nݱ`Sel $[eÉñ\£uC‚Ýr¶C&„@‡K"ðÙ÷bÍZ`n
+IÃ>;ºÔÞ_ŸJ‰$+xæ€gìã'‡Cånby.»€ÝŸ~3(ÓöÝñ·\Q›@à¬$ð6’‚ÿ À"Cß%.bß¿TóeZ¹;õáÔŸ¦(?1:»réõËÜúY¬;­j§¯2ø s]|$å7Ú¼þFŸ6¤ ×Ni'ÞÐÞÖn,=[¿±¾áäoðíŒSº[Õœ)=ÁWo¿{ë6ÑÈD†ÔQb";{hA+S‹þÂÚ©• Äêòë—¯^#7×+nNºDÈüÛϘÍÌÍH$–ɤcs(êøÂ+¯­W+YEâãœH7&Jwb jìa çĘŒAáQc«þÍ䦺9·Yݤ6¯eÿbrQ]œ[¬.R‹‚»(7Jr‰Y.VNƒÓèf>qUYÅWKœˆÉ;P)
+>‘mlsä§ÖB'íÀ[àDr
+Ú¼‹¾›PËu· Kr¿íN Þ®É|:;Ž&J•*:V.Î/ÌùZ›dÍ‹ZÄŸ¶Û1VNOPÒÔçÅ’R.Í—j@Ƕ|%"m½[]4V†™ßÎ/”[Ê
+ý,2ãä&‹5ïËÃ<%×{$Qø/”OoèÞþ;¤IÜ'nº[Ã=9Ø5Á0“«f¤öGP7'µÕ{ÐÀ>€u2éê'¸>[v­V˜ZI¡¸o• ZÌz`vR/0Ãè>f‰\ú Ô,š¡¾„P)¸I*û¬’ Ûë}’•#[•Y‡ghŽÑ>ST«·#˜®Ì±‹,6|H?IE.µ
+2ï zûÛî<^þ§|ÏǶV±§*>‡¶ÉDzjmö5Ãæå:‰žFÏ ×ѯOßn“}sFê;cÚI®UÆ&{6{–6…Zt9 Ès7Ú"¤µ‘ÕP1ÅÀKÌG4•-å²íƒ<ÿÚï«]Ó‘¡í“ïïÍI¬æÁGÆWœ–Í¥éOüÓàWóÏÎ]øôåï^Ú^i‰µïu^ÀC1_¬ºüØ+7®•¤<¸¿'²>ô
+Sdž–Üc¯¶üqºVçÎD;Àáõ¼¶nÉWŠ• ˇöE‰Á HÇ,3t蘦cŠŽI|ZrMg.4‹ÕåÄò@öð(;œˆGzb«së.c Û‚Ly¤3[’¦I–žäðVBDeËcz®^J”’륵²Tc¥dY´.û–áòràŠÉ–§ô{÷Éó#2¾û_MÌ
+Ô îÜ1yBògò‘#z©ßÚ­#>òÿ¯Ý;Q“2f5ï¤+AqÂ
+òØC
+È^÷s£Ó\æ>—¨i[ d§c¼NÀˆÚÊ`ièÐBÉ#Gúç–OÏðJ·«ÛiK\º6?ç:Lš yŽÕdÉ­—€=åºðûÞ1<ÅÅækË„{—½¬(°m'‡>?^EhO+¬+Þ)ð"¸›ˆ¸{±Çèžu¾.n«ëe…ÖæüHo቗¾<(ã€Î7(º0vƒoQ6Ye9^e»â€ \„¿TùnUm]NÒÅÂѯmÌCßû‘@X4³‡Ø*zÉ3'˜[¹ø«Ið³ À”»'mîõÆPs
+%á€H„KŸ„¿ˆ„*êâá;ÁžΉ7DØ¿{@ˆ¶D¤r(ò¡C!<ò•VÀ®c+±„þ)6”¨zé¶ê·<y ±Ã"€.È1­ k¯hÄ’¶¬]×îjÄ hÚE°­‚§7¯Eäo¾’i´^è•'Xý1 ¨X»‚-£c‡°%ìcŽcðZ[Á öpEL©6sS©GOæž4úõ†»Ñ@á“զŠõ5ZR¬66 Ý!uÎ5ªqk%50²±`ؽ{^¢(\¶D{-‹¶ß—ùd<ª ¬õqPøÔçlÃh$Q`Ü‘æZEÄ0šVþÒ‚Z /;´%Ûæ-Xé´Ð6Íûº\qo _éJJï;õ’ÐîV¾Xœ^NP"lçMkÕ†öê]~ß±¯å*¥o‰Õž~K–cY褀iÚ’l¼ù°î‰ˆ\‚±ºûC?舶·»Þ´Ò ƒ[:Ê¥n%áíòÙ¥gnäÕ×L¤•(~ä‚¡ÕþÍÏð—á± ö½B;æhï#8I vÇ 1ä ‹I9Y‘*‰%n ü#ÀBA±QÞ…0gs¡£VV`G KöA=Ý1Á®!ªPà£_Û?´É@žŒ!ªü*½xGºˆ]"!8lll<«ˆF‚v5#Ú¶±1ÐLüv’hä­
+§ÔJçlea—zi<{"ûJv)Kd³ù*Ïå.Õ€ƒ>š Øñ£8†VÈ brÜà°cm,ÎNŽê¾¼ïú]»ƒ&tr{Ð#Ül6ôì2¼Ùñ\‘)´aŒÁ™ÉÊâQ0jœôj‹ËFŸyd”£‘ ¼[EçºYdZW`ër ,ÂF%ºüN§Aƒ ¤3^Õ0`¤g#·ÐlÌ@*c :¨5kIÒ•È “Fm _LfZu-: šiºIöÔ3È7gGçhÐ#"÷œµ‹ì*™ŽF±
+w÷)’€^÷s¢ ;sÜ aÊuöb70»^üÊÞ#ç;œ4HX‹P°‘Æ/žLþàtmw8bOpù±&µ"¢• !á0™EÇGNœÿÅh_-¹!î#i+Nºuüu“s,3œÐJÂ/='õq¹§ g‚&Êfeó“ÍOàoá.ì
+©;©áOZMÌÏ,Á±.œ”(ú‡Zµù¹áØó²n÷¥ò¶ÍâÞÃ
+_MÉU"^%ÝåV B|ÛAò.MÏdÕéÉ*³¦Hšª M= sùÿ’3f£•_j.ËÔ¦œ§kLÛ¸NŠj‚´·½Ì±4%qšK”(–m)7°[GP‹»|™Mè€8”‹ìÎz)bwÕ×IŒ’íýþܺŒÃÓ”eÚšj:îl.ú…óÜÎû4·ˆ”d ˱æ$*š¡L¼[>péÄœp÷O>ÉÊk²…ål<ÃÙ)†âYâYÁÂX8[wæËÇ–?îÏ«VÙ̇Ýuù´7­9ÞÚJ¾õ÷mv3mÑtNíŽ>L¿EøK©³©#Q¤Ø€µ=
+…Óé"›è à‹DNäÁ™Ïƒbs©ºÍñ˜Ü@q‘d(OòÑl2 !Ç.ú’’áÆͶ?»©ª­fʹ¬¦<39[ÒÜl–O$}n/¨®ˆ©ÕlShGžCº{§äKÞp÷Þ8{=w+GŸÉa*¸Ha_ŽÜÏ‘\ú¹c˜¨ñ(íÚ*­e0.ÊÅE¤.}ç—W#e*Já3ò³ÉqñÔ=# B ÀMÒxðyƒ[œAªaUR:Ó/Íl£ðTÓ¸>×Må¨MÙÁ—š75ù7‹+%ÙTÑË^ÈPi†\ª»ü¦3í¹^r¸ûm–o2Y±¸—'—Þ ÚÞûî
+2"¬{”8$>r.Îï=T0@#±’º™C%öÑOФÓˆ‚gJVë\‹VÒ(MË´)†‡{ŒjkIo¡{ññÃ%£‘á+¤¢«õIÕ­:±ht#Ö`ôØÈÓ§»¹&˜]_ßåŽìȪþ=Åá ïQ»œŠfz¶KP:Ëã=;Ïö]fB&¡ÑÁ™Ý±H*ÙÑ<‘xûØÕc/ ÐªßaÛ´«- ©ÁÔ`v÷•Ã£7æôuØL}‡~• €
+°¿d½)ßLQàÖÒô¨)ú¨ù÷H¢Âs-åéb÷»~,°sv=nQÆÚ
+¨l:Ú…´ú"ò’¼¤·Ð£•µ×HÆñ„@ÃhQ aˆ5ÂhxR»¥Èçz×ï°ú6Ô×çœÞÑ®f';#² ‘û¢£#Ö(+õ¬Ó=Z¾øLeKFÜÔÙæomMæü1O8YØuuæèB.j³§>œN·DT5`gí-þÜ®«G+o—Z” VÝOƒ`ù
+ ¿`ïpÌ
+°a £ `ömÄûÚ¼
+ ‹ˆŠ‰KHJIËÈÊÉ+(*1¨¨ª©khjiëèê雘š™[XZYÛØÚÙ;8:1¸¸º¹{xzyûøúù‡„†…GDFEÇÄâ²rËV†M„F-ÀÂ
+ÊfªÈ€²Y€ì P6½ÃÅßÑÍÙ]Û=±(17?/E×)?'…X1 ÿݜܴ8‘¡ˆsòòR‚RÒJrÀâøÕê28Y9 „Ì$]8Ô˜¦1ü
+endstream endobj 1635 0 obj << /Type /FontDescriptor /Ascent 891 /CapHeight 0 /Descent -216 /Flags 34 /FontBBox [ -568 -307 2000 1007 ] /FontName /DOAHLD+TimesNewRomanPSMT /ItalicAngle 0 /StemV 0 /FontFile2 1636 0 R >> endobj 1636 0 obj << /Filter /FlateDecode /Length 8293 /Length1 22292 >> stream
+H‰\U tWþö9ÿoDã-I½rãJ7‚J*¹âýŽVåÆ# ×£ŠÑ¢ñZâU+õX ­1šŽÞèPÔLÔTQo£«Ôc†fT-³pÏ|ÉLgµs¿õßµÏ9ûœý}öù@æCcà€! r2GÆ׳·ÿØBŸ¿îh{50„mI;s†ëGÏå»8Lðçþùæ\!äw€£}nÁì ;2›Œ’’è—7Þ7îä«M‹¸ýÑ)õ~¨÷¨u‘íy…3f•÷ ?Æö3 Q\Á”±>8F]²ÙöúfùäÎNÎ/ ¿k²¯püóõ)¡•\®ÊôäÍßÐSUãþiãý7“JÈ¥U  v}{%`÷E¿&ºsƒßM~w‚}Ì3{ÜÁ|s]×çìßÿ÷b°ï£*¥=¡}ð!^Á@” NâÔÂl9 n¤a;b$
+
+éˆp ¯aná:≫RëxáG8º˜»üÏÄR³^¡HÅNì—‚ÚÊ#my•©@âÌ s‘­M¸%-L92h}ºh‰yxõcæYU‘ƒR™+wl[‰Ö23 ]±ç$“V?̶/ÖØÎÚ*Ra®™Ûø“%Ï•ÞÁR2Þ…
+ÕV§ÚÀ…X¼Œþðqô7¸$õ¥½N1-MO³½¥x¨Ú¨#ÚImÐc°[˜ó¸‰Ÿ¤¦t”MRFœ–vÕéfâ Ìa]mböJñ1öI{i¯"T³VƱUØÆøŸâ”dJ–TÈA½Ínìa˜†æ¶1h‘dø>2Æ#iGFÐÍõ «™5Ãîð|w8q
+§Éã*óþžHkâ†z[Í3#Ìvs‹\B…$ Â(LÁL¼‰ßòTáKüSžªô<i¶çØ•f s‹žä>€ÞC¸v1Oiöç¹Ëºââ.’¤¿ –\Y%ke¯\’KÊ¡¢ÕTuOôq}ÅêdÛ&™+…£ãº1y<·™í5ÜïvÆQi(±ÏçüǪ«J#¶ª“êª^¤WYÏìÅÁëÁ¿Ÿšep²Êz1oà#fá '‡V’/Óå;2_­þ ké:Ú­;êWôP¥—êýWýµ5Í*³.Û½mŸ]æô'O›L³¹8È«%<HDgÖÏVÓ$òóÓ0 ° +Y/kðʸï/pçð-þÁ€D“óDF/dÕ-’•ÄùXÊa9*7äqTs"NuR=TªJW¹jQ¢N©óêŽn¢Çêyz>±YïÑ—,X–eìD†]l—:Ž;ãœÎœ¯žÝÞúyÖó«A_ ®  Þ6ÃÍlòA<Ú’é²ÜÀÜF|ÄJ܃#ø
+ª¹>%6+>RܬO­‡ô’ÞD?D #FÈ(Â'9’GÌ“ùòŽÉBY!ïUc=÷¶MvÈâ3ÙOœ“kò½Ü“‡ŠE¬4«9FµT ª wšªz©j0‘«¦~5MÍä •ªOÕ>u^××1:^ûôT½AïÔ‡ôYý/KY+Áêf ·r­"ë¤uÚºh=µ£l¯go¶9;ÃùŽõŽOwÏœç@gŽs®ó¬Ó„ÄP­þÂ}ïÆ/ Ž“2Ýn`ÍR×x/"µß^"Ø1‡ª ôJý=A*µK.Ë2=QO2[uºz¢§Èpõ…4×Qv²ž€å0R¦n¨Gê¶ÕP†ª»g½+Ÿ©):U9ª‚Øg¬†V‘}P¬Þ’
+uXé"óG$ۛ嚽Y†Ëº®êãoõµŽ“¾VU1FZ‰öSLdÞwس˜ïîj©´Ög­Í¸¥ÝêG©”µTÒÇj¡^W]¤ŒŠû\šá¾L…_ÞCŠ|.ßÊ^ˆl×¥ÒW½ÀÓ
+¨0éÌGè„Ž–³:YU%V5”ªR Ó§tGªÄ7˜#ZÚ±v~þ1™7 Dµ¤¦y©&g¤"±Žzÿ(x J±í‹v1ël‹ö`0Úa´:ŽdÞ[ÄH,Fìg .E;µsÍ|GÝïGýTØ+ùHšTËr›Ç÷"\5§ŽaÔ'ÔÿcTýLy€7ÅÅ›U8«jd¹å¥2eS‹‰qÍÖF¬qì¶Ï`€D
+±ÇEá-rîÎ{>ÐÊ ò®5ùÜáD¾Q}ù&ÅD³©<»Á¦ÈcŒÙb^C.†˜íÔß™f:a‰¥†Ûm¬DjìQù’ïÑߤ˜ºËÔ£‰Ä=‚ï4ºÛŸc™uÚÙÃ,7çÐùhÎ åð½‰B<`Þ2t^
+öWå&]ûùB]à Sj¢$y¦€Ê{
+ܯ¶ûUÛ««í0ÚÑÑœàòF楹’íòÒgæ-óf§q¹òš¡©îÔñ¡ñ”‡Ö¤Y“V Âí/—ˆîRm¨or¹BHI¹Ó¼ÝiU :Æë8h¤7­qttV¼' ©cÝ9¸{j·©vAju˜À¿Y¯بŽ#<oß»ŸŸøórÇãŒì³1?åçLq®Øw1˜&16æÎuš3˜p›PñÑFÁ(â'hKÚF„P!܆g“´6•Q¡´¢iU”„¶) miˆ´‚H~ýfß½ã|¡…VµüÝìÎììÎÎÎììs×›¹L`ï†öúª†Œ½>Z•
+çué] SíLò…a¬Û`NøæGït1yQ}bW¶Ô¯±‰ëÜ5Œ]óHs"[äßds˜"Oq,¼.lj `-±#™0•X0Àûà=Ù»[£Ç˜“Z0Ðëkõ)L©aÒò­ÁþÒÒè õ•ÆFkBšûõdgCY_ Ë·žœ L-©®êóÚníäåg7Ödd²%‡s«iyƯ
+[¤/A8˜ÕX’б§ü³f«`þ’
+´Ì.œÇ:óú”á«ßÇú¦+äÓÆMÂùëŸ|<šÓ™æ¸C¾›ÄMŽ’L Aî´Ípج¬ä
+aüÓè·€îRÁ_
+|
+T-@
+|ŸÇ`Þñ?v<Ï1ñÉñÁ±!ãñ$c–Ïq#|Ã1fçÌÄS´(ªðQ²3JŒ•ùÂçÈ6s.ðÜ[3…¼ÜŽ{åï“c*‹ê®*¹¶ÌAŽ­,ZÁ±ÏTÊ=Tˆ!šÇ1kûÚ¡Ò†ç#ç„C{8?eŽ€ªÝT̾ãsw¨ã‹ =B!È–¹Þ¥G´Y´R} ñßöã óáŸÃ2¯i? Äž!ªÂYrî¾’C0<ÃÊzÌ7_–kçèI‡ÅTmXq¹z­+®^ñ¼ §Ms¡ Ù2¦ŒlÙËÿ_ λzé)´ÿæ¶,m˜^Â^Éówe&p(øý@Pé +¼ÝÊ€gù77€g´(¾_£4_¢‡µq2ïBà¯ÀÜ5Z7-„žŠ/µÕtÔÝK_P‡qŽXKœ§<?è†LåÆÜçcIR'^ïB9ò*s*býAæUÄú£ÌɈ5bSŠpmàûYÖ’ws¡¯™¸|•ÊÕ›Yñ™§Yñ¹z¾Ü¸Ì¢c™¦kK¾“§Ðϵ†÷/ïÇ6™Oòžƒ¬ßŸK3úÇi@·Þ—÷ð9jwò˜„ ÿEúÁ=Œóæš¹Ïêp?ku¨K­ìó§î] ×­“bºÕ—©©!š¾ËJZÊ~r£²L Ñcéû,ÄõT;†n×ÑbY?ÿB]×åÝ6[ÚËyÈ9Xƒ{o:êø?¬ÛZ=­¾H¤"/™if™æ¥qêŸpç.¥Mêaëwê~yÅÔJªaä0t᳉.Ae®j‚Éùx (óØ~·†øä» }œ•s/óÙ»oS>0Ýu÷QÆ—{ É{ü
+ü]–®ƒEè Ðïdá»i^™M• x—Ú¤¬•>§Å qÚêæw ú=©þçw‚‚j;ê÷[¨ Q×ÂW¿¡„úk´§‚Ø‚·ß&*Ð
+¨K½„q³!Û
+1ØâyóBmØ{ð¿ËÞ!e³wL}Z¾ÍÞ$>ÂÙiÖkyzãéÓB$²!…’>È~`
+y4LK×¹\»]ÌëÊq•¸ W­+­ž5³Æ8+fe¬†EYZb¤Ït,]2VjKµºÛÝ1wŸ»ßÓú´~m@ÔÒr´ÍÐjµuZƒ¶[kÕÚ5W«ÖêPֹܻÝÌëÎq—¸ w­;;h{d/[¿I ½@Ð
+¨Xã(ì9ìI Š¯ÅR< ;$м@?Êà4hÔó žV¬X ¤ðÔë€Ë«zì6¢þ ð
+yXÊýR–I™g¤Wê7+õ_Vê߯ÔóQP
+HŽƒRæîˆ~*¢×DôˆŽÞî#¹DWfH© Iÿ&å£RŒ\ýV®þQ®þA®þJ®¾#Wÿb®h7gWW2¤t I_”²RÊy†›ëoq} ×K¹Ñé1ŠÑÉr)çH™%$ýð”§ÜC\ç臤=Q3\È
+‘DGÌptÛ ¯ ›ác ™áü<½EeH£7ͼë<2ƒ~L+T¡dñ´‚t‚Á›Á?%a
+_êÿ:æ% Zïä ób*%è£ó4ŸçùåT¾RzVYLôFб˱ޱÚñ˜c™c‘c#Ç‘í˜íÈpúœ^gºsªsŠÓéÔœªSqgFbdÀ("8…šW¦
+©Ê²WBÜú
+u*8;±é¬J©z|9ùªHÕªå±Ò¢ª„cä˱%EU1gíWמ¤ôGO@‹)û”¬Z‹ *L{³b¾‡Öž!”ï}>ë?ìW_hSg?ß½Iš´]“Ö?»ÎV’#Ž»¶1ZÝæÅܤ7Î[kSFnÑ™˜Fí6­zsëa ÃÁþ¡CØ;6(c{ðÆL±lé2P{ß:ö0Áþø0-Ù97w­Ž‘mÂ=Éïœóó»çÜ|ßw“/dß8ý¾¦±´9S€ôþ y'ƒŸ£~÷ é'X9â-ÛšŸÛ®.¡r¶–Eî¡Íü0ÉšŸ·ifŒœj›–6_È÷d§¸cÜHJ⎒ѲSì$w,ÕOqvRÕh rG‘2¢U@$ˆ¬bÑvZ4ܦbJ-‹b4Ë^$nŸY‹t°Vk-¶ÀZ}dÆ­µV­µÜ¢á~¨óß_¬˜ß*æo«X+‘Ê‘Rž‰¥¼%‚„rd‹•þb1ŽÔnGƒˆÕ'Â4«c‹œõ5î›Ãy‘#ý—RL>™Uò·†
+©b8• §Šˆœùîè!Áß –‡nQ"hòërû ‡Èæ‹æ­pQ5‡Âj°œ/,‘.P:VËPH dË¥¨^È+ùT8¯j•É±îô½Þ^èÕ=¶D±1*ÖM½&ÓK¤Ó”ž¤^iꕦ^“ʤÕ+ÝŸdé¾lÙ I­{OÍV¸†z|r«CZreàè6ëáØN­¾âüÙj4³1œ4Ÿ@Pª=Ñž >”j°ßN §¶†V_aŸÙ©
+xÚex2o‡eˆ0b#"ƒpÃר¿CÌ!~E¸àMÔçŸ"*áÛùö”0¬RGM¢/U¢]±g/£Í¨ÙÌ`ͦzkVNÄ´âë~<x3¸‚úâ&â'Ä7ãcVq£¶k5t‰áíJ¤t©Ä$tMwI—$ ÐÇ@ªÄÜ÷Àtp*pAÐ ÉŠêt™Av‘ˆßÁ­
+;lŸƒ&xÝöyŒ¿eûtw—l߃þê®ÄŽª40|¸¨÷OôÎéËô <jTØ ì¿= `CtèE}úaÇy8}Ì÷cü ðF?òÕÿ7g×óü2ŒBÎf
+endstream endobj 1637 0 obj << /Type /FontDescriptor /Ascent 861 /CapHeight 0 /Descent -263 /Flags 98 /FontBBox [ -217 -315 1129 993 ] /FontName /DOANLJ+Garamond-Italic /ItalicAngle -15 /StemV 0 /FontFile2 1638 0 R >> endobj 1638 0 obj << /Filter /FlateDecode /Length 28519 /Length1 39628 >> stream
+H‰|V tTÕÝçÞ÷fò…@H˜PÞø’ð™L$…@f&$L.f"Ò™|Hˆ‰Ÿ•
+þ‚ˆÖGB­ íª®ªˆJë EºPRÄZÅÚ–U«‚­à¨Š¸°J^Ï›´µ³ÉpÎ=çÞ»Ï>ç½5
+ëúxu^}[$Ú^˜•lIèõí+5×1‰cïŽÂ%Ѧ¶SW/œWÖ4µ®Z’±áÛÅÀ˜@[\sc¤¡O^#ù¼M¼çòf^2=Î$Ú~vsÛÊ›­¶#íì÷ Zoª=ý"й‹ýEm‘›£Î.åÞŸÍùÚ‘¶Æ¹·}Xt`>ÁèM+VZ_sÝ;ìxtycô½
+ÞÒ™süÃj! Ž†¦|Šqr²gµ>‰ýÿɹï8Fèê÷
+ÄLQ!ªÅ|±P4‰¢«ÿ\œ–er®\-×ÉGå6ù’<¬d(ó•¨ò©Pç© êZõ~õ)G‹s¤Óõýëý迪ÿÕþ­‰V­µÖú™õ¼µ×:ÀJwo8Ï]6Oÿž¥˜‰JžŠkbl…êP%Œ&~—¢ßËø‰XÁjµ³VkbíÂÏãW1lclg•žam^ÃAeÃÇø'ñwù4¾£8îrOV* tÊ ±4ž5šÃlF)£ŒÊéjª¢…TK×ÑbF#J«èº-†;é.Ú@÷Ñ´…ºyF¶1ž ô½L{h/í§tˆu=LÐQúœNÓ7Â)âEš%ÜŒl‘#òÄåü†Ä"±„±L¬ëűEt³Ê‰ÝâˆøT&H—tËqr¼Ì—Sd±¼J¶È;dG ½²O”oÊ#ò3yF’¦¤+#¹J±Ò¤´*?QnU¶ò„Z*©iŒLµ@-W«ÔÇÕ×Ô?8¼ŽiŽ{÷9ž`ìwwœu¦:‡;óçCqq:«ÞÅú_ò¡Mâcaˆ=깚Ÿ£íxWfÉ.åe~ïTÑYq¿2[Üü²Ž¾”}#kÅFáÅûx•uñ²"·«ó„Éœ
+ÄqÜ ÷R;}/¶`-™<ñ»•Y@ÉÅWM›2¹pÒÄ‚|ožgÂøqcss²õËÜÚ˜ÎrÊÌ™>bxZê°”¡C’“âãœU‘‚ç×aÍÌ ›J®>gŽ×öõ/D.Z›/.Í1µp,M»4³„3—üGfÉ@fÉ…LJѮĕÞ<ͯkæAŸ®õRmuí.ŸÒÌ“1{nÌVrcN2;n7ïÐüÍ>ͤ°æ7í͆?ìãózJõÒÆozÙLdË èÑ
+Ì ˜!þâ¸dfeVè>¿Y®ûl
+¦ÌñG̪ê ßçr»CÞ<“Jëõ:úls¨'–‚ÒØ5¦£ÔtƮіÚå`½Ö“·ÇèìMA]Ø“Ô 7DM Ùw ó˜eºÏ,[ýQ†7¯—¶-šñ¥½„ÁPauô”wø|!û¶ÔÒà=§»¤áÏXªÙ®aÜ£™T/ŽºíïPˆõæUÖÝÌZ÷wjv5ÁX|(e0I{Í.s àFÝo¯„[43^Ÿ­7-anÖ(ÃDÍ*÷ÎQ%/ðë¸Â¯ ‚ºÛœéÒC_VÏp5«~[^¢•_ñæõ¤ PºgÈÐóFRòÅFã…XÌŠ¥Û³”šlFz9ˆ©ÕkÌ$¨›"§Èþj,‚Q_Äiü +º”õ )Åv#Ôœ]3΀A?yâÒ•ÈùGNÊئ=.FŽãƒ¶éñ˜&Ø“â,åÖ2³1ª7¯Ý¬Ô£)šYÉ’¡*È›BÅ,¹Ûmwy}o êØ1;ªƒ¾†:×N”xB¦Û‘=ƒ‘×Ú‘ŽÁÈ…íaÇy쟲#Ì¸Ü ÿ†¦¤§ù›‹MJÿ?áÆ8?>~­GQsŒª`nÄXïÊ !nM€EÃèZÀ‘^«£N×Rt£§²ÒˆúÃ%õZ/®w™ÎP3±¨æä5Ì´Ò t‰Ð€%\’­ÊùzeumPóáó;¿Rób/:þu}ìÜ1þ [Üïë/‹ÿ7§ÕÕUÆ¿sνwwƒ1Û@n¥Pn@(
+˜…ýË
+UôšÊ¢üq6ä©*ç6öuªZç–*êí22è(ã­ðÓM v_ôd8í®aÚÄ9\rATŸV«8Uè-”eÔÒTø 6‚œÁk>„¾õXÏÅ9Å2î|ÌùIJÚräSÜἬ½G÷¨ ªPÎ_¼DpŽöû
+çž#* ÿ"~^@‰íÀäk0“õ©ÚÈ~çØ‹*ÇÐËñjŠÓVw.â~¤²z?QEÎeË9JiˆO*r~¯{Ç¢‰üû7Ü»'X%Á¾‡
+¹Ààˆ ãí¿“aÊŸÃ.Ñ«“ówYEÇ8g9o8?9G8?ࣹˆËF¶ï •S6çYÂϨɀ0d€V
+zÉ«ê?‹exsnMÿ™j´|çêéM7výµíø=;œÇ<Õ¸ÿ±§µžE-Æhç–ë«&çc×_,»ÉÙº< Îí>ZmP:ÕIú‹ý8ÀW\Cî÷ :õû©}eL$©£Ÿxwy_Æø(5?Ã98KÏ£bo>¥hÎ+nïQ¾™ä|Ô~Œ*ÜÚšàÜ”¹4y󼋹ðß)ç]í–ónÿ7
+Ìý£¯Ž§¹}ÑCþ6am;bQ…ù… •‰oÖ›Tsêå*Üõ0Î{ÏÙÁg¹5Êg/K|ëÜï"s¸/âÌRœó$ê6¸ß¼r¥N£¼F+’9=˜“9n £Ÿ_GÎ.âwúy*jh¥Š:]Ü¿Ñ£ºø…§ÌƒÄdØ2Ê7 5x–ŠÑûŽi@
+°Ì˵GT7ˆ_fxbb-ê,ŠØ¿Ù:÷Û“c´˜Ðõ°ó¾–[HŽò`ˆhb™1píóÎÿ?]è¥+1þ
+CHûÐ<mZŠ4•${ïVmcgßûñ<¿{ß»ç}Þçž«îÓ#î«Óe"ísëBAÕ|XP¹Ë§‰6‡õÇON-nùöt{~:ã>SÁ yÕ=7]†G®±™Vø:ædh¸j¬Ú[2YÓLšJפÝ:·®Øg"Þ(¿©å_Ôò3µl”ú©â¯¨âgTñçTñªø:Uü!U|*ÎPÅ ª¸—*ŽS[i/í¡]ô t=m§­´…®¡YÚLi=MÓZZC4ÞÀ0[Z~dxJ‡ù2Ô¿X(‘|–j² ;’°[Kµ(Kd‡¥H ‹cäîR8-é†ÆÇ |+_"Ζ!+Ã-Jÿ´£T³}l‡‡o>ïPêÓçóyøïÃþŸ˜:ñ)^ÿ“øÝð-µ„7(÷uÊý3J%²Ã(ª@QŠ
+P¼N7»³t<;<V:îÌ—BJcÙϖ<Þ½c‹ÄâóTr‘¸¯Ty|‰©ÝŠLæ1–©b­XœTèUìѪb­àŽŠ ¨¼DÜÇŽà÷U &à%Ã(¡bŸVGs«£}›£9­Žæ&r
+†=HåÀE|yR¹ú’Ê‘çÁE…Ñã<æ@¶äU¸…B!•\˜.(Ì‚XÀÄBAT§ûð+u¥ª^­ªW«Ó¾ÒŸ*TïxÔ)
+p3¯`èïÃcWÓ éÔ¹$¾ý^éíW{K8U¾Ò©C|j_òÿû÷s’±M7Ég9Èÿ8àó£Ï:¦ÕQR‡‡ØãÆhÈoß[­¯=öß}Ž†|Âʾҫ:³ñG½ËŽ%pýù’žO” |Äãö
+ã]á°‘Ìåx„dDtòžÊ ¤yšrìMãM]¶K =˜ñ¢~S‡h}}}?~×Ü #7ÃâM¡Œl’%8:ÐDWGõbŸÔGôõM`Fj
+šô‘°(D»:‚`|töÈÊàƒ»2^¦3²œ§™ÔqeØq–p:Æ¢ºk@„â-dalë Ã& ÓÜLF"doóª°îYý %rˆ$XÒCŠ$"ËÈrí¯I˜üÕ‚^b¼Ì>®È«²<SäÊkŠÛÉv,¨°åG™­¸[w ^í‚ÀMügWÅ Šä©àÉ£G1…Ãoܦ¡1É »"kÁv4†ÃïFtl¹y2ø»2´+U¾Š~œÐó9Þ›+CËÇH¶ Ü ’çiÌÐ
+twççÍìJŒÆ?2ÃíØJl…Œ™Õ¦RÇVVæI6£Wè˜yerívAVbfæX›dÊÿ:ÀÌ18Æ›Ç×ÙÑ'B6Êj‹C-ÞCx'iy/Ça‡¹ Í±„Ò2Яì=ŸŸòwÅaâëÚC.ÖúxdõãÒK™1‰jƒÐ¬õbâ—7läø•7Ìõ”ÞÙîjA,©ŸÛjw4DmÙÖ°ÖÉ‘è±TÀÊk5‰èîf´¥{ç‹^»ÃOÙàÅr(ó¾~ë-imlïH¤oiùu
+ê>(œÝ ¿”j濾hðC¢IçÈA&`tï9Á·´Š´ËÖØ°¶Qs˜òŒåËÁÃ-˜Ésf½ý!4d~ÚöF·Ó'ÎöB³m}× Cð;ÐæþæŽá'ïÁ?±¡ðÚŠ.¯K葃8\ظEþŽ¬Á©oè90Þ–<;Ä#oŠoŽhtË7€ýiÁ€…¦´È,­“¾2ü£¤ëštŒêˆÂd\éY†&pÀ 5ku£ƒ¥èÃåÑWeøIo ½*¹.7´-B˜ÄîW¿V_a×”jëîÆ.Ø â•µJ|‚âVX´é=8ÎxEºjaØÚÕ%Ryý¨ãiL4CüÇ˶XÛC
+ÜÙáÇK…9ø<LzÄלþ-|_ßJ®z(lj.ƒáÆæ¥ åÆ]Ö¨/r@ÈžjæÛ[zcÁ-œ7:Ûæ#~óÝŸ åöºëþÉwÕÇ6qžñ÷yßûôùÎçóg'Χ1‰CLb ù0PÂ(l  )
+ÉÆGÛP(aElaCj Ój㣙j:´imWm¥%!)hƒMª¦ºhÚTD¥UkF7¶°®¢T|ì9ºîMgËö½¾{žß=ÏóûýÞ\®_Ö¿9™mkÅåŠyßl¬Zp°mÞFè¥&cùÕ½ªá*z¸É«[§(;¼ùÓǶìZ»rTÝîØÃMu Θ˓aßžì`~ߣ·ÿ_‹èAUVA¨6‰Œ¢Î={F@¡‘- uåuBt—=ÏSS¶”LM ÉtT['Ç*g:ÔVubZÂ’š½¶.
+mûž‡_~´¶1|h•u!Ûßój¹¼ æþèíkÛ/Ö­ßY¯½kYÖÌà=ë h%71ƒ…iÏ%ãÆ_ŒO Ž7´ÓðŽÑåD…5§yAƒû¿Ñ=”–A¥1âÓo äó“ø¤o¦zz˜ý¸í¾jš“DÞØ,#Vn4šSẪ`yåŒÚÓ§¬wêu}UȜڦ†Õ½Ÿ.Å@ý°ŽÖÀ¯H‰¦Ý^¾?L:ýáΠŸ÷z#ÄW®_s'â8*ñ›7'uÛ'x?²¶s h "ÚEkÑfªê~> 0ÞvÔ…gw*È?R‘¨{=>ŸQï(ñnJ΀Ú,MP€¥ö ÚÊpx^ÄœVßû
+­–´‰ò>«®^›9¯56³m嬅mñxzÞk§õwvˆ;)šöƾNÁ‹ 0Êðƒã9{îI¼%ÞòŸÉO@›³6ßÞÅ?w»ûÐÐ-â5Üê=~Z”$Ô´LÀk/ œ-°•Œ!IñqJ 2’eYAhs óI géê×d9¶[ÿ3Å{v Ä‚Ø_PˆßŽvoºÙ>Ï¢ù¦¦2™
+šÀ'ï¸s™‹ß]ÌÎeà·+àRŸµÓÀ;]…Çx…)ìjÆ1Z¿z
+T°ƒá“0X½þTûØSîC—ï+¿Ê5ÜyÇ~³#k­Ýÿ…lMºJ@]¼€ÈÜUp]Œz±’ Á½Â¿RÆxœ§³ÌùK#ì® §ÿŒ|Ð$ÃU.~ç2;ww1×o¥VXóûà cÖ÷2÷þÉßæ#äÛ$ wÓ÷Nà:ÜŒÍÃ'œGõ‘c#Ç_RN£Ü9q\—ßTÎ;nŒ¯ïí:×=ñý‰ãçOœyßxßsýÐõÃׇ?rd„;Ýkgt—u?Ó½ÿ‘ëÌu‡6i›\›t%˲\–Ï
+Y1+eå¬#«dYu‚›àÇ…1ñMiBžpL(ê˜>fäXŽËñ91'åäœ#§äœ95§å\9=çΊ._(©Íp>#dÄŒ”‘3ŽŒ’qfÔŒ–q;uC÷”ež„Þ`4x¼Òª‘áÐðú¥ËžzfÿÁ쨯ö÷¯÷4;:¶GƒÞh4¸¾#j§AÑ¿udð©Á¾<øÂU»oìÜxã‰ºÍ í›ç­ZåŸ7Ø0˜Ø9øÄ`bÍV?Ý*­7†ÙÉ`ôÅ“eZaE+´Žu ž…ñt”5™f—ùó–ÉW›KÍnó¨ùcs̼bþÍ”&˜æ‹ò wí¯´iãcÆwÄpÌv ô€>©OôàPÙSf]»>9‰ç&‘Fô©ž¸fÏda
+ù¡‹h†«Ñ7;˜al^ï^+Á˜V0Ê;
+¦ɾpE }ïPÁ Ø–¶9Ÿ¶1{tÛÁ6«Í¶Ö„A𬠈öÛêúÃà«ã
+( ‘šé?Ï·¯2l—[áQ-pà?·U¶3Æ Òþ®ˆ¸ ‚ŠÖN›¡1xÒ“´¯¡W5}"Ù¼Áév]¯-ž1ÛUx%h´U*QÕéôƒ(‚J¹J (5À¤Šîð•Ò·º»’ÁÒY’ËGK«ZUçU(
+F³ÁŠl±˜}ûÔ£S0r
+œÊnýtLOû²#[GúƒU½¾~Ú7!\®³Âá‘@º…¨Ð.|&ÈNcxž½X%°|~8ýåp†±™.[èšÿUL”M°ÛY<Ê~ÆB–÷/_dX#{‘E
+*ábͲHÜ@Ë ñhH5TwYj
+âÅé(Š“Ë_,”*Vaa¸ãŠB`E%<ÔE _+jzµH”$E‚2GQ‚JÚ„­3R:×­NG¥ œQ3„œ_ÿ‚†dœ>R|(®ÒëCÔó¹©À`ßùшӓûðØÎå¿Œ]ÝfÜWmÐBÉìße´–¦ yú×~u°¼nßËw¶ÊZF«R`,j%¯'
+ÉîÂw’
+}ÁUÚ
+½…#’ùr
+­7ÛÛRëtnÞU¶Ë¬cM2éqŠr¡
+T_•$Hà„/Ѿ£c¢][®¢Õ<­§qJïŽr.9ù¦ÇÜСµÚÕ±FI3ò®ÁOû²uÇßjžë™f³_mlô(ú.øZ9Îh$e-€4æ”7j‚úaÑ‹@=#Ë Ž±ZFj•<Åñë!êÏj¦u…K B©«}ƧlÑœ7Ul®YžÕp¬J#GÛ)ÿ%ºìc›8ï8îçÞž»çür¾³ÏNìÄïΛc;q°qÄÌKÂ…4%­«:!¢àd¤T& ÃÀ ÔÆ«¶¦¨–¢®‹*•!¦Rit›Ö?* í¯mlš¦nlŠ4¡)ž˜XWmkØïl'‹ã»ÇççË~~Ÿïïû¥iipó,Õµöošfô•)äaxVï– ¯‹õÐX¦UH‹YW6’íËîËæ³xïø'Ù_fÿý[ö_¦ÿd±¬ªò±cjÁ9¬Ã3fx6 ôÆÒ•ü@aì5o~$>ÿQþÓü¯òœ^Læ‹páÍ<—ÏŸNVšo$ÉDèˆNÑŽ@ÉYj¢˜Ê¹¯N”ΕNó3òùŠ¼$ß‘ïË•¿”y£Ü ·É½ò^ùs™;"'é!p›ç[e" ?>¨! ˜Étìyœš*ÑCC!Òù8ò8zÝI‚ ½O’?ŽOÎëø_Çå‘®Ü ·ÿ¬väñÕ§Ç×e¼;Vò]Áõãøj³OµŒH«¢~äH‘È6â&ò…Žh[E¤&æºEãǺѤª5ªÔMPÐ!
+,e(åH8
+{§§ŽLO]Lßì½Ýû`úá´àÉÅs™ÜHn2·ûNîÓoÈ¡7s(—;89‹fgg¸}{9‰NF¢L4šlnV¿ù­‹{örZùý÷n-ݼq}ë–ôf1¹L–Ë3jR›äÃw/Vv]¯ôo­l¯ôvU._߯äF /¸*ÞD%š+½Pz‘Š–¼¥0U¬œX¨œ9Q:S:ÉU VîJwJ†×NŽÝå Ú)í’öSí¡¶¢}¡aA jImH;¡±×´_k” iÓÚ÷´G3©¡´63IüªzJ½©ÒDEËj’&er¯U+«DzùîÌ}Ôøã²ð~ 36"8Ë©òîòdùçevy¡L•Ë÷©dœÂ
+µñ†ER'gfŠeÍVv—µä²kùQ¡÷¨ën
+ ?ý<Ƙi'6ž=7±åío¸›Oz, ¾ cì¦î$Åýyy‹{³‚†gŒCŽŽ6J4bÕn?JQœúåb»}Cε61Ä„ú8ªM­îÔ¹Ìs­ñíQŽjà-œ„áë1ŒhŠæÆþØ{D6Ûf9h04çœýË>tö奨Ä{A¬ ¦bÕ¡ãIÑ!#'HX¤XÕþdÛáÎÒ1}{e«C1é諆±* ¤[Þl5;=/­MÏ?ù‘)x“²&žfYš±›Eo–GÞ£{pU.høOób|G¢Ï©º­„à+*’‚L„Ò»™ô¬B‡ ›¥ 2GEQ4qþ%qÉ´dá> ÿ6üçðÞ§~Ö‡}Ao:‰“Á,ÎO“S~!ñö4:]\Xô3 =í…xªÔ/Q…wÁ¥T%W©êá-&?öº‘Ûíµ#»!(‚;‡qz3´Õ‘ˆÅtã4ø¬j‘VW+µ·¦á
+=¤«` pŒ`(nücÕ­*4>~¼Z³z¹ºP­`¡xüP|pGv®ªú"ÒëGÕ[ÄE.Ð1 ùýšˆ틦8Frð.©æJ?ùpHNŽÊ&u×l'ãîòҌ茻ã†[%ž3)\ëÏþ‹äéuH."#Îæà8ž.DŒ¤kíG_}þ7Ç[`ï= ÍT=CÓ³
+;Ã6®£Pærv>{ml~죶«£Þ6o{K´e$Û."º3¾{$;jìëëj44JTclÆÂÂâüÄ\±m” &†Å d°°;]Éì.eJM§X°‚/P—¦¾]¸r¶ðFéJéÂ
+ùX%ÎÉ Ià$B6
+==½´ñhOt[t_t,z*úvôrôZ”ŽF×{H°œ'Q’œGÿóA/MÊdï"ÕÞÖyT+ÁÑj8Ÿ.¿Ñ'@«÷ð3¥«<_MÍ>’•H’}’Eˆ9„NÒGži0—F‘ô¨ÞjÚ &Ór}Ñ…ÂP~º6ÚÒ6#4 5•/ 4]ëðåša)å Ë©IÁã4š ûª Xç–å€èYÒ5n“\î×PÞØ<ó´Ø융öå/÷¼ßµ¬Æ6Ð"g Ú(¶¥¤éá™ßßØpÊ´ÑÍb4g–½ÀOÁM¦Bä X¬
+$5ŸóR3]iušW² &|1Ïç<¹º\,×Ãs¹‹%.w.QJ¹[î­Ê„û›ÐòRû< ã;ÜŠ¥wîÍmH ¼“v ³•B2s™‹T7®jÜxé£ìõ,º. ²áD—áòÌðÓy%G¨TúRe6-xUUM¨o¨†êv5­¾­^Vo©„ªRôd¤Óó胸™æ©n
+ÝJ¡NRW)EÍñƒKtZN_ZT­˜ƒé"`Vks,½¨\¨ÑÁ"WBM§!¿] ÈØ¿ƒÅ›šVµs]ºé–t„³¹Üi5,©j¸+‘Ø“MKÙlZ™ÎççÁ\Ü¢*ðezf:?sšâ%Šâ5ñëiŽÃ4?ʽúL¬ë÷¼úÉÆj$ -a.Æû"nê!4 ¦I-¬ÃQ4 ÉOÔiÓç‡ýì¡–³ÚžÆ6 c¾
+nþÔ_â(Ü8áµ-«ÞÝf|´ª þÈ¡ ³X¸ñ‡ŽÂ×[œf‹CÔP¥,<« Ì‹oÿ…¸ ¹s¬Œ¿r­´¶66Ö[­ÂÁƒ#äˆ2$¨dìp²¿/¹¡;Ü~¯5kH­ÈÄÜ;„¤Æ3‡2‡å~kÊCmèa
+A&>U£‚>µ{}R
+hîÒkh¡‚t¦‚”¬‘³Ï 0½¯ µù†ëI?‘¾'Ö’¤¤=¶D$Vò4V‡ºHÞdrÿši–4ËÖxlÀ²ú«MT€48ÄŸZÚ=Z)c&ñ¶Ÿki®u¨>+/Ø8ŠÁüíV`g¬RIá¶È­¹SWJINS&Ù²T¶Ó.“½Jô“C´½éxÄY¹ßkö•î4{Ôéq
+ßzN›NYißöÿ‘].°iÜwç/îÅ؆€gü
+3ÅÒf“TÏ3jŒøÏ‘/XŸœ,ˆ¤(Š$\øÍE·EÔUlQ¨”ÜšIeŸ‘Peý€*în1O%2pa+ˆ´yh‹ £°b•á©£ÓÞEõœ–ypºð¬¯W¯†¢÷69 „™è ˜±Ø°‚—H!`3ÒÀ˜~; Ì6˜žËLD¨kÕWñCéLy{ œœ¬Æý\]ƒ²Ç!ÆR¥R£Äð©ÅÐSW°´fæ­é¼#yÚLeZÞ÷+æyÓü)ùwa‚¡aLÂgk«GpO«ÍSêt׌K^ÑÁ0-õÁ4If<ˆAôì ¥»)^J
+×Ô¬ë7z:úD·ÕXM3F’ÓS(·5¶ñ1¾·ëê΀‰n:ÎÄ q.*G“ñ…øbüp¼8L§™4›6¤¹]ò®dz!]ÈÑ9&Çæ 9.·[ÌrÅEvÑ°È-.,..¾Òw®ïõ¾×Šwú~Óçñ1~ÖÏ57·$ý þþéb\¬‹³7;’®AÓ4c`YŽif“lAoÿZ®Œ<'z=ÉX[[uEýQ4íËØlüÜ5¼TBç3óüéåLo… æ–<ÃË7¹ÄZ—om{¹«Üq:_[vš×ìeçH~õt¢œsÂÙAžœss/4ãÏñ/ó¯ó·ù»üŸy½|f~ Á{pª·÷dÉ¥áYŠZ+·^  –©Z†ÕòqÄuZš ª/´t{mð^i“PÑ«¨Ö€*iøjKÖb“dz0Ð+ ôfæç <%ò<µELOºâ„V[ŸÆÚfITE
+ÞžØPØ,¨!¶™`Z§ƒŠo ‡œÐz½ÕìDÔpR{dXØàJÀ kÝNU?
+ך”j`O"|€—„:Bjþi SÉÅÍ/…þÖP¿³Úbôuº¼Jr!€$X“í°UO°LïE´zÇŽÍJ—oÖüÕ2±;êhÆäØá®—â4£7
+TUÅj/Å@Ì?ãWiõñÌ*”™ññ*¼ëâ¥Õb|>­·-MBÍÁðƒhlr‡¦h²>àšæTׄ«:¦ê¦>
+52²‚\HøŪ¸•Ò¯œUY Ã'£J^ùºrU¹¡¼§üSùDaezŠRlÊT%KMÕj„ ½èº•P5Þ7jþs¿¨²n 2î'ªAÒ}|߸ºú4S™ˆÛï_PR¢¢¤¶æ ‚pÄpÂp^Á°M;´ §j VR²2Ó­ÈTû0¡õÞ
+‹êµ7Ÿ’ƬnµüTÌ&Ü\µ}T~æÐà¾g9ó–‹þ¢ÍhÌ4†’V=>?n7b¨ÁAtsq™ÀP¼ï™ê :°ƒ³Õ zgýt æløåÑô `"é–Z61€ð9
+"$*¡ûzÝUB#ÀJ£4F0TàÃõ ëWí„•æô,Àa*µ€¦¬‚’—ž7 }Ž Ÿ¦pGü
+ìU‡´-_Ýø€èÇëu 6ñ2íMy±ú:ÄëápÌç“iZ§ËšE±ÉŠ™L6ÖÓóœ$‹’$gD©'k–c$æÔ­6O6¯µEÖBå¶r
+Õ.'ä²¼"ÿZ¾',ëi¹_Fzä:5™}>{<ûböz§²Y$+šÑÆŒ4éÉfd£YJH'¥7¥ßK$âœ$iåÕ‘If¦2—2ïdðLfô'èI*c“l𺧠öHS‹¦ˆiÿCi‚z‚]ª¥ÒÉŒZ|Ù"ÿJf6ãvâ"l`æ°5hSƒA]s–þËt¹Ç6qpü~¿ŸïÎ÷°Ï;Ÿc_üº8çWlÇqlœøòrŠMȃ¤„”PÒPž{•RºRö ­TUZ¥­šDE5Â66¦µÕ6iÚ4u›ÖI{TíèŠ@¬clS•d¿³“v’m­;ÿqßï}¿Ÿ/¦&jcáÊè„ùœ³
+ĘÈ0fµdŠ E¦j碚£¬Ø?Zµf ëTO½+„m׺žqlF@x…5#ÙÓWÏ›]¾øçvw…Ï-Œ6xÒÁîRØ È…ÅL¿3¬ÉÎf»—‹%åëm¹þœ£e÷ë;ÚšSÈÛ×dºÝJ“„N†äL“tó««ßÚ.&‚Mþ6Ú3tâÌnÐÿÞ“‰nÆd2ó.ŽbxŸ?=¾úÆKß}j<ßž(’°{äÜ®¿ú
+Ø÷6´VÜ-˜¿qöKÆ©¬^(ÁÔ™Ç/CÎ`Hª}L¾{ä'¯£àéO²èâᑽjÛ„¶G·H›/ˆ$çôõÖùÚ›Ë6w¦ôemY±ÓÕþQ|¹W^Õ¼ì·DÆ  B@òœt‘"M‚( l—ûW®ÞßoÏsÏ5úA¤ÅÒ6>îÞü²Ä2É @1
+àÑ™’É°è¢D€µk„‰_›%ðˆü!±Aô#“탑ZiNÓA)țܟ|øò¬±u·­ÝFó¨Ž(O
+GÉBê¨û®ý³L¨á)~;
+îj/9
+D²f Ü$øO@µZ2~ ¾°Bˆr­"\²á5dÀjµ.6¶Ø§›¯… |¶ê@uÕU;ȸÓEKþ›E<¯Xì6D"ÖÜ+&&®É?$p,Ï*§[£ ²ÌÖ‰vÖïT†çÝ»BIÍâ8%Ý<úZßd+²šs;¸žã65Ák«,„4¥˜-ò†}²¾cg} ¢y<¨ⵧ_¸`)¶®¾â°p,§U$ÑfmìŽ%¼=Îð줔´˜
+7`ßœ_{!Dœè&~ —fØã©ó©óiS­x*©Jób¦Ñ4c¶zø—æš¹ ×b³9b5ë&“1ëúe5"ªjDµêh“?"¢€!pnèv3±é°H%&ülë´ÛÑoÌëv†TjHÕÕû* T ªË`ñjÏƶÁÕpü¿Søà®Ñw1EØ M`D¨j߆Ôx„€Æ¸¡_&O U…ÃY€«
+ùø7jß•@w,®W‚úøW†³¦{…†´°t§ñéÕW~|QL«j²óÔêí/Tú´Îx~Ë 8xd( Ÿd$ígÙƒ†6+ª³qî;g¯Ôû¢FËï]û^AY"IÒ£¦ÒpÖyÖc"YëÓýl‰tzJ!fÑùÕºE
+ÈiÊ7Á¡Màªî¦'4ʶ¤pâEÈr,–æ§õ
+?Kwqªâ§v3q©n$,0pªffh4emXÕJј_¨aÕÿxÀ©!¸S0“
+Í[ÔHù›Ö©¥Š]NV0e+_nMÖ^ÚÝ—ŠÆºü’>ϳt‘æ⃫59,±M3&à84ŒO ýíwSAyK³/ˆ7¹ö{“Hv:&^¤?é'J±R²”6ùëý~Ðò«r›¼-¥áÿ%âñD¢)žŒ§¢éb}Ñ_ CEµ­ÄJƒnRáù(™è'P¹Ë¯ƒ^OËO;ü@$UŽ)2*⣨Öæ—£ó–ï9v
+Dy¢ƒ
+úoÀ{D=lÔ•ðή·2Ü#$PH’£±hot,úDôR”ŠF¸²2ÃÓ¶\ §çÐIܨÆ=µ=<:µžV8¬ ˆy°ò ` ÌL0…™ŒÿXá¾Ã9eÃÈD²êc,ˆscÒ`FÉc@Á[F€U8A²XU3¬‡UíK¾näT]½njøáÚ¡dñê’ùò&ž·ÒlŸñ2ûÔpÑfÞ¾¯6[©^‹7»÷Р®Š„çéK ùxе:iÝóz_F
+Ï·ÏuÇáÛ`§+ÃRÝ8u$Rd`òqÔVohm™ÕqŠú6Í=;71´ïÝ溶Ãd•ø¶=ûŠo£K´¬Œ„{ÿú)Ж-ªz5KcíçÖþ P§Îfâ‹zˆ­³fàDz¢y>}"ýµôojÿˆü3ö¯K$²ç)bpºWHø0‘pæ'Bµädù;‚
+Ú– ÛÇ‘°?Æzä|ÉKº)ñØÄl^î ùDyzŽÅû&Š‘òälxT®À¤ÛéäêhìßÙMuà¾þÅ~ôðêHF;”y¾g[õðœC”ª ì²x2EO/¤Cq©ðô
+Ô‡æ—ïþ ;a"#u¥¦ÖŠ³;°PµZJ!bûXmtÄZš iÏ"J "ÄDhľZ½ŽRH ¥Þ®òË!ºÎßQŸ'»“l]Fz7Àðµdr‡u|qñ ˆKÆXÉ)†rlæk¼ ÔýÝPâÿ7߶+æƒÃ¶ @6Ši et«°[ŽÜRÓk·tØn>?Óy£î÷Õ»y‘qvò.W0ÔUíß݃ãÃéÅíÙ†ùçÓjt([‹ôë3a†OáP¤à¯H2_®Y„GlA`»Y´[½–‰¾•ñ|9^YjžÂ{"˜øå10/É©Ë
+xÁ!/žœ+”'›ÌwU゙ã,V« +ILx` ©ò^¶$l`·áïã÷nc+Ö,œŽ÷ôGøIpTšžþAÞVÓ$©G=àCµÔ4"O9°¤ªÕÚ¨iL­¦©U‚Ê[}eä]pÅŠdê>ÛW;_íD;o$êeÁÙ³B±«°³,Œ!,»þY—ð”¦k‹ÚníÚUí7Ú'šÑnjèœ4íÖT›±Ž˜e8ŒÁ^nÅ™ne®÷ÖrÑù’11-Ÿ2™˜ÛÊX–mCƒqÙ†‰–býEÍÉ0A¬0К‰D|ÓòFÌå·
+e2Ø»7M_Ú=¤å¶ùÖ¡ž#†y&ô¸|‘¡ù!‘'½…§ÎˆgƒD€a}ô¸Í
+„íö$­N¿øF¾ŸûÚæY&öó‰ìÒò‘æÏ~ªú¥%xdº{éÎ;{
+q\xöÇ]ÍßUÂ^šòį–|<M» ÌÝhêÞGèÖ nDC.éÍÐ~A”JÖ¼ôe˜+Ì» Æ0%µ7¦(C±XC-1ªZr~*aà0fŒ½M¬s6º@]Ó‰Ôz¿S-X©SmhY÷"ÈnQyõ úšjQÕt£2ÚÐZ^ŒnÜýPi46ïKm
+lù–½fZÅØÃ܃¼Óbap©ë̈ÞL8ð;¶'YšþQãT;?8Êò¤Kr9\þ芜
+­ºÂâÚw‚N›åÛÑŒ”ŒøÒÍÏaAoT!>Œóö!K§¿ïïðõí.7Ó,œë
+ ìôoAF<6+‚6G›8:fùâBF‘uq’—¹ËËAŒÈf/‹<#Š<þ)…I0æ& "‹¡ñuÅF^ç/ÔI²B äDÞ²­ÖÕá‚^CmúÜÚÈì:2Rãùm5ÓŽ É3ì«åM\qËÆ”Em•q­]E£4†w‰€3ËeêžÚ¶O˜yÞj"Ú*g+,‚7³ 6š”¥ø¿¯qNì÷ÆÃÑbºó1$ŸSÈœÛN&
+ ¤;}v,ÝK!É®¹ÇëÇûÆÕ,õ»-ç$%!†“ñ&ì‘!'³CÙ[µDŒ¬I’NBˆï³Ú™N?AîmÞ Ç^kþë+Ÿ¥8'%Pƒ¤‡ïÝ´­)è?ATÿzógéÝOç½…„=¿–ËxÝY_¸Rz§ò>q£ô ýƒÁÛ•ßWï­ÜÙO
+ƒBE¨
+š¸Ì¯„yeP©(ÕîmP«hUMÓy]Ð;ôа¨K3Ëú~á0`ú!""!aÇÒLmXwáP¸¼sÌb¹¼4Ã,-Í4\8aÙ§óÖ¨1˜Û®`°ïÇ–ê¿ŽÆa~{+Z/;·;û®ƒ78Ⱦ¥ª_xìŽß¿nêžUêç Àg^Agfn­Þ_–5%O à6ÐnJdîM¹?Ù|ÒT«Mh{ÿ#f-ùZƒÉg jXv ©ÚÈ‹JÚqÑhgË¡DøI;"¶±å0KË´8-½Kı־µµ|Œ?}@ƽ×÷嬘è;·çymH•ß[ßÖðuqj(©NÅ c»]vSC¦Óª_Ü5õÄúgúŠ´d‘¹Ý,)xEkªH¯ êUœ‰q™bâÂÉåÑHD¯¿hù+/åæêí5食]¬dÂ;_#&&SKé¨ÒYÝ^Êæqûy¸y9“?ðÜö×9ßžý•àÃ]Žàiáü¨®ÚØ&Î;~ÏïlŸí»óËùìÄñ9öʼnëçÅGbçÅvì8oã’Rœ¨FHx·†¦$€šn‚¶@Õ¢®âÚ&”Š5Û—ò©«´©é¦¶ˆíC„
+~ýÔ³;èoñâGÞ¹W‚
+“ T
+M‚~Âp˜Te& è$UcœP&„˜Ð%ìÇ…³Â¯„+·Âw‚¾PF
+?$+cÐS`¨/†x oÖ)†FkÖ·*½ŠX'[&®`£ Û7ìÓ†’Ãðаf7Þ¬3‘@Ë] ªÁÊàŽo&I£‘ç[mµA«Õ\r°”¬é­/+-6éöûˆ1¿ÞAõ\­2;* Ú,Ú\fŒ(qGãv>p£¥U¶KóEnŠ¤Ì‘ö÷ºfØ"3ë`1kàë&5Fj†÷m}Ûß‚%¡.2èt
+šø`C¿T”TÅt 9Òl%«)¢]wš,‘ÑÖ!—Û åz¿ÙÆ­2ðú±½.»ÉPZ‹_è¯ìŒ#Ž›ÊûTÝ ŒNïºb­¡µŽî¶ŸûØcÁævpüt©F«×ª –þsÜ#ÄQÎÂb
+š¶’
+fƒëXaRäß±…¹Ç–G%ÿ±Çãóöó܇®ûæÜMûMþ¦CµëåwqãüôçæƒöÓfU ÚjÎp-|‹'9µ…°¼³ZLv¯ºÀ]°œáÏ8)° PD†ŽŒL[õ-5€¤<5ËSDžªH'ò!ò QÐÈ>¥‘ ä
+‚A±ÝsÆõ@¿ÃOD TMòB¾MÈ P#
+1c]*êü¾ÕpÖ” ¦À竉BÞ/0öºVÀ†Zëy‡°&
+-4‘‡À(Àò
+TC芔^šqàáHwºjÏÉða·-±©<k0Mq› öîa)µe)hsYË
+5„H$ÙêðÔ(Å`B._ÀYL ?Ì6¤l¶Ñ]èjà£S³$B J®©3º-5>£yG—zi¡arv8;Ÿ=‘]ɾ%W² ›]­¤{f>WªKÕÏWÏVW«oToUß«Þ­~T¥WáuGª \½³Ÿ
+JCÚŽ Í‹rÁî'^Ь,Œ
+5гUL`H@·h,ÚRSz€ßE-†ªK’±\¤‹n'™ÌXoÙg}¬Ÿ (‘AÑÌUÆçïׇY/àJÚ”ˆy²Ã‡ÑòÔ…Ñr4ZºýðË¡DwR{ôF¬2]½<–lm}òõò>3…•É–àÓRçÊŸ/='ŽN´?É{H€Ò.‚Á€¡œ†vÅ;rL­t‰lèÙoevê©wÿ
+»Â­xÏðg„Ûìmïmþׂ,‚8ÅOŠ¸QÚQÆÔ¤O”$9Ô ÕÀ ¢dtòü\ÉJ%#BÔˆU#`潎\£ckàM‹Y…"]
+’9š&ãk`À
+SWTQo«è1¨j_qĸ`l8k
+À™•~Ù{J Lƒî±¢Ö¢g£JØ
+éܘàóJÁümØå¥ívÕ½ùô2f iäçVq!íu¦}‰y)Jf™lt?³?ºÀ,Dɨ¦­oYAÖ#±¬Ç£±Q\hj1Ks5¥áÓ¯Z\hV yDCòˆ…àÐFÞ"g;<-k¨ii’ÄÅæi:2Ž?à~½Ãý‡ÃiNçÊÆq¨}@ôªèzgÆÉNËëN‚Z^ozÄz³¼Â°×Yp6úqë°÷ùxÉÞG‡Ö6Æ¡Õ:Ijkèå"ôÐtá†Æ{9%PcùÓ_,ïŒú3à ÓJÏ×^Þ3»2ÑÁ°IµšŸ¹qâZ5£Æ÷Rd>¶{ãîdaalÔPãp‚ 8Á«x©"¿°JDhX€Ë¤Sð{»°lg3³ñ|ÞŠ÷VúM2f»â÷áb<>gr’irâ 7ïaë=ŠK…Ž{=4ãÂáõÇ䌋D o‚«H
+í¸Ñã ÝRÁMTCp´fI5ÜŒ›Ýæù)“8`Ó¼Ss§Ÿç?pHÈ?¸—»×–c¶B™ôï{öœ[Åÿ+`öˆÅ2|¦8d,Çe¡’ÛTÛc¯…¿ÄvI’’{Rr o0‡“vÉ’•mÖ€¯7Cý,ê=×*qªF3r8 £³4&ü±y&Ù¯¹ ®ËÁ’mMÎ4öðÔçX%ÐèJ#Œþ rÇòáþ™p
+FSñéÒ´1åãmÃ1¸€mi[=Ãþˆ}öI6#Pâq£p^ð+;]Å9MÅpî•Ç}ÂçX؆”m~ÝEðr´’ˆ)¿4™Ð|!c:ÏÁÌÝñåñÎéOaüæM ¨ ™Y-¿\kgý¢þûν÷™pˆóQ¼~e؇þ‰«EÛƒ4¥~?«„x;Zxøû0‰w,»Ÿ^ì¿‚ñµ³ùvÕŠ=-±ï¨IúcŒÑ´4üÚÛC¼$òBìþšÊPÏnþsc?/ZÁ`*•‚žYBP´T¤¡WKR±XŠ“6WßêûIÊö¦åh¾DcŨM †Oo2’l$±dr ±h7Zä‹V+×ÀÙ7ûí%û\²¹´hçŽÃ¤QhTíÿl¢™Z}Î.5)ÓüsâäÖø!¥ŠÛ‡ƒnÏ^lòË^µ×5·ù‰\gkFê˜MRœLyˆþç´j£Cö*Qòë£Æ_NU¤P0°8íuSn¹Õ@ô•§s9=³ïàßžò'K)‘¹6k±8Й)žÌEÅ®•‹G7<÷ȽÐÏ„EÆÝÖg8ºùG|ëA
+J5)ÉγÀVµlûcL¹öÏï¼{Ͻ~ïžsîùìiáŒýŒãŒót÷²pÑ~Ññsç…Š ËÝWâ×z,ƒÆꤴ¢²SÙGuëÛKDÚ>†¹`@Ò¦ßR¨,o•¨êTˤ4¤ï\ÿX»&„ÿº"L„—(F|vÞár„QGÊ1åP9–È$˜Éø®úH­Oð‘¾%r÷O_•.I¤´D\–„ð„‹Q"EL*"7a,“¡/ÑWiJ  'H|(MLQS©ù™z/#|º^ÁïtD¸s_ZÈÕ'2ŒÞ\¹‘KòŒ&Lë iÑŸ«ÙLF’Îp÷m„õC¾¢³¬Ëv-X.(Ø!O*ÜåCjÝäÜôšÍ¶/ìnJ<ÚW–ÕŠZäõ:ƒ±ÁáõÅ
+séþ±7^ø*é²ØÜÙCÇç"JÚ &öðÛãÙ7vøÕ¬‚ôÆ]‡‘ex¡ŠìÑN<Ý96µzï}cã#£•?rµ6Ùx Ãïà«%—­|jbõ•vâ4+‰ºSœÈÔ‰È ħ¯µ~KšüJ=ËpZEç±ÄñùTSÐT¡Ö>T¢B0w%iÙów96>žfY Çó#e¬FãONJ±²'.MNÎi¼XRzãOš1¢ï‰k&%/“qÖ-ß…Òw6 ²È¢Hœ:ëT%d± Å!í™X„éùŒvcVqïßR© éöÁb¢¸88ÝðûÆï\tk‚T08 ™ëÓÄthq岆´|‘y„L&nÊœMŽÕõïçÔ¼ \¾|™æ„¶¶œ(ÿ0øáÚäˆnÊ•„2Ç@kúBDÎѾß ˆeýp-P>ÿ†«ÈβÎPüMyN˜#$~\ê¾oàÔ—””hæ96p£Ýêš1éYV¯Õ+­´ÐVꯈ>eRÿòx¬ØÉq&W«EË™ÊYV©1s }ÇŸ°‰¢ž³(Hc´uVk¨‘Ùéå»Î§"å ‰2öG‡xά2ý:ÖRÚ©¥¹§DQ,m¬ßëßò¾Owúñ*Q£ôºÇ‚¬ªQ8ŠQ–Fº¿ö‘«H(ÒÆÕʲ¨X_Záé×½¸…‹¿V_Vë­Na^ÝºÊ‘ï ›€g$ÕMT×Vw¤¶?Z»/v CÕQ;.FEQ̷΋x´©IÙÂ6j¼‡K$-E¬Í6È6gø¨+B®ˆ.‘‹’À|`%–t³ËnÏ¥º…ä5é›yASË–ÅIy«ë‰ppbdæBK4XY´ÊF,êƒÞ8˜ñ\¦×I£¬»úW“Ë~¶DªŽ¹”J“^ÍYô¥%Œ†/å]Î-¨[Jk,:=©TIÁ²V½† EµÍf$·?ñƒƒ·ÇŽŽ:v¿šµèZÝLÃK÷ÆNLuóŒ1p”Ô~Ý-YyžLì}}àãÈ€¨ÔdF;–²-]VC‘²ÖÊÙÝ5W?;ùÞË‹³[\FÏÈüçèÊgÔ ªÃ-É‘:BT…Ãs `\X
+ºQÅ–;Ô¬Ù=8Öã ïtëâZ»Û`1ù[j‹|=½•ç Q Gážz»‡`X‡Íòx{Û†žyÑÌ‚¹ÔTVì‹Û}þ‘4UM‹{g~\Ðê9µÀèTc LI‡¼‘ny:^|Ósåm>«»E¡ë%´ePhû>™‡²@õ‡<èFĘòð*€ºqñnš9
+À4öïFý|ÖY|×}Ø·ŸÙÄ&6±‰Mlb›øÿX›ÈÍ”,6„
+Ú(PÍ€FËð‚Þ
+ø6þÚAÀWeÁ %à…
+A´C7ôÂ6†)xlm µäÑ2ðCÔBÄ 0ˆ£Ùµµµ¿|ѧ°ï_Ü(€µÛ_ªÁÀžÂ*˜
+²esAV¡T%[V¡Æ'î{^&ƒLA¦Pã@AV |ª «P~»3Kö÷¶f³ûçìªî=”›þOz¡R¸'Iè‡>ÀVÈÂ"b?Ìã¿îÂ}Ú 3pærý_®[û~ïç`¶î¯‹;G>Ÿ %ßE''ÑBЊÛ:Cþ1#
+ Nâ£@I¾[¿ÂÒ€FØhÿj®(6Ð[cpY¸Bߦ¾Q°i}þ˜çž)¾í.39íŸp/Çr×#o®v®<®fhÙd;æVþÇ
+ûÃG
+endstream endobj 1639 0 obj << /Type /Encoding /Differences [ 1 /bullet ] >> endobj 1640 0 obj << /Filter /FlateDecode /Length 208 >> stream
+H‰T½Â0 „÷>…GCÚÎUX:ð#
+ìiâV‘ˆ¹éз')ÄKöåÓ-öÍ¡!@\Ø©ô†4ãè&V† (AÖn©ÊJ"Âí<´ õª*×(ŽgØ´³íÜs—oAœY#`s+î8h'ïŸh‘äP× ±ÏÄþ(ýIZŒòŠ.ób5tG/²¤¡Ê‹ú]ô¿ö!ºþÝþ¾Ve^–u‰–à´É×[MÌ1Ö²î’(e0„ß‹xç“ezÙK€
+endstream endobj 1641 0 obj << /Type /Encoding /Differences [ 1 /barb4right ] >> endobj 1642 0 obj << /Producer (Acrobat Distiller 4.0 for Windows) /Creator () /ModDate (D:20081127121323+01'00') /Title (XML Configuration Reference for \013Synthesis SyncML\013Server & Client \ 3.2 Products\n) /CreationDate (D:20081127120621) >> endobj 1643 0 obj << /Type /Pages /Kids [ 1678 0 R 1 0 R 10 0 R 15 0 R 66 0 R 121 0 R 173 0 R 219 0 R 267 0 R 316 0 R ] /Count 10 /Parent 1644 0 R >> endobj 1644 0 obj << /Type /Pages /Kids [ 1643 0 R 1645 0 R 1646 0 R 1647 0 R 1648 0 R 1649 0 R 1650 0 R 1651 0 R 1652 0 R 1653 0 R ] /Count 100 /Parent 1654 0 R >> endobj 1645 0 obj << /Type /Pages /Kids [ 360 0 R 385 0 R 397 0 R 412 0 R 428 0 R 433 0 R 436 0 R 442 0 R 449 0 R 452 0 R ] /Count 10 /Parent 1644 0 R >> endobj 1646 0 obj << /Type /Pages /Kids [ 455 0 R 465 0 R 471 0 R 475 0 R 478 0 R 493 0 R 505 0 R 511 0 R 515 0 R 523 0 R ] /Count 10 /Parent 1644 0 R >> endobj 1647 0 obj << /Type /Pages /Kids [ 529 0 R 533 0 R 540 0 R 545 0 R 548 0 R 552 0 R 558 0 R 562 0 R 566 0 R 575 0 R ] /Count 10 /Parent 1644 0 R >> endobj 1648 0 obj << /Type /Pages /Kids [ 586 0 R 596 0 R 607 0 R 620 0 R 628 0 R 634 0 R 641 0 R 644 0 R 647 0 R 654 0 R ] /Count 10 /Parent 1644 0 R >> endobj 1649 0 obj << /Type /Pages /Kids [ 658 0 R 663 0 R 669 0 R 678 0 R 685 0 R 689 0 R 693 0 R 696 0 R 700 0 R 705 0 R ] /Count 10 /Parent 1644 0 R >> endobj 1650 0 obj << /Type /Pages /Kids [ 709 0 R 714 0 R 720 0 R 726 0 R 732 0 R 735 0 R 738 0 R 741 0 R 744 0 R 748 0 R ] /Count 10 /Parent 1644 0 R >> endobj 1651 0 obj << /Type /Pages /Kids [ 756 0 R 761 0 R 764 0 R 773 0 R 779 0 R 786 0 R 790 0 R 796 0 R 801 0 R 807 0 R ] /Count 10 /Parent 1644 0 R >> endobj 1652 0 obj << /Type /Pages /Kids [ 810 0 R 814 0 R 819 0 R 824 0 R 829 0 R 834 0 R 839 0 R 846 0 R 850 0 R 854 0 R ] /Count 10 /Parent 1644 0 R >> endobj 1653 0 obj << /Type /Pages /Kids [ 863 0 R 871 0 R 875 0 R 883 0 R 888 0 R 892 0 R 895 0 R 907 0 R 912 0 R 917 0 R ] /Count 10 /Parent 1644 0 R >> endobj 1654 0 obj << /Type /Pages /Kids [ 1644 0 R 1656 0 R ] /Count 198 >> endobj 1655 0 obj << /Type /Pages /Kids [ 921 0 R 927 0 R 930 0 R 935 0 R 938 0 R 945 0 R 950 0 R 962 0 R 967 0 R 975 0 R ] /Count 10 /Parent 1656 0 R >> endobj 1656 0 obj << /Type /Pages /Kids [ 1655 0 R 1657 0 R 1658 0 R 1659 0 R 1660 0 R 1661 0 R 1662 0 R 1663 0 R 1664 0 R 1665 0 R ] /Count 98 /Parent 1654 0 R >> endobj 1657 0 obj << /Type /Pages /Kids [ 986 0 R 992 0 R 997 0 R 1002 0 R 1006 0 R 1013 0 R 1025 0 R 1035 0 R 1046 0 R 1064 0 R ] /Count 10 /Parent 1656 0 R >> endobj 1658 0 obj << /Type /Pages /Kids [ 1078 0 R 1084 0 R 1094 0 R 1101 0 R 1110 0 R 1116 0 R 1120 0 R 1125 0 R 1137 0 R 1147 0 R ] /Count 10 /Parent 1656 0 R >> endobj 1659 0 obj << /Type /Pages /Kids [ 1155 0 R 1165 0 R 1177 0 R 1184 0 R 1193 0 R 1200 0 R 1205 0 R 1210 0 R 1215 0 R 1220 0 R ] /Count 10 /Parent 1656 0 R >> endobj 1660 0 obj << /Type /Pages /Kids [ 1224 0 R 1229 0 R 1235 0 R 1241 0 R 1249 0 R 1269 0 R 1276 0 R 1286 0 R 1293 0 R 1298 0 R ] /Count 10 /Parent 1656 0 R >> endobj 1661 0 obj << /Type /Pages /Kids [ 1303 0 R 1311 0 R 1317 0 R 1325 0 R 1331 0 R 1339 0 R 1346 0 R 1357 0 R 1364 0 R 1374 0 R ] /Count 10 /Parent 1656 0 R >> endobj 1662 0 obj << /Type /Pages /Kids [ 1387 0 R 1392 0 R 1398 0 R 1404 0 R 1412 0 R 1418 0 R 1422 0 R 1431 0 R 1441 0 R 1447 0 R ] /Count 10 /Parent 1656 0 R >> endobj 1663 0 obj << /Type /Pages /Kids [ 1451 0 R 1459 0 R 1470 0 R 1485 0 R 1492 0 R 1498 0 R 1502 0 R 1508 0 R 1513 0 R 1520 0 R ] /Count 10 /Parent 1656 0 R >> endobj 1664 0 obj << /Type /Pages /Kids [ 1525 0 R 1529 0 R 1538 0 R 1545 0 R 1549 0 R 1552 0 R 1555 0 R 1561 0 R 1568 0 R 1573 0 R ] /Count 10 /Parent 1656 0 R >> endobj 1665 0 obj << /Type /Pages /Kids [ 1576 0 R 1582 0 R 1586 0 R 1589 0 R 1592 0 R 1595 0 R 1598 0 R 1601 0 R ] /Count 8 /Parent 1656 0 R >> endobj 1666 0 obj << /Dt (D:20081127121322) /JTM (Distiller) >> endobj 1667 0 obj /This endobj 1668 0 obj << /CP (Distiller) /Fi 1667 0 R >> endobj 1669 0 obj << /R [ 600 600 ] >> endobj 1670 0 obj << /JTF 0 /MB [ 0 0 595 842 ] /R 1669 0 R /W [ 0 197 ] >> endobj 1671 0 obj << /Fi [ 1668 0 R ] /P [ 1670 0 R ] >> endobj 1672 0 obj << /Dm [ 595 842 595 842 ] >> endobj 1673 0 obj << /Me 1672 0 R >> endobj 1674 0 obj << /D [ 1671 0 R ] /MS 1673 0 R /Type /JobTicketContents >> endobj 1675 0 obj << /A [ 1666 0 R ] /Cn [ 1674 0 R ] /V 1.10001 >> endobj xref 0 1676 0000000000 65535 f
+0000777405 00000 n
+0000777607 00000 n
+0000777776 00000 n
+0000777958 00000 n
+0000778151 00000 n
+0000778316 00000 n
+0000778483 00000 n
+0000778659 00000 n
+0000778841 00000 n
+0000782316 00000 n
+0000782499 00000 n
+0000782659 00000 n
+0000782819 00000 n
+0000782988 00000 n
+0000785267 00000 n
+0000785776 00000 n
+0000785934 00000 n
+0000786092 00000 n
+0000786251 00000 n
+0000786410 00000 n
+0000786569 00000 n
+0000786728 00000 n
+0000786887 00000 n
+0000787046 00000 n
+0000787205 00000 n
+0000787364 00000 n
+0000787523 00000 n
+0000787682 00000 n
+0000787841 00000 n
+0000788000 00000 n
+0000788159 00000 n
+0000788318 00000 n
+0000788477 00000 n
+0000788636 00000 n
+0000788795 00000 n
+0000788954 00000 n
+0000789113 00000 n
+0000789272 00000 n
+0000789431 00000 n
+0000789590 00000 n
+0000789749 00000 n
+0000789908 00000 n
+0000790067 00000 n
+0000790226 00000 n
+0000790385 00000 n
+0000790544 00000 n
+0000790703 00000 n
+0000790862 00000 n
+0000791021 00000 n
+0000791180 00000 n
+0000791339 00000 n
+0000791498 00000 n
+0000791657 00000 n
+0000791816 00000 n
+0000791975 00000 n
+0000792134 00000 n
+0000792293 00000 n
+0000792452 00000 n
+0000792611 00000 n
+0000792770 00000 n
+0000792929 00000 n
+0000793087 00000 n
+0000793244 00000 n
+0000793401 00000 n
+0000793585 00000 n
+0000795949 00000 n
+0000796508 00000 n
+0000796667 00000 n
+0000796826 00000 n
+0000796985 00000 n
+0000797144 00000 n
+0000797303 00000 n
+0000797462 00000 n
+0000797621 00000 n
+0000797780 00000 n
+0000797939 00000 n
+0000798098 00000 n
+0000798257 00000 n
+0000798416 00000 n
+0000798575 00000 n
+0000798734 00000 n
+0000798893 00000 n
+0000799052 00000 n
+0000799211 00000 n
+0000799370 00000 n
+0000799529 00000 n
+0000799688 00000 n
+0000799847 00000 n
+0000800006 00000 n
+0000800165 00000 n
+0000800324 00000 n
+0000800483 00000 n
+0000800642 00000 n
+0000800801 00000 n
+0000800960 00000 n
+0000801119 00000 n
+0000801278 00000 n
+0000801437 00000 n
+0000801596 00000 n
+0000801755 00000 n
+0000801915 00000 n
+0000802075 00000 n
+0000802235 00000 n
+0000802395 00000 n
+0000802555 00000 n
+0000802715 00000 n
+0000802875 00000 n
+0000803035 00000 n
+0000803195 00000 n
+0000803355 00000 n
+0000803515 00000 n
+0000803675 00000 n
+0000803835 00000 n
+0000803995 00000 n
+0000804155 00000 n
+0000804315 00000 n
+0000804474 00000 n
+0000804632 00000 n
+0000804790 00000 n
+0000804975 00000 n
+0000808183 00000 n
+0000808753 00000 n
+0000808913 00000 n
+0000809073 00000 n
+0000809233 00000 n
+0000809393 00000 n
+0000809553 00000 n
+0000809713 00000 n
+0000809873 00000 n
+0000810033 00000 n
+0000810193 00000 n
+0000810353 00000 n
+0000810513 00000 n
+0000810673 00000 n
+0000810833 00000 n
+0000810993 00000 n
+0000811153 00000 n
+0000811313 00000 n
+0000811473 00000 n
+0000811633 00000 n
+0000811793 00000 n
+0000811953 00000 n
+0000812113 00000 n
+0000812273 00000 n
+0000812433 00000 n
+0000812593 00000 n
+0000812753 00000 n
+0000812913 00000 n
+0000813073 00000 n
+0000813233 00000 n
+0000813393 00000 n
+0000813553 00000 n
+0000813713 00000 n
+0000813873 00000 n
+0000814033 00000 n
+0000814193 00000 n
+0000814353 00000 n
+0000814513 00000 n
+0000814673 00000 n
+0000814833 00000 n
+0000814993 00000 n
+0000815153 00000 n
+0000815313 00000 n
+0000815473 00000 n
+0000815633 00000 n
+0000815793 00000 n
+0000815953 00000 n
+0000816113 00000 n
+0000816272 00000 n
+0000816430 00000 n
+0000816588 00000 n
+0000816773 00000 n
+0000820131 00000 n
+0000820652 00000 n
+0000820812 00000 n
+0000820972 00000 n
+0000821132 00000 n
+0000821292 00000 n
+0000821452 00000 n
+0000821612 00000 n
+0000821772 00000 n
+0000821932 00000 n
+0000822092 00000 n
+0000822252 00000 n
+0000822412 00000 n
+0000822572 00000 n
+0000822732 00000 n
+0000822892 00000 n
+0000823052 00000 n
+0000823212 00000 n
+0000823372 00000 n
+0000823532 00000 n
+0000823692 00000 n
+0000823852 00000 n
+0000824012 00000 n
+0000824172 00000 n
+0000824332 00000 n
+0000824492 00000 n
+0000824652 00000 n
+0000824812 00000 n
+0000824972 00000 n
+0000825132 00000 n
+0000825292 00000 n
+0000825452 00000 n
+0000825612 00000 n
+0000825772 00000 n
+0000825932 00000 n
+0000826093 00000 n
+0000826254 00000 n
+0000826415 00000 n
+0000826576 00000 n
+0000826737 00000 n
+0000826898 00000 n
+0000827059 00000 n
+0000827219 00000 n
+0000827378 00000 n
+0000827537 00000 n
+0000827708 00000 n
+0000831083 00000 n
+0000831620 00000 n
+0000831781 00000 n
+0000831942 00000 n
+0000832103 00000 n
+0000832264 00000 n
+0000832425 00000 n
+0000832586 00000 n
+0000832747 00000 n
+0000832908 00000 n
+0000833069 00000 n
+0000833230 00000 n
+0000833391 00000 n
+0000833552 00000 n
+0000833713 00000 n
+0000833874 00000 n
+0000834035 00000 n
+0000834196 00000 n
+0000834357 00000 n
+0000834518 00000 n
+0000834679 00000 n
+0000834840 00000 n
+0000835001 00000 n
+0000835162 00000 n
+0000835324 00000 n
+0000835486 00000 n
+0000835648 00000 n
+0000835810 00000 n
+0000835972 00000 n
+0000836134 00000 n
+0000836295 00000 n
+0000836456 00000 n
+0000836617 00000 n
+0000836778 00000 n
+0000836939 00000 n
+0000837100 00000 n
+0000837261 00000 n
+0000837422 00000 n
+0000837583 00000 n
+0000837744 00000 n
+0000837905 00000 n
+0000838066 00000 n
+0000838227 00000 n
+0000838388 00000 n
+0000838549 00000 n
+0000838709 00000 n
+0000838868 00000 n
+0000839039 00000 n
+0000842554 00000 n
+0000843099 00000 n
+0000843260 00000 n
+0000843421 00000 n
+0000843582 00000 n
+0000843743 00000 n
+0000843904 00000 n
+0000844065 00000 n
+0000844226 00000 n
+0000844387 00000 n
+0000844548 00000 n
+0000844709 00000 n
+0000844870 00000 n
+0000845031 00000 n
+0000845192 00000 n
+0000845353 00000 n
+0000845514 00000 n
+0000845675 00000 n
+0000845836 00000 n
+0000845997 00000 n
+0000846158 00000 n
+0000846319 00000 n
+0000846480 00000 n
+0000846641 00000 n
+0000846802 00000 n
+0000846963 00000 n
+0000847124 00000 n
+0000847285 00000 n
+0000847446 00000 n
+0000847607 00000 n
+0000847768 00000 n
+0000847929 00000 n
+0000848090 00000 n
+0000848251 00000 n
+0000848412 00000 n
+0000848573 00000 n
+0000848734 00000 n
+0000848895 00000 n
+0000849056 00000 n
+0000849217 00000 n
+0000849378 00000 n
+0000849539 00000 n
+0000849700 00000 n
+0000849861 00000 n
+0000850022 00000 n
+0000850182 00000 n
+0000850341 00000 n
+0000850500 00000 n
+0000850685 00000 n
+0000854121 00000 n
+0000854626 00000 n
+0000854787 00000 n
+0000854948 00000 n
+0000855109 00000 n
+0000855270 00000 n
+0000855431 00000 n
+0000855592 00000 n
+0000855753 00000 n
+0000855914 00000 n
+0000856075 00000 n
+0000856236 00000 n
+0000856397 00000 n
+0000856558 00000 n
+0000856719 00000 n
+0000856880 00000 n
+0000857041 00000 n
+0000857202 00000 n
+0000857363 00000 n
+0000857524 00000 n
+0000857685 00000 n
+0000857846 00000 n
+0000858007 00000 n
+0000858168 00000 n
+0000858329 00000 n
+0000858490 00000 n
+0000858651 00000 n
+0000858812 00000 n
+0000858973 00000 n
+0000859134 00000 n
+0000859295 00000 n
+0000859456 00000 n
+0000859617 00000 n
+0000859778 00000 n
+0000859939 00000 n
+0000860100 00000 n
+0000860261 00000 n
+0000860422 00000 n
+0000860583 00000 n
+0000860743 00000 n
+0000860902 00000 n
+0000861061 00000 n
+0000861220 00000 n
+0000861405 00000 n
+0000864614 00000 n
+0000864964 00000 n
+0000865125 00000 n
+0000865286 00000 n
+0000865447 00000 n
+0000865608 00000 n
+0000865769 00000 n
+0000865930 00000 n
+0000866091 00000 n
+0000866252 00000 n
+0000866413 00000 n
+0000866574 00000 n
+0000866735 00000 n
+0000866896 00000 n
+0000867057 00000 n
+0000867218 00000 n
+0000867379 00000 n
+0000867540 00000 n
+0000867701 00000 n
+0000867862 00000 n
+0000868023 00000 n
+0000868184 00000 n
+0000868345 00000 n
+0000868506 00000 n
+0000868691 00000 n
+0000870343 00000 n
+0000870579 00000 n
+0000870761 00000 n
+0000870922 00000 n
+0000871083 00000 n
+0000871244 00000 n
+0000871405 00000 n
+0000871565 00000 n
+0000871725 00000 n
+0000871883 00000 n
+0000872079 00000 n
+0000875513 00000 n
+0000875588 00000 n
+0000875857 00000 n
+0000876018 00000 n
+0000876179 00000 n
+0000876340 00000 n
+0000876500 00000 n
+0000876662 00000 n
+0000876824 00000 n
+0000876985 00000 n
+0000877146 00000 n
+0000877308 00000 n
+0000877468 00000 n
+0000877629 00000 n
+0000877788 00000 n
+0000877984 00000 n
+0000881768 00000 n
+0000882045 00000 n
+0000882205 00000 n
+0000882366 00000 n
+0000882527 00000 n
+0000882688 00000 n
+0000882848 00000 n
+0000883008 00000 n
+0000883169 00000 n
+0000883330 00000 n
+0000883491 00000 n
+0000883652 00000 n
+0000883813 00000 n
+0000883974 00000 n
+0000884134 00000 n
+0000884330 00000 n
+0000887750 00000 n
+0000887938 00000 n
+0000888100 00000 n
+0000888262 00000 n
+0000888471 00000 n
+0000891954 00000 n
+0000892113 00000 n
+0000892268 00000 n
+0000893007 00000 n
+0000893203 00000 n
+0000893364 00000 n
+0000893525 00000 n
+0000893686 00000 n
+0000893882 00000 n
+0000897128 00000 n
+0000897332 00000 n
+0000897493 00000 n
+0000897655 00000 n
+0000897816 00000 n
+0000897978 00000 n
+0000898173 00000 n
+0000901266 00000 n
+0000901425 00000 n
+0000901635 00000 n
+0000904295 00000 n
+0000904454 00000 n
+0000904636 00000 n
+0000906078 00000 n
+0000906306 00000 n
+0000906467 00000 n
+0000906628 00000 n
+0000906789 00000 n
+0000906950 00000 n
+0000907111 00000 n
+0000907272 00000 n
+0000907433 00000 n
+0000907645 00000 n
+0000910647 00000 n
+0000910843 00000 n
+0000911004 00000 n
+0000911165 00000 n
+0000911326 00000 n
+0000911538 00000 n
+0000914189 00000 n
+0000914369 00000 n
+0000914530 00000 n
+0000914755 00000 n
+0000917168 00000 n
+0000917327 00000 n
+0000917523 00000 n
+0000920969 00000 n
+0000921238 00000 n
+0000921399 00000 n
+0000921561 00000 n
+0000921723 00000 n
+0000921885 00000 n
+0000922047 00000 n
+0000922208 00000 n
+0000922369 00000 n
+0000922530 00000 n
+0000922692 00000 n
+0000922853 00000 n
+0000923015 00000 n
+0000923175 00000 n
+0000923372 00000 n
+0000927691 00000 n
+0000927936 00000 n
+0000928096 00000 n
+0000928258 00000 n
+0000928419 00000 n
+0000928580 00000 n
+0000928741 00000 n
+0000928902 00000 n
+0000929063 00000 n
+0000929224 00000 n
+0000929384 00000 n
+0000929596 00000 n
+0000932737 00000 n
+0000932933 00000 n
+0000933114 00000 n
+0000933274 00000 n
+0000933435 00000 n
+0000933619 00000 n
+0000936477 00000 n
+0000936657 00000 n
+0000936818 00000 n
+0000937028 00000 n
+0000939595 00000 n
+0000939807 00000 n
+0000939968 00000 n
+0000940129 00000 n
+0000940290 00000 n
+0000940451 00000 n
+0000940612 00000 n
+0000940824 00000 n
+0000944392 00000 n
+0000944588 00000 n
+0000944749 00000 n
+0000944910 00000 n
+0000945071 00000 n
+0000945269 00000 n
+0000948154 00000 n
+0000948334 00000 n
+0000948494 00000 n
+0000948719 00000 n
+0000951431 00000 n
+0000951635 00000 n
+0000951796 00000 n
+0000951956 00000 n
+0000952117 00000 n
+0000952278 00000 n
+0000952503 00000 n
+0000956205 00000 n
+0000956393 00000 n
+0000956554 00000 n
+0000956715 00000 n
+0000956940 00000 n
+0000960003 00000 n
+0000960162 00000 n
+0000960331 00000 n
+0000962324 00000 n
+0000962504 00000 n
+0000962665 00000 n
+0000962849 00000 n
+0000965540 00000 n
+0000965736 00000 n
+0000965897 00000 n
+0000966057 00000 n
+0000966219 00000 n
+0000966444 00000 n
+0000969812 00000 n
+0000969992 00000 n
+0000970159 00000 n
+0000970344 00000 n
+0000976045 00000 n
+0000976225 00000 n
+0000976386 00000 n
+0000976571 00000 n
+0000980900 00000 n
+0000981120 00000 n
+0000981281 00000 n
+0000981442 00000 n
+0000981603 00000 n
+0000981764 00000 n
+0000981925 00000 n
+0000982086 00000 n
+0000982257 00000 n
+0000987971 00000 n
+0000988207 00000 n
+0000988368 00000 n
+0000988529 00000 n
+0000988690 00000 n
+0000988851 00000 n
+0000989012 00000 n
+0000989173 00000 n
+0000989335 00000 n
+0000989497 00000 n
+0000989709 00000 n
+0000993036 00000 n
+0000993264 00000 n
+0000993425 00000 n
+0000993586 00000 n
+0000993747 00000 n
+0000993908 00000 n
+0000994069 00000 n
+0000994230 00000 n
+0000994391 00000 n
+0000994576 00000 n
+0000998924 00000 n
+0000999160 00000 n
+0000999321 00000 n
+0000999482 00000 n
+0000999643 00000 n
+0000999804 00000 n
+0000999965 00000 n
+0001000126 00000 n
+0001000287 00000 n
+0001000446 00000 n
+0001000631 00000 n
+0001005142 00000 n
+0001005395 00000 n
+0001005556 00000 n
+0001005717 00000 n
+0001005879 00000 n
+0001006041 00000 n
+0001006202 00000 n
+0001006364 00000 n
+0001006525 00000 n
+0001006686 00000 n
+0001006847 00000 n
+0001007007 00000 n
+0001007178 00000 n
+0001011825 00000 n
+0001012037 00000 n
+0001012199 00000 n
+0001012361 00000 n
+0001012523 00000 n
+0001012683 00000 n
+0001012845 00000 n
+0001013030 00000 n
+0001015891 00000 n
+0001016087 00000 n
+0001016248 00000 n
+0001016409 00000 n
+0001016571 00000 n
+0001016783 00000 n
+0001021292 00000 n
+0001021496 00000 n
+0001021657 00000 n
+0001021818 00000 n
+0001021979 00000 n
+0001022140 00000 n
+0001022352 00000 n
+0001026899 00000 n
+0001027058 00000 n
+0001027283 00000 n
+0001031216 00000 n
+0001031375 00000 n
+0001031600 00000 n
+0001034592 00000 n
+0001034796 00000 n
+0001034957 00000 n
+0001035118 00000 n
+0001035279 00000 n
+0001035441 00000 n
+0001035639 00000 n
+0001039046 00000 n
+0001039226 00000 n
+0001039387 00000 n
+0001039543 00000 n
+0001040448 00000 n
+0001040636 00000 n
+0001040795 00000 n
+0001040956 00000 n
+0001041139 00000 n
+0001043509 00000 n
+0001043705 00000 n
+0001043866 00000 n
+0001044027 00000 n
+0001044185 00000 n
+0001044355 00000 n
+0001046456 00000 n
+0001046676 00000 n
+0001046837 00000 n
+0001046997 00000 n
+0001047158 00000 n
+0001047319 00000 n
+0001047480 00000 n
+0001047639 00000 n
+0001047851 00000 n
+0001050183 00000 n
+0001050387 00000 n
+0001050548 00000 n
+0001050709 00000 n
+0001050870 00000 n
+0001051031 00000 n
+0001051243 00000 n
+0001053478 00000 n
+0001053658 00000 n
+0001053819 00000 n
+0001054029 00000 n
+0001057122 00000 n
+0001057302 00000 n
+0001057463 00000 n
+0001057645 00000 n
+0001061843 00000 n
+0001062002 00000 n
+0001062212 00000 n
+0001064829 00000 n
+0001064988 00000 n
+0001065218 00000 n
+0001067321 00000 n
+0001074061 00000 n
+0001074249 00000 n
+0001074410 00000 n
+0001074571 00000 n
+0001074767 00000 n
+0001076998 00000 n
+0001077178 00000 n
+0001077339 00000 n
+0001077535 00000 n
+0001080070 00000 n
+0001080258 00000 n
+0001080418 00000 n
+0001080579 00000 n
+0001080789 00000 n
+0001083516 00000 n
+0001083712 00000 n
+0001083873 00000 n
+0001084033 00000 n
+0001084194 00000 n
+0001084390 00000 n
+0001087233 00000 n
+0001087429 00000 n
+0001087590 00000 n
+0001087751 00000 n
+0001087912 00000 n
+0001088082 00000 n
+0001090157 00000 n
+0001090353 00000 n
+0001090514 00000 n
+0001090676 00000 n
+0001090837 00000 n
+0001091020 00000 n
+0001093095 00000 n
+0001093254 00000 n
+0001093479 00000 n
+0001096193 00000 n
+0001096352 00000 n
+0001096548 00000 n
+0001099312 00000 n
+0001099471 00000 n
+0001099667 00000 n
+0001101924 00000 n
+0001102083 00000 n
+0001102279 00000 n
+0001104464 00000 n
+0001104644 00000 n
+0001104805 00000 n
+0001105001 00000 n
+0001108171 00000 n
+0001108383 00000 n
+0001108545 00000 n
+0001108704 00000 n
+0001108866 00000 n
+0001109028 00000 n
+0001109187 00000 n
+0001109383 00000 n
+0001112560 00000 n
+0001112748 00000 n
+0001112909 00000 n
+0001113070 00000 n
+0001113252 00000 n
+0001117368 00000 n
+0001117527 00000 n
+0001117752 00000 n
+0001120853 00000 n
+0001121073 00000 n
+0001121234 00000 n
+0001121395 00000 n
+0001121556 00000 n
+0001121717 00000 n
+0001121878 00000 n
+0001122037 00000 n
+0001122249 00000 n
+0001124967 00000 n
+0001125163 00000 n
+0001125324 00000 n
+0001125485 00000 n
+0001125646 00000 n
+0001125885 00000 n
+0001129351 00000 n
+0001129555 00000 n
+0001129716 00000 n
+0001129877 00000 n
+0001130039 00000 n
+0001130200 00000 n
+0001130412 00000 n
+0001134073 00000 n
+0001134253 00000 n
+0001134414 00000 n
+0001134611 00000 n
+0001138527 00000 n
+0001138723 00000 n
+0001138884 00000 n
+0001139045 00000 n
+0001139206 00000 n
+0001139417 00000 n
+0001143354 00000 n
+0001143542 00000 n
+0001143703 00000 n
+0001143863 00000 n
+0001144088 00000 n
+0001147483 00000 n
+0001147679 00000 n
+0001147840 00000 n
+0001148001 00000 n
+0001148162 00000 n
+0001148387 00000 n
+0001151563 00000 n
+0001151722 00000 n
+0001151947 00000 n
+0001155210 00000 n
+0001155390 00000 n
+0001155551 00000 n
+0001155762 00000 n
+0001159838 00000 n
+0001160026 00000 n
+0001160187 00000 n
+0001160349 00000 n
+0001160560 00000 n
+0001163353 00000 n
+0001163541 00000 n
+0001163702 00000 n
+0001163861 00000 n
+0001164071 00000 n
+0001166466 00000 n
+0001166654 00000 n
+0001166815 00000 n
+0001166974 00000 n
+0001167170 00000 n
+0001169695 00000 n
+0001169883 00000 n
+0001170044 00000 n
+0001170205 00000 n
+0001170430 00000 n
+0001173116 00000 n
+0001173304 00000 n
+0001173465 00000 n
+0001173624 00000 n
+0001173836 00000 n
+0001177983 00000 n
+0001178187 00000 n
+0001178348 00000 n
+0001178509 00000 n
+0001178670 00000 n
+0001178831 00000 n
+0001179041 00000 n
+0001181951 00000 n
+0001182131 00000 n
+0001182291 00000 n
+0001182474 00000 n
+0001184892 00000 n
+0001185072 00000 n
+0001185233 00000 n
+0001185403 00000 n
+0001187781 00000 n
+0001188001 00000 n
+0001188162 00000 n
+0001188323 00000 n
+0001188484 00000 n
+0001188645 00000 n
+0001188806 00000 n
+0001188968 00000 n
+0001189153 00000 n
+0001191618 00000 n
+0001191830 00000 n
+0001191991 00000 n
+0001192152 00000 n
+0001192314 00000 n
+0001192475 00000 n
+0001192637 00000 n
+0001192835 00000 n
+0001196547 00000 n
+0001196727 00000 n
+0001196888 00000 n
+0001197086 00000 n
+0001200435 00000 n
+0001200647 00000 n
+0001200809 00000 n
+0001200971 00000 n
+0001201132 00000 n
+0001201293 00000 n
+0001201454 00000 n
+0001201639 00000 n
+0001205601 00000 n
+0001205789 00000 n
+0001205950 00000 n
+0001206111 00000 n
+0001206309 00000 n
+0001209311 00000 n
+0001209491 00000 n
+0001209652 00000 n
+0001209864 00000 n
+0001213091 00000 n
+0001213250 00000 n
+0001213392 00000 n
+0001214378 00000 n
+0001214623 00000 n
+0001214785 00000 n
+0001214947 00000 n
+0001215109 00000 n
+0001215271 00000 n
+0001215433 00000 n
+0001215595 00000 n
+0001215757 00000 n
+0001215919 00000 n
+0001216081 00000 n
+0001216277 00000 n
+0001219054 00000 n
+0001219242 00000 n
+0001219404 00000 n
+0001219565 00000 n
+0001219735 00000 n
+0001222585 00000 n
+0001222773 00000 n
+0001222934 00000 n
+0001223096 00000 n
+0001223292 00000 n
+0001226291 00000 n
+0001226471 00000 n
+0001226633 00000 n
+0001226816 00000 n
+0001229526 00000 n
+0001229722 00000 n
+0001229883 00000 n
+0001230045 00000 n
+0001230206 00000 n
+0001230376 00000 n
+0001231846 00000 n
+0001232005 00000 n
+0001232190 00000 n
+0001235862 00000 n
+0001236050 00000 n
+0001236211 00000 n
+0001236372 00000 n
+0001236542 00000 n
+0001238536 00000 n
+0001238695 00000 n
+0001238865 00000 n
+0001240938 00000 n
+0001241142 00000 n
+0001241304 00000 n
+0001241465 00000 n
+0001241626 00000 n
+0001241787 00000 n
+0001241972 00000 n
+0001244211 00000 n
+0001244399 00000 n
+0001244560 00000 n
+0001244721 00000 n
+0001244906 00000 n
+0001247473 00000 n
+0001247718 00000 n
+0001247879 00000 n
+0001248041 00000 n
+0001248200 00000 n
+0001248362 00000 n
+0001248524 00000 n
+0001248685 00000 n
+0001248847 00000 n
+0001249008 00000 n
+0001249169 00000 n
+0001249354 00000 n
+0001251895 00000 n
+0001252083 00000 n
+0001252245 00000 n
+0001252406 00000 n
+0001252577 00000 n
+0001255059 00000 n
+0001255271 00000 n
+0001255432 00000 n
+0001255594 00000 n
+0001255756 00000 n
+0001255917 00000 n
+0001256079 00000 n
+0001256264 00000 n
+0001259376 00000 n
+0001259612 00000 n
+0001259773 00000 n
+0001259935 00000 n
+0001260097 00000 n
+0001260259 00000 n
+0001260421 00000 n
+0001260583 00000 n
+0001260745 00000 n
+0001260905 00000 n
+0001261076 00000 n
+0001265690 00000 n
+0001265886 00000 n
+0001266048 00000 n
+0001266210 00000 n
+0001266372 00000 n
+0001266570 00000 n
+0001269110 00000 n
+0001269298 00000 n
+0001269459 00000 n
+0001269621 00000 n
+0001269806 00000 n
+0001271933 00000 n
+0001272123 00000 n
+0001272284 00000 n
+0001272445 00000 n
+0001272616 00000 n
+0001275073 00000 n
+0001275257 00000 n
+0001275419 00000 n
+0001275616 00000 n
+0001278691 00000 n
+0001278902 00000 n
+0001279065 00000 n
+0001279226 00000 n
+0001279388 00000 n
+0001279551 00000 n
+0001279762 00000 n
+0001282549 00000 n
+0001282806 00000 n
+0001282968 00000 n
+0001283130 00000 n
+0001283292 00000 n
+0001283454 00000 n
+0001283616 00000 n
+0001283778 00000 n
+0001283940 00000 n
+0001284102 00000 n
+0001284264 00000 n
+0001284450 00000 n
+0001286717 00000 n
+0001286955 00000 n
+0001287117 00000 n
+0001287279 00000 n
+0001287441 00000 n
+0001287603 00000 n
+0001287765 00000 n
+0001287927 00000 n
+0001288089 00000 n
+0001288275 00000 n
+0001291069 00000 n
+0001291317 00000 n
+0001291480 00000 n
+0001291642 00000 n
+0001291805 00000 n
+0001291967 00000 n
+0001292129 00000 n
+0001292291 00000 n
+0001292453 00000 n
+0001292615 00000 n
+0001292801 00000 n
+0001296859 00000 n
+0001297170 00000 n
+0001297332 00000 n
+0001297494 00000 n
+0001297656 00000 n
+0001297818 00000 n
+0001297980 00000 n
+0001298142 00000 n
+0001298304 00000 n
+0001298466 00000 n
+0001298628 00000 n
+0001298790 00000 n
+0001298952 00000 n
+0001299114 00000 n
+0001299277 00000 n
+0001299439 00000 n
+0001299600 00000 n
+0001299772 00000 n
+0001303845 00000 n
+0001304111 00000 n
+0001304294 00000 n
+0001304457 00000 n
+0001304620 00000 n
+0001304782 00000 n
+0001304944 00000 n
+0001305107 00000 n
+0001305269 00000 n
+0001305432 00000 n
+0001305594 00000 n
+0001305756 00000 n
+0001305928 00000 n
+0001310605 00000 n
+0001310668 00000 n
+0001310870 00000 n
+0001311033 00000 n
+0001311196 00000 n
+0001311359 00000 n
+0001311545 00000 n
+0001313759 00000 n
+0001313997 00000 n
+0001314160 00000 n
+0001314323 00000 n
+0001314486 00000 n
+0001314649 00000 n
+0001314811 00000 n
+0001314973 00000 n
+0001315136 00000 n
+0001315322 00000 n
+0001317523 00000 n
+0001317734 00000 n
+0001317897 00000 n
+0001318060 00000 n
+0001318223 00000 n
+0001318385 00000 n
+0001318571 00000 n
+0001322401 00000 n
+0001322630 00000 n
+0001322793 00000 n
+0001322956 00000 n
+0001323119 00000 n
+0001323282 00000 n
+0001323444 00000 n
+0001323606 00000 n
+0001323777 00000 n
+0001326229 00000 n
+0001326431 00000 n
+0001326594 00000 n
+0001326757 00000 n
+0001326919 00000 n
+0001327132 00000 n
+0001330054 00000 n
+0001330238 00000 n
+0001330401 00000 n
+0001330614 00000 n
+0001333119 00000 n
+0001333312 00000 n
+0001333475 00000 n
+0001333638 00000 n
+0001333809 00000 n
+0001336158 00000 n
+0001336415 00000 n
+0001336578 00000 n
+0001336741 00000 n
+0001336904 00000 n
+0001337067 00000 n
+0001337229 00000 n
+0001337392 00000 n
+0001337555 00000 n
+0001337718 00000 n
+0001337879 00000 n
+0001338050 00000 n
+0001340591 00000 n
+0001340829 00000 n
+0001340989 00000 n
+0001341152 00000 n
+0001341315 00000 n
+0001341478 00000 n
+0001341641 00000 n
+0001341803 00000 n
+0001341965 00000 n
+0001342163 00000 n
+0001346214 00000 n
+0001346434 00000 n
+0001346597 00000 n
+0001346760 00000 n
+0001346922 00000 n
+0001347085 00000 n
+0001347247 00000 n
+0001347445 00000 n
+0001351517 00000 n
+0001351755 00000 n
+0001351918 00000 n
+0001352081 00000 n
+0001352244 00000 n
+0001352407 00000 n
+0001352569 00000 n
+0001352732 00000 n
+0001352893 00000 n
+0001353119 00000 n
+0001356544 00000 n
+0001356801 00000 n
+0001356964 00000 n
+0001357127 00000 n
+0001357290 00000 n
+0001357453 00000 n
+0001357616 00000 n
+0001357778 00000 n
+0001357941 00000 n
+0001358104 00000 n
+0001358267 00000 n
+0001358453 00000 n
+0001362032 00000 n
+0001362243 00000 n
+0001362406 00000 n
+0001362569 00000 n
+0001362731 00000 n
+0001362894 00000 n
+0001363093 00000 n
+0001366168 00000 n
+0001366397 00000 n
+0001366560 00000 n
+0001366723 00000 n
+0001366886 00000 n
+0001367049 00000 n
+0001367211 00000 n
+0001367374 00000 n
+0001367560 00000 n
+0001370536 00000 n
+0001370747 00000 n
+0001370910 00000 n
+0001371073 00000 n
+0001371236 00000 n
+0001371399 00000 n
+0001371598 00000 n
+0001374663 00000 n
+0001374856 00000 n
+0001375018 00000 n
+0001375180 00000 n
+0001375366 00000 n
+0001377797 00000 n
+0001377990 00000 n
+0001378153 00000 n
+0001378315 00000 n
+0001378528 00000 n
+0001381286 00000 n
+0001381479 00000 n
+0001381641 00000 n
+0001381804 00000 n
+0001382030 00000 n
+0001384378 00000 n
+0001384571 00000 n
+0001384733 00000 n
+0001384895 00000 n
+0001385081 00000 n
+0001387311 00000 n
+0001387495 00000 n
+0001387658 00000 n
+0001387844 00000 n
+0001390412 00000 n
+0001390605 00000 n
+0001390766 00000 n
+0001390928 00000 n
+0001391099 00000 n
+0001393155 00000 n
+0001393357 00000 n
+0001393519 00000 n
+0001393681 00000 n
+0001393844 00000 n
+0001394015 00000 n
+0001396190 00000 n
+0001396392 00000 n
+0001396554 00000 n
+0001396716 00000 n
+0001396878 00000 n
+0001397049 00000 n
+0001398848 00000 n
+0001399068 00000 n
+0001399231 00000 n
+0001399393 00000 n
+0001399556 00000 n
+0001399719 00000 n
+0001399882 00000 n
+0001400108 00000 n
+0001403283 00000 n
+0001403613 00000 n
+0001403776 00000 n
+0001403938 00000 n
+0001404101 00000 n
+0001404264 00000 n
+0001404426 00000 n
+0001404589 00000 n
+0001404751 00000 n
+0001404914 00000 n
+0001405076 00000 n
+0001405239 00000 n
+0001405401 00000 n
+0001405564 00000 n
+0001405727 00000 n
+0001405889 00000 n
+0001406052 00000 n
+0001406215 00000 n
+0001406376 00000 n
+0001406562 00000 n
+0001410381 00000 n
+0001410592 00000 n
+0001410755 00000 n
+0001410917 00000 n
+0001411079 00000 n
+0001411240 00000 n
+0001411426 00000 n
+0001415236 00000 n
+0001415474 00000 n
+0001415636 00000 n
+0001415799 00000 n
+0001415962 00000 n
+0001416124 00000 n
+0001416287 00000 n
+0001416449 00000 n
+0001416612 00000 n
+0001416798 00000 n
+0001420795 00000 n
+0001421006 00000 n
+0001421169 00000 n
+0001421332 00000 n
+0001421494 00000 n
+0001421657 00000 n
+0001421843 00000 n
+0001425646 00000 n
+0001425839 00000 n
+0001426002 00000 n
+0001426165 00000 n
+0001426364 00000 n
+0001428816 00000 n
+0001429009 00000 n
+0001429172 00000 n
+0001429334 00000 n
+0001429505 00000 n
+0001431530 00000 n
+0001431750 00000 n
+0001431913 00000 n
+0001432074 00000 n
+0001432237 00000 n
+0001432399 00000 n
+0001432559 00000 n
+0001432756 00000 n
+0001435451 00000 n
+0001435653 00000 n
+0001435814 00000 n
+0001435977 00000 n
+0001436140 00000 n
+0001436337 00000 n
+0001439033 00000 n
+0001439253 00000 n
+0001439416 00000 n
+0001439579 00000 n
+0001439742 00000 n
+0001439905 00000 n
+0001440066 00000 n
+0001440279 00000 n
+0001444093 00000 n
+0001444295 00000 n
+0001444458 00000 n
+0001444621 00000 n
+0001444784 00000 n
+0001444995 00000 n
+0001447717 00000 n
+0001447937 00000 n
+0001448100 00000 n
+0001448263 00000 n
+0001448426 00000 n
+0001448589 00000 n
+0001448752 00000 n
+0001448963 00000 n
+0001452033 00000 n
+0001452244 00000 n
+0001452407 00000 n
+0001452569 00000 n
+0001452731 00000 n
+0001452892 00000 n
+0001453076 00000 n
+0001455569 00000 n
+0001455817 00000 n
+0001455980 00000 n
+0001456143 00000 n
+0001456306 00000 n
+0001456469 00000 n
+0001456631 00000 n
+0001456794 00000 n
+0001456957 00000 n
+0001457119 00000 n
+0001457290 00000 n
+0001460104 00000 n
+0001460315 00000 n
+0001460478 00000 n
+0001460641 00000 n
+0001460804 00000 n
+0001460967 00000 n
+0001461193 00000 n
+0001464189 00000 n
+0001464427 00000 n
+0001464590 00000 n
+0001464753 00000 n
+0001464916 00000 n
+0001465079 00000 n
+0001465242 00000 n
+0001465405 00000 n
+0001465568 00000 n
+0001465725 00000 n
+0001468015 00000 n
+0001468281 00000 n
+0001468444 00000 n
+0001468607 00000 n
+0001468770 00000 n
+0001468933 00000 n
+0001469096 00000 n
+0001469259 00000 n
+0001469422 00000 n
+0001469585 00000 n
+0001469748 00000 n
+0001469909 00000 n
+0001470092 00000 n
+0001473580 00000 n
+0001473773 00000 n
+0001473936 00000 n
+0001474099 00000 n
+0001474309 00000 n
+0001477367 00000 n
+0001477569 00000 n
+0001477732 00000 n
+0001477895 00000 n
+0001478056 00000 n
+0001478269 00000 n
+0001480923 00000 n
+0001481125 00000 n
+0001481288 00000 n
+0001481451 00000 n
+0001481614 00000 n
+0001481826 00000 n
+0001485561 00000 n
+0001485781 00000 n
+0001485944 00000 n
+0001486107 00000 n
+0001486268 00000 n
+0001486431 00000 n
+0001486592 00000 n
+0001486805 00000 n
+0001489679 00000 n
+0001489881 00000 n
+0001490039 00000 n
+0001490201 00000 n
+0001490364 00000 n
+0001490561 00000 n
+0001493196 00000 n
+0001493380 00000 n
+0001493541 00000 n
+0001493712 00000 n
+0001495596 00000 n
+0001495825 00000 n
+0001495988 00000 n
+0001496151 00000 n
+0001496314 00000 n
+0001496476 00000 n
+0001496639 00000 n
+0001496802 00000 n
+0001497028 00000 n
+0001500322 00000 n
+0001500560 00000 n
+0001500723 00000 n
+0001500886 00000 n
+0001501049 00000 n
+0001501212 00000 n
+0001501375 00000 n
+0001501538 00000 n
+0001501701 00000 n
+0001501887 00000 n
+0001505019 00000 n
+0001505221 00000 n
+0001505384 00000 n
+0001505547 00000 n
+0001505710 00000 n
+0001505907 00000 n
+0001508779 00000 n
+0001508963 00000 n
+0001509126 00000 n
+0001509338 00000 n
+0001513550 00000 n
+0001513770 00000 n
+0001513933 00000 n
+0001514093 00000 n
+0001514256 00000 n
+0001514419 00000 n
+0001514581 00000 n
+0001514792 00000 n
+0001518831 00000 n
+0001519079 00000 n
+0001519241 00000 n
+0001519404 00000 n
+0001519566 00000 n
+0001519729 00000 n
+0001519892 00000 n
+0001520055 00000 n
+0001520218 00000 n
+0001520381 00000 n
+0001520594 00000 n
+0001524154 00000 n
+0001524438 00000 n
+0001524601 00000 n
+0001524764 00000 n
+0001524926 00000 n
+0001525089 00000 n
+0001525252 00000 n
+0001525415 00000 n
+0001525577 00000 n
+0001525739 00000 n
+0001525902 00000 n
+0001526065 00000 n
+0001526228 00000 n
+0001526391 00000 n
+0001526563 00000 n
+0001530403 00000 n
+0001530614 00000 n
+0001530777 00000 n
+0001530940 00000 n
+0001531102 00000 n
+0001531264 00000 n
+0001531477 00000 n
+0001534108 00000 n
+0001534310 00000 n
+0001534473 00000 n
+0001534636 00000 n
+0001534798 00000 n
+0001534984 00000 n
+0001537125 00000 n
+0001537309 00000 n
+0001537472 00000 n
+0001537643 00000 n
+0001538492 00000 n
+0001538694 00000 n
+0001538856 00000 n
+0001539019 00000 n
+0001539182 00000 n
+0001539408 00000 n
+0001541938 00000 n
+0001542131 00000 n
+0001542294 00000 n
+0001542456 00000 n
+0001542640 00000 n
+0001544812 00000 n
+0001545023 00000 n
+0001545186 00000 n
+0001545349 00000 n
+0001545512 00000 n
+0001545673 00000 n
+0001545885 00000 n
+0001548454 00000 n
+0001548647 00000 n
+0001548809 00000 n
+0001548971 00000 n
+0001549183 00000 n
+0001551990 00000 n
+0001552174 00000 n
+0001552336 00000 n
+0001552507 00000 n
+0001554600 00000 n
+0001554829 00000 n
+0001555012 00000 n
+0001555174 00000 n
+0001555337 00000 n
+0001555500 00000 n
+0001555662 00000 n
+0001555822 00000 n
+0001555993 00000 n
+0001557766 00000 n
+0001557977 00000 n
+0001558139 00000 n
+0001558302 00000 n
+0001558465 00000 n
+0001558627 00000 n
+0001558798 00000 n
+0001561039 00000 n
+0001561223 00000 n
+0001561386 00000 n
+0001561557 00000 n
+0001563485 00000 n
+0001563647 00000 n
+0001563844 00000 n
+0001566130 00000 n
+0001566292 00000 n
+0001566489 00000 n
+0001567972 00000 n
+0001568174 00000 n
+0001568336 00000 n
+0001568496 00000 n
+0001568659 00000 n
+0001568856 00000 n
+0001571651 00000 n
+0001571862 00000 n
+0001572025 00000 n
+0001572187 00000 n
+0001572349 00000 n
+0001572510 00000 n
+0001572681 00000 n
+0001574689 00000 n
+0001574882 00000 n
+0001575044 00000 n
+0001575205 00000 n
+0001575376 00000 n
+0001576334 00000 n
+0001576496 00000 n
+0001576667 00000 n
+0001581181 00000 n
+0001581383 00000 n
+0001581593 00000 n
+0001581802 00000 n
+0001581964 00000 n
+0001582135 00000 n
+0001584192 00000 n
+0001584376 00000 n
+0001584539 00000 n
+0001584710 00000 n
+0001587098 00000 n
+0001587260 00000 n
+0001587417 00000 n
+0001588416 00000 n
+0001588578 00000 n
+0001588735 00000 n
+0001591694 00000 n
+0001591856 00000 n
+0001591999 00000 n
+0001595461 00000 n
+0001595623 00000 n
+0001595766 00000 n
+0001596919 00000 n
+0001597081 00000 n
+0001597224 00000 n
+0001600074 00000 n
+0001600236 00000 n
+0001600379 00000 n
+0001602254 00000 n
+0001602725 00000 n
+0001603340 00000 n
+0001603568 00000 n
+0001603761 00000 n
+0001603946 00000 n
+0001604735 00000 n
+0001605529 00000 n
+0001606030 00000 n
+0001606084 00000 n
+0001606388 00000 n
+0001606573 00000 n
+0001606749 00000 n
+0001606934 00000 n
+0001607579 00000 n
+0001614183 00000 n
+0001614407 00000 n
+0001614751 00000 n
+0001614965 00000 n
+0001615139 00000 n
+0001615523 00000 n
+0001618642 00000 n
+0001618856 00000 n
+0001619030 00000 n
+0001619252 00000 n
+0001619489 00000 n
+0001619703 00000 n
+0001619877 00000 n
+0001620088 00000 n
+0001641252 00000 n
+0001641470 00000 n
+0001677339 00000 n
+0001677560 00000 n
+0001685945 00000 n
+0001686165 00000 n
+0001714777 00000 n
+0001714848 00000 n
+0001715132 00000 n
+0001715207 00000 n
+0001715457 00000 n
+0001715614 00000 n
+0001715786 00000 n
+0001715947 00000 n
+0001716108 00000 n
+0001716269 00000 n
+0001716430 00000 n
+0001716591 00000 n
+0001716752 00000 n
+0001716913 00000 n
+0001717074 00000 n
+0001717235 00000 n
+0001717316 00000 n
+0001717477 00000 n
+0001717648 00000 n
+0001717816 00000 n
+0001717987 00000 n
+0001718158 00000 n
+0001718329 00000 n
+0001718500 00000 n
+0001718671 00000 n
+0001718842 00000 n
+0001719013 00000 n
+0001719165 00000 n
+0001719231 00000 n
+0001719256 00000 n
+0001719312 00000 n
+0001719354 00000 n
+0001719436 00000 n
+0001719497 00000 n
+0001719548 00000 n
+0001719588 00000 n
+0001719671 00000 n
+trailer << /Size 1676 /ID[<1b0c5acde4b3a254aa19b761e04c3766><1b0c5acde4b3a254aa19b761e04c3766>] >> startxref 173 %%EOF \ No newline at end of file
diff --git a/doc/engine_settings_keys.rtf b/doc/engine_settings_keys.rtf
new file mode 100755
index 0000000..1965d53
--- /dev/null
+++ b/doc/engine_settings_keys.rtf
@@ -0,0 +1,142 @@
+{\rtf1\ansi\ansicpg1252\cocoartf949\cocoasubrtf430
+{\fonttbl\f0\fswiss\fcharset0 ArialMT;\f1\froman\fcharset0 TimesNewRomanPSMT;}
+{\colortbl;\red255\green255\blue255;}
+{\info
+{\author Lukas Zeller}
+{\*\company Sherwood Forest}}\paperw11905\paperh16837\margl1417\margr1417\margb1134\margt1417\vieww19240\viewh20720\viewkind0
+\deftab720
+\pard\tx2414\pardeftab720\li2556\fi-2556\ri0
+
+\f0\b\fs20 \cf0 /engineinfo general info about engine (read-only)\
+\pard\tx2414\pardeftab720\li2556\fi-2556\ri0
+
+\b0 \cf0 version - SySync full version string\
+ platform - name of the platform\
+\
+\pard\tx2414\pardeftab720\li2556\fi-2556\ri0
+
+\b \cf0 /configvars configuration variables (volatile)\
+\pard\tx2414\pardeftab720\li2556\fi-2556\ri0
+
+\b0 \cf0 platformname - name of the current platform\
+ platformvers - version string of the current platform\
+ globcfg_path - global system-wide config path (such as C:\\Windows or /etc)\
+ loccfg_path - local config path (such as exedir or user's dir)\
+ defout_path - default path to writable directory to write logs and other output by default\
+ temp_path - path where we can write temp files\
+ exedir_path - path to directory where executable resides\
+ userdir_path - path to the user's home directory for user-visible documents and files\
+ appdata_path - path to the user's preference directory for this application\
+ prefs_path - path to directory where all application prefs reside (not just mine)\
+ device_uri - URI of the device (as from getDeviceInfo)\
+ device_name - Name of the device (as from getDeviceInfo)\
+ user_name - name of the currently logged-in user\
+ conferrpath -for Synthesis SyncML engine library only: path of the file to output configuration parsing error messages. Can be set to "console" to di-rect the error messages to the standard output (note that a usable standard output might not exist for certain platforms).\
+\pard\tx2414\tx2414\pardeftab720\li2556\fi-2556\ri0
+\cf0 xxxx - user-defined variables (can also set to override default value of one of the above)\
+\pard\tx2414\pardeftab720\li2556\fi-2556\ri0
+\cf0 \
+\pard\tx2414\pardeftab720\li2556\fi-2556\ri0
+
+\b \cf0 /licensing license (volatile, text/code must be set every time app ist started)\
+\pard\tx2414\pardeftab720\li2556\fi-2556\ri0
+
+\b0 \cf0 licensetext - license text\
+ licensecode - Writeonly: license code (setting it will recalculate all the following status variables)\
+ regStatus - Readonly: TSyError status code of currently set license\
+ regOK - Readonly: if true, license is ok\
+ productCode - Readonly: product code from license\
+ productFlags - Readonly: product flags from license\
+ quantity - Readonly: licensed quantity\
+ licenseType - Readonly: license type\
+ daysleft - Readonly: number of days left of expiring license or demo mode (-1 = not expiring)\
+\
+\pard\tx2414\pardeftab720\li2556\fi-2556\ri0
+
+\b \cf0 /profiles Client settings profiles (persistent)\
+\pard\tx2414\pardeftab720\li2556\fi-2556\ri0
+
+\b0 \cf0 settingsstatus - TSyError status of the settings. MUST BE CALLED AT LEAST ONCE before opening subkeys\
+ overwrite - (volatile) boolean flag. In case opening settings would cause deleting incompatible settings, this is done only if overwrite is set to true.\
+\pard\tx2414\pardeftab720\li2556\fi-2556\ri0
+
+\b \cf0 /<profileID> Profile ID (as assigned by engine when profile is created)\
+\pard\tx2414\pardeftab720\li2556\fi-2556\ri0
+
+\b0 \cf0 profileName - display name of the profile\
+ protocol - transport protocol: 0=included in URI, 1=http, 2=https, 3=wsp, 4=obex_irda, 5=obex_bt, 6=obex_tcp\
+ serverURI - SyncML Server URI\
+ URIpath - Path element appended to SyncML Server URI (e.g. in case URI is hardcoded)\
+ serverUser - SyncML Server user\
+ serverPassword - SyncML Server password (stored in disguised form)\
+ transportUser - user for login at the transport level (e.g. HTTP auth)\
+ transportPassword - password for transport level login (stored in disguised form)\
+ socksHost - SOCKS proxy address\
+ proxyHost - HTTP proxy address\
+ proxyUser - user for login at the proxy\
+ proxyPassword - password for proxy login (stored in disguised form)\
+ encoding - SyncML encoding (1=WBXML, 2=XML - note that not some client builds only support WBXML)\
+ syncmlvers - SyncML version to use to start session (0=automatic, 1=1.0, 2=1.1, 3=1.2)\
+ useProxy - If set to true, this indicates that configured proxy server(s) should be used\
+ useConnectionProxy - if set to true, this indicates that OS-defined, connection specific proxies should be used\
+ timedSyncMobile - Number of minutes for mobile timed autosync (0=none)\
+ timedSyncCradled - Number of minutes for cradled timed autosync (0=none)\
+\pard\tx2414\pardeftab720\li2556\fi-2556\ri0
+
+\b \cf0 \
+ /autosynclevels\
+ /<id> Autosync level ID, 0..2, 0=first priority, 2=least priority\
+\pard\tx2414\pardeftab720\li2556\fi-2556\ri0
+
+\b0 \cf0 mode - Autosync mode for this level (0=IPP, 1=timed, 2=off, 3=server alerted)\
+ startDayTime - minute of the day when autosync starts in this level\
+ endDayTime - minute of the day when autosync ends in this level\
+ weekdayMask - weekdays where autosync is enabled in this level (Bit 0=Sun, 1=Mon .. 6=Sat\
+ chargeLevel - percentage of battery charge needed to enable autosync (0..100, 100=with AC supply only)\
+ memLevel - percentage of memory free needed to enable autosync (0..100)\
+ flags - flags reserved for future use\
+\pard\tx2414\pardeftab720\li2556\fi-2556\ri0
+
+\b \cf0 \
+ /targets Targets (databases available for sync in this profile)\
+ <targetID> Target ID is the <dbtypeid> as defined in the <datastore> config\
+\pard\tx2414\pardeftab720\li2556\fi-2556\ri0
+
+\b0 \cf0 enabled - if set to true, this datastore will be included in next sync\
+ forceslow - if set to true, next sync will be a slow sync\
+ syncmode - sync mode: 0=twoway, 1=from server only, 2=from client only\
+ limit1 - sync range limit (such as number of days in the past, depends on datastore)\
+ limit2 - sync range limit (such as number of days in the future, depends on datastore)\
+ extras - flags for sync range limist (depends on datastore)\
+ localpath - local database path (if any), to differentiate multiple instances of the same database type\
+ remotepath - remote (server) database path\
+ localcontainer - local container name, if any (usage depends on datastore implementation)\
+ dbname - Readonly: name of the related <datastore> (in the XML config)\
+ lastSync - Readonly: time of last successful sync\
+ lastToRemoteSync - Readonly: time of last sync that sent data to the remote party (server)\
+ resumeAlertCode - Read: if != 0, next sync will be a resume. Write: set to 0 to prevent resume. DO NOT WRITE OTHER VALUES THAN 0.\
+\pard\tx2414\tx2414\pardeftab720\li2556\fi-2556\ri0
+\cf0 dispName - Readonly: the datastore's display name as configured with <displayname> (if none configured, this returns same as "dbname")\
+\pard\tx2414\pardeftab720\li2556\fi-2556\ri0
+
+\b \cf0 \
+ Session key unnamed implicit per-session key obtained by OpenSessionKey()\
+\
+\pard\tx2414\pardeftab720\li2556\fi-2556\ri0
+
+\b0 \cf0 connectURI - URI to use to connect to SyncML server. Note that this might be different from the original Server URI in profile's "serverURI" as the SyncML server might request sending requests to another URI during a sync session.\
+ contenttype - content type string to use for the HTTP "Content-Type:" header.\
+\
+\pard\tx2414\pardeftab720\li2556\fi-2556\ri0
+
+\b \cf0 /sessionvars Session context script variables (for PRO engines with scripting only)\
+\pard\tx2414\pardeftab720\li2556\fi-2556\ri0
+
+\b0 \cf0 <varname> - name of any script variable defined in session context scripts (like <sessioninitscript>).\
+\pard\tx2414\pardeftab720\li2556\fi-2556\ri0
+
+\f1\fs24 \cf0 \
+\pard\tx2414\pardeftab720\li2556\fi-2556\ri0
+
+\f0\fs20 \cf0 \
+} \ No newline at end of file
diff --git a/src/DB_interfaces/api_db/DLL_interface.cpp b/src/DB_interfaces/api_db/DLL_interface.cpp
new file mode 100755
index 0000000..e4e8194
--- /dev/null
+++ b/src/DB_interfaces/api_db/DLL_interface.cpp
@@ -0,0 +1,215 @@
+/*
+ * File: DLL_interface.cpp
+ *
+ * Author: Beat Forster (bfo@synthesis.ch)
+ *
+ *
+ * General interface to access the routines
+ * of a DLL.
+ *
+ * Copyright (c) 2004-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ *
+ */
+
+
+#include "sync_include.h"
+#include "sync_dbapidef.h"
+#include "sync_dbapiconnect.h"
+#include "SDK_util.h"
+#include "SDK_support.h"
+#include "DLL_interface.h"
+
+
+#ifdef SYSYNC_ENGINE
+ #include "syncappbase.h"
+#endif
+
+#ifdef JNI_SUPPORT
+ #include "JNI_interface.h"
+#endif
+
+#ifdef __cplusplus
+ namespace sysync {
+#endif
+
+
+// ------------------------------------------------------------------
+#ifdef PLUGIN_DLL
+//! derived class for DLL access
+bool TDLL::Connect( cAppCharP aModName, ErrReport aReport, void* /* ref */ )
+{
+ fModName= Plugin_MainName( aModName );
+ return ConnectDLL( fMod, fModName.c_str(), aReport );
+} // TDLL::Connect
+
+
+bool TDLL::GetFunction( cAppCharP aFuncName, cAppCharP /* aParams */,
+ appPointer &aFunc, ErrMReport aReport, void* ref )
+{
+ appPointer m; /* don't overwrite <aFunc> if "" or in case of error */
+ if (*aFuncName=='\0') return true;
+ if (!DLL_Function( fMod, aFuncName, m )) {
+ aReport( ref, aFuncName,fModName.c_str() ); return false; }
+
+ aFunc= m; return true;
+} // TDLL::GetFunction
+
+
+bool TDLL::Disconnect() {
+ return DisconnectDLL( fMod );
+} // TDLL::Disconnect
+#endif // PLUGIN_DLL
+
+
+// ---- Handler for DLL connection reporting ------------------------
+void ModuleConnectionError( void* /* ref */, cAppCharP aModName ) {
+ ConsolePrintf( "Can't connect to library '%s'.", aModName );
+} // ModuleConnectionError
+
+
+void FuncConnectionError( void* /* ref */, cAppCharP aFuncName, cAppCharP aModName )
+{
+ string s= "Can't connect to function '%s'";
+ if (*aModName!='\0') s+= " of module '%s'";
+ s+= ".";
+
+ ConsolePrintf( s.c_str(), aFuncName,aModName );
+} // FuncConnectionError
+
+
+
+// ------------------------------------------------------------------
+static TAccess* AssignedObject( cAppCharP name, bool is_jni )
+// Create object for LIB or DLL or JNI
+{
+ TAccess* d = NULL;
+ bool is_lib= false;
+
+ do {
+ #ifdef JNI_SUPPORT
+ if (is_jni) { d= static_cast<TAccess*>( new TJNI_Methods ); break; } // JNI (Java)
+ #endif
+
+ is_lib= IsLib( name );
+ if (is_lib) { d= new TAccess; break; } // LIB direct
+
+ #ifdef PLUGIN_DLL
+ d= static_cast<TAccess*>( new TDLL ); break; // DLL (C/C++)
+ #endif
+ } while (false);
+
+ if (d==NULL) return NULL;
+
+ d->fJNI= is_jni;
+ d->fLIB= is_lib;
+
+ return d;
+} // AssignedObject
+
+
+
+// ------------------------------------------------------------------
+TSyError ConnectModule( appPointer &aMod, cAppCharP aModName, bool is_jni )
+{
+ TAccess* d= AssignedObject( aModName, is_jni );
+ if (!d) return DB_Forbidden;
+ if (d->Connect ( aModName,ModuleConnectionError ))
+ { aMod= d; return LOCERR_OK; }
+ else { aMod= NULL; return DB_NotFound; }
+} // ConnectModule
+
+
+
+TSyError ConnectFunctions( appPointer aMod, appPointer aField, memSize aFieldSize,
+ bool aParamInfo, ... )
+/* Connect to DLL <aDLLname> (or LIB if <aDLLname> = "") */
+{
+ TSyError err = LOCERR_OK;
+ TAccess* d = (TAccess*)aMod;
+ char* p = NULL;
+ char* q;
+ appPointer* m = (appPointer*)aField;
+ bool notEnough= false;
+ bool tooMany = false;
+
+ if (aParamInfo) aFieldSize= 2*aFieldSize;
+
+ // do vararg handling
+ va_list args;
+ va_start( args, aParamInfo ); // the element before the open param list
+
+ // now go thru the list of all functions and put them into the list
+ uInt32 ii= 0;
+ while (ii*sizeof(p)<=aFieldSize) {
+ p= va_arg( args, char* ); if (p==NULL) break;
+ if (p==(char*)XX)
+ p= NULL; // XX is equivalent to NULL, but not termination of the list
+ q= NULL;
+
+ // if aParamInfo, treat them as a pair <p>,<q>
+ if (aParamInfo) {
+ q= va_arg( args, char* ); if (q==NULL) break;
+ ii++;
+ } // if
+ ii++;
+
+ if (!err) {
+ bool ok= d->GetFunction( p,q, *m, FuncConnectionError );
+ if (!ok) err= DB_NotFound;
+ } // if
+
+ m++;
+ } // while
+ if (err) return err;
+
+ // check if table is too small or too long
+ while (ii*sizeof(p)<aFieldSize) {
+ notEnough= true;
+ *m= NULL; // initialize the rest to zero
+ m++;
+ ii++;
+ if (aParamInfo) ii++;
+ } // while
+ tooMany= (p!=NULL);
+
+ va_end( args );
+
+ if (notEnough || tooMany) return LOCERR_WRONGUSAGE;
+ return err;
+} // ConnectFunctions
+
+
+
+TSyError DisconnectModule( appPointer &aMod )
+{
+ TAccess* d = (TAccess*)aMod;
+ TSyError err= LOCERR_OK;
+
+ if (d==NULL) return err;
+ if (!d->Disconnect()) err= DB_NotFound;
+
+ do {
+ #ifdef JNI_SUPPORT
+ if (d->fJNI) { TJNI_Methods* dj= static_cast<TJNI_Methods*>( d );
+ delete dj; break; } // JNI (Java)
+ #endif
+
+ if (d->fLIB) { delete d; break; } // LIB direct
+
+ #ifdef PLUGIN_DLL
+ { TDLL* dd= static_cast<TDLL*>( d );
+ delete dd; break; } // DLL (C/C++)
+ #endif
+ } while (false);
+
+ aMod= NULL; // no longer access
+ return err;
+} // DisconnectModule
+
+
+#ifdef __cplusplus
+ } // namespace
+#endif
+
+/* eof */
diff --git a/src/DB_interfaces/api_db/DLL_interface.h b/src/DB_interfaces/api_db/DLL_interface.h
new file mode 100755
index 0000000..d534f28
--- /dev/null
+++ b/src/DB_interfaces/api_db/DLL_interface.h
@@ -0,0 +1,119 @@
+/*
+ * File: DLL_interface.h
+ *
+ * Author: Beat Forster (bfo@synthesis.ch)
+ *
+ *
+ * General interface to access the routines
+ * of a DLL.
+ *
+ * Copyright (c) 2004-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ *
+ */
+
+#ifndef DLL_INTERFACE_H
+#define DLL_INTERFACE_H
+
+#include "target_options.h"
+#include "platform_DLL.h"
+
+#include <string>
+using namespace std;
+
+#include "generic_types.h"
+#include "syerror.h"
+
+
+#ifdef __cplusplus
+ namespace sysync {
+#endif
+
+
+// base class for (internal) routine access
+class TAccess
+{
+ public:
+ TAccess() { fMod= NULL;
+ fJNI= false;
+ fLIB= false; }
+
+ virtual ~TAccess() { }; // virtual destructor
+
+ virtual bool Connect ( cAppCharP aModName, ErrReport, void* ref= NULL )
+ {
+ fModName= aModName;
+ ref= NULL; // dummy to avoid warning
+ return true;
+ } // Connect
+
+ virtual bool GetFunction ( cAppCharP aFuncName, cAppCharP,
+ appPointer &aFunc, ErrMReport, void* ref= NULL )
+ {
+ if (aFuncName!=NULL) aFunc= (appPointer)aFuncName; // interpret it as address
+ ref= NULL; // dummy to avoid warning
+ return true;
+ } // GetFunction
+
+ virtual bool Disconnect () { return true; }
+
+ bool fJNI;
+ bool fLIB;
+
+ protected:
+ appPointer fMod;
+ string fModName;
+}; // TAccess
+
+
+#ifdef PLUGIN_DLL
+//! derived class for DLL access
+class TDLL : public TAccess
+{
+ public:
+ virtual bool Connect ( cAppCharP aModName, ErrReport aReport, void* ref= NULL );
+ virtual bool GetFunction( cAppCharP aFuncName, cAppCharP aParams,
+ appPointer &aFunc, ErrMReport aReport, void* ref= NULL );
+ virtual bool Disconnect ();
+}; // TDLL
+#endif // PLUGIN_DLL
+
+
+// --------------------------------------------------------------------------
+/*! Error output, if <aModName> can't be found */
+void ModuleConnectionError( void* /* ref */, cAppCharP aModName );
+
+/*! Error output, if <aFuncName> can't be found */
+void FuncConnectionError ( void* /* ref */, cAppCharP aFuncName, cAppCharP aModName );
+
+
+/*! Connects library/DLL/JNI <aModName>.
+ * <aMod> is the module reference pointer.
+ */
+TSyError ConnectModule( appPointer &aMod, cAppCharP aModName, bool is_jni= false );
+
+
+/*! Connects a list of functions to module <aMod>.
+ * The functions will be filled into <aField> with <aFieldSize>. An error will be
+ * returned if the list of functions is too long or too short.
+ * The open parameter list will be interpreted - as function names for DLLs
+ * (or if module name is LIB/JNI) - as function pointers for libraries
+ * <aMod> is the module reference pointer.
+ * <aParamInfo> must be true, if each element contains a second param with parameter info.
+ */
+TSyError ConnectFunctions( appPointer aMod, appPointer aField, memSize aFieldSize,
+ bool aParamInfo, ... );
+
+
+/*! Disconnect a connected unit
+ * If mode is a library, <aMod>=NULL can be passed
+ */
+TSyError DisconnectModule( appPointer &aMod );
+
+
+#ifdef __cplusplus
+ } // namespace
+#endif
+
+#endif /* DLL_INTERFACE_H */
+/* eof */
diff --git a/src/DB_interfaces/api_db/dbapi.cpp b/src/DB_interfaces/api_db/dbapi.cpp
new file mode 100755
index 0000000..055cf55
--- /dev/null
+++ b/src/DB_interfaces/api_db/dbapi.cpp
@@ -0,0 +1,2203 @@
+/*
+ * File: dbapi.cpp
+ *
+ * Author: Beat Forster (bfo@synthesis.ch)
+ *
+ *
+ * Programming interface between Synthesis SyncML engine
+ * and a database structure: C++ class interface
+ *
+ * Copyright (c) 2004-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ */
+
+#include "dbapi.h"
+#include "SDK_util.h"
+#include "SDK_support.h"
+#include "DLL_interface.h"
+
+#ifndef SYSYNC_ENGINE
+#include "stringutil.h"
+#endif
+
+#ifdef JNI_SUPPORT
+#include "JNI_interface.h"
+#endif
+
+
+// the simple demo adapter
+#ifdef DBAPI_DEMO
+ namespace SDK_demodb {
+ #include "dbapi_include.h"
+ } // namespace
+#endif
+
+// more built-in adapters of the same kind can be used
+#ifdef DBAPI_EXAMPLE
+ namespace example1 {
+ #include "dbapi_include.h"
+ } // namespace
+ namespace example2 {
+ #include "dbapi_include.h"
+ } // namespace
+#endif
+
+// a silent adapter, which always returns OK
+#ifdef DBAPI_SILENT
+ namespace silent {
+ #include "dbapi_include.h"
+ } // namespace
+#endif
+
+// the textdb adapter
+#ifdef DBAPI_TEXT
+ namespace SDK_textdb {
+ #include "dbapi_include.h"
+ } // namespace
+#endif
+
+// the snowwhite/oceanblue adapter
+#ifdef DBAPI_SNOWWHITE
+ namespace oceanblue {
+ #include "dbapi_include.h"
+ } // namespace
+#endif
+
+// the JNI bridge to Java
+#if defined JNI_SUPPORT && defined PLUGIN_DLL
+ namespace SDK_jni {
+ #include "dbapi_include.h"
+ } // namespace
+#endif
+
+// the C# bridge
+#if defined CSHARP_SUPPORT && defined PLUGIN_DLL
+ namespace SDK_csharp {
+ #include "dbapi_include.h"
+ } // namespace
+#endif
+
+// the bridge to OMA-DS 1.2 FILEOBJ
+#ifdef DBAPI_FILEOBJ
+ namespace SDK_fileobj {
+ #include "dbapi_include.h"
+ } // namespace
+#endif
+
+// the dbapi logger bridge
+#ifdef DBAPI_LOGGER
+ namespace logger {
+ #include "dbapi_include.h"
+ } // namespace
+#endif
+
+
+// some internal modules do not have the full set of routines
+#define DISABLE_PLUGIN_SESSIONAUTH 1
+#define DISABLE_PLUGIN_DEVICEADMIN 1
+#define DISABLE_PLUGIN_DATASTOREADMIN 1
+#define DISABLE_PLUGIN_ADAPTITEM 1
+
+// the bridge via tunnel to any datastore inside the engine
+#if defined DBAPI_TUNNEL && defined PLUGIN_DLL
+ namespace SDK_tunnel {
+ #include "dbapi_include.h"
+ } // namespace
+#endif
+
+
+// iPhone plugins are AsKey only
+#define DISABLE_PLUGIN_DATASTOREDATA_STR
+// static plugins required for iPhoneOS (no DLLs allowed)
+#ifdef IPHONE_PLUGINS_STATIC
+ #ifdef HARDCODED_CONTACTS
+ namespace iPhone_addressbook {
+ #include "dbapi_include.h"
+ } // namespace
+ #endif
+
+ #ifdef HARDCODED_CALENDAR
+ namespace iPhone_calendar {
+ #include "dbapi_include.h"
+ } // namespace
+ #endif
+
+ #ifdef HARDCODED_TODO
+ namespace iPhone_todos {
+ #include "dbapi_include.h"
+ } // namespace
+ #endif
+
+ #ifdef HARDCODED_NOTES
+ namespace iPhone_notes {
+ #include "dbapi_include.h"
+ } // namespace
+ #endif
+
+ #ifdef HARDCODED_EMAILS
+ namespace iPhone_emails {
+ #include "dbapi_include.h"
+ } // namespace
+ #endif
+
+ #ifdef HARDCODED_CUSTOM
+ namespace iPhone_db0 {
+ #include "dbapi_include.h"
+ } // namespace
+
+ namespace iPhone_db1 {
+ #include "dbapi_include.h"
+ } // namespace
+
+ namespace iPhone_db2 {
+ #include "dbapi_include.h"
+ } // namespace
+
+ namespace iPhone_db3 {
+ #include "dbapi_include.h"
+ } // namespace
+ #endif
+#endif
+#undef DISABLE_PLUGIN_DATASTOREDATA_STR
+
+
+#undef DISABLE_PLUGIN_ADAPTITEM
+#define DISABLE_PLUGIN_DATASTOREDATA 1
+
+// the "adaptitem" module does not contain most of the routines
+#ifdef ADAPTITEM_SUPPORT
+ namespace SDK_adapt {
+ #include "dbapi_include.h"
+ } // namespace
+#endif
+
+// the UI demo adapter is something special
+#define DISABLE_PLUGIN_ADAPTITEM 1
+#define ENABLE_PLUGIN_UI 1
+
+#ifdef UIAPI_DEMO
+ namespace SDK_ui {
+ #include "dbapi_include.h"
+ } // namespace
+#endif
+
+#undef DISABLE_PLUGIN_ADAPTITEM
+#undef DISABLE_PLUGIN_SESSIONAUTH
+#undef DISABLE_PLUGIN_DEVICEADMIN
+#undef DISABLE_PLUGIN_DATASTOREADMIN
+#undef DISABLE_PLUGIN_DATASTOREDATA
+// all should be switched on now for "no_dbapi"
+
+
+
+// ------------------------------------------------------------
+#define MyDB "DBApi" // identifier of the sandwich layer
+
+
+namespace no_dbapi {
+ #include "dbapi_include.h"
+
+ // combine the definitions of different namespaces
+ using sysync::DB_Callback;
+ using sysync::UI_Call_In;
+ using sysync::ItemID;
+ using sysync::MapID;
+ using sysync::cItemID;
+ using sysync::cMapID;
+
+ using sysync::LOCERR_NOTIMP;
+ using sysync::Password_Mode_Undefined;
+
+ // default routines, if no context is available
+ // all of them are returning false or 0
+
+ /* --- MODULE ------------------------------------------------------------------------------------ */
+ TSyError Module_CreateContext ( CContext* mc, cAppCharP /* moduleName */,
+ cAppCharP /* subName */,
+ cAppCharP /* mContextName */,
+ DB_Callback /* mCB */ ) { *mc= 0; return LOCERR_OK; }
+
+ CVersion Module_Version ( CContext /* mc */ ) /* invalid */ { return VP_BadVersion;}
+ TSyError Module_Capabilities ( CContext /* mc */, appCharP *aCapaP )
+ { *aCapaP= NULL; return DB_NotFound; }
+ TSyError Module_PluginParams ( CContext /* mc */, cAppCharP /* mConfigParams */,
+ CVersion /* engineVersion */ ) { return DB_Error; }
+ void Module_DisposeObj ( CContext /* mc */, void* /* memory */ ) { }
+ TSyError Module_DeleteContext ( CContext /* mc */ ) { return LOCERR_OK; }
+
+
+ /* --- SESSION ----------------------------------------------------------------------------------- */
+ TSyError Session_CreateContext ( CContext* /* sc */, cAppCharP /* sessionName */,
+ DB_Callback /* sCB */ ) { return DB_Fatal; }
+
+ TSyError Session_AdaptItem ( CContext /* sc */, appCharP* /* sItemData1 */,
+ appCharP* /* sItemData2 */,
+ appCharP* /* sLocalvars */,
+ uInt32 /* sIdentifier */ ) { return DB_Forbidden; }
+
+ TSyError Session_CheckDevice ( CContext /* sc */, cAppCharP /* aDeviceID */,
+ appCharP* /* sDevKey */,
+ appCharP* /* nonce */ ) { return DB_Forbidden; }
+ sInt32 Session_PasswordMode ( CContext /* sc */ ) { return Password_Mode_Undefined; }
+ TSyError Session_Login ( CContext /* sc */, cAppCharP /* sUsername */,
+ appCharP* /* sPassword */,
+ appCharP* /* sUsrKey */ ) { return DB_Forbidden; }
+ TSyError Session_Logout ( CContext /* sc */ ) { return DB_Forbidden; }
+
+ TSyError Session_GetNonce ( CContext /* sc */, appCharP* /* nonce */ ) { return DB_Forbidden; }
+ TSyError Session_SaveNonce ( CContext /* sc */, cAppCharP /* nonce */ ) { return DB_Forbidden; }
+ TSyError Session_SaveDeviceInfo ( CContext /* sc */, cAppCharP /* aDeviceInfo */ ) { return DB_Forbidden; }
+ TSyError Session_GetDBTime ( CContext /* sc */, appCharP* /* currentDBTime */) { return DB_Forbidden; }
+
+ void Session_DisposeObj ( CContext /* sc */, void* /* memory */ ) { }
+ void Session_ThreadMayChangeNow( CContext /* sc */ ) { }
+ void Session_DispItems ( CContext /* sc */, bool, cAppCharP ) { }
+ TSyError Session_DeleteContext ( CContext /* sc */ ) { return DB_Fatal; }
+
+
+ /* --- DATASTORE --------------------------------------------------------------------------------- */
+ /* ----- OPEN ------------------------ */
+ TSyError CreateContext ( CContext* /* co */, cAppCharP /* aContextName */, DB_Callback /* aCB */,
+ cAppCharP /* sDevKey */,
+ cAppCharP /* sUsrKey */ ) { return DB_Fatal; }
+ uInt32 ContextSupport ( CContext /* co */, cAppCharP /* aContextRules */ ) { return 0; }
+ uInt32 FilterSupport ( CContext /* co */, cAppCharP /* aFilterRules */ ) { return 0; }
+
+
+ /* ----- ADMINISTRATION -------------- */
+ TSyError LoadAdminData ( CContext /* co */, cAppCharP /* aLocDB */,
+ cAppCharP /* aRemDB */,
+ appCharP* /* adminData */ ) { return DB_Forbidden; }
+ TSyError SaveAdminData ( CContext /* co */, cAppCharP /* adminData */ ) { return DB_Forbidden; }
+ bool ReadNextMapItem ( CContext /* co */, MapID /* mID */, bool /* aFirst */ ) { return false; }
+ TSyError InsertMapItem ( CContext /* co */, cMapID /* mID */ ) { return DB_Forbidden; }
+ TSyError UpdateMapItem ( CContext /* co */, cMapID /* mID */ ) { return DB_Forbidden; }
+ TSyError DeleteMapItem ( CContext /* co */, cMapID /* mID */ ) { return DB_Forbidden; }
+
+
+ /* ----- GENERAL --------------------- */
+ void DisposeObj ( CContext /* co */, void* /* memory */ ) { }
+ void ThreadMayChangeNow ( CContext /* co */ ) { }
+ void WriteLogData ( CContext /* co */, cAppCharP /* logData */ ) { }
+ void DispItems ( CContext /* co */, bool, cAppCharP ) { }
+
+ /* ----- "script-like" ADAPT --------- */
+ TSyError AdaptItem ( CContext /* co */, appCharP* /* aItemData1 */,
+ appCharP* /* aItemData2 */,
+ appCharP* /* aLocalvars */,
+ uInt32 /* aIdentifier */ ) { return DB_Forbidden; }
+
+ /* ----- READ ------------------------ */
+ TSyError StartDataRead ( CContext /* co */, cAppCharP /* lastToken */,
+ cAppCharP /* resumeToken */ ) { return DB_Fatal; }
+
+ TSyError ReadNextItem ( CContext /* co */, ItemID /* aID */, appCharP* /* aItemData */,
+ sInt32* /* aStatus */, bool /* aFirst */ ) { return DB_Fatal; }
+ TSyError ReadNextItemAsKey( CContext /* co */, ItemID /* aID */, KeyH /* aItemKey */,
+ sInt32* /* aStatus */, bool /* aFirst */ ) { return DB_Fatal; }
+
+ TSyError ReadItem ( CContext /* co */, cItemID /* aID */, appCharP* /* aItemData */ ) { return DB_Fatal; }
+ TSyError ReadItemAsKey ( CContext /* co */, cItemID /* aID */, KeyH /* aItemKey */ ) { return DB_Fatal; }
+
+ TSyError ReadBlob ( CContext /* co */, cItemID /* aID */, cAppCharP /* aBlobID */,
+ appPointer* /* aBlkPtr */, memSize* /* aBlkSize */,
+ memSize* /* aTotSize */,
+ bool /* aFirst */, bool* /* aLast */ ) { return DB_Fatal; }
+ TSyError EndDataRead ( CContext /* co */ ) { return DB_Fatal; }
+
+
+ /* ----- WRITE ----------------------- */
+ TSyError StartDataWrite ( CContext /* co */ ) { return DB_Fatal; }
+
+ TSyError InsertItem ( CContext /* co */, cAppCharP /* aItemData */, ItemID /* newID */ ) { return DB_Fatal; }
+ TSyError InsertItemAsKey ( CContext /* co */, KeyH /* aItemKey */, ItemID /* newID */ ) { return DB_Fatal; }
+ TSyError UpdateItem ( CContext /* co */, cAppCharP /* aItemData */, cItemID /* aID */,
+ ItemID /* updID */ ) { return DB_Fatal; }
+ TSyError UpdateItemAsKey ( CContext /* co */, KeyH /* aItemKey */, cItemID /* aID */,
+ ItemID /* updID */ ) { return DB_Fatal; }
+
+ TSyError MoveItem ( CContext /* co */, cItemID /* aID */, cAppCharP /* newParID */ ) { return DB_Fatal; }
+ TSyError DeleteItem ( CContext /* co */, cItemID /* aID */ ) { return DB_Fatal; }
+ TSyError FinalizeLocalID ( CContext /* co */, cItemID /* aID */, ItemID /* updID */ ) { return DB_Fatal; }
+ TSyError DeleteSyncSet ( CContext /* co */ ) { return DB_Fatal; }
+ TSyError WriteBlob ( CContext /* co */, cItemID /* aID */, cAppCharP /* aBlobID */,
+ appPointer /* aBlkPtr */,memSize /* aBlkSize */,
+ memSize /* aTotSize */,
+ bool /* aFirst */, bool /* aLast */ ) { return DB_Fatal; }
+
+ // There are older implementations, where "DeleteBlob" is not yet available.
+ TSyError DeleteBlob ( CContext /* co */, cItemID /* aID */, cAppCharP /* aBlobID */ ) { return LOCERR_NOTIMP; }
+ TSyError EndDataWrite ( CContext /* co */, bool /* success */, char** /* newToken */ ) { return DB_Fatal; }
+
+ /* ----- CLOSE ----------------------- */
+ TSyError DeleteContext ( CContext /* co */ ) { return DB_Fatal; }
+
+
+ /* ---- UI API ----------------------- */
+ TSyError UI_CreateContext ( CContext* /* co */, cAppCharP /* uiName */, UI_Call_In /* uCI */ ) { return DB_Fatal; }
+ TSyError UI_RunContext ( CContext /* co */ ) { return DB_Fatal; }
+ TSyError UI_DeleteContext ( CContext /* co */ ) { return DB_Fatal; }
+} // namespace no_dbapi
+
+
+
+/* ---- "TDB_Api_Str" implementation --------------------------------------- */
+namespace sysync {
+
+TDB_Api_Str::TDB_Api_Str() { clear(); } // constructor
+TDB_Api_Str::TDB_Api_Str( string &s ) { clear(); fStr= (char*)s.c_str(); } // alternative constructor
+TDB_Api_Str::~TDB_Api_Str() { DisposeStr(); } // destructor
+
+
+void TDB_Api_Str::AssignStr( CContext aContext, DisposeProc aDisposeProc, bool itself )
+{
+ fContext= aContext;
+ fItself = itself;
+
+ if (fStr!=NULL) fDisposeProc= aDisposeProc;
+ else { fDisposeProc= NULL; fStr= const_cast<char *>(""); /* this is okay because the string is not going to be disposed */ }
+} // AssignStr
+
+
+
+void TDB_Api_Str::DisposeStr()
+{
+ // nothing to do, already disposed ?
+ if (fStr !=NULL &&
+ fDisposeProc!=NULL) {
+ void* ref= const_cast<char *>(fStr);
+ if (fItself) ref= this; fDisposeProc( fContext,ref );
+ } // if
+
+ clear(); // don't do it again !!
+} // DisposeStr
+
+
+
+/* Dispose a string which has been allocated with 'StrAlloc'
+ * Used as handler for:
+ * typedef void( *DisposeProc)( long aContext, void* s );
+ */
+static void StrDispose_Handler( CContext /* aContext */, void* s ) {
+ StrDispose( s );
+} // StrDispose_Handler
+
+
+
+/* ---------- allocate local memory for a string ------------- */
+void TDB_Api_Str::LocalAlloc( CContext aContext, cAppCharP str )
+{
+ fStr= StrAlloc( str );
+ AssignStr( aContext, (DisposeProc)StrDispose_Handler );
+} // LocalAlloc
+
+
+
+/* ---- "DB_Api_Blk" implementation --------------------------------------- */
+TDB_Api_Blk::TDB_Api_Blk() { clear(); } // constructor
+TDB_Api_Blk::~TDB_Api_Blk() { DisposeBlk(); } // destructor
+
+
+void TDB_Api_Blk::AssignBlk( CContext aContext, DisposeProc aDisposeProc, bool itself )
+{
+ fContext= aContext;
+ fItself = itself;
+
+ if (fPtr!=NULL) fDisposeProc= (DisposeProc)aDisposeProc;
+ else { fDisposeProc= NULL; fSize= 0; }
+} // AssignBlk
+
+
+
+void TDB_Api_Blk::DisposeBlk()
+{
+ // nothing to do, already disposed ?
+ if (fPtr !=NULL &&
+ fDisposeProc!=NULL) {
+ void* ref= fPtr;
+ if (fItself) ref= this; fDisposeProc( fContext,ref );
+ } // if
+
+ clear(); // don't do it again !!
+} // DisposeBlk
+
+
+
+/* --- connect to library ------------------------------------------------- */
+/*! These are the built-in linked libraries, which can be accessed directly
+ * NOTE: Some of them are normally switched off with compile options
+ *
+ * SDK plugin modules can be also linked into the engine directly.
+ * Normally the compiler must be able to link all SDK routines.
+ * The SDK concept allows to have some routines unimplemented,
+ * they will be provided by the "no_dbapi" built-in module.
+ */
+static TSyError DBApi_LibAssign( appPointer aMod, cAppCharP aName, appPointer aField, int aFSize,
+ cAppCharP aKey= "" )
+{
+ TSyError err= DB_NotFound;
+
+ string p= Plugin_MainName( aName );
+ p= NoBracks( p );
+ cAppCharP ps= p.c_str();
+
+ // the blind adapter is always available and acts as default as well
+ if (strucmp( ps,"" )==0 ||
+ strucmp( ps,"no_dbapi" )==0) err= no_dbapi::AssignMethods( aMod, aField,aFSize, aKey );
+
+ #ifdef DBAPI_DEMO // demo (C) adapter, which will be delivered with SDK
+ else if (strucmp( ps,"SDK_demodb" )==0) err= SDK_demodb::AssignMethods( aMod, aField,aFSize, aKey );
+ #endif
+
+ #ifdef DBAPI_EXAMPLE // other (C++) linked versions of the demo adapter (for test)
+ else if (strucmp( ps,"example1" )==0) err= example1::AssignMethods( aMod, aField,aFSize, aKey );
+ else if (strucmp( ps,"example2" )==0) err= example2::AssignMethods( aMod, aField,aFSize, aKey );
+ #endif
+
+ #ifdef DBAPI_SILENT // a silent adapter, which always says, everthing is ok
+ else if (strucmp( ps,"silent" )==0) err= silent::AssignMethods( aMod, aField,aFSize, aKey );
+ #endif
+
+ #ifdef DBAPI_TEXT // "text_db" implementation, which emulates the "textdb"
+ else if (strucmp( ps,"SDK_textdb" )==0) err= SDK_textdb::AssignMethods( aMod, aField,aFSize, aKey );
+ #endif
+
+ #ifdef DBAPI_SNOWWHITE // "oceanblue/snowwhite" implementation
+ else if (strucmp( ps,"snowwhite" )==0) err= oceanblue::AssignMethods( aMod, aField,aFSize, aKey );
+ #endif
+
+ #ifdef PLUGIN_DLL // plugin bridges
+ #ifdef JNI_SUPPORT // the Java (JNI) adapter
+ else if (strucmp( ps,"JNI" )==0) err= SDK_jni::AssignMethods( aMod, aField,aFSize, aKey );
+ #endif
+
+ #ifdef CSHARP_SUPPORT // the C# adapter
+ else if (strucmp( ps,"CSHARP" )==0) err= SDK_csharp::AssignMethods( aMod, aField,aFSize, aKey );
+ #endif
+
+ #ifdef DBAPI_TUNNEL // direct access to internal adapters
+ else if (strucmp( ps,"tunnel" )==0) err= SDK_tunnel::AssignMethods( aMod, aField,aFSize, aKey );
+ #endif
+
+ #ifdef DBAPI_LOGGER // dbapi logger bridge
+ else if (strucmp( ps,"logger" )==0) err= logger::AssignMethods( aMod, aField,aFSize, aKey );
+ #endif
+ #endif
+
+ #ifdef DBAPI_FILEOBJ // dbapi fileobj
+ else if (strucmp( ps,"FILEOBJ" )==0) err= SDK_fileobj::AssignMethods( aMod, aField,aFSize, aKey );
+ #endif
+
+ #ifdef ADAPTITEM_SUPPORT // "script-like" adapt item
+ else if (strucmp( ps,"ADAPTITEM" )==0) err= SDK_adapt::AssignMethods( aMod, aField,aFSize, aKey );
+ #endif
+
+ #ifdef UIAPI_DEMO // demo (C++) UI interface adapter, which will be delivered with SDK
+ else if (strucmp( ps,"SDK_ui" )==0) err= SDK_ui::AssignMethods( aMod, aField,aFSize, aKey );
+ #endif
+
+ #ifdef IPHONE_PLUGINS_STATIC // iPhone OS does not allow DLL plugins at this time, so we must link them statically
+ #ifdef HARDCODED_CONTACTS
+ else if (strucmp( ps,"iPhone_addressbook")==0) err= iPhone_addressbook::AssignMethods( aMod, aField,aFSize, aKey );
+ #endif
+ #ifdef HARDCODED_CALENDAR
+ else if (strucmp( ps,"iPhone_calendar" )==0) err= iPhone_calendar::AssignMethods( aMod, aField,aFSize, aKey );
+ #endif
+ #ifdef HARDCODED_TODO
+ else if (strucmp( ps,"iPhone_todos" )==0) err= iPhone_todos::AssignMethods( aMod, aField,aFSize, aKey );
+ #endif
+ #ifdef HARDCODED_NOTES
+ else if (strucmp( ps,"iPhone_notes" )==0) err= iPhone_notes::AssignMethods( aMod, aField,aFSize, aKey );
+ #endif
+ #ifdef HARDCODED_EMAILS
+ else if (strucmp( ps,"iPhone_emails" )==0) err= iPhone_emails::AssignMethods( aMod, aField,aFSize, aKey );
+ #endif
+
+ #ifdef HARDCODED_CUSTOM
+ else if (strucmp( ps,"iPhone_db0" )==0) err= iPhone_db0::AssignMethods( aMod, aField,aFSize, aKey );
+ else if (strucmp( ps,"iPhone_db1" )==0) err= iPhone_db1::AssignMethods( aMod, aField,aFSize, aKey );
+ else if (strucmp( ps,"iPhone_db2" )==0) err= iPhone_db2::AssignMethods( aMod, aField,aFSize, aKey );
+ else if (strucmp( ps,"iPhone_db3" )==0) err= iPhone_db3::AssignMethods( aMod, aField,aFSize, aKey );
+ #endif
+ #endif
+
+
+
+ else ModuleConnectionError( NULL, aName );
+
+ return err;
+} // DBApi_LibAssign
+
+
+
+/* ---- wrapper for DB_Callback -------------------------------------------- */
+TDB_Api_Callback::TDB_Api_Callback() { InitCallback( &Callback, DB_Callback_Version, NULL,NULL ); }
+
+
+
+/* ---- "DB_Api_Config" implementation ------------------------------------- */
+TDB_Api_Config::TDB_Api_Config()
+{
+ fMod= NULL;
+ clear();
+
+ fTSTversion= VP_BadVersion;
+} // constructor
+
+
+TDB_Api_Config::~TDB_Api_Config()
+{
+ Disconnect();
+} // destructor
+
+
+
+static void connect_no_dbapi( appPointer &aMod, API_Methods &m )
+{
+ DisconnectModule( aMod ); // avoid memory leak
+ ConnectModule ( aMod,"" ); // default: no db_api -> all methods return false
+//---- module --------------------------------
+ DBApi_LibAssign ( aMod,"", &m.start, sizeof(m.start), Plugin_Start );
+ DBApi_LibAssign ( aMod,"", &m.param, sizeof(m.param), Plugin_Param );
+ DBApi_LibAssign ( aMod,"", &m, sizeof(m) );
+
+//---- session -------------------------------
+ DBApi_LibAssign ( aMod,"", &m.se, sizeof(m.se), Plugin_Session );
+ DBApi_LibAssign ( aMod,"", &m.se.seAdapt, sizeof(m.se.seAdapt), Plugin_SE_Adapt );
+ DBApi_LibAssign ( aMod,"", &m.se.seAuth, sizeof(m.se.seAuth), Plugin_SE_Auth );
+ DBApi_LibAssign ( aMod,"", &m.se.dvAdmin, sizeof(m.se.dvAdmin), Plugin_DV_Admin );
+ DBApi_LibAssign ( aMod,"", &m.se.dvTime, sizeof(m.se.dvTime), Plugin_DV_DBTime );
+
+//---- datastore -----------------------------
+ DBApi_LibAssign ( aMod,"", &m.ds, sizeof(m.ds), Plugin_Datastore );
+ DBApi_LibAssign ( aMod,"", &m.ds.dsg, sizeof(m.ds.dsg), Plugin_DS_General );
+ DBApi_LibAssign ( aMod,"", &m.ds.dsAdapt, sizeof(m.ds.dsAdapt), Plugin_DS_Adapt );
+ DBApi_LibAssign ( aMod,"", &m.ds.dsAdmin, sizeof(m.ds.dsAdmin), Plugin_DS_Admin );
+ DBApi_LibAssign ( aMod,"", &m.ds.dsData, sizeof(m.ds.dsData), Plugin_DS_Data );
+ DBApi_LibAssign ( aMod,"", &m.ds.dsData.str,sizeof(m.ds.dsData.str),Plugin_DS_Data_Str );
+ DBApi_LibAssign ( aMod,"", &m.ds.dsData.key,sizeof(m.ds.dsData.key),Plugin_DS_Data_Key );
+ DBApi_LibAssign ( aMod,"", &m.ds.dsBlob, sizeof(m.ds.dsBlob), Plugin_DS_Blob );
+//---- ui context ----------------------------
+ DBApi_LibAssign ( aMod,"", &m.ui, sizeof(m.ui), Plugin_UI );
+ DisconnectModule( aMod ); // no longer used, avoid memory leak
+} // connect_no_dbapi
+
+
+
+void TDB_Api_Config::clear()
+{
+ connect_no_dbapi( fMod, m );
+
+ fConnected = false;
+ fADMIN_Info= false;
+ fSDKversion= VP_BadVersion;
+ fMODversion= VP_BadVersion;
+ mContext = 0;
+
+ DB_Callback mCB= &fCB.Callback;
+ mCB->cContext= 0; // set it to uninitialized default
+ mCB->mContext= 0;
+} // clear
+
+
+
+// ---------------------------------------------------------------------
+//! Module string disposer
+void TDB_Api_Config::DisposeStr( TDB_Api_Str &s )
+{
+ fSList.remove( &s ); // remove it from the list first to avoid double free
+
+ s.AssignStr( mContext, (DisposeProc)m.Module_DisposeObj );
+ s.DisposeStr();
+} // DisposeStr
+
+
+//! Local wrapper, because it must be DisposeProc type and can't be C++ method
+static void Local_Module_DisposeStr( CContext mObj, void* memory )
+{
+ TDB_Api_Config* c= (TDB_Api_Config*)mObj;
+ TDB_Api_Str* u= (TDB_Api_Str*)memory;
+ c->DisposeStr( *u );
+} // Local_Session_DisposeStr
+
+
+void TDB_Api_Config::AssignStr( TDB_Api_Str &s )
+{
+ if (s.fStr==NULL) return;
+
+ s.AssignStr( (CContext)this, Local_Module_DisposeStr, true );
+ fSList.push_back( &s ); // add the element to the list
+} // AssignStr
+
+
+
+// ---------------------------------------------------------------------
+// general datastore access ?
+bool DSConnect( cAppCharP aItem )
+{
+ return FlagOK( aItem, Plugin_Datastore ) &&
+ ( FlagOK( aItem, Plugin_DS_Admin ) ||
+ FlagOK( aItem, Plugin_DS_Data ) ||
+ FlagOK( aItem, Plugin_DS_Adapt ) );
+} // DSConnect
+
+
+
+TSyError TDB_Api_Config::DBApi_Assign( cAppCharP aItem, appPointer aField,
+ memSize aFieldSize, cAppCharP aKey )
+{
+ TSyError err= LOCERR_OK;
+
+ if (FlagOK( aItem,aKey )) {
+ if (is_lib) err= DBApi_LibAssign( fMod, fModName.c_str(), aField,aFieldSize, aKey );
+ else { err= DB_Forbidden; // only allowed, if PLUGIN_DLL is active
+ #ifdef PLUGIN_DLL
+ err= DBApi_DLLAssign( fMod, aField,aFieldSize, aKey, false );
+ #endif
+ } // if
+
+ DEBUG_Exotic_INT( &fCB.Callback,MyDB, "DBApi_Assign", "aKey='%s' (size=%d) err=%d",
+ aKey, aFieldSize, err );
+ } // if
+
+ return err;
+} // DBApi_Assign
+
+
+
+// Returns the engine's SDK version
+long TDB_Api_Config::EngineSDKVersion()
+{ // If not changed ...
+ if (fTSTversion==VP_BadVersion) return Plugin_Version( 0 ); // Called here it's the engine's SDK version
+ else return fTSTversion; // modified for test
+} // EngineSDKVersion
+
+
+
+// Returns LOCERR_TOOOLD, if the <capa> version is too old
+TSyError TDB_Api_Config::MinVersionCheck( string capa, CVersion &vMin )
+{
+ TSyError err= LOCERR_OK;
+ vMin= 0;
+
+ string value;
+ while (RemoveField( capa, CA_MinVersion, value )) {
+ CVersion minVersion= VersionNr( value ); // the minimum required version of the plugin
+ CVersion engVersion= EngineSDKVersion () | 0x000000ff; // the engine's version with max build number
+ if (engVersion<minVersion) err = LOCERR_TOOOLD;
+ if (vMin <minVersion) vMin= minVersion;
+
+ DB_Callback mCB= &fCB.Callback;
+ DEBUG_INT ( mCB, MyDB, "MinVersionCheck", "engine=%08X >= %08X / err=%d",
+ engVersion, minVersion, err );
+ if (err) break;
+ } // while
+
+ return err;
+} // MinVersionCheck
+
+
+// Connect to internally linked library or external DLL, dependent on <aIsLib> and <allowDLL>
+TSyError TDB_Api_Config::Connect( cAppCharP aModName, CContext &globContext,
+ cAppCharP mContextName,
+ bool aIsLib, bool allowDLL )
+{
+ TSyError err;
+ string gcs;
+
+ if (fConnected) Disconnect();
+ if (fConnected) return DB_Error; // still connected ? => error
+
+ cAppCharP x= "";
+ DB_Callback mCB= &fCB.Callback;
+ CContext* mc= &mCB->mContext; // store it at a temporary var
+ *mc= 0;
+
+ clear();
+ is_lib = IsLib( aModName );
+ fModName= aModName; // make a local copy
+
+ if (aIsLib) { // && !is_lib) : special cases like "[aaa]!bbb" => "[[aaa]!bbb]"
+ fModName= AddBracks( aModName );
+ is_lib = true;
+ } // if
+
+ GlobContext* g;
+ GlobContext* gp;
+
+ if (CB_OK( mCB,8 )) { // <gContext> available ?
+ mCB->gContext= globContext;
+
+ for (int i= 1; i<=3; i++) { // create 3 empty elements
+ gp= new GlobContext; // create and init a new record
+ gp->ref = NULL;
+ gp->next= NULL;
+ gp->cnt = 0;
+ strcpy( gp->refName,"" );
+
+ if (!mCB->gContext) mCB->gContext= (CContext)gp; // assign it
+ else {
+ g= (GlobContext*)mCB->gContext; // or add it to the end of the chain
+ while (g->next!=NULL) g= (GlobContext*)g->next;
+ g->next= gp;
+ } // if
+ } // for
+ } // if
+
+ do { // exit part
+ if (!is_lib) {
+ x= "(DLL)";
+ if (!allowDLL) { err= DB_Forbidden; break; }
+ } // if
+
+ WithSubSystem ( fModName, fModMain, fModSub );
+ bool in_bracks= CutBracks( fModMain ); // "[aaa xx]" => "aaa xx"
+ fOptions= fModMain;
+ NextToken ( fOptions, fModMain, " " ); // separate <mOptions>
+ fModName= fModMain;
+ CutBracks( fModMain );
+
+ if (in_bracks) fModName = AddBracks( fModName );
+
+ /* this mode is no longer supported ( moduleName:globContext ) */
+ /* keep it for compatiblity */
+ if (globContext) {
+ g= (GlobContext*)globContext;
+ while (g!=NULL) {
+ if (strcmp( g->refName,"" )!=0) { // at least one field is set
+ fModMain+= ":" + RefStr( globContext ); break;
+ } // if
+
+ g= g->next;
+ } // while
+ } // if
+
+ if (!fOptions.empty()) fModMain+= " " + fOptions;
+
+ // create some downwards compatibility info
+ if (CB_OK( mCB,4 )) {
+ if (allowDLL) mCB->allow_DLL_legacy= 0xffff; // Assign DLL allowance info
+ else mCB->allow_DLL_legacy= 0x0000;
+
+ if (CB_OK( mCB,5 )) {
+ mCB->allow_DLL= mCB->allow_DLL_legacy;
+ } // if
+ } // if
+
+ // Connect "Module_CreateContext" and "Module_Version" first, to take decisions
+ err= ConnectModule( fMod, fModName.c_str() ); if (err) break;
+ err= DBApi_Assign( "", &m.start, sizeof(m.start), Plugin_Start ); if (err) break;
+
+ Version_Func pv= (Version_Func)m.start.Module_Version;
+ fSDKversion= pv( 0 ); // get the plugin's version before making the first tests
+ DEBUG_INT( mCB,MyDB, "Connect", "fSDKversion=%s", VersionStr( fSDKversion ).c_str() );
+
+ CreateM_Func p= (CreateM_Func)m.start.Module_CreateContext;
+ err= p( &mContext, fModMain.c_str(),fModSub.c_str(), mContextName, mCB );
+ if (err==LOCERR_ALREADY) err= LOCERR_OK; // this is not an error, just avoid multiple assignment
+ DEBUG_INT( mCB,MyDB, "Connect", "mContext=%08X err=%d", mContext,err );
+
+ if (err) break;
+ fMODversion= pv( mContext ); // get the plugin's version before making the first tests
+ mCB->cContext = mContext; // assign for the callback mechanism
+ if (*mc==0) *mc= mContext; // assign for session & datastores, if not already in use (e.g. for JNI)
+ //err= VersionCheck(); if (err) break;
+
+ TDB_Api_Str aCapa;
+ err= Capabilities( aCapa ); if (err) break;
+ cAppCharP ca= aCapa.c_str();
+ CVersion vMin;
+ err= MinVersionCheck( ca, vMin ); if (err) break;
+ fADMIN_Info= FlagOK( ca, CA_ADMIN_Info, true );
+
+ GetField( ca, CA_Description, fDesc );
+ GetField( ca, CA_GlobContext, gcs ); // is there a global context inside ?
+
+ // !Supported( VP_EngineVersionParam ): only JNI signature changes
+ // !Supported( VP_MD5_Nonce_IN ): only JNI signature changes
+
+ cAppCharP vda= Plugin_DS_Admin;
+ if (!Supported( VP_InsertMapItem )) vda= Plugin_DS_Admin_OLD;
+ cAppCharP vdd= Plugin_DS_Data;
+ if (!Supported( VP_FLI_DSS )) vdd= Plugin_DS_Data_OLD2;
+ if (!Supported( VP_ResumeToken )) vdd= Plugin_DS_Data_OLD1;
+ cAppCharP vdb= Plugin_DS_Blob;
+ if (!Supported( VP_DeleteBlob )) vdb= Plugin_DS_Blob_OLD;
+
+ //---- module ---------------------------------
+ if (!err) err= DBApi_Assign( "", &m.param, sizeof(m.param), Plugin_Param );
+ if (!err) err= DBApi_Assign( "", &m, sizeof(m) );
+
+ //---- session --------------------------------
+ if (!err && FlagOK ( ca, Plugin_Session )) {
+ err= DBApi_Assign( ca, &m.se, sizeof(m.se), Plugin_Session );
+
+ if (!err && ( FlagOK ( ca, Plugin_SE_Auth ) ||
+ FlagOK ( ca, Plugin_DV_Admin ) )) {
+ err= DBApi_Assign( ca, &m.se, sizeof(m.se), Plugin_Session );
+ if (!err) err= DBApi_Assign( ca, &m.se.seAuth, sizeof(m.se.seAuth), Plugin_SE_Auth );
+ if (!err) err= DBApi_Assign( ca, &m.se.dvAdmin, sizeof(m.se.dvAdmin), Plugin_DV_Admin );
+
+ if (!err && Supported( VP_GetDBTime )) {
+ err= DBApi_Assign( ca, &m.se.dvTime, sizeof(m.se.dvTime), Plugin_DV_DBTime );
+ } // if
+ } // if
+
+ if (!err && Supported( VP_AdaptItem )) {
+ err= DBApi_Assign( ca, &m.se.seAdapt, sizeof(m.se.seAdapt), Plugin_SE_Adapt );
+ } // if
+ } // if
+
+ //---- datastore ------------------------------
+ if (!err && FlagOK ( ca, Plugin_Datastore )) {
+ if (!err && DSConnect( ca )) {
+ err= DBApi_Assign( ca, &m.ds, sizeof(m.ds), Plugin_Datastore );
+ } // if
+
+ if (!err && ( FlagOK ( ca, Plugin_DS_Admin ) ||
+ FlagOK ( ca, Plugin_DS_Data ) )) {
+ err= DBApi_Assign( ca, &m.ds.dsg, sizeof(m.ds.dsg), Plugin_DS_General );
+ if (!err) err= DBApi_Assign( ca, &m.ds.dsBlob, sizeof(m.ds.dsBlob), vdb );
+ } // if
+
+ if (!err) err= DBApi_Assign( ca, &m.ds.dsAdmin, sizeof(m.ds.dsAdmin), vda );
+ if (!err) err= DBApi_Assign( ca, &m.ds.dsData, sizeof(m.ds.dsData), vdd );
+
+ if (!err && FlagOK ( ca, Plugin_DS_Data )) {
+ bool asK= FlagOK ( ca, CA_ItemAsKey, true );
+ if (asK) err= DBApi_Assign( ca, &m.ds.dsData.key,sizeof(m.ds.dsData.key),Plugin_DS_Data_Key );
+
+ bool asS= !asK || FlagBoth( ca, CA_ItemAsKey );
+ if (asS) err= DBApi_Assign( ca, &m.ds.dsData.str,sizeof(m.ds.dsData.str),Plugin_DS_Data_Str );
+ } // if
+
+ if (!err && Supported( VP_AdaptItem )) {
+ err= DBApi_Assign( ca, &m.ds.dsAdapt, sizeof(m.ds.dsAdapt), Plugin_DS_Adapt );
+ } // if
+ } // if
+
+ //---- ui context -----------------------------
+ if (!err && FlagOK( ca,Plugin_UI, true )) {
+ err= DBApi_Assign( ca, &m.ui, sizeof(m.ui), Plugin_UI );
+ } // if
+ } while (false); // end exit part
+
+ if (err) clear();
+
+ if (CB_OK( mCB,8 )) { globContext= mCB->gContext; // <gContext> available ?
+ DeleteGlobContext ( globContext, mCB, true ); // remove elements with empty text and no ref
+ mCB->gContext= globContext; // can be fully removed again (gContext==0)
+ }
+ else {
+ // ---- the old fashioned way to do it ----
+ if (!gcs.empty()) {
+ uIntPtr u;
+ HexStrToUIntPtr( gcs.c_str(), u );
+
+ if (!globContext) globContext= (CContext)u;
+ if (globContext!=(CContext)u) { // link it if more than one element
+ GlobContext* gc= (GlobContext*)globContext;
+ gc->next= (GlobContext*)u;
+ } // if
+ } // if
+ // -----------------------------------------
+ } // if
+
+ string s= "ok";
+ if (err) {
+ char f[ 15 ];
+ sprintf( f, "err=%d", err );
+ s= f;
+ } // if
+
+ DEBUG_INT( mCB,MyDB,"Connect", "%s '%s' %-9s", s.c_str(), fModName.c_str(), x );
+ fConnected= !err;
+ return err;
+} // Connect
+
+
+/* Get the plug-in's version number */
+long TDB_Api_Config::Version() { return fMODversion; }
+
+
+/* Get the plug-in's capabilities */
+TSyError TDB_Api_Config::Capabilities( TDB_Api_Str &aCapa )
+{
+ typedef TSyError (*CapabFunc)( CContext mContext,
+ appCharP *mCapabilities );
+
+ aCapa.DisposeStr();
+ CapabFunc p= (CapabFunc)m.start.Module_Capabilities;
+ TSyError err= p( mContext, const_cast<char **>(&aCapa.fStr) );
+ if (!err) AssignStr( aCapa );
+ return err;
+} // Capabilities
+
+
+/* Check, if <version_feature> is supported
+ * NOTE: For the SyncML engine internally everything is supported
+ * This will be reflected with the fact that the engine's
+ * version is alway much much higher
+ */
+bool TDB_Api_Config::Supported( CVersion versionFeature ) {
+ return Feature_Supported( versionFeature, fSDKversion );
+} // Supported
+
+
+
+TSyError TDB_Api_Config::PluginParams( cAppCharP mConfigParams )
+{
+ typedef TSyError (*PlugProc)( CContext mContext,
+ cAppCharP mConfigParams, CVersion engineVersion );
+ typedef TSyError (*OLD_PlugProc)( CContext mContext,
+ cAppCharP mConfigParams ); // w/o <engineVersion>
+
+ TSyError err;
+ if (!fConnected) return DB_Error;
+
+ // new param supported for Plugin Version >= 1.0.X.4
+ if (Supported( VP_EngineVersionParam )) {
+ PlugProc p= (PlugProc)m.param.Module_PluginParams;
+ err= p( mContext, mConfigParams, EngineSDKVersion() );
+ }
+ else {
+ OLD_PlugProc p= (OLD_PlugProc)m.param.Module_PluginParams; // w/o the SDK version parameter
+ err= p( mContext, mConfigParams );
+ } // if
+
+ if (err==LOCERR_ALREADY) err= LOCERR_OK;
+ return err;
+} // PluginParams
+
+
+
+// Set the SDK version
+// *** override the internal version number / for test only ***
+void TDB_Api_Config::SetVersion( long versionNr )
+{
+ fTSTversion= versionNr;
+ DEBUG_INT( &fCB.Callback, MyDB,"SetVersion", "%08X", fTSTversion );
+} // SetVersion
+
+
+
+TSyError TDB_Api_Config::Disconnect()
+{
+ if (!fConnected) return DB_Error; // avoid double free
+
+ // remove all still allocated elements before removing the content
+ while (!fSList.empty()) DisposeStr( *fSList.front() );
+
+ Context_Func p= (Context_Func)m.Module_DeleteContext;
+ TSyError err= p( mContext );
+
+ fModName= "";
+ DisconnectModule( fMod );
+ fConnected= false;
+ return err;
+} // Disconnect
+
+
+
+/* ------------------------------------------------------------------------- */
+void DispGlobContext( CContext &globContext, DB_Callback mCB )
+{
+ cAppCharP DGC= "DispGlobContext";
+
+ GlobContext* g= (GlobContext*)globContext;
+ DEBUG_Exotic_DB ( mCB, MyDB,DGC, "g=%s mCB->gContext=%s",
+ RefStr( g ).c_str(), RefStr( mCB->gContext ).c_str() );
+ while (g!=NULL) {
+ GlobContext* nx= g->next;
+ DEBUG_Exotic_DB( mCB, MyDB,DGC, "g=%s g->ref=%s g->cnt=%-3d '%s'",
+ RefStr( g ).c_str(), RefStr( g->ref ).c_str(),
+ g->cnt, g->refName );
+ g= nx;
+ } // while
+
+ DEBUG_Exotic_DB ( mCB, MyDB,DGC, "(eof)" );
+} // DispGlobContext
+
+
+// mCB->gContext will not be touched here
+void DeleteGlobContext( CContext &globContext, DB_Callback mCB, bool emptyTextOnly )
+{
+ DispGlobContext( globContext, mCB );
+ DEBUG_Exotic_DB( mCB, "","DeleteGlobContext", "" );
+
+ GlobContext* ls= NULL;
+ GlobContext* gs= (GlobContext*)globContext;
+ GlobContext* g= gs;
+ while (g!=NULL) {
+ GlobContext* nx= g->next;
+
+ bool remCond= g->ref==NULL && (!emptyTextOnly || strcmp( g->refName,"" )==0);
+ if (remCond) {
+ if (gs==g) gs = nx; // go forward if first element removed
+ else ls->next= nx;
+
+ delete g;
+ g= ls;
+ } // if
+
+ ls= g;
+ g = nx;
+ } // while
+
+ globContext= (CContext)gs;
+ DispGlobContext( globContext, mCB );
+} // DeleteGlobContext
+
+
+
+/* ---- "DB_Api_Session" implementation ------------------------------------ */
+TDB_Api_Session::TDB_Api_Session()
+{
+ sCreated= false;
+ sContext= 0;
+
+ appPointer modu= NULL;
+ connect_no_dbapi( modu, sNo_dbapi ); // the empty connector is default as well
+ dm= &sNo_dbapi; // and assign it
+
+ sPwMode= Password_Mode_Undefined;
+} // constructor
+
+
+
+/* Create a context for a new session */
+TSyError TDB_Api_Session::CreateContext( cAppCharP sessionName, TDB_Api_Config &config )
+{
+ if (sCreated) return DB_Forbidden;
+ sSessionName= sessionName; // make a local copy
+
+ dm= &config.m; // assign reference to the methods
+ CreateS_Func p= (CreateS_Func)dm->se.Session_CreateContext;
+
+ DB_Callback sCB= &fCB.Callback;
+ CContext* sc= &sCB->sContext; // store it at a temporary var
+ *sc= 0; // set it to check later, if changed
+
+ string vers= VersionStr( config.fSDKversion );
+ if (config.fMODversion!=config.fSDKversion) vers+= " / " + VersionStr( config.fMODversion );
+
+ DEBUG_INT( sCB,MyDB,Se_CC, "desc='%s', vers=%s", config.fDesc.c_str(),
+ vers.c_str() );
+
+ sCB->cContext= config.mContext; // inherit info
+ sContext= 0; sCB->mContext= config.fCB.Callback.mContext;
+ TSyError err= p( &sContext, sSessionName.c_str(), sCB );
+ if (!err) {
+ sCreated= true;
+ if (*sc==0) *sc= sContext; // assign for datastores, but only if not assigned in plug-in module
+ } // if
+
+ return LOCERR_OK; // workaround to avoid problems w/o a session
+ return err;
+} // CreateContext
+
+
+
+// ---------------------------------------------------------------------
+void TDB_Api_Session::DisposeStr( TDB_Api_Str &s )
+// Session str disposer
+{
+ fSList.remove( &s ); // remove it from the list first to avoid double free
+
+ s.AssignStr( sContext, (DisposeProc)dm->se.Session_DisposeObj );
+ s.DisposeStr();
+} // DisposeStr
+
+
+static void Local_Session_DisposeStr( CContext sObj, void* memory )
+/* Local wrapper, because it must be DisposeProc type and can't be C++ method */
+{
+ TDB_Api_Session* s= (TDB_Api_Session*)sObj;
+ TDB_Api_Str* u= (TDB_Api_Str*)memory;
+ s->DisposeStr ( *u );
+} // Local_Session_DisposeStr
+
+
+void TDB_Api_Session::AssignStr( TDB_Api_Str &s )
+{
+ if (s.fStr==NULL) return;
+
+ s.AssignStr( (CContext)this, Local_Session_DisposeStr, true );
+ fSList.push_back( &s ); // add the element to the list
+} // AssignStr
+
+
+
+// --------------------------------------------------------------------------------
+TSyError TDB_Api_Session::CheckDevice( cAppCharP deviceID, TDB_Api_Str &sDevKey,
+ TDB_Api_Str &nonce )
+{
+ typedef TSyError (*ChkDevFunc)( CContext sContext,
+ cAppCharP deviceID,
+ appCharP *sDevKey,
+ appCharP *nonce );
+
+ sDevKey.DisposeStr();
+ nonce.DisposeStr();
+ ChkDevFunc p= (ChkDevFunc)dm->se.dvAdmin.Session_CheckDevice;
+ TSyError err= p( sContext, deviceID, &sDevKey.fStr, &nonce.fStr );
+ if (!err) { AssignStr( sDevKey );
+ AssignStr( nonce ); }
+ return err;
+} // CheckDevice
+
+
+
+TSyError TDB_Api_Session::GetNonce( TDB_Api_Str &nonce )
+{
+ typedef TSyError (*GetNcFunc)( CContext sContext,
+ appCharP *nonce );
+
+ nonce.DisposeStr();
+ GetNcFunc p= (GetNcFunc)dm->se.dvAdmin.Session_GetNonce;
+ TSyError err= p( sContext, &nonce.fStr );
+ if (!err) AssignStr( nonce );
+ return err;
+} // GetNonce
+
+
+
+TSyError TDB_Api_Session::SaveNonce( cAppCharP nonce )
+{
+ SvInfo_Func p= (SvInfo_Func)dm->se.dvAdmin.Session_SaveNonce;
+ TSyError err= p( sContext, nonce );
+ return err;
+} // SaveNonce
+
+
+
+TSyError TDB_Api_Session::SaveDeviceInfo( cAppCharP aDeviceInfo )
+{
+ SvInfo_Func p= (SvInfo_Func)dm->se.dvAdmin.Session_SaveDeviceInfo;
+ TSyError err= p( sContext, aDeviceInfo );
+ return err;
+} // SaveDeviceInfo
+
+
+TSyError TDB_Api_Session::GetDBTime( TDB_Api_Str &currentDBTime )
+{
+ typedef TSyError (*DBTimeFunc)( CContext sContext,
+ appCharP *currentDBTime );
+
+ currentDBTime.DisposeStr();
+ DBTimeFunc p= (DBTimeFunc)dm->se.dvTime.Session_GetDBTime;
+ TSyError err= p( sContext, &currentDBTime.fStr );
+ if (!err) AssignStr( currentDBTime );
+ return err;
+} // GetDBTime
+
+
+#ifdef SYSYNC_ENGINE
+// No 'lineartime_t' available for standalone
+TSyError TDB_Api_Session::GetDBTime( lineartime_t &currentDBTime, GZones* g )
+{
+ timecontext_t tctx;
+ TDB_Api_Str s;
+ TSyError err= GetDBTime( s ); currentDBTime= 0;
+ if (!err && !ISO8601StrToTimestamp ( s.c_str(), currentDBTime, tctx )) return DB_Error;
+
+ TzConvertTimestamp( currentDBTime, tctx, TCTX_UTC, g, TCTX_SYSTEM );
+ return err;
+} // GetDBTime
+#endif
+
+
+
+/* ---- Login Handling ---------------------------------------- */
+sInt32 TDB_Api_Session::PasswordMode()
+{
+ if (sContext==0) return Password_Mode_Undefined;
+ PwMode_Func p= (PwMode_Func)dm->se.seAuth.Session_PasswordMode;
+ sPwMode= p( sContext );
+
+ /* calculate it always
+ if (sPwMode==Password_Mode_Undefined) {
+ ...
+ } // if
+ */
+
+ return sPwMode;
+} // PasswordMode
+
+
+
+/*! XXX_IN modes */
+TSyError TDB_Api_Session::Login( cAppCharP sUsername,
+ cAppCharP sPassword, TDB_Api_Str &sUsrKey )
+{
+ switch (PasswordMode()) {
+ case Password_ClrText_IN :
+ case Password_MD5_Nonce_IN : break;
+ default : return DB_Forbidden; // !sCreated is covered here
+ } // switch
+
+ sUsrKey.DisposeStr();
+ Login_Func p= (Login_Func)dm->se.seAuth.Session_Login;
+ TSyError err= p( sContext, sUsername, (char**)&sPassword, &sUsrKey.fStr );
+ if (!err) AssignStr( sUsrKey );
+ return err;
+} // Login
+
+
+/*! XXX_OUT modes */
+TSyError TDB_Api_Session::Login( cAppCharP sUsername,
+ TDB_Api_Str &sPassword, TDB_Api_Str &sUsrKey )
+{
+ switch (PasswordMode()) {
+ case Password_ClrText_OUT :
+ case Password_MD5_OUT : break;
+ default : return DB_Forbidden; // !sCreated is covered here
+ } // switch
+
+ sPassword.DisposeStr();
+ sUsrKey.DisposeStr();
+ Login_Func p= (Login_Func)dm->se.seAuth.Session_Login;
+ TSyError err= p( sContext, sUsername, &sPassword.fStr, &sUsrKey.fStr );
+ if (!err) { AssignStr( sPassword );
+ AssignStr( sUsrKey ); }
+ return err;
+} // Login (overloaded)
+
+
+
+TSyError TDB_Api_Session::Logout()
+{
+ if (sContext==0) return LOCERR_OK;
+ Context_Func p= (Context_Func)dm->se.seAuth.Session_Logout;
+ return p( sContext );
+} // Logout
+
+
+
+/* ---- General session routines ------------------------------- */
+void TDB_Api_Session::AssignChanged( string &a, TDB_Api_Str &u ) {
+ if (a.c_str()!=u.fStr) { a= u.fStr; AssignStr( u ); } // assign for destruction afterwards
+} // AssignChanged
+
+
+TSyError TDB_Api_Session::AdaptItem( string &sItemData1,
+ string &sItemData2,
+ string &sLocalVars, uInt32 sIdentifier )
+{
+ TDB_Api_Str updItemData1( sItemData1 );
+ TDB_Api_Str updItemData2( sItemData2 );
+ TDB_Api_Str updLocalVars( sLocalVars );
+
+ Adapt_Func p= (Adapt_Func)dm->se.seAdapt.Session_AdaptItem;
+ TSyError err= p( sContext, &updItemData1.fStr,
+ &updItemData2.fStr,
+ &updLocalVars.fStr, sIdentifier );
+
+ AssignChanged( sItemData1, updItemData1 );
+ AssignChanged( sItemData2, updItemData2 );
+ AssignChanged( sLocalVars, updLocalVars );
+
+ return err;
+} // AdaptItem
+
+
+void TDB_Api_Session::ThreadMayChangeNow()
+{
+ VoidProc p= (VoidProc)dm->se.Session_ThreadMayChangeNow;
+ p( sContext );
+} // ThreadMayChangeNow
+
+
+
+void TDB_Api_Session::DispItems( bool allFields, cAppCharP specificItem )
+{
+ DispProc p= (DispProc)dm->se.Session_DispItems;
+ p( sContext, allFields,specificItem );
+} // DispItems
+
+
+
+/* ---- Delete the session context ---------------------------- */
+TSyError TDB_Api_Session::DeleteContext()
+{
+ if (!sCreated) return DB_Forbidden;
+
+ // remove all still allocated elements before removing the content
+ while (!fSList.empty()) DisposeStr( *fSList.front() );
+
+ Context_Func p= (Context_Func)dm->se.Session_DeleteContext;
+ TSyError err= p( sContext );
+ if (!err) sCreated= false;
+ return err;
+} // DeleteContext
+
+
+
+/* ---- "DB_Api" implementation ------------------------------------------- */
+TDB_Api::TDB_Api()
+{
+ fCreated= false;
+
+ appPointer modu= NULL;
+ connect_no_dbapi( modu, fNo_dbapi ); // the empty connector is default as well
+ dm= &fNo_dbapi; // and assign it
+} // constructor
+
+
+TDB_Api::~TDB_Api()
+{
+ DeleteContext();
+} // destructor
+
+
+
+// --- open section -------------------------------------
+TSyError TDB_Api::CreateContext( cAppCharP aContextName, bool asAdmin, TDB_Api_Config* config,
+ cAppCharP sDevKey,
+ cAppCharP sUsrKey, TDB_Api_Session* session )
+{
+ if (fCreated || !config ||
+ !config->fConnected) return DB_Forbidden;
+
+ string cNam= aContextName;
+ if (cNam.empty()) return DB_NotFound; // empty <aContextName> is not allowed
+
+ fConfig= config; // make a local copy
+
+ /* inherit the context information of module and session */
+ fCB.Callback.cContext= config->mContext;
+ fCB.Callback.mContext= config->fCB.Callback.mContext;
+ if (session) { fCB.Callback.sContext= session->fCB.Callback.sContext; }
+ fDevKey = sDevKey;
+ fUsrKey = sUsrKey;
+
+ if (config->fADMIN_Info && asAdmin) cNam+= ADMIN_Ident;
+
+ fContext= 0; dm= &config->m;
+ CreateD_Func p= (CreateD_Func)dm->ds.CreateContext;
+ TSyError err= p( &fContext, cNam.c_str(), &fCB.Callback, fDevKey.c_str(),
+ fUsrKey.c_str() );
+ if (!err) fCreated= true;
+ return err;
+} // CreateContext
+
+
+
+TSyError TDB_Api::RunContext( cAppCharP aContextName, bool asAdmin,
+ SequenceProc sequence,
+ string &token, TDB_Api_Config* config,
+ cAppCharP sDevKey,
+ cAppCharP sUsrKey, TDB_Api_Session* session )
+{
+ TSyError err= CreateContext( aContextName, asAdmin, config,
+ sDevKey,sUsrKey, session ); if (err) return err;
+ TSyError dErr;
+ TDB_Api_Str newToken;
+
+ err= StartDataRead( token.c_str() );
+ // no sequence is allowed as well
+ if (!err && sequence!=NULL) {
+ err = sequence( *this );
+ dErr= EndDataWrite( !err, newToken ); if (!err) err= dErr;
+ token= newToken.c_str();
+ newToken.DisposeStr();
+ } // if
+
+ dErr= DeleteContext(); if (!err) err= dErr;
+ return err;
+} // RunContext
+
+
+
+// ---------------------------------------------------------------------
+// returns the nth config data field, which is supported (and activated)
+// result= 0: no filter supported.
+uInt32 TDB_Api::ContextSupport( cAppCharP aContextRules )
+{
+ Text_Func p= (Text_Func)dm->ds.dsg.ContextSupport;
+ return p( fContext,aContextRules );
+} // ContextSupport
+
+
+// returns the nth filter rule, which is supported (and activated)
+// result= 0: no filter supported.
+uInt32 TDB_Api::FilterSupport ( cAppCharP aFilterRules )
+{
+ Text_Func p= (Text_Func)dm->ds.dsg.FilterSupport;
+ return p( fContext,aFilterRules );
+} // FilterSupport
+
+
+
+// ---------------------------------------------------------------------
+// Str disposer
+void TDB_Api::DisposeStr( TDB_Api_Str &s )
+{
+ fSList.remove( &s ); // remove it from the list first to avoid double free
+
+ s.AssignStr( fContext, (DisposeProc)dm->ds.DisposeObj );
+ s.DisposeStr();
+} // DisposeStr
+
+
+
+// Local wrapper, because it must be DisposeProc type and can't be C++ method
+static void Local_DisposeStr( CContext aObj, void* memory )
+{
+ TDB_Api* a= (TDB_Api*)aObj;
+ TDB_Api_Str* u= (TDB_Api_Str*)memory; a->DisposeStr( *u );
+} // Local_DisposeStr
+
+
+
+void TDB_Api::AssignStr( TDB_Api_Str &s )
+{
+ if (s.fStr==NULL) return;
+
+ s.AssignStr( (CContext)this, Local_DisposeStr, true );
+ fSList.push_back( &s ); // add the element to the list
+} // AssignStr
+
+
+
+// ---------------------------------------------------------------------
+// Blk disposer
+void TDB_Api::DisposeBlk( TDB_Api_Blk &b )
+{
+ fBList.remove( &b ); // remove it from the list first to avoid double free
+
+ b.AssignBlk( fContext, (DisposeProc)dm->ds.DisposeObj );
+ b.DisposeBlk();
+} // DisposeBlk
+
+
+
+// Local wrapper, because it must be DisposeProc type and can't be C++ method
+static void Local_DisposeBlk( CContext aObj, void* memory )
+{
+ TDB_Api* a= (TDB_Api*)aObj;
+ TDB_Api_Blk* u= (TDB_Api_Blk*)memory; a->DisposeBlk( *u );
+} // Local_DisposeBlk
+
+
+
+void TDB_Api::AssignBlk( TDB_Api_Blk &b )
+{
+ if (b.fPtr==NULL) return;
+
+ b.AssignBlk( (CContext)this, Local_DisposeBlk, true );
+ fBList.push_back( &b ); // add the element to the list
+} // AssignBlk
+
+
+
+// ---------------------------------------------------------------------
+void TDB_Api::GetItemID( TDB_Api_ItemID &aID, TDB_Api_Str &aItemID )
+{
+ aItemID= aID.item;
+ fSList.remove( &aID.item );
+ aID.item.fStr= NULL; // avoid double delete
+ AssignStr ( aItemID );
+
+ aID.parent.DisposeStr(); // not used
+} // GetItemID
+
+
+
+// --- admin section -------------------------------
+// This function gets the stored information about the record with the four paramters:
+// <sDevKey>, <sUsrKey> (taken from the session context)
+// <aLocDB>, <aRemDB>.
+TSyError TDB_Api::LoadAdminData( cAppCharP aLocDB,
+ cAppCharP aRemDB, TDB_Api_Str &adminData )
+{
+ adminData.DisposeStr();
+ LoadAdm_Func p= (LoadAdm_Func)dm->ds.dsAdmin.LoadAdminData;
+ TSyError err= p( fContext, aLocDB,aRemDB, &adminData.fStr );
+ if (!err) AssignStr( adminData );
+ return err;
+} // LoadAdminData
+
+
+
+//! This functions stores the new <adminData> for this context
+TSyError TDB_Api::SaveAdminData( cAppCharP adminData )
+{
+ SvInfo_Func p= (SvInfo_Func)dm->ds.dsAdmin.SaveAdminData;
+ return p( fContext, adminData );
+} // SaveAdminData
+
+
+
+// --- Map table handling ----------------------------
+//! Get a map item of this context
+bool TDB_Api::ReadNextMapItem( TDB_Api_MapID &mID, bool aFirst )
+{
+ MapID_Struct u;
+ mID.localID.DisposeStr();
+ mID.remoteID.DisposeStr();
+
+ RdNMap_Func p= (RdNMap_Func)dm->ds.dsAdmin.ReadNextMapItem;
+ bool ok= p( fContext, &u, aFirst );
+ if (ok) {
+ mID.localID.fStr = u.localID; AssignStr( mID.localID );
+ mID.remoteID.fStr= u.remoteID; AssignStr( mID.remoteID );
+ mID.flags = u.flags;
+ mID.ident = u.ident;
+ } // if
+
+ return ok;
+} // ReadNextMapItem
+
+
+
+// Insert a map item of this context
+TSyError TDB_Api::InsertMapItem( MapID mID )
+{
+ InsMap_Func p= (InsMap_Func)dm->ds.dsAdmin.InsertMapItem;
+ return p( fContext, mID );
+} // InsertMapItem
+
+
+
+// Update a map item of this context
+TSyError TDB_Api::UpdateMapItem( MapID mID )
+{
+ UpdMap_Func p= (UpdMap_Func)dm->ds.dsAdmin.UpdateMapItem;
+ return p( fContext, mID );
+} // UpdateMapItem
+
+
+
+// Delete a map item of this context
+TSyError TDB_Api::DeleteMapItem( MapID mID )
+{
+ DelMap_Func p= (DelMap_Func)dm->ds.dsAdmin.DeleteMapItem;
+ return p( fContext, mID );
+} // DeleteMapItem
+
+
+
+// --- general -----------------------------------------
+void TDB_Api::ThreadMayChangeNow()
+{
+ VoidProc p= (VoidProc)dm->ds.dsg.ThreadMayChangeNow;
+ p( fContext );
+} // ThreadMayChangeNow
+
+
+
+//! This functions gets the <logData> for this datastore
+void TDB_Api::WriteLogData( cAppCharP logData )
+{
+ SvInfo_Func p= (SvInfo_Func)dm->ds.dsg.WriteLogData;
+ p( fContext, logData );
+} // WriteLogData
+
+
+
+void TDB_Api::DispItems( bool allFields, cAppCharP specificItem )
+{
+ DispProc p= (DispProc)dm->ds.dsg.DispItems;
+ p( fContext, allFields, specificItem );
+} // DispItems
+
+
+void TDB_Api::AssignChanged( string &a, TDB_Api_Str &u ) {
+ if (a.c_str()!=u.fStr) { a= u.fStr; AssignStr( u ); } // assign for destruction afterwards
+} // AssignChanged
+
+
+TSyError TDB_Api::AdaptItem( string &sItemData1,
+ string &sItemData2,
+ string &sLocalVars, uInt32 sIdentifier )
+{
+ TDB_Api_Str updItemData1( sItemData1 );
+ TDB_Api_Str updItemData2( sItemData2 );
+ TDB_Api_Str updLocalVars( sLocalVars );
+
+ Adapt_Func p= (Adapt_Func)dm->ds.dsAdapt.AdaptItem;
+ TSyError err= p( fContext, &updItemData1.fStr,
+ &updItemData2.fStr,
+ &updLocalVars.fStr, sIdentifier );
+
+ AssignChanged( sItemData1, updItemData1 );
+ AssignChanged( sItemData2, updItemData2 );
+ AssignChanged( sLocalVars, updLocalVars );
+
+ return err;
+} // AdaptItem
+
+
+
+// --- read section ------------------------------------
+TSyError TDB_Api::StartDataRead( cAppCharP lastToken, cAppCharP resumeToken )
+{
+ typedef TSyError (*OLD_SDR_Func)( CContext aContext, cAppCharP lastToken );
+
+ if (!fCreated) return DB_Fatal; // <fConfig> is not defined, if not created
+
+ // new param supported for Plugin Version >= 1.0.6.X
+ if (fConfig->Supported( VP_ResumeToken )) {
+ SDR_Func p= (SDR_Func)dm->ds.dsData.StartDataRead;
+ return p( fContext, lastToken,resumeToken );
+ }
+ else {
+ OLD_SDR_Func p= (OLD_SDR_Func)dm->ds.dsData.StartDataRead;
+ return p( fContext, lastToken );
+ } // if
+} // StartDataRead
+
+
+// overloaded for using DB:Api_ItemID class
+TSyError TDB_Api::ReadNextItem( TDB_Api_ItemID &aID,
+ TDB_Api_Str &aItemData, int &aStatus, bool aFirst )
+{
+ ItemID_Struct u;
+ sInt32 lStatus;
+
+ aID.item.DisposeStr();
+ aID.parent.DisposeStr();
+ aItemData.DisposeStr();
+
+ u.parent= NULL; // works correctly, even if not implemented on user side
+ RdNItemSFunc p= (RdNItemSFunc)dm->ds.dsData.str.ReadNextItem;
+ TSyError err= p( fContext, &u, &aItemData.fStr, &lStatus, aFirst ); aStatus= (int)lStatus;
+ if (err || aStatus==ReadNextItem_EOF) return err;
+
+ aID.item.fStr = u.item; AssignStr( aID.item );
+ aID.parent.fStr= u.parent; AssignStr( aID.parent );
+ AssignStr( aItemData );
+
+ // additional info for test
+//DEBUG_Exotic_DB( fCB, aID.item.c_str(), "ReadNextItem", aItemData.c_str() );
+
+ return LOCERR_OK;
+} // ReadNextItem
+
+
+// overloaded for mode w/o aParentID
+TSyError TDB_Api::ReadNextItem( TDB_Api_Str &aItemID,
+ TDB_Api_Str &aItemData,
+ int &aStatus, bool aFirst )
+{
+ TDB_Api_ItemID aID;
+
+ TSyError err= ReadNextItem( aID, aItemData, aStatus, aFirst );
+ if (err || aStatus==ReadNextItem_EOF) return err;
+ GetItemID ( aID, aItemID );
+ return LOCERR_OK;
+} // ReadNextItem
+
+
+TSyError TDB_Api::ReadNextItemAsKey( TDB_Api_ItemID &aID,
+ KeyH aItemKey,
+ int &aStatus, bool aFirst )
+{
+ ItemID_Struct u;
+ sInt32 lStatus;
+
+ aID.item.DisposeStr();
+ aID.parent.DisposeStr();
+
+ u.parent= NULL; // works correctly, even if not implemented on user side
+ RdNItemKFunc p= (RdNItemKFunc)dm->ds.dsData.key.ReadNextItemAsKey;
+ TSyError err= p( fContext, &u, aItemKey, &lStatus, aFirst ); aStatus= (int) lStatus;
+ if (err || aStatus==ReadNextItem_EOF) return err;
+
+ aID.item.fStr = u.item; AssignStr( aID.item );
+ aID.parent.fStr= u.parent; AssignStr( aID.parent );
+
+ return LOCERR_OK;
+} // ReadNextItemAsKey
+
+
+
+// overloaded for using ItemID_Struct
+TSyError TDB_Api::ReadItem( ItemID_Struct aID, TDB_Api_Str &aItemData )
+{
+ aItemData.DisposeStr();
+ Rd_ItemSFunc p= (Rd_ItemSFunc)dm->ds.dsData.str.ReadItem;
+ TSyError err= p( fContext, &aID, &aItemData.fStr );
+ if (!err) AssignStr ( aItemData );
+ return err;
+} // ReadItem
+
+
+// overloaded for <aItemID>,<aParentID> call
+TSyError TDB_Api::ReadItem( cAppCharP aItemID,
+ cAppCharP aParentID, TDB_Api_Str &aItemData )
+{
+ ItemID_Struct a; a.item = (char*) aItemID;
+ a.parent= (char*)aParentID;
+ return ReadItem( a, aItemData );
+} // ReadItem
+
+
+// overloaded for mode w/o aParentID
+TSyError TDB_Api::ReadItem( cAppCharP aItemID, TDB_Api_Str &aItemData ) {
+ return ReadItem( aItemID,"", aItemData );
+} // ReadItem
+
+
+TSyError TDB_Api::ReadItemAsKey( ItemID_Struct aID, KeyH aItemKey )
+{
+ Rd_ItemKFunc p= (Rd_ItemKFunc)dm->ds.dsData.key.ReadItemAsKey;
+ TSyError err= p( fContext, &aID, aItemKey );
+ return err;
+} // ReadItemAsKey
+
+
+
+// overloaded for using ItemID_Struct
+TSyError TDB_Api::ReadBlob( ItemID_Struct aID,
+ cAppCharP aBlobID, memSize blkSize,
+ TDB_Api_Blk &aBlk, memSize &totSize, bool aFirst, bool &aLast )
+{
+ typedef TSyError (*Rd_Blob_Func)( CContext aContext,
+ cItemID aID,
+ cAppCharP aBlobID,
+ appPointer *blkPtr,
+ memSize *blkSize,
+ memSize *totSize,
+ bool aFirst,
+ bool *aLast );
+ aBlk.DisposeBlk();
+ aBlk.fSize= blkSize;
+
+ Rd_Blob_Func p= (Rd_Blob_Func)dm->ds.dsBlob.ReadBlob;
+ TSyError err= p( fContext, &aID,aBlobID,
+ &aBlk.fPtr,&aBlk.fSize, &totSize, aFirst,&aLast );
+ if (!err) aBlk.AssignBlk( fContext, (DisposeProc)dm->ds.DisposeObj );
+ return err;
+} // ReadBlob
+
+
+// overloaded for <aItemID>,<aParentID> call
+TSyError TDB_Api::ReadBlob( cAppCharP aItemID,
+ cAppCharP aParentID,
+ cAppCharP aBlobID, memSize blkSize,
+ TDB_Api_Blk &aBlk, memSize &totSize, bool aFirst, bool &aLast )
+{
+ ItemID_Struct a; a.item = (char*)aItemID;
+ a.parent= (char*)aParentID;
+ return ReadBlob( a, aBlobID, blkSize,aBlk,totSize, aFirst,aLast );
+} // ReadBlob
+
+
+// overloaded for mode w/o aParentID
+TSyError TDB_Api::ReadBlob( cAppCharP aItemID,
+ cAppCharP aBlobID, memSize blkSize,
+ TDB_Api_Blk &aBlk, memSize &totSize, bool aFirst, bool &aLast ) {
+ return ReadBlob( aItemID,"", aBlobID, blkSize,aBlk,totSize, aFirst,aLast );
+} // ReadBlob
+
+
+
+TSyError TDB_Api::EndDataRead()
+{
+ EDR_Func p= (EDR_Func)dm->ds.dsData.EndDataRead;
+ return p( fContext );
+} // EndDataRead
+
+
+
+// --- write section ----------------------------------------------------------------------
+TSyError TDB_Api::StartDataWrite()
+{
+ SDW_Func p= (SDW_Func)dm->ds.dsData.StartDataWrite;
+ return p( fContext );
+} // StartDataWrite
+
+
+
+// Assign <aID> to <newID>, ignore new parent
+void TDB_Api::Assign_ItemID( TDB_Api_ItemID &newID, ItemID_Struct &a, cAppCharP parentID )
+{
+ newID.item.fStr= a.item;
+ AssignStr ( newID.item );
+
+ if (a.parent!=parentID) { // remove it, if explicitely allocated
+ newID.parent.fStr= a.parent; // parent can't be changed !!
+ AssignStr( newID.parent );
+ newID.parent.DisposeStr();
+ } // if
+
+ newID.parent.LocalAlloc( fContext, parentID );
+} // Assign_ItemID
+
+
+// -------------------------------------------------------------------------
+// overloaded for <aParentID> call
+TSyError TDB_Api::InsertItem( cAppCharP aItemData, cAppCharP parentID,
+ TDB_Api_ItemID &newID )
+{
+ newID.item.DisposeStr();
+ newID.parent.DisposeStr();
+
+ ItemID_Struct a;
+ a.item = NULL;
+ a.parent= (char*)parentID; // works correctly, even if not implemented on user side
+
+ InsItemSFunc p= (InsItemSFunc)dm->ds.dsData.str.InsertItem;
+ TSyError err= p( fContext, aItemData, &a );
+ if (!err || err==DB_DataMerged) {
+ Assign_ItemID( newID, a, parentID );
+ } // if
+
+ return err;
+} // InsertItem
+
+
+// overloaded for mode w/o aParentID
+TSyError TDB_Api::InsertItem( cAppCharP aItemData, TDB_Api_Str &newItemID )
+{
+ TDB_Api_ItemID nID;
+ TSyError err= InsertItem( aItemData, "",nID );
+ if (!err ||
+ err==DB_DataMerged) GetItemID( nID, newItemID );
+ return err;
+} // InsertItem
+
+
+TSyError TDB_Api::InsertItemAsKey( KeyH aItemKey, cAppCharP parentID,
+ TDB_Api_ItemID &newID )
+{
+ newID.item.DisposeStr();
+ newID.parent.DisposeStr();
+
+ ItemID_Struct a;
+ a.item = NULL;
+ a.parent= (char*)parentID; // works correctly, even if not implemented on user side
+
+ InsItemKFunc p= (InsItemKFunc)dm->ds.dsData.key.InsertItemAsKey;
+ TSyError err= p( fContext, aItemKey, &a );
+ if (!err ||
+ err==DB_DataMerged) {
+ Assign_ItemID( newID, a, parentID );
+ } // if
+
+ return err;
+} // InsertItemAsKey
+
+
+
+// -------------------------------------------------------------------------
+// overloaded for using ItemID_Struct
+TSyError TDB_Api::UpdateItem( cAppCharP aItemData, ItemID_Struct aID,
+ TDB_Api_ItemID &updID )
+{
+ updID.item.DisposeStr();
+ updID.parent.DisposeStr();
+
+ ItemID_Struct u;
+ u.item = NULL;
+ u.parent= (char*)aID.parent; // works correctly, even if not implemented on user side
+
+ UpdItemSFunc p= (UpdItemSFunc)dm->ds.dsData.str.UpdateItem;
+ TSyError err= p( fContext, aItemData, &aID,&u );
+ if (!err) {
+ Assign_ItemID( updID, u, aID.parent );
+ } // if
+
+ return err;
+} // UpdateItem
+
+
+// overloaded for <aItemID>,<aParentID> call
+TSyError TDB_Api::UpdateItem( cAppCharP aItemData,
+ cAppCharP aItemID,
+ cAppCharP aParentID, TDB_Api_ItemID &updID )
+{
+ ItemID_Struct a; a.item = (char*)aItemID;
+ a.parent= (char*)aParentID;
+ return UpdateItem( aItemData, a,updID );
+} // UpdateItem
+
+
+// overloaded for mode w/o aParentID
+TSyError TDB_Api::UpdateItem( cAppCharP aItemData, cAppCharP aItemID,
+ TDB_Api_Str &updItemID )
+{
+ TDB_Api_ItemID uID;
+ TSyError err= UpdateItem( aItemData, aItemID,"", uID );
+ if (!err) GetItemID( uID, updItemID );
+ return err;
+} // UpdateItem
+
+
+TSyError TDB_Api::UpdateItem( cAppCharP aItemData, TDB_Api_ItemID &aID,
+ TDB_Api_ItemID &updID )
+{
+ return UpdateItem( aItemData, (char*)aID.item.c_str(),
+ (char*)aID.parent.c_str(), updID );
+} // UpdateItem
+
+
+
+TSyError TDB_Api::UpdateItemAsKey( KeyH aItemKey, ItemID_Struct aID,
+ TDB_Api_ItemID &updID )
+{
+ updID.item.DisposeStr();
+ updID.parent.DisposeStr();
+
+ ItemID_Struct u;
+ u.item = NULL;
+ u.parent= (char*)aID.parent; // works correctly, even if not implemented on user side
+
+ UpdItemKFunc p= (UpdItemKFunc)dm->ds.dsData.key.UpdateItemAsKey;
+ TSyError err= p( fContext, aItemKey, &aID,&u );
+ if (!err) {
+ Assign_ItemID( updID, u, aID.parent );
+ } // if
+
+ return err;
+} // UpdateItemAsKey
+
+
+TSyError TDB_Api::UpdateItemAsKey( KeyH aItemKey, TDB_Api_ItemID &aID,
+ TDB_Api_ItemID &updID )
+{
+ ItemID_Struct a; a.item = (char*)aID.item.c_str();
+ a.parent= (char*)aID.parent.c_str();
+ return UpdateItemAsKey( aItemKey, a, updID );
+} // UpdateItemAsKey
+
+
+
+// -------------------------------------------------------------------------
+// overloaded for using ItemID_Struct
+TSyError TDB_Api::MoveItem( ItemID_Struct aID, cAppCharP newParID )
+{
+ MovItem_Func p= (MovItem_Func)dm->ds.dsData.ind.MoveItem;
+ return p( fContext, &aID,newParID );
+} // MoveItem
+
+
+// overloaded for <aItemID>,<aParentID> call
+TSyError TDB_Api::MoveItem( cAppCharP aItemID,
+ cAppCharP aParentID, cAppCharP newParID )
+{
+ ItemID_Struct a; a.item = (appCharP) aItemID;
+ a.parent= (appCharP)aParentID;
+ return MoveItem( a, newParID );
+} // MoveItem
+
+
+
+// -------------------------------------------------------------------------
+// overloaded for using ItemID_Struct
+TSyError TDB_Api::DeleteItem( ItemID_Struct aID )
+{
+ DelItem_Func p= (DelItem_Func)dm->ds.dsData.ind.DeleteItem;
+ return p( fContext, &aID );
+} // DeleteItem
+
+
+// overloaded for <aItemID>,<aParentID> call
+TSyError TDB_Api::DeleteItem( cAppCharP aItemID, cAppCharP aParentID )
+{
+ ItemID_Struct a; a.item = (char*)aItemID;
+ a.parent= (char*)aParentID;
+ return DeleteItem( a );
+} // DeleteItem
+
+
+// overloaded for using TDB_Api_ItemID
+// <aID> object will not be removed here
+TSyError TDB_Api::DeleteItem( TDB_Api_ItemID &aID )
+{
+ return DeleteItem( (char*)aID.item.c_str(),
+ (char*)aID.parent.c_str() );
+} // DeleteItem
+
+
+
+// -------------------------------------------------------------------------
+TSyError TDB_Api::FinalizeLocalID( ItemID_Struct aID,
+ TDB_Api_ItemID &updID )
+{
+ updID.item.DisposeStr();
+ updID.parent.DisposeStr();
+
+ if (!fConfig->Supported( VP_FLI_DSS )) return LOCERR_NOTIMP;
+
+ ItemID_Struct u;
+ u.item = NULL;
+ u.parent= (char*)aID.parent; // works correctly, even if not implemented on user side
+
+ FLI_Func p= (FLI_Func)dm->ds.dsData.ind.FinalizeLocalID;
+ TSyError err= p( fContext, &aID,&u );
+ if (!err) {
+ Assign_ItemID( updID, u, aID.parent );
+ } // if
+
+ return err;
+} // FinalizeLocalID
+
+
+TSyError TDB_Api::FinalizeLocalID( TDB_Api_ItemID &aID,
+ TDB_Api_ItemID &updID ) {
+ return FinalizeLocalID( (char*)aID.item.c_str(),
+ (char*)aID.parent.c_str(), updID );
+} // FinalizeLocalID
+
+
+TSyError TDB_Api::FinalizeLocalID( cAppCharP aItemID,
+ TDB_Api_Str &updItemID )
+{
+ TDB_Api_ItemID uID;
+ TSyError err= FinalizeLocalID( aItemID,"", uID );
+ if (!err) GetItemID( uID, updItemID );
+ return err;
+} // FinalizeLocalID
+
+
+TSyError TDB_Api::FinalizeLocalID( cAppCharP aItemID,
+ cAppCharP aParentID,
+ TDB_Api_ItemID &updID )
+{
+ ItemID_Struct a; a.item = (char*)aItemID;
+ a.parent= (char*)aParentID;
+ return FinalizeLocalID( a, updID );
+} // FinalizeLocalID
+
+
+
+TSyError TDB_Api::DeleteSyncSet()
+{
+ if (!fConfig->Supported( VP_FLI_DSS )) return LOCERR_NOTIMP;
+
+ DelSS_Func p= (DelSS_Func)dm->ds.dsData.ind.DeleteSyncSet;
+ return p( fContext );
+} // DeleteSyncSet
+
+
+
+// -------------------------------------------------------------------------
+// overloaded for using ItemID_Struct
+TSyError TDB_Api::WriteBlob( ItemID_Struct aID, cAppCharP aBlobID,
+ appPointer blkPtr, memSize blkSize,
+ memSize totSize, bool aFirst, bool aLast )
+{
+ typedef TSyError (*Wr_BlobFunc)( CContext aContext,
+ cItemID aID,
+ cAppCharP aBlobID,
+ appPointer blkPtr,
+ ulong blkSize,
+ ulong totSize,
+ bool aFirst,
+ bool aLast );
+
+ Wr_BlobFunc p= (Wr_BlobFunc)dm->ds.dsBlob.WriteBlob;
+ return p( fContext, &aID,aBlobID, blkPtr,blkSize,totSize, aFirst,aLast );
+} // WriteBlob
+
+
+// overloaded for mode with <aItemID> and <aParentID>
+TSyError TDB_Api::WriteBlob( cAppCharP aItemID,
+ cAppCharP aParentID, cAppCharP aBlobID,
+ appPointer blkPtr, memSize blkSize,
+ memSize totSize, bool aFirst, bool aLast )
+{
+ ItemID_Struct a; a.item = (char*)aItemID;
+ a.parent= (char*)aParentID;
+ return WriteBlob( a,aBlobID, blkPtr,blkSize,totSize, aFirst,aLast );
+} // WriteBlob
+
+
+// overloaded for mode w/o <aParentID>
+TSyError TDB_Api::WriteBlob( cAppCharP aItemID, cAppCharP aBlobID,
+ appPointer blkPtr, memSize blkSize,
+ memSize totSize, bool aFirst, bool aLast )
+{
+ return WriteBlob( aItemID,"", aBlobID, blkPtr,blkSize,totSize, aFirst,aLast );
+} // WriteBlob
+
+
+// overloaded for using TDB_Api_ItemID
+TSyError TDB_Api::WriteBlob( TDB_Api_ItemID &aID, cAppCharP aBlobID,
+ appPointer blkPtr, memSize blkSize,
+ memSize totSize, bool aFirst, bool aLast )
+{
+ return WriteBlob( (char*)aID.item.c_str(),
+ (char*)aID.parent.c_str(), aBlobID, blkPtr,blkSize,totSize, aFirst,aLast );
+} // WriteBlob
+
+
+
+// -------------------------------------------------------------------------
+// overloaded for using ItemID_Struct
+TSyError TDB_Api::DeleteBlob( ItemID_Struct aID, cAppCharP aBlobID )
+{
+ typedef TSyError (*DelBlobFunc)( CContext aContext,
+ cItemID aID,
+ cAppCharP aBlobID );
+
+ DelBlobFunc p= (DelBlobFunc)dm->ds.dsBlob.DeleteBlob;
+ return p( fContext, &aID,aBlobID );
+} // DeleteBlob
+
+
+// overloaded for mode with <aItemID> and <aParentID>
+TSyError TDB_Api::DeleteBlob( cAppCharP aItemID,
+ cAppCharP aParentID,
+ cAppCharP aBlobID )
+{
+ ItemID_Struct a; a.item = (char*)aItemID;
+ a.parent= (char*)aParentID;
+ return DeleteBlob( a,aBlobID );
+} // DeleteBlob
+
+
+// overloaded for mode w/o <aParentID>
+TSyError TDB_Api::DeleteBlob( cAppCharP aItemID,
+ cAppCharP aBlobID )
+{
+ return DeleteBlob( aItemID,"", aBlobID );
+} // DeleteBlob
+
+
+// overloaded for using TDB_Api_ItemID
+TSyError TDB_Api::DeleteBlob( TDB_Api_ItemID &aID, cAppCharP aBlobID )
+{
+ return DeleteBlob( (char*)aID.item.c_str(),
+ (char*)aID.parent.c_str(), aBlobID );
+} // DeleteBlob
+
+
+
+// -------------------------------------------------------------------------
+TSyError TDB_Api::EndDataWrite( bool success, TDB_Api_Str &newToken )
+{
+ newToken.DisposeStr();
+ EDW_Func p= (EDW_Func)dm->ds.dsData.EndDataWrite;
+ TSyError err= p( fContext, success, &newToken.fStr );
+ if (!err) {
+ AssignStr( newToken );
+ if (!success) err= DB_Fatal;
+ } // if
+
+ return err;
+} // EndDataWrite
+
+
+
+// --- close section --------------------------------
+TSyError TDB_Api::DeleteContext()
+{
+ if (!fCreated) return DB_Forbidden;
+
+ // remove all still allocated elements before removing the content
+ while (!fSList.empty()) DisposeStr( *fSList.front() );
+ while (!fBList.empty()) DisposeBlk( *fBList.front() );
+
+ Context_Func p= (Context_Func)dm->ds.DeleteContext;
+ TSyError err= p( fContext );
+ if (!err) fCreated= false;
+ return err;
+} // DeleteContext
+
+
+} // namespace
+/* eof */
diff --git a/src/DB_interfaces/api_db/dbapi.h b/src/DB_interfaces/api_db/dbapi.h
new file mode 100755
index 0000000..3cf00d2
--- /dev/null
+++ b/src/DB_interfaces/api_db/dbapi.h
@@ -0,0 +1,556 @@
+/*
+ * File: dbapi.h
+ *
+ * Author: Beat Forster (bfo@synthesis.ch)
+ *
+ * TDB_Api class
+ * Bridge to user programmable interface
+ *
+ * Copyright (c) 2004-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ * The "TDB_Api" class acts as a standard interface between
+ * the SySync Server and a (user programmable) module "sync_dbapi".
+ *
+ * It is possible to have more than one (identical) interface module,
+ * either packed into a DLL or directly linked to the server
+ * (or combined).
+ *
+ * - In case of DLL, the object must be created with
+ * <interfaceName> = DLL file name, e.g "sync_dbapi.dll"
+ * The constructor automatically creates the connection to the DLL,
+ * the destructor releases the DLL.
+ *
+ * - In case of the directly linked module, it runs in a
+ * specific namespace, which can be assigned by calling
+ * <interfaceName> = '['<namespace>']', e.g. "[example1]".
+ * (Of course an internal implementation for each namespace must
+ * exist!)
+ *
+ * NOTE: The public method names are identical to the ones of the
+ * user interface, but not all parameters are visible on the
+ * object side, e.g. the context variable is held private
+ * within the object.
+ * The methods will be assigned internally (at API_Methods),
+ * when the object is created.
+ */
+
+
+#ifndef DB_API_H
+#define DB_API_H
+
+// access to the definitions of the interface
+#include <list>
+#include <string>
+#include "sync_dbapiconnect.h"
+#include "sync_dbapidef.h"
+
+
+namespace sysync {
+
+
+/* -- Utility procs -- */
+bool DSConnect( cAppCharP aItem );
+
+
+
+/* -- handling for "sync_dbapi" returned strings -- */
+class TDB_Api_Str
+{
+ friend class TDB_Api_Config;
+ friend class TDB_Api_Session;
+ friend class TDB_Api;
+
+ public:
+ TDB_Api_Str(); // constructor
+ TDB_Api_Str( string &s ); // alternative constructor
+ ~TDB_Api_Str(); // destructor
+
+ // the string reference
+ cAppCharP c_str() const { return (fStr==NULL) ? "":fStr; }
+ bool empty(void) { return *c_str()==0; }
+ int length() const { return sizeof( c_str() ); };
+
+ // has the same effect as the destructor, but can be called earlier
+ void DisposeStr();
+
+ private:
+ void AssignStr ( CContext aContext, DisposeProc aDisposeProc, bool itself= false );
+ void LocalAlloc( CContext aContext, cAppCharP ); // if memory must be allocated locally
+
+ CContext fContext;
+ char* fStr;
+ bool fItself; // if true: Dispose <this> instead of <fStr>
+ DisposeProc fDisposeProc;
+ void clear() { fDisposeProc= NULL; fStr= NULL; }
+}; // TDB_Api_Str
+
+
+
+/* -- handling for "sync_dbapi" returned itemID/parentID strings */
+class TDB_Api_ItemID
+{
+ public:
+ TDB_Api_Str item;
+ TDB_Api_Str parent;
+}; // TDB_Api_ItemID
+
+
+
+class TDB_Api_MapID
+{
+ public:
+ TDB_Api_Str localID;
+ TDB_Api_Str remoteID;
+ uInt16 flags;
+ uInt8 ident;
+}; // TDB_Api_MapID
+
+
+
+/* -- handling for "sync_dbapi" returned blocks */
+class TDB_Api_Blk
+{
+ friend class TDB_Api_Config;
+ friend class TDB_Api;
+
+ public:
+ TDB_Api_Blk(); // constructor
+ ~TDB_Api_Blk(); // destructor
+
+ appPointer fPtr; // the block reference
+ memSize fSize; // the block size
+ void DisposeBlk(); // has the same effect as the destructor, but can be called earlier
+
+ protected:
+ void AssignBlk( CContext aContext, DisposeProc aDisposeProc, bool itself= false );
+
+ private:
+ CContext fContext;
+ bool fItself; // if true: Dispose <this> instead of <fPtr>
+ DisposeProc fDisposeProc;
+ void clear() { fDisposeProc= NULL; fPtr= NULL; fSize= 0; }
+}; // TDB_Api_Blk
+
+
+
+// -- internally used type definitions ---------
+typedef TSyError (*CreateM_Func)( CContext *mc, cAppCharP moduleName,
+ cAppCharP subName,
+ cAppCharP mContextName, DB_Callback mCB );
+typedef CVersion (*Version_Func)( CContext mc );
+typedef TSyError (*Context_Func)( CContext mc );
+
+// ---------------------------------------------
+typedef TSyError (*CreateS_Func)( CContext *sc, cAppCharP sessionName, DB_Callback sCB );
+typedef TSyError (*SvInfo_Func)( CContext sc, cAppCharP info );
+typedef int (*PwMode_Func)( CContext sc );
+typedef TSyError (*Login_Func)( CContext sc, cAppCharP sUsername,
+ appCharP *sPassword,
+ appCharP *sUsrKey );
+// ---------------------------------------------
+typedef TSyError (*CreateD_Func)( CContext *ac, cAppCharP aContextName, DB_Callback aCB,
+ cAppCharP sDevKey,
+ cAppCharP sUsrKey );
+typedef int (*Text_Func)( CContext ac, cAppCharP aText );
+typedef void (*DispProc)( CContext ac, bool allFields, cAppCharP specificItem );
+
+typedef TSyError (*LoadAdm_Func)( CContext ac, cAppCharP aLocDB,
+ cAppCharP aRemDB,
+ appCharP *adminData );
+
+typedef bool (*RdNMap_Func)( CContext ac, MapID mID, bool aFirst );
+typedef TSyError (*InsMap_Func)( CContext ac, cMapID mID );
+typedef TSyError (*UpdMap_Func)( CContext ac, cMapID mID );
+typedef TSyError (*DelMap_Func)( CContext ac, cMapID mID );
+
+typedef void (*VoidProc)( CContext ac );
+
+typedef TSyError (*Adapt_Func)( CContext ac, appCharP *aItemData1,
+ appCharP *aItemData2,
+ appCharP *aLocalVars, sInt32 aIdentifier );
+// ---------------------------------------------
+
+
+
+// wrapper for DB_Callback
+class TDB_Api_Callback
+{
+ public:
+ TDB_Api_Callback();
+ SDK_Interface_Struct Callback;
+}; // TDB_Api_Callback
+
+
+// the module context class
+class TDB_Api_Config
+{
+ friend class TDB_Api_Session;
+ friend class TDB_Api;
+ friend class TUI_Api;
+
+ public:
+ TDB_Api_Config(); // constructor
+ ~TDB_Api_Config(); // destructor
+
+ // Connect to the plug-in <moduleName> with <mContextName>
+ // <globContext> must be 0 before called the first time
+ TSyError Connect( cAppCharP moduleName, CContext &globContext,
+ cAppCharP mContextName= "", bool aIsLib= false, bool allowDLL= true );
+
+ bool Connected() { return fConnected; } // read status of <fConnected>
+
+ // The plug-in module's version, capabilities and plugin params from config file
+ long Version(); // the plugin's SDK version number
+ long EngineSDKVersion(); // the internal engine's SDK version number
+ TSyError Capabilities( TDB_Api_Str &mCapabilities );
+ TSyError PluginParams( cAppCharP mConfigParams );
+
+ // Change the internal engine's SDK version;
+ // *** override the version number / for test only ***
+ void SetVersion( long versionNr );
+
+ /* Check the minimum required version */
+ TSyError MinVersionCheck( string capa, CVersion &vMin ); // Check, if MINVERSION is high enough
+
+ // Disconnect the module(at the end
+ TSyError Disconnect();
+
+ bool is_lib; // flag: as internal library
+ TDB_Api_Callback fCB; // Callback wrapper
+
+ cAppCharP ModName() { return fModName.c_str(); } // the <moduleName>
+ cAppCharP ModOptions() { return fOptions.c_str(); } // the module's parameters
+
+ void DisposeStr( TDB_Api_Str &s );
+ private:
+ void AssignStr( TDB_Api_Str &s ); // for internal use
+ void clear();
+
+ bool Supported( CVersion version_feature ); // Check, if <version_feature> is supported
+
+ // Internal Api assignment
+ TSyError DBApi_Assign( cAppCharP item, appPointer aField, memSize aFieldSize, cAppCharP aKey= "" );
+
+ CContext mContext; // the module's context
+ API_Methods m; // set of Lib/DLL routines
+
+ string fModName; // local copy of <aModName>
+ string fOptions; // local copy of module's paramters
+ string fModMain; // main part of <aModName> w/o brackets
+ string fModSub; // sub part of <aModName> w/o brackets
+ string fDesc; // module's description
+
+ bool fConnected; // if successful= API_Methods valid
+ bool fADMIN_Info; // ADMIN info will be given with "CreateContext"
+
+ CVersion fSDKversion; // The SDK's version (directly connected module)
+ CVersion fMODversion; // The SDK's version (lowest of the chain)
+ CVersion fTSTversion; // Modified version (*** for test only ***)
+ appPointer fMod; // internal module reference
+ list<TDB_Api_Str*> fSList; // list of currently allocated str objects of this session
+}; // class TDB_Api_Config
+
+
+// Display global context contents
+void DispGlobContext ( CContext &globContext, DB_Callback mCB );
+
+// Delete the global context at the end
+void DeleteGlobContext( CContext &globContext, DB_Callback mCB, bool emptyTextOnly= false );
+
+
+// the session context class
+class TDB_Api_Session
+{
+ public:
+ TDB_Api_Session(); // constructor
+
+ TSyError CreateContext ( cAppCharP sessionName, TDB_Api_Config &config );
+
+ TSyError CheckDevice ( cAppCharP deviceID, TDB_Api_Str &sDevKey, TDB_Api_Str &nonce );
+ TSyError GetNonce ( TDB_Api_Str &nonce );
+ TSyError SaveNonce ( cAppCharP nonce );
+ TSyError SaveDeviceInfo( cAppCharP aDeviceInfo );
+ TSyError GetDBTime ( TDB_Api_Str &currentDBTime );
+
+ #ifdef SYSYNC_ENGINE
+ TSyError GetDBTime ( lineartime_t &currentDBTime, GZones* g );
+ #endif
+
+ sInt32 PasswordMode();
+ TSyError Login( cAppCharP sUsername,
+ cAppCharP sPassword, TDB_Api_Str &sUsrKey ); // for password mode 0+3
+ TSyError Login( cAppCharP sUsername,
+ TDB_Api_Str &sPassword, TDB_Api_Str &sUsrKey ); // for password mode 1+2
+ TSyError Logout();
+
+ TSyError AdaptItem( string &sItemData1,
+ string &sItemData2,
+ string &sLocalVars, uInt32 sIdentifier );
+
+ void ThreadMayChangeNow(); // notification for the context
+ void DispItems( bool allFields= true, cAppCharP specificItem= "" ); // by default: all
+ TSyError DeleteContext();
+
+ TDB_Api_Callback fCB; // Callback wrapper
+
+ bool sCreated;
+ CContext sContext; // the session context
+
+ void DisposeStr( TDB_Api_Str &s );
+ private:
+ void AssignStr ( TDB_Api_Str &s ); // for internal use
+ void AssignChanged ( string &a, TDB_Api_Str &u );
+
+ API_Methods* dm; // local reference to the API methods
+ API_Methods sNo_dbapi; // default connection for call methods
+ string sSessionName; // local copy of <sessionName>
+ sInt32 sPwMode; // local copy of password mode
+
+ list<TDB_Api_Str*> fSList; // list of currently allocated str objects of this session
+}; // class TDB_Api_Session
+
+
+
+// for detailed description of the methods see
+// C interface definition at "sync_dbapi.h"
+
+// An object for each context must be created.
+// It is not possible to create more than one context per object
+// It is allowed to bind more than one context to the same module.
+class TDB_Api
+{
+ public:
+ TDB_Api(); // constructor
+ ~TDB_Api(); // destructor
+
+ // --- open section --------------------------------
+ //! Open a context, it is possible to do this w/o any session
+ TSyError CreateContext( cAppCharP aContextName, bool asAdmin, TDB_Api_Config* config,
+ cAppCharP sDevKey= "",
+ cAppCharP sUsrKey= "", TDB_Api_Session* session= NULL );
+
+ /*! Gets true after calling 'CreateContext'.
+ * Will return again false after 'DeleteContext'
+ */
+ bool Created() { return fCreated; };
+
+
+ //! run a sequence: CreateContext -> StartDataRead -> sequence -> EndDataWrite -> DeleteContext
+ //! <token> as input: the token of the last session, "" if the first time.
+ //! " as output: the new token, which must be given to the next session.
+ typedef TSyError(*SequenceProc)( TDB_Api &dbApi );
+ TSyError RunContext( cAppCharP aContextName, bool asAdmin,
+ SequenceProc sequence,
+ string &token, TDB_Api_Config* config,
+ cAppCharP sDevKey= "",
+ cAppCharP sUsrKey= "", TDB_Api_Session* session= NULL );
+
+
+ //! returns the nth config data field, which is supported (and activated)
+ //! result= 0: no filter supported.
+ uInt32 ContextSupport( cAppCharP aContextRules );
+
+
+ //! returns the nth filter rule, which is supported (and activated)
+ //! result= 0: no filter supported.
+ uInt32 FilterSupport( cAppCharP aFilterRules );
+
+
+
+ // --- admin section -------------------------------
+ //! This function gets the stored information about the record with the four paramters:
+ //! <sDevKey>, <sUsrKey> (taken from the session context)
+ //! <aLocDB>, <aRemDB>.
+ TSyError LoadAdminData( cAppCharP aLocDB,
+ cAppCharP aRemDB, TDB_Api_Str &adminData );
+
+ //! This functions stores the new <adminData> for this context
+ TSyError SaveAdminData( cAppCharP adminData );
+
+
+ // --- Map table handling
+ //! Get a map item of this context
+ bool ReadNextMapItem( TDB_Api_MapID &mID, bool aFirst= false );
+
+ //! Insert a map item of this context
+ TSyError InsertMapItem( MapID mID );
+
+ //! Update a map item of this context
+ TSyError UpdateMapItem( MapID mID );
+
+ //! Delete a map item of this context
+ TSyError DeleteMapItem( MapID mID );
+
+
+
+ // --- utility section -----------------------------
+ //! Get the current context value
+ CContext MyContext() { return fContext; }
+
+ //! Notification for the context
+ void ThreadMayChangeNow();
+
+ //! Write log information for the datastore access
+ void WriteLogData( cAppCharP logData );
+
+ //! Display the current items (not used by the engine)
+ void DispItems( bool allFields= true, cAppCharP specificItem= "" ); // by default: all
+
+
+ //! Adapt <aItemData1>,<aItemData2>,<aLocalVars> of script context, with <aIdentifier>
+ TSyError AdaptItem( string &aItemData1,
+ string &aItemData2,
+ string &aLocalVars, uInt32 aIdentifier );
+
+ // --- read section --------------------------------
+ TSyError StartDataRead( cAppCharP lastToken, cAppCharP resumeToken= "" );
+
+ // <aID>/<aItemID> and <aItemData> will automatically be disposed at the beginning
+ // 2 overloaded versions
+ TSyError ReadNextItem ( TDB_Api_ItemID &aID, TDB_Api_Str &aItemData, int &aStatus, bool aFirst= false );
+ TSyError ReadNextItem ( TDB_Api_Str &aItemID, TDB_Api_Str &aItemData, int &aStatus, bool aFirst= false );
+ TSyError ReadNextItemAsKey( TDB_Api_ItemID &aID, KeyH aItemKey, int &aStatus, bool aFirst= false );
+
+
+ // <aItemData> will automatically be disposed at the beginning
+ // 3 overloaded versions
+ TSyError ReadItem ( ItemID_Struct aID, TDB_Api_Str &aItemData );
+ TSyError ReadItem ( cAppCharP aItemID, TDB_Api_Str &aItemData );
+ TSyError ReadItem ( cAppCharP aItemID,
+ cAppCharP aParentID, TDB_Api_Str &aItemData );
+ TSyError ReadItemAsKey( ItemID_Struct aID, KeyH aItemKey );
+
+ // <aBlk> will automatically be disposed at the beginning
+ // 3 overloaded versions
+ TSyError ReadBlob( ItemID_Struct aID, cAppCharP aBlobID,
+ memSize blkSize,
+ TDB_Api_Blk &aBlk, memSize &totSize, bool aFirst, bool &aLast );
+
+ TSyError ReadBlob( cAppCharP aItemID, cAppCharP aBlobID,
+ memSize blkSize,
+ TDB_Api_Blk &aBlk, memSize &totSize, bool aFirst, bool &aLast );
+
+ TSyError ReadBlob( cAppCharP aItemID,
+ cAppCharP aParentID, cAppCharP aBlobID,
+ memSize blkSize,
+ TDB_Api_Blk &aBlk, memSize &totSize, bool aFirst, bool &aLast );
+
+ TSyError EndDataRead();
+
+
+
+ // --- write section --------------------------------
+ TSyError StartDataWrite();
+
+ // <newItemID> will automatically be disposed at the beginning.
+ // 2 overloaded versions
+ TSyError InsertItem ( cAppCharP aItemData, cAppCharP parentID, TDB_Api_ItemID &newID );
+ TSyError InsertItem ( cAppCharP aItemData, TDB_Api_Str &newItemID );
+
+ TSyError InsertItemAsKey( KeyH aItemKey, cAppCharP parentID, TDB_Api_ItemID &newID );
+
+
+ TSyError FinalizeLocalID( ItemID_Struct aID,
+ TDB_Api_ItemID &updID );
+ TSyError FinalizeLocalID( TDB_Api_ItemID &aID,
+ TDB_Api_ItemID &updID );// <aID> obj will not be removed here
+
+ TSyError FinalizeLocalID( cAppCharP aItemID,
+ TDB_Api_Str &updItemID );
+ TSyError FinalizeLocalID( cAppCharP aItemID,
+ cAppCharP aParentID,
+ TDB_Api_ItemID &updID );
+
+
+ // <updID> will automatically be disposed at the beginning.
+ // 4 overloaded versions
+ TSyError UpdateItem( cAppCharP aItemData, ItemID_Struct aID,
+ TDB_Api_ItemID &updID );
+ TSyError UpdateItem( cAppCharP aItemData, TDB_Api_ItemID &aID,
+ TDB_Api_ItemID &updID ); // <aID> obj will not be removed here
+
+ TSyError UpdateItem( cAppCharP aItemData, cAppCharP aItemID,
+ TDB_Api_Str &updItemID );
+ TSyError UpdateItem( cAppCharP aItemData, cAppCharP aItemID,
+ cAppCharP aParentID,
+ TDB_Api_ItemID &updID );
+
+ TSyError UpdateItemAsKey( KeyH aItemKey, ItemID_Struct aID,
+ TDB_Api_ItemID &updID );
+ TSyError UpdateItemAsKey( KeyH aItemKey, TDB_Api_ItemID &aID,
+ TDB_Api_ItemID &updID ); // <aID> obj will not be removed here
+
+ // 2 overloaded versions
+ TSyError MoveItem ( ItemID_Struct aID, cAppCharP newParID );
+ TSyError MoveItem ( cAppCharP aItemID,
+ cAppCharP aParentID, cAppCharP newParID );
+
+ // 3 overloaded versions
+ TSyError DeleteItem( ItemID_Struct aID );
+ TSyError DeleteItem( cAppCharP aItemID, cAppCharP parentID="" );
+ TSyError DeleteItem( TDB_Api_ItemID &aID ); // <aID> obj will not be removed here
+
+ TSyError DeleteSyncSet();
+
+
+ // <totSize>, <aFirst> and <aLast> can be omitted, their default values are 0,true,true
+ // 4 overloaded versions
+ TSyError WriteBlob ( ItemID_Struct aID, cAppCharP aBlobID,
+ appPointer blkPtr, memSize blkSize,
+ memSize totSize= 0, bool aFirst= true, bool aLast= true );
+ TSyError WriteBlob ( cAppCharP aItemID, cAppCharP aBlobID,
+ appPointer blkPtr, memSize blkSize,
+ memSize totSize= 0, bool aFirst= true, bool aLast= true );
+ TSyError WriteBlob ( cAppCharP aItemID,
+ cAppCharP aParentID, cAppCharP aBlobID,
+ appPointer blkPtr, memSize blkSize,
+ memSize totSize= 0, bool aFirst= true, bool aLast= true );
+ TSyError WriteBlob ( TDB_Api_ItemID &aID, cAppCharP aBlobID, // <aID> obj will not be removed here
+ appPointer blkPtr, memSize blkSize,
+ memSize totSize= 0, bool aFirst= true, bool aLast= true );
+
+ // 4 overloaded versions
+ TSyError DeleteBlob( ItemID_Struct aID, cAppCharP aBlobID );
+ TSyError DeleteBlob( cAppCharP aItemID, cAppCharP aBlobID );
+ TSyError DeleteBlob( cAppCharP aItemID,
+ cAppCharP aParentID, cAppCharP aBlobID );
+ TSyError DeleteBlob( TDB_Api_ItemID &aID, cAppCharP aBlobID ); // <aID> obj will not be removed here
+
+ TSyError EndDataWrite( bool success, TDB_Api_Str &newToken );
+
+
+ // --- close section --------------------------------
+ TSyError DeleteContext();
+
+
+ TDB_Api_Callback fCB; // Callback wrapper
+ CContext fContext; // local copy of the context
+
+ void DisposeStr ( TDB_Api_Str &s );
+ void DisposeBlk ( TDB_Api_Blk &b );
+
+ private:
+ void AssignStr ( TDB_Api_Str &s ); // for internal use only
+ void AssignBlk ( TDB_Api_Blk &b );
+ void AssignChanged( string &a, TDB_Api_Str &u );
+ void GetItemID ( TDB_Api_ItemID &aID, TDB_Api_Str &aItem );
+ void Assign_ItemID( TDB_Api_ItemID &newID, ItemID_Struct &aID, cAppCharP parentID );
+
+ API_Methods* dm; // connection field reference
+ API_Methods fNo_dbapi; // default connection for call methods
+
+ list<TDB_Api_Str*> fSList; // list of currently allocated str objects of this datastore
+ list<TDB_Api_Blk*> fBList; // " " " " blk " " " "
+
+ TDB_Api_Config* fConfig; // assigned module
+ bool fCreated; // true, as long <fContext> is valid (between 'CreateContext'
+ // and 'DeleteContext')
+ string fDevKey; // local copies
+ string fUsrKey;
+}; // TDB_Api
+
+
+
+} // namespace
+#endif // DB_API_H
+/* eof */
diff --git a/src/DB_interfaces/api_db/dbapi_include.h b/src/DB_interfaces/api_db/dbapi_include.h
new file mode 100755
index 0000000..d5cc0c0
--- /dev/null
+++ b/src/DB_interfaces/api_db/dbapi_include.h
@@ -0,0 +1,382 @@
+/*
+ * File: dbapi_include.h
+ *
+ * Author: Beat Forster
+ *
+ * DB_Api class
+ * Bridge to user programmable interface
+ *
+ * Copyright (c) 2004-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ */
+
+
+
+/* include definitions for each namespace */
+#undef SYNC_DBAPI_H
+#include "sync_dbapi.h"
+
+#undef SYNC_UIAPI_H
+#include "sync_uiapi.h"
+
+#if defined __cplusplus
+ using sysync::ConnectFunctions;
+ using sysync::LOCERR_OK;
+ using sysync::DB_NotFound;
+ using sysync::DB_Forbidden;
+ using sysync::DB_Error;
+ using sysync::DB_Fatal;
+ using sysync::VP_BadVersion;
+#endif
+
+
+static TSyError AssignMethods( appPointer aMod, appPointer aField, memSize aFieldSize, cAppCharP aKey= "" )
+{
+ // ---- module -----------------------------------------------------------------
+ if (strcmp( aKey,Plugin_Start )==0) {
+ return ConnectFunctions( aMod, aField,aFieldSize, false,
+ // ---- start of plugin connection
+ Module_CreateContext,
+ Module_Version,
+ Module_Capabilities,
+ NULL );
+ } // if
+
+ if (strcmp( aKey,Plugin_Param )==0 || // new AND old
+ strcmp( aKey,Plugin_Param_OLD )==0) {
+ return ConnectFunctions( aMod, aField,aFieldSize, false,
+ // ---- plugin params
+ Module_PluginParams,
+ NULL );
+ } // if
+
+
+ // ---- session ----------------------------------------------------------------
+ #if !defined DISABLE_PLUGIN_SESSIONAUTH || !defined DISABLE_PLUGIN_DEVICEADMIN
+ if (strcmp( aKey,Plugin_Session )==0) {
+ return ConnectFunctions( aMod, aField,aFieldSize, false,
+ // ---- session ----
+ Session_CreateContext,
+ XX, /*-----* adaptitem */
+
+ XX, /*-----* session auth */
+ XX, /* */
+ XX, /*-----*/
+
+ XX, /*-----* device admin */
+ XX, /* */
+ XX, /* */
+ XX, /* */
+ XX, /*-----*(dbtime) */
+
+ Session_DisposeObj,
+ Session_ThreadMayChangeNow,
+ Session_DispItems,
+ Session_DeleteContext,
+ NULL );
+ } // if
+
+ if (strcmp( aKey,Plugin_SE_Adapt )==0) {
+ return ConnectFunctions( aMod, aField,aFieldSize, false,
+ // ---- device admin (GetDBTime) ----
+ Session_AdaptItem,
+ NULL );
+ } // if
+ #endif
+
+ #ifndef DISABLE_PLUGIN_SESSIONAUTH
+ if (strcmp( aKey,Plugin_SE_Auth )==0 || // new AND old
+ strcmp( aKey,Plugin_SE_Auth_OLD )==0) {
+ return ConnectFunctions( aMod, aField,aFieldSize, false,
+ // ---- session auth ----
+ Session_PasswordMode,
+ Session_Login,
+ Session_Logout,
+ NULL );
+ } // if
+ #endif
+
+ #ifndef DISABLE_PLUGIN_DEVICEADMIN
+ if (strcmp( aKey,Plugin_DV_Admin )==0) {
+ return ConnectFunctions( aMod, aField,aFieldSize, false,
+ // ---- device admin ----
+ Session_CheckDevice,
+ Session_GetNonce,
+ Session_SaveNonce,
+ Session_SaveDeviceInfo,
+ NULL );
+ } // if
+
+ if (strcmp( aKey,Plugin_DV_DBTime )==0) {
+ return ConnectFunctions( aMod, aField,aFieldSize, false,
+ // ---- device admin (GetDBTime) ----
+ Session_GetDBTime,
+ NULL );
+ } // if
+ #endif
+
+
+ // ---- datastore --------------------------------------------------------------
+ #if !defined DISABLE_PLUGIN_DATASTOREADMIN || !defined DISABLE_PLUGIN_DATASTOREDATA
+ if (strcmp( aKey,Plugin_DS_General )==0) {
+ return ConnectFunctions( aMod, aField,aFieldSize, false,
+ // ---- general datastore routines ----
+ ContextSupport,
+ FilterSupport,
+ ThreadMayChangeNow,
+ WriteLogData,
+ DispItems,
+ NULL );
+ } // if
+ #endif
+
+ #ifndef DISABLE_PLUGIN_DATASTOREADMIN
+ if (strcmp( aKey,Plugin_DS_Admin )==0 || // new AND old
+ strcmp( aKey,Plugin_DS_Admin_OLD )==0) {
+ return ConnectFunctions( aMod, aField,aFieldSize, false,
+ // ---- datastore admin ----
+ LoadAdminData,
+ SaveAdminData,
+ ReadNextMapItem,
+ InsertMapItem,
+ UpdateMapItem,
+ DeleteMapItem,
+ NULL );
+ } // if
+ #endif
+
+ #ifndef DISABLE_PLUGIN_ADAPTITEM
+ if (strcmp( aKey,Plugin_DS_Adapt )==0) {
+ return ConnectFunctions( aMod, aField,aFieldSize, false,
+ AdaptItem,
+ NULL );
+ } // if
+ #endif
+
+ #ifndef DISABLE_PLUGIN_DATASTOREDATA
+ if (strcmp( aKey,Plugin_DS_Data )==0 || // new AND old
+ strcmp( aKey,Plugin_DS_Data_OLD1 )==0 ||
+ strcmp( aKey,Plugin_DS_Data_OLD2 )==0) {
+ return ConnectFunctions( aMod, aField,aFieldSize, false,
+ // ---- datastore data ----
+ StartDataRead, // read
+ EndDataRead,
+
+ StartDataWrite, // write
+ EndDataWrite,
+
+ /*-----*/
+ XX, /* */
+ XX, /* str */
+ XX, /* */
+ XX, /* */
+ /*-----*/
+ XX, /* */
+ XX, /* key */
+ XX, /* */
+ XX, /* */
+ /*-----*/
+
+ FinalizeLocalID, // ind
+ MoveItem,
+ DeleteItem,
+ DeleteSyncSet,
+ NULL );
+ } // if
+
+ #ifndef DISABLE_PLUGIN_DATASTOREDATA_STR
+ if (strcmp( aKey,Plugin_DS_Data_Str )==0) {
+ return ConnectFunctions( aMod, aField,aFieldSize, false,
+ // ---- aItemData routines ----
+ ReadNextItem,
+ ReadItem,
+ InsertItem,
+ UpdateItem,
+ NULL );
+ } // if
+ #endif
+
+ #ifndef DISABLE_PLUGIN_DATASTOREDATA_KEY
+ if (strcmp( aKey,Plugin_DS_Data_Key )==0) {
+ return ConnectFunctions( aMod, aField,aFieldSize, false,
+ // ---- aItemKey routines ----
+ ReadNextItemAsKey,
+ ReadItemAsKey,
+ InsertItemAsKey,
+ UpdateItemAsKey,
+ NULL );
+ } // if
+ #endif
+ #endif
+
+ #if !defined DISABLE_PLUGIN_DATASTOREADMIN || !defined DISABLE_PLUGIN_DATASTOREDATA
+ if (strcmp( aKey,Plugin_DS_Blob )==0 || // new AND old
+ strcmp( aKey,Plugin_DS_Blob_OLD )==0) {
+ return ConnectFunctions( aMod, aField,aFieldSize, false,
+ // ---- BLOBs ----
+ ReadBlob,
+ WriteBlob,
+ DeleteBlob,
+ NULL );
+ } // if
+ #endif
+
+ #if !defined DISABLE_PLUGIN_DATASTOREADMIN || !defined DISABLE_PLUGIN_DATASTOREDATA || !defined DISABLE_PLUGIN_ADAPTITEM
+ if (strcmp( aKey,Plugin_Datastore )==0) {
+ return ConnectFunctions( aMod, aField,aFieldSize, false,
+ CreateContext, // open
+
+ XX, /*+*---- general */
+ XX, /*+*/
+ XX, /*+*/
+ XX, /*+*/
+ XX, /*+*/
+ /* */
+ XX, /*-----* admin */
+ XX, /* */
+ XX, /* */
+ XX, /* */
+ XX, /* */
+ XX, /*-----*/
+ /* */
+ /*-----* data read/write */
+ XX, /* rd */
+ XX, /* */
+ /*-----*/
+ XX, /* wr */
+ XX, /* */
+ /*-----*/
+ XX, /* */
+ XX, /* str */
+ XX, /* */
+ XX, /* */
+ /*-----*/
+ XX, /* */
+ XX, /* key */
+ XX, /* */
+ XX, /* */
+ /*-----*/
+ XX, /* */
+ XX, /* ind */
+ XX, /* */
+ XX, /* */
+ /*-----*/
+ /* */
+ XX, /*-----* blobs */
+ XX, /* */
+ XX, /*-----*/
+
+ XX, /*-----* adaptitem */
+
+ DisposeObj,
+ DeleteContext, // close
+ NULL );
+ } // if
+ #endif
+
+
+ #ifdef ENABLE_PLUGIN_UI
+ // ---- ui context ------------------------------------------------
+ if (strcmp( aKey,Plugin_UI )==0) {
+ return ConnectFunctions( aMod, aField,aFieldSize, false,
+ // ---- ui ----
+ UI_CreateContext,
+ UI_RunContext,
+ UI_DeleteContext,
+ NULL );
+ } // if
+ #endif
+
+
+ // default settings
+ if (*aKey=='\0') {
+ return ConnectFunctions( aMod, aField,aFieldSize, false,
+ // ---- module ----
+ XX, /*-----* start */
+ XX, /* */
+ XX, /*-----*/
+
+ XX, /*-----* plugin params */
+
+ Module_DisposeObj,
+ Module_DeleteContext,
+
+ // ---- session ----
+ XX, /*+*/
+ /* */
+ XX, /*-*---* login */
+ XX, /* */
+ XX, /*-*---*/
+ /* */
+ XX, /*-*---* dev admin */
+ XX, /* */
+ XX, /* */
+ XX, /* */
+ XX, /*-*---*(db time) */
+ /* */
+ XX, /*-*---* adaptitem */
+ /* */
+ XX, /*+*/
+ XX, /*+*/
+ XX, /*+*/
+ XX, /*+*/
+
+ // ---- datastore ----
+ XX, /*-* open */
+
+ XX, /*+*---- general */
+ XX, /*+*/
+ XX, /*+*/
+ XX, /*+*/
+ XX, /*+*/
+ /* */
+ XX, /*-----* admin */
+ XX, /* */
+ XX, /* */
+ XX, /* */
+ XX, /* */
+ XX, /*-----*/
+ /* */
+ /*-----* data read/write */
+ XX, /* rd */
+ XX, /* */
+ /*-----*/
+ XX, /* wr */
+ XX, /* */
+ /*-----*/
+ XX, /* */
+ XX, /* str */
+ XX, /* */
+ XX, /* */
+ /*-----*/
+ XX, /* */
+ XX, /* key */
+ XX, /* */
+ XX, /* */
+ /*-----*/
+ XX, /* */
+ XX, /* ind */
+ XX, /* */
+ XX, /* */
+ /*-----*/
+ /* */
+ XX, /*-----* blobs */
+ XX, /* */
+ XX, /*-----*/
+
+ XX, /*-----* "script-like" adapt */
+
+ XX, /*-* DisposeObj */
+ XX, /*-* close */
+
+ // ---- ui context ----
+ XX, /*-* open */
+ XX, /*-* run */
+ XX, /*-* close */
+ NULL );
+ } // if
+
+ return DB_NotFound;
+} // AssignMethods
+
+/* eof */
diff --git a/src/DB_interfaces/api_db/pluginapiagent.cpp b/src/DB_interfaces/api_db/pluginapiagent.cpp
new file mode 100755
index 0000000..c964b29
--- /dev/null
+++ b/src/DB_interfaces/api_db/pluginapiagent.cpp
@@ -0,0 +1,709 @@
+/**
+ * @File pluginapiagent.cpp
+ *
+ * @Author Lukas Zeller (luz@synthesis.ch)
+ *
+ * @brief TPluginApiAgent
+ * Plugin based agent (client or server session) API implementation
+ *
+ * Copyright (c) 2001-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ * @Date 2005-10-06 : luz : created from apidbagent
+ */
+
+// includes
+#include "pluginapiagent.h"
+#include "pluginapids.h"
+
+#ifdef SYSER_REGISTRATION
+#include "syserial.h"
+#endif
+
+#ifdef ENGINEINTERFACE_SUPPORT
+#include "engineentry.h"
+#endif
+
+namespace sysync {
+
+
+// Callback adaptor functions for session level logging
+// Note: appbase level logging functions are now in syncappbase.cpp as these are
+// needed even without DB plugins
+
+
+#ifdef SYDEBUG
+
+extern "C" void SessionLogDebugPuts(void *aCallbackRef, const char *aText)
+{
+ if (aCallbackRef) {
+ POBJDEBUGPUTSX(static_cast<TSyncSession *>(aCallbackRef),DBG_DBAPI+DBG_PLUGIN,aText);
+ }
+} // SessionLogDebugPuts
+
+
+extern "C" void SessionLogDebugExotic(void *aCallbackRef, const char *aText)
+{
+ if (aCallbackRef) {
+ POBJDEBUGPUTSX(static_cast<TSyncSession *>(aCallbackRef),DBG_DBAPI+DBG_PLUGIN+DBG_EXOTIC,aText);
+ }
+} // SessionLogDebugExotic
+
+
+extern "C" void SessionLogDebugBlock(void *aCallbackRef, const char *aTag, const char *aDesc, const char *aAttrText )
+{
+ if (aCallbackRef) {
+ bool collapsed=false;
+ if (aTag && aTag[0]=='-') { aTag++; collapsed=true; }
+ static_cast<TSyncSession *>(aCallbackRef)->getDbgLogger()->DebugOpenBlock(aTag,aDesc,collapsed,"%s",aAttrText);
+ }
+} // SessionLogDebugBlock
+
+
+extern "C" void SessionLogDebugEndBlock(void *aCallbackRef, const char *aTag)
+{
+ if (aCallbackRef) {
+ if (aTag && aTag[0]=='-') aTag++;
+ static_cast<TSyncSession *>(aCallbackRef)->getDbgLogger()->DebugCloseBlock(aTag);
+ }
+} // SessionLogDebugEndBlock
+
+
+extern "C" void SessionLogDebugEndThread(void *aCallbackRef)
+{
+ if (aCallbackRef) {
+ static_cast<TSyncSession *>(aCallbackRef)->getDbgLogger()->DebugThreadOutputDone(false); // leave session thread record live until session dies
+ }
+} // SessionLogDebugEndThread
+
+#endif
+
+
+#ifdef ENGINEINTERFACE_SUPPORT
+
+extern "C" TSyError SessionOpenSessionKey(void* aCB, SessionH aSessionH, KeyH *aKeyH, uInt16 aMode)
+{
+ // Note: aSessionH must be NULL, as we are implicitly in a session context and cannot specify the session
+ if (!aCB || !aKeyH || aSessionH!=NULL) return LOCERR_BADPARAM;
+ DB_Callback cb = static_cast<DB_Callback>(aCB);
+ if (!cb->callbackRef) return LOCERR_BADPARAM;
+ // create settings key for the session
+ TSyncSession *sessionP = static_cast<TSyncSession *>(cb->callbackRef);
+ *aKeyH = static_cast<KeyH>(sessionP->newSessionKey(sessionP->getSyncAppBase()->fEngineInterfaceP));
+ // done
+ return LOCERR_OK;
+} // SessionOpenSessionKey
+
+#endif // ENGINEINTERFACE_SUPPORT
+
+
+
+// TApiParamConfig
+// ===============
+
+TApiParamConfig::TApiParamConfig(TConfigElement *aParentElement) :
+ TConfigElement("plugin_config",aParentElement)
+{
+ // nop so far
+} // TApiParamConfig::TPluginAgentConfig
+
+
+TApiParamConfig::~TApiParamConfig()
+{
+ clear();
+} // TApiParamConfig::~TApiParamConfig
+
+
+
+// init defaults
+void TApiParamConfig::clear(void)
+{
+ // init defaults
+ fConfigString.erase();
+ fLastTagName.erase();
+ fLastTagValue.erase();
+ // clear inherited
+ inherited::clear();
+} // TApiParamConfig::clear
+
+
+// store last tag's value
+void TApiParamConfig::storeLastTag(void)
+{
+ // init defaults
+ if (!fLastTagName.empty()) {
+ fConfigString.append(fLastTagName);
+ fConfigString+=':';
+ StrToCStrAppend(fLastTagValue.c_str(),fConfigString,true); // allow 8-bit chars to be represented as-is (no \xXX escape needed)
+ fConfigString+="\r\n"; // CRLF at end
+ }
+ // done, erase them
+ fLastTagName.erase();
+ fLastTagValue.erase();
+} // TApiParamConfig::storeLastTag
+
+
+
+// config element parsing
+bool TApiParamConfig::localStartElement(const char *aElementName, const char **aAttributes, sInt32 aLine)
+{
+ // all elements will be passed 1:1 into DBAPI
+ // - make sure previous element is stored
+ storeLastTag();
+ // - remember name of this tag
+ fLastTagName=aElementName;
+ // - contents is always a string
+ expectString(fLastTagValue);
+ // ok
+ return true;
+} // TApiParamConfig::localStartElement
+
+
+// resolve
+void TApiParamConfig::localResolve(bool aLastPass)
+{
+ // make sure previous element is stored, if any
+ storeLastTag();
+ // resolve inherited
+ inherited::localResolve(aLastPass);
+} // TApiParamConfig::localResolve
+
+
+// TPluginAgentConfig
+// ==================
+
+TPluginAgentConfig::TPluginAgentConfig(TConfigElement *aParentElement) :
+ inherited(aParentElement),
+ fPluginParams(this)
+{
+ // nop so far
+} // TPluginAgentConfig::TPluginAgentConfig
+
+
+TPluginAgentConfig::~TPluginAgentConfig()
+{
+ clear();
+ // - disconnect from the API module
+ fDBApiConfig.Disconnect();
+} // TPluginAgentConfig::~TPluginAgentConfig
+
+
+// init defaults
+void TPluginAgentConfig::clear(void)
+{
+ // init defaults
+ // - API Module
+ fLoginAPIModule.erase();
+ // - use api session auth or not?
+ fApiSessionAuth=false;
+ // - use api device admin or not?
+ fApiDeviceAdmin=false;
+ // - default to use all debug flags set (if debug for plugin is enabled at all)
+ fPluginDbgMask=0xFFFF;
+ // - clear plugin params
+ fPluginParams.clear();
+ // clear inherited
+ inherited::clear();
+} // TPluginAgentConfig::clear
+
+
+// config element parsing
+bool TPluginAgentConfig::localStartElement(const char *aElementName, const char **aAttributes, sInt32 aLine)
+{
+ // checking the elements
+ // - API connection
+ if (strucmp(aElementName,"plugin_module")==0)
+ expectMacroString(fLoginAPIModule);
+ else if (strucmp(aElementName,"plugin_sessionauth")==0)
+ expectBool(fApiSessionAuth);
+ else if (strucmp(aElementName,"plugin_deviceadmin")==0)
+ expectBool(fApiDeviceAdmin);
+ else if (strucmp(aElementName,"plugin_params")==0)
+ expectChildParsing(fPluginParams);
+ else if (strucmp(aElementName,"plugin_debugflags")==0)
+ expectUInt16(fPluginDbgMask);
+ else
+ return inherited::localStartElement(aElementName,aAttributes,aLine);
+ // ok
+ return true;
+} // TPluginAgentConfig::localStartElement
+
+
+// resolve
+void TPluginAgentConfig::localResolve(bool aLastPass)
+{
+ // resolve plugin specific config leaf
+ fPluginParams.Resolve(aLastPass);
+ if (aLastPass) {
+ if (!fLoginAPIModule.empty()) {
+ // Determine if we may use non-built-in plugins
+ bool allowDLL= true; // by default, it is allowed, but if PLUGIN_DLL is not set, it will be disabled anyway.
+ #if defined(SYSER_REGISTRATION) && !defined(DLL_PLUGINS_ALWAYS_ALLOWED)
+ // license flags present and DLL plugins not generally allowed:
+ // -> license decides if DLL is allowed
+ allowDLL= (getSyncAppBase()->fRegProductFlags & SYSER_PRODFLAG_SERVER_SDKAPI)!=0;
+ // warn about DLL not allowed ONLY if this build actually supports DLL plugins
+ #if defined(PLUGIN_DLL)
+ if (!allowDLL) {
+ SYSYNC_THROW(TConfigParseException("License does not allow using <datastore type=\"plugin\">"));
+ }
+ #endif // DLL support available in the code at all
+ #endif // DLL plugins not generally allowed (or DLL support not compiled in)
+ // Module level debug goes to appbase (global) log
+ DB_Callback cb= &fDBApiConfig.fCB.Callback;
+ cb->callbackRef = getSyncAppBase();
+ #ifdef ENGINEINTERFACE_SUPPORT
+ cb->thisBase = getSyncAppBase()->fEngineInterfaceP;
+ #endif
+ #ifdef SYDEBUG
+ cb->debugFlags= (getSyncAppBase()->getRootConfig()->fDebugConfig.fGlobalDebugLogs) &&
+ PDEBUGTEST(DBG_ADMIN+DBG_DBAPI+DBG_PLUGIN) ? fPluginDbgMask : 0;
+ cb->DB_DebugPuts = AppBaseLogDebugPuts;
+ cb->DB_DebugBlock = AppBaseLogDebugBlock;
+ cb->DB_DebugEndBlock = AppBaseLogDebugEndBlock;
+ cb->DB_DebugEndThread = AppBaseLogDebugEndThread;
+ cb->DB_DebugExotic = AppBaseLogDebugExotic;
+ #endif
+ // check for required settings
+ if (fDBApiConfig.Connect(fLoginAPIModule.c_str(), getSyncAppBase()->fApiInterModuleContext, "", false, allowDLL)!=LOCERR_OK)
+ SYSYNC_THROW(TConfigParseException("Cannot connect to session handler module specified in <plugin_module>"));
+ // now pass plugin-specific config
+ if (fDBApiConfig.PluginParams(fPluginParams.fConfigString.c_str())!=LOCERR_OK)
+ SYSYNC_THROW(TConfigParseException("Module does not understand params passed in <plugin_params>"));
+ }
+ }
+ // resolve inherited
+ inherited::localResolve(aLastPass);
+} // TPluginAgentConfig::localResolve
+
+
+
+// TPluginApiAgent
+// ===============
+
+
+#ifdef SYSYNC_CLIENT
+TPluginApiAgent::TPluginApiAgent(TSyncClientBase *aSyncClientBaseP, const char *aSessionID) :
+ inherited(aSyncClientBaseP, aSessionID),
+#else
+TPluginApiAgent::TPluginApiAgent(TSyncAppBase *aAppBaseP, TSyncSessionHandle *aSessionHandleP, const char *aSessionID) :
+ inherited(aAppBaseP, aSessionHandleP, aSessionID),
+#endif
+ fPluginAgentConfigP(NULL),
+ fApiLocked(false)
+{
+ // get config for agent and save direct link to agent config for easy reference
+ fPluginAgentConfigP = static_cast<TPluginAgentConfig *>(getRootConfig()->fAgentConfigP);
+ // Note: Datastores are already created from config
+ if (fPluginAgentConfigP->fDBApiConfig.Connected()) {
+ TSyError dberr;
+ DB_Callback cb= &fDBApiSession.fCB.Callback;
+ cb->callbackRef = this; // the session
+ #ifdef ENGINEINTERFACE_SUPPORT
+ cb->thisBase = getSyncAppBase()->fEngineInterfaceP;
+ #endif
+ #ifdef SYDEBUG
+ // Agent Context level debug goes to session log
+ cb->debugFlags = getDbgMask() ? 0xFFFF : 0;
+ cb->DB_DebugPuts = SessionLogDebugPuts;
+ cb->DB_DebugBlock = SessionLogDebugBlock;
+ cb->DB_DebugEndBlock = SessionLogDebugEndBlock;
+ cb->DB_DebugEndThread= SessionLogDebugEndThread;
+ cb->DB_DebugExotic = SessionLogDebugExotic;
+ #endif // SYDEBUG
+ #ifdef ENGINEINTERFACE_SUPPORT
+ // Data module can use Get/SetValue for "AsKey" routines and for session script var access
+ // Note: these are essentially context free and work without a global call-in structure
+ // (which is not necessarily there, for example in no-library case)
+ CB_Connect_KeyAccess(cb); // connect generic key access routines
+ // Version of OpenSessionKey that implicitly opens a key for the current session (DB plugins
+ // do not have a session handle, as their use is always implicitly in a session context).
+ cb->ui.OpenSessionKey = SessionOpenSessionKey;
+ #endif // ENGINEINTERFACE_SUPPORT
+ // Now create the context for running this session
+ // - use session ID as session name
+ dberr=fDBApiSession.CreateContext(getLocalSessionID(),fPluginAgentConfigP->fDBApiConfig);
+ if (dberr!=LOCERR_OK)
+ SYSYNC_THROW(TSyncException("Error creating context for API session module",dberr));
+ }
+} // TPluginApiAgent::TPluginApiAgent
+
+
+TPluginApiAgent::~TPluginApiAgent()
+{
+ // make sure everything is terminated BEFORE destruction of hierarchy begins
+ TerminateSession();
+} // TPluginApiAgent::~TPluginApiAgent
+
+
+// Terminate session
+void TPluginApiAgent::TerminateSession()
+{
+ if (!fTerminated) {
+ // Make sure datastores know that the agent will go down soon (and give them opportunity to disconnect contexts)
+ announceDestruction();
+ // Note that the following will happen BEFORE destruction of
+ // individual datastores, so make sure datastore have their inherited stuff (e.g. ODBC)
+ // stuff finished before disposing environment
+ InternalResetSession();
+ // now we can destroy the session level API context (no connected DB context exist any more)
+ fDBApiSession.DeleteContext();
+ }
+ inherited::TerminateSession();
+} // TPluginApiAgent::TerminateSession
+
+
+
+// Reset session
+void TPluginApiAgent::InternalResetSession(void)
+{
+ // reset all datastores now to make sure all Plugin access is over (including log writing)
+ // before the Plugin Agent is destroyed.
+ // (Note: TerminateDatastores() will be called again by ancestors)
+ TerminateDatastores();
+ // logout api
+ LogoutApi();
+} // TPluginApiAgent::InternalResetSession
+
+
+// Virtual version
+void TPluginApiAgent::ResetSession(void)
+{
+ // do my own stuff
+ InternalResetSession();
+ // let ancestor do its stuff
+ TStdLogicAgent::ResetSession();
+} // TPluginApiAgent::ResetSession
+
+
+// get time of DB server
+lineartime_t TPluginApiAgent::getDatabaseNowAs(timecontext_t aTimecontext)
+{
+ #ifndef SDK_ONLY_SUPPORT
+ // only handle here if we are in charge - otherwise let ancestor handle it
+ if (!fPluginAgentConfigP->fApiDeviceAdmin) return inherited::getDatabaseNowAs(aTimecontext);
+ #endif
+
+ lineartime_t dbtime;
+
+ #if defined RELEASE_VERSION && !defined _MSC_VER
+ //#warning "GZones should not be passed NULL here"
+ #endif
+ if (fDBApiSession.GetDBTime( dbtime, NULL )!=LOCERR_OK) {
+ dbtime=getSystemNowAs(TCTX_UTC); // just use time of this machine
+ }
+ // return it
+ return dbtime;
+} // TPluginApiAgent::getDatabaseNowAs
+
+
+
+#ifndef SYSYNC_CLIENT
+
+// info about requested auth type
+TAuthTypes TPluginApiAgent::requestedAuthType(void)
+{
+ // if not checking via api, let inherited decide
+ if (!fPluginAgentConfigP->fApiSessionAuth) return inherited::requestedAuthType();
+ // get default
+ TAuthTypes auth = TSyncServer::requestedAuthType();
+ // depending on possibilities of used module, we need to enforce basic
+ int pwmode=fDBApiSession.PasswordMode();
+ if (pwmode==Password_ClrText_IN || (pwmode==Password_MD5_OUT && getSyncMLVersion()<syncml_vers_1_1)) {
+ // we need clear text password to login, so we can only do basic auth
+ auth=auth_basic; // force basic auth, this can be checked
+ }
+ // return
+ return auth;
+} // TPluginApiAgent::requestedAuthType
+
+
+// - get nonce string, which is expected to be used by remote party for MD5 auth.
+void TPluginApiAgent::getAuthNonce(const char *aDeviceID, string &aAuthNonce)
+{
+ if (!fPluginAgentConfigP->fApiDeviceAdmin) {
+ // no device ID or no persistent nonce, use method of ancestor
+ inherited::getAuthNonce(aDeviceID,aAuthNonce);
+ }
+ else {
+ // we have a persistent nonce
+ DEBUGPRINTFX(DBG_ADMIN,("getAuthNonce: current auth nonce='%s'",fLastNonce.c_str()));
+ aAuthNonce=fLastNonce.c_str();
+ }
+} // TPluginApiAgent::getAuthNonce
+
+
+// get next nonce (to be sent to remote party for next auth of that remote party with us)
+// Must generate and persistently save a new nonce string which will be sent in
+// this session to the remote, and which will be used in the next session for
+// authenticating the remote device.
+void TPluginApiAgent::getNextNonce(const char *aDeviceID, string &aNextNonce)
+{
+ if (!fPluginAgentConfigP->fApiDeviceAdmin) {
+ // no persistent nonce, use method of ancestor
+ inherited::getNextNonce(aDeviceID,aNextNonce);
+ }
+ else {
+ // plugin is responsible for storing a persistent nonce per device
+ // - check if plugin wants to generate the nonce itself
+ TDB_Api_Str apiNonce;
+ if (fDBApiSession.GetNonce(apiNonce)==LOCERR_OK) {
+ // plugin has generated a nonce, use it
+ aNextNonce = apiNonce.c_str();
+ }
+ else {
+ // plugin does not generate nonce itself, use our own generator
+ // - create new one (pure 7bit ASCII)
+ generateNonce(aNextNonce,aDeviceID,getSystemNowAs(TCTX_UTC)+rand());
+ }
+ // - save it such that next CheckDevice() can retrieve it as fLastNonce (see below)
+ TSyError sta=fDBApiSession.SaveNonce(aNextNonce.c_str());
+ if (sta!=LOCERR_OK) {
+ PDEBUGPRINTFX(DBG_ERROR,("getNextNonce: SaveNonce failed with error = %hd",sta));
+ }
+ }
+} // TPluginApiAgent::getNextNonce
+
+#endif // SYSYNC_CLIENT
+
+
+#ifndef BASED_ON_BINFILE_CLIENT
+
+
+// check device related stuff
+// - Must retrieve device record for device identified by aDeviceID.
+// - If no such record exists, create a new one and set:
+// - fLastNonce to empty string
+// - If the record exists, the following fields must be retrieved from the DB:
+// - fLastNonce : opaque string used for MD5 auth
+// - In any case, the routine should set:
+// - fDeviceKey : identifier that can be used in other calls (like LoadAdminData/SaveAdminData
+// or dsLogSyncResult) to refer to this device
+void TPluginApiAgent::CheckDevice(const char *aDeviceID)
+{
+ // do it here only if we have device info stuff implemented in ApiDB
+ if (!fPluginAgentConfigP->fApiDeviceAdmin) {
+ // let inherited process it
+ inherited::CheckDevice(aDeviceID);
+ }
+ else {
+ // Plugin will do the device checking and has stored the nonce generated in the previous session
+ TDB_Api_Str lastNonce;
+ TDB_Api_Str deviceKey;
+ TSyError sta = fDBApiSession.CheckDevice(aDeviceID,deviceKey,lastNonce);
+ if (sta!=LOCERR_OK)
+ SYSYNC_THROW(TSyncException("TPluginApiAgent: checkdevice failed",sta));
+ #ifndef SYSYNC_CLIENT
+ // save last nonce
+ fLastNonce = lastNonce.c_str();
+ #endif
+ // device "key" is set to deviceID. Plugin internally maintains its key needed for storing Nonce or DevInf
+ fDeviceKey = deviceKey.c_str();
+ } // if device table implemented in ApiDB
+} // TPluginApiAgent::CheckDevice
+
+
+// Device is no known as detailed as possible (i.e. we have the device information, if it was possible to get it)
+// The implementation can now save some of this information for logging and tracking purposes:
+// - getRemoteURI() : string, remote Device URI (usually IMEI or other globally unique ID)
+// - getRemoteDescName() : string, remote Device's descriptive name (constructed from DevInf <man> and <mod>, or
+// as set by <remoterule>'s <descriptivename>.
+// - getRemoteInfoString() : string, Remote Device Version Info ("Type (HWV, FWV, SWV) Oem")
+// - fUserKey : string, identifies user
+// - fDeviceKey : string, identifies device
+// - fDomainName : string, identifies domain (aka clientID, aka enterpriseID)
+// - fRemoteDevInf_mod : string, model name from remote devinf
+// - fRemoteDevInf_man : string, manufacturer name from remote devinf
+// - fRemoteDevInf_oem : string, OEM name from remote devinf
+// - fRemoteDevInf_fwv : string, firmware version from remote devinf
+// - fRemoteDevInf_swv : string, software version from remote devinf
+// - fRemoteDevInf_hwv : string, hardware version from remote devinf
+// fDeviceKey is normally used to re-identify the record in the database that was found or created at CheckDevice.
+void TPluginApiAgent::remoteAnalyzed(void)
+{
+ // save basic device info used for associating targets and logentries with a device
+ if (fPluginAgentConfigP->fApiDeviceAdmin) {
+ // save device info stuff
+ string devInf,val;
+ devInf.erase();
+ // save as properties
+ // - internals
+ devInf += "REMOTE_URI:";
+ storeUTF8ToString(getRemoteURI(),val,fConfigP->fDataCharSet,fConfigP->fDataLineEndMode);
+ StrToCStrAppend(val.c_str(),devInf,true); // allow 8-bit chars to be represented as-is (no \xXX escape needed)
+ devInf += "\r\nREMOTE_DESC:";
+ storeUTF8ToString(getRemoteDescName(),val,fConfigP->fDataCharSet,fConfigP->fDataLineEndMode);
+ StrToCStrAppend(val.c_str(),devInf,true); // allow 8-bit chars to be represented as-is (no \xXX escape needed)
+ devInf += "\r\nREMOTE_INFO:";
+ storeUTF8ToString(getRemoteInfoString(),val,fConfigP->fDataCharSet,fConfigP->fDataLineEndMode);
+ StrToCStrAppend(val.c_str(),devInf,true); // allow 8-bit chars to be represented as-is (no \xXX escape needed)
+ #ifdef SCRIPT_SUPPORT
+ devInf += "\r\nDOMAIN:";
+ storeUTF8ToString(fDomainName.c_str(),val,fConfigP->fDataCharSet,fConfigP->fDataLineEndMode);
+ StrToCStrAppend(val.c_str(),devInf,true); // allow 8-bit chars to be represented as-is (no \xXX escape needed)
+ #endif
+ // - strictly from DevInf
+ devInf += "\r\nMOD:";
+ storeUTF8ToString(fRemoteDevInf_mod.c_str(),val,fConfigP->fDataCharSet,fConfigP->fDataLineEndMode);
+ StrToCStrAppend(val.c_str(),devInf,true); // allow 8-bit chars to be represented as-is (no \xXX escape needed)
+ devInf += "\r\nMAN:";
+ storeUTF8ToString(fRemoteDevInf_man.c_str(),val,fConfigP->fDataCharSet,fConfigP->fDataLineEndMode);
+ StrToCStrAppend(val.c_str(),devInf,true); // allow 8-bit chars to be represented as-is (no \xXX escape needed)
+ devInf += "\r\nOEM:";
+ storeUTF8ToString(fRemoteDevInf_oem.c_str(),val,fConfigP->fDataCharSet,fConfigP->fDataLineEndMode);
+ StrToCStrAppend(val.c_str(),devInf,true); // allow 8-bit chars to be represented as-is (no \xXX escape needed)
+ devInf += "\r\nFWV:";
+ storeUTF8ToString(fRemoteDevInf_fwv.c_str(),val,fConfigP->fDataCharSet,fConfigP->fDataLineEndMode);
+ StrToCStrAppend(val.c_str(),devInf,true); // allow 8-bit chars to be represented as-is (no \xXX escape needed)
+ devInf += "\r\nSWV:";
+ storeUTF8ToString(fRemoteDevInf_swv.c_str(),val,fConfigP->fDataCharSet,fConfigP->fDataLineEndMode);
+ StrToCStrAppend(val.c_str(),devInf,true); // allow 8-bit chars to be represented as-is (no \xXX escape needed)
+ devInf += "\r\nHWV:";
+ storeUTF8ToString(fRemoteDevInf_hwv.c_str(),val,fConfigP->fDataCharSet,fConfigP->fDataLineEndMode);
+ StrToCStrAppend(val.c_str(),devInf,true); // allow 8-bit chars to be represented as-is (no \xXX escape needed)
+ devInf += "\r\n"; // last line end
+ // now store
+ TSyError sta = fDBApiSession.SaveDeviceInfo(devInf.c_str());
+ if (sta!=LOCERR_OK)
+ SYSYNC_THROW(TSyncException("TPluginApiAgent: SaveDeviceInfo failed",sta));
+ } // if device table implemented in ApiDB
+ else {
+ // let inherited process it
+ inherited::remoteAnalyzed();
+ }
+} // TPluginApiAgent::remoteAnalyzed
+
+
+
+// check login information
+bool TPluginApiAgent::CheckLogin(const char *aOriginalUserName, const char *aModifiedUserName, const char *aAuthString, TAuthSecretTypes aAuthStringType, const char *aDeviceID)
+{
+ bool authok=false;
+ string nonce;
+
+ // - get nonce (if we have a device table, we should have read it by now)
+ if (aAuthStringType==sectyp_md5_V10 || aAuthStringType==sectyp_md5_V11)
+ getAuthNonce(aDeviceID,nonce);
+
+ if (!fPluginAgentConfigP->fDBApiConfig.Connected() || !fPluginAgentConfigP->fApiSessionAuth) {
+ // let inherited handle it (if direct ancestor is TCustomImplAgent, we'll fail here)
+ return inherited::CheckLogin(aOriginalUserName, aModifiedUserName, aAuthString, aAuthStringType, aDeviceID);
+ }
+ // api auth required
+ TDB_Api_Str userKey;
+ int pwmode = fDBApiSession.PasswordMode();
+ // check if we can auth at all
+ if (pwmode == Password_ClrText_IN) {
+ // we can auth only if we have a cleartext password from the remote
+ if (aAuthStringType!=sectyp_clearpass) return false; // auth not possible
+ // login with clear text password
+ authok = fDBApiSession.Login(aModifiedUserName,aAuthString,userKey)==LOCERR_OK;
+ }
+ else if (pwmode == Password_MD5_Nonce_IN) {
+ if (aAuthStringType==sectyp_clearpass) return false; // auth not possible
+ // login with MD5( MD5( user:pwd ):nonce )
+ authok = fDBApiSession.Login(aModifiedUserName,aAuthString,userKey)==LOCERR_OK;
+ }
+ else {
+ TDB_Api_Str dbSecret;
+ if (pwmode == Password_MD5_OUT) {
+ // if the database has MD5, we can auth basic and SyncML 1.1 MD5, but not SyncML 1.0 MD5
+ if (aAuthStringType==sectyp_md5_V10) return false; // auth with SyncML 1.0 MD5 not possible
+ }
+ // get Secret and preliminary authok (w/o password check yet) from database
+ authok= fDBApiSession.Login(aModifiedUserName,dbSecret,userKey)==LOCERR_OK;
+ if (authok) {
+ // check secret
+ if (pwmode == Password_ClrText_OUT) {
+ // we have the clear text password, check against what was transmitted from remote
+ authok=checkAuthPlain(aOriginalUserName,dbSecret.c_str(),nonce.c_str(),aAuthString,aAuthStringType);
+ }
+ else {
+ // whe have the MD5 of user:password, check against what was transmitted from remote
+ // Note: this can't work with non-V1.1 type creds
+ authok=checkAuthMD5(aOriginalUserName,dbSecret.c_str(),nonce.c_str(),aAuthString,aAuthStringType);
+ }
+ }
+ }
+ if (!authok) {
+ // failed
+ PDEBUGPRINTFX(DBG_ADMIN,(
+ "==== api authentication for '%s' failed",
+ aModifiedUserName
+ ));
+ return false;
+ }
+ else {
+ // save user key as returned from dbapi login()
+ fUserKey = userKey.c_str();
+ // authenticated successfully, sessionID is set
+ PDEBUGPRINTFX(DBG_ADMIN,(
+ "==== api authentication for '%s' (userkey='%s') successful",
+ aModifiedUserName,
+ fUserKey.c_str()
+ ));
+ return true;
+ }
+} // TPluginApiAgent::CheckLogin
+
+#endif // not BASED_ON_BINFILE_CLIENT
+
+
+// - logout api
+void TPluginApiAgent::LogoutApi(void)
+{
+ if (fPluginAgentConfigP->fDBApiConfig.Connected() && fPluginAgentConfigP->fApiSessionAuth) {
+ // log out from session
+ fDBApiSession.Logout();
+ }
+} // TPluginApiAgent::LogoutApi
+
+
+
+
+
+#ifndef SYSYNC_CLIENT
+
+void TPluginApiAgent::RequestEnded(bool &aHasData)
+{
+ // first let ancestors finish their stuff
+ // - this will include calling RequestEnded() in all datastores
+ inherited::RequestEnded(aHasData);
+
+ // let my API know as well, thread might change for next request (but not necessarily does!)
+ if (fPluginAgentConfigP->fDBApiConfig.Connected()) {
+ if (!fApiLocked)
+ fDBApiSession.ThreadMayChangeNow();
+ }
+
+} // TPluginApiAgent::RequestEnded
+
+#endif
+
+
+// factory methods of Agent config
+// ===============================
+
+#ifdef SYSYNC_CLIENT
+
+TSyncClient *TPluginAgentConfig::CreateClientSession(const char *aSessionID)
+{
+ // return appropriate client session
+ MP_RETURN_NEW(TPluginApiAgent,DBG_HOT,"TPluginApiAgent",TPluginApiAgent(static_cast<TSyncClientBase *>(getSyncAppBase()),aSessionID));
+} // TPluginAgentConfig::CreateClientSession
+
+
+#else
+
+TSyncServer *TPluginAgentConfig::CreateServerSession(TSyncSessionHandle *aSessionHandle, const char *aSessionID)
+{
+ // return XML2GO or ODBC-server session
+ MP_RETURN_NEW(TPluginApiAgent,DBG_HOT,"TPluginApiAgent",TPluginApiAgent(getSyncAppBase(),aSessionHandle,aSessionID));
+} // TPluginAgentConfig::CreateServerSession
+
+#endif
+
+} // namespace sysync
+
+/* end of TPluginApiAgent implementation */
+
+// eof
diff --git a/src/DB_interfaces/api_db/pluginapiagent.h b/src/DB_interfaces/api_db/pluginapiagent.h
new file mode 100755
index 0000000..6e4cd4c
--- /dev/null
+++ b/src/DB_interfaces/api_db/pluginapiagent.h
@@ -0,0 +1,183 @@
+/**
+ * @File pluginapiagent.h
+ *
+ * @Author Lukas Zeller (luz@synthesis.ch)
+ *
+ * @brief TPluginApiAgent
+ * Plugin based agent (client or server session) API implementation
+ *
+ * Copyright (c) 2001-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ * @Date 2005-10-06 : luz : created from apidbagent
+ */
+
+#ifndef PLUGINAPIAGENT_H
+#define PLUGINAPIAGENT_H
+
+// includes
+
+#ifdef SQL_SUPPORT
+ #include "odbcapiagent.h"
+#else
+ #include "customimplagent.h"
+#endif
+
+namespace sysync {
+
+// plugin module generic config
+class TApiParamConfig: public TConfigElement
+{
+ typedef TConfigElement inherited;
+public:
+ TApiParamConfig(TConfigElement *aParentElement);
+ virtual ~TApiParamConfig();
+ // Assembled tag:value type config lines
+ string fConfigString;
+ virtual void clear();
+protected:
+ // check config elements
+ virtual bool localStartElement(const char *aElementName, const char **aAttributes, sInt32 aLine);
+ virtual void localResolve(bool aLastPass);
+private:
+ void storeLastTag(void);
+ string fLastTagName;
+ string fLastTagValue;
+}; // TApiParamConfig
+
+} // namespace sysync
+
+#include "pluginapids.h"
+
+using namespace sysync;
+
+namespace sysync {
+
+
+// Session level callback adaptor functions
+#ifdef SYDEBUG
+extern "C" void SessionLogDebugPuts(void *aCallbackRef, const char *aText);
+extern "C" void SessionLogDebugExotic(void *aCallbackRef, const char *aText);
+extern "C" void SessionLogDebugBlock(void *aCallbackRef, const char *aTag, const char *aDesc, const char *aAttrText );
+extern "C" void SessionLogDebugEndBlock(void *aCallbackRef, const char *aTag);
+extern "C" void SessionLogDebugEndThread(void *aCallbackRef);
+#endif // SYDEBUG
+#ifdef ENGINEINTERFACE_SUPPORT
+extern "C" TSyError SessionOpenSessionKey(void* aCB, SessionH aSessionH, KeyH *aKeyH, uInt16 aMode);
+#endif // ENGINEINTERFACE_SUPPORT
+
+
+// forward
+class TPluginDSConfig;
+#ifdef SCRIPT_SUPPORT
+class TScriptContext;
+#endif
+
+
+// config
+class TPluginAgentConfig :
+ #ifdef SQL_SUPPORT
+ public TOdbcAgentConfig
+ #else
+ public TCustomAgentConfig
+ #endif
+{
+ #ifdef SQL_SUPPORT
+ typedef TOdbcAgentConfig inherited;
+ #else
+ typedef TCustomAgentConfig inherited;
+ #endif
+public:
+ TPluginAgentConfig(TConfigElement *aParentElement);
+ virtual ~TPluginAgentConfig();
+ // properties
+ // - name of the login module
+ string fLoginAPIModule;
+ // - use Api session auth or not?
+ bool fApiSessionAuth;
+ // - use api device admin or not?
+ bool fApiDeviceAdmin;
+ // - the DB API config object
+ TDB_Api_Config fDBApiConfig;
+ // - generic module params
+ TApiParamConfig fPluginParams;
+ // - debug mask
+ uInt16 fPluginDbgMask;
+protected:
+ // check config elements
+ virtual bool localStartElement(const char *aElementName, const char **aAttributes, sInt32 aLine);
+ virtual void clear();
+ virtual void localResolve(bool aLastPass);
+public:
+ #ifdef SYSYNC_CLIENT
+ // create appropriate session (=agent) for this client
+ virtual TSyncClient *CreateClientSession(const char *aSessionID);
+ #else
+ // create appropriate session (=agent) for this server
+ virtual TSyncServer *CreateServerSession(TSyncSessionHandle *aSessionHandle, const char *aSessionID);
+ #endif
+}; // TPluginAgentConfig
+
+
+class TPluginApiAgent :
+ #ifdef SQL_SUPPORT
+ public TODBCApiAgent
+ #else
+ public TCustomImplAgent
+ #endif
+{
+ #ifdef SQL_SUPPORT
+ typedef TODBCApiAgent inherited;
+ #else
+ typedef TCustomImplAgent inherited;
+ #endif
+public:
+ #ifdef SYSYNC_CLIENT
+ TPluginApiAgent(TSyncClientBase *aSyncClientBaseP, const char *aSessionID);
+ #else
+ TPluginApiAgent(TSyncAppBase *aAppBaseP, TSyncSessionHandle *aSessionHandleP, const char *aSessionID);
+ #endif
+ virtual ~TPluginApiAgent();
+ virtual void TerminateSession(void); // Terminate session, like destructor, but without actually destructing object itself
+ virtual void ResetSession(void); // Resets session (but unlike TerminateSession, session might be re-used)
+ void InternalResetSession(void); // static implementation for calling through virtual destructor and virtual ResetSession();
+ // user authentication
+ #ifndef SYSYNC_CLIENT
+ // - return auth type to be requested from remote
+ virtual TAuthTypes requestedAuthType(void); // avoids MD5 when it cannot be checked
+ // - get next nonce string top be sent to remote party for subsequent MD5 auth
+ virtual void getNextNonce(const char *aDeviceID, string &aNextNonce);
+ // - get nonce string, which is expected to be used by remote party for MD5 auth.
+ virtual void getAuthNonce(const char *aDeviceID, string &aAuthNonce);
+ #endif
+ #ifndef BASED_ON_BINFILE_CLIENT
+ // - check device ID related stuff
+ virtual void CheckDevice(const char *aDeviceID);
+ // - remote device is analyzed, eventually save status
+ virtual void remoteAnalyzed(void);
+ // - check login for this session (everything else is done by CustomAgent's SessionLogin)
+ virtual bool CheckLogin(const char *aOriginalUserName, const char *aModifiedUserName, const char *aAuthString, TAuthSecretTypes aAuthStringType, const char *aDeviceID);
+ #endif // not BASED_ON_BINFILE_CLIENT
+ // - logout
+ void LogoutApi(void);
+ // current database date & time
+ virtual lineartime_t getDatabaseNowAs(timecontext_t aTimecontext);
+ // agent config
+ TPluginAgentConfig *fPluginAgentConfigP;
+ // set while Api access is locked because of a thread using it
+ bool fApiLocked;
+ // get API session object
+ TDB_Api_Session *getDBApiSession() { return &fDBApiSession; };
+protected:
+ #ifndef SYSYNC_CLIENT
+ // - request end, used to clean up
+ virtual void RequestEnded(bool &aHasData);
+ #endif
+private:
+ TDB_Api_Session fDBApiSession;
+}; // TPluginApiAgent
+
+} // namespace sysync
+
+#endif // PLUGINAPIAGENT_H
+
+// eof
diff --git a/src/DB_interfaces/api_db/pluginapids.cpp b/src/DB_interfaces/api_db/pluginapids.cpp
new file mode 100755
index 0000000..8c08ffe
--- /dev/null
+++ b/src/DB_interfaces/api_db/pluginapids.cpp
@@ -0,0 +1,2757 @@
+/**
+ * @File pluginapids.cpp
+ *
+ * @Author Lukas Zeller (luz@synthesis.ch)
+ *
+ * @brief TPluginApiDS
+ * Plugin based datastore API implementation
+ *
+ * Copyright (c) 2001-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ * @Date 2005-10-06 : luz : created from apidbdatastore
+ */
+
+// includes
+#include "sysync.h"
+#include "pluginapids.h"
+#include "pluginapiagent.h"
+
+#ifdef SYSER_REGISTRATION
+#include "syserial.h"
+#endif
+
+#ifdef ENGINEINTERFACE_SUPPORT
+#include "engineentry.h"
+#endif
+
+#include "SDK_support.h"
+
+
+namespace sysync {
+
+// Config
+// ======
+
+// Helpers
+// =======
+
+
+// Field Map item
+// ==============
+
+TApiFieldMapItem::TApiFieldMapItem(const char *aElementName, TConfigElement *aParentElement) :
+ inherited(aElementName,aParentElement)
+{
+ /* nop for now */
+} // TApiFieldMapItem::TApiFieldMapItem
+
+
+void TApiFieldMapItem::checkAttrs(const char **aAttributes)
+{
+ /* nop for now */
+ inherited::checkAttrs(aAttributes);
+} // TApiFieldMapItem::checkAttrs
+
+
+#ifdef ARRAYDBTABLES_SUPPORT
+
+// Array Map item
+// ===============
+
+TApiFieldMapArrayItem::TApiFieldMapArrayItem(TCustomDSConfig *aCustomDSConfigP, TConfigElement *aParentElement) :
+ inherited(aCustomDSConfigP,aParentElement)
+{
+ /* nop for now */
+} // TApiFieldMapArrayItem::TApiFieldMapArrayItem
+
+
+void TApiFieldMapArrayItem::checkAttrs(const char **aAttributes)
+{
+ /* nop for now */
+ inherited::checkAttrs(aAttributes);
+} // TApiFieldMapArrayItem::checkAttrs
+
+#endif
+
+
+
+// TPluginDSConfig
+// ============
+
+TPluginDSConfig::TPluginDSConfig(const char* aName, TConfigElement *aParentElement) :
+ inherited(aName,aParentElement),
+ fPluginParams_Admin(this),
+ fPluginParams_Data(this)
+{
+ // nop so far
+ clear();
+} // TPluginDSConfig::TPluginDSConfig
+
+
+TPluginDSConfig::~TPluginDSConfig()
+{
+ clear();
+ // disconnect from the API module
+ fDBApiConfig_Data.Disconnect();
+ fDBApiConfig_Admin.Disconnect();
+} // TPluginDSConfig::~TPluginDSConfig
+
+
+// init defaults
+void TPluginDSConfig::clear(void)
+{
+ // init defaults
+ fDBAPIModule_Admin.erase();
+ fDBAPIModule_Data.erase();
+ fDataModuleAlsoHandlesAdmin=false;
+ // - default to use all debug flags set (if debug for plugin is enabled at all)
+ fPluginDbgMask_Admin=0xFFFF;
+ fPluginDbgMask_Data=0xFFFF;
+ // - clear plugin params
+ fPluginParams_Admin.clear();
+ fPluginParams_Data.clear();
+ // - clear capabilities
+ fItemAsKey = false;
+ // clear inherited
+ inherited::clear();
+} // TPluginDSConfig::clear
+
+
+// config element parsing
+bool TPluginDSConfig::localStartElement(const char *aElementName, const char **aAttributes, sInt32 aLine)
+{
+ // checking the elements
+ if (strucmp(aElementName,"plugin_module")==0)
+ expectMacroString(fDBAPIModule_Data);
+ else if (strucmp(aElementName,"plugin_datastoreadmin")==0)
+ expectBool(fDataModuleAlsoHandlesAdmin);
+ else if (strucmp(aElementName,"plugin_params")==0)
+ expectChildParsing(fPluginParams_Data);
+ else if (strucmp(aElementName,"plugin_debugflags")==0)
+ expectUInt16(fPluginDbgMask_Data);
+ else if (strucmp(aElementName,"plugin_module_admin")==0)
+ expectMacroString(fDBAPIModule_Admin);
+ else if (strucmp(aElementName,"plugin_params_admin")==0)
+ expectChildParsing(fPluginParams_Admin);
+ else if (strucmp(aElementName,"plugin_debugflags_admin")==0)
+ expectUInt16(fPluginDbgMask_Admin);
+ else
+ return inherited::localStartElement(aElementName,aAttributes,aLine);
+ // ok
+ return true;
+} // TPluginDSConfig::localStartElement
+
+
+// resolve
+void TPluginDSConfig::localResolve(bool aLastPass)
+{
+ // resolve plugin specific config leaf
+ fPluginParams_Admin.Resolve(aLastPass);
+ fPluginParams_Data.Resolve(aLastPass);
+ // try to resolve configured API-module name into set of function pointers
+ if (aLastPass) {
+ // Determine if we may use non-built-in plugins
+ bool allowDLL= true; // by default, it is allowed, but if PLUGIN_DLL is not set, it will be disabled anyway.
+ #if defined(SYSER_REGISTRATION) && !defined(DLL_PLUGINS_ALWAYS_ALLOWED)
+ // license flags present and DLL plugins not generally allowed:
+ // -> license decides if DLL is allowed
+ allowDLL= (getSyncAppBase()->fRegProductFlags & SYSER_PRODFLAG_SERVER_SDKAPI)!=0;
+ // warn about DLL not allowed ONLY if this build actually supports DLL plugins
+ #if defined(PLUGIN_DLL)
+ if (!allowDLL) {
+ SYSYNC_THROW(TConfigParseException("License does not allow using <datastore type=\"plugin\">"));
+ }
+ #endif // DLL support available in the code at all
+ #endif // DLL plugins not generally allowed (or DLL support not compiled in)
+ // Connect module for handling data access
+ if (!fDBAPIModule_Data.empty()) {
+ // we have a module specified for data access
+ DB_Callback cb= &fDBApiConfig_Data.fCB.Callback;
+ cb->callbackRef = getSyncAppBase();
+ #ifdef ENGINEINTERFACE_SUPPORT
+ cb->thisBase = getSyncAppBase()->fEngineInterfaceP;
+ #endif
+ #ifdef SYDEBUG
+ // direct Module level debug to global log
+ cb->debugFlags= (getSyncAppBase()->getRootConfig()->fDebugConfig.fGlobalDebugLogs) &&
+ PDEBUGTEST(DBG_DATA+DBG_DBAPI+DBG_PLUGIN) ? fPluginDbgMask_Data : 0;
+ cb->DB_DebugPuts = AppBaseLogDebugPuts;
+ cb->DB_DebugBlock = AppBaseLogDebugBlock;
+ cb->DB_DebugEndBlock = AppBaseLogDebugEndBlock;
+ cb->DB_DebugEndThread = AppBaseLogDebugEndThread;
+ cb->DB_DebugExotic = AppBaseLogDebugExotic;
+ #endif
+ if (fDBApiConfig_Data.Connect(fDBAPIModule_Data.c_str(), getSyncAppBase()->fApiInterModuleContext, getName(), false, allowDLL)!=LOCERR_OK)
+ SYSYNC_THROW(TConfigParseException("Cannot connect to datastore implementation module specified in <plugin_module>"));
+ // now pass plugin-specific config
+ if (fDBApiConfig_Data.PluginParams(fPluginParams_Data.fConfigString.c_str())!=LOCERR_OK)
+ SYSYNC_THROW(TConfigParseException("Module does not understand params passed in <plugin_params>"));
+ // Check for new method for data access (as keys instead of as text items)
+ TDB_Api_Str capa;
+ fDBApiConfig_Data.Capabilities(capa);
+ string capaStr = capa.c_str();
+ fItemAsKey = FlagOK(capaStr,CA_ItemAsKey,true);
+ // Check if engine is compatible
+ #ifndef DBAPI_TEXTITEMS
+ if (!fItemAsKey) SYSYNC_THROW(TConfigParseException("This engine does not support data items in text format"));
+ #endif
+ #if !defined(DBAPI_ASKEYITEMS) || !defined(ENGINEINTERFACE_SUPPORT)
+ if (fItemAsKey) SYSYNC_THROW(TConfigParseException("This engine does not support data items passed as key handles"));
+ #endif
+ }
+ // connect module for handling admin access
+ // - use same module and params as data if no separate module specified and plugin_datastoreadmin is set
+ if (fDBAPIModule_Admin.empty() && fDataModuleAlsoHandlesAdmin) {
+ fDBAPIModule_Admin=fDBAPIModule_Data;
+ fPluginParams_Admin=fPluginParams_Data;
+ fPluginDbgMask_Admin=fPluginDbgMask_Data;
+ }
+ // - now connect the admin module
+ if (!fDBAPIModule_Admin.empty()) {
+ // we have a module specified for data access
+ DB_Callback cb= &fDBApiConfig_Admin.fCB.Callback;
+ cb->callbackRef = getSyncAppBase();
+ #ifdef ENGINEINTERFACE_SUPPORT
+ cb->thisBase = getSyncAppBase()->fEngineInterfaceP;
+ #endif
+ #ifdef SYDEBUG
+ // direct Module level debug to global log
+ cb->debugFlags= (getSyncAppBase()->getRootConfig()->fDebugConfig.fGlobalDebugLogs) &&
+ PDEBUGTEST(DBG_ADMIN+DBG_DBAPI+DBG_PLUGIN) ? fPluginDbgMask_Admin : 0;
+ cb->DB_DebugPuts = AppBaseLogDebugPuts;
+ cb->DB_DebugBlock = AppBaseLogDebugBlock;
+ cb->DB_DebugEndBlock = AppBaseLogDebugEndBlock;
+ cb->DB_DebugEndThread= AppBaseLogDebugEndThread;
+ cb->DB_DebugExotic = AppBaseLogDebugExotic;
+ #endif
+ if (fDBApiConfig_Admin.Connect(fDBAPIModule_Admin.c_str(),getSyncAppBase()->fApiInterModuleContext,getName())!=LOCERR_OK)
+ SYSYNC_THROW(TConfigParseException("Cannot connect to datastore implementation module specified in <plugin_module_admin>"));
+ // now pass plugin-specific config
+ if (fDBApiConfig_Admin.PluginParams(fPluginParams_Admin.fConfigString.c_str())!=LOCERR_OK)
+ SYSYNC_THROW(TConfigParseException("Module does not understand params passed in <plugin_params_admin>"));
+ }
+ }
+ // resolve inherited
+ inherited::localResolve(aLastPass);
+} // TPluginDSConfig::localResolve
+
+
+// - create appropriate datastore from config, calls addTypeSupport as well
+TLocalEngineDS *TPluginDSConfig::newLocalDataStore(TSyncSession *aSessionP)
+{
+ // Synccap defaults to normal set supported by the engine by default
+ TLocalEngineDS *ldsP =
+ #ifdef SYSYNC_CLIENT
+ new TPluginApiDS(this,aSessionP,getName(),aSessionP->getSyncCapMask() & ~(isOneWayFromRemoteSupported() ? 0 : SCAP_MASK_ONEWAY_SERVER));
+ #else
+ new TPluginApiDS(this,aSessionP,getName(),aSessionP->getSyncCapMask() & ~(isOneWayFromRemoteSupported() ? 0 : SCAP_MASK_ONEWAY_CLIENT));
+ #endif
+ // do common stuff
+ addTypes(ldsP,aSessionP);
+ // return
+ return ldsP;
+} // TPluginDSConfig::newLocalDataStore
+
+
+/*
+ * Implementation of TPluginApiDS
+ */
+
+// constructor
+TPluginApiDS::TPluginApiDS(
+ TPluginDSConfig *aConfigP,
+ sysync::TSyncSession *aSessionP,
+ const char *aName,
+ uInt32 aCommonSyncCapMask
+) :
+ #ifdef SDK_ONLY_SUPPORT
+ TCustomImplDS(aConfigP,aSessionP, aName, aCommonSyncCapMask)
+ #else
+ TODBCApiDS(aConfigP,aSessionP, aName, aCommonSyncCapMask)
+ #endif
+{
+ // save a type casted pointer to the agent
+ fPluginAgentP=static_cast<TPluginApiAgent *>(aSessionP);
+ // save pointer to config record
+ fPluginDSConfigP=aConfigP;
+ // make a local copy of the typed agent pointer (note that the agent itself does
+ // NOT YET have its constructor completely run so we can't just copy the agents pointer)
+ fPluginAgentConfigP = dynamic_cast<TPluginAgentConfig *>(
+ aSessionP->getRootConfig()->fAgentConfigP
+ );
+ if (!fPluginAgentConfigP) SYSYNC_THROW(TSyncException(DEBUGTEXT("TPluginApiDS finds no AgentConfig","api1")));
+ // Note: do not create context here because Agent is not yet initialized.
+ // clear rest
+ InternalResetDataStore();
+} // TPluginApiDS::TPluginApiDS
+
+
+TPluginApiDS::~TPluginApiDS()
+{
+ InternalResetDataStore();
+} // TPluginApiDS::~TPluginApiDS
+
+
+/// @brief called while agent is still fully ok, so we must clean up such that later call of destructor does NOT access agent any more
+void TPluginApiDS::announceAgentDestruction(void)
+{
+ // reset myself
+ InternalResetDataStore();
+ // make sure we don't access the agent any more
+ engTerminateDatastore();
+ fPluginAgentP = NULL;
+ // destroy API context
+ fDBApi_Data.DeleteContext();
+ fDBApi_Admin.DeleteContext();
+ // call inherited
+ inherited::announceAgentDestruction();
+} // TPluginApiDS::announceAgentDestruction
+
+
+/// @brief called to reset datastore
+/// @note must be safe to be called multiple times and even after announceAgentDestruction()
+void TPluginApiDS::InternalResetDataStore(void)
+{
+ // init some vars
+ /* nop for now */
+} // TPluginApiDS::InternalResetDataStore
+
+
+
+// helper to process params
+// - if aParamName!=NULL, it searches for the value of the requested parameter and returns != NULL, NULL if none found
+// - if aParamName==NULL, it scans until all params are skipped and returns end of params
+static const char *paramScan(const char *aParams,const char *aParamName, string &aValue)
+{
+ const char *p = aParams;
+ const char *q,*r;
+ int nl,vl;
+ bool quotedvalue=false;
+ if (!p) return false;
+ while (*p && *p==';') {
+ // skip param intro
+ p++;
+ // find end of param name
+ for (q=p; *q!=0 && *q!=';' && *q!=':' && *q!='=';) q++;
+ nl=q-p;
+ // - now: p=start of name, nl=length of name
+ // find end of param value
+ if (nl && *q=='=') {
+ // value starts after equal sign
+ q++;
+ if (*q=='"') { // " ) { work around bug in colorizer
+ // quoted value
+ quotedvalue=true;
+ r=++q;
+ while (*r && *r!='"') { // " ) { work around bug in colorizer
+ if (*r=='\\') {
+ r++;
+ if (*r) r++;
+ }
+ else
+ r++;
+ }
+ vl=r-q;
+ if (*r) r++; // skip closing quote if not delimited by end of string
+ }
+ else {
+ // unquoted value, ends at next colon, semicolon or line end (no value case)
+ for (r=q; *r && *r!=':' && *r!=';' && *r!='\r' && *r!='\n';) r++;
+ vl = r-q;
+ }
+ // - now: q=start of value, vl=length of value, *r=char after value
+ }
+ else {
+ // no value
+ r=q;
+ vl=0;
+ }
+ // check if it's our value
+ if (aParamName) {
+ // we are searching a single parameter
+ if (strucmp(p,aParamName,nl)==0) {
+ // found, return it's value
+ if (quotedvalue)
+ CStrToStrAppend(q, aValue, true); // stop at quote or end of line
+ else
+ aValue.assign(q,vl);
+ return p; // position of parameter name
+ }
+ }
+ // next param
+ p=r;
+ }
+ // end of all params
+ if (aParamName) return NULL; // we were searching for a special param and haven't found it
+ // we were scanning for the end of all params
+ // - save all params
+ aValue.assign(aParams,p-aParams);
+ // - return pointer to what comes after params
+ return p;
+} // paramScan
+
+
+#ifdef DBAPI_TEXTITEMS
+
+// store API key/value pair field in mapped field, if one is defined
+bool TPluginApiDS::storeField(
+ const char *aName,
+ const char *aParams,
+ const char *aValue,
+ TMultiFieldItem &aItem,
+ uInt16 aSetNo,
+ sInt16 aArrayIndex
+)
+{
+ TFieldMapList *fmlP = &(fPluginDSConfigP->fFieldMappings.fFieldMapList);
+ TFieldMapList::iterator pos;
+ TApiFieldMapItem *fmiP;
+ string s;
+ bool stored=false;
+ // search field map list for matching map entry
+ for (pos=fmlP->begin(); pos!=fmlP->end(); pos++) {
+ fmiP = static_cast<TApiFieldMapItem *>(*pos);
+ // check name
+ if (
+ fmiP->readable &&
+ fmiP->setNo==aSetNo &&
+ strucmp(fmiP->getName(),aName)==0
+ ) {
+ // DB-readable field with matching name
+ TDBFieldType dbfty = fmiP->dbfieldtype;
+ TItemField *fieldP;
+ sInt16 fid = fmiP->fid;
+ // determine leaf field
+ fieldP = getMappedFieldOrVar(aItem,fid,aArrayIndex);
+ // continue only if we have a field
+ if (!fieldP) continue;
+ // check if the field is proxyable and input defines a BLOB id
+ #ifdef STREAMFIELD_SUPPORT
+ if (fieldP->isBasedOn(fty_string)) {
+ // - check if params contain a BLOBID
+ string blobid;
+ if (paramScan(aParams,"BLOBID",blobid)) {
+ // this field is a blob, create a proxy for it
+ TApiBlobProxy *apiProxyP = new TApiBlobProxy(this,!fieldP->isBasedOn(fty_blob),blobid.c_str(),aItem.getLocalID());
+ // attach it to the string or blob field
+ static_cast<TStringField *>(fieldP)->setBlobProxy(apiProxyP);
+ // check if we must read it right now
+ if (paramScan(aParams,"READNOW",blobid))
+ static_cast<TStringField *>(fieldP)->pullFromProxy();
+ // check next mapping
+ continue;
+ }
+ }
+ #endif
+ // store according to database field type
+ switch (dbfty) {
+ case dbft_string:
+ // for explicit strings, perform character set and line feed conversion
+ s.erase();
+ // - convert from database charset to UTF-8 and to C-string linefeeds
+ appendStringAsUTF8(aValue, s, fPluginDSConfigP->fDataCharSet, lem_cstr);
+ fieldP->setAsString(s.c_str());
+ break;
+ case dbft_blob:
+ // blob is treated as 1:1 string if there's no proxy for it
+ default:
+ // for all other types, simply set as string w/o charset conversion etc.
+ if (fieldP->isBasedOn(fty_timestamp)) {
+ // interpret timestamps in dataTimeZone context (or as floating if this field is mapped in "f" mode)
+ TTimestampField *tsfP = static_cast<TTimestampField *>(fieldP);
+ tsfP->setAsISO8601(aValue, fmiP->floating_ts ? TCTX_UNKNOWN : fPluginDSConfigP->fDataTimeZone, false);
+ // modify time zone if params contain a TZNAME
+ if (paramScan(aParams,"TZNAME",s)) {
+ // convert to time zone context
+ timecontext_t tctx;
+ TimeZoneNameToContext(s.c_str(), tctx, tsfP->getGZones());
+ tsfP->moveToContext(tctx, true); // move to new context, bind floating (and float fixed, if TZNAME=FLOATING)
+ }
+ }
+ else {
+ // just set as string
+ fieldP->setAsString(aValue);
+ }
+ break;
+ } // switch
+ // field successfully stored, do NOT exit loop
+ // because there could be a second map for the same attribute!
+ stored=true;
+ } // if map found for attribute
+ } // for all field mappings
+ return stored;
+} // TPluginApiDS::storeField
+
+
+
+// - parse data into item
+bool TPluginApiDS::parseItemData(
+ TMultiFieldItem &aItem,
+ const char *aItemData,
+ uInt16 aSetNo
+)
+{
+ // read data from input string into mapped fields (or local vars)
+ const char *p = aItemData;
+ const char *q;
+ string fieldname,params,value;
+ bool readsomething=false;
+ uInt16 arrayindex;
+ // show item data as is
+ PDEBUGPRINTFX(DBG_USERDATA+DBG_DBAPI+DBG_EXOTIC+DBG_HOT,("parseItemData received string from DBApi:"));
+ PDEBUGPUTSXX(DBG_USERDATA+DBG_DBAPI+DBG_EXOTIC,aItemData,0,true);
+ // read all fields
+ while(*p) {
+ arrayindex=0;
+ // find name
+ for (q=p; *q && *q!='[' && *q!=':' && *q!=';';) q++;
+ fieldname.assign(p,q-p);
+ // check for array index
+ if (*q=='[') {
+ q++;
+ q+=StrToUShort(q,arrayindex);
+ if (*q==']') q++;
+ }
+ p=q;
+ // find and skip params
+ p = paramScan(p,NULL,params);
+ // p should now point to ':'
+ if (*p==':' || !params.empty()) { // blobs needn't to contain a ':'
+ value.erase();
+
+ if (*p==':') { // only get a value, if there is one !!
+ p++; // consume colon
+ // get value
+ p += CStrToStrAppend( p,value,true ); // stop at quote or ctrl char
+ } // if
+
+ // store field now
+ if (storeField(
+ fieldname.c_str(),
+ params.c_str(),
+ value.c_str(),
+ aItem,
+ aSetNo, // ordering of params is correct now ( before <arrayindex> !! )
+ arrayindex
+ ))
+ readsomething=true;
+ }
+ // skip everything up to next end of line (in case value was terminated by a quote or other ctrl char)
+ while (*p && *p!='\r' && *p!='\n') p++;
+ // skip all line end chars up to beginning of next line or end of record
+ while (*p && (*p=='\r' || *p=='\n')) p++;
+ // p now points to next line's beginning
+ };
+ // post-process
+ return postReadProcessItem(aItem,aSetNo);
+} // TPluginApiDS::parseItemData
+
+
+
+// generate text representations of item's fields (BLOBs and parametrized fields not included)
+// - returns true if at least one field appended
+bool TPluginApiDS::generateItemData(
+ bool aAssignedOnly,
+ TMultiFieldItem &aItem,
+ uInt16 aSetNo,
+ string &aDataFields
+)
+{
+ TFieldMapList *fmlP = &(fPluginDSConfigP->fFieldMappings.fFieldMapList);
+ TFieldMapList::iterator pos;
+ TApiFieldMapItem *fmiP;
+ string val;
+ bool createdone=false;
+
+ // pre-process (run scripts)
+ if (!preWriteProcessItem(aItem)) return false;
+ // create text representation for all mapped and writable fields
+ for (pos=fmlP->begin(); pos!=fmlP->end(); pos++) {
+ fmiP = static_cast<TApiFieldMapItem *>(*pos);
+ if (
+ fmiP->writable &&
+ fmiP->setNo==aSetNo
+ ) {
+ // get field
+ TItemField *basefieldP,*leaffieldP;
+ sInt16 fid = fmiP->fid;
+ // determine base field (might be array)
+ basefieldP = getMappedBaseFieldOrVar(aItem,fid);
+ // ignore map if we have no field for it
+ if (!basefieldP) continue;
+ // ignore map if field is not assigned and assignedonly flag is set
+ if (aAssignedOnly && basefieldP->isUnassigned()) continue;
+ // yes, we want to write this field
+ #ifdef ARRAYFIELD_SUPPORT
+ uInt16 arrayIndex=0;
+ #endif
+ do {
+ // first check if there is an element at all
+ #ifdef ARRAYFIELD_SUPPORT
+ if (basefieldP->isArray())
+ leaffieldP = basefieldP->getArrayField(arrayIndex,true); // get existing leaf fields only
+ else
+ leaffieldP = basefieldP; // leaf is base field
+ #else
+ leaffieldP = basefieldP; // leaf is base field
+ #endif
+ // if no leaf field, we'll need to exit here (we're done with the array)
+ if (leaffieldP==NULL) break;
+ // we have some data, first append name
+ aDataFields+=fmiP->getName();
+ #ifdef ARRAYFIELD_SUPPORT
+ // append array index if this is an array field
+ if (basefieldP->isArray())
+ StringObjAppendPrintf(aDataFields,"[%d]",arrayIndex);
+ #endif
+ // append value
+ if (basefieldP->isBasedOn(fty_blob) || fmiP->as_param) {
+ // - for blobs and parametrized values, we use a BlobID and send the data later
+ aDataFields+= ";BLOBID=";
+ aDataFields+= fmiP->getName();
+ #ifdef ARRAYFIELD_SUPPORT
+ // append array index if this is an array field
+ if (basefieldP->isArray())
+ StringObjAppendPrintf(aDataFields,"[%d]",arrayIndex);
+ #endif
+ }
+ else {
+ // - literal value (converted to DB charset as C-escaped string)
+ if (leaffieldP->isBasedOn(fty_timestamp) && fmiP->dbfieldtype==dbft_timestamp) {
+ TTimestampField *tsfP = static_cast<TTimestampField *>(leaffieldP);
+ // get original zone
+ timecontext_t tctx = tsfP->getTimeContext();
+ if (TCTX_IS_DURATION(tctx) || TCTX_IS_DATEONLY(tctx) ||!TCTX_IS_UNKNOWN(tctx)) {
+ // not fully floating, get name
+ TimeZoneContextToName(tctx, val, tsfP->getGZones());
+ // append it
+ aDataFields+= ";TZNAME=";
+ aDataFields+= val;
+ }
+ // now convert to database time zone
+ tctx = fPluginDSConfigP->fDataTimeZone; // desired database zone
+ // report as-is if we have a floating map or if it IS floating
+ if (fmiP->floating_ts || tsfP->isFloating())
+ tctx = TCTX_UNKNOWN; // report as-is
+ // now create ISO8601 of it
+ tsfP->getAsISO8601(val,tctx,true,false,false);
+ }
+ else {
+ leaffieldP->getAsString(val); // get value
+ }
+ aDataFields+=':'; // delimiter
+ string valDB;
+ appendUTF8ToString(
+ val.c_str(),
+ valDB,
+ fPluginDSConfigP->fDataCharSet,
+ fPluginDSConfigP->fDataLineEndMode
+ );
+ StrToCStrAppend(valDB.c_str(),aDataFields,true); // allow 8-bit chars to be represented as-is (no \xXX escape needed)
+ } // if
+ aDataFields+="\r\n"; // CRLF at end
+ // we now have at least one field
+ createdone=true;
+ // next item in array
+ #ifdef ARRAYFIELD_SUPPORT
+ arrayIndex++;
+ #endif
+ } while(basefieldP->isArray()); // only arrays do loop
+ } // if writable field
+ } // for all field mappings
+ PDEBUGPRINTFX(DBG_USERDATA+DBG_DBAPI+DBG_EXOTIC+DBG_HOT,("generateItemData generated string for DBApi:"));
+ PDEBUGPUTSXX(DBG_USERDATA+DBG_DBAPI+DBG_EXOTIC,aDataFields.c_str(),0,true);
+ return createdone;
+} // TPluginApiDS::generateItemData
+
+#endif // DBAPI_TEXTITEMS
+
+
+
+#if defined(DBAPI_ASKEYITEMS) && defined(ENGINEINTERFACE_SUPPORT)
+
+// - get a settings key instance that can access the item
+// NULL is allowed for aItemP for cases where we don't have or want an item (!ReadNextItem:allfields)
+TDBItemKey *TPluginApiDS::newDBItemKey(TMultiFieldItem *aItemP)
+{
+ return new TDBItemKey(getSession()->getSyncAppBase()->fEngineInterfaceP,aItemP,this);
+} // TPluginApiDS::newDBItemKey
+
+#endif // DBAPI_ASKEYITEMS + ENGINEINTERFACE_SUPPORT
+
+
+
+
+
+
+
+// - post process item after reading from DB (run script)
+bool TPluginApiDS::postReadProcessItem(TMultiFieldItem &aItem, uInt16 aSetNo)
+{
+ #if defined(DBAPI_ASKEYITEMS) && defined(STREAMFIELD_SUPPORT)
+ // for ItemKey mode, we need to create a BLOB proxy for each as_param mapped field
+ if (fPluginDSConfigP->fItemAsKey) {
+ // find all asParam fields that can have proxies and create BLOB proxies for these
+ TFieldMapList *fmlP = &(fPluginDSConfigP->fFieldMappings.fFieldMapList);
+ TFieldMapList::iterator pos;
+ TApiFieldMapItem *fmiP;
+
+ // create text representation for all mapped and writable fields
+ for (pos=fmlP->begin(); pos!=fmlP->end(); pos++) {
+ fmiP = static_cast<TApiFieldMapItem *>(*pos);
+ if (
+ fmiP->readable &&
+ fmiP->setNo==aSetNo &&
+ fmiP->as_param // only if explicitly marked as parameter
+ ) {
+ // get field
+ TItemField *basefieldP, *leaffieldP;
+ sInt16 fid = fmiP->fid;
+ // determine base field (might be array)
+ basefieldP = getMappedBaseFieldOrVar(aItem,fid);
+ // ignore map if we have no field for it
+ if (!basefieldP) continue;
+ // ignore all that can't have a blob proxy (non-strings)
+ if (!basefieldP->isBasedOn(fty_string)) continue;
+ // unlike with textItems that get the BLOBID from the DB,
+ // in asKey mode, BLOBID is just map name plus an eventual array index.
+ // Plugin must be able to identify the BLOB using this plus the item ID.
+ // Plugin must also make sure an array element exists (value does not matter, can be empty)
+ // for each element that should be proxied here.
+ // - create the proxies (one for each array element)
+ #ifdef ARRAYFIELD_SUPPORT
+ uInt16 arrayIndex=0;
+ #endif
+ do {
+ // first check if there is an element at all
+ string blobid = fmiP->getName(); // map name
+ #ifdef ARRAYFIELD_SUPPORT
+ if (basefieldP->isArray()) {
+ leaffieldP = basefieldP->getArrayField(arrayIndex,true); // get existing leaf fields only
+ StringObjAppendPrintf(blobid,"[%d]",arrayIndex); // add array index to blobid
+ arrayIndex++;
+ }
+ else
+ leaffieldP = basefieldP; // leaf is base field
+ // if no leaf field, we'll need to exit here (we're done with the array)
+ if (leaffieldP==NULL) break;
+ #else
+ leaffieldP = basefieldP; // no arrays: leaf is always base field
+ #endif
+ // this array element exists, create the proxy
+ TApiBlobProxy *apiProxyP = new TApiBlobProxy(this,!leaffieldP->isBasedOn(fty_blob),blobid.c_str(),aItem.getLocalID());
+ // Note: we do not support "READNOW" proxies here, as they are useless in ItemKey context: if the
+ // BLOB cannot be read later, no need for as_aparam map exists and plugin should just put the value
+ // directly via the SetKeyValue() API.
+ // attach proxy to the string or blob field
+ static_cast<TStringField *>(leaffieldP)->setBlobProxy(apiProxyP);
+ } while(basefieldP->isArray()); // only arrays do loop all array elements
+ } // readable field mapping with explicit as_param mark
+ } // for all field mappings
+ } // if AsKey access
+ #endif // DBAPI_ASKEYITEMS
+ // execute afterread script now
+ #ifdef SCRIPT_SUPPORT
+ // process afterread script
+ fPluginAgentP->fScriptContextDatastore=this;
+ if (!TScriptContext::execute(fScriptContextP,fPluginDSConfigP->fFieldMappings.fAfterReadScript,fPluginDSConfigP->getDSFuncTableP(),fPluginAgentP,&aItem,true))
+ SYSYNC_THROW(TSyncException("<afterreadscript> fatal error"));
+ #endif
+ // always ok for now %%%
+ return true;
+} // TPluginApiDS::postReadProcessItem
+
+
+
+// - pre-process item before writing to DB (run script)
+bool TPluginApiDS::preWriteProcessItem(TMultiFieldItem &aItem)
+{
+ #ifdef SCRIPT_SUPPORT
+ // process beforewrite script
+ fWriting=true;
+ fDeleting=false;
+ fParentKey=aItem.getLocalID();
+ fPluginAgentP->fScriptContextDatastore=this;
+ if (!TScriptContext::execute(fScriptContextP,fPluginDSConfigP->fFieldMappings.fBeforeWriteScript,fPluginDSConfigP->getDSFuncTableP(),fPluginAgentP,&aItem,true))
+ SYSYNC_THROW(TSyncException("<beforewritescript> fatal error"));
+ #endif
+ // always ok for now %%%
+ return true;
+} // TPluginApiDS::preWriteProcessItem
+
+
+
+
+
+// - send BLOBs of this item one by one. Returns true if we have a blob at all
+bool TPluginApiDS::writeBlobs(
+ bool aAssignedOnly,
+ TMultiFieldItem &aItem,
+ uInt16 aSetNo
+)
+{
+ TFieldMapList *fmlP = &(fPluginDSConfigP->fFieldMappings.fFieldMapList);
+ TFieldMapList::iterator pos;
+ TApiFieldMapItem *fmiP;
+ string blobfieldname;
+ bool blobwritten=false;
+ TSyError dberr;
+
+ // store all parametrized fields or BLOBs one by one
+ for (pos=fmlP->begin(); pos!=fmlP->end(); pos++) {
+ fmiP = static_cast<TApiFieldMapItem *>(*pos);
+ if (
+ fmiP->writable &&
+ fmiP->setNo==aSetNo
+ ) {
+ TItemField *basefieldP,*leaffieldP;
+ sInt16 fid = fmiP->fid;
+ // determine base field (might be array)
+ basefieldP = getMappedBaseFieldOrVar(aItem,fid);
+ // ignore map if we have no field for it
+ if (!basefieldP) continue;
+ // ignore map if field is not assigned and assignedonly flag is set
+ if (aAssignedOnly && basefieldP->isUnassigned()) continue;
+ // omit all non-BLOB normal fields
+ // Note: in ItemKey mode, blobs must have the as_aparam flag to be written as blobs.
+ // in textItem mode, all fty_blob based fields will ALWAYS be written as blobs.
+ if (!(fmiP->as_param || (basefieldP->isBasedOn(fty_blob) && !fPluginDSConfigP->fItemAsKey))) continue;
+ // yes, we want to write this field as a BLOB
+ #ifdef ARRAYFIELD_SUPPORT
+ uInt16 arrayIndex;
+ for (arrayIndex=0; true; arrayIndex++)
+ #endif
+ {
+ // first create BLOB ID
+ blobfieldname=fmiP->getName();
+ #ifdef ARRAYFIELD_SUPPORT
+ // append array index if this is an array field
+ if (basefieldP->isArray()) {
+ StringObjAppendPrintf(blobfieldname,"[%d]",arrayIndex);
+ // calculate leaf field
+ leaffieldP = basefieldP->getArrayField(arrayIndex,true); // get existing leaf fields only
+ }
+ else {
+ leaffieldP = basefieldP; // leaf is base field
+ }
+ // if no leaf field, we'll need to exit here (we're done with the array)
+ if (leaffieldP==NULL) break;
+ #else
+ leaffieldP = basefieldP; // leaf is base field
+ #endif
+ // Now write this BLOB or string field
+ size_t maxBlobWriteBlockSz;
+ size_t blobsize;
+ void *bufferP = NULL;
+ string strDB;
+ bool isString = !leaffieldP->isBasedOn(fty_blob);
+ if (!isString) {
+ #ifdef STREAMFIELD_SUPPORT
+ leaffieldP->resetStream(); // this might be the wrong place to do this ...
+ blobsize = leaffieldP->getStreamSize();
+ maxBlobWriteBlockSz = 16000;
+ // allocate buffer
+ if (blobsize)
+ bufferP = new unsigned char[blobsize>maxBlobWriteBlockSz ? maxBlobWriteBlockSz : blobsize];
+ #else
+ // we cannot stream, but just read string contents
+ blobsize = leaffieldP->getStringSize();
+ maxBlobWriteBlockSz = blobsize; // all at once
+ bufferP = (void *) leaffieldP->getCStr(); // get pointer to string
+ #endif
+ }
+ else {
+ // is string -> first convert to DB charset
+ blobsize = leaffieldP->getStringSize();
+ maxBlobWriteBlockSz = blobsize;
+ string s;
+ leaffieldP->getAsString(s);
+ // convert to DB charset and linefeeds
+ appendUTF8ToString(
+ s.c_str(),
+ strDB,
+ fPluginDSConfigP->fDataCharSet,
+ fPluginDSConfigP->fDataLineEndMode
+ );
+ bufferP = (void *)strDB.c_str();
+ }
+ SYSYNC_TRY {
+ // now read from stream field and send to API
+ bool first=true;
+ bool last=false;
+ size_t remaining=blobsize;
+ size_t bytes,actualbytes;
+ while (!last) {
+ // it's the last block when we can write remaining bytes now
+ last = remaining<=maxBlobWriteBlockSz;
+ // calculate block size of this iteration
+ bytes = last ? remaining : maxBlobWriteBlockSz;
+ #ifdef STREAMFIELD_SUPPORT
+ if (!isString) {
+ // read these from the stream field
+ actualbytes = leaffieldP->readStream(bufferP,bytes); // we need to read
+ }
+ else
+ #endif
+ {
+ // we have it already in the buffer
+ actualbytes = bytes;
+ }
+ if (actualbytes<bytes) last=false;
+ // write to the DB API
+ dberr= fDBApi_Data.WriteBlob(
+ aItem.getLocalID(),
+ blobfieldname.c_str(),
+ bufferP,
+ actualbytes,
+ blobsize,
+ first,
+ last
+ );
+ if (dberr!=LOCERR_OK) SYSYNC_THROW(TSyncException("DBapi::WriteBlob fatal error"));
+ // not first call any more
+ first=false;
+ // calculate new remaining
+ remaining-=actualbytes;
+ } // while not last
+ #ifdef STREAMFIELD_SUPPORT
+ if (!isString && bufferP) delete (char *)bufferP;
+ #endif
+ }
+ SYSYNC_CATCH (...)
+ #ifdef STREAMFIELD_SUPPORT
+ if (!isString && bufferP) delete (char *)bufferP;
+ #endif
+ SYSYNC_RETHROW;
+ SYSYNC_ENDCATCH
+ // we now have at least one field
+ blobwritten=true;
+ #ifdef ARRAYFIELD_SUPPORT
+ // non-array do not loop
+ if (!basefieldP->isArray()) break;
+ #endif
+ } // for all array elements
+ } // if writable BLOB/parametrized field
+ } // for all field mappings
+ return blobwritten;
+} // TPluginApiDS::writeBlobs
+
+
+bool TPluginApiDS::deleteBlobs(
+ bool aAssignedOnly,
+ TMultiFieldItem &aItem,
+ uInt16 aSetNo
+)
+{
+ TFieldMapList *fmlP = &(fPluginDSConfigP->fFieldMappings.fFieldMapList);
+ TFieldMapList::iterator pos;
+ TApiFieldMapItem *fmiP;
+ string blobfieldname;
+ bool blobdeleted=false;
+ TSyError dberr;
+
+ // store all parametrized fields or BLOBs one by one
+ for (pos=fmlP->begin(); pos!=fmlP->end(); pos++) {
+ fmiP = static_cast<TApiFieldMapItem *>(*pos);
+ if (fmiP->writable &&
+ fmiP->setNo==aSetNo) {
+ // get field
+ TItemField *basefieldP,*leaffieldP;
+ sInt16 fid = fmiP->fid;
+ // determine base field (might be array)
+ basefieldP = getMappedBaseFieldOrVar(aItem,fid);
+ // ignore map if we have no field for it
+ if (!basefieldP) continue;
+
+ // %%% what is this, obsolete??
+ // ignore map if field is not assigned and assignedonly flag is set
+ //if (aAssignedOnly && basefieldP->isUnassigned()) continue;
+
+ // omit all non-BLOB normal fields
+ // Note: in ItemKey mode, blobs must have the as_aparam flag to be written as blobs.
+ // in textItem mode, all fty_blob based fields will ALWAYS be written as blobs.
+ if (!(fmiP->as_param || (basefieldP->isBasedOn(fty_blob) && !fPluginDSConfigP->fItemAsKey))) continue;
+ // yes, we want to write this field as a BLOB
+
+ #ifdef ARRAYFIELD_SUPPORT
+ uInt16 arrayIndex;
+ for (arrayIndex=0; true; arrayIndex++)
+ #endif
+ {
+ // first create BLOB ID
+ blobfieldname=fmiP->getName();
+ #ifdef ARRAYFIELD_SUPPORT
+ // append array index if this is an array field
+ if (basefieldP->isArray()) {
+ StringObjAppendPrintf(blobfieldname,"[%d]",arrayIndex);
+ // calculate leaf field
+ leaffieldP= basefieldP->getArrayField(arrayIndex,true); // get existing leaf fields only
+ }
+ else {
+ leaffieldP= basefieldP; // leaf is base field
+ }
+ // if no leaf field, we'll need to exit here (we're done with the array)
+ if (leaffieldP==NULL) break;
+ #else
+ leaffieldP= basefieldP; // leaf is base field
+ #endif
+
+ SYSYNC_TRY {
+ dberr= fDBApi_Data.DeleteBlob( aItem.getLocalID(),blobfieldname.c_str() );
+ if (dberr==LOCERR_NOTIMP) dberr= LOCERR_OK; // Not implemented is not an error
+ if (dberr!=LOCERR_OK) SYSYNC_THROW(TSyncException("DBapi::DeleteBlob fatal error"));
+ }
+ SYSYNC_CATCH (...)
+ SYSYNC_RETHROW;
+ SYSYNC_ENDCATCH
+
+ // we now have at least one field
+ blobdeleted= true;
+
+ #ifdef ARRAYFIELD_SUPPORT
+ // non-array do not loop
+ if (!basefieldP->isArray()) break;
+ #endif
+ } // for all array elements
+ } // if writable BLOB/parametrized field
+ } // for all field mappings
+ return blobdeleted;
+} // TPluginApiDS::deleteBlobs
+
+
+
+/// returns true if DB implementation supports resume (saving of resume marks, alert code, pending maps, tempGUIDs)
+bool TPluginApiDS::dsResumeSupportedInDB(void)
+{
+ if (fPluginDSConfigP->fDBApiConfig_Admin.Connected()) {
+ // we can do resume if plugin supports it
+ return fPluginDSConfigP->fDBApiConfig_Admin.Version()>=sInt32(VE_InsertMapItem);
+ }
+ return inherited::dsResumeSupportedInDB();
+} // TPluginApiDS::dsResumeSupportedInDB
+
+
+#ifdef OBJECT_FILTERING
+
+// - returns true if DB implementation can also apply special filters like CGI-options
+// /dr(x,y) etc. during fetching
+bool TPluginApiDS::dsOptionFilterFetchesFromDB(void)
+{
+ #ifndef SDK_ONLY_SUPPORT
+ // if we are not connected, let immediate ancestor check it (e.g. SQL/ODBC)
+ if (!fDBApi_Data.Created()) return inherited::dsOptionFilterFetchesFromDB();
+ #endif
+ #ifdef SYSYNC_TARGET_OPTIONS
+ string rangeFilter,s;
+ // check range filtering
+ // - date start end
+ rangeFilter = "daterangestart:";
+ TimestampToISO8601Str(s, fDateRangeStart, TCTX_UTC, false, false);
+ rangeFilter += s.c_str();
+ // - date range end
+ rangeFilter += "\r\ndaterangeend:";
+ TimestampToISO8601Str(s, fDateRangeEnd, TCTX_UTC, false, false);
+ rangeFilter += s.c_str();
+ // %%% tbd:
+ // - attachments inhibit
+ // - size limit
+ #if !defined _MSC_VER || defined WINCE
+ #warning "attachments and limit filters not yet supported"
+ #endif
+ // - let plugin know and check (we can filter at DBlevel if plugin understands both start/end)
+ rangeFilter += "\r\n";
+ bool canfilter =
+ (fDBApi_Data.FilterSupport(rangeFilter.c_str())>=2) ||
+ (fDateRangeStart==0 && fDateRangeEnd==0); // no range can always be "filtered"
+ // if we can filter, that's sufficient
+ if (canfilter) return true;
+ #else
+ // there is no range ever: yes, we can filter
+ return true;
+ #endif
+ // otherwise, let implementation test (not immediate anchestor, which is a different API like ODBC)
+ return TCustomImplDS::dsOptionFilterFetchesFromDB();
+} // TPluginApiDS::dsOptionFilterFetchesFromDB
+
+
+// - returns true if DB implementation can filter the standard filters
+// (LocalDBFilter, TargetFilter and InvisibleFilter) during database fetch
+// - otherwise, fetched items will be filtered after being read from DB.
+bool TPluginApiDS::dsFilteredFetchesFromDB(bool aFilterChanged)
+{
+ #ifndef SDK_ONLY_SUPPORT
+ // if we are not connected, let immediate ancestor check it (e.g. SQL/ODBC)
+ if (!fDBApi_Data.Created()) return inherited::dsFilteredFetchesFromDB(aFilterChanged);
+ #endif
+ // Anyway, let DBApi know (even if all filters are empty)
+ string filters;
+ // - local DB filter (=static filter, from config)
+ filters = "staticfilter:";
+ filters += fLocalDBFilter.c_str();
+ // - dynamic sync set filter
+ filters += "\r\ndynamicfilter:";
+ filters += fSyncSetFilter.c_str();
+ // - invisible filter (those that MATCH this filter should NOT be included)
+ filters += "\r\ninvisiblefilter:";
+ filters += fPluginDSConfigP->fInvisibleFilter.c_str();
+ // - let plugin know and check (we can filter at DBlevel if plugin understands both start/end)
+ filters += "\r\n";
+ bool canfilter =
+ (fDBApi_Data.FilterSupport(filters.c_str())>=3) ||
+ (fLocalDBFilter.empty() && fSyncSetFilter.empty() && fPluginDSConfigP->fInvisibleFilter.empty()); // no filter set = we can "filter"
+ // if we can filter, that's sufficient
+ if (canfilter) return true;
+ // otherwise, let implementation test (not immediate anchestor, which might be a different API like ODBC)
+ return TCustomImplDS::dsFilteredFetchesFromDB(aFilterChanged);
+} // TPluginApiDS::dsFilteredFetchesFromDB
+
+#endif
+
+
+
+// read sync set IDs and mod dates (and rest of data if technically unavoidable or
+// requested by aNeedAll)
+localstatus TPluginApiDS::apiReadSyncSet(bool aNeedAll)
+{
+ TSyError dberr=LOCERR_OK;
+
+ #ifdef BASED_ON_BINFILE_CLIENT
+ // we need to create the context for the data plugin here, as loadAdminData is not called in BASED_ON_BINFILE_CLIENT case.
+ dberr = connectDataPlugin();
+ if (dberr==LOCERR_OK) {
+ if (!fDBApi_Data.Created()) {
+ // - use datastore name as context name and link with session context
+ dberr = fDBApi_Data.CreateContext(
+ getName(), false,
+ &(fPluginDSConfigP->fDBApiConfig_Data),
+ "anydevice", // no real device key
+ "singleuser", // no real user key
+ NULL // no associated session level // fPluginAgentP->getDBApiSession()
+ );
+ }
+ }
+ else if (dberr==LOCERR_NOTIMP)
+ dberr=LOCERR_OK; // we just don't have a data plugin, that's ok, inherited (SQL) will handle data
+ if (dberr!=LOCERR_OK)
+ return dberr;
+ #endif
+ #ifndef SDK_ONLY_SUPPORT
+ // only handle here if we are in charge - otherwise let ancestor handle it
+ if (!fDBApi_Data.Created()) return inherited::apiReadSyncSet(aNeedAll);
+ #endif
+
+ // just let plugin know if we want data (if it actually does is the plugin's choice)
+ if (aNeedAll) {
+ // we'd like to get all data, let datastore know
+ fDBApi_Data.ContextSupport("ReadNextItem:allfields\n\r");
+ }
+ #ifdef SCRIPT_SUPPORT
+ // process init script
+ fParentKey.erase();
+ fWriting=false;
+ fPluginAgentP->fScriptContextDatastore=this;
+ if (!TScriptContext::executeTest(true,fScriptContextP,fPluginDSConfigP->fFieldMappings.fInitScript,fPluginDSConfigP->getDSFuncTableP(),fPluginAgentP))
+ SYSYNC_THROW(TSyncException("<initscript> failed"));
+ #endif
+ // start reading
+ // - read list of all local IDs that are in the current sync set
+ DeleteSyncSet();
+ #ifdef SYDEBUG
+ string ts1,ts2;
+ StringObjTimestamp(ts1,getPreviousToRemoteSyncCmpRef());
+ StringObjTimestamp(ts2,getPreviousSuspendCmpRef());
+ PDEBUGPRINTFX(DBG_DATA,(
+ "Now reading local sync set: last sync to remote at %s, last suspend at %s",
+ ts1.c_str(),
+ ts2.c_str()
+ ));
+ #endif
+ // start the reading phase anyway (to make sure call order is always StartRead/EndRead/StartWrite/EndWrite)
+ dberr = fDBApi_Data.StartDataRead(fPreviousToRemoteSyncIdentifier.c_str(),fPreviousSuspendIdentifier.c_str());
+ if (dberr!=LOCERR_OK)
+ SYSYNC_THROW(TSyncException("DBapi::StartDataRead fatal error",dberr));
+ // we don't need to load the syncset if we are only refreshing from remote
+ // but we also must load it if we can't zap without it on slow refresh
+ if (!fRefreshOnly || (fSlowSync && apiNeedSyncSetToZap()) || implNeedSyncSetToRetrieve()) {
+ SYSYNC_TRY {
+ // read the items
+ #if defined(DBAPI_ASKEYITEMS) && defined(ENGINEINTERFACE_SUPPORT)
+ TMultiFieldItem *mfitemP = NULL;
+ #endif
+ #ifdef DBAPI_TEXTITEMS
+ TDB_Api_Str itemData;
+ #endif
+ do {
+ // read next item
+ int itemstatus;
+ TSyncSetItem *syncSetItemP=NULL;
+ TDB_Api_ItemID itemAndParentID;
+ // two API variants
+ #if defined(DBAPI_ASKEYITEMS) && defined(ENGINEINTERFACE_SUPPORT)
+ if (fPluginDSConfigP->fItemAsKey) {
+ // ALWAYS prepare a multifield item, in case plugin wants to return data (it should not
+ // unless queried with ContextSupport("ReadNextItem:allfields"), but it does not harm
+ // if it still does except wasting memory
+ // Note: check for canCreateItemForRemote() should be always true now as loading syncset has been
+ // moved within the progress of client sync session to a point where types ARE known.
+ // Pre-3.2 engines however called this routine early so types could be unknown here.
+ if (mfitemP==NULL && canCreateItemForRemote()) {
+ mfitemP =
+ (TMultiFieldItem *) newItemForRemote(
+ ity_multifield
+ );
+ }
+ // as key (Note: will be functional key but w/o any fields in case we pass NULL item pointer)
+ TDBItemKey *itemKeyP = newDBItemKey(mfitemP);
+ dberr=fDBApi_Data.ReadNextItemAsKey(itemAndParentID, (KeyH)itemKeyP, itemstatus);
+ // check if plugin wrote something to our key. If so, we assume this is the item and save
+ // it, EVEN IF we did not request getting item data.
+ if (!itemKeyP->isWritten()) {
+ // nothing in this item, forget it
+ delete itemKeyP; // key first
+ if (mfitemP) delete mfitemP; // then item if we had one at all
+ mfitemP=NULL;
+ }
+ else {
+ // got item, delete the key
+ delete itemKeyP;
+ // post-process (run scripts, create BLOB proxies if needed)
+ postReadProcessItem(*mfitemP,0);
+ }
+ }
+ else
+ #endif
+ #ifdef DBAPI_TEXTITEMS
+ {
+ // as text item
+ dberr=fDBApi_Data.ReadNextItem(itemAndParentID, itemData, itemstatus);
+ }
+ #else
+ return LOCERR_WRONGUSAGE; // completely wrong usage - should never happen as compatibility is tested at module connect
+ #endif
+ if (dberr!=LOCERR_OK)
+ SYSYNC_THROW(TSyncException("DBapi::ReadNextItem fatal error",dberr));
+ // check if we have seen all items
+ if (itemstatus==ReadNextItem_EOF)
+ break;
+ // we have received an item
+ // - save returned data as item in the syncsetlist
+ syncSetItemP = new TSyncSetItem;
+ // - copy item object ID
+ syncSetItemP->localid = itemAndParentID.item.c_str();
+ // - copy parent ID
+ // %%% tbd, now empty
+ syncSetItemP->containerid = "";
+ // - set modified status
+ syncSetItemP->isModifiedAfterSuspend = itemstatus==ReadNextItem_Resumed;
+ syncSetItemP->isModified = syncSetItemP->isModifiedAfterSuspend || itemstatus==ReadNextItem_Changed;
+ #ifdef SYDEBUG
+ PDEBUGPRINTFX(DBG_DATA+DBG_EXOTIC,(
+ "read local item info in sync set: localid='%s'%s%s",
+ syncSetItemP->localid.c_str(),
+ syncSetItemP->isModified ? ", MODIFIED since last sync" : "",
+ syncSetItemP->isModifiedAfterSuspend ? " AND since last suspend" : ""
+ ));
+ #endif
+ // no data yet, no item yet
+ syncSetItemP->itemP = NULL;
+ // two API variants
+ #if defined(DBAPI_ASKEYITEMS) && defined(ENGINEINTERFACE_SUPPORT)
+ if (fPluginDSConfigP->fItemAsKey) {
+ // as key
+ if (mfitemP) {
+ // we have read some data
+ syncSetItemP->itemP = mfitemP;
+ mfitemP = NULL; // now owned by syncSetItem
+ syncSetItemP->itemP->setLocalID(itemAndParentID.item.c_str());
+ }
+ }
+ else
+ #endif
+ #ifdef DBAPI_TEXTITEMS
+ {
+ // as text item
+ // - if we have received actual item data already, create and store an item here
+ if (!itemData.empty()) {
+ // store data in new item now
+ // - create new empty TMultiFieldItem
+ syncSetItemP->itemP =
+ (TMultiFieldItem *) newItemForRemote(
+ ity_multifield
+ );
+ // - set localid as we might need it for reading specials or arrays
+ syncSetItemP->itemP->setLocalID(itemAndParentID.item.c_str());
+ // - read data into item
+ parseItemData(*(syncSetItemP->itemP),itemData.c_str(),0);
+ }
+ }
+ #else
+ return LOCERR_WRONGUSAGE; // completely wrong usage - should never happen as compatibility is tested at module connect
+ #endif
+ // now save syncset item
+ fSyncSetList.push_back(syncSetItemP);
+ } while (true);
+ } // try
+ SYSYNC_CATCH (...)
+ dberr=LOCERR_EXCEPTION;
+ SYSYNC_ENDCATCH
+ } // if we need the syncset at all
+ // then end read here
+ if (dberr==LOCERR_OK) {
+ dberr=fDBApi_Data.EndDataRead();
+ if (dberr!=LOCERR_OK) {
+ PDEBUGPRINTFX(DBG_ERROR,("DBapi::EndDataRead failed, err=%hd",dberr));
+ }
+ }
+ return dberr;
+} // TPluginApiDS::apiReadSyncSet
+
+
+
+// Check if we need the syncset to zap
+bool TPluginApiDS::apiNeedSyncSetToZap(void)
+{
+ #ifndef SDK_ONLY_SUPPORT
+ // only handle here if we are in charge - otherwise let ancestor handle it
+ if (!fDBApi_Data.Created()) return inherited::apiNeedSyncSetToZap();
+ #endif
+ // we do not need the sync set to zap it (DBApi has a DeleteAllItems() function)
+ //return false;
+ //%%% NOTE: the DeleteAllItems() is a fake and is not passed to the datastore, and calls
+ // ReadNextItem out of StartDataRead/EndDataRead bracket, and is not AsKey aware
+ // so we DO NOT USE IT.
+ return true; // instead, we need the sync set to zap using generic zapSyncSet()
+} // TPluginApiDS::apiNeedSyncSetToZap
+
+
+// Zap all data in syncset (note that everything outside the sync set will remain intact)
+localstatus TPluginApiDS::apiZapSyncSet(void)
+{
+ #ifndef SDK_ONLY_SUPPORT
+ // only handle here if we are in charge - otherwise let ancestor handle it
+ if (!fDBApi_Data.Created()) return inherited::apiZapSyncSet();
+ #endif
+
+ // try to use plugin's specialized implementation
+ TSyError dberr = fDBApi_Data.DeleteSyncSet();
+ // do it one by one if DeleteAllItems() is not implemented
+ if (dberr==LOCERR_NOTIMP) {
+ dberr = zapSyncSet();
+ }
+ // return status
+ return dberr;
+} // TPluginApiDS::apiZapSyncSet
+
+
+// fetch actual record from DB by localID. SyncSetItem might be passed to give additional information
+// such as containerid
+localstatus TPluginApiDS::apiFetchItem(TMultiFieldItem &aItem, bool aReadPhase, TSyncSetItem *aSyncSetItemP)
+{
+ #ifndef SDK_ONLY_SUPPORT
+ // only handle here if we are in charge - otherwise let ancestor handle it
+ if (!fDBApi_Data.Created()) return inherited::apiFetchItem(aItem, aReadPhase, aSyncSetItemP);
+ #endif
+
+ TSyError dberr=LOCERR_OK;
+ ItemID_Struct itemAndParentID;
+
+ // set up item ID and parent ID
+ itemAndParentID.item=(appCharP)aItem.getLocalID();
+ itemAndParentID.parent=const_cast<char *>("");
+
+ // two API variants
+ #if defined(DBAPI_ASKEYITEMS) && defined(ENGINEINTERFACE_SUPPORT)
+ if (fPluginDSConfigP->fItemAsKey) {
+ // get key
+ TDBItemKey *itemKeyP = newDBItemKey(&aItem);
+ // let plugin use it to fill item
+ dberr=fDBApi_Data.ReadItemAsKey(itemAndParentID,(KeyH)itemKeyP);
+ if (itemKeyP->isWritten()) {
+ // post-process (run scripts, create BLOB proxies if needed)
+ postReadProcessItem(aItem,0);
+ }
+ // done with the key
+ delete itemKeyP;
+ }
+ else
+ #endif
+ #ifdef DBAPI_TEXTITEMS
+ {
+ TDB_Api_Str itemData;
+ // read the item in text form from the DB
+ dberr=fDBApi_Data.ReadItem(itemAndParentID,itemData);
+ if (dberr==LOCERR_OK) {
+ // put it into aItem
+ parseItemData(aItem,itemData.c_str(),0);
+ }
+ }
+ #else
+ return LOCERR_WRONGUSAGE; // completely wrong usage - should never happen as compatibility is tested at module connect
+ #endif
+ // return status
+ return dberr;
+} // TPluginApiDS::apiFetchItem
+
+
+
+// start of write
+localstatus TPluginApiDS::apiStartDataWrite(void)
+{
+ #ifndef SDK_ONLY_SUPPORT
+ // only handle here if we are in charge - otherwise let ancestor handle it
+ if (!fDBApi_Data.Created()) return inherited::apiStartDataWrite();
+ #endif
+
+ TSyError dberr=fDBApi_Data.StartDataWrite();
+ if (dberr!=LOCERR_OK) {
+ PDEBUGPRINTFX(DBG_ERROR,("DBapi::StartDataWrite returns dberr=%hd",dberr));
+ }
+ return dberr;
+} // TPluginApiDS::apiStartDataWrite
+
+
+
+// add new item to datastore, returns created localID
+localstatus TPluginApiDS::apiAddItem(TMultiFieldItem &aItem, string &aLocalID)
+{
+ #ifndef SDK_ONLY_SUPPORT
+ // only handle here if we are in charge - otherwise let ancestor handle it
+ if (!fDBApi_Data.Created()) return inherited::apiAddItem(aItem, aLocalID);
+ #endif
+
+ TSyError dberr=LOCERR_OK;
+ TDB_Api_ItemID itemAndParentID;
+
+ // prepare data from item
+ #ifdef SCRIPT_SUPPORT
+ fInserting=true; // flag for script, we are inserting new record
+ #endif
+ // two API variants
+ #if defined(DBAPI_ASKEYITEMS) && defined(ENGINEINTERFACE_SUPPORT)
+ if (fPluginDSConfigP->fItemAsKey) {
+ // preprocess
+ if (!preWriteProcessItem(aItem)) return 510; // DB error
+ // get key
+ TDBItemKey *itemKeyP = newDBItemKey(&aItem);
+ // let plugin use it to obtain data to write
+ dberr=fDBApi_Data.InsertItemAsKey((KeyH)itemKeyP,"",itemAndParentID);
+ // done with the key
+ delete itemKeyP;
+ }
+ else
+ #endif
+ #ifdef DBAPI_TEXTITEMS
+ {
+ string itemData;
+ generateItemData(
+ false, // all fields, not only assigned ones
+ aItem,
+ 0, // we do not use different sets for now
+ itemData // here we'll get the data
+ );
+ // now insert main record
+ dberr=fDBApi_Data.InsertItem(itemData.c_str(),"",itemAndParentID);
+ }
+ #else
+ return LOCERR_WRONGUSAGE; // completely wrong usage - should never happen as compatibility is tested at module connect
+ #endif
+ // now check result
+ if (dberr==LOCERR_OK) {
+ // save new ID
+ aLocalID = itemAndParentID.item.c_str();
+ aItem.setLocalID(aLocalID.c_str()); // make sure item itself has correct ID as well
+ // now write all the BLOBs
+ writeBlobs(false,aItem,0);
+ #ifdef SCRIPT_SUPPORT
+ // process overall afterwrite script
+ fWriting=true;
+ fInserting=true;
+ fDeleting=false;
+ fPluginAgentP->fScriptContextDatastore=this;
+ if (!TScriptContext::execute(fScriptContextP,fPluginDSConfigP->fFieldMappings.fAfterWriteScript,fPluginDSConfigP->getDSFuncTableP(),fPluginAgentP,&aItem,true))
+ SYSYNC_THROW(TSyncException("<afterwritescript> failed"));
+ #endif
+ }
+ // return status
+ return dberr;
+} // TPluginApiDS::apiAddItem
+
+
+#ifdef SYSYNC_CLIENT
+
+/// finalize local ID (for datastores that can't efficiently produce these at insert)
+bool TPluginApiDS::dsFinalizeLocalID(string &aLocalID)
+{
+ #ifndef SDK_ONLY_SUPPORT
+ // only handle here if we are in charge - otherwise let ancestor handle it
+ if (!fDBApi_Data.Created()) return inherited::dsFinalizeLocalID(aLocalID);
+ #endif
+
+ TDB_Api_Str finalizedID;
+ localstatus sta = fDBApi_Data.FinalizeLocalID(aLocalID.c_str(),finalizedID);
+ if (sta==LOCERR_OK && !finalizedID.empty()) {
+ // pass modified ID back
+ aLocalID = finalizedID.c_str();
+ // ID was updated
+ return true;
+ }
+ // no change - ID is ok as-is
+ return false;
+
+ /*
+ // hacky implementation for now, as this routine is not yet in the DBApi.
+ // But as we need it for iPhone only so far, and iPhone only allows statically linked DB plugins, we can call
+ // it directly
+ #ifdef IPHONE_PLUGINS_STATIC
+ #warning "Ugly hack here"
+ char *finalizedID = NULL;
+ #ifdef HARDCODED_CONTACTS
+ if (fPluginDSConfigP->fLocalDBTypeID == 1001) {
+ sta = iPhone_addressbook::FinalizeLocalID(fDBApi_Data.fContext, aLocalID.c_str(), &finalizedID);
+ }
+ else
+ #endif // HARDCODED_CONTACTS
+ #ifdef HARDCODED_CALENDAR
+ if (fPluginDSConfigP->fLocalDBTypeID == 1002) {
+ sta = iPhone_calendar::FinalizeLocalID(fDBApi_Data.fContext, aLocalID.c_str(), &finalizedID);
+ }
+ else
+ #endif // HARDCODED_CALENDAR
+ {
+ return false; // no know datastore, no finalisation
+ }
+ // now get back translated ID
+ if (sta==LOCERR_OK && finalizedID) {
+ aLocalID = finalizedID;
+ free(finalizedID); // %%% should be StrDispose
+ return true; // final ID is different from temp one
+ }
+ #endif // IPHONE_PLUGINS_STATIC
+
+ // no change - ID is ok as-is
+ return false;
+ */
+} // TPluginApiDS::dsFinalizeLocalID
+
+#endif // SYSYNC_CLIENT
+
+
+
+// update existing item in datastore, returns 404 if item not found
+localstatus TPluginApiDS::apiUpdateItem(TMultiFieldItem &aItem)
+{
+ #ifndef SDK_ONLY_SUPPORT
+ // only handle here if we are in charge - otherwise let ancestor handle it
+ if (!fDBApi_Data.Created()) return inherited::apiUpdateItem(aItem);
+ #endif
+
+ TSyError dberr=LOCERR_OK;
+ TDB_Api_ItemID updItemAndParentID;
+ ItemID_Struct itemAndParentID;
+
+ // set up item ID and parent ID
+ itemAndParentID.item=(appCharP)aItem.getLocalID();
+ itemAndParentID.parent=const_cast<char *>("");
+
+ // prepare data from item
+ #ifdef SCRIPT_SUPPORT
+ fInserting=false; // flag for script, we are updating, not inserting now
+ #endif
+ // two API variants
+ #if defined(DBAPI_ASKEYITEMS) && defined(ENGINEINTERFACE_SUPPORT)
+ if (fPluginDSConfigP->fItemAsKey) {
+ // preprocess
+ if (!preWriteProcessItem(aItem)) return 510; // DB error
+ // get key
+ TDBItemKey *itemKeyP = newDBItemKey(&aItem);
+ // let plugin use it to obtain data to write
+ dberr=fDBApi_Data.UpdateItemAsKey((KeyH)itemKeyP,itemAndParentID,updItemAndParentID);
+ // done with the key
+ delete itemKeyP;
+ }
+ else
+ #endif
+ #ifdef DBAPI_TEXTITEMS
+ {
+ string itemData;
+ generateItemData(
+ true, // only assigned fields
+ aItem,
+ 0, // we do not use different sets for now
+ itemData // here we'll get the data
+ );
+ // now update main record
+ dberr=fDBApi_Data.UpdateItem(itemData.c_str(),itemAndParentID,updItemAndParentID);
+ }
+ #else
+ return LOCERR_WRONGUSAGE; // completely wrong usage - should never happen as compatibility is tested at module connect
+ #endif
+ if (dberr==LOCERR_OK) {
+ // check if ID has changed
+ if (!updItemAndParentID.item.empty() && strcmp(updItemAndParentID.item.c_str(),aItem.getLocalID())!=0) {
+ #ifndef SYSYNC_CLIENT
+ // update item ID and Map
+ dsLocalIdHasChanged(aItem.getLocalID(),updItemAndParentID.item.c_str());
+ #endif
+ // - update in this item we have here as well
+ aItem.setLocalID(updItemAndParentID.item.c_str());
+ aItem.updateLocalIDDependencies();
+ }
+ // now write all the BLOBs
+ writeBlobs(true,aItem,0);
+ #ifdef SCRIPT_SUPPORT
+ // process overall afterwrite script
+ fWriting=true;
+ fInserting=false;
+ fDeleting=false;
+ fPluginAgentP->fScriptContextDatastore=this;
+ if (!TScriptContext::execute(fScriptContextP,fPluginDSConfigP->fFieldMappings.fAfterWriteScript,fPluginDSConfigP->getDSFuncTableP(),fPluginAgentP,&aItem,true))
+ SYSYNC_THROW(TSyncException("<afterwritescript> failed"));
+ #endif
+ }
+ // return status
+ return dberr;
+} // TPluginApiDS::apiUpdateItem
+
+
+// delete existing item in datastore, returns 211 if not existing any more
+localstatus TPluginApiDS::apiDeleteItem(TMultiFieldItem &aItem)
+{
+ #ifndef SDK_ONLY_SUPPORT
+ // only handle here if we are in charge - otherwise let ancestor handle it
+ if (!fDBApi_Data.Created()) return inherited::apiDeleteItem(aItem);
+ #endif
+
+ TSyError dberr=LOCERR_OK;
+
+ // delete item
+ dberr=fDBApi_Data.DeleteItem( aItem.getLocalID() );
+ if (dberr==LOCERR_OK) {
+ deleteBlobs(true,aItem,0); // Item related blobs must be removed as well
+ #ifdef SCRIPT_SUPPORT
+ // process overall afterwrite script
+ fWriting=true;
+ fInserting=false;
+ fDeleting=true;
+ fPluginAgentP->fScriptContextDatastore=this;
+ if (!TScriptContext::execute(fScriptContextP,fPluginDSConfigP->fFieldMappings.fAfterWriteScript,fPluginDSConfigP->getDSFuncTableP(),fPluginAgentP,&aItem,true))
+ SYSYNC_THROW(TSyncException("<afterwritescript> failed"));
+ #endif
+ } // if
+
+ // return status
+ return dberr;
+} // TPluginApiDS::apiDeleteItem
+
+
+
+
+// - end DB data write sequence (but not yet admin data), returns DB-specific identifier for this sync (if any)
+localstatus TPluginApiDS::apiEndDataWrite(string &aThisSyncIdentifier)
+{
+ #ifndef SDK_ONLY_SUPPORT
+ // only handle here if we are in charge - otherwise let ancestor handle it
+ if (!fDBApi_Data.Created()) return inherited::apiEndDataWrite(aThisSyncIdentifier);
+ #endif
+
+ // nothing special to do in ODBC case, as we do not have a separate sync identifier
+ TDB_Api_Str newSyncIdentifier;
+ bool ok=fDBApi_Data.EndDataWrite(true, newSyncIdentifier)==LOCERR_OK;
+ aThisSyncIdentifier=newSyncIdentifier.c_str();
+ return ok;
+} // TPluginApiDS::apiEndDataWrite
+
+
+
+// must be called before starting a thread. If returns false, starting a thread now
+// is not allowed and must be postponed.
+// Includes ThreadMayChange() call
+bool TPluginApiDS::startingThread(void)
+{
+ // %%% tbd: if modules are completely independent, we might not need this in all cases
+ if (!dbAccessLocked()) {
+ static_cast<TPluginApiAgent *>(fSessionP)->fApiLocked=true;
+ // Now post possible thread change to the API
+ // - on the database level
+ ThreadMayChangeNow();
+ // - on the session level as well (to make sure, and because we will post a change back
+ // at the end of the thread, which will be called FROM the new thread, which constitutes
+ // executing something in the session context.
+ static_cast<TPluginApiAgent *>(fSessionP)->getDBApiSession()->ThreadMayChangeNow();
+ return true;
+ }
+ else
+ return false;
+} // TPluginApiDS::startingThread
+
+
+// - must be called when a thread's activity has ended
+// BUT THE CALL MUST BE FROM THE ENDING THREAD, not the main thread!
+void TPluginApiDS::endingThread(void) {
+ // thread may change for the API now again
+ // - on the database level
+ ThreadMayChangeNow();
+ // - on the session level as well
+ static_cast<TPluginApiAgent *>(fSessionP)->getDBApiSession()->ThreadMayChangeNow();
+ // Now other threads are allowed to access the API again
+ static_cast<TPluginApiAgent *>(fSessionP)->fApiLocked=false;
+} // TPluginApiDS::endingThread
+
+
+// should be called before doing DB accesses that might be locked (e.g. because another thread is using the DB resources)
+bool TPluginApiDS::dbAccessLocked(void)
+{
+ return static_cast<TPluginApiAgent *>(fSessionP)->fApiLocked;
+} // TPluginApiDS::dbAccessLocked
+
+
+// - alert possible thread change to plugins
+// Does not check if API is locked or not, see dsThreadMayChangeNow()
+void TPluginApiDS::ThreadMayChangeNow(void)
+{
+ // let API know, thread might change for next request (but not necessarily does!)
+ if (fDBApi_Data.Created()) fDBApi_Data.ThreadMayChangeNow();
+ if (fDBApi_Admin.Created()) fDBApi_Admin.ThreadMayChangeNow();
+} // TPluginApiDS::ThreadMayChangeNow
+
+
+// - engine Thread might change
+void TPluginApiDS::dsThreadMayChangeNow(void)
+{
+ // Do not post thread change infos when DB access is locked.
+ // If it is locked, the thread that locked it will call a
+ // ThreadMayChangeNow() to the API when the thread terminates (in endingThread()).
+ if (!dbAccessLocked()) {
+ ThreadMayChangeNow();
+ }
+ // let ancestor do it's own stuff
+ inherited::dsThreadMayChangeNow();
+} // TPluginApiDS::dsThreadMayChangeNow
+
+
+
+// - connect data handling part of plugin, Returns LOCERR_NOTIMPL when no data plugin is selected
+// Note: this is either called as part of apiLoadAdminData (even if plugin is NOT responsible for data!)
+// or directly before startDataRead (in BASED_ON_BINFILE_CLIENT case)
+TSyError TPluginApiDS::connectDataPlugin(void)
+{
+ TSyError err = LOCERR_NOTIMP;
+ // only connect if we have plugin data support
+ if (fPluginDSConfigP->fDBApiConfig_Data.Connected()) {
+ err = LOCERR_OK;
+ DB_Callback cb= &fDBApi_Data.fCB.Callback;
+ cb->callbackRef = fSessionP; // the session
+ #ifdef ENGINEINTERFACE_SUPPORT
+ cb->thisBase = fPluginDSConfigP->getSyncAppBase()->fEngineInterfaceP;
+ #endif
+ #ifdef SYDEBUG
+ // Datastore Data access debug goes to session log
+ cb->debugFlags = PDEBUGTEST(DBG_DATA+DBG_DBAPI+DBG_PLUGIN) ? 0xFFFF : 0;
+ cb->DB_DebugPuts = SessionLogDebugPuts;
+ cb->DB_DebugBlock = SessionLogDebugBlock;
+ cb->DB_DebugEndBlock = SessionLogDebugEndBlock;
+ cb->DB_DebugEndThread = SessionLogDebugEndThread;
+ cb->DB_DebugExotic = SessionLogDebugExotic;
+ #endif // SYDEBUG
+ #ifdef ENGINEINTERFACE_SUPPORT
+ // Data module can use Get/SetValue for "AsKey" routines and for session script var access
+ // Note: these are essentially context free and work without a global call-in structure
+ // (which is not necessarily there, for example in no-library case)
+ CB_Connect_KeyAccess(cb); // connect generic key access routines
+ // Version of OpenSessionKey that implicitly opens a key for the current session (DB plugins
+ // do not have a session handle, as their use is always implicitly in a session context).
+ cb->ui.OpenSessionKey = SessionOpenSessionKey;
+ #endif // ENGINEINTERFACE_SUPPORT
+ }
+ return err;
+} // connectDataPlugin
+
+
+#ifndef BASED_ON_BINFILE_CLIENT
+
+/// @brief save admin data
+/// Must save the following items:
+/// - fRemoteSyncAnchor = anchor string used by remote party for this session
+/// - fThisLocalAnchor = anchor (beginning of session) timestamp for this sync
+/// - fThisSync = timestamp for this sync (same as fThisLocalAnchor unless fSyncTimeStampAtEnd config is set)
+/// - fLastToRemoteLocalAnchor = timestamp for anchor (beginning of session) of last session that sent data to remote
+/// (same as fThisLocalAnchor unless we did a refrehs-from-remote session)
+/// - fLastToRemoteSync = timestamp for last session that sent data to remote
+/// (same as fThisSync unless we did a refresh-from-remote session)
+/// - fLastToRemoteSyncIdentifier = string identifying last session that sent data to remote (needs only be saved
+/// if derived datastore cannot work with timestamps and has its own identifier).
+/// - fMapTable = list<TMapEntry> containing map entries. For each entry the implementation must:
+/// - if changed==false: the entry hasn't been changed, so no DB operation is required
+/// - if changed==true and remoteid is not empty:
+/// - if added==true, add the entry as a new record to the DB
+/// - if added==false, update the entry in the DB with matching localid
+/// - if changed==true and remoteid is empty and added==false: delete the entry(s) in the DB with matching localid
+/// For resumable datastores (fConfigP->fResumeSupport==true):
+/// - fMapTable = In addition to the above, the markforresume flag must be saved in the mapflags
+// when it is not equal to the savedmark flag - independently of added/deleted/changed.
+/// - fResumeAlertCode = alert code of current suspend state, 0 if none
+/// - fPreviousSuspendCmpRef = reference time of last suspend (used to detect items modified during a suspend / resume)
+/// - fPreviousSuspendIdentifier = identifier of last suspend (used to detect items modified during a suspend / resume)
+/// (needs only be saved if derived datastore cannot work with timestamps and has
+/// its own identifier)
+///
+/// For datastores that can resume in middle of a chunked item (fConfigP->fResumeItemSupport==true):
+/// - fPartialItemState = state of partial item (TPartialItemState enum):
+/// - if pi_state_none: save params, delete BLOB data (empty data)
+/// - if pi_state_save_incoming: save params+BLOB, save as in DB such that we will get pi_state_loaded_incoming when loaded again
+/// - if pi_state_save_outgoing: save params+BLOB, save as in DB such that we will get pi_state_loaded_outgoing when loaded again
+/// - if pi_state_loaded_incoming: no need to save, as params+BLOB have not changed since last save (but currently saved params+BLOB in DB must be retained)
+/// - if pi_state_loaded_outgoing: no need to save, as params+BLOB have not changed since last save (but currently saved params+BLOB in DB must be retained)
+///
+/// - fLastItemStatus = status code (TSyError) of last item
+/// - fLastSourceURI = item ID (string, if limited in length should be long enough for large IDs, >=64 chars recommended)
+/// - fLastTargetURI = item ID (string, if limited in length should be long enough for large IDs, >=64 chars recommended)
+/// - fPITotalSize = uInt32, total item size
+/// - fPIUnconfirmedSize= uInt32, unconfirmed part of item size
+/// - fPIStoredSize = uInt32, size of BLOB to store, 0=none
+/// - fPIStoredDataP = void *, BLOB data, NULL if none
+///
+/// @param aDataCommitted[in] indicates if data has been committed to the database already or not
+/// @param aSessionFinished[in] indicates if this is a final, end-of-session admin save (otherwise, it's only a resume state save)
+localstatus TPluginApiDS::apiSaveAdminData(bool aDataCommitted, bool aSessionFinished)
+{
+ // security - don't use API when locked
+ if (dbAccessLocked()) return 503; // service unavailable
+
+ const char* PIStored = "PIStored"; // blob name field
+
+ #ifndef SDK_ONLY_SUPPORT
+ // only handle here if we are in charge - otherwise let ancestor handle it
+ if (!fDBApi_Admin.Created()) return inherited::apiSaveAdminData(aDataCommitted, aSessionFinished);
+ #endif
+
+ localstatus sta=LOCERR_OK;
+ TMapContainer::iterator pos;
+
+ // save the entire map list differentially
+ pos=fMapTable.begin();
+ PDEBUGPRINTFX(DBG_ADMIN+DBG_EXOTIC,("apiSaveAdminData: internal map table has %ld entries (normal and others)",fMapTable.size()));
+ while (pos!=fMapTable.end()) {
+ DEBUGPRINTFX(DBG_ADMIN+DBG_EXOTIC,(
+ "apiSaveAdminData: entryType=%s, localid='%s', remoteID='%s', mapflags=0x%lX, changed=%d, deleted=%d, added=%d, markforresume=%d, savedmark=%d",
+ MapEntryTypeNames[(*pos).entrytype],
+ (*pos).localid.c_str(),
+ (*pos).remoteid.c_str(),
+ (*pos).mapflags,
+ (int)(*pos).changed,
+ (int)(*pos).deleted,
+ (int)(*pos).added,
+ (int)(*pos).markforresume,
+ (int)(*pos).savedmark
+ ));
+ // check if item has changed since map table was read, or if its markforresume has changed
+ // or if this is a successful end of a session, when we can safely assume that any pending maps
+ // are from adds to the client that have never reached the client (otherwise, we'd have got
+ // a map for it, even if the add was in a previous session or session attempt)
+ if (
+ (*pos).changed || (*pos).added || (*pos).deleted || // update of DB needed
+ ((*pos).markforresume!=(*pos).savedmark) // mark for resume changed
+ ) {
+ // make sure it does not get written again if not really modified again
+ (*pos).changed=false;
+ // update new mapflags w/o changing mapflag_useforresume in the actual flags (as we still need it while session goes on)
+ uInt32 newmapflags = (*pos).mapflags & ~mapflag_useforresume;
+ if ((*pos).markforresume)
+ newmapflags |= mapflag_useforresume;
+ // remember last saved state
+ (*pos).savedmark=(*pos).markforresume;
+ // do something!
+ MapID_Struct mapid;
+ mapid.ident=(int)(*pos).entrytype;
+ mapid.localID=(char *)((*pos).localid.c_str());
+ mapid.remoteID=(char *)((*pos).remoteid.c_str());
+ mapid.flags=newmapflags;
+ if ((*pos).deleted) {
+ if (!(*pos).added) {
+ // delete this entry (only needed if it was not also added since last save - otherwise, map entry was never saved to the DB yet)
+ sta=fDBApi_Admin.DeleteMapItem(&mapid);
+ if (sta!=LOCERR_OK) break;
+ }
+ // now remove it from the list, such that we don't try to delete it again
+ TMapContainer::iterator delpos=pos++; // that's the next to have a look at
+ fMapTable.erase(delpos); // remove it now
+ continue; // pos is already updated
+ } // deleted
+ else if ((*pos).added) {
+ // add a new entry
+ sta=fDBApi_Admin.InsertMapItem(&mapid);
+ if (sta!=LOCERR_OK) break;
+ // is now added, don't add again later
+ (*pos).added=false;
+ }
+ else {
+ // explicitly changed or needs update because of resume mark or pendingmap flag
+ // change existing entry
+ sta=fDBApi_Admin.UpdateMapItem(&mapid);
+ if (sta!=LOCERR_OK) break;
+ }
+ } // if something changed
+ // anyway - reset mark for resume, it must be reconstructed before next save
+ (*pos).markforresume=false;
+ // next
+ pos++;
+ } // while
+ if (sta!=LOCERR_OK) return sta;
+ // collect admin data in a string
+ string adminData,s;
+ adminData.erase();
+ // add remote sync anchor
+ adminData+="remotesyncanchor:";
+ StrToCStrAppend(fLastRemoteAnchor.c_str(),adminData,true); // allow 8-bit chars to be represented as-is (no \xXX escape needed)
+ /* not needed any more
+ // add local anchor
+ adminData+="\r\nlastlocalanchor:";
+ timeStampToISO8601(fThisLocalAnchor,s,true,true);
+ adminData+=s.c_str();
+ */
+ // add last sync time
+ adminData+="\r\nlastsync:";
+ TimestampToISO8601Str(s, fPreviousSyncTime, TCTX_UTC, false, false);
+ adminData+=s.c_str();
+ /* not needed any more
+ // add local anchor of last sync with sending data to remote
+ adminData+="\r\nlasttoremotelocalanchor:";
+ timeStampToISO8601(fLastToRemoteLocalAnchor,s,true,true);
+ adminData+=s.c_str();
+ */
+ // add last to remote sync time
+ adminData+="\r\nlasttoremotesync:";
+ TimestampToISO8601Str(s, fPreviousToRemoteSyncCmpRef, TCTX_UTC, false, false);
+ adminData+=s.c_str();
+ // add identifier needed by datastore to identify records changes since last to-remote-sync
+ if (fPluginDSConfigP->fStoreSyncIdentifiers) {
+ adminData+="\r\nlasttoremotesyncid:";
+ StrToCStrAppend(fPreviousToRemoteSyncIdentifier.c_str(),adminData,true); // allow 8-bit chars to be represented as-is (no \xXX escape needed)
+ }
+ // add resume alert code
+ adminData+="\r\nresumealertcode:"; StringObjAppendPrintf(adminData,"%hd",fResumeAlertCode);
+ // add last suspend time
+ adminData+="\r\nlastsuspend:";
+ TimestampToISO8601Str(s, fPreviousSuspendCmpRef, TCTX_UTC, false, false);
+ adminData+=s.c_str();
+ // add identifier needed by datastore to identify records changes since last suspend
+ if (fPluginDSConfigP->fStoreSyncIdentifiers) {
+ adminData+="\r\nlastsuspendid:";
+ StrToCStrAppend(fPreviousSuspendIdentifier.c_str(),adminData,true); // allow 8-bit chars to be represented as-is (no \xXX escape needed)
+ }
+
+ /// For datastores that can resume in middle of a chunked item (fConfigP->fResumeItemSupport==true):
+ void* blPtr = fPIStoredDataP; // position
+ ulong blSize= fPIStoredSize; // actualbytes
+
+ if (dsResumeChunkedSupportedInDB()) {
+ /// - fPartialItemState = state of partial item (TPartialItemState enum):
+ /// - if pi_state_none: save params, delete BLOB data (empty data)
+ /// - if pi_state_save_incoming: save params+BLOB, save as in DB such that we will get pi_state_loaded_incoming when loaded again
+ /// - if pi_state_save_outgoing: save params+BLOB, save as in DB such that we will get pi_state_loaded_outgoing when loaded again
+ /// - if pi_state_loaded_incoming or
+ /// pi_state_loaded_outgoing: no need to save, as params+BLOB have not changed since last save
+ /// (but currently saved params+BLOB in DB must be retained)
+ if (
+ fPartialItemState!=pi_state_loaded_incoming &&
+ fPartialItemState!=pi_state_loaded_outgoing
+ ) {
+ // and create the new status for these cases
+ TPartialItemState pp= fPartialItemState;
+ if (pp==pi_state_save_incoming) pp= pi_state_loaded_incoming; // adapt them before
+ if (pp==pi_state_save_outgoing) pp= pi_state_loaded_outgoing;
+ adminData+="\r\npartialitemstate:"; StringObjAppendPrintf( adminData,"%hd",pp );
+ // - fLastItemStatus = status code (TSyError) of last item
+ adminData+="\r\nlastitemstatus:"; StringObjAppendPrintf( adminData,"%hd",fLastItemStatus );
+ // - fLastSourceURI = item ID (string, if limited in length should be long enough for large IDs, >=64 chars recommended)
+ adminData+="\r\nlastsourceURI:"; StrToCStrAppend( fLastSourceURI.c_str(), adminData,true );
+ // - fLastTargetURI = item ID (string, if limited in length should be long enough for large IDs, >=64 chars recommended)
+ adminData+="\r\nlasttargetURI:"; StrToCStrAppend( fLastTargetURI.c_str(), adminData,true );
+ // - fPITotalSize = uInt32, total item size
+ adminData+="\r\ntotalsize:"; StringObjAppendPrintf( adminData,"%hd", fPITotalSize );
+ // - fPIUnconfirmedSize= uInt32, unconfirmed part of item size
+ adminData+="\r\nunconfirmedsize:"; StringObjAppendPrintf( adminData,"%hd", fPIUnconfirmedSize );
+ // - fPIStoredSize = uInt32, size of BLOB to store, store it as well to make ReadBlob easier (mallloc)
+ adminData+="\r\nstoredsize:"; StringObjAppendPrintf( adminData,"%hd", blSize );
+ // - fPIStoredSize = uInt32, size of BLOB to store, 0=none
+ // - fPIStoredDataP = void *, BLOB data, NULL if none
+ adminData+="\r\nstored;BLOBID="; adminData+= PIStored;
+ } // if
+ } // if
+ // CRLF at end
+ adminData+="\r\n";
+ // save admin data
+ sta= fDBApi_Admin.SaveAdminData(adminData.c_str()); if (sta) return sta;
+ // now write all the BLOBs, currently there is only the PIStored object of ResumeChunkedSupport
+ if (dsResumeChunkedSupportedInDB()) {
+ TPartialItemState pis= fPartialItemState;
+ if (
+ blSize==0 &&
+ (pis==pi_state_save_incoming || pis==pi_state_save_outgoing)
+ )
+ pis= pi_state_none; // delete blob, if size==0
+ // handle BLOB
+ switch (pis) {
+ // make sure BLOB is deleted when it is empty
+ case pi_state_none:
+ sta =
+ fDBApi_Admin.DeleteBlob(
+ "", // aItem.getLocalID()
+ PIStored // blobfieldname.c_str()
+ );
+ if (sta==DB_NotFound)
+ sta= LOCERR_OK; // no error, if not existing
+ break;
+ // save BLOB contents
+ case pi_state_save_incoming: // Write the whole BLOB at once
+ case pi_state_save_outgoing:
+ sta =
+ fDBApi_Admin.WriteBlob (
+ "", // aItem.getLocalID()
+ PIStored, // blobfieldname.c_str()
+ blPtr, // bufferP
+ blSize, // actualbytes
+ blSize, // blobsize
+ true, // first
+ true // last
+ );
+ break;
+ case pi_state_loaded_incoming:
+ case pi_state_loaded_outgoing:
+ // do nothing, as the blob is saved already
+ break;
+ } // switch
+ } // if
+ return sta;
+} // TPluginApiDS::apiSaveAdminData
+
+
+/// @brief Load admin data from Plugin-implemented database
+/// Must search for existing target record matching the triple (aDeviceID,aDatabaseID,aRemoteDBID)
+/// - if there is a matching record: load it
+/// - if there is no matching record, set fFirstTimeSync=true. The implementation may already create a
+/// new record with the key (aDeviceID,aDatabaseID,aRemoteDBID) and initialize it with the data from
+/// the items as shown below. At least, fTargetKey must be set to a value that will allow apiSaveAdminData to
+/// update the record. In case implementation chooses not create the record only in apiSaveAdminData, it must
+/// buffer the triple (aDeviceID,aDatabaseID,aRemoteDBID) such that it is available at apiSaveAdminData.
+/// If a record exists implementation must load the following items:
+/// - fTargetKey = some key value that can be used to re-identify the target record later at SaveAdminData.
+/// If the database implementation has other means to re-identify the target, this can be
+/// left unassigned.
+/// - fLastRemoteAnchor = anchor string used by remote party for last session (and saved to DB then)
+/// - fPreviousSyncTime = anchor (beginning of session) timestamp of last session.
+/// - fPreviousToRemoteSyncCmpRef = Reference time to determine items modified since last time sending data to remote
+/// - fPreviousToRemoteSyncIdentifier = string identifying last session that sent data to remote (needs only be saved
+/// if derived datastore cannot work with timestamps and has its own identifier).
+/// - fMapTable = list<TMapEntry> containing map entries. The implementation must load all map entries
+/// related to the current sync target identified by the triple of (aDeviceID,aDatabaseID,aRemoteDBID)
+/// or by fTargetKey. The entries added to fMapTable must have "changed", "added" and "deleted" flags
+/// set to false.
+/// For resumable datastores (fConfigP->fResumeSupport==true):
+/// - fMapTable = In addition to the above, the markforresume flag must be saved in the mapflags
+// when it is not equal to the savedmark flag - independently of added/deleted/changed.
+/// - fResumeAlertCode = alert code of current suspend state, 0 if none
+/// - fPreviousSuspendCmpRef = reference time of last suspend (used to detect items modified during a suspend / resume)
+/// - fPreviousSuspendIdentifier = identifier of last suspend (used to detect items modified during a suspend / resume)
+/// (needs only be saved if derived datastore cannot work with timestamps and has
+/// its own identifier)
+/// - fPendingAddMaps = map<string,string>. The implementation must load all all pending maps (client only) into
+/// fPendingAddMaps (and fUnconfirmedMaps must be left empty).
+/// - fTempGUIDMap = map<string,string>. The implementation must save all entries as temporary LUID to GUID mappings
+/// (server only)
+///
+/// For datastores that can resume in middle of a chunked item (fConfigP->fResumeItemSupport==true):
+/// - fPartialItemState = state of partial item (TPartialItemState enum):
+/// - after load, value must always be pi_state_none, pi_state_loaded_incoming or pi_state_loaded_outgoing.
+/// - pi_state_save_xxx MUST NOT be set after loading (see apiSaveAdminData comments)
+/// - fLastItemStatus = status code (TSyError) of last item
+/// - fLastSourceURI = item ID (string, if limited in length should be long enough for large IDs, >=64 chars recommended)
+/// - fLastTargetURI = item ID (string, if limited in length should be long enough for large IDs, >=64 chars recommended)
+/// - fPITotalSize = uInt32, total item size
+/// - fPIUnconfirmedSize= uInt32, unconfirmed part of item size
+/// - fPIStoredSize = uInt32, size of BLOB, 0=none
+/// - fPIStoredDataP = void *, BLOB data.
+/// - If this is not NULL on entry AND fPIStoredDataAllocated is set,
+/// the current block must be freed using smlLibFree() (and NOT JUST free()!!).
+/// - If no BLOB is loaded, this must be set to NULL
+/// - If a BLOB is loaded, an appropriate memory block should be allocated for it
+/// using smlLibMalloc() (and NOT JUST malloc()!!)
+/// - fPIStoredDataAllocated: MUST BE SET to true when a memory block was allocated into fPIStoredDataP.
+/// @param aDeviceID[in] remote device URI (device ID)
+/// @param aDatabaseID[in] local database ID
+/// @param aRemoteDBID[in] database ID of remote device
+localstatus TPluginApiDS::apiLoadAdminData(
+ const char *aDeviceID,
+ const char *aDatabaseID,
+ const char *aRemoteDBID
+)
+{
+ // security - don't use API when locked
+ if (dbAccessLocked()) return 503; // service unavailable
+
+ const char* PIStored = "PIStored"; // blob name field
+
+ TSyError err= LOCERR_OK;
+
+ // In any case - this is the time to create the contexts for the datastore
+ // - admin if selected
+ if (fPluginDSConfigP->fDBApiConfig_Admin.Connected()) {
+ DB_Callback cb= &fDBApi_Admin.fCB.Callback;
+ cb->callbackRef = fSessionP; // the session
+ #ifdef ENGINEINTERFACE_SUPPORT
+ cb->thisBase = getSyncAppBase()->fEngineInterfaceP;
+ #endif
+ #ifdef SYDEBUG
+ // Datastore Admin debug goes to session log
+ cb->debugFlags = PDEBUGTEST(DBG_ADMIN+DBG_DBAPI+DBG_PLUGIN) ? 0xFFFF : 0;
+ cb->DB_DebugPuts = SessionLogDebugPuts;
+ cb->DB_DebugBlock = SessionLogDebugBlock;
+ cb->DB_DebugEndBlock = SessionLogDebugEndBlock;
+ cb->DB_DebugEndThread = SessionLogDebugEndThread;
+ cb->DB_DebugExotic = SessionLogDebugExotic;
+ #endif // SYDEBUG
+ #ifdef ENGINEINTERFACE_SUPPORT
+ // Admin module can use Get/SetValue for session script var access
+ // Note: these are essentially context free and work without a global call-in structure
+ // (which is not necessarily there, for example in no-library case)
+ CB_Connect_KeyAccess(cb); // connect generic key access routines
+ // Version of OpenSessionKey that implicitly opens a key for the current session (DB plugins
+ // do not have a session handle, as their use is always implicitly in a session context).
+ cb->ui.OpenSessionKey = SessionOpenSessionKey;
+ #endif // ENGINEINTERFACE_SUPPORT
+ if (!fDBApi_Admin.Created()) {
+ // - use datastore name as context name and link with session context
+ err= fDBApi_Admin.CreateContext(
+ getName(), true,
+ &(fPluginDSConfigP->fDBApiConfig_Admin),
+ fPluginAgentP->fDeviceKey.c_str(),
+ fPluginAgentP->fUserKey.c_str(),
+ fPluginAgentP->getDBApiSession()
+ );
+ }
+ if (err!=LOCERR_OK)
+ SYSYNC_THROW(TSyncException("Error creating context for plugin module handling admin",err));
+ }
+ // - data if selected
+ err = connectDataPlugin();
+ if (err==LOCERR_OK) {
+ if (!fDBApi_Data.Created()) {
+ // - use datastore name as context name and link with session context
+ err= fDBApi_Data.CreateContext(
+ getName(), false,
+ &(fPluginDSConfigP->fDBApiConfig_Data),
+ fPluginAgentP->fDeviceKey.c_str(),
+ fPluginAgentP->fUserKey.c_str(),
+ fPluginAgentP->getDBApiSession()
+ );
+ }
+ }
+ else if (err==LOCERR_NOTIMP)
+ err=LOCERR_OK; // we just don't have a data plugin, that's ok, inherited (SQL) will handle data
+ if (err!=LOCERR_OK)
+ SYSYNC_THROW(TSyncException("Error creating context for plugin module handling data",err));
+ // Perform actual loading of admin data
+ #ifndef SDK_ONLY_SUPPORT
+ // only handle here if we are in charge - otherwise let ancestor handle it
+ if (!fDBApi_Admin.Created())
+ return inherited::apiLoadAdminData(aDeviceID, aDatabaseID, aRemoteDBID);
+ #endif
+ // find and read (or create) the admin data
+ TDB_Api_Str adminData;
+ err=fDBApi_Admin.LoadAdminData(aDatabaseID,aRemoteDBID,adminData);
+ if (err==404) {
+ // this means that this admin data set did not exists before
+ fFirstTimeSync=true;
+ }
+ else if (err!=LOCERR_OK)
+ return err; // failed
+ else
+ fFirstTimeSync=false; // we already have admin data, so it can't be first sync
+ // parse data
+ const char *p = adminData.c_str();
+ // second check: if empty adminData returned, this is treated as first sync as well
+ if (*p==0) fFirstTimeSync=true;
+ const char *q;
+ string fieldname,value;
+ lineartime_t *ltP;
+ string *strP;
+ uInt16 *usP;
+ uInt32 *ulP;
+ // read all fields
+ while(*p) {
+ // find name
+ for (q=p; *q && (*q!=':' && *q!=';');) q++;
+ fieldname.assign(p,q-p);
+ p=q;
+ // p should now point to ':' or ';'
+ if (*p==':' || *p==';') {
+ p++; // consume colon or semicolon
+ // get value
+ value.erase();
+ p += CStrToStrAppend(p, value, true); // stop at quote or ctrl char
+ // analyze and store now
+ // - no storage location found yet
+ ltP=NULL;
+ strP=NULL;
+ usP=NULL;
+ ulP=NULL;
+
+ // - find where we need to store this
+ if (strucmp(fieldname.c_str(),"remotesyncanchor")==0) {
+ strP=&fLastRemoteAnchor;
+ }
+ else if (strucmp(fieldname.c_str(),"lastsync")==0) {
+ ltP=&fPreviousSyncTime;
+ }
+ else if (strucmp(fieldname.c_str(),"lasttoremotesync")==0) {
+ ltP=&fPreviousToRemoteSyncCmpRef;
+ }
+ else if (strucmp(fieldname.c_str(),"lasttoremotesyncid")==0) {
+ strP=&fPreviousToRemoteSyncIdentifier;
+ }
+ else if (strucmp(fieldname.c_str(),"resumealertcode")==0) {
+ usP=&fResumeAlertCode;
+ }
+ else if (strucmp(fieldname.c_str(),"lastsuspend")==0) {
+ ltP=&fPreviousSuspendCmpRef;
+ }
+ else if (strucmp(fieldname.c_str(),"lastsuspendid")==0) {
+ strP=&fPreviousSuspendIdentifier;
+ }
+
+ /// For datastores that can resume in middle of a chunked item (fConfigP->fResumeItemSupport==true):
+ else {
+ if (dsResumeChunkedSupportedInDB()) {
+ if (strucmp(fieldname.c_str(),"partialitemstate")==0) {
+ usP = (TSyError*)&fPartialItemState; // enum
+ }
+ else if (strucmp(fieldname.c_str(),"lastitemstatus")==0) {
+ usP= &fLastItemStatus; // status code (TSyError) of last item
+ }
+ else if (strucmp(fieldname.c_str(),"lastsourceURI" )==0) {
+ strP= &fLastSourceURI; // item ID (string, if limited in len should be long enough for large IDs, >=64 chars recommended)
+ }
+ else if (strucmp(fieldname.c_str(),"lasttargetURI" )==0) {
+ strP= &fLastTargetURI; // item ID (string, if limited in len should be long enough for large IDs, >=64 chars recommended)
+ }
+ else if (strucmp(fieldname.c_str(),"totalsize" )==0) {
+ ulP= &fPITotalSize; // uInt32, total item size
+ }
+ else if (strucmp(fieldname.c_str(),"unconfirmedsize")==0) {
+ ulP= &fPIUnconfirmedSize; // uInt32, unconfirmed part of item size
+ }
+ else if (strucmp(fieldname.c_str(),"storedsize")==0) {
+ ulP= &fPIStoredSize; // uInt32, size of BLOB, 0=none
+ }
+ /// - fPIStoredDataP = void *, BLOB data.
+ /// - If this is not NULL on entry AND fPIStoredDataAllocated is set,
+ /// the current block must be freed using smlLibFree() (and NOT JUST free()!!).
+ /// - If no BLOB is loaded, this must be set to NULL
+ /// - If a BLOB is loaded, an appropriate memory block should be allocated for it
+ /// using smlLibMalloc() (and NOT JUST malloc()!!)
+ /// - fPIStoredDataAllocated: MUST BE SET to true when a memory block was allocated into fPIStoredDataP.
+ else if (strucmp(fieldname.c_str(),"stored")==0) {
+ if (
+ fPIStoredDataP!=NULL &&
+ fPIStoredDataAllocated
+ )
+ smlLibFree(fPIStoredDataP);
+ fPIStoredDataP= NULL;
+ fPIStoredDataAllocated= false;
+
+ TDB_Api_Blk b;
+ memSize totSize;
+ bool last;
+ TSyError err;
+
+ if (fPIStoredSize>0) {
+ fPIStoredDataP= smlLibMalloc( fPIStoredSize ); // now prepare for the full blob
+ fPIStoredDataAllocated= true;
+ unsigned char* dp = (unsigned char*)fPIStoredDataP;
+ unsigned char* lim = dp + fPIStoredSize;
+ bool first= true;
+ do {
+ err= fDBApi_Admin.ReadBlob(
+ "", // fParentObjectID.c_str(), // the item ID
+ PIStored, // fBlobID.c_str(), // the ID of the blob
+ 0, // neededBytes, // how much we need
+ b, // blobData, // blob data
+ totSize, // totalsize, // will receive total size or 0 if unknown
+ first, // first,
+ last // last
+ );
+ if (err)
+ break;
+
+ ulong rema= b.fSize;
+ if (dp+rema > lim)
+ rema= lim-dp; // avoid overflow
+ memcpy( dp, b.fPtr, rema ); dp+= rema;
+ fDBApi_Admin.DisposeBlk( b ); // we have now a copy => remove it
+ first= false;
+ } while (!last);
+ } // if
+ }
+ } // if (dsResume ...)
+ } // if
+
+ // - store
+ if (strP) {
+ // - is a string
+ (*strP) = value;
+ }
+ else if (usP) {
+ // - is a uInt16
+ StrToUShort(value.c_str(),*usP);
+ }
+ else if (ulP) {
+ // - is a uInt32
+ StrToULong(value.c_str(),*ulP);
+ }
+ else if (ltP) {
+ // - is a ISO8601
+ lineartime_t tim;
+ timecontext_t tctx;
+ if (ISO8601StrToTimestamp(value.c_str(), tim, tctx)!=0) {
+ // converted ok, now make sure we get UTC
+ TzConvertTimestamp(tim,tctx,TCTX_UTC,getSessionZones(),fPluginDSConfigP->fDataTimeZone);
+ *ltP=tim;
+ }
+ else {
+ // no valid date/time = empty
+ *ltP=0;
+ }
+ }
+ }
+ // skip everything up to next end of line (in case value was terminated by a quote or other ctrl char)
+ while (*p && *p!='\r' && *p!='\n') p++;
+ // skip all line end chars up to beginning of next line or end of record
+ while (*p && (*p=='\r' || *p=='\n')) p++;
+ // p now points to next line's beginning
+ };
+ // then read maps
+ bool firstEntry=true;
+ fMapTable.clear();
+ TDB_Api_MapID mapid;
+ TMapEntry mapEntry;
+ while (fDBApi_Admin.ReadNextMapItem(mapid, firstEntry)) {
+ // get entry
+ mapEntry.localid=mapid.localID.c_str();
+ mapEntry.remoteid=mapid.remoteID.c_str();
+ mapEntry.mapflags=mapid.flags;
+ // check for old API which did not support entry types
+ if (fPluginDSConfigP->fDBApiConfig_Admin.Version()<VE_InsertMapItem) {
+ mapEntry.entrytype = mapentry_normal; // DB has no entry types, treat all as normal entries
+ }
+ else {
+ if (mapid.ident>=numMapEntryTypes)
+ mapEntry.entrytype = mapentry_invalid;
+ else
+ mapEntry.entrytype = (TMapEntryType)mapid.ident;
+ }
+ PDEBUGPRINTFX(DBG_ADMIN+DBG_EXOTIC,(
+ "read map entry (type=%s): localid='%s', remoteid='%s', mapflags=0x%lX",
+ MapEntryTypeNames[mapEntry.entrytype],
+ mapEntry.localid.c_str(),
+ mapEntry.remoteid.c_str(),
+ mapEntry.mapflags
+ ));
+ // save entry in list
+ mapEntry.changed=false; // not yet changed
+ mapEntry.added=false; // already there
+ // remember saved state of suspend mark
+ mapEntry.markforresume=false; // not yet marked for this session (mark of last session is in mapflag_useforresume!)
+ mapEntry.savedmark=mapEntry.mapflags & mapflag_useforresume;
+ // IMPORTANT: non-normal entries must be saved as deleted in the main map - they will be re-activated at the
+ // next save if needed
+ mapEntry.deleted = mapEntry.entrytype!=mapentry_normal; // only normal ones may be saved as existing in the main map
+ // save to main map list anyway to allow differential updates to map table (instead of writing everything all the time)
+ fMapTable.push_back(mapEntry);
+ // now save special maps to extra lists according to type
+ // Note: in the main map, these are marked deleted. Before the next saveAdminData, these will
+ // be re-added (=re-activated) from the extra lists if they still exist.
+ switch (mapEntry.entrytype) {
+ #ifndef SYSYNC_CLIENT
+ case mapentry_tempidmap:
+ fTempGUIDMap[mapEntry.remoteid]=mapEntry.localid; // tempGUIDs are accessed by remoteID=tempID
+ break;
+ #else
+ case mapentry_pendingmap:
+ fPendingAddMaps[mapEntry.localid]=mapEntry.remoteid;
+ break;
+ #endif
+ }
+ // next is not first entry any more
+ firstEntry=false;
+ }
+ return LOCERR_OK;
+} // TPluginApiDS::apiLoadAdminData
+
+
+#endif // not BASED_ON_BINFILE_CLIENT
+
+
+/// @brief log datastore sync result, called at end of sync with this datastore
+/// Available log information is:
+/// - fCurrentSyncTime : timestamp of start this sync session (anchor time)
+/// - fCurrentSyncCmpRef : timestamp used by next session to detect changes since this one
+/// (end of session time of last session that send data to remote)
+/// - fTargetKey : string, identifies target (user/device/datastore/remotedatastore)-tuple
+/// - fSessionP->fUserKey : string, identifies user
+/// - fSessionP->fDeviceKey : string, identifies device
+/// - fSessionP->fDomainName : string, identifies domain (aka clientID, aka enterpriseID)
+/// - getName() : string, local datastore's configured name (such as "contacts", "events" etc.)
+/// - getRemoteDBPath() : string, remote datastore's path (things like EriCalDB or "Z:\System\Contacts.cdb")
+/// - getRemoteViewOfLocalURI() : string, shows how remote specifies local datastore URI (including cgi, or
+/// in case of symbian "calendar", this can be different from getName(), as "calendar"
+/// is internally split-processed by "events" and "tasks".
+/// - fSessionP->getRemoteURI() : string, remote Device URI (usually IMEI or other globally unique ID)
+/// - fSessionP->getRemoteDescName() : string, remote Device's descriptive name (constructed from DevInf <man> and <mod>, or
+/// as set by <remoterule>'s <descriptivename>.
+/// - fSessionP->getRemoteInfoString() : string, Remote Device Version Info ("Type (HWV, FWV, SWV) Oem")
+/// - fSessionP->getSyncUserName() : string, User name as sent by the device
+/// - fSessionP->getLocalSessionID() : string, the local session ID (the one that is used to construct log file names)
+/// - fAbortStatusCode : TSyError, if == 0, sync with this datastore was ok, if <>0, there was an error.
+/// - fSessionP->getAbortReasonStatus() : TSyError, shows status of entire session at the point when datastore finishes syncing.
+/// - fSyncMode : TSyncModes, (smo_twoway,smo_fromserver,smo_fromclient)
+/// - isSlowSync() : boolean, true if slow sync (or refresh from sync, which is a one-way-slow-sync)
+/// - isFirstTimeSync() : boolean, true if first time sync of this device with this datastore
+/// - isResuming() : boolean, true if this is a resumed session
+/// - SyncMLVerDTDNames[fSessionP->getSyncMLVersion()] : SyncML version string ("1.0", "1.1". "1.2" ...)
+/// - fLocalItemsAdded : number of locally added Items
+/// - fRemoteItemsAdded : number of remotely added Items
+/// - fLocalItemsDeleted : number of locally deleted Items
+/// - fRemoteItemsDeleted : number of remotely deleted Items
+/// - fLocalItemsError : number of locally rejected Items (caused error to be sent to remote)
+/// - fRemoteItemsError : number of remotely rejected Items (returned error, will be resent)
+/// - fLocalItemsUpdated : number of locally updated Items
+/// - fRemoteItemsUpdated : number of remotely updated Items
+/// - fSlowSyncMatches : number of items matched in Slow Sync
+/// - fConflictsServerWins : number of server won conflicts
+/// - fConflictsClientWins : number of client won conflicts
+/// - fConflictsDuplicated : number of conflicts solved by duplicating item
+/// - fSessionP->getIncomingBytes() : total number of incoming bytes in this session so far
+/// - fSessionP->getOutgoingBytes() : total number of outgoing bytes in this session so far
+/// - fIncomingDataBytes : net incoming data bytes for this datastore (= payload data, without SyncML protocol overhead)
+/// - fOutgoingDataBytes : net outgoing data bytes for this datastore (= payload data, without SyncML protocol overhead)
+void TPluginApiDS::dsLogSyncResult(void)
+{
+ // security - don't use API when locked
+ if (dbAccessLocked()) return;
+
+ // format for DB Api
+ string logData,s;
+ logData.erase();
+ logData+="lastsync:"; TimestampToISO8601Str(s,fCurrentSyncTime,TCTX_UTC); logData+=s.c_str();
+ logData+="\r\ntargetkey:"; StrToCStrAppend(fTargetKey.c_str(),logData,true);
+ #ifndef BASED_ON_BINFILE_CLIENT
+ logData+="\r\nuserkey:"; StrToCStrAppend(fPluginAgentP->fUserKey.c_str(),logData,true);
+ logData+="\r\ndevicekey:"; StrToCStrAppend(fPluginAgentP->fDeviceKey.c_str(),logData,true);
+ #ifdef SCRIPT_SUPPORT
+ logData+="\r\ndomain:"; StrToCStrAppend(fPluginAgentP->fDomainName.c_str(),logData,true);
+ #endif
+ #endif
+ logData+="\r\ndsname:"; StrToCStrAppend(getName(),logData,true);
+ #ifndef MINIMAL_CODE
+ logData+="\r\ndsremotepath:"; StrToCStrAppend(getRemoteDBPath(),logData,true);
+ #endif
+ logData+="\r\ndslocalpath:"; StrToCStrAppend(getRemoteViewOfLocalURI(),logData,true);
+ logData+="\r\nfolderkey:"; StrToCStrAppend(fFolderKey.c_str(),logData,true);
+ logData+="\r\nremoteuri:"; StrToCStrAppend(fSessionP->getRemoteURI(),logData,true);
+ logData+="\r\nremotedesc:"; StrToCStrAppend(fSessionP->getRemoteDescName(),logData,true);
+ logData+="\r\nremoteinfo:"; StrToCStrAppend(fSessionP->getRemoteInfoString(),logData,true);
+ logData+="\r\nremoteuser:"; StrToCStrAppend(fSessionP->getSyncUserName(),logData,true);
+ logData+="\r\nsessionid:"; StrToCStrAppend(fSessionP->getLocalSessionID(),logData,true);
+ logData+="\r\nsyncstatus:"; StringObjAppendPrintf(logData,"%hd",fAbortStatusCode);
+ logData+="\r\nsessionstatus:"; StringObjAppendPrintf(logData,"%hd",fSessionP->getAbortReasonStatus());
+ logData+="\r\nsyncmlvers:"; StrToCStrAppend(SyncMLVerDTDNames[fSessionP->getSyncMLVersion()],logData,true);
+ logData+="\r\nsyncmode:"; StringObjAppendPrintf(logData,"%hd",(uInt16)fSyncMode);
+ logData+="\r\nsynctype:"; StringObjAppendPrintf(logData,"%d",(fSlowSync ? (fFirstTimeSync ? 2 : 1) : 0) + (isResuming() ? 10 : 0));
+ logData+="\r\nlocaladded:"; StringObjAppendPrintf(logData,"%ld",(long)fLocalItemsAdded);
+ logData+="\r\ndeviceadded:"; StringObjAppendPrintf(logData,"%ld",(long)fRemoteItemsAdded);
+ logData+="\r\nlocaldeleted:"; StringObjAppendPrintf(logData,"%ld",(long)fLocalItemsDeleted);
+ logData+="\r\ndevicedeleted:"; StringObjAppendPrintf(logData,"%ld",(long)fRemoteItemsDeleted);
+ logData+="\r\nlocalrejected:"; StringObjAppendPrintf(logData,"%ld",(long)fLocalItemsError);
+ logData+="\r\ndevicerejected:"; StringObjAppendPrintf(logData,"%ld",(long)fRemoteItemsError);
+ logData+="\r\nlocalupdated:"; StringObjAppendPrintf(logData,"%ld",(long)fLocalItemsUpdated);
+ logData+="\r\ndeviceupdated:"; StringObjAppendPrintf(logData,"%ld",(long)fRemoteItemsUpdated);
+ #ifndef SYSYNC_CLIENT
+ logData+="\r\nslowsyncmatches:"; StringObjAppendPrintf(logData,"%ld",fSlowSyncMatches);
+ logData+="\r\nserverwins:"; StringObjAppendPrintf(logData,"%ld",fConflictsServerWins);
+ logData+="\r\nclientwins:"; StringObjAppendPrintf(logData,"%ld",fConflictsClientWins);
+ logData+="\r\nduplicated:"; StringObjAppendPrintf(logData,"%ld",fConflictsDuplicated);
+ #endif
+ logData+="\r\nsessionbytesin:"; StringObjAppendPrintf(logData,"%ld",(long)fSessionP->getIncomingBytes());
+ logData+="\r\nsessionbytesout:"; StringObjAppendPrintf(logData,"%ld",(long)fSessionP->getOutgoingBytes());
+ logData+="\r\ndatabytesin:"; StringObjAppendPrintf(logData,"%ld",(long)fIncomingDataBytes);
+ logData+="\r\ndatabytesout:"; StringObjAppendPrintf(logData,"%ld",(long)fOutgoingDataBytes);
+ logData+="\r\n";
+ if (fDBApi_Admin.Created()) {
+ fDBApi_Admin.WriteLogData(logData.c_str());
+ if (fPluginDSConfigP->fDBAPIModule_Data != fPluginDSConfigP->fDBAPIModule_Admin) {
+ // admin and data are different modules, show log to data module as well
+ if (fDBApi_Data.Created())
+ fDBApi_Data.WriteLogData(logData.c_str());
+ }
+ }
+ else if (fDBApi_Data.Created()) {
+ fDBApi_Data.WriteLogData(logData.c_str());
+ }
+ // anyway: let ancestor save log info as well (if it is configured so)
+ inherited::dsLogSyncResult();
+} // TPluginApiDS::dsLogSyncResult
+
+
+
+#ifdef STREAMFIELD_SUPPORT
+
+// TApiBlobProxy
+// =============
+
+TApiBlobProxy::TApiBlobProxy(
+ TPluginApiDS *aApiDsP,
+ bool aIsStringBLOB,
+ const char *aBlobID,
+ const char *aParentID
+)
+{
+ // save values
+ fApiDsP = aApiDsP;
+ fIsStringBLOB = aIsStringBLOB;
+ fBlobID = aBlobID;
+ fParentObjectID = aParentID;
+ fBlobSize = 0;
+ fFetchedSize = 0;
+ fBufferSize = 0;
+ fBlobBuffer = NULL; // nothing retrieved yet
+} // TApiBlobProxy::TApiBlobProxy
+
+
+TApiBlobProxy::~TApiBlobProxy()
+{
+ if (fBlobBuffer) delete (char *)fBlobBuffer; // gcc 3.2.2 needs cast to suppress warning
+ fBlobBuffer=NULL;
+} // TApiBlobProxy::~TApiBlobProxy
+
+
+// fetch BLOB from DPAPI
+void TApiBlobProxy::fetchBlob(size_t aNeededSize)
+{
+ TSyError dberr=LOCERR_OK;
+
+ if (fBufferSize==0 || aNeededSize>fFetchedSize) {
+ // if do not have anything yet or not enough yet, we need to read
+ void *bufP=NULL;
+ bool last=false;
+ bool first=fFetchedSize==0; // first if we haven't fetched anything so far
+ TDB_Api_Blk blobData;
+ memSize neededBytes = aNeededSize-fFetchedSize; // how much we need to read more
+ memSize totalsize = 0; // not known
+ memSize offs= 0;
+
+ if (!fBlobBuffer && neededBytes==0) neededBytes=200; // just read a bit to eventually obtain the total size
+ do {
+ // read a block
+ dberr = fApiDsP->fDBApi_Data.ReadBlob(
+ fParentObjectID.c_str(), // the item ID
+ fBlobID.c_str(), // the ID of the blob
+ neededBytes, // how much we need
+ blobData, // blob data
+ totalsize, // will receive total size or 0 if unknown
+ first,
+ last
+ );
+ if (dberr!=LOCERR_OK)
+ SYSYNC_THROW(TSyncException("DB_Api::ReadBlob fatal error",dberr));
+ // obtain a buffer
+ size_t newBufSiz = (neededBytes ? neededBytes : fFetchedSize+=blobData.fSize) + 1; // +1 for eventual string terminator
+ if (fBufferSize<newBufSiz) {
+ // we need a larger buffer
+ bufP = new unsigned char[newBufSiz];
+ fBufferSize=newBufSiz; // save new buffer size
+ // if there's something in the old buffer, we need to copy it and delete it
+ if (fBlobBuffer) {
+ if (fFetchedSize)
+ memcpy(bufP,fBlobBuffer,fFetchedSize); // copy fetched portion from old buffer
+ delete (unsigned char *)fBlobBuffer; // dispose old buffer
+ //fBlobBuffer=bufP; // save new one
+ } // if
+
+ fBlobBuffer= bufP; // save new one, in EVERY CASE
+ }
+ else {
+ // enough room in current buffer, just use it
+ bufP=fBlobBuffer;
+ }
+ // get pointer where to copy data to
+ //bufP=(char *)fBlobBuffer+fFetchedSize; // don't use the fFetchedSize here !! (this is out of the buffer)
+ bufP=(char *)fBlobBuffer+offs;
+ // actually copy data from DBApi block to buffer
+ memcpy(bufP,blobData.fPtr,blobData.fSize);
+
+ offs += blobData.fSize;
+ fFetchedSize+= blobData.fSize;
+ neededBytes -= blobData.fSize; // see what's remaining until what we originally requested
+ blobData.DisposeBlk(); // <blobData.fSize> will be set back to 0 here !!
+
+ if (neededBytes<=0) { // for the moment we have what we want !!
+ fBlobSize= fFetchedSize; // that's what we have already
+ break;
+ } // if
+
+ // now decide what to do
+ if (totalsize || last) {
+ // we know the total size now
+ fBlobSize=totalsize;
+ if (last) fBlobSize= fFetchedSize; // we know it anyway
+ // - we can stop if we have fetched enough to satisfy aNeededSize
+ if (last || (!fIsStringBLOB && fFetchedSize>=aNeededSize)) break;
+ }
+ // we need to continue until we get the total size or last
+ first=false;
+ // calculate how much we want to read next time
+ //neededBytes-=blobData.fSize; // it's too late for blobData.fSize here: fSize is 0 after DisposeBlk
+ // if neededBytes now gets zero, this will request as much as possible for the next call to ReadBlob
+ } while(true);
+
+ // for strings, we need to convert the data and re-adjust the size
+ if (fIsStringBLOB) {
+ // we KNOW that we have the entire BLOB text here
+ // - set a terminator
+ *((char *)fBlobBuffer+fFetchedSize)=0; // set terminator
+ // - convert to UTF8 and internal linefeeds
+ string strUtf8;
+ appendStringAsUTF8((const char *)fBlobBuffer, strUtf8, fApiDsP->fPluginDSConfigP->fDataCharSet, lem_cstr);
+ // set actual size
+ fBlobSize=strUtf8.size();
+ // copy from string to buffer
+ if (fBlobSize+1<=fBufferSize) {
+ bufP = fBlobBuffer; // use old buffer
+ }
+ else {
+ fBufferSize=fBlobSize+1;
+ bufP = new unsigned char [fBufferSize];
+ delete (unsigned char *)fBlobBuffer;
+ fBlobBuffer = bufP;
+ }
+ memcpy(bufP,strUtf8.c_str(),strUtf8.size());
+ fFetchedSize=strUtf8.size();
+ *((char *)fBlobBuffer+fFetchedSize)=0; // set terminator
+ } // if
+ }
+} // TApiBlobProxy::fetchBlob
+
+
+
+
+// returns size of entire blob
+size_t TApiBlobProxy::getBlobSize(TStringField *aFieldP)
+{
+ fetchBlob(0);
+ return fBlobSize;
+} // TApiBlobProxy::getBlobSize
+
+
+// read from Blob from specified stream position and update stream pos
+size_t TApiBlobProxy::readBlobStream(TStringField *aFieldP, size_t &aPos, void *aBuffer, size_t aMaxBytes)
+{
+ if (fBlobSize<=aPos || !fBlobBuffer) { // <=aPos instead of <
+ // we need to read the body
+ fetchBlob(aPos+aMaxBytes);
+ }
+ // now copy from our buffer
+ if (aPos>fBlobSize) return 0;
+ if (aPos+aMaxBytes>fBlobSize) aMaxBytes=fBlobSize-aPos;
+ if (aMaxBytes==0) return 0;
+ // copy data from api answer buffer to caller's buffer
+ memcpy(aBuffer,(char *)fBlobBuffer+aPos,aMaxBytes); // not with an additional offset (out of buffer)
+ aPos+= aMaxBytes;
+ return aMaxBytes; // return number of bytes actually read
+} // TApiBlobProxy::readBlobStream
+
+#endif // STREAMFIELD_SUPPORT
+
+
+
+#if defined(DBAPI_ASKEYITEMS) && defined(ENGINEINTERFACE_SUPPORT)
+
+// TDBItemKey
+// ==========
+
+
+// get FID for specified name
+sInt16 TDBItemKey::getFidFor(cAppCharP aName, stringSize aNameSz)
+{
+ if (!fItemP) return VARIDX_UNDEFINED; // no item, no field is accessible
+
+ TFieldMapList *fmlP = &(fPluginApiDS->fPluginDSConfigP->fFieldMappings.fFieldMapList);
+
+ // check for iterator commands first
+ if (strucmp(aName,VALNAME_FIRST)==0) {
+ fIterator=fmlP->begin();
+ if (fIterator!=fmlP->end())
+ return static_cast<TApiFieldMapItem *>(*fIterator)->fid;
+ }
+ else if (strucmp(aName,VALNAME_NEXT)==0) {
+ if (fIterator!=fmlP->end())
+ fIterator++;
+ if (fIterator!=fmlP->end())
+ return static_cast<TApiFieldMapItem *>(*fIterator)->fid;
+ }
+ else {
+ TFieldMapList::iterator pos;
+ for (pos=fmlP->begin(); pos!=fmlP->end(); pos++) {
+ // check for name
+ TApiFieldMapItem *fmiP = static_cast<TApiFieldMapItem *>(*pos);
+ if (strucmp(aName,fmiP->getName(),aNameSz)==0) {
+ // return field ID (negative = local script var, positive = item field)
+ return fmiP->fid;
+ }
+ }
+ }
+ // none found
+ return VARIDX_UNDEFINED;
+} // TDBItemKey::getFidFor
+
+
+
+TItemField *TDBItemKey::getBaseFieldFromFid(sInt16 aFid)
+{
+ if (!fItemP) return false; // no item, no field is accessible
+ return fPluginApiDS->getMappedBaseFieldOrVar(*fItemP, aFid);
+} // TDBItemKey::getBaseFieldFromFid
+
+
+bool TDBItemKey::getFieldNameFromFid(sInt16 aFid, string &aFieldName)
+{
+ if (!fItemP) return false; // no item, no field is accessible
+ // name is map name (NOT field name!)
+ TFieldMapList *fmlP = &(fPluginApiDS->fPluginDSConfigP->fFieldMappings.fFieldMapList);
+ TFieldMapList::iterator pos;
+ TApiFieldMapItem *fmiP;
+
+ // search field map item by fid, return name
+ for (pos=fmlP->begin(); pos!=fmlP->end(); pos++) {
+ fmiP = static_cast<TApiFieldMapItem *>(*pos);
+ // check for fid
+ if (fmiP->fid == aFid) {
+ // return name
+ aFieldName = fmiP->getName();
+ return true;
+ }
+ }
+ // none found
+ return false;
+} // TDBItemKey::getFieldNameFromFid
+
+
+#endif // DBAPI_ASKEYITEMS + ENGINEINTERFACE_SUPPORT
+
+
+
+
+} // namespace sysync
+
+/* end of TPluginApiDS implementation */
+
+// eof
diff --git a/src/DB_interfaces/api_db/pluginapids.h b/src/DB_interfaces/api_db/pluginapids.h
new file mode 100755
index 0000000..5df5e1d
--- /dev/null
+++ b/src/DB_interfaces/api_db/pluginapids.h
@@ -0,0 +1,445 @@
+/**
+ * @File pluginapids.h
+ *
+ * @Author Lukas Zeller (luz@synthesis.ch)
+ *
+ * @brief TPluginApiDS
+ * Plugin based datastore API implementation
+ *
+ * Copyright (c) 2001-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ * @Date 2005-10-06 : luz : created from apidbdatastore
+ */
+
+
+#ifndef PLUGINAPIDS_H
+#define PLUGINAPIDS_H
+
+
+// includes
+
+#ifdef SQL_SUPPORT
+ #include "odbcapids.h"
+ #undef SDK_ONLY_SUPPORT
+#else
+ #include "customimplds.h"
+ #define SDK_ONLY_SUPPORT 1
+#endif
+
+#include "dbapi.h"
+#include "pluginapiagent.h"
+
+
+using namespace sysync;
+
+namespace sysync {
+
+// single field mapping
+class TApiFieldMapItem:
+ #ifdef SDK_ONLY_SUPPORT
+ public TFieldMapItem
+ #else
+ public TODBCFieldMapItem
+ #endif
+{
+ #ifdef SDK_ONLY_SUPPORT
+ typedef TFieldMapItem inherited;
+ #else
+ typedef TODBCFieldMapItem inherited;
+ #endif
+public:
+ TApiFieldMapItem(const char *aElementName, TConfigElement *aParentElement);
+ // - parser for extra attributes (for derived classes)
+ virtual void checkAttrs(const char **aAttributes);
+ // properties
+}; // TApiFieldMapItem
+
+
+#ifdef ARRAYDBTABLES_SUPPORT
+// array mapping
+class TApiFieldMapArrayItem:
+ #ifdef SDK_ONLY_SUPPORT
+ public TFieldMapArrayItem
+ #else
+ public TODBCFieldMapArrayItem
+ #endif
+{
+ #ifdef SDK_ONLY_SUPPORT
+ typedef TFieldMapArrayItem inherited;
+ #else
+ typedef TODBCFieldMapArrayItem inherited;
+ #endif
+public:
+ TApiFieldMapArrayItem(TCustomDSConfig *aCustomDSConfigP, TConfigElement *aParentElement);
+ // - parser for extra attributes (for derived classes)
+ virtual void checkAttrs(const char **aAttributes);
+ // properties
+}; // TApiFieldMapArrayItem
+#endif
+
+
+
+class TPluginDSConfig:
+ #ifdef SDK_ONLY_SUPPORT
+ public TCustomDSConfig
+ #else
+ public TOdbcDSConfig
+ #endif
+{
+ #ifdef SDK_ONLY_SUPPORT
+ typedef TCustomDSConfig inherited;
+ #else
+ typedef TOdbcDSConfig inherited;
+ #endif
+public:
+ TPluginDSConfig(const char* aName, TConfigElement *aParentElement);
+ virtual ~TPluginDSConfig();
+ // properties
+ /// name of the DB API modules
+ /// @{
+ string fDBAPIModule_Data; ///< name of the module which handles data - if fDataModuleAlsoHandlesAdmin is set this is used for admin as well
+ string fDBAPIModule_Admin; ///< name of the module which handles admin
+ /// @}
+ /// flag to handle admin in same API module as data
+ /// @Note this is only needed for old-style configs where we had only ONE module
+ /// per datastore for both admin and data. Now the normal way is to configure
+ /// either fDBAPIModule_Data or fDBAPIModule_Admin to select plugin handling of
+ /// either data or admin independently.
+ bool fDataModuleAlsoHandlesAdmin;
+ // - config object for API module
+ TDB_Api_Config fDBApiConfig_Data;
+ TDB_Api_Config fDBApiConfig_Admin;
+ // - generic module params
+ TApiParamConfig fPluginParams_Admin;
+ TApiParamConfig fPluginParams_Data;
+ // - debug flags
+ uInt16 fPluginDbgMask_Data;
+ uInt16 fPluginDbgMask_Admin;
+ // capabilities of connected plugin
+ bool fItemAsKey;
+ // public methods
+ // - create appropriate datastore from config, calls addTypeSupport as well
+ virtual TLocalEngineDS *newLocalDataStore(TSyncSession *aSessionP);
+ // factory functions for field map items
+ virtual TFieldMapItem *newFieldMapItem(const char *aElementName, TConfigElement *aParentElement)
+ { return new TApiFieldMapItem(aElementName,aParentElement); };
+ #ifdef ARRAYDBTABLES_SUPPORT
+ virtual TFieldMapArrayItem *newFieldMapArrayItem(TCustomDSConfig *aCustomDSConfigP, TConfigElement *aParentElement)
+ { return new TApiFieldMapArrayItem(aCustomDSConfigP,aParentElement); };
+ #endif
+protected:
+ // check config elements
+ virtual bool localStartElement(const char *aElementName, const char **aAttributes, sInt32 aLine);
+ virtual void clear();
+ virtual void localResolve(bool aLastPass);
+private:
+}; // TPluginDSConfig
+
+
+// forward
+class TPluginAgentConfig;
+class TPluginApiAgent;
+#if defined(DBAPI_ASKEYITEMS) && defined(ENGINEINTERFACE_SUPPORT)
+class TDBItemKey;
+#endif // DBAPI_ASKEYITEMS + ENGINEINTERFACE_SUPPORT
+
+
+class TPluginApiDS:
+ #ifdef SDK_ONLY_SUPPORT
+ public TCustomImplDS
+ #else
+ public TODBCApiDS
+ #endif
+{
+ #ifdef SDK_ONLY_SUPPORT
+ typedef TCustomImplDS inherited;
+ #else
+ typedef TODBCApiDS inherited;
+ #endif
+ friend class TApiBlobProxy;
+ #if defined(DBAPI_ASKEYITEMS) && defined(ENGINEINTERFACE_SUPPORT)
+ friend class TDBItemKey;
+ #endif // DBAPI_ASKEYITEMS + ENGINEINTERFACE_SUPPORT
+private:
+ void InternalResetDataStore(void); // reset for re-use without re-creation
+public:
+ TPluginApiDS(
+ TPluginDSConfig *aConfigP,
+ sysync::TSyncSession *aSessionP,
+ const char *aName,
+ uInt32 aCommonSyncCapMask=0);
+ virtual void announceAgentDestruction(void);
+ virtual void dsResetDataStore(void) { InternalResetDataStore(); inherited::dsResetDataStore(); };
+ virtual ~TPluginApiDS();
+
+ #ifndef BASED_ON_BINFILE_CLIENT
+ /// @name apiXXXX methods defining the interface from TCustomImplDS to TXXXApi actual API implementations
+ /// @{
+ //
+ /// @brief Load admin data from ODBC database
+ /// @param aDeviceID[in] remote device URI (device ID)
+ /// @param aDatabaseID[in] local database ID
+ /// @param aRemoteDBID[in] database ID of remote device
+ /// Must search for existing target record matching the triple (aDeviceID,aDatabaseID,aRemoteDBID)
+ /// - if there is a matching record: load it
+ /// - if there is no matching record, set fFirstTimeSync=true. The implementation may already create a
+ /// new record with the key (aDeviceID,aDatabaseID,aRemoteDBID) and initialize it with the data from
+ /// the items as shown below. At least, fTargetKey must be set to a value that will allow apiSaveAdminData to
+ /// update the record. In case implementation chooses not create the record only in apiSaveAdminData, it must
+ /// buffer the triple (aDeviceID,aDatabaseID,aRemoteDBID) such that it is available at apiSaveAdminData.
+ /// If a record exists implementation must load the following items:
+ /// - fTargetKey = some key value that can be used to re-identify the target record later at SaveAdminData.
+ /// If the database implementation has other means to re-identify the target, this can be
+ /// left unassigned.
+ /// - fLastRemoteAnchor = anchor string used by remote party for last session (and saved to DB then)
+ /// - fPreviousSyncTime = anchor (beginning of session) timestamp of last session.
+ /// - fPreviousToRemoteSyncCmpRef = Reference time to determine items modified since last time sending data to remote
+ /// - fPreviousToRemoteSyncIdentifier = string identifying last session that sent data to remote (needs only be saved
+ /// if derived datastore cannot work with timestamps and has its own identifier).
+ /// - fMapTable = list<TMapEntry> containing map entries. The implementation must load all map entries
+ /// related to the current sync target identified by the triple of (aDeviceID,aDatabaseID,aRemoteDBID)
+ /// or by fTargetKey. The entries added to fMapTable must have "changed", "added" and "deleted" flags
+ /// set to false.
+ /// For resumable datastores:
+ /// - fMapTable = In addition to the above, the markforresume flag must be saved in the mapflags
+ // when it is not equal to the savedmark flag - independently of added/deleted/changed.
+ /// - fResumeAlertCode = alert code of current suspend state, 0 if none
+ /// - fPreviousSuspendCmpRef = reference time of last suspend (used to detect items modified during a suspend / resume)
+ /// - fPreviousSuspendIdentifier = identifier of last suspend (used to detect items modified during a suspend / resume)
+ /// (needs only be saved if derived datastore cannot work with timestamps and has
+ /// its own identifier)
+ /// - fPendingAddMaps = map<string,string>. The implementation must load all all pending maps (client only) into
+ /// fPendingAddMaps (and fUnconfirmedMaps must be left empty).
+ /// - fTempGUIDMap = map<string,string>. The implementation must save all entries as temporary LUID to GUID mappings
+ /// (server only)
+ virtual localstatus apiLoadAdminData(
+ const char *aDeviceID, // remote device URI (device ID)
+ const char *aDatabaseID, // database ID
+ const char *aRemoteDBID // database ID of remote device
+ );
+ /// @brief Save admin data to ODBC database
+ /// @param[in] aSessionFinished if true, this is a end-of-session save (and not only a suspend save) - but not necessarily a successful one
+ /// @param[in] aSuccessful if true, this is a successful end-of-session
+ /// Must save to the target record addressed at LoadAdminData() by the triple (aDeviceID,aDatabaseID,aRemoteDBID)
+ /// Implementation must save the following items:
+ /// - fLastRemoteAnchor = anchor string used by remote party for this session (and saved to DB then)
+ /// - fPreviousSyncTime = anchor (beginning of session) timestamp of this session.
+ /// - fPreviousToRemoteSyncCmpRef = Reference time to determine items modified since last time sending data to remote
+ /// - fPreviousToRemoteSyncIdentifier = string identifying last session that sent data to remote (needs only be saved
+ /// if derived datastore cannot work with timestamps and has its own identifier).
+ /// - fMapTable = list<TMapEntry> containing map entries. The implementation must save all map entries
+ /// that have changed, are new or are deleted. See below for additional resume requirements.
+ /// For resumable datastores:
+ /// - fMapTable = In addition to the above, the markforresume flag must be saved in the mapflags
+ // when it is not equal to the savedmark flag - independently of added/deleted/changed.
+ /// - fResumeAlertCode = alert code of current suspend state, 0 if none
+ /// - fPreviousSuspendCmpRef = reference time of last suspend (used to detect items modified during a suspend / resume)
+ /// - fPreviousSuspendIdentifier = identifier of last suspend (used to detect items modified during a suspend / resume)
+ /// (needs only be saved if derived datastore cannot work with timestamps and has
+ /// its own identifier)
+ /// - fPendingAddMaps and fUnconfirmedMaps = map<string,string>. The implementation must save all entries as
+ /// pending maps (client only). Note that fPendingAddMaps might contain temporary localIDs,
+ /// so call dsFinalizeLocalID() to ensure these are converted to final before saving.
+ /// - fTempGUIDMap = map<string,string>. The implementation must save all entries as temporary LUID to GUID mappings
+ /// (server only)
+ virtual localstatus apiSaveAdminData(bool aSessionFinished, bool aSuccessful);
+ /// read sync set IDs and mod dates.
+ /// @param[in] if set, all data fields are needed, so ReadSyncSet MAY
+ /// read items here already. Note that ReadSyncSet MAY read items here
+ /// even if aNeedAll is not set (if it is more efficient than reading
+ /// them separately afterwards).
+ #endif // not BASED_ON_BINFILE_CLIENT
+
+ virtual localstatus apiReadSyncSet(bool aNeedAll);
+ /// Zap all data in syncset (note that everything outside the sync set will remain intact)
+ virtual localstatus apiZapSyncSet(void);
+ virtual bool apiNeedSyncSetToZap(void);
+ /// fetch record contents from DB by localID.
+ virtual localstatus apiFetchItem(TMultiFieldItem &aItem, bool aReadPhase, TSyncSetItem *aSyncSetItemP);
+ /// add new item to datastore, returns created localID
+ virtual localstatus apiAddItem(TMultiFieldItem &aItem, string &aLocalID);
+ /// update existing item in datastore, returns 404 if item not found
+ virtual localstatus apiUpdateItem(TMultiFieldItem &aItem);
+ /// delete existing item in datastore, returns 211 if not existing any more
+ virtual localstatus apiDeleteItem(TMultiFieldItem &aItem);
+ /// end of syncset read
+ virtual localstatus apiEndDataRead(void) { return LOCERR_OK; /* NOP at this time */ };
+ /// start of write
+ virtual localstatus apiStartDataWrite(void);
+ /// end DB data write sequence (but not yet admin data)
+ virtual localstatus apiEndDataWrite(string &aThisSyncIdentifier);
+ /// @}
+
+ /// @name dsXXXX virtuals defined by TLocalEngineDS
+ /// These are usually designed such that they should always call inherited::dsXXX to let the entire chain
+ /// of ancestors see the calls
+ /// @{
+ //
+ /// end of message handling
+ // virtual void dsEndOfMessage(void);
+ #ifdef OBJECT_FILTERING
+ /// test expression filtering capability of datastore
+ /// @return true if DB implementation can filter the standard filters
+ /// (LocalDBFilter, TargetFilter and InvisibleFilter) during database fetch
+ /// otherwise, fetched items will be filtered after being read from DB.
+ virtual bool dsFilteredFetchesFromDB(bool aFilterChanged=false);
+ /// test special option filtering capability of datastore
+ /// @return true if DB implementation can also apply special filters like CGI-options
+ /// /dr(x,y) etc. during fetching
+ virtual bool dsOptionFilterFetchesFromDB(void);
+ #endif
+ /// alert possible thread change
+ virtual void dsThreadMayChangeNow(void);
+ /// returns true if DB implementation supports resume (saving of resume marks, alert code, pending maps, tempGUIDs)
+ virtual bool dsResumeSupportedInDB(void);
+ #ifdef SYSYNC_CLIENT
+ /// finalize local ID (for datastores that can't efficiently produce these at insert)
+ virtual bool dsFinalizeLocalID(string &aLocalID);
+ #endif
+ /// @}
+
+
+ // - must be called before starting a thread. If returns false, starting a thread now
+ // is not allowed and must be postponed.
+ virtual bool startingThread(void);
+ // - must be called when a thread's activity has ended
+ // BUT THE CALL MUST BE FROM THE ENDING THREAD, not the main thread!
+ virtual void endingThread(void);
+ // - should be called before doing DB accesses that might be locked (e.g. because another thread is using the DB resources)
+ virtual bool dbAccessLocked(void);
+ // - log datastore sync result, Called at end of sync with this datastore
+ virtual void dsLogSyncResult(void);
+private:
+ // - connect data handling part of plugin. Returns LOCERR_NOTIMPL when no data plugin is selected
+ TSyError connectDataPlugin(void);
+ // - alert possible thread change to plugins
+ // Does not check if API is locked or not, see dsThreadMayChangeNow()
+ void ThreadMayChangeNow(void);
+ // api helpers, public because called from non-member callbacks
+ // - store itemdata field into mapped TItemField
+ bool storeField(
+ const char *aName,
+ const char *aParams,
+ const char *aValue,
+ TMultiFieldItem &aItem,
+ uInt16 aSetNo,
+ sInt16 aArrayIndex
+ );
+ #ifdef DBAPI_TEXTITEMS
+ // - parse itemdata into item
+ bool parseItemData(
+ TMultiFieldItem &aItem,
+ const char *aItemData,
+ uInt16 aSetNo
+ );
+ // - create itemdata from mapped fields
+ bool generateItemData(
+ bool aAssignedOnly,
+ TMultiFieldItem &aItem,
+ uInt16 aSetNo,
+ string &aDataFields
+ );
+ #endif
+ #if defined(DBAPI_ASKEYITEMS) && defined(ENGINEINTERFACE_SUPPORT)
+ TDBItemKey *newDBItemKey(TMultiFieldItem *aItemP);
+ #endif // DBAPI_ASKEYITEMS + ENGINEINTERFACE_SUPPORT
+ // - post process item after reading from DB (run script)
+ bool postReadProcessItem(TMultiFieldItem &aItem, uInt16 aSetNo);
+ // - pre-process item before writing to DB (run script)
+ bool preWriteProcessItem(TMultiFieldItem &aItem);
+ // - send BLOBs of this item one by one
+ bool writeBlobs(
+ bool aAssignedOnly,
+ TMultiFieldItem &aItem,
+ uInt16 aSetNo
+ );
+ // - delete BLOBs of this item one by one
+ bool deleteBlobs(
+ bool aAssignedOnly,
+ TMultiFieldItem &aItem,
+ uInt16 aSetNo
+ );
+private:
+ // agent (typed pointers for convenience)
+ TPluginApiAgent *fPluginAgentP;
+ // config (typed pointers for convenience)
+ TPluginAgentConfig *fPluginAgentConfigP;
+ // the actual API access instances
+ TDB_Api fDBApi_Data; ///< access to data
+ TDB_Api fDBApi_Admin; ///< access to admin
+ // config pointer
+ TPluginDSConfig *fPluginDSConfigP;
+}; // TPluginApiDS
+
+
+#ifdef STREAMFIELD_SUPPORT
+
+// proxy for loading blobs
+class TApiBlobProxy : public TBlobProxy
+{
+ typedef TBlobProxy inherited;
+public:
+ TApiBlobProxy(TPluginApiDS *aApiDsP, bool aIsStringBLOB, const char *aBlobID, const char *aParentID);
+ virtual ~TApiBlobProxy();
+ // - returns size of entire blob
+ virtual size_t getBlobSize(TStringField *aFieldP);
+ // - read from Blob from specified stream position and update stream pos
+ virtual size_t readBlobStream(TStringField *aFieldP, size_t &aPos, void *aBuffer, size_t aMaxBytes);
+ // - dependency on a local ID
+ virtual void setParentLocalID(const char *aParentLocalID) { fParentObjectID=aParentLocalID; };
+private:
+ // fetch BLOB from DPAPI
+ void fetchBlob(size_t aNeededSize);
+ // Vars
+ TPluginApiDS *fApiDsP; // datastore which can be asked to retrieve data
+ bool fIsStringBLOB; // if set, the blob must be treated as a string (applying DB charset conversions)
+ string fBlobID; // object ID of blob
+ string fParentObjectID; // id of parent object (will be updated in case parent signals ID change)
+ size_t fBlobSize; // total size of the BLOB
+ size_t fFetchedSize; // how much we have already retrieved (and is in fBlobBuffer)
+ size_t fBufferSize; // how much room we have in the buffer
+ void *fBlobBuffer; // buffer for blob already retrieved (NULL if nothing yet)
+}; // TApiBlobProxy
+
+#endif
+
+
+#if defined(DBAPI_ASKEYITEMS) && defined(ENGINEINTERFACE_SUPPORT)
+
+// key for access to a item using the settings key API
+class TDBItemKey :
+ public TItemFieldKey
+{
+ typedef TItemFieldKey inherited;
+public:
+ TDBItemKey(TEngineInterface *aEngineInterfaceP, TMultiFieldItem *aItemP, TPluginApiDS *aPluginApiDS) :
+ inherited(aEngineInterfaceP),
+ fPluginApiDS(aPluginApiDS),
+ fItemP(aItemP)
+ {};
+
+protected:
+
+ // methods to actually access a TItemField
+ virtual sInt16 getFidFor(cAppCharP aName, stringSize aNameSz);
+ virtual bool getFieldNameFromFid(sInt16 aFid, string &aFieldName);
+ virtual TItemField *getBaseFieldFromFid(sInt16 aFid);
+
+ // the datastore
+ TPluginApiDS *fPluginApiDS;
+ // the item being accessed
+ TMultiFieldItem *fItemP;
+ // iterator
+ TFieldMapList::iterator fIterator;
+
+}; // TDBItemKey
+
+#endif // DBAPI_ASKEYITEMS + ENGINEINTERFACE_SUPPORT
+
+
+
+} // namespace sysync
+
+#endif // PLUGINAPIDS_H
+
+// eof
diff --git a/src/DB_interfaces/api_db/plugindb.h b/src/DB_interfaces/api_db/plugindb.h
new file mode 100755
index 0000000..f03ef6a
--- /dev/null
+++ b/src/DB_interfaces/api_db/plugindb.h
@@ -0,0 +1,21 @@
+/*
+ * Common include file for all Plugin DB sources
+ */
+
+#ifndef PLUGINDB_H
+#define PLUGINDB_H
+
+
+// precompiled portion
+#include "plugindb_precomp.h"
+
+// other common includes
+#include "sysync.h"
+
+// Items used by ODBC DB interface
+#include "multifielditem.h"
+#include "mimediritemtype.h"
+
+#endif
+
+/* eof */
diff --git a/src/DB_interfaces/api_db/plugindb_precomp.h b/src/DB_interfaces/api_db/plugindb_precomp.h
new file mode 100755
index 0000000..1132e76
--- /dev/null
+++ b/src/DB_interfaces/api_db/plugindb_precomp.h
@@ -0,0 +1,18 @@
+/*
+ * precompiled/precompilable headers
+ */
+
+#ifndef PLUGINDB_PRECOMP_H
+#define PLUGINDB_PRECOMP_H
+
+
+/* Specific headers for Plugin DB interface
+ */
+
+#ifdef _WIN32
+ #include <windows.h>
+#endif
+
+#endif
+
+/* eof */
diff --git a/src/DB_interfaces/api_db/sync_dbapiconnect.cpp b/src/DB_interfaces/api_db/sync_dbapiconnect.cpp
new file mode 100755
index 0000000..cfd1eef
--- /dev/null
+++ b/src/DB_interfaces/api_db/sync_dbapiconnect.cpp
@@ -0,0 +1,533 @@
+/*
+ * File: sync_dbapiconnect.cpp
+ *
+ * Author: Beat Forster (bfo@synthesis.ch)
+ *
+ * C/C++ Programming interface between
+ * the Synthesis SyncML engine
+ * and the database layer
+ *
+ * Copyright (c) 2005-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ *
+ * This module contains the method table and connection
+ * routines, which are used e.g. for the JNI plugin.
+ *
+ */
+
+
+#include "sync_include.h"
+#include "sync_dbapidef.h"
+#include "sync_dbapiconnect.h"
+#include "DLL_interface.h"
+
+#ifdef __cplusplus
+ namespace sysync {
+#endif
+
+
+// Support functions for creating Java signatures
+string LCP( string jP, string className )
+{
+ if (jP.empty()) jP = className;
+ else jP+= '/' + className;
+ return "L" + jP + ";";
+} // LCP
+
+
+ string JCS ( void ) { return LCP( "java/lang", "String" ); }
+
+static string Sgn ( string s,
+ string ret ){ return "(" + s + ")" + ret; }
+
+ string SgnS ( string s ) { return Sgn( s, "S" ); }
+ string SgnI ( string s ) { return Sgn( s, "I" ); }
+ string SgnV ( string s ) { return Sgn( s, "V" ); }
+
+//static string RefS ( bool a64bit, string s= "" ) { if (a64bit) return "J" + s;
+// else return "I" + s; }
+
+//static string SgnS_X( bool a64bit, string s= "" ) { return Sgn( RefS( a64bit, s ),"S" ); }
+//static string SgnI_X( bool a64bit, string s= "" ) { return Sgn( RefS( a64bit, s ),"I" ); }
+//static string SgnZ_X( bool a64bit, string s= "" ) { return Sgn( RefS( a64bit, s ),"Z" ); }
+//static string SgnV_X( bool a64bit, string s= "" ) { return Sgn( RefS( a64bit, s ),"V" ); }
+
+
+class JSgn {
+ public:
+ JSgn() { f64bit= false; }
+ bool f64bit;
+ string fvr;
+ string fr;
+
+ string SgnS_V( string s= "" ) { return Sgn( fvr + s,"S" ); }
+ string SgnS_X( string s= "" ) { return Sgn( fr + s,"S" ); }
+ string SgnI_X( string s= "" ) { return Sgn( fr + s,"I" ); }
+ string SgnZ_X( string s= "" ) { return Sgn( fr + s,"Z" ); }
+ string SgnV_X( string s= "" ) { return Sgn( fr + s,"V" ); }
+}; // JSgn
+
+
+
+/*! connect the list of these functions, including JNI signatures, if required */
+TSyError DBApi_DLLAssign( appPointer aMod, appPointer aField, memSize aFieldSize,
+ string aKey, bool a64bit, string jP )
+{
+ bool keyCur, keyOld, keyOld2;
+ string js1, js2, js3, js4, js5;
+
+ string jt = JCS();
+ string jvt= LCP( jP, c_VAR_String );
+
+ string jvl= LCP( jP, c_VAR_long );
+ string jvi= LCP( jP, c_VAR_int );
+ string jdc= LCP( jP, c_JNI_DB_Callback );
+ string jmi= LCP( jP, c_JNI_MapID );
+ string jii= LCP( jP, c_JNI_ItemID );
+
+ JSgn j;
+ j.f64bit= a64bit;
+ if (j.f64bit) { j.fvr= jvl; j.fr= "J"; }
+ else { j.fvr= jvi; j.fr= "I"; }
+
+//string jvr= jvi; string jr= "I";
+//if (a64bit) { jvr= jvl; jr= "J"; }
+
+ string js_ = j.SgnS_X(); // "(I)S"
+ string jsT = j.SgnS_X( jt ); // "(ILjava/lang/String;)S"
+ string jsvT= j.SgnS_X( jvt ); // "(ILVAR_String;)S"
+ string jsA = j.SgnS_X( jvt + jvt + jvt + "I" ); // "(ILVAR_String;LVAR_String;LVAR_String;I)S"
+
+
+ // -----------------------------------------------------------------------------------------
+ if (strcmp( aKey.c_str(),Plugin_Start )==0) {
+ // "(LVAR_xxx; ... (VAR_xxx = VAR_int/VAR_long)
+ js1= j.SgnS_V( jt // ... Ljava/lang/String; ...
+ + jt // ... Ljava/lang/String; ...
+ + jt // ... Ljava/lang/String; ...
+ + jdc ); // ... LDB_Callback;)S"
+ js2= j.SgnI_X();
+
+ return ConnectFunctions( aMod, aField,aFieldSize, true,
+ // ---- start of plugin connection
+ Mo_CC, js1.c_str(),
+ Mo_Ve, js2.c_str(),
+ Mo_Ca,jsvT.c_str(),
+ NULL );
+ } // if
+
+
+ // compatibility to older version ( w/o <engineVersion> )
+ keyOld= strcmp( aKey.c_str(),Plugin_Param_OLD )==0;
+ keyCur= strcmp( aKey.c_str(),Plugin_Param )==0;
+
+ if (keyCur || keyOld) {
+ // additional param for newer version
+ if (keyCur) js1= j.SgnS_X( jt + "I" ); // "(ILjava/lang/String;I)S"
+ else js1= jsT; // "(ILjava/lang/String;)S"
+
+ return ConnectFunctions( aMod, aField,aFieldSize, true,
+ // ---- start of plugin connection
+ Mo_PP, js1.c_str(),
+ NULL );
+ } // if
+
+
+ // ---- session ---------------------------------------------------
+ if (strcmp( aKey.c_str(),Plugin_Session )==0) {
+ // "(LVAR_xxx; ... (VAR_xxx = VAR_int/VAR_long)
+ js1= j.SgnS_V( jt // ... Ljava/lang/String; ...
+ + jdc ); // ... LDB_Callback;)S"
+
+ js2= j.SgnV_X();
+ js3= j.SgnV_X( "Z" + jt );
+
+ return ConnectFunctions( aMod, aField,aFieldSize, true,
+ // ---- session ----
+ Se_CC, js1.c_str(),
+ "", "", /*-----* adaptitem */
+
+ "", "", /*-----* session auth */
+ "", "", /* */
+ "", "", /*-----*/
+
+ "", "", /*-----* device admin */
+ "", "", /* */
+ "", "", /* */
+ "", "", /*-----*/
+ "", "", /*-----* dbtime */
+
+ Se_DO, "", /*~~~~~*/
+ Se_TC, js2.c_str(),
+ Se_DI, js3.c_str(),
+ Se_DC, js_.c_str(),
+ NULL );
+ } // if
+
+ if (strcmp( aKey.c_str(),Plugin_SE_Adapt )==0) {
+ return ConnectFunctions( aMod, aField,aFieldSize, true,
+ // ---- device admin (GetDBTime) ----
+ "Session_AdaptItem", jsA.c_str(),
+ NULL );
+ } // if
+
+
+ // The correct signature with VAR_String can be used now
+ keyOld= strcmp( aKey.c_str(),Plugin_SE_Auth_OLD )==0;
+ keyCur= strcmp( aKey.c_str(),Plugin_SE_Auth )==0;
+
+ if (keyCur || keyOld) {
+ js1= j.SgnI_X();
+ if (keyCur) js2= j.SgnS_X( jt // "(ILjava/lang/String; ...
+ + jvt // ... LVAR_String; ...
+ + jvt ); // ... LVAR_String;)S"
+
+ if (keyOld) js2= j.SgnS_X( jt // "(ILjava/lang/String; ...
+ + jt // ... Ljava/lang/String; ...
+ + jvt ); // ... LVAR_String;)S"
+
+ return ConnectFunctions( aMod, aField,aFieldSize, true,
+ // ---- session auth ----
+ Se_PM, js1.c_str(),
+ Se_LI, js2.c_str(),
+ Se_LO, js_.c_str(),
+ NULL );
+ } // if
+
+ if (strcmp( aKey.c_str(),Plugin_DV_Admin )==0) {
+ js1= j.SgnS_X( jt + jvt + jvt ); // "(ILjava/lang/String;LVAR_String;LVAR_String;)S"
+
+ return ConnectFunctions( aMod, aField,aFieldSize, true,
+ // ---- device admin ----
+ Se_CD, js1.c_str(),
+ Se_GN,jsvT.c_str(),
+ Se_SN, jsT.c_str(),
+ Se_SD, jsT.c_str(), // the same
+ NULL );
+ } // if
+
+ if (strcmp( aKey.c_str(),Plugin_DV_DBTime )==0) {
+ return ConnectFunctions( aMod, aField,aFieldSize, true,
+ // ---- device admin (GetDBTime) ----
+ Se_GT, jsvT.c_str(),
+ NULL );
+ } // if
+
+
+ // ---- datastore -------------------------------------------------
+ if (strcmp( aKey.c_str(),Plugin_DS_General )==0) {
+ js1= j.SgnI_X( jt );
+ js2= j.SgnV_X( );
+ js3= j.SgnV_X( jt );
+ js4= j.SgnV_X( "Z" + jt );
+
+ return ConnectFunctions( aMod, aField,aFieldSize, true,
+ // ---- datastore general ----
+ Da_CS, js1.c_str(),
+ Da_FS, js1.c_str(),
+ Da_TC, js2.c_str(),
+ Da_WL, js3.c_str(),
+ Da_DI, js4.c_str(),
+ NULL );
+ } // if
+
+
+ // "InsertMapItem" can be used now
+ keyOld= strcmp( aKey.c_str(),Plugin_DS_Admin_OLD )==0;
+ keyCur= strcmp( aKey.c_str(),Plugin_DS_Admin )==0;
+
+ if (keyCur || keyOld) {
+ cAppCharP proc_insM= "";
+ if (keyCur) proc_insM= Da_IM;
+
+ js1= j.SgnS_X( jt + jt + jvt ); // "(ILjava/lang/String;Ljava/lang/String;LVAR_String;)S"
+ js2= j.SgnZ_X( jmi + "Z" ); // "(ILMapID;Z)Z"
+ js3= j.SgnS_X( jmi ); // "(ILMapID;)S"
+
+ return ConnectFunctions( aMod, aField,aFieldSize, true,
+ // ---- datastore admin ----
+ Da_LA, js1.c_str(),
+ Da_SA, jsT.c_str(),
+ Da_RM, js2.c_str(),
+ proc_insM, js3.c_str(),
+ Da_UM, js3.c_str(), // 2nd
+ Da_DM, js3.c_str(), // 3rd
+ NULL );
+ } // if
+
+
+ // "DeleteBlob" can be used now
+ keyOld = strcmp( aKey.c_str(),Plugin_DS_Data_OLD1 )==0;
+ keyOld2= strcmp( aKey.c_str(),Plugin_DS_Data_OLD2 )==0;
+ keyCur = strcmp( aKey.c_str(),Plugin_DS_Data )==0;
+
+ if (keyCur || keyOld || keyOld2) {
+ if (keyOld) js1= jsT; // "(ILjava/lang/String;)S"
+ else js1= j.SgnS_X( jt + jt ); // "(ILjava/lang/String;Ljava/lang/String;)S"
+
+ cAppCharP proc_fli= "";
+ if (keyCur) proc_fli= Da_FLI;
+ cAppCharP proc_dss= "";
+ if (keyCur) proc_dss= Da_DSS;
+
+ js2= j.SgnS_X( "Z" + jvt ); // "(IZLVAR_String;)S"
+ js3= j.SgnS_X( jii + jii ); // "(ILItemID;LItemID;)S"
+ js4= j.SgnS_X( jii + jt ); // "(ILItemID;Ljava/lang/String;)S"
+ js5= j.SgnS_X( jii ); // "(ILItemID;)S"
+
+ return ConnectFunctions( aMod, aField,aFieldSize, true,
+ // ---- datastore data ----
+ Da_SR, js1.c_str(), // read
+ Da_ER, js_.c_str(),
+ Da_SW, js_.c_str(), // write
+ Da_EW, js2.c_str(),
+
+ /*-----*/
+ "", "", /* */
+ "", "", /* str */
+ "", "", /* */
+ "", "", /* */
+ /*-----*/
+ "", "", /* */
+ "", "", /* key */
+ "", "", /* */
+ "", "", /* */
+ /*-----*/
+
+ proc_fli, js3.c_str(), // independent
+ Da_MvI, js4.c_str(),
+ Da_DeI, js5.c_str(),
+ proc_dss, js_.c_str(),
+ NULL );
+ } // if
+
+ if (strcmp( aKey.c_str(),Plugin_DS_Data_Str )==0) {
+ js1= j.SgnS_X( jii + jvt + jvi + "Z" ); // "(ILItemID;LVAR_String;LVAR_int;Z)S"
+ js2= j.SgnS_X( jii + jvt ); // "(ILItemID;LVAR_String;)S"
+ js3= j.SgnS_X( jt + jii ); // "(ILjava/lang/String;LItemID;)S"
+ js4= j.SgnS_X( jt + jii + jii ); // "(ILjava/lang/String;LItemID;LItemID;)S"
+
+ return ConnectFunctions( aMod, aField,aFieldSize, true,
+ // ---- aItemData routines ----
+ Da_RN, js1.c_str(),
+ Da_RI, js2.c_str(),
+ Da_II, js3.c_str(),
+ Da_UI, js4.c_str(),
+ NULL );
+ } // if
+
+ if (strcmp( aKey.c_str(),Plugin_DS_Data_Key )==0) {
+ js1= j.SgnS_X( jii + j.fr + jvi + "Z" ); // "(ILItemID;ILVAR_int;Z)S"
+ js2= j.SgnS_X( jii + j.fr ); // "(ILItemID;I)S"
+ js3= j.SgnS_X( j.fr + jii ); // "(IILItemID;)S"
+ js4= j.SgnS_X( j.fr + jii + jii ); // "(IILItemID;LItemID;)S"
+
+ return ConnectFunctions( aMod, aField,aFieldSize, true,
+ // ---- aItemKey routines ----
+ Da_RNK, js1.c_str(),
+ Da_RIK, js2.c_str(),
+ Da_IIK, js3.c_str(),
+ Da_UIK, js4.c_str(),
+ NULL );
+ } // if
+
+ keyOld= strcmp( aKey.c_str(),Plugin_DS_Blob_OLD )==0;
+ keyCur= strcmp( aKey.c_str(),Plugin_DS_Blob )==0;
+
+ if (keyCur || keyOld) {
+ const char* proc_delB= "";
+ if (keyCur) proc_delB= Da_DB;
+
+ js1 = j.SgnS_X( jii // "(ILItemID;Ljava/lang/String;LVAR_byteArray; ...
+ + jt // ... LVAR_int;LVAR_int;ZLVAR_boolean;)S"
+ + LCP( jP, c_VAR_byteArray )
+ + jvi
+ + jvi + "Z"
+ + LCP( jP, c_VAR_bool ) );
+
+ js2 = j.SgnS_X( jii + jt + "[BIIZZ" ); // "(ILItemID;Ljava/lang/String;[BIIZZ)S"
+ js3 = j.SgnS_X( jii + jt ); // "(ILItemID;Ljava/lang/String;)S"
+
+ return ConnectFunctions( aMod, aField,aFieldSize, true,
+ // ---- datastore data ----
+ Da_RB, js1.c_str(),
+ Da_WB, js2.c_str(),
+ proc_delB, js3.c_str(),
+ NULL );
+ } // if
+
+ if (strcmp( aKey.c_str(),Plugin_DS_Adapt )==0) {
+ return ConnectFunctions( aMod, aField,aFieldSize, true,
+ "AdaptItem", jsA.c_str(),
+ NULL );
+ } // if
+
+ if (strcmp( aKey.c_str(),Plugin_Datastore )==0) {
+ // "(LVAR_xxx; ... (VAR_xxx = VAR_int/VAR_long)
+ js1= j.SgnS_V( jt // ... Ljava/lang/String; ...
+ + jdc // ... LDB_Callback; ...
+ + jt // ... Ljava/lang/String; ...
+ + jt ); // ... Ljava/lang/String;)S"
+
+ return ConnectFunctions( aMod, aField,aFieldSize, true,
+ Da_CC, js1.c_str(),
+
+ "", "", /*-----* general */
+ "", "", /* */
+ "", "", /* */
+ "", "", /* */
+ "", "", /*-----*/
+
+ "", "", /*-----* admin */
+ "", "", /* */
+ "", "", /* */
+ "", "", /* */
+ "", "", /* */
+ "", "", /*-----*/
+
+ /*-----* data read/write */
+ "", "", /* rd */
+ "", "", /* */
+ /*-----*/
+ "", "", /* wr */
+ "", "", /* */
+ /*-----*/
+ "", "", /* */
+ "", "", /* str */
+ "", "", /* */
+ "", "", /* */
+ /*-----*/
+ "", "", /* */
+ "", "", /* key */
+ "", "", /* */
+ "", "", /* */
+ /*-----*/
+ "", "", /* */
+ "", "", /* ind */
+ "", "", /* */
+ "", "", /* */
+ /*-----*/
+
+ "", "", /*-----* blobs */
+ "", "", /* */
+ "", "", /*-----*/
+
+ "", "", /*-----* adaptitem */
+
+ Da_DO, "", /*~~~~~*/ // general
+ Da_DC, js_.c_str(), // close
+ NULL );
+ } // if
+
+
+ // ---- ui context ------------------------------------------------
+ if (strcmp( aKey.c_str(),Plugin_UI )==0) {
+ // "(LVAR_xxx; ... (VAR_xxx = VAR_int/VAR_long)
+ js1= j.SgnS_V( jt // ... Ljava/lang/String; ...
+ + jdc ); // ... LDB_Callback;)S"
+
+ return ConnectFunctions( aMod, aField,aFieldSize, true,
+ // ---- ui context ----
+ "UI_CreateContext", js1.c_str(),
+ "UI_RunContext", js_.c_str(),
+ "UI_DeleteContext", js_.c_str(),
+ NULL );
+ } // if
+
+
+ // default settings
+ if (strcmp( aKey.c_str(),"" )==0) {
+ return ConnectFunctions( aMod, aField,aFieldSize, true,
+ // ---- module --------------------------------------------------
+ "", "", /*-----* start */
+ "", "", /* */
+ "", "", /*-----*/
+
+ "", "", /*-----* plugin params */
+
+ Mo_DO, "", /*~~~~~*/
+ Mo_DC, js_.c_str(),
+
+ // ---- session -------------------------------------------------
+ "", "", /*+*/
+ "", "", /*-*---* adaptitem */
+ /* */
+ "", "", /*-*---* session auth */
+ "", "", /* */
+ "", "", /*-*---*/
+ /* */
+ "", "", /*-*---* device admin */
+ "", "", /* */
+ "", "", /* */
+ "", "", /* */
+ "", "", /*-*---*(dbtime) */
+ /* */
+ "", "", /*+*/
+ "", "", /*+*/
+ "", "", /*+*/
+ "", "", /*+*/
+
+ // ---- datastore -----------------------------------------------
+ "", "", /*-* open */
+
+ "", "", /*-----* general */
+ "", "", /* */
+ "", "", /* */
+ "", "", /* */
+ "", "", /*-----*/
+
+ "", "", /*-----* admin */
+ "", "", /* */
+ "", "", /* */
+ "", "", /* */
+ "", "", /* */
+ "", "", /*-----*/
+
+ /*-----* data read/write */
+ "", "", /* rd */
+ "", "", /* */
+ /*-----*/
+ "", "", /* wr */
+ "", "", /* */
+ /*-----*/
+ "", "", /* */
+ "", "", /* str */
+ "", "", /* */
+ "", "", /* */
+ /*-----*/
+ "", "", /* */
+ "", "", /* key */
+ "", "", /* */
+ "", "", /* */
+ /*-----*/
+ "", "", /* */
+ "", "", /* ind */
+ "", "", /* */
+ "", "", /* */
+ /*-----*/
+
+ "", "", /*-----* blobs */
+ "", "", /* */
+ "", "", /*-----*/
+
+ "", "", /*-----* adaptitem */
+
+ "", "", /*-* general */
+ "", "", /*-* close */
+
+ // ---- ui context ------------------------------------------------
+ "", "", /*-* open */
+ "", "", /*-* run */
+ "", "", /*-* close */
+ NULL );
+ } // if
+
+ return DB_NotFound;
+} // DBApi_DLLAssign
+
+
+#if defined __cplusplus
+ } // namespace */
+#endif
+
+/* eof */
diff --git a/src/DB_interfaces/api_db/sync_dbapiconnect.h b/src/DB_interfaces/api_db/sync_dbapiconnect.h
new file mode 100755
index 0000000..c9ad9dd
--- /dev/null
+++ b/src/DB_interfaces/api_db/sync_dbapiconnect.h
@@ -0,0 +1,234 @@
+/*
+ * File: sync_dbapiconnect.h
+ *
+ * Author: Beat Forster (bfo@synthesis.ch)
+ *
+ * C/C++ Programming interface between
+ * the Synthesis SyncML engine
+ * and the database layer
+ *
+ * Copyright (c) 2005-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ *
+ * This module contains the method table and connection
+ * routines, which are used e.g. for the JNI plugin.
+ *
+ */
+
+#ifndef SYNC_DBAPICONNECT_H
+#define SYNC_DBAPICONNECT_H
+
+#include "sync_include.h"
+#include <string>
+
+#ifdef __cplusplus
+ namespace sysync {
+#endif
+
+
+/* -- configuration for the DB_Api class ------------------------------------------- */
+/* ---- MODULE ---------------- */
+typedef struct { /* start */
+ void* Module_CreateContext;
+ void* Module_Version;
+ void* Module_Capabilities;
+} Start_Methods;
+
+
+typedef struct { /* plugin params */
+ void* Module_PluginParams;
+} Param_Methods;
+
+
+/* ---- SESSION --------------- */
+typedef struct {
+ void* Session_PasswordMode; /* session auth */
+ void* Session_Login;
+ void* Session_Logout;
+} SE_Auth__Methods;
+
+
+typedef struct {
+ void* Session_CheckDevice; /* device admin */
+ void* Session_GetNonce;
+ void* Session_SaveNonce;
+ void* Session_SaveDeviceInfo;
+} DV_Admin_Methods;
+
+
+typedef struct {
+ void* Session_GetDBTime; /* dbtime */
+} DV_Time__Methods;
+
+
+typedef struct {
+ void* Session_AdaptItem; /* adaptitem */
+} SE_Adapt_Methods;
+
+
+typedef struct { /* --- session handling --- */
+ void* Session_CreateContext; /* open */
+
+ SE_Adapt_Methods seAdapt; /* adaptitem */
+ SE_Auth__Methods seAuth; /* auth */
+ DV_Admin_Methods dvAdmin; /* admin */
+ DV_Time__Methods dvTime; /* dbtime */
+
+ void* Session_DisposeObj; /* general */
+ void* Session_ThreadMayChangeNow;
+ void* Session_DispItems;
+
+ void* Session_DeleteContext; /* close */
+} SE_Methods;
+
+
+/* ---- DATASTORE ------------- */
+typedef struct {
+ void* ContextSupport; /* general */
+ void* FilterSupport;
+ void* ThreadMayChangeNow;
+ void* WriteLogData;
+ void* DispItems;
+} DS_General_Methods;
+
+
+typedef struct {
+ void* LoadAdminData; /* datastore admin */
+ void* SaveAdminData;
+ void* ReadNextMapItem;
+ void* InsertMapItem;
+ void* UpdateMapItem;
+ void* DeleteMapItem;
+} DS_Admin_Methods;
+
+
+typedef struct { /* string read/write */
+ void* ReadNextItem;
+ void* ReadItem;
+ void* InsertItem;
+ void* UpdateItem;
+} DS_Str_Methods;
+
+
+typedef struct { /* key read/write */
+ void* ReadNextItemAsKey;
+ void* ReadItemAsKey;
+ void* InsertItemAsKey;
+ void* UpdateItemAsKey;
+} DS_Key_Methods;
+
+
+typedef struct { /* independent */
+ void* FinalizeLocalID;
+ void* MoveItem;
+ void* DeleteItem;
+ void* DeleteSyncSet;
+} DS_Ind_Methods;
+
+
+typedef struct {
+ void* StartDataRead; /* read */
+ void* EndDataRead;
+
+ void* StartDataWrite; /* write */
+ void* EndDataWrite;
+
+ DS_Str_Methods str;
+ DS_Key_Methods key;
+ DS_Ind_Methods ind;
+} DS_Data_Methods;
+
+
+typedef struct { /* BLOBs */
+ void* ReadBlob;
+ void* WriteBlob;
+ void* DeleteBlob;
+} DS_Blob_Methods;
+
+
+typedef struct { /* adaptitem */
+ void* AdaptItem;
+} DS_Adapt_Methods;
+
+
+typedef struct { /* --- datastore handling --- */
+ void* CreateContext; /* open */
+
+ DS_General_Methods dsg; /* general */
+ DS_Admin_Methods dsAdmin; /* data admin */
+ DS_Data_Methods dsData; /* data read/write */
+ DS_Blob_Methods dsBlob; /* BLOBs */
+ DS_Adapt_Methods dsAdapt; /* adaptitem */
+
+ void* DisposeObj;
+ void* DeleteContext; /* close */
+} DS_Methods;
+
+
+
+typedef struct { /* --- ui context handling --- */
+ void* UI_CreateContext; /* open */
+ void* UI_RunContext; /* run */
+ void* UI_DeleteContext; /* close */
+} UI_Methods;
+
+
+
+/* ======= configuration for the DB_Api class ====== */
+typedef struct {
+ /* -- module ------------------------------------- */
+ Start_Methods start; /* start */
+ Param_Methods param; /* params */
+
+ void* Module_DisposeObj; /* general */
+ void* Module_DeleteContext;
+
+ /* -- session ------------------------------------ */
+ SE_Methods se;
+
+ /* -- datastore ---------------------------------- */
+ DS_Methods ds;
+
+ /* -- ui context --------------------------------- */
+ UI_Methods ui;
+} API_Methods;
+
+
+
+// the Java interface classes
+#define c_VAR_short "VAR_short"
+#define c_VAR_int "VAR_int"
+#define c_VAR_long "VAR_long"
+#define c_VAR_bool "VAR_boolean"
+#define c_VAR_String "VAR_String"
+#define c_VAR_byteArray "VAR_byteArray"
+
+#define c_JNI_ItemID "ItemID"
+#define c_JNI_MapID "MapID"
+#define c_JNI_TPI "TEngineProgressInfo"
+#define c_JNI_DB_Callback "DB_Callback"
+#define c_JNI_JCallback "JCallback"
+#define c_JNI_JCallback64 "JCallback64"
+
+
+#ifdef __cplusplus
+ // Support functions for creating Java signatures
+ string LCP ( string jP, string className ); // "L<jP>/<className>;
+ string JCS ( void ); // "Ljava/lang/String;"
+ string SgnS( string s= "" ); // "(<s>)S"
+ string SgnI( string s= "" ); // "(<s>)I"
+ string SgnV( string s= "" ); // "(<s>)V"
+
+ /*! Assign the DB_API interface functions and JNI signatures */
+ /* (Call ConnectFunctions) */
+ TSyError DBApi_DLLAssign( appPointer aMod, appPointer aField, memSize aFieldSize,
+ string aKey, bool a64bit, string jP= "" );
+#endif
+
+
+#if defined __cplusplus
+ } // namespace */
+#endif
+
+#endif /* SYNC_DBAPICONNECT_H */
+/* eof */
diff --git a/src/DB_interfaces/odbc_db/odbcapiagent.cpp b/src/DB_interfaces/odbc_db/odbcapiagent.cpp
new file mode 100644
index 0000000..5ecfc98
--- /dev/null
+++ b/src/DB_interfaces/odbc_db/odbcapiagent.cpp
@@ -0,0 +1,3895 @@
+/**
+ * @File odbcapiagent.cpp
+ *
+ * @Author Lukas Zeller (luz@synthesis.ch)
+ *
+ * @brief TODBCApiAgent
+ * ODBC based agent (client or server session) implementation
+ *
+ * Copyright (c) 2001-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ * @Date 2005-10-06 : luz : created from odbcdbagent
+ */
+
+#include "prefix_file.h"
+
+#ifdef SQL_SUPPORT
+
+// includes
+#include "odbcapids.h"
+#include "odbcapiagent.h"
+
+namespace sysync {
+
+
+// Support for SySync Diagnostic Tool
+#ifdef SYSYNC_TOOL
+
+// execute SQL command
+int execSQL(int argc, const char *argv[])
+{
+ if (argc<0) {
+ // help requested
+ CONSOLEPRINTF((" execsql <sql statement> [<maxrows>]"));
+ CONSOLEPRINTF((" Execute SQL statement via ODBC on the server database"));
+ return EXIT_SUCCESS;
+ }
+
+ TODBCApiAgent *odbcagentP = NULL;
+ const char *sql = NULL;
+
+ // show only one row by default
+ long maxrows=1;
+
+ // check for argument
+ if (argc<1) {
+ CONSOLEPRINTF(("argument containing SQL statement required"));
+ return EXIT_FAILURE;
+ }
+ sql = argv[0];
+ if (argc>=2) {
+ // second arg is maxrows
+ StrToLong(argv[1],maxrows);
+ }
+
+ // get ODBC session to work with
+ odbcagentP = dynamic_cast<TODBCApiAgent *>(
+ static_cast<TSyncSessionDispatch *>(getSyncAppBase())->getSySyToolSession()
+ );
+ if (!odbcagentP) {
+ CONSOLEPRINTF(("Config does not contain an ODBC server section"));
+ return EXIT_FAILURE;
+ }
+
+ // execute query
+ // - show DB where we will exec it
+ CONSOLEPRINTF(("Connecting to ODBC with connection string = '%s'",odbcagentP->fConfigP->fDBConnStr.c_str()));
+
+ SQLRETURN res;
+ SQLHSTMT statement=odbcagentP->newStatementHandle(odbcagentP->getODBCConnectionHandle());
+ try {
+ // set parameter
+ const int paramsiz=100;
+ SQLCHAR paramval[paramsiz];
+ SQLINTEGER lenorind=SQL_NULL_DATA;
+ res=SQLBindParameter(
+ statement,
+ 1, // parameter index
+ SQL_PARAM_OUTPUT, // inout
+ SQL_C_CHAR, // we want it as string
+ SQL_INTEGER, // parameter type
+ paramsiz, // column size
+ 0, // decimal digits
+ &paramval, // parameter value
+ paramsiz, // value buffer size
+ (SQLLEN*)&lenorind // length or indicator
+ );
+ odbcagentP->checkStatementError(res,statement);
+
+ // issue
+ res = SafeSQLExecDirect(
+ statement,
+ (SQLCHAR *)sql,
+ SQL_NTS
+ );
+ odbcagentP->checkStatementError(res,statement);
+ // show param
+ if (lenorind!=SQL_NULL_DATA || lenorind!=SQL_NO_TOTAL) {
+ CONSOLEPRINTF(("Returned parameter = '%s'",paramval));
+ }
+
+ // - get number of result columns
+ SQLSMALLINT numcolumns;
+ SQLSMALLINT stringlength;
+ // - get number of columns
+ res = SafeSQLNumResultCols(statement,&numcolumns);
+ odbcagentP->checkStatementError(res,statement);
+ // - fetch result row(s)
+ int rownum=0;
+ while (true) {
+ // try to fetch row
+ res=SafeSQLFetch(statement);
+ if (!odbcagentP->checkStatementHasData(res,statement))
+ break; // done
+ // we have a row to show
+ if (rownum>=maxrows) {
+ CONSOLEPRINTF(("\nMore rows in result than allowed to display"));
+ break;
+ }
+ // Show Row no
+ rownum++;
+ CONSOLEPRINTF(("\nResult Row #%d: ",rownum));
+ // fetch data
+ for (int i=1; i<=numcolumns; i++) {
+ // - get name of the column
+ const int maxnamelen=100;
+ SQLCHAR colname[maxnamelen];
+ SQLSMALLINT colnamelen;
+ SQLINTEGER dummy;
+ res=SQLColAttribute (
+ statement, // statement handle
+ i, // column number
+ SQL_DESC_BASE_COLUMN_NAME, // return column base name
+ colname, // col name return buffer
+ maxnamelen, // col name return buffer size
+ &colnamelen, // no string length expected
+ (SQLLEN*)&dummy // dummy
+ );
+ if (res!=SQL_SUCCESS) {
+ strcpy((char *)colname,"<error getting name>");
+ }
+ // - get data of the column in this row
+ const int maxdatalen=100;
+ SQLCHAR databuf[maxdatalen];
+ SQLINTEGER actualLength;
+ res = SQLGetData(
+ statement, // statement handle
+ i, // column number
+ SQL_C_CHAR, // target type: string
+ &databuf, // Target Value buffer pointer
+ maxdatalen, // max size of value
+ (SQLLEN*)&actualLength
+ );
+ if (res!=SQL_SUCCESS) break; // no more columns
+ if (actualLength==SQL_NULL_DATA || actualLength==SQL_NO_TOTAL) {
+ strcpy((char *)databuf,"<NULL>");
+ }
+ CONSOLEPRINTF((" %3d. %20s : %s",i,colname,databuf));
+ }
+ }
+ CONSOLEPRINTF((""));
+ SafeSQLCloseCursor(statement);
+ // dispose statement handle
+ SafeSQLFreeHandle(SQL_HANDLE_STMT,statement);
+ }
+ catch (exception &e) {
+ // dispose statement handle
+ SafeSQLCloseCursor(statement);
+ SafeSQLFreeHandle(SQL_HANDLE_STMT,statement);
+ // show error
+ CONSOLEPRINTF(("ODBC Error : %s",e.what()));
+ return EXIT_FAILURE;
+ }
+ catch (...) {
+ // dispose statement handle
+ SafeSQLCloseCursor(statement);
+ SafeSQLFreeHandle(SQL_HANDLE_STMT,statement);
+ // show error
+ CONSOLEPRINTF(("ODBC caused unknown exception"));
+ return EXIT_FAILURE;
+ }
+ return EXIT_SUCCESS;
+} // execSQL
+
+#endif // SYSYNC_TOOL
+
+
+
+
+// names for transaction isolation modes
+const char *TxnIsolModeNames[numTxnIsolModes] = {
+ // Note: these MUST be in the same order as
+ // Win32/ODBC defined SQL_TXN_READ_UNCOMMITTED..SQL_TXN_SERIALIZABLE
+ // bits appear in the transaction bitmasks.
+ "read-uncommitted",
+ "read-committed",
+ "repeatable",
+ "serializable",
+ // special values
+ "none",
+ "default"
+};
+
+
+// Config
+
+TOdbcAgentConfig::TOdbcAgentConfig(TConfigElement *aParentElement) :
+ TCustomAgentConfig(aParentElement)
+{
+ // nop so far
+} // TOdbcAgentConfig::TOdbcAgentConfig
+
+
+TOdbcAgentConfig::~TOdbcAgentConfig()
+{
+ clear();
+} // TOdbcAgentConfig::~TOdbcAgentConfig
+
+
+// init defaults
+void TOdbcAgentConfig::clear(void)
+{
+ // init defaults
+ #ifdef ODBCAPI_SUPPORT
+ // - ODBC data source
+ fDataSource.erase();
+ fUsername.erase();
+ fPassword.erase();
+ #ifdef SCRIPT_SUPPORT
+ fAfterConnectScript.erase();
+ #endif
+ // - usually, SQLSetConnectAttr() is not problematic
+ fNoConnectAttrs=false;
+ // - use medium timeout by default
+ fODBCTimeout=30;
+ // - use DB default
+ fODBCTxnMode=txni_default;
+ // - cursor library usage
+ fUseCursorLib=false; // use driver (this is also the ODBC default)
+ // - device table
+ fGetDeviceSQL.erase();
+ fNewDeviceSQL.erase();
+ fSaveNonceSQL.erase();
+ fSaveInfoSQL.erase();
+ fSaveDevInfSQL.erase();
+ fLoadDevInfSQL.erase();
+ // - auth
+ fUserKeySQL.erase();
+ fClearTextPw=true; // otherwise, Nonce auth is not possible
+ fMD5UserPass=false; // exclusive with ClearTextPw
+ fMD5UPAsHex=false;
+ // - datetime
+ fGetCurrentDateTimeSQL.erase();
+ // - statement to save log info
+ fWriteLogSQL.erase();
+ #endif // ODBCAPI_SUPPORT
+ fQuotingMode=qm_duplsingle; // default to what was hard-coded before it became configurable in 2.1.1.5
+ // clear inherited
+ inherited::clear();
+} // TOdbcAgentConfig::clear
+
+
+#ifdef SCRIPT_SUPPORT
+
+
+// ODBC agent specific script functions
+// ====================================
+
+
+class TODBCCommonFuncs {
+public:
+
+ // string DBLITERAL(variant value, string dbfieldtype)
+ static void func_DBLiteral(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ TODBCApiAgent *agentP = static_cast<TODBCApiAgent *>(aFuncContextP->getCallerContext());
+ string tname,literal;
+ TItemField *fieldP = aFuncContextP->getLocalVar(0); // the field to be converted
+ aFuncContextP->getLocalVar(1)->getAsString(tname); // DB type string
+ // search DB type, default to string
+ sInt16 ty;
+ TDBFieldType dbfty=dbft_string;
+ if (StrToEnum(DBFieldTypeNames,numDBfieldTypes,ty,tname.c_str()))
+ dbfty=(TDBFieldType)ty;
+ // now create literal
+ literal.erase();
+ TOdbcAgentConfig *cfgP = static_cast<TODBCApiAgent *>(aFuncContextP->getCallerContext())->fConfigP;
+ timecontext_t tctx;
+ TCharSets chs;
+ TLineEndModes lem;
+ TQuotingModes qm;
+ if (agentP->fScriptContextDatastore) {
+ TOdbcDSConfig *dsCfgP = static_cast<TOdbcDSConfig *>(
+ static_cast<TODBCApiDS *>(agentP->fScriptContextDatastore)->fConfigP
+ );
+ tctx=dsCfgP->fDataTimeZone;
+ chs=dsCfgP->fDataCharSet;
+ lem=dsCfgP->fDataLineEndMode;
+ qm=dsCfgP->fQuotingMode;
+ }
+ else {
+ tctx=cfgP->fCurrentDateTimeZone;
+ chs=cfgP->fDataCharSet;
+ lem=cfgP->fDataLineEndMode;
+ qm=cfgP->fQuotingMode;
+ }
+ sInt32 s=0;
+ agentP->appendFieldValueLiteral(
+ *fieldP,dbfty,0,literal,
+ chs, lem, qm, tctx,
+ s
+ );
+ // return it
+ aTermP->setAsString(literal);
+ }; // func_DBLiteral
+
+
+ #ifdef ODBCAPI_SUPPORT
+
+ // SETDBCONNECTSTRING(string dbconnectstring)
+ // sets DB connect string, useful in <logininitscript> to switch database
+ // depending on user
+ static void func_SetDBConnectString(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ TODBCApiAgent *agentP = static_cast<TODBCApiAgent *>(aFuncContextP->getCallerContext());
+ // - close the current script statement (if any)
+ agentP->commitAndCloseScriptStatement();
+ // - close the current connection (if any)
+ agentP->closeODBCConnection(agentP->fODBCConnectionHandle);
+ // - assign new DB connection string which will be used for next connection
+ aFuncContextP->getLocalVar(0)->getAsString(agentP->fSessionDBConnStr); // override default DB connect string from config
+ } // func_SetDBConnectString
+
+
+ // SETDBPASSWORD(string password)
+ // sets password, useful when using SETDBCONNECTSTRING() to switch databases
+ // depending on user
+ static void func_SetDBPassword(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ TODBCApiAgent *agentP = static_cast<TODBCApiAgent *>(aFuncContextP->getCallerContext());
+ // - assign new DB connection password which will be used for next connection
+ aFuncContextP->getLocalVar(0)->getAsString(agentP->fSessionDBPassword); // override default DB password from config
+ } // func_SetDBPassword
+
+ #endif // ODBCAPI_SUPPORT
+
+
+
+ // integer SQLEXECUTE(string statement)
+ // executes statement, returns 0 or ODBC error code
+ static void func_SQLExecute(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ string sql;
+ #ifdef ODBCAPI_SUPPORT
+ SQLRETURN res;
+ #endif
+ TODBCApiAgent *agentP = static_cast<TODBCApiAgent *>(aFuncContextP->getCallerContext());
+ TODBCApiDS *datastoreP = static_cast<TODBCApiDS *>(agentP->fScriptContextDatastore);
+ aFuncContextP->getLocalVar(0)->getAsString(sql); // get SQL to be executed
+ bool ok = true; // assume ok
+ SQLHSTMT stmt = SQL_NULL_HANDLE;
+ try {
+ if (datastoreP) {
+ // do datastore-level substitutions and parameter mapping
+ datastoreP->resetSQLParameterMaps();
+ datastoreP->DoDataSubstitutions(
+ sql,
+ datastoreP->fConfigP->fFieldMappings.fFieldMapList,
+ 0, // standard set
+ false, // not for write
+ false, // not for update
+ aFuncContextP->fTargetItemP // item in this context, if any
+ );
+ #ifdef SQLITE_SUPPORT
+ if (!datastoreP->fUseSQLite)
+ #endif
+ {
+ #ifdef ODBCAPI_SUPPORT
+ stmt = agentP->getScriptStatement();
+ #endif // ODBCAPI_SUPPORT
+ }
+ }
+ else {
+ #ifdef ODBCAPI_SUPPORT
+ // do agent-level substitutions and parameter mapping
+ stmt=agentP->getScriptStatement();
+ agentP->resetSQLParameterMaps();
+ agentP->DoSQLSubstitutions(sql);
+ #else
+ // %%% no session level SQLEXECUTE for now
+ aTermP->setAsBoolean(false);
+ POBJDEBUGPUTSX(aFuncContextP->getSession(),DBG_ERROR,"SQLEXECUTE() can only be used in datastore context for non-ODBC!");
+ return;
+ #endif
+ }
+ // execute SQL statement
+ #ifdef SYDEBUG
+ if (POBJDEBUGTEST(aFuncContextP->getSession(),DBG_SCRIPTS+DBG_DBAPI)) {
+ POBJDEBUGPUTSX(aFuncContextP->getSession(),DBG_DBAPI,"SQL issued by SQLEXECUTE() script function:");
+ POBJDEBUGPUTSX(aFuncContextP->getSession(),DBG_DBAPI,sql.c_str());
+ }
+ else {
+ POBJDEBUGPUTSX(aFuncContextP->getSession(),DBG_DBAPI,"SQLEXECUTE() executes a statement (hidden because script debugging is off)");
+ }
+ #endif
+ if (datastoreP) {
+ datastoreP->prepareSQLStatement(stmt,sql.c_str(),true,NULL);
+ datastoreP->bindSQLParameters(stmt,true);
+ // execute in datastore
+ datastoreP->execSQLStatement(stmt, sql, true, NULL, true);
+ // do datastore-level parameter mapping
+ datastoreP->saveAndCleanupSQLParameters(stmt,true);
+ }
+ #ifdef ODBCAPI_SUPPORT
+ else {
+ agentP->bindSQLParameters(stmt);
+ // execute
+ res = SafeSQLExecDirect(
+ stmt,
+ (SQLCHAR *)sql.c_str(),
+ SQL_NTS
+ );
+ agentP->checkStatementHasData(res,stmt); // treat NO_DATA error as ok
+ // do agent-level parameter mapping
+ agentP->saveAndCleanupSQLParameters(stmt);
+ }
+ #endif // ODBCAPI_SUPPORT
+ }
+ catch (exception &e) {
+ POBJDEBUGPRINTFX(aFuncContextP->getSession(),DBG_ERROR,(
+ "SQLEXECUTE() caused Error: %s",
+ e.what()
+ ));
+ ok=false;
+ }
+ // return ok status
+ aTermP->setAsBoolean(ok);
+ }; // func_SQLExecute
+
+
+ // integer SQLFETCHROW()
+ // fetches next row, returns true if data found
+ static void func_SQLFetchRow(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ #ifdef ODBCAPI_SUPPORT
+ SQLRETURN res;
+ #endif
+ TODBCApiAgent *agentP = static_cast<TODBCApiAgent *>(aFuncContextP->getCallerContext());
+ TODBCApiDS *datastoreP = static_cast<TODBCApiDS *>(agentP->fScriptContextDatastore);
+ bool ok = true; // assume ok
+ try {
+ if (datastoreP) {
+ ok=datastoreP->fetchNextRow(agentP->fScriptStatement,true); // always data access
+ }
+ #ifdef ODBCAPI_SUPPORT
+ else {
+ // - fetch result row
+ res=SafeSQLFetch(agentP->getScriptStatement());
+ ok=agentP->checkStatementHasData(res,agentP->getScriptStatement());
+ }
+ #endif
+ }
+ catch (exception &e) {
+ POBJDEBUGPRINTFX(aFuncContextP->getSession(),DBG_ERROR,(
+ "SQLFETCHROW() caused Error: %s",
+ e.what()
+ ));
+ ok=false;
+ }
+ // return ok status
+ aTermP->setAsBoolean(ok);
+ }; // func_SQLFetchRow
+
+
+ // integer SQLGETCOLUMN(integer index, &field, string dbtype)
+ // gets value of next column into specified variable
+ // returns true if successful
+ static void func_SQLGetColumn(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ TODBCApiAgent *agentP = static_cast<TODBCApiAgent *>(aFuncContextP->getCallerContext());
+ TODBCApiDS *datastoreP = static_cast<TODBCApiDS *>(agentP->fScriptContextDatastore);
+
+ bool ok = true; // assume ok
+ // get column index
+ sInt16 colindex = aFuncContextP->getLocalVar(0)->getAsInteger();
+ // get field reference
+ TItemField *fldP = aFuncContextP->getLocalVar(1);
+ // get DB type for field
+ string tname;
+ aFuncContextP->getLocalVar(2)->getAsString(tname); // DB type string
+ sInt16 ty;
+ TDBFieldType dbfty=dbft_string; // default to string
+ if (StrToEnum(DBFieldTypeNames,numDBfieldTypes,ty,tname.c_str()))
+ dbfty=(TDBFieldType)ty;
+ // get DB params
+ TOdbcAgentConfig *cfgP = agentP->fConfigP;
+ timecontext_t tctx;
+ bool uzo;
+ #ifdef SQLITE_SUPPORT
+ bool sqlite=false;
+ #endif
+ TCharSets chs;
+ if (datastoreP) {
+ TOdbcDSConfig *dsCfgP = static_cast<TOdbcDSConfig *>(datastoreP->fConfigP);
+ tctx=dsCfgP->fDataTimeZone;
+ chs=dsCfgP->fDataCharSet;
+ uzo=dsCfgP->fUserZoneOutput;
+ #ifdef SQLITE_SUPPORT
+ sqlite=datastoreP->fUseSQLite;
+ #endif
+ }
+ else {
+ tctx=cfgP->fCurrentDateTimeZone;
+ chs=cfgP->fDataCharSet;
+ uzo=false;
+ #ifdef SQLITE_SUPPORT
+ sqlite=false; // no SQLite support at agent level
+ #endif
+ }
+ // get value now
+ try {
+ #ifdef SQLITE_SUPPORT
+ if (sqlite && datastoreP) {
+ // - get column value as field
+ if (!agentP->getSQLiteColValueAsField(
+ datastoreP->fSQLiteStmtP,
+ colindex-1, // SQLITE colindex starts at 0, not 1 like in ODBC
+ dbfty, fldP,
+ chs, tctx, uzo
+ ))
+ fldP->unAssign(); // NULL -> unassigned
+ }
+ else
+ #endif
+ {
+ #ifdef ODBCAPI_SUPPORT
+ // - get column value as field
+ if (!agentP->getColumnValueAsField(
+ agentP->getScriptStatement(), colindex, dbfty, fldP,
+ chs, tctx, uzo
+ ))
+ fldP->unAssign(); // NULL -> unassigned
+ #endif
+ }
+ }
+ catch (exception &e) {
+ POBJDEBUGPRINTFX(aFuncContextP->getSession(),DBG_ERROR,(
+ "SQLGETCOLUMN() caused Error: %s",
+ e.what()
+ ));
+ ok=false;
+ }
+ // return ok status
+ aTermP->setAsBoolean(ok);
+ }; // func_SQLGetColumn
+
+
+ // SQLCOMMIT()
+ // commits agent-level transactions
+ static void func_SQLCommit(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ TODBCApiAgent *agentP = static_cast<TODBCApiAgent *>(aFuncContextP->getCallerContext());
+ TODBCApiDS *datastoreP = static_cast<TODBCApiDS *>(agentP->fScriptContextDatastore);
+ if (datastoreP) {
+ // we might need finalizing (for SQLite...)
+ datastoreP->finalizeSQLStatement(agentP->fScriptStatement, true);
+ }
+ #ifdef ODBCAPI_SUPPORT
+ try {
+ agentP->commitAndCloseScriptStatement();
+ /*
+ if (agentP->fScriptStatement!=SQL_NULL_HANDLE) {
+ // only commit if we have used a statement at all in scripts
+ SafeSQLFreeHandle(SQL_HANDLE_STMT,agentP->fScriptStatement);
+ agentP->fScriptStatement=SQL_NULL_HANDLE;
+ SafeSQLEndTran(SQL_HANDLE_DBC,agentP->getODBCConnectionHandle(),SQL_COMMIT);
+ }
+ */
+ }
+ catch (exception &e) {
+ POBJDEBUGPRINTFX(aFuncContextP->getSession(),DBG_ERROR,(
+ "SQLCOMMIT() caused Error: %s",
+ e.what()
+ ));
+ }
+ #endif // ODBCAPI_SUPPORT
+ }; // func_SQLCommit
+
+
+ // SQLROLLBACK()
+ // rollback agent-level transactions
+ static void func_SQLRollback(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ TODBCApiAgent *agentP = static_cast<TODBCApiAgent *>(aFuncContextP->getCallerContext());
+ TODBCApiDS *datastoreP = static_cast<TODBCApiDS *>(agentP->fScriptContextDatastore);
+ if (datastoreP) {
+ // we might need finalizing (for SQLite...)
+ datastoreP->finalizeSQLStatement(agentP->fScriptStatement, true);
+ }
+ #ifdef ODBCAPI_SUPPORT
+ try {
+ if (agentP->fScriptStatement!=SQL_NULL_HANDLE) {
+ // only roll back if we have used a statement at all in scripts
+ SafeSQLFreeHandle(SQL_HANDLE_STMT,agentP->fScriptStatement);
+ agentP->fScriptStatement=SQL_NULL_HANDLE;
+ SafeSQLEndTran(SQL_HANDLE_DBC,agentP->getODBCConnectionHandle(),SQL_ROLLBACK);
+ }
+ }
+ catch (exception &e) {
+ POBJDEBUGPRINTFX(aFuncContextP->getSession(),DBG_ERROR,(
+ "SQLROLLBACK() caused Error: %s",
+ e.what()
+ ));
+ }
+ #endif // ODBCAPI_SUPPORT
+ }; // func_SQLRollback
+
+}; // TODBCCommonFuncs
+
+const uInt8 param_DBLiteral[] = { VAL(fty_none), VAL(fty_string) };
+const uInt8 param_OneString[] = { VAL(fty_string) };
+const uInt8 param_SQLGetColumn[] = { VAL(fty_integer), REF(fty_none), VAL(fty_string) };
+
+// builtin function defs for ODBC database and login contexts
+const TBuiltInFuncDef ODBCAgentAndDSFuncDefs[] = {
+ // generic DB access
+ { "DBLITERAL", TODBCCommonFuncs::func_DBLiteral, fty_string, 2, param_DBLiteral }, // note that there's a second version of this in ODBCDatastore
+ #ifdef ODBCAPI_SUPPORT
+ { "SETDBCONNECTSTRING", TODBCCommonFuncs::func_SetDBConnectString, fty_none, 1, param_OneString },
+ { "SETDBPASSWORD", TODBCCommonFuncs::func_SetDBPassword, fty_none, 1, param_OneString },
+ #endif // ODBCAPI_SUPPORT
+ { "SQLEXECUTE", TODBCCommonFuncs::func_SQLExecute, fty_integer, 1, param_OneString },
+ { "SQLFETCHROW", TODBCCommonFuncs::func_SQLFetchRow, fty_integer, 0, NULL },
+ { "SQLGETCOLUMN", TODBCCommonFuncs::func_SQLGetColumn, fty_integer, 3, param_SQLGetColumn },
+ { "SQLCOMMIT", TODBCCommonFuncs::func_SQLCommit, fty_none, 0, NULL },
+ { "SQLROLLBACK", TODBCCommonFuncs::func_SQLRollback, fty_none, 0, NULL }
+};
+
+
+#ifdef BASED_ON_BINFILE_CLIENT
+
+// Binfile based version just has the SQL access functions
+
+// function table for connectionscript
+const TFuncTable ODBCAgentFuncTable = {
+ sizeof(ODBCAgentAndDSFuncDefs) / sizeof(TBuiltInFuncDef), // size of table
+ ODBCAgentAndDSFuncDefs, // table pointer
+ NULL // no chain func
+};
+
+#else
+
+// Full range of functions for non-binfile-based version
+
+// chain from ODBC agent funcs to Custom agent Funcs
+extern const TFuncTable CustomAgentFuncTable2;
+static void *ODBCAgentChainFunc1(void *&aCtx)
+{
+ // caller context remains unchanged
+ // -> no change needed
+ // next table is Custom Agent's general function table
+ return (void *)&CustomAgentFuncTable2;
+} // ODBCAgentChainFunc1
+
+// function table which is chained from login-context function table
+const TFuncTable ODBCAgentFuncTable2 = {
+ sizeof(ODBCAgentAndDSFuncDefs) / sizeof(TBuiltInFuncDef), // size of table
+ ODBCAgentAndDSFuncDefs, // table pointer
+ ODBCAgentChainFunc1 // chain to non-ODBC specific agent Funcs
+};
+
+// chain from login context agent funcs to general agent funcs
+extern const TFuncTable ODBCDSFuncTable2;
+static void *ODBCAgentChainFunc(void *&aCtx)
+{
+ // caller context remains unchanged
+ // -> no change needed
+ // next table is Agent's general function table
+ return (void *)&ODBCAgentFuncTable2;
+} // ODBCAgentChainFunc
+
+// function table for login context scripts
+// Note: ODBC agent has no login-context specific functions, but is just using those from customImplAgent
+const TFuncTable ODBCAgentFuncTable = {
+ sizeof(CustomAgentFuncDefs) / sizeof(TBuiltInFuncDef), // size of table
+ CustomAgentFuncDefs, // table pointer
+ ODBCAgentChainFunc // chain to general agent funcs.
+};
+
+
+#endif // not BASED_ON_BINFILE_CLIENT
+
+
+// chain from agent funcs to ODBC local datastore funcs (when chained via ODBCDSFuncTable1
+extern const TFuncTable ODBCDSFuncTable2;
+static void *ODBCDSChainFunc1(void *&aCtx)
+{
+ // caller context for datastore-level functions is the datastore pointer
+ if (aCtx)
+ aCtx = static_cast<TODBCApiAgent *>(aCtx)->fScriptContextDatastore;
+ // next table is ODBC datastore's
+ return (void *)&ODBCDSFuncTable2;
+} // ODBCDSChainFunc1
+
+// function table for linking in Custom agent level functions between ODBC agent and ODBC DS level functions
+const TFuncTable ODBCAgentFuncTable3 = {
+ sizeof(CustomAgentAndDSFuncDefs) / sizeof(TBuiltInFuncDef), // size of agent's table
+ CustomAgentAndDSFuncDefs, // table pointer to agent's general purpose (non login-context specific) funcs
+ ODBCDSChainFunc1 // NOW finally chain back to ODBC datastore level DB functions
+};
+
+
+// chain from ODBC agent funcs to generic agent funcs and THEN back to ODBC DS funcs
+extern const TFuncTable ODBCDSFuncTable2;
+static void *ODBCDSChainFunc2(void *&aCtx)
+{
+ // caller context remains unchanged
+ // -> no change needed
+ // next table is Agent's general function table
+ return (void *)&ODBCAgentFuncTable3;
+} // ODBCDSChainFunc2
+
+// function table which is used by ODBC datastore scripts to access agent-level funcs and then chain
+// back to datastore level funcs
+const TFuncTable ODBCDSFuncTable1 = {
+ sizeof(ODBCAgentAndDSFuncDefs) / sizeof(TBuiltInFuncDef), // size of agent's table
+ ODBCAgentAndDSFuncDefs, // table pointer to agent's general purpose (non login-context specific) funcs
+ ODBCDSChainFunc2 // first chain to Custom Agent level functions, but then back to ODBC datastore level DB functions
+};
+
+
+#endif
+
+// config element parsing
+bool TOdbcAgentConfig::localStartElement(const char *aElementName, const char **aAttributes, sInt32 aLine)
+{
+ // checking the elements
+ #ifdef ODBCAPI_SUPPORT
+ // - ODBC connection
+ if (strucmp(aElementName,"datasource")==0)
+ expectString(fDataSource);
+ else if (strucmp(aElementName,"dbuser")==0)
+ expectString(fUsername);
+ else if (strucmp(aElementName,"dbpass")==0)
+ expectString(fPassword);
+ else if (strucmp(aElementName,"dbconnectionstring")==0)
+ expectString(fDBConnStr);
+ // - odbc timeout
+ else if (strucmp(aElementName,"dbtimeout")==0)
+ expectInt32(fODBCTimeout);
+ // - transaction mode
+ else if (strucmp(aElementName,"transactionmode")==0)
+ expectEnum(sizeof(fODBCTxnMode),&fODBCTxnMode,TxnIsolModeNames,numTxnIsolModes);
+ // - preventing use of SQLSetConnectAttr()
+ else if (strucmp(aElementName,"preventconnectattrs")==0)
+ expectBool(fNoConnectAttrs);
+ // - cursor library usage
+ else if (strucmp(aElementName,"usecursorlib")==0)
+ expectBool(fUseCursorLib);
+ #ifdef SCRIPT_SUPPORT
+ else if (strucmp(aElementName,"afterconnectscript")==0)
+ expectScript(fAfterConnectScript,aLine,getAgentFuncTableP());
+ #endif
+ #ifdef HAS_SQL_ADMIN
+ // - device table
+ else if (strucmp(aElementName,"getdevicesql")==0)
+ expectString(fGetDeviceSQL);
+ else if (strucmp(aElementName,"newdevicesql")==0)
+ expectString(fNewDeviceSQL);
+ else if (strucmp(aElementName,"savenoncesql")==0)
+ expectString(fSaveNonceSQL);
+ else if (strucmp(aElementName,"saveinfosql")==0)
+ expectString(fSaveInfoSQL);
+ else if (strucmp(aElementName,"savedevinfsql")==0)
+ expectString(fSaveDevInfSQL);
+ else if (strucmp(aElementName,"loaddevinfsql")==0)
+ expectString(fLoadDevInfSQL);
+ // - user auth SQL
+ else if (strucmp(aElementName,"userkeysql")==0)
+ expectString(fUserKeySQL);
+ else if (strucmp(aElementName,"cleartextpw")==0)
+ expectBool(fClearTextPw);
+ else if (strucmp(aElementName,"md5userpass")==0)
+ expectBool(fMD5UserPass);
+ else if (strucmp(aElementName,"md5hex")==0)
+ expectBool(fMD5UPAsHex);
+ // - database time SQL
+ else if (strucmp(aElementName,"timestampsql")==0)
+ expectString(fGetCurrentDateTimeSQL);
+ // - statement to save log info
+ else if (strucmp(aElementName,"writelogsql")==0)
+ expectString(fWriteLogSQL);
+ else
+ #endif // HAS_SQL_ADMIN
+ #endif // ODBCAPI_SUPPORT
+ // - quoting mode
+ if (strucmp(aElementName,"quotingmode")==0)
+ expectEnum(sizeof(fQuotingMode),&fQuotingMode,quotingModeNames,numQuotingModes);
+ // - none known here
+ else
+ return inherited::localStartElement(aElementName,aAttributes,aLine);
+ // ok
+ return true;
+} // TOdbcAgentConfig::localStartElement
+
+
+// resolve
+void TOdbcAgentConfig::localResolve(bool aLastPass)
+{
+ if (aLastPass) {
+ #ifdef ODBCAPI_SUPPORT
+ // check for required settings
+ // - create fDBConnStr from dsn,user,pw if none specified explicitly
+ if (fDBConnStr.empty()) {
+ // Note: we do not add the password here as fDBConnStr is shown in logs.
+ StringObjPrintf(fDBConnStr,"DSN=%s;UID=%s;",fDataSource.c_str(),fUsername.c_str());
+ }
+ #ifdef HAS_SQL_ADMIN
+ if (fClearTextPw && fMD5UserPass)
+ throw TConfigParseException("only one of 'cleartextpw' and 'md5userpass' can be set");
+ #ifndef SYSYNC_CLIENT
+ if (fAutoNonce && !fClearTextPw && !fMD5UserPass)
+ throw TConfigParseException("if 'autononce' is set, 'cleartextpw' or 'md5userpass' MUST be set as well");
+ #endif
+ #ifdef SYSYNC_CLIENT
+ if (fNoLocalDBLogin && !fUserKeySQL.empty())
+ throw TConfigParseException("'nolocaldblogin' is not allowed when 'userkeysql' is defined");
+ #endif
+ #endif // HAS_SQL_ADMIN
+ #endif // ODBCAPI_SUPPORT
+ }
+ // resolve inherited
+ // - Note: this resolves the ancestor's scripts first
+ inherited::localResolve(aLastPass);
+} // TOdbcAgentConfig::localResolve
+
+
+
+#ifdef SCRIPT_SUPPORT
+// resolve scripts
+void TOdbcAgentConfig::ResolveAPIScripts(void)
+{
+ #ifdef ODBCAPI_SUPPORT
+ // afterconnect script has it's own context as it might be called nested in other scripts (e.g. triggered by SQLEXECUTE)
+ TScriptContext *ctxP = NULL;
+ TScriptContext::resolveScript(getSyncAppBase(),fAfterConnectScript,ctxP,NULL);
+ if (ctxP) delete ctxP;
+ #endif
+} // TOdbcAgentConfig::localResolve
+#endif
+
+
+/* public TODBCApiAgent members */
+
+
+#ifdef SYSYNC_CLIENT
+TODBCApiAgent::TODBCApiAgent(TSyncClientBase *aSyncClientBaseP, const char *aSessionID) :
+ TCustomImplAgent(aSyncClientBaseP, aSessionID),
+#else
+TODBCApiAgent::TODBCApiAgent(TSyncAppBase *aAppBaseP, TSyncSessionHandle *aSessionHandleP, const char *aSessionID) :
+ TCustomImplAgent(aAppBaseP, aSessionHandleP, aSessionID),
+#endif
+ fConfigP(NULL)
+ #ifdef ODBCAPI_SUPPORT
+ ,fODBCConnectionHandle(SQL_NULL_HANDLE)
+ ,fODBCEnvironmentHandle(SQL_NULL_HANDLE)
+ #ifdef SCRIPT_SUPPORT
+ ,fScriptStatement(SQL_NULL_HANDLE)
+ ,fAfterConnectContext(NULL)
+ #endif
+ #endif // ODBCAPI_SUPPORT
+{
+ // get config for agent and save direct link to agent config for easy reference
+ fConfigP = static_cast<TOdbcAgentConfig *>(getRootConfig()->fAgentConfigP);
+ // Note: Datastores are already created from config
+ #ifdef SCRIPT_SUPPORT
+ #ifdef ODBCAPI_SUPPORT
+ // - rebuild afterconnect script
+ TScriptContext::rebuildContext(getSyncAppBase(),fConfigP->fAfterConnectScript,fAfterConnectContext,this,true);
+ // - assign default DB connection string and password
+ fSessionDBConnStr = fConfigP->fDBConnStr;
+ fSessionDBPassword = fConfigP->fPassword;
+ #endif
+ #endif
+} // TODBCApiAgent::TODBCApiAgent
+
+
+// destructor
+TODBCApiAgent::~TODBCApiAgent()
+{
+ // make sure everything is terminated BEFORE destruction of hierarchy begins
+ TerminateSession();
+} // TODBCApiAgent::~TODBCApiAgent
+
+
+// Terminate session
+void TODBCApiAgent::TerminateSession()
+{
+ if (!fTerminated) {
+ string msg,state;
+
+ // Note that the following will happen BEFORE destruction of
+ // individual datastores, so make sure datastore have their ODBC
+ // stuff finished before disposing environment
+ InternalResetSession();
+ #ifdef ODBCAPI_SUPPORT
+ #ifdef SCRIPT_SUPPORT
+ // get rid of afterconnect context
+ if (fAfterConnectContext) delete fAfterConnectContext;
+ #endif
+ SQLRETURN res;
+ if (fODBCEnvironmentHandle!=SQL_NULL_HANDLE) {
+ // release the enviroment
+ res=SafeSQLFreeHandle(SQL_HANDLE_ENV,fODBCEnvironmentHandle);
+ if (getODBCError(res,msg,state,SQL_HANDLE_ENV,fODBCEnvironmentHandle)) {
+ DEBUGPRINTFX(DBG_ERROR,("~TODBCApiAgent: SQLFreeHandle(ENV) failed: %s",msg.c_str()));
+ }
+ fODBCEnvironmentHandle=SQL_NULL_HANDLE;
+ }
+ #endif
+ // Make sure datastores know that the agent will go down soon
+ announceDestruction();
+ }
+ inherited::TerminateSession();
+} // TODBCApiAgent::TerminateSession
+
+
+
+// Reset session
+void TODBCApiAgent::InternalResetSession(void)
+{
+ // reset all datastores now to make sure ODBC is reset before we close the
+ // global connection and the environment handle (if called by destructor)!
+ // (Note: TerminateDatastores() will be called again by ancestors)
+ TerminateDatastores();
+ #ifdef ODBCAPI_SUPPORT
+ #ifdef SCRIPT_SUPPORT
+ // commit connection (eventual scripted statements)
+ commitAndCloseScriptStatement();
+ #endif
+ // clear parameter maps
+ resetSQLParameterMaps();
+ // close session level connection if not already closed
+ closeODBCConnection(fODBCConnectionHandle);
+ #endif // ODBCAPI_SUPPORT
+} // TODBCApiAgent::InternalResetSession
+
+
+// Virtual version
+void TODBCApiAgent::ResetSession(void)
+{
+ // do my own stuff
+ InternalResetSession();
+ // let ancestor do its stuff
+ inherited::ResetSession();
+} // TODBCApiAgent::ResetSession
+
+
+#ifdef ODBCAPI_SUPPORT
+#ifdef SCRIPT_SUPPORT
+
+// commit and close eventually open script statement
+void TODBCApiAgent::commitAndCloseScriptStatement(void)
+{
+ if (fODBCConnectionHandle!=SQL_NULL_HANDLE) {
+ // free script statement, if any still open
+ if (fScriptStatement!=SQL_NULL_HANDLE) {
+ PDEBUGPRINTFX(DBG_DBAPI+DBG_EXOTIC,("Script statement exists -> closing it now"));
+ SafeSQLFreeHandle(SQL_HANDLE_STMT,fScriptStatement);
+ fScriptStatement=SQL_NULL_HANDLE;
+ }
+ // now commit transaction
+ SafeSQLEndTran(SQL_HANDLE_DBC,fODBCConnectionHandle,SQL_COMMIT);
+ }
+} // TODBCApiAgent::commitAndCloseScriptStatement
+
+
+// get statement for executing scripted SQL
+HSTMT TODBCApiAgent::getScriptStatement(void)
+{
+ if (fScriptStatement==SQL_NULL_HANDLE) {
+ fScriptStatement=newStatementHandle(getODBCConnectionHandle());
+ }
+ return fScriptStatement;
+} // TODBCApiAgent::getScriptStatement
+
+#endif
+#endif // ODBCAPI_SUPPORT
+
+
+#ifdef ODBCAPI_SUPPORT
+
+#if !defined(NO_AV_GUARDING) // && !__option(microsoft_exceptions)
+
+// SEH-aware versions of ODBC calls (to avoid that crashing drivers blame our server)
+// ==================================================================================
+
+// Special exception returning the SEH code
+TODBCSEHexception::TODBCSEHexception(uInt32 aCode)
+{
+ StringObjPrintf(fMessage,"ODBC Driver caused SEH/AV Code=%08lX",aCode);
+} // TODBCSEHexception::TODBCSEHexception
+
+
+
+// define what object is to be thrown
+#define SEH_THROWN_OBJECT TODBCSEHexception(GetExceptionCode())
+
+SQLRETURN SafeSQLAllocHandle(
+ SQLSMALLINT HandleType,
+ SQLHANDLE InputHandle,
+ SQLHANDLE * OutputHandlePtr)
+{
+ __try {
+ /*
+ #ifndef RELEASE_VERSION
+ // %%% causes AV
+ InputHandle=0;
+ *((char *)(InputHandle)) = 'X';
+ #else
+ #error "throw that out!! %%%%"
+ #endif
+ */
+ return SQLAllocHandle(HandleType,InputHandle,OutputHandlePtr);
+ }
+ __except(EXCEPTION_EXECUTE_HANDLER)
+ {
+ throw SEH_THROWN_OBJECT;
+ }
+} // SafeSQLAllocHandle
+
+
+SQLRETURN SafeSQLFreeHandle(
+ SQLSMALLINT HandleType,
+ SQLHANDLE Handle)
+{
+ __try {
+ return SQLFreeHandle(HandleType,Handle);
+ }
+ __except(EXCEPTION_EXECUTE_HANDLER)
+ {
+ throw SEH_THROWN_OBJECT;
+ }
+} // SafeSQLFreeHandle
+
+
+SQLRETURN SafeSQLSetEnvAttr(
+ SQLHENV EnvironmentHandle,
+ SQLINTEGER Attribute,
+ SQLPOINTER ValuePtr,
+ SQLINTEGER StringLength)
+{
+ __try {
+ return SQLSetEnvAttr(EnvironmentHandle,Attribute,ValuePtr,StringLength);
+ }
+ __except(EXCEPTION_EXECUTE_HANDLER)
+ {
+ throw SEH_THROWN_OBJECT;
+ }
+} // SafeSQLSetEnvAttr
+
+
+SQLRETURN SafeSQLSetConnectAttr(
+ SQLHDBC ConnectionHandle,
+ SQLINTEGER Attribute,
+ SQLPOINTER ValuePtr,
+ SQLINTEGER StringLength)
+{
+ __try {
+ return SQLSetConnectAttr(ConnectionHandle,Attribute,ValuePtr,StringLength);
+ }
+ __except(EXCEPTION_EXECUTE_HANDLER)
+ {
+ throw SEH_THROWN_OBJECT;
+ }
+} // SafeSQLSetConnectAttr
+
+
+SQLRETURN SafeSQLConnect(
+ SQLHDBC ConnectionHandle,
+ SQLCHAR * ServerName,
+ SQLSMALLINT NameLength1,
+ SQLCHAR * UserName,
+ SQLSMALLINT NameLength2,
+ SQLCHAR * Authentication,
+ SQLSMALLINT NameLength3)
+{
+ __try {
+ return SQLConnect(ConnectionHandle,ServerName,NameLength1,UserName,NameLength2,Authentication,NameLength3);
+ }
+ __except(EXCEPTION_EXECUTE_HANDLER)
+ {
+ throw SEH_THROWN_OBJECT;
+ }
+} // SafeSQLConnect
+
+
+SQLRETURN SafeSQLDriverConnect(
+ SQLHDBC ConnectionHandle,
+ SQLHWND WindowHandle,
+ SQLCHAR * InConnectionString,
+ SQLSMALLINT StringLength1,
+ SQLCHAR * OutConnectionString,
+ SQLSMALLINT BufferLength,
+ SQLSMALLINT * StringLength2Ptr,
+ SQLUSMALLINT DriverCompletion)
+{
+ __try {
+ return SQLDriverConnect(ConnectionHandle,WindowHandle,InConnectionString,StringLength1,OutConnectionString,BufferLength,StringLength2Ptr,DriverCompletion);
+ }
+ __except(EXCEPTION_EXECUTE_HANDLER)
+ {
+ throw SEH_THROWN_OBJECT;
+ }
+} // SafeSQLDriverConnect
+
+
+SQLRETURN SafeSQLGetInfo(
+ SQLHDBC ConnectionHandle,
+ SQLUSMALLINT InfoType,
+ SQLPOINTER InfoValuePtr,
+ SQLSMALLINT BufferLength,
+ SQLSMALLINT * StringLengthPtr)
+{
+ __try {
+ return SQLGetInfo(ConnectionHandle,InfoType,InfoValuePtr,BufferLength,StringLengthPtr);
+ }
+ __except(EXCEPTION_EXECUTE_HANDLER)
+ {
+ throw SEH_THROWN_OBJECT;
+ }
+} // SafeSQLGetInfo
+
+
+SQLRETURN SafeSQLEndTran(
+ SQLSMALLINT HandleType,
+ SQLHANDLE Handle,
+ SQLSMALLINT CompletionType)
+{
+ __try {
+ return SQLEndTran(HandleType,Handle,CompletionType);
+ }
+ __except(EXCEPTION_EXECUTE_HANDLER)
+ {
+ throw SEH_THROWN_OBJECT;
+ }
+} // SafeSQLEndTran
+
+
+SQLRETURN SafeSQLExecDirect(
+ SQLHSTMT StatementHandle,
+ SQLCHAR * StatementText,
+ SQLINTEGER TextLength)
+{
+ __try {
+ return SQLExecDirect(StatementHandle,StatementText,TextLength);
+ }
+ __except(EXCEPTION_EXECUTE_HANDLER)
+ {
+ throw SEH_THROWN_OBJECT;
+ }
+} // SafeSQLExecDirect
+
+
+#ifdef ODBC_UNICODE
+
+SQLRETURN SafeSQLExecDirectW(
+ SQLHSTMT StatementHandle,
+ SQLWCHAR * StatementText,
+ SQLINTEGER TextLength)
+{
+ __try {
+ return SQLExecDirectW(StatementHandle,StatementText,TextLength);
+ }
+ __except(EXCEPTION_EXECUTE_HANDLER)
+ {
+ throw SEH_THROWN_OBJECT;
+ }
+} // SafeSQLExecDirectW
+
+#endif
+
+
+SQLRETURN SafeSQLFetch(
+ SQLHSTMT StatementHandle)
+{
+ __try {
+ return SQLFetch(StatementHandle);
+ }
+ __except(EXCEPTION_EXECUTE_HANDLER)
+ {
+ throw SEH_THROWN_OBJECT;
+ }
+} // SafeSQLFetch
+
+
+SQLRETURN SafeSQLNumResultCols(
+ SQLHSTMT StatementHandle,
+ SQLSMALLINT * ColumnCountPtr)
+{
+ __try {
+ return SQLNumResultCols(StatementHandle,ColumnCountPtr);
+ }
+ __except(EXCEPTION_EXECUTE_HANDLER)
+ {
+ throw SEH_THROWN_OBJECT;
+ }
+} // SafeSQLNumResultCols
+
+
+SQLRETURN SafeSQLGetData(
+ SQLHSTMT StatementHandle,
+ SQLUSMALLINT ColumnNumber,
+ SQLSMALLINT TargetType,
+ SQLPOINTER TargetValuePtr,
+ SQLINTEGER BufferLength,
+ SQLINTEGER * StrLen_or_IndPtr)
+{
+ __try {
+ return SQLGetData(StatementHandle,ColumnNumber,TargetType,TargetValuePtr,BufferLength,StrLen_or_IndPtr);
+ }
+ __except(EXCEPTION_EXECUTE_HANDLER)
+ {
+ throw SEH_THROWN_OBJECT;
+ }
+} //
+
+
+SQLRETURN SafeSQLCloseCursor(
+ SQLHSTMT StatementHandle)
+{
+ __try {
+ return SQLCloseCursor(StatementHandle);
+ }
+ __except(EXCEPTION_EXECUTE_HANDLER)
+ {
+ throw SEH_THROWN_OBJECT;
+ }
+} // SafeSQLCloseCursor
+
+
+#endif // AV Guarding
+
+#endif // ODBCAPI_SUPPORT
+
+
+// append field value as literal to SQL text
+// - returns true if field(s) were not empty
+// - even non-existing or empty field will append at least NULL or '' to SQL
+bool TODBCApiAgent::appendFieldValueLiteral(
+ TItemField &aField,TDBFieldType aDBFieldType, uInt32 aMaxSize, string &aSQL,
+ TCharSets aDataCharSet, TLineEndModes aDataLineEndMode, TQuotingModes aQuotingMode,
+ timecontext_t aTimeContext,
+ sInt32 &aRecordSize
+)
+{
+ bool dat=false;
+ bool tim=false;
+ bool intts=false;
+ string val;
+ TTimestampField *tsFldP;
+ sInt32 factor;
+ sInt32 sz;
+ sInt16 moffs;
+ lineartime_t ts;
+ timecontext_t tctx;
+
+ bool isempty=aField.isEmpty();
+ if (
+ isempty &&
+ aDBFieldType!=dbft_string
+ ) {
+ // non-string field does not have a value: NULL
+ aSQL+="NULL";
+ }
+ else {
+ switch (aDBFieldType) {
+ // numeric time offsets
+ case dbft_uctoffsfortime_hours:
+ factor = 0; // special case, float
+ goto timezone;
+ case dbft_uctoffsfortime_mins:
+ factor = 1;
+ goto timezone;
+ case dbft_uctoffsfortime_secs:
+ factor = SecsPerMin;;
+ goto timezone;
+ case dbft_zonename:
+ factor = -1; // name, not offset
+ timezone:
+ // get field
+ if (!aField.isBasedOn(fty_timestamp))
+ goto nullfield; // no timestamp -> no zone
+ tsFldP = static_cast<TTimestampField *>(&aField);
+ // get name or offset
+ tctx = tsFldP->getTimeContext();
+ if (factor>=0) {
+ // offset requested
+ if (tsFldP->isFloating()) goto nullfield; // floating -> no offset
+ if (!TzResolveToOffset(tctx,moffs,tsFldP->getTimestampAs(TCTX_UNKNOWN),false,tsFldP->getGZones()))
+ goto nullfield; // cannot calc offset -> no zone
+ if (factor==0)
+ StringObjAppendPrintf(aSQL,"%g",(sInt32)moffs/60.0); // make hours with fraction
+ else
+ StringObjAppendPrintf(aSQL,"%ld",(long)moffs * factor); // mins or seconds
+ }
+ else {
+ // name requested
+ if (!TCTX_IS_DURATION(tctx) && !TCTX_IS_DATEONLY(tctx) && TCTX_IS_UNKNOWN(tctx))
+ goto nullfield; // really floating (not duration or dateonly) -> no zone (and not "FLOATING" string we'd get from TimeZoneContextToName)
+ TimeZoneContextToName(tctx, val, tsFldP->getGZones());
+ goto asstring;
+ }
+ break;
+ // date and time values
+ case dbft_lineardate:
+ case dbft_unixdate_s:
+ case dbft_unixdate_ms:
+ case dbft_unixdate_us:
+ intts=true; // integer timestamp
+ case dbft_date: // date-only field
+ dat=true;
+ goto settimestamp;
+ case dbft_timefordate:
+ case dbft_time:
+ tim=true;
+ goto settimestamp;
+ case dbft_lineartime:
+ case dbft_unixtime_s:
+ case dbft_unixtime_ms:
+ case dbft_unixtime_us:
+ intts=true; // integer timestamp
+ case dbft_dateonly: // date-only, but stored as timestamp
+ case dbft_timestamp:
+ dat=true;
+ tim=true;
+ settimestamp:
+ // get timestamp in DB time zone
+ if (aField.isBasedOn(fty_timestamp)) {
+ // get it from field in specified zone (or floating)
+ ts=static_cast<TTimestampField *>(&aField)->getTimestampAs(aTimeContext,&tctx);
+ }
+ else {
+ // try to convert ISO8601 string representation
+ aField.getAsString(val);
+ ISO8601StrToTimestamp(val.c_str(), ts, tctx);
+ TzConvertTimestamp(ts,tctx,aTimeContext,getSessionZones(),aTimeContext);
+ }
+ // remove time part on date-only
+ if (dat & !tim)
+ ts = lineartime2dateonlyTime(ts);
+ if (intts) {
+ // Timestamp represented as integer in the DB
+ // - add as integer timestamp
+ StringObjAppendPrintf(aSQL,PRINTF_LLD,PRINTF_LLD_ARG(lineartimeToDbInt(ts,aDBFieldType)));
+ }
+ else {
+ // add as ODBC date/time literal
+ lineartimeToODBCLiteralAppend(ts, aSQL, dat, tim);
+ }
+ break;
+ case dbft_numeric:
+ // numeric fields are copied to SQL w/o quotes
+ aField.getAsString(val);
+ aSQL.append(val); // just append
+ break;
+ case dbft_blob:
+ // BLOBs cannot be written literally (should never occur, as we automatically parametrize them)
+ throw TSyncException("FATAL: BLOB fields must be written with parameters");
+ break;
+ case dbft_string:
+ default:
+ // Database field is string (or unknown), add it as string literal
+ aField.getAsString(val);
+ // only net string sizes are counted
+ sz=val.size(); // net size of string
+ if (sz>sInt32(aMaxSize)) sz=aMaxSize; // limit to what can be actually stored
+ aRecordSize+=sz; // add to count
+ asstring:
+ stringToODBCLiteralAppend(
+ val.c_str(),
+ aSQL,
+ aDataCharSet,
+ aDataLineEndMode,
+ aQuotingMode,
+ aMaxSize
+ );
+ break;
+ nullfield:
+ aSQL+="NULL";
+ break;
+ } // switch
+ } // field has a value
+ return !isempty;
+} // TODBCApiAgent::appendFieldValueLiteral
+
+
+// - make ODBC string literal from UTF8 string
+void TODBCApiAgent::stringToODBCLiteralAppend(
+ cAppCharP aText,
+ string &aLiteral,
+ TCharSets aCharSet,
+ TLineEndModes aLineEndMode,
+ TQuotingModes aQuotingMode,
+ size_t aMaxBytes
+)
+{
+ aLiteral+='\'';
+ appendUTF8ToString(
+ aText,aLiteral,
+ aCharSet, // charset
+ aLineEndMode, // line end mode
+ aQuotingMode, // quoting mode
+ aMaxBytes // max size (0 if unlimited)
+ );
+ aLiteral+='\'';
+} // TODBCApiAgent::stringToODBCLiteralAppend
+
+
+// - make ODBC date/time literals from lineartime_t
+void TODBCApiAgent::lineartimeToODBCLiteralAppend(
+ lineartime_t aTimestamp, string &aString,
+ bool aWithDate, bool aWithTime,
+ timecontext_t aTsContext, timecontext_t aDBContext
+)
+{
+ // make correct zone if needed
+ if (!TCTX_IS_UNKNOWN(aTsContext)) {
+ TzConvertTimestamp(aTimestamp,aTsContext,aDBContext,getSessionZones(),TCTX_UNKNOWN);
+ }
+ // calculate components
+ sInt16 y,mo,d,h,mi,s,ms;
+ lineartime2date(aTimestamp,&y,&mo,&d);
+ lineartime2time(aTimestamp,&h,&mi,&s,&ms);
+ // create prefix
+ aString+='{';
+ if (aWithDate && aWithTime)
+ aString+="ts";
+ else if (aWithTime)
+ aString+="t";
+ else
+ aString+="d";
+ aString+=" '";
+ // add date if selected
+ if (aWithDate) {
+ StringObjAppendPrintf(
+ aString,"%04d-%02d-%02d",
+ y, mo, d
+ );
+ }
+ // add time if selected
+ if (aWithTime) {
+ if (aWithDate) aString+=' '; // separate
+ StringObjAppendPrintf(aString,
+ "%02d:%02d:%02d",
+ h, mi, s
+ );
+ // microseconds, if any
+ if (ms!=0) {
+ StringObjAppendPrintf(aString,".%03d",ms);
+ }
+ }
+ // suffix
+ aString+="'}";
+} // TODBCApiAgent::lineartimeToODBCLiteralAppend
+
+
+// - make integer-based literals from lineartime_t
+void TODBCApiAgent::lineartimeToIntLiteralAppend(
+ lineartime_t aTimestamp, string &aString,
+ TDBFieldType aDbfty,
+ timecontext_t aTsContext, timecontext_t aDBContext
+)
+{
+ // make correct zone if needed
+ if (!TCTX_IS_UNKNOWN(aTsContext)) {
+ TzConvertTimestamp(aTimestamp,aTsContext,aDBContext,getSessionZones(),TCTX_UNKNOWN);
+ }
+ // - add as integer timestamp
+ StringObjAppendPrintf(aString,PRINTF_LLD,PRINTF_LLD_ARG(lineartimeToDbInt(aTimestamp,aDbfty)));
+} // TODBCApiAgent::lineartimeToIntLiteralAppend
+
+
+
+/*%%% obsolete
+// - make ODBC date/time literals from UTC timestamp
+void TODBCApiAgent::timeStampToODBCLiteralAppend(lineartime_t aTimeStamp, string &aString, bool aAsUTC, bool aWithDate, bool aWithTime)
+{
+ if (aTimeStamp!=0) {
+ if (!aAsUTC)
+ aTimeStamp=makeLocalTimestamp(aTimeStamp); // convert to local time
+ struct tm tim;
+ lineartime2tm(aTimeStamp,&tim);
+ // format as ODBC date/time literal
+ tmToODBCLiteralAppend(tim,aString,aWithDate,aWithTime);
+ }
+ else {
+ aString.append("NULL");
+ }
+} // TODBCApiAgent::timeStampToODBCLiteralAppend
+
+// - make ODBC date/time literals from struct tm
+void TODBCApiAgent::tmToODBCLiteralAppend(const struct tm &tim, string &aString, bool aWithDate, bool aWithTime)
+{
+ // create prefix
+ aString+='{';
+ if (aWithDate && aWithTime)
+ aString+="ts";
+ else if (aWithTime)
+ aString+="t";
+ else
+ aString+="d";
+ aString+=" '";
+ // add date if selected
+ if (aWithDate) {
+ StringObjAppendPrintf(
+ aString,"%04d-%02d-%02d",
+ tim.tm_year+1900,
+ tim.tm_mon+1,
+ tim.tm_mday
+ );
+ }
+ // add time if selected
+ if (aWithTime) {
+ if (aWithDate) aString+=' '; // separate
+ StringObjAppendPrintf(aString,
+ "%02d:%02d:%02d",
+ tim.tm_hour,
+ tim.tm_min,
+ tim.tm_sec
+ );
+ }
+ // suffix
+ aString+="'}";
+} // TODBCApiAgent::tmToODBCLiteralAppend
+*/
+
+
+
+// - return quoted version of string if aDoQuote is set
+// bfo: Problems with XCode (expicit qualification), already within namespace ?
+//const char *sysync::quoteString(string &aIn, string &aOut, TQuotingModes aQuoteMode)
+const char *quoteString(string &aIn, string &aOut, TQuotingModes aQuoteMode)
+{
+ return quoteString(aIn.c_str(),aOut,aQuoteMode);
+} // TODBCApiAgent::quoteString
+
+
+// - return quoted version of string if aDoQuote is set
+// bfo: Problems with XCode (expicit qualification), already within namespace ?
+//const char *sysync::quoteString(const char *aIn, string &aOut, TQuotingModes aQuoteMode)
+const char *quoteString(const char *aIn, string &aOut, TQuotingModes aQuoteMode)
+{
+ aOut.erase();
+ quoteStringAppend(aIn,aOut,aQuoteMode);
+ return aOut.c_str();
+} // TODBCApiAgent::quoteString
+
+
+// - append quoted version of string if aDoQuote is set
+// bfo: Problems with XCode (expicit qualification), already within namespace ?
+//void sysync::quoteStringAppend(const char *aIn, string &aOut, TQuotingModes aQuoteMode)
+void quoteStringAppend(const char *aIn, string &aOut, TQuotingModes aQuoteMode)
+{
+ if (!aQuoteMode!=qm_none) aOut.append(aIn);
+ else {
+ sInt16 n=strlen(aIn);
+ aOut.reserve(n+2);
+ aOut+='\'';
+ appendUTF8ToString(
+ aIn,
+ aOut,
+ chs_ascii, // charset
+ lem_cstr, // line end mode
+ aQuoteMode, // quoting mode
+ 0 // max size (0 if unlimited)
+ );
+ aOut+='\'';
+ }
+} // TODBCApiAgent::quoteStringAppend
+
+
+// - append quoted version of string if aDoQuote is set
+// bfo: Problems with XCode (expicit qualification), already within namespace ?
+//void sysync::quoteStringAppend(string &aIn, string &aOut, TQuotingModes aQuoteMode)
+void quoteStringAppend(string &aIn, string &aOut, TQuotingModes aQuoteMode)
+{
+ quoteStringAppend(aIn.c_str(),aOut,aQuoteMode);
+} // TODBCApiAgent::quoteStringAppend
+
+
+// reset all mapped parameters
+void TODBCApiAgent::resetSQLParameterMaps(TParameterMapList &aParamMapList)
+{
+ TParameterMapList::iterator pos;
+ for (pos=aParamMapList.begin();pos!=aParamMapList.end();++pos) {
+ // clean up entry
+ if (pos->mybuffer && pos->ParameterValuePtr) {
+ // delete buffer if we have allocated one
+ sysync_free(pos->ParameterValuePtr);
+ pos->ParameterValuePtr=NULL;
+ pos->mybuffer=false;
+ }
+ }
+ // now clear list
+ aParamMapList.clear();
+} // TODBCApiAgent::resetSQLParameterMaps
+
+
+// parsing of %p(mode,var_or_field[,dbfieldtype[,maxcolsize]]) sequence
+bool TODBCApiAgent::ParseParamSubst(
+ string &aSQL, // string to parse
+ string::size_type &i, // input=position where % sequence starts in aSQL, output = if result==false: where to continue parsing, else: where to substitute
+ string::size_type &n, // input=number of chars of % sequence eventuall with "(" but nothing more, if result==true: output=number of chars to substitute at i in aSQL
+ TParameterMapList &aParameterMaps, // parameter maps list to add params to
+ TMultiFieldItem *aItemP // the involved item for field params
+ #ifdef SCRIPT_SUPPORT
+ ,TScriptContext *aScriptContextP // the script context for variable params
+ #endif
+)
+{
+ string::size_type j,k,h;
+
+ // %p(mode,fieldname,dbfieldtype) = field as SQL parameter, where mode can be "i","o" or "io"
+ j=i+n;
+ // find closing paranthesis
+ k = aSQL.find(")",j);
+ if (k==string::npos) { i=j; n=0; return false; } // no closing paranthesis, do not substitute
+ // get mode
+ bool paramin=false,paramout=false;
+ paramin=false;
+ paramout=false;
+ if (tolower(aSQL[j]=='i')) {
+ paramin=true;
+ if (tolower(aSQL[j+1])=='o') {
+ paramout=true;
+ ++j;
+ }
+ ++j;
+ }
+ else if (tolower(aSQL[j]=='o')) {
+ paramout=true;
+ ++j;
+ }
+ // now get item field or variable name
+ if (aSQL[j]!=',') { i=k+1; n=0; return false; } // continue after closing paranthesis
+ ++j;
+ // extract name (without eventual array index)
+ h = aSQL.find(",",j);
+ if (h==string::npos) { h=k; } // no second comma, only field name, use default dbfieldtype
+ string fldname;
+ fldname.assign(aSQL,j,h-j);
+ j=h+1; // after , or )
+ // get fieldtype (default if none specified)
+ TDBFieldType dbfty=dbft_string; // default to string
+ uInt32 colmaxsize=0;
+ if (h<k) {
+ // more params specified (database field type and eventually column size)
+ h = aSQL.find(",",j);
+ if (h==string::npos) { h=k; } // no third comma, only field type, use default column size)
+ // get database field type
+ string tyname;
+ tyname.assign(aSQL,j,h-j);
+ sInt16 ty;
+ if (StrToEnum(DBFieldTypeNames,numDBfieldTypes,ty,tyname.c_str()))
+ dbfty=(TDBFieldType)ty;
+ j=h+1; // after , or )
+ // get column max size (default if none specified)
+ if (h<k) {
+ // column size specified
+ StrToULong(aSQL.c_str()+j,colmaxsize,k-j);
+ }
+ }
+ // find field or var
+ TItemField *fldP=NULL;
+ #ifdef SCRIPT_SUPPORT
+ if (aScriptContextP) {
+ sInt16 idx=aScriptContextP->getIdentifierIndex(OBJ_AUTO, aItemP ? aItemP->getFieldDefinitions() : NULL ,fldname.c_str());
+ fldP=aScriptContextP->getFieldOrVar(aItemP,idx,0);
+ }
+ else
+ #endif
+ if (aItemP) fldP = aItemP->getArrayField(fldname.c_str(),0,true);
+ if (!fldP) { i=k+1; n=0; return false; } // no suitable mapping
+ // now map as parameter
+ TParameterMap map;
+ // assign basics
+ map.inparam=paramin;
+ map.outparam=paramout;
+ map.parammode=param_field;
+ map.mybuffer=false;
+ map.ParameterValuePtr=NULL;
+ map.BufferLength=0;
+ map.StrLen_or_Ind=SQL_NULL_DATA; // note that this is not zero (but -1)
+ map.itemP=aItemP;
+ map.fieldP=fldP;
+ map.maxSize=colmaxsize;
+ map.dbFieldType=dbfty;
+ // save in list
+ aParameterMaps.push_back(map);
+ // set substitution parameters
+ n=k+1-i;
+ // ok, substitute
+ return true;
+} // TODBCApiAgent::ParseParamSubst
+
+
+// do generic substitutions
+void TODBCApiAgent::DoSQLSubstitutions(string &aSQL)
+{
+ #ifndef BASED_ON_BINFILE_CLIENT
+ // substitute: %u = userkey
+ StringSubst(aSQL,"%u",fUserKey,2,fConfigP->fDataCharSet,fConfigP->fDataLineEndMode,fConfigP->fQuotingMode);
+ // substitute: %d = devicekey
+ StringSubst(aSQL,"%d",fDeviceKey,2,fConfigP->fDataCharSet,fConfigP->fDataLineEndMode,fConfigP->fQuotingMode);
+ #ifdef SCRIPT_SUPPORT
+ // substitute: %C = domain name (such as company selector)
+ StringSubst(aSQL,"%C",fDomainName,2,fConfigP->fDataCharSet,fConfigP->fDataLineEndMode,fConfigP->fQuotingMode);
+ #endif
+ #endif
+ #ifdef SCRIPT_SUPPORT
+ // substitute %sv(sessionvarname) = session variable by name
+ string::size_type i=0;
+ while((i=aSQL.find("%sv(",i))!=string::npos) {
+ string s;
+ // skip lead-in
+ string::size_type j=i+4;
+ // find closing paranthesis
+ string::size_type k = aSQL.find(")",j);
+ if (k==string::npos) { i=j; continue; } // no closing paranthesis
+ sInt32 m = k; // assume end of name is here
+ // extract name
+ s.assign(aSQL,j,m-j);
+ // find session var with this name
+ if (!fSessionScriptContextP) { i=j; continue; }
+ TItemField *fldP = fSessionScriptContextP->getFieldOrVar(
+ NULL,
+ fSessionScriptContextP->getIdentifierIndex(OBJ_LOCAL,NULL,s.c_str(),s.size())
+ );
+ if (!fldP) { i=j; continue; } // field not found, no action
+ // get field contents
+ fldP->getAsString(s);
+ // subsititute (starting with "%d(", ending with ")" )
+ aSQL.replace(i,k+1-i,s); i+=s.size();
+ }
+ // substitute %p(mode,var_or_field[,dbfieldtype]) = map field to parameter
+ // - as the only source of params in session-level SQL is the %p() sequence, we
+ // can clear the list here (to make sure no param from another call remains active)
+ // - if this is called from Datastore context, %p() sequences are already substituted, and
+ // therefore no parameter list mixing will occur
+ resetSQLParameterMaps();
+ i=0;
+ while((i=aSQL.find("%p(",i))!=string::npos) {
+ string::size_type n=3; // size of base sequence %p(
+ if (!ParseParamSubst(
+ aSQL,i,n,
+ fParameterMaps,
+ NULL
+ #ifdef SCRIPT_SUPPORT
+ ,fAgentContext
+ #endif
+ )) break;
+ // subsititute param spec with single question mark
+ aSQL.replace(i,n,"?"); i+=1;
+ }
+ #endif
+} // TODBCApiAgent::DoSQLSubstitutions
+
+
+// reset all mapped parameters
+void TODBCApiAgent::resetSQLParameterMaps(void)
+{
+ resetSQLParameterMaps(fParameterMaps);
+} // TODBCApiAgent::resetSQLParameterMaps
+
+
+// add parameter definition to the session level parameter list
+void TODBCApiAgent::addSQLParameterMap(
+ bool aInParam,
+ bool aOutParam,
+ TParamMode aParamMode,
+ TItemField *aFieldP,
+ TDBFieldType aDbFieldType
+)
+{
+ TParameterMap map;
+
+ // assign basics
+ map.inparam=aInParam;
+ map.outparam=aOutParam;
+ map.parammode=aParamMode;
+ map.mybuffer=false;
+ map.ParameterValuePtr=NULL;
+ map.BufferLength=0;
+ map.StrLen_or_Ind=SQL_NULL_DATA; // note that this is not zero (but -1)
+ map.itemP=NULL; // no item
+ map.fieldP=aFieldP;
+ map.maxSize=std_paramsize;
+ map.dbFieldType=aDbFieldType;
+ // save in list
+ fParameterMaps.push_back(map);
+} // TODBCApiAgent::addSQLParameterMap
+
+
+// ODBC Utils
+// ==========
+
+#ifdef ODBCAPI_SUPPORT
+
+// get existing or create new ODBC environment handle
+SQLHENV TODBCApiAgent::getODBCEnvironmentHandle(void)
+{
+ if (fODBCEnvironmentHandle==SQL_NULL_HANDLE) {
+ // create one environment handle for the session
+ if (SafeSQLAllocHandle(
+ SQL_HANDLE_ENV, SQL_NULL_HANDLE, &fODBCEnvironmentHandle
+ ) != SQL_SUCCESS) {
+ // problem
+ throw TSyncException("Cannot allocated ODBC environment handle");
+ }
+ // Set ODBC 3.0 (needed, else function sequence error will occur)
+ if (SafeSQLSetEnvAttr(
+ fODBCEnvironmentHandle, SQL_ATTR_ODBC_VERSION, (void*)SQL_OV_ODBC3, 0
+ ) != SQL_SUCCESS) {
+ // problem
+ throw TSyncException("Cannot set environment to ODBC 3.0");
+ }
+ }
+ // return the handle
+ return fODBCEnvironmentHandle;
+} // TODBCApiAgent::getODBCEnvironmentHandle
+
+
+// check for connection-level error
+void TODBCApiAgent::checkConnectionError(SQLRETURN aResult)
+{
+ checkODBCError(aResult,SQL_HANDLE_DBC,fODBCConnectionHandle);
+} // TODBCApiAgent::checkConnectionError
+
+
+
+// get handle to open connection
+SQLHDBC TODBCApiAgent::getODBCConnectionHandle(void)
+{
+ SQLRETURN res;
+
+ if (fODBCConnectionHandle==SQL_NULL_HANDLE) {
+ // no connection exists, allocate new Connection Handle
+ PDEBUGPRINTFX(DBG_DBAPI,("Trying to open new ODBC connection with ConnStr = '%s'",fSessionDBConnStr.c_str()));
+ // - get environment handle (session global)
+ SQLHENV envhandle=getODBCEnvironmentHandle();
+ if (envhandle==SQL_NULL_HANDLE)
+ throw TSyncException("No environment handle available");
+ // - allocate connection handle
+ res=SafeSQLAllocHandle(
+ SQL_HANDLE_DBC,
+ envhandle,
+ &fODBCConnectionHandle
+ );
+ checkODBCError(res,SQL_HANDLE_ENV,envhandle);
+ try {
+ // Some ODBC drivers (apparently MyODBC 3.51 on Mac OS X 10.5.2) crash when using SQLSetConnectAttr
+ if (!fConfigP->fNoConnectAttrs) {
+ // Set Connection attributes
+ // - Make sure no dialog boxes are ever shown
+ res=SafeSQLSetConnectAttr(fODBCConnectionHandle,SQL_ATTR_QUIET_MODE,NULL,0);
+ checkConnectionError(res);
+ // - Cursor library usage (default is NO)
+ res=SafeSQLSetConnectAttr(fODBCConnectionHandle,SQL_ATTR_ODBC_CURSORS,(void*)(fConfigP->fUseCursorLib ? SQL_CUR_USE_ODBC : SQL_CUR_USE_DRIVER),0);
+ checkConnectionError(res);
+ // - Commit mode manually
+ res=SafeSQLSetConnectAttr(fODBCConnectionHandle,SQL_ATTR_AUTOCOMMIT,(void*)SQL_AUTOCOMMIT_OFF,0);
+ checkConnectionError(res);
+ // - Set a timeout
+ res=SafeSQLSetConnectAttr(fODBCConnectionHandle,SQL_ATTR_CONNECTION_TIMEOUT,(void*)fConfigP->fODBCTimeout,0);
+ checkConnectionError(res);
+ }
+ // Now connect, before setting transaction stuff
+ // - append password to configured connection string
+ string connstr;
+ connstr=fSessionDBConnStr.c_str();
+ if (!fSessionDBPassword.empty()) {
+ connstr+="PWD=";
+ connstr+=fSessionDBPassword;
+ connstr+=';';
+ }
+ const SQLSMALLINT outStrMax=1024;
+ SQLCHAR outStr[outStrMax];
+ SQLSMALLINT outStrSiz;
+ res=SafeSQLDriverConnect(
+ fODBCConnectionHandle, // connection handle
+ NULL, // no windows handle
+ (SQLCHAR *)connstr.c_str(), // input string
+ connstr.size(),
+ outStr, // output string
+ outStrMax,
+ &outStrSiz,
+ SQL_DRIVER_NOPROMPT
+ );
+ checkConnectionError(res);
+ // Note: the following may show the password, so it MUST NOT be a PDEBUGxxx, but a DEBUGxxx !
+ DEBUGPRINTFX(DBG_DBAPI+DBG_EXOTIC,("SQLDriverConnect returns connection string = '%s'",outStr));
+ // Now configure transactions
+ #ifdef SYDEBUG
+ if (PDEBUGMASK) {
+ string msg,state;
+ // - check what DB can offer for isolation levels
+ SQLUINTEGER txnmask;
+ res=SafeSQLGetInfo(fODBCConnectionHandle,SQL_TXN_ISOLATION_OPTION,&txnmask,0,NULL);
+ if (getODBCError(res,msg,state,SQL_HANDLE_DBC,fODBCConnectionHandle)) {
+ PDEBUGPRINTFX(DBG_ERROR,("SQLGetInfo for SQL_TXN_ISOLATION_OPTION failed: %s",msg.c_str()));
+ } else {
+ PDEBUGPRINTFX(DBG_DBAPI,("ODBC source's Transaction support mask = 0x%04lX",txnmask));
+ }
+ // - check standard isolation level
+ SQLUINTEGER txn;
+ res=SafeSQLGetInfo(fODBCConnectionHandle,SQL_DEFAULT_TXN_ISOLATION,&txn,0,NULL);
+ if (getODBCError(res,msg,state,SQL_HANDLE_DBC,fODBCConnectionHandle)) {
+ DEBUGPRINTFX(DBG_ERROR,("SQLGetInfo for SQL_DEFAULT_TXN_ISOLATION failed: %s",msg.c_str()));
+ }
+ else {
+ DEBUGPRINTF(("ODBC source's standard isolation mask = 0x%04lX",txn));
+ }
+ }
+ #endif
+ // - set isolation level
+ if (fConfigP->fODBCTxnMode!=txni_default) {
+ SQLUINTEGER txnmode = 0; // none by default
+ if (fConfigP->fODBCTxnMode<txni_none) {
+ txnmode = 1 << (fConfigP->fODBCTxnMode);
+ }
+ PDEBUGPRINTFX(DBG_DBAPI,("Setting SQL_ATTR_TXN_ISOLATION to = 0x%04lX",txnmode));
+ res=SafeSQLSetConnectAttr(fODBCConnectionHandle,SQL_ATTR_TXN_ISOLATION,(void*)txnmode,0);
+ checkConnectionError(res);
+ }
+ // done!
+ PDEBUGPRINTFX(DBG_DBAPI,("Created and opened new ODBC connection (timeout=%ld sec)",fConfigP->fODBCTimeout));
+ #ifdef SCRIPT_SUPPORT
+ // save datastore context (as this script can be executed implicitly from another scripts SQLEXECUTE())
+ TCustomImplDS *currScriptDS = fScriptContextDatastore;
+ // now execute afterconnect script, if any
+ fScriptContextDatastore=NULL; // connecting is not datastore related
+ TScriptContext::execute(
+ fAfterConnectContext,
+ fConfigP->fAfterConnectScript,
+ fConfigP->getAgentFuncTableP(), // context function table
+ (void *)this // context data (myself)
+ );
+ // restore datastore context as it was before
+ fScriptContextDatastore = currScriptDS;
+ #endif
+ }
+ catch(...) {
+ // connection is not usable, dispose handle again
+ SafeSQLFreeHandle(SQL_HANDLE_DBC,fODBCConnectionHandle);
+ fODBCConnectionHandle=SQL_NULL_HANDLE;
+ throw;
+ }
+ }
+ PDEBUGPRINTFX(DBG_DBAPI+DBG_EXOTIC,("Session: using connection handle 0x%lX",(uInt32)fODBCConnectionHandle));
+ return fODBCConnectionHandle;
+} // TODBCApiAgent::getODBCConnectionHandle
+
+
+// pull connection handle out of session into another object (datastore)
+SQLHDBC TODBCApiAgent::pullODBCConnectionHandle(void)
+{
+ SQLHDBC connhandle = getODBCConnectionHandle();
+ fODBCConnectionHandle = SQL_NULL_HANDLE; // owner is caller, must do closing and disposing
+ #ifdef SCRIPT_SUPPORT
+ // make sure eventual script statement gets disposed, as connection is now owned by datastore
+ commitAndCloseScriptStatement();
+ /*
+ if (fScriptStatement!=SQL_NULL_HANDLE) {
+ PDEBUGPRINTFX(DBG_DBAPI+DBG_EXOTIC,("Script statement exists, connection pulled into DS -> closing the script statement now"));
+ SafeSQLFreeHandle(SQL_HANDLE_STMT,fScriptStatement);
+ fScriptStatement=SQL_NULL_HANDLE;
+ }
+ */
+ #endif
+ return connhandle;
+} // TODBCApiAgent::pullODBCConnectionHandle
+
+
+// close connection
+void TODBCApiAgent::closeODBCConnection(SQLHDBC &aConnHandle)
+{
+ string msg,state;
+ SQLRETURN res;
+
+ if (aConnHandle!=SQL_NULL_HANDLE) {
+ // Roll back to make sure we don't leave a unfinished transaction
+ res=SafeSQLEndTran(SQL_HANDLE_DBC,aConnHandle,SQL_ROLLBACK);
+ if (getODBCError(res,msg,state,SQL_HANDLE_DBC,aConnHandle)) {
+ PDEBUGPRINTFX(DBG_ERROR,("closeODBCConnection: SQLEndTran failed: %s",msg.c_str()));
+ }
+ // Actually disconnect
+ res=SQLDisconnect(aConnHandle);
+ if (getODBCError(res,msg,state,SQL_HANDLE_DBC,aConnHandle)) {
+ PDEBUGPRINTFX(DBG_ERROR,("closeODBCConnection: SQLDisconnect failed: %s",msg.c_str()));
+ }
+ // free the connection handle
+ res=SafeSQLFreeHandle(SQL_HANDLE_DBC,aConnHandle);
+ if (getODBCError(res,msg,state,SQL_HANDLE_DBC,aConnHandle)) {
+ PDEBUGPRINTFX(DBG_ERROR,("closeODBCConnection: SQLFreeHandle(DBC) failed: %s",msg.c_str()));
+ }
+ aConnHandle=SQL_NULL_HANDLE;
+ }
+} // TODBCApiAgent::closeODBCConnection
+
+
+// check if aResult signals error and throw exception if so
+void TODBCApiAgent::checkODBCError(SQLRETURN aResult,SQLSMALLINT aHandleType,SQLHANDLE aHandle)
+{
+ string msg,state;
+
+ if (getODBCError(aResult,msg,state,aHandleType,aHandle)) {
+ // error
+ throw TSyncException(msg.c_str());
+ }
+} // TODBCApiAgent::checkODBCError
+
+
+// - check if aResult signals error and throw exception if so
+void TODBCApiAgent::checkStatementError(SQLRETURN aResult,SQLHSTMT aHandle)
+{
+ checkODBCError(aResult,SQL_HANDLE_STMT,aHandle);
+} // TODBCApiAgent::checkStatementError
+
+
+// - check if aResult signals error and throw exception if so
+// does not report NO_DATA error, but returns false if NO_DATA condition exists
+bool TODBCApiAgent::checkStatementHasData(SQLRETURN aResult,SQLHSTMT aHandle)
+{
+ if (aResult==SQL_NO_DATA)
+ return false; // signal NO DATA
+ else
+ checkStatementError(aResult,aHandle);
+ return true; // signal ok
+} // TODBCApiAgent::checkStatementHasData
+
+
+// get ODBC error message for given result code
+// - returns false if no error
+bool TODBCApiAgent::getODBCError(SQLRETURN aResult,string &aMessage,string &aSQLState, SQLSMALLINT aHandleType,SQLHANDLE aHandle)
+{
+ aSQLState.erase();
+ if(aResult==SQL_SUCCESS)
+ return false; // no error
+ else {
+ StringObjPrintf(aMessage,"ODBC SQL return code = %ld\n",(sInt32)aResult);
+ // get Diag Info
+ SQLCHAR sqlstate[6]; // buffer for state message
+ sqlstate[5]=0; // terminate
+
+ SQLINTEGER nativeerror;
+ SQLSMALLINT msgsize,recno;
+ SQLRETURN res;
+ recno=1;
+ // message buffer
+ sInt16 maxmsgsize = 200;
+ SQLCHAR *messageP = (SQLCHAR *) malloc(maxmsgsize);
+ do {
+ if (messageP==NULL) {
+ StringObjAppendPrintf(aMessage,"- SQLGetDiagRec[%hd] failed because needed buffer of %ld bytes cannot be allocated",(sInt16)recno,(sInt32)maxmsgsize);
+ break; // don't continue
+ }
+ msgsize=0; // just to make sure
+ res=SQLGetDiagRec(
+ aHandleType, // handle Type
+ aHandle, // statement handle
+ recno, // record number
+ sqlstate, // state buffer
+ &nativeerror, // native error code
+ messageP, // message buffer, gets message
+ maxmsgsize, // message buffer size
+ &msgsize // gets size of message buffer
+ );
+ if (res==SQL_NO_DATA) break; // seen all diagnostic info
+ if (res==SQL_SUCCESS_WITH_INFO) {
+ if (msgsize>maxmsgsize) {
+ // buffer is too small, allocate a bigger one and try again
+ free(messageP);
+ maxmsgsize=msgsize+1; // make buffer large enough for message
+ messageP = (SQLCHAR *) malloc(maxmsgsize);
+ // try again
+ continue;
+ }
+ // SQL_SUCCESS_WITH_INFO, but buffer is large enough - strange...
+ StringObjAppendPrintf(aMessage,"- SQLGetDiagRec[%hd] said SQL_SUCCESS_WITH_INFO, but buffer is large enough\n",(sInt16)recno);
+ }
+ // info found, append it to error text message
+ else if (res==SQL_SUCCESS) {
+ StringObjAppendPrintf(aMessage,"- SQLState = '%s', NativeError=%ld, Message = %s\n",sqlstate,(sInt32)nativeerror,messageP);
+ // save latest SQLState
+ aSQLState.assign((const char *)sqlstate,5);
+ }
+ else {
+ StringObjAppendPrintf(aMessage,"- SQLGetDiagRec[%hd] failed, Result=%ld\n",(sInt16)recno,(sInt32)res);
+ break; // abort on error, too
+ }
+ recno++;
+ } while(true);
+ // get rid of message buffer
+ free(messageP);
+ // if it's success with Info, this is not considered an error, but do show info in log
+ if (aResult==SQL_SUCCESS_WITH_INFO) {
+ // show info as exotics in debug logs, but treat as success
+ // (note: MS-SQL is verbose here...)
+ PDEBUGPRINTFX(DBG_DBAPI+DBG_EXOTIC,("SQL_SUCCESS_WITH_INFO: %s",aMessage.c_str()));
+ return false; // this is no error
+ }
+ else {
+ return true; // treat as error error
+ }
+ }
+} // TODBCApiAgent::getODBCError
+
+
+
+// get new statement handle
+SQLHSTMT TODBCApiAgent::newStatementHandle(SQLHDBC aConnection)
+{
+ SQLRETURN res;
+ SQLHSTMT statement;
+
+ // Allocate Statement Handle
+ res=SafeSQLAllocHandle(SQL_HANDLE_STMT,aConnection,&statement);
+ checkODBCError(res,SQL_HANDLE_DBC,aConnection);
+ // Statement Attributes
+ // - row array size (ODBC 3.0, not really needed as default is 1 anyway)
+ res=SQLSetStmtAttr(
+ statement,
+ SQL_ATTR_ROW_ARRAY_SIZE,
+ (void*)1,
+ SQL_IS_UINTEGER
+ );
+ checkStatementError(res,statement);
+ return statement;
+} // TODBCApiAgent::newStatementHandle
+
+
+
+
+
+/* About SQLGetData from WIN32_SDK:
+
+If the driver does not support extensions to SQLGetData, the function can only
+return data for unbound columns with a number greater than that of the last bound
+column. Furthermore, within a row of data, the value of the ColumnNumber argument
+in each call to SQLGetData must be greater than or equal to the value of
+ColumnNumber in the previous call; that is, data must be retrieved in increasing
+column number order. Finally, if no extensions are supported, SQLGetData cannot
+be called if the rowset size is greater than 1.
+
+*/
+
+// get value, returns false if no Data or Null
+bool TODBCApiAgent::getColumnValueAsULong(
+ SQLHSTMT aStatement,
+ sInt16 aColNumber,
+ uInt32 &aLongValue
+)
+{
+ SQLRETURN res;
+ SQLINTEGER ind;
+ SQLSMALLINT numcols;
+
+ // check if there aColNumber is in range
+ res = SafeSQLNumResultCols(aStatement,&numcols);
+ checkStatementError(res,aStatement);
+ if (aColNumber<1 || aColNumber>numcols)
+ throw TSyncException(DEBUGTEXT("getColumnValueAsULong with bad col index","odds1"));
+ aLongValue=0;
+ res = SafeSQLGetData(
+ aStatement, // statement handle
+ aColNumber, // column number
+ SQL_C_ULONG, // target type: unsigned long
+ &aLongValue, // where to store the long
+ 4, // max size of value (not used here)
+ (SQLLEN*)&ind // indicator
+ );
+ checkStatementError(res,aStatement);
+ // return true if real data returned
+ return (ind!=SQL_NULL_DATA && ind!=SQL_NO_TOTAL);
+} // TODBCApiAgent::getColumnValueAsULong
+
+
+// get value, returns false if no Data or Null
+bool TODBCApiAgent::getColumnValueAsLong(
+ SQLHSTMT aStatement,
+ sInt16 aColNumber,
+ sInt32 &aLongValue
+)
+{
+ SQLRETURN res;
+ SQLINTEGER ind;
+ SQLSMALLINT numcols;
+
+ // check if there aColNumber is in range
+ res = SafeSQLNumResultCols(aStatement,&numcols);
+ checkStatementError(res,aStatement);
+ if (aColNumber<1 || aColNumber>numcols)
+ throw TSyncException(DEBUGTEXT("getColumnValueAsLong with bad col index","odds2"));
+ aLongValue=0;
+ res = SafeSQLGetData(
+ aStatement, // statement handle
+ aColNumber, // column number
+ SQL_C_LONG, // target type: signed long
+ &aLongValue, // where to store the long
+ 4, // max size of value (not used here)
+ (SQLLEN*)&ind // indicator
+ );
+ checkStatementError(res,aStatement);
+ // return true if real data returned
+ return (ind!=SQL_NULL_DATA && ind!=SQL_NO_TOTAL);
+} // TODBCApiAgent::getColumnValueAsLong
+
+
+// get value, returns false if no Data or Null
+bool TODBCApiAgent::getColumnValueAsDouble(
+ SQLHSTMT aStatement,
+ sInt16 aColNumber,
+ double &aDoubleValue
+)
+{
+ SQLRETURN res;
+ SQLINTEGER ind;
+ SQLSMALLINT numcols;
+
+ // check if there aColNumber is in range
+ res = SafeSQLNumResultCols(aStatement,&numcols);
+ checkStatementError(res,aStatement);
+ if (aColNumber<1 || aColNumber>numcols)
+ throw TSyncException(DEBUGTEXT("getColumnValueAsDouble with bad col index","odds3"));
+ aDoubleValue=0;
+ res = SafeSQLGetData(
+ aStatement, // statement handle
+ aColNumber, // column number
+ SQL_C_DOUBLE, // target type: signed long
+ &aDoubleValue, // where to store the double
+ 8, // max size of value (not used here)
+ (SQLLEN*)&ind // indicator
+ );
+ checkStatementError(res,aStatement);
+ // return true if real data returned
+ return (ind!=SQL_NULL_DATA && ind!=SQL_NO_TOTAL);
+} // TODBCApiAgent::getColumnValueAsDouble
+
+
+// get value, returns false if no Data or Null
+bool TODBCApiAgent::getColumnValueAsString(
+ SQLHSTMT aStatement,
+ sInt16 aColNumber,
+ string &aStringValue,
+ TCharSets aCharSet, // real charset, including UTF16!
+ bool aAsBlob
+)
+{
+ SQLRETURN res;
+ sInt32 maxstringlen=512; // enough to start with for most fields
+ sInt32 nextbuflen;
+ uInt8 *strbufP=NULL;
+ SQLINTEGER siz;
+ SQLSMALLINT numcols;
+ bool gotData=false;
+ #ifdef ODBC_UNICODE
+ string wStr;
+ #endif
+
+ // check if there aColNumber is in range
+ res = SafeSQLNumResultCols(aStatement,&numcols);
+ checkStatementError(res,aStatement);
+ if (aColNumber<1 || aColNumber>numcols)
+ throw TSyncException(DEBUGTEXT("getColumnValueAsString with bad col index","odds4"));
+ // get data
+ // - start with empty string
+ aStringValue.erase();
+ strbufP = new uInt8[maxstringlen+1];
+ try {
+ bool gotAllData=false;
+ do {
+ // make sure we have the buffer
+ if (!strbufP)
+ throw TSyncException(DEBUGTEXT("getColumnValueAsString can't allocate enough buffer memory","odds4a"));
+ strbufP[maxstringlen]=0; // make sure we have ALWAYS a terminator (for appendStringAsUTF8)
+ nextbuflen=maxstringlen; // default to same size we already have
+ // now get data
+ res = SafeSQLGetData(
+ aStatement, // statement handle
+ aColNumber, // column number
+ aAsBlob ? SQL_C_BINARY :
+ #ifdef ODBC_UNICODE
+ (aCharSet==chs_utf16 ? SQL_C_WCHAR : SQL_C_CHAR), // target type: Binary, 8-bit or 16-bit string
+ #else
+ SQL_C_CHAR, // no 16-bit chars
+ #endif
+ strbufP, // where to store the data
+ maxstringlen, // max size of string
+ (SQLLEN*)&siz // returns real remaining size of data (=what we got in this call + what still remains to be fetched)
+ );
+ if (res==SQL_NO_DATA) {
+ // no (more) data
+ gotAllData=true;
+ break;
+ }
+ if (res!=SQL_SUCCESS && res!=SQL_SUCCESS_WITH_INFO) {
+ checkStatementError(res,aStatement);
+ break;
+ }
+ // determine size
+ if (siz==SQL_NULL_DATA) {
+ // NULL data
+ gotAllData=true;
+ break;
+ }
+ else if (siz==SQL_NO_TOTAL) {
+ // we do not know how much is remaining, so it's certainly at least a full buffer
+ PDEBUGPRINTFX(DBG_DBAPI+DBG_EXOTIC,("SQLGetData returned SQL_NO_TOTAL and %ld bytes of data",maxstringlen));
+ siz=maxstringlen;
+ nextbuflen=maxstringlen*2; // suggest next buffer twice as big as current one
+ }
+ else {
+ // what we get is either the rest or a full buffer
+ if (siz>maxstringlen) {
+ PDEBUGPRINTFX(DBG_DBAPI+DBG_EXOTIC,("SQLGetData returned %ld bytes of %ld total remaining",maxstringlen,siz));
+ // that's how much we need for the remaining data. Plus one to avoid extra loop
+ // at end for drivers that do not return state '01004'
+ nextbuflen=siz-maxstringlen+1;
+ // that's how much we got this time: one buffer full
+ siz=maxstringlen;
+ }
+ }
+ gotData=true; // not NULL
+ // now copy data
+ if (res==SQL_SUCCESS_WITH_INFO) {
+ // probably data truncated
+ string msg,sqlstate;
+ getODBCError(res,msg,sqlstate,SQL_HANDLE_STMT,aStatement);
+ if (sqlstate=="01004") {
+ // data truncated.
+ PDEBUGPRINTFX(DBG_DBAPI+DBG_EXOTIC,("SQLGetData returns state '01004' (truncated) -> more data to be fetched"));
+ // - do not yet exit loop
+ gotAllData=false; // not all...
+ gotData=true; // ...but some
+ }
+ else {
+ // otherwise treat as success
+ PDEBUGPRINTFX(DBG_DBAPI+DBG_EXOTIC,("SQLGetData returns state '%s' -> ignore",sqlstate.c_str()));
+ res=SQL_SUCCESS;
+ }
+ }
+ if (res==SQL_SUCCESS) {
+ // it seems that not all drivers return SQL_SUCCESS_WITH_INFO when there is more data to read
+ // so only stop reading here already if we haven't got one buffer full. Otherwise,
+ // loop one more time to try getting more. We'll get SQL_NULL_DATA or SQL_NO_DATA then and exit the loop.
+ if (siz<maxstringlen) {
+ // received all we can receive
+ gotAllData=true;
+ }
+ else {
+ // received exactly one buffer full - maybe there is more (MyODBC on Linux does not issue SQL_SUCCESS_WITH_INFO "01004")
+ PDEBUGPRINTFX(DBG_DBAPI+DBG_EXOTIC,("SQLGetData returns SQL_SUCCESS but buffer full of data (%ld bytes) -> try to get more",(uInt32)siz));
+ }
+ }
+ // copy what we already have
+ if (aAsBlob)
+ aStringValue.append((const char *)strbufP,siz); // assign all data 1:1 to string
+ #ifdef ODBC_UNICODE
+ else if (aCharSet==chs_utf16)
+ wStr.append((const char *)strbufP,siz); // assign all data 1:1 to wide string buffer, will be converted later
+ #endif
+ else
+ appendStringAsUTF8((const char *)strbufP, aStringValue, aCharSet, lem_cstr); // Convert to app-charset (UTF8) and C-type lineends
+ // get a bigger buffer in case there's more to fetch and we haven't got 64k already
+ if (!gotAllData) {
+ if (maxstringlen<65536 && nextbuflen>maxstringlen) {
+ // we could need a larger buffer
+ maxstringlen = nextbuflen>65536 ? 65536 : nextbuflen;
+ delete strbufP;
+ strbufP = new uInt8[maxstringlen+1];
+ PDEBUGPRINTFX(DBG_DBAPI+DBG_EXOTIC,("Allocating bigger buffer for next call to SQLGetData: %ld bytes",(uInt32)maxstringlen));
+ }
+ }
+ } while(!gotAllData);
+ // done, we don't need the buffer any more
+ if (strbufP) delete strbufP;
+ }
+ catch (...) {
+ // clean up buffer
+ if (strbufP) delete strbufP;
+ throw;
+ }
+ // convert from Unicode to UTF-8 if we got unicode here
+ #ifdef ODBC_UNICODE
+ if (aCharSet==chs_utf16) {
+ appendUTF16AsUTF8((const uInt16 *)wStr.c_str(), wStr.size()/2, ODBC_BIGENDIAN, aStringValue, true, false);
+ }
+ #endif
+ // done
+ return gotData;
+} // TODBCApiAgent::getColumnValueAsString
+
+
+// returns true if successfully and filled aODBCTimestamp
+bool TODBCApiAgent::getColumnAsODBCTimestamp(
+ SQLHSTMT aStatement,
+ sInt16 aColNumber,
+ SQL_TIMESTAMP_STRUCT &aODBCTimestamp
+)
+{
+ SQLRETURN res;
+ SQLINTEGER ind;
+ SQLSMALLINT numcols;
+
+ // check if there aColNumber is in range
+ res = SafeSQLNumResultCols(aStatement,&numcols);
+ checkStatementError(res,aStatement);
+ if (aColNumber<1 || aColNumber>numcols)
+ throw TSyncException(DEBUGTEXT("getColumnAsODBCTimestamp with bad col index","odds5"));
+ // get data
+ res = SafeSQLGetData(
+ aStatement, // statement handle
+ aColNumber, // column number
+ SQL_C_TYPE_TIMESTAMP, // target type: timestamp
+ &aODBCTimestamp, // where to store the timestamp
+ 0, // n/a
+ (SQLLEN*)&ind // returns indication if NULL
+ );
+ checkStatementError(res,aStatement);
+ // return true if data filled in
+ return (ind!=SQL_NULL_DATA && ind!=SQL_NO_TOTAL);
+} // TODBCApiAgent::getColumnAsODBCTimestamp
+
+
+// get value (UTC timestamp), returns false if no Data or Null
+bool TODBCApiAgent::getColumnValueAsTimestamp(
+ SQLHSTMT aStatement,
+ sInt16 aColNumber,
+ lineartime_t &aTimestamp
+)
+{
+ SQL_TIMESTAMP_STRUCT odbctimestamp;
+
+ if (getColumnAsODBCTimestamp(aStatement,aColNumber,odbctimestamp)) {
+ // there is a timestamp
+ DEBUGPRINTFX(DBG_DBAPI+DBG_EXOTIC,(
+ "ODBCTimestamp: %04hd-%02hd-%02hd %02hd:%02hd:%02hd.%03ld",
+ odbctimestamp.year,odbctimestamp.month,odbctimestamp.day,
+ odbctimestamp.hour,odbctimestamp.minute,odbctimestamp.second,
+ odbctimestamp.fraction / 1000000
+ ));
+ aTimestamp =
+ date2lineartime(odbctimestamp.year,odbctimestamp.month,odbctimestamp.day) +
+ time2lineartime(odbctimestamp.hour,odbctimestamp.minute,odbctimestamp.second, odbctimestamp.fraction / 1000000);
+ return true;
+ }
+ // no data
+ aTimestamp=0;
+ return false;
+} // TODBCApiAgent::getColumnValueAsTimestamp
+
+
+// get value, returns false if no Data or Null
+bool TODBCApiAgent::getColumnValueAsTime(
+ SQLHSTMT aStatement,
+ sInt16 aColNumber,
+ lineartime_t &aTime
+)
+{
+ SQL_TIMESTAMP_STRUCT odbctimestamp;
+
+ if (getColumnAsODBCTimestamp(aStatement,aColNumber,odbctimestamp)) {
+ // there is a timestamp
+ aTime = time2lineartime(
+ odbctimestamp.hour,
+ odbctimestamp.minute,
+ odbctimestamp.second,
+ odbctimestamp.fraction / 1000000
+ );
+ return true;
+ }
+ // no data
+ aTime=0;
+ return false;
+} // TODBCApiAgent::getColumnValueAsTime
+
+
+// get value, returns false if no Data or Null
+bool TODBCApiAgent::getColumnValueAsDate(
+ SQLHSTMT aStatement,
+ sInt16 aColNumber,
+ lineartime_t &aDate
+)
+{
+ SQL_TIMESTAMP_STRUCT odbctimestamp;
+
+ if (getColumnAsODBCTimestamp(aStatement,aColNumber,odbctimestamp)) {
+ // there is a timestamp
+ aDate = date2lineartime(
+ odbctimestamp.year,
+ odbctimestamp.month,
+ odbctimestamp.day
+ );
+ return true;
+ }
+ // no data
+ aDate=0;
+ return false;
+} // TODBCApiAgent::getColumnValueAsDate
+
+
+
+// get column value as database field
+bool TODBCApiAgent::getColumnValueAsField(
+ SQLHSTMT aStatement, sInt16 aColIndex, TDBFieldType aDbfty, TItemField *aFieldP,
+ TCharSets aDataCharSet, timecontext_t aTimecontext, bool aMoveToUserContext
+)
+{
+ string val;
+ lineartime_t ts, basedate=0; // no base date yet
+ sInt32 dbts;
+ timecontext_t tctx = TCTX_UNKNOWN;
+ sInt16 moffs=0;
+ sInt32 i=0;
+ TTimestampField *tsfP; // timestamp field helper
+ double hrs=0;
+ bool notnull=false; // default to empty
+ // field available in multifielditem
+ switch (aDbfty) {
+ case dbft_uctoffsfortime_hours:
+ notnull=getColumnValueAsDouble(aStatement,aColIndex,hrs);
+ if (!notnull) goto assignzone; // assign TCTX_UNKNOWN
+ moffs=(sInt16)(hrs*MinsPerHour); // convert to minutes
+ goto assignoffs;
+ case dbft_uctoffsfortime_mins:
+ notnull=getColumnValueAsLong(aStatement,aColIndex,i);
+ if (!notnull) goto assignzone; // assign TCTX_UNKNOWN
+ moffs=i; // these are minutes
+ goto assignoffs;
+ case dbft_uctoffsfortime_secs:
+ notnull=getColumnValueAsLong(aStatement,aColIndex,i);
+ if (!notnull) goto assignzone; // assign TCTX_UNKNOWN
+ moffs = i / SecsPerMin;
+ goto assignoffs;
+ case dbft_zonename:
+ // get zone name as string
+ notnull=getColumnValueAsString(aStatement,aColIndex,val,aDataCharSet, lem_cstr);
+ if (!notnull) goto assignzone; // assign TCTX_UNKNOWN
+ // convert to context
+ TimeZoneNameToContext(val.c_str(), tctx, getSessionZones());
+ goto assignzone;
+ assignoffs:
+ tctx = TCTX_MINOFFSET(moffs);
+ assignzone:
+ // zone works only for timestamps
+ // - move to new zone or assign zone if timestamp is still empty or floating
+ if (aFieldP->isBasedOn(fty_timestamp)) {
+ static_cast<TTimestampField *>(aFieldP)->moveToContext(tctx,true);
+ }
+ break;
+
+ case dbft_lineardate:
+ case dbft_unixdate_s:
+ case dbft_unixdate_ms:
+ case dbft_unixdate_us:
+ notnull=getColumnValueAsLong(aStatement,aColIndex,dbts);
+ if (notnull)
+ ts=dbIntToLineartime(dbts,aDbfty);
+ goto dateonly;
+ case dbft_dateonly:
+ case dbft_date:
+ notnull=getColumnValueAsDate(aStatement,aColIndex,ts);
+ dateonly:
+ tctx = TCTX_UNKNOWN | TCTX_DATEONLY; // dates are always floating
+ goto assignval;
+ case dbft_timefordate:
+ // get base date used to build up datetime
+ if (aFieldP->isBasedOn(fty_timestamp)) {
+ tsfP = static_cast<TTimestampField *>(aFieldP);
+ basedate = tsfP->getTimestampAs(TCTX_UNKNOWN); // unconverted, as-is
+ }
+ // otherwise handle like time
+ case dbft_time:
+ notnull=getColumnValueAsTime(aStatement,aColIndex,ts);
+ // combine with date
+ ts+=basedate;
+ goto assigntimeval;
+ case dbft_timestamp:
+ notnull=getColumnValueAsTimestamp(aStatement,aColIndex,ts);
+ goto assigntimeval;
+ case dbft_lineartime:
+ case dbft_unixtime_s:
+ case dbft_unixtime_ms:
+ case dbft_unixtime_us:
+ notnull=getColumnValueAsLong(aStatement,aColIndex,dbts);
+ if (notnull)
+ ts=dbIntToLineartime(dbts,aDbfty);
+ assigntimeval:
+ // time values will be put into aTimecontext (usually <datatimezone>, but can
+ // be TCTX_UNKNOWN for field explicitly marked floating in DB with <map mode="f"...>
+ tctx = aTimecontext;
+ assignval:
+ // something convertible to lineartime_t
+ if (notnull) {
+ // first, if output in user zone is selected, move timestamp to user zone
+ if (aMoveToUserContext) {
+ if (TzConvertTimestamp(ts,tctx,fUserTimeContext,getSessionZones()))
+ tctx = fUserTimeContext; // moved to user zone
+ }
+ // now store in item field
+ if (aFieldP->isBasedOn(fty_timestamp)) {
+ tsfP = static_cast<TTimestampField *>(aFieldP);
+ // assign timestamp and context passed (TCTX_UNKNOWN for floating)
+ tsfP->setTimestampAndContext(ts,tctx);
+ }
+ else {
+ // destination is NOT timestamp, assign ISO date/time string,
+ // either UTC or qualified with local zone offset
+ bool dateonly = aDbfty==dbft_date || aDbfty==dbft_dateonly;
+ tctx = dateonly ? TCTX_UNKNOWN | TCTX_DATEONLY : aTimecontext;
+ // - aWithTime, NOT aAsUTC, aWithOffset, offset, NOT aShowOffset
+ TimestampToISO8601Str(val, ts, tctx, false, false);
+ aFieldP->setAsString(val.c_str());
+ }
+ }
+ else
+ aFieldP->assignEmpty(); // NULL value: not assigned
+ break;
+ case dbft_blob:
+ // Database field is BLOB, assign it to item as binary string
+ if ((notnull=getColumnValueAsString(aStatement,aColIndex,val,chs_unknown,true)))
+ aFieldP->setAsString(val.c_str(),val.size());
+ else
+ aFieldP->assignEmpty(); // NULL value: not assigned
+ break;
+ case dbft_string:
+ case dbft_numeric:
+ default:
+ // Database field is string (or unknown), assign to item as string
+ if ((notnull=getColumnValueAsString(aStatement,aColIndex,val,aDataCharSet)))
+ aFieldP->setAsString(val.c_str());
+ else
+ aFieldP->assignEmpty(); // NULL value: not assigned
+ break;
+ } // switch
+ return notnull;
+} // TODBCApiAgent::getColumnValueAsField
+
+
+// bind parameters (and values for IN-Params) to the statement
+void TODBCApiAgent::bindSQLParameters(
+ TSyncSession *aSessionP,
+ SQLHSTMT aStatement,
+ TParameterMapList &aParamMapList, // the list of mapped parameters
+ TCharSets aDataCharSet,
+ TLineEndModes aDataLineEndMode
+)
+{
+ SQLSMALLINT valueType,paramType;
+ SQLUSMALLINT paramNo=1;
+ SQLUINTEGER colSiz;
+ string s1,s2;
+ TParameterMapList::iterator pos;
+ bool copyvalue;
+ for (pos=aParamMapList.begin();pos!=aParamMapList.end();++pos) {
+ // bind parameter to statement
+ copyvalue=false;
+ colSiz=std_paramsize;
+ switch (pos->parammode) {
+ case param_localid_str:
+ case param_localid_int:
+ case param_remoteid_str:
+ case param_remoteid_int:
+ pos->StrLen_or_Ind=0; // no size indication known yet
+ // a item id, always as string
+ valueType=SQL_C_CHAR;
+ // but column could also be integer
+ paramType=pos->parammode==param_localid_int || pos->parammode==param_remoteid_int ? SQL_INTEGER : SQL_VARCHAR;
+ // get input value (if any)
+ if (pos->inparam) {
+ if (pos->itemP) {
+ // item available
+ pos->ParameterValuePtr=(void *)(
+ pos->parammode==param_localid_str || pos->parammode==param_localid_int ?
+ pos->itemP->getLocalID() :
+ pos->itemP->getRemoteID()
+ );
+ }
+ else {
+ // no item
+ pos->ParameterValuePtr=(void *)""; // empty value
+ }
+ pos->StrLen_or_Ind = strlen((const char *)pos->ParameterValuePtr); // actual length
+ }
+ // maximum value size
+ pos->BufferLength=std_paramsize+1; // %%% fixed value for ids, should be enough for all cases
+ colSiz=std_paramsize;
+ break;
+ case param_field:
+ pos->StrLen_or_Ind=0; // no size indication known for field yet (for buffer we use predefined values)
+ case param_buffer:
+ // determine value type and pointer
+ switch (pos->dbFieldType) {
+ case dbft_numeric:
+ valueType=SQL_C_CHAR;
+ paramType=SQL_INTEGER;
+ break;
+ case dbft_blob:
+ valueType=SQL_C_BINARY;
+ paramType=SQL_LONGVARBINARY;
+ break;
+ case dbft_string:
+ default:
+ #ifdef ODBC_UNICODE
+ if (aDataCharSet==chs_utf16) {
+ // 16-bit string
+ valueType=SQL_C_WCHAR;
+ paramType=SQL_WVARCHAR;
+ }
+ else
+ #endif
+ {
+ // 8-bit string
+ valueType=SQL_C_CHAR;
+ paramType=SQL_VARCHAR;
+ }
+ break;
+ }
+ // that's all for param with already prepared buffer
+ if (pos->parammode==param_buffer)
+ break;
+ // get maximum value size
+ colSiz=pos->maxSize;
+ if (pos->maxSize==0) {
+ // no max size specified
+ colSiz=std_paramsize; // default to standard size
+ }
+ // %%%no longer, we calc it below now%%% pos->BufferLength=colSiz+1;
+ if (pos->inparam) {
+ if (pos->fieldP) {
+ // get actual value to pass as param into string s2
+ switch (pos->dbFieldType) {
+ default:
+ case dbft_string:
+ case dbft_numeric:
+ // get as app string
+ pos->fieldP->getAsString(s1);
+ // convert to database string
+ s2.erase();
+ #ifdef ODBC_UNICODE
+ if (aDataCharSet==chs_utf16) {
+ // 16-bit string
+ appendUTF8ToUTF16ByteString(
+ s1.c_str(),
+ s2,
+ ODBC_BIGENDIAN,
+ aDataLineEndMode,
+ pos->maxSize // max size (0 = unlimited)
+ );
+ }
+ else
+ #endif // ODBC_UNICODE
+ {
+ // 8-bit string
+ appendUTF8ToString(
+ s1.c_str(),
+ s2,
+ aDataCharSet,
+ aDataLineEndMode,
+ qm_none, // no quoting needed
+ pos->maxSize // max size (0 = unlimited)
+ );
+ }
+ break;
+ case dbft_blob:
+ #ifndef _MSC_VER // does not understand warnings
+ #warning "maybe we should store blobs using SQLPutData and SQL_DATA_AT_EXEC mode"
+ #endif
+
+ // get as unconverted binary chunk
+ if (pos->fieldP->isBasedOn(fty_blob))
+ static_cast<TBlobField *>(pos->fieldP)->getBlobAsString(s2);
+ else
+ pos->fieldP->getAsString(s2);
+ break;
+ } // switch
+ // s2 now contains the value
+ pos->ParameterValuePtr=(void *)s2.c_str(); // value
+ // default to full length of input string
+ pos->StrLen_or_Ind=s2.size(); // size
+ pos->BufferLength=s2.size(); // default to as long as string is
+ // limit to max size
+ if (pos->maxSize!=0 && pos->maxSize<s2.size()) {
+ pos->BufferLength=pos->maxSize;
+ pos->StrLen_or_Ind=pos->maxSize;
+ }
+ // expand buffer to colsiz if input is larger than default column size
+ if (colSiz<pos->BufferLength)
+ colSiz=pos->BufferLength;
+ // buffer for output must be at least colsiz (which is maxsize, or std_paramsize if no maxsize specified)
+ if (pos->outparam) {
+ // input/output param, make buffer minimally as large as indicated by colsiz
+ if (pos->BufferLength<colSiz)
+ pos->BufferLength=colSiz;
+ }
+ // plus one for terminator
+ pos->BufferLength+=1;
+ copyvalue=true; // we need to copy it
+ } // if field
+ } // if inparam
+ break;
+ } // switch parammode
+ // if this is an out param, create a buffer of BufferLength
+ if (pos->parammode!=param_buffer) {
+ if (pos->outparam || copyvalue) {
+ // determine needed size of buffer
+ /* %%% no longer needed as we calculate buffer size above now
+ if (!pos->outparam || pos->BufferLength<pos->StrLen_or_Ind+1) {
+ // adjust col size
+ colSiz=pos->StrLen_or_Ind;
+ // adapt buffer size to actual length of input param
+ pos->BufferLength=colSiz+1; // set buffer size to what we need for input parameter
+ }
+ */
+ // create buffer
+ void *bP = sysync_malloc(pos->BufferLength);
+ pos->mybuffer=true; // this is now a buffer allocated by myself
+ if (pos->inparam) {
+ // init buffer with input value
+ memcpy(bP,pos->ParameterValuePtr,pos->StrLen_or_Ind+1);
+ }
+ // pass buffer, not original value
+ pos->ParameterValuePtr = bP;
+ // make sure buffer contains a NUL terminator
+ *((uInt8 *)bP+pos->StrLen_or_Ind)=0;
+ }
+ if (pos->outparam && !pos->inparam)
+ pos->StrLen_or_Ind=SQL_NULL_DATA; // out only has no indicator for input
+ }
+ // now actually bind to parameter
+ POBJDEBUGPRINTFX(aSessionP,DBG_DBAPI+DBG_EXOTIC,(
+ "SQLBind: paramNo=%hd, in=%d, out=%d, parammode=%hd, valuetype=%hd, paramtype=%hd, lenorind=%ld, valptr=%lX, bufsiz=%ld, maxcol=%ld, colsiz=%ld",
+ paramNo,
+ (int)pos->inparam,
+ (int)pos->outparam,
+ (uInt16) (pos->outparam ? (pos->inparam ? SQL_PARAM_INPUT_OUTPUT : SQL_PARAM_OUTPUT ) : SQL_PARAM_INPUT), // type of param
+ valueType,
+ paramType,
+ pos->StrLen_or_Ind,
+ pos->ParameterValuePtr,
+ pos->BufferLength,
+ pos->maxSize,
+ colSiz
+ ));
+ SQLRETURN res=SQLBindParameter(
+ aStatement,
+ paramNo, // parameter number
+ pos->outparam ? (pos->inparam ? SQL_PARAM_INPUT_OUTPUT : SQL_PARAM_OUTPUT ) : SQL_PARAM_INPUT, // type of param
+ valueType, // value type: how we want it represented
+ paramType, // parameter type (integer or char)
+ (colSiz==0 ? 1 : colSiz), // column size, relevant for char parameter only (must never be 0, even for zero length input-only params)
+ 0, // decimal digits
+ pos->ParameterValuePtr, // parameter value
+ pos->BufferLength, // value buffer size (for output params)
+ (SQLLEN*)&(pos->StrLen_or_Ind) // length or indicator
+ );
+ checkStatementError(res,aStatement);
+ // next param
+ paramNo++;
+ } // for all parametermaps
+} // TODBCApiAgent::bindSQLParameters
+
+
+// save out parameter values and clean up
+void TODBCApiAgent::saveAndCleanupSQLParameters(
+ TSyncSession *aSessionP,
+ SQLHSTMT aStatement,
+ TParameterMapList &aParamMapList, // the list of mapped parameters
+ TCharSets aDataCharSet,
+ TLineEndModes aDataLineEndMode
+)
+{
+ SQLUSMALLINT paramNo=1;
+ string s,s2;
+ TParameterMapList::iterator pos;
+ for (pos=aParamMapList.begin();pos!=aParamMapList.end();++pos) {
+ // save output parameter value
+ if (pos->outparam) {
+ switch (pos->parammode) {
+ case param_localid_str:
+ case param_localid_int:
+ case param_remoteid_str:
+ case param_remoteid_int:
+ // id output params are always strings
+ POBJDEBUGPRINTFX(aSessionP,DBG_DBAPI+DBG_EXOTIC,(
+ "Postprocessing Item ID param: in=%d, out=%d, lenorind=%ld, valptr=%lX, bufsiz=%ld",
+ (int)pos->inparam,
+ (int)pos->outparam,
+ pos->StrLen_or_Ind,
+ pos->ParameterValuePtr,
+ pos->BufferLength
+ ));
+ /* Note: len or ind does not contain a useful value
+ DEBUGPRINTFX(DBG_DBAPI,("%%%% saving remote or localid out param: len=%ld, '%s'",pos->StrLen_or_Ind, s.c_str()));
+ if (pos->StrLen_or_Ind==SQL_NULL_DATA || pos->StrLen_or_Ind==SQL_NO_TOTAL)
+ s.erase();
+ else
+ s.assign((const char *)pos->ParameterValuePtr,pos->StrLen_or_Ind);
+ */
+ // SQL_C_CHAR is null terminated, so just assign
+ s=(const char *)pos->ParameterValuePtr;
+ // assign to item
+ if (pos->itemP) {
+ if (pos->parammode==param_localid_str || pos->parammode==param_localid_int)
+ pos->itemP->setLocalID(s.c_str());
+ else
+ pos->itemP->setRemoteID(s.c_str());
+ }
+ break;
+ case param_buffer:
+ // buffer will be processed externally
+ if (pos->outSiz) {
+ if (pos->StrLen_or_Ind==SQL_NULL_DATA || pos->StrLen_or_Ind==SQL_NO_TOTAL)
+ *(pos->outSiz) = 0; // no output
+ else
+ *(pos->outSiz) = pos->StrLen_or_Ind; // indicate how much is in buffer
+ }
+ case param_field:
+ // get value field
+ if (pos->fieldP) {
+ POBJDEBUGPRINTFX(aSessionP,DBG_DBAPI+DBG_EXOTIC,(
+ "Postprocessing field param: in=%d, out=%d, lenorind=%ld, valptr=%lX, bufsiz=%ld",
+ (int)pos->inparam,
+ (int)pos->outparam,
+ pos->StrLen_or_Ind,
+ pos->ParameterValuePtr,
+ pos->BufferLength
+ ));
+ // check for NULL
+ if (pos->StrLen_or_Ind==SQL_NULL_DATA || pos->StrLen_or_Ind==SQL_NO_TOTAL)
+ pos->fieldP->assignEmpty(); // NULL is empty
+ else {
+ // save value according to type
+ switch (pos->dbFieldType) {
+ default:
+ case dbft_string:
+ case dbft_numeric:
+ // get as db string
+ /* Note: len or ind does not contain a useful value
+ DEBUGPRINTFX(DBG_DBAPI,("%%%% saving remote or localid out param: len=%ld, '%s'",pos->StrLen_or_Ind, s.c_str()));
+ if (pos->StrLen_or_Ind==SQL_NULL_DATA || pos->StrLen_or_Ind==SQL_NO_TOTAL)
+ s.erase();
+ else
+ s.assign((const char *)pos->ParameterValuePtr,pos->StrLen_or_Ind);
+ */
+ #ifdef ODBC_UNICODE
+ if (aDataCharSet==chs_utf16) {
+ // SQL_C_WCHAR is null terminated, so just assign
+ appendUTF16AsUTF8(
+ (const uInt16 *)pos->ParameterValuePtr,
+ pos->StrLen_or_Ind, // num of 16-bit chars
+ ODBC_BIGENDIAN,
+ s2,
+ true, // convert line ends
+ false // no filemaker CRs
+ );
+ }
+ else
+ #endif
+ {
+ // SQL_C_CHAR is null terminated, so just assign
+ s=(const char *)pos->ParameterValuePtr;
+ // convert to app string
+ s2.erase();
+ appendStringAsUTF8(
+ (const char *)s.c_str(),
+ s2,
+ aDataCharSet,
+ aDataLineEndMode
+ );
+ }
+ pos->fieldP->setAsString(s2);
+ break;
+ case dbft_blob:
+ // save as it comes from DB
+ pos->fieldP->setAsString((const char *)pos->ParameterValuePtr,pos->StrLen_or_Ind);
+ break;
+ } // switch
+ } // if not NULL
+ } // if field
+ break;
+ } // switch parammode
+ } // if outparam
+ // if there is a buffer, free it
+ if (pos->mybuffer && pos->ParameterValuePtr) {
+ // delete buffer if we have allocated one
+ sysync_free(pos->ParameterValuePtr);
+ pos->ParameterValuePtr=NULL;
+ pos->mybuffer=false;
+ }
+ // next param
+ paramNo++;
+ } // for all parametermaps
+} // TODBCApiAgent::saveAndCleanupSQLParameters
+
+// Parameter handling for session level statements
+
+// bind parameters (and values for IN-Params) to the statement
+void TODBCApiAgent::bindSQLParameters(
+ SQLHSTMT aStatement
+)
+{
+ bindSQLParameters(
+ this,
+ aStatement,
+ fParameterMaps,
+ fConfigP->fDataCharSet,
+ fConfigP->fDataLineEndMode
+ );
+} // TODBCApiAgent::bindSQLParameters
+
+
+// save out parameter values and clean up
+void TODBCApiAgent::saveAndCleanupSQLParameters(
+ SQLHSTMT aStatement
+)
+{
+ saveAndCleanupSQLParameters(
+ this,
+ aStatement,
+ fParameterMaps,
+ fConfigP->fDataCharSet,
+ fConfigP->fDataLineEndMode
+ );
+} // TODBCApiAgent::bindSQLParameters
+
+
+#endif // ODBCAPI_SUPPORT
+
+
+// SQLite utils
+// ============
+
+#ifdef SQLITE_SUPPORT
+
+// get SQLite error message for given result code
+// - returns false if no error
+bool TODBCApiAgent::getSQLiteError(int aRc,string &aMessage, sqlite3 *aDb)
+{
+ if (aRc == SQLITE_OK || aRc == SQLITE_ROW) return false; // no error
+ StringObjPrintf(aMessage,"SQLite Error %d : %s",aRc,aDb==NULL ? "<cannot look up text>" : sqlite3_errmsg(aDb));
+ return true;
+} // getSQLiteError
+
+
+// check if aResult signals error and throw exception if so
+void TODBCApiAgent::checkSQLiteError(int aRc, sqlite3 *aDb)
+{
+ string msg;
+
+ if (getSQLiteError(aRc,msg,aDb)) {
+ // error
+ throw TSyncException(msg.c_str());
+ }
+} // TODBCApiAgent::checkSQLiteError
+
+
+// - check if aRc signals error and throw exception if so
+// returns true if data available, false if not
+bool TODBCApiAgent::checkSQLiteHasData(int aRc, sqlite3 *aDb)
+{
+ if (aRc==SQLITE_DONE)
+ return false; // signal NO DATA
+ else if (aRc==SQLITE_ROW)
+ return true; // signal data
+ else
+ checkSQLiteError(aRc,aDb);
+ return true; // signal ok
+} // TODBCApiAgent::checkStatementHasData
+
+
+// get column value from SQLite result
+bool TODBCApiAgent::getSQLiteColValueAsField(
+ sqlite3_stmt *aStatement, sInt16 aColIndex, TDBFieldType aDbfty, TItemField *aFieldP,
+ TCharSets aDataCharSet, timecontext_t aTimecontext, bool aMoveToUserContext
+)
+{
+ string val;
+ lineartime_t ts;
+ timecontext_t tctx = TCTX_UNKNOWN;
+ sInt16 moffs=0;
+ TTimestampField *tsfP; // timestamp field helper
+ size_t siz;
+ double hrs=0;
+ // determine if NULL
+ bool notnull=sqlite3_column_type(aStatement,aColIndex)!=SQLITE_NULL; // if column is not null
+ // field available in multifielditem
+ switch (aDbfty) {
+ case dbft_uctoffsfortime_hours:
+ if (!notnull) goto assignzone; // assign TCTX_UNKNOWN
+ hrs = sqlite3_column_double(aStatement,aColIndex);
+ moffs=(sInt16)(hrs*MinsPerHour); // convert to minutes
+ goto assignoffs;
+ case dbft_uctoffsfortime_mins:
+ if (!notnull) goto assignzone; // assign TCTX_UNKNOWN
+ moffs = sqlite3_column_int(aStatement,aColIndex);
+ goto assignoffs;
+ case dbft_uctoffsfortime_secs:
+ if (!notnull) goto assignzone; // assign TCTX_UNKNOWN
+ moffs = sqlite3_column_int(aStatement,aColIndex);
+ moffs /= SecsPerMin;
+ goto assignoffs;
+ case dbft_zonename:
+ if (!notnull) goto assignzone; // assign TCTX_UNKNOWN
+ // get zone name as string
+ appendStringAsUTF8((const char *)sqlite3_column_text(aStatement,aColIndex), val, aDataCharSet, lem_cstr);
+ // convert to context
+ TimeZoneNameToContext(val.c_str(), tctx, getSessionZones());
+ goto assignzone;
+ assignoffs:
+ tctx = TCTX_MINOFFSET(moffs);
+ assignzone:
+ // zone works only for timestamps
+ // - move to new zone or assign zone if timestamp is still empty or floating
+ if (aFieldP->isBasedOn(fty_timestamp)) {
+ static_cast<TTimestampField *>(aFieldP)->moveToContext(tctx,true);
+ }
+ break;
+
+ case dbft_lineardate:
+ case dbft_unixdate_s:
+ case dbft_unixdate_ms:
+ case dbft_unixdate_us:
+ if (notnull)
+ ts = dbIntToLineartime(sqlite3_column_int64(aStatement,aColIndex),aDbfty);
+ tctx = TCTX_UNKNOWN | TCTX_DATEONLY; // dates are always floating
+ goto assignval;
+ case dbft_lineartime:
+ case dbft_unixtime_s:
+ case dbft_unixtime_ms:
+ case dbft_unixtime_us:
+ if (notnull)
+ ts = dbIntToLineartime(sqlite3_column_int64(aStatement,aColIndex),aDbfty);
+ // time values will be put into aTimecontext (usually <datatimezone>, but can
+ // be TCTX_UNKNOWN for field explicitly marked floating in DB with <map mode="f"...>
+ tctx = aTimecontext;
+ assignval:
+ // something convertible to lineartime_t
+ if (notnull) {
+ // first, if output in user zone is selected, move timestamp to user zone
+ if (aMoveToUserContext) {
+ if (TzConvertTimestamp(ts,tctx,fUserTimeContext,getSessionZones()))
+ tctx = fUserTimeContext; // moved to user zone
+ }
+ // now store in item field
+ if (aFieldP->isBasedOn(fty_timestamp)) {
+ tsfP = static_cast<TTimestampField *>(aFieldP);
+ // first assign timestamp and context (TCTX_UNKNOWN for floating)
+ tsfP->setTimestampAndContext(ts,tctx);
+ }
+ else {
+ // destination is NOT timestamp, assign ISO date/time string,
+ // either UTC or qualified with local zone offset
+ bool dateonly = aDbfty==dbft_date || aDbfty==dbft_dateonly;
+ tctx = dateonly ? TCTX_UNKNOWN | TCTX_DATEONLY : aTimecontext;
+ // - aWithTime, NOT aAsUTC, aWithOffset, offset, NOT aShowOffset
+ TimestampToISO8601Str(val, ts, tctx, false, false);
+ aFieldP->setAsString(val.c_str());
+ }
+ }
+ else
+ aFieldP->assignEmpty(); // NULL value: not assigned
+ break;
+ case dbft_blob:
+ // Database field is BLOB, assign it to item as binary string
+ siz=0;
+ if (notnull) {
+ siz = sqlite3_column_bytes(aStatement,aColIndex);
+ }
+ if (siz>0)
+ aFieldP->setAsString((cAppCharP)sqlite3_column_blob(aStatement,aColIndex),siz);
+ else
+ aFieldP->assignEmpty(); // NULL or empty value: not assigned
+ break;
+ case dbft_string:
+ case dbft_numeric:
+ default:
+ // Database field is string (or unknown), assign to item as string
+ if (notnull) {
+ appendStringAsUTF8((const char *)sqlite3_column_text(aStatement,aColIndex), val, aDataCharSet, lem_cstr); // Convert to app-charset (UTF8) and C-type lineends
+ aFieldP->setAsString(val.c_str());
+ }
+ else
+ aFieldP->assignEmpty(); // NULL value: not assigned
+ break;
+ } // switch
+ return notnull;
+} // TODBCApiAgent::getSQLiteColValueAsField
+
+
+// - prepare SQLite statement
+void TODBCApiAgent::prepareSQLiteStatement(
+ cAppCharP aSQL,
+ sqlite3 *aDB,
+ sqlite3_stmt *&aStatement
+)
+{
+ const char *sqltail;
+
+ // discard eventually existing one
+ if (aStatement) {
+ sqlite3_finalize(aStatement);
+ aStatement=NULL;
+ }
+ // make new one
+ #if (SQLITE_VERSION_NUMBER>=3003009)
+ // use new recommended v2 call with SQLite >=3.3.9
+ int rc = sqlite3_prepare_v2(
+ aDB, /* Database handle */
+ aSQL, /* SQL statement, UTF-8 encoded */
+ -1, /* null terminated string */
+ &aStatement, /* OUT: Statement handle */
+ &sqltail /* OUT: Pointer to unused portion of zSql */
+ );
+ #else
+ // use now deprecated call for older SQLite3 version compatibility
+ int rc = sqlite3_prepare(
+ aDB, /* Database handle */
+ aSQL, /* SQL statement, UTF-8 encoded */
+ -1, /* null terminated string */
+ &aStatement, /* OUT: Statement handle */
+ &sqltail /* OUT: Pointer to unused portion of zSql */
+ );
+ #endif
+ checkSQLiteError(rc,aDB);
+} // TODBCApiAgent::prepareSQLiteStatement
+
+
+// bind parameter values to the statement (only IN-params for strings and BLOBs are supported at all)
+void TODBCApiAgent::bindSQLiteParameters(
+ TSyncSession *aSessionP,
+ sqlite3_stmt *aStatement,
+ TParameterMapList &aParamMapList, // the list of mapped parameters
+ TCharSets aDataCharSet,
+ TLineEndModes aDataLineEndMode
+)
+{
+ string s1,s2;
+ TParameterMapList::iterator pos;
+ int paramno=1;
+ int rc;
+ for (pos=aParamMapList.begin();pos!=aParamMapList.end();++pos) {
+ // bind parameter to statement
+ rc=SQLITE_OK;
+ if (pos->inparam) {
+ // SQLite only supports input params
+ switch (pos->parammode) {
+ case param_field:
+ if (pos->fieldP) {
+ // get actual value to pass as param into string s2
+ switch (pos->dbFieldType) {
+ default:
+ case dbft_string:
+ case dbft_numeric:
+ // get as app string
+ pos->fieldP->getAsString(s1);
+ // convert to database string
+ s2.erase();
+ appendUTF8ToString(
+ s1.c_str(),
+ s2,
+ aDataCharSet,
+ aDataLineEndMode,
+ qm_none, // no quoting needed
+ pos->maxSize // max size (0 = unlimited)
+ );
+ // s2 is on the stack and will be deallocated on routine exit
+ rc=sqlite3_bind_text(aStatement,paramno++,s2.c_str(),s2.size(),SQLITE_TRANSIENT);
+ break;
+ case dbft_blob:
+ // get as unconverted binary chunk
+ if (pos->fieldP->isBasedOn(fty_blob)) {
+ void *ptr;
+ size_t sz;
+ static_cast<TBlobField *>(pos->fieldP)->getBlobDataPtrSz(ptr,sz);
+ // BLOB buffer remains static as until statement is finalized
+ rc=sqlite3_bind_blob(aStatement,paramno++,ptr,sz,SQLITE_STATIC);
+ }
+ else {
+ pos->fieldP->getAsString(s2);
+ // s2 is on the stack and will be deallocated on routine exit
+ rc=sqlite3_bind_blob(aStatement,paramno++,s2.c_str(),s2.size(),SQLITE_TRANSIENT);
+ }
+ break;
+ } // switch
+ } // if field
+ break;
+ case param_buffer:
+ // we already have buffer pointers
+ switch (pos->dbFieldType) {
+ default:
+ case dbft_string:
+ case dbft_numeric:
+ // external buffer remains stable until statement finalizes
+ rc=sqlite3_bind_text(aStatement,paramno++,(const char *)pos->ParameterValuePtr,pos->StrLen_or_Ind,SQLITE_STATIC);
+ break;
+ case dbft_blob:
+ // external buffer remains stable until statement finalizes
+ rc=sqlite3_bind_blob(aStatement,paramno++,pos->ParameterValuePtr,pos->StrLen_or_Ind,SQLITE_STATIC);
+ break;
+ } // switch
+ break;
+ case param_localid_int:
+ case param_localid_str:
+ case param_remoteid_int:
+ case param_remoteid_str:
+ // invalid
+ break;
+ } // switch parammode
+ if (rc==SQLITE_OK) {
+ POBJDEBUGPRINTFX(aSessionP,DBG_DBAPI+DBG_EXOTIC,(
+ "Bound Param #%d to SQLite statement",
+ paramno
+ ));
+ }
+ else {
+
+ }
+ } // if inparam
+ else {
+ POBJDEBUGPRINTFX(aSessionP,DBG_ERROR,("SQLite only supports IN params"));
+ }
+ } // for all parametermaps
+} // TODBCApiAgent::bindSQLiteParameters
+
+
+#endif // SQLITE_SUPPORT
+
+
+
+// Session level DB access
+// =======================
+
+
+#ifdef ODBCAPI_SUPPORT
+
+// get database time
+lineartime_t TODBCApiAgent::getDatabaseNowAs(timecontext_t aTimecontext)
+{
+ lineartime_t now;
+
+ if (fConfigP->fGetCurrentDateTimeSQL.empty()) {
+ return inherited::getDatabaseNowAs(aTimecontext); // just use base class' implementation
+ }
+ else {
+ // query database for current time
+ SQLRETURN res;
+ SQLHSTMT statement=newStatementHandle(getODBCConnectionHandle());
+ try {
+ // issue
+ PDEBUGPUTSX(DBG_DBAPI,"SQL for getting current date/time:");
+ PDEBUGPUTSX(DBG_DBAPI,fConfigP->fGetCurrentDateTimeSQL.c_str());
+ TP_DEFIDX(li);
+ TP_SWITCH(li,fTPInfo,TP_database);
+ res = SafeSQLExecDirect(
+ statement,
+ (SQLCHAR *)fConfigP->fGetCurrentDateTimeSQL.c_str(),
+ SQL_NTS
+ );
+ TP_START(fTPInfo,li);
+ checkStatementError(res,statement);
+ // - fetch result row
+ res=SafeSQLFetch(statement);
+ if (!checkStatementHasData(res,statement))
+ throw TSyncException("no data getting date/time from database");
+ // get datetime (in database time context)
+ if (!getColumnValueAsTimestamp(statement,1,now))
+ throw TSyncException("bad data getting date/time from database");
+ SafeSQLCloseCursor(statement);
+ // dispose statement handle
+ SafeSQLFreeHandle(SQL_HANDLE_STMT,statement);
+ }
+ catch (...) {
+ // dispose statement handle
+ SafeSQLCloseCursor(statement);
+ SafeSQLFreeHandle(SQL_HANDLE_STMT,statement);
+ throw;
+ }
+ // convert to requested zone
+ TzConvertTimestamp(now,fConfigP->fCurrentDateTimeZone,aTimecontext,getSessionZones(),TCTX_UNKNOWN);
+ return now;
+ }
+} // TODBCApiAgent::getDatabaseNowAs
+
+
+#ifndef SYSYNC_CLIENT
+
+// info about requested auth type
+TAuthTypes TODBCApiAgent::requestedAuthType(void)
+{
+ TAuthTypes auth = TSyncServer::requestedAuthType();
+
+ // make sure that we don't request MD5 if we cannot check it:
+ // either we need plain text passwords in the DB or SyncML V1.1 MD5 schema
+ if (auth==auth_md5 && fSyncMLVersion<syncml_vers_1_1 && !fConfigP->fClearTextPw) {
+ PDEBUGPRINTFX(DBG_ADMIN,("Switching down to Basic Auth because server cannot check pre-V1.1 MD5"));
+ auth=auth_basic; // force basic auth, this can be checked
+ }
+ return auth;
+} // TODBCApiAgent::requestedAuthType
+
+
+// %%% note: make the following available in clients as well if they support more than
+// pseudo-auth by local config (that is, real nonce is needed)
+
+// - get nonce string, which is expected to be used by remote party for MD5 auth.
+void TODBCApiAgent::getAuthNonce(const char *aDeviceID, string &aAuthNonce)
+{
+ if (!aDeviceID || fConfigP->fSaveNonceSQL.empty()) {
+ // no device ID or no persistent nonce, use method of ancestor
+ TStdLogicAgent::getAuthNonce(aDeviceID,aAuthNonce);
+ }
+ else {
+ // we have a persistent nonce
+ DEBUGPRINTFX(DBG_ADMIN,("getAuthNonce: current auth nonce='%s'",fLastNonce.c_str()));
+ aAuthNonce=fLastNonce.c_str();
+ }
+} // TODBCApiAgent::getAuthNonce
+
+
+// get next nonce (to be sent to remote party)
+void TODBCApiAgent::getNextNonce(const char *aDeviceID, string &aNextNonce)
+{
+ SQLRETURN res;
+ SQLHSTMT statement;
+ string sql;
+
+ if (fConfigP->fSaveNonceSQL.empty()) {
+ // no persistent nonce, use method of ancestor
+ TStdLogicAgent::getNextNonce(aDeviceID,aNextNonce);
+ }
+ else {
+ // we have persistent nonce, create a new one and save it
+ // - create one (pure 7bit ASCII)
+ generateNonce(aNextNonce,aDeviceID,getSystemNowAs(TCTX_UTC)+rand());
+ // - save it
+ try {
+ statement=newStatementHandle(getODBCConnectionHandle());
+ try {
+ // get SQL
+ sql = fConfigP->fSaveNonceSQL;
+ // - substitute: %N = new nonce
+ StringSubst(sql,"%N",aNextNonce,2,chs_ascii,lem_none,fConfigP->fQuotingMode);
+ // - standard substitutions: %u=userkey, %d=devicekey
+ resetSQLParameterMaps();
+ DoSQLSubstitutions(sql);
+ bindSQLParameters(statement);
+ // - issue
+ PDEBUGPUTSX(DBG_ADMIN+DBG_DBAPI,"SQL for saving nonce");
+ PDEBUGPUTSX(DBG_ADMIN+DBG_DBAPI,sql.c_str());
+ TP_DEFIDX(li);
+ TP_SWITCH(li,fTPInfo,TP_database);
+ res = SafeSQLExecDirect(
+ statement,
+ (SQLCHAR *)sql.c_str(),
+ SQL_NTS
+ );
+ TP_START(fTPInfo,li);
+ checkStatementError(res,statement);
+ saveAndCleanupSQLParameters(statement);
+ // commit saved nonce
+ SafeSQLFreeHandle(SQL_HANDLE_STMT,statement);
+ SafeSQLEndTran(SQL_HANDLE_DBC,getODBCConnectionHandle(),SQL_COMMIT);
+ } // try
+ catch (exception &e) {
+ // release the statement handle
+ SafeSQLFreeHandle(SQL_HANDLE_STMT,statement);
+ SafeSQLEndTran(SQL_HANDLE_DBC,getODBCConnectionHandle(),SQL_ROLLBACK);
+ throw;
+ }
+ }
+ catch (exception &e) {
+ PDEBUGPRINTFX(DBG_ERROR,("Failed saving nonce: %s",e.what()));
+ aNextNonce=fLastNonce; // return last nonce again
+ }
+ }
+} // TODBCApiAgent::getNextNonce
+
+#endif // not SYSYNC_CLIENT
+
+
+#ifdef HAS_SQL_ADMIN
+
+// cleanup what is needed after login
+void TODBCApiAgent::LoginCleanUp(void)
+{
+ SQLRETURN res;
+
+ try {
+ // close the transaction as well
+ if (fODBCConnectionHandle!=SQL_NULL_HANDLE) {
+ res=SafeSQLEndTran(SQL_HANDLE_DBC,getODBCConnectionHandle(),SQL_ROLLBACK);
+ checkODBCError(res,SQL_HANDLE_DBC,getODBCConnectionHandle());
+ }
+ }
+ catch (exception &e) {
+ // log error
+ PDEBUGPRINTFX(DBG_ERROR,("TODBCApiAgent::LoginCleanUp: Exception: %s",e.what()));
+ }
+} // TODBCApiAgent::LoginCleanUp
+
+
+// check device related stuff
+void TODBCApiAgent::CheckDevice(const char *aDeviceID)
+{
+ // first let ancestor
+ if (!fConfigP->fGetDeviceSQL.empty()) {
+ SQLRETURN res;
+ SQLHSTMT statement;
+ string sql;
+ statement=newStatementHandle(getODBCConnectionHandle());
+ try {
+ bool creatednew=false;
+ do {
+ // get SQL
+ sql = fConfigP->fGetDeviceSQL;
+ // substitute: %D = deviceID
+ StringSubst(sql,"%D",aDeviceID,2,-1,fConfigP->fDataCharSet,fConfigP->fDataLineEndMode,fConfigP->fQuotingMode);
+ // issue
+ PDEBUGPUTSX(DBG_ADMIN+DBG_DBAPI,"SQL for getting device key (and last nonce sent to device)");
+ PDEBUGPUTSX(DBG_ADMIN+DBG_DBAPI,sql.c_str());
+ TP_DEFIDX(li);
+ TP_SWITCH(li,fTPInfo,TP_database);
+ res = SafeSQLExecDirect(
+ statement,
+ (SQLCHAR *)sql.c_str(),
+ SQL_NTS
+ );
+ TP_START(fTPInfo,li);
+ checkStatementHasData(res,statement);
+ // - fetch result row
+ res=SafeSQLFetch(statement);
+ if (!checkStatementHasData(res,statement)) {
+ if (creatednew) {
+ throw TSyncException("Fatal: inserted device record cannot be found again");
+ }
+ PDEBUGPRINTFX(DBG_ADMIN,("Unknown device '%.30s', creating new record",aDeviceID));
+ #ifndef SYSYNC_CLIENT
+ // device does not exist yet
+ fLastNonce.erase();
+ #endif
+ // create new device
+ SafeSQLCloseCursor(statement);
+ // - get SQL
+ sql = fConfigP->fNewDeviceSQL;
+ string dk; // temp device key
+ // - substitute: %D = deviceID
+ StringSubst(sql,"%D",aDeviceID,2,-1,fConfigP->fDataCharSet,fConfigP->fDataLineEndMode,fConfigP->fQuotingMode);
+ // - issue
+ PDEBUGPUTSX(DBG_ADMIN+DBG_DBAPI,"SQL for inserting new device (and last nonce sent to device)");
+ PDEBUGPUTSX(DBG_ADMIN+DBG_DBAPI,sql.c_str());
+ TP_DEFIDX(li);
+ TP_SWITCH(li,fTPInfo,TP_database);
+ res = SafeSQLExecDirect(
+ statement,
+ (SQLCHAR *)sql.c_str(),
+ SQL_NTS
+ );
+ TP_START(fTPInfo,li);
+ checkStatementError(res,statement);
+ // commit new device
+ SafeSQLEndTran(SQL_HANDLE_DBC,getODBCConnectionHandle(),SQL_COMMIT);
+ // try again to get device key
+ creatednew=true;
+ continue;
+ }
+ else {
+ // device exists, read key and nonce
+ #ifdef SCRIPT_SUPPORT
+ fUnknowndevice=false; // we have seen it once before at least
+ #endif
+ // - device key
+ getColumnValueAsString(statement,1,fDeviceKey,chs_ascii);
+ // - nonce
+ #ifdef SYSYNC_CLIENT
+ string dummy;
+ getColumnValueAsString(statement,2,dummy,chs_ascii);
+ #else
+ getColumnValueAsString(statement,2,fLastNonce,chs_ascii);
+ #endif
+ // - done for now
+ SafeSQLCloseCursor(statement);
+ PDEBUGPRINTFX(DBG_ADMIN,("Device '%.30s' found, fDeviceKey='%.30s'",aDeviceID,fDeviceKey.c_str()));
+ #ifndef SYSYNC_CLIENT
+ DEBUGPRINTFX(DBG_ADMIN,("Last nonce saved for device='%.30s'",fLastNonce.c_str()));
+ #endif
+ break;
+ }
+ } while (true); // do until device found or created new
+ } // try
+ catch (...) {
+ // release the statement handle
+ SafeSQLCloseCursor(statement);
+ SafeSQLFreeHandle(SQL_HANDLE_STMT,statement);
+ SafeSQLEndTran(SQL_HANDLE_DBC,getODBCConnectionHandle(),SQL_ROLLBACK);
+ throw;
+ }
+ // release the statement handle
+ SafeSQLCloseCursor(statement);
+ SafeSQLFreeHandle(SQL_HANDLE_STMT,statement);
+ } // if device table implemented
+} // TODBCApiAgent::CheckDevice
+
+
+// check credential string
+bool TODBCApiAgent::CheckLogin(const char *aOriginalUserName, const char *aModifiedUserName, const char *aAuthString, TAuthSecretTypes aAuthStringType, const char *aDeviceID)
+{
+ bool authok = false;
+ string nonce;
+ SQLRETURN res;
+ SQLHSTMT statement;
+
+ // - if no userkey query, we cannot proceed and must fail auth now
+ if (fConfigP->fUserKeySQL.empty()) return false;
+ // - get nonce (if we have a device table, we should have read it by now)
+ if (aAuthStringType==sectyp_md5_V10 || aAuthStringType==sectyp_md5_V11)
+ getAuthNonce(aDeviceID,nonce);
+ // - try to obtain an appropriate user key
+ statement=newStatementHandle(getODBCConnectionHandle());
+ try {
+ // get SQL
+ string sql = fConfigP->fUserKeySQL;
+ string uk; // temp user key
+ // substitute: %U = original username as sent by remote,
+ // %dU = username modified for DB search,
+ // %M = authstring, %N = Nonce string
+ StringSubst(sql,"%U",aOriginalUserName,2,-1,fConfigP->fDataCharSet,fConfigP->fDataLineEndMode,fConfigP->fQuotingMode);
+ StringSubst(sql,"%dU",aModifiedUserName,3,-1,fConfigP->fDataCharSet,fConfigP->fDataLineEndMode,fConfigP->fQuotingMode);
+ // %%% probably obsolete
+ StringSubst(sql,"%M",aAuthString,2,-1,fConfigP->fDataCharSet,fConfigP->fDataLineEndMode,fConfigP->fQuotingMode);
+ StringSubst(sql,"%N",nonce,2,fConfigP->fDataCharSet,fConfigP->fDataLineEndMode,fConfigP->fQuotingMode);
+ // now do the standard stuff
+ resetSQLParameterMaps();
+ DoSQLSubstitutions(sql);
+ bindSQLParameters(statement);
+ // issue
+ PDEBUGPUTSX(DBG_ADMIN+DBG_DBAPI,"SQL for getting user key (and password/md5userpass if not checked in query):");
+ PDEBUGPUTSX(DBG_ADMIN+DBG_DBAPI,sql.c_str());
+ TP_DEFIDX(li);
+ TP_SWITCH(li,fTPInfo,TP_database);
+ res = SafeSQLExecDirect(
+ statement,
+ (SQLCHAR *)sql.c_str(),
+ SQL_NTS
+ );
+ TP_START(fTPInfo,li);
+ checkStatementHasData(res,statement);
+ saveAndCleanupSQLParameters(statement);
+ // - fetch result row
+ res=SafeSQLFetch(statement);
+ if (fConfigP->fClearTextPw || fConfigP->fMD5UserPass) {
+ authok=false;
+ // go though all returned rows until match (same username might be used with 2 different pws)
+ while (checkStatementHasData(res,statement)) {
+ // read in ascending index order!!
+ string secret;
+ SQLSMALLINT col=1;
+ // - user key
+ getColumnValueAsString(statement,col,uk,chs_ascii);
+ col++;
+ #ifdef SCRIPT_SUPPORT
+ TItemField *hfP;
+ // also store it in first local context var
+ if (fAgentContext) {
+ hfP=fAgentContext->getLocalVar(0);
+ if (hfP) hfP->setAsString(uk.c_str());
+ }
+ #endif
+ // also store it in fUserKey already for USERKEY() function
+ fUserKey=uk;
+ // - password or md5userpass
+ getColumnValueAsString(statement,col,secret,chs_ascii);
+ // - convert from hex to b64 if needed
+ if (fConfigP->fMD5UPAsHex && secret.size()>=32) {
+ uInt8 md5[16];
+ cAppCharP p=secret.c_str();
+ for (int k=0; k<16; k++) {
+ uInt16 h;
+ if (HexStrToUShort(p+(k*2),h,2)<2) break;
+ md5[k]=(uInt8)h;
+ }
+ p = b64::encode(&md5[0],16);
+ secret = p; // save converted string
+ sysync_free((void *)p);
+ }
+ col++;
+ #ifdef SCRIPT_SUPPORT
+ // also store it in second local context var
+ if (fAgentContext) {
+ hfP=fAgentContext->getLocalVar(1);
+ if (hfP) hfP->setAsString(secret.c_str());
+ // if result set has more columns, save them as well
+ SQLSMALLINT numcols;
+ // - get number of columns
+ res = SafeSQLNumResultCols(statement,&numcols);
+ checkStatementError(res,statement);
+ // - save remaining columns into defined variables
+ while (col<=numcols) {
+ // check if enough variables
+ if (col>fAgentContext->getNumLocals()) break;
+ // get variable
+ hfP=fAgentContext->getLocalVar(col-1);
+ if (!hfP) break;
+ // timestamp is special
+ if (hfP->getType()==fty_timestamp) {
+ // get as timestamp
+ lineartime_t ts;
+ if (!getColumnValueAsTimestamp(statement,col,ts)) break;
+ static_cast<TTimestampField *>(hfP)->setTimestampAndContext(ts,fConfigP->fCurrentDateTimeZone);
+ }
+ else {
+ // get and assign as string
+ string v;
+ if (!getColumnValueAsString(statement,col,v,chs_ascii)) break;
+ hfP->setAsString(v.c_str());
+ }
+ col++;
+ }
+ }
+ #endif
+ // make standard auth check
+ if (fConfigP->fClearTextPw) {
+ // we have the clear text password, check against what was transmitted from remote
+ authok=checkAuthPlain(aOriginalUserName,secret.c_str(),nonce.c_str(),aAuthString,aAuthStringType);
+ }
+ else {
+ // whe have the MD5 of user:password, check against what was transmitted from remote
+ // Note: this can't work with non-V1.1 type creds
+ authok=checkAuthMD5(aOriginalUserName,secret.c_str(),nonce.c_str(),aAuthString,aAuthStringType);
+ }
+ #ifdef SCRIPT_SUPPORT
+ // if there is a script, call it to perform decision
+ // - refresh auth status
+ fStandardAuthOK=authok; // for AUTHOK() func
+ // - call script (if any)
+ fScriptContextDatastore=NULL;
+ authok=TScriptContext::executeTest(
+ authok, // default for no script, no result or script error is current auth
+ fAgentContext,
+ fConfigP->fLoginCheckScript,
+ fConfigP->getAgentFuncTableP(), // context function table
+ (void *)this // context data (myself)
+ );
+ #endif
+ if (authok) break; // found, authorized
+ // fetch next
+ res=SafeSQLFetch(statement);
+ }
+ }
+ else {
+ // AuthString was checked in SQL query
+ // - no record found -> no auth
+ if (res==SQL_NO_DATA) authok=false;
+ }
+ if (authok) {
+ // now assign user key
+ fUserKey=uk;
+ PDEBUGPRINTFX(DBG_ADMIN,("Auth successful, fUserKey='%s'",fUserKey.c_str()));
+ }
+ else {
+ // we do not have a valid user key
+ fUserKey.erase();
+ }
+ }
+ catch (...) {
+ // release the statement handle
+ SafeSQLCloseCursor(statement);
+ SafeSQLFreeHandle(SQL_HANDLE_STMT,statement);
+ SafeSQLEndTran(SQL_HANDLE_DBC,getODBCConnectionHandle(),SQL_ROLLBACK);
+ throw;
+ }
+ // release the statement handle
+ SafeSQLCloseCursor(statement);
+ SafeSQLFreeHandle(SQL_HANDLE_STMT,statement);
+ // return auth status
+ return authok;
+} // TODBCApiAgent::CheckLogin
+
+
+// do something with the analyzed data
+void TODBCApiAgent::remoteAnalyzed(void)
+{
+ SQLRETURN res;
+ SQLHSTMT statement;
+ string sql;
+
+ // call ancestor first
+ inherited::remoteAnalyzed();
+ if (!fConfigP->fSaveInfoSQL.empty()) {
+ // save info string
+ statement=newStatementHandle(getODBCConnectionHandle());
+ try {
+ // get SQL
+ sql = fConfigP->fSaveInfoSQL;
+ // %nR Remote name: [Manufacturer ]Model")
+ StringSubst(sql,"%nR",getRemoteDescName(),3,-1,fConfigP->fDataCharSet,fConfigP->fDataLineEndMode,fConfigP->fQuotingMode);
+ // %vR Remote Device Version Info ("Type (HWV, FWV, SWV) Oem")
+ StringSubst(sql,"%vR",getRemoteInfoString(),3,-1,fConfigP->fDataCharSet,fConfigP->fDataLineEndMode,fConfigP->fQuotingMode);
+ // - standard substitutions: %u=userkey, %d=devicekey
+ resetSQLParameterMaps();
+ DoSQLSubstitutions(sql);
+ bindSQLParameters(statement);
+ // - issue
+ PDEBUGPUTSX(DBG_ADMIN+DBG_DBAPI,"SQL for saving device info");
+ PDEBUGPUTSX(DBG_ADMIN+DBG_DBAPI,sql.c_str());
+ TP_DEFIDX(li);
+ TP_SWITCH(li,fTPInfo,TP_database);
+ res = SafeSQLExecDirect(
+ statement,
+ (SQLCHAR *)sql.c_str(),
+ SQL_NTS
+ );
+ TP_START(fTPInfo,li);
+ checkStatementError(res,statement);
+ saveAndCleanupSQLParameters(statement);
+ // commit new device
+ SafeSQLFreeHandle(SQL_HANDLE_STMT,statement);
+ SafeSQLEndTran(SQL_HANDLE_DBC,getODBCConnectionHandle(),SQL_COMMIT);
+ } // try
+ catch (exception &e) {
+ // release the statement handle
+ SafeSQLFreeHandle(SQL_HANDLE_STMT,statement);
+ SafeSQLEndTran(SQL_HANDLE_DBC,getODBCConnectionHandle(),SQL_ROLLBACK);
+ PDEBUGPRINTFX(DBG_ERROR,("Failed saving device info: %s",e.what()));
+ }
+ }
+} // TODBCApiAgent::remoteAnalyzed
+
+#endif // HAS_SQL_ADMIN
+
+#endif // ODBCAPI_SUPPORT
+
+
+
+#ifndef SYSYNC_CLIENT
+
+void TODBCApiAgent::RequestEnded(bool &aHasData)
+{
+ // first let ancestors finish their stuff
+ inherited::RequestEnded(aHasData);
+ #ifdef ODBCAPI_SUPPORT
+ // now close the ODBC connection if there is one left at the session level
+ // and no script statement is still unfinished
+ #ifdef SCRIPT_SUPPORT
+ if (!fScriptStatement)
+ #endif
+ {
+ closeODBCConnection(fODBCConnectionHandle);
+ }
+ #endif
+} // TODBCApiAgent::RequestEnded
+
+#endif
+
+
+// factory methods of Agent config
+// ===============================
+
+#ifdef SYSYNC_CLIENT
+
+TSyncClient *TOdbcAgentConfig::CreateClientSession(const char *aSessionID)
+{
+ // return appropriate client session
+ MP_RETURN_NEW(TODBCApiAgent,DBG_HOT,"TODBCApiAgent",TODBCApiAgent(static_cast<TSyncClientBase *>(getSyncAppBase()),aSessionID));
+} // TOdbcAgentConfig::CreateClientSession
+
+
+#else
+
+TSyncServer *TOdbcAgentConfig::CreateServerSession(TSyncSessionHandle *aSessionHandle, const char *aSessionID)
+{
+ // return XML2GO or ODBC-server session
+ MP_RETURN_NEW(TODBCApiAgent,DBG_HOT,"TODBCApiAgent",TODBCApiAgent(getSyncAppBase(),aSessionHandle,aSessionID));
+} // TOdbcAgentConfig::CreateServerSession
+
+#endif
+
+
+} // namespace sysync
+
+/* end of TODBCApiAgent implementation */
+
+#endif // SQL_SUPPORT
+
+// eof
diff --git a/src/DB_interfaces/odbc_db/odbcapiagent.h b/src/DB_interfaces/odbc_db/odbcapiagent.h
new file mode 100755
index 0000000..6c77738
--- /dev/null
+++ b/src/DB_interfaces/odbc_db/odbcapiagent.h
@@ -0,0 +1,603 @@
+/**
+ * @File odbcapiagent.h
+ *
+ * @Author Lukas Zeller (luz@synthesis.ch)
+ *
+ * @brief TODBCApiAgent
+ * ODBC based agent (client or server session) implementation
+ *
+ * Copyright (c) 2001-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ * @Date 2005-10-06 : luz : created from odbcdbagent
+ */
+
+#ifdef SQL_SUPPORT
+
+#ifndef ODBCAPIAGENT_H
+#define ODBCAPIAGENT_H
+
+// includes
+
+#ifdef SQLITE_SUPPORT
+#include "sqlite3.h"
+#endif
+
+#include "odbcdb.h"
+
+#include "customimplagent.h"
+#include "customimplds.h"
+#include "multifielditem.h"
+#include "scriptcontext.h"
+
+
+using namespace sysync;
+
+namespace sysync {
+
+// Support for SySync Diagnostic Tool
+#ifdef SYSYNC_TOOL
+// - execute SQL command
+int execSQL(int argc, const char *argv[]);
+#endif // SYSYNC_TOOL
+
+
+#ifdef SCRIPT_SUPPORT
+// publish as derivates might need it
+extern const TFuncTable ODBCAgentFuncTable;
+// publish as ODBCDBDatastore uses this (chains back to Datastore level funcs)
+extern const TFuncTable ODBCDSFuncTable1;
+#endif
+
+// supported transaction isolation levels
+typedef enum {
+ // Note: these MUST be in the same order as
+ // Win32/ODBC defined SQL_TXN_READ_UNCOMMITTED..SQL_TXN_SERIALIZABLE
+ // bits appear in the transaction bitmasks.
+ txni_uncommitted, // read uncommitted (dirty reads possible)
+ txni_committed, // read committed only
+ txni_repeatable, // repeatable
+ txni_serializable, // no dirty reads, repeatable, no phantoms
+ // special values
+ txni_none, // no transactions
+ txni_default // default of data source
+} TTxnIsolModes;
+const sInt16 numTxnIsolModes = txni_default-txni_uncommitted+1;
+
+
+// standard size for parameter buffers if not specified
+const size_t std_paramsize = 512;
+
+// parameter value type
+typedef enum {
+ param_field,
+ param_localid_str,
+ param_localid_int,
+ param_remoteid_str,
+ param_remoteid_int,
+ param_buffer
+} TParamMode;
+
+// parameter mapping entry
+typedef struct {
+ // parameter specification
+ bool inparam;
+ bool outparam;
+ TParamMode parammode;
+ // value source/target
+ TDBFieldType dbFieldType;
+ // field info for param_field
+ TItemField *fieldP;
+ size_t maxSize;
+ TMultiFieldItem *itemP; // can be null if no item available
+ // size info for param_buffer
+ uInt32 *outSiz;
+ // Parameter value buffer
+ bool mybuffer;
+ #ifdef ODBCAPI_SUPPORT
+ SQLPOINTER ParameterValuePtr;
+ SQLINTEGER BufferLength;
+ SQLINTEGER StrLen_or_Ind;
+ #elif defined(SQLITE_SUPPORT)
+ appPointer ParameterValuePtr;
+ memSize BufferLength;
+ memSize StrLen_or_Ind;
+ #endif
+} TParameterMap;
+
+// parameter mapping list
+typedef std::list<TParameterMap> TParameterMapList;
+
+#ifdef WIN32
+ // windows, has SEH, leave it on (unless set by target options etc.)
+#else
+ // non-windows, no SEH anyway
+ #define NO_AV_GUARDING
+#endif
+
+#ifdef ODBCAPI_SUPPORT
+
+#if defined(NO_AV_GUARDING) // || __option(microsoft_exceptions)
+
+// SEH is on (or we can't catch AV at all), we don't need extra crash-guarding
+#define SafeSQLAllocHandle SQLAllocHandle
+#define SafeSQLFreeHandle SQLFreeHandle
+#define SafeSQLSetEnvAttr SQLSetEnvAttr
+#define SafeSQLSetConnectAttr SQLSetConnectAttr
+#define SafeSQLConnect SQLConnect
+#define SafeSQLDriverConnect SQLDriverConnect
+#define SafeSQLGetInfo SQLGetInfo
+#define SafeSQLEndTran SQLEndTran
+#define SafeSQLExecDirect SQLExecDirect
+#define SafeSQLFetch SQLFetch
+#define SafeSQLNumResultCols SQLNumResultCols
+#define SafeSQLGetData SQLGetData
+#define SafeSQLCloseCursor SQLCloseCursor
+
+#ifdef ODBC_UNICODE
+#define SafeSQLExecDirectW SQLExecDirectW
+#endif
+
+#else
+
+// special exception for SEH
+class TODBCSEHexception : public TSyncException
+{
+ typedef TSyncException inherited;
+public:
+ TODBCSEHexception(uInt32 aCode);
+}; // TODBCSEHexception
+
+
+// prototypes
+
+SQLRETURN SafeSQLAllocHandle(
+ SQLSMALLINT HandleType,
+ SQLHANDLE InputHandle,
+ SQLHANDLE * OutputHandlePtr);
+
+SQLRETURN SafeSQLFreeHandle(
+ SQLSMALLINT HandleType,
+ SQLHANDLE Handle);
+
+SQLRETURN SafeSQLSetEnvAttr(
+ SQLHENV EnvironmentHandle,
+ SQLINTEGER Attribute,
+ SQLPOINTER ValuePtr,
+ SQLINTEGER StringLength);
+
+SQLRETURN SafeSQLSetConnectAttr(
+ SQLHDBC ConnectionHandle,
+ SQLINTEGER Attribute,
+ SQLPOINTER ValuePtr,
+ SQLINTEGER StringLength);
+
+SQLRETURN SafeSQLConnect(
+ SQLHDBC ConnectionHandle,
+ SQLCHAR * ServerName,
+ SQLSMALLINT NameLength1,
+ SQLCHAR * UserName,
+ SQLSMALLINT NameLength2,
+ SQLCHAR * Authentication,
+ SQLSMALLINT NameLength3);
+
+SQLRETURN SafeSQLDriverConnect(
+ SQLHDBC ConnectionHandle,
+ SQLHWND WindowHandle,
+ SQLCHAR * InConnectionString,
+ SQLSMALLINT StringLength1,
+ SQLCHAR * OutConnectionString,
+ SQLSMALLINT BufferLength,
+ SQLSMALLINT * StringLength2Ptr,
+ SQLUSMALLINT DriverCompletion);
+
+SQLRETURN SafeSQLGetInfo(
+ SQLHDBC ConnectionHandle,
+ SQLUSMALLINT InfoType,
+ SQLPOINTER InfoValuePtr,
+ SQLSMALLINT BufferLength,
+ SQLSMALLINT * StringLengthPtr);
+
+SQLRETURN SafeSQLEndTran(
+ SQLSMALLINT HandleType,
+ SQLHANDLE Handle,
+ SQLSMALLINT CompletionType);
+
+SQLRETURN SafeSQLExecDirect(
+ SQLHSTMT StatementHandle,
+ SQLCHAR * StatementText,
+ SQLINTEGER TextLength);
+
+#ifdef ODBC_UNICODE
+SQLRETURN SafeSQLExecDirectW(
+ SQLHSTMT StatementHandle,
+ SQLWCHAR * StatementText,
+ SQLINTEGER TextLength);
+#endif
+
+SQLRETURN SafeSQLFetch(
+ SQLHSTMT StatementHandle);
+
+SQLRETURN SafeSQLNumResultCols(
+ SQLHSTMT StatementHandle,
+ SQLSMALLINT * ColumnCountPtr);
+
+SQLRETURN SafeSQLGetData(
+ SQLHSTMT StatementHandle,
+ SQLUSMALLINT ColumnNumber,
+ SQLSMALLINT TargetType,
+ SQLPOINTER TargetValuePtr,
+ SQLINTEGER BufferLength,
+ SQLINTEGER * StrLen_or_IndPtr);
+
+SQLRETURN SafeSQLCloseCursor(
+ SQLHSTMT StatementHandle);
+
+#endif
+
+#endif // ODBCAPI_SUPPORT
+
+// ODBC Utils
+
+// - return quoted version of string if aDoQuote is set
+const char *quoteString(string &aIn, string &aOut, TQuotingModes aQuoteMode);
+const char *quoteString(const char *aIn, string &aOut, TQuotingModes aQuoteMode);
+void quoteStringAppend(string &aIn, string &aOut, TQuotingModes aQuoteMode);
+void quoteStringAppend(const char *aIn, string &aOut, TQuotingModes aQuoteMode);
+
+
+// forward
+class TODBCDSConfig;
+#ifdef SCRIPT_SUPPORT
+class TScriptContext;
+#endif
+
+
+class TOdbcAgentConfig : public TCustomAgentConfig
+{
+ typedef TCustomAgentConfig inherited;
+public:
+ TOdbcAgentConfig(TConfigElement *aParentElement);
+ virtual ~TOdbcAgentConfig();
+ // properties
+ #ifdef ODBCAPI_SUPPORT
+ // - ODBC datasource
+ string fDataSource;
+ #ifdef SCRIPT_SUPPORT
+ // - scripts
+ string fAfterConnectScript; // called after opening an ODBC connection
+ #endif
+ // - Server username / password
+ string fUsername;
+ string fPassword;
+ // - connection string
+ string fDBConnStr;
+ // - preventing use of SQLSetConnectAttr (for drivers that crash when using it)
+ bool fNoConnectAttrs;
+ // - timeout
+ sInt32 fODBCTimeout;
+ // - transaction isolation mode
+ sInt16 fODBCTxnMode;
+ // - cursor library usage
+ bool fUseCursorLib;
+ // - statements to maintain device table
+ string fGetDeviceSQL; // if set, fGetDeviceSQL must return device key and last saved nonce
+ string fNewDeviceSQL; // create new device entry
+ string fSaveNonceSQL; // save nonce into existing device entry
+ string fSaveInfoSQL; // save extra device info
+ string fSaveDevInfSQL; // save device information (such as name) into existing device entry
+ string fLoadDevInfSQL; // load cached device information from DB
+ // - statement to obtain user key (and password if fClearTextPw) from Database
+ // replacements: %U=username, %M=user:pw: MD5
+ string fUserKeySQL;
+ bool fClearTextPw; // if set, fUserKeySQL must return clear text PW in second column.
+ bool fMD5UserPass; // if set, fUserKeySQL must return MD5B64("user:password") or HEX(MD5("user:password"))in second column.
+ bool fMD5UPAsHex; // if set, format expected from DB is a hex string (not B64)
+ // - statement to obtain database server's date and time (if none, local time is used)
+ string fGetCurrentDateTimeSQL;
+ // - statement to save log info
+ string fWriteLogSQL;
+ #endif // ODBCAPI_SUPPORT
+ // - Literal string quoting mode
+ TQuotingModes fQuotingMode;
+ #ifdef SCRIPT_SUPPORT
+ // provided to allow derivates to add API specific script functions to scripts called from customagent
+ virtual const TFuncTable *getAgentFuncTableP(void) { return &ODBCAgentFuncTable; };
+ #endif
+protected:
+ // check config elements
+ virtual bool localStartElement(const char *aElementName, const char **aAttributes, sInt32 aLine);
+ virtual void clear();
+ virtual void localResolve(bool aLastPass);
+ #ifdef SCRIPT_SUPPORT
+ virtual void ResolveAPIScripts(void);
+ #endif
+ #ifdef SYSYNC_CLIENT
+ // create appropriate session (=agent) for this client
+ virtual TSyncClient *CreateClientSession(const char *aSessionID);
+ #else
+public:
+ // create appropriate session (=agent) for this server
+ virtual TSyncServer *CreateServerSession(TSyncSessionHandle *aSessionHandle, const char *aSessionID);
+ #endif
+}; // TOdbcAgentConfig
+
+
+class TODBCApiDS;
+
+class TODBCApiAgent: public TCustomImplAgent
+{
+ friend class TODBCCommonFuncs;
+ friend class TODBCAgentFuncs;
+ typedef TCustomImplAgent inherited;
+public:
+ #ifdef SYSYNC_CLIENT
+ TODBCApiAgent(TSyncClientBase *aSyncClientBaseP, const char *aSessionID);
+ #else
+ TODBCApiAgent(TSyncAppBase *aAppBaseP, TSyncSessionHandle *aSessionHandleP, const char *aSessionID);
+ #endif
+ virtual ~TODBCApiAgent();
+ virtual void TerminateSession(void); // Terminate session, like destructor, but without actually destructing object itself
+ virtual void ResetSession(void); // Resets session (but unlike TerminateSession, session might be re-used)
+ void InternalResetSession(void); // static implementation for calling through virtual destructor and virtual ResetSession();
+ #ifdef HAS_SQL_ADMIN
+ // Note: %%% for now, login is supported by ODBC only
+ // user authentication
+ #ifndef SYSYNC_CLIENT
+ // - return auth type to be requested from remote
+ virtual TAuthTypes requestedAuthType(void); // avoids MD5 when it cannot be checked
+ // - get next nonce string top be sent to remote party for subsequent MD5 auth
+ virtual void getNextNonce(const char *aDeviceID, string &aNextNonce);
+ // - get nonce string, which is expected to be used by remote party for MD5 auth.
+ virtual void getAuthNonce(const char *aDeviceID, string &aAuthNonce);
+ #endif // SYSYNC_CLIENT
+ // - clean up after all login activity is over (including finishscript)
+ virtual void LoginCleanUp(void);
+ #endif // HAS_SQL_ADMIN
+ #ifdef ODBCAPI_SUPPORT
+ // current date & time
+ virtual lineartime_t getDatabaseNowAs(timecontext_t aTimecontext);
+ #endif
+ // SQL generic
+ // - do substitutions (%x-type) in a SQL string
+ void DoSQLSubstitutions(string &aSQL);
+ // Parameter handling
+ // - reset all mapped parameters
+ void resetSQLParameterMaps(void);
+ // - add parameter definition to the session level parameter list
+ void addSQLParameterMap(
+ bool aInParam,
+ bool aOutParam,
+ TParamMode aParamMode,
+ TItemField *aFieldP,
+ TDBFieldType aDbFieldType
+ );
+ #ifdef ODBCAPI_SUPPORT
+ // ODBC
+ SQLHENV getODBCEnvironmentHandle(void);
+ // - get handle to open connection (open it if not already open)
+ SQLHDBC getODBCConnectionHandle(void);
+ // - pull (=take ownership of) connection handle out of session into another object (datastore)
+ SQLHDBC pullODBCConnectionHandle(void);
+ // - check for connection-level error
+ void checkConnectionError(SQLRETURN aResult);
+ #endif // ODBCAPI_SUPPORT
+ // SQL based agent config
+ TOdbcAgentConfig *fConfigP;
+ // %%% Script statement is always defined, as we need it as dummy even in SQLite only case for now
+ #ifdef SCRIPT_SUPPORT
+ HSTMT fScriptStatement;
+ #endif
+ // SQL generic utils
+ // - make ODBC string from field
+ bool appendFieldValueLiteral(
+ TItemField &aField,TDBFieldType aDBFieldType, uInt32 aMaxSize, string &aSQL,
+ TCharSets aDataCharSet, TLineEndModes aDataLineEndMode, TQuotingModes aQuotingMode,
+ timecontext_t aTimeContext,
+ sInt32 &aRecordSize
+ );
+ // - make ODBC string literal from UTF8 string
+ void stringToODBCLiteralAppend(
+ cAppCharP aText,
+ string &aLiteral,
+ TCharSets aCharSet,
+ TLineEndModes aLineEndMode,
+ TQuotingModes aQuotingMode,
+ size_t aMaxBytes=0
+ );
+ // - make ODBC date/time literals from lineartime_t
+ void lineartimeToODBCLiteralAppend(
+ lineartime_t aTimestamp, string &aString,
+ bool aWithDate, bool aWithTime,
+ timecontext_t aTsContext=TCTX_UNKNOWN, timecontext_t aDBContext=TCTX_UNKNOWN
+ );
+ // - make integer-based literals from lineartime_t
+ void lineartimeToIntLiteralAppend(
+ lineartime_t aTimestamp, string &aString,
+ TDBFieldType aDbfty,
+ timecontext_t aTsContext=TCTX_UNKNOWN, timecontext_t aDBContext=TCTX_UNKNOWN
+ );
+ /* obsolete
+ // - make ODBC date/time literal from timestamp
+ void timeStampToODBCLiteralAppend(lineartime_t aTimeStamp, string &aString,
+ bool aAsUTC, bool aWithDate, bool aWithTime);
+ // - make ODBC date/time literal from struct tm
+ void tmToODBCLiteralAppend(const struct tm &tim, string &aString,
+ bool aWithDate, bool aWithTime);
+ */
+ #ifdef ODBCAPI_SUPPORT
+ // ODBC utils
+ // - commit and close eventually open script statement
+ void commitAndCloseScriptStatement(void);
+ #ifdef SCRIPT_SUPPORT
+ // - get statement for executing scripted SQL
+ HSTMT getScriptStatement(void);
+ #endif
+ // - close connection
+ void closeODBCConnection(SQLHDBC &aConnHandle);
+ // - check if aResult signals error and throw exception if so
+ void checkODBCError(SQLRETURN aResult,SQLSMALLINT aHandleType,SQLHANDLE aHandle);
+ // - check if aResult signals error and throw exception if so
+ void checkStatementError(SQLRETURN aResult,SQLHSTMT aHandle);
+ // - check if aResult is NO_DATA, return False if so, throw exception on error
+ bool checkStatementHasData(SQLRETURN aResult,SQLHSTMT aHandle);
+ // - get ODBC error message for given result code, returns false if no error
+ bool getODBCError(SQLRETURN aResult, string &aMessage, string &aSQLState, SQLSMALLINT aHandleType, SQLHANDLE aHandle);
+ // - get new statement handle
+ SQLHSTMT newStatementHandle(SQLHDBC aConnection);
+ // - get column value as database field
+ bool getColumnValueAsField(
+ SQLHSTMT aStatement, sInt16 aColIndex, TDBFieldType aDbfty, TItemField *aFieldP,
+ TCharSets aDataCharSet, timecontext_t aTimecontext, bool aMoveToUserContext
+ );
+ // - get integer value, returns false if no Data or Null
+ bool getColumnValueAsULong(
+ SQLHSTMT aStatement,
+ sInt16 aColNumber,
+ uInt32 &aLongValue
+ );
+ bool getColumnValueAsLong(
+ SQLHSTMT aStatement,
+ sInt16 aColNumber,
+ sInt32 &aLongValue
+ );
+ // - get double value, returns false if no Data or Null
+ bool getColumnValueAsDouble(
+ SQLHSTMT aStatement,
+ sInt16 aColNumber,
+ double &aDoubleValue
+ );
+ // - get string value, returns false if no Data or Null
+ bool getColumnValueAsString(
+ SQLHSTMT aStatement,
+ sInt16 aColNumber,
+ string &aStringValue,
+ TCharSets aCharSet,
+ bool aAsBlob=false
+ );
+ // - get time/date values, returns false if no Data or Null
+ bool getColumnAsODBCTimestamp(
+ SQLHSTMT aStatement,
+ sInt16 aColNumber,
+ SQL_TIMESTAMP_STRUCT &aODBCTimestamp
+ );
+ bool getColumnValueAsTimestamp(
+ SQLHSTMT aStatement,
+ sInt16 aColNumber,
+ lineartime_t &aTimestamp
+ );
+ bool getColumnValueAsTime(
+ SQLHSTMT aStatement,
+ sInt16 aColNumber,
+ lineartime_t &aTime
+ );
+ bool getColumnValueAsDate(
+ SQLHSTMT aStatement,
+ sInt16 aColNumber,
+ lineartime_t &aDate
+ );
+ // - bind parameters (and values for IN-Params) to the statement
+ void bindSQLParameters(
+ TSyncSession *aSessionP,
+ SQLHSTMT aStatement,
+ TParameterMapList &aParamMapList, // the list of mapped parameters
+ TCharSets aDataCharSet,
+ TLineEndModes aDataLineEndMode
+ );
+ // - save out parameter values and clean up
+ void saveAndCleanupSQLParameters(
+ TSyncSession *aSessionP,
+ SQLHSTMT aStatement,
+ TParameterMapList &aParamMapList, // the list of mapped parameters
+ TCharSets aDataCharSet,
+ TLineEndModes aDataLineEndMode
+ );
+ // - bind parameters (and values for IN-Params) to the statement for session level SQL
+ void bindSQLParameters(
+ SQLHSTMT aStatement
+ );
+ // - save out parameter values and clean up for session level SQL
+ void saveAndCleanupSQLParameters(
+ SQLHSTMT aStatement
+ );
+ #endif // ODBCAPI_SUPPORT
+ // SQLite utils
+ #ifdef SQLITE_SUPPORT
+ // - get SQLite error message for given result code; returns false if no error
+ bool getSQLiteError(int aRc,string &aMessage, sqlite3 *aDb);
+ // - check if aResult signals error and throw exception if so
+ void checkSQLiteError(int aRc, sqlite3 *aDb);
+ // - check if aRc signals error and throw exception if so; returns true if data available, false if not
+ bool checkSQLiteHasData(int aRc, sqlite3 *aDb);
+ // - get column value into field
+ bool getSQLiteColValueAsField(
+ sqlite3_stmt *aStatement, sInt16 aColIndex, TDBFieldType aDbfty, TItemField *aFieldP,
+ TCharSets aDataCharSet, timecontext_t aTimecontext, bool aMoveToUserContext
+ );
+ // - prepare SQLite statement
+ void prepareSQLiteStatement(
+ cAppCharP aSQL,
+ sqlite3 *aDB,
+ sqlite3_stmt *&aStatement
+ );
+ // - bind SQLite parameters (we have ONLY in-params)!
+ void bindSQLiteParameters(
+ TSyncSession *aSessionP,
+ sqlite3_stmt *aStatement,
+ TParameterMapList &aParamMapList, // the list of mapped parameters
+ TCharSets aDataCharSet,
+ TLineEndModes aDataLineEndMode
+ );
+ #endif // SQLITE_SUPPORT
+ // parameter handling routines
+ // - reset all mapped parameters
+ void resetSQLParameterMaps(TParameterMapList &aParamMapList);
+ // - parsing of %p(fieldname,mode[,dbfieldtype]) sequence in SQL statements
+ bool ParseParamSubst(
+ string &aSQL, // string to parse
+ string::size_type &i, // position where % sequence starts in aSQL
+ string::size_type &n, // input=number of chars of % sequence without parameters, if result==true: output=number of chars to substitute at i in aSQL
+ TParameterMapList &aParameterMaps, // parameter maps list to add params to
+ TMultiFieldItem *aItemP // the involved item for field params
+ #ifdef SCRIPT_SUPPORT
+ ,TScriptContext *aScriptContextP // the script context for variable params
+ #endif
+ );
+protected:
+ #ifdef HAS_SQL_ADMIN
+ // - check device ID related stuff
+ virtual void CheckDevice(const char *aDeviceID);
+ // - check user/pw (part of SessionLogin process)
+ virtual bool CheckLogin(const char *aOriginalUserName, const char *aModifiedUserName, const char *aAuthString, TAuthSecretTypes aAuthStringType, const char *aDeviceID);
+ // - remote device is analyzed, eventually save status
+ virtual void remoteAnalyzed(void);
+ #endif // HAS_SQL_ADMIN
+ #ifndef SYSYNC_CLIENT
+ // - request end, used to clean up
+ virtual void RequestEnded(bool &aHasData);
+ #endif
+private:
+ #ifdef ODBCAPI_SUPPORT
+ // ODBC vars
+ // - session-specific connection string
+ string fSessionDBConnStr;
+ string fSessionDBPassword;
+ // - handles
+ SQLHDBC fODBCConnectionHandle;
+ SQLHENV fODBCEnvironmentHandle;
+ #ifdef SCRIPT_SUPPORT
+ // private context for afterconnect script as it might be called nested in other scripts (e.g. triggered by SQLEXECUTE)
+ TScriptContext *fAfterConnectContext;
+ #endif // SCRIPT_SUPPORT
+ #endif // ODBCAPI_SUPPORT
+ // parameter list for execution of commands
+ TParameterMapList fParameterMaps;
+}; // TODBCApiAgent
+
+
+} // namespace sysync
+
+#endif // ODBCAPIAGENT_H
+
+#endif // SQL_SUPPORT
+
+// eof
diff --git a/src/DB_interfaces/odbc_db/odbcapids.cpp b/src/DB_interfaces/odbc_db/odbcapids.cpp
new file mode 100755
index 0000000..e4f16fa
--- /dev/null
+++ b/src/DB_interfaces/odbc_db/odbcapids.cpp
@@ -0,0 +1,4455 @@
+/**
+ * @File odbcapids.cpp
+ *
+ * @Author Lukas Zeller (luz@synthesis.ch)
+ *
+ * @brief TODBCApiDS
+ * ODBC based datastore API implementation
+ * @Note Currently also contains what will later become TCustomImplDS
+ *
+ * Copyright (c) 2001-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ * @Date 2005-09-16 : luz : created from odbcdbdatastore
+ */
+
+#include "prefix_file.h"
+
+#ifdef SQL_SUPPORT
+
+// includes
+#include "sysync.h"
+#include "odbcapids.h"
+#include "odbcapiagent.h"
+
+
+// sanity check for current implementation - either SQLite or ODBC must be enabled
+#if !defined(ODBCAPI_SUPPORT) && !defined(SQLITE_SUPPORT)
+ #error "ODBC or SQLite API must be enabled"
+#endif
+
+
+namespace sysync {
+
+// special ID mode names
+const char * const SpecialIDModeNames[numSpecialIDModes] = {
+ "none",
+ "unixmsrnd6"
+};
+
+
+
+#ifdef SCRIPT_SUPPORT
+
+class TODBCDSfuncs {
+public:
+
+ // ODBC datastore specific script functions
+ // ========================================
+
+ // SETSQLFILTER(string filter)
+ static void func_SetSQLFilter(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ aFuncContextP->getLocalVar(0)->getAsString(
+ static_cast<TODBCApiDS *>(aFuncContextP->getCallerContext())->fSQLFilter
+ );
+ }; // func_SetSQLFilter
+
+
+ // ADDBYUPDATING(string idtoupdate)
+ static void func_AddByUpdating(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ TODBCApiDS *dsP = static_cast<TODBCApiDS *>(aFuncContextP->getCallerContext());
+ // only if inserting, convert to updating an existing record
+ if (dsP->fInserting) {
+ string localid;
+ aFuncContextP->getLocalVar(0)->getAsString(localid);
+ aFuncContextP->fParentContextP->fTargetItemP->setLocalID(localid.c_str());
+ // switch off inserting from here on, such that UPDATE SQL will be used
+ dsP->fInserting = false;
+ }
+ }; // func_AddByUpdating
+
+
+ #ifdef SQLITE_SUPPORT
+ // integer SQLITELASTID()
+ // get ROWID created by last insert
+ static void func_SQLGetLastID(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ TODBCApiDS *dsP = static_cast<TODBCApiDS *>(aFuncContextP->getCallerContext());
+ if (dsP->fUseSQLite && dsP->fSQLiteP) {
+ aTermP->setAsInteger(
+ sqlite3_last_insert_rowid(dsP->fSQLiteP)
+ );
+ }
+ else {
+ aTermP->unAssign();
+ }
+ }; // func_SQLGetLastID
+ #endif // SQLITE_SUPPORT
+
+}; // TODBCDSfuncs
+
+
+const uInt8 param_OneStr[] = { VAL(fty_string) };
+
+// builtin function table for datastore level
+const TBuiltInFuncDef ODBCDSFuncDefs[] = {
+ { "SETSQLFILTER", TODBCDSfuncs::func_SetSQLFilter, fty_none, 1, param_OneStr },
+ { "ADDBYUPDATING", TODBCDSfuncs::func_AddByUpdating, fty_none, 1, param_OneStr },
+ #ifdef SQLITE_SUPPORT
+ { "SQLITELASTID", TODBCDSfuncs::func_SQLGetLastID, fty_integer, 0, NULL },
+ #endif
+};
+
+
+// chain to generic local datastore funcs
+static void *ODBCDSChainFunc2(void *&aCtx)
+{
+ // context pointer for datastore-level funcs is the datastore
+ // -> no change needed
+ // next table is customimplds's (and then the localdatastore's from there)
+ return (void *)&CustomDSFuncTable2;
+} // ODBCDSChainFunc
+
+const TFuncTable ODBCDSFuncTable2 = {
+ sizeof(ODBCDSFuncDefs) / sizeof(TBuiltInFuncDef), // size of table
+ ODBCDSFuncDefs, // table pointer
+ ODBCDSChainFunc2 // chain generic DB functions
+};
+
+
+
+#endif // SCRIPT_SUPPORT
+
+
+
+// Config
+// ======
+
+TOdbcDSConfig::TOdbcDSConfig(const char* aName, TConfigElement *aParentElement) :
+ inherited(aName,aParentElement)
+{
+ // nop so far
+ clear();
+ // except ensure we have a decent rand()
+ srand((uInt32)time(NULL));
+} // TOdbcDSConfig::TOdbcDSConfig
+
+
+TOdbcDSConfig::~TOdbcDSConfig()
+{
+ // nop so far
+} // TOdbcDSConfig::~TOdbcDSConfig
+
+
+// init defaults
+void TOdbcDSConfig::clear(void)
+{
+ // init defaults
+ // - commit modes
+ fCommitItems=false; // commit item changes all at end of write by default
+ // - retries
+ fInsertRetries=0; // none
+ fUpdateRetries=0; // none
+ // - folder key
+ #ifdef HAS_SQL_ADMIN
+ fFolderKeySQL.erase();
+ // - Sync target table
+ fGetSyncTargetSQL.erase();
+ fNewSyncTargetSQL.erase();
+ fUpdateSyncTargetSQL.erase();
+ fDeleteSyncTargetSQL.erase();
+ fSyncTimestamp=true;
+ #ifdef OLD_1_0_5_CONFIG_COMPATIBLE
+ // Compatibility with old config files
+ // - layout of map table
+ fMapTableName.erase();
+ fRemoteIDMapField.erase();
+ fLocalIDMapField.erase();
+ fStringLocalID=true;
+ fTargetKeyMapField.erase();
+ fStringTargetkey=true;
+ // - layout of data table
+ fLocalTableName.erase();
+ // - name of folderkey subselection field in data table, empty if none
+ fFolderKeyField.erase();
+ fStringFolderkey=true;
+ // - name of field which contains Local ID (GUID)
+ fLocalIDField.erase();
+ // - mod date/time
+ fModifiedDateField.erase();
+ fModifiedTimeField.erase();
+ #endif // OLD_1_0_5_CONFIG_COMPATIBLE
+ // Map table access, new version
+ // - SQL to fetch all map entries of a target
+ fMapFetchAllSQL.erase();
+ // - SQL to insert new map entry by localid
+ fMapInsertSQL.erase();
+ // - SQL to update existing map entry by localid
+ fMapUpdateSQL.erase();
+ // - SQL to delete a map entry by localid
+ fMapDeleteSQL.erase();
+ #endif // HAS_SQL_ADMIN
+ // Data table access, new version
+ // - SQL to fetch local ID and timestamp from data table
+ fLocalIDAndTimestampFetchSQL.erase();
+ fModifiedTimestamp=true;
+ // - SQL to fetch actual data fields from data table. SQL result must
+ // contain mapped fields in the same order as they appear in <fieldmap>
+ fDataFetchSQL.erase();
+ // - SQL to insert new data in a record, including modification date
+ fDataInsertSQL.erase();
+ // - SQL to update actual data in a record, including modification date
+ fDataUpdateSQL.erase();
+ // - SQL to delete a record
+ fDataDeleteSQL.erase();
+ // - SQL statement(s) to zap entire sync set
+ fDataZapSQL.erase();
+ // Data table options
+ // - try to translate filters to SQL if possible
+ fFilterOnDBLevel=false; // don't try
+ // - quoting mode for string literals
+ fQuotingMode=qm_duplsingle; // default to what was hard-coded before it became configurable in 2.1.1.5
+ // - Obtaining ID for new records
+ fDetermineNewIDOnce=false;
+ fObtainNewIDAfterInsert=false;
+ fObtainNewLocalIDSql.erase();
+ #ifdef SCRIPT_SUPPORT
+ fLocalIDScript.erase();
+ #endif
+ fMinNextID=1000000;
+ fSpecialIDMode=sidm_none;
+ fInsertReturnsID=false;
+ fInsertIDAsOutParam=false;
+ fIgnoreAffectedCount=false;
+ fLastModDBFieldType=dbft_timestamp; // default to what we always had before 3.1 engine for ODBC
+ // SQLite support
+ #ifdef SQLITE_SUPPORT
+ fSQLiteFileName.erase();
+ fSQLiteBusyTimeout=15; // 15 secs
+ #endif
+ // clear inherited
+ inherited::clear();
+} // TOdbcDSConfig::clear
+
+
+// config element parsing
+bool TOdbcDSConfig::localStartElement(const char *aElementName, const char **aAttributes, sInt32 aLine)
+{
+ // commit modes
+ if (strucmp(aElementName,"commititems")==0)
+ expectBool(fCommitItems);
+ // retries
+ else if (strucmp(aElementName,"insertretries")==0)
+ expectInt16(fInsertRetries);
+ else if (strucmp(aElementName,"updateretries")==0)
+ expectInt16(fUpdateRetries);
+ #ifdef HAS_SQL_ADMIN
+ // - folder key
+ else if (strucmp(aElementName,"folderkeysql")==0)
+ expectString(fFolderKeySQL);
+ // - Sync target table handling
+ else if (strucmp(aElementName,"synctargetgetsql")==0)
+ expectString(fGetSyncTargetSQL);
+ else if (strucmp(aElementName,"synctargetnewsql")==0)
+ expectString(fNewSyncTargetSQL);
+ else if (strucmp(aElementName,"synctargetupdatesql")==0)
+ expectString(fUpdateSyncTargetSQL);
+ else if (strucmp(aElementName,"synctargetdeletesql")==0)
+ expectString(fDeleteSyncTargetSQL);
+ else if (strucmp(aElementName,"synctimestamp")==0)
+ expectBool(fSyncTimestamp);
+ #ifdef OLD_1_0_5_CONFIG_COMPATIBLE
+ // Compatibility with old config files
+ // - layout of map table
+ else if (strucmp(aElementName,"maptablename")==0)
+ expectString(fMapTableName);
+ else if (strucmp(aElementName,"remoteidmapfield")==0)
+ expectString(fRemoteIDMapField);
+ else if (strucmp(aElementName,"localidmapfield")==0)
+ expectString(fLocalIDMapField);
+ else if (strucmp(aElementName,"stringlocalid")==0)
+ expectBool(fStringLocalID);
+ else if (strucmp(aElementName,"targetkeymapfield")==0)
+ expectString(fTargetKeyMapField);
+ else if (strucmp(aElementName,"stringtargetkey")==0)
+ expectBool(fStringTargetkey);
+ // - layout of data table
+ else if (strucmp(aElementName,"datatablename")==0) {
+ ReportError(false,"Warning: old-style config - use full SQL statements with %%xx replacement sequences instead");
+ expectString(fLocalTableName);
+ }
+ else if (strucmp(aElementName,"folderkeyfield")==0)
+ expectString(fFolderKeyField);
+ else if (strucmp(aElementName,"stringfolderkey")==0)
+ expectBool(fStringFolderkey);
+ else if (strucmp(aElementName,"localidfield")==0)
+ expectString(fLocalIDField);
+ else if (strucmp(aElementName,"moddatefield")==0)
+ expectString(fModifiedDateField);
+ else if (strucmp(aElementName,"modtimefield")==0)
+ expectString(fModifiedTimeField);
+ // Note: was never documented so will be phased out right now
+ // else if (strucmp(aElementName,"genselectcond")==0)
+ // expectString(fGeneralSelectCondition);
+ #endif // OLD_1_0_5_CONFIG_COMPATIBLE
+ // Map table access, new version
+ else if (strucmp(aElementName,"selectmapallsql")==0)
+ expectString(fMapFetchAllSQL);
+ else if (strucmp(aElementName,"insertmapsql")==0)
+ expectString(fMapInsertSQL);
+ else if (strucmp(aElementName,"updatemapsql")==0)
+ expectString(fMapUpdateSQL);
+ else if (strucmp(aElementName,"deletemapsql")==0)
+ expectString(fMapDeleteSQL);
+ #endif // HAS_SQL_ADMIN
+ // Data table access, new version
+ else if (strucmp(aElementName,"quotingmode")==0)
+ expectEnum(sizeof(fQuotingMode),&fQuotingMode,quotingModeNames,numQuotingModes);
+ else if (strucmp(aElementName,"dbcanfilter")==0)
+ expectBool(fFilterOnDBLevel);
+ else if (strucmp(aElementName,"selectidandmodifiedsql")==0)
+ expectString(fLocalIDAndTimestampFetchSQL);
+ else if (strucmp(aElementName,"modtimestamp")==0)
+ expectBool(fModifiedTimestamp);
+ else if (strucmp(aElementName,"selectdatasql")==0)
+ expectString(fDataFetchSQL);
+ else if (strucmp(aElementName,"insertdatasql")==0)
+ expectString(fDataInsertSQL);
+ else if (strucmp(aElementName,"updatedatasql")==0)
+ expectString(fDataUpdateSQL);
+ else if (strucmp(aElementName,"deletedatasql")==0)
+ expectString(fDataDeleteSQL);
+ else if (strucmp(aElementName,"zapdatasql")==0)
+ expectString(fDataZapSQL);
+ // - Obtaining ID for new records
+ else if (strucmp(aElementName,"determineidonce")==0)
+ expectBool(fDetermineNewIDOnce);
+ else if (strucmp(aElementName,"obtainidafterinsert")==0)
+ expectBool(fObtainNewIDAfterInsert);
+ else if (strucmp(aElementName,"obtainlocalidsql")==0)
+ expectString(fObtainNewLocalIDSql);
+ else if (strucmp(aElementName,"minnextid")==0)
+ expectInt32(fMinNextID);
+ #ifdef SCRIPT_SUPPORT
+ else if (strucmp(aElementName,"localidscript")==0)
+ expectScript(fLocalIDScript, aLine, getDSFuncTableP());
+ #endif
+ else if (strucmp(aElementName,"specialidmode")==0)
+ expectEnum(sizeof(fSpecialIDMode),&fSpecialIDMode,SpecialIDModeNames,numSpecialIDModes);
+ else if (strucmp(aElementName,"insertreturnsid")==0)
+ expectBool(fInsertReturnsID);
+ else if (strucmp(aElementName,"idasoutparam")==0) // %%% possibly obsolete, replaced by %pkos and %pkoi
+ expectBool(fInsertIDAsOutParam);
+ else if (strucmp(aElementName,"ignoreaffectedcount")==0)
+ expectBool(fIgnoreAffectedCount);
+ else if (strucmp(aElementName,"lastmodfieldtype")==0)
+ expectEnum(sizeof(fLastModDBFieldType),&fLastModDBFieldType,DBFieldTypeNames,numDBfieldTypes);
+ // - SQLite support
+ #ifdef SQLITE_SUPPORT
+ else if (strucmp(aElementName,"sqlitefile")==0)
+ expectMacroString(fSQLiteFileName);
+ else if (strucmp(aElementName,"sqlitebusytimeout")==0)
+ expectUInt32(fSQLiteBusyTimeout);
+ #endif
+ // - field mappings
+ else if (strucmp(aElementName,"fieldmap")==0) {
+ // check reference argument
+ const char* ref = getAttr(aAttributes,"fieldlist");
+ if (!ref) {
+ ReportError(true,"fieldmap missing 'fieldlist' attribute");
+ }
+ else {
+ // look for field list
+ TMultiFieldDatatypesConfig *mfcfgP =
+ dynamic_cast<TMultiFieldDatatypesConfig *>(getSyncAppBase()->getRootConfig()->fDatatypesConfigP);
+ if (!mfcfgP) throw TConfigParseException("no multifield config");
+ TFieldListConfig *cfgP = mfcfgP->getFieldList(ref);
+ if (!cfgP)
+ return fail("fieldlist '%s' not defined for fieldmap",ref);
+ // - store field list reference in map
+ fFieldMappings.fFieldListP=cfgP;
+ // - let element handle parsing
+ expectChildParsing(fFieldMappings);
+ }
+ }
+ // - none known here
+ else
+ return inherited::localStartElement(aElementName,aAttributes,aLine);
+ // ok
+ return true;
+} // TOdbcDSConfig::localStartElement
+
+
+// resolve
+void TOdbcDSConfig::localResolve(bool aLastPass)
+{
+ if (aLastPass) {
+ #ifndef ODBC_UNICODE
+ // make sure we don't try to use UTF-16
+ if (fDataCharSet==chs_utf16)
+ SYSYNC_THROW(TConfigParseException("UTF-16 wide character set can only be used on ODBC-Unicode enabled platforms"));
+ #endif
+ #ifdef SQLITE_SUPPORT
+ // %%% NOP at this time
+ #endif
+ #ifdef OLD_1_0_5_CONFIG_COMPATIBLE
+ // generate new settings from old ones
+ if (fLocalIDAndTimestampFetchSQL.empty()) {
+ // SELECT localid,modifieddate(,modifiedtime)...
+ fLocalIDAndTimestampFetchSQL="SELECT ";
+ fLocalIDAndTimestampFetchSQL+=fLocalIDField;
+ fLocalIDAndTimestampFetchSQL+=", ";
+ fLocalIDAndTimestampFetchSQL += fModifiedDateField;
+ if (!fModifiedTimestamp && !fModifiedTimeField.empty()) {
+ fLocalIDAndTimestampFetchSQL += ", ";
+ fLocalIDAndTimestampFetchSQL += fModifiedTimeField;
+ }
+ // ...FROM datatable
+ fLocalIDAndTimestampFetchSQL+=" FROM ";
+ fLocalIDAndTimestampFetchSQL+=fLocalTableName;
+ // ...WHERE folderkey=x...
+ fLocalIDAndTimestampFetchSQL+=" WHERE ";
+ fLocalIDAndTimestampFetchSQL+=fFolderKeyField;
+ fLocalIDAndTimestampFetchSQL+='=';
+ quoteStringAppend("%f",fLocalIDAndTimestampFetchSQL,fStringFolderkey ? qm_backslash : qm_none);
+ // ...AND filterclause
+ fLocalIDAndTimestampFetchSQL+="%AF";
+ PDEBUGPRINTFX(DBG_EXOTIC,(" <selectidandmodifiedsql>%s</selectidandmodifiedsql>",fLocalIDAndTimestampFetchSQL.c_str()));
+ }
+ if (fDataFetchSQL.empty()) {
+ // SELECT %N FROM datatable WHERE localid=%k AND folderkey=%f
+ fDataFetchSQL="SELECT %N FROM ";
+ fDataFetchSQL+=fLocalTableName;
+ fDataFetchSQL+=" WHERE ";
+ fDataFetchSQL+=fLocalIDField;
+ fDataFetchSQL+='=';
+ quoteStringAppend("%k",fDataFetchSQL,fStringLocalID ? qm_backslash : qm_none);
+ fDataFetchSQL+=" AND ";
+ fDataFetchSQL+=fFolderKeyField;
+ fDataFetchSQL+='=';
+ quoteStringAppend("%f",fDataFetchSQL,fStringFolderkey ? qm_backslash : qm_none);
+ PDEBUGPRINTFX(DBG_EXOTIC,(" <selectdatasql>%s</selectdatasql>",fDataFetchSQL.c_str()));
+ }
+ if (fDataInsertSQL.empty()) {
+ // INSERT INTO datatable (keyfield,modtimestamp,folderkey,%N) VALUES (%k,%M,%f,%v)
+ fDataInsertSQL="INSERT INTO ";
+ fDataInsertSQL+=fLocalTableName;
+ fDataInsertSQL+=" (";
+ if (fDetermineNewIDOnce || !fObtainNewIDAfterInsert) {
+ // insert key only if previously generated separately
+ fDataInsertSQL+=fLocalIDField;
+ fDataInsertSQL+=", ";
+ }
+ fDataInsertSQL += fModifiedDateField;
+ if (!fModifiedTimestamp && !fModifiedTimeField.empty()) {
+ fDataInsertSQL += ", ";
+ fDataInsertSQL += fModifiedTimeField;
+ }
+ if (!fFolderKeyField.empty()) {
+ fDataInsertSQL += ", ";
+ fDataInsertSQL+=fFolderKeyField;
+ }
+ fDataInsertSQL+=", %N) VALUES (";
+ if (fDetermineNewIDOnce || !fObtainNewIDAfterInsert) {
+ // insert key only if previously generated separately
+ quoteStringAppend("%k",fDataInsertSQL,fStringLocalID ? qm_backslash : qm_none);
+ fDataInsertSQL += ", ";
+ }
+ if (!fModifiedTimestamp) {
+ fDataInsertSQL += "%dM";
+ if (!fModifiedTimeField.empty()) fDataInsertSQL += ", %tM";
+ }
+ else {
+ fDataInsertSQL += "%M";
+ }
+ if (!fFolderKeyField.empty()) {
+ fDataInsertSQL += ", %f";
+ }
+ fDataInsertSQL+=", %v)";
+ PDEBUGPRINTFX(DBG_EXOTIC,(" <insertdatasql>%s</insertdatasql>",fDataInsertSQL.c_str()));
+ }
+ if (fDataUpdateSQL.empty()) {
+ // UPDATE datatable SET modtimestamp=%M, %V WHERE localid=%k
+ fDataUpdateSQL="UPDATE ";
+ fDataUpdateSQL+=fLocalTableName;
+ fDataUpdateSQL+=" SET ";
+ if (!fModifiedTimestamp) {
+ fDataUpdateSQL += fModifiedDateField;
+ fDataUpdateSQL += "=%dM";
+ if (!fModifiedTimeField.empty()) {
+ fDataUpdateSQL += ", ";
+ fDataUpdateSQL += fModifiedTimeField;
+ fDataUpdateSQL += "=%tM";
+ }
+ }
+ else {
+ fDataUpdateSQL += fModifiedDateField;
+ fDataUpdateSQL += "=%M";
+ }
+ fDataUpdateSQL += ", %V WHERE ";
+ fDataUpdateSQL+=fLocalIDField;
+ fDataUpdateSQL += "=";
+ quoteStringAppend("%k",fDataUpdateSQL,fStringLocalID ? qm_backslash : qm_none);
+ PDEBUGPRINTFX(DBG_EXOTIC,(" <updatedatasql>%s</updatedatasql>",fDataUpdateSQL.c_str()));
+ }
+ if (fDataDeleteSQL.empty()) {
+ // DELETE FROM datatable WHERE localid=%k
+ fDataDeleteSQL="DELETE FROM ";
+ fDataDeleteSQL+=fLocalTableName;
+ fDataDeleteSQL+=" WHERE ";
+ fDataDeleteSQL+=fLocalIDField;
+ fDataDeleteSQL+="=";
+ quoteStringAppend("%k",fDataDeleteSQL,fStringLocalID ? qm_backslash : qm_none);
+ PDEBUGPRINTFX(DBG_EXOTIC,(" <deletedatasql>%s</deletedatasql>",fDataDeleteSQL.c_str()));
+ }
+ if (fDataZapSQL.empty() && !fLocalTableName.empty()) {
+ // DELETE FROM datatable
+ fDataZapSQL="DELETE FROM ";
+ fDataZapSQL+=fLocalTableName;
+ // ...WHERE folderkey=x...
+ fDataZapSQL+=" WHERE ";
+ fDataZapSQL+=fFolderKeyField;
+ fDataZapSQL+='=';
+ quoteStringAppend("%f",fDataZapSQL,fStringFolderkey ? qm_backslash : qm_none);
+ // ...AND filterclause
+ fDataZapSQL+="%AF";
+ PDEBUGPRINTFX(DBG_EXOTIC,(" <zapdatasql>%s</zapdatasql>",fDataZapSQL.c_str()));
+ }
+ // Map table
+ if (fMapFetchAllSQL.empty()) {
+ // SELECT localid,remoteid FROM maptable WHERE targetkey=%t
+ fMapFetchAllSQL="SELECT ";
+ fMapFetchAllSQL+=fLocalIDMapField;
+ fMapFetchAllSQL+=", ";
+ fMapFetchAllSQL+=fRemoteIDMapField;
+ fMapFetchAllSQL+=" FROM ";
+ fMapFetchAllSQL+=fMapTableName;
+ fMapFetchAllSQL+=" WHERE ";
+ fMapFetchAllSQL+=fTargetKeyMapField;
+ fMapFetchAllSQL+="=";
+ quoteStringAppend("%t",fMapFetchAllSQL,fStringTargetkey ? qm_backslash : qm_none);
+ PDEBUGPRINTFX(DBG_EXOTIC,(" <selectmapallsql>%s</selectmapallsql>",fMapFetchAllSQL.c_str()));
+ }
+ if (fMapInsertSQL.empty()) {
+ // INSERT INTO maptable (targetkey,localid,remoteid) VALUES (%t,%k,%r)
+ fMapInsertSQL="INSERT INTO ";
+ fMapInsertSQL+=fMapTableName;
+ fMapInsertSQL+=" (";
+ fMapInsertSQL+=fTargetKeyMapField;
+ fMapInsertSQL+=", ";
+ fMapInsertSQL+=fLocalIDMapField;
+ fMapInsertSQL+=", ";
+ fMapInsertSQL+=fRemoteIDMapField;
+ fMapInsertSQL+=") VALUES (";
+ quoteStringAppend("%t",fMapInsertSQL,fStringTargetkey ? qm_backslash : qm_none);
+ fMapInsertSQL+=", ";
+ quoteStringAppend("%k",fMapInsertSQL,fStringLocalID ? qm_backslash : qm_none);
+ fMapInsertSQL+=", '%r')";
+ PDEBUGPRINTFX(DBG_EXOTIC,(" <insertmapsql>%s</insertmapsql>",fMapInsertSQL.c_str()));
+ }
+ if (fMapUpdateSQL.empty()) {
+ // UPDATE maptable SET remoteid=%r WHERE targetkey=%t AND localid=%k
+ fMapUpdateSQL="UPDATE ";
+ fMapUpdateSQL+=fMapTableName;
+ fMapUpdateSQL+=" SET ";
+ fMapUpdateSQL+=fRemoteIDMapField;
+ fMapUpdateSQL+="='%r' WHERE "; // is always a string
+ fMapUpdateSQL+=fTargetKeyMapField;
+ fMapUpdateSQL+="=";
+ quoteStringAppend("%t",fMapUpdateSQL,fStringTargetkey ? qm_backslash : qm_none);
+ fMapUpdateSQL+=" AND ";
+ fMapUpdateSQL+=fLocalIDMapField;
+ fMapUpdateSQL+="=";
+ quoteStringAppend("%k",fMapUpdateSQL,fStringLocalID ? qm_backslash : qm_none);
+ PDEBUGPRINTFX(DBG_EXOTIC,(" <updatemapsql>%s</updatemapsql>",fMapUpdateSQL.c_str()));
+ }
+ if (fMapDeleteSQL.empty()) {
+ // DELETE FROM maptable WHERE targetkey=%t AND localid=%k
+ fMapDeleteSQL="DELETE FROM ";
+ fMapDeleteSQL+=fMapTableName;
+ fMapDeleteSQL+=" WHERE ";
+ fMapDeleteSQL+=fTargetKeyMapField;
+ fMapDeleteSQL+="=";
+ quoteStringAppend("%t",fMapDeleteSQL,fStringTargetkey ? qm_backslash : qm_none);
+ // up to here, this is a DELETE all for one target, so we
+ // can use this to extend fDeleteSyncTargetSQL
+ fDeleteSyncTargetSQL+=" %GO ";
+ fDeleteSyncTargetSQL+=fMapDeleteSQL;
+ // now add clause for single item
+ fMapDeleteSQL+=" AND ";
+ fMapDeleteSQL+=fLocalIDMapField;
+ fMapDeleteSQL+="=";
+ quoteStringAppend("%k",fMapDeleteSQL,fStringLocalID ? qm_backslash : qm_none);
+ PDEBUGPRINTFX(DBG_EXOTIC,(" <deletemapsql>%s</deletemapsql>",fMapDeleteSQL.c_str()));
+ }
+ #endif
+ }
+ // resolve inherited
+ inherited::localResolve(aLastPass);
+} // TOdbcDSConfig::localResolve
+
+
+#ifdef SCRIPT_SUPPORT
+void TOdbcDSConfig::apiResolveScripts(void)
+{
+ // resolve scripts which are API specific (in SAME ORDER AS IN apiRebuildScriptContexts()!)
+ TScriptContext::resolveScript(getSyncAppBase(),fLocalIDScript,fResolveContextP,NULL);
+}
+#endif
+
+
+// - create appropriate datastore from config, calls addTypeSupport as well
+TLocalEngineDS *TOdbcDSConfig::newLocalDataStore(TSyncSession *aSessionP)
+{
+ // Synccap defaults to normal set supported by the engine by default
+ TLocalEngineDS *ldsP =
+ #ifdef SYSYNC_CLIENT
+ new TODBCApiDS(this,aSessionP,getName(),aSessionP->getSyncCapMask() & ~(isOneWayFromRemoteSupported() ? 0 : SCAP_MASK_ONEWAY_SERVER));
+ #else
+ new TODBCApiDS(this,aSessionP,getName(),aSessionP->getSyncCapMask() & ~(isOneWayFromRemoteSupported() ? 0 : SCAP_MASK_ONEWAY_CLIENT));
+ #endif
+ // do common stuff
+ addTypes(ldsP,aSessionP);
+ // return
+ return ldsP;
+} // TOdbcDSConfig::newLocalDataStore
+
+
+
+// Field Map item
+// ==============
+
+TODBCFieldMapItem::TODBCFieldMapItem(const char *aElementName, TConfigElement *aParentElement) :
+ inherited(aElementName,aParentElement)
+{
+ #ifdef STREAMFIELD_SUPPORT
+ fReadBlobSQL.erase();
+ fKeyFieldName.erase();
+ #endif
+} // TODBCFieldMapItem::TODBCFieldMapItem
+
+
+void TODBCFieldMapItem::checkAttrs(const char **aAttributes)
+{
+ #ifdef STREAMFIELD_SUPPORT
+ AssignString(fReadBlobSQL,getAttr(aAttributes,"readblobsql"));
+ AssignString(fKeyFieldName,getAttr(aAttributes,"keyfield"));
+ #endif
+ inherited::checkAttrs(aAttributes);
+} // TODBCFieldMapItem::checkAttrs
+
+
+
+#ifdef ARRAYDBTABLES_SUPPORT
+
+// array container
+// ===============
+
+TODBCFieldMapArrayItem::TODBCFieldMapArrayItem(TCustomDSConfig *aCustomDSConfigP, TConfigElement *aParentElement) :
+ inherited(aCustomDSConfigP,aParentElement)
+{
+ clear();
+} // TODBCFieldMapArrayItem::TODBCFieldMapArrayItem
+
+
+TODBCFieldMapArrayItem::~TODBCFieldMapArrayItem()
+{
+ // nop so far
+ clear();
+} // TODBCFieldMapArrayItem::~TODBCFieldMapArrayItem
+
+
+// init defaults
+void TODBCFieldMapArrayItem::clear(void)
+{
+ // init defaults
+ // - clear values
+ fSelectArraySQL.erase();
+ fInsertElementSQL.erase();
+ fDeleteArraySQL.erase();
+ fAlwaysCleanArray=false;
+ // clear inherited
+ inherited::clear();
+} // TODBCFieldMapArrayItem::clear
+
+
+// config element parsing
+bool TODBCFieldMapArrayItem::localStartElement(const char *aElementName, const char **aAttributes, sInt32 aLine)
+{
+ if (strucmp(aElementName,"selectarraysql")==0)
+ expectString(fSelectArraySQL);
+ else if (strucmp(aElementName,"deletearraysql")==0)
+ expectString(fDeleteArraySQL);
+ else if (strucmp(aElementName,"insertelementsql")==0)
+ expectString(fInsertElementSQL);
+ else if (strucmp(aElementName,"alwaysclean")==0)
+ expectBool(fAlwaysCleanArray);
+ // - none known here
+ else
+ return inherited::localStartElement(aElementName,aAttributes,aLine);
+ // ok
+ return true;
+} // TODBCFieldMapArrayItem::localStartElement
+
+
+// resolve
+void TODBCFieldMapArrayItem::localResolve(bool aLastPass)
+{
+ if (aLastPass) {
+ // check for required settings
+ // %%% tbd
+ }
+ // resolve inherited
+ inherited::localResolve(aLastPass);
+} // TODBCFieldMapArrayItem::localResolve
+
+#endif // ARRAYDBTABLES_SUPPORT
+
+
+/*
+ * Implementation of TODBCApiDS
+ */
+
+
+// constructor
+TODBCApiDS::TODBCApiDS(
+ TOdbcDSConfig *aConfigP,
+ sysync::TSyncSession *aSessionP,
+ const char *aName,
+ uInt32 aCommonSyncCapMask
+) :
+ inherited(aConfigP,aSessionP, aName, aCommonSyncCapMask)
+ #ifdef ODBCAPI_SUPPORT
+ ,fODBCConnectionHandle(SQL_NULL_HANDLE) // no connection yet
+ ,fODBCReadStatement(SQL_NULL_HANDLE) // no active statements either
+ ,fODBCWriteStatement(SQL_NULL_HANDLE)
+ #ifdef SCRIPT_SUPPORT
+ ,fODBCScriptStatement(SQL_NULL_HANDLE)
+ #endif
+ #endif // ODBCAPI_SUPPORT
+ #ifdef SQLITE_SUPPORT
+ ,fSQLiteP(NULL)
+ ,fSQLiteStmtP(NULL)
+ ,fStepRc(SQLITE_OK)
+ #endif
+{
+ // save pointer to config record
+ fConfigP=aConfigP;
+ // make a local copy of the typed agent pointer
+ fAgentP=static_cast<TODBCApiAgent *>(fSessionP);
+ // make a local copy of the typed agent config pointer
+ fAgentConfigP = dynamic_cast<TOdbcAgentConfig *>(
+ aSessionP->getRootConfig()->fAgentConfigP
+ );
+ if (!fAgentConfigP) throw TSyncException(DEBUGTEXT("TODBCApiDS finds no AgentConfig","odds7"));
+ // init other stuff
+ fODBCAdminData=false; // not known yet if we'll get admin data (target, map) from ODBC or not
+ // - SQLite support
+ #ifdef SQLITE_SUPPORT
+ fUseSQLite = !fConfigP->fSQLiteFileName.empty(); // if we have a SQLite file name, use it instead of ODBC
+ #endif
+ // clear rest
+ InternalResetDataStore();
+} // TODBCApiDS::TODBCApiDS
+
+
+TODBCApiDS::~TODBCApiDS()
+{
+ InternalResetDataStore();
+} // TODBCApiDS::~TODBCApiDS
+
+
+/// @brief called while agent is still fully ok, so we must clean up such that later call of destructor does NOT access agent any more
+void TODBCApiDS::announceAgentDestruction(void)
+{
+ // reset myself
+ InternalResetDataStore();
+ // make sure we don't access the agent any more
+ engTerminateDatastore();
+ fAgentP = NULL;
+ // call inherited
+ inherited::announceAgentDestruction();
+} // TODBCApiDS::announceAgentDestruction
+
+
+/// @brief called to reset datastore
+/// @note must be safe to be called multiple times and even after announceAgentDestruction()
+void TODBCApiDS::InternalResetDataStore(void)
+{
+ #ifdef OBJECT_FILTERING
+ fFilterExpressionTested=false; // not tested yet
+ fFilterWorksOnDBLevel=true; // but assume it works
+ #endif
+ #ifdef SCRIPT_SUPPORT
+ // no SQL filter in place yet
+ fSQLFilter.erase();
+ #endif
+ #ifdef SQLITE_SUPPORT
+ // clean up SQLite
+ if (fSQLiteP) {
+ // stop it in case something is still running
+ sqlite3_interrupt(fSQLiteP);
+ // if we have a statement, finalize it
+ if (fSQLiteStmtP) {
+ // discard it
+ sqlite3_finalize(fSQLiteStmtP);
+ fSQLiteStmtP=NULL;
+ }
+ int sqrc = sqlite3_close(fSQLiteP);
+ if (sqrc!=SQLITE_OK) {
+ PDEBUGPRINTFX(DBG_ERROR,("Error closing SQLite data file: sqlite3_close() returns %d",sqrc));
+ }
+ else {
+ PDEBUGPRINTFX(DBG_DBAPI,("Closed SQLite data file"));
+ }
+ fSQLiteP=NULL;
+ }
+ #endif
+ // Clear map table and sync set lists
+ if (fAgentP) {
+ #ifdef ODBCAPI_SUPPORT
+ // no active statements any more
+ if (fODBCReadStatement) {
+ SafeSQLFreeHandle(SQL_HANDLE_STMT,fODBCReadStatement);
+ fODBCReadStatement=SQL_NULL_HANDLE;
+ }
+ if (fODBCWriteStatement) {
+ SafeSQLFreeHandle(SQL_HANDLE_STMT,fODBCWriteStatement);
+ fODBCWriteStatement=SQL_NULL_HANDLE;
+ }
+ // make sure connection is closed (and open transaction rolled back)
+ fAgentP->closeODBCConnection(fODBCConnectionHandle);
+ #endif // ODBCAPI_SUPPORT
+ // clear parameter maps
+ resetSQLParameterMaps();
+ }
+} // TODBCApiDS::InternalResetDataStore
+
+
+#ifdef SCRIPT_SUPPORT
+// called to rebuild script context for API level scripts in the datastore context
+// Note: rebuild order must be SAME AS resolve order in apiResolveScripts()
+void TODBCApiDS::apiRebuildScriptContexts(void)
+{
+ // local ID generation script
+ TScriptContext::rebuildContext(fSessionP->getSyncAppBase(),fConfigP->fLocalIDScript,fScriptContextP,fSessionP);
+} // TODBCApiDS::apiRebuildScriptContexts
+#endif
+
+
+#ifdef ODBCAPI_SUPPORT
+
+// - get (session level) ODBC handle
+SQLHDBC TODBCApiDS::getODBCConnectionHandle(void)
+{
+ if (fODBCConnectionHandle==SQL_NULL_HANDLE && fAgentP) {
+ // get a handle
+ PDEBUGPRINTFX(DBG_DBAPI+DBG_EXOTIC,("Datastore %s does not own a DB connection yet -> pulling connection from session level",getName()));
+ fODBCConnectionHandle = fAgentP->pullODBCConnectionHandle();
+ }
+ PDEBUGPRINTFX(DBG_DBAPI+DBG_EXOTIC,("Datastore %s: using connection handle 0x%lX",getName(),(uInt32)fODBCConnectionHandle));
+ return fODBCConnectionHandle;
+} // TODBCApiDS::getODBCConnectionHandle
+
+
+// - check for connection-level error
+void TODBCApiDS::checkConnectionError(SQLRETURN aResult)
+{
+ if (!fAgentP)
+ return;
+ fAgentP->checkConnectionError(aResult);
+} // TODBCApiDS::checkConnectionError
+
+#endif // ODBCAPI_SUPPORT
+
+
+// helper for mapping field names
+bool TODBCApiDS::addFieldNameList(string &aSQL, bool aForWrite, bool aForUpdate, bool aAssignedOnly, TMultiFieldItem *aItemP, TFieldMapList &fml, uInt16 aSetNo, char aFirstSep)
+{
+ TFieldMapList::iterator pos;
+ TODBCFieldMapItem *fmiP;
+
+ bool doit;
+ bool allfields=true; // all fields listed (no array fields)
+
+ for (pos=fml.begin(); pos!=fml.end(); pos++) {
+ fmiP = (TODBCFieldMapItem *)*pos;
+ if (!fmiP->isArray()) {
+ doit=
+ aSetNo==fmiP->setNo &&
+ (aForWrite
+ ? (fmiP->writable && ((aForUpdate && fmiP->for_update) || (!aForUpdate && fmiP->for_insert)))
+ : fmiP->readable
+ #ifdef STREAMFIELD_SUPPORT
+ && (fmiP->fReadBlobSQL.empty() || !fmiP->fKeyFieldName.empty()) // only read fields here that don't use a proxy or read a key FOR the proxy
+ #endif
+
+ );
+ if (doit && aAssignedOnly && aForWrite && aItemP) {
+ // check base field is assigned
+ // This should cover all cases (remember that MimeDirItem assigns empty values
+ // to all "available" fields before parsing data)
+ // - array fields are "assigned" if they have an element assigned or
+ // been called assignEmpty()
+ // - field blocks addressed by repeating properties will either be assigned all
+ // or not at all, so we can safely check base field ID only
+ // - negative FIDs except VARIDX_UNDEFINED address local vars, which
+ // should be assigned only if they may also be written
+ TItemField *fieldP=getMappedFieldOrVar(*aItemP,fmiP->fid,0,true); // existing only
+ if (!fieldP || !(fieldP->isAssigned())) doit=false; // avoid writing unassigned or non-existing (array) fields
+ }
+ if (doit) {
+ // add comma
+ if (aFirstSep) { aSQL+=aFirstSep; aSQL+=' '; }
+ aFirstSep=','; // further names are always comma separated
+ #ifdef STREAMFIELD_SUPPORT
+ if (fmiP->readable && !aForWrite && !fmiP->fKeyFieldName.empty()) {
+ // the actual field contents will be read via proxy, but we read the record key here instead
+ aSQL+=fmiP->fKeyFieldName;
+ }
+ else
+ #endif
+ {
+ // just add field name
+ aSQL+=fmiP->fElementName;
+ }
+ }
+ }
+ else
+ allfields=false; // at least one array field
+ }
+ return allfields;
+} // TODBCApiDS::addFieldNameList
+
+
+// helper for mapping field values
+// - returns aNoData=true if there is no field (e.g. array index too high) for any of the values
+// - returns false if not all fields could be listed (i.e. there are array fields)
+bool TODBCApiDS::addFieldNameValueList(string &aSQL, bool aAssignedOnly, bool &aNoData, bool &aAllEmpty, TMultiFieldItem &aItem ,bool aForUpdate, TFieldMapList &fml, uInt16 aSetNo, sInt16 aRepOffset, char aFirstSep)
+{
+ TFieldMapList::iterator pos;
+ TODBCFieldMapItem *fmiP;
+ bool allfields=true;
+ bool doit;
+
+ aNoData = true; // assume no data
+ aAllEmpty = true; // assume empty fields only
+ for (pos=fml.begin(); pos!=fml.end(); pos++) {
+ fmiP = (TODBCFieldMapItem *)*pos;
+ if (fmiP->isArray()) {
+ allfields=false; // at least one array field
+ }
+ else if (aSetNo==fmiP->setNo && fmiP->writable && ((aForUpdate && fmiP->for_update) || (!aForUpdate && fmiP->for_insert)) ) {
+ doit=true;
+ if (aAssignedOnly) {
+ // check base field is assigned (see notes in addFieldNameList())
+ TItemField *fieldP=getMappedFieldOrVar(aItem,fmiP->fid,0,true); // get existing fields only (for arrays)
+ if (!fieldP || !(fieldP->isAssigned())) doit=false; // avoid writing unassigned fields
+ }
+ if (doit) {
+ // add comma
+ if (aFirstSep) { aSQL+=aFirstSep; aSQL+=' '; }
+ aFirstSep=','; // further names are always comma separated
+ // add field name and equal sign if for update
+ if (aForUpdate) {
+ // add field name
+ aSQL+=fmiP->fElementName;
+ aSQL+='=';
+ }
+ // check how to add field value
+ if (fmiP->as_param || fmiP->dbfieldtype==dbft_blob) {
+ // either explicitly marked as param or BLOB: pass value as in-param
+ addSQLParameterMap(true,false,param_field,fmiP,&aItem,aRepOffset); // in-param
+ // add parameter placeholder into SQL
+ aSQL+='?';
+ }
+ else {
+ // add field value as literal
+ // - get base field
+ sInt16 fid=fmiP->fid;
+ if (fid==VARIDX_UNDEFINED) return false; // field does not exist
+ #ifndef SCRIPT_SUPPORT
+ // check index before using it (should not be required, as map indices are resolved
+ if (!aItem.getItemType()->isFieldIndexValid(fid)) return false; // field does not exist
+ #endif
+ if (appendFieldsLiteral(aItem,fid,aRepOffset,*fmiP,aSQL)) aAllEmpty=false;
+ }
+ }
+ }
+ }
+ aNoData=false; // data for all non-array fields found
+ return allfields;
+} // TODBCApiDS::addFieldNameValueList
+
+
+// helper for filling ODBC results into mapped fields
+// - if mapped field is an array, aRepOffset will be used as array index
+// - if mapped field is not an array, aRepOffset will be added to the base fid
+void TODBCApiDS::fillFieldsFromSQLResult(SQLHSTMT aStatement, sInt16 &aColIndex, TMultiFieldItem &aItem, TFieldMapList &fml, uInt16 aSetNo, sInt16 aRepOffset)
+{
+ sInt16 fid;
+ TFieldMapList::iterator pos;
+ TODBCFieldMapItem *fmiP;
+ bool notnull=false; // default to empty
+
+ // get data for all mapped fields
+ for (pos=fml.begin(); pos!=fml.end(); pos++) {
+ fmiP = (TODBCFieldMapItem *)*pos;
+ try {
+ if (
+ !fmiP->isArray() &&
+ aSetNo==fmiP->setNo &&
+ fmiP->readable
+ ) {
+ // was specified in SELECT, so we can read it
+ if ((fid=fmiP->fid)!=VARIDX_UNDEFINED) {
+ TItemField *fieldP;
+ TDBFieldType dbfty = fmiP->dbfieldtype;
+ // single field mapping
+ fieldP=getMappedFieldOrVar(aItem,fid,aRepOffset);
+ if (fieldP) {
+ #ifdef STREAMFIELD_SUPPORT
+ if (!fmiP->fReadBlobSQL.empty()) {
+ // retrieve key for this field if map specifies a "keyfield"
+ string key;
+ #ifdef SQLITE_SUPPORT
+ // - always for data access
+ if (fUseSQLite) {
+ if (!fmiP->fKeyFieldName.empty()) {
+ key = (const char *)sqlite3_column_text(fSQLiteStmtP,aColIndex-1);
+ // read something, next column
+ aColIndex++;
+ }
+ }
+ else
+ #endif
+ {
+ #ifdef ODBCAPI_SUPPORT
+ if (!fmiP->fKeyFieldName.empty()) {
+ fAgentP->getColumnValueAsString(aStatement,aColIndex,key,chs_ascii);
+ // read something, next column
+ aColIndex++;
+ }
+ #endif // ODBCAPI_SUPPORT
+ }
+ // use proxy for this field
+ if (!fieldP->isBasedOn(fty_string)) {
+ throw TSyncException("<readblobsql> allowed for string or BLOB fields only!");
+ }
+ // create and install a proxy for this field
+ PDEBUGPRINTFX(DBG_DBAPI+DBG_EXOTIC,(
+ "Installed proxy for '%s' to retrieve data later when needed. MasterKey='%s', 'DetailKey='%s'",
+ fmiP->getName(),
+ aItem.getLocalID(),
+ key.c_str()
+ ));
+ TODBCFieldProxy *odbcProxyP = new TODBCFieldProxy(this,fmiP,aItem.getLocalID(),key.c_str());
+ // attach it to the string or blob field
+ static_cast<TStringField *>(fieldP)->setBlobProxy(odbcProxyP);
+ }
+ else
+ #endif // STREAMFIELD_SUPPORT
+ {
+ // Note: this will also select fields that may not
+ // be available for the current remote party, but these
+ // will be suppressed by the TMultiFieldItemType descendant.
+ // As different items COULD theoretically use different
+ // formats, filtering here already would be complicated.
+ // so we live with reading probably more fields than really
+ // needed.
+ #ifdef SQLITE_SUPPORT
+ // - always for data access
+ if (fUseSQLite) {
+ // SQLite
+ notnull = fAgentP->getSQLiteColValueAsField(
+ fSQLiteStmtP, // local sqlite statement
+ aColIndex-1, // SQLITE colindex starts at 0, not 1 like in ODBC
+ dbfty,
+ fieldP,
+ fConfigP->fDataCharSet,
+ fmiP->floating_ts ? TCTX_UNKNOWN : fConfigP->fDataTimeZone,
+ fConfigP->fUserZoneOutput
+ );
+ }
+ else
+ #endif
+ {
+ #ifdef ODBCAPI_SUPPORT
+ // ODBC
+ notnull=fAgentP->getColumnValueAsField(
+ aStatement,
+ aColIndex,
+ dbfty,
+ fieldP,
+ fConfigP->fDataCharSet, // real charset, including UTF16
+ fmiP->floating_ts ? TCTX_UNKNOWN : fConfigP->fDataTimeZone,
+ fConfigP->fUserZoneOutput
+ );
+ #endif // ODBCAPI_SUPPORT
+ }
+ // next column
+ aColIndex++;
+ }
+ } // if field available
+ else {
+ // this can happen with array maps mapping to field blocks with a too high fMaxRepeat
+ // it should not happen with array fields.
+ throw TSyncException(DEBUGTEXT("FATAL: Field in map not found in item","odds6"));
+ }
+ } // if fid specified
+ else {
+ // no field mapped, skip column
+ aColIndex++;
+ }
+ } // if readable, not array and correct set number (and thus SELECTed) field
+ }
+ catch (exception &e) {
+ PDEBUGPRINTFX(DBG_ERROR,(
+ "fillFieldsFromSQLResult field='%s', colindex=%hd, fid=%hd%s, setno=%hd, dbfty=%s,failed: %s",
+ fmiP->fElementName.c_str(),
+ aColIndex,
+ fmiP->fid,
+ fmiP->isArray() ? " (array)" : "",
+ fmiP->setNo,
+ DBFieldTypeNames[fmiP->dbfieldtype],
+ e.what()
+ ));
+ throw;
+ }
+ } // field loop
+} // TODBCApiDS::fillFieldsFromSQLResult
+
+
+
+#ifdef ARRAYDBTABLES_SUPPORT
+
+// read an array field
+// NOTE: non-array fields are already read when this is called
+void TODBCApiDS::readArray(SQLHSTMT aStatement, TMultiFieldItem &aItem, TFieldMapArrayItem *aMapItemP)
+{
+ string sql;
+ sInt16 colindex,i;
+ uInt16 setno;
+
+ TODBCFieldMapArrayItem *fmaiP = dynamic_cast<TODBCFieldMapArrayItem *>(aMapItemP);
+ if (!fmaiP) return; // do nothing
+ #ifdef SCRIPT_SUPPORT
+ // process init script
+ fArrIdx=0; // start at array index=0
+ fParentKey=aItem.getLocalID();
+ fWriting=false;
+ fInserting=false;
+ fDeleting=false;
+ fAgentP->fScriptContextDatastore=this;
+ if (!TScriptContext::executeTest(
+ true, // read array if script returns nothing or no script present
+ fScriptContextP, // context
+ fmaiP->fInitScript, // the script
+ fConfigP->getDSFuncTableP(),fAgentP, // funcdefs/context
+ &aItem,true // target item, writeable
+ )) {
+ // prevented array reading by script returning false
+ return; // do nothing for this array
+ }
+ #else
+ fArrIdx=0; // start at array index=0
+ #endif // SCRIPT_SUPPORT
+ // process SQL statement(s)
+ i=0;
+ while (getNextSQLStatement(fmaiP->fSelectArraySQL,i,sql,setno)) {
+ // Apply substitutions
+ resetSQLParameterMaps();
+ DoDataSubstitutions(sql,fmaiP->fArrayFieldMapList,setno,false,false,&aItem,fArrIdx*fmaiP->fRepeatInc);
+ prepareSQLStatement(aStatement, sql.c_str(),true,"reading array");
+ // - prepare parameters
+ bindSQLParameters(aStatement,true);
+ // Execute
+ try {
+ execSQLStatement(aStatement,sql,true,NULL,true);
+ }
+ catch (exception &e) {
+ PDEBUGPRINTFX(DBG_DATA+DBG_DBAPI,(
+ "readarray:execSQLStatement sql='%s' failed: %s",
+ sql.c_str(),
+ e.what()
+ ));
+ throw;
+ }
+ // get out params
+ saveAndCleanupSQLParameters(aStatement,true);
+ // Fetch
+ // - fetch result(s) now, at most fMaxRepeat
+ while (fmaiP->fMaxRepeat==0 || fArrIdx<fmaiP->fMaxRepeat) {
+ colindex=1; // ODBC style, fillFieldsFromSQLResult will adjust for SQLite if needed
+ if (!fetchNextRow(aStatement,true)) break; // all available rows fetched
+ // now fill data into array's mapped fields (array fields or field blocks)
+ fillFieldsFromSQLResult(aStatement,colindex,aItem,fmaiP->fArrayFieldMapList,setno,fArrIdx*fmaiP->fRepeatInc);
+ #ifdef SCRIPT_SUPPORT
+ // process afterread script
+ fAgentP->fScriptContextDatastore=this;
+ if (!TScriptContext::execute(fScriptContextP,fmaiP->fAfterReadScript,fConfigP->getDSFuncTableP(),fAgentP,&aItem,true))
+ throw TSyncException("<afterreadscript> failed");
+ #endif
+ // increment array index/count
+ fArrIdx++;
+ }
+ // no more records
+ finalizeSQLStatement(aStatement,true);
+ // save size of array in "sizefrom" field (if it's not an array)
+ TItemField *fldP = getMappedBaseFieldOrVar(aItem,fmaiP->fid);
+ if (fldP && !fldP->isArray())
+ fldP->setAsInteger(fArrIdx);
+ // make pass filter if there are no items in the array
+ #ifdef OBJECT_FILTERING
+ if (fArrIdx==0) aItem.makePassFilter(fmaiP->fNoItemsFilter.c_str()); // item is made pass filter, if possible
+ #endif
+ #ifdef SCRIPT_SUPPORT
+ // process finish script, can perform more elaborated stuff that makePassFilter can
+ fAgentP->fScriptContextDatastore=this;
+ if (!TScriptContext::execute(fScriptContextP,fmaiP->fFinishScript,fConfigP->getDSFuncTableP(),fAgentP,&aItem,true))
+ throw TSyncException("<finishscript> failed");
+ #endif
+ }
+} // TODBCApiDS::readArray
+
+
+// write or delete an array field
+// NOTE: non-array fields are already written when this is called
+void TODBCApiDS::writeArray(bool aDelete, bool aInsert, SQLHSTMT aStatement, TMultiFieldItem &aItem, TFieldMapArrayItem *aMapItemP)
+{
+ string sql;
+ sInt16 i;
+ uInt16 setno;
+ bool done, allempty;
+
+ TODBCFieldMapArrayItem *fmaiP = dynamic_cast<TODBCFieldMapArrayItem *>(aMapItemP);
+ PDEBUGPRINTFX(DBG_DATA+DBG_DBAPI+DBG_EXOTIC,("Writing Array, fmaiP=0x%lX",(long)fmaiP));
+ if (!fmaiP) return; // do nothing
+ PDEBUGPRINTFX(DBG_DATA+DBG_DBAPI+DBG_EXOTIC,("Writing Array for field '%s'",fmaiP->getName()));
+ // Check initscript first. If it returns false, we do not insert anything
+ bool doit;
+ #ifdef SCRIPT_SUPPORT
+ // process init script
+ fArrIdx=0; // start at array index=0
+ fParentKey=aItem.getLocalID();
+ fWriting=true;
+ fInserting=aInsert;
+ fDeleting=aDelete;
+ fAgentP->fScriptContextDatastore=this;
+ doit = TScriptContext::executeTest(
+ true, // write array if script returns nothing or no script present
+ fScriptContextP, // context
+ fmaiP->fInitScript, // the script
+ &ODBCDSFuncTable1,fAgentP, // funcdefs/context
+ &aItem,true // target item, writeable
+ );
+ #else
+ doit=true; // no script, always do it
+ fArrIdx=0; // start at array index=0
+ #endif // SCRIPT_SUPPORT
+ PDEBUGPRINTFX(DBG_DATA+DBG_DBAPI+DBG_EXOTIC,("Writing Array: doit=%d, aDelete=%d, aInsert=%d",(int) doit,(int) aDelete,(int) aInsert));
+ // check if we need delete first
+ if (doit && aDelete) {
+ i=0;
+ while (getNextSQLStatement(fmaiP->fDeleteArraySQL,i,sql,setno)) {
+ // apply data substitutions
+ resetSQLParameterMaps();
+ DoDataSubstitutions(sql,fmaiP->fArrayFieldMapList,setno,true,false,&aItem,0);
+ // - prepare parameters
+ prepareSQLStatement(aStatement, sql.c_str(),true,"array erase");
+ bindSQLParameters(aStatement,true);
+ // issue
+ execSQLStatement(aStatement,sql,true,NULL,true);
+ // get out params
+ saveAndCleanupSQLParameters(aStatement,true);
+ // done
+ finalizeSQLStatement(aStatement,true);
+ }
+ }
+ // check if we need to insert the array elements
+ doit=doit && aInsert;
+ #ifdef OBJECT_FILTERING
+ if (doit) {
+ // test filter
+ doit =
+ fmaiP->fNoItemsFilter.empty() || // no filter, always generate
+ !aItem.testFilter(fmaiP->fNoItemsFilter.c_str()); // if item passes filter, array should not be written
+ }
+ #endif
+ if (doit) {
+ // do array write
+ sInt32 numElements=0; // unlimited to begin with
+ // - see if we have a sizefrom field
+ TItemField *fldP = getMappedBaseFieldOrVar(aItem,fmaiP->fid);
+ if (fldP) {
+ if (fldP->isArray())
+ numElements=fldP->arraySize(); // number of detail records to write is size of this array
+ else
+ numElements=fldP->getAsInteger(); // number of detail records to write are specified in integer variable
+ }
+ else {
+ // entire array (=up to INSERT statement that would contain only empty fields)
+ // Note: we cannot use the size of a single array field, as there might be
+ // more than one with different sizes. End of array is when
+ // DoDataSubstitutions() returns allempty==true
+ numElements=REP_ARRAY; // this is a big number
+ }
+ // - limit to maxrepeat
+ if (fmaiP->fMaxRepeat!=0 && numElements>fmaiP->fMaxRepeat)
+ numElements=fmaiP->fMaxRepeat;
+ // - now write
+ #ifdef SCRIPT_SUPPORT
+ fDeleting=false; // no longer deleting records
+ #endif
+ while (fArrIdx<numElements) {
+ #ifdef SCRIPT_SUPPORT
+ // process beforewrite script, end of array if it returns false
+ fAgentP->fScriptContextDatastore=this;
+ if (!TScriptContext::executeTest(true,fScriptContextP,fmaiP->fBeforeWriteScript,fConfigP->getDSFuncTableP(),fAgentP,&aItem,true))
+ goto endarray;
+ #endif
+ // perform insert statement(s)
+ i=0;
+ bool firststatement=true;
+ while (getNextSQLStatement(fmaiP->fInsertElementSQL,i,sql,setno)) {
+ // do substitutions and find out if empty
+ resetSQLParameterMaps();
+ DoDataSubstitutions(sql,fmaiP->fArrayFieldMapList,setno,true,false,&aItem,fArrIdx*fmaiP->fRepeatInc,&allempty,&done);
+ // stop here if FIRST STATEMENT in (unlimited) array insert is all empty
+ if (firststatement && fmaiP->fMaxRepeat==0 && allempty)
+ goto endarray;
+ // store if not empty or empty storage allowed
+ if (fmaiP->fStoreEmpty || !allempty) {
+ // - prepare parameters
+ prepareSQLStatement(aStatement, sql.c_str(),true,"array element insert");
+ bindSQLParameters(aStatement,true);
+ // issue
+ execSQLStatement(aStatement,sql,false,NULL,true);
+ // get out params
+ saveAndCleanupSQLParameters(aStatement,true);
+ // no more records
+ finalizeSQLStatement(aStatement,true);
+ }
+ firststatement=false;
+ }
+ #ifdef SCRIPT_SUPPORT
+ // process afterwrite script, end of array if it returns false
+ fAgentP->fScriptContextDatastore=this;
+ if (!TScriptContext::executeTest(true,fScriptContextP,fmaiP->fAfterWriteScript,fConfigP->getDSFuncTableP(),fAgentP,&aItem,true))
+ goto endarray;
+ #endif
+ // next
+ fArrIdx++;
+ } // while
+ endarray:
+ #ifdef SCRIPT_SUPPORT
+ // process finish script
+ fAgentP->fScriptContextDatastore=this;
+ if (!TScriptContext::execute(fScriptContextP,fmaiP->fFinishScript,fConfigP->getDSFuncTableP(),fAgentP,&aItem,true))
+ throw TSyncException("<finishscript> failed");
+ #endif
+ ;
+ }
+} // TODBCApiDS::writeArray
+
+
+// read array fields.
+// - if aStatement is passed NULL, routine will allocate/destroy its
+// own statement for reading the array fields
+void TODBCApiDS::readArrayFields(SQLHSTMT aStatement, TMultiFieldItem &aItem, TFieldMapList &fml)
+{
+ TFieldMapList::iterator pos;
+
+ // get data for all array table fields
+ for (pos=fml.begin(); pos!=fml.end(); pos++) {
+ if ((*pos)->isArray()) {
+ readArray(aStatement,aItem,(TFieldMapArrayItem *)*pos);
+ }
+ }
+} // TODBCApiDS::readArrayFields
+
+
+void TODBCApiDS::writeArrayFields(SQLHSTMT aStatement, TMultiFieldItem &aItem, TFieldMapList &fml, bool aInsert)
+{
+ TFieldMapList::iterator pos;
+
+ // write all array table fields
+ for (pos=fml.begin(); pos!=fml.end(); pos++) {
+ if ((*pos)->isArray()) {
+ // delete first, then insert
+ writeArray(!aInsert || static_cast<TODBCFieldMapArrayItem *>(*pos)->fAlwaysCleanArray,true,aStatement,aItem,(TFieldMapArrayItem *)*pos);
+ }
+ }
+} // TODBCApiDS::writeArrayFields
+
+
+void TODBCApiDS::deleteArrayFields(SQLHSTMT aStatement, TMultiFieldItem &aItem, TFieldMapList &fml)
+{
+ TFieldMapList::iterator pos;
+
+ // delete all array table fields
+ for (pos=fml.begin(); pos!=fml.end(); pos++) {
+ if ((*pos)->isArray()) {
+ // only delete, no insert
+ writeArray(true,false,aStatement,aItem,(TFieldMapArrayItem *)*pos);
+ }
+ }
+} // TODBCApiDS::deleteArrayFields
+
+#endif // ARRAYDBTABLES_SUPPORT
+
+
+
+// append field value(s) as single literal to SQL text
+// - returns true if field(s) were not empty
+// - even non-existing or empty field will append at least NULL or '' to SQL
+bool TODBCApiDS::appendFieldsLiteral(TMultiFieldItem &aItem, sInt16 aFid, sInt16 aRepOffset,TODBCFieldMapItem &aFieldMapping, string &aSQL)
+{
+ string val;
+ bool notempty=false;
+
+ TItemField *fieldP;
+
+ // get mapped item field or local script variable
+ fieldP=getMappedFieldOrVar(aItem,aFid,aRepOffset,true); // existing (array fields) only
+ // now process single field
+ if (!fieldP) goto novalue; // no data -> empty
+ notempty=appendFieldValueLiteral(*fieldP, aFieldMapping.dbfieldtype, aFieldMapping.maxsize, aFieldMapping.floating_ts, aSQL);
+ return notempty;
+novalue:
+ // no value was produced
+ aSQL+="NULL";
+ return false;
+} // TODBCApiDS::appendFieldsLiteral
+
+
+// append field value as literal to SQL text
+// - returns true if field(s) were not empty
+// - even non-existing or empty field will append at least NULL or '' to SQL
+bool TODBCApiDS::appendFieldValueLiteral(TItemField &aField,TDBFieldType aDBFieldType, uInt32 aMaxSize, bool aIsFloating, string &aSQL)
+{
+ return
+ fAgentP->appendFieldValueLiteral(
+ aField, aDBFieldType, aMaxSize, aSQL,
+ fConfigP->sqlPrepCharSet(),
+ fConfigP->fDataLineEndMode,
+ fConfigP->fQuotingMode,
+ aIsFloating ? TCTX_UNKNOWN : fConfigP->fDataTimeZone,
+ fRecordSize
+ );
+} // TODBCApiDS::appendFieldValueLiteral
+
+
+// issue single (or multiple) data update statements
+// NOTE: returns false if no rows affected. If no information about rows affected
+// is found, returns false if all statements return SQL_NODATA.
+// If config fIgnoreAffectedCount is set, always returns true (unless we have an error).
+bool TODBCApiDS::IssueDataWriteSQL(
+ SQLHSTMT aStatement,
+ const string &aSQL,
+ const char *aComment,
+ bool aForUpdate,
+ TFieldMapList &aFieldMapList, // field map list for %N,%V and %v
+ TMultiFieldItem *aItemP // item to read values and localid from
+)
+{
+ uInt16 setno=0; // default to 0
+ string sql;
+ bool d,hasdata=false;
+ SQLLEN affectedRows,totalAffected=-1;
+
+ sInt16 i=0;
+
+ // add field values
+ fRecordSize=0; // reset count, appendFieldValueLiteral will update size
+ while (getNextSQLStatement(aSQL,i,sql,setno)) {
+ // - do substitutions
+ resetSQLParameterMaps();
+ PDEBUGPRINTFX(DBG_DBAPI+DBG_EXOTIC,("SQL before substitutions: %s",aSQL.c_str()));
+ DoDataSubstitutions(
+ sql, // string to apply substitutions to
+ aFieldMapList, // field map list for %N,%V and %v
+ setno, // set number
+ true, // for write
+ aForUpdate, // for update?
+ aItemP // item to read values and localid from
+ );
+ // - prepare parameters
+ prepareSQLStatement(aStatement, sql.c_str(),true,aComment);
+ bindSQLParameters(aStatement,true);
+ // - execute it
+ d = execSQLStatement(aStatement,sql,true,NULL,true);
+ // - get number of affected rows
+ #ifdef SQLITE_SUPPORT
+ if (fUseSQLite) {
+ affectedRows = sqlite3_changes(fSQLiteP);
+ }
+ else
+ #endif
+ {
+ #ifdef ODBCAPI_SUPPORT
+ SQLRETURN res = SQLRowCount(aStatement,&affectedRows);
+ if (res!=SQL_SUCCESS && res!=SQL_SUCCESS_WITH_INFO)
+ affectedRows = -1; // unknown
+ #else
+ affectedRows=0; // no API, no rows affected
+ #endif
+ }
+ // - count affected rows
+ if (totalAffected<0)
+ totalAffected = affectedRows;
+ else if (affectedRows>0)
+ totalAffected += affectedRows;
+ PDEBUGPRINTFX(DBG_DBAPI+DBG_EXOTIC,("IssueDataWriteSQL: Statement reports %ld affected rows - totalAffected=%ld",affectedRows,totalAffected));
+ // get out params
+ saveAndCleanupSQLParameters(aStatement,true);
+ // done with statement
+ finalizeSQLStatement(aStatement,true);
+ hasdata = hasdata || d;
+ }
+ if (fConfigP->fIgnoreAffectedCount)
+ return true; // assume always successful
+ else if (totalAffected<0)
+ return hasdata; // no reliable info about affected rows - use hasdata instead
+ else
+ return totalAffected>0; // assume modified something if any row was affected
+} // TODBCApiDS::IssueDataWriteSQL
+
+
+#ifdef HAS_SQL_ADMIN
+
+// issue single (or multiple) map access statements
+// NOTE: if all return SQL_NODATA, function will return false.
+bool TODBCApiDS::IssueMapSQL(
+ SQLHSTMT aStatement,
+ const string &aSQL,
+ const char *aComment,
+ TMapEntryType aEntryType,
+ const char *aLocalID,
+ const char *aRemoteID,
+ uInt32 aMapFlags
+)
+{
+ uInt16 setno=0; // default to 0
+ string sql;
+ bool d,hasdata=false;
+
+ sInt16 i=0;
+ while (getNextSQLStatement(aSQL,i,sql,setno)) {
+ // - do substitutions
+ DoMapSubstitutions(sql,aEntryType,aLocalID,aRemoteID,aMapFlags);
+ // - execute it
+ d = execSQLStatement(aStatement,sql,true,aComment,false);
+ hasdata = hasdata || d;
+ }
+ return hasdata;
+} // TODBCApiDS::IssueMapSQL
+
+#endif // HAS_SQL_ADMIN
+
+
+// execute SQL statement as-is, without any substitutions
+bool TODBCApiDS::execSQLStatement(SQLHSTMT aStatement, string &aSQL, bool aNoDataAllowed, const char *aComment, bool aForData)
+{
+ #ifdef ODBCAPI_SUPPORT
+ SQLRETURN res;
+ #endif
+
+ // show what statement will be executed
+ #ifdef SYDEBUG
+ if (aComment && PDEBUGTEST(DBG_DBAPI)) {
+ PDEBUGPRINTFX(DBG_DBAPI,("SQL for %s:",aComment));
+ PDEBUGPUTSX(DBG_DBAPI,aSQL.c_str());
+ }
+ #endif
+ // avoid executing empty statement
+ if (aSQL.empty()) return true; // "ok", nothing to execute
+ #ifdef SQLITE_SUPPORT
+ if (fUseSQLite && aForData) {
+ // execute (eventually already prepared) statement in SQLite
+ if (!fSQLiteStmtP) {
+ // not yet prepared, do it now
+ fAgentP->prepareSQLiteStatement(aSQL.c_str(),fSQLiteP,fSQLiteStmtP);
+ }
+ // do first step
+ fStepRc = sqlite3_step(fSQLiteStmtP);
+ // clean up right now if we don't have data
+ if (fStepRc!=SQLITE_ROW) {
+ fStepRc=sqlite3_finalize(fSQLiteStmtP);
+ fSQLiteStmtP=NULL;
+ fAgentP->checkSQLiteError(fStepRc,fSQLiteP);
+ }
+ // check if we MUST have data here
+ if (!aNoDataAllowed) {
+ if (fStepRc!=SQLITE_ROW) {
+ // we're not happy because we have no data
+ return false;
+ }
+ }
+ return true; // ok
+ }
+ #endif
+ {
+ #ifdef ODBCAPI_SUPPORT
+ // execute statement
+ TP_DEFIDX(li);
+ TP_SWITCH(li,fSessionP->fTPInfo,TP_database);
+ #ifdef ODBC_UNICODE
+ if (fConfigP->fDataCharSet==chs_utf16) {
+ // aSQL is UTF-8 here
+ // make UCS2 string to pass to wide char API
+ string wSQL;
+ appendUTF8ToUTF16ByteString(aSQL.c_str(), wSQL, ODBC_BIGENDIAN);
+ wSQL += (char)0; // together with the implicit 8-bit NUL terminator, this makes a 16-bit NUL terminator
+ // execute it with the "W" version
+ res = SafeSQLExecDirectW(
+ aStatement,
+ (SQLWCHAR *)wSQL.c_str(),
+ wSQL.size()/2 // actual number of Unicode chars
+ );
+ }
+ else
+ #endif // ODBC_UNICODE
+ {
+ res = SafeSQLExecDirect(
+ aStatement,
+ (SQLCHAR *)aSQL.c_str(),
+ aSQL.size()
+ );
+ }
+ TP_START(fSessionP->fTPInfo,li);
+ // check
+ if (aNoDataAllowed)
+ return fAgentP->checkStatementHasData(res,aStatement);
+ else {
+ fAgentP->checkStatementError(res,aStatement);
+ return true;
+ }
+ #endif // ODBCAPI_SUPPORT
+ }
+ // should never happen
+ return false;
+} // TODBCApiDS::execSQLStatement
+
+
+// get next statment from statement list
+// - may begin with %GO(setno)
+bool TODBCApiDS::getNextSQLStatement(const string &aSQL, sInt16 &aStartAt, string &aOneSQL, uInt16 &aSetNo)
+{
+ aSetNo=0; // default to set 0
+ string::size_type e;
+ bool foundone=false;
+ sInt32 n=aSQL.size();
+
+ // check for %GO
+ while (aStartAt<n) {
+ // avoid "compare" here as it is not consistent in Linux g++ bastring.h and MSL
+ if (strnncmp(aSQL.c_str()+aStartAt,"%GO",3)==0) {
+ aStartAt+=3;
+ // check for statement number spec
+ const char *p = aSQL.c_str()+aStartAt;
+ const char *q=p;
+ if (*p=='(') {
+ p++;
+ // get set number
+ if (sscanf(p,"%hd",&aSetNo)!=1) aSetNo=0;
+ // search closing paranthesis
+ while (*p) { if (*p++==')') break; }
+ }
+ // skip set no specs
+ aStartAt+=p-q;
+ }
+ // - skip spaces
+ while (aStartAt<sInt32(aSQL.size())) {
+ if (!isspace(aSQL[aStartAt])) break;
+ aStartAt++;
+ }
+ // aStart now points to beginning of next statement
+ // - determine end of next statement
+ e=aSQL.find("%GO",aStartAt);
+ if (e==string::npos) {
+ // last statement
+ e=aSQL.size(); // set to end of string
+ }
+ if (e>string::size_type(aStartAt)) {
+ // Not-empty statement
+ aOneSQL.assign(aSQL,aStartAt,e-aStartAt);
+ // Next statement starts here
+ aStartAt=e;
+ // statement found
+ foundone=true;
+ break;
+ }
+ } // while more in input and empty statement
+ return foundone;
+} // TODBCApiDS::getNextSQLStatement
+
+
+// inform logic of coming state change
+localstatus TODBCApiDS::dsBeforeStateChange(TLocalEngineDSState aOldState,TLocalEngineDSState aNewState)
+{
+ // let inherited do its stuff as well
+ return inherited::dsBeforeStateChange(aOldState,aNewState);
+} // TODBCApiDS::dsBeforeStateChange
+
+
+
+// inform logic of happened state change
+localstatus TODBCApiDS::dsAfterStateChange(TLocalEngineDSState aOldState,TLocalEngineDSState aNewState)
+{
+ // let inherited do its stuff as well
+ return inherited::dsAfterStateChange(aOldState,aNewState);
+} // TODBCApiDS::dsAfterStateChange
+
+
+#ifdef ODBCAPI_SUPPORT
+
+// Routine mapping
+#define lineartimeToLiteralAppend lineartimeToODBCLiteralAppend
+
+#endif // ODBCAPI_SUPPORT
+
+
+#ifdef HAS_SQL_ADMIN
+
+// log datastore sync result
+// - Called at end of sync with this datastore
+void TODBCApiDS::dsLogSyncResult(void)
+{
+ uInt16 setno=0; // default to 0
+ string sql;
+ sInt16 i=0;
+
+ // if we have a SQL statement and logging of this session is enabled, log
+ if (fSessionP->logEnabled() && !fAgentConfigP->fWriteLogSQL.empty()) {
+ // execute SQL statement for logging
+ try {
+ SQLHSTMT statement=fAgentP->newStatementHandle(getODBCConnectionHandle());
+ try {
+ while (getNextSQLStatement(fAgentConfigP->fWriteLogSQL,i,sql,setno)) {
+ // - do substitutions
+ DoLogSubstitutions(sql,false);
+ execSQLStatement(statement,sql,true,"Log Entry Write",false);
+ finalizeSQLStatement(statement,false);
+ }
+ // release the statement handle
+ SafeSQLFreeHandle(SQL_HANDLE_STMT,statement);
+ // commit the transaction (this is called AFTER the data transactions probably have been
+ // rolled back in EndDataWrite
+ SafeSQLEndTran(SQL_HANDLE_DBC,getODBCConnectionHandle(),SQL_COMMIT);
+ }
+ catch (exception &e) {
+ // release the statement handle
+ SafeSQLFreeHandle(SQL_HANDLE_STMT,statement);
+ throw;
+ }
+ }
+ catch (exception &e) {
+ // release the statement handle
+ PDEBUGPRINTFX(DBG_ERROR,("Failed to issue Log SQL statement: %s",e.what()));
+ }
+ }
+ // let ancestor process
+ TStdLogicDS::dsLogSyncResult();
+} // TODBCApiDS::dsLogSyncResult
+
+
+// Do logfile SQL/plaintext substitutions
+void TODBCApiDS::DoLogSubstitutions(string &aLog,bool aPlaintext)
+{
+ string s;
+ size_t i;
+
+ // logging
+ if (!aPlaintext) {
+ // only for SQL text
+ // Note: we need to do free form strings here to ensure correct DB string escaping
+ // %rD Datastore remote path
+ StringSubst(aLog,"%rD",getRemoteDBPath(),3,chs_ascii,lem_cstr,fConfigP->fQuotingMode);
+ // %nR Remote name: [Manufacturer ]Model")
+ StringSubst(aLog,"%nR",fSessionP->getRemoteDescName(),3,chs_ascii,lem_cstr,fConfigP->fQuotingMode);
+ // %vR Remote Device Version Info ("Type (HWV, FWV, SWV) Oem")
+ StringSubst(aLog,"%vR",fSessionP->getRemoteInfoString(),3,chs_ascii,lem_cstr,fConfigP->fQuotingMode);
+ // %U User Name
+ StringSubst(aLog,"%U",fSessionP->getSyncUserName(),2,chs_ascii,lem_cstr,fConfigP->fQuotingMode);
+ // %lD Datastore local path (complete with all CGI)
+ StringSubst(aLog,"%lD",getRemoteViewOfLocalURI(),3,chs_ascii,lem_cstr,fConfigP->fQuotingMode);
+ // %iR Remote Device ID (URI)
+ StringSubst(aLog,"%iR",fSessionP->getRemoteURI(),3,chs_ascii,lem_cstr,fConfigP->fQuotingMode);
+ // %dT Sync date
+ i=0; while((i=aLog.find("%dT",i))!=string::npos) {
+ s.erase();
+ fAgentP->lineartimeToLiteralAppend(fCurrentSyncTime, s, true, false, TCTX_UTC, fConfigP->fDataTimeZone);
+ aLog.replace(i,3,s); i+=s.size();
+ }
+ // %tT Sync time
+ i=0; while((i=aLog.find("%tT",i))!=string::npos) {
+ s.erase();
+ fAgentP->lineartimeToLiteralAppend(fCurrentSyncTime, s, false, true, TCTX_UTC, fConfigP->fDataTimeZone);
+ aLog.replace(i,3,s); i+=s.size();
+ }
+ // %T Sync timestamp
+ i=0; while((i=aLog.find("%T",i))!=string::npos) {
+ s.erase();
+ fAgentP->lineartimeToLiteralAppend(fCurrentSyncTime, s, true, true, TCTX_UTC, fConfigP->fDataTimeZone);
+ aLog.replace(i,2,s); i+=s.size();
+ }
+ // %ssT Sync start time timestamp
+ i=0; while((i=aLog.find("%ssT",i))!=string::npos) {
+ s.erase();
+ fAgentP->lineartimeToLiteralAppend(fSessionP->getSessionStarted(), s, true, true, TCTX_UTC, fConfigP->fDataTimeZone);
+ aLog.replace(i,4,s); i+=s.size();
+ }
+ // %seT Sync end time timestamp
+ i=0; while((i=aLog.find("%seT",i))!=string::npos) {
+ s.erase();
+ fAgentP->lineartimeToLiteralAppend(getEndOfSyncTime(), s, true, true, TCTX_UTC, fConfigP->fDataTimeZone);
+ aLog.replace(i,4,s); i+=s.size();
+ }
+ }
+ // - let ancestor process its own substitutions
+ TStdLogicDS::DoLogSubstitutions(aLog,aPlaintext);
+ // %t Target Key
+ // %f Folder Key
+ // %u User key
+ // %d device key
+ // - let standard SQL substitution handle this
+ DoSQLSubstitutions(aLog);
+} // TODBCApiDS::DoLogSubstitutions
+
+
+// do substitutions for map table access
+void TODBCApiDS::DoMapSubstitutions(
+ string &aSQL, // string to apply substitutions to
+ TMapEntryType aEntryType, // the entry type
+ const char *aLocalID, // local ID
+ const char *aRemoteID, // remote ID
+ uInt32 aMapFlags // map flags
+)
+{
+ // can be empty, but we don't do NULLs
+ if (!aLocalID) aLocalID="";
+ if (!aRemoteID) aRemoteID="";
+ // %k = data key (local ID)
+ StringSubst(aSQL,"%k",aLocalID,2,-1,fConfigP->sqlPrepCharSet(),fConfigP->fDataLineEndMode,fConfigP->fQuotingMode);
+ // %r = remote ID (always a string)
+ StringSubst(aSQL,"%r",aRemoteID,2,-1,fConfigP->sqlPrepCharSet(),fConfigP->fDataLineEndMode,fConfigP->fQuotingMode);
+ // %x = flags (a uInt32)
+ StringSubst(aSQL,"%x",(sInt32)aMapFlags,2);
+ // %e = entry type (a small number, uInt8 is enough)
+ StringSubst(aSQL,"%e",(sInt32)aEntryType,2);
+ // now substitute standard: %f=folderkey, %u=userkey, %t=targetkey
+ DoSQLSubstitutions(aSQL);
+} // TODBCApiDS::DoMapSubstitutions
+
+
+#endif // HAS_SQL_ADMIN
+
+
+// Do the SQL substitutions common for all SQL statements
+void TODBCApiDS::DoSQLSubstitutions(string &aSQL)
+{
+ string::size_type i;
+
+ // let session substitute session level stuff first
+ fAgentP->DoSQLSubstitutions(aSQL);
+ StringSubst(aSQL,"%f",fFolderKey,2,fConfigP->sqlPrepCharSet(),fConfigP->fDataLineEndMode,fConfigP->fQuotingMode);
+ StringSubst(aSQL,"%t",fTargetKey,2,fConfigP->sqlPrepCharSet(),fConfigP->fDataLineEndMode,fConfigP->fQuotingMode);
+ // ID generator
+ // - new ID
+ i=0; while((i=aSQL.find("%X",i))!=string::npos) {
+ // - generate new one
+ nextLocalID(fConfigP->fSpecialIDMode,SQL_NULL_HANDLE);
+ // - use it
+ aSQL.replace(i,2,fLastGeneratedLocalID);
+ i+=fLastGeneratedLocalID.size();
+ }
+ // last used ID
+ StringSubst(aSQL,"%x",fLastGeneratedLocalID,2,fConfigP->sqlPrepCharSet(),fConfigP->fDataLineEndMode,fConfigP->fQuotingMode);
+} // TODBCApiDS::DoSQLSubstitutions
+
+
+
+
+typedef struct {
+ const char *substTag;
+ int substCode;
+} TSubstHandlerDef;
+
+typedef enum {
+ dsh_datakey,
+ dsh_datakey_outparam_string,
+ dsh_datakey_outparam_integer,
+ dsh_fieldnamelist,
+ dsh_fieldnamelist_a,
+ dsh_namevaluelist,
+ dsh_namevaluelist_a,
+ dsh_valuelist,
+ dsh_valuelist_a,
+ dsh_param,
+ dsh_field,
+ dsh_recordsize,
+ dsh_moddate,
+ dsh_modtime,
+ dsh_moddatetime,
+ dsh_andfilter,
+ dsh_wherefilter,
+ // number of enums
+ dsh_NUMENTRIES
+} TDataSubstHandlers;
+
+static TSubstHandlerDef DataSubstHandlers[dsh_NUMENTRIES] = {
+ { "k", dsh_datakey },
+ { "pkos", dsh_datakey_outparam_string },
+ { "pkoi", dsh_datakey_outparam_integer },
+ { "N", dsh_fieldnamelist },
+ { "aN", dsh_fieldnamelist_a },
+ { "V", dsh_namevaluelist },
+ { "aV", dsh_namevaluelist_a },
+ { "v", dsh_valuelist },
+ { "av", dsh_valuelist_a },
+ { "p(", dsh_param },
+ { "d(", dsh_field },
+ { "S", dsh_recordsize },
+ { "dM", dsh_moddate },
+ { "tM", dsh_modtime },
+ { "M", dsh_moddatetime },
+ { "AF", dsh_andfilter },
+ { "WF", dsh_wherefilter }
+};
+
+
+// do substitutions for data table access
+void TODBCApiDS::DoDataSubstitutions(
+ string &aSQL, // string to apply substitutions to
+ TFieldMapList &aFieldMapList, // field map list for %N,%V and %v
+ uInt16 aSetNo, // Map Set number
+ bool aForWrite, // set if read-enabled or write-enabled fields are shown in %N,%V and %v
+ bool aForUpdate, // set if for update (only assigned fields will be shown if !fUpdateAllFields)
+ TMultiFieldItem *aItemP, // item to read values and localid from
+ sInt16 aRepOffset, // array index/repeat offset
+ bool *aAllEmptyP, // true if all %V or %v empty
+ bool *aDoneP // true if no data at specified aRepOffset
+)
+{
+ string::size_type i,j,k,m,m2,n;
+ string s,s2;
+ string inStr;
+ int handlerid;
+
+ // default settings of flags
+ bool done=true; // default, in case %V or %v is missing (which could limit repetitions for arrays)
+ bool allempty=false; // not all empty unless we see that %V has all empty fields
+
+ // substitute one by one in the order the escape sequences appear in the SQL string
+ // (this is important for correct parameter mapping!!)
+ i=0;
+ inStr=aSQL;
+ aSQL.erase(); // will be rebuilt later
+ while((i=inStr.find("%",i))!=string::npos) {
+ // potential escape sequence found
+ // - search in table
+ for (handlerid=0; handlerid<dsh_NUMENTRIES; handlerid++) {
+ // - get compare length
+ n = strlen(DataSubstHandlers[handlerid].substTag);
+ if (strnncmp(inStr.c_str()+i+1,DataSubstHandlers[handlerid].substTag,n)==0) {
+ // found handler
+ // - i = index of % lead-in
+ n+=1;
+ // - n = size of matched sequence
+ s.erase();
+ // - s = replacement string. If set to non-empty, n chars at i will be replaced by it
+ // Now process
+ bool upc=false,lowc=false,asci=false;
+ TDBFieldType dbfty=dbft_numeric;
+ sInt16 ty,idx;
+ sInt32 sz=0;
+ sInt16 arrindex=0;
+ TItemField *fldP;
+ int substCode = DataSubstHandlers[handlerid].substCode;
+ DEBUGPRINTFX(DBG_DBAPI+DBG_EXOTIC,("- found %%%s (%ld chars)",DataSubstHandlers[substCode].substTag,(long)n));
+ switch (substCode) {
+ case dsh_datakey:
+ if (aItemP) s=aItemP->getLocalID();
+ break;
+ case dsh_datakey_outparam_string:
+ // generated key output (string key)
+ addSQLParameterMap(false,true,param_localid_str,NULL,aItemP,aRepOffset);
+ goto paramsubst;
+ case dsh_datakey_outparam_integer:
+ // generated key output (integer key)
+ addSQLParameterMap(false,true,param_localid_int,NULL,aItemP,aRepOffset);
+ goto paramsubst;
+ case dsh_fieldnamelist:
+ case dsh_fieldnamelist_a:
+ // %N and %aN = field name list
+ addFieldNameList(s,aForWrite,aForUpdate,substCode!=dsh_fieldnamelist_a && !fConfigP->fUpdateAllFields && aForUpdate && aItemP,aItemP,aFieldMapList,aSetNo);
+ break;
+ case dsh_namevaluelist:
+ case dsh_namevaluelist_a:
+ if (!aItemP) break; // needs an item
+ // %V,%aV = data field=value list
+ addFieldNameValueList(s,substCode!=dsh_namevaluelist_a && !fConfigP->fUpdateAllFields && aForUpdate,done,allempty,*aItemP,true,aFieldMapList,aSetNo,aRepOffset,allempty);
+ break;
+ case dsh_valuelist:
+ case dsh_valuelist_a:
+ if (!aItemP) break; // needs an item
+ // %v,%av = data value list
+ addFieldNameValueList(s,substCode!=dsh_valuelist_a && !fConfigP->fUpdateAllFields && aForUpdate,done,allempty,*aItemP,false,aFieldMapList,aSetNo,aRepOffset,allempty);
+ break;
+ case dsh_param:
+ // handle with global routine used in different substitution places
+ // i=start of % sequence, n=size of matched basic sequence = %+identifier+( = %p(
+ if (!fAgentP->ParseParamSubst(
+ inStr,i,n,
+ fParameterMaps,
+ aItemP
+ #ifdef SCRIPT_SUPPORT
+ ,fScriptContextP
+ #endif
+ )) break;
+ // i=start of % sequence, n=size of entire sequence including params and closing paranthesis
+ paramsubst:
+ s="?";
+ break;
+ case dsh_field:
+ // init options
+ upc=false;
+ lowc=false;
+ asci=false;
+ // skip lead-in
+ j=i+n;
+ // find closing paranthesis
+ k = inStr.find(")",j);
+ if (k==string::npos) { i=j; n=0; break; } // no closing paranthesis
+ m = k; // assume end of name is here
+ m2 = k;
+ // look for dbfieldtype
+ dbfty=dbft_numeric; // default to numeric (no quotes, to be compatible with old %d() definition)
+ m2 = inStr.find(",",j);
+ if (m2!=string::npos) {
+ // get dbfieldtype
+ if (StrToEnum(DBFieldTypeNames,numDBfieldTypes,ty,inStr.c_str()+m2+1,k-m2-1))
+ dbfty=(TDBFieldType)ty;
+ }
+ else
+ m2=k; // set end of name to closing paranthesis again
+ #ifdef ARRAYFIELD_SUPPORT
+ // also look for array index
+ m = inStr.find("#",j);
+ if (m!=string::npos && m<m2) {
+ // get array index
+ StrToShort(inStr.c_str()+m+1,arrindex);
+ }
+ else
+ m=m2; // set end of name to closing paranthesis or comma again
+ #endif
+ // extract options, if any
+ if (inStr[j]=='[') {
+ do {
+ j++;
+ if (inStr.size()<=j) break;
+ if (inStr[j]==']') { j++; break; }; // end of options
+ // check options
+ if (inStr[j]=='u') upc=true;
+ else if (inStr[j]=='l') lowc=true;
+ else if (inStr[j]=='a') asci=true;
+ } while(true);
+ }
+ // extract name (without eventual array index or dbfieldtype)
+ s.assign(inStr,j,m-j);
+ // find field
+ if (!aItemP) { i=k+1; n=0; break; } // no item, no action
+ #ifdef SCRIPT_SUPPORT
+ if (fScriptContextP) {
+ idx=fScriptContextP->getIdentifierIndex(OBJ_AUTO, aItemP->getFieldDefinitions(),s.c_str());
+ fldP=fScriptContextP->getFieldOrVar(aItemP,idx,arrindex);
+ }
+ else
+ #endif
+ fldP = aItemP->getArrayField(s.c_str(),arrindex,true);
+ if (!fldP) { i=k+1; n=0; break; } // field not found, no action
+ // produce DB literal
+ sz=0;
+ s.erase();
+ fAgentP->appendFieldValueLiteral(
+ *fldP,
+ dbfty,
+ 0, // unlimited
+ s,
+ asci ? chs_ascii : fConfigP->sqlPrepCharSet(),
+ fConfigP->fDataLineEndMode,
+ fConfigP->fQuotingMode,
+ fConfigP->fDataTimeZone,
+ sz
+ );
+ // apply options
+ if (upc) StringUpper(s);
+ else if (lowc) StringLower(s);
+ fRecordSize+=s.size();
+ // set substitution parameters
+ n=k+1-i;
+ break;
+ case dsh_recordsize:
+ if (aItemP) StringObjPrintf(s,"%ld",(long)fRecordSize);
+ break;
+ #ifdef ODBCAPI_SUPPORT
+ case dsh_moddate:
+ // %dM = modified date
+ fAgentP->lineartimeToODBCLiteralAppend(fCurrentSyncTime,s,true,false,TCTX_UTC,fConfigP->fDataTimeZone);
+ break;
+ case dsh_modtime:
+ // %tM = modified time
+ fAgentP->lineartimeToODBCLiteralAppend(fCurrentSyncTime,s,false,true,TCTX_UTC,fConfigP->fDataTimeZone);
+ break;
+ #endif // ODBCAPI_SUPPORT
+ case dsh_moddatetime:
+ // %M = modified datetimestamp
+ #ifdef SQLITE_SUPPORT
+ if (fUseSQLite) {
+ // integer timestamp modes
+ fAgentP->lineartimeToIntLiteralAppend(fCurrentSyncTime,s,fConfigP->fLastModDBFieldType,TCTX_UTC,fConfigP->fDataTimeZone);
+ }
+ #ifdef ODBCAPI_SUPPORT
+ else
+ #endif
+ #endif
+ #ifdef ODBCAPI_SUPPORT
+ {
+ if (fConfigP->fLastModDBFieldType==dbft_timestamp)
+ fAgentP->lineartimeToODBCLiteralAppend(fCurrentSyncTime,s,true,true,TCTX_UTC,fConfigP->fDataTimeZone);
+ else
+ fAgentP->lineartimeToIntLiteralAppend(fCurrentSyncTime,s,fConfigP->fLastModDBFieldType,TCTX_UTC,fConfigP->fDataTimeZone);
+ }
+ #endif // ODBCAPI_SUPPORT
+ ; // make sure we can't else out the break
+ break;
+ #ifdef OBJECT_FILTERING
+ case dsh_andfilter:
+ if (fFilterWorksOnDBLevel && fFilterExpressionTested)
+ appendFiltersClause(s,"AND");
+ break;
+ case dsh_wherefilter:
+ if (fFilterWorksOnDBLevel && fFilterExpressionTested)
+ appendFiltersClause(s,"WHERE");
+ break;
+ #endif
+ default:
+ i+=n; // continue parsing after unhandled tag
+ n=0; // no replacement
+ break;
+ } // switch
+ // apply substitution if any (n>0)
+ // - i=pos of %, n=size of text to be replaced, s=replacement string
+ // Note: if n==0, i must point to where parsing should continue
+ if (n>0) {
+ // get everything up to this % sequence
+ string intermediateStr;
+ intermediateStr.assign(inStr,0,i);
+ // have non-processed sequences processed for the other substitution possibilities now
+ // %f=folderkey, %u=userkey, %d=devicekey, %t=targetkey, %C=domain
+ // Note: it is important that %t and %d is checked here, after %d(), %tL and %tS above!!
+ DEBUGPRINTFX(DBG_DBAPI+DBG_EXOTIC,("Intermediate before applying basic substitutions: '%s'",intermediateStr.c_str()));
+ DoSQLSubstitutions(intermediateStr);
+ DEBUGPRINTFX(DBG_DBAPI+DBG_EXOTIC,("Intermediate after applying basic substitutions: '%s'",intermediateStr.c_str()));
+ // add it to the output string
+ aSQL+=intermediateStr;
+ // add the substitution (will NOT BE PROCESSED AGAIN!!)
+ aSQL+=s;
+ // have the rest of the inStr processed
+ inStr.erase(0,i+n); // remove already processed stuff and escape sequence now
+ // reset parsing position in inStr
+ i=0;
+ DEBUGPRINTFX(DBG_DBAPI+DBG_EXOTIC,("- substitution with '%s' done, SQL so far: '%s'",s.c_str(),aSQL.c_str()));
+ DEBUGPRINTFX(DBG_DBAPI+DBG_EXOTIC,("Remaining input string to process: '%s'",inStr.c_str()));
+ }
+ // sequence substitution done
+ break;
+ }
+ } // for
+ if (handlerid>=dsh_NUMENTRIES) {
+ // unknown sequence
+ // - check if this is an explicit literal % (two % in sequence)
+ if (inStr[i+1]=='%') {
+ // yes, replace %% by % in output
+ inStr.replace(i,2,"%");
+ i+=1;
+ }
+ else {
+ // no, just leave % in input as it is
+ i+=1;
+ }
+ }
+ } // while % found in string
+ // process rest of inStr (not containing any of the above sequences, but probably some from
+ // parent implementation
+ // have non-processed sequences processed for the other substitution possibilities now
+ // %f=folderkey, %u=userkey, %d=devicekey, %t=targetkey, %C=domain
+ // Note: it is important that %t and %d is checked here, after %d(), %tL and %tS above!!
+ DoSQLSubstitutions(inStr);
+ // Append end of inStr to output
+ aSQL+=inStr;
+ // return flags
+ if (aAllEmptyP) *aAllEmptyP=allempty;
+ if (aDoneP) *aDoneP=done;
+} // TODBCApiDS::DoDataSubstitutions
+
+
+
+// insert additional data selection conditions, if any. Returns true if something appended
+bool TODBCApiDS::appendFiltersClause(string &aSQL, const char *linktext)
+{
+ #ifndef OBJECT_FILTERING
+ return false; // no filters
+ #else
+
+ if (
+ (!fConfigP->fFilterOnDBLevel || (fLocalDBFilter.empty() && fSyncSetFilter.empty() && fConfigP->fInvisibleFilter.empty()))
+ #ifdef SCRIPT_SUPPORT
+ && fSQLFilter.empty()
+ #endif
+ )
+ return false; // no filter clause needed
+ // some conditions, add link text
+ if (linktext) { aSQL+=' '; aSQL+=linktext; aSQL+=' '; }
+ linktext=NULL; // link within paranthesis
+ #ifdef SCRIPT_SUPPORT
+ // - Explicit SQL filter
+ if (!fSQLFilter.empty()) {
+ if (linktext) aSQL+=linktext;
+ linktext=" AND ";
+ aSQL+='(';
+ aSQL+=fSQLFilter;
+ aSQL+=')';
+ }
+ #endif
+ // - hardcoded filter conditions
+ if (!fLocalDBFilter.empty()) {
+ if (linktext) aSQL+=linktext;
+ linktext=" AND ";
+ aSQL+='(';
+ if (!appendFilterConditions(aSQL,fLocalDBFilter))
+ throw TSyncException("<localdbfilter> incompatible with SQL database");
+ aSQL+=')';
+ }
+ // - filter invisible items
+ if (!fConfigP->fInvisibleFilter.empty()) {
+ if (linktext) aSQL+=linktext;
+ linktext=" AND ";
+ aSQL+="NOT (";
+ if (!appendFilterConditions(aSQL,fConfigP->fInvisibleFilter))
+ throw TSyncException("<invisiblefilter> incompatible with SQL database");
+ aSQL+=')';
+ }
+ // - sync set filter conditions
+ if (!fSyncSetFilter.empty()) {
+ if (linktext) aSQL+=linktext;
+ linktext=" AND ";
+ aSQL+='(';
+ if (!appendFilterConditions(aSQL,fSyncSetFilter))
+ throw TSyncException("sync set filter incompatible with SQL database");
+ aSQL+=')';
+ }
+ return true; // something appended
+ #endif // OBJECT_FILTERING
+} // TODBCApiDS::appendFiltersClause
+
+
+
+// reset all mapped parameters
+void TODBCApiDS::resetSQLParameterMaps(void)
+{
+ #ifdef SQLITE_SUPPORT
+ if (fUseSQLite) {
+ // resetting the parameter map finalizes any eventually running statement
+ if (fSQLiteStmtP) {
+ sqlite3_finalize(fSQLiteStmtP);
+ fSQLiteStmtP=NULL;
+ }
+ }
+ #endif
+ fAgentP->resetSQLParameterMaps(fParameterMaps);
+} // TODBCApiDS::resetSQLParameterMaps
+
+
+// add parameter definition to the datastore level parameter list
+void TODBCApiDS::addSQLParameterMap(
+ bool aInParam, bool aOutParam,
+ TParamMode aParamMode,
+ TODBCFieldMapItem *aFieldMapP,
+ TMultiFieldItem *aItemP,
+ sInt16 aRepOffset
+)
+{
+ TParameterMap map;
+
+ // assign basics
+ map.inparam=aInParam;
+ map.outparam=aOutParam;
+ map.parammode=aParamMode;
+ map.mybuffer=false;
+ map.ParameterValuePtr=NULL;
+ map.BufferLength=0;
+ map.StrLen_or_Ind=SQL_NULL_DATA; // note that this is not zero (but -1)
+ map.itemP=aItemP;
+ map.outSiz=NULL;
+ // get things from field map
+ if (aFieldMapP) {
+ map.fieldP=getMappedFieldOrVar(*aItemP,aFieldMapP->fid,aRepOffset);
+ map.maxSize=aFieldMapP->maxsize;
+ map.dbFieldType=aFieldMapP->dbfieldtype;
+ }
+ else {
+ map.fieldP=NULL;
+ map.maxSize=0;
+ map.dbFieldType=dbft_string;
+ }
+ // save in list
+ fParameterMaps.push_back(map);
+} // TODBCApiDS::addSQLParameterMap
+
+
+
+// prepare SQL statment as far as needed for parameter binding
+void TODBCApiDS::prepareSQLStatement(SQLHSTMT aStatement, cAppCharP aSQL, bool aForData, cAppCharP aComment)
+{
+ // show what statement will be executed
+ #ifdef SYDEBUG
+ if (aComment && aSQL && PDEBUGTEST(DBG_DBAPI)) {
+ PDEBUGPRINTFX(DBG_DBAPI,("SQL for %s:",aComment));
+ PDEBUGPUTSX(DBG_DBAPI,aSQL);
+ }
+ #endif
+
+ #ifdef SQLITE_SUPPORT
+ if (fUseSQLite && aForData) {
+ // bind params
+ fAgentP->prepareSQLiteStatement(
+ aSQL,
+ fSQLiteP,
+ fSQLiteStmtP
+ );
+ }
+ #endif
+} // TODBCApiDS::prepareSQLStatement
+
+
+// bind parameters (and values for IN-Params) to the statement
+void TODBCApiDS::bindSQLParameters(SQLHSTMT aStatement, bool aForData)
+{
+ #ifdef SQLITE_SUPPORT
+ if (fUseSQLite && aForData) {
+ // bind params
+ fAgentP->bindSQLiteParameters(
+ fSessionP,
+ fSQLiteStmtP,
+ fParameterMaps,
+ fConfigP->fDataCharSet,
+ fConfigP->fDataLineEndMode
+ );
+ }
+ else
+ #endif
+ {
+ #ifdef ODBCAPI_SUPPORT
+ fAgentP->bindSQLParameters(
+ fSessionP,
+ aStatement,
+ fParameterMaps,
+ fConfigP->fDataCharSet, // actual charset, including utf16, bindSQLiteParameters handles UTF16 case
+ fConfigP->fDataLineEndMode
+ );
+ #endif
+ }
+} // TODBCApiDS::bindSQLParameters
+
+
+// save out parameter values and clean up
+void TODBCApiDS::saveAndCleanupSQLParameters(
+ SQLHSTMT aStatement,
+ bool aForData
+)
+{
+ #ifdef SQLITE_SUPPORT
+ if (fUseSQLite && aForData) {
+ // NOP here because SQLite has no out params
+ return;
+ }
+ else
+ #endif
+ {
+ #ifdef ODBCAPI_SUPPORT
+ fAgentP->saveAndCleanupSQLParameters(
+ fSessionP,
+ aStatement,
+ fParameterMaps,
+ fConfigP->fDataCharSet, // actual charset, including utf16, saveAndCleanupSQLParameters handles UTF16 case
+ fConfigP->fDataLineEndMode
+ );
+ #endif
+ }
+} // TODBCApiDS::bindSQLParameters
+
+
+// Fetch next row from SQL statement, returns true if there is any
+bool TODBCApiDS::fetchNextRow(SQLHSTMT aStatement, bool aForData)
+{
+ #ifdef SQLITE_SUPPORT
+ if (fUseSQLite && aForData) {
+ int rc = fStepRc;
+ if (rc!=SQLITE_ROW) {
+ if (rc==SQLITE_OK) {
+ // last step was ok, we need to do next step first
+ rc = sqlite3_step(fSQLiteStmtP);
+ }
+ }
+ // clean up right now if we don't have data
+ if (rc!=SQLITE_ROW) {
+ rc=sqlite3_finalize(fSQLiteStmtP);
+ fStepRc = rc;
+ fSQLiteStmtP=NULL;
+ fAgentP->checkSQLiteError(rc,fSQLiteP);
+ return false; // no more data
+ }
+ else {
+ // we are reporting data now, make sure we fetch data in next call
+ fStepRc=SQLITE_OK;
+ }
+ // more data found
+ return true; // ok
+ }
+ else
+ #endif
+ #ifdef ODBCAPI_SUPPORT
+ {
+ if (aStatement==SQL_NULL_HANDLE)
+ return false; // no statement, nothing fetched
+ SQLRETURN res=SafeSQLFetch(aStatement);
+ // check for end
+ return fAgentP->checkStatementHasData(res,aStatement);
+ }
+ #else
+ return false; // no API, no result
+ #endif // ODBCAPI_SUPPORT
+} // TODBCApiDS::fetchNextRow
+
+
+
+
+
+// SQL statement complete, finalize it
+void TODBCApiDS::finalizeSQLStatement(SQLHSTMT aStatement, bool aForData)
+{
+ #ifdef SQLITE_SUPPORT
+ if (fUseSQLite && aForData) {
+ // finalize if not already done
+ if (fSQLiteStmtP) {
+ sqlite3_finalize(fSQLiteStmtP);
+ fSQLiteStmtP=NULL;
+ }
+ }
+ else
+ #endif
+ {
+ #ifdef ODBCAPI_SUPPORT
+ // just close cursor of the statement
+ SafeSQLCloseCursor(aStatement);
+ #endif // ODBCAPI_SUPPORT
+ }
+} // TODBCApiDS::prepareSQLStatement
+
+
+
+#ifdef OBJECT_FILTERING
+
+// - returns true if DB implementation can filter the standard filters
+// (LocalDBFilter, TargetFilter and InvisibleFilter) during database fetch
+// - otherwise, fetched items will be filtered after being read from DB.
+bool TODBCApiDS::dsFilteredFetchesFromDB(bool aFilterChanged)
+{
+ // can do filtering while fetching from DB, such that items that get (in)visible
+ // because of changed filter conditions are correctly detected as added/deleted
+ if (aFilterChanged || !fFilterExpressionTested) {
+ fFilterExpressionTested=true;
+ if (fConfigP->fFilterOnDBLevel) {
+ // try to dummy-append the filterclause to check if this will work
+ string s;
+ try {
+ appendFiltersClause(s,"");
+ fFilterWorksOnDBLevel=true;
+ }
+ catch(exception &e) {
+ PDEBUGPRINTFX(DBG_FILTER+DBG_HOT,("%s -> filter will be applied to fetched records",e.what()));
+ fFilterWorksOnDBLevel=false;
+ }
+ }
+ else
+ fFilterWorksOnDBLevel=false; // reject all DB level filtering
+ }
+ // if we can filter, that's sufficient
+ if (fFilterWorksOnDBLevel) return true;
+ // otherwise, let ancestor test
+ return inherited::dsFilteredFetchesFromDB(aFilterChanged);
+} // TODBCApiDS::dsfilteredFetchesFromDB
+
+
+// - appends logical condition to SQL from filter string
+bool TODBCApiDS::appendFilterConditions(string &aSQL, const string &aFilter)
+{
+ const char *p=aFilter.c_str();
+ return appendFilterTerm(aSQL,p,p+aFilter.size());
+} // TODBCApiDS::appendFilterConditions
+
+
+// - appends logical condition term to SQL from filter string
+bool TODBCApiDS::appendFilterTerm(string &aSQL, const char *&aPos, const char *aStop)
+{
+ char c=0;
+ const char *st;
+ string str,cmp,val;
+ bool result;
+ sInt16 fid;
+ bool specialValue;
+ bool caseInsensitive;
+
+ // determine max length
+ if (aStop==NULL) aStop=aPos+strlen(aPos);
+ // empty expression is ok
+ do {
+ result=true;
+ // process simple term (<ident><op><value>)
+ // - get first non-space
+ while (aPos<=aStop) {
+ c=*aPos;
+ if (c!=' ') break;
+ aPos++;
+ }
+ // Term starts here, first char is c, aPos points to it
+ // - check subexpression paranthesis
+ if (c=='(') {
+ // boolean term is grouped subexpression
+ aSQL+='(';
+ aPos++;
+ result=appendFilterTerm(aSQL,aPos,aStop);
+ // check if matching paranthesis
+ if (*(aPos++)!=')') {
+ PDEBUGPRINTFX(DBG_EXOTIC,("Filter expression syntax error at: %s",--aPos));
+ aPos=aStop; // skip rest
+ return false; // always fail
+ }
+ aSQL+=')';
+ }
+ else if (c==0) {
+ // empty term, is ok
+ return true;
+ }
+ else {
+ // must be simple boolean term
+ // - remember start of ident
+ st=aPos;
+ // - search end of ident
+ while (isFilterIdent(c)) c=*(++aPos);
+ // - c/aPos=char after ident, get ident
+ str.assign(st,aPos-st);
+ // - check for subscript index
+ uInt16 subsIndex=0; // no index (is 1-based in DS 1.2 filter specs)
+ if (c=='[') {
+ // expect numeric index
+ aPos++; // next
+ aPos+=StrToUShort(aPos,subsIndex);
+ if (*aPos!=']') {
+ PDEBUGPRINTFX(DBG_ERROR,("Filter expression error (missing \"]\") at: %s",--aPos));
+ return false; // syntax error, does not pass
+ }
+ c=*(++aPos); // process next after subscript
+ }
+ // - get operator
+ while (isspace(c)) c=*(++aPos);
+ if (c==':') c=*(++aPos); // ignore assign-to-make-true modifier
+ specialValue = c=='*'; // special-value modifier
+ if (specialValue) c=*(++aPos);
+ caseInsensitive = c=='^'; // case insensitive modifier
+ if (caseInsensitive) c=*(++aPos);
+ aPos++; // one char at least
+ cmp.erase();
+ bool cmplike=c=='%' || c=='$';
+ if (cmplike) cmp= (c=='%') ? " LIKE " : " NOT LIKE "; // "contains" special case, use SQL LIKE %val%
+ else {
+ cmp+=c; // simply use first char as is
+ if (c=='>' || c=='<') {
+ if (*aPos=='=' || *aPos=='>') {
+ c=*aPos++;
+ cmp+=c; // >=, <= and <>
+ }
+ }
+ }
+ // - get comparison value
+ while (isspace(c)) c=*(++aPos);
+ st=aPos; // should start here
+ while (aPos<aStop && *aPos!='&' && *aPos!='|' && *aPos!=')') aPos++;
+ // - assign value string
+ if (cmplike) val='%'; else val.erase();
+ val.append(st,aPos-st);
+ if (cmplike) val+='%';
+ // Now we have str=field identifier, cmp=operator, val=value string
+ // - check if directly addressing DB field
+ if (strucmp(str.c_str(),"D.",2)==0) {
+ // this directly refers to a DB field
+ // - translate for special values
+ if (specialValue) {
+ if (val=="N" || val=="E") {
+ val=" NULL";
+ caseInsensitive=false; // not needed for NULL
+ cmp="IS";
+ if (cmp!="=") cmp+=" NOT";
+ }
+ }
+ if (caseInsensitive) {
+ aSQL+="LOWER(";
+ aSQL.append(str.c_str()+2);
+ aSQL+=')';
+ aSQL+=cmp; // operator
+ aSQL+="LOWER(";
+ aSQL+=val;
+ aSQL+=')';
+ }
+ else {
+ aSQL.append(str.c_str()+2);
+ aSQL+=cmp; // operator
+ aSQL+=val; // value must be specified in DB syntax (including quotes for strings)
+ }
+ }
+ else {
+ // get field ID for that ident (can be -1 if none found)
+ TMultiFieldItemType *mfitP = dynamic_cast<TMultiFieldItemType *>(getLocalSendType());
+ if (mfitP)
+ fid=mfitP->getFilterIdentifierFieldIndex(str.c_str(),subsIndex);
+ else
+ return false; // field not known, bad syntax
+ if (fid==FID_NOT_SUPPORTED) return false; // unknown field
+ // search for map for that field id to obtain DB field name
+ TFieldMapList &fml = fConfigP->fFieldMappings.fFieldMapList;
+ TFieldMapList::iterator mainpos;
+ TFieldMapList::iterator pos;
+ bool mainfound=false;
+ for (pos=fml.begin(); pos!=fml.end(); pos++) {
+ // check field id, but search in setNo==0 only (fields of other sets are
+ // not available when SELECTing syncset.
+ if ((*pos)->setNo==0 && fid==(*pos)->fid) {
+ if ((*pos)->isArray()) return false; // array fields cannot be used in filters
+ // found
+ if (mainfound) break; // second match just breaks loop (time for date, eventually)
+ // main
+ mainpos=pos; // save position of main field
+ mainfound=true;
+ // search for further fields with same fid (possibly needed for timefordate)
+ }
+ }
+ if (!mainfound) {
+ return false; // no such field
+ PDEBUGPRINTFX(DBG_EXOTIC,("Could not find DB field for fid=%hd",fid));
+ }
+ // - translate for special values
+ if (specialValue) {
+ if ((*mainpos)->dbfieldtype==dbft_string && val=="E") {
+ // Empty is not NULL for strings
+ val="''";
+ }
+ else if (val=="N" || val=="E") {
+ // for other types, NULL and EMPTY are the same
+ val="NULL";
+ if (cmp!="=") cmp=" IS NOT ";
+ else cmp=" IS ";
+ }
+ // now append (no check for caseInsensitive needed as NULL check does not need it
+ aSQL+=(*mainpos)->fElementName;
+ aSQL+=cmp;
+ aSQL+=val;
+ }
+ else {
+ // create field to convert value string correctly
+ TItemField *valfldP = newItemField(
+ mfitP->getFieldDefinition(fid)->type,
+ getSessionZones()
+ );
+ // assign value as string
+ valfldP->setAsString(val.c_str());
+ // check for special case when timestamp is mapped to separate date/time fields
+ if (
+ pos!=fml.end() && ( // if there is another map with the same fid
+ ((*mainpos)->dbfieldtype==dbft_date && (*pos)->dbfieldtype==dbft_timefordate)
+ )
+ ) {
+ // separate date & time
+ aSQL+='(';
+ if (cmp=="=" || cmp=="<>") {
+ // (datefield=val AND timefield=val)
+ // (datefield<>val OR timefield<>val)
+ aSQL+=(*mainpos)->fElementName;
+ aSQL+=cmp;
+ appendFieldValueLiteral(*valfldP, (*mainpos)->dbfieldtype,(*mainpos)->maxsize, (*mainpos)->floating_ts, aSQL);
+ if (cmp=="=")
+ aSQL+=" AND ";
+ else
+ aSQL+=" OR ";
+ // - time field
+ aSQL+=(*pos)->fElementName;
+ aSQL+=cmp;
+ appendFieldValueLiteral(*valfldP, (*pos)->dbfieldtype,(*pos)->maxsize, (*mainpos)->floating_ts, aSQL);
+ }
+ else {
+ // (datefield >< val OR (datefield = val AND timefield >< val))
+ aSQL+=(*mainpos)->fElementName;
+ aSQL+=cmp;
+ appendFieldValueLiteral(*valfldP, (*mainpos)->dbfieldtype,(*mainpos)->maxsize, (*mainpos)->floating_ts, aSQL);
+ aSQL+=" OR (";
+ aSQL+=(*mainpos)->fElementName;
+ aSQL+='=';
+ appendFieldValueLiteral(*valfldP, (*mainpos)->dbfieldtype,(*mainpos)->maxsize, (*mainpos)->floating_ts, aSQL);
+ aSQL+=" AND ";
+ // - time field
+ aSQL+=(*pos)->fElementName;
+ aSQL+=cmp;
+ appendFieldValueLiteral(*valfldP, (*pos)->dbfieldtype,(*pos)->maxsize, (*mainpos)->floating_ts, aSQL);
+ aSQL+=')';
+ }
+ aSQL+=')';
+ }
+ else {
+ // standard case
+ // field ><= val
+ if (caseInsensitive) {
+ aSQL+="LOWER(";
+ aSQL+=(*mainpos)->fElementName;
+ aSQL+=')';
+ aSQL+=cmp; // operator
+ aSQL+="LOWER(";
+ appendFieldValueLiteral(*valfldP, (*mainpos)->dbfieldtype,(*mainpos)->maxsize, (*mainpos)->floating_ts, aSQL);
+ aSQL+=')';
+ }
+ else {
+ aSQL+=(*mainpos)->fElementName;
+ aSQL+=cmp;
+ appendFieldValueLiteral(*valfldP, (*mainpos)->dbfieldtype,(*mainpos)->maxsize, (*mainpos)->floating_ts, aSQL);
+ }
+ }
+ // field can be deleted now
+ delete valfldP;
+ } // not special value
+ } // refers to field
+ } // simple boolean term
+ // now check logical operators
+ // - skip spaces
+ c=*aPos++;
+ while (c==' ') c=*(++aPos);
+ // - check char at aPos
+ if (c=='|') aSQL+=" OR ";
+ else if (c=='&') aSQL+= " AND ";
+ else {
+ aPos--; // let caller check it
+ break; // end of logical term
+ }
+ } while (true); // process terms until all done
+ // conversion ok
+ return true;
+} // TODBCApiDS::appendFilterTerm
+
+
+#endif // OBJECT_FILTERING
+
+
+#ifdef ODBCAPI_SUPPORT
+
+// get one or two successive columns as time stamp depending on config
+void TODBCApiDS::getColumnsAsTimestamp(
+ SQLHSTMT aStatement,
+ sInt16 &aColNumber, // will be updated by 1 or 2
+ bool aCombined,
+ lineartime_t &aTimestamp,
+ timecontext_t aTargetContext
+)
+{
+ lineartime_t t;
+
+ if (aCombined) {
+ // combined date/time
+ fAgentP->getColumnValueAsTimestamp(aStatement,aColNumber++,aTimestamp);
+ }
+ else {
+ // date, then time
+ fAgentP->getColumnValueAsDate(aStatement,aColNumber++,aTimestamp);
+ fAgentP->getColumnValueAsTime(aStatement,aColNumber++,t);
+ aTimestamp+=t; // add time to date
+ }
+ // convert to target zone requested
+ if (!TCTX_IS_UNKNOWN(aTargetContext)) {
+ TzConvertTimestamp(aTimestamp,fConfigP->fDataTimeZone,aTargetContext,getSessionZones(),TCTX_UNKNOWN);
+ }
+} // TODBCApiDS::getColumnsAsTimestamp
+
+#endif // ODBCAPI_SUPPORT
+
+
+// - get a column as integer based timestamp
+lineartime_t TODBCApiDS::dbIntToLineartimeAs(
+ sInt64 aDBInt, TDBFieldType aDbfty,
+ timecontext_t aTargetContext
+)
+{
+ lineartime_t ts = dbIntToLineartime(aDBInt, aDbfty);
+ // convert to target zone requested
+ if (!TCTX_IS_UNKNOWN(aTargetContext)) {
+ TzConvertTimestamp(ts,fConfigP->fDataTimeZone,aTargetContext,getSessionZones(),TCTX_UNKNOWN);
+ }
+ return ts;
+} // TODBCApiDS::dbIntToLineartimeAs
+
+
+
+// create local ID with special algorithm
+void TODBCApiDS::createLocalID(string &aLocalID,TSpecialIDMode aSpecialIDMode)
+{
+ lineartime_t unixms;
+ switch (aSpecialIDMode) {
+ case sidm_unixmsrnd6:
+ // ID is generated by UNIX time in milliseconds, with 6 digits of random
+ // appended.
+ // - get Unix time in milliseconds:
+ unixms =
+ (getSession()->getSystemNowAs(TCTX_SYSTEM)-UnixToLineartimeOffset) // unix time in lineartime_t units
+ *(1000/secondToLinearTimeFactor); // convert into ms
+ unixms*=1000000; // room for 6 more digits
+ // - add random number between 0 and 999999
+ unixms+=(sInt32)rand()*1000000/RAND_MAX;
+ // - make numeric ID out of this
+ StringObjPrintf(aLocalID,"%lld",(long long)unixms);
+ break;
+ default:
+ aLocalID="<error>";
+ }
+} // TODBCApiDS::createLocalID
+
+
+#ifdef HAS_SQL_ADMIN
+
+// update sync target
+localstatus TODBCApiDS::updateSyncTarget(SQLHSTMT aStatement, bool aSessionFinished)
+{
+ string sql,s;
+ localstatus sta=LOCERR_OK;
+
+ resetSQLParameterMaps();
+ sql = fConfigP->fUpdateSyncTargetSQL;
+ sInt32 i;
+ // Suspend/Resume
+ // - alert code for next resume
+ StringSubst(sql,"%SUA",fResumeAlertCode,4);
+ // - suspend reference time
+ i=0; while((i=sql.find("%dSU",i))!=string::npos) {
+ s.erase();
+ fAgentP->lineartimeToLiteralAppend(fPreviousSuspendCmpRef, s, true, false, TCTX_UTC, fConfigP->fDataTimeZone);
+ sql.replace(i,4,s); i+=s.size();
+ }
+ // - suspend reference time
+ i=0; while((i=sql.find("%tSU",i))!=string::npos) {
+ s.erase();
+ fAgentP->lineartimeToLiteralAppend(fPreviousSuspendCmpRef, s, false, true, TCTX_UTC, fConfigP->fDataTimeZone);
+ sql.replace(i,4,s); i+=s.size();
+ }
+ // - suspend reference time
+ i=0; while((i=sql.find("%SU",i))!=string::npos) {
+ s.erase();
+ fAgentP->lineartimeToLiteralAppend(fPreviousSuspendCmpRef, s, true, true, TCTX_UTC, fConfigP->fDataTimeZone);
+ sql.replace(i,3,s); i+=s.size();
+ }
+ // - last suspend identifier (for derived datastores that might need another token than time)
+ StringSubst(sql,"%iSU",fPreviousSuspendIdentifier,4,fConfigP->sqlPrepCharSet(),fConfigP->fDataLineEndMode,fConfigP->fQuotingMode);
+ // - anchor
+ StringSubst(sql,"%A",fLastRemoteAnchor,2,fConfigP->sqlPrepCharSet(),fConfigP->fDataLineEndMode,fConfigP->fQuotingMode);
+ // Suspend/Resume in mid-chunk
+ // - lastitem
+ StringSubst(sql,"%pSU",fLastSourceURI,4,fConfigP->sqlPrepCharSet(),fConfigP->fDataLineEndMode,fConfigP->fQuotingMode);
+ StringSubst(sql,"%pTU",fLastTargetURI,4,fConfigP->sqlPrepCharSet(),fConfigP->fDataLineEndMode,fConfigP->fQuotingMode);
+ // - lastitemstatus
+ StringSubst(sql,"%pSt",fLastItemStatus,4);
+ // - partial item State/Mode
+ long pista;
+ if (fPartialItemState==pi_state_save_incoming)
+ pista=(long)pi_state_loaded_incoming;
+ else if (fPartialItemState==pi_state_save_outgoing)
+ pista=(long)pi_state_loaded_outgoing;
+ else
+ pista=(long)pi_state_none;
+ StringSubst(sql,"%pM",pista,3);
+ // - size info
+ StringSubst(sql,"%pTS",fPITotalSize,4);
+ StringSubst(sql,"%pUS",fPIUnconfirmedSize,4);
+ StringSubst(sql,"%pSS",fPIStoredSize,4);
+ // - buffered data
+ i=0; while((i=sql.find("%pDAT",i))!=string::npos) {
+ sql.replace(i,5,"?"); i+=1;
+ // now bind data to param
+ TParameterMap map;
+ map.inparam=true;
+ map.outparam=false;
+ map.parammode=param_buffer;
+ map.outSiz=NULL;
+ map.dbFieldType=dbft_blob;
+ // - pass the buffer
+ map.mybuffer=false; // not owned by ODBC api
+ if (fPIStoredDataP) {
+ map.BufferLength=fPIStoredSize;
+ map.StrLen_or_Ind=fPIStoredSize;
+ map.ParameterValuePtr=fPIStoredDataP; // the BLOB data to store
+ }
+ else {
+ map.BufferLength=0;
+ map.StrLen_or_Ind=SQL_NULL_DATA; // no data
+ map.ParameterValuePtr=NULL;
+ }
+ // save in list
+ fParameterMaps.push_back(map);
+ }
+ // - last to remote sync reference date
+ // Note: If we DON'T HAVE fSyncTimeStampAtEnd, but HAVE fOneWayFromRemoteSupported,
+ // we MUST NOT store the reference time here, but save save session start time.
+ // The reference time will be saved under %dRS
+ lineartime_t dltime;
+ if (!fConfigP->fSyncTimeStampAtEnd && fConfigP->fOneWayFromRemoteSupported)
+ dltime = fPreviousSyncTime;
+ else
+ dltime = fPreviousToRemoteSyncCmpRef;
+ i=0; while((i=sql.find("%dL",i))!=string::npos) {
+ s.erase();
+ fAgentP->lineartimeToLiteralAppend(dltime, s, true, false, TCTX_UTC, fConfigP->fDataTimeZone);
+ sql.replace(i,3,s); i+=s.size();
+ }
+ // - last to remote sync reference time
+ i=0; while((i=sql.find("%tL",i))!=string::npos) {
+ s.erase();
+ fAgentP->lineartimeToLiteralAppend(dltime, s, false, true, TCTX_UTC, fConfigP->fDataTimeZone);
+ sql.replace(i,3,s); i+=s.size();
+ }
+ // - last to remote sync reference date/timestamp
+ i=0; while((i=sql.find("%L",i))!=string::npos) {
+ s.erase();
+ fAgentP->lineartimeToLiteralAppend(dltime, s, true, true, TCTX_UTC, fConfigP->fDataTimeZone);
+ sql.replace(i,2,s); i+=s.size();
+ }
+ // - last sync session start date
+ i=0; while((i=sql.find("%dS",i))!=string::npos) {
+ s.erase();
+ fAgentP->lineartimeToLiteralAppend(fPreviousSyncTime, s, true, false, TCTX_UTC, fConfigP->fDataTimeZone);
+ sql.replace(i,3,s); i+=s.size();
+ }
+ // - last sync session start time
+ i=0; while((i=sql.find("%tS",i))!=string::npos) {
+ s.erase();
+ fAgentP->lineartimeToLiteralAppend(fPreviousSyncTime, s, false, true, TCTX_UTC, fConfigP->fDataTimeZone);
+ sql.replace(i,3,s); i+=s.size();
+ }
+ // - last sync session start date/timestamp
+ i=0; while((i=sql.find("%S",i))!=string::npos) {
+ s.erase();
+ fAgentP->lineartimeToLiteralAppend(fPreviousSyncTime, s, true, true, TCTX_UTC, fConfigP->fDataTimeZone);
+ sql.replace(i,2,s); i+=s.size();
+ }
+ // - last sync with data to remote date
+ i=0; while((i=sql.find("%dRL",i))!=string::npos) {
+ s.erase();
+ fAgentP->lineartimeToLiteralAppend(fPreviousToRemoteSyncCmpRef, s, true, false, TCTX_UTC, fConfigP->fDataTimeZone);
+ sql.replace(i,4,s); i+=s.size();
+ }
+ // - last sync with data to remote time
+ i=0; while((i=sql.find("%tRL",i))!=string::npos) {
+ s.erase();
+ fAgentP->lineartimeToLiteralAppend(fPreviousToRemoteSyncCmpRef, s, false, true, TCTX_UTC, fConfigP->fDataTimeZone);
+ sql.replace(i,4,s); i+=s.size();
+ }
+ // - last sync with data to remote date/timestamp
+ i=0; while((i=sql.find("%RL",i))!=string::npos) {
+ s.erase();
+ fAgentP->lineartimeToLiteralAppend(fPreviousToRemoteSyncCmpRef, s, true, true, TCTX_UTC, fConfigP->fDataTimeZone);
+ sql.replace(i,3,s); i+=s.size();
+ }
+ // - last sync with data to remote identifier (for derived datastores that might need another token than time)
+ StringSubst(sql,"%iRL",fPreviousToRemoteSyncIdentifier,4,fConfigP->sqlPrepCharSet(),fConfigP->fDataLineEndMode,fConfigP->fQuotingMode);
+ // - last anchor for sync with data to remote date
+ // Note: this is only needed if we DON'T HAVE fSyncTimeStampAtEnd, but HAVE fOneWayFromRemoteSupported,
+ // because then the %L cannot be used as reference time (it must save session time)
+ i=0; while((i=sql.find("%dRS",i))!=string::npos) {
+ s.erase();
+ fAgentP->lineartimeToLiteralAppend(fPreviousToRemoteSyncCmpRef, s, true, false, TCTX_UTC, fConfigP->fDataTimeZone);
+ sql.replace(i,4,s); i+=s.size();
+ }
+ // - last server anchor with data to remote time
+ i=0; while((i=sql.find("%tRS",i))!=string::npos) {
+ s.erase();
+ fAgentP->lineartimeToLiteralAppend(fPreviousToRemoteSyncCmpRef, s, false, true, TCTX_UTC, fConfigP->fDataTimeZone);
+ sql.replace(i,4,s); i+=s.size();
+ }
+ // - last server anchor with data to remote date/timestamp
+ i=0; while((i=sql.find("%RS",i))!=string::npos) {
+ s.erase();
+ fAgentP->lineartimeToLiteralAppend(fPreviousToRemoteSyncCmpRef, s, true, true, TCTX_UTC, fConfigP->fDataTimeZone);
+ sql.replace(i,3,s); i+=s.size();
+ }
+ // now substitute standard: %f=folderkey, %u=userkey, %d=devicekey, %t=targetkey
+ // - Note: it is important that %t,%d is checked here, after %dL, %dS, %tL and %tS above!!
+ DoSQLSubstitutions(sql);
+ // - bind eventual params
+ bindSQLParameters(aStatement,false);
+ // - issue
+ execSQLStatement(aStatement,sql,false,"updating anchor/lastsync",false);
+ return LOCERR_OK;
+} // TODBCApiDS::updateSyncTarget
+
+
+
+// update map changes from memory list into actual map table
+localstatus TODBCApiDS::updateODBCMap(SQLHSTMT aStatement, bool aSessionFinishedSuccessfully)
+{
+ string sql,s;
+ localstatus sta=LOCERR_OK;
+ TMapContainer::iterator pos;
+
+ // now save the entire list differentially
+ pos=fMapTable.begin();
+ DEBUGPRINTFX(DBG_ADMIN+DBG_EXOTIC,("updateODBCMap: internal map table has %ld entries (normal and others)",fMapTable.size()));
+ while (pos!=fMapTable.end()) {
+ DEBUGPRINTFX(DBG_ADMIN+DBG_EXOTIC,(
+ "updateODBCMap: entryType=%s, localid='%s', remoteID='%s', mapflags=0x%lX, changed=%d, deleted=%d, added=%d, markforresume=%d, savedmark=%d",
+ MapEntryTypeNames[(*pos).entrytype],
+ (*pos).localid.c_str(),
+ (*pos).remoteid.c_str(),
+ (*pos).mapflags,
+ (int)(*pos).changed,
+ (int)(*pos).deleted,
+ (int)(*pos).added,
+ (int)(*pos).markforresume,
+ (int)(*pos).savedmark
+ ));
+ try {
+ // check if item has changed since map table was read, or if its markforresume has changed
+ // or if this is a successful end of a session, when we can safely assume that any pending maps
+ // are from adds to the client that have never reached the client (otherwise, we'd have got
+ // a map for it, even if the add was in a previous session or session attempt)
+ if (
+ (*pos).changed || (*pos).added || (*pos).deleted || // update of DB needed
+ ((*pos).markforresume!=(*pos).savedmark) // mark for resume changed
+ ) {
+ // make sure it does not get written again if not really modified again
+ (*pos).changed=false;
+ // update new mapflags w/o changing mapflag_useforresume in the actual flags (as we still need it while session goes on)
+ uInt32 newmapflags = (*pos).mapflags & ~mapflag_useforresume;
+ if ((*pos).markforresume)
+ newmapflags |= mapflag_useforresume;
+ // remember last saved state
+ (*pos).savedmark=(*pos).markforresume;
+ // do something!
+ if ((*pos).deleted) {
+ if (!(*pos).added) {
+ // delete this entry (only needed if it was not also added since last save - otherwise, map entry was never saved to the DB yet)
+ IssueMapSQL(aStatement,fConfigP->fMapDeleteSQL,"deleting a map entry",(*pos).entrytype,(*pos).localid.c_str(),NULL,newmapflags);
+ }
+ // now remove it from the list, such that we don't try to delete it again
+ TMapContainer::iterator delpos=pos++; // that's the next to have a look at
+ fMapTable.erase(delpos); // remove it now
+ continue; // pos is already updated
+ } // deleted
+ else if ((*pos).added) {
+ // add a new entry
+ IssueMapSQL(aStatement,fConfigP->fMapInsertSQL,"inserting a map entry",(*pos).entrytype,(*pos).localid.c_str(),(*pos).remoteid.c_str(),newmapflags);
+ // is now added, don't add again later
+ (*pos).added=false;
+ }
+ else {
+ // explicitly changed or needs update because of resume mark or pendingmap flag
+ // change existing entry
+ IssueMapSQL(aStatement,fConfigP->fMapUpdateSQL,"changing a map entry",(*pos).entrytype,(*pos).localid.c_str(),(*pos).remoteid.c_str(),newmapflags);
+ }
+ } // if something changed
+ // anyway - reset mark for resume, it must be reconstructed before next save
+ (*pos).markforresume=false;
+ // next
+ pos++;
+ }
+ // catch exceptions, but nevertheless continue writing
+ catch (exception &e) {
+ PDEBUGPRINTFX(DBG_ERROR,("******** TODBCApiDS::updateODBCMap exception: %s",e.what()));
+ sta=510;
+ break;
+ }
+ catch (...) {
+ DEBUGPRINTFX(DBG_ERROR,("******** TODBCApiDS::updateODBCMap unknown exception"));
+ sta=510;
+ break;
+ }
+ } // while
+ return sta;
+} // TODBCApiDS::updateMap
+
+
+#endif // HAS_SQL_ADMIN
+
+
+// called when message processing
+void TODBCApiDS::dsEndOfMessage(void)
+{
+ string msg,state;
+
+ #ifdef ODBCAPI_SUPPORT
+ SQLRETURN res;
+ // commit data if not anyway committed at end of every item
+ if (
+ fODBCConnectionHandle!=SQL_NULL_HANDLE &&
+ !fConfigP->fCommitItems
+ ) {
+ DEBUGPRINTFX(DBG_DATA+DBG_DBAPI,("dsEndOfMessage - committing transactions at end of message"));
+ res=SafeSQLEndTran(SQL_HANDLE_DBC,getODBCConnectionHandle(),SQL_COMMIT);
+ if (fAgentP->getODBCError(res,msg,state,SQL_HANDLE_DBC,getODBCConnectionHandle())) {
+ DEBUGPRINTFX(DBG_ERROR,("dsEndOfMessage: SQLEndTran failed: %s",msg.c_str()));
+ }
+ }
+ #endif // ODBCAPI_SUPPORT
+
+ // let ancestor do things
+ inherited::dsEndOfMessage();
+} // TODBCApiDS::dsEndOfMessage
+
+
+
+// Simple DB API access interface methods
+
+
+// Zap all data in syncset (note that everything outside the sync set will remain intact)
+localstatus TODBCApiDS::apiZapSyncSet(void)
+{
+ localstatus sta = LOCERR_OK;
+
+ try {
+ string sql=fConfigP->fDataZapSQL;
+ if (sql.empty()) {
+ // we have no statement that can zap everything at once, so we'll have to do
+ // use generic one by one syncset deletion
+ sta = zapSyncSet();
+ }
+ else {
+ // we have SQL statement(s) for zapping, use it
+ sInt16 i=0;
+ uInt16 setno;
+ while (getNextSQLStatement(fConfigP->fDataZapSQL,i,sql,setno)) {
+ DoDataSubstitutions(sql,fConfigP->fFieldMappings.fFieldMapList,0,false,false);
+ // - issue
+ prepareSQLStatement(fODBCWriteStatement, sql.c_str(), true, "slow-refresh from remote: deleting all records in sync set first");
+ execSQLStatement(fODBCWriteStatement, sql, true, NULL, true);
+ finalizeSQLStatement(fODBCWriteStatement, true);
+ } // while more statements
+ }
+ }
+ catch (exception &e) {
+ PDEBUGPRINTFX(DBG_ERROR,("apiZapSyncSet exception: %s",e.what()));
+ return 510;
+ }
+ // done
+ return sta;
+} // TODBCApiDS::apiZapSyncSet
+
+
+// read sync set IDs and mod dates.
+// - If aNeedAll is set, all data fields are needed, so apiReadSyncSet MAY
+// read items here already. Note that apiReadSyncSet MAY read items here
+// even if aNeedAll is not set (if it is more efficient than reading
+// them separately afterwards).
+localstatus TODBCApiDS::apiReadSyncSet(bool aNeedAll)
+{
+ string sql;
+ localstatus sta = LOCERR_OK;
+
+ // get field map list
+ TFieldMapList &fml = fConfigP->fFieldMappings.fFieldMapList;
+ #ifdef SQLITE_SUPPORT
+ // start reading
+ if (fUseSQLite) {
+ // open the SQLite file
+ PDEBUGPRINTFX(DBG_DBAPI,("Opening SQLite3 file '%s'",fConfigP->fSQLiteFileName.c_str()));
+ int rc = sqlite3_open(fConfigP->fSQLiteFileName.c_str(), &fSQLiteP);
+ fAgentP->checkSQLiteError(rc,fSQLiteP);
+ // set the database (lock) timeout
+ rc = sqlite3_busy_timeout(fSQLiteP, fConfigP->fSQLiteBusyTimeout*1000);
+ fAgentP->checkSQLiteError(rc,fSQLiteP);
+ }
+ else
+ #endif
+ #ifdef ODBCAPI_SUPPORT
+ {
+ fODBCReadStatement = fAgentP->newStatementHandle(getODBCConnectionHandle());
+ }
+ #else
+ return 510; // no API -> DB error
+ #endif // ODBCAPI_SUPPORT
+ #ifdef SCRIPT_SUPPORT
+ // process mappings init script
+ fWriting=false;
+ fInserting=false;
+ fDeleting=false;
+ fAgentP->fScriptContextDatastore=this;
+ if (!TScriptContext::executeTest(true,fScriptContextP,fConfigP->fFieldMappings.fInitScript,fConfigP->getDSFuncTableP(),fAgentP))
+ throw TSyncException("<initscript> failed");
+ #endif
+ // read list of all local IDs that are in the current sync set
+ DeleteSyncSet();
+ #ifdef SYDEBUG
+ string ts;
+ StringObjTimestamp(ts,getPreviousToRemoteSyncCmpRef());
+ PDEBUGPRINTFX(DBG_DATA,(
+ "Now reading local sync set: last sync to remote was at %s",
+ ts.c_str()
+ ));
+ #endif
+ // we don't need to load the syncset if we are only refreshing from remote
+ // but we also must load it if we can't zap without it on slow refresh
+ // or if the syncset is needed to retrieve items
+ if (!fRefreshOnly || (fSlowSync && apiNeedSyncSetToZap()) || implNeedSyncSetToRetrieve()) {
+ // %%% add checking for aNeedAll and decide to use another (tbd) SQL to get all
+ // records with all fields with this single query
+ // - SELECT localid,modifieddate(,modifiedtime) FROM data WHERE <folderkey, filter conds, other conds>
+ // NOTE: this must always be a SINGLE statement (no %GO() are allowed) !
+ // Expects 2 or 3 (when date&time are separate) columns: localid,modifieddate(,modifiedtime)
+ sql=fConfigP->fLocalIDAndTimestampFetchSQL;
+ DoDataSubstitutions(sql,fml,0,false);
+ // - prepare
+ prepareSQLStatement(fODBCReadStatement, sql.c_str(), true, "reading of all localIDs/moddates in sync set");
+ // - issue
+ execSQLStatement(fODBCReadStatement, sql, true, NULL, true);
+ // - fetch data
+ while (fetchNextRow(fODBCReadStatement, true)) {
+ // get local ID and mod date
+ TSyncSetItem *syncsetitemP = new TSyncSetItem;
+ if (!syncsetitemP) throw TSyncException(DEBUGTEXT("cannot allocate new syncsetitem","odds12"));
+ syncsetitemP->isModified=false;
+ syncsetitemP->isModifiedAfterSuspend=false;
+ lineartime_t lastmodified = 0;
+ #ifdef SQLITE_SUPPORT
+ if (fUseSQLite) {
+ // SQLite
+ sInt16 col=0; // SQLite has 0 based column index
+ // - localid
+ syncsetitemP->localid = (const char *)sqlite3_column_text(fSQLiteStmtP,col++);
+ // - modified timestamp
+ lastmodified = dbIntToLineartimeAs(sqlite3_column_int64(fSQLiteStmtP,col++), fConfigP->fLastModDBFieldType, TCTX_UTC);
+ }
+ else
+ #endif
+ {
+ #ifdef ODBCAPI_SUPPORT
+ // ODBC
+ sInt16 col=1; // ODBC has 1 based column index
+ fAgentP->getColumnValueAsString(fODBCReadStatement, col++, syncsetitemP->localid, chs_ascii);
+ // get modified timestamp
+ if (fConfigP->fLastModDBFieldType==dbft_timestamp)
+ getColumnsAsTimestamp(fODBCReadStatement, col, fConfigP->fModifiedTimestamp, lastmodified, TCTX_UTC);
+ else {
+ uInt32 u;
+ fAgentP->getColumnValueAsULong(fODBCReadStatement, col, u);
+ lastmodified = dbIntToLineartimeAs(u, fConfigP->fLastModDBFieldType, TCTX_UTC);
+ }
+ #endif // ODBCAPI_SUPPORT
+ }
+ // compare now
+ syncsetitemP->isModified = lastmodified > getPreviousToRemoteSyncCmpRef();
+ syncsetitemP->isModifiedAfterSuspend = lastmodified > getPreviousSuspendCmpRef();
+ #ifdef SYDEBUG
+ StringObjTimestamp(ts,lastmodified);
+ PDEBUGPRINTFX(DBG_DATA+DBG_EXOTIC,(
+ "read local item info in sync set: localid='%s', last modified %s%s%s",
+ syncsetitemP->localid.c_str(),
+ ts.c_str(),
+ syncsetitemP->isModified ? " -> MODIFIED since last sync" : "",
+ syncsetitemP->isModifiedAfterSuspend ? " AND since last suspend" : ""
+ ));
+ #endif
+ // %%% for now, we do not read item contents yet
+ syncsetitemP->itemP=NULL; // no item data
+ // save ID in list
+ fSyncSetList.push_back(syncsetitemP);
+ }
+ // - no more records
+ finalizeSQLStatement(fODBCReadStatement, true);
+ } // not refreshing
+ PDEBUGPRINTFX(DBG_DATA,(
+ "Fetched %ld items from database (not necessarily all visible in SyncSet!)",
+ (long)fSyncSetList.size()
+ ));
+ return sta;
+} // TODBCApiDS::apiReadSyncSet
+
+
+// fetch actual record from DB by localID
+localstatus TODBCApiDS::apiFetchItem(TMultiFieldItem &aItem, bool aReadPhase, TSyncSetItem *aSyncSetItemP)
+{
+ // decide what statement to use
+ SQLHSTMT statement = SQL_NULL_HANDLE;
+ #ifdef SQLITE_SUPPORT
+ if (!fUseSQLite)
+ #endif
+ {
+ #ifdef ODBCAPI_SUPPORT
+ if (aReadPhase)
+ statement = fODBCReadStatement; // we can reuse the statement that is already here
+ else
+ statement = fAgentP->newStatementHandle(getODBCConnectionHandle());
+ #endif // ODBCAPI_SUPPORT
+ }
+ // get field map list
+ TFieldMapList &fml = fConfigP->fFieldMappings.fFieldMapList;
+ // assume ok
+ localstatus sta=LOCERR_OK;
+ // now fetch
+ try {
+ string sql;
+ TMultiFieldItem *myitemP = (TMultiFieldItem *)&aItem;
+ // execute statements needed to fetch record data
+ sInt16 i=0;
+ uInt16 setno;
+ while (getNextSQLStatement(fConfigP->fDataFetchSQL,i,sql,setno)) {
+ // - something like: SELECT %N FROM datatable WHERE localid=%k AND folderkey=%f
+ resetSQLParameterMaps();
+ DoDataSubstitutions(sql,fml,setno,false,false,myitemP); // item needed for %k
+ // - prepare parameters
+ prepareSQLStatement(statement, sql.c_str(), true, "getting item data");
+ bindSQLParameters(statement,true);
+ // - issue
+ execSQLStatement(statement,sql,true,NULL,true);
+ // fetch
+ if (!fetchNextRow(statement,true)) {
+ // - close cursor anyway
+ finalizeSQLStatement(statement,true);
+ // No data for select statement
+ // This is ignored for all setno except setno=0
+ if (setno==0) {
+ // this record cannot be found
+ sta=404; // not found
+ break; // no need to execute further statements
+ } // setno==0, that is, non-optional data
+ // if optional data is not present, just NOP
+ } // has no data
+ else {
+ // - fill item with fields from SQL query
+ sInt16 col=1;
+ fillFieldsFromSQLResult(statement,col,*myitemP,fml,setno,0);
+ // close cursor of main fetch
+ finalizeSQLStatement(statement,true);
+ // get out params
+ saveAndCleanupSQLParameters(statement,true);
+ }
+ } // while more statements
+ // Finish reading record (if one fetched at all, and not converted to delete etc.)
+ if (sta==LOCERR_OK) {
+ #ifdef ARRAYDBTABLES_SUPPORT
+ // - also read array fields from auxiliary tables, if any
+ if (fHasArrayFields) {
+ // get data from linked array tables as well
+ readArrayFields(
+ statement,
+ *myitemP,
+ fml
+ );
+ }
+ #endif
+ #ifdef SCRIPT_SUPPORT
+ // - finally process afterread script of entire record
+ fArrIdx=0; // base item
+ fParentKey=myitemP->getLocalID();
+ fAgentP->fScriptContextDatastore=this;
+ if (!TScriptContext::execute(fScriptContextP,fConfigP->fFieldMappings.fAfterReadScript,fConfigP->getDSFuncTableP(),fAgentP,myitemP,true))
+ throw TSyncException("<afterreadscript> failed");
+ #endif
+ }
+ #ifdef SQLITE_SUPPORT
+ if (!fUseSQLite)
+ #endif
+ {
+ #ifdef ODBCAPI_SUPPORT
+ // dispose statement handle if we have allocated it
+ if (!aReadPhase) SafeSQLFreeHandle(SQL_HANDLE_STMT,statement);
+ #endif
+ }
+ }
+ catch (...) {
+ #ifdef SQLITE_SUPPORT
+ if (!fUseSQLite)
+ #endif
+ {
+ #ifdef ODBCAPI_SUPPORT
+ // dispose statement handle if we have allocated it
+ if (!aReadPhase) SafeSQLFreeHandle(SQL_HANDLE_STMT,statement);
+ #endif
+ }
+ // re-throw
+ throw;
+ }
+ // return status
+ return sta;
+} // TODBCApiDS::apiFetchItem
+
+
+/// end of syncset reading phase
+localstatus TODBCApiDS::apiEndDataRead(void)
+{
+ #ifdef ODBCAPI_SUPPORT
+ SQLRETURN res=SQL_SUCCESS;
+ #endif
+ localstatus sta = LOCERR_OK;
+
+ #ifdef ODBCAPI_SUPPORT
+ // release the statement handle (if any)
+ try {
+ DEBUGPRINTFX(DBG_DATA+DBG_DBAPI,("EndDataRead: committing read phase"));
+ if (fODBCReadStatement!=SQL_NULL_HANDLE) {
+ res=SafeSQLFreeHandle(SQL_HANDLE_STMT,fODBCReadStatement);
+ fODBCReadStatement=SQL_NULL_HANDLE;
+ checkConnectionError(res);
+ // Commit the transaction (to make sure a new one begins when starting to write)
+ SafeSQLEndTran(SQL_HANDLE_DBC,getODBCConnectionHandle(),SQL_COMMIT);
+ checkConnectionError(res);
+ }
+ }
+ catch (exception &e) {
+ PDEBUGPRINTFX(DBG_ERROR,("******** EndDataRead exception: %s",e.what()));
+ sta=510;
+ }
+ #endif // ODBCAPI_SUPPORT
+ return sta;
+} // TODBCApiDS::apiEndDataRead
+
+
+// start of data write
+localstatus TODBCApiDS::apiStartDataWrite(void)
+{
+ // create statement handle for writing if we don't have one already
+ #ifdef SQLITE_SUPPORT
+ if (!fUseSQLite)
+ #endif
+ {
+ #ifdef ODBCAPI_SUPPORT
+ // for ODBC only
+ if (fODBCWriteStatement==SQL_NULL_HANDLE)
+ fODBCWriteStatement = fAgentP->newStatementHandle(getODBCConnectionHandle());
+ #endif
+ }
+ return LOCERR_OK;
+} // TODBCApiDS::apiStartDataWrite
+
+
+// generate new LocalID into fLastGeneratedLocalID.
+// - can be used with %X,%x escape in SQL statements.
+void TODBCApiDS::nextLocalID(TSpecialIDMode aMode,SQLHSTMT aStatement)
+{
+ if (aMode!=sidm_none) {
+ createLocalID(fLastGeneratedLocalID,aMode);
+ }
+ else {
+ if (fConfigP->fDetermineNewIDOnce) {
+ // we use incremented starting value
+ StringObjPrintf(fLastGeneratedLocalID,"%ld",(long)fNextLocalID++);
+ }
+ else {
+ #ifdef SCRIPT_SUPPORT
+ // first check for script
+ if (!fConfigP->fLocalIDScript.empty()) {
+ // call script tro obtain new localID
+ TItemField *resP=NULL;
+ fAgentP->fScriptContextDatastore=this;
+ if (!TScriptContext::executeWithResult(
+ resP, // can be default result or NULL, will contain result or NULL if no result
+ fScriptContextP,
+ fConfigP->fLocalIDScript,
+ fConfigP->getDSFuncTableP(), // context function table
+ fAgentP, // context data (myself)
+ NULL, false, NULL, false
+ ))
+ throw TSyncException("<localidscript> failed");
+ if (resP) {
+ // get ID
+ resP->getAsString(fLastGeneratedLocalID);
+ delete resP;
+ }
+ }
+ #endif
+ // get new statement if none was passed
+ string sql=fConfigP->fObtainNewLocalIDSql;
+ // obtainNewLocalIDSql can be empty, for example if ID is returned in an
+ // out param from the inserting statement(s) or generated by fLocalIDScript
+ if (!sql.empty()) {
+ #ifdef SQLITE_SUPPORT
+ if (fUseSQLite) {
+ throw TSyncException("SQLite does not support <obtainlocalidsql>");
+ }
+ else
+ #endif
+ {
+ #ifdef ODBCAPI_SUPPORT
+ // there is a statement to execute
+ SQLHSTMT statement=aStatement;
+ if (!statement) statement=fAgentP->newStatementHandle(getODBCConnectionHandle());
+ // issue the SQL
+ try {
+ // execute query BEFORE insert to get unique ID value for new record
+ DoSQLSubstitutions(sql);
+ execSQLStatement(statement,sql,true,"getting local ID for new record",true);
+ // - fetch result row
+ SQLRETURN res=SafeSQLFetch(statement);
+ fAgentP->checkStatementError(res,statement);
+ // - get value of first field as string
+ if (!fAgentP->getColumnValueAsString(statement,1,fLastGeneratedLocalID,chs_ascii)) {
+ throw TSyncException("Failed getting new localID");
+ }
+ // close cursor (for re-using statement handle, this is needed)
+ SafeSQLCloseCursor(statement);
+ }
+ catch (...) {
+ if (!aStatement) SafeSQLFreeHandle(SQL_HANDLE_STMT,statement);
+ throw;
+ }
+ // get rid of local statement
+ if (!aStatement) SafeSQLFreeHandle(SQL_HANDLE_STMT,statement);
+ #endif
+ }
+ }
+ }
+ }
+ PDEBUGPRINTFX(DBG_DATA,("Generated new localID: %s",fLastGeneratedLocalID.c_str()));
+} // TODBCApiDS::nextLocalID
+
+
+// private helper: start writing item
+void TODBCApiDS::startWriteItem(void)
+{
+ // make sure transaction is complete if we are in item commit mode
+ #ifdef ODBCAPI_SUPPORT
+ SQLRETURN res;
+ if (fConfigP->fCommitItems) {
+ PDEBUGPRINTFX(DBG_DATA+DBG_DBAPI,("startWriteItem: commititems=true, item processing starts with new transaction"));
+ res=SafeSQLEndTran(SQL_HANDLE_DBC,getODBCConnectionHandle(),SQL_COMMIT);
+ checkConnectionError(res);
+ }
+ #endif
+} // TODBCApiDS::startWriteItem
+
+
+// private helper: end writing item
+void TODBCApiDS::endWriteItem(void)
+{
+ #ifdef ODBCAPI_SUPPORT
+ SQLRETURN res;
+ if (fConfigP->fCommitItems) {
+ PDEBUGPRINTFX(DBG_DATA+DBG_DBAPI,("endWriteItem: commititems=true, commit changes to DB"));
+ res=SafeSQLEndTran(SQL_HANDLE_DBC,getODBCConnectionHandle(),SQL_COMMIT);
+ checkConnectionError(res);
+ }
+ #endif
+} // TODBCApiDS::endWriteItem
+
+
+
+// add new item to datastore, returns created localID
+localstatus TODBCApiDS::apiAddItem(TMultiFieldItem &aItem, string &aLocalID)
+{
+ localstatus sta=LOCERR_OK;
+
+ startWriteItem();
+
+ // get field map list
+ TFieldMapList &fml = fConfigP->fFieldMappings.fFieldMapList;
+ try {
+ // determine ID
+ aLocalID.erase(); // make sure we don't assign bogus ID (fObtainNewIDAfterInsert case)
+ if (fConfigP->fSpecialIDMode==sidm_none && !fConfigP->fObtainNewIDAfterInsert) {
+ // No repeatable ID creator, and not obtain-after-insert: do it once here
+ // - create next local ID using standard method
+ nextLocalID(sidm_none,fODBCWriteStatement);
+ // - use it
+ aLocalID=fLastGeneratedLocalID;
+ } // if no special algorithm for IDs (such as system time/random based)
+ // add new record
+ sInt16 rep=0;
+ do {
+ try {
+ // - make sure we have a new ID in case it is time based
+ if (fConfigP->fSpecialIDMode!=sidm_none) {
+ // - create next local ID using special method
+ nextLocalID(fConfigP->fSpecialIDMode,fODBCWriteStatement);
+ // - use it
+ aLocalID=fLastGeneratedLocalID;
+ }
+ // - assign localID to be used (empty here if fObtainNewIDAfterInsert=true)
+ aItem.setLocalID(aLocalID.c_str());
+ #ifdef SCRIPT_SUPPORT
+ // - process beforewrite script
+ fWriting=true;
+ fInserting=true; // might be reset by ADDBYUPDATING()
+ fDeleting=false;
+ fAgentP->fScriptContextDatastore=this;
+ if (!TScriptContext::execute(fScriptContextP,fConfigP->fFieldMappings.fBeforeWriteScript,fConfigP->getDSFuncTableP(),fAgentP,&aItem,true))
+ throw TSyncException("<beforewritescript> failed");
+ if (!fInserting) {
+ // inserting was turned off by beforewritescript, in particular by ADDBYUPDATING()
+ // -> perform an update using the localid set by ADDBYUPDATING()
+ PDEBUGPRINTFX(DBG_DATA+DBG_DBAPI,("Updating existing DB row '%s' instead of inserting (ADDBYUPDATING)",aItem.getLocalID()));
+ // - return localID to caller (for caller, this looks like a regular add)
+ aLocalID = aItem.getLocalID();
+ // - perform update instead of insert
+ if (!IssueDataWriteSQL(
+ fODBCWriteStatement,
+ fConfigP->fDataUpdateSQL,
+ "updating instead of inserting",
+ true, // for update
+ fml, // field map
+ &aItem // item to read values and localid from
+ )) {
+ // not found in data table - do a normal add (even if ADDBYUPDATING() wanted an update)
+ fInserting = true; // revert back to inserting
+ PDEBUGPRINTFX(DBG_ERROR,("Updating existing not possible -> reverting to insert"));
+ }
+ }
+ // check if we still need to insert
+ if (fInserting)
+ #endif
+ {
+ // Normal insert
+ // - issue statement(s)
+ #ifdef ODBCAPI_SUPPORT
+ bool hasdata=
+ #endif
+ IssueDataWriteSQL(
+ fODBCWriteStatement,
+ fConfigP->fDataInsertSQL,
+ "adding record",
+ false, // not for update
+ fml, // field map
+ &aItem // item to read values and localid from
+ );
+ // copy ID in case we've got it via an output param
+ aLocalID=aItem.getLocalID();
+ #ifdef SQLITE_SUPPORT
+ if (!fUseSQLite)
+ #endif
+ {
+ #ifdef ODBCAPI_SUPPORT
+ if (fConfigP->fInsertReturnsID) {
+ // insert statement should return ID of record inserted
+ if (hasdata) {
+ SQLRETURN res=SafeSQLFetch(fODBCWriteStatement);
+ if (fAgentP->checkStatementHasData(res,fODBCWriteStatement)) {
+ // get id (must be in first result column of first and only result row)
+ if (!fAgentP->getColumnValueAsString(fODBCWriteStatement,1,fLastGeneratedLocalID,chs_ascii)) {
+ throw TSyncException("Failed getting localID from <datainsertsql> result set");
+ }
+ // - use it
+ aLocalID=fLastGeneratedLocalID;
+ }
+ else
+ hasdata=false;
+ // - close cursor anyway
+ SafeSQLCloseCursor(fODBCWriteStatement);
+ }
+ if (!hasdata)
+ throw TSyncException("<datainsertsql> statement did not return insert ID");
+ }
+ #endif // ODBCAPI_SUPPORT
+ }
+ } // normal insert
+ // done ok
+ break;
+ }
+ catch (TSyncException &e) {
+ // error while issuing write
+ if (++rep>fConfigP->fInsertRetries) throw; // error, let it show
+ PDEBUGPRINTFX(DBG_DATA+DBG_DBAPI,(
+ "Insert failed with localid='%s': %s --> retrying",
+ aLocalID.c_str(),
+ e.what()
+ ));
+ // repeat once more
+ continue;
+ }
+ } while(true);
+ // check if we want to use local ID created implicitly by inserting row
+ #ifdef SCRIPT_SUPPORT
+ if (fInserting) // only if really inserting (but not in ADDBYUPDATING() case)
+ #endif
+ {
+ if (!fConfigP->fInsertReturnsID && fConfigP->fSpecialIDMode==sidm_none && fConfigP->fObtainNewIDAfterInsert) {
+ // determine new ID AFTER insert
+ #ifdef SQLITE_SUPPORT
+ if (fUseSQLite) {
+ // For SQLite, localID is always autocreated ROWID, get it now
+ StringObjPrintf(aLocalID,"%lld",sqlite3_last_insert_rowid(fSQLiteP));
+ PDEBUGPRINTFX(DBG_DBAPI,("sqlite3_last_insert_rowid() returned %s",aLocalID.c_str()));
+ }
+ else
+ #endif // SQLITE_SUPPORT
+ {
+ #ifdef ODBCAPI_SUPPORT
+ // get local ID using standard method (SQL statement to get ID)
+ nextLocalID(sidm_none,fODBCWriteStatement);
+ // - use it
+ aLocalID=fLastGeneratedLocalID;
+ #endif // ODBCAPI_SUPPORT
+ }
+ }
+ // make sure localID is known now
+ aItem.setLocalID(aLocalID.c_str());
+ } // determine ID of inserted record
+ // also write array fields, if any
+ #ifdef ARRAYDBTABLES_SUPPORT
+ if (fHasArrayFields) {
+ // write data to linked array tables as well
+ // - now write
+ writeArrayFields(
+ fODBCWriteStatement,
+ aItem,
+ fml,
+ #ifdef SCRIPT_SUPPORT
+ fInserting // if really performed insert (i.e. not ADDBYUPDATING()), child record cleaning is not necessary (but can be forced by fAlwaysCleanArray)
+ #else
+ true // is an insert, so child record cleaning not necessary (but can be forced by fAlwaysCleanArray)
+ #endif
+ );
+ }
+ #endif
+ #ifdef SCRIPT_SUPPORT
+ // process oevrall afterwrite script
+ // Note: fInserting is still valid from above (and can be false in ADDBYUPDATING() case)
+ fWriting=true;
+ fDeleting=false;
+ fAgentP->fScriptContextDatastore=this;
+ if (!TScriptContext::execute(fScriptContextP,fConfigP->fFieldMappings.fAfterWriteScript,fConfigP->getDSFuncTableP(),fAgentP,&aItem,true))
+ throw TSyncException("<afterwritescript> failed");
+ #endif
+ }
+ catch (...) {
+ endWriteItem();
+ throw;
+ }
+ // end writing
+ endWriteItem();
+ return sta;
+} // TODBCApiDS::apiAddItem
+
+
+// update existing item in datastore, returns 404 if item not found
+localstatus TODBCApiDS::apiUpdateItem(TMultiFieldItem &aItem)
+{
+ localstatus sta=LOCERR_OK;
+
+ startWriteItem();
+ // get field map list
+ TFieldMapList &fml = fConfigP->fFieldMappings.fFieldMapList;
+ try {
+ // update record with this localID
+ #ifdef SCRIPT_SUPPORT
+ // - process beforewrite script
+ fWriting=true;
+ fInserting=false;
+ fDeleting=false;
+ fAgentP->fScriptContextDatastore=this;
+ if (!TScriptContext::execute(fScriptContextP,fConfigP->fFieldMappings.fBeforeWriteScript,fConfigP->getDSFuncTableP(),fAgentP,&aItem,true))
+ throw TSyncException("<beforewritescript> failed");
+ #endif
+ // - issue
+ if (!IssueDataWriteSQL(
+ fODBCWriteStatement,
+ fConfigP->fDataUpdateSQL,
+ "replacing record",
+ true, // for update
+ fml, // field map
+ &aItem // item to read values and localid from
+ )) {
+ // not found in data table
+ sta=404;
+ }
+ else {
+ // also write array fields, if any
+ #ifdef ARRAYDBTABLES_SUPPORT
+ if (fHasArrayFields) {
+ // write data to linked array tables as well
+ writeArrayFields(
+ fODBCWriteStatement,
+ aItem, // item to read values from
+ fml,
+ false // is not an insert, update needs erasing first in all cases!
+ );
+ }
+ #endif
+ #ifdef SCRIPT_SUPPORT
+ // process oevrall afterwrite script
+ fWriting=true;
+ fInserting=false;
+ fDeleting=false;
+ fAgentP->fScriptContextDatastore=this;
+ if (!TScriptContext::execute(fScriptContextP,fConfigP->fFieldMappings.fAfterWriteScript,fConfigP->getDSFuncTableP(),fAgentP,&aItem,true))
+ throw TSyncException("<afterwritescript> failed");
+ #endif
+ }
+ }
+ catch (...) {
+ endWriteItem();
+ throw;
+ }
+ // end writing
+ endWriteItem();
+ return sta;
+} // TODBCApiDS::apiUpdateItem
+
+
+// delete existing item in datastore, returns 211 if not existing any more
+localstatus TODBCApiDS::apiDeleteItem(TMultiFieldItem &aItem)
+{
+ localstatus sta=LOCERR_OK;
+
+ startWriteItem();
+ // get field map list
+ TFieldMapList &fml = fConfigP->fFieldMappings.fFieldMapList;
+ try {
+ // - issue statements for delete (could be UPDATE or DELETE)
+ if (!IssueDataWriteSQL(
+ fODBCWriteStatement,
+ fConfigP->fDataDeleteSQL,
+ "deleting record",
+ false, // not for update
+ fml, // field map
+ &aItem // item to read values and localid from
+ )) {
+ sta=211; // item not deleted, was not there any more
+ }
+ else {
+ // also delete array fields, if any
+ #ifdef ARRAYDBTABLES_SUPPORT
+ // get data from linked array tables as well
+ deleteArrayFields(
+ fODBCWriteStatement,
+ aItem,
+ fConfigP->fFieldMappings.fFieldMapList
+ );
+ #endif
+ #ifdef SCRIPT_SUPPORT
+ // process overall afterwrite script
+ fWriting=true;
+ fInserting=false;
+ fDeleting=true;
+ fAgentP->fScriptContextDatastore=this;
+ if (!TScriptContext::execute(fScriptContextP,fConfigP->fFieldMappings.fAfterWriteScript,fConfigP->getDSFuncTableP(),fAgentP,&aItem,true))
+ throw TSyncException("<afterwritescript> failed");
+ #endif
+ }
+ }
+ catch (...) {
+ endWriteItem();
+ throw;
+ }
+ // end writing
+ endWriteItem();
+ return sta;
+} // TODBCApiDS::apiDeleteItem
+
+
+/// @brief Load admin data from ODBC database
+/// Must search for existing target record matching the triple (aDeviceID,aDatabaseID,aRemoteDBID)
+/// - if there is a matching record: load it
+/// - if there is no matching record, set fFirstTimeSync=true. The implementation may already create a
+/// new record with the key (aDeviceID,aDatabaseID,aRemoteDBID) and initialize it with the data from
+/// the items as shown below. At least, fTargetKey must be set to a value that will allow apiSaveAdminData to
+/// update the record. In case implementation chooses not create the record only in apiSaveAdminData, it must
+/// buffer the triple (aDeviceID,aDatabaseID,aRemoteDBID) such that it is available at apiSaveAdminData.
+/// If a record exists implementation must load the following items:
+/// - fTargetKey = some key value that can be used to re-identify the target record later at SaveAdminData.
+/// If the database implementation has other means to re-identify the target, this can be
+/// left unassigned.
+/// - fLastRemoteAnchor = anchor string used by remote party for last session (and saved to DB then)
+/// - fPreviousSyncTime = anchor (beginning of session) timestamp of last session.
+/// - fPreviousToRemoteSyncCmpRef = Reference time to determine items modified since last time sending data to remote
+/// - fPreviousToRemoteSyncIdentifier = string identifying last session that sent data to remote (needs only be saved
+/// if derived datastore cannot work with timestamps and has its own identifier).
+/// - fMapTable = list<TMapEntry> containing map entries. The implementation must load all map entries
+/// related to the current sync target identified by the triple of (aDeviceID,aDatabaseID,aRemoteDBID)
+/// or by fTargetKey. The entries added to fMapTable must have "changed", "added" and "deleted" flags
+/// set to false.
+/// For resumable datastores:
+/// - fMapTable = In addition to the above, the markforresume flag must be saved in the mapflags
+// when it is not equal to the savedmark flag - independently of added/deleted/changed.
+/// - fResumeAlertCode = alert code of current suspend state, 0 if none
+/// - fPreviousSuspendCmpRef = reference time of last suspend (used to detect items modified during a suspend / resume)
+/// - fPreviousSuspendIdentifier = identifier of last suspend (used to detect items modified during a suspend / resume)
+/// (needs only be saved if derived datastore cannot work with timestamps and has
+/// its own identifier)
+/// - fPendingAddMaps = map<string,string>. The implementation must load all all pending maps (client only) into
+/// fPendingAddMaps (and fUnconfirmedMaps must be left empty).
+/// - fTempGUIDMap = map<string,string>. The implementation must save all entries as temporary LUID to GUID mappings
+/// (server only)
+localstatus TODBCApiDS::apiLoadAdminData(
+ const char *aDeviceID, // remote device URI (device ID)
+ const char *aDatabaseID, // database ID
+ const char *aRemoteDBID // database ID of remote device
+)
+{
+ #ifndef HAS_SQL_ADMIN
+ return 510; // must use plugin, no ODBC
+ #else // HAS_SQL_ADMIN
+ localstatus sta=0; // assume ok
+ string sql;
+
+ // ODBC based target/map
+ fODBCAdminData=true;
+ // determine Folder key, if any
+ sta=LOCERR_OK;
+ // - determine folder name, if any
+ string bname,foldername;
+ analyzeName(aDatabaseID,&bname,&foldername);
+ // - search on foldername
+ SQLRETURN res;
+ SQLHSTMT statement=fAgentP->newStatementHandle(getODBCConnectionHandle());
+ try {
+ // get SQL
+ sql = fConfigP->fFolderKeySQL;
+ if (!sql.empty()) {
+ // substitute: %F = foldername
+ StringSubst(sql,"%F",foldername,2,fConfigP->sqlPrepCharSet(),fConfigP->fDataLineEndMode,fConfigP->fQuotingMode);
+ DoSQLSubstitutions(sql); // for %u = userkey, %d=devicekey
+ // issue
+ execSQLStatement(statement,sql,true,"getting folder key",false);
+ // - fetch result row
+ res=SafeSQLFetch(statement);
+ if (!fAgentP->checkStatementHasData(res,statement)) {
+ // No data: user does not have permission for this folder
+ PDEBUGPRINTFX(DBG_ERROR,("apiLoadAdminData: User has no permission for acessing folder '%s'",foldername.c_str()));
+ sta=403;
+ }
+ else {
+ // get folder key
+ fAgentP->getColumnValueAsString(statement,1,fFolderKey,chs_ascii);
+ PDEBUGPRINTFX(DBG_ADMIN,("apiLoadAdminData: Folder key is '%s'",fFolderKey.c_str()));
+ }
+ SafeSQLCloseCursor(statement);
+ } // if folderkeySQL
+ else {
+ // no folderkey, just copy user key
+ fFolderKey=fAgentP->getUserKey();
+ }
+ if (sta==LOCERR_OK) {
+ // now determine sync target key
+ bool madenew=false;
+ do {
+ // get SQL
+ sql=fConfigP->fGetSyncTargetSQL;
+ // substitute standard: %f=folderkey, %u=userkey, %d=devicekey, %t=targetkey
+ DoSQLSubstitutions(sql);
+ // substitute specific: %D=deviceid, %P=devicedbpath
+ StringSubst(sql,"%D",aDeviceID,2,-1,fConfigP->sqlPrepCharSet(),fConfigP->fDataLineEndMode,fConfigP->fQuotingMode);
+ StringSubst(sql,"%P",aRemoteDBID,2,-1,fConfigP->sqlPrepCharSet(),fConfigP->fDataLineEndMode,fConfigP->fQuotingMode);
+ // issue
+ execSQLStatement(statement,sql,true,"getting target info",false);
+ // - fetch result row
+ res=SafeSQLFetch(statement);
+ if (!fAgentP->checkStatementHasData(res,statement)) {
+ if (madenew)
+ throw TSyncException("apiLoadAdminData: new created SyncTarget is not accessible");
+ // target does not yet exist, create new
+ DEBUGPRINTFX(DBG_ADMIN,("apiLoadAdminData: Target does not yet exist, create new"));
+ SafeSQLCloseCursor(statement);
+ // create new sync target entry
+ sql=fConfigP->fNewSyncTargetSQL;
+ // substitute standard: %f=folderkey, %u=userkey, %d=devicekey, %t=targetkey
+ DoSQLSubstitutions(sql);
+ // substitute specific: %D=deviceid, %P=devicedbpath
+ StringSubst(sql,"%D",aDeviceID,2,-1,fConfigP->sqlPrepCharSet(),fConfigP->fDataLineEndMode,fConfigP->fQuotingMode);
+ StringSubst(sql,"%P",aRemoteDBID,2,-1,fConfigP->sqlPrepCharSet(),fConfigP->fDataLineEndMode,fConfigP->fQuotingMode);
+ // now issue
+ execSQLStatement(statement,sql,false,"creating new target info",false);
+ // try to read again
+ madenew=true;
+ continue;
+ }
+ else {
+ // get data: IN ASCENDING COL ORDER!!!
+ sInt16 col=1;
+ fAgentP->getColumnValueAsString(statement,col++,fTargetKey,chs_ascii);
+ fAgentP->getColumnValueAsString(statement,col++,fLastRemoteAnchor,chs_ascii);
+ PDEBUGPRINTFX(DBG_ADMIN,("apiLoadAdminData: Target key is '%s'",fTargetKey.c_str()));
+ PDEBUGPRINTFX(DBG_ADMIN,("apiLoadAdminData: Saved Remote Sync Anchor is '%s'",fLastRemoteAnchor.c_str()));
+ // first sync if target record is new or if remote anchor is (still) empty
+ // NOTE: this is because only a successful sync makes next attempt non-firsttime!
+ fFirstTimeSync=madenew || fLastRemoteAnchor.empty();
+ // last to remote sync reference date/time OR start of last session date/time
+ // Note: If we DON'T HAVE fSyncTimeStampAtEnd, but HAVE fOneWayFromRemoteSupported,
+ // this is NOT the reference time here, but the session start time;
+ // the reference time is saved separately (see below)
+ lineartime_t dltime;
+ getColumnsAsTimestamp(statement,col,fConfigP->fSyncTimestamp,dltime, TCTX_UTC);
+ if (!fConfigP->fSyncTimeStampAtEnd && fConfigP->fOneWayFromRemoteSupported)
+ fPreviousSyncTime = dltime;
+ else {
+ fPreviousToRemoteSyncCmpRef = dltime;
+ }
+ // In case we DON'T HAVE fSyncTimeStampAtEnd, and DON'T HAVE fOneWayFromRemoteSupported,
+ // the above loaded fPreviousToRemoteSyncCmpRef is the ONLY time we get, so for this
+ // case we won't get separate session start time below, so initialize it here.
+ fPreviousSyncTime = dltime;
+ // check for separate start-of-session time
+ if (fConfigP->fSyncTimeStampAtEnd) {
+ // @note For new V3.0 architecture, this is now a bit the wrong way around, as
+ // we only need the anchor (fPreviousSyncTime) and a reference for the
+ // last-to-remote sync (fPreviousToRemoteSyncCmpRef). However, to remain compatible with
+ // existing DB schemas we need to swap values a bit.
+ // In case database cannot explicitly set modification timestamps, we need the start-of-session
+ // (which is used for creation of anchor for remote) and we need a comparison reference.
+ // - override anchor time with separately stored value
+ getColumnsAsTimestamp(statement,col,fConfigP->fSyncTimestamp,fPreviousSyncTime, TCTX_UTC);
+ }
+ // get date and time of last two-way-sync if database has it stored separately
+ // Note: when we have fSyncTimeStampAtEnd, fPreviousToRemoteSyncCmpRef is already set here,
+ // but we'll get it again (it is redundantly stored if we have both fSyncTimeStampAtEnd and fPreviousToRemoteSyncCmpRef)
+ // - get from DB if available
+ if (fConfigP->fOneWayFromRemoteSupported) {
+ // one-way from remote is supported: we have a separate date for last sync that sent changes to remote
+ // - override fPreviousToRemoteSyncCmpRef (when one-way-from-remote is supported, this is not
+ // necessarily same as start of last session (fPreviousSyncTime) any more)
+ getColumnsAsTimestamp(statement,col,fConfigP->fSyncTimestamp,fPreviousToRemoteSyncCmpRef, TCTX_UTC);
+ if (fConfigP->fSyncTimeStampAtEnd) {
+ // Note: for V3.0, this may be redundant and already loaded in case we also have fSyncTimeStampAtEnd
+ getColumnsAsTimestamp(statement,col,fConfigP->fSyncTimestamp,fPreviousToRemoteSyncCmpRef, TCTX_UTC);
+ }
+ }
+ // get opaque reference identifier from DB, if it stores them
+ // (otherwise, implMakeAdminReady will create ISO timestamp strings as standard identifiers)
+ if (fConfigP->fStoreSyncIdentifiers) {
+ // we have stored the identifier (and do now know its meaning, it's just a string that
+ // the DB implementation needs to sort out changed items in the datastore
+ fAgentP->getColumnValueAsString(statement,col++,fPreviousToRemoteSyncIdentifier,chs_ascii);
+ }
+ // Summarize
+ #ifdef SYDEBUG
+ string ts;
+ StringObjTimestamp(ts,fPreviousSyncTime);
+ DEBUGPRINTFX(DBG_ADMIN,("apiLoadAdminData: start time of last sync is: %s",ts.c_str()));
+ StringObjTimestamp(ts,fPreviousToRemoteSyncCmpRef);
+ DEBUGPRINTFX(DBG_ADMIN,("apiLoadAdminData: compare reference time of last-to-remote-sync is: %s",ts.c_str()));
+ DEBUGPRINTFX(DBG_ADMIN,("apiLoadAdminData: stored reference identifier of last-to-remote-sync is: '%s'",fPreviousToRemoteSyncIdentifier.c_str()));
+ #endif
+ // Resume support if there
+ if (fConfigP->fResumeSupport) {
+ // next column is alert code for resuming
+ uInt32 templong;
+ fAgentP->getColumnValueAsULong(statement,col++,templong);
+ fResumeAlertCode=templong;
+ PDEBUGPRINTFX(DBG_ADMIN+DBG_EXOTIC,("apiLoadAdminData: fResumeAlertCode is %hd",fResumeAlertCode));
+ // next column(s) is/are last suspend date/time
+ getColumnsAsTimestamp(statement,col,fConfigP->fSyncTimestamp,fPreviousSuspendCmpRef, TCTX_UTC);
+ #ifdef SYDEBUG
+ StringObjTimestamp(ts,fPreviousSuspendCmpRef);
+ PDEBUGPRINTFX(DBG_ADMIN+DBG_EXOTIC,("apiLoadAdminData: compare reference time of last suspend is %s",ts.c_str()));
+ #endif
+ // Get last suspend identifier from DB
+ if (fConfigP->fStoreSyncIdentifiers) {
+ // next line is opaque string idenifier of last sync with transfer to remote
+ fAgentP->getColumnValueAsString(statement,col++,fPreviousSuspendIdentifier,chs_ascii);
+ PDEBUGPRINTFX(DBG_ADMIN+DBG_EXOTIC,("apiLoadAdminData: stored reference identifier of last suspend is '%s'",fPreviousSuspendIdentifier.c_str()));
+ }
+ // get partial item suspend data, if any:
+ // - Source,Target,LastStatus,PIState,Totalsize,Unconfirmedsize,StoredSize,BLOB
+ if (fConfigP->fResumeItemSupport) {
+ // - last item URIs
+ fAgentP->getColumnValueAsString(statement,col++,fLastSourceURI,chs_ascii);
+ fAgentP->getColumnValueAsString(statement,col++,fLastTargetURI,chs_ascii);
+ // - last status
+ fAgentP->getColumnValueAsULong(statement,col++,templong); fLastItemStatus=templong;
+ // - partial item state
+ fAgentP->getColumnValueAsULong(statement,col++,templong); fPartialItemState=(TPartialItemState)templong;
+ // - sizes
+ fAgentP->getColumnValueAsULong(statement,col++,fPITotalSize);
+ fAgentP->getColumnValueAsULong(statement,col++,fPIUnconfirmedSize);
+ fAgentP->getColumnValueAsULong(statement,col++,fPIStoredSize);
+ // - the BLOB data itself
+ string blob;
+ fAgentP->getColumnValueAsString(statement,col++,blob,chs_ascii,true);
+ // - move it into data
+ if (fPIStoredDataP && fPIStoredDataAllocated) smlLibFree(fPIStoredDataP);
+ fPIStoredDataAllocated=false;
+ fPIStoredDataP=smlLibMalloc(blob.size()+1); // plus terminator for string interpretation
+ if (fPIStoredDataP) {
+ fPIStoredDataAllocated=true;
+ smlLibMemcpy(fPIStoredDataP,blob.c_str(),blob.size());
+ if (fPIStoredSize>blob.size()) fPIStoredSize=blob.size(); // security
+ *((uInt8 *)fPIStoredDataP+fPIStoredSize)=0; // string terminator
+ }
+ PDEBUGPRINTFX(DBG_ADMIN+DBG_EXOTIC,(
+ "apiLoadAdminData: partial item resume info: src='%s', targ='%s', lastStatus=%hd, state=%hd, totalSz=%ld, unconfSz=%ld, storedSz=%ld",
+ fLastSourceURI.c_str(),
+ fLastTargetURI.c_str(),
+ fLastItemStatus,
+ (TSyError)fPartialItemState,
+ fPITotalSize,
+ fPIUnconfirmedSize,
+ fPIStoredSize
+ ));
+ }
+ }
+ SafeSQLCloseCursor(statement);
+ // now read current map entries of target
+ sql=fConfigP->fMapFetchAllSQL;
+ DoSQLSubstitutions(sql); // only folderkey,targetkey,userkey is available
+ // - issue
+ execSQLStatement(statement,sql,true,"reading map into internal list",false);
+ // - fetch results
+ do {
+ res=SafeSQLFetch(statement);
+ // check for end
+ if (!fAgentP->checkStatementHasData(res,statement)) break;
+ // get map entry
+ TMapEntry entry;
+ fAgentP->getColumnValueAsString(statement,1,entry.localid,chs_ascii);
+ fAgentP->getColumnValueAsString(statement,2,entry.remoteid,chs_ascii);
+ if (fConfigP->fResumeSupport) {
+ // we have a separate entry type
+ uInt32 et;
+ fAgentP->getColumnValueAsULong(statement,3,et);
+ if (et>=numMapEntryTypes)
+ entry.entrytype = mapentry_invalid;
+ else
+ entry.entrytype = (TMapEntryType)et;
+ // and some map flags
+ fAgentP->getColumnValueAsULong(statement,4,entry.mapflags);
+ }
+ else {
+ entry.mapflags=0; // no flags stored
+ entry.entrytype=mapentry_normal; // normal entry
+ }
+ PDEBUGPRINTFX(DBG_ADMIN+DBG_EXOTIC,(
+ "read map entry (type=%s): localid='%s', remoteid='%s', mapflags=0x%lX",
+ MapEntryTypeNames[entry.entrytype],
+ entry.localid.c_str(),
+ entry.remoteid.c_str(),
+ entry.mapflags
+ ));
+ // save entry in list
+ entry.changed=false; // not yet changed
+ entry.added=false; // already there
+ // remember saved state of suspend mark
+ entry.markforresume=false; // not yet marked for this session (mark of last session is in mapflag_useforresume!)
+ entry.savedmark=entry.mapflags & mapflag_useforresume;
+ // IMPORTANT: non-normal entries must be saved as deleted in the main map - they will be re-activated at the
+ // next save if needed
+ entry.deleted = entry.entrytype!=mapentry_normal; // only normal ones may be saved as existing in the main map
+ // save to main map list anyway to allow differential SQL updates to map table (instead of writing everything all the time)
+ fMapTable.push_back(entry);
+ // now save special maps to extra lists according to type
+ // Note: in the main map, these are marked deleted. Before the next saveAdminData, these will
+ // be re-added (=re-activated) from the extra lists if they still exist.
+ switch (entry.entrytype) {
+ #ifndef SYSYNC_CLIENT
+ case mapentry_tempidmap:
+ fTempGUIDMap[entry.remoteid]=entry.localid; // tempGUIDs are accessed by remoteID=tempID
+ break;
+ #else
+ case mapentry_pendingmap:
+ fPendingAddMaps[entry.localid]=entry.remoteid;
+ break;
+ #endif
+ }
+ } while(true);
+ // no more records
+ SafeSQLCloseCursor(statement);
+ // done
+ break;
+ } // target record found
+ } while(true);
+ } // if ok
+ if (sta==LOCERR_OK) {
+ // determine how to create new IDs for database
+ fNextLocalID=-1; // no global ID, must be determined at actual insert
+ // for some databases w/o usable ID variable (FMPro), starting point for ids
+ // might be some "SELECT MAX()" query, which would be executed here
+ if (fConfigP->fDetermineNewIDOnce) {
+ uInt32 maxid;
+ SQLRETURN res;
+ sql=fConfigP->fObtainNewLocalIDSql.c_str();
+ DoSQLSubstitutions(sql);
+ execSQLStatement(statement,sql,true,"Next-to-be-used localID (determined once in makeAdminReady)",false);
+ // - fetch result row
+ res=SafeSQLFetch(statement);
+ fAgentP->checkStatementError(res,statement);
+ // - get value of first field as long
+ if (fAgentP->getColumnValueAsULong(statement,1,maxid)) {
+ // next ID found
+ if (maxid<fConfigP->fMinNextID)
+ maxid=fConfigP->fMinNextID; // don't use value smaller than defined minimum
+ }
+ else {
+ // no max value found, start at MinNextID
+ maxid=fConfigP->fMinNextID;
+ }
+ SafeSQLCloseCursor(statement);
+ // now assign
+ fNextLocalID=maxid;
+ #ifdef SYDEBUG
+ PDEBUGPRINTFX(DBG_ADMIN,("apiLoadAdminData: next local ID is %ld",fNextLocalID));
+ #endif
+ }
+ } // if ok
+ // release the statement handle
+ SafeSQLFreeHandle(SQL_HANDLE_STMT,statement);
+ }
+ catch (...) {
+ // release the statement handle
+ SafeSQLFreeHandle(SQL_HANDLE_STMT,statement);
+ throw;
+ }
+ PDEBUGPRINTFX(DBG_ADMIN,("apiLoadAdminData: Number of map entries read = %ld",fMapTable.size()));
+ return sta;
+ #endif // HAS_SQL_ADMIN
+} // TODBCApiDS::apiLoadAdminData
+
+
+/// @brief Save admin data to ODBC database
+/// @param[in] aSessionFinished if true, this is a end-of-session save (and not only a suspend save)
+/// Must save to the target record addressed at LoadAdminData() by the triple (aDeviceID,aDatabaseID,aRemoteDBID)
+/// Implementation must save the following items:
+/// - fLastRemoteAnchor = anchor string used by remote party for this session (and saved to DB then)
+/// - fPreviousSyncTime = anchor (beginning of session) timestamp of this session.
+/// - fPreviousToRemoteSyncCmpRef = Reference time to determine items modified since last time sending data to remote
+/// - fPreviousToRemoteSyncIdentifier = string identifying last session that sent data to remote (needs only be saved
+/// if derived datastore cannot work with timestamps and has its own identifier).
+/// - fMapTable = list<TMapEntry> containing map entries. The implementation must save all map entries
+/// that have changed, are new or are deleted. See below for additional resume requirements.
+/// For resumable datastores:
+/// - fMapTable = In addition to the above, the markforresume flag must be saved in the mapflags
+// when it is not equal to the savedmark flag - independently of added/deleted/changed.
+/// - fResumeAlertCode = alert code of current suspend state, 0 if none
+/// - fPreviousSuspendCmpRef = reference time of last suspend (used to detect items modified during a suspend / resume)
+/// - fPreviousSuspendIdentifier = identifier of last suspend (used to detect items modified during a suspend / resume)
+/// (needs only be saved if derived datastore cannot work with timestamps and has
+/// its own identifier)
+/// - fPendingAddMaps and fUnconfirmedMaps = map<string,string>. The implementation must save all entries as
+/// pending maps (client only). Note that fPendingAddMaps might contain temporary localIDs,
+/// so call dsFinalizeLocalID() to ensure these are converted to final before saving.
+/// - fTempGUIDMap = map<string,string>. The implementation must save all entries as temporary LUID to GUID mappings
+/// (server only)
+localstatus TODBCApiDS::apiSaveAdminData(bool aSessionFinished, bool aSuccessful)
+{
+ #ifndef HAS_SQL_ADMIN
+ return 510; // must use plugin, no ODBC
+ #else
+ SQLRETURN res;
+ localstatus sta=LOCERR_OK;
+ try {
+ // if data is not handled by ODBC, we might have no write statement here, so open one
+ if (fODBCWriteStatement==SQL_NULL_HANDLE)
+ fODBCWriteStatement = fAgentP->newStatementHandle(getODBCConnectionHandle());
+ // now save target head record admin data
+ sta=updateSyncTarget(fODBCWriteStatement,aSessionFinished && aSuccessful);
+ if (sta==LOCERR_OK) {
+ // then apply Map modifications (target detail records)
+ sta=updateODBCMap(fODBCWriteStatement,aSessionFinished && aSuccessful);
+ }
+ // dispose of Write statement handle
+ if (fODBCWriteStatement!=SQL_NULL_HANDLE && aSessionFinished) {
+ DEBUGPRINTFX(DBG_DBAPI,("SaveAdminData: End of session - Freeing Write statement handle..."));
+ res=SafeSQLFreeHandle(SQL_HANDLE_STMT,fODBCWriteStatement);
+ fODBCWriteStatement=SQL_NULL_HANDLE;
+ checkConnectionError(res);
+ }
+ // commit transaction
+ if (fODBCConnectionHandle!=SQL_NULL_HANDLE) {
+ // commit DB transaction
+ DEBUGPRINTFX(DBG_DBAPI,("SaveAdminData(commit): Committing DB transaction..."));
+ res=SafeSQLEndTran(SQL_HANDLE_DBC,getODBCConnectionHandle(),SQL_COMMIT);
+ checkConnectionError(res);
+ }
+ }
+ catch (exception &e) {
+ PDEBUGPRINTFX(DBG_ERROR,("******** SaveAdminData exception: %s",e.what()));
+ sta=510; // failed, DB error
+ }
+ // return status
+ return sta;
+ #endif // HAS_SQL_ADMIN
+} // TODBCApiDS::apiSaveAdminData
+
+
+// - end DB data write sequence (but not yet admin data)
+localstatus TODBCApiDS::apiEndDataWrite(string &aThisSyncIdentifier)
+{
+ // we do not have a separate sync identifier
+ aThisSyncIdentifier.erase();
+ // make sure we commit the transaction here in case admin data is not in ODBC
+ #ifdef ODBCAPI_SUPPORT
+ try {
+ SQLRETURN res;
+ if (fODBCWriteStatement!=SQL_NULL_HANDLE) {
+ DEBUGPRINTFX(DBG_DBAPI,("EndDBDataWrite: we have no ODBC admin data, freeing Write statement handle now..."));
+ res=SafeSQLFreeHandle(SQL_HANDLE_STMT,fODBCWriteStatement);
+ fODBCWriteStatement=SQL_NULL_HANDLE;
+ checkConnectionError(res);
+ }
+ // commit DB transaction
+ if (fODBCConnectionHandle!=SQL_NULL_HANDLE) {
+ DEBUGPRINTFX(DBG_DBAPI,("EndDBDataWrite: ...and commit DB transaction"));
+ res=SafeSQLEndTran(SQL_HANDLE_DBC,getODBCConnectionHandle(),SQL_COMMIT);
+ checkConnectionError(res);
+ }
+ }
+ catch (exception &e) {
+ PDEBUGPRINTFX(DBG_ERROR,("******** EndDBDataWrite exception: %s",e.what()));
+ return 510;
+ }
+ #endif // ODBCAPI_SUPPORT
+ return LOCERR_OK;
+} // TODBCApiDS::apiEndDataWrite
+
+
+/* end of TODBCApiDS implementation */
+
+
+#ifdef STREAMFIELD_SUPPORT
+
+// TODBCFieldProxy
+// ===============
+
+TODBCFieldProxy::TODBCFieldProxy(
+ TODBCApiDS *aODBCdsP,
+ TODBCFieldMapItem *aFieldMapP,
+ const char *aMasterKey,
+ const char *aDetailKey
+)
+{
+ // save values
+ fODBCdsP = aODBCdsP;
+ fFieldMapP = aFieldMapP;
+ fMasterKey = aMasterKey;
+ fDetailKey = aDetailKey;
+ fFetched = false;
+ fValue.erase();
+} // TODBCFieldProxy::TODBCFieldProxy
+
+
+TODBCFieldProxy::~TODBCFieldProxy()
+{
+ // nop at this time
+} // TODBCFieldProxy::~TODBCFieldProxy
+
+
+// fetch BLOB from DPAPI
+void TODBCFieldProxy::fetchBlob(size_t aNeededSize)
+{
+ if (!fFetched) {
+ // if do not have anything yet and need something, read it now
+ string sql = fFieldMapP->fReadBlobSQL;
+ // the only possible substitutions are %k and %K
+ StringSubst(sql,"%k",fMasterKey,2); // the localID of the entire record
+ StringSubst(sql,"%K",fDetailKey,2); // the key of this specific array element
+ // fetch now
+ SQLHSTMT statement = 0;
+ #ifdef SQLITE_SUPPORT
+ if (!fODBCdsP->fUseSQLite)
+ #endif
+ {
+ #ifdef ODBCAPI_SUPPORT
+ statement=fODBCdsP->fAgentP->newStatementHandle(fODBCdsP->getODBCConnectionHandle());
+ #endif
+ }
+ try {
+ fODBCdsP->prepareSQLStatement(statement, sql.c_str(), true, "Proxy Field Fetch");
+ fODBCdsP->execSQLStatement(statement, sql, true, NULL, true);
+ if (!fODBCdsP->fetchNextRow(statement, true)) {
+ // no data
+ fValue.erase();
+ }
+ else {
+ // get data
+ #ifdef SQLITE_SUPPORT
+ if (fODBCdsP->fUseSQLite) {
+ size_t siz = sqlite3_column_bytes(fODBCdsP->fSQLiteStmtP,0);
+ if (siz>0) {
+ if (fFieldMapP->dbfieldtype==dbft_blob)
+ fValue.assign((cAppCharP)sqlite3_column_blob(fODBCdsP->fSQLiteStmtP,0),siz);
+ else
+ appendStringAsUTF8((const char *)sqlite3_column_text(fODBCdsP->fSQLiteStmtP,0), fValue, fODBCdsP->fConfigP->sqlPrepCharSet(), lem_cstr); // Convert to app-charset (UTF8) and C-type lineends
+ }
+ else {
+ fValue.erase();
+ }
+ }
+ else
+ #endif
+ {
+ #ifdef ODBCAPI_SUPPORT
+ // pass in actual charset (including utf16 - getColumnValueAsString handles UTF16 case internally)
+ fODBCdsP->fAgentP->getColumnValueAsString(statement,1,fValue,fODBCdsP->fConfigP->fDataCharSet,fFieldMapP->dbfieldtype==dbft_blob);
+ #endif
+ }
+ }
+ // fetched now
+ fFetched=true;
+ // done
+ fODBCdsP->finalizeSQLStatement(statement,true);
+ // release the statement handle
+ #ifdef SQLITE_SUPPORT
+ if (!fODBCdsP->fUseSQLite)
+ #endif
+ {
+ #ifdef ODBCAPI_SUPPORT
+ SafeSQLFreeHandle(SQL_HANDLE_STMT,statement);
+ #endif
+ }
+ }
+ catch (exception &e) {
+ // release the statement handle
+ #ifdef SQLITE_SUPPORT
+ if (!fODBCdsP->fUseSQLite)
+ #endif
+ {
+ #ifdef ODBCAPI_SUPPORT
+ SafeSQLFreeHandle(SQL_HANDLE_STMT,statement);
+ #endif
+ }
+ throw;
+ }
+ }
+} // TODBCFieldProxy::fetchBlob
+
+
+// returns size of entire blob
+size_t TODBCFieldProxy::getBlobSize(TStringField *aFieldP)
+{
+ fetchBlob(0);
+ return fValue.size();
+} // TODBCFieldProxy::getBlobSize
+
+
+// read from Blob from specified stream position and update stream pos
+size_t TODBCFieldProxy::readBlobStream(TStringField *aFieldP, size_t &aPos, void *aBuffer, size_t aMaxBytes)
+{
+ if (fValue.size()<=aPos || !fFetched) { // <=aPos instead of <
+ // we need to read the body
+ fetchBlob(aPos+aMaxBytes);
+ }
+ // now copy from our value
+ if (aPos>fValue.size()) return 0;
+ if (aPos+aMaxBytes>fValue.size()) aMaxBytes=fValue.size()-aPos;
+ if (aMaxBytes==0) return 0;
+ // copy data from ODBC answer buffer to caller's buffer
+ memcpy(aBuffer,fValue.c_str()+aPos,aMaxBytes);
+ aPos+= aMaxBytes;
+ return aMaxBytes; // return number of bytes actually read
+} // TODBCFieldProxy::readBlobStream
+
+#endif // STREAMFIELD_SUPPORT
+
+
+} // namespace
+
+#endif // SQL_SUPPORT
+
+// eof
diff --git a/src/DB_interfaces/odbc_db/odbcapids.h b/src/DB_interfaces/odbc_db/odbcapids.h
new file mode 100755
index 0000000..39a1afd
--- /dev/null
+++ b/src/DB_interfaces/odbc_db/odbcapids.h
@@ -0,0 +1,689 @@
+/**
+ * @File odbcapids.h
+ *
+ * @Author Lukas Zeller (luz@synthesis.ch)
+ *
+ * @brief TODBCApiDS
+ * ODBC based datastore API implementation
+ * @Note Currently also contains what will later become TCustomImplDS
+ *
+ * Copyright (c) 2001-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ * @Date 2005-09-16 : luz : created from odbcdbdatastore
+ */
+
+#include "prefix_file.h"
+
+#ifndef ODBCAPIDS_H
+#define ODBCAPIDS_H
+
+#ifdef SQL_SUPPORT
+
+// includes
+#include "odbcdb.h"
+#include "stdlogicds.h"
+#include "multifielditem.h"
+#include "odbcapiagent.h"
+
+
+using namespace sysync;
+
+namespace sysync {
+
+#ifdef SCRIPT_SUPPORT
+// publish as derivates might need it
+extern const TFuncTable ODBCDSFuncTable2;
+#endif
+
+
+// the datastore config
+
+
+// special ID generation modes
+typedef enum {
+ sidm_none, // none
+ sidm_unixmsrnd6 // UNIX milliseconds and 6-digit random
+} TSpecialIDMode;
+const sInt16 numSpecialIDModes = sidm_unixmsrnd6-sidm_none+1;
+
+
+
+
+// ODBC variant of field map
+class TODBCFieldMapItem : public TFieldMapItem
+{
+ typedef TFieldMapItem inherited;
+public:
+ TODBCFieldMapItem(const char *aElementName, TConfigElement *aParentElement);
+ // - parser for extra attributes (for derived classes)
+ virtual void checkAttrs(const char **aAttributes);
+ #ifdef STREAMFIELD_SUPPORT
+ // properties
+ string fReadBlobSQL; // read field when needed trough field proxy
+ string fKeyFieldName; // if set, this field name will be used in %N when reading the record rather than the actual data field -> to get record key for array elements for proxies
+ #endif
+}; // TODBCFieldMapItem
+
+
+#ifdef ARRAYDBTABLES_SUPPORT
+
+// special field map item: Array map
+class TODBCFieldMapArrayItem : public TFieldMapArrayItem
+{
+ typedef TFieldMapArrayItem inherited;
+public:
+ TODBCFieldMapArrayItem(TCustomDSConfig *aCustomDSConfigP, TConfigElement *aParentElement);
+ virtual ~TODBCFieldMapArrayItem();
+ // SQL properties
+ // %k = record key of associated main table record
+ // %i = index of current element (0 based)
+ // %N = field name list for INSERT and SELECT
+ // %V = value list for INSERT
+ // %d([opt]fieldname#arrayindex)
+ // %dM,%tM,%M = modified date/time/timestamp
+ // %f=folderkey, %u=userkey, %t=targetkey
+ // %S = size of record (sum of all field literal sizes)
+ // - SQL for selecting all elements of the array
+ // NOTE: %N must be first in (and is probably entire) SELECT field list!
+ string fSelectArraySQL;
+ // - SQL for clearing the array, must clear all subrecords of the array
+ string fDeleteArraySQL;
+ bool fAlwaysCleanArray; // if set, fDeleteArraySQL will be executed even if master record is inserted new
+ // - SQL to insert a new array element
+ string fInsertElementSQL;
+ // Methods
+ virtual void clear();
+protected:
+ // check config elements
+ virtual bool localStartElement(const char *aElementName, const char **aAttributes, sInt32 aLine);
+ virtual void localResolve(bool aLastPass);
+}; // TODBCFieldMapArrayItem
+
+#endif
+
+
+
+class TOdbcDSConfig: public TCustomDSConfig
+{
+ typedef TCustomDSConfig inherited;
+public:
+ TOdbcDSConfig(const char* aName, TConfigElement *aParentElement);
+ virtual ~TOdbcDSConfig();
+ // properties
+ // - set this if every item modification needs to be committed separately
+ // (for DBs such as Achil where any insert will block the size accumulation
+ // record).
+ bool fCommitItems;
+ // - define how many times an insert is retried in case of ODBC error, 0=no retry
+ sInt16 fInsertRetries;
+ // - define how many times an update is retried in case of ODBC error, 0=no retry
+ sInt16 fUpdateRetries;
+ #ifdef HAS_SQL_ADMIN
+ // - statement to obtain folder key from Database, empty if none
+ // replacements: %F=foldername, %u=userkey, %d=devicekey
+ // Note: see fFolderKeyField and %f substitute to use folderkey for subselecting data
+ string fFolderKeySQL;
+ // - sqls for handling Sync Target Table
+ // - replacements: %f=folderkey, %u=userkey, %D=deviceID, %P=deviceDBpath
+ string fGetSyncTargetSQL; // must return SYNCTARGETKEY,ANCHOR,LASTSYNC
+ string fNewSyncTargetSQL;
+ // - additional replacements: %L=last sync timestamp, %dL=last sync date, %tL=last sync time
+ string fUpdateSyncTargetSQL;
+ string fDeleteSyncTargetSQL; // should also delete all associated map entries!
+ bool fSyncTimestamp; // if set, target table SELECT expects single timestamp, date & time otherwise
+ #ifdef OLD_1_0_5_CONFIG_COMPATIBLE
+ // Map table access, old version
+ // - layout of map table
+ string fMapTableName; // name
+ string fRemoteIDMapField; // remote ID
+ string fLocalIDMapField; // local ID
+ bool fStringLocalID; // if set, local ID is quoted as string literal in SQL
+ string fTargetKeyMapField; // target key
+ bool fStringTargetkey; // if set, target key is quoted as string literal in SQL
+ // Data table access, old version
+ // - name of table containing the data
+ string fLocalTableName;
+ /* never documented (only used in achil), so not supported any more
+ // - additional constant insert values (appended to INSERT field/value lists, resp.)
+ // %d([opt]fieldname#arrayindex)
+ // %dM,%tM,%M = modified date/time/timestamp
+ // %f=folderkey, %u=userkey, %t=targetkey
+ // %S = size of record (sum of all field literal sizes)
+ string fConstInsFields;
+ string fConstInsValues;
+ // - additional constant update terms (appended to UPDATE SET list)
+ // %d([opt]fieldname#arrayindex)
+ // %dM,%tM,%M = modified date/time/timestamp
+ // %f=folderkey, %u=userkey, %t=targetkey
+ // %S = size of record (sum of all field literal sizes)
+ string fConstUpdates;
+ */
+ // - name of folderkey subselection field in data table, empty if none
+ string fFolderKeyField;
+ bool fStringFolderkey; // if set, folder key is quoted as string literal in SQL
+ // - name of field which contains Local ID (GUID)
+ string fLocalIDField;
+ // - modified date & time fields
+ string fModifiedDateField; // this field is used as modified date or datetime
+ string fModifiedTimeField; // if set, this field is used as modified time
+ // - additional select clause (to be used for all selects BUT NOT INSERTS!)
+ // NOTE: can contain %x-type substitutions (see DoSQLSubstitutions())
+ // Note: was never documented so will be phased out right now
+ // string fGeneralSelectCondition;
+ #endif // OLD_1_0_5_CONFIG_COMPATIBLE
+ // Map table access, new version
+ // Substitutions:
+ // from agent's DoSQLSubstitutions:
+ // %u = userkey
+ // %d = devicekey
+ // from datastore's DoSQLSubstitutions:
+ // %f = folderkey
+ // %t = targetkey
+ // from DoMapSubstitutions:
+ // %e = entry type
+ // %k = data key (local ID)
+ // %r = remote ID (always a string)
+ // - SQL to fetch all map entries of a target: order= localid,remoteid
+ string fMapFetchAllSQL;
+ // - SQL to insert new map entry by localid
+ string fMapInsertSQL;
+ // - SQL to update existing map entry by localid
+ string fMapUpdateSQL;
+ // - SQL to delete a map entry by localid
+ string fMapDeleteSQL;
+ // Data table access, new version
+ // Substitutions:
+ // from agent's DoSQLSubstitutions:
+ // %u = userkey
+ // %d = devicekey
+ // from datastore's DoSQLSubstitutions:
+ // %f = folderkey
+ // %t = targetkey
+ // from DoDataSubstitutions:
+ // %dM,%tM,%M = modified date,time,timestamp
+ // %d([opts]fieldname#arrindex) = contents of field, opts l=lowercase, u=uppercase, a=ascii-only
+ // %S = size of all string field literal sizes
+ // %AF = filter conditions preceeded by AND (can be empty)
+ // %WF = filter conditions preceeded by WHERE (can be empty)
+ // %k = data key (local ID)
+ // %N = data field name list
+ // %aN = data field name list with all field, regardless of eventual unassigned state of fields in update statements
+ // %V = data field=value list
+ // %aV = data field=value list with all field
+ // %v = data value list
+ // %aV = data value list with all field
+ // for Array fields only:
+ // %i = 0 based index of array elements
+ #endif // HAS_SQL_ADMIN
+ // - single(!) SQL statement to fetch local ID and timestamp from data table
+ string fLocalIDAndTimestampFetchSQL;
+ bool fModifiedTimestamp; // if set, above statement returns separate date and time
+ // Note: the following data access SQL strings may consist of multiple SQL statements
+ // separated by %GO or %GO(setno). Default setno=0 without preceeding %GO(setno)
+ // - SQL statement(s) to fetch actual data fields from data table. SQL result must
+ // contain mapped fields in the same order as they appear in <fieldmap>
+ // Note: Statement(s) executed with setno==0 returning no data are interpreted
+ // as "record does not exist (any more)". Statements with setno!=0 returning
+ // no data are just ignored (assuming optional data not present)
+ string fDataFetchSQL;
+ // - SQL statement(s) to insert new data in a record, including modification date
+ string fDataInsertSQL;
+ // - SQL statement(s) to update actual data in a record, including modification date
+ string fDataUpdateSQL;
+ // - SQL statement(s) to delete a record
+ string fDataDeleteSQL;
+ // - SQL statement(s) to zap entire sync set
+ string fDataZapSQL;
+ // Data table options
+ // - try to translate filters to SQL if possible
+ bool fFilterOnDBLevel;
+ // - Literal string quoting mode
+ TQuotingModes fQuotingMode;
+ // Local ID generation
+ // - if set, fObtainNextIDSql is executed only ONCE per write phase, and
+ // IDs generated by incrementing the ID by 1 for every record added.
+ // This is NOT MULTI-USER-SAFE, but can be useful for slow DBs like FMPro
+ // Setting this excludes fObtainNextIDAfterInsert
+ bool fDetermineNewIDOnce;
+ // - if set, fObtainNewLocalIDSql is used AFTER inserting without a GUID
+ // to obtain the GUID generated by the DB (such as MS-SQL autoincrement)
+ bool fObtainNewIDAfterInsert;
+ // - This SQL query is used to obtain the local ID
+ // for adds. It is expected to return the ID in the first result column.
+ // - if (fDetermineNextIDOnce), it is executed once at implimplSyncLogin,
+ // further IDs are generated by incrementing it (for single-User DBs only!)
+ // - if (fObtainNextIDAfterInsert), this is executed AFTER inserting
+ // without a local ID specified in INSERT statement to determine DB-assigned ID
+ string fObtainNewLocalIDSql;
+ #ifdef SCRIPT_SUPPORT
+ // - This script is called when a new localID must be generated (fObtainNextIDAfterInsert == false)
+ // before adding a record. If it returns non-undefined, the return value is used as localID
+ string fLocalIDScript;
+ #endif
+ // - if This specifies the minimum ID to be used if fObtainNextIDSql
+ // returns something lower. This is also dangerous, but also needed
+ // for FMPro to keep FMPro generated IDs far (much lower) from Sync
+ // generated IDs
+ sInt32 fMinNextID;
+ // - specifies special ID calculation method
+ TSpecialIDMode fSpecialIDMode;
+ // - if set, the fDataInsertSQL statement is expected to return the new ID as
+ // the first column of the result set (for example if insert is implemented
+ // with a stored procedue)
+ bool fInsertReturnsID;
+ bool fInsertIDAsOutParam;
+ // - if set, SQLRowCount result is ignored and write statements are always assumed to be successful
+ // (unless they fail with an error, of course)
+ bool fIgnoreAffectedCount;
+ // - type of the last-modified date column
+ TDBFieldType fLastModDBFieldType;
+ // SQLite support
+ #ifdef SQLITE_SUPPORT
+ // - name of the SQLite file that contains the user data for this datastore
+ string fSQLiteFileName;
+ // - busy timeout, 0 = no wait
+ uInt32 fSQLiteBusyTimeout;
+ #endif
+ // public methods
+ // - return charset used to create SQL statments (will return UTF-8 when using UTF-16/UCS-2
+ // to allow SQL statement construction in 8-bit mode, and only converting before
+ // actually calling ODBC
+ TCharSets sqlPrepCharSet(void) { return fDataCharSet==chs_utf16 ? chs_utf8 : fDataCharSet; };
+ // - create appropriate datastore from config, calls addTypeSupport as well
+ virtual TLocalEngineDS *newLocalDataStore(TSyncSession *aSessionP);
+ #ifdef SCRIPT_SUPPORT
+ // provided to allow derivates to add API specific script functions to scripts called from CustomImplDS
+ virtual const TFuncTable *getDSFuncTableP(void) { return &ODBCDSFuncTable1; };
+ #endif
+ // factory functions for field map items
+ virtual TFieldMapItem *newFieldMapItem(const char *aElementName, TConfigElement *aParentElement)
+ { return new TODBCFieldMapItem(aElementName,aParentElement); };
+ #ifdef ARRAYDBTABLES_SUPPORT
+ virtual TFieldMapArrayItem *newFieldMapArrayItem(TCustomDSConfig *aCustomDSConfigP, TConfigElement *aParentElement)
+ { return new TODBCFieldMapArrayItem(aCustomDSConfigP,aParentElement); };
+ #endif
+protected:
+ // check config elements
+ virtual bool localStartElement(const char *aElementName, const char **aAttributes, sInt32 aLine);
+ virtual void clear();
+ virtual void localResolve(bool aLastPass);
+ #ifdef SCRIPT_SUPPORT
+ virtual void apiResolveScripts(void);
+ #endif
+}; // TOdbcDSConfig
+
+
+
+
+class TODBCApiDS: public TCustomImplDS
+{
+ friend class TODBCDSfuncs;
+ friend class TODBCCommonFuncs;
+ friend class TODBCFieldProxy;
+ typedef TCustomImplDS inherited;
+private:
+ void InternalResetDataStore(void); // reset for re-use without re-creation
+public:
+ TODBCApiDS(
+ TOdbcDSConfig *aConfigP,
+ sysync::TSyncSession *aSessionP,
+ const char *aName,
+ uInt32 aCommonSyncCapMask=0);
+ virtual void announceAgentDestruction(void);
+ virtual void dsResetDataStore(void) { InternalResetDataStore(); inherited::dsResetDataStore(); };
+ virtual ~TODBCApiDS();
+
+ /// @name apiXXXX methods defining the interface from TCustomImplDS to TXXXApi actual API implementations
+ /// @{
+ //
+ /// @brief Load admin data from ODBC database
+ /// @param aDeviceID[in] remote device URI (device ID)
+ /// @param aDatabaseID[in] local database ID
+ /// @param aRemoteDBID[in] database ID of remote device
+ /// Must search for existing target record matching the triple (aDeviceID,aDatabaseID,aRemoteDBID)
+ /// - if there is a matching record: load it
+ /// - if there is no matching record, set fFirstTimeSync=true. The implementation may already create a
+ /// new record with the key (aDeviceID,aDatabaseID,aRemoteDBID) and initialize it with the data from
+ /// the items as shown below. At least, fTargetKey must be set to a value that will allow apiSaveAdminData to
+ /// update the record. In case implementation chooses not create the record only in apiSaveAdminData, it must
+ /// buffer the triple (aDeviceID,aDatabaseID,aRemoteDBID) such that it is available at apiSaveAdminData.
+ /// If a record exists implementation must load the following items:
+ /// - fTargetKey = some key value that can be used to re-identify the target record later at SaveAdminData.
+ /// If the database implementation has other means to re-identify the target, this can be
+ /// left unassigned.
+ /// - fLastRemoteAnchor = anchor string used by remote party for last session (and saved to DB then)
+ /// - fPreviousSyncTime = anchor (beginning of session) timestamp of last session.
+ /// - fPreviousToRemoteSyncCmpRef = Reference time to determine items modified since last time sending data to remote
+ /// - fPreviousToRemoteSyncIdentifier = string identifying last session that sent data to remote (needs only be saved
+ /// if derived datastore cannot work with timestamps and has its own identifier).
+ /// - fMapTable = list<TMapEntry> containing map entries. The implementation must load all map entries
+ /// related to the current sync target identified by the triple of (aDeviceID,aDatabaseID,aRemoteDBID)
+ /// or by fTargetKey. The entries added to fMapTable must have "changed", "added" and "deleted" flags
+ /// set to false.
+ /// For resumable datastores:
+ /// - fMapTable = In addition to the above, the markforresume flag must be saved in the mapflags
+ // when it is not equal to the savedmark flag - independently of added/deleted/changed.
+ /// - fResumeAlertCode = alert code of current suspend state, 0 if none
+ /// - fPreviousSuspendCmpRef = reference time of last suspend (used to detect items modified during a suspend / resume)
+ /// - fPreviousSuspendIdentifier = identifier of last suspend (used to detect items modified during a suspend / resume)
+ /// (needs only be saved if derived datastore cannot work with timestamps and has
+ /// its own identifier)
+ /// - fPendingAddMaps = map<string,string>. The implementation must load all all pending maps (client only) into
+ /// fPendingAddMaps (and fUnconfirmedMaps must be left empty).
+ /// - fTempGUIDMap = map<string,string>. The implementation must save all entries as temporary LUID to GUID mappings
+ /// (server only)
+ virtual localstatus apiLoadAdminData(
+ const char *aDeviceID, // remote device URI (device ID)
+ const char *aDatabaseID, // database ID
+ const char *aRemoteDBID // database ID of remote device
+ );
+ /// @brief Save admin data to ODBC database
+ /// @param[in] aSessionFinished if true, this is a end-of-session save (and not only a suspend save) - but not necessarily a successful one
+ /// @param[in] aSuccessful if true, this is a successful end-of-session
+ /// Must save to the target record addressed at LoadAdminData() by the triple (aDeviceID,aDatabaseID,aRemoteDBID)
+ /// Implementation must save the following items:
+ /// - fLastRemoteAnchor = anchor string used by remote party for this session (and saved to DB then)
+ /// - fPreviousSyncTime = anchor (beginning of session) timestamp of this session.
+ /// - fPreviousToRemoteSyncCmpRef = Reference time to determine items modified since last time sending data to remote
+ /// - fPreviousToRemoteSyncIdentifier = string identifying last session that sent data to remote (needs only be saved
+ /// if derived datastore cannot work with timestamps and has its own identifier).
+ /// - fMapTable = list<TMapEntry> containing map entries. The implementation must save all map entries
+ /// that have changed, are new or are deleted. See below for additional resume requirements.
+ /// For resumable datastores:
+ /// - fMapTable = In addition to the above, the markforresume flag must be saved in the mapflags
+ // when it is not equal to the savedmark flag - independently of added/deleted/changed.
+ /// - fResumeAlertCode = alert code of current suspend state, 0 if none
+ /// - fPreviousSuspendCmpRef = reference time of last suspend (used to detect items modified during a suspend / resume)
+ /// - fPreviousSuspendIdentifier = identifier of last suspend (used to detect items modified during a suspend / resume)
+ /// (needs only be saved if derived datastore cannot work with timestamps and has
+ /// its own identifier)
+ /// - fPendingAddMaps and fUnconfirmedMaps = map<string,string>. The implementation must save all entries as
+ /// pending maps (client only). Note that fPendingAddMaps might contain temporary localIDs,
+ /// so call dsFinalizeLocalID() to ensure these are converted to final before saving.
+ /// - fTempGUIDMap = map<string,string>. The implementation must save all entries as temporary LUID to GUID mappings
+ /// (server only)
+ virtual localstatus apiSaveAdminData(bool aSessionFinished, bool aSuccessful);
+ /// read sync set IDs and mod dates.
+ /// @param[in] if set, all data fields are needed, so ReadSyncSet MAY
+ /// read items here already. Note that ReadSyncSet MAY read items here
+ /// even if aNeedAll is not set (if it is more efficient than reading
+ /// them separately afterwards).
+ virtual localstatus apiReadSyncSet(bool aNeedAll);
+ /// Zap all data in syncset (note that everything outside the sync set will remain intact)
+ virtual localstatus apiZapSyncSet(void);
+ virtual bool apiNeedSyncSetToZap(void) { return fConfigP->fDataZapSQL.empty(); }; // we need it if we don't have a zap SQL
+ /// fetch record contents from DB by localID.
+ virtual localstatus apiFetchItem(TMultiFieldItem &aItem, bool aReadPhase, TSyncSetItem *aSyncSetItemP);
+ /// add new item to datastore, returns created localID
+ virtual localstatus apiAddItem(TMultiFieldItem &aItem, string &aLocalID);
+ /// update existing item in datastore, returns 404 if item not found
+ virtual localstatus apiUpdateItem(TMultiFieldItem &aItem);
+ /// delete existing item in datastore, returns 211 if not existing any more
+ virtual localstatus apiDeleteItem(TMultiFieldItem &aItem);
+ /// end of syncset reading phase
+ virtual localstatus apiEndDataRead(void);
+ /// start of write
+ virtual localstatus apiStartDataWrite(void);
+ /// end DB data write sequence (but not yet admin data)
+ virtual localstatus apiEndDataWrite(string &aThisSyncIdentifier);
+
+ /// @}
+
+
+public:
+ /// @name dsXXXX virtuals defined by TLocalEngineDS
+ /// These are usually designed such that they should always call inherited::dsXXX to let the entire chain
+ /// of ancestors see the calls
+ /// @{
+ //
+ /// end of message handling
+ virtual void dsEndOfMessage(void);
+ /// inform logic of coming state change
+ virtual localstatus dsBeforeStateChange(TLocalEngineDSState aOldState,TLocalEngineDSState aNewState);
+ /// inform logic of happened state change
+ virtual localstatus dsAfterStateChange(TLocalEngineDSState aOldState,TLocalEngineDSState aNewState);
+ // log datastore sync result
+ #ifdef HAS_SQL_ADMIN
+ // - Called at end of sync with this datastore
+ virtual void dsLogSyncResult(void);
+ #endif // HAS_SQL_ADMIN
+ /// @}
+
+
+ /// @name dsHelpers private/protected helper routines
+ /// @{
+ //
+private:
+ #ifdef HAS_SQL_ADMIN
+ /// update sync target head record
+ localstatus updateSyncTarget(SQLHSTMT aStatement, bool aSessionFinishedSuccessfully);
+ /// update map changes from memory list into actual map table
+ localstatus updateODBCMap(SQLHSTMT aStatement, bool aSessionFinishedSuccessfully);
+ // - log substitutions
+ virtual void DoLogSubstitutions(string &aLog,bool aPlaintext);
+ #endif // HAS_SQL_ADMIN
+
+ /// @}
+
+
+ // Publics for context funcs
+ // - generate new LocalID into fLastGeneratedLocalID.
+ void nextLocalID(TSpecialIDMode aMode,SQLHSTMT aStatement);
+ // agent
+ TODBCApiAgent *fAgentP; // access to agent (casted fSessionP for convenience)
+ // config (typed pointers for convenience)
+ TOdbcDSConfig *fConfigP;
+ TOdbcAgentConfig *fAgentConfigP;
+
+protected:
+ // some vars
+ string fLastGeneratedLocalID;
+ #ifdef SCRIPT_SUPPORT
+ string fSQLFilter; // run-time calculated filter expression for SELECT WHERE clause
+ SQLHSTMT fODBCScriptStatement; // statement used for executing SQL in scripts
+ virtual void apiRebuildScriptContexts(void);
+ #endif
+ #ifdef OBJECT_FILTERING
+ // - returns true if DB implementation can filter the standard filters
+ // (LocalDBFilter, TargetFilter and InvisibleFilter) during database fetch
+ // - otherwise, fetched items will be filtered after being read from DB.
+ virtual bool dsFilteredFetchesFromDB(bool aFilterChanged=false);
+ #endif
+protected:
+ #ifdef ODBCAPI_SUPPORT
+ // ODBC
+ // - get (session level) ODBC handle
+ SQLHDBC getODBCConnectionHandle(void);
+ // - check for connection-level error
+ void checkConnectionError(SQLRETURN aResult);
+ #ifdef HAS_SQL_ADMIN
+ // - issue single (or multiple) map access statements
+ // NOTE: if all return SQL_NODATA, function will return false.
+ bool IssueMapSQL(
+ SQLHSTMT aStatement,
+ const string &aSQL,
+ const char *aComment,
+ TMapEntryType aEntryType,
+ const char *aLocalID,
+ const char *aRemoteID,
+ uInt32 aMapFlags
+ );
+ // - do substitutions for map table access
+ void DoMapSubstitutions(
+ string &aSQL, // string to apply substitutions to
+ TMapEntryType aEntryType, // the entry type
+ const char *aLocalID, // local ID
+ const char *aRemoteID, // remote ID
+ uInt32 aMapFlags // map flags
+ );
+ #endif // HAS_SQL_ADMIN
+ #endif // ODBCAPI_SUPPORT
+ // Generic SQL
+ // - get next statement and setno from statement list (may begin with %GO(setno))
+ bool getNextSQLStatement(const string &aSQL, sInt16 &aStartAt, string &aOneSQL, uInt16 &aSetNo);
+ // - execute single SQL statements
+ bool execSQLStatement(SQLHSTMT aStatement, string &aSQL, bool aNoDataAllowed, const char *aComment, bool aForData);
+ // - issue single (or multiple) data write (or delete) statements
+ // NOTE: if all return SQL_NODATA, function will return false.
+ bool IssueDataWriteSQL(
+ SQLHSTMT aStatement,
+ const string &aSQL,
+ const char *aComment,
+ bool aForUpdate, // for update
+ TFieldMapList &aFieldMapList, // field map list for %N,%V and %v
+ TMultiFieldItem *aItemP // item to read values and localid from
+ );
+ // - do standard substitutions (%x-type) in a SQL string
+ void DoSQLSubstitutions(string &aSQL);
+ // insert additional data selection conditions, if any.
+ bool appendFiltersClause(string &aSQL, const char *linktext);
+public:
+ // - do substitutions for data table access
+ void DoDataSubstitutions(
+ string &aSQL, // string to apply substitutions to
+ TFieldMapList &aFieldMapList, // field map list for %N,%V and %v
+ uInt16 aSetNo, // Map Set number
+ bool aForWrite, // set if read-enabled or write-enabled fields are shown in %N,%V and %v
+ bool aForUpdate=false, // set if for update (only assigned fields will be shown if !fUpdateAllFields)
+ TMultiFieldItem *aItemP=NULL, // item to read values and localid from
+ sInt16 aRepOffset=0, // array index/repeat offset
+ bool *aAllEmptyP=NULL, // true if all %V or %v empty
+ bool *aDoneP=NULL // true if no data at specified aRepOffset
+ );
+ // Parameter handling
+ // - reset all mapped parameters
+ void resetSQLParameterMaps(void);
+ // - add parameter definition to the datastore level parameter list
+ void addSQLParameterMap(
+ bool aInParam, bool aOutParam,
+ TParamMode aParamMode,
+ TODBCFieldMapItem *aFieldMapP,
+ TMultiFieldItem *aItemP,
+ sInt16 aRepOffset
+ );
+ // - prepare SQL statment as far as needed for parameter binding
+ void prepareSQLStatement(SQLHSTMT aStatement, cAppCharP aSQL, bool aForData, cAppCharP aComment);
+ // - bind parameters (and values for IN-Params) to the statement
+ void bindSQLParameters(SQLHSTMT aStatement, bool aForData);
+ // - save out parameter values and clean up
+ void saveAndCleanupSQLParameters(SQLHSTMT aStatement, bool aForData);
+ // - fetch next row from SQL statement, returns true if there is any
+ bool fetchNextRow(SQLHSTMT aStatement, bool aForData);
+ // - SQL statement complete, finalize it
+ void finalizeSQLStatement(SQLHSTMT aStatement, bool aForData);
+protected:
+ #ifdef OBJECT_FILTERING
+ // - convert filter expression into SQL WHERE clause condition
+ bool appendFilterConditions(string &aSQL, const string &aFilter);
+ // - appends logical condition term to SQL from filter string
+ bool appendFilterTerm(string &aSQL, const char *&aPos, const char *aStop);
+ #endif
+ #ifdef ODBCAPI_SUPPORT
+ // - get one or two successive columns as time stamp depending on config
+ void getColumnsAsTimestamp(
+ SQLHSTMT aStatement,
+ sInt16 &aColNumber, // will be updated by 1 or 2
+ bool aCombined,
+ lineartime_t &aTimestamp,
+ timecontext_t aTargetContext
+ );
+ #endif
+ // - get a column as integer based timestamp
+ lineartime_t dbIntToLineartimeAs(
+ sInt64 aDBInt, TDBFieldType aDbfty,
+ timecontext_t aTargetContext
+ );
+ // - helpers for mapping field values
+ bool addFieldNameList(string &aSQL, bool aForWrite, bool aForUpdate, bool aAssignedOnly, TMultiFieldItem *aItemP, TFieldMapList &fml,uInt16 aSetNo, char aFirstSep=0);
+ bool addFieldNameValueList(string &aSQL, bool aAssignedOnly, bool &aNoData, bool &aAllEmpty, TMultiFieldItem &aItem, bool aForUpdate,TFieldMapList &fml, uInt16 aSetNo, sInt16 aRepOffset=0, char aFirstSep=0);
+ void fillFieldsFromSQLResult(SQLHSTMT aStatement, sInt16 &aColIndex, TMultiFieldItem &aItem, TFieldMapList &fml, uInt16 aSetNo, sInt16 aRepOffset=0);
+ // - add field value as literal to SQL text
+ bool appendFieldsLiteral(TMultiFieldItem &aItem, sInt16 aFid, sInt16 aRepOffset,TODBCFieldMapItem &aFieldMapping, string &aSQL);
+public:
+ bool appendFieldValueLiteral(TItemField &aField,TDBFieldType aDBFieldType, uInt32 aMaxSize, bool aIsFloating, string &aSQL);
+protected:
+ #ifdef ARRAYDBTABLES_SUPPORT
+ // array table field access basic functions
+ void readArray(SQLHSTMT aStatement, TMultiFieldItem &aItem, TFieldMapArrayItem *aMapItemP);
+ void writeArray(bool aDelete, bool aInsert, SQLHSTMT aStatement, TMultiFieldItem &aItem, TFieldMapArrayItem *aMapItemP);
+ // accessing all array fields of a item
+ void readArrayFields(SQLHSTMT aStatement, TMultiFieldItem &aItem, TFieldMapList &fml);
+ void writeArrayFields(SQLHSTMT aStatement, TMultiFieldItem &aItem, TFieldMapList &fml, bool aInsert);
+ void deleteArrayFields(SQLHSTMT aStatement, TMultiFieldItem &aItem, TFieldMapList &fml);
+ #endif
+ // other private utils
+ // - private helper: start writing item
+ void startWriteItem(void);
+ // - private helper: end writing item
+ void endWriteItem(void);
+ // - create local ID with special algorithm
+ void createLocalID(string &aLocalID,TSpecialIDMode aSpecialIDMode);
+protected:
+ // Fully ODBC based DB
+ bool fODBCAdminData; // set if admin data is actually in ODBC tables (and not managed in derived class' implementation)
+ // StartDataRead/GetItem vars
+ sInt32 fNextLocalID;
+ // Parameters
+ TParameterMapList fParameterMaps;
+ // Sum of all string sizes per record
+ sInt32 fRecordSize;
+ // Filtering
+ #ifdef OBJECT_FILTERING
+ bool fFilterExpressionTested;
+ bool fFilterWorksOnDBLevel; // set if filter can be executed by DB
+ #endif
+ // ODBC
+ #ifdef ODBCAPI_SUPPORT
+ SQLHDBC fODBCConnectionHandle;
+ #endif
+ // Note: %%% we need the statements as dummies even if ODBC is not compiled in
+ SQLHSTMT fODBCReadStatement;
+ SQLHSTMT fODBCWriteStatement;
+ // SQLite
+ #ifdef SQLITE_SUPPORT
+ bool fUseSQLite;
+ sqlite3 *fSQLiteP;
+ sqlite3_stmt *fSQLiteStmtP;
+ int fStepRc;
+ #endif
+}; // TODBCApiDS
+
+
+#ifdef STREAMFIELD_SUPPORT
+
+// proxy for loading blobs
+class TODBCFieldProxy : public TBlobProxy
+{
+ typedef TBlobProxy inherited;
+public:
+ TODBCFieldProxy(TODBCApiDS *aODBCdsP, TODBCFieldMapItem *aFieldMapP, const char *aMasterKey, const char *aDetailKey);
+ virtual ~TODBCFieldProxy();
+ // - returns size of entire blob
+ virtual size_t getBlobSize(TStringField *aFieldP);
+ // - read from Blob from specified stream position and update stream pos
+ virtual size_t readBlobStream(TStringField *aFieldP, size_t &aPos, void *aBuffer, size_t aMaxBytes);
+ // - dependency on a local ID
+ virtual void setParentLocalID(const char *aParentLocalID) { fMasterKey=aParentLocalID; };
+private:
+ // fetch BLOB from DPAPI
+ void fetchBlob(size_t aNeededSize);
+ // Vars
+ TODBCApiDS *fODBCdsP; // datastore which can be asked to retrieve data
+ TODBCFieldMapItem *fFieldMapP; // field map item config (contains SQL needed to fetch BLOB field)
+ bool fIsStringBLOB; // if set, the blob must be treated as a string (applying DB charset conversions)
+ string fMasterKey; // key of master (main) record containing the field to be retrieved
+ string fDetailKey; // optional key of detail record, available only when "keyfield" attribute is present in <map>
+ bool fFetched; // already fetched (if value is empty, fBlobBuffer may still be NULL)
+ string fValue; // string or binary value
+}; // TODBCFieldProxy
+
+#endif
+
+
+} // namespace sysync
+
+#endif // ODBCAPIDS_H
+
+#endif // SQL_SUPPORT
+
+// eof
diff --git a/src/DB_interfaces/odbc_db/odbcdb.h b/src/DB_interfaces/odbc_db/odbcdb.h
new file mode 100755
index 0000000..0913caa
--- /dev/null
+++ b/src/DB_interfaces/odbc_db/odbcdb.h
@@ -0,0 +1,34 @@
+/*
+ * Common include file for all ODBC_H sources
+ */
+
+#ifndef ODBCDB_H
+#define ODBCDB_H
+
+
+// precompiled portion
+#include "odbcdb_precomp.h"
+
+// derived defines and sanity checks
+#ifdef ODBC_SUPPORT
+ #error "ODBC_SUPPORT no longer exists, is now called SQL_SUPPORT. ODBCAPI_SUPPORT exists."
+#endif
+// SQL based admin
+#if defined (SQL_SUPPORT) && defined(ODBCAPI_SUPPORT) && !defined(BASED_ON_BINFILE_CLIENT)
+ #define HAS_SQL_ADMIN 1
+#endif
+
+
+// other common includes
+#include "sysync.h"
+
+// Items used by ODBC DB interface
+#include "multifielditem.h"
+#include "mimediritemtype.h"
+
+#define TEXTMAP_FILE_EXTENSION ".txt"
+#define USERAUTH_FILE_EXTENSION ".txt"
+
+#endif
+
+/* eof */
diff --git a/src/DB_interfaces/odbc_db/odbcdb_precomp.h b/src/DB_interfaces/odbc_db/odbcdb_precomp.h
new file mode 100755
index 0000000..71790da
--- /dev/null
+++ b/src/DB_interfaces/odbc_db/odbcdb_precomp.h
@@ -0,0 +1,56 @@
+/*
+ * precompiled/precompilable headers
+ */
+
+#ifndef ODBCDB_PRECOMP_H
+#define ODBCDB_PRECOMP_H
+
+
+/* Specific headers for ODBC DB interface
+ */
+
+
+#ifdef ODBCAPI_SUPPORT
+ // Real ODBC support, include headers
+ #ifdef _WIN32
+
+ #include <windows.h>
+ #include <odbcinst.h>
+ #include <sqlext.h>
+
+ #else
+ // ODBC HEADERS (MacOSX and Linux: in /usr/include)
+ #include <sql.h>
+ #include <sqlext.h>
+
+ #ifdef OBDC_UNICODE
+ // unicode ODBC
+ #include <sqlucode.h>
+ #endif
+
+ // it seems that SQLLEN is not defined on all platforms, in particular, Mac OS X
+ #ifdef MACOSX
+ #ifndef SQLLEN
+ #define SQLLEN SQLINTEGER
+ #endif // SQLLEN
+ #endif // MACOSX
+
+ #endif // not _WIN32
+
+#else
+ // Some fake definitions to make SQLite-only versions compile w/o having ODBC headers
+
+ #define HSTMT long
+ #define SQLHSTMT HSTMT
+ #define SQL_NULL_HANDLE 0
+ #define SQLRETURN long
+ #define SQL_NULL_DATA (-1)
+ #define SQLLEN long
+ #define SQL_SUCCESS 0
+
+#endif // ODBCAPI_SUPPORT
+
+
+#endif // ODBCDB_PRECOMP_H
+
+/* eof */
diff --git a/src/Makefile.am.in b/src/Makefile.am.in
new file mode 100644
index 0000000..d8c402b
--- /dev/null
+++ b/src/Makefile.am.in
@@ -0,0 +1,139 @@
+# The Makefile.am is generated from Makefile.am.in by autogen.sh
+# because the list of files to compile has to be determined
+# dynamically.
+#
+# When adding new files or changing options rerun autogen.sh.
+# Beware that changes made to Makefile.am will get lost, always
+# edit Makefile.am.in! The following rule ensures that make
+# itself will rebuild its makefiles when Makefile.am.in is edited.
+$(srcdir)/Makefile.am: $(srcdir)/Makefile.am.in $(srcdir)/gen-makefile-am.sh
+ cd $(srcdir) && sh ./gen-makefile-am.sh
+
+CLEANFILES =
+EXTRA_DIST =
+
+lib_LTLIBRARIES = libsmltk.la libsynthesis.la libsynthesissdk.la
+nobase_include_HEADERS = @LIBSYNTHESISSDK_HEADERS@ synthesis/target_options.h
+
+# Configuration header files are searched in this order:
+# - Targets/ReleasedProducts/autotools: files only used when compiling with autotools
+# - Targets/ReleasedProducts/[clientEngine_opensource_linux|SDK]: files also used with Synthesis Linux makefile
+#
+# Always included first via -include:
+# - config.h: configure result (top directory)
+# - Targets/ReleasedProducts/clientEngine_opensource_linux/clientengine_demo_x86_linux_prefix.h
+#
+# Included as needed:
+# - define.h: hard-coded SyncML Toolkit config (from search path above)
+
+libsynthesis_la_SOURCES = @LIBSYNTHESIS_SOURCES@
+libsynthesis_la_CPPFLAGS = -I$(srcdir)/sysync_SDK/Sources
+LIBSYNTHESIS_CFLAGS = \
+ -include $(top_builddir)/config.h \
+ -include $(srcdir)/Targets/ReleasedProducts/clientEngine_opensource_linux/clientengine_demo_x86_linux_prefix.h \
+ -I$(srcdir)/Targets/ReleasedProducts/clientEngine_autotools/ \
+ -I$(srcdir)/Targets/ReleasedProducts/clientEngine_opensource_linux/ \
+ -I$(srcdir)/platform_adapters/linux/ \
+ -I$(srcdir)/platform_adapters/unix_common/ \
+ -I$(srcdir)/platform_adapters/ \
+ -I$(srcdir)/syncapps/clientEngine_custom/ \
+ -I$(srcdir)/sysync/ \
+ -I$(srcdir)/Transport_interfaces/engine/ \
+ -I$(srcdir)/ \
+ -I$(srcdir)/syncml_tk/src/sml/inc/ \
+ -I$(srcdir)/syncml_tk/src/sml/lib/inc/ \
+ -I$(srcdir)/syncml_tk/src/sml/lib/ \
+ -I$(srcdir)/syncml_tk/src/sml/mgr/inc/ \
+ -I$(srcdir)/syncml_tk/src/sml/mgr/ \
+ -I$(srcdir)/syncml_tk/src/sml/wsm/inc/ \
+ -I$(srcdir)/syncml_tk/src/sml/xlt/inc \
+ -I$(srcdir)/syncml_tk/src/sml/xlt/all \
+ -I$(srcdir)/DB_interfaces/odbc_db/ \
+ -I$(srcdir)/DB_interfaces/api_db/ \
+ -I$(srcdir)/sysync_SDK/Sources/ \
+ $(PCRE_CFLAGS) $(SQLITE3_CFLAGS) $(ZLIB_CFLAGS) $(XMLPARSE_CFLAGS) $(LIBICAL_CFLAGS) $(LIBECAL_CFLAGS)
+libsynthesis_la_CFLAGS = $(LIBSYNTHESIS_CFLAGS)
+libsynthesis_la_CXXFLAGS = $(LIBSYNTHESIS_CFLAGS)
+libsynthesis_la_LIBADD = $(PCRE_LIBS) $(SQLITE3_LIBS) $(XMLPARSE_LIBS) $(LIBICAL_LIBS) $(LIBECAL_LIBS) libsmltk.la
+if COND_XMLPARSE
+LIBSYNTHESIS_CFLAGS += -I$(srcdir)/expat -I$(srcdir)/expat/xmltok -I$(srcdir)/expat/xmlparse
+libsynthesis_la_SOURCES += \
+ expat/xmlparse/xmlparse.c \
+ expat/xmlparse/xmlparse.h \
+ expat/xmltok/ascii.h \
+ expat/xmltok/asciitab.h \
+ expat/xmltok/iasciitab.h \
+ expat/xmltok/latin1tab.h \
+ expat/xmltok/nametab.h \
+ expat/xmltok/utf8tab.h \
+ expat/xmltok/xmldef.h \
+ expat/xmltok/xmlrole.c \
+ expat/xmltok/xmlrole.h \
+ expat/xmltok/xmltok.c \
+ expat/xmltok/xmltok.h \
+ expat/xmltok/xmltok_impl.h
+EXTRA_DIST += \
+ expat/xmltok/xmltok_impl.c \
+ expat/xmltok/xmltok_ns.c
+else
+# add path to xmlparse.h which includes the system's xmltok/xmlparse.h
+LIBSYNTHESIS_CFLAGS += -I$(srcdir)/Targets/ReleasedProducts/clientEngine_autotools/systemxml
+endif
+
+# versioning:
+# CURRENT - most recent interface version
+# REVISION - minor version number of that interface implementation
+# AGE - number of previous interface versions supported in addition
+# to the current one
+#
+# When adding to the interface, increment CURRENT and AGE and reset
+# REVISION to zero. When breaking the interface in a backwards
+# incompatible way, increment CURRENT and reset AGE and REVISION to
+# zero. When fixing something without interface change, increment
+# REVISION.
+ENGINE_CURRENT = 0
+ENGINE_REVISION = 0
+ENGINE_AGE = 0
+libsynthesis_la_LDFLAGS = -version-info $(ENGINE_CURRENT):$(ENGINE_REVISION):$(ENGINE_AGE) \
+ -Wl,--version-script=$(srcdir)/synthesis-linker.map
+libsynthesis_la_DEPENDENCIES = $(srcdir)/synthesis-linker.map libsmltk.la
+
+libsynthesissdk_la_LIBADD = libsynthesis.la
+libsynthesissdk_la_LDFLAGS = -static
+libsynthesissdk_la_SOURCES = @LIBSYNTHESISSDK_HEADERS@
+if COND_STATIC
+libsynthesissdk_la_SOURCES += @LIBSYNTHESISSDK_SOURCES_SDK_ONLY@
+else
+libsynthesissdk_la_SOURCES += @LIBSYNTHESISSDK_SOURCES_BOTH@
+endif
+libsynthesissdk_la_CPPFLAGS = \
+ -I$(srcdir)/sysync_SDK/Sources \
+ -I$(srcdir)/Targets/ReleasedProducts/SDK
+
+# All files needed by libsynthesissdk.a come from either
+# sysync_SDK/Sources (shared between engine and SDK) or
+# sysync_SDK/SDK (only in SDK). We patch the files so that
+# they include their own files via #include "synthesis/..."
+vpath %.cpp $(srcdir)/sysync_SDK/Sources $(srcdir)/sysync_SDK/SDK # $(srcdir)/Targets/ReleasedProducts/clientEngine_opensource_linux $(srcdir)/syncapps/clientEngine_custom $(srcdir)/sysync $(srcdir)/platform_adapters $(srcdir)
+vpath %.c $(srcdir)/sysync_SDK/Sources $(srcdir)/sysync_SDK/SDK # $(srcdir)/Targets/ReleasedProducts/clientEngine_opensource_linux $(srcdir)/syncapps/clientEngine_custom $(srcdir)/sysync $(srcdir)/platform_adapters $(srcdir)
+vpath %.h $(srcdir)/sysync_SDK/Sources $(srcdir)/sysync_SDK/SDK # $(srcdir)/Targets/ReleasedProducts/clientEngine_opensource_linux $(srcdir)/syncapps/clientEngine_custom $(srcdir)/sysync $(srcdir)/platform_adapters $(srcdir)
+@LIBSYNTHESISSDK_HEADERS@: synthesis/%: %
+ mkdir -p synthesis
+ sed -e 's;# *include *";#include "synthesis/;' -e 's/defined(HAVE_STDINT_H)/1/' $< >$@
+
+synthesis/target_options.h: $(srcdir)/Targets/ReleasedProducts/SDK/target_options.h
+ mkdir -p synthesis
+ cp $< $@
+
+# ensure that the header file is in place before compiling the SDK
+@LIBSYNTHESISSDK_SOURCES@: synthesis/target_options.h
+
+# libsmltk: compiled just like libsynthesis
+libsmltk_la_SOURCES = @LIBSMLTK_SOURCES@
+libsmltk_la_CFLAGS = $(LIBSYNTHESIS_CFLAGS)
+libsmltk_la_CXXFLAGS = $(LIBSYNTHESIS_CFLAGS)
+
+# versioning: same as of engine! changes in libsmltk are not tracked separately.
+libsmltk_la_LDFLAGS = -version-info $(ENGINE_CURRENT):$(ENGINE_REVISION):$(ENGINE_AGE) \
+ -Wl,--version-script=$(srcdir)/smltk-linker.map
+libsmltk_la_DEPENDENCIES = $(srcdir)/smltk-linker.map
diff --git a/src/Targets/ReleasedProducts/SDK/target_options.h b/src/Targets/ReleasedProducts/SDK/target_options.h
new file mode 100644
index 0000000..02dfeb0
--- /dev/null
+++ b/src/Targets/ReleasedProducts/SDK/target_options.h
@@ -0,0 +1,55 @@
+/*
+ * File: target_options.h
+ *
+ * Author: Beat Forster (bfo@synthesis.ch),
+ * Patrick Ohly (patrick.ohly@intel.com)
+ *
+ * Programming interface between Synthesis SyncML engine
+ * and a database structure or client.
+ *
+ * Options for the client side which links against
+ * libsynthesissdk.
+ *
+ * Copyright (c) 2009-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ */
+
+#ifndef TARGET_OPTIONS_H
+#define TARGET_OPTIONS_H
+
+
+/* - find out target platform */
+#ifdef __MACH__
+ #define MACOSX
+#else
+ #if defined __MWERKS__ || defined _MSC_VER
+ #ifndef _WIN32
+ #define _WIN32
+ #endif
+ #else
+ #define LINUX
+ #endif
+#endif
+
+
+/* - we are not at the SyncML engine's side here */
+#undef SYSYNC_ENGINE
+
+/* - but we link directly to the module */
+#define DBAPI_LINKED 1
+
+/*
+ * The libsynthesis shared library uses SySync_ as prefix for C
+ * functions. TEngineModuleBridge checks for the name with and without
+ * the prefix, so clients are compatible with the current shared
+ * libraries and (potentially older) commercial releases.
+ */
+#define SYSYNC_EXTERNAL(_x) SySync_ ## _x
+#define SYSYNC_PREFIX "SySync_"
+
+/* activate debug output */
+#define SYDEBUG 2
+
+
+#endif /* TARGET_OPTIONS_H */
+/* eof */
diff --git a/src/Targets/ReleasedProducts/clientEngine_DEMO_linux/clientengine_demo++.pch b/src/Targets/ReleasedProducts/clientEngine_DEMO_linux/clientengine_demo++.pch
new file mode 100755
index 0000000..14d8002
--- /dev/null
+++ b/src/Targets/ReleasedProducts/clientEngine_DEMO_linux/clientengine_demo++.pch
@@ -0,0 +1,50 @@
+/* precompiled headers source file
+ * ===============================
+ *
+ * CVS:
+ * $Author: bfo $
+ * $Date: 2007/11/08 15:58:02 $
+ * $Revision: 1.1 $
+ * $Source: /usr/local/cvsroot/sysync/Source/Targets/ReleasedProducts/clientEngine_DEMO_linux/clientengine_demo++.pch,v $
+ * $State: Exp $
+ * $Name: $ (Tag)
+ * $Locker: $ (who has reserved checkout)
+ * Log:
+ * $Log: clientengine_demo++.pch,v $
+ * Revision 1.1 2007/11/08 15:58:02 bfo
+ * project files added
+ *
+ * Revision 1.1 2007/10/02 10:12:57 luz
+ * Added clientEngine_DEMO target to xcode
+ *
+ * Revision 1.1 2007/09/13 18:55:30 luz
+ * New DEMO client engine (SQLite, TextDB but no ODBC nor DBPlugins) release target
+ *
+ * Revision 1.1 2007/09/05 14:03:05 luz
+ * client engine with custom datastores (SQL, ODBC, SQLite and PluginAPI)
+ *
+ * Revision 1.2 2007/04/25 11:59:30 luz
+ * Moved platform specific includes to syncclient_outlook_precomp.h - it belongs there so that file can be used in MW precompiled header source files (.pch) as well as in prefix_file.h for VC++.
+ *
+ * Revision 1.1 2004/02/04 14:24:06 luz
+ * 2.0.5.3 s2g/ONE client release (and Oracle release candidate) checkin
+ *
+ *
+ */
+
+// SySync is a C++ project, make sure precomp headers
+// are compiled as C++!
+#pragma cplusplus on
+
+// determine target file name
+#pragma precompile_target "clientengine_demo_x86_linux++.mch"
+
+
+// include all headers that are suitable for precompiled use
+
+// - target options can incfluence everything
+#include "target_options.h"
+// - precompilable headers
+#include "clientengine_custom_precomp.h"
+
+// eof
diff --git a/src/Targets/ReleasedProducts/clientEngine_DEMO_linux/clientengine_demo.pch b/src/Targets/ReleasedProducts/clientEngine_DEMO_linux/clientengine_demo.pch
new file mode 100755
index 0000000..270327a
--- /dev/null
+++ b/src/Targets/ReleasedProducts/clientEngine_DEMO_linux/clientengine_demo.pch
@@ -0,0 +1,43 @@
+/* precompiled headers source file
+ * ===============================
+ *
+ * CVS:
+ * $Author: bfo $
+ * $Date: 2007/11/08 15:58:02 $
+ * $Revision: 1.1 $
+ * $Source: /usr/local/cvsroot/sysync/Source/Targets/ReleasedProducts/clientEngine_DEMO_linux/clientengine_demo.pch,v $
+ * $State: Exp $
+ * $Name: $ (Tag)
+ * $Locker: $ (who has reserved checkout)
+ * Log:
+ * $Log: clientengine_demo.pch,v $
+ * Revision 1.1 2007/11/08 15:58:02 bfo
+ * project files added
+ *
+ * Revision 1.1 2007/10/02 10:12:57 luz
+ * Added clientEngine_DEMO target to xcode
+ *
+ * Revision 1.1 2007/09/13 18:55:30 luz
+ * New DEMO client engine (SQLite, TextDB but no ODBC nor DBPlugins) release target
+ *
+ * Revision 1.1 2007/09/05 14:03:05 luz
+ * client engine with custom datastores (SQL, ODBC, SQLite and PluginAPI)
+ *
+ *
+ */
+
+// C-version for SyncML toolkit files
+#pragma cplusplus off
+
+// determine target file name
+#pragma precompile_target "clientengine_demo_x86_linux.mch"
+
+// include all headers that are suitable for precompiled
+// C version use
+// - target options can incfluence everything
+#include "target_options.h"
+
+// standard SyncML TK stuff
+#include "smltk_precomp.h"
+
+// eof
diff --git a/src/Targets/ReleasedProducts/clientEngine_DEMO_linux/clientengine_demo_x86_linux_prefix.h b/src/Targets/ReleasedProducts/clientEngine_DEMO_linux/clientengine_demo_x86_linux_prefix.h
new file mode 100644
index 0000000..530f4d5
--- /dev/null
+++ b/src/Targets/ReleasedProducts/clientEngine_DEMO_linux/clientengine_demo_x86_linux_prefix.h
@@ -0,0 +1,23 @@
+/* prefix file
+ * ===========
+ *
+ */
+
+#ifdef __cplusplus
+ // include all headers that are suitable for precompiled use
+ // - target options can incfluence everything
+ #include "target_options.h"
+ // - platform specifics
+ #include "platform_headers.h"
+ // - precompilable headers
+ #include "clientengine_custom_precomp.h"
+#else
+ // include all headers that are suitable for precompiled
+ // C version use
+ // - target options can incfluence everything
+ #include "target_options.h"
+ // - platform specifics
+ #include "platform_headers.h"
+#endif
+
+/* eof */
diff --git a/src/Targets/ReleasedProducts/clientEngine_DEMO_linux/define.h b/src/Targets/ReleasedProducts/clientEngine_DEMO_linux/define.h
new file mode 100755
index 0000000..42b2247
--- /dev/null
+++ b/src/Targets/ReleasedProducts/clientEngine_DEMO_linux/define.h
@@ -0,0 +1,119 @@
+
+// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+// NOTE: this is a local copy for this specific target
+// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+
+
+/*************************************************************************/
+/* module: Compiler Flag Definition File */
+/* file: define.h */
+/* target system: win */
+/* target OS: win */
+/*************************************************************************/
+
+/*
+ * Copyright Notice
+ * Copyright (c) Ericsson, IBM, Lotus, Matsushita Communication
+ * Industrial Co., Ltd., Motorola, Nokia, Openwave Systems, Inc.,
+ * Palm, Inc., Psion, Starfish Software, Symbian, Ltd. (2001).
+ * All Rights Reserved.
+ * Implementation of all or part of any Specification may require
+ * licenses under third party intellectual property rights,
+ * including without limitation, patent rights (such a third party
+ * may or may not be a Supporter). The Sponsors of the Specification
+ * are not responsible and shall not be held responsible in any
+ * manner for identifying or failing to identify any or all such
+ * third party intellectual property rights.
+ *
+ * THIS DOCUMENT AND THE INFORMATION CONTAINED HEREIN ARE PROVIDED
+ * ON AN "AS IS" BASIS WITHOUT WARRANTY OF ANY KIND AND ERICSSON, IBM,
+ * LOTUS, MATSUSHITA COMMUNICATION INDUSTRIAL CO. LTD, MOTOROLA,
+ * NOKIA, PALM INC., PSION, STARFISH SOFTWARE AND ALL OTHER SYNCML
+ * SPONSORS DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+ * BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
+ * HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
+ * SHALL ERICSSON, IBM, LOTUS, MATSUSHITA COMMUNICATION INDUSTRIAL CO.,
+ * LTD, MOTOROLA, NOKIA, PALM INC., PSION, STARFISH SOFTWARE OR ANY
+ * OTHER SYNCML SPONSOR BE LIABLE TO ANY PARTY FOR ANY LOSS OF
+ * PROFITS, LOSS OF BUSINESS, LOSS OF USE OF DATA, INTERRUPTION OF
+ * BUSINESS, OR FOR DIRECT, INDIRECT, SPECIAL OR EXEMPLARY, INCIDENTAL,
+ * PUNITIVE OR CONSEQUENTIAL DAMAGES OF ANY KIND IN CONNECTION WITH
+ * THIS DOCUMENT OR THE INFORMATION CONTAINED HEREIN, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH LOSS OR DAMAGE.
+ *
+ * The above notice and this paragraph must be included on all copies
+ * of this document that are made.
+ *
+ */
+
+/**
+ * File for Windows Specific Compiler Flags
+ */
+
+#ifndef _DEFINE_H
+ #define _DEFINE_H
+#define __ANSI_C__
+
+
+/* thread safety (added by luz@synthesis.ch, 2001-10-29) */
+/* Note: moved define of this to target_options.h of every target */
+#undef __MAKE_THREADSAFE
+
+/* enable Alloc helpers */
+#define __USE_ALLOCFUNCS__
+
+/* do we need WBXML (binary XML) processing ? */
+#define __SML_WBXML__
+/* do we need the capability to decode plain text tokens in WBXML? */
+#define __SML_WBXML_TEXTTOKENS__
+/* do we need XML processing ? */
+#define __SML_XML__
+/* are we using a 'light' toolkit ? */
+//#define __SML_LITE__
+/* do we use Sub DTD extensions ? */
+#define __USE_EXTENSIONS__
+/* do we need Metainformation DTD parsing ? */
+#define __USE_METINF__
+/* do we use Device Info DTD ? */
+#define __USE_DEVINF__
+
+/* which of the following optional commands should be included ? */
+
+#define ADD_SEND
+//#define ATOMIC_SEND
+//#define ATOMIC_RECEIVE
+//#define COPY_SEND
+//#define COPY_RECEIVE
+//#define EXEC_SEND
+//#define EXEC_RECEIVE
+#define GET_SEND
+#define MAP_RECEIVE
+#define MAPITEM_RECEIVE
+#define RESULT_RECEIVE
+//#define SEARCH_SEND
+//#define SEARCH_RECEIVE
+//#define SEQUENCE_SEND
+//#define SEQUENCE_RECEIVE
+
+
+/* TK: to improve interoperability and handling we
+ * switched to using .def files instead of compiler
+ * specific per function definitions. As long as we only
+ * use C this is the easiest and cleanes way
+ */
+
+#define SML_API
+#define SML_API_DEF
+#define XPT_API
+#define XPT_API_DEF
+
+
+/* Multi segment macro for Palm OS */
+#define LIB_FUNC
+#define MGR_FUNC
+#define WSM_FUNC
+#define XLT_FUNC
+
+#endif
diff --git a/src/Targets/ReleasedProducts/clientEngine_DEMO_linux/target_options.h b/src/Targets/ReleasedProducts/clientEngine_DEMO_linux/target_options.h
new file mode 100755
index 0000000..5bebb36
--- /dev/null
+++ b/src/Targets/ReleasedProducts/clientEngine_DEMO_linux/target_options.h
@@ -0,0 +1,196 @@
+/* Target options
+ * ==============
+ *
+ *
+ */
+
+// SYNCML CLIENT ENGINE LIBRARY DEMO Linux
+// #######################################
+
+// - define to make release version
+// define platform
+#define LINUX
+
+// Release version status
+#undef RELEASE_VERSION
+#define RELEASE_SYDEBUG 2 // extended DBG included
+//#define OPTIONAL_SYDEBUG 1
+
+// now include platform independent product options (which include global_options.h)
+#include "product_options.h"
+
+
+// Identification strings
+#define CUST_SYNC_MAN SYSYNC_OEM // manufactured by ourselves
+#define CUST_SYNC_MODEL "SySync Client Library DEMO Linux"
+#define CUST_SYNC_FIRMWARE NULL // no firmware
+#define CUST_SYNC_HARDWARE NULL // no hardware
+
+// String used to construct logfile names
+#define TARGETID "sysynclib_linux"
+
+// Configuration
+// =============
+
+// Default profile
+#undef HARD_CODED_SERVER_URI
+#undef HARD_CODED_DBNAMES
+#define DEFAULT_SERVER_URI ""
+#define DEFAULT_LOCALDB_PROFILE ""
+#define DEFAULT_SERVER_USER ""
+#define DEFAULT_SERVER_PASSWD ""
+#define DEFAULT_ENCODING SML_WBXML
+#define DEFAULT_TRANSPORT_USER NULL
+#define DEFAULT_TRANSPORT_PASSWD NULL
+#define DEFAULT_SOCKS_HOST NULL
+#define DEFAULT_PROXY_HOST NULL
+#define DEFAULT_DATASTORES_ENABLED false
+#define DEFAULT_EVENTS_DAYSBEFORE 30
+#define DEFAULT_EVENTS_DAYSAFTER 90
+#define DEFAULT_EVENTS_LIMITED false
+#define DEFAULT_EMAILS_LIMITED true
+#define DEFAULT_EMAILS_HDRONLY true
+#define DEFAULT_EMAILS_MAXKB 2
+#define DEFAULT_EMAILS_ONLYLAST true
+#define DEFAULT_EMAILS_DAYSBEFORE 10
+
+
+// Eval limit options
+// ==================
+
+// Note: Hard expiration date settings moved to product_options.h
+
+// - if defined, server will stop working after defined date
+#undef VERSION_COMMENTS
+#define VERSION_COMMENTS "Demo Version Expiring after " EXPIRY_DATE_STRING
+#define EXPIRES_AFTER_DATE 1
+
+// - if defined, software will stop specified number of days after
+// first use
+#undef EXPIRES_AFTER_DAYS
+// - if defined, software will run without valid license until hard
+// expiry date (EXPIRES_AFTER_DATE) and then continue only with a valid license
+#define NO_LICENSE_UNTIL_HARDEXPIRY 1
+
+// - Identification for update check and demo period
+#define SYSER_VARIANT_CODE SYSER_VARIANT_DEMO
+#define SYSER_PRODUCT_CODE SYSER_PRODCODE_CLIENT_LIB_WIN32
+#define SYSER_EXTRA_ID SYSER_EXTRA_ID_NONE
+
+
+// - if defined, software can be registered
+#define SYSER_REGISTRATION 1
+// - define allowed product codes
+#define SYSER_PRODUCT_CODE_MAIN SYSER_PRODCODE_CLIENT_DEMO // a permanent DEMO license
+#define SYSER_PRODUCT_CODE_ALT1 SYSER_PRODCODE_CLIENT_LIB_LINUX // ..or a library license for MacOSX
+#define SYSER_PRODUCT_CODE_ALT2 SYSER_PRODCODE_CLIENT_LIB_ALL // ..or for all platforms
+#define SYSER_PRODUCT_CODE_ALT3 SYSER_PRODCODE_CLIENT_LIB_DESK // ..or for desktop platforms
+#define SYSER_PRODUCT_CODE_ALT4 SYSER_PRODCODE_CLIENT_ODBC_PRO_LINUX // ..or a PRO console client for MacOSX
+// - define needed product flags
+// only licenses that are release date limited (or explicitly NOT release date limited,
+// or time limited) are allowed
+#define SYSER_NEEDED_PRODUCT_FLAGS SYSER_PRODFLAG_MAXRELDATE
+#define SYSER_FORBIDDEN_PRODUCT_FLAGS 0
+
+
+// Database support options
+// ========================
+
+// - if defined, SQL support is included
+#define SQL_SUPPORT 1
+#undef ODBCAPI_SUPPORT
+#define SQLITE_SUPPORT 1
+// - if defined, ODBC DB mapping of arrays to aux tables is supported
+#define ARRAYDBTABLES_SUPPORT 1
+
+// - if defined, SDK support is included
+#define SDK_SUPPORT 1
+#undef PLUGIN_DLL
+
+// - define what SDK modules are linked in
+//#define DBAPI_DEMO 1
+#define DBAPI_TEXT 1
+#define FILEOBJ_SUPPORT 1
+#define ADAPTITEM_SUPPORT 1
+#define JNI_SUPPORT 1 // JNI needed for GUI, but without PLUGIN_DLL, JNI is not available for DB plugins (correct for DEMO!)
+
+
+// Datastore Options
+// -----------------
+
+// - link in customimplds on top of binfile
+#define BASED_ON_BINFILE_CLIENT 1
+// - SQL and API datastores have changedetection, so build binfile for that
+#define CHANGEDETECTION_AVAILABLE 1
+// - we need a separate changelog mechanism
+#define CHECKSUM_CHANGELOG 1
+// - string localIDs with sufficiently large size
+#define STRING_LOCALID_MAXLEN 256
+
+
+// SySync options
+// ==============
+
+// - enhanced profile record
+#define ENHANCED_PROFILES_2004 1
+#define CLIENTFEATURES_2008 1
+
+// - NO support for automatic syncing (timed, IPP, server alerted...)
+#undef AUTOSYNC_SUPPORT
+// - support for intelligent push & poll (IPP)
+//#define IPP_SUPPORT 1
+//#define IPP_SUPPORT_ALWAYS 1 // regardless of license flags
+// - support for timed sync
+#undef TIMEDSYNC_SUPPORT
+// - support for WAP push alerted sync
+#undef SERVERALERT_SUPPORT
+
+// - support for Proxy
+#define PROXY_SUPPORT 1
+
+// - support for multiple profiles
+#define MULTI_PROFILE_SUPPORT 1
+
+// - show progress events
+#define PROGRESS_EVENTS 1
+
+// - we do not need to squeeze code
+#undef MINIMAL_CODE
+
+// - script with regex support
+#define SCRIPT_SUPPORT
+#define REGEX_SUPPORT
+
+// - client does not need target options
+#undef SYSYNC_TARGET_OPTIONS
+
+// - filters
+#define OBJECT_FILTERING
+
+// - client does not need superdatastores
+#undef SUPERDATASTORES
+
+
+// general options needed for email
+#define EMAIL_FORMAT_SUPPORT 1
+#define EMAIL_ATTACHMENT_SUPPORT 1
+#define ARRAYFIELD_SUPPORT 1
+
+// - if defined, stream field support will be included
+#define STREAMFIELD_SUPPORT 1
+
+// - if defined, semi-proprietary zipped-binary <data> for items (any type) can be used
+// (enabled on a by type basis in the config)
+#define ZIPPED_BINDATA_SUPPORT 1
+
+
+// - where to save application data by default (if not otherwise configured)
+// APPDATA_SUBDIR is a subdirectory of the user's "application data" dir.
+#define APPDATA_SUBDIR "synthesis.ch/SySyncLib"
+
+// - if defined, code for incoming and outgoing SyncML dumping into (WB)XML logfiles is included
+#define MSGDUMP 1
+
+
+
+/* eof */
diff --git a/src/Targets/ReleasedProducts/clientEngine_autotools/systemxml/xmlparse.h b/src/Targets/ReleasedProducts/clientEngine_autotools/systemxml/xmlparse.h
new file mode 100644
index 0000000..e872ee0
--- /dev/null
+++ b/src/Targets/ReleasedProducts/clientEngine_autotools/systemxml/xmlparse.h
@@ -0,0 +1,2 @@
+/* redirect #include <xmlparse.h> as used in the source to xmltok/xmlparse.h */
+#include <xmltok/xmlparse.h>
diff --git a/src/Targets/ReleasedProducts/clientEngine_opensource_linux/clientengine_demo++.pch b/src/Targets/ReleasedProducts/clientEngine_opensource_linux/clientengine_demo++.pch
new file mode 100755
index 0000000..3cbcf34
--- /dev/null
+++ b/src/Targets/ReleasedProducts/clientEngine_opensource_linux/clientengine_demo++.pch
@@ -0,0 +1,53 @@
+/* precompiled headers source file
+ * ===============================
+ *
+ * CVS:
+ * $Author: bfo $
+ * $Date: 2009/01/08 19:00:27 $
+ * $Revision: 1.1 $
+ * $Source: /usr/local/cvsroot/sysync/Source/Targets/ReleasedProducts/clientEngine_opensource_linux/clientengine_demo++.pch,v $
+ * $State: Exp $
+ * $Name: $ (Tag)
+ * $Locker: $ (who has reserved checkout)
+ * Log:
+ * $Log: clientengine_demo++.pch,v $
+ * Revision 1.1 2009/01/08 19:00:27 bfo
+ * Required files added
+ *
+ * Revision 1.1 2007/11/08 15:58:02 bfo
+ * project files added
+ *
+ * Revision 1.1 2007/10/02 10:12:57 luz
+ * Added clientEngine_DEMO target to xcode
+ *
+ * Revision 1.1 2007/09/13 18:55:30 luz
+ * New DEMO client engine (SQLite, TextDB but no ODBC nor DBPlugins) release target
+ *
+ * Revision 1.1 2007/09/05 14:03:05 luz
+ * client engine with custom datastores (SQL, ODBC, SQLite and PluginAPI)
+ *
+ * Revision 1.2 2007/04/25 11:59:30 luz
+ * Moved platform specific includes to syncclient_outlook_precomp.h - it belongs there so that file can be used in MW precompiled header source files (.pch) as well as in prefix_file.h for VC++.
+ *
+ * Revision 1.1 2004/02/04 14:24:06 luz
+ * 2.0.5.3 s2g/ONE client release (and Oracle release candidate) checkin
+ *
+ *
+ */
+
+// SySync is a C++ project, make sure precomp headers
+// are compiled as C++!
+#pragma cplusplus on
+
+// determine target file name
+#pragma precompile_target "clientengine_demo_x86_linux++.mch"
+
+
+// include all headers that are suitable for precompiled use
+
+// - target options can incfluence everything
+#include "target_options.h"
+// - precompilable headers
+#include "clientengine_custom_precomp.h"
+
+// eof
diff --git a/src/Targets/ReleasedProducts/clientEngine_opensource_linux/clientengine_demo.pch b/src/Targets/ReleasedProducts/clientEngine_opensource_linux/clientengine_demo.pch
new file mode 100755
index 0000000..0ee683a
--- /dev/null
+++ b/src/Targets/ReleasedProducts/clientEngine_opensource_linux/clientengine_demo.pch
@@ -0,0 +1,46 @@
+/* precompiled headers source file
+ * ===============================
+ *
+ * CVS:
+ * $Author: bfo $
+ * $Date: 2009/01/08 19:00:27 $
+ * $Revision: 1.1 $
+ * $Source: /usr/local/cvsroot/sysync/Source/Targets/ReleasedProducts/clientEngine_opensource_linux/clientengine_demo.pch,v $
+ * $State: Exp $
+ * $Name: $ (Tag)
+ * $Locker: $ (who has reserved checkout)
+ * Log:
+ * $Log: clientengine_demo.pch,v $
+ * Revision 1.1 2009/01/08 19:00:27 bfo
+ * Required files added
+ *
+ * Revision 1.1 2007/11/08 15:58:02 bfo
+ * project files added
+ *
+ * Revision 1.1 2007/10/02 10:12:57 luz
+ * Added clientEngine_DEMO target to xcode
+ *
+ * Revision 1.1 2007/09/13 18:55:30 luz
+ * New DEMO client engine (SQLite, TextDB but no ODBC nor DBPlugins) release target
+ *
+ * Revision 1.1 2007/09/05 14:03:05 luz
+ * client engine with custom datastores (SQL, ODBC, SQLite and PluginAPI)
+ *
+ *
+ */
+
+// C-version for SyncML toolkit files
+#pragma cplusplus off
+
+// determine target file name
+#pragma precompile_target "clientengine_demo_x86_linux.mch"
+
+// include all headers that are suitable for precompiled
+// C version use
+// - target options can incfluence everything
+#include "target_options.h"
+
+// standard SyncML TK stuff
+#include "smltk_precomp.h"
+
+// eof
diff --git a/src/Targets/ReleasedProducts/clientEngine_opensource_linux/clientengine_demo_x86_linux_prefix.h b/src/Targets/ReleasedProducts/clientEngine_opensource_linux/clientengine_demo_x86_linux_prefix.h
new file mode 100644
index 0000000..81612bd
--- /dev/null
+++ b/src/Targets/ReleasedProducts/clientEngine_opensource_linux/clientengine_demo_x86_linux_prefix.h
@@ -0,0 +1,35 @@
+/* prefix file
+ * ===========
+ *
+ */
+
+
+// override default in global_options.h:
+// use SySync_ as prefix for external C functions
+#define SYSYNC_EXTERNAL(_x) SySync_ ## _x
+#define SYSYNC_PREFIX "SySync_"
+
+// required before time.h to get tm_gmtoff in struct tm:
+// this is used to find standard and daylight saving offset
+// of the system, see timezones.cpp
+#define _BSD_SOURCE 1
+#define USE_TM_GMTOFF 1
+
+#ifdef __cplusplus
+ // include all headers that are suitable for precompiled use
+ // - target options can incfluence everything
+ #include "target_options.h"
+ // - platform specifics
+ #include "platform_headers.h"
+ // - precompilable headers
+ #include "clientengine_custom_precomp.h"
+#else
+ // include all headers that are suitable for precompiled
+ // C version use
+ // - target options can incfluence everything
+ #include "target_options.h"
+ // - platform specifics
+ #include "platform_headers.h"
+#endif
+
+/* eof */
diff --git a/src/Targets/ReleasedProducts/clientEngine_opensource_linux/define.h b/src/Targets/ReleasedProducts/clientEngine_opensource_linux/define.h
new file mode 100755
index 0000000..42b2247
--- /dev/null
+++ b/src/Targets/ReleasedProducts/clientEngine_opensource_linux/define.h
@@ -0,0 +1,119 @@
+
+// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+// NOTE: this is a local copy for this specific target
+// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+
+
+/*************************************************************************/
+/* module: Compiler Flag Definition File */
+/* file: define.h */
+/* target system: win */
+/* target OS: win */
+/*************************************************************************/
+
+/*
+ * Copyright Notice
+ * Copyright (c) Ericsson, IBM, Lotus, Matsushita Communication
+ * Industrial Co., Ltd., Motorola, Nokia, Openwave Systems, Inc.,
+ * Palm, Inc., Psion, Starfish Software, Symbian, Ltd. (2001).
+ * All Rights Reserved.
+ * Implementation of all or part of any Specification may require
+ * licenses under third party intellectual property rights,
+ * including without limitation, patent rights (such a third party
+ * may or may not be a Supporter). The Sponsors of the Specification
+ * are not responsible and shall not be held responsible in any
+ * manner for identifying or failing to identify any or all such
+ * third party intellectual property rights.
+ *
+ * THIS DOCUMENT AND THE INFORMATION CONTAINED HEREIN ARE PROVIDED
+ * ON AN "AS IS" BASIS WITHOUT WARRANTY OF ANY KIND AND ERICSSON, IBM,
+ * LOTUS, MATSUSHITA COMMUNICATION INDUSTRIAL CO. LTD, MOTOROLA,
+ * NOKIA, PALM INC., PSION, STARFISH SOFTWARE AND ALL OTHER SYNCML
+ * SPONSORS DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+ * BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
+ * HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
+ * SHALL ERICSSON, IBM, LOTUS, MATSUSHITA COMMUNICATION INDUSTRIAL CO.,
+ * LTD, MOTOROLA, NOKIA, PALM INC., PSION, STARFISH SOFTWARE OR ANY
+ * OTHER SYNCML SPONSOR BE LIABLE TO ANY PARTY FOR ANY LOSS OF
+ * PROFITS, LOSS OF BUSINESS, LOSS OF USE OF DATA, INTERRUPTION OF
+ * BUSINESS, OR FOR DIRECT, INDIRECT, SPECIAL OR EXEMPLARY, INCIDENTAL,
+ * PUNITIVE OR CONSEQUENTIAL DAMAGES OF ANY KIND IN CONNECTION WITH
+ * THIS DOCUMENT OR THE INFORMATION CONTAINED HEREIN, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH LOSS OR DAMAGE.
+ *
+ * The above notice and this paragraph must be included on all copies
+ * of this document that are made.
+ *
+ */
+
+/**
+ * File for Windows Specific Compiler Flags
+ */
+
+#ifndef _DEFINE_H
+ #define _DEFINE_H
+#define __ANSI_C__
+
+
+/* thread safety (added by luz@synthesis.ch, 2001-10-29) */
+/* Note: moved define of this to target_options.h of every target */
+#undef __MAKE_THREADSAFE
+
+/* enable Alloc helpers */
+#define __USE_ALLOCFUNCS__
+
+/* do we need WBXML (binary XML) processing ? */
+#define __SML_WBXML__
+/* do we need the capability to decode plain text tokens in WBXML? */
+#define __SML_WBXML_TEXTTOKENS__
+/* do we need XML processing ? */
+#define __SML_XML__
+/* are we using a 'light' toolkit ? */
+//#define __SML_LITE__
+/* do we use Sub DTD extensions ? */
+#define __USE_EXTENSIONS__
+/* do we need Metainformation DTD parsing ? */
+#define __USE_METINF__
+/* do we use Device Info DTD ? */
+#define __USE_DEVINF__
+
+/* which of the following optional commands should be included ? */
+
+#define ADD_SEND
+//#define ATOMIC_SEND
+//#define ATOMIC_RECEIVE
+//#define COPY_SEND
+//#define COPY_RECEIVE
+//#define EXEC_SEND
+//#define EXEC_RECEIVE
+#define GET_SEND
+#define MAP_RECEIVE
+#define MAPITEM_RECEIVE
+#define RESULT_RECEIVE
+//#define SEARCH_SEND
+//#define SEARCH_RECEIVE
+//#define SEQUENCE_SEND
+//#define SEQUENCE_RECEIVE
+
+
+/* TK: to improve interoperability and handling we
+ * switched to using .def files instead of compiler
+ * specific per function definitions. As long as we only
+ * use C this is the easiest and cleanes way
+ */
+
+#define SML_API
+#define SML_API_DEF
+#define XPT_API
+#define XPT_API_DEF
+
+
+/* Multi segment macro for Palm OS */
+#define LIB_FUNC
+#define MGR_FUNC
+#define WSM_FUNC
+#define XLT_FUNC
+
+#endif
diff --git a/src/Targets/ReleasedProducts/clientEngine_opensource_linux/target_options.h b/src/Targets/ReleasedProducts/clientEngine_opensource_linux/target_options.h
new file mode 100755
index 0000000..10337b3
--- /dev/null
+++ b/src/Targets/ReleasedProducts/clientEngine_opensource_linux/target_options.h
@@ -0,0 +1,197 @@
+/* Target options
+ * ==============
+ *
+ *
+ */
+
+// SYNCML CLIENT ENGINE LIBRARY DEMO Linux
+// #######################################
+
+// define platform
+#define LINUX
+
+// Release version status
+#define RELEASE_VERSION
+#define RELEASE_SYDEBUG 2 // extended DBG included
+//#define OPTIONAL_SYDEBUG 1
+
+// Eval limit options
+// ==================
+
+// INTERNAL library linked directly with Synthesis' App Store products does not expire
+#define NEVER_EXPIRES_IS_OK 1 // to explicitly override check in SyncAppBase
+
+// now include platform independent product options (which include global_options.h)
+#include "product_options.h"
+
+
+// Identification strings
+#define CUST_SYNC_MAN SYSYNC_OEM // manufactured by ourselves
+#define CUST_SYNC_MODEL "SySync Client Library OpenSource Linux"
+#define CUST_SYNC_FIRMWARE NULL // no firmware
+#define CUST_SYNC_HARDWARE NULL // no hardware
+
+#define SYNCML_CLIENT_DEVTYP "pda"; // Linux mobiles are probably more "PDA"s than pure "smartphones"
+
+// String used to construct logfile names
+#define TARGETID "sysynclib_linux"
+
+// Configuration
+// =============
+
+// Default profile
+#undef HARD_CODED_SERVER_URI
+#undef HARD_CODED_DBNAMES
+#define DEFAULT_SERVER_URI "http://"
+#define DEFAULT_LOCALDB_PROFILE ""
+#define DEFAULT_SERVER_USER ""
+#define DEFAULT_SERVER_PASSWD ""
+#define DEFAULT_ENCODING SML_WBXML
+#define DEFAULT_TRANSPORT_USER NULL
+#define DEFAULT_TRANSPORT_PASSWD NULL
+#define DEFAULT_SOCKS_HOST NULL
+#define DEFAULT_PROXY_HOST NULL
+#define DEFAULT_DATASTORES_ENABLED false
+#define DEFAULT_EVENTS_DAYSBEFORE 30
+#define DEFAULT_EVENTS_DAYSAFTER 90
+#define DEFAULT_EVENTS_LIMITED false
+#define DEFAULT_EMAILS_LIMITED true
+#define DEFAULT_EMAILS_HDRONLY true
+#define DEFAULT_EMAILS_MAXKB 2
+#define DEFAULT_EMAILS_ONLYLAST true
+#define DEFAULT_EMAILS_DAYSBEFORE 10
+
+
+// Eval limit options
+// ==================
+
+// Note: Hard expiration date settings moved to product_options.h
+
+// - if defined, software will stop specified number of days after
+// first use
+#undef EXPIRES_AFTER_DAYS // Engine does not have auto-demo
+
+// Identification for update check and demo period
+#define SYSER_VARIANT_CODE SYSER_VARIANT_PRO
+#define SYSER_PRODUCT_CODE SYSER_PRODCODE_CLIENT_LIB_LINUX
+#define SYSER_EXTRA_ID SYSER_EXTRA_ID_NONE
+
+// This is the opensource Linux library, and needs no registration
+#undef SYSER_REGISTRATION
+#undef VERSION_COMMENTS
+#define VERSION_COMMENTS "Synthesis OpenSource"
+#undef EXPIRES_AFTER_DATE
+
+// - define allowed product codes
+#define SYSER_PRODUCT_CODE_MAIN SYSER_PRODCODE_CLIENT_DEMO // a permanent DEMO license
+#define SYSER_PRODUCT_CODE_ALT1 SYSER_PRODCODE_CLIENT_LIB_LINUX // ..or a library license for Linux
+#define SYSER_PRODUCT_CODE_ALT2 SYSER_PRODCODE_CLIENT_LIB_ALL // ..or for all platforms
+#define SYSER_PRODUCT_CODE_ALT3 SYSER_PRODCODE_CLIENT_LIB_DESK // ..or for desktop platforms
+#define SYSER_PRODUCT_CODE_ALT4 SYSER_PRODCODE_CLIENT_ODBC_PRO_LINUX // ..or a PRO console client for Linux
+// - define needed product flags
+// only licenses that are release date limited (or explicitly NOT release date limited,
+// or time limited) are allowed
+#define SYSER_NEEDED_PRODUCT_FLAGS SYSER_PRODFLAG_MAXRELDATE
+#define SYSER_FORBIDDEN_PRODUCT_FLAGS 0
+
+// for the opensource version, DLL plugins are always allowed
+// (even with licenses that do not have SYSER_PRODFLAG_SERVER_SDKAPI set)
+// because we do not use a license at all!
+#define DLL_PLUGINS_ALWAYS_ALLOWED 1
+
+
+// Database support options
+// ========================
+
+// - if defined, SQL support is included
+#define SQL_SUPPORT 1
+#undef ODBCAPI_SUPPORT
+#define SQLITE_SUPPORT 1
+// - if defined, ODBC DB mapping of arrays to aux tables is supported
+#define ARRAYDBTABLES_SUPPORT 1
+
+// - if defined, SDK support is included
+#define SDK_SUPPORT 1
+// - id defined, code allows calling of subsequent DLLs
+#define PLUGIN_DLL 1
+
+
+// Datastore Options
+// -----------------
+
+// - link in customimplds on top of binfile
+#define BASED_ON_BINFILE_CLIENT 1
+// - SQL and API datastores have changedetection, so build binfile for that
+#define CHANGEDETECTION_AVAILABLE 1
+// - we need a separate changelog mechanism
+#define CHECKSUM_CHANGELOG 1
+// - string localIDs with sufficiently large size
+#define STRING_LOCALID_MAXLEN 256
+
+
+// SySync options
+// ==============
+
+// - enhanced profile record
+#define ENHANCED_PROFILES_2004 1
+#define CLIENTFEATURES_2008 1
+
+// - NO support for automatic syncing (timed, IPP, server alerted...)
+#undef AUTOSYNC_SUPPORT
+// - support for intelligent push & poll (IPP)
+//#define IPP_SUPPORT 1
+//#define IPP_SUPPORT_ALWAYS 1 // regardless of license flags
+// - support for timed sync
+#undef TIMEDSYNC_SUPPORT
+// - support for WAP push alerted sync
+#undef SERVERALERT_SUPPORT
+
+// - support for Proxy
+#define PROXY_SUPPORT 1
+
+// - support for multiple profiles
+#define MULTI_PROFILE_SUPPORT 1
+
+// - show progress events
+#define PROGRESS_EVENTS 1
+
+// - we do not need to squeeze code
+#undef MINIMAL_CODE
+
+// - script with regex support
+#define SCRIPT_SUPPORT
+#define REGEX_SUPPORT
+
+// - client does not need target options
+#undef SYSYNC_TARGET_OPTIONS
+
+// - filters
+#define OBJECT_FILTERING
+
+// - client does not need superdatastores
+#undef SUPERDATASTORES
+
+
+// general options needed for email
+#define EMAIL_FORMAT_SUPPORT 1
+#define EMAIL_ATTACHMENT_SUPPORT 1
+#define ARRAYFIELD_SUPPORT 1
+
+// - if defined, stream field support will be included
+#define STREAMFIELD_SUPPORT 1
+
+// - if defined, semi-proprietary zipped-binary <data> for items (any type) can be used
+// (enabled on a by type basis in the config)
+#define ZIPPED_BINDATA_SUPPORT 1
+
+
+// - where to save application data by default (if not otherwise configured)
+// APPDATA_SUBDIR is a subdirectory of the user's "application data" dir.
+#define APPDATA_SUBDIR "synthesis.ch/SySyncLib"
+
+// - if defined, code for incoming and outgoing SyncML dumping into (WB)XML logfiles is included
+#define MSGDUMP 1
+
+
+
+/* eof */
diff --git a/src/Targets/ReleasedProducts/sysytool_linux/define.h b/src/Targets/ReleasedProducts/sysytool_linux/define.h
new file mode 100755
index 0000000..faacac8
--- /dev/null
+++ b/src/Targets/ReleasedProducts/sysytool_linux/define.h
@@ -0,0 +1,119 @@
+
+// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+// NOTE: this is a local copy for this specific target
+// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+
+
+/*************************************************************************/
+/* module: Compiler Flag Definition File */
+/* file: define.h */
+/* target system: mac os x */
+/* target OS: mac os x */
+/*************************************************************************/
+
+/*
+ * Copyright Notice
+ * Copyright (c) Ericsson, IBM, Lotus, Matsushita Communication
+ * Industrial Co., Ltd., Motorola, Nokia, Openwave Systems, Inc.,
+ * Palm, Inc., Psion, Starfish Software, Symbian, Ltd. (2001).
+ * All Rights Reserved.
+ * Implementation of all or part of any Specification may require
+ * licenses under third party intellectual property rights,
+ * including without limitation, patent rights (such a third party
+ * may or may not be a Supporter). The Sponsors of the Specification
+ * are not responsible and shall not be held responsible in any
+ * manner for identifying or failing to identify any or all such
+ * third party intellectual property rights.
+ *
+ * THIS DOCUMENT AND THE INFORMATION CONTAINED HEREIN ARE PROVIDED
+ * ON AN "AS IS" BASIS WITHOUT WARRANTY OF ANY KIND AND ERICSSON, IBM,
+ * LOTUS, MATSUSHITA COMMUNICATION INDUSTRIAL CO. LTD, MOTOROLA,
+ * NOKIA, PALM INC., PSION, STARFISH SOFTWARE AND ALL OTHER SYNCML
+ * SPONSORS DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+ * BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
+ * HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
+ * SHALL ERICSSON, IBM, LOTUS, MATSUSHITA COMMUNICATION INDUSTRIAL CO.,
+ * LTD, MOTOROLA, NOKIA, PALM INC., PSION, STARFISH SOFTWARE OR ANY
+ * OTHER SYNCML SPONSOR BE LIABLE TO ANY PARTY FOR ANY LOSS OF
+ * PROFITS, LOSS OF BUSINESS, LOSS OF USE OF DATA, INTERRUPTION OF
+ * BUSINESS, OR FOR DIRECT, INDIRECT, SPECIAL OR EXEMPLARY, INCIDENTAL,
+ * PUNITIVE OR CONSEQUENTIAL DAMAGES OF ANY KIND IN CONNECTION WITH
+ * THIS DOCUMENT OR THE INFORMATION CONTAINED HEREIN, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH LOSS OR DAMAGE.
+ *
+ * The above notice and this paragraph must be included on all copies
+ * of this document that are made.
+ *
+ */
+
+/**
+ * File for MacOS X Specific Compiler Flags
+ */
+
+#ifndef _DEFINE_H
+ #define _DEFINE_H
+//#define __ANSI_C__
+
+
+/* thread safety (added by luz@synthesis.ch, 2001-10-29) */
+/* Note: moved define of this to target_options.h of every target */
+#undef __MAKE_THREADSAFE
+
+/* enable Alloc helpers */
+#define __USE_ALLOCFUNCS__
+
+/* do we need WBXML (binary XML) processing ? */
+#define __SML_WBXML__
+/* do we need the capability to decode plain text tokens in WBXML? */
+#define __SML_WBXML_TEXTTOKENS__
+/* do we need XML processing ? */
+#define __SML_XML__
+/* are we using a 'light' toolkit ? */
+//#define __SML_LITE__
+/* do we use Sub DTD extensions ? */
+#define __USE_EXTENSIONS__
+/* do we need Metainformation DTD parsing ? */
+#define __USE_METINF__
+/* do we use Device Info DTD ? */
+#define __USE_DEVINF__
+
+/* which of the following optional commands should be included ? */
+
+#define ADD_SEND
+//#define ATOMIC_SEND
+//#define ATOMIC_RECEIVE
+//#define COPY_SEND
+//#define COPY_RECEIVE
+//#define EXEC_SEND
+//#define EXEC_RECEIVE
+#define GET_SEND
+#define MAP_RECEIVE
+#define MAPITEM_RECEIVE
+#define RESULT_RECEIVE
+//#define SEARCH_SEND
+//#define SEARCH_RECEIVE
+//#define SEQUENCE_SEND
+//#define SEQUENCE_RECEIVE
+
+
+/* TK: to improve interoperability and handling we
+ * switched to using .def files instead of compiler
+ * specific per function definitions. As long as we only
+ * use C this is the easiest and cleanes way
+ */
+
+#define SML_API
+#define SML_API_DEF
+#define XPT_API
+#define XPT_API_DEF
+
+/* Multi segment macro for Palm OS */
+#define LIB_FUNC
+#define MGR_FUNC
+#define WSM_FUNC
+#define XLT_FUNC
+
+
+#endif
diff --git a/src/Targets/ReleasedProducts/sysytool_linux/sysytool_linux_x86_prefix.h b/src/Targets/ReleasedProducts/sysytool_linux/sysytool_linux_x86_prefix.h
new file mode 100755
index 0000000..71f527f
--- /dev/null
+++ b/src/Targets/ReleasedProducts/sysytool_linux/sysytool_linux_x86_prefix.h
@@ -0,0 +1,25 @@
+/* prefix file
+ * ===========
+ *
+ */
+
+#ifdef __cplusplus
+ // include all headers that are suitable for precompiled use
+ // - target options can incfluence everything
+ #include "target_options.h"
+ // - platform specifics
+ #include "platform_headers.h"
+ // - precompilable headers
+ #include "sysytool_precomp.h"
+#else
+ // include all headers that are suitable for precompiled
+ // C version use
+ // - target options can incfluence everything
+ #include "target_options.h"
+ // - platform specifics
+ #include "platform_headers.h"
+ // - SML toolkit including xpt part
+ #include "smltk_precomp_xpt.h"
+#endif
+
+/* eof */
diff --git a/src/Targets/ReleasedProducts/sysytool_linux/target_options.h b/src/Targets/ReleasedProducts/sysytool_linux/target_options.h
new file mode 100755
index 0000000..f172ca6
--- /dev/null
+++ b/src/Targets/ReleasedProducts/sysytool_linux/target_options.h
@@ -0,0 +1,177 @@
+/* Target options
+ * ==============
+ *
+ *
+ */
+
+// Most hyperglobal definitions, might even influence global_options.h:
+// - THIS IS THE DIAGNOSTIC TOOL
+#define SYSYNC_TOOL
+
+// - this is the LINUX version of the sysytool
+#define LINUX
+
+// now include global switches
+#include "global_options.h"
+
+// - is a standalone APP
+#define STANDALONE_APP 1
+
+// Identification strings
+#define CUST_SYNC_MAN SYSYNC_OEM // manufactured by ourselves
+#define CUST_SYNC_MODEL "SySync SyncML Diagnostic Tool"
+#define CUST_SYNC_FIRMWARE NULL // no firmware
+#define CUST_SYNC_HARDWARE NULL // no hardware
+
+// String used to construct logfile names
+#define TARGETID "sysytool"
+
+// general comments
+#define VERSION_COMMENTS "Internal Synthesis AG Version"
+
+// Eval limit options
+// ==================
+
+// - if defined, server will have a restriction on concurrent sessions
+// from different devices.
+#define VERSION_COMMENTS "Limited to 1 simultaneous sync sessions"
+
+// - if defined, software will stop working as demo after defined date
+#define EXPIRES_AFTER_DATE 1
+
+// - if defined, software will stop specified number of days after
+// first use
+#undef EXPIRES_AFTER_DAYS
+
+// - variant
+#define SYSER_VARIANT_CODE SYSER_VARIANT_PRO
+
+// - if defined, software can be registered
+#define SYSER_REGISTRATION 1
+// - define allowed product codes (any server)
+#define SYSER_PRODUCT_CODE_MAIN SYSER_PRODCODE_SERVER_PRO
+#define SYSER_PRODUCT_CODE_ALT1 SYSER_PRODCODE_SERVER_STD
+#define SYSER_PRODUCT_CODE_ALT2 SYSER_PRODCODE_SERVER_DEMO
+#define SYSER_PRODUCT_CODE_ALT3 SYSER_PRODCODE_SERVER_XML2GO
+#undef SYSER_PRODUCT_CODE_ALT4
+// - define needed product flags
+#define SYSER_NEEDED_PRODUCT_FLAGS 0 // no ISAPI/APACHE flag needed for XPT
+#define SYSER_FORBIDDEN_PRODUCT_FLAGS 0
+
+
+// Database support options
+// ========================
+
+// - if defined, SQL support is included
+#define SQL_SUPPORT 1
+#define ODBCAPI_SUPPORT 1
+#define SQLITE_SUPPORT 1
+// - if defined, ODBC DB mapping of arrays to aux tables is supported
+#define ARRAYDBTABLES_SUPPORT 1
+
+// - if defined, SDK support is included
+//#define SDK_SUPPORT 1
+//#define PLUGIN_DLL 1
+
+// - define what SDK modules are linked in
+//#define DBAPI_DEMO 1
+#define DBAPI_TEXT 1
+#define FILEOBJ_SUPPORT 1
+#define ADAPTITEM_SUPPORT 1
+#define JNI_SUPPORT 1
+
+
+// SySync options
+// ==============
+
+// - if defined, debug code is included (not necessarily enabled, see gDebug)
+// if 1, only "public" debugging is enabled, if >1, all debugging is enabled
+#ifdef RELEASE_VERSION
+#define SYDEBUG 1
+#else
+#define SYDEBUG 2
+#endif
+
+#define CONSOLEINFO 1
+
+// %%% include all profiling
+//#define TIME_PROFILING 1
+//#define MEMORY_PROFILING 1
+
+
+// - if defined, support for configurable types will be included
+#define CONFIGURABLE_TYPE_SUPPORT 1
+
+// - if defined, object filtering will be included
+#define OBJECT_FILTERING 1
+
+// - if defined, procedure interpreter features will be included
+#define SCRIPT_SUPPORT 1
+
+// - if defined, superdatastores will be included
+#define SUPERDATASTORES 1
+
+// - if defined, array field support will be included
+#define ARRAYFIELD_SUPPORT 1
+
+// do not modify remote IDs in any way while processing them
+#define DONT_STRIP_PATHPREFIX_FROM_REMOTEIDS 1
+
+
+// ODBC options
+// ============
+
+// No SEH around ODBC calls
+#define NO_AV_GUARDING 1
+
+// if both text and ODBC maps are supported, mode can be switched
+// in config.
+#define USE_TEXTMAPS 1
+#define USE_ODBCMAPS 1
+
+
+// SyncML Toolkit options
+// ======================
+
+// if defined, the entire complicated and thread-unsafe workspace manager
+// is completely bypassed
+#define NOWSM 1
+
+/* correct tagging of payload with <![CDATA[, does not parse correctly
+ with RTK, therefore it should be normally disabled */
+// %%% disabled for DCM, magically works ok with it.
+#define PCDATA_OPAQUE_AS_CDATA 1
+
+// %%%%% allow Alert w/o Itemlist, was needed for magically
+//#define SYNCFEST_ALLOW_ALERT_WITHOUT_ITEMLIST 1
+
+/* thread safety (added by luz@synthesis.ch, 2001-10-29) %%% */
+//#define __MAKE_THREADSAFE 1
+/* debug thread locking by using global ThreadDebugPrintf() to log locks/unlocks */
+/* 1=only log waiting for >1sec for lock, 2=log all lock enter/leave ops */
+//#define __DEBUG_LOCKS 1
+
+// we want the toolkit linked static
+#define __LINK_TOOLKIT_STATIC__ 1
+// we want the XPT linked static
+#define LINK_TRANSPORT_STATICALLY 1
+// - select transports
+#define INCLUDE_HTTP_STATICALLY
+//#define INCLUDE_OBEX_STATICALLY
+//#define INCLUDE_WSP_STATICALLY
+
+// Verbose XPT debug only if high debug level
+#if SYDEBUG>2
+
+// switch on tracing for XPT
+#define TRACE 1
+#define TRACE_TO_STDOUT 1 // use global localOutput() function
+
+// Debug options for OBEX (smlobex)
+// - define one or several of these
+//#define DEBUGALL // also hex-dumps all!! IrDA traffic
+#define DEBUGFLOW
+#define DEBUGINFO
+#define DEBUGERROR
+
+#endif
diff --git a/src/Targets/clientEngine_dbg/clientengine_dbg++.pch b/src/Targets/clientEngine_dbg/clientengine_dbg++.pch
new file mode 100755
index 0000000..f7c0506
--- /dev/null
+++ b/src/Targets/clientEngine_dbg/clientengine_dbg++.pch
@@ -0,0 +1,41 @@
+/* precompiled headers source file
+ * ===============================
+ *
+ * CVS:
+ * $Author: luz $
+ * $Date: 2007/09/05 14:03:05 $
+ * $Revision: 1.1 $
+ * $Source: /usr/local/cvsroot/sysync/Source/Targets/clientEngine_dbg/clientengine_dbg++.pch,v $
+ * $State: Exp $
+ * $Name: $ (Tag)
+ * $Locker: $ (who has reserved checkout)
+ * Log:
+ * $Log: clientengine_dbg++.pch,v $
+ * Revision 1.1 2007/09/05 14:03:05 luz
+ * client engine with custom datastores (SQL, ODBC, SQLite and PluginAPI)
+ *
+ * Revision 1.2 2007/04/25 11:59:30 luz
+ * Moved platform specific includes to syncclient_outlook_precomp.h - it belongs there so that file can be used in MW precompiled header source files (.pch) as well as in prefix_file.h for VC++.
+ *
+ * Revision 1.1 2004/02/04 14:24:06 luz
+ * 2.0.5.3 s2g/ONE client release (and Oracle release candidate) checkin
+ *
+ *
+ */
+
+// SySync is a C++ project, make sure precomp headers
+// are compiled as C++!
+#pragma cplusplus on
+
+// determine target file name
+#pragma precompile_target "clientengine_dbg_x86++.mch"
+
+
+// include all headers that are suitable for precompiled use
+
+// - target options can incfluence everything
+#include "target_options.h"
+// - precompilable headers
+#include "clientengine_custom_precomp.h"
+
+// eof
diff --git a/src/Targets/clientEngine_dbg/clientengine_dbg.pch b/src/Targets/clientEngine_dbg/clientengine_dbg.pch
new file mode 100755
index 0000000..04add53
--- /dev/null
+++ b/src/Targets/clientEngine_dbg/clientengine_dbg.pch
@@ -0,0 +1,35 @@
+/* precompiled headers source file
+ * ===============================
+ *
+ * CVS:
+ * $Author: luz $
+ * $Date: 2007/09/05 14:03:05 $
+ * $Revision: 1.1 $
+ * $Source: /usr/local/cvsroot/sysync/Source/Targets/clientEngine_dbg/clientengine_dbg.pch,v $
+ * $State: Exp $
+ * $Name: $ (Tag)
+ * $Locker: $ (who has reserved checkout)
+ * Log:
+ * $Log: clientengine_dbg.pch,v $
+ * Revision 1.1 2007/09/05 14:03:05 luz
+ * client engine with custom datastores (SQL, ODBC, SQLite and PluginAPI)
+ *
+ *
+ */
+
+// C-version for SyncML toolkit files
+#pragma cplusplus off
+
+// determine target file name
+#if __INTEL__
+ #pragma precompile_target "clientengine_dbg_x86.mch"
+#else
+ #error "undefined target, cannot set precompiled header file name"
+#endif
+
+// include all headers that are suitable for precompiled
+// C version use
+// - target options can incfluence everything
+#include "target_options.h"
+
+// eof
diff --git a/src/Targets/clientEngine_dbg/clientengine_dbg_x86++.mch b/src/Targets/clientEngine_dbg/clientengine_dbg_x86++.mch
new file mode 100755
index 0000000..d8aa637
--- /dev/null
+++ b/src/Targets/clientEngine_dbg/clientengine_dbg_x86++.mch
Binary files differ
diff --git a/src/Targets/clientEngine_dbg/clientengine_dbg_x86.mch b/src/Targets/clientEngine_dbg/clientengine_dbg_x86.mch
new file mode 100755
index 0000000..eef2713
--- /dev/null
+++ b/src/Targets/clientEngine_dbg/clientengine_dbg_x86.mch
Binary files differ
diff --git a/src/Targets/clientEngine_dbg/clientengine_dbg_x86_prefix.h b/src/Targets/clientEngine_dbg/clientengine_dbg_x86_prefix.h
new file mode 100755
index 0000000..849b076
--- /dev/null
+++ b/src/Targets/clientEngine_dbg/clientengine_dbg_x86_prefix.h
@@ -0,0 +1,14 @@
+/* prefix file
+ * ===========
+ *
+ */
+
+#ifdef __cplusplus
+ #include "clientEngine_dbg_x86++.mch"
+#else
+ #include "clientEngine_dbg_x86.mch"
+#endif
+
+#include "target_options.h"
+
+/* eof */
diff --git a/src/Targets/clientEngine_dbg/target_options.h b/src/Targets/clientEngine_dbg/target_options.h
new file mode 100755
index 0000000..73904ac
--- /dev/null
+++ b/src/Targets/clientEngine_dbg/target_options.h
@@ -0,0 +1,205 @@
+/* Target options
+ * ==============
+ *
+ *
+ */
+
+// SYNCML CLIENT ENGINE LIBRARY WIN32
+// ##################################
+
+// define platform
+// - we are on Windows
+#ifndef _WIN32
+ #define _WIN32 // needed for MWERKS
+#endif
+#ifndef WIN32
+ #define WIN32 // needed for others
+#endif
+
+// Release version status#undef RELEASE_VERSION
+#define RELEASE_SYDEBUG 2 // extended DBG included
+//#define OPTIONAL_SYDEBUG 1
+
+// now include platform independent product options (which include global_options.h)
+#include "product_options.h"
+
+
+// Identification strings
+#define CUST_SYNC_MAN SYSYNC_OEM // manufactured by ourselves
+#define CUST_SYNC_MODEL "SySync Client Library PROTO Win32"
+#define CUST_SYNC_FIRMWARE NULL // no firmware
+#define CUST_SYNC_HARDWARE NULL // no hardware
+
+// String used to construct logfile names
+#define TARGETID "sysynclib_win32"
+
+// Configuration
+// =============
+
+// Default profile
+#undef HARD_CODED_SERVER_URI
+#undef HARD_CODED_DBNAMES
+#define DEFAULT_SERVER_URI "http://www.synthesis.ch/sync"
+#define DEFAULT_LOCALDB_PROFILE ""
+#define DEFAULT_SERVER_USER ""
+#define DEFAULT_SERVER_PASSWD ""
+#define DEFAULT_ENCODING SML_WBXML
+#define DEFAULT_TRANSPORT_USER NULL
+#define DEFAULT_TRANSPORT_PASSWD NULL
+#define DEFAULT_SOCKS_HOST NULL
+#define DEFAULT_PROXY_HOST NULL
+#define DEFAULT_DATASTORES_ENABLED false
+#define DEFAULT_EVENTS_DAYSBEFORE 30
+#define DEFAULT_EVENTS_DAYSAFTER 90
+#define DEFAULT_EVENTS_LIMITED false
+#define DEFAULT_EMAILS_LIMITED true
+#define DEFAULT_EMAILS_HDRONLY true
+#define DEFAULT_EMAILS_MAXKB 2
+#define DEFAULT_EMAILS_ONLYLAST true
+#define DEFAULT_EMAILS_DAYSBEFORE 10
+
+// Outlook specifics
+// =================
+
+
+// Eval limit options
+// ==================
+
+// Note: Hard expiration date settings moved to product_options.h
+
+// - if defined, software will stop specified number of days after
+// first use
+#undef EXPIRES_AFTER_DAYS // Engine does not have auto-demo
+
+// Identification for update check and demo period
+#define SYSER_VARIANT_CODE SYSER_VARIANT_PRO
+#define SYSER_PRODUCT_CODE SYSER_PRODCODE_CLIENT_OUTLOOK_PRO
+#define SYSER_EXTRA_ID SYSER_EXTRA_ID_PROTO
+
+
+// - if defined, software can be registered
+#define SYSER_REGISTRATION 1
+// - define allowed product codes
+#define SYSER_PRODUCT_CODE_MAIN SYSER_PRODCODE_CLIENT_LIB_WIN32 // we need a sysync library license for windows
+#define SYSER_PRODUCT_CODE_ALT1 SYSER_PRODCODE_CLIENT_ODBC_PRO_WIN32 // a PRO license for the Win console client is ok as well
+#define SYSER_PRODUCT_CODE_ALT2 SYSER_PRODCODE_CLIENT_LIB_ALL
+#define SYSER_PRODUCT_CODE_ALT3 SYSER_PRODCODE_CLIENT_LIB_DESK
+#undef SYSER_PRODUCT_CODE_ALT4
+// - define needed product flags
+// only licenses that are release date limited (or explicitly NOT release date limited,
+// or time limited) are allowed
+#define SYSER_NEEDED_PRODUCT_FLAGS SYSER_PRODFLAG_MAXRELDATE
+#define SYSER_FORBIDDEN_PRODUCT_FLAGS 0
+
+// for this DBG variant, DLL plugins are always allowed
+// (even with licenses that do not have SYSER_PRODFLAG_SERVER_SDKAPI set)
+#define DLL_PLUGINS_ALWAYS_ALLOWED 1
+
+
+// SySync options
+// ==============
+
+// - enhanced profile record
+#define ENHANCED_PROFILES_2004 1
+#define CLIENTFEATURES_2008 1
+
+// - support for automatic syncing (timed, IPP, server alerted...)
+#define AUTOSYNC_SUPPORT 1
+// - support for intelligent push & poll (IPP)
+//#define IPP_SUPPORT 1
+//#define IPP_SUPPORT_ALWAYS 1 // regardless of license flags
+// - support for timed sync
+#define TIMEDSYNC_SUPPORT 1
+// - support for WAP push alerted sync
+#define SERVERALERT_SUPPORT 1
+
+// - support for Proxy
+#define PROXY_SUPPORT 1
+
+// - support for multiple profiles
+#define MULTI_PROFILE_SUPPORT 1
+
+// - show progress events
+#define PROGRESS_EVENTS 1
+
+// - we do not need to squeeze code
+#undef MINIMAL_CODE
+
+// - script with regex support
+#define SCRIPT_SUPPORT
+#define REGEX_SUPPORT
+
+// - client does not need target options
+#undef SYSYNC_TARGET_OPTIONS
+
+// - filters
+#define OBJECT_FILTERING
+
+// - client does not need superdatastores
+#undef SUPERDATASTORES
+
+
+// general options needed for email
+#define EMAIL_FORMAT_SUPPORT 1
+#define EMAIL_ATTACHMENT_SUPPORT 1
+#define ARRAYFIELD_SUPPORT 1
+
+// - if defined, stream field support will be included
+#define STREAMFIELD_SUPPORT 1
+
+// - if defined, semi-proprietary zipped-binary <data> for items (any type) can be used
+// (enabled on a by type basis in the config)
+#define ZIPPED_BINDATA_SUPPORT 1
+
+
+// - where to save application data by default (if not otherwise configured)
+// APPDATA_SUBDIR is a subdirectory of the user's "application data" dir.
+#define APPDATA_SUBDIR "synthesis.ch\\SySyncLib"
+
+// - if defined, code for incoming and outgoing SyncML dumping into (WB)XML logfiles is included
+#define MSGDUMP 1
+
+
+// Datastore Options
+// -----------------
+
+// - support DBApi tunnel
+//#define DBAPI_TUNNEL_SUPPORT 1
+
+// - link in customimplds on top of binfile
+#define BASED_ON_BINFILE_CLIENT 1
+// - SQL and API datastores have changedetection, so build binfile for that
+#define CHANGEDETECTION_AVAILABLE 1
+// - we need a separate changelog mechanism
+#define CHECKSUM_CHANGELOG 1
+// - string localIDs with sufficiently large size
+#define STRING_LOCALID_MAXLEN 256
+
+// - supports ODBC, SQLite and DBApi
+#define SQL_SUPPORT 1
+#define ODBCAPI_SUPPORT 1
+#define SQLITE_SUPPORT 1
+// - master detail (array) tables supported
+#define ARRAYDBTABLES_SUPPORT 1
+
+// - if defined, SDK support is included
+#define SDK_SUPPORT 1
+#define PLUGIN_DLL 1 // external DLL allowed as well
+
+// - define what SDK modules are linked in
+//#define DBAPI_DEMO 1
+#define DBAPI_TEXT 1
+//#define DBAPI_SILENT 1
+#define DBAPI_LOGGER 1
+#define DBAPI_SNOWWHITE 1
+#define JNI_SUPPORT 1
+#define FILEOBJ_SUPPORT 1
+#define ADAPTITEM_SUPPORT 1
+
+#ifdef _WIN32
+ #define CSHARP_SUPPORT 1
+#endif
+
+
+
+/* eof */
diff --git a/src/Targets/sysytool/define.h b/src/Targets/sysytool/define.h
new file mode 100755
index 0000000..faacac8
--- /dev/null
+++ b/src/Targets/sysytool/define.h
@@ -0,0 +1,119 @@
+
+// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+// NOTE: this is a local copy for this specific target
+// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+
+
+/*************************************************************************/
+/* module: Compiler Flag Definition File */
+/* file: define.h */
+/* target system: mac os x */
+/* target OS: mac os x */
+/*************************************************************************/
+
+/*
+ * Copyright Notice
+ * Copyright (c) Ericsson, IBM, Lotus, Matsushita Communication
+ * Industrial Co., Ltd., Motorola, Nokia, Openwave Systems, Inc.,
+ * Palm, Inc., Psion, Starfish Software, Symbian, Ltd. (2001).
+ * All Rights Reserved.
+ * Implementation of all or part of any Specification may require
+ * licenses under third party intellectual property rights,
+ * including without limitation, patent rights (such a third party
+ * may or may not be a Supporter). The Sponsors of the Specification
+ * are not responsible and shall not be held responsible in any
+ * manner for identifying or failing to identify any or all such
+ * third party intellectual property rights.
+ *
+ * THIS DOCUMENT AND THE INFORMATION CONTAINED HEREIN ARE PROVIDED
+ * ON AN "AS IS" BASIS WITHOUT WARRANTY OF ANY KIND AND ERICSSON, IBM,
+ * LOTUS, MATSUSHITA COMMUNICATION INDUSTRIAL CO. LTD, MOTOROLA,
+ * NOKIA, PALM INC., PSION, STARFISH SOFTWARE AND ALL OTHER SYNCML
+ * SPONSORS DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+ * BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
+ * HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
+ * SHALL ERICSSON, IBM, LOTUS, MATSUSHITA COMMUNICATION INDUSTRIAL CO.,
+ * LTD, MOTOROLA, NOKIA, PALM INC., PSION, STARFISH SOFTWARE OR ANY
+ * OTHER SYNCML SPONSOR BE LIABLE TO ANY PARTY FOR ANY LOSS OF
+ * PROFITS, LOSS OF BUSINESS, LOSS OF USE OF DATA, INTERRUPTION OF
+ * BUSINESS, OR FOR DIRECT, INDIRECT, SPECIAL OR EXEMPLARY, INCIDENTAL,
+ * PUNITIVE OR CONSEQUENTIAL DAMAGES OF ANY KIND IN CONNECTION WITH
+ * THIS DOCUMENT OR THE INFORMATION CONTAINED HEREIN, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH LOSS OR DAMAGE.
+ *
+ * The above notice and this paragraph must be included on all copies
+ * of this document that are made.
+ *
+ */
+
+/**
+ * File for MacOS X Specific Compiler Flags
+ */
+
+#ifndef _DEFINE_H
+ #define _DEFINE_H
+//#define __ANSI_C__
+
+
+/* thread safety (added by luz@synthesis.ch, 2001-10-29) */
+/* Note: moved define of this to target_options.h of every target */
+#undef __MAKE_THREADSAFE
+
+/* enable Alloc helpers */
+#define __USE_ALLOCFUNCS__
+
+/* do we need WBXML (binary XML) processing ? */
+#define __SML_WBXML__
+/* do we need the capability to decode plain text tokens in WBXML? */
+#define __SML_WBXML_TEXTTOKENS__
+/* do we need XML processing ? */
+#define __SML_XML__
+/* are we using a 'light' toolkit ? */
+//#define __SML_LITE__
+/* do we use Sub DTD extensions ? */
+#define __USE_EXTENSIONS__
+/* do we need Metainformation DTD parsing ? */
+#define __USE_METINF__
+/* do we use Device Info DTD ? */
+#define __USE_DEVINF__
+
+/* which of the following optional commands should be included ? */
+
+#define ADD_SEND
+//#define ATOMIC_SEND
+//#define ATOMIC_RECEIVE
+//#define COPY_SEND
+//#define COPY_RECEIVE
+//#define EXEC_SEND
+//#define EXEC_RECEIVE
+#define GET_SEND
+#define MAP_RECEIVE
+#define MAPITEM_RECEIVE
+#define RESULT_RECEIVE
+//#define SEARCH_SEND
+//#define SEARCH_RECEIVE
+//#define SEQUENCE_SEND
+//#define SEQUENCE_RECEIVE
+
+
+/* TK: to improve interoperability and handling we
+ * switched to using .def files instead of compiler
+ * specific per function definitions. As long as we only
+ * use C this is the easiest and cleanes way
+ */
+
+#define SML_API
+#define SML_API_DEF
+#define XPT_API
+#define XPT_API_DEF
+
+/* Multi segment macro for Palm OS */
+#define LIB_FUNC
+#define MGR_FUNC
+#define WSM_FUNC
+#define XLT_FUNC
+
+
+#endif
diff --git a/src/Targets/sysytool/sysytool++.pch b/src/Targets/sysytool/sysytool++.pch
new file mode 100755
index 0000000..2c49a5e
--- /dev/null
+++ b/src/Targets/sysytool/sysytool++.pch
@@ -0,0 +1,20 @@
+// precompiled headers source file
+
+// SySync is a C++ project, make sure precomp headers
+// are compiled as C++!
+#pragma cplusplus on
+
+// determine target file name
+#if __INTEL__
+ #pragma precompile_target "sysytool_x86++.mch"
+#else
+ #error "undefined target, cannot set precompiled header file name"
+#endif
+
+// include all headers that are suitable for precompiled use
+// - target options can incfluence everything
+#include "target_options.h"
+// - precompilable headers
+#include "sysytool_precomp.h"
+
+// eof
diff --git a/src/Targets/sysytool/sysytool.pch b/src/Targets/sysytool/sysytool.pch
new file mode 100755
index 0000000..8947061
--- /dev/null
+++ b/src/Targets/sysytool/sysytool.pch
@@ -0,0 +1,20 @@
+// precompiled headers source file
+
+// C-version for SyncML toolkit files
+#pragma cplusplus off
+
+// determine target file name
+#if __INTEL__
+ #pragma precompile_target "sysytool_x86.mch"
+#else
+ #error "undefined target, cannot set precompiled header file name"
+#endif
+
+// include all headers that are suitable for precompiled
+// C version use
+// - target options can incfluence everything
+#include "target_options.h"
+// - SML toolkit including xpt part
+#include "smltk_precomp_xpt.h"
+
+// eof
diff --git a/src/Targets/sysytool/sysytool_linux_x86_prefix.h b/src/Targets/sysytool/sysytool_linux_x86_prefix.h
new file mode 100755
index 0000000..71f527f
--- /dev/null
+++ b/src/Targets/sysytool/sysytool_linux_x86_prefix.h
@@ -0,0 +1,25 @@
+/* prefix file
+ * ===========
+ *
+ */
+
+#ifdef __cplusplus
+ // include all headers that are suitable for precompiled use
+ // - target options can incfluence everything
+ #include "target_options.h"
+ // - platform specifics
+ #include "platform_headers.h"
+ // - precompilable headers
+ #include "sysytool_precomp.h"
+#else
+ // include all headers that are suitable for precompiled
+ // C version use
+ // - target options can incfluence everything
+ #include "target_options.h"
+ // - platform specifics
+ #include "platform_headers.h"
+ // - SML toolkit including xpt part
+ #include "smltk_precomp_xpt.h"
+#endif
+
+/* eof */
diff --git a/src/Targets/sysytool/sysytool_x86++.mch b/src/Targets/sysytool/sysytool_x86++.mch
new file mode 100644
index 0000000..07fc80b
--- /dev/null
+++ b/src/Targets/sysytool/sysytool_x86++.mch
Binary files differ
diff --git a/src/Targets/sysytool/sysytool_x86.mch b/src/Targets/sysytool/sysytool_x86.mch
new file mode 100644
index 0000000..9214ab2
--- /dev/null
+++ b/src/Targets/sysytool/sysytool_x86.mch
Binary files differ
diff --git a/src/Targets/sysytool/sysytool_x86_prefix.h b/src/Targets/sysytool/sysytool_x86_prefix.h
new file mode 100755
index 0000000..bcc8b50
--- /dev/null
+++ b/src/Targets/sysytool/sysytool_x86_prefix.h
@@ -0,0 +1,10 @@
+// prefix file
+#ifdef __cplusplus
+#include "sysytool_x86++.mch"
+#else
+#include "sysytool_x86.mch"
+#endif
+
+#include "target_options.h"
+
+/* eof */
diff --git a/src/Targets/sysytool/target_options.h b/src/Targets/sysytool/target_options.h
new file mode 100755
index 0000000..a64fd12
--- /dev/null
+++ b/src/Targets/sysytool/target_options.h
@@ -0,0 +1,176 @@
+/* Target options
+ * ==============
+ *
+ *
+ */
+
+// Most hyperglobal definitions, might even influence global_options.h:
+// - THIS IS THE DIAGNOSTIC TOOL
+#define SYSYNC_TOOL
+
+// - find out target platform
+#ifdef __MWERKS__
+#define _WIN32
+#else
+#define LINUX
+#endif
+
+// now include global switches
+#include "global_options.h"
+
+
+// - is a standalone APP
+#define STANDALONE_APP 1
+
+// Identification strings
+#define CUST_SYNC_MAN SYSYNC_OEM // manufactured by ourselves
+#define CUST_SYNC_MODEL "SySync SyncML Diagnostic Tool"
+#define CUST_SYNC_FIRMWARE NULL // no firmware
+#define CUST_SYNC_HARDWARE NULL // no hardware
+
+// String used to construct logfile names
+#define TARGETID "sysytool"
+
+// general comments
+#define VERSION_COMMENTS "Internal Synthesis AG Version"
+
+// Eval limit options
+// ==================
+
+// - if defined, server will have a restriction on concurrent sessions
+// from different devices.
+#define VERSION_COMMENTS "Limited to 1 simultaneous sync sessions"
+
+// - if defined, software will stop working as demo after defined date
+#define EXPIRES_AFTER_DATE 1
+
+// - if defined, software will stop specified number of days after
+// first use
+#undef EXPIRES_AFTER_DAYS
+
+// - variant
+#define SYSER_VARIANT_CODE SYSER_VARIANT_PRO
+
+// - if defined, software can be registered
+#define SYSER_REGISTRATION 1
+// - define allowed product codes (any server)
+#define SYSER_PRODUCT_CODE_MAIN SYSER_PRODCODE_SERVER_PRO
+#define SYSER_PRODUCT_CODE_ALT1 SYSER_PRODCODE_SERVER_STD
+#define SYSER_PRODUCT_CODE_ALT2 SYSER_PRODCODE_SERVER_DEMO
+#define SYSER_PRODUCT_CODE_ALT3 SYSER_PRODCODE_SERVER_XML2GO
+#undef SYSER_PRODUCT_CODE_ALT4
+// - define needed product flags
+#define SYSER_NEEDED_PRODUCT_FLAGS 0 // no ISAPI/APACHE flag needed for XPT
+#define SYSER_FORBIDDEN_PRODUCT_FLAGS 0
+
+
+// Database support options
+// ========================
+
+// - if defined, SQL support is included
+#define SQL_SUPPORT 1
+#define ODBCAPI_SUPPORT 1
+#define SQLITE_SUPPORT 1
+// - if defined, ODBC DB mapping of arrays to aux tables is supported
+#define ARRAYDBTABLES_SUPPORT 1
+
+// - if defined, SDK support is included
+// #define SDK_SUPPORT 1
+// #define PLUGIN_DLL 1
+
+// - define what SDK modules are linked in
+//#define DBAPI_DEMO 1
+#define DBAPI_TEXT 1
+//#define FILEOBJ_SUPPORT 1
+#define ADAPTITEM_SUPPORT 1
+#define JNI_SUPPORT 1
+
+
+// SySync options
+// ==============
+
+// - if defined, debug code is included (not necessarily enabled, depending on debug mask)
+// if 1, only "public" debugging is enabled, if >1, all debugging is enabled
+#ifdef RELEASE_VERSION
+#define SYDEBUG 1
+#else
+#define SYDEBUG 2
+#endif
+
+#define CONSOLEINFO 1
+
+// %%% include all profiling
+//#define TIME_PROFILING 1
+//#define MEMORY_PROFILING 1
+
+// - if defined, support for configurable types will be included
+#define CONFIGURABLE_TYPE_SUPPORT 1
+
+// - if defined, object filtering will be included
+#define OBJECT_FILTERING 1
+
+// - if defined, procedure interpreter features will be included
+#define SCRIPT_SUPPORT 1
+
+// - if defined, superdatastores will be included
+#define SUPERDATASTORES 1
+
+// - if defined, array field support will be included
+#define ARRAYFIELD_SUPPORT 1
+
+// do not modify remote IDs in any way while processing them
+#define DONT_STRIP_PATHPREFIX_FROM_REMOTEIDS 1
+
+
+// ODBC options
+// ============
+
+// No SEH around ODBC calls
+#define NO_AV_GUARDING 1
+
+
+// SyncML Toolkit options
+// ======================
+
+// if defined, the entire complicated and thread-unsafe workspace manager
+// is completely bypassed
+#define NOWSM 1
+
+/* correct tagging of payload with <![CDATA[, does not parse correctly
+ with RTK, therefore it should be normally disabled */
+// %%% disabled for DCM, magically works ok with it.
+#define PCDATA_OPAQUE_AS_CDATA 1
+
+// %%%%% allow Alert w/o Itemlist, was needed for magically
+//#define SYNCFEST_ALLOW_ALERT_WITHOUT_ITEMLIST 1
+
+/* thread safety (added by luz@synthesis.ch, 2001-10-29) %%% */
+//#define __MAKE_THREADSAFE 1
+/* debug thread locking by using global ThreadDebugPrintf() to log locks/unlocks */
+/* 1=only log waiting for >1sec for lock, 2=log all lock enter/leave ops */
+//#define __DEBUG_LOCKS 1
+
+// we want the toolkit linked static
+#define __LINK_TOOLKIT_STATIC__ 1
+// we want the XPT linked static
+#define LINK_TRANSPORT_STATICALLY 1
+// - select transports
+#define INCLUDE_HTTP_STATICALLY
+//#define INCLUDE_OBEX_STATICALLY
+//#define INCLUDE_WSP_STATICALLY
+
+// Verbose XPT debug only if high debug level
+#if SYDEBUG>2
+
+// switch on tracing for XPT
+#define TRACE 1
+#define TRACE_TO_STDOUT 1 // use global localOutput() function
+
+// Debug options for OBEX (smlobex)
+// - define one or several of these
+//#define DEBUGALL // also hex-dumps all!! IrDA traffic
+#define DEBUGFLOW
+#define DEBUGINFO
+#define DEBUGERROR
+
+#endif
diff --git a/src/Targets/sysytool/version.rc b/src/Targets/sysytool/version.rc
new file mode 100755
index 0000000..2339555
--- /dev/null
+++ b/src/Targets/sysytool/version.rc
@@ -0,0 +1,46 @@
+///////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+
+#include <winver.h>
+#include "target_options.h"
+
+
+1 VERSIONINFO
+ FILEVERSION SYSYNC_VERSION_MAJOR,SYSYNC_VERSION_MINOR,SYSYNC_SUBVERSION,SYSYNC_BUILDNUMBER
+ PRODUCTVERSION SYSYNC_VERSION_MAJOR,SYSYNC_VERSION_MINOR,SYSYNC_SUBVERSION,SYSYNC_BUILDNUMBER
+ FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
+#if SYDEBUG>1
+ FILEFLAGS VS_FF_DEBUG
+#else
+ FILEFLAGS 0x0L
+#endif
+
+ FILEOS VOS__WINDOWS32
+
+ FILETYPE VFT_DLL
+
+ FILESUBTYPE VFT2_UNKNOWN
+
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904b0"
+ BEGIN
+ VALUE "Comments", VERSION_COMMENTS "\0"
+ VALUE "CompanyName", "Synthesis AG - www.synthesis.ch\0"
+ VALUE "FileDescription", "Synthesis SyncML Diagnostic Tool\0"
+ VALUE "FileVersion", SYSYNC_VERSION_STRING "\0"
+ VALUE "InternalName", "sysytool\0"
+ VALUE "LegalCopyright", "Copyright (C) 2004-" RELEASE_YEAR_TXT " by Synthesis AG\0"
+ VALUE "OriginalFilename", "sysytool.exe\0"
+ VALUE "ProductName", "Synthesis SyncML Diagnostic Tool\0"
+ VALUE "ProductVersion", SYSYNC_VERSION_STRING "\0"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1200
+ END
+END
diff --git a/src/Transport_interfaces/engine/engine_client.h b/src/Transport_interfaces/engine/engine_client.h
new file mode 100755
index 0000000..ce41354
--- /dev/null
+++ b/src/Transport_interfaces/engine/engine_client.h
@@ -0,0 +1,28 @@
+/* client engine (w/o internal transport) generic header file
+ *
+ */
+
+#ifndef ENGINE_CLIENT_H
+#define ENGINE_CLIENT_H
+
+/* Headers that might be available in precompiled form
+ * (standard libraries, SyncML toolkit...)
+ */
+#include "engine_client_precomp.h"
+
+/* headers not suitable for / entirely included in precompilation */
+
+// sysync core
+#include "sysync.h"
+// platform utilities
+//#include "platform_utils.h"
+
+// classes
+#include "engineclientbase.h"
+
+/* globals */
+
+
+#endif // ENGINE_CLIENT_H
+
+/* eof */
diff --git a/src/Transport_interfaces/engine/engine_client_precomp.h b/src/Transport_interfaces/engine/engine_client_precomp.h
new file mode 100755
index 0000000..7865d1e
--- /dev/null
+++ b/src/Transport_interfaces/engine/engine_client_precomp.h
@@ -0,0 +1,35 @@
+/* Headers that might be available in precompiled form
+ * (standard libraries, SyncML toolkit...)
+ *
+ */
+
+#ifndef ENGINE_CLIENT_PRECOMP_H
+#define ENGINE_CLIENT_PRECOMP_H
+
+
+/* precompiled portion for SySync Core */
+#include "sysync_precomp.h"
+
+
+#ifdef __EPOC_OS__
+ // Symbian/EPOC:
+#elif __INTEL__
+ // Windows32: nothing special
+#elif defined(__MC68K__)
+ // PalmOS: nothing special
+#elif defined(LINUX)
+ // Linux: nothing special
+#elif defined(MACOSX)
+ // MacOSX: nothing special
+#elif defined(WINCE)
+ // WinCE/PocketPC: nothing special
+#elif defined(_MSC_VER)
+ // Visual C++: nothing special
+#else
+ #error "Engine Client is Win32/PalmOS/PocketPC/Linux only at this time"
+#endif
+
+
+#endif // ENGINE_CLIENT_PRECOMP_H
+
+// eof
diff --git a/src/Transport_interfaces/engine/engineclientbase.cpp b/src/Transport_interfaces/engine/engineclientbase.cpp
new file mode 100755
index 0000000..6e6067c
--- /dev/null
+++ b/src/Transport_interfaces/engine/engineclientbase.cpp
@@ -0,0 +1,138 @@
+/*
+ * TEngineClientBase
+ * Engine library specific descendant of TSyncClientBase
+ * Global object, manages starting of client sessions.
+ *
+ * Copyright (c) 2002-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ * 2007-09-04 : luz : Created
+ *
+ */
+
+
+#include "prefix_file.h"
+#include "engine_client.h"
+#include "engineclientbase.h"
+
+
+namespace sysync {
+
+
+// write to platform's "console", whatever that is
+void AppConsolePuts(const char *aText)
+{
+ // Just print to platform's console
+ PlatformConsolePuts(aText);
+} // AppConsolePuts
+
+
+// TEngineCommConfig
+// =================
+// (dummy at this time)
+
+
+// config constructor
+TEngineCommConfig::TEngineCommConfig(TConfigElement *aParentElementP) :
+ TCommConfig("engineclient",aParentElementP)
+{
+ // do not call clear(), because this is virtual!
+} // TEngineCommConfig::TXPTCommConfig
+
+
+// config destructor
+TEngineCommConfig::~TEngineCommConfig()
+{
+ // nop by now
+} // TEngineCommConfig::~TXPTCommConfig
+
+
+// init defaults
+void TEngineCommConfig::clear(void)
+{
+ // init defaults
+ // %%% none for now
+ // clear inherited
+ inherited::clear();
+} // TEngineCommConfig::clear
+
+
+#ifndef HARDCODED_CONFIG
+
+// XPT transport config element parsing
+bool TEngineCommConfig::localStartElement(const char *aElementName, const char **aAttributes, sInt32 aLine)
+{
+ // checking the elements
+ /*
+ if (strucmp(aElementName,"xxx")==0)
+ expectBool(fXXX);
+ else
+ */
+ return inherited::localStartElement(aElementName,aAttributes,aLine);
+ // ok
+ return true;
+} // TEngineCommConfig::localStartElement
+
+#endif
+
+
+// resolve
+void TEngineCommConfig::localResolve(bool aLastPass)
+{
+ if (aLastPass) {
+ // check for required settings
+ // %%% tbd
+ }
+ // resolve inherited
+ inherited::localResolve(aLastPass);
+} // TEngineCommConfig::localResolve
+
+
+
+// TEngineClientBase
+// =================
+
+
+// constructor
+TEngineClientBase::TEngineClientBase() :
+ TSyncClientBase()
+{
+ // init
+} // TEngineClientBase::TEngineClientBase
+
+
+// destructor
+TEngineClientBase::~TEngineClientBase()
+{
+ fDeleting=true; // flag deletion to block calling critical (virtual) methods
+ // clean up
+ // %%%
+} // TEngineClientBase::~TEngineClientBase
+
+
+
+// factory methods of Rootconfig
+// =============================
+
+
+// create default transport config
+void TEngineClientRootConfig::installCommConfig(void)
+{
+ // engine API needs no config at this time, commconfig is a NOP dummy for now
+ fCommConfigP=new TEngineCommConfig(this);
+} // TEngineClientRootConfig::installCommConfig
+
+
+#ifndef HARDCODED_CONFIG
+
+bool TEngineClientRootConfig::parseCommConfig(const char **aAttributes, sInt32 aLine)
+{
+ // engine API needs no config at this time
+ return false;
+} // TEngineClientRootConfig::parseCommConfig
+
+#endif
+
+
+} // namespace sysync
+
+// eof
diff --git a/src/Transport_interfaces/engine/engineclientbase.h b/src/Transport_interfaces/engine/engineclientbase.h
new file mode 100755
index 0000000..6bc71e8
--- /dev/null
+++ b/src/Transport_interfaces/engine/engineclientbase.h
@@ -0,0 +1,77 @@
+/*
+ * TEngineClientBase
+ * client library specific descendant of TSyncClientBase
+ * Global object, manages starting of client sessions.
+ *
+ * Copyright (c) 2002-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ * 2002-05-03 : luz : Created
+ *
+ */
+
+#ifndef ENGINECLIENTBASE_H
+#define ENGINECLIENTBASE_H
+
+// required headers
+#include "syncappbase.h"
+#include "syncclientbase.h"
+#include "syncclient.h"
+
+
+namespace sysync {
+
+
+// engine client root config
+class TEngineClientRootConfig : public TRootConfig
+{
+ typedef TRootConfig inherited;
+public:
+ TEngineClientRootConfig(TSyncAppBase *aSyncAppBaseP) : inherited(aSyncAppBaseP) {};
+ // factory methods
+ virtual void installCommConfig(void);
+ // Config parsing
+ #ifndef HARDCODED_CONFIG
+ virtual bool parseCommConfig(const char **aAttributes, sInt32 aLine);
+ #endif
+}; // TEngineClientRootConfig
+
+
+// engine client transport config
+class TEngineCommConfig: public TCommConfig
+{
+ typedef TCommConfig inherited;
+public:
+ TEngineCommConfig(TConfigElement *aParentElementP);
+ virtual ~TEngineCommConfig();
+protected:
+ // check config elements
+ #ifndef HARDCODED_CONFIG
+ virtual bool localStartElement(const char *aElementName, const char **aAttributes, sInt32 aLine);
+ #endif
+ virtual void clear();
+ virtual void localResolve(bool aLastPass);
+}; // TXPTCommConfig
+
+
+// forward declarations
+class TSyncSession;
+class TEngineClientBase;
+
+
+
+// AppBase class for all client engines (libararies with API to build custom clients)
+class TEngineClientBase : public TSyncClientBase {
+ typedef TSyncClientBase inherited;
+public:
+ // constructors/destructors
+ TEngineClientBase();
+ virtual ~TEngineClientBase();
+private:
+}; // TEngineClientBase
+
+} // namespace sysync
+
+#endif // ENGINECLIENTBASE_H
+
+
+// eof
diff --git a/src/client_engine_linux.mk b/src/client_engine_linux.mk
new file mode 100644
index 0000000..68e4aba
--- /dev/null
+++ b/src/client_engine_linux.mk
@@ -0,0 +1,233 @@
+# Makefile generated by Metrowerks CodeWarrior IDE
+# adapted for standalone use 09/02/05 by bfo@synthesis.ch
+
+all: clientEngine
+clean: clean_clientEngine
+
+
+####### common definitions
+WD=$(shell echo `pwd`)
+WD_OBJS=$(WD)/../OBJS
+
+CC="gcc"
+LD="gcc"
+AR="ar -crs"
+SIZE="size"
+
+CFLAGS= -m32 -Wall -O2 -c -MMD
+LDFLAGS= -m32
+MAKEFILE= "sysync_linux.mk"
+
+
+SYNCML_TK_SML=\
+ syncml_tk/src/sml/lib/all/liblock.c\
+ syncml_tk/src/sml/lib/all/libmem.c\
+ syncml_tk/src/sml/lib/all/libstr.c\
+ syncml_tk/src/sml/lib/all/libutil.c\
+ syncml_tk/src/sml/mgr/all/mgr.c\
+ syncml_tk/src/sml/mgr/all/mgrcmdbuilder.c\
+ syncml_tk/src/sml/mgr/all/mgrcmddispatcher.c\
+ syncml_tk/src/sml/mgr/all/mgrinstancelist.c\
+ syncml_tk/src/sml/mgr/all/mgrinstancemgr.c\
+ syncml_tk/src/sml/mgr/all/mgrutil.c\
+ syncml_tk/src/sml/xlt/all/xltdec.c\
+ syncml_tk/src/sml/xlt/all/xltdecwbxml.c\
+ syncml_tk/src/sml/xlt/all/xltdecxml.c\
+ syncml_tk/src/sml/xlt/all/xltdevinf.c\
+ syncml_tk/src/sml/xlt/all/xltenc.c\
+ syncml_tk/src/sml/xlt/all/xltenccom.c\
+ syncml_tk/src/sml/xlt/all/xltencwbxml.c\
+ syncml_tk/src/sml/xlt/all/xltencxml.c\
+ syncml_tk/src/sml/xlt/all/xltmetinf.c\
+ syncml_tk/src/sml/xlt/all/xlttags.c\
+ syncml_tk/src/sml/xlt/all/xltutilstack.c
+
+EXPAT=\
+ expat/xmltok/xmltok.c\
+ expat/xmltok/xmlrole.c\
+ expat/xmlparse/xmlparse.c
+
+ZLIB=\
+ zlib/adler32.c\
+ zlib/compress.c\
+ zlib/crc32.c\
+ zlib/deflate.c\
+ zlib/gzio.c\
+ zlib/infback.c\
+ zlib/inffast.c\
+ zlib/inflate.c\
+ zlib/inftrees.c\
+ zlib/trees.c\
+ zlib/uncompr.c\
+ zlib/zutil.c
+
+C_BASICS=\
+ $(SYNCML_TK_SML)\
+ $(EXPAT)\
+ platform_adapters/linux/platform_exec.c\
+ sysync_SDK/Sources/SDK_util.c\
+ SQLite/sqlite3.c
+
+PLATFORM=\
+ platform_adapters/linux/configfiles.cpp\
+ platform_adapters/linux/profiling.cpp\
+ platform_adapters/linux/platform_time.cpp\
+ platform_adapters/unix_common/platform_mutex.cpp\
+ platform_adapters/sysyncinit.cpp
+
+PLATFORM_EXT=\
+ $(PLATFORM)\
+ platform_adapters/linux/platform_DLL.cpp\
+ platform_adapters/unix_common/platform_thread.cpp\
+ platform_adapters/unix_common/platform_file.cpp
+
+BINFILES=\
+ platform_adapters/binfile.cpp\
+ sysync/binfileimplds.cpp\
+ sysync/binfileimplclient.cpp\
+ sysync/binfilebase.cpp
+
+SYSYNC=\
+ sysync/sysync_utils.cpp\
+ sysync/sysync_b64.cpp\
+ sysync/sysync_md5.cpp\
+ sysync/syncsession.cpp\
+ sysync/syncappbase.cpp\
+ sysync/lineartime.cpp\
+ sysync/iso8601.cpp\
+ sysync/stringutils.cpp\
+ sysync/superdatastore.cpp\
+ sysync/scriptcontext.cpp\
+ sysync/itemfield.cpp\
+ sysync/mimediritemtype.cpp\
+ sysync/mimedirprofile.cpp\
+ sysync/multifielditem.cpp\
+ sysync/multifielditemtype.cpp\
+ sysync/remotedatastore.cpp\
+ sysync/syncitem.cpp\
+ sysync/syncitemtype.cpp\
+ sysync/simpleitem.cpp\
+ sysync/synccommand.cpp\
+ sysync/syncdatastore.cpp\
+ sysync/textitemtype.cpp\
+ sysync/vcalendaritemtype.cpp\
+ sysync/vcarditemtype.cpp\
+ sysync/syncexception.cpp\
+ sysync/configelement.cpp\
+ sysync/sysync_crc16.cpp\
+ sysync/timezones.cpp\
+ sysync/rrules.cpp\
+ sysync/localengineds.cpp\
+ sysync/debuglogger.cpp\
+ sysync/textprofile.cpp\
+ sysync/dataobjtype.cpp\
+ sysync/stdlogicds.cpp\
+ sysync/stdlogicagent.cpp\
+ sysync/customimplagent.cpp\
+ sysync/customimplds.cpp\
+ sysync/vtimezone.cpp
+
+CLIENTENGINE=\
+ $(SYSYNC)\
+ syncapps/clientEngine_custom/clientengine_custom_Base.cpp\
+ Transport_interfaces/engine/engineclientbase.cpp\
+ sysync_SDK/Sources/enginemodulebase.cpp\
+ sysync/engineentry.cpp\
+ sysync/engineinterface.cpp\
+ sysync/syncclient.cpp\
+ sysync/syncclientbase.cpp
+
+ODBC_DB=\
+ DB_interfaces/odbc_db/odbcapiagent.cpp\
+ DB_interfaces/odbc_db/odbcapids.cpp
+
+API_DB=\
+ DB_interfaces/api_db/pluginapiagent.cpp\
+ DB_interfaces/api_db/pluginapids.cpp\
+ DB_interfaces/api_db/dbapi.cpp\
+ DB_interfaces/api_db/DLL_interface.cpp\
+ DB_interfaces/api_db/sync_dbapiconnect.cpp\
+ sysync_SDK/Sources/SDK_support.cpp
+
+# --------------------------------------------------------
+INCLUDE_PLATFORM=\
+ -I platform_adapters/linux/\
+ -I platform_adapters/unix_common/\
+ -I platform_adapters/
+
+INCLUDE_SYNCML_TK_SML=\
+ -I syncml_tk/src/sml/inc/\
+ -I syncml_tk/src/sml/lib/inc/\
+ -I syncml_tk/src/sml/lib/\
+ -I syncml_tk/src/sml/mgr/inc/\
+ -I syncml_tk/src/sml/mgr/\
+ -I syncml_tk/src/sml/wsm/inc/\
+ -I syncml_tk/src/sml/xlt/inc/\
+ -I syncml_tk/src/sml/xlt/all/
+
+INCLUDE_EXPAT=\
+ -I expat/xmltok/\
+ -I expat/xmlparse/
+
+INCLUDE_CLIENTENGINE=\
+ -I syncapps/clientEngine_custom/\
+ -I Transport_interfaces/engine/\
+ -I sysync_SDK/Sources\
+ -I sysync/\
+ -I ./
+
+
+#############################
+####### TARGET: clientEngine
+#############################
+WD_OBJS_clientEngine=$(WD_OBJS)/clientEngine
+
+c_SRC_clientEngine=\
+ $(C_BASICS) $(ZLIB)
+
+cpp_SRC_clientEngine=\
+ $(PLATFORM_EXT)\
+ $(CLIENTENGINE)\
+ $(BINFILES)\
+ $(ODBC_DB)\
+ $(API_DB)
+
+OBJS_clientEngine+= $(c_SRC_clientEngine:.c=.c.o)
+OBJS_clientEngine+=$(cpp_SRC_clientEngine:.cpp=.cpp.o)
+
+INCLUDE_clientEngine=\
+ -include Targets/ReleasedProducts/clientEngine_opensource_linux/clientengine_demo_x86_linux_prefix.h\
+ -I Targets/ReleasedProducts/clientEngine_opensource_linux/\
+ $(INCLUDE_PLATFORM)\
+ $(INCLUDE_SYNCML_TK_SML)\
+ $(INCLUDE_EXPAT)\
+ $(INCLUDE_CLIENTENGINE)\
+ -I DB_interfaces/odbc_db/\
+ -I DB_interfaces/api_db/\
+ -I SQLite/\
+ -I zlib/\
+ -I /usr/include/
+
+clientEngine:
+ $(MAKE) -f $(MAKEFILE) sysync_client_engine.so TARGET=sysync_client_engine.so\
+ WDOP="$(WD_OBJS_clientEngine)"\
+ OBJS="$(addprefix $(WD_OBJS_clientEngine)/,$(OBJS_clientEngine))"\
+ LIBS="-L/usr/lib32 -L/usr/lib -lstdc++ -lpthread -lltdl -lpcre"\
+ INCL="$(INCLUDE_clientEngine)"
+
+sysync_client_engine.so: $(OBJS)
+ $(LD) -shared -Xlinker -soname=sysync_client_engine $(LDFLAGS) $(OBJS) $(LIBS)\
+ -o sysync_SDK/bin/Linux/sysync_client_engine.so
+
+ifeq ($(TARGET), sysync_client_engine.so)
+$(WDOP)/%.c.o: $(WD)/%.c
+ mkdir -p $(dir $@)
+ $(CC) -fPIC $(CFLAGS) $(INCL) $< -o $@
+
+$(WDOP)/%.cpp.o: $(WD)/%.cpp
+ mkdir -p $(dir $@)
+ $(CC) -fPIC $(CFLAGS) $(INCL) $< -o $@
+endif
+
+clean_clientEngine:
+ rm -f -r $(WD_OBJS_clientEngine)
diff --git a/src/expat/copying.txt b/src/expat/copying.txt
new file mode 100755
index 0000000..4b2def9
--- /dev/null
+++ b/src/expat/copying.txt
@@ -0,0 +1,22 @@
+Copyright (c) 1998, 1999, 2000 Thai Open Source Software Center Ltd
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
diff --git a/src/expat/xmlparse/xmlparse.c b/src/expat/xmlparse/xmlparse.c
new file mode 100755
index 0000000..4cf1e5f
--- /dev/null
+++ b/src/expat/xmlparse/xmlparse.c
@@ -0,0 +1,3927 @@
+/*
+Copyright (c) 1998, 1999, 2000 Thai Open Source Software Center Ltd
+See the file copying.txt for copying permission.
+*/
+
+#include "xmldef.h"
+#include "xmlparse.h"
+#include <stddef.h>
+
+#ifdef XML_UNICODE
+#define XML_ENCODE_MAX XML_UTF16_ENCODE_MAX
+#define XmlConvert XmlUtf16Convert
+#define XmlGetInternalEncoding XmlGetUtf16InternalEncoding
+#define XmlGetInternalEncodingNS XmlGetUtf16InternalEncodingNS
+#define XmlEncode XmlUtf16Encode
+#define MUST_CONVERT(enc, s) (!(enc)->isUtf16 || (((unsigned long)s) & 1))
+typedef unsigned short ICHAR;
+#else
+#define XML_ENCODE_MAX XML_UTF8_ENCODE_MAX
+#define XmlConvert XmlUtf8Convert
+#define XmlGetInternalEncoding XmlGetUtf8InternalEncoding
+#define XmlGetInternalEncodingNS XmlGetUtf8InternalEncodingNS
+#define XmlEncode XmlUtf8Encode
+#define MUST_CONVERT(enc, s) (!(enc)->isUtf8)
+typedef char ICHAR;
+#endif
+
+
+#ifndef XML_NS
+
+#define XmlInitEncodingNS XmlInitEncoding
+#define XmlInitUnknownEncodingNS XmlInitUnknownEncoding
+#undef XmlGetInternalEncodingNS
+#define XmlGetInternalEncodingNS XmlGetInternalEncoding
+#define XmlParseXmlDeclNS XmlParseXmlDecl
+
+#endif
+
+#ifdef XML_UNICODE_WCHAR_T
+#define XML_T(x) L ## x
+#else
+#define XML_T(x) x
+#endif
+
+/* Round up n to be a multiple of sz, where sz is a power of 2. */
+#define ROUND_UP(n, sz) (((n) + ((sz) - 1)) & ~((sz) - 1))
+
+#include "xmltok.h"
+#include "xmlrole.h"
+
+typedef const XML_Char *KEY;
+
+typedef struct {
+ KEY name;
+} NAMED;
+
+typedef struct {
+ NAMED **v;
+ size_t size;
+ size_t used;
+ size_t usedLim;
+} HASH_TABLE;
+
+typedef struct {
+ NAMED **p;
+ NAMED **end;
+} HASH_TABLE_ITER;
+
+#define INIT_TAG_BUF_SIZE 32 /* must be a multiple of sizeof(XML_Char) */
+#define INIT_DATA_BUF_SIZE 1024
+#define INIT_ATTS_SIZE 16
+#define INIT_BLOCK_SIZE 1024
+#define INIT_BUFFER_SIZE 1024
+
+#define EXPAND_SPARE 24
+
+typedef struct binding {
+ struct prefix *prefix;
+ struct binding *nextTagBinding;
+ struct binding *prevPrefixBinding;
+ const struct attribute_id *attId;
+ XML_Char *uri;
+ int uriLen;
+ int uriAlloc;
+} BINDING;
+
+typedef struct prefix {
+ const XML_Char *name;
+ BINDING *binding;
+} PREFIX;
+
+typedef struct {
+ const XML_Char *str;
+ const XML_Char *localPart;
+ int uriLen;
+} TAG_NAME;
+
+typedef struct tag {
+ struct tag *parent;
+ const char *rawName;
+ int rawNameLength;
+ TAG_NAME name;
+ char *buf;
+ char *bufEnd;
+ BINDING *bindings;
+} TAG;
+
+typedef struct {
+ const XML_Char *name;
+ const XML_Char *textPtr;
+ int textLen;
+ const XML_Char *systemId;
+ const XML_Char *base;
+ const XML_Char *publicId;
+ const XML_Char *notation;
+ char open;
+} ENTITY;
+
+typedef struct block {
+ struct block *next;
+ int size;
+ XML_Char s[1];
+} BLOCK;
+
+typedef struct {
+ BLOCK *blocks;
+ BLOCK *freeBlocks;
+ const XML_Char *end;
+ XML_Char *ptr;
+ XML_Char *start;
+} STRING_POOL;
+
+/* The XML_Char before the name is used to determine whether
+an attribute has been specified. */
+typedef struct attribute_id {
+ XML_Char *name;
+ PREFIX *prefix;
+ char maybeTokenized;
+ char xmlns;
+} ATTRIBUTE_ID;
+
+typedef struct {
+ const ATTRIBUTE_ID *id;
+ char isCdata;
+ const XML_Char *value;
+} DEFAULT_ATTRIBUTE;
+
+typedef struct {
+ const XML_Char *name;
+ PREFIX *prefix;
+ const ATTRIBUTE_ID *idAtt;
+ int nDefaultAtts;
+ int allocDefaultAtts;
+ DEFAULT_ATTRIBUTE *defaultAtts;
+} ELEMENT_TYPE;
+
+typedef struct {
+ HASH_TABLE generalEntities;
+ HASH_TABLE elementTypes;
+ HASH_TABLE attributeIds;
+ HASH_TABLE prefixes;
+ STRING_POOL pool;
+ int complete;
+ int standalone;
+#ifdef XML_DTD
+ HASH_TABLE paramEntities;
+#endif /* XML_DTD */
+ PREFIX defaultPrefix;
+} DTD;
+
+typedef struct open_internal_entity {
+ const char *internalEventPtr;
+ const char *internalEventEndPtr;
+ struct open_internal_entity *next;
+ ENTITY *entity;
+} OPEN_INTERNAL_ENTITY;
+
+typedef enum XML_Error Processor(XML_Parser parser,
+ const char *start,
+ const char *end,
+ const char **endPtr);
+
+static Processor prologProcessor;
+static Processor prologInitProcessor;
+static Processor contentProcessor;
+static Processor cdataSectionProcessor;
+#ifdef XML_DTD
+static Processor ignoreSectionProcessor;
+#endif /* XML_DTD */
+static Processor epilogProcessor;
+static Processor errorProcessor;
+static Processor externalEntityInitProcessor;
+static Processor externalEntityInitProcessor2;
+static Processor externalEntityInitProcessor3;
+static Processor externalEntityContentProcessor;
+
+static enum XML_Error
+handleUnknownEncoding(XML_Parser parser, const XML_Char *encodingName);
+static enum XML_Error
+processXmlDecl(XML_Parser parser, int isGeneralTextEntity, const char *, const char *);
+static enum XML_Error
+initializeEncoding(XML_Parser parser);
+static enum XML_Error
+doProlog(XML_Parser parser, const ENCODING *enc, const char *s,
+ const char *end, int tok, const char *next, const char **nextPtr);
+#ifdef XML_DTD
+static enum XML_Error
+processInternalParamEntity(XML_Parser parser, ENTITY *entity);
+#endif
+static enum XML_Error
+doContent(XML_Parser parser, int startTagLevel, const ENCODING *enc,
+ const char *start, const char *end, const char **endPtr);
+static enum XML_Error
+doCdataSection(XML_Parser parser, const ENCODING *, const char **startPtr, const char *end, const char **nextPtr);
+#ifdef XML_DTD
+static enum XML_Error
+doIgnoreSection(XML_Parser parser, const ENCODING *, const char **startPtr, const char *end, const char **nextPtr);
+#endif /* XML_DTD */
+static enum XML_Error storeAtts(XML_Parser parser, const ENCODING *, const char *s,
+ TAG_NAME *tagNamePtr, BINDING **bindingsPtr);
+static
+int addBinding(XML_Parser parser, PREFIX *prefix, const ATTRIBUTE_ID *attId, const XML_Char *uri, BINDING **bindingsPtr);
+static int
+defineAttribute(ELEMENT_TYPE *type, ATTRIBUTE_ID *, int isCdata, int isId, const XML_Char *dfltValue);
+static enum XML_Error
+storeAttributeValue(XML_Parser parser, const ENCODING *, int isCdata, const char *, const char *,
+ STRING_POOL *);
+static enum XML_Error
+appendAttributeValue(XML_Parser parser, const ENCODING *, int isCdata, const char *, const char *,
+ STRING_POOL *);
+static ATTRIBUTE_ID *
+getAttributeId(XML_Parser parser, const ENCODING *enc, const char *start, const char *end);
+static int setElementTypePrefix(XML_Parser parser, ELEMENT_TYPE *);
+static enum XML_Error
+storeEntityValue(XML_Parser parser, const ENCODING *enc, const char *start, const char *end);
+static int
+reportProcessingInstruction(XML_Parser parser, const ENCODING *enc, const char *start, const char *end);
+static int
+reportComment(XML_Parser parser, const ENCODING *enc, const char *start, const char *end);
+static void
+reportDefault(XML_Parser parser, const ENCODING *enc, const char *start, const char *end);
+
+static const XML_Char *getContext(XML_Parser parser);
+static int setContext(XML_Parser parser, const XML_Char *context);
+static void normalizePublicId(XML_Char *s);
+static int dtdInit(DTD *);
+static void dtdDestroy(DTD *);
+static int dtdCopy(DTD *newDtd, const DTD *oldDtd);
+static int copyEntityTable(HASH_TABLE *, STRING_POOL *, const HASH_TABLE *);
+#ifdef XML_DTD
+static void dtdSwap(DTD *, DTD *);
+#endif /* XML_DTD */
+static NAMED *lookup(HASH_TABLE *table, KEY name, size_t createSize);
+static void hashTableInit(HASH_TABLE *);
+static void hashTableDestroy(HASH_TABLE *);
+static void hashTableIterInit(HASH_TABLE_ITER *, const HASH_TABLE *);
+static NAMED *hashTableIterNext(HASH_TABLE_ITER *);
+static void poolInit(STRING_POOL *);
+static void poolClear(STRING_POOL *);
+static void poolDestroy(STRING_POOL *);
+static XML_Char *poolAppend(STRING_POOL *pool, const ENCODING *enc,
+ const char *ptr, const char *end);
+static XML_Char *poolStoreString(STRING_POOL *pool, const ENCODING *enc,
+ const char *ptr, const char *end);
+static int poolGrow(STRING_POOL *pool);
+static const XML_Char *poolCopyString(STRING_POOL *pool, const XML_Char *s);
+static const XML_Char *poolCopyStringN(STRING_POOL *pool, const XML_Char *s, int n);
+
+#define poolStart(pool) ((pool)->start)
+#define poolEnd(pool) ((pool)->ptr)
+#define poolLength(pool) ((pool)->ptr - (pool)->start)
+#define poolChop(pool) ((void)--(pool->ptr))
+#define poolLastChar(pool) (((pool)->ptr)[-1])
+#define poolDiscard(pool) ((pool)->ptr = (pool)->start)
+#define poolFinish(pool) ((pool)->start = (pool)->ptr)
+#define poolAppendChar(pool, c) \
+ (((pool)->ptr == (pool)->end && !poolGrow(pool)) \
+ ? 0 \
+ : ((*((pool)->ptr)++ = c), 1))
+
+typedef struct {
+ /* The first member must be userData so that the XML_GetUserData macro works. */
+ void *m_userData;
+ void *m_handlerArg;
+ char *m_buffer;
+ /* first character to be parsed */
+ const char *m_bufferPtr;
+ /* past last character to be parsed */
+ char *m_bufferEnd;
+ /* allocated end of buffer */
+ const char *m_bufferLim;
+ long m_parseEndByteIndex;
+ const char *m_parseEndPtr;
+ XML_Char *m_dataBuf;
+ XML_Char *m_dataBufEnd;
+ XML_StartElementHandler m_startElementHandler;
+ XML_EndElementHandler m_endElementHandler;
+ XML_CharacterDataHandler m_characterDataHandler;
+ XML_ProcessingInstructionHandler m_processingInstructionHandler;
+ XML_CommentHandler m_commentHandler;
+ XML_StartCdataSectionHandler m_startCdataSectionHandler;
+ XML_EndCdataSectionHandler m_endCdataSectionHandler;
+ XML_DefaultHandler m_defaultHandler;
+ XML_StartDoctypeDeclHandler m_startDoctypeDeclHandler;
+ XML_EndDoctypeDeclHandler m_endDoctypeDeclHandler;
+ XML_UnparsedEntityDeclHandler m_unparsedEntityDeclHandler;
+ XML_NotationDeclHandler m_notationDeclHandler;
+ XML_ExternalParsedEntityDeclHandler m_externalParsedEntityDeclHandler;
+ XML_InternalParsedEntityDeclHandler m_internalParsedEntityDeclHandler;
+ XML_StartNamespaceDeclHandler m_startNamespaceDeclHandler;
+ XML_EndNamespaceDeclHandler m_endNamespaceDeclHandler;
+ XML_NotStandaloneHandler m_notStandaloneHandler;
+ XML_ExternalEntityRefHandler m_externalEntityRefHandler;
+ void *m_externalEntityRefHandlerArg;
+ XML_UnknownEncodingHandler m_unknownEncodingHandler;
+ const ENCODING *m_encoding;
+ INIT_ENCODING m_initEncoding;
+ const ENCODING *m_internalEncoding;
+ const XML_Char *m_protocolEncodingName;
+ int m_ns;
+ void *m_unknownEncodingMem;
+ void *m_unknownEncodingData;
+ void *m_unknownEncodingHandlerData;
+ void (*m_unknownEncodingRelease)(void *);
+ PROLOG_STATE m_prologState;
+ Processor *m_processor;
+ enum XML_Error m_errorCode;
+ const char *m_eventPtr;
+ const char *m_eventEndPtr;
+ const char *m_positionPtr;
+ OPEN_INTERNAL_ENTITY *m_openInternalEntities;
+ int m_defaultExpandInternalEntities;
+ int m_tagLevel;
+ ENTITY *m_declEntity;
+ const XML_Char *m_declNotationName;
+ const XML_Char *m_declNotationPublicId;
+ ELEMENT_TYPE *m_declElementType;
+ ATTRIBUTE_ID *m_declAttributeId;
+ char m_declAttributeIsCdata;
+ char m_declAttributeIsId;
+ DTD m_dtd;
+ const XML_Char *m_curBase;
+ TAG *m_tagStack;
+ TAG *m_freeTagList;
+ BINDING *m_inheritedBindings;
+ BINDING *m_freeBindingList;
+ int m_attsSize;
+ int m_nSpecifiedAtts;
+ int m_idAttIndex;
+ ATTRIBUTE *m_atts;
+ POSITION m_position;
+ STRING_POOL m_tempPool;
+ STRING_POOL m_temp2Pool;
+ char *m_groupConnector;
+ unsigned m_groupSize;
+ int m_hadExternalDoctype;
+ XML_Char m_namespaceSeparator;
+#ifdef XML_DTD
+ enum XML_ParamEntityParsing m_paramEntityParsing;
+ XML_Parser m_parentParser;
+#endif
+} Parser;
+
+#define userData (((Parser *)parser)->m_userData)
+#define handlerArg (((Parser *)parser)->m_handlerArg)
+#define startElementHandler (((Parser *)parser)->m_startElementHandler)
+#define endElementHandler (((Parser *)parser)->m_endElementHandler)
+#define characterDataHandler (((Parser *)parser)->m_characterDataHandler)
+#define processingInstructionHandler (((Parser *)parser)->m_processingInstructionHandler)
+#define commentHandler (((Parser *)parser)->m_commentHandler)
+#define startCdataSectionHandler (((Parser *)parser)->m_startCdataSectionHandler)
+#define endCdataSectionHandler (((Parser *)parser)->m_endCdataSectionHandler)
+#define defaultHandler (((Parser *)parser)->m_defaultHandler)
+#define startDoctypeDeclHandler (((Parser *)parser)->m_startDoctypeDeclHandler)
+#define endDoctypeDeclHandler (((Parser *)parser)->m_endDoctypeDeclHandler)
+#define unparsedEntityDeclHandler (((Parser *)parser)->m_unparsedEntityDeclHandler)
+#define notationDeclHandler (((Parser *)parser)->m_notationDeclHandler)
+#define externalParsedEntityDeclHandler (((Parser *)parser)->m_externalParsedEntityDeclHandler)
+#define internalParsedEntityDeclHandler (((Parser *)parser)->m_internalParsedEntityDeclHandler)
+#define startNamespaceDeclHandler (((Parser *)parser)->m_startNamespaceDeclHandler)
+#define endNamespaceDeclHandler (((Parser *)parser)->m_endNamespaceDeclHandler)
+#define notStandaloneHandler (((Parser *)parser)->m_notStandaloneHandler)
+#define externalEntityRefHandler (((Parser *)parser)->m_externalEntityRefHandler)
+#define externalEntityRefHandlerArg (((Parser *)parser)->m_externalEntityRefHandlerArg)
+#define unknownEncodingHandler (((Parser *)parser)->m_unknownEncodingHandler)
+#define encoding (((Parser *)parser)->m_encoding)
+#define initEncoding (((Parser *)parser)->m_initEncoding)
+#define internalEncoding (((Parser *)parser)->m_internalEncoding)
+#define unknownEncodingMem (((Parser *)parser)->m_unknownEncodingMem)
+#define unknownEncodingData (((Parser *)parser)->m_unknownEncodingData)
+#define unknownEncodingHandlerData \
+ (((Parser *)parser)->m_unknownEncodingHandlerData)
+#define unknownEncodingRelease (((Parser *)parser)->m_unknownEncodingRelease)
+#define protocolEncodingName (((Parser *)parser)->m_protocolEncodingName)
+#define ns (((Parser *)parser)->m_ns)
+#define prologState (((Parser *)parser)->m_prologState)
+#define processor (((Parser *)parser)->m_processor)
+#define errorCode (((Parser *)parser)->m_errorCode)
+#define eventPtr (((Parser *)parser)->m_eventPtr)
+#define eventEndPtr (((Parser *)parser)->m_eventEndPtr)
+#define positionPtr (((Parser *)parser)->m_positionPtr)
+#define position (((Parser *)parser)->m_position)
+#define openInternalEntities (((Parser *)parser)->m_openInternalEntities)
+#define defaultExpandInternalEntities (((Parser *)parser)->m_defaultExpandInternalEntities)
+#define tagLevel (((Parser *)parser)->m_tagLevel)
+#define buffer (((Parser *)parser)->m_buffer)
+#define bufferPtr (((Parser *)parser)->m_bufferPtr)
+#define bufferEnd (((Parser *)parser)->m_bufferEnd)
+#define parseEndByteIndex (((Parser *)parser)->m_parseEndByteIndex)
+#define parseEndPtr (((Parser *)parser)->m_parseEndPtr)
+#define bufferLim (((Parser *)parser)->m_bufferLim)
+#define dataBuf (((Parser *)parser)->m_dataBuf)
+#define dataBufEnd (((Parser *)parser)->m_dataBufEnd)
+#define dtd (((Parser *)parser)->m_dtd)
+#define curBase (((Parser *)parser)->m_curBase)
+#define declEntity (((Parser *)parser)->m_declEntity)
+#define declNotationName (((Parser *)parser)->m_declNotationName)
+#define declNotationPublicId (((Parser *)parser)->m_declNotationPublicId)
+#define declElementType (((Parser *)parser)->m_declElementType)
+#define declAttributeId (((Parser *)parser)->m_declAttributeId)
+#define declAttributeIsCdata (((Parser *)parser)->m_declAttributeIsCdata)
+#define declAttributeIsId (((Parser *)parser)->m_declAttributeIsId)
+#define freeTagList (((Parser *)parser)->m_freeTagList)
+#define freeBindingList (((Parser *)parser)->m_freeBindingList)
+#define inheritedBindings (((Parser *)parser)->m_inheritedBindings)
+#define tagStack (((Parser *)parser)->m_tagStack)
+#define atts (((Parser *)parser)->m_atts)
+#define attsSize (((Parser *)parser)->m_attsSize)
+#define nSpecifiedAtts (((Parser *)parser)->m_nSpecifiedAtts)
+#define idAttIndex (((Parser *)parser)->m_idAttIndex)
+#define tempPool (((Parser *)parser)->m_tempPool)
+#define temp2Pool (((Parser *)parser)->m_temp2Pool)
+#define groupConnector (((Parser *)parser)->m_groupConnector)
+#define groupSize (((Parser *)parser)->m_groupSize)
+#define hadExternalDoctype (((Parser *)parser)->m_hadExternalDoctype)
+#define namespaceSeparator (((Parser *)parser)->m_namespaceSeparator)
+#ifdef XML_DTD
+#define parentParser (((Parser *)parser)->m_parentParser)
+#define paramEntityParsing (((Parser *)parser)->m_paramEntityParsing)
+#endif /* XML_DTD */
+
+#ifdef _MSC_VER
+#ifdef _DEBUG
+Parser *asParser(XML_Parser parser)
+{
+ return parser;
+}
+#endif
+#endif
+
+XML_Parser XML_ParserCreate(const XML_Char *encodingName)
+{
+ XML_Parser parser = malloc(sizeof(Parser));
+ if (!parser)
+ return parser;
+ processor = prologInitProcessor;
+ XmlPrologStateInit(&prologState);
+ userData = 0;
+ handlerArg = 0;
+ startElementHandler = 0;
+ endElementHandler = 0;
+ characterDataHandler = 0;
+ processingInstructionHandler = 0;
+ commentHandler = 0;
+ startCdataSectionHandler = 0;
+ endCdataSectionHandler = 0;
+ defaultHandler = 0;
+ startDoctypeDeclHandler = 0;
+ endDoctypeDeclHandler = 0;
+ unparsedEntityDeclHandler = 0;
+ notationDeclHandler = 0;
+ externalParsedEntityDeclHandler = 0;
+ internalParsedEntityDeclHandler = 0;
+ startNamespaceDeclHandler = 0;
+ endNamespaceDeclHandler = 0;
+ notStandaloneHandler = 0;
+ externalEntityRefHandler = 0;
+ externalEntityRefHandlerArg = parser;
+ unknownEncodingHandler = 0;
+ buffer = 0;
+ bufferPtr = 0;
+ bufferEnd = 0;
+ parseEndByteIndex = 0;
+ parseEndPtr = 0;
+ bufferLim = 0;
+ declElementType = 0;
+ declAttributeId = 0;
+ declEntity = 0;
+ declNotationName = 0;
+ declNotationPublicId = 0;
+ memset(&position, 0, sizeof(POSITION));
+ errorCode = XML_ERROR_NONE;
+ eventPtr = 0;
+ eventEndPtr = 0;
+ positionPtr = 0;
+ openInternalEntities = 0;
+ tagLevel = 0;
+ tagStack = 0;
+ freeTagList = 0;
+ freeBindingList = 0;
+ inheritedBindings = 0;
+ attsSize = INIT_ATTS_SIZE;
+ atts = malloc(attsSize * sizeof(ATTRIBUTE));
+ nSpecifiedAtts = 0;
+ dataBuf = malloc(INIT_DATA_BUF_SIZE * sizeof(XML_Char));
+ groupSize = 0;
+ groupConnector = 0;
+ hadExternalDoctype = 0;
+ unknownEncodingMem = 0;
+ unknownEncodingRelease = 0;
+ unknownEncodingData = 0;
+ unknownEncodingHandlerData = 0;
+ namespaceSeparator = '!';
+#ifdef XML_DTD
+ parentParser = 0;
+ paramEntityParsing = XML_PARAM_ENTITY_PARSING_NEVER;
+#endif
+ ns = 0;
+ poolInit(&tempPool);
+ poolInit(&temp2Pool);
+ protocolEncodingName = encodingName ? poolCopyString(&tempPool, encodingName) : 0;
+ curBase = 0;
+ if (!dtdInit(&dtd) || !atts || !dataBuf
+ || (encodingName && !protocolEncodingName)) {
+ XML_ParserFree(parser);
+ return 0;
+ }
+ dataBufEnd = dataBuf + INIT_DATA_BUF_SIZE;
+ XmlInitEncoding(&initEncoding, &encoding, 0);
+ internalEncoding = XmlGetInternalEncoding();
+ return parser;
+}
+
+XML_Parser XML_ParserCreateNS(const XML_Char *encodingName, XML_Char nsSep)
+{
+ static
+ const XML_Char implicitContext[] = {
+ XML_T('x'), XML_T('m'), XML_T('l'), XML_T('='),
+ XML_T('h'), XML_T('t'), XML_T('t'), XML_T('p'), XML_T(':'),
+ XML_T('/'), XML_T('/'), XML_T('w'), XML_T('w'), XML_T('w'),
+ XML_T('.'), XML_T('w'), XML_T('3'),
+ XML_T('.'), XML_T('o'), XML_T('r'), XML_T('g'),
+ XML_T('/'), XML_T('X'), XML_T('M'), XML_T('L'),
+ XML_T('/'), XML_T('1'), XML_T('9'), XML_T('9'), XML_T('8'),
+ XML_T('/'), XML_T('n'), XML_T('a'), XML_T('m'), XML_T('e'),
+ XML_T('s'), XML_T('p'), XML_T('a'), XML_T('c'), XML_T('e'),
+ XML_T('\0')
+ };
+
+ XML_Parser parser = XML_ParserCreate(encodingName);
+ if (parser) {
+ XmlInitEncodingNS(&initEncoding, &encoding, 0);
+ ns = 1;
+ internalEncoding = XmlGetInternalEncodingNS();
+ namespaceSeparator = nsSep;
+ }
+ if (!setContext(parser, implicitContext)) {
+ XML_ParserFree(parser);
+ return 0;
+ }
+ return parser;
+}
+
+int XML_SetEncoding(XML_Parser parser, const XML_Char *encodingName)
+{
+ if (!encodingName)
+ protocolEncodingName = 0;
+ else {
+ protocolEncodingName = poolCopyString(&tempPool, encodingName);
+ if (!protocolEncodingName)
+ return 0;
+ }
+ return 1;
+}
+
+XML_Parser XML_ExternalEntityParserCreate(XML_Parser oldParser,
+ const XML_Char *context,
+ const XML_Char *encodingName)
+{
+ XML_Parser parser = oldParser;
+ DTD *oldDtd = &dtd;
+ XML_StartElementHandler oldStartElementHandler = startElementHandler;
+ XML_EndElementHandler oldEndElementHandler = endElementHandler;
+ XML_CharacterDataHandler oldCharacterDataHandler = characterDataHandler;
+ XML_ProcessingInstructionHandler oldProcessingInstructionHandler = processingInstructionHandler;
+ XML_CommentHandler oldCommentHandler = commentHandler;
+ XML_StartCdataSectionHandler oldStartCdataSectionHandler = startCdataSectionHandler;
+ XML_EndCdataSectionHandler oldEndCdataSectionHandler = endCdataSectionHandler;
+ XML_DefaultHandler oldDefaultHandler = defaultHandler;
+ XML_UnparsedEntityDeclHandler oldUnparsedEntityDeclHandler = unparsedEntityDeclHandler;
+ XML_NotationDeclHandler oldNotationDeclHandler = notationDeclHandler;
+ XML_ExternalParsedEntityDeclHandler oldExternalParsedEntityDeclHandler = externalParsedEntityDeclHandler;
+ XML_InternalParsedEntityDeclHandler oldInternalParsedEntityDeclHandler = internalParsedEntityDeclHandler;
+ XML_StartNamespaceDeclHandler oldStartNamespaceDeclHandler = startNamespaceDeclHandler;
+ XML_EndNamespaceDeclHandler oldEndNamespaceDeclHandler = endNamespaceDeclHandler;
+ XML_NotStandaloneHandler oldNotStandaloneHandler = notStandaloneHandler;
+ XML_ExternalEntityRefHandler oldExternalEntityRefHandler = externalEntityRefHandler;
+ XML_UnknownEncodingHandler oldUnknownEncodingHandler = unknownEncodingHandler;
+ void *oldUserData = userData;
+ void *oldHandlerArg = handlerArg;
+ int oldDefaultExpandInternalEntities = defaultExpandInternalEntities;
+ void *oldExternalEntityRefHandlerArg = externalEntityRefHandlerArg;
+#ifdef XML_DTD
+ int oldParamEntityParsing = paramEntityParsing;
+#endif
+ parser = (ns
+ ? XML_ParserCreateNS(encodingName, namespaceSeparator)
+ : XML_ParserCreate(encodingName));
+ if (!parser)
+ return 0;
+ startElementHandler = oldStartElementHandler;
+ endElementHandler = oldEndElementHandler;
+ characterDataHandler = oldCharacterDataHandler;
+ processingInstructionHandler = oldProcessingInstructionHandler;
+ commentHandler = oldCommentHandler;
+ startCdataSectionHandler = oldStartCdataSectionHandler;
+ endCdataSectionHandler = oldEndCdataSectionHandler;
+ defaultHandler = oldDefaultHandler;
+ unparsedEntityDeclHandler = oldUnparsedEntityDeclHandler;
+ notationDeclHandler = oldNotationDeclHandler;
+ externalParsedEntityDeclHandler = oldExternalParsedEntityDeclHandler;
+ internalParsedEntityDeclHandler = oldInternalParsedEntityDeclHandler;
+ startNamespaceDeclHandler = oldStartNamespaceDeclHandler;
+ endNamespaceDeclHandler = oldEndNamespaceDeclHandler;
+ notStandaloneHandler = oldNotStandaloneHandler;
+ externalEntityRefHandler = oldExternalEntityRefHandler;
+ unknownEncodingHandler = oldUnknownEncodingHandler;
+ userData = oldUserData;
+ if (oldUserData == oldHandlerArg)
+ handlerArg = userData;
+ else
+ handlerArg = parser;
+ if (oldExternalEntityRefHandlerArg != oldParser)
+ externalEntityRefHandlerArg = oldExternalEntityRefHandlerArg;
+ defaultExpandInternalEntities = oldDefaultExpandInternalEntities;
+#ifdef XML_DTD
+ paramEntityParsing = oldParamEntityParsing;
+ if (context) {
+#endif /* XML_DTD */
+ if (!dtdCopy(&dtd, oldDtd) || !setContext(parser, context)) {
+ XML_ParserFree(parser);
+ return 0;
+ }
+ processor = externalEntityInitProcessor;
+#ifdef XML_DTD
+ }
+ else {
+ dtdSwap(&dtd, oldDtd);
+ parentParser = oldParser;
+ XmlPrologStateInitExternalEntity(&prologState);
+ dtd.complete = 1;
+ hadExternalDoctype = 1;
+ }
+#endif /* XML_DTD */
+ return parser;
+}
+
+static
+void destroyBindings(BINDING *bindings)
+{
+ for (;;) {
+ BINDING *b = bindings;
+ if (!b)
+ break;
+ bindings = b->nextTagBinding;
+ free(b->uri);
+ free(b);
+ }
+}
+
+void XML_ParserFree(XML_Parser parser)
+{
+ for (;;) {
+ TAG *p;
+ if (tagStack == 0) {
+ if (freeTagList == 0)
+ break;
+ tagStack = freeTagList;
+ freeTagList = 0;
+ }
+ p = tagStack;
+ tagStack = tagStack->parent;
+ free(p->buf);
+ destroyBindings(p->bindings);
+ free(p);
+ }
+ destroyBindings(freeBindingList);
+ destroyBindings(inheritedBindings);
+ poolDestroy(&tempPool);
+ poolDestroy(&temp2Pool);
+#ifdef XML_DTD
+ if (parentParser) {
+ if (hadExternalDoctype)
+ dtd.complete = 0;
+ dtdSwap(&dtd, &((Parser *)parentParser)->m_dtd);
+ }
+#endif /* XML_DTD */
+ dtdDestroy(&dtd);
+ free((void *)atts);
+ free(groupConnector);
+ free(buffer);
+ free(dataBuf);
+ free(unknownEncodingMem);
+ if (unknownEncodingRelease)
+ unknownEncodingRelease(unknownEncodingData);
+ free(parser);
+}
+
+void XML_UseParserAsHandlerArg(XML_Parser parser)
+{
+ handlerArg = parser;
+}
+
+void XML_SetUserData(XML_Parser parser, void *p)
+{
+ if (handlerArg == userData)
+ handlerArg = userData = p;
+ else
+ userData = p;
+}
+
+int XML_SetBase(XML_Parser parser, const XML_Char *p)
+{
+ if (p) {
+ p = poolCopyString(&dtd.pool, p);
+ if (!p)
+ return 0;
+ curBase = p;
+ }
+ else
+ curBase = 0;
+ return 1;
+}
+
+const XML_Char *XML_GetBase(XML_Parser parser)
+{
+ return curBase;
+}
+
+int XML_GetSpecifiedAttributeCount(XML_Parser parser)
+{
+ return nSpecifiedAtts;
+}
+
+int XML_GetIdAttributeIndex(XML_Parser parser)
+{
+ return idAttIndex;
+}
+
+void XML_SetElementHandler(XML_Parser parser,
+ XML_StartElementHandler start,
+ XML_EndElementHandler end)
+{
+ startElementHandler = start;
+ endElementHandler = end;
+}
+
+void XML_SetCharacterDataHandler(XML_Parser parser,
+ XML_CharacterDataHandler handler)
+{
+ characterDataHandler = handler;
+}
+
+void XML_SetProcessingInstructionHandler(XML_Parser parser,
+ XML_ProcessingInstructionHandler handler)
+{
+ processingInstructionHandler = handler;
+}
+
+void XML_SetCommentHandler(XML_Parser parser,
+ XML_CommentHandler handler)
+{
+ commentHandler = handler;
+}
+
+void XML_SetCdataSectionHandler(XML_Parser parser,
+ XML_StartCdataSectionHandler start,
+ XML_EndCdataSectionHandler end)
+{
+ startCdataSectionHandler = start;
+ endCdataSectionHandler = end;
+}
+
+void XML_SetDefaultHandler(XML_Parser parser,
+ XML_DefaultHandler handler)
+{
+ defaultHandler = handler;
+ defaultExpandInternalEntities = 0;
+}
+
+void XML_SetDefaultHandlerExpand(XML_Parser parser,
+ XML_DefaultHandler handler)
+{
+ defaultHandler = handler;
+ defaultExpandInternalEntities = 1;
+}
+
+void XML_SetDoctypeDeclHandler(XML_Parser parser,
+ XML_StartDoctypeDeclHandler start,
+ XML_EndDoctypeDeclHandler end)
+{
+ startDoctypeDeclHandler = start;
+ endDoctypeDeclHandler = end;
+}
+
+void XML_SetUnparsedEntityDeclHandler(XML_Parser parser,
+ XML_UnparsedEntityDeclHandler handler)
+{
+ unparsedEntityDeclHandler = handler;
+}
+
+void XML_SetExternalParsedEntityDeclHandler(XML_Parser parser,
+ XML_ExternalParsedEntityDeclHandler handler)
+{
+ externalParsedEntityDeclHandler = handler;
+}
+
+void XML_SetInternalParsedEntityDeclHandler(XML_Parser parser,
+ XML_InternalParsedEntityDeclHandler handler)
+{
+ internalParsedEntityDeclHandler = handler;
+}
+
+void XML_SetNotationDeclHandler(XML_Parser parser,
+ XML_NotationDeclHandler handler)
+{
+ notationDeclHandler = handler;
+}
+
+void XML_SetNamespaceDeclHandler(XML_Parser parser,
+ XML_StartNamespaceDeclHandler start,
+ XML_EndNamespaceDeclHandler end)
+{
+ startNamespaceDeclHandler = start;
+ endNamespaceDeclHandler = end;
+}
+
+void XML_SetNotStandaloneHandler(XML_Parser parser,
+ XML_NotStandaloneHandler handler)
+{
+ notStandaloneHandler = handler;
+}
+
+void XML_SetExternalEntityRefHandler(XML_Parser parser,
+ XML_ExternalEntityRefHandler handler)
+{
+ externalEntityRefHandler = handler;
+}
+
+void XML_SetExternalEntityRefHandlerArg(XML_Parser parser, void *arg)
+{
+ if (arg)
+ externalEntityRefHandlerArg = arg;
+ else
+ externalEntityRefHandlerArg = parser;
+}
+
+void XML_SetUnknownEncodingHandler(XML_Parser parser,
+ XML_UnknownEncodingHandler handler,
+ void *data)
+{
+ unknownEncodingHandler = handler;
+ unknownEncodingHandlerData = data;
+}
+
+int XML_SetParamEntityParsing(XML_Parser parser,
+ enum XML_ParamEntityParsing parsing)
+{
+#ifdef XML_DTD
+ paramEntityParsing = parsing;
+ return 1;
+#else
+ return parsing == XML_PARAM_ENTITY_PARSING_NEVER;
+#endif
+}
+
+int XML_Parse(XML_Parser parser, const char *s, int len, int isFinal)
+{
+ if (len == 0) {
+ if (!isFinal)
+ return 1;
+ positionPtr = bufferPtr;
+ errorCode = processor(parser, bufferPtr, parseEndPtr = bufferEnd, 0);
+ if (errorCode == XML_ERROR_NONE)
+ return 1;
+ eventEndPtr = eventPtr;
+ processor = errorProcessor;
+ return 0;
+ }
+ else if (bufferPtr == bufferEnd) {
+ const char *end;
+ int nLeftOver;
+ parseEndByteIndex += len;
+ positionPtr = s;
+ if (isFinal) {
+ errorCode = processor(parser, s, parseEndPtr = s + len, 0);
+ if (errorCode == XML_ERROR_NONE)
+ return 1;
+ eventEndPtr = eventPtr;
+ processor = errorProcessor;
+ return 0;
+ }
+ errorCode = processor(parser, s, parseEndPtr = s + len, &end);
+ if (errorCode != XML_ERROR_NONE) {
+ eventEndPtr = eventPtr;
+ processor = errorProcessor;
+ return 0;
+ }
+ XmlUpdatePosition(encoding, positionPtr, end, &position);
+ nLeftOver = s + len - end;
+ if (nLeftOver) {
+ if (buffer == 0 || nLeftOver > bufferLim - buffer) {
+ /* FIXME avoid integer overflow */
+ buffer = buffer == 0 ? malloc(len * 2) : realloc(buffer, len * 2);
+ /* FIXME storage leak if realloc fails */
+ if (!buffer) {
+ errorCode = XML_ERROR_NO_MEMORY;
+ eventPtr = eventEndPtr = 0;
+ processor = errorProcessor;
+ return 0;
+ }
+ bufferLim = buffer + len * 2;
+ }
+ memcpy(buffer, end, nLeftOver);
+ bufferPtr = buffer;
+ bufferEnd = buffer + nLeftOver;
+ }
+ return 1;
+ }
+ else {
+ memcpy(XML_GetBuffer(parser, len), s, len);
+ return XML_ParseBuffer(parser, len, isFinal);
+ }
+}
+
+int XML_ParseBuffer(XML_Parser parser, int len, int isFinal)
+{
+ const char *start = bufferPtr;
+ positionPtr = start;
+ bufferEnd += len;
+ parseEndByteIndex += len;
+ errorCode = processor(parser, start, parseEndPtr = bufferEnd,
+ isFinal ? (const char **)0 : &bufferPtr);
+ if (errorCode == XML_ERROR_NONE) {
+ if (!isFinal)
+ XmlUpdatePosition(encoding, positionPtr, bufferPtr, &position);
+ return 1;
+ }
+ else {
+ eventEndPtr = eventPtr;
+ processor = errorProcessor;
+ return 0;
+ }
+}
+
+void *XML_GetBuffer(XML_Parser parser, int len)
+{
+ if (len > bufferLim - bufferEnd) {
+ /* FIXME avoid integer overflow */
+ int neededSize = len + (bufferEnd - bufferPtr);
+ if (neededSize <= bufferLim - buffer) {
+ memmove(buffer, bufferPtr, bufferEnd - bufferPtr);
+ bufferEnd = buffer + (bufferEnd - bufferPtr);
+ bufferPtr = buffer;
+ }
+ else {
+ char *newBuf;
+ int bufferSize = bufferLim - bufferPtr;
+ if (bufferSize == 0)
+ bufferSize = INIT_BUFFER_SIZE;
+ do {
+ bufferSize *= 2;
+ } while (bufferSize < neededSize);
+ newBuf = malloc(bufferSize);
+ if (newBuf == 0) {
+ errorCode = XML_ERROR_NO_MEMORY;
+ return 0;
+ }
+ bufferLim = newBuf + bufferSize;
+ if (bufferPtr) {
+ memcpy(newBuf, bufferPtr, bufferEnd - bufferPtr);
+ free(buffer);
+ }
+ bufferEnd = newBuf + (bufferEnd - bufferPtr);
+ bufferPtr = buffer = newBuf;
+ }
+ }
+ return bufferEnd;
+}
+
+enum XML_Error XML_GetErrorCode(XML_Parser parser)
+{
+ return errorCode;
+}
+
+long XML_GetCurrentByteIndex(XML_Parser parser)
+{
+ if (eventPtr)
+ return parseEndByteIndex - (parseEndPtr - eventPtr);
+ return -1;
+}
+
+int XML_GetCurrentByteCount(XML_Parser parser)
+{
+ if (eventEndPtr && eventPtr)
+ return eventEndPtr - eventPtr;
+ return 0;
+}
+
+int XML_GetCurrentLineNumber(XML_Parser parser)
+{
+ if (eventPtr) {
+ XmlUpdatePosition(encoding, positionPtr, eventPtr, &position);
+ positionPtr = eventPtr;
+ }
+ return position.lineNumber + 1;
+}
+
+int XML_GetCurrentColumnNumber(XML_Parser parser)
+{
+ if (eventPtr) {
+ XmlUpdatePosition(encoding, positionPtr, eventPtr, &position);
+ positionPtr = eventPtr;
+ }
+ return position.columnNumber;
+}
+
+void XML_DefaultCurrent(XML_Parser parser)
+{
+ if (defaultHandler) {
+ if (openInternalEntities)
+ reportDefault(parser,
+ internalEncoding,
+ openInternalEntities->internalEventPtr,
+ openInternalEntities->internalEventEndPtr);
+ else
+ reportDefault(parser, encoding, eventPtr, eventEndPtr);
+ }
+}
+
+const XML_LChar *XML_ErrorString(int code)
+{
+ static const XML_LChar *message[] = {
+ 0,
+ XML_T("out of memory"),
+ XML_T("syntax error"),
+ XML_T("no element found"),
+ XML_T("not well-formed"),
+ XML_T("unclosed token"),
+ XML_T("unclosed token"),
+ XML_T("mismatched tag"),
+ XML_T("duplicate attribute"),
+ XML_T("junk after document element"),
+ XML_T("illegal parameter entity reference"),
+ XML_T("undefined entity"),
+ XML_T("recursive entity reference"),
+ XML_T("asynchronous entity"),
+ XML_T("reference to invalid character number"),
+ XML_T("reference to binary entity"),
+ XML_T("reference to external entity in attribute"),
+ XML_T("xml processing instruction not at start of external entity"),
+ XML_T("unknown encoding"),
+ XML_T("encoding specified in XML declaration is incorrect"),
+ XML_T("unclosed CDATA section"),
+ XML_T("error in processing external entity reference"),
+ XML_T("document is not standalone")
+ };
+ if (code > 0 && code < sizeof(message)/sizeof(message[0]))
+ return message[code];
+ return 0;
+}
+
+static
+enum XML_Error contentProcessor(XML_Parser parser,
+ const char *start,
+ const char *end,
+ const char **endPtr)
+{
+ return doContent(parser, 0, encoding, start, end, endPtr);
+}
+
+static
+enum XML_Error externalEntityInitProcessor(XML_Parser parser,
+ const char *start,
+ const char *end,
+ const char **endPtr)
+{
+ enum XML_Error result = initializeEncoding(parser);
+ if (result != XML_ERROR_NONE)
+ return result;
+ processor = externalEntityInitProcessor2;
+ return externalEntityInitProcessor2(parser, start, end, endPtr);
+}
+
+static
+enum XML_Error externalEntityInitProcessor2(XML_Parser parser,
+ const char *start,
+ const char *end,
+ const char **endPtr)
+{
+ const char *next;
+ int tok = XmlContentTok(encoding, start, end, &next);
+ switch (tok) {
+ case XML_TOK_BOM:
+ start = next;
+ break;
+ case XML_TOK_PARTIAL:
+ if (endPtr) {
+ *endPtr = start;
+ return XML_ERROR_NONE;
+ }
+ eventPtr = start;
+ return XML_ERROR_UNCLOSED_TOKEN;
+ case XML_TOK_PARTIAL_CHAR:
+ if (endPtr) {
+ *endPtr = start;
+ return XML_ERROR_NONE;
+ }
+ eventPtr = start;
+ return XML_ERROR_PARTIAL_CHAR;
+ }
+ processor = externalEntityInitProcessor3;
+ return externalEntityInitProcessor3(parser, start, end, endPtr);
+}
+
+static
+enum XML_Error externalEntityInitProcessor3(XML_Parser parser,
+ const char *start,
+ const char *end,
+ const char **endPtr)
+{
+ const char *next;
+ int tok = XmlContentTok(encoding, start, end, &next);
+ switch (tok) {
+ case XML_TOK_XML_DECL:
+ {
+ enum XML_Error result = processXmlDecl(parser, 1, start, next);
+ if (result != XML_ERROR_NONE)
+ return result;
+ start = next;
+ }
+ break;
+ case XML_TOK_PARTIAL:
+ if (endPtr) {
+ *endPtr = start;
+ return XML_ERROR_NONE;
+ }
+ eventPtr = start;
+ return XML_ERROR_UNCLOSED_TOKEN;
+ case XML_TOK_PARTIAL_CHAR:
+ if (endPtr) {
+ *endPtr = start;
+ return XML_ERROR_NONE;
+ }
+ eventPtr = start;
+ return XML_ERROR_PARTIAL_CHAR;
+ }
+ processor = externalEntityContentProcessor;
+ tagLevel = 1;
+ return doContent(parser, 1, encoding, start, end, endPtr);
+}
+
+static
+enum XML_Error externalEntityContentProcessor(XML_Parser parser,
+ const char *start,
+ const char *end,
+ const char **endPtr)
+{
+ return doContent(parser, 1, encoding, start, end, endPtr);
+}
+
+static enum XML_Error
+doContent(XML_Parser parser,
+ int startTagLevel,
+ const ENCODING *enc,
+ const char *s,
+ const char *end,
+ const char **nextPtr)
+{
+ const char **eventPP;
+ const char **eventEndPP;
+ if (enc == encoding) {
+ eventPP = &eventPtr;
+ eventEndPP = &eventEndPtr;
+ }
+ else {
+ eventPP = &(openInternalEntities->internalEventPtr);
+ eventEndPP = &(openInternalEntities->internalEventEndPtr);
+ }
+ *eventPP = s;
+ for (;;) {
+ const char *next = s; /* XmlContentTok doesn't always set the last arg */
+ int tok = XmlContentTok(enc, s, end, &next);
+ *eventEndPP = next;
+ switch (tok) {
+ case XML_TOK_TRAILING_CR:
+ if (nextPtr) {
+ *nextPtr = s;
+ return XML_ERROR_NONE;
+ }
+ *eventEndPP = end;
+ if (characterDataHandler) {
+ XML_Char c = 0xA;
+ characterDataHandler(handlerArg, &c, 1);
+ }
+ else if (defaultHandler)
+ reportDefault(parser, enc, s, end);
+ if (startTagLevel == 0)
+ return XML_ERROR_NO_ELEMENTS;
+ if (tagLevel != startTagLevel)
+ return XML_ERROR_ASYNC_ENTITY;
+ return XML_ERROR_NONE;
+ case XML_TOK_NONE:
+ if (nextPtr) {
+ *nextPtr = s;
+ return XML_ERROR_NONE;
+ }
+ if (startTagLevel > 0) {
+ if (tagLevel != startTagLevel)
+ return XML_ERROR_ASYNC_ENTITY;
+ return XML_ERROR_NONE;
+ }
+ return XML_ERROR_NO_ELEMENTS;
+ case XML_TOK_INVALID:
+ *eventPP = next;
+ return XML_ERROR_INVALID_TOKEN;
+ case XML_TOK_PARTIAL:
+ if (nextPtr) {
+ *nextPtr = s;
+ return XML_ERROR_NONE;
+ }
+ return XML_ERROR_UNCLOSED_TOKEN;
+ case XML_TOK_PARTIAL_CHAR:
+ if (nextPtr) {
+ *nextPtr = s;
+ return XML_ERROR_NONE;
+ }
+ return XML_ERROR_PARTIAL_CHAR;
+ case XML_TOK_ENTITY_REF:
+ {
+ const XML_Char *name;
+ ENTITY *entity;
+ XML_Char ch = XmlPredefinedEntityName(enc,
+ s + enc->minBytesPerChar,
+ next - enc->minBytesPerChar);
+ if (ch) {
+ if (characterDataHandler)
+ characterDataHandler(handlerArg, &ch, 1);
+ else if (defaultHandler)
+ reportDefault(parser, enc, s, next);
+ break;
+ }
+ name = poolStoreString(&dtd.pool, enc,
+ s + enc->minBytesPerChar,
+ next - enc->minBytesPerChar);
+ if (!name)
+ return XML_ERROR_NO_MEMORY;
+ entity = (ENTITY *)lookup(&dtd.generalEntities, name, 0);
+ poolDiscard(&dtd.pool);
+ if (!entity) {
+ if (dtd.complete || dtd.standalone)
+ return XML_ERROR_UNDEFINED_ENTITY;
+ if (defaultHandler)
+ reportDefault(parser, enc, s, next);
+ break;
+ }
+ if (entity->open)
+ return XML_ERROR_RECURSIVE_ENTITY_REF;
+ if (entity->notation)
+ return XML_ERROR_BINARY_ENTITY_REF;
+ if (entity) {
+ if (entity->textPtr) {
+ enum XML_Error result;
+ OPEN_INTERNAL_ENTITY openEntity;
+ if (defaultHandler && !defaultExpandInternalEntities) {
+ reportDefault(parser, enc, s, next);
+ break;
+ }
+ entity->open = 1;
+ openEntity.next = openInternalEntities;
+ openInternalEntities = &openEntity;
+ openEntity.entity = entity;
+ openEntity.internalEventPtr = 0;
+ openEntity.internalEventEndPtr = 0;
+ result = doContent(parser,
+ tagLevel,
+ internalEncoding,
+ (char *)entity->textPtr,
+ (char *)(entity->textPtr + entity->textLen),
+ 0);
+ entity->open = 0;
+ openInternalEntities = openEntity.next;
+ if (result)
+ return result;
+ }
+ else if (externalEntityRefHandler) {
+ const XML_Char *context;
+ entity->open = 1;
+ context = getContext(parser);
+ entity->open = 0;
+ if (!context)
+ return XML_ERROR_NO_MEMORY;
+ if (!externalEntityRefHandler(externalEntityRefHandlerArg,
+ context,
+ entity->base,
+ entity->systemId,
+ entity->publicId))
+ return XML_ERROR_EXTERNAL_ENTITY_HANDLING;
+ poolDiscard(&tempPool);
+ }
+ else if (defaultHandler)
+ reportDefault(parser, enc, s, next);
+ }
+ break;
+ }
+ case XML_TOK_START_TAG_WITH_ATTS:
+ if (!startElementHandler) {
+ enum XML_Error result = storeAtts(parser, enc, s, 0, 0);
+ if (result)
+ return result;
+ }
+ /* fall through */
+ case XML_TOK_START_TAG_NO_ATTS:
+ {
+ TAG *tag;
+ if (freeTagList) {
+ tag = freeTagList;
+ freeTagList = freeTagList->parent;
+ }
+ else {
+ tag = malloc(sizeof(TAG));
+ if (!tag)
+ return XML_ERROR_NO_MEMORY;
+ tag->buf = malloc(INIT_TAG_BUF_SIZE);
+ if (!tag->buf)
+ return XML_ERROR_NO_MEMORY;
+ tag->bufEnd = tag->buf + INIT_TAG_BUF_SIZE;
+ }
+ tag->bindings = 0;
+ tag->parent = tagStack;
+ tagStack = tag;
+ tag->name.localPart = 0;
+ tag->rawName = s + enc->minBytesPerChar;
+ tag->rawNameLength = XmlNameLength(enc, tag->rawName);
+ if (nextPtr) {
+ /* Need to guarantee that:
+ tag->buf + ROUND_UP(tag->rawNameLength, sizeof(XML_Char)) <= tag->bufEnd - sizeof(XML_Char) */
+ if (tag->rawNameLength + (int)(sizeof(XML_Char) - 1) + (int)sizeof(XML_Char) > tag->bufEnd - tag->buf) {
+ int bufSize = tag->rawNameLength * 4;
+ bufSize = ROUND_UP(bufSize, sizeof(XML_Char));
+ tag->buf = realloc(tag->buf, bufSize);
+ if (!tag->buf)
+ return XML_ERROR_NO_MEMORY;
+ tag->bufEnd = tag->buf + bufSize;
+ }
+ memcpy(tag->buf, tag->rawName, tag->rawNameLength);
+ tag->rawName = tag->buf;
+ }
+ ++tagLevel;
+ if (startElementHandler) {
+ enum XML_Error result;
+ XML_Char *toPtr;
+ for (;;) {
+ const char *rawNameEnd = tag->rawName + tag->rawNameLength;
+ const char *fromPtr = tag->rawName;
+ int bufSize;
+ if (nextPtr)
+ toPtr = (XML_Char *)(tag->buf + ROUND_UP(tag->rawNameLength, sizeof(XML_Char)));
+ else
+ toPtr = (XML_Char *)tag->buf;
+ tag->name.str = toPtr;
+ XmlConvert(enc,
+ &fromPtr, rawNameEnd,
+ (ICHAR **)&toPtr, (ICHAR *)tag->bufEnd - 1);
+ if (fromPtr == rawNameEnd)
+ break;
+ bufSize = (tag->bufEnd - tag->buf) << 1;
+ tag->buf = realloc(tag->buf, bufSize);
+ if (!tag->buf)
+ return XML_ERROR_NO_MEMORY;
+ tag->bufEnd = tag->buf + bufSize;
+ if (nextPtr)
+ tag->rawName = tag->buf;
+ }
+ *toPtr = XML_T('\0');
+ result = storeAtts(parser, enc, s, &(tag->name), &(tag->bindings));
+ if (result)
+ return result;
+ startElementHandler(handlerArg, tag->name.str, (const XML_Char **)atts);
+ poolClear(&tempPool);
+ }
+ else {
+ tag->name.str = 0;
+ if (defaultHandler)
+ reportDefault(parser, enc, s, next);
+ }
+ break;
+ }
+ case XML_TOK_EMPTY_ELEMENT_WITH_ATTS:
+ if (!startElementHandler) {
+ enum XML_Error result = storeAtts(parser, enc, s, 0, 0);
+ if (result)
+ return result;
+ }
+ /* fall through */
+ case XML_TOK_EMPTY_ELEMENT_NO_ATTS:
+ if (startElementHandler || endElementHandler) {
+ const char *rawName = s + enc->minBytesPerChar;
+ enum XML_Error result;
+ BINDING *bindings = 0;
+ TAG_NAME name;
+ name.str = poolStoreString(&tempPool, enc, rawName,
+ rawName + XmlNameLength(enc, rawName));
+ if (!name.str)
+ return XML_ERROR_NO_MEMORY;
+ poolFinish(&tempPool);
+ result = storeAtts(parser, enc, s, &name, &bindings);
+ if (result)
+ return result;
+ poolFinish(&tempPool);
+ if (startElementHandler)
+ startElementHandler(handlerArg, name.str, (const XML_Char **)atts);
+ if (endElementHandler) {
+ if (startElementHandler)
+ *eventPP = *eventEndPP;
+ endElementHandler(handlerArg, name.str);
+ }
+ poolClear(&tempPool);
+ while (bindings) {
+ BINDING *b = bindings;
+ if (endNamespaceDeclHandler)
+ endNamespaceDeclHandler(handlerArg, b->prefix->name);
+ bindings = bindings->nextTagBinding;
+ b->nextTagBinding = freeBindingList;
+ freeBindingList = b;
+ b->prefix->binding = b->prevPrefixBinding;
+ }
+ }
+ else if (defaultHandler)
+ reportDefault(parser, enc, s, next);
+ if (tagLevel == 0)
+ return epilogProcessor(parser, next, end, nextPtr);
+ break;
+ case XML_TOK_END_TAG:
+ if (tagLevel == startTagLevel)
+ return XML_ERROR_ASYNC_ENTITY;
+ else {
+ int len;
+ const char *rawName;
+ TAG *tag = tagStack;
+ tagStack = tag->parent;
+ tag->parent = freeTagList;
+ freeTagList = tag;
+ rawName = s + enc->minBytesPerChar*2;
+ len = XmlNameLength(enc, rawName);
+ if (len != tag->rawNameLength
+ || memcmp(tag->rawName, rawName, len) != 0) {
+ *eventPP = rawName;
+ return XML_ERROR_TAG_MISMATCH;
+ }
+ --tagLevel;
+ if (endElementHandler && tag->name.str) {
+ if (tag->name.localPart) {
+ XML_Char *to = (XML_Char *)tag->name.str + tag->name.uriLen;
+ const XML_Char *from = tag->name.localPart;
+ while ((*to++ = *from++) != 0)
+ ;
+ }
+ endElementHandler(handlerArg, tag->name.str);
+ }
+ else if (defaultHandler)
+ reportDefault(parser, enc, s, next);
+ while (tag->bindings) {
+ BINDING *b = tag->bindings;
+ if (endNamespaceDeclHandler)
+ endNamespaceDeclHandler(handlerArg, b->prefix->name);
+ tag->bindings = tag->bindings->nextTagBinding;
+ b->nextTagBinding = freeBindingList;
+ freeBindingList = b;
+ b->prefix->binding = b->prevPrefixBinding;
+ }
+ if (tagLevel == 0)
+ return epilogProcessor(parser, next, end, nextPtr);
+ }
+ break;
+ case XML_TOK_CHAR_REF:
+ {
+ int n = XmlCharRefNumber(enc, s);
+ if (n < 0)
+ return XML_ERROR_BAD_CHAR_REF;
+ if (characterDataHandler) {
+ XML_Char buf[XML_ENCODE_MAX];
+ characterDataHandler(handlerArg, buf, XmlEncode(n, (ICHAR *)buf));
+ }
+ else if (defaultHandler)
+ reportDefault(parser, enc, s, next);
+ }
+ break;
+ case XML_TOK_XML_DECL:
+ return XML_ERROR_MISPLACED_XML_PI;
+ case XML_TOK_DATA_NEWLINE:
+ if (characterDataHandler) {
+ XML_Char c = 0xA;
+ characterDataHandler(handlerArg, &c, 1);
+ }
+ else if (defaultHandler)
+ reportDefault(parser, enc, s, next);
+ break;
+ case XML_TOK_CDATA_SECT_OPEN:
+ {
+ enum XML_Error result;
+ if (startCdataSectionHandler)
+ startCdataSectionHandler(handlerArg);
+#if 0
+ /* Suppose you doing a transformation on a document that involves
+ changing only the character data. You set up a defaultHandler
+ and a characterDataHandler. The defaultHandler simply copies
+ characters through. The characterDataHandler does the transformation
+ and writes the characters out escaping them as necessary. This case
+ will fail to work if we leave out the following two lines (because &
+ and < inside CDATA sections will be incorrectly escaped).
+
+ However, now we have a start/endCdataSectionHandler, so it seems
+ easier to let the user deal with this. */
+
+ else if (characterDataHandler)
+ characterDataHandler(handlerArg, dataBuf, 0);
+#endif
+ else if (defaultHandler)
+ reportDefault(parser, enc, s, next);
+ result = doCdataSection(parser, enc, &next, end, nextPtr);
+ if (!next) {
+ processor = cdataSectionProcessor;
+ return result;
+ }
+ }
+ break;
+ case XML_TOK_TRAILING_RSQB:
+ if (nextPtr) {
+ *nextPtr = s;
+ return XML_ERROR_NONE;
+ }
+ if (characterDataHandler) {
+ if (MUST_CONVERT(enc, s)) {
+ ICHAR *dataPtr = (ICHAR *)dataBuf;
+ XmlConvert(enc, &s, end, &dataPtr, (ICHAR *)dataBufEnd);
+ characterDataHandler(handlerArg, dataBuf, dataPtr - (ICHAR *)dataBuf);
+ }
+ else
+ characterDataHandler(handlerArg,
+ (XML_Char *)s,
+ (XML_Char *)end - (XML_Char *)s);
+ }
+ else if (defaultHandler)
+ reportDefault(parser, enc, s, end);
+ if (startTagLevel == 0) {
+ *eventPP = end;
+ return XML_ERROR_NO_ELEMENTS;
+ }
+ if (tagLevel != startTagLevel) {
+ *eventPP = end;
+ return XML_ERROR_ASYNC_ENTITY;
+ }
+ return XML_ERROR_NONE;
+ case XML_TOK_DATA_CHARS:
+ if (characterDataHandler) {
+ if (MUST_CONVERT(enc, s)) {
+ for (;;) {
+ ICHAR *dataPtr = (ICHAR *)dataBuf;
+ XmlConvert(enc, &s, next, &dataPtr, (ICHAR *)dataBufEnd);
+ *eventEndPP = s;
+ characterDataHandler(handlerArg, dataBuf, dataPtr - (ICHAR *)dataBuf);
+ if (s == next)
+ break;
+ *eventPP = s;
+ }
+ }
+ else
+ characterDataHandler(handlerArg,
+ (XML_Char *)s,
+ (XML_Char *)next - (XML_Char *)s);
+ }
+ else if (defaultHandler)
+ reportDefault(parser, enc, s, next);
+ break;
+ case XML_TOK_PI:
+ if (!reportProcessingInstruction(parser, enc, s, next))
+ return XML_ERROR_NO_MEMORY;
+ break;
+ case XML_TOK_COMMENT:
+ if (!reportComment(parser, enc, s, next))
+ return XML_ERROR_NO_MEMORY;
+ break;
+ default:
+ if (defaultHandler)
+ reportDefault(parser, enc, s, next);
+ break;
+ }
+ *eventPP = s = next;
+ }
+ /* not reached */
+}
+
+/* If tagNamePtr is non-null, build a real list of attributes,
+otherwise just check the attributes for well-formedness. */
+
+static enum XML_Error storeAtts(XML_Parser parser, const ENCODING *enc,
+ const char *attStr, TAG_NAME *tagNamePtr,
+ BINDING **bindingsPtr)
+{
+ ELEMENT_TYPE *elementType = 0;
+ int nDefaultAtts = 0;
+ const XML_Char **appAtts; /* the attribute list to pass to the application */
+ int attIndex = 0;
+ int i;
+ int n;
+ int nPrefixes = 0;
+ BINDING *binding;
+ const XML_Char *localPart;
+
+ /* lookup the element type name */
+ if (tagNamePtr) {
+ elementType = (ELEMENT_TYPE *)lookup(&dtd.elementTypes, tagNamePtr->str, 0);
+ if (!elementType) {
+ tagNamePtr->str = poolCopyString(&dtd.pool, tagNamePtr->str);
+ if (!tagNamePtr->str)
+ return XML_ERROR_NO_MEMORY;
+ elementType = (ELEMENT_TYPE *)lookup(&dtd.elementTypes, tagNamePtr->str, sizeof(ELEMENT_TYPE));
+ if (!elementType)
+ return XML_ERROR_NO_MEMORY;
+ if (ns && !setElementTypePrefix(parser, elementType))
+ return XML_ERROR_NO_MEMORY;
+ }
+ nDefaultAtts = elementType->nDefaultAtts;
+ }
+ /* get the attributes from the tokenizer */
+ n = XmlGetAttributes(enc, attStr, attsSize, atts);
+ if (n + nDefaultAtts > attsSize) {
+ int oldAttsSize = attsSize;
+ attsSize = n + nDefaultAtts + INIT_ATTS_SIZE;
+ atts = realloc((void *)atts, attsSize * sizeof(ATTRIBUTE));
+ if (!atts)
+ return XML_ERROR_NO_MEMORY;
+ if (n > oldAttsSize)
+ XmlGetAttributes(enc, attStr, n, atts);
+ }
+ appAtts = (const XML_Char **)atts;
+ for (i = 0; i < n; i++) {
+ /* add the name and value to the attribute list */
+ ATTRIBUTE_ID *attId = getAttributeId(parser, enc, atts[i].name,
+ atts[i].name
+ + XmlNameLength(enc, atts[i].name));
+ if (!attId)
+ return XML_ERROR_NO_MEMORY;
+ /* detect duplicate attributes */
+ if ((attId->name)[-1]) {
+ if (enc == encoding)
+ eventPtr = atts[i].name;
+ return XML_ERROR_DUPLICATE_ATTRIBUTE;
+ }
+ (attId->name)[-1] = 1;
+ appAtts[attIndex++] = attId->name;
+ if (!atts[i].normalized) {
+ enum XML_Error result;
+ int isCdata = 1;
+
+ /* figure out whether declared as other than CDATA */
+ if (attId->maybeTokenized) {
+ int j;
+ for (j = 0; j < nDefaultAtts; j++) {
+ if (attId == elementType->defaultAtts[j].id) {
+ isCdata = elementType->defaultAtts[j].isCdata;
+ break;
+ }
+ }
+ }
+
+ /* normalize the attribute value */
+ result = storeAttributeValue(parser, enc, isCdata,
+ atts[i].valuePtr, atts[i].valueEnd,
+ &tempPool);
+ if (result)
+ return result;
+ if (tagNamePtr) {
+ appAtts[attIndex] = poolStart(&tempPool);
+ poolFinish(&tempPool);
+ }
+ else
+ poolDiscard(&tempPool);
+ }
+ else if (tagNamePtr) {
+ /* the value did not need normalizing */
+ appAtts[attIndex] = poolStoreString(&tempPool, enc, atts[i].valuePtr, atts[i].valueEnd);
+ if (appAtts[attIndex] == 0)
+ return XML_ERROR_NO_MEMORY;
+ poolFinish(&tempPool);
+ }
+ /* handle prefixed attribute names */
+ if (attId->prefix && tagNamePtr) {
+ if (attId->xmlns) {
+ /* deal with namespace declarations here */
+ if (!addBinding(parser, attId->prefix, attId, appAtts[attIndex], bindingsPtr))
+ return XML_ERROR_NO_MEMORY;
+ --attIndex;
+ }
+ else {
+ /* deal with other prefixed names later */
+ attIndex++;
+ nPrefixes++;
+ (attId->name)[-1] = 2;
+ }
+ }
+ else
+ attIndex++;
+ }
+ if (tagNamePtr) {
+ int j;
+ nSpecifiedAtts = attIndex;
+ if (elementType->idAtt && (elementType->idAtt->name)[-1]) {
+ for (i = 0; i < attIndex; i += 2)
+ if (appAtts[i] == elementType->idAtt->name) {
+ idAttIndex = i;
+ break;
+ }
+ }
+ else
+ idAttIndex = -1;
+ /* do attribute defaulting */
+ for (j = 0; j < nDefaultAtts; j++) {
+ const DEFAULT_ATTRIBUTE *da = elementType->defaultAtts + j;
+ if (!(da->id->name)[-1] && da->value) {
+ if (da->id->prefix) {
+ if (da->id->xmlns) {
+ if (!addBinding(parser, da->id->prefix, da->id, da->value, bindingsPtr))
+ return XML_ERROR_NO_MEMORY;
+ }
+ else {
+ (da->id->name)[-1] = 2;
+ nPrefixes++;
+ appAtts[attIndex++] = da->id->name;
+ appAtts[attIndex++] = da->value;
+ }
+ }
+ else {
+ (da->id->name)[-1] = 1;
+ appAtts[attIndex++] = da->id->name;
+ appAtts[attIndex++] = da->value;
+ }
+ }
+ }
+ appAtts[attIndex] = 0;
+ }
+ i = 0;
+ if (nPrefixes) {
+ /* expand prefixed attribute names */
+ for (; i < attIndex; i += 2) {
+ if (appAtts[i][-1] == 2) {
+ ATTRIBUTE_ID *id;
+ ((XML_Char *)(appAtts[i]))[-1] = 0;
+ id = (ATTRIBUTE_ID *)lookup(&dtd.attributeIds, appAtts[i], 0);
+ if (id->prefix->binding) {
+ int j;
+ const BINDING *b = id->prefix->binding;
+ const XML_Char *s = appAtts[i];
+ for (j = 0; j < b->uriLen; j++) {
+ if (!poolAppendChar(&tempPool, b->uri[j]))
+ return XML_ERROR_NO_MEMORY;
+ }
+ while (*s++ != ':')
+ ;
+ do {
+ if (!poolAppendChar(&tempPool, *s))
+ return XML_ERROR_NO_MEMORY;
+ } while (*s++);
+ appAtts[i] = poolStart(&tempPool);
+ poolFinish(&tempPool);
+ }
+ if (!--nPrefixes)
+ break;
+ }
+ else
+ ((XML_Char *)(appAtts[i]))[-1] = 0;
+ }
+ }
+ /* clear the flags that say whether attributes were specified */
+ for (; i < attIndex; i += 2)
+ ((XML_Char *)(appAtts[i]))[-1] = 0;
+ if (!tagNamePtr)
+ return XML_ERROR_NONE;
+ for (binding = *bindingsPtr; binding; binding = binding->nextTagBinding)
+ binding->attId->name[-1] = 0;
+ /* expand the element type name */
+ if (elementType->prefix) {
+ binding = elementType->prefix->binding;
+ if (!binding)
+ return XML_ERROR_NONE;
+ localPart = tagNamePtr->str;
+ while (*localPart++ != XML_T(':'))
+ ;
+ }
+ else if (dtd.defaultPrefix.binding) {
+ binding = dtd.defaultPrefix.binding;
+ localPart = tagNamePtr->str;
+ }
+ else
+ return XML_ERROR_NONE;
+ tagNamePtr->localPart = localPart;
+ tagNamePtr->uriLen = binding->uriLen;
+ for (i = 0; localPart[i++];)
+ ;
+ n = i + binding->uriLen;
+ if (n > binding->uriAlloc) {
+ TAG *p;
+ XML_Char *uri = malloc((n + EXPAND_SPARE) * sizeof(XML_Char));
+ if (!uri)
+ return XML_ERROR_NO_MEMORY;
+ binding->uriAlloc = n + EXPAND_SPARE;
+ memcpy(uri, binding->uri, binding->uriLen * sizeof(XML_Char));
+ for (p = tagStack; p; p = p->parent)
+ if (p->name.str == binding->uri)
+ p->name.str = uri;
+ free(binding->uri);
+ binding->uri = uri;
+ }
+ memcpy(binding->uri + binding->uriLen, localPart, i * sizeof(XML_Char));
+ tagNamePtr->str = binding->uri;
+ return XML_ERROR_NONE;
+}
+
+static
+int addBinding(XML_Parser parser, PREFIX *prefix, const ATTRIBUTE_ID *attId, const XML_Char *uri, BINDING **bindingsPtr)
+{
+ BINDING *b;
+ int len;
+ for (len = 0; uri[len]; len++)
+ ;
+ if (namespaceSeparator)
+ len++;
+ if (freeBindingList) {
+ b = freeBindingList;
+ if (len > b->uriAlloc) {
+ b->uri = realloc(b->uri, sizeof(XML_Char) * (len + EXPAND_SPARE));
+ if (!b->uri)
+ return 0;
+ b->uriAlloc = len + EXPAND_SPARE;
+ }
+ freeBindingList = b->nextTagBinding;
+ }
+ else {
+ b = malloc(sizeof(BINDING));
+ if (!b)
+ return 0;
+ b->uri = malloc(sizeof(XML_Char) * (len + EXPAND_SPARE));
+ if (!b->uri) {
+ free(b);
+ return 0;
+ }
+ b->uriAlloc = len + EXPAND_SPARE;
+ }
+ b->uriLen = len;
+ memcpy(b->uri, uri, len * sizeof(XML_Char));
+ if (namespaceSeparator)
+ b->uri[len - 1] = namespaceSeparator;
+ b->prefix = prefix;
+ b->attId = attId;
+ b->prevPrefixBinding = prefix->binding;
+ if (*uri == XML_T('\0') && prefix == &dtd.defaultPrefix)
+ prefix->binding = 0;
+ else
+ prefix->binding = b;
+ b->nextTagBinding = *bindingsPtr;
+ *bindingsPtr = b;
+ if (startNamespaceDeclHandler)
+ startNamespaceDeclHandler(handlerArg, prefix->name,
+ prefix->binding ? uri : 0);
+ return 1;
+}
+
+/* The idea here is to avoid using stack for each CDATA section when
+the whole file is parsed with one call. */
+
+static
+enum XML_Error cdataSectionProcessor(XML_Parser parser,
+ const char *start,
+ const char *end,
+ const char **endPtr)
+{
+ enum XML_Error result = doCdataSection(parser, encoding, &start, end, endPtr);
+ if (start) {
+ processor = contentProcessor;
+ return contentProcessor(parser, start, end, endPtr);
+ }
+ return result;
+}
+
+/* startPtr gets set to non-null is the section is closed, and to null if
+the section is not yet closed. */
+
+static
+enum XML_Error doCdataSection(XML_Parser parser,
+ const ENCODING *enc,
+ const char **startPtr,
+ const char *end,
+ const char **nextPtr)
+{
+ const char *s = *startPtr;
+ const char **eventPP;
+ const char **eventEndPP;
+ if (enc == encoding) {
+ eventPP = &eventPtr;
+ *eventPP = s;
+ eventEndPP = &eventEndPtr;
+ }
+ else {
+ eventPP = &(openInternalEntities->internalEventPtr);
+ eventEndPP = &(openInternalEntities->internalEventEndPtr);
+ }
+ *eventPP = s;
+ *startPtr = 0;
+ for (;;) {
+ const char *next;
+ int tok = XmlCdataSectionTok(enc, s, end, &next);
+ *eventEndPP = next;
+ switch (tok) {
+ case XML_TOK_CDATA_SECT_CLOSE:
+ if (endCdataSectionHandler)
+ endCdataSectionHandler(handlerArg);
+#if 0
+ /* see comment under XML_TOK_CDATA_SECT_OPEN */
+ else if (characterDataHandler)
+ characterDataHandler(handlerArg, dataBuf, 0);
+#endif
+ else if (defaultHandler)
+ reportDefault(parser, enc, s, next);
+ *startPtr = next;
+ return XML_ERROR_NONE;
+ case XML_TOK_DATA_NEWLINE:
+ if (characterDataHandler) {
+ XML_Char c = 0xA;
+ characterDataHandler(handlerArg, &c, 1);
+ }
+ else if (defaultHandler)
+ reportDefault(parser, enc, s, next);
+ break;
+ case XML_TOK_DATA_CHARS:
+ if (characterDataHandler) {
+ if (MUST_CONVERT(enc, s)) {
+ for (;;) {
+ ICHAR *dataPtr = (ICHAR *)dataBuf;
+ XmlConvert(enc, &s, next, &dataPtr, (ICHAR *)dataBufEnd);
+ *eventEndPP = next;
+ characterDataHandler(handlerArg, dataBuf, dataPtr - (ICHAR *)dataBuf);
+ if (s == next)
+ break;
+ *eventPP = s;
+ }
+ }
+ else
+ characterDataHandler(handlerArg,
+ (XML_Char *)s,
+ (XML_Char *)next - (XML_Char *)s);
+ }
+ else if (defaultHandler)
+ reportDefault(parser, enc, s, next);
+ break;
+ case XML_TOK_INVALID:
+ *eventPP = next;
+ return XML_ERROR_INVALID_TOKEN;
+ case XML_TOK_PARTIAL_CHAR:
+ if (nextPtr) {
+ *nextPtr = s;
+ return XML_ERROR_NONE;
+ }
+ return XML_ERROR_PARTIAL_CHAR;
+ case XML_TOK_PARTIAL:
+ case XML_TOK_NONE:
+ if (nextPtr) {
+ *nextPtr = s;
+ return XML_ERROR_NONE;
+ }
+ return XML_ERROR_UNCLOSED_CDATA_SECTION;
+ default:
+ abort();
+ }
+ *eventPP = s = next;
+ }
+ /* not reached */
+}
+
+#ifdef XML_DTD
+
+/* The idea here is to avoid using stack for each IGNORE section when
+the whole file is parsed with one call. */
+
+static
+enum XML_Error ignoreSectionProcessor(XML_Parser parser,
+ const char *start,
+ const char *end,
+ const char **endPtr)
+{
+ enum XML_Error result = doIgnoreSection(parser, encoding, &start, end, endPtr);
+ if (start) {
+ processor = prologProcessor;
+ return prologProcessor(parser, start, end, endPtr);
+ }
+ return result;
+}
+
+/* startPtr gets set to non-null is the section is closed, and to null if
+the section is not yet closed. */
+
+static
+enum XML_Error doIgnoreSection(XML_Parser parser,
+ const ENCODING *enc,
+ const char **startPtr,
+ const char *end,
+ const char **nextPtr)
+{
+ const char *next;
+ int tok;
+ const char *s = *startPtr;
+ const char **eventPP;
+ const char **eventEndPP;
+ if (enc == encoding) {
+ eventPP = &eventPtr;
+ *eventPP = s;
+ eventEndPP = &eventEndPtr;
+ }
+ else {
+ eventPP = &(openInternalEntities->internalEventPtr);
+ eventEndPP = &(openInternalEntities->internalEventEndPtr);
+ }
+ *eventPP = s;
+ *startPtr = 0;
+ tok = XmlIgnoreSectionTok(enc, s, end, &next);
+ *eventEndPP = next;
+ switch (tok) {
+ case XML_TOK_IGNORE_SECT:
+ if (defaultHandler)
+ reportDefault(parser, enc, s, next);
+ *startPtr = next;
+ return XML_ERROR_NONE;
+ case XML_TOK_INVALID:
+ *eventPP = next;
+ return XML_ERROR_INVALID_TOKEN;
+ case XML_TOK_PARTIAL_CHAR:
+ if (nextPtr) {
+ *nextPtr = s;
+ return XML_ERROR_NONE;
+ }
+ return XML_ERROR_PARTIAL_CHAR;
+ case XML_TOK_PARTIAL:
+ case XML_TOK_NONE:
+ if (nextPtr) {
+ *nextPtr = s;
+ return XML_ERROR_NONE;
+ }
+ return XML_ERROR_SYNTAX; /* XML_ERROR_UNCLOSED_IGNORE_SECTION */
+ default:
+ abort();
+ }
+ /* not reached */
+}
+
+#endif /* XML_DTD */
+
+static enum XML_Error
+initializeEncoding(XML_Parser parser)
+{
+ const char *s;
+#ifdef XML_UNICODE
+ char encodingBuf[128];
+ if (!protocolEncodingName)
+ s = 0;
+ else {
+ int i;
+ for (i = 0; protocolEncodingName[i]; i++) {
+ if (i == sizeof(encodingBuf) - 1
+ || (protocolEncodingName[i] & ~0x7f) != 0) {
+ encodingBuf[0] = '\0';
+ break;
+ }
+ encodingBuf[i] = (char)protocolEncodingName[i];
+ }
+ encodingBuf[i] = '\0';
+ s = encodingBuf;
+ }
+#else
+ s = protocolEncodingName;
+#endif
+ if ((ns ? XmlInitEncodingNS : XmlInitEncoding)(&initEncoding, &encoding, s))
+ return XML_ERROR_NONE;
+ return handleUnknownEncoding(parser, protocolEncodingName);
+}
+
+static enum XML_Error
+processXmlDecl(XML_Parser parser, int isGeneralTextEntity,
+ const char *s, const char *next)
+{
+ const char *encodingName = 0;
+ const ENCODING *newEncoding = 0;
+ const char *version;
+ int standalone = -1;
+ if (!(ns
+ ? XmlParseXmlDeclNS
+ : XmlParseXmlDecl)(isGeneralTextEntity,
+ encoding,
+ s,
+ next,
+ &eventPtr,
+ &version,
+ &encodingName,
+ &newEncoding,
+ &standalone))
+ return XML_ERROR_SYNTAX;
+ if (!isGeneralTextEntity && standalone == 1) {
+ dtd.standalone = 1;
+#ifdef XML_DTD
+ if (paramEntityParsing == XML_PARAM_ENTITY_PARSING_UNLESS_STANDALONE)
+ paramEntityParsing = XML_PARAM_ENTITY_PARSING_NEVER;
+#endif /* XML_DTD */
+ }
+ if (defaultHandler)
+ reportDefault(parser, encoding, s, next);
+ if (!protocolEncodingName) {
+ if (newEncoding) {
+ if (newEncoding->minBytesPerChar != encoding->minBytesPerChar) {
+ eventPtr = encodingName;
+ return XML_ERROR_INCORRECT_ENCODING;
+ }
+ encoding = newEncoding;
+ }
+ else if (encodingName) {
+ enum XML_Error result;
+ const XML_Char *s = poolStoreString(&tempPool,
+ encoding,
+ encodingName,
+ encodingName
+ + XmlNameLength(encoding, encodingName));
+ if (!s)
+ return XML_ERROR_NO_MEMORY;
+ result = handleUnknownEncoding(parser, s);
+ poolDiscard(&tempPool);
+ if (result == XML_ERROR_UNKNOWN_ENCODING)
+ eventPtr = encodingName;
+ return result;
+ }
+ }
+ return XML_ERROR_NONE;
+}
+
+static enum XML_Error
+handleUnknownEncoding(XML_Parser parser, const XML_Char *encodingName)
+{
+ if (unknownEncodingHandler) {
+ XML_Encoding info;
+ int i;
+ for (i = 0; i < 256; i++)
+ info.map[i] = -1;
+ info.convert = 0;
+ info.data = 0;
+ info.release = 0;
+ if (unknownEncodingHandler(unknownEncodingHandlerData, encodingName, &info)) {
+ ENCODING *enc;
+ unknownEncodingMem = malloc(XmlSizeOfUnknownEncoding());
+ if (!unknownEncodingMem) {
+ if (info.release)
+ info.release(info.data);
+ return XML_ERROR_NO_MEMORY;
+ }
+ enc = (ns
+ ? XmlInitUnknownEncodingNS
+ : XmlInitUnknownEncoding)(unknownEncodingMem,
+ info.map,
+ info.convert,
+ info.data);
+ if (enc) {
+ unknownEncodingData = info.data;
+ unknownEncodingRelease = info.release;
+ encoding = enc;
+ return XML_ERROR_NONE;
+ }
+ }
+ if (info.release)
+ info.release(info.data);
+ }
+ return XML_ERROR_UNKNOWN_ENCODING;
+}
+
+static enum XML_Error
+prologInitProcessor(XML_Parser parser,
+ const char *s,
+ const char *end,
+ const char **nextPtr)
+{
+ enum XML_Error result = initializeEncoding(parser);
+ if (result != XML_ERROR_NONE)
+ return result;
+ processor = prologProcessor;
+ return prologProcessor(parser, s, end, nextPtr);
+}
+
+static enum XML_Error
+prologProcessor(XML_Parser parser,
+ const char *s,
+ const char *end,
+ const char **nextPtr)
+{
+ const char *next;
+ int tok = XmlPrologTok(encoding, s, end, &next);
+ return doProlog(parser, encoding, s, end, tok, next, nextPtr);
+}
+
+static enum XML_Error
+doProlog(XML_Parser parser,
+ const ENCODING *enc,
+ const char *s,
+ const char *end,
+ int tok,
+ const char *next,
+ const char **nextPtr)
+{
+#ifdef XML_DTD
+ static const XML_Char externalSubsetName[] = { '#' , '\0' };
+#endif /* XML_DTD */
+
+ const char **eventPP;
+ const char **eventEndPP;
+ if (enc == encoding) {
+ eventPP = &eventPtr;
+ eventEndPP = &eventEndPtr;
+ }
+ else {
+ eventPP = &(openInternalEntities->internalEventPtr);
+ eventEndPP = &(openInternalEntities->internalEventEndPtr);
+ }
+ for (;;) {
+ int role;
+ *eventPP = s;
+ *eventEndPP = next;
+ if (tok <= 0) {
+ if (nextPtr != 0 && tok != XML_TOK_INVALID) {
+ *nextPtr = s;
+ return XML_ERROR_NONE;
+ }
+ switch (tok) {
+ case XML_TOK_INVALID:
+ *eventPP = next;
+ return XML_ERROR_INVALID_TOKEN;
+ case XML_TOK_PARTIAL:
+ return XML_ERROR_UNCLOSED_TOKEN;
+ case XML_TOK_PARTIAL_CHAR:
+ return XML_ERROR_PARTIAL_CHAR;
+ case XML_TOK_NONE:
+#ifdef XML_DTD
+ if (enc != encoding)
+ return XML_ERROR_NONE;
+ if (parentParser) {
+ if (XmlTokenRole(&prologState, XML_TOK_NONE, end, end, enc)
+ == XML_ROLE_ERROR)
+ return XML_ERROR_SYNTAX;
+ hadExternalDoctype = 0;
+ return XML_ERROR_NONE;
+ }
+#endif /* XML_DTD */
+ return XML_ERROR_NO_ELEMENTS;
+ default:
+ tok = -tok;
+ next = end;
+ break;
+ }
+ }
+ role = XmlTokenRole(&prologState, tok, s, next, enc);
+ switch (role) {
+ case XML_ROLE_XML_DECL:
+ {
+ enum XML_Error result = processXmlDecl(parser, 0, s, next);
+ if (result != XML_ERROR_NONE)
+ return result;
+ enc = encoding;
+ }
+ break;
+ case XML_ROLE_DOCTYPE_NAME:
+ if (startDoctypeDeclHandler) {
+ const XML_Char *name = poolStoreString(&tempPool, enc, s, next);
+ if (!name)
+ return XML_ERROR_NO_MEMORY;
+ startDoctypeDeclHandler(handlerArg, name);
+ poolClear(&tempPool);
+ }
+ break;
+#ifdef XML_DTD
+ case XML_ROLE_TEXT_DECL:
+ {
+ enum XML_Error result = processXmlDecl(parser, 1, s, next);
+ if (result != XML_ERROR_NONE)
+ return result;
+ enc = encoding;
+ }
+ break;
+#endif /* XML_DTD */
+ case XML_ROLE_DOCTYPE_PUBLIC_ID:
+#ifdef XML_DTD
+ declEntity = (ENTITY *)lookup(&dtd.paramEntities,
+ externalSubsetName,
+ sizeof(ENTITY));
+ if (!declEntity)
+ return XML_ERROR_NO_MEMORY;
+#endif /* XML_DTD */
+ /* fall through */
+ case XML_ROLE_ENTITY_PUBLIC_ID:
+ if (!XmlIsPublicId(enc, s, next, eventPP))
+ return XML_ERROR_SYNTAX;
+ if (declEntity) {
+ XML_Char *tem = poolStoreString(&dtd.pool,
+ enc,
+ s + enc->minBytesPerChar,
+ next - enc->minBytesPerChar);
+ if (!tem)
+ return XML_ERROR_NO_MEMORY;
+ normalizePublicId(tem);
+ declEntity->publicId = tem;
+ poolFinish(&dtd.pool);
+ }
+ break;
+ case XML_ROLE_DOCTYPE_CLOSE:
+ if (dtd.complete && hadExternalDoctype) {
+ dtd.complete = 0;
+#ifdef XML_DTD
+ if (paramEntityParsing && externalEntityRefHandler) {
+ ENTITY *entity = (ENTITY *)lookup(&dtd.paramEntities,
+ externalSubsetName,
+ 0);
+ if (!externalEntityRefHandler(externalEntityRefHandlerArg,
+ 0,
+ entity->base,
+ entity->systemId,
+ entity->publicId))
+ return XML_ERROR_EXTERNAL_ENTITY_HANDLING;
+ }
+#endif /* XML_DTD */
+ if (!dtd.complete
+ && !dtd.standalone
+ && notStandaloneHandler
+ && !notStandaloneHandler(handlerArg))
+ return XML_ERROR_NOT_STANDALONE;
+ }
+ if (endDoctypeDeclHandler)
+ endDoctypeDeclHandler(handlerArg);
+ break;
+ case XML_ROLE_INSTANCE_START:
+ processor = contentProcessor;
+ return contentProcessor(parser, s, end, nextPtr);
+ case XML_ROLE_ATTLIST_ELEMENT_NAME:
+ {
+ const XML_Char *name = poolStoreString(&dtd.pool, enc, s, next);
+ if (!name)
+ return XML_ERROR_NO_MEMORY;
+ declElementType = (ELEMENT_TYPE *)lookup(&dtd.elementTypes, name, sizeof(ELEMENT_TYPE));
+ if (!declElementType)
+ return XML_ERROR_NO_MEMORY;
+ if (declElementType->name != name)
+ poolDiscard(&dtd.pool);
+ else {
+ poolFinish(&dtd.pool);
+ if (!setElementTypePrefix(parser, declElementType))
+ return XML_ERROR_NO_MEMORY;
+ }
+ break;
+ }
+ case XML_ROLE_ATTRIBUTE_NAME:
+ declAttributeId = getAttributeId(parser, enc, s, next);
+ if (!declAttributeId)
+ return XML_ERROR_NO_MEMORY;
+ declAttributeIsCdata = 0;
+ declAttributeIsId = 0;
+ break;
+ case XML_ROLE_ATTRIBUTE_TYPE_CDATA:
+ declAttributeIsCdata = 1;
+ break;
+ case XML_ROLE_ATTRIBUTE_TYPE_ID:
+ declAttributeIsId = 1;
+ break;
+ case XML_ROLE_IMPLIED_ATTRIBUTE_VALUE:
+ case XML_ROLE_REQUIRED_ATTRIBUTE_VALUE:
+ if (dtd.complete
+ && !defineAttribute(declElementType, declAttributeId, declAttributeIsCdata,
+ declAttributeIsId, 0))
+ return XML_ERROR_NO_MEMORY;
+ break;
+ case XML_ROLE_DEFAULT_ATTRIBUTE_VALUE:
+ case XML_ROLE_FIXED_ATTRIBUTE_VALUE:
+ {
+ const XML_Char *attVal;
+ enum XML_Error result
+ = storeAttributeValue(parser, enc, declAttributeIsCdata,
+ s + enc->minBytesPerChar,
+ next - enc->minBytesPerChar,
+ &dtd.pool);
+ if (result)
+ return result;
+ attVal = poolStart(&dtd.pool);
+ poolFinish(&dtd.pool);
+ if (dtd.complete
+ // ID attributes aren't allowed to have a default
+ && !defineAttribute(declElementType, declAttributeId, declAttributeIsCdata, 0, attVal))
+ return XML_ERROR_NO_MEMORY;
+ break;
+ }
+ case XML_ROLE_ENTITY_VALUE:
+ {
+ enum XML_Error result = storeEntityValue(parser, enc,
+ s + enc->minBytesPerChar,
+ next - enc->minBytesPerChar);
+ if (declEntity) {
+ declEntity->textPtr = poolStart(&dtd.pool);
+ declEntity->textLen = poolLength(&dtd.pool);
+ poolFinish(&dtd.pool);
+ if (internalParsedEntityDeclHandler
+ // Check it's not a parameter entity
+ && ((ENTITY *)lookup(&dtd.generalEntities, declEntity->name, 0)
+ == declEntity)) {
+ *eventEndPP = s;
+ internalParsedEntityDeclHandler(handlerArg,
+ declEntity->name,
+ declEntity->textPtr,
+ declEntity->textLen);
+ }
+ }
+ else
+ poolDiscard(&dtd.pool);
+ if (result != XML_ERROR_NONE)
+ return result;
+ }
+ break;
+ case XML_ROLE_DOCTYPE_SYSTEM_ID:
+ if (!dtd.standalone
+#ifdef XML_DTD
+ && !paramEntityParsing
+#endif /* XML_DTD */
+ && notStandaloneHandler
+ && !notStandaloneHandler(handlerArg))
+ return XML_ERROR_NOT_STANDALONE;
+ hadExternalDoctype = 1;
+#ifndef XML_DTD
+ break;
+#else /* XML_DTD */
+ if (!declEntity) {
+ declEntity = (ENTITY *)lookup(&dtd.paramEntities,
+ externalSubsetName,
+ sizeof(ENTITY));
+ if (!declEntity)
+ return XML_ERROR_NO_MEMORY;
+ }
+ /* fall through */
+#endif /* XML_DTD */
+ case XML_ROLE_ENTITY_SYSTEM_ID:
+ if (declEntity) {
+ declEntity->systemId = poolStoreString(&dtd.pool, enc,
+ s + enc->minBytesPerChar,
+ next - enc->minBytesPerChar);
+ if (!declEntity->systemId)
+ return XML_ERROR_NO_MEMORY;
+ declEntity->base = curBase;
+ poolFinish(&dtd.pool);
+ }
+ break;
+ case XML_ROLE_ENTITY_NOTATION_NAME:
+ if (declEntity) {
+ declEntity->notation = poolStoreString(&dtd.pool, enc, s, next);
+ if (!declEntity->notation)
+ return XML_ERROR_NO_MEMORY;
+ poolFinish(&dtd.pool);
+ if (unparsedEntityDeclHandler) {
+ *eventEndPP = s;
+ unparsedEntityDeclHandler(handlerArg,
+ declEntity->name,
+ declEntity->base,
+ declEntity->systemId,
+ declEntity->publicId,
+ declEntity->notation);
+ }
+
+ }
+ break;
+ case XML_ROLE_EXTERNAL_GENERAL_ENTITY_NO_NOTATION:
+ if (declEntity && externalParsedEntityDeclHandler) {
+ *eventEndPP = s;
+ externalParsedEntityDeclHandler(handlerArg,
+ declEntity->name,
+ declEntity->base,
+ declEntity->systemId,
+ declEntity->publicId);
+ }
+ break;
+ case XML_ROLE_GENERAL_ENTITY_NAME:
+ {
+ const XML_Char *name;
+ if (XmlPredefinedEntityName(enc, s, next)) {
+ declEntity = 0;
+ break;
+ }
+ name = poolStoreString(&dtd.pool, enc, s, next);
+ if (!name)
+ return XML_ERROR_NO_MEMORY;
+ if (dtd.complete) {
+ declEntity = (ENTITY *)lookup(&dtd.generalEntities, name, sizeof(ENTITY));
+ if (!declEntity)
+ return XML_ERROR_NO_MEMORY;
+ if (declEntity->name != name) {
+ poolDiscard(&dtd.pool);
+ declEntity = 0;
+ }
+ else
+ poolFinish(&dtd.pool);
+ }
+ else {
+ poolDiscard(&dtd.pool);
+ declEntity = 0;
+ }
+ }
+ break;
+ case XML_ROLE_PARAM_ENTITY_NAME:
+#ifdef XML_DTD
+ if (dtd.complete) {
+ const XML_Char *name = poolStoreString(&dtd.pool, enc, s, next);
+ if (!name)
+ return XML_ERROR_NO_MEMORY;
+ declEntity = (ENTITY *)lookup(&dtd.paramEntities, name, sizeof(ENTITY));
+ if (!declEntity)
+ return XML_ERROR_NO_MEMORY;
+ if (declEntity->name != name) {
+ poolDiscard(&dtd.pool);
+ declEntity = 0;
+ }
+ else
+ poolFinish(&dtd.pool);
+ }
+#else /* not XML_DTD */
+ declEntity = 0;
+#endif /* not XML_DTD */
+ break;
+ case XML_ROLE_NOTATION_NAME:
+ declNotationPublicId = 0;
+ declNotationName = 0;
+ if (notationDeclHandler) {
+ declNotationName = poolStoreString(&tempPool, enc, s, next);
+ if (!declNotationName)
+ return XML_ERROR_NO_MEMORY;
+ poolFinish(&tempPool);
+ }
+ break;
+ case XML_ROLE_NOTATION_PUBLIC_ID:
+ if (!XmlIsPublicId(enc, s, next, eventPP))
+ return XML_ERROR_SYNTAX;
+ if (declNotationName) {
+ XML_Char *tem = poolStoreString(&tempPool,
+ enc,
+ s + enc->minBytesPerChar,
+ next - enc->minBytesPerChar);
+ if (!tem)
+ return XML_ERROR_NO_MEMORY;
+ normalizePublicId(tem);
+ declNotationPublicId = tem;
+ poolFinish(&tempPool);
+ }
+ break;
+ case XML_ROLE_NOTATION_SYSTEM_ID:
+ if (declNotationName && notationDeclHandler) {
+ const XML_Char *systemId
+ = poolStoreString(&tempPool, enc,
+ s + enc->minBytesPerChar,
+ next - enc->minBytesPerChar);
+ if (!systemId)
+ return XML_ERROR_NO_MEMORY;
+ *eventEndPP = s;
+ notationDeclHandler(handlerArg,
+ declNotationName,
+ curBase,
+ systemId,
+ declNotationPublicId);
+ }
+ poolClear(&tempPool);
+ break;
+ case XML_ROLE_NOTATION_NO_SYSTEM_ID:
+ if (declNotationPublicId && notationDeclHandler) {
+ *eventEndPP = s;
+ notationDeclHandler(handlerArg,
+ declNotationName,
+ curBase,
+ 0,
+ declNotationPublicId);
+ }
+ poolClear(&tempPool);
+ break;
+ case XML_ROLE_ERROR:
+ switch (tok) {
+ case XML_TOK_PARAM_ENTITY_REF:
+ return XML_ERROR_PARAM_ENTITY_REF;
+ case XML_TOK_XML_DECL:
+ return XML_ERROR_MISPLACED_XML_PI;
+ default:
+ return XML_ERROR_SYNTAX;
+ }
+#ifdef XML_DTD
+ case XML_ROLE_IGNORE_SECT:
+ {
+ enum XML_Error result;
+ if (defaultHandler)
+ reportDefault(parser, enc, s, next);
+ result = doIgnoreSection(parser, enc, &next, end, nextPtr);
+ if (!next) {
+ processor = ignoreSectionProcessor;
+ return result;
+ }
+ }
+ break;
+#endif /* XML_DTD */
+ case XML_ROLE_GROUP_OPEN:
+ if (prologState.level >= groupSize) {
+ if (groupSize)
+ groupConnector = realloc(groupConnector, groupSize *= 2);
+ else
+ groupConnector = malloc(groupSize = 32);
+ if (!groupConnector)
+ return XML_ERROR_NO_MEMORY;
+ }
+ groupConnector[prologState.level] = 0;
+ break;
+ case XML_ROLE_GROUP_SEQUENCE:
+ if (groupConnector[prologState.level] == '|')
+ return XML_ERROR_SYNTAX;
+ groupConnector[prologState.level] = ',';
+ break;
+ case XML_ROLE_GROUP_CHOICE:
+ if (groupConnector[prologState.level] == ',')
+ return XML_ERROR_SYNTAX;
+ groupConnector[prologState.level] = '|';
+ break;
+ case XML_ROLE_PARAM_ENTITY_REF:
+#ifdef XML_DTD
+ case XML_ROLE_INNER_PARAM_ENTITY_REF:
+ if (paramEntityParsing
+ && (dtd.complete || role == XML_ROLE_INNER_PARAM_ENTITY_REF)) {
+ const XML_Char *name;
+ ENTITY *entity;
+ name = poolStoreString(&dtd.pool, enc,
+ s + enc->minBytesPerChar,
+ next - enc->minBytesPerChar);
+ if (!name)
+ return XML_ERROR_NO_MEMORY;
+ entity = (ENTITY *)lookup(&dtd.paramEntities, name, 0);
+ poolDiscard(&dtd.pool);
+ if (!entity) {
+ /* FIXME what to do if !dtd.complete? */
+ return XML_ERROR_UNDEFINED_ENTITY;
+ }
+ if (entity->open)
+ return XML_ERROR_RECURSIVE_ENTITY_REF;
+ if (entity->textPtr) {
+ enum XML_Error result;
+ result = processInternalParamEntity(parser, entity);
+ if (result != XML_ERROR_NONE)
+ return result;
+ break;
+ }
+ if (role == XML_ROLE_INNER_PARAM_ENTITY_REF)
+ return XML_ERROR_PARAM_ENTITY_REF;
+ if (externalEntityRefHandler) {
+ dtd.complete = 0;
+ entity->open = 1;
+ if (!externalEntityRefHandler(externalEntityRefHandlerArg,
+ 0,
+ entity->base,
+ entity->systemId,
+ entity->publicId)) {
+ entity->open = 0;
+ return XML_ERROR_EXTERNAL_ENTITY_HANDLING;
+ }
+ entity->open = 0;
+ if (dtd.complete)
+ break;
+ }
+ }
+#endif /* XML_DTD */
+ if (!dtd.standalone
+ && notStandaloneHandler
+ && !notStandaloneHandler(handlerArg))
+ return XML_ERROR_NOT_STANDALONE;
+ dtd.complete = 0;
+ if (defaultHandler)
+ reportDefault(parser, enc, s, next);
+ break;
+ case XML_ROLE_NONE:
+ switch (tok) {
+ case XML_TOK_PI:
+ if (!reportProcessingInstruction(parser, enc, s, next))
+ return XML_ERROR_NO_MEMORY;
+ break;
+ case XML_TOK_COMMENT:
+ if (!reportComment(parser, enc, s, next))
+ return XML_ERROR_NO_MEMORY;
+ break;
+ }
+ break;
+ }
+ if (defaultHandler) {
+ switch (tok) {
+ case XML_TOK_PI:
+ case XML_TOK_COMMENT:
+ case XML_TOK_BOM:
+ case XML_TOK_XML_DECL:
+#ifdef XML_DTD
+ case XML_TOK_IGNORE_SECT:
+#endif /* XML_DTD */
+ case XML_TOK_PARAM_ENTITY_REF:
+ break;
+ default:
+#ifdef XML_DTD
+ if (role != XML_ROLE_IGNORE_SECT)
+#endif /* XML_DTD */
+ reportDefault(parser, enc, s, next);
+ }
+ }
+ s = next;
+ tok = XmlPrologTok(enc, s, end, &next);
+ }
+ /* not reached */
+}
+
+static
+enum XML_Error epilogProcessor(XML_Parser parser,
+ const char *s,
+ const char *end,
+ const char **nextPtr)
+{
+ processor = epilogProcessor;
+ eventPtr = s;
+ for (;;) {
+ const char *next;
+ int tok = XmlPrologTok(encoding, s, end, &next);
+ eventEndPtr = next;
+ switch (tok) {
+ case -XML_TOK_PROLOG_S:
+ if (defaultHandler) {
+ eventEndPtr = end;
+ reportDefault(parser, encoding, s, end);
+ }
+ /* fall through */
+ case XML_TOK_NONE:
+ if (nextPtr)
+ *nextPtr = end;
+ return XML_ERROR_NONE;
+ case XML_TOK_PROLOG_S:
+ if (defaultHandler)
+ reportDefault(parser, encoding, s, next);
+ break;
+ case XML_TOK_PI:
+ if (!reportProcessingInstruction(parser, encoding, s, next))
+ return XML_ERROR_NO_MEMORY;
+ break;
+ case XML_TOK_COMMENT:
+ if (!reportComment(parser, encoding, s, next))
+ return XML_ERROR_NO_MEMORY;
+ break;
+ case XML_TOK_INVALID:
+ eventPtr = next;
+ return XML_ERROR_INVALID_TOKEN;
+ case XML_TOK_PARTIAL:
+ if (nextPtr) {
+ *nextPtr = s;
+ return XML_ERROR_NONE;
+ }
+ return XML_ERROR_UNCLOSED_TOKEN;
+ case XML_TOK_PARTIAL_CHAR:
+ if (nextPtr) {
+ *nextPtr = s;
+ return XML_ERROR_NONE;
+ }
+ return XML_ERROR_PARTIAL_CHAR;
+ default:
+ return XML_ERROR_JUNK_AFTER_DOC_ELEMENT;
+ }
+ eventPtr = s = next;
+ }
+}
+
+#ifdef XML_DTD
+
+static enum XML_Error
+processInternalParamEntity(XML_Parser parser, ENTITY *entity)
+{
+ const char *s, *end, *next;
+ int tok;
+ enum XML_Error result;
+ OPEN_INTERNAL_ENTITY openEntity;
+ entity->open = 1;
+ openEntity.next = openInternalEntities;
+ openInternalEntities = &openEntity;
+ openEntity.entity = entity;
+ openEntity.internalEventPtr = 0;
+ openEntity.internalEventEndPtr = 0;
+ s = (char *)entity->textPtr;
+ end = (char *)(entity->textPtr + entity->textLen);
+ tok = XmlPrologTok(internalEncoding, s, end, &next);
+ result = doProlog(parser, internalEncoding, s, end, tok, next, 0);
+ entity->open = 0;
+ openInternalEntities = openEntity.next;
+ return result;
+}
+
+#endif /* XML_DTD */
+
+static
+enum XML_Error errorProcessor(XML_Parser parser,
+ const char *s,
+ const char *end,
+ const char **nextPtr)
+{
+ return errorCode;
+}
+
+static enum XML_Error
+storeAttributeValue(XML_Parser parser, const ENCODING *enc, int isCdata,
+ const char *ptr, const char *end,
+ STRING_POOL *pool)
+{
+ enum XML_Error result = appendAttributeValue(parser, enc, isCdata, ptr, end, pool);
+ if (result)
+ return result;
+ if (!isCdata && poolLength(pool) && poolLastChar(pool) == 0x20)
+ poolChop(pool);
+ if (!poolAppendChar(pool, XML_T('\0')))
+ return XML_ERROR_NO_MEMORY;
+ return XML_ERROR_NONE;
+}
+
+static enum XML_Error
+appendAttributeValue(XML_Parser parser, const ENCODING *enc, int isCdata,
+ const char *ptr, const char *end,
+ STRING_POOL *pool)
+{
+ for (;;) {
+ const char *next;
+ int tok = XmlAttributeValueTok(enc, ptr, end, &next);
+ switch (tok) {
+ case XML_TOK_NONE:
+ return XML_ERROR_NONE;
+ case XML_TOK_INVALID:
+ if (enc == encoding)
+ eventPtr = next;
+ return XML_ERROR_INVALID_TOKEN;
+ case XML_TOK_PARTIAL:
+ if (enc == encoding)
+ eventPtr = ptr;
+ return XML_ERROR_INVALID_TOKEN;
+ case XML_TOK_CHAR_REF:
+ {
+ XML_Char buf[XML_ENCODE_MAX];
+ int i;
+ int n = XmlCharRefNumber(enc, ptr);
+ if (n < 0) {
+ if (enc == encoding)
+ eventPtr = ptr;
+ return XML_ERROR_BAD_CHAR_REF;
+ }
+ if (!isCdata
+ && n == 0x20 /* space */
+ && (poolLength(pool) == 0 || poolLastChar(pool) == 0x20))
+ break;
+ n = XmlEncode(n, (ICHAR *)buf);
+ if (!n) {
+ if (enc == encoding)
+ eventPtr = ptr;
+ return XML_ERROR_BAD_CHAR_REF;
+ }
+ for (i = 0; i < n; i++) {
+ if (!poolAppendChar(pool, buf[i]))
+ return XML_ERROR_NO_MEMORY;
+ }
+ }
+ break;
+ case XML_TOK_DATA_CHARS:
+ if (!poolAppend(pool, enc, ptr, next))
+ return XML_ERROR_NO_MEMORY;
+ break;
+ // break; // BCPPB found this one
+ case XML_TOK_TRAILING_CR:
+ next = ptr + enc->minBytesPerChar;
+ /* fall through */
+ case XML_TOK_ATTRIBUTE_VALUE_S:
+ case XML_TOK_DATA_NEWLINE:
+ if (!isCdata && (poolLength(pool) == 0 || poolLastChar(pool) == 0x20))
+ break;
+ if (!poolAppendChar(pool, 0x20))
+ return XML_ERROR_NO_MEMORY;
+ break;
+ case XML_TOK_ENTITY_REF:
+ {
+ const XML_Char *name;
+ ENTITY *entity;
+ XML_Char ch = XmlPredefinedEntityName(enc,
+ ptr + enc->minBytesPerChar,
+ next - enc->minBytesPerChar);
+ if (ch) {
+ if (!poolAppendChar(pool, ch))
+ return XML_ERROR_NO_MEMORY;
+ break;
+ }
+ name = poolStoreString(&temp2Pool, enc,
+ ptr + enc->minBytesPerChar,
+ next - enc->minBytesPerChar);
+ if (!name)
+ return XML_ERROR_NO_MEMORY;
+ entity = (ENTITY *)lookup(&dtd.generalEntities, name, 0);
+ poolDiscard(&temp2Pool);
+ if (!entity) {
+ if (dtd.complete) {
+ if (enc == encoding)
+ eventPtr = ptr;
+ return XML_ERROR_UNDEFINED_ENTITY;
+ }
+ }
+ else if (entity->open) {
+ if (enc == encoding)
+ eventPtr = ptr;
+ return XML_ERROR_RECURSIVE_ENTITY_REF;
+ }
+ else if (entity->notation) {
+ if (enc == encoding)
+ eventPtr = ptr;
+ return XML_ERROR_BINARY_ENTITY_REF;
+ }
+ else if (!entity->textPtr) {
+ if (enc == encoding)
+ eventPtr = ptr;
+ return XML_ERROR_ATTRIBUTE_EXTERNAL_ENTITY_REF;
+ }
+ else {
+ enum XML_Error result;
+ const XML_Char *textEnd = entity->textPtr + entity->textLen;
+ entity->open = 1;
+ result = appendAttributeValue(parser, internalEncoding, isCdata, (char *)entity->textPtr, (char *)textEnd, pool);
+ entity->open = 0;
+ if (result)
+ return result;
+ }
+ }
+ break;
+ default:
+ abort();
+ }
+ ptr = next;
+ }
+ /* not reached */
+}
+
+static
+enum XML_Error storeEntityValue(XML_Parser parser,
+ const ENCODING *enc,
+ const char *entityTextPtr,
+ const char *entityTextEnd)
+{
+ STRING_POOL *pool = &(dtd.pool);
+ for (;;) {
+ const char *next;
+ int tok = XmlEntityValueTok(enc, entityTextPtr, entityTextEnd, &next);
+ switch (tok) {
+ case XML_TOK_PARAM_ENTITY_REF:
+#ifdef XML_DTD
+ if (parentParser || enc != encoding) {
+ enum XML_Error result;
+ const XML_Char *name;
+ ENTITY *entity;
+ name = poolStoreString(&tempPool, enc,
+ entityTextPtr + enc->minBytesPerChar,
+ next - enc->minBytesPerChar);
+ if (!name)
+ return XML_ERROR_NO_MEMORY;
+ entity = (ENTITY *)lookup(&dtd.paramEntities, name, 0);
+ poolDiscard(&tempPool);
+ if (!entity) {
+ if (enc == encoding)
+ eventPtr = entityTextPtr;
+ return XML_ERROR_UNDEFINED_ENTITY;
+ }
+ if (entity->open) {
+ if (enc == encoding)
+ eventPtr = entityTextPtr;
+ return XML_ERROR_RECURSIVE_ENTITY_REF;
+ }
+ if (entity->systemId) {
+ if (enc == encoding)
+ eventPtr = entityTextPtr;
+ return XML_ERROR_PARAM_ENTITY_REF;
+ }
+ entity->open = 1;
+ result = storeEntityValue(parser,
+ internalEncoding,
+ (char *)entity->textPtr,
+ (char *)(entity->textPtr + entity->textLen));
+ entity->open = 0;
+ if (result)
+ return result;
+ break;
+ }
+#endif /* XML_DTD */
+ eventPtr = entityTextPtr;
+ return XML_ERROR_SYNTAX;
+ case XML_TOK_NONE:
+ return XML_ERROR_NONE;
+ case XML_TOK_ENTITY_REF:
+ case XML_TOK_DATA_CHARS:
+ if (!poolAppend(pool, enc, entityTextPtr, next))
+ return XML_ERROR_NO_MEMORY;
+ break;
+ case XML_TOK_TRAILING_CR:
+ next = entityTextPtr + enc->minBytesPerChar;
+ /* fall through */
+ case XML_TOK_DATA_NEWLINE:
+ if (pool->end == pool->ptr && !poolGrow(pool))
+ return XML_ERROR_NO_MEMORY;
+ *(pool->ptr)++ = 0xA;
+ break;
+ case XML_TOK_CHAR_REF:
+ {
+ XML_Char buf[XML_ENCODE_MAX];
+ int i;
+ int n = XmlCharRefNumber(enc, entityTextPtr);
+ if (n < 0) {
+ if (enc == encoding)
+ eventPtr = entityTextPtr;
+ return XML_ERROR_BAD_CHAR_REF;
+ }
+ n = XmlEncode(n, (ICHAR *)buf);
+ if (!n) {
+ if (enc == encoding)
+ eventPtr = entityTextPtr;
+ return XML_ERROR_BAD_CHAR_REF;
+ }
+ for (i = 0; i < n; i++) {
+ if (pool->end == pool->ptr && !poolGrow(pool))
+ return XML_ERROR_NO_MEMORY;
+ *(pool->ptr)++ = buf[i];
+ }
+ }
+ break;
+ case XML_TOK_PARTIAL:
+ if (enc == encoding)
+ eventPtr = entityTextPtr;
+ return XML_ERROR_INVALID_TOKEN;
+ case XML_TOK_INVALID:
+ if (enc == encoding)
+ eventPtr = next;
+ return XML_ERROR_INVALID_TOKEN;
+ default:
+ abort();
+ }
+ entityTextPtr = next;
+ }
+ /* not reached */
+}
+
+static void
+normalizeLines(XML_Char *s)
+{
+ XML_Char *p;
+ for (;; s++) {
+ if (*s == XML_T('\0'))
+ return;
+ if (*s == 0xD)
+ break;
+ }
+ p = s;
+ do {
+ if (*s == 0xD) {
+ *p++ = 0xA;
+ if (*++s == 0xA)
+ s++;
+ }
+ else
+ *p++ = *s++;
+ } while (*s);
+ *p = XML_T('\0');
+}
+
+static int
+reportProcessingInstruction(XML_Parser parser, const ENCODING *enc, const char *start, const char *end)
+{
+ const XML_Char *target;
+ XML_Char *data;
+ const char *tem;
+ if (!processingInstructionHandler) {
+ if (defaultHandler)
+ reportDefault(parser, enc, start, end);
+ return 1;
+ }
+ start += enc->minBytesPerChar * 2;
+ tem = start + XmlNameLength(enc, start);
+ target = poolStoreString(&tempPool, enc, start, tem);
+ if (!target)
+ return 0;
+ poolFinish(&tempPool);
+ data = poolStoreString(&tempPool, enc,
+ XmlSkipS(enc, tem),
+ end - enc->minBytesPerChar*2);
+ if (!data)
+ return 0;
+ normalizeLines(data);
+ processingInstructionHandler(handlerArg, target, data);
+ poolClear(&tempPool);
+ return 1;
+}
+
+static int
+reportComment(XML_Parser parser, const ENCODING *enc, const char *start, const char *end)
+{
+ XML_Char *data;
+ if (!commentHandler) {
+ if (defaultHandler)
+ reportDefault(parser, enc, start, end);
+ return 1;
+ }
+ data = poolStoreString(&tempPool,
+ enc,
+ start + enc->minBytesPerChar * 4,
+ end - enc->minBytesPerChar * 3);
+ if (!data)
+ return 0;
+ normalizeLines(data);
+ commentHandler(handlerArg, data);
+ poolClear(&tempPool);
+ return 1;
+}
+
+static void
+reportDefault(XML_Parser parser, const ENCODING *enc, const char *s, const char *end)
+{
+ if (MUST_CONVERT(enc, s)) {
+ const char **eventPP;
+ const char **eventEndPP;
+ if (enc == encoding) {
+ eventPP = &eventPtr;
+ eventEndPP = &eventEndPtr;
+ }
+ else {
+ eventPP = &(openInternalEntities->internalEventPtr);
+ eventEndPP = &(openInternalEntities->internalEventEndPtr);
+ }
+ do {
+ ICHAR *dataPtr = (ICHAR *)dataBuf;
+ XmlConvert(enc, &s, end, &dataPtr, (ICHAR *)dataBufEnd);
+ *eventEndPP = s;
+ defaultHandler(handlerArg, dataBuf, dataPtr - (ICHAR *)dataBuf);
+ *eventPP = s;
+ } while (s != end);
+ }
+ else
+ defaultHandler(handlerArg, (XML_Char *)s, (XML_Char *)end - (XML_Char *)s);
+}
+
+
+static int
+defineAttribute(ELEMENT_TYPE *type, ATTRIBUTE_ID *attId, int isCdata, int isId, const XML_Char *value)
+{
+ DEFAULT_ATTRIBUTE *att;
+ if (value || isId) {
+ /* The handling of default attributes gets messed up if we have
+ a default which duplicates a non-default. */
+ int i;
+ for (i = 0; i < type->nDefaultAtts; i++)
+ if (attId == type->defaultAtts[i].id)
+ return 1;
+ if (isId && !type->idAtt && !attId->xmlns)
+ type->idAtt = attId;
+ }
+ if (type->nDefaultAtts == type->allocDefaultAtts) {
+ if (type->allocDefaultAtts == 0) {
+ type->allocDefaultAtts = 8;
+ type->defaultAtts = malloc(type->allocDefaultAtts*sizeof(DEFAULT_ATTRIBUTE));
+ }
+ else {
+ type->allocDefaultAtts *= 2;
+ type->defaultAtts = realloc(type->defaultAtts,
+ type->allocDefaultAtts*sizeof(DEFAULT_ATTRIBUTE));
+ }
+ if (!type->defaultAtts)
+ return 0;
+ }
+ att = type->defaultAtts + type->nDefaultAtts;
+ att->id = attId;
+ att->value = value;
+ att->isCdata = isCdata;
+ if (!isCdata)
+ attId->maybeTokenized = 1;
+ type->nDefaultAtts += 1;
+ return 1;
+}
+
+static int setElementTypePrefix(XML_Parser parser, ELEMENT_TYPE *elementType)
+{
+ const XML_Char *name;
+ for (name = elementType->name; *name; name++) {
+ if (*name == XML_T(':')) {
+ PREFIX *prefix;
+ const XML_Char *s;
+ for (s = elementType->name; s != name; s++) {
+ if (!poolAppendChar(&dtd.pool, *s))
+ return 0;
+ }
+ if (!poolAppendChar(&dtd.pool, XML_T('\0')))
+ return 0;
+ prefix = (PREFIX *)lookup(&dtd.prefixes, poolStart(&dtd.pool), sizeof(PREFIX));
+ if (!prefix)
+ return 0;
+ if (prefix->name == poolStart(&dtd.pool))
+ poolFinish(&dtd.pool);
+ else
+ poolDiscard(&dtd.pool);
+ elementType->prefix = prefix;
+
+ }
+ }
+ return 1;
+}
+
+static ATTRIBUTE_ID *
+getAttributeId(XML_Parser parser, const ENCODING *enc, const char *start, const char *end)
+{
+ ATTRIBUTE_ID *id;
+ const XML_Char *name;
+ if (!poolAppendChar(&dtd.pool, XML_T('\0')))
+ return 0;
+ name = poolStoreString(&dtd.pool, enc, start, end);
+ if (!name)
+ return 0;
+ ++name;
+ id = (ATTRIBUTE_ID *)lookup(&dtd.attributeIds, name, sizeof(ATTRIBUTE_ID));
+ if (!id)
+ return 0;
+ if (id->name != name)
+ poolDiscard(&dtd.pool);
+ else {
+ poolFinish(&dtd.pool);
+ if (!ns)
+ ;
+ else if (name[0] == 'x'
+ && name[1] == 'm'
+ && name[2] == 'l'
+ && name[3] == 'n'
+ && name[4] == 's'
+ && (name[5] == XML_T('\0') || name[5] == XML_T(':'))) {
+ if (name[5] == '\0')
+ id->prefix = &dtd.defaultPrefix;
+ else
+ id->prefix = (PREFIX *)lookup(&dtd.prefixes, name + 6, sizeof(PREFIX));
+ id->xmlns = 1;
+ }
+ else {
+ int i;
+ for (i = 0; name[i]; i++) {
+ if (name[i] == XML_T(':')) {
+ int j;
+ for (j = 0; j < i; j++) {
+ if (!poolAppendChar(&dtd.pool, name[j]))
+ return 0;
+ }
+ if (!poolAppendChar(&dtd.pool, XML_T('\0')))
+ return 0;
+ id->prefix = (PREFIX *)lookup(&dtd.prefixes, poolStart(&dtd.pool), sizeof(PREFIX));
+ if (id->prefix->name == poolStart(&dtd.pool))
+ poolFinish(&dtd.pool);
+ else
+ poolDiscard(&dtd.pool);
+ break;
+ }
+ }
+ }
+ }
+ return id;
+}
+
+#define CONTEXT_SEP XML_T('\f')
+
+static
+const XML_Char *getContext(XML_Parser parser)
+{
+ HASH_TABLE_ITER iter;
+ int needSep = 0;
+
+ if (dtd.defaultPrefix.binding) {
+ int i;
+ int len;
+ if (!poolAppendChar(&tempPool, XML_T('=')))
+ return 0;
+ len = dtd.defaultPrefix.binding->uriLen;
+ if (namespaceSeparator != XML_T('\0'))
+ len--;
+ for (i = 0; i < len; i++)
+ if (!poolAppendChar(&tempPool, dtd.defaultPrefix.binding->uri[i]))
+ return 0;
+ needSep = 1;
+ }
+
+ hashTableIterInit(&iter, &(dtd.prefixes));
+ for (;;) {
+ int i;
+ int len;
+ const XML_Char *s;
+ PREFIX *prefix = (PREFIX *)hashTableIterNext(&iter);
+ if (!prefix)
+ break;
+ if (!prefix->binding)
+ continue;
+ if (needSep && !poolAppendChar(&tempPool, CONTEXT_SEP))
+ return 0;
+ for (s = prefix->name; *s; s++)
+ if (!poolAppendChar(&tempPool, *s))
+ return 0;
+ if (!poolAppendChar(&tempPool, XML_T('=')))
+ return 0;
+ len = prefix->binding->uriLen;
+ if (namespaceSeparator != XML_T('\0'))
+ len--;
+ for (i = 0; i < len; i++)
+ if (!poolAppendChar(&tempPool, prefix->binding->uri[i]))
+ return 0;
+ needSep = 1;
+ }
+
+
+ hashTableIterInit(&iter, &(dtd.generalEntities));
+ for (;;) {
+ const XML_Char *s;
+ ENTITY *e = (ENTITY *)hashTableIterNext(&iter);
+ if (!e)
+ break;
+ if (!e->open)
+ continue;
+ if (needSep && !poolAppendChar(&tempPool, CONTEXT_SEP))
+ return 0;
+ for (s = e->name; *s; s++)
+ if (!poolAppendChar(&tempPool, *s))
+ return 0;
+ needSep = 1;
+ }
+
+ if (!poolAppendChar(&tempPool, XML_T('\0')))
+ return 0;
+ return tempPool.start;
+}
+
+static
+int setContext(XML_Parser parser, const XML_Char *context)
+{
+ const XML_Char *s = context;
+
+ while (*context != XML_T('\0')) {
+ if (*s == CONTEXT_SEP || *s == XML_T('\0')) {
+ ENTITY *e;
+ if (!poolAppendChar(&tempPool, XML_T('\0')))
+ return 0;
+ e = (ENTITY *)lookup(&dtd.generalEntities, poolStart(&tempPool), 0);
+ if (e)
+ e->open = 1;
+ if (*s != XML_T('\0'))
+ s++;
+ context = s;
+ poolDiscard(&tempPool);
+ }
+ else if (*s == '=') {
+ PREFIX *prefix;
+ if (poolLength(&tempPool) == 0)
+ prefix = &dtd.defaultPrefix;
+ else {
+ if (!poolAppendChar(&tempPool, XML_T('\0')))
+ return 0;
+ prefix = (PREFIX *)lookup(&dtd.prefixes, poolStart(&tempPool), sizeof(PREFIX));
+ if (!prefix)
+ return 0;
+ if (prefix->name == poolStart(&tempPool)) {
+ prefix->name = poolCopyString(&dtd.pool, prefix->name);
+ if (!prefix->name)
+ return 0;
+ }
+ poolDiscard(&tempPool);
+ }
+ for (context = s + 1; *context != CONTEXT_SEP && *context != XML_T('\0'); context++)
+ if (!poolAppendChar(&tempPool, *context))
+ return 0;
+ if (!poolAppendChar(&tempPool, XML_T('\0')))
+ return 0;
+ if (!addBinding(parser, prefix, 0, poolStart(&tempPool), &inheritedBindings))
+ return 0;
+ poolDiscard(&tempPool);
+ if (*context != XML_T('\0'))
+ ++context;
+ s = context;
+ }
+ else {
+ if (!poolAppendChar(&tempPool, *s))
+ return 0;
+ s++;
+ }
+ }
+ return 1;
+}
+
+
+static
+void normalizePublicId(XML_Char *publicId)
+{
+ XML_Char *p = publicId;
+ XML_Char *s;
+ for (s = publicId; *s; s++) {
+ switch (*s) {
+ case 0x20:
+ case 0xD:
+ case 0xA:
+ if (p != publicId && p[-1] != 0x20)
+ *p++ = 0x20;
+ break;
+ default:
+ *p++ = *s;
+ }
+ }
+ if (p != publicId && p[-1] == 0x20)
+ --p;
+ *p = XML_T('\0');
+}
+
+static int dtdInit(DTD *p)
+{
+ poolInit(&(p->pool));
+ hashTableInit(&(p->generalEntities));
+ hashTableInit(&(p->elementTypes));
+ hashTableInit(&(p->attributeIds));
+ hashTableInit(&(p->prefixes));
+ p->complete = 1;
+ p->standalone = 0;
+#ifdef XML_DTD
+ hashTableInit(&(p->paramEntities));
+#endif /* XML_DTD */
+ p->defaultPrefix.name = 0;
+ p->defaultPrefix.binding = 0;
+ return 1;
+}
+
+#ifdef XML_DTD
+
+static void dtdSwap(DTD *p1, DTD *p2)
+{
+ DTD tem;
+ memcpy(&tem, p1, sizeof(DTD));
+ memcpy(p1, p2, sizeof(DTD));
+ memcpy(p2, &tem, sizeof(DTD));
+}
+
+#endif /* XML_DTD */
+
+static void dtdDestroy(DTD *p)
+{
+ HASH_TABLE_ITER iter;
+ hashTableIterInit(&iter, &(p->elementTypes));
+ for (;;) {
+ ELEMENT_TYPE *e = (ELEMENT_TYPE *)hashTableIterNext(&iter);
+ if (!e)
+ break;
+ if (e->allocDefaultAtts != 0)
+ free(e->defaultAtts);
+ }
+ hashTableDestroy(&(p->generalEntities));
+#ifdef XML_DTD
+ hashTableDestroy(&(p->paramEntities));
+#endif /* XML_DTD */
+ hashTableDestroy(&(p->elementTypes));
+ hashTableDestroy(&(p->attributeIds));
+ hashTableDestroy(&(p->prefixes));
+ poolDestroy(&(p->pool));
+}
+
+/* Do a deep copy of the DTD. Return 0 for out of memory; non-zero otherwise.
+The new DTD has already been initialized. */
+
+static int dtdCopy(DTD *newDtd, const DTD *oldDtd)
+{
+ HASH_TABLE_ITER iter;
+
+ /* Copy the prefix table. */
+
+ hashTableIterInit(&iter, &(oldDtd->prefixes));
+ for (;;) {
+ const XML_Char *name;
+ const PREFIX *oldP = (PREFIX *)hashTableIterNext(&iter);
+ if (!oldP)
+ break;
+ name = poolCopyString(&(newDtd->pool), oldP->name);
+ if (!name)
+ return 0;
+ if (!lookup(&(newDtd->prefixes), name, sizeof(PREFIX)))
+ return 0;
+ }
+
+ hashTableIterInit(&iter, &(oldDtd->attributeIds));
+
+ /* Copy the attribute id table. */
+
+ for (;;) {
+ ATTRIBUTE_ID *newA;
+ const XML_Char *name;
+ const ATTRIBUTE_ID *oldA = (ATTRIBUTE_ID *)hashTableIterNext(&iter);
+
+ if (!oldA)
+ break;
+ /* Remember to allocate the scratch byte before the name. */
+ if (!poolAppendChar(&(newDtd->pool), XML_T('\0')))
+ return 0;
+ name = poolCopyString(&(newDtd->pool), oldA->name);
+ if (!name)
+ return 0;
+ ++name;
+ newA = (ATTRIBUTE_ID *)lookup(&(newDtd->attributeIds), name, sizeof(ATTRIBUTE_ID));
+ if (!newA)
+ return 0;
+ newA->maybeTokenized = oldA->maybeTokenized;
+ if (oldA->prefix) {
+ newA->xmlns = oldA->xmlns;
+ if (oldA->prefix == &oldDtd->defaultPrefix)
+ newA->prefix = &newDtd->defaultPrefix;
+ else
+ newA->prefix = (PREFIX *)lookup(&(newDtd->prefixes), oldA->prefix->name, 0);
+ }
+ }
+
+ /* Copy the element type table. */
+
+ hashTableIterInit(&iter, &(oldDtd->elementTypes));
+
+ for (;;) {
+ int i;
+ ELEMENT_TYPE *newE;
+ const XML_Char *name;
+ const ELEMENT_TYPE *oldE = (ELEMENT_TYPE *)hashTableIterNext(&iter);
+ if (!oldE)
+ break;
+ name = poolCopyString(&(newDtd->pool), oldE->name);
+ if (!name)
+ return 0;
+ newE = (ELEMENT_TYPE *)lookup(&(newDtd->elementTypes), name, sizeof(ELEMENT_TYPE));
+ if (!newE)
+ return 0;
+ if (oldE->nDefaultAtts) {
+ newE->defaultAtts = (DEFAULT_ATTRIBUTE *)malloc(oldE->nDefaultAtts * sizeof(DEFAULT_ATTRIBUTE));
+ if (!newE->defaultAtts)
+ return 0;
+ }
+ if (oldE->idAtt)
+ newE->idAtt = (ATTRIBUTE_ID *)lookup(&(newDtd->attributeIds), oldE->idAtt->name, 0);
+ newE->allocDefaultAtts = newE->nDefaultAtts = oldE->nDefaultAtts;
+ if (oldE->prefix)
+ newE->prefix = (PREFIX *)lookup(&(newDtd->prefixes), oldE->prefix->name, 0);
+ for (i = 0; i < newE->nDefaultAtts; i++) {
+ newE->defaultAtts[i].id = (ATTRIBUTE_ID *)lookup(&(newDtd->attributeIds), oldE->defaultAtts[i].id->name, 0);
+ newE->defaultAtts[i].isCdata = oldE->defaultAtts[i].isCdata;
+ if (oldE->defaultAtts[i].value) {
+ newE->defaultAtts[i].value = poolCopyString(&(newDtd->pool), oldE->defaultAtts[i].value);
+ if (!newE->defaultAtts[i].value)
+ return 0;
+ }
+ else
+ newE->defaultAtts[i].value = 0;
+ }
+ }
+
+ /* Copy the entity tables. */
+ if (!copyEntityTable(&(newDtd->generalEntities),
+ &(newDtd->pool),
+ &(oldDtd->generalEntities)))
+ return 0;
+
+#ifdef XML_DTD
+ if (!copyEntityTable(&(newDtd->paramEntities),
+ &(newDtd->pool),
+ &(oldDtd->paramEntities)))
+ return 0;
+#endif /* XML_DTD */
+
+ newDtd->complete = oldDtd->complete;
+ newDtd->standalone = oldDtd->standalone;
+ return 1;
+}
+
+static int copyEntityTable(HASH_TABLE *newTable,
+ STRING_POOL *newPool,
+ const HASH_TABLE *oldTable)
+{
+ HASH_TABLE_ITER iter;
+ const XML_Char *cachedOldBase = 0;
+ const XML_Char *cachedNewBase = 0;
+
+ hashTableIterInit(&iter, oldTable);
+
+ for (;;) {
+ ENTITY *newE;
+ const XML_Char *name;
+ const ENTITY *oldE = (ENTITY *)hashTableIterNext(&iter);
+ if (!oldE)
+ break;
+ name = poolCopyString(newPool, oldE->name);
+ if (!name)
+ return 0;
+ newE = (ENTITY *)lookup(newTable, name, sizeof(ENTITY));
+ if (!newE)
+ return 0;
+ if (oldE->systemId) {
+ const XML_Char *tem = poolCopyString(newPool, oldE->systemId);
+ if (!tem)
+ return 0;
+ newE->systemId = tem;
+ if (oldE->base) {
+ if (oldE->base == cachedOldBase)
+ newE->base = cachedNewBase;
+ else {
+ cachedOldBase = oldE->base;
+ tem = poolCopyString(newPool, cachedOldBase);
+ if (!tem)
+ return 0;
+ cachedNewBase = newE->base = tem;
+ }
+ }
+ }
+ else {
+ const XML_Char *tem = poolCopyStringN(newPool, oldE->textPtr, oldE->textLen);
+ if (!tem)
+ return 0;
+ newE->textPtr = tem;
+ newE->textLen = oldE->textLen;
+ }
+ if (oldE->notation) {
+ const XML_Char *tem = poolCopyString(newPool, oldE->notation);
+ if (!tem)
+ return 0;
+ newE->notation = tem;
+ }
+ }
+ return 1;
+}
+
+#define INIT_SIZE 64
+
+static
+int keyeq(KEY s1, KEY s2)
+{
+ for (; *s1 == *s2; s1++, s2++)
+ if (*s1 == 0)
+ return 1;
+ return 0;
+}
+
+static
+unsigned long hash(KEY s)
+{
+ unsigned long h = 0;
+ while (*s)
+ h = (h << 5) + h + (unsigned char)*s++;
+ return h;
+}
+
+static
+NAMED *lookup(HASH_TABLE *table, KEY name, size_t createSize)
+{
+ size_t i;
+ if (table->size == 0) {
+ if (!createSize)
+ return 0;
+ table->v = calloc(INIT_SIZE, sizeof(NAMED *));
+ if (!table->v)
+ return 0;
+ table->size = INIT_SIZE;
+ table->usedLim = INIT_SIZE / 2;
+ i = hash(name) & (table->size - 1);
+ }
+ else {
+ unsigned long h = hash(name);
+ for (i = h & (table->size - 1);
+ table->v[i];
+ i == 0 ? i = table->size - 1 : --i) {
+ if (keyeq(name, table->v[i]->name))
+ return table->v[i];
+ }
+ if (!createSize)
+ return 0;
+ if (table->used == table->usedLim) {
+ /* check for overflow */
+ size_t newSize = table->size * 2;
+ NAMED **newV = calloc(newSize, sizeof(NAMED *));
+ if (!newV)
+ return 0;
+ for (i = 0; i < table->size; i++)
+ if (table->v[i]) {
+ size_t j;
+ for (j = hash(table->v[i]->name) & (newSize - 1);
+ newV[j];
+ j == 0 ? j = newSize - 1 : --j)
+ ;
+ newV[j] = table->v[i];
+ }
+ free(table->v);
+ table->v = newV;
+ table->size = newSize;
+ table->usedLim = newSize/2;
+ for (i = h & (table->size - 1);
+ table->v[i];
+ i == 0 ? i = table->size - 1 : --i)
+ ;
+ }
+ }
+ table->v[i] = calloc(1, createSize);
+ if (!table->v[i])
+ return 0;
+ table->v[i]->name = name;
+ (table->used)++;
+ return table->v[i];
+}
+
+static
+void hashTableDestroy(HASH_TABLE *table)
+{
+ size_t i;
+ for (i = 0; i < table->size; i++) {
+ NAMED *p = table->v[i];
+ if (p)
+ free(p);
+ }
+ if (table->v)
+ free(table->v);
+}
+
+static
+void hashTableInit(HASH_TABLE *p)
+{
+ p->size = 0;
+ p->usedLim = 0;
+ p->used = 0;
+ p->v = 0;
+}
+
+static
+void hashTableIterInit(HASH_TABLE_ITER *iter, const HASH_TABLE *table)
+{
+ iter->p = table->v;
+ iter->end = iter->p + table->size;
+}
+
+static
+NAMED *hashTableIterNext(HASH_TABLE_ITER *iter)
+{
+ while (iter->p != iter->end) {
+ NAMED *tem = *(iter->p)++;
+ if (tem)
+ return tem;
+ }
+ return 0;
+}
+
+
+static
+void poolInit(STRING_POOL *pool)
+{
+ pool->blocks = 0;
+ pool->freeBlocks = 0;
+ pool->start = 0;
+ pool->ptr = 0;
+ pool->end = 0;
+}
+
+static
+void poolClear(STRING_POOL *pool)
+{
+ if (!pool->freeBlocks)
+ pool->freeBlocks = pool->blocks;
+ else {
+ BLOCK *p = pool->blocks;
+ while (p) {
+ BLOCK *tem = p->next;
+ p->next = pool->freeBlocks;
+ pool->freeBlocks = p;
+ p = tem;
+ }
+ }
+ pool->blocks = 0;
+ pool->start = 0;
+ pool->ptr = 0;
+ pool->end = 0;
+}
+
+static
+void poolDestroy(STRING_POOL *pool)
+{
+ BLOCK *p = pool->blocks;
+ while (p) {
+ BLOCK *tem = p->next;
+ free(p);
+ p = tem;
+ }
+ pool->blocks = 0;
+ p = pool->freeBlocks;
+ while (p) {
+ BLOCK *tem = p->next;
+ free(p);
+ p = tem;
+ }
+ pool->freeBlocks = 0;
+ pool->ptr = 0;
+ pool->start = 0;
+ pool->end = 0;
+}
+
+static
+XML_Char *poolAppend(STRING_POOL *pool, const ENCODING *enc,
+ const char *ptr, const char *end)
+{
+ if (!pool->ptr && !poolGrow(pool))
+ return 0;
+ for (;;) {
+ XmlConvert(enc, &ptr, end, (ICHAR **)&(pool->ptr), (ICHAR *)pool->end);
+ if (ptr == end)
+ break;
+ if (!poolGrow(pool))
+ return 0;
+ }
+ return pool->start;
+}
+
+static const XML_Char *poolCopyString(STRING_POOL *pool, const XML_Char *s)
+{
+ do {
+ if (!poolAppendChar(pool, *s))
+ return 0;
+ } while (*s++);
+ s = pool->start;
+ poolFinish(pool);
+ return s;
+}
+
+static const XML_Char *poolCopyStringN(STRING_POOL *pool, const XML_Char *s, int n)
+{
+ if (!pool->ptr && !poolGrow(pool))
+ return 0;
+ for (; n > 0; --n, s++) {
+ if (!poolAppendChar(pool, *s))
+ return 0;
+
+ }
+ s = pool->start;
+ poolFinish(pool);
+ return s;
+}
+
+static
+XML_Char *poolStoreString(STRING_POOL *pool, const ENCODING *enc,
+ const char *ptr, const char *end)
+{
+ if (!poolAppend(pool, enc, ptr, end))
+ return 0;
+ if (pool->ptr == pool->end && !poolGrow(pool))
+ return 0;
+ *(pool->ptr)++ = 0;
+ return pool->start;
+}
+
+static
+int poolGrow(STRING_POOL *pool)
+{
+ if (pool->freeBlocks) {
+ if (pool->start == 0) {
+ pool->blocks = pool->freeBlocks;
+ pool->freeBlocks = pool->freeBlocks->next;
+ pool->blocks->next = 0;
+ pool->start = pool->blocks->s;
+ pool->end = pool->start + pool->blocks->size;
+ pool->ptr = pool->start;
+ return 1;
+ }
+ if (pool->end - pool->start < pool->freeBlocks->size) {
+ BLOCK *tem = pool->freeBlocks->next;
+ pool->freeBlocks->next = pool->blocks;
+ pool->blocks = pool->freeBlocks;
+ pool->freeBlocks = tem;
+ memcpy(pool->blocks->s, pool->start, (pool->end - pool->start) * sizeof(XML_Char));
+ pool->ptr = pool->blocks->s + (pool->ptr - pool->start);
+ pool->start = pool->blocks->s;
+ pool->end = pool->start + pool->blocks->size;
+ return 1;
+ }
+ }
+ if (pool->blocks && pool->start == pool->blocks->s) {
+ int blockSize = (pool->end - pool->start)*2;
+ pool->blocks = realloc(pool->blocks, offsetof(BLOCK, s) + blockSize * sizeof(XML_Char));
+ if (!pool->blocks)
+ return 0;
+ pool->blocks->size = blockSize;
+ pool->ptr = pool->blocks->s + (pool->ptr - pool->start);
+ pool->start = pool->blocks->s;
+ pool->end = pool->start + blockSize;
+ }
+ else {
+ BLOCK *tem;
+ int blockSize = pool->end - pool->start;
+ if (blockSize < INIT_BLOCK_SIZE)
+ blockSize = INIT_BLOCK_SIZE;
+ else
+ blockSize *= 2;
+ tem = malloc(offsetof(BLOCK, s) + blockSize * sizeof(XML_Char));
+ if (!tem)
+ return 0;
+ tem->size = blockSize;
+ tem->next = pool->blocks;
+ pool->blocks = tem;
+ if (pool->ptr != pool->start)
+ memcpy(tem->s, pool->start, (pool->ptr - pool->start) * sizeof(XML_Char));
+ pool->ptr = tem->s + (pool->ptr - pool->start);
+ pool->start = tem->s;
+ pool->end = tem->s + blockSize;
+ }
+ return 1;
+}
diff --git a/src/expat/xmlparse/xmlparse.h b/src/expat/xmlparse/xmlparse.h
new file mode 100755
index 0000000..6dffe74
--- /dev/null
+++ b/src/expat/xmlparse/xmlparse.h
@@ -0,0 +1,527 @@
+/*
+Copyright (c) 1998, 1999, 2000 Thai Open Source Software Center Ltd
+See the file copying.txt for copying permission.
+*/
+
+#ifndef XmlParse_INCLUDED
+#define XmlParse_INCLUDED 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef XMLPARSEAPI
+#define XMLPARSEAPI /* as nothing */
+#endif
+
+typedef void *XML_Parser;
+
+#ifdef XML_UNICODE_WCHAR_T
+
+/* XML_UNICODE_WCHAR_T will work only if sizeof(wchar_t) == 2 and wchar_t
+uses Unicode. */
+/* Information is UTF-16 encoded as wchar_ts */
+
+#ifndef XML_UNICODE
+#define XML_UNICODE
+#endif
+
+#include <stddef.h>
+typedef wchar_t XML_Char;
+typedef wchar_t XML_LChar;
+
+#else /* not XML_UNICODE_WCHAR_T */
+
+#ifdef XML_UNICODE
+
+/* Information is UTF-16 encoded as unsigned shorts */
+typedef unsigned short XML_Char;
+typedef char XML_LChar;
+
+#else /* not XML_UNICODE */
+
+/* Information is UTF-8 encoded. */
+typedef char XML_Char;
+typedef char XML_LChar;
+
+#endif /* not XML_UNICODE */
+
+#endif /* not XML_UNICODE_WCHAR_T */
+
+
+/* Constructs a new parser; encoding is the encoding specified by the external
+protocol or null if there is none specified. */
+
+XML_Parser XMLPARSEAPI
+XML_ParserCreate(const XML_Char *encoding);
+
+/* Constructs a new parser and namespace processor. Element type names
+and attribute names that belong to a namespace will be expanded;
+unprefixed attribute names are never expanded; unprefixed element type
+names are expanded only if there is a default namespace. The expanded
+name is the concatenation of the namespace URI, the namespace separator character,
+and the local part of the name. If the namespace separator is '\0' then
+the namespace URI and the local part will be concatenated without any
+separator. When a namespace is not declared, the name and prefix will be
+passed through without expansion. */
+
+XML_Parser XMLPARSEAPI
+XML_ParserCreateNS(const XML_Char *encoding, XML_Char namespaceSeparator);
+
+
+/* atts is array of name/value pairs, terminated by 0;
+ names and values are 0 terminated. */
+
+typedef void (*XML_StartElementHandler)(void *userData,
+ const XML_Char *name,
+ const XML_Char **atts);
+
+typedef void (*XML_EndElementHandler)(void *userData,
+ const XML_Char *name);
+
+/* s is not 0 terminated. */
+typedef void (*XML_CharacterDataHandler)(void *userData,
+ const XML_Char *s,
+ int len);
+
+/* target and data are 0 terminated */
+typedef void (*XML_ProcessingInstructionHandler)(void *userData,
+ const XML_Char *target,
+ const XML_Char *data);
+
+/* data is 0 terminated */
+typedef void (*XML_CommentHandler)(void *userData, const XML_Char *data);
+
+typedef void (*XML_StartCdataSectionHandler)(void *userData);
+typedef void (*XML_EndCdataSectionHandler)(void *userData);
+
+/* This is called for any characters in the XML document for
+which there is no applicable handler. This includes both
+characters that are part of markup which is of a kind that is
+not reported (comments, markup declarations), or characters
+that are part of a construct which could be reported but
+for which no handler has been supplied. The characters are passed
+exactly as they were in the XML document except that
+they will be encoded in UTF-8. Line boundaries are not normalized.
+Note that a byte order mark character is not passed to the default handler.
+There are no guarantees about how characters are divided between calls
+to the default handler: for example, a comment might be split between
+multiple calls. */
+
+typedef void (*XML_DefaultHandler)(void *userData,
+ const XML_Char *s,
+ int len);
+
+/* This is called for the start of the DOCTYPE declaration when the
+name of the DOCTYPE is encountered. */
+typedef void (*XML_StartDoctypeDeclHandler)(void *userData,
+ const XML_Char *doctypeName);
+
+/* This is called for the start of the DOCTYPE declaration when the
+closing > is encountered, but after processing any external subset. */
+typedef void (*XML_EndDoctypeDeclHandler)(void *userData);
+
+/* This is called for a declaration of an unparsed (NDATA)
+entity. The base argument is whatever was set by XML_SetBase.
+The entityName, systemId and notationName arguments will never be null.
+The other arguments may be. */
+
+typedef void (*XML_UnparsedEntityDeclHandler)(void *userData,
+ const XML_Char *entityName,
+ const XML_Char *base,
+ const XML_Char *systemId,
+ const XML_Char *publicId,
+ const XML_Char *notationName);
+
+/* This is called for a declaration of notation.
+The base argument is whatever was set by XML_SetBase.
+The notationName will never be null. The other arguments can be. */
+
+typedef void (*XML_NotationDeclHandler)(void *userData,
+ const XML_Char *notationName,
+ const XML_Char *base,
+ const XML_Char *systemId,
+ const XML_Char *publicId);
+
+typedef void (*XML_ExternalParsedEntityDeclHandler)(void *userData,
+ const XML_Char *entityName,
+ const XML_Char *base,
+ const XML_Char *systemId,
+ const XML_Char *publicId);
+
+typedef void (*XML_InternalParsedEntityDeclHandler)(void *userData,
+ const XML_Char *entityName,
+ const XML_Char *replacementText,
+ int replacementTextLength);
+
+/* When namespace processing is enabled, these are called once for
+each namespace declaration. The call to the start and end element
+handlers occur between the calls to the start and end namespace
+declaration handlers. For an xmlns attribute, prefix will be null.
+For an xmlns="" attribute, uri will be null. */
+
+typedef void (*XML_StartNamespaceDeclHandler)(void *userData,
+ const XML_Char *prefix,
+ const XML_Char *uri);
+
+typedef void (*XML_EndNamespaceDeclHandler)(void *userData,
+ const XML_Char *prefix);
+
+/* This is called if the document is not standalone (it has an
+external subset or a reference to a parameter entity, but does not
+have standalone="yes"). If this handler returns 0, then processing
+will not continue, and the parser will return a
+XML_ERROR_NOT_STANDALONE error. */
+
+typedef int (*XML_NotStandaloneHandler)(void *userData);
+
+/* This is called for a reference to an external parsed general entity.
+The referenced entity is not automatically parsed.
+The application can parse it immediately or later using
+XML_ExternalEntityParserCreate.
+The parser argument is the parser parsing the entity containing the reference;
+it can be passed as the parser argument to XML_ExternalEntityParserCreate.
+The systemId argument is the system identifier as specified in the entity declaration;
+it will not be null.
+The base argument is the system identifier that should be used as the base for
+resolving systemId if systemId was relative; this is set by XML_SetBase;
+it may be null.
+The publicId argument is the public identifier as specified in the entity declaration,
+or null if none was specified; the whitespace in the public identifier
+will have been normalized as required by the XML spec.
+The context argument specifies the parsing context in the format
+expected by the context argument to
+XML_ExternalEntityParserCreate; context is valid only until the handler
+returns, so if the referenced entity is to be parsed later, it must be copied.
+The handler should return 0 if processing should not continue because of
+a fatal error in the handling of the external entity.
+In this case the calling parser will return an XML_ERROR_EXTERNAL_ENTITY_HANDLING
+error.
+Note that unlike other handlers the first argument is the parser, not userData. */
+
+typedef int (*XML_ExternalEntityRefHandler)(XML_Parser parser,
+ const XML_Char *context,
+ const XML_Char *base,
+ const XML_Char *systemId,
+ const XML_Char *publicId);
+
+/* This structure is filled in by the XML_UnknownEncodingHandler
+to provide information to the parser about encodings that are unknown
+to the parser.
+The map[b] member gives information about byte sequences
+whose first byte is b.
+If map[b] is c where c is >= 0, then b by itself encodes the Unicode scalar value c.
+If map[b] is -1, then the byte sequence is malformed.
+If map[b] is -n, where n >= 2, then b is the first byte of an n-byte
+sequence that encodes a single Unicode scalar value.
+The data member will be passed as the first argument to the convert function.
+The convert function is used to convert multibyte sequences;
+s will point to a n-byte sequence where map[(unsigned char)*s] == -n.
+The convert function must return the Unicode scalar value
+represented by this byte sequence or -1 if the byte sequence is malformed.
+The convert function may be null if the encoding is a single-byte encoding,
+that is if map[b] >= -1 for all bytes b.
+When the parser is finished with the encoding, then if release is not null,
+it will call release passing it the data member;
+once release has been called, the convert function will not be called again.
+
+Expat places certain restrictions on the encodings that are supported
+using this mechanism.
+
+1. Every ASCII character that can appear in a well-formed XML document,
+other than the characters
+
+ $@\^`{}~
+
+must be represented by a single byte, and that byte must be the
+same byte that represents that character in ASCII.
+
+2. No character may require more than 4 bytes to encode.
+
+3. All characters encoded must have Unicode scalar values <= 0xFFFF,
+(ie characters that would be encoded by surrogates in UTF-16
+are not allowed). Note that this restriction doesn't apply to
+the built-in support for UTF-8 and UTF-16.
+
+4. No Unicode character may be encoded by more than one distinct sequence
+of bytes. */
+
+typedef struct {
+ int map[256];
+ void *data;
+ int (*convert)(void *data, const char *s);
+ void (*release)(void *data);
+} XML_Encoding;
+
+/* This is called for an encoding that is unknown to the parser.
+The encodingHandlerData argument is that which was passed as the
+second argument to XML_SetUnknownEncodingHandler.
+The name argument gives the name of the encoding as specified in
+the encoding declaration.
+If the callback can provide information about the encoding,
+it must fill in the XML_Encoding structure, and return 1.
+Otherwise it must return 0.
+If info does not describe a suitable encoding,
+then the parser will return an XML_UNKNOWN_ENCODING error. */
+
+typedef int (*XML_UnknownEncodingHandler)(void *encodingHandlerData,
+ const XML_Char *name,
+ XML_Encoding *info);
+
+void XMLPARSEAPI
+XML_SetElementHandler(XML_Parser parser,
+ XML_StartElementHandler start,
+ XML_EndElementHandler end);
+
+void XMLPARSEAPI
+XML_SetCharacterDataHandler(XML_Parser parser,
+ XML_CharacterDataHandler handler);
+
+void XMLPARSEAPI
+XML_SetProcessingInstructionHandler(XML_Parser parser,
+ XML_ProcessingInstructionHandler handler);
+void XMLPARSEAPI
+XML_SetCommentHandler(XML_Parser parser,
+ XML_CommentHandler handler);
+
+void XMLPARSEAPI
+XML_SetCdataSectionHandler(XML_Parser parser,
+ XML_StartCdataSectionHandler start,
+ XML_EndCdataSectionHandler end);
+
+/* This sets the default handler and also inhibits expansion of internal entities.
+The entity reference will be passed to the default handler. */
+
+void XMLPARSEAPI
+XML_SetDefaultHandler(XML_Parser parser,
+ XML_DefaultHandler handler);
+
+/* This sets the default handler but does not inhibit expansion of internal entities.
+The entity reference will not be passed to the default handler. */
+
+void XMLPARSEAPI
+XML_SetDefaultHandlerExpand(XML_Parser parser,
+ XML_DefaultHandler handler);
+
+void XMLPARSEAPI
+XML_SetDoctypeDeclHandler(XML_Parser parser,
+ XML_StartDoctypeDeclHandler start,
+ XML_EndDoctypeDeclHandler end);
+
+void XMLPARSEAPI
+XML_SetUnparsedEntityDeclHandler(XML_Parser parser,
+ XML_UnparsedEntityDeclHandler handler);
+
+void XMLPARSEAPI
+XML_SetNotationDeclHandler(XML_Parser parser,
+ XML_NotationDeclHandler handler);
+
+void XMLPARSEAPI
+XML_SetExternalParsedEntityDeclHandler(XML_Parser parser,
+ XML_ExternalParsedEntityDeclHandler handler);
+
+void XMLPARSEAPI
+XML_SetInternalParsedEntityDeclHandler(XML_Parser parser,
+ XML_InternalParsedEntityDeclHandler handler);
+
+void XMLPARSEAPI
+XML_SetNamespaceDeclHandler(XML_Parser parser,
+ XML_StartNamespaceDeclHandler start,
+ XML_EndNamespaceDeclHandler end);
+
+void XMLPARSEAPI
+XML_SetNotStandaloneHandler(XML_Parser parser,
+ XML_NotStandaloneHandler handler);
+
+void XMLPARSEAPI
+XML_SetExternalEntityRefHandler(XML_Parser parser,
+ XML_ExternalEntityRefHandler handler);
+
+/* If a non-null value for arg is specified here, then it will be passed
+as the first argument to the external entity ref handler instead
+of the parser object. */
+void XMLPARSEAPI
+XML_SetExternalEntityRefHandlerArg(XML_Parser, void *arg);
+
+void XMLPARSEAPI
+XML_SetUnknownEncodingHandler(XML_Parser parser,
+ XML_UnknownEncodingHandler handler,
+ void *encodingHandlerData);
+
+/* This can be called within a handler for a start element, end element,
+processing instruction or character data. It causes the corresponding
+markup to be passed to the default handler. */
+void XMLPARSEAPI XML_DefaultCurrent(XML_Parser parser);
+
+/* This value is passed as the userData argument to callbacks. */
+void XMLPARSEAPI
+XML_SetUserData(XML_Parser parser, void *userData);
+
+/* Returns the last value set by XML_SetUserData or null. */
+#define XML_GetUserData(parser) (*(void **)(parser))
+
+/* This is equivalent to supplying an encoding argument
+to XML_ParserCreate. It must not be called after XML_Parse
+or XML_ParseBuffer. */
+
+int XMLPARSEAPI
+XML_SetEncoding(XML_Parser parser, const XML_Char *encoding);
+
+/* If this function is called, then the parser will be passed
+as the first argument to callbacks instead of userData.
+The userData will still be accessible using XML_GetUserData. */
+
+void XMLPARSEAPI
+XML_UseParserAsHandlerArg(XML_Parser parser);
+
+/* Sets the base to be used for resolving relative URIs in system identifiers in
+declarations. Resolving relative identifiers is left to the application:
+this value will be passed through as the base argument to the
+XML_ExternalEntityRefHandler, XML_NotationDeclHandler
+and XML_UnparsedEntityDeclHandler. The base argument will be copied.
+Returns zero if out of memory, non-zero otherwise. */
+
+int XMLPARSEAPI
+XML_SetBase(XML_Parser parser, const XML_Char *base);
+
+const XML_Char XMLPARSEAPI *
+XML_GetBase(XML_Parser parser);
+
+/* Returns the number of the attribute/value pairs passed in last call
+to the XML_StartElementHandler that were specified in the start-tag
+rather than defaulted. Each attribute/value pair counts as 2; thus
+this correspondds to an index into the atts array passed to the
+XML_StartElementHandler. */
+
+int XMLPARSEAPI XML_GetSpecifiedAttributeCount(XML_Parser parser);
+
+/* Returns the index of the ID attribute passed in the last call to
+XML_StartElementHandler, or -1 if there is no ID attribute. Each
+attribute/value pair counts as 2; thus this correspondds to an index
+into the atts array passed to the XML_StartElementHandler. */
+int XMLPARSEAPI XML_GetIdAttributeIndex(XML_Parser parser);
+
+/* Parses some input. Returns 0 if a fatal error is detected.
+The last call to XML_Parse must have isFinal true;
+len may be zero for this call (or any other). */
+int XMLPARSEAPI
+XML_Parse(XML_Parser parser, const char *s, int len, int isFinal);
+
+void XMLPARSEAPI *
+XML_GetBuffer(XML_Parser parser, int len);
+
+int XMLPARSEAPI
+XML_ParseBuffer(XML_Parser parser, int len, int isFinal);
+
+/* Creates an XML_Parser object that can parse an external general entity;
+context is a '\0'-terminated string specifying the parse context;
+encoding is a '\0'-terminated string giving the name of the externally specified encoding,
+or null if there is no externally specified encoding.
+The context string consists of a sequence of tokens separated by formfeeds (\f);
+a token consisting of a name specifies that the general entity of the name
+is open; a token of the form prefix=uri specifies the namespace for a particular
+prefix; a token of the form =uri specifies the default namespace.
+This can be called at any point after the first call to an ExternalEntityRefHandler
+so longer as the parser has not yet been freed.
+The new parser is completely independent and may safely be used in a separate thread.
+The handlers and userData are initialized from the parser argument.
+Returns 0 if out of memory. Otherwise returns a new XML_Parser object. */
+XML_Parser XMLPARSEAPI
+XML_ExternalEntityParserCreate(XML_Parser parser,
+ const XML_Char *context,
+ const XML_Char *encoding);
+
+enum XML_ParamEntityParsing {
+ XML_PARAM_ENTITY_PARSING_NEVER,
+ XML_PARAM_ENTITY_PARSING_UNLESS_STANDALONE,
+ XML_PARAM_ENTITY_PARSING_ALWAYS
+};
+
+/* Controls parsing of parameter entities (including the external DTD
+subset). If parsing of parameter entities is enabled, then references
+to external parameter entities (including the external DTD subset)
+will be passed to the handler set with
+XML_SetExternalEntityRefHandler. The context passed will be 0.
+Unlike external general entities, external parameter entities can only
+be parsed synchronously. If the external parameter entity is to be
+parsed, it must be parsed during the call to the external entity ref
+handler: the complete sequence of XML_ExternalEntityParserCreate,
+XML_Parse/XML_ParseBuffer and XML_ParserFree calls must be made during
+this call. After XML_ExternalEntityParserCreate has been called to
+create the parser for the external parameter entity (context must be 0
+for this call), it is illegal to make any calls on the old parser
+until XML_ParserFree has been called on the newly created parser. If
+the library has been compiled without support for parameter entity
+parsing (ie without XML_DTD being defined), then
+XML_SetParamEntityParsing will return 0 if parsing of parameter
+entities is requested; otherwise it will return non-zero. */
+
+int XMLPARSEAPI
+XML_SetParamEntityParsing(XML_Parser parser,
+ enum XML_ParamEntityParsing parsing);
+
+enum XML_Error {
+ XML_ERROR_NONE,
+ XML_ERROR_NO_MEMORY,
+ XML_ERROR_SYNTAX,
+ XML_ERROR_NO_ELEMENTS,
+ XML_ERROR_INVALID_TOKEN,
+ XML_ERROR_UNCLOSED_TOKEN,
+ XML_ERROR_PARTIAL_CHAR,
+ XML_ERROR_TAG_MISMATCH,
+ XML_ERROR_DUPLICATE_ATTRIBUTE,
+ XML_ERROR_JUNK_AFTER_DOC_ELEMENT,
+ XML_ERROR_PARAM_ENTITY_REF,
+ XML_ERROR_UNDEFINED_ENTITY,
+ XML_ERROR_RECURSIVE_ENTITY_REF,
+ XML_ERROR_ASYNC_ENTITY,
+ XML_ERROR_BAD_CHAR_REF,
+ XML_ERROR_BINARY_ENTITY_REF,
+ XML_ERROR_ATTRIBUTE_EXTERNAL_ENTITY_REF,
+ XML_ERROR_MISPLACED_XML_PI,
+ XML_ERROR_UNKNOWN_ENCODING,
+ XML_ERROR_INCORRECT_ENCODING,
+ XML_ERROR_UNCLOSED_CDATA_SECTION,
+ XML_ERROR_EXTERNAL_ENTITY_HANDLING,
+ XML_ERROR_NOT_STANDALONE
+};
+
+/* If XML_Parse or XML_ParseBuffer have returned 0, then XML_GetErrorCode
+returns information about the error. */
+
+enum XML_Error XMLPARSEAPI XML_GetErrorCode(XML_Parser parser);
+
+/* These functions return information about the current parse location.
+They may be called when XML_Parse or XML_ParseBuffer return 0;
+in this case the location is the location of the character at which
+the error was detected.
+They may also be called from any other callback called to report
+some parse event; in this the location is the location of the first
+of the sequence of characters that generated the event. */
+
+int XMLPARSEAPI XML_GetCurrentLineNumber(XML_Parser parser);
+int XMLPARSEAPI XML_GetCurrentColumnNumber(XML_Parser parser);
+long XMLPARSEAPI XML_GetCurrentByteIndex(XML_Parser parser);
+
+/* Return the number of bytes in the current event.
+Returns 0 if the event is in an internal entity. */
+
+int XMLPARSEAPI XML_GetCurrentByteCount(XML_Parser parser);
+
+/* For backwards compatibility with previous versions. */
+#define XML_GetErrorLineNumber XML_GetCurrentLineNumber
+#define XML_GetErrorColumnNumber XML_GetCurrentColumnNumber
+#define XML_GetErrorByteIndex XML_GetCurrentByteIndex
+
+/* Frees memory used by the parser. */
+void XMLPARSEAPI
+XML_ParserFree(XML_Parser parser);
+
+/* Returns a string describing the error. */
+const XML_LChar XMLPARSEAPI *XML_ErrorString(int code);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* not XmlParse_INCLUDED */
diff --git a/src/expat/xmltok/ascii.h b/src/expat/xmltok/ascii.h
new file mode 100755
index 0000000..d45a3ca
--- /dev/null
+++ b/src/expat/xmltok/ascii.h
@@ -0,0 +1,86 @@
+/*
+Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd
+See the file copying.txt for copying permission.
+*/
+
+#define ASCII_A 0x41
+#define ASCII_B 0x42
+#define ASCII_C 0x43
+#define ASCII_D 0x44
+#define ASCII_E 0x45
+#define ASCII_F 0x46
+#define ASCII_G 0x47
+#define ASCII_H 0x48
+#define ASCII_I 0x49
+#define ASCII_J 0x4A
+#define ASCII_K 0x4B
+#define ASCII_L 0x4C
+#define ASCII_M 0x4D
+#define ASCII_N 0x4E
+#define ASCII_O 0x4F
+#define ASCII_P 0x50
+#define ASCII_Q 0x51
+#define ASCII_R 0x52
+#define ASCII_S 0x53
+#define ASCII_T 0x54
+#define ASCII_U 0x55
+#define ASCII_V 0x56
+#define ASCII_W 0x57
+#define ASCII_X 0x58
+#define ASCII_Y 0x59
+#define ASCII_Z 0x5A
+
+#define ASCII_a 0x61
+#define ASCII_b 0x62
+#define ASCII_c 0x63
+#define ASCII_d 0x64
+#define ASCII_e 0x65
+#define ASCII_f 0x66
+#define ASCII_g 0x67
+#define ASCII_h 0x68
+#define ASCII_i 0x69
+#define ASCII_j 0x6A
+#define ASCII_k 0x6B
+#define ASCII_l 0x6C
+#define ASCII_m 0x6D
+#define ASCII_n 0x6E
+#define ASCII_o 0x6F
+#define ASCII_p 0x70
+#define ASCII_q 0x71
+#define ASCII_r 0x72
+#define ASCII_s 0x73
+#define ASCII_t 0x74
+#define ASCII_u 0x75
+#define ASCII_v 0x76
+#define ASCII_w 0x77
+#define ASCII_x 0x78
+#define ASCII_y 0x79
+#define ASCII_z 0x7A
+
+#define ASCII_0 0x30
+#define ASCII_1 0x31
+#define ASCII_2 0x32
+#define ASCII_3 0x33
+#define ASCII_4 0x34
+#define ASCII_5 0x35
+#define ASCII_6 0x36
+#define ASCII_7 0x37
+#define ASCII_8 0x38
+#define ASCII_9 0x39
+
+#define ASCII_TAB 0x09
+#define ASCII_SPACE 0x20
+#define ASCII_EXCL 0x21
+#define ASCII_QUOT 0x22
+#define ASCII_AMP 0x26
+#define ASCII_APOS 0x27
+#define ASCII_MINUS 0x2D
+#define ASCII_PERIOD 0x2E
+#define ASCII_COLON 0x3A
+#define ASCII_SEMI 0x3B
+#define ASCII_LT 0x3C
+#define ASCII_EQUALS 0x3D
+#define ASCII_GT 0x3E
+#define ASCII_LSQB 0x5B
+#define ASCII_RSQB 0x5D
+#define ASCII_UNDERSCORE 0x5F
diff --git a/src/expat/xmltok/asciitab.h b/src/expat/xmltok/asciitab.h
new file mode 100755
index 0000000..e994576
--- /dev/null
+++ b/src/expat/xmltok/asciitab.h
@@ -0,0 +1,37 @@
+/*
+Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd
+See the file copying.txt for copying permission.
+*/
+
+/* 0x00 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML,
+/* 0x04 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML,
+/* 0x08 */ BT_NONXML, BT_S, BT_LF, BT_NONXML,
+/* 0x0C */ BT_NONXML, BT_CR, BT_NONXML, BT_NONXML,
+/* 0x10 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML,
+/* 0x14 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML,
+/* 0x18 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML,
+/* 0x1C */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML,
+/* 0x20 */ BT_S, BT_EXCL, BT_QUOT, BT_NUM,
+/* 0x24 */ BT_OTHER, BT_PERCNT, BT_AMP, BT_APOS,
+/* 0x28 */ BT_LPAR, BT_RPAR, BT_AST, BT_PLUS,
+/* 0x2C */ BT_COMMA, BT_MINUS, BT_NAME, BT_SOL,
+/* 0x30 */ BT_DIGIT, BT_DIGIT, BT_DIGIT, BT_DIGIT,
+/* 0x34 */ BT_DIGIT, BT_DIGIT, BT_DIGIT, BT_DIGIT,
+/* 0x38 */ BT_DIGIT, BT_DIGIT, BT_COLON, BT_SEMI,
+/* 0x3C */ BT_LT, BT_EQUALS, BT_GT, BT_QUEST,
+/* 0x40 */ BT_OTHER, BT_HEX, BT_HEX, BT_HEX,
+/* 0x44 */ BT_HEX, BT_HEX, BT_HEX, BT_NMSTRT,
+/* 0x48 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0x4C */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0x50 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0x54 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0x58 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_LSQB,
+/* 0x5C */ BT_OTHER, BT_RSQB, BT_OTHER, BT_NMSTRT,
+/* 0x60 */ BT_OTHER, BT_HEX, BT_HEX, BT_HEX,
+/* 0x64 */ BT_HEX, BT_HEX, BT_HEX, BT_NMSTRT,
+/* 0x68 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0x6C */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0x70 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0x74 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0x78 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_OTHER,
+/* 0x7C */ BT_VERBAR, BT_OTHER, BT_OTHER, BT_OTHER,
diff --git a/src/expat/xmltok/iasciitab.h b/src/expat/xmltok/iasciitab.h
new file mode 100755
index 0000000..2694d9d
--- /dev/null
+++ b/src/expat/xmltok/iasciitab.h
@@ -0,0 +1,38 @@
+/*
+Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd
+See the file copying.txt for copying permission.
+*/
+
+/* Like asciitab.h, except that 0xD has code BT_S rather than BT_CR */
+/* 0x00 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML,
+/* 0x04 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML,
+/* 0x08 */ BT_NONXML, BT_S, BT_LF, BT_NONXML,
+/* 0x0C */ BT_NONXML, BT_S, BT_NONXML, BT_NONXML,
+/* 0x10 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML,
+/* 0x14 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML,
+/* 0x18 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML,
+/* 0x1C */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML,
+/* 0x20 */ BT_S, BT_EXCL, BT_QUOT, BT_NUM,
+/* 0x24 */ BT_OTHER, BT_PERCNT, BT_AMP, BT_APOS,
+/* 0x28 */ BT_LPAR, BT_RPAR, BT_AST, BT_PLUS,
+/* 0x2C */ BT_COMMA, BT_MINUS, BT_NAME, BT_SOL,
+/* 0x30 */ BT_DIGIT, BT_DIGIT, BT_DIGIT, BT_DIGIT,
+/* 0x34 */ BT_DIGIT, BT_DIGIT, BT_DIGIT, BT_DIGIT,
+/* 0x38 */ BT_DIGIT, BT_DIGIT, BT_COLON, BT_SEMI,
+/* 0x3C */ BT_LT, BT_EQUALS, BT_GT, BT_QUEST,
+/* 0x40 */ BT_OTHER, BT_HEX, BT_HEX, BT_HEX,
+/* 0x44 */ BT_HEX, BT_HEX, BT_HEX, BT_NMSTRT,
+/* 0x48 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0x4C */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0x50 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0x54 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0x58 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_LSQB,
+/* 0x5C */ BT_OTHER, BT_RSQB, BT_OTHER, BT_NMSTRT,
+/* 0x60 */ BT_OTHER, BT_HEX, BT_HEX, BT_HEX,
+/* 0x64 */ BT_HEX, BT_HEX, BT_HEX, BT_NMSTRT,
+/* 0x68 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0x6C */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0x70 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0x74 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0x78 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_OTHER,
+/* 0x7C */ BT_VERBAR, BT_OTHER, BT_OTHER, BT_OTHER,
diff --git a/src/expat/xmltok/latin1tab.h b/src/expat/xmltok/latin1tab.h
new file mode 100755
index 0000000..6e01d50
--- /dev/null
+++ b/src/expat/xmltok/latin1tab.h
@@ -0,0 +1,37 @@
+/*
+Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd
+See the file copying.txt for copying permission.
+*/
+
+/* 0x80 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER,
+/* 0x84 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER,
+/* 0x88 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER,
+/* 0x8C */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER,
+/* 0x90 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER,
+/* 0x94 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER,
+/* 0x98 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER,
+/* 0x9C */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER,
+/* 0xA0 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER,
+/* 0xA4 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER,
+/* 0xA8 */ BT_OTHER, BT_OTHER, BT_NMSTRT, BT_OTHER,
+/* 0xAC */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER,
+/* 0xB0 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER,
+/* 0xB4 */ BT_OTHER, BT_NMSTRT, BT_OTHER, BT_NAME,
+/* 0xB8 */ BT_OTHER, BT_OTHER, BT_NMSTRT, BT_OTHER,
+/* 0xBC */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER,
+/* 0xC0 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0xC4 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0xC8 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0xCC */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0xD0 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0xD4 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_OTHER,
+/* 0xD8 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0xDC */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0xE0 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0xE4 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0xE8 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0xEC */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0xF0 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0xF4 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_OTHER,
+/* 0xF8 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0xFC */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
diff --git a/src/expat/xmltok/nametab.h b/src/expat/xmltok/nametab.h
new file mode 100755
index 0000000..b05e62c
--- /dev/null
+++ b/src/expat/xmltok/nametab.h
@@ -0,0 +1,150 @@
+static const unsigned namingBitmap[] = {
+0x00000000, 0x00000000, 0x00000000, 0x00000000,
+0x00000000, 0x00000000, 0x00000000, 0x00000000,
+0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+0x00000000, 0x04000000, 0x87FFFFFE, 0x07FFFFFE,
+0x00000000, 0x00000000, 0xFF7FFFFF, 0xFF7FFFFF,
+0xFFFFFFFF, 0x7FF3FFFF, 0xFFFFFDFE, 0x7FFFFFFF,
+0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFE00F, 0xFC31FFFF,
+0x00FFFFFF, 0x00000000, 0xFFFF0000, 0xFFFFFFFF,
+0xFFFFFFFF, 0xF80001FF, 0x00000003, 0x00000000,
+0x00000000, 0x00000000, 0x00000000, 0x00000000,
+0xFFFFD740, 0xFFFFFFFB, 0x547F7FFF, 0x000FFFFD,
+0xFFFFDFFE, 0xFFFFFFFF, 0xDFFEFFFF, 0xFFFFFFFF,
+0xFFFF0003, 0xFFFFFFFF, 0xFFFF199F, 0x033FCFFF,
+0x00000000, 0xFFFE0000, 0x027FFFFF, 0xFFFFFFFE,
+0x0000007F, 0x00000000, 0xFFFF0000, 0x000707FF,
+0x00000000, 0x07FFFFFE, 0x000007FE, 0xFFFE0000,
+0xFFFFFFFF, 0x7CFFFFFF, 0x002F7FFF, 0x00000060,
+0xFFFFFFE0, 0x23FFFFFF, 0xFF000000, 0x00000003,
+0xFFF99FE0, 0x03C5FDFF, 0xB0000000, 0x00030003,
+0xFFF987E0, 0x036DFDFF, 0x5E000000, 0x001C0000,
+0xFFFBAFE0, 0x23EDFDFF, 0x00000000, 0x00000001,
+0xFFF99FE0, 0x23CDFDFF, 0xB0000000, 0x00000003,
+0xD63DC7E0, 0x03BFC718, 0x00000000, 0x00000000,
+0xFFFDDFE0, 0x03EFFDFF, 0x00000000, 0x00000003,
+0xFFFDDFE0, 0x03EFFDFF, 0x40000000, 0x00000003,
+0xFFFDDFE0, 0x03FFFDFF, 0x00000000, 0x00000003,
+0x00000000, 0x00000000, 0x00000000, 0x00000000,
+0xFFFFFFFE, 0x000D7FFF, 0x0000003F, 0x00000000,
+0xFEF02596, 0x200D6CAE, 0x0000001F, 0x00000000,
+0x00000000, 0x00000000, 0xFFFFFEFF, 0x000003FF,
+0x00000000, 0x00000000, 0x00000000, 0x00000000,
+0x00000000, 0x00000000, 0x00000000, 0x00000000,
+0x00000000, 0xFFFFFFFF, 0xFFFF003F, 0x007FFFFF,
+0x0007DAED, 0x50000000, 0x82315001, 0x002C62AB,
+0x40000000, 0xF580C900, 0x00000007, 0x02010800,
+0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+0x0FFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x03FFFFFF,
+0x3F3FFFFF, 0xFFFFFFFF, 0xAAFF3F3F, 0x3FFFFFFF,
+0xFFFFFFFF, 0x5FDFFFFF, 0x0FCF1FDC, 0x1FDC1FFF,
+0x00000000, 0x00004C40, 0x00000000, 0x00000000,
+0x00000007, 0x00000000, 0x00000000, 0x00000000,
+0x00000080, 0x000003FE, 0xFFFFFFFE, 0xFFFFFFFF,
+0x001FFFFF, 0xFFFFFFFE, 0xFFFFFFFF, 0x07FFFFFF,
+0xFFFFFFE0, 0x00001FFF, 0x00000000, 0x00000000,
+0x00000000, 0x00000000, 0x00000000, 0x00000000,
+0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+0xFFFFFFFF, 0x0000003F, 0x00000000, 0x00000000,
+0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+0xFFFFFFFF, 0x0000000F, 0x00000000, 0x00000000,
+0x00000000, 0x07FF6000, 0x87FFFFFE, 0x07FFFFFE,
+0x00000000, 0x00800000, 0xFF7FFFFF, 0xFF7FFFFF,
+0x00FFFFFF, 0x00000000, 0xFFFF0000, 0xFFFFFFFF,
+0xFFFFFFFF, 0xF80001FF, 0x00030003, 0x00000000,
+0xFFFFFFFF, 0xFFFFFFFF, 0x0000003F, 0x00000003,
+0xFFFFD7C0, 0xFFFFFFFB, 0x547F7FFF, 0x000FFFFD,
+0xFFFFDFFE, 0xFFFFFFFF, 0xDFFEFFFF, 0xFFFFFFFF,
+0xFFFF007B, 0xFFFFFFFF, 0xFFFF199F, 0x033FCFFF,
+0x00000000, 0xFFFE0000, 0x027FFFFF, 0xFFFFFFFE,
+0xFFFE007F, 0xBBFFFFFB, 0xFFFF0016, 0x000707FF,
+0x00000000, 0x07FFFFFE, 0x0007FFFF, 0xFFFF03FF,
+0xFFFFFFFF, 0x7CFFFFFF, 0xFFEF7FFF, 0x03FF3DFF,
+0xFFFFFFEE, 0xF3FFFFFF, 0xFF1E3FFF, 0x0000FFCF,
+0xFFF99FEE, 0xD3C5FDFF, 0xB080399F, 0x0003FFCF,
+0xFFF987E4, 0xD36DFDFF, 0x5E003987, 0x001FFFC0,
+0xFFFBAFEE, 0xF3EDFDFF, 0x00003BBF, 0x0000FFC1,
+0xFFF99FEE, 0xF3CDFDFF, 0xB0C0398F, 0x0000FFC3,
+0xD63DC7EC, 0xC3BFC718, 0x00803DC7, 0x0000FF80,
+0xFFFDDFEE, 0xC3EFFDFF, 0x00603DDF, 0x0000FFC3,
+0xFFFDDFEC, 0xC3EFFDFF, 0x40603DDF, 0x0000FFC3,
+0xFFFDDFEC, 0xC3FFFDFF, 0x00803DCF, 0x0000FFC3,
+0x00000000, 0x00000000, 0x00000000, 0x00000000,
+0xFFFFFFFE, 0x07FF7FFF, 0x03FF7FFF, 0x00000000,
+0xFEF02596, 0x3BFF6CAE, 0x03FF3F5F, 0x00000000,
+0x03000000, 0xC2A003FF, 0xFFFFFEFF, 0xFFFE03FF,
+0xFEBF0FDF, 0x02FE3FFF, 0x00000000, 0x00000000,
+0x00000000, 0x00000000, 0x00000000, 0x00000000,
+0x00000000, 0x00000000, 0x1FFF0000, 0x00000002,
+0x000000A0, 0x003EFFFE, 0xFFFFFFFE, 0xFFFFFFFF,
+0x661FFFFF, 0xFFFFFFFE, 0xFFFFFFFF, 0x77FFFFFF,
+};
+static const unsigned char nmstrtPages[] = {
+0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x00,
+0x00, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
+0x10, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x13,
+0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x15, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x17,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x18,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+static const unsigned char namePages[] = {
+0x19, 0x03, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x00,
+0x00, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25,
+0x10, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x13,
+0x26, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x27, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x17,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x18,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
diff --git a/src/expat/xmltok/utf8tab.h b/src/expat/xmltok/utf8tab.h
new file mode 100755
index 0000000..28d9b59
--- /dev/null
+++ b/src/expat/xmltok/utf8tab.h
@@ -0,0 +1,38 @@
+/*
+Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd
+See the file copying.txt for copying permission.
+*/
+
+
+/* 0x80 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
+/* 0x84 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
+/* 0x88 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
+/* 0x8C */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
+/* 0x90 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
+/* 0x94 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
+/* 0x98 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
+/* 0x9C */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
+/* 0xA0 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
+/* 0xA4 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
+/* 0xA8 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
+/* 0xAC */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
+/* 0xB0 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
+/* 0xB4 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
+/* 0xB8 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
+/* 0xBC */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
+/* 0xC0 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2,
+/* 0xC4 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2,
+/* 0xC8 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2,
+/* 0xCC */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2,
+/* 0xD0 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2,
+/* 0xD4 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2,
+/* 0xD8 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2,
+/* 0xDC */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2,
+/* 0xE0 */ BT_LEAD3, BT_LEAD3, BT_LEAD3, BT_LEAD3,
+/* 0xE4 */ BT_LEAD3, BT_LEAD3, BT_LEAD3, BT_LEAD3,
+/* 0xE8 */ BT_LEAD3, BT_LEAD3, BT_LEAD3, BT_LEAD3,
+/* 0xEC */ BT_LEAD3, BT_LEAD3, BT_LEAD3, BT_LEAD3,
+/* 0xF0 */ BT_LEAD4, BT_LEAD4, BT_LEAD4, BT_LEAD4,
+/* 0xF4 */ BT_LEAD4, BT_NONXML, BT_NONXML, BT_NONXML,
+/* 0xF8 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML,
+/* 0xFC */ BT_NONXML, BT_NONXML, BT_MALFORM, BT_MALFORM,
diff --git a/src/expat/xmltok/xmldef.h b/src/expat/xmltok/xmldef.h
new file mode 100755
index 0000000..57b8333
--- /dev/null
+++ b/src/expat/xmltok/xmldef.h
@@ -0,0 +1,52 @@
+/*
+Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd
+See the file copying.txt for copying permission.
+*/
+
+#include <string.h>
+
+#ifdef XML_WINLIB
+
+#define WIN32_LEAN_AND_MEAN
+#define STRICT
+#include <windows.h>
+
+#define malloc(x) HeapAlloc(GetProcessHeap(), 0, (x))
+#define calloc(x, y) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (x)*(y))
+#define free(x) HeapFree(GetProcessHeap(), 0, (x))
+#define realloc(x, y) HeapReAlloc(GetProcessHeap(), 0, x, y)
+#define abort() /* as nothing */
+
+#else /* not XML_WINLIB */
+
+#include <stdlib.h>
+
+#endif /* not XML_WINLIB */
+
+/* This file can be used for any definitions needed in
+particular environments. */
+
+/* Mozilla specific defines */
+
+#ifdef MOZILLA_CLIENT
+
+#include "nspr.h"
+#define malloc(x) PR_Malloc((size_t)(x))
+#define realloc(x, y) PR_Realloc((x), (size_t)(y))
+#define calloc(x, y) PR_Calloc((x),(y))
+#define free(x) PR_Free(x)
+#if PR_BYTES_PER_INT != 4
+#define int int32
+#endif
+
+/* Enable Unicode string processing in expat. */
+#ifndef XML_UNICODE
+#define XML_UNICODE
+#endif
+
+/* Enable external parameter entity parsing in expat */
+#ifndef XML_DTD
+#define XML_DTD 1
+#endif
+
+#endif /* MOZILLA_CLIENT */
diff --git a/src/expat/xmltok/xmlrole.c b/src/expat/xmltok/xmlrole.c
new file mode 100755
index 0000000..8eaba3a
--- /dev/null
+++ b/src/expat/xmltok/xmlrole.c
@@ -0,0 +1,1265 @@
+/*
+Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd
+See the file copying.txt for copying permission.
+*/
+
+#include "xmldef.h"
+#include "xmlrole.h"
+#include "ascii.h"
+
+/* Doesn't check:
+
+ that ,| are not mixed in a model group
+ content of literals
+
+*/
+
+static const char KW_ANY[] = { ASCII_A, ASCII_N, ASCII_Y, '\0' };
+static const char KW_ATTLIST[] = { ASCII_A, ASCII_T, ASCII_T, ASCII_L, ASCII_I, ASCII_S, ASCII_T, '\0' };
+static const char KW_CDATA[] = { ASCII_C, ASCII_D, ASCII_A, ASCII_T, ASCII_A, '\0' };
+static const char KW_DOCTYPE[] = { ASCII_D, ASCII_O, ASCII_C, ASCII_T, ASCII_Y, ASCII_P, ASCII_E, '\0' };
+static const char KW_ELEMENT[] = { ASCII_E, ASCII_L, ASCII_E, ASCII_M, ASCII_E, ASCII_N, ASCII_T, '\0' };
+static const char KW_EMPTY[] = { ASCII_E, ASCII_M, ASCII_P, ASCII_T, ASCII_Y, '\0' };
+static const char KW_ENTITIES[] = { ASCII_E, ASCII_N, ASCII_T, ASCII_I, ASCII_T, ASCII_I, ASCII_E, ASCII_S, '\0' };
+static const char KW_ENTITY[] = { ASCII_E, ASCII_N, ASCII_T, ASCII_I, ASCII_T, ASCII_Y, '\0' };
+static const char KW_FIXED[] = { ASCII_F, ASCII_I, ASCII_X, ASCII_E, ASCII_D, '\0' };
+static const char KW_ID[] = { ASCII_I, ASCII_D, '\0' };
+static const char KW_IDREF[] = { ASCII_I, ASCII_D, ASCII_R, ASCII_E, ASCII_F, '\0' };
+static const char KW_IDREFS[] = { ASCII_I, ASCII_D, ASCII_R, ASCII_E, ASCII_F, ASCII_S, '\0' };
+static const char KW_IGNORE[] = { ASCII_I, ASCII_G, ASCII_N, ASCII_O, ASCII_R, ASCII_E, '\0' };
+static const char KW_IMPLIED[] = { ASCII_I, ASCII_M, ASCII_P, ASCII_L, ASCII_I, ASCII_E, ASCII_D, '\0' };
+static const char KW_INCLUDE[] = { ASCII_I, ASCII_N, ASCII_C, ASCII_L, ASCII_U, ASCII_D, ASCII_E, '\0' };
+static const char KW_NDATA[] = { ASCII_N, ASCII_D, ASCII_A, ASCII_T, ASCII_A, '\0' };
+static const char KW_NMTOKEN[] = { ASCII_N, ASCII_M, ASCII_T, ASCII_O, ASCII_K, ASCII_E, ASCII_N, '\0' };
+static const char KW_NMTOKENS[] = { ASCII_N, ASCII_M, ASCII_T, ASCII_O, ASCII_K, ASCII_E, ASCII_N, ASCII_S, '\0' };
+static const char KW_NOTATION[] = { ASCII_N, ASCII_O, ASCII_T, ASCII_A, ASCII_T, ASCII_I, ASCII_O, ASCII_N, '\0' };
+static const char KW_PCDATA[] = { ASCII_P, ASCII_C, ASCII_D, ASCII_A, ASCII_T, ASCII_A, '\0' };
+static const char KW_PUBLIC[] = { ASCII_P, ASCII_U, ASCII_B, ASCII_L, ASCII_I, ASCII_C, '\0' };
+static const char KW_REQUIRED[] = { ASCII_R, ASCII_E, ASCII_Q, ASCII_U, ASCII_I, ASCII_R, ASCII_E, ASCII_D, '\0' };
+static const char KW_SYSTEM[] = { ASCII_S, ASCII_Y, ASCII_S, ASCII_T, ASCII_E, ASCII_M, '\0' };
+
+#ifndef MIN_BYTES_PER_CHAR
+#define MIN_BYTES_PER_CHAR(enc) ((enc)->minBytesPerChar)
+#endif
+
+#ifdef XML_DTD
+#define setTopLevel(state) \
+ ((state)->handler = ((state)->documentEntity \
+ ? internalSubset \
+ : externalSubset1))
+#else /* not XML_DTD */
+#define setTopLevel(state) ((state)->handler = internalSubset)
+#endif /* not XML_DTD */
+
+typedef int PROLOG_HANDLER(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc);
+
+static PROLOG_HANDLER
+ prolog0, prolog1, prolog2,
+ doctype0, doctype1, doctype2, doctype3, doctype4, doctype5,
+ internalSubset,
+ entity0, entity1, entity2, entity3, entity4, entity5, entity6,
+ entity7, entity8, entity9,
+ notation0, notation1, notation2, notation3, notation4,
+ attlist0, attlist1, attlist2, attlist3, attlist4, attlist5, attlist6,
+ attlist7, attlist8, attlist9,
+ element0, element1, element2, element3, element4, element5, element6,
+ element7,
+#ifdef XML_DTD
+ externalSubset0, externalSubset1,
+ condSect0, condSect1, condSect2,
+#endif /* XML_DTD */
+ declClose,
+ error;
+
+static
+int common(PROLOG_STATE *state, int tok);
+
+static
+int prolog0(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ state->handler = prolog1;
+ return XML_ROLE_NONE;
+ case XML_TOK_XML_DECL:
+ state->handler = prolog1;
+ return XML_ROLE_XML_DECL;
+ case XML_TOK_PI:
+ state->handler = prolog1;
+ return XML_ROLE_NONE;
+ case XML_TOK_COMMENT:
+ state->handler = prolog1;
+ case XML_TOK_BOM:
+ return XML_ROLE_NONE;
+ case XML_TOK_DECL_OPEN:
+ if (!XmlNameMatchesAscii(enc,
+ ptr + 2 * MIN_BYTES_PER_CHAR(enc),
+ end,
+ KW_DOCTYPE))
+ break;
+ state->handler = doctype0;
+ return XML_ROLE_NONE;
+ case XML_TOK_INSTANCE_START:
+ state->handler = error;
+ return XML_ROLE_INSTANCE_START;
+ }
+ return common(state, tok);
+}
+
+static
+int prolog1(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_NONE;
+ case XML_TOK_PI:
+ case XML_TOK_COMMENT:
+ case XML_TOK_BOM:
+ return XML_ROLE_NONE;
+ case XML_TOK_DECL_OPEN:
+ if (!XmlNameMatchesAscii(enc,
+ ptr + 2 * MIN_BYTES_PER_CHAR(enc),
+ end,
+ KW_DOCTYPE))
+ break;
+ state->handler = doctype0;
+ return XML_ROLE_NONE;
+ case XML_TOK_INSTANCE_START:
+ state->handler = error;
+ return XML_ROLE_INSTANCE_START;
+ }
+ return common(state, tok);
+}
+
+static
+int prolog2(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_NONE;
+ case XML_TOK_PI:
+ case XML_TOK_COMMENT:
+ return XML_ROLE_NONE;
+ case XML_TOK_INSTANCE_START:
+ state->handler = error;
+ return XML_ROLE_INSTANCE_START;
+ }
+ return common(state, tok);
+}
+
+static
+int doctype0(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_NONE;
+ case XML_TOK_NAME:
+ case XML_TOK_PREFIXED_NAME:
+ state->handler = doctype1;
+ return XML_ROLE_DOCTYPE_NAME;
+ }
+ return common(state, tok);
+}
+
+static
+int doctype1(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_NONE;
+ case XML_TOK_OPEN_BRACKET:
+ state->handler = internalSubset;
+ return XML_ROLE_NONE;
+ case XML_TOK_DECL_CLOSE:
+ state->handler = prolog2;
+ return XML_ROLE_DOCTYPE_CLOSE;
+ case XML_TOK_NAME:
+ if (XmlNameMatchesAscii(enc, ptr, end, KW_SYSTEM)) {
+ state->handler = doctype3;
+ return XML_ROLE_NONE;
+ }
+ if (XmlNameMatchesAscii(enc, ptr, end, KW_PUBLIC)) {
+ state->handler = doctype2;
+ return XML_ROLE_NONE;
+ }
+ break;
+ }
+ return common(state, tok);
+}
+
+static
+int doctype2(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_NONE;
+ case XML_TOK_LITERAL:
+ state->handler = doctype3;
+ return XML_ROLE_DOCTYPE_PUBLIC_ID;
+ }
+ return common(state, tok);
+}
+
+static
+int doctype3(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_NONE;
+ case XML_TOK_LITERAL:
+ state->handler = doctype4;
+ return XML_ROLE_DOCTYPE_SYSTEM_ID;
+ }
+ return common(state, tok);
+}
+
+static
+int doctype4(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_NONE;
+ case XML_TOK_OPEN_BRACKET:
+ state->handler = internalSubset;
+ return XML_ROLE_NONE;
+ case XML_TOK_DECL_CLOSE:
+ state->handler = prolog2;
+ return XML_ROLE_DOCTYPE_CLOSE;
+ }
+ return common(state, tok);
+}
+
+static
+int doctype5(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_NONE;
+ case XML_TOK_DECL_CLOSE:
+ state->handler = prolog2;
+ return XML_ROLE_DOCTYPE_CLOSE;
+ }
+ return common(state, tok);
+}
+
+static
+int internalSubset(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_NONE;
+ case XML_TOK_DECL_OPEN:
+ if (XmlNameMatchesAscii(enc,
+ ptr + 2 * MIN_BYTES_PER_CHAR(enc),
+ end,
+ KW_ENTITY)) {
+ state->handler = entity0;
+ return XML_ROLE_NONE;
+ }
+ if (XmlNameMatchesAscii(enc,
+ ptr + 2 * MIN_BYTES_PER_CHAR(enc),
+ end,
+ KW_ATTLIST)) {
+ state->handler = attlist0;
+ return XML_ROLE_NONE;
+ }
+ if (XmlNameMatchesAscii(enc,
+ ptr + 2 * MIN_BYTES_PER_CHAR(enc),
+ end,
+ KW_ELEMENT)) {
+ state->handler = element0;
+ return XML_ROLE_NONE;
+ }
+ if (XmlNameMatchesAscii(enc,
+ ptr + 2 * MIN_BYTES_PER_CHAR(enc),
+ end,
+ KW_NOTATION)) {
+ state->handler = notation0;
+ return XML_ROLE_NONE;
+ }
+ break;
+ case XML_TOK_PI:
+ case XML_TOK_COMMENT:
+ return XML_ROLE_NONE;
+ case XML_TOK_PARAM_ENTITY_REF:
+ return XML_ROLE_PARAM_ENTITY_REF;
+ case XML_TOK_CLOSE_BRACKET:
+ state->handler = doctype5;
+ return XML_ROLE_NONE;
+ }
+ return common(state, tok);
+}
+
+#ifdef XML_DTD
+
+static
+int externalSubset0(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ state->handler = externalSubset1;
+ if (tok == XML_TOK_XML_DECL)
+ return XML_ROLE_TEXT_DECL;
+ return externalSubset1(state, tok, ptr, end, enc);
+}
+
+static
+int externalSubset1(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_COND_SECT_OPEN:
+ state->handler = condSect0;
+ return XML_ROLE_NONE;
+ case XML_TOK_COND_SECT_CLOSE:
+ if (state->includeLevel == 0)
+ break;
+ state->includeLevel -= 1;
+ return XML_ROLE_NONE;
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_NONE;
+ case XML_TOK_CLOSE_BRACKET:
+ break;
+ case XML_TOK_NONE:
+ if (state->includeLevel)
+ break;
+ return XML_ROLE_NONE;
+ default:
+ return internalSubset(state, tok, ptr, end, enc);
+ }
+ return common(state, tok);
+}
+
+#endif /* XML_DTD */
+
+static
+int entity0(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_NONE;
+ case XML_TOK_PERCENT:
+ state->handler = entity1;
+ return XML_ROLE_NONE;
+ case XML_TOK_NAME:
+ state->handler = entity2;
+ return XML_ROLE_GENERAL_ENTITY_NAME;
+ }
+ return common(state, tok);
+}
+
+static
+int entity1(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_NONE;
+ case XML_TOK_NAME:
+ state->handler = entity7;
+ return XML_ROLE_PARAM_ENTITY_NAME;
+ }
+ return common(state, tok);
+}
+
+static
+int entity2(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_NONE;
+ case XML_TOK_NAME:
+ if (XmlNameMatchesAscii(enc, ptr, end, KW_SYSTEM)) {
+ state->handler = entity4;
+ return XML_ROLE_NONE;
+ }
+ if (XmlNameMatchesAscii(enc, ptr, end, KW_PUBLIC)) {
+ state->handler = entity3;
+ return XML_ROLE_NONE;
+ }
+ break;
+ case XML_TOK_LITERAL:
+ state->handler = declClose;
+ return XML_ROLE_ENTITY_VALUE;
+ }
+ return common(state, tok);
+}
+
+static
+int entity3(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_NONE;
+ case XML_TOK_LITERAL:
+ state->handler = entity4;
+ return XML_ROLE_ENTITY_PUBLIC_ID;
+ }
+ return common(state, tok);
+}
+
+
+static
+int entity4(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_NONE;
+ case XML_TOK_LITERAL:
+ state->handler = entity5;
+ return XML_ROLE_ENTITY_SYSTEM_ID;
+ }
+ return common(state, tok);
+}
+
+static
+int entity5(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_NONE;
+ case XML_TOK_DECL_CLOSE:
+ setTopLevel(state);
+ return XML_ROLE_EXTERNAL_GENERAL_ENTITY_NO_NOTATION;
+ case XML_TOK_NAME:
+ if (XmlNameMatchesAscii(enc, ptr, end, KW_NDATA)) {
+ state->handler = entity6;
+ return XML_ROLE_NONE;
+ }
+ break;
+ }
+ return common(state, tok);
+}
+
+static
+int entity6(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_NONE;
+ case XML_TOK_NAME:
+ state->handler = declClose;
+ return XML_ROLE_ENTITY_NOTATION_NAME;
+ }
+ return common(state, tok);
+}
+
+static
+int entity7(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_NONE;
+ case XML_TOK_NAME:
+ if (XmlNameMatchesAscii(enc, ptr, end, KW_SYSTEM)) {
+ state->handler = entity9;
+ return XML_ROLE_NONE;
+ }
+ if (XmlNameMatchesAscii(enc, ptr, end, KW_PUBLIC)) {
+ state->handler = entity8;
+ return XML_ROLE_NONE;
+ }
+ break;
+ case XML_TOK_LITERAL:
+ state->handler = declClose;
+ return XML_ROLE_ENTITY_VALUE;
+ }
+ return common(state, tok);
+}
+
+static
+int entity8(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_NONE;
+ case XML_TOK_LITERAL:
+ state->handler = entity9;
+ return XML_ROLE_ENTITY_PUBLIC_ID;
+ }
+ return common(state, tok);
+}
+
+static
+int entity9(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_NONE;
+ case XML_TOK_LITERAL:
+ state->handler = declClose;
+ return XML_ROLE_ENTITY_SYSTEM_ID;
+ }
+ return common(state, tok);
+}
+
+static
+int notation0(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_NONE;
+ case XML_TOK_NAME:
+ state->handler = notation1;
+ return XML_ROLE_NOTATION_NAME;
+ }
+ return common(state, tok);
+}
+
+static
+int notation1(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_NONE;
+ case XML_TOK_NAME:
+ if (XmlNameMatchesAscii(enc, ptr, end, KW_SYSTEM)) {
+ state->handler = notation3;
+ return XML_ROLE_NONE;
+ }
+ if (XmlNameMatchesAscii(enc, ptr, end, KW_PUBLIC)) {
+ state->handler = notation2;
+ return XML_ROLE_NONE;
+ }
+ break;
+ }
+ return common(state, tok);
+}
+
+static
+int notation2(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_NONE;
+ case XML_TOK_LITERAL:
+ state->handler = notation4;
+ return XML_ROLE_NOTATION_PUBLIC_ID;
+ }
+ return common(state, tok);
+}
+
+static
+int notation3(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_NONE;
+ case XML_TOK_LITERAL:
+ state->handler = declClose;
+ return XML_ROLE_NOTATION_SYSTEM_ID;
+ }
+ return common(state, tok);
+}
+
+static
+int notation4(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_NONE;
+ case XML_TOK_LITERAL:
+ state->handler = declClose;
+ return XML_ROLE_NOTATION_SYSTEM_ID;
+ case XML_TOK_DECL_CLOSE:
+ setTopLevel(state);
+ return XML_ROLE_NOTATION_NO_SYSTEM_ID;
+ }
+ return common(state, tok);
+}
+
+static
+int attlist0(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_NONE;
+ case XML_TOK_NAME:
+ case XML_TOK_PREFIXED_NAME:
+ state->handler = attlist1;
+ return XML_ROLE_ATTLIST_ELEMENT_NAME;
+ }
+ return common(state, tok);
+}
+
+static
+int attlist1(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_NONE;
+ case XML_TOK_DECL_CLOSE:
+ setTopLevel(state);
+ return XML_ROLE_NONE;
+ case XML_TOK_NAME:
+ case XML_TOK_PREFIXED_NAME:
+ state->handler = attlist2;
+ return XML_ROLE_ATTRIBUTE_NAME;
+ }
+ return common(state, tok);
+}
+
+static
+int attlist2(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_NONE;
+ case XML_TOK_NAME:
+ {
+ static const char *types[] = {
+ KW_CDATA,
+ KW_ID,
+ KW_IDREF,
+ KW_IDREFS,
+ KW_ENTITY,
+ KW_ENTITIES,
+ KW_NMTOKEN,
+ KW_NMTOKENS,
+ };
+ int i;
+ for (i = 0; i < (int)(sizeof(types)/sizeof(types[0])); i++)
+ if (XmlNameMatchesAscii(enc, ptr, end, types[i])) {
+ state->handler = attlist8;
+ return XML_ROLE_ATTRIBUTE_TYPE_CDATA + i;
+ }
+ }
+ if (XmlNameMatchesAscii(enc, ptr, end, KW_NOTATION)) {
+ state->handler = attlist5;
+ return XML_ROLE_NONE;
+ }
+ break;
+ case XML_TOK_OPEN_PAREN:
+ state->handler = attlist3;
+ return XML_ROLE_NONE;
+ }
+ return common(state, tok);
+}
+
+static
+int attlist3(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_NONE;
+ case XML_TOK_NMTOKEN:
+ case XML_TOK_NAME:
+ case XML_TOK_PREFIXED_NAME:
+ state->handler = attlist4;
+ return XML_ROLE_ATTRIBUTE_ENUM_VALUE;
+ }
+ return common(state, tok);
+}
+
+static
+int attlist4(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_NONE;
+ case XML_TOK_CLOSE_PAREN:
+ state->handler = attlist8;
+ return XML_ROLE_NONE;
+ case XML_TOK_OR:
+ state->handler = attlist3;
+ return XML_ROLE_NONE;
+ }
+ return common(state, tok);
+}
+
+static
+int attlist5(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_NONE;
+ case XML_TOK_OPEN_PAREN:
+ state->handler = attlist6;
+ return XML_ROLE_NONE;
+ }
+ return common(state, tok);
+}
+
+
+static
+int attlist6(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_NONE;
+ case XML_TOK_NAME:
+ state->handler = attlist7;
+ return XML_ROLE_ATTRIBUTE_NOTATION_VALUE;
+ }
+ return common(state, tok);
+}
+
+static
+int attlist7(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_NONE;
+ case XML_TOK_CLOSE_PAREN:
+ state->handler = attlist8;
+ return XML_ROLE_NONE;
+ case XML_TOK_OR:
+ state->handler = attlist6;
+ return XML_ROLE_NONE;
+ }
+ return common(state, tok);
+}
+
+/* default value */
+static
+int attlist8(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_NONE;
+ case XML_TOK_POUND_NAME:
+ if (XmlNameMatchesAscii(enc,
+ ptr + MIN_BYTES_PER_CHAR(enc),
+ end,
+ KW_IMPLIED)) {
+ state->handler = attlist1;
+ return XML_ROLE_IMPLIED_ATTRIBUTE_VALUE;
+ }
+ if (XmlNameMatchesAscii(enc,
+ ptr + MIN_BYTES_PER_CHAR(enc),
+ end,
+ KW_REQUIRED)) {
+ state->handler = attlist1;
+ return XML_ROLE_REQUIRED_ATTRIBUTE_VALUE;
+ }
+ if (XmlNameMatchesAscii(enc,
+ ptr + MIN_BYTES_PER_CHAR(enc),
+ end,
+ KW_FIXED)) {
+ state->handler = attlist9;
+ return XML_ROLE_NONE;
+ }
+ break;
+ case XML_TOK_LITERAL:
+ state->handler = attlist1;
+ return XML_ROLE_DEFAULT_ATTRIBUTE_VALUE;
+ }
+ return common(state, tok);
+}
+
+static
+int attlist9(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_NONE;
+ case XML_TOK_LITERAL:
+ state->handler = attlist1;
+ return XML_ROLE_FIXED_ATTRIBUTE_VALUE;
+ }
+ return common(state, tok);
+}
+
+static
+int element0(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_NONE;
+ case XML_TOK_NAME:
+ case XML_TOK_PREFIXED_NAME:
+ state->handler = element1;
+ return XML_ROLE_ELEMENT_NAME;
+ }
+ return common(state, tok);
+}
+
+static
+int element1(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_NONE;
+ case XML_TOK_NAME:
+ if (XmlNameMatchesAscii(enc, ptr, end, KW_EMPTY)) {
+ state->handler = declClose;
+ return XML_ROLE_CONTENT_EMPTY;
+ }
+ if (XmlNameMatchesAscii(enc, ptr, end, KW_ANY)) {
+ state->handler = declClose;
+ return XML_ROLE_CONTENT_ANY;
+ }
+ break;
+ case XML_TOK_OPEN_PAREN:
+ state->handler = element2;
+ state->level = 1;
+ return XML_ROLE_GROUP_OPEN;
+ }
+ return common(state, tok);
+}
+
+static
+int element2(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_NONE;
+ case XML_TOK_POUND_NAME:
+ if (XmlNameMatchesAscii(enc,
+ ptr + MIN_BYTES_PER_CHAR(enc),
+ end,
+ KW_PCDATA)) {
+ state->handler = element3;
+ return XML_ROLE_CONTENT_PCDATA;
+ }
+ break;
+ case XML_TOK_OPEN_PAREN:
+ state->level = 2;
+ state->handler = element6;
+ return XML_ROLE_GROUP_OPEN;
+ case XML_TOK_NAME:
+ case XML_TOK_PREFIXED_NAME:
+ state->handler = element7;
+ return XML_ROLE_CONTENT_ELEMENT;
+ case XML_TOK_NAME_QUESTION:
+ state->handler = element7;
+ return XML_ROLE_CONTENT_ELEMENT_OPT;
+ case XML_TOK_NAME_ASTERISK:
+ state->handler = element7;
+ return XML_ROLE_CONTENT_ELEMENT_REP;
+ case XML_TOK_NAME_PLUS:
+ state->handler = element7;
+ return XML_ROLE_CONTENT_ELEMENT_PLUS;
+ }
+ return common(state, tok);
+}
+
+static
+int element3(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_NONE;
+ case XML_TOK_CLOSE_PAREN:
+ case XML_TOK_CLOSE_PAREN_ASTERISK:
+ state->handler = declClose;
+ return XML_ROLE_GROUP_CLOSE_REP;
+ case XML_TOK_OR:
+ state->handler = element4;
+ return XML_ROLE_NONE;
+ }
+ return common(state, tok);
+}
+
+static
+int element4(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_NONE;
+ case XML_TOK_NAME:
+ case XML_TOK_PREFIXED_NAME:
+ state->handler = element5;
+ return XML_ROLE_CONTENT_ELEMENT;
+ }
+ return common(state, tok);
+}
+
+static
+int element5(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_NONE;
+ case XML_TOK_CLOSE_PAREN_ASTERISK:
+ state->handler = declClose;
+ return XML_ROLE_GROUP_CLOSE_REP;
+ case XML_TOK_OR:
+ state->handler = element4;
+ return XML_ROLE_NONE;
+ }
+ return common(state, tok);
+}
+
+static
+int element6(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_NONE;
+ case XML_TOK_OPEN_PAREN:
+ state->level += 1;
+ return XML_ROLE_GROUP_OPEN;
+ case XML_TOK_NAME:
+ case XML_TOK_PREFIXED_NAME:
+ state->handler = element7;
+ return XML_ROLE_CONTENT_ELEMENT;
+ case XML_TOK_NAME_QUESTION:
+ state->handler = element7;
+ return XML_ROLE_CONTENT_ELEMENT_OPT;
+ case XML_TOK_NAME_ASTERISK:
+ state->handler = element7;
+ return XML_ROLE_CONTENT_ELEMENT_REP;
+ case XML_TOK_NAME_PLUS:
+ state->handler = element7;
+ return XML_ROLE_CONTENT_ELEMENT_PLUS;
+ }
+ return common(state, tok);
+}
+
+static
+int element7(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_NONE;
+ case XML_TOK_CLOSE_PAREN:
+ state->level -= 1;
+ if (state->level == 0)
+ state->handler = declClose;
+ return XML_ROLE_GROUP_CLOSE;
+ case XML_TOK_CLOSE_PAREN_ASTERISK:
+ state->level -= 1;
+ if (state->level == 0)
+ state->handler = declClose;
+ return XML_ROLE_GROUP_CLOSE_REP;
+ case XML_TOK_CLOSE_PAREN_QUESTION:
+ state->level -= 1;
+ if (state->level == 0)
+ state->handler = declClose;
+ return XML_ROLE_GROUP_CLOSE_OPT;
+ case XML_TOK_CLOSE_PAREN_PLUS:
+ state->level -= 1;
+ if (state->level == 0)
+ state->handler = declClose;
+ return XML_ROLE_GROUP_CLOSE_PLUS;
+ case XML_TOK_COMMA:
+ state->handler = element6;
+ return XML_ROLE_GROUP_SEQUENCE;
+ case XML_TOK_OR:
+ state->handler = element6;
+ return XML_ROLE_GROUP_CHOICE;
+ }
+ return common(state, tok);
+}
+
+#ifdef XML_DTD
+
+static
+int condSect0(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_NONE;
+ case XML_TOK_NAME:
+ if (XmlNameMatchesAscii(enc, ptr, end, KW_INCLUDE)) {
+ state->handler = condSect1;
+ return XML_ROLE_NONE;
+ }
+ if (XmlNameMatchesAscii(enc, ptr, end, KW_IGNORE)) {
+ state->handler = condSect2;
+ return XML_ROLE_NONE;
+ }
+ break;
+ }
+ return common(state, tok);
+}
+
+static
+int condSect1(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_NONE;
+ case XML_TOK_OPEN_BRACKET:
+ state->handler = externalSubset1;
+ state->includeLevel += 1;
+ return XML_ROLE_NONE;
+ }
+ return common(state, tok);
+}
+
+static
+int condSect2(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_NONE;
+ case XML_TOK_OPEN_BRACKET:
+ state->handler = externalSubset1;
+ return XML_ROLE_IGNORE_SECT;
+ }
+ return common(state, tok);
+}
+
+#endif /* XML_DTD */
+
+static
+int declClose(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_NONE;
+ case XML_TOK_DECL_CLOSE:
+ setTopLevel(state);
+ return XML_ROLE_NONE;
+ }
+ return common(state, tok);
+}
+
+#if 0
+
+static
+int ignore(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_DECL_CLOSE:
+ state->handler = internalSubset;
+ return 0;
+ default:
+ return XML_ROLE_NONE;
+ }
+ return common(state, tok);
+}
+#endif
+
+static
+int error(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ return XML_ROLE_NONE;
+}
+
+static
+int common(PROLOG_STATE *state, int tok)
+{
+#ifdef XML_DTD
+ if (!state->documentEntity && tok == XML_TOK_PARAM_ENTITY_REF)
+ return XML_ROLE_INNER_PARAM_ENTITY_REF;
+#endif
+ state->handler = error;
+ return XML_ROLE_ERROR;
+}
+
+void XmlPrologStateInit(PROLOG_STATE *state)
+{
+ state->handler = prolog0;
+#ifdef XML_DTD
+ state->documentEntity = 1;
+ state->includeLevel = 0;
+#endif /* XML_DTD */
+}
+
+#ifdef XML_DTD
+
+void XmlPrologStateInitExternalEntity(PROLOG_STATE *state)
+{
+ state->handler = externalSubset0;
+ state->documentEntity = 0;
+ state->includeLevel = 0;
+}
+
+#endif /* XML_DTD */
diff --git a/src/expat/xmltok/xmlrole.h b/src/expat/xmltok/xmlrole.h
new file mode 100755
index 0000000..22958df
--- /dev/null
+++ b/src/expat/xmltok/xmlrole.h
@@ -0,0 +1,99 @@
+/*
+Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd
+See the file copying.txt for copying permission.
+*/
+
+#ifndef XmlRole_INCLUDED
+#define XmlRole_INCLUDED 1
+
+#include "xmltok.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum {
+ XML_ROLE_ERROR = -1,
+ XML_ROLE_NONE = 0,
+ XML_ROLE_XML_DECL,
+ XML_ROLE_INSTANCE_START,
+ XML_ROLE_DOCTYPE_NAME,
+ XML_ROLE_DOCTYPE_SYSTEM_ID,
+ XML_ROLE_DOCTYPE_PUBLIC_ID,
+ XML_ROLE_DOCTYPE_CLOSE,
+ XML_ROLE_GENERAL_ENTITY_NAME,
+ XML_ROLE_PARAM_ENTITY_NAME,
+ XML_ROLE_ENTITY_VALUE,
+ XML_ROLE_ENTITY_SYSTEM_ID,
+ XML_ROLE_ENTITY_PUBLIC_ID,
+ XML_ROLE_ENTITY_NOTATION_NAME,
+ XML_ROLE_NOTATION_NAME,
+ XML_ROLE_NOTATION_SYSTEM_ID,
+ XML_ROLE_NOTATION_NO_SYSTEM_ID,
+ XML_ROLE_NOTATION_PUBLIC_ID,
+ XML_ROLE_ATTRIBUTE_NAME,
+ XML_ROLE_ATTRIBUTE_TYPE_CDATA,
+ XML_ROLE_ATTRIBUTE_TYPE_ID,
+ XML_ROLE_ATTRIBUTE_TYPE_IDREF,
+ XML_ROLE_ATTRIBUTE_TYPE_IDREFS,
+ XML_ROLE_ATTRIBUTE_TYPE_ENTITY,
+ XML_ROLE_ATTRIBUTE_TYPE_ENTITIES,
+ XML_ROLE_ATTRIBUTE_TYPE_NMTOKEN,
+ XML_ROLE_ATTRIBUTE_TYPE_NMTOKENS,
+ XML_ROLE_ATTRIBUTE_ENUM_VALUE,
+ XML_ROLE_ATTRIBUTE_NOTATION_VALUE,
+ XML_ROLE_ATTLIST_ELEMENT_NAME,
+ XML_ROLE_IMPLIED_ATTRIBUTE_VALUE,
+ XML_ROLE_REQUIRED_ATTRIBUTE_VALUE,
+ XML_ROLE_DEFAULT_ATTRIBUTE_VALUE,
+ XML_ROLE_FIXED_ATTRIBUTE_VALUE,
+ XML_ROLE_ELEMENT_NAME,
+ XML_ROLE_CONTENT_ANY,
+ XML_ROLE_CONTENT_EMPTY,
+ XML_ROLE_CONTENT_PCDATA,
+ XML_ROLE_GROUP_OPEN,
+ XML_ROLE_GROUP_CLOSE,
+ XML_ROLE_GROUP_CLOSE_REP,
+ XML_ROLE_GROUP_CLOSE_OPT,
+ XML_ROLE_GROUP_CLOSE_PLUS,
+ XML_ROLE_GROUP_CHOICE,
+ XML_ROLE_GROUP_SEQUENCE,
+ XML_ROLE_CONTENT_ELEMENT,
+ XML_ROLE_CONTENT_ELEMENT_REP,
+ XML_ROLE_CONTENT_ELEMENT_OPT,
+ XML_ROLE_CONTENT_ELEMENT_PLUS,
+#ifdef XML_DTD
+ XML_ROLE_TEXT_DECL,
+ XML_ROLE_IGNORE_SECT,
+ XML_ROLE_INNER_PARAM_ENTITY_REF,
+#endif /* XML_DTD */
+ XML_ROLE_PARAM_ENTITY_REF,
+ XML_ROLE_EXTERNAL_GENERAL_ENTITY_NO_NOTATION
+};
+
+typedef struct prolog_state {
+ int (*handler)(struct prolog_state *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc);
+ unsigned level;
+#ifdef XML_DTD
+ unsigned includeLevel;
+ int documentEntity;
+#endif /* XML_DTD */
+} PROLOG_STATE;
+
+void XMLTOKAPI XmlPrologStateInit(PROLOG_STATE *);
+#ifdef XML_DTD
+void XMLTOKAPI XmlPrologStateInitExternalEntity(PROLOG_STATE *);
+#endif /* XML_DTD */
+
+#define XmlTokenRole(state, tok, ptr, end, enc) \
+ (((state)->handler)(state, tok, ptr, end, enc))
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* not XmlRole_INCLUDED */
diff --git a/src/expat/xmltok/xmltok.c b/src/expat/xmltok/xmltok.c
new file mode 100755
index 0000000..da2a6fb
--- /dev/null
+++ b/src/expat/xmltok/xmltok.c
@@ -0,0 +1,1558 @@
+/*
+Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd
+See the file copying.txt for copying permission.
+*/
+
+#include "xmldef.h"
+#include "xmltok.h"
+#include "nametab.h"
+
+#ifdef XML_DTD
+#define IGNORE_SECTION_TOK_VTABLE , PREFIX(ignoreSectionTok)
+#else
+#define IGNORE_SECTION_TOK_VTABLE /* as nothing */
+#endif
+
+#define VTABLE1 \
+ { PREFIX(prologTok), PREFIX(contentTok), \
+ PREFIX(cdataSectionTok) IGNORE_SECTION_TOK_VTABLE }, \
+ { PREFIX(attributeValueTok), PREFIX(entityValueTok) }, \
+ PREFIX(sameName), \
+ PREFIX(nameMatchesAscii), \
+ PREFIX(nameLength), \
+ PREFIX(skipS), \
+ PREFIX(getAtts), \
+ PREFIX(charRefNumber), \
+ PREFIX(predefinedEntityName), \
+ PREFIX(updatePosition), \
+ PREFIX(isPublicId)
+
+#define VTABLE VTABLE1, PREFIX(toUtf8), PREFIX(toUtf16)
+
+#define UCS2_GET_NAMING(pages, hi, lo) \
+ (namingBitmap[(pages[hi] << 3) + ((lo) >> 5)] & (1 << ((lo) & 0x1F)))
+
+/* A 2 byte UTF-8 representation splits the characters 11 bits
+between the bottom 5 and 6 bits of the bytes.
+We need 8 bits to index into pages, 3 bits to add to that index and
+5 bits to generate the mask. */
+#define UTF8_GET_NAMING2(pages, byte) \
+ (namingBitmap[((pages)[(((byte)[0]) >> 2) & 7] << 3) \
+ + ((((byte)[0]) & 3) << 1) \
+ + ((((byte)[1]) >> 5) & 1)] \
+ & (1 << (((byte)[1]) & 0x1F)))
+
+/* A 3 byte UTF-8 representation splits the characters 16 bits
+between the bottom 4, 6 and 6 bits of the bytes.
+We need 8 bits to index into pages, 3 bits to add to that index and
+5 bits to generate the mask. */
+#define UTF8_GET_NAMING3(pages, byte) \
+ (namingBitmap[((pages)[((((byte)[0]) & 0xF) << 4) \
+ + ((((byte)[1]) >> 2) & 0xF)] \
+ << 3) \
+ + ((((byte)[1]) & 3) << 1) \
+ + ((((byte)[2]) >> 5) & 1)] \
+ & (1 << (((byte)[2]) & 0x1F)))
+
+#define UTF8_GET_NAMING(pages, p, n) \
+ ((n) == 2 \
+ ? UTF8_GET_NAMING2(pages, (const unsigned char *)(p)) \
+ : ((n) == 3 \
+ ? UTF8_GET_NAMING3(pages, (const unsigned char *)(p)) \
+ : 0))
+
+#define UTF8_INVALID3(p) \
+ ((*p) == 0xED \
+ ? (((p)[1] & 0x20) != 0) \
+ : ((*p) == 0xEF \
+ ? ((p)[1] == 0xBF && ((p)[2] == 0xBF || (p)[2] == 0xBE)) \
+ : 0))
+
+#define UTF8_INVALID4(p) ((*p) == 0xF4 && ((p)[1] & 0x30) != 0)
+
+static
+int isNever(const ENCODING *enc, const char *p)
+{
+ return 0;
+}
+
+static
+int utf8_isName2(const ENCODING *enc, const char *p)
+{
+ return UTF8_GET_NAMING2(namePages, (const unsigned char *)p);
+}
+
+static
+int utf8_isName3(const ENCODING *enc, const char *p)
+{
+ return UTF8_GET_NAMING3(namePages, (const unsigned char *)p);
+}
+
+#define utf8_isName4 isNever
+
+static
+int utf8_isNmstrt2(const ENCODING *enc, const char *p)
+{
+ return UTF8_GET_NAMING2(nmstrtPages, (const unsigned char *)p);
+}
+
+static
+int utf8_isNmstrt3(const ENCODING *enc, const char *p)
+{
+ return UTF8_GET_NAMING3(nmstrtPages, (const unsigned char *)p);
+}
+
+#define utf8_isNmstrt4 isNever
+
+#define utf8_isInvalid2 isNever
+
+static
+int utf8_isInvalid3(const ENCODING *enc, const char *p)
+{
+ return UTF8_INVALID3((const unsigned char *)p);
+}
+
+static
+int utf8_isInvalid4(const ENCODING *enc, const char *p)
+{
+ return UTF8_INVALID4((const unsigned char *)p);
+}
+
+struct normal_encoding {
+ ENCODING enc;
+ unsigned char type[256];
+#ifdef XML_MIN_SIZE
+ int (*byteType)(const ENCODING *, const char *);
+ int (*isNameMin)(const ENCODING *, const char *);
+ int (*isNmstrtMin)(const ENCODING *, const char *);
+ int (*byteToAscii)(const ENCODING *, const char *);
+ int (*charMatches)(const ENCODING *, const char *, int);
+#endif /* XML_MIN_SIZE */
+ int (*isName2)(const ENCODING *, const char *);
+ int (*isName3)(const ENCODING *, const char *);
+ int (*isName4)(const ENCODING *, const char *);
+ int (*isNmstrt2)(const ENCODING *, const char *);
+ int (*isNmstrt3)(const ENCODING *, const char *);
+ int (*isNmstrt4)(const ENCODING *, const char *);
+ int (*isInvalid2)(const ENCODING *, const char *);
+ int (*isInvalid3)(const ENCODING *, const char *);
+ int (*isInvalid4)(const ENCODING *, const char *);
+};
+
+#ifdef XML_MIN_SIZE
+
+#define STANDARD_VTABLE(E) \
+ E ## byteType, \
+ E ## isNameMin, \
+ E ## isNmstrtMin, \
+ E ## byteToAscii, \
+ E ## charMatches,
+
+#else
+
+#define STANDARD_VTABLE(E) /* as nothing */
+
+#endif
+
+#define NORMAL_VTABLE(E) \
+ E ## isName2, \
+ E ## isName3, \
+ E ## isName4, \
+ E ## isNmstrt2, \
+ E ## isNmstrt3, \
+ E ## isNmstrt4, \
+ E ## isInvalid2, \
+ E ## isInvalid3, \
+ E ## isInvalid4
+
+static int checkCharRefNumber(int);
+
+#include "xmltok_impl.h"
+#include "ascii.h"
+
+#ifdef XML_MIN_SIZE
+#define sb_isNameMin isNever
+#define sb_isNmstrtMin isNever
+#endif
+
+#ifdef XML_MIN_SIZE
+#define MINBPC(enc) ((enc)->minBytesPerChar)
+#else
+/* minimum bytes per character */
+#define MINBPC(enc) 1
+#endif
+
+#define SB_BYTE_TYPE(enc, p) \
+ (((struct normal_encoding *)(enc))->type[(unsigned char)*(p)])
+
+#ifdef XML_MIN_SIZE
+static
+int sb_byteType(const ENCODING *enc, const char *p)
+{
+ return SB_BYTE_TYPE(enc, p);
+}
+#define BYTE_TYPE(enc, p) \
+ (((const struct normal_encoding *)(enc))->byteType(enc, p))
+#else
+#define BYTE_TYPE(enc, p) SB_BYTE_TYPE(enc, p)
+#endif
+
+#ifdef XML_MIN_SIZE
+#define BYTE_TO_ASCII(enc, p) \
+ (((const struct normal_encoding *)(enc))->byteToAscii(enc, p))
+static
+int sb_byteToAscii(const ENCODING *enc, const char *p)
+{
+ return *p;
+}
+#else
+#define BYTE_TO_ASCII(enc, p) (*(p))
+#endif
+
+#define IS_NAME_CHAR(enc, p, n) \
+ (((const struct normal_encoding *)(enc))->isName ## n(enc, p))
+#define IS_NMSTRT_CHAR(enc, p, n) \
+ (((const struct normal_encoding *)(enc))->isNmstrt ## n(enc, p))
+#define IS_INVALID_CHAR(enc, p, n) \
+ (((const struct normal_encoding *)(enc))->isInvalid ## n(enc, p))
+
+#ifdef XML_MIN_SIZE
+#define IS_NAME_CHAR_MINBPC(enc, p) \
+ (((const struct normal_encoding *)(enc))->isNameMin(enc, p))
+#define IS_NMSTRT_CHAR_MINBPC(enc, p) \
+ (((const struct normal_encoding *)(enc))->isNmstrtMin(enc, p))
+#else
+#define IS_NAME_CHAR_MINBPC(enc, p) (0)
+#define IS_NMSTRT_CHAR_MINBPC(enc, p) (0)
+#endif
+
+#ifdef XML_MIN_SIZE
+#define CHAR_MATCHES(enc, p, c) \
+ (((const struct normal_encoding *)(enc))->charMatches(enc, p, c))
+static
+int sb_charMatches(const ENCODING *enc, const char *p, int c)
+{
+ return *p == c;
+}
+#else
+/* c is an ASCII character */
+#define CHAR_MATCHES(enc, p, c) (*(p) == c)
+#endif
+
+#define PREFIX(ident) normal_ ## ident
+#include "xmltok_impl.c"
+
+#undef MINBPC
+#undef BYTE_TYPE
+#undef BYTE_TO_ASCII
+#undef CHAR_MATCHES
+#undef IS_NAME_CHAR
+#undef IS_NAME_CHAR_MINBPC
+#undef IS_NMSTRT_CHAR
+#undef IS_NMSTRT_CHAR_MINBPC
+#undef IS_INVALID_CHAR
+
+enum { /* UTF8_cvalN is value of masked first byte of N byte sequence */
+ UTF8_cval1 = 0x00,
+ UTF8_cval2 = 0xc0,
+ UTF8_cval3 = 0xe0,
+ UTF8_cval4 = 0xf0
+};
+
+static
+void utf8_toUtf8(const ENCODING *enc,
+ const char **fromP, const char *fromLim,
+ char **toP, const char *toLim)
+{
+ char *to;
+ const char *from;
+ if (fromLim - *fromP > toLim - *toP) {
+ /* Avoid copying partial characters. */
+ for (fromLim = *fromP + (toLim - *toP); fromLim > *fromP; fromLim--)
+ if (((unsigned char)fromLim[-1] & 0xc0) != 0x80)
+ break;
+ }
+ for (to = *toP, from = *fromP; from != fromLim; from++, to++)
+ *to = *from;
+ *fromP = from;
+ *toP = to;
+}
+
+static
+void utf8_toUtf16(const ENCODING *enc,
+ const char **fromP, const char *fromLim,
+ unsigned short **toP, const unsigned short *toLim)
+{
+ unsigned short *to = *toP;
+ const char *from = *fromP;
+ while (from != fromLim && to != toLim) {
+ switch (((struct normal_encoding *)enc)->type[(unsigned char)*from]) {
+ case BT_LEAD2:
+ *to++ = ((from[0] & 0x1f) << 6) | (from[1] & 0x3f);
+ from += 2;
+ break;
+ case BT_LEAD3:
+ *to++ = ((from[0] & 0xf) << 12) | ((from[1] & 0x3f) << 6) | (from[2] & 0x3f);
+ from += 3;
+ break;
+ case BT_LEAD4:
+ {
+ unsigned long n;
+ if (to + 1 == toLim)
+ break;
+ n = ((from[0] & 0x7) << 18) | ((from[1] & 0x3f) << 12) | ((from[2] & 0x3f) << 6) | (from[3] & 0x3f);
+ n -= 0x10000;
+ to[0] = (unsigned short)((n >> 10) | 0xD800);
+ to[1] = (unsigned short)((n & 0x3FF) | 0xDC00);
+ to += 2;
+ from += 4;
+ }
+ break;
+ default:
+ *to++ = *from++;
+ break;
+ }
+ }
+ *fromP = from;
+ *toP = to;
+}
+
+#ifdef XML_NS
+static const struct normal_encoding utf8_encoding_ns = {
+ { VTABLE1, utf8_toUtf8, utf8_toUtf16, 1, 1, 0 },
+ {
+#include "asciitab.h"
+#include "utf8tab.h"
+ },
+ STANDARD_VTABLE(sb_) NORMAL_VTABLE(utf8_)
+};
+#endif
+
+static const struct normal_encoding utf8_encoding = {
+ { VTABLE1, utf8_toUtf8, utf8_toUtf16, 1, 1, 0 },
+ {
+#define BT_COLON BT_NMSTRT
+#include "asciitab.h"
+#undef BT_COLON
+#include "utf8tab.h"
+ },
+ STANDARD_VTABLE(sb_) NORMAL_VTABLE(utf8_)
+};
+
+#ifdef XML_NS
+
+static const struct normal_encoding internal_utf8_encoding_ns = {
+ { VTABLE1, utf8_toUtf8, utf8_toUtf16, 1, 1, 0 },
+ {
+#include "iasciitab.h"
+#include "utf8tab.h"
+ },
+ STANDARD_VTABLE(sb_) NORMAL_VTABLE(utf8_)
+};
+
+#endif
+
+static const struct normal_encoding internal_utf8_encoding = {
+ { VTABLE1, utf8_toUtf8, utf8_toUtf16, 1, 1, 0 },
+ {
+#define BT_COLON BT_NMSTRT
+#include "iasciitab.h"
+#undef BT_COLON
+#include "utf8tab.h"
+ },
+ STANDARD_VTABLE(sb_) NORMAL_VTABLE(utf8_)
+};
+
+static
+void latin1_toUtf8(const ENCODING *enc,
+ const char **fromP, const char *fromLim,
+ char **toP, const char *toLim)
+{
+ for (;;) {
+ unsigned char c;
+ if (*fromP == fromLim)
+ break;
+ c = (unsigned char)**fromP;
+ if (c & 0x80) {
+ if (toLim - *toP < 2)
+ break;
+ *(*toP)++ = ((c >> 6) | UTF8_cval2);
+ *(*toP)++ = ((c & 0x3f) | 0x80);
+ (*fromP)++;
+ }
+ else {
+ if (*toP == toLim)
+ break;
+ *(*toP)++ = *(*fromP)++;
+ }
+ }
+}
+
+static
+void latin1_toUtf16(const ENCODING *enc,
+ const char **fromP, const char *fromLim,
+ unsigned short **toP, const unsigned short *toLim)
+{
+ while (*fromP != fromLim && *toP != toLim)
+ *(*toP)++ = (unsigned char)*(*fromP)++;
+}
+
+#ifdef XML_NS
+
+static const struct normal_encoding latin1_encoding_ns = {
+ { VTABLE1, latin1_toUtf8, latin1_toUtf16, 1, 0, 0 },
+ {
+#include "asciitab.h"
+#include "latin1tab.h"
+ },
+ STANDARD_VTABLE(sb_)
+};
+
+#endif
+
+static const struct normal_encoding latin1_encoding = {
+ { VTABLE1, latin1_toUtf8, latin1_toUtf16, 1, 0, 0 },
+ {
+#define BT_COLON BT_NMSTRT
+#include "asciitab.h"
+#undef BT_COLON
+#include "latin1tab.h"
+ },
+ STANDARD_VTABLE(sb_)
+};
+
+static
+void ascii_toUtf8(const ENCODING *enc,
+ const char **fromP, const char *fromLim,
+ char **toP, const char *toLim)
+{
+ while (*fromP != fromLim && *toP != toLim)
+ *(*toP)++ = *(*fromP)++;
+}
+
+#ifdef XML_NS
+
+static const struct normal_encoding ascii_encoding_ns = {
+ { VTABLE1, ascii_toUtf8, latin1_toUtf16, 1, 1, 0 },
+ {
+#include "asciitab.h"
+/* BT_NONXML == 0 */
+ },
+ STANDARD_VTABLE(sb_)
+};
+
+#endif
+
+static const struct normal_encoding ascii_encoding = {
+ { VTABLE1, ascii_toUtf8, latin1_toUtf16, 1, 1, 0 },
+ {
+#define BT_COLON BT_NMSTRT
+#include "asciitab.h"
+#undef BT_COLON
+/* BT_NONXML == 0 */
+ },
+ STANDARD_VTABLE(sb_)
+};
+
+static int unicode_byte_type(char hi, char lo)
+{
+ switch ((unsigned char)hi) {
+ case 0xD8: case 0xD9: case 0xDA: case 0xDB:
+ return BT_LEAD4;
+ case 0xDC: case 0xDD: case 0xDE: case 0xDF:
+ return BT_TRAIL;
+ case 0xFF:
+ switch ((unsigned char)lo) {
+ case 0xFF:
+ case 0xFE:
+ return BT_NONXML;
+ }
+ break;
+ }
+ return BT_NONASCII;
+}
+
+#define DEFINE_UTF16_TO_UTF8(E) \
+static \
+void E ## toUtf8(const ENCODING *enc, \
+ const char **fromP, const char *fromLim, \
+ char **toP, const char *toLim) \
+{ \
+ const char *from; \
+ for (from = *fromP; from != fromLim; from += 2) { \
+ int plane; \
+ unsigned char lo2; \
+ unsigned char lo = GET_LO(from); \
+ unsigned char hi = GET_HI(from); \
+ switch (hi) { \
+ case 0: \
+ if (lo < 0x80) { \
+ if (*toP == toLim) { \
+ *fromP = from; \
+ return; \
+ } \
+ *(*toP)++ = lo; \
+ break; \
+ } \
+ /* fall through */ \
+ case 0x1: case 0x2: case 0x3: \
+ case 0x4: case 0x5: case 0x6: case 0x7: \
+ if (toLim - *toP < 2) { \
+ *fromP = from; \
+ return; \
+ } \
+ *(*toP)++ = ((lo >> 6) | (hi << 2) | UTF8_cval2); \
+ *(*toP)++ = ((lo & 0x3f) | 0x80); \
+ break; \
+ default: \
+ if (toLim - *toP < 3) { \
+ *fromP = from; \
+ return; \
+ } \
+ /* 16 bits divided 4, 6, 6 amongst 3 bytes */ \
+ *(*toP)++ = ((hi >> 4) | UTF8_cval3); \
+ *(*toP)++ = (((hi & 0xf) << 2) | (lo >> 6) | 0x80); \
+ *(*toP)++ = ((lo & 0x3f) | 0x80); \
+ break; \
+ case 0xD8: case 0xD9: case 0xDA: case 0xDB: \
+ if (toLim - *toP < 4) { \
+ *fromP = from; \
+ return; \
+ } \
+ plane = (((hi & 0x3) << 2) | ((lo >> 6) & 0x3)) + 1; \
+ *(*toP)++ = ((plane >> 2) | UTF8_cval4); \
+ *(*toP)++ = (((lo >> 2) & 0xF) | ((plane & 0x3) << 4) | 0x80); \
+ from += 2; \
+ lo2 = GET_LO(from); \
+ *(*toP)++ = (((lo & 0x3) << 4) \
+ | ((GET_HI(from) & 0x3) << 2) \
+ | (lo2 >> 6) \
+ | 0x80); \
+ *(*toP)++ = ((lo2 & 0x3f) | 0x80); \
+ break; \
+ } \
+ } \
+ *fromP = from; \
+}
+
+#define DEFINE_UTF16_TO_UTF16(E) \
+static \
+void E ## toUtf16(const ENCODING *enc, \
+ const char **fromP, const char *fromLim, \
+ unsigned short **toP, const unsigned short *toLim) \
+{ \
+ /* Avoid copying first half only of surrogate */ \
+ if (fromLim - *fromP > ((toLim - *toP) << 1) \
+ && (GET_HI(fromLim - 2) & 0xF8) == 0xD8) \
+ fromLim -= 2; \
+ for (; *fromP != fromLim && *toP != toLim; *fromP += 2) \
+ *(*toP)++ = (GET_HI(*fromP) << 8) | GET_LO(*fromP); \
+}
+
+#define SET2(ptr, ch) \
+ (((ptr)[0] = ((ch) & 0xff)), ((ptr)[1] = ((ch) >> 8)))
+#define GET_LO(ptr) ((unsigned char)(ptr)[0])
+#define GET_HI(ptr) ((unsigned char)(ptr)[1])
+
+DEFINE_UTF16_TO_UTF8(little2_)
+DEFINE_UTF16_TO_UTF16(little2_)
+
+#undef SET2
+#undef GET_LO
+#undef GET_HI
+
+#define SET2(ptr, ch) \
+ (((ptr)[0] = ((ch) >> 8)), ((ptr)[1] = ((ch) & 0xFF)))
+#define GET_LO(ptr) ((unsigned char)(ptr)[1])
+#define GET_HI(ptr) ((unsigned char)(ptr)[0])
+
+DEFINE_UTF16_TO_UTF8(big2_)
+DEFINE_UTF16_TO_UTF16(big2_)
+
+#undef SET2
+#undef GET_LO
+#undef GET_HI
+
+#define LITTLE2_BYTE_TYPE(enc, p) \
+ ((p)[1] == 0 \
+ ? ((struct normal_encoding *)(enc))->type[(unsigned char)*(p)] \
+ : unicode_byte_type((p)[1], (p)[0]))
+#define LITTLE2_BYTE_TO_ASCII(enc, p) ((p)[1] == 0 ? (p)[0] : -1)
+#define LITTLE2_CHAR_MATCHES(enc, p, c) ((p)[1] == 0 && (p)[0] == c)
+#define LITTLE2_IS_NAME_CHAR_MINBPC(enc, p) \
+ UCS2_GET_NAMING(namePages, (unsigned char)p[1], (unsigned char)p[0])
+#define LITTLE2_IS_NMSTRT_CHAR_MINBPC(enc, p) \
+ UCS2_GET_NAMING(nmstrtPages, (unsigned char)p[1], (unsigned char)p[0])
+
+#ifdef XML_MIN_SIZE
+
+static
+int little2_byteType(const ENCODING *enc, const char *p)
+{
+ return LITTLE2_BYTE_TYPE(enc, p);
+}
+
+static
+int little2_byteToAscii(const ENCODING *enc, const char *p)
+{
+ return LITTLE2_BYTE_TO_ASCII(enc, p);
+}
+
+static
+int little2_charMatches(const ENCODING *enc, const char *p, int c)
+{
+ return LITTLE2_CHAR_MATCHES(enc, p, c);
+}
+
+static
+int little2_isNameMin(const ENCODING *enc, const char *p)
+{
+ return LITTLE2_IS_NAME_CHAR_MINBPC(enc, p);
+}
+
+static
+int little2_isNmstrtMin(const ENCODING *enc, const char *p)
+{
+ return LITTLE2_IS_NMSTRT_CHAR_MINBPC(enc, p);
+}
+
+#undef VTABLE
+#define VTABLE VTABLE1, little2_toUtf8, little2_toUtf16
+
+#else /* not XML_MIN_SIZE */
+
+#undef PREFIX
+#define PREFIX(ident) little2_ ## ident
+#define MINBPC(enc) 2
+/* CHAR_MATCHES is guaranteed to have MINBPC bytes available. */
+#define BYTE_TYPE(enc, p) LITTLE2_BYTE_TYPE(enc, p)
+#define BYTE_TO_ASCII(enc, p) LITTLE2_BYTE_TO_ASCII(enc, p)
+#define CHAR_MATCHES(enc, p, c) LITTLE2_CHAR_MATCHES(enc, p, c)
+#define IS_NAME_CHAR(enc, p, n) 0
+#define IS_NAME_CHAR_MINBPC(enc, p) LITTLE2_IS_NAME_CHAR_MINBPC(enc, p)
+#define IS_NMSTRT_CHAR(enc, p, n) (0)
+#define IS_NMSTRT_CHAR_MINBPC(enc, p) LITTLE2_IS_NMSTRT_CHAR_MINBPC(enc, p)
+
+#include "xmltok_impl.c"
+
+#undef MINBPC
+#undef BYTE_TYPE
+#undef BYTE_TO_ASCII
+#undef CHAR_MATCHES
+#undef IS_NAME_CHAR
+#undef IS_NAME_CHAR_MINBPC
+#undef IS_NMSTRT_CHAR
+#undef IS_NMSTRT_CHAR_MINBPC
+#undef IS_INVALID_CHAR
+
+#endif /* not XML_MIN_SIZE */
+
+#ifdef XML_NS
+
+static const struct normal_encoding little2_encoding_ns = {
+ { VTABLE, 2, 0,
+#if XML_BYTE_ORDER == 12
+ 1
+#else
+ 0
+#endif
+ },
+ {
+#include "asciitab.h"
+#include "latin1tab.h"
+ },
+ STANDARD_VTABLE(little2_)
+};
+
+#endif
+
+static const struct normal_encoding little2_encoding = {
+ { VTABLE, 2, 0,
+#if XML_BYTE_ORDER == 12
+ 1
+#else
+ 0
+#endif
+ },
+ {
+#define BT_COLON BT_NMSTRT
+#include "asciitab.h"
+#undef BT_COLON
+#include "latin1tab.h"
+ },
+ STANDARD_VTABLE(little2_)
+};
+
+#if XML_BYTE_ORDER != 21
+
+#ifdef XML_NS
+
+static const struct normal_encoding internal_little2_encoding_ns = {
+ { VTABLE, 2, 0, 1 },
+ {
+#include "iasciitab.h"
+#include "latin1tab.h"
+ },
+ STANDARD_VTABLE(little2_)
+};
+
+#endif
+
+static const struct normal_encoding internal_little2_encoding = {
+ { VTABLE, 2, 0, 1 },
+ {
+#define BT_COLON BT_NMSTRT
+#include "iasciitab.h"
+#undef BT_COLON
+#include "latin1tab.h"
+ },
+ STANDARD_VTABLE(little2_)
+};
+
+#endif
+
+
+#define BIG2_BYTE_TYPE(enc, p) \
+ ((p)[0] == 0 \
+ ? ((struct normal_encoding *)(enc))->type[(unsigned char)(p)[1]] \
+ : unicode_byte_type((p)[0], (p)[1]))
+#define BIG2_BYTE_TO_ASCII(enc, p) ((p)[0] == 0 ? (p)[1] : -1)
+#define BIG2_CHAR_MATCHES(enc, p, c) ((p)[0] == 0 && (p)[1] == c)
+#define BIG2_IS_NAME_CHAR_MINBPC(enc, p) \
+ UCS2_GET_NAMING(namePages, (unsigned char)p[0], (unsigned char)p[1])
+#define BIG2_IS_NMSTRT_CHAR_MINBPC(enc, p) \
+ UCS2_GET_NAMING(nmstrtPages, (unsigned char)p[0], (unsigned char)p[1])
+
+#ifdef XML_MIN_SIZE
+
+static
+int big2_byteType(const ENCODING *enc, const char *p)
+{
+ return BIG2_BYTE_TYPE(enc, p);
+}
+
+static
+int big2_byteToAscii(const ENCODING *enc, const char *p)
+{
+ return BIG2_BYTE_TO_ASCII(enc, p);
+}
+
+static
+int big2_charMatches(const ENCODING *enc, const char *p, int c)
+{
+ return BIG2_CHAR_MATCHES(enc, p, c);
+}
+
+static
+int big2_isNameMin(const ENCODING *enc, const char *p)
+{
+ return BIG2_IS_NAME_CHAR_MINBPC(enc, p);
+}
+
+static
+int big2_isNmstrtMin(const ENCODING *enc, const char *p)
+{
+ return BIG2_IS_NMSTRT_CHAR_MINBPC(enc, p);
+}
+
+#undef VTABLE
+#define VTABLE VTABLE1, big2_toUtf8, big2_toUtf16
+
+#else /* not XML_MIN_SIZE */
+
+#undef PREFIX
+#define PREFIX(ident) big2_ ## ident
+#define MINBPC(enc) 2
+/* CHAR_MATCHES is guaranteed to have MINBPC bytes available. */
+#define BYTE_TYPE(enc, p) BIG2_BYTE_TYPE(enc, p)
+#define BYTE_TO_ASCII(enc, p) BIG2_BYTE_TO_ASCII(enc, p)
+#define CHAR_MATCHES(enc, p, c) BIG2_CHAR_MATCHES(enc, p, c)
+#define IS_NAME_CHAR(enc, p, n) 0
+#define IS_NAME_CHAR_MINBPC(enc, p) BIG2_IS_NAME_CHAR_MINBPC(enc, p)
+#define IS_NMSTRT_CHAR(enc, p, n) (0)
+#define IS_NMSTRT_CHAR_MINBPC(enc, p) BIG2_IS_NMSTRT_CHAR_MINBPC(enc, p)
+
+#include "xmltok_impl.c"
+
+#undef MINBPC
+#undef BYTE_TYPE
+#undef BYTE_TO_ASCII
+#undef CHAR_MATCHES
+#undef IS_NAME_CHAR
+#undef IS_NAME_CHAR_MINBPC
+#undef IS_NMSTRT_CHAR
+#undef IS_NMSTRT_CHAR_MINBPC
+#undef IS_INVALID_CHAR
+
+#endif /* not XML_MIN_SIZE */
+
+#ifdef XML_NS
+
+static const struct normal_encoding big2_encoding_ns = {
+ { VTABLE, 2, 0,
+#if XML_BYTE_ORDER == 21
+ 1
+#else
+ 0
+#endif
+ },
+ {
+#include "asciitab.h"
+#include "latin1tab.h"
+ },
+ STANDARD_VTABLE(big2_)
+};
+
+#endif
+
+static const struct normal_encoding big2_encoding = {
+ { VTABLE, 2, 0,
+#if XML_BYTE_ORDER == 21
+ 1
+#else
+ 0
+#endif
+ },
+ {
+#define BT_COLON BT_NMSTRT
+#include "asciitab.h"
+#undef BT_COLON
+#include "latin1tab.h"
+ },
+ STANDARD_VTABLE(big2_)
+};
+
+#if XML_BYTE_ORDER != 12
+
+#ifdef XML_NS
+
+static const struct normal_encoding internal_big2_encoding_ns = {
+ { VTABLE, 2, 0, 1 },
+ {
+#include "iasciitab.h"
+#include "latin1tab.h"
+ },
+ STANDARD_VTABLE(big2_)
+};
+
+#endif
+
+static const struct normal_encoding internal_big2_encoding = {
+ { VTABLE, 2, 0, 1 },
+ {
+#define BT_COLON BT_NMSTRT
+#include "iasciitab.h"
+#undef BT_COLON
+#include "latin1tab.h"
+ },
+ STANDARD_VTABLE(big2_)
+};
+
+#endif
+
+#undef PREFIX
+
+static
+int streqci(const char *s1, const char *s2)
+{
+ for (;;) {
+ char c1 = *s1++;
+ char c2 = *s2++;
+ if (ASCII_a <= c1 && c1 <= ASCII_z)
+ c1 += ASCII_A - ASCII_a;
+ if (ASCII_a <= c2 && c2 <= ASCII_z)
+ c2 += ASCII_A - ASCII_a;
+ if (c1 != c2)
+ return 0;
+ if (!c1)
+ break;
+ }
+ return 1;
+}
+
+static
+void initUpdatePosition(const ENCODING *enc, const char *ptr,
+ const char *end, POSITION *pos)
+{
+ normal_updatePosition(&utf8_encoding.enc, ptr, end, pos);
+}
+
+static
+int toAscii(const ENCODING *enc, const char *ptr, const char *end)
+{
+ char buf[1];
+ char *p = buf;
+ XmlUtf8Convert(enc, &ptr, end, &p, p + 1);
+ if (p == buf)
+ return -1;
+ else
+ return buf[0];
+}
+
+static
+int isSpace(int c)
+{
+ switch (c) {
+ case 0x20:
+ case 0xD:
+ case 0xA:
+ case 0x9:
+ return 1;
+ }
+ return 0;
+}
+
+/* Return 1 if there's just optional white space
+or there's an S followed by name=val. */
+static
+int parsePseudoAttribute(const ENCODING *enc,
+ const char *ptr,
+ const char *end,
+ const char **namePtr,
+ const char **nameEndPtr,
+ const char **valPtr,
+ const char **nextTokPtr)
+{
+ int c;
+ char open;
+ if (ptr == end) {
+ *namePtr = 0;
+ return 1;
+ }
+ if (!isSpace(toAscii(enc, ptr, end))) {
+ *nextTokPtr = ptr;
+ return 0;
+ }
+ do {
+ ptr += enc->minBytesPerChar;
+ } while (isSpace(toAscii(enc, ptr, end)));
+ if (ptr == end) {
+ *namePtr = 0;
+ return 1;
+ }
+ *namePtr = ptr;
+ for (;;) {
+ c = toAscii(enc, ptr, end);
+ if (c == -1) {
+ *nextTokPtr = ptr;
+ return 0;
+ }
+ if (c == ASCII_EQUALS) {
+ *nameEndPtr = ptr;
+ break;
+ }
+ if (isSpace(c)) {
+ *nameEndPtr = ptr;
+ do {
+ ptr += enc->minBytesPerChar;
+ } while (isSpace(c = toAscii(enc, ptr, end)));
+ if (c != ASCII_EQUALS) {
+ *nextTokPtr = ptr;
+ return 0;
+ }
+ break;
+ }
+ ptr += enc->minBytesPerChar;
+ }
+ if (ptr == *namePtr) {
+ *nextTokPtr = ptr;
+ return 0;
+ }
+ ptr += enc->minBytesPerChar;
+ c = toAscii(enc, ptr, end);
+ while (isSpace(c)) {
+ ptr += enc->minBytesPerChar;
+ c = toAscii(enc, ptr, end);
+ }
+ if (c != ASCII_QUOT && c != ASCII_APOS) {
+ *nextTokPtr = ptr;
+ return 0;
+ }
+ open = c;
+ ptr += enc->minBytesPerChar;
+ *valPtr = ptr;
+ for (;; ptr += enc->minBytesPerChar) {
+ c = toAscii(enc, ptr, end);
+ if (c == open)
+ break;
+ if (!(ASCII_a <= c && c <= ASCII_z)
+ && !(ASCII_A <= c && c <= ASCII_Z)
+ && !(ASCII_0 <= c && c <= ASCII_9)
+ && c != ASCII_PERIOD
+ && c != ASCII_MINUS
+ && c != ASCII_UNDERSCORE) {
+ *nextTokPtr = ptr;
+ return 0;
+ }
+ }
+ *nextTokPtr = ptr + enc->minBytesPerChar;
+ return 1;
+}
+
+static const char KW_version[] = {
+ ASCII_v, ASCII_e, ASCII_r, ASCII_s, ASCII_i, ASCII_o, ASCII_n, '\0'
+};
+
+static const char KW_encoding[] = {
+ ASCII_e, ASCII_n, ASCII_c, ASCII_o, ASCII_d, ASCII_i, ASCII_n, ASCII_g, '\0'
+};
+
+static const char KW_standalone[] = {
+ ASCII_s, ASCII_t, ASCII_a, ASCII_n, ASCII_d, ASCII_a, ASCII_l, ASCII_o, ASCII_n, ASCII_e, '\0'
+};
+
+static const char KW_yes[] = {
+ ASCII_y, ASCII_e, ASCII_s, '\0'
+};
+
+static const char KW_no[] = {
+ ASCII_n, ASCII_o, '\0'
+};
+
+static
+int doParseXmlDecl(const ENCODING *(*encodingFinder)(const ENCODING *,
+ const char *,
+ const char *),
+ int isGeneralTextEntity,
+ const ENCODING *enc,
+ const char *ptr,
+ const char *end,
+ const char **badPtr,
+ const char **versionPtr,
+ const char **encodingName,
+ const ENCODING **encoding,
+ int *standalone)
+{
+ const char *val = 0;
+ const char *name = 0;
+ const char *nameEnd = 0;
+ ptr += 5 * enc->minBytesPerChar;
+ end -= 2 * enc->minBytesPerChar;
+ if (!parsePseudoAttribute(enc, ptr, end, &name, &nameEnd, &val, &ptr) || !name) {
+ *badPtr = ptr;
+ return 0;
+ }
+ if (!XmlNameMatchesAscii(enc, name, nameEnd, KW_version)) {
+ if (!isGeneralTextEntity) {
+ *badPtr = name;
+ return 0;
+ }
+ }
+ else {
+ if (versionPtr)
+ *versionPtr = val;
+ if (!parsePseudoAttribute(enc, ptr, end, &name, &nameEnd, &val, &ptr)) {
+ *badPtr = ptr;
+ return 0;
+ }
+ if (!name) {
+ if (isGeneralTextEntity) {
+ /* a TextDecl must have an EncodingDecl */
+ *badPtr = ptr;
+ return 0;
+ }
+ return 1;
+ }
+ }
+ if (XmlNameMatchesAscii(enc, name, nameEnd, KW_encoding)) {
+ int c = toAscii(enc, val, end);
+ if (!(ASCII_a <= c && c <= ASCII_z) && !(ASCII_A <= c && c <= ASCII_Z)) {
+ *badPtr = val;
+ return 0;
+ }
+ if (encodingName)
+ *encodingName = val;
+ if (encoding)
+ *encoding = encodingFinder(enc, val, ptr - enc->minBytesPerChar);
+ if (!parsePseudoAttribute(enc, ptr, end, &name, &nameEnd, &val, &ptr)) {
+ *badPtr = ptr;
+ return 0;
+ }
+ if (!name)
+ return 1;
+ }
+ if (!XmlNameMatchesAscii(enc, name, nameEnd, KW_standalone) || isGeneralTextEntity) {
+ *badPtr = name;
+ return 0;
+ }
+ if (XmlNameMatchesAscii(enc, val, ptr - enc->minBytesPerChar, KW_yes)) {
+ if (standalone)
+ *standalone = 1;
+ }
+ else if (XmlNameMatchesAscii(enc, val, ptr - enc->minBytesPerChar, KW_no)) {
+ if (standalone)
+ *standalone = 0;
+ }
+ else {
+ *badPtr = val;
+ return 0;
+ }
+ while (isSpace(toAscii(enc, ptr, end)))
+ ptr += enc->minBytesPerChar;
+ if (ptr != end) {
+ *badPtr = ptr;
+ return 0;
+ }
+ return 1;
+}
+
+static
+int checkCharRefNumber(int result)
+{
+ switch (result >> 8) {
+ case 0xD8: case 0xD9: case 0xDA: case 0xDB:
+ case 0xDC: case 0xDD: case 0xDE: case 0xDF:
+ return -1;
+ case 0:
+ if (latin1_encoding.type[result] == BT_NONXML)
+ return -1;
+ break;
+ case 0xFF:
+ if (result == 0xFFFE || result == 0xFFFF)
+ return -1;
+ break;
+ }
+ return result;
+}
+
+int XmlUtf8Encode(int c, char *buf)
+{
+ enum {
+ /* minN is minimum legal resulting value for N byte sequence */
+ min2 = 0x80,
+ min3 = 0x800,
+ min4 = 0x10000
+ };
+
+ if (c < 0)
+ return 0;
+ if (c < min2) {
+ buf[0] = (c | UTF8_cval1);
+ return 1;
+ }
+ if (c < min3) {
+ buf[0] = ((c >> 6) | UTF8_cval2);
+ buf[1] = ((c & 0x3f) | 0x80);
+ return 2;
+ }
+ if (c < min4) {
+ buf[0] = ((c >> 12) | UTF8_cval3);
+ buf[1] = (((c >> 6) & 0x3f) | 0x80);
+ buf[2] = ((c & 0x3f) | 0x80);
+ return 3;
+ }
+ if (c < 0x110000) {
+ buf[0] = ((c >> 18) | UTF8_cval4);
+ buf[1] = (((c >> 12) & 0x3f) | 0x80);
+ buf[2] = (((c >> 6) & 0x3f) | 0x80);
+ buf[3] = ((c & 0x3f) | 0x80);
+ return 4;
+ }
+ return 0;
+}
+
+int XmlUtf16Encode(int charNum, unsigned short *buf)
+{
+ if (charNum < 0)
+ return 0;
+ if (charNum < 0x10000) {
+ buf[0] = charNum;
+ return 1;
+ }
+ if (charNum < 0x110000) {
+ charNum -= 0x10000;
+ buf[0] = (charNum >> 10) + 0xD800;
+ buf[1] = (charNum & 0x3FF) + 0xDC00;
+ return 2;
+ }
+ return 0;
+}
+
+struct unknown_encoding {
+ struct normal_encoding normal;
+ int (*convert)(void *userData, const char *p);
+ void *userData;
+ unsigned short utf16[256];
+ char utf8[256][4];
+};
+
+int XmlSizeOfUnknownEncoding(void)
+{
+ return sizeof(struct unknown_encoding);
+}
+
+static
+int unknown_isName(const ENCODING *enc, const char *p)
+{
+ int c = ((const struct unknown_encoding *)enc)
+ ->convert(((const struct unknown_encoding *)enc)->userData, p);
+ if (c & ~0xFFFF)
+ return 0;
+ return UCS2_GET_NAMING(namePages, c >> 8, c & 0xFF);
+}
+
+static
+int unknown_isNmstrt(const ENCODING *enc, const char *p)
+{
+ int c = ((const struct unknown_encoding *)enc)
+ ->convert(((const struct unknown_encoding *)enc)->userData, p);
+ if (c & ~0xFFFF)
+ return 0;
+ return UCS2_GET_NAMING(nmstrtPages, c >> 8, c & 0xFF);
+}
+
+static
+int unknown_isInvalid(const ENCODING *enc, const char *p)
+{
+ int c = ((const struct unknown_encoding *)enc)
+ ->convert(((const struct unknown_encoding *)enc)->userData, p);
+ return (c & ~0xFFFF) || checkCharRefNumber(c) < 0;
+}
+
+static
+void unknown_toUtf8(const ENCODING *enc,
+ const char **fromP, const char *fromLim,
+ char **toP, const char *toLim)
+{
+ char buf[XML_UTF8_ENCODE_MAX];
+ for (;;) {
+ const char *utf8;
+ int n;
+ if (*fromP == fromLim)
+ break;
+ utf8 = ((const struct unknown_encoding *)enc)->utf8[(unsigned char)**fromP];
+ n = *utf8++;
+ if (n == 0) {
+ int c = ((const struct unknown_encoding *)enc)
+ ->convert(((const struct unknown_encoding *)enc)->userData, *fromP);
+ n = XmlUtf8Encode(c, buf);
+ if (n > toLim - *toP)
+ break;
+ utf8 = buf;
+ *fromP += ((const struct normal_encoding *)enc)->type[(unsigned char)**fromP]
+ - (BT_LEAD2 - 2);
+ }
+ else {
+ if (n > toLim - *toP)
+ break;
+ (*fromP)++;
+ }
+ do {
+ *(*toP)++ = *utf8++;
+ } while (--n != 0);
+ }
+}
+
+static
+void unknown_toUtf16(const ENCODING *enc,
+ const char **fromP, const char *fromLim,
+ unsigned short **toP, const unsigned short *toLim)
+{
+ while (*fromP != fromLim && *toP != toLim) {
+ unsigned short c
+ = ((const struct unknown_encoding *)enc)->utf16[(unsigned char)**fromP];
+ if (c == 0) {
+ c = (unsigned short)((const struct unknown_encoding *)enc)
+ ->convert(((const struct unknown_encoding *)enc)->userData, *fromP);
+ *fromP += ((const struct normal_encoding *)enc)->type[(unsigned char)**fromP]
+ - (BT_LEAD2 - 2);
+ }
+ else
+ (*fromP)++;
+ *(*toP)++ = c;
+ }
+}
+
+ENCODING *
+XmlInitUnknownEncoding(void *mem,
+ int *table,
+ int (*convert)(void *userData, const char *p),
+ void *userData)
+{
+ int i;
+ struct unknown_encoding *e = mem;
+ for (i = 0; i < (int)sizeof(struct normal_encoding); i++)
+ ((char *)mem)[i] = ((char *)&latin1_encoding)[i];
+ for (i = 0; i < 128; i++)
+ if (latin1_encoding.type[i] != BT_OTHER
+ && latin1_encoding.type[i] != BT_NONXML
+ && table[i] != i)
+ return 0;
+ for (i = 0; i < 256; i++) {
+ int c = table[i];
+ if (c == -1) {
+ e->normal.type[i] = BT_MALFORM;
+ /* This shouldn't really get used. */
+ e->utf16[i] = 0xFFFF;
+ e->utf8[i][0] = 1;
+ e->utf8[i][1] = 0;
+ }
+ else if (c < 0) {
+ if (c < -4)
+ return 0;
+ e->normal.type[i] = BT_LEAD2 - (c + 2);
+ e->utf8[i][0] = 0;
+ e->utf16[i] = 0;
+ }
+ else if (c < 0x80) {
+ if (latin1_encoding.type[c] != BT_OTHER
+ && latin1_encoding.type[c] != BT_NONXML
+ && c != i)
+ return 0;
+ e->normal.type[i] = latin1_encoding.type[c];
+ e->utf8[i][0] = 1;
+ e->utf8[i][1] = (char)c;
+ e->utf16[i] = c == 0 ? 0xFFFF : c;
+ }
+ else if (checkCharRefNumber(c) < 0) {
+ e->normal.type[i] = BT_NONXML;
+ /* This shouldn't really get used. */
+ e->utf16[i] = 0xFFFF;
+ e->utf8[i][0] = 1;
+ e->utf8[i][1] = 0;
+ }
+ else {
+ if (c > 0xFFFF)
+ return 0;
+ if (UCS2_GET_NAMING(nmstrtPages, c >> 8, c & 0xff))
+ e->normal.type[i] = BT_NMSTRT;
+ else if (UCS2_GET_NAMING(namePages, c >> 8, c & 0xff))
+ e->normal.type[i] = BT_NAME;
+ else
+ e->normal.type[i] = BT_OTHER;
+ e->utf8[i][0] = (char)XmlUtf8Encode(c, e->utf8[i] + 1);
+ e->utf16[i] = c;
+ }
+ }
+ e->userData = userData;
+ e->convert = convert;
+ if (convert) {
+ e->normal.isName2 = unknown_isName;
+ e->normal.isName3 = unknown_isName;
+ e->normal.isName4 = unknown_isName;
+ e->normal.isNmstrt2 = unknown_isNmstrt;
+ e->normal.isNmstrt3 = unknown_isNmstrt;
+ e->normal.isNmstrt4 = unknown_isNmstrt;
+ e->normal.isInvalid2 = unknown_isInvalid;
+ e->normal.isInvalid3 = unknown_isInvalid;
+ e->normal.isInvalid4 = unknown_isInvalid;
+ }
+ e->normal.enc.utf8Convert = unknown_toUtf8;
+ e->normal.enc.utf16Convert = unknown_toUtf16;
+ return &(e->normal.enc);
+}
+
+/* If this enumeration is changed, getEncodingIndex and encodings
+must also be changed. */
+enum {
+ UNKNOWN_ENC = -1,
+ ISO_8859_1_ENC = 0,
+ US_ASCII_ENC,
+ UTF_8_ENC,
+ UTF_16_ENC,
+ UTF_16BE_ENC,
+ UTF_16LE_ENC,
+ /* must match encodingNames up to here */
+ NO_ENC
+};
+
+static const char KW_ISO_8859_1[] = {
+ ASCII_I, ASCII_S, ASCII_O, ASCII_MINUS, ASCII_8, ASCII_8, ASCII_5, ASCII_9, ASCII_MINUS, ASCII_1, '\0'
+};
+static const char KW_US_ASCII[] = {
+ ASCII_U, ASCII_S, ASCII_MINUS, ASCII_A, ASCII_S, ASCII_C, ASCII_I, ASCII_I, '\0'
+};
+static const char KW_UTF_8[] = {
+ ASCII_U, ASCII_T, ASCII_F, ASCII_MINUS, ASCII_8, '\0'
+};
+static const char KW_UTF_16[] = {
+ ASCII_U, ASCII_T, ASCII_F, ASCII_MINUS, ASCII_1, ASCII_6, '\0'
+};
+static const char KW_UTF_16BE[] = {
+ ASCII_U, ASCII_T, ASCII_F, ASCII_MINUS, ASCII_1, ASCII_6, ASCII_B, ASCII_E, '\0'
+};
+static const char KW_UTF_16LE[] = {
+ ASCII_U, ASCII_T, ASCII_F, ASCII_MINUS, ASCII_1, ASCII_6, ASCII_L, ASCII_E, '\0'
+};
+
+static
+int getEncodingIndex(const char *name)
+{
+ static const char *encodingNames[] = {
+ KW_ISO_8859_1,
+ KW_US_ASCII,
+ KW_UTF_8,
+ KW_UTF_16,
+ KW_UTF_16BE,
+ KW_UTF_16LE,
+ };
+ int i;
+ if (name == 0)
+ return NO_ENC;
+ for (i = 0; i < (int)(sizeof(encodingNames)/sizeof(encodingNames[0])); i++)
+ if (streqci(name, encodingNames[i]))
+ return i;
+ return UNKNOWN_ENC;
+}
+
+/* For binary compatibility, we store the index of the encoding specified
+at initialization in the isUtf16 member. */
+
+#define INIT_ENC_INDEX(enc) ((int)(enc)->initEnc.isUtf16)
+#define SET_INIT_ENC_INDEX(enc, i) ((enc)->initEnc.isUtf16 = (char)i)
+
+/* This is what detects the encoding.
+encodingTable maps from encoding indices to encodings;
+INIT_ENC_INDEX(enc) is the index of the external (protocol) specified encoding;
+state is XML_CONTENT_STATE if we're parsing an external text entity,
+and XML_PROLOG_STATE otherwise.
+*/
+
+
+static
+int initScan(const ENCODING **encodingTable,
+ const INIT_ENCODING *enc,
+ int state,
+ const char *ptr,
+ const char *end,
+ const char **nextTokPtr)
+{
+ const ENCODING **encPtr;
+
+ if (ptr == end)
+ return XML_TOK_NONE;
+ encPtr = enc->encPtr;
+ if (ptr + 1 == end) {
+ /* only a single byte available for auto-detection */
+#ifndef XML_DTD /* FIXME */
+ /* a well-formed document entity must have more than one byte */
+ if (state != XML_CONTENT_STATE)
+ return XML_TOK_PARTIAL;
+#endif
+ /* so we're parsing an external text entity... */
+ /* if UTF-16 was externally specified, then we need at least 2 bytes */
+ switch (INIT_ENC_INDEX(enc)) {
+ case UTF_16_ENC:
+ case UTF_16LE_ENC:
+ case UTF_16BE_ENC:
+ return XML_TOK_PARTIAL;
+ }
+ switch ((unsigned char)*ptr) {
+ case 0xFE:
+ case 0xFF:
+ case 0xEF: /* possibly first byte of UTF-8 BOM */
+ if (INIT_ENC_INDEX(enc) == ISO_8859_1_ENC
+ && state == XML_CONTENT_STATE)
+ break;
+ /* fall through */
+ case 0x00:
+ case 0x3C:
+ return XML_TOK_PARTIAL;
+ }
+ }
+ else {
+ switch (((unsigned char)ptr[0] << 8) | (unsigned char)ptr[1]) {
+ case 0xFEFF:
+ if (INIT_ENC_INDEX(enc) == ISO_8859_1_ENC
+ && state == XML_CONTENT_STATE)
+ break;
+ *nextTokPtr = ptr + 2;
+ *encPtr = encodingTable[UTF_16BE_ENC];
+ return XML_TOK_BOM;
+ /* 00 3C is handled in the default case */
+ case 0x3C00:
+ if ((INIT_ENC_INDEX(enc) == UTF_16BE_ENC
+ || INIT_ENC_INDEX(enc) == UTF_16_ENC)
+ && state == XML_CONTENT_STATE)
+ break;
+ *encPtr = encodingTable[UTF_16LE_ENC];
+ return XmlTok(*encPtr, state, ptr, end, nextTokPtr);
+ case 0xFFFE:
+ if (INIT_ENC_INDEX(enc) == ISO_8859_1_ENC
+ && state == XML_CONTENT_STATE)
+ break;
+ *nextTokPtr = ptr + 2;
+ *encPtr = encodingTable[UTF_16LE_ENC];
+ return XML_TOK_BOM;
+ case 0xEFBB:
+ /* Maybe a UTF-8 BOM (EF BB BF) */
+ /* If there's an explicitly specified (external) encoding
+ of ISO-8859-1 or some flavour of UTF-16
+ and this is an external text entity,
+ don't look for the BOM,
+ because it might be a legal data. */
+ if (state == XML_CONTENT_STATE) {
+ int e = INIT_ENC_INDEX(enc);
+ if (e == ISO_8859_1_ENC || e == UTF_16BE_ENC || e == UTF_16LE_ENC || e == UTF_16_ENC)
+ break;
+ }
+ if (ptr + 2 == end)
+ return XML_TOK_PARTIAL;
+ if ((unsigned char)ptr[2] == 0xBF) {
+ *encPtr = encodingTable[UTF_8_ENC];
+ *nextTokPtr = ptr + 3; // %%% luz 2002-11-11 added this, was missing and caused crash with UTF-8 BOM
+ return XML_TOK_BOM;
+ }
+ break;
+ default:
+ if (ptr[0] == '\0') {
+ /* 0 isn't a legal data character. Furthermore a document entity can only
+ start with ASCII characters. So the only way this can fail to be big-endian
+ UTF-16 if it it's an external parsed general entity that's labelled as
+ UTF-16LE. */
+ if (state == XML_CONTENT_STATE && INIT_ENC_INDEX(enc) == UTF_16LE_ENC)
+ break;
+ *encPtr = encodingTable[UTF_16BE_ENC];
+ return XmlTok(*encPtr, state, ptr, end, nextTokPtr);
+ }
+ else if (ptr[1] == '\0') {
+ /* We could recover here in the case:
+ - parsing an external entity
+ - second byte is 0
+ - no externally specified encoding
+ - no encoding declaration
+ by assuming UTF-16LE. But we don't, because this would mean when
+ presented just with a single byte, we couldn't reliably determine
+ whether we needed further bytes. */
+ if (state == XML_CONTENT_STATE)
+ break;
+ *encPtr = encodingTable[UTF_16LE_ENC];
+ return XmlTok(*encPtr, state, ptr, end, nextTokPtr);
+ }
+ break;
+ }
+ }
+ *encPtr = encodingTable[INIT_ENC_INDEX(enc)];
+ return XmlTok(*encPtr, state, ptr, end, nextTokPtr);
+}
+
+
+#define NS(x) x
+#define ns(x) x
+#include "xmltok_ns.c"
+#undef NS
+#undef ns
+
+#ifdef XML_NS
+
+#define NS(x) x ## NS
+#define ns(x) x ## _ns
+
+#include "xmltok_ns.c"
+
+#undef NS
+#undef ns
+
+ENCODING *
+XmlInitUnknownEncodingNS(void *mem,
+ int *table,
+ int (*convert)(void *userData, const char *p),
+ void *userData)
+{
+ ENCODING *enc = XmlInitUnknownEncoding(mem, table, convert, userData);
+ if (enc)
+ ((struct normal_encoding *)enc)->type[ASCII_COLON] = BT_COLON;
+ return enc;
+}
+
+#endif /* XML_NS */
diff --git a/src/expat/xmltok/xmltok.h b/src/expat/xmltok/xmltok.h
new file mode 100755
index 0000000..6f49887
--- /dev/null
+++ b/src/expat/xmltok/xmltok.h
@@ -0,0 +1,301 @@
+/*
+Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd
+See the file copying.txt for copying permission.
+*/
+
+#ifndef XmlTok_INCLUDED
+#define XmlTok_INCLUDED 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef XMLTOKAPI
+#define XMLTOKAPI /* as nothing */
+#endif
+
+/* The following token may be returned by XmlContentTok */
+#define XML_TOK_TRAILING_RSQB -5 /* ] or ]] at the end of the scan; might be start of
+ illegal ]]> sequence */
+/* The following tokens may be returned by both XmlPrologTok and XmlContentTok */
+#define XML_TOK_NONE -4 /* The string to be scanned is empty */
+#define XML_TOK_TRAILING_CR -3 /* A CR at the end of the scan;
+ might be part of CRLF sequence */
+#define XML_TOK_PARTIAL_CHAR -2 /* only part of a multibyte sequence */
+#define XML_TOK_PARTIAL -1 /* only part of a token */
+#define XML_TOK_INVALID 0
+
+/* The following tokens are returned by XmlContentTok; some are also
+ returned by XmlAttributeValueTok, XmlEntityTok, XmlCdataSectionTok */
+
+#define XML_TOK_START_TAG_WITH_ATTS 1
+#define XML_TOK_START_TAG_NO_ATTS 2
+#define XML_TOK_EMPTY_ELEMENT_WITH_ATTS 3 /* empty element tag <e/> */
+#define XML_TOK_EMPTY_ELEMENT_NO_ATTS 4
+#define XML_TOK_END_TAG 5
+#define XML_TOK_DATA_CHARS 6
+#define XML_TOK_DATA_NEWLINE 7
+#define XML_TOK_CDATA_SECT_OPEN 8
+#define XML_TOK_ENTITY_REF 9
+#define XML_TOK_CHAR_REF 10 /* numeric character reference */
+
+/* The following tokens may be returned by both XmlPrologTok and XmlContentTok */
+#define XML_TOK_PI 11 /* processing instruction */
+#define XML_TOK_XML_DECL 12 /* XML decl or text decl */
+#define XML_TOK_COMMENT 13
+#define XML_TOK_BOM 14 /* Byte order mark */
+
+/* The following tokens are returned only by XmlPrologTok */
+#define XML_TOK_PROLOG_S 15
+#define XML_TOK_DECL_OPEN 16 /* <!foo */
+#define XML_TOK_DECL_CLOSE 17 /* > */
+#define XML_TOK_NAME 18
+#define XML_TOK_NMTOKEN 19
+#define XML_TOK_POUND_NAME 20 /* #name */
+#define XML_TOK_OR 21 /* | */
+#define XML_TOK_PERCENT 22
+#define XML_TOK_OPEN_PAREN 23
+#define XML_TOK_CLOSE_PAREN 24
+#define XML_TOK_OPEN_BRACKET 25
+#define XML_TOK_CLOSE_BRACKET 26
+#define XML_TOK_LITERAL 27
+#define XML_TOK_PARAM_ENTITY_REF 28
+#define XML_TOK_INSTANCE_START 29
+
+/* The following occur only in element type declarations */
+#define XML_TOK_NAME_QUESTION 30 /* name? */
+#define XML_TOK_NAME_ASTERISK 31 /* name* */
+#define XML_TOK_NAME_PLUS 32 /* name+ */
+#define XML_TOK_COND_SECT_OPEN 33 /* <![ */
+#define XML_TOK_COND_SECT_CLOSE 34 /* ]]> */
+#define XML_TOK_CLOSE_PAREN_QUESTION 35 /* )? */
+#define XML_TOK_CLOSE_PAREN_ASTERISK 36 /* )* */
+#define XML_TOK_CLOSE_PAREN_PLUS 37 /* )+ */
+#define XML_TOK_COMMA 38
+
+/* The following token is returned only by XmlAttributeValueTok */
+#define XML_TOK_ATTRIBUTE_VALUE_S 39
+
+/* The following token is returned only by XmlCdataSectionTok */
+#define XML_TOK_CDATA_SECT_CLOSE 40
+
+/* With namespace processing this is returned by XmlPrologTok
+ for a name with a colon. */
+#define XML_TOK_PREFIXED_NAME 41
+
+#ifdef XML_DTD
+#define XML_TOK_IGNORE_SECT 42
+#endif /* XML_DTD */
+
+#ifdef XML_DTD
+#define XML_N_STATES 4
+#else /* not XML_DTD */
+#define XML_N_STATES 3
+#endif /* not XML_DTD */
+
+#define XML_PROLOG_STATE 0
+#define XML_CONTENT_STATE 1
+#define XML_CDATA_SECTION_STATE 2
+#ifdef XML_DTD
+#define XML_IGNORE_SECTION_STATE 3
+#endif /* XML_DTD */
+
+#define XML_N_LITERAL_TYPES 2
+#define XML_ATTRIBUTE_VALUE_LITERAL 0
+#define XML_ENTITY_VALUE_LITERAL 1
+
+/* The size of the buffer passed to XmlUtf8Encode must be at least this. */
+#define XML_UTF8_ENCODE_MAX 4
+/* The size of the buffer passed to XmlUtf16Encode must be at least this. */
+#define XML_UTF16_ENCODE_MAX 2
+
+typedef struct position {
+ /* first line and first column are 0 not 1 */
+ unsigned long lineNumber;
+ unsigned long columnNumber;
+} POSITION;
+
+typedef struct {
+ const char *name;
+ const char *valuePtr;
+ const char *valueEnd;
+ char normalized;
+} ATTRIBUTE;
+
+struct encoding;
+typedef struct encoding ENCODING;
+
+struct encoding {
+ int (*scanners[XML_N_STATES])(const ENCODING *,
+ const char *,
+ const char *,
+ const char **);
+ int (*literalScanners[XML_N_LITERAL_TYPES])(const ENCODING *,
+ const char *,
+ const char *,
+ const char **);
+ int (*sameName)(const ENCODING *,
+ const char *, const char *);
+ int (*nameMatchesAscii)(const ENCODING *,
+ const char *, const char *, const char *);
+ int (*nameLength)(const ENCODING *, const char *);
+ const char *(*skipS)(const ENCODING *, const char *);
+ int (*getAtts)(const ENCODING *enc, const char *ptr,
+ int attsMax, ATTRIBUTE *atts);
+ int (*charRefNumber)(const ENCODING *enc, const char *ptr);
+ int (*predefinedEntityName)(const ENCODING *, const char *, const char *);
+ void (*updatePosition)(const ENCODING *,
+ const char *ptr,
+ const char *end,
+ POSITION *);
+ int (*isPublicId)(const ENCODING *enc, const char *ptr, const char *end,
+ const char **badPtr);
+ void (*utf8Convert)(const ENCODING *enc,
+ const char **fromP,
+ const char *fromLim,
+ char **toP,
+ const char *toLim);
+ void (*utf16Convert)(const ENCODING *enc,
+ const char **fromP,
+ const char *fromLim,
+ unsigned short **toP,
+ const unsigned short *toLim);
+ int minBytesPerChar;
+ char isUtf8;
+ char isUtf16;
+};
+
+/*
+Scan the string starting at ptr until the end of the next complete token,
+but do not scan past eptr. Return an integer giving the type of token.
+
+Return XML_TOK_NONE when ptr == eptr; nextTokPtr will not be set.
+
+Return XML_TOK_PARTIAL when the string does not contain a complete token;
+nextTokPtr will not be set.
+
+Return XML_TOK_INVALID when the string does not start a valid token; nextTokPtr
+will be set to point to the character which made the token invalid.
+
+Otherwise the string starts with a valid token; nextTokPtr will be set to point
+to the character following the end of that token.
+
+Each data character counts as a single token, but adjacent data characters
+may be returned together. Similarly for characters in the prolog outside
+literals, comments and processing instructions.
+*/
+
+
+#define XmlTok(enc, state, ptr, end, nextTokPtr) \
+ (((enc)->scanners[state])(enc, ptr, end, nextTokPtr))
+
+#define XmlPrologTok(enc, ptr, end, nextTokPtr) \
+ XmlTok(enc, XML_PROLOG_STATE, ptr, end, nextTokPtr)
+
+#define XmlContentTok(enc, ptr, end, nextTokPtr) \
+ XmlTok(enc, XML_CONTENT_STATE, ptr, end, nextTokPtr)
+
+#define XmlCdataSectionTok(enc, ptr, end, nextTokPtr) \
+ XmlTok(enc, XML_CDATA_SECTION_STATE, ptr, end, nextTokPtr)
+
+#ifdef XML_DTD
+
+#define XmlIgnoreSectionTok(enc, ptr, end, nextTokPtr) \
+ XmlTok(enc, XML_IGNORE_SECTION_STATE, ptr, end, nextTokPtr)
+
+#endif /* XML_DTD */
+
+/* This is used for performing a 2nd-level tokenization on
+the content of a literal that has already been returned by XmlTok. */
+
+#define XmlLiteralTok(enc, literalType, ptr, end, nextTokPtr) \
+ (((enc)->literalScanners[literalType])(enc, ptr, end, nextTokPtr))
+
+#define XmlAttributeValueTok(enc, ptr, end, nextTokPtr) \
+ XmlLiteralTok(enc, XML_ATTRIBUTE_VALUE_LITERAL, ptr, end, nextTokPtr)
+
+#define XmlEntityValueTok(enc, ptr, end, nextTokPtr) \
+ XmlLiteralTok(enc, XML_ENTITY_VALUE_LITERAL, ptr, end, nextTokPtr)
+
+#define XmlSameName(enc, ptr1, ptr2) (((enc)->sameName)(enc, ptr1, ptr2))
+
+#define XmlNameMatchesAscii(enc, ptr1, end1, ptr2) \
+ (((enc)->nameMatchesAscii)(enc, ptr1, end1, ptr2))
+
+#define XmlNameLength(enc, ptr) \
+ (((enc)->nameLength)(enc, ptr))
+
+#define XmlSkipS(enc, ptr) \
+ (((enc)->skipS)(enc, ptr))
+
+#define XmlGetAttributes(enc, ptr, attsMax, atts) \
+ (((enc)->getAtts)(enc, ptr, attsMax, atts))
+
+#define XmlCharRefNumber(enc, ptr) \
+ (((enc)->charRefNumber)(enc, ptr))
+
+#define XmlPredefinedEntityName(enc, ptr, end) \
+ (((enc)->predefinedEntityName)(enc, ptr, end))
+
+#define XmlUpdatePosition(enc, ptr, end, pos) \
+ (((enc)->updatePosition)(enc, ptr, end, pos))
+
+#define XmlIsPublicId(enc, ptr, end, badPtr) \
+ (((enc)->isPublicId)(enc, ptr, end, badPtr))
+
+#define XmlUtf8Convert(enc, fromP, fromLim, toP, toLim) \
+ (((enc)->utf8Convert)(enc, fromP, fromLim, toP, toLim))
+
+#define XmlUtf16Convert(enc, fromP, fromLim, toP, toLim) \
+ (((enc)->utf16Convert)(enc, fromP, fromLim, toP, toLim))
+
+typedef struct {
+ ENCODING initEnc;
+ const ENCODING **encPtr;
+} INIT_ENCODING;
+
+int XMLTOKAPI XmlParseXmlDecl(int isGeneralTextEntity,
+ const ENCODING *enc,
+ const char *ptr,
+ const char *end,
+ const char **badPtr,
+ const char **versionPtr,
+ const char **encodingNamePtr,
+ const ENCODING **namedEncodingPtr,
+ int *standalonePtr);
+
+int XMLTOKAPI XmlInitEncoding(INIT_ENCODING *, const ENCODING **, const char *name);
+const ENCODING XMLTOKAPI *XmlGetUtf8InternalEncoding(void);
+const ENCODING XMLTOKAPI *XmlGetUtf16InternalEncoding(void);
+int XMLTOKAPI XmlUtf8Encode(int charNumber, char *buf);
+int XMLTOKAPI XmlUtf16Encode(int charNumber, unsigned short *buf);
+
+int XMLTOKAPI XmlSizeOfUnknownEncoding(void);
+ENCODING XMLTOKAPI *
+XmlInitUnknownEncoding(void *mem,
+ int *table,
+ int (*conv)(void *userData, const char *p),
+ void *userData);
+
+int XMLTOKAPI XmlParseXmlDeclNS(int isGeneralTextEntity,
+ const ENCODING *enc,
+ const char *ptr,
+ const char *end,
+ const char **badPtr,
+ const char **versionPtr,
+ const char **encodingNamePtr,
+ const ENCODING **namedEncodingPtr,
+ int *standalonePtr);
+int XMLTOKAPI XmlInitEncodingNS(INIT_ENCODING *, const ENCODING **, const char *name);
+const ENCODING XMLTOKAPI *XmlGetUtf8InternalEncodingNS(void);
+const ENCODING XMLTOKAPI *XmlGetUtf16InternalEncodingNS(void);
+ENCODING XMLTOKAPI *
+XmlInitUnknownEncodingNS(void *mem,
+ int *table,
+ int (*conv)(void *userData, const char *p),
+ void *userData);
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* not XmlTok_INCLUDED */
diff --git a/src/expat/xmltok/xmltok_impl.c b/src/expat/xmltok/xmltok_impl.c
new file mode 100755
index 0000000..e337f84
--- /dev/null
+++ b/src/expat/xmltok/xmltok_impl.c
@@ -0,0 +1,1765 @@
+/*
+Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd
+See the file copying.txt for copying permission.
+*/
+
+#ifndef IS_INVALID_CHAR
+#define IS_INVALID_CHAR(enc, ptr, n) (0)
+#endif
+
+#define INVALID_LEAD_CASE(n, ptr, nextTokPtr) \
+ case BT_LEAD ## n: \
+ if (end - ptr < n) \
+ return XML_TOK_PARTIAL_CHAR; \
+ if (IS_INVALID_CHAR(enc, ptr, n)) { \
+ *(nextTokPtr) = (ptr); \
+ return XML_TOK_INVALID; \
+ } \
+ ptr += n; \
+ break;
+
+#define INVALID_CASES(ptr, nextTokPtr) \
+ INVALID_LEAD_CASE(2, ptr, nextTokPtr) \
+ INVALID_LEAD_CASE(3, ptr, nextTokPtr) \
+ INVALID_LEAD_CASE(4, ptr, nextTokPtr) \
+ case BT_NONXML: \
+ case BT_MALFORM: \
+ case BT_TRAIL: \
+ *(nextTokPtr) = (ptr); \
+ return XML_TOK_INVALID;
+
+#define CHECK_NAME_CASE(n, enc, ptr, end, nextTokPtr) \
+ case BT_LEAD ## n: \
+ if (end - ptr < n) \
+ return XML_TOK_PARTIAL_CHAR; \
+ if (!IS_NAME_CHAR(enc, ptr, n)) { \
+ *nextTokPtr = ptr; \
+ return XML_TOK_INVALID; \
+ } \
+ ptr += n; \
+ break;
+
+#define CHECK_NAME_CASES(enc, ptr, end, nextTokPtr) \
+ case BT_NONASCII: \
+ if (!IS_NAME_CHAR_MINBPC(enc, ptr)) { \
+ *nextTokPtr = ptr; \
+ return XML_TOK_INVALID; \
+ } \
+ case BT_NMSTRT: \
+ case BT_HEX: \
+ case BT_DIGIT: \
+ case BT_NAME: \
+ case BT_MINUS: \
+ ptr += MINBPC(enc); \
+ break; \
+ CHECK_NAME_CASE(2, enc, ptr, end, nextTokPtr) \
+ CHECK_NAME_CASE(3, enc, ptr, end, nextTokPtr) \
+ CHECK_NAME_CASE(4, enc, ptr, end, nextTokPtr)
+
+#define CHECK_NMSTRT_CASE(n, enc, ptr, end, nextTokPtr) \
+ case BT_LEAD ## n: \
+ if (end - ptr < n) \
+ return XML_TOK_PARTIAL_CHAR; \
+ if (!IS_NMSTRT_CHAR(enc, ptr, n)) { \
+ *nextTokPtr = ptr; \
+ return XML_TOK_INVALID; \
+ } \
+ ptr += n; \
+ break;
+
+#define CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr) \
+ case BT_NONASCII: \
+ if (!IS_NMSTRT_CHAR_MINBPC(enc, ptr)) { \
+ *nextTokPtr = ptr; \
+ return XML_TOK_INVALID; \
+ } \
+ case BT_NMSTRT: \
+ case BT_HEX: \
+ ptr += MINBPC(enc); \
+ break; \
+ CHECK_NMSTRT_CASE(2, enc, ptr, end, nextTokPtr) \
+ CHECK_NMSTRT_CASE(3, enc, ptr, end, nextTokPtr) \
+ CHECK_NMSTRT_CASE(4, enc, ptr, end, nextTokPtr)
+
+#ifndef PREFIX
+#define PREFIX(ident) ident
+#endif
+
+/* ptr points to character following "<!-" */
+
+static
+int PREFIX(scanComment)(const ENCODING *enc, const char *ptr, const char *end,
+ const char **nextTokPtr)
+{
+ if (ptr != end) {
+ if (!CHAR_MATCHES(enc, ptr, ASCII_MINUS)) {
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+ ptr += MINBPC(enc);
+ while (ptr != end) {
+ switch (BYTE_TYPE(enc, ptr)) {
+ INVALID_CASES(ptr, nextTokPtr)
+ case BT_MINUS:
+ if ((ptr += MINBPC(enc)) == end)
+ return XML_TOK_PARTIAL;
+ if (CHAR_MATCHES(enc, ptr, ASCII_MINUS)) {
+ if ((ptr += MINBPC(enc)) == end)
+ return XML_TOK_PARTIAL;
+ if (!CHAR_MATCHES(enc, ptr, ASCII_GT)) {
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+ *nextTokPtr = ptr + MINBPC(enc);
+ return XML_TOK_COMMENT;
+ }
+ break;
+ default:
+ ptr += MINBPC(enc);
+ break;
+ }
+ }
+ }
+ return XML_TOK_PARTIAL;
+}
+
+/* ptr points to character following "<!" */
+
+static
+int PREFIX(scanDecl)(const ENCODING *enc, const char *ptr, const char *end,
+ const char **nextTokPtr)
+{
+ if (ptr == end)
+ return XML_TOK_PARTIAL;
+ switch (BYTE_TYPE(enc, ptr)) {
+ case BT_MINUS:
+ return PREFIX(scanComment)(enc, ptr + MINBPC(enc), end, nextTokPtr);
+ case BT_LSQB:
+ *nextTokPtr = ptr + MINBPC(enc);
+ return XML_TOK_COND_SECT_OPEN;
+ case BT_NMSTRT:
+ case BT_HEX:
+ ptr += MINBPC(enc);
+ break;
+ default:
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+ while (ptr != end) {
+ switch (BYTE_TYPE(enc, ptr)) {
+ case BT_PERCNT:
+ if (ptr + MINBPC(enc) == end)
+ return XML_TOK_PARTIAL;
+ /* don't allow <!ENTITY% foo "whatever"> */
+ switch (BYTE_TYPE(enc, ptr + MINBPC(enc))) {
+ case BT_S: case BT_CR: case BT_LF: case BT_PERCNT:
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+ /* fall through */
+ case BT_S: case BT_CR: case BT_LF:
+ *nextTokPtr = ptr;
+ return XML_TOK_DECL_OPEN;
+ case BT_NMSTRT:
+ case BT_HEX:
+ ptr += MINBPC(enc);
+ break;
+ default:
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+ }
+ return XML_TOK_PARTIAL;
+}
+
+static
+int PREFIX(checkPiTarget)(const ENCODING *enc, const char *ptr, const char *end, int *tokPtr)
+{
+ int upper = 0;
+ *tokPtr = XML_TOK_PI;
+ if (end - ptr != MINBPC(enc)*3)
+ return 1;
+ switch (BYTE_TO_ASCII(enc, ptr)) {
+ case ASCII_x:
+ break;
+ case ASCII_X:
+ upper = 1;
+ break;
+ default:
+ return 1;
+ }
+ ptr += MINBPC(enc);
+ switch (BYTE_TO_ASCII(enc, ptr)) {
+ case ASCII_m:
+ break;
+ case ASCII_M:
+ upper = 1;
+ break;
+ default:
+ return 1;
+ }
+ ptr += MINBPC(enc);
+ switch (BYTE_TO_ASCII(enc, ptr)) {
+ case ASCII_l:
+ break;
+ case ASCII_L:
+ upper = 1;
+ break;
+ default:
+ return 1;
+ }
+ if (upper)
+ return 0;
+ *tokPtr = XML_TOK_XML_DECL;
+ return 1;
+}
+
+/* ptr points to character following "<?" */
+
+static
+int PREFIX(scanPi)(const ENCODING *enc, const char *ptr, const char *end,
+ const char **nextTokPtr)
+{
+ int tok;
+ const char *target = ptr;
+ if (ptr == end)
+ return XML_TOK_PARTIAL;
+ switch (BYTE_TYPE(enc, ptr)) {
+ CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr)
+ default:
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+ while (ptr != end) {
+ switch (BYTE_TYPE(enc, ptr)) {
+ CHECK_NAME_CASES(enc, ptr, end, nextTokPtr)
+ case BT_S: case BT_CR: case BT_LF:
+ if (!PREFIX(checkPiTarget)(enc, target, ptr, &tok)) {
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+ ptr += MINBPC(enc);
+ while (ptr != end) {
+ switch (BYTE_TYPE(enc, ptr)) {
+ INVALID_CASES(ptr, nextTokPtr)
+ case BT_QUEST:
+ ptr += MINBPC(enc);
+ if (ptr == end)
+ return XML_TOK_PARTIAL;
+ if (CHAR_MATCHES(enc, ptr, ASCII_GT)) {
+ *nextTokPtr = ptr + MINBPC(enc);
+ return tok;
+ }
+ break;
+ default:
+ ptr += MINBPC(enc);
+ break;
+ }
+ }
+ return XML_TOK_PARTIAL;
+ case BT_QUEST:
+ if (!PREFIX(checkPiTarget)(enc, target, ptr, &tok)) {
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+ ptr += MINBPC(enc);
+ if (ptr == end)
+ return XML_TOK_PARTIAL;
+ if (CHAR_MATCHES(enc, ptr, ASCII_GT)) {
+ *nextTokPtr = ptr + MINBPC(enc);
+ return tok;
+ }
+ /* fall through */
+ default:
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+ }
+ return XML_TOK_PARTIAL;
+}
+
+
+static
+int PREFIX(scanCdataSection)(const ENCODING *enc, const char *ptr, const char *end,
+ const char **nextTokPtr)
+{
+ static const char CDATA_LSQB[] = { ASCII_C, ASCII_D, ASCII_A, ASCII_T, ASCII_A, ASCII_LSQB };
+ int i;
+ /* CDATA[ */
+ if (end - ptr < 6 * MINBPC(enc))
+ return XML_TOK_PARTIAL;
+ for (i = 0; i < 6; i++, ptr += MINBPC(enc)) {
+ if (!CHAR_MATCHES(enc, ptr, CDATA_LSQB[i])) {
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+ }
+ *nextTokPtr = ptr;
+ return XML_TOK_CDATA_SECT_OPEN;
+}
+
+static
+int PREFIX(cdataSectionTok)(const ENCODING *enc, const char *ptr, const char *end,
+ const char **nextTokPtr)
+{
+ if (ptr == end)
+ return XML_TOK_NONE;
+ if (MINBPC(enc) > 1) {
+ size_t n = end - ptr;
+ if (n & (MINBPC(enc) - 1)) {
+ n &= ~(MINBPC(enc) - 1);
+ if (n == 0)
+ return XML_TOK_PARTIAL;
+ end = ptr + n;
+ }
+ }
+ switch (BYTE_TYPE(enc, ptr)) {
+ case BT_RSQB:
+ ptr += MINBPC(enc);
+ if (ptr == end)
+ return XML_TOK_PARTIAL;
+ if (!CHAR_MATCHES(enc, ptr, ASCII_RSQB))
+ break;
+ ptr += MINBPC(enc);
+ if (ptr == end)
+ return XML_TOK_PARTIAL;
+ if (!CHAR_MATCHES(enc, ptr, ASCII_GT)) {
+ ptr -= MINBPC(enc);
+ break;
+ }
+ *nextTokPtr = ptr + MINBPC(enc);
+ return XML_TOK_CDATA_SECT_CLOSE;
+ case BT_CR:
+ ptr += MINBPC(enc);
+ if (ptr == end)
+ return XML_TOK_PARTIAL;
+ if (BYTE_TYPE(enc, ptr) == BT_LF)
+ ptr += MINBPC(enc);
+ *nextTokPtr = ptr;
+ return XML_TOK_DATA_NEWLINE;
+ case BT_LF:
+ *nextTokPtr = ptr + MINBPC(enc);
+ return XML_TOK_DATA_NEWLINE;
+ INVALID_CASES(ptr, nextTokPtr)
+ default:
+ ptr += MINBPC(enc);
+ break;
+ }
+ while (ptr != end) {
+ switch (BYTE_TYPE(enc, ptr)) {
+#define LEAD_CASE(n) \
+ case BT_LEAD ## n: \
+ if (end - ptr < n || IS_INVALID_CHAR(enc, ptr, n)) { \
+ *nextTokPtr = ptr; \
+ return XML_TOK_DATA_CHARS; \
+ } \
+ ptr += n; \
+ break;
+ LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4)
+#undef LEAD_CASE
+ case BT_NONXML:
+ case BT_MALFORM:
+ case BT_TRAIL:
+ case BT_CR:
+ case BT_LF:
+ case BT_RSQB:
+ *nextTokPtr = ptr;
+ return XML_TOK_DATA_CHARS;
+ default:
+ ptr += MINBPC(enc);
+ break;
+ }
+ }
+ *nextTokPtr = ptr;
+ return XML_TOK_DATA_CHARS;
+}
+
+/* ptr points to character following "</" */
+
+static
+int PREFIX(scanEndTag)(const ENCODING *enc, const char *ptr, const char *end,
+ const char **nextTokPtr)
+{
+ if (ptr == end)
+ return XML_TOK_PARTIAL;
+ switch (BYTE_TYPE(enc, ptr)) {
+ CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr)
+ default:
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+ while (ptr != end) {
+ switch (BYTE_TYPE(enc, ptr)) {
+ CHECK_NAME_CASES(enc, ptr, end, nextTokPtr)
+ case BT_S: case BT_CR: case BT_LF:
+ for (ptr += MINBPC(enc); ptr != end; ptr += MINBPC(enc)) {
+ switch (BYTE_TYPE(enc, ptr)) {
+ case BT_S: case BT_CR: case BT_LF:
+ break;
+ case BT_GT:
+ *nextTokPtr = ptr + MINBPC(enc);
+ return XML_TOK_END_TAG;
+ default:
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+ }
+ return XML_TOK_PARTIAL;
+#ifdef XML_NS
+ case BT_COLON:
+ /* no need to check qname syntax here, since end-tag must match exactly */
+ ptr += MINBPC(enc);
+ break;
+#endif
+ case BT_GT:
+ *nextTokPtr = ptr + MINBPC(enc);
+ return XML_TOK_END_TAG;
+ default:
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+ }
+ return XML_TOK_PARTIAL;
+}
+
+/* ptr points to character following "&#X" */
+
+static
+int PREFIX(scanHexCharRef)(const ENCODING *enc, const char *ptr, const char *end,
+ const char **nextTokPtr)
+{
+ if (ptr != end) {
+ switch (BYTE_TYPE(enc, ptr)) {
+ case BT_DIGIT:
+ case BT_HEX:
+ break;
+ default:
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+ for (ptr += MINBPC(enc); ptr != end; ptr += MINBPC(enc)) {
+ switch (BYTE_TYPE(enc, ptr)) {
+ case BT_DIGIT:
+ case BT_HEX:
+ break;
+ case BT_SEMI:
+ *nextTokPtr = ptr + MINBPC(enc);
+ return XML_TOK_CHAR_REF;
+ default:
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+ }
+ }
+ return XML_TOK_PARTIAL;
+}
+
+/* ptr points to character following "&#" */
+
+static
+int PREFIX(scanCharRef)(const ENCODING *enc, const char *ptr, const char *end,
+ const char **nextTokPtr)
+{
+ if (ptr != end) {
+ if (CHAR_MATCHES(enc, ptr, ASCII_x))
+ return PREFIX(scanHexCharRef)(enc, ptr + MINBPC(enc), end, nextTokPtr);
+ switch (BYTE_TYPE(enc, ptr)) {
+ case BT_DIGIT:
+ break;
+ default:
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+ for (ptr += MINBPC(enc); ptr != end; ptr += MINBPC(enc)) {
+ switch (BYTE_TYPE(enc, ptr)) {
+ case BT_DIGIT:
+ break;
+ case BT_SEMI:
+ *nextTokPtr = ptr + MINBPC(enc);
+ return XML_TOK_CHAR_REF;
+ default:
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+ }
+ }
+ return XML_TOK_PARTIAL;
+}
+
+/* ptr points to character following "&" */
+
+static
+int PREFIX(scanRef)(const ENCODING *enc, const char *ptr, const char *end,
+ const char **nextTokPtr)
+{
+ if (ptr == end)
+ return XML_TOK_PARTIAL;
+ switch (BYTE_TYPE(enc, ptr)) {
+ CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr)
+ case BT_NUM:
+ return PREFIX(scanCharRef)(enc, ptr + MINBPC(enc), end, nextTokPtr);
+ default:
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+ while (ptr != end) {
+ switch (BYTE_TYPE(enc, ptr)) {
+ CHECK_NAME_CASES(enc, ptr, end, nextTokPtr)
+ case BT_SEMI:
+ *nextTokPtr = ptr + MINBPC(enc);
+ return XML_TOK_ENTITY_REF;
+ default:
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+ }
+ return XML_TOK_PARTIAL;
+}
+
+/* ptr points to character following first character of attribute name */
+
+static
+int PREFIX(scanAtts)(const ENCODING *enc, const char *ptr, const char *end,
+ const char **nextTokPtr)
+{
+#ifdef XML_NS
+ int hadColon = 0;
+#endif
+ while (ptr != end) {
+ switch (BYTE_TYPE(enc, ptr)) {
+ CHECK_NAME_CASES(enc, ptr, end, nextTokPtr)
+#ifdef XML_NS
+ case BT_COLON:
+ if (hadColon) {
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+ hadColon = 1;
+ ptr += MINBPC(enc);
+ if (ptr == end)
+ return XML_TOK_PARTIAL;
+ switch (BYTE_TYPE(enc, ptr)) {
+ CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr)
+ default:
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+ break;
+#endif
+ case BT_S: case BT_CR: case BT_LF:
+ for (;;) {
+ int t;
+
+ ptr += MINBPC(enc);
+ if (ptr == end)
+ return XML_TOK_PARTIAL;
+ t = BYTE_TYPE(enc, ptr);
+ if (t == BT_EQUALS)
+ break;
+ switch (t) {
+ case BT_S:
+ case BT_LF:
+ case BT_CR:
+ break;
+ default:
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+ }
+ /* fall through */
+ case BT_EQUALS:
+ {
+ int open;
+#ifdef XML_NS
+ hadColon = 0;
+#endif
+ for (;;) {
+
+ ptr += MINBPC(enc);
+ if (ptr == end)
+ return XML_TOK_PARTIAL;
+ open = BYTE_TYPE(enc, ptr);
+ if (open == BT_QUOT || open == BT_APOS)
+ break;
+ switch (open) {
+ case BT_S:
+ case BT_LF:
+ case BT_CR:
+ break;
+ default:
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+ }
+ ptr += MINBPC(enc);
+ /* in attribute value */
+ for (;;) {
+ int t;
+ if (ptr == end)
+ return XML_TOK_PARTIAL;
+ t = BYTE_TYPE(enc, ptr);
+ if (t == open)
+ break;
+ switch (t) {
+ INVALID_CASES(ptr, nextTokPtr)
+ case BT_AMP:
+ {
+ int tok = PREFIX(scanRef)(enc, ptr + MINBPC(enc), end, &ptr);
+ if (tok <= 0) {
+ if (tok == XML_TOK_INVALID)
+ *nextTokPtr = ptr;
+ return tok;
+ }
+ break;
+ }
+ case BT_LT:
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ default:
+ ptr += MINBPC(enc);
+ break;
+ }
+ }
+ ptr += MINBPC(enc);
+ if (ptr == end)
+ return XML_TOK_PARTIAL;
+ switch (BYTE_TYPE(enc, ptr)) {
+ case BT_S:
+ case BT_CR:
+ case BT_LF:
+ break;
+ case BT_SOL:
+ goto sol;
+ case BT_GT:
+ goto gt;
+ default:
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+ /* ptr points to closing quote */
+ for (;;) {
+ ptr += MINBPC(enc);
+ if (ptr == end)
+ return XML_TOK_PARTIAL;
+ switch (BYTE_TYPE(enc, ptr)) {
+ CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr)
+ case BT_S: case BT_CR: case BT_LF:
+ continue;
+ case BT_GT:
+ gt:
+ *nextTokPtr = ptr + MINBPC(enc);
+ return XML_TOK_START_TAG_WITH_ATTS;
+ case BT_SOL:
+ sol:
+ ptr += MINBPC(enc);
+ if (ptr == end)
+ return XML_TOK_PARTIAL;
+ if (!CHAR_MATCHES(enc, ptr, ASCII_GT)) {
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+ *nextTokPtr = ptr + MINBPC(enc);
+ return XML_TOK_EMPTY_ELEMENT_WITH_ATTS;
+ default:
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+ break;
+ }
+ break;
+ }
+ default:
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+ }
+ return XML_TOK_PARTIAL;
+}
+
+/* ptr points to character following "<" */
+
+static
+int PREFIX(scanLt)(const ENCODING *enc, const char *ptr, const char *end,
+ const char **nextTokPtr)
+{
+#ifdef XML_NS
+ int hadColon;
+#endif
+ if (ptr == end)
+ return XML_TOK_PARTIAL;
+ switch (BYTE_TYPE(enc, ptr)) {
+ CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr)
+ case BT_EXCL:
+ if ((ptr += MINBPC(enc)) == end)
+ return XML_TOK_PARTIAL;
+ switch (BYTE_TYPE(enc, ptr)) {
+ case BT_MINUS:
+ return PREFIX(scanComment)(enc, ptr + MINBPC(enc), end, nextTokPtr);
+ case BT_LSQB:
+ return PREFIX(scanCdataSection)(enc, ptr + MINBPC(enc), end, nextTokPtr);
+ }
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ case BT_QUEST:
+ return PREFIX(scanPi)(enc, ptr + MINBPC(enc), end, nextTokPtr);
+ case BT_SOL:
+ return PREFIX(scanEndTag)(enc, ptr + MINBPC(enc), end, nextTokPtr);
+ default:
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+#ifdef XML_NS
+ hadColon = 0;
+#endif
+ /* we have a start-tag */
+ while (ptr != end) {
+ switch (BYTE_TYPE(enc, ptr)) {
+ CHECK_NAME_CASES(enc, ptr, end, nextTokPtr)
+#ifdef XML_NS
+ case BT_COLON:
+ if (hadColon) {
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+ hadColon = 1;
+ ptr += MINBPC(enc);
+ if (ptr == end)
+ return XML_TOK_PARTIAL;
+ switch (BYTE_TYPE(enc, ptr)) {
+ CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr)
+ default:
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+ break;
+#endif
+ case BT_S: case BT_CR: case BT_LF:
+ {
+ ptr += MINBPC(enc);
+ while (ptr != end) {
+ switch (BYTE_TYPE(enc, ptr)) {
+ CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr)
+ case BT_GT:
+ goto gt;
+ case BT_SOL:
+ goto sol;
+ case BT_S: case BT_CR: case BT_LF:
+ ptr += MINBPC(enc);
+ continue;
+ default:
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+ return PREFIX(scanAtts)(enc, ptr, end, nextTokPtr);
+ }
+ return XML_TOK_PARTIAL;
+ }
+ case BT_GT:
+ gt:
+ *nextTokPtr = ptr + MINBPC(enc);
+ return XML_TOK_START_TAG_NO_ATTS;
+ case BT_SOL:
+ sol:
+ ptr += MINBPC(enc);
+ if (ptr == end)
+ return XML_TOK_PARTIAL;
+ if (!CHAR_MATCHES(enc, ptr, ASCII_GT)) {
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+ *nextTokPtr = ptr + MINBPC(enc);
+ return XML_TOK_EMPTY_ELEMENT_NO_ATTS;
+ default:
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+ }
+ return XML_TOK_PARTIAL;
+}
+
+static
+int PREFIX(contentTok)(const ENCODING *enc, const char *ptr, const char *end,
+ const char **nextTokPtr)
+{
+ if (ptr == end)
+ return XML_TOK_NONE;
+ if (MINBPC(enc) > 1) {
+ size_t n = end - ptr;
+ if (n & (MINBPC(enc) - 1)) {
+ n &= ~(MINBPC(enc) - 1);
+ if (n == 0)
+ return XML_TOK_PARTIAL;
+ end = ptr + n;
+ }
+ }
+ switch (BYTE_TYPE(enc, ptr)) {
+ case BT_LT:
+ return PREFIX(scanLt)(enc, ptr + MINBPC(enc), end, nextTokPtr);
+ case BT_AMP:
+ return PREFIX(scanRef)(enc, ptr + MINBPC(enc), end, nextTokPtr);
+ case BT_CR:
+ ptr += MINBPC(enc);
+ if (ptr == end)
+ return XML_TOK_TRAILING_CR;
+ if (BYTE_TYPE(enc, ptr) == BT_LF)
+ ptr += MINBPC(enc);
+ *nextTokPtr = ptr;
+ return XML_TOK_DATA_NEWLINE;
+ case BT_LF:
+ *nextTokPtr = ptr + MINBPC(enc);
+ return XML_TOK_DATA_NEWLINE;
+ case BT_RSQB:
+ ptr += MINBPC(enc);
+ if (ptr == end)
+ return XML_TOK_TRAILING_RSQB;
+ if (!CHAR_MATCHES(enc, ptr, ASCII_RSQB))
+ break;
+ ptr += MINBPC(enc);
+ if (ptr == end)
+ return XML_TOK_TRAILING_RSQB;
+ if (!CHAR_MATCHES(enc, ptr, ASCII_GT)) {
+ ptr -= MINBPC(enc);
+ break;
+ }
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ INVALID_CASES(ptr, nextTokPtr)
+ default:
+ ptr += MINBPC(enc);
+ break;
+ }
+ while (ptr != end) {
+ switch (BYTE_TYPE(enc, ptr)) {
+#define LEAD_CASE(n) \
+ case BT_LEAD ## n: \
+ if (end - ptr < n || IS_INVALID_CHAR(enc, ptr, n)) { \
+ *nextTokPtr = ptr; \
+ return XML_TOK_DATA_CHARS; \
+ } \
+ ptr += n; \
+ break;
+ LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4)
+#undef LEAD_CASE
+ case BT_RSQB:
+ if (ptr + MINBPC(enc) != end) {
+ if (!CHAR_MATCHES(enc, ptr + MINBPC(enc), ASCII_RSQB)) {
+ ptr += MINBPC(enc);
+ break;
+ }
+ if (ptr + 2*MINBPC(enc) != end) {
+ if (!CHAR_MATCHES(enc, ptr + 2*MINBPC(enc), ASCII_GT)) {
+ ptr += MINBPC(enc);
+ break;
+ }
+ *nextTokPtr = ptr + 2*MINBPC(enc);
+ return XML_TOK_INVALID;
+ }
+ }
+ /* fall through */
+ case BT_AMP:
+ case BT_LT:
+ case BT_NONXML:
+ case BT_MALFORM:
+ case BT_TRAIL:
+ case BT_CR:
+ case BT_LF:
+ *nextTokPtr = ptr;
+ return XML_TOK_DATA_CHARS;
+ default:
+ ptr += MINBPC(enc);
+ break;
+ }
+ }
+ *nextTokPtr = ptr;
+ return XML_TOK_DATA_CHARS;
+}
+
+/* ptr points to character following "%" */
+
+static
+int PREFIX(scanPercent)(const ENCODING *enc, const char *ptr, const char *end,
+ const char **nextTokPtr)
+{
+ if (ptr == end)
+ return XML_TOK_PARTIAL;
+ switch (BYTE_TYPE(enc, ptr)) {
+ CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr)
+ case BT_S: case BT_LF: case BT_CR: case BT_PERCNT:
+ *nextTokPtr = ptr;
+ return XML_TOK_PERCENT;
+ default:
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+ while (ptr != end) {
+ switch (BYTE_TYPE(enc, ptr)) {
+ CHECK_NAME_CASES(enc, ptr, end, nextTokPtr)
+ case BT_SEMI:
+ *nextTokPtr = ptr + MINBPC(enc);
+ return XML_TOK_PARAM_ENTITY_REF;
+ default:
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+ }
+ return XML_TOK_PARTIAL;
+}
+
+static
+int PREFIX(scanPoundName)(const ENCODING *enc, const char *ptr, const char *end,
+ const char **nextTokPtr)
+{
+ if (ptr == end)
+ return XML_TOK_PARTIAL;
+ switch (BYTE_TYPE(enc, ptr)) {
+ CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr)
+ default:
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+ while (ptr != end) {
+ switch (BYTE_TYPE(enc, ptr)) {
+ CHECK_NAME_CASES(enc, ptr, end, nextTokPtr)
+ case BT_CR: case BT_LF: case BT_S:
+ case BT_RPAR: case BT_GT: case BT_PERCNT: case BT_VERBAR:
+ *nextTokPtr = ptr;
+ return XML_TOK_POUND_NAME;
+ default:
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+ }
+ return -XML_TOK_POUND_NAME;
+}
+
+static
+int PREFIX(scanLit)(int open, const ENCODING *enc,
+ const char *ptr, const char *end,
+ const char **nextTokPtr)
+{
+ while (ptr != end) {
+ int t = BYTE_TYPE(enc, ptr);
+ switch (t) {
+ INVALID_CASES(ptr, nextTokPtr)
+ case BT_QUOT:
+ case BT_APOS:
+ ptr += MINBPC(enc);
+ if (t != open)
+ break;
+ if (ptr == end)
+ return -XML_TOK_LITERAL;
+ *nextTokPtr = ptr;
+ switch (BYTE_TYPE(enc, ptr)) {
+ case BT_S: case BT_CR: case BT_LF:
+ case BT_GT: case BT_PERCNT: case BT_LSQB:
+ return XML_TOK_LITERAL;
+ default:
+ return XML_TOK_INVALID;
+ }
+ default:
+ ptr += MINBPC(enc);
+ break;
+ }
+ }
+ return XML_TOK_PARTIAL;
+}
+
+static
+int PREFIX(prologTok)(const ENCODING *enc, const char *ptr, const char *end,
+ const char **nextTokPtr)
+{
+ int tok;
+ if (ptr == end)
+ return XML_TOK_NONE;
+ if (MINBPC(enc) > 1) {
+ size_t n = end - ptr;
+ if (n & (MINBPC(enc) - 1)) {
+ n &= ~(MINBPC(enc) - 1);
+ if (n == 0)
+ return XML_TOK_PARTIAL;
+ end = ptr + n;
+ }
+ }
+ switch (BYTE_TYPE(enc, ptr)) {
+ case BT_QUOT:
+ return PREFIX(scanLit)(BT_QUOT, enc, ptr + MINBPC(enc), end, nextTokPtr);
+ case BT_APOS:
+ return PREFIX(scanLit)(BT_APOS, enc, ptr + MINBPC(enc), end, nextTokPtr);
+ case BT_LT:
+ {
+ ptr += MINBPC(enc);
+ if (ptr == end)
+ return XML_TOK_PARTIAL;
+ switch (BYTE_TYPE(enc, ptr)) {
+ case BT_EXCL:
+ return PREFIX(scanDecl)(enc, ptr + MINBPC(enc), end, nextTokPtr);
+ case BT_QUEST:
+ return PREFIX(scanPi)(enc, ptr + MINBPC(enc), end, nextTokPtr);
+ case BT_NMSTRT:
+ case BT_HEX:
+ case BT_NONASCII:
+ case BT_LEAD2:
+ case BT_LEAD3:
+ case BT_LEAD4:
+ *nextTokPtr = ptr - MINBPC(enc);
+ return XML_TOK_INSTANCE_START;
+ }
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+ case BT_CR:
+ if (ptr + MINBPC(enc) == end)
+ return -XML_TOK_PROLOG_S;
+ /* fall through */
+ case BT_S: case BT_LF:
+ for (;;) {
+ ptr += MINBPC(enc);
+ if (ptr == end)
+ break;
+ switch (BYTE_TYPE(enc, ptr)) {
+ case BT_S: case BT_LF:
+ break;
+ case BT_CR:
+ /* don't split CR/LF pair */
+ if (ptr + MINBPC(enc) != end)
+ break;
+ /* fall through */
+ default:
+ *nextTokPtr = ptr;
+ return XML_TOK_PROLOG_S;
+ }
+ }
+ *nextTokPtr = ptr;
+ return XML_TOK_PROLOG_S;
+ case BT_PERCNT:
+ return PREFIX(scanPercent)(enc, ptr + MINBPC(enc), end, nextTokPtr);
+ case BT_COMMA:
+ *nextTokPtr = ptr + MINBPC(enc);
+ return XML_TOK_COMMA;
+ case BT_LSQB:
+ *nextTokPtr = ptr + MINBPC(enc);
+ return XML_TOK_OPEN_BRACKET;
+ case BT_RSQB:
+ ptr += MINBPC(enc);
+ if (ptr == end)
+ return -XML_TOK_CLOSE_BRACKET;
+ if (CHAR_MATCHES(enc, ptr, ASCII_RSQB)) {
+ if (ptr + MINBPC(enc) == end)
+ return XML_TOK_PARTIAL;
+ if (CHAR_MATCHES(enc, ptr + MINBPC(enc), ASCII_GT)) {
+ *nextTokPtr = ptr + 2*MINBPC(enc);
+ return XML_TOK_COND_SECT_CLOSE;
+ }
+ }
+ *nextTokPtr = ptr;
+ return XML_TOK_CLOSE_BRACKET;
+ case BT_LPAR:
+ *nextTokPtr = ptr + MINBPC(enc);
+ return XML_TOK_OPEN_PAREN;
+ case BT_RPAR:
+ ptr += MINBPC(enc);
+ if (ptr == end)
+ return -XML_TOK_CLOSE_PAREN;
+ switch (BYTE_TYPE(enc, ptr)) {
+ case BT_AST:
+ *nextTokPtr = ptr + MINBPC(enc);
+ return XML_TOK_CLOSE_PAREN_ASTERISK;
+ case BT_QUEST:
+ *nextTokPtr = ptr + MINBPC(enc);
+ return XML_TOK_CLOSE_PAREN_QUESTION;
+ case BT_PLUS:
+ *nextTokPtr = ptr + MINBPC(enc);
+ return XML_TOK_CLOSE_PAREN_PLUS;
+ case BT_CR: case BT_LF: case BT_S:
+ case BT_GT: case BT_COMMA: case BT_VERBAR:
+ case BT_RPAR:
+ *nextTokPtr = ptr;
+ return XML_TOK_CLOSE_PAREN;
+ }
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ case BT_VERBAR:
+ *nextTokPtr = ptr + MINBPC(enc);
+ return XML_TOK_OR;
+ case BT_GT:
+ *nextTokPtr = ptr + MINBPC(enc);
+ return XML_TOK_DECL_CLOSE;
+ case BT_NUM:
+ return PREFIX(scanPoundName)(enc, ptr + MINBPC(enc), end, nextTokPtr);
+#define LEAD_CASE(n) \
+ case BT_LEAD ## n: \
+ if (end - ptr < n) \
+ return XML_TOK_PARTIAL_CHAR; \
+ if (IS_NMSTRT_CHAR(enc, ptr, n)) { \
+ ptr += n; \
+ tok = XML_TOK_NAME; \
+ break; \
+ } \
+ if (IS_NAME_CHAR(enc, ptr, n)) { \
+ ptr += n; \
+ tok = XML_TOK_NMTOKEN; \
+ break; \
+ } \
+ *nextTokPtr = ptr; \
+ return XML_TOK_INVALID;
+ LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4)
+#undef LEAD_CASE
+ case BT_NMSTRT:
+ case BT_HEX:
+ tok = XML_TOK_NAME;
+ ptr += MINBPC(enc);
+ break;
+ case BT_DIGIT:
+ case BT_NAME:
+ case BT_MINUS:
+#ifdef XML_NS
+ case BT_COLON:
+#endif
+ tok = XML_TOK_NMTOKEN;
+ ptr += MINBPC(enc);
+ break;
+ case BT_NONASCII:
+ if (IS_NMSTRT_CHAR_MINBPC(enc, ptr)) {
+ ptr += MINBPC(enc);
+ tok = XML_TOK_NAME;
+ break;
+ }
+ if (IS_NAME_CHAR_MINBPC(enc, ptr)) {
+ ptr += MINBPC(enc);
+ tok = XML_TOK_NMTOKEN;
+ break;
+ }
+ /* fall through */
+ default:
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+ while (ptr != end) {
+ switch (BYTE_TYPE(enc, ptr)) {
+ CHECK_NAME_CASES(enc, ptr, end, nextTokPtr)
+ case BT_GT: case BT_RPAR: case BT_COMMA:
+ case BT_VERBAR: case BT_LSQB: case BT_PERCNT:
+ case BT_S: case BT_CR: case BT_LF:
+ *nextTokPtr = ptr;
+ return tok;
+#ifdef XML_NS
+ case BT_COLON:
+ ptr += MINBPC(enc);
+ switch (tok) {
+ case XML_TOK_NAME:
+ if (ptr == end)
+ return XML_TOK_PARTIAL;
+ tok = XML_TOK_PREFIXED_NAME;
+ switch (BYTE_TYPE(enc, ptr)) {
+ CHECK_NAME_CASES(enc, ptr, end, nextTokPtr)
+ default:
+ tok = XML_TOK_NMTOKEN;
+ break;
+ }
+ break;
+ case XML_TOK_PREFIXED_NAME:
+ tok = XML_TOK_NMTOKEN;
+ break;
+ }
+ break;
+#endif
+ case BT_PLUS:
+ if (tok == XML_TOK_NMTOKEN) {
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+ *nextTokPtr = ptr + MINBPC(enc);
+ return XML_TOK_NAME_PLUS;
+ case BT_AST:
+ if (tok == XML_TOK_NMTOKEN) {
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+ *nextTokPtr = ptr + MINBPC(enc);
+ return XML_TOK_NAME_ASTERISK;
+ case BT_QUEST:
+ if (tok == XML_TOK_NMTOKEN) {
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+ *nextTokPtr = ptr + MINBPC(enc);
+ return XML_TOK_NAME_QUESTION;
+ default:
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+ }
+ return -tok;
+}
+
+static
+int PREFIX(attributeValueTok)(const ENCODING *enc, const char *ptr, const char *end,
+ const char **nextTokPtr)
+{
+ const char *start;
+ if (ptr == end)
+ return XML_TOK_NONE;
+ start = ptr;
+ while (ptr != end) {
+ switch (BYTE_TYPE(enc, ptr)) {
+#define LEAD_CASE(n) \
+ case BT_LEAD ## n: ptr += n; break;
+ LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4)
+#undef LEAD_CASE
+ case BT_AMP:
+ if (ptr == start)
+ return PREFIX(scanRef)(enc, ptr + MINBPC(enc), end, nextTokPtr);
+ *nextTokPtr = ptr;
+ return XML_TOK_DATA_CHARS;
+ case BT_LT:
+ /* this is for inside entity references */
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ case BT_LF:
+ if (ptr == start) {
+ *nextTokPtr = ptr + MINBPC(enc);
+ return XML_TOK_DATA_NEWLINE;
+ }
+ *nextTokPtr = ptr;
+ return XML_TOK_DATA_CHARS;
+ case BT_CR:
+ if (ptr == start) {
+ ptr += MINBPC(enc);
+ if (ptr == end)
+ return XML_TOK_TRAILING_CR;
+ if (BYTE_TYPE(enc, ptr) == BT_LF)
+ ptr += MINBPC(enc);
+ *nextTokPtr = ptr;
+ return XML_TOK_DATA_NEWLINE;
+ }
+ *nextTokPtr = ptr;
+ return XML_TOK_DATA_CHARS;
+ case BT_S:
+ if (ptr == start) {
+ *nextTokPtr = ptr + MINBPC(enc);
+ return XML_TOK_ATTRIBUTE_VALUE_S;
+ }
+ *nextTokPtr = ptr;
+ return XML_TOK_DATA_CHARS;
+ default:
+ ptr += MINBPC(enc);
+ break;
+ }
+ }
+ *nextTokPtr = ptr;
+ return XML_TOK_DATA_CHARS;
+}
+
+static
+int PREFIX(entityValueTok)(const ENCODING *enc, const char *ptr, const char *end,
+ const char **nextTokPtr)
+{
+ const char *start;
+ if (ptr == end)
+ return XML_TOK_NONE;
+ start = ptr;
+ while (ptr != end) {
+ switch (BYTE_TYPE(enc, ptr)) {
+#define LEAD_CASE(n) \
+ case BT_LEAD ## n: ptr += n; break;
+ LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4)
+#undef LEAD_CASE
+ case BT_AMP:
+ if (ptr == start)
+ return PREFIX(scanRef)(enc, ptr + MINBPC(enc), end, nextTokPtr);
+ *nextTokPtr = ptr;
+ return XML_TOK_DATA_CHARS;
+ case BT_PERCNT:
+ if (ptr == start)
+ return PREFIX(scanPercent)(enc, ptr + MINBPC(enc), end, nextTokPtr);
+ *nextTokPtr = ptr;
+ return XML_TOK_DATA_CHARS;
+ case BT_LF:
+ if (ptr == start) {
+ *nextTokPtr = ptr + MINBPC(enc);
+ return XML_TOK_DATA_NEWLINE;
+ }
+ *nextTokPtr = ptr;
+ return XML_TOK_DATA_CHARS;
+ case BT_CR:
+ if (ptr == start) {
+ ptr += MINBPC(enc);
+ if (ptr == end)
+ return XML_TOK_TRAILING_CR;
+ if (BYTE_TYPE(enc, ptr) == BT_LF)
+ ptr += MINBPC(enc);
+ *nextTokPtr = ptr;
+ return XML_TOK_DATA_NEWLINE;
+ }
+ *nextTokPtr = ptr;
+ return XML_TOK_DATA_CHARS;
+ default:
+ ptr += MINBPC(enc);
+ break;
+ }
+ }
+ *nextTokPtr = ptr;
+ return XML_TOK_DATA_CHARS;
+}
+
+#ifdef XML_DTD
+
+static
+int PREFIX(ignoreSectionTok)(const ENCODING *enc, const char *ptr, const char *end,
+ const char **nextTokPtr)
+{
+ int level = 0;
+ if (MINBPC(enc) > 1) {
+ size_t n = end - ptr;
+ if (n & (MINBPC(enc) - 1)) {
+ n &= ~(MINBPC(enc) - 1);
+ end = ptr + n;
+ }
+ }
+ while (ptr != end) {
+ switch (BYTE_TYPE(enc, ptr)) {
+ INVALID_CASES(ptr, nextTokPtr)
+ case BT_LT:
+ if ((ptr += MINBPC(enc)) == end)
+ return XML_TOK_PARTIAL;
+ if (CHAR_MATCHES(enc, ptr, ASCII_EXCL)) {
+ if ((ptr += MINBPC(enc)) == end)
+ return XML_TOK_PARTIAL;
+ if (CHAR_MATCHES(enc, ptr, ASCII_LSQB)) {
+ ++level;
+ ptr += MINBPC(enc);
+ }
+ }
+ break;
+ case BT_RSQB:
+ if ((ptr += MINBPC(enc)) == end)
+ return XML_TOK_PARTIAL;
+ if (CHAR_MATCHES(enc, ptr, ASCII_RSQB)) {
+ if ((ptr += MINBPC(enc)) == end)
+ return XML_TOK_PARTIAL;
+ if (CHAR_MATCHES(enc, ptr, ASCII_GT)) {
+ ptr += MINBPC(enc);
+ if (level == 0) {
+ *nextTokPtr = ptr;
+ return XML_TOK_IGNORE_SECT;
+ }
+ --level;
+ }
+ }
+ break;
+ default:
+ ptr += MINBPC(enc);
+ break;
+ }
+ }
+ return XML_TOK_PARTIAL;
+}
+
+#endif /* XML_DTD */
+
+static
+int PREFIX(isPublicId)(const ENCODING *enc, const char *ptr, const char *end,
+ const char **badPtr)
+{
+ ptr += MINBPC(enc);
+ end -= MINBPC(enc);
+ for (; ptr != end; ptr += MINBPC(enc)) {
+ switch (BYTE_TYPE(enc, ptr)) {
+ case BT_DIGIT:
+ case BT_HEX:
+ case BT_MINUS:
+ case BT_APOS:
+ case BT_LPAR:
+ case BT_RPAR:
+ case BT_PLUS:
+ case BT_COMMA:
+ case BT_SOL:
+ case BT_EQUALS:
+ case BT_QUEST:
+ case BT_CR:
+ case BT_LF:
+ case BT_SEMI:
+ case BT_EXCL:
+ case BT_AST:
+ case BT_PERCNT:
+ case BT_NUM:
+#ifdef XML_NS
+ case BT_COLON:
+#endif
+ break;
+ case BT_S:
+ if (CHAR_MATCHES(enc, ptr, ASCII_TAB)) {
+ *badPtr = ptr;
+ return 0;
+ }
+ break;
+ case BT_NAME:
+ case BT_NMSTRT:
+ if (!(BYTE_TO_ASCII(enc, ptr) & ~0x7f))
+ break;
+ default:
+ switch (BYTE_TO_ASCII(enc, ptr)) {
+ case 0x24: /* $ */
+ case 0x40: /* @ */
+ break;
+ default:
+ *badPtr = ptr;
+ return 0;
+ }
+ break;
+ }
+ }
+ return 1;
+}
+
+/* This must only be called for a well-formed start-tag or empty element tag.
+Returns the number of attributes. Pointers to the first attsMax attributes
+are stored in atts. */
+
+static
+int PREFIX(getAtts)(const ENCODING *enc, const char *ptr,
+ int attsMax, ATTRIBUTE *atts)
+{
+ enum { other, inName, inValue } state = inName;
+ int nAtts = 0;
+ int open = 0; /* defined when state == inValue;
+ initialization just to shut up compilers */
+
+ for (ptr += MINBPC(enc);; ptr += MINBPC(enc)) {
+ switch (BYTE_TYPE(enc, ptr)) {
+#define START_NAME \
+ if (state == other) { \
+ if (nAtts < attsMax) { \
+ atts[nAtts].name = ptr; \
+ atts[nAtts].normalized = 1; \
+ } \
+ state = inName; \
+ }
+#define LEAD_CASE(n) \
+ case BT_LEAD ## n: START_NAME ptr += (n - MINBPC(enc)); break;
+ LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4)
+#undef LEAD_CASE
+ case BT_NONASCII:
+ case BT_NMSTRT:
+ case BT_HEX:
+ START_NAME
+ break;
+#undef START_NAME
+ case BT_QUOT:
+ if (state != inValue) {
+ if (nAtts < attsMax)
+ atts[nAtts].valuePtr = ptr + MINBPC(enc);
+ state = inValue;
+ open = BT_QUOT;
+ }
+ else if (open == BT_QUOT) {
+ state = other;
+ if (nAtts < attsMax)
+ atts[nAtts].valueEnd = ptr;
+ nAtts++;
+ }
+ break;
+ case BT_APOS:
+ if (state != inValue) {
+ if (nAtts < attsMax)
+ atts[nAtts].valuePtr = ptr + MINBPC(enc);
+ state = inValue;
+ open = BT_APOS;
+ }
+ else if (open == BT_APOS) {
+ state = other;
+ if (nAtts < attsMax)
+ atts[nAtts].valueEnd = ptr;
+ nAtts++;
+ }
+ break;
+ case BT_AMP:
+ if (nAtts < attsMax)
+ atts[nAtts].normalized = 0;
+ break;
+ case BT_S:
+ if (state == inName)
+ state = other;
+ else if (state == inValue
+ && nAtts < attsMax
+ && atts[nAtts].normalized
+ && (ptr == atts[nAtts].valuePtr
+ || BYTE_TO_ASCII(enc, ptr) != ASCII_SPACE
+ || BYTE_TO_ASCII(enc, ptr + MINBPC(enc)) == ASCII_SPACE
+ || BYTE_TYPE(enc, ptr + MINBPC(enc)) == open))
+ atts[nAtts].normalized = 0;
+ break;
+ case BT_CR: case BT_LF:
+ /* This case ensures that the first attribute name is counted
+ Apart from that we could just change state on the quote. */
+ if (state == inName)
+ state = other;
+ else if (state == inValue && nAtts < attsMax)
+ atts[nAtts].normalized = 0;
+ break;
+ case BT_GT:
+ case BT_SOL:
+ if (state != inValue)
+ return nAtts;
+ break;
+ default:
+ break;
+ }
+ }
+ /* not reached */
+}
+
+static
+int PREFIX(charRefNumber)(const ENCODING *enc, const char *ptr)
+{
+ int result = 0;
+ /* skip &# */
+ ptr += 2*MINBPC(enc);
+ if (CHAR_MATCHES(enc, ptr, ASCII_x)) {
+ for (ptr += MINBPC(enc); !CHAR_MATCHES(enc, ptr, ASCII_SEMI); ptr += MINBPC(enc)) {
+ int c = BYTE_TO_ASCII(enc, ptr);
+ switch (c) {
+ case ASCII_0: case ASCII_1: case ASCII_2: case ASCII_3: case ASCII_4:
+ case ASCII_5: case ASCII_6: case ASCII_7: case ASCII_8: case ASCII_9:
+ result <<= 4;
+ result |= (c - ASCII_0);
+ break;
+ case ASCII_A: case ASCII_B: case ASCII_C: case ASCII_D: case ASCII_E: case ASCII_F:
+ result <<= 4;
+ result += 10 + (c - ASCII_A);
+ break;
+ case ASCII_a: case ASCII_b: case ASCII_c: case ASCII_d: case ASCII_e: case ASCII_f:
+ result <<= 4;
+ result += 10 + (c - ASCII_a);
+ break;
+ }
+ if (result >= 0x110000)
+ return -1;
+ }
+ }
+ else {
+ for (; !CHAR_MATCHES(enc, ptr, ASCII_SEMI); ptr += MINBPC(enc)) {
+ int c = BYTE_TO_ASCII(enc, ptr);
+ result *= 10;
+ result += (c - ASCII_0);
+ if (result >= 0x110000)
+ return -1;
+ }
+ }
+ return checkCharRefNumber(result);
+}
+
+static
+int PREFIX(predefinedEntityName)(const ENCODING *enc, const char *ptr, const char *end)
+{
+ switch ((end - ptr)/MINBPC(enc)) {
+ case 2:
+ if (CHAR_MATCHES(enc, ptr + MINBPC(enc), ASCII_t)) {
+ switch (BYTE_TO_ASCII(enc, ptr)) {
+ case ASCII_l:
+ return ASCII_LT;
+ case ASCII_g:
+ return ASCII_GT;
+ }
+ }
+ break;
+ case 3:
+ if (CHAR_MATCHES(enc, ptr, ASCII_a)) {
+ ptr += MINBPC(enc);
+ if (CHAR_MATCHES(enc, ptr, ASCII_m)) {
+ ptr += MINBPC(enc);
+ if (CHAR_MATCHES(enc, ptr, ASCII_p))
+ return ASCII_AMP;
+ }
+ }
+ break;
+ case 4:
+ switch (BYTE_TO_ASCII(enc, ptr)) {
+ case ASCII_q:
+ ptr += MINBPC(enc);
+ if (CHAR_MATCHES(enc, ptr, ASCII_u)) {
+ ptr += MINBPC(enc);
+ if (CHAR_MATCHES(enc, ptr, ASCII_o)) {
+ ptr += MINBPC(enc);
+ if (CHAR_MATCHES(enc, ptr, ASCII_t))
+ return ASCII_QUOT;
+ }
+ }
+ break;
+ case ASCII_a:
+ ptr += MINBPC(enc);
+ if (CHAR_MATCHES(enc, ptr, ASCII_p)) {
+ ptr += MINBPC(enc);
+ if (CHAR_MATCHES(enc, ptr, ASCII_o)) {
+ ptr += MINBPC(enc);
+ if (CHAR_MATCHES(enc, ptr, ASCII_s))
+ return ASCII_APOS;
+ }
+ }
+ break;
+ }
+ }
+ return 0;
+}
+
+static
+int PREFIX(sameName)(const ENCODING *enc, const char *ptr1, const char *ptr2)
+{
+ for (;;) {
+ switch (BYTE_TYPE(enc, ptr1)) {
+#define LEAD_CASE(n) \
+ case BT_LEAD ## n: \
+ if (*ptr1++ != *ptr2++) \
+ return 0;
+ LEAD_CASE(4) LEAD_CASE(3) LEAD_CASE(2)
+#undef LEAD_CASE
+ /* fall through */
+ if (*ptr1++ != *ptr2++)
+ return 0;
+ break;
+ case BT_NONASCII:
+ case BT_NMSTRT:
+#ifdef XML_NS
+ case BT_COLON:
+#endif
+ case BT_HEX:
+ case BT_DIGIT:
+ case BT_NAME:
+ case BT_MINUS:
+ if (*ptr2++ != *ptr1++)
+ return 0;
+ if (MINBPC(enc) > 1) {
+ if (*ptr2++ != *ptr1++)
+ return 0;
+ if (MINBPC(enc) > 2) {
+ if (*ptr2++ != *ptr1++)
+ return 0;
+ if (MINBPC(enc) > 3) {
+ if (*ptr2++ != *ptr1++)
+ return 0;
+ }
+ }
+ }
+ break;
+ default:
+ if (MINBPC(enc) == 1 && *ptr1 == *ptr2)
+ return 1;
+ switch (BYTE_TYPE(enc, ptr2)) {
+ case BT_LEAD2:
+ case BT_LEAD3:
+ case BT_LEAD4:
+ case BT_NONASCII:
+ case BT_NMSTRT:
+#ifdef XML_NS
+ case BT_COLON:
+#endif
+ case BT_HEX:
+ case BT_DIGIT:
+ case BT_NAME:
+ case BT_MINUS:
+ return 0;
+ default:
+ return 1;
+ }
+ }
+ }
+ /* not reached */
+}
+
+static
+int PREFIX(nameMatchesAscii)(const ENCODING *enc, const char *ptr1,
+ const char *end1, const char *ptr2)
+{
+ for (; *ptr2; ptr1 += MINBPC(enc), ptr2++) {
+ if (ptr1 == end1)
+ return 0;
+ if (!CHAR_MATCHES(enc, ptr1, *ptr2))
+ return 0;
+ }
+ return ptr1 == end1;
+}
+
+static
+int PREFIX(nameLength)(const ENCODING *enc, const char *ptr)
+{
+ const char *start = ptr;
+ for (;;) {
+ switch (BYTE_TYPE(enc, ptr)) {
+#define LEAD_CASE(n) \
+ case BT_LEAD ## n: ptr += n; break;
+ LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4)
+#undef LEAD_CASE
+ case BT_NONASCII:
+ case BT_NMSTRT:
+#ifdef XML_NS
+ case BT_COLON:
+#endif
+ case BT_HEX:
+ case BT_DIGIT:
+ case BT_NAME:
+ case BT_MINUS:
+ ptr += MINBPC(enc);
+ break;
+ default:
+ return ptr - start;
+ }
+ }
+}
+
+static
+const char *PREFIX(skipS)(const ENCODING *enc, const char *ptr)
+{
+ for (;;) {
+ switch (BYTE_TYPE(enc, ptr)) {
+ case BT_LF:
+ case BT_CR:
+ case BT_S:
+ ptr += MINBPC(enc);
+ break;
+ default:
+ return ptr;
+ }
+ }
+}
+
+static
+void PREFIX(updatePosition)(const ENCODING *enc,
+ const char *ptr,
+ const char *end,
+ POSITION *pos)
+{
+ while (ptr != end) {
+ switch (BYTE_TYPE(enc, ptr)) {
+#define LEAD_CASE(n) \
+ case BT_LEAD ## n: \
+ ptr += n; \
+ break;
+ LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4)
+#undef LEAD_CASE
+ case BT_LF:
+ pos->columnNumber = (unsigned)-1;
+ pos->lineNumber++;
+ ptr += MINBPC(enc);
+ break;
+ case BT_CR:
+ pos->lineNumber++;
+ ptr += MINBPC(enc);
+ if (ptr != end && BYTE_TYPE(enc, ptr) == BT_LF)
+ ptr += MINBPC(enc);
+ pos->columnNumber = (unsigned)-1;
+ break;
+ default:
+ ptr += MINBPC(enc);
+ break;
+ }
+ pos->columnNumber++;
+ }
+}
+
+#undef DO_LEAD_CASE
+#undef MULTIBYTE_CASES
+#undef INVALID_CASES
+#undef CHECK_NAME_CASE
+#undef CHECK_NAME_CASES
+#undef CHECK_NMSTRT_CASE
+#undef CHECK_NMSTRT_CASES
diff --git a/src/expat/xmltok/xmltok_impl.h b/src/expat/xmltok/xmltok_impl.h
new file mode 100755
index 0000000..eb92802
--- /dev/null
+++ b/src/expat/xmltok/xmltok_impl.h
@@ -0,0 +1,46 @@
+/*
+Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd
+See the file copying.txt for copying permission.
+*/
+
+enum {
+ BT_NONXML,
+ BT_MALFORM,
+ BT_LT,
+ BT_AMP,
+ BT_RSQB,
+ BT_LEAD2,
+ BT_LEAD3,
+ BT_LEAD4,
+ BT_TRAIL,
+ BT_CR,
+ BT_LF,
+ BT_GT,
+ BT_QUOT,
+ BT_APOS,
+ BT_EQUALS,
+ BT_QUEST,
+ BT_EXCL,
+ BT_SOL,
+ BT_SEMI,
+ BT_NUM,
+ BT_LSQB,
+ BT_S,
+ BT_NMSTRT,
+ BT_COLON,
+ BT_HEX,
+ BT_DIGIT,
+ BT_NAME,
+ BT_MINUS,
+ BT_OTHER, /* known not to be a name or name start character */
+ BT_NONASCII, /* might be a name or name start character */
+ BT_PERCNT,
+ BT_LPAR,
+ BT_RPAR,
+ BT_AST,
+ BT_PLUS,
+ BT_COMMA,
+ BT_VERBAR
+};
+
+#include <stddef.h>
diff --git a/src/expat/xmltok/xmltok_ns.c b/src/expat/xmltok/xmltok_ns.c
new file mode 100755
index 0000000..2427898
--- /dev/null
+++ b/src/expat/xmltok/xmltok_ns.c
@@ -0,0 +1,96 @@
+const ENCODING *NS(XmlGetUtf8InternalEncoding)(void)
+{
+ return &ns(internal_utf8_encoding).enc;
+}
+
+const ENCODING *NS(XmlGetUtf16InternalEncoding)(void)
+{
+#if XML_BYTE_ORDER == 12
+ return &ns(internal_little2_encoding).enc;
+#elif XML_BYTE_ORDER == 21
+ return &ns(internal_big2_encoding).enc;
+#else
+ const short n = 1;
+ return *(const char *)&n ? &ns(internal_little2_encoding).enc : &ns(internal_big2_encoding).enc;
+#endif
+}
+
+static
+const ENCODING *NS(encodings)[] = {
+ &ns(latin1_encoding).enc,
+ &ns(ascii_encoding).enc,
+ &ns(utf8_encoding).enc,
+ &ns(big2_encoding).enc,
+ &ns(big2_encoding).enc,
+ &ns(little2_encoding).enc,
+ &ns(utf8_encoding).enc /* NO_ENC */
+};
+
+static
+int NS(initScanProlog)(const ENCODING *enc, const char *ptr, const char *end,
+ const char **nextTokPtr)
+{
+ return initScan(NS(encodings), (const INIT_ENCODING *)enc, XML_PROLOG_STATE, ptr, end, nextTokPtr);
+}
+
+static
+int NS(initScanContent)(const ENCODING *enc, const char *ptr, const char *end,
+ const char **nextTokPtr)
+{
+ return initScan(NS(encodings), (const INIT_ENCODING *)enc, XML_CONTENT_STATE, ptr, end, nextTokPtr);
+}
+
+int NS(XmlInitEncoding)(INIT_ENCODING *p, const ENCODING **encPtr, const char *name)
+{
+ int i = getEncodingIndex(name);
+ if (i == UNKNOWN_ENC)
+ return 0;
+ SET_INIT_ENC_INDEX(p, i);
+ p->initEnc.scanners[XML_PROLOG_STATE] = NS(initScanProlog);
+ p->initEnc.scanners[XML_CONTENT_STATE] = NS(initScanContent);
+ p->initEnc.updatePosition = initUpdatePosition;
+ p->encPtr = encPtr;
+ *encPtr = &(p->initEnc);
+ return 1;
+}
+
+static
+const ENCODING *NS(findEncoding)(const ENCODING *enc, const char *ptr, const char *end)
+{
+#define ENCODING_MAX 128
+ char buf[ENCODING_MAX];
+ char *p = buf;
+ int i;
+ XmlUtf8Convert(enc, &ptr, end, &p, p + ENCODING_MAX - 1);
+ if (ptr != end)
+ return 0;
+ *p = 0;
+ if (streqci(buf, KW_UTF_16) && enc->minBytesPerChar == 2)
+ return enc;
+ i = getEncodingIndex(buf);
+ if (i == UNKNOWN_ENC)
+ return 0;
+ return NS(encodings)[i];
+}
+
+int NS(XmlParseXmlDecl)(int isGeneralTextEntity,
+ const ENCODING *enc,
+ const char *ptr,
+ const char *end,
+ const char **badPtr,
+ const char **versionPtr,
+ const char **encodingName,
+ const ENCODING **encoding,
+ int *standalone)
+{
+ return doParseXmlDecl(NS(findEncoding),
+ isGeneralTextEntity,
+ enc,
+ ptr,
+ end,
+ badPtr,
+ versionPtr,
+ encodingName,
+ encoding,
+ standalone);
+}
diff --git a/src/gen-makefile-am.sh b/src/gen-makefile-am.sh
new file mode 100755
index 0000000..b94270f
--- /dev/null
+++ b/src/gen-makefile-am.sh
@@ -0,0 +1,16 @@
+#! /bin/sh
+#
+# Turns Makefile.am.in into a Makefile.am which can be processed by
+# automake. This is necessary because automake cannot build a list
+# of source files dynamically.
+
+# find expression for files which are in sysync_SDK/Sources but only
+# need to be compiled into libsynthesissdk.a
+SDK_FILES='-name admindata.cpp -o -name admindata.h -o -name blobs.cpp -o -name blobs.h -o -name dbitem.cpp -o -name dbitem.h -o -name enginemodulebridge.cpp -o -name enginemodulebridge.h -o -name stringutil.cpp -o -name stringutil.h -o -name target_options.h -o -name timeutil.cpp -o -name timeutil.h -o -name UI_util.cpp -o -name UI_util.h'
+
+sed -e "s;@LIBSYNTHESIS_SOURCES@;`find sysync DB_interfaces sysync_SDK/Sources Transport_interfaces/engine platform_adapters syncapps/clientEngine_custom \( -name '*.cpp' -o -name '*.[ch]' \) \! \( ${SDK_FILES} -o -name clientprovisioning_inc.cpp -o -name \*_tables_inc.cpp -o -name syncserver.cpp -o -name syncsessiondispatch.cpp \) -printf '%p '`;" \
+ -e "s;@LIBSYNTHESISSDK_SOURCES_BOTH@;`find sysync_SDK/Sources \( -name '*.cpp' -o -name '*.c' \) -printf '%p '`;" \
+ -e "s;@LIBSYNTHESISSDK_SOURCES_SDK_ONLY@;`find sysync_SDK/Sources \( -name '*.cpp' -o -name '*.c' \) -a \( ${SDK_FILES} \) -printf '%p '`;" \
+ -e "s;@LIBSMLTK_SOURCES@;`find syncml_tk \( -name '*.cpp' -o -name '*.[ch]' \) \! \( -wholename syncml_tk/src/sml/\*/palm/\* -o -wholename syncml_tk/src/sml/\*/win/\* \) -printf '%p '`;" \
+ -e "s;@LIBSYNTHESISSDK_HEADERS@;`find sysync_SDK/Sources \( -name '*.h' \) -printf 'synthesis/%f '`;" \
+ Makefile.am.in >Makefile.am
diff --git a/src/global_options.h b/src/global_options.h
new file mode 100644
index 0000000..645cbc2
--- /dev/null
+++ b/src/global_options.h
@@ -0,0 +1,248 @@
+/* SySync entirely global options (affects all targets)
+ * ====================================================
+ *
+ * (c) 2001-2009 by Synthesis AG
+ *
+ */
+
+#ifndef GLOBAL_OPTIONS_H
+#define GLOBAL_OPTIONS_H
+
+// signal that we are compiling with the SYSYNC engine
+#define SYSYNC_ENGINE 1
+
+
+// Expiry date for products (those that have use EXPIRES_AFTER_DATE at all)
+#if defined(EXPIRY_YEAR) && defined(SYSER_REGISTRATION)
+ #error "it seems that this target still defines it's own private expiry date - please update target_options.h"
+#endif
+#if defined(EXPIRY_YEAR) && EXPIRY_YEAR<2009
+ #warning "Target has a dangerously early expiry year - please check if it is correct"
+#endif
+// global expiry date (usually applies for unregistered demos or regular products in trial mode)
+#ifndef EXPIRY_YEAR
+ #define EXPIRY_DATE_STRING "2010-08-31"
+ #define EXPIRY_YEAR 2010
+ #define EXPIRY_MONTH 8
+ #define EXPIRY_DAY 31
+#endif
+
+// Release date (date relevant for licenses that are valid only up to a certain release date)
+#if !defined(RELEASE_YEAR) || !defined(RELEASE_MONTH)
+ // define one globally in case target or product does not specify it's own date
+ #define RELEASE_YEAR 2008
+ #define RELEASE_YEAR_TXT "2008"
+ #define RELEASE_MONTH 1
+ #define RELEASE_MONTH_TXT "1"
+#endif
+
+
+// For old targets that do not have product_options.h yet
+// - Set common Version information
+#ifndef SYSYNC_VERSION_MAJOR
+#define SYSYNC_VERSION_MAJOR 3
+#define SYSYNC_VERSION_MAJOR_TXT "3"
+#endif
+
+#ifndef SYSYNC_VERSION_MINOR
+#define SYSYNC_VERSION_MINOR 2
+#define SYSYNC_VERSION_MINOR_TXT "2"
+#endif
+
+#ifndef SYSYNC_SUBVERSION
+#define SYSYNC_SUBVERSION 0
+#define SYSYNC_SUBVERSION_TXT "0"
+#endif
+
+#ifndef SYSYNC_BUILDNUMBER
+#define SYSYNC_BUILDNUMBER 25
+#define SYSYNC_BUILDNUMBER_TXT "25"
+#endif
+
+
+// Platform name
+#ifndef SYSYNC_PLATFORM_NAME
+ #if defined(__EPOC_OS__)
+ #define SYSYNC_PLATFORM_NAME "SymbianOS"
+ #elif defined(MOBOSX)
+ #define SYSYNC_PLATFORM_NAME "iPhoneOS"
+ #elif defined(MACOSX)
+ #define SYSYNC_PLATFORM_NAME "MacOSX"
+ #elif defined(LINUX)
+ #define SYSYNC_PLATFORM_NAME "Linux"
+ #elif defined(__PALM_OS__)
+ #define SYSYNC_PLATFORM_NAME "PalmOS"
+ #elif defined(WINCE)
+ #define SYSYNC_PLATFORM_NAME "WinCE"
+ #elif defined(_WIN32)
+ #define SYSYNC_PLATFORM_NAME "Win32"
+ #endif
+#endif
+
+
+// product version string
+#define SYSYNC_VERSION_STRING SYSYNC_VERSION_MAJOR_TXT "." SYSYNC_VERSION_MINOR_TXT "." SYSYNC_SUBVERSION_TXT
+// full version string
+#define SYSYNC_FULL_VERSION_STRING SYSYNC_VERSION_MAJOR_TXT "." SYSYNC_VERSION_MINOR_TXT "." SYSYNC_SUBVERSION_TXT "." SYSYNC_BUILDNUMBER_TXT
+// full version string
+#define SYSYNC_MAIN_VERSION_STRING SYSYNC_VERSION_MAJOR_TXT "." SYSYNC_VERSION_MINOR_TXT
+// uInt32 format of version (8 bits per dotted group)
+#define SYSYNC_VERSION_UINT32 (((uInt32)SYSYNC_VERSION_MAJOR<<24)+((uInt32)SYSYNC_VERSION_MINOR<<16)+((uInt32)SYSYNC_SUBVERSION<<8)+(uInt32)SYSYNC_BUILDNUMBER)
+
+
+// client and server strings for XPT-based apps
+#define SERVER_NAME "Synthesis SyncML Server/" SYSYNC_FULL_VERSION_STRING
+#define CLIENT_NAME "Synthesis SyncML Client/" SYSYNC_FULL_VERSION_STRING " [en] (" SYSYNC_PLATFORM_NAME "; I)"
+
+// FPI product identifier
+#define SYSYNC_FPI "-//Synthesis AG//NONSGML SyncML Engine V" SYSYNC_FULL_VERSION_STRING "//EN"
+
+
+// Global feature switches (might be overridden in individual targets)
+// ###################################################################
+
+
+// build type
+#ifdef ENGINE_LIBRARY
+ // SySync engine with API
+ // - may not have any globals
+ #undef DIRECT_APPBASE_GLOBALACCESS
+ // - has Engine Interface
+ #define ENGINEINTERFACE_SUPPORT 1
+ // - should support constant XML config
+ #define CONSTANTXML_CONFIG 1
+#else
+ // Classic SySync build
+ // - needs direct accessors to syncAppBase and maybe other global vars
+ #define DIRECT_APPBASE_GLOBALACCESS 1
+#endif
+
+
+// default datatype support
+#define MIMEDIR_SUPPORT 1
+#define TEXTTYPE_SUPPORT 1
+#define DATAOBJ_SUPPORT 1
+
+// intermediate stuff for v3.x development
+/// @todo
+/// the following defines are for v3.x development only and must be cleared out or moved to a permanent location for a final version!
+
+// default to ODBC support (if and only if SQL_SUPPORT is selected in the target)
+#define ODBCAPI_SUPPORT 1
+
+// default to both text and AsKey variants of item passing in Api datastores
+#define DBAPI_TEXTITEMS 1
+#define DBAPI_ASKEYITEMS 1
+
+
+// SyncML version support
+#define MAX_SYNCML_VERSION syncml_vers_1_2
+
+#ifdef SYSYNC_CLIENT
+ // these are normally client options, servers don't have them by default
+
+ // client provides GUI suport for CGI server options such as TAF and/or /li(x) /dr(x,y)...
+ #define CGI_SERVER_OPTIONS 1
+
+ #undef CHINESE_SUPPORT
+
+#else
+ // these are normally server options, clients don't have them by default
+
+ // include large-footprint conversions between chinese and UTF-8
+ #define CHINESE_SUPPORT 1
+
+ // if defined, TAF option chars will be used like syncset filter /fi(...)
+ // (that's how it always was before 1.0.8.9 server)
+ // Was globally defined 1 until and including server 2.0.5.12
+ // Starting with 2.0.5.13 server, real TAF is now (partially) available, so we
+ // switch this off
+ #undef TAF_AS_SYNCSETFILTER
+ // real SyncML-like TAF
+ #define SYNCML_TAF_SUPPORT 1
+
+ // if defined, we support Synthesis style target options (/opt(args) style)
+ #define SYSYNC_TARGET_OPTIONS 1
+
+ // object is enabled by default for now (standard version turns it off)
+ #define OBJECT_FILTERING 1
+
+ // array field support is enabled by default for now (standard version turns it off)
+ #define ARRAYFIELD_SUPPORT 1
+
+ // array field support is enabled by default for now (standard version turns it off)
+ #define STREAMFIELD_SUPPORT 1
+
+ // full support for email in textitem
+ #define EMAIL_FORMAT_SUPPORT 1
+ #define EMAIL_ATTACHMENT_SUPPORT 1
+
+ // script support is enabled by default for now (standard version turns it off)
+ #define SCRIPT_SUPPORT 1
+ #define REGEX_SUPPORT 1
+
+ // superdatastore support is enabled by default
+ #define SUPERDATASTORES 1
+
+#endif
+
+
+
+// ODBC not compatible with 1.0.5.x type config any more
+#undef OLD_1_0_5_CONFIG_COMPATIBLE
+
+
+// Settings that have proven globally correct by now
+// #################################################
+
+// SySync options
+// ==============
+
+// s2g does not need these extras any more, everything is solved with scripts
+// #define SPACE2GO_EXTRAS 1
+
+// use new space evaluation method
+#define USE_SML_EVALUATION 1
+
+// do not modify remote IDs in any way while processing them
+#define DONT_STRIP_PATHPREFIX_FROM_REMOTEIDS 1
+
+// Time options
+// - if defined, querying current lineartime will return millisecond
+// accuray value.
+#define NOW_WITH_MILLISECONDS 1
+// activate the new timezone system
+#define NEW_TIMEZONES 1
+
+// - if defined, code for incoming and outgoing SyncML dumping into (WB)XML logfiles is included
+#define MSGDUMP 1
+
+// - if defined, code for incoming message simulation from "i_" prefixed incoming message dump file is included
+#undef SIMMSGREAD
+
+// - allow keep connection
+#define HTTP_KEEP_CONNECTION 1
+
+
+// SyncML Toolkit options
+// ======================
+
+// if defined, the entire complicated and thread-unsafe workspace manager
+// is completely bypassed
+#define NOWSM 1
+
+/* correct tagging of XML payload with <![CDATA[ */
+#define PCDATA_OPAQUE_AS_CDATA 1
+
+// allow Alert w/o Itemlist
+#define SYNCFEST_ALLOW_ALERT_WITHOUT_ITEMLIST 1
+
+
+// PCRE options
+// ============
+
+#define PCRE_STATIC 1 // no special declaration, PCRE is compiled-in
+
+#endif // GLOBAL_OPTIONS_H
+
+/* eof */
diff --git a/src/platform_adapters/binfile.cpp b/src/platform_adapters/binfile.cpp
new file mode 100755
index 0000000..fcc47a7
--- /dev/null
+++ b/src/platform_adapters/binfile.cpp
@@ -0,0 +1,147 @@
+/**
+ * @File binfile.cpp
+ *
+ * @Author Lukas Zeller (luz@synthesis.ch)
+ *
+ * @brief TBinFile
+ * Standard C file implementation of TBinFile
+ *
+ * Copyright (c) 2003-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ * @Date 2006-03-28 : luz : created
+ */
+/*
+ */
+
+#include "prefix_file.h"
+#include "binfile.h"
+
+#ifdef WINCE
+#include "win32_utils.h"
+#endif
+
+namespace sysync {
+
+
+// @brief constructor
+TBinFile::TBinFile() :
+ fCBinFile(NULL)
+{
+} // TBinFile
+
+
+// @brief destructor
+TBinFile::~TBinFile()
+{
+ destruct();
+} // TBinFile::~TBinFile
+
+
+// @brief test if platform file open
+bool TBinFile::platformFileIsOpen(void)
+{
+ return fCBinFile!=NULL;
+} // TBinFile::platformFileIsOpen
+
+
+/// @brief Open a platform file
+bool TBinFile::platformOpenFile(cAppCharP aFilePath, TFileOpenModes aMode)
+{
+ switch (aMode) {
+ case fopm_update :
+ fCBinFile = fopen(aFilePath,"r+b");
+ break;
+ case fopm_create :
+ fCBinFile = fopen(aFilePath,"w+b");
+ break;
+ }
+ return fCBinFile!=NULL;
+} // TBinFile::platformOpenFile
+
+
+/// @brief close file
+bool TBinFile::platformCloseFile(void)
+{
+ fclose(fCBinFile);
+ fCBinFile=NULL;
+ return true;
+} // TBinFile::platformCloseFile
+
+
+/// @brief seek in file
+bool TBinFile::platformSeekFile(uInt32 aPos, bool aFromEnd)
+{
+ return fseek(fCBinFile,aPos,aFromEnd ? SEEK_END : SEEK_SET) == 0;
+} // TBinFile::platformSeekFile
+
+
+/// @brief read from file
+bool TBinFile::platformReadFile(void *aBuffer, uInt32 aMaxRead)
+{
+ return fread(aBuffer,aMaxRead,1,fCBinFile) == 1;
+} // TBinFile::platformReadFile
+
+
+/// @brief write to file
+bool TBinFile::platformWriteFile(const void *aBuffer, uInt32 aBytes)
+{
+ return fwrite(aBuffer,aBytes,1,fCBinFile) == 1;
+} // TBinFile::platformWriteFile
+
+
+/// @brief flush all buffers
+bool TBinFile::platformFlushFile(void)
+{
+ return fflush(fCBinFile)==0;
+} // TBinFile::platformFlushFile
+
+
+/// @brief truncate file to a specific length
+bool TBinFile::platformTruncateFile(uInt32 aNewSize)
+{
+ #if defined(LINUX) || defined(MACOSX)
+ fflush(fCBinFile); // unbuffer everything
+ int fd = fileno(fCBinFile); // get file descriptor
+ if (ftruncate(fd,aNewSize))
+ ; // error ignored
+ #elif defined(WIN32)
+ fflush(fCBinFile); // unbuffer everything
+ HANDLE h = (HANDLE)fileno(fCBinFile); // get file descriptor
+ SetFilePointer(h,aNewSize,NULL,FILE_BEGIN);
+ SetEndOfFile(h);
+ #elif defined(__EPOC_OS__)
+ #ifdef RELEASE_VERSION
+ #warning "%%% we need file truncation for symbian here"
+ #endif
+ return false;
+ #else
+ #error "file truncation not implemented for this platform"
+ #endif
+ // successfully truncated
+ return true;
+} // TBinFile::platformTruncateFile
+
+
+/// @delete file entirely
+bool TBinFile::platformDeleteFile(cAppCharP aFilePath)
+{
+ #if defined(LINUX) || defined(MACOSX)
+ unlink(aFilePath);
+ #elif defined(WINCE)
+ TCHAR filename[MAX_PATH];
+ UTF8toWCharBuf(aFilePath,filename,MAX_PATH);
+ DeleteFileW(filename);
+ #elif defined(WIN32)
+ unlink(aFilePath);
+ #else
+ #error "file delete not implemented for this platform"
+ #endif
+ // successfully deleted
+ return true;
+} // TBinFile::platformDeleteFile
+
+
+
+} // namespace sysync
+
+// eof
diff --git a/src/platform_adapters/binfile.h b/src/platform_adapters/binfile.h
new file mode 100755
index 0000000..72ac351
--- /dev/null
+++ b/src/platform_adapters/binfile.h
@@ -0,0 +1,55 @@
+/**
+ * @File binfile.h
+ *
+ * @Author Lukas Zeller (luz@synthesis.ch)
+ *
+ * @brief TBinFile
+ * Standard C file implementation of TBinFile
+ *
+ * Copyright (c) 2003-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ * @Date 2006-03-28 : luz : created
+ */
+/*
+ */
+
+#ifndef BINFILE_H
+#define BINFILE_H
+
+#include "binfilebase.h"
+
+namespace sysync {
+
+class TBinFile : public TBinFileBase
+{
+ typedef TBinFileBase inherited;
+public:
+ TBinFile();
+ virtual ~TBinFile();
+protected:
+ // Platform file implementation abstraction
+ // - test if platform file open
+ virtual bool platformFileIsOpen(void);
+ // - open file
+ virtual bool platformOpenFile(cAppCharP aFilePath, TFileOpenModes aMode);
+ // - close file
+ virtual bool platformCloseFile(void);
+ // - seek in file
+ virtual bool platformSeekFile(uInt32 aPos, bool aFromEnd=false);
+ // - read from file
+ virtual bool platformReadFile(void *aBuffer, uInt32 aMaxRead);
+ // - write to file
+ virtual bool platformWriteFile(const void *aBuffer, uInt32 aBytes);
+ // - flush all buffers
+ virtual bool platformFlushFile(void);
+ // - truncate file to a specific length
+ virtual bool platformTruncateFile(uInt32 aNewSize);
+ // - delete file entirely
+ virtual bool platformDeleteFile(cAppCharP aFilePath);
+private:
+ FILE *fCBinFile;
+}; // TBinFile
+
+} // namespace sysync
+
+#endif // BINFILE_H
diff --git a/src/platform_adapters/configfiles.h b/src/platform_adapters/configfiles.h
new file mode 100755
index 0000000..0b1d94b
--- /dev/null
+++ b/src/platform_adapters/configfiles.h
@@ -0,0 +1,93 @@
+/*
+ * File: configfiles.h
+ *
+ * Author: Lukas Zeller (luz@synthesis.ch)
+ *
+ * Platform adaptors for accessing config files
+ *
+ * Copyright (c) 2002-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ * 2002-11-18 : luz : created
+ *
+ *
+ */
+
+// conditional field length specs for Xprintf
+#ifdef __PALM_OS__
+ // PalmOS do not support all formatting
+ #define FMT_LENGTH(n)
+ #define FMT_LENGTH_LIMITED(n,p) ((string(p,strlen(p)>n ? n : strlen(p))).c_str())
+ #define PRINTF_LLD "%ld"
+ #define PRINTF_LLD_ARG(x) ((long)x)
+ #ifdef NO64BITINT
+ #define LONGLONGTOSTR(s,ll) %%%%%%% // cause compiling error
+ #else
+ #define LONGLONGTOSTR(s,ll) LongLongToStr(s,ll)
+ // need a function to do that
+ void LongLongToStr(string &s, sInt64 ll);
+ #endif
+#else
+ #define PRINTF_LLD "%lld"
+ #define PRINTF_LLD_ARG(x) static_cast<long long>(x)
+ #define FMT_LENGTH(n) n
+ #define FMT_LENGTH_LIMITED(n,p) p
+ #define LONGLONGTOSTR(s,ll) StringObjPrintf(s,"%lld",ll)
+#endif
+
+/* %%% replaced by getPlatformString()
+// get default paths
+// - for writing logs (when we have no configured log dir)
+bool getDefaultWritePath(string &aString);
+// - for reading local config (first try)
+bool getDefaultLocalConfigPath(string &aString);
+// - for reading global config (second try)
+bool getDefaultGlobalConfigPath(string &aString);
+*/
+
+
+// IDs to request platform-specific strings (mostly paths)
+// Note: paths do NOT have a trailing separator!
+typedef enum {
+ pfs_platformvers, // version string of the current platform (as from getFirmwareVersion())
+ pfs_globcfg_path, // global system-wide config path (such as C:\Windows or /etc)
+ pfs_loccfg_path, // local config path (such as exedir or user's dir)
+ pfs_defout_path, // default path to writable directory to write default logs
+ pfs_temp_path, // path where we can write temp files
+ pfs_exedir_path, // path to directory where executable resides
+ pfs_userdir_path, // path to the user's home directory for user-visible documents and files
+ pfs_appdata_path, // path to the user's preference directory for this application
+ pfs_prefs_path, // path to directory where all application prefs reside (not just mine)
+ pfs_device_uri, // URI of the device (as from former getDeviceInfo)
+ pfs_device_name, // Name of the device (as from former getHardwareVersion())
+ pfs_user_name, // Name of the current user
+ numPlatformStrings
+} TPlatformStringID;
+
+/// @brief get platform specific string by ID
+/// @return false if string is not available for this platform
+/// @param aStringID string selector ID
+/// @param aString will receive the requested string
+bool getPlatformString(TPlatformStringID aStringID, string &aString);
+
+
+/// @brief update string such that it can be used as target OS directory path
+/// (filename can be appended without any separator)
+/// @param aPath path to be updated
+/// @param aMakeDirs if set, directories along path are created if not existing
+/// @return false if directory does not exist or could not be created (when aMakeDirs is set)
+bool makeOSDirPath(string &aPath, bool aMakeDirs=false);
+
+
+// returns timestamp (UTC) of last file modification or 0 if none known
+lineartime_t getFileModificationDate(const char *aFileName);
+
+// get local device URI (returns false if ID cannot be guaranteed unique)
+bool getLocalDeviceID(string &aURI);
+
+
+// write to platform's "console", whatever that is
+void PlatformConsolePuts(const char *aText);
+
+
+
+/* eof */
diff --git a/src/platform_adapters/linux/configfiles.cpp b/src/platform_adapters/linux/configfiles.cpp
new file mode 100755
index 0000000..afb06ee
--- /dev/null
+++ b/src/platform_adapters/linux/configfiles.cpp
@@ -0,0 +1,240 @@
+/*
+ * File: configfiles.cpp
+ *
+ * Author: Lukas Zeller (luz@synthesis.ch)
+ *
+ * Linux Platform adaptor for accessing config files
+ *
+ * Copyright (c) 2002-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ * 2002-11-18 : luz : created
+ *
+ *
+ */
+
+#include "sysync.h"
+
+#include <sys/types.h>
+#include <pwd.h>
+
+
+// determine OS version
+static bool getOSVersion(string &aOSVersion)
+{
+ // Obtain OS Version
+ //%%%% for now
+ aOSVersion="unknown";
+ return false;
+} // getOSVersion
+
+
+// determine hardware name
+static bool getHardwareName(string &aHardwareName)
+{
+ // Obtain Device name
+ aHardwareName="Linux PC";
+ return true;
+} // getHardwareName
+
+
+/// @brief get platform specific string by ID
+/// @return false if string is not available for this platform
+/// @param aStringID string selector ID
+/// @param aString will receive the requested string
+bool getPlatformString(TPlatformStringID aStringID, string &aString)
+{
+ const sInt16 bufsiz=1024;
+ char buffer[bufsiz];
+ buffer[0]=0; // terminate for safety
+ string str;
+ struct passwd *userInfoP=NULL;
+
+ switch (aStringID) {
+ case pfs_platformvers:
+ // Platform OS version string
+ getOSVersion(aString);
+ break;
+ case pfs_device_uri:
+ getLocalDeviceID(aString);
+ break;
+ case pfs_device_name:
+ getHardwareName(aString);
+ break;
+ case pfs_loccfg_path:
+ #ifdef STANDALONE_APP
+ // default path for local config for XPT standalones is the current dir
+ aString = ".";
+ break;
+ #endif
+ // for server modules, it is same as the global config path
+ #ifndef STANDALONE_APP
+ case pfs_globcfg_path:
+ // global config directory path
+ aString = "/etc";
+ break;
+ #endif
+ case pfs_defout_path:
+ // default output path (formerly: default write path)
+ #ifdef STANDALONE_APP
+ // default path for local config for XPT standalones is the current dir
+ aString = ".";
+ #else
+ // for server modules, default log path is /var/log
+ aString = "/var/log";
+ #endif
+ break;
+ case pfs_temp_path:
+ // temp path
+ aString = "/temp";
+ break;
+ case pfs_userdir_path:
+ // user's home dir for user-visible documents and files
+ userInfoP = getpwuid(getuid());
+ aString = userInfoP->pw_dir; // home dir
+ break;
+ #ifdef APPDATA_SUBDIR
+ case pfs_appdata_path:
+ // My specific subdirectory for storing my app data/prefs
+ userInfoP = getpwuid(getuid());
+ aString = userInfoP->pw_dir; // user home dir
+ aString += "/.sysync/" APPDATA_SUBDIR; // application specific subdir
+ break;
+ #endif
+ /*
+ case pfs_prefs_path:
+ // general directory where all applications store data & prefs (for current user)
+ aString = %%%
+ break;
+ */
+ /*
+ case pfs_exedir_path:
+ exedir:
+ // path where module file resides
+ aString = %%%
+ break;
+ */
+ default:
+ // unknown ID
+ return false;
+ }
+ // ok
+ return true;
+} // getPlatformString
+
+
+extern "C" {
+ #include <ctime>
+ #include <sys/stat.h>
+}
+
+/// @brief update string such that it can be used as target OS directory path
+/// (filename can be appended without any separator)
+/// @param aPath path to be updated
+/// @param aMakeDirs if set, directories along path are created if not existing
+/// @return false if directory does not exist or could not be created (when aMakeDirs is set)
+bool makeOSDirPath(string &aPath, bool aMakeDirs)
+{
+ // make sure path ends with backslash
+ string::size_type n=aPath.size();
+ if (n>=1 && aPath[n-1]!='/')
+ aPath+='/';
+ // now make sure path exists if requested
+ if (aMakeDirs) {
+ // no slash at the end
+ string tmppath;
+ tmppath.assign(aPath,0,aPath.size()-1);
+ // optimization check if entire path exists
+ struct stat statinfo;
+ int rc=stat(aPath.c_str(),&statinfo);
+ if(rc!=0 || !S_ISDIR(statinfo.st_mode)) {
+ rc = errno;
+ // path does not exist yet - start from beginning to create it
+ n=0;
+ bool knownmissing=false;
+ // skip first slash for absolute paths
+ if (aPath[0]=='/') n+=1; // skip
+ do {
+ // find first directory in path
+ n=aPath.find("/",n);
+ // if no more separators to find, all dirs exist now
+ if (n==string::npos) break;
+ tmppath.assign(aPath,0,n);
+ n+=1; // skip separator
+ if (!knownmissing) {
+ // test if this dir exists
+ rc=stat(tmppath.c_str(),&statinfo);
+ if(rc!=0 || !S_ISDIR(statinfo.st_mode)) {
+ rc = errno;
+ knownmissing=true;
+ }
+ }
+ if (knownmissing) {
+ // create the subdir (might fail if part of path already exists)
+ if (mkdir(tmppath.c_str(),S_IRWXU)!=0)
+ return false; // failure to create directory
+ }
+ } while(true);
+ } // path does not exist yet entirely
+ } // make path existing
+ return true;
+} // makeOSDirPath
+
+
+// returns timestamp (UTC) of last file modification or 0 if none known
+lineartime_t getFileModificationDate(const char *aFileName)
+{
+ struct stat st;
+ lineartime_t res;
+
+ if (stat(aFileName,&st)!=0) res=0;
+ else {
+ // stat ok, get modification date
+ res= ((lineartime_t)st.st_mtime * secondToLinearTimeFactor) + UnixToLineartimeOffset;
+ }
+ // return timestamp
+ return res;
+} // getFileModificationDate
+
+
+#include <netdb.h>
+#include <unistd.h>
+
+// get local device URI/name info
+bool getLocalDeviceID(string &aURI)
+{
+ char szHostname[100];
+ struct hostent *pHostEnt=NULL;
+ string hostName;
+
+ // get name of own machine
+ if (gethostname(szHostname, sizeof(szHostname))!=0) {
+ hostName="_unknown_";
+ }
+ else {
+ // get host entry
+ pHostEnt = gethostbyname(szHostname);
+ // return fully qualified name of machine as ID
+ if (pHostEnt)
+ hostName=pHostEnt->h_name; // DNS name of machine
+ else
+ hostName=szHostname; // just name of machine
+ }
+ // generate URI from name
+ aURI="linux:"; // %%% SCTS does not like http:// here, so we take os:xxxx
+ // add name of this machine (fully qualified if possible)
+ aURI+=hostName;
+ // this is more or less unique
+ return true;
+} // getLocalDeviceID
+
+
+// write to platform's "console", whatever that is
+void PlatformConsolePuts(const char *aText)
+{
+ // generic output
+ puts(aText); // appends newline itself
+} // PlatformConsolePuts
+
+
+
+/* eof */
diff --git a/src/platform_adapters/linux/ctype b/src/platform_adapters/linux/ctype
new file mode 100755
index 0000000..b20896b
--- /dev/null
+++ b/src/platform_adapters/linux/ctype
@@ -0,0 +1,2 @@
+// platform has no new C++ type header for this include file
+#include <ctype.h>
diff --git a/src/platform_adapters/linux/platform_DLL.cpp b/src/platform_adapters/linux/platform_DLL.cpp
new file mode 100755
index 0000000..3f5bb63
--- /dev/null
+++ b/src/platform_adapters/linux/platform_DLL.cpp
@@ -0,0 +1,142 @@
+/*
+ * File: platform_DLL.cpp
+ *
+ * Author: Beat Forster (bfo@synthesis.ch)
+ *
+ *
+ * General interface to access the routines
+ * of a DLL (LINUX platform)
+ *
+ * Copyright (c) 2004-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ *
+ */
+#define DLOPEN_NO_WARN 1
+#define GNU_SOURCE 1
+#include <dlfcn.h> // the Linux DLL functionality
+
+#include "platform_DLL.h"
+#include <string>
+#include <stdio.h>
+
+using namespace std;
+
+/**
+ * Instances of this class are returned
+ * as DLL handles to the caller of ConnectDLL.
+ *
+ * Loadable modules are opened dlopen() and
+ * functions are found via dlsym(). If the
+ * platform supports RTLD_DEFAULT and finds
+ * a <DLName>_Module_Version, then dlopen()
+ * isn't used and all functions are searched
+ * via RTLD_DEFAULT.
+ */
+class DLWrapper {
+ /**
+ * name of shared object, including suffix,
+ * or function prefix (when functions were
+ * linked in)
+ */
+ string aDLLName;
+ /** dlopen() result, NULL if not opened */
+ void *aDLL;
+
+public:
+ DLWrapper(const char *name) :
+ aDLLName(name),
+ aDLL(NULL)
+ {}
+
+ /** check for linked function or open shared object,
+ return true for success */
+ bool connect()
+ {
+#ifdef RTLD_NEXT
+ string fullname = aDLLName + "_Module_Version";
+ if (dlsym(RTLD_DEFAULT, fullname.c_str()))
+ return true;
+#endif
+
+ const char* DSuff= ".so";
+ string lName;
+ string aName= aDLLName;
+
+ do {
+ aName+= DSuff;
+ aDLL = dlopen(aName.c_str(), RTLD_LAZY); if (!dlerror()) break;
+
+ lName= "./"; lName+= aName; // try Linux current path as well
+ aDLL = dlopen( lName.c_str(), RTLD_LAZY ); if (!dlerror()) break;
+ aDLL = dlopen( aDLLName.c_str(), RTLD_LAZY ); if (!dlerror()) break;
+
+ lName= "./"; lName+= aDLLName; // try Linux current path as well
+ aDLL = dlopen( lName.c_str(), RTLD_LAZY );
+ } while (false);
+
+ return !dlerror();
+ }
+
+ bool destroy()
+ {
+ bool ok = true;
+ if (this) {
+ if (aDLL) {
+ int err = dlclose(aDLL);
+ ok = !err;
+ }
+ delete this;
+ }
+ return ok;
+ }
+
+ bool function(const char *aFuncName, void *&aFunc)
+ {
+#ifdef RTLD_DEFAULT
+ if (!aDLL) {
+ string fullname = aDLLName + '_' + aFuncName;
+ aFunc = dlsym(RTLD_DEFAULT, fullname.c_str());
+ } else
+#endif
+ aFunc = dlsym(aDLL, aFuncName);
+ return aFunc!=NULL && dlerror()==NULL;
+ }
+};
+
+bool ConnectDLL( void* &aDLL, const char* aDLLname, ErrReport aReport, void* ref )
+/* Connect to <aDLLname>, result is <aDLL> reference */
+/* Returns true, if successful */
+{
+ DLWrapper *wrapper = new DLWrapper(aDLLname);
+ bool ok = false;
+ if (wrapper) {
+ ok = wrapper->connect();
+ if (ok)
+ aDLL = (void *)wrapper;
+ else
+ delete wrapper;
+ }
+
+ if (!ok && aReport) aReport( ref, aDLLname );
+ return ok;
+} // ConnectDLL
+
+
+
+bool DisconnectDLL( void* aDLL )
+/* Disconnect <hDLL> */
+{
+ return ((DLWrapper *)aDLL)->destroy();
+} // DisonnectDLL
+
+
+
+bool DLL_Function( void* aDLL, const char* aFuncName, void* &aFunc )
+/* Get <aFunc> of <aFuncName> */
+/* Returns true, if available */
+{
+ return ((DLWrapper *)aDLL)->function(aFuncName, aFunc);
+} // DLL_Function
+
+
+/* eof */
diff --git a/src/platform_adapters/linux/platform_exec.c b/src/platform_adapters/linux/platform_exec.c
new file mode 100755
index 0000000..d5981c8
--- /dev/null
+++ b/src/platform_adapters/linux/platform_exec.c
@@ -0,0 +1,92 @@
+/*
+ * File: platform_exec.c
+ *
+ * Author: Lukas Zeller (luz@synthesis.ch)
+ *
+ * Platform specific implementation for executing external commands
+ *
+ * Copyright (c) 2004-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ * 2004-07-21 : luz : created
+ *
+ */
+
+#include "platform_exec.h"
+
+#include <sys/wait.h>
+#include <stdlib.h>
+#include <string.h>
+
+
+// returns -1 if command could not be started, exit code of the command otherwise.
+// if used with aBackground==true, the return code is always 0 in case
+sInt32 shellExecCommand(cAppCharP aCommandName, cAppCharP aCommandParams, int aBackground)
+{
+ int status;
+ uInt32 cmdlen;
+ char *cmd;
+
+ if (!aCommandName || *aCommandName==0) return 0; // no command -> exec "successful"
+ // simply use system()
+ // - prepare command line
+ cmdlen=
+ strlen(aCommandName) +
+ (aCommandParams ? strlen(aCommandParams) : 0) +
+ 2; // for separator and terminator
+ cmd=malloc(cmdlen);
+ strcpy(cmd,aCommandName);
+ if (aCommandParams) {
+ strcat(cmd," ");
+ strcat(cmd,aCommandParams);
+ }
+ // - execute
+ status=system(cmd);
+ free(cmd);
+ return status;
+
+ /* does not work ok
+ // fork
+ pid = fork();
+ alarm(0); // make sure we have no alarm running
+ if (pid == -1) return -1; // fork failed
+ if (pid == 0) {
+ // forked process
+ // prepare command line
+ cmdlen=
+ strlen(aCommandName) +
+ (aCommandParams ? strlen(aCommandParams) : 0) +
+ 2; // for separator and terminator
+ cmd=malloc(cmdlen);
+ strcpy(cmd,aCommandName);
+ if (aCommandParams) {
+ strcat(cmd," ");
+ strcat(cmd,aCommandParams);
+ }
+ // prepare for execve
+ argv[0] = "sh";
+ argv[1] = "-c";
+ argv[2] = cmd;
+ argv[3] = 0;
+ envp[1] = 0; // no environment
+ execve("/bin/sh", argv, envp); // replace process, will exit with command's exit code
+ exit(-1); // problem executing
+ }
+ else {
+ // process that called fork()
+ do {
+ if (waitpid(pid, &status, 0) == -1) {
+ // keep waiting if it's EINTR
+ if (errno != EINTR)
+ return -1;
+ }
+ else {
+ // return exit status of executed process
+ return status;
+ }
+ } while(1);
+ }
+ */
+} // shellExecCommand
+
+
+/* eof */
diff --git a/src/platform_adapters/linux/platform_exec.h b/src/platform_adapters/linux/platform_exec.h
new file mode 100755
index 0000000..9a9488d
--- /dev/null
+++ b/src/platform_adapters/linux/platform_exec.h
@@ -0,0 +1,29 @@
+/*
+ * File: platform_exec.h
+ *
+ * Author: Lukas Zeller (luz@synthesis.ch)
+ *
+ * Platform specific implementation for executing external commands
+ *
+ * Copyright (c) 2004-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ * 2004-07-21 : luz : created
+ *
+ */
+
+
+#include <generic_types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// returns -1 if command could not be started, exit code of the command otherwise.
+// if used with aBackground==true, the return code is always 0 in case
+sInt32 shellExecCommand(cAppCharP aCommandName, cAppCharP aCommandParams, int aBackground);
+
+#ifdef __cplusplus
+}
+#endif
+
+/* eof */
diff --git a/src/platform_adapters/linux/platform_headers.h b/src/platform_adapters/linux/platform_headers.h
new file mode 100755
index 0000000..f380f6a
--- /dev/null
+++ b/src/platform_adapters/linux/platform_headers.h
@@ -0,0 +1,37 @@
+/*
+ * File: platform_headers.h
+ *
+ * Author: Lukas Zeller (luz@synthesis.ch)
+ *
+ * Common include files for all platform-related standard headers
+ * (suitable for precompiled headers and/or prefix file)
+ *
+ * Copyright (c) 2003-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ * NOTE: this file is part of the mod_sysync source files.
+ *
+ */
+
+
+#ifndef __PLATFORM_HEADERS_H
+#define __PLATFORM_HEADERS_H
+
+// ANSI C
+
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdlib.h>
+
+// C++
+#ifdef __cplusplus
+
+
+#endif // __cplusplus
+
+
+#endif
+
+// eof
diff --git a/src/platform_adapters/linux/platform_time.cpp b/src/platform_adapters/linux/platform_time.cpp
new file mode 100755
index 0000000..1a53c82
--- /dev/null
+++ b/src/platform_adapters/linux/platform_time.cpp
@@ -0,0 +1,82 @@
+/*
+ * File: platform_time.cpp
+ *
+ * Platform specific time implementations
+ *
+ * Author: Lukas Zeller (luz@synthesis.ch)
+ *
+ * Copyright (c) 2002-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ * 2004-11-15 : luz : extracted from lineartime.h
+ */
+
+#include "prefix_file.h"
+
+#include "lineartime.h"
+#include "timezones.h"
+
+// Linux uses standard c time stuff
+//#warning "probably better use <cmath> here"
+#include <climits>
+#include <cstdarg>
+#include <cstdlib>
+#include <cstdio>
+#include <cstring>
+// - Linux specific time stuff
+#include <sys/time.h>
+// - we need some library extras
+#define __USE_MISC 1
+#include <ctime>
+
+namespace sysync {
+
+
+
+/// @brief get system real time
+/// @return system's real time in lineartime_t scale, in specified time zone context
+/// @param[in] aTimeContext desired output time zone
+lineartime_t getSystemNowAs(timecontext_t aTimeContext,GZones *aGZones, bool noOffset)
+{
+ #ifdef NOW_WITH_MILLISECONDS
+ // high precision time, UTC based
+ struct timeval tv;
+ struct timezone tz;
+ // gettimeofday return seconds and milliseconds since start of the UNIX epoch
+ gettimeofday(&tv,&tz);
+ lineartime_t systime =
+ (tv.tv_sec*secondToLinearTimeFactor+UnixToLineartimeOffset) +
+ (tv.tv_usec*secondToLinearTimeFactor/1000000);
+ #else
+ // standard precision time (unix time), base is UTC
+ lineartime_t systime =
+ time(NULL)*secondToLinearTimeFactor+UnixToLineartimeOffset;
+ #endif
+ // - return as-is if requested time zone is UTC
+ if (noOffset || TCTX_IS_UTC(aTimeContext))
+ return systime; // return as-is
+ // - convert to requested zone
+ sInt32 aOffsSeconds;
+ if (!TzOffsetSeconds(systime,TCTX_UTC,aTimeContext,aOffsSeconds,aGZones))
+ return noLinearTime; // no time
+ // return time with offset
+ return systime + (aOffsSeconds * secondToLinearTimeFactor);
+} // getSystemNowAs
+
+
+/// @brief fine resolution sleep support
+/// @param[in] aHowLong desired time to wait in lineartime_t units
+void sleepLineartime(lineartime_t aHowLong)
+{
+ // Linux has nanosleep in nanoseconds
+ timespec sleeptime;
+ sleeptime.tv_sec=aHowLong/secondToLinearTimeFactor;
+ sleeptime.tv_nsec=(aHowLong % secondToLinearTimeFactor)*1000000L;
+ nanosleep(&sleeptime,NULL);
+} // sleepLineartime
+
+
+
+
+} // namespace sysync
+
+/* eof */
diff --git a/src/platform_adapters/linux/platform_time.h b/src/platform_adapters/linux/platform_time.h
new file mode 100755
index 0000000..a0df49a
--- /dev/null
+++ b/src/platform_adapters/linux/platform_time.h
@@ -0,0 +1,53 @@
+/*
+ * File: platform_time.h
+ *
+ * Platform specific time implementations
+ *
+ * Author: Lukas Zeller (luz@synthesis.ch)
+ *
+ * Copyright (c) 2002-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ * 2004-11-15 : luz : extracted from lineartime.h
+ */
+
+#ifndef PLATFORM_TIME_H
+#define PLATFORM_TIME_H
+
+//#include <ctime>
+#include "math.h" // for floor() needed in lineartime.cpp
+
+/* obsolete
+using namespace std;
+
+namespace sysync {
+
+// define a type for it
+typedef struct std::tm struct_tm;
+
+// Windows and Linux/MacOSX have UNIX-time compatible origin
+// which is 1970-01-01 00:00:00
+// but as we have 64bit ints, we use our own linear time
+// in milliseconds (=24*60*60*1000*lineardate)
+typedef sInt32 lineardate_t;
+// standard Linux/Win32/MacOSX
+typedef sInt64 lineartime_t;
+const lineartime_t noLinearTime = 0x0; // undefined lineartime value
+const lineartime_t maxLinearTime = 0x7FFFFFFFFFFFFFFFLL; // signed 64 bit
+// date origin definition relative to algorithm's origin -4712-01-01 00:00:00
+const lineardate_t linearDateOriginOffset=0; // no offset
+const sInt16 linearDateOriginWeekday=1; // Monday
+// scaling of lineartime relative to seconds
+const lineartime_t secondToLinearTimeFactor = 1000; // unit is milliseconds
+
+// On Linux, we always need the new timezones implementation
+#define NEW_TIMEZONES 1
+
+// fine resolution sleep support
+void sleepLineartime(lineartime_t aHowLong);
+
+} // namespace sysync
+*/
+
+#endif // PLATFORM_TIME_H
+
+/* eof */
diff --git a/src/platform_adapters/linux/platform_timezones.cpp b/src/platform_adapters/linux/platform_timezones.cpp
new file mode 100644
index 0000000..d0c508f
--- /dev/null
+++ b/src/platform_adapters/linux/platform_timezones.cpp
@@ -0,0 +1,173 @@
+/*
+ * File: platform_timezones.cpp
+ *
+ * Author: Lukas Zeller / Patrick Ohly
+ *
+ * Time zone dependent routines for Linux
+ *
+ * Copyright (c) 2004-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ * 2009-04-02 : Created by Lukas Zeller from timezones.cpp work by Patrick Ohly
+ *
+ */
+
+// must be first in file, everything above is ignored by MVC compilers
+#include "prefix_file.h"
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "timezones.h"
+#include "vtimezone.h"
+
+#ifdef HAVE_LIBICAL
+# ifndef HANDLE_LIBICAL_MEMORY
+# define HANDLE_LIBICAL_MEMORY 1
+# endif
+# include <libical/ical.h>
+#endif
+
+#ifdef LINUX
+ extern char *tzname[ 2 ];
+ #ifndef BSD
+ extern long timezone;
+ #endif
+ extern int daylight;
+#endif
+
+
+namespace sysync {
+
+/*! @brief platform specific loading of time zone definitions
+ * @return true if this list is considered complete (i.e. no built-in zones should be used additionally)
+ * @param[in/out] aGZones : the GZones object where system zones should be loaded into
+ */
+bool loadSystemZoneDefinitions(GZones* aGZones)
+{
+ // load zones from system here
+ #ifdef HAVE_LIBICAL
+ icalarray *builtin = icaltimezone_get_builtin_timezones();
+ for (unsigned i = 0; builtin && i < builtin->num_elements; i++) {
+ icaltimezone *zone = (icaltimezone *)icalarray_element_at(builtin, i);
+ if (!zone)
+ continue;
+ icalcomponent *comp = icaltimezone_get_component(zone);
+ if (!comp)
+ continue;
+ char *vtimezone = icalcomponent_as_ical_string(comp);
+ if (!vtimezone)
+ continue;
+ tz_entry t;
+ string dstName, stdName;
+ if (VTIMEZONEtoTZEntry(
+ vtimezone,
+ t,
+ stdName,
+ dstName,
+ #ifdef SYDEBUG
+ aGZones->getDbgLogger
+ #endif
+ )) {
+ t.ident = "x";
+ t.dynYear = "";
+ // expect Olson /<domain>/<version>/<location> TZIDs and
+ // extract the trailing location (which might contain
+ // several slashes, so a backwards search doesn't work)
+ std::string::size_type off;
+ if (t.name.size() > 2 &&
+ t.name[0] == '/' &&
+ (off = t.name.find('/', 1)) != t.name.npos &&
+ (off = t.name.find('/', off + 1)) != t.name.npos) {
+ t.location = t.name.substr(off + 1);
+ }
+ aGZones->tzP.push_back(t);
+ }
+ #ifdef LIBICAL_MEMFIXES
+ // new-style Evolution libical: memory must be freed by caller
+ free(vtimezone);
+ #endif
+ }
+ #endif // HAVE_LIBICAL
+ // return true if this list is considered complete (i.e. no built-in zones should be used additionally)
+ return false; // we need the built-in zones
+} // loadSystemZoneDefinitions
+
+
+
+/*! @brief get current system time zone
+ * @return true if successful
+ * @param[out] aContext : the time zone context representing the current system time zone.
+ * @param[in] aGZones : the GZones object.
+ */
+bool getSystemTimeZoneContext(timecontext_t &aContext, GZones* aGZones)
+{
+ tz_entry t;
+ bool ok = true;
+
+ tzset();
+ t.name = tzname[ 0 ];
+ if (strcmp( t.name.c_str(),tzname[ 1 ] )!=0) {
+ t.name+= "/";
+ t.name+= tzname[ 1 ];
+ } // if
+
+ //if (isDbg) PNCDEBUGPRINTFX( DBG_SESSION, ( "Timezone: %s", sName.c_str() ));
+
+ // search based on name before potentially using offset search
+ if (TimeZoneNameToContext( t.name.c_str(),aContext, aGZones ))
+ return true; // found, done
+ #if defined USE_TM_GMTOFF
+ else {
+ // We can use tm_gmtoff as fallback when the name computed above doesn't
+ // match: identify offsets, then search based on offsets.
+ time_t now = time(NULL);
+ bool have_dst = false,
+ have_std = false;
+ // start searching for offsets today, moving ahead one week at a time
+ int week = 0;
+ do {
+ struct tm tm;
+ time_t day = now + 60 * 60 * 24 * 7 * week;
+ localtime_r(&day, &tm);
+ if (tm.tm_isdst) {
+ if (!have_dst) {
+ t.biasDST = tm.tm_gmtoff / 60;
+ have_dst = true;
+ }
+ } else {
+ if (!have_std) {
+ t.bias = tm.tm_gmtoff / 60;
+ have_std = true;
+ }
+ }
+ week++;
+ } while ((!have_std || !have_dst) && week <= 54);
+
+ if (have_dst) {
+ if (have_std) {
+ // make biasDST relative to bias
+ t.biasDST -= t.bias;
+ } else {
+ // daylight saving without standard?!
+ }
+ }
+ // search for name based on offsets
+ t.ident="o";
+ if (FoundTZ(t, t.name, aContext, aGZones))
+ goto done;
+ }
+ #endif // USE_TM_GMTOFF
+ // not enough information to create a new time zone below, give up
+ ok = false;
+done:
+ return ok;
+} // getSystemTimeZoneContext
+
+
+
+
+
+} // namespace sysync
+
+/* eof */
diff --git a/src/platform_adapters/linux/profiling.cpp b/src/platform_adapters/linux/profiling.cpp
new file mode 100755
index 0000000..a8abbea
--- /dev/null
+++ b/src/platform_adapters/linux/profiling.cpp
@@ -0,0 +1,32 @@
+/*
+ * File: profiling.cpp
+ *
+ * Author: Lukas Zeller (luz@synthesis.ch)
+ *
+ * Platform-dependent profiling implementation
+ *
+ * Copyright (c) 2002-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ * 2002-10-01 : luz : created
+ *
+ */
+
+#include "profiling.h"
+#include "sysync_globs.h"
+
+#ifdef TIME_PROFILING
+
+#error "No time profiling for linux yet"
+
+#endif // TIME_PROFILING
+
+
+#ifdef MEMORY_PROFILING
+
+#error "No memory profiling for linux yet"
+
+#error "Seems to be dangerous"
+
+#endif // MEMORY_PROFILING
+
+/* eof */
diff --git a/src/platform_adapters/platform_DLL.h b/src/platform_adapters/platform_DLL.h
new file mode 100755
index 0000000..32f2e9b
--- /dev/null
+++ b/src/platform_adapters/platform_DLL.h
@@ -0,0 +1,45 @@
+/*
+ * File: platform_DLL.h
+ *
+ * Author: Beat Forster (bfo@synthesis.ch)
+ *
+ *
+ * General interface to access the routines
+ * of a DLL.
+ *
+ * Copyright (c) 2004-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ *
+ */
+
+#ifndef PLATFORM_DLL_H
+#define PLATFORM_DLL_H
+
+#include "target_options.h"
+#include <stddef.h> // NULL
+#include <string>
+
+using namespace std;
+
+// ------------------------------------------------------------------
+// Error handler procedure type
+typedef void (*ErrReport)( void* ref, const char* aName );
+typedef void (*ErrMReport)( void* ref, const char* aName, const char* aModName );
+
+
+bool ConnectDLL( void* &aMod, const char* aModName, ErrReport aReport, void* ref= NULL );
+/* Connect to <aModName>, result is <aMod> reference */
+/* Returns 0, if successful */
+
+
+bool DLL_Function( void* aMod, const char* aFuncName, void* &aFunc );
+/* Get <aFunc> of <aFuncName> at <aMod> */
+/* Returns 0, if available */
+
+
+bool DisconnectDLL( void* aMod );
+/* Disconnect <aMod>. Returns 0, if operation successful */
+
+
+#endif /* PLATFORM_DLL_H */
+/* eof */
diff --git a/src/platform_adapters/platform_file.h b/src/platform_adapters/platform_file.h
new file mode 100755
index 0000000..59879ba
--- /dev/null
+++ b/src/platform_adapters/platform_file.h
@@ -0,0 +1,49 @@
+/*
+ * File: platform_file.h
+ *
+ * Author: Beat Forster (bfo@synthesis.ch)
+ *
+ *
+ * General interface to get/set file info
+ * like date/attributes/...
+ *
+ * Copyright (c) 2005-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ *
+ */
+
+#ifndef PLATFORM_FILE_H
+#define PLATFORM_FILE_H
+
+#include "sync_dbapidef.h"
+#include <string>
+using namespace std;
+
+
+namespace sysync {
+
+
+/*! File attributes */
+struct TAttr {
+ bool h,s,a,d,w,r,x;
+}; // TAttr
+
+
+/*! File dates */
+struct TDates {
+ /* ISO8601 format, usually as localtime */
+ string created, modified, accessed;
+}; // TDates
+
+/* Get/set attributes */
+TSyError Get_FileAttr( string pathName, TAttr &aAttr, bool &isFolder );
+TSyError Set_FileAttr( string pathName, TAttr aAttr );
+
+/* Get/set file dates */
+TSyError Get_FileDate( string pathName, TDates &aDate );
+TSyError Set_FileDate( string pathName, TDates aDate );
+
+
+} // namespace
+#endif /* PLATFORM_FILE_H */
+/* eof */
diff --git a/src/platform_adapters/platform_headers.h b/src/platform_adapters/platform_headers.h
new file mode 100755
index 0000000..fac91de
--- /dev/null
+++ b/src/platform_adapters/platform_headers.h
@@ -0,0 +1,30 @@
+/*
+ * File: platform_headers.h
+ *
+ * Author: Lukas Zeller (luz@synthesis.ch)
+ *
+ * Common include files for all platform-related standard headers
+ * (suitable for precompiled headers and/or prefix file)
+ *
+ * Copyright (c) 2003-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ * 2003-08-12 : luz : created
+ *
+ */
+
+
+#ifndef __PLATFORM_HEADERS_H
+#define __PLATFORM_HEADERS_H
+
+// ANSI C
+
+#warning "platform_headers.h should be platform-specific!"
+
+// C++
+#ifdef __cplusplus
+
+ // These are the C++ MSL headers
+
+#endif
+
+#endif
diff --git a/src/platform_adapters/platform_mutex.h b/src/platform_adapters/platform_mutex.h
new file mode 100755
index 0000000..ee8cdc3
--- /dev/null
+++ b/src/platform_adapters/platform_mutex.h
@@ -0,0 +1,28 @@
+/*
+ * File: platform_mutex.h
+ *
+ * Author: Beat Forster (bfo@synthesis.ch)
+ *
+ *
+ * Mutex handling
+ *
+ * Copyright (c) 2005-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ *
+ */
+
+#ifndef PLATFORM_MUTEX_H
+#define PLATFORM_MUTEX_H
+
+
+typedef void* MutexPtr_t;
+
+MutexPtr_t newMutex();
+bool lockMutex( MutexPtr_t m );
+bool tryLockMutex( MutexPtr_t m );
+bool unlockMutex( MutexPtr_t m );
+void freeMutex( MutexPtr_t m );
+
+
+#endif /* PLATFORM_MUTEX_H */
+/* eof */
diff --git a/src/platform_adapters/platform_thread.h b/src/platform_adapters/platform_thread.h
new file mode 100755
index 0000000..fe9a052
--- /dev/null
+++ b/src/platform_adapters/platform_thread.h
@@ -0,0 +1,110 @@
+/*
+ * File: platform_thread.h
+ *
+ * Author: Lukas Zeller (luz@synthesis.ch)
+ *
+ * Platform specific thread object implementation
+ *
+ * Copyright (c) 2004-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ * 2004-04-15 : luz : created
+ *
+ */
+
+#ifndef PLATFORM_THREAD_H
+#define PLATFORM_THREAD_H
+
+#include <generic_types.h>
+
+#ifdef _WIN32
+ #include <windows.h>
+#endif
+
+/*
+#ifndef _MSC_VER
+ using namespace sysync;
+#endif
+*/
+
+namespace sysync {
+
+
+// get id of the running process
+uInt32 myProcessID();
+// get id of the running thread
+uInt32 myThreadID();
+
+
+class TThreadObject; // forward
+
+// function executed by thread
+typedef uInt32 (*TThreadFunc)(TThreadObject *aThreadObject, uInt32 aParam);
+
+
+// wrapper class for thread
+class TThreadObject {
+public:
+ // creates thread object. Thread ist not started yet, must use launch() for this
+ TThreadObject();
+ // destroys the thread object
+ virtual ~TThreadObject();
+ // starts thread (or re-starts it again after termination)
+ bool launch(
+ TThreadFunc aThreadFunc=NULL, // the function to execute in the thread
+ uInt32 aThreadFuncParam=0, // a parameter to pass to the thread
+ size_t aStackSize=0, // if 0, default stack size is used
+ bool aAutoDispose=false // if true, the thread object will dispose itself when thread has finished running
+ );
+ // get thread ID
+ uInt32 getid(void);
+ // soft-terminates thread (sets a flag which requests execute() to terminate
+ void terminate(void) { fTerminationRequested=true; };
+ // hard (emergency) terminate (aborts processing on the OS level)
+ void kill(void);
+ // wait for termination of the thread, returns true if so within specified time
+ // negative wait time means waiting infinitely.
+ bool waitfor(sInt32 aMilliSecondsToWait=0);
+ // get exit code of the thread (valid only if thread has already terminated
+ uInt32 exitcode(void) { return fExitCode; };
+ // This method is the thread function itself
+ // - can be derived to create special threads
+ // default behaviour is to call the fThreadFunc with this and fThreadFuncParam
+ virtual uInt32 execute(void);
+ // checks for termination request
+ bool terminationRequested(void) { return fTerminationRequested; };
+private:
+ // thread options
+ uInt32 fStackSize;
+ // the thread function
+ TThreadFunc fThreadFunc;
+ uInt32 fThreadFuncParam;
+ // the termination request flag
+ bool fTerminationRequested;
+ // the exit code
+ uInt32 fExitCode;
+public:
+ // auto disposal of the thread object when thread exits
+ bool fAutoDisposeThreadObj;
+private:
+
+ #ifdef _WIN32
+ // the windows thread
+ HANDLE fWinThreadHandle;
+ DWORD fWinThreadId;
+ #endif
+
+ #if defined LINUX || defined MACOSX
+ // the linux POSIX thread
+public:
+ pthread_t fPosixThread;
+ pthread_mutex_t fDoneCondMutex;
+ pthread_cond_t fDoneCond;
+ bool fTerminated; // really finished
+ #endif
+};
+
+
+} // namespace sysync
+
+#endif // PLATFORM_THREAD_H
+/* eof */
diff --git a/src/platform_adapters/profiling.h b/src/platform_adapters/profiling.h
new file mode 100755
index 0000000..f02e17b
--- /dev/null
+++ b/src/platform_adapters/profiling.h
@@ -0,0 +1,248 @@
+/*
+ * File: profiling.h
+ *
+ * Author: Lukas Zeller (luz@synthesis.ch)
+ *
+ * Platform-dependent profiling implementation
+ *
+ * Copyright (c) 2002-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ * 2002-10-01 : luz : created
+ *
+ */
+
+
+#ifndef _PROFILING_H
+#define _PROFILING_H
+
+#ifdef __cplusplus
+
+//#include "sysync.h"
+#include "generic_types.h"
+#include "lineartime.h"
+#include "sysync_globs.h"
+
+#endif
+
+#ifdef TIME_PROFILING
+
+#ifdef __cplusplus
+
+typedef enum {
+ TP_general,
+ TP_database,
+ TP_configread,
+ TP_scripts,
+ TP_dbgout,
+ TP_none, // does not count
+} TTP_Types;
+const sInt16 numTPTypes = TP_none-TP_general; // do not count TP_none
+
+extern const char * const TP_TypeNames[numTPTypes];
+
+
+// a profiling timer
+typedef struct {
+ long long kerneltime;
+ long long usertime;
+} TTP_Timer;
+
+// profiling info packet
+typedef struct {
+ TTP_Timer fTP_last;
+ TTP_Timer fTP_total;
+ TTP_Timer fTP_times[numTPTypes];
+ bool fTP_started;
+ TTP_Types fTP_lastindex;
+ lineartime_t fTP_startrealtime;
+} TTP_info;
+
+
+// opaque pointer
+typedef void *TTP_infoP;
+
+// initialize time profiling
+void TP_Init(TTP_infoP aTpinfoP);
+
+// start profiling a certain part of the code
+TTP_Types TP_Start(TTP_infoP aTpinfoP, TTP_Types aTimerIdx);
+
+// stop profiling
+void TP_Stop(TTP_infoP aTpinfoP);
+
+// get profiled information
+uInt32 TP_GetSystemMS(TTP_infoP aTpinfoP,TTP_Types aTimerIdx=TP_none);
+uInt32 TP_GetUserMS(TTP_infoP aTpinfoP,TTP_Types aTimerIdx=TP_none);
+uInt32 TP_GetRealtimeMS(TTP_infoP aTpinfoP);
+
+#endif // C++
+
+
+// active macros
+#define TP_DEFINFO(x) TTP_info x;
+#define TP_DEFIDX(i) TTP_Types i
+#define TP_INIT(i) TP_Init(&i)
+#define TP_START(i,x) { if (PDEBUGMASK & DBG_PROFILE) TP_Start(&i,x); }
+#define TP_SWITCH(l,i,x) { if (PDEBUGMASK & DBG_PROFILE) l=TP_Start(&i,x); }
+#define TP_STOP(i) { if (PDEBUGMASK & DBG_PROFILE) TP_Stop(&i); }
+#define TP_GETSYSTEMMS(i,x) TP_GetSystemMS(&i,x)
+#define TP_GETUSERMS(i,x) TP_GetUserMS(&i,x)
+#define TP_GETTOTALSYSTEMMS(i) TP_GetSystemMS(&i)
+#define TP_GETTOTALUSERMS(i) TP_GetUserMS(&i)
+#define TP_GETREALTIME(i) TP_GetRealtimeMS(&i)
+
+
+#else // TIME_PROFILING
+
+// dummy macros
+#define TP_DEFINFO(x)
+#define TP_DEFIDX(x)
+#define TP_INIT(i)
+#define TP_START(i,x)
+#define TP_SWITCH(l,i,x)
+#define TP_STOP(i)
+#define TP_GETSYSTEMMS(i,x) 0
+#define TP_GETUSERMS(i,x) 0
+#define TP_GETTOTALSYSTEMMS(i) 0
+#define TP_GETTOTALUSERMS(i) 0
+#define TP_GETREALTIME(i) 0
+
+
+#endif // TIME_PROFILING
+
+
+#ifdef MEMORY_PROFILING
+
+#warning "Probably obsolete and dangerous"
+
+#ifdef __cplusplus
+#define SYSYNC_CDECL "C"
+#else
+#define SYSYNC_CDECL
+#endif
+
+
+extern SYSYNC_CDECL size_t gAllocatedMem;
+extern SYSYNC_CDECL size_t gMaxAllocatedMem;
+extern SYSYNC_CDECL int gMemProfilingInUse;
+
+extern SYSYNC_CDECL void *sysync_malloc(size_t size);
+extern SYSYNC_CDECL void *sysync_realloc(void *mem, size_t newsize);
+extern SYSYNC_CDECL void sysync_free(void *mem);
+
+#ifdef __cplusplus
+
+void* operator new (std::size_t size) throw(std::bad_alloc);
+void* operator new (std::size_t size, const std::nothrow_t&) throw();
+
+void* operator new[](std::size_t size) throw(std::bad_alloc);
+void* operator new[](std::size_t size, const std::nothrow_t&) throw();
+
+void operator delete(void* ptr) throw();
+void operator delete(void* ptr, const std::nothrow_t&) throw();
+
+void operator delete[](void* ptr) throw();
+void operator delete[](void* ptr, const std::nothrow_t&) throw();
+
+
+#ifdef MP_SHOW_NEW_AND_DELETE
+
+#define MP_NEW(p,lvl,msg,x) {\
+ size_t MP_before=gAllocatedMem;\
+ if (!gMemProfilingInUse) {\
+ gMemProfilingInUse=1;\
+ PDEBUGPRINTFX((lvl|DBG_PROFILE),(\
+ "+++ %-30s : before calling new: %10ld total, %10ld max",\
+ msg,\
+ gAllocatedMem,\
+ gMaxAllocatedMem\
+ ));\
+ gMemProfilingInUse=0;\
+ }\
+ p = new x;\
+ if (!gMemProfilingInUse) {\
+ gMemProfilingInUse=1;\
+ PDEBUGPRINTFX((lvl|DBG_PROFILE),(\
+ "+++ %-30s : %10ld bytes allocated, %10ld total, %10ld max",\
+ msg,\
+ gAllocatedMem-MP_before,\
+ gAllocatedMem,\
+ gMaxAllocatedMem\
+ ));\
+ gMemProfilingInUse=0;\
+ }\
+}
+
+#define MP_RETURN_NEW(t,lvl,msg,x) {\
+ t *obj;\
+ MP_NEW(obj,lvl,msg,x);\
+ return obj;\
+}
+
+#define MP_DELETE(lvl,msg,x) {\
+ size_t MP_before=gAllocatedMem;\
+ if (!gMemProfilingInUse) {\
+ gMemProfilingInUse=1;\
+ PDEBUGPRINTFX((lvl|DBG_PROFILE),(\
+ "--- %-30s : before calling delete: %10ld total, %10ld max",\
+ msg,\
+ gAllocatedMem,\
+ gMaxAllocatedMem\
+ ));\
+ gMemProfilingInUse=0;\
+ }\
+ delete x;\
+ if (!gMemProfilingInUse) {\
+ gMemProfilingInUse=1;\
+ PDEBUGPRINTFX((lvl|DBG_PROFILE),(\
+ "--- %-30s : %10ld bytes freed, %10ld total, %10ld max",\
+ msg,\
+ MP_before-gAllocatedMem,\
+ gAllocatedMem,\
+ gMaxAllocatedMem\
+ ));\
+ gMemProfilingInUse=0;\
+ }\
+}
+
+#else // MP_SHOW_NEW_AND_DELETE
+
+// let new and delete only count, but not show
+#define MP_NEW(p,lvl,msg,x) p = new x
+#define MP_RETURN_NEW(t,lvl,msg,x) return new x
+#define MP_DELETE(lvl,msg,x) delete x
+
+#endif // MP_SHOW_NEW_AND_DELETE
+
+
+#define MP_SHOWCURRENT(lvl,msg) \
+ PDEBUGPRINTFX((lvl|DBG_PROFILE),(\
+ "=== %-30s : Current memory usage: %10ld total, %10ld max",\
+ msg,\
+ gAllocatedMem,\
+ gMaxAllocatedMem\
+ ))
+
+#endif
+
+#else // MEMORY_PROFILING
+
+#define sysync_malloc(m) malloc(m)
+#define sysync_realloc(m,n) realloc(m,n)
+#define sysync_free(m) free(m)
+
+#ifdef __cplusplus
+
+#define MP_NEW(p,lvl,msg,x) p = new x
+#define MP_RETURN_NEW(t,lvl,msg,x) return new x
+#define MP_DELETE(lvl,msg,x) delete x
+#define MP_SHOWCURRENT(lvl,msg)
+
+#endif
+
+#endif // MEMORY_PROFILING
+
+
+#endif // _PROFILING_H
+
+/* eof */
diff --git a/src/platform_adapters/sysync_glob_vars.h b/src/platform_adapters/sysync_glob_vars.h
new file mode 100755
index 0000000..0ade9c8
--- /dev/null
+++ b/src/platform_adapters/sysync_glob_vars.h
@@ -0,0 +1,42 @@
+/*
+ * File: sysync_glob_vars.h
+ *
+ * Author: Lukas Zeller (luz@synthesis.ch)
+ *
+ * Global variables
+ *
+ * Copyright (c) 2001-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ * 2004-08-09 : luz : created
+ *
+ */
+
+#ifndef SYSYNC_GLOB_VARS_H
+#define SYSYNC_GLOB_VARS_H
+
+#include "generic_types.h"
+
+namespace sysync {
+
+#ifdef DIRECT_APPBASE_GLOBALACCESS
+
+// init and deinit global sysync stuff
+void sysync_glob_init(void);
+void sysync_glob_deinit(void);
+void *sysync_glob_anchor(void);
+void sysync_glob_setanchor(void *aAnchor);
+
+#else // DIRECT_APPBASE_GLOBALACCESS
+
+// define dummies
+#define sysync_glob_init()
+#define sysync_glob_deinit()
+
+#endif // not DIRECT_APPBASE_GLOBALACCESS
+
+} // namespace sysync
+
+#endif // SYSYNC_GLOB_VARS_H
+
+// eof
+
diff --git a/src/platform_adapters/sysyncinit.cpp b/src/platform_adapters/sysyncinit.cpp
new file mode 100755
index 0000000..4601bd3
--- /dev/null
+++ b/src/platform_adapters/sysyncinit.cpp
@@ -0,0 +1,79 @@
+/*
+ * File: sysyncinit.cpp
+ *
+ * Global variables instantiation
+ *
+ * Author: Lukas Zeller (luz@synthesis.ch)
+ *
+ * Copyright (c) 2004-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ * 2004-08-09 : luz : created
+ *
+ *
+ */
+
+
+#include "prefix_file.h"
+
+#define _IMPLEMENTS_DEBUG_GLOBALS
+#include "sysync.h"
+#undef _IMPLEMENTS_DEBUG_GLOBALS
+
+// note: this is the generic implementation assuming an environment that supports
+// static global vars.
+
+namespace sysync {
+
+#ifdef DIRECT_APPBASE_GLOBALACCESS
+
+/* %%% obsolete
+// globals for debugging purposes
+// Note: These are duplicates of the values in TSyncAppBase.fRootConfig.fDebugConfig
+// which are here for convenience
+#ifdef SYDEBUG
+uInt16 gDebug; // if <>0 (and #defined SYDEBUG), debug output is generated, value is used as mask
+bool gGlobalDebugLogs; // if set, global debug output is generated
+bool gMsgDump; // if set (and #defined MSGDUMP), messages sent and received are logged;
+bool gXMLtranslate; // if set, communication will be translated to XML and logged
+bool gSimMsgRead; // if set (and #defined SIMMSGREAD), simulated input with "i_" prefixed incoming messages are supported
+bool gSeparateSessionLogs; // if set, session-specific logs are generated in separate files
+string gDebugLogPath; // path to store debug files
+#endif
+*/
+
+// static global for anchoring singluar syncappbase object
+void *gSySyncGlobAnchor=NULL;
+
+
+// get the global anchor for the sysync framework
+void *sysync_glob_anchor(void)
+{
+ return gSySyncGlobAnchor;
+} // sysync_glob_anchor
+
+// get the global anchor for the sysync framework
+void sysync_glob_setanchor(void *aAnchor)
+{
+ gSySyncGlobAnchor=aAnchor;
+} // sysync_glob_setanchor
+
+
+// global initialisation routine
+void sysync_glob_init(void)
+{
+ gSySyncGlobAnchor=NULL;
+} // sysync_glob_init
+
+// global de-initialisation routine
+void sysync_glob_deinit(void)
+{
+ // get rid of global stuff outside the SyncAppBase framework
+ /* none here */
+} // sysync_glob_deinit
+
+#endif // DIRECT_APPBASE_GLOBALACCESS
+
+
+} // namespace sysync
+
+/* eof */
diff --git a/src/platform_adapters/unix_common/platform_file.cpp b/src/platform_adapters/unix_common/platform_file.cpp
new file mode 100755
index 0000000..a596250
--- /dev/null
+++ b/src/platform_adapters/unix_common/platform_file.cpp
@@ -0,0 +1,138 @@
+/*
+ * File: platform_file.cpp
+ *
+ * Author: Beat Forster (bfo@synthesis.ch)
+ *
+ *
+ * General interface to get/set file info
+ * like date/attributes/...
+ * >>>> UNIX Version <<<<
+ *
+ * Copyright (c) 2005-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ *
+ */
+
+#include "platform_file.h"
+#include <time.h>
+
+
+namespace sysync {
+
+
+TSyError Get_FileAttr( string /* pathName */, TAttr &attr, bool &isFolder )
+{
+ /*
+ WIN32_FIND_DATA data;
+ TSyError err= FileData( pathName, data );
+ if (!err) {
+ DWORD att= data.dwFileAttributes;
+ attr.h= att & FILE_ATTRIBUTE_HIDDEN;
+ attr.s= att & FILE_ATTRIBUTE_SYSTEM;
+ attr.a= att & FILE_ATTRIBUTE_ARCHIVE;
+ attr.d= false; // delete
+ attr.w= true;
+ attr.r= true;
+ attr.x= false;
+ } // if
+
+ return err;
+ */
+
+ attr.h= false;
+ attr.s= false;
+ attr.a= false;
+ attr.d= false; // delete
+ attr.w= true;
+ attr.r= true;
+ attr.x= false;
+
+ isFolder= false;
+
+ return LOCERR_OK;
+} // Get_FileAttr
+
+
+TSyError Set_FileAttr( string /* pathName */, TAttr /* aAttr */ )
+{
+ return LOCERR_OK;
+} // Set_FileAttr
+
+
+static void FTime( time_t u, string &utcStr )
+{
+ char f[ 20 ];
+ struct tm* t= localtime( &u );
+ sprintf( f, "%04d%02d%02dT%02d%02d%02d", t->tm_year+1900, t->tm_mon+1, t->tm_mday,
+ t->tm_hour, t->tm_min, t->tm_sec );
+ utcStr= f;
+} // FTime
+
+
+extern "C" {
+ #include <ctime>
+ #include <sys/stat.h>
+ #include <utime.h>
+}
+
+
+static TSyError FSTime( string tStr, time_t &u )
+// UTC is currently not supported
+{
+ string tt= tStr.substr( 8,1 );
+ if (tt!="T") return DB_Forbidden;
+ tt= tStr.substr( 15,1 );
+ //bool usUTC= tt=="Z";
+
+ time( &u ); // get some defaults
+ struct tm* t= localtime( &u );
+
+ t->tm_year = atoi( tStr.substr( 0,4 ).c_str() )-1900;
+ t->tm_mon = atoi( tStr.substr( 4,2 ).c_str() )-1;
+ t->tm_mday = atoi( tStr.substr( 6,2 ).c_str() );
+ // "T"
+ t->tm_hour = atoi( tStr.substr( 9,2 ).c_str() );
+ t->tm_min = atoi( tStr.substr( 11,2 ).c_str() );
+ t->tm_sec = atoi( tStr.substr( 13,2 ).c_str() );
+
+ t->tm_wday = 0; // avoid troubles
+ t->tm_yday = 0;
+//t->tm_isdst = 1;
+
+ u= mktime( t );
+ return LOCERR_OK;
+} // FSTime
+
+
+TSyError Get_FileDate( string pathName, TDates &aDate )
+{
+ struct stat info;
+ TSyError err= stat( pathName.c_str(), &info );
+ if (!err) {
+ FTime( info.st_ctime, aDate.created );
+ FTime( info.st_mtime, aDate.modified );
+ FTime( info.st_atime, aDate.accessed );
+ } // if
+
+ return err;
+} // Get_FileDate
+
+
+TSyError Set_FileDate( string pathName, TDates aDate )
+{
+ TSyError err;
+ struct utimbuf buf;
+ err= FSTime( aDate.created, buf.modtime ); if (err) return err;
+ err= FSTime( aDate.modified, buf.modtime ); if (err) return err;
+ err= FSTime( aDate.accessed, buf.actime ); if (err) return err;
+
+//string uu;
+//FTime( buf.modtime, uu ); printf( "uum='%s'\n", uu.c_str() );
+//FTime( buf.actime, uu ); printf( "uua='%s'\n", uu.c_str() );
+
+ return utime( pathName.c_str(), &buf ); return err;
+} // Set_FileDate
+
+
+} /* namespace */
+/* eof */
diff --git a/src/platform_adapters/unix_common/platform_mutex.cpp b/src/platform_adapters/unix_common/platform_mutex.cpp
new file mode 100755
index 0000000..5ff8d8e
--- /dev/null
+++ b/src/platform_adapters/unix_common/platform_mutex.cpp
@@ -0,0 +1,28 @@
+/*
+ * File: platform_mutex.cpp
+ *
+ * Author: Beat Forster (bfo@synthesis.ch)
+ *
+ *
+ * Mutex handling
+ * (Unix implementation)
+ *
+ * Copyright (c) 2005-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ *
+ */
+
+
+#include <pthread.h>
+#include "platform_mutex.h"
+
+
+MutexPtr_t newMutex() { pthread_mutex_t* m= new pthread_mutex_t;
+ pthread_mutex_init ( (pthread_mutex_t*)m, NULL ); return m; }
+bool lockMutex( MutexPtr_t m ) { return pthread_mutex_lock ( (pthread_mutex_t*)m ) == 0; }
+bool tryLockMutex( MutexPtr_t m ) { return pthread_mutex_trylock( (pthread_mutex_t*)m ) == 0; }
+bool unlockMutex( MutexPtr_t m ) { return pthread_mutex_unlock ( (pthread_mutex_t*)m ) == 0; }
+void freeMutex( MutexPtr_t m ) { pthread_mutex_destroy( (pthread_mutex_t*)m );
+ delete (pthread_mutex_t*)m; }
+
+/* eof */
diff --git a/src/platform_adapters/unix_common/platform_pipe.c b/src/platform_adapters/unix_common/platform_pipe.c
new file mode 100755
index 0000000..f2c48d4
--- /dev/null
+++ b/src/platform_adapters/unix_common/platform_pipe.c
@@ -0,0 +1,91 @@
+/*
+ * File: platform_pipe.c
+ *
+ * Author: Lukas Zeller (luz@synthesis.ch)
+ *
+ * Platform specific utility routines
+ *
+ * Copyright (c) 2003-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ * NOTE: this file is part of the mod_sysync source files.
+ *
+ */
+
+#include <fcntl.h>
+#include "platform_headers.h"
+#include "platform_pipe.h"
+
+
+// create new pipe. Fails if it already exists
+int createPipe(const char *aPipePathName)
+{
+ if (mkfifo(aPipePathName,0620)!=0) {
+ // error
+ return appFalse;
+ }
+ // ok
+ return appTrue;
+} // createPipe
+
+
+int deletePipe(const char *aPipePathName)
+{
+ if (unlink(aPipePathName)!=0) {
+ // error
+ return appFalse;
+ }
+ // ok
+ return appTrue;
+} // deletePipe
+
+
+pipeDescriptor_t openPipeForRead(const char *aPipePathName)
+{
+ return open(aPipePathName, O_RDONLY);
+} // openPipeForRead
+
+
+pipeDescriptor_t openPipeForWrite(const char *aPipePathName)
+{
+ return open(aPipePathName, O_WRONLY);
+} // openPipeForWrite
+
+
+int closePipe(pipeDescriptor_t aPipeDesc)
+{
+ return (close(aPipeDesc)!=0);
+} // closePipe
+
+
+long readPipe(pipeDescriptor_t aPipeDesc,void *aBuffer, long aMaxBytes, int aReadAll)
+{
+ long gotBytes=0;
+ long bytes;
+ do {
+ bytes=read(aPipeDesc,aBuffer,aMaxBytes);
+ if (bytes<0) return bytes; // error
+ if (bytes==0) return gotBytes; // no error, but end of pipe - abort reading and return number of bytes already read
+ gotBytes+=bytes; // add them up
+ aMaxBytes-=bytes; // reduce max
+ aBuffer = (void *)((char *)aBuffer + bytes);
+ } while(aReadAll && aMaxBytes>0);
+ return gotBytes; // return total
+} // readPipe
+
+
+long writePipe(pipeDescriptor_t aPipeDesc,void *aBuffer, long aNumBytes)
+{
+ long sentBytes=0;
+ long bytes;
+ do {
+ bytes=write(aPipeDesc,aBuffer,aNumBytes);
+ if (bytes<0) return bytes; // error
+ sentBytes+=bytes; // add them up
+ aNumBytes-=bytes; // reduce max
+ aBuffer = (void *)((char *)aBuffer + bytes);
+ } while(aNumBytes>0);
+ return sentBytes; // return total
+} // writePipe
+
+
+/* eof */
diff --git a/src/platform_adapters/unix_common/platform_pipe.h b/src/platform_adapters/unix_common/platform_pipe.h
new file mode 100755
index 0000000..6647031
--- /dev/null
+++ b/src/platform_adapters/unix_common/platform_pipe.h
@@ -0,0 +1,38 @@
+/*
+ * File: platform_pipe.h
+ *
+ * Author: Lukas Zeller (luz@synthesis.ch)
+ *
+ * Platform specific pipe implementation
+ *
+ * Copyright (c) 2003-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ * NOTE: this file is part of the mod_sysync source files.
+ */
+
+#include <errno.h>
+#include "generic_types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef int pipeDescriptor_t;
+#define PIPEDESCRIPTORVALID(pd) (pd>=0)
+#define PIPEERRORCODE ((int)errno)
+#define PIPEINVALIDDESC -1
+#define PIPEPATHDELIM '/'
+
+int createPipe(const char *aPipePathName);
+int deletePipe(const char *aPipePathName);
+pipeDescriptor_t openPipeForRead(const char *aPipePathName);
+pipeDescriptor_t openPipeForWrite(const char *aPipePathName);
+int closePipe(pipeDescriptor_t aPipeDesc);
+long readPipe(pipeDescriptor_t aPipeDesc,void *aBuffer, long aMaxBytes, int aReadAll);
+long writePipe(pipeDescriptor_t aPipeDesc,void *aBuffer, long aNumBytes);
+
+#ifdef __cplusplus
+}
+#endif
+
+/* eof */
diff --git a/src/platform_adapters/unix_common/platform_thread.cpp b/src/platform_adapters/unix_common/platform_thread.cpp
new file mode 100755
index 0000000..d4fbb5b
--- /dev/null
+++ b/src/platform_adapters/unix_common/platform_thread.cpp
@@ -0,0 +1,217 @@
+/*
+ * File: platform_thread.cpp
+ *
+ * Author: Lukas Zeller (luz@synthesis.ch)
+ *
+ * Platform specific thread object implementation
+ *
+ * Copyright (c) 2004-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ * 2004-05-24 : luz : created from Win version
+ *
+ */
+
+#include "prefix_file.h"
+
+#include "platform_thread.h"
+#include "platform_time.h"
+#include "lineartime.h"
+#include "timezones.h"
+
+// private includes needed
+#include <errno.h>
+#include <signal.h>
+
+namespace sysync {
+
+
+// get id of the running process
+uInt32 myProcessID() {
+ return (uInt32)getpid();
+} // myProcessID
+
+
+// get id of the running thread
+uInt32 myThreadID() {
+ return (uInt32)pthread_self();
+} // myThreadID
+
+
+// The POSIX thread function, must be passed the Thread Object address as parameter
+extern "C" void * PosixThreadFunc(void *aParam);
+void* PosixThreadFunc(void *aParam)
+{
+ // get Thread Object pointer
+ TThreadObject *threadObjP = static_cast<TThreadObject *>(aParam);
+ // call thread execution method
+ int retval = (int)threadObjP->execute();
+ // signal thread termination condition
+ pthread_mutex_lock (&(threadObjP->fDoneCondMutex));
+ pthread_cond_signal (&(threadObjP->fDoneCond));
+ threadObjP->fTerminated= true; // no longer valid
+ pthread_mutex_unlock(&(threadObjP->fDoneCondMutex));
+ // auto-dispose the thread object if requested
+ if (threadObjP->fAutoDisposeThreadObj) {
+ delete threadObjP;
+ }
+ // Exit thread now
+ pthread_exit((void *)retval);
+ return NULL;
+} // PosixThreadFunc
+
+
+// creates thread object. Thread ist not started yet, must use launch() for this
+TThreadObject::TThreadObject(void) :
+ fStackSize(0),
+ fThreadFunc(NULL),
+ fThreadFuncParam(0),
+ fTerminationRequested(false),
+ fExitCode(0),
+ fAutoDisposeThreadObj(false),
+ fPosixThread(0),
+ fTerminated(false)
+{
+ // init cond and mutex required to implement WaitFor
+ pthread_mutex_init(&fDoneCondMutex,NULL);
+ pthread_cond_init(&fDoneCond,NULL);
+} // TThreadObject::TThreadObject
+
+
+// destroys thread object. This will kill the thread if it still active.
+TThreadObject::~TThreadObject()
+{
+ // kill thread (only if not auto-disposed at end of thread)
+ if (!fAutoDisposeThreadObj) kill();
+ // destroy cond/mutex
+ pthread_mutex_destroy(&fDoneCondMutex);
+ pthread_cond_destroy(&fDoneCond);
+} // TThreadObject::TThreadObject
+
+
+// launches thread. Returns false if thread could not be started. exitcode() will
+// contain the platform error code for the failure to start the thread
+bool TThreadObject::launch(
+ TThreadFunc aThreadFunc, // the function to execute in the thread
+ uInt32 aThreadFuncParam, // a parameter to pass to the thread
+ size_t aStackSize, // if 0, default stack size is used
+ bool aAutoDispose // if true, the thread object will dispose itself when thread has finished running
+)
+{
+ // save parameters
+ fThreadFunc=aThreadFunc;
+ fThreadFuncParam=aThreadFuncParam;
+ fStackSize=aStackSize;
+ fAutoDisposeThreadObj=aAutoDispose;
+ // assume ok exit errno.h
+ fExitCode=0;
+ fTerminationRequested=false;
+ // thread must not exist yet
+ if (fPosixThread!=0 && !fTerminated) {
+ fExitCode=EEXIST; // we use this to signal thread is already started
+ return false;
+ }
+ // now create a POSIX thread
+ fPosixThread= 0;
+ fTerminated= false;
+ fExitCode = pthread_create(
+ &fPosixThread, // pointer to returned thread identifier
+ NULL, // default attributes = joinable, not realtime
+ PosixThreadFunc, // pointer to thread function
+ this // pass the pointer to the thread object
+ );
+ if (fExitCode==0) {
+ // thread created successfully
+ return true; // ok
+ }
+ else {
+ // could not create thread
+ fTerminated= true;
+ return false; // not ok
+ }
+} // TThreadObject::launch
+
+
+uInt32 TThreadObject::getid(void) {
+ return (uInt32)fPosixThread;
+} // TThreadObject::getid
+
+
+
+// kills the thread
+void TThreadObject::kill(void)
+{
+ if (fPosixThread!=0 && !fTerminated) {
+ // the thread is actually running
+ fExitCode=EINTR; // nearest match, interrupted
+ // kill it
+ pthread_kill(fPosixThread, SIGKILL);
+ fTerminated= true;
+ }
+} // TThreadObject::kill
+
+
+// waits for the thread to stop
+bool TThreadObject::waitfor(sInt32 aMilliSecondsToWait)
+{
+ int retval= 0;
+ if (fPosixThread==0) return true; // thread not running
+
+ // thread is running
+ // wait for termination condition of the thread
+ pthread_mutex_lock(&fDoneCondMutex);
+ if (!fTerminated) { // catch also, if signalled already
+ if (aMilliSecondsToWait<0) {
+ // wait indefinitely
+ retval= pthread_cond_wait(&fDoneCond, &fDoneCondMutex);
+ }
+ else {
+ // wait specified amount of time
+ struct timespec timeout;
+
+ /* this conversion might be implemented in time module later */
+ #define milli 1E-3
+ #define msToLinearTimeFactor (secondToLinearTimeFactor*milli) // is usually 1 again
+
+ lineartime_t ltm = getSystemNowAs(TCTX_UTC,NULL) - UnixToLineartimeOffset; // starting 1970
+ ltm = (lineartime_t)(ltm/msToLinearTimeFactor); // as milliSeconds
+ ltm+= aMilliSecondsToWait; // add the offset from now
+
+ timeout.tv_sec = (unsigned int)(ltm * milli);
+ timeout.tv_nsec= ( ltm % 1000 )*1000000; // ns
+
+ retval= pthread_cond_timedwait(&fDoneCond, &fDoneCondMutex, &timeout);
+ } /* if */
+ } /* if */
+ pthread_mutex_unlock(&fDoneCondMutex);
+ if (retval!=0) return false; // check if thread has completed => no, not yet
+
+ // thread has terminated (or is terminating) -> join and get exit code
+ void* threadret;
+ int ret= pthread_join(fPosixThread,&threadret);
+ if (ret==0) fExitCode= (uInt32)(uIntPtr)threadret;
+ else fExitCode= ret;
+
+ fTerminated= true; // thread has ended
+ return true;
+} // TThreadObject::waitfor
+
+
+
+// Thread execution method
+uInt32 TThreadObject::execute()
+{
+ // standard implementation simply starts the predefined thread function, if any
+ if (fThreadFunc) {
+ // return fThreadFunc(this,fThreadFuncParam);
+ uInt32 err= fThreadFunc(this,fThreadFuncParam);
+ return err;
+ }
+ else {
+ return 0;
+ }
+} // TThreadObject::execute
+
+
+} // namespace sysync
+
+/* eof */
diff --git a/src/prefix_file.h b/src/prefix_file.h
new file mode 100755
index 0000000..5b32f90
--- /dev/null
+++ b/src/prefix_file.h
@@ -0,0 +1,11 @@
+// dummy prefix file, used for CW based projects which do
+// not need the prefix file here, but in the target settings
+
+#ifdef _MSC_VER
+#error "this is not the right prefix_file.h -> reorder include paths such that project-specific prefix is used!"
+#endif
+
+#ifdef __PALM_OS__
+#error "this is not the right prefix_file.h -> reorder access paths such that project-specific prefix is used!"
+#endif
+
diff --git a/src/smltk-linker.map b/src/smltk-linker.map
new file mode 100644
index 0000000..051d30a
--- /dev/null
+++ b/src/smltk-linker.map
@@ -0,0 +1,6 @@
+VER_1.0 {
+ global:
+ sml*;
+ local:
+ *;
+};
diff --git a/src/syncapps/clientEngine_custom/clientengine_custom.h b/src/syncapps/clientEngine_custom/clientengine_custom.h
new file mode 100755
index 0000000..8f0879d
--- /dev/null
+++ b/src/syncapps/clientEngine_custom/clientengine_custom.h
@@ -0,0 +1,26 @@
+/*
+ */
+
+#ifndef CLIENTENGINE_CUSTOM_H
+#define CLIENTENGINE_CUSTOM_H
+
+/* Headers that might be available in precompiled form
+ * (standard libraries, SyncML toolkit...)
+ */
+#include "clientengine_custom_precomp.h"
+
+/* headers not suitable for / entirely included in precompilation */
+
+// DB interface related includes
+#ifdef SQL_SUPPORT
+ #include "odbcdb.h"
+#endif
+#ifdef SDK_SUPPORT
+ #include "plugindb.h"
+#endif
+#ifdef OUTLOOK_SUPPORT
+ #include "outlookdb.h"
+#endif
+
+
+#endif // CLIENTENGINE_CUSTOM_H
diff --git a/src/syncapps/clientEngine_custom/clientengine_custom_Base.cpp b/src/syncapps/clientEngine_custom/clientengine_custom_Base.cpp
new file mode 100644
index 0000000..894c323
--- /dev/null
+++ b/src/syncapps/clientEngine_custom/clientengine_custom_Base.cpp
@@ -0,0 +1,64 @@
+/*
+ * TCustomClientEngineBase, TCustomClientEngineInterface
+ * SyncML client engine for custom clients - base classes
+ *
+ * Copyright (c) 2004-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ * 2007-09-04 : luz : Created
+ *
+ */
+
+
+// includes
+#include "clientengine_custom.h"
+#include "clientengine_custom_Base.h"
+
+// common includes
+#include "engineclientbase.h"
+
+
+namespace sysync {
+
+
+// factory function implementation - declared in TEngineInterface
+ENGINE_IF_CLASS *newEngine(void)
+{
+ return new TCustomClientEngineInterface;
+} // newEngine
+
+
+
+/*
+ * Implementation of TCustomClientEngineInterface
+ */
+
+
+/// @brief returns a new application base.
+TSyncAppBase *TCustomClientEngineInterface::newSyncAppBase(void)
+{
+ return new TCustomClientEngineBase;
+} // TCustomClientEngineInterface::newSyncAppBase
+
+
+
+/*
+ * Implementation of TCustomClientEngineBase
+ */
+
+
+TCustomClientEngineBase::TCustomClientEngineBase()
+{
+ // create config root
+ fConfigP=new TEngineClientRootConfig(this);
+} // TCustomClientEngineBase::TCustomClientEngineBase
+
+
+TCustomClientEngineBase::~TCustomClientEngineBase()
+{
+ fDeleting=true; // flag deletion to block calling critical (virtual) methods
+} // TCustomClientEngineBase::~TCustomClientEngineBase
+
+
+} // namespace sysync
+
+// eof
diff --git a/src/syncapps/clientEngine_custom/clientengine_custom_Base.h b/src/syncapps/clientEngine_custom/clientengine_custom_Base.h
new file mode 100755
index 0000000..bbebd79
--- /dev/null
+++ b/src/syncapps/clientEngine_custom/clientengine_custom_Base.h
@@ -0,0 +1,47 @@
+/*
+ * TCustomClientEngineBase, TCustomClientEngineInterface
+ * SyncML client engine for custom clients - base classes
+ *
+ * Copyright (c) 2004-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ * 2007-09-04 : luz : Created
+ *
+ *
+ */
+
+#ifndef CLIENTENGINE_CUSTOM_BASE_H
+#define CLIENTENGINE_CUSTOM_BASE_H
+
+// directly based on syncclient base
+#include "engineclientbase.h"
+// using binfile engine interface
+#include "binfileimplclient.h"
+
+
+namespace sysync {
+
+// Appbase class
+class TCustomClientEngineBase: public TEngineClientBase
+{
+ typedef TEngineClientBase inherited;
+public:
+ TCustomClientEngineBase();
+ virtual ~TCustomClientEngineBase();
+}; // TCustomClientEngineBase
+
+
+// Engine interface class
+class TCustomClientEngineInterface:
+ public TBinfileEngineInterface
+{
+ typedef TBinfileEngineInterface inherited;
+public:
+ /// @brief returns a new application base.
+ virtual TSyncAppBase *newSyncAppBase(void);
+}; // TCustomClientEngineInterface
+
+}
+
+#endif // CLIENTENGINE_CUSTOM_BASE_H
+
+// eof
diff --git a/src/syncapps/clientEngine_custom/clientengine_custom_precomp.h b/src/syncapps/clientEngine_custom/clientengine_custom_precomp.h
new file mode 100755
index 0000000..300f569
--- /dev/null
+++ b/src/syncapps/clientEngine_custom/clientengine_custom_precomp.h
@@ -0,0 +1,26 @@
+/* Headers that might be available in precompiled form
+ * (standard libraries, SyncML toolkit...)
+ *
+ */
+
+#ifndef CLIENTENGINE_CUSTOM_PRECOMP_H
+#define CLIENTENGINE_CUSTOM_PRECOMP_H
+
+// Needed for Engine
+#include "sysync_precomp.h"
+
+// DB interface related includes
+#ifdef SQL_SUPPORT
+ #include "odbcdb_precomp.h"
+#endif
+#ifdef SDK_SUPPORT
+ #include "plugindb_precomp.h"
+#endif
+#ifdef OUTLOOK_SUPPORT
+ #include "outlookdb_precomp.h"
+#endif
+
+
+#endif // CLIENTENGINE_CUSTOM_PRECOMP_H
+
+// eof
diff --git a/src/syncapps/clientEngine_custom/product_options.h b/src/syncapps/clientEngine_custom/product_options.h
new file mode 100755
index 0000000..0f338da
--- /dev/null
+++ b/src/syncapps/clientEngine_custom/product_options.h
@@ -0,0 +1,125 @@
+/* Common Product options
+ * ======================
+ *
+ */
+
+// Custom Client Library Products
+// ##############################
+
+// Separate Version
+// ================
+
+// Note: for now, client library has NO SEPARATE VERSION, but shows engine version
+
+/*
+#define SYSYNC_VERSION_MAJOR 1
+#define SYSYNC_VERSION_MAJOR_TXT "1"
+
+#define SYSYNC_VERSION_MINOR 1
+#define SYSYNC_VERSION_MINOR_TXT "1"
+
+#define SYSYNC_SUBVERSION 0
+#define SYSYNC_SUBVERSION_TXT "0"
+
+#define SYSYNC_BUILDNUMBER 0
+#define SYSYNC_BUILDNUMBER_TXT "0"
+*/
+
+
+// new major or minor gives a new demo period
+#define SYSER_VERSCHECK_MASK 0xFFFF0000
+
+
+// Most hyperglobal definition, might even influence global_options.h:
+// - THIS IS A CLIENT
+#define SYSYNC_CLIENT 1
+// - is a engine library
+#define ENGINE_LIBRARY 1
+
+// - ...which is used in standalone apps
+#define STANDALONE_APP 1
+
+// global options
+#include "global_options.h"
+
+// Product (but not target) specific options
+// #########################################
+
+// Hard expiration date, software will stop working after defined date
+// if not registered
+#undef VERSION_COMMENTS
+#define VERSION_COMMENTS "Demo expires after " EXPIRY_DATE_STRING " if not registered"
+#define EXPIRES_AFTER_DATE 1
+
+// SySync options
+// ==============
+
+// - use precise time when possible
+#define NOW_WITH_MILLISECONDS 1
+
+
+// - if defined, debug code is included (not necessarily enabled, see gDebug)
+// if 1, only "public" debugging is enabled, if >1, all debugging is enabled
+#ifdef RELEASE_VERSION
+ #if RELEASE_SYDEBUG
+ #define SYDEBUG RELEASE_SYDEBUG
+ #else
+ #undef SYDEBUG // absolutely no debug code for release!
+ #endif
+ #undef CONSOLEINFO
+#else
+ #define SYDEBUG 2 // 3=including XPT trace, 4=including memory leak detection
+ #undef CONSOLEINFO
+#endif
+
+// local Database options
+#define DESKTOP_CLIENT 1 // selects different binfile format (actually, will merge with mobile format at version 5)
+// separate files for outlook - different from old 1.0 client to make sure we don't import old problems
+#define SETTINGS_BFI_PREFIX "sysynclib_"
+
+// - if defined, local DB does not require any login
+#define NO_LOCAL_DBLOGIN 1
+
+// - if defined, config file might contain <syncrequest>s which will
+// allow a client to run directly from config w/o additionally
+// provided parameters.
+#undef PRECONFIGURED_SYNCREQUESTS
+
+// - if defined, XML config parsing is completely disabled,
+// all config must be set programmatically
+#undef HARDCODED_CONFIG
+
+// - if defined, remoterule mechanism is not included
+#define NO_REMOTE_RULES 1
+
+// - if defined, support for hardcoded (predefined)
+// type definitions will be included
+#undef HARDCODED_TYPE_SUPPORT
+// - if defined, support for configurable types will be included
+#define CONFIGURABLE_TYPE_SUPPORT 1
+
+
+// do not modify remote IDs in any way while processing them
+#define DONT_STRIP_PATHPREFIX_FROM_REMOTEIDS 1
+
+// if defined, session continues after 401/407
+//#define AUTH_RETRY_USES_SAME_CLIENT_SESSION
+
+
+
+// SyncML Toolkit options
+// ======================
+
+// if defined, the entire complicated and thread-unsafe workspace manager
+// is completely bypassed
+#define NOWSM 1
+
+/* if defined, avoids using <![CDATA[ for opaque data except when really needed, i.e. contents would break XML */
+#define PCDATA_OPAQUE_AS_CDATA 1
+
+
+// we want the toolkit linked static
+#define __LINK_TOOLKIT_STATIC__ 1
+
+
+/* eof */
diff --git a/src/syncapps/sysytool/sysytool.cpp b/src/syncapps/sysytool/sysytool.cpp
new file mode 100755
index 0000000..bb6c464
--- /dev/null
+++ b/src/syncapps/sysytool/sysytool.cpp
@@ -0,0 +1,235 @@
+/*
+ * SySyTool
+ * Synthesis SyncML Diagnostic Tool
+ *
+ * Copyright (c) 2004-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ */
+
+
+#include <iostream>
+#include <signal.h>
+
+#include "sysytool.h"
+
+// include files where commands are implemented
+#include "odbcapiagent.h"
+#include "syncserver.h"
+#include "sysync_utils.h"
+#include "rrules.h"
+
+static void cleanup(void)
+{
+ NCDEBUGPRINTFX(DBG_ERROR,("Program aborted, atexit handler will now kill syncappbase"));
+ // kill base object if any (will cause all current sessions to die)
+ freeSyncAppBase();
+}
+
+static void sighandler(int sig)
+{
+ cleanup();
+}
+
+
+// global options
+const char *gConfigFile = NULL; // -f, not optional
+const char *gUserName = NULL; // -u
+const char *gDeviceID = NULL; // -d
+bool gVerbose = false; // -v
+bool gSilent = false; // -s
+
+
+static void printTitle(void)
+{
+ CONSOLEPRINTF((
+ "Synthesis SyncML Diagnostic Tool %d.%d.%d.%d - (c) 2004-" RELEASE_YEAR_TXT " by Synthesis AG\n",
+ SYSYNC_VERSION_MAJOR,
+ SYSYNC_VERSION_MINOR,
+ SYSYNC_SUBVERSION,
+ SYSYNC_BUILDNUMBER
+ ));
+} // printTitle
+
+
+static void printUsage(const char *aCmdName)
+{
+ printTitle();
+ CONSOLEPRINTF(("Usage:"));
+ CONSOLEPRINTF((" %s [<options>] <command> [<commandarg> ...]",aCmdName));
+ CONSOLEPRINTF(("Options:"));
+ CONSOLEPRINTF((" -f configfile : (required) server's xml configuration file"));
+ CONSOLEPRINTF(("Commands:"));
+ CONSOLEPRINTF((" check"));
+ CONSOLEPRINTF((" check config for syntax errors"));
+ // let them show their own help text
+ #ifdef SQL_SUPPORT
+ execSQL(-1,NULL);
+ #endif
+ convertData(-1,NULL);
+ testLogin(-1,NULL);
+ charConv(-1,NULL);
+ timeConv(-1,NULL);
+ rruleConv(-1,NULL);
+ parse2822AddrSpec(-1,NULL);
+ wbxmlConv(-1,NULL);
+}
+
+
+int main(int argc, char *argv[])
+{
+ Ret_t err;
+
+ #ifdef MEMORY_PROFILING
+ size_t mematstart = gAllocatedMem;
+ #endif
+
+ // init hyper global stuff
+ sysync_glob_init();
+
+ // set exit handler
+ atexit(cleanup);
+ signal(SIGABRT,sighandler);
+
+ const int maxcmdargs=5;
+ int cmdargc=-1; // number of non-option command arguments (-1 = command not found)
+ const char *command = NULL;
+ const char *cmdargv[maxcmdargs];
+
+ int idx=1;
+ const char *p;
+ while (idx<argc) {
+ // get arg
+ p=argv[idx];
+ // check for option
+ if (*p=='-') {
+ ++p;
+ if (*p=='-') {
+ // extended option syntax
+ // %%% tbd
+ CONSOLEPRINTF(("Invalid Option '-%s'\n",p));
+ printUsage(argv[0]);
+ exit(EXIT_FAILURE);
+ }
+ else {
+ // get next arg if any
+ const char* optarg = NULL;
+ if (idx+1<argc)
+ optarg = argv[idx+1];
+ // evaluate option
+ switch (*p) {
+ case 'h' : // help
+ printUsage(argv[0]);
+ exit(EXIT_SUCCESS);
+ case 'f' :
+ gConfigFile = optarg;
+ break;
+ case 'u' :
+ gUserName = optarg;
+ break;
+ case 'd' :
+ gDeviceID = optarg;
+ break;
+ case 'v' :
+ gVerbose = true;
+ gSilent = false;
+ optarg=NULL;
+ break;
+ case 's' :
+ gSilent = true;
+ optarg=NULL;
+ break;
+ default:
+ CONSOLEPRINTF(("Unknown Option '-%c'\n",*p));
+ printUsage(argv[0]);
+ exit(EXIT_FAILURE);
+ }
+ // skip option argument if consumed by option
+ if (optarg) {
+ idx++;
+ }
+ } // single char options
+ } // option
+ else {
+ // non-option
+ if (cmdargc<0) {
+ // this is the command
+ command = argv[idx];
+ cmdargc=0;
+ }
+ else if (cmdargc<maxcmdargs) {
+ cmdargv[cmdargc]=argv[idx];
+ cmdargc++;
+ }
+ }
+ // next arg
+ idx++;
+ } // while args
+
+ // check if we have a command
+ if (!command) {
+ CONSOLEPRINTF(("No command specified\n"));
+ printUsage(argv[0]);
+ exit(EXIT_FAILURE);
+ }
+
+ // now we need a config first
+ if (!gConfigFile) {
+ CONSOLEPRINTF(("Please specify a config file using the -f option!\n"));
+ printUsage(argv[0]);
+ exit(EXIT_FAILURE);
+ }
+ // read config
+ // - create dispatcher object
+ TXPTSessionDispatch *sessionDispatchP = getXPTSessionDispatch();
+ // let it read the config
+ err = sessionDispatchP->readXMLConfigStandard(gConfigFile,false,true); // local and global try
+ // test if config is ok
+ if (err!=LOCERR_OK) {
+ CONSOLEPRINTF(("Fatal error in configuration (Error=%hd)\n",err));
+ exit(EXIT_FAILURE);
+ }
+
+ // override config's debug options to make it suitable for console output
+ TDebugLogger *loggerP = sessionDispatchP->getDbgLogger();
+ TDbgOptions *optsP = (TDbgOptions *)(loggerP->getOptions());
+ optsP->fIndentString.erase();
+ optsP->fOutputFormat=dbgfmt_text;
+ optsP->fTimestampForAll=false;
+ optsP->fTimestampStructure=true;
+ if (gVerbose) {
+ sessionDispatchP->getDbgLogger()->setMask(DBG_TOOL);
+ sessionDispatchP->getDbgLogger()->setEnabled(true);
+ }
+ else {
+ sessionDispatchP->getDbgLogger()->setMask(0);
+ sessionDispatchP->getDbgLogger()->setEnabled(false);
+ }
+ // now execute command
+ if (strucmp(command,"check")==0)
+ exit(EXIT_SUCCESS); // ok if we get that far
+ #ifdef SQL_SUPPORT
+ else if (strucmp(command,"execsql")==0)
+ exit(sysync::execSQL(cmdargc,cmdargv));
+ #endif
+ else if (strucmp(command,"convert")==0)
+ exit(sysync::convertData(cmdargc,cmdargv));
+ else if (strucmp(command,"login")==0)
+ exit(sysync::testLogin(cmdargc,cmdargv));
+ else if (strucmp(command,"charconv")==0)
+ exit(sysync::charConv(cmdargc,cmdargv));
+ else if (strucmp(command,"addrparse")==0)
+ exit(sysync::parse2822AddrSpec(cmdargc,cmdargv));
+ else if (strucmp(command,"time")==0)
+ exit(sysync::timeConv(cmdargc,cmdargv));
+ else if (strucmp(command,"rrule")==0)
+ exit(sysync::rruleConv(cmdargc,cmdargv));
+ else if (strucmp(command,"wbxml2xml")==0)
+ exit(sysync::wbxmlConv(cmdargc,cmdargv));
+ else {
+ CONSOLEPRINTF(("Unknown Command '%s'\n",command));
+ printUsage(argv[0]);
+ exit(EXIT_FAILURE);
+ }
+}
+
+// eof
diff --git a/src/syncapps/sysytool/sysytool.h b/src/syncapps/sysytool/sysytool.h
new file mode 100755
index 0000000..7946f00
--- /dev/null
+++ b/src/syncapps/sysytool/sysytool.h
@@ -0,0 +1,27 @@
+/* syncserver xpt generic header file */
+
+#ifndef SYSYTOOL_H
+#define SYSYTOOL_H
+
+/* Headers that might be available in precompiled form
+ * (standard libraries, SyncML toolkit...)
+ */
+#include "sysytool_precomp.h"
+
+/* headers not suitable for / entirely included in precompilation */
+
+// transport related includes
+#include "xpt_server.h"
+
+// DB interface related includes
+#ifdef XML2GO_SUPPORT
+ #include "xml2godb.h"
+#endif
+#ifdef SQL_SUPPORT
+ #include "odbcdb.h"
+#endif
+#ifdef SDK_SUPPORT
+ #include "plugindb.h"
+#endif
+
+#endif // SYSYTOOL_H
diff --git a/src/syncapps/sysytool/sysytool_dispatch.cpp b/src/syncapps/sysytool/sysytool_dispatch.cpp
new file mode 100755
index 0000000..e0a454d
--- /dev/null
+++ b/src/syncapps/sysytool/sysytool_dispatch.cpp
@@ -0,0 +1,70 @@
+/**
+ * @File sysytool_dispatch.cpp
+ *
+ * @Author Lukas Zeller (luz@synthesis.ch)
+ *
+ * @brief TSySyncToolDispatch
+ * Pseudo "Dispatcher" for SySyTool (debugging aid tool operating on a server session)
+ *
+ * Copyright (c) 2001-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ * @Date 2005-12-07 : luz : created from old version
+ */
+
+
+// includes
+#include "sysytool.h"
+#include "sysytool_dispatch.h"
+
+// common includes
+#include "syncappbase.h"
+
+#ifdef XML2GO_SUPPORT
+ #include "curl.h"
+#endif
+
+
+namespace sysync {
+
+// dispatcher creation function
+TXPTSessionDispatch *newXPTSessionDispatch(void)
+{
+ // create new app-sepcific dispatcher
+ MP_RETURN_NEW(TSySyncToolDispatch,DBG_OBJINST,"TSySyncToolDispatch",TSySyncToolDispatch());
+} // newXPTSessionDispatch
+
+
+
+/*
+ * Implementation of TSySyncToolDispatch
+ */
+
+/* public TSySyncToolDispatch members */
+
+
+TSySyncToolDispatch::TSySyncToolDispatch()
+{
+ // create config root
+ fConfigP=new TXPTServerRootConfig(this);
+ #ifdef XML2GO_SUPPORT
+ // xml2go needs cURL
+ curl_global_init(CURL_GLOBAL_ALL);
+ #endif
+} // TSySyncToolDispatch::TSySyncToolDispatch
+
+
+TSySyncToolDispatch::~TSySyncToolDispatch()
+{
+ fDeleting=true; // flag deletion to block calling critical (virtual) methods
+ #ifdef XML2GO_SUPPORT
+ // xml2go needs cURL
+ curl_global_cleanup();
+ #endif
+} // TSySyncToolDispatch::TSySyncToolDispatch
+
+
+} // namespace sysync
+
+/* end of TSySyncToolDispatch implementation */
+
+// eof
diff --git a/src/syncapps/sysytool/sysytool_dispatch.h b/src/syncapps/sysytool/sysytool_dispatch.h
new file mode 100755
index 0000000..90acc2d
--- /dev/null
+++ b/src/syncapps/sysytool/sysytool_dispatch.h
@@ -0,0 +1,41 @@
+/**
+ * @File sysytool_dispatch.h
+ *
+ * @Author Lukas Zeller (luz@synthesis.ch)
+ *
+ * @brief TSySyncToolDispatch
+ * Pseudo "Dispatcher" for SySyTool (debugging aid tool operating on a server session)
+ *
+ * Copyright (c) 2001-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ * @Date 2005-12-07 : luz : created from old version
+ */
+
+#ifndef SYSYTOOL_DISPATCH_H
+#define SYSYTOOL_DISPATCH_H
+
+// required headers
+// - based on XPT transport session dispatcher
+#include "xptsessiondispatch.h"
+
+
+namespace sysync {
+
+
+class TSySyncToolDispatch: public TXPTSessionDispatch
+{
+ typedef TXPTSessionDispatch inherited;
+public:
+ TSySyncToolDispatch();
+ virtual ~TSySyncToolDispatch();
+protected:
+ // return output channel handle for debug
+ virtual TDbgOut *newDbgOutputter(bool aGlobal) { return new TConsoleDbgOut; };
+}; // TSySyncToolDispatch
+
+
+} // namespace sysync
+
+#endif // SYSYTOOL_DISPATCH_H
+
+// eof
diff --git a/src/syncapps/sysytool/sysytool_precomp.h b/src/syncapps/sysytool/sysytool_precomp.h
new file mode 100755
index 0000000..557f0e2
--- /dev/null
+++ b/src/syncapps/sysytool/sysytool_precomp.h
@@ -0,0 +1,26 @@
+/* Headers that might be available in precompiled form
+ * (standard libraries, SyncML toolkit...)
+ */
+
+#ifndef SYSYTOOL_PRECOMP_H
+#define SYSYTOOL_PRECOMP_H
+
+
+// transport related includes
+#include "xpt_server_precomp.h"
+
+// DB interface related includes
+#ifdef XML2GO_SUPPORT
+ #include "xml2godb_precomp.h"
+#endif
+#ifdef SQL_SUPPORT
+ #include "odbcdb_precomp.h"
+#endif
+#ifdef SDK_SUPPORT
+ #include "plugindb_precomp.h"
+#endif
+
+
+#endif // SYSYTOOL_PRECOMP_H
+
+// eof
diff --git a/src/syncml_tk/Synthesis_RTK_history.txt b/src/syncml_tk/Synthesis_RTK_history.txt
new file mode 100755
index 0000000..76d2a31
--- /dev/null
+++ b/src/syncml_tk/Synthesis_RTK_history.txt
@@ -0,0 +1,159 @@
+Synthesis' changes to SyncML RTK - History
+------------------------------------------
+
+
+2002-09-03 - changed message size calculation from inaccurate estimation
+ to RTK's built-in space_evaluation method. This is accurate,
+ but RTK code had a few nasty bug that took many hours to
+ find and fix.
+
+2002-11-27 - fixed bug in sml which prevented creating CTCap without Properties
+ (but e.g. P800 email sends this).
+
+2003-04-24 - Fixed RTK to generate MaxObjSize
+ - Fixed to send namespace according to SyncML version
+ and accept all namespaces beginning with "SYNCML:SYNCML"
+ as namespace for document.
+
+2003-04-28 - Fixed a zillion bugs in the RTK that made SyncML 1.1
+ fail (Nokia 7250). Encoder support for new DevInf
+ flags (NOC, UTC, LargeObjSupport) was completely
+ missing (interestingly, the TN_DEVINF_xx were defined
+ but not listed in the tag table). Hopefully corrected
+ all these now.
+
+ - engine/rtk: RTK was using incorrect FPI's in WBXML when using
+ SyncML 1.1. Introduced "vers" parameter for
+ encoder to handle all these version dependencies neatly
+
+ - SyncML RTK PPC: xptitcp.c for PocketPC did rely on send()
+ to send the entire buffer, which is not something that
+ can be guaranteed (altough it is normally the case).
+
+ - SyncML RTK PPC: xptitcp.c tcpSendData() allowed a
+ timeout of only 5 seconds for the socket to get ready
+ for send(). In the HTC SmartPhone2003 this timeout gets
+ exhaused after around 20k of data sent over GPRS, so
+ sending failed. Now added a loop retrying the select()
+ for about 4 minutes (interruptable). Made HTC work!
+
+2003-06-26 - Reorganized xpt-tcp.c/h and xptitcp.c/h to have all platform-specific
+ code in xptitcp.c. Standard (generic) implementation of TCP read and
+ write is still in xpt-tcp.c but can be disabled if xptitcp.h
+ defines PLATFORM_TCPxxxx symbols. A tcpEnableSSL() dummy is now
+ also in xpt-tcp.c, so for a 100% standard xpt the xptitcp.c (which
+ we added to the toolkit) is not a requirement any more.
+
+ - Moved WINCE-specific timeout extensions for tcp read/write to
+ win/xptitcp.c
+
+2003-09-03 - Palm: Fixed bug in xptitcp.c: when send() did not send all
+ bytes at once (PalmOS seems to send only 16k per call)
+ we got a "Sending Error...". Now implemented a loop
+ that send()s until all data is transmitted.
+
+2003-11-12 - PalmOS client: Fixed problem in xptitcp.c which caused the net
+ connection to be shut down even if it was already open
+ when starting the client.
+
+2004-02-23 - XPT: HTTP send was moving data in the send buffer (copyBlock(),
+ called by encodeData()). Changed to only update the buffer
+ pointer instead - send buffer should REALLY be read-only for
+ sending routines!!
+
+2004-04-28 - xpt-http: implemented CONNECT for proxy with SSL case.
+
+ - PALM: implemented new stuff in xptitcp.c to allow deferred
+ SSL handshake for SSL/Proxy case. Could not be fully tested
+ because Palm emulator occasionally crashes on SslRead and
+ SslReceive, however we could run one SSL session through the
+ proxy so we assume its ok. Needs testing on real PalmOS >=5.2
+ device to verify.
+
+2004-10-12 - working gzip (receiving only!) support now in xpt.
+
+2005-01-10 - Added new error codes in xpt to pass through user abort from
+ TCP level down to xptclientbase: TCP_TC_USER_ABORT in TCP,
+ HTTP_RC_USERABORT in HTTP.
+
+2005-01-11 - Made sure USER ABORT condition is also checked when opening
+ connection, not only in message exchange.
+
+2005-03-07 - RTK: Found catastropic bug in xltdec.c in routine concatPCData(), which
+ was called only when (WB)XML contents were encoded not as a single
+ string, but consisting of multiple, adjacent strings. The routine just
+ did it plain wrong by using strcat into a buffer that did not have
+ correct size allocated first.
+
+ - RTK: Added #ifdef to RTK sml part to include the strings for the XML
+ tags even if only WBXML is included in a client to allow decoding
+ tags that are not represented by their WBXML token but by their
+ XML tag string.
+
+2005-04-29 - xpt http: Added HTTP_KEEP_CONNECTION #define
+
+2005-05-18 - xpt: finished HTTP 1.1 "Connection: keep-alive". Now both XPT servers
+ and XPT clients support this. The #define HTTP_KEEP_CONNECTION now
+ only affects the config default for clients. For XPT servers, keep
+ connection is always supported.
+
+2005-06-07 - PalmOS: fixed error checking in xptitcp.c when sending or receiving data
+ which caused that the app did not notice when the socket closed early
+ (e.g. due to SSL timeout in HTTP 1.1 keepalive case) and then hung in
+ waiting for receive on a closed socket.
+
+2005-08-16 - Updated sml part of RTK from current sourceforce version - mainly change
+ to doxygen style docs, no substantial code changes at all.
+
+2005-08-17 - Many changes to adapt RTK for DS 1.2.
+
+2005-09-29 - RTK: missing VerCT for CTCap, now added it.
+
+2005-11-11 - SyncML RTK: made xml parser understand xml where metInf tags are used w/o
+ namespace attribute (as in sync4j PDA clients).
+
+2006-07-11 - xpt: Made HTTP auth support work in Basic and Digest mode
+ (various little problems in xpt).
+
+2006-09-07 - xltenc(XML): Finally implemented a real safe (= XML nestable) <![CDATA[
+ implementation that works without any restrictions: "]]>" in input
+ string is escaped by exiting and re-entering CDATA and becomes
+ "]]>]<![CDATA[]>". This breaks the contained ]]> sequence so it is
+ no longer visible to the surrounding CDATA parsing process.
+
+ - xltenc(XML): Now checking strings passed as SML_PCDATA_STRING for invalid
+ XML chars (&,<,>) and auto-switch to SML_PCDATA_CDATA generation mode if so.
+ This mostly because of filter strings which now often contain pseudo-entities
+ like "&iCON;" or "&NULL;".
+
+ - xltdec(XML): Made parsing PCDATA sections that contain normal PCDATA and CDATA
+ mixed actually work. The design was prepared for it, but was apparently not
+ properly debugged.
+
+ - Fixed bugs in xltdevinf, FilterRx parsing was missing, FilterCap
+ parsing did not really work.
+
+ - Added workaround in xltdevinf.c to allow extra rx-pref
+ and tx-pref (as some clients send these).
+
+2006-11-02 - Added detail parsing error logging for sml toolkit decoder.
+
+2006-11-08 - xpt: gzip receiving did not work correctly when sender did not
+ use chunked transfer, but sent gzipped message in a normal
+ response (where content-length is the size of the compressed data).
+
+2006-12-06 - XPT: fixed httpRead to make sure it always waits until it
+ can return some data except if connection or document ends.
+ This is important for chunked mode, because there are cases where the server
+ sends some data (e.g. a chunk header only) that does not
+ decode to any output of data. In this case, httpRead now
+ repeats reading from the TCP socket until data arrives or
+ connection ends.
+
+2007-06-20 - xpt/http: Enhanced parsing of Connection: header, must be prepared for
+ multiple tokens in http 1.1, of which only one might be the "close" token.
+
+2007-08-14 - Made SML generator accept a <Property> with <ValEnum>s AND a <DataType>,
+ when the latter is an empty string. Still, only ValEnums will be generated.
+ This is to correctly translate WBXML back to XML from devices like E90.
+
diff --git a/src/syncml_tk/doc/ReadmeMaintenance4_1.doc b/src/syncml_tk/doc/ReadmeMaintenance4_1.doc
new file mode 100644
index 0000000..74ea1f8
--- /dev/null
+++ b/src/syncml_tk/doc/ReadmeMaintenance4_1.doc
Binary files differ
diff --git a/src/syncml_tk/doc/ReadmeMaintenance4_2.doc b/src/syncml_tk/doc/ReadmeMaintenance4_2.doc
new file mode 100644
index 0000000..5d97ee0
--- /dev/null
+++ b/src/syncml_tk/doc/ReadmeMaintenance4_2.doc
Binary files differ
diff --git a/src/syncml_tk/doc/ReadmeMaintenance4_3.doc b/src/syncml_tk/doc/ReadmeMaintenance4_3.doc
new file mode 100644
index 0000000..7842a48
--- /dev/null
+++ b/src/syncml_tk/doc/ReadmeMaintenance4_3.doc
Binary files differ
diff --git a/src/syncml_tk/doc/ReadmeMaintenance4_4.doc b/src/syncml_tk/doc/ReadmeMaintenance4_4.doc
new file mode 100644
index 0000000..db9dbfb
--- /dev/null
+++ b/src/syncml_tk/doc/ReadmeMaintenance4_4.doc
Binary files differ
diff --git a/src/syncml_tk/opensource_license.txt b/src/syncml_tk/opensource_license.txt
new file mode 100644
index 0000000..f9454c9
--- /dev/null
+++ b/src/syncml_tk/opensource_license.txt
@@ -0,0 +1,42 @@
+LICENSE
+
+The Copyright Holders of this software, including all accompanying
+documentation ("Software"), hereby grant, royalty free and for any
+purpose, permission to use, copy, modify and prepare derivative works
+therefrom, distribute, publish, sublicense and sell copies of the
+Software and to permit persons to whom the Software is furnished to
+do the same, all subject to the following conditions:
+
+1. The complete text of the following noticeshall be reproduced
+on each copy or substantial copy of the Software in a location readily
+viewable to users of the Software:
+
+NOTICE
+
+Copyright (c) Ericsson, IBM, Lotus, Matsushita Communication Industrial Co., Ltd.,
+Motorola, Nokia, Openwave Systems, Inc., Palm, Inc., Psion, Starfish Software,
+Symbian, Ltd. (2001-2002).
+
+All Rights Reserved.
+
+Implementation of all or part of any Software may require licenses
+under third party intellectual property rights, including without limitation,
+patent rights. The Copyright Holders are not responsible and shall not
+be held responsible in any manner for identifying or failing to identify
+any or all such third party intellectual property rights.
+
+THIS DOCUMENT AND THE INFORMATION CONTAINED HEREIN ARE PROVIDED ON AN "AS IS"
+BASIS WITHOUT WARRANTY OF ANY KIND AND ERICSSON, IBM, LOTUS,
+MATSUSHITA COMMUNICATION INDUSTRIAL CO. LTD, MOTOROLA, NOKIA, OPENWAVE,
+PALM INC., PSION, STARFISH SOFTWARE, SYMBIAN AND ALL OTHER SYNCML
+SPONSORS DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT
+LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION HEREIN WILL NOT
+INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR
+FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL ERICSSON, IBM, LOTUS,
+MATSUSHITA COMMUNICATION INDUSTRIAL CO., LTD, MOTOROLA, NOKIA, OPENWAVE,
+PALM INC., PSION, STARFISH SOFTWARE. SYMBIAN OR ANY OTHER SYNCML SPONSOR
+BE LIABLE TO ANY PARTY FOR ANY LOSS OF PROFITS, LOSS OF BUSINESS, LOSS
+OF USE OF DATA, INTERRUPTION OF BUSINESS, OR FOR DIRECT, INDIRECT,
+SPECIAL OR EXEMPLARY, INCIDENTAL, PUNITIVE OR CONSEQUENTIAL DAMAGES OF
+ANY KIND IN CONNECTION WITH THIS DOCUMENT OR THE INFORMATION CONTAINED
+HEREIN, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH LOSS OR DAMAGE. \ No newline at end of file
diff --git a/src/syncml_tk/src/sml/SyncML.def b/src/syncml_tk/src/sml/SyncML.def
new file mode 100755
index 0000000..bd17cf7
--- /dev/null
+++ b/src/syncml_tk/src/sml/SyncML.def
@@ -0,0 +1,151 @@
+EXPORTS
+smlAddCmd
+smlAlertCmd
+smlAllocAdd
+smlAllocAlert
+smlAllocAtomic
+smlAllocChal
+smlAllocCopy
+smlAllocCred
+smlAllocDelete
+smlAllocDevInfCTCap
+smlAllocDevInfCTData
+smlAllocDevInfCTDataList
+smlAllocDevInfCTDataProp
+smlAllocDevInfCTDataPropList
+smlAllocDevInfCtcapList
+smlAllocDevInfDSMem
+smlAllocDevInfDatastore
+smlAllocDevInfDatastoreList
+smlAllocDevInfDevInf
+smlAllocDevInfExt
+smlAllocDevInfExtList
+smlAllocDevInfSyncCap
+smlAllocDevInfXmit
+smlAllocDevInfXmitList
+smlAllocExec
+smlAllocGeneric
+smlAllocGet
+smlAllocItem
+smlAllocItemList
+smlAllocMap
+smlAllocMapItem
+smlAllocMapItemList
+smlAllocMetInfAnchor
+smlAllocMetInfMem
+smlAllocMetInfMetInf
+smlAllocPcdata
+smlAllocPcdataList
+smlAllocPut
+smlAllocReplace
+smlAllocResults
+smlAllocSearch
+smlAllocSequence
+smlAllocSource
+smlAllocSourceList
+smlAllocSourceRefList
+smlAllocStatus
+smlAllocSync
+smlAllocSyncHdr
+smlAllocTarget
+smlAllocTargetRefList
+smlAllocUnknownProtoElement
+smlCopyCmd
+smlDeleteCmd
+smlEndAtomic
+smlEndMessage
+smlEndSequence
+smlEndSync
+smlExecCmd
+smlFreeAlert
+smlFreeAtomic
+smlFreeChalPtr
+smlFreeCredPtr
+smlFreeDevInfCTCap
+smlFreeDevInfCTData
+smlFreeDevInfCTDataList
+smlFreeDevInfCTDataProp
+smlFreeDevInfCTDataPropList
+smlFreeDevInfCtcapList
+smlFreeDevInfDSMem
+smlFreeDevInfDatastore
+smlFreeDevInfDatastoreList
+smlFreeDevInfDevInf
+smlFreeDevInfExt
+smlFreeDevInfExtList
+smlFreeDevInfSynccap
+smlFreeDevInfXmit
+smlFreeDevInfXmitList
+smlFreeExec
+smlFreeGeneric
+smlFreeGetPut
+smlFreeItemList
+smlFreeItemPtr
+smlFreeMap
+smlFreeMapItemList
+smlFreeMapItemPtr
+smlFreeMetinfAnchor
+smlFreeMetinfMem
+smlFreeMetinfMetinf
+smlFreePcdata
+smlFreePcdataList
+smlFreeProtoElement
+smlFreeResults
+smlFreeSearch
+smlFreeSourceList
+smlFreeSourceRefList
+smlFreeSourceTargetPtr
+smlFreeStatus
+smlFreeSync
+smlFreeSyncHdr
+smlFreeTargetRefList
+smlGetCmd
+smlGetFreeBuffer
+smlInit
+smlInitInstance
+smlLibFree
+smlLibMalloc
+smlLibMemcmp
+smlLibMemcpy
+smlLibMemmove
+smlLibMemset
+smlLibPrint
+smlLibRealloc
+smlLibStrcat
+smlLibStrchr
+smlLibStrcmp
+smlLibStrcpy
+smlLibStrdup
+smlLibStrlen
+smlLibStrncat
+smlLibStrncmp
+smlLibStrncpy
+smlLibStrstr
+smlLibVprintf
+smlLockReadBuffer
+smlLockWriteBuffer
+smlMapCmd
+smlPcdata2String
+smlPcdataDup
+smlProcessData
+smlPutCmd
+smlReplaceCmd
+smlResultsCmd
+smlSearchCmd
+smlSetCallbacks
+smlSetEncoding
+smlSetSyncMLOptions
+smlSetUserData
+smlStartAtomic
+smlStartMessage
+smlStartMessageExt
+smlStartSequence
+smlStartSync
+smlStatusCmd
+smlString2Pcdata
+smlTerminate
+smlTerminateInstance
+smlUnlockReadBuffer
+smlUnlockWriteBuffer
+smlStartEvaluation
+smlEndEvaluation
diff --git a/src/syncml_tk/src/sml/inc/palm/define.h b/src/syncml_tk/src/sml/inc/palm/define.h
new file mode 100755
index 0000000..91d05e8
--- /dev/null
+++ b/src/syncml_tk/src/sml/inc/palm/define.h
@@ -0,0 +1,102 @@
+/**
+ * @file
+ * Compiler Flag Definition File
+ *
+ * @target_system palm
+ * @target_os palm
+ */
+
+/*
+ * Copyright Notice
+ * Copyright (c) Ericsson, IBM, Lotus, Matsushita Communication
+ * Industrial Co., Ltd., Motorola, Nokia, Openwave Systems, Inc.,
+ * Palm, Inc., Psion, Starfish Software, Symbian, Ltd. (2001).
+ * All Rights Reserved.
+ * Implementation of all or part of any Specification may require
+ * licenses under third party intellectual property rights,
+ * including without limitation, patent rights (such a third party
+ * may or may not be a Supporter). The Sponsors of the Specification
+ * are not responsible and shall not be held responsible in any
+ * manner for identifying or failing to identify any or all such
+ * third party intellectual property rights.
+ *
+ * THIS DOCUMENT AND THE INFORMATION CONTAINED HEREIN ARE PROVIDED
+ * ON AN "AS IS" BASIS WITHOUT WARRANTY OF ANY KIND AND ERICSSON, IBM,
+ * LOTUS, MATSUSHITA COMMUNICATION INDUSTRIAL CO. LTD, MOTOROLA,
+ * NOKIA, PALM INC., PSION, STARFISH SOFTWARE AND ALL OTHER SYNCML
+ * SPONSORS DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+ * BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
+ * HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
+ * SHALL ERICSSON, IBM, LOTUS, MATSUSHITA COMMUNICATION INDUSTRIAL CO.,
+ * LTD, MOTOROLA, NOKIA, PALM INC., PSION, STARFISH SOFTWARE OR ANY
+ * OTHER SYNCML SPONSOR BE LIABLE TO ANY PARTY FOR ANY LOSS OF
+ * PROFITS, LOSS OF BUSINESS, LOSS OF USE OF DATA, INTERRUPTION OF
+ * BUSINESS, OR FOR DIRECT, INDIRECT, SPECIAL OR EXEMPLARY, INCIDENTAL,
+ * PUNITIVE OR CONSEQUENTIAL DAMAGES OF ANY KIND IN CONNECTION WITH
+ * THIS DOCUMENT OR THE INFORMATION CONTAINED HEREIN, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH LOSS OR DAMAGE.
+ *
+ * The above notice and this paragraph must be included on all copies
+ * of this document that are made.
+ *
+ */
+
+/**
+ * File for Palm OS Specific Compiler Flags
+ */
+
+#ifndef _DEFINE_H
+ #define _DEFINE_H
+
+#define __PALM_OS__
+
+
+#define PILOT_PRECOMPILED_HEADERS_OFF
+
+#define SML_API
+#define SML_API_DEF
+#define XPT_API
+#define XPT_API_DEF
+
+/* Multi segment macro for Palm OS */
+#define LIB_FUNC __attribute__ ((section ("lib")))
+#define MGR_FUNC __attribute__ ((section ("mgr")))
+#define WSM_FUNC __attribute__ ((section ("wsm")))
+#define XLT_FUNC __attribute__ ((section ("xlt")))
+
+
+// SyncML commands which are not required in a client can be switched off
+
+#define ATOMIC_SEND
+#define ATOMIC_RECEIVE
+#define COPY_SEND
+#define COPY_RECEIVE
+#define EXEC_SEND
+#define EXEC_RECEIVE
+#define MAP_RECEIVE
+#define MAPITEM_RECEIVE
+#define SEARCH_SEND
+#define SEARCH_RECEIVE
+#define SEQUENCE_SEND
+#define SEQUENCE_RECEIVE
+#define ADD_SEND
+#define GET_SEND
+#define RESULT_RECEIVE
+
+/* --- Toolkit a la Carte --- */
+/* Compilerflags to include only subsets of the Toolkit functionality */
+/* enable Alloc helpers */
+#define __USE_ALLOCFUNCS__
+
+/* Compiler Flag to include the XML Parsing Module */
+#define __SML_XML__
+/* Compiler Flag to include the WBXML Parsing Module */
+#define __SML_WBXML__
+/* Compiler Flag to include only the Minimum Toolkit functionality */
+//#define __SML_LITE__
+#define __USE_EXTENSIONS__
+#define __USE_METINF__
+#define __USE_DEVINF__
+
+#endif
diff --git a/src/syncml_tk/src/sml/inc/sml.h b/src/syncml_tk/src/sml/inc/sml.h
new file mode 100755
index 0000000..c8d641b
--- /dev/null
+++ b/src/syncml_tk/src/sml/inc/sml.h
@@ -0,0 +1,328 @@
+/**
+ * @file
+ * External SyncML API
+ *
+ * @target_system all
+ * @target_os all
+ * @description Platform independent definition of the SyncML API functions
+ * This is the external API exposed to applications using SyncML
+ */
+
+
+/*
+ * Copyright Notice
+ * Copyright (c) Ericsson, IBM, Lotus, Matsushita Communication
+ * Industrial Co., Ltd., Motorola, Nokia, Openwave Systems, Inc.,
+ * Palm, Inc., Psion, Starfish Software, Symbian, Ltd. (2001).
+ * All Rights Reserved.
+ * Implementation of all or part of any Specification may require
+ * licenses under third party intellectual property rights,
+ * including without limitation, patent rights (such a third party
+ * may or may not be a Supporter). The Sponsors of the Specification
+ * are not responsible and shall not be held responsible in any
+ * manner for identifying or failing to identify any or all such
+ * third party intellectual property rights.
+ *
+ * THIS DOCUMENT AND THE INFORMATION CONTAINED HEREIN ARE PROVIDED
+ * ON AN "AS IS" BASIS WITHOUT WARRANTY OF ANY KIND AND ERICSSON, IBM,
+ * LOTUS, MATSUSHITA COMMUNICATION INDUSTRIAL CO. LTD, MOTOROLA,
+ * NOKIA, PALM INC., PSION, STARFISH SOFTWARE AND ALL OTHER SYNCML
+ * SPONSORS DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+ * BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
+ * HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
+ * SHALL ERICSSON, IBM, LOTUS, MATSUSHITA COMMUNICATION INDUSTRIAL CO.,
+ * LTD, MOTOROLA, NOKIA, PALM INC., PSION, STARFISH SOFTWARE OR ANY
+ * OTHER SYNCML SPONSOR BE LIABLE TO ANY PARTY FOR ANY LOSS OF
+ * PROFITS, LOSS OF BUSINESS, LOSS OF USE OF DATA, INTERRUPTION OF
+ * BUSINESS, OR FOR DIRECT, INDIRECT, SPECIAL OR EXEMPLARY, INCIDENTAL,
+ * PUNITIVE OR CONSEQUENTIAL DAMAGES OF ANY KIND IN CONNECTION WITH
+ * THIS DOCUMENT OR THE INFORMATION CONTAINED HEREIN, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH LOSS OR DAMAGE.
+ *
+ * The above notice and this paragraph must be included on all copies
+ * of this document that are made.
+ *
+ */
+
+
+#ifndef _SML_H
+ #define _SML_H
+
+
+/*************************************************************************
+ * Definitions
+ *************************************************************************/
+
+#include <smldef.h>
+#include <smldtd.h>
+
+
+
+
+/*************************************************************************
+ * External Functions
+ *************************************************************************/
+
+#if !defined(NOWSM) || !__LINK_TOOLKIT_STATIC__
+
+/*
+ * ============================
+ * General SyncML Functions
+ * ============================
+ */
+SML_API_DEF Ret_t smlInit(SmlOptionsPtr_t pOptions);
+SML_API_DEF Ret_t smlSetSyncMLOptions (SmlOptionsPtr_t pOptions);
+SML_API_DEF Ret_t smlTerminate(void);
+
+#endif
+
+
+/*
+ * ============================
+ * Workspace Handling Functions
+ * ============================
+ */
+
+SML_API_DEF Ret_t smlLockReadBuffer(InstanceID_t id, MemPtr_t *pReadPosition, MemSize_t *usedSize);
+SML_API_DEF Ret_t smlUnlockReadBuffer(InstanceID_t id, MemSize_t processedBytes);
+#ifdef NOWSM
+SML_API Ret_t smlSetMaxOutgoingSize(InstanceID_t id, MemSize_t maxOutgoingSize);
+SML_API Ret_t smlSetOutgoingBegin(InstanceID_t id);
+SML_API Ret_t smlReadOutgoingAgain(InstanceID_t id);
+SML_API Ret_t smlPeekMessageBuffer(InstanceID_t id, Boolean_t outgoing, MemPtr_t *message, MemSize_t *msgsize);
+#endif
+SML_API_DEF Ret_t smlLockWriteBuffer(InstanceID_t id, MemPtr_t *pWritePosition, MemSize_t *freeSize);
+SML_API_DEF Ret_t smlUnlockWriteBuffer(InstanceID_t id, MemSize_t writtenBytes);
+
+
+
+/*
+ * ===========================================
+ * Protocol Element Building Functions (for Originator)
+ * ===========================================
+ */
+
+/* Protocol Management */
+SML_API_DEF Ret_t smlStartMessage(InstanceID_t id, SmlSyncHdrPtr_t pContent);
+SML_API_DEF Ret_t smlStartMessageExt(InstanceID_t id, SmlSyncHdrPtr_t pContent, SmlVersion_t vers); /* %%% added by luz 2003-08-06) */
+SML_API_DEF Ret_t smlEndMessage(InstanceID_t id, Boolean_t final);
+SML_API_DEF Ret_t smlStartSync(InstanceID_t id, SmlSyncPtr_t pContent);
+SML_API_DEF Ret_t smlEndSync(InstanceID_t id);
+
+#ifdef ATOMIC_SEND /* these API calls are NOT included in the Toolkit lite version */
+ SML_API_DEF Ret_t smlStartAtomic(InstanceID_t id, SmlAtomicPtr_t pContent);
+ SML_API_DEF Ret_t smlEndAtomic(InstanceID_t id);
+#endif
+#ifdef SEQUENCE_SEND
+ SML_API_DEF Ret_t smlStartSequence(InstanceID_t id, SmlSequencePtr_t pContent);
+ SML_API_DEF Ret_t smlEndSequence(InstanceID_t id);
+#endif
+
+/* Sync Commands */
+#ifdef ADD_SEND
+ SML_API_DEF Ret_t smlAddCmd(InstanceID_t id, SmlAddPtr_t pContent);
+#endif
+SML_API_DEF Ret_t smlAlertCmd(InstanceID_t id, SmlAlertPtr_t pContent);
+SML_API_DEF Ret_t smlDeleteCmd(InstanceID_t id, SmlDeletePtr_t pContent);
+#ifdef GET_SEND
+ SML_API_DEF Ret_t smlGetCmd(InstanceID_t id, SmlGetPtr_t pContent);
+#endif
+SML_API_DEF Ret_t smlPutCmd(InstanceID_t id, SmlPutPtr_t pContent);
+SML_API_DEF Ret_t smlMapCmd(InstanceID_t id, SmlMapPtr_t pContent);
+SML_API_DEF Ret_t smlResultsCmd(InstanceID_t id, SmlResultsPtr_t pContent);
+SML_API_DEF Ret_t smlStatusCmd(InstanceID_t id, SmlStatusPtr_t pContent);
+SML_API_DEF Ret_t smlReplaceCmd(InstanceID_t id, SmlReplacePtr_t pContent);
+
+#ifdef COPY_SEND /* these API calls are NOT included in the Toolkit lite version */
+ SML_API_DEF Ret_t smlCopyCmd(InstanceID_t id, SmlCopyPtr_t pContent);
+#endif
+SML_API_DEF Ret_t smlMoveCmd(InstanceID_t id, SmlMovePtr_t pContent);
+#ifdef EXEC_SEND
+ SML_API_DEF Ret_t smlExecCmd(InstanceID_t id, SmlExecPtr_t pContent);
+#endif
+#ifdef SEARCH_SEND
+ SML_API_DEF Ret_t smlSearchCmd(InstanceID_t id, SmlSearchPtr_t pContent);
+#endif
+SML_API_DEF Ret_t smlStartEvaluation(InstanceID_t id);
+SML_API_DEF Ret_t smlEndEvaluation(InstanceID_t id, MemSize_t *freemem);
+
+
+/*
+ * ============================================
+ * Command Dispatching Functions (for Receiver)
+ * ============================================
+ */
+
+SML_API_DEF Ret_t smlProcessData(InstanceID_t id, SmlProcessMode_t mode);
+
+
+
+/*
+ * ====================================================
+ * Callback Functions to be implemented by the Receiver
+ * ====================================================
+ */
+
+/* Protocol Management */
+typedef Ret_t (*smlStartMessageFunc) (InstanceID_t id, VoidPtr_t userData, SmlSyncHdrPtr_t pContent);
+typedef Ret_t (*smlEndMessageFunc) (InstanceID_t id, VoidPtr_t userData, Boolean_t final);
+typedef Ret_t (*smlStartSyncFunc) (InstanceID_t id, VoidPtr_t userData, SmlSyncPtr_t pContent);
+typedef Ret_t (*smlEndSyncFunc) (InstanceID_t id, VoidPtr_t userData);
+
+#ifdef ATOMIC_RECEIVE /* these callbacks are NOT included in the Toolkit lite version */
+ typedef Ret_t (*smlStartAtomicFunc) (InstanceID_t id, VoidPtr_t userData, SmlAtomicPtr_t pContent);
+ typedef Ret_t (*smlEndAtomicFunc) (InstanceID_t id, VoidPtr_t userData);
+#endif
+#ifdef SEQUENCE_RECEIVE
+ typedef Ret_t (*smlStartSequenceFunc) (InstanceID_t id, VoidPtr_t userData, SmlSequencePtr_t pContent);
+ typedef Ret_t (*smlEndSequenceFunc) (InstanceID_t id, VoidPtr_t userData);
+#endif
+
+/* Sync Commands */
+typedef Ret_t (*smlAddCmdFunc) (InstanceID_t id, VoidPtr_t userData, SmlAddPtr_t pContent);
+typedef Ret_t (*smlAlertCmdFunc) (InstanceID_t id, VoidPtr_t userData, SmlAlertPtr_t pContent);
+typedef Ret_t (*smlDeleteCmdFunc) (InstanceID_t id, VoidPtr_t userData, SmlDeletePtr_t pContent);
+typedef Ret_t (*smlGetCmdFunc) (InstanceID_t id, VoidPtr_t userData, SmlGetPtr_t pContent);
+typedef Ret_t (*smlPutCmdFunc) (InstanceID_t id, VoidPtr_t userData, SmlPutPtr_t pContent);
+#ifdef MAP_RECEIVE
+ typedef Ret_t (*smlMapCmdFunc) (InstanceID_t id, VoidPtr_t userData, SmlMapPtr_t pContent);
+#endif
+#ifdef RESULT_RECEIVE
+ typedef Ret_t (*smlResultsCmdFunc) (InstanceID_t id, VoidPtr_t userData, SmlResultsPtr_t pContent);
+#endif
+typedef Ret_t (*smlStatusCmdFunc) (InstanceID_t id, VoidPtr_t userData, SmlStatusPtr_t pContent);
+typedef Ret_t (*smlReplaceCmdFunc) (InstanceID_t id, VoidPtr_t userData, SmlReplacePtr_t pContent);
+
+#ifdef COPY_RECEIVE /* these callbacks are NOT included in the Toolkit lite version */
+ typedef Ret_t (*smlCopyCmdFunc) (InstanceID_t id, VoidPtr_t userData, SmlCopyPtr_t param);
+#endif
+#ifdef EXEC_RECEIVE
+ typedef Ret_t (*smlExecCmdFunc) (InstanceID_t id, VoidPtr_t userData, SmlExecPtr_t pContent);
+#endif
+#ifdef SEARCH_RECEIVE
+ typedef Ret_t (*smlSearchCmdFunc) (InstanceID_t id, VoidPtr_t userData, SmlSearchPtr_t pContent);
+#endif
+ typedef Ret_t (*smlMoveCmdFunc) (InstanceID_t id, VoidPtr_t userData, SmlMovePtr_t param);
+
+
+/* Other Callbacks */
+typedef Ret_t (*smlHandleErrorFunc) (InstanceID_t id, VoidPtr_t userData);
+typedef Ret_t (*smlTransmitChunkFunc) (InstanceID_t id, VoidPtr_t userData);
+
+
+
+
+/**
+ * Structure defining references to the applications callback implementations
+ **/
+typedef struct sml_callbacks_s {
+ /* Protocol Management Callbacks */
+ smlStartMessageFunc startMessageFunc;
+ smlEndMessageFunc endMessageFunc;
+ smlStartSyncFunc startSyncFunc;
+ smlEndSyncFunc endSyncFunc;
+#ifdef ATOMIC_RECEIVE /* these callbacks are NOT included in the Toolkit lite version */
+ smlStartAtomicFunc startAtomicFunc;
+ smlEndAtomicFunc endAtomicFunc;
+#endif
+#ifdef SEQUENCE_RECEIVE
+ smlStartSequenceFunc startSequenceFunc;
+ smlEndSequenceFunc endSequenceFunc;
+#endif
+ /* Sync Command callbacks */
+ smlAddCmdFunc addCmdFunc;
+ smlAlertCmdFunc alertCmdFunc;
+ smlDeleteCmdFunc deleteCmdFunc;
+ smlGetCmdFunc getCmdFunc;
+ smlPutCmdFunc putCmdFunc;
+#ifdef MAP_RECEIVE
+ smlMapCmdFunc mapCmdFunc;
+#endif
+#ifdef RESULT_RECEIVE
+ smlResultsCmdFunc resultsCmdFunc;
+#endif
+ smlStatusCmdFunc statusCmdFunc;
+ smlReplaceCmdFunc replaceCmdFunc;
+#ifdef COPY_RECEIVE /* these callbacks are NOT included in the Toolkit lite version */
+ smlCopyCmdFunc copyCmdFunc;
+#endif
+#ifdef EXEC_RECEIVE
+ smlExecCmdFunc execCmdFunc;
+#endif
+#ifdef SEARCH_RECEIVE
+ smlSearchCmdFunc searchCmdFunc;
+#endif
+ smlMoveCmdFunc moveCmdFunc;
+ /* Other Callbacks */
+ smlHandleErrorFunc handleErrorFunc;
+ smlTransmitChunkFunc transmitChunkFunc;
+ //smlPrintFunc printFunc;
+} *SmlCallbacksPtr_t, SmlCallbacks_t;
+
+typedef const SmlCallbacks_t *SmlCallbacksCPtr_t;
+
+
+/*
+ * ============================
+ * Instance Management Functions
+ * ============================
+ */
+
+SML_API_DEF Ret_t smlInitInstance(SmlCallbacksCPtr_t pCallbacks, SmlInstanceOptionsPtr_t pOptions, VoidPtr_t pUserData, InstanceID_t *pId);
+SML_API_DEF Ret_t smlTerminateInstance (InstanceID_t id);
+SML_API_DEF Ret_t smlSetCallbacks (InstanceID_t id, SmlCallbacksCPtr_t pCallbacks);
+SML_API_DEF Ret_t smlSetUserData (InstanceID_t id, VoidPtr_t pUserData);
+// added by luz %%%:
+SML_API Ret_t smlGetUserData(InstanceID_t id, VoidPtr_t *ppUserData);
+SML_API Ret_t smlGetEncoding(InstanceID_t id, SmlEncoding_t *pEncoding);
+
+#ifndef __SML_LITE__ /* these API calls are NOT included in the Toolkit lite version */
+ SML_API_DEF Ret_t smlSetEncoding (InstanceID_t id, SmlEncoding_t encoding);
+#endif
+
+
+
+/*
+ * ===================================
+ * Some Helper Functions and Utilities
+ * ===================================
+ */
+
+SML_API_DEF Ret_t smlFreeProtoElement(VoidPtr_t pProtoElement);
+SML_API_DEF void smlFreePcdata(SmlPcdataPtr_t pPcdata);
+#ifndef __SML_LITE__ /* these API calls are NOT included in the Toolkit lite version */
+ SML_API_DEF String_t smlPcdata2String( SmlPcdataPtr_t pcdata );
+ SML_API_DEF SmlPcdataPtr_t smlString2Pcdata( String_t str );
+ SML_API_DEF SmlPcdataPtr_t smlPcdataDup(SmlPcdataPtr_t pcdata);
+ SML_API_DEF MemSize_t smlGetFreeBuffer(InstanceID_t id);
+#endif
+
+#ifndef __PALM_OS__ /* these API calls are NOT exported in the Palm OS version */
+SML_API_DEF void *smlLibMemset(void *pObject, int value, MemSize_t count);
+SML_API_DEF void *smlLibMemcpy(void *pTarget, const void *pSource, MemSize_t count);
+SML_API_DEF int smlLibMemcmp(const void *pTarget, const void *pSource, MemSize_t count);
+//SML_API_DEF void *smlLibMalloc(MemSize_t size);
+SML_API_DEF void smlLibFree(void *pObject);
+SML_API_DEF MemSize_t smlLibMemsize(const void *pObject);
+
+SML_API_DEF String_t smlLibStrdup (const char *constStringP);
+SML_API_DEF String_t smlLibStrcpy(const char *pTarget, const char *pSource);
+SML_API_DEF int smlLibStrcmp(const char *pTarget, const char *pSource);
+SML_API_DEF int smlLibStrlen(const char *pString);
+SML_API_DEF String_t smlLibStrncpy(const char *pTarget, const char *pSource, int count);
+SML_API_DEF int smlLibStrncmp(const char *pTarget, const char *pSource, int count);
+SML_API_DEF String_t smlLibStrcat(const char *pTarget, const char *pSource);
+SML_API_DEF String_t smlLibStrchr(const char *pString, char character);
+#ifndef __SML_LITE__ /* these API calls are NOT included in the Toolkit lite version */
+ SML_API_DEF void smlLibPrint(const char *text, ...);
+ SML_API_DEF void *smlLibMemmove(void *pTarget, const void *pSource, MemSize_t count);
+ SML_API_DEF void *smlLibRealloc(void *pObject, MemSize_t size);
+ SML_API_DEF String_t smlLibStrncat(const char *pTarget, const char *pSource, int count);
+ SML_API_DEF String_t smlLibStrstr(const char *pString, const char *pSubString);
+#endif
+#endif
+
+
+
+#endif
diff --git a/src/syncml_tk/src/sml/inc/smldef.h b/src/syncml_tk/src/sml/inc/smldef.h
new file mode 100755
index 0000000..e918b83
--- /dev/null
+++ b/src/syncml_tk/src/sml/inc/smldef.h
@@ -0,0 +1,238 @@
+/**
+ * @file
+ * Type definitions for SyncML
+ *
+ * @target_system all
+ * @target_os all
+ *
+ * @description Platform independent header with syncML types and definitions
+ */
+
+
+/*
+ * Copyright Notice
+ * Copyright (c) Ericsson, IBM, Lotus, Matsushita Communication
+ * Industrial Co., Ltd., Motorola, Nokia, Openwave Systems, Inc.,
+ * Palm, Inc., Psion, Starfish Software, Symbian, Ltd. (2001).
+ * All Rights Reserved.
+ * Implementation of all or part of any Specification may require
+ * licenses under third party intellectual property rights,
+ * including without limitation, patent rights (such a third party
+ * may or may not be a Supporter). The Sponsors of the Specification
+ * are not responsible and shall not be held responsible in any
+ * manner for identifying or failing to identify any or all such
+ * third party intellectual property rights.
+ *
+ * THIS DOCUMENT AND THE INFORMATION CONTAINED HEREIN ARE PROVIDED
+ * ON AN "AS IS" BASIS WITHOUT WARRANTY OF ANY KIND AND ERICSSON, IBM,
+ * LOTUS, MATSUSHITA COMMUNICATION INDUSTRIAL CO. LTD, MOTOROLA,
+ * NOKIA, PALM INC., PSION, STARFISH SOFTWARE AND ALL OTHER SYNCML
+ * SPONSORS DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+ * BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
+ * HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
+ * SHALL ERICSSON, IBM, LOTUS, MATSUSHITA COMMUNICATION INDUSTRIAL CO.,
+ * LTD, MOTOROLA, NOKIA, PALM INC., PSION, STARFISH SOFTWARE OR ANY
+ * OTHER SYNCML SPONSOR BE LIABLE TO ANY PARTY FOR ANY LOSS OF
+ * PROFITS, LOSS OF BUSINESS, LOSS OF USE OF DATA, INTERRUPTION OF
+ * BUSINESS, OR FOR DIRECT, INDIRECT, SPECIAL OR EXEMPLARY, INCIDENTAL,
+ * PUNITIVE OR CONSEQUENTIAL DAMAGES OF ANY KIND IN CONNECTION WITH
+ * THIS DOCUMENT OR THE INFORMATION CONTAINED HEREIN, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH LOSS OR DAMAGE.
+ *
+ * The above notice and this paragraph must be included on all copies
+ * of this document that are made.
+ *
+ */
+
+
+
+
+#ifndef _SML_DEF_H
+ #define _SML_DEF_H
+
+
+
+/*************************************************************************
+ * Definitions
+ *************************************************************************/
+
+
+
+/**
+ * include target/compiler specific defines/options/settings
+ */
+#include "define.h"
+
+
+/*
+ * ===================================
+ * Common all-purpose type definitions
+ * ===================================
+ */
+
+
+
+/*
+ * ==================================
+ * Basic data types
+ * ==================================
+ */
+typedef short Short_t; /**< short integer, 16 Bytes */
+typedef long Long_t; /**< long integer, 32 Bytes */
+typedef char* String_t; /**< String pointer, */
+typedef unsigned char Byte_t; /**< a single byte */
+typedef Byte_t Boolean_t; /**< a boolean */
+#ifndef NULL /**< a NULL pointer */
+ #define NULL (void*) 0
+#endif
+#ifndef FALSE
+#define FALSE 0
+#endif
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+
+typedef Short_t Ret_t; /**< Return Type of API Commands */
+typedef Long_t Length_t; /**< System dependent string length */
+typedef Short_t MemHandle_t; /**< Memory object Handle */
+typedef unsigned char *MemPtr_t; /**< Memory object Pointer */
+typedef void *VoidPtr_t; /**< Void Pointer */
+typedef Long_t MemSize_t; /**< System dependent memory object size */
+typedef unsigned char MemByte_t; /**< Memory element */
+typedef unsigned int Flag_t; /**< A generic flag type. This type is
+ * used to declare variables in structures
+ * wherever flags are used.
+ */
+
+
+/*
+ * ==================================
+ * Definitions used in the SyncML API
+ * ==================================
+ */
+
+/**
+ * Application callback function displaying output strings to the user
+ */
+typedef void (*smlPrintFunc) (String_t outputString);
+
+#ifdef NOWSM
+/**
+ * structure describing the options and setting of this syncml process
+ */
+typedef struct sml_options_s {
+ smlPrintFunc defaultPrintFunc; /**< default application callback for displaying strings, */
+ MemSize_t maxWorkspaceAvailMem; /**< size which all workspaces in total MUST not exceed */
+} *SmlOptionsPtr_t, SmlOptions_t;
+#endif
+
+
+
+/**
+ * Reference of an instance
+ */
+#ifdef NOWSM
+typedef void *InstanceID_t; /**< without wsm, instance ID is direct pointer to instance info */
+#else
+typedef MemHandle_t InstanceID_t; /**< Handle, used as a unique ID of
+ * an synchronization instance
+ */
+#endif
+
+
+/**
+ * Type of used encoding
+ */
+typedef enum {
+ SML_UNDEF = 0,
+ SML_WBXML,
+ SML_XML
+} SmlEncoding_t;
+
+
+/**
+ * SyncML version
+ */
+// %%% added luz 2003-07-31:
+// %%% added DS 1.2 luz 2005-08-17:
+typedef enum {
+ SML_VERS_UNDEF = 0,
+ SML_VERS_1_0,
+ SML_VERS_1_1,
+ SML_VERS_1_2,
+ SML_NUM_VERS
+} SmlVersion_t;
+
+/**
+ * structure describing the options of an instance,
+ */
+typedef struct sml_instance_options_s {
+ SmlEncoding_t encoding; /**< Used encoding type, */
+ MemSize_t workspaceSize; /**< size of the workspace to allocate (instance buffer size if NOWSM defined) */
+ #ifndef NOWSM
+ String_t workspaceName; /**< name of the workspace */
+ #else
+ MemSize_t maxOutgoingSize; /**< max size of outgoing message, 0 if no restriction */
+ #endif
+} *SmlInstanceOptionsPtr_t, SmlInstanceOptions_t;
+
+
+/**
+ * Processing modes
+ */
+typedef enum {
+ SML_DO_NOTHING = 0,
+ SML_FIRST_COMMAND,
+ SML_NEXT_COMMAND,
+ SML_NEXT_MESSAGE,
+ SML_ALL_COMMANDS
+} SmlProcessMode_t;
+
+
+/**
+ * Requested buffer pointer position
+ */
+typedef enum {
+ SML_FIRST_DATA_ITEM = 0,
+ SML_FIRST_FREE_ITEM
+} SmlBufPtrPos_t;
+
+
+
+/**
+ * SyncML Protocol Management and Command Elements (PE)
+ */
+typedef enum {
+ SML_PE_UNDEF = 0,
+ SML_PE_ERROR,
+ SML_PE_ADD,
+ SML_PE_ALERT,
+ SML_PE_ATOMIC_START,
+ SML_PE_ATOMIC_END,
+ SML_PE_COPY,
+ SML_PE_DELETE,
+ SML_PE_EXEC,
+ SML_PE_GET,
+ SML_PE_MAP,
+ SML_PE_PUT,
+ SML_PE_RESULTS,
+ SML_PE_SEARCH,
+ SML_PE_SEQUENCE_START,
+ SML_PE_SEQUENCE_END,
+ SML_PE_STATUS,
+ SML_PE_SYNC_START,
+ SML_PE_SYNC_END,
+ SML_PE_REPLACE,
+ SML_PE_HEADER,
+ SML_PE_PUT_GET,
+ SML_PE_CMD_GROUP,
+ SML_PE_GENERIC,
+ SML_PE_FINAL,
+ /* %%% Added 2005-08-17 by synthesis/luz for DS 1.2 */
+ SML_PE_MOVE
+} SmlProtoElement_t;
+
+
+#endif
diff --git a/src/syncml_tk/src/sml/inc/smldevinfdtd.h b/src/syncml_tk/src/sml/inc/smldevinfdtd.h
new file mode 100755
index 0000000..34709be
--- /dev/null
+++ b/src/syncml_tk/src/sml/inc/smldevinfdtd.h
@@ -0,0 +1,194 @@
+/**
+ * @file
+ * SyncML Device Information DTD specific type definitions
+ *
+ * @target_system all
+ * @target_os all
+ * @description Definition of structures representing DevInf DTD elements
+ * This file reflects DevInf as specified in the document
+ * http://www.openmobilealliance.org/tech/affiliates/syncml/syncml_devinf_v101_20010615.pdf
+ */
+
+
+/*
+ * Copyright Notice
+ * Copyright (c) Ericsson, IBM, Lotus, Matsushita Communication
+ * Industrial Co., Ltd., Motorola, Nokia, Openwave Systems, Inc.,
+ * Palm, Inc., Psion, Starfish Software, Symbian, Ltd. (2001).
+ * All Rights Reserved.
+ * Implementation of all or part of any Specification may require
+ * licenses under third party intellectual property rights,
+ * including without limitation, patent rights (such a third party
+ * may or may not be a Supporter). The Sponsors of the Specification
+ * are not responsible and shall not be held responsible in any
+ * manner for identifying or failing to identify any or all such
+ * third party intellectual property rights.
+ *
+ * THIS DOCUMENT AND THE INFORMATION CONTAINED HEREIN ARE PROVIDED
+ * ON AN "AS IS" BASIS WITHOUT WARRANTY OF ANY KIND AND ERICSSON, IBM,
+ * LOTUS, MATSUSHITA COMMUNICATION INDUSTRIAL CO. LTD, MOTOROLA,
+ * NOKIA, PALM INC., PSION, STARFISH SOFTWARE AND ALL OTHER SYNCML
+ * SPONSORS DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+ * BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
+ * HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
+ * SHALL ERICSSON, IBM, LOTUS, MATSUSHITA COMMUNICATION INDUSTRIAL CO.,
+ * LTD, MOTOROLA, NOKIA, PALM INC., PSION, STARFISH SOFTWARE OR ANY
+ * OTHER SYNCML SPONSOR BE LIABLE TO ANY PARTY FOR ANY LOSS OF
+ * PROFITS, LOSS OF BUSINESS, LOSS OF USE OF DATA, INTERRUPTION OF
+ * BUSINESS, OR FOR DIRECT, INDIRECT, SPECIAL OR EXEMPLARY, INCIDENTAL,
+ * PUNITIVE OR CONSEQUENTIAL DAMAGES OF ANY KIND IN CONNECTION WITH
+ * THIS DOCUMENT OR THE INFORMATION CONTAINED HEREIN, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH LOSS OR DAMAGE.
+ *
+ * The above notice and this paragraph must be included on all copies
+ * of this document that are made.
+ *
+ */
+
+
+
+#ifndef _SML_DEVINFDTD_H
+#define _SML_DEVINFDTD_H
+
+/* process only if we really use DevInf DTD */
+#ifdef __USE_DEVINF__
+
+/*************************************************************************/
+/* Definitions */
+/*************************************************************************/
+
+
+#include <smldef.h>
+#include <smldtd.h>
+
+
+typedef struct sml_devinf_ext_s {
+ SmlPcdataPtr_t xnam;
+ SmlPcdataListPtr_t xval; /* optional */
+} *SmlDevInfExtPtr_t, SmlDevInfExt_t;
+
+typedef struct sml_devinf_extlist_s {
+ SmlDevInfExtPtr_t data;
+ struct sml_devinf_extlist_s *next;
+} *SmlDevInfExtListPtr_t, SmlDevInfExtList_t;
+
+typedef struct sml_devinf_synccap_s {
+ SmlPcdataListPtr_t synctype;
+} *SmlDevInfSyncCapPtr_t, SmlDevInfSyncCap_t;
+
+
+typedef struct sml_devinf_ctdata_s {
+ SmlPcdataPtr_t name;
+ SmlPcdataPtr_t dname; /* optional, display name */
+
+ SmlPcdataListPtr_t valenum;
+ SmlPcdataPtr_t datatype;
+ SmlPcdataPtr_t maxsize; // called size before DS 1.2 (still represents devInf <Size> for DS 1.1/1.0)
+ /* SyncML DS 1.2, Synthesis/luz 2005-08-24 */
+ SmlPcdataPtr_t maxoccur;
+ Flag_t flags; //
+
+} *SmlDevInfCTDataPtr_t, SmlDevInfCTData_t;
+
+typedef struct sml_devinf_ctdatalist_s {
+ SmlDevInfCTDataPtr_t data;
+ struct sml_devinf_ctdatalist_s *next;
+} *SmlDevInfCTDataListPtr_t, SmlDevInfCTDataList_t;
+
+typedef struct sml_devinf_ctdataprop_s {
+ SmlDevInfCTDataPtr_t prop;
+ SmlDevInfCTDataListPtr_t param;
+} *SmlDevInfCTDataPropPtr_t, SmlDevInfCTDataProp_t;
+
+typedef struct sml_devinf_ctdataproplist_s {
+ SmlDevInfCTDataPropPtr_t data;
+ struct sml_devinf_ctdataproplist_s *next;
+} *SmlDevInfCTDataPropListPtr_t, SmlDevInfCTDataPropList_t;
+
+/* SyncML DS 1.2, Synthesis/luz 2005-08-24 */
+typedef struct sml_devinf_filtercap_s {
+ SmlPcdataPtr_t cttype;
+ SmlPcdataPtr_t verct;
+ SmlPcdataListPtr_t filterkeyword;
+ SmlPcdataListPtr_t propname;
+} *SmlDevInfFilterCapPtr_t, SmlDevInfFilterCap_t;
+
+/* SyncML DS 1.2, Synthesis/luz 2005-08-24 */
+typedef struct sml_devinf_filtercaplist_s {
+ SmlDevInfFilterCapPtr_t data;
+ struct sml_devinf_filtercaplist_s *next;
+} *SmlDevInfFilterCapListPtr_t, SmlDevInfFilterCapList_t;
+
+typedef struct sml_devinf_ctcap_s {
+ SmlPcdataPtr_t cttype;
+ SmlPcdataPtr_t verct; // new for DS 1.2
+ SmlDevInfCTDataPropListPtr_t prop;
+ Flag_t flags; // SmlDevInfFieldLevel_f, opt.
+} *SmlDevInfCTCapPtr_t, SmlDevInfCTCap_t;
+
+typedef struct sml_devinf_ctcaplist_s {
+ SmlDevInfCTCapPtr_t data;
+ struct sml_devinf_ctcaplist_s *next;
+} *SmlDevInfCtcapListPtr_t, SmlDevInfCtcapList_t;
+
+
+typedef struct sml_devinf_dsmem_s {
+ SmlPcdataPtr_t maxmem; /* optional */
+ SmlPcdataPtr_t maxid; /* optional */
+ Flag_t flags; /* %%% luz:2003-04-28, made sharedMem a flag, was PCData (completely wrong) */
+} *SmlDevInfDSMemPtr_t, SmlDevInfDSMem_t;
+
+
+typedef struct sml_devinf_xmit_s {
+ SmlPcdataPtr_t cttype;
+ SmlPcdataPtr_t verct;
+} *SmlDevInfXmitPtr_t, SmlDevInfXmit_t;
+
+typedef struct sml_devinf_xmitlist_s {
+ SmlDevInfXmitPtr_t data;
+ struct sml_devinf_xmitlist_s *next;
+} *SmlDevInfXmitListPtr_t, SmlDevInfXmitList_t;
+
+typedef struct sml_devinf_datastore_s {
+ SmlPcdataPtr_t sourceref;
+ SmlPcdataPtr_t displayname; /* optional */
+ SmlPcdataPtr_t maxguidsize; /* optional */
+ SmlDevInfXmitPtr_t rxpref;
+ SmlDevInfXmitListPtr_t rx; /* optional */
+ SmlDevInfXmitPtr_t txpref;
+ SmlDevInfXmitListPtr_t tx; /* optional */
+ SmlDevInfDSMemPtr_t dsmem; /* optional */
+ SmlDevInfSyncCapPtr_t synccap;
+ /* SyncML DS 1.2, Synthesis/luz 2005-08-24 */
+ SmlDevInfXmitListPtr_t filterrx; /* optional */
+ SmlDevInfCtcapListPtr_t ctcap; // opt., Datastore-local >=DS1.2 type list
+ SmlDevInfFilterCapListPtr_t filtercap; // opt., filter capabilities list
+ Flag_t flags; // SmlDevInfHierarchical_f, opt.
+} *SmlDevInfDatastorePtr_t, SmlDevInfDatastore_t;
+
+
+typedef struct sml_devinf_datastorelist_s {
+ SmlDevInfDatastorePtr_t data;
+ struct sml_devinf_datastorelist_s *next;
+} *SmlDevInfDatastoreListPtr_t, SmlDevInfDatastoreList_t;
+
+typedef struct sml_devinf_devinf_s {
+ SmlPcdataPtr_t verdtd;
+ SmlPcdataPtr_t man; /* optional */
+ SmlPcdataPtr_t mod; /* optional */
+ SmlPcdataPtr_t oem; /* optional */
+ SmlPcdataPtr_t fwv; /* optional */
+ SmlPcdataPtr_t swv; /* optional */
+ SmlPcdataPtr_t hwv; /* optional */
+ SmlPcdataPtr_t devid;
+ SmlPcdataPtr_t devtyp;
+ SmlDevInfDatastoreListPtr_t datastore;
+ SmlDevInfCtcapListPtr_t ctcap; // opt., global pre-DS 1.2 type list
+ SmlDevInfExtListPtr_t ext;
+ /* SCTSTK - 18/03/2002, S.H. 2002-04-05 : SyncML 1.1 */
+ Flag_t flags;
+} *SmlDevInfDevInfPtr_t, SmlDevInfDevInf_t;
+
+#endif // __USE_DEVINF__
+#endif //_SML_DEVINFDTD_H_
diff --git a/src/syncml_tk/src/sml/inc/smldtd.h b/src/syncml_tk/src/sml/inc/smldtd.h
new file mode 100755
index 0000000..a751ddc
--- /dev/null
+++ b/src/syncml_tk/src/sml/inc/smldtd.h
@@ -0,0 +1,455 @@
+/**
+ * @file
+ * SyncML DTD specific type definitions
+ *
+ * @target_system all
+ * @target_os all
+ * @description Definition of structures representing DTD elements
+ */
+
+
+/********************************************************************/
+/* @note */
+/* These definitions are based on the DTD dated from July, 7th, 00 */
+/********************************************************************/
+
+/*
+ * Copyright Notice
+ * Copyright (c) Ericsson, IBM, Lotus, Matsushita Communication
+ * Industrial Co., Ltd., Motorola, Nokia, Openwave Systems, Inc.,
+ * Palm, Inc., Psion, Starfish Software, Symbian, Ltd. (2001).
+ * All Rights Reserved.
+ * Implementation of all or part of any Specification may require
+ * licenses under third party intellectual property rights,
+ * including without limitation, patent rights (such a third party
+ * may or may not be a Supporter). The Sponsors of the Specification
+ * are not responsible and shall not be held responsible in any
+ * manner for identifying or failing to identify any or all such
+ * third party intellectual property rights.
+ *
+ * THIS DOCUMENT AND THE INFORMATION CONTAINED HEREIN ARE PROVIDED
+ * ON AN "AS IS" BASIS WITHOUT WARRANTY OF ANY KIND AND ERICSSON, IBM,
+ * LOTUS, MATSUSHITA COMMUNICATION INDUSTRIAL CO. LTD, MOTOROLA,
+ * NOKIA, PALM INC., PSION, STARFISH SOFTWARE AND ALL OTHER SYNCML
+ * SPONSORS DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+ * BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
+ * HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
+ * SHALL ERICSSON, IBM, LOTUS, MATSUSHITA COMMUNICATION INDUSTRIAL CO.,
+ * LTD, MOTOROLA, NOKIA, PALM INC., PSION, STARFISH SOFTWARE OR ANY
+ * OTHER SYNCML SPONSOR BE LIABLE TO ANY PARTY FOR ANY LOSS OF
+ * PROFITS, LOSS OF BUSINESS, LOSS OF USE OF DATA, INTERRUPTION OF
+ * BUSINESS, OR FOR DIRECT, INDIRECT, SPECIAL OR EXEMPLARY, INCIDENTAL,
+ * PUNITIVE OR CONSEQUENTIAL DAMAGES OF ANY KIND IN CONNECTION WITH
+ * THIS DOCUMENT OR THE INFORMATION CONTAINED HEREIN, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH LOSS OR DAMAGE.
+ *
+ * The above notice and this paragraph must be included on all copies
+ * of this document that are made.
+ *
+ */
+
+
+#ifndef _SML_DTD_H
+ #define _SML_DTD_H
+
+
+/*************************************************************************/
+/* Definitions */
+/*************************************************************************/
+
+
+#include <smldef.h>
+
+
+
+
+/**
+ * ===========================
+ * Common used SyncML Elements
+ * ===========================
+ */
+
+
+
+/**
+ * PCDATA - types of synchronization data which SyncML supports
+ */
+typedef enum {
+ SML_PCDATA_UNDEFINED = 0,
+ SML_PCDATA_STRING, /**< String type */
+ SML_PCDATA_OPAQUE, /**< Opaque type */
+ SML_PCDATA_EXTENSION, /**< Extention type - specified by PcdataExtension_t */
+ SML_PCDATA_CDATA /**< XML CDATA type */
+} SmlPcdataType_t;
+
+
+/**
+ * PCDATA - types of extensions for PCData elements
+ */
+typedef enum {
+ SML_EXT_UNDEFINED = 0,
+ SML_EXT_METINF, /**< Meta Information */
+ SML_EXT_DEVINF, /**< Device Information */
+ SML_EXT_LAST /**< last codepage, needed for loops! */
+} SmlPcdataExtension_t;
+
+
+
+/**
+ * PCDATA - into this structure SyncML wraps the synchronization data itself
+ */
+typedef struct sml_pcdata_s {
+ SmlPcdataType_t contentType; /**< The type of data which a PCDATA structure contains */
+ SmlPcdataExtension_t extension; /**< PCData Extension type */
+ MemSize_t length; /**< length of the data in this PCDATA structure */
+ VoidPtr_t content; /**< Pointer to the data itself */
+} *SmlPcdataPtr_t, SmlPcdata_t;
+
+/** generic list of PCData elements */
+typedef struct sml_pcdata_list_s {
+ SmlPcdataPtr_t data;
+ struct sml_pcdata_list_s *next;
+} *SmlPcdataListPtr_t, SmlPcdataList_t;
+
+/*
+ * Various flags which are actually declared and (EMPTY) elements in
+ * SyncML. This assumes at least a 16-bit architecture for the
+ * underlying OS. We need to review this if that is deemed a problem.
+ */
+#define SmlArchive_f 0x8000 /**< Delete flags */
+#define SmlSftDel_f 0x4000 /**< Delete flags */
+#define SmlMoreData_f 0x0400 /**< MoreData flag */
+#define SmlNoResults_f 0x0200 /**< No Results flag */
+#define SmlNoResp_f 0x0100 /**< No Response flag */
+#define SmlFinal_f 0x0001 /**< Header flag */
+#ifdef __USE_METINF__
+#define SmlMetInfSharedMem_f 0x0002 /**< MetInf Shared Memory Flag */
+/* SyncML DS 1.2, Synthesis/luz 2005-08-24 */
+#define SmlMetInfFieldLevel_f 0x0004 /**< MetInf FieldLevel Flag */
+#endif
+#ifdef __USE_DEVINF__
+#define SmlDevInfSharedMem_f 0x0004 /**< DevInf Shared Memory Flag */
+/* SCTSTK - 18/03/2002, S.H. 2002-04-05 : SyncML 1.1 */
+#define SmlDevInfUTC_f 0x0001 /**< DevInf utc Flag */
+#define SmlDevInfNOfM_f 0x0002 /**< DevInf support n of m Flag */
+#define SmlDevInfLargeObject_f 0x0008 /**< DevInf support large object Flag */
+/* SyncML DS 1.2, Synthesis/luz 2005-08-24 */
+#define SmlDevInfFieldLevel_f 0x0010 /**< DevInf support field level replaces */
+#define SmlDevInfNoTruncate_f 0x0020 /**< DevInf support noTruncate Flag */
+#define SmlDevInfHierarchical_f 0x0040 /**< DevInf support SupportHierarchicalSync Flag */
+
+#endif
+
+
+/**
+ * Chal
+ */
+typedef struct sml_chal_s {
+ SmlPcdataPtr_t meta;
+} *SmlChalPtr_t, SmlChal_t;
+
+/**
+ * Credentials
+ */
+typedef struct sml_cred_s {
+ SmlPcdataPtr_t meta; // opt.
+ SmlPcdataPtr_t data;
+} *SmlCredPtr_t, SmlCred_t;
+
+
+/* forward declaration as item is used in filter which is used
+ in target which is used in item itself */
+//typedef struct sml_item_s;
+
+/**
+ * Record or Field level filter
+ * %%% Added 2005-08-17 by synthesis/luz for DS 1.2
+ */
+typedef struct sml_record_or_field_filter_s {
+ struct sml_item_s *item;
+} *SmlRecordOrFieldFilterPtr_t, SmlRecordOrFieldFilter_t;
+
+/**
+ * Filter
+ * %%% Added 2005-08-17 by synthesis/luz for DS 1.2
+ */
+typedef struct sml_filter_s {
+ SmlPcdataPtr_t meta;
+ SmlRecordOrFieldFilterPtr_t field; // opt.
+ SmlRecordOrFieldFilterPtr_t record; // opt.
+ SmlPcdataPtr_t filtertype; // opt.
+} *SmlFilterPtr_t, SmlFilter_t;
+
+
+
+/**
+ * Source or target parent location
+ * %%% Added 2005-08-17 by synthesis/luz for DS 1.2
+ */
+typedef struct sml_source_or_target_parent_s {
+ SmlPcdataPtr_t locURI;
+} *SmlSourceParentPtr_t, SmlSourceParent_t,
+ *SmlTargetParentPtr_t, SmlTargetParent_t;
+
+
+/**
+ * Source or target location
+ */
+typedef struct sml_source_or_target_s {
+ SmlPcdataPtr_t locURI;
+ SmlPcdataPtr_t locName; // opt.
+ SmlFilterPtr_t filter; // opt., for target only
+} *SmlSourcePtr_t, SmlSource_t,
+ *SmlTargetPtr_t, SmlTarget_t;
+
+typedef struct sml_source_list_s {
+ SmlSourcePtr_t source;
+ struct sml_source_list_s *next;
+} *SmlSourceListPtr_t, SmlSourceList_t;
+
+
+
+/*
+ * ==============================
+ * SyncML Message Header Elements
+ * ==============================
+ */
+
+
+/**
+ * SyncML header
+ * As the header is needed for each SyncML message, it's also the parameter
+ * of the startMessage call.
+ */
+typedef struct sml_sync_hdr_s {
+ SmlProtoElement_t elementType; // Internal Toolkit Field
+ SmlPcdataPtr_t version;
+ SmlPcdataPtr_t proto;
+ SmlPcdataPtr_t sessionID;
+ SmlPcdataPtr_t msgID;
+ Flag_t flags; // NoResp
+ SmlTargetPtr_t target;
+ SmlSourcePtr_t source;
+ SmlPcdataPtr_t respURI; // opt.
+ SmlCredPtr_t cred; // opt.
+ SmlPcdataPtr_t meta; // opt.
+} *SmlSyncHdrPtr_t, SmlSyncHdr_t;
+
+// SyncML Body and SyncML container is not needed, as there are function calls
+// (smlStartMessage(), smlEndMessage()) that let the framework know when to start and end
+// the SyncML document
+
+
+
+/*
+ * =========================
+ * Data description elements
+ * =========================
+ */
+
+
+/**
+ * Data in SyncML is encapsulated in an "item" element.
+ */
+typedef struct sml_item_s {
+ SmlTargetPtr_t target; // opt.
+ SmlSourcePtr_t source; // opt.
+ SmlTargetParentPtr_t targetParent; // opt. (Added by luz 2005-08-17 for DS 1.2)
+ SmlSourceParentPtr_t sourceParent; // opt. (Added by luz 2005-08-17 for DS 1.2)
+
+ SmlPcdataPtr_t meta; // opt.
+ SmlPcdataPtr_t data; // opt.
+ Flag_t flags; // opt. for MoreData
+} *SmlItemPtr_t, SmlItem_t;
+
+typedef struct sml_item_list_s {
+ SmlItemPtr_t item;
+ struct sml_item_list_s *next;
+} *SmlItemListPtr_t, SmlItemList_t;
+
+
+/*
+ * ==============================================
+ * SyncML Commands (Protocol Management Elements)
+ * ==============================================
+ */
+
+/**
+ * Generic commands:
+ * Add, Copy, Replace, Delete, Move (added for DS 1.2, 2005-08-17 by luz)
+ */
+typedef struct sml_generic_s {
+ SmlProtoElement_t elementType; // Internal Toolkit Field
+ SmlPcdataPtr_t cmdID;
+ Flag_t flags; // NoResp, Archive (Delete), SftDel (Delete)
+ SmlCredPtr_t cred; // opt.
+ SmlPcdataPtr_t meta; // opt.
+ SmlItemListPtr_t itemList;
+} *SmlAddPtr_t, SmlAdd_t,
+ *SmlCopyPtr_t, SmlCopy_t,
+ *SmlMovePtr_t, SmlMove_t,
+ *SmlReplacePtr_t, SmlReplace_t,
+ *SmlDeletePtr_t, SmlDelete_t,
+ *SmlGenericCmdPtr_t, SmlGenericCmd_t;
+
+/**
+ * Alert command:
+ */
+typedef struct sml_alert_s {
+ SmlProtoElement_t elementType; // Internal Toolkit Field
+ SmlPcdataPtr_t cmdID;
+ Flag_t flags; // NoResp
+ SmlCredPtr_t cred; // opt.
+ SmlPcdataPtr_t data; // opt.
+ SmlItemListPtr_t itemList;
+} *SmlAlertPtr_t, SmlAlert_t;
+
+
+/**
+ * Atomic/Sequence command:
+ */
+typedef struct sml_atomic_s {
+ SmlProtoElement_t elementType; // Internal Toolkit Field
+ SmlPcdataPtr_t cmdID;
+ Flag_t flags; // NoResp
+ SmlPcdataPtr_t meta; // opt.
+} *SmlAtomicPtr_t, SmlAtomic_t,
+ *SmlSequencePtr_t, SmlSequence_t;
+
+
+/**
+ * Sync command:
+ */
+typedef struct sml_sync_s {
+ SmlProtoElement_t elementType; // Internal Toolkit Field
+ SmlPcdataPtr_t cmdID;
+ Flag_t flags; // NoResp
+ SmlCredPtr_t cred; // opt.
+ SmlTargetPtr_t target; // opt.
+ SmlSourcePtr_t source; // opt.
+ SmlPcdataPtr_t meta; // opt.
+ SmlPcdataPtr_t noc; // opt. (SyncML 1.1)
+} *SmlSyncPtr_t, SmlSync_t;
+
+
+/**
+ * Exec command:
+ */
+typedef struct sml_exec_s {
+ SmlProtoElement_t elementType;
+ SmlPcdataPtr_t cmdID;
+ Flag_t flags; // NoResp
+ SmlCredPtr_t cred; // opt.
+ SmlPcdataPtr_t meta; // opt.
+ SmlItemPtr_t item;
+} *SmlExecPtr_t, SmlExec_t;
+
+
+/**
+ * Get and Put command:
+ */
+typedef struct sml_get_put_s {
+ SmlProtoElement_t elementType; // Internal Toolkit Field
+ SmlPcdataPtr_t cmdID;
+ Flag_t flags; // NoResp
+ SmlPcdataPtr_t lang; // opt.
+ SmlCredPtr_t cred; // opt.
+ SmlPcdataPtr_t meta; // opt.
+ SmlItemListPtr_t itemList;
+} *SmlPutPtr_t, SmlPut_t,
+ *SmlGetPtr_t, SmlGet_t;
+
+
+/**
+ * Map command:
+ */
+typedef struct sml_map_item_s {
+ SmlTargetPtr_t target;
+ SmlSourcePtr_t source;
+} *SmlMapItemPtr_t, SmlMapItem_t;
+
+typedef struct sml_map_item_list_s {
+ SmlMapItemPtr_t mapItem;
+ struct sml_map_item_list_s *next;
+} *SmlMapItemListPtr_t, SmlMapItemList_t;
+
+typedef struct sml_map_s {
+ SmlProtoElement_t elementType; // InternalToolkit Field
+ SmlPcdataPtr_t cmdID;
+ SmlTargetPtr_t target;
+ SmlSourcePtr_t source;
+ SmlCredPtr_t cred; // opt.
+ SmlPcdataPtr_t meta; // opt.
+ SmlMapItemListPtr_t mapItemList;
+} *SmlMapPtr_t, SmlMap_t;
+
+
+/**
+ * Results command:
+ */
+typedef struct sml_results_s {
+ SmlProtoElement_t elementType; // Internal Toolkit Field
+ SmlPcdataPtr_t cmdID;
+ SmlPcdataPtr_t msgRef; // opt.
+ SmlPcdataPtr_t cmdRef;
+ SmlPcdataPtr_t meta; // opt.
+ SmlPcdataPtr_t targetRef; // opt.
+ SmlPcdataPtr_t sourceRef; // opt.
+ SmlItemListPtr_t itemList;
+} *SmlResultsPtr_t, SmlResults_t;
+
+
+/**
+ * Search command:
+ */
+typedef struct sml_search_s {
+ SmlProtoElement_t elementType; // Internal Toolkit Field
+ SmlPcdataPtr_t cmdID;
+ Flag_t flags; // NoResp, NoResults
+ SmlCredPtr_t cred; // opt.
+ SmlTargetPtr_t target; // opt.
+ SmlSourceListPtr_t sourceList;
+ SmlPcdataPtr_t lang; // opt.
+ SmlPcdataPtr_t meta;
+ SmlPcdataPtr_t data;
+} *SmlSearchPtr_t, SmlSearch_t;
+
+
+/**
+ * Status command:
+ */
+typedef struct sml_target_ref_list_s {
+ SmlPcdataPtr_t targetRef;
+ struct sml_target_ref_list_s *next;
+} *SmlTargetRefListPtr_t, SmlTargetRefList_t;
+
+typedef struct sml_source_ref_list_s {
+ SmlPcdataPtr_t sourceRef;
+ struct sml_source_ref_list_s *next;
+} *SmlSourceRefListPtr_t, SmlSourceRefList_t;
+
+typedef struct sml_status_s {
+ SmlProtoElement_t elementType; // Internal Toolkit Field
+ SmlPcdataPtr_t cmdID;
+ SmlPcdataPtr_t msgRef; // Opt.
+ SmlPcdataPtr_t cmdRef;
+ SmlPcdataPtr_t cmd;
+ SmlTargetRefListPtr_t targetRefList; // opt.
+ SmlSourceRefListPtr_t sourceRefList; // opt.
+ SmlCredPtr_t cred; // opt.
+ SmlChalPtr_t chal; // opt.
+ SmlPcdataPtr_t data;
+ SmlItemListPtr_t itemList; // opt.
+} *SmlStatusPtr_t, SmlStatus_t;
+
+
+/**
+ * a little helper for typecasting
+ */
+typedef struct sml_unknown_proto_element_s {
+ SmlProtoElement_t elementType; // Internal Toolkit Field
+} *SmlUnknownProtoElementPtr_t, SmlUnknownProtoElement_t;
+
+
+#endif
diff --git a/src/syncml_tk/src/sml/inc/smlerr.h b/src/syncml_tk/src/sml/inc/smlerr.h
new file mode 100755
index 0000000..2072b07
--- /dev/null
+++ b/src/syncml_tk/src/sml/inc/smlerr.h
@@ -0,0 +1,132 @@
+/**
+ * @file
+ * ErrorLibrary
+ *
+ * @target_system all
+ * @target_os all
+ * @description Definition of the used Error Codes
+ */
+
+
+/*
+ * Copyright Notice
+ * Copyright (c) Ericsson, IBM, Lotus, Matsushita Communication
+ * Industrial Co., Ltd., Motorola, Nokia, Openwave Systems, Inc.,
+ * Palm, Inc., Psion, Starfish Software, Symbian, Ltd. (2001).
+ * All Rights Reserved.
+ * Implementation of all or part of any Specification may require
+ * licenses under third party intellectual property rights,
+ * including without limitation, patent rights (such a third party
+ * may or may not be a Supporter). The Sponsors of the Specification
+ * are not responsible and shall not be held responsible in any
+ * manner for identifying or failing to identify any or all such
+ * third party intellectual property rights.
+ *
+ * THIS DOCUMENT AND THE INFORMATION CONTAINED HEREIN ARE PROVIDED
+ * ON AN "AS IS" BASIS WITHOUT WARRANTY OF ANY KIND AND ERICSSON, IBM,
+ * LOTUS, MATSUSHITA COMMUNICATION INDUSTRIAL CO. LTD, MOTOROLA,
+ * NOKIA, PALM INC., PSION, STARFISH SOFTWARE AND ALL OTHER SYNCML
+ * SPONSORS DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+ * BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
+ * HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
+ * SHALL ERICSSON, IBM, LOTUS, MATSUSHITA COMMUNICATION INDUSTRIAL CO.,
+ * LTD, MOTOROLA, NOKIA, PALM INC., PSION, STARFISH SOFTWARE OR ANY
+ * OTHER SYNCML SPONSOR BE LIABLE TO ANY PARTY FOR ANY LOSS OF
+ * PROFITS, LOSS OF BUSINESS, LOSS OF USE OF DATA, INTERRUPTION OF
+ * BUSINESS, OR FOR DIRECT, INDIRECT, SPECIAL OR EXEMPLARY, INCIDENTAL,
+ * PUNITIVE OR CONSEQUENTIAL DAMAGES OF ANY KIND IN CONNECTION WITH
+ * THIS DOCUMENT OR THE INFORMATION CONTAINED HEREIN, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH LOSS OR DAMAGE.
+ *
+ * The above notice and this paragraph must be included on all copies
+ * of this document that are made.
+ *
+ */
+
+
+#ifndef _SML_ERR_H
+ #define _SML_ERR_H
+
+
+
+/*************************************************************************
+ * Definitions
+ *************************************************************************/
+
+
+
+/*
+ * No error, success code
+ */
+#define SML_ERR_OK 0x00 /**< OK */
+
+#define SML_ERR_CONTINUE 0x01 /**< OK, but processing of message not finished yet (smlProcessData(NEXT_COMMAND) case) */
+
+
+/*
+ * SyncML Common Error Codes
+ */
+
+// general errors
+#define SML_ERR_UNSPECIFIC 0x10 /**< unspecific error */
+#define SML_ERR_NOT_ENOUGH_SPACE 0x11 /**< not enough memory to perform this operation */
+#define SML_ERR_WRONG_USAGE 0x13 /**< function was called in wrong context */
+
+// wrong parameters
+#define SML_ERR_WRONG_PARAM 0x20 /**< wrong parameter */
+#define SML_ERR_INVALID_SIZE 0x21 /**< param has an invalid size */
+#define SML_ERR_INVALID_HANDLE 0x22 /**< if handle is invalid/unknown */
+#define SML_ERR_INVALID_OPTIONS 0x23 /**< unkown or unallowed options */
+
+
+/*
+ * SyncML Mgr Error Codes
+ */
+#define SML_ERR_A_MGR_ERROR 0x1001 /**< a template */
+#define SML_ERR_MGR_INVALID_INSTANCE_INFO 0x1002 /**< a invalid Instance Info structure is used */
+#define SML_ERR_COMMAND_NOT_HANDLED 0x1003 /**< no callback function is available to handle this command */
+#define SML_ERR_ALREADY_INITIALIZED 0x1004 /**< Mgr allready initialized */
+
+
+/*
+ * SyncML Xlt Error Codes
+ */
+#define SML_ERR_XLT_MISSING_CONT 0x2001 /**< required field content missing */
+#define SML_ERR_XLT_BUF_ERR 0x2002 /**< Buffer too small */
+#define SML_ERR_XLT_INVAL_PCDATA_TYPE 0x2003 /**< Invalid (WBXML) Element Type (STR_I etc.) */
+#define SML_ERR_XLT_INVAL_LIST_TYPE 0x2004 /**< Invalid List Type (COL_LIST etc.) */
+#define SML_ERR_XLT_INVAL_TAG_TYPE 0x2005 /**< Invalid Tag Type (TT_BEG etc.) */
+#define SML_ERR_XLT_ENC_UNK 0x2007 /**< Unknown Encoding (WBXML, XML) */
+#define SML_ERR_XLT_INVAL_PROTO_ELEM 0x2008 /**< Invalid Protocol Element (ADD, Delete, ...) */
+#define SML_ERR_MISSING_LIST_ELEM 0x2009 /**< Missing Content of List Elements */
+#define SML_ERR_XLT_INCOMP_WBXML_VERS 0x200A /**< Incompatible WBXML Content Format Version */
+#define SML_ERR_XLT_INVAL_SYNCML_DOC 0x200B /**< Document does not conform to SyncML DTD */
+#define SML_ERR_XLT_INVAL_PCDATA 0x200C /**< Invalid PCData elem (e.g. not encoded as OPAQUE data) */
+#define SML_ERR_XLT_TOKENIZER_ERROR 0x200D /**< Unspecified tokenizer error */
+#define SML_ERR_XLT_INVAL_WBXML_DOC 0x200E /**< Document does not conform to WBXML specification */
+#define SML_ERR_XLT_WBXML_UKN_TOK 0x200F /**< Document contains unknown WBXML token */
+#define SML_ERR_XLT_MISSING_END_TAG 0x2010 /**< Non-empty start tag without matching end tag */
+#define SML_ERR_XLT_INVALID_CODEPAGE 0x2011 /**< WBXML document uses unspecified code page */
+#define SML_ERR_XLT_END_OF_BUFFER 0x2012 /**< End of buffer reached */
+#define SML_ERR_XLT_INVAL_XML_DOC 0x2013 /**< Document does not conform to XML 1.0 specification */
+#define SML_ERR_XLT_XML_UKN_TAG 0x2014 /**< Document contains unknown XML tag */
+#define SML_ERR_XLT_INVAL_PUB_IDENT 0x2015 /**< Invalid Public Identifier */
+#define SML_ERR_XLT_INVAL_EXT 0x2016 /**< Invalid Codepage Extension */
+#define SML_ERR_XLT_NO_MATCHING_CODEPAGE 0x2017 /**< No matching Codepage could be found */
+#define SML_ERR_XLT_INVAL_INPUT_DATA 0x2018 /**< Data missing in input structure */
+
+
+/*
+ * SyncML Wsm Error Codes
+ */
+#define SML_ERR_WSM_BUF_TABLE_FULL 0x3001 /**< no more empty entries in buffer table available */
+
+/*
+ * SyncML Util Error Codes
+ */
+#define SML_ERR_A_UTI_UNKNOWN_PROTO_ELEMENT 0x7001
+
+
+#endif
+
diff --git a/src/syncml_tk/src/sml/inc/smlmetinfdtd.h b/src/syncml_tk/src/sml/inc/smlmetinfdtd.h
new file mode 100755
index 0000000..b19dd28
--- /dev/null
+++ b/src/syncml_tk/src/sml/inc/smlmetinfdtd.h
@@ -0,0 +1,101 @@
+/**
+ * @file
+ * SyncML Meta Information DTD specific type definitions
+ *
+ * @target_system all
+ * @target_os all
+ * @description Definition of structures representing MetInf DTD elements
+ */
+
+
+/**************************************************************************/
+/* @note */
+/* These definitions are based on the MetInf DTD dated from Aug, 29th, 00 */
+/**************************************************************************/
+
+
+
+/*
+ * Copyright Notice
+ * Copyright (c) Ericsson, IBM, Lotus, Matsushita Communication
+ * Industrial Co., Ltd., Motorola, Nokia, Openwave Systems, Inc.,
+ * Palm, Inc., Psion, Starfish Software, Symbian, Ltd. (2001).
+ * All Rights Reserved.
+ * Implementation of all or part of any Specification may require
+ * licenses under third party intellectual property rights,
+ * including without limitation, patent rights (such a third party
+ * may or may not be a Supporter). The Sponsors of the Specification
+ * are not responsible and shall not be held responsible in any
+ * manner for identifying or failing to identify any or all such
+ * third party intellectual property rights.
+ *
+ * THIS DOCUMENT AND THE INFORMATION CONTAINED HEREIN ARE PROVIDED
+ * ON AN "AS IS" BASIS WITHOUT WARRANTY OF ANY KIND AND ERICSSON, IBM,
+ * LOTUS, MATSUSHITA COMMUNICATION INDUSTRIAL CO. LTD, MOTOROLA,
+ * NOKIA, PALM INC., PSION, STARFISH SOFTWARE AND ALL OTHER SYNCML
+ * SPONSORS DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+ * BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
+ * HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
+ * SHALL ERICSSON, IBM, LOTUS, MATSUSHITA COMMUNICATION INDUSTRIAL CO.,
+ * LTD, MOTOROLA, NOKIA, PALM INC., PSION, STARFISH SOFTWARE OR ANY
+ * OTHER SYNCML SPONSOR BE LIABLE TO ANY PARTY FOR ANY LOSS OF
+ * PROFITS, LOSS OF BUSINESS, LOSS OF USE OF DATA, INTERRUPTION OF
+ * BUSINESS, OR FOR DIRECT, INDIRECT, SPECIAL OR EXEMPLARY, INCIDENTAL,
+ * PUNITIVE OR CONSEQUENTIAL DAMAGES OF ANY KIND IN CONNECTION WITH
+ * THIS DOCUMENT OR THE INFORMATION CONTAINED HEREIN, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH LOSS OR DAMAGE.
+ *
+ * The above notice and this paragraph must be included on all copies
+ * of this document that are made.
+ *
+ */
+
+#ifndef _SML_METINFDTD_H
+#define _SML_METINFDTD_H
+
+/* process only if we really use MetInf DTD */
+#ifdef __USE_METINF__
+
+/*************************************************************************/
+/* Definitions */
+/*************************************************************************/
+
+
+#include <smldef.h>
+#include <smldtd.h>
+
+
+typedef struct sml_metinf_anchor_s {
+ SmlPcdataPtr_t last; /* optional */
+ SmlPcdataPtr_t next;
+} *SmlMetInfAnchorPtr_t, SmlMetInfAnchor_t;
+
+typedef struct sml_metinf_mem_s {
+ /* %%% luz 2005-08-24 :this definition was plain wrong - shared is a flag, not Pcdata!
+ SmlPcdataPtr_t shared; */
+ SmlPcdataPtr_t free;
+ SmlPcdataPtr_t freeid;
+ /* Added by Synthesis/luz 2005-08-24, was wrong in toolkit up to now! */
+ Flag_t flags; // SharedMem flag (SmlMetInfSharedMem_f)
+} *SmlMetInfMemPtr_t, SmlMetInfMem_t;
+
+typedef struct sml_metinf_metinf_s {
+ SmlPcdataPtr_t format; /* opt. */
+ SmlPcdataPtr_t type; /* opt. */
+ SmlPcdataPtr_t mark; /* opt. */
+ SmlPcdataPtr_t size; /* opt. */
+ SmlPcdataPtr_t nextnonce; /* opt. */
+ SmlPcdataPtr_t version;
+ SmlPcdataPtr_t maxmsgsize; /* optional */
+ /* SCTSTK - 18/03/2002, S.H. 2002-04-05 : SyncML 1.1 */
+ SmlPcdataPtr_t maxobjsize; /* optional */
+ SmlMetInfMemPtr_t mem; /* optional */
+ SmlPcdataListPtr_t emi; /* optional */
+ SmlMetInfAnchorPtr_t anchor; /* opt. */
+ /* SyncML DS 1.2, Synthesis/luz 2005-08-24 */
+ Flag_t flags; // FieldLevel flag (SmlMetInfFieldLevel_f)
+} *SmlMetInfMetInfPtr_t, SmlMetInfMetInf_t;
+
+#endif /* __USE_METINF__ */
+#endif /* _SML_METINFDTD_H */
diff --git a/src/syncml_tk/src/sml/inc/win/define.h b/src/syncml_tk/src/sml/inc/win/define.h
new file mode 100755
index 0000000..5c43e9d
--- /dev/null
+++ b/src/syncml_tk/src/sml/inc/win/define.h
@@ -0,0 +1,171 @@
+/**
+ * @file
+ * Compiler Flag Definition File
+ *
+ * @target_system win
+ * @target_os win
+ */
+
+/*
+ * Copyright Notice
+ * Copyright (c) Ericsson, IBM, Lotus, Matsushita Communication
+ * Industrial Co., Ltd., Motorola, Nokia, Openwave Systems, Inc.,
+ * Palm, Inc., Psion, Starfish Software, Symbian, Ltd. (2001).
+ * All Rights Reserved.
+ * Implementation of all or part of any Specification may require
+ * licenses under third party intellectual property rights,
+ * including without limitation, patent rights (such a third party
+ * may or may not be a Supporter). The Sponsors of the Specification
+ * are not responsible and shall not be held responsible in any
+ * manner for identifying or failing to identify any or all such
+ * third party intellectual property rights.
+ *
+ * THIS DOCUMENT AND THE INFORMATION CONTAINED HEREIN ARE PROVIDED
+ * ON AN "AS IS" BASIS WITHOUT WARRANTY OF ANY KIND AND ERICSSON, IBM,
+ * LOTUS, MATSUSHITA COMMUNICATION INDUSTRIAL CO. LTD, MOTOROLA,
+ * NOKIA, PALM INC., PSION, STARFISH SOFTWARE AND ALL OTHER SYNCML
+ * SPONSORS DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+ * BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
+ * HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
+ * SHALL ERICSSON, IBM, LOTUS, MATSUSHITA COMMUNICATION INDUSTRIAL CO.,
+ * LTD, MOTOROLA, NOKIA, PALM INC., PSION, STARFISH SOFTWARE OR ANY
+ * OTHER SYNCML SPONSOR BE LIABLE TO ANY PARTY FOR ANY LOSS OF
+ * PROFITS, LOSS OF BUSINESS, LOSS OF USE OF DATA, INTERRUPTION OF
+ * BUSINESS, OR FOR DIRECT, INDIRECT, SPECIAL OR EXEMPLARY, INCIDENTAL,
+ * PUNITIVE OR CONSEQUENTIAL DAMAGES OF ANY KIND IN CONNECTION WITH
+ * THIS DOCUMENT OR THE INFORMATION CONTAINED HEREIN, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH LOSS OR DAMAGE.
+ *
+ * The above notice and this paragraph must be included on all copies
+ * of this document that are made.
+ *
+ */
+
+/**
+ * File for Windows Specific Compiler Flags
+ */
+
+#ifndef _DEFINE_H
+ #define _DEFINE_H
+#define __ANSI_C__
+
+
+/* thread safety (added by luz@synthesis.ch, 2001-10-29) */
+/* Note: moved define of this to target_options.h of every target
+#undef __MAKE_THREADSAFE
+
+/* enable Alloc helpers */
+#define __USE_ALLOCFUNCS__
+
+/* do we need WBXML (binary XML) processing ? */
+#define __SML_WBXML__
+/* do we need the capability to decode plain text tokens in WBXML? */
+#define __SML_WBXML_TEXTTOKENS__
+/* do we need XML processing ? */
+#define __SML_XML__
+/* are we using a 'light' toolkit ? */
+//#define __SML_LITE__
+/* do we use Sub DTD extensions ? */
+#define __USE_EXTENSIONS__
+/* do we need Metainformation DTD parsing ? */
+#define __USE_METINF__
+/* do we use Device Info DTD ? */
+#define __USE_DEVINF__
+
+/* which of the following optional commands should be included ? */
+
+#define ADD_SEND
+#define ATOMIC_SEND
+#define ATOMIC_RECEIVE
+#define COPY_SEND
+#define COPY_RECEIVE
+#define EXEC_SEND
+#define EXEC_RECEIVE
+#define GET_SEND
+#define MAP_RECEIVE
+#define MAPITEM_RECEIVE
+#define RESULT_RECEIVE
+#define SEARCH_SEND
+#define SEARCH_RECEIVE
+#define SEQUENCE_SEND
+#define SEQUENCE_RECEIVE
+
+
+/* TK: to improve interoperability and handling we
+ * switched to using .def files instead of compiler
+ * specific per function definitions. As long as we only
+ * use C this is the easiest and cleanes way
+ */
+
+#define SML_API
+#define SML_API_DEF
+#define XPT_API
+#define XPT_API_DEF
+
+/* Multi segment macro for Palm OS */
+#define LIB_FUNC
+#define MGR_FUNC
+#define WSM_FUNC
+#define XLT_FUNC
+
+/* TK: Old, now obsolete code follows here */
+#ifdef USE_OLD_DEFINES
+/*
+When building the DLL code with GNU, you should define BUILDING_DLL so that
+the variables/functions are exported correctly. When using the DLL,
+do NOT define BUILDING_DLL, and then the variables/functions will be
+imported correctly.
+
+You need to be using egcs-1.1.1 or newer.
+
+Building the DLL:
+ - define BUILDING_DLL, which defines SML_API __attribute__((dllexport))
+Building the client code:
+ - DO NOT define BUILDING_DLL, which defines SML_API to be one __attribute__((dllimport))
+*/
+
+
+#if __GNUC__ || __MSVC__ || __MSVCPP__ || _WIN32_WCE
+ /* define this, if you want to link the toolkit static */
+ #if __LINK_TOOLKIT_STATIC__ || __linux__
+ #define SML_API
+ #define SML_API_DEF
+ #define XPT_API
+ #define XPT_API_DEF
+ #else
+ #if BUILDING_DLL
+ #define SML_API __declspec (dllexport)
+ #define SML_API_DEF __declspec (dllexport)
+ #define XPT_API __declspec (dllexport)
+ #define XPT_API_DEF __declspec (dllexport)
+ #else /* Not BUILDING_DLL */
+ #define SML_API __declspec (dllimport)
+ #define SML_API_DEF __declspec (dllimport)
+ #define XPT_API __declspec (dllimport)
+ #define XPT_API_DEF __declspec (dllimport)
+ #endif /* Not BUILDING_DLL */
+ #endif
+#else
+ #if __IBMC__ || __IBMCPP__
+ #define SML_API_DEF __stdcall
+ #define SML_API __stdcall _Export
+ #define XPT_API_DEF __stdcall
+ #define XPT_API __stdcall _Export
+ #else
+ #if WIN32
+ #define SML_API __declspec (dllexport)
+ #define SML_API_DEF __declspec (dllexport)
+ #define XPT_API #error
+ #define XPT_API_DEF #error
+ #else
+ #define SML_API_DEF __stdcall
+ #define SML_API __stdcall
+ #define XPT_API_DEF __stdcall
+ #define XPT_API __stdcall
+ #endif
+#endif
+#endif
+#endif // USE_OLD_DEFINES
+
+#endif
diff --git a/src/syncml_tk/src/sml/lib/all/liblock.c b/src/syncml_tk/src/sml/lib/all/liblock.c
new file mode 100755
index 0000000..e4ce97c
--- /dev/null
+++ b/src/syncml_tk/src/sml/lib/all/liblock.c
@@ -0,0 +1,75 @@
+/**
+ * @file
+ * Library for Thread Locking Functions
+ *
+ * @target_system ALL
+ * @target_os ALL
+ * @description thread-locking library, RTK addition by luz@synthesis.ch
+ */
+
+#include "syncml_tk_prefix_file.h" // %%% luz: needed for precompiled headers in eVC++
+
+#define _IMPLEMENTS_LOCK_GLOBALS
+#include "liblock.h"
+#undef _IMPLEMENTS_LOCK_GLOBALS
+
+
+#ifdef __DEBUG_LOCKS
+
+static short gLockNest;
+
+
+void _ToolKitLockInit(const char *msg)
+{
+ gLockNest=0;
+ InitializeCriticalSection(&gSmlLock);
+ DEBUGPRINTF(("Toolkitlock initialized from '%s'",msg))
+} // _ToolKitLockInit
+
+
+void _ToolKitLockFree(const char *msg) {
+ DeleteCriticalSection(&gSmlLock);
+ DEBUGPRINTF(("Toolkitlock freed from '%s'",msg));
+} // _ToolKitLockFree
+
+
+void _LockToolKit(const char *msg)
+{
+ short sec=0;
+ int k;
+
+ if (!TryEnterCriticalSection(&gSmlLock)) {
+ // could not enter
+ for (k=0; k<10; k++) {
+ Sleep(100); // wait 0.1 sec
+ if (TryEnterCriticalSection(&gSmlLock)) goto entered;
+ }
+ // could not enter after 1 sec
+ DEBUGPRINTF(("Toolkitlock not free after 1 sec trying..."))
+ do {
+ Sleep(30000); // wait 30 secs
+ sec+=30;
+ if (TryEnterCriticalSection(&gSmlLock)) goto entered;
+ DEBUGPRINTF(("Toolkitlock not free after %d sec trying...",sec))
+ } while (1);
+ }
+entered:
+ #if __DEBUG_LOCKS>1
+ DEBUGPRINTF(("Toolkitlock entered from '%s'",msg))
+ #endif
+ ;
+} // _LockToolKit
+
+
+void _ReleaseToolKit(const char *msg)
+{
+ LeaveCriticalSection(&gSmlLock);
+ #if __DEBUG_LOCKS>1
+ DEBUGPRINTF(("Toolkitlock released from '%s'",msg))
+ #endif
+} // _ReleaseToolKit
+
+
+#endif
+
+/* eof */
diff --git a/src/syncml_tk/src/sml/lib/all/libmem.c b/src/syncml_tk/src/sml/lib/all/libmem.c
new file mode 100755
index 0000000..fdb720a
--- /dev/null
+++ b/src/syncml_tk/src/sml/lib/all/libmem.c
@@ -0,0 +1,187 @@
+/**
+ * @file
+ * Library for Memory Functions
+ *
+ * @target_system ALL
+ * @target_os ALL
+ * @description Header for the implementation of common memory handling
+ * functions
+ */
+
+/*
+ * Copyright Notice
+ * Copyright (c) Ericsson, IBM, Lotus, Matsushita Communication
+ * Industrial Co., Ltd., Motorola, Nokia, Openwave Systems, Inc.,
+ * Palm, Inc., Psion, Starfish Software, Symbian, Ltd. (2001).
+ * All Rights Reserved.
+ * Implementation of all or part of any Specification may require
+ * licenses under third party intellectual property rights,
+ * including without limitation, patent rights (such a third party
+ * may or may not be a Supporter). The Sponsors of the Specification
+ * are not responsible and shall not be held responsible in any
+ * manner for identifying or failing to identify any or all such
+ * third party intellectual property rights.
+ *
+ * THIS DOCUMENT AND THE INFORMATION CONTAINED HEREIN ARE PROVIDED
+ * ON AN "AS IS" BASIS WITHOUT WARRANTY OF ANY KIND AND ERICSSON, IBM,
+ * LOTUS, MATSUSHITA COMMUNICATION INDUSTRIAL CO. LTD, MOTOROLA,
+ * NOKIA, PALM INC., PSION, STARFISH SOFTWARE AND ALL OTHER SYNCML
+ * SPONSORS DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+ * BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
+ * HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
+ * SHALL ERICSSON, IBM, LOTUS, MATSUSHITA COMMUNICATION INDUSTRIAL CO.,
+ * LTD, MOTOROLA, NOKIA, PALM INC., PSION, STARFISH SOFTWARE OR ANY
+ * OTHER SYNCML SPONSOR BE LIABLE TO ANY PARTY FOR ANY LOSS OF
+ * PROFITS, LOSS OF BUSINESS, LOSS OF USE OF DATA, INTERRUPTION OF
+ * BUSINESS, OR FOR DIRECT, INDIRECT, SPECIAL OR EXEMPLARY, INCIDENTAL,
+ * PUNITIVE OR CONSEQUENTIAL DAMAGES OF ANY KIND IN CONNECTION WITH
+ * THIS DOCUMENT OR THE INFORMATION CONTAINED HEREIN, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH LOSS OR DAMAGE.
+ *
+ * The above notice and this paragraph must be included on all copies
+ * of this document that are made.
+ *
+ */
+
+/*************************************************************************
+ * Definitions
+ *************************************************************************/
+
+#include "syncml_tk_prefix_file.h" // %%% luz: needed for precompiled headers in eVC++
+
+#include <smldef.h>
+#include "libmem.h"
+#ifdef __PALM_OS__
+#include "MemoryMgr.h"
+#endif
+
+#ifdef MEMORY_PROFILING
+ // %%% luz 2002-10-02
+ #include "profiling.h"
+#endif
+
+/*************************************************************************
+ * External Functions for all TOOLKIT Versions
+ *************************************************************************/
+
+
+/**
+ * Deallocates the memory of object "pObject", which has been allocated
+ * previously.
+ * If "pObject" is a NULL pointer nothing happens.
+ * If "pObject" is a pointer to memory which has not been allocated
+ * previouly, the behaviour is undefined.
+ * The contents of the deallocated memory object is destroyed.
+ */
+SML_API void smlLibFree(void *pObject)
+{
+ if (! pObject) return;
+ #ifdef __PALM_OS__
+ MemPtrFree(pObject);
+ #else
+ // %%% luz 2002-10-02
+ #ifdef MEMORY_PROFILING
+ sysync_free(pObject);
+ #else
+ free(pObject);
+ #endif
+ #endif
+}
+
+
+/**
+ * Changes size of preallocated space for memory object "pObject"
+ * to the new size specified by "constSize".
+ *
+ * If the new size is larger than the old size, the old contents
+ * is not changed. Additionally space is added at the the end of
+ * "pObject". The new allocated space is not initialized
+ * to any special value.
+ * If the new size is smaller than the old size, the unused space
+ * is discarded.
+ *
+ * If "pObject" is a NULL pointer, this function behaves just like
+ * smlLibMalloc().
+ * If "pObject" does not point to a previously allocated memory area,
+ * the behavior is undefined.
+ * If "constSize" is 0, a NULL pointer is returned and the space
+ * which "pObject" points to is freed up.
+ *
+ * Returns a pointer to the first byte of the resized object.
+ * If no new memory could be allocated, a NULL Pointer is returned
+ * without changing the memory object "pObject" (Nothing happens to the content).
+ *
+ * @param pObject (IN/OUT)
+ * memory object, which size should be changed
+ * @param constSize (IN)
+ * new size the memory object shall use
+ * @return void pointer to memory object, which size has been be changed\n
+ * NULL, if not successfull or if constSize==0
+ */
+SML_API void *smlLibRealloc(void *pObject, MemSize_t constSize)
+{
+#ifdef __PALM_OS__
+ VoidPtr_t _new_object;
+ MemSize_t _old_size;
+
+ // It's a malloc!
+ if (pObject == NULL)
+ return smlLibMalloc(constSize);
+
+ _old_size = MemPtrSize(pObject);
+ if (constSize <= _old_size) {
+ // make it smaller
+ MemPtrResize(pObject, constSize);
+ _new_object = pObject;
+ } else {
+ // maker it larger (we need to allocate new memory somewhere else)
+ _new_object = smlLibMalloc(constSize);
+ if (_new_object != NULL) {
+ smlLibMemmove(_new_object, pObject, _old_size);
+ smlLibFree(pObject);
+ }
+ }
+
+ return _new_object;
+#else
+ // %%% luz 2002-10-02
+ #ifdef MEMORY_PROFILING
+ return sysync_realloc(pObject, constSize);
+ #else
+ return realloc(pObject, constSize);
+ #endif
+#endif
+}
+
+
+#ifndef __PALM_OS__
+/* If not Palm OS we use the Standard ANSI C functions */
+SML_API void *smlLibMemset(void *pObject, int value, MemSize_t count){
+ return memset(pObject, value, count);
+}
+SML_API void *smlLibMemcpy(void *pTarget, const void *pSource, MemSize_t count){
+ return memcpy(pTarget, pSource, count);
+}
+SML_API void *smlLibMemmove(void *pTarget, const void *pSource, MemSize_t count){
+ return memmove(pTarget, pSource, count);
+}
+SML_API int smlLibMemcmp(const void *pTarget, const void *pSource, MemSize_t count){
+ return memcmp(pTarget, pSource, count);
+}
+#ifndef smlLibMalloc
+// define only if not already defined as a macro
+SML_API void *smlLibMalloc(MemSize_t size) {
+ // %%% luz 2002-10-02
+ #ifdef MEMORY_PROFILING
+ return (void *)sysync_malloc(size);
+ #else
+ return (void *)malloc(size);
+ #endif
+}
+#endif
+
+#endif
+
+
+
diff --git a/src/syncml_tk/src/sml/lib/all/libstr.c b/src/syncml_tk/src/sml/lib/all/libstr.c
new file mode 100755
index 0000000..6c6a31f
--- /dev/null
+++ b/src/syncml_tk/src/sml/lib/all/libstr.c
@@ -0,0 +1,136 @@
+/**
+ * @file
+ * Library for String Functions
+ *
+ * @target_system ALL
+ * @target_os ALL
+ * @description implementation of common string-handling functions
+ */
+
+/*
+ * Copyright Notice
+ * Copyright (c) Ericsson, IBM, Lotus, Matsushita Communication
+ * Industrial Co., Ltd., Motorola, Nokia, Openwave Systems, Inc.,
+ * Palm, Inc., Psion, Starfish Software, Symbian, Ltd. (2001).
+ * All Rights Reserved.
+ * Implementation of all or part of any Specification may require
+ * licenses under third party intellectual property rights,
+ * including without limitation, patent rights (such a third party
+ * may or may not be a Supporter). The Sponsors of the Specification
+ * are not responsible and shall not be held responsible in any
+ * manner for identifying or failing to identify any or all such
+ * third party intellectual property rights.
+ *
+ * THIS DOCUMENT AND THE INFORMATION CONTAINED HEREIN ARE PROVIDED
+ * ON AN "AS IS" BASIS WITHOUT WARRANTY OF ANY KIND AND ERICSSON, IBM,
+ * LOTUS, MATSUSHITA COMMUNICATION INDUSTRIAL CO. LTD, MOTOROLA,
+ * NOKIA, PALM INC., PSION, STARFISH SOFTWARE AND ALL OTHER SYNCML
+ * SPONSORS DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+ * BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
+ * HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
+ * SHALL ERICSSON, IBM, LOTUS, MATSUSHITA COMMUNICATION INDUSTRIAL CO.,
+ * LTD, MOTOROLA, NOKIA, PALM INC., PSION, STARFISH SOFTWARE OR ANY
+ * OTHER SYNCML SPONSOR BE LIABLE TO ANY PARTY FOR ANY LOSS OF
+ * PROFITS, LOSS OF BUSINESS, LOSS OF USE OF DATA, INTERRUPTION OF
+ * BUSINESS, OR FOR DIRECT, INDIRECT, SPECIAL OR EXEMPLARY, INCIDENTAL,
+ * PUNITIVE OR CONSEQUENTIAL DAMAGES OF ANY KIND IN CONNECTION WITH
+ * THIS DOCUMENT OR THE INFORMATION CONTAINED HEREIN, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH LOSS OR DAMAGE.
+ *
+ * The above notice and this paragraph must be included on all copies
+ * of this document that are made.
+ *
+ */
+
+
+/*************************************************************************
+ * Definitions
+ *************************************************************************/
+
+#include "syncml_tk_prefix_file.h" // %%% luz: needed for precompiled headers in eVC++
+
+#include <smldef.h>
+#include "libstr.h"
+#include "libmem.h"
+#ifdef __ANSI_C__
+#include <string.h>
+#endif
+#ifdef __PALM_OS__
+#include <StringMgr.h>
+#endif
+
+
+
+/*************************************************************************
+ * External Functions for all TOOLKIT Versions
+ *************************************************************************/
+
+
+/**
+ * Duplicates the String "constStringP".
+ * Returns a pointer to the new copy of "constStringP".
+ *
+ * @param constStringP (IN)
+ * string which is duplicated
+ * @return pointer to the new copy,\n
+ * NULL if no copy could be allocated
+ */
+SML_API String_t smlLibStrdup (const char *constStringP)
+{
+ String_t _new_str;
+
+ // allocate memory for new copy
+ _new_str = (String_t)smlLibMalloc(smlLibStrlen(constStringP) + 1);
+
+ // Copy the string into the new memory
+ if (_new_str != NULL)
+ smlLibStrcpy(_new_str, constStringP);
+
+ return _new_str;
+}
+
+
+#ifndef __PALM_OS__
+/* If not Palm OS we use the Standard ANSI C functions */
+SML_API String_t smlLibStrcpy(const char *pTarget, const char *pSource) {
+ return strcpy((char *)pTarget, (char *)pSource);
+}
+SML_API String_t smlLibStrncpy(const char *pTarget, const char *pSource, int count){
+ return strncpy((char *)pTarget, (char *)pSource, count);
+}
+SML_API String_t smlLibStrcat(const char *pTarget, const char *pSource){
+ return strcat((char *)pTarget, (char *)pSource);
+}
+SML_API int smlLibStrcmp(const char *pTarget, const char *pSource){
+ return strcmp((char *)pTarget, (char *)pSource);
+}
+SML_API int smlLibStrncmp(const char *pTarget, const char *pSource, int count){
+ return strncmp((char *)pTarget, (char *)pSource, count);
+}
+SML_API String_t smlLibStrchr(const char *pString, char character){
+ return strchr((char *)pString, character);
+}
+SML_API int smlLibStrlen(const char *pString){
+ return strlen((char *)pString);
+}
+
+
+#endif
+
+
+
+/*************************************************************************
+ * Additional External Functions for Full Size TOOLKIT ONLY
+ *************************************************************************/
+
+#ifndef __SML_LITE__ /* these API calls are NOT included in the Toolkit lite version */
+#ifndef __PALM_OS__ /* we use #define to reduce heap usage */
+SML_API String_t smlLibStrncat(const char *pTarget, const char *pSource, int count){
+ return strncat((char *)pTarget, (char *)pSource, count);
+}
+SML_API String_t smlLibStrstr(const char *pString, const char *pSubString){
+ return strstr((char *)pString, (char *)pSubString);
+}
+#endif
+#endif
diff --git a/src/syncml_tk/src/sml/lib/all/libutil.c b/src/syncml_tk/src/sml/lib/all/libutil.c
new file mode 100755
index 0000000..a2c1c5c
--- /dev/null
+++ b/src/syncml_tk/src/sml/lib/all/libutil.c
@@ -0,0 +1,177 @@
+/**
+ * @file
+ * Library for IO Functions
+ *
+ * @target_system ALL
+ * @target_os ALL
+ * @description Utility I/O functions
+ */
+
+
+
+/*
+ * Copyright Notice
+ * Copyright (c) Ericsson, IBM, Lotus, Matsushita Communication
+ * Industrial Co., Ltd., Motorola, Nokia, Openwave Systems, Inc.,
+ * Palm, Inc., Psion, Starfish Software, Symbian, Ltd. (2001).
+ * All Rights Reserved.
+ * Implementation of all or part of any Specification may require
+ * licenses under third party intellectual property rights,
+ * including without limitation, patent rights (such a third party
+ * may or may not be a Supporter). The Sponsors of the Specification
+ * are not responsible and shall not be held responsible in any
+ * manner for identifying or failing to identify any or all such
+ * third party intellectual property rights.
+ *
+ * THIS DOCUMENT AND THE INFORMATION CONTAINED HEREIN ARE PROVIDED
+ * ON AN "AS IS" BASIS WITHOUT WARRANTY OF ANY KIND AND ERICSSON, IBM,
+ * LOTUS, MATSUSHITA COMMUNICATION INDUSTRIAL CO. LTD, MOTOROLA,
+ * NOKIA, PALM INC., PSION, STARFISH SOFTWARE AND ALL OTHER SYNCML
+ * SPONSORS DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+ * BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
+ * HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
+ * SHALL ERICSSON, IBM, LOTUS, MATSUSHITA COMMUNICATION INDUSTRIAL CO.,
+ * LTD, MOTOROLA, NOKIA, PALM INC., PSION, STARFISH SOFTWARE OR ANY
+ * OTHER SYNCML SPONSOR BE LIABLE TO ANY PARTY FOR ANY LOSS OF
+ * PROFITS, LOSS OF BUSINESS, LOSS OF USE OF DATA, INTERRUPTION OF
+ * BUSINESS, OR FOR DIRECT, INDIRECT, SPECIAL OR EXEMPLARY, INCIDENTAL,
+ * PUNITIVE OR CONSEQUENTIAL DAMAGES OF ANY KIND IN CONNECTION WITH
+ * THIS DOCUMENT OR THE INFORMATION CONTAINED HEREIN, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH LOSS OR DAMAGE.
+ *
+ * The above notice and this paragraph must be included on all copies
+ * of this document that are made.
+ *
+ */
+
+
+
+
+/*************************************************************************
+ * Definitions
+ *************************************************************************/
+
+#include "syncml_tk_prefix_file.h" // %%% luz: needed for precompiled headers in eVC++
+
+#ifndef NOWSM
+// Note: in NOWSM case, these routines must be implemented in the
+// calling main code.
+
+#include <smldef.h>
+#include <smlerr.h>
+
+#ifdef __ANSI_C__
+#include <stdarg.h>
+#include <stdio.h>
+#endif
+
+#ifdef __PALM_OS__
+#include <PalmOS.h>
+#include <unix_stdarg.h>
+#endif
+
+#ifdef __EPOC_OS__
+#include <stdarg.h>
+#include <stdio.h>
+#endif
+
+#include "define.h"
+#include "libmem.h"
+#include "mgr.h"
+#include "libutil.h"
+
+
+// size of output buffer
+#define BUFFERSIZE 128
+
+
+/* Used external functions */
+extern SyncMLInfoPtr_t mgrGetSyncMLAnchor();
+
+
+/*************************************************************************
+ * External Functions
+ *************************************************************************/
+
+
+/**
+ * Assembles a formatted textstring out of a list of argument. This string is
+ * passed to a callback function, which is implementated by the application
+ * for output to the user
+ *
+ * @param text (IN)
+ * printf like format text string with multiple
+ * arguments to be formatted as specified\n
+ * Supported are:
+ * \%d, \%i, \%u, \%x, \%s, \%c\n
+ * +, -, \<space\>, *, \<number\>, h, l, L
+ */
+SML_API void smlLibPrint(const char *text, ...)
+{
+ char pBuffer[BUFFERSIZE];
+ va_list args;
+ SyncMLInfoPtr_t pSyncMLInfo;
+
+ if ((pSyncMLInfo = mgrGetSyncMLAnchor()) != NULL) {
+
+ pBuffer[0] = '\0';
+
+ va_start(args, text);
+
+ // assemble the text string out of the single arguments
+ #ifdef __ANSI_C__
+ vsprintf(pBuffer, text, args);
+ #endif
+
+ #ifdef __PALM_OS__
+ StrVPrintF(pBuffer, text, args);
+ #endif
+
+ #ifdef __EPOC_OS__
+ vsprintf(pBuffer, text, args);
+ #endif
+
+ va_end(args);
+
+ // use the application callback function,
+ // which implements the output.
+ if (pSyncMLInfo &&
+ pSyncMLInfo->syncmlOptions &&
+ pSyncMLInfo->syncmlOptions->defaultPrintFunc)
+ ((smlPrintFunc)(pSyncMLInfo->syncmlOptions->defaultPrintFunc))(pBuffer);
+ }
+}
+
+SML_API void smlLibVprintf(const char *format, va_list args)
+{
+ char pBuffer[BUFFERSIZE];
+ SyncMLInfoPtr_t pSyncMLInfo;
+
+ if ((pSyncMLInfo = mgrGetSyncMLAnchor()) != NULL) {
+
+ pBuffer[0] = '\0';
+
+ // assemble the text string out of the single arguments
+ #ifdef __ANSI_C__
+ vsprintf(pBuffer, format, args);
+ #endif
+
+ #ifdef __PALM_OS__
+ StrVPrintF(pBuffer, format, args);
+ #endif
+
+ #ifdef __EPOC_OS__
+ vsprintf(pBuffer, format, args);
+ #endif
+
+ // use the application callback function,
+ // which implements the output.
+ if (pSyncMLInfo &&
+ pSyncMLInfo->syncmlOptions &&
+ pSyncMLInfo->syncmlOptions->defaultPrintFunc)
+ ((smlPrintFunc)(pSyncMLInfo->syncmlOptions->defaultPrintFunc))(pBuffer);
+ }
+}
+
+#endif // !defined(NOWSM)
diff --git a/src/syncml_tk/src/sml/lib/inc/liblock.h b/src/syncml_tk/src/sml/lib/inc/liblock.h
new file mode 100755
index 0000000..5f86f33
--- /dev/null
+++ b/src/syncml_tk/src/sml/lib/inc/liblock.h
@@ -0,0 +1,81 @@
+/**
+ * @file
+ * Library for Thread Locking Functions
+ *
+ * @target_system ALL
+ * @target_os ALL
+ * @description thread-locking library, RTK addition by luz@synthesis.ch
+ */
+
+#ifndef LIBLOCK_H
+#define LIBLOCK_H
+
+#include "define.h"
+
+#ifdef __DEBUG_LOCKS
+// needs debug global and DebugPrintf()
+extern void ThreadDebugPrintf(const char *text,...);
+#define DEBUGPRINTF(m) { ThreadDebugPrintf m; }
+#endif
+
+#if defined(__MAKE_THREADSAFE) && !defined(NOWSM)
+ // thread safety measures are required only when working with WSM
+ #ifdef _WIN32
+ #ifdef __DEBUG_LOCKS
+ // we need TryEnterCriticalSection for debug
+ #define _WIN32_WINNT 0x0400
+ #endif
+ #define WIN32_LEAN_AND_MEAN
+ #include <windows.h>
+ #undef WIN32_LEAN_AND_MEAN
+ // remove problematic windows defines (OPAQUE in wingdi.h, OPTIONAL in ????)
+ #undef OPAQUE
+ #undef OPTIONAL
+
+ #include <stdio.h>
+
+ #ifdef __DEBUG_LOCKS
+ /* - functions that also document lock usage */
+ void _ToolKitLockInit(const char *msg);
+ void _ToolKitLockFree(const char *msg);
+ void _LockToolKit(const char *msg);
+ void _ReleaseToolKit(const char *msg);
+ #define TOOLKITLOCK_INIT(m) _ToolKitLockInit(m);
+ #define TOOLKITLOCK_FREE(m) _ToolKitLockFree(m);
+ #define LOCKTOOLKIT(m) _LockToolKit(m);
+ #define RELEASETOOLKIT(m) _ReleaseToolKit(m);
+ #else
+ /* - simple macros to use the lock */
+ #define TOOLKITLOCK_INIT(m) InitializeCriticalSection(&gSmlLock);
+ #define TOOLKITLOCK_FREE(m) DeleteCriticalSection(&gSmlLock);
+ #define LOCKTOOLKIT(m) EnterCriticalSection(&gSmlLock);
+ #define RELEASETOOLKIT(m) LeaveCriticalSection(&gSmlLock);
+ #endif
+ #else
+ #error "liblock.h: unsupported platform"
+ #endif
+#else
+ /* just NOP */
+ #define TOOLKITLOCK_INIT(m)
+ #define TOOLKITLOCK_FREE(m)
+ #define LOCKTOOLKIT(m)
+ #define RELEASETOOLKIT(m)
+#endif
+
+#endif // LIBLOCK_H
+
+// globals declarations may not be omitted on second include!
+#ifdef __MAKE_THREADSAFE
+ #ifdef _WIN32
+ /* define the required things */
+ #ifndef _IMPLEMENTS_LOCK_GLOBALS
+ /* - the lock itself, as global variable */
+ extern CRITICAL_SECTION gSmlLock;
+ #else
+ CRITICAL_SECTION gSmlLock;
+ #endif
+ #endif
+#endif
+
+
+/* eof */
diff --git a/src/syncml_tk/src/sml/lib/inc/libmem.h b/src/syncml_tk/src/sml/lib/inc/libmem.h
new file mode 100755
index 0000000..95be40a
--- /dev/null
+++ b/src/syncml_tk/src/sml/lib/inc/libmem.h
@@ -0,0 +1,98 @@
+/**
+ * @file
+ * Library for Memory Functions
+ *
+ * @target_system ALL
+ * @target_os ALL
+ * @description Header for the implementation of common memory handling functions
+ */
+
+
+
+/*
+ * Copyright Notice
+ * Copyright (c) Ericsson, IBM, Lotus, Matsushita Communication
+ * Industrial Co., Ltd., Motorola, Nokia, Openwave Systems, Inc.,
+ * Palm, Inc., Psion, Starfish Software, Symbian, Ltd. (2001).
+ * All Rights Reserved.
+ * Implementation of all or part of any Specification may require
+ * licenses under third party intellectual property rights,
+ * including without limitation, patent rights (such a third party
+ * may or may not be a Supporter). The Sponsors of the Specification
+ * are not responsible and shall not be held responsible in any
+ * manner for identifying or failing to identify any or all such
+ * third party intellectual property rights.
+ *
+ * THIS DOCUMENT AND THE INFORMATION CONTAINED HEREIN ARE PROVIDED
+ * ON AN "AS IS" BASIS WITHOUT WARRANTY OF ANY KIND AND ERICSSON, IBM,
+ * LOTUS, MATSUSHITA COMMUNICATION INDUSTRIAL CO. LTD, MOTOROLA,
+ * NOKIA, PALM INC., PSION, STARFISH SOFTWARE AND ALL OTHER SYNCML
+ * SPONSORS DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+ * BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
+ * HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
+ * SHALL ERICSSON, IBM, LOTUS, MATSUSHITA COMMUNICATION INDUSTRIAL CO.,
+ * LTD, MOTOROLA, NOKIA, PALM INC., PSION, STARFISH SOFTWARE OR ANY
+ * OTHER SYNCML SPONSOR BE LIABLE TO ANY PARTY FOR ANY LOSS OF
+ * PROFITS, LOSS OF BUSINESS, LOSS OF USE OF DATA, INTERRUPTION OF
+ * BUSINESS, OR FOR DIRECT, INDIRECT, SPECIAL OR EXEMPLARY, INCIDENTAL,
+ * PUNITIVE OR CONSEQUENTIAL DAMAGES OF ANY KIND IN CONNECTION WITH
+ * THIS DOCUMENT OR THE INFORMATION CONTAINED HEREIN, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH LOSS OR DAMAGE.
+ *
+ * The above notice and this paragraph must be included on all copies
+ * of this document that are made.
+ *
+ */
+
+
+
+#ifndef _LIB_MEM_H
+#define _LIB_MEM_H
+
+
+
+/*************************************************************************
+ * Definitions
+ *************************************************************************/
+
+#include "define.h"
+
+#include <stdlib.h>
+#include <smldef.h>
+#ifdef __ANSI_C__
+#include <string.h>
+#endif
+#ifdef __PALM_OS__
+#include <MemoryMgr.h>
+#endif
+
+
+/*************************************************************************
+ * External Functions for all Toolkit versions
+ *************************************************************************/
+
+
+#ifdef __PALM_OS__ /* we use #define to reduce heap usage */
+ void *smlLibRealloc (VoidPtr_t objectP, MemSize_t constSize) LIB_FUNC;
+ void smlLibFree (void* objectP) LIB_FUNC;
+ #define smlLibMemset(pObject,value,count) ((void)MemSet((VoidPtr_t)pObject,(MemSize_t)count,(int)value))
+ #define smlLibMemcpy(pTarget,pSource,count) (MemMove(pTarget,(VoidPtr_t)pSource,count) ? pTarget : pTarget)
+ #define smlLibMemmove(pTarget,pSource,count) (MemMove(pTarget,(VoidPtr_t)pSource,(MemSize_t)count) ? pTarget : pTarget)
+ #define smlLibMemcmp(pTarget,pSource,count) (MemCmp((VoidPtr_t)pTarget,(VoidPtr_t)pSource,(MemSize_t)count))
+ #define smlLibMalloc(size) ((VoidPtr_t)MemPtrNew((MemSize_t)size))
+ #define smlLibMemsize(pObject) ((MemSize_t)MemPtrSize((VoidPtr_t)pObject))
+#else
+ SML_API_DEF void *smlLibRealloc(void *pObject, MemSize_t size) LIB_FUNC;
+ SML_API_DEF void smlLibFree(void *pObject) LIB_FUNC;
+ SML_API_DEF void *smlLibMemset(void *pObject, int value, MemSize_t count) LIB_FUNC;
+ SML_API_DEF void *smlLibMemcpy(void *pTarget, const void *pSource, MemSize_t count) LIB_FUNC;
+ SML_API_DEF void *smlLibMemmove(void *pTarget, const void *pSource, MemSize_t count) LIB_FUNC;
+ SML_API_DEF int smlLibMemcmp(const void *pTarget, const void *pSource, MemSize_t count) LIB_FUNC;
+ // original: SML_API_DEF void *smlLibMalloc(MemSize_t size) LIB_FUNC;
+ #define smlLibMalloc(m) malloc(m)
+#endif
+
+
+#endif
+
diff --git a/src/syncml_tk/src/sml/lib/inc/libstr.h b/src/syncml_tk/src/sml/lib/inc/libstr.h
new file mode 100755
index 0000000..f3ed1ed
--- /dev/null
+++ b/src/syncml_tk/src/sml/lib/inc/libstr.h
@@ -0,0 +1,111 @@
+/**
+ * @file
+ * Library for String Functions
+ *
+ * @target_system ALL
+ * @target_os ALL
+ * @description Header for the implementation of common string-handling functions
+ */
+
+
+
+/*
+ * Copyright Notice
+ * Copyright (c) Ericsson, IBM, Lotus, Matsushita Communication
+ * Industrial Co., Ltd., Motorola, Nokia, Openwave Systems, Inc.,
+ * Palm, Inc., Psion, Starfish Software, Symbian, Ltd. (2001).
+ * All Rights Reserved.
+ * Implementation of all or part of any Specification may require
+ * licenses under third party intellectual property rights,
+ * including without limitation, patent rights (such a third party
+ * may or may not be a Supporter). The Sponsors of the Specification
+ * are not responsible and shall not be held responsible in any
+ * manner for identifying or failing to identify any or all such
+ * third party intellectual property rights.
+ *
+ * THIS DOCUMENT AND THE INFORMATION CONTAINED HEREIN ARE PROVIDED
+ * ON AN "AS IS" BASIS WITHOUT WARRANTY OF ANY KIND AND ERICSSON, IBM,
+ * LOTUS, MATSUSHITA COMMUNICATION INDUSTRIAL CO. LTD, MOTOROLA,
+ * NOKIA, PALM INC., PSION, STARFISH SOFTWARE AND ALL OTHER SYNCML
+ * SPONSORS DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+ * BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
+ * HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
+ * SHALL ERICSSON, IBM, LOTUS, MATSUSHITA COMMUNICATION INDUSTRIAL CO.,
+ * LTD, MOTOROLA, NOKIA, PALM INC., PSION, STARFISH SOFTWARE OR ANY
+ * OTHER SYNCML SPONSOR BE LIABLE TO ANY PARTY FOR ANY LOSS OF
+ * PROFITS, LOSS OF BUSINESS, LOSS OF USE OF DATA, INTERRUPTION OF
+ * BUSINESS, OR FOR DIRECT, INDIRECT, SPECIAL OR EXEMPLARY, INCIDENTAL,
+ * PUNITIVE OR CONSEQUENTIAL DAMAGES OF ANY KIND IN CONNECTION WITH
+ * THIS DOCUMENT OR THE INFORMATION CONTAINED HEREIN, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH LOSS OR DAMAGE.
+ *
+ * The above notice and this paragraph must be included on all copies
+ * of this document that are made.
+ *
+ */
+
+
+#ifndef __LIB_STR_H
+#define __LIB_STR_H
+
+/*************************************************************************
+ * Definitions
+ *************************************************************************/
+
+#include "define.h"
+
+#include "smldef.h"
+#include "libmem.h"
+#ifdef __ANSI_C__
+#include <string.h>
+#endif
+#ifdef __PALM_OS__
+#include <StringMgr.h>
+#endif
+
+
+/*************************************************************************
+ * External Functions for all Toolkit versions
+ *************************************************************************/
+
+
+#ifdef __PALM_OS__ /* we use #define to reduce heap usage */
+ String_t smlLibStrdup (const char* constStringP) LIB_FUNC;
+ #define smlLibStrcpy(pTarget,pSource) (char*)StrCopy((char*)pTarget,(char*)pSource)
+ #define smlLibStrncpy(pTarget,pSource,count) (char*)StrNCopy((char*)pTarget,(char*)pSource,count)
+ #define smlLibStrcat(pTarget,pSource) (char*)StrCat((char*)pTarget,(char*)pSource)
+ #define smlLibStrcmp(pTarget,pSource) StrCompare((char*)pTarget,(char*)pSource)
+ #define smlLibStrncmp(pTarget,pSource,count) StrNCompare((char*)pTarget,(char*)pSource,count)
+ #define smlLibStrchr(pString,character) (char*)StrChr((String_t)pString,character)
+ #define smlLibStrlen(pString) StrLen((char*)pString)
+#else /* we use functions, to make the library exportable */
+ SML_API_DEF String_t smlLibStrdup (const char *constStringP) LIB_FUNC;
+ SML_API_DEF String_t smlLibStrcpy(const char *pTarget, const char *pSource) LIB_FUNC;
+ SML_API_DEF String_t smlLibStrncpy(const char *pTarget, const char *pSource, int count) LIB_FUNC;
+ SML_API_DEF String_t smlLibStrcat(const char *pTarget, const char *pSource) LIB_FUNC;
+ SML_API_DEF int smlLibStrcmp(const char *pTarget, const char *pSource) LIB_FUNC;
+ SML_API_DEF int smlLibStrncmp(const char *pTarget, const char *pSource, int count) LIB_FUNC;
+ SML_API_DEF String_t smlLibStrchr(const char *pString, char character) LIB_FUNC;
+ SML_API_DEF int smlLibStrlen(const char *pString) LIB_FUNC;
+#endif
+
+
+
+
+/*************************************************************************
+ * Additional External Functions for Full Sized Toolkit Only
+ *************************************************************************/
+
+#ifndef __SML_LITE__ /* these API calls are NOT included in the Toolkit lite version */
+#ifdef __PALM_OS__ /* we use define to reduce heap usage */
+ #define smlLibStrncat(pTarget,pSource,count) (char*)StrNCat((char*)pTarget,(char*)pSource,count)
+ #define smlLibStrstr(pString,pSubstring) (char*)StrStr((char*)pString,(char*)pSubstring)
+#else /* we use functions, to make the library exportable */
+ SML_API_DEF String_t smlLibStrncat(const char *pTarget, const char *pSource, int count) LIB_FUNC;
+ SML_API_DEF String_t smlLibStrstr(const char *pString, const char *pSubString) LIB_FUNC;
+#endif
+#endif
+
+
+#endif
diff --git a/src/syncml_tk/src/sml/lib/inc/libutil.h b/src/syncml_tk/src/sml/lib/inc/libutil.h
new file mode 100755
index 0000000..6f2c2e9
--- /dev/null
+++ b/src/syncml_tk/src/sml/lib/inc/libutil.h
@@ -0,0 +1,87 @@
+/**
+ * @file
+ * Library for utility Functions
+ *
+ * @target_system ALL
+ * @target_os ALL
+ * @description Some I/O Utilities
+ */
+
+
+/*
+ * Copyright Notice
+ * Copyright (c) Ericsson, IBM, Lotus, Matsushita Communication
+ * Industrial Co., Ltd., Motorola, Nokia, Openwave Systems, Inc.,
+ * Palm, Inc., Psion, Starfish Software, Symbian, Ltd. (2001).
+ * All Rights Reserved.
+ * Implementation of all or part of any Specification may require
+ * licenses under third party intellectual property rights,
+ * including without limitation, patent rights (such a third party
+ * may or may not be a Supporter). The Sponsors of the Specification
+ * are not responsible and shall not be held responsible in any
+ * manner for identifying or failing to identify any or all such
+ * third party intellectual property rights.
+ *
+ * THIS DOCUMENT AND THE INFORMATION CONTAINED HEREIN ARE PROVIDED
+ * ON AN "AS IS" BASIS WITHOUT WARRANTY OF ANY KIND AND ERICSSON, IBM,
+ * LOTUS, MATSUSHITA COMMUNICATION INDUSTRIAL CO. LTD, MOTOROLA,
+ * NOKIA, PALM INC., PSION, STARFISH SOFTWARE AND ALL OTHER SYNCML
+ * SPONSORS DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+ * BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
+ * HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
+ * SHALL ERICSSON, IBM, LOTUS, MATSUSHITA COMMUNICATION INDUSTRIAL CO.,
+ * LTD, MOTOROLA, NOKIA, PALM INC., PSION, STARFISH SOFTWARE OR ANY
+ * OTHER SYNCML SPONSOR BE LIABLE TO ANY PARTY FOR ANY LOSS OF
+ * PROFITS, LOSS OF BUSINESS, LOSS OF USE OF DATA, INTERRUPTION OF
+ * BUSINESS, OR FOR DIRECT, INDIRECT, SPECIAL OR EXEMPLARY, INCIDENTAL,
+ * PUNITIVE OR CONSEQUENTIAL DAMAGES OF ANY KIND IN CONNECTION WITH
+ * THIS DOCUMENT OR THE INFORMATION CONTAINED HEREIN, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH LOSS OR DAMAGE.
+ *
+ * The above notice and this paragraph must be included on all copies
+ * of this document that are made.
+ *
+ */
+
+
+#ifndef _LIB_UTIL_H
+#define _LIB_UTIL_H
+
+
+
+/*************************************************************************
+ * Definitions
+ *************************************************************************/
+
+#include "define.h"
+
+#include "smldef.h"
+
+
+#ifdef __ANSI_C__
+ #include <stdarg.h>
+#endif
+
+#ifdef __PALM_OS__
+ #ifndef _MSL_CSTDARG
+ #include <unix_stdarg.h> // only if we don't use MSL
+ #endif
+#endif
+
+#ifdef __EPOC_OS__
+ #include <stdarg.h>
+#endif
+
+
+/*************************************************************************
+ * External Functions
+ *************************************************************************/
+
+/* IO functions */
+SML_API_DEF void smlLibPrint(const char *text, ...) LIB_FUNC;
+SML_API_DEF void smlLibVprintf(const char *format, va_list va) LIB_FUNC;
+
+
+
+#endif
diff --git a/src/syncml_tk/src/sml/mgr/all/mgr.c b/src/syncml_tk/src/sml/mgr/all/mgr.c
new file mode 100755
index 0000000..4a53128
--- /dev/null
+++ b/src/syncml_tk/src/sml/mgr/all/mgr.c
@@ -0,0 +1,288 @@
+/**
+ * @file
+ * Managing SyncML
+ *
+ * @target_system all
+ * @target_os all
+ * @description Core Module managing the life-cycle of a syncML Process itself
+ */
+
+
+/*
+ * Copyright Notice
+ * Copyright (c) Ericsson, IBM, Lotus, Matsushita Communication
+ * Industrial Co., Ltd., Motorola, Nokia, Openwave Systems, Inc.,
+ * Palm, Inc., Psion, Starfish Software, Symbian, Ltd. (2001).
+ * All Rights Reserved.
+ * Implementation of all or part of any Specification may require
+ * licenses under third party intellectual property rights,
+ * including without limitation, patent rights (such a third party
+ * may or may not be a Supporter). The Sponsors of the Specification
+ * are not responsible and shall not be held responsible in any
+ * manner for identifying or failing to identify any or all such
+ * third party intellectual property rights.
+ *
+ * THIS DOCUMENT AND THE INFORMATION CONTAINED HEREIN ARE PROVIDED
+ * ON AN "AS IS" BASIS WITHOUT WARRANTY OF ANY KIND AND ERICSSON, IBM,
+ * LOTUS, MATSUSHITA COMMUNICATION INDUSTRIAL CO. LTD, MOTOROLA,
+ * NOKIA, PALM INC., PSION, STARFISH SOFTWARE AND ALL OTHER SYNCML
+ * SPONSORS DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+ * BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
+ * HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
+ * SHALL ERICSSON, IBM, LOTUS, MATSUSHITA COMMUNICATION INDUSTRIAL CO.,
+ * LTD, MOTOROLA, NOKIA, PALM INC., PSION, STARFISH SOFTWARE OR ANY
+ * OTHER SYNCML SPONSOR BE LIABLE TO ANY PARTY FOR ANY LOSS OF
+ * PROFITS, LOSS OF BUSINESS, LOSS OF USE OF DATA, INTERRUPTION OF
+ * BUSINESS, OR FOR DIRECT, INDIRECT, SPECIAL OR EXEMPLARY, INCIDENTAL,
+ * PUNITIVE OR CONSEQUENTIAL DAMAGES OF ANY KIND IN CONNECTION WITH
+ * THIS DOCUMENT OR THE INFORMATION CONTAINED HEREIN, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH LOSS OR DAMAGE.
+ *
+ * The above notice and this paragraph must be included on all copies
+ * of this document that are made.
+ *
+ */
+
+
+
+
+/*************************************************************************
+ * Definitions
+ *************************************************************************/
+
+#include "syncml_tk_prefix_file.h" // %%% luz: needed for precompiled headers in eVC++
+
+/* Include Headers */
+#include <sml.h>
+#include <smldef.h>
+#include <smlerr.h>
+
+#if defined(NOWSM) && !__LINK_TOOLKIT_STATIC__
+// we need dummies of these as they are listed in the SyncML.def file
+SML_API_DEF Ret_t smlInit(SmlOptionsPtr_t pOptions) { return SML_ERR_OK; }
+SML_API_DEF Ret_t smlSetSyncMLOptions (SmlOptionsPtr_t pOptions) { return SML_ERR_OK; }
+SML_API_DEF Ret_t smlTerminate(void) { return SML_ERR_OK; }
+#endif
+
+#ifndef NOWSM
+
+#include "libmem.h"
+#include "liblock.h"
+#include "wsm.h"
+#include "mgr.h"
+
+#ifdef __EPOC_OS__
+#include "core_globals_epoc.h"
+#endif
+
+/* Prototypes of exported SyncML API functions */
+SML_API Ret_t smlInit(SmlOptionsPtr_t pOptions);
+SML_API Ret_t smlSetSyncMLOptions (SmlOptionsPtr_t pOptions);
+SML_API Ret_t smlTerminate(void);
+
+/* SyncML internal function prototypes */
+InstanceInfoPtr_t mgrGetInstanceListAnchor(void);
+void mgrSetInstanceListAnchor(InstanceInfoPtr_t newListAnchor);
+SyncMLInfoPtr_t mgrGetSyncMLAnchor(void);
+
+
+/**
+ * Anchor of the global syncML info structure
+ */
+#ifndef __EPOC_OS__
+static SyncMLInfoPtr_t pGlobalAnchor=NULL; // this global pointer is used to access ALL globals within syncml
+#endif // This is the ONLY global varible of SyncML!
+
+#ifdef __EPOC_OS__
+#define pGlobalAnchor TheCoreGlobalsEpoc()->pGlobalAnchor
+#endif
+
+/*************************************************************************
+ * Exported SyncML API functions
+ *************************************************************************/
+
+
+
+
+/**
+ * Initializes the SyncML Reference Tookit. This is required, before any
+ * other function can be used.
+ *
+ * @param pCoreOptions (IN)
+ * options to be applied for the toolkit
+ * @return Return Code
+ */
+SML_API Ret_t smlInit(SmlOptionsPtr_t pCoreOptions)
+{
+
+ /* ---- Definitions --- */
+ WsmOptions_t* pWorkspaceOptions;
+ Ret_t rc;
+
+
+ /* --- check, if SyncML has already been initialized --- */
+ if (pGlobalAnchor!=NULL) return SML_ERR_ALREADY_INITIALIZED;
+
+ /* --- Check pOptions, which have been passed by the application --- */
+ if (!pCoreOptions)
+ return SML_ERR_WRONG_USAGE;
+
+
+ /* --- Create a SyncML info memory object to store all globals --- */
+ TOOLKITLOCK_INIT("smlInit");
+ pGlobalAnchor = (SyncMLInfoPtr_t)smlLibMalloc((MemSize_t)sizeof(SyncMLInfo_t));
+ if (pGlobalAnchor==NULL) return SML_ERR_NOT_ENOUGH_SPACE;
+ smlLibMemset(pGlobalAnchor,0,(MemSize_t)sizeof(SyncMLInfo_t));
+
+
+ /* --- Set SyncML settings and options --- */
+ pGlobalAnchor->instanceListAnchor = NULL; // no instance exists at the beginning
+ rc = smlSetSyncMLOptions (pCoreOptions); // store the options in the global structure
+ if (rc!=SML_ERR_OK){
+ smlLibFree(pGlobalAnchor);
+ pGlobalAnchor = NULL;
+ return rc;
+ }
+
+ pGlobalAnchor->tokTbl = (TokenInfoPtr_t)smlLibMalloc(sizeof(TokenInfo_t));
+ if (pGlobalAnchor->tokTbl == NULL)
+ {
+ smlLibFree(pGlobalAnchor);
+ return SML_ERR_NOT_ENOUGH_SPACE;
+ }
+ smlLibMemset(pGlobalAnchor->tokTbl, 0, sizeof(TokenInfo_t));
+ /* --- Init all modules ---*/
+
+ /* Init Workspace Module */
+ pWorkspaceOptions=(WsmOptions_t*)smlLibMalloc((MemSize_t)sizeof(WsmOptions_t)); // create workspace options
+ if (pWorkspaceOptions == NULL) {
+ smlLibFree(pGlobalAnchor->syncmlOptions);
+ smlLibFree(pGlobalAnchor->tokTbl);
+ smlLibFree(pGlobalAnchor);
+ pGlobalAnchor = NULL;
+ return SML_ERR_NOT_ENOUGH_SPACE;
+ }
+
+ smlLibMemset(pWorkspaceOptions,0,(MemSize_t)sizeof(WsmOptions_t));
+ pWorkspaceOptions->maxAvailMem=(MemSize_t)pGlobalAnchor->syncmlOptions->maxWorkspaceAvailMem;
+
+ rc = wsmInit (pWorkspaceOptions);
+ if (rc!=SML_ERR_OK){
+ smlLibFree(pGlobalAnchor->syncmlOptions);
+ smlLibFree(pGlobalAnchor->tokTbl);
+ smlLibFree(pGlobalAnchor);
+ pGlobalAnchor = NULL;
+ smlLibFree(pWorkspaceOptions);
+ return rc;
+ }
+ smlLibFree(pWorkspaceOptions);
+ return SML_ERR_OK;
+}
+
+
+/**
+ * Terminate SyncML. Frees all memory and other ressources used by
+ * SyncML. This function must be called when terminating SyncML
+ *
+ * @pre All instances must have been terminated
+ * @return Return Code
+ */
+SML_API Ret_t smlTerminate(void) {
+ // Have all Instances been terminated?
+ if (pGlobalAnchor->instanceListAnchor!=NULL)
+ return SML_ERR_WRONG_USAGE;
+
+ /* --- Make sure, the workspace is destroyed --*/
+ LOCKTOOLKIT("smlTerminate");
+ wsmTerminate();
+
+ /* --- Free the global structure --*/
+ smlLibFree(pGlobalAnchor->tokTbl->SyncML);
+ smlLibFree(pGlobalAnchor->tokTbl->MetInf);
+ smlLibFree(pGlobalAnchor->tokTbl->DevInf);
+ smlLibFree(pGlobalAnchor->tokTbl);
+ smlLibFree(pGlobalAnchor->syncmlOptions);
+ smlLibFree(pGlobalAnchor);
+ pGlobalAnchor=NULL;
+
+ TOOLKITLOCK_FREE("smlTerminate");
+
+ return SML_ERR_OK;
+
+}
+
+
+
+/**
+ * Change the option settings for syncML
+ *
+ * @param pCoreOptions (IN)
+ * options to be applied for the toolkit
+ * @return Return Code
+ */
+SML_API Ret_t smlSetSyncMLOptions(SmlOptionsPtr_t pCoreOptions) {
+
+
+ /* ---- Definitions --- */
+ SmlOptionsPtr_t pCoreOptionsCopy;
+
+
+ /* --- Check pOptions, which have been passed by the application --- */
+ if (!pCoreOptions)
+ return SML_ERR_WRONG_USAGE;
+
+
+ /* --- free SyncML options --- */
+ smlLibFree(pGlobalAnchor->syncmlOptions);
+ pGlobalAnchor->syncmlOptions = NULL;
+ /* --- Use a copy of pCoreOptions --- */
+ pCoreOptionsCopy = (SmlOptionsPtr_t)smlLibMalloc((MemSize_t)sizeof(SmlOptions_t));
+ if (pCoreOptionsCopy==NULL) return SML_ERR_NOT_ENOUGH_SPACE;
+ smlLibMemcpy(pCoreOptionsCopy,pCoreOptions,(MemSize_t)sizeof(SmlOptions_t));
+
+
+ /* --- set new SyncML options --- */
+ pGlobalAnchor->syncmlOptions = pCoreOptionsCopy; // set the options,passed from the application
+
+ return SML_ERR_OK;
+
+}
+
+/*************************************************************************
+ * SyncML internal functions
+ *************************************************************************/
+/**
+ * Retrieves a pointer to the structure holding all global informations
+ * within SyncML
+ *
+ * @return Pointer to the pGlobalAnchor
+ */
+SyncMLInfoPtr_t mgrGetSyncMLAnchor(void)
+{
+ return pGlobalAnchor;
+}
+
+/**
+ * Retrieves a pointer to the list holding all instance informations
+ *
+ * @return Pointer to the pInstanceListAnchor
+ */
+InstanceInfoPtr_t mgrGetInstanceListAnchor(void)
+{
+ return pGlobalAnchor->instanceListAnchor;
+}
+
+/**
+ * Set the pointer to the list holding all instance informations
+ *
+ * @param newListAnchor (IN)
+ * pointer to the pInstanceListAnchor
+ */
+void mgrSetInstanceListAnchor(InstanceInfoPtr_t newListAnchor)
+{
+ pGlobalAnchor->instanceListAnchor=newListAnchor;
+}
+
+
+#endif // !defined(NOWSM)
diff --git a/src/syncml_tk/src/sml/mgr/all/mgrcmdbuilder.c b/src/syncml_tk/src/sml/mgr/all/mgrcmdbuilder.c
new file mode 100755
index 0000000..0c1ad7e
--- /dev/null
+++ b/src/syncml_tk/src/sml/mgr/all/mgrcmdbuilder.c
@@ -0,0 +1,808 @@
+/**
+ * @file
+ * SyncML Command Builder
+ *
+ * @target_system all
+ * @target_os all
+ * @description Core Module for assembling SyncML compliant documents
+ */
+
+
+/*
+ * Copyright Notice
+ * Copyright (c) Ericsson, IBM, Lotus, Matsushita Communication
+ * Industrial Co., Ltd., Motorola, Nokia, Openwave Systems, Inc.,
+ * Palm, Inc., Psion, Starfish Software, Symbian, Ltd. (2001).
+ * All Rights Reserved.
+ * Implementation of all or part of any Specification may require
+ * licenses under third party intellectual property rights,
+ * including without limitation, patent rights (such a third party
+ * may or may not be a Supporter). The Sponsors of the Specification
+ * are not responsible and shall not be held responsible in any
+ * manner for identifying or failing to identify any or all such
+ * third party intellectual property rights.
+ *
+ * THIS DOCUMENT AND THE INFORMATION CONTAINED HEREIN ARE PROVIDED
+ * ON AN "AS IS" BASIS WITHOUT WARRANTY OF ANY KIND AND ERICSSON, IBM,
+ * LOTUS, MATSUSHITA COMMUNICATION INDUSTRIAL CO. LTD, MOTOROLA,
+ * NOKIA, PALM INC., PSION, STARFISH SOFTWARE AND ALL OTHER SYNCML
+ * SPONSORS DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+ * BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
+ * HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
+ * SHALL ERICSSON, IBM, LOTUS, MATSUSHITA COMMUNICATION INDUSTRIAL CO.,
+ * LTD, MOTOROLA, NOKIA, PALM INC., PSION, STARFISH SOFTWARE OR ANY
+ * OTHER SYNCML SPONSOR BE LIABLE TO ANY PARTY FOR ANY LOSS OF
+ * PROFITS, LOSS OF BUSINESS, LOSS OF USE OF DATA, INTERRUPTION OF
+ * BUSINESS, OR FOR DIRECT, INDIRECT, SPECIAL OR EXEMPLARY, INCIDENTAL,
+ * PUNITIVE OR CONSEQUENTIAL DAMAGES OF ANY KIND IN CONNECTION WITH
+ * THIS DOCUMENT OR THE INFORMATION CONTAINED HEREIN, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH LOSS OR DAMAGE.
+ *
+ * The above notice and this paragraph must be included on all copies
+ * of this document that are made.
+ *
+ */
+
+
+
+
+/*************************************************************************
+ * Definitions
+ *************************************************************************/
+
+#include "syncml_tk_prefix_file.h" // %%% luz: needed for precompiled headers in eVC++
+
+/* Include Headers */
+#include <smldef.h>
+#include "xltenc.h"
+#include "xltdec.h"
+#include "libmem.h"
+#include "mgr.h"
+
+
+/* Used external functions */
+extern Ret_t smlLockWriteBuffer(InstanceID_t id, MemPtr_t *pWritePosition, MemSize_t *freeSize);
+extern Ret_t smlUnlockWriteBuffer(InstanceID_t id, MemSize_t writtenBytes);
+
+#ifndef __SML_LITE__ /* these API calls are NOT included in the Toolkit lite version */
+ extern Ret_t addInfo(InstanceInfoPtr_t pInfo);
+ extern InstanceInfoPtr_t findInfo(InstanceID_t id);
+ extern Ret_t removeInfo(InstanceID_t id);
+#endif
+
+/* Prototypes of exported SyncML API functions */
+SML_API Ret_t smlStartMessage(InstanceID_t id, SmlSyncHdrPtr_t pContent);
+SML_API Ret_t smlStartMessageExt(InstanceID_t id, SmlSyncHdrPtr_t pContent, SmlVersion_t vers);
+SML_API Ret_t smlEndMessage(InstanceID_t id, Boolean_t final);
+SML_API Ret_t smlStartSync(InstanceID_t id, SmlSyncPtr_t pContent);
+SML_API Ret_t smlEndSync(InstanceID_t id);
+
+#ifdef ATOMIC_SEND /* these API calls are NOT included in the Toolkit lite version */
+ SML_API Ret_t smlStartAtomic(InstanceID_t id, SmlAtomicPtr_t pContent);
+ SML_API Ret_t smlEndAtomic(InstanceID_t id);
+#endif
+#ifdef SEQUENCE_SEND
+ SML_API Ret_t smlStartSequence(InstanceID_t id, SmlSequencePtr_t pContent);
+ SML_API Ret_t smlEndSequence(InstanceID_t id);
+#endif
+
+#ifdef ADD_SEND
+ SML_API Ret_t smlAddCmd(InstanceID_t id, SmlAddPtr_t pContent);
+#endif
+SML_API Ret_t smlAlertCmd(InstanceID_t id, SmlAlertPtr_t pContent);
+SML_API Ret_t smlDeleteCmd(InstanceID_t id, SmlDeletePtr_t pContent);
+#ifdef GET_SEND
+ SML_API Ret_t smlGetCmd(InstanceID_t id, SmlGetPtr_t pContent);
+#endif
+SML_API Ret_t smlPutCmd(InstanceID_t id, SmlPutPtr_t pContent);
+SML_API Ret_t smlMapCmd(InstanceID_t id, SmlMapPtr_t pContent);
+SML_API Ret_t smlResultsCmd(InstanceID_t id, SmlResultsPtr_t pContent);
+SML_API Ret_t smlStatusCmd(InstanceID_t id, SmlStatusPtr_t pContent);
+SML_API Ret_t smlReplaceCmd(InstanceID_t id, SmlReplacePtr_t pContent);
+
+#ifdef COPY_SEND /* these API calls are NOT included in the Toolkit lite version */
+ SML_API Ret_t smlCopyCmd(InstanceID_t id, SmlCopyPtr_t pContent);
+#endif
+#ifdef EXEC_SEND
+ SML_API Ret_t smlExecCmd(InstanceID_t id, SmlExecPtr_t pContent);
+#endif
+#ifdef SEARCH_SEND
+ SML_API Ret_t smlSearchCmd(InstanceID_t id, SmlSearchPtr_t pContent);
+#endif
+ SML_API Ret_t smlMoveCmd(InstanceID_t id, SmlMovePtr_t pContent);
+
+
+/* Private function prototypes */
+static Ret_t mgrCreateNextCommand(InstanceID_t id, SmlProtoElement_t cmdType, VoidPtr_t pContent);
+Ret_t mgrResetWorkspace (InstanceID_t id);
+
+
+
+
+/*************************************************************************
+ * Exported SyncML API functions
+ *************************************************************************/
+
+
+/**
+ * Start a SyncML Message
+ *
+ * @param id (IN)
+ * ID of the used instance
+ * @param pContent (IN)
+ * Data to pass along with that SyncML command
+ * @return Return Code
+ * @note (%%% luz 2003-08-06) this entry point is for compatibilty
+ * reasons only and works for SyncML 1.0 only\n
+ * please use smlStartMessageExt() instead in new projects.
+ */
+SML_API Ret_t smlStartMessage(InstanceID_t id, SmlSyncHdrPtr_t pContent)
+{
+ /* just call smlStartMessageExt with vers set to SyncML 1.0 */
+ return smlStartMessageExt(id,pContent,SML_VERS_1_0);
+}
+
+
+/**
+ * Start a SyncML Message
+ *
+ * @param id (IN)
+ * ID of the used instance
+ * @param pContent (IN)
+ * Data to pass along with that SyncML command
+ * @param vers (IN)
+ * SyncML version
+ * @note (%%% added by luz 2003-08-06 to support SyncML versions other than
+ * 1.0 with new vers parameter)
+ * @return Return Code
+ */
+SML_API Ret_t smlStartMessageExt(InstanceID_t id, SmlSyncHdrPtr_t pContent, SmlVersion_t vers)
+{
+
+ /* --- Definitions --- */
+ InstanceInfoPtr_t pInstanceInfo; // pointer the the instance info structure for this id
+ Ret_t rc;
+ MemPtr_t pCurrentWritePosition; // current Position from to which to write
+ MemPtr_t pBeginPosition; // saves the first position which has been written
+ MemSize_t freeSize; // size of free memory for writing
+
+
+ #ifdef NOWSM
+ pInstanceInfo = (InstanceInfoPtr_t)id; // ID is the instance info pointer
+ #else
+ /* --- Retrieve the corresponding instanceInfo structure --- */
+ #ifdef __SML_LITE__ /* Only ONE instance is supported in the Toolkit lite version */
+ pInstanceInfo = mgrGetInstanceListAnchor();
+ #else
+ pInstanceInfo = (InstanceInfoPtr_t) findInfo(id);
+ #endif
+ #endif
+
+ if (pInstanceInfo==NULL) return SML_ERR_MGR_INVALID_INSTANCE_INFO;
+
+
+ /* --- Get Write Access to the workspace --- */
+
+ #ifdef NOWSM
+ // first remember where outgoing message starts in buffer
+ smlSetOutgoingBegin(id);
+ #endif
+
+ // eventually, this will cause a buffer reset (if nothing is
+ // unread in the buffer), and will auto-readjust the OutgoingBegin.
+ rc = smlLockWriteBuffer(id, &pCurrentWritePosition, &freeSize);
+
+ if (rc!=SML_ERR_OK) {
+ // abort, unlock the buffer again without changing it's current position
+ smlUnlockWriteBuffer(id, (MemSize_t)0);
+ return rc;
+ }
+
+ /* Remember the position we have started writing */
+ pBeginPosition=pCurrentWritePosition;
+
+ /* --- Call the encoder module --- */
+ /* (Saves the returned encoder state to the corresponding instanceInfo structure */
+ rc = xltEncInit
+ (pInstanceInfo->instanceOptions->encoding,
+ pContent,
+ pCurrentWritePosition+freeSize,
+ &pCurrentWritePosition,
+ (XltEncoderPtr_t *)&(pInstanceInfo->encoderState),
+ vers
+ );
+
+ if (rc!=SML_ERR_OK) {
+ // abort, unlock the buffer again without changing it's current position
+ smlUnlockWriteBuffer(id, (MemSize_t)0);
+ // Reset the encoder module (free the encoding object)
+ xltEncReset(pInstanceInfo->encoderState);
+ // this encoding job is over! reset instanceInfo pointer
+ pInstanceInfo->encoderState=NULL;
+
+ return rc;
+ }
+
+ /* --- End Write Access to the workspace --- */
+ rc = smlUnlockWriteBuffer(id, (MemSize_t)pCurrentWritePosition-(MemSize_t)pBeginPosition);
+ return rc;
+}
+
+/**
+ * End a SyncML Message
+ *
+ * @param id (IN)
+ * ID of the used instance
+ * @param final (IN)
+ * Final Flag indicates last message within a package
+ * @return Return Code
+ */
+SML_API Ret_t smlEndMessage(InstanceID_t id, Boolean_t final)
+{
+
+ /* --- Definitions --- */
+ InstanceInfoPtr_t pInstanceInfo; // pointer the the instance info structure for this id
+ Ret_t rc;
+ MemPtr_t pCurrentWritePosition; // current Position from to which to write
+ MemPtr_t pBeginPosition; // saves the first position which has been written
+ MemSize_t freeSize; // size of free memory for writing
+
+
+ #ifdef NOWSM
+ pInstanceInfo = (InstanceInfoPtr_t)id; // ID is the instance info pointer
+ #else
+ /* --- Retrieve the corresponding instanceInfo structure --- */
+ #ifdef __SML_LITE__ /* Only ONE instance is supported in the Toolkit lite version */
+ pInstanceInfo = mgrGetInstanceListAnchor();
+ #else
+ pInstanceInfo = (InstanceInfoPtr_t) findInfo(id);
+ #endif
+ #endif
+
+ if (pInstanceInfo==NULL) return SML_ERR_MGR_INVALID_INSTANCE_INFO;
+ // %%% luz 2003-08-19: added NULL check as previously failed encoding will delete encoder
+ if (pInstanceInfo->encoderState==NULL) return SML_ERR_MGR_INVALID_INSTANCE_INFO;
+
+
+ /* --- Get Write Access to the workspace --- */
+ rc = smlLockWriteBuffer(id, &pCurrentWritePosition, &freeSize);
+
+ if (rc!=SML_ERR_OK) {
+ // abort, unlock the buffer again without changing it's current position
+ smlUnlockWriteBuffer(id, (MemSize_t)0);
+ return rc;
+ }
+
+
+ /* Remember the position we have started writing */
+ pBeginPosition=pCurrentWritePosition;
+
+ /* -- set Final Flag --*/
+ ((XltEncoderPtr_t)(pInstanceInfo->encoderState))->final = final;
+
+ /* --- Call the encoder module --- */
+ rc = xltEncTerminate(pInstanceInfo->encoderState, pCurrentWritePosition+freeSize,&pCurrentWritePosition);
+
+ if (rc!=SML_ERR_OK) {
+ // abort, unlock the buffer again without changing it's current position
+ smlUnlockWriteBuffer(id, (MemSize_t)0);
+ // this encoding job is over! reset instanceInfo pointer
+ pInstanceInfo->encoderState=NULL;
+
+ return rc;
+ }
+
+ // this encoding job is over! reset instanceInfo pointer
+ // (the decoding object itself has been freed by the decoder)
+ pInstanceInfo->encoderState=NULL;
+
+ /* --- End Write Access to the workspace --- */
+ rc = smlUnlockWriteBuffer(id, (MemSize_t)pCurrentWritePosition-(MemSize_t)pBeginPosition);
+
+
+ return rc;
+}
+
+
+
+
+
+/**
+ * Start synchronizing
+ *
+ * @param id (IN)
+ * ID of the used instance
+ * @param pContent (IN) Data to pass along with that SyncML command
+ * @return Return Code
+ */
+SML_API Ret_t smlStartSync(InstanceID_t id, SmlSyncPtr_t pContent)
+{
+ return mgrCreateNextCommand(id, SML_PE_SYNC_START, pContent);
+}
+
+
+
+/**
+ * End synchronizing
+ *
+ * @param id (IN)
+ * ID of the used instance
+ * @return Return Code
+ */
+SML_API Ret_t smlEndSync(InstanceID_t id)
+{
+ return mgrCreateNextCommand(id, SML_PE_SYNC_END, NULL);
+}
+
+
+#ifdef ATOMIC_SEND /* these API calls are NOT included in the Toolkit lite version */
+
+/**
+ * Start an atomic sequence
+ *
+ * @param id (IN)
+ * ID of the used instance
+ * @param pContent (IN)
+ * Data to pass along with that SyncML command
+ * @return Return Code
+ */
+SML_API Ret_t smlStartAtomic(InstanceID_t id, SmlAtomicPtr_t pContent)
+{
+ return mgrCreateNextCommand(id, SML_PE_ATOMIC_START, pContent);
+}
+
+
+/**
+ * End an atomic sequence
+ *
+ * @param id (IN)
+ * ID of the used instance
+ * @return Return Code
+ */
+SML_API Ret_t smlEndAtomic(InstanceID_t id)
+{
+ return mgrCreateNextCommand(id, SML_PE_ATOMIC_END, NULL);
+}
+
+#endif
+
+#ifdef SEQUENCE_SEND
+
+/**
+ * Start a sequence
+ *
+ * @param id (IN)
+ * ID of the used instance
+ * @param pContent (IN)
+ * Data to pass along with that SyncML command
+ * @return Return Code
+ */
+SML_API Ret_t smlStartSequence(InstanceID_t id, SmlSequencePtr_t pContent)
+{
+ return mgrCreateNextCommand(id, SML_PE_SEQUENCE_START, pContent);
+}
+
+
+
+/**
+ * End a sequence
+ *
+ * @param id (IN)
+ * ID of the used instance
+ * @return Return Code
+ */
+SML_API Ret_t smlEndSequence(InstanceID_t id)
+{
+ return mgrCreateNextCommand(id, SML_PE_SEQUENCE_END, NULL);
+}
+
+#endif
+
+
+#ifdef ADD_SEND
+/**
+ * Create a Add Command
+ *
+ * @param id (IN)
+ * ID of the used instance
+ * @param pContent (IN)
+ * Data to pass along with that SyncML command
+ * @return Return Code
+ */
+SML_API Ret_t smlAddCmd(InstanceID_t id, SmlAddPtr_t pContent)
+{
+ return mgrCreateNextCommand(id, SML_PE_ADD, pContent);
+}
+#endif
+
+
+/**
+ * Create a Alert Command
+ *
+ * @param id (IN)
+ * ID of the used instance
+ * @param pContent (IN)
+ * Data to pass along with that SyncML command
+ * @return Return Code
+ */
+SML_API Ret_t smlAlertCmd(InstanceID_t id, SmlAlertPtr_t pContent)
+{
+ return mgrCreateNextCommand(id, SML_PE_ALERT, pContent);
+}
+
+
+
+
+/**
+ * Create a Start Message Command
+ *
+ * @param id (IN)
+ * ID of the used instance
+ * @param pContent (IN)
+ * Data to pass along with that SyncML command
+ * @return Return Code
+ */
+SML_API Ret_t smlDeleteCmd(InstanceID_t id, SmlDeletePtr_t pContent)
+{
+ return mgrCreateNextCommand(id, SML_PE_DELETE, pContent);
+}
+
+
+
+#ifdef GET_SEND
+
+
+/**
+ * Create a Get Command
+ *
+ * @param id (IN)
+ * ID of the used instance
+ * @param pContent (IN)
+ * Data to pass along with that SyncML command
+ * @return Return Code
+ */
+SML_API Ret_t smlGetCmd(InstanceID_t id, SmlGetPtr_t pContent)
+{
+ return mgrCreateNextCommand(id, SML_PE_GET, pContent);
+}
+
+#endif
+
+
+/**
+ * Create a Put Command
+ *
+ * @param id (IN)
+ * ID of the used instance
+ * @param pContent (IN)
+ * Data to pass along with that SyncML command
+ * @return Return Code
+ */
+SML_API Ret_t smlPutCmd(InstanceID_t id, SmlPutPtr_t pContent)
+{
+ return mgrCreateNextCommand(id, SML_PE_PUT, pContent);
+}
+
+
+
+/**
+ * Create a Map Command
+ *
+ * @param id (IN)
+ * ID of the used instance
+ * @param pContent (IN)
+ * Data to pass along with that SyncML command
+ * @return Return Code
+ */
+SML_API Ret_t smlMapCmd(InstanceID_t id, SmlMapPtr_t pContent)
+{
+ return mgrCreateNextCommand(id, SML_PE_MAP, pContent);
+}
+
+
+
+/**
+ * Create a Results Command
+ *
+ * @param id (IN)
+ * ID of the used instance
+ * @param pContent (IN)
+ * Data to pass along with that SyncML command
+ * @return Return Code
+ */
+SML_API Ret_t smlResultsCmd(InstanceID_t id, SmlResultsPtr_t pContent)
+{
+ return mgrCreateNextCommand(id, SML_PE_RESULTS, pContent);
+}
+
+
+
+
+
+/**
+ * Create a Status Command
+ *
+ * @param id (IN)
+ * ID of the used instance
+ * @param pContent (IN)
+ * Data to pass along with that SyncML command
+ * @return Return Code
+ */
+SML_API Ret_t smlStatusCmd(InstanceID_t id, SmlStatusPtr_t pContent)
+{
+ return mgrCreateNextCommand(id, SML_PE_STATUS, pContent);
+}
+
+
+
+/**
+ * Create a Replace Command
+ *
+ * @param id (IN)
+ * ID of the used instance
+ * @param pContent (IN)
+ * Data to pass along with that SyncML command
+ * @return Return Code
+ */
+SML_API Ret_t smlReplaceCmd(InstanceID_t id, SmlReplacePtr_t pContent)
+{
+ return mgrCreateNextCommand(id, SML_PE_REPLACE, pContent);
+}
+
+
+
+#ifdef COPY_SEND /* these API calls are NOT included in the Toolkit lite version */
+
+
+/**
+ * Create a Copy Command
+ *
+ * @param id (IN)
+ * ID of the used instance
+ * @param pContent (IN)
+ * Data to pass along with that SyncML command
+ * @return Return Code
+ */
+SML_API Ret_t smlCopyCmd(InstanceID_t id, SmlCopyPtr_t pContent)
+{
+ return mgrCreateNextCommand(id, SML_PE_COPY, pContent);
+}
+
+#endif
+
+
+/**
+ * Create a Move Command
+ *
+ * @param id (IN)
+ * ID of the used instance
+ * @param pContent (IN)
+ * Data to pass along with that SyncML command
+ * @return Return Code
+ */
+SML_API Ret_t smlMoveCmd(InstanceID_t id, SmlMovePtr_t pContent)
+{
+ return mgrCreateNextCommand(id, SML_PE_MOVE, pContent);
+}
+
+
+#ifdef EXEC_SEND
+
+/**
+ * Create a Exec Command
+ *
+ * @param id (IN)
+ * ID of the used instance
+ * @param pContent (IN)
+ * Data to pass along with that SyncML command
+ * @return Return Code
+ */
+SML_API Ret_t smlExecCmd(InstanceID_t id, SmlExecPtr_t pContent)
+{
+ return mgrCreateNextCommand(id, SML_PE_EXEC, pContent);
+}
+
+#endif
+
+#ifdef SEARCH_SEND
+
+/**
+ * Create a Search Command
+ *
+ * @param id (IN)
+ * ID of the used instance
+ * @param pContent (IN)
+ * Data to pass along with that SyncML command
+ * @return Return Code
+ */
+SML_API Ret_t smlSearchCmd(InstanceID_t id, SmlSearchPtr_t pContent)
+{
+ return mgrCreateNextCommand(id, SML_PE_SEARCH, pContent);
+}
+
+
+#endif
+
+
+/*************************************************************************
+ * Exported SyncML API functions (FULL-SIZE TOOLKIT ONLY)
+ *************************************************************************/
+
+#ifndef __SML_LITE__ /* these API calls are NOT included in the Toolkit lite version */
+/**
+ * Starts an evaluation run which prevents further API-Calls to write tags -
+ * just the tag-sizes are calculated. Must be sopped via smlEndEvaluation
+ *
+ * @param id (IN)
+ * ID of the used instance
+ * @return Return Code
+ */
+SML_API Ret_t smlStartEvaluation(InstanceID_t id)
+{
+ InstanceInfoPtr_t pInstanceInfo; // pointer the the instance info structure for this id
+ Ret_t rc;
+
+ #ifdef NOWSM
+ pInstanceInfo = (InstanceInfoPtr_t)id; // ID is the instance info pointer
+ #else
+ /* --- Retrieve the corresponding instanceInfo structure --- */
+ #ifdef __SML_LITE__ /* Only ONE instance is supported in the Toolkit lite version */
+ pInstanceInfo = mgrGetInstanceListAnchor();
+ #else
+ pInstanceInfo = (InstanceInfoPtr_t) findInfo(id);
+ #endif
+ #endif
+
+ if (pInstanceInfo==NULL) return SML_ERR_MGR_INVALID_INSTANCE_INFO;
+
+
+ /* --- Initialize Encoder for evaluation mode --- */
+
+ rc = xltStartEvaluation((XltEncoderPtr_t)(pInstanceInfo->encoderState));
+
+ return rc;
+}
+
+
+/**
+ * Stops an evaluation run which prevents further API-Calls to write tags -
+ * the remaining free buffer size after all Tags are written is returned
+ *
+ * @param id (IN)
+ * ID of the used instance
+ * @param freemem (IN/OUT)
+ * Size of free buffer for data after all tags are written
+ * @return Return Code
+ */
+SML_API Ret_t smlEndEvaluation(InstanceID_t id, MemSize_t *freemem)
+{
+ InstanceInfoPtr_t pInstanceInfo; // pointer the the instance info structure for this id
+ Ret_t rc;
+
+ #ifdef NOWSM
+ pInstanceInfo = (InstanceInfoPtr_t)id; // ID is the instance info pointer
+ #else
+ /* --- Retrieve the corresponding instanceInfo structure --- */
+ #ifdef __SML_LITE__ /* Only ONE instance is supported in the Toolkit lite version */
+ pInstanceInfo = mgrGetInstanceListAnchor();
+ #else
+ pInstanceInfo = (InstanceInfoPtr_t) findInfo(id);
+ #endif
+ #endif
+
+ if (pInstanceInfo==NULL) return SML_ERR_MGR_INVALID_INSTANCE_INFO;
+
+ // %%% luz 2002-09-03: encoder can be null here if fatal error occurred before
+ if (pInstanceInfo->encoderState==NULL)
+ return SML_ERR_WRONG_USAGE;
+
+ rc = xltEndEvaluation(id, (XltEncoderPtr_t)(pInstanceInfo->encoderState), freemem);
+ return SML_ERR_OK;
+}
+
+#endif
+
+
+/*************************************************************************
+ * Private Functions
+ *************************************************************************/
+
+
+/**
+ * Calls the encoding routines of the Encoder Module for a given Command Type
+ * and Command Content
+ *
+ * @param id (IN)
+ * ID of the Instance
+ * @param cmdType (IN)
+ * Type of the command (defined by the Proto Element Enumeration)
+ * @param pContent (IN)
+ * Content of the command to encode
+ * @return Return value,\n
+ * SML_ERR_OK if command has been encoded successfully
+ */
+static Ret_t mgrCreateNextCommand(InstanceID_t id, SmlProtoElement_t cmdType, VoidPtr_t pContent)
+{
+ /* --- Definitions --- */
+ InstanceInfoPtr_t pInstanceInfo; // pointer the the instance info structure for this id
+ Ret_t rc;
+ MemPtr_t pCurrentWritePosition; // current Position from to which to write
+ MemPtr_t pBeginPosition; // saves the first position which has been written
+ MemSize_t freeSize; // size of free memory for writing
+
+
+ #ifdef NOWSM
+ pInstanceInfo = (InstanceInfoPtr_t)id; // ID is the instance info pointer
+ #else
+ /* --- Retrieve the corresponding instanceInfo structure --- */
+ #ifdef __SML_LITE__ /* Only ONE instance is supported in the Toolkit lite version */
+ pInstanceInfo = mgrGetInstanceListAnchor();
+ #else
+ pInstanceInfo = (InstanceInfoPtr_t) findInfo(id);
+ #endif
+ #endif
+
+ if (pInstanceInfo==NULL) return SML_ERR_MGR_INVALID_INSTANCE_INFO;
+ // %%% luz 2002-11-27: added NULL check as previously failed encoding will delete encoder
+ if (pInstanceInfo->encoderState==NULL) return SML_ERR_MGR_INVALID_INSTANCE_INFO;
+
+ /* --- Get Write Access to the workspace --- */
+ rc = smlLockWriteBuffer(id, &pCurrentWritePosition, &freeSize);
+
+ if (rc!=SML_ERR_OK) {
+ // abort, unlock the buffer again without changing it's current position
+ smlUnlockWriteBuffer(id, (MemSize_t)0);
+ return rc;
+ }
+
+
+ // Remember the position we have started writing
+ pBeginPosition=pCurrentWritePosition;
+
+
+ /* --- Call the encoder module --- */
+ rc = xltEncAppend(pInstanceInfo->encoderState, cmdType, pCurrentWritePosition+freeSize, pContent, &pCurrentWritePosition);
+
+ if (rc!=SML_ERR_OK) {
+ /* check for full buffer and call TransmitChunk */
+ if (rc == SML_ERR_XLT_BUF_ERR) {
+ // first check wether callback is defined
+ if (pInstanceInfo->callbacks->transmitChunkFunc!= NULL) {
+ // abort, unlock the buffer again without changing it's current position
+ smlUnlockWriteBuffer(id, (MemSize_t)0);
+ // call the callback
+ pInstanceInfo->callbacks->transmitChunkFunc(id,NULL);
+ // lock -> returns the amount of free buffer space
+ smlLockWriteBuffer(id, &pCurrentWritePosition, &freeSize);
+ pBeginPosition = pCurrentWritePosition;
+ // now try again to encode and see wether we now have enough mem available
+ rc = xltEncAppend(pInstanceInfo->encoderState, cmdType, pCurrentWritePosition+freeSize, pContent, &pCurrentWritePosition);
+ // if rc == SML_ERR_OK continue else
+ // return the errorcode
+ if( rc != SML_ERR_OK)
+ {
+ smlUnlockWriteBuffer(id, (MemSize_t)0);
+ // Reset the encoder module (free the encoding object)
+ xltEncReset(pInstanceInfo->encoderState);
+ // this encoding job is over! reset instanceInfo pointer
+ pInstanceInfo->encoderState=NULL;
+ return rc;
+ }
+ }
+ } else {
+ // abort, unlock the buffer again without changing it's current position
+ smlUnlockWriteBuffer(id, (MemSize_t)0);
+ // Reset the encoder module (free the encoding object)
+ xltEncReset(pInstanceInfo->encoderState);
+ // this encoding job is over! reset instanceInfo pointer
+ pInstanceInfo->encoderState=NULL;
+ return rc;
+ }
+ }
+ /* --- End Write Access to the workspace --- */
+ rc = smlUnlockWriteBuffer(id, (MemSize_t)pCurrentWritePosition-(MemSize_t)pBeginPosition);
+ return rc;
+}
+
+/* eof */
diff --git a/src/syncml_tk/src/sml/mgr/all/mgrcmddispatcher.c b/src/syncml_tk/src/sml/mgr/all/mgrcmddispatcher.c
new file mode 100755
index 0000000..723ec02
--- /dev/null
+++ b/src/syncml_tk/src/sml/mgr/all/mgrcmddispatcher.c
@@ -0,0 +1,510 @@
+/**
+ * @file
+ * SyncML Command Dispatcher
+ *
+ * @target_system all
+ * @target_os all
+ * @description Core module for dispatching parsed commands and invoking
+ * callback functions of the application
+ */
+
+
+/*
+ * Copyright Notice
+ * Copyright (c) Ericsson, IBM, Lotus, Matsushita Communication
+ * Industrial Co., Ltd., Motorola, Nokia, Openwave Systems, Inc.,
+ * Palm, Inc., Psion, Starfish Software, Symbian, Ltd. (2001).
+ * All Rights Reserved.
+ * Implementation of all or part of any Specification may require
+ * licenses under third party intellectual property rights,
+ * including without limitation, patent rights (such a third party
+ * may or may not be a Supporter). The Sponsors of the Specification
+ * are not responsible and shall not be held responsible in any
+ * manner for identifying or failing to identify any or all such
+ * third party intellectual property rights.
+ *
+ * THIS DOCUMENT AND THE INFORMATION CONTAINED HEREIN ARE PROVIDED
+ * ON AN "AS IS" BASIS WITHOUT WARRANTY OF ANY KIND AND ERICSSON, IBM,
+ * LOTUS, MATSUSHITA COMMUNICATION INDUSTRIAL CO. LTD, MOTOROLA,
+ * NOKIA, PALM INC., PSION, STARFISH SOFTWARE AND ALL OTHER SYNCML
+ * SPONSORS DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+ * BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
+ * HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
+ * SHALL ERICSSON, IBM, LOTUS, MATSUSHITA COMMUNICATION INDUSTRIAL CO.,
+ * LTD, MOTOROLA, NOKIA, PALM INC., PSION, STARFISH SOFTWARE OR ANY
+ * OTHER SYNCML SPONSOR BE LIABLE TO ANY PARTY FOR ANY LOSS OF
+ * PROFITS, LOSS OF BUSINESS, LOSS OF USE OF DATA, INTERRUPTION OF
+ * BUSINESS, OR FOR DIRECT, INDIRECT, SPECIAL OR EXEMPLARY, INCIDENTAL,
+ * PUNITIVE OR CONSEQUENTIAL DAMAGES OF ANY KIND IN CONNECTION WITH
+ * THIS DOCUMENT OR THE INFORMATION CONTAINED HEREIN, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH LOSS OR DAMAGE.
+ *
+ * The above notice and this paragraph must be included on all copies
+ * of this document that are made.
+ *
+ */
+
+
+
+
+/*************************************************************************
+ * Definitions
+ *************************************************************************/
+
+#include "syncml_tk_prefix_file.h" // %%% luz: needed for precompiled headers in eVC++
+
+#include "define.h"
+/* Include Headers */
+#include "smldef.h"
+#include "smldtd.h"
+#include "smlerr.h"
+
+#include "libmem.h"
+#include "xltdec.h"
+#include "mgr.h"
+
+
+
+/* Used external functions */
+#ifndef __SML_LITE__ /* these API calls are NOT included in the Toolkit lite version */
+ extern Ret_t addInfo(InstanceInfoPtr_t pInfo);
+ extern InstanceInfoPtr_t findInfo(InstanceID_t id);
+ extern Ret_t removeInfo(InstanceID_t id);
+#endif
+Ret_t smlLockReadBuffer(InstanceID_t id, MemPtr_t *pReadPosition, MemSize_t *usedSize);
+Ret_t smlUnlockReadBuffer(InstanceID_t id, MemSize_t processedBytes);
+
+/* Prototypes of exported SyncML API functions */
+extern Ret_t smlProcessData(InstanceID_t id, SmlProcessMode_t mode);
+
+/* Private function prototypes */
+static Ret_t mgrProcessNextCommand(InstanceID_t id, InstanceInfoPtr_t pInstanceInfo);
+static Ret_t mgrProcessStartMessage(InstanceID_t id, InstanceInfoPtr_t pInstanceInfo);
+Ret_t mgrResetWorkspace (InstanceID_t id);
+
+
+
+/*************************************************************************
+ * Exported SyncML API functions
+ *************************************************************************/
+
+
+
+
+/**
+ * Start the parsing of the XML code in the workspace buffer,
+ * dispatches the interpreted command and calls the corresponding callback
+ * functions provided by the application.
+ *
+ * @param id (IN)
+ * The SyncML instance id is used for referencing the
+ * workspace buffer from the XML content is parsed
+ * @param mode (IN)
+ * Mode of processing, Defines, if only the first or next
+ * XML command is parsed or if all commands are processed
+ * subsequently until the end of the entire workspace buffer
+ * is reached. The NEXT_COMMAND flag defines the blocking mode,
+ * the ALL_COMMANDS tag defines the non-blocking mode.
+ * @return Return Code
+ */
+SML_API Ret_t smlProcessData(InstanceID_t id, SmlProcessMode_t mode)
+{
+ /* --- Definitions --- */
+ InstanceInfoPtr_t pInstanceInfo; // state info for the given instanceID
+ Ret_t rc; // Temporary return code saver
+
+
+ #ifdef NOWSM
+ pInstanceInfo = (InstanceInfoPtr_t)id; // ID is the instance info pointer
+ #else
+ /* --- Find that instance --- */
+ #ifdef __SML_LITE__ /* Only ONE instance is supported in the Toolkit lite version */
+ pInstanceInfo = mgrGetInstanceListAnchor();
+ #else
+ pInstanceInfo = (InstanceInfoPtr_t) findInfo(id);
+ #endif
+ #endif
+
+ if (pInstanceInfo==NULL) return SML_ERR_MGR_INVALID_INSTANCE_INFO;
+
+ /* --- Are callback functions defined? --- */
+ if (pInstanceInfo->callbacks==NULL) return SML_ERR_COMMAND_NOT_HANDLED;
+
+ /* --- Is parsing already in progress? --- */
+ if (pInstanceInfo->decoderState==NULL)
+ {
+ /* No! Parse the Message header section first */
+ rc = mgrProcessStartMessage(id, pInstanceInfo);
+
+ if (rc!=SML_ERR_OK) return rc;
+ }
+
+
+ /* --- Parse now the Message body section! --- */
+ do {
+ rc=mgrProcessNextCommand(id, pInstanceInfo);
+ } while (
+ // keep processing while no error occurs,
+ // AND the document end was not reached (decoderState has been invalidated),
+ // AND the ALL_COMMAND mode is used
+ (rc==SML_ERR_OK)
+ &&((pInstanceInfo->decoderState)!=NULL)
+ &&(mode==SML_ALL_COMMANDS)
+ );
+
+ if (rc != SML_ERR_OK) {
+ // abort, unlock the buffer again without changing it's current position
+ smlUnlockReadBuffer(id, (MemSize_t)0);
+ // Reset the decoder module (free the decoding object)
+ xltDecReset(pInstanceInfo->decoderState);
+ // this decoding job is over! reset Instance Info pointer
+ pInstanceInfo->decoderState=NULL;
+ // Reset the Workspace (the remaining unparsed document fragment will be lost)
+ mgrResetWorkspace(id);
+ }
+ else {
+ // %%%luz 2007-09-11: added SML_ERR_CONTINUE to signal processing of message not
+ // yet complete in NEXT_COMMAND mode (is otherwise not very useful)
+ if ((mode==SML_NEXT_COMMAND) && ((pInstanceInfo->decoderState)!=NULL)) {
+ rc = SML_ERR_CONTINUE; // processing not yet complete
+ }
+ }
+
+ return rc;
+}
+
+
+/*************************************************************************
+ * Private Functions
+ *************************************************************************/
+
+
+
+
+/**
+ * Parses the header information at the beginning of an SyncML document.
+ *
+ * @param id (IN)
+ * current InstanceID to pass to callback functions
+ * @param pInstanceInfo (IN/OUT)
+ * state information of the given InstanceID
+ * (decoder state will be changed)
+ * @return Return value of the Parser,\n
+ * SML_ERR_OK if next command was handled successfully
+ */
+static Ret_t mgrProcessStartMessage(InstanceID_t id, InstanceInfoPtr_t pInstanceInfo)
+{
+
+
+ /* --- Definitions --- */
+ Ret_t rc; // Temporary return code saver
+ SmlSyncHdrPtr_t pContent=NULL; // data of the command to process
+ MemPtr_t pCurrentReadPosition ; // current Position from which is read
+ MemPtr_t pBeginPosition; // saves the first position which has been reading
+ MemSize_t usedSize ; // size of used memory to be read
+
+
+ /* --- Get Read Access to the workspace --- */
+ rc = smlLockReadBuffer(id, &pCurrentReadPosition, &usedSize);
+
+ if (rc!=SML_ERR_OK) {
+ // abort, unlock the buffer again without changing it's current position
+ smlUnlockReadBuffer(id, (MemSize_t)0);
+ return rc;
+ }
+
+ // Remember the position we have started reading
+ pBeginPosition=pCurrentReadPosition;
+ #ifdef NOWSM
+ // luz 2008-02-08: also remember the position of the first byte of the message to allow dumping it AFTER parsing the header
+ pInstanceInfo->incomingMsgStart = pBeginPosition;
+ #endif
+
+ /* --- Start new decoding sequence and pass returned decoder status structure to instanceInfo --- */
+ rc = xltDecInit(pInstanceInfo->instanceOptions->encoding,
+ pCurrentReadPosition+usedSize-1, &pCurrentReadPosition,
+ (XltDecoderPtr_t *)&(pInstanceInfo->decoderState), &pContent);
+
+ if (rc!=SML_ERR_OK) {
+ // abort, unlock the buffer again without changing it's current position
+ smlUnlockReadBuffer(id, (MemSize_t)0);
+ // Reset the decoder module (free the decoding object)
+ xltDecReset(pInstanceInfo->decoderState);
+ // this decoding job is over! reset Instance Info pointer
+ pInstanceInfo->decoderState=NULL;
+ // Reset the Workspace (the remaining unparsed document fragment will be lost)
+ mgrResetWorkspace(id);
+ return rc;
+ }
+
+ /* --- End Read Access to the workspace --- */
+ rc = smlUnlockReadBuffer(id, (MemSize_t)pCurrentReadPosition-(MemSize_t)pBeginPosition);
+ if (rc!=SML_ERR_OK) return rc;
+
+ /* --- Perform callback to handle the beginning of a new message --- */
+ if (pInstanceInfo->callbacks->startMessageFunc==NULL) return SML_ERR_COMMAND_NOT_HANDLED;
+ rc=pInstanceInfo->callbacks->startMessageFunc(id, pInstanceInfo->userData, pContent);
+
+ if (rc != SML_ERR_OK)
+ {
+ // abort, unlock the buffer again without changing it's current position
+ smlUnlockReadBuffer(id, (MemSize_t)0);
+ // Reset the decoder module (free the decoding object)
+ xltDecReset(pInstanceInfo->decoderState);
+ // this decoding job is over! reset Instance Info pointer
+ pInstanceInfo->decoderState=NULL;
+ // Reset the Workspace (the remaining unparsed document fragment will be lost)
+ mgrResetWorkspace(id);
+ }
+
+ return rc;
+}
+
+
+
+/**
+ * Parses the next Sync Command in the sync document.
+ *
+ * @param id (IN)
+ * current InstanceID to pass to callback functions
+ * @param pInstanceInfo (IN)
+ * state information of the given InstanceID
+ * @return Return value of the Parser of the called application callback,\n
+ * SML_ERR_OK if next command was handled successfully
+ */
+static Ret_t mgrProcessNextCommand(InstanceID_t id, InstanceInfoPtr_t pInstanceInfo)
+{
+
+ /* --- Definitions --- */
+ Ret_t rc; // Temporary return code saver
+ SmlProtoElement_t cmdType; // ID of the command to process
+ VoidPtr_t pContent=NULL; // data of the command to process
+ MemPtr_t pCurrentReadPosition; // current Position from which is read
+ MemPtr_t pBeginPosition; // saves the first position which has been reading
+ MemSize_t usedSize; // size of used memory to be read
+ Boolean_t final; // flag indicates last message within a package
+
+
+ /* --- Get Read Access to the workspace --- */
+ rc = smlLockReadBuffer(id, &pCurrentReadPosition, &usedSize);
+
+ if (rc!=SML_ERR_OK) {
+ // abort, unlock the buffer again without changing it's current position
+ smlUnlockReadBuffer(id, (MemSize_t)0);
+ return rc;
+ }
+
+ // Remember the position we have started reading
+ pBeginPosition=pCurrentReadPosition;
+
+
+ /* --- Parse next Command --- */
+ rc = xltDecNext(pInstanceInfo->decoderState, pCurrentReadPosition+usedSize, &pCurrentReadPosition, &cmdType, &pContent);
+
+ if (rc!=SML_ERR_OK) {
+ // abort, unlock the buffer again without changing it's current position
+ smlUnlockReadBuffer(id, (MemSize_t)0);
+ // Reset the decoder module (free the decoding object)
+ xltDecReset(pInstanceInfo->decoderState);
+ // this decoding job is over! reset Instance Info pointer
+ pInstanceInfo->decoderState=NULL;
+ // Reset the Workspace (the remaining unparsed document fragment will be lost)
+ mgrResetWorkspace(id);
+ return rc;
+ }
+
+ /* --- End Read Access to the workspace --- */
+ rc = smlUnlockReadBuffer(id, (MemSize_t)pCurrentReadPosition-(MemSize_t)pBeginPosition);
+
+ if (rc!=SML_ERR_OK) {
+ // abort, unlock the buffer again without changing it's current position
+ smlUnlockReadBuffer(id, (MemSize_t)0);
+ return rc;
+ }
+
+ /* --- Did we reach end of synchronization document? --- */
+ if (((XltDecoderPtr_t)(pInstanceInfo->decoderState))->finished!=0) {
+ final = ((XltDecoderPtr_t)(pInstanceInfo->decoderState))->final; // flag is returned to appl. with callback
+ rc=xltDecTerminate(pInstanceInfo->decoderState);
+
+ if (rc!=SML_ERR_OK)
+ {
+ // abort, unlock the buffer again without changing it's current position
+ smlUnlockReadBuffer(id, (MemSize_t)0);
+ // Reset the decoder module (free the decoding object)
+ xltDecReset(pInstanceInfo->decoderState);
+ // this decoding job is over! reset Instance Info pointer
+ pInstanceInfo->decoderState=NULL;
+ // Reset the Workspace (the remaining unparsed document fragment will be lost)
+ mgrResetWorkspace(id);
+ return rc;
+ }
+
+ // this decoding job is over! reset Instance Info pointer
+ // (the decoding object itself has been freed by the decoder)
+ pInstanceInfo->decoderState=NULL;
+
+ // Call the callback for handling an message ending
+ if (pInstanceInfo->callbacks->endMessageFunc==NULL) return SML_ERR_COMMAND_NOT_HANDLED;
+
+ rc=pInstanceInfo->callbacks->endMessageFunc(id, pInstanceInfo->userData, final);
+ return rc;
+ }
+
+ /* --- Dispatch parsed command (and call the applications command handler function)--- */
+ switch (cmdType)
+ {
+ /* Handle ADD Command */
+ case SML_PE_ADD:
+ if (pInstanceInfo->callbacks->addCmdFunc==NULL) return SML_ERR_COMMAND_NOT_HANDLED;
+ return pInstanceInfo->callbacks->addCmdFunc (id, pInstanceInfo->userData, pContent);
+ break;
+
+ /* Handle ALERT Command */
+ case SML_PE_ALERT:
+ if (pInstanceInfo->callbacks->alertCmdFunc==NULL) return SML_ERR_COMMAND_NOT_HANDLED;
+ return pInstanceInfo->callbacks->alertCmdFunc (id, pInstanceInfo->userData, pContent);
+ break;
+
+ /* Handle DELETE Command */
+ case SML_PE_DELETE:
+ if (pInstanceInfo->callbacks->deleteCmdFunc==NULL) return SML_ERR_COMMAND_NOT_HANDLED;
+ return pInstanceInfo->callbacks->deleteCmdFunc (id, pInstanceInfo->userData, pContent);
+ break;
+
+ /* Handle PUT Command */
+ case SML_PE_PUT:
+ if (pInstanceInfo->callbacks->putCmdFunc==NULL) return SML_ERR_COMMAND_NOT_HANDLED;
+ return pInstanceInfo->callbacks->putCmdFunc (id, pInstanceInfo->userData, pContent);
+ break;
+
+ /* Handle GET Command */
+ case SML_PE_GET:
+ if (pInstanceInfo->callbacks->getCmdFunc==NULL) return SML_ERR_COMMAND_NOT_HANDLED;
+ return pInstanceInfo->callbacks->getCmdFunc (id, pInstanceInfo->userData, pContent);
+ break;
+
+ #ifdef MAP_RECEIVE
+ /* Handle MAP Command */
+ case SML_PE_MAP:
+ if (pInstanceInfo->callbacks->mapCmdFunc==NULL) return SML_ERR_COMMAND_NOT_HANDLED;
+ return pInstanceInfo->callbacks->mapCmdFunc (id, pInstanceInfo->userData, pContent);
+ break;
+ #endif
+
+ #ifdef RESULT_RECEIVE
+ /* Handle RESULTS Command */
+ case SML_PE_RESULTS:
+ if (pInstanceInfo->callbacks->resultsCmdFunc==NULL) return SML_ERR_COMMAND_NOT_HANDLED;
+ return pInstanceInfo->callbacks->resultsCmdFunc (id, pInstanceInfo->userData, pContent);
+ break;
+ #endif
+
+ /* Handle STATUS Command */
+ case SML_PE_STATUS:
+ if (pInstanceInfo->callbacks->statusCmdFunc==NULL) return SML_ERR_COMMAND_NOT_HANDLED;
+ return pInstanceInfo->callbacks->statusCmdFunc (id, pInstanceInfo->userData, pContent);
+ break;
+
+ /* Handle START SYNC Command */
+ case SML_PE_SYNC_START:
+ if (pInstanceInfo->callbacks->startSyncFunc==NULL) return SML_ERR_COMMAND_NOT_HANDLED;
+ return pInstanceInfo->callbacks->startSyncFunc (id, pInstanceInfo->userData, pContent);
+ break;
+
+ /* Handle END SYNC Command */
+ case SML_PE_SYNC_END:
+ if (pInstanceInfo->callbacks->endSyncFunc==NULL) return SML_ERR_COMMAND_NOT_HANDLED;
+ return pInstanceInfo->callbacks->endSyncFunc (id, pInstanceInfo->userData);
+ break;
+
+ /* Handle REPLACE Command */
+ case SML_PE_REPLACE:
+ if (pInstanceInfo->callbacks->replaceCmdFunc==NULL) return SML_ERR_COMMAND_NOT_HANDLED;
+ return pInstanceInfo->callbacks->replaceCmdFunc (id, pInstanceInfo->userData, pContent);
+ break;
+
+ /* Handle Final Flag */
+ case SML_PE_FINAL:
+ // if a FINAL Flag appears do nothing
+ return SML_ERR_OK;
+ break;
+
+ #ifdef SEARCH_RECEIVE /* these API calls are NOT included in the Toolkit lite version */
+
+ /* Handle SEARCH Command */
+ case SML_PE_SEARCH:
+ if (pInstanceInfo->callbacks->searchCmdFunc==NULL) return SML_ERR_COMMAND_NOT_HANDLED;
+ return pInstanceInfo->callbacks->searchCmdFunc (id, pInstanceInfo->userData, pContent);
+ break;
+ #endif
+
+ #ifdef SEQUENCE_RECEIVE
+ /* Handle START SEQUENCE Command */
+ case SML_PE_SEQUENCE_START:
+ if (pInstanceInfo->callbacks->startSequenceFunc==NULL) return SML_ERR_COMMAND_NOT_HANDLED;
+ return pInstanceInfo->callbacks->startSequenceFunc (id, pInstanceInfo->userData, pContent);
+ break;
+
+ /* Handle END SEQUENCE Command */
+ case SML_PE_SEQUENCE_END:
+ if (pInstanceInfo->callbacks->endSequenceFunc==NULL) return SML_ERR_COMMAND_NOT_HANDLED;
+ return pInstanceInfo->callbacks->endSequenceFunc (id, pInstanceInfo->userData);
+ break;
+ #endif
+
+ #ifdef ATOMIC_RECEIVE
+
+ /* Handle START ATOMIC Command */
+ case SML_PE_ATOMIC_START:
+ if (pInstanceInfo->callbacks->startAtomicFunc==NULL) return SML_ERR_COMMAND_NOT_HANDLED;
+ return pInstanceInfo->callbacks->startAtomicFunc (id, pInstanceInfo->userData, pContent);
+ break;
+
+ /* Handle END ATOMIC Command */
+ case SML_PE_ATOMIC_END:
+ if (pInstanceInfo->callbacks->endAtomicFunc==NULL) return SML_ERR_COMMAND_NOT_HANDLED;
+ return pInstanceInfo->callbacks->endAtomicFunc (id, pInstanceInfo->userData);
+ break;
+ #endif
+
+ #ifdef COPY_RECEIVE
+
+ /* Handle COPY Command */
+ case SML_PE_COPY:
+ if (pInstanceInfo->callbacks->copyCmdFunc==NULL) return SML_ERR_COMMAND_NOT_HANDLED;
+ return pInstanceInfo->callbacks->copyCmdFunc (id, pInstanceInfo->userData, pContent);
+ break;
+ #endif
+
+ /* Handle MOVE Command */
+ case SML_PE_MOVE:
+ if (pInstanceInfo->callbacks->moveCmdFunc==NULL) return SML_ERR_COMMAND_NOT_HANDLED;
+ return pInstanceInfo->callbacks->moveCmdFunc (id, pInstanceInfo->userData, pContent);
+ break;
+
+ #ifdef EXEC_RECEIVE
+
+ /* Handle EXEC Command */
+ case SML_PE_EXEC:
+ if (pInstanceInfo->callbacks->execCmdFunc==NULL) return SML_ERR_COMMAND_NOT_HANDLED;
+ return pInstanceInfo->callbacks->execCmdFunc (id, pInstanceInfo->userData, pContent);
+ break;
+
+ #endif
+
+ /* Handle ERROR DETECTED */
+ //case SML_PE_ERROR:
+ // if (pInstanceInfo->callbacks->handleErrorFunc==NULL) return SML_ERR_COMMAND_NOT_HANDLED;
+ // return pInstanceInfo->callbacks->handleErrorFunc (id, pInstanceInfo->userData);
+ // break;
+
+ /* --- Invalid Command Element --- */
+ default:
+ return SML_ERR_XLT_INVAL_PROTO_ELEM;
+ break;
+ } // switch
+}
+
+/* eof */
diff --git a/src/syncml_tk/src/sml/mgr/all/mgrinstancelist.c b/src/syncml_tk/src/sml/mgr/all/mgrinstancelist.c
new file mode 100755
index 0000000..dd851a5
--- /dev/null
+++ b/src/syncml_tk/src/sml/mgr/all/mgrinstancelist.c
@@ -0,0 +1,226 @@
+/**
+ * @file
+ * List of SyncML Instances
+ *
+ * @target_system all
+ * @target_os all
+ * @description This module handles an element list of type InstanceInfo. Each
+ * element is identified by the InstanceID. There are functions provided
+ * to add, find and remove InstanceInfo elements.
+ * This file is private to the core module. The InstanceInfo list is
+ * used by the Modules MGR, MGRCmdDispatcher, MGRCmdBuilder
+ * and MGRInstanceMgr.
+ */
+
+/*
+ * Copyright Notice
+ * Copyright (c) Ericsson, IBM, Lotus, Matsushita Communication
+ * Industrial Co., Ltd., Motorola, Nokia, Openwave Systems, Inc.,
+ * Palm, Inc., Psion, Starfish Software, Symbian, Ltd. (2001).
+ * All Rights Reserved.
+ * Implementation of all or part of any Specification may require
+ * licenses under third party intellectual property rights,
+ * including without limitation, patent rights (such a third party
+ * may or may not be a Supporter). The Sponsors of the Specification
+ * are not responsible and shall not be held responsible in any
+ * manner for identifying or failing to identify any or all such
+ * third party intellectual property rights.
+ *
+ * THIS DOCUMENT AND THE INFORMATION CONTAINED HEREIN ARE PROVIDED
+ * ON AN "AS IS" BASIS WITHOUT WARRANTY OF ANY KIND AND ERICSSON, IBM,
+ * LOTUS, MATSUSHITA COMMUNICATION INDUSTRIAL CO. LTD, MOTOROLA,
+ * NOKIA, PALM INC., PSION, STARFISH SOFTWARE AND ALL OTHER SYNCML
+ * SPONSORS DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+ * BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
+ * HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
+ * SHALL ERICSSON, IBM, LOTUS, MATSUSHITA COMMUNICATION INDUSTRIAL CO.,
+ * LTD, MOTOROLA, NOKIA, PALM INC., PSION, STARFISH SOFTWARE OR ANY
+ * OTHER SYNCML SPONSOR BE LIABLE TO ANY PARTY FOR ANY LOSS OF
+ * PROFITS, LOSS OF BUSINESS, LOSS OF USE OF DATA, INTERRUPTION OF
+ * BUSINESS, OR FOR DIRECT, INDIRECT, SPECIAL OR EXEMPLARY, INCIDENTAL,
+ * PUNITIVE OR CONSEQUENTIAL DAMAGES OF ANY KIND IN CONNECTION WITH
+ * THIS DOCUMENT OR THE INFORMATION CONTAINED HEREIN, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH LOSS OR DAMAGE.
+ *
+ * The above notice and this paragraph must be included on all copies
+ * of this document that are made.
+ *
+ */
+
+
+
+
+/*************************************************************************
+ * Definitions
+ *************************************************************************/
+
+#include "syncml_tk_prefix_file.h" // %%% luz: needed for precompiled headers in eVC++
+
+
+/* Include Headers */
+#include <smlerr.h>
+#include "libmem.h"
+#include "liblock.h"
+#include "mgr.h"
+
+#ifndef __SML_LITE__ /* Only ONE instance is supported in the Toolkit lite version */
+
+#ifndef NOWSM /* only need if we are using workspace manager */
+
+
+/* Used external functions */
+SyncMLInfoPtr_t mgrGetSyncMLAnchor(void);
+InstanceInfoPtr_t mgrGetInstanceListAnchor(void);
+void mgrSetInstanceListAnchor(InstanceInfoPtr_t newListAnchor);
+
+
+/* SyncML internal function prototypes */
+Ret_t addInfo(InstanceInfoPtr_t pInfo);
+InstanceInfoPtr_t findInfo(InstanceID_t id);
+Ret_t removeInfo(InstanceID_t id);
+
+
+/* Private function prototypes */
+
+
+
+
+
+
+/*************************************************************************
+ * SyncML internal functions
+ *************************************************************************/
+
+
+/**
+ * Adds a new element to the list
+ *
+ * @param pInfo (IN)
+ * pointer to the structure to be be added to list
+ * @return Return value,\n
+ * SML_ERR_OK if element was added successfully
+ */
+Ret_t addInfo(InstanceInfoPtr_t pInfo)
+{
+
+ if (pInfo!=NULL)
+ {
+ InstanceInfoPtr_t _pTmp;
+
+ LOCKTOOLKIT("addInfo");
+ /* Remember old beginning of the list */
+ _pTmp=mgrGetInstanceListAnchor();
+
+ /* insert element immediately after anchor */
+ mgrSetInstanceListAnchor(pInfo); // anchor of list points now to new info element
+ pInfo->nextInfo=_pTmp; // Next info element is the prior first one.
+ RELEASETOOLKIT("addInfo");
+ return SML_ERR_OK;
+
+ } else { // Invalid InstanceInfo pointer was used (NULL)
+
+ return SML_ERR_MGR_INVALID_INSTANCE_INFO;
+ }
+
+}
+
+
+
+
+
+/**
+ * Searches an element with the given InstanceID in the list
+ *
+ * @param id (IN)
+ * ID of the InstanceInfo structure to be retrieved
+ * @return Pointer to the InstanceInfo structure with the given ID\n
+ * NULL, if no InstanceInfo with the given ID has been found
+ */
+InstanceInfoPtr_t findInfo(InstanceID_t id)
+{
+
+ InstanceInfoPtr_t _pTmp; // A helper pointer
+
+ /* go through the list until end */
+ LOCKTOOLKIT("findInfo");
+ for (_pTmp=mgrGetInstanceListAnchor(); _pTmp!=NULL; _pTmp=_pTmp->nextInfo)
+ {
+ if (_pTmp->id == id) {
+ RELEASETOOLKIT("findInfo");
+ return _pTmp; // STOP, we've found the info, return!
+ }
+ }
+ RELEASETOOLKIT("findInfo");
+ return NULL; // Info was not found, return NULL
+
+}
+
+
+
+
+
+/**
+ * Removes an element with the given InstanceID from the list
+ *
+ * @param id (IN)
+ * ID of the InstanceInfo structure to be removed
+ * @return Return value,\n
+ * SML_ERR_OK if element was removed successfully
+ */
+Ret_t removeInfo(InstanceID_t id)
+{
+
+ InstanceInfoPtr_t _pTmp; // A helper pointer
+ InstanceInfoPtr_t _pRemember; // A helper pointer
+
+
+ LOCKTOOLKIT("removeInfo");
+ /* Remember current anchor */
+ _pRemember=mgrGetInstanceListAnchor();
+
+ /* special check, if list is empty */
+ if (_pRemember==NULL ) {
+ RELEASETOOLKIT("removeInfo");
+ return SML_ERR_MGR_INVALID_INSTANCE_INFO;
+ }
+
+ /* special check, if first element should be removed */
+ if (_pRemember->id == id)
+ {
+ // It's the first element, update anchor!
+ mgrSetInstanceListAnchor(_pRemember->nextInfo);
+ //freeInfo(_pRemember); // Delete structure, free memory
+ RELEASETOOLKIT("removeInfo");
+ return SML_ERR_OK; // return
+ }
+
+
+ /* go through the list until end */
+ for (_pTmp=_pRemember->nextInfo; _pTmp!=NULL; _pTmp=_pTmp->nextInfo)
+ {
+ if (_pTmp->id == id) // STOP, we've found the info
+ {
+ _pRemember->nextInfo=_pTmp->nextInfo;
+ //freeInfo(_pTmp); // Delete structure, free memory
+ RELEASETOOLKIT("removeInfo");
+ return SML_ERR_OK; // return
+
+ } else {
+
+ _pRemember=_pTmp; // update helper pointer
+ }
+ }
+
+ RELEASETOOLKIT("removeInfo");
+ return SML_ERR_MGR_INVALID_INSTANCE_INFO; // Info wasn't found
+
+}
+
+
+
+
+#endif // !defined(NOWSM)
+
+
+#endif
diff --git a/src/syncml_tk/src/sml/mgr/all/mgrinstancemgr.c b/src/syncml_tk/src/sml/mgr/all/mgrinstancemgr.c
new file mode 100755
index 0000000..e8eef7f
--- /dev/null
+++ b/src/syncml_tk/src/sml/mgr/all/mgrinstancemgr.c
@@ -0,0 +1,1106 @@
+/**
+ * @file
+ * Managing SyncML Instances
+ *
+ * @target_system all
+ * @target_os all
+ * @description Core module for managing creation and usage of instances
+ */
+
+
+/*
+ * Copyright Notice
+ * Copyright (c) Ericsson, IBM, Lotus, Matsushita Communication
+ * Industrial Co., Ltd., Motorola, Nokia, Openwave Systems, Inc.,
+ * Palm, Inc., Psion, Starfish Software, Symbian, Ltd. (2001).
+ * All Rights Reserved.
+ * Implementation of all or part of any Specification may require
+ * licenses under third party intellectual property rights,
+ * including without limitation, patent rights (such a third party
+ * may or may not be a Supporter). The Sponsors of the Specification
+ * are not responsible and shall not be held responsible in any
+ * manner for identifying or failing to identify any or all such
+ * third party intellectual property rights.
+ *
+ * THIS DOCUMENT AND THE INFORMATION CONTAINED HEREIN ARE PROVIDED
+ * ON AN "AS IS" BASIS WITHOUT WARRANTY OF ANY KIND AND ERICSSON, IBM,
+ * LOTUS, MATSUSHITA COMMUNICATION INDUSTRIAL CO. LTD, MOTOROLA,
+ * NOKIA, PALM INC., PSION, STARFISH SOFTWARE AND ALL OTHER SYNCML
+ * SPONSORS DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+ * BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
+ * HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
+ * SHALL ERICSSON, IBM, LOTUS, MATSUSHITA COMMUNICATION INDUSTRIAL CO.,
+ * LTD, MOTOROLA, NOKIA, PALM INC., PSION, STARFISH SOFTWARE OR ANY
+ * OTHER SYNCML SPONSOR BE LIABLE TO ANY PARTY FOR ANY LOSS OF
+ * PROFITS, LOSS OF BUSINESS, LOSS OF USE OF DATA, INTERRUPTION OF
+ * BUSINESS, OR FOR DIRECT, INDIRECT, SPECIAL OR EXEMPLARY, INCIDENTAL,
+ * PUNITIVE OR CONSEQUENTIAL DAMAGES OF ANY KIND IN CONNECTION WITH
+ * THIS DOCUMENT OR THE INFORMATION CONTAINED HEREIN, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH LOSS OR DAMAGE.
+ *
+ * The above notice and this paragraph must be included on all copies
+ * of this document that are made.
+ *
+ */
+
+
+/*************************************************************************
+ * Definitions
+ *************************************************************************/
+
+#include "syncml_tk_prefix_file.h" // %%% luz: needed for precompiled headers in eVC++
+
+/* Include Headers */
+#include <smldef.h>
+#include <sml.h>
+#include <smlerr.h>
+#include "libmem.h"
+#include "libstr.h"
+#include "liblock.h"
+#include "wsm.h"
+#include "mgr.h"
+
+
+
+/* Used external functions */
+#ifndef NOWSM
+ #ifndef __SML_LITE__ /* Only ONE instance is supported in the Toolkit lite version */
+ extern Ret_t addInfo(InstanceInfoPtr_t pInfo);
+ extern InstanceInfoPtr_t findInfo(InstanceID_t id);
+ extern Ret_t removeInfo(InstanceID_t id);
+ #endif
+ SyncMLInfoPtr_t mgrGetSyncMLAnchor(void);
+#endif
+
+/* Prototypes of exported SyncML API functions */
+SML_API Ret_t smlInitInstance(SmlCallbacksCPtr_t callbacks, SmlInstanceOptionsPtr_t pOptions, VoidPtr_t pUserData, InstanceID_t *pInstanceID);
+SML_API Ret_t smlTerminateInstance (InstanceID_t id);
+SML_API Ret_t smlLockReadBuffer(InstanceID_t id, MemPtr_t *pReadPosition, MemSize_t *usedSize);
+SML_API Ret_t smlUnlockReadBuffer(InstanceID_t id, MemSize_t processedBytes);
+#ifdef NOWSM
+SML_API Ret_t smlSetMaxOutgoingSize(InstanceID_t id, MemSize_t maxOutgoingSize);
+SML_API Ret_t smlSetOutgoingBegin(InstanceID_t id);
+#endif
+SML_API Ret_t smlLockWriteBuffer(InstanceID_t id, MemPtr_t *pWritePosition, MemSize_t *freeSize);
+SML_API Ret_t smlUnlockWriteBuffer(InstanceID_t id, MemSize_t writtenBytes);
+SML_API Ret_t smlSetCallbacks (InstanceID_t id, SmlCallbacksCPtr_t pCallbacks);
+SML_API Ret_t smlSetUserData (InstanceID_t id, VoidPtr_t pUserData);
+// added by luz %%%:
+SML_API Ret_t smlGetUserData(InstanceID_t id, VoidPtr_t *ppUserData);
+SML_API Ret_t smlGetEncoding(InstanceID_t id, SmlEncoding_t *pEncoding);
+#ifndef __SML_LITE__ /* these API calls are NOT included in the Toolkit lite version */
+ SML_API Ret_t smlSetEncoding (InstanceID_t id, SmlEncoding_t encoding);
+#endif
+
+
+
+/* Private function prototypes */
+Ret_t freeInstanceOptions (InstanceInfoPtr_t pInstanceInfo);
+static Ret_t freeInstanceInfo (InstanceInfoPtr_t pInfo);
+Ret_t mgrResetWorkspace (InstanceID_t id);
+Ret_t setInstanceOptions (InstanceID_t id, SmlInstanceOptionsPtr_t pOptions);
+
+
+/*************************************************************************
+ * Public SyncML API Functions
+ *************************************************************************/
+
+
+/**
+ * Creates a SyncML instance and assigns a corresponding workspace buffer in
+ * which XML documents are assembled or parsed.
+ * All callback functions implemented by a particular application are defined.
+ * Instance specific options can be passed. This function has to be called
+ * before the first synchronization tasks can be performed. A reference valid
+ * for a SyncML instance is returned.
+ * An instance is active when processing a synchronization request
+ * otherwise it is idle. An instance is terminated when smlTerminateInstance
+ * is called.
+ *
+ * @param pCallbacks (IN)
+ * A structure holding references to the callback functions
+ * implemented by the application
+ * @param pOptions (IN)
+ * Option settings of a particular SyncML instance
+ * @param pUserData (IN)
+ * UserData is a pointer to a void structure the application
+ * can pass into the SyncML Toolkit instance info. It will
+ * be returned to the application with every called callback
+ * function call!\n
+ * NOTE: This is only a pointer, the memory object itself
+ * remains within the responsibility of the calling application.
+ * The memory object will not be copied, moved or freed by the Toolkit.
+ * @param pInstanceID (OUT)
+ * Instance ID assigned to the initialized instance
+ * @return Error Code
+ */
+SML_API Ret_t smlInitInstance(SmlCallbacksCPtr_t pCallbacks, SmlInstanceOptionsPtr_t pOptions, VoidPtr_t pUserData, InstanceID_t *pInstanceID)
+{
+
+ /* --- Definitions --- */
+ InstanceInfoPtr_t pInstanceInfo;
+ Ret_t rc;
+
+
+ #ifndef NOWSM
+ /* --- Check pOptions, which have been passed by the application --- */
+ if (!pOptions || !pOptions->workspaceName)
+ return SML_ERR_WRONG_USAGE;
+
+ #ifdef __SML_LITE__ /* Only ONE instance is supported in the Toolkit lite version */
+ /* if ONE instance is already initialized */
+ if (mgrGetInstanceListAnchor()!=NULL)
+ return SML_ERR_WRONG_USAGE;
+ #endif
+
+ /* --- check wether we already know about this instance --- */
+ #ifdef __SML_LITE__ /* Only ONE instance is supported in the Toolkit lite version */
+ pInstanceInfo = mgrGetInstanceListAnchor();
+ #else
+ pInstanceInfo = (InstanceInfoPtr_t) findInfo(*pInstanceID);
+ #endif
+
+ /* --- bail outh when we already have a instance with that id --- */
+ if (pInstanceInfo != NULL) return SML_ERR_WRONG_USAGE;
+
+
+ /* --- Create a workspace for this instance --- */
+ LOCKTOOLKIT("smlInitInstance");
+ if ((rc = wsmCreate(pOptions->workspaceName, pOptions->workspaceSize, pInstanceID)) != SML_ERR_OK) {
+ RELEASETOOLKIT("smlInitInstance after wsmCreate failure");
+ return rc;
+ }
+ RELEASETOOLKIT("smlInitInstance");
+ #else // NOWSM
+ /* --- Check pOptions, which have been passed by the application --- */
+ if (!pOptions || !pOptions->workspaceSize)
+ return SML_ERR_WRONG_USAGE;
+ // ok so far
+ rc=SML_ERR_OK;
+ #endif
+
+ /* --- Create an instance info memory object --- */
+ pInstanceInfo = (InstanceInfoPtr_t)smlLibMalloc((MemSize_t)sizeof(InstanceInfo_t));
+ if (pInstanceInfo==NULL) {
+ #ifndef NOWSM
+ wsmDestroy(pOptions->workspaceName);
+ return SML_ERR_NOT_ENOUGH_SPACE;
+ #endif
+ }
+ #ifdef NOWSM
+ else {
+ // instance info created, return pointer as instanceID
+ *pInstanceID = (InstanceID_t)pInstanceInfo;
+ }
+ #endif
+
+ smlLibMemset(pInstanceInfo,0,(MemSize_t)sizeof(InstanceInfo_t));
+
+
+
+ /* --- Set mandatory instance infos for this instance to defaults --- */
+ pInstanceInfo->status=MGR_IDLE;
+ pInstanceInfo->encoderState=NULL; // no encoding in progress, currently not used
+ pInstanceInfo->decoderState=NULL; // no decoding in progress, currently not used
+ #ifndef NOWSM
+ pInstanceInfo->id=*pInstanceID;
+ pInstanceInfo->workspaceState=NULL; // to do: some workspace status info
+ pInstanceInfo->nextInfo=NULL;
+ #else
+ // create a instance buffer
+ pInstanceInfo->instanceBufSiz=pOptions->workspaceSize; // get requested size for the buffer
+ pInstanceInfo->maxOutgoingSize=pOptions->maxOutgoingSize; // set max outgoing message size
+ pInstanceInfo->instanceBuffer=smlLibMalloc(pInstanceInfo->instanceBufSiz);
+ if (pInstanceInfo->instanceBuffer==NULL)
+ return SML_ERR_NOT_ENOUGH_SPACE;
+ // init buffer pointers
+ pInstanceInfo->readPointer=pInstanceInfo->instanceBuffer;
+ pInstanceInfo->writePointer=pInstanceInfo->instanceBuffer;
+ pInstanceInfo->readLocked=0;
+ pInstanceInfo->writeLocked=0;
+ pInstanceInfo->outgoingMsgStart=NULL;
+ pInstanceInfo->incomingMsgStart=NULL;
+ #endif
+
+
+ #ifndef NOWSM
+ /* --- Add instance infos memory object to the instance info list --- */
+ #ifdef __SML_LITE__ /* Only ONE instance is supported in the Toolkit lite version */
+ mgrSetInstanceListAnchor(pInstanceInfo);
+ #else
+ rc = addInfo( pInstanceInfo );
+ if (rc!=SML_ERR_OK) return rc;
+ #endif
+ #endif
+
+
+ /* --- Set the values of instance Infos as defined by the calling application ---*/
+
+ /* Set user data pointer */
+ pInstanceInfo->userData=pUserData;
+ /* Set callback functions implemented by applications */
+ if (smlSetCallbacks(*pInstanceID, pCallbacks) != SML_ERR_OK) {
+ #ifndef NOWSM
+ wsmDestroy(pOptions->workspaceName);
+ #endif
+ return rc;
+ }
+
+ // luz: %%% this was called twice, probably this is a bug, so I disabled the second call
+ //smlSetCallbacks(*pInstanceID, pCallbacks);
+
+ /* Set other application defined options for that instance */
+ if (setInstanceOptions (*pInstanceID, pOptions) != SML_ERR_OK) {
+ #ifndef NOWSM
+ wsmDestroy(pOptions->workspaceName);
+ #endif
+ return rc;
+ }
+
+ return SML_ERR_OK;
+
+}
+
+
+
+/**
+ * Terminates a SyncML instance. The instance info is removed from the instances
+ * list. Allmemory allocated for the workspace and the options variables is
+ * freed.
+ *
+ * @param id (IN)
+ * ID of the instance to be terminated
+ * @return Error Code
+ */
+SML_API Ret_t smlTerminateInstance (InstanceID_t id)
+{
+
+ /* --- Definitions --- */
+ InstanceInfoPtr_t pInstanceInfo;
+
+ #ifdef NOWSM
+ pInstanceInfo = (InstanceInfoPtr_t)id; // ID is the instance info pointer
+ #else
+ Ret_t rc;
+
+ /* --- Find that instance --- */
+ #ifdef __SML_LITE__ /* Only ONE instance is supported in the Toolkit lite version */
+ pInstanceInfo = mgrGetInstanceListAnchor();
+ #else
+ pInstanceInfo = (InstanceInfoPtr_t) findInfo(id);
+ #endif
+ #endif
+
+ if (pInstanceInfo==NULL) return SML_ERR_MGR_INVALID_INSTANCE_INFO;
+
+ #ifndef NOWSM
+ /* --- Close the workspace --- */
+ if (pInstanceInfo->instanceOptions != NULL) {
+ LOCKTOOLKIT("smlTerminateInstance");
+ rc = wsmDestroy(pInstanceInfo->instanceOptions->workspaceName);
+ RELEASETOOLKIT("smlTerminateInstance");
+ if (rc!=SML_ERR_OK) {
+ // freeInstanceInfo(pInstanceInfo);
+ return rc;
+ }
+ }
+
+ /* --- Delete instance info and options --- */
+ #ifdef __SML_LITE__ /* Only ONE instance is supported in the Toolkit lite version */
+ mgrSetInstanceListAnchor(NULL);
+ #else
+ removeInfo(id);
+ #endif
+ #endif
+
+ freeInstanceInfo (pInstanceInfo);
+
+ return SML_ERR_OK;
+}
+
+
+
+/**
+ * Sets new callback functions to an instance
+ *
+ * @param id (IN)
+ * ID of the Instance
+ * @param pCallbacks (IN)
+ * A structure holding references to the callback functions
+ * implemented by the application
+ * @return Return value,\n
+ * SML_ERR_OK if successful
+ */
+SML_API Ret_t smlSetCallbacks(InstanceID_t id, SmlCallbacksCPtr_t pCallbacks)
+{
+
+ /* --- Definitions --- */
+ InstanceInfoPtr_t pInstanceInfo;
+ SmlCallbacksPtr_t pCallbacksCopy;
+
+ /* --- Check pCallbacks, which have been passed by the application --- */
+ if (!pCallbacks)
+ return SML_ERR_WRONG_USAGE;
+
+
+ #ifdef NOWSM
+ pInstanceInfo = (InstanceInfoPtr_t)id; // ID is the instance info pointer
+ #else
+ /* --- Find that instance --- */
+ #ifdef __SML_LITE__ /* Only ONE instance is supported in the Toolkit lite version */
+ pInstanceInfo = mgrGetInstanceListAnchor();
+ #else
+ pInstanceInfo = (InstanceInfoPtr_t) findInfo(id);
+ #endif
+
+ if (pInstanceInfo==NULL) return SML_ERR_MGR_INVALID_INSTANCE_INFO;
+ #endif
+
+ /* --- free old callback structure ---*/
+ smlLibFree(pInstanceInfo->callbacks);
+
+
+ /* --- Use a copy of pCallbacksCopy --- */
+ pCallbacksCopy = (SmlCallbacksPtr_t)smlLibMalloc((MemSize_t)sizeof(SmlCallbacks_t));
+ if (pCallbacksCopy==NULL) return SML_ERR_NOT_ENOUGH_SPACE;
+ smlLibMemcpy(pCallbacksCopy,pCallbacks,(MemSize_t)sizeof(SmlCallbacks_t));
+
+
+ /* --- set new Callbacks --- */
+ pInstanceInfo->callbacks = pCallbacksCopy;
+
+ return SML_ERR_OK;
+}
+
+
+
+/**
+ * Sets a new Pointer to application specific user data,
+ * which is passed to all invoked callback functions
+ *
+ * @param id (IN)
+ * ID of the Instance
+ * @param pUserData (IN)
+ * UserData is a pointer to a void structure the application
+ * can pass into the SyncML Toolkit instance info. It will
+ * be returned to the application with every called callback
+ * function call!\n
+ * NOTE: This is only a pointer, the memory object itself
+ * remains within the responsibility of the calling application.
+ * The memory object will not be copied, moved or freed by the Toolkit.
+ * @return Return value,\n
+ * SML_ERR_OK if successful
+ */
+SML_API Ret_t smlSetUserData(InstanceID_t id, VoidPtr_t pUserData)
+{
+
+ /* --- Definitions --- */
+ InstanceInfoPtr_t pInstanceInfo;
+
+
+ #ifdef NOWSM
+ pInstanceInfo = (InstanceInfoPtr_t)id; // ID is the instance info pointer
+ #else
+ /* --- Find that instance --- */
+ #ifdef __SML_LITE__ /* Only ONE instance is supported in the Toolkit lite version */
+ pInstanceInfo = mgrGetInstanceListAnchor();
+ #else
+ pInstanceInfo = (InstanceInfoPtr_t) findInfo(id);
+ #endif
+ #endif
+
+ if (pInstanceInfo==NULL) return SML_ERR_MGR_INVALID_INSTANCE_INFO;
+
+
+ /* --- set new user data pointer ---*/
+ pInstanceInfo->userData=pUserData;
+
+ return SML_ERR_OK;
+}
+
+
+/**
+ * Returns Pointer to application specific user data,
+ * which is passed to all invoked callback functions
+ *
+ * @param id (IN)
+ * ID of the Instance
+ * @param ppUserData (OUT)
+ * Receives current Userdata pointer
+ * @return Return value,\n
+ * SML_ERR_OK if successful
+ * @note (added by luz %%%)
+ */
+SML_API Ret_t smlGetUserData(InstanceID_t id, VoidPtr_t *ppUserData)
+{
+
+ /* --- Definitions --- */
+ InstanceInfoPtr_t pInstanceInfo;
+
+ #ifdef NOWSM
+ pInstanceInfo = (InstanceInfoPtr_t)id; // ID is the instance info pointer
+ #else
+ /* --- Find that instance --- */
+ #ifdef __SML_LITE__ /* Only ONE instance is supported in the Toolkit lite version */
+ pInstanceInfo = mgrGetInstanceListAnchor();
+ #else
+ pInstanceInfo = (InstanceInfoPtr_t) findInfo(id);
+ #endif
+ #endif
+
+ if (pInstanceInfo==NULL) return SML_ERR_MGR_INVALID_INSTANCE_INFO;
+
+
+ /* --- get userdata pointer ---*/
+ *ppUserData = pInstanceInfo->userData;
+
+ return SML_ERR_OK;
+} // smlGetUserData
+
+
+/**
+ * Returns Currently set encoding type
+ *
+ * @param id (IN)
+ * ID of the Instance
+ * @param pEncoding (OUT)
+ * Receives current encoding
+ * @return Return value,\n
+ * SML_ERR_OK if successful
+ * @note (added by luz %%%)
+ */
+SML_API Ret_t smlGetEncoding(InstanceID_t id, SmlEncoding_t *pEncoding)
+{
+
+ /* --- Definitions --- */
+ InstanceInfoPtr_t pInstanceInfo;
+
+ #ifdef NOWSM
+ pInstanceInfo = (InstanceInfoPtr_t)id; // ID is the instance info pointer
+ #else
+ /* --- Find that instance --- */
+ #ifdef __SML_LITE__ /* Only ONE instance is supported in the Toolkit lite version */
+ pInstanceInfo = mgrGetInstanceListAnchor();
+ #else
+ pInstanceInfo = (InstanceInfoPtr_t) findInfo(id);
+ #endif
+ #endif
+
+ if (pInstanceInfo==NULL) return SML_ERR_MGR_INVALID_INSTANCE_INFO;
+
+ /* --- get encoding ---*/
+ *pEncoding = pInstanceInfo->instanceOptions->encoding;
+
+ return SML_ERR_OK;
+} // smlGetEncoding
+
+
+/**
+ * Sets new encoding type for this Instance
+ *
+ * @param id (IN)
+ * ID of the Instance
+ * @param encoding (IN)
+ * Type of Encoding to be used within this Instance
+ * @return Return value,\n
+ * SML_ERR_OK if successful
+ */
+#ifndef __SML_LITE__ /* these API calls are NOT included in the Toolkit lite version */
+SML_API Ret_t smlSetEncoding(InstanceID_t id, SmlEncoding_t encoding)
+{
+
+ /* --- Definitions --- */
+ InstanceInfoPtr_t pInstanceInfo;
+
+ /* --- Check pCallbacks, which have been passed by the application --- */
+ if (encoding==SML_UNDEF)
+ return SML_ERR_WRONG_USAGE;
+
+
+ #ifdef NOWSM
+ pInstanceInfo = (InstanceInfoPtr_t)id; // ID is the instance info pointer
+ #else
+ /* --- Find that instance --- */
+ #ifdef __SML_LITE__ /* Only ONE instance is supported in the Toolkit lite version */
+ pInstanceInfo = mgrGetInstanceListAnchor();
+ #else
+ pInstanceInfo = (InstanceInfoPtr_t) findInfo(id);
+ #endif
+ #endif
+
+ if (pInstanceInfo==NULL) return SML_ERR_MGR_INVALID_INSTANCE_INFO;
+
+
+ /* --- free old callback structure ---*/
+ pInstanceInfo->instanceOptions->encoding = encoding;
+
+ return SML_ERR_OK;
+}
+#endif
+
+
+
+
+/**
+ * Locks the workspace buffer, which is assigned to the given instance
+ * for reading. After this function is called, the application has
+ * access to the workspace buffer, beginning at the address pReadPosition which
+ * is returned by this function. SyncML will not change the workspace
+ * buffer until smlUnlockReadBuffer is called.
+ * pReadPosition returns a pointer to a valid position in the SyncML workspace
+ * buffer. The pointer can be used by the application for copying outgoing
+ * synchronization data from the buffer into some transport layer. usedSize
+ * retrieves the size of synchronization data currently stored in the
+ * workspace buffer beginning from the address to which pReadPosition points to.
+ * This information is needed by the application when copying XML code out
+ * of the buffer (while sending synchronization data)
+ *
+ * @param id (IN)
+ * ID of the Instance
+ * @param pReadPosition (OUT)
+ * Workspace Pointer from which data can be read
+ * @param usedSize (OUT)
+ * Size of used data in workspace which may be read
+ * @return Return value,\n
+ * SML_ERR_OK if successful
+ */
+SML_API Ret_t smlLockReadBuffer(InstanceID_t id, MemPtr_t *pReadPosition, MemSize_t *usedSize)
+{
+ #ifdef NOWSM
+ InstanceInfoPtr_t pInstanceInfo;
+
+ pInstanceInfo = (InstanceInfoPtr_t)id; // ID is the instance info pointer
+ if (pInstanceInfo==NULL) return SML_ERR_MGR_INVALID_INSTANCE_INFO;
+ // must not be already locked here
+ if (pInstanceInfo->readLocked)
+ return SML_ERR_WRONG_USAGE;
+ // everything that is already written can also be read
+ *pReadPosition = pInstanceInfo->readPointer;
+ // used portion is what is between read and write pointers
+ *usedSize = pInstanceInfo->writePointer-pInstanceInfo->readPointer;
+ // lock
+ pInstanceInfo->readLocked=1;
+ #else
+ Ret_t rc;
+
+ LOCKTOOLKIT("smlLockReadBuffer");
+ /* --- Lock Workspace exclusively for reading and get a "Read" pointer --- */
+ rc = wsmLockH(id, SML_FIRST_DATA_ITEM, pReadPosition);
+ RELEASETOOLKIT("smlLockReadBuffer");
+ if (rc!=SML_ERR_OK) return rc;
+
+ /* --- Check, how much data has to be read ---*/
+ LOCKTOOLKIT("smlLockReadBuffer");
+ rc = wsmGetUsedSize(id,usedSize);
+ RELEASETOOLKIT("smlLockReadBuffer");
+ if (rc!=SML_ERR_OK) return rc;
+ #endif
+
+ return SML_ERR_OK;
+}
+
+
+
+
+/**
+ * End the read access of the application to the workspace buffer.
+ * SyncML is now owner of the buffer again and is able to manipulate its contents.
+ * processedBytes passes the number of bytes, which the application has
+ * successfully read and processed (e.g. when the application has copied
+ * outgoing synchronization data from the workspace into a communication module).
+ * SyncML removes the given number of bytes from the workspace!
+ *
+ * @param id (IN)
+ * ID of the Instance
+ * @param processedBytes (IN)
+ * Actually read and processed bytes
+ * @return Return value,\n
+ * SML_ERR_OK if successful
+ */
+SML_API Ret_t smlUnlockReadBuffer(InstanceID_t id, MemSize_t processedBytes)
+{
+ #ifdef NOWSM
+ InstanceInfoPtr_t pInstanceInfo;
+
+ pInstanceInfo = (InstanceInfoPtr_t)id; // ID is the instance info pointer
+ if (pInstanceInfo==NULL) return SML_ERR_MGR_INVALID_INSTANCE_INFO;
+ // must be already locked here
+ if (!pInstanceInfo->readLocked)
+ return SML_ERR_WRONG_USAGE;
+ // advance read pointer by number of bytes processed
+ if (pInstanceInfo->readPointer+processedBytes>pInstanceInfo->writePointer)
+ return SML_ERR_WRONG_USAGE; // too many bytes processed
+ // update read pointer
+ pInstanceInfo->readPointer+=processedBytes;
+ /* %%% moved from here to smlLockWriteBuffer to leave buffer intact until we actually
+ need to write more data (important for re-sending retries)
+ // auto-reset pointers if we have now read everything
+ if (pInstanceInfo->readPointer == pInstanceInfo->writePointer) {
+ // clear the buffer
+ mgrResetWorkspace(pInstanceInfo);
+ }
+ */
+ // unlock
+ pInstanceInfo->readLocked=0;
+ #else
+ Ret_t rc;
+
+ /* --- Pass the number of bytes which have been read --- */
+ LOCKTOOLKIT("smlUnlockReadBuffer");
+ rc = wsmProcessedBytes (id,processedBytes);
+ RELEASETOOLKIT("smlUnlockReadBuffer");
+ if (rc!=SML_ERR_OK) return rc;
+
+ /* --- Unlock Workspace --- */
+ LOCKTOOLKIT("smlUnlockReadBuffer");
+ rc = wsmUnlockH(id);
+ RELEASETOOLKIT("smlUnlockReadBuffer");
+ if (rc!=SML_ERR_OK) return rc;
+ #endif
+
+ return SML_ERR_OK;
+}
+
+
+#ifdef NOWSM
+
+/**
+ * Set max outgoing message size
+ *
+ * @param id (IN)
+ * ID of the Instance
+ * @param maxOutgoingSize (IN)
+ * maximum size of outgoing message
+ * (0=no limit except buffer size)
+ * @return Return value,\n
+ * SML_ERR_OK if successful
+ */
+SML_API Ret_t smlSetMaxOutgoingSize(InstanceID_t id, MemSize_t maxOutgoingSize)
+{
+ InstanceInfoPtr_t pInstanceInfo;
+
+ pInstanceInfo = (InstanceInfoPtr_t)id; // ID is the instance info pointer
+ if (pInstanceInfo==NULL) return SML_ERR_MGR_INVALID_INSTANCE_INFO;
+
+ // set max outgoing message size
+ pInstanceInfo->maxOutgoingSize = maxOutgoingSize;
+
+ return SML_ERR_OK;
+}
+
+
+/**
+ * Marks the current write pointer position as beginning of a new outgoing
+ * message. This is used to track outgoing message size while writing it
+ *
+ * @param id (IN)
+ * ID of the Instance
+ * @return Return value,\n
+ * SML_ERR_OK if successful
+ */
+SML_API Ret_t smlSetOutgoingBegin(InstanceID_t id)
+{
+ InstanceInfoPtr_t pInstanceInfo;
+
+ pInstanceInfo = (InstanceInfoPtr_t)id; // ID is the instance info pointer
+ if (pInstanceInfo==NULL) return SML_ERR_MGR_INVALID_INSTANCE_INFO;
+
+ // remember current write pointer
+ pInstanceInfo->outgoingMsgStart=pInstanceInfo->writePointer;
+
+ return SML_ERR_OK;
+}
+
+
+/**
+ * resets the read pointer to the beginning of the outgoing message
+ * (must be set previously using smlSetOutgoingBegin())
+ *
+ * @param id (IN)
+ * ID of the Instance
+ * @return Return value,\n
+ * SML_ERR_OK if successful
+ */
+SML_API Ret_t smlReadOutgoingAgain(InstanceID_t id)
+{
+ InstanceInfoPtr_t pInstanceInfo;
+
+ pInstanceInfo = (InstanceInfoPtr_t)id; // ID is the instance info pointer
+ if (pInstanceInfo==NULL) return SML_ERR_MGR_INVALID_INSTANCE_INFO;
+
+ // reset read pointer back to beginning of outgoing message (can be used for message retries)
+ if (pInstanceInfo->outgoingMsgStart==NULL) return SML_ERR_WRONG_USAGE; // no outgoing start set
+ pInstanceInfo->readPointer=pInstanceInfo->outgoingMsgStart;
+
+ return SML_ERR_OK;
+}
+
+
+/**
+ * gets pointer to message in buffer for dumping purposes
+ * @note This is only valid for incoming messages BEFORE outgoing message writing has started
+ * into the same instance buffer!
+ *
+ * @param id (IN)
+ * ID of the Instance
+ * @param outgoing (IN)
+ * if set, outgoing message is returned, incoming otherwise
+ * @param message (OUT)
+ * pointer to message
+ * @param msgsize (OUT)
+ * message size
+ * @return Return value,\n
+ * SML_ERR_OK if successful
+ */
+SML_API Ret_t smlPeekMessageBuffer(InstanceID_t id, Boolean_t outgoing, MemPtr_t *message, MemSize_t *msgsize)
+{
+ InstanceInfoPtr_t pInstanceInfo;
+
+ pInstanceInfo = (InstanceInfoPtr_t)id; // ID is the instance info pointer
+ if (pInstanceInfo==NULL) return SML_ERR_MGR_INVALID_INSTANCE_INFO;
+
+ if (outgoing) {
+ if (pInstanceInfo->outgoingMsgStart==NULL) return SML_ERR_WRONG_USAGE; // no outgoing message yet
+ *message = pInstanceInfo->outgoingMsgStart;
+ }
+ else {
+ if (pInstanceInfo->incomingMsgStart) {
+ // incoming message start already detected, return it
+ *message = pInstanceInfo->incomingMsgStart;
+ }
+ else {
+ // apparently not started analyzing, assume read pointer as beginning of message
+ if (pInstanceInfo->readPointer==0)
+ return SML_ERR_WRONG_USAGE; // no outgoing message yet
+ *message = pInstanceInfo->readPointer;
+ }
+ }
+ if (*message>=pInstanceInfo->writePointer) return SML_ERR_WRONG_USAGE; // invalid pointer positions
+ // size is space between start and write pointer
+ *msgsize = pInstanceInfo->writePointer-*message;
+ return SML_ERR_OK;
+}
+
+
+#endif
+
+/**
+ * Locks the workspace buffer, which is assigned to the given
+ * instance for writing. After this function is called, the
+ * application has access to the workspace buffer, beginning
+ * at the address pWritePosition which is returned by this
+ * function. SyncML will not change the workspace buffer until
+ * smlUnlockWriteBuffer is called.
+ * pWritePosition returns a pointer to a valid position in the
+ * SyncML workspace buffer. The pointer can be used by the application
+ * for copying incoming synchronization data from some transport
+ * layer into the buffer. freeSize retrieves the maximum usable
+ * size of the workspace buffer beginning from the address to
+ * which pWritePosition points to. This information is needed by
+ * the application when copying XML code into the buffer (while
+ * receiving synchronization data)
+ *
+ * @param id (IN)
+ * ID of the Instance
+ * @param pWritePosition (OUT)
+ * Workspace Pointer to which data can be written
+ * @param freeSize (OUT)
+ * Max free Size of available space for data
+ * @return Return value,\n
+ * SML_ERR_OK if successful
+ */
+SML_API Ret_t smlLockWriteBuffer(InstanceID_t id, MemPtr_t *pWritePosition, MemSize_t *freeSize)
+{
+ #ifdef NOWSM
+ InstanceInfoPtr_t pInstanceInfo;
+ Boolean_t ogs;
+
+ pInstanceInfo = (InstanceInfoPtr_t)id; // ID is the instance info pointer
+ if (pInstanceInfo==NULL) return SML_ERR_MGR_INVALID_INSTANCE_INFO;
+ // must not be already locked here
+ if (pInstanceInfo->writeLocked)
+ return SML_ERR_WRONG_USAGE;
+ // auto-reset pointers if buffer is empty by now
+ if (pInstanceInfo->readPointer == pInstanceInfo->writePointer) {
+ // remember if we were at outgoing message start point
+ ogs = pInstanceInfo->outgoingMsgStart == pInstanceInfo->writePointer;
+ // clear the buffer
+ mgrResetWorkspace(pInstanceInfo);
+ // restore outgoingMsgStart (to beginning of buffer now!) if it was set before
+ if (ogs) pInstanceInfo->outgoingMsgStart=pInstanceInfo->writePointer;
+ }
+
+ // return current write pointer
+ *pWritePosition = pInstanceInfo->writePointer;
+ // free portion is either determined by actual room in buffer, or maximum outgoing size if set
+ if (
+ pInstanceInfo->maxOutgoingSize &&
+ pInstanceInfo->outgoingMsgStart &&
+ pInstanceInfo->outgoingMsgStart<pInstanceInfo->writePointer
+ ) {
+ // calculate what is allowed according to maxOutgoingSize
+ *freeSize =
+ (pInstanceInfo->maxOutgoingSize) - // maximum outgoing size
+ (pInstanceInfo->writePointer-pInstanceInfo->outgoingMsgStart); // size of outgoing message so far
+ if (pInstanceInfo->writePointer+*freeSize > pInstanceInfo->instanceBuffer+pInstanceInfo->instanceBufSiz) {
+ // actual space in buffer is smaller
+ *freeSize =
+ (pInstanceInfo->instanceBuffer+pInstanceInfo->instanceBufSiz) - // end of buffer
+ pInstanceInfo->writePointer; // current write position
+ }
+ }
+ else {
+ // simply return available size in buffer
+ *freeSize =
+ (pInstanceInfo->instanceBuffer+pInstanceInfo->instanceBufSiz) - // end of buffer
+ pInstanceInfo->writePointer; // current write position
+ }
+ // lock
+ pInstanceInfo->writeLocked=1;
+ #else
+ Ret_t rc;
+
+ /* --- Lock Workspace exclusively for writing and get a "Write" pointer --- */
+ LOCKTOOLKIT("smlLockWriteBuffer");
+ rc = wsmLockH(id, SML_FIRST_FREE_ITEM, pWritePosition);
+ RELEASETOOLKIT("smlLockWriteBuffer");
+ if (rc!=SML_ERR_OK) return rc;
+
+ /* --- Check, how much free space is available for writing --- */
+ LOCKTOOLKIT("smlLockWriteBuffer");
+ rc = wsmGetFreeSize(id, freeSize);
+ RELEASETOOLKIT("smlLockWriteBuffer");
+ if (rc!=SML_ERR_OK) return rc;
+ #endif
+
+ return SML_ERR_OK;
+}
+
+
+
+
+/**
+ *
+ * End the write access of the application to the workspace buffer.
+ * SyncML is now owner of the buffer again and is able to manipulate its
+ * contents. writtenBytes passes the number of bytes which have been
+ * written into the workspace buffer (e.g. when the application has copied
+ * incoming synchronization data from a communication module into the
+ * workspace). This information is needed by SyncML when processing received
+ * synchronization data.
+ *
+ * @param id (IN)
+ * ID of the Instance
+ * @param writtenBytes (IN)
+ * Actually written bytes
+ * @return Return value,\n
+ * SML_ERR_OK if successful
+ */
+SML_API Ret_t smlUnlockWriteBuffer(InstanceID_t id, MemSize_t writtenBytes)
+{
+ #ifdef NOWSM
+ InstanceInfoPtr_t pInstanceInfo;
+
+ pInstanceInfo = (InstanceInfoPtr_t)id; // ID is the instance info pointer
+ if (pInstanceInfo==NULL) return SML_ERR_MGR_INVALID_INSTANCE_INFO;
+ // must be already locked here
+ if (!pInstanceInfo->writeLocked)
+ return SML_ERR_WRONG_USAGE;
+ if (writtenBytes > 0) {
+ // advance write pointer by number of bytes written
+ if (pInstanceInfo->writePointer+writtenBytes>pInstanceInfo->instanceBuffer+pInstanceInfo->instanceBufSiz)
+ return SML_ERR_WRONG_USAGE; // too many bytes written
+ // update write pointer
+ pInstanceInfo->writePointer+=writtenBytes;
+ }
+ // unlock
+ pInstanceInfo->writeLocked=0;
+ #else
+ Ret_t rc;
+
+ if (writtenBytes > 0)
+ {
+ /* --- Pass the number of bytes which have been written --- */
+ LOCKTOOLKIT("smlUnlockWriteBuffer");
+ rc = wsmSetUsedSize(id,writtenBytes);
+ RELEASETOOLKIT("smlUnlockWriteBuffer");
+ if (rc!=SML_ERR_OK) return rc;
+ }
+ /* --- Unlock Workspace --- */
+ LOCKTOOLKIT("smlUnlockWriteBuffer");
+ rc = wsmUnlockH(id);
+ RELEASETOOLKIT("smlUnlockWriteBuffer");
+ if (rc!=SML_ERR_OK) return rc;
+ #endif
+
+ return SML_ERR_OK;
+}
+
+
+
+
+/*************************************************************************
+ * SyncML internal functions
+ *************************************************************************/
+
+
+/**
+ * Reset the Workspace Buffer position to the beginning of the workspace
+ * (clears all data in the buffer)
+ *
+ * @param id (IN)
+ * ID of the Instance
+ * @return Return value,\n
+ * SML_ERR_OK if successful
+ */
+Ret_t mgrResetWorkspace (InstanceID_t id) {
+ #ifdef NOWSM
+ InstanceInfoPtr_t pInstanceInfo;
+
+ pInstanceInfo = (InstanceInfoPtr_t)id; // ID is the instance info pointer
+ if (pInstanceInfo==NULL) return SML_ERR_MGR_INVALID_INSTANCE_INFO;
+
+ pInstanceInfo->readPointer=pInstanceInfo->instanceBuffer;
+ pInstanceInfo->writePointer=pInstanceInfo->instanceBuffer;
+ pInstanceInfo->outgoingMsgStart=NULL; // no outgoing message in the buffer
+ pInstanceInfo->incomingMsgStart=NULL; // no incoming message in the buffer
+ return SML_ERR_OK; // ok
+ #else
+ Ret_t rc;
+ LOCKTOOLKIT("mgrResetWorkspace");
+ rc=wsmReset (id);
+ RELEASETOOLKIT("mgrResetWorkspace");
+ return rc;
+ #endif
+}
+
+
+
+/**
+ * The options settings of an instance are set to a new value
+ *
+ * @param id (IN)
+ * Instance ID assigned to the instance
+ * @param pOptions (IN)
+ * New option settings of that particular SyncML instance\n
+ * NOTE: only the encoding can be changed during life-time of an
+ * instance. The other parameters of the instance options
+ * (workspace size and name cannot be changed)
+ * @return Error Code
+ */
+Ret_t setInstanceOptions (InstanceID_t id, SmlInstanceOptionsPtr_t pOptions)
+{
+
+ /* --- Definitions --- */
+ InstanceInfoPtr_t pInstanceInfo;
+ SmlInstanceOptionsPtr_t pOptionsCopy;
+
+
+ #ifdef NOWSM
+ /* --- Ckeck pOptions, which have been passed by the application --- */
+ if (!pOptions || (pOptions->encoding==SML_UNDEF))
+ return SML_ERR_WRONG_USAGE;
+
+ pInstanceInfo = (InstanceInfoPtr_t)id; // ID is the instance info pointer
+ #else
+ /* --- Ckeck pOptions, which have been passed by the application --- */
+ if (!pOptions || !pOptions->workspaceName|| (pOptions->encoding==SML_UNDEF))
+ return SML_ERR_WRONG_USAGE;
+
+ /* --- Find that instance --- */
+ #ifdef __SML_LITE__ /* Only ONE instance is supported in the Toolkit lite version */
+ pInstanceInfo = mgrGetInstanceListAnchor();
+ #else
+ pInstanceInfo = (InstanceInfoPtr_t) findInfo(id);
+ #endif
+ #endif
+
+ if (pInstanceInfo==NULL) return SML_ERR_MGR_INVALID_INSTANCE_INFO;
+
+ /* --- free old instance options ---*/
+ freeInstanceOptions(pInstanceInfo);
+
+ /* --- Use a copy of pOptionsCopy --- */
+ pOptionsCopy = (SmlInstanceOptionsPtr_t)smlLibMalloc((MemSize_t)sizeof(SmlInstanceOptions_t));
+ if (pOptionsCopy==NULL) return SML_ERR_NOT_ENOUGH_SPACE;
+ smlLibMemcpy(pOptionsCopy,pOptions,(MemSize_t)sizeof(SmlInstanceOptions_t));
+
+ #ifndef NOWSM
+ pOptionsCopy->workspaceName=smlLibStrdup(pOptions->workspaceName);
+
+ if (pOptionsCopy->workspaceName == NULL) {
+ pInstanceInfo->instanceOptions=NULL;
+ smlLibFree(pOptionsCopy);
+ return SML_ERR_NOT_ENOUGH_SPACE;
+ }
+ #endif
+
+ /* --- Assign the new options --- */
+ pInstanceInfo->instanceOptions=pOptionsCopy;
+
+
+ /* --- Let the new settingds take effect --- */
+ /* --- Adjust workspace size ---*/
+ /* --- Change workspace name ---*/
+ // NOT SUPPORTED FOR YELLOW
+
+ return SML_ERR_OK;
+}
+
+
+
+/**
+ * Free Instances Options
+ *
+ * @param pInfo (IN)
+ * Pointer to the pInstance Info, which options should be freed
+ * @return SML_ERR_OK
+ */
+Ret_t freeInstanceOptions (InstanceInfoPtr_t pInfo) {
+
+ /* --- Delete instance options (if there are any) --- */
+ if (pInfo->instanceOptions!=NULL) {
+ #ifndef NOWSM
+ if (pInfo->instanceOptions->workspaceName!=NULL)
+ smlLibFree(pInfo->instanceOptions->workspaceName); // don't forget the substructures
+ #endif
+ smlLibFree(pInfo->instanceOptions);
+ }
+
+ return SML_ERR_OK;
+}
+
+/**
+ * Free the memory of an removed Instance Info
+ * (including referenced sub structures)
+ *
+ * @param id (IN)
+ * ID of the InstanceInfo structure to be freed
+ */
+static Ret_t freeInstanceInfo(InstanceInfoPtr_t pInfo) {
+
+ if (pInfo) {
+
+ #ifdef NOWSM
+ // return the instance buffer
+ if (pInfo->instanceBuffer)
+ smlLibFree(pInfo->instanceBuffer);
+ #else
+ if (pInfo->workspaceState)
+ smlLibFree(pInfo->workspaceState);
+ #endif
+ if (pInfo->encoderState)
+ smlLibFree(pInfo->encoderState);
+ if (pInfo->decoderState)
+ smlLibFree(pInfo->decoderState);
+ if (pInfo->callbacks)
+ smlLibFree(pInfo->callbacks);
+
+ freeInstanceOptions(pInfo);
+
+ smlLibFree(pInfo);
+ }
+
+ return SML_ERR_OK;
+}
diff --git a/src/syncml_tk/src/sml/mgr/all/mgrutil.c b/src/syncml_tk/src/sml/mgr/all/mgrutil.c
new file mode 100755
index 0000000..16b3a60
--- /dev/null
+++ b/src/syncml_tk/src/sml/mgr/all/mgrutil.c
@@ -0,0 +1,1635 @@
+/**
+ * @file
+ * some helper functions
+ *
+ * @target_system all
+ * @target_os all
+ * @description
+ */
+
+/*
+ * Copyright Notice
+ * Copyright (c) Ericsson, IBM, Lotus, Matsushita Communication
+ * Industrial Co., Ltd., Motorola, Nokia, Openwave Systems, Inc.,
+ * Palm, Inc., Psion, Starfish Software, Symbian, Ltd. (2001).
+ * All Rights Reserved.
+ * Implementation of all or part of any Specification may require
+ * licenses under third party intellectual property rights,
+ * including without limitation, patent rights (such a third party
+ * may or may not be a Supporter). The Sponsors of the Specification
+ * are not responsible and shall not be held responsible in any
+ * manner for identifying or failing to identify any or all such
+ * third party intellectual property rights.
+ *
+ * THIS DOCUMENT AND THE INFORMATION CONTAINED HEREIN ARE PROVIDED
+ * ON AN "AS IS" BASIS WITHOUT WARRANTY OF ANY KIND AND ERICSSON, IBM,
+ * LOTUS, MATSUSHITA COMMUNICATION INDUSTRIAL CO. LTD, MOTOROLA,
+ * NOKIA, PALM INC., PSION, STARFISH SOFTWARE AND ALL OTHER SYNCML
+ * SPONSORS DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+ * BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
+ * HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
+ * SHALL ERICSSON, IBM, LOTUS, MATSUSHITA COMMUNICATION INDUSTRIAL CO.,
+ * LTD, MOTOROLA, NOKIA, PALM INC., PSION, STARFISH SOFTWARE OR ANY
+ * OTHER SYNCML SPONSOR BE LIABLE TO ANY PARTY FOR ANY LOSS OF
+ * PROFITS, LOSS OF BUSINESS, LOSS OF USE OF DATA, INTERRUPTION OF
+ * BUSINESS, OR FOR DIRECT, INDIRECT, SPECIAL OR EXEMPLARY, INCIDENTAL,
+ * PUNITIVE OR CONSEQUENTIAL DAMAGES OF ANY KIND IN CONNECTION WITH
+ * THIS DOCUMENT OR THE INFORMATION CONTAINED HEREIN, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH LOSS OR DAMAGE.
+ *
+ * The above notice and this paragraph must be included on all copies
+ * of this document that are made.
+ *
+ */
+
+/*************************************************************************
+ * Definitions
+ *************************************************************************/
+
+#include "syncml_tk_prefix_file.h" // %%% luz: needed for precompiled headers in eVC++
+
+/* Include Headers */
+#include <sml.h>
+#include <smldtd.h>
+#include <smldef.h>
+#include <smlerr.h>
+#include <smlmetinfdtd.h>
+#include <smldevinfdtd.h>
+#include "libmem.h"
+#include "libstr.h"
+#include "liblock.h"
+#include "mgr.h"
+#include "mgrutil.h"
+
+
+/*************************************************************************
+ * Exported SyncML API functions
+ *************************************************************************/
+
+/**
+ * Frees all allocated memory of a smlProtoElement
+ *
+ * @param pProtoElement (IN)
+ * Element to free
+ * @return Return Code
+ */
+SML_API Ret_t smlFreeProtoElement(VoidPtr_t pProtoElement)
+{
+ if (! pProtoElement)
+ return(SML_ERR_OK);
+
+ switch (((SmlUnknownProtoElementPtr_t)pProtoElement)->elementType) {
+
+ case SML_PE_HEADER:
+ smlFreeSyncHdr((SmlSyncHdrPtr_t)pProtoElement);
+ break;
+
+ case SML_PE_SYNC_START:
+ smlFreeSync((SmlSyncPtr_t)pProtoElement);
+ break;
+
+ case SML_PE_ADD:
+ case SML_PE_COPY:
+ case SML_PE_MOVE:
+ case SML_PE_REPLACE:
+ case SML_PE_DELETE:
+ case SML_PE_GENERIC:
+ smlFreeGeneric((SmlGenericCmdPtr_t)pProtoElement);
+ break;
+
+ case SML_PE_ALERT:
+ smlFreeAlert((SmlAlertPtr_t)pProtoElement);
+ break;
+
+ case SML_PE_ATOMIC_START:
+ case SML_PE_SEQUENCE_START:
+ case SML_PE_CMD_GROUP:
+ smlFreeAtomic((SmlAtomicPtr_t)pProtoElement);
+ break;
+
+#if (defined EXEC_SEND || defined EXEC_RECEIVE)
+ case SML_PE_EXEC:
+ smlFreeExec((SmlExecPtr_t)pProtoElement);
+ break;
+#endif
+
+ case SML_PE_PUT:
+ case SML_PE_GET:
+ case SML_PE_PUT_GET:
+ smlFreeGetPut((SmlPutPtr_t)pProtoElement);
+ break;
+
+ case SML_PE_MAP:
+ smlFreeMap((SmlMapPtr_t)pProtoElement);
+ break;
+
+ case SML_PE_RESULTS:
+ smlFreeResults((SmlResultsPtr_t)pProtoElement);
+ break;
+
+#if (defined SEARCH_SEND || defined SEARCH_RECEIVE)
+ case SML_PE_SEARCH:
+ smlFreeSearch((SmlSearchPtr_t)pProtoElement);
+ break;
+#endif
+ case SML_PE_STATUS:
+ smlFreeStatus((SmlStatusPtr_t)pProtoElement);
+ break;
+
+ default:
+ return(SML_ERR_A_UTI_UNKNOWN_PROTO_ELEMENT);
+ }
+
+ return(SML_ERR_OK);
+}
+
+
+/**
+ * Frees the Memory of an allocated Pcdata memory object
+ *
+ * @param pPcdata (IN)
+ * A Pointer to a PcData structure, which should be freed
+ */
+SML_API void smlFreePcdata(SmlPcdataPtr_t pPcdata)
+{
+ if (! pPcdata)
+ return;
+
+ if (pPcdata->contentType == SML_PCDATA_EXTENSION) {
+ switch ((int)pPcdata->extension) {
+#ifdef __USE_METINF__
+ case SML_EXT_METINF:
+ smlFreeMetinfMetinf(pPcdata->content);
+ smlLibFree(pPcdata);
+ break;
+#endif
+#ifdef __USE_DEVINF__
+ case SML_EXT_DEVINF:
+ smlFreeDevInfDevInf(pPcdata->content);
+ smlLibFree(pPcdata);
+ break;
+#endif
+ }
+ return;
+ }
+
+ if (pPcdata->content)
+ smlLibFree(pPcdata->content);
+
+ smlLibFree(pPcdata);
+}
+
+
+SML_API void smlFreePcdataList(SmlPcdataListPtr_t list) {
+ if (!list) return;
+ smlFreePcdataList(list->next);
+ smlFreePcdata(list->data);
+ smlLibFree(list);
+ return;
+}
+
+#ifdef __USE_METINF__
+/** Subfunctions to smlFreePcdata, to freeup MetaInf DTD structures */
+SML_API void smlFreeMetinfMetinf(SmlMetInfMetInfPtr_t data) {
+ if (!data) return;
+ smlFreePcdata(data->format);
+ smlFreePcdata(data->type);
+ smlFreePcdata(data->mark);
+ smlFreePcdata(data->size);
+ smlFreePcdata(data->version);
+ smlFreePcdata(data->nextnonce);
+ smlFreePcdata(data->maxmsgsize);
+ /* SCTSTK - 18/03/2002, S.H. 2002-04-05 : SyncML 1.1 */
+ smlFreePcdata(data->maxobjsize);
+ smlFreeMetinfAnchor(data->anchor);
+ smlFreeMetinfMem(data->mem);
+ smlFreePcdataList(data->emi);
+ smlLibFree(data);
+ return;
+}
+SML_API void smlFreeMetinfAnchor(SmlMetInfAnchorPtr_t data) {
+ if (!data) return;
+ smlFreePcdata(data->last);
+ smlFreePcdata(data->next);
+ smlLibFree(data);
+ return;
+}
+SML_API void smlFreeMetinfMem(SmlMetInfMemPtr_t data) {
+ if (!data) return;
+ /* smlFreePcdata(data->shared); %%% luz 2005-08-24: shared is a flag, not a Pcdata - */
+ smlFreePcdata(data->free);
+ smlFreePcdata(data->freeid);
+ smlLibFree(data);
+ return;
+}
+#endif
+
+#ifdef __USE_DEVINF__
+/** Subfunctions to smlFreePcdata, to freeup DevInf DTD structures */
+SML_API void smlFreeDevInfDevInf(SmlDevInfDevInfPtr_t data) {
+ if (!data) return;
+ smlFreePcdata(data->verdtd);
+ smlFreePcdata(data->man);
+ smlFreePcdata(data->mod);
+ smlFreePcdata(data->oem);
+ smlFreePcdata(data->fwv);
+ smlFreePcdata(data->hwv);
+ smlFreePcdata(data->swv);
+ smlFreePcdata(data->devid);
+ smlFreePcdata(data->devtyp);
+ smlFreeDevInfDatastoreList(data->datastore);
+ smlFreeDevInfExtList(data->ext);
+ smlFreeDevInfCtcapList(data->ctcap);
+ smlLibFree(data);
+}
+SML_API void smlFreeDevInfDatastore(SmlDevInfDatastorePtr_t data) {
+ if (!data) return;
+ smlFreePcdata(data->sourceref);
+ smlFreePcdata(data->displayname);
+ smlFreePcdata(data->maxguidsize);
+ smlFreeDevInfXmit(data->rxpref);
+ smlFreeDevInfXmit(data->txpref);
+ smlFreeDevInfXmitList(data->rx);
+ smlFreeDevInfXmitList(data->tx);
+ smlFreeDevInfDSMem(data->dsmem);
+ smlFreeDevInfSynccap(data->synccap);
+ smlFreeDevInfXmitList(data->filterrx);
+ smlFreeDevInfCtcapList(data->ctcap);
+ smlFreeDevInfFilterCapList(data->filtercap);
+ smlLibFree(data);
+ return;
+}
+SML_API void smlFreeDevInfDatastoreList(SmlDevInfDatastoreListPtr_t data) {
+ if (!data) return;
+ smlFreeDevInfDatastore(data->data);
+ smlFreeDevInfDatastoreList(data->next);
+ smlLibFree(data);
+ return;
+}
+SML_API void smlFreeDevInfXmitList(SmlDevInfXmitListPtr_t data) {
+ if (!data) return;
+ smlFreeDevInfXmit(data->data);
+ smlFreeDevInfXmitList(data->next);
+ smlLibFree(data);
+}
+SML_API void smlFreeDevInfXmit(SmlDevInfXmitPtr_t data) {
+ if (!data) return;
+ smlFreePcdata(data->cttype);
+ smlFreePcdata(data->verct);
+ smlLibFree(data);
+}
+SML_API void smlFreeDevInfDSMem(SmlDevInfDSMemPtr_t data) {
+ if (!data) return;
+ // %%%luz:2003-04-28: this is now a flag! smlFreePcdata(data->shared);
+ smlFreePcdata(data->maxmem);
+ smlFreePcdata(data->maxid);
+ smlLibFree(data);
+}
+SML_API void smlFreeDevInfSynccap(SmlDevInfSyncCapPtr_t data) {
+ if (!data) return;
+ smlFreePcdataList(data->synctype);
+ smlLibFree(data);
+}
+SML_API void smlFreeDevInfExt(SmlDevInfExtPtr_t data) {
+ if (!data) return;
+ smlFreePcdata(data->xnam);
+ smlFreePcdataList(data->xval);
+ smlLibFree(data);
+}
+SML_API void smlFreeDevInfExtList(SmlDevInfExtListPtr_t data) {
+ if (!data) return;
+ smlFreeDevInfExt(data->data);
+ smlFreeDevInfExtList(data->next);
+ smlLibFree(data);
+}
+SML_API void smlFreeDevInfCTData(SmlDevInfCTDataPtr_t data) {
+ if (!data) return;
+ smlFreePcdata(data->name);
+ smlFreePcdata(data->dname);
+ smlFreePcdataList(data->valenum);
+ smlFreePcdata(data->datatype);
+ smlFreePcdata(data->maxsize);
+ smlFreePcdata(data->maxoccur);
+ smlLibFree(data);
+}
+SML_API void smlFreeDevInfCTDataProp(SmlDevInfCTDataPropPtr_t data) {
+ if (!data) return;
+ smlFreeDevInfCTData(data->prop);
+ smlFreeDevInfCTDataList(data->param);
+ smlLibFree(data);
+}
+
+SML_API void smlFreeDevInfCTDataList(SmlDevInfCTDataListPtr_t data) {
+ if (!data) return;
+ smlFreeDevInfCTData(data->data);
+ smlFreeDevInfCTDataList(data->next);
+ smlLibFree(data);
+}
+
+SML_API void smlFreeDevInfCTDataPropList(SmlDevInfCTDataPropListPtr_t data) {
+ if (!data) return;
+ smlFreeDevInfCTDataProp(data->data);
+ smlFreeDevInfCTDataPropList(data->next);
+ smlLibFree(data);
+}
+
+SML_API void smlFreeDevInfCTCap(SmlDevInfCTCapPtr_t data) {
+ if (!data) return;
+ smlFreePcdata(data->cttype);
+ smlFreePcdata(data->verct);
+ smlFreeDevInfCTDataPropList(data->prop);
+ smlLibFree(data);
+}
+
+SML_API void smlFreeDevInfCtcapList(SmlDevInfCtcapListPtr_t data) {
+ if (!data) return;
+
+ smlFreeDevInfCTCap(data->data);
+ smlFreeDevInfCtcapList(data->next);
+ smlLibFree(data);
+}
+
+SML_API void smlFreeDevInfFilterCap(SmlDevInfFilterCapPtr_t data) {
+ if (!data) return;
+ smlFreePcdata(data->cttype);
+ smlFreePcdata(data->verct);
+ smlFreePcdataList(data->filterkeyword);
+ smlFreePcdataList(data->propname);
+ smlLibFree(data);
+}
+
+SML_API void smlFreeDevInfFilterCapList(SmlDevInfFilterCapListPtr_t data) {
+ if (!data) return;
+ smlFreeDevInfFilterCap(data->data);
+ smlFreeDevInfFilterCapList(data->next);
+ smlLibFree(data);
+}
+
+#endif
+
+
+/*************************************************************************
+ * Exported SyncML API functions (FULL-SIZE TOOLKIT ONLY)
+ *************************************************************************/
+
+#ifndef __SML_LITE__ /* these API calls are NOT included in the Toolkit lite version */
+
+/**
+ * Return amount of unused Workspace memory
+ *
+ * @param id instance ID
+ * @return Amount of unused Workspace memory
+ */
+SML_API MemSize_t smlGetFreeBuffer(InstanceID_t id)
+{
+ /* Definitions */
+ MemSize_t freeMem=0;
+
+ #ifdef NOWSM
+ InstanceInfoPtr_t pInstanceInfo = (InstanceInfoPtr_t)id; // ID is the instance info pointer
+ if (pInstanceInfo==NULL) return 0; // no buffer if no instance there
+ freeMem =
+ (pInstanceInfo->instanceBuffer+pInstanceInfo->instanceBufSiz) - // end of buffer
+ pInstanceInfo->writePointer; // current write pointer
+ #else
+ /* ask the Workspace Buffer */
+ LOCKTOOLKIT("smlGetFreeBuffer");
+ wsmGetFreeSize(id, &freeMem);
+ RELEASETOOLKIT("smlGetFreeBuffer");
+ #endif
+
+ return (MemSize_t)freeMem;
+}
+
+
+/**
+ * Copy a string into a Pcdata structure
+ *
+ * @param str (IN)
+ * Input String
+ * @return A Pointer to a PcData structure
+ */
+SML_API SmlPcdataPtr_t smlString2Pcdata(String_t str)
+{
+ /* Definitions */
+ SmlPcdataPtr_t pcdata;
+
+ /* Invalid Input */
+ if (! str)
+ return NULL;
+
+ /* Allocate the PcData Structure */
+ pcdata = (SmlPcdataPtr_t)smlLibMalloc((MemSize_t)sizeof(SmlPcdata_t));
+ if (! pcdata)
+ return NULL;
+ smlLibMemset (pcdata, 0, (MemSize_t)sizeof(SmlPcdata_t));
+
+ /* Set the PcData Structure */
+ pcdata->contentType = SML_PCDATA_STRING;
+ pcdata->length = smlLibStrlen( str );
+ pcdata->content = (VoidPtr_t)smlLibStrdup(str);
+
+ return pcdata;
+}
+
+
+/**
+ * Copy a Pcdata structure into a string
+ *
+ * @param pcdata (IN)
+ * A Pointer to a PcData structure
+ * @return Input String
+ */
+SML_API String_t smlPcdata2String(SmlPcdataPtr_t pcdata)
+{
+ /* Definitions */
+ String_t str;
+
+ /* Invalid Input */
+ if (! pcdata)
+ return NULL;
+
+ /* Allocate the String */
+ str = (String_t)smlLibMalloc((MemSize_t)(pcdata->length+1));
+ if (str == NULL)
+ return NULL;
+
+ /* Copy the string into the allocated data structure */
+ smlLibMemcpy((MemPtr_t)str, (MemPtr_t)pcdata->content, pcdata->length);
+ *(str + pcdata->length) = '\0';
+
+ return str;
+}
+
+
+/**
+ * Duplicates a Pcdata memory object
+ *
+ * @param pcdata (IN)
+ * A Pointer to the original PcData structure
+ * @return A Pointer to the copy of the PcData structure
+ */
+SML_API SmlPcdataPtr_t smlPcdataDup(SmlPcdataPtr_t pcdata)
+{
+ /* Definitions */
+ SmlPcdataPtr_t newPcdata;
+
+ /* Invalid Input */
+ if (! pcdata)
+ return NULL;
+
+ /* Allocate the new pcdata memory object */
+ newPcdata = (SmlPcdataPtr_t)smlLibMalloc((MemSize_t)sizeof(SmlPcdata_t));
+ if (! newPcdata)
+ return NULL;
+ smlLibMemset (newPcdata, 0, (MemSize_t)sizeof(SmlPcdata_t));
+
+ /* Set the PcData Structure */
+ newPcdata->contentType = pcdata->contentType;
+ newPcdata->length = pcdata->length;
+ newPcdata->content =(VoidPtr_t)smlLibMalloc((MemSize_t)pcdata->length+1);
+ if (newPcdata->content ==NULL)
+ {
+ smlLibFree(newPcdata->content);
+ smlLibFree(newPcdata);
+ return NULL;
+ }
+ smlLibMemset(newPcdata->content, 0, (MemSize_t)((pcdata->length)+1));
+ smlLibMemcpy(newPcdata->content, pcdata->content, (MemSize_t)pcdata->length);
+
+ return newPcdata;
+}
+
+#endif
+
+SML_API void smlFreeSyncHdr(SmlSyncHdrPtr_t pSyncHdr)
+{
+ if (! pSyncHdr)
+ return;
+
+ smlFreePcdata(pSyncHdr->version);
+ smlFreePcdata(pSyncHdr->proto);
+ smlFreePcdata(pSyncHdr->sessionID);
+ smlFreePcdata(pSyncHdr->msgID);
+ smlFreePcdata(pSyncHdr->respURI);
+ smlFreePcdata(pSyncHdr->meta);
+
+ smlFreeSourceTargetPtr(pSyncHdr->source);
+ smlFreeSourceTargetPtr(pSyncHdr->target);
+
+ smlFreeCredPtr(pSyncHdr->cred);
+
+ smlLibFree(pSyncHdr);
+}
+
+
+SML_API void smlFreeSync(SmlSyncPtr_t pSync)
+{
+ if (! pSync)
+ return;
+
+ smlFreePcdata(pSync->cmdID);
+ smlFreePcdata(pSync->meta);
+ smlFreePcdata(pSync->noc); // %%% luz 2004-09-29 added to fix mem leak
+
+ smlFreeSourceTargetPtr(pSync->source);
+ smlFreeSourceTargetPtr(pSync->target);
+
+ smlFreeCredPtr(pSync->cred);
+
+ smlLibFree(pSync);
+}
+
+
+SML_API void smlFreeGeneric(SmlGenericCmdPtr_t pGenericCmd)
+{
+ if (! pGenericCmd)
+ return;
+
+ smlFreePcdata(pGenericCmd->cmdID);
+ smlFreePcdata(pGenericCmd->meta);
+
+ smlFreeCredPtr(pGenericCmd->cred);
+
+ smlFreeItemList(pGenericCmd->itemList);
+
+ smlLibFree(pGenericCmd);
+}
+
+
+SML_API void smlFreeAlert(SmlAlertPtr_t pAlert)
+{
+ if (! pAlert)
+ return;
+
+ smlFreePcdata(pAlert->cmdID);
+ smlFreePcdata(pAlert->data);
+
+ smlFreeCredPtr(pAlert->cred);
+
+ smlFreeItemList(pAlert->itemList);
+
+ smlLibFree(pAlert);
+}
+
+
+SML_API void smlFreeAtomic(SmlAtomicPtr_t pAtomic)
+{
+ if (! pAtomic)
+ return;
+
+ smlFreePcdata(pAtomic->cmdID);
+ smlFreePcdata(pAtomic->meta);
+
+ smlLibFree(pAtomic);
+}
+
+#if (defined EXEC_SEND || defined EXEC_RECEIVE)
+
+SML_API void smlFreeExec(SmlExecPtr_t pExec)
+{
+ if (! pExec)
+ return;
+
+ smlFreePcdata(pExec->cmdID);
+
+ smlFreeCredPtr(pExec->cred);
+
+ smlFreeItemPtr(pExec->item);
+
+ smlLibFree(pExec);
+}
+
+#endif
+
+SML_API void smlFreeGetPut(SmlPutPtr_t pGetPut)
+{
+ if (! pGetPut)
+ return;
+
+ smlFreePcdata(pGetPut->cmdID);
+ smlFreePcdata(pGetPut->meta);
+ smlFreePcdata(pGetPut->lang);
+
+ smlFreeCredPtr(pGetPut->cred);
+
+ smlFreeItemList(pGetPut->itemList);
+
+ smlLibFree(pGetPut);
+}
+
+
+SML_API void smlFreeMap(SmlMapPtr_t pMap)
+{
+ if (! pMap)
+ return;
+
+ smlFreePcdata(pMap->cmdID);
+ smlFreePcdata(pMap->meta);
+
+ smlFreeCredPtr(pMap->cred);
+
+ smlFreeSourceTargetPtr(pMap->source);
+ smlFreeSourceTargetPtr(pMap->target);
+
+ smlFreeMapItemList(pMap->mapItemList);
+
+ smlLibFree(pMap);
+}
+
+
+SML_API void smlFreeResults(SmlResultsPtr_t pResults)
+{
+ if (! pResults)
+ return;
+
+ smlFreePcdata(pResults->cmdID);
+ smlFreePcdata(pResults->msgRef);
+ smlFreePcdata(pResults->cmdRef);
+ smlFreePcdata(pResults->meta);
+ smlFreePcdata(pResults->targetRef);
+ smlFreePcdata(pResults->sourceRef);
+
+ smlFreeItemList(pResults->itemList);
+
+ smlLibFree(pResults);
+}
+
+#if (defined SEARCH_SEND || defined SEARCH_RECEIVE)
+
+SML_API void smlFreeSearch(SmlSearchPtr_t pSearch)
+{
+ if (! pSearch)
+ return;
+
+ smlFreePcdata(pSearch->cmdID);
+ smlFreePcdata(pSearch->lang);
+ smlFreePcdata(pSearch->meta);
+ smlFreePcdata(pSearch->data);
+
+ smlFreeCredPtr(pSearch->cred);
+
+ smlFreeSourceTargetPtr(pSearch->target);
+
+ smlFreeSourceList(pSearch->sourceList);
+
+ smlLibFree(pSearch);
+}
+
+#endif
+
+SML_API void smlFreeStatus(SmlStatusPtr_t pStatus)
+{
+ if (! pStatus)
+ return;
+
+ smlFreePcdata(pStatus->cmdID);
+ smlFreePcdata(pStatus->msgRef);
+ smlFreePcdata(pStatus->cmdRef);
+ smlFreePcdata(pStatus->cmd);
+ smlFreePcdata(pStatus->data);
+
+ smlFreeCredPtr(pStatus->cred);
+ smlFreeChalPtr(pStatus->chal);
+
+ smlFreeTargetRefList(pStatus->targetRefList);
+ smlFreeSourceRefList(pStatus->sourceRefList);
+
+ smlFreeItemList(pStatus->itemList);
+
+ smlLibFree(pStatus);
+}
+
+
+SML_API void smlFreeCredPtr(SmlCredPtr_t pCred)
+{
+ if (! pCred)
+ return;
+
+ smlFreePcdata(pCred->meta);
+ smlFreePcdata(pCred->data);
+
+ smlLibFree(pCred);
+}
+
+
+SML_API void smlFreeChalPtr(SmlChalPtr_t pChal)
+{
+ if (! pChal)
+ return;
+
+ smlFreePcdata(pChal->meta);
+
+ smlLibFree(pChal);
+}
+
+
+
+SML_API void smlFreeRecordFieldFilterPtr(SmlRecordOrFieldFilterPtr_t pRecordFieldFilter)
+{
+ if (! pRecordFieldFilter)
+ return;
+
+ smlFreeItemPtr(pRecordFieldFilter->item);
+ smlLibFree(pRecordFieldFilter);
+}
+
+
+SML_API void smlFreeFilterPtr(SmlFilterPtr_t pFilter)
+{
+ if (! pFilter)
+ return;
+
+ smlFreePcdata(pFilter->meta);
+ smlFreeRecordFieldFilterPtr(pFilter->field);
+ smlFreeRecordFieldFilterPtr(pFilter->record);
+ smlFreePcdata(pFilter->filtertype);
+ smlLibFree(pFilter);
+}
+
+
+SML_API void smlFreeSourceTargetParentPtr(SmlSourceParentPtr_t pSourceTargetParent)
+{
+ if (! pSourceTargetParent)
+ return;
+
+ smlFreePcdata(pSourceTargetParent->locURI);
+ smlLibFree(pSourceTargetParent);
+}
+
+
+SML_API void smlFreeSourceTargetPtr(SmlSourcePtr_t pSourceTarget)
+{
+ if (! pSourceTarget)
+ return;
+
+ smlFreePcdata(pSourceTarget->locURI);
+ smlFreePcdata(pSourceTarget->locName);
+ smlFreeFilterPtr(pSourceTarget->filter);
+
+ smlLibFree(pSourceTarget);
+}
+
+
+SML_API void smlFreeSourceList(SmlSourceListPtr_t pSourceList)
+{
+ SmlSourceListPtr_t pTmp;
+
+ while (pSourceList) {
+ pTmp = pSourceList->next;
+ smlFreeSourceTargetPtr(pSourceList->source);
+ smlLibFree(pSourceList);
+ pSourceList = pTmp;
+ }
+}
+
+
+SML_API void smlFreeSourceRefList(SmlSourceRefListPtr_t pSourceRefList)
+{
+ SmlSourceRefListPtr_t pTmp;
+
+ while (pSourceRefList) {
+ pTmp = pSourceRefList->next;
+ smlFreePcdata(pSourceRefList->sourceRef);
+ smlLibFree(pSourceRefList);
+ pSourceRefList = pTmp;
+ }
+}
+
+
+SML_API void smlFreeTargetRefList(SmlTargetRefListPtr_t pTargetRefList)
+{
+ SmlTargetRefListPtr_t pTmp;
+
+ while (pTargetRefList) {
+ pTmp = pTargetRefList->next;
+ smlFreePcdata(pTargetRefList->targetRef);
+ smlLibFree(pTargetRefList);
+ pTargetRefList = pTmp;
+ }
+}
+
+
+SML_API void smlFreeItemPtr(SmlItemPtr_t pItem)
+{
+ if (! pItem)
+ return;
+
+ smlFreePcdata(pItem->meta);
+ smlFreePcdata(pItem->data);
+
+ smlFreeSourceTargetPtr(pItem->source);
+ smlFreeSourceTargetPtr(pItem->target);
+
+ smlLibFree(pItem);
+}
+
+
+SML_API void smlFreeItemList(SmlItemListPtr_t pItemList)
+{
+ SmlItemListPtr_t pTmp;
+
+ while (pItemList) {
+ pTmp = pItemList->next;
+ smlFreeItemPtr(pItemList->item);
+ smlLibFree(pItemList);
+ pItemList = pTmp;
+ }
+}
+
+
+SML_API void smlFreeMapItemPtr(SmlMapItemPtr_t pMapItem)
+{
+ if (! pMapItem)
+ return;
+
+ smlFreeSourceTargetPtr(pMapItem->source);
+ smlFreeSourceTargetPtr(pMapItem->target);
+
+ smlLibFree(pMapItem);
+}
+
+
+SML_API void smlFreeMapItemList(SmlMapItemListPtr_t pMapItemList)
+{
+ SmlMapItemListPtr_t pTmp;
+
+ while (pMapItemList) {
+ pTmp = pMapItemList->next;
+ smlFreeMapItemPtr(pMapItemList->mapItem);
+ smlLibFree(pMapItemList);
+ pMapItemList = pTmp;
+ }
+}
+
+#ifdef __USE_ALLOCFUNCS__
+/* Helperfunctions, that allocate and preset SyncML C structs */
+SML_API SmlPcdataPtr_t smlAllocPcdata() {
+ SmlPcdataPtr_t p = (SmlPcdataPtr_t)smlLibMalloc(sizeof(SmlPcdata_t));
+ if (p == NULL) return NULL;
+ smlLibMemset(p, 0, sizeof(SmlPcdata_t));
+ return p;
+}
+
+SML_API SmlPcdataListPtr_t smlAllocPcdataList() {
+ SmlPcdataListPtr_t p = (SmlPcdataListPtr_t)smlLibMalloc(sizeof(SmlPcdataList_t));
+ if (p == NULL) return NULL;
+ smlLibMemset(p, 0, sizeof(SmlPcdataList_t));
+ p->data = smlAllocPcdata();
+ if (p->data == NULL) {
+ smlFreePcdataList(p);
+ return NULL;
+ }
+ return p;
+}
+
+SML_API SmlChalPtr_t smlAllocChal() {
+ SmlChalPtr_t p = (SmlChalPtr_t)smlLibMalloc(sizeof(SmlChal_t));
+ if (p == NULL) return NULL;
+ smlLibMemset(p, 0, sizeof(SmlChal_t));
+ p->meta = smlAllocPcdata();
+ if (p->meta == NULL) {
+ smlFreeChalPtr(p);
+ return NULL;
+ }
+ return p;
+}
+
+SML_API SmlCredPtr_t smlAllocCred() {
+ SmlCredPtr_t p = (SmlCredPtr_t)smlLibMalloc(sizeof(SmlCred_t));
+ if (p == NULL) return NULL;
+ smlLibMemset(p, 0, sizeof(SmlCred_t));
+ p->data = smlAllocPcdata();
+ if (p->data == NULL) {
+ smlFreeCredPtr(p);
+ return NULL;
+ }
+ return p;
+}
+
+SML_API SmlRecordOrFieldFilterPtr_t smlAllocRecordFieldFilter()
+{
+ SmlRecordOrFieldFilterPtr_t p = (SmlRecordOrFieldFilterPtr_t)smlLibMalloc(sizeof(SmlRecordOrFieldFilterPtr_t));
+ if (p == NULL) return NULL;
+ smlLibMemset(p, 0, sizeof(SmlRecordOrFieldFilterPtr_t));
+ p->item = smlAllocItem();
+ if (p->item == NULL) {
+ smlFreeRecordFieldFilterPtr(p);
+ return NULL;
+ }
+ return p;
+}
+
+SML_API SmlFilterPtr_t smlAllocFilter()
+{
+ SmlFilterPtr_t p = (SmlFilterPtr_t)smlLibMalloc(sizeof(SmlFilterPtr_t));
+ if (p == NULL) return NULL;
+ smlLibMemset(p, 0, sizeof(SmlFilterPtr_t));
+ p->meta = smlAllocPcdata();
+ if (p->meta == NULL) {
+ smlFreeFilterPtr(p);
+ return NULL;
+ }
+ return p;
+}
+
+
+SML_API SmlSourceParentPtr_t smlAllocSourceParent()
+{
+ SmlSourceParentPtr_t p = (SmlSourceParentPtr_t)smlLibMalloc(sizeof(SmlSourceParentPtr_t));
+ if (p == NULL) return NULL;
+ smlLibMemset(p, 0, sizeof(SmlSourceParentPtr_t));
+ p->locURI = smlAllocPcdata();
+ if (p->locURI == NULL) {
+ smlFreeSourceTargetParentPtr(p);
+ return NULL;
+ }
+ return p;
+}
+
+
+SML_API SmlTargetParentPtr_t smlAllocTargetParent()
+{
+ return smlAllocSourceParent();
+}
+
+SML_API SmlSourcePtr_t smlAllocSource() {
+ SmlSourcePtr_t p = (SmlSourcePtr_t)smlLibMalloc(sizeof(SmlSource_t));
+ if (p == NULL) return NULL;
+ smlLibMemset(p, 0, sizeof(SmlSource_t));
+ p->locURI = smlAllocPcdata();
+ if (p->locURI == NULL) {
+ smlFreeSourceTargetPtr(p);
+ return NULL;
+ }
+ return p;
+}
+
+
+SML_API SmlTargetPtr_t smlAllocTarget() {
+ return smlAllocSource();
+}
+
+SML_API SmlSourceListPtr_t smlAllocSourceList() {
+ SmlSourceListPtr_t p = (SmlSourceListPtr_t)smlLibMalloc(sizeof(SmlSourceList_t));
+ if (p == NULL) return NULL;
+ smlLibMemset(p, 0, sizeof(SmlSourceList_t));
+ p->source = smlAllocSource();
+ if (p->source == NULL) {
+ smlFreeSourceList(p);
+ return NULL;
+ }
+ return p;
+}
+
+
+SML_API SmlSyncHdrPtr_t smlAllocSyncHdr() {
+ SmlSyncHdrPtr_t p = (SmlSyncHdrPtr_t)smlLibMalloc(sizeof(SmlSyncHdr_t));
+ if (p == NULL) return NULL;
+ smlLibMemset(p, 0, sizeof(SmlSyncHdr_t));
+ p->elementType = SML_PE_HEADER;
+ p->version = smlAllocPcdata();
+ if (p->version == NULL) {
+ smlFreeSyncHdr(p);
+ return NULL;
+ }
+ p->proto = smlAllocPcdata();
+ if (p->proto == NULL) {
+ smlFreeSyncHdr(p);
+ return NULL;
+ }
+ p->sessionID = smlAllocPcdata();
+ if (p->sessionID == NULL) {
+ smlFreeSyncHdr(p);
+ return NULL;
+ }
+ p->msgID = smlAllocPcdata();
+ if (p->msgID == NULL) {
+ smlFreeSyncHdr(p);
+ return NULL;
+ }
+ p->target = smlAllocTarget();
+ if (p->target == NULL) {
+ smlFreeSyncHdr(p);
+ return NULL;
+ }
+ p->source = smlAllocSource();
+ if (p->source == NULL) {
+ smlFreeSyncHdr(p);
+ return NULL;
+ }
+ return p;
+}
+
+SML_API SmlItemPtr_t smlAllocItem() {
+ SmlItemPtr_t p = (SmlItemPtr_t)smlLibMalloc(sizeof(SmlItem_t));
+ if (p == NULL) return NULL;
+ smlLibMemset(p, 0, sizeof(SmlItem_t));
+ return p;
+}
+
+SML_API SmlItemListPtr_t smlAllocItemList() {
+ SmlItemListPtr_t p = (SmlItemListPtr_t)smlLibMalloc(sizeof(SmlItemList_t));
+ if (p == NULL) return NULL;
+ smlLibMemset(p, 0, sizeof(SmlItemList_t));
+ p->item = smlAllocItem();
+ if (p->item == NULL) {
+ smlFreeItemList(p);
+ return NULL;
+ }
+ return p;
+}
+
+SML_API SmlGenericCmdPtr_t smlAllocGeneric() {
+ SmlGenericCmdPtr_t p = (SmlGenericCmdPtr_t)smlLibMalloc(sizeof(SmlGenericCmd_t));
+ if (p == NULL) return NULL;
+ smlLibMemset(p, 0, sizeof(SmlGenericCmd_t));
+ p->elementType = SML_PE_GENERIC;
+ p->cmdID = smlAllocPcdata();
+ if (p->cmdID == NULL) {
+ smlFreeGeneric(p);
+ return NULL;
+ }
+ p->itemList = smlAllocItemList();
+ if (p->itemList == NULL) {
+ smlFreeGeneric(p);
+ return NULL;
+ }
+ return p;
+}
+
+SML_API SmlAddPtr_t smlAllocAdd() {
+ SmlAddPtr_t p = smlAllocGeneric();
+ if (p == NULL) return p;
+ p->elementType = SML_PE_ADD;
+ return p;
+}
+
+SML_API SmlCopyPtr_t smlAllocCopy() {
+ SmlCopyPtr_t p = smlAllocGeneric();
+ if (p == NULL) return p;
+ p->elementType = SML_PE_COPY;
+ return p;
+}
+
+SML_API SmlMovePtr_t smlAllocMove() {
+ SmlCopyPtr_t p = smlAllocGeneric();
+ if (p == NULL) return p;
+ p->elementType = SML_PE_MOVE;
+ return p;
+}
+
+SML_API SmlReplacePtr_t smlAllocReplace() {
+ SmlReplacePtr_t p = smlAllocGeneric();
+ if (p == NULL) return p;
+ p->elementType = SML_PE_REPLACE;
+ return p;
+}
+
+SML_API SmlDeletePtr_t smlAllocDelete() {
+ SmlDeletePtr_t p = smlAllocGeneric();
+ if (p == NULL) return p;
+ p->elementType = SML_PE_DELETE;
+ return p;
+}
+
+SML_API SmlAlertPtr_t smlAllocAlert() {
+ SmlAlertPtr_t p = (SmlAlertPtr_t)smlLibMalloc(sizeof(SmlAlert_t));
+ if (p == NULL) return NULL;
+ smlLibMemset(p, 0, sizeof(SmlAlert_t));
+ p->elementType = SML_PE_ALERT;
+ p->cmdID = smlAllocPcdata();
+ if (p->cmdID == NULL) {
+ smlFreeAlert(p);
+ return NULL;
+ }
+ p->itemList = smlAllocItemList();
+ if (p->itemList == NULL) {
+ smlFreeAlert(p);
+ return NULL;
+ }
+ return p;
+}
+
+SML_API SmlAtomicPtr_t smlAllocAtomic() {
+ SmlAtomicPtr_t p = (SmlAtomicPtr_t)smlLibMalloc(sizeof(SmlAtomic_t));
+ if (p == NULL) return NULL;
+ smlLibMemset(p, 0, sizeof(SmlAtomic_t));
+ p->elementType = SML_PE_ATOMIC_START;
+ p->cmdID = smlAllocPcdata();
+ if (p->cmdID == NULL) {
+ smlFreeAtomic(p);
+ return NULL;
+ }
+ return p;
+}
+
+SML_API SmlSequencePtr_t smlAllocSequence() {
+ SmlSequencePtr_t p = smlAllocAtomic();
+ if (p == NULL) return NULL;
+ p->elementType = SML_PE_SEQUENCE_START;
+ return p;
+}
+
+SML_API SmlSyncPtr_t smlAllocSync() {
+ SmlSyncPtr_t p = (SmlSyncPtr_t)smlLibMalloc(sizeof(SmlSync_t));
+ if (p == NULL) return NULL;
+ smlLibMemset(p, 0, sizeof(SmlSync_t));
+ p->elementType = SML_PE_SYNC_START;
+ p->cmdID = smlAllocPcdata();
+ if (p->cmdID == NULL) {
+ smlFreeSync(p);
+ return NULL;
+ }
+ return p;
+}
+
+#if defined(EXEC_SEND) || defined(EXEC_RECEIVE)
+SML_API SmlExecPtr_t smlAllocExec() {
+ SmlExecPtr_t p = (SmlExecPtr_t)smlLibMalloc(sizeof(SmlExec_t));
+ if (p == NULL) return NULL;
+ smlLibMemset(p, 0, sizeof(SmlExec_t));
+ p->elementType = SML_PE_EXEC;
+ p->cmdID = smlAllocPcdata();
+ if (p->cmdID == NULL) {
+ smlFreeExec(p);
+ return NULL;
+ }
+ p->item = smlAllocItem();
+ if (p->item == NULL) {
+ smlFreeExec(p);
+ return NULL;
+ }
+ return p;
+}
+#endif
+
+SML_API SmlGetPtr_t smlAllocGet() {
+ SmlGetPtr_t p = (SmlGetPtr_t)smlLibMalloc(sizeof(SmlGet_t));
+ if (p == NULL) return NULL;
+ smlLibMemset(p, 0, sizeof(SmlGet_t));
+ p->elementType = SML_PE_GET;
+ p->cmdID = smlAllocPcdata();
+ if (p->cmdID == NULL) {
+ smlFreeGetPut(p);
+ return NULL;
+ }
+ p->itemList = smlAllocItemList();
+ if (p->itemList == NULL) {
+ smlFreeGetPut(p);
+ return NULL;
+ }
+ return p;
+}
+
+SML_API SmlPutPtr_t smlAllocPut() {
+ SmlPutPtr_t p = smlAllocGet();
+ if (p == NULL) return NULL;
+ p->elementType = SML_PE_PUT;
+ return p;
+}
+
+SML_API SmlMapItemPtr_t smlAllocMapItem() {
+ SmlMapItemPtr_t p = (SmlMapItemPtr_t)smlLibMalloc(sizeof(SmlMapItem_t));
+ if (p == 0) return NULL;
+ smlLibMemset(p, 0, sizeof(SmlMapItem_t));
+ p->target = smlAllocTarget();
+ if (p->target == NULL) {
+ smlFreeMapItemPtr(p);
+ return NULL;
+ }
+ p->source = smlAllocSource();
+ if (p->source == NULL) {
+ smlFreeMapItemPtr(p);
+ return NULL;
+ }
+ return p;
+}
+
+SML_API SmlMapItemListPtr_t smlAllocMapItemList() {
+ SmlMapItemListPtr_t p = (SmlMapItemListPtr_t)smlLibMalloc(sizeof(SmlMapItemList_t));
+ if (p == 0) return NULL;
+ smlLibMemset(p, 0, sizeof(SmlMapItemList_t));
+ p->mapItem = smlAllocMapItem();
+ if (p->mapItem == NULL) {
+ smlFreeMapItemList(p);
+ return NULL;
+ }
+ return p;
+}
+
+SML_API SmlMapPtr_t smlAllocMap() {
+ SmlMapPtr_t p = (SmlMapPtr_t)smlLibMalloc(sizeof(SmlMap_t));
+ if (p == 0) return NULL;
+ smlLibMemset(p, 0, sizeof(SmlMap_t));
+ p->elementType = SML_PE_MAP;
+ p->cmdID = smlAllocPcdata();
+ if (p->cmdID == NULL) {
+ smlFreeMap(p);
+ return NULL;
+ }
+ p->target = smlAllocTarget();
+ if (p->target == NULL) {
+ smlFreeMap(p);
+ return NULL;
+ }
+ p->source = smlAllocSource();
+ if (p->source == NULL) {
+ smlFreeMap(p);
+ return NULL;
+ }
+ p->mapItemList = smlAllocMapItemList();
+ if (p->mapItemList == NULL) {
+ smlFreeMap(p);
+ return NULL;
+ }
+ return p;
+}
+
+SML_API SmlResultsPtr_t smlAllocResults() {
+ SmlResultsPtr_t p = (SmlResultsPtr_t)smlLibMalloc(sizeof(SmlResults_t));
+ if (p == 0) return NULL;
+ smlLibMemset(p, 0, sizeof(SmlResults_t));
+ p->elementType = SML_PE_RESULTS;
+ p->cmdID = smlAllocPcdata();
+ if (p->cmdID == NULL) {
+ smlFreeResults(p);
+ return NULL;
+ }
+ p->cmdRef = smlAllocPcdata();
+ if (p->cmdRef == NULL) {
+ smlFreeResults(p);
+ return NULL;
+ }
+ p->itemList = smlAllocItemList();
+ if (p->itemList == NULL) {
+ smlFreeResults(p);
+ return NULL;
+ }
+ return p;
+}
+
+#if (defined SEARCH_SEND || defined SEARCH_RECEIVE)
+
+SML_API SmlSearchPtr_t smlAllocSearch() {
+ SmlSearchPtr_t p = (SmlSearchPtr_t)smlLibMalloc(sizeof(SmlSearch_t));
+ if (p == 0) return NULL;
+ smlLibMemset(p, 0, sizeof(SmlSearch_t));
+ p->elementType = SML_PE_SEARCH;
+ p->cmdID = smlAllocPcdata();
+ if (p->cmdID == NULL) {
+ smlFreeSearch(p);
+ return NULL;
+ }
+ p->meta = smlAllocPcdata();
+ if (p->meta == NULL) {
+ smlFreeSearch(p);
+ return NULL;
+ }
+ p->data = smlAllocPcdata();
+ if (p->data == NULL) {
+ smlFreeSearch(p);
+ return NULL;
+ }
+ p->sourceList = smlAllocSourceList();
+ if (p->sourceList == NULL) {
+ smlFreeSearch(p);
+ return NULL;
+ }
+ return p;
+}
+#endif
+
+SML_API SmlTargetRefListPtr_t smlAllocTargetRefList() {
+ SmlTargetRefListPtr_t p = (SmlTargetRefListPtr_t)smlLibMalloc(sizeof(SmlTargetRefList_t));
+ if (p == 0) return NULL;
+ smlLibMemset(p, 0, sizeof(SmlTargetRefList_t));
+ p->targetRef = smlAllocPcdata();
+ if (p->targetRef == NULL) {
+ smlFreeTargetRefList(p);
+ return NULL;
+ }
+ return p;
+}
+
+SML_API SmlSourceRefListPtr_t smlAllocSourceRefList() {
+ SmlSourceRefListPtr_t p = (SmlSourceRefListPtr_t)smlLibMalloc(sizeof(SmlSourceRefList_t));
+ if (p == 0) return NULL;
+ smlLibMemset(p, 0, sizeof(SmlSourceRefList_t));
+ p->sourceRef = smlAllocPcdata();
+ if (p->sourceRef == NULL) {
+ smlFreeSourceRefList(p);
+ return NULL;
+ }
+ return p;
+}
+
+SML_API SmlStatusPtr_t smlAllocStatus() {
+ SmlStatusPtr_t p = (SmlStatusPtr_t)smlLibMalloc(sizeof(SmlStatus_t));
+ if (p == 0) return NULL;
+ smlLibMemset(p, 0, sizeof(SmlStatus_t));
+ p->elementType = SML_PE_STATUS;
+ p->cmdID = smlAllocPcdata();
+ if (p->cmdID == NULL) {
+ smlFreeStatus(p);
+ return NULL;
+ }
+ p->msgRef = smlAllocPcdata();
+ if (p->msgRef == NULL) {
+ smlFreeStatus(p);
+ return NULL;
+ }
+ p->cmdRef = smlAllocPcdata();
+ if (p->cmdRef == NULL) {
+ smlFreeStatus(p);
+ return NULL;
+ }
+ p->cmd = smlAllocPcdata();
+ if (p->cmd == NULL) {
+ smlFreeStatus(p);
+ return NULL;
+ }
+ p->data = smlAllocPcdata();
+ if (p->data == NULL) {
+ smlFreeStatus(p);
+ return NULL;
+ }
+ return p;
+}
+
+SML_API SmlUnknownProtoElementPtr_t smlAllocUnknownProtoElement() {
+ SmlUnknownProtoElementPtr_t p = (SmlUnknownProtoElementPtr_t)smlLibMalloc(sizeof(SmlUnknownProtoElement_t));
+ if (p == 0) return NULL;
+ smlLibMemset(p, 0, sizeof(SmlUnknownProtoElement_t));
+ p->elementType = SML_PE_UNDEF;
+ return p;
+}
+
+#ifdef __USE_METINF__
+SML_API SmlMetInfMetInfPtr_t smlAllocMetInfMetInf() {
+ SmlMetInfMetInfPtr_t p = (SmlMetInfMetInfPtr_t)smlLibMalloc(sizeof(SmlMetInfMetInf_t));
+ if (p == NULL) return NULL;
+ smlLibMemset(p, 0, sizeof(SmlMetInfMetInf_t));
+ return p;
+}
+
+SML_API SmlMetInfAnchorPtr_t smlAllocMetInfAnchor() {
+ SmlMetInfAnchorPtr_t p = (SmlMetInfAnchorPtr_t)smlLibMalloc(sizeof(SmlMetInfAnchor_t));
+ if (p == NULL) return NULL;
+ smlLibMemset(p, 0, sizeof(SmlMetInfAnchor_t));
+ p->next = smlAllocPcdata();
+ if (p->next == NULL) {
+ smlFreeMetinfAnchor(p);
+ return NULL;
+ }
+ return p;
+}
+
+SML_API SmlMetInfMemPtr_t smlAllocMetInfMem() {
+ SmlMetInfMemPtr_t p = (SmlMetInfMemPtr_t)smlLibMalloc(sizeof(SmlMetInfMem_t));
+ if (p == NULL) return NULL;
+ smlLibMemset(p, 0, sizeof(SmlMetInfMem_t));
+ p->free = smlAllocPcdata();
+ if (p->free == NULL) {
+ smlFreeMetinfMem(p);
+ return NULL;
+ }
+ p->freeid = smlAllocPcdata();
+ if (p->freeid == NULL) {
+ smlFreeMetinfMem(p);
+ return NULL;
+ }
+ return p;
+}
+#endif
+
+#ifdef __USE_DEVINF__
+SML_API SmlDevInfExtPtr_t smlAllocDevInfExt() {
+ SmlDevInfExtPtr_t p = (SmlDevInfExtPtr_t)smlLibMalloc(sizeof(SmlDevInfExt_t));
+ if (p == NULL) return NULL;
+ smlLibMemset(p, 0, sizeof(SmlDevInfExt_t));
+ p->xnam = smlAllocPcdata();
+ if (p->xnam == NULL) {
+ smlFreeDevInfExt(p);
+ return NULL;
+ }
+ return p;
+}
+
+SML_API SmlDevInfExtListPtr_t smlAllocDevInfExtList() {
+ SmlDevInfExtListPtr_t p = (SmlDevInfExtListPtr_t)smlLibMalloc(sizeof(SmlDevInfExtList_t));
+ if (p == NULL) return NULL;
+ smlLibMemset(p, 0, sizeof(SmlDevInfExtList_t));
+ p->data = smlAllocDevInfExt();
+ if (p->data == NULL) {
+ smlFreeDevInfExtList(p);
+ return NULL;
+ }
+ return p;
+}
+
+SML_API SmlDevInfSyncCapPtr_t smlAllocDevInfSyncCap() {
+ SmlDevInfSyncCapPtr_t p = (SmlDevInfSyncCapPtr_t)smlLibMalloc(sizeof(SmlDevInfSyncCap_t));
+ if (p == NULL) return NULL;
+ smlLibMemset(p, 0, sizeof(SmlDevInfSyncCap_t));
+ p->synctype = smlAllocPcdataList();
+ if (p->synctype == NULL) {
+ smlFreeDevInfSynccap(p);
+ return NULL;
+ }
+ return p;
+}
+
+SML_API SmlDevInfCTDataPtr_t smlAllocDevInfCTData() {
+ SmlDevInfCTDataPtr_t p = (SmlDevInfCTDataPtr_t)smlLibMalloc(sizeof(SmlDevInfCTData_t));
+ if (p == NULL) return NULL;
+ smlLibMemset(p, 0, sizeof(SmlDevInfCTData_t));
+ p->name = smlAllocPcdata();
+ if (p->name == NULL) {
+ smlFreeDevInfCTData(p);
+ return NULL;
+ }
+ return p;
+}
+
+SML_API SmlDevInfCTDataListPtr_t smlAllocDevInfCTDataList() {
+ SmlDevInfCTDataListPtr_t p = (SmlDevInfCTDataListPtr_t)smlLibMalloc(sizeof(SmlDevInfCTDataList_t));
+ if (p == NULL) return NULL;
+ smlLibMemset(p, 0, sizeof(SmlDevInfCTDataList_t));
+ p->data = smlAllocDevInfCTData();
+ if (p->data == NULL) {
+ smlFreeDevInfCTDataList(p);
+ return NULL;
+ }
+ return p;
+}
+
+SML_API SmlDevInfCTDataPropPtr_t smlAllocDevInfCTDataProp() {
+ SmlDevInfCTDataPropPtr_t p = (SmlDevInfCTDataPropPtr_t)smlLibMalloc(sizeof(SmlDevInfCTDataProp_t));
+ if (p == NULL) return NULL;
+ smlLibMemset(p, 0, sizeof(SmlDevInfCTDataProp_t));
+ p->prop = smlAllocDevInfCTData();
+ if (p->prop == NULL) {
+ smlFreeDevInfCTDataProp(p);
+ return NULL;
+ }
+ return p;
+}
+
+SML_API SmlDevInfCTDataPropListPtr_t smlAllocDevInfCTDataPropList() {
+ SmlDevInfCTDataPropListPtr_t p = (SmlDevInfCTDataPropListPtr_t)smlLibMalloc(sizeof(SmlDevInfCTDataPropList_t));
+ if (p == NULL) return NULL;
+ smlLibMemset(p, 0, sizeof(SmlDevInfCTDataPropList_t));
+ p->data = smlAllocDevInfCTDataProp();
+ if (p->data == NULL) {
+ smlFreeDevInfCTDataPropList(p);
+ return NULL;
+ }
+ return p;
+}
+
+SML_API SmlDevInfCTCapPtr_t smlAllocDevInfCTCap() {
+ SmlDevInfCTCapPtr_t p = (SmlDevInfCTCapPtr_t)smlLibMalloc(sizeof(SmlDevInfCTCap_t));
+ if (p == NULL) return NULL;
+ smlLibMemset(p, 0, sizeof(SmlDevInfCTCap_t));
+ p->cttype = smlAllocPcdata();
+ if (p->cttype == NULL) {
+ smlFreeDevInfCTCap(p);
+ return NULL;
+ }
+ p->prop = smlAllocDevInfCTDataPropList();
+ if (p->prop == NULL) {
+ smlFreeDevInfCTCap(p);
+ return NULL;
+ }
+ return p;
+}
+
+SML_API SmlDevInfCtcapListPtr_t smlAllocDevInfCtcapList() {
+ SmlDevInfCtcapListPtr_t p = (SmlDevInfCtcapListPtr_t)smlLibMalloc(sizeof(SmlDevInfCtcapList_t));
+ if (p == NULL) return NULL;
+ smlLibMemset(p, 0, sizeof(SmlDevInfCtcapList_t));
+ p->data = smlAllocDevInfCTCap();
+ if (p->data == NULL) {
+ smlFreeDevInfCtcapList(p);
+ return NULL;
+ }
+ return p;
+}
+
+SML_API SmlDevInfDSMemPtr_t smlAllocDevInfDSMem() {
+ SmlDevInfDSMemPtr_t p = (SmlDevInfDSMemPtr_t)smlLibMalloc(sizeof(SmlDevInfDSMem_t));
+ if (p == NULL) return NULL;
+ smlLibMemset(p, 0, sizeof(SmlDevInfDSMem_t));
+ return p;
+}
+
+SML_API SmlDevInfXmitPtr_t smlAllocDevInfXmit() {
+ SmlDevInfXmitPtr_t p = (SmlDevInfXmitPtr_t)smlLibMalloc(sizeof(SmlDevInfXmit_t));
+ if (p == NULL) return NULL;
+ smlLibMemset(p, 0, sizeof(SmlDevInfXmit_t));
+ p->cttype = smlAllocPcdata();
+ if (p->cttype == NULL) {
+ smlFreeDevInfXmit(p);
+ return NULL;
+ }
+ p->verct = smlAllocPcdata();
+ if (p->verct == NULL) {
+ smlFreeDevInfXmit(p);
+ return NULL;
+ }
+ return p;
+}
+
+SML_API SmlDevInfXmitListPtr_t smlAllocDevInfXmitList() {
+ SmlDevInfXmitListPtr_t p = (SmlDevInfXmitListPtr_t)smlLibMalloc(sizeof(SmlDevInfXmitList_t));
+ if (p == NULL) return NULL;
+ smlLibMemset(p, 0, sizeof(SmlDevInfXmitList_t));
+ p->data = smlAllocDevInfXmit();
+ if (p->data == NULL) {
+ smlFreeDevInfXmitList(p);
+ return NULL;
+ }
+ return p;
+}
+
+SML_API SmlDevInfDatastorePtr_t smlAllocDevInfDatastore() {
+ SmlDevInfDatastorePtr_t p = (SmlDevInfDatastorePtr_t)smlLibMalloc(sizeof(SmlDevInfDatastore_t));
+ if (p == NULL) return NULL;
+ smlLibMemset(p, 0, sizeof(SmlDevInfDatastore_t));
+ p->sourceref = smlAllocPcdata();
+ if (p->sourceref == NULL) {
+ smlFreeDevInfDatastore(p);
+ return NULL;
+ }
+ p->rxpref = smlAllocDevInfXmit();
+ if (p->rxpref == NULL) {
+ smlFreeDevInfDatastore(p);
+ return NULL;
+ }
+ p->txpref = smlAllocDevInfXmit();
+ if (p->txpref == NULL) {
+ smlFreeDevInfDatastore(p);
+ return NULL;
+ }
+ p->synccap = smlAllocDevInfSyncCap();
+ if (p->synccap == NULL) {
+ smlFreeDevInfDatastore(p);
+ return NULL;
+ }
+ return p;
+}
+
+SML_API SmlDevInfDatastoreListPtr_t smlAllocDevInfDatastoreList() {
+ SmlDevInfDatastoreListPtr_t p = (SmlDevInfDatastoreListPtr_t)smlLibMalloc(sizeof(SmlDevInfDatastoreList_t));
+ if (p == NULL) return NULL;
+ smlLibMemset(p, 0, sizeof(SmlDevInfDatastoreList_t));
+ p->data = smlAllocDevInfDatastore();
+ if (p->data == NULL) {
+ smlFreeDevInfDatastoreList(p);
+ return NULL;
+ }
+ return p;
+}
+
+SML_API SmlDevInfDevInfPtr_t smlAllocDevInfDevInf() {
+ SmlDevInfDevInfPtr_t p = (SmlDevInfDevInfPtr_t)smlLibMalloc(sizeof(SmlDevInfDevInf_t));
+ if (p == NULL) return NULL;
+ smlLibMemset(p, 0, sizeof(SmlDevInfDevInf_t));
+ p->verdtd = smlAllocPcdata();
+ if (p->verdtd == NULL) {
+ smlFreeDevInfDevInf(p);
+ return NULL;
+ }
+ p->devid = smlAllocPcdata();
+ if (p->devid == NULL) {
+ smlFreeDevInfDevInf(p);
+ return NULL;
+ }
+ p->devtyp = smlAllocPcdata();
+ if (p->devtyp == NULL) {
+ smlFreeDevInfDevInf(p);
+ return NULL;
+ }
+ p->datastore = smlAllocDevInfDatastoreList();
+ if (p->verdtd == NULL) {
+ smlFreeDevInfDevInf(p);
+ return NULL;
+ }
+ p->ctcap = smlAllocDevInfCtcapList();
+ if (p->ctcap == NULL) {
+ smlFreeDevInfDevInf(p);
+ return NULL;
+ }
+ p->ext = smlAllocDevInfExtList();
+ if (p->ext == NULL) {
+ smlFreeDevInfDevInf(p);
+ return NULL;
+ }
+
+ return p;
+}
+
+#endif // DevInf
+#endif // AllocFuncs
diff --git a/src/syncml_tk/src/sml/mgr/inc/mgr.h b/src/syncml_tk/src/sml/mgr/inc/mgr.h
new file mode 100755
index 0000000..5c26a46
--- /dev/null
+++ b/src/syncml_tk/src/sml/mgr/inc/mgr.h
@@ -0,0 +1,162 @@
+/**
+ * @file
+ * SyncML internal API of the MGR module
+ *
+ * @target_system all
+ * @target_os all
+ * @description Definitions for internal use within the SyncML implementation
+ */
+
+
+
+/*
+ * Copyright Notice
+ * Copyright (c) Ericsson, IBM, Lotus, Matsushita Communication
+ * Industrial Co., Ltd., Motorola, Nokia, Openwave Systems, Inc.,
+ * Palm, Inc., Psion, Starfish Software, Symbian, Ltd. (2001).
+ * All Rights Reserved.
+ * Implementation of all or part of any Specification may require
+ * licenses under third party intellectual property rights,
+ * including without limitation, patent rights (such a third party
+ * may or may not be a Supporter). The Sponsors of the Specification
+ * are not responsible and shall not be held responsible in any
+ * manner for identifying or failing to identify any or all such
+ * third party intellectual property rights.
+ *
+ * THIS DOCUMENT AND THE INFORMATION CONTAINED HEREIN ARE PROVIDED
+ * ON AN "AS IS" BASIS WITHOUT WARRANTY OF ANY KIND AND ERICSSON, IBM,
+ * LOTUS, MATSUSHITA COMMUNICATION INDUSTRIAL CO. LTD, MOTOROLA,
+ * NOKIA, PALM INC., PSION, STARFISH SOFTWARE AND ALL OTHER SYNCML
+ * SPONSORS DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+ * BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
+ * HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
+ * SHALL ERICSSON, IBM, LOTUS, MATSUSHITA COMMUNICATION INDUSTRIAL CO.,
+ * LTD, MOTOROLA, NOKIA, PALM INC., PSION, STARFISH SOFTWARE OR ANY
+ * OTHER SYNCML SPONSOR BE LIABLE TO ANY PARTY FOR ANY LOSS OF
+ * PROFITS, LOSS OF BUSINESS, LOSS OF USE OF DATA, INTERRUPTION OF
+ * BUSINESS, OR FOR DIRECT, INDIRECT, SPECIAL OR EXEMPLARY, INCIDENTAL,
+ * PUNITIVE OR CONSEQUENTIAL DAMAGES OF ANY KIND IN CONNECTION WITH
+ * THIS DOCUMENT OR THE INFORMATION CONTAINED HEREIN, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH LOSS OR DAMAGE.
+ *
+ * The above notice and this paragraph must be included on all copies
+ * of this document that are made.
+ *
+ */
+
+
+#ifndef _MGR_H
+ #define _MGR_H
+
+
+/*************************************************************************
+ * Definitions
+ *************************************************************************/
+
+
+#include <smldef.h>
+#include <sml.h>
+#include "wsm.h"
+#include <xlttagtbl.h>
+
+
+/**
+ * ========================================
+ * Definitions used for Instance Management
+ * ========================================
+ **/
+
+
+
+/**
+ * Current instance status
+ */
+typedef enum {
+ MGR_IDLE, /**< instance is idle (available for usage by applications) */
+ MGR_USED, /**< instance is in use, but currently inactive */
+ MGR_RECEIVE, /**< actively used for receiving (locked by application) */
+ MGR_SEND, /**< actively used for sending (locked by application) */
+ MGR_ENCODING, /**< actively used for encoding (locked by SyncML) */
+ MGR_DECODING /**< actively used for decoding (locked by SyncML) */
+} InstanceStatus_t;
+
+
+
+/**
+ * structure describing the current status of an instance,
+ */
+typedef struct instance_info_s {
+ #ifndef NOWSM
+ InstanceID_t id; /**< unique ID of the instance */
+ MemPtr_t workspaceHandle; /**< handle to the first position of the assigned workspace memory */
+ #else
+ // buffer pointers for NOWSM simplified case
+ MemPtr_t instanceBuffer; /**< pointer to instance work buffer */
+ MemSize_t instanceBufSiz; /**< size of currently allocated buffer */
+ Byte_t readLocked; /**< set when buffer is locked for read */
+ Byte_t writeLocked; /**< set when buffer is locked for read */
+ MemPtr_t readPointer; /**< read pointer */
+ MemPtr_t writePointer; /**< write pointer */
+ MemPtr_t outgoingMsgStart; /**< set whenever a smlStartMessage is issued, NULL when invalid */
+ MemPtr_t incomingMsgStart; /**< set whenever mgrProcessStartMessage starts reading a message, NULL when invalid */
+ MemSize_t maxOutgoingSize; /**< if<>0, smlXXXCmd will not modify the buffer when there's not enough room */
+ #endif
+ InstanceStatus_t status; /**< current internal state of instance */
+ SmlCallbacksPtr_t callbacks; /**< Defined callback refererences for this Instance */
+ SmlInstanceOptionsPtr_t instanceOptions; /**< Defined options for this Instance (e.g. encoding type) */
+ VoidPtr_t userData; /**< Pointer to a structure, which is passed to the invoked callback functions */
+ #ifndef NOWSM
+ VoidPtr_t workspaceState; /**< Pointer to a structure defining the current workspace status */
+ #endif
+ VoidPtr_t encoderState; /**< Pointer to a structure defining the current encoder status */
+ VoidPtr_t decoderState; /**< Pointer to a structure defining the current decoder status */
+ #ifndef NOWSM
+ struct instance_info_s* nextInfo; /**< Pointer to next Instance Info in a list */
+ #else
+ smlPrintFunc defaultPrintFunc; /**< default application callback for displaying strings (is a global in original version) */
+ #endif
+} *InstanceInfoPtr_t, InstanceInfo_t;
+
+
+/** Pointers to store the global Tag tables */
+typedef struct tokeninfo_s {
+ TagPtr_t SyncML;
+ TagPtr_t MetInf;
+ TagPtr_t DevInf;
+} *TokenInfoPtr_t, TokenInfo_t;
+
+
+#ifndef NOWSM
+// Note, version without WSM has NO globals at all
+/**
+ * structure describing the current status of the global syncml module
+ * (holds all global variables within SyncML)
+ */
+typedef struct syncml_info_s {
+ InstanceInfoPtr_t instanceListAnchor;/**< Anchor of the global list of known SyncML instances */
+ SmlOptionsPtr_t syncmlOptions; /**< Options valid for this SyncML Process */
+ WsmGlobalsPtr_t wsmGlobals; /**< Workspace global variables */
+ TokenInfoPtr_t tokTbl;
+} *SyncMLInfoPtr_t, SyncMLInfo_t;
+
+/*************************************************************************
+ * External Function Declarations
+ *************************************************************************/
+
+SyncMLInfoPtr_t mgrGetSyncMLAnchor(void) MGR_FUNC;
+InstanceInfoPtr_t mgrGetInstanceListAnchor(void) MGR_FUNC;
+void mgrSetInstanceListAnchor(InstanceInfoPtr_t newListAnchor) MGR_FUNC;
+
+#ifndef __SML_LITE__ /* these API calls are NOT included in the Toolkit lite version */
+/* SyncML internal function prototypes */
+Ret_t addInfo(InstanceInfoPtr_t pInfo) MGR_FUNC;
+InstanceInfoPtr_t findInfo(InstanceID_t id) MGR_FUNC;
+Ret_t removeInfo(InstanceID_t id) MGR_FUNC;
+#endif
+
+#endif // !defined(NOWSM)
+
+Ret_t mgrResetWorkspace (InstanceID_t id) MGR_FUNC;
+
+#endif // ifndef _MGR_H
diff --git a/src/syncml_tk/src/sml/mgr/inc/mgrutil.h b/src/syncml_tk/src/sml/mgr/inc/mgrutil.h
new file mode 100755
index 0000000..72dae73
--- /dev/null
+++ b/src/syncml_tk/src/sml/mgr/inc/mgrutil.h
@@ -0,0 +1,180 @@
+/**
+ * @file
+ * SyncML API for freeing SyncML C structures
+ *
+ * @target_system all
+ * @target_os all
+ * @description Definitions for internal use within the SyncML implementation
+ */
+
+
+
+/*
+ * Copyright Notice
+ * Copyright (c) Ericsson, IBM, Lotus, Matsushita Communication
+ * Industrial Co., Ltd., Motorola, Nokia, Openwave Systems, Inc.,
+ * Palm, Inc., Psion, Starfish Software, Symbian, Ltd. (2001).
+ * All Rights Reserved.
+ * Implementation of all or part of any Specification may require
+ * licenses under third party intellectual property rights,
+ * including without limitation, patent rights (such a third party
+ * may or may not be a Supporter). The Sponsors of the Specification
+ * are not responsible and shall not be held responsible in any
+ * manner for identifying or failing to identify any or all such
+ * third party intellectual property rights.
+ *
+ * THIS DOCUMENT AND THE INFORMATION CONTAINED HEREIN ARE PROVIDED
+ * ON AN "AS IS" BASIS WITHOUT WARRANTY OF ANY KIND AND ERICSSON, IBM,
+ * LOTUS, MATSUSHITA COMMUNICATION INDUSTRIAL CO. LTD, MOTOROLA,
+ * NOKIA, PALM INC., PSION, STARFISH SOFTWARE AND ALL OTHER SYNCML
+ * SPONSORS DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+ * BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
+ * HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
+ * SHALL ERICSSON, IBM, LOTUS, MATSUSHITA COMMUNICATION INDUSTRIAL CO.,
+ * LTD, MOTOROLA, NOKIA, PALM INC., PSION, STARFISH SOFTWARE OR ANY
+ * OTHER SYNCML SPONSOR BE LIABLE TO ANY PARTY FOR ANY LOSS OF
+ * PROFITS, LOSS OF BUSINESS, LOSS OF USE OF DATA, INTERRUPTION OF
+ * BUSINESS, OR FOR DIRECT, INDIRECT, SPECIAL OR EXEMPLARY, INCIDENTAL,
+ * PUNITIVE OR CONSEQUENTIAL DAMAGES OF ANY KIND IN CONNECTION WITH
+ * THIS DOCUMENT OR THE INFORMATION CONTAINED HEREIN, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH LOSS OR DAMAGE.
+ *
+ * The above notice and this paragraph must be included on all copies
+ * of this document that are made.
+ *
+ */
+
+
+
+#ifndef _MGR_UTIL_H
+ #define _MGR_UTIL_H
+
+
+/* Prototypes of exported SyncML API functions */
+SML_API Ret_t smlFreeProtoElement(VoidPtr_t pProtoElement) MGR_FUNC;
+SML_API void smlFreePcdata(SmlPcdataPtr_t pPcdata) MGR_FUNC;
+SML_API void smlFreePcdataList(SmlPcdataListPtr_t list) MGR_FUNC;
+
+SML_API void smlFreeSyncHdr(SmlSyncHdrPtr_t pSyncHdr) MGR_FUNC;
+SML_API void smlFreeSync(SmlSyncPtr_t pSync) MGR_FUNC;
+SML_API void smlFreeGeneric(SmlGenericCmdPtr_t pGenericCmd) MGR_FUNC;
+SML_API void smlFreeAlert(SmlAlertPtr_t pAlert) MGR_FUNC;
+SML_API void smlFreeAtomic(SmlAtomicPtr_t pAtomic) MGR_FUNC;
+#if (defined EXEC_SEND || defined EXEC_RECEIVE)
+ SML_API void smlFreeExec(SmlExecPtr_t pExec) MGR_FUNC;
+#endif
+SML_API void smlFreeGetPut(SmlPutPtr_t pGetPut) MGR_FUNC;
+SML_API void smlFreeMap(SmlMapPtr_t pMap) MGR_FUNC;
+SML_API void smlFreeResults(SmlResultsPtr_t pResults) MGR_FUNC;
+#if (defined SEARCH_SEND || defined SEARCH_RECEIVE)
+ SML_API void smlFreeSearch(SmlSearchPtr_t pSearch) MGR_FUNC;
+#endif
+SML_API void smlFreeStatus(SmlStatusPtr_t pStatus) MGR_FUNC;
+SML_API void smlFreeCredPtr(SmlCredPtr_t pCred) MGR_FUNC;
+SML_API void smlFreeChalPtr(SmlChalPtr_t pChal) MGR_FUNC;
+SML_API void smlFreeRecordFieldFilterPtr(SmlRecordOrFieldFilterPtr_t pRecordFieldFilter) MGR_FUNC;
+SML_API void smlFreeFilterPtr(SmlFilterPtr_t pFilter) MGR_FUNC;
+SML_API void smlFreeSourceTargetParentPtr(SmlSourceParentPtr_t pSourceTargetParent) MGR_FUNC;
+SML_API void smlFreeSourceTargetPtr(SmlSourcePtr_t pSourceTarget) MGR_FUNC;
+SML_API void smlFreeSourceList(SmlSourceListPtr_t pSourceList) MGR_FUNC;
+SML_API void smlFreeSourceRefList(SmlSourceRefListPtr_t pSourceRefList) MGR_FUNC;
+SML_API void smlFreeTargetRefList(SmlTargetRefListPtr_t pTargetRefList) MGR_FUNC;
+SML_API void smlFreeItemPtr(SmlItemPtr_t pItem) MGR_FUNC;
+SML_API void smlFreeItemList(SmlItemListPtr_t pItemList) MGR_FUNC;
+SML_API void smlFreeMapItemPtr(SmlMapItemPtr_t pMapItem) MGR_FUNC;
+SML_API void smlFreeMapItemList(SmlMapItemListPtr_t pMapItemList) MGR_FUNC;
+
+#ifdef __USE_METINF__
+SML_API void smlFreeMetinfAnchor(SmlMetInfAnchorPtr_t data) MGR_FUNC;
+SML_API void smlFreeMetinfMem(SmlMetInfMemPtr_t data) MGR_FUNC;
+SML_API void smlFreeMetinfMetinf(SmlMetInfMetInfPtr_t data) MGR_FUNC;
+#endif
+#ifdef __USE_DEVINF__
+SML_API void smlFreeDevInfDatastore(SmlDevInfDatastorePtr_t data) MGR_FUNC;
+SML_API void smlFreeDevInfDatastoreList(SmlDevInfDatastoreListPtr_t data) MGR_FUNC;
+SML_API void smlFreeDevInfXmitList(SmlDevInfXmitListPtr_t data) MGR_FUNC;
+SML_API void smlFreeDevInfXmit(SmlDevInfXmitPtr_t data) MGR_FUNC;
+SML_API void smlFreeDevInfDSMem(SmlDevInfDSMemPtr_t data) MGR_FUNC;
+SML_API void smlFreeDevInfSynccap(SmlDevInfSyncCapPtr_t data) MGR_FUNC;
+SML_API void smlFreeDevInfExt(SmlDevInfExtPtr_t data) MGR_FUNC;
+SML_API void smlFreeDevInfExtList(SmlDevInfExtListPtr_t data) MGR_FUNC;
+SML_API void smlFreeDevInfCTData(SmlDevInfCTDataPtr_t data) MGR_FUNC;
+SML_API void smlFreeDevInfCTDataList(SmlDevInfCTDataListPtr_t data) MGR_FUNC;
+SML_API void smlFreeDevInfCTDataProp(SmlDevInfCTDataPropPtr_t data) MGR_FUNC;
+SML_API void smlFreeDevInfCTDataPropList(SmlDevInfCTDataPropListPtr_t data) MGR_FUNC;
+SML_API void smlFreeDevInfCTCap(SmlDevInfCTCapPtr_t data) MGR_FUNC;
+SML_API void smlFreeDevInfCtcapList(SmlDevInfCtcapListPtr_t data) MGR_FUNC;
+SML_API void smlFreeDevInfFilterCap(SmlDevInfFilterCapPtr_t data) MGR_FUNC;
+SML_API void smlFreeDevInfFilterCapList(SmlDevInfFilterCapListPtr_t data) MGR_FUNC;
+SML_API void smlFreeDevInfDevInf(SmlDevInfDevInfPtr_t data) MGR_FUNC;
+#endif
+#ifndef __SML_LITE__ /* these API calls are NOT included in the Toolkit lite version */
+SML_API String_t smlPcdata2String( SmlPcdataPtr_t pcdata ) MGR_FUNC;
+SML_API SmlPcdataPtr_t smlString2Pcdata( String_t str ) MGR_FUNC;
+SML_API SmlPcdataPtr_t smlPcdataDup(SmlPcdataPtr_t pcdata) MGR_FUNC;
+SML_API MemSize_t smlGetFreeBuffer(InstanceID_t id) MGR_FUNC;
+#endif
+
+#ifdef __USE_ALLOCFUNCS__
+SML_API SmlPcdataPtr_t smlAllocPcdata() MGR_FUNC;
+SML_API SmlPcdataListPtr_t smlAllocPcdataList() MGR_FUNC;
+SML_API SmlChalPtr_t smlAllocChal() MGR_FUNC;
+SML_API SmlCredPtr_t smlAllocCred() MGR_FUNC;
+SML_API SmlRecordOrFieldFilterPtr_t smlAllocRecordFieldFilter() MGR_FUNC;
+SML_API SmlFilterPtr_t smlAllocFilter() MGR_FUNC;
+SML_API SmlSourceParentPtr_t smlAllocSourceParent() MGR_FUNC;
+SML_API SmlTargetParentPtr_t smlAllocTargetParent() MGR_FUNC;
+SML_API SmlSourcePtr_t smlAllocSource() MGR_FUNC;
+SML_API SmlTargetPtr_t smlAllocTarget() MGR_FUNC;
+SML_API SmlSourceListPtr_t smlAllocSourceList() MGR_FUNC;
+SML_API SmlSyncHdrPtr_t smlAllocSyncHdr() MGR_FUNC;
+SML_API SmlItemPtr_t smlAllocItem() MGR_FUNC;
+SML_API SmlItemListPtr_t smlAllocItemList() MGR_FUNC;
+SML_API SmlGenericCmdPtr_t smlAllocGeneric() MGR_FUNC;
+SML_API SmlAddPtr_t smlAllocAdd() MGR_FUNC;
+SML_API SmlCopyPtr_t smlAllocCopy() MGR_FUNC;
+SML_API SmlMovePtr_t smlAllocMove() MGR_FUNC;
+SML_API SmlReplacePtr_t smlAllocReplace() MGR_FUNC;
+SML_API SmlDeletePtr_t smlAllocDelete() MGR_FUNC;
+SML_API SmlAlertPtr_t smlAllocAlert() MGR_FUNC;
+SML_API SmlAtomicPtr_t smlAllocAtomic() MGR_FUNC;
+SML_API SmlSequencePtr_t smlAllocSequence() MGR_FUNC;
+SML_API SmlSyncPtr_t smlAllocSync() MGR_FUNC;
+SML_API SmlExecPtr_t smlAllocExec() MGR_FUNC;
+SML_API SmlGetPtr_t smlAllocGet() MGR_FUNC;
+SML_API SmlPutPtr_t smlAllocPut() MGR_FUNC;
+SML_API SmlMapItemPtr_t smlAllocMapItem() MGR_FUNC;
+SML_API SmlMapItemListPtr_t smlAllocMapItemList() MGR_FUNC;
+SML_API SmlMapPtr_t smlAllocMap() MGR_FUNC;
+SML_API SmlResultsPtr_t smlAllocResults() MGR_FUNC;
+SML_API SmlSearchPtr_t smlAllocSearch() MGR_FUNC;
+SML_API SmlTargetRefListPtr_t smlAllocTargetRefList() MGR_FUNC;
+SML_API SmlSourceRefListPtr_t smlAllocSourceRefList() MGR_FUNC;
+SML_API SmlStatusPtr_t smlAllocStatus() MGR_FUNC;
+SML_API SmlUnknownProtoElementPtr_t smlAllocUnknownProtoElement() MGR_FUNC;
+#ifdef __USE_METINF__
+SML_API SmlMetInfMetInfPtr_t smlAllocMetInfMetInf() MGR_FUNC;
+SML_API SmlMetInfAnchorPtr_t smlAllocMetInfAnchor() MGR_FUNC;
+SML_API SmlMetInfMemPtr_t smlAllocMetInfMem() MGR_FUNC;
+#endif // MetInf
+
+#ifdef __USE_DEVINF__
+SML_API SmlDevInfExtPtr_t smlAllocDevInfExt() MGR_FUNC;
+SML_API SmlDevInfExtListPtr_t smlAllocDevInfExtList() MGR_FUNC;
+SML_API SmlDevInfSyncCapPtr_t smlAllocDevInfSyncCap() MGR_FUNC;
+SML_API SmlDevInfCTDataPtr_t smlAllocDevInfCTData() MGR_FUNC;
+SML_API SmlDevInfCTDataListPtr_t smlAllocDevInfCTDataList() MGR_FUNC;
+SML_API SmlDevInfCTDataPropPtr_t smlAllocDevInfCTDataProp() MGR_FUNC;
+SML_API SmlDevInfCTDataPropListPtr_t smlAllocDevInfCTDataPropList() MGR_FUNC;
+SML_API SmlDevInfCTCapPtr_t smlAllocDevInfCTCap() MGR_FUNC;
+SML_API SmlDevInfCtcapListPtr_t smlAllocDevInfCtcapList() MGR_FUNC;
+SML_API SmlDevInfDSMemPtr_t smlAllocDevInfDSMem() MGR_FUNC;
+SML_API SmlDevInfXmitPtr_t smlAllocDevInfXmit() MGR_FUNC;
+SML_API SmlDevInfXmitListPtr_t smlAllocDevInfXmitList() MGR_FUNC;
+SML_API SmlDevInfDatastorePtr_t smlAllocDevInfDatastore() MGR_FUNC;
+SML_API SmlDevInfDatastoreListPtr_t smlAllocDevInfDatastoreList() MGR_FUNC;
+SML_API SmlDevInfDevInfPtr_t smlAllocDevInfDevInf() MGR_FUNC;
+#endif // DevInf
+#endif // AllocFuncs
+#endif // MgrUtil.h
diff --git a/src/syncml_tk/src/sml/mgr/win/libinit.c b/src/syncml_tk/src/sml/mgr/win/libinit.c
new file mode 100755
index 0000000..94f4c26
--- /dev/null
+++ b/src/syncml_tk/src/sml/mgr/win/libinit.c
@@ -0,0 +1,95 @@
+/**
+ * @file
+ * Managing DLL
+ *
+ * @target_system Windows
+ * @target_os Windows 9x/NT
+ * @description Platform dependant DLL-entry-function
+ */
+
+/*
+ * Copyright Notice
+ * Copyright (c) Ericsson, IBM, Lotus, Matsushita Communication
+ * Industrial Co., Ltd., Motorola, Nokia, Openwave Systems, Inc.,
+ * Palm, Inc., Psion, Starfish Software, Symbian, Ltd. (2001).
+ * All Rights Reserved.
+ * Implementation of all or part of any Specification may require
+ * licenses under third party intellectual property rights,
+ * including without limitation, patent rights (such a third party
+ * may or may not be a Supporter). The Sponsors of the Specification
+ * are not responsible and shall not be held responsible in any
+ * manner for identifying or failing to identify any or all such
+ * third party intellectual property rights.
+ *
+ * THIS DOCUMENT AND THE INFORMATION CONTAINED HEREIN ARE PROVIDED
+ * ON AN "AS IS" BASIS WITHOUT WARRANTY OF ANY KIND AND ERICSSON, IBM,
+ * LOTUS, MATSUSHITA COMMUNICATION INDUSTRIAL CO. LTD, MOTOROLA,
+ * NOKIA, PALM INC., PSION, STARFISH SOFTWARE AND ALL OTHER SYNCML
+ * SPONSORS DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+ * BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
+ * HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
+ * SHALL ERICSSON, IBM, LOTUS, MATSUSHITA COMMUNICATION INDUSTRIAL CO.,
+ * LTD, MOTOROLA, NOKIA, PALM INC., PSION, STARFISH SOFTWARE OR ANY
+ * OTHER SYNCML SPONSOR BE LIABLE TO ANY PARTY FOR ANY LOSS OF
+ * PROFITS, LOSS OF BUSINESS, LOSS OF USE OF DATA, INTERRUPTION OF
+ * BUSINESS, OR FOR DIRECT, INDIRECT, SPECIAL OR EXEMPLARY, INCIDENTAL,
+ * PUNITIVE OR CONSEQUENTIAL DAMAGES OF ANY KIND IN CONNECTION WITH
+ * THIS DOCUMENT OR THE INFORMATION CONTAINED HEREIN, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH LOSS OR DAMAGE.
+ *
+ * The above notice and this paragraph must be included on all copies
+ * of this document that are made.
+ *
+ */
+
+/*************************************************************************
+ * Definitions
+ *************************************************************************/
+
+#include "syncml_tk_prefix_file.h" // %%% luz: needed for precompiled headers in eVC++
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#undef WIN32_LEAN_AND_MEAN
+#include <stdio.h>
+
+
+/* SyncML internal function prototypes */
+BOOL APIENTRY DllMain (HINSTANCE hInst, DWORD reason, LPVOID reserved);
+
+/**
+ * This routine is called by the Mingw32, Cygwin32 or VC++ C run
+ * time library init code, or the Borland DllEntryPoint routine. It
+ * is responsible for initializing various dynamically loaded
+ * libraries.
+ *
+ * @param hInst (IN)
+ * Library instance handle.
+ * @param reason (IN)
+ * Reason this function is being called.
+ * @param reserved (IN)
+ * Not used.
+ * @return TRUE on sucess,\n
+ * FALSE on failure.
+ */
+BOOL APIENTRY DllMain(HINSTANCE hInst, DWORD reason, LPVOID reserved)
+{
+
+ switch (reason)
+ {
+ case DLL_PROCESS_ATTACH:
+ break;
+
+ case DLL_PROCESS_DETACH:
+ break;
+
+ case DLL_THREAD_ATTACH:
+ break;
+
+ case DLL_THREAD_DETACH:
+ break;
+ }
+ return TRUE;
+}
+
diff --git a/src/syncml_tk/src/sml/wsm/all/wsm.c b/src/syncml_tk/src/sml/wsm/all/wsm.c
new file mode 100755
index 0000000..41fff54
--- /dev/null
+++ b/src/syncml_tk/src/sml/wsm/all/wsm.c
@@ -0,0 +1,1246 @@
+/**
+ * @file
+ * SyncML WorkSpace Manager
+ *
+ * @target_system All
+ * @target_os All
+ * @description Workspace Manager API
+ * Manages the SyncML document in memory.
+ */
+
+
+/*
+ * Copyright Notice
+ * Copyright (c) Ericsson, IBM, Lotus, Matsushita Communication
+ * Industrial Co., Ltd., Motorola, Nokia, Openwave Systems, Inc.,
+ * Palm, Inc., Psion, Starfish Software, Symbian, Ltd. (2001).
+ * All Rights Reserved.
+ * Implementation of all or part of any Specification may require
+ * licenses under third party intellectual property rights,
+ * including without limitation, patent rights (such a third party
+ * may or may not be a Supporter). The Sponsors of the Specification
+ * are not responsible and shall not be held responsible in any
+ * manner for identifying or failing to identify any or all such
+ * third party intellectual property rights.
+ *
+ * THIS DOCUMENT AND THE INFORMATION CONTAINED HEREIN ARE PROVIDED
+ * ON AN "AS IS" BASIS WITHOUT WARRANTY OF ANY KIND AND ERICSSON, IBM,
+ * LOTUS, MATSUSHITA COMMUNICATION INDUSTRIAL CO. LTD, MOTOROLA,
+ * NOKIA, PALM INC., PSION, STARFISH SOFTWARE AND ALL OTHER SYNCML
+ * SPONSORS DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+ * BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
+ * HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
+ * SHALL ERICSSON, IBM, LOTUS, MATSUSHITA COMMUNICATION INDUSTRIAL CO.,
+ * LTD, MOTOROLA, NOKIA, PALM INC., PSION, STARFISH SOFTWARE OR ANY
+ * OTHER SYNCML SPONSOR BE LIABLE TO ANY PARTY FOR ANY LOSS OF
+ * PROFITS, LOSS OF BUSINESS, LOSS OF USE OF DATA, INTERRUPTION OF
+ * BUSINESS, OR FOR DIRECT, INDIRECT, SPECIAL OR EXEMPLARY, INCIDENTAL,
+ * PUNITIVE OR CONSEQUENTIAL DAMAGES OF ANY KIND IN CONNECTION WITH
+ * THIS DOCUMENT OR THE INFORMATION CONTAINED HEREIN, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH LOSS OR DAMAGE.
+ *
+ * The above notice and this paragraph must be included on all copies
+ * of this document that are made.
+ *
+ */
+
+#include "syncml_tk_prefix_file.h"
+
+#ifndef NOWSM
+// if no WSM, we can leave this one out completely
+
+/*************************************************************************/
+/* Definitions */
+/*************************************************************************/
+
+#include "wsm.h"
+
+#include "wsm_sm.h"
+#include "smldef.h"
+#include "libmem.h"
+#include "libstr.h"
+#include "liblock.h" // for THREADDEBUGPRINTF %%% luz
+#include "mgr.h" // for global anchor
+
+
+/** WSM buffer flags */
+#define WSM_VALID_F (Byte_t) 0x01
+#define WSM_LOCKED_F (Byte_t) 0x02
+
+#define WSM_MEMH_UNUSED -1
+
+#ifndef __SML_LITE__
+
+/* Global Vars */
+/* =========== */
+
+/* defines for convient use of global anchor */
+
+#define wsmRet (mgrGetSyncMLAnchor())->wsmGlobals->wsmRet
+#define initWasCalled (mgrGetSyncMLAnchor())->wsmGlobals->initWasCalled
+#define maxWsmAvailMem (mgrGetSyncMLAnchor())->syncmlOptions->maxWorkspaceAvailMem
+#define wsmBuf (mgrGetSyncMLAnchor())->wsmGlobals->wsmBuf
+#define wsmIndex (mgrGetSyncMLAnchor())->wsmGlobals->wsmIndex
+
+void createDataStructs(void);
+
+void createDataStructs() {
+ if ( (mgrGetSyncMLAnchor())->wsmGlobals == NULL ) {
+ if ( ((mgrGetSyncMLAnchor())->wsmGlobals=smlLibMalloc(sizeof(WsmGlobals_t))) == 0 ) {
+ return;
+ }
+ smlLibMemset((mgrGetSyncMLAnchor())->wsmGlobals, 0, sizeof(WsmGlobals_t));
+ wsmRet = 0;
+ initWasCalled = 0;
+ wsmIndex = 0;
+#ifdef __ANSI_C__
+ (mgrGetSyncMLAnchor())->wsmGlobals->wsmSm = NULL;
+#endif
+#ifdef __PALM_OS__
+ (mgrGetSyncMLAnchor())->wsmGlobals->wsmSm.smMemH = 0;
+ (mgrGetSyncMLAnchor())->wsmGlobals->wsmSm.smLocked = 0;
+#endif
+#ifdef __EPOC_OS__
+ (mgrGetSyncMLAnchor())->wsmGlobals->wsmSm = NULL;
+#endif
+ }
+}
+#define freeDataStructs() smlLibFree((mgrGetSyncMLAnchor())->wsmGlobals)
+
+
+/* private functions prototypes */
+static Short_t getNextFreeEntry();
+static Short_t lookup(MemHandle_t memH);
+static MemHandle_t nameToHandle(String_t name);
+static Short_t deleteBufferHandle(MemHandle_t memH);
+static Short_t resetBufferGlobals(MemHandle_t memH);
+static Byte_t isValidMemH(MemHandle_t memH);
+static Byte_t isLockedMemH(MemHandle_t memH);
+static Byte_t isMemAvailable(MemSize_t memToAlloc);
+
+
+
+
+/*************************************************************************/
+/* Internal Functions */
+/*************************************************************************/
+
+
+
+/**
+ * Get next free buffer entry.
+ * Returns index of next free entry, or -1 if buffer table is full.
+ */
+static Short_t getNextFreeEntry() {
+ Short_t i;
+
+ for ( i=0; i < MAX_WSM_BUFFERS; ++i )
+ if ( wsmBuf[i].memH == WSM_MEMH_UNUSED )
+ return i;
+
+ return -1;
+}
+
+
+
+/**
+ * Get buffer table index for memH.
+ * Returns -1 if memH not found.
+ */
+static Short_t lookup(MemHandle_t memH) {
+ Short_t i;
+
+ // first check cache
+ if ( wsmBuf[wsmIndex].memH == memH )
+ return wsmIndex;
+
+ // search through buffer
+ for ( i=0; (i < MAX_WSM_BUFFERS) && (wsmBuf[i].memH != memH); ++i )
+ ;
+ if ( i < MAX_WSM_BUFFERS ) {
+ wsmIndex = i;
+ return i;
+ }
+ else {
+ return -1; // memH not found
+ }
+}
+
+
+/**
+ * Find memory handle corresponding to name.
+ * Return WSM_MEMH_UNUSED, if name not found in wsmBuf.
+ */
+static MemHandle_t nameToHandle(String_t name) {
+ int i;
+
+ // first check cache
+ if ( (wsmBuf[wsmIndex].bufName != NULL) &&
+ (smlLibStrcmp(wsmBuf[wsmIndex].bufName, name) == 0) )
+ return wsmBuf[wsmIndex].memH;
+
+ // search through buffer
+ for ( i=0; ((i < MAX_WSM_BUFFERS) &&
+ (wsmBuf[i].bufName == NULL ? 1 :
+ smlLibStrcmp(wsmBuf[i].bufName, name) != 0)); ++i )
+ ;
+ if ( i < MAX_WSM_BUFFERS )
+ return wsmBuf[i].memH;
+ else {
+ return WSM_MEMH_UNUSED; // name not found
+ }
+}
+
+
+
+/**
+ * Delete memory handle from buffer.
+ * Return -1, if handle not found.
+ */
+static Short_t deleteBufferHandle(MemHandle_t memH) {
+ if ( (wsmIndex = lookup(memH)) < 0 )
+ return -1; // handle not found
+
+ // reset the values
+ wsmBuf[wsmIndex].memH = WSM_MEMH_UNUSED;
+ wsmBuf[wsmIndex].pFirstFree = NULL;
+ wsmBuf[wsmIndex].pFirstData = NULL;
+ wsmBuf[wsmIndex].size = 0;
+ wsmBuf[wsmIndex].usedBytes = 0;
+ //wsmBuf[wsmIndex].flags = ~WSM_VALID_F;
+ wsmBuf[wsmIndex].flags = ((Byte_t) ~WSM_VALID_F);
+ smlLibFree(wsmBuf[wsmIndex].bufName); // free mem
+ wsmBuf[wsmIndex].bufName = NULL;
+
+ return 0;
+}
+
+
+/**
+ * Reset values in buffer table for entry memH.
+ * If memH doesn't exist create an entry.
+ * Return index to memH in buffer table,
+ * or -1 if table is full
+ */
+static Short_t resetBufferGlobals(MemHandle_t memH) {
+ if ( (wsmIndex = lookup(memH)) < 0 ) {
+ // create new one
+ if ( (wsmIndex = getNextFreeEntry()) < 0 )
+ return -1; // buffer table full
+ wsmBuf[wsmIndex].memH = memH;
+ } else
+ // use existing one, which has to be reset prior usage
+ smlLibFree(wsmBuf[wsmIndex].bufName); // free mem
+
+ // reset the values
+ wsmBuf[wsmIndex].pFirstFree = NULL;
+ wsmBuf[wsmIndex].pFirstData = NULL;
+ wsmBuf[wsmIndex].size = 0;
+ wsmBuf[wsmIndex].usedBytes = 0;
+ wsmBuf[wsmIndex].flags = WSM_VALID_F;
+ wsmBuf[wsmIndex].bufName = NULL;
+
+ return wsmIndex;
+}
+
+
+static Byte_t isValidMemH(MemHandle_t memH) {
+ return (Byte_t) (wsmBuf[lookup(memH)].flags & WSM_VALID_F);
+}
+
+static Byte_t isLockedMemH(MemHandle_t memH) {
+ return (Byte_t) (wsmBuf[lookup(memH)].flags & WSM_LOCKED_F);
+}
+
+static Byte_t isMemAvailable(MemSize_t memToAlloc) {
+ int i;
+ MemSize_t actMem = memToAlloc;
+ if ( maxWsmAvailMem == 0 )
+ return 1; // no memsize restrictions
+ for (i=0; i < MAX_WSM_BUFFERS; ++i) {
+ if ( wsmBuf[i].memH != WSM_MEMH_UNUSED )
+ actMem += wsmBuf[i].size;
+ }
+ return ((Byte_t)(actMem <= maxWsmAvailMem));
+}
+
+
+
+/*************************************************************************/
+/* External Functions */
+/*************************************************************************/
+
+
+
+/**
+ * Initializes all Workspace Manager related resources.
+ * Should only be called once!
+ *
+ * @pre This is the first function call to WSM
+ * @post All WSM resources are initialized
+ * @param wsmOpts (IN)
+ * WSM options, valid options are:
+ * - maxAvailMem\n
+ * Maximal amount of memory which wsm can use for the buffers.\n
+ * 0 == no limitation
+ * @return
+ * - SML_ERR_OK, if O.K.
+ * - SML_ERR_INVALID_OPTIONS, if wsmOpts is not valid
+ * - SML_ERR_NOT_ENOUGH_SPACE, if not enough available memory
+ * - SML_ERR_WRONG_USAGE, if wsmInit was already called
+ */
+Ret_t wsmInit (const WsmOptions_t *wsmOpts) {
+ int i;
+
+ // create global datastructs
+ createDataStructs();
+
+ if (NULL == mgrGetSyncMLAnchor()->wsmGlobals) {
+ return SML_ERR_NOT_ENOUGH_SPACE;
+ }
+
+ // check if init was already called
+ if ( initWasCalled )
+ return SML_ERR_WRONG_USAGE;
+
+ // check options
+ if ( wsmOpts != NULL ) {
+ if ( wsmOpts->maxAvailMem > 0 ) {
+ maxWsmAvailMem = wsmOpts->maxAvailMem;
+ }
+ }
+
+ // init resources
+ for ( i=0; i < MAX_WSM_BUFFERS; ++i )
+ wsmBuf[i].memH = WSM_MEMH_UNUSED;
+ wsmIndex = 0;
+ initWasCalled = (Byte_t) 1;
+
+ return wsmRet=SML_ERR_OK;
+}
+
+
+/**
+ * Creates and opens a new buffer with name bufName and size bufSize.
+ * If a buffer with name bufName already exists, the existing buffer
+ * is resized to bufSize.
+ *
+ * @pre bufSize > 0
+ * @post
+ * - handle refers to buffer bufName
+ * - BufferSize = size
+ * @param bufName (IN)
+ * Name of buffer to be created
+ * @param bufSize (IN)
+ * Size of buffer to be created
+ * @param wsmH (OUT)
+ * Handle to new buffer
+ * @return
+ * - SML_ERR_OK, if O.K.
+ * - SML_ERR_INVALID_SIZE, if bufSize <= 0
+ * - SML_ERR_NOT_ENOUGH_SPACE, if available memory < bufSize
+ * - SML_ERR_WSM_BUF_TABLE_FULL, if buffer table is full
+ * - SML_ERR_WRONG_USAGE, if wsmInit wasn't called before
+ * @see wsmDestroy
+ */
+Ret_t wsmCreate (String_t bufName, MemSize_t bufSize, MemHandle_t *wsmH) {
+
+ *wsmH = 0; // 0 in case of error
+
+ if ( ! initWasCalled )
+ return SML_ERR_WRONG_USAGE;
+
+ // check buffer space
+ if ( getNextFreeEntry() == -1 ) {
+ return wsmRet=SML_ERR_WSM_BUF_TABLE_FULL;
+ }
+ // check for maxMemAvailable
+ if ( ! isMemAvailable(bufSize) ) {
+ return SML_ERR_NOT_ENOUGH_SPACE;
+ }
+
+ // create buffer
+ if ( (wsmRet = smCreate(bufName, bufSize, wsmH)) != SML_ERR_OK ) {
+ if ( wsmRet == SML_ERR_WRONG_USAGE ) { // buffer already exists
+ // resize existing buffer
+ // open buffer
+ if ( (wsmRet = smOpen(bufName, wsmH)) != SML_ERR_OK ) {
+ return wsmRet=SML_ERR_NOT_ENOUGH_SPACE;
+ }
+ // resize buffer
+ if ( (wsmRet = smSetSize(*wsmH, bufSize)) != SML_ERR_OK ) {
+ return wsmRet=SML_ERR_NOT_ENOUGH_SPACE;
+ }
+ }
+ else {
+ return wsmRet;
+ }
+ }
+
+ // reset buffer vars
+ wsmIndex = resetBufferGlobals(*wsmH);
+
+ // set buffer vars
+ wsmBuf[wsmIndex].size = bufSize;
+ wsmBuf[wsmIndex].bufName = smlLibStrdup(bufName);
+
+ if (wsmBuf[wsmIndex].bufName == NULL) {
+ smClose(*wsmH);
+ smDestroy(bufName);
+ return wsmRet=SML_ERR_NOT_ENOUGH_SPACE;
+ }
+
+
+ return wsmRet=SML_ERR_OK;
+}
+
+
+/**
+ * Open existing buffer with name bufName.
+ *
+ * @pre WSM knows bufName
+ * @post wsmH refers to buffer bufName
+ * @param bufName (IN)
+ * Name of buffer to be opened
+ * @param wsmH (OUT)
+ * Handle to new buffer (OUT)
+ * @return
+ * - SML_ERR_OK, if O.K.
+ * - SML_WRONG_PARAM, if bufName is unknown
+ * @see wsmClose
+ */
+Ret_t wsmOpen (String_t bufName, MemHandle_t *wsmH){
+
+ // open buffer
+ if ( (wsmRet = smOpen(bufName, wsmH)) != SML_ERR_OK ) {
+ return wsmRet;
+ }
+
+ // reset buffer vars
+ wsmIndex = resetBufferGlobals(*wsmH);
+
+ // set buf vars
+ wsmRet = smGetSize(*wsmH, &wsmBuf[wsmIndex].size);
+ wsmBuf[wsmIndex].bufName = smlLibStrdup(bufName);
+
+ return wsmRet=SML_ERR_OK;
+}
+
+
+/**
+ * Close an open buffer.
+ *
+ * @pre
+ * - handle is valid
+ * - handle is unlocked
+ * @post handle is not known to WSM any more
+ * @param wsmH (IN)
+ * Handle to the open buffer
+ * @return
+ * - SML_ERR_OK, if O.K.
+ * - SML_ERR_INVALID_HANDLE, if handle was invalid
+ * - SML_ERR_WRONG_USAGE, if handle was still locked
+ * @see wsmOpen
+ */
+Ret_t wsmClose (MemHandle_t wsmH) {
+
+ // check if handle is invalid
+ if ( ! isValidMemH(wsmH) ) {
+ return wsmRet=SML_ERR_INVALID_HANDLE;
+ }
+
+ // close handle
+ if ( (wsmRet = smClose(wsmH)) != SML_ERR_OK ) {
+ return wsmRet;
+ }
+ wsmRet = deleteBufferHandle(wsmH);
+
+ return wsmRet=SML_ERR_OK;
+}
+
+
+/**
+ * Destroy existing buffer with name bufName.
+ *
+ * @pre
+ * - WSM knows bufName
+ * - handle is unlocked
+ * @post
+ * - buffer is not known to WSM any more
+ * - all resources connected to this buffer are freed
+ * @param bufName (IN)
+ * Name of buffer to be opened
+ * @return
+ * - SML_ERR_OK, if O.K.
+ * - SML_ERR_WRONG_PARAM, if bufName is unknown to WSM
+ * - SML_ERR_WRONG_USAGE, if handle was still locked
+ * @see wsmCreate
+ */
+Ret_t wsmDestroy (String_t bufName) {
+
+ // free resources
+ if ( (wsmRet = wsmClose(nameToHandle(bufName))) != SML_ERR_OK ) {
+ return wsmRet;
+ }
+
+ // free buffer
+ if ( (wsmRet = smDestroy(bufName)) != SML_ERR_OK ) {
+ return wsmRet;
+ }
+
+ return wsmRet=SML_ERR_OK;
+}
+
+
+/**
+ * Terminate WSM; free all buffers and resources.
+ *
+ * @pre all handles must be unlocked
+ * @post all resources are freed
+ * @return
+ * - SML_ERR_OK, if O.K.
+ * - SML_ERR_WRONG_USAGE, if a handle was still locked
+ */
+Ret_t wsmTerminate (void) {
+ int i;
+
+ // free all WSM resources
+ for (i=0; i < MAX_WSM_BUFFERS; ++i) {
+ if ( wsmBuf[i].memH != WSM_MEMH_UNUSED )
+ if ( wsmDestroy(wsmBuf[i].bufName) == SML_ERR_WRONG_USAGE ) {
+ return SML_ERR_WRONG_USAGE;
+ }
+ }
+
+ // free global DataStructs
+ freeDataStructs();
+
+ return SML_ERR_OK;
+}
+
+
+/**
+ * Tell Workspace Manager the number of bytes already processed.
+ *
+ * @pre
+ * - handle is locked
+ * - handle is valid
+ * - noBytes <= wsmGetUsedSize
+ * @post
+ * - noBytes starting at wsmGetPtr() position are deleted;
+ * - remaining bytes are copied to wsmGetPtr(SML_FIRST_FREE_ITEM) position
+ * - wsmGetUsedSize -= noBytes
+ * - wsmGetFreeSize += noBytes
+ * @param wsmH (IN)
+ * Handle to the open buffer
+ * @param noBytes (IN)
+ * Number of bytes already processed from buffer.
+ * @return
+ * - SML_ERR_OK, if O.K.
+ * - SML_ERR_INVALID_HANDLE, if handle was invalid
+ * - SML_ERR_WRONG_USAGE, if handle was not locked
+ * - SML_ERR_INVALID_SIZE, if noBytes > wsmGetUsedSize
+ * @see wsmGetFreeSize
+ */
+Ret_t wsmProcessedBytes (MemHandle_t wsmH, MemSize_t noBytes) {
+
+ // check if handle is invalid
+ if ( ! isValidMemH(wsmH) ) {
+ return wsmRet=SML_ERR_INVALID_HANDLE;
+ }
+ // check if handle is unlocked
+ if ( ! isLockedMemH(wsmH) ) {
+ return wsmRet=SML_ERR_WRONG_USAGE;
+ }
+
+ wsmIndex = lookup(wsmH);
+
+ if ( noBytes > wsmBuf[wsmIndex].usedBytes ) {
+ return wsmRet=SML_ERR_INVALID_SIZE;
+ }
+
+ // adapt usedSize
+ wsmBuf[wsmIndex].usedBytes -= noBytes;
+
+ // move memory
+ // check return ?????
+ smlLibMemmove(wsmBuf[wsmIndex].pFirstData,
+ (wsmBuf[wsmIndex].pFirstData + noBytes),
+ wsmBuf[wsmIndex].usedBytes);
+
+ // move pFirstFree
+ wsmBuf[wsmIndex].pFirstFree -= noBytes;
+
+ return wsmRet=SML_ERR_OK;
+}
+
+
+/**
+ * Locks handle wsmH and get a pointer to the contents of wsmH.
+ * RequestedPos describes the position in the buffer to which the returned
+ * pointer should point. Valid values are:
+ * - SML_FIRST_DATA_ITEM
+ * - SML_FIRST_FREE_ITEM
+ *
+ * @pre
+ * - handle is unlocked
+ * - handle is valid
+ * @post
+ * - handle is locked
+ * - points to first data item, or first free item.
+ * @param wsmH (IN)
+ * Handle to the open buffer
+ * @param requestedPos (IN)
+ * Requested position of the returned pointer
+ * - SML_FIRST_DATA_ITEM : points to first data entry
+ * - SML_FIRST_FREE_ITEM : points to first free entry
+ * @param pMem (OUT)
+ * Pointer to requested memory
+ * @return
+ * - SML_ERR_OK, if O.K.
+ * - SML_ERR_INVALID_HANDLE, if handle was invalid
+ * - SML_ERR_WRONG_USAGE, if handle was still locked
+ * - SML_ERR_UNSPECIFIC, if requested position is unknown, or lock failed
+ * @see wsmUnlockH
+ */
+Ret_t wsmLockH (MemHandle_t wsmH, SmlBufPtrPos_t requestedPos,
+ MemPtr_t *pMem) {
+
+ // check if handle is invalid
+ if ( ! isValidMemH(wsmH) ) {
+ return wsmRet=SML_ERR_INVALID_HANDLE;
+ }
+ // check if handle is locked
+ if ( isLockedMemH(wsmH) ) {
+ return wsmRet=SML_ERR_WRONG_USAGE;
+ }
+
+ // lock
+ if ( (wsmRet = smLock(wsmH, pMem)) != SML_ERR_OK ) {
+ return wsmRet=SML_ERR_UNSPECIFIC;
+ }
+
+ // set local pointers
+ wsmIndex = lookup(wsmH);
+ wsmBuf[wsmIndex].pFirstData = *pMem;
+ wsmBuf[wsmIndex].pFirstFree = *pMem + wsmBuf[wsmIndex].usedBytes;
+ wsmBuf[wsmIndex].flags |= WSM_LOCKED_F;
+
+ switch (requestedPos) {
+ case SML_FIRST_DATA_ITEM:
+ *pMem = wsmBuf[wsmIndex].pFirstData;
+ break;
+ case SML_FIRST_FREE_ITEM:
+ *pMem = wsmBuf[wsmIndex].pFirstFree;
+ break;
+ default:
+ return wsmRet=SML_ERR_UNSPECIFIC;
+ }
+
+ return wsmRet=SML_ERR_OK;
+}
+
+
+/**
+ * Returns the remaining unused bytes in the buffer.
+ *
+ * @pre handle is valid
+ * @post wsmGetFreeSize = BufferSize - wsmGetUsedSize
+ * @param wsmH (IN)
+ * Handle to the open buffer
+ * @param freeSize (OUT)
+ * Number of bytes which are unused in this buffer
+ * @return
+ * - SML_ERR_OK, if O.K.
+ * - SML_ERR_INVALID_HANDLE, if handle was invalid
+ * @see wsmGetUsedSize, wsmProcessedBytes
+ */
+Ret_t wsmGetFreeSize(MemHandle_t wsmH, MemSize_t *freeSize) {
+
+ // check if handle is invalid
+ if ( ! isValidMemH(wsmH) ) {
+ return wsmRet=SML_ERR_INVALID_HANDLE;
+ }
+
+ wsmIndex = lookup(wsmH);
+
+ *freeSize = wsmBuf[wsmIndex].size - wsmBuf[wsmIndex].usedBytes;
+
+ return wsmRet=SML_ERR_OK;
+}
+
+
+/**
+ * Returns the number of bytes used in the buffer.
+ *
+ * @pre handle is valid
+ * @post usedSize = BufferSize - wsmGetFreeSize
+ * @param wsmH (IN)
+ * Handle to the open buffer
+ * @param usedSize (OUT)
+ * Number of bytes which are already used in this buffer
+ * @return
+ * - SML_ERR_OK, if O.K.
+ * - SML_ERR_INVALID_HANDLE, if handle was invalid
+ * @see wsmGetFreeSize, wsmSetUsedSize
+ */
+Ret_t wsmGetUsedSize(MemHandle_t wsmH, MemSize_t *usedSize) {
+
+ // check if handle is invalid
+ if ( ! isValidMemH(wsmH) ) {
+ return wsmRet=SML_ERR_INVALID_HANDLE;
+ }
+
+ wsmIndex = lookup(wsmH);
+
+ *usedSize = wsmBuf[wsmIndex].usedBytes;
+
+ return wsmRet=SML_ERR_OK;
+}
+
+
+/**
+ * Unlock handle wsmH.
+ * After this call all pointers to this memory handle are invalid
+ * and should no longer be used.
+ *
+ * @pre
+ * - handle is locked
+ * - handle is valid
+ * @post handle is unlocked
+ * @param wsmH Handle to unlock (OUT)
+ * @return
+ * - SML_ERR_OK, if O.K.
+ * - SML_ERR_INVALID_HANDLE, if handle was invalid
+ * - SML_ERR_WRONG_USAGE, if handle was not locked
+ * - SML_ERR_UNSPECIFIC, unlock failed
+ * @see wsmLockH
+ */
+Ret_t wsmUnlockH (MemHandle_t wsmH) {
+
+ // check if handle is invalid
+ if ( ! isValidMemH(wsmH) ) {
+ return wsmRet=SML_ERR_INVALID_HANDLE;
+ }
+ // check if handle is already unlocked
+ if ( ! isLockedMemH(wsmH) ) {
+ return wsmRet=SML_ERR_WRONG_USAGE;
+ }
+
+ // unlock
+ if ( (wsmRet = smUnlock(wsmH)) != SML_ERR_OK ) {
+ return wsmRet=SML_ERR_UNSPECIFIC;
+ }
+
+ // set local pointers
+ wsmIndex = lookup(wsmH);
+ wsmBuf[wsmIndex].pFirstData = NULL;
+ wsmBuf[wsmIndex].pFirstFree = NULL;
+ wsmBuf[wsmIndex].flags &= ~WSM_LOCKED_F;
+
+ return wsmRet=SML_ERR_OK;
+}
+
+
+/**
+ * Tell Workspace how many data were written into buffer.
+ *
+ * @pre
+ * - handle is valid
+ * - usedSize <= wsmGetFreeSize
+ * - handle is locked
+ * @post
+ * - wsmGetUsedSize += usedSize
+ * - wsmGetFreeSize -= usedSize
+ * - instancePtr += usedSize
+ * @param wsmH (IN)
+ * Handle to the open buffer
+ * @param usedSize (IN)
+ * Number of bytes which were written into buffer
+ * @return
+ * - SML_ERR_OK, if O.K.
+ * - SML_ERR_INVALID_HANDLE, if handle was invalid
+ * - SML_ERR_INVALID_SIZE, if usedSize <= wsmGetFreeSize
+ * @see wsmGetUsedSize
+ */
+Ret_t wsmSetUsedSize (MemHandle_t wsmH, MemSize_t usedSize) {
+
+ // check if handle is invalid
+ if ( ! isValidMemH(wsmH) ) {
+ return wsmRet=SML_ERR_INVALID_HANDLE;
+ }
+ // check if handle is unlocked
+ if ( ! isLockedMemH(wsmH) ) {
+ return wsmRet=SML_ERR_WRONG_USAGE;
+ }
+
+ wsmIndex = lookup(wsmH);
+
+ // usedSize > freeSize?
+ if ( usedSize >
+ (wsmBuf[wsmIndex].size - wsmBuf[wsmIndex].usedBytes) ) {
+ return wsmRet=SML_ERR_INVALID_SIZE;
+ }
+
+ // adapt usedSize
+ wsmBuf[wsmIndex].usedBytes += usedSize;
+
+ // move pFirstFree
+ wsmBuf[wsmIndex].pFirstFree += usedSize;
+
+ return wsmRet=SML_ERR_OK;
+}
+
+/**
+ * Reset the Workspace
+ *
+ * @post all data is lost. The FirstFree Position equals
+ * the First Data position
+ * @param wsmH (IN)
+ * Handle to the open buffer
+ * @return
+ * - SML_ERR_OK, if O.K.
+ */
+Ret_t wsmReset (MemHandle_t wsmH) {
+
+ wsmIndex = lookup(wsmH);
+ wsmBuf[wsmIndex].pFirstFree = wsmBuf[wsmIndex].pFirstFree - wsmBuf[wsmIndex].usedBytes ;
+ wsmBuf[wsmIndex].pFirstData = wsmBuf[wsmIndex].pFirstFree;
+ wsmBuf[wsmIndex].usedBytes = 0;
+
+ return SML_ERR_OK;
+}
+
+/*======================================================================================*/
+#else
+/* WSM_LITE Version - uses only one buffer*/
+/*======================================================================================*/
+
+
+/* Global Vars */
+/* =========== */
+
+/* defines for convient use of global anchor */
+#define wsmRet (mgrGetSyncMLAnchor())->wsmGlobals->wsmRet
+#define initWasCalled (mgrGetSyncMLAnchor())->wsmGlobals->initWasCalled
+#define maxWsmAvailMem (mgrGetSyncMLAnchor())->syncmlOptions->maxWorkspaceAvailMem
+#define wsmBuf (mgrGetSyncMLAnchor())->wsmGlobals->wsmBuf
+#define wsmIndex (mgrGetSyncMLAnchor())->wsmGlobals->wsmIndex
+
+void createDataStructs(void);
+
+void createDataStructs() {
+ if ( (mgrGetSyncMLAnchor())->wsmGlobals == NULL ) {
+ if ( ((mgrGetSyncMLAnchor())->wsmGlobals=smlLibMalloc(sizeof(WsmGlobals_t))) == 0 ) {
+ return;
+ }
+ wsmRet = 0;
+ initWasCalled = 0;
+ wsmIndex = 0;
+#ifdef __ANSI_C__
+ (mgrGetSyncMLAnchor())->wsmGlobals->wsmSm = NULL;
+#endif
+#ifdef __PALM_OS__
+ (mgrGetSyncMLAnchor())->wsmGlobals->wsmSm.smMemH = 0;
+ (mgrGetSyncMLAnchor())->wsmGlobals->wsmSm.smLocked = 0;
+#endif
+#ifdef __EPOC_OS__
+ (mgrGetSyncMLAnchor())->wsmGlobals->wsmSm = NULL;
+#endif
+ }
+}
+#define freeDataStructs() smlLibFree((mgrGetSyncMLAnchor())->wsmGlobals)
+
+
+
+/* private functions prototypes */
+static Short_t getNextFreeEntry();
+static Short_t deleteBufferHandle(MemHandle_t memH);
+static Short_t resetBufferGlobals(MemHandle_t memH);
+static Byte_t isMemAvailable(MemSize_t memToAlloc);
+
+
+
+
+/*************************************************************************/
+/* Internal Functions */
+/*************************************************************************/
+
+
+
+
+/**
+ * Delete memory handle from buffer.
+ * Return -1, if handle not found.
+ */
+static Short_t deleteBufferHandle(MemHandle_t memH) {
+ // reset the values
+ wsmBuf[0].memH = WSM_MEMH_UNUSED;
+ wsmBuf[0].pFirstFree = NULL;
+ wsmBuf[0].pFirstData = NULL;
+ wsmBuf[0].size = 0;
+ wsmBuf[0].usedBytes = 0;
+ wsmBuf[0].flags = ~WSM_VALID_F;
+ smlLibFree(wsmBuf[0].bufName); // free mem
+ wsmBuf[0].bufName = NULL;
+
+ return 0;
+}
+
+
+/**
+ * Reset values in buffer table for entry memH.
+ * If memH doesn't exist create an entry.
+ * Return index to memH in buffer table,
+ * or -1 if table is full
+ */
+static Short_t resetBufferGlobals(MemHandle_t memH) {
+ if ( (wsmBuf[0].memH != memH) && (wsmBuf[0].memH == WSM_MEMH_UNUSED)) {
+ // create new one
+ wsmBuf[0].memH = memH;
+ } else {
+ // use existing one, which has to be reset prior usage
+ smlLibFree(wsmBuf[0].bufName); // free mem
+ }
+ // reset the values
+ wsmBuf[0].pFirstFree = NULL;
+ wsmBuf[0].pFirstData = NULL;
+ wsmBuf[0].size = 0;
+ wsmBuf[0].usedBytes = 0;
+ wsmBuf[0].flags = WSM_VALID_F;
+ wsmBuf[0].bufName = NULL;
+
+ return 0;
+}
+
+
+static Byte_t isMemAvailable(MemSize_t memToAlloc) {
+ MemSize_t actMem = memToAlloc;
+ if ( maxWsmAvailMem == 0 )
+ return 1; // no memsize restrictions
+ if ( wsmBuf[0].memH != WSM_MEMH_UNUSED )
+ actMem += wsmBuf[0].size;
+ return (actMem <= maxWsmAvailMem);
+}
+
+
+
+/*************************************************************************/
+/* External Functions */
+/*************************************************************************/
+
+
+Ret_t wsmInit (const WsmOptions_t *wsmOpts) {
+ // create global datastructs
+ createDataStructs();
+
+ if (NULL == mgrGetSyncMLAnchor()->wsmGlobals) {
+ return SML_ERR_NOT_ENOUGH_SPACE;
+ }
+
+
+ // check if init was already called
+ if ( initWasCalled )
+ return SML_ERR_WRONG_USAGE;
+
+ // check options
+ if ( wsmOpts != NULL ) {
+ if ( wsmOpts->maxAvailMem > 0 ) {
+ maxWsmAvailMem = wsmOpts->maxAvailMem;
+ }
+ }
+
+ // init resources
+ wsmBuf[0].memH = WSM_MEMH_UNUSED;
+ wsmIndex = 0;
+ initWasCalled = (Byte_t) 1;
+
+ return wsmRet=SML_ERR_OK;
+}
+
+
+Ret_t wsmCreate (String_t bufName, MemSize_t bufSize, MemHandle_t *wsmH) {
+
+ *wsmH = 0; // 0 in case of error
+
+ if ( ! initWasCalled )
+ return SML_ERR_WRONG_USAGE;
+
+ // check buffer space
+ if ( wsmBuf[0].memH != WSM_MEMH_UNUSED ) {
+ return wsmRet=SML_ERR_WSM_BUF_TABLE_FULL;
+ }
+ // check for maxMemAvailable
+ if ( ! isMemAvailable(bufSize) ) {
+ return SML_ERR_NOT_ENOUGH_SPACE;
+ }
+
+ // create buffer
+ if ( (wsmRet = smCreate(bufName, bufSize, wsmH)) != SML_ERR_OK ) {
+ if ( wsmRet == SML_ERR_WRONG_USAGE ) { // buffer already exists
+ // resize existing buffer
+ // open buffer
+ if ( (wsmRet = smOpen(bufName, wsmH)) != SML_ERR_OK ) {
+ return wsmRet=SML_ERR_NOT_ENOUGH_SPACE;
+ }
+ // resize buffer
+ if ( (wsmRet = smSetSize(*wsmH, bufSize)) != SML_ERR_OK ) {
+ return wsmRet=SML_ERR_NOT_ENOUGH_SPACE;
+ }
+ }
+ else {
+ return wsmRet;
+ }
+ }
+
+ // reset buffer vars
+ resetBufferGlobals(*wsmH);
+
+ // set buffer vars
+ wsmBuf[0].size = bufSize;
+ wsmBuf[0].bufName = smlLibStrdup(bufName);
+
+ return wsmRet=SML_ERR_OK;
+}
+
+
+Ret_t wsmOpen (String_t bufName, MemHandle_t *wsmH){
+
+ // open buffer
+ if ( (wsmRet = smOpen(bufName, wsmH)) != SML_ERR_OK ) {
+ return wsmRet;
+ }
+
+ // reset buffer vars
+ resetBufferGlobals(*wsmH);
+
+ // set buf vars
+ wsmRet = smGetSize(*wsmH, &wsmBuf[0].size);
+ wsmBuf[0].bufName = smlLibStrdup(bufName);
+
+ return wsmRet=SML_ERR_OK;
+}
+
+
+Ret_t wsmClose (MemHandle_t wsmH) {
+
+ // check if handle is invalid
+ // must be buffer 0, as only this one exists
+ if ( ! ((wsmBuf[0].memH == wsmH) || (wsmBuf[0].flags & WSM_VALID_F)) ) {
+ return wsmRet=SML_ERR_INVALID_HANDLE;
+ }
+
+ // close handle
+ if ( (wsmRet = smClose(wsmH)) != SML_ERR_OK ) {
+ return wsmRet;
+ }
+ wsmRet = deleteBufferHandle(wsmH);
+
+ return wsmRet=SML_ERR_OK;
+}
+
+
+Ret_t wsmDestroy (String_t bufName) {
+
+ // free resources
+ if ( (wsmRet = wsmClose(wsmBuf[0].memH)) != SML_ERR_OK ) {
+ return wsmRet;
+ }
+
+ // free buffer
+ if ( (wsmRet = smDestroy(bufName)) != SML_ERR_OK ) {
+ return wsmRet;
+ }
+
+ return wsmRet=SML_ERR_OK;
+}
+
+
+Ret_t wsmTerminate () {
+ int i;
+
+ // free all WSM resources
+ for (i=0; i < MAX_WSM_BUFFERS; ++i) {
+ if ( wsmBuf[i].memH != WSM_MEMH_UNUSED )
+ if ( wsmDestroy(wsmBuf[i].bufName) == SML_ERR_WRONG_USAGE ) {
+ return SML_ERR_WRONG_USAGE;
+ }
+ }
+
+ // free global DataStructs
+ freeDataStructs();
+
+ return SML_ERR_OK;
+}
+
+
+Ret_t wsmProcessedBytes (MemHandle_t wsmH, MemSize_t noBytes) {
+
+ // check if handle is invalid
+ // must be buffer 0, as only this one exists
+ if ( ! ((wsmBuf[0].memH == wsmH) || (wsmBuf[0].flags & WSM_VALID_F)) ) {
+ return wsmRet=SML_ERR_INVALID_HANDLE;
+ }
+ // check if handle is unlocked
+ // must be buffer 0, as only this one exists
+ if ( ! ((wsmBuf[0].memH == wsmH) || (wsmBuf[0].flags & WSM_LOCKED_F)) ) {
+ return wsmRet=SML_ERR_WRONG_USAGE;
+ }
+
+ if ( noBytes > wsmBuf[0].usedBytes ) {
+ return wsmRet=SML_ERR_INVALID_SIZE;
+ }
+
+ // adapt usedSize
+ wsmBuf[0].usedBytes -= noBytes;
+
+ // move memory
+ // check return ?????
+ smlLibMemmove(wsmBuf[0].pFirstData,
+ (wsmBuf[0].pFirstData + noBytes),
+ wsmBuf[0].usedBytes);
+
+ // move pFirstFree
+ wsmBuf[0].pFirstFree -= noBytes;
+
+ return wsmRet=SML_ERR_OK;
+}
+
+
+Ret_t wsmLockH (MemHandle_t wsmH, SmlBufPtrPos_t requestedPos,
+ MemPtr_t *pMem) {
+
+ // check if handle is invalid
+ // must be buffer 0, as only this one exists
+ if ( ! ((wsmBuf[0].memH == wsmH) || (wsmBuf[0].flags & WSM_VALID_F)) ) {
+ return wsmRet=SML_ERR_INVALID_HANDLE;
+ }
+ // check if handle is locked
+ // must be buffer 0, as only this one exists
+ if ( ! ((wsmBuf[0].memH == wsmH) || (wsmBuf[0].flags & WSM_LOCKED_F)) ) {
+ return wsmRet=SML_ERR_WRONG_USAGE;
+ }
+
+ // lock
+ if ( (wsmRet = smLock(wsmH, pMem)) != SML_ERR_OK ) {
+ return wsmRet=SML_ERR_UNSPECIFIC;
+ }
+
+ // set local pointers
+ wsmBuf[0].pFirstData = *pMem;
+ wsmBuf[0].pFirstFree = *pMem + wsmBuf[0].usedBytes;
+ wsmBuf[0].flags |= WSM_LOCKED_F;
+
+ switch (requestedPos) {
+ case SML_FIRST_DATA_ITEM:
+ *pMem = wsmBuf[0].pFirstData;
+ break;
+ case SML_FIRST_FREE_ITEM:
+ *pMem = wsmBuf[0].pFirstFree;
+ break;
+ default:
+ return wsmRet=SML_ERR_UNSPECIFIC;
+ }
+
+ return wsmRet=SML_ERR_OK;
+}
+
+
+Ret_t wsmGetFreeSize(MemHandle_t wsmH, MemSize_t *freeSize) {
+
+ // check if handle is invalid
+ // must be buffer 0, as only this one exists
+ if ( ! ((wsmBuf[0].memH == wsmH) || (wsmBuf[0].flags & WSM_VALID_F)) ) {
+ return wsmRet=SML_ERR_INVALID_HANDLE;
+ }
+
+ *freeSize = wsmBuf[0].size - wsmBuf[0].usedBytes;
+
+ return wsmRet=SML_ERR_OK;
+}
+
+
+Ret_t wsmGetUsedSize(MemHandle_t wsmH, MemSize_t *usedSize) {
+
+ // check if handle is invalid
+ // must be buffer 0, as only this one exists
+ if ( ! ((wsmBuf[0].memH == wsmH) || (wsmBuf[0].flags & WSM_VALID_F)) ) {
+ return wsmRet=SML_ERR_INVALID_HANDLE;
+ }
+
+ *usedSize = wsmBuf[0].usedBytes;
+
+ return wsmRet=SML_ERR_OK;
+}
+
+
+Ret_t wsmUnlockH (MemHandle_t wsmH) {
+
+ // check if handle is invalid
+ // must be buffer 0, as only this one exists
+ if ( ! ((wsmBuf[0].memH == wsmH) || (wsmBuf[0].flags & WSM_VALID_F)) ) {
+ return wsmRet=SML_ERR_INVALID_HANDLE;
+ }
+ // check if handle is already unlocked
+ // must be buffer 0, as only this one exists
+ if ( ! ((wsmBuf[0].memH == wsmH) || (wsmBuf[0].flags & WSM_LOCKED_F)) ) {
+ return wsmRet=SML_ERR_WRONG_USAGE;
+ }
+
+ // unlock
+ if ( (wsmRet = smUnlock(wsmH)) != SML_ERR_OK ) {
+ return wsmRet=SML_ERR_UNSPECIFIC;
+ }
+
+ // set local pointers
+ wsmBuf[0].pFirstData = NULL;
+ wsmBuf[0].pFirstFree = NULL;
+ wsmBuf[0].flags &= ~WSM_LOCKED_F;
+
+ return wsmRet=SML_ERR_OK;
+}
+
+
+Ret_t wsmSetUsedSize (MemHandle_t wsmH, MemSize_t usedSize) {
+
+ // check if handle is invalid
+ // must be buffer 0, as only this one exists
+ if ( ! ((wsmBuf[0].memH == wsmH) || (wsmBuf[0].flags & WSM_VALID_F)) ) {
+ return wsmRet=SML_ERR_INVALID_HANDLE;
+ }
+ // check if handle is unlocked
+ // must be buffer 0, as only this one exists
+ if ( ! ((wsmBuf[0].memH == wsmH) || (wsmBuf[0].flags & WSM_LOCKED_F)) ) {
+ return wsmRet=SML_ERR_WRONG_USAGE;
+ }
+
+ // usedSize > freeSize?
+ if ( usedSize >
+ (wsmBuf[0].size - wsmBuf[0].usedBytes) ) {
+ return wsmRet=SML_ERR_INVALID_SIZE;
+ }
+
+ // adapt usedSize
+ wsmBuf[0].usedBytes += usedSize;
+
+ // move pFirstFree
+ wsmBuf[0].pFirstFree += usedSize;
+
+ return wsmRet=SML_ERR_OK;
+}
+
+
+Ret_t wsmReset (MemHandle_t wsmH) {
+ wsmBuf[0].pFirstFree = wsmBuf[0].pFirstFree - wsmBuf[0].usedBytes ;
+ wsmBuf[0].pFirstData = wsmBuf[0].pFirstFree;
+ wsmBuf[0].usedBytes = 0;
+
+ return SML_ERR_OK;
+}
+
+
+#endif // #ifndef __SML_LITE__
+
+#endif // #ifndef NOWSM
+
diff --git a/src/syncml_tk/src/sml/wsm/inc/wsm.h b/src/syncml_tk/src/sml/wsm/inc/wsm.h
new file mode 100755
index 0000000..909cdbe
--- /dev/null
+++ b/src/syncml_tk/src/sml/wsm/inc/wsm.h
@@ -0,0 +1,118 @@
+/**
+ * @file
+ * SyncML WorkSpace Manager
+ *
+ * @target_system All
+ * @target_os All
+ * @description Workspace Manager API
+ * Manages the SyncML document in memory.
+ */
+
+
+/*
+ * Copyright Notice
+ * Copyright (c) Ericsson, IBM, Lotus, Matsushita Communication
+ * Industrial Co., Ltd., Motorola, Nokia, Openwave Systems, Inc.,
+ * Palm, Inc., Psion, Starfish Software, Symbian, Ltd. (2001).
+ * All Rights Reserved.
+ * Implementation of all or part of any Specification may require
+ * licenses under third party intellectual property rights,
+ * including without limitation, patent rights (such a third party
+ * may or may not be a Supporter). The Sponsors of the Specification
+ * are not responsible and shall not be held responsible in any
+ * manner for identifying or failing to identify any or all such
+ * third party intellectual property rights.
+ *
+ * THIS DOCUMENT AND THE INFORMATION CONTAINED HEREIN ARE PROVIDED
+ * ON AN "AS IS" BASIS WITHOUT WARRANTY OF ANY KIND AND ERICSSON, IBM,
+ * LOTUS, MATSUSHITA COMMUNICATION INDUSTRIAL CO. LTD, MOTOROLA,
+ * NOKIA, PALM INC., PSION, STARFISH SOFTWARE AND ALL OTHER SYNCML
+ * SPONSORS DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+ * BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
+ * HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
+ * SHALL ERICSSON, IBM, LOTUS, MATSUSHITA COMMUNICATION INDUSTRIAL CO.,
+ * LTD, MOTOROLA, NOKIA, PALM INC., PSION, STARFISH SOFTWARE OR ANY
+ * OTHER SYNCML SPONSOR BE LIABLE TO ANY PARTY FOR ANY LOSS OF
+ * PROFITS, LOSS OF BUSINESS, LOSS OF USE OF DATA, INTERRUPTION OF
+ * BUSINESS, OR FOR DIRECT, INDIRECT, SPECIAL OR EXEMPLARY, INCIDENTAL,
+ * PUNITIVE OR CONSEQUENTIAL DAMAGES OF ANY KIND IN CONNECTION WITH
+ * THIS DOCUMENT OR THE INFORMATION CONTAINED HEREIN, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH LOSS OR DAMAGE.
+ *
+ * The above notice and this paragraph must be included on all copies
+ * of this document that are made.
+ *
+ */
+
+
+
+#ifndef _WSM_H
+#define _WSM_H
+
+#include "smlerr.h"
+#include "smldef.h"
+
+#include "syncml_tk_prefix_file.h" // %%% luz: needed for precompiled headers in eVC++
+
+#ifndef NOWSM
+
+#include "wsm_sm.h"
+
+
+typedef struct WsmOptions_s {
+ MemSize_t maxAvailMem; /**< maximum amount of memory available for all wsm buffers */
+} WsmOptions_t;
+
+
+#ifdef __SML_LITE__
+ #ifdef MAX_WSM_BUFFERS
+ #error "for __SML_LITE__, MAX_WSM_BUFFERS must not be predefined!"
+ #endif
+ #define MAX_WSM_BUFFERS 1
+#else
+ #ifndef MAX_WSM_BUFFERS
+ // use default value of 4 (not much for a multi-connection server)
+ #define MAX_WSM_BUFFERS 4
+ #endif
+#endif
+
+
+/** WSM internal buffer structure */
+typedef struct WsmBuf_s {
+ String_t bufName; /**< external name of buffer */
+ MemHandle_t memH; /**< memory handle */
+ MemPtr_t pFirstFree; /**< pointer to first free element in buffer */
+ MemPtr_t pFirstData; /**< pointer to first data element in buffer */
+ MemSize_t size; /**< size of buffer */
+ MemSize_t usedBytes; /**< used bytes in buffer */
+ Byte_t flags;
+} WsmBuf_t;
+
+
+/** WSM globals for use with global Anchor */
+typedef struct WsmGlobals_s {
+ Ret_t wsmRet; /**< last WSM return code */
+ Byte_t initWasCalled; /**< was wsmInit() called? */
+ WsmBuf_t wsmBuf[MAX_WSM_BUFFERS];
+ Short_t wsmIndex; /**< Index of actual buffer */
+ WsmSmGlobals_t wsmSm; /**< WSM_SM global; device dependent! */
+} *WsmGlobalsPtr_t, WsmGlobals_t;
+
+Ret_t wsmInit (const WsmOptions_t *wsmOpts) WSM_FUNC;
+Ret_t wsmCreate (String_t bufName, MemSize_t bufSize, MemHandle_t *wsmH) WSM_FUNC;
+Ret_t wsmOpen (String_t bufName, MemHandle_t *wsmH) WSM_FUNC;
+Ret_t wsmClose (MemHandle_t wsmH) WSM_FUNC;
+Ret_t wsmDestroy (String_t bufName) WSM_FUNC;
+Ret_t wsmTerminate (void) WSM_FUNC;
+Ret_t wsmProcessedBytes (MemHandle_t wsmH, MemSize_t noBytes) WSM_FUNC;
+Ret_t wsmLockH (MemHandle_t wsmH, SmlBufPtrPos_t requestedPos, MemPtr_t *pMem) WSM_FUNC;
+Ret_t wsmGetFreeSize(MemHandle_t wsmH, MemSize_t *freeSize) WSM_FUNC;
+Ret_t wsmGetUsedSize(MemHandle_t wsmH, MemSize_t *usedSize) WSM_FUNC;
+Ret_t wsmUnlockH (MemHandle_t wsmH) WSM_FUNC;
+Ret_t wsmSetUsedSize (MemHandle_t wsmH, MemSize_t usedSize) WSM_FUNC;
+Ret_t wsmReset (MemHandle_t wsmH) ;
+
+#endif // !defined(NOWSM)
+
+#endif
diff --git a/src/syncml_tk/src/sml/wsm/inc/wsm_sm.h b/src/syncml_tk/src/sml/wsm/inc/wsm_sm.h
new file mode 100755
index 0000000..22ef2b0
--- /dev/null
+++ b/src/syncml_tk/src/sml/wsm/inc/wsm_sm.h
@@ -0,0 +1,103 @@
+/**
+ * @file
+ * SyncML WorkSpace Manager
+ *
+ * @target_system All
+ * @target_os All
+ * @description Storage Management for Workspace Manager API
+ * Encapsulates OS dependent parts of WSM.
+ */
+
+
+/*
+ * Copyright Notice
+ * Copyright (c) Ericsson, IBM, Lotus, Matsushita Communication
+ * Industrial Co., Ltd., Motorola, Nokia, Openwave Systems, Inc.,
+ * Palm, Inc., Psion, Starfish Software, Symbian, Ltd. (2001).
+ * All Rights Reserved.
+ * Implementation of all or part of any Specification may require
+ * licenses under third party intellectual property rights,
+ * including without limitation, patent rights (such a third party
+ * may or may not be a Supporter). The Sponsors of the Specification
+ * are not responsible and shall not be held responsible in any
+ * manner for identifying or failing to identify any or all such
+ * third party intellectual property rights.
+ *
+ * THIS DOCUMENT AND THE INFORMATION CONTAINED HEREIN ARE PROVIDED
+ * ON AN "AS IS" BASIS WITHOUT WARRANTY OF ANY KIND AND ERICSSON, IBM,
+ * LOTUS, MATSUSHITA COMMUNICATION INDUSTRIAL CO. LTD, MOTOROLA,
+ * NOKIA, PALM INC., PSION, STARFISH SOFTWARE AND ALL OTHER SYNCML
+ * SPONSORS DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+ * BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
+ * HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
+ * SHALL ERICSSON, IBM, LOTUS, MATSUSHITA COMMUNICATION INDUSTRIAL CO.,
+ * LTD, MOTOROLA, NOKIA, PALM INC., PSION, STARFISH SOFTWARE OR ANY
+ * OTHER SYNCML SPONSOR BE LIABLE TO ANY PARTY FOR ANY LOSS OF
+ * PROFITS, LOSS OF BUSINESS, LOSS OF USE OF DATA, INTERRUPTION OF
+ * BUSINESS, OR FOR DIRECT, INDIRECT, SPECIAL OR EXEMPLARY, INCIDENTAL,
+ * PUNITIVE OR CONSEQUENTIAL DAMAGES OF ANY KIND IN CONNECTION WITH
+ * THIS DOCUMENT OR THE INFORMATION CONTAINED HEREIN, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH LOSS OR DAMAGE.
+ *
+ * The above notice and this paragraph must be included on all copies
+ * of this document that are made.
+ *
+ */
+
+
+
+#ifndef _WSM_SM_H
+#define _WSM_SM_H
+
+#include <smldef.h>
+
+
+#ifdef __ANSI_C__
+/** sbuffer list */
+typedef struct smWinList_s {
+ char *memName; /**< name of buffer */
+ char *winH; /**< reference to memory block */
+ MemHandle_t memH; /**< handle of memory block */
+ Byte_t locked; /**< is handle locked? */
+ MemSize_t memSize; /**< size of memory block */
+ struct smWinList_s *next; /**< next list item */
+} smWinList_t;
+typedef smWinList_t *WsmSmGlobals_t;
+#endif
+
+#ifdef __PALM_OS__
+#include <PalmOS.h>
+/** dynamic buffer array */
+typedef struct smPalm_s {
+ MemHandle smPalmH; /**< reference to only memory block */
+ MemHandle_t smMemH; /**< handle of only memory block */
+ Byte_t smLocked; /**< is handle locked? */
+} WsmSmGlobals_t;
+#endif
+
+#ifdef __EPOC_OS__
+/** sbuffer list */
+typedef struct smWinList_s {
+ char *memName; /**< name of buffer */
+ char *winH; /**< reference to memory block */
+ MemHandle_t memH; /**< handle of memory block */
+ Byte_t locked; /**< is handle locked? */
+ MemSize_t memSize; /**< size of memory block */
+ struct smWinList_s *next; /**< next list item */
+} smWinList_t;
+typedef smWinList_t *WsmSmGlobals_t;
+#endif
+
+
+Ret_t smCreate (String_t memName, MemSize_t memSize, MemHandle_t *memH) WSM_FUNC;
+Ret_t smOpen (String_t memName, MemHandle_t *memH) WSM_FUNC;
+Ret_t smClose (MemHandle_t memH) WSM_FUNC;
+Ret_t smDestroy (String_t memName) WSM_FUNC;
+Ret_t smLock (MemHandle_t memH, MemPtr_t *pMem) WSM_FUNC;
+Ret_t smUnlock (MemHandle_t memH) WSM_FUNC;
+Ret_t smSetSize (MemHandle_t memH, MemSize_t newSize) WSM_FUNC;
+Ret_t smGetSize (MemHandle_t memH, MemSize_t *actSize) WSM_FUNC;
+
+#endif
+
diff --git a/src/syncml_tk/src/sml/wsm/palm/wsm_sm.c b/src/syncml_tk/src/sml/wsm/palm/wsm_sm.c
new file mode 100755
index 0000000..c87899f
--- /dev/null
+++ b/src/syncml_tk/src/sml/wsm/palm/wsm_sm.c
@@ -0,0 +1,206 @@
+/**
+ * @file
+ * SyncML WorkSpace Manager
+ *
+ * @target_system Palm
+ * @target_os PalmOS 3.5
+ * @description Storage Management for Workspace Manager API.
+ * Palm OS version.
+ */
+
+
+
+/*
+ * Copyright Notice
+ * Copyright (c) Ericsson, IBM, Lotus, Matsushita Communication
+ * Industrial Co., Ltd., Motorola, Nokia, Openwave Systems, Inc.,
+ * Palm, Inc., Psion, Starfish Software, Symbian, Ltd. (2001).
+ * All Rights Reserved.
+ * Implementation of all or part of any Specification may require
+ * licenses under third party intellectual property rights,
+ * including without limitation, patent rights (such a third party
+ * may or may not be a Supporter). The Sponsors of the Specification
+ * are not responsible and shall not be held responsible in any
+ * manner for identifying or failing to identify any or all such
+ * third party intellectual property rights.
+ *
+ * THIS DOCUMENT AND THE INFORMATION CONTAINED HEREIN ARE PROVIDED
+ * ON AN "AS IS" BASIS WITHOUT WARRANTY OF ANY KIND AND ERICSSON, IBM,
+ * LOTUS, MATSUSHITA COMMUNICATION INDUSTRIAL CO. LTD, MOTOROLA,
+ * NOKIA, PALM INC., PSION, STARFISH SOFTWARE AND ALL OTHER SYNCML
+ * SPONSORS DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+ * BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
+ * HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
+ * SHALL ERICSSON, IBM, LOTUS, MATSUSHITA COMMUNICATION INDUSTRIAL CO.,
+ * LTD, MOTOROLA, NOKIA, PALM INC., PSION, STARFISH SOFTWARE OR ANY
+ * OTHER SYNCML SPONSOR BE LIABLE TO ANY PARTY FOR ANY LOSS OF
+ * PROFITS, LOSS OF BUSINESS, LOSS OF USE OF DATA, INTERRUPTION OF
+ * BUSINESS, OR FOR DIRECT, INDIRECT, SPECIAL OR EXEMPLARY, INCIDENTAL,
+ * PUNITIVE OR CONSEQUENTIAL DAMAGES OF ANY KIND IN CONNECTION WITH
+ * THIS DOCUMENT OR THE INFORMATION CONTAINED HEREIN, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH LOSS OR DAMAGE.
+ *
+ * The above notice and this paragraph must be included on all copies
+ * of this document that are made.
+ *
+ */
+
+
+/*************************************************************************
+ * Definitions *
+ *************************************************************************/
+
+
+#include <smldef.h>
+#include <smlerr.h>
+#include <PalmOS.h>
+#include "wsm_sm.h"
+
+
+
+/* Global Vars */
+/* =========== */
+
+#include "mgr.h"
+#define smPalmH (mgrGetSyncMLAnchor())->wsmGlobals->wsmSm.smPalmH
+#define smMemH (mgrGetSyncMLAnchor())->wsmGlobals->wsmSm.smMemH
+#define smLocked (mgrGetSyncMLAnchor())->wsmGlobals->wsmSm.smLocked
+
+
+/*************************************************************************
+ * Internal Functions *
+ *************************************************************************/
+
+
+
+
+
+/*************************************************************************
+ * External Functions *
+ *************************************************************************/
+
+
+Ret_t smCreate (String_t memName, MemSize_t memSize, MemHandle_t *memH) {
+
+ if ( memSize <= 0 ) {
+ return SML_ERR_INVALID_SIZE;
+ }
+
+ // only one create call does make sense under Palm OS
+ if ( smMemH != 0 ) {
+ return SML_ERR_WRONG_USAGE;
+ }
+
+ // set new values
+ smLocked = 0;
+ smMemH = 1;
+ *memH = smMemH;
+
+ // create memory
+ if ( (smPalmH=MemHandleNew(memSize)) == 0 ) {
+ return SML_ERR_NOT_ENOUGH_SPACE;
+ }
+
+ return SML_ERR_OK;
+}
+
+
+Ret_t smOpen (String_t memName, MemHandle_t *memH) {
+
+ if ( smMemH == 0 ) {
+ return SML_ERR_WRONG_PARAM;
+ }
+
+ *memH = smMemH;
+ return SML_ERR_OK;
+}
+
+
+Ret_t smClose (MemHandle_t memH) {
+ Err ret;
+
+ // reset handle
+ if ( (ret=MemHandleFree(smPalmH)) != 0 ) {
+ return SML_ERR_WRONG_USAGE;
+ }
+ smMemH = 0;
+ smLocked = 0;
+
+ return SML_ERR_OK;
+}
+
+
+Ret_t smDestroy (String_t memName) {
+
+ return SML_ERR_OK;
+}
+
+
+Ret_t smLock (MemHandle_t memH, MemPtr_t *pMem) {
+
+ if ( memH != smMemH ) {
+ return SML_ERR_WRONG_PARAM;
+ }
+ if ( smLocked ) {
+ return SML_ERR_WRONG_USAGE;
+ }
+
+ if ( (*pMem = MemHandleLock(smPalmH)) == NULL ) {
+ return SML_ERR_UNSPECIFIC;
+ }
+ smLocked = 1;
+
+ return SML_ERR_OK;
+}
+
+
+Ret_t smUnlock (MemHandle_t memH) {
+
+ if ( memH != smMemH ) {
+ return SML_ERR_WRONG_PARAM;
+ }
+ if ( ! smLocked ) {
+ return SML_ERR_WRONG_USAGE;
+ }
+
+ if ( MemHandleUnlock(smPalmH) != 0 ) {
+ return SML_ERR_UNSPECIFIC;
+ }
+ smLocked = 0;
+
+ return SML_ERR_OK;
+}
+
+
+Ret_t smSetSize (MemHandle_t memH, MemSize_t newSize) {
+ Err ret;
+
+ if ( memH != smMemH ) {
+ return SML_ERR_WRONG_PARAM;
+ }
+ if ( smLocked ) {
+ return SML_ERR_WRONG_USAGE;
+ }
+ if ( newSize <= 0 ) {
+ return SML_ERR_INVALID_SIZE;
+ }
+
+ if ( (ret=MemHandleResize(smPalmH, newSize)) != 0 ) {
+ return SML_ERR_NOT_ENOUGH_SPACE;
+ }
+
+ return SML_ERR_OK;
+}
+
+
+Ret_t smGetSize (MemHandle_t memH, MemSize_t *actSize) {
+
+ if ( memH != smMemH ) {
+ return SML_ERR_WRONG_PARAM;
+ }
+
+ *actSize = MemHandleSize(smPalmH);
+ return SML_ERR_OK;
+}
+
diff --git a/src/syncml_tk/src/sml/wsm/win/wsm_sm.c b/src/syncml_tk/src/sml/wsm/win/wsm_sm.c
new file mode 100755
index 0000000..591ca66
--- /dev/null
+++ b/src/syncml_tk/src/sml/wsm/win/wsm_sm.c
@@ -0,0 +1,481 @@
+/**
+ * @file
+ * SyncML WorkSpace Manager
+ *
+ * @target_system MS Windows
+ * @target_os Windows 98 / NT
+ * @description Storage Management for Workspace Manager API.
+ * MS Windows version.
+ */
+
+/*
+ * Copyright Notice
+ * Copyright (c) Ericsson, IBM, Lotus, Matsushita Communication
+ * Industrial Co., Ltd., Motorola, Nokia, Openwave Systems, Inc.,
+ * Palm, Inc., Psion, Starfish Software, Symbian, Ltd. (2001).
+ * All Rights Reserved.
+ * Implementation of all or part of any Specification may require
+ * licenses under third party intellectual property rights,
+ * including without limitation, patent rights (such a third party
+ * may or may not be a Supporter). The Sponsors of the Specification
+ * are not responsible and shall not be held responsible in any
+ * manner for identifying or failing to identify any or all such
+ * third party intellectual property rights.
+ *
+ * THIS DOCUMENT AND THE INFORMATION CONTAINED HEREIN ARE PROVIDED
+ * ON AN "AS IS" BASIS WITHOUT WARRANTY OF ANY KIND AND ERICSSON, IBM,
+ * LOTUS, MATSUSHITA COMMUNICATION INDUSTRIAL CO. LTD, MOTOROLA,
+ * NOKIA, PALM INC., PSION, STARFISH SOFTWARE AND ALL OTHER SYNCML
+ * SPONSORS DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+ * BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
+ * HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
+ * SHALL ERICSSON, IBM, LOTUS, MATSUSHITA COMMUNICATION INDUSTRIAL CO.,
+ * LTD, MOTOROLA, NOKIA, PALM INC., PSION, STARFISH SOFTWARE OR ANY
+ * OTHER SYNCML SPONSOR BE LIABLE TO ANY PARTY FOR ANY LOSS OF
+ * PROFITS, LOSS OF BUSINESS, LOSS OF USE OF DATA, INTERRUPTION OF
+ * BUSINESS, OR FOR DIRECT, INDIRECT, SPECIAL OR EXEMPLARY, INCIDENTAL,
+ * PUNITIVE OR CONSEQUENTIAL DAMAGES OF ANY KIND IN CONNECTION WITH
+ * THIS DOCUMENT OR THE INFORMATION CONTAINED HEREIN, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH LOSS OR DAMAGE.
+ *
+ * The above notice and this paragraph must be included on all copies
+ * of this document that are made.
+ *
+ */
+
+#include "syncml_tk_prefix_file.h"
+
+#ifndef NOWSM
+// if no WSM, we can leave this one out completely
+
+
+/*************************************************************************/
+/* Definitions */
+/*************************************************************************/
+
+#include "wsm_sm.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include "libmem.h"
+#include "liblock.h" // for THREADDEBUGPRINTF %%% luz
+#include "smldef.h"
+#include "smlerr.h"
+
+
+/* Global Vars */
+/* =========== */
+
+/** root of buffer list */
+
+#include "mgr.h"
+#define root (mgrGetSyncMLAnchor())->wsmGlobals->wsmSm
+
+/* private functions prototypes */
+static Byte_t newListEle(const char *name, smWinList_t **newEle, MemHandle_t *newHandle);
+static Byte_t locateEle(const char *eleName, smWinList_t **p);
+static Byte_t locateH(MemHandle_t memH, smWinList_t **p);
+static void removeEle(const char *eleName);
+
+
+/*************************************************************************/
+/* Internal Functions */
+/*************************************************************************/
+
+/** create new buffer element and assign name to it
+ * return pointer to new element and handle of new element
+ */
+/*
+ SCTSTK - 16/03/2002 S.H. 2002-04-05 : fixed so that it works even if the sequence of buffer termination
+ is not in the reverse order of buffer creation
+ */
+
+// luz %%% NOTE: called only from routines which lock the toolkit already,
+// no separate lock required here
+static Byte_t newListEle(const char *name,
+ smWinList_t **newEle,
+ MemHandle_t *newHandle
+)
+{
+ smWinList_t *p;
+ int i;
+ for ( i=0; *newHandle < MAX_WSM_BUFFERS && (mgrGetSyncMLAnchor())->wsmGlobals->wsmBuf[i].memH != -1; ++i ) {};
+ if (i == MAX_WSM_BUFFERS) return 0;
+ *newHandle=i+1;
+
+ if ( ((*newEle) = smlLibMalloc(sizeof(smWinList_t))) == 0 )
+ return 0; // no more memory
+ if ( ((*newEle)->memName = smlLibMalloc(strlen(name)+1)) == 0 ){
+ smlLibFree(*newEle);
+ return 0; // no more memory
+ }
+ memcpy((*newEle)->memName, name, strlen(name));
+ (*newEle)->memName[strlen(name)] = '\0';
+ if ( root == 0 )
+ root = *newEle;
+ else {
+ p=root;
+ while ( p->next != NULL) p = p->next;
+ p->next = *newEle;
+ }
+ return 1;
+}
+
+
+/**
+ * search for buffer with name eleName and return pointer to it in p.
+ *
+ * @return
+ * - 0 if not found;
+ * - 1 if found
+ */
+// luz %%% NOTE: called only from routines which lock the toolkit already,
+// no separate lock required here
+static Byte_t locateEle(const char *eleName, smWinList_t **p) {
+ *p = root;
+ while ( (*p != NULL) && (strcmp((*p)->memName, eleName) != 0) ) {
+ *p = (*p)->next;
+ }
+ if ( *p == NULL )
+ return 0;
+ else
+ return 1;
+}
+
+/**
+ * search for buffer with memHandle memH and return pointer to it in p.
+ *
+ * @return
+ * - 0 if not found;
+ * - 1 if found
+ */
+// luz %%% NOTE: called only from routines which lock the toolkit already,
+// no separate lock required here
+static Byte_t locateH(MemHandle_t memH, smWinList_t **p) {
+ *p = root;
+ while ( (*p != NULL) && ((*p)->memH != memH) ) {
+ *p = (*p)->next;
+ }
+ if ( *p == NULL )
+ return 0;
+ else
+ return 1;
+}
+
+/**
+ * remove buffer with name eleName from smWinList.
+ */
+// luz %%% NOTE: called only from routines which lock the toolkit already,
+// no separate lock required here
+static void removeEle(const char *eleName) {
+ smWinList_t *act, *old;
+
+ old = act = root;
+ while ( (act != NULL) && (strcmp(act->memName, eleName) != 0) ) {
+ old = act;
+ act = act->next;
+ }
+ if ( act != NULL ) {
+ if ( old == act ) // delete first list ele
+ root = act->next;
+ else
+ old->next = act->next;
+ smlLibFree(act->memName);
+ smlLibFree(act);
+ }
+}
+
+
+
+
+/*************************************************************************/
+/* External Functions */
+/*************************************************************************/
+
+
+/**
+ * Creates a new memory block with name memName and size memSize.
+ *
+ * @pre
+ * - OS does not know memName
+ * - memSize > 0
+ * @post
+ * - memName exists with size memSize
+ * - memH refers to new memory block.
+ * @param memName (IN)
+ * Name of new memory block
+ * @param memSize (IN)
+ * Size of new memory block
+ * @param memH (OUT)
+ * Handle to new memory block
+ * @return
+ * - SML_ERR_OK, if O.K.
+ * - SML_ERR_WRONG_USAGE, if memName is already known to the OS
+ * - SML_ERR_INVALID_SIZE, if memSize <= 0
+ * - SML_ERR_NOT_ENOUGH_SPACE, if available memory < memSize
+ * @see smDestroy
+ */
+Ret_t smCreate (String_t memName, MemSize_t memSize, MemHandle_t *memH) {
+ smWinList_t *pEle; // pointer to new buffer
+
+ if ( memSize <= 0 ) {
+ return SML_ERR_INVALID_SIZE;
+ }
+ if ( locateEle(memName, &pEle) ) {
+ return SML_ERR_WRONG_USAGE;
+ }
+
+ // create new element in buffer list
+ if ( ! newListEle(memName, &pEle, memH) ) {
+ return SML_ERR_NOT_ENOUGH_SPACE;
+ }
+
+ // create memory
+ if ( (pEle->winH=smlLibMalloc(memSize)) == 0 ) {
+ smlLibFree(pEle->memName);
+ smlLibFree(pEle);
+ return SML_ERR_NOT_ENOUGH_SPACE;
+ }
+
+ // set new values
+ pEle->locked = 0;
+ pEle->memH = *memH;
+ pEle->memSize = memSize;
+ pEle->next = NULL;
+
+ return SML_ERR_OK;
+}
+
+
+/**
+ * Open connection to memory block with name memName.
+ *
+ * @pre OS does know memName
+ * @post memH refers to memory block memName
+ * @param memName (IN)
+ * Name of memory block to open\n
+ * Windows version: Name is ignored
+ * @param memH (OUT)
+ * Handle to opened memory block
+ * @return
+ * - SML_ERR_OK, if O.K.
+ * - SML_ERR_WRONG_PARAM, if memName is unknown
+ * @see smClose
+ */
+Ret_t smOpen (String_t memName, MemHandle_t *memH) {
+ smWinList_t *pEle; // pointer to buffer element
+
+ if ( ! locateEle(memName, &pEle) ) {
+ return SML_ERR_WRONG_PARAM;
+ }
+
+ *memH = pEle->memH;
+ return SML_ERR_OK;
+}
+
+
+/**
+ * Close link to memory block.
+ *
+ * @pre
+ * - memH is a valid memory block handle
+ * - memH is unlocked
+ * - no pointers to records are in use
+ * @post memH is not valid anymore
+ * @param memH (IN)
+ * Handle to close
+ * @return
+ * - SML_ERR_OK, if O.K.
+ * - SML_ERR_WRONG_USAGE, if memH is locked or unknown
+ * @see smOpen
+ */
+Ret_t smClose (MemHandle_t memH) {
+ smWinList_t *pEle; // pointer to buffer element
+
+ if ( ! locateH(memH, &pEle) ) {
+ return SML_ERR_WRONG_USAGE;
+ }
+
+ // reset handle
+ smlLibFree(pEle->winH);
+ pEle->memH = 0;
+ pEle->locked = 0;
+ pEle->memSize = 0;
+
+ return SML_ERR_OK;
+}
+
+
+/**
+ * Remove memory block memName within OS.
+ *
+ * @pre
+ * - memName is a valid memory block name
+ * - memory block is not in use (i.e. no handles and
+ * pointers to this memory block are in use)
+ * @post memName is not a valid memory block name anymore
+ * @param memName (IN)
+ * Name of memory block to remove
+ * @return
+ * - SML_ERR_OK, if O.K.
+ * - SML_ERR_WRONG_PARAM, if memName is unknown
+ * - SML_ERR_WRONG_USAGE, if memory block is still locked
+ * @see smCreate
+ */
+Ret_t smDestroy (String_t memName) {
+ smWinList_t *pEle; // pointer to buffer element
+
+ if ( ! locateEle(memName, &pEle) ) {
+ return SML_ERR_WRONG_PARAM;
+ }
+ if ( pEle->locked ) {
+ return SML_ERR_WRONG_USAGE;
+ }
+
+ // remove memory buffer
+ removeEle(memName);
+
+ return SML_ERR_OK;
+}
+
+
+/**
+ * Map memory block memH to local address space.
+ *
+ * @pre
+ * - memH is a valid handle
+ * - memory block is not locked
+ * @post
+ * - pMem points to memory block memH
+ * - memory block is locked
+ * @param memH (IN)
+ * Handle to memory block
+ * @param pMem (OUT)
+ * Pointer to memory block memH mapped in local address space
+ * @return
+ * - SML_ERR_OK, if O.K.
+ * - SML_ERR_WRONG_PARAM, if memH is unknown
+ * - SML_ERR_WRONG_USAGE, if memH was already locked
+ * - SML_ERR_UNSPECIFIC, if lock failed
+ * @see smUnlock
+ */
+Ret_t smLock (MemHandle_t memH, MemPtr_t *pMem) {
+ smWinList_t *pEle; // pointer to buffer element
+
+ if ( ! locateH(memH, &pEle) ) {
+ return SML_ERR_WRONG_PARAM;
+ }
+ if ( pEle->locked ) {
+ return SML_ERR_WRONG_USAGE;
+ }
+
+ *pMem = (MemPtr_t)pEle->winH;
+ pEle->locked = 1;
+
+ return SML_ERR_OK;
+}
+
+
+/**
+ * Free pointer mapped to memH memory block.
+ *
+ * @pre
+ * - memH is a valid handle
+ * - memory block is locked
+ * @post memory block is unlocked
+ * @param memH (IN)
+ * Handle to memory block
+ * @return
+ * - SML_ERR_OK, if O.K.
+ * - SML_ERR_WRONG_PARAM, if memH is unknown
+ * - SML_ERR_WRONG_USAGE, if memH was already unlocked
+ * - SML_ERR_UNSPECIFIC, if unlock failed
+ * @see smLock
+ */
+Ret_t smUnlock (MemHandle_t memH) {
+ smWinList_t *pEle; // pointer to buffer element
+
+ if ( ! locateH(memH, &pEle) ) {
+ return SML_ERR_WRONG_PARAM;
+ }
+ if ( ! pEle->locked ) {
+ return SML_ERR_WRONG_USAGE;
+ }
+
+ pEle->locked = 0;
+
+ return SML_ERR_OK;
+}
+
+
+/**
+ * Set size of memory block memH to newSize.
+ *
+ * @pre
+ * - memH is a valid handle
+ * - newSize > 0
+ * - memory block is unlocked
+ * @post memory block size = newSize
+ * @param memH (IN)
+ * Handle to memory block
+ * @param newSize (IN)
+ * New size of memory block
+ * @return
+ * - SML_ERR_OK, if O.K.
+ * - SML_ERR_WRONG_PARAM, if memH is unknown
+ * - SML_ERR_WRONG_USAGE, if memH is locked
+ * - SML_ERR_INVALID_SIZE, if newSize <= 0
+ * - SML_ERR_NOT_ENOUGH_SPACE, if available memory < newSize
+ * @see smGetSize
+ */
+Ret_t smSetSize (MemHandle_t memH, MemSize_t newSize) {
+ smWinList_t *pEle; // pointer to buffer element
+
+ if ( ! locateH(memH, &pEle) ) {
+ return SML_ERR_WRONG_PARAM;
+ }
+ if ( pEle->locked ) {
+ return SML_ERR_WRONG_USAGE;
+ }
+ if ( newSize <= 0 ) {
+ return SML_ERR_INVALID_SIZE;
+ }
+
+ smlLibFree(pEle->winH);
+ if ( (pEle->winH=smlLibMalloc(newSize)) == 0 ) {
+ return SML_ERR_NOT_ENOUGH_SPACE;
+ }
+ pEle->memSize = newSize;
+
+ return SML_ERR_OK;
+}
+
+
+/**
+ * Get size of memory block memH.
+ *
+ * @pre memH is a valid handle
+ * @post actSize = memory block size
+ * @param memH (IN)
+ * Handle to memory block
+ * @param actSize (OUT)
+ * Actual size of memory block
+ * @return
+ * - SML_ERR_OK, if O.K.
+ * - SML_ERR_WRONG_PARAM, if memH is unknown
+ * @see smSetSize
+ */
+Ret_t smGetSize (MemHandle_t memH, MemSize_t *actSize) {
+ smWinList_t *pEle; // pointer to buffer element
+
+ if ( ! locateH(memH, &pEle) ) {
+ return SML_ERR_WRONG_PARAM;
+ }
+
+ *actSize = pEle->memSize;
+
+ return SML_ERR_OK;
+}
+
+#endif // #ifndef NOWSM
diff --git a/src/syncml_tk/src/sml/xlt/all/xltdec.c b/src/syncml_tk/src/sml/xlt/all/xltdec.c
new file mode 100755
index 0000000..33f939d
--- /dev/null
+++ b/src/syncml_tk/src/sml/xlt/all/xltdec.c
@@ -0,0 +1,2635 @@
+/**
+ * @file
+ * SyncmML Decoder
+ *
+ * @target_system all
+ * @target_os all
+ * @description The SyncML parser.
+ */
+
+
+/*
+ * Copyright Notice
+ * Copyright (c) Ericsson, IBM, Lotus, Matsushita Communication
+ * Industrial Co., Ltd., Motorola, Nokia, Openwave Systems, Inc.,
+ * Palm, Inc., Psion, Starfish Software, Symbian, Ltd. (2001).
+ * All Rights Reserved.
+ * Implementation of all or part of any Specification may require
+ * licenses under third party intellectual property rights,
+ * including without limitation, patent rights (such a third party
+ * may or may not be a Supporter). The Sponsors of the Specification
+ * are not responsible and shall not be held responsible in any
+ * manner for identifying or failing to identify any or all such
+ * third party intellectual property rights.
+ *
+ * THIS DOCUMENT AND THE INFORMATION CONTAINED HEREIN ARE PROVIDED
+ * ON AN "AS IS" BASIS WITHOUT WARRANTY OF ANY KIND AND ERICSSON, IBM,
+ * LOTUS, MATSUSHITA COMMUNICATION INDUSTRIAL CO. LTD, MOTOROLA,
+ * NOKIA, PALM INC., PSION, STARFISH SOFTWARE AND ALL OTHER SYNCML
+ * SPONSORS DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+ * BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
+ * HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
+ * SHALL ERICSSON, IBM, LOTUS, MATSUSHITA COMMUNICATION INDUSTRIAL CO.,
+ * LTD, MOTOROLA, NOKIA, PALM INC., PSION, STARFISH SOFTWARE OR ANY
+ * OTHER SYNCML SPONSOR BE LIABLE TO ANY PARTY FOR ANY LOSS OF
+ * PROFITS, LOSS OF BUSINESS, LOSS OF USE OF DATA, INTERRUPTION OF
+ * BUSINESS, OR FOR DIRECT, INDIRECT, SPECIAL OR EXEMPLARY, INCIDENTAL,
+ * PUNITIVE OR CONSEQUENTIAL DAMAGES OF ANY KIND IN CONNECTION WITH
+ * THIS DOCUMENT OR THE INFORMATION CONTAINED HEREIN, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH LOSS OR DAMAGE.
+ *
+ * The above notice and this paragraph must be included on all copies
+ * of this document that are made.
+ *
+ */
+
+/*************************************************************************/
+/* Definitions */
+/*************************************************************************/
+#include "syncml_tk_prefix_file.h" // %%% luz: needed for precompiled headers in eVC++
+
+#include "xltdec.h"
+#include "xltdeccom.h"
+#include "xlttags.h"
+#include "xltutilstack.h"
+#include "xlttagtbl.h"
+#include "xltmetinf.h"
+#include "xltdevinf.h"
+
+#include <smldef.h>
+#include <smldtd.h>
+#include <smlmetinfdtd.h>
+#include <smldevinfdtd.h>
+#include <smlerr.h>
+
+#include <libmem.h>
+#include <libstr.h>
+#include <mgrutil.h>
+
+
+#ifdef SYDEBUG
+
+#if !defined(DIRECT_APPBASE_GLOBALACCESS) && defined(CONSOLEINFO)
+ void ConsolePrintf(const char *text, ...);
+ #define SMLERRPRINTFX(lvl,m) CONSOLEPRINTF(m)
+#else
+ #define SMLERRPRINTFX(lvl,m) PNCDEBUGPRINTFX(lvl,m)
+#endif
+
+
+/**
+ * Better parsing error reporting when used within Synthesis SyncML engine
+ * added 2006-11-02 by luz@synthesis.ch
+ */
+
+// Error texts
+#define numSmlErrTexts (SML_ERR_XLT_INVAL_INPUT_DATA-SML_ERR_XLT_MISSING_CONT + 1)
+static const char * const smlErrTexts[numSmlErrTexts] = {
+ "required field content missing", // 0x2001
+ "Buffer too small", // 0x2002
+ "Invalid (WBXML) Element Type", // 0x2003
+ "Invalid List Type", // 0x2004
+ "Invalid Tag Type", // 0x2005
+ "???", // 0x2006
+ "Unknown Encoding", // 0x2007
+ "Invalid Protocol Element", // 0x2008
+ "Missing Content of List Elements", // 0x2009
+ "Incompatible WBXML Content Format Version", // 0x200A
+ "Document does not conform to SyncML DTD", // 0x200B
+ "Invalid PCData elem", // 0x200C
+ "Unspecified tokenizer error", // 0x200D
+ "Document does not conform to WBXML specification", // 0x200E
+ "Document contains unknown WBXML token", // 0x200F
+ "Non-empty start tag without matching end tag", // 0x2010
+ "WBXML document uses unspecified code page", // 0x2011
+ "End of buffer reached", // 0x2012
+ "Document does not conform to XML 1.0 specification", // 0x2012
+ "Document contains unknown XML tag", // 0x2014
+ "Invalid Public Identifier", // 0x2015
+ "Invalid Codepage Extension", // 0x2016
+ "No matching Codepage could be found", // 0x2017
+ "Data missing in input structure", // 0x2018
+};
+
+// get error text
+const char *smlErrorText(Ret_t aRc)
+{
+ if (aRc>=SML_ERR_XLT_MISSING_CONT && aRc<=SML_ERR_XLT_INVAL_INPUT_DATA) {
+ return smlErrTexts[aRc-SML_ERR_XLT_MISSING_CONT];
+ }
+ // no text found
+ return "<no error message found>";
+}
+
+
+// makes hex char out of nibble
+static char NibbleToHexDigit(uInt8 aNibble)
+{
+ aNibble &= 0x0F;
+ if (aNibble>9) return 'A'-0x0A+aNibble;
+ else return '0'+aNibble;
+} // NibbleToHexDigit
+
+#define numBytesShow 32
+
+
+// show token info
+void show_token(XltDecTokenPtr_t aToken, uInt32 aDebugMask)
+{
+ int i;
+ SmlPcdataExtension_t ext;
+ TagPtr_t pTags;
+ short tagwbxml;
+ char *tagxml;
+ char *extname;
+ char *toktype;
+
+ if (aToken==NULL) return;
+
+ // get token type
+ switch (aToken->type) {
+ case TOK_TAG_START:
+ toktype="start";
+ break;
+ case TOK_TAG_END:
+ toktype="end";
+ break;
+ case TOK_TAG_EMPTY:
+ toktype="empty";
+ break;
+ case TOK_CONT:
+ toktype="content";
+ break;
+ default:
+ toktype="UNKNOWN";
+ break;
+ }
+ // get extension name
+ switch (aToken->ext) {
+ case SML_EXT_METINF:
+ extname="metInf";
+ break;
+ case SML_EXT_DEVINF:
+ extname="devInf";
+ break;
+ default:
+ extname="undef/SyncML";
+ break;
+ }
+ // check if this is a tag
+ if (IS_TAG(aToken)) {
+ // get tag information
+ tagwbxml=-1; // undefined
+ tagxml="UNDEFINED";
+ for (ext=SML_EXT_UNDEFINED; ext<SML_EXT_LAST; ext++) {
+ i=0;
+ pTags=getTagTable(ext);
+ if (pTags!=NULL) {
+ while (((pTags+i)->id) != TN_UNDEF) {
+ if (((pTags+i)->id) == aToken->tagid) {
+ tagwbxml = (pTags+i)->wbxml;
+ #if defined(__SML_XML__) || defined(__SML_WBXML_TEXTTOKENS__)
+ tagxml = (pTags+i)->xml;
+ #endif
+ goto foundtag;
+ }
+ i++;
+ }
+ }
+ }
+ foundtag:
+ #if defined(__SML_XML__) || defined(__SML_WBXML_TEXTTOKENS__)
+ // we have tag names
+ SMLERRPRINTFX(aDebugMask,(
+ "- parsing %s token, ext=%s: <%s> (WBXML 0x%hX, tagid=%hd)",
+ toktype,
+ extname,
+ tagxml,
+ (short)tagwbxml,
+ (short)aToken->tagid
+ ));
+ #else
+ // we only have WBXML tag codes
+ SMLERRPRINTFX(aDebugMask,(
+ "- parsing %s token, ext=%s: tag (WBXML 0x%hX, tagid=%hd)",
+ toktype,
+ extname,
+ (short)tagwbxml,
+ (short)aToken->tagid
+ ));
+ #endif
+ } // if tag
+ else if (IS_CONTENT(aToken)) {
+ // content
+ SMLERRPRINTFX(aDebugMask,("- parsing content (%s token, ext=%s)",toktype,extname));
+ }
+ else {
+ // unknown
+ SMLERRPRINTFX(aDebugMask,("- parsing %s token, ext=%s",toktype,extname));
+ }
+} // show_token
+
+
+Ret_t show_decode_error(Ret_t aRc, XltDecScannerPtr_t aScanner, char *aRoutineName)
+{
+ char hexshow[numBytesShow*3+1];
+ char *p;
+ unsigned char *b;
+ int i;
+
+ if (aRc!=SML_ERR_OK) {
+ // get routine name
+ if (aRoutineName==NULL)
+ aRoutineName="SML"; // generic sml
+ // Show error
+ SMLERRPRINTFX(DBG_ERROR,("%s: smlErr 0x%hX (%s) while parsing",aRoutineName,aRc,smlErrorText(aRc)));
+ // Show details
+ if (aScanner && aScanner->curtok) {
+ // show what token we are parsing
+ show_token(aScanner->curtok,DBG_ERROR);
+ // show hex of 32 bytes after scan position
+ b = aScanner->getPos(aScanner);
+ SMLERRPRINTFX(DBG_ERROR,(
+ "- Tag start at 0x%lX, scanner pos at 0x%lX%s, data:",
+ aScanner->curtok->start,
+ b,
+ aScanner->finished ? " (finished)" : ""
+ ));
+ if (b!=NULL) {
+ p=hexshow;
+ for (i=0; i<numBytesShow; i++) {
+ *p++ = NibbleToHexDigit(*b>>4);
+ *p++ = NibbleToHexDigit(*b++);
+ *p++ = ' ';
+ }
+ *p=0;
+ SMLERRPRINTFX(DBG_ERROR,(hexshow));
+ p=hexshow;
+ for (i=0; i<numBytesShow; i++) {
+ *p++ = (*b>=0x20) && (*b<0x7F) ? *b : '_';
+ b++;
+ }
+ *p=0;
+ SMLERRPRINTFX(DBG_ERROR,(hexshow));
+ }
+ }
+ else {
+ // no scanner or no current token
+ SMLERRPRINTFX(DBG_ERROR,(
+ "%s: smlErr 0x%hX while parsing no current tag",
+ aRoutineName,
+ aRc
+ ));
+ }
+ }
+ // return error code
+ return aRc;
+} // show_decode_error
+
+#endif
+
+
+#ifdef __USE_EXTENSIONS__
+/* prototype for function in xltdecwbxml.c */
+void subdtdDecodeWbxml(XltDecoderPtr_t pDecoder,SmlPcdataPtr_t *ppPcdata);
+#endif
+
+/**
+ * Tries to concatenate two Pcdata elements. Only works when the two
+ * elements are of the same type (e.g. SML_PCDATA_STRING). Returns a
+ * pointer to the new Pcdata element or NULL if concatenation failed.
+ */
+static SmlPcdataPtr_t concatPCData(SmlPcdataPtr_t pDat1, const SmlPcdataPtr_t pDat2);
+
+
+/**
+ * appendXXXList
+ *
+ * These are auxiliary functions for building SyncML elements that contain
+ * lists of certain other data structures (e.g. Items). They take an
+ * existing list (e.g. of type ItemListPtr_t) and append an appropriate
+ * element at the end. If the ListPtr points to NULL a new list is created.
+ *
+ * @pre The scanner's current token is the start tag (may be
+ * empty) of the SyncML element to be appended to the list.
+ * @post The scanner's current token is the end tag (or empty
+ * start tag) of the SyncML element that was added to the list.
+ * @param pDecoder (IN/OUT)
+ * the decoder
+ * @param ppXXXList (IN/OUT)
+ * NULL or an initialized list, to which element will be appended
+ * @return SML_ERR_OK, if an element was successfully appended\n
+ * else error code
+ */
+static Ret_t appendItemList(XltDecoderPtr_t pDecoder, SmlItemListPtr_t *ppItemList);
+#ifdef SEARCH_RECEIVE
+static Ret_t appendSourceList(XltDecoderPtr_t pDecoder, SmlSourceListPtr_t *ppSourceList);
+#endif
+#ifdef MAPITEM_RECEIVE
+ static Ret_t appendMapItemList(XltDecoderPtr_t pDecoder, SmlMapItemListPtr_t *ppMapItemList);
+#endif
+static Ret_t appendTargetRefList(XltDecoderPtr_t pDecoder, SmlTargetRefListPtr_t *ppTargetRefList);
+static Ret_t appendSourceRefList(XltDecoderPtr_t pDecoder, SmlSourceRefListPtr_t *ppSourceRefList);
+
+/* if the commands are not defined we let the functions point to NULL */
+#ifndef RESULT_RECEIVE
+#define buildResults NULL
+#endif
+
+#ifndef MAP_RECEIVE
+#define buildMap NULL
+#endif
+
+#ifndef EXEC_RECEIVE
+#define buildExec NULL
+#endif
+
+#if !defined(ATOMIC_RECEIVE) && !defined(SEQUENCE_RECEIVE)
+#define buildAtomOrSeq NULL
+#endif
+
+#ifndef SEARCH_RECEIVE
+#define buildSearch NULL
+#endif
+
+
+typedef struct PEBuilder_s
+{
+ XltTagID_t tagid;
+ SmlProtoElement_t type;
+ Ret_t (*build)(XltDecoderPtr_t pDecoder, VoidPtr_t *ppElem);
+} PEBuilder_t, *PEBuilderPtr_t;
+
+/* luz 2005-08-17: simplified, removed unnecessary copying of the const static table */
+
+static const PEBuilder_t cPE_Dec[] = {
+ { TN_ADD, SML_PE_ADD, buildGenericCmd },
+ { TN_ALERT, SML_PE_ALERT, buildAlert },
+ { TN_ATOMIC, SML_PE_ATOMIC_START, buildAtomOrSeq },
+ { TN_COPY, SML_PE_COPY, buildGenericCmd },
+ { TN_DELETE, SML_PE_DELETE, buildGenericCmd },
+ { TN_EXEC, SML_PE_EXEC, buildExec },
+ { TN_GET, SML_PE_GET, buildPutOrGet },
+ { TN_MAP, SML_PE_MAP, buildMap },
+ { TN_PUT, SML_PE_PUT, buildPutOrGet },
+ { TN_RESULTS, SML_PE_RESULTS, buildResults },
+ { TN_SEARCH, SML_PE_SEARCH, buildSearch },
+ { TN_SEQUENCE, SML_PE_SEQUENCE_START, buildAtomOrSeq },
+ { TN_STATUS, SML_PE_STATUS, buildStatus },
+ { TN_SYNC, SML_PE_SYNC_START, buildSync },
+ { TN_REPLACE, SML_PE_REPLACE, buildGenericCmd },
+ { TN_MOVE, SML_PE_MOVE, buildGenericCmd },
+ { TN_UNDEF, SML_PE_UNDEF, 0 }
+};
+
+
+/*************************************************************************/
+/* External Functions */
+/*************************************************************************/
+/**
+ * Description see XLTDec.h header file.
+ */
+Ret_t
+xltDecInit(const SmlEncoding_t enc,
+ const MemPtr_t pBufEnd,
+ MemPtr_t *ppBufPos,
+ XltDecoderPtr_t *ppDecoder,
+ SmlSyncHdrPtr_t *ppSyncHdr)
+{
+ XltDecoderPtr_t pDecoder;
+ Ret_t rc;
+
+
+ /* create new decoder object */
+ if ((pDecoder = (XltDecoderPtr_t)smlLibMalloc(sizeof(XltDecoder_t))) == NULL)
+ return SML_ERR_NOT_ENOUGH_SPACE;
+ pDecoder->finished = 0;
+ pDecoder->final = 0;
+ pDecoder->scanner = NULL;
+ if ((rc = xltUtilCreateStack(&pDecoder->tagstack, 10)) != SML_ERR_OK) {
+ xltDecTerminate(pDecoder);
+ return rc;
+ }
+
+#ifdef __SML_WBXML__
+ if (enc == SML_WBXML)
+ {
+ rc = xltDecWbxmlInit(pBufEnd, ppBufPos, &pDecoder->scanner);
+ if (rc == SML_ERR_OK)
+ {
+ pDecoder->charset = pDecoder->scanner->charset;
+ pDecoder->charsetStr = NULL;
+ }
+ } else
+#endif
+
+#ifdef __SML_XML__
+ if (enc == SML_XML)
+ {
+
+ rc = xltDecXmlInit(pBufEnd, ppBufPos, &pDecoder->scanner);
+ if (rc == SML_ERR_OK)
+ {
+ pDecoder->charset = 0;
+ pDecoder->charsetStr = pDecoder->scanner->charsetStr;
+ }
+ } else
+#endif
+
+ {
+ rc = SML_ERR_XLT_ENC_UNK;
+ }
+
+ if (rc != SML_ERR_OK)
+ {
+ xltDecTerminate((XltDecoderPtr_t)pDecoder);
+ return rc;
+ }
+
+ /* try to find SyncHdr element, first comes the SyncML tag... */
+ if (((rc = nextToken(pDecoder)) != SML_ERR_OK)) {
+ xltDecTerminate((XltDecoderPtr_t)pDecoder);
+ return rc;
+ }
+ if (!IS_START(pDecoder->scanner->curtok) ||
+ (pDecoder->scanner->curtok->tagid != TN_SYNCML)) {
+ smlFreePcdata(pDecoder->scanner->curtok->pcdata);
+ xltDecTerminate((XltDecoderPtr_t)pDecoder);
+ return SML_DECODEERROR(SML_ERR_XLT_INVAL_SYNCML_DOC,pDecoder->scanner,"xltDecInit");
+ }
+
+ /* ... then the SyncHdr */
+ if (((rc = nextToken(pDecoder)) != SML_ERR_OK)) {
+ xltDecTerminate((XltDecoderPtr_t)pDecoder);
+ return rc;
+ }
+ if ((rc = buildSyncHdr(pDecoder, (VoidPtr_t)ppSyncHdr)) != SML_ERR_OK) {
+ xltDecTerminate((XltDecoderPtr_t)pDecoder);
+ return rc;
+ }
+
+ *ppBufPos = pDecoder->scanner->getPos(pDecoder->scanner);
+
+ *ppDecoder = (XltDecoderPtr_t)pDecoder;
+
+ return SML_ERR_OK;
+}
+
+/**
+ * Description see XLTDec.h header file.
+ */
+Ret_t
+xltDecNext(XltDecoderPtr_t pDecoder,
+ const MemPtr_t pBufEnd,
+ MemPtr_t *ppBufPos,
+ SmlProtoElement_t *pe,
+ VoidPtr_t *ppContent)
+{
+ XltDecoderPtr_t pDecPriv = (XltDecoderPtr_t)pDecoder;
+ XltDecScannerPtr_t pScanner = pDecPriv->scanner;
+ XltTagID_t tagid;
+ Ret_t rc;
+ int i;
+
+ pScanner->setBuf(pScanner, *ppBufPos, pBufEnd);
+
+ /* if we are still outside the SyncBody, look for SyncBody start tag */
+ if ((rc = pDecPriv->tagstack->top(pDecPriv->tagstack, &tagid)) != SML_ERR_OK)
+ return rc;
+ if (tagid == TN_SYNCML) {
+ if (((rc = nextToken(pDecPriv)) != SML_ERR_OK)) {
+ return rc;
+ }
+ if (!((IS_START(pScanner->curtok)) &&
+ (pScanner->curtok->tagid == TN_SYNCBODY))) {
+ return SML_DECODEERROR(SML_ERR_XLT_INVAL_PROTO_ELEM,pScanner,"xltDecNext");
+ }
+ }
+
+ if ((rc = nextToken(pDecPriv)) != SML_ERR_OK)
+ return rc;
+
+ /* if we find a SyncML protocol element build the corresponding
+ data structure */
+ if ((IS_START_OR_EMPTY(pScanner->curtok)) && (pScanner->curtok->tagid != TN_FINAL)) {
+
+ i = 0;
+ while ((cPE_Dec[i].tagid) != TN_UNDEF)
+ {
+ if ((cPE_Dec[i].tagid) == pScanner->curtok->tagid)
+ {
+ *pe = (cPE_Dec[i].type);
+ if ((rc = cPE_Dec[i].build(pDecPriv, ppContent)) != SML_ERR_OK)
+ return rc;
+ /* T.K. adjust the SML_PE_ for 'generic' structures
+ if (*pe == SML_PE_GENERIC) {
+ SmlGenericCmdPtr_t g = *ppContent;
+ switch ((int) (cPE_Dec[i].tagid)) {
+ case TN_ADD : g->elementType = SML_PE_ADD; break;
+ case TN_COPY : g->elementType = SML_PE_COPY; break;
+ case TN_DELETE : g->elementType = SML_PE_DELETE; break;
+ case TN_REPLACE: g->elementType = SML_PE_REPLACE; break;
+ }
+ }
+ */
+ /* %%% luz 2005-08-17: we can do this a lot easier... */
+ /* if the procduced element has no distinct element type now,
+ but is still SML_PE_GENERIC, we need to set it to the correct distinct value */
+ if (((SmlGenericCmdPtr_t)(*ppContent))->elementType == SML_PE_GENERIC) {
+ ((SmlGenericCmdPtr_t)(*ppContent))->elementType = cPE_Dec[i].type;
+ }
+
+ break;
+ }
+ i++;
+ }
+ if ((cPE_Dec[i].tagid) == TN_UNDEF)
+ {
+ *pe = SML_PE_UNDEF;
+ *ppContent = NULL;
+ return SML_DECODEERROR(SML_ERR_XLT_INVAL_PROTO_ELEM,pScanner,"xltDecNext");
+ }
+ } else {
+
+ /* found end tag */
+ switch (pScanner->curtok->tagid) {
+ case TN_ATOMIC:
+ *pe = SML_PE_ATOMIC_END;
+ *ppContent = NULL;
+ break;
+ case TN_SEQUENCE:
+ *pe = SML_PE_SEQUENCE_END;
+ *ppContent = NULL;
+ break;
+ case TN_SYNC:
+ *pe = SML_PE_SYNC_END;
+ *ppContent = NULL;
+ break;
+ case TN_FINAL:
+ *pe = SML_PE_FINAL;
+ *ppContent = NULL;
+ pDecPriv->final = 1;
+ break;
+ case TN_SYNCBODY:
+ /* next comes the SyncML end tag, then we're done */
+ if ((rc = nextToken(pDecPriv)) != SML_ERR_OK)
+ return rc;
+ if ((pScanner->curtok->type == TOK_TAG_END) &&
+ (pScanner->curtok->tagid == TN_SYNCML)) {
+ *pe = SML_PE_UNDEF;
+ *ppContent = NULL;
+ pDecPriv->finished = 1;
+ } else {
+ return SML_DECODEERROR(SML_ERR_XLT_INVAL_SYNCML_DOC,pScanner,"xltDecNext");
+ }
+ break;
+ default:
+ return SML_DECODEERROR(SML_ERR_XLT_INVAL_PROTO_ELEM,pScanner,"xltDecNext");
+ }
+ }
+
+ *ppBufPos = pScanner->getPos(pScanner);
+
+ return SML_ERR_OK;
+}
+
+/**
+ * Description see XLTDec.h header file.
+ */
+Ret_t
+xltDecTerminate(XltDecoderPtr_t pDecoder)
+{
+ XltDecoderPtr_t pDecPriv;
+
+ if (pDecoder == NULL)
+ return SML_ERR_OK;
+
+ pDecPriv = (XltDecoderPtr_t)pDecoder;
+ if (pDecPriv->scanner != NULL)
+ pDecPriv->scanner->destroy(pDecPriv->scanner);
+ if (pDecPriv->tagstack != NULL)
+ pDecPriv->tagstack->destroy(pDecPriv->tagstack);
+ smlLibFree(pDecPriv);
+
+ return SML_ERR_OK;
+}
+
+
+Ret_t xltDecReset(XltDecoderPtr_t pDecoder)
+{
+ return xltDecTerminate(pDecoder);
+}
+
+/**
+ * Gets the next token from the scanner.
+ * Checks if the current tag is an end tag and if so, whether the last
+ * open start tag has the same tag id as the current end tag. An open start
+ * tag is one which matching end tag has not been seen yet.
+ * If the current tag is a start tag its tag ID will be pushed onto the
+ * tag stack.
+ * If the current tag is an empty tag or not a tag at all nothing will be
+ * done.
+ */
+Ret_t
+nextToken(XltDecoderPtr_t pDecoder)
+{
+ XltUtilStackPtr_t pTagStack;
+ XltDecTokenPtr_t pToken;
+ Ret_t rc;
+
+
+ if ((rc = pDecoder->scanner->nextTok(pDecoder->scanner)) != SML_ERR_OK)
+ return rc;
+
+ pToken = pDecoder->scanner->curtok;
+ pTagStack = pDecoder->tagstack;
+
+ #if SYDEBUG>2
+ show_token(pToken,DBG_DETAILS);
+ #endif
+
+ if (IS_START(pToken)) {
+ if (pTagStack->push(pTagStack, pToken->tagid))
+ return SML_ERR_UNSPECIFIC;
+ } else if (IS_END(pToken)) {
+ XltTagID_t lastopen;
+ if (pTagStack->pop(pTagStack, &lastopen))
+ return SML_ERR_UNSPECIFIC;
+ if (pToken->tagid != lastopen)
+ return SML_DECODEERROR(SML_ERR_XLT_INVAL_SYNCML_DOC,pDecoder->scanner,"nextToken");
+ }
+ return SML_ERR_OK;
+}
+
+Ret_t discardToken(XltDecoderPtr_t pDecoder)
+
+{
+ Ret_t rc;
+ XltTagID_t tmp;
+ if ((rc = pDecoder->scanner->pushTok(pDecoder->scanner)) != SML_ERR_OK)
+ return rc;
+ if ((rc = pDecoder->tagstack->pop(pDecoder->tagstack, &tmp)) != SML_ERR_OK)
+ return rc;
+ return SML_ERR_OK;
+}
+
+/*************************************************************************/
+/* Internal Functions */
+/*************************************************************************/
+
+static SmlPcdataPtr_t
+concatPCData(SmlPcdataPtr_t pDat1, const SmlPcdataPtr_t pDat2)
+{
+ // luz: 2006-09-07: plain wrong again. We can very well concatenate
+ // STRING, OPAQUE and CDATA, only extensions are not concatenatable
+ // For this, no extra check is needed, as implementation below already does
+ //%%% if (pDat1->contentType != pDat2->contentType)
+ //%%% return NULL;
+
+ switch (pDat1->contentType) {
+ case SML_PCDATA_STRING:
+ /* luz: 2005-03-07: original RTK code: plain wrong - causes crash as pDat1->content is NOT
+ big enough to have data appended without re-alloc!!
+ - No need to make difference between STRING and OPAQUE anyway!
+ pDat1->content = (VoidPtr_t)smlLibStrcat(pDat1->content, pDat2->content);
+ pDat1->length += pDat2->length;
+ break;
+ */
+ case SML_PCDATA_CDATA:
+ case SML_PCDATA_OPAQUE:
+ if (pDat2->contentType==SML_PCDATA_EXTENSION)
+ return NULL; // can't concat
+ // luz: 2005-03-07: made sure we ALWAYS have a null byte at the end of an opaque string
+ if ((pDat1->content = smlLibRealloc(pDat1->content, pDat1->length + pDat2->length + 1)) == NULL)
+ return NULL;
+ smlLibMemmove(((Byte_t*)pDat1->content) + pDat1->length, pDat2->content, pDat2->length);
+ pDat1->length += pDat2->length;
+ ((char *)pDat1->content)[pDat1->length]=0; // null byte at the end
+ break;
+ default:
+ return NULL; // can't concat
+ }
+ return pDat1;
+}
+
+Ret_t
+buildSyncHdr(XltDecoderPtr_t pDecoder, VoidPtr_t *ppSyncHdr)
+{
+ XltDecScannerPtr_t pScanner;
+ SmlSyncHdrPtr_t pSyncHdr;
+ Ret_t rc;
+ Long_t sessionid = 0, msgid = 0, source = 0, target = 0, version = 0, proto = 0;
+
+ /* shortcut to the scanner object */
+ pScanner = pDecoder->scanner;
+
+ /* if ppSyncHdr is not NULL we've already
+ found a SyncHdr before! */
+ if (*ppSyncHdr != NULL)
+ return SML_DECODEERROR(SML_ERR_XLT_INVAL_SYNCML_DOC,pScanner,"buildSyncHdr");
+
+ /* initialize new SmlSyncHdr */
+ if ((pSyncHdr = (SmlSyncHdrPtr_t)smlLibMalloc(sizeof(SmlSyncHdr_t))) == NULL)
+ return SML_ERR_NOT_ENOUGH_SPACE;
+ smlLibMemset(pSyncHdr, 0, sizeof(SmlSyncHdr_t));
+
+ /* initialize the element type field */
+ pSyncHdr->elementType = SML_PE_HEADER;
+
+ /* empty SmlSyncHdr is possible */
+ if (IS_EMPTY(pScanner->curtok)) {
+ *ppSyncHdr = pSyncHdr;
+ return SML_ERR_OK;
+ }
+
+ /* get next Token */
+ if (((rc = nextToken(pDecoder)) != SML_ERR_OK)) {
+ smlLibFree(pSyncHdr);
+ return rc;
+ }
+
+ /* parse child elements until we find a matching end tag */
+ while (pScanner->curtok->type != TOK_TAG_END) {
+ switch (pScanner->curtok->tagid) {
+
+ /* PCDATA elements */
+ case TN_VERSION:
+ rc = buildPCData(pDecoder, (VoidPtr_t)&pSyncHdr->version);
+ version++;
+ break;
+ case TN_PROTO:
+ rc = buildPCData(pDecoder, (VoidPtr_t)&pSyncHdr->proto);
+ proto++;
+ break;
+ case TN_SESSIONID:
+ rc = buildPCData(pDecoder, (VoidPtr_t)&pSyncHdr->sessionID);
+ sessionid++;
+ break;
+ case TN_MSGID:
+ rc = buildPCData(pDecoder, (VoidPtr_t)&pSyncHdr->msgID);
+ msgid++;
+ break;
+ case TN_RESPURI:
+ rc = buildPCData(pDecoder, (VoidPtr_t)&pSyncHdr->respURI);
+ break;
+
+ /* child tags */
+ case TN_TARGET:
+ rc = buildTargetOrSource(pDecoder, (VoidPtr_t)&pSyncHdr->target);
+ target++;
+ break;
+ case TN_SOURCE:
+ rc = buildTargetOrSource(pDecoder, (VoidPtr_t)&pSyncHdr->source);
+ source++;
+ break;
+ case TN_CRED:
+ rc = buildCred(pDecoder, (VoidPtr_t)&pSyncHdr->cred);
+ break;
+ case TN_META:
+ rc = buildPCData(pDecoder, (VoidPtr_t)&pSyncHdr->meta);
+ break;
+
+ /* flags (empty tags) */
+ case TN_NORESP:
+ pSyncHdr->flags |= SmlNoResp_f;
+ rc = buildEmptyTag(pDecoder); // allow for <tag></tag> instead of <tag/>
+ break;
+
+ default:
+ rc = SML_DECODEERROR(SML_ERR_XLT_INVAL_SYNCML_DOC,pScanner,"buildSyncHdr");
+ }
+
+ /* decoding of child element went ok? */
+ if (rc != SML_ERR_OK) {
+ smlFreeSyncHdr(pSyncHdr);
+
+ return rc;
+ }
+
+ /* get next token */
+ if ((rc = nextToken(pDecoder)) != SML_ERR_OK) {
+ smlFreeSyncHdr(pSyncHdr);
+ return rc;
+ }
+ }
+
+ if ((sessionid == 0) || (msgid == 0) || (target == 0) || (source == 0) || (version == 0) || (proto == 0))
+ {
+ smlFreeSyncHdr(pSyncHdr);
+ return SML_DECODEERROR(SML_ERR_XLT_INVAL_SYNCML_DOC,pScanner,"buildSyncHdr");
+ }
+
+ *ppSyncHdr = pSyncHdr;
+
+ return SML_ERR_OK;
+}
+
+Ret_t
+buildSync(XltDecoderPtr_t pDecoder, VoidPtr_t *ppElem)
+{
+ XltDecScannerPtr_t pScanner;
+ SmlSyncPtr_t pSync;
+ Ret_t rc;
+ Long_t cmdid = 0;
+
+ /* stop decoding the Sync when we find a SyncML command */
+ Byte_t break_sync = 0;
+
+ pScanner = pDecoder->scanner;
+
+ if (*ppElem != NULL)
+ return SML_DECODEERROR(SML_ERR_XLT_INVAL_SYNCML_DOC,pScanner,"buildSync");
+
+ /* initialize a new Sync */
+ if ((pSync = (SmlSyncPtr_t)smlLibMalloc(sizeof(SmlSync_t))) == NULL)
+ return SML_ERR_NOT_ENOUGH_SPACE;
+ smlLibMemset(pSync, 0, sizeof(SmlSync_t));
+
+ /* initialize the element type field */
+ pSync->elementType = SML_PE_SYNC_START;
+
+ if (IS_EMPTY(pScanner->curtok)) {
+
+ smlLibFree(pSync);
+ return SML_ERR_OK;
+ }
+
+ /* get next token */
+ if (((rc = nextToken(pDecoder)) != SML_ERR_OK)) {
+ smlLibFree(pSync);
+ return rc;
+ }
+
+ /* parse child elements until we find a matching end tag
+ or until we find a TN_ADD, TN_ATOMIC, etc. start tag */
+ while ((pScanner->curtok->type != TOK_TAG_END) && !break_sync) {
+ switch (pScanner->curtok->tagid) {
+
+ /* PCDATA elements */
+ case TN_CMDID:
+ rc = buildPCData(pDecoder, (VoidPtr_t)&pSync->cmdID);
+ cmdid++;
+ break;
+ case TN_META:
+ rc = buildPCData(pDecoder, (VoidPtr_t)&pSync->meta);
+ break;
+ case TN_NUMBEROFCHANGES:
+ rc = buildPCData(pDecoder, (VoidPtr_t)&pSync->noc);
+ break;
+
+ /* child tags */
+ case TN_CRED:
+ rc = buildCred(pDecoder, (VoidPtr_t)&pSync->cred);
+ break;
+ case TN_TARGET:
+ rc = buildTargetOrSource(pDecoder, (VoidPtr_t)&pSync->target);
+ break;
+ case TN_SOURCE:
+ rc = buildTargetOrSource(pDecoder, (VoidPtr_t)&pSync->source);
+ break;
+
+ /* flags */
+ case TN_NORESP:
+ pSync->flags |= SmlNoResp_f;
+ rc = buildEmptyTag(pDecoder); // allow for <tag></tag> instead of <tag/>
+ break;
+
+ /* quit if we find an Add, Atomic, etc.
+ element */
+ case TN_ADD:
+ case TN_ATOMIC:
+ case TN_COPY:
+ case TN_DELETE:
+ case TN_SEQUENCE:
+ case TN_REPLACE:
+ break_sync = 1;
+ break;
+
+ default:
+ rc = SML_DECODEERROR(SML_ERR_XLT_INVAL_SYNCML_DOC,pScanner,"buildSync");
+ }
+ if (rc != SML_ERR_OK) {
+ smlFreeSync(pSync);
+ return rc;
+ }
+ if (!break_sync) {
+ /* get next token and continue as usual */
+ if ((rc = nextToken(pDecoder)) != SML_ERR_OK) {
+ smlFreeSync(pSync);
+ return rc;
+ }
+ } else {
+ /* we've found a SyncML command - we need to go
+ back one token and correct the tagstack */
+ if ((rc = discardToken(pDecoder)) != SML_ERR_OK) {
+ smlFreeSync(pSync);
+ return rc;
+ }
+ }
+ }
+
+ if (!break_sync) {
+ if ((pScanner->curtok->tagid) != TN_SYNC)
+ {
+ smlFreeSync(pSync);
+ return SML_DECODEERROR(SML_ERR_XLT_INVAL_SYNCML_DOC,pScanner,"buildSync");
+ }
+ else
+ {
+ if (pDecoder->tagstack->push(pDecoder->tagstack, pScanner->curtok->tagid))
+ {
+ smlFreeSync(pSync);
+ return SML_ERR_UNSPECIFIC;
+ }
+ if ((rc = pDecoder->scanner->pushTok(pDecoder->scanner)) != SML_ERR_OK)
+ {
+ smlFreeSync(pSync);
+ return rc;
+ }
+ }
+ }
+
+ *ppElem = pSync;
+
+ return SML_ERR_OK;
+}
+
+#if (defined ATOMIC_RECEIVE || defined SEQUENCE_RECEIVE)
+Ret_t
+buildAtomOrSeq(XltDecoderPtr_t pDecoder, VoidPtr_t *ppElem)
+{
+ XltDecScannerPtr_t pScanner;
+ SmlAtomicPtr_t pAoS; /* SmlAtomicPtr_t and SequencePtr_t are pointer
+ to the same structure! */
+ Ret_t rc;
+ Byte_t break_aos = 0; /* stop decoding the Atomic when we find a
+ SyncML command */
+ Long_t cmdid = 0;
+
+ pScanner = pDecoder->scanner;
+
+ if (*ppElem != NULL)
+ return SML_DECODEERROR(SML_ERR_XLT_INVAL_SYNCML_DOC,pScanner,"buildAtomOrSeq");
+
+ if ((pAoS = (SmlAtomicPtr_t)smlLibMalloc(sizeof(SmlAtomic_t))) == NULL)
+ return SML_ERR_NOT_ENOUGH_SPACE;
+ smlLibMemset(pAoS, 0, sizeof(SmlAtomic_t));
+
+ /* initialize the element type field */
+ pAoS->elementType = SML_PE_CMD_GROUP;
+
+ if (IS_EMPTY(pScanner->curtok)) {
+ smlLibFree(pAoS);
+ return SML_DECODEERROR(SML_ERR_XLT_INVAL_SYNCML_DOC,pScanner,"buildAtomOrSeq");
+ }
+
+ /* get next token */
+ if (((rc = nextToken(pDecoder)) != SML_ERR_OK)) {
+ smlLibFree (pAoS);
+ return rc;
+ }
+
+ /* parse child elements until we find a matching end tag
+ or until we find a TN_ADD, TN_ATOMIC, etc. start tag */
+ while ((pScanner->curtok->type != TOK_TAG_END) && !break_aos) {
+ switch (pScanner->curtok->tagid) {
+
+ /* PCDATA elements */
+ case TN_CMDID:
+ rc = buildPCData(pDecoder, (VoidPtr_t)&pAoS->cmdID);
+ cmdid++;
+ break;
+ case TN_META:
+ rc = buildPCData(pDecoder, (VoidPtr_t)&pAoS->meta);
+ break;
+
+ /* flags */
+ case TN_NORESP:
+ pAoS->flags |= SmlNoResp_f;
+ rc = buildEmptyTag(pDecoder); // allow for <tag></tag> instead of <tag/>
+ break;
+
+ /* quit if we find an Add, Atomic, etc.
+ element */
+ case TN_ADD:
+ case TN_REPLACE:
+ case TN_DELETE:
+ case TN_COPY:
+ case TN_ATOMIC:
+ case TN_MAP:
+ case TN_SYNC:
+ case TN_GET:
+ case TN_ALERT:
+ case TN_EXEC:
+ break_aos = 1;
+ break;
+
+ default:
+ rc = SML_DECODEERROR(SML_ERR_XLT_INVAL_SYNCML_DOC,pScanner,"buildAtomOrSeq");
+ }
+ if (rc != SML_ERR_OK) {
+ smlFreeAtomic(pAoS);
+ return rc;
+ }
+ if (!break_aos) {
+ if ((rc = nextToken(pDecoder)) != SML_ERR_OK) {
+ smlFreeAtomic(pAoS);
+ return rc;
+ }
+ } else {
+ /* we've found a SyncML command - we need to go
+ back one token and correct the tagstack */
+ if ((rc = discardToken(pDecoder)) != SML_ERR_OK) {
+ smlFreeAtomic(pAoS);
+ return rc;
+ }
+ }
+ }
+
+ if (!break_aos) {
+ /* Atomic/Sequence must contain at least one SyncML command */
+ smlFreeAtomic(pAoS);
+ return SML_DECODEERROR(SML_ERR_XLT_INVAL_SYNCML_DOC,pScanner,"buildAtomOrSeq");
+ }
+
+ if (cmdid == 0)
+ {
+ smlFreeAtomic(pAoS);
+ return SML_DECODEERROR(SML_ERR_XLT_INVAL_SYNCML_DOC,pScanner,"buildAtomOrSeq");
+ }
+
+ *ppElem = pAoS;
+
+ return SML_ERR_OK;
+}
+#endif
+
+#ifdef EXEC_RECEIVE
+Ret_t
+buildExec(XltDecoderPtr_t pDecoder, VoidPtr_t *ppElem)
+{
+ XltDecScannerPtr_t pScanner;
+ SmlExecPtr_t pExec;
+ Ret_t rc;
+ Long_t items = 0, cmdid = 0;
+
+ pScanner = pDecoder->scanner;
+
+ if (*ppElem != NULL)
+ return SML_DECODEERROR(SML_ERR_XLT_INVAL_SYNCML_DOC,pScanner,"buildExec");
+
+ if ((pExec = (SmlExecPtr_t)smlLibMalloc(sizeof(SmlExec_t))) == NULL)
+ return SML_ERR_NOT_ENOUGH_SPACE;
+ smlLibMemset(pExec, 0, sizeof(SmlExec_t));
+
+ /* initialize the element type field */
+ pExec->elementType = SML_PE_EXEC;
+
+ if (IS_EMPTY(pScanner->curtok)) {
+ smlLibFree(pExec);
+ return SML_DECODEERROR(SML_ERR_XLT_INVAL_SYNCML_DOC,pScanner,"buildExec");
+ }
+
+ if (((rc = nextToken(pDecoder)) != SML_ERR_OK)) {
+ smlLibFree(pExec);
+ return rc;
+ }
+
+ while (pScanner->curtok->type != TOK_TAG_END) {
+ switch (pScanner->curtok->tagid) {
+
+ /* PCData */
+ case TN_CMDID:
+ rc = buildPCData(pDecoder, (VoidPtr_t)&pExec->cmdID);
+ cmdid++;
+ break;
+
+ case TN_META:
+ rc = buildPCData(pDecoder, (VoidPtr_t)&pExec->meta);
+ break;
+
+ /* child tags */
+ case TN_CRED:
+ rc = buildCred(pDecoder, (VoidPtr_t)&pExec->cred);
+ break;
+
+ case TN_ITEM:
+ rc = buildItem(pDecoder, (VoidPtr_t)&pExec->item);
+ items++;
+ break;
+
+ /* flags */
+ case TN_NORESP:
+ pExec->flags |= SmlNoResp_f;
+ rc = buildEmptyTag(pDecoder); // allow for <tag></tag> instead of <tag/>
+ break;
+
+ default:
+ rc = SML_DECODEERROR(SML_ERR_XLT_INVAL_SYNCML_DOC,pScanner,"buildExec");
+ }
+ if (rc != SML_ERR_OK) {
+ smlFreeExec(pExec);
+ return rc;
+ }
+ if (((rc = nextToken(pDecoder)) != SML_ERR_OK)) {
+ smlFreeExec(pExec);
+ return rc;
+ }
+ }
+
+ if ((items == 0) || (cmdid == 0)) {
+ smlFreeExec(pExec);
+ return SML_DECODEERROR(SML_ERR_XLT_INVAL_SYNCML_DOC,pScanner,"buildExec");
+ }
+
+ *ppElem = pExec;
+
+ return SML_ERR_OK;
+}
+#endif
+
+Ret_t
+buildGenericCmd(XltDecoderPtr_t pDecoder, VoidPtr_t *ppElem)
+{
+ XltDecScannerPtr_t pScanner;
+ SmlGenericCmdPtr_t pGenCmd;
+ Ret_t rc;
+ Long_t items = 0, cmdid = 0;
+
+ pScanner = pDecoder->scanner;
+
+ if (*ppElem != NULL)
+ return SML_DECODEERROR(SML_ERR_XLT_INVAL_SYNCML_DOC,pScanner,"buildGenericCmd");
+
+ /* initialize a new GenericCmd */
+ if ((pGenCmd = (SmlGenericCmdPtr_t)smlLibMalloc(sizeof(SmlGenericCmd_t))) == NULL)
+ return SML_ERR_NOT_ENOUGH_SPACE;
+ smlLibMemset(pGenCmd, 0, sizeof(SmlGenericCmd_t));
+
+ /* initialize the element type field */
+ pGenCmd->elementType = SML_PE_GENERIC;
+
+ if (IS_EMPTY(pScanner->curtok)) {
+ smlLibFree(pGenCmd);
+ return SML_DECODEERROR(SML_ERR_XLT_INVAL_SYNCML_DOC,pScanner,"buildGenericCmd");
+ }
+
+ if (((rc = nextToken(pDecoder)) != SML_ERR_OK)) {
+ smlLibFree(pGenCmd);
+ return rc;
+ }
+
+ while (pScanner->curtok->type != TOK_TAG_END) {
+ switch (pScanner->curtok->tagid) {
+
+ /* PCDATA elements */
+ case TN_CMDID:
+ rc = buildPCData(pDecoder, (VoidPtr_t)&pGenCmd->cmdID);
+ cmdid++;
+ break;
+ case TN_META:
+ rc = buildPCData(pDecoder, (VoidPtr_t)&pGenCmd->meta);
+ break;
+
+ /* child tags */
+ case TN_CRED:
+ rc = buildCred(pDecoder, (VoidPtr_t)&pGenCmd->cred);
+ break;
+
+ /* flags (empty tags) */
+ case TN_NORESP:
+ pGenCmd->flags |= SmlNoResp_f;
+ rc = buildEmptyTag(pDecoder); // allow for <tag></tag> instead of <tag/>
+ break;
+ case TN_ARCHIVE:
+ pGenCmd->flags |= SmlArchive_f;
+ rc = buildEmptyTag(pDecoder); // allow for <tag></tag> instead of <tag/>
+ break;
+ case TN_SFTDEL:
+ pGenCmd->flags |= SmlSftDel_f;
+ rc = buildEmptyTag(pDecoder); // allow for <tag></tag> instead of <tag/>
+ break;
+
+ /* Lists */
+ case TN_ITEM:
+ rc = appendItemList(pDecoder, &pGenCmd->itemList);
+ items++;
+ break;
+
+ default:
+ rc = SML_DECODEERROR(SML_ERR_XLT_INVAL_SYNCML_DOC,pScanner,"buildGenericCmd");
+ }
+ if (rc != SML_ERR_OK) {
+ smlFreeGeneric(pGenCmd);
+ return rc;
+ }
+ if (((rc = nextToken(pDecoder)) != SML_ERR_OK)) {
+ smlFreeGeneric(pGenCmd);
+ return rc;
+ }
+ }
+
+ if ((items == 0) || (cmdid == 0))
+ {
+ smlFreeGeneric(pGenCmd);
+ return SML_DECODEERROR(SML_ERR_XLT_INVAL_SYNCML_DOC,pScanner,"buildGenericCmd");
+ }
+
+ *ppElem = pGenCmd;
+
+ return SML_ERR_OK;
+}
+
+Ret_t
+buildAlert(XltDecoderPtr_t pDecoder, VoidPtr_t *ppElem)
+{
+ XltDecScannerPtr_t pScanner;
+ SmlAlertPtr_t pAlert;
+ Ret_t rc;
+ Long_t cmdid = 0;
+
+ pScanner = pDecoder->scanner;
+
+ if (*ppElem != NULL)
+ return SML_DECODEERROR(SML_ERR_XLT_INVAL_SYNCML_DOC,pScanner,"buildAlert");
+
+ if ((pAlert = (SmlAlertPtr_t)smlLibMalloc(sizeof(SmlAlert_t))) == NULL)
+ return SML_ERR_NOT_ENOUGH_SPACE;
+ smlLibMemset(pAlert, 0, sizeof(SmlAlert_t));
+
+ /* initialize the element type field */
+ pAlert->elementType = SML_PE_ALERT;
+
+ if (IS_EMPTY(pScanner->curtok)) {
+ smlLibFree(pAlert);
+ return SML_DECODEERROR(SML_ERR_XLT_INVAL_SYNCML_DOC,pScanner,"buildAlert");
+ }
+
+ if (((rc = nextToken(pDecoder)) != SML_ERR_OK)) {
+ smlLibFree(pAlert);
+ return rc;
+ }
+
+ while (pScanner->curtok->type != TOK_TAG_END) {
+ switch (pScanner->curtok->tagid) {
+
+ /* PCDATA elements */
+ case TN_CMDID:
+ rc = buildPCData(pDecoder, (VoidPtr_t)&pAlert->cmdID);
+ cmdid++;
+ break;
+ case TN_DATA:
+ rc = buildPCData(pDecoder, (VoidPtr_t)&pAlert->data);
+ break;
+
+ /* child tags */
+ case TN_CRED:
+ rc = buildCred(pDecoder, (VoidPtr_t)&pAlert->cred);
+ break;
+
+ /* flags (empty tags) */
+ case TN_NORESP:
+ pAlert->flags |= SmlNoResp_f;
+ rc = buildEmptyTag(pDecoder); // allow for <tag></tag> instead of <tag/>
+ break;
+
+ /* Lists */
+ case TN_ITEM:
+ rc = appendItemList(pDecoder, &pAlert->itemList);
+ break;
+
+ default:
+ rc = SML_DECODEERROR(SML_ERR_XLT_INVAL_SYNCML_DOC,pScanner,"buildAlert");
+ }
+ if (rc != SML_ERR_OK) {
+ smlFreeAlert(pAlert);
+ return rc;
+ }
+ if (((rc = nextToken(pDecoder)) != SML_ERR_OK)) {
+ smlFreeAlert(pAlert);
+ return rc;
+ }
+ }
+
+ if (cmdid == 0)
+ {
+ smlFreeAlert(pAlert);
+ return SML_DECODEERROR(SML_ERR_XLT_INVAL_SYNCML_DOC,pScanner,"buildAlert");
+ }
+
+ *ppElem = pAlert;
+
+ return SML_ERR_OK;
+}
+
+#ifdef MAP_RECEIVE
+Ret_t
+buildMap(XltDecoderPtr_t pDecoder, VoidPtr_t *ppElem)
+{
+ XltDecScannerPtr_t pScanner;
+ SmlMapPtr_t pMap;
+ Ret_t rc;
+ Long_t target = 0, source = 0, mapitems = 0, cmdid = 0;
+
+ pScanner = pDecoder->scanner;
+
+ if (*ppElem != NULL)
+ return SML_DECODEERROR(SML_ERR_XLT_INVAL_SYNCML_DOC,pScanner,"buildMap");
+
+ if ((pMap = (SmlMapPtr_t)smlLibMalloc(sizeof(SmlMap_t))) == NULL)
+ return SML_ERR_NOT_ENOUGH_SPACE;
+ smlLibMemset(pMap, 0, sizeof(SmlMap_t));
+
+ /* initialize the element type field */
+ pMap->elementType = SML_PE_MAP;
+
+ /* Source is required */
+ if (IS_EMPTY(pScanner->curtok)) {
+ smlLibFree(pMap);
+ return SML_DECODEERROR(SML_ERR_XLT_INVAL_SYNCML_DOC,pScanner,"buildMap");
+ }
+
+ if (((rc = nextToken(pDecoder)) != SML_ERR_OK)) {
+ smlLibFree(pMap);
+ return rc;
+ }
+
+ while (pScanner->curtok->type != TOK_TAG_END) {
+ switch (pScanner->curtok->tagid) {
+
+ /* PCDATA elements */
+ case TN_CMDID:
+ rc = buildPCData(pDecoder, (VoidPtr_t)&pMap->cmdID);
+ cmdid++;
+ break;
+ case TN_META:
+ rc = buildPCData(pDecoder, (VoidPtr_t)&pMap->meta);
+ break;
+
+ /* child tags */
+ case TN_CRED:
+ rc = buildCred(pDecoder, (VoidPtr_t)&pMap->cred);
+ break;
+ case TN_SOURCE:
+ rc = buildTargetOrSource(pDecoder, (VoidPtr_t)&pMap->source);
+ source++;
+ break;
+ case TN_TARGET:
+ rc = buildTargetOrSource(pDecoder, (VoidPtr_t)&pMap->target);
+ target++;
+ break;
+#ifdef MAPITEM_RECEIVE
+ /* Lists */
+ case TN_MAPITEM:
+ rc = appendMapItemList(pDecoder, &pMap->mapItemList);
+ mapitems++;
+ break;
+#endif
+ default:
+ rc = SML_DECODEERROR(SML_ERR_XLT_INVAL_SYNCML_DOC,pScanner,"buildMap");
+ }
+ if (rc != SML_ERR_OK) {
+ smlFreeMap(pMap);
+ return rc;
+ }
+ if (((rc = nextToken(pDecoder)) != SML_ERR_OK)) {
+ smlFreeMap(pMap);
+ return rc;
+ }
+ }
+
+ if ((source == 0) || (mapitems == 0) || (target == 0) || (cmdid == 0)) {
+ smlFreeMap(pMap);
+ return SML_DECODEERROR(SML_ERR_XLT_INVAL_SYNCML_DOC,pScanner,"buildMap");
+ }
+
+ *ppElem = pMap;
+
+ return SML_ERR_OK;
+}
+#endif
+
+#ifdef SEARCH_RECEIVE
+Ret_t
+buildSearch(XltDecoderPtr_t pDecoder, VoidPtr_t *ppSearch)
+{
+ XltDecScannerPtr_t pScanner;
+ SmlSearchPtr_t pSearch;
+ Ret_t rc;
+ Long_t source = 0, meta = 0, data = 0, cmdid = 0;
+
+ pScanner = pDecoder->scanner;
+
+ if (*ppSearch != NULL)
+ return SML_DECODEERROR(SML_ERR_XLT_INVAL_SYNCML_DOC,pScanner,"buildSearch");
+
+ if ((pSearch = (SmlSearchPtr_t)smlLibMalloc(sizeof(SmlSearch_t))) == NULL)
+ return SML_ERR_NOT_ENOUGH_SPACE;
+ smlLibMemset(pSearch, 0, sizeof(SmlSearch_t));
+
+ /* initialize the element type field */
+ pSearch->elementType = SML_PE_SEARCH;
+
+ /* Meta is required */
+ if (IS_EMPTY(pScanner->curtok)) {
+ smlLibFree(pSearch);
+ return SML_DECODEERROR(SML_ERR_XLT_INVAL_SYNCML_DOC,pScanner,"buildSearch");
+ }
+
+ if (((rc = nextToken(pDecoder)) != SML_ERR_OK)) {
+ smlLibFree(pSearch);
+ return rc;
+ }
+
+ while (pScanner->curtok->type != TOK_TAG_END) {
+ switch (pScanner->curtok->tagid) {
+
+ /* PCDATA elements */
+ case TN_CMDID:
+ rc = buildPCData(pDecoder, (VoidPtr_t)&pSearch->cmdID);
+ cmdid++;
+ break;
+ case TN_LANG:
+ rc = buildPCData(pDecoder, (VoidPtr_t)&pSearch->lang);
+ break;
+ case TN_META:
+ rc = buildPCData(pDecoder, (VoidPtr_t)&pSearch->meta);
+ meta++;
+ break;
+ case TN_DATA:
+ rc = buildPCData(pDecoder, (VoidPtr_t)&pSearch->data);
+ data++;
+ break;
+
+
+ /* child tags */
+ case TN_CRED:
+ rc = buildCred(pDecoder, (VoidPtr_t)&pSearch->cred);
+ break;
+ case TN_TARGET:
+ rc = buildTargetOrSource(pDecoder, (VoidPtr_t)&pSearch->target);
+ break;
+
+ /* flags */
+ case TN_NORESP:
+ pSearch->flags |= SmlNoResp_f;
+ rc = buildEmptyTag(pDecoder); // allow for <tag></tag> instead of <tag/>
+ break;
+ case TN_NORESULTS:
+ pSearch->flags |= SmlNoResults_f;
+ rc = buildEmptyTag(pDecoder); // allow for <tag></tag> instead of <tag/>
+ break;
+
+ /* Lists */
+ case TN_SOURCE:
+ rc = appendSourceList(pDecoder, &pSearch->sourceList);
+ source++;
+ break;
+
+ default:
+ rc = SML_DECODEERROR(SML_ERR_XLT_INVAL_SYNCML_DOC,pScanner,"buildSearch");
+ }
+ if (rc != SML_ERR_OK) {
+ smlFreeSearch(pSearch);
+ return rc;
+ }
+ if (((rc = nextToken(pDecoder)) != SML_ERR_OK)) {
+ smlFreeSearch(pSearch);
+ return rc;
+ }
+ }
+
+ if ((source == 0) || (meta == 0) || (data == 0) || (cmdid == 0)) {
+ smlFreeSearch(pSearch);
+ return SML_DECODEERROR(SML_ERR_XLT_INVAL_SYNCML_DOC,pScanner,"buildSearch");
+ }
+
+ *ppSearch = pSearch;
+
+ return SML_ERR_OK;
+}
+#endif
+
+Ret_t
+buildPutOrGet(XltDecoderPtr_t pDecoder, VoidPtr_t *ppElem)
+{
+ XltDecScannerPtr_t pScanner;
+ SmlGetPtr_t pGet;
+ Ret_t rc;
+ Long_t items = 0, cmdid = 0;
+
+ pScanner = pDecoder->scanner;
+
+ if (*ppElem != NULL)
+ return SML_DECODEERROR(SML_ERR_XLT_INVAL_SYNCML_DOC,pScanner,"buildPutOrGet");
+
+ if ((pGet = (SmlGetPtr_t)smlLibMalloc(sizeof(SmlGet_t))) == NULL)
+ return SML_ERR_NOT_ENOUGH_SPACE;
+ smlLibMemset(pGet, 0, sizeof(SmlGet_t));
+
+ /* initialize the element type field */
+ pGet->elementType = SML_PE_PUT_GET;
+
+ if (IS_EMPTY(pScanner->curtok)) {
+ smlLibFree(pGet);
+ return SML_DECODEERROR(SML_ERR_XLT_INVAL_SYNCML_DOC,pScanner,"buildPutOrGet");
+ }
+
+ if (((rc = nextToken(pDecoder)) != SML_ERR_OK)) {
+ smlLibFree(pGet);
+ return rc;
+ }
+
+ while (pScanner->curtok->type != TOK_TAG_END) {
+ switch (pScanner->curtok->tagid) {
+
+ /* PCDATA elements */
+ case TN_CMDID:
+ rc = buildPCData(pDecoder, (VoidPtr_t)&pGet->cmdID);
+ cmdid++;
+ break;
+ case TN_LANG:
+ rc = buildPCData(pDecoder, (VoidPtr_t)&pGet->lang);
+ break;
+ case TN_META:
+ rc = buildPCData(pDecoder, (VoidPtr_t)&pGet->meta);
+ break;
+
+ /* child tags */
+ case TN_CRED:
+ rc = buildCred(pDecoder, (VoidPtr_t)&pGet->cred);
+ break;
+
+ /* flags */
+ case TN_NORESP:
+ pGet->flags |= SmlNoResp_f;
+ rc = buildEmptyTag(pDecoder); // allow for <tag></tag> instead of <tag/>
+ break;
+
+ /* Lists */
+
+ case TN_ITEM:
+ rc = appendItemList(pDecoder, &pGet->itemList);
+ items++;
+ break;
+
+ default:
+ rc = SML_DECODEERROR(SML_ERR_XLT_INVAL_SYNCML_DOC,pScanner,"buildPutOrGet");
+ }
+ if (rc != SML_ERR_OK) {
+ smlFreeGetPut(pGet);
+ return rc;
+ }
+ if (((rc = nextToken(pDecoder)) != SML_ERR_OK)) {
+ smlFreeGetPut(pGet);
+ return rc;
+ }
+ }
+
+ if ((items == 0) || (cmdid == 0))
+ {
+ smlFreeGetPut(pGet);
+ return SML_DECODEERROR(SML_ERR_XLT_INVAL_SYNCML_DOC,pScanner,"buildPutOrGet");
+ }
+
+ *ppElem = pGet;
+
+ return SML_ERR_OK;
+}
+
+
+Ret_t
+buildRecordFieldFilter(XltDecoderPtr_t pDecoder, VoidPtr_t *ppRecordFieldFilter)
+{
+ XltDecScannerPtr_t pScanner;
+ SmlRecordOrFieldFilterPtr_t pRecordFieldFilter;
+ Long_t item = 0;
+ Ret_t rc;
+
+ pScanner = pDecoder->scanner;
+
+ if (*ppRecordFieldFilter != NULL)
+ return SML_DECODEERROR(SML_ERR_XLT_INVAL_SYNCML_DOC,pScanner,"buildRecordFieldFilter");
+
+ if ((pRecordFieldFilter = (SmlRecordOrFieldFilterPtr_t)smlLibMalloc(sizeof(SmlRecordOrFieldFilter_t))) == NULL)
+ return SML_ERR_NOT_ENOUGH_SPACE;
+ smlLibMemset(pRecordFieldFilter, 0, sizeof(SmlRecordOrFieldFilter_t));
+
+ if (IS_EMPTY(pScanner->curtok)) {
+ smlLibFree(pRecordFieldFilter);
+ return SML_ERR_OK;
+ }
+
+ if (((rc = nextToken(pDecoder)) != SML_ERR_OK)) {
+ smlLibFree(pRecordFieldFilter);
+ return rc;
+ }
+
+ while (pScanner->curtok->type != TOK_TAG_END) {
+ switch (pScanner->curtok->tagid) {
+
+ /* Item */
+ case TN_ITEM:
+ rc = buildItem(pDecoder, (VoidPtr_t)&pRecordFieldFilter->item);
+ item++;
+ break;
+
+ default:
+ rc = SML_DECODEERROR(SML_ERR_XLT_INVAL_SYNCML_DOC,pScanner,"buildRecordFieldFilter");
+ }
+ if (rc != SML_ERR_OK) {
+ smlFreeRecordFieldFilterPtr(pRecordFieldFilter);
+ return rc;
+ }
+ if (((rc = nextToken(pDecoder)) != SML_ERR_OK)) {
+ smlFreeRecordFieldFilterPtr(pRecordFieldFilter);
+ return rc;
+ }
+ }
+
+ if (item == 0)
+ {
+ smlFreeRecordFieldFilterPtr(pRecordFieldFilter);
+ return SML_DECODEERROR(SML_ERR_XLT_INVAL_SYNCML_DOC,pScanner,"buildRecordFieldFilter");
+ }
+
+ *ppRecordFieldFilter = pRecordFieldFilter;
+
+ return SML_ERR_OK;
+}
+
+
+Ret_t
+buildFilter(XltDecoderPtr_t pDecoder, VoidPtr_t *ppFilter)
+{
+ XltDecScannerPtr_t pScanner;
+ SmlFilterPtr_t pFilter;
+ Long_t meta = 0;
+ Ret_t rc;
+
+ pScanner = pDecoder->scanner;
+
+ if (*ppFilter != NULL)
+ return SML_DECODEERROR(SML_ERR_XLT_INVAL_SYNCML_DOC,pScanner,"buildFilter");
+
+ if ((pFilter = (SmlFilterPtr_t)smlLibMalloc(sizeof(SmlFilter_t))) == NULL)
+ return SML_ERR_NOT_ENOUGH_SPACE;
+ smlLibMemset(pFilter, 0, sizeof(SmlFilter_t));
+
+ if (IS_EMPTY(pScanner->curtok)) {
+ smlLibFree(pFilter);
+ return SML_ERR_OK;
+ }
+
+ if (((rc = nextToken(pDecoder)) != SML_ERR_OK)) {
+ smlLibFree(pFilter);
+ return rc;
+ }
+
+ while (pScanner->curtok->type != TOK_TAG_END) {
+ switch (pScanner->curtok->tagid) {
+
+ /* PCDATA elements */
+ case TN_META:
+ rc = buildPCData(pDecoder, (VoidPtr_t)&pFilter->meta);
+ meta++;
+ break;
+
+ /* Record or Field elements */
+ case TN_FIELD:
+ rc = buildRecordFieldFilter(pDecoder, (VoidPtr_t)&pFilter->field);
+ break;
+
+ case TN_RECORD:
+ rc = buildRecordFieldFilter(pDecoder, (VoidPtr_t)&pFilter->record);
+ break;
+
+ /* Flags */
+ case TN_FILTERTYPE:
+ rc = buildPCData(pDecoder, (VoidPtr_t)&pFilter->filtertype);
+ break;
+
+ default:
+ rc = SML_DECODEERROR(SML_ERR_XLT_INVAL_SYNCML_DOC,pScanner,"buildFilter");
+ }
+ if (rc != SML_ERR_OK) {
+ smlFreeFilterPtr(pFilter);
+ return rc;
+ }
+ if (((rc = nextToken(pDecoder)) != SML_ERR_OK)) {
+ smlFreeFilterPtr(pFilter);
+ return rc;
+ }
+ }
+
+ if (meta == 0)
+ {
+ smlFreeFilterPtr(pFilter);
+ return SML_DECODEERROR(SML_ERR_XLT_INVAL_SYNCML_DOC,pScanner,"buildFilter");
+ }
+
+ *ppFilter = pFilter;
+
+ return SML_ERR_OK;
+}
+
+
+Ret_t
+buildTargetOrSourceParent(XltDecoderPtr_t pDecoder, VoidPtr_t *ppTargetParent)
+{
+ XltDecScannerPtr_t pScanner;
+ SmlTargetParentPtr_t pTargetParent;
+ Long_t locuri = 0;
+ Ret_t rc;
+
+ pScanner = pDecoder->scanner;
+
+ if (*ppTargetParent != NULL)
+ return SML_DECODEERROR(SML_ERR_XLT_INVAL_SYNCML_DOC,pScanner,"buildTargetOrSourceParent");
+
+ if ((pTargetParent = (SmlTargetParentPtr_t)smlLibMalloc(sizeof(SmlTargetParent_t))) == NULL)
+ return SML_ERR_NOT_ENOUGH_SPACE;
+ smlLibMemset(pTargetParent, 0, sizeof(SmlTargetParent_t));
+
+ if (IS_EMPTY(pScanner->curtok)) {
+ smlLibFree(pTargetParent);
+ return SML_ERR_OK;
+ }
+
+ if (((rc = nextToken(pDecoder)) != SML_ERR_OK)) {
+ smlLibFree(pTargetParent);
+ return rc;
+ }
+
+ while (pScanner->curtok->type != TOK_TAG_END) {
+ switch (pScanner->curtok->tagid) {
+
+ /* PCDATA elements */
+ case TN_LOCURI:
+ rc = buildPCData(pDecoder, (VoidPtr_t)&pTargetParent->locURI);
+ locuri++;
+ break;
+ default:
+ rc = SML_DECODEERROR(SML_ERR_XLT_INVAL_SYNCML_DOC,pScanner,"buildTargetOrSourceParent");
+ }
+ if (rc != SML_ERR_OK) {
+ smlFreeSourceTargetParentPtr(pTargetParent);
+ return rc;
+ }
+ if (((rc = nextToken(pDecoder)) != SML_ERR_OK)) {
+ smlFreeSourceTargetParentPtr(pTargetParent);
+ return rc;
+ }
+ }
+
+ if (locuri == 0)
+ {
+ smlFreeSourceTargetParentPtr(pTargetParent);
+ return SML_DECODEERROR(SML_ERR_XLT_INVAL_SYNCML_DOC,pScanner,"buildTargetOrSourceParent");
+ }
+
+ *ppTargetParent = pTargetParent;
+
+ return SML_ERR_OK;
+}
+
+
+Ret_t
+buildTargetOrSource(XltDecoderPtr_t pDecoder, VoidPtr_t *ppTarget)
+{
+ XltDecScannerPtr_t pScanner;
+ SmlTargetPtr_t pTarget;
+ Long_t locuri = 0;
+ Ret_t rc;
+
+ pScanner = pDecoder->scanner;
+
+ if (*ppTarget != NULL)
+ return SML_DECODEERROR(SML_ERR_XLT_INVAL_SYNCML_DOC,pScanner,"buildTargetOrSourceParent");
+
+ if ((pTarget = (SmlTargetPtr_t)smlLibMalloc(sizeof(SmlTarget_t))) == NULL)
+ return SML_ERR_NOT_ENOUGH_SPACE;
+ smlLibMemset(pTarget, 0, sizeof(SmlTarget_t));
+
+ if (IS_EMPTY(pScanner->curtok)) {
+ smlLibFree(pTarget);
+ return SML_ERR_OK;
+ }
+
+ if (((rc = nextToken(pDecoder)) != SML_ERR_OK)) {
+ smlLibFree(pTarget);
+ return rc;
+ }
+
+ while (pScanner->curtok->type != TOK_TAG_END) {
+ switch (pScanner->curtok->tagid) {
+
+ /* PCDATA elements */
+ case TN_LOCURI:
+ rc = buildPCData(pDecoder, (VoidPtr_t)&pTarget->locURI);
+ locuri++;
+ break;
+ case TN_LOCNAME:
+ rc = buildPCData(pDecoder, (VoidPtr_t)&pTarget->locName);
+ break;
+ /* SyncML 1.2 Filter element, %%% added 2005-08-17 by synthesis/luz */
+ case TN_FILTER:
+ rc = buildFilter(pDecoder, (VoidPtr_t)&pTarget->filter);
+ break;
+ default:
+ rc = SML_DECODEERROR(SML_ERR_XLT_INVAL_SYNCML_DOC,pScanner,"buildTargetOrSourceParent");
+ }
+ if (rc != SML_ERR_OK) {
+ smlFreeSourceTargetPtr(pTarget);
+ return rc;
+ }
+ if (((rc = nextToken(pDecoder)) != SML_ERR_OK)) {
+ smlFreeSourceTargetPtr(pTarget);
+ return rc;
+ }
+ }
+
+ if (locuri == 0)
+ {
+ smlFreeSourceTargetPtr(pTarget);
+ return SML_DECODEERROR(SML_ERR_XLT_INVAL_SYNCML_DOC,pScanner,"buildTargetOrSourceParent");
+ }
+
+ *ppTarget = pTarget;
+
+ return SML_ERR_OK;
+}
+
+Ret_t
+buildChal(XltDecoderPtr_t pDecoder, VoidPtr_t *ppChal)
+{
+ XltDecScannerPtr_t pScanner;
+ SmlChalPtr_t pChal;
+ Long_t meta = 0;
+ Ret_t rc;
+
+ pScanner = pDecoder->scanner;
+
+ if (*ppChal != NULL)
+ return SML_DECODEERROR(SML_ERR_XLT_INVAL_SYNCML_DOC,pScanner,"buildChal");
+
+ if ((pChal = (SmlChalPtr_t)smlLibMalloc(sizeof(SmlChal_t))) == NULL)
+ return SML_ERR_NOT_ENOUGH_SPACE;
+ smlLibMemset(pChal, 0, sizeof(SmlChal_t));
+
+ if (IS_EMPTY(pScanner->curtok)) {
+ *ppChal = pChal;
+ return SML_ERR_OK;
+ }
+
+ if (((rc = nextToken(pDecoder)) != SML_ERR_OK)) {
+ smlLibFree(pChal);
+ return rc;
+ }
+
+ while (pScanner->curtok->type != TOK_TAG_END) {
+ switch (pScanner->curtok->tagid) {
+
+ /* PCDATA elements */
+ case TN_META:
+ rc = buildPCData(pDecoder, (VoidPtr_t)&pChal->meta);
+ meta++;
+ break;
+
+ default:
+ rc = SML_DECODEERROR(SML_ERR_XLT_INVAL_SYNCML_DOC,pScanner,"buildChal");
+ }
+ if (rc != SML_ERR_OK) {
+ smlFreeChalPtr(pChal);
+ return rc;
+ }
+ if (((rc = nextToken(pDecoder)) != SML_ERR_OK)) {
+ smlFreeChalPtr(pChal);
+ return rc;
+ }
+ }
+
+ if (meta == 0)
+ {
+ smlFreeChalPtr(pChal);
+ return SML_DECODEERROR(SML_ERR_XLT_INVAL_SYNCML_DOC,pScanner,"buildChal");
+ }
+
+ *ppChal = pChal;
+
+ return SML_ERR_OK;
+}
+
+Ret_t
+buildCred(XltDecoderPtr_t pDecoder, VoidPtr_t *ppCred)
+{
+ XltDecScannerPtr_t pScanner;
+ SmlCredPtr_t pCred;
+ Ret_t rc;
+ Long_t data = 0;
+
+ pScanner = pDecoder->scanner;
+
+ if (*ppCred != NULL)
+ return SML_DECODEERROR(SML_ERR_XLT_INVAL_SYNCML_DOC,pScanner,"buildCred");
+
+ if ((pCred = (SmlCredPtr_t)smlLibMalloc(sizeof(SmlCred_t))) == NULL)
+ return SML_ERR_NOT_ENOUGH_SPACE;
+ smlLibMemset(pCred, 0, sizeof(SmlCred_t));
+
+ if (IS_EMPTY(pScanner->curtok)) {
+ *ppCred = pCred;
+ return SML_ERR_OK;
+ }
+
+ if (((rc = nextToken(pDecoder)) != SML_ERR_OK)) {
+ smlLibFree(pCred);
+ return rc;
+ }
+
+ while (pScanner->curtok->type != TOK_TAG_END) {
+ switch (pScanner->curtok->tagid) {
+
+ /* PCDATA elements */
+ case TN_DATA:
+ rc = buildPCData(pDecoder, (VoidPtr_t)&pCred->data);
+ data++;
+ break;
+ case TN_META:
+ rc = buildPCData(pDecoder, (VoidPtr_t)&pCred->meta);
+ break;
+
+ default:
+ rc = SML_DECODEERROR(SML_ERR_XLT_INVAL_SYNCML_DOC,pScanner,"buildCred");
+ }
+ if (rc != SML_ERR_OK) {
+ smlFreeCredPtr(pCred);
+ return rc;
+ }
+ if (((rc = nextToken(pDecoder)) != SML_ERR_OK)) {
+ smlFreeCredPtr(pCred);
+ return rc;
+ }
+ }
+
+ if (data == 0)
+ {
+ smlFreeCredPtr(pCred);
+ return SML_DECODEERROR(SML_ERR_XLT_INVAL_SYNCML_DOC,pScanner,"buildCred");
+ }
+
+ *ppCred = pCred;
+
+ return SML_ERR_OK;
+}
+
+Ret_t
+buildItem(XltDecoderPtr_t pDecoder, VoidPtr_t *ppElem)
+{
+ XltDecScannerPtr_t pScanner;
+ SmlItemPtr_t pItem;
+ Ret_t rc;
+
+ pScanner = pDecoder->scanner;
+
+ if (*ppElem != NULL)
+ return SML_DECODEERROR(SML_ERR_XLT_INVAL_SYNCML_DOC,pScanner,"buildItem");
+
+ if ((pItem = (SmlItemPtr_t)smlLibMalloc(sizeof(SmlItem_t))) == NULL)
+ return SML_ERR_NOT_ENOUGH_SPACE;
+ smlLibMemset(pItem, 0, sizeof(SmlItem_t));
+
+ /* Item might be empty */
+ if (IS_EMPTY(pScanner->curtok)) {
+ *ppElem = pItem;
+ return SML_ERR_OK;
+ }
+
+ if (((rc = nextToken(pDecoder)) != SML_ERR_OK)) {
+ smlLibFree(pItem);
+ return rc;
+ }
+
+ while (pScanner->curtok->type != TOK_TAG_END) {
+ switch (pScanner->curtok->tagid) {
+
+ /* PCDATA elements */
+ case TN_META:
+ rc = buildPCData(pDecoder, (VoidPtr_t)&pItem->meta);
+ break;
+ case TN_DATA:
+ rc = buildPCData(pDecoder, (VoidPtr_t)&pItem->data);
+#ifdef __USE_EXTENSIONS__
+#ifdef __SML_WBXML__
+ if (pItem->data && pItem->data->contentType == SML_PCDATA_OPAQUE)
+ subdtdDecodeWbxml(pDecoder, (SmlPcdataPtr_t*)&pItem->data);
+#endif
+#endif
+ break;
+ /* child tags */
+ case TN_TARGET:
+ rc = buildTargetOrSource(pDecoder, (VoidPtr_t)&pItem->target);
+ break;
+ case TN_SOURCE:
+ rc = buildTargetOrSource(pDecoder, (VoidPtr_t)&pItem->source);
+ break;
+ case TN_TARGETPARENT:
+ rc = buildTargetOrSourceParent(pDecoder, (VoidPtr_t)&pItem->targetParent);
+ break;
+ case TN_SOURCEPARENT:
+ rc = buildTargetOrSourceParent(pDecoder, (VoidPtr_t)&pItem->sourceParent);
+ break;
+
+ /* flags */
+ case TN_MOREDATA:
+ pItem->flags |= SmlMoreData_f;
+ rc = buildEmptyTag(pDecoder); // allow for <tag></tag> instead of <tag/>
+ break;
+
+ default:
+ rc = SML_DECODEERROR(SML_ERR_XLT_INVAL_SYNCML_DOC,pScanner,"buildItem");
+ }
+ if (rc != SML_ERR_OK) {
+ smlFreeItemPtr(pItem);
+ return rc;
+ }
+ if (((rc = nextToken(pDecoder)) != SML_ERR_OK)) {
+ smlFreeItemPtr(pItem);
+ return rc;
+ }
+ }
+
+ *ppElem = pItem;
+
+ return SML_ERR_OK;
+}
+
+#ifdef MAPITEM_RECEIVE
+Ret_t
+buildMapItem(XltDecoderPtr_t pDecoder, VoidPtr_t *ppElem)
+{
+ XltDecScannerPtr_t pScanner;
+ SmlMapItemPtr_t pMapItem;
+ Long_t target = 0, source = 0;
+ Ret_t rc;
+
+ pScanner = pDecoder->scanner;
+
+ if (*ppElem != NULL)
+ return SML_DECODEERROR(SML_ERR_XLT_INVAL_SYNCML_DOC,pScanner,"buildMapItem");
+
+ if ((pMapItem = (SmlMapItemPtr_t)smlLibMalloc(sizeof(SmlMapItem_t))) == NULL)
+ return SML_ERR_NOT_ENOUGH_SPACE;
+ smlLibMemset(pMapItem, 0, sizeof(SmlMapItem_t));
+
+ if (IS_EMPTY(pScanner->curtok)) {
+ smlLibFree(pMapItem);
+ return SML_DECODEERROR(SML_ERR_XLT_INVAL_SYNCML_DOC,pScanner,"buildMapItem");
+ }
+
+ if (((rc = nextToken(pDecoder)) != SML_ERR_OK)) {
+ smlLibFree(pMapItem);
+ return rc;
+ }
+
+ while (pScanner->curtok->type != TOK_TAG_END) {
+ switch (pScanner->curtok->tagid) {
+ /* child tags */
+ case TN_TARGET:
+ rc = buildTargetOrSource(pDecoder, (VoidPtr_t)&pMapItem->target);
+ target++;
+ break;
+ case TN_SOURCE:
+ rc = buildTargetOrSource(pDecoder, (VoidPtr_t)&pMapItem->source);
+ source++;
+ break;
+
+ default:
+ rc = SML_DECODEERROR(SML_ERR_XLT_INVAL_SYNCML_DOC,pScanner,"buildMapItem");
+ }
+ if (rc != SML_ERR_OK) {
+ smlFreeMapItemPtr(pMapItem);
+ return rc;
+ }
+ if (((rc = nextToken(pDecoder)) != SML_ERR_OK)) {
+ smlFreeMapItemPtr(pMapItem);
+ return rc;
+ }
+ }
+
+ if ((target == 0) || (source == 0)) {
+ smlFreeMapItemPtr(pMapItem);
+ return SML_DECODEERROR(SML_ERR_XLT_INVAL_SYNCML_DOC,pScanner,"buildMapItem");
+ }
+
+ *ppElem = pMapItem;
+
+ return SML_ERR_OK;
+}
+
+#endif
+
+Ret_t
+buildStatus(XltDecoderPtr_t pDecoder, VoidPtr_t *ppElem)
+{
+ XltDecScannerPtr_t pScanner;
+ SmlStatusPtr_t pStatus;
+ Ret_t rc;
+ Long_t cmd = 0, data = 0, cmdid = 0;
+
+ pScanner = pDecoder->scanner;
+
+ if (*ppElem != NULL)
+ return SML_DECODEERROR(SML_ERR_XLT_INVAL_SYNCML_DOC,pScanner,"buildStatus");
+
+ if ((pStatus = (SmlStatusPtr_t)smlLibMalloc(sizeof(SmlStatus_t))) == NULL)
+ return SML_ERR_NOT_ENOUGH_SPACE;
+ smlLibMemset(pStatus, 0, sizeof(SmlStatus_t));
+
+ /* initialize the element type field */
+ pStatus->elementType = SML_PE_STATUS;
+
+ if (IS_EMPTY(pScanner->curtok)) {
+ smlLibFree(pStatus);
+ return SML_DECODEERROR(SML_ERR_XLT_INVAL_SYNCML_DOC,pScanner,"buildStatus");
+ }
+
+ if (((rc = nextToken(pDecoder)) != SML_ERR_OK)) {
+ smlLibFree(pStatus);
+ return rc;
+ }
+
+ while (pScanner->curtok->type != TOK_TAG_END) {
+ switch (pScanner->curtok->tagid) {
+
+ /* PCData elements */
+ case TN_CMDID:
+ rc = buildPCData(pDecoder, (VoidPtr_t)&pStatus->cmdID);
+ cmdid++;
+ break;
+ case TN_MSGREF:
+ rc = buildPCData(pDecoder, (VoidPtr_t)&pStatus->msgRef);
+ break;
+ case TN_CMDREF:
+ rc = buildPCData(pDecoder, (VoidPtr_t)&pStatus->cmdRef);
+ break;
+ case TN_CMD:
+ rc = buildPCData(pDecoder, (VoidPtr_t)&pStatus->cmd);
+ cmd++;
+ break;
+ case TN_DATA:
+ rc = buildPCData(pDecoder, (VoidPtr_t)&pStatus->data);
+ data++;
+ break;
+ case TN_CHAL:
+ rc = buildChal(pDecoder, (VoidPtr_t)&pStatus->chal);
+ break;
+ case TN_CRED:
+ rc = buildCred(pDecoder, (VoidPtr_t)&pStatus->cred);
+ break;
+
+ /* Lists */
+ case TN_ITEM:
+ rc = appendItemList(pDecoder, (VoidPtr_t)&pStatus->itemList);
+ break;
+ case TN_TARGETREF:
+ rc = appendTargetRefList(pDecoder, (VoidPtr_t)&pStatus->targetRefList);
+ break;
+ case TN_SOURCEREF:
+ rc = appendSourceRefList(pDecoder, (VoidPtr_t)&pStatus->sourceRefList);
+ break;
+
+ default:
+ rc = SML_DECODEERROR(SML_ERR_XLT_INVAL_SYNCML_DOC,pScanner,"buildStatus");
+ }
+ if (rc != SML_ERR_OK) {
+ smlFreeStatus(pStatus);
+ return rc;
+ }
+ if (((rc = nextToken(pDecoder)) != SML_ERR_OK)) {
+ smlFreeStatus(pStatus);
+ return rc;
+ }
+ }
+
+ if ((cmd == 0) || (data == 0) || (cmdid == 0))
+ {
+ smlFreeStatus(pStatus);
+ return SML_DECODEERROR(SML_ERR_XLT_INVAL_SYNCML_DOC,pScanner,"buildStatus");
+ }
+
+ *ppElem = pStatus;
+
+ return SML_ERR_OK;
+}
+
+#ifdef RESULT_RECEIVE
+Ret_t
+buildResults(XltDecoderPtr_t pDecoder, VoidPtr_t *ppResults)
+{
+ XltDecScannerPtr_t pScanner;
+ SmlResultsPtr_t pResults;
+ Ret_t rc;
+ Long_t cmdref = 0, items = 0, cmdid = 0;
+
+ pScanner = pDecoder->scanner;
+
+ if (*ppResults != NULL)
+ return SML_DECODEERROR(SML_ERR_XLT_INVAL_SYNCML_DOC,pScanner,"buildResults");
+
+ if ((pResults = (SmlResultsPtr_t)smlLibMalloc(sizeof(SmlResults_t))) == NULL)
+ return SML_ERR_NOT_ENOUGH_SPACE;
+ smlLibMemset(pResults, 0, sizeof(SmlResults_t));
+
+ /* initialize the element type field */
+ pResults->elementType = SML_PE_RESULTS;
+
+ if (IS_EMPTY(pScanner->curtok)) {
+ smlLibFree(pResults);
+ return SML_DECODEERROR(SML_ERR_XLT_INVAL_SYNCML_DOC,pScanner,"buildResults");
+ }
+
+ if (((rc = nextToken(pDecoder)) != SML_ERR_OK)) {
+ smlLibFree(pResults);
+ return rc;
+ }
+
+ while (pScanner->curtok->type != TOK_TAG_END) {
+ switch (pScanner->curtok->tagid) {
+
+ /* PCDATA elements */
+ case TN_CMDID:
+ rc = buildPCData(pDecoder, (VoidPtr_t)&pResults->cmdID);
+ cmdid++;
+ break;
+ case TN_MSGREF:
+ rc = buildPCData(pDecoder, (VoidPtr_t)&pResults->msgRef);
+ break;
+ case TN_CMDREF:
+ rc = buildPCData(pDecoder, (VoidPtr_t)&pResults->cmdRef);
+ cmdref++;
+ break;
+ case TN_META:
+ rc = buildPCData(pDecoder, (VoidPtr_t)&pResults->meta);
+ break;
+ case TN_TARGETREF:
+ rc = buildPCData(pDecoder, (VoidPtr_t)&pResults->targetRef);
+ break;
+ case TN_SOURCEREF:
+ rc = buildPCData(pDecoder, (VoidPtr_t)&pResults->sourceRef);
+ break;
+
+ /* Lists */
+ case TN_ITEM:
+ rc = appendItemList(pDecoder, &pResults->itemList);
+ items++;
+ break;
+
+ default:
+ rc = SML_DECODEERROR(SML_ERR_XLT_INVAL_SYNCML_DOC,pScanner,"buildResults");
+ }
+ if (rc != SML_ERR_OK) {
+ smlFreeResults(pResults);
+ return rc;
+ }
+ if (((rc = nextToken(pDecoder)) != SML_ERR_OK)) {
+ smlFreeResults(pResults);
+ return rc;
+ }
+ }
+
+ if ((cmdref == 0) || (items == 0) || (cmdid == 0))
+ {
+ smlFreeResults(pResults);
+ return SML_DECODEERROR(SML_ERR_XLT_INVAL_SYNCML_DOC,pScanner,"buildResults");
+ }
+
+ *ppResults = pResults;
+
+ return SML_ERR_OK;
+}
+
+#endif
+
+
+
+/// @brief can be called to process both forms of an empty tag (<tag></tag> and <tag/>
+Ret_t
+buildEmptyTag(XltDecoderPtr_t pDecoder)
+{
+ Ret_t rc;
+ // if current tag is empty (that is, ist a start-and-end like <tag/>), that's simply ok
+ if (IS_EMPTY(pDecoder->scanner->curtok))
+ return SML_ERR_OK;
+ // otherwise, we need a next tag...
+ if (((rc = nextToken(pDecoder)) != SML_ERR_OK))
+ return rc; // no next tag found
+ // ...and it must be a closing tag
+ if (!IS_END(pDecoder->scanner->curtok))
+ return SML_DECODEERROR(SML_ERR_XLT_INVAL_SYNCML_DOC,pDecoder->scanner,"buildEmptyTag");
+ // yep, ok
+ return SML_ERR_OK;
+}
+
+
+Ret_t
+buildPCData(XltDecoderPtr_t pDecoder, VoidPtr_t *ppPCData)
+{
+ XltDecScannerPtr_t pScanner;
+ SmlPcdataPtr_t pPCData = 0;
+ SmlPcdataExtension_t ext;
+ Ret_t rc;
+
+ pScanner = pDecoder->scanner;
+
+ if (*ppPCData != NULL)
+ return SML_DECODEERROR(SML_ERR_XLT_INVAL_SYNCML_DOC,pScanner,"buildPCData_1");
+
+ if (IS_EMPTY(pScanner->curtok)) {
+ if ((pPCData = (SmlPcdataPtr_t)smlLibMalloc(sizeof(SmlPcdata_t))) == NULL)
+ return SML_ERR_NOT_ENOUGH_SPACE;
+
+ smlLibMemset(pPCData, 0, sizeof(SmlPcdata_t));
+
+ *ppPCData = pPCData;
+ return SML_ERR_OK;
+ }
+
+ pPCData = NULL;
+
+ if (((rc = nextToken(pDecoder)) != SML_ERR_OK)) {
+ if (rc == SML_DECODEERROR(SML_ERR_XLT_INVAL_SYNCML_DOC,pScanner,"buildPCData_2")) { /* leaks if dtd failed */
+ pPCData = pScanner->curtok->pcdata;
+ *ppPCData = pPCData;
+ }
+
+ return rc;
+ }
+
+ if (IS_CONTENT(pScanner->curtok)) {
+ /* PCData element has a regular string or opaque content */
+ while (pScanner->curtok->type == TOK_CONT) {
+ if (pPCData == NULL)
+ pPCData = pScanner->curtok->pcdata;
+ else {
+ pPCData = concatPCData(pPCData, pScanner->curtok->pcdata);
+ smlLibFree(pScanner->curtok->pcdata->content);
+ smlLibFree(pScanner->curtok->pcdata);
+
+ if (pPCData == NULL)
+ return SML_DECODEERROR(SML_ERR_XLT_INVAL_PCDATA,pScanner,"buildPCData_3");
+ }
+
+ if (((rc = nextToken(pDecoder)) != SML_ERR_OK)) {
+ *ppPCData = pPCData;
+ return rc;
+ }
+ }
+ } else if (IS_START_OR_EMPTY(pScanner->curtok)) {
+ /* PCData element contains an XML dokument that is handled by an
+ extension mechanism */
+ ext = pScanner->curtok->ext;
+ if ((rc = discardToken(pDecoder)) != SML_ERR_OK) return rc;
+ if ((pPCData = (SmlPcdataPtr_t)smlLibMalloc(sizeof(SmlPcdata_t))) == NULL)
+ return SML_ERR_NOT_ENOUGH_SPACE;
+ smlLibMemset(pPCData, 0, sizeof(SmlPcdata_t));
+ pPCData->contentType = SML_PCDATA_EXTENSION;
+ pPCData->extension = ext;
+ switch (ext) {
+#ifdef __USE_METINF__
+ case SML_EXT_METINF:
+ if ((rc = buildMetInfMetInfCmd(pDecoder, (VoidPtr_t)&pPCData->content)) != SML_ERR_OK) {
+ smlLibFree(pPCData);
+ return rc;
+ }
+ break;
+#endif
+#ifdef __USE_DEVINF__
+ case SML_EXT_DEVINF:
+
+ if ((rc = buildDevInfDevInfCmd(pDecoder, (VoidPtr_t)&pPCData->content)) != SML_ERR_OK) {
+
+ smlLibFree(pPCData);
+ return rc;
+ }
+
+ /* the scanner must point to the closing PCDATA tag */
+ if (((rc = nextToken(pDecoder)) != SML_ERR_OK)) {
+ smlLibFree(pPCData);
+ return rc;
+ }
+ break;
+#endif
+ default:
+ // %%% luz 2005-11-11 : added fallback to MetInf (workaround for ill-formed xml like in sync4j pda clients)
+ // if undefined, try with metinf for ill-formatted xml like from sync4j palm client
+ // - act like we had detected a switch in namespace to METINF
+ pPCData->contentType = SML_PCDATA_EXTENSION;
+ pPCData->extension = SML_EXT_METINF;
+ pScanner->curtok->ext = SML_EXT_METINF;
+ if ((rc = buildMetInfMetInfCmd(pDecoder, (VoidPtr_t)&pPCData->content)) != SML_ERR_OK) {
+ smlLibFree(pPCData);
+ return SML_DECODEERROR(SML_ERR_XLT_INVAL_EXT,pScanner,"buildPCData_4"); // if we fail parsing meta here, this is an invalid extension
+ }
+ break;
+ } // switch
+
+ } else if (IS_END(pScanner->curtok)) {
+ /* PCData element is empty */
+ } else {
+ return SML_DECODEERROR(SML_ERR_XLT_INVAL_PCDATA,pScanner,"buildPCData_5");
+ }
+
+
+ if (pScanner->curtok->type != TOK_TAG_END)
+ return SML_DECODEERROR(SML_ERR_XLT_INVAL_PCDATA,pScanner,"buildPCData_6");
+
+ if (pPCData == NULL) {
+ if ((pPCData = (SmlPcdataPtr_t)smlLibMalloc(sizeof(SmlPcdata_t))) == NULL)
+ return SML_ERR_NOT_ENOUGH_SPACE;
+ smlLibMemset(pPCData, 0, sizeof(SmlPcdata_t));
+ }
+
+ *ppPCData = pPCData;
+
+ return SML_ERR_OK;
+}
+
+Ret_t
+buildPCDataList(XltDecoderPtr_t pDecoder, VoidPtr_t *ppPCData)
+{
+ SmlPcdataListPtr_t pPCDataList = NULL, pPrev = NULL;
+
+ pPCDataList = (SmlPcdataListPtr_t) *ppPCData;
+
+ /* advance to the end of the list, and create ther an empty list element */
+ while (pPCDataList != NULL) {
+ pPrev = pPCDataList;
+ pPCDataList = pPrev->next;
+ }
+ if ((pPCDataList = (SmlPcdataListPtr_t)smlLibMalloc(sizeof(SmlPcdataList_t))) == NULL)
+ return SML_ERR_NOT_ENOUGH_SPACE;
+ smlLibMemset(pPCDataList, 0, sizeof(SmlPcdataList_t));
+ if (pPrev != NULL) /* we already had some entries in the list */
+ pPrev->next = pPCDataList;
+ else /* nope we created a new list */
+ *ppPCData = pPCDataList;
+ pPCDataList->data = NULL;
+ /* at this point pPCDataList should point to an valid list element */
+ return buildPCData(pDecoder, (VoidPtr_t)&pPCDataList->data);
+}
+
+
+static Ret_t
+appendItemList(XltDecoderPtr_t pDecoder, SmlItemListPtr_t *ppItemList)
+{
+ SmlItemListPtr_t pNewItemList;
+ SmlItemListPtr_t pItemList;
+ Ret_t rc;
+
+ pItemList = *ppItemList;
+ if (pItemList != NULL)
+ while (pItemList->next != NULL)
+ pItemList = pItemList->next;
+
+ if ((pNewItemList = (SmlItemListPtr_t)smlLibMalloc(sizeof(SmlItemList_t))) == NULL)
+ return SML_ERR_NOT_ENOUGH_SPACE;
+ smlLibMemset(pNewItemList, 0, sizeof(SmlItemList_t));
+
+ if ((rc = buildItem(pDecoder, (VoidPtr_t)&pNewItemList->item)) != SML_ERR_OK) {
+ smlLibFree(pNewItemList);
+ return rc;
+ }
+
+ if (pItemList == NULL)
+ *ppItemList = pNewItemList;
+ else
+ pItemList->next = pNewItemList;
+
+ return SML_ERR_OK;
+}
+
+#ifdef SEARCH_RECEIVE
+static Ret_t
+appendSourceList(XltDecoderPtr_t pDecoder, SmlSourceListPtr_t *ppSourceList)
+{
+ SmlSourceListPtr_t pNewSourceList;
+ SmlSourceListPtr_t pSourceList;
+ Ret_t rc;
+
+ pSourceList = *ppSourceList;
+ if (pSourceList != NULL)
+ while (pSourceList->next != NULL)
+ pSourceList = pSourceList->next;
+
+ if ((pNewSourceList = (SmlSourceListPtr_t)smlLibMalloc(sizeof(SmlSourceList_t))) == NULL)
+ return SML_ERR_NOT_ENOUGH_SPACE;
+ smlLibMemset(pNewSourceList, 0, sizeof(SmlSourceList_t));
+
+ if ((rc = buildTargetOrSource(pDecoder, (VoidPtr_t)&pNewSourceList->source)) != SML_ERR_OK) {
+ smlLibFree(pNewSourceList);
+ return rc;
+ }
+
+ if (pSourceList == NULL)
+ *ppSourceList = pNewSourceList;
+ else
+ pSourceList->next = pNewSourceList;
+
+ return SML_ERR_OK;
+}
+#endif /* SEARCH_RECEIVE */
+
+#ifdef MAPITEM_RECEIVE
+
+static Ret_t
+appendMapItemList(XltDecoderPtr_t pDecoder, SmlMapItemListPtr_t *ppMapItemList)
+{
+ SmlMapItemListPtr_t pNewMapItemList;
+ SmlMapItemListPtr_t pMapItemList;
+ Ret_t rc;
+
+ pMapItemList = *ppMapItemList;
+ if (pMapItemList != NULL)
+ while (pMapItemList->next != NULL)
+ pMapItemList = pMapItemList->next;
+
+ if ((pNewMapItemList = (SmlMapItemListPtr_t)smlLibMalloc(sizeof(SmlMapItemList_t))) == NULL)
+ return SML_ERR_NOT_ENOUGH_SPACE;
+ smlLibMemset(pNewMapItemList, 0, sizeof(SmlMapItemList_t));
+
+ if ((rc = buildMapItem(pDecoder, (VoidPtr_t)&pNewMapItemList->mapItem)) != SML_ERR_OK) {
+ smlLibFree(pNewMapItemList);
+ return rc;
+ }
+
+ if (pMapItemList == NULL)
+ *ppMapItemList = pNewMapItemList;
+ else
+ pMapItemList->next = pNewMapItemList;
+
+ return SML_ERR_OK;
+}
+#endif
+
+static Ret_t
+appendTargetRefList(XltDecoderPtr_t pDecoder, SmlTargetRefListPtr_t *ppTargetRefList)
+{
+ SmlTargetRefListPtr_t pNewTargetRefList;
+ SmlTargetRefListPtr_t pTargetRefList;
+ Ret_t rc;
+
+ pTargetRefList = *ppTargetRefList;
+ if (pTargetRefList != NULL)
+ while (pTargetRefList->next != NULL)
+ pTargetRefList = pTargetRefList->next;
+
+ if ((pNewTargetRefList = (SmlTargetRefListPtr_t)smlLibMalloc(sizeof(SmlTargetRefList_t))) == NULL)
+ return SML_ERR_NOT_ENOUGH_SPACE;
+ smlLibMemset(pNewTargetRefList, 0, sizeof(SmlTargetRefList_t));
+
+ if ((rc = buildPCData(pDecoder, (VoidPtr_t)&pNewTargetRefList->targetRef)) != SML_ERR_OK) {
+ smlFreePcdata(pNewTargetRefList->targetRef);
+ smlLibFree(pNewTargetRefList);
+ return rc;
+ }
+
+ if (pTargetRefList == NULL)
+ *ppTargetRefList = pNewTargetRefList;
+ else
+ pTargetRefList->next = pNewTargetRefList;
+
+ return SML_ERR_OK;
+}
+
+static Ret_t
+appendSourceRefList(XltDecoderPtr_t pDecoder, SmlSourceRefListPtr_t *ppSourceRefList)
+{
+ SmlSourceRefListPtr_t pNewSourceRefList;
+ SmlSourceRefListPtr_t pSourceRefList;
+ Ret_t rc;
+
+ pSourceRefList = *ppSourceRefList;
+ if (pSourceRefList != NULL)
+ while (pSourceRefList->next != NULL)
+ pSourceRefList = pSourceRefList->next;
+
+ if ((pNewSourceRefList = (SmlSourceRefListPtr_t)smlLibMalloc(sizeof(SmlSourceRefList_t))) == NULL)
+ return SML_ERR_NOT_ENOUGH_SPACE;
+ smlLibMemset(pNewSourceRefList, 0, sizeof(SmlSourceRefList_t));
+
+ if ((rc = buildPCData(pDecoder, (VoidPtr_t)&pNewSourceRefList->sourceRef)) != SML_ERR_OK) {
+ smlFreePcdata(pNewSourceRefList->sourceRef);
+ smlLibFree(pNewSourceRefList);
+ return rc;
+ }
+
+ if (pSourceRefList == NULL)
+ *ppSourceRefList = pNewSourceRefList;
+ else
+ pSourceRefList->next = pNewSourceRefList;
+
+ return SML_ERR_OK;
+}
+
diff --git a/src/syncml_tk/src/sml/xlt/all/xltdeccom.h b/src/syncml_tk/src/sml/xlt/all/xltdeccom.h
new file mode 100755
index 0000000..33cf526
--- /dev/null
+++ b/src/syncml_tk/src/sml/xlt/all/xltdeccom.h
@@ -0,0 +1,214 @@
+/**
+ * @file
+ * XML/WBXML scanner
+ *
+ * @target_system all
+ * @target_os all
+ * @description Common header file for the WBXML and the XML scanner.
+ */
+
+/*
+ * Copyright Notice
+ * Copyright (c) Ericsson, IBM, Lotus, Matsushita Communication
+ * Industrial Co., Ltd., Motorola, Nokia, Openwave Systems, Inc.,
+ * Palm, Inc., Psion, Starfish Software, Symbian, Ltd. (2001).
+ * All Rights Reserved.
+ * Implementation of all or part of any Specification may require
+ * licenses under third party intellectual property rights,
+ * including without limitation, patent rights (such a third party
+ * may or may not be a Supporter). The Sponsors of the Specification
+ * are not responsible and shall not be held responsible in any
+ * manner for identifying or failing to identify any or all such
+ * third party intellectual property rights.
+ *
+ * THIS DOCUMENT AND THE INFORMATION CONTAINED HEREIN ARE PROVIDED
+ * ON AN "AS IS" BASIS WITHOUT WARRANTY OF ANY KIND AND ERICSSON, IBM,
+ * LOTUS, MATSUSHITA COMMUNICATION INDUSTRIAL CO. LTD, MOTOROLA,
+ * NOKIA, PALM INC., PSION, STARFISH SOFTWARE AND ALL OTHER SYNCML
+ * SPONSORS DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+ * BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
+ * HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
+ * SHALL ERICSSON, IBM, LOTUS, MATSUSHITA COMMUNICATION INDUSTRIAL CO.,
+ * LTD, MOTOROLA, NOKIA, PALM INC., PSION, STARFISH SOFTWARE OR ANY
+ * OTHER SYNCML SPONSOR BE LIABLE TO ANY PARTY FOR ANY LOSS OF
+ * PROFITS, LOSS OF BUSINESS, LOSS OF USE OF DATA, INTERRUPTION OF
+ * BUSINESS, OR FOR DIRECT, INDIRECT, SPECIAL OR EXEMPLARY, INCIDENTAL,
+ * PUNITIVE OR CONSEQUENTIAL DAMAGES OF ANY KIND IN CONNECTION WITH
+ * THIS DOCUMENT OR THE INFORMATION CONTAINED HEREIN, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH LOSS OR DAMAGE.
+ *
+ * The above notice and this paragraph must be included on all copies
+ * of this document that are made.
+ *
+ */
+
+/*************************************************************************/
+/* Definitions */
+/*************************************************************************/
+#ifndef _XLT_DEC_COM_H
+#define _XLT_DEC_COM_H
+
+#include <smldef.h>
+#include <smldtd.h>
+#include "xlttags.h"
+
+#define DEC_ID XLT_DEC_ID + 0
+#define WBXML_ID XLT_DEC_ID + 200
+#define XML_ID XLT_DEC_ID + 400
+#define STACK_ID XLT_DEC_ID + 600
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/* Token types returned by the scanner */
+typedef enum {
+ TOK_UNDEF = 0,
+ TOK_TAG_START,
+ TOK_TAG_END,
+ TOK_TAG_EMPTY,
+ TOK_CONT
+} XltTokType_t;
+
+/**
+ * Struct containing a token returned by the scanner
+ */
+typedef struct
+{
+ XltTokType_t type; /**< TOK_TAG_START, etc. */
+ XltTagID_t tagid; /**< TN_SYNCHDR, etc. - valid for token of type
+ * TOK_TAG_xxx */
+ SmlPcdataExtension_t ext; /**< type of extension this tag belongs to -
+ * valid for token of type TOK_TAG_xxx */
+ SmlPcdataPtr_t pcdata; /**< valid for token of type TOK_CONT */
+ MemPtr_t start; /**< pointer to the start of this token within
+ * the document - needed for pushback */
+} XltDecToken_t, *XltDecTokenPtr_t;
+
+/** @copydoc XltDecScanner_s */
+typedef struct XltDecScanner_s XltDecScanner_t, *XltDecScannerPtr_t;
+/**
+ * Public interface for the XML/WBXML scanner components.
+ *
+ * The struct used for holding the XML and WBXML scanner state information
+ * are the same - function pointers are used to map the "objects" public
+ * methods to the right functions for scanning XML and WBXML documents.
+ * This object-oriented interface enables the parser component to use the
+ * two scanners interchangeably.
+ * The interface contains serveral public methods and attributes:
+ */
+struct XltDecScanner_s
+{
+ /* public methods */
+ /**
+ * Decodes the next valid token at the current position within the
+ * document. Information about this token is placed into the curtok
+ * attribute of the scanner object.
+ *
+ * @post curtok contains the last valid token.
+ * @param pScanner (IN/OUT)
+ * the scanner
+ * @return SML_ERR_OK or an appropriate error code
+ */
+ Ret_t (*nextTok)(XltDecScannerPtr_t pScanner);
+
+ /**
+ * Free the memory allocated by the scanner.
+ *
+ * @param pScanner (IN/OUT)
+ * the scanner
+ * @return SML_ERR_OK or an appropriate error code
+ */
+ Ret_t (*destroy)(XltDecScannerPtr_t pScanner);
+
+ /**
+ * Resets the scanner to the beginning position within the document of the
+ * last valid token stored in curtok. Only the last found token can be
+ * pushed back. It is not possible to go back more than one token.
+ *
+ * @pre curtok contains a valid token.
+ * @post the next call of nextTok will find the token that was pushed back.
+ * @param pScanner (IN/OUT)
+ * the scanner
+ * @return SML_ERR_OK or an appropriate error code
+ */
+ Ret_t (*pushTok)(XltDecScannerPtr_t pScanner);
+
+ /**
+ * Set the buffer the scanner works on.
+ */
+ void (*setBuf)(XltDecScannerPtr_t pScanner, const MemPtr_t pBufStart, const MemPtr_t pBufEnd);
+
+ /**
+ * Get the current position of the scanner within the working buffer.
+ */
+ MemPtr_t (*getPos)(XltDecScannerPtr_t pScanner);
+
+ /* public attributes */
+
+ /** Contains the last valid token found by a call to nextTok. */
+ XltDecTokenPtr_t curtok;
+
+ /**
+ * The charset information as specified in the XML/WBXML document.
+ * This is the IANA assigned MIBEnum value.
+ */
+ Long_t charset;
+
+ /**
+ * String representation of the charset. This attribute is valid only when
+ * charset equals zero. Otherwise charsetStr will be NULL.
+ */
+ String_t charsetStr;
+
+ /**
+ * The document public identifier as specified in the XML/WBXML document.
+ * This is the numeric identifier assigned by the WAP Forum. If this value
+ * is zero, the public ID is instead specified as a string contained in the
+ * pubIDStr public attribute.
+ */
+ Long_t pubID;
+
+ /**
+ * The string representation of the document public identifier as specified
+ * in the XML/WBXML document (e.g. "-//WAPFORUM//DTD WML 1.0//EN"). This
+ * attribute is valid only when pubID equals zero. Otherwise pubIDStr will
+ * be NULL.
+ */
+ String_t pubIDStr;
+
+ /**
+ * This flag is set by the nextTok method when the scanner reaches the end
+ * of the buffer.
+ */
+ Flag_t finished;
+};
+
+/**
+ * Initialize a new WBXML scanner.
+ *
+ * @pre ppScanner is NULL
+ * @post ppScanner points to an initialized scanner status object
+ * @param pBufEnd (IN)
+ * buffer containing the WBXML/XML document
+ * @param ppBufPos (IN/OUT)
+ * pointer to the current position within the buffer
+ * @param ppScanner (OUT)
+ * a new WBXML/XML scanner status object
+ * @return SML_ERR_OK or an appropriate error code
+ */
+Ret_t xltDecWbxmlInit(const MemPtr_t pBufEnd, MemPtr_t *ppBufPos, XltDecScannerPtr_t *ppScanner);
+/**
+ * Initialize a new XML scanner.
+ *
+ * @copydoc xltDecWbxmlInit
+ */
+Ret_t xltDecXmlInit(const MemPtr_t pBufEnd, MemPtr_t *ppBufPos, XltDecScannerPtr_t *ppScanner);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/syncml_tk/src/sml/xlt/all/xltdecwbxml.c b/src/syncml_tk/src/sml/xlt/all/xltdecwbxml.c
new file mode 100755
index 0000000..6f2ca45
--- /dev/null
+++ b/src/syncml_tk/src/sml/xlt/all/xltdecwbxml.c
@@ -0,0 +1,1053 @@
+/**
+ * @file
+ * WBXML decoder
+ *
+ * @target_system all
+ * @target_os all
+ * @description The WBXML scanner/tokenizer. Used by the SyncML parser.
+ */
+
+
+/*
+ * Copyright Notice
+ * Copyright (c) Ericsson, IBM, Lotus, Matsushita Communication
+ * Industrial Co., Ltd., Motorola, Nokia, Openwave Systems, Inc.,
+ * Palm, Inc., Psion, Starfish Software, Symbian, Ltd. (2001).
+ * All Rights Reserved.
+ * Implementation of all or part of any Specification may require
+ * licenses under third party intellectual property rights,
+ * including without limitation, patent rights (such a third party
+ * may or may not be a Supporter). The Sponsors of the Specification
+ * are not responsible and shall not be held responsible in any
+ * manner for identifying or failing to identify any or all such
+ * third party intellectual property rights.
+ *
+ * THIS DOCUMENT AND THE INFORMATION CONTAINED HEREIN ARE PROVIDED
+ * ON AN "AS IS" BASIS WITHOUT WARRANTY OF ANY KIND AND ERICSSON, IBM,
+ * LOTUS, MATSUSHITA COMMUNICATION INDUSTRIAL CO. LTD, MOTOROLA,
+ * NOKIA, PALM INC., PSION, STARFISH SOFTWARE AND ALL OTHER SYNCML
+ * SPONSORS DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+ * BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
+ * HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
+ * SHALL ERICSSON, IBM, LOTUS, MATSUSHITA COMMUNICATION INDUSTRIAL CO.,
+ * LTD, MOTOROLA, NOKIA, PALM INC., PSION, STARFISH SOFTWARE OR ANY
+ * OTHER SYNCML SPONSOR BE LIABLE TO ANY PARTY FOR ANY LOSS OF
+ * PROFITS, LOSS OF BUSINESS, LOSS OF USE OF DATA, INTERRUPTION OF
+ * BUSINESS, OR FOR DIRECT, INDIRECT, SPECIAL OR EXEMPLARY, INCIDENTAL,
+ * PUNITIVE OR CONSEQUENTIAL DAMAGES OF ANY KIND IN CONNECTION WITH
+ * THIS DOCUMENT OR THE INFORMATION CONTAINED HEREIN, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH LOSS OR DAMAGE.
+ *
+ * The above notice and this paragraph must be included on all copies
+ * of this document that are made.
+ *
+ */
+
+#include "syncml_tk_prefix_file.h" // %%% luz: needed for precompiled headers in eVC++
+
+#include "define.h"
+#ifdef __SML_WBXML__
+/*************************************************************************/
+/* Definitions */
+/*************************************************************************/
+#include "xltdevinf.h"
+#include "xltdeccom.h"
+#include "xlttags.h"
+#include "xltutilstack.h"
+#include "xltdec.h"
+
+#include "smldtd.h"
+#include "smldevinfdtd.h"
+#include "smlmetinfdtd.h"
+#include "mgrutil.h"
+
+#include <libmem.h>
+#include <libstr.h>
+
+#include <smlerr.h>
+
+#ifdef IS_END /* to avoid redefinition of this macro */
+#undef IS_END
+#endif
+
+void
+subdtdDecodeWbxml(XltDecoderPtr_t pDecoder,SmlPcdataPtr_t *ppPcdata);
+
+
+/* WBXML version that this parser knows */
+#define _MAJOR_VERSION 1
+#define _MINOR_VERSION 2
+
+#define TAG_STATE 0
+#define ATTRIBUTE_STATE 1
+
+/* various checks about wbxml token */
+#define HAS_ATTRIBUTES(tag) (*tag & 0x80)
+#define HAS_CONTENT(tag) (*tag & 0x40)
+#define IDENTITY(tag) (*tag & 0x3F)
+
+#define IS_SWITCH(tok) (*(tok) == 0x00)
+#define IS_END(tok) (*(tok) == 0x01)
+#define IS_ENTITY(tok) (*(tok) == 0x02)
+#define IS_STR_I(tok) (*(tok) == 0x03)
+#define IS_LITERAL(tok) (IDENTITY(tok) == 0x04)
+// Note: gcc cannot parse multi-line macros when file has DOS line ends
+#define IS_EXT_I(tok) ((*(tok) == 0x40) || (*(tok) == 0x41) || (*(tok) == 0x42))
+#define IS_PI(tok) (*(tok) == 0x43)
+#define IS_EXT_T(tok) ((*(tok) == 0x80) || (*(tok) == 0x81) || (*(tok) == 0x82))
+#define IS_STR_T(tok) (*(tok) == 0x83)
+#define IS_EXT(tok) ((*(tok) == 0xC0) || (*(tok) == 0xC1) || (*(tok) == 0xC2))
+#define IS_OPAQUE(tok) (*(tok) == 0xC3)
+#define IS_STRING(tok) (IS_STR_I(tok) || IS_STR_T(tok))
+#define IS_EXTENSION(tok) (IS_EXT_I(tok) || IS_EXT_T(tok) || IS_EXT(tok))
+
+#define IS_ATTRIBUTE_VALUE(tok) (*(tok) & 0x80)
+#define IS_ATTRIBUTE_START(tok) (~IS_ATTRIBUTE_VALUE(tok))
+
+
+/** @copydoc wbxmlScannerPriv_s */
+typedef struct wbxmlScannerPriv_s wbxmlScannerPriv_t, *wbxmlScannerPrivPtr_t;
+/**
+ * Private Interface for the WBXML scanner.
+ *
+ * The private scanner interface contains some additional member attributes
+ * that are not listed in the public interface, e.g. a copy of the string
+ * table and some other items that do not need to be known outside the
+ * scanner module.
+ */
+struct wbxmlScannerPriv_s
+{
+ /* public methods */
+ Ret_t (*nextTok)(XltDecScannerPtr_t);
+ Ret_t (*destroy)(XltDecScannerPtr_t);
+ Ret_t (*pushTok)(XltDecScannerPtr_t);
+ void (*setBuf)(XltDecScannerPtr_t pScanner, const MemPtr_t pBufStart, const MemPtr_t pBufEnd);
+ MemPtr_t (*getPos)(XltDecScannerPtr_t pScanner);
+
+ /* public attributes */
+ XltDecTokenPtr_t curtok; /**< current token */
+ Long_t charset; /**< character set as specified in the
+ WBXML header */
+ String_t charsetStr; /**< NULL */
+ Long_t pubID; /**< document public identifier as
+ specified in the WBXML header */
+ String_t pubIDStr; /**< pubID as a string - valid only when
+ pubID == 0 */
+ Flag_t finished; /**< set when end of buffer is reached */
+
+ /* private attributes */
+ MemPtr_t pos; /**< current buffer position */
+ MemPtr_t bufend; /**< end of buffer */
+ Long_t pubIDIdx; /**< strtbl index of the string
+ version of the pubID - valid only
+ when pubID == 0 */
+
+ XltUtilStackPtr_t tagstack; /**< stack of open start tags */
+
+ MemPtr_t strtbl; /**< copy of the string table */
+ Long_t strtbllen; /**< length of the string table */
+
+ Byte_t state; /**< tag state or attribute state */
+ SmlPcdataExtension_t cptag; /**< current codepage for tags */
+ Byte_t cpattr; /**< current codepage for attributes */
+ SmlPcdataExtension_t activeExt; /**< the active Sub DTD */
+};
+
+/* typedef for multi-byte unsigned integers as specified in the
+ WAP Binary XML Content Format specification */
+typedef Long_t MBINT;
+
+/*
+ * Public methods of the scanner interface.
+ *
+ * Description see XLTDecCom.h.
+ */
+static Ret_t _destroy(XltDecScannerPtr_t);
+static Ret_t _nextTok(XltDecScannerPtr_t);
+static Ret_t _pushTok(XltDecScannerPtr_t);
+static void _setBuf(XltDecScannerPtr_t, const MemPtr_t, const MemPtr_t);
+static MemPtr_t _getPos(XltDecScannerPtr_t);
+
+/**
+ * Advance the current position pointer after checking whether the end of
+ * the buffer has been reached. If the end of the buffer has been reached
+ * the scanner's finished flag is set.
+ *
+ * @return 0, if end of buffer has been reached
+ * 1 otherwise
+ */
+static Boolean_t readBytes(wbxmlScannerPrivPtr_t pScanner, Long_t bytes);
+
+/**
+ * Decodes multi-byte integers.
+ *
+ * @pre pScanner->pos points to the first byte of the mb_int.
+ * @post pScanner->pos points to the last byte of the mb_int.
+ */
+static Ret_t parseInt(wbxmlScannerPrivPtr_t pScanner, MBINT *mbi);
+
+/**
+ * wbxmlHeader, wbxmlVersion, wbxmlPublicID, wbxmlCharset
+ *
+ * These functions are used for decoding the WBXML document header.
+ * wbxmlHeader is a short wrapper that calls the other four functions in
+ * the right order to scan the header. wbxmlStrtbl makes a copy of the
+ * string table.
+ */
+static Ret_t wbxmlHeader(wbxmlScannerPrivPtr_t pScanner);
+static Ret_t wbxmlVersion(wbxmlScannerPrivPtr_t pScanner);
+static Ret_t wbxmlPublicID(wbxmlScannerPrivPtr_t pScanner);
+static Ret_t wbxmlCharset(wbxmlScannerPrivPtr_t pScanner);
+static Ret_t wbxmlStrtbl(wbxmlScannerPrivPtr_t pScanner);
+
+/**
+ * Switch WBXML code page
+ */
+static Ret_t wbxmlSwitchPage(wbxmlScannerPrivPtr_t pScanner);
+
+/**
+ * wbxmlXXXToken
+ *
+ * Scan the document for the next valid XML/WBXML token as defined in the
+ * XLTDecCom header file (e.g. TOK_TAG_START).
+ *
+ * @pre pScanner->pos points to the first byte of a valid WBXML
+ * element (String, Tag, etc.)
+ * @post pScanner->pos points to the last byte of the WBXML element;
+ * pScanner->curtok contains type and tagid or pcdata of the token
+ */
+static Ret_t wbxmlStringToken(wbxmlScannerPrivPtr_t pScanner);
+static Ret_t wbxmlOpaqueToken(wbxmlScannerPrivPtr_t pScanner);
+static Ret_t wbxmlTagToken(wbxmlScannerPrivPtr_t pScanner);
+
+/**
+ * wbxmlSkipXXX
+ *
+ * WBXML extensions, entities, processing instructions and attributes are
+ * not supported by this scanner. If one is found it is skipped and
+ * processing continues afterwards.
+ */
+static Ret_t wbxmlSkipExtension(wbxmlScannerPrivPtr_t pScanner);
+static Ret_t wbxmlSkipEntity(wbxmlScannerPrivPtr_t pScanner);
+static Ret_t wbxmlSkipPI(wbxmlScannerPrivPtr_t);
+static Ret_t wbxmlSkipAttribute(wbxmlScannerPrivPtr_t);
+
+/*************************************************************************/
+/* External Functions */
+/*************************************************************************/
+
+/*
+ * Create and initialize a new WBXML scanner. Description see XLTDec.h.
+ */
+Ret_t
+xltDecWbxmlInit(const MemPtr_t pBufEnd, MemPtr_t *ppBufPos,
+ XltDecScannerPtr_t *ppScanner)
+{
+ wbxmlScannerPrivPtr_t pScanner;
+ Ret_t rc;
+
+ /* initialize new WBXML scanner */
+ if ((pScanner = (wbxmlScannerPrivPtr_t)smlLibMalloc(sizeof(wbxmlScannerPriv_t))) == NULL)
+ return SML_ERR_NOT_ENOUGH_SPACE;
+ smlLibMemset(pScanner, 0, sizeof(wbxmlScannerPriv_t));
+ pScanner->bufend = pBufEnd;
+ pScanner->pos = *ppBufPos;
+ if ((pScanner->curtok = (XltDecTokenPtr_t)smlLibMalloc(sizeof(XltDecToken_t))) == NULL) {
+ smlLibFree(pScanner);
+ *ppScanner = NULL;
+ return SML_ERR_NOT_ENOUGH_SPACE;
+ }
+ smlLibMemset(pScanner->curtok, 0, sizeof(XltDecToken_t));
+ if ((rc = xltUtilCreateStack(&pScanner->tagstack, 10)) != SML_ERR_OK) {
+ smlLibFree(pScanner->curtok);
+ smlLibFree(pScanner);
+ *ppScanner = NULL;
+ return rc;
+ }
+ pScanner->state = TAG_STATE;
+
+ /* point public/private methods to the right implementation */
+ pScanner->nextTok = _nextTok;
+ pScanner->destroy = _destroy;
+ pScanner->pushTok = _pushTok;
+ pScanner->setBuf = _setBuf;
+ pScanner->getPos = _getPos;
+
+ /* decode WBXML header */
+ if ((rc = wbxmlHeader(pScanner)) != SML_ERR_OK) {
+ pScanner->destroy((XltDecScannerPtr_t)pScanner);
+ *ppScanner = NULL;
+ return rc;
+ }
+
+ *ppScanner = (XltDecScannerPtr_t)pScanner;
+
+ return SML_ERR_OK;
+}
+
+/**
+ * Free memory. Description see XltDecAll.h.
+ */
+static Ret_t
+_destroy(XltDecScannerPtr_t pScanner)
+{
+ wbxmlScannerPrivPtr_t pScannerPriv;
+
+ if (pScanner == NULL)
+ return SML_ERR_OK;
+
+ pScannerPriv = (wbxmlScannerPrivPtr_t)pScanner;
+ if (pScannerPriv->tagstack != NULL)
+ pScannerPriv->tagstack->destroy(pScannerPriv->tagstack);
+ smlLibFree(pScannerPriv->curtok);
+ smlLibFree(pScannerPriv->strtbl);
+ smlLibFree(pScannerPriv);
+
+ return SML_ERR_OK;
+}
+
+/**
+ * Get next token.
+ */
+static Ret_t
+_nextTok(XltDecScannerPtr_t pScanner)
+{
+ wbxmlScannerPrivPtr_t pScannerPriv;
+ Ret_t rc;
+
+ pScannerPriv = (wbxmlScannerPrivPtr_t)pScanner;
+ // T.K.: chanched Ptr_t to _t
+ smlLibMemset(pScanner->curtok, 0, sizeof(XltDecToken_t));
+ pScannerPriv->curtok->start = pScannerPriv->pos;
+
+ /* keep going until we find a "supported" element */
+ rc = SML_ERR_OK;
+ while (rc == SML_ERR_OK) {
+ /* skip PIs, extensions and entities... */
+ if (IS_PI(pScannerPriv->pos)) {
+ rc = wbxmlSkipPI(pScannerPriv);
+ } else if (IS_EXTENSION(pScannerPriv->pos)) {
+ rc = wbxmlSkipExtension(pScannerPriv);
+ } else if (IS_ENTITY(pScannerPriv->pos)) {
+ rc = wbxmlSkipEntity(pScannerPriv);
+
+ /* ... decode strings, opaque data and tags */
+ } else if (IS_STRING(pScannerPriv->pos)) {
+ rc = wbxmlStringToken(pScannerPriv);
+ break;
+ } else if (IS_OPAQUE(pScannerPriv->pos)) {
+ rc = wbxmlOpaqueToken(pScannerPriv);
+ break;
+ } else {
+ rc = wbxmlTagToken(pScannerPriv);
+ break;
+ }
+ }
+
+ return rc;
+}
+
+/**
+ * Reset the scanner to the starting position of the current token within
+ * the buffer.
+ */
+static Ret_t
+_pushTok(XltDecScannerPtr_t pScanner)
+{
+ wbxmlScannerPrivPtr_t pScannerPriv;
+ XltUtilStackPtr_t pTagStack;
+ XltTagID_t tagid;
+ Ret_t rc = 0;
+
+ pScannerPriv = (wbxmlScannerPrivPtr_t)pScanner;
+ pTagStack = pScannerPriv->tagstack;
+
+ if (pScannerPriv->curtok->start == NULL)
+ return SML_ERR_WRONG_USAGE;
+
+ /* reset scanner to position where tok begins */
+ pScannerPriv->pos = pScannerPriv->curtok->start;
+
+ /* correct the tag stack */
+ if (pScannerPriv->curtok->type == TOK_TAG_START) {
+ rc = pTagStack->pop(pTagStack, &tagid);
+ } else if (pScannerPriv->curtok->type == TOK_TAG_END) {
+ tagid = pScannerPriv->curtok->tagid;
+ rc = pTagStack->push(pTagStack, tagid);
+ }
+ if (rc) return rc;
+
+ /* invalidate curtok */
+ /* T.K. Possible Error. pScannerPriv->curtok is of type XltDecToken_t NOT ...Ptr_t */
+ // OrigLine:
+ // smlLibMemset(pScannerPriv->curtok, 0, sizeof(XltDecTokenPtr_t));
+ pScannerPriv->curtok->type = (XltTokType_t)0;
+
+ return SML_ERR_OK;
+}
+
+static void
+_setBuf(XltDecScannerPtr_t pScanner, const MemPtr_t pBufStart,
+ const MemPtr_t pBufEnd)
+{
+ wbxmlScannerPrivPtr_t pScannerPriv = (wbxmlScannerPrivPtr_t)pScanner;
+ pScannerPriv->pos = pBufStart;
+ pScannerPriv->bufend = pBufEnd;
+}
+
+static MemPtr_t
+_getPos(XltDecScannerPtr_t pScanner)
+{
+ return ((wbxmlScannerPrivPtr_t)pScanner)->pos;
+}
+
+/*************************************************************************/
+/* Internal Functions */
+/*************************************************************************/
+
+/*
+ * Advance the position pointer. Description see above.
+ */
+static Boolean_t
+readBytes(wbxmlScannerPrivPtr_t pScanner, Long_t bytes)
+{
+ if (pScanner->pos + bytes > pScanner->bufend) {
+ pScanner->finished = 1;
+ return 0;
+ }
+ pScanner->pos += bytes;
+ return 1;
+}
+
+/*
+ * NOTICE: Entities, Extensions, Processing Instructions and Attributes
+ * are not supported by the WBXML scanner.
+ *
+ * Extensions and Attributes are document-specific and are as such not used
+ * by the SyncML specification.
+ * The scanner will just ignore and skip over them. Neither
+ * this scanner nor the parser use processing instructions so they are
+ * skipped as well.
+ */
+
+/*
+ * Decode the WBXML header containing version number, document public
+ * identifier, character set and a string table.
+ */
+static Ret_t
+wbxmlHeader(wbxmlScannerPrivPtr_t pScanner)
+{
+ Ret_t rc;
+
+ /* decode the WBXML header */
+ if ((rc = wbxmlVersion(pScanner)) != SML_ERR_OK)
+ return rc;
+ if ((rc = wbxmlPublicID(pScanner)) != SML_ERR_OK)
+ return rc;
+ if ((rc = wbxmlCharset(pScanner)) != SML_ERR_OK)
+ return rc;
+ if ((rc = wbxmlStrtbl(pScanner)) != SML_ERR_OK)
+ return rc;
+ return SML_ERR_OK;
+}
+
+/**
+ * Decode WBXML version. The scanner returns an error if the major version
+ * of the document differs from the major version this scanner supports or
+ * if the minor version of the document is larger than the minor version
+ * the scanner supports.
+ */
+static Ret_t
+wbxmlVersion(wbxmlScannerPrivPtr_t pScanner)
+{
+ Byte_t major, minor;
+
+ minor = ((Byte_t)(*pScanner->pos & 0x0F));
+ major = ((Byte_t)((*pScanner->pos >> 4) + 1));
+
+
+
+ if (major != _MAJOR_VERSION || minor > _MINOR_VERSION)
+ return SML_DECODEERROR(SML_ERR_XLT_INCOMP_WBXML_VERS,pScanner,"wbxmlVersion");
+
+ if (!readBytes(pScanner, 1))
+ return SML_DECODEERROR(SML_ERR_XLT_END_OF_BUFFER,pScanner,"wbxmlVersion");
+
+ return SML_ERR_OK;
+}
+
+/**
+ * Decodes WBXML Document Public Identifier.
+ */
+static Ret_t
+wbxmlPublicID(wbxmlScannerPrivPtr_t pScanner)
+{
+ MBINT tmp;
+ Ret_t rc;
+
+ if (*pScanner->pos != 0) {
+ /* pre-defined numeric identifier */
+ if ((rc = parseInt(pScanner, &tmp)) != SML_ERR_OK)
+ return rc;
+ pScanner->pubID = tmp;
+ pScanner->pubIDIdx = 0;
+ } else {
+ /* public id is given as string table entry (which we
+ haven't read at this point so we'll save the reference
+ for later) */
+ if (!readBytes(pScanner, 1))
+ return SML_DECODEERROR(SML_ERR_XLT_END_OF_BUFFER,pScanner,"wbxmlPublicID");
+ if ((rc = parseInt(pScanner, &tmp)) != SML_ERR_OK)
+ return rc;
+ pScanner->pubID = 0;
+ pScanner->pubIDIdx = tmp;
+ }
+ if (!readBytes(pScanner, 1))
+ return SML_DECODEERROR(SML_ERR_XLT_END_OF_BUFFER,pScanner,"wbxmlPublicID");
+ return SML_ERR_OK;
+}
+
+/**
+ * Decode WBXML Charset.
+ */
+static Ret_t
+wbxmlCharset(wbxmlScannerPrivPtr_t pScanner)
+{
+ /* TODO: if charset iformation has to be processed
+ it can be done here. For the moment only UTF-8 is used by SyncML */
+ MBINT mibenum;
+ Ret_t rc;
+
+ /* charset is given as a single IANA assigned MIBEnum value */
+ if ((rc = parseInt(pScanner, &mibenum)) != SML_ERR_OK)
+ return rc;
+ pScanner->charset = mibenum;
+
+ if (!readBytes(pScanner, 1))
+ return SML_DECODEERROR(SML_ERR_XLT_END_OF_BUFFER,pScanner,"wbxmlCharset");
+
+ return SML_ERR_OK;
+}
+
+/**
+ * Keep a copy of the string table.
+ */
+static Ret_t
+wbxmlStrtbl(wbxmlScannerPrivPtr_t pScanner)
+{
+ MBINT len;
+ Ret_t rc;
+
+ if ((rc = parseInt(pScanner, &len)) != SML_ERR_OK)
+ return rc;
+ if (!readBytes(pScanner, 1))
+ return SML_DECODEERROR(SML_ERR_XLT_END_OF_BUFFER,pScanner,"wbxmlStrtbl");
+ pScanner->strtbllen = len;
+ if (len > 0) {
+ if (pScanner->pos + len > pScanner->bufend)
+ return SML_DECODEERROR(SML_ERR_XLT_END_OF_BUFFER,pScanner,"wbxmlStrtbl");
+ if ((pScanner->strtbl = smlLibMalloc(len)) == NULL)
+ {
+ return SML_ERR_NOT_ENOUGH_SPACE;
+ }
+ smlLibMemcpy(pScanner->strtbl, pScanner->pos, len);
+ readBytes(pScanner, len);
+ } else {
+ pScanner->strtbl = NULL;
+ }
+
+ /* if the public ID was given as a string table reference save a
+ reference to the corresponding string for later */
+ if (pScanner->pubID == 0) {
+ if (pScanner->pubIDIdx > pScanner->strtbllen)
+ return SML_DECODEERROR(SML_ERR_XLT_INVAL_WBXML_DOC,pScanner,"wbxmlStrtbl");
+ pScanner->pubIDStr = (String_t)(pScanner->strtbl + pScanner->pubIDIdx);
+ }
+
+ return SML_ERR_OK;
+}
+
+static Ret_t
+parseInt(wbxmlScannerPrivPtr_t pScanner, MBINT *mbi)
+{
+ *mbi = 0;
+ /* accumulate byte value until continuation flag (MSB) is zero */
+ for (;;) {
+ *mbi = *mbi << 7;
+ *mbi += *(pScanner->pos) & 0x7F;
+ if (!(*pScanner->pos & 0x80)) break;
+ if (!readBytes(pScanner, 1))
+ return SML_DECODEERROR(SML_ERR_XLT_END_OF_BUFFER,pScanner,"parseInt");
+ }
+ return SML_ERR_OK;
+}
+
+static Ret_t
+wbxmlStringToken(wbxmlScannerPrivPtr_t pScanner)
+{
+ SmlPcdataPtr_t pPcdata;
+ Ret_t rc;
+
+ if ((pPcdata = (SmlPcdataPtr_t)smlLibMalloc(sizeof(SmlPcdata_t))) == NULL)
+ return SML_ERR_NOT_ENOUGH_SPACE;
+ /* copy the string into the new PCdata struct */
+ if (IS_STR_I(pScanner->pos)) {
+ /* inline string */
+ if (!readBytes(pScanner, 1))
+ return SML_DECODEERROR(SML_ERR_XLT_END_OF_BUFFER,pScanner,"wbxmlStringToken");
+ pPcdata->extension = SML_EXT_UNDEFINED;
+ pPcdata->contentType = SML_PCDATA_STRING;
+ pPcdata->length = smlLibStrlen((String_t)pScanner->pos);
+ if (pScanner->pos + pPcdata->length + 1 > pScanner->bufend) {
+ smlLibFree(pPcdata);
+ return SML_DECODEERROR(SML_ERR_XLT_END_OF_BUFFER,pScanner,"wbxmlStringToken");
+ }
+ if ((pPcdata->content = smlLibMalloc(pPcdata->length + 1)) == NULL) {
+ smlLibFree(pPcdata);
+ return SML_ERR_NOT_ENOUGH_SPACE;
+ }
+ smlLibStrncpy(pPcdata->content, (String_t)pScanner->pos, pPcdata->length + 1);
+ readBytes(pScanner, pPcdata->length + 1);
+
+ } else {
+ /* string table reference */
+ MBINT offset; /* offset into string table */
+ if (!readBytes(pScanner, 1))
+ return SML_DECODEERROR(SML_ERR_XLT_END_OF_BUFFER,pScanner,"wbxmlStringToken");
+ if ((rc = parseInt(pScanner, &offset)) != SML_ERR_OK) {
+ smlLibFree(pPcdata);
+ return rc;
+ }
+ if (offset >= pScanner->strtbllen) {
+ smlLibFree(pPcdata);
+ return SML_DECODEERROR(SML_ERR_XLT_INVAL_WBXML_DOC,pScanner,"wbxmlStringToken");
+ }
+ pPcdata->contentType = SML_PCDATA_STRING;
+ pPcdata->length = smlLibStrlen((String_t)(pScanner->strtbl + offset));
+ if ((pPcdata->content = smlLibMalloc(pPcdata->length + 1)) == NULL) {
+ smlLibFree(pPcdata);
+ return SML_ERR_NOT_ENOUGH_SPACE;
+ }
+ smlLibStrncpy(pPcdata->content, (String_t)(pScanner->strtbl + offset), pPcdata->length + 1);
+ readBytes(pScanner, 1);
+ }
+
+ pScanner->curtok->pcdata = pPcdata;
+
+ pScanner->curtok->type = TOK_CONT;
+
+ return SML_ERR_OK;
+}
+
+static Ret_t
+wbxmlOpaqueToken(wbxmlScannerPrivPtr_t pScanner)
+{
+ SmlPcdataPtr_t pPcdata = NULL;
+ MBINT len;
+ Ret_t rc;
+
+ if (!readBytes(pScanner, 1))
+ return SML_DECODEERROR(SML_ERR_XLT_END_OF_BUFFER,pScanner,"wbxmlOpaqueToken");
+
+ /* a mbi indicates the length of the opaque data block that we'll
+ copy into new PCdata struct */
+ if ((rc = parseInt(pScanner, &len)) != SML_ERR_OK)
+ return rc;
+ if (!readBytes(pScanner, 1))
+ return SML_DECODEERROR(SML_ERR_XLT_END_OF_BUFFER,pScanner,"wbxmlOpaqueToken");
+ if (pScanner->pos + len > pScanner->bufend)
+ return SML_DECODEERROR(SML_ERR_XLT_END_OF_BUFFER,pScanner,"wbxmlOpaqueToken");
+ if ((pPcdata = (SmlPcdataPtr_t)smlLibMalloc(sizeof(SmlPcdata_t))) == NULL)
+ return SML_ERR_NOT_ENOUGH_SPACE;
+ pPcdata->extension = SML_EXT_UNDEFINED;
+ pPcdata->contentType = SML_PCDATA_OPAQUE;
+ pPcdata->length = len;
+ /* Modification 2001-07-03 by Luz %%%%%:
+ * made sure that content is one null byte longer
+ * than indicated opaque content, such that strings that are coded as
+ * opaque (happens to be the case with Nokia 9210) can still be read
+ * as C-string without need for an intermediate buffer
+ */
+ /* original:
+ if ((pPcdata->content = smlLibMalloc(len)) == NULL) {
+ smlLibFree(pPcdata);
+ return SML_ERR_NOT_ENOUGH_SPACE;
+ }
+ */
+ /* modified: */
+ if ((pPcdata->content = smlLibMalloc(len+1)) == NULL) {
+ smlLibFree(pPcdata);
+ return SML_ERR_NOT_ENOUGH_SPACE;
+ }
+ ((char *)pPcdata->content)[len]=0; /* make sure there is a c-string terminator */
+ /* end modification */
+
+ smlLibMemcpy(pPcdata->content, pScanner->pos, len);
+ pScanner->curtok->pcdata = pPcdata;
+
+ readBytes(pScanner, len);
+
+ pScanner->curtok->type = TOK_CONT;
+
+ return SML_ERR_OK;
+}
+
+static Ret_t
+wbxmlTagToken(wbxmlScannerPrivPtr_t pScanner)
+{
+ XltTagID_t tagid;
+ Boolean_t has_cont, has_attr;
+ Ret_t rc;
+
+ if (IS_SWITCH(pScanner->pos)) {
+ if ((rc = wbxmlSwitchPage(pScanner)) != SML_ERR_OK)
+ return rc;
+ }
+
+ /* we have to look at the top of the tagstack to see which
+ start tag an end tag belongs to */
+ if (IS_END(pScanner->pos)) {
+ if (!readBytes(pScanner, 1))
+ return SML_DECODEERROR(SML_ERR_XLT_END_OF_BUFFER,pScanner,"wbxmlTagToken");
+ pScanner->curtok->type = TOK_TAG_END;
+ rc = pScanner->tagstack->pop(pScanner->tagstack, &tagid);
+ if (rc == SML_ERR_WRONG_USAGE)
+ return SML_DECODEERROR(SML_ERR_XLT_INVAL_WBXML_DOC,pScanner,"wbxmlTagToken");
+ else if (rc)
+ return rc;
+ pScanner->curtok->tagid = tagid;
+ return SML_ERR_OK;
+ }
+
+
+ /* look at the two MSB: does this tag have content or attributes? */
+
+ has_cont = ((Boolean_t)(HAS_CONTENT(pScanner->pos)));
+ has_attr = ((Boolean_t)(HAS_ATTRIBUTES(pScanner->pos)));
+
+
+ /* look up tag ID either by string or by number */
+ if (IS_LITERAL(pScanner->pos)) {
+ MBINT offset; /* offset into the string table */
+ if (!readBytes(pScanner, 1))
+ return SML_DECODEERROR(SML_ERR_XLT_END_OF_BUFFER,pScanner,"wbxmlTagToken");
+ if ((rc = parseInt(pScanner, &offset)) != SML_ERR_OK)
+ return rc;
+ if (offset > pScanner->strtbllen)
+ return SML_DECODEERROR(SML_ERR_XLT_INVAL_WBXML_DOC,pScanner,"wbxmlTagToken");
+
+ rc = (Ret_t)getTagIDByStringAndExt((String_t)(pScanner->strtbl + offset), pScanner->activeExt, &tagid);
+ if ((tagid == TN_UNDEF) || (rc != SML_ERR_OK)) return rc;
+
+ } else {
+ rc = (Ret_t)getTagIDByByteAndExt((Byte_t)IDENTITY(pScanner->pos), pScanner->activeExt, &tagid);
+ if ((tagid == TN_UNDEF) || (rc != SML_ERR_OK)) return rc;
+
+ }
+
+ /* we know everything we need to know */
+ pScanner->curtok->tagid = tagid;
+ pScanner->curtok->type = has_cont ? TOK_TAG_START : TOK_TAG_EMPTY;
+#ifdef __USE_METINF__
+ pScanner->curtok->ext = pScanner->cptag == 0 ? SML_EXT_UNDEFINED : SML_EXT_METINF;
+#else
+ pScanner->curtok->ext = SML_EXT_UNDEFINED;
+#endif
+
+ if (!readBytes(pScanner, 1))
+ return SML_DECODEERROR(SML_ERR_XLT_END_OF_BUFFER,pScanner,"wbxmlTagToken");
+
+ /* push tag onto tagstack unless this tag is empty */
+ if (has_cont) {
+ if ((rc = pScanner->tagstack->push(pScanner->tagstack, tagid)) != SML_ERR_OK)
+ return rc;
+ }
+
+ /* skip attributes */
+ if (has_attr) {
+ pScanner->state = ATTRIBUTE_STATE;
+ if ((rc = wbxmlSkipAttribute(pScanner)) != SML_ERR_OK)
+ return rc;
+ pScanner->state = TAG_STATE;
+ }
+
+ return SML_ERR_OK;
+}
+
+/*
+ * Switch WBXML code page.
+ */
+/* T.K. 06.02.01
+ * We need to enhance this as soon as we introduce
+ * Sub DTD's with more than one WBXML codepage. But till then
+ * there is only one case where WBXML codepages can occure, and
+ * this is the MetInf Sub DTD. So in case we find a codepage switch
+ * to something other than codepage zero, we set the active extension
+ * to metinf.
+ * In future versions the pScanner needs to be enhanced, to translate
+ * codepageswitches context sensitive to the active extension.
+ */
+static Ret_t
+wbxmlSwitchPage(wbxmlScannerPrivPtr_t pScanner)
+{
+ if (!readBytes(pScanner, 1))
+ return SML_DECODEERROR(SML_ERR_XLT_END_OF_BUFFER,pScanner,"wbxmlSwitchPage");
+ if (pScanner->state == TAG_STATE)
+ pScanner->cptag = (SmlPcdataExtension_t)*pScanner->pos;
+ else
+ pScanner->cpattr = *pScanner->pos;
+ readBytes(pScanner, 1);
+ /* T.K. this needs to be adjusted as described above */
+ if (pScanner->cpattr != 0 || pScanner->cptag != 0)
+ pScanner->activeExt = SML_EXT_METINF;
+ else
+ pScanner->activeExt = SML_EXT_UNDEFINED;
+ return SML_ERR_OK;
+}
+
+
+/******************************/
+/* Unsupported WBXML elements */
+/******************************/
+
+/**
+ * Skips entities but doesn't do anything useful yet.
+ */
+static Ret_t
+wbxmlSkipEntity(wbxmlScannerPrivPtr_t pScanner)
+{
+ MBINT tmp;
+ Ret_t rc;
+
+ if (!readBytes(pScanner, 1))
+ return SML_DECODEERROR(SML_ERR_XLT_END_OF_BUFFER,pScanner,"wbxmlSkipEntity");
+ if ((rc = parseInt(pScanner, &tmp)) != SML_ERR_OK)
+ return rc;
+ if (!readBytes(pScanner, 1))
+ return SML_DECODEERROR(SML_ERR_XLT_END_OF_BUFFER,pScanner,"wbxmlSkipEntity");
+
+ return SML_ERR_OK;
+}
+
+
+/*
+ * Decode WBXML extensions. Skips the extension but doesn't do anything
+ * useful with it.
+ */
+static Ret_t
+wbxmlSkipExtension(wbxmlScannerPrivPtr_t pScanner)
+{
+ MBINT tmp;
+ Ret_t rc;
+
+ if (IS_EXT(pScanner->pos)) {
+ /* single byte extension token */
+ if (!readBytes(pScanner, 1))
+ return SML_DECODEERROR(SML_ERR_XLT_END_OF_BUFFER,pScanner,"wbxmlSkipExtension");
+ } else if (IS_EXT_I(pScanner->pos)) {
+ /* inline string extension token */
+ if (!readBytes(pScanner, 1))
+ return SML_DECODEERROR(SML_ERR_XLT_END_OF_BUFFER,pScanner,"wbxmlSkipExtension");
+ if (!readBytes(pScanner, smlLibStrlen((String_t)pScanner->pos) + 1))
+ return SML_DECODEERROR(SML_ERR_XLT_END_OF_BUFFER,pScanner,"wbxmlSkipExtension");
+ } else {
+ /* inline integer extension token */
+ if (!readBytes(pScanner, 1))
+ return SML_DECODEERROR(SML_ERR_XLT_END_OF_BUFFER,pScanner,"wbxmlSkipExtension");
+ if ((rc = parseInt(pScanner, &tmp)) != SML_ERR_OK)
+ return rc;
+ if (!readBytes(pScanner, tmp + 1))
+ return SML_DECODEERROR(SML_ERR_XLT_END_OF_BUFFER,pScanner,"wbxmlSkipExtension");
+ }
+ return SML_ERR_OK;
+}
+
+/*
+ * Handle XML processing instructions. PIs are not supported but the
+ * scanner recognizes and skips over them.
+ */
+static Ret_t
+wbxmlSkipPI(wbxmlScannerPrivPtr_t pScanner)
+{
+ /* PIs are just like tag attributes with a special PI token instead
+ * of the attribute start token */
+ return wbxmlSkipAttribute(pScanner);
+}
+
+/*
+ * Handle attributes. Attributes are not supported but the
+ * scanner recognizes and skips over them.
+ */
+static Ret_t
+wbxmlSkipAttribute(wbxmlScannerPrivPtr_t pScanner)
+{
+ XltDecTokenPtr_t oldtok;
+ MBINT tmp;
+ Ret_t rc = 0;
+
+ /* skipping attributes shouldn't change the current token so we
+ make a copy... */
+ if ((oldtok = (XltDecTokenPtr_t)smlLibMalloc(sizeof(XltDecToken_t))) == NULL)
+ return SML_ERR_NOT_ENOUGH_SPACE;
+ smlLibMemcpy(oldtok, pScanner->curtok, sizeof(XltDecToken_t));
+
+ /* ... skip until attribute end tag... */
+ while (!IS_END(pScanner->pos)) {
+ if (IS_STRING(pScanner->pos)) {
+ rc = wbxmlStringToken(pScanner);
+ /* avoid memory leak due to this ugly workaround of
+ skipping attributes */
+ smlLibFree(pScanner->curtok->pcdata);
+ } else if (IS_EXTENSION(pScanner->pos)) {
+ rc = wbxmlSkipExtension(pScanner);
+ } else if (IS_ENTITY(pScanner->pos)) {
+ rc = wbxmlSkipEntity(pScanner);
+ } else if (IS_OPAQUE(pScanner->pos)) {
+ rc = wbxmlOpaqueToken(pScanner);
+ /* avoid memory leak due to this ugly workaround of
+ skipping attributes */
+ smlLibFree(pScanner->curtok->pcdata);
+ } else if (IS_LITERAL(pScanner->pos)) {
+ if (!readBytes(pScanner, 1))
+ return SML_DECODEERROR(SML_ERR_XLT_END_OF_BUFFER,pScanner,"wbxmlSkipAttribute");
+ rc = parseInt(pScanner, &tmp);
+ if (!readBytes(pScanner, 1))
+ return SML_DECODEERROR(SML_ERR_XLT_END_OF_BUFFER,pScanner,"wbxmlSkipAttribute");
+ } else if (IS_SWITCH(pScanner->pos)) {
+ rc = wbxmlSwitchPage(pScanner);
+ } else {
+ if (!readBytes(pScanner, 1))
+ return SML_DECODEERROR(SML_ERR_XLT_END_OF_BUFFER,pScanner,"wbxmlSkipAttribute");
+ }
+
+ if (rc != SML_ERR_OK) {
+ smlLibFree(oldtok);
+ return rc;
+ }
+
+ }
+ /* ... then skip the end tag itself... */
+ readBytes(pScanner, 1);
+
+ /* ... and finaly restore our copy of curtok */
+ smlLibMemcpy(pScanner->curtok, oldtok, sizeof(XltDecToken_t));
+ smlLibFree(oldtok);
+
+ return SML_ERR_OK;
+}
+
+#ifdef __USE_EXTENSIONS__
+/*
+ * This function tries to decode an inlined WBXML document inside
+ * an PCDATA element.
+ * In case of failing to decode it the PCDATA element isn't changed
+ * at all.
+ */
+
+void
+subdtdDecodeWbxml(XltDecoderPtr_t pDecoder,SmlPcdataPtr_t *ppPcdata) {
+ Ret_t _err = SML_ERR_OK;
+ MemPtr_t pSubBuf = NULL;
+ SmlPcdataPtr_t pSubPcdata = NULL;
+ XltDecoderPtr_t pSubDecoder = NULL;
+#ifdef __USE_DEVINF__
+ wbxmlScannerPrivPtr_t pScannerPriv = NULL;
+#endif
+
+ /* some sanity checks at first */
+
+ if (*ppPcdata == NULL) {
+ if (pDecoder) /* use this rare case to remove warning */
+ {
+ }
+ return;
+ }
+
+ if ((*ppPcdata)->contentType != SML_PCDATA_OPAQUE) return;
+
+ // now create a sub buffer
+ pSubBuf = (MemPtr_t)smlLibMalloc((*ppPcdata)->length);
+ if (pSubBuf == NULL) return;
+ smlLibMemset(pSubBuf, 0x00, (*ppPcdata)->length);
+ smlLibMemmove(pSubBuf, (*ppPcdata)->content, (*ppPcdata)->length);
+
+ /* ok looks fine sofar - now lets decode the rest */
+ /* now lets create a decoder, but without parsing the SyncML
+ * start tags (because it's not there) and skip the XML
+ * part as we don't need it.
+ */
+ pSubDecoder = (XltDecoderPtr_t)smlLibMalloc(sizeof(XltDecoder_t));
+ if (pSubDecoder == NULL) {
+ smlLibFree(pSubBuf);
+ return;
+ }
+ pSubDecoder->finished = 0;
+ pSubDecoder->final = 0;
+ pSubDecoder->scanner = NULL;
+ if (xltUtilCreateStack(&pSubDecoder->tagstack, 10) != SML_ERR_OK) {
+ smlLibFree(pSubDecoder);
+ smlLibFree(pSubBuf);
+ return;
+ }
+ if (xltDecWbxmlInit(pSubBuf+(*ppPcdata)->length,&pSubBuf, &pSubDecoder->scanner) != SML_ERR_OK) {
+ xltDecTerminate(pSubDecoder);
+ smlLibFree(pSubBuf);
+ return;
+ }
+ pSubDecoder->charset = pSubDecoder->scanner->charset;
+ pSubDecoder->charsetStr = NULL;
+
+ pSubPcdata = (SmlPcdataPtr_t)smlLibMalloc(sizeof(SmlPcdata_t));
+ if (pSubPcdata == NULL) {
+ xltDecTerminate(pSubDecoder);
+ smlLibFree(pSubPcdata);
+ smlLibFree(pSubBuf);
+ return;
+ }
+ /* T.K.
+ * In the future we need to check the WBXML stringtable and
+ * switch into the right Sub DTD. But sofar only DevInf is
+ * supported so we can save time and space
+ */
+ /* T.K.
+ * To prevent buffer corruption when __USE_DEVINF__ is not used
+ * we initialize _err with any errorcode != OK, and this way
+ * force the function to exit without modifying the ppPcdata
+ */
+ _err = SML_ERR_UNSPECIFIC;
+#ifdef __USE_DEVINF__
+ pSubPcdata->contentType = SML_PCDATA_EXTENSION;
+ pSubPcdata->extension = SML_EXT_DEVINF;
+ pSubPcdata->length = 0;
+ pSubPcdata->content = NULL;
+
+ pScannerPriv = (wbxmlScannerPrivPtr_t)pSubDecoder->scanner;
+ pScannerPriv->activeExt = SML_EXT_DEVINF;
+ pScannerPriv->cpattr = 0;
+ pScannerPriv->cptag = (SmlPcdataExtension_t)0;
+ smlLibMemset(pScannerPriv->curtok, 0,sizeof(XltDecToken_t));
+
+ _err = buildDevInfDevInfCmd(pSubDecoder, (VoidPtr_t)&pSubPcdata->content);
+#endif
+
+ if (_err != SML_ERR_OK) {
+ xltDecTerminate(pSubDecoder);
+ smlLibFree(pSubPcdata);
+ smlLibFree(pSubBuf);
+ return;
+ }
+
+ /* parsing is done, now lets anchor it within the original PCDATA element */
+ smlFreePcdata(*ppPcdata);
+ *ppPcdata = pSubPcdata;
+
+ /* we are done */
+ xltDecTerminate(pSubDecoder);
+ smlLibFree(pSubBuf);
+
+ return;
+}
+
+#endif
+#endif
diff --git a/src/syncml_tk/src/sml/xlt/all/xltdecwbxml.h b/src/syncml_tk/src/sml/xlt/all/xltdecwbxml.h
new file mode 100755
index 0000000..949dfb1
--- /dev/null
+++ b/src/syncml_tk/src/sml/xlt/all/xltdecwbxml.h
@@ -0,0 +1,60 @@
+/**
+ * @file
+ * internal WBXML decoder header file
+ *
+ * @target_system -
+ * @target_os -
+ */
+
+/*
+ * Copyright Notice
+ * Copyright (c) Ericsson, IBM, Lotus, Matsushita Communication
+ * Industrial Co., Ltd., Motorola, Nokia, Openwave Systems, Inc.,
+ * Palm, Inc., Psion, Starfish Software, Symbian, Ltd. (2001).
+ * All Rights Reserved.
+ * Implementation of all or part of any Specification may require
+ * licenses under third party intellectual property rights,
+ * including without limitation, patent rights (such a third party
+ * may or may not be a Supporter). The Sponsors of the Specification
+ * are not responsible and shall not be held responsible in any
+ * manner for identifying or failing to identify any or all such
+ * third party intellectual property rights.
+ *
+ * THIS DOCUMENT AND THE INFORMATION CONTAINED HEREIN ARE PROVIDED
+ * ON AN "AS IS" BASIS WITHOUT WARRANTY OF ANY KIND AND ERICSSON, IBM,
+ * LOTUS, MATSUSHITA COMMUNICATION INDUSTRIAL CO. LTD, MOTOROLA,
+ * NOKIA, PALM INC., PSION, STARFISH SOFTWARE AND ALL OTHER SYNCML
+ * SPONSORS DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+ * BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
+ * HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
+ * SHALL ERICSSON, IBM, LOTUS, MATSUSHITA COMMUNICATION INDUSTRIAL CO.,
+ * LTD, MOTOROLA, NOKIA, PALM INC., PSION, STARFISH SOFTWARE OR ANY
+ * OTHER SYNCML SPONSOR BE LIABLE TO ANY PARTY FOR ANY LOSS OF
+ * PROFITS, LOSS OF BUSINESS, LOSS OF USE OF DATA, INTERRUPTION OF
+ * BUSINESS, OR FOR DIRECT, INDIRECT, SPECIAL OR EXEMPLARY, INCIDENTAL,
+ * PUNITIVE OR CONSEQUENTIAL DAMAGES OF ANY KIND IN CONNECTION WITH
+ * THIS DOCUMENT OR THE INFORMATION CONTAINED HEREIN, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH LOSS OR DAMAGE.
+ *
+ * The above notice and this paragraph must be included on all copies
+ * of this document that are made.
+ *
+ */
+
+/*************************************************************************/
+/* Definitions */
+/*************************************************************************/
+#ifndef _XLT_DEC_WBXML_H
+#define _XLT_DEC_WBXML_H
+
+#include "XLTDecAll.h"
+
+xltDecScanner_t*
+xltDecWbxmlScannerCreate(unsigned char *buf, long buflen);
+
+void
+xltDecWbxmlScannerDestroy(xltDecScanner_t *p);
+
+
+#endif
diff --git a/src/syncml_tk/src/sml/xlt/all/xltdecxml.c b/src/syncml_tk/src/sml/xlt/all/xltdecxml.c
new file mode 100755
index 0000000..a3ea694
--- /dev/null
+++ b/src/syncml_tk/src/sml/xlt/all/xltdecxml.c
@@ -0,0 +1,1234 @@
+/**
+ * @file
+ * XML scanner
+ *
+ * @target_system all
+ * @target_os all
+ * @description The XML scanner/tokenizer. Used by the SyncML parser.
+ */
+
+/*
+ * Copyright Notice
+ * Copyright (c) Ericsson, IBM, Lotus, Matsushita Communication
+ * Industrial Co., Ltd., Motorola, Nokia, Openwave Systems, Inc.,
+ * Palm, Inc., Psion, Starfish Software, Symbian, Ltd. (2001).
+ * All Rights Reserved.
+ * Implementation of all or part of any Specification may require
+ * licenses under third party intellectual property rights,
+ * including without limitation, patent rights (such a third party
+ * may or may not be a Supporter). The Sponsors of the Specification
+ * are not responsible and shall not be held responsible in any
+ * manner for identifying or failing to identify any or all such
+ * third party intellectual property rights.
+ *
+ * THIS DOCUMENT AND THE INFORMATION CONTAINED HEREIN ARE PROVIDED
+ * ON AN "AS IS" BASIS WITHOUT WARRANTY OF ANY KIND AND ERICSSON, IBM,
+ * LOTUS, MATSUSHITA COMMUNICATION INDUSTRIAL CO. LTD, MOTOROLA,
+ * NOKIA, PALM INC., PSION, STARFISH SOFTWARE AND ALL OTHER SYNCML
+ * SPONSORS DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+ * BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
+ * HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
+ * SHALL ERICSSON, IBM, LOTUS, MATSUSHITA COMMUNICATION INDUSTRIAL CO.,
+ * LTD, MOTOROLA, NOKIA, PALM INC., PSION, STARFISH SOFTWARE OR ANY
+ * OTHER SYNCML SPONSOR BE LIABLE TO ANY PARTY FOR ANY LOSS OF
+ * PROFITS, LOSS OF BUSINESS, LOSS OF USE OF DATA, INTERRUPTION OF
+ * BUSINESS, OR FOR DIRECT, INDIRECT, SPECIAL OR EXEMPLARY, INCIDENTAL,
+ * PUNITIVE OR CONSEQUENTIAL DAMAGES OF ANY KIND IN CONNECTION WITH
+ * THIS DOCUMENT OR THE INFORMATION CONTAINED HEREIN, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH LOSS OR DAMAGE.
+ *
+ * The above notice and this paragraph must be included on all copies
+ * of this document that are made.
+ *
+ */
+
+#include "syncml_tk_prefix_file.h" // %%% luz: needed for precompiled headers in eVC++
+
+#include "define.h"
+#ifdef __SML_XML__
+/*************************************************************************/
+/* Definitions */
+/*************************************************************************/
+#include "xltdeccom.h"
+#include "xlttags.h"
+#include "xltdec.h"
+
+#include <libmem.h>
+#include <libstr.h>
+
+#include <smlerr.h>
+
+/** @copydoc wbxmlScannerPriv_s */
+typedef struct xmlScannerPriv_s xmlScannerPriv_t, *xmlScannerPrivPtr_t;
+/**
+ * Private Interface for the XML scanner.
+ */
+struct xmlScannerPriv_s
+{
+ /* public */
+ Ret_t (*nextTok)(XltDecScannerPtr_t);
+ Ret_t (*destroy)(XltDecScannerPtr_t);
+ Ret_t (*pushTok)(XltDecScannerPtr_t);
+ void (*setBuf)(XltDecScannerPtr_t pScanner, const MemPtr_t pBufStart, const MemPtr_t pBufEnd);
+ MemPtr_t (*getPos)(XltDecScannerPtr_t pScanner);
+
+ XltDecTokenPtr_t curtok; /**< current token */
+ Long_t charset; /**< 0 */
+ String_t charsetStr; /**< character set */
+ Long_t pubID; /**< 0 */
+ String_t pubIDStr; /**< document public identifier */
+ SmlPcdataExtension_t ext; /**< which is the actual open namespace ? */
+ SmlPcdataExtension_t prev_ext; /**< which is the previous open namespace ? */
+ XltTagID_t ext_tag; /**< which tag started the actual namespace ? */
+ XltTagID_t prev_ext_tag; /**< which tag started the previous open namespace ? */
+ String_t nsprefix; /**< prefix used for active namespace (if any) */
+ Byte_t nsprelen; /**< how long is the prefix ? (to save smlLibStrlen calls) */
+ Flag_t finished;
+
+ /* private */
+ MemPtr_t pos; /**< current position */
+ MemPtr_t bufend; /**< end of buffer */
+};
+
+/*
+ * Public methods of the scanner interface.
+ *
+ * Description see XLTDecCom.h.
+ */
+static Ret_t _destroy(XltDecScannerPtr_t);
+static Ret_t _nextTok(XltDecScannerPtr_t);
+static Ret_t _pushTok(XltDecScannerPtr_t);
+static void _setBuf(XltDecScannerPtr_t, const MemPtr_t, const MemPtr_t);
+static MemPtr_t _getPos(XltDecScannerPtr_t);
+
+/**
+ * Advance the current position pointer after checking whether the end of
+ * the buffer has been reached. If the end of the buffer has been reached
+ * the scanner's finished flag is set.
+ *
+ * @param bytes (IN)
+ * read this many bytes
+ * @param pScanner (IN/OUT)
+ * the scanner
+ * @return 1, if end of buffer has not been reached\n
+ * 0 otherwise
+ */
+static Boolean_t readBytes(xmlScannerPrivPtr_t pScanner, Long_t bytes);
+
+/*
+ * Skip whitespaces.
+ */
+static void skipS(xmlScannerPrivPtr_t pScanner);
+
+static Ret_t xmlTag(xmlScannerPrivPtr_t pScanner, Byte_t endtag);
+static Ret_t xmlName(xmlScannerPrivPtr_t pScanner, String_t *name);
+static Ret_t xmlCharData(xmlScannerPrivPtr_t pScanner);
+static Ret_t xmlProlog(xmlScannerPrivPtr_t pScanner);
+static Ret_t xmlDocTypeDecl(xmlScannerPrivPtr_t pScanner);
+static Ret_t xmlXMLDecl(xmlScannerPrivPtr_t pScanner);
+static Ret_t xmlAttribute(xmlScannerPrivPtr_t pScanner, String_t *name, String_t *value);
+static Ret_t xmlStringConst(xmlScannerPrivPtr_t pScanner, String_t *value);
+
+static Ret_t xmlSkipPCDATA(xmlScannerPrivPtr_t pScanner);
+static Ret_t xmlSkipComment(xmlScannerPrivPtr_t pScanner);
+static Ret_t xmlSkipAttributes(xmlScannerPrivPtr_t pScanner);
+static Ret_t xmlSkipPI(xmlScannerPrivPtr_t pScanner);
+static Ret_t xmlCDATA(xmlScannerPrivPtr_t pScanner);
+Boolean_t isPcdata(XltTagID_t tagid);
+
+/*************************************************************************/
+/* External Functions */
+/*************************************************************************/
+
+
+Ret_t
+xltDecXmlInit(const MemPtr_t pBufEnd, MemPtr_t *ppBufPos, XltDecScannerPtr_t *ppScanner)
+{
+ xmlScannerPrivPtr_t pScanner;
+ Ret_t rc;
+
+ pScanner = (xmlScannerPrivPtr_t)smlLibMalloc(sizeof(xmlScannerPriv_t));
+ if (pScanner == NULL) {
+ *ppScanner = NULL;
+ return SML_ERR_NOT_ENOUGH_SPACE;
+ }
+
+ memset(pScanner, 0, sizeof(*pScanner));
+ pScanner->pos = *ppBufPos;
+ pScanner->bufend = pBufEnd;
+ pScanner->curtok = (XltDecTokenPtr_t)smlLibMalloc(sizeof(XltDecToken_t));
+ if (pScanner->curtok == NULL) {
+ smlLibFree(pScanner);
+ *ppScanner = NULL;
+ return SML_ERR_NOT_ENOUGH_SPACE;
+ }
+
+ memset(pScanner->curtok, 0, sizeof(*pScanner->curtok));
+ pScanner->curtok->tagid = TN_UNDEF;
+ pScanner->ext = SML_EXT_UNDEFINED;
+ pScanner->prev_ext = (SmlPcdataExtension_t)255;
+ pScanner->ext_tag = TN_UNDEF;
+ pScanner->prev_ext_tag = TN_UNDEF;
+
+ /* point public/private methods to the right implementation */
+ pScanner->nextTok = _nextTok;
+ pScanner->destroy = _destroy;
+ pScanner->pushTok = _pushTok;
+ pScanner->setBuf = _setBuf;
+ pScanner->getPos = _getPos;
+
+ if ((rc = xmlProlog(pScanner)) != SML_ERR_OK) {
+ smlLibFree(pScanner->curtok);
+ smlLibFree(pScanner);
+ *ppScanner = NULL;
+ return rc;
+ }
+
+ *ppScanner = (XltDecScannerPtr_t)pScanner;
+
+
+
+ return SML_ERR_OK;
+}
+
+/**
+ * Free memory. Description see XltDecAll.h.
+ */
+static Ret_t
+_destroy(XltDecScannerPtr_t pScanner)
+{
+ xmlScannerPrivPtr_t pScannerPriv;
+
+ if (pScanner == NULL)
+ return SML_ERR_OK;
+
+ pScannerPriv = (xmlScannerPrivPtr_t)pScanner;
+ smlLibFree(pScannerPriv->curtok);
+ smlLibFree(pScannerPriv->charsetStr);
+ smlLibFree(pScannerPriv->pubIDStr);
+ smlLibFree(pScannerPriv);
+
+ return SML_ERR_OK;
+}
+
+/**
+ * Get next token. Description see XltDecAll.h.
+ */
+static Ret_t
+_nextTok(XltDecScannerPtr_t pScanner)
+{
+ xmlScannerPrivPtr_t pScannerPriv;
+ Ret_t rc;
+
+ pScannerPriv = (xmlScannerPrivPtr_t)pScanner;
+ pScannerPriv->curtok->start = pScannerPriv->pos;
+
+ if (pScannerPriv->curtok->type!=TOK_CONT)
+ skipS(pScannerPriv);
+
+ /* skip unsupported elements until we find a supported one */
+ rc = 0;
+
+
+ while (!rc) {
+ if (smlLibStrncmp((String_t)pScannerPriv->pos, "<!--", 4) == 0) {
+ rc = xmlSkipComment(pScannerPriv);
+ } else if (smlLibStrncmp((String_t)pScannerPriv->pos, "<?", 2) == 0) {
+ rc = xmlSkipPI(pScannerPriv);
+ } else if (smlLibStrncmp((String_t)pScannerPriv->pos, "</", 2) == 0) {
+ rc = xmlTag(pScannerPriv, 1);
+ break;
+ } else if (smlLibStrncmp((String_t)pScannerPriv->pos, "<![CDATA[", 9) == 0) {
+ rc = xmlCDATA(pScannerPriv);
+ break;
+ } else if ((isPcdata(pScannerPriv->curtok->tagid)) && (pScannerPriv->curtok->type != TOK_TAG_END)) {
+ rc = xmlSkipPCDATA(pScannerPriv);
+ break;
+ } else if (smlLibStrncmp((String_t)pScannerPriv->pos, "<", 1) == 0) {
+ rc = xmlTag(pScannerPriv, 0);
+ break;
+ } else {
+ rc = xmlCharData(pScannerPriv);
+ break;
+ }
+ }
+ if (rc)
+ return rc;
+
+ return SML_ERR_OK;
+}
+
+/**
+ * Reset the scanner to the starting position of the current token within
+ * the buffer. Description see XltDecAll.h.
+ */
+static Ret_t _pushTok(XltDecScannerPtr_t pScanner)
+{
+ xmlScannerPrivPtr_t pScannerPriv;
+
+ pScannerPriv = (xmlScannerPrivPtr_t)pScanner;
+ pScannerPriv->pos = pScannerPriv->curtok->start;
+
+ /* invalidate curtok */
+ /* T.K. Possible Error. pScannerPriv->curtok is of type XltDecToken_t NOT ...Ptr_t */
+ // OrigLine:
+ // smlLibMemset(pScannerPriv->curtok, 0, sizeof(XltDecTokenPtr_t));
+ pScannerPriv->curtok->type = (XltTokType_t)0;
+
+ return SML_ERR_OK;
+}
+
+/**
+ * Set the working buffer of the scanner.
+ */
+static void
+_setBuf(XltDecScannerPtr_t pScanner, const MemPtr_t pBufStart,
+ const MemPtr_t pBufEnd)
+{
+ xmlScannerPrivPtr_t pScannerPriv = (xmlScannerPrivPtr_t)pScanner;
+ pScannerPriv->pos = pBufStart;
+ pScannerPriv->bufend = pBufEnd;
+}
+
+/**
+ * Get the current position of the scanner within its working buffer.
+ */
+static MemPtr_t
+_getPos(XltDecScannerPtr_t pScanner)
+{
+ return ((xmlScannerPrivPtr_t)pScanner)->pos;
+}
+
+
+
+
+/*************************************************************************/
+/* Internal Functions */
+/*************************************************************************/
+
+/*
+ * Advance the position pointer. Description see above.
+ */
+static Boolean_t
+readBytes(xmlScannerPrivPtr_t pScanner, Long_t bytes)
+{
+ if (pScanner->pos + bytes > pScanner->bufend) {
+ pScanner->finished = 1;
+ return 0;
+ }
+ pScanner->pos += bytes;
+ return 1;
+}
+
+/**
+ * Skip whitespace.
+ */
+static void skipS(xmlScannerPrivPtr_t pScanner)
+{
+ for (;;) {
+ switch (*pScanner->pos) {
+ case 9: /* tab stop */
+ case 10: /* line feed */
+ case 13: /* carriage return */
+ case 32: /* space */
+ // %%% luz: 2001-07-03: added exit from loop if no more bytes
+ if (!readBytes(pScanner, 1)) return;
+ break;
+ default:
+ return;
+ }
+ }
+}
+
+/**
+ * Scan the XML prolog (might be empty...).
+ */
+static Ret_t
+xmlProlog(xmlScannerPrivPtr_t pScanner)
+{
+ Ret_t rc;
+
+ if (pScanner->pos + 5 > pScanner->bufend)
+ return SML_ERR_OK;
+ if (smlLibStrncmp((String_t)pScanner->pos, "<?xml", 5) == 0)
+ if ((rc = xmlXMLDecl(pScanner)) != SML_ERR_OK)
+ return rc;
+
+ skipS(pScanner);
+
+ while ((pScanner->pos + 4 <= pScanner->bufend) &&
+ ((smlLibStrncmp((String_t)pScanner->pos, "<!--", 4) == 0) ||
+ (smlLibStrncmp((String_t)pScanner->pos, "<?", 2) == 0))) {
+ if (smlLibStrncmp((String_t)pScanner->pos, "<!--", 4) == 0)
+ rc = xmlSkipComment(pScanner);
+ else
+ rc = xmlSkipPI(pScanner);
+ if (rc != SML_ERR_OK)
+ return rc;
+ skipS(pScanner);
+ }
+
+ if ((pScanner->pos + 9 <= pScanner->bufend) &&
+ (smlLibStrncmp((String_t)pScanner->pos, "<!DOCTYPE", 9) == 0))
+ if ((rc = xmlDocTypeDecl(pScanner)) != SML_ERR_OK)
+ return rc;
+
+ skipS(pScanner);
+
+ return SML_ERR_OK;
+}
+
+/**
+ * Part of the Prolog scanning
+ */
+static Ret_t
+xmlDocTypeDecl(xmlScannerPrivPtr_t pScanner)
+{
+ Ret_t rc;
+ String_t name = NULL;
+ String_t syslit = NULL;
+ String_t publit = NULL;
+
+ readBytes(pScanner, 9);
+ skipS(pScanner);
+ if ((rc = xmlName(pScanner, &name)) != SML_ERR_OK) {
+ smlLibFree(name);
+ return rc;
+ }
+ skipS(pScanner);
+
+ /* parse ExternalID */
+ if ((pScanner->pos + 6 <= pScanner->bufend) &&
+ (smlLibStrncmp((String_t)pScanner->pos, "SYSTEM", 6) == 0)) {
+ readBytes(pScanner, 6);
+ skipS(pScanner);
+ if ((rc = xmlStringConst(pScanner, &syslit)) != SML_ERR_OK) {
+ smlLibFree(name);
+ smlLibFree(syslit);
+ return rc;
+ }
+ } else if ((pScanner->pos + 6 <= pScanner->bufend) &&
+ (smlLibStrncmp((String_t)pScanner->pos, "PUBLIC", 6) == 0)) {
+ readBytes(pScanner, 6);
+ skipS(pScanner);
+ if ((rc = xmlStringConst(pScanner, &publit)) != SML_ERR_OK) {
+ smlLibFree(name);
+ smlLibFree(publit);
+ return rc;
+ }
+ skipS(pScanner);
+ if ((rc = xmlStringConst(pScanner, &syslit)) != SML_ERR_OK) {
+ smlLibFree(name);
+ smlLibFree(syslit);
+ smlLibFree(publit);
+ return rc;
+ }
+ }
+
+ smlLibFree(name);
+ smlLibFree(syslit);
+ smlLibFree(publit);
+
+ skipS(pScanner);
+
+ if (*pScanner->pos != '>')
+ return SML_DECODEERROR(SML_ERR_XLT_INVAL_XML_DOC,pScanner,"xmlDocTypeDecl");
+ readBytes(pScanner, 1);
+
+ return SML_ERR_OK;
+}
+
+/**
+ * Part of the Prolog scanning
+ */
+static Ret_t
+xmlXMLDecl(xmlScannerPrivPtr_t pScanner)
+{
+ String_t name, value;
+ Ret_t rc;
+
+ readBytes(pScanner, 5);
+ skipS(pScanner);
+
+ /* mandatory version info */
+ if ((rc = xmlAttribute(pScanner, &name, &value)) != SML_ERR_OK)
+ return rc;
+ if (smlLibStrcmp(name, "version") != 0) {
+ smlLibFree(name);
+ smlLibFree(value);
+ return SML_DECODEERROR(SML_ERR_XLT_INVAL_XML_DOC,pScanner,"xmlXMLDecl");
+ }
+ smlLibFree(name);
+ smlLibFree(value);
+
+ skipS(pScanner);
+
+ /* optional attributes are encoding and standalone */
+ while ((pScanner->pos + 2 <= pScanner->bufend) &&
+ (smlLibStrncmp((String_t)pScanner->pos, "?>", 2) != 0)) {
+ if ((rc = xmlAttribute(pScanner, &name, &value)) != SML_ERR_OK)
+ return rc;
+ smlLibFree(name);
+ smlLibFree(value);
+ skipS(pScanner);
+ }
+
+ if (pScanner->pos + 2 > pScanner->bufend)
+ return SML_DECODEERROR(SML_ERR_XLT_END_OF_BUFFER,pScanner,"xmlXMLDecl");
+
+ readBytes(pScanner, 2);
+
+ return SML_ERR_OK;
+}
+
+/**
+ * Handle Attributes //function can be used if attributes get necessary
+ */
+static Ret_t
+xmlAttribute(xmlScannerPrivPtr_t pScanner, String_t *name, String_t *value)
+{
+ Ret_t rc;
+
+ skipS(pScanner);
+
+ if ((rc = xmlName(pScanner, name)) != SML_ERR_OK)
+ return rc;
+
+ skipS(pScanner);
+
+ /* no attributes found, because this tag has none -> bail out */
+ if (*pScanner->pos == '>') {
+ return SML_DECODEERROR(SML_ERR_XLT_MISSING_CONT,pScanner,"xmlAttribute");
+ }
+ if (smlLibStrncmp((String_t)pScanner->pos, "/>", 2) == 0) {
+ return SML_DECODEERROR(SML_ERR_XLT_MISSING_CONT,pScanner,"xmlAttribute");
+ }
+
+ if (*pScanner->pos != '=') {
+ smlLibFree(*name);
+ *name = NULL;
+ *value = NULL;
+ return SML_DECODEERROR(SML_ERR_XLT_INVAL_XML_DOC,pScanner,"xmlAttribute");
+ }
+ readBytes(pScanner, 1);
+
+ skipS(pScanner);
+
+ if ((rc = xmlStringConst(pScanner, value)) != SML_ERR_OK) {
+ smlLibFree(*name);
+ *name = NULL;
+ *value = NULL;
+ return rc;
+ }
+
+ return SML_ERR_OK;
+}
+
+/**
+ * Handle Pcdata String Constants
+ */
+static Ret_t
+xmlStringConst(xmlScannerPrivPtr_t pScanner, String_t *value)
+{
+ String_t end;
+ int len;
+ char del;
+
+ if ((*pScanner->pos != '"') && (*pScanner->pos != '\'')) {
+ *value = NULL;
+ return SML_DECODEERROR(SML_ERR_XLT_INVAL_XML_DOC,pScanner,"xmlStringConst");
+ }
+ del = *pScanner->pos;
+ readBytes(pScanner, 1);
+
+ if ((end = smlLibStrchr((String_t)pScanner->pos, del)) == NULL) {
+ *value = NULL;
+
+ return SML_DECODEERROR(SML_ERR_XLT_END_OF_BUFFER,pScanner,"xmlStringConst");
+ }
+ len = end - (String_t)pScanner->pos;
+ if ((*value = (String_t)smlLibMalloc(len + 1)) == NULL)
+ {
+
+ return SML_ERR_NOT_ENOUGH_SPACE;
+ }
+ smlLibMemset(*value, 0, len + 1);
+ smlLibStrncpy(*value, (String_t)pScanner->pos, len);
+ readBytes(pScanner, len + 1);
+
+ return SML_ERR_OK;
+}
+
+/**
+ * parse HTML entity (like &amp;)
+ *
+ * pScanner->pos must pointer to &. Afterwards begin
+ * points to the character and len is set to 1.
+ */
+static Ret_t
+xmlHTMLEntity(xmlScannerPrivPtr_t pScanner, MemPtr_t *begin, int *len)
+{
+ // start of an HTML entity: decode just that single character
+ static const struct {
+ const char *entity;
+ char character;
+ } entities[] = {
+ { "amp", '&' },
+ { "gt", '>' },
+ { "lt", '<' },
+ { "apos", '\'' },
+ { "quot", '"' }
+ };
+ MemPtr_t entity = pScanner->pos + 1;
+ int i;
+
+ while (*pScanner->pos != ';') {
+ if (pScanner->pos >= pScanner->bufend) {
+ return SML_DECODEERROR(SML_ERR_XLT_INVAL_SYNCML_DOC,pScanner,"xmlHTMLEntity");
+ }
+ if (!readBytes(pScanner, 1)) {
+ return SML_DECODEERROR(SML_ERR_XLT_END_OF_BUFFER,pScanner,"xmlHTMLEntity");
+ }
+ }
+
+ // remember length and skip ;
+ *len = pScanner->pos - entity;
+ if (!readBytes(pScanner, 1)) {
+ return SML_DECODEERROR(SML_ERR_XLT_END_OF_BUFFER,pScanner,"xmlHTMLEntity");
+ }
+
+ // identify the entity
+ *begin = NULL;
+ for (i = 0; i < sizeof(entities)/sizeof(entities[0]); i++) {
+ if (!strncmp(entities[i].entity, (char *)entity, *len) &&
+ entities[i].entity[*len] == 0) {
+ *begin = (MemPtr_t)&entities[i].character;
+ break;
+ }
+ }
+ if (*begin) {
+ // found it, continue below by copying it
+ *len = 1;
+ } else {
+ // invalid entity: abort!?
+ return SML_DECODEERROR(SML_ERR_XLT_INVAL_SYNCML_DOC,pScanner,"xmlHTMLEntity");
+ }
+
+ return SML_ERR_OK;
+}
+
+
+/**
+ * FUNCTION: xmlCharData
+ *
+ * Handle Pcdata character data content
+ */
+static Ret_t
+xmlCharData(xmlScannerPrivPtr_t pScanner)
+{
+ SmlPcdataPtr_t pPCData;
+ MemPtr_t begin;
+ int len;
+
+
+ pPCData = (SmlPcdataPtr_t)smlLibMalloc(sizeof(SmlPcdata_t));
+ if (pPCData == NULL)
+ return SML_ERR_NOT_ENOUGH_SPACE;
+ pPCData->contentType = SML_PCDATA_UNDEFINED;
+ pPCData->length = 0;
+ pPCData->content = NULL;
+
+ begin = pScanner->pos;
+
+ if (*pScanner->pos >= *pScanner->bufend) {
+ pPCData->content = NULL;
+ pPCData->contentType = SML_PCDATA_UNDEFINED;
+ pPCData->extension = SML_EXT_UNDEFINED;
+ pPCData->length = 0;
+ pScanner->curtok->type = TOK_CONT;
+ pScanner->curtok->pcdata = pPCData;
+ //smlLibFree(pPCData);
+ return SML_DECODEERROR(SML_ERR_XLT_END_OF_BUFFER,pScanner,"xmlCharData");
+ }
+
+ if (*pScanner->pos == '&') {
+ Ret_t ret = xmlHTMLEntity(pScanner, &begin, &len);
+ if (ret) {
+ smlLibFree(pPCData);
+ return ret;
+ }
+ } else {
+ while (*pScanner->pos != '<' && (*pScanner->pos != '&'))
+ {
+ if (pScanner->pos >= pScanner->bufend)
+ {
+ smlLibFree(pPCData);
+ return SML_DECODEERROR(SML_ERR_XLT_INVAL_SYNCML_DOC,pScanner,"xmlCharData");
+ }
+ if (!readBytes(pScanner, 1)) {
+ smlLibFree(pPCData);
+ return SML_DECODEERROR(SML_ERR_XLT_END_OF_BUFFER,pScanner,"xmlCharData");
+ }
+ }
+ len = pScanner->pos - begin;
+ }
+
+ pPCData->content = smlLibMalloc(len + 1);
+ if (pPCData->content == NULL){
+ smlLibFree(pPCData);
+ return SML_ERR_NOT_ENOUGH_SPACE;
+ }
+ //%%%waste of CPU: smlLibMemset(pPCData->content, 0, len + 1);
+ ((MemPtr_t)pPCData->content)[len]=0; // set terminator
+ smlLibMemcpy(pPCData->content, begin, len);
+ pPCData->contentType = SML_PCDATA_STRING;
+ pPCData->length = len;
+
+ pScanner->curtok->type = TOK_CONT;
+ pScanner->curtok->pcdata = pPCData;
+
+ return SML_ERR_OK;
+}
+
+/**
+ * Handle Name Elements
+ */
+static Ret_t
+xmlName(xmlScannerPrivPtr_t pScanner, String_t *name)
+{
+ MemPtr_t begin;
+ String_t tmp;
+ int len;
+
+ begin = pScanner->pos;
+ while (((*pScanner->pos >= 'a') && (*pScanner->pos <= 'z')) ||
+ ((*pScanner->pos >= 'A') && (*pScanner->pos <= 'Z')) ||
+ ((*pScanner->pos >= '0') && (*pScanner->pos <= '9')) ||
+ (*pScanner->pos == '.') || (*pScanner->pos == '-') ||
+ (*pScanner->pos == '_') || (*pScanner->pos == ':'))
+ if (!readBytes(pScanner, 1))
+ return SML_DECODEERROR(SML_ERR_XLT_END_OF_BUFFER,pScanner,"xmlName");
+
+ len = pScanner->pos - begin;
+ /* T.K. bail out if len is zero without modifying name */
+ if (len == 0) return SML_ERR_OK;
+
+
+ tmp = (String_t)smlLibMalloc(len + 1);
+ if (tmp == NULL) {
+ *name = NULL;
+ return SML_ERR_NOT_ENOUGH_SPACE;
+ }
+ smlLibMemset(tmp, 0, len + 1);
+ smlLibStrncpy(tmp, (String_t)begin, len);
+ *name = tmp;
+ return SML_ERR_OK;
+}
+
+/**
+ * FUNCTION: xmlTag
+ *
+ * Handle XML Tags
+ */
+static Ret_t
+xmlTag(xmlScannerPrivPtr_t pScanner, Byte_t endtag)
+{
+ Ret_t rc;
+ String_t name, attname=NULL, value = NULL, nsprefix = NULL;
+ Byte_t nsprelen = 0;
+ XltTagID_t tagid;
+ SmlPcdataExtension_t ext;
+
+
+ if (endtag) {
+ if (!readBytes(pScanner, 2))
+ return SML_DECODEERROR(SML_ERR_XLT_END_OF_BUFFER,pScanner,"xmlTag");
+ } else {
+ if (!readBytes(pScanner, 1))
+ return SML_DECODEERROR(SML_ERR_XLT_END_OF_BUFFER,pScanner,"xmlTag");
+ }
+
+ if ((rc = xmlName(pScanner, &name)) != SML_ERR_OK)
+ {
+ if (rc != SML_ERR_NOT_ENOUGH_SPACE)
+ {
+ return SML_DECODEERROR(SML_ERR_XLT_INVAL_XML_DOC,pScanner,"xmlTag");
+ }
+ else
+ {
+ return rc;
+ }
+
+ }
+
+ ext = pScanner->ext;
+ if (!endtag) {
+ /* Namespaces can only be defined on start, never on endtags
+ * but we have to make sure we close a namespace on the corrsponding endtag.
+ * Thats why we a) only open a namespace when it differs from the previous one, and
+ * b) record the tag_id that opend the namespace so we can close it when the
+ * corresponding endtag is reached.
+ */
+
+ if ((rc = xmlAttribute(pScanner, &attname, &value)) == SML_ERR_OK)
+ {
+ if (smlLibStrncmp(attname, "xmlns", 5) == 0) {
+ /* Heureka we found a Namespace :-) */
+ /* It's save to check attname[5] here, as it contains at least the terminating '\000' */
+ if (attname[5] == ':') { /* we found a namespace prefixdefinition */
+ nsprelen = (Byte_t)smlLibStrlen(&attname[6]);
+ nsprefix = smlLibMalloc(nsprelen+1);
+ if (nsprefix == NULL) {
+ smlLibFree(attname);
+ smlLibFree(value);
+ smlLibFree(name);
+ return SML_ERR_NOT_ENOUGH_SPACE;
+ }
+ smlLibStrcpy(nsprefix,&attname[6]);
+ }
+ ext = getExtByName(value);
+ if (ext == 255) {
+ smlLibFree(nsprefix); /* doesn't harm, even when empty */
+ smlLibFree(attname);
+ smlLibFree(value);
+ smlLibFree(name);
+ return SML_DECODEERROR(SML_ERR_XLT_INVALID_CODEPAGE,pScanner,"xmlTag");
+ }
+ } else {
+ if (rc == SML_ERR_NOT_ENOUGH_SPACE) {
+ smlLibFree(attname);
+ smlLibFree(value);
+ smlLibFree(name);
+ return SML_ERR_NOT_ENOUGH_SPACE;
+ }
+ else {
+ /* we found an unknown attribute -> bail out */
+ smlLibFree(attname);
+ smlLibFree(value);
+ /* nsprefix is empty here so we save us a function call */
+ smlLibFree(name);
+ return SML_DECODEERROR(SML_ERR_XLT_INVAL_XML_DOC,pScanner,"xmlTag");
+ }
+ }
+ }
+ else if ( rc != SML_DECODEERROR(SML_ERR_XLT_MISSING_CONT,pScanner,"xmlTag"))
+ {
+ /* xmlAttribute returns an SML_ERR_XLT_MISSING_CONT error when
+ * no attribute was found. This is not an error, but everything else is.
+ */
+ smlLibFree(value);
+ smlLibFree(name);
+ return rc;
+ }
+ } // if endtag
+
+ if (pScanner->ext == ext) {
+ /* no new Namespace found - lets proceed with the active one */
+
+ /* first lets check wether a tag is in the right namespace, in case
+ * we are using namespaces with prefix notation ('mi:Format' instead of
+ * 'Format nsattr="...").
+ * If we are and the token is not in this namespace -> bail out
+ */
+ if (pScanner->nsprelen > 0 && smlLibStrlen(name) > pScanner->nsprelen+1) {
+ if (name[pScanner->nsprelen] != ':' || smlLibStrncmp(name,pScanner->nsprefix, pScanner->nsprelen) != 0) {
+ smlLibFree(name);
+ smlLibFree(attname);
+ smlLibFree(value);
+ smlLibFree(nsprefix);
+ return SML_DECODEERROR(SML_ERR_XLT_NO_MATCHING_CODEPAGE,pScanner,"xmlTag");
+ }
+ }
+ /* Strip off namespace prefixes and ':' to find the tag.
+ * If no prefix is defined (pScanner->nsprelen == 0) take the whole tagname.
+ */
+ if (pScanner->nsprelen > 0)
+ rc = getTagIDByStringAndExt(&name[0+pScanner->nsprelen+1], pScanner->ext, &tagid);
+ else
+ rc = getTagIDByStringAndExt(name, pScanner->ext, &tagid);
+ // %%% luz 2005-11-11 : added implicit try with MetInf namespace (workaround for ill-formed xml like in sync4j pda clients)
+ if (rc==SML_ERR_XLT_INVAL_PROTO_ELEM) {
+ if (pScanner->nsprelen > 0)
+ rc = getTagIDByStringAndExt(&name[0+pScanner->nsprelen+1], SML_EXT_METINF, &tagid);
+ else
+ rc = getTagIDByStringAndExt(name, SML_EXT_METINF, &tagid);
+ }
+ } else {
+ /* we have a new Namespace */
+ if (nsprelen > 0 && smlLibStrlen(name) > nsprelen+1) {
+ if (name[nsprelen] != ':' || smlLibStrncmp(name,nsprefix, nsprelen) != 0) {
+ smlLibFree(name);
+ smlLibFree(attname);
+ smlLibFree(value);
+ smlLibFree(nsprefix);
+ return SML_DECODEERROR(SML_ERR_XLT_NO_MATCHING_CODEPAGE,pScanner,"xmlTag");
+ }
+ }
+ /* Strip off namespace prefixes and ':' to find the tag.
+ * If no prefix is defined (pScanner->nsprelen == 0) take the whole tagname.
+ */
+ if (nsprelen > 0)
+ rc = getTagIDByStringAndExt(&name[nsprelen+1], ext, &tagid);
+ else
+ rc = getTagIDByStringAndExt(name, ext, &tagid);
+ }
+ /* free temporary buffers */
+ smlLibFree(name);
+ smlLibFree(attname);
+ smlLibFree(value);
+
+ if ((tagid == TN_UNDEF) || (rc != SML_ERR_OK)) {
+ smlLibFree(nsprefix);
+ return rc;
+ }
+
+ /* remember the old extension including the corresponding start tag if we found a new one */
+ if (ext != pScanner->ext) { /* namespace changed */
+ pScanner->prev_ext = pScanner->ext; /* remember the old ext */
+ pScanner->prev_ext_tag = pScanner->ext_tag; /* and the corresponding start tag */
+ pScanner->ext = ext;
+ pScanner->ext_tag = tagid;
+ smlLibFree(pScanner->nsprefix);
+ pScanner->nsprefix = nsprefix;
+ pScanner->nsprelen = nsprelen;
+ }
+
+
+ pScanner->curtok->tagid = tagid;
+ pScanner->curtok->ext = pScanner->ext;
+ skipS(pScanner);
+
+ if (endtag) {
+ /* found end tag */
+ if (smlLibStrncmp((String_t)pScanner->pos, ">", 1) != 0)
+ return SML_DECODEERROR(SML_ERR_XLT_INVAL_XML_DOC,pScanner,"xmlTag");
+ pScanner->curtok->type = TOK_TAG_END;
+ readBytes(pScanner, 1);
+ /* in case of an endtag we might need to close the current CP */
+ if (tagid == pScanner->ext_tag) {
+ pScanner->ext_tag = pScanner->prev_ext_tag;
+ pScanner->ext = pScanner->prev_ext;
+ pScanner->prev_ext = SML_EXT_UNDEFINED;
+ pScanner->prev_ext_tag = TN_UNDEF;
+ pScanner->nsprelen = 0;
+ smlLibFree(pScanner->nsprefix);
+ pScanner->nsprefix = NULL;
+ }
+ } else {
+ /* Attributes are not supported in SyncML -> skip them*/
+ if ((rc = xmlSkipAttributes(pScanner)) != SML_ERR_OK) return rc;
+
+ if (smlLibStrncmp((String_t)pScanner->pos, "/>", 2) == 0) {
+ /* found empty tag */
+ pScanner->curtok->type = TOK_TAG_EMPTY;
+ readBytes(pScanner, 2);
+ } else if (smlLibStrncmp((String_t)pScanner->pos, ">", 1) == 0) {
+ pScanner->curtok->type = TOK_TAG_START;
+ readBytes(pScanner, 1);
+ } else {
+ return SML_DECODEERROR(SML_ERR_XLT_INVAL_XML_DOC,pScanner,"xmlTag");
+ }
+ }
+ return SML_ERR_OK;
+}
+
+/**
+ * Skip PI elements
+ */
+static Ret_t
+xmlSkipPI(xmlScannerPrivPtr_t pScanner)
+{
+ if (pScanner) { /* Get rid of warning, this should not be called anyway */
+ }
+
+ return SML_ERR_UNSPECIFIC;
+}
+
+/**
+ * Skip comments
+ */
+static Ret_t
+xmlSkipComment(xmlScannerPrivPtr_t pScanner)
+{
+ readBytes(pScanner, 4);
+
+ while ((pScanner->pos + 3 <= pScanner->bufend) &&
+ (smlLibStrncmp((String_t)pScanner->pos, "-->", 3) != 0))
+ if (!readBytes(pScanner, 1))
+ return SML_DECODEERROR(SML_ERR_XLT_END_OF_BUFFER,pScanner,"xmlSkipComment");
+
+
+ if (pScanner->pos + 3 > pScanner->bufend)
+ return SML_DECODEERROR(SML_ERR_XLT_END_OF_BUFFER,pScanner,"xmlSkipComment");
+
+ if (!readBytes(pScanner, 3))
+ return SML_DECODEERROR(SML_ERR_XLT_END_OF_BUFFER,pScanner,"xmlSkipComment");
+
+ skipS(pScanner);
+
+ return SML_ERR_OK;
+}
+
+/**
+ * Skip attributes -> they are not supported in SyncML
+ */
+static Ret_t
+xmlSkipAttributes(xmlScannerPrivPtr_t pScanner)
+{
+
+ while ((pScanner->pos + 1 <= pScanner->bufend) &&
+ (smlLibStrncmp((String_t)pScanner->pos, ">", 1)) && (smlLibStrncmp((String_t)pScanner->pos, "/>", 2)))
+ if (!readBytes(pScanner, 1))
+ return SML_DECODEERROR(SML_ERR_XLT_END_OF_BUFFER,pScanner,"xmlSkipAttributes");
+
+
+ if (pScanner->pos + 1 > pScanner->bufend)
+ return SML_DECODEERROR(SML_ERR_XLT_END_OF_BUFFER,pScanner,"xmlSkipAttributes");
+
+ return SML_ERR_OK;
+}
+
+/**
+ * Handle a CDATA content
+ */
+static Ret_t
+xmlCDATA(xmlScannerPrivPtr_t pScanner)
+{
+ SmlPcdataPtr_t pPCData;
+ MemPtr_t begin;
+ int len;
+
+ readBytes(pScanner, 9);
+
+ pPCData = (SmlPcdataPtr_t)smlLibMalloc(sizeof(SmlPcdata_t));
+ if (pPCData == NULL)
+ return SML_ERR_NOT_ENOUGH_SPACE;
+ pPCData->contentType = SML_PCDATA_UNDEFINED;
+ pPCData->length = 0;
+ pPCData->content = NULL;
+
+ begin = pScanner->pos;
+ while (!((pScanner->pos[0] == ']') && (pScanner->pos[1] == ']') && (pScanner->pos[2] == '>')))
+ if (!readBytes(pScanner, 1))
+ return SML_DECODEERROR(SML_ERR_XLT_END_OF_BUFFER,pScanner,"xmlCDATA");
+
+ len = pScanner->pos - begin;
+ pPCData->content = smlLibMalloc(len + 1);
+ if (pPCData->content == NULL) {
+ smlLibFree(pPCData);
+ return SML_ERR_NOT_ENOUGH_SPACE;
+ }
+
+ //%%%waste of CPU: smlLibMemset(pPCData->content, 0, len + 1);
+ ((MemPtr_t)pPCData->content)[len]=0; // set terminator
+ smlLibMemcpy(pPCData->content, begin, len); // copy data
+ pPCData->contentType = SML_PCDATA_CDATA;
+ pPCData->length = len;
+
+ pScanner->curtok->type = TOK_CONT;
+ pScanner->curtok->pcdata = pPCData;
+
+ readBytes(pScanner, 3);
+
+ return SML_ERR_OK;
+}
+
+/**
+ * Read over a Pcdata content
+ */
+static Ret_t
+xmlSkipPCDATA(xmlScannerPrivPtr_t pScanner)
+{
+ SmlPcdataPtr_t pPCData;
+ MemPtr_t begin;
+ int len;
+ Ret_t rc;
+ String_t _tagString = NULL;
+ String_t _tagString2 = NULL;
+
+ /* Check wether this PCData might contain a subdtd.
+ ** We assume a Sub DTD starts with '<' as first char.
+ ** If this char is present start further processing else
+ ** take it as pure String data. If the scanning returns an
+ ** error we reject the file, as '<' is not a valid char inside
+ ** PCData elements.
+ */
+ if (smlLibStrncmp((String_t)pScanner->pos, "<", 1) == 0) {
+ rc = xmlTag(pScanner, 0);
+ return rc;
+ }
+
+ _tagString = smlLibMalloc(XML_MAX_TAGLEN);
+ if (_tagString == NULL) return SML_ERR_NOT_ENOUGH_SPACE;
+ if ((rc = getTagString(pScanner->curtok->tagid, _tagString, pScanner->curtok->ext)) != SML_ERR_OK)
+ {
+ smlLibFree(_tagString);
+ return rc;
+ }
+
+ _tagString2 = smlLibMalloc(smlLibStrlen(_tagString) + 4 + (pScanner->nsprelen +1));
+
+ // build a end tag String to compate (e.g. </Meta>)
+ // beware of possible namespace prefixes
+ if (_tagString2 == NULL)
+ {
+ smlLibFree(_tagString);
+ return SML_ERR_NOT_ENOUGH_SPACE;
+ }
+
+ _tagString2 = smlLibStrcpy(_tagString2,"</");
+ if (pScanner->nsprelen > 0) {
+ _tagString2 = smlLibStrcat(_tagString2,pScanner->nsprefix);
+ _tagString2 = smlLibStrcat(_tagString2,":");
+ }
+ _tagString2 = smlLibStrcat(_tagString2,_tagString);
+ _tagString2 = smlLibStrcat(_tagString2,">");
+ smlLibFree(_tagString);
+
+ pPCData = (SmlPcdataPtr_t)smlLibMalloc(sizeof(SmlPcdata_t));
+
+
+
+ if (pPCData == NULL)
+ {
+ smlLibFree(_tagString2);
+ return SML_ERR_NOT_ENOUGH_SPACE;
+ }
+ pPCData->contentType = SML_PCDATA_UNDEFINED;
+ pPCData->extension = SML_EXT_UNDEFINED;
+ pPCData->length = 0;
+ pPCData->content = NULL;
+
+ begin = pScanner->pos;
+
+ if (*pScanner->pos == '&') {
+ Ret_t ret = xmlHTMLEntity(pScanner, &begin, &len);
+ if (ret) {
+ smlLibFree(_tagString2);
+ return ret;
+ }
+ } else {
+ // Read Pcdata content until end tag appears or we run into something which
+ // requires special decoding: CDATA or HTML entity
+ while (smlLibStrncmp((String_t)pScanner->pos, _tagString2, smlLibStrlen(_tagString2)) != 0)
+ {
+ // check if end of buffer
+ if (pScanner->pos >= pScanner->bufend) {
+ smlLibFree(_tagString2);
+ return SML_DECODEERROR(SML_ERR_XLT_INVAL_SYNCML_DOC,pScanner,"xmlSkipPCDATA");
+ }
+ // %%% luz 2006-09-07
+ // check if another <![CDATA[ opens here or an HTML entity
+ if (smlLibStrncmp((String_t)pScanner->pos,"<![CDATA[",9)==0 ||
+ *pScanner->pos == '&') {
+ // stop PCDATA scanning here
+ break;
+ }
+ if (!readBytes(pScanner, 1))
+ return SML_DECODEERROR(SML_ERR_XLT_END_OF_BUFFER,pScanner,"xmlSkipPCDATA");
+
+ }
+ len = pScanner->pos - begin;
+ }
+
+ smlLibFree(_tagString2);
+
+ pPCData->content = smlLibMalloc(len + 1);
+ if (pPCData->content == NULL)
+ {
+ smlLibFree(pPCData);
+ return SML_ERR_NOT_ENOUGH_SPACE;
+ }
+
+ //%%%waste of CPU: smlLibMemset(pPCData->content, 0, len + 1);
+ ((MemPtr_t)pPCData->content)[len]=0; // set terminator
+ smlLibMemcpy(pPCData->content, begin, len);
+ pPCData->contentType = SML_PCDATA_STRING;
+ pPCData->length = len;
+
+ pScanner->curtok->type = TOK_CONT;
+ pScanner->curtok->pcdata = pPCData;
+
+
+ return SML_ERR_OK;
+}
+
+/**
+ * Check if the current tag id represents a Pcdata element
+ */
+Boolean_t isPcdata(XltTagID_t tagid)
+{
+ switch (tagid)
+ {
+ case TN_CMD:
+ case TN_CMDID:
+ case TN_CMDREF:
+ case TN_LANG:
+ case TN_LOCNAME:
+ case TN_LOCURI:
+ case TN_MSGID:
+ case TN_MSGREF:
+ case TN_RESPURI:
+ case TN_SESSIONID:
+ case TN_SOURCEREF:
+ case TN_TARGETREF:
+ case TN_VERSION:
+ case TN_PROTO:
+ case TN_DATA:
+ case TN_META:
+ case TN_NUMBEROFCHANGES:
+ /* SyncML DS 1.2, Synthesis/luz 2005-08-24 */
+ case TN_FILTERTYPE:
+#ifdef __USE_METINF__
+ case TN_METINF_EMI:
+ case TN_METINF_FORMAT:
+ case TN_METINF_FREEID:
+ case TN_METINF_FREEMEM:
+ case TN_METINF_LAST:
+ case TN_METINF_MARK:
+ case TN_METINF_MAXMSGSIZE:
+ /* SCTSTK - 18/03/2002 S.H. 2002-04-05 : SyncML 1.1 */
+ case TN_METINF_MAXOBJSIZE:
+ case TN_METINF_NEXT:
+ case TN_METINF_NEXTNONCE:
+ case TN_METINF_SIZE:
+ case TN_METINF_TYPE:
+ case TN_METINF_VERSION:
+#endif
+#ifdef __USE_DEVINF__
+ case TN_DEVINF_MAN:
+ case TN_DEVINF_MOD:
+ case TN_DEVINF_OEM:
+ case TN_DEVINF_FWV:
+ case TN_DEVINF_SWV:
+ case TN_DEVINF_HWV:
+ case TN_DEVINF_DEVID:
+ case TN_DEVINF_DEVTYP:
+ case TN_DEVINF_MAXGUIDSIZE:
+ case TN_DEVINF_SOURCEREF:
+ case TN_DEVINF_DISPLAYNAME:
+ case TN_DEVINF_CTTYPE:
+ case TN_DEVINF_DATATYPE:
+ case TN_DEVINF_SIZE:
+ case TN_DEVINF_PROPNAME:
+ case TN_DEVINF_VALENUM:
+ case TN_DEVINF_PARAMNAME:
+ case TN_DEVINF_SYNCTYPE:
+ case TN_DEVINF_XNAM:
+ case TN_DEVINF_XVAL:
+ case TN_DEVINF_MAXMEM:
+ case TN_DEVINF_MAXID:
+ case TN_DEVINF_VERCT:
+ case TN_DEVINF_VERDTD:
+ /* SyncML DS 1.2, Synthesis/luz 2005-08-24 */
+ case TN_DEVINF_MAXOCCUR:
+ case TN_DEVINF_FILTERKEYWORD:
+ case TN_DEVINF_MAXSIZE:
+#endif
+ return 1;
+ default:
+ return 0;
+ }
+}
+#endif
diff --git a/src/syncml_tk/src/sml/xlt/all/xltdevinf.c b/src/syncml_tk/src/sml/xlt/all/xltdevinf.c
new file mode 100755
index 0000000..fd0d6c7
--- /dev/null
+++ b/src/syncml_tk/src/sml/xlt/all/xltdevinf.c
@@ -0,0 +1,1355 @@
+/**
+ * @file
+ * DeviceInf DTD related functions for the en-/decoder
+ *
+ * @target_system all
+ * @target_os all
+ */
+
+/*
+ * Copyright Notice
+ * Copyright (c) Ericsson, IBM, Lotus, Matsushita Communication
+ * Industrial Co., Ltd., Motorola, Nokia, Openwave Systems, Inc.,
+ * Palm, Inc., Psion, Starfish Software, Symbian, Ltd. (2001).
+ * All Rights Reserved.
+ * Implementation of all or part of any Specification may require
+ * licenses under third party intellectual property rights,
+ * including without limitation, patent rights (such a third party
+ * may or may not be a Supporter). The Sponsors of the Specification
+ * are not responsible and shall not be held responsible in any
+ * manner for identifying or failing to identify any or all such
+ * third party intellectual property rights.
+ *
+ * THIS DOCUMENT AND THE INFORMATION CONTAINED HEREIN ARE PROVIDED
+ * ON AN "AS IS" BASIS WITHOUT WARRANTY OF ANY KIND AND ERICSSON, IBM,
+ * LOTUS, MATSUSHITA COMMUNICATION INDUSTRIAL CO. LTD, MOTOROLA,
+ * NOKIA, PALM INC., PSION, STARFISH SOFTWARE AND ALL OTHER SYNCML
+ * SPONSORS DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+ * BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
+ * HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
+ * SHALL ERICSSON, IBM, LOTUS, MATSUSHITA COMMUNICATION INDUSTRIAL CO.,
+ * LTD, MOTOROLA, NOKIA, PALM INC., PSION, STARFISH SOFTWARE OR ANY
+ * OTHER SYNCML SPONSOR BE LIABLE TO ANY PARTY FOR ANY LOSS OF
+ * PROFITS, LOSS OF BUSINESS, LOSS OF USE OF DATA, INTERRUPTION OF
+ * BUSINESS, OR FOR DIRECT, INDIRECT, SPECIAL OR EXEMPLARY, INCIDENTAL,
+ * PUNITIVE OR CONSEQUENTIAL DAMAGES OF ANY KIND IN CONNECTION WITH
+ * THIS DOCUMENT OR THE INFORMATION CONTAINED HEREIN, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH LOSS OR DAMAGE.
+ *
+ * The above notice and this paragraph must be included on all copies
+ * of this document that are made.
+ *
+ */
+#include "syncml_tk_prefix_file.h" // %%% luz: needed for precompiled headers in eVC++
+
+#include "define.h"
+#ifdef __USE_DEVINF__
+
+#include "smldevinfdtd.h"
+#include "smlmetinfdtd.h"
+#include "xlttags.h"
+#include "xltdevinf.h"
+#include "xlttagtbl.h"
+#include "xltenc.h"
+#include "xltencwbxml.h"
+
+#include <libstr.h>
+#include <smlerr.h>
+#include <smldtd.h>
+#include <libmem.h>
+#include <libutil.h>
+#include <mgrutil.h>
+
+/* decoder callbacks */
+Ret_t buildDevInfDevInfContent(XltDecoderPtr_t pDecoder, VoidPtr_t *ppElem);
+Ret_t buildDevInfCtcap(XltDecoderPtr_t pDecoder, VoidPtr_t *ppElem, Boolean_t datastoreLocal);
+
+
+Ret_t buildDevInfDevInfCmd(XltDecoderPtr_t pDecoder, VoidPtr_t *ppElem) {
+ XltDecScannerPtr_t pScanner;
+ SmlDevInfDevInfPtr_t pElem = NULL;
+ Ret_t rc;
+
+ pScanner = pDecoder->scanner;
+
+ if (*ppElem != NULL)
+ return SML_DECODEERROR(SML_ERR_XLT_INVAL_SYNCML_DOC,pScanner,"buildDevInfDevInfCmd");
+
+ if (IS_EMPTY(pScanner->curtok)) {
+ *ppElem = pElem;
+ return SML_ERR_OK;
+ }
+
+ if (((rc = nextToken(pDecoder)) != SML_ERR_OK)) {
+ smlLibFree(pElem);
+ return rc;
+ }
+
+ switch (pScanner->curtok->tagid) {
+ case TN_DEVINF_DEVINF:
+ rc = buildDevInfDevInfContent(pDecoder, (VoidPtr_t)&pElem);
+ break;
+ default:
+ rc = SML_DECODEERROR(SML_ERR_XLT_INVAL_SYNCML_DOC,pScanner,"buildDevInfDevInfCmd");
+ }
+ if (rc != SML_ERR_OK) {
+ smlLibFree(pElem);
+ return rc;
+ }
+ *ppElem = pElem;
+
+ return SML_ERR_OK;
+}
+
+Ret_t buildDevInfDevInfContent(XltDecoderPtr_t pDecoder, VoidPtr_t *ppElem) {
+ XltDecScannerPtr_t pScanner;
+ SmlDevInfDevInfPtr_t pElem;
+ Ret_t rc;
+
+ pScanner = pDecoder->scanner;
+
+ if (*ppElem != NULL)
+ return SML_DECODEERROR(SML_ERR_XLT_INVAL_SYNCML_DOC,pScanner,"buildDevInfDevInfContent");
+
+ if ((pElem = (SmlDevInfDevInfPtr_t)smlLibMalloc(sizeof(SmlDevInfDevInf_t))) == NULL)
+ return SML_ERR_NOT_ENOUGH_SPACE;
+ smlLibMemset(pElem, 0, sizeof(SmlDevInfDevInf_t));
+
+ if (IS_EMPTY(pScanner->curtok)) {
+ *ppElem = pElem;
+ return SML_ERR_OK;
+ }
+
+ if (((rc = nextToken(pDecoder)) != SML_ERR_OK)) {
+ smlLibFree(pElem);
+ return rc;
+ }
+
+ while (pScanner->curtok->type != TOK_TAG_END) {
+ switch (pScanner->curtok->tagid) {
+ case TN_DEVINF_VERDTD:
+ rc = buildPCData(pDecoder, (VoidPtr_t)&pElem->verdtd);
+ break;
+ case TN_DEVINF_MAN:
+ rc = buildPCData(pDecoder, (VoidPtr_t)&pElem->man);
+ break;
+ case TN_DEVINF_MOD:
+ rc = buildPCData(pDecoder, (VoidPtr_t)&pElem->mod);
+ break;
+ case TN_DEVINF_OEM:
+ rc = buildPCData(pDecoder, (VoidPtr_t)&pElem->oem);
+ break;
+ case TN_DEVINF_FWV:
+ rc = buildPCData(pDecoder, (VoidPtr_t)&pElem->fwv);
+ break;
+ case TN_DEVINF_SWV:
+ rc = buildPCData(pDecoder, (VoidPtr_t)&pElem->swv);
+ break;
+ case TN_DEVINF_HWV:
+ rc = buildPCData(pDecoder, (VoidPtr_t)&pElem->hwv);
+ break;
+ case TN_DEVINF_DEVID:
+ rc = buildPCData(pDecoder, (VoidPtr_t)&pElem->devid);
+ break;
+ case TN_DEVINF_DEVTYP:
+ rc = buildPCData(pDecoder, (VoidPtr_t)&pElem->devtyp);
+ break;
+ case TN_DEVINF_DATASTORE:
+ rc = buildDevInfDataStoreList(pDecoder, (VoidPtr_t)&pElem->datastore);
+ break;
+ case TN_DEVINF_CTCAP:
+ rc = buildDevInfCtcap(pDecoder, (VoidPtr_t)&pElem->ctcap,FALSE); // CTCap which is global in devInf itself (pre-DS 1.2)
+ break;
+ case TN_DEVINF_EXT:
+ rc = buildDevInfExtList(pDecoder, (VoidPtr_t)&pElem->ext);
+ break;
+ /* SCTSTK - 18/03/2002 S.H. 2002-04-05 : SyncML 1.1 */
+ case TN_DEVINF_UTC:
+ pElem->flags |= SmlDevInfUTC_f;
+ rc = buildEmptyTag(pDecoder); // allow for <tag></tag> instead of <tag/>
+ break;
+ case TN_DEVINF_NOFM:
+ pElem->flags |= SmlDevInfNOfM_f;
+ rc = buildEmptyTag(pDecoder); // allow for <tag></tag> instead of <tag/>
+ break;
+ case TN_DEVINF_LARGEOBJECT:
+ pElem->flags |= SmlDevInfLargeObject_f;
+ rc = buildEmptyTag(pDecoder); // allow for <tag></tag> instead of <tag/>
+ break;
+ default:
+ rc = SML_DECODEERROR(SML_ERR_XLT_INVAL_SYNCML_DOC,pScanner,"buildDevInfDevInfContent");
+ }
+ if (rc != SML_ERR_OK) {
+ smlLibFree(pElem);
+ return rc;
+ }
+ if (((rc = nextToken(pDecoder)) != SML_ERR_OK)) {
+ smlLibFree(pElem);
+ return rc;
+ }
+ }
+ *ppElem = pElem;
+
+ return SML_ERR_OK;
+}
+
+
+static Ret_t buildDevInfFilterCapList(XltDecoderPtr_t pDecoder, VoidPtr_t *ppElem)
+{
+ SmlDevInfFilterCapListPtr_t *ppListElem = NULL;
+ SmlDevInfFilterCapListPtr_t pFiltercap = NULL;
+ SmlPcdataListPtr_t *ppList = NULL;
+ SmlPcdataListPtr_t pList = NULL;
+ XltDecScannerPtr_t pScanner = pDecoder->scanner;
+
+ Ret_t rc;
+
+ // if empty, that's ok and treated as NOP: <FilterCap/>
+ if (IS_EMPTY(pScanner->curtok)) {
+ return SML_ERR_OK;
+ }
+ // get next token
+ if (((rc = nextToken(pDecoder)) != SML_ERR_OK)) {
+ return rc;
+ }
+ // get pointer to where we'd insert the next list element
+ ppListElem = (SmlDevInfFilterCapListPtr_t *) ppElem;
+ while (*ppListElem!=NULL) {
+ ppListElem = &((*ppListElem)->next);
+ }
+ // create new list element now
+ pFiltercap = (SmlDevInfFilterCapListPtr_t)smlLibMalloc(sizeof(SmlDevInfFilterCapList_t));
+ if (pFiltercap == NULL) return SML_ERR_NOT_ENOUGH_SPACE;
+ smlLibMemset(pFiltercap, 0, sizeof(SmlDevInfFilterCapList_t));
+ pFiltercap->data = (SmlDevInfFilterCapPtr_t)smlLibMalloc(sizeof(SmlDevInfFilterCap_t));
+ if (pFiltercap->data == NULL) {
+ smlFreeDevInfFilterCapList(pFiltercap);
+ return SML_ERR_NOT_ENOUGH_SPACE;
+ }
+ smlLibMemset(pFiltercap->data, 0, sizeof(SmlDevInfFilterCap_t));
+ // scan until we hit end of enclosure (</FilterCap>)
+ while (pScanner->curtok->type != TOK_TAG_END) {
+ // process tags
+ switch (pScanner->curtok->tagid) {
+ /* PCDATA elements */
+ case TN_DEVINF_CTTYPE:
+ rc = buildPCData(pDecoder, (VoidPtr_t)&pFiltercap->data->cttype);
+ break;
+ case TN_DEVINF_VERCT:
+ rc = buildPCData(pDecoder, (VoidPtr_t)&pFiltercap->data->verct);
+ break;
+ case TN_DEVINF_FILTERKEYWORD:
+ ppList = &pFiltercap->data->filterkeyword;
+ goto keywordOrPropname;
+ case TN_DEVINF_PROPNAME:
+ ppList = &pFiltercap->data->propname;
+ goto keywordOrPropname;
+ keywordOrPropname:
+ // find last existing valenum
+ while (*ppList!=NULL)
+ ppList=&((*ppList)->next);
+ // create new list element
+ pList = (SmlPcdataListPtr_t)smlLibMalloc(sizeof(SmlPcdataList_t));
+ if (pList == NULL) return SML_ERR_NOT_ENOUGH_SPACE;
+ smlLibMemset(pList, 0, sizeof(SmlPcdataList_t));
+ // insert contents
+ rc = buildPCData(pDecoder, (VoidPtr_t)&pList->data);
+ if (rc==SML_ERR_OK) {
+ // ok, add element to list
+ *ppList = pList;
+ ppList = &(pList->next);
+ }
+ else
+ smlFreePcdataList(pList); // invalid, get rid of it
+ break;
+ default:
+ rc = SML_DECODEERROR(SML_ERR_XLT_INVAL_SYNCML_DOC,pScanner,"buildDevInfFilterCapList");
+ }
+ if (rc != SML_ERR_OK) {
+ smlFreeDevInfFilterCapList(pFiltercap);
+ return rc;
+ }
+ if (((rc = nextToken(pDecoder)) != SML_ERR_OK)) {
+ smlFreeDevInfFilterCapList(pFiltercap);
+ return rc;
+ }
+ } /* while we do not see an end tag */
+ // link into list
+ *ppListElem = pFiltercap;
+ // set elem link to where we must link in next element
+ ppListElem = &(pFiltercap->next);
+ // done ok
+ return SML_ERR_OK;
+}
+
+
+Ret_t buildDevInfDataStoreCmd(XltDecoderPtr_t pDecoder, VoidPtr_t *ppElem) {
+ XltDecScannerPtr_t pScanner;
+ SmlDevInfDatastorePtr_t pElem;
+ Ret_t rc;
+
+ pScanner = pDecoder->scanner;
+
+ if (*ppElem != NULL)
+ return SML_DECODEERROR(SML_ERR_XLT_INVAL_SYNCML_DOC,pScanner,"buildDevInfDataStoreCmd");
+
+ if ((pElem = (SmlDevInfDatastorePtr_t)smlLibMalloc(sizeof(SmlDevInfDatastore_t))) == NULL)
+ return SML_ERR_NOT_ENOUGH_SPACE;
+ smlLibMemset(pElem, 0, sizeof(SmlDevInfDatastore_t));
+
+ if (IS_EMPTY(pScanner->curtok)) {
+ *ppElem = pElem;
+ return SML_ERR_OK;
+ }
+
+ if (((rc = nextToken(pDecoder)) != SML_ERR_OK)) {
+ smlLibFree(pElem);
+ return rc;
+ }
+
+ while (pScanner->curtok->type != TOK_TAG_END) {
+ switch (pScanner->curtok->tagid) {
+ /* PCDATA elements */
+ case TN_DEVINF_SOURCEREF:
+ rc = buildPCData(pDecoder, (VoidPtr_t)&pElem->sourceref);
+ break;
+ case TN_DEVINF_DISPLAYNAME:
+ rc = buildPCData(pDecoder, (VoidPtr_t)&pElem->displayname);
+ break;
+ case TN_DEVINF_MAXGUIDSIZE:
+ rc = buildPCData(pDecoder, (VoidPtr_t)&pElem->maxguidsize);
+ break;
+ /* other elements */
+ case TN_DEVINF_RXPREF:
+ if (pElem->rxpref!=NULL) {
+ // we already have a RxPref - simply ignore further ones
+ // (in DS 1.2, some implementations send an other rx-pref for the folder object apart from the main type)
+ SmlDevInfXmitPtr_t dummyP = NULL;
+ rc = buildDevInfXmitCmd(pDecoder, (VoidPtr_t)&dummyP);
+ smlFreeDevInfXmit(dummyP); // simply discard after parsing
+ }
+ else
+ rc = buildDevInfXmitCmd(pDecoder, (VoidPtr_t)&pElem->rxpref);
+ break;
+ case TN_DEVINF_TXPREF:
+ if (pElem->txpref!=NULL) {
+ // we already have a txpref - simply ignore further ones
+ // (in DS 1.2, some implementations send an other tx-pref for the folder object apart from the main type)
+ SmlDevInfXmitPtr_t dummyP = NULL;
+ rc = buildDevInfXmitCmd(pDecoder, (VoidPtr_t)&dummyP);
+ smlFreeDevInfXmit(dummyP); // simply discard after parsing
+ }
+ else
+ rc = buildDevInfXmitCmd(pDecoder, (VoidPtr_t)&pElem->txpref);
+ break;
+ case TN_DEVINF_RX:
+ rc = buildDevInfXmitList(pDecoder, (VoidPtr_t)&pElem->rx);
+ break;
+ case TN_DEVINF_TX:
+ rc = buildDevInfXmitList(pDecoder, (VoidPtr_t)&pElem->tx);
+ break;
+ case TN_DEVINF_CTCAP:
+ rc = buildDevInfCtcap(pDecoder, (VoidPtr_t)&pElem->ctcap,TRUE); // CTCap which is local to datastore (DS 1.2 style)
+ break;
+ case TN_DEVINF_DSMEM:
+ rc = buildDevInfDSMemCmd(pDecoder, (VoidPtr_t)&pElem->dsmem);
+ break;
+ case TN_DEVINF_SYNCCAP:
+ rc = buildDevInfSyncCapCmd(pDecoder, (VoidPtr_t)&pElem->synccap);
+ break;
+
+
+
+ /* SyncML DS 1.2, Synthesis/luz 2005-08-25 */
+ case TN_DEVINF_FILTERCAP:
+ rc = buildDevInfFilterCapList(pDecoder, (VoidPtr_t)&pElem->filtercap);
+ break;
+ case TN_DEVINF_FILTERRX:
+ rc = buildDevInfXmitList(pDecoder, (VoidPtr_t)&pElem->filterrx);
+ break;
+ case TN_DEVINF_HIERARCHICAL:
+ pElem->flags |= SmlDevInfHierarchical_f;
+ rc = buildEmptyTag(pDecoder); // allow for <tag></tag> instead of <tag/>
+ break;
+ case TN_DEVINF_FIELDLEVEL:
+ pElem->flags |= SmlDevInfFieldLevel_f;
+ rc = buildEmptyTag(pDecoder); // allow for <tag></tag> instead of <tag/>
+ break;
+
+ default:
+ rc = SML_DECODEERROR(SML_ERR_XLT_INVAL_SYNCML_DOC,pScanner,"buildDevInfDataStoreCmd");
+ }
+ if (rc != SML_ERR_OK) {
+ smlLibFree(pElem);
+ return rc;
+ }
+ if (((rc = nextToken(pDecoder)) != SML_ERR_OK)) {
+ smlLibFree(pElem);
+ return rc;
+ }
+ }
+ *ppElem = pElem;
+
+ return SML_ERR_OK;
+}
+
+Ret_t buildDevInfXmitCmd(XltDecoderPtr_t pDecoder, VoidPtr_t *ppElem) {
+ XltDecScannerPtr_t pScanner;
+ SmlDevInfXmitPtr_t pXmit;
+ Ret_t rc;
+
+ pScanner = pDecoder->scanner;
+
+ if (*ppElem != NULL)
+ return SML_DECODEERROR(SML_ERR_XLT_INVAL_SYNCML_DOC,pScanner,"buildDevInfXmitCmd");
+
+ if ((pXmit = (SmlDevInfXmitPtr_t)smlLibMalloc(sizeof(SmlDevInfXmit_t))) == NULL)
+ return SML_ERR_NOT_ENOUGH_SPACE;
+ smlLibMemset(pXmit, 0, sizeof(SmlDevInfXmit_t));
+
+ if (IS_EMPTY(pScanner->curtok)) {
+ *ppElem = pXmit;
+ return SML_ERR_OK;
+ }
+
+ if (((rc = nextToken(pDecoder)) != SML_ERR_OK)) {
+ smlLibFree(pXmit);
+ return rc;
+ }
+
+ while (pScanner->curtok->type != TOK_TAG_END) {
+ switch (pScanner->curtok->tagid) {
+ /* PCDATA elements */
+ case TN_DEVINF_CTTYPE:
+ rc = buildPCData(pDecoder, (VoidPtr_t)&pXmit->cttype);
+ break;
+ case TN_DEVINF_VERCT:
+ rc = buildPCData(pDecoder, (VoidPtr_t)&pXmit->verct);
+ break;
+ default:
+ rc = SML_DECODEERROR(SML_ERR_XLT_INVAL_SYNCML_DOC,pScanner,"buildDevInfXmitCmd");
+ }
+ if (rc != SML_ERR_OK) {
+ smlLibFree(pXmit);
+ return rc;
+ }
+ if (((rc = nextToken(pDecoder)) != SML_ERR_OK)) {
+ smlLibFree(pXmit);
+ return rc;
+ }
+ }
+ *ppElem = pXmit;
+
+ return SML_ERR_OK;
+}
+
+Ret_t buildDevInfXmitList(XltDecoderPtr_t pDecoder, VoidPtr_t *ppElem) {
+ SmlDevInfXmitListPtr_t pElem = NULL, pPrev = NULL;
+
+ pElem = (SmlDevInfXmitListPtr_t) *ppElem;
+
+ /* advance to the end of the list, and create ther an empty list element */
+ while (pElem != NULL) {
+ pPrev = pElem;
+ pElem = pPrev->next;
+ }
+ if ((pElem = (SmlDevInfXmitListPtr_t)smlLibMalloc(sizeof(SmlDevInfXmitList_t))) == NULL)
+ return SML_ERR_NOT_ENOUGH_SPACE;
+ smlLibMemset(pElem, 0, sizeof(SmlDevInfXmitList_t));
+ if (pPrev != NULL) /* we already had some entries in the list */
+ pPrev->next = pElem;
+ else /* nope we created a new list */
+ *ppElem = pElem;
+ pElem->data = NULL;
+ /* at this point pElem should point to an valid list element */
+ return buildDevInfXmitCmd(pDecoder, (VoidPtr_t)&pElem->data);
+}
+
+Ret_t buildDevInfDataStoreList(XltDecoderPtr_t pDecoder, VoidPtr_t *ppElem) {
+ SmlDevInfDatastoreListPtr_t pElem = NULL, pPrev = NULL;
+
+ pElem = (SmlDevInfDatastoreListPtr_t) *ppElem;
+
+ /* advance to the end of the list, and create ther an empty list element */
+ while (pElem != NULL) {
+ pPrev = pElem;
+ pElem = pPrev->next;
+ }
+ if ((pElem = (SmlDevInfDatastoreListPtr_t)smlLibMalloc(sizeof(SmlDevInfDatastoreList_t))) == NULL)
+ return SML_ERR_NOT_ENOUGH_SPACE;
+ smlLibMemset(pElem, 0, sizeof(SmlDevInfDatastoreList_t));
+ if (pPrev != NULL) /* we already had some entries in the list */
+ pPrev->next = pElem;
+ else /* nope we created a new list */
+ *ppElem = pElem;
+ pElem->data = NULL;
+ /* at this point pElem should point to an valid list element */
+ return buildDevInfDataStoreCmd(pDecoder, (VoidPtr_t)&pElem->data);
+}
+
+Ret_t buildDevInfExtList(XltDecoderPtr_t pDecoder, VoidPtr_t *ppElem) {
+ SmlDevInfExtListPtr_t pElem = NULL, pPrev = NULL;
+
+ pElem = (SmlDevInfExtListPtr_t) *ppElem;
+
+ /* advance to the end of the list, and create ther an empty list element */
+ while (pElem != NULL) {
+ pPrev = pElem;
+ pElem = pPrev->next;
+ }
+ if ((pElem = (SmlDevInfExtListPtr_t)smlLibMalloc(sizeof(SmlDevInfExtList_t))) == NULL)
+ return SML_ERR_NOT_ENOUGH_SPACE;
+ smlLibMemset(pElem, 0, sizeof(SmlDevInfExtList_t));
+ if (pPrev != NULL) /* we already had some entries in the list */
+ pPrev->next = pElem;
+ else /* nope we created a new list */
+ *ppElem = pElem;
+ pElem->data = NULL;
+ /* at this point pElem should point to an valid list element */
+ return buildDevInfExtCmd(pDecoder, (VoidPtr_t)&pElem->data);
+}
+
+
+/// @brief scans common elements of Properties and PropParams
+/// @return SML_ERR_XLT_INVAL_PROTO_ELEM if unknown tag is encountered
+static Ret_t scanPropOrParamElement(XltDecoderPtr_t pDecoder, SmlDevInfCTDataPtr_t pPropOrParam)
+{
+ SmlPcdataListPtr_t *ppValenums = &(pPropOrParam->valenum);
+ SmlPcdataListPtr_t pValenum;
+ XltDecScannerPtr_t pScanner = pDecoder->scanner;
+ Ret_t rc;
+
+ // process tags
+ switch (pScanner->curtok->tagid) {
+ case TN_DEVINF_DISPLAYNAME:
+ rc = buildPCData(pDecoder, (VoidPtr_t)&pPropOrParam->dname);
+ break;
+ case TN_DEVINF_DATATYPE:
+ rc = buildPCData(pDecoder, (VoidPtr_t)&pPropOrParam->datatype);
+ break;
+ case TN_DEVINF_MAXSIZE:
+ case TN_DEVINF_SIZE:
+ rc = buildPCData(pDecoder, (VoidPtr_t)&pPropOrParam->maxsize);
+ break;
+ case TN_DEVINF_MAXOCCUR:
+ rc = buildPCData(pDecoder, (VoidPtr_t)&pPropOrParam->maxoccur);
+ break;
+ /* Flags */
+ case TN_DEVINF_NOTRUNCATE:
+ pPropOrParam->flags |= SmlDevInfNoTruncate_f;
+ rc = buildEmptyTag(pDecoder); // allow for <tag></tag> instead of <tag/>
+ break;
+ /* Valenum* */
+ case TN_DEVINF_VALENUM:
+ // find last existing valenum
+ while (*ppValenums!=NULL)
+ ppValenums=&((*ppValenums)->next);
+ // create new list element
+ pValenum = (SmlPcdataListPtr_t)smlLibMalloc(sizeof(SmlPcdataList_t));
+ if (pValenum == NULL) return SML_ERR_NOT_ENOUGH_SPACE;
+ smlLibMemset(pValenum, 0, sizeof(SmlPcdataList_t));
+ // insert contents
+ rc = buildPCData(pDecoder, (VoidPtr_t)&pValenum->data);
+ if (rc==SML_ERR_OK) {
+ // ok, add element to list
+ *ppValenums = pValenum;
+ ppValenums = &(pValenum->next);
+ }
+ else
+ smlFreePcdataList(pValenum); // invalid, get rid of it
+ break;
+ default:
+ rc = SML_DECODEERROR(SML_ERR_XLT_INVAL_PROTO_ELEM,pScanner,"scanPropOrParamElement");
+ }
+ return rc;
+}
+
+
+// @brief scan a single <PropParam></PropParam> bracket, or a <1.2 type list where a new <PropName> implies end of current PropParam and start of a new one
+static Ret_t buildDevInfPropParam(XltDecoderPtr_t pDecoder, VoidPtr_t *ppElem, int datastoreLocal) {
+ SmlDevInfCTDataListPtr_t *ppListElem = NULL;
+ SmlDevInfCTDataListPtr_t pParam = NULL;
+ Boolean_t newElement = FALSE;
+ XltDecScannerPtr_t pScanner = pDecoder->scanner;
+ Ret_t rc;
+
+ if (IS_EMPTY(pScanner->curtok)) {
+ return SML_ERR_OK;
+ }
+
+ if (datastoreLocal) {
+ // DS 1.2: we have no initial tag to process right away, get next tag now
+ if (((rc = nextToken(pDecoder)) != SML_ERR_OK)) {
+ return rc;
+ }
+ newElement=TRUE; // new <property> has already started here
+ }
+
+ // get pointer to where we'd insert the next list element
+ ppListElem = (SmlDevInfCTDataListPtr_t *)ppElem;
+ while (*ppListElem!=NULL) {
+ ppListElem = &((*ppListElem)->next);
+ }
+ pParam=NULL;
+ // now ppListElem points to a SmlDevInfCTDataListPtr_t which is NULL and
+ // can be overwritten with the link to a new element
+
+ // scan until we hit end of enclosure (</PropParam>) or an unknown other tag
+ while (pScanner->curtok->type != TOK_TAG_END) {
+ if (!datastoreLocal && pScanner->curtok->tagid==TN_DEVINF_PARAMNAME) {
+ // DS 1.1-style CTCap, seeing <ParamName> implies new element
+ newElement=TRUE;
+ }
+ // create new element if needed
+ if (newElement) {
+ newElement=FALSE;
+ pParam = (SmlDevInfCTDataListPtr_t)smlLibMalloc(sizeof(SmlDevInfCTDataList_t));
+ if (pParam == NULL) return SML_ERR_NOT_ENOUGH_SPACE;
+ smlLibMemset(pParam, 0, sizeof(SmlDevInfCTDataList_t));
+ pParam->data = (SmlDevInfCTDataPtr_t)smlLibMalloc(sizeof(SmlDevInfCTData_t));
+ if (pParam->data == NULL) {
+ smlFreeDevInfCTDataList(pParam);
+ return SML_ERR_NOT_ENOUGH_SPACE;
+ }
+ smlLibMemset(pParam->data, 0, sizeof(SmlDevInfCTData_t));
+ // link into list
+ *ppListElem = pParam;
+ // set elem link to where we must link in next element
+ ppListElem = &(pParam->next);
+ }
+ // now, if we don't have a pParam, this is an invalid <DS 1.2 devinf
+ if (pParam==NULL)
+ return SML_DECODEERROR(SML_ERR_XLT_INVAL_SYNCML_DOC,pScanner,"buildDevInfPropParam");
+
+ // process tags
+ switch (pScanner->curtok->tagid) {
+ /* PCDATA */
+ case TN_DEVINF_PARAMNAME:
+ rc = buildPCData(pDecoder, (VoidPtr_t)&pParam->data->name);
+ break;
+ default:
+ rc = scanPropOrParamElement(pDecoder, pParam->data);
+ if (rc==SML_ERR_XLT_INVAL_PROTO_ELEM && !datastoreLocal)
+ return SML_ERR_OK; // parser signals unknown tag, this is ok for pre-1.2
+ break;
+ }
+ if (rc != SML_ERR_OK)
+ return rc;
+ if (((rc = nextToken(pDecoder)) != SML_ERR_OK))
+ return rc;
+ } /* while we do not see an end tag */
+ return SML_ERR_OK;
+}
+
+
+// @brief scan a single <Property></Property> bracket, or a <1.2 type list where a new <PropName> implies end of current property and start of a new one
+static Ret_t buildDevInfProperty(XltDecoderPtr_t pDecoder, VoidPtr_t *ppElem, int datastoreLocal) {
+ SmlDevInfCTDataPropListPtr_t *ppListElem = NULL;
+ SmlDevInfCTDataPropListPtr_t pProp = NULL;
+ Boolean_t newElement = FALSE;
+ XltDecScannerPtr_t pScanner = pDecoder->scanner;
+ Ret_t rc;
+
+ if (IS_EMPTY(pScanner->curtok)) {
+ return SML_ERR_OK;
+ }
+
+ if (datastoreLocal) {
+ // DS 1.2: we have no initial tag to process right away, get next tag now
+ if (((rc = nextToken(pDecoder)) != SML_ERR_OK)) {
+ return rc;
+ }
+ newElement=TRUE; // new <property> has already started here
+ }
+
+ // get pointer to where we'd insert the next list element
+ ppListElem = (SmlDevInfCTDataPropListPtr_t *) ppElem;
+ while (*ppListElem!=NULL) {
+ ppListElem = &((*ppListElem)->next);
+ }
+ pProp=NULL;
+ // now ppListElem points to a SmlDevInfCTDataPropListPtr_t which is NULL and
+ // can be overwritten with the link to a new element
+
+ // scan until we hit end of enclosure (</Property>) or an unknown other tag
+ while (pScanner->curtok->type != TOK_TAG_END) {
+ if (!datastoreLocal && pScanner->curtok->tagid==TN_DEVINF_PROPNAME) {
+ // DS 1.1-style CTCap, seeing <PropName> implies new element
+ newElement=TRUE;
+ }
+ // create new element if needed
+ if (newElement) {
+ newElement=FALSE;
+ pProp = (SmlDevInfCTDataPropListPtr_t)smlLibMalloc(sizeof(SmlDevInfCTDataPropList_t));
+ if (pProp == NULL) return SML_ERR_NOT_ENOUGH_SPACE;
+ smlLibMemset(pProp, 0, sizeof(SmlDevInfCTDataPropList_t));
+ pProp->data = (SmlDevInfCTDataPropPtr_t)smlLibMalloc(sizeof(SmlDevInfCTDataProp_t));
+ if (pProp->data == NULL) {
+ smlFreeDevInfCTDataPropList(pProp);
+ return SML_ERR_NOT_ENOUGH_SPACE;
+ }
+ smlLibMemset(pProp->data, 0, sizeof(SmlDevInfCTDataProp_t));
+ pProp->data->prop = (SmlDevInfCTDataPtr_t)smlLibMalloc(sizeof(SmlDevInfCTData_t));
+ if (pProp->data->prop == NULL) {
+ smlFreeDevInfCTDataPropList(pProp);
+ return SML_ERR_NOT_ENOUGH_SPACE;
+ }
+ smlLibMemset(pProp->data->prop, 0, sizeof(SmlDevInfCTData_t));
+ // link into list
+ *ppListElem = pProp;
+ // set elem link to where we must link in next element
+ ppListElem = &(pProp->next);
+ }
+ // now, if we don't have a pProp, this is an invalid <DS 1.2 devinf
+ if (pProp==NULL)
+ return SML_DECODEERROR(SML_ERR_XLT_INVAL_SYNCML_DOC,pScanner,"buildDevInfProperty");
+
+ // process tags
+ switch (pScanner->curtok->tagid) {
+ case TN_DEVINF_PROPPARAM:
+ // DS 1.2 case: only </PropParam> ends the param building process
+ rc = buildDevInfPropParam(pDecoder, (VoidPtr_t)&pProp->data->param,datastoreLocal);
+ break;
+ case TN_DEVINF_PARAMNAME:
+ // <DS 1.2 case: current token TN_DEVINF_PARAMNAME is processed by builder
+ if (datastoreLocal) {
+ // found <ParamName> directly within <Property> (instead of enclosed in a <PropParam>) - Nokia 2630 style
+ // -> do not reject it but try to process in DS 1.1 style even if this is a DS 1.2 devInf
+ rc = buildDevInfPropParam(pDecoder, (VoidPtr_t)&pProp->data->param,FALSE); // force DS 1.1 style
+ if (rc==SML_ERR_OK)
+ continue; // re-evaluate current tag (tag that caused buildDevInfPropParam() to end, either next <PropName>, unknown or closing </CTCap>
+ }
+ else {
+ rc = buildDevInfPropParam(pDecoder, (VoidPtr_t)&pProp->data->param,datastoreLocal);
+ if (rc==SML_ERR_OK)
+ continue; // re-evaluate current tag (tag that caused buildDevInfPropParam() to end, either next <PropName>, unknown or closing </CTCap>
+ }
+ break;
+
+ /* PCDATA */
+ case TN_DEVINF_PROPNAME:
+ rc = buildPCData(pDecoder, (VoidPtr_t)&pProp->data->prop->name);
+ break;
+
+ default:
+ rc = scanPropOrParamElement(pDecoder, pProp->data->prop);
+ if (rc==SML_ERR_XLT_INVAL_PROTO_ELEM && !datastoreLocal)
+ return SML_ERR_OK; // parser signals unknown tag, this is ok for pre-1.2
+ }
+ if (rc != SML_ERR_OK)
+ return rc;
+ if (((rc = nextToken(pDecoder)) != SML_ERR_OK))
+ return rc;
+ } /* while we do not see an end tag */
+ return SML_ERR_OK;
+}
+
+
+
+// @brief scan a single <ctcap></ctcap> bracket, which may contain serveral ctcaps for <DS 1.2, and only one single ctcap for >=DS 1.2
+Ret_t buildDevInfCtcap(XltDecoderPtr_t pDecoder, VoidPtr_t *ppElem, Boolean_t datastoreLocal)
+{
+ SmlDevInfCtcapListPtr_t *ppListElem = NULL;
+ SmlDevInfCtcapListPtr_t pCtcap = NULL;
+ Boolean_t newElement = FALSE;
+ XltDecScannerPtr_t pScanner = pDecoder->scanner;
+ Ret_t rc;
+
+ if (IS_EMPTY(pScanner->curtok)) {
+ return SML_ERR_OK;
+ }
+
+ if (((rc = nextToken(pDecoder)) != SML_ERR_OK)) {
+ return rc;
+ }
+
+ // get pointer to where we'd insert the next list element
+ ppListElem = (SmlDevInfCtcapListPtr_t *)ppElem;
+ while (*ppListElem!=NULL) {
+ ppListElem = &((*ppListElem)->next);
+ }
+ pCtcap=NULL;
+ // now ppListElem points to a SmlDevInfCtcapListPtr_t which is NULL and
+ // can be overwritten with the link to a new element
+
+ // datastore local CTCap is DS 1.2 or later, and implies that every type description has its own <CTCap></CTCap>
+ // So starting a CTCap implies creating a new element
+ newElement=datastoreLocal;
+
+ while (pScanner->curtok->type != TOK_TAG_END) {
+ if (!datastoreLocal && pScanner->curtok->tagid==TN_DEVINF_CTTYPE) {
+ // DS 1.1-style CTCap, seeing CTTYPE implies new element
+ newElement=TRUE;
+ }
+ // create new element if needed
+ if (newElement) {
+ newElement=FALSE;
+ pCtcap = (SmlDevInfCtcapListPtr_t)smlLibMalloc(sizeof(SmlDevInfCtcapList_t));
+ if (pCtcap == NULL) return SML_ERR_NOT_ENOUGH_SPACE;
+ smlLibMemset(pCtcap, 0, sizeof(SmlDevInfCtcapList_t));
+ pCtcap->data = (SmlDevInfCTCapPtr_t)smlLibMalloc(sizeof(SmlDevInfCTCap_t));
+ if (pCtcap->data == NULL) {
+ smlFreeDevInfCtcapList(pCtcap);
+ return SML_ERR_NOT_ENOUGH_SPACE;
+ }
+ smlLibMemset(pCtcap->data, 0, sizeof(SmlDevInfCTCap_t));
+ // link into list
+ *ppListElem = pCtcap;
+ // set elem link to where we must link in next element
+ ppListElem = &(pCtcap->next);
+ }
+ // now, if we don't have a pCtcap, this is an invalid <DS 1.2 devinf where another tag than <CTType> follows <CTCap>
+ if (pCtcap==NULL)
+ return SML_DECODEERROR(SML_ERR_XLT_INVAL_SYNCML_DOC,pScanner,"buildDevInfCtcap");
+ // process tags
+ switch (pScanner->curtok->tagid) {
+ case TN_DEVINF_CTTYPE:
+ rc = buildPCData(pDecoder, (VoidPtr_t)&pCtcap->data->cttype);
+ break;
+ case TN_DEVINF_VERCT:
+ rc = buildPCData(pDecoder, (VoidPtr_t)&pCtcap->data->verct);
+ break;
+ case TN_DEVINF_PROPERTY:
+ // DS 1.2 case: only </Property> ends the property building process, next token must be read first
+ rc = buildDevInfProperty(pDecoder, (VoidPtr_t)&pCtcap->data->prop,datastoreLocal);
+ break;
+ case TN_DEVINF_PROPNAME:
+ // <DS 1.2 case: current token TN_DEVINF_PROPNAME is processed by builder, next occurence of TN_DEVINF_PROPNAME ends property as well
+ if (datastoreLocal)
+ rc = SML_DECODEERROR(SML_ERR_XLT_INVAL_SYNCML_DOC,pScanner,"buildDevInfCtcap"); // <PropName> must be inside <Property>
+ else {
+ rc = buildDevInfProperty(pDecoder, (VoidPtr_t)&pCtcap->data->prop,datastoreLocal);
+ if (rc==SML_ERR_OK)
+ continue; // re-evaluate current tag (tag that caused buildDevInfProperty() to end, either unknown or closing </CTCap>
+ }
+ break;
+
+ default:
+ rc = SML_DECODEERROR(SML_ERR_XLT_INVAL_SYNCML_DOC,pScanner,"buildDevInfCtcap");
+ }
+ if (rc != SML_ERR_OK) {
+ return rc;
+ }
+ if (((rc = nextToken(pDecoder)) != SML_ERR_OK)) {
+ return rc;
+ }
+ } /* eof while */
+ return SML_ERR_OK;
+}
+
+
+Ret_t buildDevInfDSMemCmd(XltDecoderPtr_t pDecoder, VoidPtr_t *ppElem) {
+ XltDecScannerPtr_t pScanner;
+ SmlDevInfDSMemPtr_t pElem;
+ Ret_t rc;
+
+ pScanner = pDecoder->scanner;
+
+ if (*ppElem != NULL)
+ return SML_DECODEERROR(SML_ERR_XLT_INVAL_SYNCML_DOC,pScanner,"buildDevInfDSMemCmd");
+
+ if ((pElem = (SmlDevInfDSMemPtr_t)smlLibMalloc(sizeof(SmlDevInfDSMem_t))) == NULL)
+ return SML_ERR_NOT_ENOUGH_SPACE;
+ smlLibMemset(pElem, 0, sizeof(SmlDevInfDSMem_t));
+
+ if (IS_EMPTY(pScanner->curtok)) {
+ *ppElem = pElem;
+ return SML_ERR_OK;
+ }
+
+ if (((rc = nextToken(pDecoder)) != SML_ERR_OK)) {
+ smlLibFree(pElem);
+ return rc;
+ }
+
+ while (pScanner->curtok->type != TOK_TAG_END) {
+ switch (pScanner->curtok->tagid) {
+ /* PCDATA elements */
+ case TN_DEVINF_SHAREDMEM:
+ // %%% luz:2003-04-28: made work as a flag
+ pElem->flags |= SmlDevInfSharedMem_f;
+ rc = buildEmptyTag(pDecoder); // allow for <tag></tag> instead of <tag/>
+ break;
+ case TN_DEVINF_MAXMEM:
+ rc = buildPCData(pDecoder, (VoidPtr_t)&pElem->maxmem);
+ break;
+ case TN_DEVINF_MAXID:
+ rc = buildPCData(pDecoder, (VoidPtr_t)&pElem->maxid);
+ break;
+ default:
+ rc = SML_DECODEERROR(SML_ERR_XLT_INVAL_SYNCML_DOC,pScanner,"buildDevInfDSMemCmd");
+ }
+ if (rc != SML_ERR_OK) {
+ smlLibFree(pElem);
+ return rc;
+ }
+ if (((rc = nextToken(pDecoder)) != SML_ERR_OK)) {
+ smlLibFree(pElem);
+ return rc;
+ }
+ }
+ *ppElem = pElem;
+
+ return SML_ERR_OK;
+}
+
+Ret_t buildDevInfCTCapCmd(XltDecoderPtr_t pDecoder, VoidPtr_t *ppElem) {
+ XltDecScannerPtr_t pScanner;
+ SmlDevInfCTCapPtr_t pElem;
+ Ret_t rc;
+
+ pScanner = pDecoder->scanner;
+
+ if (*ppElem != NULL)
+ return SML_DECODEERROR(SML_ERR_XLT_INVAL_SYNCML_DOC,pScanner,"buildDevInfCTCapCmd");
+
+ if ((pElem = (SmlDevInfCTCapPtr_t)smlLibMalloc(sizeof(SmlDevInfCTCap_t))) == NULL)
+ return SML_ERR_NOT_ENOUGH_SPACE;
+ smlLibMemset(pElem, 0, sizeof(SmlDevInfCTCap_t));
+
+ if (IS_EMPTY(pScanner->curtok)) {
+ *ppElem = pElem;
+ return SML_ERR_OK;
+ }
+
+ if (((rc = nextToken(pDecoder)) != SML_ERR_OK)) {
+ smlLibFree(pElem);
+ return rc;
+ }
+
+ while (pScanner->curtok->type != TOK_TAG_END) {
+ switch (pScanner->curtok->tagid) {
+ case TN_DEVINF_CTTYPE:
+ rc = buildPCData(pDecoder, (VoidPtr_t)&pElem->cttype);
+ break;
+ default:
+ rc = SML_DECODEERROR(SML_ERR_XLT_INVAL_SYNCML_DOC,pScanner,"buildDevInfCTCapCmd");
+ }
+ if (rc != SML_ERR_OK) {
+ smlLibFree(pElem);
+ return rc;
+ }
+ if (((rc = nextToken(pDecoder)) != SML_ERR_OK)) {
+ smlLibFree(pElem);
+ return rc;
+ }
+ }
+ *ppElem = pElem;
+
+ return SML_ERR_OK;
+}
+
+Ret_t buildDevInfSyncCapCmd(XltDecoderPtr_t pDecoder, VoidPtr_t *ppElem) {
+ XltDecScannerPtr_t pScanner;
+ SmlDevInfSyncCapPtr_t pElem;
+ Ret_t rc;
+
+ pScanner = pDecoder->scanner;
+
+ if (*ppElem != NULL)
+ return SML_DECODEERROR(SML_ERR_XLT_INVAL_SYNCML_DOC,pScanner,"buildDevInfSyncCapCmd");
+
+ if ((pElem = (SmlDevInfSyncCapPtr_t)smlLibMalloc(sizeof(SmlDevInfSyncCap_t))) == NULL)
+ return SML_ERR_NOT_ENOUGH_SPACE;
+ smlLibMemset(pElem, 0, sizeof(SmlDevInfSyncCap_t));
+
+ if (IS_EMPTY(pScanner->curtok)) {
+ *ppElem = pElem;
+ return SML_ERR_OK;
+ }
+
+ if (((rc = nextToken(pDecoder)) != SML_ERR_OK)) {
+ smlLibFree(pElem);
+ return rc;
+ }
+
+ while (pScanner->curtok->type != TOK_TAG_END) {
+ switch (pScanner->curtok->tagid) {
+ case TN_DEVINF_SYNCTYPE:
+ rc = buildPCDataList(pDecoder, (VoidPtr_t)&pElem->synctype);
+ break;
+ default:
+ rc = SML_DECODEERROR(SML_ERR_XLT_INVAL_SYNCML_DOC,pScanner,"buildDevInfSyncCapCmd");
+ }
+ if (rc != SML_ERR_OK) {
+ smlLibFree(pElem);
+ return rc;
+ }
+ if (((rc = nextToken(pDecoder)) != SML_ERR_OK)) {
+ smlLibFree(pElem);
+ return rc;
+ }
+ }
+ *ppElem = pElem;
+
+ return SML_ERR_OK;
+}
+
+Ret_t buildDevInfExtCmd(XltDecoderPtr_t pDecoder, VoidPtr_t *ppElem) {
+ XltDecScannerPtr_t pScanner;
+ SmlDevInfExtPtr_t pElem;
+ Ret_t rc;
+
+ pScanner = pDecoder->scanner;
+
+ if (*ppElem != NULL)
+ return SML_DECODEERROR(SML_ERR_XLT_INVAL_SYNCML_DOC,pScanner,"buildDevInfExtCmd");
+
+ if ((pElem = (SmlDevInfExtPtr_t)smlLibMalloc(sizeof(SmlDevInfExt_t))) == NULL)
+ return SML_ERR_NOT_ENOUGH_SPACE;
+ smlLibMemset(pElem, 0, sizeof(SmlDevInfExt_t));
+
+ if (IS_EMPTY(pScanner->curtok)) {
+ *ppElem = pElem;
+ return SML_ERR_OK;
+ }
+
+ if (((rc = nextToken(pDecoder)) != SML_ERR_OK)) {
+ smlLibFree(pElem);
+ return rc;
+ }
+
+ while (pScanner->curtok->type != TOK_TAG_END) {
+ switch (pScanner->curtok->tagid) {
+ case TN_DEVINF_XNAM:
+ rc = buildPCData(pDecoder, (VoidPtr_t)&pElem->xnam);
+ break;
+ case TN_DEVINF_XVAL:
+ rc = buildPCDataList(pDecoder, (VoidPtr_t)&pElem->xval);
+ break;
+ default:
+ rc = SML_DECODEERROR(SML_ERR_XLT_INVAL_SYNCML_DOC,pScanner,"buildDevInfExtCmd");
+ }
+ if (rc != SML_ERR_OK) {
+ smlLibFree(pElem);
+ return rc;
+ }
+ if (((rc = nextToken(pDecoder)) != SML_ERR_OK)) {
+ smlLibFree(pElem);
+ return rc;
+ }
+ }
+ *ppElem = pElem;
+
+ return SML_ERR_OK;
+}
+
+
+/* see xltenc.c:XltEncBlock for description of parameters */
+Ret_t devinfEncBlock(XltTagID_t tagId, XltRO_t reqOptFlag, const VoidPtr_t pContent, SmlEncoding_t enc, BufferMgmtPtr_t pBufMgr, SmlPcdataExtension_t attFlag) {
+ //Return variable
+ Ret_t _err;
+ SmlPcdataListPtr_t pList = NULL;
+ SmlDevInfDatastoreListPtr_t dsList = NULL;
+ SmlDevInfCtcapListPtr_t ctList = NULL;
+ SmlDevInfExtListPtr_t exList = NULL;
+ SmlDevInfXmitListPtr_t xmList = NULL;
+ SmlDevInfFilterCapListPtr_t fcapList = NULL;
+ SmlDevInfCTDataPropListPtr_t propList = NULL;
+ SmlDevInfCTDataListPtr_t paramList = NULL;
+ XltTagID_t nameTagID = TN_UNDEF;
+ SmlDevInfCTDataPtr_t pPropOrParam = NULL;
+
+
+ //Check if pContent of a required field is missing
+ if ((reqOptFlag == REQUIRED) && (pContent == NULL))
+ return SML_ERR_XLT_MISSING_CONT;
+ //Check if pContent of a optional field is missing -> if yes we are done
+ else if (pContent == NULL)
+ return SML_ERR_OK;
+
+ //Generate the commands -> see DTD
+ switch (tagId) {
+ case TN_DEVINF_EXT:
+ if ((_err = xltGenerateTag(TN_DEVINF_EXT, TT_BEG, enc, pBufMgr, SML_EXT_DEVINF)) != SML_ERR_OK) return _err;
+ if ((_err = devinfEncBlock(TN_DEVINF_XNAM, REQUIRED, ((SmlDevInfExtPtr_t) pContent)->xnam, enc, pBufMgr, SML_EXT_DEVINF)) != SML_ERR_OK) return _err;
+ pList = ((SmlDevInfExtPtr_t)pContent)->xval;
+ while (pList != NULL) {
+ if ((_err = devinfEncBlock(TN_DEVINF_XVAL, OPTIONAL, pList->data, enc, pBufMgr, SML_EXT_DEVINF)) != SML_ERR_OK) return _err;
+ pList = pList->next;
+ };
+ if ((_err = xltGenerateTag(TN_DEVINF_EXT, TT_END, enc, pBufMgr, SML_EXT_DEVINF)) != SML_ERR_OK) return _err;
+ break;
+ case TN_DEVINF_SYNCCAP:
+ if ((_err = xltGenerateTag(TN_DEVINF_SYNCCAP, TT_BEG, enc, pBufMgr, SML_EXT_DEVINF)) != SML_ERR_OK) return _err;
+ pList = ((SmlDevInfSyncCapPtr_t)pContent)->synctype;
+ while (pList != NULL) {
+ if ((_err = devinfEncBlock(TN_DEVINF_SYNCTYPE, OPTIONAL, pList->data, enc, pBufMgr, SML_EXT_DEVINF)) != SML_ERR_OK) return _err;
+ pList = pList->next;
+ };
+ if ((_err = xltGenerateTag(TN_DEVINF_SYNCCAP, TT_END, enc, pBufMgr, SML_EXT_DEVINF)) != SML_ERR_OK) return _err;
+ break;
+ case TN_DEVINF_SHAREDMEM:
+ //set the flag in the (WB)XML document if the flag is in the pContent
+ if ((*((Flag_t *) pContent)) & (SmlDevInfSharedMem_f))
+ if ((_err = xltGenerateTag(tagId, TT_ALL, enc, pBufMgr, SML_EXT_DEVINF)) != SML_ERR_OK) return _err;
+ break;
+ // %%% luz:2003-04-28 added missing 1.1 devinf tags here
+ case TN_DEVINF_UTC:
+ //set the flag in the (WB)XML document if the flag is in the pContent
+ if ((*((Flag_t *) pContent)) & (SmlDevInfUTC_f)) {
+ if ((_err = xltGenerateTag(tagId, TT_ALL, enc, pBufMgr, SML_EXT_DEVINF)) != SML_ERR_OK) return _err;
+ }
+ break;
+ case TN_DEVINF_NOFM:
+ //set the flag in the (WB)XML document if the flag is in the pContent
+ if ((*((Flag_t *) pContent)) & (SmlDevInfNOfM_f)) {
+ if ((_err = xltGenerateTag(tagId, TT_ALL, enc, pBufMgr, SML_EXT_DEVINF)) != SML_ERR_OK) return _err;
+ }
+ break;
+ case TN_DEVINF_LARGEOBJECT:
+ //set the flag in the (WB)XML document if the flag is in the pContent
+ if ((*((Flag_t *) pContent)) & (SmlDevInfLargeObject_f)) {
+ if ((_err = xltGenerateTag(tagId, TT_ALL, enc, pBufMgr, SML_EXT_DEVINF)) != SML_ERR_OK) return _err;
+ }
+ break;
+
+ /* SyncML DS 1.2, Synthesis/luz 2005-08-24 */
+ case TN_DEVINF_FIELDLEVEL:
+ //set the flag in the (WB)XML document if the flag is in the pContent
+ if ((*((Flag_t *) pContent)) & (SmlDevInfFieldLevel_f)) {
+ if ((_err = xltGenerateTag(tagId, TT_ALL, enc, pBufMgr, SML_EXT_DEVINF)) != SML_ERR_OK) return _err;
+ }
+ break;
+ case TN_DEVINF_HIERARCHICAL:
+ //set the flag in the (WB)XML document if the flag is in the pContent
+ if ((*((Flag_t *) pContent)) & (SmlDevInfHierarchical_f)) {
+ if ((_err = xltGenerateTag(tagId, TT_ALL, enc, pBufMgr, SML_EXT_DEVINF)) != SML_ERR_OK) return _err;
+ }
+ break;
+ case TN_DEVINF_NOTRUNCATE:
+ //set the flag in the (WB)XML document if the flag is in the pContent
+ if ((*((Flag_t *) pContent)) & (SmlDevInfNoTruncate_f)) {
+ if ((_err = xltGenerateTag(tagId, TT_ALL, enc, pBufMgr, SML_EXT_DEVINF)) != SML_ERR_OK) return _err;
+ }
+ break;
+
+ case TN_DEVINF_PROPERTY:
+ // pContent is SmlDevInfCTDataPropPtr_t
+ paramList = ((SmlDevInfCTDataPropPtr_t)pContent)->param; // get param list
+ // modify pContent
+ pPropOrParam = ((SmlDevInfCTDataPropPtr_t)pContent)->prop; // common property/propparam description record
+ // set the tag to be used for name
+ nameTagID=TN_DEVINF_PROPNAME; // is a property, use <PropName>
+ goto param_or_property;
+ case TN_DEVINF_PROPPARAM:
+ // pContent is SmlDevInfCTDataPtr_t (common data structure for <Property> and <PropParam>)
+ paramList=NULL;
+ // set the tag to be used for name
+ nameTagID=TN_DEVINF_PARAMNAME; // is a propparam, use <ParamName>
+ // set content
+ pPropOrParam = (SmlDevInfCTDataPtr_t)pContent;
+ goto param_or_property;
+ param_or_property:
+ // - pPropOrParam is SmlDevInfCTDataPtr_t (common data structure for <Property> and <PropParam>)
+ // - paramList is either NULL (for params, which do not have params themselves) or SmlDevInfCTDataListPtr_t (for properties)
+ // DS 1.2 or higher : open <Property>/<PropParam> to properly group all property related elements
+ if (pBufMgr->vers>=SML_VERS_1_2) {
+ if ((_err = xltGenerateTag(tagId, TT_BEG, enc, pBufMgr, SML_EXT_DEVINF)) != SML_ERR_OK) return _err;
+ }
+ /* -- Propname or ParamName */
+ if ((_err = devinfEncBlock(nameTagID, REQUIRED, pPropOrParam->name, enc, pBufMgr, SML_EXT_DEVINF)) != SML_ERR_OK) return _err;
+ /* -- (ValEnum+ | (Datatype, Size?))? */
+ //%%% luz 2007-08-14 empty datatype is allowed as well (as this is what e.g. Nokia E90 sends, and when using SML to translate back to XML, we'd failed here before)
+ if (pPropOrParam->valenum != NULL && pPropOrParam->datatype != NULL && pPropOrParam->datatype->length!=0)
+ return SML_ERR_XLT_INVAL_INPUT_DATA;
+ if (pPropOrParam->valenum != NULL) {
+ // ValEnum+
+ pList = pPropOrParam->valenum;
+ while (pList != NULL) {
+ if ((_err = devinfEncBlock(TN_DEVINF_VALENUM, REQUIRED, pList->data, enc, pBufMgr, SML_EXT_DEVINF)) != SML_ERR_OK) return _err;
+ pList = pList->next;
+ };
+ } else if (pPropOrParam->datatype != NULL) {
+ // Datatype?
+ if ((_err = devinfEncBlock(TN_DEVINF_DATATYPE, REQUIRED, pPropOrParam->datatype, enc, pBufMgr, SML_EXT_DEVINF)) != SML_ERR_OK) return _err;
+ }
+ /* (Max)Size? (note, in <1.2, the tag was called "Size", in >=1.2, the tag is called "MaxSize" - but both have the same WBXML token) */
+ if ((_err = devinfEncBlock(pBufMgr->vers>=SML_VERS_1_2 ? TN_DEVINF_MAXSIZE : TN_DEVINF_SIZE, OPTIONAL, pPropOrParam->maxsize, enc, pBufMgr, SML_EXT_DEVINF)) != SML_ERR_OK) return _err;
+ /* -- DS 1.2: MaxOccur?, is valid for Properties only, not for PropParams */
+ if ((_err = devinfEncBlock(TN_DEVINF_MAXOCCUR, OPTIONAL, pPropOrParam->maxoccur, enc, pBufMgr, SML_EXT_DEVINF)) != SML_ERR_OK) return _err;
+ /* -- DS 1.2: (NoTruncate?), is valid for Properties only, not for PropParams */
+ if ((_err = devinfEncBlock(TN_DEVINF_NOTRUNCATE, OPTIONAL, &(pPropOrParam->flags), enc, pBufMgr, SML_EXT_DEVINF)) != SML_ERR_OK) return _err;
+
+ /* -- DisplayName ? */
+ if ((_err = devinfEncBlock(TN_DEVINF_DISPLAYNAME, OPTIONAL, pPropOrParam->dname, enc, pBufMgr, SML_EXT_DEVINF)) != SML_ERR_OK) return _err;
+
+ /* -- now the paramList */
+ while (paramList != NULL) {
+ /* Synthesis/luz 2005-08-24 : separated generation of <PropParam> element into own case */
+ if ((_err = devinfEncBlock(TN_DEVINF_PROPPARAM, REQUIRED, paramList->data, enc, pBufMgr, SML_EXT_DEVINF)) != SML_ERR_OK) return _err;
+ paramList = paramList->next;
+ }
+ // DS 1.2 or higher : close </Property>/</PropParam> encapsulation
+ if (pBufMgr->vers>=SML_VERS_1_2) {
+ if ((_err = xltGenerateTag(tagId, TT_END, enc, pBufMgr, SML_EXT_DEVINF)) != SML_ERR_OK) return _err;
+ }
+ break;
+
+ case TN_DEVINF_CTCAP:
+ ctList = ((SmlDevInfCtcapListPtr_t)pContent);
+ if (ctList == NULL)
+ break; // empty CTCap - is ok, do not output anything
+ // before DS 1.2 : all types are encapsulated in a single CTCap
+ if (pBufMgr->vers<SML_VERS_1_2) {
+ if ((_err = xltGenerateTag(TN_DEVINF_CTCAP, TT_BEG, enc, pBufMgr, SML_EXT_DEVINF)) != SML_ERR_OK) return _err;
+ }
+ while (ctList != NULL) {
+ if (ctList->data == NULL) return SML_ERR_XLT_INVAL_INPUT_DATA;
+ // DS 1.2 or higher : every content type is separately enclosed in a CTCap
+ if (pBufMgr->vers>=SML_VERS_1_2) {
+ if ((_err = xltGenerateTag(TN_DEVINF_CTCAP, TT_BEG, enc, pBufMgr, SML_EXT_DEVINF)) != SML_ERR_OK) return _err;
+ }
+ if ((_err = devinfEncBlock(TN_DEVINF_CTTYPE, REQUIRED, ctList->data->cttype, enc, pBufMgr, SML_EXT_DEVINF)) != SML_ERR_OK) return _err;
+ /* SyncML DS 1.2, Synthesis/luz 2005-09-28, required for DS 1.2 only, therefore OPTIONAL here */
+ if ((_err = devinfEncBlock(TN_DEVINF_VERCT, OPTIONAL, ctList->data->verct, enc, pBufMgr, SML_EXT_DEVINF)) != SML_ERR_OK) return _err;
+ /* SyncML DS 1.2, Synthesis/luz 2005-08-24 */
+ if ((_err = devinfEncBlock(TN_DEVINF_FIELDLEVEL, OPTIONAL, &(ctList->data->flags), enc, pBufMgr, SML_EXT_DEVINF)) != SML_ERR_OK) return _err;
+
+ /* now the propList */
+ // %%% luz 2002-11-27: made property list optional (e.g. text/message of P800 has none)
+ propList = ctList->data->prop;
+ // %%% original: if (propList == NULL) return SML_ERR_XLT_INVAL_INPUT_DATA;
+ while (propList != NULL) {
+ if (propList->data == NULL) return SML_ERR_XLT_INVAL_INPUT_DATA;
+ if (propList->data->prop == NULL) return SML_ERR_XLT_INVAL_INPUT_DATA;
+ /* Synthesis/luz 2005-08-24 : separated generation of <property> element into own case */
+ if ((_err = devinfEncBlock(TN_DEVINF_PROPERTY, REQUIRED, propList->data, enc, pBufMgr, SML_EXT_DEVINF)) != SML_ERR_OK) return _err;
+ propList = propList->next;
+ }
+ // DS 1.2 or higher : every content type is separately enclosed in a CTCap
+ if (pBufMgr->vers>=SML_VERS_1_2) {
+ if ((_err = xltGenerateTag(TN_DEVINF_CTCAP, TT_END, enc, pBufMgr, SML_EXT_DEVINF)) != SML_ERR_OK) return _err;
+ }
+ /* eof propList */
+ ctList = ctList->next;
+ };
+ // before DS 1.2 : all types are encapsulated in a single CTCap
+ if (pBufMgr->vers<SML_VERS_1_2) {
+ if ((_err = xltGenerateTag(TN_DEVINF_CTCAP, TT_END, enc, pBufMgr, SML_EXT_DEVINF)) != SML_ERR_OK) return _err;
+ }
+ break;
+
+ case TN_DEVINF_DSMEM:
+ if ((_err = xltGenerateTag(TN_DEVINF_DSMEM, TT_BEG, enc, pBufMgr, SML_EXT_DEVINF)) != SML_ERR_OK) return _err;
+ if ((_err = devinfEncBlock(TN_DEVINF_SHAREDMEM, OPTIONAL, &(((SmlDevInfDSMemPtr_t) pContent)->flags), enc, pBufMgr, SML_EXT_DEVINF)) != SML_ERR_OK) return _err;
+ if ((_err = devinfEncBlock(TN_DEVINF_MAXMEM, OPTIONAL, ((SmlDevInfDSMemPtr_t) pContent)->maxmem, enc, pBufMgr, SML_EXT_DEVINF)) != SML_ERR_OK) return _err;
+ if ((_err = devinfEncBlock(TN_DEVINF_MAXID, OPTIONAL, ((SmlDevInfDSMemPtr_t) pContent)->maxid, enc, pBufMgr, SML_EXT_DEVINF)) != SML_ERR_OK) return _err;
+ if ((_err = xltGenerateTag(TN_DEVINF_DSMEM, TT_END, enc, pBufMgr, SML_EXT_DEVINF)) != SML_ERR_OK) return _err;
+ break;
+
+ // special case, the following 5 have the same structure, only the tag name differs
+ case TN_DEVINF_RX:
+ case TN_DEVINF_TX:
+ case TN_DEVINF_RXPREF:
+ case TN_DEVINF_TXPREF:
+ case TN_DEVINF_FILTERRX: /* SyncML DS 1.2, Synthesis/luz 2005-08-24 */
+ if ((_err = xltGenerateTag(tagId, TT_BEG, enc, pBufMgr, SML_EXT_DEVINF)) != SML_ERR_OK) return _err;
+ if ((_err = devinfEncBlock(TN_DEVINF_CTTYPE, REQUIRED, ((SmlDevInfXmitPtr_t) pContent)->cttype, enc, pBufMgr, SML_EXT_DEVINF)) != SML_ERR_OK) return _err;
+ if ((_err = devinfEncBlock(TN_DEVINF_VERCT, REQUIRED, ((SmlDevInfXmitPtr_t) pContent)->verct, enc, pBufMgr, SML_EXT_DEVINF)) != SML_ERR_OK) return _err;
+ if ((_err = xltGenerateTag(tagId, TT_END, enc, pBufMgr, SML_EXT_DEVINF)) != SML_ERR_OK) return _err;
+ break;
+
+ /* SyncML DS 1.2, Synthesis/luz 2005-08-24 */
+ case TN_DEVINF_FILTERCAP:
+ if ((_err = xltGenerateTag(tagId, TT_BEG, enc, pBufMgr, SML_EXT_DEVINF)) != SML_ERR_OK) return _err;
+ if ((_err = devinfEncBlock(TN_DEVINF_CTTYPE, REQUIRED, ((SmlDevInfFilterCapPtr_t) pContent)->cttype, enc, pBufMgr, SML_EXT_DEVINF)) != SML_ERR_OK) return _err;
+ if ((_err = devinfEncBlock(TN_DEVINF_VERCT, REQUIRED, ((SmlDevInfFilterCapPtr_t) pContent)->verct, enc, pBufMgr, SML_EXT_DEVINF)) != SML_ERR_OK) return _err;
+ pList = ((SmlDevInfFilterCapPtr_t) pContent)->filterkeyword;
+ while (pList != NULL) {
+ if ((_err = devinfEncBlock(TN_DEVINF_FILTERKEYWORD, REQUIRED, pList->data, enc, pBufMgr, SML_EXT_DEVINF)) != SML_ERR_OK) return _err;
+ pList=pList->next;
+ }
+ pList = ((SmlDevInfFilterCapPtr_t) pContent)->propname;
+ while (pList != NULL) {
+ if ((_err = devinfEncBlock(TN_DEVINF_PROPNAME, REQUIRED, pList->data, enc, pBufMgr, SML_EXT_DEVINF)) != SML_ERR_OK) return _err;
+ pList=pList->next;
+ }
+ if ((_err = xltGenerateTag(tagId, TT_END, enc, pBufMgr, SML_EXT_DEVINF)) != SML_ERR_OK) return _err;
+ break;
+
+
+ case TN_DEVINF_DATASTORE:
+ if ((_err = xltGenerateTag(TN_DEVINF_DATASTORE, TT_BEG, enc, pBufMgr, SML_EXT_DEVINF)) != SML_ERR_OK) return _err;
+ if ((_err = devinfEncBlock(TN_DEVINF_SOURCEREF, REQUIRED, ((SmlDevInfDatastorePtr_t) pContent)->sourceref, enc, pBufMgr, SML_EXT_DEVINF)) != SML_ERR_OK) return _err;
+ if ((_err = devinfEncBlock(TN_DEVINF_DISPLAYNAME, OPTIONAL, ((SmlDevInfDatastorePtr_t) pContent)->displayname, enc, pBufMgr, SML_EXT_DEVINF)) != SML_ERR_OK) return _err;
+ if ((_err = devinfEncBlock(TN_DEVINF_MAXGUIDSIZE, OPTIONAL, ((SmlDevInfDatastorePtr_t) pContent)->maxguidsize, enc, pBufMgr, SML_EXT_DEVINF)) != SML_ERR_OK) return _err;
+ if ((_err = devinfEncBlock(TN_DEVINF_RXPREF, REQUIRED, ((SmlDevInfDatastorePtr_t) pContent)->rxpref, enc, pBufMgr, SML_EXT_DEVINF)) != SML_ERR_OK) return _err;
+ xmList = ((SmlDevInfDatastorePtr_t)pContent)->rx;
+ while (xmList != NULL) {
+ if ((_err = devinfEncBlock(TN_DEVINF_RX, OPTIONAL, xmList->data, enc, pBufMgr, SML_EXT_DEVINF)) != SML_ERR_OK) return _err;
+ xmList = xmList->next;
+ };
+ if ((_err = devinfEncBlock(TN_DEVINF_TXPREF, REQUIRED, ((SmlDevInfDatastorePtr_t) pContent)->txpref, enc, pBufMgr, SML_EXT_DEVINF)) != SML_ERR_OK) return _err;
+ xmList = ((SmlDevInfDatastorePtr_t)pContent)->tx;
+ while (xmList != NULL) {
+ if ((_err = devinfEncBlock(TN_DEVINF_TX, OPTIONAL, xmList->data, enc, pBufMgr, SML_EXT_DEVINF)) != SML_ERR_OK) return _err;
+ xmList = xmList->next;
+ };
+ /* For SyncML DS 1.2, CTCaps are local to datastore, no longer global in DevInf */
+ if ((_err = devinfEncBlock(TN_DEVINF_CTCAP, OPTIONAL, ((SmlDevInfDatastorePtr_t) pContent)->ctcap, enc, pBufMgr, SML_EXT_DEVINF)) != SML_ERR_OK) return _err;
+
+ if ((_err = devinfEncBlock(TN_DEVINF_DSMEM, OPTIONAL, ((SmlDevInfDatastorePtr_t) pContent)->dsmem, enc, pBufMgr, SML_EXT_DEVINF)) != SML_ERR_OK) return _err;
+ /* SyncML DS 1.2, Synthesis/luz 2005-08-24 */
+ if ((_err = devinfEncBlock(TN_DEVINF_HIERARCHICAL, OPTIONAL, &(((SmlDevInfDatastorePtr_t) pContent)->flags), enc, pBufMgr, SML_EXT_DEVINF)) != SML_ERR_OK) return _err;
+
+ if ((_err = devinfEncBlock(TN_DEVINF_SYNCCAP, REQUIRED, ((SmlDevInfDatastorePtr_t) pContent)->synccap, enc, pBufMgr, SML_EXT_DEVINF)) != SML_ERR_OK) return _err;
+
+ /* SyncML DS 1.2, Synthesis/luz 2005-08-24 */
+ xmList = ((SmlDevInfDatastorePtr_t)pContent)->filterrx;
+ while (xmList != NULL) {
+ if ((_err = devinfEncBlock(TN_DEVINF_FILTERRX, OPTIONAL, xmList->data, enc, pBufMgr, SML_EXT_DEVINF)) != SML_ERR_OK) return _err;
+ xmList = xmList->next;
+ };
+ /* SyncML DS 1.2, Synthesis/luz 2005-08-25 */
+ fcapList = ((SmlDevInfDatastorePtr_t)pContent)->filtercap;
+ while (fcapList != NULL) {
+ if ((_err = devinfEncBlock(TN_DEVINF_FILTERCAP, OPTIONAL, fcapList->data, enc, pBufMgr, SML_EXT_DEVINF)) != SML_ERR_OK) return _err;
+ fcapList = fcapList->next;
+ };
+
+ if ((_err = xltGenerateTag(TN_DEVINF_DATASTORE, TT_END, enc, pBufMgr, SML_EXT_DEVINF)) != SML_ERR_OK) return _err;
+ break;
+
+ case TN_DEVINF_DEVINF:
+ if ((_err = xltGenerateTag(TN_DEVINF_DEVINF, TT_BEG, enc, pBufMgr, SML_EXT_DEVINF)) != SML_ERR_OK) return _err;
+ if ((_err = devinfEncBlock(TN_DEVINF_VERDTD, REQUIRED, ((SmlDevInfDevInfPtr_t) pContent)->verdtd, enc, pBufMgr, SML_EXT_DEVINF)) != SML_ERR_OK) return _err;
+ if ((_err = devinfEncBlock(TN_DEVINF_MAN, OPTIONAL, ((SmlDevInfDevInfPtr_t) pContent)->man, enc, pBufMgr, SML_EXT_DEVINF)) != SML_ERR_OK) return _err;
+ if ((_err = devinfEncBlock(TN_DEVINF_MOD, OPTIONAL, ((SmlDevInfDevInfPtr_t) pContent)->mod, enc, pBufMgr, SML_EXT_DEVINF)) != SML_ERR_OK) return _err;
+ if ((_err = devinfEncBlock(TN_DEVINF_OEM, OPTIONAL, ((SmlDevInfDevInfPtr_t) pContent)->oem, enc, pBufMgr, SML_EXT_DEVINF)) != SML_ERR_OK) return _err;
+ if ((_err = devinfEncBlock(TN_DEVINF_FWV, OPTIONAL, ((SmlDevInfDevInfPtr_t) pContent)->fwv, enc, pBufMgr, SML_EXT_DEVINF)) != SML_ERR_OK) return _err;
+ if ((_err = devinfEncBlock(TN_DEVINF_SWV, OPTIONAL, ((SmlDevInfDevInfPtr_t) pContent)->swv, enc, pBufMgr, SML_EXT_DEVINF)) != SML_ERR_OK) return _err;
+ if ((_err = devinfEncBlock(TN_DEVINF_HWV, OPTIONAL, ((SmlDevInfDevInfPtr_t) pContent)->hwv, enc, pBufMgr, SML_EXT_DEVINF)) != SML_ERR_OK) return _err;
+ if ((_err = devinfEncBlock(TN_DEVINF_DEVID, REQUIRED, ((SmlDevInfDevInfPtr_t) pContent)->devid, enc, pBufMgr, SML_EXT_DEVINF)) != SML_ERR_OK) return _err;
+ if ((_err = devinfEncBlock(TN_DEVINF_DEVTYP, REQUIRED, ((SmlDevInfDevInfPtr_t) pContent)->devtyp, enc, pBufMgr, SML_EXT_DEVINF)) != SML_ERR_OK) return _err;
+ if ((_err = devinfEncBlock(TN_DEVINF_UTC, OPTIONAL, &(((SmlDevInfDevInfPtr_t) pContent)->flags), enc, pBufMgr, SML_EXT_DEVINF)) != SML_ERR_OK) return _err;
+ if ((_err = devinfEncBlock(TN_DEVINF_NOFM, OPTIONAL, &(((SmlDevInfDevInfPtr_t) pContent)->flags), enc, pBufMgr, SML_EXT_DEVINF)) != SML_ERR_OK) return _err;
+ if ((_err = devinfEncBlock(TN_DEVINF_LARGEOBJECT, OPTIONAL, &(((SmlDevInfDevInfPtr_t) pContent)->flags), enc, pBufMgr, SML_EXT_DEVINF)) != SML_ERR_OK) return _err;
+ dsList = ((SmlDevInfDevInfPtr_t)pContent)->datastore;
+ if (dsList == NULL) return SML_ERR_XLT_MISSING_CONT;
+ if ((_err = devinfEncBlock(TN_DEVINF_DATASTORE, REQUIRED, dsList->data, enc, pBufMgr, SML_EXT_DEVINF)) != SML_ERR_OK) return _err;
+ dsList = dsList->next;
+ while (dsList != NULL) {
+ if ((_err = devinfEncBlock(TN_DEVINF_DATASTORE, OPTIONAL, dsList->data, enc, pBufMgr, SML_EXT_DEVINF)) != SML_ERR_OK) return _err;
+ dsList = dsList->next;
+ };
+ // for pre DS 1.2, CTCaps are global (i.e at the devInf level):
+ if ((_err = devinfEncBlock(TN_DEVINF_CTCAP, OPTIONAL, ((SmlDevInfDevInfPtr_t)pContent)->ctcap, enc, pBufMgr, SML_EXT_DEVINF)) != SML_ERR_OK) return _err;
+ // extensions
+ exList = ((SmlDevInfDevInfPtr_t)pContent)->ext;
+ while (exList != NULL) {
+ if ((_err = devinfEncBlock(TN_DEVINF_EXT, OPTIONAL, exList->data, enc, pBufMgr, SML_EXT_DEVINF)) != SML_ERR_OK) return _err;
+ exList = exList->next;
+ };
+
+ if ((_err = xltGenerateTag(TN_DEVINF_DEVINF, TT_END, enc, pBufMgr, SML_EXT_DEVINF)) != SML_ERR_OK) return _err;
+ break;
+
+ default: { // all leaf nodes (PCDATA#)
+ return xltEncPcdata(tagId, reqOptFlag, pContent, enc, pBufMgr, attFlag);
+ } //* eof default statement from switch tagid
+ } // eof switch tagid
+ return SML_ERR_OK;
+}
+#endif /* __USE_DEVINF__ */
diff --git a/src/syncml_tk/src/sml/xlt/all/xltdevinf.h b/src/syncml_tk/src/sml/xlt/all/xltdevinf.h
new file mode 100755
index 0000000..935379d
--- /dev/null
+++ b/src/syncml_tk/src/sml/xlt/all/xltdevinf.h
@@ -0,0 +1,75 @@
+/**
+ * @file
+ * Definition of DevInf DTD prototypefunctions for the en-/decoder
+ *
+ * @target_system all
+ * @target_os all
+ */
+
+/*
+ * Copyright Notice
+ * Copyright (c) Ericsson, IBM, Lotus, Matsushita Communication
+ * Industrial Co., Ltd., Motorola, Nokia, Openwave Systems, Inc.,
+ * Palm, Inc., Psion, Starfish Software, Symbian, Ltd. (2001).
+ * All Rights Reserved.
+ * Implementation of all or part of any Specification may require
+ * licenses under third party intellectual property rights,
+ * including without limitation, patent rights (such a third party
+ * may or may not be a Supporter). The Sponsors of the Specification
+ * are not responsible and shall not be held responsible in any
+ * manner for identifying or failing to identify any or all such
+ * third party intellectual property rights.
+ *
+ * THIS DOCUMENT AND THE INFORMATION CONTAINED HEREIN ARE PROVIDED
+ * ON AN "AS IS" BASIS WITHOUT WARRANTY OF ANY KIND AND ERICSSON, IBM,
+ * LOTUS, MATSUSHITA COMMUNICATION INDUSTRIAL CO. LTD, MOTOROLA,
+ * NOKIA, PALM INC., PSION, STARFISH SOFTWARE AND ALL OTHER SYNCML
+ * SPONSORS DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+ * BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
+ * HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
+ * SHALL ERICSSON, IBM, LOTUS, MATSUSHITA COMMUNICATION INDUSTRIAL CO.,
+ * LTD, MOTOROLA, NOKIA, PALM INC., PSION, STARFISH SOFTWARE OR ANY
+ * OTHER SYNCML SPONSOR BE LIABLE TO ANY PARTY FOR ANY LOSS OF
+ * PROFITS, LOSS OF BUSINESS, LOSS OF USE OF DATA, INTERRUPTION OF
+ * BUSINESS, OR FOR DIRECT, INDIRECT, SPECIAL OR EXEMPLARY, INCIDENTAL,
+ * PUNITIVE OR CONSEQUENTIAL DAMAGES OF ANY KIND IN CONNECTION WITH
+ * THIS DOCUMENT OR THE INFORMATION CONTAINED HEREIN, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH LOSS OR DAMAGE.
+ *
+ * The above notice and this paragraph must be included on all copies
+ * of this document that are made.
+ *
+ */
+
+
+#ifndef _XLT_DEVINF_H
+#define _XLT_DEVINF_H
+
+/* process only if we really use DevInf DTD */
+#ifdef __USE_DEVINF__
+
+/*************************************************************************/
+/* Definitions */
+/*************************************************************************/
+
+#include "xlttagtbl.h"
+#include "xltenc.h"
+
+Ret_t buildDevInfDevInfCmd(XltDecoderPtr_t pDecoder, VoidPtr_t *ppElem);
+Ret_t buildDevInfDataStoreCmd(XltDecoderPtr_t pDecoder, VoidPtr_t *ppElem);
+Ret_t buildDevInfXmitCmd(XltDecoderPtr_t pDecoder, VoidPtr_t *ppElem);
+Ret_t buildDevInfXmitList(XltDecoderPtr_t pDecoder, VoidPtr_t *ppElem);
+Ret_t buildDevInfDSMemCmd(XltDecoderPtr_t pDecoder, VoidPtr_t *ppElem);
+Ret_t buildDevInfCTCapCmd(XltDecoderPtr_t pDecoder, VoidPtr_t *ppElem);
+Ret_t buildDevInfSyncCapCmd(XltDecoderPtr_t pDecoder, VoidPtr_t *ppElem);
+Ret_t buildDevInfExtCmd(XltDecoderPtr_t pDecoder, VoidPtr_t *ppElem);
+Ret_t buildDevInfDataStoreList(XltDecoderPtr_t pDecoder, VoidPtr_t *ppElem);
+Ret_t buildDevInfExtList(XltDecoderPtr_t pDecoder, VoidPtr_t *ppElem);
+Ret_t buildDevInfCTCapList(XltDecoderPtr_t pDecoder, VoidPtr_t *ppElem);
+
+
+Ret_t devinfEncBlock(XltTagID_t tagId, XltRO_t reqOptFlag, const VoidPtr_t pContent, SmlEncoding_t enc, BufferMgmtPtr_t pBufMgr, SmlPcdataExtension_t attFlag);
+#endif /* __USE_DEVINF__ */
+#endif /* _XLT_DEVINF_H */
+
diff --git a/src/syncml_tk/src/sml/xlt/all/xltenc.c b/src/syncml_tk/src/sml/xlt/all/xltenc.c
new file mode 100755
index 0000000..cdbfb88
--- /dev/null
+++ b/src/syncml_tk/src/sml/xlt/all/xltenc.c
@@ -0,0 +1,1565 @@
+/**
+ * @file
+ * Encoder source file
+ *
+ * @target_system All
+ * @target_os All
+ */
+
+/*
+ * Copyright Notice
+ * Copyright (c) Ericsson, IBM, Lotus, Matsushita Communication
+ * Industrial Co., Ltd., Motorola, Nokia, Openwave Systems, Inc.,
+ * Palm, Inc., Psion, Starfish Software, Symbian, Ltd. (2001).
+ * All Rights Reserved.
+ * Implementation of all or part of any Specification may require
+ * licenses under third party intellectual property rights,
+ * including without limitation, patent rights (such a third party
+ * may or may not be a Supporter). The Sponsors of the Specification
+ * are not responsible and shall not be held responsible in any
+ * manner for identifying or failing to identify any or all such
+ * third party intellectual property rights.
+ *
+ * THIS DOCUMENT AND THE INFORMATION CONTAINED HEREIN ARE PROVIDED
+ * ON AN "AS IS" BASIS WITHOUT WARRANTY OF ANY KIND AND ERICSSON, IBM,
+ * LOTUS, MATSUSHITA COMMUNICATION INDUSTRIAL CO. LTD, MOTOROLA,
+ * NOKIA, PALM INC., PSION, STARFISH SOFTWARE AND ALL OTHER SYNCML
+ * SPONSORS DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+ * BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
+ * HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
+ * SHALL ERICSSON, IBM, LOTUS, MATSUSHITA COMMUNICATION INDUSTRIAL CO.,
+ * LTD, MOTOROLA, NOKIA, PALM INC., PSION, STARFISH SOFTWARE OR ANY
+ * OTHER SYNCML SPONSOR BE LIABLE TO ANY PARTY FOR ANY LOSS OF
+ * PROFITS, LOSS OF BUSINESS, LOSS OF USE OF DATA, INTERRUPTION OF
+ * BUSINESS, OR FOR DIRECT, INDIRECT, SPECIAL OR EXEMPLARY, INCIDENTAL,
+ * PUNITIVE OR CONSEQUENTIAL DAMAGES OF ANY KIND IN CONNECTION WITH
+ * THIS DOCUMENT OR THE INFORMATION CONTAINED HEREIN, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH LOSS OR DAMAGE.
+ *
+ * The above notice and this paragraph must be included on all copies
+ * of this document that are made.
+ *
+ */
+
+/*************************************************************************/
+/* Definitions */
+/*************************************************************************/
+#include "syncml_tk_prefix_file.h" // %%% luz: needed for precompiled headers in eVC++
+
+#include <xltenc.h>
+#ifdef __SML_WBXML__
+ #include "xltencwbxml.h"
+#endif
+#ifdef __SML_XML__
+ #include "xltencxml.h"
+#endif
+#include "xlttags.h"
+#include "xltmetinf.h"
+#include "xltdevinf.h"
+#include "smlmetinfdtd.h"
+#include "smldevinfdtd.h"
+#include <libstr.h>
+#include <libmem.h>
+#include <smldtd.h>
+
+Ret_t getTNbyPE(SmlProtoElement_t, XltTagID_t*);
+Ret_t xltEncBlock(XltTagID_t, XltRO_t, const VoidPtr_t, SmlEncoding_t, BufferMgmtPtr_t, SmlPcdataExtension_t);
+Ret_t xltEncList(XltListType_t, XltRO_t, VoidPtr_t, SmlEncoding_t, BufferMgmtPtr_t, SmlPcdataExtension_t);
+#ifndef __SML_LITE__
+Ret_t xltBuildExtention(SmlPcdataExtension_t, XltRO_t, VoidPtr_t, SmlEncoding_t, BufferMgmtPtr_t);
+#endif
+
+typedef struct PEEnc_s
+{
+ XltTagID_t tagid;
+ SmlProtoElement_t type;
+} PEEnc_t, *PEEncPtr_t;
+
+/* luz 2005-08-17: simplified, removed unnecessary copying of the const static table */
+
+static const PEEnc_t cPE_Enc[] =
+ {
+ { TN_ADD, SML_PE_ADD },
+ { TN_ALERT, SML_PE_ALERT },
+ { TN_ATOMIC, SML_PE_ATOMIC_START },
+ { TN_ATOMIC_END, SML_PE_ATOMIC_END },
+ { TN_COPY, SML_PE_COPY },
+ { TN_DELETE, SML_PE_DELETE },
+ { TN_EXEC, SML_PE_EXEC },
+ { TN_GET, SML_PE_GET },
+ { TN_MAP, SML_PE_MAP },
+ { TN_PUT, SML_PE_PUT },
+ { TN_RESULTS, SML_PE_RESULTS },
+ { TN_SEARCH, SML_PE_SEARCH },
+ { TN_SEQUENCE, SML_PE_SEQUENCE_START },
+ { TN_SEQUENCE_END, SML_PE_SEQUENCE_END },
+ { TN_STATUS, SML_PE_STATUS },
+ { TN_SYNC, SML_PE_SYNC_START },
+ { TN_SYNC_END, SML_PE_SYNC_END },
+ { TN_REPLACE, SML_PE_REPLACE },
+ { TN_UNDEF, SML_PE_UNDEF }
+ };
+
+
+Ret_t getTNbyPE(SmlProtoElement_t pE, XltTagID_t *tagID)
+{
+ int i = 0;
+ while ((cPE_Enc[i].type) != SML_PE_UNDEF)
+ {
+ if ((cPE_Enc[i].type) == pE)
+ {
+ *tagID = cPE_Enc[i].tagid;
+ return SML_ERR_OK;
+ }
+ i++;
+ }
+ *tagID = TN_UNDEF;
+ return SML_ERR_XLT_INVAL_PROTO_ELEM;
+}
+
+
+// %%% luz:2003-07-31: added SyncML FPI (formal public identifier) tables
+const char * const SyncMLFPI[SML_NUM_VERS] = {
+ "???",
+ "-//SYNCML//DTD SyncML 1.0//EN",
+ "-//SYNCML//DTD SyncML 1.1//EN",
+ "-//SYNCML//DTD SyncML 1.2//EN"
+};
+const char * const SyncMLDevInfFPI[SML_NUM_VERS] = {
+ "???",
+ "-//SYNCML//DTD DevInf 1.0//EN",
+ "-//SYNCML//DTD DevInf 1.1//EN",
+ "-//SYNCML//DTD DevInf 1.2//EN"
+};
+const int SyncMLWBXMLPublicID[SML_NUM_VERS] = {
+ 0,
+ 0,
+ 0x0FD3,
+ 0x1201
+};
+const int SyncMLDevInfWBXMLPublicID[SML_NUM_VERS] = {
+ 0,
+ 0,
+ 0x0FD4, // according to Nokia E70
+ 0x1203 // according to Nokia E70
+};
+
+
+
+/**
+ * Initializes an XML buffer; Creates XML code for the SyncHdr
+ * and appends it to the buffer.
+ * Returns 0 if operation was successful.
+ *
+ * @pre no memory should be allocated for ppEncoder (should be NULL)
+ * pHeader has to contain a valid SyncHdr structure
+ * pBufEnd must point to the end of the (WB)XML buffer
+ * ppBufPos has to be initialized to the start point of the
+ * (WB)XML buffer.
+ * @post After the function call ppBufPos points to the
+ * first free byte in the buffer behind the (WB)XML document
+ * @param enc (IN)
+ * the encoding constant (SML_WBXML or SML_XML)
+ * @param pHeader (IN)
+ * the SyncML header structure
+ * @param pBufEnd (IN)
+ * pointer to the end of the buffer to write on
+ * @param ppBufPos (IN/OUT)
+ * current position of the bufferpointer
+ * @param ppEncoder (IN/OUT)
+ * the encoder object
+ * @param vers (IN)
+ * SyncML version
+ * @return shows error codes of function,\n
+ * 0, if OK\n
+ * Possible Error Codes:
+ * - SML_ERR_XLT_MISSING_CONT
+ * - SML_ERR_XLT_BUF_ERR
+ * - SML_ERR_XLT_INVAL_ELEM_TYPE
+ * - SML_ERR_XLT_INVAL_LIST_TYPE
+ * - SML_ERR_XLT_INVAL_TAG_TYPE
+ * - SML_ERR_XLT_ENC_UNK
+ * - SML_ERR_XLT_INVAL_PROTO_ELEM
+ */
+Ret_t xltEncInit(SmlEncoding_t enc,
+ const SmlSyncHdrPtr_t pHeader,
+ const MemPtr_t pBufEnd,
+ MemPtr_t *ppBufPos,
+ XltEncoderPtr_t *ppEncoder,
+ SmlVersion_t vers)
+{
+ // Return variable
+ Ret_t _err;
+
+ XltEncoderPtr_t _pEncoder;
+
+ //Structure containing buffer pointers, length and written bytes
+ BufferMgmtPtr_t _pBufMgr;
+
+#ifdef __SML_XML__
+ MemPtr_t _tmpStr;
+ MemPtr_t _xmlver = (MemPtr_t)XML_VERSION;
+ MemPtr_t _xmlenc = (MemPtr_t)XML_ENCODING;
+ MemByte_t _begpar = XML_BEGPAR;
+ MemByte_t _endpar = XML_ENDPAR;
+#endif
+
+#ifdef __SML_WBXML__
+ MemByte_t _wbxmlver = XLT_WBXMLVER;
+ MemByte_t _charset = XLT_CHARSET;
+ // check if we want to send numeric or textual FPI
+ MemByte_t _pubident1 = 0; // default to textual
+ MemByte_t _pubident2 = 0;
+ const char *_syncmldtd = SyncMLFPI[vers];
+ unsigned short fpi = SyncMLWBXMLPublicID[vers];
+ MemByte_t _stablen = smlLibStrlen(_syncmldtd); // %% not 0x1D constant!
+ if (fpi) {
+ // numeric FPI available
+ _pubident1 = ((fpi>>7) & 0x7F)+0x80; // upper 7 bit
+ _pubident2 = (fpi & 0x7F); // lower 7 bits
+ _stablen = 0; // no string table
+ }
+#endif
+
+ //MemByte_t _tmp = 0x00;CURRENTLY NOT USED
+
+ if ((_pEncoder = (XltEncoderPtr_t)smlLibMalloc(sizeof(XltEncoder_t))) == NULL) return SML_ERR_NOT_ENOUGH_SPACE;
+ if ((_pBufMgr = (BufferMgmtPtr_t)smlLibMalloc(sizeof(BufferMgmt_t))) == NULL) {
+ smlLibFree(_pEncoder);
+ return SML_ERR_NOT_ENOUGH_SPACE;
+ }
+
+ //set the encoding
+ _pEncoder->enc = enc;
+
+ // %%% luz:2003-07-31: added version
+ _pEncoder->vers = vers;
+
+ _pEncoder->cur_ext = (SmlPcdataExtension_t)SML_EXT_UNDEFINED;
+ _pEncoder->last_ext = (SmlPcdataExtension_t)SML_EXT_UNDEFINED;
+ _pEncoder->end_tag_size = 0;
+ _pEncoder->space_evaluation = NULL;
+
+
+ _pBufMgr->smlXltBufferP = *ppBufPos;
+ _pBufMgr->smlXltBufferLen = pBufEnd - *ppBufPos;
+ _pBufMgr->smlXltStoreBufP = _pBufMgr->smlXltBufferP;
+ _pBufMgr->smlXltWrittenBytes = 0;
+ _pBufMgr->smlCurExt = _pEncoder->cur_ext;
+ _pBufMgr->smlLastExt = _pEncoder->last_ext;
+ _pBufMgr->smlActiveExt = SML_EXT_UNDEFINED;
+ _pBufMgr->switchExtTag = TN_UNDEF;
+ _pBufMgr->spaceEvaluation = 0;
+ _pBufMgr->vers = vers;
+ _pBufMgr->endTagSize = 0;
+
+ switch (enc)
+ {
+
+#ifdef __SML_WBXML__
+ case SML_WBXML:
+ {
+
+ // Set the WBXML Header Values
+ // WBXML Version
+ if ((_err = wbxmlWriteTypeToBuffer((MemPtr_t)(&_wbxmlver), TAG, 1, _pBufMgr)) != SML_ERR_OK) break;
+ // Public Idetifier - default unknown
+ if ((_err = wbxmlWriteTypeToBuffer((MemPtr_t)(&_pubident1), TAG, 1, _pBufMgr)) != SML_ERR_OK) break;
+ if ((_err = wbxmlWriteTypeToBuffer((MemPtr_t)(&_pubident2), TAG, 1, _pBufMgr)) != SML_ERR_OK) break;
+ // Character set - not yet implemented
+ if ((_err = wbxmlWriteTypeToBuffer((MemPtr_t)(&_charset), TAG, 1, _pBufMgr)) != SML_ERR_OK) break;
+ // - String table length - only used for textual FPI
+ if ((_err = wbxmlWriteTypeToBuffer((MemPtr_t)(&_stablen), TAG, 1, _pBufMgr)) != SML_ERR_OK) break;
+ // FPI - %%% luz:2003-07-31: not constant any more, varies according to SyncML version
+ if (fpi==0) {
+ // textual FPI
+ // - string table consisting of FPI only
+ if ((_err = xltAddToBuffer((MemPtr_t) (_syncmldtd) , smlLibStrlen((String_t)_syncmldtd), _pBufMgr)) != SML_ERR_OK) break;
+ }
+ else {
+ // numeric FPI
+ // NOP - no string table
+ }
+
+ break;
+ }
+#endif
+
+#ifdef __SML_XML__
+ case SML_XML:
+ {
+
+ if ((_err = xltAddToBuffer((MemPtr_t)(&_begpar), 1, _pBufMgr)) != SML_ERR_OK) break;
+ _tmpStr = (MemPtr_t)"?xml version=\"";
+ if ((_err = xltAddToBuffer(_tmpStr, smlLibStrlen((String_t)_tmpStr), _pBufMgr)) != SML_ERR_OK) break;
+ _tmpStr = _xmlver;
+ if ((_err = xltAddToBuffer(_tmpStr, smlLibStrlen((String_t)_tmpStr), _pBufMgr)) != SML_ERR_OK) break;
+ _tmpStr = (MemPtr_t)"\" encoding=\"";
+ if ((_err = xltAddToBuffer(_tmpStr, smlLibStrlen((String_t)_tmpStr), _pBufMgr)) != SML_ERR_OK) break;
+ _tmpStr = _xmlenc;
+ if ((_err = xltAddToBuffer(_tmpStr, smlLibStrlen((String_t)_tmpStr), _pBufMgr)) != SML_ERR_OK) break;
+ _tmpStr = (MemPtr_t)"\"?";
+ if ((_err = xltAddToBuffer(_tmpStr, smlLibStrlen((String_t)_tmpStr), _pBufMgr)) != SML_ERR_OK) break;
+ if ((_err = xltAddToBuffer((MemPtr_t)(&_endpar), 1, _pBufMgr)) != SML_ERR_OK) break;
+
+ break;
+ }
+#endif
+
+ default:
+ {
+ _err = SML_ERR_XLT_ENC_UNK;
+ }
+ }
+ if (_err != SML_ERR_OK)
+ {
+ smlLibFree(_pBufMgr);
+ smlLibFree(_pEncoder);
+ return _err;
+ }
+
+ // SyncML Tag
+ if ((_err = xltGenerateTag(TN_SYNCML, TT_BEG, enc, _pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK)
+ {
+ smlLibFree(_pBufMgr);
+ smlLibFree(_pEncoder);
+ return _err;
+ }
+
+ // Generate SmlSyncHdr
+
+ if ((_err = xltEncBlock(TN_SYNCHDR, REQUIRED, pHeader, enc, _pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK)
+ {
+ smlLibFree(_pBufMgr);
+ smlLibFree(_pEncoder);
+ return _err;
+ }
+
+ // SyncBody Tag
+ if ((_err = xltGenerateTag(TN_SYNCBODY, TT_BEG, enc, _pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK)
+ {
+ smlLibFree(_pBufMgr);
+ smlLibFree(_pEncoder);
+ return _err;
+ }
+
+ _pEncoder->cur_ext = _pBufMgr->smlCurExt;
+ _pEncoder->last_ext = _pBufMgr->smlLastExt;
+ _pEncoder->end_tag_size = _pBufMgr->endTagSize;
+
+ *ppBufPos = _pBufMgr->smlXltBufferP;
+
+ smlLibFree(_pBufMgr);
+
+ _pEncoder->final = 0;
+
+ *ppEncoder = (XltEncoderPtr_t)_pEncoder;
+
+ return SML_ERR_OK;
+}
+
+/**
+ * Generates (WB)XML code and appends it to the XML buffer.
+ *
+ * @pre pEncoder holds the initialized encoder structure.
+ * the initialization takes place in the xltEncAppend function
+ * pContent has to contain a valid content structure structure
+ * pBufEnd must point to the end of the (WB)XML buffer
+ * ppBufPos has to be initialized to the start point of the
+ * (WB)XML buffer.
+ * @post After the function call ppBufPos points to the
+ * first free byte in the buffer behind the (WB)XML document
+ * @param pEncoder (IN)
+ * the encoder object
+ * @param pe (IN)
+ * the protocol element (PE_ADD, ...)
+ * @param pBufEnd (IN)
+ * pointer to the end of the buffer to write on
+ * @param pContent (IN)
+ * the content to append to the SyncML document
+ * @param ppBufPos (IN/OUT)
+ * current position of the bufferpointer
+ * @return shows error codes of function,\n
+ * 0, if OK\n
+ * Possible Error Codes:
+ * - SML_ERR_XLT_MISSING_CONT
+ * - SML_ERR_XLT_BUF_ERR
+ * - SML_ERR_XLT_INVAL_ELEM_TYPE
+ * - SML_ERR_XLT_INVAL_LIST_TYPE
+ * - SML_ERR_XLT_INVAL_TAG_TYPE
+ * - SML_ERR_XLT_ENC_UNK
+ * - SML_ERR_XLT_INVAL_PROTO_ELEM
+ */
+Ret_t xltEncAppend(const XltEncoderPtr_t pEncoder,
+ SmlProtoElement_t pe,
+ const MemPtr_t pBufEnd,
+ const VoidPtr_t pContent,
+ MemPtr_t *ppBufPos)
+{
+ // Return variable
+ Ret_t _err;
+
+ XltTagID_t tagID = TN_UNDEF;
+
+ // encoding type
+ SmlEncoding_t _enc;
+
+ //Structure containing buffer pointers, length and written bytes
+ BufferMgmtPtr_t _pBufMgr;
+
+ if ((_pBufMgr = (BufferMgmtPtr_t)smlLibMalloc(sizeof(BufferMgmt_t))) == NULL) return SML_ERR_NOT_ENOUGH_SPACE;
+ smlLibMemset(_pBufMgr, 0, sizeof(BufferMgmt_t));
+
+ //get the encoding type
+ _enc = pEncoder->enc;
+
+ _pBufMgr->vers = pEncoder->vers; // %%% luz:2003-07-31: pass SyncML version to bufmgr
+ _pBufMgr->smlXltBufferP = *ppBufPos;
+ _pBufMgr->smlXltBufferLen = pBufEnd - *ppBufPos;
+ _pBufMgr->smlXltStoreBufP = _pBufMgr->smlXltBufferP;
+ _pBufMgr->smlXltWrittenBytes = 0;
+ _pBufMgr->smlActiveExt = SML_EXT_UNDEFINED;
+ _pBufMgr->switchExtTag = TN_UNDEF;
+ _pBufMgr->spaceEvaluation = ((pEncoder->space_evaluation == NULL) ? 0 : 1);
+ // %%% luz 2002-09-03: evaluation may not mess with encoder state
+ if ( _pBufMgr->spaceEvaluation) {
+ // spaceEval state
+ _pBufMgr->smlCurExt = pEncoder->space_evaluation->cur_ext;
+ _pBufMgr->smlLastExt = pEncoder->space_evaluation->last_ext;
+ }
+ else {
+ // normal encoder state
+ _pBufMgr->smlCurExt = pEncoder->cur_ext;
+ _pBufMgr->smlLastExt = pEncoder->last_ext;
+ }
+
+ _pBufMgr->endTagSize =0;
+
+ _err = getTNbyPE(pe, &tagID);
+
+ _err = xltEncBlock(tagID, REQUIRED, pContent, _enc, _pBufMgr, SML_EXT_UNDEFINED);
+ if (_err != SML_ERR_OK)
+ {
+ smlLibFree(_pBufMgr);
+ return _err;
+ }
+
+
+ if (pEncoder->space_evaluation != NULL) {
+ // Only calculating size
+ pEncoder->space_evaluation->written_bytes += _pBufMgr->smlXltWrittenBytes;
+ pEncoder->space_evaluation->end_tag_size += _pBufMgr->endTagSize;
+ // save it only into evaluation state
+ pEncoder->space_evaluation->cur_ext = _pBufMgr->smlCurExt;
+ pEncoder->space_evaluation->last_ext = _pBufMgr->smlLastExt;
+ } else {
+ // really generating data
+ pEncoder->end_tag_size += _pBufMgr->endTagSize;
+ // save it into encoder state
+ pEncoder->cur_ext = _pBufMgr->smlCurExt;
+ pEncoder->last_ext = _pBufMgr->smlLastExt;
+ }
+
+ *ppBufPos = _pBufMgr->smlXltBufferP;
+
+ smlLibFree(_pBufMgr);
+
+ return SML_ERR_OK;
+}
+
+/**
+ * Filnalizes the (WB)XML document and returns the size of written bytes to
+ * the workspace module
+ *
+ * @pre pEncoder holds the initialized encoder structure.
+ * the initialization takes place in the xltEncAppend function
+ * pBufEnd must point to the end of the (WB)XML buffer
+ * ppBufPos has to be initialized to the start point of the
+ * (WB)XML buffer.
+ * @post After the function call ppBufPos points to the
+ * first free byte in the buffer behind the (WB)XML document
+ * @param pEncoder (IN)
+ * the encoder object
+ * @param pBufEnd (IN)
+ * pointer to the end of the buffer to write on
+ * @param ppBufPos (IN/OUT)
+ * current position of the bufferpointer
+ * @return shows error codes of function,\n
+ * 0, if OK\n
+ * Possible Error Codes:
+ * - SML_ERR_XLT_BUF_ERR
+ * - SML_ERR_XLT_MISSING_CONT
+ * - SML_ERR_XLT_INVAL_ELEM_TYPE
+ * - SML_ERR_XLT_INVAL_LIST_TYPE
+ * - SML_ERR_XLT_INVAL_TAG_TYPE
+ * - SML_ERR_XLT_ENC_UNK
+ * - SML_ERR_XLT_INVAL_PROTO_ELEM
+ */
+Ret_t xltEncTerminate(const XltEncoderPtr_t pEncoder,
+ const MemPtr_t pBufEnd,
+ MemPtr_t *ppBufPos)
+{
+ // Return variable
+ Ret_t _err;
+
+ // encoding type
+ SmlEncoding_t _enc;
+
+ //Structure containing buffer pointers, length and written bytes
+ BufferMgmtPtr_t _pBufMgr;
+
+ //get the encoding type
+ _enc = pEncoder->enc;
+
+ //Initialize buffer variables
+ if ((_pBufMgr = smlLibMalloc(sizeof(BufferMgmt_t))) == NULL) {
+ smlLibFree(pEncoder);
+ return SML_ERR_NOT_ENOUGH_SPACE;
+ }
+
+ _pBufMgr->vers = pEncoder->vers; // %%% luz:2003-07-31: pass SyncML version to bufmgr
+ _pBufMgr->smlXltWrittenBytes = 0;
+ _pBufMgr->smlXltBufferP = *ppBufPos;
+ _pBufMgr->smlXltStoreBufP = _pBufMgr->smlXltBufferP;
+ _pBufMgr->smlXltBufferLen = pBufEnd - *ppBufPos;
+ _pBufMgr->smlCurExt = pEncoder->cur_ext;
+ _pBufMgr->smlLastExt = pEncoder->last_ext;
+ _pBufMgr->smlActiveExt = pEncoder->cur_ext;
+ _pBufMgr->switchExtTag = TN_UNDEF;
+ _pBufMgr->spaceEvaluation = ((pEncoder->space_evaluation == NULL) ? 0 : 1);
+ _pBufMgr->endTagSize =0;
+
+ if (pEncoder->final == 1)
+ {
+ // Final Flag
+ if ((_err = xltGenerateTag(TN_FINAL, TT_ALL, _enc, _pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK)
+ {
+ smlLibFree(_pBufMgr);
+ xltEncReset(pEncoder);
+ return _err;
+ }
+ }
+
+ // SyncBody End Tag
+ if ((_err = xltGenerateTag(TN_SYNCBODY, TT_END, _enc, _pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK)
+ {
+ smlLibFree(_pBufMgr);
+ xltEncReset(pEncoder);
+ return _err;
+ }
+
+ // SyncML End Tag
+ if ((_err = xltGenerateTag(TN_SYNCML, TT_END, _enc, _pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK)
+ {
+ smlLibFree(_pBufMgr);
+ xltEncReset(pEncoder);
+ return _err;
+ }
+
+ pEncoder->cur_ext = _pBufMgr->smlCurExt;
+ pEncoder->last_ext = _pBufMgr->smlLastExt;
+
+ *ppBufPos = _pBufMgr->smlXltBufferP;
+
+ smlLibFree(_pBufMgr);
+
+ xltEncReset(pEncoder);
+
+ return SML_ERR_OK;
+}
+
+Ret_t xltEncReset(XltEncoderPtr_t pEncoder)
+{
+ if ((pEncoder) && (pEncoder->space_evaluation)) {
+ smlLibFree(pEncoder->space_evaluation);
+ pEncoder->space_evaluation = NULL;
+ }
+ smlLibFree(pEncoder);
+ return SML_ERR_OK;
+}
+
+
+/**
+ * Starts an evaluation run which prevents further API-Calls to write tags -
+ * just the tag-sizes are calculated. Must be sopped via smlEndEvaluation
+ *
+ * @param pEncoder (IN)
+ * the encoder object
+ * @return Return Code
+ */
+SML_API Ret_t xltStartEvaluation(XltEncoderPtr_t pEncoder)
+{
+ XltSpaceEvaluationPtr_t _pSpaceEvaluation;
+
+ if (pEncoder->space_evaluation != NULL)
+ return SML_ERR_WRONG_USAGE;
+
+ if ((_pSpaceEvaluation = (XltSpaceEvaluationPtr_t)smlLibMalloc(sizeof(XltSpaceEvaluation_t))) == NULL)
+ return SML_ERR_NOT_ENOUGH_SPACE;
+ smlLibMemset(_pSpaceEvaluation, 0, sizeof(XltSpaceEvaluation_t));
+ // %%% luz 2002-09-03: init encoder state shadow copies for evaluation from real encoder
+ _pSpaceEvaluation->cur_ext = pEncoder->cur_ext;
+ _pSpaceEvaluation->last_ext = pEncoder->last_ext;
+
+ pEncoder->space_evaluation = _pSpaceEvaluation;
+ return SML_ERR_OK;
+}
+
+
+/**
+ * Stops an evaluation run which prevents further API-Calls to write tags -
+ * the remaining free buffer size after all Tags are written is returned
+ *
+ * @param id (IN)
+ * instance ID
+ * @param pEncoder (IN)
+ * the encoder object
+ * @param freemem (IN/OUT)
+ * Size of free buffer for data after all tags are written
+ * @return Return Code
+ */
+SML_API Ret_t xltEndEvaluation(InstanceID_t id, XltEncoderPtr_t pEncoder, MemSize_t *freemem)
+{
+
+ MemSize_t _free;
+ XltSpaceEvaluationPtr_t _evaluation;
+
+ _evaluation = pEncoder->space_evaluation;
+
+ if (_evaluation == NULL)
+ return SML_ERR_WRONG_USAGE;
+
+ _free = smlGetFreeBuffer(id);
+ _free -= _evaluation->written_bytes;
+ _free -= _evaluation->end_tag_size;
+ _free -= pEncoder->end_tag_size;
+
+ *freemem = _free;
+
+ smlLibFree(_evaluation);
+ pEncoder->space_evaluation = NULL;
+
+ return SML_ERR_OK;
+}
+
+
+/**
+ * Generates a (WB)XML Block for a given tag ID and a given content
+ *
+ * @pre pContent holds a valid content structure
+ * tagId contains a valid SyncML tag ID
+ * @post the (WB)XML buffer in the pBufMgr structure contains the
+ * encoded (WB)XML block
+ * @param tagId (IN)
+ * the ID for the tag to generate (TN_ADD, ...)
+ * @param reqOptFlag (IN)
+ * flag if the block is required or optional
+ * @param pContent (IN)
+ * the content structure of the block
+ * @param enc (IN)
+ * the encoding constant (SML_WBXML or SML_XML)
+ * @param attFlag (IN)
+ * indicates if the encoded tag contain Attributes
+ * in namespace extensions
+ * @param pBufMgr (IN/OUT)
+ * pointer to a structure containing buffer management elements
+ * @return shows error codes of function,\n
+ * 0, if OK
+ */
+Ret_t xltEncBlock(XltTagID_t tagId, XltRO_t reqOptFlag, const VoidPtr_t pContent, SmlEncoding_t enc, BufferMgmtPtr_t pBufMgr, SmlPcdataExtension_t attFlag)
+{
+
+ //Return variable
+ Ret_t _err;
+
+
+ //Check if pContent of a required field is missing
+ if ((reqOptFlag == REQUIRED) && (pContent == NULL)) {
+ switch ((int)tagId) {
+ case TN_ATOMIC_END:
+ case TN_SYNC_END:
+ case TN_SEQUENCE_END:
+ break;
+ default:
+ return SML_ERR_XLT_MISSING_CONT;
+ }
+ }
+ //Check if pContent of a optional field is missing
+ else if ((pContent == NULL) && (tagId != TN_SYNC_END) && (tagId != TN_ATOMIC_END) && (tagId != TN_SEQUENCE_END))
+ return SML_ERR_OK;
+
+ //Generate the commands -> see DTD
+ switch ((int)tagId){
+ case TN_SYNCHDR:
+ // SyncHdr Begin Tag
+ if ((_err = xltGenerateTag(TN_SYNCHDR, TT_BEG, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ // Version
+ if ((_err = xltEncBlock(TN_VERSION, REQUIRED, ((SmlSyncHdrPtr_t) pContent)->version, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ // Proto
+ if ((_err = xltEncBlock(TN_PROTO, REQUIRED, ((SmlSyncHdrPtr_t) pContent)->proto, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ // SessionID
+ if ((_err = xltEncBlock(TN_SESSIONID, REQUIRED, ((SmlSyncHdrPtr_t) pContent)->sessionID, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ // MsgID
+ if ((_err = xltEncBlock(TN_MSGID, REQUIRED, ((SmlSyncHdrPtr_t) pContent)->msgID, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ // Target
+ if ((_err = xltEncBlock(TN_TARGET, REQUIRED, ((SmlSyncHdrPtr_t) pContent)->target, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ // Source
+ if ((_err = xltEncBlock(TN_SOURCE, REQUIRED, ((SmlSyncHdrPtr_t) pContent)->source, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ // RespURI?
+ if ((_err = xltEncBlock(TN_RESPURI, OPTIONAL, ((SmlSyncHdrPtr_t) pContent)->respURI, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ // NoResp?
+ if ((_err = xltEncBlock(TN_NORESP, OPTIONAL, &((SmlSyncHdrPtr_t) pContent)->flags, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ // Cred?
+ if ((_err = xltEncBlock(TN_CRED, OPTIONAL, ((SmlSyncHdrPtr_t) pContent)->cred, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ // Meta?
+ if ((_err = xltEncBlock(TN_META, OPTIONAL, ((SmlSyncHdrPtr_t) pContent)->meta, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ // SyncHdr End Tag
+ if ((_err = xltGenerateTag(TN_SYNCHDR, TT_END, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+
+ break;
+ case TN_CRED:
+ // Begin tag
+ if ((_err = xltGenerateTag(TN_CRED, TT_BEG, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ // Meta?
+ if ((_err = xltEncBlock(TN_META, OPTIONAL, ((SmlCredPtr_t) pContent)->meta, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ // Data
+ if ((_err = xltEncBlock(TN_DATA, REQUIRED, ((SmlCredPtr_t) pContent)->data, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ // End tag
+ if ((_err = xltGenerateTag(TN_CRED, TT_END, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ break;
+
+ case TN_SOURCEPARENT:
+ case TN_TARGETPARENT:
+ // Begin tag
+ if ((_err = xltGenerateTag(tagId, TT_BEG, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ // LocURI
+ if ((_err = xltEncBlock(TN_LOCURI, REQUIRED, ((SmlSourceParentPtr_t) pContent)->locURI, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ // End tag
+ if ((_err = xltGenerateTag(tagId, TT_END, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ break;
+
+ case TN_SOURCE:
+ case TN_TARGET:
+ // Begin tag
+ if ((_err = xltGenerateTag(tagId, TT_BEG, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ // LocURI
+ if ((_err = xltEncBlock(TN_LOCURI, REQUIRED, ((SmlSourcePtr_t) pContent)->locURI, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ // LocName?
+ if ((_err = xltEncBlock(TN_LOCNAME, OPTIONAL, ((SmlSourcePtr_t) pContent)->locName, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ // Filter?
+ if ((_err = xltEncBlock(TN_FILTER, OPTIONAL, ((SmlSourcePtr_t) pContent)->filter, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ // End tag
+ if ((_err = xltGenerateTag(tagId, TT_END, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ break;
+
+ case TN_ITEM:
+ // Begin tag
+ if ((_err = xltGenerateTag(TN_ITEM, TT_BEG, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ // Target?
+ if ((_err = xltEncBlock(TN_TARGET, OPTIONAL, ((SmlItemPtr_t) pContent)->target, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ // Source?
+ if ((_err = xltEncBlock(TN_SOURCE, OPTIONAL, ((SmlItemPtr_t) pContent)->source, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ // TargetParent?
+ if ((_err = xltEncBlock(TN_TARGETPARENT, OPTIONAL, ((SmlItemPtr_t) pContent)->targetParent, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ // SourceParent?
+ if ((_err = xltEncBlock(TN_SOURCEPARENT, OPTIONAL, ((SmlItemPtr_t) pContent)->sourceParent, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ // Meta?
+ if ((_err = xltEncBlock(TN_META, OPTIONAL, ((SmlItemPtr_t) pContent)->meta, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ // Data?
+ if ((_err = xltEncBlock(TN_DATA, OPTIONAL, ((SmlItemPtr_t) pContent)->data, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ // MoreData?
+ if ((_err = xltEncBlock(TN_MOREDATA, OPTIONAL, &(((SmlItemPtr_t) pContent)->flags), enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ // End tag
+ if ((_err = xltGenerateTag(TN_ITEM, TT_END, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ break;
+
+ /* %%% Added Filter tags 2005-08-17 by synthesis/luz for DS 1.2 */
+ case TN_FIELD:
+ case TN_RECORD:
+ // Begin tag
+ if ((_err = xltGenerateTag(tagId, TT_BEG, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ // Item
+ if ((_err = xltEncBlock(TN_ITEM, REQUIRED, ((SmlRecordOrFieldFilterPtr_t) pContent)->item, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ // End tag
+ if ((_err = xltGenerateTag(tagId, TT_END, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ break;
+
+ case TN_FILTER:
+ // Begin tag
+ if ((_err = xltGenerateTag(tagId, TT_BEG, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ // Meta
+ if ((_err = xltEncBlock(TN_META, REQUIRED, ((SmlFilterPtr_t) pContent)->meta, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ // Field?
+ if ((_err = xltEncBlock(TN_FIELD, OPTIONAL, ((SmlFilterPtr_t) pContent)->field, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ // Record?
+ if ((_err = xltEncBlock(TN_RECORD, OPTIONAL, ((SmlFilterPtr_t) pContent)->record, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ // FilterType?
+ if ((_err = xltEncBlock(TN_FILTERTYPE, OPTIONAL, ((SmlFilterPtr_t) pContent)->filtertype, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ // End tag
+ if ((_err = xltGenerateTag(tagId, TT_END, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ break;
+
+ case TN_REPLACE:
+ case TN_ADD:
+ case TN_COPY:
+ case TN_MOVE:
+ // Begin tag
+ if ((_err = xltGenerateTag(tagId, TT_BEG, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ // CmdID
+ if ((_err = xltEncBlock(TN_CMDID, REQUIRED, ((SmlGenericCmdPtr_t) pContent)->cmdID, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ // NoResp?
+ if ((_err = xltEncBlock(TN_NORESP, OPTIONAL, &((SmlGenericCmdPtr_t) pContent)->flags, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ // Cred?
+ if ((_err = xltEncBlock(TN_CRED, OPTIONAL, ((SmlGenericCmdPtr_t) pContent)->cred, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ // Meta?
+ if ((_err = xltEncBlock(TN_META, OPTIONAL, ((SmlGenericCmdPtr_t) pContent)->meta, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ // Item+
+ if ((_err = xltEncList(ITEM_LIST, REQUIRED, ((SmlGenericCmdPtr_t) pContent)->itemList, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ // End tag
+ if ((_err = xltGenerateTag(tagId, TT_END, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ break;
+
+ case TN_ALERT:
+ // Begin tag
+ if ((_err = xltGenerateTag(TN_ALERT, TT_BEG, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ // CmdID
+ if ((_err = xltEncBlock(TN_CMDID, REQUIRED, ((SmlAlertPtr_t) pContent)->cmdID, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ // NoResp?
+ if ((_err = xltEncBlock(TN_NORESP, OPTIONAL, &((SmlAlertPtr_t) pContent)->flags, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ // Cred?
+ if ((_err = xltEncBlock(TN_CRED, OPTIONAL, ((SmlAlertPtr_t) pContent)->cred, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ // Data?
+ if ((_err = xltEncBlock(TN_DATA, OPTIONAL, ((SmlAlertPtr_t) pContent)->data, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ // Item*
+ if ((_err = xltEncList(ITEM_LIST, OPTIONAL, ((SmlAlertPtr_t) pContent)->itemList, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ // End tag
+ if ((_err = xltGenerateTag(TN_ALERT, TT_END, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ break;
+
+#if (defined ATOMIC_SEND || defined SEQUENCE_SEND)
+ case TN_ATOMIC:
+ case TN_SEQUENCE:
+ // Begin tag
+ if ((_err = xltGenerateTag(tagId, TT_BEG, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ // CmdID
+ if ((_err = xltEncBlock(TN_CMDID, REQUIRED, ((SmlAtomicPtr_t) pContent)->cmdID, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ // NoResp?
+ if ((_err = xltEncBlock(TN_NORESP, OPTIONAL, &((SmlAtomicPtr_t) pContent)->flags, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ // Meta?
+ if ((_err = xltEncBlock(TN_META, OPTIONAL, ((SmlAtomicPtr_t) pContent)->meta, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ //End tag in TN_ATOMIC_END
+ break;
+
+ case TN_ATOMIC_END:
+ // End tag
+ if ((_err = xltGenerateTag(TN_ATOMIC, TT_END, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ break;
+
+ case TN_SEQUENCE_END:
+ // End tag
+ if ((_err = xltGenerateTag(TN_SEQUENCE, TT_END, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ break;
+#endif
+
+ case TN_DELETE:
+ // Begin tag
+ if ((_err = xltGenerateTag(TN_DELETE, TT_BEG, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ // CmdID
+ if ((_err = xltEncBlock(TN_CMDID, REQUIRED, ((SmlDeletePtr_t) pContent)->cmdID, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ // NoResp?
+ if ((_err = xltEncBlock(TN_NORESP, OPTIONAL, &((SmlDeletePtr_t) pContent)->flags, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ // Archive?
+ if ((_err = xltEncBlock(TN_ARCHIVE, OPTIONAL, &(((SmlDeletePtr_t) pContent)->flags), enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ // SftDel?
+ if ((_err = xltEncBlock(TN_SFTDEL, OPTIONAL, &(((SmlDeletePtr_t) pContent)->flags), enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ // Cred?
+ if ((_err = xltEncBlock(TN_CRED, OPTIONAL, ((SmlDeletePtr_t) pContent)->cred, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ // Meta?
+ if ((_err = xltEncBlock(TN_META, OPTIONAL, ((SmlDeletePtr_t) pContent)->meta, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ // Item+
+ if ((_err = xltEncList(ITEM_LIST, REQUIRED, ((SmlDeletePtr_t) pContent)->itemList, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ // End tag
+ if ((_err = xltGenerateTag(TN_DELETE, TT_END, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ break;
+
+#ifdef EXEC_SEND
+ case TN_EXEC:
+ // Begin tag
+ if ((_err = xltGenerateTag(TN_EXEC, TT_BEG, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ // CmdID
+ if ((_err = xltEncBlock(TN_CMDID, REQUIRED, ((SmlExecPtr_t) pContent)->cmdID, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ // NoResp?
+ if ((_err = xltEncBlock(TN_NORESP, OPTIONAL, &((SmlExecPtr_t) pContent)->flags, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ // Cred?
+ if ((_err = xltEncBlock(TN_CRED, OPTIONAL, ((SmlExecPtr_t) pContent)->cred, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ // Meta?
+ if ((_err = xltEncBlock(TN_META, OPTIONAL, ((SmlExecPtr_t) pContent)->meta, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ // Item
+ if ((_err = xltEncBlock(TN_ITEM, REQUIRED, ((SmlExecPtr_t) pContent)->item, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ // End tag
+ if ((_err = xltGenerateTag(TN_EXEC, TT_END, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ break;
+#endif
+
+ case TN_GET:
+ case TN_PUT:
+ // Begin tag
+ if ((_err = xltGenerateTag(tagId, TT_BEG, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ // CmdID
+ if ((_err = xltEncBlock(TN_CMDID, REQUIRED, ((SmlGetPtr_t) pContent)->cmdID, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ // NoResp?
+ if ((_err = xltEncBlock(TN_NORESP, OPTIONAL, &((SmlGetPtr_t) pContent)->flags, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ // Lang?
+ if ((_err = xltEncBlock(TN_LANG, OPTIONAL, ((SmlGetPtr_t) pContent)->lang, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ // Cred?
+ if ((_err = xltEncBlock(TN_CRED, OPTIONAL, ((SmlGetPtr_t) pContent)->cred, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ // Meta?
+ if ((_err = xltEncBlock(TN_META, OPTIONAL, ((SmlGetPtr_t) pContent)->meta, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ // Item+
+ if ((_err = xltEncList(ITEM_LIST, REQUIRED, ((SmlGetPtr_t) pContent)->itemList, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ // End tag
+ if ((_err = xltGenerateTag(tagId, TT_END, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ break;
+
+ case TN_MAP:
+ // Begin tag
+ if ((_err = xltGenerateTag(TN_MAP, TT_BEG, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ // CmdID
+ if ((_err = xltEncBlock(TN_CMDID, REQUIRED, ((SmlMapPtr_t) pContent)->cmdID, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ // Target
+ if ((_err = xltEncBlock(TN_TARGET, REQUIRED, ((SmlMapPtr_t) pContent)->target, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ // Source
+ if ((_err = xltEncBlock(TN_SOURCE, REQUIRED, ((SmlMapPtr_t) pContent)->source, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ // Cred?
+ if ((_err = xltEncBlock(TN_CRED, OPTIONAL, ((SmlMapPtr_t) pContent)->cred, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ // Meta?
+ if ((_err = xltEncBlock(TN_META, OPTIONAL, ((SmlMapPtr_t) pContent)->meta, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ // Mapitemlist
+ if ((_err = xltEncList(MAPITEM_LIST, REQUIRED, ((SmlMapPtr_t) pContent)->mapItemList, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ // End tag
+ if ((_err = xltGenerateTag(TN_MAP, TT_END, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ break;
+
+ case TN_MAPITEM:
+ // Begin tag
+ if ((_err = xltGenerateTag(TN_MAPITEM, TT_BEG, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ // Target
+ if ((_err = xltEncBlock(TN_TARGET, REQUIRED, ((SmlMapItemPtr_t) pContent)->target, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ // Source
+ if ((_err = xltEncBlock(TN_SOURCE, REQUIRED, ((SmlMapItemPtr_t) pContent)->source, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ // End tag
+ if ((_err = xltGenerateTag(TN_MAPITEM, TT_END, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ break;
+
+ case TN_RESULTS:
+ // Begin tag
+ if ((_err = xltGenerateTag(TN_RESULTS, TT_BEG, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ // CmdID
+ if ((_err = xltEncBlock(TN_CMDID, REQUIRED, ((SmlResultsPtr_t) pContent)->cmdID, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ // MsgRef?
+ if ((_err = xltEncBlock(TN_MSGREF, OPTIONAL, ((SmlResultsPtr_t) pContent)->msgRef, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ // CmdRef
+ if ((_err = xltEncBlock(TN_CMDREF, REQUIRED, ((SmlResultsPtr_t) pContent)->cmdRef, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ // Meta?
+ if ((_err = xltEncBlock(TN_META, OPTIONAL, ((SmlResultsPtr_t) pContent)->meta, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ // TargetRef?
+ if ((_err = xltEncBlock(TN_TARGETREF, OPTIONAL, ((SmlResultsPtr_t) pContent)->targetRef, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ // SourceRef?
+ if ((_err = xltEncBlock(TN_SOURCEREF, OPTIONAL, ((SmlResultsPtr_t) pContent)->sourceRef, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ // Item+
+ if ((_err = xltEncList(ITEM_LIST, REQUIRED, ((SmlResultsPtr_t) pContent)->itemList, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ // End tag
+ if ((_err = xltGenerateTag(TN_RESULTS, TT_END, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ break;
+
+ case TN_CHAL:
+ // Begin tag
+ if ((_err = xltGenerateTag(TN_CHAL, TT_BEG, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ // Meta
+ if ((_err = xltEncBlock(TN_META, REQUIRED, ((SmlChalPtr_t) pContent)->meta, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ // End tag
+ if ((_err = xltGenerateTag(TN_CHAL, TT_END, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ break;
+
+#ifdef SEARCH_SEND
+ case TN_SEARCH:
+ // Begin tag
+ if ((_err = xltGenerateTag(TN_SEARCH, TT_BEG, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ // CmdID
+ if ((_err = xltEncBlock(TN_CMDID, REQUIRED, ((SmlSearchPtr_t) pContent)->cmdID, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ // NoResp?
+ if ((_err = xltEncBlock(TN_NORESP, OPTIONAL, &((SmlSearchPtr_t) pContent)->flags, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ // NoResults?
+ if ((_err = xltEncBlock(TN_NORESULTS, OPTIONAL, &((SmlSearchPtr_t) pContent)->flags, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ // Cred?
+ if ((_err = xltEncBlock(TN_CRED, OPTIONAL, ((SmlSearchPtr_t) pContent)->cred, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ // Target?
+ if ((_err = xltEncBlock(TN_TARGET, OPTIONAL, ((SmlSearchPtr_t) pContent)->target, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ // Source List
+ if ((_err = xltEncList(SOURCE_LIST, REQUIRED, ((SmlSearchPtr_t) pContent)->sourceList, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ // Lang?
+ if ((_err = xltEncBlock(TN_LANG, OPTIONAL, ((SmlSearchPtr_t) pContent)->lang, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ // Meta
+ if ((_err = xltEncBlock(TN_META, REQUIRED, ((SmlSearchPtr_t) pContent)->meta, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ // Dsta
+ if ((_err = xltEncBlock(TN_DATA, REQUIRED, ((SmlSearchPtr_t) pContent)->data, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ // End tag
+ if ((_err = xltGenerateTag(TN_SEARCH, TT_END, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ break;
+#endif
+
+ case TN_STATUS:
+ // Begin tag
+ if ((_err = xltGenerateTag(TN_STATUS, TT_BEG, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ // CmdID
+ if ((_err = xltEncBlock(TN_CMDID, REQUIRED, ((SmlStatusPtr_t) pContent)->cmdID, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ // MsgRef?
+ if ((_err = xltEncBlock(TN_MSGREF, REQUIRED, ((SmlStatusPtr_t) pContent)->msgRef, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ // CmdRef
+ if ((_err = xltEncBlock(TN_CMDREF, REQUIRED, ((SmlStatusPtr_t) pContent)->cmdRef, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ // Cmd
+ if ((_err = xltEncBlock(TN_CMD, REQUIRED, ((SmlStatusPtr_t) pContent)->cmd, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ // TargetRefList?
+ if ((_err = xltEncList(TARGETREF_LIST, OPTIONAL, ((SmlStatusPtr_t) pContent)->targetRefList, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ // SourceRefList?
+ if ((_err = xltEncList(SOURCEREF_LIST, OPTIONAL, ((SmlStatusPtr_t) pContent)->sourceRefList, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ // Cred?
+ if ((_err = xltEncBlock(TN_CRED, OPTIONAL, ((SmlStatusPtr_t) pContent)->cred, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ // Chal?
+ if ((_err = xltEncBlock(TN_CHAL, OPTIONAL, ((SmlStatusPtr_t) pContent)->chal, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ // Data
+ if ((_err = xltEncBlock(TN_DATA, REQUIRED, ((SmlStatusPtr_t) pContent)->data, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ // Item*
+ if ((_err = xltEncList(ITEM_LIST, OPTIONAL, ((SmlStatusPtr_t) pContent)->itemList, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ // End tag
+ if ((_err = xltGenerateTag(TN_STATUS, TT_END, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ break;
+
+ case TN_SYNC:
+ // Begin tag
+ if ((_err = xltGenerateTag(TN_SYNC, TT_BEG, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ // CmdID
+ if ((_err = xltEncBlock(TN_CMDID, REQUIRED, ((SmlSyncPtr_t) pContent)->cmdID, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ // NoResp?
+ if ((_err = xltEncBlock(TN_NORESP, OPTIONAL, &((SmlSyncPtr_t) pContent)->flags, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ // Cred?
+ if ((_err = xltEncBlock(TN_CRED, OPTIONAL, ((SmlSyncPtr_t) pContent)->cred, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ // Target?
+ if ((_err = xltEncBlock(TN_TARGET, OPTIONAL, ((SmlSyncPtr_t) pContent)->target, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ // Source?
+ if ((_err = xltEncBlock(TN_SOURCE, OPTIONAL, ((SmlSyncPtr_t) pContent)->source, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ // Meta?
+ if ((_err = xltEncBlock(TN_META, OPTIONAL, ((SmlSyncPtr_t) pContent)->meta, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ // NumberOfChanges?
+ if ((_err = xltEncBlock(TN_NUMBEROFCHANGES, OPTIONAL, ((SmlSyncPtr_t) pContent)->noc, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ // End tag in TN_SYNC_END
+ break;
+
+ case TN_SYNC_END:
+ //End tag
+ if ((_err = xltGenerateTag(TN_SYNC, TT_END, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ break;
+
+ case TN_ARCHIVE:
+ //set the flag in the (WB)XML document if the flag is in the pContent
+ if ((*((Flag_t*)pContent)) & (SmlArchive_f)) {
+ if ((_err = xltGenerateTag(tagId, TT_ALL, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ }
+ break;
+
+ case TN_SFTDEL:
+ //set the flag in the (WB)XML document if the flag is in the pContent
+ if ((*((Flag_t *) pContent)) & (SmlSftDel_f)) {
+ if ((_err = xltGenerateTag(tagId, TT_ALL, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ break;
+
+ case TN_MOREDATA:
+ //set the flag in the (WB)XML document if the flag is in the pContent
+ if ((*((Flag_t *) pContent)) & (SmlMoreData_f)) {
+ if ((_err = xltGenerateTag(tagId, TT_ALL, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ }
+ break;
+
+ case TN_NORESULTS:
+ //set the flag in the (WB)XML document if the flag is in the pContent
+ if ((*((Flag_t *) pContent)) & (SmlNoResults_f)) {
+ if ((_err = xltGenerateTag(tagId, TT_ALL, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ }
+ break;
+
+ case TN_NORESP:
+ //set the flag in the (WB)XML document if the flag is in the pContent
+ if ((*((Flag_t *) pContent)) & (SmlNoResp_f)){
+ if ((_err = xltGenerateTag(tagId, TT_ALL, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ }
+ break;
+
+ case TN_FINAL:
+ //set the flag in the (WB)XML document if the flag is in the pContent
+ if ((*((Flag_t *) pContent)) & (SmlFinal_f)) {
+ if ((_err = xltGenerateTag(tagId, TT_ALL, enc, pBufMgr, SML_EXT_UNDEFINED)) != SML_ERR_OK) return _err;
+ }
+ break;
+
+ default: // all leaf nodes (PCDATA#)
+ return xltEncPcdata(tagId, reqOptFlag, pContent, enc, pBufMgr, attFlag);
+ }
+ }
+ return SML_ERR_OK;
+}
+
+Ret_t xltEncPcdata(XltTagID_t tagId, XltRO_t reqOptFlag, const VoidPtr_t pContent, SmlEncoding_t enc, BufferMgmtPtr_t pBufMgr, SmlPcdataExtension_t attFlag) {
+ //Return variable
+ Ret_t _err;
+
+ //generate PCDATA begin tag
+ if ((_err = xltGenerateTag(tagId, TT_BEG, enc, pBufMgr, attFlag)) != SML_ERR_OK) return _err;
+
+ //write the pContent to the buffer according the encoding type
+ switch ((int)enc) {
+#ifdef __SML_WBXML__
+ case SML_WBXML: {
+ switch (((SmlPcdataPtr_t)pContent)->contentType) {
+ case SML_PCDATA_STRING:
+ if ((_err = wbxmlWriteTypeToBuffer(((SmlPcdataPtr_t)pContent)->content, STR_I, ((SmlPcdataPtr_t)pContent)->length, pBufMgr)) != SML_ERR_OK) return _err;
+ break;
+ // Note: SML_PCDATA_CDATA case added by luz to allow direct translation from XML to WBXML
+ case SML_PCDATA_CDATA:
+ case SML_PCDATA_OPAQUE:
+ if ((_err = wbxmlWriteTypeToBuffer(((SmlPcdataPtr_t)pContent)->content, OPAQUE, ((SmlPcdataPtr_t)pContent)->length, pBufMgr)) != SML_ERR_OK) return _err;
+ break;
+#ifdef __USE_EXTENSIONS__
+ case SML_PCDATA_EXTENSION:
+ if ((_err = xltBuildExtention(((SmlPcdataPtr_t)pContent)->extension, reqOptFlag, ((SmlPcdataPtr_t)pContent)->content, enc, pBufMgr)) != SML_ERR_OK) return _err;
+ break;
+#endif
+ default:
+ // 2003-11-24: Tomy to deal with pcdata empty extensions (for example <Meta></Meta> which is valid)
+ // refer to xltdec.c to see that empty extensions result in SmlPcdataPtr_t with all fields (data) set to 0
+ if (((SmlPcdataPtr_t)pContent)->contentType != SML_PCDATA_UNDEFINED ||
+ ((SmlPcdataPtr_t)pContent)->extension != SML_EXT_UNDEFINED ||
+ ((SmlPcdataPtr_t)pContent)->length != 0 ||
+ ((SmlPcdataPtr_t)pContent)->content != NULL)
+ return SML_ERR_XLT_INVAL_PCDATA_TYPE;
+ // return SML_ERR_XLT_INVAL_PCDATA_TYPE;
+ // end modified by Tomy
+ }; // eof switch(contenttype)
+ break;
+ } // case WBXML
+#endif // eof WBXML
+
+#ifdef __SML_XML__
+ case SML_XML: {
+ MemPtr_t _tmpStr;
+ MemPtr_t p;
+ MemSize_t len,n;
+ char c;
+ switch (((SmlPcdataPtr_t)pContent)->contentType) {
+ // Note: SML_PCDATA_OPAQUE case added by luz to allow direct translation from WBXML to XML
+ #ifdef PCDATA_OPAQUE_AS_CDATA
+ case SML_PCDATA_OPAQUE:
+ #endif
+ case SML_PCDATA_CDATA:
+ stringAsCData:
+ {
+ // %%% luz 2006-09-07 : made completely CDATA nesting safe
+ // add CDATA
+ _tmpStr = (MemPtr_t) "<![CDATA[";
+ if ((_err = xltAddToBuffer(_tmpStr, smlLibStrlen((String_t)_tmpStr), pBufMgr)) != SML_ERR_OK) return _err;
+ // search and escape eventually contained ]]> sequences
+ p=((SmlPcdataPtr_t)pContent)->content;
+ len=((SmlPcdataPtr_t)pContent)->length;
+ n=0;
+ while (n<len-2) {
+ if (p[n]==']' && p[n+1]==']' && p[n+2]=='>') {
+ // we must substitute "]]>" with "]]>]<![CDATA[]>"
+ // - copy what we have so far (includes ]]>)
+ n+=3;
+ if ((_err = xltAddToBuffer(p, n, pBufMgr)) != SML_ERR_OK) return _err;
+ // - add extra code needed
+ _tmpStr = (MemPtr_t) "]<![CDATA[]>";
+ if ((_err = xltAddToBuffer(_tmpStr, smlLibStrlen((String_t)_tmpStr), pBufMgr)) != SML_ERR_OK) return _err;
+ // - restart where we are now
+ p+=n; // first char after ]]>
+ len-=n; // remaining length
+ n=0; // processed chars
+ }
+ else {
+ n++; // just go to next char
+ }
+ }
+ // add remaining data
+ if ((_err = xltAddToBuffer(p, len, pBufMgr)) != SML_ERR_OK) return _err;
+ // add end of CDATA marker
+ _tmpStr = (MemPtr_t) "]]>";
+ if ((_err = xltAddToBuffer(_tmpStr, smlLibStrlen((String_t)_tmpStr), pBufMgr)) != SML_ERR_OK) return _err;
+ break;
+ }
+ // Note: SyncFest #5 shows that <![CDATA[ is not correctly parsed by the RTK
+ // so we don't use it and risk the danger of failing on payload which has
+ // XML in it.
+ #ifndef PCDATA_OPAQUE_AS_CDATA
+ case SML_PCDATA_OPAQUE:
+ #endif
+ case SML_PCDATA_STRING: {
+ // %%% luz 2006-09-07 : check for invalid characters that would break XML and use CDATA instead
+ p=((SmlPcdataPtr_t)pContent)->content;
+ len=((SmlPcdataPtr_t)pContent)->length;
+ while (len-->0) {
+ c=*p++;
+ if (c=='&' || c=='<' || c=='>')
+ goto stringAsCData; // encode as CDATA
+ }
+ // ok, string is clean and does not need CDATA
+ if ((_err = xltAddToBuffer(((SmlPcdataPtr_t)pContent)->content, ((SmlPcdataPtr_t)pContent)->length, pBufMgr)) != SML_ERR_OK) return _err;
+ break;
+ }
+#ifdef __USE_EXTENSIONS__
+ case SML_PCDATA_EXTENSION: {
+ if ((_err = xltBuildExtention(((SmlPcdataPtr_t)pContent)->extension, reqOptFlag, ((SmlPcdataPtr_t)pContent)->content, enc, pBufMgr)) != SML_ERR_OK) return _err;
+ break;
+ }
+#endif
+ default: {
+ return SML_ERR_XLT_INVAL_PCDATA_TYPE;
+ // refer to xltdec.c to see that empty extensions result in SmlPcdataPtr_t with all fields (data) set to 0
+ if (((SmlPcdataPtr_t)pContent)->contentType != SML_PCDATA_UNDEFINED ||
+ ((SmlPcdataPtr_t)pContent)->extension != SML_EXT_UNDEFINED ||
+ ((SmlPcdataPtr_t)pContent)->length != 0 ||
+ ((SmlPcdataPtr_t)pContent)->content != NULL)
+ return SML_ERR_XLT_INVAL_PCDATA_TYPE;
+ // return SML_ERR_XLT_INVAL_PCDATA_TYPE;
+ // end modified by Tomy
+ }
+ } // switch tags
+ break;
+ } // case XML
+#endif // eof XML
+
+ default:
+ return SML_ERR_XLT_ENC_UNK;
+ } // eof switch(enc)
+
+ //generate PCDATA END tag
+ if ((_err = xltGenerateTag(tagId, TT_END, enc, pBufMgr, attFlag)) != SML_ERR_OK) return _err;
+ return SML_ERR_OK;
+}
+
+/**
+ * Generates a list element which is not directly related to a tag
+ *
+ * @pre pList holds a valid list structure
+ * listId contains a valid SyncML list ID
+ * @post the (WB)XML buffer in the pBufMgr structure contains the
+ * encoded (WB)XML list
+ * @param listId (IN)
+ * the ID of the list to generate (e.g. TARGET_LIST, ...)
+ * @param reqOptFlag (IN)
+ * flag if the block is required or optional
+ * @param pList (IN)
+ * reference to the list to process
+ * @param enc (IN)
+ * the encoding constant (SML_WBXML or SML_XML)
+ * @param pBufMgr (IN/OUT)
+ * pointer to a structure containing buffer management elements
+ * @param attFlag (IN)
+ * indicates if the encoded tag contain Attributes
+ * in namespace extensions
+ * @return shows error codes of function,\n
+ * 0, if OK
+ */
+Ret_t xltEncList(XltListType_t listId, XltRO_t reqOptFlag, VoidPtr_t pList, SmlEncoding_t enc, BufferMgmtPtr_t pBufMgr, SmlPcdataExtension_t attFlag)
+{
+ //Return variable
+ Ret_t _err;
+
+ //check if list is required or not
+ if ((reqOptFlag == REQUIRED) && (pList == NULL))
+ return SML_ERR_XLT_MISSING_CONT;
+ else if (pList == NULL)
+ return SML_ERR_OK;
+
+ //encode the different list types
+ switch ((int)listId)
+ {
+ case ITEM_LIST:
+ {
+ do
+ {
+ if ((_err = xltEncBlock(TN_ITEM, OPTIONAL, ((SmlItemListPtr_t)pList)->item, enc, pBufMgr, attFlag)) != SML_ERR_OK) return _err;
+ pList = ((SmlItemListPtr_t)pList)->next;
+ } while ((SmlItemListPtr_t)pList != NULL);
+
+ break;
+ }
+ case SOURCE_LIST:
+ {
+ do
+ {
+ if ((_err = xltEncBlock(TN_SOURCE, OPTIONAL, ((SmlSourceListPtr_t)pList)->source, enc, pBufMgr, attFlag)) != SML_ERR_OK) return _err;
+ pList = ((SmlSourceListPtr_t)pList)->next;
+ } while ((SmlSourceListPtr_t)pList != NULL);
+
+ break;
+ }
+ case TARGETREF_LIST:
+ {
+ do
+ {
+ if ((_err = xltEncBlock(TN_TARGETREF, OPTIONAL, ((SmlTargetRefListPtr_t)pList)->targetRef, enc, pBufMgr, attFlag)) != SML_ERR_OK) return _err;
+ pList = ((SmlTargetRefListPtr_t)pList)->next;
+ } while ((SmlTargetRefListPtr_t)pList != NULL);
+
+ break;
+ }
+ case SOURCEREF_LIST:
+ {
+ do
+ {
+ if ((_err = xltEncBlock(TN_SOURCEREF, OPTIONAL, ((SmlSourceRefListPtr_t)pList)->sourceRef, enc, pBufMgr, attFlag)) != SML_ERR_OK) return _err;
+ pList = ((SmlSourceRefListPtr_t)pList)->next;
+ } while ((SmlSourceRefListPtr_t)pList != NULL);
+
+ break;
+ }
+ case MAPITEM_LIST:
+ {
+ do
+ {
+ if ((_err = xltEncBlock(TN_MAPITEM, OPTIONAL, ((SmlMapItemListPtr_t)pList)->mapItem, enc, pBufMgr, attFlag)) != SML_ERR_OK) return _err;
+ pList = ((SmlMapItemListPtr_t)pList)->next;
+ } while ((SmlMapItemListPtr_t)pList != NULL);
+
+ break;
+ }
+ default:
+ return SML_ERR_XLT_INVAL_LIST_TYPE;
+ }
+
+ return SML_ERR_OK;
+}
+
+/**
+ * Generates a (WB)XML tag
+ *
+ * @pre valid parameters
+ * @post the buffer contains a new tag
+ * @param tagId (IN)
+ * the tag ID
+ * @param TagType (IN)
+ * the tag type (begin tag, end tag, ...)
+ * @param enc (IN)
+ * the encoding constant (SML_WBXML or SML_XML)
+ * @param attFlag (IN)
+ * indicates if the encoded tag contain Attributes
+ * in namespace extensions
+ * @param pBufMgr (IN/OUT)
+ * pointer to a structure containing buffer management elements
+ * @return shows error codes of function,\n
+ * 0, if OK
+ */
+Ret_t xltGenerateTag(XltTagID_t tagId, XltTagType_t TagType, SmlEncoding_t enc, BufferMgmtPtr_t pBufMgr, SmlPcdataExtension_t attFlag)
+{
+
+ Ret_t _err;
+#ifdef __SML_WBXML__
+ MemByte_t _switchpage = XLT_SWITCHPAGE;
+#endif
+
+ switch ((int)enc) {
+#ifdef __SML_WBXML__
+ case SML_WBXML:
+ /* in WBXML codepage switches are done for starting tags only */
+ if (TagType != TT_END) {
+ //codepage switching with wbxml instead of namespace
+ if (getCodePage(attFlag) != getCodePage(pBufMgr->smlCurExt)) {
+ MemByte_t _newcp = getCodePage(attFlag);
+ if ((_err = wbxmlWriteTypeToBuffer((MemPtr_t)(&_switchpage), TAG, 1, pBufMgr)) != SML_ERR_OK) return _err;
+ if ((_err = wbxmlWriteTypeToBuffer((MemPtr_t)(&_newcp), TAG, 1, pBufMgr)) != SML_ERR_OK) return _err;
+ }
+
+ if (attFlag != pBufMgr->smlCurExt) {
+ pBufMgr->switchExtTag = tagId;
+ pBufMgr->smlLastExt = pBufMgr->smlCurExt;
+ pBufMgr->smlCurExt = attFlag;
+ }
+ } // for TagType
+ return wbxmlGenerateTag(tagId, TagType, pBufMgr);
+#endif
+#ifdef __SML_XML__
+ case SML_XML:
+
+ if (attFlag != pBufMgr->smlCurExt) {
+ pBufMgr->switchExtTag = tagId;
+ pBufMgr->smlLastExt = pBufMgr->smlCurExt;
+ pBufMgr->smlCurExt = attFlag;
+ }
+ return xmlGenerateTag(tagId, TagType, pBufMgr, attFlag);
+#endif
+ default:
+ return SML_ERR_XLT_ENC_UNK;
+ }
+
+ //return SML_ERR_XLT_ENC_UNK;NOT NEEDED
+}
+
+#ifdef __USE_EXTENSIONS__
+/* Entrypoint for SubDTD's. If we reached this point we already know
+ * a) we have data fora sub-DTD to encode and
+ * b) we know which sub-DTD should be encoded.
+ * So just call the appropriate sub-DTD encoder and thats it.
+ */
+Ret_t xltBuildExtention(SmlPcdataExtension_t extId, XltRO_t reqOptFlag, VoidPtr_t pContent, SmlEncoding_t enc, BufferMgmtPtr_t pBufMgr) {
+
+ switch (extId) {
+#ifdef __USE_METINF__
+ case SML_EXT_METINF:
+ /* a metaInf DTD always starts with this token */
+ return metinfEncBlock(TN_METINF_METINF,reqOptFlag,pContent,enc,pBufMgr,SML_EXT_METINF);
+ break;
+#endif
+#ifdef __USE_DEVINF__
+ case SML_EXT_DEVINF:
+ /* a deviceInf DTD always starts with this token */
+ /* we have to choose, wether we have to encode the DevInf as XML or WBXML */
+ /* in the latter case, we need a special treatment of this sub-dtd, as we have */
+ /* to put it into a SML_PCDATA_OPAQUE field ... */
+ if (enc == SML_XML)
+ return devinfEncBlock(TN_DEVINF_DEVINF,reqOptFlag,pContent,enc,pBufMgr,SML_EXT_DEVINF);
+ else
+ return subdtdEncWBXML(TN_DEVINF_DEVINF,reqOptFlag,pContent,SML_WBXML,pBufMgr,SML_EXT_DEVINF);
+ break;
+#endif
+ /* oops - we don not know about that extension -> bail out */
+ default:
+ return SML_ERR_XLT_INVAL_EXT;
+ }
+ //return SML_ERR_OK;CAN NOT BE REACHED
+}
+
+
+/* Sub DTD's need a special treatment when used together with WBXML.
+ * We need to eoncode them as a complete WBXML message including headers and stuff
+ * and store the result within an SML_PCDATA_OPAQUE datafield.
+ * To archieve this we create a new encoder, encode the message and finally
+ * copy the result into the allready existing encoder.
+ */
+#ifdef __SML_WBXML__
+Ret_t subdtdEncWBXML(XltTagID_t tagId, XltRO_t reqOptFlag, const VoidPtr_t pContent, SmlEncoding_t enc, BufferMgmtPtr_t pBufMgr, SmlPcdataExtension_t attFlag)
+{
+ #ifdef __USE_DEVINF__
+ Ret_t _err = SML_ERR_OK;
+ #endif
+
+ Short_t SubBufSize = 12000; // for starters we use 12kB for each sub DTD to encode in WBXML
+ BufferMgmtPtr_t pSubBufMgr = NULL;
+
+
+ // first create a sub buffer
+ pSubBufMgr = (BufferMgmtPtr_t)smlLibMalloc(sizeof(BufferMgmt_t));
+ if (pSubBufMgr == NULL) {
+ if (enc && pContent && reqOptFlag && tagId) {
+ }
+ return SML_ERR_NOT_ENOUGH_SPACE;
+ }
+
+ smlLibMemset(pSubBufMgr, 0,sizeof(BufferMgmt_t));
+ pSubBufMgr->smlXltBufferLen = SubBufSize;
+ pSubBufMgr->smlXltBufferP = (MemPtr_t)smlLibMalloc(SubBufSize);
+ if (pSubBufMgr->smlXltBufferP == NULL) {
+ smlLibFree(pSubBufMgr);
+ return SML_ERR_NOT_ENOUGH_SPACE;
+ }
+
+ smlLibMemset(pSubBufMgr->smlXltBufferP, 0, SubBufSize);
+ pSubBufMgr->smlXltStoreBufP = pSubBufMgr->smlXltBufferP;
+ pSubBufMgr->smlXltWrittenBytes = 0;
+ pSubBufMgr->smlActiveExt = pBufMgr->smlActiveExt;
+ pSubBufMgr->smlCurExt = pBufMgr->smlCurExt;
+ pSubBufMgr->smlLastExt = pBufMgr->smlLastExt;
+ pSubBufMgr->spaceEvaluation = pBufMgr->spaceEvaluation;
+ pSubBufMgr->vers = pBufMgr->vers;
+
+ // check if we want to use numeric or textual FPI
+ if (SyncMLDevInfWBXMLPublicID[pBufMgr->vers]==0) {
+ // textual FPI
+ // - get the FPI string
+ const char *FPIstring = SyncMLDevInfFPI[pBufMgr->vers];
+ Short_t FPIsize = smlLibStrlen(FPIstring);
+ if (pSubBufMgr->spaceEvaluation == 0) {
+ // - create the WBXML header
+ pSubBufMgr->smlXltBufferP[0] = 0x02; // WBXML Version 1.2
+ pSubBufMgr->smlXltBufferP[1] = 0x00; // use Stringtable for ID
+ pSubBufMgr->smlXltBufferP[2] = 0x00; // empty/unknown public ID
+ pSubBufMgr->smlXltBufferP[3] = 0x6A; // charset encoding UTF-8
+ pSubBufMgr->smlXltBufferP[4] = FPIsize; // %%% not fixed=0x1D!! lenght of stringtable = length of FPIsize
+ pSubBufMgr->smlXltBufferP += 5;
+ // - Generate textual FPI
+ smlLibMemmove(pSubBufMgr->smlXltBufferP, FPIstring, FPIsize);
+ pSubBufMgr->smlXltBufferP += FPIsize;
+ }
+ // in case of space evaluation, just count the number of written bytes
+ pSubBufMgr->smlXltWrittenBytes = 5 + FPIsize;
+ }
+ else {
+ // numeric FPI, no string table
+ if (pSubBufMgr->spaceEvaluation == 0) {
+ // - create the WBXML header
+ unsigned short fpi=SyncMLDevInfWBXMLPublicID[pBufMgr->vers];
+ pSubBufMgr->smlXltBufferP[0] = 0x02; // WBXML Version 1.2
+ pSubBufMgr->smlXltBufferP[1] = ((fpi>>7) & 0x7F)+0x80; // upper 7 bit
+ pSubBufMgr->smlXltBufferP[2] = (fpi & 0x7F); // lower 7 bits
+ pSubBufMgr->smlXltBufferP[3] = 0x6A; // charset encoding UTF-8
+ pSubBufMgr->smlXltBufferP[4] = 0; // no string table
+ pSubBufMgr->smlXltBufferP += 5;
+ }
+ // in case of space evaluation, just count the number of written bytes
+ pSubBufMgr->smlXltWrittenBytes = 5;
+ }
+
+ // do the encoding
+ switch (attFlag) {
+ #ifdef __USE_DEVINF__
+ case SML_EXT_DEVINF:
+ if ((_err = devinfEncBlock(TN_DEVINF_DEVINF,reqOptFlag,pContent,enc,pSubBufMgr,SML_EXT_DEVINF)) != SML_ERR_OK) {
+ smlLibFree(pSubBufMgr->smlXltStoreBufP);
+ smlLibFree(pSubBufMgr);
+ return _err;
+ }
+ break;
+ #endif
+ /* oops - we don not know about that extension -> bail out */
+ default:
+ smlLibFree(pSubBufMgr->smlXltStoreBufP);
+ smlLibFree(pSubBufMgr);
+ return SML_ERR_XLT_INVAL_EXT;
+ }
+
+ #ifdef __USE_DEVINF__
+ // move it to the 'real' encoder buffer
+ // now set up the OPAQUE field
+ if (pBufMgr->spaceEvaluation == 0) {
+ pBufMgr->smlXltBufferP[0] = 0xC3; // OPAQUE data identifier
+ pBufMgr->smlXltBufferP += 1;
+
+ wbxmlOpaqueSize2Buf(pSubBufMgr->smlXltWrittenBytes, pBufMgr);
+
+ smlLibMemmove(pBufMgr->smlXltBufferP, pSubBufMgr->smlXltStoreBufP, pSubBufMgr->smlXltWrittenBytes);
+ pBufMgr->smlXltBufferP += pSubBufMgr->smlXltWrittenBytes;
+ pBufMgr->smlXltWrittenBytes += pSubBufMgr->smlXltWrittenBytes;
+ } else {
+ pBufMgr->smlXltWrittenBytes++;
+ wbxmlOpaqueSize2Buf(pSubBufMgr->smlXltWrittenBytes, pBufMgr);
+ pBufMgr->smlXltWrittenBytes += pSubBufMgr->smlXltWrittenBytes;
+ }
+
+ // clean up the temporary stuff
+ smlLibFree(pSubBufMgr->smlXltStoreBufP);
+ smlLibFree(pSubBufMgr);
+
+ return _err;
+#endif
+}
+#endif
+#endif
diff --git a/src/syncml_tk/src/sml/xlt/all/xltenccom.c b/src/syncml_tk/src/sml/xlt/all/xltenccom.c
new file mode 100755
index 0000000..5703ab1
--- /dev/null
+++ b/src/syncml_tk/src/sml/xlt/all/xltenccom.c
@@ -0,0 +1,104 @@
+/**
+ * @file
+ * Encoder utils file
+ *
+ * @target_system All
+ * @target_os All
+ */
+
+/*
+ * Copyright Notice
+ * Copyright (c) Ericsson, IBM, Lotus, Matsushita Communication
+ * Industrial Co., Ltd., Motorola, Nokia, Openwave Systems, Inc.,
+ * Palm, Inc., Psion, Starfish Software, Symbian, Ltd. (2001).
+ * All Rights Reserved.
+ * Implementation of all or part of any Specification may require
+ * licenses under third party intellectual property rights,
+ * including without limitation, patent rights (such a third party
+ * may or may not be a Supporter). The Sponsors of the Specification
+ * are not responsible and shall not be held responsible in any
+ * manner for identifying or failing to identify any or all such
+ * third party intellectual property rights.
+ *
+ * THIS DOCUMENT AND THE INFORMATION CONTAINED HEREIN ARE PROVIDED
+ * ON AN "AS IS" BASIS WITHOUT WARRANTY OF ANY KIND AND ERICSSON, IBM,
+ * LOTUS, MATSUSHITA COMMUNICATION INDUSTRIAL CO. LTD, MOTOROLA,
+ * NOKIA, PALM INC., PSION, STARFISH SOFTWARE AND ALL OTHER SYNCML
+ * SPONSORS DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+ * BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
+ * HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
+ * SHALL ERICSSON, IBM, LOTUS, MATSUSHITA COMMUNICATION INDUSTRIAL CO.,
+ * LTD, MOTOROLA, NOKIA, PALM INC., PSION, STARFISH SOFTWARE OR ANY
+ * OTHER SYNCML SPONSOR BE LIABLE TO ANY PARTY FOR ANY LOSS OF
+ * PROFITS, LOSS OF BUSINESS, LOSS OF USE OF DATA, INTERRUPTION OF
+ * BUSINESS, OR FOR DIRECT, INDIRECT, SPECIAL OR EXEMPLARY, INCIDENTAL,
+ * PUNITIVE OR CONSEQUENTIAL DAMAGES OF ANY KIND IN CONNECTION WITH
+ * THIS DOCUMENT OR THE INFORMATION CONTAINED HEREIN, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH LOSS OR DAMAGE.
+ *
+ * The above notice and this paragraph must be included on all copies
+ * of this document that are made.
+ *
+ */
+
+/*************************************************************************/
+/* Definitions */
+/*************************************************************************/
+
+#include "syncml_tk_prefix_file.h" // %%% luz: needed for precompiled headers in eVC++
+
+#include "xltenccom.h"
+#include <smlerr.h>
+#include <libmem.h>
+
+/**
+ * Add a string to the global buffer
+ *
+ * @pre pContent contains some content bytes to write to the (WB) XML buffer
+ * @post content is written to the buffer
+ * @param pContent (IN)
+ * the character pointer referencing the content to write to the buffer
+ * @param size (IN)
+ * the content length
+ * @param pBufMgr (IN/OUT)
+ * pointer to a structure containing buffer management elements
+ * @return shows error codes of function,\n
+ * 0, if OK
+ */
+Ret_t xltAddToBuffer(const MemPtr_t pContent, MemSize_t size, BufferMgmtPtr_t pBufMgr)
+{
+ // if we are doing a space evaluation, do not write the data physically - just remember its length
+ if (!pBufMgr->spaceEvaluation) {
+ //check if buffersize is to small to write the content
+ if ((size + pBufMgr->smlXltWrittenBytes) > pBufMgr->smlXltBufferLen) {
+ #ifdef NCDEBUGPRINTFX
+ #warning "%%%%% delete that message later"
+ NCDEBUGPRINTFX(DBG_RTK_SML,(
+ "xltAddToBuffer: buffer too small, pContent='%0.30s', size=%ld, pBufMgr->smlXltWrittenBytes=%ld, pBufMgr->smlXltBufferLen=%ld",
+ pContent,
+ size,
+ pBufMgr->smlXltWrittenBytes,
+ pBufMgr->smlXltBufferLen
+ ));
+ #endif
+ return SML_ERR_XLT_BUF_ERR;
+ }
+
+ if (!(smlLibMemcpy((void*) pBufMgr->smlXltBufferP, (void*) pContent, (MemSize_t) size))) {
+ #ifdef NCDEBUGPRINTFX
+ #warning "%%%%% delete that message later"
+ NCDEBUGPRINTFX(DBG_RTK_SML,(
+ "xltAddToBuffer: memCpy failed, pBufMgr->smlXltBufferP=%lX, size=%ld",
+ (long)pBufMgr->smlXltBufferP,
+ size
+ ));
+ #endif
+ return SML_ERR_XLT_BUF_ERR;
+ }
+ pBufMgr->smlXltBufferP += size;
+ }
+ pBufMgr->smlXltWrittenBytes += size;
+
+ return SML_ERR_OK;
+}
diff --git a/src/syncml_tk/src/sml/xlt/all/xltenccom.h b/src/syncml_tk/src/sml/xlt/all/xltenccom.h
new file mode 100755
index 0000000..fafdebb
--- /dev/null
+++ b/src/syncml_tk/src/sml/xlt/all/xltenccom.h
@@ -0,0 +1,120 @@
+/**
+ * @file
+ * Encoder utils header file
+ *
+ * @target_system All
+ * @target_os All
+ */
+
+/*
+ * Copyright Notice
+ * Copyright (c) Ericsson, IBM, Lotus, Matsushita Communication
+ * Industrial Co., Ltd., Motorola, Nokia, Openwave Systems, Inc.,
+ * Palm, Inc., Psion, Starfish Software, Symbian, Ltd. (2001).
+ * All Rights Reserved.
+ * Implementation of all or part of any Specification may require
+ * licenses under third party intellectual property rights,
+ * including without limitation, patent rights (such a third party
+ * may or may not be a Supporter). The Sponsors of the Specification
+ * are not responsible and shall not be held responsible in any
+ * manner for identifying or failing to identify any or all such
+ * third party intellectual property rights.
+ *
+ * THIS DOCUMENT AND THE INFORMATION CONTAINED HEREIN ARE PROVIDED
+ * ON AN "AS IS" BASIS WITHOUT WARRANTY OF ANY KIND AND ERICSSON, IBM,
+ * LOTUS, MATSUSHITA COMMUNICATION INDUSTRIAL CO. LTD, MOTOROLA,
+ * NOKIA, PALM INC., PSION, STARFISH SOFTWARE AND ALL OTHER SYNCML
+ * SPONSORS DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+ * BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
+ * HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
+ * SHALL ERICSSON, IBM, LOTUS, MATSUSHITA COMMUNICATION INDUSTRIAL CO.,
+ * LTD, MOTOROLA, NOKIA, PALM INC., PSION, STARFISH SOFTWARE OR ANY
+ * OTHER SYNCML SPONSOR BE LIABLE TO ANY PARTY FOR ANY LOSS OF
+ * PROFITS, LOSS OF BUSINESS, LOSS OF USE OF DATA, INTERRUPTION OF
+ * BUSINESS, OR FOR DIRECT, INDIRECT, SPECIAL OR EXEMPLARY, INCIDENTAL,
+ * PUNITIVE OR CONSEQUENTIAL DAMAGES OF ANY KIND IN CONNECTION WITH
+ * THIS DOCUMENT OR THE INFORMATION CONTAINED HEREIN, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH LOSS OR DAMAGE.
+ *
+ * The above notice and this paragraph must be included on all copies
+ * of this document that are made.
+ *
+ */
+/*************************************************************************/
+/* Definitions */
+/*************************************************************************/
+
+#ifndef _XLT_ENC_COM_H
+#define _XLT_ENC_COM_H
+
+#include <smldef.h>
+#include <smldtd.h>
+#include <sml.h>
+
+#include "xlttags.h"
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** flags if a (WB)XML block is required or optional */
+typedef enum {
+ OPTIONAL = 0,
+ REQUIRED
+} XltRO_t;
+
+/** Tag Types (TT) - begin tags, end tags, ... */
+typedef enum {
+ TT_END = 0,
+ TT_BEG,
+ TT_ALL
+} XltTagType_t;
+
+/** Structure elements which are not directly relied to a tag */
+typedef enum {
+ ITEM_LIST,
+ TARGET_LIST,
+ SOURCE_LIST,
+ META_LIST,
+ MAPITEM_LIST,
+ TARGETREF_LIST,
+ SOURCEREF_LIST
+} XltListType_t;
+
+/** Type for storing free mem size evaluation information */
+typedef struct XltSpaceEvaluation_s
+{
+ MemSize_t written_bytes;
+ MemSize_t end_tag_size;
+ // %%% luz 2002-09-03: evaluation needs private shadow copies of these:
+ SmlPcdataExtension_t cur_ext;
+ SmlPcdataExtension_t last_ext;
+} XltSpaceEvaluation_t, *XltSpaceEvaluationPtr_t;
+
+
+typedef struct bufferMgmt_s
+{
+ MemPtr_t smlXltBufferP;
+ MemPtr_t smlXltStoreBufP;
+ MemSize_t smlXltWrittenBytes;
+ MemSize_t smlXltBufferLen;
+ XltTagID_t switchExtTag;
+ SmlPcdataExtension_t smlCurExt;
+ SmlPcdataExtension_t smlLastExt;
+ SmlPcdataExtension_t smlActiveExt;
+ MemSize_t endTagSize;
+ Boolean_t spaceEvaluation;
+ // %%% luz:2003-04-24: added syncmlvers
+ // %%% luz:2003-07-31: made it an enum, now called vers
+ SmlVersion_t vers;
+} BufferMgmt_t, *BufferMgmtPtr_t;
+
+Ret_t xltAddToBuffer(const MemPtr_t pContent, MemSize_t size, BufferMgmtPtr_t pBufMgr);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/syncml_tk/src/sml/xlt/all/xltencwbxml.c b/src/syncml_tk/src/sml/xlt/all/xltencwbxml.c
new file mode 100755
index 0000000..c04588f
--- /dev/null
+++ b/src/syncml_tk/src/sml/xlt/all/xltencwbxml.c
@@ -0,0 +1,299 @@
+/**
+ * @file
+ * The WBXML Encoder source file
+ *
+ * @target_system All
+ * @target_os All
+ */
+
+/*
+ * Copyright Notice
+ * Copyright (c) Ericsson, IBM, Lotus, Matsushita Communication
+ * Industrial Co., Ltd., Motorola, Nokia, Openwave Systems, Inc.,
+ * Palm, Inc., Psion, Starfish Software, Symbian, Ltd. (2001).
+ * All Rights Reserved.
+ * Implementation of all or part of any Specification may require
+ * licenses under third party intellectual property rights,
+ * including without limitation, patent rights (such a third party
+ * may or may not be a Supporter). The Sponsors of the Specification
+ * are not responsible and shall not be held responsible in any
+ * manner for identifying or failing to identify any or all such
+ * third party intellectual property rights.
+ *
+ * THIS DOCUMENT AND THE INFORMATION CONTAINED HEREIN ARE PROVIDED
+ * ON AN "AS IS" BASIS WITHOUT WARRANTY OF ANY KIND AND ERICSSON, IBM,
+ * LOTUS, MATSUSHITA COMMUNICATION INDUSTRIAL CO. LTD, MOTOROLA,
+ * NOKIA, PALM INC., PSION, STARFISH SOFTWARE AND ALL OTHER SYNCML
+ * SPONSORS DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+ * BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
+ * HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
+ * SHALL ERICSSON, IBM, LOTUS, MATSUSHITA COMMUNICATION INDUSTRIAL CO.,
+ * LTD, MOTOROLA, NOKIA, PALM INC., PSION, STARFISH SOFTWARE OR ANY
+ * OTHER SYNCML SPONSOR BE LIABLE TO ANY PARTY FOR ANY LOSS OF
+ * PROFITS, LOSS OF BUSINESS, LOSS OF USE OF DATA, INTERRUPTION OF
+ * BUSINESS, OR FOR DIRECT, INDIRECT, SPECIAL OR EXEMPLARY, INCIDENTAL,
+ * PUNITIVE OR CONSEQUENTIAL DAMAGES OF ANY KIND IN CONNECTION WITH
+ * THIS DOCUMENT OR THE INFORMATION CONTAINED HEREIN, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH LOSS OR DAMAGE.
+ *
+ * The above notice and this paragraph must be included on all copies
+ * of this document that are made.
+ *
+ */
+
+#include "syncml_tk_prefix_file.h" // %%% luz: needed for precompiled headers in eVC++
+
+#include "define.h"
+#ifdef __SML_WBXML__
+/*************************************************************************/
+/* Definitions */
+/*************************************************************************/
+
+#include "xltencwbxml.h"
+#include <libmem.h>
+#include <libstr.h>
+#include "xlttags.h"
+
+
+/**
+ * Converts a element type into its wbxml token
+ *
+ * @pre valid element type
+ * @post return of wbxml token
+ * @param elType (IN)
+ * element type
+ * @return wbxml token\n
+ * 0, if no matching wbxml token
+ */
+MemByte_t wbxmlGetGlobToken(XltElementType_t elType)
+{
+
+ typedef struct GlobTok_s
+ {
+ XltElementType_t id;
+ MemByte_t wbxml;
+ } GlobTok_t;
+
+ // encoding of global tokens; related to the type XML_ElementType_t
+ GlobTok_t globtoken[] =
+ {
+ { END, 0x01 }, //Tag End
+ { STR_I, 0x03 }, //Inline string
+ { OPAQUE, 0xC3 }, //Opaque Data
+ { UNDEF, 0x00 }
+ };
+
+ int i = -1;
+ while (globtoken[++i].id != UNDEF)
+ if (globtoken[i].id == elType)
+ return globtoken[i].wbxml;
+ return 0;
+
+}
+
+/**
+ * Converts a Long_t opaque size to a wbxml mb_u_int32 and adds it to the buffer
+ *
+ * @pre size of the content to be written as opaque datatype
+ * @post the size is converted to the mb_u_int32 representation and added
+ * to the buffer
+ * @param size (IN)
+ * length of the opaque data
+ * @param pBufMgr (IN/OUT)
+ * pointer to a structure containing buffer management elements
+ * @return shows error codes of function,\n
+ * 0, if OK
+ */
+Ret_t wbxmlOpaqueSize2Buf(Long_t size, BufferMgmtPtr_t pBufMgr)
+{
+ Long_t _thresholdcount = 1;
+ Long_t _bytesNeeded = 0;
+ MemPtr_t _byteArray;
+ MemPtr_t _tmpByteArray;
+ int i, j;
+ Ret_t _err;
+
+ //j max = number of bytes of size
+ for (j=1; j<=sizeof(size); j++)
+ {
+ //if the size of the content is smaller than the power of 128,j ->
+ //one more byte is needed in the mb_u_int32 representation of WBXML
+ _thresholdcount = _thresholdcount * 128;
+ if(size < _thresholdcount)
+ {
+ _bytesNeeded = j;
+ break;
+ }
+ }
+
+ if (pBufMgr->spaceEvaluation == 0)
+ {
+ //allocate number of bytes needed by the mb_u_int32 data type
+ if ((_byteArray = smlLibMalloc(_bytesNeeded)) == NULL) return SML_ERR_NOT_ENOUGH_SPACE;
+
+ _tmpByteArray = _byteArray;
+
+ //process al bytes in the mb_u_int32 data type
+ for (i=1; i<=_bytesNeeded; i++)
+ {
+ // the lowest byte needs a 0 in its highest bit -> no | 0x80
+ if ((_bytesNeeded - i) == 0)
+ {
+ *_tmpByteArray = ((unsigned char)(size & 0x7F));
+ }
+ // all the other byte needs a 1 in its highest bit -> | 0x80
+ else
+ {
+ // only the seven lower bits contain the size value -> >> 7
+ *_tmpByteArray = ((unsigned char)(((size >> (7 * (_bytesNeeded - i))) & 0x7F) | 0x80));
+ _tmpByteArray++;
+ }
+ }
+
+ _err = xltAddToBuffer(_byteArray, _bytesNeeded, pBufMgr);
+
+ smlLibFree(_byteArray);
+ } else {
+ pBufMgr->smlXltWrittenBytes += _bytesNeeded;
+ // %%% luz 2002-09-03: return value was missing here.
+ _err=SML_ERR_OK;
+ }
+
+ return _err;
+}
+
+/**
+ * Generates a tag for a given tag ID and a given tag type
+ *
+ * @pre valid parameters
+ * @post a new wbxml tag is written to the buffer
+ * @param tagId (IN)
+ * the ID for the tag to generate (TN_ADD, ...)
+ * @param tagType (IN)
+ * the tag type (e.g. Begin Tag -> TT_BEG, ...)
+ * @param pBufMgr (IN/OUT)
+ * pointer to a structure containing buffer management elements
+ * @return shows error codes of function,\n
+ * 0, if OK
+ */
+Ret_t wbxmlGenerateTag(XltTagID_t tagId, XltTagType_t tagType, BufferMgmtPtr_t pBufMgr )
+{
+
+ Ret_t _err = SML_ERR_OK;
+ MemByte_t _tmp = 0x00;
+
+ //check if content byte has to be added to the tag
+ switch (tagType)
+ {
+ //set the end tag
+ case TT_END:
+ {
+ _tmp = (MemByte_t)wbxmlGetGlobToken(END);
+ if (!_tmp) return SML_ERR_XLT_INVAL_TAG_TYPE;
+ _err = xltAddToBuffer((&_tmp), 1, pBufMgr);
+ // remember the number of byte that must follow for the according end-tag
+ if (_err == SML_ERR_OK) pBufMgr->endTagSize -= 1;
+ return _err;
+ }
+ //Begin and End Tag in one
+ case TT_ALL:
+ {
+ _err = (MemByte_t)getTagByte(tagId, pBufMgr->smlCurExt, &_tmp);
+ if ((!_tmp) || (_err != SML_ERR_OK)) return _err;
+ return xltAddToBuffer((MemPtr_t)(&_tmp), 1, pBufMgr);
+ }
+ //Only Begin Tag -> content follows -> content byte has to be added
+ case TT_BEG:
+ {
+ _err = (MemByte_t)getTagByte(tagId, pBufMgr->smlCurExt, &_tmp);
+ if ((!_tmp) || (_err != SML_ERR_OK)) return _err;
+
+ _tmp = ((MemByte_t)(_tmp | XLT_CONTBYTE));
+ _err = xltAddToBuffer(&_tmp, 1, pBufMgr);
+ // remember the number of byte that must follow for the according end-tag
+ if (_err == SML_ERR_OK) pBufMgr->endTagSize += 1;
+ return _err;
+ }
+ default:
+ return SML_ERR_XLT_INVAL_TAG_TYPE;
+ }
+
+ // return SML_ERR_OK;Unreachable
+}
+
+/**
+ * Write a content of a certain WBXML element type (e.g.\ STR_I)
+ * to the global buffer
+ *
+ * @pre valid parameters
+ * @post the content is written to the wbxml buffer with the leading
+ * bytes for the opaque data type or the STR_I data type
+ * @param pContent (IN)
+ * the character pointer referencing the content to
+ * write to the buffer
+ * @param elType (IN)
+ * the element type to write to the buffer (e.g. STR_I)
+ * @param size (IN)
+ * the content length
+ * @param pBufMgr (IN/OUT)
+ * pointer to a structure containing buffer management elements
+ * @return shows error codes of function,\n
+ * 0, if OK
+ */
+Ret_t wbxmlWriteTypeToBuffer(const MemPtr_t pContent, XltElementType_t elType, Long_t size, BufferMgmtPtr_t pBufMgr)
+{
+ Ret_t _err;
+
+ MemByte_t _termstr = XLT_TERMSTR;
+ MemByte_t _tmp;
+
+ switch((int)elType)
+ {
+ case TAG:
+ {
+
+ return (xltAddToBuffer(pContent, size, pBufMgr));
+
+ }
+ case STR_I:
+ {
+ _tmp = (MemByte_t)wbxmlGetGlobToken(STR_I);
+ if (!_tmp) return SML_ERR_XLT_WBXML_UKN_TOK;
+
+ //add the STR_I identifier
+ if ((_err = xltAddToBuffer(&_tmp, 1, pBufMgr)) != SML_ERR_OK) return _err;
+
+ //add the string to the buffer
+ if ((_err = xltAddToBuffer(pContent, (!pContent) ? 0 : smlLibStrlen((String_t)pContent), pBufMgr)) != SML_ERR_OK) return _err;
+
+ //add the string terminator '\0'
+ if ((_err = xltAddToBuffer(&_termstr, 1, pBufMgr)) != SML_ERR_OK) return _err;
+
+ return SML_ERR_OK;
+ }
+ case OPAQUE:
+ {
+ _tmp = (MemByte_t)wbxmlGetGlobToken(OPAQUE);
+ if (!_tmp) return SML_ERR_XLT_WBXML_UKN_TOK;
+
+
+
+ //add the OPAQUE identifier
+ if ((_err = xltAddToBuffer(&_tmp, 1, pBufMgr)) != SML_ERR_OK) return _err;
+
+ //add the pContent length
+ if ((_err = wbxmlOpaqueSize2Buf(size, pBufMgr)) != SML_ERR_OK) return _err;
+
+ //add the string buffer
+ if ((_err = xltAddToBuffer(pContent, size, pBufMgr)) != SML_ERR_OK) return _err;
+
+ return SML_ERR_OK;
+ }
+ default:
+ return SML_ERR_XLT_INVAL_PCDATA_TYPE;
+ }
+
+// return SML_ERR_OK;unreachable
+}
+#endif
diff --git a/src/syncml_tk/src/sml/xlt/all/xltencwbxml.h b/src/syncml_tk/src/sml/xlt/all/xltencwbxml.h
new file mode 100755
index 0000000..b6347aa
--- /dev/null
+++ b/src/syncml_tk/src/sml/xlt/all/xltencwbxml.h
@@ -0,0 +1,112 @@
+/**
+ * @file
+ * The WBXML Encoder header file
+ *
+ * @target_system All
+ * @target_os All
+ */
+
+/*
+ * Copyright Notice
+ * Copyright (c) Ericsson, IBM, Lotus, Matsushita Communication
+ * Industrial Co., Ltd., Motorola, Nokia, Openwave Systems, Inc.,
+ * Palm, Inc., Psion, Starfish Software, Symbian, Ltd. (2001).
+ * All Rights Reserved.
+ * Implementation of all or part of any Specification may require
+ * licenses under third party intellectual property rights,
+ * including without limitation, patent rights (such a third party
+ * may or may not be a Supporter). The Sponsors of the Specification
+ * are not responsible and shall not be held responsible in any
+ * manner for identifying or failing to identify any or all such
+ * third party intellectual property rights.
+ *
+ * THIS DOCUMENT AND THE INFORMATION CONTAINED HEREIN ARE PROVIDED
+ * ON AN "AS IS" BASIS WITHOUT WARRANTY OF ANY KIND AND ERICSSON, IBM,
+ * LOTUS, MATSUSHITA COMMUNICATION INDUSTRIAL CO. LTD, MOTOROLA,
+ * NOKIA, PALM INC., PSION, STARFISH SOFTWARE AND ALL OTHER SYNCML
+ * SPONSORS DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+ * BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
+ * HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
+ * SHALL ERICSSON, IBM, LOTUS, MATSUSHITA COMMUNICATION INDUSTRIAL CO.,
+ * LTD, MOTOROLA, NOKIA, PALM INC., PSION, STARFISH SOFTWARE OR ANY
+ * OTHER SYNCML SPONSOR BE LIABLE TO ANY PARTY FOR ANY LOSS OF
+ * PROFITS, LOSS OF BUSINESS, LOSS OF USE OF DATA, INTERRUPTION OF
+ * BUSINESS, OR FOR DIRECT, INDIRECT, SPECIAL OR EXEMPLARY, INCIDENTAL,
+ * PUNITIVE OR CONSEQUENTIAL DAMAGES OF ANY KIND IN CONNECTION WITH
+ * THIS DOCUMENT OR THE INFORMATION CONTAINED HEREIN, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH LOSS OR DAMAGE.
+ *
+ * The above notice and this paragraph must be included on all copies
+ * of this document that are made.
+ *
+ */
+
+/*************************************************************************/
+/* Definitions */
+/*************************************************************************/
+#ifndef _XLT_ENC_WBXML_H
+#define _XLT_ENC_WBXML_H
+
+#include <smlerr.h>
+#include <smldef.h>
+#include <smldtd.h>
+#include "xlttags.h"
+#include "xltenccom.h"
+
+#define test 1
+
+/** byte for WBXML String Table Length - not yet implemented yet -> 0x00 */
+#define XLT_STABLEN 0x00
+
+/** byte for WBXML charset - not yet implemented - default UTF-8 */
+#define XLT_CHARSET 0x6A
+
+/** byte for WBXML Version Number */
+#define XLT_WBXMLVER 0x02
+
+/** byte to add to a tag if a content follows */
+#define XLT_CONTBYTE 0x40
+
+/** byte to add to a tag if an attribute follows */
+#define XLT_ATTRBYTE 0x80
+
+/** termination character for certain WBXML element types (e.g.\ STR_I) */
+#define XLT_TERMSTR 0x00
+
+/** public identifier 0x00,0x00 -> unknown, use stringtable */
+#define XLT_PUBIDENT1 0x00
+#define XLT_PUBIDENT2 0x00
+
+// %%% luz: 2003-07-31: now in xltenc.c's SyncMLFPI table
+//#define XLT_DTD_ID "-//SYNCML//DTD SyncML 1.0//EN"
+
+/** switch page tag 0x00 */
+#define XLT_SWITCHPAGE 0x00
+
+/** default codepage */
+#define XLT_DEFAULTCODEPAGE 0x00
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** global tokens in WBXML */
+typedef enum {
+ UNDEF = 0,
+ END,
+ STR_I,
+ OPAQUE,
+ TAG
+} XltElementType_t;
+
+Ret_t wbxmlGenerateTag(XltTagID_t tagId, XltTagType_t tagType, BufferMgmtPtr_t pBufMgr);
+Ret_t wbxmlWriteTypeToBuffer(const MemPtr_t pContent, XltElementType_t elType, Long_t size, BufferMgmtPtr_t pBufMgr);
+Ret_t wbxmlOpaqueSize2Buf(Long_t size, BufferMgmtPtr_t pBufMgr);
+MemByte_t wbxmlGetGlobToken(XltElementType_t elType);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/syncml_tk/src/sml/xlt/all/xltencxml.c b/src/syncml_tk/src/sml/xlt/all/xltencxml.c
new file mode 100755
index 0000000..07852a4
--- /dev/null
+++ b/src/syncml_tk/src/sml/xlt/all/xltencxml.c
@@ -0,0 +1,171 @@
+/**
+ * @file
+ * The XML Encoder source file
+ *
+ * @target_system All
+ * @target_os All
+ */
+
+/*
+ * Copyright Notice
+ * Copyright (c) Ericsson, IBM, Lotus, Matsushita Communication
+ * Industrial Co., Ltd., Motorola, Nokia, Openwave Systems, Inc.,
+ * Palm, Inc., Psion, Starfish Software, Symbian, Ltd. (2001).
+ * All Rights Reserved.
+ * Implementation of all or part of any Specification may require
+ * licenses under third party intellectual property rights,
+ * including without limitation, patent rights (such a third party
+ * may or may not be a Supporter). The Sponsors of the Specification
+ * are not responsible and shall not be held responsible in any
+ * manner for identifying or failing to identify any or all such
+ * third party intellectual property rights.
+ *
+ * THIS DOCUMENT AND THE INFORMATION CONTAINED HEREIN ARE PROVIDED
+ * ON AN "AS IS" BASIS WITHOUT WARRANTY OF ANY KIND AND ERICSSON, IBM,
+ * LOTUS, MATSUSHITA COMMUNICATION INDUSTRIAL CO. LTD, MOTOROLA,
+ * NOKIA, PALM INC., PSION, STARFISH SOFTWARE AND ALL OTHER SYNCML
+ * SPONSORS DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+ * BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
+ * HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
+ * SHALL ERICSSON, IBM, LOTUS, MATSUSHITA COMMUNICATION INDUSTRIAL CO.,
+ * LTD, MOTOROLA, NOKIA, PALM INC., PSION, STARFISH SOFTWARE OR ANY
+ * OTHER SYNCML SPONSOR BE LIABLE TO ANY PARTY FOR ANY LOSS OF
+ * PROFITS, LOSS OF BUSINESS, LOSS OF USE OF DATA, INTERRUPTION OF
+ * BUSINESS, OR FOR DIRECT, INDIRECT, SPECIAL OR EXEMPLARY, INCIDENTAL,
+ * PUNITIVE OR CONSEQUENTIAL DAMAGES OF ANY KIND IN CONNECTION WITH
+ * THIS DOCUMENT OR THE INFORMATION CONTAINED HEREIN, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH LOSS OR DAMAGE.
+ *
+ * The above notice and this paragraph must be included on all copies
+ * of this document that are made.
+ *
+ */
+
+#include "syncml_tk_prefix_file.h" // %%% luz: needed for precompiled headers in eVC++
+
+#include "define.h"
+#ifdef __SML_XML__
+/*************************************************************************/
+/* Definitions */
+/*************************************************************************/
+
+#include "xltencxml.h"
+#include <libstr.h>
+#include "xlttags.h"
+#include <libmem.h>
+
+/**
+ * Generates a XML tag
+ *
+ * @pre valid parameters
+ * @post the XML tag is written to the XML buffer
+ * @param tagId (IN)
+ * the ID for the tag to generate (TN_ADD, ...)
+ * @param tagType (IN)
+ * the tag type (e.g. Begin Tag -> TT_BEG, ...)
+ * @param attFlag (IN)
+ * indicates if the encoded tag contain Attributes in namespace
+ * extensions
+ * @param pBufMgr (IN/OUT)
+ * pointer to a structure containing buffer management elements
+ * @return shows error codes of function,\n
+ * 0, if OK
+ */
+Ret_t xmlGenerateTag(XltTagID_t tagId, XltTagType_t tagType, BufferMgmtPtr_t pBufMgr, SmlPcdataExtension_t attFlag)
+{
+ Ret_t _err;
+
+ MemByte_t _begpar = XML_BEGPAR;
+ MemByte_t _tagdel = XML_TAGDEL;
+ MemByte_t _endpar = XML_ENDPAR;
+ MemByte_t _nstagstart[] = XML_NSSTART;
+ MemByte_t _nstagend[] = XML_NSEND;
+
+
+ String_t _tagstr;
+ String_t _tagnsattr = NULL;
+
+ if ((_tagstr = (String_t)smlLibMalloc(XML_MAX_TAGLEN)) == NULL) return SML_ERR_NOT_ENOUGH_SPACE;
+
+ if ((_err = getTagString(tagId, _tagstr, attFlag)) != SML_ERR_OK) {
+ smlLibFree(_tagstr);
+ return _err;
+ }
+
+ if (!_tagstr) { // check again as _tagstr might be alterd in getTagString
+ smlLibFree(_tagstr);
+ return SML_ERR_XLT_INVAL_TAG_TYPE;
+ }
+
+ /* the <SyncML> tag _must_ have an xmlns attribute */
+ if (attFlag != pBufMgr->smlActiveExt || tagId == TN_SYNCML) {
+ // %%% luz:2003-07-31: now uses namespace from table according to version
+ if (getExtName(attFlag, &_tagnsattr, pBufMgr->vers) != SML_ERR_OK) {
+ smlLibFree(_tagstr);
+ return SML_ERR_XLT_INVAL_TAG_TYPE;
+ }
+ }
+ pBufMgr->smlActiveExt = attFlag;
+ //check if content byte has to be added to the tag
+ switch (tagType)
+ {
+ // set the end tag
+ case TT_END:
+ {
+ if ((_err = xltAddToBuffer((MemPtr_t)(&_begpar), 1, pBufMgr)) != SML_ERR_OK) break;
+ if ((_err = xltAddToBuffer((MemPtr_t)(&_tagdel), 1, pBufMgr)) != SML_ERR_OK) break;
+ if ((_err = xltAddToBuffer((MemPtr_t)_tagstr, smlLibStrlen(_tagstr), pBufMgr)) != SML_ERR_OK) break;
+ if ((_err = xltAddToBuffer((MemPtr_t)(&_endpar), 1, pBufMgr)) != SML_ERR_OK) break;
+ if (tagId == pBufMgr->switchExtTag) {
+ pBufMgr->smlActiveExt = pBufMgr->smlLastExt;
+ pBufMgr->smlCurExt = pBufMgr->smlLastExt;
+ pBufMgr->smlLastExt = attFlag;
+ }
+ // just forget the stored number ob bytes for this end-tag since written now
+ pBufMgr->endTagSize -= (3 + smlLibStrlen(_tagstr));
+ break;
+ }
+ //Empty tag
+ case TT_ALL:
+ {
+ if ((_err = xltAddToBuffer((MemPtr_t)(&_begpar), 1, pBufMgr)) != SML_ERR_OK) break;
+ if ((_err = xltAddToBuffer((MemPtr_t)_tagstr, smlLibStrlen(_tagstr), pBufMgr)) != SML_ERR_OK) break;
+ if (_tagnsattr) {
+ if ((_err = xltAddToBuffer((MemPtr_t)(&_nstagstart), 8, pBufMgr)) != SML_ERR_OK) break;
+ if ((_err = xltAddToBuffer((MemPtr_t)_tagnsattr, smlLibStrlen(_tagnsattr), pBufMgr)) != SML_ERR_OK) break;
+ if ((_err = xltAddToBuffer((MemPtr_t)&_nstagend, 1, pBufMgr)) != SML_ERR_OK) break;
+ }
+ if ((_err = xltAddToBuffer((MemPtr_t)(&_tagdel), 1, pBufMgr)) != SML_ERR_OK) break;
+ if ((_err = xltAddToBuffer((MemPtr_t)(&_endpar), 1, pBufMgr)) != SML_ERR_OK) break;
+
+ break;
+ }
+ //Only Begin Tag -> content follows -> content byte has to be added
+ case TT_BEG:
+ {
+ if ((_err = xltAddToBuffer((MemPtr_t)(&_begpar), 1, pBufMgr)) != SML_ERR_OK) break;
+ if ((_err = xltAddToBuffer((MemPtr_t)_tagstr, smlLibStrlen(_tagstr), pBufMgr)) != SML_ERR_OK) break;
+ if (_tagnsattr) {
+ if ((_err = xltAddToBuffer((MemPtr_t)&_nstagstart, 8, pBufMgr)) != SML_ERR_OK) break;
+ if ((_err = xltAddToBuffer((MemPtr_t)_tagnsattr, smlLibStrlen(_tagnsattr), pBufMgr)) != SML_ERR_OK) break;
+ if ((_err = xltAddToBuffer((MemPtr_t)&_nstagend, 1, pBufMgr)) != SML_ERR_OK) break;
+ }
+ if ((_err = xltAddToBuffer((MemPtr_t)(&_endpar), 1, pBufMgr)) != SML_ERR_OK) break;
+
+ // remember the number of byte that must follow for the according end-tag
+ pBufMgr->endTagSize += (3 + smlLibStrlen(_tagstr));
+ break;
+ }
+ default:
+ {
+ smlLibFree(_tagstr);
+ smlLibFree(_tagnsattr);
+ return SML_ERR_XLT_INVAL_TAG_TYPE;
+ }
+ }
+ smlLibFree(_tagstr);
+ smlLibFree(_tagnsattr);
+ return _err;
+}
+#endif
diff --git a/src/syncml_tk/src/sml/xlt/all/xltencxml.h b/src/syncml_tk/src/sml/xlt/all/xltencxml.h
new file mode 100755
index 0000000..37fceaa
--- /dev/null
+++ b/src/syncml_tk/src/sml/xlt/all/xltencxml.h
@@ -0,0 +1,86 @@
+/**
+ * @file
+ * The XML Encoder header file
+ *
+ * @target_system All
+ * @target_os All
+ */
+
+/*
+ * Copyright Notice
+ * Copyright (c) Ericsson, IBM, Lotus, Matsushita Communication
+ * Industrial Co., Ltd., Motorola, Nokia, Openwave Systems, Inc.,
+ * Palm, Inc., Psion, Starfish Software, Symbian, Ltd. (2001).
+ * All Rights Reserved.
+ * Implementation of all or part of any Specification may require
+ * licenses under third party intellectual property rights,
+ * including without limitation, patent rights (such a third party
+ * may or may not be a Supporter). The Sponsors of the Specification
+ * are not responsible and shall not be held responsible in any
+ * manner for identifying or failing to identify any or all such
+ * third party intellectual property rights.
+ *
+ * THIS DOCUMENT AND THE INFORMATION CONTAINED HEREIN ARE PROVIDED
+ * ON AN "AS IS" BASIS WITHOUT WARRANTY OF ANY KIND AND ERICSSON, IBM,
+ * LOTUS, MATSUSHITA COMMUNICATION INDUSTRIAL CO. LTD, MOTOROLA,
+ * NOKIA, PALM INC., PSION, STARFISH SOFTWARE AND ALL OTHER SYNCML
+ * SPONSORS DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+ * BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
+ * HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
+ * SHALL ERICSSON, IBM, LOTUS, MATSUSHITA COMMUNICATION INDUSTRIAL CO.,
+ * LTD, MOTOROLA, NOKIA, PALM INC., PSION, STARFISH SOFTWARE OR ANY
+ * OTHER SYNCML SPONSOR BE LIABLE TO ANY PARTY FOR ANY LOSS OF
+ * PROFITS, LOSS OF BUSINESS, LOSS OF USE OF DATA, INTERRUPTION OF
+ * BUSINESS, OR FOR DIRECT, INDIRECT, SPECIAL OR EXEMPLARY, INCIDENTAL,
+ * PUNITIVE OR CONSEQUENTIAL DAMAGES OF ANY KIND IN CONNECTION WITH
+ * THIS DOCUMENT OR THE INFORMATION CONTAINED HEREIN, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH LOSS OR DAMAGE.
+ *
+ * The above notice and this paragraph must be included on all copies
+ * of this document that are made.
+ *
+ */
+
+/*************************************************************************/
+/* Definitions */
+/*************************************************************************/
+#ifdef __SML_XML__
+#ifndef _XLT_ENC_XML_H
+#define _XLT_ENC_XML_H
+
+#include <smlerr.h>
+#include "xlttags.h"
+#include "xltenccom.h"
+
+/** byte for XML tag begin parentheses */
+#define XML_BEGPAR '<'
+
+/** byte for XML tag end parentheses */
+#define XML_ENDPAR '>'
+
+/** byte for XML tag del */
+#define XML_TAGDEL '/'
+
+/** XML version */
+#define XML_VERSION "1.0"
+
+/** XML encoding */
+#define XML_ENCODING "UTF-8"
+
+/** XML namespaceattribute */
+#define XML_NSSTART " xmlns='"
+#define XML_NSEND "'"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+Ret_t xmlGenerateTag(XltTagID_t tagId, XltTagType_t tagType, BufferMgmtPtr_t pBufMgr, SmlPcdataExtension_t attFlag);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+#endif
diff --git a/src/syncml_tk/src/sml/xlt/all/xltmetinf.c b/src/syncml_tk/src/sml/xlt/all/xltmetinf.c
new file mode 100755
index 0000000..f37a470
--- /dev/null
+++ b/src/syncml_tk/src/sml/xlt/all/xltmetinf.c
@@ -0,0 +1,362 @@
+/**
+ * @file
+ * MetaInf DTD related functions for the en-/decoder
+ *
+ * @target_system all
+ * @target_os all
+ */
+
+/*
+ * Copyright Notice
+ * Copyright (c) Ericsson, IBM, Lotus, Matsushita Communication
+ * Industrial Co., Ltd., Motorola, Nokia, Openwave Systems, Inc.,
+ * Palm, Inc., Psion, Starfish Software, Symbian, Ltd. (2001).
+ * All Rights Reserved.
+ * Implementation of all or part of any Specification may require
+ * licenses under third party intellectual property rights,
+ * including without limitation, patent rights (such a third party
+ * may or may not be a Supporter). The Sponsors of the Specification
+ * are not responsible and shall not be held responsible in any
+ * manner for identifying or failing to identify any or all such
+ * third party intellectual property rights.
+ *
+ * THIS DOCUMENT AND THE INFORMATION CONTAINED HEREIN ARE PROVIDED
+ * ON AN "AS IS" BASIS WITHOUT WARRANTY OF ANY KIND AND ERICSSON, IBM,
+ * LOTUS, MATSUSHITA COMMUNICATION INDUSTRIAL CO. LTD, MOTOROLA,
+ * NOKIA, PALM INC., PSION, STARFISH SOFTWARE AND ALL OTHER SYNCML
+ * SPONSORS DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+ * BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
+ * HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
+ * SHALL ERICSSON, IBM, LOTUS, MATSUSHITA COMMUNICATION INDUSTRIAL CO.,
+ * LTD, MOTOROLA, NOKIA, PALM INC., PSION, STARFISH SOFTWARE OR ANY
+ * OTHER SYNCML SPONSOR BE LIABLE TO ANY PARTY FOR ANY LOSS OF
+ * PROFITS, LOSS OF BUSINESS, LOSS OF USE OF DATA, INTERRUPTION OF
+ * BUSINESS, OR FOR DIRECT, INDIRECT, SPECIAL OR EXEMPLARY, INCIDENTAL,
+ * PUNITIVE OR CONSEQUENTIAL DAMAGES OF ANY KIND IN CONNECTION WITH
+ * THIS DOCUMENT OR THE INFORMATION CONTAINED HEREIN, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH LOSS OR DAMAGE.
+ *
+ * The above notice and this paragraph must be included on all copies
+ * of this document that are made.
+ *
+ */
+#include "syncml_tk_prefix_file.h" // %%% luz: needed for precompiled headers in eVC++
+
+#include "define.h"
+#ifdef __USE_METINF__
+
+#include "smlmetinfdtd.h"
+#include "xlttags.h"
+#include "xltmetinf.h"
+#include "xlttagtbl.h"
+#include "xltenc.h"
+#include "xltencwbxml.h"
+
+#include "define.h"
+#include "libstr.h"
+#include "smlerr.h"
+#include "smldtd.h"
+#include "libmem.h"
+#include "libutil.h"
+/* extern */
+ extern SML_API void smlFreeMetinfAnchor(SmlMetInfAnchorPtr_t data) XLT_FUNC;
+ extern SML_API void smlFreeMetinfMem(SmlMetInfMemPtr_t data) XLT_FUNC;
+ extern SML_API void smlFreeMetinfMetinf(SmlMetInfMetInfPtr_t data) XLT_FUNC;
+
+
+/* decoder callbacks */
+Ret_t buildMetInfAnchorCmd(XltDecoderPtr_t pDecoder, VoidPtr_t *ppElem) {
+ XltDecScannerPtr_t pScanner;
+ SmlMetInfAnchorPtr_t pAnchor;
+ Ret_t rc;
+
+ pScanner = pDecoder->scanner;
+
+ if (*ppElem != NULL)
+ return SML_ERR_XLT_INVAL_SYNCML_DOC;
+
+ if ((pAnchor = (SmlMetInfAnchorPtr_t)smlLibMalloc(sizeof(SmlMetInfAnchor_t))) == NULL)
+ return SML_ERR_NOT_ENOUGH_SPACE;
+ smlLibMemset(pAnchor, 0, sizeof(SmlMetInfAnchor_t));
+
+ if (IS_EMPTY(pScanner->curtok)) {
+ *ppElem = pAnchor;
+ return SML_ERR_OK;
+ }
+
+ if (((rc = nextToken(pDecoder)) != SML_ERR_OK)) {
+ smlFreeMetinfAnchor(pAnchor);
+ //smlLibFree(pAnchor);
+ return rc;
+ }
+
+ while (pScanner->curtok->type != TOK_TAG_END) {
+ switch (pScanner->curtok->tagid) {
+ /* PCDATA elements */
+ case TN_METINF_LAST:
+ rc = buildPCData(pDecoder, (VoidPtr_t)&pAnchor->last);
+ break;
+ case TN_METINF_NEXT:
+ rc = buildPCData(pDecoder, (VoidPtr_t)&pAnchor->next);
+ break;
+ default:
+ rc = SML_ERR_XLT_INVAL_SYNCML_DOC;
+ }
+ if (rc != SML_ERR_OK) {
+ smlFreeMetinfAnchor(pAnchor);
+ //smlLibFree(pAnchor);
+ return rc;
+ }
+ if (((rc = nextToken(pDecoder)) != SML_ERR_OK)) {
+ smlFreeMetinfAnchor(pAnchor);
+ //smlLibFree(pAnchor);
+ return rc;
+ }
+ }
+ *ppElem = pAnchor;
+
+ return SML_ERR_OK;
+}
+
+Ret_t buildMetInfMemCmd(XltDecoderPtr_t pDecoder, VoidPtr_t *ppElem) {
+ XltDecScannerPtr_t pScanner;
+ SmlMetInfMemPtr_t pMem;
+ Ret_t rc;
+
+ pScanner = pDecoder->scanner;
+
+ if (*ppElem != NULL)
+ return SML_ERR_XLT_INVAL_SYNCML_DOC;
+
+ if ((pMem = (SmlMetInfMemPtr_t)smlLibMalloc(sizeof(SmlMetInfMem_t))) == NULL)
+ return SML_ERR_NOT_ENOUGH_SPACE;
+ smlLibMemset(pMem, 0, sizeof(SmlMetInfMem_t));
+
+ if (IS_EMPTY(pScanner->curtok)) {
+ *ppElem = pMem;
+ return SML_ERR_OK;
+ }
+
+ if (((rc = nextToken(pDecoder)) != SML_ERR_OK)) {
+ smlFreeMetinfMem(pMem);
+ //smlLibFree(pMem);
+ return rc;
+ }
+
+ while (pScanner->curtok->type != TOK_TAG_END) {
+ switch (pScanner->curtok->tagid) {
+ /* %%% luz 2005-08-24 :this definition was plain wrong - shared is a flag, not Pcdata!
+ case TN_METINF_SHAREDMEM:
+ rc = buildPCData(pDecoder, (VoidPtr_t)&pMem->shared);
+ break; */
+
+ /* PCDATA elements */
+ case TN_METINF_FREEMEM:
+ rc = buildPCData(pDecoder, (VoidPtr_t)&pMem->free);
+ break;
+ case TN_METINF_FREEID:
+ rc = buildPCData(pDecoder, (VoidPtr_t)&pMem->freeid);
+ break;
+
+ /* flags */
+ case TN_METINF_SHAREDMEM:
+ pMem->flags |= SmlMetInfSharedMem_f;
+ rc = buildEmptyTag(pDecoder); // allow for <tag></tag> instead of <tag/>
+ break;
+
+ default:
+ rc = SML_ERR_XLT_INVAL_SYNCML_DOC;
+ }
+ if (rc != SML_ERR_OK) {
+ smlFreeMetinfMem(pMem);
+ //smlLibFree(pMem);
+ return rc;
+ }
+ if (((rc = nextToken(pDecoder)) != SML_ERR_OK)) {
+ smlFreeMetinfMem(pMem);
+ //smlLibFree(pMem);
+ return rc;
+ }
+ }
+ *ppElem = pMem;
+
+ return SML_ERR_OK;
+}
+
+Ret_t buildMetInfMetInfCmd(XltDecoderPtr_t pDecoder, VoidPtr_t *ppElem) {
+ XltDecScannerPtr_t pScanner;
+ SmlMetInfMetInfPtr_t pMeta;
+ Ret_t rc;
+ int foundWrapper = 0;
+
+ pScanner = pDecoder->scanner;
+
+ if (*ppElem != NULL)
+ return SML_ERR_XLT_INVAL_SYNCML_DOC;
+
+ if ((pMeta = (SmlMetInfMetInfPtr_t)smlLibMalloc(sizeof(SmlMetInfMetInf_t))) == NULL)
+ return SML_ERR_NOT_ENOUGH_SPACE;
+ smlLibMemset(pMeta, 0, sizeof(SmlMetInfMetInf_t));
+
+ if (IS_EMPTY(pScanner->curtok)) {
+ *ppElem = pMeta;
+ return SML_ERR_OK;
+ }
+
+ if (((rc = nextToken(pDecoder)) != SML_ERR_OK)) {
+ smlFreeMetinfMetinf(pMeta);
+ //smlLibFree(pMeta);
+ return rc;
+ }
+
+ while (pScanner->curtok->type != TOK_TAG_END) {
+ switch (pScanner->curtok->tagid) {
+ case TN_METINF_METINF: /* ignore - it's just the wrapper tag */
+ foundWrapper = 1;
+ break;
+ case TN_METINF_FORMAT:
+ rc = buildPCData(pDecoder, (VoidPtr_t)&pMeta->format);
+ break;
+ case TN_METINF_TYPE:
+ rc = buildPCData(pDecoder, (VoidPtr_t)&pMeta->type);
+ break;
+ case TN_METINF_MARK:
+ rc = buildPCData(pDecoder, (VoidPtr_t)&pMeta->mark);
+ break;
+ case TN_METINF_SIZE:
+ rc = buildPCData(pDecoder, (VoidPtr_t)&pMeta->size);
+ break;
+ case TN_METINF_VERSION:
+ rc = buildPCData(pDecoder, (VoidPtr_t)&pMeta->version);
+ break;
+ case TN_METINF_NEXTNONCE:
+ rc = buildPCData(pDecoder, (VoidPtr_t)&pMeta->nextnonce);
+ break;
+ case TN_METINF_ANCHOR:
+ rc = buildMetInfAnchorCmd(pDecoder, (VoidPtr_t)&pMeta->anchor);
+ break;
+ case TN_METINF_MAXMSGSIZE:
+ rc = buildPCData(pDecoder, (VoidPtr_t)&pMeta->maxmsgsize);
+ break;
+ /* SCTSTK - 18/03/2002 S.H. 2002-04-05: SyncML 1.1 */
+ case TN_METINF_MAXOBJSIZE:
+ rc = buildPCData(pDecoder, (VoidPtr_t)&pMeta->maxobjsize);
+ break;
+ case TN_METINF_MEM:
+ rc = buildMetInfMemCmd(pDecoder, (VoidPtr_t)&pMeta->mem);
+ break;
+ case TN_METINF_EMI:
+ rc = buildPCDataList(pDecoder, (VoidPtr_t)&pMeta->emi);
+ break;
+
+ /* SyncML DS 1.2, Synthesis/luz 2005-08-24 */
+ case TN_METINF_FIELDLEVEL:
+ pMeta->flags |= SmlMetInfFieldLevel_f;
+ rc = buildEmptyTag(pDecoder); // allow for <tag></tag> instead of <tag/>
+ break;
+
+ default:
+ rc = SML_ERR_XLT_INVAL_SYNCML_DOC;
+ }
+ if (rc != SML_ERR_OK) {
+ smlFreeMetinfMetinf(pMeta);
+ //smlLibFree(pMeta);
+ return rc;
+ }
+ if (((rc = nextToken(pDecoder)) != SML_ERR_OK)) {
+ smlFreeMetinfMetinf(pMeta);
+ //smlLibFree(pMeta);
+ return rc;
+ }
+ }
+
+ if (foundWrapper) {
+ /* Optional Metinf root tag was used in this message.
+ * The actual token is the closing root tag.
+ * It is required that the scanner points to the first tag _after_
+ * <MetInf>...</MetInf>, so we just skip to the next token and continue.
+ */
+ if (((rc = nextToken(pDecoder)) != SML_ERR_OK)) {
+ smlFreeMetinfMetinf(pMeta);
+ //smlLibFree(pMeta);
+ return rc;
+ }
+ }
+ *ppElem = pMeta;
+
+ return SML_ERR_OK;
+}
+
+
+
+/* see xltenc.c:XltEncBlock for description of parameters */
+Ret_t metinfEncBlock(XltTagID_t tagId, XltRO_t reqOptFlag, const VoidPtr_t pContent, SmlEncoding_t enc, BufferMgmtPtr_t pBufMgr, SmlPcdataExtension_t attFlag) {
+ //Return variable
+ Ret_t _err;
+ SmlPcdataListPtr_t pList = NULL;
+ //Check if pContent of a required field is missing
+ if ((reqOptFlag == REQUIRED) && (pContent == NULL))
+ return SML_ERR_XLT_MISSING_CONT;
+ //Check if pContent of a optional field is missing -> if yes we are done
+ else if (pContent == NULL)
+ return SML_ERR_OK;
+
+ //Generate the commands -> see DTD
+ switch (tagId) {
+ case TN_METINF_ANCHOR:
+ if ((_err = xltGenerateTag(TN_METINF_ANCHOR, TT_BEG, enc, pBufMgr, SML_EXT_METINF)) != SML_ERR_OK) return _err;
+ if ((_err = metinfEncBlock(TN_METINF_LAST, OPTIONAL, ((SmlMetInfAnchorPtr_t) pContent)->last, enc, pBufMgr, SML_EXT_METINF)) != SML_ERR_OK) return _err;
+ if ((_err = metinfEncBlock(TN_METINF_NEXT, REQUIRED, ((SmlMetInfAnchorPtr_t) pContent)->next, enc, pBufMgr, SML_EXT_METINF)) != SML_ERR_OK) return _err;
+ if ((_err = xltGenerateTag(TN_METINF_ANCHOR, TT_END, enc, pBufMgr, SML_EXT_METINF)) != SML_ERR_OK) return _err;
+ break;
+ case TN_METINF_MEM:
+ if ((_err = xltGenerateTag(TN_METINF_MEM, TT_BEG, enc, pBufMgr, SML_EXT_METINF)) != SML_ERR_OK) return _err;
+ if ((_err = metinfEncBlock(TN_METINF_SHAREDMEM, OPTIONAL, &(((SmlMetInfMemPtr_t) pContent)->flags), enc, pBufMgr, SML_EXT_METINF)) != SML_ERR_OK) return _err;
+ if ((_err = metinfEncBlock(TN_METINF_FREEMEM, REQUIRED, ((SmlMetInfMemPtr_t) pContent)->free, enc, pBufMgr, SML_EXT_METINF)) != SML_ERR_OK) return _err;
+ if ((_err = metinfEncBlock(TN_METINF_FREEID, REQUIRED, ((SmlMetInfMemPtr_t) pContent)->freeid, enc, pBufMgr, SML_EXT_METINF)) != SML_ERR_OK) return _err;
+ if ((_err = xltGenerateTag(TN_METINF_MEM, TT_END, enc, pBufMgr, SML_EXT_METINF)) != SML_ERR_OK) return _err;
+ break;
+ case TN_METINF_SHAREDMEM:
+ //set the flag in the (WB)XML document if the flag is in the pContent
+ if ((*((Flag_t *) pContent)) & (SmlMetInfSharedMem_f))
+ if ((_err = xltGenerateTag(tagId, TT_ALL, enc, pBufMgr, SML_EXT_METINF)) != SML_ERR_OK) return _err;
+ break;
+ case TN_METINF_FIELDLEVEL:
+ //set the flag in the (WB)XML document if the flag is in the pContent
+ if ((*((Flag_t *) pContent)) & (SmlMetInfFieldLevel_f))
+ if ((_err = xltGenerateTag(tagId, TT_ALL, enc, pBufMgr, SML_EXT_METINF)) != SML_ERR_OK) return _err;
+ break;
+ case TN_METINF_METINF:
+ //if ((_err = xltGenerateTag(TN_METINF_METINF, TT_BEG, enc, pBufMgr, SML_EXT_METINF)) != SML_ERR_OK) return _err;
+ if ((_err = metinfEncBlock(TN_METINF_FORMAT, OPTIONAL, ((SmlMetInfMetInfPtr_t) pContent)->format, enc, pBufMgr, SML_EXT_METINF)) != SML_ERR_OK) return _err;
+ if ((_err = metinfEncBlock(TN_METINF_TYPE, OPTIONAL, ((SmlMetInfMetInfPtr_t) pContent)->type, enc, pBufMgr, SML_EXT_METINF)) != SML_ERR_OK) return _err;
+ if ((_err = metinfEncBlock(TN_METINF_MARK, OPTIONAL, ((SmlMetInfMetInfPtr_t) pContent)->mark, enc, pBufMgr, SML_EXT_METINF)) != SML_ERR_OK) return _err;
+ if ((_err = metinfEncBlock(TN_METINF_SIZE, OPTIONAL, ((SmlMetInfMetInfPtr_t) pContent)->size, enc, pBufMgr, SML_EXT_METINF)) != SML_ERR_OK) return _err;
+ if ((_err = metinfEncBlock(TN_METINF_ANCHOR, OPTIONAL, ((SmlMetInfMetInfPtr_t) pContent)->anchor, enc, pBufMgr, SML_EXT_METINF)) != SML_ERR_OK) return _err;
+ if ((_err = metinfEncBlock(TN_METINF_VERSION, OPTIONAL, ((SmlMetInfMetInfPtr_t) pContent)->version, enc, pBufMgr, SML_EXT_METINF)) != SML_ERR_OK) return _err;
+ if ((_err = metinfEncBlock(TN_METINF_NEXTNONCE, OPTIONAL, ((SmlMetInfMetInfPtr_t) pContent)->nextnonce, enc, pBufMgr, SML_EXT_METINF)) != SML_ERR_OK) return _err;
+ if ((_err = metinfEncBlock(TN_METINF_MAXMSGSIZE,OPTIONAL, ((SmlMetInfMetInfPtr_t) pContent)->maxmsgsize, enc, pBufMgr, SML_EXT_METINF)) != SML_ERR_OK) return _err;
+ // %%% luz 2003-04-24: added maxobjsize generation (was missing = bug in original RTK 4.1)
+ if ((_err = metinfEncBlock(TN_METINF_MAXOBJSIZE,OPTIONAL, ((SmlMetInfMetInfPtr_t) pContent)->maxobjsize, enc, pBufMgr, SML_EXT_METINF)) != SML_ERR_OK) return _err;
+ pList = ((SmlMetInfMetInfPtr_t)pContent)->emi;
+ while (pList != NULL) {
+ if ((_err = xltEncBlock(TN_METINF_EMI, OPTIONAL, pList->data, enc, pBufMgr, SML_EXT_METINF)) != SML_ERR_OK) return _err;
+ pList = pList->next;
+ };
+
+ if ((_err = metinfEncBlock(TN_METINF_MEM, OPTIONAL, ((SmlMetInfMetInfPtr_t) pContent)->mem, enc, pBufMgr, SML_EXT_METINF)) != SML_ERR_OK) return _err;
+ /* SyncML DS 1.2, Synthesis/luz 2005-08-24 */
+ if ((_err = metinfEncBlock(TN_METINF_FIELDLEVEL,OPTIONAL, &(((SmlMetInfMetInfPtr_t) pContent)->flags), enc, pBufMgr, SML_EXT_METINF)) != SML_ERR_OK) return _err;
+
+ //if ((_err = xltGenerateTag(TN_METINF_METINF, TT_END, enc, pBufMgr, SML_EXT_METINF)) != SML_ERR_OK) return _err;
+ break;
+ default: { // all leaf nodes (PCDATA#)
+ return xltEncPcdata(tagId, reqOptFlag, pContent, enc, pBufMgr, attFlag);
+ } /* eof default statement from switch tagid */
+ } /* eof switch tagid */
+ return SML_ERR_OK;
+}
+
+#endif /* __USE_METINF__ */
+
diff --git a/src/syncml_tk/src/sml/xlt/all/xltmetinf.h b/src/syncml_tk/src/sml/xlt/all/xltmetinf.h
new file mode 100755
index 0000000..97af871
--- /dev/null
+++ b/src/syncml_tk/src/sml/xlt/all/xltmetinf.h
@@ -0,0 +1,64 @@
+/**
+ * @file
+ * Definition of MetaInf DTD prototypefunctions for the en-/decoder
+ *
+ * @target_system all
+ * @target_os all
+ */
+
+/*
+ * Copyright Notice
+ * Copyright (c) Ericsson, IBM, Lotus, Matsushita Communication
+ * Industrial Co., Ltd., Motorola, Nokia, Openwave Systems, Inc.,
+ * Palm, Inc., Psion, Starfish Software, Symbian, Ltd. (2001).
+ * All Rights Reserved.
+ * Implementation of all or part of any Specification may require
+ * licenses under third party intellectual property rights,
+ * including without limitation, patent rights (such a third party
+ * may or may not be a Supporter). The Sponsors of the Specification
+ * are not responsible and shall not be held responsible in any
+ * manner for identifying or failing to identify any or all such
+ * third party intellectual property rights.
+ *
+ * THIS DOCUMENT AND THE INFORMATION CONTAINED HEREIN ARE PROVIDED
+ * ON AN "AS IS" BASIS WITHOUT WARRANTY OF ANY KIND AND ERICSSON, IBM,
+ * LOTUS, MATSUSHITA COMMUNICATION INDUSTRIAL CO. LTD, MOTOROLA,
+ * NOKIA, PALM INC., PSION, STARFISH SOFTWARE AND ALL OTHER SYNCML
+ * SPONSORS DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+ * BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
+ * HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
+ * SHALL ERICSSON, IBM, LOTUS, MATSUSHITA COMMUNICATION INDUSTRIAL CO.,
+ * LTD, MOTOROLA, NOKIA, PALM INC., PSION, STARFISH SOFTWARE OR ANY
+ * OTHER SYNCML SPONSOR BE LIABLE TO ANY PARTY FOR ANY LOSS OF
+ * PROFITS, LOSS OF BUSINESS, LOSS OF USE OF DATA, INTERRUPTION OF
+ * BUSINESS, OR FOR DIRECT, INDIRECT, SPECIAL OR EXEMPLARY, INCIDENTAL,
+ * PUNITIVE OR CONSEQUENTIAL DAMAGES OF ANY KIND IN CONNECTION WITH
+ * THIS DOCUMENT OR THE INFORMATION CONTAINED HEREIN, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH LOSS OR DAMAGE.
+ *
+ * The above notice and this paragraph must be included on all copies
+ * of this document that are made.
+ *
+ */
+
+#ifndef _XLT_METINF_H
+#define _XLT_METINF_H
+
+/* process only if we really use MetInf DTD */
+#ifdef __USE_METINF__
+
+/*************************************************************************/
+/* Definitions */
+/*************************************************************************/
+
+#include "xlttagtbl.h"
+#include "xltenc.h"
+
+Ret_t buildMetInfAnchorCmd(XltDecoderPtr_t pDecoder, VoidPtr_t *ppElem);
+Ret_t buildMetInfMetInfCmd(XltDecoderPtr_t pDecoder, VoidPtr_t *ppElem);
+Ret_t buildMetInfMemCmd(XltDecoderPtr_t pDecoder, VoidPtr_t *ppElem);
+Ret_t metinfEncBlock(XltTagID_t tagId, XltRO_t reqOptFlag, const VoidPtr_t pContent, SmlEncoding_t enc, BufferMgmtPtr_t pBufMgr, SmlPcdataExtension_t attFlag);
+#endif /* __USE_METINF__ */
+#endif /* _XLT_METINF_H */
+
diff --git a/src/syncml_tk/src/sml/xlt/all/xlttags.c b/src/syncml_tk/src/sml/xlt/all/xlttags.c
new file mode 100755
index 0000000..da3741d
--- /dev/null
+++ b/src/syncml_tk/src/sml/xlt/all/xlttags.c
@@ -0,0 +1,697 @@
+/**
+ * @file
+ * Definition of WBXML/XML tags for the en-/decoder
+ *
+ * @target_system all
+ * @target_os all
+ */
+
+/*
+ * Copyright Notice
+ * Copyright (c) Ericsson, IBM, Lotus, Matsushita Communication
+ * Industrial Co., Ltd., Motorola, Nokia, Openwave Systems, Inc.,
+ * Palm, Inc., Psion, Starfish Software, Symbian, Ltd. (2001).
+ * All Rights Reserved.
+ * Implementation of all or part of any Specification may require
+ * licenses under third party intellectual property rights,
+ * including without limitation, patent rights (such a third party
+ * may or may not be a Supporter). The Sponsors of the Specification
+ * are not responsible and shall not be held responsible in any
+ * manner for identifying or failing to identify any or all such
+ * third party intellectual property rights.
+ *
+ * THIS DOCUMENT AND THE INFORMATION CONTAINED HEREIN ARE PROVIDED
+ * ON AN "AS IS" BASIS WITHOUT WARRANTY OF ANY KIND AND ERICSSON, IBM,
+ * LOTUS, MATSUSHITA COMMUNICATION INDUSTRIAL CO. LTD, MOTOROLA,
+ * NOKIA, PALM INC., PSION, STARFISH SOFTWARE AND ALL OTHER SYNCML
+ * SPONSORS DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+ * BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
+ * HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
+ * SHALL ERICSSON, IBM, LOTUS, MATSUSHITA COMMUNICATION INDUSTRIAL CO.,
+ * LTD, MOTOROLA, NOKIA, PALM INC., PSION, STARFISH SOFTWARE OR ANY
+ * OTHER SYNCML SPONSOR BE LIABLE TO ANY PARTY FOR ANY LOSS OF
+ * PROFITS, LOSS OF BUSINESS, LOSS OF USE OF DATA, INTERRUPTION OF
+ * BUSINESS, OR FOR DIRECT, INDIRECT, SPECIAL OR EXEMPLARY, INCIDENTAL,
+ * PUNITIVE OR CONSEQUENTIAL DAMAGES OF ANY KIND IN CONNECTION WITH
+ * THIS DOCUMENT OR THE INFORMATION CONTAINED HEREIN, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH LOSS OR DAMAGE.
+ *
+ * The above notice and this paragraph must be included on all copies
+ * of this document that are made.
+ *
+ */
+
+#include "syncml_tk_prefix_file.h" // %%% luz: needed for precompiled headers in eVC++
+
+#include "xlttags.h"
+
+#include <libstr.h>
+#include <smlerr.h>
+#include <libmem.h>
+#include <libutil.h>
+#include <mgr.h>
+
+#include "xltmetinf.h"
+#include "xltdevinf.h"
+#include "xlttagtbl.h"
+
+
+// %%% luz:2003-07-31: added SyncML namespace tables
+const char * const SyncMLNamespaces[SML_NUM_VERS] = {
+ "???",
+ "SYNCML:SYNCML1.0",
+ "SYNCML:SYNCML1.1",
+ "SYNCML:SYNCML1.2"
+};
+
+/* local prototypes */
+#ifdef NOWSM
+//%%% removed const to prevent gcc "type qualifiers ignored on function return type" warning
+//const // without WSM, the tag table is a global read-only constant
+#endif
+TagPtr_t getTagTable(SmlPcdataExtension_t ext);
+
+//SmlPcdataExtension_t getByName(String_t ns);
+void freeDtdTable(DtdPtr_t tbl);
+
+#ifdef NOWSM
+//%%% removed const to prevent gcc "type qualifiers ignored on function return type" warning
+//const // without WSM, the DTD table is a global read-only constant
+#endif
+DtdPtr_t getDtdTable();
+
+
+// free table obtained with getDtdTable()
+void freeDtdTable(DtdPtr_t tbl)
+{
+ #ifndef NOWSM
+ // only with WSM this is an allocated table
+ smlLibFree(tbl);
+ #endif
+}
+
+/**
+ * Returns a copy of the table containing all known (sub) dtd's
+ * On error a NULL pointer is returned
+ */
+#ifdef NOWSM
+//%%% removed const to prevent gcc "type qualifiers ignored on function return type" warning
+//const // without WSM, the DTD table is a global read-only constant
+#endif
+DtdPtr_t getDtdTable() {
+ #ifdef NOWSM
+ // NOWSM method, table is const, just return a pointer
+ static const Dtd_t XltDtdTbl[] = {
+ { "SYNCML:SYNCML1.0", SML_EXT_UNDEFINED}, // %%% note that this is the default, will be override by syncml version specific string from
+ { "syncml:metinf", SML_EXT_METINF},
+ { "syncml:devinf", SML_EXT_DEVINF},
+ { NULL, SML_EXT_LAST}
+ };
+ return (DtdPtr_t)XltDtdTbl;
+ #else
+ // WSM method wasting a lot of memory
+ DtdPtr_t _tmpPtr;
+
+ Dtd_t XltDtdTbl[] = {
+ { "SYNCML:SYNCML1.0", SML_EXT_UNDEFINED},
+ { "syncml:metinf", SML_EXT_METINF},
+ { "syncml:devinf", SML_EXT_DEVINF},
+ { NULL, SML_EXT_LAST}
+ };
+ _tmpPtr = NULL;
+ _tmpPtr = (DtdPtr_t)smlLibMalloc(sizeof(XltDtdTbl));
+ if (_tmpPtr == NULL) return NULL;
+ smlLibMemcpy(_tmpPtr, &XltDtdTbl, sizeof(XltDtdTbl));
+ return _tmpPtr;
+ #endif
+}
+
+
+/**
+ * Returns the official name for a given extention/sub-DTD
+ * and stored it in 'name'. If not found name isn't modified
+ */
+// %%% luz:2003-04-24: added syncmlvers parameter
+// %%% luz:2003-07-31: changed to vers enum
+Ret_t getExtName(SmlPcdataExtension_t ext, String_t *name, SmlVersion_t vers) {
+ DtdPtr_t dtdhead = getDtdTable();
+ DtdPtr_t dtd = dtdhead;
+ const char *dtdname;
+ if (!dtdhead) return -1;
+ for (;dtd->ext != SML_EXT_LAST; dtd++) {
+ if (!dtd->name) continue; /* skip empty names (should not appear but better be on the safe side) */
+ if (dtd->ext == ext) {
+ String_t _tmp;
+ // this is the default
+ dtdname=dtd->name;
+ // %%% luz:2003-04-24: added dynamic generation of namespace according to SyncML version
+ if (ext==SML_EXT_UNDEFINED && vers!=SML_VERS_UNDEF) {
+ // this requests SyncML namespace
+ dtdname=SyncMLNamespaces[vers];
+ }
+ _tmp = smlLibMalloc(smlLibStrlen(dtdname)+1);
+ if (!_tmp) {
+ freeDtdTable(dtdhead);
+ return SML_ERR_NOT_ENOUGH_SPACE;
+ }
+ smlLibStrcpy(_tmp, dtdname);
+ freeDtdTable(dtdhead);
+ *name = _tmp;
+ return SML_ERR_OK;
+ }
+ }
+ freeDtdTable(dtdhead);
+ return -1;
+}
+
+/**
+ * Returns the codepage constant assoziated with the name stored in 'ns'
+ *
+ * @return a SmlPcdataExtension_t representing the corresponding codepage id.
+ * If no corresponding codepage is found -1 is returned.
+ */
+SmlPcdataExtension_t getExtByName(String_t ns) {
+ DtdPtr_t dtdhead = getDtdTable();
+ DtdPtr_t dtd = dtdhead;
+ SmlPcdataExtension_t ext = (SmlPcdataExtension_t) 255;
+ if (!dtdhead) return SML_EXT_UNDEFINED;
+ for (;dtd->ext != SML_EXT_LAST; dtd++) {
+ const char *dtdname=dtd->name;
+ if (!dtdname) continue; /* skip empty names (should not appear but better be on the safe side) */
+ if (dtd->ext==SML_EXT_UNDEFINED && smlLibStrncmp("SYNCML:SYNCML",ns,13)==0) {
+ // SyncML namespace is ok without checking version!
+ ext = SML_EXT_UNDEFINED;
+ break;
+ }
+ else if (smlLibStrcmp(dtdname,ns) == 0) {
+ ext = dtd->ext;
+ break;
+ }
+ }
+ freeDtdTable(dtdhead);
+ return ext;
+}
+
+
+
+/* if the commands are not defined we let the functions point to NULL */
+#ifndef RESULT_RECEIVE
+#define buildResults NULL
+#endif
+
+#ifndef MAP_RECEIVE
+#define buildMap NULL
+#endif
+
+#ifndef EXEC_RECEIVE
+#define buildExec NULL
+#endif
+
+#if !defined(ATOMIC_RECEIVE) && !defined(SEQUENCE_RECEIVE)
+#define buildAtomOrSeq NULL
+#endif
+
+#ifndef SEARCH_RECEIVE
+#define buildSearch NULL
+#endif
+
+
+/**
+ * Returns the tag table - this function is used to avoid a global
+ * tag table variable
+ *
+ * @return a pointer to the tag table containing tag ids,
+ * codepages, wbxml tags and xml tags
+ */
+/* T.K. initialized the structure via _TOKEN Macro, to take
+ * out the XML name tags when not compiled with XML support.
+ * In addtion removed the (unused) pointer for the build functions
+ */
+#if defined(__SML_XML__) || defined(__SML_WBXML_TEXTTOKENS__)
+#define _TOKEN(id, wbxml, xml) (id), (wbxml), (xml)
+#else
+#define _TOKEN(id, wbxml, xml) (id), (wbxml), ""
+#endif
+
+#ifdef NOWSM
+//%%% removed const to prevent gcc "type qualifiers ignored on function return type" warning
+//const // without WSM, the tag table is a global read-only constant
+#endif
+TagPtr_t getTagTable(SmlPcdataExtension_t ext)
+{
+ #ifndef NOWSM
+ int mySize = 0;
+ TagPtr_t _tmpTagPtr;
+ SyncMLInfoPtr_t pGA = NULL;
+ #else
+ TagPtr_t _tmpTagPtr=NULL;
+ #endif
+ /* standard SyncML codepage */
+ static const Tag_t syncml[] =
+ {
+ { _TOKEN(TN_ADD, 0x05, "Add")},
+ { _TOKEN(TN_ALERT, 0x06, "Alert")},
+ { _TOKEN(TN_ARCHIVE, 0x07, "Archive")},
+ { _TOKEN(TN_ATOMIC, 0x08, "Atomic")},
+ { _TOKEN(TN_CHAL, 0x09, "Chal")},
+ { _TOKEN(TN_CMD, 0x0A, "Cmd")},
+ { _TOKEN(TN_CMDID, 0x0B, "CmdID")},
+ { _TOKEN(TN_CMDREF, 0x0C, "CmdRef")},
+ { _TOKEN(TN_COPY, 0x0D, "Copy")},
+ { _TOKEN(TN_CRED, 0x0E, "Cred")},
+ { _TOKEN(TN_DATA, 0x0F, "Data")},
+ { _TOKEN(TN_DELETE, 0x10, "Delete")},
+ { _TOKEN(TN_EXEC, 0x11, "Exec")},
+ { _TOKEN(TN_FINAL, 0x12, "Final")},
+ { _TOKEN(TN_GET, 0x13, "Get")},
+ { _TOKEN(TN_ITEM, 0x14, "Item")},
+ { _TOKEN(TN_LANG, 0x15, "Lang")},
+ { _TOKEN(TN_LOCNAME, 0x16, "LocName")},
+ { _TOKEN(TN_LOCURI, 0x17, "LocURI")},
+ { _TOKEN(TN_MAP, 0x18, "Map")},
+ { _TOKEN(TN_MAPITEM, 0x19, "MapItem")},
+ { _TOKEN(TN_META, 0x1A, "Meta")},
+ { _TOKEN(TN_MSGID, 0x1B, "MsgID")},
+ { _TOKEN(TN_MSGREF, 0x1C, "MsgRef")},
+ { _TOKEN(TN_NORESP, 0x1D, "NoResp")},
+ { _TOKEN(TN_NORESULTS, 0x1E, "NoResults")},
+ { _TOKEN(TN_PUT, 0x1F, "Put")},
+ { _TOKEN(TN_REPLACE, 0x20, "Replace")},
+ { _TOKEN(TN_RESPURI, 0x21, "RespURI")},
+ { _TOKEN(TN_RESULTS, 0x22, "Results")},
+ { _TOKEN(TN_SEARCH, 0x23, "Search")},
+ { _TOKEN(TN_SEQUENCE, 0x24, "Sequence")},
+ { _TOKEN(TN_SESSIONID, 0x25, "SessionID")},
+ { _TOKEN(TN_SFTDEL, 0x26, "SftDel")},
+ { _TOKEN(TN_SOURCE, 0x27, "Source")},
+ { _TOKEN(TN_SOURCEREF, 0x28, "SourceRef")},
+ { _TOKEN(TN_STATUS, 0x29, "Status")},
+ { _TOKEN(TN_SYNC, 0x2A, "Sync")},
+ { _TOKEN(TN_SYNCBODY, 0x2B, "SyncBody")},
+ { _TOKEN(TN_SYNCHDR, 0x2C, "SyncHdr")},
+ { _TOKEN(TN_SYNCML, 0x2D, "SyncML")},
+ { _TOKEN(TN_TARGET, 0x2E, "Target")},
+ { _TOKEN(TN_TARGETREF, 0x2F, "TargetRef")},
+ { _TOKEN(TN_VERSION, 0x31, "VerDTD")},
+ { _TOKEN(TN_PROTO, 0x32, "VerProto")},
+ /* SyncML DS 1.1 */
+ { _TOKEN(TN_NUMBEROFCHANGES, 0x33, "NumberOfChanges")},
+ { _TOKEN(TN_MOREDATA, 0x34, "MoreData")},
+ /* SyncML DS 1.2, Synthesis/luz 2005-08-17 */
+ { _TOKEN(TN_FIELD, 0x35, "Field")},
+ { _TOKEN(TN_FILTER, 0x36, "Filter")},
+ { _TOKEN(TN_RECORD, 0x37, "Record")},
+ { _TOKEN(TN_FILTERTYPE, 0x38, "FilterType")},
+ { _TOKEN(TN_SOURCEPARENT, 0x39, "SourceParent")},
+ { _TOKEN(TN_TARGETPARENT, 0x3A, "TargetParent")},
+ { _TOKEN(TN_MOVE, 0x3B, "Move")},
+ { _TOKEN(TN_CORRELATOR, 0x3C, "Correlator")},
+ /* End of table */
+ { _TOKEN(TN_UNDEF, 0x00, NULL)}
+ };
+
+ #ifdef __USE_METINF__
+ static const Tag_t metinf[] = {
+ { _TOKEN(TN_METINF_ANCHOR, 0x05, "Anchor")},
+ { _TOKEN(TN_METINF_EMI, 0x06, "EMI")},
+ { _TOKEN(TN_METINF_FORMAT, 0x07, "Format")},
+ { _TOKEN(TN_METINF_FREEID, 0x08, "FreeID")},
+ { _TOKEN(TN_METINF_FREEMEM, 0x09, "FreeMem")},
+ { _TOKEN(TN_METINF_LAST, 0x0A, "Last")},
+ { _TOKEN(TN_METINF_MARK, 0x0B, "Mark")},
+ { _TOKEN(TN_METINF_MAXMSGSIZE, 0x0C, "MaxMsgSize")},
+ { _TOKEN(TN_METINF_MEM, 0x0D, "Mem")},
+ { _TOKEN(TN_METINF_METINF, 0x0E, "MetInf")},
+ { _TOKEN(TN_METINF_NEXT, 0x0F, "Next")},
+ { _TOKEN(TN_METINF_NEXTNONCE, 0x10, "NextNonce")},
+ { _TOKEN(TN_METINF_SHAREDMEM, 0x11, "SharedMem")},
+ { _TOKEN(TN_METINF_SIZE, 0x12, "Size")},
+ { _TOKEN(TN_METINF_TYPE, 0x13, "Type")},
+ { _TOKEN(TN_METINF_VERSION, 0x14, "Version")},
+ /* SCTSTK - 18/03/2002, S.H. 2002-04-05 : SyncML 1.1 */
+ { _TOKEN(TN_METINF_MAXOBJSIZE, 0x15, "MaxObjSize")},
+ /* SyncML DS 1.2, Synthesis/luz 2005-08-17 */
+ { _TOKEN(TN_METINF_FIELDLEVEL, 0x16, "FieldLevel")},
+ /* End of table */
+ { _TOKEN(TN_UNDEF, 0x00, NULL)}
+ };
+ #endif
+
+
+ #ifdef __USE_DEVINF__
+ static const Tag_t devinf[] = {
+ {_TOKEN(TN_DEVINF_CTCAP, 0x05, "CTCap")},
+ {_TOKEN(TN_DEVINF_CTTYPE, 0x06, "CTType")},
+ {_TOKEN(TN_DEVINF_DATASTORE, 0x07, "DataStore")},
+ {_TOKEN(TN_DEVINF_DATATYPE, 0x08, "DataType")},
+ {_TOKEN(TN_DEVINF_DEVID, 0x09, "DevID")},
+ {_TOKEN(TN_DEVINF_DEVINF, 0x0A, "DevInf")},
+ {_TOKEN(TN_DEVINF_DEVTYP, 0x0B, "DevTyp")},
+ {_TOKEN(TN_DEVINF_DISPLAYNAME, 0x0C, "DisplayName")},
+ {_TOKEN(TN_DEVINF_DSMEM, 0x0D, "DSMem")},
+ {_TOKEN(TN_DEVINF_EXT, 0x0E, "Ext")},
+ {_TOKEN(TN_DEVINF_FWV, 0x0F, "FwV")},
+ {_TOKEN(TN_DEVINF_HWV, 0x10, "HwV")},
+ {_TOKEN(TN_DEVINF_MAN, 0x11, "Man")},
+ {_TOKEN(TN_DEVINF_MAXGUIDSIZE, 0x12, "MaxGUIDSize")},
+ {_TOKEN(TN_DEVINF_MAXID, 0x13, "MaxID")},
+ {_TOKEN(TN_DEVINF_MAXMEM, 0x14, "MaxMem")},
+ {_TOKEN(TN_DEVINF_MOD, 0x15, "Mod")},
+ {_TOKEN(TN_DEVINF_OEM, 0x16, "OEM")},
+ {_TOKEN(TN_DEVINF_PARAMNAME, 0x17, "ParamName")},
+ {_TOKEN(TN_DEVINF_PROPNAME, 0x18, "PropName")},
+ {_TOKEN(TN_DEVINF_RX, 0x19, "Rx")},
+ {_TOKEN(TN_DEVINF_RXPREF, 0x1A, "Rx-Pref")},
+ {_TOKEN(TN_DEVINF_SHAREDMEM, 0x1B, "SharedMem")},
+ {_TOKEN(TN_DEVINF_SIZE, 0x1C, "Size")},
+ {_TOKEN(TN_DEVINF_SOURCEREF, 0x1D, "SourceRef")},
+ {_TOKEN(TN_DEVINF_SWV, 0x1E, "SwV")},
+ {_TOKEN(TN_DEVINF_SYNCCAP, 0x1F, "SyncCap")},
+ {_TOKEN(TN_DEVINF_SYNCTYPE, 0x20, "SyncType")},
+ {_TOKEN(TN_DEVINF_TX, 0x21, "Tx")},
+ {_TOKEN(TN_DEVINF_TXPREF, 0x22, "Tx-Pref")},
+ {_TOKEN(TN_DEVINF_VALENUM, 0x23, "ValEnum")},
+ {_TOKEN(TN_DEVINF_VERCT, 0x24, "VerCT")},
+ {_TOKEN(TN_DEVINF_VERDTD, 0x25, "VerDTD")},
+ {_TOKEN(TN_DEVINF_XNAM, 0x26, "XNam")},
+ {_TOKEN(TN_DEVINF_XVAL, 0x27, "XVal")},
+ /* %%% luz:2003-04-28 : added these SyncML 1.2 tags, they were missing */
+ {_TOKEN(TN_DEVINF_UTC, 0x28, "UTC")},
+ {_TOKEN(TN_DEVINF_NOFM, 0x29, "SupportNumberOfChanges")},
+ {_TOKEN(TN_DEVINF_LARGEOBJECT, 0x2A, "SupportLargeObjs")},
+ /* SyncML DS 1.2, Synthesis/luz 2005-08-17 */
+ {_TOKEN(TN_DEVINF_PROPERTY, 0x2B, "Property")},
+ {_TOKEN(TN_DEVINF_PROPPARAM, 0x2C, "PropParam")},
+ {_TOKEN(TN_DEVINF_MAXOCCUR, 0x2D, "MaxOccur")},
+ {_TOKEN(TN_DEVINF_NOTRUNCATE, 0x2E, "NoTruncate")},
+ {_TOKEN(TN_DEVINF_FILTERRX, 0x30, "Filter-Rx")},
+ {_TOKEN(TN_DEVINF_FILTERCAP, 0x31, "FilterCap")},
+ {_TOKEN(TN_DEVINF_FILTERKEYWORD,0x32, "FilterKeyword")},
+ {_TOKEN(TN_DEVINF_FIELDLEVEL, 0x33, "FieldLevel")},
+ {_TOKEN(TN_DEVINF_HIERARCHICAL, 0x34, "SupportHierarchicalSync")},
+ {_TOKEN(TN_DEVINF_MAXSIZE, 0x1C, "MaxSize")}, // alias for TN_DEVINF_SIZE (same WBXML, different XML)
+ /* End of table */
+ {_TOKEN(TN_UNDEF, 0x00, NULL)}
+ };
+ #endif
+
+ #ifndef NOWSM
+ _tmpTagPtr = NULL;
+ pGA = mgrGetSyncMLAnchor();
+ if (pGA == NULL) return NULL;
+ #endif
+
+ /* get the correct codepage */
+ if (ext == SML_EXT_UNDEFINED) {
+ #ifndef NOWSM
+ _tmpTagPtr = pGA->tokTbl->SyncML;
+ if (_tmpTagPtr == NULL) {
+ mySize = sizeof(syncml);
+ pGA->tokTbl->SyncML = (TagPtr_t)smlLibMalloc(mySize);
+ if (pGA->tokTbl->SyncML == NULL) return NULL;
+ smlLibMemcpy(pGA->tokTbl->SyncML, &syncml, mySize);
+ _tmpTagPtr = pGA->tokTbl->SyncML;
+ }
+ #else
+ _tmpTagPtr=(TagPtr_t)syncml;
+ #endif
+ }
+
+ #ifdef __USE_METINF__
+ else if (ext == SML_EXT_METINF) {
+ #ifndef NOWSM
+ _tmpTagPtr = pGA->tokTbl->MetInf;
+ if (_tmpTagPtr == NULL) {
+ mySize = sizeof(metinf);
+ pGA->tokTbl->MetInf = (TagPtr_t)smlLibMalloc(mySize);
+ if (pGA->tokTbl->MetInf == NULL) return NULL;
+ smlLibMemcpy(pGA->tokTbl->MetInf, &metinf, mySize);
+ _tmpTagPtr = pGA->tokTbl->MetInf;
+ }
+ #else
+ _tmpTagPtr=(TagPtr_t)metinf;
+ #endif
+ }
+ #endif
+
+ #ifdef __USE_DEVINF__
+ else if (ext == SML_EXT_DEVINF) {
+ #ifndef NOWSM
+ _tmpTagPtr = pGA->tokTbl->DevInf;
+ if (_tmpTagPtr == NULL) {
+ mySize = sizeof(devinf);
+ pGA->tokTbl->DevInf = (TagPtr_t)smlLibMalloc(mySize);
+ if (pGA->tokTbl->DevInf == NULL) return NULL;
+ smlLibMemcpy(pGA->tokTbl->DevInf, &devinf, mySize);
+ _tmpTagPtr = pGA->tokTbl->DevInf;
+ }
+ #else
+ _tmpTagPtr=(TagPtr_t)devinf;
+ #endif
+ }
+ #endif
+ return _tmpTagPtr;
+}
+#undef _TOKEN // we don't need that macro any longer
+
+/**
+ * Returns a tag string which belongs to a tag ID.
+ * This function is needed for the XML encoding
+ *
+ * @pre valid tag ID, the tagSring has to be allocated
+ * @post tag string is returned
+ * @param tagID (IN)
+ * the ID for the tag
+ * @param tagString (IN/OUT)
+ * allocated string into which the XML tag string will be written
+ * @param ext (IN)
+ * the PCDATA extension type
+ * @return 0, if OK
+ */
+#ifdef __SML_XML__
+Ret_t getTagString(XltTagID_t tagID, String_t tagString, SmlPcdataExtension_t ext)
+{
+ // %%% luz 2005-11-11 : added retry loop to try MetInf as a fallback (workaround for ill-formed xml like in sync4j pda clients)
+ do {
+ int i = 0;
+ TagPtr_t pTags = getTagTable(ext);
+ if (pTags == NULL) {
+ tagString[0] = '\0';
+ return SML_ERR_NOT_ENOUGH_SPACE;
+ }
+ while (((pTags+i)->id) != TN_UNDEF) {
+ if ((((pTags+i)->id) == tagID)) {
+ String_t _tmp = (pTags+i)->xml;
+ smlLibStrcpy(tagString, _tmp);
+ return SML_ERR_OK;
+ }
+ i++;
+ }
+ // tag not found
+ if (ext==SML_EXT_METINF) break; // already tried with implicit or explicit metinf
+ // try metinf implicitly for ill-formed xml like sync4j clients
+ ext=SML_EXT_METINF;
+ } while(TRUE);
+
+ tagString[0] = '\0';
+ return SML_ERR_XLT_INVAL_PROTO_ELEM;
+}
+#endif
+
+/**
+ * Returns a WBXML byte which belongs to a tag ID in a defined codepage.
+ * This function is needed for the WBXML encoding
+ *
+ * @pre valid tag ID, valid code page
+ * @post tag byte is returned
+ * @param tagID (IN)
+ * the ID for the tag
+ * @param ext (IN)
+ * code page group for the tag
+ * @param pTagByte (IN)
+ * the byte representation of the tag
+ * @return 0, if OK
+ */
+Ret_t getTagByte(XltTagID_t tagID, SmlPcdataExtension_t ext, Byte_t *pTagByte)
+{
+ int i = 0;
+ TagPtr_t pTags = getTagTable(ext);
+ if (pTags == NULL)
+ {
+ return SML_ERR_NOT_ENOUGH_SPACE;
+ }
+ while (((pTags+i)->id) != TN_UNDEF)
+ {
+ if (((pTags+i)->id) == tagID)
+ {
+ *pTagByte = (pTags+i)->wbxml;
+ return SML_ERR_OK;
+ }
+ i++;
+ }
+ *pTagByte = 0;
+ return SML_ERR_XLT_INVAL_PROTO_ELEM;
+}
+
+/**
+ * Returns the code page which belongs to a certain PCDATA extension type.
+ *
+ * @pre valid PCDATA extension type
+ * @post the code page is returned
+ * @param ext (IN)
+ * the PCDATA extension type
+ * @return the code page
+ */
+Byte_t getCodePage(SmlPcdataExtension_t ext)
+{
+ #ifdef __USE_METINF__
+ if (ext == SML_EXT_METINF)
+ return 1;
+ #endif
+ #ifdef __USE_DEVINF__
+ if (ext == SML_EXT_DEVINF)
+ return 0;
+ #endif
+ return 0;
+}
+
+/**
+ * Returns the codepage which belongs to a certain tag ID
+ *
+ * @pre valid tag ID
+ * @post the code page is returned
+ * @param tagID (IN)
+ * the ID of the tag
+ * @param pExt (IN)
+ * the codepage/extention of the tag
+ * @return 0, if OK
+ */
+Ret_t getExtById(XltTagID_t tagID, SmlPcdataExtension_t *pExt)
+{
+ int i = 0;
+ SmlPcdataExtension_t ext;
+ /* Iterate over all defined extensions to find the corresponding TAG.
+ * Empty extensions, e.g. not defined numbers will be skipped.
+ */
+ for (ext = SML_EXT_UNDEFINED; ext < SML_EXT_LAST; ext++) {
+ TagPtr_t pTags = getTagTable(ext);
+ if (pTags == NULL) {
+ continue; /* skip empty codepage */
+ }
+ i = 0;
+ while (((pTags+i)->id) != TN_UNDEF) {
+ if ((((pTags+i)->id) == tagID)){
+ *pExt = ext;
+ return SML_ERR_OK;
+ }
+ i++;
+ }
+ }
+ /* tag not found in any extension */
+ *pExt = (SmlPcdataExtension_t)255;
+ return SML_ERR_XLT_INVAL_PROTO_ELEM;
+}
+
+/**
+ * Returns the tag ID which belongs to a tag string in a certain codepage
+ *
+ * @pre valid tag string, valid code page
+ * @post tag id is returned
+ * @param tag (IN)
+ * the string representation of the tag
+ * @param ext (IN)
+ * code page group for the tag
+ * @param pTagID (IN)
+ * the tag id of the tag
+ * @return 0, if OK
+ */
+Ret_t getTagIDByStringAndExt(String_t tag, SmlPcdataExtension_t ext, XltTagID_t *pTagID)
+{
+ int i = 0;
+ TagPtr_t pTags = getTagTable(ext);
+ if (pTags == NULL) {
+ return SML_ERR_NOT_ENOUGH_SPACE;
+ }
+ for (i=0;((pTags+i)->id) != TN_UNDEF; i++) {
+ if (*(pTags+i)->xml != *tag) continue; // if the first char doesn't match we skip the strcmp to speed things up
+ if (smlLibStrcmp(((pTags+i)->xml), tag) == 0) {
+ *pTagID = (pTags+i)->id;
+ return SML_ERR_OK;
+ }
+ }
+ *pTagID = TN_UNDEF;
+ return SML_ERR_XLT_INVAL_PROTO_ELEM;
+}
+
+/**
+ * Returns the tag ID which belongs to a tag byte in a certain codepage
+ *
+ * @pre valid tag byte, valid code page
+ * @post tag id is returned
+ * @param tag (IN)
+ * the byte representation of the tag
+ * @param ext (IN)
+ * code page group for the tag
+ * @param pTagID (IN)
+ * the tag id of the tag
+ * @return 0, if OK
+ */
+Ret_t getTagIDByByteAndExt(Byte_t tag, SmlPcdataExtension_t ext, XltTagID_t *pTagID)
+{
+
+ int i = 0;
+ TagPtr_t pTags = getTagTable(ext);
+ if (pTags == NULL)
+ {
+ return SML_ERR_NOT_ENOUGH_SPACE;
+ }
+ while (((pTags+i)->id) != TN_UNDEF)
+ {
+ if (((pTags+i)->wbxml) == tag)
+ {
+ *pTagID = (pTags+i)->id;
+ return SML_ERR_OK;
+ }
+ i++;
+ }
+ *pTagID = TN_UNDEF;
+ return SML_ERR_XLT_INVAL_PROTO_ELEM;
+}
+
+#ifdef __SML_XML__
+/**
+ * Returns the tag ID which belongs to a tag string in a certain namespace
+ *
+ * @pre valid tag string, valid namespace
+ * @post tag id is returned
+ * @param tag (IN)
+ * the string representation of the tag
+ * @param ns (IN)
+ * namespace group for the tag
+ * @param pTagID (IN)
+ * the tag id of the tag
+ * @return 0, if OK
+ */
+Ret_t getTagIDByStringAndNamespace(String_t tag, String_t ns, XltTagID_t *pTagID)
+{
+ int i = 0;
+ TagPtr_t pTags = getTagTable(getExtByName(ns));
+ if (pTags == NULL)
+ {
+ return SML_ERR_NOT_ENOUGH_SPACE;
+ }
+ while (((pTags+i)->id) != TN_UNDEF)
+ {
+ if ((smlLibStrcmp(((pTags+i)->xml), tag) == 0))
+ {
+ *pTagID = (pTags+i)->id;
+ return SML_ERR_OK;
+ }
+ i++;
+ }
+ *pTagID = TN_UNDEF;
+ return SML_ERR_XLT_INVAL_PROTO_ELEM;
+}
+
+#endif
diff --git a/src/syncml_tk/src/sml/xlt/all/xlttags.h b/src/syncml_tk/src/sml/xlt/all/xlttags.h
new file mode 100755
index 0000000..5e2c4e1
--- /dev/null
+++ b/src/syncml_tk/src/sml/xlt/all/xlttags.h
@@ -0,0 +1,232 @@
+/**
+ * @file
+ * Definition of WBXML/XML tags for the en-/decoder
+ *
+ * @target_system all
+ * @target_os all
+ */
+
+/*
+ * Copyright Notice
+ * Copyright (c) Ericsson, IBM, Lotus, Matsushita Communication
+ * Industrial Co., Ltd., Motorola, Nokia, Openwave Systems, Inc.,
+ * Palm, Inc., Psion, Starfish Software, Symbian, Ltd. (2001).
+ * All Rights Reserved.
+ * Implementation of all or part of any Specification may require
+ * licenses under third party intellectual property rights,
+ * including without limitation, patent rights (such a third party
+ * may or may not be a Supporter). The Sponsors of the Specification
+ * are not responsible and shall not be held responsible in any
+ * manner for identifying or failing to identify any or all such
+ * third party intellectual property rights.
+ *
+ * THIS DOCUMENT AND THE INFORMATION CONTAINED HEREIN ARE PROVIDED
+ * ON AN "AS IS" BASIS WITHOUT WARRANTY OF ANY KIND AND ERICSSON, IBM,
+ * LOTUS, MATSUSHITA COMMUNICATION INDUSTRIAL CO. LTD, MOTOROLA,
+ * NOKIA, PALM INC., PSION, STARFISH SOFTWARE AND ALL OTHER SYNCML
+ * SPONSORS DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+ * BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
+ * HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
+ * SHALL ERICSSON, IBM, LOTUS, MATSUSHITA COMMUNICATION INDUSTRIAL CO.,
+ * LTD, MOTOROLA, NOKIA, PALM INC., PSION, STARFISH SOFTWARE OR ANY
+ * OTHER SYNCML SPONSOR BE LIABLE TO ANY PARTY FOR ANY LOSS OF
+ * PROFITS, LOSS OF BUSINESS, LOSS OF USE OF DATA, INTERRUPTION OF
+ * BUSINESS, OR FOR DIRECT, INDIRECT, SPECIAL OR EXEMPLARY, INCIDENTAL,
+ * PUNITIVE OR CONSEQUENTIAL DAMAGES OF ANY KIND IN CONNECTION WITH
+ * THIS DOCUMENT OR THE INFORMATION CONTAINED HEREIN, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH LOSS OR DAMAGE.
+ *
+ * The above notice and this paragraph must be included on all copies
+ * of this document that are made.
+ *
+ */
+
+/*************************************************************************/
+/* Definitions */
+/*************************************************************************/
+
+#ifndef _XLT_TAGS_H
+#define _XLT_TAGS_H
+
+#include <smldtd.h>
+#include <smldef.h>
+
+#define XML_MAX_TAGLEN 35
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Tag IDs
+typedef enum {
+ TN_UNDEF = 0,
+
+ TN_ADD,
+ TN_ALERT,
+ TN_ARCHIVE,
+ TN_ATOMIC,
+ TN_ATOMIC_END, /* 5 */
+ TN_CHAL,
+ TN_CMD,
+ TN_CMDID,
+ TN_CMDREF,
+ TN_COPY, /* 10 */
+ TN_CRED,
+ TN_DATA,
+ TN_DELETE,
+ TN_EXEC,
+ TN_FINAL, /* 15 */
+ TN_GET,
+ TN_ITEM,
+ TN_LANG,
+ TN_LOCNAME,
+ TN_LOCURI, /* 20 */
+ TN_MAP,
+ TN_MAPITEM,
+ TN_META,
+ TN_MSGID,
+ TN_MSGREF, /* 25 */
+ TN_NORESP,
+ TN_NORESULTS,
+ TN_PUT,
+ TN_REPLACE,
+ TN_RESPURI, /* 30 */
+ TN_RESULTS,
+ TN_SEARCH,
+ TN_SEQUENCE,
+ TN_SEQUENCE_END,
+ TN_SESSIONID, /* 35 */
+ TN_SFTDEL,
+ TN_SOURCE,
+ TN_SOURCEREF,
+ TN_STATUS,
+ TN_SYNC, /* 40 */
+ TN_SYNCBODY,
+ TN_SYNCHDR,
+ TN_SYNCML,
+ TN_SYNC_END,
+ TN_TARGET, /* 45 */
+ TN_TARGETREF,
+ TN_VERSION,
+ TN_PROTO,
+ TN_METINF_ANCHOR,
+ TN_METINF_EMI, /* 50 */
+ TN_METINF_FORMAT,
+ TN_METINF_FREEID,
+ TN_METINF_FREEMEM,
+ TN_METINF_LAST,
+ TN_METINF_MARK, /* 55 */
+ TN_METINF_MAXMSGSIZE,
+ TN_METINF_MEM,
+ TN_METINF_METINF,
+ TN_METINF_NEXT,
+ TN_METINF_NEXTNONCE, /* 60 */
+ TN_METINF_SHAREDMEM,
+ TN_METINF_SIZE,
+ TN_METINF_TYPE,
+ TN_METINF_VERSION,
+ TN_DEVINF_CTCAP, /* 65 */
+ TN_DEVINF_CTTYPE,
+ TN_DEVINF_DATASTORE,
+ TN_DEVINF_DATATYPE,
+ TN_DEVINF_DEVID,
+ TN_DEVINF_DEVINF, /* 70 */
+ TN_DEVINF_DEVTYP,
+ TN_DEVINF_DISPLAYNAME,
+ TN_DEVINF_DSMEM,
+ TN_DEVINF_EXT,
+ TN_DEVINF_FWV, /* 75 */
+ TN_DEVINF_HWV,
+ TN_DEVINF_MAN,
+ TN_DEVINF_MAXGUIDSIZE,
+ TN_DEVINF_MAXID,
+ TN_DEVINF_MAXMEM, /* 80 */
+ TN_DEVINF_MOD,
+ TN_DEVINF_OEM,
+ TN_DEVINF_PARAMNAME,
+ TN_DEVINF_PROPNAME,
+ TN_DEVINF_RX, /* 85 */
+ TN_DEVINF_RXPREF,
+ TN_DEVINF_SHAREDMEM,
+ TN_DEVINF_SIZE,
+ TN_DEVINF_SOURCEREF,
+ TN_DEVINF_SWV, /* 90 */
+ TN_DEVINF_SYNCCAP,
+ TN_DEVINF_SYNCTYPE,
+ TN_DEVINF_TX,
+ TN_DEVINF_TXPREF,
+ TN_DEVINF_VALENUM, /* 95 */
+ TN_DEVINF_VERCT,
+ TN_DEVINF_VERDTD,
+ TN_DEVINF_XNAM,
+ TN_DEVINF_XVAL,
+ /* SCTSTK - 18/03/2002, S.H. 2002-04-05 : SyncML 1.1 */
+ TN_NUMBEROFCHANGES, /* 100 */
+ TN_MOREDATA,
+ TN_METINF_MAXOBJSIZE,
+ TN_DEVINF_UTC,
+ TN_DEVINF_NOFM,
+ TN_DEVINF_LARGEOBJECT,
+ /* SyncML DS 1.2, Synthesis/luz 2005-08-17 */
+ TN_FIELD,
+ TN_FILTER,
+ TN_RECORD,
+ TN_FILTERTYPE,
+ TN_SOURCEPARENT,
+ TN_TARGETPARENT,
+ TN_MOVE,
+ TN_CORRELATOR,
+ TN_METINF_FIELDLEVEL,
+ TN_DEVINF_PROPERTY,
+ TN_DEVINF_PROPPARAM,
+ TN_DEVINF_MAXOCCUR,
+ TN_DEVINF_NOTRUNCATE,
+ TN_DEVINF_FILTERRX,
+ TN_DEVINF_FILTERCAP,
+ TN_DEVINF_FILTERKEYWORD,
+ TN_DEVINF_FIELDLEVEL,
+ TN_DEVINF_HIERARCHICAL,
+ TN_DEVINF_MAXSIZE // alias for TN_DEVINF_SIZE (same WBXML, different XML)
+} XltTagID_t;
+
+
+extern const char * const SyncMLNamespaces[SML_NUM_VERS];
+
+typedef struct Dtd_s
+{
+ String_t name;
+ SmlPcdataExtension_t ext;
+} Dtd_t, *DtdPtr_t;
+
+
+Ret_t getTagString(XltTagID_t tagID, String_t tagString, SmlPcdataExtension_t ext);
+
+#ifdef __SML_WBXML__
+Ret_t getTagByte(XltTagID_t tagID, SmlPcdataExtension_t ext, Byte_t *tagByte);
+#endif
+
+Byte_t getCodePage(SmlPcdataExtension_t ext);
+Ret_t getTagIDByStringAndExt(String_t tag, SmlPcdataExtension_t ext, XltTagID_t *pTagID);
+
+#ifdef __SML_WBXML__
+Ret_t getTagIDByByteAndExt(Byte_t tag, SmlPcdataExtension_t ext, XltTagID_t *pTagID);
+#endif
+
+#ifdef __SML_XML__
+Ret_t getTagIDByStringAndNamespace(String_t tag, String_t ns, XltTagID_t *pTagID);
+#endif
+
+Ret_t getExtById(XltTagID_t tagID, SmlPcdataExtension_t *pExt);
+
+// %%% luz:2003-07-31: added vers parameter
+Ret_t getExtName(SmlPcdataExtension_t ext, String_t *name, SmlVersion_t vers);
+Ret_t getTagIDByStringAndExt(String_t tag, SmlPcdataExtension_t ext, XltTagID_t *pTagID);
+SmlPcdataExtension_t getExtByName(String_t ns);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/syncml_tk/src/sml/xlt/all/xlttagtbl.h b/src/syncml_tk/src/sml/xlt/all/xlttagtbl.h
new file mode 100755
index 0000000..4dcf1ce
--- /dev/null
+++ b/src/syncml_tk/src/sml/xlt/all/xlttagtbl.h
@@ -0,0 +1,118 @@
+/**
+ * @file
+ * Definition of en-/decoder tagtable data
+ *
+ * @target_system all
+ * @target_os all
+ */
+
+/*
+ * Copyright Notice
+ * Copyright (c) Ericsson, IBM, Lotus, Matsushita Communication
+ * Industrial Co., Ltd., Motorola, Nokia, Openwave Systems, Inc.,
+ * Palm, Inc., Psion, Starfish Software, Symbian, Ltd. (2001).
+ * All Rights Reserved.
+ * Implementation of all or part of any Specification may require
+ * licenses under third party intellectual property rights,
+ * including without limitation, patent rights (such a third party
+ * may or may not be a Supporter). The Sponsors of the Specification
+ * are not responsible and shall not be held responsible in any
+ * manner for identifying or failing to identify any or all such
+ * third party intellectual property rights.
+ *
+ * THIS DOCUMENT AND THE INFORMATION CONTAINED HEREIN ARE PROVIDED
+ * ON AN "AS IS" BASIS WITHOUT WARRANTY OF ANY KIND AND ERICSSON, IBM,
+ * LOTUS, MATSUSHITA COMMUNICATION INDUSTRIAL CO. LTD, MOTOROLA,
+ * NOKIA, PALM INC., PSION, STARFISH SOFTWARE AND ALL OTHER SYNCML
+ * SPONSORS DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+ * BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
+ * HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
+ * SHALL ERICSSON, IBM, LOTUS, MATSUSHITA COMMUNICATION INDUSTRIAL CO.,
+ * LTD, MOTOROLA, NOKIA, PALM INC., PSION, STARFISH SOFTWARE OR ANY
+ * OTHER SYNCML SPONSOR BE LIABLE TO ANY PARTY FOR ANY LOSS OF
+ * PROFITS, LOSS OF BUSINESS, LOSS OF USE OF DATA, INTERRUPTION OF
+ * BUSINESS, OR FOR DIRECT, INDIRECT, SPECIAL OR EXEMPLARY, INCIDENTAL,
+ * PUNITIVE OR CONSEQUENTIAL DAMAGES OF ANY KIND IN CONNECTION WITH
+ * THIS DOCUMENT OR THE INFORMATION CONTAINED HEREIN, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH LOSS OR DAMAGE.
+ *
+ * The above notice and this paragraph must be included on all copies
+ * of this document that are made.
+ *
+ */
+
+#ifndef _XLT_TAGTBL_H
+#define _XLT_TAGTBL_H
+
+
+/*************************************************************************/
+/* Definitions */
+/*************************************************************************/
+
+#include "xlttags.h"
+#include "xltdec.h"
+#include "xltdeccom.h"
+
+typedef struct Tag_s
+{
+ XltTagID_t id;
+ Byte_t wbxml;
+ String_t xml;
+} Tag_t, *TagPtr_t;
+
+/**
+ * buildXXX
+ *
+ * These functions each decode one single SyncML element starting at the
+ * current position within the SyncML document. Child elements are build
+ * recursively.
+ * The functions check that the pointer to the memory structures are
+ * NULL when called and return an error otherwise. This will only happen
+ * when a SyncML element contains several child elements of the same type
+ * for which this is not allowed according to the SyncML DTD; e.g. a
+ * SyncHdr with two or more MsgID tags. Items and other list types
+ * are handled separately by the appendXXXList functions (see below).
+ *
+ * @pre ppElem is NULL
+ * The scanner's current token is the start tag (may be
+ * empty) of the SyncML element to be decoded.
+ * @post The scanner's current token is the end tag (or empty
+ * start tag) of the SyncML element to be decoded.
+ */
+/* implemented in xltdec.c! */
+Ret_t buildAlert(XltDecoderPtr_t pDecoder, VoidPtr_t *ppElem);
+#if (defined ATOMIC_RECEIVE || defined SEQUENCE_RECEIVE)
+Ret_t buildAtomOrSeq(XltDecoderPtr_t pDecoder, VoidPtr_t *ppElem);
+#endif
+Ret_t buildChal(XltDecoderPtr_t pDecoder, VoidPtr_t *ppElem);
+Ret_t buildCred(XltDecoderPtr_t pDecoder, VoidPtr_t *ppElem);
+Ret_t buildDelete(XltDecoderPtr_t pDecoder, VoidPtr_t *ppElem);
+Ret_t buildExec(XltDecoderPtr_t pDecoder, VoidPtr_t *ppElem);
+Ret_t buildRecordFieldFilter(XltDecoderPtr_t pDecoder, VoidPtr_t *ppElem);
+Ret_t buildFilter(XltDecoderPtr_t pDecoder, VoidPtr_t *ppElem);
+Ret_t buildGenericCmd(XltDecoderPtr_t pDecoder, VoidPtr_t *ppElem);
+Ret_t buildItem(XltDecoderPtr_t pDecoder, VoidPtr_t *ppElem);
+Ret_t buildMap(XltDecoderPtr_t pDecoder, VoidPtr_t *ppElem);
+Ret_t buildMapItem(XltDecoderPtr_t pDecoder, VoidPtr_t *ppElem);
+Ret_t buildEmptyTag(XltDecoderPtr_t pDecoder);
+Ret_t buildPCData(XltDecoderPtr_t pDecoder, VoidPtr_t *ppElem);
+Ret_t buildPCDataList(XltDecoderPtr_t pDecoder, VoidPtr_t *ppPCData);
+Ret_t buildPutOrGet(XltDecoderPtr_t pDecoder, VoidPtr_t *ppElem);
+Ret_t buildResults(XltDecoderPtr_t pDecoder, VoidPtr_t *ppElem);
+#ifdef SEARCH_RECEIVE
+Ret_t buildSearch(XltDecoderPtr_t pDecoder, VoidPtr_t *ppElem);
+#endif
+Ret_t buildStatus(XltDecoderPtr_t pDecoder, VoidPtr_t *ppElem);
+Ret_t buildSync(XltDecoderPtr_t pDecoder, VoidPtr_t *ppElem);
+Ret_t buildSyncHdr(XltDecoderPtr_t pDecoder, VoidPtr_t *ppElem);
+Ret_t buildTargetOrSource(XltDecoderPtr_t pDecoder, VoidPtr_t *ppElem);
+Ret_t buildTargetOrSourceParent(XltDecoderPtr_t pDecoder, VoidPtr_t *ppElem);
+#ifdef NOWSM
+//%%% removed const to prevent gcc "type qualifiers ignored on function return type" warning
+//const // without WSM, the tag table is a global read-only constant
+#endif
+TagPtr_t getTagTable(SmlPcdataExtension_t cp);
+
+#endif /* _XLT_TAGTBL_H */
+
diff --git a/src/syncml_tk/src/sml/xlt/all/xltutilstack.c b/src/syncml_tk/src/sml/xlt/all/xltutilstack.c
new file mode 100755
index 0000000..866d13f
--- /dev/null
+++ b/src/syncml_tk/src/sml/xlt/all/xltutilstack.c
@@ -0,0 +1,206 @@
+/**
+ * @file
+ * XLT Decoder Stack
+ *
+ * @target_system all
+ * @target_os all
+ * @description A simple array-based stack implementation.
+ */
+
+
+/*
+ * Copyright Notice
+ * Copyright (c) Ericsson, IBM, Lotus, Matsushita Communication
+ * Industrial Co., Ltd., Motorola, Nokia, Openwave Systems, Inc.,
+ * Palm, Inc., Psion, Starfish Software, Symbian, Ltd. (2001).
+ * All Rights Reserved.
+ * Implementation of all or part of any Specification may require
+ * licenses under third party intellectual property rights,
+ * including without limitation, patent rights (such a third party
+ * may or may not be a Supporter). The Sponsors of the Specification
+ * are not responsible and shall not be held responsible in any
+ * manner for identifying or failing to identify any or all such
+ * third party intellectual property rights.
+ *
+ * THIS DOCUMENT AND THE INFORMATION CONTAINED HEREIN ARE PROVIDED
+ * ON AN "AS IS" BASIS WITHOUT WARRANTY OF ANY KIND AND ERICSSON, IBM,
+ * LOTUS, MATSUSHITA COMMUNICATION INDUSTRIAL CO. LTD, MOTOROLA,
+ * NOKIA, PALM INC., PSION, STARFISH SOFTWARE AND ALL OTHER SYNCML
+ * SPONSORS DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+ * BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
+ * HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
+ * SHALL ERICSSON, IBM, LOTUS, MATSUSHITA COMMUNICATION INDUSTRIAL CO.,
+ * LTD, MOTOROLA, NOKIA, PALM INC., PSION, STARFISH SOFTWARE OR ANY
+ * OTHER SYNCML SPONSOR BE LIABLE TO ANY PARTY FOR ANY LOSS OF
+ * PROFITS, LOSS OF BUSINESS, LOSS OF USE OF DATA, INTERRUPTION OF
+ * BUSINESS, OR FOR DIRECT, INDIRECT, SPECIAL OR EXEMPLARY, INCIDENTAL,
+ * PUNITIVE OR CONSEQUENTIAL DAMAGES OF ANY KIND IN CONNECTION WITH
+ * THIS DOCUMENT OR THE INFORMATION CONTAINED HEREIN, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH LOSS OR DAMAGE.
+ *
+ * The above notice and this paragraph must be included on all copies
+ * of this document that are made.
+ *
+ */
+
+/*************************************************************************/
+/* Definitions */
+/*************************************************************************/
+
+#include "syncml_tk_prefix_file.h" // %%% luz: needed for precompiled headers in eVC++
+
+#include "xltdeccom.h"
+#include "xltutilstack.h"
+
+#include <smlerr.h>
+
+#include <libmem.h>
+
+struct ArrayStack_s;
+typedef struct ArrayStack_s *ArrayStackPtr_t, ArrayStack_t;
+struct ArrayStack_s
+{
+ /* public */
+ Ret_t (*top)(const XltUtilStackPtr_t, XltUtilStackItem_t *);
+ Ret_t (*pop)(XltUtilStackPtr_t, XltUtilStackItem_t *);
+ Ret_t (*push)(XltUtilStackPtr_t, const XltUtilStackItem_t);
+ Ret_t (*destroy)(XltUtilStackPtr_t);
+
+ /* private */
+ Long_t topidx; // index of the top of the stack
+ Long_t size; // size of the stack (multiple of chunksize)
+ Long_t chunksize; // size of memory chunks allocated at a time
+ XltUtilStackItem_t *array; // the stack itself
+};
+
+static Ret_t _top(const XltUtilStackPtr_t, XltUtilStackItem_t *);
+static Ret_t _pop(XltUtilStackPtr_t, XltUtilStackItem_t *);
+static Ret_t _push(XltUtilStackPtr_t, const XltUtilStackItem_t);
+static Ret_t _destroy(XltUtilStackPtr_t);
+
+/*************************************************************************/
+/* External Functions */
+/*************************************************************************/
+
+Ret_t
+xltUtilCreateStack(XltUtilStackPtr_t *ppStack, const Long_t size)
+{
+ ArrayStackPtr_t pStack;
+
+ if (size <= 0)
+ return SML_ERR_WRONG_PARAM;
+ if ((pStack = (ArrayStackPtr_t)smlLibMalloc(sizeof(ArrayStack_t))) == NULL) {
+ *ppStack = NULL;
+ return SML_ERR_NOT_ENOUGH_SPACE;
+ }
+
+ pStack->top = _top;
+ pStack->pop = _pop;
+ pStack->push = _push;
+ pStack->destroy = _destroy;
+ pStack->topidx = -1;
+ pStack->size = size;
+ pStack->chunksize = size;
+ pStack->array = NULL;
+ if ((pStack->array = (XltUtilStackItem_t*)smlLibMalloc(size * sizeof(XltUtilStackItem_t))) == NULL) {
+ *ppStack = NULL;
+ smlLibFree(pStack);
+ return SML_ERR_NOT_ENOUGH_SPACE;
+ }
+
+ *ppStack = (XltUtilStackPtr_t)pStack;
+
+
+
+ return SML_ERR_OK;
+}
+
+/*************************************************************************/
+/* Internal Functions */
+/*************************************************************************/
+
+static Ret_t
+_top(const XltUtilStackPtr_t pStack, XltUtilStackItem_t *itemPtr)
+{
+ ArrayStackPtr_t pStackPriv = (ArrayStackPtr_t)pStack;
+
+ if (pStackPriv->topidx == -1)
+ return SML_ERR_WRONG_USAGE;
+
+ *itemPtr = pStackPriv->array[pStackPriv->topidx];
+
+ return SML_ERR_OK;
+}
+
+static Ret_t
+_pop(XltUtilStackPtr_t pStack, XltUtilStackItem_t *itemPtr)
+{
+ ArrayStackPtr_t pStackPriv = (ArrayStackPtr_t)pStack;
+ XltUtilStackItem_t item;
+
+ if (pStackPriv->topidx == -1)
+ return SML_ERR_WRONG_USAGE;
+
+ item = pStackPriv->array[pStackPriv->topidx];
+ pStackPriv->topidx--;
+
+ if ((pStackPriv->topidx >= 0) &&
+ (pStackPriv->topidx < pStackPriv->size - pStackPriv->chunksize)) {
+ Long_t newsize;
+ XltUtilStackItem_t *newarray;
+
+ newsize = pStackPriv->size - pStackPriv->chunksize;
+ if ((newarray = (XltUtilStackItem_t*)smlLibRealloc(pStackPriv->array,
+ newsize * sizeof(XltUtilStackItem_t))) != NULL) {
+ pStackPriv->size = newsize;
+ pStackPriv->array = newarray;
+ } else {
+ return SML_ERR_NOT_ENOUGH_SPACE;
+ }
+ }
+
+ *itemPtr = item;
+
+ return SML_ERR_OK;
+}
+
+static Ret_t
+_push(XltUtilStackPtr_t pStack, const XltUtilStackItem_t item)
+{
+ ArrayStackPtr_t pStackPriv = (ArrayStackPtr_t)pStack;
+
+ if (pStackPriv->topidx == pStackPriv->size - 1) {
+ Long_t newsize;
+ XltUtilStackItem_t *newarray;
+
+ newsize = pStackPriv->size + pStackPriv->chunksize;
+ if ((newarray = (XltUtilStackItem_t*)smlLibRealloc(pStackPriv->array,
+ newsize * sizeof(XltUtilStackItem_t))) != NULL) {
+ pStackPriv->size = newsize;
+ pStackPriv->array = newarray;
+ } else {
+ return SML_ERR_NOT_ENOUGH_SPACE;
+ }
+ }
+
+ pStackPriv->topidx++;
+ pStackPriv->array[pStackPriv->topidx] = item;
+
+ return SML_ERR_OK;
+}
+
+static Ret_t
+_destroy(XltUtilStackPtr_t pStack)
+{
+ ArrayStackPtr_t pStackPriv;
+
+ if (pStack == NULL)
+ return SML_ERR_OK;
+
+ pStackPriv = (ArrayStackPtr_t)pStack;
+
+ smlLibFree(pStackPriv->array);
+ smlLibFree(pStackPriv);
+ return SML_ERR_OK;
+}
diff --git a/src/syncml_tk/src/sml/xlt/all/xltutilstack.h b/src/syncml_tk/src/sml/xlt/all/xltutilstack.h
new file mode 100755
index 0000000..cc1bbd5
--- /dev/null
+++ b/src/syncml_tk/src/sml/xlt/all/xltutilstack.h
@@ -0,0 +1,139 @@
+/**
+ * @file
+ * XLT Decoder Util
+ *
+ * @target_system all
+ * @target_os all
+ * @description Header file for a simple stack implementation used
+ * by the WBXML scanner and the SyncML parser.
+ */
+
+/*
+ * Copyright Notice
+ * Copyright (c) Ericsson, IBM, Lotus, Matsushita Communication
+ * Industrial Co., Ltd., Motorola, Nokia, Openwave Systems, Inc.,
+ * Palm, Inc., Psion, Starfish Software, Symbian, Ltd. (2001).
+ * All Rights Reserved.
+ * Implementation of all or part of any Specification may require
+ * licenses under third party intellectual property rights,
+ * including without limitation, patent rights (such a third party
+ * may or may not be a Supporter). The Sponsors of the Specification
+ * are not responsible and shall not be held responsible in any
+ * manner for identifying or failing to identify any or all such
+ * third party intellectual property rights.
+ *
+ * THIS DOCUMENT AND THE INFORMATION CONTAINED HEREIN ARE PROVIDED
+ * ON AN "AS IS" BASIS WITHOUT WARRANTY OF ANY KIND AND ERICSSON, IBM,
+ * LOTUS, MATSUSHITA COMMUNICATION INDUSTRIAL CO. LTD, MOTOROLA,
+ * NOKIA, PALM INC., PSION, STARFISH SOFTWARE AND ALL OTHER SYNCML
+ * SPONSORS DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+ * BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
+ * HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
+ * SHALL ERICSSON, IBM, LOTUS, MATSUSHITA COMMUNICATION INDUSTRIAL CO.,
+ * LTD, MOTOROLA, NOKIA, PALM INC., PSION, STARFISH SOFTWARE OR ANY
+ * OTHER SYNCML SPONSOR BE LIABLE TO ANY PARTY FOR ANY LOSS OF
+ * PROFITS, LOSS OF BUSINESS, LOSS OF USE OF DATA, INTERRUPTION OF
+ * BUSINESS, OR FOR DIRECT, INDIRECT, SPECIAL OR EXEMPLARY, INCIDENTAL,
+ * PUNITIVE OR CONSEQUENTIAL DAMAGES OF ANY KIND IN CONNECTION WITH
+ * THIS DOCUMENT OR THE INFORMATION CONTAINED HEREIN, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH LOSS OR DAMAGE.
+ *
+ * The above notice and this paragraph must be included on all copies
+ * of this document that are made.
+ *
+ */
+
+/*************************************************************************/
+/* Definitions */
+/*************************************************************************/
+#ifndef _XLT_UTIL_STACK_H
+#define _XLT_UTIL_STACK_H
+
+#include "xlttags.h"
+#include <smldef.h>
+
+/** type for stack elements */
+typedef XltTagID_t XltUtilStackItem_t;
+
+/** @copydoc XltUtilStack_s */
+typedef struct XltUtilStack_s *XltUtilStackPtr_t, XltUtilStack_t;
+/**
+ * XLTUtilStack interface
+ *
+ * Like the WBXML/XML scanner, this stack implementation tries to emulate
+ * an object-oriented interface. It consist of one stack structure that
+ * contains the public methods and attributes and another private stack
+ * structure that is not visible to the users of the stack. A stack object
+ * has the following public methods:
+ */
+struct XltUtilStack_s
+{
+ /**
+ * Returns the top element of the stack.
+ *
+ * @pre the stack contains at least one element
+ * @param XltUtilStackPtr_t (IN)
+ * the stack
+ * @param XltUtilStackItem_t (OUT)
+ * the top element of the stack
+ * @return - SML_ERR_WRONG_USAGE, if the stack is empty
+ * - SML_ERR_OK, else
+ */
+ Ret_t (*top)(const XltUtilStackPtr_t, XltUtilStackItem_t *);
+
+ /**
+ * Returns the top element and takes it off the stack.
+ *
+ * @pre the stack contains at least one element
+ * @post the top element of the stack is removed
+ * @param XltUtilStackPtr_t (IN/OUT)
+ * the stack
+ * @param XltUtilStackItem_t (OUT)
+ * the top element of the stack
+ * @return - SML_ERR_WRONG_USAGE, if the stack is empty
+ * - SML_ERR_NOT_ENOUGH_SPACE, if memory reallocation failed
+ * - SML_ERR_OK, else
+ */
+ Ret_t (*pop)(XltUtilStackPtr_t, XltUtilStackItem_t *);
+
+ /**
+ * Put a new element on top of the stack.
+ *
+ * @post popping the stack yields the same stack as before the push
+ * @param XltUtilStackPtr_t (IN/OUT)
+ * the stack
+ * @param XltUtilStackItem_t (IN)
+ * the new stack element
+ * @return - SML_ERR_NOT_ENOUGH_SPACE, if memory reallocation failed
+ * - SML_ERR_OK, else
+ */
+ Ret_t (*push)(XltUtilStackPtr_t, const XltUtilStackItem_t);
+
+ /**
+ * Free the memory used by the stack.
+ *
+ * @param XltUtilStackPtr_t (IN/OUT)
+ * the stack
+ * @return - SML_ERR_OK
+ */
+ Ret_t (*destroy)(XltUtilStackPtr_t);
+};
+
+/**
+ * Creates a new stack. The size parameter indicates for how many elements
+ * memory should be allocated initially. This does _not_ mean that you can
+ * not push more than that many element onto the stack - in that case
+ * memory for another size elements is allocated.
+ *
+ * @post the stack pointer points to a new, empty stack
+ * @param ppStack (OUT)
+ * a new stack
+ * @param size (IN)
+ * the initial size of the stack
+ * @return - SML_ERR_NOT_ENOUGH_SPACE, if memory allocation failed
+ * - SML_ERR_OK, else
+ */
+Ret_t xltUtilCreateStack(XltUtilStackPtr_t *ppStack, const Long_t size);
+
+#endif
diff --git a/src/syncml_tk/src/sml/xlt/inc/xltdec.h b/src/syncml_tk/src/sml/xlt/inc/xltdec.h
new file mode 100755
index 0000000..eb096c3
--- /dev/null
+++ b/src/syncml_tk/src/sml/xlt/inc/xltdec.h
@@ -0,0 +1,227 @@
+/**
+ * @file
+ * Interface for the XLT Decoder component.
+ *
+ * @target_system all
+ * @target_os all
+ * @description Interface for the WBXML and XML decoder component.
+ */
+
+
+/*
+ * Copyright Notice
+ * Copyright (c) Ericsson, IBM, Lotus, Matsushita Communication
+ * Industrial Co., Ltd., Motorola, Nokia, Openwave Systems, Inc.,
+ * Palm, Inc., Psion, Starfish Software, Symbian, Ltd. (2001).
+ * All Rights Reserved.
+ * Implementation of all or part of any Specification may require
+ * licenses under third party intellectual property rights,
+ * including without limitation, patent rights (such a third party
+ * may or may not be a Supporter). The Sponsors of the Specification
+ * are not responsible and shall not be held responsible in any
+ * manner for identifying or failing to identify any or all such
+ * third party intellectual property rights.
+ *
+ * THIS DOCUMENT AND THE INFORMATION CONTAINED HEREIN ARE PROVIDED
+ * ON AN "AS IS" BASIS WITHOUT WARRANTY OF ANY KIND AND ERICSSON, IBM,
+ * LOTUS, MATSUSHITA COMMUNICATION INDUSTRIAL CO. LTD, MOTOROLA,
+ * NOKIA, PALM INC., PSION, STARFISH SOFTWARE AND ALL OTHER SYNCML
+ * SPONSORS DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+ * BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
+ * HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
+ * SHALL ERICSSON, IBM, LOTUS, MATSUSHITA COMMUNICATION INDUSTRIAL CO.,
+ * LTD, MOTOROLA, NOKIA, PALM INC., PSION, STARFISH SOFTWARE OR ANY
+ * OTHER SYNCML SPONSOR BE LIABLE TO ANY PARTY FOR ANY LOSS OF
+ * PROFITS, LOSS OF BUSINESS, LOSS OF USE OF DATA, INTERRUPTION OF
+ * BUSINESS, OR FOR DIRECT, INDIRECT, SPECIAL OR EXEMPLARY, INCIDENTAL,
+ * PUNITIVE OR CONSEQUENTIAL DAMAGES OF ANY KIND IN CONNECTION WITH
+ * THIS DOCUMENT OR THE INFORMATION CONTAINED HEREIN, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH LOSS OR DAMAGE.
+ *
+ * The above notice and this paragraph must be included on all copies
+ * of this document that are made.
+ *
+ */
+
+/*************************************************************************/
+/* Definitions */
+/*************************************************************************/
+#ifndef _XLT_DEC_H
+#define _XLT_DEC_H
+
+#include <smldef.h>
+#include <smldtd.h>
+#include <smlerr.h>
+
+#include "xltdeccom.h"
+#include "xltutilstack.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Better parsing error reporting when used within Synthesis SyncML engine
+ * added 2006-11-02 by luz@synthesis.ch
+ */
+#ifdef SYDEBUG
+ #include "sysync_debug.h"
+ Ret_t show_decode_error(Ret_t aRc, XltDecScannerPtr_t aScanner, char *aRoutineName);
+ #define SML_DECODEERROR(e,sc,n) show_decode_error(e,(XltDecScannerPtr_t)sc,n)
+#else
+ #define SML_DECODEERROR(e,sc,n) (e)
+#endif
+
+
+/**
+ * The XLT Decoder Interface consists of a single XltDecoder "object"
+ * (struct) and an creation/initialization function. The XltDecoder
+ * object contains all "public" methods and data structures. The first
+ * parameter for any public method is the object of which the method is
+ * called.
+ */
+typedef struct XltDecoder_s
+{
+ /**
+ * Character set used in the document - this is the MIBEnum value assigned
+ * by the IANA for the character encoding, e.g. "3" for US-ASCII.
+ */
+ Long_t charset;
+ /**
+ * Name of the character set, e.g. "US-ASCII" - valid
+ * only when charset == 0.
+ */
+ String_t charsetStr;
+ /**
+ * Indicates whether the decoder has reached the end of the buffer during
+ * the last call to xltDecNext.
+ */
+ Flag_t finished;
+
+ Boolean_t final;
+
+ /**
+ * Pointer to the scanner status object used by this decoder. The scanner
+ * will be created during the initialization of the decoder as either a XML
+ * or WBXML scanner.
+ */
+ XltDecScannerPtr_t scanner;
+
+ /**
+ * The decoder uses an internal stack to check that for every start tag
+ * there is a corresponding end tag.
+
+ */
+ XltUtilStackPtr_t tagstack;
+
+} XltDecoder_t, *XltDecoderPtr_t;
+
+/**
+ * Initializes a new decoder object. This function allocates memory for the
+ * decoder structure which has to be freed by a call to the decoder's
+ * terminate method when the decoder is not needed anymore. As part of the
+ * initialization the decoder begins decoding the SyncML document to find
+ * the SyncHdr element.
+ *
+ * @pre ppDecoder is NULL
+ * ppBufPos
+ * @post ppDecoder points to an initialized decoder status object
+ * @param enc (IN)
+ * the document encoding (WBXML or XML)
+ * @param pBufEnd (IN)
+ * pointer to the end of the buffer which contains the document
+ * @param ppBufPos (IN/OUT)
+ * pointer to the current position within the buffer
+ * @param ppDecoder (OUT)
+ * the decoder status object
+ * @param ppSyncHdr (OUT)
+ * the SyncHdr element
+ * @return
+ * - SML_ERR_OK, if the decoder could be created and the
+ * SmlSyncHdr was found
+ * - else error code
+ */
+Ret_t xltDecInit(const SmlEncoding_t enc,
+ const MemPtr_t pBufEnd,
+ MemPtr_t *ppBufPos,
+ XltDecoderPtr_t *ppDecoder,
+ SmlSyncHdrPtr_t *ppSyncHdr) XLT_FUNC;
+
+/**
+ * Decodes the next protocol element of the given SyncML document. This
+ * function creates the data structures detailed in the SMLDtd header file.
+ * It is the responsibility of the SyncML client application to free the
+ * allocated memory after it is done processing the data.
+ * This function sets the decoder's finished flag if no protocol element was
+ * found. In that case pPE is set to SML_PE_UNDEF and pContent is NULL.
+ *
+ * @pre pDecoder points to a decoder status object initialized by xltDecInit
+ * @post pPE and pContent describe the next valid protocol
+ * element within the SyncML document OR
+ * the finished flag of the decoder status object is set
+ * @param pBufEnd (IN)
+ * pointer to the end of the buffer
+ * @param pDecoder (IN/OUT)
+ * the decoder status object
+ * @param ppBufPos (IN/OUT)
+ * pointer to the current position within the
+ * buffer before and after the call to xltDecNext
+ * @param pPE (OUT)
+ * the type of the protocol element (e.g. SML_PE_ADD)
+ * @param pContent (OUT)
+ * the data structure for the p.e. cast
+ * (e.g. AddPtr_t) to a void pointer
+ * @return
+ * - SML_ERR_OK, if a valid protocol element was found
+ * or if decoder reached the end of the buffer
+ * - else error code showing where the parsing failed
+ */
+Ret_t xltDecNext(XltDecoderPtr_t pDecoder,
+ const MemPtr_t pBufEnd,
+ MemPtr_t *ppBufPos,
+ SmlProtoElement_t *pPE,
+ VoidPtr_t *pContent) XLT_FUNC;
+
+/**
+ * Frees the memory allocated by the decoder.
+ *
+ * @pre pDecoder points to a decoder status object initialized by xltDecInit
+ * @post all memory allocated by the decoder status object is freed
+ * @param pDecoder (IN)
+ * the decoder
+ * @return
+ * - SML_ERR_OK, if the memory could be freed
+ * - else error code
+ */
+Ret_t xltDecTerminate(XltDecoderPtr_t pDecoder) XLT_FUNC;
+
+
+Ret_t xltDecReset(XltDecoderPtr_t pDecoder) XLT_FUNC;
+
+/* T.K. moved here from xltdec.c for use in sub-DTD parsing */
+#define IS_START(tok) ((tok)->type == TOK_TAG_START)
+#define IS_END(tok) ((tok)->type == TOK_TAG_END)
+#define IS_EMPTY(tok) ((tok)->type == TOK_TAG_EMPTY)
+#define IS_TAG(tok) (IS_START(tok) || IS_EMPTY(tok) || IS_END(tok))
+#define IS_START_OR_EMPTY(tok) (IS_START(tok) || IS_EMPTY(tok))
+#define IS_CONTENT(tok) ((tok)->type == TOK_CONT)
+/**
+ * just wrapper around the scanner's
+ * nextTok methods that do some error checking.
+ */
+Ret_t nextToken(XltDecoderPtr_t pDecoder) XLT_FUNC;
+/**
+ * just wrapper around the scanner's
+ * pushTok methods that do some error checking.
+ */
+Ret_t discardToken(XltDecoderPtr_t pDecoder) XLT_FUNC;
+/* eof xltdec.c stuff */
+
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/syncml_tk/src/sml/xlt/inc/xltenc.h b/src/syncml_tk/src/sml/xlt/inc/xltenc.h
new file mode 100755
index 0000000..62004f2
--- /dev/null
+++ b/src/syncml_tk/src/sml/xlt/inc/xltenc.h
@@ -0,0 +1,92 @@
+/**
+ * @file
+ * Encoder header file
+ *
+ * @target_system All
+ * @target_os All
+ */
+
+/*
+ * Copyright Notice
+ * Copyright (c) Ericsson, IBM, Lotus, Matsushita Communication
+ * Industrial Co., Ltd., Motorola, Nokia, Openwave Systems, Inc.,
+ * Palm, Inc., Psion, Starfish Software, Symbian, Ltd. (2001).
+ * All Rights Reserved.
+ * Implementation of all or part of any Specification may require
+ * licenses under third party intellectual property rights,
+ * including without limitation, patent rights (such a third party
+ * may or may not be a Supporter). The Sponsors of the Specification
+ * are not responsible and shall not be held responsible in any
+ * manner for identifying or failing to identify any or all such
+ * third party intellectual property rights.
+ *
+ * THIS DOCUMENT AND THE INFORMATION CONTAINED HEREIN ARE PROVIDED
+ * ON AN "AS IS" BASIS WITHOUT WARRANTY OF ANY KIND AND ERICSSON, IBM,
+ * LOTUS, MATSUSHITA COMMUNICATION INDUSTRIAL CO. LTD, MOTOROLA,
+ * NOKIA, PALM INC., PSION, STARFISH SOFTWARE AND ALL OTHER SYNCML
+ * SPONSORS DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+ * BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
+ * HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
+ * SHALL ERICSSON, IBM, LOTUS, MATSUSHITA COMMUNICATION INDUSTRIAL CO.,
+ * LTD, MOTOROLA, NOKIA, PALM INC., PSION, STARFISH SOFTWARE OR ANY
+ * OTHER SYNCML SPONSOR BE LIABLE TO ANY PARTY FOR ANY LOSS OF
+ * PROFITS, LOSS OF BUSINESS, LOSS OF USE OF DATA, INTERRUPTION OF
+ * BUSINESS, OR FOR DIRECT, INDIRECT, SPECIAL OR EXEMPLARY, INCIDENTAL,
+ * PUNITIVE OR CONSEQUENTIAL DAMAGES OF ANY KIND IN CONNECTION WITH
+ * THIS DOCUMENT OR THE INFORMATION CONTAINED HEREIN, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH LOSS OR DAMAGE.
+ *
+ * The above notice and this paragraph must be included on all copies
+ * of this document that are made.
+ *
+ */
+
+/*************************************************************************/
+/* Definitions */
+/*************************************************************************/
+
+#ifndef _XLT_ENC_H
+#define _XLT_ENC_H
+
+
+#include <smlerr.h>
+#include <xltenccom.h>
+#include <smldef.h>
+#include <smldtd.h>
+#include <xlttags.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** Type for storing encoder information */
+typedef struct XltEncoder_s
+{
+ SmlEncoding_t enc;
+ SmlVersion_t vers; // %%% luz 2003-07-31: added SyncML version here
+ SmlPcdataExtension_t cur_ext;
+ SmlPcdataExtension_t last_ext;
+ Boolean_t final;
+ XltSpaceEvaluationPtr_t space_evaluation;
+ MemSize_t end_tag_size;
+} XltEncoder_t, *XltEncoderPtr_t;
+
+
+Ret_t xltEncInit(SmlEncoding_t enc, const SmlSyncHdrPtr_t pHeader, const MemPtr_t pBufEnd, MemPtr_t *ppBufPos, XltEncoderPtr_t *ppEncoder, SmlVersion_t vers) XLT_FUNC;
+Ret_t xltEncAppend(const XltEncoderPtr_t pEncoder, SmlProtoElement_t pe, const MemPtr_t pBufEnd, const VoidPtr_t pContent, MemPtr_t *ppBufPos) XLT_FUNC;
+Ret_t xltEncTerminate(const XltEncoderPtr_t pEncoder, const MemPtr_t pBufEnd, MemPtr_t *ppBufPos) XLT_FUNC;
+Ret_t xltEncReset(XltEncoderPtr_t pEncoder) XLT_FUNC;
+Ret_t xltGenerateTag(XltTagID_t, XltTagType_t, SmlEncoding_t, BufferMgmtPtr_t, SmlPcdataExtension_t) XLT_FUNC;
+Ret_t xltStartEvaluation(XltEncoderPtr_t pEncoder) XLT_FUNC;
+Ret_t xltEndEvaluation(InstanceID_t id, XltEncoderPtr_t pEncoder, MemSize_t *freemem) XLT_FUNC;
+Ret_t xltEncBlock(XltTagID_t tagId, XltRO_t reqOptFlag, const VoidPtr_t pContent, SmlEncoding_t enc, BufferMgmtPtr_t pBufMgr, SmlPcdataExtension_t attFlag) XLT_FUNC;
+Ret_t xltBuildExtention(SmlPcdataExtension_t extId, XltRO_t reqOptFlag, VoidPtr_t pContent, SmlEncoding_t enc, BufferMgmtPtr_t pBufMgr) XLT_FUNC;
+Ret_t xltEncPcdata(XltTagID_t tagId, XltRO_t reqOptFlag, const VoidPtr_t pContent, SmlEncoding_t enc, BufferMgmtPtr_t pBufMgr, SmlPcdataExtension_t attFlag) XLT_FUNC;
+Ret_t subdtdEncWBXML(XltTagID_t tagId, XltRO_t reqOptFlag, const VoidPtr_t pContent, SmlEncoding_t enc, BufferMgmtPtr_t pBufMgr, SmlPcdataExtension_t attFlag) XLT_FUNC;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/syncml_tk_prefix_file.h b/src/syncml_tk_prefix_file.h
new file mode 100755
index 0000000..d141a30
--- /dev/null
+++ b/src/syncml_tk_prefix_file.h
@@ -0,0 +1,9 @@
+// dummy prefix file, used for CW based projects which do
+// not need the prefix file here, but in the target settings
+
+#ifdef _MSC_VER
+#error "this is not the right syncml_tk_prefix_file.h -> reorder include paths such that project-specific prefix is used!"
+#endif
+#ifdef __PALM_OS__
+#error "this is not the right syncml_tk_prefix_file.h -> reorder access paths such that project-specific prefix is used!"
+#endif
diff --git a/src/synthesis-linker.map b/src/synthesis-linker.map
new file mode 100644
index 0000000..079c84b
--- /dev/null
+++ b/src/synthesis-linker.map
@@ -0,0 +1,11 @@
+VER_1.0 {
+ global:
+ SySync_ConnectEngine;
+ SySync_ConnectEngineS;
+ SySync_DisconnectEngine;
+ extern "C++" {
+ sysync::DataConversion*;
+ };
+ local:
+ *;
+};
diff --git a/src/sysync/binfilebase.cpp b/src/sysync/binfilebase.cpp
new file mode 100755
index 0000000..96b69af
--- /dev/null
+++ b/src/sysync/binfilebase.cpp
@@ -0,0 +1,333 @@
+/**
+ * @File binfile.cpp
+ *
+ * @Author Lukas Zeller (luz@synthesis.ch)
+ *
+ * @brief TBinFileBase
+ * Simple record based binary file storage class
+ *
+ * Copyright (c) 2003-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ * @Date 2006-03-28 : luz : extracted into separate file from TBinfileImplDS
+ */
+
+#include "prefix_file.h"
+
+#include "binfilebase.h"
+
+namespace sysync {
+
+// TBinFileBase
+// ============
+
+// constructor
+TBinFileBase::TBinFileBase() :
+ fDestructed(false)
+{
+ fBinFileHeader.idword=0;
+ fBinFileHeader.version=0;
+ fBinFileHeader.headersize=sizeof(TBinFileHeader);
+ fBinFileHeader.recordsize=0;
+ fBinFileHeader.numrecords=0;
+ fBinFileHeader.allocatedrecords=0;
+ fBinFileHeader.uniquerecordid=0;
+ fHeaderDirty=false;
+ fExtraHeaderDirty=false;
+ fExtraHeaderP=NULL;
+ fExtraHeaderSize=0;
+} // TBinFileBase::TBinFileBase
+
+
+// destructor
+TBinFileBase::~TBinFileBase()
+{
+ destruct();
+} // TBinFileBase::~TBinFileBase
+
+
+void TBinFileBase::destruct(void)
+{
+ if (!fDestructed) doDestruct();
+ fDestructed=true;
+} // TBinFileBase::destruct
+
+
+void TBinFileBase::doDestruct(void)
+{
+ // make sure files are closed
+ close();
+} // TBinFileBase::doDestruct
+
+
+
+
+
+// - set path to binary file containing the database
+void TBinFileBase::setFileInfo(const char *aFilename,uInt32 aVersion,uInt32 aIdWord)
+{
+ close();
+ fFilename=aFilename;
+ fIdWord=aIdWord;
+ fVersion=aVersion;
+} // TBinFileBase::setFileInfo
+
+
+// - try to open existing DB file according to params set with setFileInfo
+bferr TBinFileBase::open(uInt32 aExtraHeadersize, void *aExtraHeaderP, TUpdateFunc aUpdateFunc)
+{
+ // make sure it is closed first
+ close();
+ // save extra header info
+ fExtraHeaderSize=aExtraHeadersize;
+ fExtraHeaderP=aExtraHeaderP;
+ // try to open file for (binary) update
+ if (!platformOpenFile(fFilename.c_str(),fopm_update))
+ return BFE_NOTFOUND;
+ // read header
+ fHeaderDirty=false;
+ platformSeekFile(0);
+ if (!platformReadFile(&fBinFileHeader,sizeof(fBinFileHeader))) {
+ close();
+ return BFE_BADSTRUCT;
+ }
+ // check type and Version
+ if (fBinFileHeader.idword!=fIdWord) {
+ close();
+ return BFE_BADTYPE;
+ }
+ if (fBinFileHeader.version!=fVersion) {
+ // try to update file if update-func is provided
+ if (aUpdateFunc) {
+ // check if we can update (no data provided for update)
+ uInt32 oldversion=fBinFileHeader.version;
+ uInt32 newrecordsize=aUpdateFunc(oldversion,fVersion,NULL,NULL,0);
+ if (newrecordsize) {
+ // we can update from current to requested version
+ // - allocate buffer for all records
+ uInt32 numrecords = fBinFileHeader.numrecords;
+ uInt32 oldrecordsize = fBinFileHeader.recordsize;
+ void *oldrecords = malloc(numrecords * oldrecordsize);
+ if (!oldrecords) return BFE_MEMORY;
+ // - read all current records into memory
+ readRecord(0,oldrecords,numrecords);
+ // - truncate the file
+ truncate();
+ // - modify header fields
+ fBinFileHeader.version=fVersion; // update version
+ fBinFileHeader.recordsize=newrecordsize; // update record size
+ fHeaderDirty=true; // header must be updated
+ // - write new header
+ flushHeader();
+ // - now convert buffered records
+ void *newrecord = malloc(newrecordsize);
+ for (uInt32 i=0; i<numrecords; i++) {
+ // call updatefunc to convert record
+ if (aUpdateFunc(oldversion,fVersion,(void *)((uInt8 *)oldrecords+i*oldrecordsize),newrecord,oldrecordsize)) {
+ // save new record
+ uInt32 newi;
+ newRecord(newi,newrecord);
+ }
+ }
+ // - forget buffers
+ free(newrecord);
+ free(oldrecords);
+ }
+ else {
+ // cannot update
+ close();
+ return BFE_BADVERSION;
+ }
+ }
+ else {
+ // cannot update
+ close();
+ return BFE_BADVERSION;
+ }
+ }
+ // check extra header compatibility
+ if (fBinFileHeader.headersize<sizeof(TBinFileHeader)+fExtraHeaderSize) {
+ close();
+ return BFE_BADSTRUCT;
+ }
+ // read extra header
+ if (fExtraHeaderP) {
+ platformReadFile(fExtraHeaderP,fExtraHeaderSize);
+ fExtraHeaderDirty=false;
+ }
+ return BFE_OK;
+} // TBinFileBase::open
+
+
+// - create new DB file according to params set with setFileInfo
+bferr TBinFileBase::create(uInt32 aRecordsize, uInt32 aExtraHeadersize, void *aExtraHeaderP, bool aOverwrite)
+{
+ bferr e;
+
+ close();
+ // try to open
+ e=open(aExtraHeadersize,NULL); // do not pass our new header data in case there is an old file already
+ if (e==BFE_NOTFOUND || aOverwrite) {
+ close();
+ // create new file
+ if (!platformOpenFile(fFilename.c_str(),fopm_create))
+ return BFE_IOERR; // could not create for some reason
+ // prepare header
+ fBinFileHeader.idword=fIdWord;
+ fBinFileHeader.version=fVersion;
+ fBinFileHeader.headersize=sizeof(TBinFileHeader)+aExtraHeadersize;
+ fBinFileHeader.recordsize=aRecordsize;
+ fBinFileHeader.numrecords=0;
+ fBinFileHeader.allocatedrecords=0;
+ fBinFileHeader.uniquerecordid=0;
+ fHeaderDirty=true;
+ // - link in the new extra header buffer
+ fExtraHeaderP=aExtraHeaderP;
+ fExtraHeaderDirty=true; // make sure it gets written
+ // write entire header
+ e=flushHeader();
+ }
+ else if (e==BFE_OK) {
+ // already exists
+ close();
+ e=BFE_EXISTS;
+ }
+ return e;
+} // TBinFileBase::create
+
+
+// - close the file
+bferr TBinFileBase::close(void)
+{
+ if (platformFileIsOpen()) {
+ // remove empty space from end of file
+ truncate(fBinFileHeader.numrecords);
+ // write new header
+ flushHeader();
+ // close file
+ platformCloseFile();
+ }
+ return BFE_OK;
+} // TBinFileBase::close
+
+
+// - close and delete file (full cleanup)
+bferr TBinFileBase::closeAndDelete(void)
+{
+ close();
+ // now delete
+ return platformDeleteFile(fFilename.c_str()) ? BFE_OK : BFE_IOERR;
+} // TBinFileBase::closeAndDelete
+
+
+
+// - flush the header to the file
+bferr TBinFileBase::flushHeader(void)
+{
+ // save header if dirty
+ if (fHeaderDirty) {
+ platformSeekFile(0);
+ platformWriteFile(&fBinFileHeader,sizeof(fBinFileHeader));
+ fHeaderDirty=false;
+ }
+ // save extra header if existing and requested
+ if (fExtraHeaderP && fExtraHeaderDirty) {
+ platformSeekFile(sizeof(fBinFileHeader));
+ platformWriteFile(fExtraHeaderP,fExtraHeaderSize);
+ fExtraHeaderDirty=false;
+ }
+ // make sure data is really flushed in case we get improperly
+ // destructed
+ platformFlushFile();
+ return BFE_OK;
+} // TBinFileBase::flushHeader
+
+
+// - truncate to specified number of records
+bferr TBinFileBase::truncate(uInt32 aNumRecords)
+{
+ if (!platformFileIsOpen()) return BFE_NOTOPEN;
+ platformTruncateFile(fBinFileHeader.headersize+aNumRecords*fBinFileHeader.recordsize);
+ fBinFileHeader.numrecords=aNumRecords;
+ fBinFileHeader.allocatedrecords=aNumRecords;
+ fHeaderDirty=true;
+ return BFE_OK;
+} // TBinFileBase::truncate
+
+
+// - read by index
+bferr TBinFileBase::readRecord(uInt32 aIndex, void *aRecordData, uInt32 aNumRecords)
+{
+ if (!platformFileIsOpen()) return BFE_NOTOPEN;
+ if (aNumRecords==0) return BFE_OK;
+ // find record position in file
+ if (aIndex+aNumRecords>fBinFileHeader.numrecords)
+ return BFE_BADINDEX; // not enough records to read
+ if (!platformSeekFile(fBinFileHeader.headersize+aIndex*fBinFileHeader.recordsize))
+ return BFE_BADINDEX;
+ // read data now
+ if (!platformReadFile(aRecordData,fBinFileHeader.recordsize*aNumRecords))
+ return BFE_IOERR; // could not read as expected
+ return BFE_OK;
+} // TBinFileBase::readRecord
+
+
+// - update by index
+bferr TBinFileBase::updateRecord(uInt32 aIndex, const void *aRecordData, uInt32 aNumRecords)
+{
+ if (!platformFileIsOpen()) return BFE_NOTOPEN;
+ if (aNumRecords==0) return BFE_OK; // nothing to do
+ // find record position in file
+ if (aIndex+aNumRecords>fBinFileHeader.numrecords)
+ return BFE_BADINDEX; // trying to update more records than actually here
+ if (!platformSeekFile(fBinFileHeader.headersize+aIndex*fBinFileHeader.recordsize))
+ return BFE_BADINDEX;
+ // write data now
+ if (!platformWriteFile(aRecordData,fBinFileHeader.recordsize*aNumRecords))
+ return BFE_IOERR; // could not read as expected
+ return BFE_OK;
+} // TBinFileBase::updateRecord
+
+
+// - new record, returns new index
+bferr TBinFileBase::newRecord(uInt32 &aIndex, const void *aRecordData)
+{
+ if (!platformFileIsOpen()) return BFE_NOTOPEN;
+ // go to end of file
+ if (!platformSeekFile(fBinFileHeader.headersize+fBinFileHeader.numrecords*fBinFileHeader.recordsize))
+ return BFE_IOERR;
+ // write a new record
+ if (!platformWriteFile(aRecordData,fBinFileHeader.recordsize))
+ return BFE_IOERR; // could not read as expected
+ // update header
+ aIndex=fBinFileHeader.numrecords++; // return index of new record
+ fBinFileHeader.allocatedrecords++;
+ fHeaderDirty=true;
+ return BFE_OK;
+} // TBinFileBase::newRecord
+
+
+// - delete record
+bferr TBinFileBase::deleteRecord(uInt32 aIndex)
+{
+ if (!platformFileIsOpen()) return BFE_NOTOPEN;
+ if (aIndex>=fBinFileHeader.numrecords) return BFE_BADINDEX;
+ if (aIndex<fBinFileHeader.numrecords-1) {
+ // we need to move last record
+ void *recP = malloc(fBinFileHeader.recordsize);
+ if (!recP) return BFE_IOERR; // no memory
+ // read last record
+ readRecord(fBinFileHeader.numrecords-1,recP);
+ // write it to new position
+ updateRecord(aIndex,recP);
+ free(recP);
+ }
+ fBinFileHeader.numrecords--;
+ fHeaderDirty=true;
+ return BFE_OK;
+} // TBinFileBase::deleteRecord
+
+} // namespace sysync
+
+/* end of TBinFileBase implementation */
+
+// eof
diff --git a/src/sysync/binfilebase.h b/src/sysync/binfilebase.h
new file mode 100755
index 0000000..8814f1f
--- /dev/null
+++ b/src/sysync/binfilebase.h
@@ -0,0 +1,146 @@
+/**
+ * @File binfile.h
+ *
+ * @Author Lukas Zeller (luz@synthesis.ch)
+ *
+ * @brief TBinFileBase
+ * Simple record based binary file storage class
+ *
+ * Copyright (c) 2003-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ * @Date 2006-03-28 : luz : extracted into separate file from TBinfileImplDS
+ */
+/*
+ */
+
+#ifndef BINFILEBASE_H
+#define BINFILEBASE_H
+
+#include "generic_types.h"
+
+// we need STL strings
+#include <string>
+using namespace std;
+
+
+namespace sysync {
+
+#pragma pack(push,4) // 32bit
+
+// general defines for bindatastore
+
+// File header
+typedef struct {
+ uInt32 idword; // identifies the database file type
+ uInt32 version; // identifies the version of the database file
+ uInt32 headersize; // size of header in bytes (specific files might have additional header info)
+ uInt32 recordsize; // size in bytes of a single record
+ uInt32 numrecords; // number of actual records
+ uInt32 allocatedrecords; // number of allocated records in the file (including empty space)
+ uInt32 uniquerecordid; // ever increasing counter to generate unique IDs for records
+} TBinFileHeader;
+
+#pragma pack(pop)
+
+typedef uInt16 bferr;
+#define BFE_OK 0 // ok
+#define BFE_BADVERSION 1 // version mismatch
+#define BFE_BADTYPE 2 // type mismatch
+#define BFE_BADSTRUCT 3 // bad file structure (e.g. extra header size too big)
+#define BFE_NOTFOUND 4 // file not found
+#define BFE_EXISTS 5 // file already exists
+#define BFE_BADINDEX 6 // no such record
+#define BFE_NOTOPEN 7 // file not open
+#define BFE_IOERR 8 // I/O error
+#define BFE_MEMORY 9 // memory error
+
+typedef enum {
+ fopm_update, // open for read and write
+ fopm_create // create for read and write (truncate eventually existing)
+} TFileOpenModes;
+
+typedef uInt32 (*TUpdateFunc)(uInt32 aOldVersion, uInt32 aNewVersion, void *aOldRecordData, void *aNewRecordData, uInt32 aOldSize);
+
+class TBinFileBase
+{
+ // construction/destruction
+private:
+ bool fDestructed; // flag which will be set once destruct() has been called - by the outermost derivate's destructor
+public:
+ TBinFileBase();
+ virtual void doDestruct(void); // will be called by destruct, derived must call inherited if they implement it
+ void destruct(void); // to be called by ALL destructors of derivates.
+ virtual ~TBinFileBase();
+ // DB file access
+ // - set path to binary file containing the database
+ void setFileInfo(const char *aFilename,uInt32 aVersion,uInt32 aIdWord);
+ // - check if open
+ bool isOpen(void) { return platformFileIsOpen(); };
+ // - try to open existing DB file according to params set with setFileInfo
+ bferr open(uInt32 aExtraHeadersize=0, void *aExtraHeaderP=NULL, TUpdateFunc aUpdateFunc=NULL);
+ // - create existing DB file according to params set with setFileInfo
+ bferr create(uInt32 aRecordsize, uInt32 aExtraHeadersize=0, void *aExtraHeaderP=NULL, bool aOverwrite=false);
+ // - truncate to specified number of records
+ bferr truncate(uInt32 aNumRecords=0);
+ // - make the extra header dirty
+ void setExtraHeaderDirty(void) { fExtraHeaderDirty=true; };
+ // - flush the header to the file
+ bferr flushHeader(void);
+ // - close the file
+ bferr close(void);
+ // - close and delete file (full cleanup)
+ bferr closeAndDelete(void);
+ // Info
+ // - number of records
+ uInt32 getNumRecords(void) { return fBinFileHeader.numrecords; };
+ // - net size of record
+ uInt32 getRecordSize(void) { return fBinFileHeader.recordsize; };
+ // - get next unique ID (starts at 1, is never 0!)
+ uInt32 getNextUniqueID(void) { return ++fBinFileHeader.uniquerecordid; fHeaderDirty=true; };
+ // record access
+ // - read by index
+ bferr readRecord(uInt32 aIndex, void *aRecordData, uInt32 aNumRecords=1);
+ // - update by index
+ bferr updateRecord(uInt32 aIndex, const void *aRecorddata, uInt32 aNumRecords=1);
+ // - new record
+ bferr newRecord(const void *aRecorddata) { uInt32 i; return newRecord(i,aRecorddata); };
+ bferr newRecord(uInt32 &aIndex, const void *aRecorddata);
+ // - delete record
+ bferr deleteRecord(uInt32 aIndex);
+protected:
+ // Platform file implementation abstraction
+ // - test if platform file open
+ virtual bool platformFileIsOpen(void) = 0;
+ // - open file
+ virtual bool platformOpenFile(cAppCharP aFilePath, TFileOpenModes aMode) = 0;
+ // - close file
+ virtual bool platformCloseFile(void) = 0;
+ // - seek in file
+ virtual bool platformSeekFile(uInt32 aPos, bool aFromEnd=false) = 0;
+ // - read from file
+ virtual bool platformReadFile(void *aBuffer, uInt32 aMaxRead) = 0;
+ // - write to file
+ virtual bool platformWriteFile(const void *aBuffer, uInt32 aBytes) = 0;
+ // - flush all buffers
+ virtual bool platformFlushFile(void) = 0;
+ // - truncate file to a specific length
+ virtual bool platformTruncateFile(uInt32 aNewSize) = 0;
+ // - delete file entirely
+ virtual bool platformDeleteFile(cAppCharP aFilePath) = 0;
+private:
+ // file identification
+ string fFilename;
+ uInt32 fIdWord;
+ uInt32 fVersion;
+ // cached header
+ bool fHeaderDirty; // set if header must be written back to DB file
+ bool fExtraHeaderDirty; // set if extra header must be written back to DB file
+ TBinFileHeader fBinFileHeader; // standard header
+ void *fExtraHeaderP; // pointer to extra header that will be read at open() and written at close()
+ uInt32 fExtraHeaderSize; // size of extra header bytes at fExtraHeaderP
+}; // TBinFileBase
+
+
+} // namespace sysync
+
+#endif // BINFILEBASE_H
diff --git a/src/sysync/binfileimplclient.cpp b/src/sysync/binfileimplclient.cpp
new file mode 100755
index 0000000..b50b2b5
--- /dev/null
+++ b/src/sysync/binfileimplclient.cpp
@@ -0,0 +1,2772 @@
+/**
+ * @File binfileimplclient.cpp
+ *
+ * @Author Lukas Zeller (luz@synthesis.ch)
+ *
+ * @brief TBinfileImplClient
+ * Represents a client session (agent) that saves profile, target, resume info
+ * and optionally changelog in TBinFile binary files
+ *
+ * Copyright (c) 2003-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ * @Date 2005-09-30 : luz : created from TBinfileImplClient
+ */
+/*
+ */
+
+// includes
+#include "prefix_file.h"
+#include "binfileimplclient.h"
+#include "binfileimplds.h"
+#include "syserial.h"
+
+
+namespace sysync {
+
+
+// Support for EngineModule common interface
+// =========================================
+
+#ifdef ENGINEINTERFACE_SUPPORT
+
+#ifndef ENGINE_LIBRARY
+#ifdef RELEASE_VERSION
+ #error "this is here for Q&D testing with outlook client only"
+#endif
+// factory function implementation - declared in TEngineInterface
+ENGINE_IF_CLASS *newEngine(void)
+{
+ return new TBinfileEngineInterface;
+} // newEngine
+
+#ifdef RELEASE_VERSION
+ #error "this is only here for Q&D testing with outlook client - remove it later, global factory function may no longer be used by engineInterface targets!!"
+#else
+/// @brief returns a new application base.
+TSyncAppBase *TBinfileEngineInterface::newSyncAppBase(void)
+{
+ return sysync::newSyncAppBase(); // use global factory function
+} // TBinfileEngineInterface::newSyncAppBase
+#endif
+
+#endif // not ENGINE_LIBRARY
+
+
+
+// create appropriate root key
+TSettingsKeyImpl *TBinfileEngineInterface::newSettingsRootKey(void)
+{
+ return new TBinfileClientRootKey(this); // return base class which can return some engine infos
+} // TBinfileEngineInterface::newSettingsRootKey
+
+
+
+// Target key
+// ----------
+
+// constructor
+TBinfileTargetKey::TBinfileTargetKey(
+ TEngineInterface *aEngineInterfaceP,
+ sInt32 aTargetIndex,
+ TBinfileDBSyncTarget *aTargetP,
+ TBinfileClientConfig *aBinfileClientConfigP,
+ TBinfileDSConfig *aBinfileDSConfigP
+) :
+ inherited(aEngineInterfaceP),
+ fTargetIndex(aTargetIndex),
+ fTargetP(aTargetP),
+ fBinfileClientConfigP(aBinfileClientConfigP),
+ fBinfileDSConfigP(aBinfileDSConfigP)
+{
+ // nop
+} // TBinfileTargetKey::TBinfileTargetKey
+
+
+// destructor - close key
+TBinfileTargetKey::~TBinfileTargetKey()
+{
+ // closing key
+ if (fTargetP) {
+ if (fDirty && fTargetIndex>=0) {
+ // write back changed record
+ fBinfileClientConfigP->writeTarget(fTargetIndex,*fTargetP);
+ }
+ // now delete the target record
+ delete fTargetP;
+ }
+} // TBinfileTargetKey::~TBinfileTargetKey
+
+
+// return ID of current key
+TSyError TBinfileTargetKey::GetKeyID(sInt32 &aID)
+{
+ aID = fTargetP ? fTargetP->localDBTypeID : KEYVAL_ID_UNKNOWN;
+ return LOCERR_OK;
+} // TBinfileTargetKey::GetKeyID
+
+
+// - read display name
+static TSyError readDispName(
+ TStructFieldsKey *aStructFieldsKeyP, const TStructFieldInfo *aFldInfoP,
+ appPointer aBuffer, memSize aBufSize, memSize &aValSize
+)
+{
+ // get from config
+ TBinfileTargetKey *targetKeyP = static_cast<TBinfileTargetKey *>(aStructFieldsKeyP);
+ cAppCharP dispName = "";
+ #ifndef MINIMAL_CODE
+ dispName = targetKeyP->getBinfileDSConfig()->fDisplayName.c_str();
+ #endif
+ if (*dispName==0) {
+ // no display name, get technical name instead
+ dispName = targetKeyP->getBinfileDSConfig()->getName();
+ }
+ return TStructFieldsKey::returnString(dispName, aBuffer, aBufSize, aValSize);
+} // readDispName
+
+
+// - read availability flag for this datastore
+static TSyError readIsAvailable(
+ TStructFieldsKey *aStructFieldsKeyP, const TStructFieldInfo *aFldInfoP,
+ appPointer aBuffer, memSize aBufSize, memSize &aValSize
+)
+{
+ TBinfileTargetKey *targetKeyP = static_cast<TBinfileTargetKey *>(aStructFieldsKeyP);
+ // - get profile by ID
+ bool avail = false;
+ if (targetKeyP && targetKeyP->getTarget()) {
+ TBinfileDBSyncProfile profile;
+ targetKeyP->getBinfileClientConfig()->getProfileByID(targetKeyP->getProfileID(),profile);
+ avail = targetKeyP->getBinfileClientConfig()->isTargetAvailable(&profile,targetKeyP->getTarget()->localDBTypeID);
+ }
+ return TStructFieldsKey::returnInt(avail, sizeof(avail), aBuffer, aBufSize, aValSize);
+} // readIsAvailable
+
+
+// - read danger flags for this datastore (will return them even if not enabled)
+static TSyError readTargetDangerFlags(
+ TStructFieldsKey *aStructFieldsKeyP, const TStructFieldInfo *aFldInfoP,
+ appPointer aBuffer, memSize aBufSize, memSize &aValSize
+)
+{
+ TBinfileTargetKey *targetKeyP = static_cast<TBinfileTargetKey *>(aStructFieldsKeyP);
+ bool zapsServer, zapsClient;
+ lineartime_t lastSync;
+ uInt32 dbid;
+ // get info
+ uInt8 danger=0;
+ TBinfileDBSyncTarget *targetP;
+ if (targetKeyP && (targetP=targetKeyP->getTarget())) {
+ targetKeyP->getBinfileClientConfig()->getTargetLastSyncTime(*targetP, lastSync, zapsServer, zapsClient, dbid);
+ danger =
+ (zapsServer ? DANGERFLAG_WILLZAPSERVER : 0) +
+ (zapsClient ? DANGERFLAG_WILLZAPCLIENT : 0);
+ }
+ return TStructFieldsKey::returnInt(danger, sizeof(danger), aBuffer, aBufSize, aValSize);
+} // readTargetDangerFlags
+
+
+// - write a readonly bitmask (rdonly_xxx), if this returns LOCERR_OK, readonly is enabled
+static TSyError writeTargetReadOnlyCheck(
+ TStructFieldsKey *aStructFieldsKeyP, const TStructFieldInfo *aFldInfoP,
+ cAppPointer aBuffer, memSize aValSize
+)
+{
+ TBinfileTargetKey *targetKeyP = static_cast<TBinfileTargetKey *>(aStructFieldsKeyP);
+ // get readonly mask to check for
+ uInt16 readOnlyMask = *((uInt16 *)aBuffer);
+ // check in profile
+ bool rdOnly=false;
+ if (targetKeyP && targetKeyP->getTarget()) {
+ TBinfileDBSyncProfile profile;
+ targetKeyP->getBinfileClientConfig()->getProfileByID(targetKeyP->getProfileID(),profile);
+ rdOnly = targetKeyP->getBinfileClientConfig()->isReadOnly(
+ &profile,
+ readOnlyMask
+ );
+ }
+ // if feature is enabled for this profile, return LOCERR_OK, DB_NoContent otherwise
+ return rdOnly ? LOCERR_OK : DB_NoContent;
+} // writeTargetReadOnlyCheck
+
+
+// - write a feature number (APPFTR_xxx), if this returns LOCERR_OK, feature is available, otherwise not
+static TSyError writeTargetFeatureCheck(
+ TStructFieldsKey *aStructFieldsKeyP, const TStructFieldInfo *aFldInfoP,
+ cAppPointer aBuffer, memSize aValSize
+)
+{
+ TBinfileTargetKey *targetKeyP = static_cast<TBinfileTargetKey *>(aStructFieldsKeyP);
+ // get feature to check for
+ uInt16 featureNo = *((uInt16 *)aBuffer);
+ // extra check for those that only make sense together with certain fDSAvailFlag
+ bool avail=true;
+ uInt16 dsAvailFlags = targetKeyP->getBinfileDSConfig()->fDSAvailFlag;
+ if (featureNo==APP_FTR_EVENTRANGE && (dsAvailFlags & dsavail_events)==0)
+ avail=false; // event range makes sense for events only
+ if (featureNo==APP_FTR_EMAILRANGE && (dsAvailFlags & dsavail_emails)==0)
+ avail=false; // email range makes sense for emails only
+ // check on profile (and global) level
+ if (avail && targetKeyP && targetKeyP->getTarget()) {
+ TBinfileDBSyncProfile profile;
+ targetKeyP->getBinfileClientConfig()->getProfileByID(targetKeyP->getProfileID(),profile);
+ avail = targetKeyP->getBinfileClientConfig()->isFeatureEnabled(
+ &profile,
+ featureNo
+ );
+ }
+ // if feature is enabled for this profile, return LOCERR_OK, DB_NoContent otherwise
+ return avail ? LOCERR_OK : DB_NoContent;
+} // writeTargetFeatureCheck
+
+
+// macro simplifying typing in the table below
+#define OFFS_SZ_TG(n) (offsetof(TBinfileDBSyncTarget,n)), sizeof(dP_tg->n)
+// dummy pointer needed for sizeof
+static const TBinfileDBSyncTarget *dP_tg=NULL;
+
+// accessor table for target key and for TARGETSETTING() function
+const TStructFieldInfo TargetFieldInfos[] =
+{
+ // valName, valType, writable, fieldOffs, valSiz
+ { "enabled", VALTYPE_ENUM, true, OFFS_SZ_TG(enabled) },
+ { "forceslow", VALTYPE_ENUM, true, OFFS_SZ_TG(forceSlowSync) },
+ { "syncmode", VALTYPE_ENUM, true, OFFS_SZ_TG(syncmode) },
+ { "limit1", VALTYPE_INT32, true, OFFS_SZ_TG(limit1) },
+ { "limit2", VALTYPE_INT32, true, OFFS_SZ_TG(limit2) },
+ { "extras", VALTYPE_INT32, true, OFFS_SZ_TG(extras) },
+ { "localpath", VALTYPE_TEXT, true, OFFS_SZ_TG(localDBPath) },
+ { "remotepath", VALTYPE_TEXT, true, OFFS_SZ_TG(remoteDBpath) },
+ #if defined(DESKTOP_CLIENT) || TARGETS_DB_VERSION>4
+ { "localcontainer", VALTYPE_TEXT, true, OFFS_SZ_TG(localContainerName) },
+ #endif
+ // read-only status info
+ { "dbname", VALTYPE_TEXT, false, OFFS_SZ_TG(dbname) },
+ { "lastSync", VALTYPE_TIME64, false, OFFS_SZ_TG(lastSync) },
+ { "lastToRemoteSync", VALTYPE_TIME64, false, OFFS_SZ_TG(lastTwoWaySync) },
+ { "resumeAlertCode", VALTYPE_INT16, true, OFFS_SZ_TG(resumeAlertCode) },
+ // programmatic danger flags (will sync zap client or server data?)
+ { "dangerFlags", VALTYPE_INT8, false, 0,0, &readTargetDangerFlags, NULL },
+ // special programmatic availability and feature checks
+ { "isAvailable", VALTYPE_ENUM, false, 0,0, &readIsAvailable, NULL }, // datastore availability
+ { "checkForReadOnly", VALTYPE_INT16, true, 0,0, NULL, &writeTargetReadOnlyCheck }, // target level readonly check
+ { "checkForFeature", VALTYPE_INT16, true, 0,0, NULL, &writeTargetFeatureCheck }, // target level feature check
+ // display name from config
+ { "dispName", VALTYPE_TEXT, false, 0,0, &readDispName, NULL }, // display name from config
+ // new fields of TARGETS_DB_VERSION 6 and beyond
+ #if TARGETS_DB_VERSION>5
+ { "lastSyncIdentifer", VALTYPE_TEXT, false, OFFS_SZ_TG(lastSyncIdentifier) },
+ { "remoteDispName", VALTYPE_TEXT, false, OFFS_SZ_TG(remoteDBdispName) },
+ // filtering
+ { "filterCapDesc", VALTYPE_TEXT, false, OFFS_SZ_TG(filterCapDesc) },
+ { "remoteFilters", VALTYPE_TEXT, true, OFFS_SZ_TG(remoteFilters) },
+ { "localFilters", VALTYPE_TEXT, true, OFFS_SZ_TG(localFilters) },
+ #endif
+};
+
+
+// get table describing the fields in the struct
+const TStructFieldInfo *TBinfileTargetKey::getFieldsTable(void)
+{
+ return TargetFieldInfos;
+} // TBinfileTargetKey::getFieldsTable
+
+sInt32 TBinfileTargetKey::numFields(void)
+{
+ return sizeof(TargetFieldInfos)/sizeof(TStructFieldInfo);
+} // TBinfileTargetKey::numFields
+
+
+
+// get actual struct base address
+uInt8P TBinfileTargetKey::getStructAddr(void)
+{
+ return (uInt8P)fTargetP;
+} // TBinfileTargetKey::getStructAddr
+
+
+// Profiles container key
+// ----------------------
+
+// constructor
+TBinfileTargetsKey::TBinfileTargetsKey(TEngineInterface *aEngineInterfaceP, sInt32 aProfileID) :
+ inherited(aEngineInterfaceP),
+ fProfileID(aProfileID),
+ fTargetIterator(-1)
+{
+ // get pointer to BinFileClientConfig
+ fBinfileClientConfigP =
+ static_cast<TBinfileClientConfig *>(
+ aEngineInterfaceP->getSyncAppBase()->getRootConfig()->fAgentConfigP
+ );
+} // TBinfileTargetsKey::TBinfileTargetsKey
+
+
+// target can be opened only by (dbtype-)ID
+TSyError TBinfileTargetsKey::OpenSubkey(
+ TSettingsKeyImpl *&aSettingsKeyP,
+ sInt32 aID, uInt16 aMode
+)
+{
+ TBinfileDBSyncTarget *targetP = new TBinfileDBSyncTarget;
+ TSyError sta = LOCERR_OK;
+ sInt32 targetIndex = -1;
+ // safety check to see if targets are open at all
+ if (!fBinfileClientConfigP->fTargetsBinFile.isOpen())
+ return LOCERR_WRONGUSAGE;
+ while(true) {
+ switch (aID) {
+ case KEYVAL_ID_FIRST:
+ fTargetIterator = 0; // go to first
+ goto getnthtarget;
+ // then fetch next
+ case KEYVAL_ID_NEXT:
+ // increment
+ fTargetIterator++;
+ getnthtarget:
+ // get n-th target as indicated by iterator
+ if (fTargetIterator>=0)
+ targetIndex = fBinfileClientConfigP->findTargetIndex(fProfileID,fTargetIterator);
+ if (targetIndex>=0)
+ goto gettarget; // next target found in iteration, get it
+ // no more targets found
+ sta=DB_NoContent; // no more targets
+ break;
+ default:
+ // get target by ID
+ if (aID<0) {
+ sta=LOCERR_WRONGUSAGE;
+ break;
+ }
+ // find target by dbtypeid
+ targetIndex = fBinfileClientConfigP->findTargetIndexByDBInfo(fProfileID,aID,NULL);
+ gettarget:
+ // get target by index
+ targetIndex = fBinfileClientConfigP->getTarget(targetIndex,*targetP);
+ // check error
+ if (targetIndex<0)
+ sta=DB_NotFound; // target not found
+ break;
+ }
+ if (sta==LOCERR_OK && targetIndex>=0) {
+ // we have loaded a target, create subkey handler and pass data
+ // - find related datastore config (by dbname)
+ TBinfileDSConfig *dsCfgP = static_cast<TBinfileDSConfig *>(
+ fBinfileClientConfigP->getLocalDS(targetP->dbname)
+ );
+ if (dsCfgP==NULL) {
+ // this target entry is not configured in the config -> skip it while iterating,
+ // or return "DB error" if explicitly addressed
+ // - re-enter iteration if iterating
+ if (aID==KEYVAL_ID_FIRST)
+ aID=KEYVAL_ID_NEXT;
+ if (aID==KEYVAL_ID_NEXT)
+ continue; // re-iterate
+ // otherwise return DB error to signal inconsistency between target and config
+ sta=DB_Error;
+ }
+ else {
+ aSettingsKeyP = new TBinfileTargetKey(fEngineInterfaceP,targetIndex,targetP,fBinfileClientConfigP,dsCfgP);
+ if (aSettingsKeyP)
+ targetP=NULL; // ownership passed
+ else
+ sta=LOCERR_OUTOFMEM; // cannot create object
+ }
+ }
+ // done, no re-iteration needed
+ break;
+ } // while true
+ // get rid of it if we couldn't pass it to the subkey handler
+ if (targetP)
+ delete targetP;
+ // done
+ return sta;
+} // TBinfileTargetsKey::OpenSubkey
+
+
+
+#ifdef AUTOSYNC_SUPPORT
+
+// Autosync level key
+// ------------------
+
+// constructor
+TBinfileASLevelKey::TBinfileASLevelKey(
+ TEngineInterface *aEngineInterfaceP,
+ sInt32 aLevelIndex,
+ TBinfileDBSyncProfile *aProfileP,
+ TBinfileClientConfig *aBinfileClientConfigP
+) :
+ inherited(aEngineInterfaceP),
+ fLevelIndex(aLevelIndex),
+ fProfileP(aProfileP),
+ fBinfileClientConfigP(aBinfileClientConfigP)
+{
+} // TBinfileASLevelKey::TBinfileASLevelKey
+
+
+// destructor - close key
+TBinfileASLevelKey::~TBinfileASLevelKey()
+{
+ // closing key
+ // NOP here
+} // TBinfileASLevelKey::~TBinfileASLevelKey
+
+
+// return ID of current key
+TSyError TBinfileASLevelKey::GetKeyID(sInt32 &aID)
+{
+ aID = fLevelIndex; // ID is level index
+ return LOCERR_OK;
+} // TBinfileASLevelKey::GetKeyID
+
+
+// macro simplifying typing in the table below
+#define OFFS_SZ_AS(n) (offsetof(TAutoSyncLevel,n)), sizeof(dP_as->n)
+// dummy pointer needed for sizeof
+static const TAutoSyncLevel *dP_as=NULL;
+
+// accessor table for profiles
+static const TStructFieldInfo ASLevelFieldInfos[] =
+{
+ // valName, valType, writable, fieldOffs, valSiz
+ { "mode", VALTYPE_ENUM, true, OFFS_SZ_AS(Mode) },
+ { "startDayTime", VALTYPE_INT16, true, OFFS_SZ_AS(StartDayTime) },
+ { "endDayTime", VALTYPE_INT16, true, OFFS_SZ_AS(EndDayTime) },
+ { "weekdayMask", VALTYPE_INT8, true, OFFS_SZ_AS(WeekdayMask) },
+ { "chargeLevel", VALTYPE_INT8, true, OFFS_SZ_AS(ChargeLevel) },
+ { "memLevel", VALTYPE_INT8, true, OFFS_SZ_AS(MemLevel) },
+ { "flags", VALTYPE_INT8, true, OFFS_SZ_AS(Flags) },
+};
+
+
+// get table describing the fields in the struct
+const TStructFieldInfo *TBinfileASLevelKey::getFieldsTable(void)
+{
+ return ASLevelFieldInfos;
+} // TBinfileASLevelKey::getFieldsTable
+
+sInt32 TBinfileASLevelKey::numFields(void)
+{
+ return sizeof(ASLevelFieldInfos)/sizeof(TStructFieldInfo);
+} // TBinfileASLevelKey::numFields
+
+
+
+// get actual struct base address
+uInt8P TBinfileASLevelKey::getStructAddr(void)
+{
+ return (uInt8P)&(fProfileP->AutoSyncLevel[fLevelIndex]);
+} // TBinfileASLevelKey::getStructAddr
+
+
+// Autosync levels container key
+// -----------------------------
+
+// constructor
+TBinfileASLevelsKey::TBinfileASLevelsKey(TEngineInterface *aEngineInterfaceP, TBinfileDBSyncProfile *aProfileP) :
+ inherited(aEngineInterfaceP),
+ fASLevelIterator(-1),
+ fProfileP(aProfileP)
+{
+ // get pointer to BinFileClientConfig
+ fBinfileClientConfigP =
+ static_cast<TBinfileClientConfig *>(
+ aEngineInterfaceP->getSyncAppBase()->getRootConfig()->fAgentConfigP
+ );
+} // TBinfileASLevelsKey::TBinfileASLevelsKey
+
+
+// autosync levels can be opened only by ID
+TSyError TBinfileASLevelsKey::OpenSubkey(
+ TSettingsKeyImpl *&aSettingsKeyP,
+ sInt32 aID, uInt16 aMode
+)
+{
+ TSyError sta = LOCERR_OK;
+
+ switch (aID) {
+ case KEYVAL_ID_FIRST:
+ fASLevelIterator = 0; // go to first
+ goto getlevel;
+ // then fetch next
+ case KEYVAL_ID_NEXT:
+ // increment
+ fASLevelIterator++;
+ getlevel:
+ // use iterator as ID (ID=level index)
+ aID = fASLevelIterator;
+ default:
+ // open specified ID
+ if (aID<0) {
+ sta=LOCERR_WRONGUSAGE;
+ }
+ else if (aID>=NUM_AUTOSYNC_LEVELS)
+ sta=DB_NoContent; // no more autosync levels
+ else {
+ fASLevelIterator = aID;
+ aSettingsKeyP = new TBinfileASLevelKey(fEngineInterfaceP,aID,fProfileP,fBinfileClientConfigP);
+ }
+ break;
+ }
+ // done
+ return sta;
+} // TBinfileASLevelsKey::OpenSubkey
+
+#endif // AUTOSYNC_SUPPORT
+
+
+// Profile key
+// -----------
+
+// constructor
+TBinfileProfileKey::TBinfileProfileKey(
+ TEngineInterface *aEngineInterfaceP,
+ sInt32 aProfileIndex, // if <0, aProfileP is the running session's profile record
+ TBinfileDBSyncProfile *aProfileP,
+ TBinfileClientConfig *aBinfileClientConfigP
+) :
+ inherited(aEngineInterfaceP),
+ fProfileIndex(aProfileIndex),
+ fProfileP(aProfileP),
+ fBinfileClientConfigP(aBinfileClientConfigP)
+{
+} // TBinfileProfileKey::TBinfileProfileKey
+
+
+// destructor - close key
+TBinfileProfileKey::~TBinfileProfileKey()
+{
+ // closing key
+ if (fProfileP) {
+ if (fDirty && fProfileIndex>=0) {
+ // write back changed record
+ fBinfileClientConfigP->writeProfile(fProfileIndex,*fProfileP);
+ }
+ // now delete the profile record if it is not a running session's (because then it is just passed)
+ if (fProfileIndex>=0)
+ delete fProfileP;
+ }
+} // TBinfileProfileKey::~TBinfileProfileKey
+
+
+// return ID of current key
+TSyError TBinfileProfileKey::GetKeyID(sInt32 &aID)
+{
+ aID = getProfileID();
+ return LOCERR_OK;
+} // TBinfileProfileKey::GetKeyID
+
+
+// open subkey by name (not by path!)
+// - this is the actual implementation
+TSyError TBinfileProfileKey::OpenSubKeyByName(
+ TSettingsKeyImpl *&aSettingsKeyP,
+ cAppCharP aName, stringSize aNameSize,
+ uInt16 aMode
+) {
+ if (fProfileP && strucmp(aName,"targets",aNameSize)==0)
+ aSettingsKeyP = new TBinfileTargetsKey(fEngineInterfaceP,fProfileP->profileID);
+ #ifdef AUTOSYNC_SUPPORT
+ else if (fProfileP && strucmp(aName,"autosynclevels",aNameSize)==0)
+ aSettingsKeyP = new TBinfileASLevelsKey(fEngineInterfaceP,fProfileP);
+ #endif
+ else
+ return inherited::OpenSubKeyByName(aSettingsKeyP,aName,aNameSize,aMode);
+ // opened a key
+ return LOCERR_OK;
+} // TBinfileProfileKey::OpenSubKeyByName
+
+
+// - read danger flags for the profile (combined danger of all enabled datastores)
+static TSyError readDangerFlags(
+ TStructFieldsKey *aStructFieldsKeyP, const TStructFieldInfo *aFldInfoP,
+ appPointer aBuffer, memSize aBufSize, memSize &aValSize
+)
+{
+ TBinfileProfileKey *profileKeyP = static_cast<TBinfileProfileKey *>(aStructFieldsKeyP);
+ bool zapsServer, zapsClient;
+ lineartime_t lastSync;
+ // get info
+ uInt8 danger=0;
+ profileKeyP->getBinfileClientConfig()->getProfileLastSyncTime(
+ profileKeyP->getProfileID(),
+ lastSync, zapsServer, zapsClient
+ );
+ danger =
+ (zapsServer ? DANGERFLAG_WILLZAPSERVER : 0) +
+ (zapsClient ? DANGERFLAG_WILLZAPCLIENT : 0);
+ return TStructFieldsKey::returnInt(danger, sizeof(danger), aBuffer, aBufSize, aValSize);
+} // readDangerFlags
+
+
+
+
+
+// - write a feature number (APPFTR_xxx), if this returns LOCERR_OK, feature is available, otherwise not
+static TSyError writeFeatureCheck(
+ TStructFieldsKey *aStructFieldsKeyP, const TStructFieldInfo *aFldInfoP,
+ cAppPointer aBuffer, memSize aValSize
+)
+{
+ TBinfileProfileKey *profileKeyP = static_cast<TBinfileProfileKey *>(aStructFieldsKeyP);
+ // get feature to check for
+ uInt16 featureNo = *((uInt16 *)aBuffer);
+ // if feature is enabled for this profile, return LOCERR_OK, DB_NoContent otherwise
+ return
+ profileKeyP->getBinfileClientConfig()->isFeatureEnabled(
+ profileKeyP->getProfile(),
+ featureNo
+ )
+ ? LOCERR_OK : DB_NoContent;
+} // writeFeatureCheck
+
+
+// - write a readonly bitmask (rdonly_xxx), if this returns LOCERR_OK, readonly is enabled
+static TSyError writeReadOnlyCheck(
+ TStructFieldsKey *aStructFieldsKeyP, const TStructFieldInfo *aFldInfoP,
+ cAppPointer aBuffer, memSize aValSize
+)
+{
+ TBinfileProfileKey *profileKeyP = static_cast<TBinfileProfileKey *>(aStructFieldsKeyP);
+ // get readonly mask to check for
+ uInt16 readOnlyMask = *((uInt16 *)aBuffer);
+ // check in profile
+ bool rdOnly=
+ profileKeyP->getBinfileClientConfig()->isReadOnly(
+ profileKeyP->getProfile(),
+ readOnlyMask
+ );
+ // if feature is enabled for this profile, return LOCERR_OK, DB_NoContent otherwise
+ return rdOnly ? LOCERR_OK : DB_NoContent;
+} // writeReadOnlyCheck
+
+
+// macro simplifying typing in the table below
+#define OFFS_SZ_PF(n) (offsetof(TBinfileDBSyncProfile,n)), sizeof(dP_pf->n)
+// dummy pointer needed for sizeof
+static const TBinfileDBSyncProfile *dP_pf=NULL;
+
+// accessor table for profiles
+static const TStructFieldInfo ProfileFieldInfos[] =
+{
+ // valName, valType, writable, fieldOffs, valSiz
+ { "profileName", VALTYPE_TEXT, true, OFFS_SZ_PF(profileName) },
+ { "protocol", VALTYPE_ENUM, true, OFFS_SZ_PF(protocol) },
+ { "serverURI", VALTYPE_TEXT, true, OFFS_SZ_PF(serverURI) },
+ { "URIpath", VALTYPE_TEXT, true, OFFS_SZ_PF(URIpath) },
+ { "serverUser", VALTYPE_TEXT, true, OFFS_SZ_PF(serverUser) },
+ { "serverPassword", VALTYPE_TEXT_OBFUS, true, OFFS_SZ_PF(serverPassword) },
+ { "transportUser", VALTYPE_TEXT, true, OFFS_SZ_PF(transportUser) },
+ { "transportPassword", VALTYPE_TEXT_OBFUS, true, OFFS_SZ_PF(transportPassword) },
+ { "socksHost", VALTYPE_TEXT, true, OFFS_SZ_PF(socksHost) },
+ { "proxyHost", VALTYPE_TEXT, true, OFFS_SZ_PF(proxyHost) },
+ { "proxyUser", VALTYPE_TEXT, true, OFFS_SZ_PF(proxyUser) },
+ { "proxyPassword", VALTYPE_TEXT_OBFUS, true, OFFS_SZ_PF(proxyPassword) },
+ { "encoding", VALTYPE_ENUM, true, OFFS_SZ_PF(encoding) },
+ { "syncmlvers", VALTYPE_ENUM, true, OFFS_SZ_PF(lastSyncMLVersion) },
+ { "useProxy", VALTYPE_ENUM, true, OFFS_SZ_PF(useProxy) },
+ { "useConnectionProxy", VALTYPE_ENUM, true, OFFS_SZ_PF(useConnectionProxy) },
+ { "transpFlags", VALTYPE_INT32, true, OFFS_SZ_PF(transpFlags) },
+ { "profileFlags", VALTYPE_INT32, true, OFFS_SZ_PF(profileFlags) },
+ // generic fields for app specific usage
+ { "profileExtra1", VALTYPE_INT32, true, OFFS_SZ_PF(profileExtra1) },
+ { "profileExtra2", VALTYPE_INT32, true, OFFS_SZ_PF(profileExtra2) },
+ { "profileData", VALTYPE_BUF, true, OFFS_SZ_PF(profileData) },
+ // feature/availability/extras flags
+ { "dsAvailFlags", VALTYPE_INT16, true, OFFS_SZ_PF(dsAvailFlags) },
+ { "readOnlyFlags", VALTYPE_INT8, true, OFFS_SZ_PF(readOnlyFlags) },
+ { "remoteFlags", VALTYPE_INT8, false, OFFS_SZ_PF(remoteFlags) },
+ { "featureFlags", VALTYPE_INT8, true, OFFS_SZ_PF(featureFlags) },
+ // programmatic danger flags (will sync zap client or server data?)
+ { "dangerFlags", VALTYPE_INT8, false, 0,0, &readDangerFlags, NULL },
+ // special programmatic feature check
+ { "checkForFeature", VALTYPE_INT16, true, 0,0, NULL, &writeFeatureCheck },
+ { "checkForReadOnly", VALTYPE_INT16, true, 0,0, NULL, &writeReadOnlyCheck },
+ #ifdef AUTOSYNC_SUPPORT
+ // autosync
+ { "timedSyncMobile", VALTYPE_INT16, true, OFFS_SZ_PF(TimedSyncMobilePeriod) },
+ { "timedSyncCradled", VALTYPE_INT16, true, OFFS_SZ_PF(TimedSyncCradledPeriod) },
+ #endif
+};
+
+
+// get table describing the fields in the struct
+const TStructFieldInfo *TBinfileProfileKey::getFieldsTable(void)
+{
+ return ProfileFieldInfos;
+} // TBinfileProfileKey::getFieldsTable
+
+sInt32 TBinfileProfileKey::numFields(void)
+{
+ return sizeof(ProfileFieldInfos)/sizeof(TStructFieldInfo);
+} // TBinfileProfileKey::numFields
+
+
+
+// get actual struct base address
+uInt8P TBinfileProfileKey::getStructAddr(void)
+{
+ return (uInt8P)fProfileP;
+} // TBinfileProfileKey::getStructAddr
+
+
+// Profiles container key
+// ----------------------
+
+// constructor
+TBinfileProfilesKey::TBinfileProfilesKey(TEngineInterface *aEngineInterfaceP) :
+ inherited(aEngineInterfaceP), fProfileIterator(-1)
+{
+ // get pointer to BinFileClientConfig (can be NULL in case engine was never initialized with a config)
+ fBinfileClientConfigP =
+ static_cast<TBinfileClientConfig *>(
+ aEngineInterfaceP->getSyncAppBase()->getRootConfig()->fAgentConfigP
+ );
+ // default to not loosing or upgrading config
+ fMayLooseOldCfg=false;
+} // TBinfileProfilesKey::TBinfileProfilesKey
+
+
+// destructor
+TBinfileProfilesKey::~TBinfileProfilesKey()
+{
+ // make sure all settings are saved
+ if (
+ fBinfileClientConfigP && // it's possible that we get there before engine was ever initialized!
+ (fBinfileClientConfigP->fProfileBinFile.isOpen() || fBinfileClientConfigP->fTargetsBinFile.isOpen())
+ ) {
+ fBinfileClientConfigP->closeSettingsDatabases();
+ fBinfileClientConfigP->openSettingsDatabases(false);
+ }
+} // TBinfileProfilesKey::~TBinfileProfilesKey
+
+
+
+// profiles can be opened only by ID
+TSyError TBinfileProfilesKey::OpenSubkey(
+ TSettingsKeyImpl *&aSettingsKeyP,
+ sInt32 aID, uInt16 aMode
+)
+{
+ TBinfileDBSyncProfile *profileP = NULL;
+ TSyError sta = LOCERR_OK;
+
+ // safety check to see if config is here and profiles are open
+ if (!fBinfileClientConfigP || !(fBinfileClientConfigP->fProfileBinFile.isOpen()))
+ return LOCERR_WRONGUSAGE;
+ // now create a profile record (which will be passed to subkey on success)
+ profileP = new TBinfileDBSyncProfile;
+ // check what to do
+ switch (aID) {
+ case KEYVAL_ID_NEW:
+ // create a new empty profile
+ fProfileIterator = fBinfileClientConfigP->newProfile("empty profile", false);
+ // now get it
+ goto getprofile;
+ case KEYVAL_ID_NEW_DEFAULT:
+ // create new profile with default values
+ fProfileIterator = fBinfileClientConfigP->newProfile("default profile", true);
+ // now get it
+ goto getprofile;
+ case KEYVAL_ID_NEW_DUP:
+ // create duplicate of last opened profile (if any)
+ fProfileIterator = fBinfileClientConfigP->newProfile("duplicated profile", false, fProfileIterator);
+ // now get it
+ goto getprofile;
+ case KEYVAL_ID_FIRST:
+ fProfileIterator = 0; // go to first
+ goto getprofile;
+ // then fetch next
+ case KEYVAL_ID_NEXT:
+ // increment
+ fProfileIterator++;
+ getprofile:
+ // get profile by index
+ if (fProfileIterator>=0)
+ fProfileIterator=fBinfileClientConfigP->getProfile(fProfileIterator,*profileP);
+ goto checkerror;
+ default:
+ if (aID<0) {
+ sta=LOCERR_WRONGUSAGE;
+ break;
+ }
+ // open by ID
+ fProfileIterator=fBinfileClientConfigP->getProfileByID(aID,*profileP);
+ checkerror:
+ // check error
+ if (fProfileIterator<0)
+ sta=DB_NoContent; // no more profiles
+ break;
+ }
+ if (sta==LOCERR_OK && fProfileIterator>=0) {
+ // we have loaded a profile, create subkey handler and pass data
+ aSettingsKeyP = new TBinfileProfileKey(fEngineInterfaceP,fProfileIterator,profileP,fBinfileClientConfigP);
+ if (aSettingsKeyP)
+ profileP=NULL; // ownership passed
+ else
+ sta=LOCERR_OUTOFMEM; // cannot create object
+ }
+ // get rid of it if we couldn't pass it to the subkey handler
+ if (profileP)
+ delete profileP;
+ // done
+ return sta;
+} // TBinfileProfilesKey::OpenSubkey
+
+
+// delete profile by ID
+TSyError TBinfileProfilesKey::DeleteSubkey(sInt32 aID)
+{
+ sInt32 profileIndex = fBinfileClientConfigP->getProfileIndex(aID);
+ if (profileIndex<0) return DB_NotFound;
+ fBinfileClientConfigP->deleteProfile(profileIndex);
+ fProfileIterator = -1; // invalidate iterator
+ return LOCERR_OK;
+} // TBinfileProfilesKey::DeleteSubkey
+
+
+// - write a feature number (APPFTR_xxx), if this returns LOCERR_OK, feature is available, otherwise not
+static TSyError writeGlobalFeatureCheck(
+ TStructFieldsKey *aStructFieldsKeyP, const TStructFieldInfo *aFldInfoP,
+ cAppPointer aBuffer, memSize aValSize
+)
+{
+ // get feature to check for
+ uInt16 featureNo = *((uInt16 *)aBuffer);
+ // if feature is enabled for this profile, return LOCERR_OK, DB_NoContent otherwise
+ return
+ aStructFieldsKeyP->getEngineInterface()->getSyncAppBase()->isFeatureEnabled(featureNo)
+ ? LOCERR_OK : DB_NoContent;
+} // writeGlobalFeatureCheck
+
+// - write a feature number (APPFTR_xxx), if works ok, feature is available, otherwise not
+static TSyError writeProvisioningString(
+ TStructFieldsKey *aStructFieldsKeyP, const TStructFieldInfo *aFldInfoP,
+ cAppPointer aBuffer, memSize aValSize
+)
+{
+ TBinfileProfilesKey *profilesKeyP = static_cast<TBinfileProfilesKey *>(aStructFieldsKeyP);
+ // execute the provisioning string
+ // Note: if a new profile is created, the iterator is set such that KEYVAL_ID_NEXT
+ // will return the profile just added/updated by provisioning
+ sInt32 touchedProfile;
+ cAppCharP p=cAppCharP(aBuffer);
+ TSyError sta = LOCERR_CFGPARSE;
+ while (p && *p) {
+ // push this line
+ if (profilesKeyP->getBinfileClientConfig()->executeProvisioningString(p, touchedProfile)) {
+ // parsed ok
+ sta = LOCERR_OK; // at least one line is ok
+ profilesKeyP->setNextProfileindex(touchedProfile); // such that KEYVAL_ID_NEXT will get the new/modified profile
+ }
+ // search for next line
+ // - skip to end
+ while (*p && *p!=0x0A && *p!=0x0D) ++p;
+ // - skip line end
+ while (*p==0x0A || *p==0x0D) ++p;
+ }
+ return sta;
+} // writeProvisioningString
+
+// - read registration status code
+static TSyError readVarCfgStatus(
+ TStructFieldsKey *aStructFieldsKeyP, const TStructFieldInfo *aFldInfoP,
+ appPointer aBuffer, memSize aBufSize, memSize &aValSize
+)
+{
+ aValSize=2;
+ if (aBufSize>=aValSize) {
+ // get ptr
+ TBinfileProfilesKey *profilesKeyP = static_cast<TBinfileProfilesKey *>(aStructFieldsKeyP);
+ // sanity check
+ if (!(profilesKeyP->fBinfileClientConfigP))
+ return LOCERR_WRONGUSAGE; // it's possible that we get there before engine was ever initialized!
+ // try to access variable part of config (profiles/targets)
+ localstatus sta = profilesKeyP->fBinfileClientConfigP->loadVarConfig(profilesKeyP->fMayLooseOldCfg);
+ if (sta==LOCERR_OK) {
+ // make sure profiles have all targets currently found in config
+ profilesKeyP->fBinfileClientConfigP->checkProfiles();
+ }
+ // copy from config
+ *((uInt16*)aBuffer)=sta;
+ }
+ return LOCERR_OK;
+} // readVarCfgStatus
+
+
+// - read "overwrite" flag
+static TSyError readMayLooseOldConfig(
+ TStructFieldsKey *aStructFieldsKeyP, const TStructFieldInfo *aFldInfoP,
+ appPointer aBuffer, memSize aBufSize, memSize &aValSize
+)
+{
+ return TStructFieldsKey::returnInt(static_cast<TBinfileProfilesKey *>(aStructFieldsKeyP)->fMayLooseOldCfg, 1, aBuffer, aBufSize, aValSize);
+} // readMayLooseOldConfig
+
+
+// - write "overwrite" flag
+static TSyError writeMayLooseOldConfig(
+ TStructFieldsKey *aStructFieldsKeyP, const TStructFieldInfo *aFldInfoP,
+ cAppPointer aBuffer, memSize aValSize
+)
+{
+ // set flag
+ static_cast<TBinfileProfilesKey *>(aStructFieldsKeyP)->fMayLooseOldCfg =
+ *((uInt8 *)aBuffer);
+ return LOCERR_OK;
+} // writeMayLooseOldConfig
+
+
+
+// does not work at this time, but intention would be to get rid of the warning below
+// #pragma GCC diagnostic ignored "-Wno-invalid-offsetof"
+
+// accessor table for profiles
+static const TStructFieldInfo ProfilesFieldInfos[] =
+{
+ // valName, valType, writable, fieldOffs, valSiz
+ { "settingsstatus", VALTYPE_INT16, false, 0, 0, &readVarCfgStatus, NULL },
+ //{ "overwrite", VALTYPE_INT8, true, offsetof(TBinfileProfilesKey,fMayLooseOldCfg), 1 }, //%%% this use of offsetof is not clean, so we use a getter/setter instead
+ { "overwrite", VALTYPE_INT8, true, 0, 0, &readMayLooseOldConfig, &writeMayLooseOldConfig },
+ { "provisioningstring", VALTYPE_TEXT, true, 0, 0, NULL, &writeProvisioningString },
+ { "checkForFeature", VALTYPE_INT16, true, 0, 0, NULL, &writeGlobalFeatureCheck }, // global level feature check
+};
+
+// get table describing the fields in the struct
+const TStructFieldInfo *TBinfileProfilesKey::getFieldsTable(void)
+{
+ return ProfilesFieldInfos;
+} // TBinfileProfilesKey::getFieldsTable
+
+sInt32 TBinfileProfilesKey::numFields(void)
+{
+ return sizeof(ProfilesFieldInfos)/sizeof(TStructFieldInfo);
+} // TBinfileProfilesKey::numFields
+
+// get actual struct base address
+uInt8P TBinfileProfilesKey::getStructAddr(void)
+{
+ return (uInt8P)this;
+} // TBinfileProfilesKey::getStructAddr
+
+
+
+// Log entry key
+// -------------
+
+// constructor
+TBinfileLogKey::TBinfileLogKey(
+ TEngineInterface *aEngineInterfaceP,
+ TLogFileEntry *aLogEntryP,
+ TBinfileClientConfig *aBinfileClientConfigP
+) :
+ inherited(aEngineInterfaceP),
+ fLogEntryP(aLogEntryP),
+ fBinfileClientConfigP(aBinfileClientConfigP)
+{
+} // TBinfileLogKey::TBinfileLogKey
+
+
+// destructor - close key
+TBinfileLogKey::~TBinfileLogKey()
+{
+ // closing key
+ // - dispose log entry memory
+ if (fLogEntryP) delete fLogEntryP;
+} // TBinfileLogKey::~TBinfileLogKey
+
+
+// - read DB display name related to this log entry
+static TSyError readLogDispName(
+ TStructFieldsKey *aStructFieldsKeyP, const TStructFieldInfo *aFldInfoP,
+ appPointer aBuffer, memSize aBufSize, memSize &aValSize
+)
+{
+ TBinfileLogKey *logentryKeyP = static_cast<TBinfileLogKey *>(aStructFieldsKeyP);
+
+ // get config of datastore via DBID
+ TLocalDSConfig *dscfgP = logentryKeyP->getBinfileClientConfig()->getLocalDS(NULL,logentryKeyP->getLogEntry()->dbID);
+ cAppCharP dispName = "";
+ #ifndef MINIMAL_CODE
+ dispName = dscfgP->fDisplayName.c_str();
+ #endif
+ if (*dispName==0) {
+ // no display name, get technical name instead
+ dispName = dscfgP->getName();
+ }
+ return TStructFieldsKey::returnString(dispName, aBuffer, aBufSize, aValSize);
+} // readLogDispName
+
+
+// - read DB display name related to this log entry
+static TSyError readLogProfileName(
+ TStructFieldsKey *aStructFieldsKeyP, const TStructFieldInfo *aFldInfoP,
+ appPointer aBuffer, memSize aBufSize, memSize &aValSize
+)
+{
+ TBinfileLogKey *logentryKeyP = static_cast<TBinfileLogKey *>(aStructFieldsKeyP);
+
+ // get profile data via profile ID
+ TBinfileDBSyncProfile profile;
+ logentryKeyP->getBinfileClientConfig()->getProfileByID(logentryKeyP->getLogEntry()->profileID,profile);
+ return TStructFieldsKey::returnString(profile.profileName, aBuffer, aBufSize, aValSize);
+} // readLogProfileName
+
+
+
+
+// macro simplifying typing in the table below
+#define OFFS_SZ_LOG(n) (offsetof(TLogFileEntry,n)), sizeof(dP_log->n)
+// dummy pointer needed for sizeof
+static const TLogFileEntry *dP_log=NULL;
+
+// accessor table for log entries
+static const TStructFieldInfo LogEntryFieldInfos[] =
+{
+ // valName, valType, writable, fieldOffs, valSiz
+ // - direct access
+ { "time", VALTYPE_TIME64, false, OFFS_SZ_LOG(time) },
+ { "dbtypeid", VALTYPE_INT32, false, OFFS_SZ_LOG(dbID) },
+ { "profileid", VALTYPE_INT32, false, OFFS_SZ_LOG(profileID) },
+ { "status", VALTYPE_INT16, false, OFFS_SZ_LOG(status) },
+ { "mode", VALTYPE_INT16, false, OFFS_SZ_LOG(mode) },
+ { "dirmode", VALTYPE_INT16, false, OFFS_SZ_LOG(dirmode) },
+ { "locAdded", VALTYPE_INT32, false, OFFS_SZ_LOG(locAdded) },
+ { "locUpdated", VALTYPE_INT32, false, OFFS_SZ_LOG(locUpdated) },
+ { "locDeleted", VALTYPE_INT32, false, OFFS_SZ_LOG(locDeleted) },
+ { "remAdded", VALTYPE_INT32, false, OFFS_SZ_LOG(remAdded) },
+ { "remUpdated", VALTYPE_INT32, false, OFFS_SZ_LOG(remUpdated) },
+ { "remDeleted", VALTYPE_INT32, false, OFFS_SZ_LOG(remDeleted) },
+ { "inBytes", VALTYPE_INT32, false, OFFS_SZ_LOG(inBytes) },
+ { "outBytes", VALTYPE_INT32, false, OFFS_SZ_LOG(outBytes) },
+ { "locRejected", VALTYPE_INT32, false, OFFS_SZ_LOG(locRejected) },
+ { "remRejected", VALTYPE_INT32, false, OFFS_SZ_LOG(remRejected) },
+ // - procedural convenience
+ { "dispName", VALTYPE_TEXT, false, 0, 0, &readLogDispName, NULL },
+ { "profileName", VALTYPE_TEXT, false, 0, 0, &readLogProfileName, NULL },
+};
+
+
+// get table describing the fields in the struct
+const TStructFieldInfo *TBinfileLogKey::getFieldsTable(void)
+{
+ return LogEntryFieldInfos;
+} // TBinfileLogKey::getFieldsTable
+
+sInt32 TBinfileLogKey::numFields(void)
+{
+ return sizeof(LogEntryFieldInfos)/sizeof(TStructFieldInfo);
+} // TBinfileLogKey::numFields
+
+
+
+// get actual struct base address
+uInt8P TBinfileLogKey::getStructAddr(void)
+{
+ return (uInt8P)(fLogEntryP);
+} // TBinfileLogKey::getStructAddr
+
+
+
+// Log entries container key
+// -------------------------
+
+// constructor
+TBinfileLogsKey::TBinfileLogsKey(TEngineInterface *aEngineInterfaceP) :
+ inherited(aEngineInterfaceP),
+ fLogEntryIterator(-1)
+{
+ fBinfileClientConfigP =
+ static_cast<TBinfileClientConfig *>(
+ aEngineInterfaceP->getSyncAppBase()->getRootConfig()->fAgentConfigP
+ );
+ // open the log file
+ // - get base path
+ string filepath;
+ fBinfileClientConfigP->getBinFilesPath(filepath);
+ filepath += LOGFILE_DB_NAME;
+ // - try to open
+ fLogFile.setFileInfo(filepath.c_str(),LOGFILE_DB_VERSION,LOGFILE_DB_ID);
+ fLogFile.open(0,NULL,NULL);
+ // Note: errors are not checked here, as no logfile is ok. We'll check before trying to read with isOpen()
+} // TBinfileLogsKey::TBinfileLogsKey
+
+
+// log entries can be opened only by reverse index (0=newest...n=oldest)
+TSyError TBinfileLogsKey::OpenSubkey(
+ TSettingsKeyImpl *&aSettingsKeyP,
+ sInt32 aID, uInt16 aMode
+)
+{
+ // if there is no logfile, we have no entries
+ if (!fLogFile.isOpen()) return DB_NotFound;
+ switch (aID) {
+ case KEYVAL_ID_FIRST:
+ fLogEntryIterator = 0; // go to latest (last in file!)
+ goto getnthentry;
+ // then fetch next
+ case KEYVAL_ID_NEXT:
+ // increment
+ fLogEntryIterator++;
+ goto getnthentry;
+ default:
+ fLogEntryIterator=aID;
+ getnthentry:
+ // get n-th-last log entry as indicated by iterator
+ // - check index range
+ sInt32 numrecs = fLogFile.getNumRecords();
+ if (fLogEntryIterator<0) return LOCERR_WRONGUSAGE;
+ if (fLogEntryIterator>=numrecs) return DB_NoContent;
+ // - retrieve
+ TLogFileEntry *entryP = new TLogFileEntry;
+ if (fLogFile.readRecord(numrecs-1-fLogEntryIterator,entryP) != BFE_OK) {
+ delete entryP;
+ return DB_Fatal;
+ }
+ // - create key and return it (loaded entry gets owned by key)
+ aSettingsKeyP = new TBinfileLogKey(fEngineInterfaceP,entryP,fBinfileClientConfigP);
+ }
+ return LOCERR_OK;
+} // TBinfileLogsKey::OpenSubkey
+
+
+// delete profile by ID
+TSyError TBinfileLogsKey::DeleteSubkey(sInt32 aID)
+{
+ // log can only be cleared entirely
+ if (aID!=KEYVAL_ID_ALL) return LOCERR_WRONGUSAGE;
+ // erase log
+ fLogFile.truncate(0);
+ return LOCERR_OK;
+} // TBinfileLogsKey::DeleteSubkey
+
+
+// Client runtime settings key
+// ---------------------------
+
+
+// Constructor
+TBinFileClientParamsKey::TBinFileClientParamsKey(TEngineInterface *aEngineInterfaceP, TSyncClient *aClientSessionP) :
+ inherited(aEngineInterfaceP,aClientSessionP)
+{
+} // TBinFileClientParamsKey::TBinFileClientParamsKey
+
+
+// open subkey by name (not by path!)
+TSyError TBinFileClientParamsKey::OpenSubKeyByName(
+ TSettingsKeyImpl *&aSettingsKeyP,
+ cAppCharP aName, stringSize aNameSize,
+ uInt16 aMode
+) {
+ if (strucmp(aName,"profile",aNameSize)==0) {
+ // get binfileclient session pointer
+ TBinfileImplClient *bfclientP = static_cast<TBinfileImplClient *>(fClientSessionP);
+ // opens current session's active profile
+ aSettingsKeyP = new TBinfileProfileKey(
+ fEngineInterfaceP,
+ -1, // signals passing active session's profile
+ &bfclientP->fProfile, // pointer to the current session's profile
+ bfclientP->fConfigP // the config
+ );
+ }
+ else
+ return inherited::OpenSubKeyByName(aSettingsKeyP,aName,aNameSize,aMode);
+ // opened a key
+ return LOCERR_OK;
+} // TBinFileClientParamsKey::OpenSubKeyByName
+
+
+#endif // ENGINEINTERFACE_SUPPORT
+
+
+
+
+// Config
+// ======
+
+TBinfileClientConfig::TBinfileClientConfig(TConfigElement *aParentElement) :
+ TClientConfig("BinFileDBClient",aParentElement)
+{
+} // TBinfileClientConfig::TBinfileClientConfig
+
+
+TBinfileClientConfig::~TBinfileClientConfig()
+{
+ clear();
+} // TBinfileClientConfig::~TBinfileClientConfig
+
+
+// init defaults
+void TBinfileClientConfig::clear(void)
+{
+ #ifndef HARDCODED_CONFIG
+ // init defaults
+ fBinFilesPath.erase();
+ #endif
+ fBinFileLog=false;
+ // - clear inherited
+ inherited::clear();
+} // TBinfileClientConfig::clear
+
+
+#ifndef HARDCODED_CONFIG
+
+// config element parsing
+bool TBinfileClientConfig::localStartElement(const char *aElementName, const char **aAttributes, sInt32 aLine)
+{
+ // checking the elements
+ // - binfiles path
+ if (strucmp(aElementName,"binfilespath")==0)
+ expectMacroString(fBinFilesPath);
+ else if (strucmp(aElementName,"binfilelog")==0)
+ expectBool(fBinFileLog);
+ // - none known here
+ else
+ return inherited::localStartElement(aElementName,aAttributes,aLine);
+ // ok
+ return true;
+} // TBinfileClientConfig::localStartElement
+
+#endif
+
+
+// update profile record contents
+static uInt32 profileUpdateFunc(uInt32 aOldVersion, uInt32 aNewVersion, void *aOldRecordData, void *aNewRecordData, uInt32 aOldSize)
+{
+ if (aOldVersion<LOWEST_PROFILE_DB_VERSION || aOldVersion>PROFILE_DB_VERSION) return 0; // unknown old or newer than current version, cannot update
+ if (aNewVersion!=PROFILE_DB_VERSION) return 0; // cannot update to other version than current
+ // create default values for profile
+ if (aOldRecordData && aNewRecordData) {
+ TBinfileDBSyncProfile *profileP = (TBinfileDBSyncProfile *)aNewRecordData;
+ // make an empty default profile
+ TBinfileClientConfig::initProfile(*profileP, "dummy", true);
+ #if (PROFILE_DB_VERSION>=6) && (LOWEST_PROFILE_DB_VERSION<6)
+ // copy in old version's data
+ if (aOldVersion<=5) {
+ // between 5 and 6, sizes of user and pw fields have all changed in size, so we need
+ // to copy field-by-field
+ o_TBinfileDBSyncProfile *oP=(o_TBinfileDBSyncProfile *)aOldRecordData;
+ // now copy
+ profileP->profileID = oP->profileID;
+ AssignCString(profileP->profileName,oP->profileName,maxnamesiz);
+ profileP->encoding = oP->encoding;
+ AssignCString(profileP->serverURI,oP->serverURI,maxurisiz);
+ AssignCString(profileP->serverUser,oP->serverUser,maxupwsiz);
+ memcpy(profileP->serverPassword,oP->serverPassword,o_maxupwsiz);
+ AssignCString(profileP->transportUser,oP->transportUser,maxupwsiz);
+ memcpy(profileP->transportPassword,oP->transportPassword,o_maxupwsiz);
+ AssignCString(profileP->socksHost,oP->socksHost,maxurisiz);
+ AssignCString(profileP->proxyHost,oP->proxyHost,maxurisiz);
+ profileP->sessionID = oP->sessionID;
+ profileP->lastSyncMLVersion = oP->lastSyncMLVersion;
+ profileP->lastAuthMethod = oP->lastAuthMethod;
+ profileP->lastAuthFormat = oP->lastAuthFormat;
+ AssignCString(profileP->lastNonce,oP->lastNonce,maxnoncesiz);
+ profileP->firstuse = oP->firstuse;
+ if (aOldVersion>=5) { // can only be OldVersion==5
+ // copy additional PROFILE_DB_VERSION 5 settings
+ AssignCString(profileP->URIpath,oP->URIpath,maxpathsiz);
+ profileP->protocol = oP->protocol;
+ profileP->readOnlyFlags = oP->readOnlyFlags;
+ AssignCString(profileP->localDBProfileName,oP->localDBProfileName,localDBpathMaxLen);
+ profileP->useProxy = oP->useProxy;
+ profileP->useConnectionProxy = oP->useConnectionProxy;
+ // - Auto-Sync levels (3 levels, first match overrides lower level settings)
+ profileP->AutoSyncLevel[0]=oP->AutoSyncLevel[0];
+ profileP->AutoSyncLevel[1]=oP->AutoSyncLevel[1];
+ profileP->AutoSyncLevel[2]=oP->AutoSyncLevel[2];
+ profileP->TimedSyncMobilePeriod = oP->TimedSyncMobilePeriod;
+ profileP->TimedSyncCradledPeriod = oP->TimedSyncCradledPeriod;
+ // - IPP settings are not copied, they have changed a bit between 6 and 7
+ }
+ }
+ #endif
+ if (aOldVersion<7) {
+ // as ippsettings have changed between 6 and 7, do not copy them, but erase them
+ #if PROFILE_DB_VERSION>=5
+ profileP->ippSettings.id[0]=0;
+ profileP->ippSettings.cred[0]=0;
+ profileP->ippSettings.srv[0]=0;
+ profileP->ippSettings.method=0;
+ #endif
+ }
+ // From Version 7 onwards, only some fields were added at the end, so simple copy is ok
+ if (aOldVersion==7) {
+ memcpy(aNewRecordData,aOldRecordData,PROFILE_DB_VERSION_7_SZ);
+ }
+ if (aOldVersion==8) {
+ memcpy(aNewRecordData,aOldRecordData,PROFILE_DB_VERSION_8_SZ);
+ }
+ /* when we have PROFILE_DB_VERSION > 9
+ if (aOldVersion==9) {
+ memcpy(aNewRecordData,aOldRecordData,PROFILE_DB_VERSION_9_SZ);
+ }
+ */
+ }
+ // updated ok (or updateable ok if no data pointers provided)
+ return sizeof(TBinfileDBSyncProfile);
+} // profileUpdateFunc
+
+
+// update target record contents
+static uInt32 targetUpdateFunc(uInt32 aOldVersion, uInt32 aNewVersion, void *aOldRecordData, void *aNewRecordData, uInt32 aOldSize)
+{
+ if (aOldVersion<LOWEST_TARGETS_DB_VERSION || aOldVersion>TARGETS_DB_VERSION) return 0; // unknown old version, cannot update
+ if (aNewVersion!=TARGETS_DB_VERSION) return 0; // cannot update to other version than current
+ // create default values for profile
+ if (aOldRecordData && aNewRecordData) {
+ TBinfileDBSyncTarget *targetP = (TBinfileDBSyncTarget *)aNewRecordData;
+ // copy old data - beginning of record is identical
+ memcpy(aNewRecordData,aOldRecordData,aOldSize);
+ // now initialize fields that old version didn't have
+ if (aOldVersion<4) {
+ // init new version 4 fields
+ targetP->resumeAlertCode = 0;
+ // also wipe out version 3 "lastModCount", which now becomes "lastSuspendModCount"
+ targetP->lastSuspendModCount = 0;
+ }
+ #if TARGETS_DB_VERSION >= 5
+ if (aOldVersion<5) {
+ // init new version 5 fields
+ AssignCString(targetP->localContainerName,NULL,localDBpathMaxLen);
+ }
+ #endif
+ #if TARGETS_DB_VERSION >= 6
+ if (aOldVersion<6) {
+ // init new version 6 fields
+ AssignCString(targetP->lastSyncIdentifier,NULL,remoteAnchorMaxLen);
+ AssignCString(targetP->lastSuspendIdentifier,NULL,remoteAnchorMaxLen);
+ AssignCString(targetP->remoteDBdispName,NULL,dispNameMaxLen);
+ AssignCString(targetP->filterCapDesc,NULL,filterCapDescMaxLen);
+ AssignCString(targetP->remoteFilters,NULL,filterExprMaxLen);
+ AssignCString(targetP->localFilters,NULL,filterExprMaxLen);
+ }
+ #endif
+ }
+ // updated ok (or updateable ok if no data pointers provided)
+ // - return size of new record
+ return sizeof(TBinfileDBSyncTarget);
+} // targetUpdateFunc
+
+
+// get path where to store binfiles
+void TBinfileClientConfig::getBinFilesPath(string &aPath)
+{
+ #ifndef HARDCODED_CONFIG
+ if (!fBinFilesPath.empty())
+ aPath = fBinFilesPath;
+ else
+ #endif
+ {
+ // use appdata directory as defined by the current platform/OS
+ if (!getPlatformString(pfs_appdata_path,aPath)) {
+ // we have no path
+ aPath.erase();
+ }
+ }
+ // make OS path of it (if not empty) and create directory if not existing yet
+ if (!aPath.empty()) {
+ // make sure it exists
+ makeOSDirPath(aPath,true);
+ }
+}; // TBinfileClientConfig::getBinFilesPath
+
+
+// open settings databases
+localstatus TBinfileClientConfig::openSettingsDatabases(bool aDoLoose)
+{
+ // safe for calling more than once
+ if (fProfileBinFile.isOpen() && fTargetsBinFile.isOpen())
+ return LOCERR_OK; // already open - ok
+ // open profile and targets databases
+ // - get base path
+ string basepath;
+ getBinFilesPath(basepath);
+ string usedpath;
+ bool newprofiles=false;
+ bferr err;
+ // - profiles
+ usedpath=basepath + PROFILE_DB_NAME;
+ fProfileBinFile.setFileInfo(usedpath.c_str(),PROFILE_DB_VERSION,PROFILE_DB_ID);
+ err = fProfileBinFile.open(0,NULL,profileUpdateFunc);
+ if (err!=BFE_OK) {
+ // create new one or overwrite incompatible one if allowed
+ if (aDoLoose || err!=BFE_BADVERSION) {
+ err=fProfileBinFile.create(sizeof(TBinfileDBSyncProfile),0,NULL,true);
+ newprofiles=true;
+ }
+ else {
+ // would create new file due to bad (newer or non-upgradeable older) version
+ return LOCERR_CFGPARSE; // this is kind of a config parsing error
+ }
+ }
+ // - targets
+ usedpath=basepath + TARGETS_DB_NAME;
+ fTargetsBinFile.setFileInfo(usedpath.c_str(),TARGETS_DB_VERSION,TARGETS_DB_ID);
+ err = fTargetsBinFile.open(0,NULL,targetUpdateFunc);
+ if (err!=BFE_OK || newprofiles) {
+ // create new one or overwrite incompatible one
+ // also ALWAYS create new targets if we HAVE created new profiles
+ if (aDoLoose || newprofiles || err!=BFE_BADVERSION) {
+ err=fTargetsBinFile.create(sizeof(TBinfileDBSyncTarget),0,NULL,true);
+ }
+ else {
+ // would create new file due to bad (newer or non-upgradeable older) version
+ return LOCERR_CFGPARSE; // this is kind of a config parsing error
+ }
+ }
+ return err;
+} // TBinfileClientConfig::openSettingsDatabases
+
+
+// close settings databases
+void TBinfileClientConfig::closeSettingsDatabases(void)
+{
+ // open profile and targets databases
+ fProfileBinFile.close();
+ fTargetsBinFile.close();
+} // TBinfileClientConfig::closeSettingsDatabases
+
+
+// resolve
+void TBinfileClientConfig::localResolve(bool aLastPass)
+{
+ // resolve inherited
+ inherited::localResolve(aLastPass);
+} // TBinfileClientConfig::localResolve
+
+
+
+// MUST be called after creating config to load (or pre-load) variable parts of config
+// such as binfile profiles. If aDoLoose==false, situations, where existing config
+// is detected but cannot be re-used will return an error. With aDoLoose==true, config
+// files etc. are created even if it means a loss of data.
+localstatus TBinfileClientConfig::loadVarConfig(bool aDoLoose)
+{
+ // let inherited to it's stuff
+ localstatus err=inherited::loadVarConfig(aDoLoose);
+ // now do my own stuf
+ if (err==LOCERR_OK) {
+ err=openSettingsDatabases(aDoLoose);
+ }
+ // return status
+ return err;
+} // TBinfileClientConfig::loadVarConfig
+
+
+
+// save app state (such as settings in datastore configs etc.)
+void TBinfileClientConfig::saveAppState(void)
+{
+ // close and re-open the settings binfiles to make sure their
+ // contents is permanently saved
+ closeSettingsDatabases();
+ openSettingsDatabases(false);
+} // TBinfileClientConfig::saveAppState
+
+
+// API for settings management
+
+
+// initialize a new-created profile database
+void TBinfileClientConfig::initProfileDb(void)
+{
+ // create default profile
+ newProfile("Default",true);
+} // TBinfileClientConfig::initProfileDb
+
+
+// Defaults for profile
+#ifndef DEFAULT_ENCODING
+#define DEFAULT_ENCODING SML_WBXML
+#endif
+#ifndef DEFAULT_SERVER_URI
+#define DEFAULT_SERVER_URI NULL
+#endif
+#ifndef DEFAULT_URI_PATH
+#define DEFAULT_URI_PATH NULL
+#endif
+#ifndef DEFAULT_SERVER_USER
+#define DEFAULT_SERVER_USER NULL
+#endif
+#ifndef DEFAULT_SERVER_PASSWD
+#define DEFAULT_SERVER_PASSWD NULL
+#endif
+#ifndef DEFAULT_TRANSPORT_USER
+#define DEFAULT_TRANSPORT_USER NULL
+#endif
+#ifndef DEFAULT_TRANSPORT_PASSWD
+#define DEFAULT_TRANSPORT_PASSWD NULL
+#endif
+#ifndef DEFAULT_SOCKS_HOST
+#define DEFAULT_SOCKS_HOST NULL
+#endif
+#ifndef DEFAULT_PROXY_HOST
+#define DEFAULT_PROXY_HOST NULL
+#endif
+#ifndef DEFAULT_PROXY_USER
+#define DEFAULT_PROXY_USER NULL
+#endif
+#ifndef DEFAULT_PROXY_PASSWD
+#define DEFAULT_PROXY_PASSWD NULL
+#endif
+#ifndef DEFAULT_DATASTORES_ENABLED
+#define DEFAULT_DATASTORES_ENABLED false
+#endif
+#ifndef DEFAULT_LOCALDB_PROFILE
+#define DEFAULT_LOCALDB_PROFILE NULL
+#endif
+#ifndef DEFAULT_USESYSTEMPROXY
+#define DEFAULT_USESYSTEMPROXY true
+#endif
+
+// create new profile with targets for all configured datastores
+// - returns index of new profile
+sInt32 TBinfileClientConfig::newProfile(const char *aProfileName, bool aSetDefaults, sInt32 aTemplateProfile)
+{
+ TBinfileDBSyncProfile profile;
+ TBinfileDBSyncTarget target;
+ sInt32 profileIndex;
+ TBinfileDBSyncProfile templateprofile;
+ TBinfileDBSyncTarget templatetarget;
+
+ // create profile record
+ initProfile(profile,aProfileName,aSetDefaults);
+ #ifdef EXPIRES_AFTER_DAYS
+ // copy date of first use (to see if someone tried to tamper with...)
+ uInt32 vers;
+ getSyncAppBase()->getFirstUseInfo(SYSER_VARIANT_CODE,profile.firstuse,vers);
+ #endif
+ // copy from template profile, if any
+ if (aTemplateProfile>=0) {
+ aTemplateProfile=getProfile(aTemplateProfile,templateprofile);
+ }
+ if (aTemplateProfile>=0) {
+ // copy user config
+ profile.encoding=templateprofile.encoding;
+ #ifndef HARD_CODED_SERVER_URI
+ strncpy(profile.serverURI,templateprofile.serverURI,maxurisiz); // if hardcoded, don't copy from template
+ #endif
+ strncpy(profile.serverUser,templateprofile.serverUser,maxupwsiz);
+ strncpy(profile.transportUser,templateprofile.transportUser,maxupwsiz);
+ strncpy(profile.socksHost,templateprofile.socksHost,maxurisiz);
+ strncpy(profile.proxyHost,templateprofile.proxyHost,maxurisiz);
+ strncpy(profile.proxyUser,templateprofile.proxyUser,maxupwsiz);
+ // additional proxy flags
+ profile.useProxy=templateprofile.useProxy;
+ profile.useConnectionProxy=templateprofile.useConnectionProxy;
+ // improved URI settings
+ strncpy(profile.URIpath,templateprofile.URIpath,maxpathsiz);
+ profile.protocol=templateprofile.protocol;
+ // feature flags
+ profile.readOnlyFlags = 0; // no read-only flags by default
+ profile.featureFlags=templateprofile.featureFlags; // inherit features
+ profile.dsAvailFlags=templateprofile.dsAvailFlags; // inherit datastore availability
+ // extras
+ profile.transpFlags=templateprofile.transpFlags; // inherit transport related flags
+ profile.profileFlags=templateprofile.profileFlags; // inherit general profile flags
+ // Note: do not copy profileExtra1/2 and profileData - as these are too app specific
+ // local DB profile (not used in PPC, only for Outlook client)
+ strncpy(profile.localDBProfileName,templateprofile.localDBProfileName,localDBpathMaxLen);
+ // autosync settings
+ profile.AutoSyncLevel[0]=templateprofile.AutoSyncLevel[0];
+ profile.AutoSyncLevel[1]=templateprofile.AutoSyncLevel[1];
+ profile.AutoSyncLevel[2]=templateprofile.AutoSyncLevel[2];
+ // timed sync settings
+ profile.TimedSyncMobilePeriod=templateprofile.TimedSyncMobilePeriod;
+ profile.TimedSyncCradledPeriod=templateprofile.TimedSyncCradledPeriod;
+ /* %%% do not copy IPP settings, these should be provisioned by the SyncML server
+ // IPP settings (not available in all clients, but present in all profile records
+ profile.ippSettings=templateprofile.ippSettings;
+ */
+ }
+ #ifndef HARD_CODED_SERVER_URI
+ // override with config-defined fixed server URL, if any
+ if (!fServerURI.empty()) {
+ strncpy(profile.serverURI,fServerURI.c_str(),maxurisiz);
+ profile.readOnlyFlags = rdonly_URI; // make URI readonly
+ }
+ #endif
+ // save profile record
+ profileIndex=writeProfile(-1,profile);
+ // create a target for each configured datastore
+ // Note: create target also for not available datastores
+ TLocalDSList::iterator pos;
+ for (pos=fDatastores.begin();pos!=fDatastores.end();pos++) {
+ TBinfileDSConfig *cfgP = static_cast<TBinfileDSConfig *>(*pos);
+ cfgP->initTarget(target,profile.profileID,aSetDefaults ? NULL : "",aSetDefaults && DEFAULT_DATASTORES_ENABLED); // remote datastore names default to local ones, empty if not default
+ // copy from template
+ if (aTemplateProfile>=0) {
+ // find matching template target
+ sInt32 tti=findTargetIndexByDBInfo(
+ templateprofile.profileID, // profile to search targets for
+ cfgP->fLocalDBTypeID,
+ NULL // can be NULL if name does not matter
+ );
+ if (tti>=0) {
+ getTarget(tti,templatetarget);
+ // copy user config
+ strncpy(target.remoteDBpath,templatetarget.remoteDBpath,remoteDBpathMaxLen);
+ target.syncmode=templatetarget.syncmode;
+ target.limit1=templatetarget.limit1;
+ target.limit2=templatetarget.limit2;
+ target.extras=templatetarget.extras;
+ }
+ }
+ // save target record
+ writeTarget(-1,target);
+ }
+ return profileIndex;
+} // TBinfileClientConfig::newProfile
+
+
+// helper
+static void AssignDefault(char *aCStr, bool aWithDefaults, const char *aDefault, size_t aLen)
+{
+ if (aWithDefaults)
+ AssignCString(aCStr,aDefault,aLen);
+ else
+ AssignCString(aCStr,NULL,aLen);
+} // AssignDefault
+
+
+// init empty profile record
+void TBinfileClientConfig::initProfile(TBinfileDBSyncProfile &aProfile, const char *aName, bool aWithDefaults)
+{
+ // - wipe it to all zeroes to make sure we don't save any garbage under no circumstances
+ memset((void *)&aProfile,0,sizeof(TBinfileDBSyncProfile));
+ // - Name
+ if (aName==0) aName="<untitled>";
+ strncpy(aProfile.profileName,aName,maxnamesiz);
+ // - encoding defaults to WBXML
+ aProfile.encoding=DEFAULT_ENCODING;
+ AssignDefault(aProfile.serverURI,aWithDefaults, DEFAULT_SERVER_URI, maxurisiz);
+ // - suffix (for hardcoded base URI versions)
+ AssignDefault(aProfile.URIpath,aWithDefaults, DEFAULT_URI_PATH, maxpathsiz);
+ #ifdef PROTOCOL_SELECTOR
+ aProfile.protocol=PROTOCOL_SELECTOR;
+ #else
+ aProfile.protocol=transp_proto_uri;
+ #endif
+ aProfile.readOnlyFlags=0; // all read&write so far
+ aProfile.remoteFlags=0; // no remote specifics known so far
+ #ifdef FTRFLAGS_ALWAYS_AVAILABLE
+ aProfile.featureFlags=FTRFLAGS_ALWAYS_AVAILABLE; // set default availability
+ #else
+ aProfile.featureFlags=0; // no special feature flags
+ #endif
+ #ifdef DSFLAGS_ALWAYS_AVAILABLE
+ aProfile.dsAvailFlags=DSFLAGS_ALWAYS_AVAILABLE; // set default availability
+ #else
+ aProfile.dsAvailFlags=0; // no extra DS enabled
+ #endif
+ // - User name
+ AssignDefault(aProfile.serverUser,aWithDefaults, DEFAULT_SERVER_USER, maxupwsiz);
+ // - password, mangled
+ assignMangledToCString(aProfile.serverPassword, (const char *)(aWithDefaults ? DEFAULT_SERVER_PASSWD : NULL), maxupwsiz,true);
+ // - Transport user
+ AssignDefault(aProfile.transportUser,aWithDefaults, DEFAULT_TRANSPORT_USER, maxupwsiz);
+ // - Transport password, mangled
+ assignMangledToCString(aProfile.transportPassword, (const char *)(aWithDefaults ? DEFAULT_TRANSPORT_PASSWD : NULL), maxupwsiz,true);
+ // - Socks Host
+ AssignDefault(aProfile.socksHost,aWithDefaults, DEFAULT_SOCKS_HOST, maxurisiz);
+ // - Proxy Host
+ AssignDefault(aProfile.proxyHost,aWithDefaults, DEFAULT_PROXY_HOST, maxurisiz);
+ // - local DB profile name
+ AssignDefault(aProfile.localDBProfileName,true,DEFAULT_LOCALDB_PROFILE,localDBpathMaxLen);
+ // - additional proxy flags
+ aProfile.useProxy=aProfile.proxyHost[0]!=0; // use if a default proxy is defined
+ aProfile.useConnectionProxy=DEFAULT_USESYSTEMPROXY; // use proxy from connection if available
+ // - proxy user
+ AssignDefault(aProfile.proxyUser,aWithDefaults, DEFAULT_PROXY_USER, maxupwsiz);
+ // - proxy password, mangled
+ assignMangledToCString(aProfile.proxyPassword, (const char *)(aWithDefaults ? DEFAULT_PROXY_PASSWD : NULL), maxupwsiz,true);
+ // - extra flags
+ aProfile.transpFlags = 0;
+ aProfile.profileFlags = 0;
+ // - general purpose reserved fields
+ aProfile.profileExtra1 = 0;
+ aProfile.profileExtra2 = 0;
+ memset(&aProfile.profileData, 0, profiledatasiz);
+ // - automatic sync scheduling
+ aProfile.AutoSyncLevel[0].Mode=autosync_none; // not enabled
+ aProfile.AutoSyncLevel[0].StartDayTime=8*60; // 8:00 AM
+ aProfile.AutoSyncLevel[0].EndDayTime=17*60; // 5:00 PM (17:00)
+ aProfile.AutoSyncLevel[0].WeekdayMask=0x3E; // Mo-Fr
+ aProfile.AutoSyncLevel[0].ChargeLevel=60; // 60% battery level needed
+ aProfile.AutoSyncLevel[0].MemLevel=10; // 10% memory must be free
+ aProfile.AutoSyncLevel[0].Flags=0; // no flags so far
+ aProfile.AutoSyncLevel[1].Mode=autosync_none; // not enabled
+ aProfile.AutoSyncLevel[1].StartDayTime=0;
+ aProfile.AutoSyncLevel[1].EndDayTime=0;
+ aProfile.AutoSyncLevel[1].WeekdayMask=0;
+ aProfile.AutoSyncLevel[1].ChargeLevel=0;
+ aProfile.AutoSyncLevel[1].MemLevel=0;
+ aProfile.AutoSyncLevel[1].Flags=0;
+ aProfile.AutoSyncLevel[2].Mode=autosync_none; // not enabled
+ aProfile.AutoSyncLevel[2].StartDayTime=0;
+ aProfile.AutoSyncLevel[2].EndDayTime=0;
+ aProfile.AutoSyncLevel[2].WeekdayMask=0;
+ aProfile.AutoSyncLevel[2].ChargeLevel=0;
+ aProfile.AutoSyncLevel[2].MemLevel=0;
+ aProfile.AutoSyncLevel[2].Flags=0;
+ // Timed sync settings
+ aProfile.TimedSyncMobilePeriod=2*60; // every 2 hours
+ aProfile.TimedSyncCradledPeriod=15; // every 15 minutes
+ // IPP settings
+ aProfile.ippSettings.srv[0]=0;
+ aProfile.ippSettings.port=0;
+ aProfile.ippSettings.period=5*60; // 5 mins
+ aProfile.ippSettings.path[0]=0;
+ aProfile.ippSettings.id[0]=0;
+ aProfile.ippSettings.method=0; // none defined
+ aProfile.ippSettings.cred[0]=0;
+ aProfile.ippSettings.maxinterval=0;
+ aProfile.ippSettings.timedds[0]=0;
+ // Internals
+ // - session ID
+ aProfile.sessionID=0;
+ // - last connection's parameters (used as default for next session)
+ aProfile.lastSyncMLVersion=syncml_vers_unknown;
+ aProfile.lastAuthMethod=auth_none;
+ aProfile.lastAuthFormat=fmt_chr;
+ AssignCString(aProfile.lastNonce,NULL,maxnoncesiz);
+} // initProfile
+
+
+// checks that all profiles are complete with targets for all configured datastores
+// (in case of STD->PRO upgrade for example)
+void TBinfileClientConfig::checkProfiles(void)
+{
+ for (sInt32 pidx=0; pidx<numProfiles(); pidx++) {
+ checkProfile(pidx);
+ }
+} // TBinfileClientConfig::checkProfiles
+
+
+// checks that profile is complete with targets for all configured datastores
+// (in case of STD->PRO upgrade for example)
+void TBinfileClientConfig::checkProfile(sInt32 aProfileIndex)
+{
+ TBinfileDBSyncProfile profile;
+
+ // get profile record
+ if (getProfile(aProfileIndex,profile)>=0) {
+ // make sure a target exists for each configured datastore
+ // Note: also creates targets for currently unavailable datastores (profile.dsAvailFlags, license...)
+ TLocalDSList::iterator pos;
+ for (pos=fDatastores.begin();pos!=fDatastores.end();pos++) {
+ TBinfileDSConfig *cfgP = static_cast<TBinfileDSConfig *>(*pos);
+ // find matching template target
+ findOrCreateTargetIndexByDBInfo(
+ profile.profileID, // profile to search targets for
+ cfgP->fLocalDBTypeID,
+ NULL // can be NULL if name does not matter
+ );
+ }
+ }
+} // TBinfileClientConfig::newProfile
+
+
+
+// - write profile, returns index of profile
+sInt32 TBinfileClientConfig::writeProfile(
+ sInt32 aProfileIndex, // -1 if adding new profile
+ TBinfileDBSyncProfile &aProfile // profile ID is set to new ID in aProfile
+)
+{
+ uInt32 newindex=aProfileIndex;
+ if (aProfileIndex<0) {
+ // set new unqiue ID
+ aProfile.profileID=fProfileBinFile.getNextUniqueID();
+ // create new record
+ fProfileBinFile.newRecord(newindex,&aProfile);
+ // make sure header is up to date (in case we terminate improperly)
+ fProfileBinFile.flushHeader();
+ }
+ else {
+ // find record
+ fProfileBinFile.updateRecord(aProfileIndex,&aProfile);
+ }
+ // return index of data
+ return newindex;
+} // TBinfileClientConfig::writeProfile
+
+
+// - delete profile (and all of its targets)
+bool TBinfileClientConfig::deleteProfile(sInt32 aProfileIndex)
+{
+ // remove all targets
+ sInt32 idx;
+ uInt32 profileID = getIDOfProfile(aProfileIndex);
+ if (profileID==0) return false;
+ do {
+ // find first (still existing) target for this profile
+ idx = findTargetIndex(profileID,0);
+ if (idx<0) break; // no more targets
+ // if this is the last profile, also delete targets' changelogs
+ if (fProfileBinFile.getNumRecords()<=1) {
+ // clean related changelogs
+ cleanChangeLogForTarget(idx);
+ }
+ // delete target
+ deleteTarget(idx);
+ } while (true);
+ // remove profile itself
+ if (fProfileBinFile.deleteRecord(aProfileIndex)!=BFE_OK) return false;
+ if (fProfileBinFile.getNumRecords()==0) {
+ // last profile deleted, remove file itself to clean up as much as possible
+ fProfileBinFile.closeAndDelete();
+ fProfileBinFile.open(); // re-open = re-create
+ }
+ else {
+ // make sure header is up to date (in case we terminate improperly)
+ fProfileBinFile.flushHeader();
+ }
+ // return
+ return true;
+} // TBinfileClientConfig::deleteProfile
+
+
+// - get number of existing profiles
+sInt32 TBinfileClientConfig::numProfiles(void)
+{
+ return fProfileBinFile.getNumRecords();
+} // TBinfileClientConfig::numProfiles
+
+
+// - get profile, returns index or -1 if no more profiles
+sInt32 TBinfileClientConfig::getProfile(
+ sInt32 aProfileIndex,
+ TBinfileDBSyncProfile &aProfile
+)
+{
+ if (fProfileBinFile.readRecord(aProfileIndex,&aProfile)!=BFE_OK)
+ return -1;
+ return aProfileIndex;
+} // TBinfileClientConfig::getProfile
+
+
+// - get index of profile by ID
+sInt32 TBinfileClientConfig::getProfileIndex(uInt32 aProfileID)
+{
+ TBinfileDBSyncProfile profile;
+ return getProfileByID(aProfileID,profile);
+} // TBinfileClientConfig::getProfileIndex
+
+
+// - get profile by ID, returns index or -1 if no profile found
+// Note: aProfile might be writte even if profile is not found
+sInt32 TBinfileClientConfig::getProfileByID(
+ uInt32 aProfileID,
+ TBinfileDBSyncProfile &aProfile
+)
+{
+ sInt32 aIndex=0;
+ while (true) {
+ aIndex = getProfile(aIndex,aProfile);
+ if (aIndex<0) break; // not found
+ // check ID
+ if (aProfile.profileID == aProfileID) break;
+ // next profile
+ aIndex++;
+ }
+ return aIndex;
+} // TBinfileClientConfig::getProfile
+
+
+// - get profile index from name, returns index or -1 if no matching profile found
+sInt32 TBinfileClientConfig::getProfileIndexByName(cAppCharP aProfileName)
+{
+ sInt32 np = numProfiles();
+ sInt32 pi = np;
+ TBinfileDBSyncProfile profile;
+ while (pi>0) {
+ pi--;
+ getProfile(pi,profile);
+ if (strucmp(profile.profileName,aProfileName)==0) {
+ return pi; // found, return index
+ }
+ }
+ return -1; // not found
+} // TBinfileClientConfig::getProfileByName
+
+
+// - get ID of profile from index, 0 if none found
+uInt32 TBinfileClientConfig::getIDOfProfile(sInt32 aProfileIndex)
+{
+ // get profile ID
+ TBinfileDBSyncProfile profile;
+ if (fProfileBinFile.readRecord(aProfileIndex,&profile)!=BFE_OK)
+ return 0;
+ return profile.profileID;
+} // TBinfileClientConfig::getIDOfProfile
+
+
+
+// - get last sync (earliest of lastSync of all sync-enabled targets), 0=never
+bool TBinfileClientConfig::getProfileLastSyncTime(uInt32 aProfileID, lineartime_t &aLastSync, bool &aZapsServer, bool &aZapsClient)
+{
+ sInt32 i,targetIndex;
+ uInt32 dbid;
+ lineartime_t targlast;
+ aLastSync=maxLinearTime;
+ aZapsServer=false;
+ aZapsClient=false;
+ bool zs,zc;
+ i=0;
+ do {
+ targetIndex=findTargetIndex(aProfileID,i++);
+ if (targetIndex<0) break;
+ // get last sync of this target
+ if (getTargetLastSyncTime(targetIndex,targlast,zs,zc,dbid)) {
+ // enabled target
+ if (targlast<aLastSync) aLastSync=targlast;
+ aZapsServer = aZapsServer || zs;
+ aZapsClient = aZapsClient || zc;
+ }
+ } while(true);
+ return aLastSync!=maxLinearTime; // at least one enabled target
+} // TBinfileClientConfig::getProfileLastSyncTime
+
+
+// - get last sync (earliest of lastSync of all sync-enabled targets), 0=never
+bool TBinfileClientConfig::getTargetLastSyncTime(sInt32 aTargetIndex, lineartime_t &aLastSync, bool &aZapsServer, bool &aZapsClient, uInt32 &aDBID)
+{
+ // get target
+ TBinfileDBSyncTarget target;
+ if (fTargetsBinFile.readRecord(aTargetIndex,&target)!=BFE_OK)
+ return false; // no such target
+ return getTargetLastSyncTime(target, aLastSync, aZapsServer, aZapsClient, aDBID);
+} // TBinfileClientConfig::getTargetLastSyncTime
+
+
+// - get last sync (earliest of lastSync of all sync-enabled targets), 0=never
+bool TBinfileClientConfig::getTargetLastSyncTime(TBinfileDBSyncTarget &aTarget, lineartime_t &aLastSync, bool &aZapsServer, bool &aZapsClient, uInt32 &aDBID)
+{
+ // returns info even if not enabled
+ // Note: if suspended, zapping has already occurred (and has been confirmed, so don't report it here)
+ aZapsServer=aTarget.syncmode==smo_fromclient && aTarget.forceSlowSync && aTarget.resumeAlertCode==0;
+ aZapsClient=aTarget.syncmode==smo_fromserver && aTarget.forceSlowSync && aTarget.resumeAlertCode==0;
+ // return info anyway
+ aLastSync=aTarget.lastSync;
+ aDBID=aTarget.localDBTypeID;
+ return aTarget.enabled;
+} // TBinfileClientConfig::getTargetLastSyncTime
+
+
+
+// - check for feature enabled (profile or license dependent)
+bool TBinfileClientConfig::isFeatureEnabled(sInt32 aProfileIndex, uInt16 aFeatureNo)
+{
+ TBinfileDBSyncProfile profile;
+ if (getProfile(aProfileIndex,profile)<0)
+ return isFeatureEnabled((TBinfileDBSyncProfile *)NULL, aFeatureNo); // no profile, get general availablity
+ else
+ return isFeatureEnabled(&profile, aFeatureNo); // check if available in this profile
+} // TBinfileClientConfig::isFeatureEnabled
+
+
+// - check for feature enabled (profile or license dependent)
+bool TBinfileClientConfig::isFeatureEnabled(TBinfileDBSyncProfile *aProfileP, uInt16 aFeatureNo)
+{
+ // first check for general (local) availability
+ bool locAvail;
+ switch (aFeatureNo) {
+ #ifdef CGI_SERVER_OPTIONS
+ // we have the range options
+ case APP_FTR_EVENTRANGE:
+ case APP_FTR_EMAILRANGE:
+ locAvail=true; break;
+ #endif
+ default:
+ locAvail=false;
+ }
+ // if not locally available anyway - check for general (global) availability
+ if (!locAvail && !getSyncAppBase()->isFeatureEnabled(aFeatureNo))
+ return false; // this feature is not enabled at all
+ #ifdef FTRFLAGS_ALWAYS_AVAILABLE
+ // check for profile-level enabling
+ // - get flag for feature No
+ uInt8 ftrflag=0;
+ switch (aFeatureNo) {
+ case APP_FTR_AUTOSYNC:
+ ftrflag=ftrflg_autosync; break;
+ case APP_FTR_IPP:
+ ftrflag=ftrflg_dmu; break;
+ case APP_FTR_EVENTRANGE:
+ case APP_FTR_EMAILRANGE:
+ ftrflag=ftrflg_range; break;
+ default : return true; // not a profile-level feature, but it is globally on -> is enabled
+ }
+ // - check if generally enabled even if not in profile flags
+ if ((ftrflag & FTRFLAGS_ALWAYS_AVAILABLE)!=0)
+ return true; // always available (hardcoded)
+ // - now it depends on the profile flags
+ return (aProfileP && (aProfileP->featureFlags & ftrflag)!=0);
+ #else
+ // no profile level feature enabling - everything that is globally enabled is also enabled in this profile
+ return true;
+ #endif
+} // TBinfileClientConfig::isFeatureEnabled
+
+
+// - check for feature enabled (profile or license dependent)
+bool TBinfileClientConfig::isReadOnly(sInt32 aProfileIndex, uInt8 aReadOnlyMask)
+{
+ TBinfileDBSyncProfile profile;
+ if (getProfile(aProfileIndex,profile)<0)
+ return true; // make all read-only if we have no profile
+ else
+ return isReadOnly(&profile, aReadOnlyMask); // check if available in this profile
+} // TBinfileClientConfig::isFeatureEnabled
+
+
+// - check for readonly (profile or license dependent)
+bool TBinfileClientConfig::isReadOnly(TBinfileDBSyncProfile *aProfileP, uInt8 aReadOnlyMask)
+{
+ // check special hardcoded cases
+ #ifdef HARD_CODED_DBNAMES
+ if (aReadOnlyMask & rdonly_dbpath) return true; // with hardcoded DB names, these are ALWAYS readonly
+ #endif
+ #ifdef HARD_CODED_SERVER_URI
+ if (aReadOnlyMask & rdonly_URI) return true; // with hardcoded URI, this is ALWAYS readonly
+ #endif
+
+ // just check profile flags
+ if (!aProfileP) return true; // make all read-only if we have no profile
+ return aProfileP->readOnlyFlags & aReadOnlyMask; // readonly if flag is set in profile
+} // TBinfileClientConfig::isReadOnly
+
+
+// - check if datastore of specified target is available
+// in the given profile
+bool TBinfileClientConfig::isTargetAvailable(
+ TBinfileDBSyncProfile *aProfileP,
+ uInt32 aLocalDBTypeID
+) {
+ TLocalDSList::iterator pos;
+ for (pos=fDatastores.begin();pos!=fDatastores.end();pos++) {
+ TBinfileDSConfig *cfgP = static_cast<TBinfileDSConfig *>(*pos);
+ if (
+ cfgP->fLocalDBTypeID==aLocalDBTypeID
+ ) {
+ // right datastore config found
+ // - return it's availability status
+ return cfgP->isAvailable(aProfileP);
+ }
+ }
+ // datastore not found -> not available
+ return false;
+} // TBinfileClientConfig::isTargetAvailable
+
+
+// - find available target for profile by DB ID/name. Returns target index or -1 if
+// DBTypeID not available in this profile/license or not implemented at all
+sInt32 TBinfileClientConfig::findAvailableTargetIndexByDBInfo(
+ TBinfileDBSyncProfile *aProfileP, // profile to search targets for
+ uInt32 aLocalDBTypeID,
+ const char *aLocalDBName // can be NULL if name does not matter
+)
+{
+ if (!isTargetAvailable(aProfileP,aLocalDBTypeID)) return -1;
+ if (!aProfileP) return -1; // no profile, no target
+ return findOrCreateTargetIndexByDBInfo(aProfileP->profileID,aLocalDBTypeID,aLocalDBName);
+} // TBinfileClientConfig::findAvailableTargetIndexByDBInfo
+
+
+// - find target for profile by DB ID/name. Returns target index or -1 if none found
+sInt32 TBinfileClientConfig::findOrCreateTargetIndexByDBInfo(
+ uInt32 aProfileID, // profile to search targets for
+ uInt32 aLocalDBTypeID,
+ const char *aLocalDBName // can be NULL if name does not matter
+)
+{
+ sInt32 targidx;
+ TBinfileDBSyncTarget target;
+
+ targidx=findTargetIndexByDBInfo(aProfileID,aLocalDBTypeID,aLocalDBName);
+ if (targidx<0) {
+ // does not exist yet, create it now
+ TLocalDSList::iterator pos;
+ for (pos=fDatastores.begin();pos!=fDatastores.end();pos++) {
+ TBinfileDSConfig *cfgP = static_cast<TBinfileDSConfig *>(*pos);
+ if (
+ cfgP->fLocalDBTypeID==aLocalDBTypeID &&
+ (aLocalDBName==NULL || cfgP->fLocalDBPath==aLocalDBName)
+ ) {
+ // right datastore found
+ cfgP->initTarget(target,aProfileID,NULL,DEFAULT_DATASTORES_ENABLED); // init default
+ // write new target and get a index back
+ targidx=writeTarget(-1,target);
+ // done
+ break;
+ }
+ }
+ }
+ return targidx;
+} // findOrCreateTargetIndexByDBInfo
+
+
+// - find target for profile by DB ID/name. Returns target index or -1 if none found
+sInt32 TBinfileClientConfig::findTargetIndexByDBInfo(
+ uInt32 aProfileID, // profile to search targets for
+ uInt32 aLocalDBTypeID,
+ const char *aLocalDBName // can be NULL if name does not matter
+)
+{
+ // now search targets
+ TBinfileDBSyncTarget target;
+ sInt32 maxidx = fTargetsBinFile.getNumRecords();
+ sInt32 targidx;
+ for (targidx=0; targidx<maxidx; targidx++) {
+ fTargetsBinFile.readRecord(targidx,&target);
+ if (
+ target.remotepartyID == aProfileID &&
+ target.localDBTypeID==aLocalDBTypeID &&
+ (aLocalDBName==NULL || strucmp(target.localDBPath,aLocalDBName)==0)
+ ) {
+ // target found, return its index
+ return targidx;
+ }
+ }
+ // none found
+ return -1;
+} // TBinfileClientConfig::findTargetIndexByDBInfo
+
+
+// - find n-th target for profile. Returns target index or -1 if none found
+sInt32 TBinfileClientConfig::findTargetIndex(
+ uInt32 aProfileID, // profile to search targets for
+ sInt32 aTargetSeqNum // sequence number (0..n)
+)
+{
+ // now search targets
+ TBinfileDBSyncTarget target;
+ sInt32 maxidx = fTargetsBinFile.getNumRecords();
+ sInt32 targidx;
+ for (targidx=0; targidx<maxidx; targidx++) {
+ fTargetsBinFile.readRecord(targidx,&target);
+ if (target.remotepartyID == aProfileID) {
+ // belongs to that profile
+ if (aTargetSeqNum<=0) {
+ // n-th target found, return its index
+ return targidx;
+ }
+ aTargetSeqNum--; // next in sequence
+ }
+ }
+ // none found
+ return -1;
+} // TBinfileClientConfig::findTargetIndex
+
+
+// - write target for profile, returns index of target
+sInt32 TBinfileClientConfig::writeTarget(
+ sInt32 aTargetIndex, // -1 if adding new target
+ const TBinfileDBSyncTarget &aTarget
+)
+{
+ uInt32 newindex;
+
+ if (aTargetIndex<0) {
+ // create new record
+ fTargetsBinFile.newRecord(newindex,&aTarget);
+ // make sure header is up to date (in case we terminate improperly)
+ fTargetsBinFile.flushHeader();
+ return newindex;
+ }
+ else {
+ // update record
+ fTargetsBinFile.updateRecord(aTargetIndex,&aTarget);
+ return aTargetIndex;
+ }
+} // TBinfileClientConfig::writeTarget
+
+
+// - delete target
+bool TBinfileClientConfig::deleteTarget(
+ sInt32 aTargetIndex
+)
+{
+ if (fTargetsBinFile.deleteRecord(aTargetIndex)!=BFE_OK) return false;
+ if (fTargetsBinFile.getNumRecords()==0) {
+ // last target deleted, remove file itself to clean up as much as possible
+ fTargetsBinFile.closeAndDelete();
+ fTargetsBinFile.open(); // re-open = re-create
+ }
+ else {
+ // make sure header is up to date (in case we terminate improperly)
+ fTargetsBinFile.flushHeader();
+ }
+ return true; // ok
+} // TBinfileClientConfig::deleteTarget
+
+
+// - get target info
+sInt32 TBinfileClientConfig::getTarget(
+ sInt32 aTargetIndex,
+ TBinfileDBSyncTarget &aTarget
+)
+{
+ if (aTargetIndex<0) return -1; // no valid target index, do not even try to read
+ if (fTargetsBinFile.readRecord(aTargetIndex,&aTarget)!=BFE_OK)
+ return -1;
+ return aTargetIndex;
+} // TBinfileClientConfig::getTarget
+
+
+// completely clear changelog and pending maps for specified target
+void TBinfileClientConfig::cleanChangeLogForTarget(sInt32 aTargetIndex)
+{
+ TBinfileDBSyncTarget target;
+ getTarget(aTargetIndex,target);
+ cleanChangeLogForDBname(target.dbname);
+} // TBinfileClientConfig::cleanChangeLogForTarget
+
+
+// completely clear changelog and pending maps for specified database name
+void TBinfileClientConfig::cleanChangeLogForDBname(cAppCharP aDBName)
+{
+ // open changelog. Name is datastore name with _clg.bfi suffix
+ string filename;
+ TBinFile binfile;
+ // delete changelog
+ getBinFilesPath(filename);
+ filename += aDBName;
+ filename += CHANGELOG_DB_SUFFIX;
+ binfile.setFileInfo(filename.c_str(),CHANGELOG_DB_VERSION,CHANGELOG_DB_ID);
+ binfile.closeAndDelete();
+ // delete pending maps
+ getBinFilesPath(filename);
+ filename += aDBName;
+ filename += PENDINGMAP_DB_SUFFIX;
+ binfile.setFileInfo(filename.c_str(),PENDINGMAP_DB_VERSION,PENDINGMAP_DB_ID);
+ binfile.closeAndDelete();
+ // delete pending item
+ getBinFilesPath(filename);
+ filename += aDBName;
+ filename += PENDINGITEM_DB_SUFFIX;
+ binfile.setFileInfo(filename.c_str(),PENDINGITEM_DB_VERSION,PENDINGITEM_DB_ID);
+ binfile.closeAndDelete();
+} // TBinfileClientConfig::cleanChangeLogForDBname
+
+
+// remote provisioning (using generic code in include file)
+#include "clientprovisioning_inc.cpp"
+// Autosync mechanisms (using generic code in include file)
+#include "clientautosync_inc.cpp"
+
+/*
+ * Implementation of TBinfileImplClient
+ */
+
+/* public TBinfileImplClient members */
+
+
+TBinfileImplClient::TBinfileImplClient(TSyncClientBase *aSyncClientBaseP, const char *aSessionID) :
+ TStdLogicAgent(aSyncClientBaseP, aSessionID),
+ fConfigP(NULL)
+{
+ // get config for agent
+ TRootConfig *rootcfgP = aSyncClientBaseP->getRootConfig();
+ // - save direct link to agent config for easy reference
+ fConfigP = static_cast<TBinfileClientConfig *>(rootcfgP->fAgentConfigP);
+ // - make profile invalid
+ fProfileIndex=-1;
+ fProfileDirty=false;
+ // Note: Datastores are already created from config
+} // TBinfileImplClient::TBinfileImplClient
+
+
+TBinfileImplClient::~TBinfileImplClient()
+{
+ // make sure everything is terminated BEFORE destruction of hierarchy begins
+ TerminateSession();
+} // TBinfileImplClient::~TBinfileImplClient
+
+
+// Terminate session
+void TBinfileImplClient::TerminateSession()
+{
+ if (!fTerminated) {
+ // save profile changes
+ if (fProfileIndex>=0 && fProfileDirty) {
+ fConfigP->fProfileBinFile.updateRecord(fProfileIndex,&fProfile);
+ fProfileDirty=false;
+ fProfileIndex=-1;
+ }
+ // now reset session
+ InternalResetSession();
+ // make sure all data is flushed
+ fConfigP->fProfileBinFile.flushHeader();
+ fConfigP->fTargetsBinFile.flushHeader();
+ }
+ inherited::TerminateSession();
+} // TBinfileImplClient::TerminateSession
+
+
+
+#ifdef ENGINEINTERFACE_SUPPORT
+
+// set profileID to client session before doing first SessionStep
+void TBinfileImplClient::SetProfileSelector(uInt32 aProfileSelector)
+{
+ if (aProfileSelector==0)
+ fProfileSelectorInternal = DEFAULT_PROFILE_ID;
+ else
+ fProfileSelectorInternal = fConfigP->getProfileIndex(aProfileSelector);
+} // TBinfileImplClient::SetProfileSelector
+
+
+/// @brief Get new session key to access details of this session
+appPointer TBinfileImplClient::newSessionKey(TEngineInterface *aEngineInterfaceP)
+{
+ return new TBinFileClientParamsKey(aEngineInterfaceP,this);
+} // TBinfileImplClient::newSessionKey
+
+#endif // ENGINEINTERFACE_SUPPORT
+
+
+// Reset session
+void TBinfileImplClient::InternalResetSession(void)
+{
+ // reset all datastores now to make sure all DB activity is done
+ // before we eventually close session-global databases
+ // (Note: TerminateDatastores() will be called again by TSyncSession)
+ TerminateDatastores();
+ fRemoteFlags=0; // no remote-specifics enabled so far
+} // TBinfileImplClient::InternalResetSession
+
+
+// Virtual version
+void TBinfileImplClient::ResetSession(void)
+{
+ // do my own stuff
+ InternalResetSession();
+ // let ancestor do its stuff
+ TStdLogicAgent::ResetSession();
+} // TBinfileImplClient::ResetSession
+
+
+// - load remote connect params (syncml version, type, format and last nonce)
+// Note: agents that can cache this information between sessions will load
+// last info here.
+void TBinfileImplClient::loadRemoteParams(void)
+{
+ if (fProfileIndex<0) {
+ // no profile loaded, let ancestor handle case
+ TStdLogicAgent::loadRemoteParams();
+ }
+ else {
+ // copy profile cached values
+ fSyncMLVersion=
+ fProfile.lastSyncMLVersion >= numSyncMLVersions ?
+ syncml_vers_unknown : // if profile has higher version than we support, reset to unknown
+ fProfile.lastSyncMLVersion;
+ fRemoteRequestedAuth=
+ fProfile.lastAuthMethod >= numAuthTypes ?
+ auth_md5 : // if auth is unknown, reset to MD5
+ fProfile.lastAuthMethod;
+ fRemoteRequestedAuthEnc=
+ fProfile.lastAuthFormat >= numFmtTypes ?
+ fmt_b64 : // if format is unknown, reset to B64
+ fProfile.lastAuthFormat;
+ fRemoteNonce=fProfile.lastNonce;
+ fRemoteFlags=fProfile.remoteFlags; // init remote specific behaviour flags from profile
+ }
+ // modify some behaviour based on remote flags
+ if (fRemoteFlags & remotespecs_noDS12Filters)
+ fServerHasSINCEBEFORE = false; // prevent auto-generated SINCE/BEFORE
+} // TBinfileImplClient::loadRemoteParams
+
+
+// - save remote connect params for use in next session (if descendant implements it)
+void TBinfileImplClient::saveRemoteParams(void)
+{
+ if (fProfileIndex>=0) {
+ // save values to profile
+ // - SyncML version (save it only if it is "better" than what we knew so far)
+ if (fSyncMLVersion > fProfile.lastSyncMLVersion) {
+ fProfile.lastSyncMLVersion=fSyncMLVersion;
+ }
+ fProfile.lastAuthMethod=fRemoteRequestedAuth;
+ fProfile.lastAuthFormat=fRemoteRequestedAuthEnc;
+ fProfile.remoteFlags=fRemoteFlags; // save remote-specific behaviour flags
+ fProfileDirty=true;
+ AssignCString(fProfile.lastNonce,fRemoteNonce.c_str(),maxnoncesiz);
+ }
+} // TBinfileImplClient::saveRemoteParams
+
+
+// check remote devinf to detect special behaviour needed for some servers.
+localstatus TBinfileImplClient::checkRemoteSpecifics(SmlDevInfDevInfPtr_t aDevInfP)
+{
+ if (aDevInfP) {
+ // check for some specific servers we KNOW they need special treatment
+ uInt8 setFlags = 0;
+ uInt8 clearFlags = 0;
+ if (strnncmp(smlPCDataToCharP(aDevInfP->devid),"OracleSyncServer")==0) {
+ // Oracle OCS
+ // - unqiue ID even if device might have non-unique ones
+ // - no X-nnn type params
+ // - no DS 1.2 <filter>
+ PDEBUGPRINTFX(DBG_HOT+DBG_REMOTEINFO,("Detected Oracle OCS Server - suppress dynamic X-nnnn TYPE params, and <filter>"));
+ setFlags |= remotespecs_noXTypeParams+remotespecs_noDS12Filters;
+ #ifndef GUARANTEED_UNIQUE_DEVICID
+ // - Oracle OCS needs unique deviceID/locURI under ALL circumstances as they do
+ // session tracking by locURI, so we always use a hash of deviceID + username
+ // as visible deviceID
+ PDEBUGPRINTFX(DBG_HOT+DBG_REMOTEINFO,("OCS with device that has not guaranteed unique ID - use user+devid hash for Source LocURI"));
+ setFlags |= remotespecs_devidWithUserHash;
+ #else
+ clearFlags |= remotespecs_devidWithUserHash;
+ #endif
+ }
+ else if (strnncmp(smlPCDataToCharP(aDevInfP->mod), "Beehive", 7)==0) {
+ // Oracle Beehive, <mod> starts with "Beehive", i.e. "Beehive R1"
+ // - no DS 1.2 <filter>
+ PDEBUGPRINTFX(DBG_HOT+DBG_REMOTEINFO,("Detected Oracle Beehive - suppress <filter>"));
+ setFlags |= remotespecs_noDS12Filters;
+ clearFlags |= remotespecs_noXTypeParams+remotespecs_devidWithUserHash;
+ }
+ else {
+ // make sure oracle-specific flags are cleared for all other server types
+ clearFlags |= remotespecs_noXTypeParams+remotespecs_noDS12Filters+remotespecs_devidWithUserHash;
+ }
+ // now apply
+ sInt8 newFlags = (fRemoteFlags & ~clearFlags) | setFlags;
+ if (newFlags != fRemoteFlags) {
+ // change of remotespecs_devidWithUserHash needs session restart
+ bool needrestart = (newFlags & remotespecs_devidWithUserHash) != (fRemoteFlags & remotespecs_devidWithUserHash);
+ // remote flags need to be changed
+ fRemoteFlags = newFlags;
+ // save them into profile
+ saveRemoteParams();
+ // restart session when needed
+ if (needrestart) {
+ // - abort session, but have main loop retry starting it
+ PDEBUGPRINTFX(DBG_ERROR,("Warning: must restart session with changed remotespecs_devidWithUserHash"));
+ AbortSession(LOCERR_RESTART,true);
+ }
+ }
+ }
+ // let session handle other details
+ return inherited::checkRemoteSpecifics(aDevInfP);
+} // TBinfileImplClient::checkRemoteSpecifics
+
+
+#ifdef IPP_SUPPORT
+
+// generates custom PUT in case IPP/DMU is enabled to request settings
+void TBinfileImplClient::issueCustomGetPut(bool aGotDevInf, bool aSentDevInf)
+{
+ // get autosync PUT req string
+ string req;
+ fConfigP->autosync_get_putrequest(req);
+ // now create a PUT
+ if (!req.empty()) {
+ TPutCommand *putcommandP = new TPutCommand(this);
+ putcommandP->setMeta(newMetaType(IPP_PARAMS_ITEM_METATYPE));
+ SmlItemPtr_t putItemP = putcommandP->addSourceLocItem(IPP_PARAMS_LOCURI_REQ);
+ // - add data to item
+ putItemP->data = newPCDataString(req.c_str());
+ putcommandP->allowFailure(); // do not abort session in case server does not understand the command
+ ISSUE_COMMAND_ROOT(this,putcommandP);
+ }
+ // let ancestors issue their custom gets and puts, if any
+ inherited::issueCustomGetPut(aGotDevInf, aSentDevInf);
+} // TBinfileImplClient::issueCustomGetPut
+
+#endif // IPP_SUPPORT
+
+
+// handler of custom IPP/DMU put and result commands
+void TBinfileImplClient::processPutResultItem(bool aIsPut, const char *aLocUri, TSmlCommand *aPutResultsCommandP, SmlItemPtr_t aPutResultsItemP, TStatusCommand &aStatusCommand)
+{
+ #ifdef IPP_SUPPORT
+ // check for DMU specials
+ if (strucmp(relativeURI(aLocUri),relativeURI(IPP_PARAMS_LOCURI_CFG))==0) {
+ // get DMU string
+ const char *ippstring = smlItemDataToCharP(aPutResultsItemP);
+ PDEBUGPRINTFX(DBG_HOT,("received IPP config string: %s",ippstring));
+ // process it
+ string tag,value;
+ while (ippstring && *ippstring) {
+ ippstring=nextTag(ippstring,tag,value);
+ // set ipp params in current profile
+ fConfigP->ipp_setparam(tag.c_str(),value.c_str(),fProfile.ippSettings);
+ // make sure autosync profile copy gets updated as well
+ if (fConfigP->fAutosyncProfileLastidx==fProfileIndex) {
+ fConfigP->fAutosyncProfile = fProfile; // copy profile into autosync
+ fConfigP->autosync_condchanged(); // do immediately check settings at next autosync step
+ }
+ // make sure profile gets saved
+ fProfileDirty=true;
+ }
+ // is ok
+ aStatusCommand.setStatusCode(200); // is ok
+ aStatusCommand.dontSend(); // ..but do not send it (%%% as session would then end with error 9999)
+ }
+ else
+ #endif
+ #ifdef SETTINGS_PROVISIONING_VIA_PUT
+ // check for provisioning strings arriving via PUT command
+ if (strucmp(relativeURI(aLocUri),relativeURI(SETTINGS_LOCURI_CFG))==0) {
+ // get provisioning string
+ const char *provstring = smlItemDataToCharP(aPutResultsItemP);
+ PDEBUGPRINTFX(DBG_HOT,("received settings provisioning string: %s",provstring));
+ // process it
+ // - but first save current profile
+ fConfigP->writeProfile(fProfileIndex,fProfile);
+ // - now modify profile (may include the current profile)
+ sInt32 activeprofile;
+ bool provok = fConfigP->executeProvisioningString(provstring, activeprofile);
+ // - reload current profile (eventually modified)
+ fConfigP->getProfile(fProfileIndex,fProfile);
+ // is ok
+ aStatusCommand.setStatusCode(provok ? 200 : 400); // ok or bad request
+ aStatusCommand.dontSend(); // ..but do not send it (%%% as session would then end with error 9999)
+ }
+ else
+ #endif
+ {
+ // let ancestors process it
+ inherited::processPutResultItem(aIsPut,aLocUri,aPutResultsCommandP,aPutResultsItemP,aStatusCommand);
+ }
+} // TBinfileImplClient::processPutResultItem
+
+
+
+
+
+#ifdef PROTOCOL_SELECTOR
+// Names of protocols according to TTransportProtocols
+const char * const Protocol_Names[num_transp_protos] = {
+ "", // none, protocol contained in URI
+ "http://",
+ "https://",
+ "wsp://", // WSP protocol
+ "OBEX:IRDA", // OBEX/IRDA protocol
+ "OBEX:BT", // OBEX/BT protocol
+ "OBEX://", // OBEX/TCP protocol
+};
+#endif
+
+
+// - selects a profile (returns LOCERR_NOCFG if profile not found)
+// Note: This call must create and initialize all datastores that
+// are to be synced with that profile.
+localstatus TBinfileImplClient::SelectProfile(uInt32 aProfileSelector, bool aAutoSyncSession)
+{
+ uInt32 recidx,maxidx;
+
+ // Note: profile database has already been opened in config resolve()
+ if (aProfileSelector==DEFAULT_PROFILE_ID) {
+ // default is first one
+ aProfileSelector=0;
+ }
+ // try to load profile
+ if (fConfigP->fProfileBinFile.readRecord(aProfileSelector,&fProfile)!=BFE_OK)
+ goto defaultprofile; // use default
+ // Now we have the profile
+ fProfileIndex=aProfileSelector;
+ fRemotepartyID=fProfile.profileID;
+ // Set session parameters
+ #ifdef SYNCML_ENCODING_OVERRIDE
+ fEncoding = SYNCML_ENCODING_OVERRIDE; // we use a globally defined encoding
+ #else
+ fEncoding = fProfile.encoding; // we use the profile's encoding
+ #endif
+
+ #ifdef HARD_CODED_SERVER_URI
+ // - Hard coded base URL of server
+ fRemoteURI=DEFAULT_SERVER_URI; // always use fixed URI
+ #else
+ // - configured URL
+ // check config-level fixed URI first
+ if (!fConfigP->fServerURI.empty()) {
+ // override server URL from fixed value in config
+ fRemoteURI = fConfigP->fServerURI; // config
+ if (strucmp(fProfile.serverURI,fRemoteURI.c_str())!=0) {
+ PDEBUGPRINTFX(DBG_ERROR,("Warning - <serverurl> config overrides/overwrites server URL in profile"));
+ // re-adjust profile
+ AssignCString(fProfile.serverURI, fConfigP->fServerURI.c_str(), maxurisiz); // copy predefined URI back into profile
+ fProfile.readOnlyFlags |= rdonly_URI; // make it read-only
+ }
+ }
+ else {
+ // use URL from profile
+ fRemoteURI=fProfile.serverURI;
+ }
+ #endif
+ #ifdef CUSTOM_URI_SUFFIX
+ // - append custom URI suffix stored in serverURI field of profile (if one is set)
+ if (*(fProfile.URIpath))
+ {
+ // - append delimiter first if one defined (CUSTOM_URI_SUFFIX not NULL)
+ const char *p=CUSTOM_URI_SUFFIX;
+ if (p) fRemoteURI.append(p);
+ // - now append custom URI suffix
+ fRemoteURI.append(fProfile.URIpath);
+ }
+ #endif
+ #ifdef PROTOCOL_SELECTOR
+ fRemoteURI.insert(0,Protocol_Names[fProfile.protocol]);
+ fNoCRCPrefixLen=strlen(Protocol_Names[fProfile.protocol]);
+ #endif
+ fServerUser=fProfile.serverUser;
+ getUnmangled(fServerPassword,fProfile.serverPassword,maxupwsiz);
+ // - HTTP auth
+ fTransportUser=fProfile.transportUser;
+ getUnmangled(fTransportPassword,fProfile.transportPassword,maxupwsiz);
+ // - proxy
+ fSocksHost.erase(); // default to none
+ fProxyHost.erase();
+ fProxyUser.erase();
+ fProxyPassword.erase();
+ #ifdef PROXY_SUPPORT
+ if (fProfile.useProxy)
+ {
+ fSocksHost=fProfile.socksHost;
+ fProxyHost=fProfile.proxyHost;
+ fProxyUser=fProfile.proxyUser;
+ getUnmangled(fProxyPassword,fProfile.proxyPassword,maxupwsiz);;
+ PDEBUGPRINTFX(DBG_TRANSP,("Sync Profile contains active proxy settings: http=%s, socks=%s, proxyuser=%s",fProxyHost.c_str(), fSocksHost.c_str(), fProxyUser.c_str()));
+ }
+ #endif
+ // check for forced legacy mode
+ fLegacyMode = fProfile.profileFlags & PROFILEFLAG_LEGACYMODE;
+ // - get and increment session ID and save for next session
+ // Note: as auth retries will increment the ID as well, we inc by 5
+ // to avoid repeating the ID too soon
+ fProfile.sessionID+=5;
+ fProfileDirty=true;
+ fClientSessionNo=fProfile.sessionID;
+ // Note: loadRemoteParams will fetch the cached params from fProfile
+ // Reset session after profile change (especially fRemoteURI)
+ // and also remove any datastores we might have
+ ResetAndRemoveDatastores();
+ // Now iterate trough associated target records and create datastores
+ maxidx=fConfigP->fTargetsBinFile.getNumRecords();
+ TBinfileDBSyncTarget target;
+ for (recidx=0; recidx<maxidx; recidx++) {
+ // - get record
+ if (fConfigP->fTargetsBinFile.readRecord(recidx,&target)==BFE_OK) {
+ // check if this one of my targets
+ if (target.remotepartyID == fRemotepartyID)
+ {
+ // get datastore config
+ TBinfileDSConfig *binfiledscfgP = static_cast<TBinfileDSConfig *>(
+ getSessionConfig()->getLocalDS(target.dbname)
+ );
+ // check if we have config and if this DS is available now (profile.dsAvailFlags, DSFLAGS_ALWAYS_AVAILABLE, evtl. license...)
+ if (binfiledscfgP && binfiledscfgP->isAvailable(&fProfile)) {
+ // check if this DB must be synced
+ bool syncit=false;
+ #ifdef AUTOSYNC_SUPPORT
+ if (aAutoSyncSession) {
+ // target enable status is not relevant, but autosync alert is
+ syncit =
+ binfiledscfgP->fAutosyncForced ||
+ (binfiledscfgP->fAutosyncAlerted && target.enabled);
+ }
+ else {
+ // normal session
+ syncit = target.enabled && target.remoteDBpath[0]!=0; // and remote DB path specified
+ binfiledscfgP->fAutosyncAlerted=false; // this is NOT an autosync session
+ binfiledscfgP->fAutosyncForced=false;
+ }
+ #else
+ syncit = target.enabled && target.remoteDBpath[0]!=0; // and remote DB path specified
+ #endif
+ if (syncit)
+ {
+ TBinfileImplDS *binfiledsP=NULL;
+ if (binfiledscfgP) {
+ // create datastore
+ binfiledsP = static_cast<TBinfileImplDS *>(binfiledscfgP->newLocalDataStore(this));
+ }
+ if (binfiledsP) {
+ // copy target info to datastore for later access during sync
+ binfiledsP->fTargetIndex=recidx;
+ binfiledsP->fTarget=target;
+ // determine sync mode / flags to use
+ TSyncModes myMode;
+ bool mySlow;
+ #ifdef AUTOSYNC_SUPPORT
+ if (aAutoSyncSession && binfiledscfgP->fAutosyncAlertCode!=0) {
+ // syncmode provided from auto sync alert (e.g. SAN)
+ bool myIsSA;
+ TLocalEngineDS::getSyncModeFromAlertCode(
+ binfiledscfgP->fAutosyncAlertCode,
+ myMode,
+ mySlow,
+ myIsSA
+ );
+ }
+ else
+ #endif
+ {
+ // take it from config
+ myMode = target.syncmode;
+ mySlow = target.forceSlowSync;
+ }
+ // clean change logs if...
+ // ...this is the only profile
+ // ...this is NOT a resumable session (resumable not necessarily means that it WILL be resumed)
+ // ...we're about to slow sync
+ if (mySlow && target.resumeAlertCode==0 && fConfigP->fProfileBinFile.getNumRecords()==1) {
+ fConfigP->cleanChangeLogForDBname(target.dbname);
+ }
+ // set non-BinFile specific parameters (note that this call might
+ // be to a derivate which uses additional info from fTarget to set sync params)
+ binfiledsP->dsSetClientSyncParams(
+ myMode,
+ mySlow,
+ target.remoteDBpath,
+ NULL, // DB user
+ NULL, // DB password
+ NULL, // local path extension
+ // %%% add filters here later!!!
+ NULL, // filter query
+ false // filter inclusive
+ );
+ // prepare local datastore (basic init can be done here) and check availability
+ if (binfiledsP->localDatastorePrep()) {
+ // add to datastores for this sync
+ fLocalDataStores.push_back(binfiledsP);
+ }
+ else {
+ // silently discard (do not sync it)
+ PDEBUGPRINTFX(DBG_ERROR,("Local Database for datastore '%s' prepares not ok -> not synced",binfiledsP->getName()));
+ // show event (alerted for no database)
+ OBJ_PROGRESS_EVENT(
+ getSyncAppBase(),
+ pev_error,
+ binfiledscfgP,
+ LOCERR_LOCDBNOTRDY,0,0
+ );
+ delete binfiledsP;
+ }
+ }
+ } // if target DB enabled for sync
+ } // if we have a datastore config for this target
+ } // if target belongs to this profile
+ } // if we can read the target record
+ } // for all target records
+ // ok if at least one datastore enabled
+ return fLocalDataStores.size()>0 && fRemoteURI.size()>0 ? LOCERR_OK : LOCERR_NOCFG;
+defaultprofile:
+ return inherited::SelectProfile(aProfileSelector, aAutoSyncSession);
+} // TBinfileImplClient::SelectProfile
+
+/* end of TBinfileImplClient implementation */
+
+} // namespace sysync
+
+
+
+
+// eof
diff --git a/src/sysync/binfileimplclient.h b/src/sysync/binfileimplclient.h
new file mode 100755
index 0000000..59a7af8
--- /dev/null
+++ b/src/sysync/binfileimplclient.h
@@ -0,0 +1,587 @@
+/**
+ * @File binfileimplclient.h
+ *
+ * @Author Lukas Zeller (luz@synthesis.ch)
+ *
+ * @brief TBinfileImplClient
+ * Represents a client session (agent) that saves profile, target, resume info
+ * and optionally changelog in TBinFile binary files
+ *
+ * Copyright (c) 2003-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ * @Date 2005-09-30 : luz : created from TBinfileImplClient
+ */
+/*
+ */
+
+
+#ifndef BINFILEIMPLCLIENT_H
+#define BINFILEIMPLCLIENT_H
+
+// includes
+#include "sysync.h"
+#include "stdlogicagent.h"
+#include "binfileimplds.h"
+#include "engineinterface.h"
+
+#ifndef SYSYNC_CLIENT
+ #error "binfileimplclient is CLIENT-ONLY!"
+#endif
+
+namespace sysync {
+
+
+// Support for EngineModule common interface
+// =========================================
+
+#ifdef ENGINEINTERFACE_SUPPORT
+
+// forward
+class TBinfileClientRootKey;
+class TBinfileClientConfig;
+
+// Engine module class
+class TBinfileEngineInterface :
+ public TClientEngineInterface
+{
+ typedef TClientEngineInterface inherited;
+public:
+ //%%%
+protected:
+ /// @brief Must be derived in engineBase derivates for generating root for the appropriate settings tree
+ /// @return root settings object, or NULL if failure
+ virtual TSettingsKeyImpl *newSettingsRootKey(void);
+
+ #ifndef ENGINE_LIBRARY
+ #ifdef RELEASE_VERSION
+ #error "this is here for Q&D testing with outlook client only"
+ #endif
+ /// @brief returns a new application base.
+ /// @note in engineInterface based targets, this is the replacement for the formerly
+ /// global newSyncAppBase() factory function.
+ virtual TSyncAppBase *newSyncAppBase(void);
+ #endif
+
+}; // TBinfileEngineInterface
+
+
+
+// Binfile based target key
+class TBinfileTargetKey :
+ public TStructFieldsKey
+{
+ typedef TStructFieldsKey inherited;
+
+public:
+ TBinfileTargetKey(
+ TEngineInterface *aEngineInterfaceP,
+ sInt32 aTargetIndex,
+ TBinfileDBSyncTarget *aTargetP,
+ TBinfileClientConfig *aBinfileClientConfigP,
+ TBinfileDSConfig *aBinfileDSConfigP
+ );
+ virtual ~TBinfileTargetKey();
+
+ TBinfileDSConfig *getBinfileDSConfig(void) { return fBinfileDSConfigP; };
+ TBinfileDBSyncTarget *getTarget(void) { return fTargetP; };
+ TBinfileClientConfig *getBinfileClientConfig(void) { return fBinfileClientConfigP; };
+ sInt32 getProfileID() { return fTargetP ? fTargetP->remotepartyID : KEYVAL_ID_UNKNOWN; };
+
+protected:
+ // return ID of this key
+ virtual TSyError GetKeyID(sInt32 &aID);
+
+ // get table describing the fields in the struct
+ virtual const TStructFieldInfo *getFieldsTable(void);
+ virtual sInt32 numFields(void);
+ // get actual struct base address
+ virtual uInt8P getStructAddr(void);
+
+private:
+ // profile
+ sInt32 fTargetIndex; // index for writing back profile
+ TBinfileDBSyncTarget *fTargetP;
+ TBinfileClientConfig *fBinfileClientConfigP;
+ TBinfileDSConfig *fBinfileDSConfigP;
+}; // TBinfileTargetKey
+
+
+
+// Binfile based targets collection key
+class TBinfileTargetsKey :
+ public TSettingsKeyImpl
+{
+ typedef TSettingsKeyImpl inherited;
+
+public:
+ TBinfileTargetsKey(TEngineInterface *aEngineInterfaceP, sInt32 aProfileID);
+
+protected:
+ // targets can be opened only by dbtype-ID
+ virtual TSyError OpenSubkey(
+ TSettingsKeyImpl *&aSettingsKeyP,
+ sInt32 aID, uInt16 aMode
+ );
+private:
+ sInt32 fProfileID; // profile ID
+ sInt32 fTargetIterator;
+ TBinfileClientConfig *fBinfileClientConfigP;
+}; // TBinfileTargetsKey
+
+
+#ifdef AUTOSYNC_SUPPORT
+
+// Binfile based autosync level key
+class TBinfileASLevelKey :
+ public TStructFieldsKey
+{
+ typedef TStructFieldsKey inherited;
+
+public:
+ TBinfileASLevelKey(
+ TEngineInterface *aEngineInterfaceP,
+ sInt32 aLevelIndex,
+ TBinfileDBSyncProfile *aProfileP,
+ TBinfileClientConfig *aBinfileClientConfigP
+ );
+ virtual ~TBinfileASLevelKey();
+
+protected:
+ // return ID of this key
+ virtual TSyError GetKeyID(sInt32 &aID);
+
+ // get table describing the fields in the struct
+ virtual const TStructFieldInfo *getFieldsTable(void);
+ virtual sInt32 numFields(void);
+ // get actual struct base address
+ virtual uInt8P getStructAddr(void);
+
+private:
+ // profile
+ sInt32 fLevelIndex; // level index
+ TBinfileDBSyncProfile *fProfileP;
+ TBinfileClientConfig *fBinfileClientConfigP;
+}; // TBinfileASLevelKey
+
+
+// Binfile based autosync levels collection key
+class TBinfileASLevelsKey :
+ public TSettingsKeyImpl
+{
+ typedef TSettingsKeyImpl inherited;
+
+public:
+ TBinfileASLevelsKey(TEngineInterface *aEngineInterfaceP, TBinfileDBSyncProfile *aProfileP);
+
+protected:
+ // levels can be opened only by ID
+ virtual TSyError OpenSubkey(
+ TSettingsKeyImpl *&aSettingsKeyP,
+ sInt32 aID, uInt16 aMode
+ );
+private:
+ sInt32 fASLevelIterator;
+ TBinfileDBSyncProfile *fProfileP;
+ TBinfileClientConfig *fBinfileClientConfigP;
+}; // TBinfileASLevelsKey
+
+#endif // AUTOSYNC_SUPPORT
+
+
+// Binfile based profile key
+class TBinfileProfileKey :
+ public TStructFieldsKey
+{
+ typedef TStructFieldsKey inherited;
+
+public:
+ TBinfileProfileKey(
+ TEngineInterface *aEngineInterfaceP,
+ sInt32 aProfileIndex,
+ TBinfileDBSyncProfile *aProfileP,
+ TBinfileClientConfig *aBinfileClientConfigP
+ );
+ virtual ~TBinfileProfileKey();
+ TBinfileClientConfig *getBinfileClientConfig(void) { return fBinfileClientConfigP; };
+ TBinfileDBSyncProfile *getProfile(void) { return fProfileP; };
+ sInt32 getProfileID() { return fProfileP ? fProfileP->profileID : KEYVAL_ID_UNKNOWN; };
+protected:
+ // return ID of this key
+ virtual TSyError GetKeyID(sInt32 &aID);
+
+ // open subkey by name (not by path!)
+ // - this is the actual implementation
+ virtual TSyError OpenSubKeyByName(
+ TSettingsKeyImpl *&aSettingsKeyP,
+ cAppCharP aName, stringSize aNameSize,
+ uInt16 aMode
+ );
+ // get table describing the fields in the struct
+ virtual const TStructFieldInfo *getFieldsTable(void);
+ virtual sInt32 numFields(void);
+ // get actual struct base address
+ virtual uInt8P getStructAddr(void);
+
+private:
+ // profile
+ sInt32 fProfileIndex; // index for writing back profile
+ TBinfileDBSyncProfile *fProfileP;
+ TBinfileClientConfig *fBinfileClientConfigP;
+}; // TBinfileProfileKey
+
+
+
+// Binfile based profiles collection key
+class TBinfileProfilesKey :
+ public TStructFieldsKey
+{
+ typedef TStructFieldsKey inherited;
+
+public:
+ TBinfileProfilesKey(TEngineInterface *aEngineInterfaceP);
+ virtual ~TBinfileProfilesKey();
+ TBinfileClientConfig *getBinfileClientConfig(void) { return fBinfileClientConfigP; };
+ // set iterator
+ void setNextProfileindex(sInt32 aProfileIndex) { fProfileIterator=aProfileIndex-1; };
+
+protected:
+ // profiles can be opened only by ID
+ virtual TSyError OpenSubkey(
+ TSettingsKeyImpl *&aSettingsKeyP,
+ sInt32 aID, uInt16 aMode
+ );
+ virtual TSyError DeleteSubkey(sInt32 aID);
+
+ // get table describing the fields in the struct
+ virtual const TStructFieldInfo *getFieldsTable(void);
+ virtual sInt32 numFields(void);
+ // get actual struct base address
+ virtual uInt8P getStructAddr(void);
+private:
+ // internal
+ sInt32 fProfileIterator;
+public:
+ // binfileconfig
+ TBinfileClientConfig *fBinfileClientConfigP;
+ // flag how to call loadVarConfig
+ bool fMayLooseOldCfg;
+}; // TBinfileProfilesKey
+
+
+
+// Binfile based log entry key
+class TBinfileLogKey :
+ public TStructFieldsKey
+{
+ typedef TStructFieldsKey inherited;
+
+public:
+ TBinfileLogKey(
+ TEngineInterface *aEngineInterfaceP,
+ TLogFileEntry *aLogEntryP,
+ TBinfileClientConfig *aBinfileClientConfigP
+ );
+ virtual ~TBinfileLogKey();
+ TBinfileClientConfig *getBinfileClientConfig(void) { return fBinfileClientConfigP; };
+ TLogFileEntry *getLogEntry(void) { return fLogEntryP; };
+protected:
+ // get table describing the fields in the struct
+ virtual const TStructFieldInfo *getFieldsTable(void);
+ virtual sInt32 numFields(void);
+ // get actual struct base address
+ virtual uInt8P getStructAddr(void);
+
+private:
+ TLogFileEntry *fLogEntryP;
+ TBinfileClientConfig *fBinfileClientConfigP;
+}; // TBinfileLogKey
+
+
+// Binfile based log entry collection key
+class TBinfileLogsKey :
+ public TSettingsKeyImpl
+{
+ typedef TSettingsKeyImpl inherited;
+
+public:
+ TBinfileLogsKey(TEngineInterface *aEngineInterfaceP);
+
+protected:
+ // targets can be opened only by dbtype-ID
+ virtual TSyError OpenSubkey(
+ TSettingsKeyImpl *&aSettingsKeyP,
+ sInt32 aID, uInt16 aMode
+ );
+ virtual TSyError DeleteSubkey(sInt32 aID);
+private:
+ // binfileconfig
+ TBinfileClientConfig *fBinfileClientConfigP;
+ // iterator
+ sInt32 fLogEntryIterator;
+ // the log file
+ TBinFile fLogFile;
+}; // TBinfileLogsKey
+
+
+
+// Binfile based client settings rootkey
+class TBinfileClientRootKey :
+ public TSettingsRootKey
+{
+ typedef TSettingsRootKey inherited;
+
+public:
+ TBinfileClientRootKey(TEngineInterface *aEngineInterfaceP) :
+ inherited(aEngineInterfaceP) {};
+
+protected:
+ // open subkey by name (not by path!)
+ // - this is the actual implementation
+ virtual TSyError OpenSubKeyByName(
+ TSettingsKeyImpl *&aSettingsKeyP,
+ cAppCharP aName, stringSize aNameSize,
+ uInt16 aMode
+ ) {
+ if (strucmp(aName,"profiles",aNameSize)==0)
+ aSettingsKeyP = new TBinfileProfilesKey(fEngineInterfaceP);
+ else if (strucmp(aName,"synclogs",aNameSize)==0)
+ aSettingsKeyP = new TBinfileLogsKey(fEngineInterfaceP);
+ else
+ return inherited::OpenSubKeyByName(aSettingsKeyP,aName,aNameSize,aMode);
+ // opened a key
+ return LOCERR_OK;
+ };
+}; // TBinfileClientRootKey
+
+
+
+#endif // ENGINEINTERFACE_SUPPORT
+
+
+// Config
+// ======
+
+class TBinfileClientConfig:
+ public TClientConfig
+{
+ typedef TClientConfig inherited;
+public:
+ TBinfileClientConfig(TConfigElement *aParentElement);
+ virtual ~TBinfileClientConfig();
+ // API for settings management
+ // - initialize a new-created profile database
+ void initProfileDb(void);
+ // - create a new profile including appropriate targets
+ sInt32 newProfile(const char *aProfileName, bool aSetDefaults, sInt32 aTemplateProfile=-1);
+ // - init profile record with defaults
+ static void initProfile(TBinfileDBSyncProfile &aProfile, const char *aName, bool aWithDefaults);
+ // - write profile, returns index of profile
+ sInt32 writeProfile(
+ sInt32 aProfileIndex, // -1 if adding new profile
+ TBinfileDBSyncProfile &aProfile // profile ID is set to new ID in aProfile
+ );
+ // - delete profile (and all of its targets)
+ bool deleteProfile(
+ sInt32 aProfileIndex
+ );
+ // - get number of existing profiles
+ sInt32 numProfiles(void);
+ // - get profile, returns index or -1 if no more profiles
+ sInt32 getProfile(
+ sInt32 aProfileIndex,
+ TBinfileDBSyncProfile &aProfile
+ );
+ // - get profile index from name, returns index or -1 if no matching profile found
+ sInt32 getProfileIndexByName(cAppCharP aProfileName);
+ // - get profile by ID
+ sInt32 getProfileByID(
+ uInt32 aProfileID,
+ TBinfileDBSyncProfile &aProfile
+ );
+ sInt32 getProfileIndex(uInt32 aProfileID);
+ // - checks that all profiles are complete with targets for all configured datastores
+ // (in case of STD->PRO upgrade for example)
+ void checkProfiles(void);
+ // - checks that profile is complete with targets for all configured datastores
+ // (in case of STD->PRO upgrade for example)
+ void checkProfile(sInt32 aProfileIndex);
+ // - get profile ID by index, 0 if none found
+ uInt32 getIDOfProfile(sInt32 aProfileIndex);
+ // - check for feature enabled (profile or license dependent)
+ bool isFeatureEnabled(TBinfileDBSyncProfile *aProfileP, uInt16 aFeatureNo); // with profile already loaded
+ bool isFeatureEnabled(sInt32 aProfileIndex, uInt16 aFeatureNo); // by profile index
+ // - check for readonly parts of profile settings
+ bool isReadOnly(TBinfileDBSyncProfile *aProfileP, uInt8 aReadOnlyMask);
+ bool isReadOnly(sInt32 aProfileIndex, uInt8 aReadOnlyMask);
+ // - get last sync (earliest of lastSync of all sync-enabled targets), 0=never. Returns false if no enabled targets
+ bool getProfileLastSyncTime(uInt32 aProfileID, lineartime_t &aLastSync, bool &aZapsServer, bool &aZapsClient);
+ // - get last sync of target, 0=never. Returns false if target not enabled
+ bool getTargetLastSyncTime(sInt32 aTargetIndex, lineartime_t &aLastSync, bool &aZapsServer, bool &aZapsClient, uInt32 &aDBID);
+ bool getTargetLastSyncTime(TBinfileDBSyncTarget &aTarget, lineartime_t &aLastSync, bool &aZapsServer, bool &aZapsClient, uInt32 &aDBID);
+ // - check if datastore of specified target is available in the given profile
+ bool isTargetAvailable(TBinfileDBSyncProfile *aProfileP, uInt32 aLocalDBTypeID);
+ // - find available target for profile by DB ID/name. Returns target index or -1 if
+ // DBTypeID not available in this profile/license or not implemented at all
+ sInt32 findAvailableTargetIndexByDBInfo(
+ TBinfileDBSyncProfile *aProfileP, // profile to search targets for
+ uInt32 aLocalDBTypeID,
+ const char *aLocalDBName // can be NULL if name does not matter
+ );
+ // - find or create target for profile by DB ID/name. creates target if not found existing already.
+ sInt32 findOrCreateTargetIndexByDBInfo(
+ uInt32 aProfileID, // profile to search targets for
+ uInt32 aLocalDBTypeID,
+ const char *aLocalDBName // can be NULL if name does not matter
+ );
+ // - find target for profile by DB ID/name. Returns target index or -1 if none found
+ sInt32 findTargetIndexByDBInfo(
+ uInt32 aProfileID, // profile to search targets for
+ uInt32 aLocalDBTypeID,
+ const char *aLocalDBName
+ );
+ // - find target for profile. Returns target index or -1 if none found
+ sInt32 findTargetIndex(
+ uInt32 aProfileID, // profile to search targets for
+ sInt32 aTargetSeqNum // sequence number (0..n)
+ );
+ // - write target, returns index of target
+ sInt32 writeTarget(
+ sInt32 aTargetIndex, // -1 if adding new target
+ const TBinfileDBSyncTarget &aTarget
+ );
+ // - delete target
+ bool deleteTarget(
+ sInt32 aTargetIndex
+ );
+ // - get target info
+ sInt32 getTarget(
+ sInt32 aTargetIndex,
+ TBinfileDBSyncTarget &aTarget
+ );
+ // - get path where to store binfiles
+ void getBinFilesPath(string &aPath);
+ #ifndef HARDCODED_CONFIG
+ // - configurable path where to store binfiles
+ string fBinFilesPath;
+ #endif
+ // - if set, sync log statistics are saved to binfile
+ bool fBinFileLog;
+ // Binary Files
+ TBinFile fProfileBinFile;
+ TBinFile fTargetsBinFile;
+ // - cleanup
+ void cleanChangeLogForTarget(sInt32 aTargetIndex);
+ void cleanChangeLogForDBname(cAppCharP aDBName);
+ // - called when app should save its persistent state
+ virtual void saveAppState(void);
+ // - MUST be called after creating config to load (or pre-load) variable parts of config
+ // such as binfile profiles. If aDoLoose==false, situations, where existing config
+ // is detected but cannot be re-used will return an error. With aDoLoose==true, config
+ // files etc. are created even if it means a loss of data.
+ virtual localstatus loadVarConfig(bool aDoLoose=false);
+ // open and close the settings databases
+ localstatus openSettingsDatabases(bool aDoLoose);
+ void closeSettingsDatabases(void);
+protected:
+ // check config elements
+ #ifndef HARDCODED_CONFIG
+ virtual bool localStartElement(const char *aElementName, const char **aAttributes, sInt32 aLine);
+ #endif
+ virtual void clear();
+ virtual void localResolve(bool aLastPass);
+ // Client provisioning declarations
+ CLIENTPROVISIONING_CLASSDECL
+ // Autosync declarations
+ CLIENTAUTOSYNC_CLASSDECL
+}; // TBinfileClientConfig
+
+
+// Binary file based session
+class TBinfileImplClient: public TStdLogicAgent
+{
+ typedef TStdLogicAgent inherited;
+ #ifdef ENGINEINTERFACE_SUPPORT
+ friend class TBinFileClientParamsKey;
+ #endif
+public:
+ TBinfileImplClient(TSyncClientBase *aSyncClientBaseP, const char *aSessionID);
+ // - selects a profile (returns false if profile not found)
+ // Note: This call must create and initialize all datastores that
+ // are to be synced with that profile.
+ virtual localstatus SelectProfile(uInt32 aProfileSelector, bool aAutoSyncSession=false);
+ virtual ~TBinfileImplClient();
+ virtual void TerminateSession(void); // Terminate session, like destructor, but without actually destructing object itself
+ virtual void ResetSession(void); // Resets session (but unlike TerminateSession, session might be re-used)
+ void InternalResetSession(void); // static implementation for calling through virtual destructor and virtual ResetSession();
+ #ifdef ENGINEINTERFACE_SUPPORT
+ // set profileID to client session before doing first SessionStep
+ virtual void SetProfileSelector(uInt32 aProfileSelector);
+ /// @brief Get new session key to access details of this session
+ virtual appPointer newSessionKey(TEngineInterface *aEngineInterfaceP);
+ #endif // ENGINEINTERFACE_SUPPORT
+ // - load remote connect params (syncml version, type, format and last nonce)
+ // Note: agents that can cache this information between sessions will load
+ // last info here.
+ virtual void loadRemoteParams(void);
+ // - save remote connect params for use in next session (if descendant implements it)
+ virtual void saveRemoteParams(void);
+ // special behaviour
+ #ifndef GUARANTEED_UNIQUE_DEVICID
+ virtual bool devidWithUserHash(void) { return (fRemoteFlags & remotespecs_devidWithUserHash)!=0; }; // include user name to make a hash-based pseudo-device ID when flag is set
+ #endif
+ // - handle custom put and result commands (for remote provisioning and IPP)
+ virtual void processPutResultItem(bool aIsPut, const char *aLocUri, TSmlCommand *aPutResultsCommandP, SmlItemPtr_t aPutResultsItemP, TStatusCommand &aStatusCommand);
+ #ifdef IPP_SUPPORT
+ // - called to issue custom get and put commands
+ virtual void issueCustomGetPut(bool aGotDevInf, bool aSentDevInf);
+ #endif
+ // binfile agent config
+ TBinfileClientConfig *fConfigP;
+ // unique ID to identify info record of remote party (profile for client, deviceentry for server)
+ uInt32 fRemotepartyID;
+protected:
+ // - check remote devinf to detect special behaviour needed for some servers. Base class
+ // does not do anything on server level (configured rules are handled at session level)
+ virtual localstatus checkRemoteSpecifics(SmlDevInfDevInfPtr_t aDevInfP);
+ TBinfileDBSyncProfile fProfile;
+ // - remote specific client behaviour flags
+ uInt8 fRemoteFlags; // flags for remote specific behaviour (remotespecs_XXX)
+private:
+ // selected profile
+ sInt32 fProfileIndex;
+ bool fProfileDirty;
+}; // TBinfileImplClient
+
+
+#ifdef ENGINEINTERFACE_SUPPORT
+
+// Support for EngineModule common interface
+// =========================================
+
+// client runtime parameters
+class TBinFileClientParamsKey :
+ public TClientParamsKey
+{
+ typedef TClientParamsKey inherited;
+
+public:
+ TBinFileClientParamsKey(TEngineInterface *aEngineInterfaceP, TSyncClient *aClientSessionP);
+ virtual ~TBinFileClientParamsKey() {};
+
+protected:
+ // open subkey by name (not by path!)
+ // - this is the actual implementation
+ virtual TSyError OpenSubKeyByName(
+ TSettingsKeyImpl *&aSettingsKeyP,
+ cAppCharP aName, stringSize aNameSize,
+ uInt16 aMode
+ );
+}; // TBinFileClientParamsKey
+
+#endif // ENGINEINTERFACE_SUPPORT
+
+} // namespace sysync
+
+#endif // BINFILEIMPLCLIENT_H
+
+// eof
diff --git a/src/sysync/binfileimplds.cpp b/src/sysync/binfileimplds.cpp
new file mode 100755
index 0000000..25f1b55
--- /dev/null
+++ b/src/sysync/binfileimplds.cpp
@@ -0,0 +1,2081 @@
+/**
+ * @File binfileimplds.cpp
+ *
+ * @Author Lukas Zeller (luz@synthesis.ch)
+ *
+ * @brief TBinfileImplDS
+ * Represents a client datastore implementation which has target management
+ * (and optionally change log) based on binary files
+ *
+ * Copyright (c) 2003-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ * @Date 2005-09-30 : luz : created from TBinfileImplDS
+ */
+/*
+ */
+
+// includes
+#include "prefix_file.h"
+#include "sysync.h"
+#include "binfileimplclient.h"
+#include "binfileimplds.h"
+
+
+namespace sysync {
+
+
+#ifdef SCRIPT_SUPPORT
+
+// Script Functions
+// ================
+
+class TBFDSfuncs {
+public:
+
+ // variant TARGETSETTING(string settingsfieldname)
+ // returns data from target settings (like /profiles/n/targets/dbid/settingsfieldname does)
+ static void func_TargetSetting(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ string varname;
+
+ TBinfileImplDS *dsP = static_cast<TBinfileImplDS *>(aFuncContextP->getCallerContext());
+ // get name
+ aFuncContextP->getLocalVar(0)->getAsString(varname);
+ // %%% simple implementation for now, only limit1, limit2, extras and remoteFilters are supported
+ // %%% later: add generic way to access target settings fields, and
+ // probably profile settings as well.
+ if (strucmp(varname.c_str(),"extras")==0) {
+ aTermP = newItemField(fty_integer, aFuncContextP->getSessionZones());
+ aTermP->setAsInteger(dsP->fTarget.extras);
+ }
+ else if (strucmp(varname.c_str(),"limit1")==0) {
+ aTermP = newItemField(fty_integer, aFuncContextP->getSessionZones());
+ aTermP->setAsInteger(dsP->fTarget.limit1);
+ }
+ else if (strucmp(varname.c_str(),"limit2")==0) {
+ aTermP = newItemField(fty_integer, aFuncContextP->getSessionZones());
+ aTermP->setAsInteger(dsP->fTarget.limit2);
+ }
+ #if TARGETS_DB_VERSION>5
+ else if (strucmp(varname.c_str(),"remoteFilters")==0) {
+ aTermP = newItemField(fty_string, aFuncContextP->getSessionZones());
+ aTermP->setAsString(dsP->fTarget.remoteFilters);
+ }
+ #endif
+ else {
+ aTermP=newItemField(fty_none, aFuncContextP->getSessionZones());
+ aTermP->unAssign(); // make it (already is...) unassigned
+ }
+ }; // func_TargetSetting
+
+}; // TBFDSfuncs
+
+
+const uInt8 param_OneStr[] = { VAL(fty_string) };
+
+const TBuiltInFuncDef BinfileDBFuncDefs[] = {
+ { "TARGETSETTING", TBFDSfuncs::func_TargetSetting, fty_none, 1, param_OneStr },
+};
+
+
+// chain to general ClientDB functions
+static void *BinfileClientDBChainFunc(void *&aCtx)
+{
+ // caller context remains unchanged
+ // -> no change needed
+ // next table is general ClientDB func table
+ return (void *)&ClientDBFuncTable;
+} // BinfileClientDBChainFunc
+
+
+// function table for client-only script functions
+const TFuncTable BinfileClientDBFuncTable = {
+ sizeof(BinfileDBFuncDefs) / sizeof(TBuiltInFuncDef), // size of table
+ BinfileDBFuncDefs, // table pointer
+ BinfileClientDBChainFunc // chain to general agent funcs.
+};
+
+
+#endif // SCRIPT_SUPPORT
+
+
+// Config
+// ======
+
+TBinfileDSConfig::TBinfileDSConfig(const char* aName, TConfigElement *aParentElement) :
+ TLocalDSConfig(aName,aParentElement)
+{
+ // nop so far
+ clear();
+} // TBinfileDSConfig::TBinfileDSConfig
+
+
+TBinfileDSConfig::~TBinfileDSConfig()
+{
+ // nop so far
+} // TBinfileDSConfig::~TBinfileDSConfig
+
+
+// init defaults
+void TBinfileDSConfig::clear(void)
+{
+ // init defaults
+ fLocalDBPath.erase();
+ fDSAvailFlag=0;
+ // clear inherited
+ inherited::clear();
+} // TBinfileDSConfig::clear
+
+
+#ifndef HARDCODED_CONFIG
+
+// config element parsing
+bool TBinfileDSConfig::localStartElement(const char *aElementName, const char **aAttributes, sInt32 aLine)
+{
+ // checking the elements
+ if (strucmp(aElementName,"dbpath")==0)
+ expectString(fLocalDBPath);
+ else if (strucmp(aElementName,"dsavailflag")==0)
+ expectUInt16(fDSAvailFlag);
+ // - none known here
+ else
+ return TLocalDSConfig::localStartElement(aElementName,aAttributes,aLine);
+ // ok
+ return true;
+} // TBinfileDSConfig::localStartElement
+
+#endif
+
+// resolve
+void TBinfileDSConfig::localResolve(bool aLastPass)
+{
+ if (aLastPass) {
+ // check for required settings
+ // %%% tbd
+ }
+ // resolve inherited
+ inherited::localResolve(aLastPass);
+} // TBinfileDSConfig::localResolve
+
+
+
+// checks if datastore is available for given profile. If aProfileP is NULL,
+// general availability is checked (profile might allow more or less)
+bool TBinfileDSConfig::isAvailable(TBinfileDBSyncProfile *aProfileP)
+{
+ #ifndef DSFLAGS_ALWAYS_AVAILABLE
+ // all datastores are always available
+ return true;
+ #else
+ // check if available
+ // - %%%later: check if license allows using this datastore
+ // %%%
+ // - if it's one of the always available datastores, always return true
+ if (fDSAvailFlag & DSFLAGS_ALWAYS_AVAILABLE) return true;
+ // - check if it's enabled in the profile
+ if (aProfileP==NULL) return false; // no profile
+ return (aProfileP->dsAvailFlags & fDSAvailFlag)!=0;
+ #endif
+} // TBinfileDSConfig::isAvailable
+
+
+
+#ifndef DEFAULT_SYNCMODE
+#define DEFAULT_SYNCMODE smo_twoway
+#endif
+
+// init default target for datastore
+void TBinfileDSConfig::initTarget(
+ TBinfileDBSyncTarget &aTarget,
+ uInt32 aRemotepartyID,
+ const char *aRemoteName, // defaults to name of datastore
+ bool aEnabled // enabled?
+)
+{
+ // link to the profile
+ aTarget.remotepartyID=aRemotepartyID;
+ // enabled or not?
+ aTarget.enabled=aEnabled;
+ // force slow sync (slow sync is used anyway, but shows this to user)
+ aTarget.forceSlowSync=true;
+ // sync mode
+ aTarget.syncmode=DEFAULT_SYNCMODE;
+ // reset all options
+ aTarget.limit1=0;
+ aTarget.limit2=0;
+ aTarget.extras=0;
+ // database identification
+ aTarget.localDBTypeID=fLocalDBTypeID; // what target database
+ AssignCString(aTarget.localDBPath,fLocalDBPath.c_str(),localDBpathMaxLen);
+ #if defined(DESKTOP_CLIENT) || TARGETS_DB_VERSION>4
+ AssignCString(aTarget.localContainerName,NULL,localDBpathMaxLen);
+ #endif
+ // link to configuration is by datastore name
+ AssignCString(aTarget.dbname,getName(),dbnamelen);
+ // default name for remote datastore is same as local one
+ if (!aRemoteName) aRemoteName=getName();
+ AssignCString(aTarget.remoteDBpath,aRemoteName,remoteDBpathMaxLen);
+ // - init new target record's variables (will be updated after sync)
+ aTarget.lastSync=0; // no last sync date yet
+ aTarget.lastTwoWaySync=0;
+ aTarget.lastSuspendModCount=0; /// @note: before DS 1.2 enginem this was used as "lastModCount"
+ aTarget.resumeAlertCode=0; // no resume yet
+ aTarget.lastTwoWayModCount=0;
+ aTarget.remoteAnchor[0]=0; // no anchor yet
+ // Version 6 and later
+ #if TARGETS_DB_VERSION>5
+ aTarget.lastSyncIdentifier[0]=0;
+ aTarget.lastSuspendIdentifier[0]=0;
+ AssignCString(aTarget.remoteDBdispName, getName(), dispNameMaxLen); // default to local name
+ aTarget.filterCapDesc[0]=0;
+ aTarget.remoteFilters[0]=0;
+ aTarget.localFilters[0]=0;
+ #endif
+} // initTarget
+
+
+
+
+/*
+ * Implementation of TBinfileImplDS
+ */
+
+TBinfileImplDS::TBinfileImplDS(
+ TBinfileDSConfig *aConfigP,
+ sysync::TSyncSession *aSessionP,
+ const char *aName,
+ uInt32 aCommonSyncCapMask
+) :
+ TStdLogicDS(aConfigP, aSessionP, aName, aCommonSyncCapMask)
+{
+ // no changelog loaded yet (needed here because InternalResetDataStore will test it)
+ fLoadedChangeLog=NULL;
+ fLoadedChangeLogEntries=0; // just to make sure
+ // now do internal reset
+ InternalResetDataStore();
+ // save config pointer
+ fConfigP=aConfigP;
+ // no target loaded yet
+ fTargetIndex=-1;
+} // TBinfileImplDS::TBinfileImplDS
+
+
+
+void TBinfileImplDS::dsResetDataStore(void)
+{
+ // Do my normal internal reset
+ InternalResetDataStore();
+ // Let ancestor initialize
+ inherited::dsResetDataStore();
+ // And now force MaxGUIDSize to what this datastore can handle
+ fMaxGUIDSize=BINFILE_MAXGUIDSIZE;
+} // TBinfileImplDS::dsResetDataStore
+
+
+void TBinfileImplDS::InternalResetDataStore(void)
+{
+ // forget preflight
+ fPreflighted=false;
+ // forget loaded changelog
+ forgetChangeLog();
+ // unknown number of changes
+ fNumberOfLocalChanges=-1;
+} // TBinfileImplDS::InternalResetDataStore
+
+
+TBinfileImplDS::~TBinfileImplDS()
+{
+ InternalResetDataStore();
+} // TBinfileImplDS::~TBinfileImplDS
+
+
+// called when message processing (and probably thread) ends
+// (can be used to finalize things that cannot continue until next request
+// such as code that cannot be called from different threads)
+void TBinfileImplDS::dsEndOfMessage(void)
+{
+ // %%% save between messages in debug
+ #ifdef SYDEBUG
+ //endWrite();
+ #endif
+ // let ancestor do things
+ inherited::dsEndOfMessage();
+} // TBinfileImplDS::dsEndOfMessage
+
+
+/// inform logic of coming state change
+localstatus TBinfileImplDS::dsBeforeStateChange(TLocalEngineDSState aOldState,TLocalEngineDSState aNewState)
+{
+ return inherited::dsBeforeStateChange(aOldState, aNewState);
+} // TBinfileImplDS::dsBeforeStateChange
+
+
+/// inform logic of happened state change
+localstatus TBinfileImplDS::dsAfterStateChange(TLocalEngineDSState aOldState,TLocalEngineDSState aNewState)
+{
+ return inherited::dsAfterStateChange(aOldState, aNewState);
+} // TBinfileImplDS::dsAfterStateChange
+
+
+// - init Sync Parameters for datastore
+// Derivates might override this to pre-process and modify parameters
+// (such as adding client settings as CGI to remoteDBPath)
+bool TBinfileImplDS::dsSetClientSyncParams(
+ TSyncModes aSyncMode,
+ bool aSlowSync,
+ const char *aRemoteDBPath,
+ const char *aDBUser,
+ const char *aDBPassword,
+ const char *aLocalPathExtension,
+ const char *aRecordFilterQuery,
+ bool aFilterInclusive
+)
+{
+ #ifdef AUTOSYNC_SUPPORT
+ string s;
+ bool cgi=false;
+ // add autosync options if this is an autosync
+ if (fConfigP->fAutosyncAlerted || fConfigP->fAutosyncForced) {
+ if (!fConfigP->fAutosyncPathCGI.empty()) {
+ s=aRemoteDBPath;
+ cgi = s.find('?')!=string::npos;
+ if (!cgi) { s+='?'; cgi=true; }
+ s += fConfigP->fAutosyncPathCGI;
+ aRemoteDBPath=s.c_str();
+ }
+ }
+ #endif
+ return inherited::dsSetClientSyncParams(aSyncMode,aSlowSync,aRemoteDBPath,aDBUser,aDBPassword,aLocalPathExtension,aRecordFilterQuery,aFilterInclusive);
+} // TBinfileImplDS::dsSetClientSyncParams
+
+
+
+// BinFile DB implementation specific routines
+// ===========================================
+
+
+// clean up change log
+// - removes chgl_deleted entries that are older or
+// same age as indicated by aOldestSyncModCount)
+// - finalizes localids
+localstatus TBinfileImplDS::changeLogPostflight(uInt32 aOldestSyncModCount)
+{
+ // review all logentries
+ TChangeLogEntry logentry;
+ uInt32 logindex=0;
+ while (true) {
+ if (fChangeLog.readRecord(logindex,&logentry)!=BFE_OK) break; // seen all
+ if (
+ (logentry.flags & chgl_deleted) &&
+ (logentry.modcount<=aOldestSyncModCount)
+ ) {
+ // all targets have seen this delete already, so this one should be deleted
+ // - delete the record, another one will appear at this index
+ fChangeLog.deleteRecord(logindex);
+ }
+ else {
+ // no delete, finalize localid (possible only with string localIDs)
+ #ifndef NUMERIC_LOCALIDS
+ localid_out_t locID = logentry.dbrecordid;
+ if (dsFinalizeLocalID(locID)) {
+ // update log entry
+ ASSIGN_LOCALID_TO_FLD(logentry.dbrecordid,LOCALID_OUT_TO_IN(locID));
+ fChangeLog.updateRecord(logindex, &logentry);
+ }
+ #endif
+ // no delete, advance index
+ logindex++;
+ }
+ }
+ return LOCERR_OK;
+} // TBinfileImplDS::changeLogPostflight
+
+
+// zap changelog. Should be called if datastore as a entiety was replaced
+// by another datatstore (or created new)
+void TBinfileImplDS::zapChangeLog(void)
+{
+ // forget cached changelog if any
+ forgetChangeLog();
+ // make sure changelog file is open
+ if (openChangeLog()) {
+ // kill all entries
+ fChangeLog.truncate(0);
+ // reset modcount
+ fChgLogHeader.modcount=0;
+ // make sure header is written
+ fChangeLog.setExtraHeaderDirty();
+ fChangeLog.flushHeader();
+ }
+ PDEBUGPRINTFX(DBG_ADMIN+DBG_DBAPI,("zapChangeLog: erased changelog"));
+} // TBinfileImplDS::zapChangeLog
+
+
+
+
+// returns true if we had a valid changelog
+bool TBinfileImplDS::openChangeLog(void)
+{
+ if (fChangeLog.isOpen()) return true; // was already open
+ // open changelog. Name is datastore name with _clg.bfi suffix
+ string changelogname;
+ static_cast<TBinfileClientConfig *>(fSessionP->getSessionConfig())->
+ getBinFilesPath(changelogname);
+ changelogname += getName();
+ changelogname += CHANGELOG_DB_SUFFIX;
+ fChangeLog.setFileInfo(changelogname.c_str(),CHANGELOG_DB_VERSION,CHANGELOG_DB_ID);
+ if (fChangeLog.open(sizeof(TChangeLogHeader),&fChgLogHeader)!=BFE_OK) {
+ // create new change log or overwrite incompatible one
+ // - init changelog header fields
+ fChgLogHeader.modcount=0;
+ // - create new changelog
+ fChangeLog.create(sizeof(TChangeLogEntry),sizeof(TChangeLogHeader),&fChgLogHeader,true);
+ PDEBUGPRINTFX(DBG_ADMIN+DBG_DBAPI,("openChangeLog: changelog did not exist (or bad version) -> created new"));
+ return false; // changelog is new, so we need a slow sync
+ }
+ return true; // changelog already existed, so we assume it's up-to-date
+} // TBinfileImplDS::openChangeLog
+
+
+// returns true if we had a valid pending map file
+bool TBinfileImplDS::openPendingMaps(void)
+{
+ if (fPendingMaps.isOpen()) return true; // was already open
+ string pendingmapsname;
+ static_cast<TBinfileClientConfig *>(fSessionP->getSessionConfig())->
+ getBinFilesPath(pendingmapsname);
+ pendingmapsname += getName();
+ pendingmapsname += PENDINGMAP_DB_SUFFIX;
+ fPendingMaps.setFileInfo(pendingmapsname.c_str(),PENDINGMAP_DB_VERSION,PENDINGMAP_DB_ID);
+ PDEBUGPRINTFX(DBG_ADMIN+DBG_DBAPI+DBG_EXOTIC,("openPendingMaps: file name='%s'",pendingmapsname.c_str()));
+ if (fPendingMaps.open(sizeof(TPendingMapHeader),&fPendingMapHeader)!=BFE_OK) {
+ // create new change log or overwrite incompatible one
+ // - bind to remote party (we have a single pendingmap file, and it is valid only for ONE remote party)
+ fPendingMapHeader.remotepartyID = static_cast<TBinfileImplClient *>(fSessionP)->fRemotepartyID;
+ // - create new changelog
+ fPendingMaps.create(sizeof(TPendingMapEntry),sizeof(TPendingMapHeader),&fPendingMapHeader,true);
+ PDEBUGPRINTFX(DBG_ADMIN+DBG_DBAPI,("openPendingMaps: pending maps did not exist (or bad version) -> created new"));
+ return false; // pending map file is new, so we have no already pending maps
+ }
+ return true; // pending map file already existed, so we assume it's up-to-date
+} // TBinfileImplDS::openPendingMaps
+
+
+// get reference time to be used as reference date for this sync's modifications
+// Note: this time marks the beginning of the sync session. If possible
+// datastore should mark all modifications with this time, even if
+// they occur later during the sync session. Only if setting the
+// modified date explicitly is not possible, defining SYNCTIME_IS_ENDOFSESSION
+// can be used to make sure that modifications made during the session
+// are not detected as changed for the next session.
+lineartime_t TBinfileImplDS::getThisSyncModRefTime(void)
+{
+ return
+ #ifdef SYNCTIME_IS_LOCALTIME
+ makeLocalTimestamp(fCurrentSyncTime); // make local time for comparison
+ #else
+ fCurrentSyncTime;
+ #endif
+} // TBinfileImplDS::getThisSyncModRefTime
+
+
+// get reference time to be used in case datastore implementation wants to compare
+// with the time of last sync (last two-way sync, that is!)
+lineartime_t TBinfileImplDS::getLastSyncModRefTime(void)
+{
+ return
+ #ifdef SYNCTIME_IS_LOCALTIME
+ makeLocalTimestamp(fPreviousSyncTime); // make local time for comparison
+ #else
+ fPreviousSyncTime;
+ #endif
+} // TBinfileImplDS::getLastSyncModRefTime
+
+
+
+// update change log using CRC checksum comparison before syncing
+// Note: Don't call before types are ok (we need TSyncItems)
+localstatus TBinfileImplDS::changeLogPreflight(bool &aValidChangelog)
+{
+ localstatus sta=LOCERR_OK;
+ aValidChangelog=false;
+ bferr err=BFE_OK;
+ TChangeLogEntry *existingentries = NULL; // none yet
+ uInt32 numexistinglogentries;
+ bool foundone;
+ uInt32 seen=0;
+ uInt32 logindex;
+ TChangeLogEntry newentry;
+ #ifndef CHANGEDETECTION_AVAILABLE
+ #ifndef RECORDHASH_FROM_DBAPI
+ TSyncItem *itemP;
+ #endif
+ localid_out_t itemLocalID;
+ uInt16 dataCRC;
+ #else
+ localid_out_t itemLocalID;
+ bool itemIsModified;
+ #endif
+
+ // just in case: make sure we don't have a changelog loaded here
+ forgetChangeLog();
+ // no changes detected yet
+ fNumberOfLocalChanges=0;
+ // make sure we have the change log DB open
+ openChangeLog();
+ // - get saved modcount for this database and increment for this preflight
+ fChgLogHeader.modcount+=1;
+ fCurrentModCount=fChgLogHeader.modcount;
+ fChangeLog.setExtraHeaderDirty();
+ PDEBUGBLOCKCOLL("changeLogPreflight");
+ #ifdef SYDEBUG
+ string lsd;
+ StringObjTimestamp(lsd,fPreviousSyncTime);
+ PDEBUGPRINTFX(DBG_ADMIN+DBG_DBAPI,(
+ "changeLogPreflight: %sfCurrentModCount=%ld, fPreviousToRemoteModCount=%ld, fPreviousSyncTime(UTC)=%s",
+ isResuming() ? "RESUMING, " : "",
+ (long)fCurrentModCount,
+ (long)fPreviousToRemoteModCount,
+ lsd.c_str()
+ ));
+ #endif
+ // - save header
+ err=fChangeLog.flushHeader();
+ if (err!=BFE_OK) goto done;
+ // - we don't need the changelog to be updated when all we do is refresh from server
+ if (isRefreshOnly()) goto done; // done ok
+ // - load entire existing changelog into memory
+ numexistinglogentries = fChangeLog.getNumRecords(); // logentries that are already there
+ PDEBUGPRINTFX(DBG_ADMIN+DBG_DBAPI,("changeLogPreflight: at start, changelog has %ld entries",(long)numexistinglogentries));
+ if (numexistinglogentries>0) {
+ // - allocate array for all existing entries (use sysync_malloc because e.g. on PalmOS this uses special funcs to allow > 64k)
+ existingentries = (TChangeLogEntry *)sysync_malloc(sizeof(TChangeLogEntry)*numexistinglogentries);
+ if (!existingentries) { err=BFE_MEMORY; goto done; } // not enough memory
+ // - read all entries
+ fChangeLog.readRecord(0,existingentries,numexistinglogentries);
+ // Mark all not-yet-deleted in the log as delete candidate
+ // (those that still exist will be get the candidate mark removed below)
+ for (logindex=0;logindex<numexistinglogentries;logindex++) {
+ // set as delete candidate if not already marked deleted
+ if (!(existingentries[logindex].flags & chgl_deleted))
+ existingentries[logindex].flags = existingentries[logindex].flags | chgl_delete_candidate; // mark as delete candidate
+ }
+ }
+ // Now update the changelog using CRC checks
+ // loop through entire database
+ #ifndef CHANGEDETECTION_AVAILABLE
+ #ifdef RECORDHASH_FROM_DBAPI
+ foundone=getFirstItemCRC(itemLocalID,dataCRC);
+ #else
+ // - get first
+ foundone=getFirstItem(itemP);
+ #endif
+ #else
+ // - get first info
+ foundone=getFirstItemInfo(itemLocalID,itemIsModified);
+ #endif
+ while (foundone) {
+ // report event to allow progress display, use existing number as approx for total # of items
+ ++seen;
+ #ifdef PROGRESS_EVENTS
+ fSessionP->getSyncAppBase()->NotifyProgressEvent(pev_preparing,getDSConfig(),seen,numexistinglogentries);
+ #endif
+ // process now
+ bool chgentryexists=false; // none found yet
+ // - get local ID
+ localid_t localid;
+ #if !defined(CHANGEDETECTION_AVAILABLE) && !defined(RECORDHASH_FROM_DBAPI)
+ STR_TO_LOCALID(itemP->getLocalID(),localid);
+ #else
+ localid=LOCALID_OUT_TO_IN(itemLocalID);
+ #endif
+ // show item info found in DB
+ #ifdef SYDEBUG
+ string sl;
+ LOCALID_TO_STRING(localid,sl);
+ #ifdef CHANGEDETECTION_AVAILABLE
+ PDEBUGPRINTFX(DBG_ADMIN+DBG_DBAPI,(
+ "changeLogPreflight: item #%ld : localid=%s, database says item %s modified",
+ (long)seen,sl.c_str(),
+ itemIsModified ? "IS" : "is NOT"
+ ));
+ #elif defined(RECORDHASH_FROM_DBAPI)
+ PDEBUGPRINTFX(DBG_ADMIN+DBG_DBAPI+DBG_EXOTIC,("changeLogPreflight: seen=%ld, NOC=%ld : localid=%s, dataCRC=0x%04hX",seen,fNumberOfLocalChanges,sl.c_str(),dataCRC));
+ #else
+ PDEBUGPRINTFX(DBG_ADMIN+DBG_DBAPI+DBG_EXOTIC,("changeLogPreflight: seen=%ld, NOC=%ld : localid=%s",seen,fNumberOfLocalChanges,sl.c_str()));
+ #endif
+ #endif
+ // - search for already existing changelog entry for this uniqueID
+ // (prevent searching those that we have created in this preflight)
+ for (logindex=0; logindex<numexistinglogentries; logindex++) {
+ if (LOCALID_EQUAL(existingentries[logindex].dbrecordid,localid)) {
+ // found
+ chgentryexists=true;
+ // - remove the deletion candidate flag if it was set
+ if (existingentries[logindex].flags & chgl_delete_candidate) {
+ existingentries[logindex].flags &= ~chgl_delete_candidate; // remove candidate flag
+ }
+ // found
+ #ifdef CHANGEDETECTION_AVAILABLE
+ PDEBUGPRINTFX(DBG_ADMIN+DBG_DBAPI+DBG_EXOTIC,(
+ "- found in changelog at index=%ld, flags=0x%02hX, modcount=%ld",
+ (long)logindex,
+ (uInt16)existingentries[logindex].flags,
+ (long)existingentries[logindex].modcount
+ ));
+ #else
+ PDEBUGPRINTFX(DBG_ADMIN+DBG_DBAPI+DBG_EXOTIC,(
+ "- found in changelog at index=%ld, flags=0x%02hX, modcount=%ld, saved CRC=0x%04hX",
+ logindex,
+ (uInt16)existingentries[logindex].flags,
+ existingentries[logindex].modcount,
+ existingentries[logindex].dataCRC
+ ));
+ #endif
+ break;
+ }
+ }
+ // - create new record
+ if (!chgentryexists) {
+ // set unique ID, all flags cleared
+ ASSIGN_LOCALID_TO_FLD(newentry.dbrecordid,localid);
+ newentry.flags=0;
+ // modified now
+ newentry.modcount=fCurrentModCount;
+ #ifndef CHANGEDETECTION_AVAILABLE
+ // no CRC yet
+ newentry.dataCRC=0;
+ #endif
+ PDEBUGPRINTFX(DBG_ADMIN+DBG_DBAPI+DBG_EXOTIC,("- does not yet exist in changelog, created new"));
+ }
+ // now check what to do
+ #if !defined(CHANGEDETECTION_AVAILABLE) && !defined(RECORDHASH_FROM_DBAPI)
+ // - calc CRC on record
+ dataCRC=itemP->getDataCRC(0,true); // start new CRC, do not include eqm_none fields
+ #endif
+ // - check if new or changed
+ if (chgentryexists) {
+ // entry exists, could be changed
+ #ifndef CHANGEDETECTION_AVAILABLE
+ // - check CRC
+ if (existingentries[logindex].dataCRC!=dataCRC) {
+ PDEBUGPRINTFX(DBG_ADMIN+DBG_DBAPI+DBG_EXOTIC,(
+ "- item has changed (current CRC=0x%04hX, old CRC=0x%04hX)",
+ dataCRC,
+ existingentries[logindex].dataCRC
+ ));
+ // has changed since last time checked by preflight (but only those! There might be more items
+ // changed since last sync or resume, but these ALREADY have a modcount in the changelog that
+ // flags them such).
+ // So this is the place to reset chgl_modbysync (which marks items changed by a sync and not from outside)
+ existingentries[logindex].flags &= ~chgl_modbysync; // detecting a real change here cancels the mod-by-sync flag set for sync-added/changed entries
+ // update CRC and modification count
+ existingentries[logindex].dataCRC=dataCRC;
+ existingentries[logindex].modcount=fCurrentModCount; // update modification count
+ // this is a local change for this session
+ fNumberOfLocalChanges++; // for suspend: those that detect a change here were modified AFTER last suspend, so always count them
+ }
+ #else
+ // - check mod date
+ if (itemIsModified) {
+ PDEBUGPRINTFX(DBG_ADMIN+DBG_DBAPI+DBG_EXOTIC,("- item has changed (according to what database says)"));
+ // has changed since last time checked by preflight (but only those! There might be more items
+ // changed since last sync or resume, but these ALREADY have a modcount in the changelog that
+ // flags them such).
+ // So this is the place to reset chgl_modbysync (which marks items changed by a sync and not from outside)
+ existingentries[logindex].flags &= ~chgl_modbysync; // detecting a real change here cancels the mod-by-sync flag set for sync-added/changed entries
+ // update modification count
+ existingentries[logindex].modcount=fCurrentModCount;
+ // this is a local change for this session
+ fNumberOfLocalChanges++; // for suspend: those that detect a change here were modified AFTER last suspend, so always count them
+ }
+ #endif
+ else {
+ // no change detected since last preflight (but still, this could be a change to report to the server)
+ if (isResuming()) {
+ // if resuming - only those count that are marked for resume
+ if (existingentries[logindex].flags & chgl_markedforresume)
+ fNumberOfLocalChanges++; // in resumes: only count those that are actually marked for resume
+ }
+ else {
+ // not resuming - all existing ones count if this is a slow sync,
+ // otherwise, those modified since last to-remote-sync count as well (although not modified since last preflight!)
+ if (fSlowSync || existingentries[logindex].modcount>fPreviousToRemoteModCount) fNumberOfLocalChanges++;
+ }
+ }
+ }
+ else {
+ // entry does not exists, means that this record was added new (since last preflight,
+ // which always means also AFTER last suspend!)
+ #ifndef CHANGEDETECTION_AVAILABLE
+ newentry.dataCRC=dataCRC;
+ #endif
+ PDEBUGPRINTFX(DBG_ADMIN+DBG_DBAPI,("- item was newly added (no entry existed in changelog before)"));
+ /* %%% too much
+ // add it directly to the bin file
+ #ifdef NUMERIC_LOCALIDS
+ DEBUGPRINTFX(DBG_ADMIN+DBG_DBAPI+DBG_EXOTIC,(
+ "new entry %ld : localID=%ld, flags=0x%X, modcount=%ld",
+ fChangeLog.getNumRecords(),
+ newentry.dbrecordid,
+ (int)newentry.flags,
+ newentry.modcount
+ ));
+ #else
+ DEBUGPRINTFX(DBG_ADMIN+DBG_DBAPI+DBG_EXOTIC,(
+ "new entry %ld : localID='%s', flags=0x%X, modcount=%ld",
+ fChangeLog.getNumRecords(),
+ newentry.dbrecordid,
+ (int)newentry.flags,
+ newentry.modcount
+ ));
+ #endif
+ */
+ fChangeLog.newRecord(&newentry);
+ // this is a local change for this session (even for resume - we need to add newly added ones in resume!)
+ fNumberOfLocalChanges++;
+ }
+ #ifndef CHANGEDETECTION_AVAILABLE
+ #ifdef RECORDHASH_FROM_DBAPI
+ foundone=getNextItemCRC(itemLocalID,dataCRC);
+ #else
+ // forget this one
+ delete itemP;
+ // check next
+ foundone=getNextItem(itemP);
+ #endif
+ #else
+ // check next
+ foundone=getNextItemInfo(itemLocalID,itemIsModified);
+ #endif
+ } // while all records in DB
+ // check that we've terminated the list because we've really seen all items and not
+ // because of an error reading an item (which would cause false deletes)
+ if (isDBError(lastDBError())) {
+ sta=510; // database error
+ goto error;
+ }
+ // now find and update delete candidates
+ // (That is, all entries in the log that have no DB record associated any more)
+ // Note: we only search logentries that were here already before the preflight,
+ // because new ones never are delete candidates
+ for (logindex=0;logindex<numexistinglogentries;logindex++) {
+ // check if delete candidate
+ if (existingentries[logindex].flags & chgl_delete_candidate) {
+ // Note: delete candidates are never already chgl_deleted
+ // - update entry
+ existingentries[logindex].flags &= ~chgl_delete_candidate; // no candidate...
+ existingentries[logindex].flags |= chgl_deleted; // ..but really deleted
+ #ifndef CHANGEDETECTION_AVAILABLE
+ existingentries[logindex].dataCRC=0; // no CRC any more
+ #endif
+ existingentries[logindex].modcount=fCurrentModCount; // deletion detected now
+ PDEBUGPRINTFX(DBG_ADMIN+DBG_DBAPI,("changeLogPreflight: item with logindex=%ld was not found in datastore -> mark deleted",(long)logindex));
+ // this is a local change for this session
+ if (!isResuming() || (existingentries[logindex].flags & chgl_markedforresume))
+ fNumberOfLocalChanges++; // in resumes: only count those that are actually marked for resume
+ }
+ }
+ // successfully updated in memory, now write changed entries back to binfile
+ #ifdef SYDEBUG
+ DEBUGPRINTFX(DBG_ADMIN+DBG_DBAPI+DBG_EXOTIC,("changeLogPreflight: saving %ld existing entries",(long)numexistinglogentries));
+ if (DEBUGTEST(DBG_ADMIN+DBG_DBAPI+DBG_EXOTIC)) {
+ for (uInt32 si=0; si<numexistinglogentries; si++) {
+ #ifdef NUMERIC_LOCALIDS
+ DEBUGPRINTFX(DBG_ADMIN+DBG_DBAPI+DBG_EXOTIC,(
+ "%ld : localID=%ld, flags=0x%X, modcount=%ld",
+ si,
+ existingentries[si].dbrecordid,
+ (int)existingentries[si].flags,
+ existingentries[si].modcount
+ ));
+ #else
+ DEBUGPRINTFX(DBG_ADMIN+DBG_DBAPI+DBG_EXOTIC,(
+ "%ld : localID='%s', flags=0x%X, modcount=%ld",
+ (long)si,
+ existingentries[si].dbrecordid,
+ (int)existingentries[si].flags,
+ (long)existingentries[si].modcount
+ ));
+ #endif
+ }
+ }
+ #endif
+ fChangeLog.updateRecord(0,existingentries,numexistinglogentries);
+ aValidChangelog=true;
+ /* %%% not true for resuming session
+ // number of changes are all records if its a slow sync
+ if (fSlowSync) fNumberOfLocalChanges=seen;
+ */
+ DEBUGPRINTFX(DBG_ADMIN+DBG_DBAPI+DBG_EXOTIC,("changeLogPreflight: seen=%ld, fNumberOfLocalChanges=%ld",(long)seen,(long)fNumberOfLocalChanges));
+done:
+ sta = err==BFE_OK ? LOCERR_OK : LOCERR_UNDEFINED;
+error:
+ // release buffered changelog
+ if (existingentries) sysync_free(existingentries);
+ // return state
+ PDEBUGPRINTFX(DBG_ADMIN+DBG_DBAPI,("changeLogPreflight: numberOfLocalChanges=%ld, status=%hd (lastDBError=%ld, binfileerr=%hd)",(long)fNumberOfLocalChanges,sta,(long)lastDBError(),err));
+ PDEBUGENDBLOCK("changeLogPreflight");
+ return sta;
+} // TBinfileImplDS::changeLogPreflight
+
+
+
+// Simple DB access interface methods
+// ==================================
+
+/// sync login (into this database)
+/// @note might be called several times (auth retries at beginning of session)
+/// @note must update the following saved AND current state variables
+/// - in TLocalEngineDS: fLastRemoteAnchor, (fLastLocalAnchor), fResumeAlertCode, fFirstTimeSync
+/// - for client: fPendingAddMaps
+/// - in TStdLogicDS: fPreviousSyncTime, fCurrentSyncTime
+/// - in TBinfileImplDS: ??? /// @todo document these
+/// - in derived classes: whatever else belongs to dsSavedAdmin and dsCurrentAdmin state
+localstatus TBinfileImplDS::implMakeAdminReady(
+ const char *aDeviceID, // remote device URI (device ID)
+ const char *aDatabaseID, // database ID
+ const char *aRemoteDBID // database ID of remote device
+)
+{
+ localstatus sta=LOCERR_OK; // assume ok
+
+ PDEBUGBLOCKDESCCOLL("implMakeAdminReady","Loading target info and pending maps");
+ // - init defaults
+ fLastRemoteAnchor.erase();
+ fPreviousSyncTime=0;
+ fFirstTimeSync=false; // assume not first time
+
+ #if !defined(PRECONFIGURED_SYNCREQUESTS)
+ // for clients without syncrequests in config,
+ // target info must already be present by now (loaded at session's SelectProfile)
+ if (fTargetIndex<0) {
+ PDEBUGENDBLOCK("implMakeAdminReady");
+ return 404; // not found
+ }
+ // we have the target in the fTarget member
+ #else
+ uInt32 remotepartyID = static_cast<TBinfileImplClient *>(fSessionP)->fRemotepartyID;
+ TBinFile *targetsBinFileP = &(static_cast<TBinfileImplClient *>(fSessionP)->fConfigP->fTargetsBinFile);
+ // for server or version with syncrequests in config, we must try to load
+ // the target record or create one if it is missing
+ if (fTargetIndex<0) {
+ // try to find target by fRemotepartyID and targetDB code/name
+ uInt32 maxidx=targetsBinFileP->getNumRecords();
+ uInt32 recidx;
+ for (recidx=0; recidx<maxidx; recidx++) {
+ // - get record
+ if (targetsBinFileP->readRecord(recidx,&fTarget)==BFE_OK) {
+ // check if this is my target
+ if (
+ fTarget.remotepartyID == remotepartyID &&
+ fTarget.localDBTypeID == fConfigP->fLocalDBTypeID &&
+ strucmp(fTarget.localDBPath,fConfigP->fLocalDBPath.c_str(),localDBpathMaxLen)==0
+ ) {
+ // this is the target record for our DB, now get it (mark it busy)
+ fTargetIndex=recidx;
+ break; // leave handle locked
+ }
+ }
+ }
+ }
+ // create new one if none found so far
+ if (fTargetIndex<0) {
+ // create new target record
+ // - init with defaults
+ fConfigP->initTarget(fTarget,remotepartyID,aRemoteDBID,true); // enabled if created here!
+ // - save new record
+ targetsBinFileP->newRecord(fTargetIndex,fTarget);
+ }
+ #endif
+ // Now fTarget has valid target info
+ // - if we don't have any remote anchor stored, this must be a first time sync
+ if (*(fTarget.remoteAnchor)==0)
+ fFirstTimeSync=true;
+ // - get last anchor
+ if (fTarget.forceSlowSync) {
+ // - forget last anchor
+ fLastRemoteAnchor.erase(); // make sure we get a slow sync
+ }
+ else {
+ // - get last anchor
+ fLastRemoteAnchor.assign(fTarget.remoteAnchor);
+ }
+ // - get last resume info
+ fPreviousSuspendModCount = fTarget.lastSuspendModCount; /// @note: before DS 1.2 engine this was used as "lastModCount"
+ fResumeAlertCode = fTarget.resumeAlertCode;
+ // - get last sync time and changelog cursor
+ fPreviousToRemoteModCount = fTarget.lastTwoWayModCount; // reference is when we've last full-synced!
+ fPreviousSyncTime = fTarget.lastSync;
+ #ifdef CHANGEDETECTION_AVAILABLE
+ fPreviousToRemoteSyncCmpRef=fTarget.lastTwoWaySync;
+ #endif
+ #if TARGETS_DB_VERSION>=6
+ // - DB api level change detection identifiers
+ fPreviousToRemoteSyncIdentifier.assign(fTarget.lastSyncIdentifier);
+ fPreviousSuspendIdentifier.assign(fTarget.lastSuspendIdentifier);
+ #endif
+ PDEBUGPRINTFX(DBG_ADMIN+DBG_DBAPI,(
+ "implMakeAdminReady(binfile): ResumeAlertCode=%hd, PreviousSuspendModCount=%ld, PreviousToRemoteModCount=%ld, LastRemoteAnchor='%s'",
+ fResumeAlertCode,
+ (long)fPreviousSuspendModCount,
+ (long)fPreviousToRemoteModCount,
+ fLastRemoteAnchor.c_str()
+ ));
+ // determine time of this sync (will be overridden in case of BASED_ON_BINFILE_CLIENT)
+ fCurrentSyncTime=getSession()->getSystemNowAs(TCTX_UTC); // NOW !
+ // do some more things if we are starting sync now
+ if (!fPreflighted) {
+ if (!openChangeLog()) {
+ // changelog did not exist yet
+ // - force slow sync
+ fTarget.forceSlowSync=true; // set target flag to force slowsync even if we repeat this
+ fLastRemoteAnchor.erase();
+ fPreviousSyncTime=0;
+ fPreviousToRemoteModCount=0;
+ fPreviousSuspendModCount=0;
+ }
+ }
+ // get pending maps anyway (even if not resuming there might be pending maps)
+ if(openPendingMaps()) {
+ // there is a pending map file, check if these are really our maps
+ PDEBUGPRINTFX(DBG_ADMIN+DBG_DBAPI+DBG_EXOTIC,(
+ "implMakeAdminReady: remotePartyID of pendingmaps=%ld, current profile's remotepartyID=%ld",
+ (long)fPendingMapHeader.remotepartyID,
+ (long)static_cast<TBinfileImplClient *>(fSessionP)->fRemotepartyID
+ ));
+ if (fPendingMapHeader.remotepartyID == static_cast<TBinfileImplClient *>(fSessionP)->fRemotepartyID) {
+ // these are our maps, load them
+ TPendingMapEntry pme;
+ string localid;
+ for (uInt32 i=0; i<fPendingMaps.getNumRecords(); i++) {
+ fPendingMaps.readRecord(i,&pme);
+ // store in localEngineDS' list
+ LOCALID_TO_STRING(pme.dbrecordid,localid);
+ fPendingAddMaps[localid]=pme.remoteID;
+ }
+ PDEBUGPRINTFX(DBG_ADMIN+DBG_DBAPI,("implMakeAdminReady: loaded %ld pending add maps",(long)fPendingMaps.getNumRecords()));
+ }
+ }
+ if (fResumeAlertCode) {
+ // get pending item only if we have a resume state
+ // - prep file
+ TBinFile pendingItemFile;
+ TPendingItemHeader pendingItemHeader;
+ string fname;
+ static_cast<TBinfileClientConfig *>(fSessionP->getSessionConfig())->
+ getBinFilesPath(fname);
+ fname += getName();
+ fname += PENDINGITEM_DB_SUFFIX;
+ pendingItemFile.setFileInfo(fname.c_str(),PENDINGITEM_DB_VERSION,PENDINGITEM_DB_ID);
+ PDEBUGPRINTFX(DBG_ADMIN+DBG_DBAPI+DBG_EXOTIC,(
+ "opening pending item file: file name='%s'",
+ fname.c_str()
+ ));
+ if (pendingItemFile.open(sizeof(TPendingItemHeader),&pendingItemHeader)==BFE_OK) {
+ // we have a pending item file
+ if (pendingItemHeader.remotepartyID == static_cast<TBinfileImplClient *>(fSessionP)->fRemotepartyID) {
+ // this is our pending item, load it
+ // - transfer header data
+ fPartialItemState = pendingItemHeader.piState; // note: we always have the pi_state_loaded... state here
+ fLastItemStatus = pendingItemHeader.lastItemStatus;
+ fLastSourceURI = pendingItemHeader.lastSourceURI;
+ fLastTargetURI = pendingItemHeader.lastTargetURI;
+ fPITotalSize = pendingItemHeader.totalSize;
+ fPIUnconfirmedSize = pendingItemHeader.unconfirmedSize;
+ fPIStoredSize = pendingItemHeader.storedSize;
+ // - load the data if any
+ if (fPIStoredDataP && fPIStoredDataAllocated) smlLibFree(fPIStoredDataP);
+ fPIStoredDataP=NULL;
+ fPIStoredDataAllocated=false;
+ if (fPIStoredSize) {
+ fPIStoredDataP=smlLibMalloc(fPIStoredSize+1); // one for safety null terminator
+ if (fPIStoredDataP) {
+ fPIStoredDataAllocated=true;
+ // get the data
+ pendingItemFile.readRecord(0,fPIStoredDataP,1);
+ *((uInt8 *)fPIStoredDataP+fPIStoredSize)=0; // safety null terminator
+ }
+ else {
+ PDEBUGPRINTFX(DBG_ERROR,("Cannot allocate buffer (%ld bytes) for pendingitem",(long)fPIStoredSize));
+ fPIStoredSize=0;
+ }
+ }
+ }
+ PDEBUGPRINTFX(DBG_ADMIN+DBG_DBAPI,("implMakeAdminReady: loaded pending item with %ld data bytes",(long)fPIStoredSize));
+ // done
+ pendingItemFile.close();
+ }
+ }
+ // done
+ PDEBUGENDBLOCK("implMakeAdminReady");
+ return sta;
+} // TBinfileImplDS::implMakeAdminReady
+
+
+localstatus TBinfileImplDS::implStartDataRead()
+{
+ // init reading of all records
+ /// @todo: check if there are other cases where we need all records even if not slow sync - probably with filters only
+ fAllRecords=fSlowSync;
+ // start at beginning of log
+ fLogEntryIndex=0;
+ return LOCERR_OK;
+} // TBinfileImplDS::implStartDataRead
+
+
+#ifdef OBJECT_FILTERING
+
+// Test Filters
+bool TBinfileImplDS::testFilters(TMultiFieldItem *aItemP)
+{
+ return
+ // generally suitable item for this datastore
+ aItemP->testFilter(fLocalDBFilter.c_str()) &&
+ // and passing current sync set filter
+ aItemP->testFilter(fSyncSetFilter.c_str()) &&
+ // and not invisible
+ (
+ getDSConfig()->fInvisibleFilter.empty() || // no invisible filter means visible
+ !aItemP->testFilter(getDSConfig()->fInvisibleFilter.c_str()) // failing invisible filter means visible too
+ );
+} // TBinfileImplDS::testFilters
+
+#endif
+
+
+/// @brief called to have all non-yet-generated sync commands as "to-be-resumed"
+void TBinfileImplDS::implMarkOnlyUngeneratedForResume(void)
+{
+
+ TChangeLogEntry *chglogP;
+
+ // simply return aEof when just refreshing
+ if (fRefreshOnly) return;
+ // make sure we have the changelog in memory
+ loadChangeLog();
+ // check if more records
+ uInt32 logEntryIndex=fLogEntryIndex;
+ DEBUGPRINTFX(DBG_ADMIN+DBG_DBAPI+DBG_EXOTIC,("implMarkOnlyUngeneratedForResume: total=%ld, already generated=%ld",(long)fLoadedChangeLogEntries,(long)logEntryIndex));
+ // Note: already generated entries had their flag cleared when they were generated.
+ // Some of them might already be marked now again by early markItemForResume() due to unsuccessful status
+ // for all remaining records, check if they must be marked or not
+ // (depends on current marked state as well if this is a resumed session already)
+ while (logEntryIndex<fLoadedChangeLogEntries) {
+ bool markforresume=true; // assume we must mark it
+ // get ptr to entry
+ chglogP=&fLoadedChangeLog[logEntryIndex];
+ // advance to next
+ logEntryIndex++;
+ // if resuming, check if we must include this item at all
+ if (isResuming()) {
+ // we are resuming, only report those that have the mark-for-resume flag set
+ if (!(chglogP->flags & chgl_markedforresume)) {
+ // not marked for resume, but check if it has changed after the last suspend
+ if (chglogP->modcount<=fPreviousSuspendModCount) {
+ // is not marked for resume AND has not changed since last suspend
+ markforresume=false; // not to be included in resume
+ }
+ }
+ }
+ if (markforresume) {
+ // this item would have been reported in THIS session
+ // now check if this should be marked for resume for NEXT session
+ if (fAllRecords) {
+ // slow sync mode
+ if (chglogP->flags & chgl_deleted) {
+ // skip deleted in slow sync
+ markforresume=false; // not to be included in resume
+ }
+ }
+ else if (!fAllRecords) {
+ // prevent ANY reporting of items marked as receiveOnly in normal sync (but send them in slow sync!)
+ if (chglogP->flags & chgl_receive_only) {
+ // skip receive-only items in normal sync. So deleting or changing them locally will not send them
+ // to the server. However after a slow sync, existing local items will be send (and possibly added,
+ // if not already there) to the server
+ markforresume=false; // not to be included in resume
+ }
+ else {
+ // mark for resume if change to be reported
+ markforresume=chglogP->modcount>fPreviousToRemoteModCount;
+ }
+ }
+ }
+ // now apply change of chgl_markedforresume to actual changelog entry
+ #ifdef NUMERIC_LOCALIDS
+ DEBUGPRINTFX(DBG_ADMIN+DBG_DBAPI+DBG_EXOTIC,(
+ "implMarkOnlyUngeneratedForResume: localID=%ld new markforresume=%d, old markforresume=%d",
+ (long)chglogP->dbrecordid,
+ (int)markforresume,
+ (int)((chglogP->flags & chgl_markedforresume)!=0)
+ ));
+ #else
+ DEBUGPRINTFX(DBG_ADMIN+DBG_DBAPI+DBG_EXOTIC,(
+ "implMarkOnlyUngeneratedForResume: localID='%s' new markforresume=%d, old markforresume=%d",
+ chglogP->dbrecordid,
+ (int)markforresume,
+ (int)((chglogP->flags & chgl_markedforresume)!=0)
+ ));
+ #endif
+ // update flag
+ if (markforresume)
+ chglogP->flags |= chgl_markedforresume;
+ else
+ chglogP->flags &= ~chgl_markedforresume;
+ // Note: changelog will be saved at SaveAdminData
+ }
+} // TBinfileImplDS::implMarkOnlyUngeneratedForResume
+
+
+// called to mark an already generated (but unsent or sent but not yet statused) item
+// as "to-be-resumed", by localID or remoteID (latter only in server case).
+void TBinfileImplDS::implMarkItemForResume(cAppCharP aLocalID, cAppCharP aRemoteID, bool aUnSent)
+{
+ // make sure we have the changelog in memory
+ loadChangeLog();
+ localid_out_t locID;
+ STR_TO_LOCALID(aLocalID,locID);
+ // search for item by localID
+ uInt32 i;
+ for (i=0; i<fLoadedChangeLogEntries; i++) {
+ if (LOCALID_EQUAL(fLoadedChangeLog[i].dbrecordid,LOCALID_OUT_TO_IN(locID))) {
+ // found - mark it for resume
+ DEBUGPRINTFX(DBG_ADMIN+DBG_DBAPI+DBG_EXOTIC,(
+ "implMarkItemForResume: localID='%s': marking changelog entry for resume=1, old markforresume=%d",
+ aLocalID,
+ (int)((fLoadedChangeLog[i].flags & chgl_markedforresume)!=0)
+ ));
+ fLoadedChangeLog[i].flags |= chgl_markedforresume;
+ break;
+ }
+ }
+ #ifdef SYDEBUG
+ if (i>=fLoadedChangeLogEntries) {
+ DEBUGPRINTFX(DBG_ADMIN+DBG_DBAPI+DBG_EXOTIC,(
+ "implMarkItemForResume: localID='%s': changelog entry not found",
+ aLocalID
+ ))
+ }
+ #endif
+} // TBinfileImplDS::implMarkItemForResume
+
+
+// called to mark an already sent item as "to-be-resent", e.g. due to temporary
+// error status conditions, by localID or remoteID (latter only in server case).
+void TBinfileImplDS::implMarkItemForResend(cAppCharP aLocalID, cAppCharP aRemoteID)
+{
+ // make sure we have the changelog in memory
+ loadChangeLog();
+ localid_out_t locID;
+ STR_TO_LOCALID(aLocalID,locID);
+ // search for item by localID
+ uInt32 i;
+ for (i=0; i<fLoadedChangeLogEntries; i++) {
+ if (LOCALID_EQUAL(fLoadedChangeLog[i].dbrecordid,LOCALID_OUT_TO_IN(locID))) {
+ // found - mark it for resume
+ DEBUGPRINTFX(DBG_ADMIN+DBG_DBAPI+DBG_EXOTIC,(
+ "implMarkItemForResend: localID='%s': marking changelog entry for resend",
+ aLocalID
+ ));
+ fLoadedChangeLog[i].flags |= chgl_resend;
+ break;
+ }
+ }
+ #ifdef SYDEBUG
+ if (i>=fLoadedChangeLogEntries) {
+ DEBUGPRINTFX(DBG_ADMIN+DBG_DBAPI+DBG_EXOTIC,(
+ "implMarkItemForResend: localID='%s': changelog entry not found",
+ aLocalID
+ ))
+ }
+ #endif
+} // TBinfileImplDS::implMarkItemForResend
+
+
+
+
+// Get next item from database
+localstatus TBinfileImplDS::implGetItem(
+ bool &aEof,
+ bool &aChanged,
+ TSyncItem* &aSyncItemP
+)
+{
+ localstatus sta=LOCERR_OK;
+ TSyncItem *myitemP=NULL;
+ TChangeLogEntry *chglogP;
+ bool aOnlyChanged = aChanged;
+
+ aEof=true;
+
+ // Update change log
+ // Note: we cannot do it earlier as we need item types setup correctly
+ // which is not the case before here!
+ if (!fPreflighted) {
+ fPreflighted=true;
+ // find and update (if not refreshing) changelog for this database
+ bool normal=false;
+ sta=changeLogPreflight(normal);
+ if (sta!=LOCERR_OK)
+ return sta; // database error
+ }
+ // simply return aEof when just refreshing
+ if (fRefreshOnly) return LOCERR_OK;
+ // make sure we have the changelog in memory
+ loadChangeLog();
+ do {
+ if (fLogEntryIndex<fLoadedChangeLogEntries) {
+ // there is a log entry, get it
+ chglogP = &fLoadedChangeLog[fLogEntryIndex];
+ // advance to next
+ fLogEntryIndex++;
+ // if resuming, check if we must include this item at all
+ if (isResuming()) {
+ // we are resuming, only report those that have the mark-for-resume flag set
+ if (!(chglogP->flags & chgl_markedforresume)) {
+ // not marked for resume, but check if it has changed after the last suspend
+ // NOTE: this catches those added after last suspend as well, which is IMPORTANT
+ // (otherwise, they would not get detected later, as we do not differentiate
+ // adds and replaces (unlike in server, where new adds will be shown in the
+ // next session after the resumed one)
+ if (chglogP->modcount<=fPreviousSuspendModCount) {
+ // is not marked for resume AND has not changed (or was added) since last suspend
+ continue; // not for resume - check next
+ }
+ else {
+ // New behaviour here from 3.1.5.2 onwards:
+ // SyncML DS 1.2.1 explicitly FORBIDS that changes happening after suspending
+ // are included in a resume. So we have to post-pone these. For that we must artificially
+ // mark them changed such that they will be detected in next non-resume (but not before).
+ // Note that this also affects other profiles as there is only one single changelog -
+ // however, this is not a problem because the records detected new or changed now
+ // will inevitably be new or changed in all other profiles as well. So we can
+ // safely touch these record's modcount w/o any noticeable side effects in other profiles.
+ PDEBUGPRINTFX(DBG_ADMIN+DBG_EXOTIC,("Detected item changed/added after suspend -> postpone reporting it for next non-resumed session"));
+ // - update the mod count such that this record will be detected again in the next non-resumed session
+ // (fCurrentModCount marks entries changed in this session, +1 makes sure these will be detected in the NEXT session)
+ chglogP->modcount=fCurrentModCount+1;
+ // - mark it "modified by sync" to prevent it being sent during resume
+ chglogP->flags |= chgl_modbysync;
+ }
+ }
+ /* NOTE: 2007-12-17, luz: I initially added chgl_modbysync just to prevent that adds from server
+ occurred in a suspended part of the session would be sent back to the
+ server in the resume (as these DO get marked for resume). However, this
+ is not a good strategy because the server cannot know which of its adds
+ were actually successful, so the client MUST send them back to the
+ server so the server can tell which items must be sent (possibly again)
+ and which are definitely already stored in the client.
+ See correspondence with Faisal from Oracle around 2007-12-17.
+ So the chgl_modbysync flag is maintained for now, but not used
+ for anything. */
+ /* NOTE: 2008-01-11, luz: THE ABOVE IS WRONG! The server CAN know which adds were
+ successful: those for which it has received a map in the suspended session
+ or a begin-of-session-map in the current session (or both). So we SHOULD
+ suppress those items that have chgl_modbysync set. */
+ // we are resuming, prevent reporting those back that were added or changed
+ // by one of the suspended earlier parts of this session
+ if (chglogP->flags & chgl_modbysync) {
+ PDEBUGPRINTFX(DBG_ADMIN+DBG_HOT,("Item not sent because it was added/changed by remote in suspended previous session or by user on device after suspend"));
+ continue; // these must NOT be reported back during a resumed sync
+ }
+ }
+ else {
+ // make sure we clear the resume mark on ALL entries, even those not reported
+ // (as the marks are all invalid)
+ chglogP->flags &= ~chgl_markedforresume;
+ // for a non-resumed slow sync, also clear all resend flags
+ if (fAllRecords) chglogP->flags &= ~chgl_resend;
+ }
+ // At this point, the current entry is a candidate for being reported
+ // (not excplicitly excluded)
+ // - now check if and how to report it
+ if (fAllRecords) {
+ // slow sync mode
+ // - skip deleted in slow sync
+ if (chglogP->flags & chgl_deleted) {
+ continue; // check next
+ }
+ }
+ if (!fAllRecords) {
+ // prevent ANY reporting of items marked as receiveOnly in normal sync (but send them in slow sync!)
+ if (chglogP->flags & chgl_receive_only) {
+ // skip receive-only items in normal sync. So deleting or changing them locally will not send them
+ // to the server. However after a slow sync, existing local items will be send (and possibly added,
+ // if not already there) to the server
+ continue;
+ }
+ // mark those as "changed" which have really changed or have the resend flag set
+ bool hasChanged=
+ (chglogP->modcount>fPreviousToRemoteModCount) || // change detected
+ (chglogP->flags & chgl_resend); // or marked for resend e.g. due to error in last session
+ #ifdef SYDEBUG
+ if (chglogP->flags & chgl_resend) {
+ PDEBUGPRINTFX(DBG_ADMIN+DBG_HOT,(
+ "Item treated as changed because resend-flag was set"
+ ));
+ }
+ #endif
+ // clear resend flag now - it is processed
+ chglogP->flags &= ~chgl_resend;
+ // skip unchanged ones if only changed ones are to be reported
+ if (!hasChanged && aOnlyChanged)
+ continue; // unchanged, do not report
+ // always report changed status in aChanged
+ aChanged=hasChanged;
+ }
+ // this entry is to be reported
+ // - now check how to report
+ if (chglogP->flags & chgl_deleted) {
+ // deleted, we cannot get it from the DB, create a empty item
+ myitemP = new TSyncItem();
+ // add ID
+ ASSIGN_LOCALID_TO_ITEM(*myitemP,chglogP->dbrecordid);
+ // deleted, syncop is delete
+ myitemP->setSyncOp(sop_delete);
+ }
+ else {
+ // get contents from the DB
+ myitemP=NULL; // in case getItemByID should abort before pointer assigned
+ sta = getItemByID(chglogP->dbrecordid,myitemP);
+ if ((sta!=LOCERR_OK) || !myitemP) {
+ // error getting record
+ if (sta==404) {
+ // record seems to have vanished between preflight and now
+ PDEBUGPRINTFX(DBG_ERROR,("Record does not exist any more in database (DBErr=%ld) -> ignore",(long)lastDBError()));
+ // simply don't include in sync set - next preflight will detect it deleted
+ if (myitemP) delete myitemP; // delete in case we have some half-filled record here
+ continue; // try next
+ }
+ else {
+ // record does not exist
+ PDEBUGPRINTFX(DBG_ERROR,("Error getting Record from DB (DBErr=%ld) -> Status %hd",(long)lastDBError(),sta));
+ goto error;
+ }
+ }
+ // added or changed, syncop is replace
+ myitemP->setSyncOp(sop_replace);
+ // make sure item has the localid which was used to retrieve it
+ ASSIGN_LOCALID_TO_ITEM(*myitemP,chglogP->dbrecordid);
+ }
+ // - make sure IDs are ok
+ myitemP->clearRemoteID(); // client never has a remote ID
+ // record found
+ // - items that do go out into the engine must not any longer be marked for resume (from this session)
+ // Note: they may get marked by receiving unsuccessful status for them before session ends!
+ chglogP->flags &= ~chgl_markedforresume;
+ DEBUGPRINTFX(DBG_ADMIN+DBG_DBAPI+DBG_EXOTIC,("Reporting localID='%s', flags=0x%x as sop=%s",myitemP->getLocalID(),(int)chglogP->flags,SyncOpNames[myitemP->getSyncOp()]));
+ aEof=false;
+ break;
+ }
+ else {
+ // no more records
+ break;
+ }
+ } while(true); // loop until record found or EOF
+ // set item to return
+ aSyncItemP = myitemP;
+ // ok
+ return LOCERR_OK;
+error:
+ // general database error
+ PDEBUGPRINTFX(DBG_ERROR,("implGetItem error %hd, lastDBError=%ld",sta,(long)lastDBError()));
+ aSyncItemP=NULL;
+ return sta;
+} // TBinfileImplDS::implGetItem
+
+
+// end of read
+localstatus TBinfileImplDS::implEndDataRead(void)
+{
+ // pass it on to the DB api (usually dummy for traditional binfile derivates, but
+ // needed for customimplds)
+ return apiEndDataRead();
+} // TBinfileImplDS::implEndDataRead
+
+
+
+
+/// forget changelog in memory
+void TBinfileImplDS::forgetChangeLog(void)
+{
+ if (fLoadedChangeLog==NULL) return; // ok, already gone
+ PDEBUGPRINTFX(DBG_ADMIN+DBG_DBAPI+DBG_EXOTIC,("forgetChangeLog: DISCARDING already loaded changelog with %ld entries",(long)fLoadedChangeLogEntries));
+ sysync_free(fLoadedChangeLog);
+ fLoadedChangeLog=NULL;
+ fLoadedChangeLogEntries=0;
+} // TBinfileImplDS::forgetChangeLog
+
+
+/// load changelog into memory for quick access
+void TBinfileImplDS::loadChangeLog(void)
+{
+ // if already loaded, simply return
+ if (fLoadedChangeLog) return; // ok, already there
+ // allocate memory for it
+ fLoadedChangeLogEntries=fChangeLog.getNumRecords();
+ if (fLoadedChangeLogEntries>0) {
+ // (use sysync_malloc because e.g. on PalmOS this uses special funcs to allow > 64k)
+ fLoadedChangeLog =
+ (TChangeLogEntry *)sysync_malloc(sizeof(TChangeLogEntry)*fLoadedChangeLogEntries);
+ if (fLoadedChangeLog) {
+ // now load it
+ if (fChangeLog.readRecord(0,fLoadedChangeLog,fLoadedChangeLogEntries)==BFE_OK) {
+ PDEBUGPRINTFX(DBG_ADMIN+DBG_DBAPI+DBG_EXOTIC,("loadChangeLog: loaded changelog with %ld entries",(long)fLoadedChangeLogEntries));
+ }
+ else {
+ // cannot load from file, discard allocated buffer
+ forgetChangeLog();
+ }
+ }
+ else {
+ // not enough memory, cannot load
+ fLoadedChangeLogEntries=0;
+ }
+ }
+} // TBinfileImplDS::loadChangeLog
+
+
+#define ZAP_FORCES_SLOWSYNC 1
+
+// start of write
+localstatus TBinfileImplDS::implStartDataWrite(void)
+{
+ localstatus sta = LOCERR_OK;
+
+ // latest chance to do preflight in case GetItem was never called
+ if (!fPreflighted) {
+ fPreflighted=true;
+ // find and update (if not refreshing) changelog for this database
+ bool normal=false;
+ changeLogPreflight(normal);
+ if (!normal) return false; // failure
+ }
+ // let api layer do it's stuff
+ sta = apiStartDataWrite();
+ if (sta!=LOCERR_OK) return sta; // failed
+ // check if we need to zap the datastore first
+ // Note: only do it if not resuming!
+ if (fSlowSync && fRefreshOnly && !isResuming()) {
+ // yes, zap data
+ sta = zapDatastore();
+ if (sta!=LOCERR_OK) {
+ preventResuming(); // only half-zapped, prevent resuming (we must retry zapping ALL and then do a full slow sync anyway)
+ return sta; // failed
+ }
+ // now, either zap the changelog and set this datastore to slowsync in all
+ // other profiles or flag all entries as deleted during this sync.
+ #ifdef ZAP_FORCES_SLOWSYNC
+ // zap the changelog as well
+ fChangeLog.truncate(0);
+ // zap the anchors in all profiles for this datastore
+ // because we have deleted the changelog. Note that this also resets our own
+ // target's anchor
+ fTarget.remoteAnchor[0]=0; // make sure. If sync completes successfully, this will be updated anyway in SaveAnchor
+ // get target DB
+ TBinFile *targetsBinFileP = &(static_cast<TBinfileImplClient *>(fSessionP)->fConfigP->fTargetsBinFile);
+ TBinfileDBSyncTarget target;
+ // - loop trough all targets
+ for (uInt32 ti=0; ti<targetsBinFileP->getNumRecords(); ti++) {
+ targetsBinFileP->readRecord(ti,&target);
+ if (
+ (target.localDBTypeID == fTarget.localDBTypeID) && // same datastoreID
+ (strucmp(target.localDBPath,fTarget.localDBPath)==0) // ..and name
+ ) {
+ // this is a target of this datastore, remove saved anchor
+ // to irreversibely force a slowsync next time
+ target.remoteAnchor[0]=0;
+ targetsBinFileP->updateRecord(ti,&target);
+ }
+ }
+ #endif
+ }
+ // Load changelog as we need quick access
+ loadChangeLog();
+ #ifndef ZAP_FORCES_SLOWSYNC
+ if (fLoadedChangeLogEntries>0) {
+ if (fSlowSync && fRefreshOnly) {
+ // database was zapped, all existing changelog entries must be set to deleted
+ // by this sync session.
+ for (uInt32 k=0; k<fLoadedChangeLogEntries; k++) {
+ fLoadedChangeLog[k].modcount=fCurrentModCount;
+ fLoadedChangeLog[k].dataCRC=0;
+ fLoadedChangeLog[k].flags=chgl_deleted;
+ }
+ }
+ }
+ #endif
+ // done
+ return sta;
+} // TBinfileImplDS::implStartDataWrite
+
+
+// - retrieve specified item from database
+bool TBinfileImplDS::implRetrieveItemByID(
+ TSyncItem &aItem, // the item
+ TStatusCommand &aStatusCommand
+)
+{
+ // %%% not so nice as we need to copy it once
+ TSyncItem *itemP=NULL;
+ // read item by local ID
+ localid_t localid;
+ localstatus sta;
+ STR_TO_LOCALID(aItem.getLocalID(),localid);
+ // get item now
+ if ((sta=getItemByID(localid,itemP))!=LOCERR_OK) {
+ aStatusCommand.setStatusCode(sta); // report status
+ aStatusCommand.addErrorCodeString(lastDBError());
+ DEBUGPRINTFX(DBG_ERROR,("Item not found in database"));
+ return false;
+ }
+ if (itemP) {
+ // make sure item has the localid which was used to retrieve it
+ ASSIGN_LOCALID_TO_ITEM(*itemP,localid);
+ // and also the same syncop
+ itemP->setSyncOp(aItem.getSyncOp());
+ // now copy retrieved contents to original item
+ aItem = *itemP;
+ delete itemP;
+ }
+ // ok
+ return true;
+} // TBinfileImplDS::implRetrieveItemByID
+
+
+// process single item (but does not consume it)
+bool TBinfileImplDS::implProcessItem(
+ TSyncItem *aItemP, // the item
+ TStatusCommand &aStatusCommand
+)
+{
+ localid_out_t newid;
+ TSyError statuscode;
+ localstatus sta;
+ bool ok;
+ bool receiveOnly=false; // default to normal two-way
+
+ SYSYNC_TRY {
+ // - get op
+ TSyncOperation sop = aItemP->getSyncOp();
+ // - get localid
+ localid_t localid;
+ STR_TO_LOCALID(aItemP->getLocalID(),localid);
+ // - now perform op
+ switch (sop) {
+ // %%% note: sop_copy is now implemented by read/add sequence
+ // in localdatatstore, but will be moved here later eventually
+ case sop_add :
+ // add record
+ if ((sta=createItem(aItemP,newid,receiveOnly))!=LOCERR_OK) {
+ PDEBUGPRINTFX(DBG_ERROR,("cannot create record in database (sta=%hd)",sta));
+ statuscode=sta;
+ goto error; // check errors
+ }
+ // set status
+ statuscode=201; // added
+ // set new localID into item
+ localid = LOCALID_OUT_TO_IN(newid);
+ ASSIGN_LOCALID_TO_ITEM(*aItemP,localid);
+ break;
+ case sop_replace :
+ // change record
+ if ((sta=updateItemByID(localid,aItemP))!=LOCERR_OK) {
+ PDEBUGPRINTFX(DBG_ERROR,("cannot update record in database (sta=%hd)",sta));
+ statuscode=sta;
+ goto error; // check errors
+ }
+ // set status
+ statuscode=200; // replaced
+ break;
+ case sop_delete :
+ // delete record
+ if ((sta=deleteItemByID(localid))!=LOCERR_OK) {
+ PDEBUGPRINTFX(DBG_ERROR,("cannot delete record in database (sta=%hd)",sta));
+ statuscode=sta; // not found
+ goto error; // check errors
+ }
+ // set status
+ statuscode=200;
+ break;
+ default :
+ PDEBUGPRINTFX(DBG_ERROR,("Unknown sync-op in TBinfileImplDS::ProcessItem"));
+ statuscode=501; // not implemented
+ goto error;
+ } // switch(sop)
+ // update changelog
+ #ifndef CHANGEDETECTION_AVAILABLE
+ // - calc new data CRC if not deleted record
+ uInt16 crc=0;
+ if (sop!=sop_delete) {
+ // - get new CRC. Note: to avoid differences in written and readback
+ // data to cause "changed" records, we read the item from the DB again
+ // altough this needs a little extra CPU performance
+ #ifdef RECORDHASH_FROM_DBAPI
+ if (getItemCRCByID(localid,crc)!=LOCERR_OK) {
+ // we don't find the item with that localID.
+ // Probably it has got another localID due to DB-internal reasons
+ // (such as POOM changing a recurring appointment)
+ // - handle this as if it was ok. Next session's preflight will find
+ // that the item is gone and will send a delete to server, and
+ // will also find a new item (this one under new localid) and add
+ // this to the server.
+ crc=0;
+ DEBUGPRINTFX(DBG_ADMIN+DBG_DBAPI,("Item has probably changed it's localid during replace, CRC gets invalid"));
+ }
+ #else
+ TSyncItem *readbackItemP=NULL;
+ if (getItemByID(localid,readbackItemP)!=LOCERR_OK) {
+ // we don't find the item with that localID.
+ // Probably it has got another localID due to DB-internal reasons
+ // (such as POOM changing a recurring appointment)
+ // - handle this as if it was ok. Next session's preflight will find
+ // that the item is gone and will send a delete to server, and
+ // will also find a new item (this one under new localid) and add
+ // this to the server.
+ crc=0;
+ DEBUGPRINTFX(DBG_ADMIN+DBG_DBAPI,("Item has probably changed it's localid during replace, CRC gets invalid"));
+ }
+ else {
+ // Note: we don't need to set localID in item as it is not used in the CRC
+ crc = readbackItemP->getDataCRC(0,true); // start new CRC, do not include eqm_none fields
+ }
+ if (readbackItemP) delete readbackItemP;
+ #endif
+ }
+ #endif
+ // - search changelog entry
+ sInt32 logindex=-1;
+ TChangeLogEntry newentry;
+ TChangeLogEntry *affectedentryP = &newentry;
+ memset(&newentry, 0, sizeof(newentry));
+ for (uInt32 i=0; i<fLoadedChangeLogEntries; i++) {
+ if (LOCALID_EQUAL(fLoadedChangeLog[i].dbrecordid,localid)) {
+ logindex=i;
+ affectedentryP=&(fLoadedChangeLog[i]);
+ break;
+ }
+ }
+ // now affectedentryP points to where we need to apply the changed crc and modcount
+ // if logindex<0 we need to add the entry to the dbfile afterwards
+ #ifndef CHANGEDETECTION_AVAILABLE
+ affectedentryP->dataCRC=crc;
+ #endif
+ affectedentryP->modcount=fCurrentModCount;
+ if (sop==sop_delete)
+ affectedentryP->flags |= chgl_deleted;
+ // set receiveOnly flag if needed. Note that this flag, once set, is never deleted (so item stays write only)
+ if (receiveOnly)
+ affectedentryP->flags |= chgl_receive_only;
+ // set special flag which prevents that this change gets sent back in a resume as a
+ // "modified or added after last suspend" type record (which it technically is, but we
+ // use the flag to prevent that it is sent back if this session is suspended and resumed later.
+ // Note that real adds and changes happening during suspend will also get this flag set
+ // (but also receive a modcount>fCurrentModCount that makes sure these will be reported in
+ // the next non-resumed session).
+ affectedentryP->flags |= chgl_modbysync;
+ // add to changelog DB if needed
+ if (logindex<0) {
+ // new added item
+ ASSIGN_LOCALID_TO_FLD(affectedentryP->dbrecordid,localid);
+ // save it
+ #ifdef NUMERIC_LOCALIDS
+ DEBUGPRINTFX(DBG_ADMIN+DBG_DBAPI+DBG_EXOTIC,(
+ "new entry %ld : localID=%ld, flags=0x%X, modcount=%ld",
+ fChangeLog.getNumRecords(),
+ affectedentryP->dbrecordid,
+ (int)affectedentryP->flags,
+ affectedentryP->modcount
+ ));
+ #else
+ DEBUGPRINTFX(DBG_ADMIN+DBG_DBAPI+DBG_EXOTIC,(
+ "new entry %ld : localID='%s', flags=0x%X, modcount=%ld",
+ (long)fChangeLog.getNumRecords(),
+ affectedentryP->dbrecordid,
+ (int)affectedentryP->flags,
+ (long)affectedentryP->modcount
+ ));
+ #endif
+ // Note: changeLogPostflight() will check these for temporary localids and finalize them when needed
+ fChangeLog.newRecord(affectedentryP);
+ }
+ // done
+ ok=true;
+ goto done;
+ }
+ SYSYNC_CATCH (exception &e)
+ statuscode=500;
+ DEBUGPRINTFX(DBG_ERROR,("******** Exception in TBinfileImplDS::ProcessItem: %s (status=%ld)",e.what(), (long)statuscode));
+ ok=false;
+ goto done;
+ SYSYNC_ENDCATCH
+ SYSYNC_CATCH (...)
+ DEBUGPRINTFX(DBG_ERROR,("******** Exception in TBinfileImplDS::ProcessItem"));
+ statuscode=500;
+ ok=false;
+ goto done;
+ SYSYNC_ENDCATCH
+error:
+ // report OS specific error codes as item text back to the originator
+ ok=false;
+ PDEBUGPRINTFX(DBG_ERROR,("Database Error --> SyncML status %ld, dberr=%ld",(long)statuscode,(long)lastDBError()));
+ aStatusCommand.addErrorCodeString(lastDBError());
+done:
+ aStatusCommand.setStatusCode(statuscode);
+ return ok;
+} // TBinfileImplDS::implProcessItem
+
+
+// called to confirm a sync operation's completion (status from remote received)
+// @note aSyncOp passed not necessarily reflects what was sent to remote, but what actually happened
+void TBinfileImplDS::dsConfirmItemOp(TSyncOperation aSyncOp, cAppCharP aLocalID, cAppCharP aRemoteID, bool aSuccess, localstatus aErrorStatus)
+{
+ // Nothing to do here, even successful deletes must not delete changelog entry (this will be done
+ // by changeLogPostFlight() for those enties that have been reported as deleted in all profiles!)
+ // - let inherited know as well
+ inherited::dsConfirmItemOp(aSyncOp, aLocalID, aRemoteID, aSuccess, aErrorStatus);
+} // TBinfileImplDS::confirmItemOp
+
+
+// - save status information required to eventually perform a resume (as passed to datastore with
+// implMarkOnlyUngeneratedForResume() and implMarkItemForResume())
+// (or, in case the session is really complete, make sure that no resume state is left)
+localstatus TBinfileImplDS::implSaveResumeMarks(void)
+{
+ // update modcount reference of last suspend
+ fPreviousSuspendModCount = fCurrentModCount;
+ // save admin data now
+ return SaveAdminData(true,false); // end of session, but not successful
+} // TBinfileImplDS::implSaveResumeMarks
+
+
+
+// log datastore sync result
+// - Called at end of sync with this datastore
+void TBinfileImplDS::dsLogSyncResult(void)
+{
+ TBinfileClientConfig *clientCfgP = static_cast<TBinfileClientConfig *>(fSessionP->getSessionConfig());
+ if (clientCfgP->fBinFileLog) {
+ // writing binfile logs enabled
+ TBinFile logFile;
+ // Open logfile
+ // - get base path
+ string filepath;
+ clientCfgP->getBinFilesPath(filepath);
+ filepath += LOGFILE_DB_NAME;
+ // - open or create
+ logFile.setFileInfo(filepath.c_str(),LOGFILE_DB_VERSION,LOGFILE_DB_ID);
+ if (logFile.open(0,NULL,NULL)!=BFE_OK) {
+ // create new one or overwrite incompatible one
+ logFile.create(sizeof(TLogFileEntry),0,NULL,true);
+ }
+ // append new record
+ // - create record
+ TLogFileEntry logInfo;
+ logInfo.time = fCurrentSyncTime; // current sync's time
+ logInfo.status = fAbortStatusCode; // reason for abort (0 if ok)
+ logInfo.mode =
+ (fSlowSync ? (fFirstTimeSync ? 2 : 1) : 0) +
+ (fResuming ? 10 : 0);
+ logInfo.dirmode = fSyncMode; // sync direction mode
+ logInfo.infoID = 0; // none
+ logInfo.dbID = fConfigP->fLocalDBTypeID; // ID of DB (to allow fetching name)
+ logInfo.profileID = static_cast<TBinfileImplClient *>(fSessionP)->fRemotepartyID; // ID of profile (to allow fetching name)
+ logInfo.locAdded = fLocalItemsAdded;
+ logInfo.locUpdated = fLocalItemsUpdated;
+ logInfo.locDeleted = fLocalItemsDeleted;
+ logInfo.remAdded = fRemoteItemsAdded;
+ logInfo.remUpdated = fRemoteItemsUpdated;
+ logInfo.remDeleted = fRemoteItemsDeleted;
+ logInfo.inBytes = fIncomingDataBytes;
+ logInfo.outBytes = fOutgoingDataBytes;
+ logInfo.locRejected = fLocalItemsError;
+ logInfo.remRejected = fRemoteItemsError;
+ // - save it
+ logFile.newRecord(&logInfo);
+ // close the logfile again
+ logFile.close();
+ }
+} // TBinfileImplDS::dsLogSyncResult
+
+
+// save end of session state
+// Note that in BASED_ON_BINFILE_CLIENT case, this is derived in customimplds and will
+// be called by the derivate after doing customimpl specific stuff.
+localstatus TBinfileImplDS::implSaveEndOfSession(bool aUpdateAnchors)
+{
+ // update TCustomImplDS dsSavedAdmin variables (other levels have already updated their variables
+ if (aUpdateAnchors) {
+ if (!fRefreshOnly || fSlowSync) {
+ // This was really a two-way sync or we implicitly know that
+ // we are now in sync with remote (like after one-way-from-remote refresh = reload local)
+ #if defined(CHANGEDETECTION_AVAILABLE) && defined(SYNCTIME_IS_ENDOFSESSION)
+ fPreviousToRemoteSyncCmpRef = getSystemNowAs(TCTX_UTC); // NOW ! (again);
+ #else
+ fPreviousToRemoteSyncCmpRef = fCurrentSyncTime;
+ #endif
+ fPreviousToRemoteModCount = fCurrentModCount;
+ }
+ // updating anchor means invalidating last Suspend
+ fPreviousSuspendCmpRef = fPreviousToRemoteSyncCmpRef; // setting to current reference can do less harm than setting it to zero
+ fPreviousSuspendModCount=0;
+ }
+ // save admin data now
+ localstatus sta=SaveAdminData(true,aUpdateAnchors); // end of session
+ if (sta==LOCERR_OK) {
+ // finalize admin data stuff now
+ TBinFile *targetsBinFileP = &(static_cast<TBinfileImplClient *>(fSessionP)->fConfigP->fTargetsBinFile);
+ // do a postFlight to remove unused entries from the changelog
+ // - find oldest sync modcount
+ uInt32 maxidx = targetsBinFileP->getNumRecords();
+ uInt32 idx;
+ uInt32 oldestmodcount=0xFFFFFFFF;
+ TBinfileDBSyncTarget target;
+ for (idx=0; idx<maxidx; idx++) {
+ targetsBinFileP->readRecord(idx,&target);
+ if (
+ // Note: %%% this is same mechanism as for changelog filenames
+ // Not ok if multiple databases go with the same datastore.getName()
+ strucmp(target.dbname,getName())==0
+ ) {
+ // target for this database found, check if modcount is older
+ if (target.lastSuspendModCount!=0 && target.lastSuspendModCount<oldestmodcount)
+ oldestmodcount=target.lastSuspendModCount; // older one found
+ if (target.lastTwoWayModCount!=0 && target.lastTwoWayModCount<oldestmodcount)
+ oldestmodcount=target.lastTwoWayModCount; // older one found
+ }
+ }
+ // Now do cleanup of all deleted entries that are same age or older as oldest target
+ sta=changeLogPostflight(oldestmodcount);
+ }
+ // done
+ return sta;
+} // TBinfileImplDS::implSaveEndOfSession
+
+
+// - end write with commit
+bool TBinfileImplDS::implEndDataWrite(void)
+{
+ // Call apiEndDataWrite variant which is possibly implemented in
+ // datastores which were designed as direct derivates of binfileds.
+ // Note that in BASED_ON_BINFILE_CLIENT case, implEndDataWrite() is
+ // derived by customimplds and will call the other apiEndDataWrite(cmpRef)
+ // variant, and then call this inherited method.
+ return apiEndDataWrite()!=LOCERR_OK;
+} // TBinfileImplDS::implEndDataWrite
+
+
+
+// private helper to prepare for apiSaveAdminData()
+localstatus TBinfileImplDS::SaveAdminData(bool aSessionFinished, bool aSuccessful)
+{
+ PDEBUGBLOCKDESCCOLL("SaveAdminData","Saving changelog, target and map info");
+ // save and free cached changelog anyway
+ if (fLoadedChangeLog) {
+ #ifdef SYDEBUG
+ PDEBUGPRINTFX(DBG_ADMIN+DBG_DBAPI+DBG_EXOTIC,("SaveAdminData: saving changelog with %ld entries",(long)fLoadedChangeLogEntries));
+ if (DEBUGTEST(DBG_ADMIN+DBG_DBAPI+DBG_EXOTIC)) {
+ for (uInt32 si=0; si<fLoadedChangeLogEntries; si++) {
+ #ifdef NUMERIC_LOCALIDS
+ PDEBUGPRINTFX(DBG_ADMIN+DBG_DBAPI+DBG_EXOTIC,(
+ "%ld : localID=%ld, flags=0x%X, modcount=%ld",
+ si,
+ fLoadedChangeLog[si].dbrecordid,
+ (int)fLoadedChangeLog[si].flags,
+ fLoadedChangeLog[si].modcount
+ ));
+ #else
+ PDEBUGPRINTFX(DBG_ADMIN+DBG_DBAPI+DBG_EXOTIC,(
+ "%ld : localID='%s', flags=0x%X, modcount=%ld",
+ (long)si,
+ fLoadedChangeLog[si].dbrecordid,
+ (int)fLoadedChangeLog[si].flags,
+ (long)fLoadedChangeLog[si].modcount
+ ));
+ #endif
+ }
+ }
+ #endif
+ fChangeLog.updateRecord(0,fLoadedChangeLog,fLoadedChangeLogEntries);
+ forgetChangeLog();
+ }
+ // save other admin data
+ TBinFile *targetsBinFileP = &(static_cast<TBinfileImplClient *>(fSessionP)->fConfigP->fTargetsBinFile);
+ // update target fields
+ // - update anchor
+ AssignCString(fTarget.remoteAnchor,fLastRemoteAnchor.c_str(),remoteAnchorMaxLen);
+ PDEBUGPRINTFX(DBG_ADMIN+DBG_DBAPI,("SaveAdminData: saving remote anchor = '%s'",fLastRemoteAnchor.c_str()));
+ // - update this sync time (last sync field in DB) and modcount
+ fTarget.lastSync=fPreviousSyncTime;
+ fTarget.lastTwoWayModCount=fPreviousToRemoteModCount; // not just refreshing, remote has all our data
+ #ifdef CHANGEDETECTION_AVAILABLE
+ fTarget.lastTwoWaySync=fPreviousToRemoteSyncCmpRef;
+ #endif
+ #if TARGETS_DB_VERSION>=6
+ AssignCString(fTarget.lastSyncIdentifier,fPreviousToRemoteSyncIdentifier.c_str(),remoteAnchorMaxLen);
+ AssignCString(fTarget.lastSuspendIdentifier,fPreviousSuspendIdentifier.c_str(),remoteAnchorMaxLen);
+ // store remote datastore's display name (is empty if we haven't got one from the remote via devInf)
+ if (getRemoteDatastore()) {
+ AssignCString(fTarget.remoteDBdispName,getRemoteDatastore()->getDisplayName(),dispNameMaxLen);
+ }
+ #endif
+ PDEBUGPRINTFX(DBG_ADMIN+DBG_DBAPI,("SaveAdminData: lastTwoWayModCount = %ld, lastTwoWaySync = " PRINTF_LLD,(long)fPreviousToRemoteModCount,PRINTF_LLD_ARG(fPreviousToRemoteSyncCmpRef)));
+ // check for other target record updates needed at end of session
+ if (aSessionFinished && aSuccessful) {
+ // - reset mode back to normal, that is after a forced reload of client OR server
+ // we assume that two-way sync will be what we want next.
+ if (fTarget.forceSlowSync) {
+ fTarget.syncmode = smo_twoway;
+ }
+ fTarget.forceSlowSync=false;
+ }
+ // special operations needed depending on suspend state
+ fTarget.resumeAlertCode=fResumeAlertCode;
+ if (fResumeAlertCode==0) {
+ fTarget.lastSuspendModCount=0;
+ }
+ else {
+ /// @note: lastSuspendModCount is the same target field that previously was called "lastModCount"
+ fTarget.lastSuspendModCount=fPreviousSuspendModCount;
+ // make sure that resume alert codes of all other profile's targets for this datastore are erased
+ // (because we have only a single changelog (markforresume flags!) and single pendingmap+pendingitem files)
+ TBinfileDBSyncTarget otherTarget;
+ for (sInt32 ti=0; ti<sInt32(targetsBinFileP->getNumRecords()); ti++) {
+ if (ti!=fTargetIndex) {
+ // get that target
+ targetsBinFileP->readRecord(ti,&otherTarget);
+ if (
+ (otherTarget.localDBTypeID == fTarget.localDBTypeID) && // same datastoreID
+ (strucmp(otherTarget.localDBPath,fTarget.localDBPath)==0) && // ..and name
+ (otherTarget.resumeAlertCode!=0) // ..and has a saved suspend state
+ ) {
+ // same datastore, but different profile is also suspended -> new suspend cancels old one
+ DEBUGPRINTFX(DBG_ADMIN+DBG_DBAPI+DBG_EXOTIC,("SaveAdminData: cancelled resumeAlertCode in profile=%ld",(long)otherTarget.remotepartyID));
+ otherTarget.resumeAlertCode=0;
+ otherTarget.lastSuspendModCount=0;
+ targetsBinFileP->updateRecord(ti,&otherTarget);
+ }
+ }
+ }
+ }
+ // save pending maps (anyway, even if not suspended)
+ openPendingMaps(); // make sure we have the pendingmap file open now
+ fPendingMaps.truncate(0); // delete current contents
+ TStringToStringMap::iterator spos;
+ TPendingMapEntry pme;
+ localid_t localid;
+ // - pending maps now belong to us!
+ fPendingMapHeader.remotepartyID = static_cast<TBinfileImplClient *>(fSessionP)->fRemotepartyID;
+ fPendingMaps.setExtraHeaderDirty();
+ // - now pending maps (unsent ones)
+ DEBUGPRINTFX(DBG_ADMIN+DBG_DBAPI+DBG_DETAILS,("SaveAdminData: saving %ld entries from fPendingAddMap to fPendingMaps binfile",(long)fPendingAddMaps.size()));
+ for (spos=fPendingAddMaps.begin();spos!=fPendingAddMaps.end();spos++) {
+ string locID = (*spos).first;
+ dsFinalizeLocalID(locID); // pending maps might have non-final ID, so give datastore implementation to return finalized version
+ STR_TO_LOCALID(locID.c_str(),localid); ASSIGN_LOCALID_TO_FLD(pme.dbrecordid,localid);
+ AssignCString(pme.remoteID,(*spos).second.c_str(),BINFILE_MAXGUIDSIZE+1);
+ fPendingMaps.newRecord(&pme);
+ }
+ // - now pending maps (sent, but not seen status yet)
+ DEBUGPRINTFX(DBG_ADMIN+DBG_DBAPI+DBG_DETAILS,("SaveAdminData: saving %ld entries from fUnconfirmedMaps to fPendingMaps binfile",(long)fUnconfirmedMaps.size()));
+ for (spos=fUnconfirmedMaps.begin();spos!=fUnconfirmedMaps.end();spos++) {
+ STR_TO_LOCALID((*spos).first.c_str(),localid); ASSIGN_LOCALID_TO_FLD(pme.dbrecordid,localid);
+ AssignCString(pme.remoteID,(*spos).second.c_str(),BINFILE_MAXGUIDSIZE+1);
+ fPendingMaps.newRecord(&pme);
+ }
+ // - save them
+ fPendingMaps.close();
+ // Save last item info and eventually partial item data
+ TPendingItemHeader pendingItemHeader;
+ memset((void*)&pendingItemHeader,0,sizeof(pendingItemHeader)); // make nicely empty by default
+ bool saveit=false;
+ // - profile ID where this pending item belongs to
+ pendingItemHeader.remotepartyID = static_cast<TBinfileImplClient *>(fSessionP)->fRemotepartyID;
+ // determine if and what to save
+ if (fPartialItemState==pi_state_none) {
+ pendingItemHeader.piState = pi_state_none;
+ pendingItemHeader.storedSize=0; // no data to store
+ saveit=true;
+ }
+ else if (fPartialItemState==pi_state_save_incoming || fPartialItemState==pi_state_save_outgoing) {
+ // - last item info
+ pendingItemHeader.lastItemStatus = fLastItemStatus;
+ AssignCString(pendingItemHeader.lastSourceURI,fLastSourceURI.c_str(),BINFILE_MAXGUIDSIZE+1);
+ AssignCString(pendingItemHeader.lastTargetURI,fLastTargetURI.c_str(),BINFILE_MAXGUIDSIZE+1);
+ // - partial item info
+ pendingItemHeader.totalSize = fPITotalSize;
+ pendingItemHeader.unconfirmedSize = fPIUnconfirmedSize;
+ if (fPartialItemState==pi_state_save_incoming) {
+ // store incoming
+ pendingItemHeader.piState = pi_state_loaded_incoming;
+ pendingItemHeader.storedSize=fPIStoredSize;
+ }
+ else if (fPartialItemState==pi_state_save_outgoing) {
+ // store outgoing
+ pendingItemHeader.piState = pi_state_loaded_outgoing;
+ pendingItemHeader.storedSize=fPIStoredSize;
+ }
+ else
+ pendingItemHeader.storedSize=0; // nothing to store
+ saveit=true;
+ }
+ if (saveit) {
+ TBinFile pendingItemFile;
+ string fname;
+ static_cast<TBinfileClientConfig *>(fSessionP->getSessionConfig())->
+ getBinFilesPath(fname);
+ fname += getName();
+ fname += PENDINGITEM_DB_SUFFIX;
+ pendingItemFile.setFileInfo(fname.c_str(),PENDINGITEM_DB_VERSION,PENDINGITEM_DB_ID);
+ PDEBUGPRINTFX(DBG_ADMIN+DBG_DBAPI+DBG_EXOTIC,(
+ "SaveAdminData: creating pending item file: file name='%s', storing %ld bytes",
+ fname.c_str(),
+ (long)pendingItemHeader.storedSize
+ ));
+ PDEBUGPRINTFX(DBG_ADMIN+DBG_DBAPI+DBG_DETAILS,(
+ "SaveAdminData: saved pending item: src='%s', targ='%s', laststatus=%hd, pistate=%hd, total=%ld, unconfirmed=%ld, stored=%ld",
+ pendingItemHeader.lastSourceURI,
+ pendingItemHeader.lastTargetURI,
+ pendingItemHeader.lastItemStatus,
+ pendingItemHeader.piState,
+ (long)pendingItemHeader.totalSize,
+ (long)pendingItemHeader.unconfirmedSize,
+ (long)pendingItemHeader.storedSize
+ ));
+ bferr bfe=pendingItemFile.create(
+ pendingItemHeader.storedSize, // record size = size of data chunk to be buffered
+ sizeof(TPendingItemHeader), // extra header size
+ &pendingItemHeader, // extra header data
+ true // overwrite existing
+ );
+ if (bfe==BFE_OK) {
+ // created successfully, store data if any
+ if (pendingItemHeader.storedSize && fPIStoredDataP) {
+ // we have data to store
+ uInt32 newIndex;
+ bfe=pendingItemFile.newRecord(newIndex,fPIStoredDataP);
+ }
+ // close file
+ pendingItemFile.close();
+ }
+ if (bfe!=BFE_OK) {
+ PDEBUGPRINTFX(DBG_ERROR,("Error writing pending item file, bferr=%hd",bfe));
+ }
+ }
+ PDEBUGPRINTFX(DBG_ADMIN+DBG_DBAPI+DBG_DETAILS,("SaveAdminData: resumeAlertCode = %hd, lastSuspendModCount = %ld",fResumeAlertCode,(long)fTarget.lastSuspendModCount));
+ // update the target record
+ if (fTargetIndex>=0) {
+ targetsBinFileP->updateRecord(fTargetIndex,&fTarget);
+ fTargetIndex=-1; // invalid now
+ }
+ PDEBUGENDBLOCK("SaveAdminData");
+ // ok
+ return LOCERR_OK;
+} // TBinfileImplDS::SaveAdminData
+
+
+/* end of TBinfileImplDS implementation */
+
+} // namespace sysync
+// eof
diff --git a/src/sysync/binfileimplds.h b/src/sysync/binfileimplds.h
new file mode 100755
index 0000000..840fb4e
--- /dev/null
+++ b/src/sysync/binfileimplds.h
@@ -0,0 +1,935 @@
+/**
+ * @File binfileimplds.h
+ *
+ * @Author Lukas Zeller (luz@synthesis.ch)
+ *
+ * @brief TBinfileImplDS
+ * Represents a client datastore implementation which has target management
+ * (and optionally change log) based on binary files
+ *
+ * Copyright (c) 2003-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ * @Date 2005-09-30 : luz : created from TBinfileImplDS
+ */
+/*
+ */
+
+
+#ifndef BINFILEIMPLDS_H
+#define BINFILEIMPLDS_H
+
+// includes
+#include "binfile.h" // platform specific header (using binfilebase)
+
+#include "stdlogicds.h"
+#include "multifielditem.h"
+
+// defines that allow using the generic method implementations
+// for provisioning and autosync
+#define CLIENTAGENTCONFIG TBinfileClientConfig
+#define CLIENTDSCONFIG TBinfileDSConfig
+#define PROFILERECORD TBinfileDBSyncProfile
+#define TARGETRECORD TBinfileDBSyncTarget
+
+#include "clientprovisioning_inc.h"
+#include "clientautosync_inc.h"
+
+
+namespace sysync {
+
+// Defines
+// =======
+
+#pragma pack(push,4) // 32bit
+
+// specific sync
+const uInt16 dbnamelen=32;
+
+// Local IDs
+// =========
+
+// local ID typedefs and Macros
+#ifdef NUMERIC_LOCALIDS
+ // numeric local IDs
+ typedef NUMERIC_LOCALIDS localid_t;
+ typedef localid_t localid_out_t;
+ #define LOCALID_EQUAL(a,b) (a==b)
+ #if NUMERIC_LOCALID_SZ==2
+ #define STR_TO_LOCALID(s,i) StrToUShort(s,i)
+ #elif NUMERIC_LOCALID_SZ==4
+ #define STR_TO_LOCALID(s,i) StrToULong(s,i)
+ #elif NUMERIC_LOCALID_SZ==8
+ #define STR_TO_LOCALID(s,i) StrToULongLong(s,i)
+ #else
+ #error "unknown size of numeric local ID"
+ #endif
+ #define ASSIGN_LOCALID_TO_FLD(a,b) (a=b)
+ #define LOCALID_OUT_TO_IN(out) out
+ #if NUMERIC_LOCALID_SZ<=4
+ #define LOCALID_TO_STRING(i,s) { StringObjPrintf(s,"%lu",(uInt32)i); }
+ #else
+ #define LOCALID_TO_STRING(i,s) { StringObjPrintf(s,"%llu",(uInt64)i); }
+ #endif
+ #define ASSIGN_LOCALID_TO_ITEM(it,i) { string s; LOCALID_TO_STRING(i,s); (it).setLocalID(s.c_str()); }
+#else
+ // string local IDs
+ const uInt16 maxidlen = STRING_LOCALID_MAXLEN;
+ typedef char *localid_t;
+ typedef string localid_out_t;
+ #define LOCALID_EQUAL(a,b) (strnncmp(a,b,maxidlen)==0)
+ #define STR_TO_LOCALID(s,i) (i=(char *)s)
+ #define LOCALID_TO_STRING(i,s) s=i
+ #define ASSIGN_LOCALID_TO_FLD(a,b) AssignCString(a,b,maxidlen)
+ #define LOCALID_OUT_TO_IN(out) ((char *)out.c_str())
+ #define ASSIGN_LOCALID_TO_ITEM(it,i) (it).setLocalID(i)
+#endif
+
+
+// User log
+// ========
+
+// Note: Engine 3.1.5.1 and later have the fBinFileLog which can be turned on to have binfileds
+// write the log records automatically. With ENGINEINTERFACE_SUPPORT, the /synclogs
+// settingskey can be used to access the log records.
+// In 3.0 clients, implementation of filling and displaying the log binfile is platform dependent
+// simply using this typedef to have a common format.
+
+#ifdef SETTINGS_BFI_PREFIX
+ // custom settings name prefix
+ #define LOGFILE_DB_NAME SETTINGS_BFI_PREFIX "log.bfi"
+#else
+ #define LOGFILE_DB_NAME "sysylog.bfi"
+#endif
+#define LOGFILE_DB_ID 8
+#define LOGFILE_DB_VERSION 3
+
+typedef struct {
+ lineartime_t time; ///< timestamp for this log entry
+ uInt32 dbID; ///< the datastore involved
+ uInt32 profileID; ///< id of the profile
+ localstatus status;
+ uInt16 mode; ///< sync mode: 0=normal, 1=slow, 2=first time, +10 = resumed
+ sInt32 infoID; ///< ressource id of text describing error (only valid with platform-specific log file filling, not available via /synclogs)
+ uInt32 locAdded;
+ uInt32 locUpdated;
+ uInt32 locDeleted;
+ uInt32 remAdded;
+ uInt32 remUpdated;
+ uInt32 remDeleted;
+ uInt32 inBytes;
+ uInt32 outBytes;
+ // V2 fields start here
+ uInt32 locRejected;
+ uInt32 remRejected;
+ // V3 fields start here
+ uInt16 dirmode; /// <sync direction: 0=twoway, 1=from server, 2=from client (According to TSyncModes)
+} TLogFileEntry;
+
+
+
+// Changelog
+// =========
+#ifdef CHECKSUM_CHANGELOG
+
+// Changelog file name
+#ifdef SETTINGS_BFI_PREFIX
+ // custom settings name prefix
+ #define CHANGELOG_DB_SUFFIX "_clg_" SETTINGS_BFI_PREFIX ".bfi"
+#else
+ #define CHANGELOG_DB_SUFFIX "_clg.bfi"
+#endif
+#define CHANGELOG_DB_ID 4
+#define CHANGELOG_DB_VERSION 2
+
+// changelog flag bits
+const uInt8 chgl_deleted=0x80;
+const uInt8 chgl_delete_candidate=0x40;
+const uInt8 chgl_receive_only=0x20;
+const uInt8 chgl_markedforresume=0x10;
+const uInt8 chgl_resend=0x08;
+const uInt8 chgl_modbysync=0x04;
+//%%%never used, NOW CONFLICTS WITH chgl_resend and chgl_noresume: const uInt8 chgl_category_mask=0x0F;
+
+// single changelog entry
+
+typedef struct {
+ // UniqueID of the database record
+ #ifdef NUMERIC_LOCALIDS
+ localid_t dbrecordid;
+ #else
+ char dbrecordid[maxidlen];
+ #endif
+ // modification count of last modification
+ uInt32 modcount;
+ #ifndef CHANGEDETECTION_AVAILABLE
+ // CRC of the record's data after the last modification
+ uInt16 dataCRC;
+ #endif
+ // flags
+ uInt8 flags;
+ uInt8 dummy;
+} TChangeLogEntry;
+
+
+// changelog header for one database
+typedef struct {
+ // modification count source (will be used to mark modified records)
+ uInt32 modcount;
+} TChangeLogHeader;
+
+#endif
+
+// Pending Maps database
+// =====================
+
+// Pending Maps file name
+#ifdef SETTINGS_BFI_PREFIX
+ // custom settings name prefix
+ #define PENDINGMAP_DB_SUFFIX "_pmap_" SETTINGS_BFI_PREFIX ".bfi"
+#else
+ #define PENDINGMAP_DB_SUFFIX "_pmap.bfi"
+#endif
+#define PENDINGMAP_DB_ID 7
+#define PENDINGMAP_DB_VERSION 1
+
+
+// define a max GUID size
+#define BINFILE_MAXGUIDSIZE 63
+
+// Pending Map entry
+typedef struct {
+ // UniqueID of the database record
+ #ifdef NUMERIC_LOCALIDS
+ localid_t dbrecordid;
+ #else
+ char dbrecordid[maxidlen];
+ #endif
+ // RemoteID
+ char remoteID[BINFILE_MAXGUIDSIZE+1];
+} TPendingMapEntry;
+
+// pendingmap header for one database
+typedef struct {
+ // profile ID where these maps belong to
+ uInt32 remotepartyID;
+} TPendingMapHeader;
+
+
+// Pending Item database
+#ifdef SETTINGS_BFI_PREFIX
+ // custom settings name prefix
+ #define PENDINGITEM_DB_SUFFIX "_pitm_" SETTINGS_BFI_PREFIX ".bfi"
+#else
+ #define PENDINGITEM_DB_SUFFIX "_pitm.bfi"
+#endif
+#define PENDINGITEM_DB_ID 8
+#define PENDINGITEM_DB_VERSION 1
+
+
+// pendingitem header for one database
+typedef struct {
+ // profile ID where these maps belong to
+ uInt32 remotepartyID;
+ // pending item info
+ TSyError lastItemStatus;
+ char lastSourceURI[BINFILE_MAXGUIDSIZE+1];
+ char lastTargetURI[BINFILE_MAXGUIDSIZE+1];
+ TPartialItemState piState;
+ uInt32 totalSize;
+ uInt32 unconfirmedSize;
+ uInt32 storedSize; // = size of one and only record in this binfile
+} TPendingItemHeader;
+
+
+
+// Sync Target database
+// ====================
+
+#if defined(CLIENTFEATURES_2008)
+ // new feature clients have version 6
+ #define TARGETS_DB_VERSION 6
+ #define LOWEST_TARGETS_DB_VERSION 3 // upgrading from V3 up is possible
+#elif defined(DESKTOP_CLIENT)
+ // desktop clients started with version 5
+ #define TARGETS_DB_VERSION 5
+ #define LOWEST_TARGETS_DB_VERSION 5 // we started anew at Version 5 for outlook client
+#else
+ // 2007/2008 production clients are still version 4
+ #define TARGETS_DB_VERSION 4
+ #define LOWEST_TARGETS_DB_VERSION 3 // upgrading from V3 up is possible
+#endif
+#define TARGETS_DB_ID 1
+#ifdef SETTINGS_BFI_PREFIX
+ // custom settings name prefix
+ #define TARGETS_DB_NAME SETTINGS_BFI_PREFIX "targ.bfi"
+#else
+ #define TARGETS_DB_NAME "sysytarg.bfi"
+#endif
+
+
+const uInt16 dispNameMaxLen=64;
+const uInt16 filterCapDescMaxLen=512;
+const uInt16 filterExprMaxLen=128;
+
+const uInt16 remoteDBpathMaxLen=128;
+#ifdef DESKTOP_CLIENT
+const uInt16 localDBpathMaxLen=128;
+#else
+const uInt16 localDBpathMaxLen=32;
+#endif
+const uInt16 remoteAnchorMaxLen=64;
+typedef struct {
+ // parent remotepartyID
+ uInt32 remotepartyID;
+ // db name (selects datastore from config and changelog file name)
+ char dbname[dbnamelen];
+ // user options
+ bool enabled; // enabled for sync?
+ bool forceSlowSync; // if set, a slow sync is forced, reset after successful sync
+ TSyncModes syncmode; // mode of sync
+ // user extras
+ sInt32 limit1; // in email: max incoming message body size
+ sInt32 limit2; // in email: max incoming attachment size
+ uInt32 extras; // reserved for future use
+ // SyncML target info
+ // - local database app involved
+ uInt32 localDBTypeID;
+ // - database name / entryID
+ char localDBPath[localDBpathMaxLen];
+ // %%% add category sub-selector later
+ // - remote database path
+ char remoteDBpath[remoteDBpathMaxLen];
+ // - time of last sync
+ lineartime_t lastSync;
+ lineartime_t lastTwoWaySync;
+ // - modification count of last sync (refers to changelog)
+ uInt32 lastSuspendModCount; /// @note: before DS 1.2 enginem this was used as "lastModCount"
+ uInt32 lastTwoWayModCount;
+ // - last remote (server) anchor
+ char remoteAnchor[remoteAnchorMaxLen];
+
+ // Version 4 fields start here
+ // ===========================
+ // - resume alert code if last session was suspended
+ uInt16 resumeAlertCode;
+ // - spare
+ uInt16 dummy1;
+
+ // Version 5 fields start here (%%%currently desktop only, mobile is still version 4)
+ // ===========================
+ #if defined(DESKTOP_CLIENT) || TARGETS_DB_VERSION>4 // unite desktop and mobile client
+ // - container name / entryID
+ char localContainerName[localDBpathMaxLen];
+ #endif
+ #if !defined(DESKTOP_CLIENT) && TARGETS_DB_VERSION>4
+ #error "Make sure to unite desktop and mobile versions - desktop already is version 5. Note that localDBpathMaxLen is shorter in mobile; check if 128 is really needed for desktop!"
+ #endif
+
+ // Version 6 fields start here
+ // ===========================
+ #if TARGETS_DB_VERSION>5
+ // storage for non-time sync identifiers in case DB on top is customImplDS
+ char lastSyncIdentifier[remoteAnchorMaxLen]; // string field to save fPreviousToRemoteSyncIdentifier
+ char lastSuspendIdentifier[remoteAnchorMaxLen]; // string field to save fPreviousSuspendIdentifier
+ // remote datastore info (retrieved from devInf, if possible)
+ char remoteDBdispName[dispNameMaxLen]; // display name of remote datastore
+ char filterCapDesc[filterCapDescMaxLen]; // description string of remote filter capabilities
+ // remote filter expression (in internal format)
+ char remoteFilters[filterExprMaxLen];
+ // local (client side) filter expression (in internal format)
+ char localFilters[filterExprMaxLen];
+
+ #endif // TARGETS_DB_VERSION > 5
+
+ // Version 7 fields %%%will%%% start here
+ // ===========================
+ #if TARGETS_DB_VERSION>6
+ #error "target versions > 6 not yet supported! - please check TARGETS_DB_VERSION"
+ #endif // TARGETS_DB_VERSION > 6
+
+
+} TBinfileDBSyncTarget;
+
+#define TARGETS_DB_VERSION_4_SZ sizeof(TBinfileDBSyncTarget)
+#define TARGETS_DB_VERSION_3_SZ offsetof(TBinfileDBSyncTarget,resumeAlertCode)
+
+
+// Sync Profile database
+// =====================
+
+#ifdef SETTINGS_BFI_PREFIX
+ // custom settings name prefix
+ #define PROFILE_DB_NAME SETTINGS_BFI_PREFIX "prof.bfi"
+#else
+ #define PROFILE_DB_NAME "sysyprof.bfi"
+#endif
+
+#ifndef ENHANCED_PROFILES_2004
+ #error "non-enhanced profiles and profile version <6 no longer supported!"
+#endif
+
+#define PROFILE_DB_ID 2
+#define PROFILE_DB_VERSION 9
+// lowest profile DB version still supported for automatic upgrading
+#define LOWEST_PROFILE_DB_VERSION 4
+
+
+// read-only flags
+const uInt8 rdonly_URI = 0x01; // server URI
+const uInt8 rdonly_URIpath = 0x02; // URI path suffix
+const uInt8 rdonly_protocol = 0x04; // protocol (transport http/https flag as well as SyncML version)
+const uInt8 rdonly_dbpath = 0x08; // DB path names
+const uInt8 rdonly_profile = 0x10; // profile may not be deleted or renamed (but non-readonly options can be set)
+const uInt8 rdonly_proxy = 0x20; // proxy settings (except proxy auth settings).
+// - Bit 6+7 reserved
+
+// feature flags
+const uInt8 ftrflg_autosync = 0x01; // autosync enabled
+const uInt8 ftrflg_dmu = 0x02; // DMU enabled
+const uInt8 ftrflg_range = 0x04; // Range filtering enabled (APP_FTR_EVENTRANGE etc...)
+
+// datastore availability flags
+const uInt16 dsavail_all = 0xFFFF; // all implemented datastores
+const uInt16 dsavail_contacts = 0x0001; // contacts
+const uInt16 dsavail_events = 0x0002; // events
+const uInt16 dsavail_tasks = 0x0004; // tasks
+const uInt16 dsavail_memos = 0x0008; // notes/memos
+const uInt16 dsavail_emails = 0x0010; // emails
+
+// remote-specifics flags
+const uInt8 remotespecs_devidWithUserHash = 0x01; // device ID must always include the user name
+const uInt8 remotespecs_noXTypeParams = 0x02; // vCard should not contain any X-Synthesis-RefX and other X-hhh type tags
+const uInt8 remotespecs_noDS12Filters = 0x04; // we should not use DS 1.2 SINCE/BEFORE filters
+// - Bit 3..7 reserved
+
+
+
+// field sizes
+const uInt16 maxurisiz=128;
+const uInt16 maxupwsiz=64;
+const uInt16 maxnamesiz=42;
+const uInt16 maxnoncesiz=64;
+const uInt16 maxpathsiz=64;
+const uInt16 profiledatasiz=256;
+
+typedef enum {
+ transp_proto_uri, // protocol included in URI
+ transp_proto_http, // HTTP protocol
+ transp_proto_https, // HTTPS protocol
+ transp_proto_wsp, // WSP protocol
+ transp_proto_obex_irda, // OBEX/IRDA protocol
+ transp_proto_obex_bt, // OBEX/BT protocol
+ transp_proto_obex_tcp, // OBEX/TCP protocol
+ num_transp_protos // number of protocols
+} TTransportProtocols;
+
+// NOTE!!!: changing number of levels implies a change of PROFILE_DB_VERSION!!
+#define NUM_AUTOSYNC_LEVELS 3 // we provide 3 levels of autosync
+
+typedef struct {
+ // Unique ID
+ uInt32 profileID; // internal unique ID of the profile (selects targets by their remotepartyID)
+ // PROFILE_DB_VERSION <=4 settings
+ char profileName[maxnamesiz];
+ // - encoding (note that release client might not allow XML)
+ SmlEncoding_t encoding;
+ // - Old-style Server URI, misused for several other things
+ // Note: for hard-coded server URI, this is also misused as companyID or secure flag:
+ // - ONE_e2g: serverURI = companyID
+ // - SCM: serverURI[0] = secure flag (HTTPS)
+ char serverURI[maxurisiz];
+ // - User name
+ char serverUser[maxupwsiz];
+ // - password
+ char serverPassword[maxupwsiz];
+ // - Transport user
+ char transportUser[maxupwsiz];
+ // - Transport password
+ char transportPassword[maxupwsiz];
+ // - Socks Host
+ char socksHost[maxurisiz];
+ // - Proxy Host
+ char proxyHost[maxurisiz];
+ // Internals
+ // - session ID
+ uInt8 sessionID;
+ // - last connection's parameters (used as default for next session)
+ TSyncMLVersions lastSyncMLVersion;
+ TAuthTypes lastAuthMethod;
+ TFmtTypes lastAuthFormat;
+ char lastNonce[maxnoncesiz];
+ // - for registration checking
+ lineardate_t firstuse; // copy of a global setting, so we can compare and see if someone has tampered with
+
+ // Additional PROFILE_DB_VERSION 5 settings
+ // - Enhanced server addressing
+ // - possibly some path extension
+ char URIpath[maxpathsiz];
+ // - protocol selector (0=free-form from URI, 1..n = reserved for fixed protocols)
+ TTransportProtocols protocol;
+ // - read-only flags (rdonly_xxx)
+ uInt8 readOnlyFlags;
+ // - flags for special remote-specific behaviour (remotespecs_XXX) - was reserved until 2.5.0.68 / 2.9.8.10.
+ uInt8 remoteFlags;
+ // - profile feature flags (ftrflg_xxx)
+ uInt8 featureFlags;
+ // - Local Database profile name
+ char localDBProfileName[localDBpathMaxLen];
+ // - Proxy usage options
+ bool useProxy; // use configured proxies
+ bool useConnectionProxy; // using proxy from current connection, if available
+ // - Auto-Sync levels (3 levels, first match overrides lower level settings)
+ TAutoSyncLevel AutoSyncLevel[NUM_AUTOSYNC_LEVELS];
+ // - Datastore available flags (for clients with variable datastores)
+ uInt16 dsAvailFlags; // dsavail_xxx flags
+ // - Timed sync settings
+ uInt16 TimedSyncMobilePeriod; // [min] how often to sync when mobile (battery, wireless)
+ uInt16 TimedSyncCradledPeriod; // [min] how often to sync when cradled (ac powered, wired)
+ // - IPP settings (not available in all clients, but present in all profile records
+ TIPPSettings ippSettings;
+
+ // Additional PROFILE_DB_VERSION 8 settings
+ // - Proxy user
+ char proxyUser[maxupwsiz];
+ // - Proxy password
+ char proxyPassword[maxupwsiz];
+
+ // Additional PROFILE_DB_VERSION 9 settings
+ // - transport related profile flags
+ uInt32 transpFlags;
+ // - general profile flags
+ uInt32 profileFlags;
+ // - general profile ints
+ uInt32 profileExtra1;
+ uInt32 profileExtra2;
+ // - general purpose profile data (intended for SDK based app's usage)
+ uInt8 profileData[profiledatasiz];
+
+} TBinfileDBSyncProfile;
+
+#define PROFILE_DB_VERSION_7_SZ offsetof(TBinfileDBSyncProfile,proxyUser)
+#define PROFILE_DB_VERSION_8_SZ offsetof(TBinfileDBSyncProfile,transpFlags)
+
+
+
+
+
+#if LOWEST_PROFILE_DB_VERSION<6
+// old structs and consts used to access fields of profiles older than PROFILE_DB_VERSION 6
+
+const uInt16 o_maxurisiz=128;
+const uInt16 o_maxupwsiz=32;
+const uInt16 o_maxnamesiz=42;
+const uInt16 o_maxnoncesiz=64;
+const uInt16 o_maxpathsiz=64;
+
+typedef struct {
+ char srv[maxipppathsiz];
+ uInt16 port;
+ uInt16 period;
+ char path[maxipppathsiz];
+ char id[maxippidsiz];
+ uInt8 method;
+} o_TIPPSettings;
+
+typedef struct {
+ // Unique ID
+ uInt32 profileID; // internal unique ID of the profile (selects targets by their remotepartyID)
+ // PROFILE_DB_VERSION <=4 settings
+ char profileName[o_maxnamesiz];
+ // - encoding (note that release client might not allow XML)
+ SmlEncoding_t encoding;
+ // - Old-style Server URI, misused for several other things
+ // Note: for hard-coded server URI, this is also misused as companyID or secure flag:
+ // - ONE_e2g: serverURI = companyID
+ // - SCM: serverURI[0] = secure flag (HTTPS)
+ char serverURI[o_maxurisiz];
+ // - User name
+ char serverUser[o_maxupwsiz];
+ // - password
+ char serverPassword[o_maxupwsiz];
+ // - Transport user
+ char transportUser[o_maxupwsiz];
+ // - Transport password
+ char transportPassword[o_maxupwsiz];
+ // - Socks Host
+ char socksHost[o_maxurisiz];
+ // - Proxy Host
+ char proxyHost[o_maxurisiz];
+ // Internals
+ // - session ID
+ uInt8 sessionID;
+ // - last connection's parameters (used as default for next session)
+ TSyncMLVersions lastSyncMLVersion;
+ TAuthTypes lastAuthMethod;
+ TFmtTypes lastAuthFormat;
+ char lastNonce[o_maxnoncesiz];
+ // - for registration checking
+ lineardate_t firstuse; // copy of a global setting, so we can compare and see if someone has tampered with
+ // Additional PROFILE_DB_VERSION 5 settings
+ // - Enhanced server addressing
+ // - possibly some path extension
+ char URIpath[o_maxpathsiz];
+ // - protocol selector (0=free-form from URI, 1..n = reserved for fixed protocols)
+ TTransportProtocols protocol;
+ // - read-only flags (Bit 0=URI, 1=URIpath, 2=protocol, 3..7 reserved)
+ uInt8 readOnlyFlags;
+ // - 2 bytes reserved for future use
+ uInt16 reserved1;
+ // - Local Database profile name
+ char localDBProfileName[localDBpathMaxLen];
+ // - Proxy usage options
+ bool useProxy; // use configured proxies
+ bool useConnectionProxy; // using proxy from current connection, if available
+ // - Auto-Sync levels (3 levels, first match overrides lower level settings)
+ TAutoSyncLevel AutoSyncLevel[NUM_AUTOSYNC_LEVELS];
+ // - reserved
+ uInt8 reserved2; // reserved
+ uInt8 reserved3; // reserved
+ // - Timed sync settings
+ uInt16 TimedSyncMobilePeriod; // [min] how often to sync when mobile (battery, wireless)
+ uInt16 TimedSyncCradledPeriod; // [min] how often to sync when cradled (ac powered, wired)
+ // - IPP settings (not available in all clients, but present in all profile records
+ o_TIPPSettings ippSettings;
+} o_TBinfileDBSyncProfile;
+
+#endif
+
+
+// versioned record sizes
+#define PROFILE_DB_VERSION_5_SZ sizeof(o_TBinfileDBSyncProfile)
+#define PROFILE_DB_VERSION_4_SZ offsetof(o_TBinfileDBSyncProfile,URIpath)
+
+
+#pragma pack(pop)
+
+
+// datastore config
+// ================
+
+#ifdef SCRIPT_SUPPORT
+// publish func table for chaining
+extern const TFuncTable BinfileClientDBFuncTable;
+#endif // SCRIPT_SUPPORT
+
+class TBinfileDSConfig: public TLocalDSConfig
+{
+ typedef TLocalDSConfig inherited;
+public:
+ TBinfileDSConfig(const char* aName, TConfigElement *aParentElement);
+ virtual ~TBinfileDSConfig();
+ // properties
+ // - identifies local Database related to this datastore
+ string fLocalDBPath;
+ // - flag that corresponds with profile.dsAvailFlags / dsavail_xxx
+ uInt16 fDSAvailFlag;
+ // public methods
+ // - init a default target for this datastore
+ virtual void initTarget(
+ TBinfileDBSyncTarget &aTarget, // target record to initialize
+ uInt32 aRemotepartyID,
+ const char *aRemoteName=NULL, // defaults to name of datastore
+ bool aEnabled=false // enabled?
+ );
+ // - checks if datastore is available for given profile. If aProfileP is NULL,
+ // general availability is checked (profile might allow more or less)
+ bool isAvailable(TBinfileDBSyncProfile *aProfileP);
+ // - one-way support is always given for binfile based DS
+ virtual bool isOneWayFromRemoteSupported() { return true; }
+ #ifdef SCRIPT_SUPPORT
+ // function table
+ virtual const TFuncTable *getClientDBFuncTable(void) { return &BinfileClientDBFuncTable; };
+ #endif // SCRIPT_SUPPORT
+protected:
+ // check config elements
+ #ifndef HARDCODED_CONFIG
+ virtual bool localStartElement(const char *aElementName, const char **aAttributes, sInt32 aLine);
+ #endif
+ virtual void clear();
+ virtual void localResolve(bool aLastPass);
+ // Autosync declarations
+ CLIENTAUTOSYNC_CLASSDECL_DB
+}; // TBinfileDSConfig
+
+
+
+class TBinfileImplDS: public TStdLogicDS
+{
+ typedef TStdLogicDS inherited;
+private:
+ void InternalResetDataStore(void); // reset for re-use without re-creation
+protected:
+ /// @name dsSavedAdmin administrative data (anchors, timestamps, maps) as saved or to-be-saved
+ /// @Note These will be loaded and saved be derived classes
+ /// @Note Some of these will be updated from resp. @ref dsCurrentAdmin members at distinct events (suspend, session end, etc.)
+ /// @Note Some of these will be updated during the session, but in a way that does NOT affect the anchoring of current/last session
+ //
+ /// @{
+ /// Reference time of previous sync which sent data to remote to compare modification dates against
+ /// @note normally==fPreviousSyncTime, but can be end of sync for datastore that can't write modified timestamps at will
+ lineartime_t fPreviousToRemoteSyncCmpRef;
+ /// Reference string used by database API level (in BASED_ON_BINFILE_CLIENT case) to detect changes
+ string fPreviousToRemoteSyncIdentifier;
+ /// modification count of last sync target's modifications
+ uInt32 fPreviousToRemoteModCount;
+ /// Reference time of last suspend, needed to detect modifications that took place between last suspend and current resume
+ lineartime_t fPreviousSuspendCmpRef;
+ /// Reference string used by database API level (in BASED_ON_BINFILE_CLIENT case) to detect changes after last suspend
+ string fPreviousSuspendIdentifier;
+ /// modification count used to determine modifications since last to-remote-sync
+ uInt32 fPreviousSuspendModCount;
+ /// @}
+
+ /// @name dsCurrentAdmin current session's admin data (anchors, timestamps, maps)
+ /// @Note These will be copied to @ref dsSavedAdmin members ONLY when a session completes successfully/suspends.
+ /// @Note Admin data is NEVER directly saved or loaded from these
+ /// @Note Derivates will update some of these at dssta_adminready with current time/anchor values
+ //
+ /// @{
+ /// Reference time of current sync to compare modification dates against
+ /// @note initially==fCurrentSyncTime, but might be set to end-of-session time for databases which cannot explicitly set modification timestamps
+ lineartime_t fCurrentSyncCmpRef;
+ /// modification count identifying this session's time (for detecting changes taking place after this session)
+ uInt32 fCurrentModCount;
+ /// Reference string returned by database API level identifying this session's time (for detecting changes taking place after this session)
+ string fCurrentSyncIdentifier;
+ /// @}
+
+public:
+ TBinfileImplDS(
+ TBinfileDSConfig *aConfigP,
+ sysync::TSyncSession *aSessionP,
+ const char *aName,
+ uInt32 aCommonSyncCapMask=0);
+ virtual ~TBinfileImplDS();
+ virtual void announceAgentDestruction(void) { /** @todo nop for now */ };
+ virtual void dsResetDataStore(void);
+
+ // - called from TBinFileDBAgent::SelectProfile(), should return false if local datastore is not accessible
+ virtual bool localDatastorePrep(void) { return true; }
+ // - set Sync Parameters. Derivate adds CGI to aRemoteDBPath
+ virtual bool dsSetClientSyncParams(
+ TSyncModes aSyncMode,
+ bool aSlowSync,
+ const char *aRemoteDBPath,
+ const char *aDBUser = NULL,
+ const char *aDBPassword = NULL,
+ const char *aLocalPathExtension = NULL,
+ const char *aRecordFilterQuery = NULL,
+ bool aFilterInclusive = false
+ );
+
+
+ // Target vars
+ sInt32 fTargetIndex;
+ TBinfileDBSyncTarget fTarget;
+protected:
+ /// called for SyncML 1.1 if remote wants number of changes.
+ /// Must return -1 no NOC value can be returned
+ virtual sInt32 getNumberOfChanges(void) { return fNumberOfLocalChanges; };
+ // Simple custom DB access interface methods
+ /// sync login (into this database)
+ /// @note might be called several times (auth retries at beginning of session)
+ /// @note must update the following state variables
+ /// - in TLocalEngineDS: fLastRemoteAnchor, fLastLocalAnchor, fResumeAlertCode, fFirstTimeSync
+ /// - for client: fPendingAddMaps
+ /// - in TStdLogicDS: fPreviousSyncTime, fCurrentSyncTime
+ /// - in derived classes: whatever else belongs to dsSavedAdmin and dsCurrentAdmin state
+ virtual localstatus implMakeAdminReady(
+ const char *aDeviceID, ///< remote device URI (device ID)
+ const char *aDatabaseID, ///< database ID
+ const char *aRemoteDBID ///< database ID of remote device
+ );
+ /// start data read
+ /// @note: fSlowSync and fRefreshOnly must be valid before calling this method
+ virtual localstatus implStartDataRead();
+ /// get item from DB
+ virtual localstatus implGetItem(
+ bool &aEof,
+ bool &aChanged,
+ TSyncItem* &aSyncItemP
+ );
+ /// end of read
+ virtual localstatus implEndDataRead(void);
+ /// start of write
+ virtual localstatus implStartDataWrite(void);
+ /// review reported entry (allows post-processing such as map deleting)
+ /// MUST be called after implStartDataWrite, before any actual writing,
+ /// for each item obtained in implGetItem
+ virtual localstatus implReviewReadItem(
+ TSyncItem &aItem // the item
+ ) { return true; }; // nop here
+ /// retrieve specified item from database
+ virtual bool implRetrieveItemByID(
+ TSyncItem &aItem, // the item
+ TStatusCommand &aStatusCommand
+ );
+ /// process item (according to operation: add/delete/replace - and for future: copy/move)
+ virtual bool implProcessItem(
+ TSyncItem *aItemP, // the item
+ TStatusCommand &aStatusCommand
+ );
+ /// called to mark an already generated (but probably not sent or not yet statused) item
+ /// as "to-be-resumed", by localID or remoteID (latter only in server case).
+ /// @note This must be repeatable without side effects, as server must mark/save suspend state
+ /// after every request (and not just at end of session)
+ virtual void implMarkItemForResume(cAppCharP aLocalID, cAppCharP aRemoteID, bool aUnSent);
+ /// called to mark an already sent item as "to-be-resent", e.g. due to temporary
+ /// error status conditions, by localID or remoteID (latter only in server case).
+ virtual void implMarkItemForResend(cAppCharP aLocalID, cAppCharP aRemoteID);
+ /// called to have all non-yet-generated sync commands as "to-be-resumed"
+ virtual void implMarkOnlyUngeneratedForResume(void);
+ /// save status information required to eventually perform a resume (as passed to datastore with
+ /// markOnlyUngeneratedForResume() and markItemForResume())
+ /// (or, in case the session is really complete, make sure that no resume state is left)
+ virtual localstatus implSaveResumeMarks(void);
+ /// save end of session state
+ virtual localstatus implSaveEndOfSession(bool aUpdateAnchors);
+ /// end write sequence
+ virtual bool implEndDataWrite(void);
+
+public:
+ /// @name dsXXXX virtuals defined by TLocalEngineDS
+ /// These are usually designed such that they should always call inherited::dsXXX to let the entire chain
+ /// of ancestors see the calls
+ /// @{
+ //
+ /// end of message handling
+ virtual void dsEndOfMessage(void);
+ /// inform logic of coming state change
+ virtual localstatus dsBeforeStateChange(TLocalEngineDSState aOldState,TLocalEngineDSState aNewState);
+ /// inform logic of happened state change
+ virtual localstatus dsAfterStateChange(TLocalEngineDSState aOldState,TLocalEngineDSState aNewState);
+ /// called to confirm a sync operation's completion (ok status from remote received)
+ /// @note aSyncOp passed not necessarily reflects what was sent to remote, but what actually happened
+ virtual void dsConfirmItemOp(TSyncOperation aSyncOp, cAppCharP aLocalID, cAppCharP aRemoteID, bool aSuccess, localstatus aErrorStatus=0);
+ /// returns true if DB implementation supports resume (saving of resume marks, alert code, pending maps)
+ virtual bool dsResumeSupportedInDB(void) { return true; }; // resume supported
+ /// returns true if DB implementation supports resuming in midst of a chunked item (can save fPIxxx.. and related admin data)
+ virtual bool dsResumeChunkedSupportedInDB(void) { return true; };
+ /// saves user log information about the sync session
+ virtual void dsLogSyncResult(void);
+
+ /// @}
+
+protected:
+ // Routines to be implemented by derived classes to actually access
+ // local databases
+ #ifndef CHANGEDETECTION_AVAILABLE
+ #ifdef RECORDHASH_FROM_DBAPI
+ /// get first item's ID and CRC from the sync set.
+ /// @return false if no item found
+ virtual bool getFirstItemCRC(localid_out_t &aLocalID, uInt16 &aItemCRC) = 0;
+ /// get next item's ID and CRC from the sync set.
+ /// @return false if no item found
+ virtual bool getNextItemCRC(localid_out_t &aLocalID, uInt16 &aItemCRC) = 0;
+ #else
+ /// get first item from the sync set. Caller obtains ownership if aItemP is not NULL after return
+ /// @return false if no item found
+ virtual bool getFirstItem(TSyncItem *&aItemP) = 0;
+ /// get next item from the sync set. Caller obtains ownership if aItemP is not NULL after return
+ /// @return false if no item found
+ virtual bool getNextItem(TSyncItem *&aItemP) = 0;
+ #endif
+ #else
+ /// get first item's ID and modification status from the sync set
+ /// @return false if no item found
+ virtual bool getFirstItemInfo(localid_out_t &aLocalID, bool &aItemHasChanged) = 0;
+ /// get next item's ID and modification status from the sync set.
+ /// @return false if no item found
+ virtual bool getNextItemInfo(localid_out_t &aLocalID, bool &aItemHasChanged) = 0;
+ #endif
+ /// get item by local ID from the sync set. Caller obtains ownership if aItemP is not NULL after return
+ /// @return != LOCERR_OK if item with specified ID is not found.
+ virtual localstatus getItemByID(localid_t aLocalID, TSyncItem *&aItemP) = 0;
+ #ifdef RECORDHASH_FROM_DBAPI
+ /// get specified item's CRC as calculated by DB
+ /// @return != LOCERR_OK if item with specified ID is not found.
+ virtual localstatus getItemCRCByID(localid_t aLocalID, uInt16 &aItemCRC) = 0;
+ #endif
+ /// end of syncset reading phase (especially for customimplds on top of binfileds)
+ virtual localstatus apiEndDataRead(void) { return LOCERR_OK; };
+ /// signal start of data write phase
+ virtual localstatus apiStartDataWrite(void) { return LOCERR_OK; };
+ /// signal end of data write phase
+ virtual localstatus apiEndDataWrite(void) { return LOCERR_OK; };
+ /// update item by local ID in the sync set. Caller retains ownership of aItemP
+ /// @return != LOCERR_OK if item with specified ID is not found.
+ virtual localstatus updateItemByID(localid_t aLocalID, TSyncItem *aItemP) = 0;
+ /// delete item by local ID in the sync set.
+ /// @return != LOCERR_OK if item with specified ID is not found.
+ virtual localstatus deleteItemByID(localid_t aLocalID) = 0;
+ /// create new item in the sync set. Caller retains ownership of aItemP.
+ /// @return LOCERR_OK or error code.
+ /// @param[out] aNewLocalID local ID assigned to new item
+ /// @param[out] aReceiveOnly is set to true if local changes/deletion of this item should not be
+ /// reported to the server in normal syncs.
+ virtual localstatus createItem(TSyncItem *aItemP,localid_out_t &aNewLocalID, bool &aReceiveOnly) = 0;
+ /// zaps the entire datastore, returns LOCERR_OK if ok
+ /// @return LOCERR_OK or error code.
+ virtual localstatus zapDatastore(void) = 0;
+ /// get error code for last routine call that returned !=LOCERR_OK
+ /// @return platform specific DB error code
+ virtual uInt32 lastDBError(void) { return 0; };
+ virtual bool isDBError(uInt32 aErrCode) { return aErrCode!=0; } // standard implementation assumes 0=ok
+ /// returns timestamp of this sync session (must be used to set mod-timestamp on added and changed records)
+ lineartime_t getThisSyncTime(void) { return fCurrentSyncTime; }
+ /// get reference time or count to be used as reference date for this sync's modifications
+ lineartime_t getThisSyncModRefTime(void);
+ uInt32 getThisSyncModCount(void) { return fCurrentModCount; }
+ /// get reference time or count to be used in case datastore implementation wants to compare
+ /// with the time or count of last sync (last two-way sync, that is!)
+ lineartime_t getLastSyncModRefTime(void);
+ uInt32 getLastSyncModCount(void) { return fPreviousToRemoteModCount; }
+ /// zaps changelog. Should be called if datastore as a entiety was replaced
+ /// by another datatstore (or created new)
+ void zapChangeLog(void);
+private:
+ /// load changelog into memory for quick access
+ void loadChangeLog(void);
+ /// forget changelog in memory
+ void forgetChangeLog(void);
+ /// private helper to prepare for apiSaveAdminData()
+ localstatus SaveAdminData(bool aSessionFinished, bool aSuccessful);
+ // private utils
+ #ifdef OBJECT_FILTERING
+ // - Test Filters
+ bool testFilters(TMultiFieldItem *aItemP);
+ #endif
+ // sync anchor, time and changelog cursor (modcount)
+ #ifdef CHECKSUM_CHANGELOG
+ /// change log
+ TBinFile fChangeLog; ///< change log binfile
+ TChangeLogHeader fChgLogHeader; ///< change log header
+ // - returns true if we had a valid changelog
+ bool openChangeLog(void);
+ // - returns true if we had a valid pending map file
+ bool openPendingMaps(void);
+ // - update change log before syncing. Don't call before types are ok (we need TSyncItems)
+ localstatus changeLogPreflight(bool &aValidChangelog);
+ // - clean up change log after syncing
+ localstatus changeLogPostflight(uInt32 aOldestSyncModCount);
+ // - vars
+ bool fPreflighted; // set if preflight (changelog update) was done
+ // - entire change log, loaded into memory for quick reference during write phase
+ TChangeLogEntry *fLoadedChangeLog;
+ uInt32 fLoadedChangeLogEntries;
+ #endif
+ /// pending maps for Resume
+ TBinFile fPendingMaps; ///< pending map binfile
+ TPendingMapHeader fPendingMapHeader; ///< pending map header
+ // number of local changes
+ sInt32 fNumberOfLocalChanges;
+ // GetItem vars
+ bool fAllRecords; /// @todo: for now, this is copied from TLocalEnginDS::fSlowSync
+ //bool fRefreshing;
+ uInt32 fLogEntryIndex;
+ // config (typed pointer for convenience)
+ TBinfileDSConfig *fConfigP;
+}; // TBinfileImplDS
+
+} // namespace sysync
+
+#endif // BINFILEIMPLDS_H
+
+// eof
diff --git a/src/sysync/clientautosync_inc.cpp b/src/sysync/clientautosync_inc.cpp
new file mode 100755
index 0000000..f80f578
--- /dev/null
+++ b/src/sysync/clientautosync_inc.cpp
@@ -0,0 +1,1947 @@
+/*
+ * File: clientautosync_inc.cpp
+ *
+ * Include file for binfileagent.cpp and palmdbagent.cpp to provide
+ * autosync mechanisms.
+ *
+ * Author: Lukas Zeller (luz@synthesis.ch)
+ *
+ * Copyright (c) 2004-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ * 2004-05-10 : luz : created from clientprovisioning_inc.cpp
+ *
+ *
+ */
+
+#ifdef AUTOSYNC_SUPPORT
+
+#ifdef SYDEBUG
+
+static const char * const IPPStateNames[num_ipp_states] = {
+ "unknown",
+ "idle",
+ "activated",
+ "subscribed"
+};
+
+
+static const char * const AutoSyncModeNames[num_autosync_modes] = {
+ "push",
+ "timed",
+ "none",
+ "serveralert"
+};
+
+static const char * const AutoSyncStateNames[num_autosync_states] = {
+ "idle",
+ "start", // start selected autosync mechanism
+ "timedsync", // pure scheduled sync
+ "timedsync_wait", // waiting for scheduled sync to occur
+ "ipp_connectwait", // start waiting until connecting again
+ "ipp_connectwaiting", // waiting for connecting again
+ "ipp_resubscribe", // re-subscribe to DMU method 1
+ "alert_implicit", // alert implicit datastores
+ "ipp_connect", // connect to server
+ "ipp_sendrequest", // send request to server
+ "ipp_answerwait", // connected, waiting for answer from IPP server
+ "ipp_disconnect", // disconnect from the server
+ "ipp_endanswer", // disconnect after a IPP answer
+ "ipp_processanswer" // process collected IPP answer
+};
+
+
+#endif
+
+
+// utility functions
+
+
+// applies the value to the IPP parameter identified by aTag
+// This can be called from client provisioning code or from a sync session's GET
+// command RESULT (if the server supports IPP autoconfig)
+void CLIENTAGENTCONFIG::ipp_setparam(const char *aTag, const char *aValue, TIPPSettings &aIPPSettings)
+{
+ #ifdef IPP_SUPPORT
+ uInt16 res=0;
+
+ if (strucmp(aTag,"srv")==0) {
+ AssignCString(aIPPSettings.srv,aValue,maxipppathsiz);
+ fIPPState=ippstate_unknown;
+ }
+ else if (strucmp(aTag,"path")==0) {
+ AssignCString(aIPPSettings.path,aValue,maxipppathsiz);
+ }
+ else if (strucmp(aTag,"id")==0) {
+ AssignCString(aIPPSettings.id,aValue,maxippidsiz);
+ fIPPState=ippstate_unknown;
+ }
+ else if (strucmp(aTag,"port")==0) {
+ StrToUShort(aValue,aIPPSettings.port);
+ }
+ else if (strucmp(aTag,"period")==0) {
+ StrToUShort(aValue,aIPPSettings.period);
+ }
+ else if (strucmp(aTag,"method")==0) {
+ StrToUShort(aValue,res);
+ aIPPSettings.method=(uInt8)res;
+ fIPPState=ippstate_unknown;
+ }
+ else if (strucmp(aTag,"cred")==0) {
+ AssignCString(aIPPSettings.cred,aValue,maxipppathsiz);
+ fIPPState=ippstate_unknown;
+ }
+ else if (strucmp(aTag,"maxinterval")==0) {
+ StrToUShort(aValue,res);
+ aIPPSettings.maxinterval=res;
+ }
+ else if (strucmp(aTag,"timedds")==0) {
+ AssignCString(aIPPSettings.timedds,aValue,maxipppathsiz);
+ }
+ #ifdef GAL_CLIENT_SUPPORT
+ else if (strucmp(aTag,"gal_url")==0) {
+ AssignCString(gGALInfo.url,aValue,GAL_STRING_MAX);
+ gGALInfo.galUpdated=true;
+ }
+ else if (strucmp(aTag,"gal_domain")==0) {
+ AssignCString(gGALInfo.domain,aValue,GAL_STRING_MAX);
+ gGALInfo.galUpdated=true;
+ }
+ #endif
+ #endif
+} // CLIENTAGENTCONFIG::ipp_setparam
+
+
+
+// generic code that can work as palmdbagent or binfiledbagent method
+
+// must be called when application starts to initialize autosync
+// BEFORE autosync_setprofile()
+void CLIENTAGENTCONFIG::autosync_init(lineartime_t aLastAlert)
+{
+ // make sure all autosync is disabled
+ fAutosyncProfile.AutoSyncLevel[0].Mode=autosync_none;
+ fAutosyncProfile.AutoSyncLevel[1].Mode=autosync_none;
+ fAutosyncProfile.AutoSyncLevel[2].Mode=autosync_none;
+ // init state
+ fAutosyncState=autosync_idle;
+ fAutosyncProfileLastidx=-1; // no profile selected yet
+ fAutosyncLastAlert=aLastAlert; // as saved by the main app
+ fNewMode=autosync_none; // no new mode
+ // init per-profile state
+ fCurrentLevelIndex=-1; // none yet
+ fAutoSyncMode=autosync_none; // disabled until we have checked
+ fNextSyncAt=0; // we do not know when next sync will be
+ fWhyNotFlags=0; // no reason why not
+ // cancel all current alerts
+ autosync_ackAlert(false);
+ #ifdef TCP_LEAK_DEBUG
+ fTcpLeakCounter=0;
+ #endif
+} // CLIENTAGENTCONFIG::autosync_init
+
+
+// to be called to forget or actually execute prepared autosync alerts in DS config
+// (e.g. after asking user for confirmation after receiving a SAN)
+void CLIENTAGENTCONFIG::autosync_ackAlert(bool aAck)
+{
+ if (!aAck) {
+ // cancel alert
+ fAutosyncAlerted=false;
+ fUnsuccessfulAlerts=0;
+ // make sure all prepared alerts in the DS config are cleared
+ TLocalDSList::iterator pos;
+ for (pos=fDatastores.begin(); pos!=fDatastores.end(); pos++) {
+ CLIENTDSCONFIG *dscfgP = static_cast<CLIENTDSCONFIG *>(*pos);
+ dscfgP->fAutosyncAlerted=false;
+ dscfgP->fAutosyncForced=false;
+ dscfgP->fAutosyncAlertCode=0;
+ dscfgP->fAutosyncPathCGI.erase();
+ }
+ }
+ else {
+ // activate alerts prepared in DS config
+ fAutosyncAlerted=true;
+ }
+} // autosync_ackAlert
+
+
+// must be called when active profile changes (or at start of application)
+void CLIENTAGENTCONFIG::autosync_setprofile(sInt32 aProfileIndex, bool aHasChanged)
+{
+ if (aProfileIndex != fAutosyncProfileLastidx || aHasChanged) {
+ // a new profile gets active
+ fAutosyncProfileLastidx=aProfileIndex;
+ // - first stop autosync
+ autosync_stop();
+ // - cancel eventually pending alerts
+ autosync_ackAlert(false);
+ // - read profile that determines autosync behaviour
+ if (getProfile(aProfileIndex,fAutosyncProfile)<0) {
+ // make sure all autosync is disabled
+ fAutosyncProfile.AutoSyncLevel[0].Mode=autosync_none;
+ fAutosyncProfile.AutoSyncLevel[1].Mode=autosync_none;
+ fAutosyncProfile.AutoSyncLevel[2].Mode=autosync_none;
+ }
+ // - re-init per-profile state
+ fAutoSyncMode=autosync_none; // disabled until we have checked
+ fAutosyncAlerted=false;
+ fNextSyncAt=0; // we do not know when next sync will be
+ fWhyNotFlags=0; // no reason why not
+ #ifdef IPP_SUPPORT
+ fIPPState=ippstate_unknown; // we do not know yet
+ #endif
+ // - make sure we do a full settings check
+ fNextCheckAt=0; // do immediately check settings at next step
+ fNextWakeupCheckAt=0;
+ }
+} // CLIENTAGENTCONFIG::autosync_setprofile
+
+
+// must be called to obtain DMU PUT command data to send to SyncML server
+void CLIENTAGENTCONFIG::autosync_get_putrequest(string &aReq)
+{
+ // use custom Put for requesting activation in s2g methods 0 and 1 (but not for method 2 and above)
+ aReq.erase(); // no DMU request when DMU is not active or status unknown
+ #ifdef IPP_SUPPORT
+ if (fAutoSyncMode==autosync_ipp && fIPPState!=ippstate_unknown && fAutosyncProfile.ippSettings.method<IPP_METHOD_OMP) {
+ // yes, send out status
+ if (fIPPState==ippstate_idle) {
+ aReq=IPP_REQ_ACTIVATE; // we have no DMUCI (any more), we need to be (re)activated
+ }
+ else {
+ if (fIPPState==ippstate_activated && fAutosyncProfile.ippSettings.method==IPP_METHOD_DMUS2G) {
+ aReq=IPP_REQ_SUBSCRIBE; // we have no valid creds or no DMU server, we need to (re)subscribe
+ }
+ else {
+ aReq=IPP_REQ_CHECK; // from our side, everything's ok, but server might want to send new IPP settings anyway
+ }
+ }
+ // always append DMUCI
+ StringObjAppendPrintf(
+ aReq,
+ "=%s\r\n",
+ fAutosyncProfile.ippSettings.id
+ );
+ // append extra params for method = IPP_METHOD_DMUS2G
+ if (fAutosyncProfile.ippSettings.method==IPP_METHOD_DMUS2G) {
+ StringObjAppendPrintf(
+ aReq,
+ "error=%hd\r\n",
+ fAutosyncRetries
+ );
+ }
+ }
+ #endif
+} // CLIENTAGENTCONFIG::autosync_get_putrequest
+
+
+
+// should be called when environmental params have changed
+// (battery, cradled/uncradled etc.)
+void CLIENTAGENTCONFIG::autosync_condchanged(void)
+{
+ fNextCheckAt=0; // do immediately check settings at next step
+} // CLIENTAGENTCONFIG::autosync_modecheck
+
+
+
+// returns true if application may quit or otherwise stop calling
+// autosync_check() in short intervals. Time when autosync_check() should
+// be called next time latest will be returned in aRestartAt.
+//
+bool CLIENTAGENTCONFIG::autosync_canquit(lineartime_t &aRestartAt, bool aEnabled)
+{
+ aRestartAt=0; // assume no timed restart needed
+ bool canquit=false;
+ // check if we can quit
+ if (!aEnabled || fAutoSyncMode==autosync_none || fAutoSyncMode==autosync_serveralerted)
+ canquit=true; // we can quit if no autosync is running or if alert event can start the app (as in case of SAN)
+ else if (fAutoSyncMode!=autosync_ipp)
+ canquit=true; // if we have no IPP running, we can quit too (but need a restart later)
+ // check if we need to restart at some time
+ if (!aEnabled) {
+ aRestartAt=0; // if not enabled, we do not need to restart at all
+ }
+ else if (fNextWakeupCheckAt<maxLinearTime || fNextSyncAt!=0) {
+ // we have scheduled activity, we need to come back
+ aRestartAt=
+ (fNextSyncAt==0 || fNextWakeupCheckAt<fNextSyncAt) ?
+ fNextWakeupCheckAt :
+ fNextSyncAt;
+ }
+ #if SYDEBUG>3
+ // Note: do no show this in normal debug clients any more as it now happens for EVERY autosync check
+ if (PDEBUGTEST(DBG_TRANSP)) {
+ string ts;
+ StringObjTimestamp(ts,aRestartAt);
+ PDEBUGPRINTFX(DBG_TRANSP,(
+ "AUTOSYNC: app %s quit now, and must be running (again) by %s",
+ canquit ? "MAY" : "MUST NOT",
+ ts.c_str()
+ ));
+ }
+ #endif
+ return canquit;
+} // CLIENTAGENTCONFIG::autosync_canquit
+
+
+
+// returns true if we need to perform a sync session now
+bool CLIENTAGENTCONFIG::autosync_check(bool aActive)
+{
+ // process one state machine step in active mode
+ autosync_step(aActive);
+ // check if we have a pending re-alert due to failed session execution,
+ // but only if autosync mode is still active (could be temporarily off because of phone flight mode)
+ if (!fAutosyncAlerted && aActive && fAutoSyncMode!=autosync_none) {
+ // no real alert pending, check if we need to generate one for retry reasons
+ if (fUnsuccessfulAlerts>0 && fUnsuccessfulAlerts<AUTOSYNC_MAX_ALERTRETRIES) {
+ // we had unsuccessful alerts before
+ if (getSystemNowAs(TCTX_SYSTEM)>fAutosyncLastAlert+(AUTOSYNC_MIN_ALERTRETRY << (fUnsuccessfulAlerts-1))*secondToLinearTimeFactor) {
+ // re-alert
+ PDEBUGPRINTFX(DBG_TRANSP,("AUTOSYNC: re-alerting after %hd unsuccessful attempts to run AutoSync Session",fUnsuccessfulAlerts));
+ fAutosyncLastAlert=getSystemNowAs(TCTX_SYSTEM);
+ fAutosyncAlerted=true;
+ if (fUnsuccessfulAlerts>=AUTOSYNC_FORCEDOWN_RETRIES) {
+ #ifndef ENGINE_LIBRARY
+ // explicitly bring down connection (if we are in a XPT client)
+ getXPTClientBase()->releaseConnection(true);
+ #endif
+ // bring down autosync
+ autosync_stop();
+ }
+ }
+ }
+ }
+ return fAutosyncAlerted && aActive;
+} // CLIENTAGENTCONFIG::autosync_check
+
+
+// called to announce that a autosync session was performed
+void CLIENTAGENTCONFIG::autosync_confirm(bool aUnsuccessful)
+{
+ if (aUnsuccessful) {
+ // re-alert again later
+ fUnsuccessfulAlerts++;
+ // but clear main alert flag
+ fAutosyncAlerted=false;
+ }
+ else {
+ // clear alert
+ autosync_ackAlert(false);
+ }
+ PDEBUGPRINTFX(DBG_HOT,("AUTOSYNC: confirmed %ssuccessful execution of autosync session",aUnsuccessful ? "un" : ""));
+} // CLIENTAGENTCONFIG::autosync_confirm
+
+
+
+// returns true if we need to perform a sync session now
+void CLIENTAGENTCONFIG::autosync_stop(void)
+{
+ // perform steps until autosync is idle
+ while(fAutosyncState!=autosync_idle) {
+ // process one state machine step in inactive mode
+ autosync_step(false);
+ }
+} // CLIENTAGENTCONFIG::autosync_stop
+
+
+// parse parameter from aText
+static const char *getParam(const char *aText, string &aParam, bool aNeedsSemicolon)
+{
+ aParam.erase();
+ // skip leading spaces
+ while (*aText && (*aText==' ')) aText++;
+ // must start with semicolon
+ if (!aNeedsSemicolon || (*aText && (*aText==';'))) {
+ if (aNeedsSemicolon) aText++; // skip it
+ // skip more spaces if any
+ while (*aText && (*aText==' ')) aText++;
+ char quotechar=0;
+ if (*aText==0x22) {
+ quotechar=*aText++; // remember quote char
+ }
+ // read parameter
+ while (*aText && (quotechar ? *aText!=quotechar : !isspace(*aText) && (*aText!=';'))) {
+ // read char
+ if (*aText=='\\') {
+ aText++;
+ if (*aText==0) break;
+ }
+ // add to result
+ aParam += *aText++;
+ }
+ // skip quote char if this was one
+ if (quotechar && *aText==quotechar) aText++;
+ }
+ return aText;
+} // getParam
+
+
+// get status information about autosync
+void CLIENTAGENTCONFIG::autosync_status(
+ TAutosyncModes &aAutosyncMode, // currently active mode
+ lineartime_t &aNextSync, // 0 if no scheduled nextsync
+ lineartime_t &aLastAlert, // 0 if no alert seen so far
+ uInt8 &aWhyNotFlags // autosync status flags
+)
+{
+ // return current mode
+ aAutosyncMode =
+ fAutosyncState==autosync_idle ?
+ autosync_none : // show "disabled" state while idle
+ fAutoSyncMode;
+ // special check for IPP (but only if we are really in autosync at this time)
+ #ifdef IPP_SUPPORT
+ if (aAutosyncMode==autosync_ipp) {
+ // check if we have the right activation level
+ if (fIPPState!=ippstate_subscribed) {
+ // reason for not being active is that we are not (yet) correctly activated and/or subscribed
+ aWhyNotFlags=autosync_status_notsubscribed;
+ aAutosyncMode=autosync_none; // show "disabled" while not activated or subscribed
+ }
+ }
+ else
+ #endif
+ {
+ // return reason for not being active right now
+ // (but omit wrongday/wrongtime as these are user settings and should not show up as errors
+ aWhyNotFlags=
+ (fAutosyncState==autosync_idle || fAutosyncState==autosync_timedsync_wait)
+ ? fWhyNotFlags & ~(autosync_status_wrongday+autosync_status_wrongtime)
+ : 0;
+ }
+ // return time of next known sync
+ aNextSync = fNextSyncAt;
+ // timed sync disabled by zero interval?
+ if (aAutosyncMode==autosync_timed) {
+ if (aNextSync==0) {
+ // reason for not being active is that for current mode (cradled/mobile), the interval is set to 0
+ aWhyNotFlags=autosync_status_disabled;
+ aAutosyncMode=autosync_none; // show "disabled" while timed autosync is not active
+ }
+ else if (aWhyNotFlags) {
+ // other reason for timed sync to be not active
+ aAutosyncMode=autosync_none; // show "disabled" while timed autosync is not active
+ }
+ }
+ // also report last alert
+ aLastAlert=fAutosyncLastAlert;
+} // CLIENTAGENTCONFIG::autosync_status
+
+
+
+/// @brief helper: check if environment factors (other than time), such as Mem or Bat, allow this level
+/// @return true if active, false if not (and updated aWhyNotFlags)
+static bool checkLevelEnvFactors(const TAutoSyncLevel &aLevel, uInt8 &aWhyNotFlags)
+{
+ bool levelActive;
+ #ifndef ENGINE_LIBRARY
+ uInt8 battlevel;
+ bool acpowered;
+ #ifdef ENGINEINTERFACE_SUPPORT
+ #error "fMasterPointer in AppBase links to owning TEngineInterface!!"
+ #endif
+ if (getPowerStatus(battlevel,acpowered,getXPTClientBase()->fMasterPointer)) {
+ // valid power info, check if this allows enabling level
+ levelActive = acpowered || battlevel>=aLevel.ChargeLevel;
+ }
+ else {
+ // no valid power info, enabled only if there's no min charge level defined
+ levelActive = aLevel.ChargeLevel==0;
+ }
+ #else
+ #ifdef RELEASE_VERSION
+ #error "reorganize batt and memory level checks in a XPT independent way"
+ #endif
+ levelActive=true;
+ #endif
+ // set flag if battery is reason for inactive level
+ if (!levelActive)
+ aWhyNotFlags |= autosync_status_lowbat;
+ // - check mem as well
+ if (levelActive) {
+ #ifndef ENGINE_LIBRARY
+ // - battery power
+ uInt8 memlevel;
+ #ifdef ENGINEINTERFACE_SUPPORT
+ #error "fMasterPointer in AppBase links to owning TEngineInterface!!"
+ #endif
+ if (getMemFreeStatus(memlevel,getXPTClientBase()->fMasterPointer)) {
+ // valid memory info, check if this allows enabling level
+ levelActive = memlevel>=aLevel.MemLevel;
+ }
+ else {
+ // no valid memory info, enabled only if there's no min free mem level defined
+ levelActive = aLevel.MemLevel==0;
+ }
+ #else
+ levelActive=true;
+ #endif
+ // set flag if memory is reason for inactive level
+ if (!levelActive)
+ aWhyNotFlags |= autosync_status_lowmem;
+ }
+ return levelActive;
+} // checkLevelEnvFactors
+
+
+
+/// @brief helper: check if this level is active.
+/// @return
+/// - true if yes: returns true and sets aMode.
+/// - false if no: returns false
+/// - aNextCheckNeeded is always updated, if 0, we do not need to check because this level cannot get active any time
+/// OR no timed activity is needed to activate it (as in case of SAN - as long as app starts when SMS arrives,
+/// that's sufficient.
+static bool checkAutoSyncLevel(const TAutoSyncLevel &aLevel, lineartime_t aNow, lineartime_t &aNextCheckNeeded, uInt8 &aWhyNotFlags)
+{
+ bool levelActive=false;
+ // check if we are in one of the selected weekdays (localtime)
+ uInt8 weekday=lineartime2weekday(aNow);
+ if ((aLevel.WeekdayMask & (1<<weekday))!=0) {
+ // aNow is in one of the selected weekdays, now check time window
+ // - get minutes of the day
+ uInt16 minuteofday = lineartime2timeonly(aNow) / secondToLinearTimeFactor / 60;
+ // - check if we are within
+ bool afterstart = minuteofday>=aLevel.StartDayTime;
+ bool beforend = minuteofday<aLevel.EndDayTime;
+ if (aLevel.StartDayTime<aLevel.EndDayTime) {
+ // normal case: start before end: just check if now is between
+ levelActive = afterstart && beforend;
+ if (levelActive) {
+ // - we are in window, calculate time when we'll be out again
+ aNextCheckNeeded = aNow+ (aLevel.EndDayTime-minuteofday) * 60 * secondToLinearTimeFactor;
+ }
+ else {
+ // - we are out of window, calculate time when we'll be in again
+ aNextCheckNeeded = aNow+ (aLevel.StartDayTime+(afterstart ? 24*60 : 0)-minuteofday) * 60 * secondToLinearTimeFactor;
+ }
+ }
+ else {
+ // Special case: end same or before start, active time before end OR after start
+ levelActive= beforend || afterstart;
+ if (levelActive) {
+ // - we are in window, calculate time when we'll be out again
+ aNextCheckNeeded = aNow+ (aLevel.EndDayTime+(afterstart ? 24*60 : 0)-minuteofday) * 60 *secondToLinearTimeFactor;
+ }
+ else {
+ // - we are out of window, calculate time when we'll be in again
+ aNextCheckNeeded = aNow+ (aLevel.StartDayTime-minuteofday) * 60 * secondToLinearTimeFactor;
+ }
+ }
+ // we know if we are in the time window and we know when we have to check next time
+ if (levelActive) {
+ // check for always enabled
+ if (
+ aLevel.WeekdayMask & 0x7F == 0x7F &&
+ aLevel.StartDayTime==0 &&
+ aLevel.EndDayTime==0
+ ) {
+ // unconditionally enabled level, we do not need to check for this again
+ aNextCheckNeeded=maxLinearTime;
+ }
+ /*
+ #ifdef IPP_SUPPORT
+ // now check secondary factors already (only for IPP, timed and SAN can check
+ // when actually SAN arrives or next sync time is reached)
+ if (aLevel.Mode==autosync_ipp) {
+ // - check them
+ levelActive = checkLevelEnvFactors(aLevel, aWhyNotFlags);
+ }
+ #endif
+ */
+ }
+ else {
+ aWhyNotFlags|=autosync_status_wrongtime;
+ }
+ } // if weekday
+ else if (aLevel.WeekdayMask==0) {
+ aWhyNotFlags|=autosync_status_wrongday;
+ // unconditionally disabled level, we do not need to check for this again
+ aNextCheckNeeded=maxLinearTime;
+ }
+ else {
+ aWhyNotFlags|=autosync_status_wrongday;
+ // calculate when we'll reach next enabled weekday
+ uInt8 w;
+ for (w=1; w<7; w++) {
+ if ((aLevel.WeekdayMask & (1<<((weekday+w) % 7)))!=0) break;
+ }
+ // we need to check w days from now
+ aNextCheckNeeded =
+ ((aNow / linearDateToTimeFactor)+w) * linearDateToTimeFactor;
+ }
+ // timed checks for entering or leaving this level are only needed if this is timed sync or IPP;
+ // for SAN, we don't need to start the app to enter or leave the level - we'll check when an SMS arrives
+ if (aLevel.Mode==autosync_serveralerted)
+ aNextCheckNeeded=maxLinearTime;
+ // return if level is active
+ return levelActive;
+} // checkAutoSyncLevel
+
+
+#ifdef SERVERALERT_SUPPORT
+
+/// @brief process SAN message and eventually trigger sync accordingly
+/// @note should only be called if Autosync is not globally disabled.
+/// @param[out] aAlertedProfile index of profile addressed by the alert (might be different from currently selected profile)
+/// @param[out] aUIMode should be checked by caller to eventually display an alert before syncing
+/// @return LOCERR_OK if SAN requests starting a sync (but does not actually trigger it yet, as we might want UI before)
+/// DB_NotAllowed (405) if everything would be fine except that autosync is disabled. GUI may alert this and allow sync nevertheless
+/// LOCERR_PROCESSMSG if something is wrong with the SAN
+/// DB_Forbidden (403) if credentials not ok
+/// DB_NotFound (404) if no matching profile or datastore found
+localstatus CLIENTAGENTCONFIG::autosync_processSAN(bool aLocalPush, void *aPushMsg, uInt32 aPushMsgSz, cAppCharP aHeaders, sInt32 &aAlertedProfile, UI_Mode &aUIMode)
+{
+ TSyError err;
+ int syncType;
+ uInt32 contentType;
+ string datastoreURI;
+ bool wantsSync=false; // assume no sync wanted
+
+ // normal SAN processing
+ SanPackage sanObj;
+
+ // pass SAN to obj
+ err=sanObj.PassSan(aPushMsg,aPushMsgSz);
+ if (err!=LOCERR_OK) {
+ PDEBUGPRINTFX(DBG_ERROR,("AUTOSYNC: PassSan: invalid SAN, err=%d",err));
+ return LOCERR_PROCESSMSG; // bad SAN
+ }
+
+ // try to get the first sync to have the header parsed by sanObj.
+ int nth=1; // start with first
+ err=sanObj.GetNthSync(
+ nth, // note: 0 here will only check header and number of syncs
+ syncType,
+ contentType,
+ datastoreURI
+ );
+ if (err!=LOCERR_OK) {
+ PDEBUGPRINTFX(DBG_ERROR,("AUTOSYNC: GetNthSync: invalid SAN, err=%d",err));
+ return LOCERR_PROCESSMSG; // bad SAN
+ }
+ // check if we have any autosync profile to check against
+ if (fAutosyncProfileLastidx<0) {
+ PDEBUGPRINTFX(DBG_ERROR,("AUTOSYNC: no autosync profile active"));
+ return DB_NotFound; // no target for SAN found
+ }
+ /// @todo: allow for switching to matching profile on the fly - which would allow
+ /// multi-server-push. For now, SAN must match current profile
+ PROFILERECORD *alertedProfile = &fAutosyncProfile;
+ // now server identifier (URL of server) should be ready, so we can check what
+ // profile is affected
+ #ifdef HARD_CODED_SERVER_URI
+ const char *serverURI = DEFAULT_SERVER_URI;
+ #else
+ const char *serverURI = alertedProfile->serverURI;
+ #endif
+ const char *uripos = strstr(serverURI,sanObj.fServerID.c_str());
+ if (!uripos) {
+ if (aLocalPush && strstr(serverURI,"/*")) {
+ // catchall for local push
+ PDEBUGPRINTFX(DBG_ERROR,("AUTOSYNC: ServerID in SAN (%s) does not match, but profile has catchall-URI for local push",sanObj.fServerID.c_str()));
+ }
+ else {
+ PDEBUGPRINTFX(DBG_ERROR,("AUTOSYNC: ServerID in SAN (%s) is not substring of current profile's Server URL (%s)",sanObj.fServerID.c_str(),serverURI));
+ return DB_NotFound; // no matching profile found
+ }
+ }
+ // server identifier is ok, check if there are any syncs at all
+ if (sanObj.fNSync<1) {
+ PDEBUGPRINTFX(DBG_ERROR,("AUTOSYNC: SAN does not contain any Sync requests"));
+ return LOCERR_PROCESSMSG; // bad SAN
+ }
+ // check Digest for SANs Version 1.2 and higher
+ if (sanObj.fProtocolVersion>=12) {
+ // get real size of SAN
+ size_t sanSize=aPushMsgSz; // not longer than this
+ err = sanObj.GetSanSize(aPushMsg,sanSize);
+ if (err!=LOCERR_OK) {
+ PDEBUGPRINTFX(DBG_ERROR,("AUTOSYNC: invalid SAN size (raw buffer = %ld), err=%d",aPushMsgSz,err));
+ return LOCERR_PROCESSMSG; // bad SAN
+ }
+ // now we have user/pw - check the digest
+ string pw;
+ getUnmangled(pw,alertedProfile->serverPassword,maxupwsiz);
+ err=sanObj.CreateDigest(
+ alertedProfile->serverUser,
+ pw.c_str(),
+ alertedProfile->lastNonce,
+ aPushMsg,
+ sanSize
+ );
+ if (err==LOCERR_OK) {
+ // have it checked
+ if (!sanObj.DigestOK(aPushMsg)) {
+ /// @todo %%% as long as it is unclear where nonce should come from, allow a "Synthesis" constant noce
+ err=sanObj.CreateDigest(
+ alertedProfile->serverUser,
+ pw.c_str(),
+ "Synthesis",
+ aPushMsg,
+ sanSize
+ );
+ if (err==LOCERR_OK)
+ err = sanObj.DigestOK(aPushMsg) ? LOCERR_OK : DB_Forbidden;
+ }
+ else
+ err=LOCERR_OK;
+ }
+ // - @todo %%% missing for the moment, needs some work on san.cpp first
+ if (err!=LOCERR_OK) {
+ PDEBUGPRINTFX(DBG_ERROR,("AUTOSYNC: invalid SAN digest, err=%hd",err));
+ return err; // digest not ok
+ }
+ } // SAN with Digest
+ // store some info from SAN
+ aAlertedProfile = fAutosyncProfileLastidx; // %%% for now, we only check the current profile
+ aUIMode=sanObj.fUI_Mode; // pass UI mode out to allow GUI to react
+ // - adjust UI mode if not specified
+ if (aUIMode==UI_not_specified) {
+ // not specified: in background for local sync, visible for remote sync (which will not run unless AutoSync is generally on!)
+ aUIMode = aLocalPush ? UI_background : UI_informative;
+ }
+ fAutosyncSANSessionID = sanObj.fSessionID;
+ // process the sync requests, first one is already here
+ do {
+ // alert by name, force sync (= sync wether target is enabled or not)
+ // if we have UI interaction or localPush, we also alert those datastores that have never synced so far
+ PDEBUGPRINTFX(DBG_HOT,("AUTOSYNC: SAN requests alert %d for '%s', content type code = 0x%lX",syncType,datastoreURI.c_str(),contentType));
+ if (alertDbByName(datastoreURI.c_str(), "",true,syncType,aUIMode==UI_user_interaction || aLocalPush)) {
+ wantsSync=true;
+ }
+ // check if there are more
+ nth++;
+ err=sanObj.GetNthSync(
+ nth,
+ syncType,
+ contentType,
+ datastoreURI
+ );
+ } while(err==LOCERR_OK);
+ if (err!=404) {
+ PDEBUGPRINTFX(DBG_ERROR,("AUTOSYNC: error processing %d-th sync request",nth,err));
+ // but do not exit - it might still be that we have started alerted a sync already
+ }
+ if (wantsSync) {
+ // update server version according to SAN SyncML version
+ TSyncMLVersions alertVers;
+ switch (sanObj.fProtocolVersion) {
+ case 12 : alertVers = syncml_vers_1_2; break;
+ case 11 : alertVers = syncml_vers_1_1; break;
+ case 10 : alertVers = syncml_vers_1_0; break;
+ default : alertVers = syncml_vers_unknown; break;
+ }
+ PDEBUGPRINTFX(DBG_TRANSP,("AUTOSYNC: SAN requests SyncML version %d.%d",sanObj.fProtocolVersion/10,sanObj.fProtocolVersion%10));
+ if (alertedProfile->lastSyncMLVersion!=alertVers) {
+ // adjust it
+ alertedProfile->lastSyncMLVersion=alertVers;
+ writeProfile(aAlertedProfile,*alertedProfile);
+ }
+ // check if the profile is enabled for autosync at all
+ /// @todo implement checking through all matching profiles - for now, only current is active
+ if (
+ (fAutoSyncMode!=autosync_serveralerted && fAutoSyncMode!=autosync_ipp) || // no push mode selected for now
+ fAutosyncState==autosync_idle // or active but disabled
+ ) {
+ // SAN is fine, however we are not in Autosync mode
+ // Note: datastores are already alerted in DS config - GUI may choose to start a sync even if Autosync is off
+ return DB_NotAllowed;
+ }
+ else {
+ // we are in autosync mode, but check environment first
+ fEnvCheckedAt = getSystemNowAs(TCTX_SYSTEM);
+ if (fCurrentLevelIndex>=0 && !checkLevelEnvFactors(fAutosyncProfile.AutoSyncLevel[fCurrentLevelIndex], fWhyNotFlags)) {
+ // environment factors disallow syncing now
+ PDEBUGPRINTFX(DBG_ERROR,("AUTOSYNC: SAN wants sync, but Bat/Mem of current level does not allow -> re-checking levels"));
+ fNextCheckAt=0; // force re-checking for active levels
+ return DB_NotAllowed;
+ }
+ }
+ }
+ // everything fine
+ return wantsSync ? LOCERR_OK : DB_NotFound; // no matching target DS found
+} // autosync_processSAN
+
+#endif
+
+
+
+// performs next step in the state machine
+void CLIENTAGENTCONFIG::autosync_step(bool aActive)
+{
+ #ifdef IPP_SUPPORT
+ TcpRc_t tcperr;
+ localstatus err;
+ uInt16 connwaitseconds;
+ const char *q,*p;
+ const int bufsiz=2000;
+ unsigned char buffer[bufsiz];
+ unsigned long received;
+ #endif
+ string s;
+ TARGETRECORD target;
+ sInt32 idx,seq;
+ CLIENTDSCONFIG *dscfgP;
+ uInt16 interval;
+ string dbname;
+ string filtercgi;
+ bool done;
+ bool newmode;
+ lineartime_t now,nowminute;
+ TAutoSyncStates newstate;
+ bool isCradled;
+
+ // get clientbase object
+ #ifndef ENGINE_LIBRARY
+ TXPTClientBase *clientBaseP = getXPTClientBase();
+ #endif
+ do {
+ // assume no state change
+ newstate=fAutosyncState;
+ fCurrentLevelIndex = -1; // none chosen yet
+ // get current time
+ now=getSystemNowAs(TCTX_SYSTEM);
+ nowminute = (now / 60 / secondToLinearTimeFactor) * 60 * secondToLinearTimeFactor; // precision by minute only
+ newmode=false; // assume no mode change
+ done=true; // assume done after processing current state
+ // sanity check for last alert:
+ if (fAutosyncLastAlert>now)
+ fAutosyncLastAlert=0; // reset when "last" alert seems to be in the future - which is not possible
+ // check for new mode
+ if (fNextCheckAt==0 || fNextCheckAt<=now) {
+ // we need to find relevant autosync params
+ fNextCheckAt=maxLinearTime; // infinity
+ fNextWakeupCheckAt=maxLinearTime; // infinity
+ lineartime_t nextcheck;
+ uInt8 lvl;
+ fWhyNotFlags=0;
+ bool found=false;
+ for (lvl=0; lvl<NUM_AUTOSYNC_LEVELS; lvl++) {
+ // check if this level is a candidate
+ uInt8 newWhyNotFlags=0;
+ bool newFound=false;
+ bool isCandidate = checkAutoSyncLevel(fAutosyncProfile.AutoSyncLevel[lvl],nowminute,nextcheck,newWhyNotFlags);
+ // check if level is active
+ if (isCandidate) {
+ // found candidate for active level
+ // check if environment allows activating this level
+ bool envOk = checkLevelEnvFactors(fAutosyncProfile.AutoSyncLevel[lvl], newWhyNotFlags);
+ fEnvCheckedAt = now;
+ // Note: for timed and SAN, environment is not relevant at this time,
+ // because they don't need the app to be active while waiting for next sync time/event
+ // Environment is important for these only when a sync is actually to be started
+ if (fAutosyncProfile.AutoSyncLevel[lvl].Mode==autosync_ipp) {
+ // only for IPP, we cannot make the level active if environment factors are not ok
+ isCandidate = envOk;
+ }
+ // activate it if no other already activated
+ if (!found && isCandidate) {
+ newFound=true;
+ fNewMode=fAutosyncProfile.AutoSyncLevel[lvl].Mode;
+ // make sure we do not switch on DMU if it is not enabled
+ if (fNewMode==autosync_ipp && !isFeatureEnabled(&fAutosyncProfile,APP_FTR_IPP)) {
+ fNewMode=autosync_none; // force DMU/IPP off if not licensed
+ fWhyNotFlags=autosync_status_notlicensed; // show why
+ }
+ }
+ }
+ // update overall whyNotFlags
+ fWhyNotFlags |= newWhyNotFlags;
+ // update when to check and/or wakeup next
+ if (nextcheck<fNextCheckAt)
+ fNextCheckAt=nextcheck;
+ if (nextcheck<fNextWakeupCheckAt)
+ fNextWakeupCheckAt=nextcheck;
+ if (!isCandidate) {
+ // this level cannot be made active. If this is an IPP level and it is not a candidate
+ // NOT due to time settings, we must make sure the environment is checked again soon
+ if (
+ fAutosyncProfile.AutoSyncLevel[lvl].Mode==autosync_ipp &&
+ ((newWhyNotFlags & (autosync_status_wrongday+autosync_status_wrongtime))==0)
+ ) {
+ // check if reason for not activating this level is memory
+ // or battery - in that case we would need to check again soon
+ lineartime_t t=maxLinearTime;
+ // - is it memory?
+ if (newWhyNotFlags & autosync_status_lowmem)
+ t=now+AUTOSYNC_MEMCHECK_INTERVAL*secondToLinearTimeFactor;
+ if (t<fNextCheckAt) fNextCheckAt=t;
+ if (t<fNextWakeupCheckAt) fNextWakeupCheckAt=t;
+ // - is it battery?
+ if (newWhyNotFlags & autosync_status_lowbat)
+ t=now+AUTOSYNC_BATTCHECK_INTERVAL*secondToLinearTimeFactor;
+ if (t<fNextCheckAt) fNextCheckAt=t;
+ if (t<fNextWakeupCheckAt) fNextWakeupCheckAt=t;
+ }
+ }
+ // show summary of check
+ if (PDEBUGTEST(DBG_TRANSP)) {
+ string ts1,ts2;
+ StringObjTimestamp(ts1,now);
+ if (nextcheck==maxLinearTime)
+ ts2="<never>";
+ else
+ StringObjTimestamp(ts2,nextcheck);
+ PDEBUGPRINTFX(DBG_TRANSP,(
+ "AUTOSYNC: Checked Level %d at %s - %s active, whynot=0x%1X, next check at %s",
+ lvl,
+ ts1.c_str(),
+ newFound ? "is" : (isCandidate ? "is not (but could be)" : "cannot be"),
+ (int)newWhyNotFlags,
+ ts2.c_str()
+ ));
+ }
+ // exit if we have found an active level
+ if (newFound) {
+ found=true;
+ fCurrentLevelIndex = lvl;
+ // check if activating this level is memory or battery dependent
+ lineartime_t t=maxLinearTime;
+ if (fAutosyncProfile.AutoSyncLevel[lvl].MemLevel>0)
+ t=now+AUTOSYNC_MEMCHECK_INTERVAL*secondToLinearTimeFactor;
+ if (t<fNextCheckAt) fNextCheckAt=t;
+ if (fAutosyncProfile.AutoSyncLevel[lvl].ChargeLevel>0)
+ t=now+AUTOSYNC_BATTCHECK_INTERVAL*secondToLinearTimeFactor;
+ if (t<fNextCheckAt) fNextCheckAt=t;
+ // if we are really active, there's no whynot to report
+ if (fNewMode!=autosync_none && fNewMode!=autosync_timed)
+ fWhyNotFlags=0;
+ // show level
+ if (PDEBUGTEST(DBG_TRANSP)) {
+ string ts;
+ StringObjTimestamp(ts,fNextCheckAt);
+ PDEBUGPRINTFX(DBG_TRANSP,(
+ "AUTOSYNC: Chosen Level %d, new mode=%s, whynot=0x%1X, next check (including regular batt/mem checks) at %s",
+ lvl,
+ AutoSyncModeNames[fNewMode],
+ (int)fWhyNotFlags,
+ ts.c_str()
+ ));
+ }
+ //break; // %%% not any more - we now always go through all levels to make sure we have all params for switching
+ }
+ } // for all autosync levels
+ // additionally check global conditions that could disable autosync
+ if (found && fNewMode!=autosync_none) {
+ // the device must be connectable
+ #ifndef ENGINE_LIBRARY
+ if (!clientBaseP->isConnectable(true)) {
+ found=false;
+ fWhyNotFlags=autosync_status_notconnectable;
+ // check availability of connection soon again
+ fNextCheckAt=now+AUTOSYNC_CONNCHECK_INTERVAL*secondToLinearTimeFactor;
+ }
+ else
+ #endif
+ if (fAutosyncState==autosync_ipp_answerwait) {
+ // is connectable and we are waiting for an answer
+ // now check for changes in cradled/mobile status
+ #ifndef ENGINE_LIBRARY
+ isCradled = clientBaseP->isDeviceCradled();
+ #else
+ isCradled = true;
+ #endif
+ if (fCurrentlyCradled!=isCradled) {
+ fCurrentlyCradled=isCradled;
+ #ifdef IPP_SUPPORT
+ // seems this has changed, make sure we do a disconnect/reconnect
+ // %%% is a bit ugly to influence the state machine here, but
+ // on the other hand it's here we check so we act here as well...
+ PDEBUGPRINTFX(DBG_HOT,("Detected change to %s state -> reconnect IPP",fCurrentlyCradled ? "cradled" : "mobile"));
+ newstate=autosync_ipp_disconnect;
+ fAutosyncRetries=2; // retry almost immediately
+ done=false;
+ #endif
+ }
+ }
+ }
+ // switch autosync off if none of the levels is active
+ if (!found) {
+ fNewMode=autosync_none; // disable
+ }
+ }
+ // if we have a mode switch pending
+ if (fNewMode!=fAutoSyncMode) {
+ // delay mode switch until we have reached autosync_idle in between
+ if (fAutosyncState==autosync_idle) {
+ // show mode change
+ PDEBUGPRINTFX(DBG_HOT,(
+ "AUTOSYNC: ** MODE CHANGED from %s to %s",
+ AutoSyncModeNames[fAutoSyncMode],
+ AutoSyncModeNames[fNewMode]
+ ));
+ // now we can switch modes
+ fAutoSyncMode=fNewMode;
+ // this is a real mode switch (not only temporary deactivation),
+ // - last alert is cleared when we are now actively going idle. For switching
+ // between DMU and timed, we'll keep the last alert.
+ if (fNewMode==autosync_none) {
+ // - forget last alert, such that timed syncs will immediately start after re-start
+ fAutosyncLastAlert=0; // never so far
+ }
+ }
+ else {
+ // stop current activity first before starting new
+ aActive=false;
+ // show pending mode change
+ PDEBUGPRINTFX(DBG_TRANSP,(
+ "AUTOSYNC: pending mode change from %s to %s - waiting for state to get idle",
+ AutoSyncModeNames[fAutoSyncMode],
+ AutoSyncModeNames[fNewMode]
+ ));
+ }
+ }
+ // now determine IPP state
+ #ifdef IPP_SUPPORT
+ if (isFeatureEnabled(&fAutosyncProfile,APP_FTR_IPP)) {
+ if (fIPPState==ippstate_unknown) {
+ // we have restarted, determine mode from what we have in the settings
+ // (NOTE: this is independent of current autosync mode!)
+ fIPPState=ippstate_idle; // Assume not activated at all
+ fConversationStage=ippconvstage_login; // we need to log in first
+ fDSCfgIterator = fDatastores.end();
+ if (fAutosyncProfile.ippSettings.id[0]!=0) {
+ // we have a DMUCI/deviceID
+ // - check further params that must be present for either state
+ if (fAutosyncProfile.ippSettings.method==IPP_METHOD_DMUS2G) {
+ fIPPState=ippstate_activated; // with a DMUCI, we are at least activated
+ // method IPP_METHOD_DMUS2G: if we have creds and server, we think we are subscribed
+ if (
+ fAutosyncProfile.ippSettings.srv[0]!=0 &&
+ fAutosyncProfile.ippSettings.cred[0]!=0
+ )
+ fIPPState=ippstate_subscribed;
+ }
+ else {
+ // method IPP_METHOD_DMU0 and OMP: when we have a server, we are subscribed
+ // Note: for OMP, the actual SUBSCRIBE command will be issued after connecting later.
+ // The "subscribed" term here relates to DMU terminology.
+ if (fAutosyncProfile.ippSettings.srv[0]!=0)
+ fIPPState=ippstate_subscribed;
+ }
+ }
+ if (fAutosyncProfile.ippSettings.method!=IPP_METHOD_DMU0) {
+ PDEBUGPRINTFX(DBG_TRANSP,(
+ "AUTOSYNC: re-evaluated IPP state from available settings = %s",
+ IPPStateNames[fIPPState]
+ ));
+ }
+ }
+ }
+ #endif
+ // now let state machine run
+ switch (fAutosyncState) {
+ case autosync_idle:
+ // if idle and we want autosync active, we must start it now
+ if (aActive && fAutoSyncMode!=autosync_none) {
+ newstate=autosync_start;
+ done=false;
+ }
+ break;
+ case autosync_start:
+ if (!aActive) {
+ newstate=autosync_idle;
+ break;
+ }
+ // select which loop to start in the state machine
+ if (fAutoSyncMode==autosync_timed) {
+ // go to timed sync checks
+ newstate=autosync_timedsync;
+ done=false;
+ }
+ #ifdef IPP_SUPPORT
+ else if (fAutoSyncMode==autosync_ipp) {
+ // for push methods, we do NOT want a timed sync immediately,
+ // but only after a certain time after the last alert.
+ if (fAutosyncLastAlert==0)
+ fAutosyncLastAlert=now; // assume we have just synced (to avoid a sync shortly after start)
+ // try to connect
+ newstate=autosync_ipp_connect;
+ fAutosyncRetries=0;
+ done=false;
+ }
+ #endif
+ // otherwise, just stay in autosync_start (e.g. for SAN push)
+ break;
+ case autosync_timedsync:
+ if (!aActive) {
+ newstate=autosync_idle;
+ break;
+ }
+ // check for timed sync
+ #ifndef ENGINE_LIBRARY
+ isCradled=clientBaseP->isDeviceCradled();
+ #else
+ isCradled=true;
+ #endif
+ interval =
+ isCradled ?
+ fAutosyncProfile.TimedSyncCradledPeriod :
+ fAutosyncProfile.TimedSyncMobilePeriod;
+ // calculate time of next sync, minute precision
+ if (fAutosyncLastAlert==0) {
+ if (interval!=0)
+ fNextSyncAt=now; // if we don't know when we've last synced, sync NOW!
+ }
+ else {
+ if (interval==0)
+ break; // do not enter timed sync wait if interval is zero
+ fNextSyncAt = fAutosyncLastAlert + (interval * 60 * secondToLinearTimeFactor);
+ }
+ if (PDEBUGTEST(DBG_TRANSP)) {
+ string ts1,ts2;
+ StringObjTimestamp(ts1,now);
+ StringObjTimestamp(ts2,fNextSyncAt);
+ PDEBUGPRINTFX(DBG_TRANSP,(
+ "AUTOSYNC: Entered timed sync at %s, interval(%s)=%hd min -> next sync at %s",
+ ts1.c_str(),
+ isCradled ? "cradled" : "mobile",
+ interval,
+ ts2.c_str()
+ ));
+ }
+ // update whyNot flags so we'll show the whynot cause as status
+ if (fCurrentLevelIndex>=0) {
+ checkLevelEnvFactors(fAutosyncProfile.AutoSyncLevel[fCurrentLevelIndex], fWhyNotFlags);
+ fEnvCheckedAt = now;
+ }
+ // go waiting
+ newstate=autosync_timedsync_wait;
+ done=false;
+ break;
+ case autosync_timedsync_wait:
+ if (!aActive) {
+ newstate=autosync_idle;
+ fNextSyncAt=0; // when we are not in timed sync, we don't know when next sync will be
+ break;
+ }
+ // remove whynots after a while
+ if (fWhyNotFlags && (now > fEnvCheckedAt+AUTOSYNC_ENVERRSDISPLAY_SECS*secondToLinearTimeFactor))
+ fWhyNotFlags = 0; // forget them until we re-evaluate them
+ // check for timed sync
+ if (fNextSyncAt!=0 && now>=fNextSyncAt) {
+ // we should start a timed sync - but check environment first (might have changed since we checked last when entering timed sync)
+ if (fCurrentLevelIndex<0) {
+ fNextCheckAt=0; // force immediate check for active levels
+ break; // don't check before we know which level we are in
+ }
+ fEnvCheckedAt = now;
+ if(!checkLevelEnvFactors(fAutosyncProfile.AutoSyncLevel[fCurrentLevelIndex], fWhyNotFlags)) {
+ // environment factors disallow syncing now
+ PDEBUGPRINTFX(DBG_ERROR,("AUTOSYNC: timed sync scheduled for now, but Bat/Mem does not allow -> re-checking levels"));
+ fNextCheckAt=0; // force immediate check for active levels
+ fNextSyncAt=0; // cancel this sync
+ fAutosyncLastAlert = now; // prevent immediate retries, treat as if a sync was alerted
+ newstate=autosync_timedsync; // calc next timed sync
+ break;
+ }
+ // we should start a autosync
+ PDEBUGPRINTFX(DBG_TRANSP,("AUTOSYNC: starting timed sync session"));
+ // we need to re-evaluate the time of next sync, and eventually
+ // enter another mode (could be that we've got here to perform
+ // a safeguard timed sync while in push mode)
+ newstate=autosync_start;
+ // if any datastores are enabled, this will now cause
+ // a sync, so count this as alert
+ fAutosyncLastAlert=now;
+ // autosync-alert all enabled local datastores
+ seq=0;
+ do {
+ idx=findTargetIndex(fAutosyncProfile.profileID,seq++);
+ if (idx<0) break; // no more targets
+ getTarget(idx,target);
+ // get related datastore in config
+ dscfgP = static_cast<CLIENTDSCONFIG *>(getLocalDS(target.dbname));
+ // check if we would cause a zap on either side
+ if (target.enabled) {
+ if (!(
+ target.forceSlowSync &&
+ (target.syncmode==smo_fromclient || target.syncmode==smo_fromserver)
+ )) {
+ dscfgP->fAutosyncForced=false;
+ dscfgP->fAutosyncAlerted=true;
+ dscfgP->fAutosyncPathCGI.erase();
+ // alerted at least one, autosync is in alerted state now
+ fAutosyncAlerted=true;
+ PDEBUGPRINTFX(DBG_HOT,("AUTOSYNC: *** ALERTED timed autosync for all enabled datastores"));
+ }
+ else {
+ // prevent autosync because of possible zap
+ PDEBUGPRINTFX(DBG_ERROR,("AUTOSYNC: datastore '%s' would cause client or server to be totally overwritten -> do not do timed autosync",dscfgP->getName()));
+ }
+ }
+ } while (true);
+ #ifdef SYDEBUG
+ if (!fAutosyncAlerted) {
+ PDEBUGPRINTFX(DBG_ERROR,("AUTOSYNC: No timed sync possible because no datastore available for sync"));
+ }
+ #endif
+ }
+ break;
+ #ifdef IPP_SUPPORT
+ case autosync_ipp_connectwait:
+ if (!aActive) {
+ newstate=autosync_idle;
+ break;
+ }
+ // start wait phase
+ fAutosyncLastStateChange=now;
+ newstate=autosync_ipp_connectwaiting;
+ // check if we need to resubscribe instead of connecting
+ // (IPP_METHOD_DMUS2G only)
+ if (
+ fAutosyncProfile.ippSettings.method==IPP_METHOD_DMUS2G &&
+ fAutosyncRetries>=IPP_RESUBSCRIBE_RETRIES
+ ) {
+ // check if we need to reactivate
+ if (fAutosyncRetries>=IPP_REACTIVATE_RETRIES) {
+ // force re-activation
+ fIPPState=ippstate_idle;
+ #ifdef SYDEBUG
+ if (fAutosyncRetries==IPP_REACTIVATE_RETRIES)
+ PDEBUGPRINTFX(DBG_ERROR,("AUTOSYNC: too many unsuccessful retries (#%hd) -> cleared activation",fAutosyncRetries));
+ #endif
+ if (fAutosyncRetries>=IPP_BLOCK_RETRIES) {
+ // prevent hectic syncs in case nothing works at all
+ #ifdef SYDEBUG
+ if (fAutosyncRetries==IPP_BLOCK_RETRIES)
+ PDEBUGPRINTFX(DBG_ERROR,("AUTOSYNC: too many unsuccessful retries (#%hd) -> IPP deactivated",fAutosyncRetries));
+ #endif
+ newstate=autosync_idle;
+ break;
+ }
+ }
+ if (fUnsuccessfulAlerts==0) {
+ // resubscribe
+ PDEBUGPRINTFX(DBG_ERROR,("AUTOSYNC: too many unsuccessful retries (#%hd) -> requesting resubscription",fAutosyncRetries));
+ newstate=autosync_ipp_resubscribe;
+ break;
+ }
+ // in case of unsuccesful alerts pending, we'll just enter the waiting state
+ }
+ PDEBUGPRINTFX(DBG_TRANSP,("AUTOSYNC: waiting to connect before next retry (#%hd)",fAutosyncRetries));
+ done=false;
+ break;
+ case autosync_ipp_connectwaiting:
+ // check for deactivation
+ if (!aActive) {
+ newstate=autosync_idle;
+ break;
+ }
+ // wait until we can reconnect
+ connwaitseconds = IPP_MIN_RECONNECTWAIT<<(fAutosyncRetries-1);
+ if (connwaitseconds>IPP_MAX_RECONNECTWAIT)
+ connwaitseconds=IPP_MAX_RECONNECTWAIT;
+ if (fAutosyncRetries==0 || now>fAutosyncLastStateChange+connwaitseconds*secondToLinearTimeFactor) {
+ // waited long enough
+ newstate=autosync_ipp_connect;
+ PDEBUGPRINTFX(DBG_TRANSP,("AUTOSYNC: waited for %hd seconds in autosync_ipp_connectwaiting",connwaitseconds));
+ done=false;
+ }
+ break;
+ case autosync_ipp_resubscribe:
+ // IPP_METHOD_DMUS2G only:
+ // force a sync session for resubscription
+ PDEBUGPRINTFX(DBG_HOT,("AUTOSYNC: Performing a sync session to (re-)subscribe now, datastores=%s",fAutosyncProfile.ippSettings.timedds));
+ if (fIPPState>ippstate_activated) fIPPState=ippstate_activated; // forget subscription if we had one
+ // - wipe server address to make sure we don't use old, expired one any more
+ fAutosyncProfile.ippSettings.srv[0]=0;
+ // - wipe credential
+ fAutosyncProfile.ippSettings.cred[0]=0;
+ // - do an implicit sync
+ newstate=autosync_alert_implicit;
+ done=false;
+ break;
+ case autosync_alert_implicit:
+ // state is idle after an implicit alert
+ newstate=autosync_idle;
+ // only alert if no unsuccessful alerts are pending
+ if (fUnsuccessfulAlerts==0) {
+ // alert all datastores in fImplicitDatastores
+ p = fAutosyncProfile.ippSettings.timedds;
+ while (*p) {
+ // find next comma or EOS
+ for (q=p; *q!=',' && *q!=0; q++);
+ dbname.assign(p,q-p);
+ // alert for sync, no extra filter
+ if (alertDbByName(dbname.c_str(),"",true)) {
+ fAutosyncAlerted=true;
+ fAutosyncLastAlert=now;
+ }
+ // next name
+ if (*q) q++; // skip delimiter
+ p=q; // start after next delimiter, if any
+ }
+ }
+ // - now alert a timed sync session
+ done=true;
+ break;
+ case autosync_ipp_connect:
+ // check if we are connectable at this time
+ if (!clientBaseP->isConnectable(true)) {
+ // we cannot connect now, disable autosync for now
+ newstate=autosync_idle;
+ // effective immediately
+ done=false;
+ // force re-checking of autosync mode (which will keep autosync off until connectable)
+ fNextCheckAt=0;
+ }
+ if (fAutosyncProfile.ippSettings.method==IPP_METHOD_DMU0) {
+ // use the "maxinterval" sync interval in addition to push
+ if (
+ (fAutosyncLastAlert!=0) &&
+ (fAutosyncProfile.ippSettings.maxinterval!=0) &&
+ (now>fAutosyncLastAlert+fAutosyncProfile.ippSettings.maxinterval*60*secondToLinearTimeFactor) &&
+ !(fAutosyncProfile.ippSettings.timedds[0]==0) &&
+ (fUnsuccessfulAlerts==0)
+ ) {
+ // no push alert within maxinterval, perform an implicit timed sync with the datastores from timedds
+ newstate=autosync_alert_implicit;
+ PDEBUGPRINTFX(DBG_HOT,("AUTOSYNC: no alert within maxinterval=%hd mins, performing an implicit autosync session now",fAutosyncProfile.ippSettings.maxinterval));
+ done=false;
+ break;
+ }
+ }
+ else if (fAutosyncProfile.ippSettings.method==IPP_METHOD_DMUS2G) {
+ // enhanced protocol with router/server architecture
+ // - check if we need to trigger a sync session to (re-)activate/subscribe and get creds
+ // - we start a sync session if we are not subscribed (and we haven't tried for at least IPP_MIN_RESUBSCRIBEWAIT)
+ // - we start a sync session if current credential has timed out
+ if (
+ (
+ (fIPPState!=ippstate_subscribed && (fAutosyncRetries==0 || now>fAutosyncLastAlert+IPP_MIN_RESUBSCRIBEWAIT*secondToLinearTimeFactor)) ||
+ (
+ (fAutosyncLastAlert!=0) &&
+ (fAutosyncProfile.ippSettings.maxinterval!=0) &&
+ (now>fAutosyncLastAlert+fAutosyncProfile.ippSettings.maxinterval*60*secondToLinearTimeFactor)
+ )
+ ) &&
+ !(fAutosyncProfile.ippSettings.timedds[0]==0) &&
+ (fUnsuccessfulAlerts==0)
+ ) {
+ PDEBUGPRINTFX(DBG_HOT,("AUTOSYNC: no credential or no alert within current credential lifetime=%d mins, performing an implicit sync to (re-)subscribe now",fAutosyncProfile.ippSettings.maxinterval));
+ // we need to resubscribe
+ newstate=autosync_ipp_resubscribe;
+ // this is a failed attempt to connect, make sure fAutosyncRetries is not zero any more
+ fAutosyncRetries++;
+ done=false;
+ break;
+ }
+ }
+ // if we have no server here, state must be re-evaluated
+ if (fAutosyncProfile.ippSettings.srv[0]==0)
+ fIPPState=ippstate_unknown;
+ // check for unsubscribed again
+ if (fIPPState!=ippstate_subscribed) {
+ // not subscribed (no server address) -> keep waiting
+ PDEBUGPRINTFX(DBG_TRANSP,("AUTOSYNC: IPP connection cannot be made because IPP state is not subscribed -> wait again"));
+ if (fAutosyncRetries<IPP_RESUBSCRIBE_RETRIES)
+ fAutosyncRetries=IPP_RESUBSCRIBE_RETRIES; // make sure we do a resubscribe at least
+ else
+ fAutosyncRetries++;
+ newstate=autosync_ipp_connectwait;
+ break;
+ }
+ // prepare for connecting the server (establish connection like GPRS, BT etc.)
+ PDEBUGPRINTFX(DBG_TRANSP,("AUTOSYNC: autosync_ipp_connect: establishing BACKGROUND connection (GPRS etc.)"));
+ StringObjPrintf(s,"http://%s:%d",fAutosyncProfile.ippSettings.srv,fAutosyncProfile.ippSettings.port);
+ err=clientBaseP->establishConnectionFor(s.c_str(),true); // background priority connection
+ if (err!=LOCERR_OK) {
+ // failed, wait until trying again
+ PDEBUGPRINTFX(DBG_ERROR,("AUTOSYNC: autosync_ipp_connect: failed establishing connection, err=%hd",err));
+ newstate=autosync_ipp_connectwait;
+ // just not being able to establish a connection MUST NOT trigger a resubscription!
+ if (fAutosyncRetries<IPP_RESUBSCRIBE_RETRIES-1)
+ fAutosyncRetries++; // count retry if it would not cause a resubscription.
+ break;
+ }
+ // connect to the server
+ PDEBUGPRINTFX(DBG_TRANSP,("AUTOSYNC: autosync_ipp_connect: connecting to IPP server '%s'",s.c_str()));
+ StringObjPrintf(s,"%s:%d",fAutosyncProfile.ippSettings.srv,fAutosyncProfile.ippSettings.port);
+ tcperr = tcpOpenConnectionEx2(
+ s.c_str(),
+ &fAutosyncIPPSocket,
+ fAutosyncProfile.ippSettings.port==443 ? "cS" : "c", // SSL if port is 443, non-SSL otherwise
+ 10, // do not hang longer than 10 seconds
+ NULL
+ );
+ if (tcperr!=TCP_RC_OK) {
+ // failed, wait until trying again
+ PDEBUGPRINTFX(DBG_ERROR,("AUTOSYNC: autosync_ipp_connect: failed connecting IPP server, tcperr=%d",tcperr));
+ newstate=autosync_ipp_connectwait;
+ fAutosyncRetries++; // count retry
+ break;
+ }
+ #ifdef TCP_LEAK_DEBUG
+ fTcpLeakCounter++;
+ #endif
+ // connection is open, send request
+ newstate=autosync_ipp_sendrequest;
+ done=false;
+ break;
+ case autosync_ipp_disconnect:
+ // disconnect
+ PDEBUGPRINTFX(DBG_TRANSP,("AUTOSYNC: autosync_ipp_disconnect: disconnecting..."));
+ if (fAutosyncIPPSocket) {
+ tcpCloseConnection(&fAutosyncIPPSocket);
+ fAutosyncIPPSocket=0;
+ #ifdef TCP_LEAK_DEBUG
+ fTcpLeakCounter--;
+ #endif
+ }
+ // release connection (force down if retried three times)
+ clientBaseP->releaseConnection(fAutosyncRetries>=IPP_FORCEDOWN_RETRIES);
+ PDEBUGPRINTFX(DBG_TRANSP,("AUTOSYNC: disconnected, entering wait for next connection..."));
+ newstate=autosync_ipp_connectwait;
+ break;
+ case autosync_ipp_sendrequest:
+ // check for deactivation
+ if (!aActive) {
+ newstate=autosync_ipp_disconnect;
+ done=false;
+ break;
+ }
+ // send IPP request
+ // - clear answer string
+ fAutosyncIPPAnswer.erase();
+ // - nothing known about headers or conten length yet
+ fHeaderLength = 0;
+ fContentLength = 0;
+ if (fAutosyncProfile.ippSettings.method==IPP_METHOD_DMU0) {
+ // DMU method 0 (aka IPP, aka DMU demo)
+ // - create GET request string
+ StringObjPrintf(s,
+ "GET %s%s\r\n",
+ fAutosyncProfile.ippSettings.path,
+ fAutosyncProfile.ippSettings.id
+ );
+ }
+ else if (fAutosyncProfile.ippSettings.method==IPP_METHOD_DMUS2G) {
+ // DMU method 1 (s2g router/server architecture)
+ // - create LOGIN request string
+ StringObjPrintf(s,
+ "LOGIN %s%s\r\n"
+ "cred=%s\r\n"
+ "\r\n",
+ fAutosyncProfile.ippSettings.path,
+ fAutosyncProfile.ippSettings.id,
+ fAutosyncProfile.ippSettings.cred
+ );
+ }
+ else if (fAutosyncProfile.ippSettings.method==IPP_METHOD_OMP) {
+ // Oracle Mobile Push
+ string data;
+ data.erase();
+ // - prepare POST request
+ StringObjPrintf(s,
+ "POST %s HTTP/1.1\r\n"
+ "Host: %s\r\n"
+ "Content-Type: application/vnd.mobilepush\r\n",
+ fAutosyncProfile.ippSettings.path,
+ fAutosyncProfile.ippSettings.srv
+ );
+ // - now add specific command
+ if (fConversationStage==ippconvstage_login) {
+ // need login first
+ string pw;
+ getUnmangled(pw, fAutosyncProfile.serverPassword);
+ // we need to login first
+ StringObjPrintf(data,
+ "LOGIN %s#P%s %s\r\n",
+ fAutosyncProfile.serverUser,
+ fAutosyncProfile.ippSettings.id,
+ pw.c_str()
+ );
+ }
+ else {
+ // not login, add the cookie
+ StringObjAppendPrintf(s,
+ "Cookie: %s\r\n",
+ fOMPCookie.c_str()
+ );
+ // now the command
+ if (fConversationStage==ippconvstage_subscribe) {
+ // logged in, now subscribe to all available datastores
+ while (fDSCfgIterator != fDatastores.end()) {
+ // check the datastore
+ CLIENTDSCONFIG *dscfgP = static_cast<CLIENTDSCONFIG *>(*fDSCfgIterator);
+ fDSCfgIterator++;
+ cAppCharP subsName = NULL;
+ if (dscfgP->fDSAvailFlag & dsavail_contacts)
+ subsName = "CONTACTS";
+ else if (dscfgP->fDSAvailFlag & dsavail_events)
+ subsName = "CALENDAR";
+ else if (dscfgP->fDSAvailFlag & dsavail_tasks)
+ subsName = "TODO";
+ else if (dscfgP->fDSAvailFlag & dsavail_emails)
+ subsName = "EMAIL";
+ else
+ continue; // try next
+ // create subscription for this datastore
+ StringObjPrintf(data,
+ "SUBSCRIBE %s\r\n",
+ subsName
+ );
+ break;
+ }
+ }
+ else if (fConversationStage==ippconvstage_polling) {
+ // subscribed, now poll
+ // - add push time header
+ StringObjAppendPrintf(s,
+ "TruePush-time: %d\r\n",
+ fAutosyncProfile.ippSettings.period
+ );
+ // - and the command
+ data = "P\r\n";
+ }
+ }
+ // add content with length header
+ StringObjAppendPrintf(s,
+ "Content-Length: %ld\r\n"
+ "\r\n"
+ "%s",
+ data.size(),
+ data.c_str()
+ );
+ }
+ else {
+ // unknown method, disconnect again
+ PDEBUGPRINTFX(DBG_ERROR,("AUTOSYNC: Warning: unknown method=%d, disconnecting",(int)fAutosyncProfile.ippSettings.method));
+ newstate=autosync_ipp_disconnect;
+ done=false;
+ break;
+ }
+ PDEBUGPRINTFX(DBG_TRANSP,("AUTOSYNC: sending request: %s",s.c_str()));
+ // - sent it
+ tcperr = tcpSendDataEx(
+ &fAutosyncIPPSocket, // socket
+ (unsigned char *)s.c_str(), // request string
+ s.size(),
+ 5 // maximally 5 seconds (usually, this should work w/o ANY timeout)
+ );
+ if (tcperr==TCP_RC_ETIMEDOUT)
+ break; // just try again later
+ if (tcperr!=TCP_RC_OK) {
+ // failed: disconnect & reconnect
+ PDEBUGPRINTFX(DBG_ERROR,("AUTOSYNC: failed sending request, tcperr=%d",tcperr));
+ newstate=autosync_ipp_disconnect;
+ fAutosyncRetries++; // count retry
+ done=false;
+ break;
+ }
+ // request sent ok
+ newstate=autosync_ipp_answerwait; // and wait for answer
+ fAutosyncLastStateChange=now;
+ break;
+ case autosync_ipp_answerwait:
+ // check for deactivation
+ if (!aActive) {
+ newstate=autosync_ipp_disconnect;
+ done=false;
+ break;
+ }
+ // when we've got so far, now enable timed-sync-while-ipp-active
+ // if not already enabled (enabled = fAutosyncLastAlert is set)
+ if (fAutosyncLastAlert==0) fAutosyncLastAlert=now;
+ // wait for IPP answer
+ received=bufsiz;
+ tcperr = tcpReadDataEx(
+ &fAutosyncIPPSocket, // Socket
+ buffer,
+ &received,
+ 0
+ );
+ if (tcperr==TCP_RC_ETIMEDOUT) {
+ // check if connection is still ok
+ if (!clientBaseP->isConnectionOK()) {
+ PDEBUGPRINTFX(DBG_ERROR,("AUTOSYNC: Connection not ok any more while waiting for answer -> disconnect"));
+ newstate=autosync_ipp_disconnect;
+ fAutosyncRetries=2; // retry almost immediately
+ done=false;
+ }
+ // check if IPP period is over
+ uInt32 timeout =
+ fAutosyncProfile.ippSettings.method==IPP_METHOD_OMP && fConversationStage!=ippconvstage_polling ?
+ IPP_OMP_COMMAND_TIMEOUT : // non-poll OMP command
+ fAutosyncProfile.ippSettings.period; // poll command
+ if (now>fAutosyncLastStateChange+timeout*secondToLinearTimeFactor) {
+ // IPP period timeout
+ PDEBUGPRINTFX(DBG_TRANSP,("AUTOSYNC: IPP period timeout (%d secs), go to disconnect",timeout));
+ newstate=autosync_ipp_disconnect;
+ fAutosyncRetries=0; // ok so far
+ done=false;
+ }
+ break; // stay in answerwait or disconnect after period timeout
+ }
+ if (tcperr==TCP_RC_EOF) {
+ // connection properly closed
+ newstate=autosync_ipp_endanswer;
+ break;
+ }
+ if (tcperr!=TCP_RC_OK) {
+ // failed: disconnect & reconnect
+ PDEBUGPRINTFX(DBG_ERROR,("AUTOSYNC: failed receiving answer, tcperr=%d",tcperr));
+ newstate=autosync_ipp_disconnect;
+ fAutosyncRetries++; // count retry
+ done=false;
+ break;
+ }
+ // received answer bytes
+ fAutosyncIPPAnswer.append((const char *)buffer,received);
+ if (fAutosyncProfile.ippSettings.method==IPP_METHOD_OMP) {
+ size_t n;
+ // check for content length
+ if (fContentLength==0) {
+ n = fAutosyncIPPAnswer.find("Content-Length: ");
+ if (n!=string::npos) {
+ n += 16;
+ n += StrToULong(fAutosyncIPPAnswer.c_str()+n, fContentLength, 9);
+ }
+ }
+ // check for end of headers
+ if (fHeaderLength==0) {
+ n = fAutosyncIPPAnswer.find("\r\n\r\n");
+ if (n!=string::npos) {
+ fHeaderLength = n+4;
+ }
+ }
+ // check for all data seen
+ if (fHeaderLength && fContentLength && fAutosyncIPPAnswer.size()>=fHeaderLength+fContentLength) {
+ // End of request, but not necessarily connection.
+ // (processing answer will decide if connection must be closed)
+ newstate=autosync_ipp_processanswer;
+ break;
+ }
+ }
+ // stay in answerwait until connection closes
+ break;
+ case autosync_ipp_endanswer:
+ PDEBUGPRINTFX(DBG_TRANSP,("AUTOSYNC: end of data from server, closing connection"));
+ // close connection
+ tcpCloseConnection(&fAutosyncIPPSocket);
+ fAutosyncIPPSocket=0;
+ #ifdef TCP_LEAK_DEBUG
+ fTcpLeakCounter--;
+ #endif
+ // release connection
+ clientBaseP->releaseConnection();
+ // state is idle after processing a message
+ newstate=autosync_ipp_processanswer;
+ // leave app some time to close and release the connection
+ done=true;
+ break;
+ case autosync_ipp_processanswer:
+ PDEBUGPRINTFX(DBG_HOT,("AUTOSYNC: processing answer from server: %" FMT_LENGTH("0.100") "s",FMT_LENGTH_LIMITED(100,fAutosyncIPPAnswer.c_str())));
+ // state is idle after processing a message
+ newstate=autosync_idle;
+ // process collected answer bytes
+ p=fAutosyncIPPAnswer.c_str();
+ uInt16 retriedsofar=fAutosyncRetries;
+ fAutosyncRetries=0;
+ bool disconnect=true; // default to disconnect for every message
+ if (fAutosyncProfile.ippSettings.method==IPP_METHOD_OMP) {
+ // parse line by line
+ bool inheaders=true;
+ disconnect=false; // for OMP, only disconnect if explicitly requested by "Connection:" header
+ while (*p) {
+ // check for those headers we need to know
+ if (strucmp(p,"\r\n",2)==0) {
+ p+=2;
+ inheaders=false;
+ break;
+ }
+ if (strucmp(p,"Set-Cookie: ",12)==0) {
+ // extract login cookie
+ p+=12; q=p;
+ while (*q && *q!=';' && *q>=' ') q++;
+ fOMPCookie.assign(p,q-p);
+ }
+ else if (strucmp(p,"Connection: close",22)==0) {
+ disconnect=true;
+ }
+ // go past next CRLF
+ while (*p && *p!='\r') p++;
+ p++; if (*p=='\n') p++;
+ }
+ // now decode status returned (still in headers means failure as well)
+ bool success = !inheaders && (strucmp(p,"OK ",3)==0) || isdigit(*p);
+ // a non-ok on any stage means we must retry or fall back to login after some retries
+ if (!success) {
+ fAutosyncRetries=retriedsofar+1; // this is an error, keep counting retries
+ PDEBUGPRINTFX(DBG_ERROR,("AUTOSYNC: OMP server error: %" FMT_LENGTH("0.100") "s",FMT_LENGTH_LIMITED(100,p)));
+ newstate=autosync_ipp_disconnect;
+ if (fAutosyncRetries>IPP_OMP_RELOGIN_RETRIES) {
+ PDEBUGPRINTFX(DBG_ERROR,("AUTOSYNC: OMP repeated server error -> force relogin"));
+ fConversationStage = ippconvstage_login;
+ }
+ break;
+ }
+ // basically ok, now depends on communication stage
+ if (fConversationStage==ippconvstage_login) {
+ // last command sent was login
+ // - next is subscribe
+ fConversationStage=ippconvstage_subscribe;
+ // - start with first datastore
+ fDSCfgIterator = fDatastores.begin();
+ }
+ else if (fConversationStage==ippconvstage_subscribe) {
+ // check if more DS must be subscribed
+ if (fDSCfgIterator == fDatastores.end())
+ fConversationStage=ippconvstage_polling; // all subscribed, proceed to polling now
+ }
+ else if (fConversationStage==ippconvstage_polling) {
+ // check polling response
+ while (isdigit(*p)) {
+ // event line
+ // Syntax: notify_seqno SP dest_app SP username SP deviceID SP event
+ // 12 EMAIL john.doe@oracle.com 123123:132123:132123 SYNC
+ // - skip seqno
+ while (*p && isdigit(*p)) p++;
+ if (*p==' ') {
+ p++;
+ // - extract app
+ uInt16 availFlag;
+ q=p; while (*q && *q!=' ') q++;
+ if (strucmp(p,"CONTACTS",q-p)==0)
+ availFlag = dsavail_contacts;
+ else if (strucmp(p,"CALENDAR",q-p)==0)
+ availFlag = dsavail_events;
+ else if (strucmp(p,"TODO",q-p)==0)
+ availFlag = dsavail_tasks;
+ else if (strucmp(p,"EMAIL",q-p)==0)
+ availFlag = dsavail_emails;
+ if (availFlag) {
+ // check for SYNC event
+ p=q; if (*p==' ') p++;
+ // - skip user
+ while (*p && *p!=' ') p++;
+ if (*p==' ') p++;
+ // - skip device ID
+ while (*p && *p!=' ') p++;
+ if (*p==' ') p++;
+ // check for SYNC command
+ if (strucmp(p,"SYNC",4)==0) {
+ PDEBUGPRINTFX(DBG_HOT,("AUTOSYNC: received OMP sync request for availFlags=0x%hX",availFlag));
+ // alert for sync
+ if (alertDbByAppType(availFlag,true)) {
+ fAutosyncAlerted=true;
+ fAutosyncLastAlert=now;
+ disconnect=true; // break TCP connection
+ }
+ }
+ }
+ else
+ break;
+ }
+ // forward to end of line or string
+ while (*p && (*p>=0x20)) p++;
+ while (*p && ((*p=='\n') || (*p=='\r'))) p++;
+ } // while
+ }
+ }
+ else {
+ // DMU0 or DMUS2G
+ while (*p) {
+ // check for basic command
+ if (
+ (fAutosyncProfile.ippSettings.method==IPP_METHOD_DMU0 && strucmp(p,"Sync:",5)==0) ||
+ (fAutosyncProfile.ippSettings.method==IPP_METHOD_DMUS2G && strucmp(p,"sync=",5)==0)
+ ) {
+ p+=5;
+ // get datastore name (remote datastore path, that is!)
+ p=getParam(p,dbname,false);
+ // get eventual additional params
+ // - first is filter CGI
+ p=getParam(p,filtercgi,true);
+ // search DB to flag autosync for it
+ PDEBUGPRINTFX(DBG_HOT,("AUTOSYNC: received sync request for db='%s', filter='%s'",dbname.c_str(),filtercgi.c_str()));
+ // alert for sync
+ if (alertDbByName(dbname.c_str(),filtercgi.c_str(),true)) {
+ fAutosyncAlerted=true;
+ fAutosyncLastAlert=now;
+ }
+ } // if "Sync:" or "sync=" line
+ else if (
+ (fAutosyncProfile.ippSettings.method==IPP_METHOD_DMUS2G && strucmp(p,"error=",6)==0)
+ ) {
+ p+=6;
+ fAutosyncRetries=retriedsofar+1; // this is an error, keep counting retries
+ // get error code
+ uInt32 dmuerr=99999; // unknown error code
+ StrToULong(p,dmuerr,6);
+ if (dmuerr==10010 || dmuerr==10020) {
+ // credential unknown, bad or expired
+ // - make sure that we'll resubscribe
+ if (fAutosyncRetries<IPP_RESUBSCRIBE_RETRIES)
+ fAutosyncRetries=IPP_RESUBSCRIBE_RETRIES;
+ }
+ // seems that we have a problem, cause re-subscription
+ PDEBUGPRINTFX(DBG_ERROR,("AUTOSYNC: DMU server error %ld, try again",dmuerr));
+ newstate=autosync_ipp_disconnect;
+ }
+ // skip until eoln or eot
+ while (*p && *p!='\n' && *p!='\r') p++;
+ while (*p=='\n' || *p=='\r') p++;
+ // this is now eot or start of next line
+ } // while text
+ } // if DMU0 or DMUS2G
+ // close connection if requested (and not already closed anyway)
+ if (disconnect) {
+ // server requests disconnect, do it right now
+ newstate = autosync_ipp_disconnect;
+ done=false;
+ break;
+ }
+ else if (fAutosyncIPPSocket) {
+ // server does not request disconnect and TCP socket is still open - proceed with polling in same connection
+ newstate = autosync_ipp_sendrequest;
+ }
+ done=true;
+ break;
+ #endif // IPP_SUPPORT
+ } // switch
+ // now perform state change if any
+ if (newstate!=fAutosyncState) {
+ // show transition
+ #ifdef IPP_SUPPORT
+ PDEBUGPRINTFX(DBG_TRANSP,(
+ "AUTOSYNC: * STATE CHANGES from %s to %s, mode=%s, active=%s, ippstate=%s, retries=%hd, unsuccessful Alerts=%hd",
+ AutoSyncStateNames[fAutosyncState],
+ AutoSyncStateNames[newstate],
+ AutoSyncModeNames[fAutoSyncMode],
+ aActive ? "yes" : "no",
+ IPPStateNames[fIPPState],
+ fAutosyncRetries,
+ fUnsuccessfulAlerts
+ ));
+ #else
+ PDEBUGPRINTFX(DBG_TRANSP,(
+ "AUTOSYNC: * STATE CHANGES from %s to %s, mode=%s, active=%s, unsuccessful Alerts=%hd",
+ AutoSyncStateNames[fAutosyncState],
+ AutoSyncStateNames[newstate],
+ AutoSyncModeNames[fAutoSyncMode],
+ aActive ? "yes" : "no",
+ fUnsuccessfulAlerts
+ ));
+ #endif
+ #ifdef TCP_LEAK_DEBUG
+ PDEBUGPRINTFX(DBG_TRANSP,(" Number of open TCP connections = %ld",fTcpLeakCounter));
+ #endif
+ // set new state
+ fAutosyncState=newstate;
+ }
+ } while (!done);
+} // CLIENTAGENTCONFIG::autosync_step
+
+
+/// @brief alert a datastore by remote database name
+/// @note if syncType is given (e.g. from a SAN) complete overwrites are possible if explicitly requested
+bool CLIENTAGENTCONFIG::alertDbByName(const char *aRemoteDBName, const char *aFilterCGI, bool aForced, uInt16 aSyncAlert, bool aAlwaysStart)
+{
+ sInt32 idx,seq;
+ TARGETRECORD target;
+ CLIENTDSCONFIG *dscfgP;
+ bool alertedAny=false;
+
+ seq=0;
+ do {
+ idx=findTargetIndex(fAutosyncProfile.profileID,seq++);
+ if (idx<0) break; // no more targets
+ getTarget(idx,target);
+ // compare DB names
+ if (strucmp(target.remoteDBpath,aRemoteDBName,strlen(aRemoteDBName))==0) {
+ // found target record, get related datastore in config
+ dscfgP = static_cast<CLIENTDSCONFIG *>(
+ getLocalDS(target.dbname)
+ );
+ bool mayStart= aAlwaysStart || (target.lastSync!=0); // start allowed ONLY if we have synced before with this target
+ // if we have an explicit sync mode, this overrides the preset mode
+ if (mayStart) {
+ if (aSyncAlert!=0) {
+ // caller provides a sync mode
+ dscfgP->fAutosyncAlertCode=aSyncAlert; // use passed sync alert code
+ }
+ else {
+ // do not automatically start in "dangerous" modes
+ dscfgP->fAutosyncAlertCode=0; // get mode from config
+ mayStart = !(
+ target.forceSlowSync &&
+ (target.syncmode==smo_fromclient || target.syncmode==smo_fromserver)
+ );
+ }
+ }
+ // start if not "dangerous" or when sync mode is explicitly requested by trusted party (SAN)
+ if (mayStart) {
+ // alert
+ dscfgP->fAutosyncForced=aForced; // forced if selected
+ dscfgP->fAutosyncAlerted=true; // alerted anyway
+ AssignString(dscfgP->fAutosyncPathCGI, aFilterCGI);
+ // alerted at least one, IPP is in alerted state now
+ alertedAny=true;
+ PDEBUGPRINTFX(DBG_HOT,("AUTOSYNC: *** ALERTED autosync for datastore '%s' (requested alert=%hd)",dscfgP->getName(),dscfgP->fAutosyncAlertCode));
+ }
+ else {
+ // prevent autosync because of possible zap
+ PDEBUGPRINTFX(DBG_ERROR,("AUTOSYNC: datastore '%s' never synceed so far or would IMPLICITLY cause client or server to be totally overwritten -> do not autosync",dscfgP->getName()));
+ }
+ }
+ } while (true);
+ return alertedAny;
+} // CLIENTAGENTCONFIG::alertDbByName
+
+
+/// @brief alert a datastore by generic application type (DSAvailFlag)
+/// @note if syncType is given (e.g. from a SAN) complete overwrites are possible if explicitly requested
+bool CLIENTAGENTCONFIG::alertDbByAppType(uInt16 aDSAvailFlag, bool aForced, bool aAlwaysStart)
+{
+ bool alertedAny=false;
+ TARGETRECORD target;
+ TLocalDSList::iterator pos;
+
+ for(pos=fDatastores.begin();pos!=fDatastores.end();pos++) {
+ CLIENTDSCONFIG *dscfgP = static_cast<CLIENTDSCONFIG *>(*pos);
+ if ((dscfgP->fDSAvailFlag & aDSAvailFlag) == aDSAvailFlag) {
+ // found datastore
+ // - get target record to check settings
+ sInt32 idx = findTargetIndexByDBInfo(fAutosyncProfile.profileID, dscfgP->fLocalDBTypeID, NULL);
+ if (idx>=0) {
+ getTarget(idx,target);
+ bool mayStart= aAlwaysStart || (target.lastSync!=0); // start allowed ONLY if we have synced before with this target
+ // if we have an explicit sync mode, this overrides the preset mode
+ if (mayStart) {
+ // do not automatically start in "dangerous" modes
+ dscfgP->fAutosyncAlertCode=0; // get mode from config
+ mayStart = !(
+ target.forceSlowSync &&
+ (target.syncmode==smo_fromclient || target.syncmode==smo_fromserver)
+ );
+ }
+ // start if not "dangerous" or when sync mode is explicitly requested by trusted party (SAN)
+ if (mayStart) {
+ // alert
+ dscfgP->fAutosyncForced=aForced; // forced if selected
+ dscfgP->fAutosyncAlerted=true; // alerted anyway
+ // alerted at least one, IPP is in alerted state now
+ alertedAny=true;
+ PDEBUGPRINTFX(DBG_HOT,("AUTOSYNC: *** ALERTED OMP autosync for datastore '%s' (requested alert=%hd)",dscfgP->getName(),dscfgP->fAutosyncAlertCode));
+ }
+ else {
+ // prevent autosync because of possible zap
+ PDEBUGPRINTFX(DBG_ERROR,("AUTOSYNC: datastore '%s' never synced so far or would IMPLICITLY cause client or server to be totally overwritten -> do not autosync",dscfgP->getName()));
+ }
+ }
+ }
+ }
+ return alertedAny;
+} // CLIENTAGENTCONFIG::alertDbByAppType
+
+
+
+#endif
+
+
+/* eof */
diff --git a/src/sysync/clientautosync_inc.h b/src/sysync/clientautosync_inc.h
new file mode 100755
index 0000000..bbddf7c
--- /dev/null
+++ b/src/sysync/clientautosync_inc.h
@@ -0,0 +1,301 @@
+/*
+ * File: clientautosync_inc.h
+ *
+ */
+
+#ifdef IPP_SUPPORT
+// we need the platform-specific TCP-IP implementation
+extern "C" {
+ #include "xptitcp.h"
+}
+#endif
+
+// we need the xpt client base if we directly do TCP/IP
+#if defined(IPP_SUPPORT) || !defined(ENGINE_LIBRARY)
+ #include "xptclientbase.h"
+#endif
+
+#ifdef SERVERALERT_SUPPORT
+ #include "san.h"
+#endif
+
+
+// define to debug TCP open/close balance
+//#define TCP_LEAK_DEBUG 1
+
+
+#ifdef GAL_CLIENT_SUPPORT
+
+#define GAL_STRING_MAX 80
+
+// globals
+typedef struct {
+ // GAL settings
+ bool galUpdated;
+ char url[GAL_STRING_MAX];
+ char domain[GAL_STRING_MAX];
+} TGALInfo;
+
+extern TGALInfo gGALInfo;
+
+#endif
+
+
+// we need some system status routins
+
+// these routines must be implemented in the application
+// - get power status (percentage) of the system, returns false if none available
+bool getPowerStatus(uInt8 &aBattLevel, bool &aACPowered, void *aMasterPointer);
+// - get free memory status (percentage free) of the system, returns false if none available
+bool getMemFreeStatus(uInt8 &aMemLevel, void *aMasterPointer);
+// - check if device is cradled
+bool isDeviceCradled(void *aMasterPointer);
+
+
+namespace sysync {
+
+// Autosync modes
+// note: %%% eventually, SMP GUI still depends on this order - PPC is fixed as per 2006-03-09
+typedef enum {
+ autosync_ipp, // Intelligent Push&Poll (DMU)
+ autosync_timed, // timed sync (scheduled sync)
+ autosync_none, // no autosync
+ autosync_serveralerted, // SAN - SyncML server alerted sync
+ num_autosync_modes
+} TAutosyncModes;
+
+
+// Autosync status
+#define autosync_status_wrongday 0x01
+#define autosync_status_wrongtime 0x02
+#define autosync_status_lowmem 0x04
+#define autosync_status_lowbat 0x08
+#define autosync_status_disabled 0x10 // disabled by interval=0 (for example)
+#define autosync_status_notconnectable 0x20
+#define autosync_status_notsubscribed 0x40
+#define autosync_status_notlicensed 0x80 // IPP/DMU without license
+
+
+
+
+// Autosync condition level settings record
+// NOTE!!!!!: If this struct is changed, this implies a PROFILE_DB_VERSION change!!!
+typedef struct {
+ TAutosyncModes Mode;
+ uInt16 StartDayTime; // [min from midnight] when to start autosync
+ uInt16 EndDayTime; // [min from midnight] when to stop autosync
+ uInt8 WeekdayMask; // [Bits: 0=sun, 1=mon .. 6=sat] days when autosync is active
+ uInt8 ChargeLevel; // percent of battery charge required to activate
+ uInt8 MemLevel; // percent of memory that must be free for doing autosync
+ uInt8 Flags; // additional condition flags - reserved for future use
+} TAutoSyncLevel;
+
+
+// IPP methods
+#define IPP_METHOD_DMU0 0 // simple poll, open specification, used by Toffa SyncWiseEnterprise
+#define IPP_METHOD_DMUS2G 1 // proprietary s2g method 1 protocol, confidential spec
+#define IPP_METHOD_OMP 2 // Oracle Mobile Push
+
+
+// Autosync IPP settings record
+// NOTE!!!!!: If this struct is changed, this implies a PROFILE_DB_VERSION change!!!
+const uInt16 maxippidsiz=64;
+const uInt16 maxipppathsiz=64;
+typedef struct {
+ char srv[maxipppathsiz];
+ uInt16 port;
+ uInt16 period;
+ char path[maxipppathsiz];
+ char id[maxippidsiz];
+ uInt8 method;
+ // new PROFILE_DB_VERSION>=6 fields
+ char cred[maxipppathsiz];
+ uInt16 maxinterval;
+ char timedds[maxipppathsiz];
+} TIPPSettings;
+
+
+typedef enum {
+ ippstate_unknown,
+ ippstate_idle,
+ ippstate_activated,
+ ippstate_subscribed,
+ num_ipp_states
+} TIPPActivationState;
+
+
+typedef enum {
+ ippconvstage_login,
+ ippconvstage_subscribe,
+ ippconvstage_polling
+} TIPPConvStage;
+
+
+// AutoSync states
+typedef enum {
+ autosync_idle, // no activity
+ autosync_start, // start selected autosync mechanism
+ autosync_timedsync, // start pure scheduled sync (calc when we need next sync)
+ autosync_timedsync_wait, // waiting for scheduled sync to occur
+ autosync_ipp_connectwait, // start waiting until connecting again
+ autosync_ipp_connectwaiting, // waiting for connecting again
+ autosync_ipp_resubscribe, // cause a timed sync to re-subscribe to IPP
+ autosync_alert_implicit, // implicit alert
+ autosync_ipp_connect, // connect to server
+ autosync_ipp_sendrequest, // send request to server
+ autosync_ipp_answerwait, // connected, waiting for answer from IPP server
+ autosync_ipp_disconnect, // disconnect from the server
+ autosync_ipp_endanswer, // disconnect after a IPP answer
+ autosync_ipp_processanswer, // process collected IPP answer
+ num_autosync_states
+} TAutoSyncStates;
+
+
+// IPP parameters
+#define IPP_MIN_RECONNECTWAIT 3 // [seconds]
+#define IPP_MAX_RECONNECTWAIT 1800 // [seconds] = half an hour
+#define IPP_FORCEDOWN_RETRIES 3 // force down connection after 3 failed reconnects
+#define IPP_RESUBSCRIBE_RETRIES 5 // re-subscribe after 5 failed reconnects
+#define IPP_REACTIVATE_RETRIES 7 // after 7 failed reconnects with resubscription, force a reactivation
+#define IPP_BLOCK_RETRIES 9 // after 9 failed reconnects, do not do anything any more
+#define IPP_MIN_RESUBSCRIBEWAIT 180 // [seconds] wait minimally 3 minutes until re-trying subcription in case previous sync session did not deliver subscription params
+#define IPP_OMP_COMMAND_TIMEOUT 10 // [seconds] wait x seconds for non-poll OMP command to get answer
+#define IPP_OMP_RELOGIN_RETRIES 2 // fall back to re-login after 2 failed attempts
+
+// Autosync parameters
+#define AUTOSYNC_MAX_ALERTRETRIES 5 // retry max 5 times to start a sync session again after a failed alert
+#define AUTOSYNC_FORCEDOWN_RETRIES 2 // after 2 retries, bring down connection first before re-trying
+#define AUTOSYNC_MIN_ALERTRETRY 10 // retry after 10,20,40,80 and 160 secs
+#define AUTOSYNC_BATTCHECK_INTERVAL 60 // check battery level every minute
+#define AUTOSYNC_MEMCHECK_INTERVAL 60 // check memory every minute
+#define AUTOSYNC_CONNCHECK_INTERVAL 10 // check connection status every 10 seconds
+#define AUTOSYNC_ENVERRSDISPLAY_SECS 15 // show battery/mem errors 15 seconds after failed timed autosync
+
+#ifdef IPP_SUPPORT
+// applies the value to the IPP parameter identified by aTag
+// This can be called from client provisioning code or from a sync session's GET
+// command RESULT (if the server supports IPP autoconfig)
+void ipp_setparam(const char *aTag, const char *aValue, TIPPSettings &aIPPSettings);
+#endif
+
+#ifdef TCP_LEAK_DEBUG
+ #define TCP_LEAKCOUNTER uInt32 fTcpLeakCounter;
+#else
+ #define TCP_LEAKCOUNTER
+#endif
+
+#ifdef IPP_SUPPORT
+ #define IPP_VARS \
+ uInt16 fAutosyncRetries; /* retry count for IPP connection attempts */ \
+ Socket_t fAutosyncIPPSocket; /* the socket used for IPP */ \
+ string fAutosyncIPPAnswer; /* the IPP answer is collected here */ \
+ TIPPActivationState fIPPState; /* the IPP activation state */ \
+ TIPPConvStage fConversationStage; /* conversation stage within IPP poll connection (OMG only at this time) */ \
+ TLocalDSList::iterator fDSCfgIterator; /* iterator for datastores (used for SUBSCRIBE in OMP) */ \
+ string fOMPCookie; /* cookie for OMP */ \
+ uInt32 fHeaderLength; /* header length */ \
+ uInt32 fContentLength; /* content length */
+ #define IPP_METHODS
+#else
+ #define IPP_METHODS
+ #define IPP_VARS
+#endif
+
+#ifdef SERVERALERT_SUPPORT
+ #define SAN_VARS \
+ uInt32 fAutosyncSANSessionID; /* Session ID as requested by SAN */
+ #define SAN_METHODS \
+ /* - analyzes server alert (SAN) message */ \
+ localstatus autosync_processSAN(bool aLocalPush, void *aPushMsg, uInt32 aPushMsgSz, cAppCharP aHeaders, sInt32 &aAlertedProfile, UI_Mode &aUIMode);
+#else
+ #define SAN_METHODS
+ #define SAN_VARS
+#endif
+
+
+
+// macro to declare autosync methods and fields in agent config
+#ifndef AUTOSYNC_SUPPORT
+#define CLIENTAUTOSYNC_CLASSDECL
+#else
+#define CLIENTAUTOSYNC_CLASSDECL \
+public: \
+ /* Autosync methods */ \
+ /* - init autosync mechanism */ \
+ void autosync_init(lineartime_t aLastAlert); \
+ /* - set profile */ \
+ void autosync_setprofile(sInt32 aProfileIndex, bool aHasChanged); \
+ /* must be called to obtain DMU PUT command data to send to SyncML server */ \
+ void autosync_get_putrequest(string &aReq); \
+ /* - should be called when environmental params have changed */ \
+ void autosync_condchanged(void); \
+ /* - check if app may quit */ \
+ bool autosync_canquit(lineartime_t &aRestartAt, bool aEnabled); \
+ /* - check if we need a sync now */ \
+ bool autosync_check(bool aActive); \
+ /* - get status information about autosync */ \
+ void autosync_status( \
+ TAutosyncModes &aAutosyncMode, /* currently active mode */ \
+ lineartime_t &aNextSync, /* 0 if no scheduled nextsync */ \
+ lineartime_t &aLastAlert, /* 0 if no alert seen so far */ \
+ uInt8 &aWhyNotFlags /* why-no-autosync status flags */ \
+ ); \
+ /* - returns true if we need to perform a sync session now */ \
+ void autosync_stop(void); \
+ /* - performs next step in the state machine */ \
+ void autosync_step(bool aActive); \
+ /* - forget or activate syncs alerted for autosync in DS config */ \
+ void autosync_ackAlert(bool aAck); \
+ /* - confirm that autosync has taken place (clears alerted flag) */ \
+ void autosync_confirm(bool aUnsuccessful=false); \
+ /* - parse ipp params */ \
+ void ipp_setparam(const char *aTag, const char *aValue, TIPPSettings &aIPPSettings); \
+ IPP_METHODS \
+ SAN_METHODS \
+ /* - profile record */ \
+ PROFILERECORD fAutosyncProfile; \
+ sInt32 fAutosyncProfileLastidx; /* -1 if no profile selected before */ \
+private: \
+ /* alert a datastore by remote database name */ \
+ bool alertDbByName(const char *aRemoteDBName, const char *aFilterCGI, bool aForced, uInt16 syncAlert=0, bool aAlwaysStart=false); \
+ /* alert a datastore by generic type flag */ \
+ bool alertDbByAppType(uInt16 aDSAvailFlag, bool aForced, bool aAlwaysStart=false); \
+ /* private vars needed for autosync */ \
+ /* - state vars */ \
+ sInt16 fCurrentLevelIndex; /* index of currently active autosync level, -1 if none */ \
+ TAutosyncModes fAutoSyncMode; /* currently active autosync mode */ \
+ TAutosyncModes fNewMode; /* new target mode */ \
+ lineartime_t fNextCheckAt; /* when we need to check for mode change or timed sync next, 0 if immediately */ \
+ lineartime_t fNextWakeupCheckAt; /* when we need to wakeup app to check levels, 0 if immediately */ \
+ uInt8 fWhyNotFlags; /* reason why we cannot run autosync now */ \
+ lineartime_t fEnvCheckedAt; /* when we last checked the mem/batt environmental factors */ \
+ lineartime_t fNextSyncAt; /* when next sync will take place */ \
+ TAutoSyncStates fAutosyncState; /* current state machine state */ \
+ bool fAutosyncAlerted; /* set when some datastores get alerted, cleared when autosync_confirm() */ \
+ lineartime_t fAutosyncLastStateChange; /* time when state changed last time */ \
+ uInt16 fUnsuccessfulAlerts; /* if > 0, we had unsuccessful alerts - delay re-alerting */ \
+ IPP_VARS \
+ SAN_VARS \
+ bool fCurrentlyCradled; /* the current IPP connection is assuming cradled operation */ \
+ /* timed sync */ \
+ lineartime_t fAutosyncLastAlert; /* time when last autosync was alerted */ \
+ TCP_LEAKCOUNTER
+#endif
+
+
+// macro to declare autosync fields in DB (target) config
+#ifndef AUTOSYNC_SUPPORT
+#define CLIENTAUTOSYNC_CLASSDECL_DB
+#else
+#define CLIENTAUTOSYNC_CLASSDECL_DB \
+public: \
+ bool fAutosyncAlerted; /* set if server requested sync of this datastore */ \
+ bool fAutosyncForced; /* set if server requests sync of this datastore even if target is disabled */ \
+ uInt16 fAutosyncAlertCode; /* if not 0, this is the alert code requested e.g. by a SAN initiated autosync */ \
+ string fAutosyncPathCGI; /* the CGI to be appended to the database path */
+#endif
+
+} // namespace sysync
+
+
+/* eof */
diff --git a/src/sysync/clientprovisioning_inc.cpp b/src/sysync/clientprovisioning_inc.cpp
new file mode 100755
index 0000000..f4c16aa
--- /dev/null
+++ b/src/sysync/clientprovisioning_inc.cpp
@@ -0,0 +1,377 @@
+/*
+ * File: clientprovisioning_inc.cpp
+ *
+ * Include file for binfileagent.cpp and palmdbagent.cpp to provide
+ * string-command based client provisioning.
+ *
+ * Author: Lukas Zeller (luz@synthesis.ch)
+ *
+ * Copyright (c) 2003-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ * 2003-09-30 : luz : created
+ *
+ *
+ */
+
+#ifndef ENHANCED_PROFILES_2004
+ #error "no support any more for non-ENHANCED_PROFILES_2004"
+#endif
+
+
+// Configure profile
+// - params must always start with pf="profilename" or defpf="profilename"
+// - aActiveProfile is updated only if profile was actually added, changed or deleted
+static bool configProfile(CLIENTAGENTCONFIG *aCfgP, const char *aParm, bool aMayAdd, bool aMayUpdate, sInt32 &aActiveProfile)
+{
+ #ifdef MULTI_PROFILE_SUPPORT
+ const sInt32 maxprofiles = 30; // prevent overflowing popup too much
+ #else
+ const sInt32 maxprofiles = 1; // only one profile
+ #endif
+ string tag,pfnam,value;
+ // get profile name
+ aParm=nextTag(aParm,tag,pfnam);
+ if (!aParm) return false;
+ bool def=false;
+ if (tag=="defpf") def=true;
+ else if (!(tag=="pf")) return false;
+ // find profile by name
+ sInt32 np = aCfgP->numProfiles();
+ sInt32 pi = np;
+ PROFILERECORD profile;
+ bool found=false;
+ while (pi>0) {
+ pi--;
+ aCfgP->getProfile(pi,profile);
+ if (strucmp(profile.profileName,pfnam.c_str())==0) {
+ found=true;
+ break;
+ }
+ }
+ // now profile is current profile record if found==true
+ // - check delete first
+ if (!aMayAdd && !aMayUpdate) {
+ // delete requested
+ if (!found) return false; // does not exist
+ if (np<2) return false; // only one profile left, do not delete
+ // perform delete
+ aCfgP->deleteProfile(pi);
+ aActiveProfile=0; // re-activate profile 0
+ return true;
+ }
+ // - For update (modify or add): If profile not found AND...
+ // ...if a default profile is specified -> overwrite first profile
+ // ...if single-profile client -> overwrite the profile
+ if (!found && aMayUpdate && aMayAdd && (def || maxprofiles==1) && np>0) {
+ pi=0; // first is default
+ aCfgP->getProfile(pi,profile);
+ AssignCString(profile.profileName,pfnam.c_str(),maxnamesiz);
+ found=true;
+ }
+ // - if not found and add not allowed or already too many profiles -> exit
+ if (!found && (!aMayAdd || np>=maxprofiles)) return false; // nothing added
+ // - if found, but update not allowed -> exit
+ if (found && !aMayUpdate) return false; // nothing updated
+ // Now, create new profile if we don't modify
+ if (!found) {
+ // - create new profile without default settings
+ pi = aCfgP->newProfile(pfnam.c_str(),false);
+ // - get the record
+ aCfgP->getProfile(pi,profile);
+ }
+ // now set the params
+ sInt32 ti=-1; // no target selected yet
+ TARGETRECORD target;
+ uInt32 profileID=aCfgP->getIDOfProfile(pi);
+ // loop through params
+ while (aParm!=NULL) {
+ // get next tag
+ aParm=nextTag(aParm,tag,value);
+ // process tag
+ if (tag=="db") {
+ // save current target first, if any
+ if (ti>=0) aCfgP->writeTarget(ti,target);
+ // select new target by DB name
+ sInt32 si=0;
+ do {
+ ti=aCfgP->findTargetIndex(profileID,si++);
+ if (ti<0) break; // all checked, none found
+ // check db name
+ ti=aCfgP->getTarget(ti,target);
+ } while(!(value==target.dbname));
+ }
+ else if (tag=="uri") {
+ // URI (or URI suffix)
+ #ifdef HARD_CODED_SERVER_URI
+ // - URI is fixed, so specified URI must be URI suffix (from old pre-2004 profiles)
+ AssignCString(profile.URIpath,value.c_str(),maxpathsiz);
+ #else
+ // - URI can be set, except if config specifies a fixed URI
+ if (aCfgP->fServerURI.empty())
+ AssignCString(profile.serverURI,value.c_str(),maxurisiz);
+ #endif
+ // setting URI resets the device to unknown SyncML version
+ profile.lastSyncMLVersion=syncml_vers_unknown;
+ }
+ #ifdef PROTOCOL_SELECTOR
+ else if (tag=="proto") {
+ // Protocol selector
+ profile.protocol = (TTransportProtocols)uint16Val(value.c_str());
+ if (profile.protocol>=num_transp_protos)
+ profile.protocol=transp_proto_uri;
+ }
+ #endif
+ else if (tag=="syncmlvers") {
+ // initial SyncML version
+ profile.lastSyncMLVersion = (TSyncMLVersions)uint16Val(value.c_str());
+ if (profile.lastSyncMLVersion>=numSyncMLVersions)
+ profile.lastSyncMLVersion=syncml_vers_unknown;
+ }
+ else if (tag=="uripath") {
+ // URI path
+ AssignCString(profile.URIpath,value.c_str(),maxpathsiz);
+ }
+ else if (tag=="roflags") {
+ // Read only flags
+ profile.readOnlyFlags=uint16Val(value.c_str());
+ }
+ else if (tag=="ftrflags") {
+ // Feature flags
+ profile.featureFlags=uint16Val(value.c_str());
+ }
+ else if (tag=="dsflags") {
+ // Datastore available flags
+ profile.dsAvailFlags=uint16Val(value.c_str());
+ }
+ else if (tag=="user") {
+ // User
+ AssignCString(profile.serverUser,value.c_str(),maxupwsiz);
+ }
+ else if (tag=="pwd") {
+ // password
+ assignMangledToCString(profile.serverPassword, value.c_str(), maxupwsiz,true);
+ }
+ else if (tag=="httpuser") {
+ // User
+ AssignCString(profile.transportUser,value.c_str(),maxupwsiz);
+ }
+ else if (tag=="httppwd") {
+ // password
+ assignMangledToCString(profile.transportPassword, value.c_str(), maxupwsiz,true);
+ }
+ else if (tag=="transpflags") {
+ // transport related flags
+ profile.transpFlags=uint32Val(value.c_str());
+ }
+ else if (tag=="profileflags") {
+ // general profile flags
+ profile.transpFlags=uint32Val(value.c_str());
+ }
+ #ifdef PROXY_SUPPORT
+ else if (tag=="proxy") {
+ // User
+ AssignCString(profile.proxyHost,value.c_str(),maxurisiz);
+ }
+ else if (tag=="socks") {
+ // User
+ AssignCString(profile.socksHost,value.c_str(),maxurisiz);
+ }
+ else if (tag=="connproxy") {
+ // use connection's proxy settings
+ profile.useConnectionProxy=boolVal(value.c_str());
+ }
+ else if (tag=="useproxy") {
+ // use connection's proxy settings
+ profile.useProxy=boolVal(value.c_str());
+ }
+ else if (tag=="proxyuser") {
+ // User
+ AssignCString(profile.proxyUser,value.c_str(),maxupwsiz);
+ }
+ else if (tag=="proxypwd") {
+ // password
+ assignMangledToCString(profile.proxyPassword, value.c_str(), maxupwsiz,true);
+ }
+ #endif
+ #ifdef AUTOSYNC_SUPPORT
+ // Autosync activity schedule
+ if (strucmp(tag.c_str(),"asl",3)==0) {
+ // autosync level
+ do {
+ // - get level number
+ if (tag.size()<5) break; // too short
+ uInt8 lvl=tag[3]-0x30-1; // get level number 1..n and convert to 0-based index
+ if (lvl>=NUM_AUTOSYNC_LEVELS) break; // invalid level number
+ if (tag[4]!='_') break;
+ // - level number found, check for subtag
+ const char *subtag = tag.c_str()+5;
+ if (strucmp(subtag,"mode")==0) {
+ // autosync mode
+ profile.AutoSyncLevel[lvl].Mode=(TAutosyncModes)uint16Val(value.c_str());
+ }
+ else if (strucmp(subtag,"start")==0) {
+ // start day time for autosync
+ profile.AutoSyncLevel[lvl].StartDayTime=uint16Val(value.c_str());
+ }
+ else if (strucmp(subtag,"end")==0) {
+ // end day time for autosync
+ profile.AutoSyncLevel[lvl].EndDayTime=uint16Val(value.c_str());
+ }
+ else if (strucmp(subtag,"wdays")==0) {
+ // weekdays for autosync (bit 0=Su, 1=Mo .. 6=Sa)
+ profile.AutoSyncLevel[lvl].WeekdayMask=uint16Val(value.c_str());
+ }
+ else if (strucmp(subtag,"charge")==0) {
+ // charge level (% of full battery)
+ profile.AutoSyncLevel[lvl].ChargeLevel=uint16Val(value.c_str());
+ }
+ else if (strucmp(subtag,"mem")==0) {
+ // min free mem level (% of total memory)
+ profile.AutoSyncLevel[lvl].MemLevel=uint16Val(value.c_str());
+ }
+ else if (strucmp(subtag,"flags")==0) {
+ // extra flags
+ profile.AutoSyncLevel[lvl].Flags=uint16Val(value.c_str());
+ }
+ } while(false);
+ } // aslvlN_xxxx
+ #ifdef IPP_SUPPORT
+ else if (strucmp(tag.c_str(),"ipp_",4)==0) {
+ // IPP parameters, parse rest after ipp_ prefix
+ aCfgP->ipp_setparam(tag.c_str()+4,value.c_str(),profile.ippSettings);
+ }
+ #endif
+ // Timed sync schedule
+ else if (tag=="ts_mobile") {
+ // interval (minutes) for mobile timed sync
+ profile.TimedSyncMobilePeriod=uint16Val(value.c_str());
+ }
+ else if (tag=="ts_cradled") {
+ // interval (minutes) for cradled timed sync
+ profile.TimedSyncCradledPeriod=uint16Val(value.c_str());
+ }
+ #endif
+ else if (ti>=0) {
+ // target-level settings
+ if (tag=="enabled") {
+ // enabled
+ target.enabled = value=="1";
+ }
+ else if (tag=="slow") {
+ // force slowsync
+ target.forceSlowSync = value=="1";
+ }
+ else if (tag=="mode" && value.size()>=1) {
+ // mode: 0=twoway, 1=fromserver, 2=fromclient
+ uInt8 m=value[0]-0x30;
+ if (m<=2) target.syncmode = (TSyncModes)m;
+ }
+ else if (tag=="path") {
+ // remote DB path
+ AssignCString(target.remoteDBpath,value.c_str(),remoteDBpathMaxLen);
+ }
+ #if TARGETS_DB_VERSION>5
+ else if (tag=="remoteFilters") {
+ // remote DB path
+ AssignCString(target.remoteFilters,value.c_str(),filterExprMaxLen);
+ }
+ else if (tag=="localFilters") {
+ // remote DB path
+ AssignCString(target.remoteFilters,value.c_str(),filterExprMaxLen);
+ }
+ #endif
+ else if (tag=="ext") {
+ // extras
+ StrToULong(value.c_str(), target.extras);
+ }
+ else if (tag=="lim1") {
+ // limit1
+ StrToLong(value.c_str(), target.limit1);
+ }
+ else if (tag=="lim2") {
+ // limit2
+ StrToLong(value.c_str(), target.limit2);
+ }
+ } // if target selected
+ } // while more tags
+ // save open target if any
+ if (ti>=0) aCfgP->writeTarget(ti,target);
+ // save profile
+ aCfgP->writeProfile(pi,profile);
+ aActiveProfile=pi; // return changed or added profile number
+ return true;
+} // configProfile
+
+
+// Configure registration
+// cmd="reg";text="name ::m=manufacturer";key="ABCD-EFGH-KIJL-1234";
+static bool configRegistration(const char *aRegParams, TSyncAppBase *aAppBaseP)
+{
+ #ifdef SYSER_REGISTRATION
+ string tag,text,key;
+ // get text
+ aRegParams = nextTag(aRegParams,tag,text);
+ if (!(tag=="text")) return false;
+ // get key
+ if (!aRegParams) return false;
+ aRegParams = nextTag(aRegParams,tag,key);
+ if (!(tag=="key")) return false;
+ // apply registration
+ return aAppBaseP->checkAndSaveRegInfo(text.c_str(), key.c_str())==LOCERR_OK;
+ #else
+ return false; // no registration possible
+ #endif
+} // configRegistration
+
+
+// generic code that can work as palmdbagent or binfiledbagent method
+// - Provisioning string format: tag="value"[;tag="value"]....
+// - commands:
+// - add : only adds a profile, does nothing if already existing or
+// with single-profile-client.
+// - update : adds new or updates existing profile, if defpf is used
+// or with single-profile client, the first profile will be
+// updated (overwritten)
+// - modify : only modifies profile with specified name, never overwrites
+// another profile.
+// - delete : deletes profile with specified name (except if it is only
+// profile left)
+// - reg : apply license information
+bool CLIENTAGENTCONFIG::executeProvisioningString(const char *aProvisioningCmd, sInt32 &aActiveProfile)
+{
+ string tag,value;
+ const char *p=aProvisioningCmd;
+ // get command tag
+ p=nextTag(p,tag,value);
+ if (!p) return false;
+ // first tag must be "cmd"
+ if (!(tag=="cmd")) return false;
+ // dispatch commands
+ if (value=="reg") {
+ // registration
+ return configRegistration(p,getSyncAppBase());
+ }
+ else if (value=="add") {
+ // add new profile if not already there
+ return configProfile(this,p,true,false,aActiveProfile); // may add but may not update
+ }
+ else if (value=="update") {
+ // update or add profile
+ return configProfile(this,p,true,true,aActiveProfile); // may add and update
+ }
+ else if (value=="modify") {
+ // only modify existing
+ return configProfile(this,p,false,true,aActiveProfile); // may not add but may update
+ }
+ else if (value=="delete") {
+ // delete existing
+ return configProfile(this,p,false,false,aActiveProfile); // neither add nor update = delete
+ }
+ else return false; // unknown command
+} // CLIENTAGENTCONFIG::executeProvisioningString
+
+
+
+
+
+/* eof */
diff --git a/src/sysync/clientprovisioning_inc.h b/src/sysync/clientprovisioning_inc.h
new file mode 100755
index 0000000..aaf9999
--- /dev/null
+++ b/src/sysync/clientprovisioning_inc.h
@@ -0,0 +1,14 @@
+/*
+ * File: clientprovisioning_inc.h
+ *
+ */
+
+
+// macro to declare clientprovisioning methods and fields
+#define CLIENTPROVISIONING_CLASSDECL \
+public: \
+ /* - execute remote provisioning command */ \
+ bool executeProvisioningString(const char *aProvisioningCmd, sInt32 &aActiveProfile);
+
+
+/* eof */
diff --git a/src/sysync/configelement.cpp b/src/sysync/configelement.cpp
new file mode 100755
index 0000000..e85f345
--- /dev/null
+++ b/src/sysync/configelement.cpp
@@ -0,0 +1,1024 @@
+/*
+ * File: ConfigElement.cpp
+ *
+ * Author: Lukas Zeller (luz@synthesis.ch)
+ *
+ * TConfigElement
+ * Element of hierarchical configuration
+ *
+ * Copyright (c) 2001-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ * 2001-11-14 : luz : created
+ */
+
+// includes
+#include "prefix_file.h"
+#include "sysync.h"
+#include "configelement.h"
+#include "syncappbase.h"
+#include "scriptcontext.h"
+#include "multifielditem.h"
+#include "sysync_crc16.h"
+#include "vtimezone.h"
+
+
+using namespace sysync;
+
+#ifndef RELEASE_VERSION
+// if defined, parsing debug info goes to console
+//#define CONSDEBUG
+#endif
+
+#ifdef CONSDEBUG
+#define CONSDBGPRINTF(m) CONSOLEPRINTF(m)
+
+const char * const ParseModeNames[numParseModes] = {
+ "element", // normal, expecting sub-elements
+ "nested", // like pamo_element, but scanning nested elements in same TConfigElement
+ "delegated", // I have delegated parsing of a single sub-element of mine to another element (without XML nesting)
+ "end", // expecting end of element
+ "endnested", // expecting end of nested element (i.e. will call nestedElementEnd())
+ "all", // read over all content
+ "string", // string, but with all WSP converted to space and removed at beginning an end
+ "rawstring", // string without any changes
+ "cstring", // string with \\, \t,\n,\r and \xXX escape conversion
+ "macrostring", // string which can contain macros to substitute config vars in $xxx or $() format
+ #ifdef SCRIPT_SUPPORT
+ "script", // tokenized script
+ "functiondef", // script function definition
+ #endif
+ "field",
+ "path", // string is updated such that a filename can be appended directly
+ "boolean",
+ "tristate",
+ "timestamp",
+ "timezone",
+ "vtimezone",
+ "idcode",
+ "char",
+ "int64",
+ "int32",
+ "int16",
+ "enum1by",
+ "enum2by",
+ "enum4by"
+};
+#else
+#define CONSDBGPRINTF(m)
+#endif
+
+
+
+/*
+ * Implementation of TConfigElement
+ */
+
+/* public TConfigElement members */
+
+
+TConfigElement::TConfigElement(const char *aElementName, TConfigElement *aParentElementP)
+{
+ // set element name
+ fElementName=aElementName;
+ fResolveImmediately=false; // by default, elements do not resolve immediately, but when entire config is read
+ // set parent and root element pointers
+ fParentElementP=aParentElementP;
+ if (fParentElementP) {
+ // has parent, get root from parent
+ fRootElementP=fParentElementP->getRootElement();
+ }
+ else {
+ // base element cannot be root
+ fRootElementP=NULL;
+ }
+ #ifndef HARDCODED_CONFIG
+ // init parsing
+ ResetParsing();
+ #endif
+} // TConfigElement::TConfigElement
+
+
+TConfigElement::~TConfigElement()
+{
+ clear();
+} // TConfigElement::~TConfigElement
+
+
+TSyncAppBase *TConfigElement::getSyncAppBase(void)
+{
+ return fRootElementP ? fRootElementP->fSyncAppBaseP : NULL;
+} // TConfigElement::getSyncAppBase
+
+
+// - convenience version for getting time
+lineartime_t TConfigElement::getSystemNowAs(timecontext_t aContext)
+{
+ return sysync::getSystemNowAs(aContext,getSyncAppBase()->getAppZones());
+} // TConfigElement::getSystemNowAs
+
+
+#ifndef HARDCODED_CONFIG
+
+// static helper, returns attribute value or NULL if none
+const char *TConfigElement::getAttr(const char **aAttributes, const char *aAttrName)
+{
+ if (!aAttributes) return NULL;
+ const char *attname;
+ while ((attname=*aAttributes)!=0) {
+ if (strucmp(attname,aAttrName)==0) {
+ return *(++aAttributes); // found, return value
+ }
+ aAttributes+=2; // skip value, go to next name
+ }
+ return NULL; // not found
+} // TConfigElement::getAttr
+
+
+// get attribute value, check for macro expansion
+// @param aDefaultExpand : if set, non-recursive expansion is done anyway, otherwise, a "expand" attribute is required
+bool TConfigElement::getAttrExpanded(const char **aAttributes, const char *aAttrName, string &aValue, bool aDefaultExpand)
+{
+ cAppCharP val = getAttr(aAttributes, aAttrName);
+ if (!val) return false; // no value
+ aValue = val;
+ getSyncAppBase()->expandConfigVars(aValue, aDefaultExpand ? 1 : fCfgVarExp, this, getName());
+ return true;
+} // TConfigElement::getAttrExpanded
+
+
+// static helper, returns true if attribute has valid (or none, if aOpt) bool value
+bool TConfigElement::getAttrBool(const char **aAttributes, const char *aAttrName, bool &aBool, bool aOpt)
+{
+ const char *v = getAttr(aAttributes,aAttrName);
+ if (!v) return aOpt; // not existing, is ok if optional
+ return StrToBool(v,aBool);
+} // TConfigElement::getAttrBool
+
+
+// static helper, returns true if attribute has valid (or none, if aOpt) short value
+bool TConfigElement::getAttrShort(const char **aAttributes, const char *aAttrName, sInt16 &aShort, bool aOpt)
+{
+ const char *v = getAttr(aAttributes,aAttrName);
+ if (!v) return aOpt; // not existing, is ok if optional
+ return StrToShort(v,aShort);
+} // TConfigElement::getAttrShort
+
+
+// static helper, returns true if attribute has valid (or none, if aOpt) short value
+bool TConfigElement::getAttrLong(const char **aAttributes, const char *aAttrName, sInt32 &aLong, bool aOpt)
+{
+ const char *v = getAttr(aAttributes,aAttrName);
+ if (!v) return aOpt; // not existing, is ok if optional
+ return StrToLong(v,aLong);
+} // TConfigElement::getAttrLong
+
+
+#ifdef SCRIPT_SUPPORT
+sInt16 TConfigElement::getFieldIndex(cAppCharP aFieldName, TFieldListConfig *aFieldListP, TScriptContext *aScriptContextP)
+{
+ // fields or local script variables (if any) can be mapped
+ if (aScriptContextP)
+ return aScriptContextP->getIdentifierIndex(OBJ_AUTO, aFieldListP,aFieldName);
+ else
+ return aFieldListP ? aFieldListP->fieldIndex(aFieldName) : VARIDX_UNDEFINED;
+}
+#else
+sInt16 TConfigElement::getFieldIndex(cAppCharP aFieldName, TFieldListConfig *aFieldListP)
+{
+ // only direct mapping of MultiFieldItem fields
+ return aFieldListP ? aFieldListP->fieldIndex(aFieldName) : VARIDX_UNDEFINED;
+}
+#endif
+
+#endif // HARDCODED_CONFIG
+
+
+
+void TConfigElement::clear(void)
+{
+ // nop
+} // TConfigElement::clear
+
+
+// resolve (finish after all data is parsed)
+void TConfigElement::Resolve(bool aLastPass)
+{
+ #ifndef HARDCODED_CONFIG
+ // Only resolve if element was not already resolved when it finished parsing
+ // or if it was not parsed at all (that is, it did not appear in the config
+ // at all and only contains default values)
+ if (!fResolveImmediately || !fCompleted) {
+ // try to finally resolve private stuff now that all children are resolved
+ localResolve(aLastPass);
+ }
+ #else
+ // hardcoded config is never resolved early
+ localResolve(aLastPass);
+ #endif
+}; // TConfigElement::Resolve
+
+
+#ifdef SYDEBUG
+
+TDebugLogger *TConfigElement::getDbgLogger(void)
+{
+ // commands log to session's logger
+ TSyncAppBase *appBase = getSyncAppBase();
+ return appBase ? appBase->getDbgLogger() : NULL;
+} // TConfigElement::getDbgLogger
+
+uInt32 TConfigElement::getDbgMask(void)
+{
+ TSyncAppBase *appBase = getSyncAppBase();
+ if (!appBase) return 0; // no session, no debug
+ return appBase->getDbgMask();
+} // TConfigElement::getDbgMask
+
+#endif
+
+
+#ifndef HARDCODED_CONFIG
+
+void TConfigElement::ResetParsing(void)
+{
+ fChildParser=NULL;
+ fParseMode=pamo_element; // expecting elements
+ fNest=0; // normal elements start with Nest=0
+ fExpectAllNestStart=-1; // no expectAll called yet
+ fCompleted=false; // not yet completed parsing
+ fTempString.erase(); // nothing accumulated yet
+ #ifdef SYSER_REGISTRATION
+ fLockedElement=false;
+ fHadLockedSubs=false;
+ #endif
+} // TConfigElement::ResetParsing(void)
+
+
+// report error
+void TConfigElement::ReportError(bool aFatal, const char *aMessage, ...)
+{
+ const sInt32 maxmsglen=1024;
+ char msg[maxmsglen];
+ va_list args;
+
+ msg[0]='\0';
+ va_start(args, aMessage);
+ char *p = msg;
+ int n=0;
+ // show fatal flag
+ if (aFatal) {
+ strcat(p,"Fatal: ");
+ n=strlen(p);
+ p+=n;
+ }
+ // assemble the message string
+ vsnprintf(p, maxmsglen-n, aMessage, args);
+ va_end(args);
+ // set the message
+ TRootConfigElement *rootP = getRootElement();
+ if (!rootP)
+ SYSYNC_THROW(TConfigParseException("Element without root"));
+ rootP->setError(aFatal,msg);
+} // TConfigElement::ReportError
+
+
+// fail in parsing (short form of ReportError)
+bool TConfigElement::fail(const char *aMessage, ...)
+{
+ const sInt32 maxmsglen=1024;
+ char msg[maxmsglen];
+ va_list args;
+
+ msg[0]='\0';
+ va_start(args, aMessage);
+ // assemble the message string
+ vsnprintf(msg, maxmsglen, aMessage, args);
+ va_end(args);
+ // report the error
+ ReportError(true,msg);
+ // skip the rest
+ expectAll();
+ // return value
+ return true;
+} // TConfigElement::fail
+
+
+// start of element, this config element decides who processes this element
+bool TConfigElement::startElement(const char *aElementName, const char **aAttributes, sInt32 aLine)
+{
+ if (fChildParser) {
+ // parsing in a sub-level, delegate
+ return fChildParser->startElement(aElementName,aAttributes,aLine);
+ }
+ else {
+ CONSDBGPRINTF((
+ "'%s' starts in configElement='%s' with parsemode='%s' nest=%hd at source line=%ld",
+ aElementName,
+ getName(),
+ ParseModeNames[fParseMode],
+ fNest,
+ aLine
+ ));
+ #ifdef SYSER_REGISTRATION
+ // check for locked elements or subtrees
+ // - copy parent's lock status
+ if (getParentElement()) fLockedElement=getParentElement()->fLockedElement;
+ // - if not already locked, see if locked section starts with this element
+ if (!fLockedElement) {
+ getAttrBool(aAttributes,"locked",fLockedElement,true);
+ }
+ // Perform CRC sum if locked element
+ if (fLockedElement) {
+ CONSDBGPRINTF((
+ "'%s' is locked and is included in LockCRC = 0x%04hX",
+ aElementName,
+ getRootElement()->getConfigLockCRC()
+ ));
+ // CRC over opening element name
+ getRootElement()->addToLockCRC(aElementName);
+ // CRC over all attributes
+ if (aAttributes) {
+ const char **attrs=aAttributes;
+ const char *attelem;
+ while ((attelem=*attrs++)!=0) {
+ // alternating names and values
+ getRootElement()->addToLockCRC(attelem);
+ }
+ }
+ }
+ #endif
+ // check conditional parsing
+ bool condmet=true; // assume parsing
+ cAppCharP cond;
+ string vv,val;
+ // check platform filter
+ if (condmet) {
+ cAppCharP pf=getAttr(aAttributes,"platform");
+ if (pf && strucmp(pf,SYSYNC_PLATFORM_NAME)!=0) {
+ // tag is not for this platform, ignore
+ condmet=false;
+ }
+ }
+ // check for config var conditionals
+ if (condmet) {
+ cond=getAttr(aAttributes,"if");
+ if (cond) {
+ // check for value comparison
+ string nam;
+ // - determine comparison
+ cAppCharP p2,p = cond;
+ int cmpres = 2; // 2: invalid, 1: var > cond, -1: var < cond, 0: var=cond
+ bool neg=false;
+ appChar c;
+ while ((c=*p)) {
+ p2=p++;
+ if (c=='=') {
+ if (*p=='=') p++; // = and == are equivalent
+ cmpres=0; // must be equal
+ break;
+ }
+ else if (c=='!' && *p=='=') {
+ p++;
+ cmpres=0;
+ neg=true; // must not be equal
+ break;
+ }
+ else if (c=='>') {
+ if (*p=='=') {
+ p++;
+ cmpres=-1; // var >= cond is equal to NOT val < cond
+ neg=true;
+ }
+ else {
+ cmpres=1; // var > cond
+ }
+ break;
+ }
+ else if (c=='<') {
+ if (*p=='=') {
+ p++;
+ cmpres=1; // var <= cond is equal to NOT val > cond
+ neg=true;
+ }
+ else {
+ cmpres=-1; // var < cond
+ }
+ break;
+ }
+ }
+ // now value is at *p or we have not found a comparison op
+ if (cmpres<2) {
+ nam.assign(cond,p2-cond);
+ val=p;
+ }
+ else {
+ nam=cond;
+ val.erase();
+ }
+ // get var - if it does not exist, comparison always renders false
+ if (nam=="version") {
+ // special comparison mode for version
+ // - parse reference into version components
+ uInt16 maj=0,min=0,rev=0,bld=0;
+ p = val.c_str();
+ do {
+ p+=StrToUShort(p,maj); if (*p++!='.') break;
+ p+=StrToUShort(p,min); if (*p++!='.') break;
+ p+=StrToUShort(p,rev); if (*p++!='.') break;
+ p+=StrToUShort(p,bld);
+ } while(false);
+ // - compare with hexversion
+ StringObjPrintf(val,"%02X%02X%02X%02X",maj,min,rev,bld);
+ nam="hexversion";
+ }
+ // get config var to perform condition check
+ condmet=getSyncAppBase()->getConfigVar(nam.c_str(),vv);
+ if (condmet) {
+ // var exists, perform comparison
+ if (cmpres>=2) {
+ // no comparison, but just boolean check. Non-bool but empty=false, non-bool-non-empty=true
+ condmet = !vv.empty();
+ if (condmet) StrToBool(vv.c_str(), condmet);
+ }
+ else {
+ int res = strcmp(vv.c_str(),val.c_str());
+ res = res>0 ? 1 : (res<0 ? -1 : 0);
+ condmet = neg != (cmpres==res);
+ }
+ }
+ }
+ }
+ // check for ifdef
+ if (condmet) {
+ cond=getAttr(aAttributes,"ifdef");
+ if (cond)
+ condmet = getSyncAppBase()->getConfigVar(cond,vv);
+ }
+ // check for ifndef
+ if (condmet) {
+ cond=getAttr(aAttributes,"ifndef");
+ if (cond)
+ condmet = !getSyncAppBase()->getConfigVar(cond,vv);
+ }
+ // skip this tag if conditions not met
+ if (!condmet) {
+ // skip everything inside
+ CONSDBGPRINTF(("Condition for parsing not met - skipping tag"));
+ expectAll();
+ return true; // ok, go on
+ }
+ // Now we know that we must actually parse this tag
+ // check configvar expansion mode
+ fCfgVarExp=0; // default to automatic (i.e. certain content such as paths or macrostrings will be expanded without "expand" attr)
+ cAppCharP cm=getAttr(aAttributes,"expand");
+ if (cm) {
+ // explicit expand directive for this tag (includes expandable attributes of this tag,
+ // but only attributes queried with getAttrExpanded() can expand at all)
+ bool b;
+ if (strucmp(cm,"single")==0) fCfgVarExp=1;
+ else if (StrToBool(cm,b)) fCfgVarExp = b ? 2 : -1;
+ }
+ // check for use-everywhere special tags
+ if (strucmp(aElementName,"configmsg")==0) {
+ bool iserr=true;
+ cAppCharP msg=getAttr(aAttributes,"error");
+ if (!msg) {
+ msg=getAttr(aAttributes,"warning");
+ iserr=false;
+ }
+ if (!msg) msg="Error: found <configmsg>";
+ ReportError(iserr,msg);
+ expectAll();
+ return false; // generate error message
+ }
+ // check if we are re-entering the same object again (trying to overwrite)
+ if (fCompleted) {
+ ReportError(true,"Duplicate definition of <%s>",aElementName);
+ expectAll();
+ return false; // not allowed, generate error message
+ }
+ if (fParseMode==pamo_element || fParseMode==pamo_nested) {
+ // expecting element
+ fTempString.erase();
+ if (localStartElement(aElementName,aAttributes,aLine)) {
+ // element known and parsing initialized ok
+ #ifdef SYSER_REGISTRATION
+ if (fLockedElement && fChildParser==NULL && fParseMode!=pamo_element && fParseMode!=pamo_nested)
+ fHadLockedSubs=true; // flag that this element has processed non-child subelements in locked mode
+ #endif
+ return true;
+ }
+ else {
+ // unknown element: read over all its contents
+ ReportError(false,"invalid tag <%s>",aElementName);
+ expectAll();
+ return false; // element not known, generate error message
+ }
+ }
+ else if (fParseMode==pamo_all) {
+ // read over contents
+ fNest++;
+ }
+ else {
+ ReportError(false,"no XML tag expected here");
+ }
+ return true;
+ }
+} // TConfigElement::startElement
+
+
+// character data of current element
+void TConfigElement::charData(const char *aCharData, sInt32 aNumChars)
+{
+ if (fChildParser) {
+ // parsing in a sub-level, delegate
+ return fChildParser->charData(aCharData,aNumChars);
+ }
+ else {
+ if (fParseMode==pamo_all /* %%% not needed, I think || fNest>0 */) {
+ // just ignore
+ }
+ else if (fParseMode==pamo_element || fParseMode==pamo_nested || fParseMode==pamo_end || fParseMode==pamo_endnested) {
+ // only whitespace allowed here
+ while (aNumChars--) {
+ if (!isspace(*aCharData++)) {
+ ReportError(false,"no character data expected");
+ break;
+ }
+ }
+ }
+ else {
+ // accumulate char data in string
+ fTempString.append(aCharData,aNumChars);
+ }
+ }
+} // TConfigElement::charData
+
+
+
+// read over all contents of current TConfigElement
+void TConfigElement::expectAll(void)
+{
+ // we are already in an element but have no non-decrementing
+ // parse mode set like expectEmpty() or expectString() etc.
+ // so nest count must be incremented to balance decrement occurring
+ // at next element end.
+ fExpectAllNestStart = fParseMode==pamo_nested ? fNest : -1; // remember where we left nested mode and entered ignoring mode
+ fNest++;
+ fParseMode=pamo_all;
+} // TConfigElement::expectAll
+
+
+// expect Enum element
+void TConfigElement::expectEnum(sInt16 aDestSize,void *aPtr, const char * const aEnumNames[], sInt16 aNumEnums)
+{
+ // save params
+ fParseEnumArray = aEnumNames;
+ fParseEnumNum = aNumEnums;
+ // determine mode and destination
+ switch (aDestSize) {
+ case 1 :
+ fParseMode=pamo_enum1by;
+ fResultPtr.fCharP=(char *)aPtr;
+ break;
+ case 2 :
+ fParseMode=pamo_enum2by;
+ fResultPtr.fShortP=(sInt16 *)aPtr;
+ break;
+ case 4 :
+ fParseMode=pamo_enum4by;
+ fResultPtr.fLongP=(sInt32 *)aPtr;
+ break;
+ default:
+ SYSYNC_THROW(TConfigParseException("expectEnum: invalid enum size"));
+ }
+} // TConfigElement::expectEnum
+
+
+// delegate parsing of a single element to another config element
+// after processing aElementName and all subtags, processing will return to this object
+// (rather than waiting for an end tag in aConfigElemP)
+bool TConfigElement::delegateParsingTo(TConfigElement *aConfigElemP, const char *aElementName, const char **aAttributes, sInt32 aLine)
+{
+ if (aConfigElemP) {
+ expectChildParsing(*aConfigElemP);
+ fParseMode=pamo_delegated;
+ // let child parse the current tag right away
+ return aConfigElemP->localStartElement(aElementName,aAttributes,aLine);
+ }
+ else
+ return false; // if we have no delegate, we can't understand this tag
+} // TConfigElement::delegateParsingTo
+
+
+// end of element, returns true when this config element is done parsing
+bool TConfigElement::endElement(const char *aElementName, bool aIsDelegated)
+{
+ if (fChildParser) {
+ // parsing in a real or simulated (delegateParsingTo) sub-level
+ if (fChildParser->endElement(aElementName,fParseMode==pamo_delegated)) {
+ // child has finished parsing
+ fChildParser=NULL; // handle next element myself
+ if (aIsDelegated) {
+ // - we were delegated to process a single element from another element.
+ // So, we nust not continue parsing here, but pass back to parent
+ // for next element
+ return true;
+ }
+ // otherwise, wait here for next element to start (or encosing element to end)
+ fParseMode=pamo_element; // expect another element or end of myself
+ }
+ else {
+ // child is still parsing, so am I
+ return false;
+ }
+ }
+ else {
+ CONSDBGPRINTF((
+ "'%s' ends in configElement='%s' with parsemode='%s' nest=%hd, aIsDelegated=%d",
+ aElementName,
+ getName(),
+ ParseModeNames[fParseMode],
+ fNest,
+ aIsDelegated
+ ));
+ #ifdef SYSER_REGISTRATION
+ // Perform CRC sum if locked element
+ if (fLockedElement) {
+ // CRC over CharData
+ getRootElement()->addToLockCRC(fTempString.c_str());
+ }
+ #endif
+ const char *p; // BCPPB: declaring vars in case does not work.
+ size_t n,lnwsp;
+ bool hlp;
+ timecontext_t tctx;
+ // expand macros in string first
+ if (fCfgVarExp==0) {
+ // auto mode
+ fCfgVarExp = fParseMode==pamo_path || fParseMode==pamo_macrostring ? 2 : -1;
+ }
+ // do config variable expansion now (or not, according to fCfgVarExp)
+ getSyncAppBase()->expandConfigVars(fTempString,fCfgVarExp,this,aElementName);
+ // now parse
+ switch (fParseMode) {
+ case pamo_end:
+ case pamo_endnested:
+ if (fNest>0) {
+ // Note: normal empty elements are NOT considered nested elements by default, only if
+ // they request endnested mode explicitly
+ if (fParseMode==pamo_endnested)
+ nestedElementEnd(); // inform eventual parser of nested element that a nested element ends here
+ // back to nested
+ fParseMode=pamo_nested;
+ }
+ else fParseMode=pamo_element; // back to normal element parsing
+ return false; // do not exit
+ case pamo_all:
+ case pamo_nested:
+ if (fNest>0) {
+ // not yet finished with startlevel, continue with pamo_all/pamo_nested
+ fNest--;
+ if (fExpectAllNestStart>0) {
+ // we are in expectAll mode within pamo_nested
+ if(fNest==fExpectAllNestStart) {
+ // reached level where we started ignoring contents before
+ fExpectAllNestStart = -1; // processed now
+ fParseMode = pamo_nested; // back to nested (but active) parsing, like in Mime-Dir profile
+ }
+ else {
+ // NOP here - do NOT call nestedElementEnd()
+ }
+ }
+ else {
+ // end of active nested element
+ nestedElementEnd(); // inform eventual parser of nested element
+ if (fNest==0) {
+ // if back on nest level 0, switch to pamo_element
+ fParseMode = pamo_element;
+ }
+ }
+ return false; // stay in this element
+ }
+ // nested or all at level 0 cause handling like pamo_element
+ case pamo_element:
+ // end of element while looking for elements:
+ // this is end of this config element itself
+ // - flag completion of this element
+ fCompleted=true; // prevents re-entry
+ // Resolve if this is element says it is self-contained (no references to other elements
+ // that might follow later in the config file)
+ if (fResolveImmediately) {
+ // resolve element NOW, last pass
+ localResolve(true);
+ }
+ // - return parsing authority to caller
+ return true;
+ case pamo_cstring:
+ // interpret C-type escapes (only \t,\r,\n and \xXX, no octal)
+ #ifdef SYSER_REGISTRATION
+ if (fHadLockedSubs && !fResultPtr.fStringP->empty())
+ ReportError(true,"Duplicate definition of <%s>",aElementName);
+ #endif
+ fResultPtr.fStringP->erase();
+ CStrToStrAppend(fTempString.c_str(), *(fResultPtr.fStringP));
+ break;
+ case pamo_string:
+ case pamo_macrostring:
+ case pamo_path:
+ #ifdef SYSER_REGISTRATION
+ if (fHadLockedSubs && !fResultPtr.fStringP->empty())
+ ReportError(true,"Duplicate definition of <%s>",aElementName);
+ #endif
+ // remove spaces at beginning and end
+ fResultPtr.fStringP->erase();
+ p = fTempString.c_str();
+ // - skip leading spaces
+ while (*p && isspace(*p)) ++p;
+ // - copy chars and convert all wsp to spaces
+ n=0; lnwsp=0;
+ while (*p) {
+ ++n; // count char
+ if (isspace(*p))
+ fResultPtr.fStringP->append(" ");
+ else {
+ fResultPtr.fStringP->append(p,1);
+ lnwsp=n; // remember last non-whitespace
+ }
+ ++p;
+ }
+ // - remove trailing spaces
+ fResultPtr.fStringP->resize(lnwsp);
+ // - if path requested, shape it up if needed
+ if (fParseMode==pamo_path) {
+ makeOSDirPath(*(fResultPtr.fStringP));
+ }
+ break;
+ case pamo_rawstring:
+ #ifdef SYSER_REGISTRATION
+ if (fHadLockedSubs && !fResultPtr.fStringP->empty())
+ ReportError(true,"Duplicate definition of <%s>",aElementName);
+ #endif
+ (*(fResultPtr.fStringP))=fTempString;
+ break;
+ case pamo_field:
+ #ifdef SCRIPT_SUPPORT
+ *(fResultPtr.fShortP) = getFieldIndex(fTempString.c_str(),fFieldListP,fScriptContextP);
+ #else
+ *(fResultPtr.fShortP) = getFieldIndex(fTempString.c_str(),fFieldListP);
+ #endif
+ break;
+ #ifdef SCRIPT_SUPPORT
+ case pamo_script:
+ case pamo_functiondef:
+ SYSYNC_TRY {
+ if (fParseMode==pamo_functiondef)
+ TScriptContext::TokenizeAndResolveFunction(getSyncAppBase(),fExpectLine,fTempString.c_str(),(*(fResultPtr.fFuncDefP)));
+ else {
+ #ifdef SYSER_REGISTRATION
+ if (fHadLockedSubs && !fResultPtr.fStringP->empty())
+ ReportError(true,"Duplicate definition of <%s>",aElementName);
+ #endif
+ TScriptContext::Tokenize(getSyncAppBase(),aElementName,fExpectLine,fTempString.c_str(),(*(fResultPtr.fStringP)),fExpectContextFuncs,false,fExpectNoDeclarations);
+ }
+ }
+ SYSYNC_CATCH (exception &e)
+ ReportError(true,"Script Error: %s",e.what());
+ SYSYNC_ENDCATCH
+ break;
+ #endif
+ case pamo_timestamp:
+ // expect timestamp, store as UTC
+ if (ISO8601StrToTimestamp(fTempString.c_str(), *(fResultPtr.fTimestampP), tctx)==0)
+ ReportError(false,"bad ISO8601 timestamp value");
+ if (TCTX_IS_UNKNOWN(tctx)) tctx=TCTX_SYSTEM;
+ TzConvertTimestamp(*(fResultPtr.fTimestampP),tctx,TCTX_UTC,getSyncAppBase()->getAppZones());
+ break;
+ case pamo_timezone:
+ // time zone by name
+ if (!TimeZoneNameToContext(fTempString.c_str(), *(fResultPtr.fTimeContextP), getSyncAppBase()->getAppZones()))
+ ReportError(false,"invalid/unknown timezone name");
+ break;
+ case pamo_vtimezone:
+ // definition of custom time zone in vTimezone format
+ if (!VTIMEZONEtoInternal(fTempString.c_str(), tctx, fResultPtr.fGZonesP, getDbgLogger()))
+ ReportError(false,"invalid vTimezone defintion");
+ break;
+ case pamo_boolean:
+ if (!StrToBool(fTempString.c_str(),*(fResultPtr.fBoolP)))
+ ReportError(false,"bad boolean value");
+ break;
+ case pamo_tristate:
+ if (fTempString.empty() || fTempString=="unspecified" || fTempString=="default")
+ *(fResultPtr.fByteP)=-1; // unspecified
+ else {
+ if (!StrToBool(fTempString.c_str(),hlp))
+ ReportError(false,"bad boolean value");
+ else
+ *(fResultPtr.fByteP)= hlp ? 1 : 0;
+ }
+ break;
+ case pamo_int64:
+ if (!StrToLongLong(fTempString.c_str(),*(fResultPtr.fLongLongP)))
+ ReportError(false,"bad integer (64bit) value");
+ break;
+ case pamo_int32:
+ if (!StrToLong(fTempString.c_str(),*(fResultPtr.fLongP)))
+ ReportError(false,"bad integer (32bit) value");
+ break;
+ case pamo_char:
+ if (fTempString.size()>1)
+ ReportError(false,"single char or nothing (=NUL char) expected");
+ if (fTempString.size()==0)
+ *(fResultPtr.fCharP) = 0;
+ else
+ *(fResultPtr.fCharP) = fTempString[0];
+ break;
+ case pamo_idCode:
+ // Palm/MacOS-type 4-char code
+ if (fTempString.size()!=4)
+ ReportError(false,"id code must be exactly 4 characters");
+ *(fResultPtr.fLongP) =
+ ((uInt32)fTempString[0] << 24) +
+ ((uInt32)fTempString[1] << 16) +
+ ((uInt16)fTempString[2] << 8) +
+ ((uInt8)fTempString[3]);
+ break;
+ case pamo_int16:
+ if (!StrToShort(fTempString.c_str(),*(fResultPtr.fShortP)))
+ ReportError(false,"bad integer (16bit) value");
+ break;
+ case pamo_enum1by:
+ case pamo_enum2by:
+ case pamo_enum4by:
+ sInt16 tempenum;
+ if (!StrToEnum(fParseEnumArray,fParseEnumNum,tempenum,fTempString.c_str()))
+ ReportError(false,"bad enumeration value '%s'",fTempString.c_str());
+ else {
+ // now assign enum
+ if (fParseMode==pamo_enum1by) *fResultPtr.fCharP = tempenum;
+ else if (fParseMode==pamo_enum2by) *fResultPtr.fShortP = tempenum;
+ else if (fParseMode==pamo_enum4by) *fResultPtr.fLongP = tempenum;
+ }
+ break;
+ default:
+ SYSYNC_THROW(TConfigParseException(DEBUGTEXT("Unknown parse mode","ce1")));
+ }
+ // normal case: end of simple element parsed at same level as parent
+ if (aIsDelegated) {
+ // - we were delegated to process a single element from another element.
+ // So, we nust not continue parsing here, but pass back to parent
+ // for next element
+ return true;
+ }
+ else {
+ // - expect next element
+ fParseMode=pamo_element;
+ }
+ }
+ // end of embedded element, but not of myself
+ #ifdef SYSER_REGISTRATION
+ #ifdef CONSDEBUG
+ if (fLockedElement) {
+ CONSDBGPRINTF((
+ "'%s' was locked and was included in LockCRC = 0x%04hX",
+ aElementName,
+ getRootElement()->getConfigLockCRC()
+ ));
+ }
+ #endif
+ // - back to parent's lock status (or false if no parent)
+ fLockedElement=getParentElement() ? getParentElement()->fLockedElement : false;
+ #endif
+ // do not return to parent config element
+ return false;
+} // TConfigElement::endElement
+
+
+
+// reset error
+void TRootConfigElement::resetError(void)
+{
+ fError=false;
+ fErrorMessage.erase();
+} // TRootConfigElement::ResetError
+
+
+// set error
+void TRootConfigElement::setError(bool aFatal, const char *aMsg)
+{
+ if (!fErrorMessage.empty())
+ fErrorMessage += '\n'; // multiple messages on multiple lines
+ fErrorMessage.append(aMsg);
+ if (aFatal && fFatalError==LOCERR_OK)
+ fFatalError=LOCERR_CFGPARSE; // this is a config parse error
+ fError=true;
+} // TRootConfigElement::setError
+
+
+// check for error message
+const char *TRootConfigElement::getErrorMsg(void)
+{
+ if (!fError) return NULL;
+ return fErrorMessage.c_str();
+} // TRootConfigElement::GetErrorMsg
+
+
+// reset parsing (=reset error and fatal errors)
+void TRootConfigElement::ResetParsing(void)
+{
+ resetError();
+ fDocStarted=false;
+ fFatalError=LOCERR_OK;
+ #ifdef SYSER_REGISTRATION
+ fLockCRC=0; // no CRC lock sum yet
+ #endif
+ TConfigElement::ResetParsing();
+} // TRootConfigElement::ResetParsing
+
+
+#endif
+
+// resolve config tree and catch errors
+bool TRootConfigElement::ResolveAll(void)
+{
+ SYSYNC_TRY {
+ Resolve(true);
+ return true;
+ }
+ SYSYNC_CATCH (TConfigParseException &e)
+ #ifndef HARDCODED_CONFIG
+ ReportError(true,e.what());
+ #endif
+ return false;
+ SYSYNC_ENDCATCH
+} // TRootConfigElement::ResolveAll
+
+
+#ifndef HARDCODED_CONFIG
+
+// start of element, this config element decides who processes this element
+bool TRootConfigElement::startElement(const char *aElementName, const char **aAttributes, sInt32 aLine)
+{
+ if (!fDocStarted) {
+ // document not yet started
+ if (strucmp(aElementName,XMLCONFIG_DOCNAME)==0) {
+ // check version
+ const char* vers = getAttr(aAttributes,"version");
+ if (!vers) {
+ ReportError(true,"Missing version attribute for document");
+ expectAll(); // ignore everything
+ }
+ else if (strucmp(vers,XMLCONFIG_DOCVERSION)!=0) {
+ ReportError(true,
+ "Bad config document version (expected %s, found %s)",
+ XMLCONFIG_DOCVERSION,
+ vers
+ );
+ expectAll(); // ignore everything inside that element
+ }
+ fDocStarted=true; // started normally
+ return true;
+ }
+ else {
+ // invalid element
+ return false;
+ }
+ }
+ else {
+ return TConfigElement::startElement(aElementName,aAttributes,aLine);
+ }
+} // TRootConfigElement::startElement
+
+
+#ifdef SYSER_REGISTRATION
+// add config text to locking CRC
+void TRootConfigElement::addToLockCRC(const char *aCharData, size_t aNumChars)
+{
+ size_t n = aNumChars ? aNumChars : strlen(aCharData);
+ char c;
+ bool lastwasctrl=false;
+ while (n--) {
+ c=*aCharData++;
+ // compact multiple whitespace to single char
+ if ((uInt8)c<=' ') {
+ if (lastwasctrl) continue;
+ lastwasctrl=true;
+ // treat them all as spaces
+ c=' ';
+ }
+ else {
+ lastwasctrl=false; // this is a non-space
+ }
+ // add to CRC
+ fLockCRC=sysync::sysync_crc16(fLockCRC,(uInt8)c);
+ }
+} // TRootConfigElement::addToLockCRC
+#endif
+
+#endif // no hardcode config
+
+
+/* end of TConfigElement implementation */
+
+// eof
diff --git a/src/sysync/configelement.h b/src/sysync/configelement.h
new file mode 100755
index 0000000..cbc92f2
--- /dev/null
+++ b/src/sysync/configelement.h
@@ -0,0 +1,285 @@
+/*
+ * File: ConfigElement.h
+ *
+ * Author: Lukas Zeller (luz@synthesis.ch)
+ *
+ * TConfigElement
+ * Element of hierarchical configuration
+ *
+ * Copyright (c) 2001-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ * 2001-11-14 : luz : created
+ */
+
+#ifndef ConfigElement_H
+#define ConfigElement_H
+
+#include "prefix_file.h"
+
+#include "sysync.h"
+#include "syncexception.h"
+#include "sysync_globs.h"
+
+namespace sysync {
+
+
+// forward
+class TConfigElement;
+class TDebugLogger;
+
+
+// parse modes
+typedef enum {
+ pamo_element, // normal, expecting sub-elements
+ pamo_nested, // like pamo_element, but scanning nested elements in same TConfigElement
+ pamo_delegated, // I have delegated parsing of a single sub-element of mine to another element (without XML nesting)
+ pamo_end, // expecting end of element (empty element)
+ pamo_endnested, // expecting end of nested element (i.e. will call nestedElementEnd())
+ pamo_all, // read over all content
+ pamo_string,
+ pamo_cstring,
+ pamo_rawstring,
+ pamo_macrostring, // string which can contain macros to substitute config vars in $xxx or $() format
+ #ifdef SCRIPT_SUPPORT
+ pamo_script,
+ pamo_functiondef,
+ #endif
+ pamo_field, // field ID
+ pamo_path, // string is updated such that a filename can be appended directly, otherwise like pamo_macrostring
+ pamo_boolean,
+ pamo_tristate,
+ pamo_timestamp,
+ pamo_timezone, // time zone name
+ pamo_vtimezone, // time zone definition in vTimezone format
+ pamo_idCode,
+ pamo_char,
+ pamo_int64,
+ pamo_int32,
+ pamo_int16,
+ pamo_enum1by,
+ pamo_enum2by,
+ pamo_enum4by,
+ numParseModes
+} TCfgParseMode;
+
+
+
+class TConfigParseException : public TSyncException
+{
+ typedef TSyncException inherited;
+public:
+ TConfigParseException(const char *aMsg1) : TSyncException(aMsg1) {};
+}; // TConfigParseException
+
+
+class TRootConfigElement; // forward
+class TFieldListConfig;
+#ifdef SCRIPT_SUPPORT
+class TUserScriptFunction;
+class TScriptContext;
+#endif
+
+class TSyncAppBase; // forward
+
+class TConfigElement
+{
+public:
+ // constructor
+ TConfigElement(const char *aElementName, TConfigElement *aParentElementP);
+ virtual ~TConfigElement();
+ virtual void clear(void); // remove all dynamic elements, reset to default values
+ // - called when all parsing is done, intended for cross-element links
+ void Resolve(bool aLastPass);
+ // - called when app should save its persistent state
+ virtual void saveAppState(void) { /* nop by default */};
+ #ifndef HARDCODED_CONFIG
+ // Sax parsing
+ // - report parsing error (may only be called from within parsing methods)
+ void ReportError(bool aFatal, const char *aMessage, ...);
+ bool fail(const char *aMessage, ...);
+ // - start of element, this config element decides who processes this element
+ virtual bool startElement(const char *aElementName, const char **aAttributes, sInt32 aLine);
+ // - character data of current element
+ void charData(const char *aCharData, sInt32 aNumChars);
+ // - end of element, returns true when this config element is done parsing
+ bool endElement(const char *aElementName, bool aIsDelegated=false);
+ #endif
+ // access to nodes
+ // - root node
+ TRootConfigElement *getRootElement(void) { return fRootElementP; };
+ // - parent node, NULL if none
+ TConfigElement *getParentElement(void) { return fParentElementP; };
+ // - get sync app base (application root)
+ TSyncAppBase *getSyncAppBase(void);
+ // - convenience version for getting time
+ lineartime_t getSystemNowAs(timecontext_t aContext);
+ // - name
+ const char *getName(void) { return fElementName.c_str(); };
+ // Public properties
+ // - name of element
+ string fElementName;
+ #ifndef HARDCODED_CONFIG
+ // helpers for attributes
+ static const char *getAttr(const char **aAttributes, const char *aAttrName);
+ bool getAttrExpanded(const char **aAttributes, const char *aAttrName, string &aValue, bool aDefaultExpand);
+ static bool getAttrBool(const char **aAttributes, const char *aAttrName, bool &aBool, bool aOpt=false);
+ static bool getAttrShort(const char **aAttributes, const char *aAttrName, sInt16 &aShort, bool aOpt=false);
+ static bool getAttrLong(const char **aAttributes, const char *aAttrName, sInt32 &aLong, bool aOpt=false);
+ #ifdef SCRIPT_SUPPORT
+ static sInt16 getFieldIndex(cAppCharP aFieldName, TFieldListConfig *aFieldListP, TScriptContext *aScriptContextP=NULL);
+ #else
+ static sInt16 getFieldIndex(cAppCharP aFieldName, TFieldListConfig *aFieldListP);
+ #endif
+ // public helpers
+ bool delegateParsingTo(TConfigElement *aConfigElemP, const char *aElementName, const char **aAttributes, sInt32 aLine);
+ void expectChildParsing(TConfigElement &aConfigElem)
+ { aConfigElem.ResetParsing(); fChildParser=&aConfigElem; }; // let child element parse contents
+ void expectAll(void);
+ void expectEmpty(bool nested=false) { fParseMode = nested ? pamo_endnested : pamo_end; };
+ #endif
+ // locked config sections support
+ #ifdef SYSER_REGISTRATION
+ bool fLockedElement; // set while parsing locked element
+ bool fHadLockedSubs; // set if this element contained locked non-child subelements (will prevent string overwrites)
+ #endif
+protected:
+ #ifdef SYDEBUG
+ TDebugLogger *getDbgLogger(void);
+ uInt32 getDbgMask(void);
+ #endif
+ // - called after parsing is finished, should resolve links and throws on error
+ virtual void localResolve(bool /* aLastPass */) { }; // nop
+ #ifndef HARDCODED_CONFIG
+ // parsing
+ virtual void ResetParsing(void);
+ // - checks if element known and decides what to do
+ virtual bool localStartElement(const char *aElementName, const char **aAttributes, sInt32 aLine) { return false; } // unknown element
+ // - called after nested level ends
+ virtual void nestedElementEnd(void) {};
+ // - helpers
+ void expectNested(void) { fParseMode=pamo_nested; };
+ void startNestedParsing()
+ { fNest++; expectNested(); } // let same element parse nested levels
+ void expectString(string &aString) { fParseMode=pamo_string; fResultPtr.fStringP=&aString; };
+ void expectMacroString(string &aString) { fParseMode=pamo_macrostring; fResultPtr.fStringP=&aString; };
+ void expectCString(string &aString) { fParseMode=pamo_cstring; fResultPtr.fStringP=&aString; };
+ void expectRawString(string &aString) { fParseMode=pamo_rawstring; fResultPtr.fStringP=&aString; };
+ #ifdef SCRIPT_SUPPORT
+ void expectScript(string &aTScript,sInt32 aLine, const TFuncTable *aContextFuncs, bool aNoDeclarations=false) { fParseMode=pamo_script; fResultPtr.fStringP=&aTScript; fExpectLine=aLine; fExpectContextFuncs=aContextFuncs; fExpectNoDeclarations=aNoDeclarations; };
+ void expectFunction(TUserScriptFunction &aFuncDef,sInt32 aLine=0) { fParseMode=pamo_functiondef; fResultPtr.fFuncDefP=&aFuncDef; fExpectLine=aLine; };
+ #endif
+ void expectPath(string &aString) { fParseMode=pamo_path; fResultPtr.fStringP=&aString; };
+ void expectBool(bool &aBool) { fParseMode=pamo_boolean; fResultPtr.fBoolP=&aBool; };
+ void expectTristate(sInt8 &aTristate) { fParseMode=pamo_tristate; fResultPtr.fByteP=&aTristate; };
+ void expectTimestamp(lineartime_t &aTimestamp) { fParseMode=pamo_timestamp; fResultPtr.fTimestampP=&aTimestamp; };
+ void expectTimezone(timecontext_t &aTimeContext) { fParseMode=pamo_timezone; fResultPtr.fTimeContextP=&aTimeContext; };
+ void expectVTimezone(GZones *aZonesToAddTo) { fParseMode=pamo_vtimezone; fResultPtr.fGZonesP=aZonesToAddTo; };
+ void expectIdCode(uInt32 &aLong) { fParseMode=pamo_idCode; fResultPtr.fLongP=(sInt32 *)(&aLong); };
+ void expectChar(char &aChar) { fParseMode=pamo_char; fResultPtr.fCharP=(char *)(&aChar); };
+ void expectInt64(sInt64 &aLongLong) { fParseMode=pamo_int64; fResultPtr.fLongLongP=&aLongLong; };
+ void expectInt32(sInt32 &aLong) { fParseMode=pamo_int32; fResultPtr.fLongP=&aLong; };
+ void expectUInt32(uInt32 &aLong) { fParseMode=pamo_int32; fResultPtr.fLongP=(sInt32 *)(&aLong); };
+ void expectInt16(sInt16 &aShort) { fParseMode=pamo_int16; fResultPtr.fShortP=&aShort; };
+ void expectUInt16(uInt16 &aShort) { fParseMode=pamo_int16; fResultPtr.fShortP=(sInt16 *)(&aShort); };
+ void expectEnum(sInt16 aDestSize,void *aPtr, const char * const aEnumNames[], sInt16 aNumEnums);
+ #ifdef SCRIPT_SUPPORT
+ void expectFieldID(sInt16 &aShort, TFieldListConfig *aFieldListP, TScriptContext *aScriptContextP=NULL)
+ { fParseMode=pamo_field; fResultPtr.fShortP=&aShort; fFieldListP=aFieldListP; fScriptContextP=aScriptContextP; };
+ #else
+ void expectFieldID(sInt16 &aShort, TFieldListConfig *aFieldListP)
+ { fParseMode=pamo_field; fResultPtr.fShortP=&aShort; fFieldListP=aFieldListP; };
+ #endif
+ #endif
+ // config tree branches (childs)
+ TRootConfigElement *fRootElementP; // root element
+ sInt16 fNest; // nesting count for unprocessed or nested (in same TConfigElement) XML tags
+ sInt16 fExpectAllNestStart; // nesting count where expectAll() was called while in pamo_nested mode
+ bool fResolveImmediately;
+private:
+ TConfigElement *fParentElementP; // parent of this element
+ #ifndef HARDCODED_CONFIG
+ // parsing
+ TConfigElement *fChildParser; // child parser, NULL if none (this is parsing)
+ string fTempString; // string to accumulate chars
+ TCfgParseMode fParseMode;
+ sInt32 fExpectLine; // line number of where expected data started
+ TFieldListConfig *fFieldListP; // field list for expectField
+ #ifdef SCRIPT_SUPPORT
+ const TFuncTable *fExpectContextFuncs; // context-specific function table
+ TScriptContext *fScriptContextP; // script context for expectField
+ bool fExpectNoDeclarations; // flag for tokenizer
+ #endif
+ // - parse params
+ const char * const * fParseEnumArray;
+ sInt16 fParseEnumNum;
+ bool fCompleted; // set when entire element has been scanned ok (not in progress any more)
+ sInt8 fCfgVarExp; // config var expansion mode: -1 = no, 0=auto (depending on expectXXX), 1=yes, 2=yes with recursion
+ // - where to store result (must be in accordance with fParseMode
+ union {
+ string *fStringP;
+ sInt64 *fLongLongP;
+ sInt32 *fLongP;
+ char *fCharP;
+ sInt16 *fShortP;
+ bool *fBoolP;
+ sInt8 *fByteP;
+ lineartime_t *fTimestampP;
+ GZones *fGZonesP;
+ timecontext_t *fTimeContextP;
+ #ifdef SCRIPT_SUPPORT
+ TUserScriptFunction *fFuncDefP;
+ #endif
+ } fResultPtr;
+ #endif // free config
+}; // TConfigElement
+
+
+class TRootConfigElement : public TConfigElement
+{
+ typedef TConfigElement inherited;
+ friend class TConfigElement;
+public:
+ TRootConfigElement(TSyncAppBase *aSyncAppBaseP) : TConfigElement("root",NULL) { fSyncAppBaseP=aSyncAppBaseP; fRootElementP=this; };
+ bool ResolveAll(void);
+ #ifndef HARDCODED_CONFIG
+ void resetError(void);
+ const char *getErrorMsg(void);
+ localstatus getFatalError(void) { return fFatalError; };
+ void setError(bool aFatal, const char *aMsg);
+ void setFatalError(localstatus aLocalFatalError) { fFatalError=aLocalFatalError; };
+ virtual void ResetParsing(void);
+ // - start of element, this config element decides who processes this element
+ virtual bool startElement(const char *aElementName, const char **aAttributes, sInt32 aLine);
+ #endif
+protected:
+ // Sync app base
+ TSyncAppBase *fSyncAppBaseP;
+ #ifndef HARDCODED_CONFIG
+ // global parsing error handling
+ bool fError; // error occurred
+ localstatus fFatalError; // set<>0 if parsing should be aborted
+ string fErrorMessage; // error message
+public:
+ // section locking support
+ #ifndef SYSER_REGISTRATION
+ // - dummy
+ uInt16 getConfigLockCRC(void) { return 0; };
+ #else
+ // - get current config lock CRC
+ uInt16 getConfigLockCRC(void) { return fLockCRC; };
+ // - add config text to locking CRC
+ void addToLockCRC(const char *aCharData, size_t aNumChars=0);
+private:
+ uInt16 fLockCRC; // accumulated CRC of all locked elements
+ #endif
+private:
+ bool fDocStarted;
+ #endif
+};
+
+
+} // namespace sysync
+
+#endif // ConfigElement_H
+
+// eof
diff --git a/src/sysync/cp936_tables_inc.cpp b/src/sysync/cp936_tables_inc.cpp
new file mode 100755
index 0000000..04dc382
--- /dev/null
+++ b/src/sysync/cp936_tables_inc.cpp
@@ -0,0 +1,5568 @@
+const size_t ucs2_to_cp936_numelems = 44054;
+
+const treeval_t ucs2_to_cp936_elements[ucs2_to_cp936_numelems] = {
+ 0x40E6,0x0220,0x0081,0x0081,0x0081,0x01C9,0x0081,0x0152,0x0092,0x0081,0x0081,0x0081,0x0081,0x0087,0x0083,0xA3A4,
+ 0x0083,0xA957,0xA3FE,0x0085,0x0083,0xA956,0xA1EA,0x0081,0xA1E9,0x00F2,0x00B2,0x0092,0x0082,0x0089,0x0085,0x0083,
+ 0xA1AB,0xA3FD,0x0083,0xA3FC,0xA3FB,0x0085,0x0083,0xA3FA,0xA3F9,0x0083,0xA3F8,0xA3F7,0x0091,0x0089,0x0085,0x0083,
+ 0xA3F6,0xA3F5,0x0083,0xA3F4,0xA3F3,0x0085,0x0083,0xA3F2,0xA3F1,0x0083,0xA3F0,0xA3EF,0x0089,0x0085,0x0083,0xA3EE,
+ 0xA3ED,0x0083,0xA3EC,0xA3EB,0x0085,0x0083,0xA3EA,0xA3E9,0x0083,0xA3E8,0xA3E7,0x00A1,0x0091,0x0089,0x0085,0x0083,
+ 0xA3E6,0xA3E5,0x0083,0xA3E4,0xA3E3,0x0085,0x0083,0xA3E2,0xA3E1,0x0083,0xA3E0,0xA3DF,0x0089,0x0085,0x0083,0xA3DE,
+ 0xA3DD,0x0083,0xA3DC,0xA3DB,0x0085,0x0083,0xA3DA,0xA3D9,0x0083,0xA3D8,0xA3D7,0x0091,0x0089,0x0085,0x0083,0xA3D6,
+ 0xA3D5,0x0083,0xA3D4,0xA3D3,0x0085,0x0083,0xA3D2,0xA3D1,0x0083,0xA3D0,0xA3CF,0x0089,0x0085,0x0083,0xA3CE,0xA3CD,
+ 0x0083,0xA3CC,0xA3CB,0x0085,0x0083,0xA3CA,0xA3C9,0x0083,0xA3C8,0xA3C7,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,
+ 0xA3C6,0xA3C5,0x0083,0xA3C4,0xA3C3,0x0085,0x0083,0xA3C2,0xA3C1,0x0083,0xA3C0,0xA3BF,0x0089,0x0085,0x0083,0xA3BE,
+ 0xA3BD,0x0083,0xA3BC,0xA3BB,0x0085,0x0083,0xA3BA,0xA3B9,0x0083,0xA3B8,0xA3B7,0x0091,0x0089,0x0085,0x0083,0xA3B6,
+ 0xA3B5,0x0083,0xA3B4,0xA3B3,0x0085,0x0083,0xA3B2,0xA3B1,0x0083,0xA3B0,0xA3AF,0x0089,0x0085,0x0083,0xA3AE,0xA3AD,
+ 0x0083,0xA3AC,0xA3AB,0x0085,0x0083,0xA3AA,0xA3A9,0x0083,0xA3A8,0xA3A7,0x0081,0x0081,0x0089,0x0085,0x0083,0xA3A6,
+ 0xA3A5,0x0083,0xA1E7,0xA3A3,0x0081,0x0083,0xA3A2,0xA3A1,0x008E,0x0082,0x0082,0x0082,0x0082,0x0082,0x0085,0x0083,
+ 0xA988,0xA987,0x0083,0xA986,0xA985,0x0081,0x00BD,0x009F,0x0090,0x0088,0x0084,0x0082,0xA984,0x0083,0xA983,0xA982,
+ 0x0085,0x0083,0xA981,0xA980,0x0083,0xA97E,0xA97D,0x0089,0x0085,0x0083,0xA97C,0xA97B,0x0083,0xA97A,0xA979,0x0085,
+ 0x0083,0xA978,0xA977,0x0081,0xA976,0x0090,0x0089,0x0085,0x0083,0xA975,0xA974,0x0083,0xA973,0xA972,0x0084,0x0082,
+ 0xA971,0x0083,0xA970,0xA96F,0x0089,0x0085,0x0083,0xA96E,0xA96D,0x0083,0xA96C,0xA96B,0x0085,0x0083,0xA96A,0xA969,
+ 0x0081,0xA968,0x009D,0x008D,0x0085,0x0082,0x0082,0xA6EB,0x0085,0x0083,0xA6EA,0xA6E9,0x0083,0xA6E8,0xA6E5,0x0089,
+ 0x0085,0x0083,0xA6E4,0xA6E7,0x0083,0xA6E6,0xA6EF,0x0085,0x0083,0xA6EE,0xA6E3,0x0083,0xA6E2,0xA6F1,0x0081,0x0089,
+ 0x0085,0x0083,0xA6F0,0xA6E1,0x0083,0xA6E0,0xA6F5,0x0084,0x0081,0xA6F4,0x0083,0xA6F2,0xA955,0x00B5,0x0082,0x0082,
+ 0x0082,0x00AB,0x0095,0x008D,0x0088,0x0084,0x0082,0xFE4F,0x0083,0xFE4E,0xFE4D,0x0082,0x0083,0xFE4C,0xFE4B,0x0081,
+ 0x0084,0x0082,0xFE4A,0x0083,0xFE49,0xFE48,0x008A,0x0085,0x0082,0x0081,0xFE47,0x0082,0x0083,0xFE46,0xFE45,0x0087,
+ 0x0084,0x0082,0xFE44,0x0082,0xFE43,0x0085,0x0083,0xFE42,0xFE41,0x0081,0xFE40,0x0082,0x0082,0x0081,0x0081,0x0082,
+ 0xFDA0,0x0081,0x0097,0x0089,0x0081,0x0081,0x0081,0x0082,0x0081,0x0081,0xFD9F,0x0088,0x0082,0x0081,0x0082,0x0082,
+ 0x0081,0xFD9E,0x0082,0x0081,0x0081,0x0082,0x0081,0xFD9D,0x0081,0x0082,0x0082,0x0082,0x0082,0x0082,0x0082,0xFD9C,
+ 0x0082,0x1F5C,0x0F68,0x076E,0x0370,0x0172,0x0082,0x00F1,0x00B1,0x0091,0x0082,0x0088,0x0084,0x0082,0xFD9B,0x0083,
+ 0xFD9A,0xFD99,0x0085,0x0083,0xFD98,0xFD97,0x0083,0xD9DF,0xB9EA,0x0091,0x0089,0x0085,0x0083,0xFD96,0xFD95,0x0083,
+ 0xFD94,0xEDE8,0x0085,0x0083,0xB9A8,0xC1FA,0x0083,0xFD93,0xFD92,0x0089,0x0085,0x0083,0xFD91,0xFD90,0x0083,0xFD8F,
+ 0xFD8E,0x0085,0x0083,0xFD8D,0xFD8C,0x0083,0xFD8B,0xFD8A,0x00A1,0x0091,0x0089,0x0085,0x0083,0xFD89,0xFD88,0x0083,
+ 0xF6BB,0xC8A3,0x0085,0x0083,0xF6BA,0xF6B9,0x0083,0xF6B8,0xF6B7,0x0089,0x0085,0x0083,0xF6B6,0xF6B5,0x0083,0xC1E4,
+ 0xF6B4,0x0085,0x0083,0xFD87,0xFD86,0x0083,0xF6B3,0xB3DD,0x0091,0x0089,0x0085,0x0083,0xFD85,0xFD84,0x0083,0xFD83,
+ 0xFD82,0x0085,0x0083,0xFD81,0xFD80,0x0083,0xFD7E,0xFD7D,0x0089,0x0085,0x0083,0xFD7C,0xFD7B,0x0083,0xFD7A,0xFD79,
+ 0x0085,0x0083,0xFD78,0xFD77,0x0083,0xFD76,0xFD75,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0xFD74,0xFD73,0x0083,
+ 0xFD72,0xFD71,0x0085,0x0083,0xFD70,0xFD6F,0x0083,0xFD6E,0xFD6D,0x0089,0x0085,0x0083,0xFD6C,0xFD6B,0x0083,0xFD6A,
+ 0xFD69,0x0085,0x0083,0xFD68,0xFD67,0x0083,0xFD66,0xFD65,0x0091,0x0089,0x0085,0x0083,0xFD64,0xFD63,0x0083,0xFD62,
+ 0xFD61,0x0085,0x0083,0xFD60,0xFD5F,0x0083,0xFD5E,0xFD5D,0x0089,0x0085,0x0083,0xFD5C,0xFD5B,0x0083,0xFD5A,0xFD59,
+ 0x0085,0x0083,0xFD58,0xECB4,0x0083,0xC6EB,0xFD57,0x00A1,0x0091,0x0089,0x0085,0x0083,0xFD56,0xFD55,0x0083,0xFD54,
+ 0xFD53,0x0085,0x0083,0xFD52,0xFD51,0x0083,0xFD50,0xFD4F,0x0089,0x0085,0x0083,0xFD4E,0xFD4D,0x0083,0xF7FE,0xFD4C,
+ 0x0085,0x0083,0xFD4B,0xFD4A,0x0083,0xFD49,0xFD48,0x0091,0x0089,0x0085,0x0083,0xF7FD,0xF7FC,0x0083,0xFD47,0xB1C7,
+ 0x0085,0x0083,0xFD46,0xF7FA,0x0083,0xFD45,0xF7FB,0x0089,0x0085,0x0083,0xFD44,0xFD43,0x0083,0xFD42,0xFD41,0x0085,
+ 0x0083,0xFD40,0xFCA0,0x0083,0xFC9F,0xF7F9,0x017F,0x00FF,0x00BF,0x009F,0x008F,0x0087,0x0083,0xFC9E,0x0083,0xFC9D,
+ 0xF7F8,0x0085,0x0083,0xFC9C,0xFC9B,0x0083,0xFC9A,0xFC99,0x0089,0x0085,0x0083,0xFC98,0xFC97,0x0083,0xFC96,0xFC95,
+ 0x0085,0x0083,0xFC94,0xF7F7,0x0083,0xFC93,0xCAF3,0x0091,0x0089,0x0085,0x0083,0xFC92,0xFC91,0x0083,0xFC90,0xFC8F,
+ 0x0085,0x0083,0xFC8E,0xFC8D,0x0083,0xDCB1,0xFC8C,0x0089,0x0085,0x0083,0xD8BB,0xFC8B,0x0083,0xFC8A,0xFC89,0x0085,
+ 0x0083,0xB9C4,0xFC88,0x0083,0xFC87,0xD8BE,0x00A1,0x0091,0x0089,0x0085,0x0083,0xFC86,0xB6A6,0x0083,0xF6BE,0xFC85,
+ 0x0085,0x0083,0xF6BD,0xFC84,0x0083,0xFC83,0xFC82,0x0089,0x0085,0x0083,0xFC81,0xFC80,0x0083,0xFC7E,0xFC7D,0x0085,
+ 0x0083,0xFC7C,0xFC7B,0x0083,0xFC7A,0xFC79,0x0091,0x0089,0x0085,0x0083,0xFC78,0xF6BC,0x0083,0xFC77,0xEDEB,0x0085,
+ 0x0083,0xEDEA,0xFC76,0x0083,0xEDE9,0xFC75,0x0089,0x0085,0x0083,0xFC74,0xFC73,0x0083,0xFC72,0xFC71,0x0085,0x0083,
+ 0xFC70,0xFC6F,0x0083,0xFC6E,0xFC6D,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0xF7F6,0xFC6C,0x0083,0xFC6B,0xFC6A,
+ 0x0085,0x0083,0xFC69,0xF7F5,0x0083,0xF7F2,0xFC68,0x0089,0x0085,0x0083,0xF7F3,0xFC67,0x0083,0xF7F4,0xFC66,0x0085,
+ 0x0083,0xFC65,0xF7F1,0x0083,0xFC64,0xF7EF,0x0091,0x0089,0x0085,0x0083,0xF7F0,0xFC63,0x0083,0xF7EE,0xF7ED,0x0085,
+ 0x0083,0xF7EC,0xFC62,0x0083,0xFC61,0xC4AC,0x0089,0x0085,0x0083,0xFC60,0xFC5F,0x0083,0xFC5E,0xC7AD,0x0085,0x0083,
+ 0xFC5D,0xFC5C,0x0083,0xBADA,0xFC5B,0x00A1,0x0091,0x0089,0x0085,0x0083,0xF0A4,0xC0E8,0x0083,0xCAF2,0xFC5A,0x0085,
+ 0x0083,0xFC59,0xFC58,0x0083,0xD9E4,0xFC57,0x0089,0x0085,0x0083,0xFC56,0xFC55,0x0083,0xFC54,0xBBC6,0x0085,0x0083,
+ 0xFC53,0xFC52,0x0083,0xFC51,0xFC50,0x0091,0x0089,0x0085,0x0083,0xFC4F,0xF7E2,0x0083,0xF7E1,0xFC4E,0x0085,0x0083,
+ 0xC2E9,0xFC4D,0x0083,0xFC4C,0xF4EF,0x0089,0x0085,0x0083,0xFC4B,0xFC4A,0x0083,0xFC49,0xF4F0,0x0085,0x0083,0xFC48,
+ 0xFC47,0x0083,0xFC46,0xFC45,0x0101,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0xFC44,0xFC43,0x0083,0xFC42,0xFC41,
+ 0x0085,0x0083,0xFC40,0xFBA0,0x0083,0xFB9F,0xFB9E,0x0089,0x0085,0x0083,0xFB9D,0xC2F3,0x0083,0xFB9C,0xFB9B,0x0085,
+ 0x0083,0xFB9A,0xFB99,0x0083,0xFB98,0xFB97,0x0091,0x0089,0x0085,0x0083,0xF7EB,0xFB96,0x0083,0xF7EA,0xFB95,0x0085,
+ 0x0083,0xFB94,0xFB93,0x0083,0xFB92,0xFB91,0x0089,0x0085,0x0083,0xFB90,0xFB8F,0x0083,0xFB8E,0xFB8D,0x0085,0x0083,
+ 0xC2B4,0xF7E8,0x0083,0xFB8C,0xFB8B,0x00A1,0x0091,0x0089,0x0085,0x0083,0xFB8A,0xFB89,0x0083,0xFB88,0xFB87,0x0085,
+ 0x0083,0xF7E7,0xFB86,0x0083,0xFB85,0xF7E6,0x0089,0x0085,0x0083,0xF7E5,0xFB84,0x0083,0xFB83,0xFB82,0x0085,0x0083,
+ 0xFB81,0xF7E4,0x0083,0xFB80,0xFB7E,0x0091,0x0089,0x0085,0x0083,0xC2B9,0xF5BA,0x0083,0xFB7D,0xFB7C,0x0085,0x0083,
+ 0xFB7B,0xFB7A,0x0083,0xFB79,0xFB78,0x0089,0x0085,0x0083,0xFB77,0xFB76,0x0083,0xFB75,0xFB74,0x0085,0x0083,0xF0D9,
+ 0xFB73,0x0083,0xF0D7,0xD3A5,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0xFB72,0xFB71,0x0083,0xF0D8,0xF0D6,0x0085,
+ 0x0083,0xF0D5,0xF0D4,0x0083,0xF0D3,0xF0D2,0x0089,0x0085,0x0083,0xF0D1,0xF0D0,0x0083,0xFB70,0xBAD7,0x0085,0x0083,
+ 0xF0CF,0xFB6F,0x0083,0xFB6E,0xFB6D,0x0091,0x0089,0x0085,0x0083,0xFB6C,0xF0CE,0x0083,0xFB6B,0xF0CD,0x0085,0x0083,
+ 0xF0CC,0xF0CB,0x0083,0xFB6A,0xF7BD,0x0089,0x0085,0x0083,0xF0CA,0xFB69,0x0083,0xF0C9,0xFB68,0x0085,0x0083,0xFB67,
+ 0xFB66,0x0083,0xF0C8,0xFB65,0x00A1,0x0091,0x0089,0x0085,0x0083,0xC5F4,0xF0C7,0x0083,0xFB64,0xF0C6,0x0085,0x0083,
+ 0xF0C5,0xC8B5,0x0083,0xF0C4,0xF0C3,0x0089,0x0085,0x0083,0xF0C2,0xF0C1,0x0083,0xB6EC,0xF0C0,0x0085,0x0083,0xBEE9,
+ 0xF0BF,0x0083,0xF0BE,0xFB63,0x0091,0x0089,0x0085,0x0083,0xBAE8,0xF0BD,0x0083,0xB8EB,0xFB62,0x0085,0x0083,0xFB61,
+ 0xF0BC,0x0083,0xF0BB,0xF0B9,0x0089,0x0085,0x0083,0xF0BA,0xF0B8,0x0083,0xCDD2,0xFB60,0x0085,0x0083,0xD4A7,0xF0B6,
+ 0x0083,0xF0B7,0xFB5F,0x027F,0x017F,0x00FF,0x00BF,0x009F,0x008F,0x0087,0x0083,0xD1EC,0x0083,0xFB5E,0xD1BC,0x0085,
+ 0x0083,0xF0B5,0xF0B4,0x0083,0xF0B3,0xF0B2,0x0089,0x0085,0x0083,0xF0B1,0xFB5D,0x0083,0xD1BB,0xC5B8,0x0085,0x0083,
+ 0xFB5C,0xC3F9,0x0083,0xF0B0,0xBCA6,0x0091,0x0089,0x0085,0x0083,0xF0AF,0xC4F1,0x0083,0xFB5B,0xFB5A,0x0085,0x0083,
+ 0xFB59,0xFB58,0x0083,0xFB57,0xFB56,0x0089,0x0085,0x0083,0xFB55,0xFB54,0x0083,0xFB53,0xFB52,0x0085,0x0083,0xFB51,
+ 0xFB50,0x0083,0xFB4F,0xFB4E,0x00A1,0x0091,0x0089,0x0085,0x0083,0xFB4D,0xFB4C,0x0083,0xFB4B,0xFB4A,0x0085,0x0083,
+ 0xFB49,0xFB48,0x0083,0xFB47,0xFB46,0x0089,0x0085,0x0083,0xFB45,0xFB44,0x0083,0xFB43,0xFB42,0x0085,0x0083,0xFB41,
+ 0xFB40,0x0083,0xFAA0,0xFA9F,0x0091,0x0089,0x0085,0x0083,0xFA9E,0xFA9D,0x0083,0xFA9C,0xFA9B,0x0085,0x0083,0xFA9A,
+ 0xFA99,0x0083,0xFA98,0xFA97,0x0089,0x0085,0x0083,0xFA96,0xFA95,0x0083,0xFA94,0xFA93,0x0085,0x0083,0xFA92,0xFA91,
+ 0x0083,0xFA90,0xFA8F,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0xFA8E,0xFA8D,0x0083,0xFA8C,0xFA8B,0x0085,0x0083,
+ 0xFA8A,0xFA89,0x0083,0xFA88,0xFA87,0x0089,0x0085,0x0083,0xFA86,0xFA85,0x0083,0xFA84,0xFA83,0x0085,0x0083,0xFA82,
+ 0xFA81,0x0083,0xFA80,0xFA7E,0x0091,0x0089,0x0085,0x0083,0xFA7D,0xFA7C,0x0083,0xFA7B,0xFA7A,0x0085,0x0083,0xFA79,
+ 0xFA78,0x0083,0xFA77,0xFA76,0x0089,0x0085,0x0083,0xFA75,0xFA74,0x0083,0xFA73,0xFA72,0x0085,0x0083,0xFA71,0xFA70,
+ 0x0083,0xFA6F,0xFA6E,0x00A1,0x0091,0x0089,0x0085,0x0083,0xFA6D,0xFA6C,0x0083,0xFA6B,0xFA6A,0x0085,0x0083,0xFA69,
+ 0xFA68,0x0083,0xFA67,0xFA66,0x0089,0x0085,0x0083,0xFA65,0xFA64,0x0083,0xFA63,0xFA62,0x0085,0x0083,0xFA61,0xFA60,
+ 0x0083,0xFA5F,0xFA5E,0x0091,0x0089,0x0085,0x0083,0xFA5D,0xFA5C,0x0083,0xFA5B,0xFA5A,0x0085,0x0083,0xFA59,0xFA58,
+ 0x0083,0xFA57,0xFA56,0x0089,0x0085,0x0083,0xFA55,0xFA54,0x0083,0xFA53,0xFA52,0x0085,0x0083,0xFA51,0xFA50,0x0083,
+ 0xFA4F,0xFA4E,0x0101,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0xFA4D,0xFA4C,0x0083,0xFA4B,0xFA4A,0x0085,0x0083,
+ 0xFA49,0xFA48,0x0083,0xFA47,0xFA46,0x0089,0x0085,0x0083,0xFA45,0xFA44,0x0083,0xFA43,0xFA42,0x0085,0x0083,0xFA41,
+ 0xFA40,0x0083,0xF9A0,0xF99F,0x0091,0x0089,0x0085,0x0083,0xF99E,0xF99D,0x0083,0xF99C,0xF99B,0x0085,0x0083,0xF99A,
+ 0xF999,0x0083,0xF998,0xF997,0x0089,0x0085,0x0083,0xF996,0xF995,0x0083,0xF994,0xF993,0x0085,0x0083,0xF992,0xF991,
+ 0x0083,0xF990,0xF98F,0x00A1,0x0091,0x0089,0x0085,0x0083,0xF98E,0xF98D,0x0083,0xF98C,0xF98B,0x0085,0x0083,0xF98A,
+ 0xF989,0x0083,0xF988,0xF987,0x0089,0x0085,0x0083,0xF986,0xF985,0x0083,0xF984,0xF983,0x0085,0x0083,0xF982,0xF981,
+ 0x0083,0xF980,0xF97E,0x0091,0x0089,0x0085,0x0083,0xF97D,0xF97C,0x0083,0xF97B,0xF97A,0x0085,0x0083,0xF979,0xF978,
+ 0x0083,0xF977,0xF976,0x0089,0x0085,0x0083,0xF975,0xF974,0x0083,0xF973,0xF972,0x0085,0x0083,0xF971,0xF970,0x0083,
+ 0xF96F,0xF96E,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0xF96D,0xF96C,0x0083,0xF96B,0xF96A,0x0085,0x0083,0xF969,
+ 0xF968,0x0083,0xF967,0xF966,0x0089,0x0085,0x0083,0xF965,0xF964,0x0083,0xF963,0xF962,0x0085,0x0083,0xF961,0xF960,
+ 0x0083,0xF95F,0xF95E,0x0091,0x0089,0x0085,0x0083,0xF95D,0xF95C,0x0083,0xF95B,0xF95A,0x0085,0x0083,0xF959,0xF958,
+ 0x0083,0xF957,0xF956,0x0089,0x0085,0x0083,0xF955,0xF954,0x0083,0xF953,0xF952,0x0085,0x0083,0xF951,0xF950,0x0083,
+ 0xF94F,0xF94E,0x00A1,0x0091,0x0089,0x0085,0x0083,0xF94D,0xF94C,0x0083,0xF94B,0xF94A,0x0085,0x0083,0xF949,0xF948,
+ 0x0083,0xF947,0xF946,0x0089,0x0085,0x0083,0xF945,0xF944,0x0083,0xF943,0xF942,0x0085,0x0083,0xF941,0xF940,0x0083,
+ 0xF8A0,0xF89F,0x0091,0x0089,0x0085,0x0083,0xF89E,0xF89D,0x0083,0xF89C,0xF89B,0x0085,0x0083,0xF89A,0xF899,0x0083,
+ 0xF898,0xF897,0x0089,0x0085,0x0083,0xF896,0xF895,0x0083,0xF894,0xF893,0x0085,0x0083,0xF892,0xF891,0x0083,0xF890,
+ 0xF88F,0x0181,0x0101,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0xF88E,0xF88D,0x0083,0xF88C,0xF88B,0x0085,0x0083,
+ 0xF88A,0xF889,0x0083,0xF888,0xF887,0x0089,0x0085,0x0083,0xF886,0xF885,0x0083,0xF884,0xF883,0x0085,0x0083,0xF882,
+ 0xF881,0x0083,0xF880,0xF87E,0x0091,0x0089,0x0085,0x0083,0xF87D,0xF87C,0x0083,0xF87B,0xF87A,0x0085,0x0083,0xF879,
+ 0xF878,0x0083,0xF877,0xF876,0x0089,0x0085,0x0083,0xF875,0xF874,0x0083,0xF873,0xF872,0x0085,0x0083,0xF871,0xF870,
+ 0x0083,0xF86F,0xF86E,0x00A1,0x0091,0x0089,0x0085,0x0083,0xF86D,0xF86C,0x0083,0xF86B,0xF86A,0x0085,0x0083,0xF869,
+ 0xF868,0x0083,0xF867,0xF866,0x0089,0x0085,0x0083,0xF865,0xF864,0x0083,0xF863,0xF862,0x0085,0x0083,0xF861,0xF860,
+ 0x0083,0xF85F,0xF85E,0x0091,0x0089,0x0085,0x0083,0xF85D,0xF85C,0x0083,0xF85B,0xF85A,0x0085,0x0083,0xF859,0xF858,
+ 0x0083,0xF857,0xF856,0x0089,0x0085,0x0083,0xF855,0xF854,0x0083,0xF853,0xF852,0x0085,0x0083,0xF851,0xF850,0x0083,
+ 0xF84F,0xF84E,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0xF84D,0xF84C,0x0083,0xF84B,0xF84A,0x0085,0x0083,0xF849,
+ 0xF848,0x0083,0xF847,0xF846,0x0089,0x0085,0x0083,0xF845,0xF844,0x0083,0xF843,0xF842,0x0085,0x0083,0xF841,0xF840,
+ 0x0083,0xF7AF,0xF7A0,0x0091,0x0089,0x0085,0x0083,0xF79F,0xF7AE,0x0083,0xC1DB,0xF7AD,0x0085,0x0083,0xF7AC,0xF79E,
+ 0x0083,0xF79D,0xF7AB,0x0089,0x0085,0x0083,0xF7AA,0xF7A9,0x0083,0xB1EE,0xF7A8,0x0085,0x0083,0xF7A7,0xF7A6,0x0083,
+ 0xF79C,0xF79B,0x00A1,0x0091,0x0089,0x0085,0x0083,0xF7A5,0xF7A4,0x0083,0xF7A3,0xF7A2,0x0085,0x0083,0xF7A1,0xF6FE,
+ 0x0083,0xF6FD,0xF79A,0x0089,0x0085,0x0083,0xF799,0xF6FC,0x0083,0xF6FB,0xF6FA,0x0085,0x0083,0xF6F9,0xC8FA,0x0083,
+ 0xF798,0xF797,0x0091,0x0089,0x0085,0x0083,0xF796,0xF795,0x0083,0xF794,0xF6F8,0x0085,0x0083,0xF6F7,0xF6F6,0x0083,
+ 0xF6F5,0xF793,0x0089,0x0085,0x0083,0xBEA8,0xF6F4,0x0083,0xF6F3,0xF6F2,0x0085,0x0083,0xF6F1,0xF6F0,0x0083,0xF6EF,
+ 0xF6EE,0x0101,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0xF6ED,0xF792,0x0083,0xF6EC,0xF6EB,0x0085,0x0083,0xF791,
+ 0xF6EA,0x0083,0xF790,0xF6E9,0x0089,0x0085,0x0083,0xF6E8,0xF6E7,0x0083,0xF6E6,0xF6E5,0x0085,0x0083,0xC0F0,0xF6E4,
+ 0x0083,0xF6E3,0xF6E2,0x0091,0x0089,0x0085,0x0083,0xF6E1,0xF6E0,0x0083,0xF6DF,0xF78F,0x0085,0x0083,0xCFCA,0xF6DE,
+ 0x0083,0xF6DD,0xF78E,0x0089,0x0085,0x0083,0xF78D,0xF78C,0x0083,0xF78B,0xF6DC,0x0085,0x0083,0xF6DB,0xF78A,0x0083,
+ 0xF6DA,0xF6D9,0x00A1,0x0091,0x0089,0x0085,0x0083,0xF6D8,0xF789,0x0083,0xF6D7,0xB1AB,0x0085,0x0083,0xF788,0xF6D6,
+ 0x0083,0xF787,0xF786,0x0089,0x0085,0x0083,0xF6D4,0xF6D3,0x0083,0xF6D2,0xF6D1,0x0085,0x0083,0xF785,0xF784,0x0083,
+ 0xF6D0,0xC2B3,0x0091,0x0089,0x0085,0x0083,0xF783,0xF6CF,0x0083,0xF782,0xF781,0x0085,0x0083,0xD3E3,0xF780,0x0083,
+ 0xF77E,0xF77D,0x0089,0x0085,0x0083,0xF77C,0xF77B,0x0083,0xF77A,0xF779,0x0085,0x0083,0xF778,0xF777,0x0083,0xF776,
+ 0xF775,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0xF774,0xF773,0x0083,0xF772,0xF771,0x0085,0x0083,0xF770,0xF76F,
+ 0x0083,0xF76E,0xF76D,0x0089,0x0085,0x0083,0xF76C,0xF76B,0x0083,0xF76A,0xF769,0x0085,0x0083,0xF768,0xF767,0x0083,
+ 0xF766,0xF765,0x0091,0x0089,0x0085,0x0083,0xF764,0xF763,0x0083,0xF762,0xF761,0x0085,0x0083,0xF760,0xF75F,0x0083,
+ 0xF75E,0xF75D,0x0089,0x0085,0x0083,0xF75C,0xF75B,0x0083,0xF75A,0xF759,0x0085,0x0083,0xF758,0xF757,0x0083,0xF756,
+ 0xF755,0x00A1,0x0091,0x0089,0x0085,0x0083,0xF754,0xF753,0x0083,0xF752,0xF751,0x0085,0x0083,0xF750,0xF74F,0x0083,
+ 0xF74E,0xF74D,0x0089,0x0085,0x0083,0xF74C,0xF74B,0x0083,0xF74A,0xF749,0x0085,0x0083,0xF748,0xF747,0x0083,0xF746,
+ 0xF745,0x0091,0x0089,0x0085,0x0083,0xF744,0xF743,0x0083,0xF742,0xF741,0x0085,0x0083,0xF740,0xF6A0,0x0083,0xF69F,
+ 0xF69E,0x0089,0x0085,0x0083,0xF69D,0xF69C,0x0083,0xF69B,0xF69A,0x0085,0x0083,0xF699,0xF698,0x0083,0xF697,0xF696,
+ 0x047D,0x027F,0x017F,0x00FF,0x00BF,0x009F,0x008F,0x0087,0x0083,0xF695,0x0083,0xF694,0xF693,0x0085,0x0083,0xF692,
+ 0xF691,0x0083,0xF690,0xF68F,0x0089,0x0085,0x0083,0xF68E,0xF68D,0x0083,0xF68C,0xF68B,0x0085,0x0083,0xF68A,0xF689,
+ 0x0083,0xF688,0xF687,0x0091,0x0089,0x0085,0x0083,0xF686,0xF685,0x0083,0xF684,0xF683,0x0085,0x0083,0xF682,0xF681,
+ 0x0083,0xF680,0xF67E,0x0089,0x0085,0x0083,0xF67D,0xF67C,0x0083,0xF67B,0xF67A,0x0085,0x0083,0xF679,0xF678,0x0083,
+ 0xF677,0xF676,0x00A1,0x0091,0x0089,0x0085,0x0083,0xF675,0xF674,0x0083,0xF673,0xF672,0x0085,0x0083,0xF671,0xF670,
+ 0x0083,0xF66F,0xF66E,0x0089,0x0085,0x0083,0xF66D,0xF66C,0x0083,0xF66B,0xF66A,0x0085,0x0083,0xF669,0xF668,0x0083,
+ 0xF667,0xF666,0x0091,0x0089,0x0085,0x0083,0xF665,0xF664,0x0083,0xF663,0xF662,0x0085,0x0083,0xF661,0xF660,0x0083,
+ 0xF65F,0xF65E,0x0089,0x0085,0x0083,0xF65D,0xF65C,0x0083,0xF65B,0xF65A,0x0085,0x0083,0xF659,0xF658,0x0083,0xF657,
+ 0xF656,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0xF655,0xF654,0x0083,0xF653,0xF652,0x0085,0x0083,0xF651,0xF650,
+ 0x0083,0xF64F,0xF64E,0x0089,0x0085,0x0083,0xF64D,0xF64C,0x0083,0xF64B,0xF64A,0x0085,0x0083,0xF649,0xF648,0x0083,
+ 0xF647,0xF646,0x0091,0x0089,0x0085,0x0083,0xF645,0xF644,0x0083,0xF643,0xF642,0x0085,0x0083,0xF641,0xF640,0x0083,
+ 0xF5A0,0xF59F,0x0089,0x0085,0x0083,0xF59E,0xF59D,0x0083,0xF59C,0xF59B,0x0085,0x0083,0xF59A,0xF599,0x0083,0xF598,
+ 0xF597,0x00A1,0x0091,0x0089,0x0085,0x0083,0xF596,0xF595,0x0083,0xF594,0xF593,0x0085,0x0083,0xF592,0xF591,0x0083,
+ 0xF590,0xF58F,0x0089,0x0085,0x0083,0xF58E,0xF58D,0x0083,0xF58C,0xF58B,0x0085,0x0083,0xF58A,0xF589,0x0083,0xF588,
+ 0xF587,0x0091,0x0089,0x0085,0x0083,0xF586,0xF585,0x0083,0xF584,0xF583,0x0085,0x0083,0xF582,0xF581,0x0083,0xF580,
+ 0xF57E,0x0089,0x0085,0x0083,0xF57D,0xF57C,0x0083,0xF57B,0xF57A,0x0085,0x0083,0xF579,0xF578,0x0083,0xF577,0xF576,
+ 0x0101,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0xF575,0xF574,0x0083,0xF573,0xF572,0x0085,0x0083,0xF571,0xF570,
+ 0x0083,0xF56F,0xF56E,0x0089,0x0085,0x0083,0xF56D,0xF56C,0x0083,0xF56B,0xF56A,0x0085,0x0083,0xF569,0xF568,0x0083,
+ 0xF567,0xF566,0x0091,0x0089,0x0085,0x0083,0xF565,0xF564,0x0083,0xF563,0xF562,0x0085,0x0083,0xF561,0xF560,0x0083,
+ 0xF55F,0xF55E,0x0089,0x0085,0x0083,0xF55D,0xF55C,0x0083,0xF55B,0xF55A,0x0085,0x0083,0xF559,0xF558,0x0083,0xF557,
+ 0xF556,0x00A1,0x0091,0x0089,0x0085,0x0083,0xF555,0xF554,0x0083,0xF553,0xF552,0x0085,0x0083,0xF551,0xF550,0x0083,
+ 0xF54F,0xF54E,0x0089,0x0085,0x0083,0xF54D,0xF54C,0x0083,0xF54B,0xF54A,0x0085,0x0083,0xF549,0xF548,0x0083,0xF547,
+ 0xF546,0x0091,0x0089,0x0085,0x0083,0xF545,0xF544,0x0083,0xF543,0xF542,0x0085,0x0083,0xF541,0xF540,0x0083,0xF4A0,
+ 0xF49F,0x0089,0x0085,0x0083,0xF49E,0xF49D,0x0083,0xF49C,0xF49B,0x0085,0x0083,0xF49A,0xF499,0x0083,0xF498,0xF497,
+ 0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0xF496,0xF495,0x0083,0xF494,0xF493,0x0085,0x0083,0xF492,0xF491,0x0083,
+ 0xF490,0xF48F,0x0089,0x0085,0x0083,0xF48E,0xF48D,0x0083,0xF48C,0xF48B,0x0085,0x0083,0xF48A,0xF489,0x0083,0xF488,
+ 0xF487,0x0091,0x0089,0x0085,0x0083,0xF486,0xF485,0x0083,0xF484,0xF483,0x0085,0x0083,0xF482,0xF481,0x0083,0xF480,
+ 0xF47E,0x0089,0x0085,0x0083,0xF47D,0xF47C,0x0083,0xF47B,0xF47A,0x0085,0x0083,0xF479,0xC4A7,0x0083,0xF478,0xF477,
+ 0x00A1,0x0091,0x0089,0x0085,0x0083,0xF7CE,0xF476,0x0083,0xCEBA,0xF475,0x0085,0x0083,0xF7CD,0xF474,0x0083,0xF473,
+ 0xF472,0x0089,0x0085,0x0083,0xF7CB,0xF7CC,0x0083,0xF7CA,0xF471,0x0085,0x0083,0xF7C8,0xC6C7,0x0083,0xF7C9,0xBBEA,
+ 0x0091,0x0089,0x0085,0x0083,0xBFFD,0xF470,0x0083,0xF46F,0xF46E,0x0085,0x0083,0xF46D,0xB9ED,0x0083,0xE5F7,0xF46C,
+ 0x0089,0x0085,0x0083,0xF46B,0xF46A,0x0083,0xF469,0xF468,0x0085,0x0083,0xF467,0xF466,0x0083,0xF465,0xD8AA,0x017F,
+ 0x00FF,0x00BF,0x009F,0x008F,0x0087,0x0083,0xF464,0x0083,0xF463,0xDBCB,0x0085,0x0083,0xF462,0xF461,0x0083,0xF460,
+ 0xF45F,0x0089,0x0085,0x0083,0xF45E,0xF45D,0x0083,0xF45C,0xF45B,0x0085,0x0083,0xF45A,0xF459,0x0083,0xF458,0xF7E0,
+ 0x0091,0x0089,0x0085,0x0083,0xF457,0xF456,0x0083,0xF455,0xF7DF,0x0085,0x0083,0xF454,0xF453,0x0083,0xF452,0xF451,
+ 0x0089,0x0085,0x0083,0xF450,0xF44F,0x0083,0xF44E,0xF44D,0x0085,0x0083,0xF44C,0xF44B,0x0083,0xF44A,0xF7DE,0x00A1,
+ 0x0091,0x0089,0x0085,0x0083,0xF449,0xF448,0x0083,0xF447,0xF7DD,0x0085,0x0083,0xF446,0xF445,0x0083,0xF444,0xF443,
+ 0x0089,0x0085,0x0083,0xF442,0xF441,0x0083,0xF7DC,0xF440,0x0085,0x0083,0xF3A0,0xF39F,0x0083,0xF39E,0xD7D7,0x0091,
+ 0x0089,0x0085,0x0083,0xF39D,0xF39C,0x0083,0xF39B,0xF39A,0x0085,0x0083,0xF399,0xF398,0x0083,0xF397,0xF7D9,0x0089,
+ 0x0085,0x0083,0xF396,0xF7DB,0x0083,0xF395,0xF394,0x0085,0x0083,0xF393,0xF392,0x0083,0xF391,0xF390,0x00C1,0x00A1,
+ 0x0091,0x0089,0x0085,0x0083,0xF38F,0xF38E,0x0083,0xF38D,0xF7D7,0x0085,0x0083,0xF38C,0xF7DA,0x0083,0xF38B,0xF7D8,
+ 0x0089,0x0085,0x0083,0xF38A,0xF389,0x0083,0xF388,0xF387,0x0085,0x0083,0xF7D6,0xF386,0x0083,0xF385,0xF384,0x0091,
+ 0x0089,0x0085,0x0083,0xF383,0xF7D5,0x0083,0xF382,0xF7D4,0x0085,0x0083,0xF381,0xF380,0x0083,0xF37E,0xF37D,0x0089,
+ 0x0085,0x0083,0xF37C,0xF37B,0x0083,0xB8DF,0xF37A,0x0085,0x0083,0xF379,0xF378,0x0083,0xF377,0xCBE8,0x00A1,0x0091,
+ 0x0089,0x0085,0x0083,0xF376,0xF7C7,0x0083,0xF375,0xF374,0x0085,0x0083,0xF373,0xF372,0x0083,0xF7C6,0xF7C5,0x0089,
+ 0x0085,0x0083,0xF371,0xF370,0x0083,0xF36F,0xF36E,0x0085,0x0083,0xF36D,0xF7C3,0x0083,0xF36C,0xF36B,0x0091,0x0089,
+ 0x0085,0x0083,0xF7C4,0xF7C1,0x0083,0xF7C2,0xF36A,0x0085,0x0083,0xF369,0xF368,0x0083,0xF7C0,0xF367,0x0089,0x0085,
+ 0x0083,0xF7BF,0xF366,0x0083,0xBAA1,0xF7BC,0x0085,0x0083,0xF7BE,0xF365,0x0083,0xF364,0xF363,0x0101,0x00C1,0x00A1,
+ 0x0091,0x0089,0x0085,0x0083,0xF362,0xF7BA,0x0083,0xF7BB,0xF361,0x0085,0x0083,0xF360,0xF35F,0x0083,0xF35E,0xF35D,
+ 0x0089,0x0085,0x0083,0xF35C,0xF35B,0x0083,0xB9C7,0xE6F8,0x0085,0x0083,0xF35A,0xE6F7,0x0083,0xD6E8,0xE6F6,0x0091,
+ 0x0089,0x0085,0x0083,0xE6F5,0xC2E2,0x0083,0xE6F4,0xE6F3,0x0085,0x0083,0xE5B9,0xE6F2,0x0083,0xE6F1,0xE6F0,0x0089,
+ 0x0085,0x0083,0xC9A7,0xF359,0x0083,0xE6EF,0xC6AD,0x0085,0x0083,0xE6EE,0xF358,0x0083,0xF357,0xE6ED,0x00A1,0x0091,
+ 0x0089,0x0085,0x0083,0xE6EC,0xC6EF,0x0083,0xE6EB,0xBFA5,0x0085,0x0083,0xF356,0xF355,0x0083,0xD1E9,0xB3D2,0x0089,
+ 0x0085,0x0083,0xE6EA,0xF354,0x0083,0xE6E9,0xBAA7,0x0085,0x0083,0xC2E6,0xE6E8,0x0083,0xBDBE,0xF353,0x0091,0x0089,
+ 0x0085,0x0083,0xC2EE,0xE6E7,0x0083,0xE6E6,0xE6E4,0x0085,0x0083,0xBCDD,0xE6E5,0x0083,0xCDD5,0xD7A4,0x0089,0x0085,
+ 0x0083,0xE6E3,0xBED4,0x0083,0xE6E2,0xE6E1,0x0085,0x0083,0xCABB,0xE6E0,0x0083,0xC2BF,0xB2B5,0x00C1,0x00A1,0x0091,
+ 0x0089,0x0085,0x0083,0xF352,0xC7FD,0x0083,0xB3DB,0xD1B1,0x0085,0x0083,0xCDD4,0xD4A6,0x0083,0xC2ED,0xF351,0x0089,
+ 0x0085,0x0083,0xF350,0xF34F,0x0083,0xF34E,0xF34D,0x0085,0x0083,0xF34C,0xF34B,0x0083,0xF34A,0xF349,0x0091,0x0089,
+ 0x0085,0x0083,0xF348,0xF347,0x0083,0xF346,0xF345,0x0085,0x0083,0xF344,0xF343,0x0083,0xF342,0xF341,0x0089,0x0085,
+ 0x0083,0xF340,0xF2A0,0x0083,0xF29F,0xF29E,0x0085,0x0083,0xF29D,0xF29C,0x0083,0xF29B,0xF29A,0x00A1,0x0091,0x0089,
+ 0x0085,0x0083,0xF299,0xF298,0x0083,0xF297,0xF296,0x0085,0x0083,0xF295,0xF294,0x0083,0xF293,0xF292,0x0089,0x0085,
+ 0x0083,0xF291,0xF290,0x0083,0xF28F,0xF28E,0x0085,0x0083,0xF28D,0xF28C,0x0083,0xF28B,0xF28A,0x0091,0x0089,0x0085,
+ 0x0083,0xF289,0xF288,0x0083,0xF287,0xF286,0x0085,0x0083,0xF285,0xF284,0x0083,0xF283,0xF282,0x0089,0x0085,0x0083,
+ 0xF281,0xF280,0x0083,0xF27E,0xF27D,0x0085,0x0083,0xF27C,0xF27B,0x0083,0xF27A,0xF279,0x027F,0x017F,0x00FF,0x00BF,
+ 0x009F,0x008F,0x0087,0x0083,0xF278,0x0083,0xF277,0xF276,0x0085,0x0083,0xF275,0xF274,0x0083,0xF273,0xF272,0x0089,
+ 0x0085,0x0083,0xF271,0xF270,0x0083,0xF26F,0xF26E,0x0085,0x0083,0xF26D,0xF26C,0x0083,0xF26B,0xF26A,0x0091,0x0089,
+ 0x0085,0x0083,0xF269,0xF268,0x0083,0xF267,0xF266,0x0085,0x0083,0xF265,0xF264,0x0083,0xF263,0xF262,0x0089,0x0085,
+ 0x0083,0xF261,0xF260,0x0083,0xF25F,0xF25E,0x0085,0x0083,0xF25D,0xF25C,0x0083,0xF25B,0xF25A,0x00A1,0x0091,0x0089,
+ 0x0085,0x0083,0xF259,0xF258,0x0083,0xF257,0xF256,0x0085,0x0083,0xF255,0xF254,0x0083,0xF253,0xF252,0x0089,0x0085,
+ 0x0083,0xF251,0xF250,0x0083,0xF24F,0xF24E,0x0085,0x0083,0xF24D,0xF24C,0x0083,0xF24B,0xF24A,0x0091,0x0089,0x0085,
+ 0x0083,0xF249,0xF248,0x0083,0xF247,0xF246,0x0085,0x0083,0xF245,0xF244,0x0083,0xF243,0xF242,0x0089,0x0085,0x0083,
+ 0xF241,0xF240,0x0083,0xF1A0,0xF19F,0x0085,0x0083,0xF19E,0xF19D,0x0083,0xF19C,0xF19B,0x00C1,0x00A1,0x0091,0x0089,
+ 0x0085,0x0083,0xF19A,0xF199,0x0083,0xF198,0xF197,0x0085,0x0083,0xF196,0xF195,0x0083,0xF194,0xF193,0x0089,0x0085,
+ 0x0083,0xF192,0xF191,0x0083,0xF190,0xF18F,0x0085,0x0083,0xF18E,0xF18D,0x0083,0xF18C,0xF18B,0x0091,0x0089,0x0085,
+ 0x0083,0xF18A,0xF189,0x0083,0xF188,0xF187,0x0085,0x0083,0xF186,0xF185,0x0083,0xF184,0xF183,0x0089,0x0085,0x0083,
+ 0xF182,0xF181,0x0083,0xF180,0xF17E,0x0085,0x0083,0xF17D,0xF17C,0x0083,0xF17B,0xF17A,0x00A1,0x0091,0x0089,0x0085,
+ 0x0083,0xF179,0xF178,0x0083,0xF177,0xF176,0x0085,0x0083,0xF175,0xF174,0x0083,0xF173,0xF172,0x0089,0x0085,0x0083,
+ 0xF171,0xF170,0x0083,0xF16F,0xF16E,0x0085,0x0083,0xF16D,0xF16C,0x0083,0xF16B,0xF16A,0x0091,0x0089,0x0085,0x0083,
+ 0xF169,0xF168,0x0083,0xF167,0xF166,0x0085,0x0083,0xF165,0xF164,0x0083,0xF163,0xF162,0x0089,0x0085,0x0083,0xF161,
+ 0xF160,0x0083,0xF15F,0xF15E,0x0085,0x0083,0xF15D,0xF15C,0x0083,0xF15B,0xF15A,0x0101,0x00C1,0x00A1,0x0091,0x0089,
+ 0x0085,0x0083,0xF159,0xF158,0x0083,0xF157,0xF156,0x0085,0x0083,0xF155,0xF154,0x0083,0xF153,0xF152,0x0089,0x0085,
+ 0x0083,0xF151,0xF150,0x0083,0xF14F,0xDCB0,0x0085,0x0083,0xF14E,0xF14D,0x0083,0xF0A5,0xF14C,0x0091,0x0089,0x0085,
+ 0x0083,0xF14B,0xF14A,0x0083,0xF149,0xF148,0x0085,0x0083,0xF147,0xF146,0x0083,0xF145,0xF144,0x0089,0x0085,0x0083,
+ 0xF143,0xF142,0x0083,0xCFE3,0xD9E5,0x0085,0x0083,0xD8B8,0xCAD7,0x0083,0xE2CE,0xE2CD,0x00A1,0x0091,0x0089,0x0085,
+ 0x0083,0xE2CC,0xC2F8,0x0083,0xE2CB,0xE2CA,0x0085,0x0083,0xC1F3,0xF141,0x0083,0xE2C9,0xF140,0x0089,0x0085,0x0083,
+ 0xB2F6,0xE2C8,0x0083,0xF0A0,0xC0A1,0x0085,0x0083,0xE2C7,0xB9DD,0x0083,0xCFDA,0xE2C6,0x0091,0x0089,0x0085,0x0083,
+ 0xF09F,0xF09E,0x0083,0xC4D9,0xE2C5,0x0085,0x0083,0xB6F6,0xF09D,0x0083,0xE2C4,0xB1FD,0x0089,0x0085,0x0083,0xF09C,
+ 0xBDC8,0x0083,0xF09B,0xF09A,0x0085,0x0083,0xE2C3,0xC8C4,0x0083,0xB6FC,0xE2C2,0x00C1,0x00A1,0x0091,0x0089,0x0085,
+ 0x0083,0xF099,0xCBC7,0x0083,0xB1A5,0xCACE,0x0085,0x0083,0xBDA4,0xD2FB,0x0083,0xB7B9,0xE2C1,0x0089,0x0085,0x0083,
+ 0xE2C0,0xE2BF,0x0083,0xE2BE,0xE2BD,0x0085,0x0083,0xE2BC,0xF098,0x0083,0xBCA2,0xF097,0x0091,0x0089,0x0085,0x0083,
+ 0xE2BB,0xF096,0x0083,0xF095,0xF094,0x0085,0x0083,0xF093,0xF092,0x0083,0xF091,0xF090,0x0089,0x0085,0x0083,0xF08F,
+ 0xF08E,0x0083,0xF08D,0xF08C,0x0085,0x0083,0xF08B,0xF08A,0x0083,0xF7D2,0xF7D3,0x00A1,0x0091,0x0089,0x0085,0x0083,
+ 0xF089,0xF088,0x0083,0xF087,0xF086,0x0085,0x0083,0xF085,0xF084,0x0083,0xF083,0xF082,0x0089,0x0085,0x0083,0xF081,
+ 0xF080,0x0083,0xF07E,0xF07D,0x0085,0x0083,0xF07C,0xF07B,0x0083,0xF07A,0xF079,0x0091,0x0089,0x0085,0x0083,0xF078,
+ 0xF077,0x0083,0xF076,0xF075,0x0085,0x0083,0xF074,0xF073,0x0083,0xF072,0xF071,0x0089,0x0085,0x0083,0xF070,0xF06F,
+ 0x0083,0xF06E,0xF06D,0x0085,0x0083,0xF06C,0xF06B,0x0083,0xF06A,0xF069,0x0181,0x0101,0x00C1,0x00A1,0x0091,0x0089,
+ 0x0085,0x0083,0xF068,0xF067,0x0083,0xF066,0xF065,0x0085,0x0083,0xF064,0xF7D1,0x0083,0xF063,0xF062,0x0089,0x0085,
+ 0x0083,0xF061,0xF060,0x0083,0xF05F,0xF05E,0x0085,0x0083,0xF05D,0xF05C,0x0083,0xF05B,0xF05A,0x0091,0x0089,0x0085,
+ 0x0083,0xF059,0xF058,0x0083,0xF057,0xF056,0x0085,0x0083,0xF055,0xF054,0x0083,0xF053,0xF052,0x0089,0x0085,0x0083,
+ 0xF051,0xF050,0x0083,0xF04F,0xF04E,0x0085,0x0083,0xF04D,0xF04C,0x0083,0xF04B,0xF04A,0x00A1,0x0091,0x0089,0x0085,
+ 0x0083,0xF049,0xF048,0x0083,0xF047,0xB2CD,0x0085,0x0083,0xF046,0xF045,0x0083,0xF7D0,0xF044,0x0089,0x0085,0x0083,
+ 0xF043,0xF042,0x0083,0xF041,0xF040,0x0085,0x0083,0xEFA0,0xEF9F,0x0083,0xEF9E,0xEF9D,0x0091,0x0089,0x0085,0x0083,
+ 0xEF9C,0xEF9B,0x0083,0xEF9A,0xEF99,0x0085,0x0083,0xEF98,0xEF97,0x0083,0xEF96,0xEF95,0x0089,0x0085,0x0083,0xEF94,
+ 0xEF93,0x0083,0xEF92,0xEF91,0x0085,0x0083,0xEF90,0xEF8F,0x0083,0xEF8E,0xEF8D,0x00C1,0x00A1,0x0091,0x0089,0x0085,
+ 0x0083,0xEF8C,0xEF8B,0x0083,0xEF8A,0xEF89,0x0085,0x0083,0xEF88,0xEF87,0x0083,0xEF86,0xEF85,0x0089,0x0085,0x0083,
+ 0xEF84,0xEF83,0x0083,0xEF82,0xF7CF,0x0085,0x0083,0xE2B8,0xEF81,0x0083,0xEF80,0xEF7E,0x0091,0x0089,0x0085,0x0083,
+ 0xEF7D,0xEF7C,0x0083,0xEF7B,0xEF7A,0x0085,0x0083,0xCAB3,0xB7C9,0x0083,0xEF79,0xEF78,0x0089,0x0085,0x0083,0xEF77,
+ 0xECAE,0x0083,0xECAD,0xC6AE,0x0085,0x0083,0xEF76,0xEF75,0x0083,0xECAC,0xEF74,0x00A1,0x0091,0x0089,0x0085,0x0083,
+ 0xECAB,0xECAA,0x0083,0xECA9,0xEF73,0x0085,0x0083,0xEF72,0xB7E7,0x0083,0xEF71,0xEF70,0x0089,0x0085,0x0083,0xEF6F,
+ 0xEF6E,0x0083,0xEF6D,0xEF6C,0x0085,0x0083,0xEF6B,0xEF6A,0x0083,0xEF69,0xEF68,0x0091,0x0089,0x0085,0x0083,0xEF67,
+ 0xEF66,0x0083,0xEF65,0xEF64,0x0085,0x0083,0xEF63,0xEF62,0x0083,0xEF61,0xEF60,0x0089,0x0085,0x0083,0xEF5F,0xEF5E,
+ 0x0083,0xEF5D,0xEF5C,0x0085,0x0083,0xEF5B,0xEF5A,0x0083,0xEF59,0xEF58,0x0101,0x00C1,0x00A1,0x0091,0x0089,0x0085,
+ 0x0083,0xEF57,0xEF56,0x0083,0xEF55,0xEF54,0x0085,0x0083,0xEF53,0xEF52,0x0083,0xEF51,0xEF50,0x0089,0x0085,0x0083,
+ 0xEF4F,0xEF4E,0x0083,0xEF4D,0xEF4C,0x0085,0x0083,0xC8A7,0xF2AD,0x0083,0xF2AC,0xB2FC,0x0091,0x0089,0x0085,0x0083,
+ 0xEF4B,0xF2AB,0x0083,0xF2AA,0xB5DF,0x0085,0x0083,0xF2A9,0xF2A8,0x0083,0xB6EE,0xD1D5,0x0089,0x0085,0x0083,0xF2A7,
+ 0xF2A6,0x0083,0xEF4A,0xCCE2,0x0085,0x0083,0xBFC5,0xD3B1,0x0083,0xEF49,0xF2A5,0x00A1,0x0091,0x0089,0x0085,0x0083,
+ 0xCDC7,0xEF48,0x0083,0xC6B5,0xD2C3,0x0085,0x0083,0xF2A4,0xEF47,0x0083,0xF2A3,0xF2A2,0x0089,0x0085,0x0083,0xEF46,
+ 0xBCD5,0x0083,0xF2A1,0xBEB1,0x0085,0x0083,0xC6C4,0xC1EC,0x0083,0xC2AD,0xD4A4,0x0091,0x0089,0x0085,0x0083,0xF1FE,
+ 0xCBCC,0x0083,0xB0E4,0xF1FD,0x0085,0x0083,0xB6D9,0xB9CB,0x0083,0xCDE7,0xE7EF,0x0089,0x0085,0x0083,0xD0EB,0xCBB3,
+ 0x0083,0xCFEE,0xF1FC,0x0085,0x0083,0xC7EA,0xB6A5,0x0083,0xD2B3,0xEF45,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,
+ 0xEF44,0xEF43,0x0083,0xEF42,0xEF41,0x0085,0x0083,0xEF40,0xEEA0,0x0083,0xEE9F,0xEE9E,0x0089,0x0085,0x0083,0xEE9D,
+ 0xEE9C,0x0083,0xEE9B,0xEE9A,0x0085,0x0083,0xEE99,0xEE98,0x0083,0xEE97,0xEE96,0x0091,0x0089,0x0085,0x0083,0xEE95,
+ 0xEE94,0x0083,0xEE93,0xEE92,0x0085,0x0083,0xEE91,0xEE90,0x0083,0xEE8F,0xEE8E,0x0089,0x0085,0x0083,0xEE8D,0xEE8C,
+ 0x0083,0xEE8B,0xEE8A,0x0085,0x0083,0xEE89,0xEE88,0x0083,0xEE87,0xEE86,0x00A1,0x0091,0x0089,0x0085,0x0083,0xEE85,
+ 0xEE84,0x0083,0xEE83,0xEE82,0x0085,0x0083,0xEE81,0xEE80,0x0083,0xEE7E,0xEE7D,0x0089,0x0085,0x0083,0xEE7C,0xEE7B,
+ 0x0083,0xEE7A,0xEE79,0x0085,0x0083,0xEE78,0xEE77,0x0083,0xEE76,0xEE75,0x0091,0x0089,0x0085,0x0083,0xEE74,0xEE73,
+ 0x0083,0xEE72,0xEE71,0x0085,0x0083,0xEE70,0xEE6F,0x0083,0xEE6E,0xEE6D,0x0089,0x0085,0x0083,0xEE6C,0xEE6B,0x0083,
+ 0xEE6A,0xEE69,0x0085,0x0083,0xEE68,0xEE67,0x0083,0xEE66,0xEE65,0x087B,0x047D,0x027F,0x017F,0x00FF,0x00BF,0x009F,
+ 0x008F,0x0087,0x0083,0xEE64,0x0083,0xEE63,0xEE62,0x0085,0x0083,0xEE61,0xEE60,0x0083,0xEE5F,0xEE5E,0x0089,0x0085,
+ 0x0083,0xEE5D,0xEE5C,0x0083,0xEE5B,0xEE5A,0x0085,0x0083,0xEE59,0xEE58,0x0083,0xEE57,0xEE56,0x0091,0x0089,0x0085,
+ 0x0083,0xEE55,0xEE54,0x0083,0xEE53,0xEE52,0x0085,0x0083,0xEE51,0xEE50,0x0083,0xEE4F,0xEE4E,0x0089,0x0085,0x0083,
+ 0xEE4D,0xEE4C,0x0083,0xEE4B,0xEE4A,0x0085,0x0083,0xEE49,0xEE48,0x0083,0xEE47,0xEE46,0x00A1,0x0091,0x0089,0x0085,
+ 0x0083,0xEE45,0xEE44,0x0083,0xEE43,0xEE42,0x0085,0x0083,0xEE41,0xEE40,0x0083,0xEDA0,0xED9F,0x0089,0x0085,0x0083,
+ 0xED9E,0xED9D,0x0083,0xED9C,0xED9B,0x0085,0x0083,0xED9A,0xED99,0x0083,0xED98,0xED97,0x0091,0x0089,0x0085,0x0083,
+ 0xED96,0xED95,0x0083,0xED94,0xED93,0x0085,0x0083,0xED92,0xED91,0x0083,0xED90,0xED8F,0x0089,0x0085,0x0083,0xED8E,
+ 0xED8D,0x0083,0xED8C,0xED8B,0x0085,0x0083,0xED8A,0xED89,0x0083,0xC9D8,0xD4CF,0x00C1,0x00A1,0x0091,0x0089,0x0085,
+ 0x0083,0xED88,0xD2F4,0x0083,0xED87,0xED86,0x0085,0x0083,0xED85,0xED84,0x0083,0xED83,0xBEC2,0x0089,0x0085,0x0083,
+ 0xE8BA,0xE8B9,0x0083,0xE8B8,0xBAAB,0x0085,0x0083,0xED82,0xC8CD,0x0083,0xCEA4,0xED81,0x0091,0x0089,0x0085,0x0083,
+ 0xED80,0xED7E,0x0083,0xED7D,0xED7C,0x0085,0x0083,0xED7B,0xED7A,0x0083,0xED79,0xED78,0x0089,0x0085,0x0083,0xED77,
+ 0xED76,0x0083,0xED75,0xED74,0x0085,0x0083,0xED73,0xED72,0x0083,0xED71,0xED70,0x00A1,0x0091,0x0089,0x0085,0x0083,
+ 0xED6F,0xED6E,0x0083,0xED6D,0xED6C,0x0085,0x0083,0xED6B,0xED6A,0x0083,0xED69,0xED68,0x0089,0x0085,0x0083,0xED67,
+ 0xED66,0x0083,0xED65,0xED64,0x0085,0x0083,0xED63,0xED62,0x0083,0xED61,0xED60,0x0091,0x0089,0x0085,0x0083,0xED5F,
+ 0xED5E,0x0083,0xED5D,0xED5C,0x0085,0x0083,0xED5B,0xED5A,0x0083,0xED59,0xED58,0x0089,0x0085,0x0083,0xED57,0xED56,
+ 0x0083,0xED55,0xED54,0x0085,0x0083,0xED53,0xED52,0x0083,0xED51,0xED50,0x0101,0x00C1,0x00A1,0x0091,0x0089,0x0085,
+ 0x0083,0xF7B9,0xED4F,0x0083,0xF7B8,0xED4E,0x0085,0x0083,0xED4D,0xF7B5,0x0083,0xED4C,0xB1DE,0x0089,0x0085,0x0083,
+ 0xED4B,0xF7B6,0x0083,0xED4A,0xED49,0x0085,0x0083,0xED48,0xED47,0x0083,0xED46,0xED45,0x0091,0x0089,0x0085,0x0083,
+ 0xED44,0xF7B7,0x0083,0xED43,0xED42,0x0085,0x0083,0xBECF,0xED41,0x0083,0xED40,0xECA0,0x0089,0x0085,0x0083,0xEC9F,
+ 0xEC9E,0x0083,0xEC9D,0xEC9C,0x0085,0x0083,0xC7CA,0xEC9B,0x0083,0xEC9A,0xEC99,0x00A1,0x0091,0x0089,0x0085,0x0083,
+ 0xF7B4,0xEC98,0x0083,0xF7B3,0xF7B2,0x0085,0x0083,0xEC97,0xEC96,0x0083,0xEC95,0xB0B0,0x0089,0x0085,0x0083,0xEC94,
+ 0xD0AC,0x0083,0xEC93,0xEC92,0x0085,0x0083,0xEC91,0xEC90,0x0083,0xEC8F,0xF7B1,0x0091,0x0089,0x0085,0x0083,0xEC8E,
+ 0xEC8D,0x0083,0xEC8C,0xEC8B,0x0085,0x0083,0xEC8A,0xEC89,0x0083,0xEC88,0xEC87,0x0089,0x0085,0x0083,0xF7B0,0xEC86,
+ 0x0083,0xEC85,0xEC84,0x0085,0x0083,0xEC83,0xEC82,0x0083,0xB0D0,0xEC81,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,
+ 0xD1A5,0xBDF9,0x0083,0xEC80,0xEC7E,0x0085,0x0083,0xEC7D,0xEC7C,0x0083,0xEC7B,0xEC7A,0x0089,0x0085,0x0083,0xEC79,
+ 0xEC78,0x0083,0xEC77,0xB8EF,0x0085,0x0083,0xEC76,0xEC75,0x0083,0xEC74,0xD8CC,0x0091,0x0089,0x0085,0x0083,0xEC73,
+ 0xEC72,0x0083,0xC3E6,0xC3D2,0x0085,0x0083,0xBFBF,0xEC71,0x0083,0xB7C7,0xEC70,0x0089,0x0085,0x0083,0xEC6F,0xB5E5,
+ 0x0083,0xEC6E,0xBEB2,0x0085,0x0083,0xEC6D,0xEC6C,0x0083,0xBEB8,0xEC6B,0x00A1,0x0091,0x0089,0x0085,0x0083,0xEC6A,
+ 0xF6A6,0x0083,0xC7E0,0xEC69,0x0085,0x0083,0xEC68,0xEC67,0x0083,0xEC66,0xEC65,0x0089,0x0085,0x0083,0xEC64,0xEC63,
+ 0x0083,0xEC62,0xEC61,0x0085,0x0083,0xEC60,0xEC5F,0x0083,0xEC5E,0xEC5D,0x0091,0x0089,0x0085,0x0083,0xEC5C,0xEC5B,
+ 0x0083,0xEC5A,0xEC59,0x0085,0x0083,0xEC58,0xEC57,0x0083,0xF6B2,0xEC56,0x0089,0x0085,0x0083,0xEC55,0xEC54,0x0083,
+ 0xEC53,0xC5F9,0x0085,0x0083,0xB0D4,0xEC52,0x0083,0xEC51,0xEC50,0x017F,0x00FF,0x00BF,0x009F,0x008F,0x0087,0x0083,
+ 0xEC4F,0x0083,0xEC4E,0xC2B6,0x0085,0x0083,0xEC4D,0xF6B1,0x0083,0xEC4C,0xEC4B,0x0089,0x0085,0x0083,0xF6B0,0xEC4A,
+ 0x0083,0xEC49,0xF6AF,0x0085,0x0083,0xEC48,0xEC47,0x0083,0xEC46,0xEC45,0x0091,0x0089,0x0085,0x0083,0xEC44,0xEC43,
+ 0x0083,0xEC42,0xEC41,0x0085,0x0083,0xEC40,0xEBA0,0x0083,0xEB9F,0xCFBC,0x0089,0x0085,0x0083,0xEB9E,0xCBAA,0x0083,
+ 0xEB9D,0xEB9C,0x0085,0x0083,0xEB9B,0xEB9A,0x0083,0xEB99,0xC1D8,0x00A1,0x0091,0x0089,0x0085,0x0083,0xEB98,0xEB97,
+ 0x0083,0xC4DE,0xEB96,0x0085,0x0083,0xEB95,0xEB94,0x0083,0xF6AD,0xF6AE,0x0089,0x0085,0x0083,0xBBF4,0xEB93,0x0083,
+ 0xEB92,0xEB91,0x0085,0x0083,0xC3B9,0xF6AC,0x0083,0xD5F0,0xF6AA,0x0091,0x0089,0x0085,0x0083,0xEB90,0xCFF6,0x0083,
+ 0xEB8F,0xEB8E,0x0085,0x0083,0xF6AB,0xD0E8,0x0083,0xEB8D,0xCEED,0x0089,0x0085,0x0083,0xEB8C,0xEB8B,0x0083,0xEB8A,
+ 0xEB89,0x0085,0x0083,0xB1A2,0xEB88,0x0083,0xC0D7,0xC1E3,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0xEB87,0xEB86,
+ 0x0083,0xF6A8,0xEB85,0x0085,0x0083,0xEB84,0xEB83,0x0083,0xF6A9,0xEB82,0x0089,0x0085,0x0083,0xEB81,0xEB80,0x0083,
+ 0xEB7E,0xD1A9,0x0085,0x0083,0xF6A7,0xD3EA,0x0083,0xEB7D,0xEB7C,0x0091,0x0089,0x0085,0x0083,0xEB7B,0xEB7A,0x0083,
+ 0xEB79,0xEB78,0x0085,0x0083,0xEB77,0xF6C5,0x0083,0xEB76,0xEB75,0x0089,0x0085,0x0083,0xEB74,0xEB73,0x0083,0xEB72,
+ 0xEB71,0x0085,0x0083,0xEB70,0xEB6F,0x0083,0xEB6E,0xEB6D,0x00A1,0x0091,0x0089,0x0085,0x0083,0xB5F1,0xEB6C,0x0083,
+ 0xEB6B,0xF6C3,0x0085,0x0083,0xEB6A,0xEB69,0x0083,0xB3FB,0xF6C2,0x0089,0x0085,0x0083,0xD3BA,0xB4C6,0x0083,0xEB68,
+ 0xEB67,0x0085,0x0083,0xEFF4,0xEB66,0x0083,0xB9CD,0xBCAF,0x0091,0x0089,0x0085,0x0083,0xD1C5,0xD0DB,0x0083,0xEB65,
+ 0xEB64,0x0085,0x0083,0xD1E3,0xC8B8,0x0083,0xEB63,0xC4D1,0x0089,0x0085,0x0083,0xF6C1,0xF6C0,0x0083,0xEB62,0xEB61,
+ 0x0085,0x0083,0xF6BF,0xEB60,0x0083,0xEB5F,0xC1A5,0x0101,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0xEB5E,0xEB5D,
+ 0x0083,0xE3C4,0xEB5C,0x0085,0x0083,0xEB5B,0xDAF4,0x0083,0xEB5A,0xEB59,0x0089,0x0085,0x0083,0xEB58,0xEB57,0x0083,
+ 0xEB56,0xEB55,0x0085,0x0083,0xEB54,0xEB53,0x0083,0xCBED,0xEB52,0x0091,0x0089,0x0085,0x0083,0xEB51,0xEB50,0x0083,
+ 0xEB4F,0xEB4E,0x0085,0x0083,0xEB4D,0xEB4C,0x0083,0xEB4B,0xEB4A,0x0089,0x0085,0x0083,0xEB49,0xD5CF,0x0083,0xEB48,
+ 0xEB47,0x0085,0x0083,0xCFB6,0xB0AF,0x0083,0xDAF3,0xEB46,0x00A1,0x0091,0x0089,0x0085,0x0083,0xEB45,0xB8F4,0x0083,
+ 0xEB44,0xEB43,0x0085,0x0083,0xEB42,0xD2FE,0x0083,0xCBE6,0xEB41,0x0089,0x0085,0x0083,0xDAF2,0xEB40,0x0083,0xCBE5,
+ 0xEAA0,0x0085,0x0083,0xEA9F,0xDAF1,0x0083,0xEA9E,0xC2A1,0x0091,0x0089,0x0085,0x0083,0xD3E7,0xEA9D,0x0083,0xEA9C,
+ 0xEA9B,0x0085,0x0083,0xEA9A,0xEA99,0x0083,0xEA98,0xEA97,0x0089,0x0085,0x0083,0xEA96,0xEA95,0x0083,0xEA94,0xEA93,
+ 0x0085,0x0083,0xEA92,0xEA91,0x0083,0xCFDD,0xCCD5,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0xC1EA,0xDAF0,0x0083,
+ 0xEA90,0xDAEF,0x0085,0x0083,0xEA8F,0xEA8E,0x0083,0xEA8D,0xEA8C,0x0089,0x0085,0x0083,0xEA8B,0xDAEE,0x0083,0xEA8A,
+ 0xC5E3,0x0085,0x0083,0xCFD5,0xD4C9,0x0083,0xDAED,0xEA89,0x0091,0x0089,0x0085,0x0083,0xEA88,0xB3FD,0x0083,0xEA87,
+ 0xD4BA,0x0085,0x0083,0xB6B8,0xEA86,0x0083,0xDAEC,0xEA85,0x0089,0x0085,0x0083,0xEA84,0xEA83,0x0083,0xB1DD,0xEA82,
+ 0x0085,0x0083,0xEA81,0xEA80,0x0083,0xEA7E,0xEA7D,0x00A1,0x0091,0x0089,0x0085,0x0083,0xC9C2,0xDAEB,0x0083,0xEA7C,
+ 0xEA7B,0x0085,0x0083,0xEA7A,0xCFDE,0x0083,0xEA79,0xEA78,0x0089,0x0085,0x0083,0xBDB5,0xC4B0,0x0083,0xC2AA,0xEA77,
+ 0x0085,0x0083,0xDAEA,0xB3C2,0x0083,0xC2A4,0xC2BD,0x0091,0x0089,0x0085,0x0083,0xBCCA,0xB8BD,0x0083,0xEA76,0xDAE9,
+ 0x0085,0x0083,0xEA75,0xCDD3,0x0083,0xB0A2,0xEA74,0x0089,0x0085,0x0083,0xDAE7,0xDAE8,0x0083,0xD7E8,0xEA73,0x0085,
+ 0x0083,0xEA72,0xEA71,0x0083,0xEA70,0xBDD7,0x027F,0x017F,0x00FF,0x00BF,0x009F,0x008F,0x0087,0x0083,0xD5F3,0x0083,
+ 0xD2F5,0xD1F4,0x0085,0x0083,0xB7C0,0xDAE5,0x0083,0xEA6F,0xEA6E,0x0089,0x0085,0x0083,0xC8EE,0xEA6D,0x0083,0xEA6C,
+ 0xEA6B,0x0085,0x0083,0xDAE6,0xEA6A,0x0083,0xEA69,0xEA68,0x0091,0x0089,0x0085,0x0083,0xEA67,0xEA66,0x0083,0xEA65,
+ 0xEA64,0x0085,0x0083,0xDAE3,0xDAE4,0x0083,0xEA63,0xB6D3,0x0089,0x0085,0x0083,0xEA62,0xDAE2,0x0083,0xB8B7,0xEA61,
+ 0x0085,0x0083,0xE3DB,0xE3DA,0x0083,0xEA60,0xE3D9,0x00A1,0x0091,0x0089,0x0085,0x0083,0xE3D8,0xE3D7,0x0083,0xC0AB,
+ 0xEA5F,0x0085,0x0083,0xE3D6,0xC0BB,0x0083,0xB2FB,0xE3D5,0x0089,0x0085,0x0083,0xD1D6,0xE3D4,0x0083,0xE3D3,0xE3D2,
+ 0x0085,0x0083,0xE3D1,0xD1CB,0x0083,0xE3D0,0xEA5E,0x0091,0x0089,0x0085,0x0083,0xE3CF,0xD4C4,0x0083,0xE3CE,0xE3CD,
+ 0x0085,0x0083,0xBAD2,0xB8F3,0x0083,0xB7A7,0xEA5D,0x0089,0x0085,0x0083,0xE3CC,0xC3F6,0x0083,0xE3CB,0xCEC5,0x0085,
+ 0x0083,0xB9EB,0xC4D6,0x0083,0xD5A2,0xC3C6,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0xE3CA,0xE3C9,0x0083,0xBCE4,
+ 0xE3C8,0x0085,0x0083,0xCFD0,0xE3C7,0x0083,0xC8F2,0xB4B3,0x0089,0x0085,0x0083,0xCECA,0xB1D5,0x0083,0xEA5C,0xE3C6,
+ 0x0085,0x0083,0xC9C1,0xE3C5,0x0083,0xC3C5,0xEA5B,0x0091,0x0089,0x0085,0x0083,0xEA5A,0xEA59,0x0083,0xEA58,0xEA57,
+ 0x0085,0x0083,0xEA56,0xEA55,0x0083,0xEA54,0xEA53,0x0089,0x0085,0x0083,0xEA52,0xEA51,0x0083,0xEA50,0xEA4F,0x0085,
+ 0x0083,0xEA4E,0xEA4D,0x0083,0xEA4C,0xEA4B,0x00A1,0x0091,0x0089,0x0085,0x0083,0xEA4A,0xEA49,0x0083,0xEA48,0xEA47,
+ 0x0085,0x0083,0xEA46,0xEA45,0x0083,0xEA44,0xEA43,0x0089,0x0085,0x0083,0xEA42,0xEA41,0x0083,0xEA40,0xE9A0,0x0085,
+ 0x0083,0xE99F,0xE99E,0x0083,0xE99D,0xE99C,0x0091,0x0089,0x0085,0x0083,0xE99B,0xE99A,0x0083,0xE999,0xE998,0x0085,
+ 0x0083,0xE997,0xE996,0x0083,0xE995,0xE994,0x0089,0x0085,0x0083,0xE993,0xE992,0x0083,0xE991,0xE990,0x0085,0x0083,
+ 0xE98F,0xE98E,0x0083,0xE98D,0xE98C,0x0101,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0xE98B,0xE98A,0x0083,0xE989,
+ 0xE988,0x0085,0x0083,0xE987,0xE986,0x0083,0xE985,0xE984,0x0089,0x0085,0x0083,0xE983,0xE982,0x0083,0xE981,0xE980,
+ 0x0085,0x0083,0xE97E,0xE97D,0x0083,0xE97C,0xE97B,0x0091,0x0089,0x0085,0x0083,0xE97A,0xE979,0x0083,0xE978,0xE977,
+ 0x0085,0x0083,0xE976,0xE975,0x0083,0xE974,0xE973,0x0089,0x0085,0x0083,0xE972,0xE971,0x0083,0xE970,0xE96F,0x0085,
+ 0x0083,0xE96E,0xE96D,0x0083,0xE96C,0xE96B,0x00A1,0x0091,0x0089,0x0085,0x0083,0xE96A,0xE969,0x0083,0xE968,0xE967,
+ 0x0085,0x0083,0xE966,0xE965,0x0083,0xE964,0xE963,0x0089,0x0085,0x0083,0xE962,0xE961,0x0083,0xE960,0xE95F,0x0085,
+ 0x0083,0xE95E,0xE95D,0x0083,0xE95C,0xE95B,0x0091,0x0089,0x0085,0x0083,0xE95A,0xE959,0x0083,0xE958,0xE957,0x0085,
+ 0x0083,0xE956,0xE955,0x0083,0xE954,0xB3A4,0x0089,0x0085,0x0083,0xE953,0xE952,0x0083,0xE951,0xE950,0x0085,0x0083,
+ 0xE94F,0xE94E,0x0083,0xE94D,0xE94C,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0xCFE2,0xE94B,0x0083,0xE94A,0xEFF0,
+ 0x0085,0x0083,0xEFEF,0xEFEE,0x0083,0xC1AD,0xEFED,0x0089,0x0085,0x0083,0xE949,0xC0D8,0x0083,0xEFEC,0xEFEB,0x0085,
+ 0x0083,0xEFEA,0xEFE9,0x0083,0xEFE8,0xEFE7,0x0091,0x0089,0x0085,0x0083,0xEFE6,0xEFE5,0x0083,0xEFE4,0xC1CD,0x0085,
+ 0x0083,0xEFE3,0xEFE2,0x0083,0xE948,0xEFE0,0x0089,0x0085,0x0083,0xEFDF,0xEFE1,0x0083,0xBEB5,0xEFDE,0x0085,0x0083,
+ 0xE947,0xEFDD,0x0083,0xEFDC,0xEFDB,0x00A1,0x0091,0x0089,0x0085,0x0083,0xEFDA,0xE946,0x0083,0xEFD9,0xEFD8,0x0085,
+ 0x0083,0xEFD7,0xB0F7,0x0083,0xB8E4,0xEFD6,0x0089,0x0085,0x0083,0xEFD5,0xC4F8,0x0083,0xEFD4,0xE945,0x0085,0x0083,
+ 0xC4F7,0xEFD3,0x0083,0xE944,0xD5F2,0x0091,0x0089,0x0085,0x0083,0xEFD2,0xEFD1,0x0083,0xEFD0,0xE943,0x0085,0x0083,
+ 0xEFCE,0xC3BE,0x0083,0xB6C6,0xEFCD,0x0089,0x0085,0x0083,0xEFCC,0xE942,0x0083,0xEFCB,0xB6CD,0x0085,0x0083,0xEFF1,
+ 0xC7C2,0x0083,0xEFCA,0xEFC9,0x0181,0x0101,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0xEFC8,0xEFCF,0x0083,0xEFC7,
+ 0xE941,0x0085,0x0083,0xEFC6,0xEFC5,0x0083,0xC3CC,0xBEE2,0x0089,0x0085,0x0083,0xBCFC,0xB6A7,0x0083,0xEFC4,0xEFC2,
+ 0x0085,0x0083,0xEFC1,0xEFC3,0x0083,0xCFC7,0xE940,0x0091,0x0089,0x0085,0x0083,0xBDF5,0xD7B6,0x0083,0xB4B8,0xC2E0,
+ 0x0085,0x0083,0xEFC0,0xCEFD,0x0083,0xE8A0,0xEFBF,0x0089,0x0085,0x0083,0xEFBE,0xEFBD,0x0083,0xE89F,0xEFBC,0x0085,
+ 0x0083,0xC3AA,0xB4ED,0x0083,0xEFBB,0xD5E0,0x00A1,0x0091,0x0089,0x0085,0x0083,0xEFBA,0xEFB9,0x0083,0xEFB8,0xEFB7,
+ 0x0085,0x0083,0xEFB6,0xCCE0,0x0083,0xC8F1,0xEFB5,0x0089,0x0085,0x0083,0xEFB4,0xEFB3,0x0083,0xD0BF,0xB7E6,0x0085,
+ 0x0083,0xEFB2,0xEFB1,0x0083,0xD0E2,0xEFB0,0x0091,0x0089,0x0085,0x0083,0xEFAF,0xB9F8,0x0083,0xB3FA,0xEFAD,0x0085,
+ 0x0083,0xEFAE,0xCBF8,0x0083,0xCFFA,0xEFAC,0x0089,0x0085,0x0083,0xC1B4,0xEFAB,0x0083,0xEFAA,0xE89E,0x0085,0x0083,
+ 0xC6CC,0xEFA9,0x0083,0xD6FD,0xEFA8,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0xD2F8,0xEFA7,0x0083,0xEFA6,0xEFA5,
+ 0x0085,0x0083,0xB2F9,0xD2BF,0x0083,0xBDC2,0xEFA4,0x0089,0x0085,0x0083,0xEFA3,0xC3FA,0x0083,0xB8F5,0xEFA2,0x0085,
+ 0x0083,0xEEFE,0xEFA1,0x0083,0xEEFD,0xEEFC,0x0091,0x0089,0x0085,0x0083,0xE89D,0xEEFB,0x0083,0xEEFA,0xCFB3,0x0085,
+ 0x0083,0xEEF9,0xD5A1,0x0083,0xEEF8,0xEEF7,0x0089,0x0085,0x0083,0xEEF6,0xC2C1,0x0083,0xCDAD,0xEEF5,0x0085,0x0083,
+ 0xE89C,0xEEF3,0x0083,0xEEF4,0xEEF2,0x00A1,0x0091,0x0089,0x0085,0x0083,0xEEF1,0xEEF0,0x0083,0xE89B,0xE89A,0x0085,
+ 0x0083,0xEEEF,0xEEEE,0x0083,0xEEED,0xE899,0x0089,0x0085,0x0083,0xEEEC,0xEEEB,0x0083,0xEEEA,0xEEE9,0x0085,0x0083,
+ 0xEEE8,0xEEE7,0x0083,0xEEE6,0xE898,0x0091,0x0089,0x0085,0x0083,0xC3AD,0xC7A6,0x0083,0xEEE5,0xC1E5,0x0085,0x0083,
+ 0xB2AC,0xCCFA,0x0083,0xD3CB,0xEEE4,0x0089,0x0085,0x0083,0xBCD8,0xEEE3,0x0083,0xEEE2,0xD7EA,0x0085,0x0083,0xEEE1,
+ 0xEEE0,0x0083,0xEEDF,0xEEDE,0x0101,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0xEEDD,0xB2A7,0x0083,0xEEDC,0xC7AF,
+ 0x0085,0x0083,0xEEDB,0xC7AE,0x0083,0xEEDA,0xEED9,0x0089,0x0085,0x0083,0xC5A5,0xEED7,0x0083,0xEED8,0xEED5,0x0085,
+ 0x0083,0xEED6,0xB9B3,0x0083,0xCED9,0xBEFB,0x0091,0x0089,0x0085,0x0083,0xC7D5,0xD4BF,0x0083,0xEED4,0xEED3,0x0085,
+ 0x0083,0xB8D6,0xB1B5,0x0083,0xC4C6,0xD6D3,0x0089,0x0085,0x0083,0xB3AE,0xB6DB,0x0083,0xEED2,0xEED1,0x0085,0x0083,
+ 0xEED0,0xB8C6,0x0083,0xE897,0xEECE,0x00A1,0x0091,0x0089,0x0085,0x0083,0xE896,0xEECF,0x0083,0xEECD,0xB5F6,0x0085,
+ 0x0083,0xB7B0,0xE895,0x0083,0xEECC,0xEECB,0x0089,0x0085,0x0083,0xC7A5,0xEECA,0x0083,0xEEC9,0xEEC7,0x0085,0x0083,
+ 0xEEC8,0xB6A4,0x0083,0xD5EB,0xEEC6,0x0091,0x0089,0x0085,0x0083,0xEEC5,0xEEC4,0x0083,0xE894,0xE893,0x0085,0x0083,
+ 0xE892,0xE891,0x0083,0xE890,0xE88F,0x0089,0x0085,0x0083,0xE88E,0xE88D,0x0083,0xE88C,0xE88B,0x0085,0x0083,0xE88A,
+ 0xE889,0x0083,0xE888,0xE887,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0xE886,0xE885,0x0083,0xE884,0xE883,0x0085,
+ 0x0083,0xE882,0xE881,0x0083,0xE880,0xE87E,0x0089,0x0085,0x0083,0xE87D,0xE87C,0x0083,0xE87B,0xF6CE,0x0085,0x0083,
+ 0xE87A,0xE879,0x0083,0xE878,0xE877,0x0091,0x0089,0x0085,0x0083,0xE876,0xE875,0x0083,0xE874,0xE873,0x0085,0x0083,
+ 0xE872,0xE871,0x0083,0xE870,0xE86F,0x0089,0x0085,0x0083,0xE86E,0xE86D,0x0083,0xE86C,0xE86B,0x0085,0x0083,0xE86A,
+ 0xE869,0x0083,0xE868,0xE867,0x00A1,0x0091,0x0089,0x0085,0x0083,0xE866,0xE865,0x0083,0xE864,0xE863,0x0085,0x0083,
+ 0xE862,0xE861,0x0083,0xE860,0xE85F,0x0089,0x0085,0x0083,0xE85E,0xE85D,0x0083,0xE85C,0xE85B,0x0085,0x0083,0xE85A,
+ 0xE859,0x0083,0xE858,0xE857,0x0091,0x0089,0x0085,0x0083,0xE856,0xE855,0x0083,0xE854,0xE853,0x0085,0x0083,0xE852,
+ 0xE851,0x0083,0xE850,0xE84F,0x0089,0x0085,0x0083,0xF6CD,0xE84E,0x0083,0xE84D,0xE84C,0x0085,0x0083,0xE84B,0xE84A,
+ 0x0083,0xE849,0xE848,0x047D,0x027F,0x017F,0x00FF,0x00BF,0x009F,0x008F,0x0087,0x0083,0xE847,0x0083,0xE846,0xE845,
+ 0x0085,0x0083,0xE844,0xE843,0x0083,0xE842,0xE841,0x0089,0x0085,0x0083,0xE840,0xE7A0,0x0083,0xE79F,0xE79E,0x0085,
+ 0x0083,0xE79D,0xE79C,0x0083,0xE79B,0xE79A,0x0091,0x0089,0x0085,0x0083,0xE799,0xE798,0x0083,0xE797,0xE796,0x0085,
+ 0x0083,0xE795,0xE794,0x0083,0xE793,0xE792,0x0089,0x0085,0x0083,0xE791,0xE790,0x0083,0xE78F,0xE78E,0x0085,0x0083,
+ 0xE78D,0xE78C,0x0083,0xE78B,0xE78A,0x00A1,0x0091,0x0089,0x0085,0x0083,0xE789,0xE788,0x0083,0xE787,0xE786,0x0085,
+ 0x0083,0xE785,0xE784,0x0083,0xE783,0xE782,0x0089,0x0085,0x0083,0xE781,0xE780,0x0083,0xE77E,0xE77D,0x0085,0x0083,
+ 0xE77C,0xE77B,0x0083,0xE77A,0xE779,0x0091,0x0089,0x0085,0x0083,0xE778,0xE777,0x0083,0xE776,0xE775,0x0085,0x0083,
+ 0xE774,0xE773,0x0083,0xE772,0xE771,0x0089,0x0085,0x0083,0xE770,0xE76F,0x0083,0xE76E,0xE76D,0x0085,0x0083,0xE76C,
+ 0xE76B,0x0083,0xE76A,0xE769,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0xE768,0xE767,0x0083,0xE766,0xE765,0x0085,
+ 0x0083,0xE764,0xE763,0x0083,0xE762,0xE761,0x0089,0x0085,0x0083,0xE760,0xE75F,0x0083,0xE75E,0xE75D,0x0085,0x0083,
+ 0xE75C,0xE75B,0x0083,0xE75A,0xE759,0x0091,0x0089,0x0085,0x0083,0xE758,0xE757,0x0083,0xE756,0xE755,0x0085,0x0083,
+ 0xE754,0xE753,0x0083,0xE752,0xE751,0x0089,0x0085,0x0083,0xE750,0xE74F,0x0083,0xE74E,0xE74D,0x0085,0x0083,0xE74C,
+ 0xE74B,0x0083,0xE74A,0xE749,0x00A1,0x0091,0x0089,0x0085,0x0083,0xE748,0xF7E9,0x0083,0xE747,0xE746,0x0085,0x0083,
+ 0xE745,0xE744,0x0083,0xE743,0xE742,0x0089,0x0085,0x0083,0xE741,0xE740,0x0083,0xE6A0,0xE69F,0x0085,0x0083,0xE69E,
+ 0xF6CB,0x0083,0xE69D,0xE69C,0x0091,0x0089,0x0085,0x0083,0xE69B,0xE69A,0x0083,0xE699,0xE698,0x0085,0x0083,0xE697,
+ 0xE696,0x0083,0xE695,0xE694,0x0089,0x0085,0x0083,0xE693,0xE692,0x0083,0xE691,0xE690,0x0085,0x0083,0xE68F,0xE68E,
+ 0x0083,0xE68D,0xE68C,0x0101,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0xE68B,0xE68A,0x0083,0xE689,0xE688,0x0085,
+ 0x0083,0xE687,0xE686,0x0083,0xE685,0xE684,0x0089,0x0085,0x0083,0xE683,0xE682,0x0083,0xE681,0xE680,0x0085,0x0083,
+ 0xE67E,0xE67D,0x0083,0xE67C,0xE67B,0x0091,0x0089,0x0085,0x0083,0xE67A,0xE679,0x0083,0xE678,0xE677,0x0085,0x0083,
+ 0xE676,0xE675,0x0083,0xE674,0xE673,0x0089,0x0085,0x0083,0xE672,0xE671,0x0083,0xE670,0xE66F,0x0085,0x0083,0xE66E,
+ 0xE66D,0x0083,0xE66C,0xE66B,0x00A1,0x0091,0x0089,0x0085,0x0083,0xE66A,0xE669,0x0083,0xE668,0xE667,0x0085,0x0083,
+ 0xE666,0xE665,0x0083,0xE664,0xE663,0x0089,0x0085,0x0083,0xF6CC,0xE662,0x0083,0xE661,0xE660,0x0085,0x0083,0xE65F,
+ 0xE65E,0x0083,0xE65D,0xE65C,0x0091,0x0089,0x0085,0x0083,0xE65B,0xE65A,0x0083,0xE659,0xE658,0x0085,0x0083,0xE657,
+ 0xE656,0x0083,0xE655,0xE654,0x0089,0x0085,0x0083,0xE653,0xE652,0x0083,0xE651,0xE650,0x0085,0x0083,0xE64F,0xE64E,
+ 0x0083,0xE64D,0xE64C,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0xE64B,0xE64A,0x0083,0xE649,0xE648,0x0085,0x0083,
+ 0xE647,0xE646,0x0083,0xE645,0xE644,0x0089,0x0085,0x0083,0xE643,0xE642,0x0083,0xE641,0xE640,0x0085,0x0083,0xE5A0,
+ 0xF6CA,0x0083,0xE59F,0xE59E,0x0091,0x0089,0x0085,0x0083,0xE59D,0xE59C,0x0083,0xE59B,0xE59A,0x0085,0x0083,0xE599,
+ 0xE598,0x0083,0xE597,0xE596,0x0089,0x0085,0x0083,0xE595,0xE594,0x0083,0xE593,0xE592,0x0085,0x0083,0xE591,0xE590,
+ 0x0083,0xE58F,0xE58E,0x00A1,0x0091,0x0089,0x0085,0x0083,0xE58D,0xE58C,0x0083,0xE58B,0xE58A,0x0085,0x0083,0xE589,
+ 0xE588,0x0083,0xE587,0xE586,0x0089,0x0085,0x0083,0xE585,0xE584,0x0083,0xE583,0xE582,0x0085,0x0083,0xE581,0xE580,
+ 0x0083,0xE57E,0xE57D,0x0091,0x0089,0x0085,0x0083,0xE57C,0xE57B,0x0083,0xE57A,0xE579,0x0085,0x0083,0xE578,0xE577,
+ 0x0083,0xE576,0xE575,0x0089,0x0085,0x0083,0xE574,0xF6C9,0x0083,0xE573,0xE572,0x0085,0x0083,0xE571,0xE570,0x0083,
+ 0xE56F,0xE56E,0x017F,0x00FF,0x00BF,0x009F,0x008F,0x0087,0x0083,0xE56D,0x0083,0xE56C,0xE56B,0x0085,0x0083,0xE56A,
+ 0xE569,0x0083,0xE568,0xE567,0x0089,0x0085,0x0083,0xE566,0xE565,0x0083,0xE564,0xE563,0x0085,0x0083,0xE562,0xE561,
+ 0x0083,0xE560,0xE55F,0x0091,0x0089,0x0085,0x0083,0xE55E,0xE55D,0x0083,0xE55C,0xE55B,0x0085,0x0083,0xE55A,0xE559,
+ 0x0083,0xE558,0xE557,0x0089,0x0085,0x0083,0xE556,0xE555,0x0083,0xE554,0xE553,0x0085,0x0083,0xE552,0xE551,0x0083,
+ 0xE550,0xE54F,0x00A1,0x0091,0x0089,0x0085,0x0083,0xE54E,0xE54D,0x0083,0xE54C,0xE54B,0x0085,0x0083,0xE54A,0xE549,
+ 0x0083,0xE548,0xE547,0x0089,0x0085,0x0083,0xE546,0xE545,0x0083,0xE544,0xE543,0x0085,0x0083,0xE542,0xE541,0x0083,
+ 0xE540,0xE4A0,0x0091,0x0089,0x0085,0x0083,0xE49F,0xE49E,0x0083,0xE49D,0xE49C,0x0085,0x0083,0xE49B,0xE49A,0x0083,
+ 0xE499,0xE498,0x0089,0x0085,0x0083,0xE497,0xE496,0x0083,0xE495,0xE494,0x0085,0x0083,0xE493,0xE492,0x0083,0xE491,
+ 0xE490,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0xE48F,0xE48E,0x0083,0xE48D,0xE48C,0x0085,0x0083,0xE48B,0xE48A,
+ 0x0083,0xE489,0xE488,0x0089,0x0085,0x0083,0xE487,0xE486,0x0083,0xE485,0xE484,0x0085,0x0083,0xE483,0xE482,0x0083,
+ 0xE481,0xE480,0x0091,0x0089,0x0085,0x0083,0xE47E,0xE47D,0x0083,0xE47C,0xE47B,0x0085,0x0083,0xE47A,0xE479,0x0083,
+ 0xE478,0xE477,0x0089,0x0085,0x0083,0xE476,0xE475,0x0083,0xE474,0xE473,0x0085,0x0083,0xE472,0xE471,0x0083,0xE470,
+ 0xE46F,0x00A1,0x0091,0x0089,0x0085,0x0083,0xE46E,0xE46D,0x0083,0xE46C,0xE46B,0x0085,0x0083,0xE46A,0xE469,0x0083,
+ 0xE468,0xE467,0x0089,0x0085,0x0083,0xE466,0xE465,0x0083,0xE464,0xE463,0x0085,0x0083,0xE462,0xE461,0x0083,0xE460,
+ 0xE45F,0x0091,0x0089,0x0085,0x0083,0xF6C8,0xE45E,0x0083,0xE45D,0xE45C,0x0085,0x0083,0xE45B,0xE45A,0x0083,0xE459,
+ 0xE458,0x0089,0x0085,0x0083,0xE457,0xE456,0x0083,0xE455,0xE454,0x0085,0x0083,0xE453,0xE452,0x0083,0xE451,0xE450,
+ 0x0101,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0xE44F,0xE44E,0x0083,0xE44D,0xE44C,0x0085,0x0083,0xE44B,0xE44A,
+ 0x0083,0xE449,0xE448,0x0089,0x0085,0x0083,0xE447,0xE446,0x0083,0xF6C7,0xE445,0x0085,0x0083,0xE444,0xE443,0x0083,
+ 0xE442,0xE441,0x0091,0x0089,0x0085,0x0083,0xE440,0xE3A0,0x0083,0xE39F,0xE39E,0x0085,0x0083,0xE39D,0xE39C,0x0083,
+ 0xE39B,0xE39A,0x0089,0x0085,0x0083,0xE399,0xE398,0x0083,0xE397,0xE396,0x0085,0x0083,0xE395,0xE394,0x0083,0xE393,
+ 0xE392,0x00A1,0x0091,0x0089,0x0085,0x0083,0xE391,0xE390,0x0083,0xE38F,0xE38E,0x0085,0x0083,0xE38D,0xE38C,0x0083,
+ 0xE38B,0xE38A,0x0089,0x0085,0x0083,0xE389,0xE388,0x0083,0xF6C6,0xE387,0x0085,0x0083,0xE386,0xE385,0x0083,0xE384,
+ 0xE383,0x0091,0x0089,0x0085,0x0083,0xE382,0xE381,0x0083,0xE380,0xE37E,0x0085,0x0083,0xE37D,0xE37C,0x0083,0xE37B,
+ 0xE37A,0x0089,0x0085,0x0083,0xE379,0xE378,0x0083,0xE377,0xE376,0x0085,0x0083,0xE375,0xE374,0x0083,0xE373,0xE372,
+ 0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0xE371,0xE370,0x0083,0xE36F,0xE36E,0x0085,0x0083,0xBCF8,0xE36D,0x0083,
+ 0xE36C,0xE36B,0x0089,0x0085,0x0083,0xE36A,0xE369,0x0083,0xE368,0xE367,0x0085,0x0083,0xE366,0xE365,0x0083,0xE364,
+ 0xE363,0x0091,0x0089,0x0085,0x0083,0xE362,0xE361,0x0083,0xE360,0xE35F,0x0085,0x0083,0xE35E,0xE35D,0x0083,0xE35C,
+ 0xE35B,0x0089,0x0085,0x0083,0xE35A,0xE359,0x0083,0xE358,0xE357,0x0085,0x0083,0xE356,0xE355,0x0083,0xE354,0xE353,
+ 0x00A1,0x0091,0x0089,0x0085,0x0083,0xE352,0xE351,0x0083,0xE350,0xE34F,0x0085,0x0083,0xE34E,0xE34D,0x0083,0xE34C,
+ 0xE34B,0x0089,0x0085,0x0083,0xE34A,0xE349,0x0083,0xE348,0xE347,0x0085,0x0083,0xE346,0xE345,0x0083,0xE344,0xE343,
+ 0x0091,0x0089,0x0085,0x0083,0xE342,0xE341,0x0083,0xE340,0xE2A0,0x0085,0x0083,0xE29F,0xE29E,0x0083,0xE29D,0xE29C,
+ 0x0089,0x0085,0x0083,0xE29B,0xE29A,0x0083,0xE299,0xE298,0x0085,0x0083,0xE297,0xE296,0x0083,0xE295,0xE294,0x027F,
+ 0x017F,0x00FF,0x00BF,0x009F,0x008F,0x0087,0x0083,0xE293,0x0083,0xE292,0xE291,0x0085,0x0083,0xE290,0xE28F,0x0083,
+ 0xE28E,0xE28D,0x0089,0x0085,0x0083,0xE28C,0xE28B,0x0083,0xE28A,0xE289,0x0085,0x0083,0xE288,0xE287,0x0083,0xE286,
+ 0xE285,0x0091,0x0089,0x0085,0x0083,0xE284,0xE283,0x0083,0xE282,0xE281,0x0085,0x0083,0xE280,0xE27E,0x0083,0xE27D,
+ 0xE27C,0x0089,0x0085,0x0083,0xE27B,0xE27A,0x0083,0xE279,0xE278,0x0085,0x0083,0xE277,0xE276,0x0083,0xE275,0xE274,
+ 0x00A1,0x0091,0x0089,0x0085,0x0083,0xE273,0xE272,0x0083,0xE271,0xE270,0x0085,0x0083,0xE26F,0xE26E,0x0083,0xE26D,
+ 0xE26C,0x0089,0x0085,0x0083,0xE26B,0xE26A,0x0083,0xE269,0xE268,0x0085,0x0083,0xE267,0xE266,0x0083,0xE265,0xE264,
+ 0x0091,0x0089,0x0085,0x0083,0xE263,0xE262,0x0083,0xE261,0xE260,0x0085,0x0083,0xE25F,0xE25E,0x0083,0xE25D,0xE25C,
+ 0x0089,0x0085,0x0083,0xE25B,0xE25A,0x0083,0xE259,0xE258,0x0085,0x0083,0xE257,0xE256,0x0083,0xE255,0xE254,0x00C1,
+ 0x00A1,0x0091,0x0089,0x0085,0x0083,0xE253,0xE252,0x0083,0xE251,0xE250,0x0085,0x0083,0xE24F,0xE24E,0x0083,0xE24D,
+ 0xE24C,0x0089,0x0085,0x0083,0xE24B,0xE24A,0x0083,0xE249,0xE248,0x0085,0x0083,0xE247,0xE246,0x0083,0xE245,0xE244,
+ 0x0091,0x0089,0x0085,0x0083,0xE243,0xE242,0x0083,0xE241,0xE240,0x0085,0x0083,0xE1A0,0xE19F,0x0083,0xE19E,0xE19D,
+ 0x0089,0x0085,0x0083,0xE19C,0xE19B,0x0083,0xE19A,0xE199,0x0085,0x0083,0xE198,0xB8AA,0x0083,0xE197,0xE196,0x00A1,
+ 0x0091,0x0089,0x0085,0x0083,0xE195,0xE194,0x0083,0xE193,0xE192,0x0085,0x0083,0xE191,0xE190,0x0083,0xE18F,0xE18E,
+ 0x0089,0x0085,0x0083,0xBDF0,0xE18D,0x0083,0xC1BF,0xD2B0,0x0085,0x0083,0xD6D8,0xC0EF,0x0083,0xE18C,0xCACD,0x0091,
+ 0x0089,0x0085,0x0083,0xD3D4,0xE18B,0x0083,0xB2C9,0xE18A,0x0085,0x0083,0xE189,0xE188,0x0083,0xE187,0xE186,0x0089,
+ 0x0085,0x0083,0xE185,0xE184,0x0083,0xE183,0xE182,0x0085,0x0083,0xE181,0xE180,0x0083,0xE17E,0xF5B8,0x0101,0x00C1,
+ 0x00A1,0x0091,0x0089,0x0085,0x0083,0xE17D,0xE17C,0x0083,0xE17B,0xE17A,0x0085,0x0083,0xF5B6,0xF5B7,0x0083,0xE179,
+ 0xE178,0x0089,0x0085,0x0083,0xE177,0xE176,0x0083,0xF5B5,0xF5B4,0x0085,0x0083,0xF5B3,0xE175,0x0083,0xE174,0xF5B2,
+ 0x0091,0x0089,0x0085,0x0083,0xE173,0xE172,0x0083,0xE171,0xE170,0x0085,0x0083,0xE16F,0xE16E,0x0083,0xF5B1,0xF5B0,
+ 0x0089,0x0085,0x0083,0xE16D,0xE16C,0x0083,0xE16B,0xE16A,0x0085,0x0083,0xE169,0xE168,0x0083,0xC8A9,0xC3D1,0x00A1,
+ 0x0091,0x0089,0x0085,0x0083,0xE167,0xE166,0x0083,0xE165,0xE164,0x0085,0x0083,0xE163,0xE162,0x0083,0xE161,0xD0D1,
+ 0x0089,0x0085,0x0083,0xF5AF,0xF5AD,0x0083,0xE160,0xE15F,0x0085,0x0083,0xF5AE,0xF5AB,0x0083,0xB4D7,0xE15E,0x0091,
+ 0x0089,0x0085,0x0083,0xD7ED,0xE15D,0x0083,0xB4BC,0xE15C,0x0085,0x0083,0xF5AC,0xE15B,0x0083,0xE15A,0xE159,0x0089,
+ 0x0085,0x0083,0xE158,0xE157,0x0083,0xC4F0,0xF5A7,0x0085,0x0083,0xF5A6,0xE156,0x0083,0xE155,0xE154,0x00C1,0x00A1,
+ 0x0091,0x0089,0x0085,0x0083,0xF5AA,0xCBE1,0x0083,0xBFE1,0xC3B8,0x0085,0x0083,0xBDCD,0xF5A9,0x0083,0xE153,0xF5A8,
+ 0x0089,0x0085,0x0083,0xBDB4,0xF5A3,0x0083,0xF5A5,0xCDAA,0x0085,0x0083,0xE152,0xB3EA,0x0083,0xE151,0xC0D2,0x0091,
+ 0x0089,0x0085,0x0083,0xF5A4,0xE150,0x0083,0xE14F,0xE14E,0x0085,0x0083,0xCBD6,0xF4FE,0x0083,0xBAA8,0xF5A1,0x0089,
+ 0x0085,0x0083,0xF5A2,0xE14D,0x0083,0xE14C,0xCCAA,0x0085,0x0083,0xD4CD,0xE14B,0x0083,0xE14A,0xB7D3,0x00A1,0x0091,
+ 0x0089,0x0085,0x0083,0xE149,0xE148,0x0083,0xD0EF,0xE147,0x0085,0x0083,0xE146,0xE145,0x0083,0xE144,0xBEC6,0x0089,
+ 0x0085,0x0083,0xE143,0xF4FB,0x0083,0xF4FD,0xF4FC,0x0085,0x0083,0xC5E4,0xD7C3,0x0083,0xC7F5,0xF4FA,0x0091,0x0089,
+ 0x0085,0x0083,0xD3CF,0xE142,0x0083,0xE141,0xDBBA,0x0085,0x0083,0xE140,0xE0A0,0x0083,0xDBB9,0xE09F,0x0089,0x0085,
+ 0x0083,0xE09E,0xE09D,0x0083,0xE09C,0xE09B,0x0085,0x0083,0xE09A,0xE099,0x0083,0xE098,0xE097,0x0181,0x0101,0x00C1,
+ 0x00A1,0x0091,0x0089,0x0085,0x0083,0xDBB8,0xE096,0x0083,0xE095,0xE094,0x0085,0x0083,0xE093,0xE092,0x0083,0xE091,
+ 0xE090,0x0089,0x0085,0x0083,0xDBB6,0xE08F,0x0083,0xDBB7,0xE08E,0x0085,0x0083,0xE08D,0xE08C,0x0083,0xE08B,0xE08A,
+ 0x0091,0x0089,0x0085,0x0083,0xE089,0xE088,0x0083,0xE087,0xE086,0x0085,0x0083,0xE085,0xE084,0x0083,0xDBB5,0xDBB3,
+ 0x0089,0x0085,0x0083,0xE083,0xE082,0x0083,0xE081,0xDBB4,0x0085,0x0083,0xE080,0xE07E,0x0083,0xE07D,0xE07C,0x00A1,
+ 0x0091,0x0089,0x0085,0x0083,0xB1C9,0xE07B,0x0083,0xE07A,0xE079,0x0085,0x0083,0xE078,0xE077,0x0083,0xE076,0xE075,
+ 0x0089,0x0085,0x0083,0xE074,0xE073,0x0083,0xE072,0xE071,0x0085,0x0083,0xE070,0xE06F,0x0083,0xE06E,0xE06D,0x0091,
+ 0x0089,0x0085,0x0083,0xE06C,0xE06B,0x0083,0xE06A,0xE069,0x0085,0x0083,0xE068,0xDBB2,0x0083,0xE067,0xB6F5,0x0089,
+ 0x0085,0x0083,0xE066,0xE065,0x0083,0xE064,0xDBB1,0x0085,0x0083,0xB6BC,0xE063,0x0083,0xE062,0xE061,0x00C1,0x00A1,
+ 0x0091,0x0089,0x0085,0x0083,0xE060,0xB5A6,0x0083,0xE05F,0xE05E,0x0085,0x0083,0xE05D,0xB3BB,0x0083,0xE05C,0xE05B,
+ 0x0089,0x0085,0x0083,0xE05A,0xE059,0x0083,0xDBB0,0xE058,0x0085,0x0083,0xB9F9,0xE057,0x0083,0xDBAF,0xE056,0x0091,
+ 0x0089,0x0085,0x0083,0xE055,0xB2BF,0x0083,0xD4C7,0xDBAA,0x0085,0x0083,0xE054,0xE053,0x0083,0xE052,0xDBAB,0x0089,
+ 0x0085,0x0083,0xBFA4,0xE051,0x0083,0xE050,0xE04F,0x0085,0x0083,0xBAC2,0xDBAC,0x0083,0xDBAE,0xE04E,0x00A1,0x0091,
+ 0x0089,0x0085,0x0083,0xE04D,0xE04C,0x0083,0xDBAD,0xE04B,0x0085,0x0083,0xE04A,0xE049,0x0083,0xDBA9,0xE048,0x0089,
+ 0x0085,0x0083,0xD6A3,0xDBA6,0x0083,0xDBA3,0xC0C9,0x0085,0x0083,0xE047,0xE046,0x0083,0xE045,0xBDBC,0x0091,0x0089,
+ 0x0085,0x0083,0xE044,0xE043,0x0083,0xDBA8,0xE042,0x0085,0x0083,0xDBA4,0xDBA7,0x0083,0xE041,0xE040,0x0089,0x0085,
+ 0x0083,0xD3F4,0xDFA0,0x0083,0xDF9F,0xDBA5,0x0085,0x0083,0xDF9E,0xDF9D,0x0083,0xC1DA,0xDAFE,0x0101,0x00C1,0x00A1,
+ 0x0091,0x0089,0x0085,0x0083,0xD7DE,0xDBA1,0x0083,0xDF9C,0xDAFD,0x0085,0x0083,0xC9DB,0xDAFB,0x0083,0xDAFC,0xDF9B,
+ 0x0089,0x0085,0x0083,0xC7F1,0xDBA2,0x0083,0xBAAA,0xD3CA,0x0085,0x0083,0xDF9A,0xDAF9,0x0083,0xDF99,0xD0B0,0x0091,
+ 0x0089,0x0085,0x0083,0xDF98,0xDF97,0x0083,0xDF96,0xB0EE,0x0085,0x0083,0xDF95,0xDF94,0x0083,0xC4C7,0xD0CF,0x0089,
+ 0x0085,0x0083,0xDAFA,0xDF93,0x0083,0xDF92,0xDF91,0x0085,0x0083,0xDAF7,0xDF90,0x0083,0xDAF6,0xDF8F,0x00A1,0x0091,
+ 0x0089,0x0085,0x0083,0xDAF8,0xDF8E,0x0083,0xDAF5,0xDF8D,0x0085,0x0083,0xE7DF,0xDF8C,0x0083,0xB5CB,0xDF8B,0x0089,
+ 0x0085,0x0083,0xD2D8,0xDF8A,0x0083,0xDF89,0xDF88,0x0085,0x0083,0xDF87,0xDF86,0x0083,0xE5E5,0xDF85,0x0091,0x0089,
+ 0x0085,0x0083,0xDF84,0xE5E3,0x0083,0xDF83,0xDF82,0x0085,0x0083,0xDF81,0xDF80,0x0083,0xE5E4,0xE5E2,0x0089,0x0085,
+ 0x0083,0xDF7E,0xD1FB,0x0083,0xB1DC,0xDF7D,0x0085,0x0083,0xE5E1,0xDF7C,0x0083,0xDF7B,0xDF7A,0x00C1,0x00A1,0x0091,
+ 0x0089,0x0085,0x0083,0xDF79,0xDF78,0x0083,0xDF77,0xDF76,0x0085,0x0083,0xD7F1,0xE5E0,0x0083,0xDF75,0xDF74,0x0089,
+ 0x0085,0x0083,0xDF73,0xDF72,0x0083,0xDF71,0xD5DA,0x0085,0x0083,0xD4E2,0xDF70,0x0083,0xDF6F,0xDF6E,0x0091,0x0089,
+ 0x0085,0x0083,0xDF6D,0xE5DB,0x0083,0xDF6C,0xDF6B,0x0085,0x0083,0xD2A3,0xDF6A,0x0083,0xC7B2,0xE5DD,0x0089,0x0085,
+ 0x0083,0xDF69,0xDF68,0x0083,0xDF67,0xDF66,0x0085,0x0083,0xDF65,0xDF64,0x0083,0xE5DE,0xDF63,0x00A1,0x0091,0x0089,
+ 0x0085,0x0083,0xDF62,0xE5DC,0x0083,0xD2C5,0xDF61,0x0085,0x0083,0xDF60,0xDF5F,0x0083,0xB5C0,0xE5D9,0x0089,0x0085,
+ 0x0083,0xE5D8,0xE5DA,0x0083,0xB6F4,0xDF5E,0x0085,0x0083,0xB1E9,0xDF5D,0x0083,0xDF5C,0xDF5B,0x0091,0x0089,0x0085,
+ 0x0083,0xDF5A,0xDF59,0x0083,0xD3F6,0xDF58,0x0085,0x0083,0xDF57,0xE5D7,0x0083,0xDF56,0xCBEC,0x0089,0x0085,0x0083,
+ 0xB6DD,0xDF55,0x0083,0xDF54,0xD3E2,0x0085,0x0083,0xDF53,0xB1C6,0x0083,0xC2DF,0xDF52,0x1075,0x087B,0x047D,0x027F,
+ 0x017F,0x00FF,0x00BF,0x009F,0x008F,0x0087,0x0083,0xDF51,0x0083,0xD2DD,0xDF50,0x0085,0x0083,0xE5D4,0xE5D3,0x0083,
+ 0xDF4F,0xDF4E,0x0089,0x0085,0x0083,0xDF4D,0xDF4C,0x0083,0xDF4B,0xE5D6,0x0085,0x0083,0xB4FE,0xE5D5,0x0083,0xDF4A,
+ 0xDF49,0x0091,0x0089,0x0085,0x0083,0xDF48,0xDF47,0x0083,0xDF46,0xDF45,0x0085,0x0083,0xE5CE,0xDF44,0x0083,0xDF43,
+ 0xDF42,0x0089,0x0085,0x0083,0xB7EA,0xE5D2,0x0083,0xD4EC,0xCBD9,0x0085,0x0083,0xB3D1,0xCAC5,0x0083,0xDF41,0xB9E4,
+ 0x00A1,0x0091,0x0089,0x0085,0x0083,0xCDA8,0xDF40,0x0083,0xDEA0,0xB6BA,0x0085,0x0083,0xE5D1,0xDE9F,0x0083,0xCDBE,
+ 0xDE9E,0x0089,0x0085,0x0083,0xB5DD,0xE5CF,0x0083,0xD6F0,0xCDB8,0x0085,0x0083,0xDE9D,0xE5D0,0x0083,0xDE9C,0xE5CD,
+ 0x0091,0x0089,0x0085,0x0083,0xD1B7,0xD1A1,0x0083,0xDE9B,0xDE9A,0x0085,0x0083,0xC4E6,0xE5CB,0x0083,0xE5CC,0xCCD3,
+ 0x0089,0x0085,0x0083,0xCACA,0xCBCD,0x0083,0xCDCB,0xDE99,0x0085,0x0083,0xDE98,0xD7B7,0x0083,0xDE97,0xDE96,0x00C1,
+ 0x00A1,0x0091,0x0089,0x0085,0x0083,0xDE95,0xBCA3,0x0083,0xB1C5,0xC3D4,0x0085,0x0083,0xDE94,0xDE93,0x0083,0xDE92,
+ 0xE5C9,0x0089,0x0085,0x0083,0xDE91,0xDE90,0x0083,0xCAF6,0xDE8F,0x0085,0x0083,0xE5C5,0xB5FC,0x0083,0xDE8E,0xC6C8,
+ 0x0091,0x0089,0x0085,0x0083,0xB5CF,0xE5C7,0x0083,0xE5CA,0xDE8D,0x0085,0x0083,0xE5C8,0xE5C4,0x0083,0xE5C6,0xDE8C,
+ 0x0089,0x0085,0x0083,0xCCF6,0xDE8B,0x0083,0xDE8A,0xB3D9,0x0085,0x0083,0xC1AC,0xCEA5,0x0083,0xD4B6,0xBDF8,0x00A1,
+ 0x0091,0x0089,0x0085,0x0083,0xDE89,0xD5E2,0x0083,0xBBB9,0xDE88,0x0085,0x0083,0xDE87,0xE5C3,0x0083,0xB7B5,0xE5C2,
+ 0x0089,0x0085,0x0083,0xDE86,0xBDFC,0x0083,0xD4CB,0xDE85,0x0085,0x0083,0xD3AD,0xDE84,0x0083,0xDE83,0xDE82,0x0091,
+ 0x0089,0x0085,0x0083,0xDE81,0xDE80,0x0083,0xC2F5,0xB9FD,0x0085,0x0083,0xDE7E,0xD1B8,0x0083,0xC6F9,0xDE7D,0x0089,
+ 0x0085,0x0083,0xD3D8,0xC7A8,0x0083,0xDE7C,0xDE7B,0x0085,0x0083,0xB4EF,0xC1C9,0x0083,0xDE7A,0xDE79,0x0101,0x00C1,
+ 0x00A1,0x0091,0x0089,0x0085,0x0083,0xDE78,0xB1DF,0x0083,0xDE77,0xDE76,0x0085,0x0083,0xE5C1,0xDE75,0x0083,0xDE74,
+ 0xDE73,0x0089,0x0085,0x0083,0xDE72,0xC8E8,0x0083,0xB3BD,0xDE71,0x0085,0x0083,0xDE70,0xDE6F,0x0083,0xDE6E,0xB1E8,
+ 0x0091,0x0089,0x0085,0x0083,0xDE6D,0xB1E7,0x0083,0xB1E6,0xDE6C,0x0085,0x0083,0xDE6B,0xDE6A,0x0083,0xDE69,0xC0B1,
+ 0x0089,0x0085,0x0083,0xDE68,0xDE67,0x0083,0xDE66,0xB1D9,0x0085,0x0083,0xB4C7,0xDE65,0x0083,0xB9BC,0xD0C1,0x00A1,
+ 0x0091,0x0089,0x0085,0x0083,0xEAA5,0xD5DE,0x0083,0xEAA4,0xD5B7,0x0085,0x0083,0xCFBD,0xD4AF,0x0083,0xE0CE,0xCAE4,
+ 0x0089,0x0085,0x0083,0xDE64,0xBCAD,0x0083,0xB7F8,0xEAA3,0x0085,0x0083,0xEAA2,0xEAA1,0x0083,0xDE63,0xE9FE,0x0091,
+ 0x0089,0x0085,0x0083,0xB9F5,0xBBD4,0x0083,0xB1B2,0xE9FD,0x0085,0x0083,0xC1BE,0xB8A8,0x0083,0xE9FC,0xBDCF,0x0089,
+ 0x0085,0x0083,0xE9FB,0xE9FA,0x0083,0xDE62,0xBDCE,0x0085,0x0083,0xE9F9,0xD4D8,0x0083,0xE9F8,0xC7E1,0x00C1,0x00A1,
+ 0x0091,0x0089,0x0085,0x0083,0xE9F7,0xE9F6,0x0083,0xE9F4,0xE9F5,0x0085,0x0083,0xE9F3,0xE9F2,0x0083,0xD6E1,0xE9F1,
+ 0x0089,0x0085,0x0083,0xE9F0,0xE9EF,0x0083,0xBAE4,0xC8ED,0x0085,0x0083,0xC2D6,0xE9EE,0x0083,0xD7AA,0xE9ED,0x0091,
+ 0x0089,0x0085,0x0083,0xDE61,0xD0F9,0x0083,0xB9EC,0xD4FE,0x0085,0x0083,0xB3B5,0xDE60,0x0083,0xDE5F,0xDE5E,0x0089,
+ 0x0085,0x0083,0xDE5D,0xDE5C,0x0083,0xDE5B,0xDE5A,0x0085,0x0083,0xDE59,0xDE58,0x0083,0xDE57,0xDE56,0x00A1,0x0091,
+ 0x0089,0x0085,0x0083,0xDE55,0xDE54,0x0083,0xDE53,0xDE52,0x0085,0x0083,0xDE51,0xDE50,0x0083,0xDE4F,0xDE4E,0x0089,
+ 0x0085,0x0083,0xDE4D,0xDE4C,0x0083,0xDE4B,0xDE4A,0x0085,0x0083,0xDE49,0xDE48,0x0083,0xDE47,0xDE46,0x0091,0x0089,
+ 0x0085,0x0083,0xDE45,0xDE44,0x0083,0xDE43,0xDE42,0x0085,0x0083,0xDE41,0xDE40,0x0083,0xDDA0,0xDD9F,0x0089,0x0085,
+ 0x0083,0xDD9E,0xDD9D,0x0083,0xDD9C,0xDD9B,0x0085,0x0083,0xDD9A,0xDD99,0x0083,0xDD98,0xDD97,0x017F,0x00FF,0x00BF,
+ 0x009F,0x008F,0x0087,0x0083,0xDD96,0x0083,0xDD95,0xDD94,0x0085,0x0083,0xDD93,0xDD92,0x0083,0xDD91,0xDD90,0x0089,
+ 0x0085,0x0083,0xDD8F,0xDD8E,0x0083,0xDD8D,0xDD8C,0x0085,0x0083,0xDD8B,0xDD8A,0x0083,0xDD89,0xDD88,0x0091,0x0089,
+ 0x0085,0x0083,0xDD87,0xDD86,0x0083,0xDD85,0xDD84,0x0085,0x0083,0xDD83,0xDD82,0x0083,0xDD81,0xDD80,0x0089,0x0085,
+ 0x0083,0xDD7E,0xDD7D,0x0083,0xDD7C,0xDD7B,0x0085,0x0083,0xDD7A,0xDD79,0x0083,0xDD78,0xDD77,0x00A1,0x0091,0x0089,
+ 0x0085,0x0083,0xDD76,0xDD75,0x0083,0xDD74,0xDD73,0x0085,0x0083,0xDD72,0xDD71,0x0083,0xDD70,0xDD6F,0x0089,0x0085,
+ 0x0083,0xDD6E,0xDD6D,0x0083,0xDD6C,0xDD6B,0x0085,0x0083,0xDD6A,0xDD69,0x0083,0xDD68,0xDD67,0x0091,0x0089,0x0085,
+ 0x0083,0xDD66,0xDD65,0x0083,0xDD64,0xDD63,0x0085,0x0083,0xDD62,0xDD61,0x0083,0xDD60,0xDD5F,0x0089,0x0085,0x0083,
+ 0xDD5E,0xDD5D,0x0083,0xDD5C,0xDD5B,0x0085,0x0083,0xDD5A,0xDD59,0x0083,0xDD58,0xDD57,0x00C1,0x00A1,0x0091,0x0089,
+ 0x0085,0x0083,0xDD56,0xDD55,0x0083,0xDD54,0xDD53,0x0085,0x0083,0xDD52,0xDD51,0x0083,0xDD50,0xDD4F,0x0089,0x0085,
+ 0x0083,0xDD4E,0xDD4D,0x0083,0xDD4C,0xDD4B,0x0085,0x0083,0xDD4A,0xDD49,0x0083,0xDD48,0xDD47,0x0091,0x0089,0x0085,
+ 0x0083,0xDD46,0xDD45,0x0083,0xDD44,0xDD43,0x0085,0x0083,0xDD42,0xDD41,0x0083,0xDD40,0xDCA0,0x0089,0x0085,0x0083,
+ 0xDC9F,0xDC9E,0x0083,0xDC9D,0xDC9C,0x0085,0x0083,0xDC9B,0xDC9A,0x0083,0xDC99,0xDC98,0x00A1,0x0091,0x0089,0x0085,
+ 0x0083,0xDC97,0xDC96,0x0083,0xDC95,0xDC94,0x0085,0x0083,0xDC93,0xDC92,0x0083,0xDC91,0xDC90,0x0089,0x0085,0x0083,
+ 0xDC8F,0xDC8E,0x0083,0xDC8D,0xDC8C,0x0085,0x0083,0xDC8B,0xEAA6,0x0083,0xDC8A,0xDC89,0x0091,0x0089,0x0085,0x0083,
+ 0xDC88,0xDC87,0x0083,0xDC86,0xDC85,0x0085,0x0083,0xDC84,0xDC83,0x0083,0xDC82,0xDC81,0x0089,0x0085,0x0083,0xDC80,
+ 0xDC7E,0x0083,0xDC7D,0xDC7C,0x0085,0x0083,0xDC7B,0xDC7A,0x0083,0xDC79,0xDC78,0x0101,0x00C1,0x00A1,0x0091,0x0089,
+ 0x0085,0x0083,0xDC77,0xCCC9,0x0083,0xDC76,0xDC75,0x0085,0x0083,0xDC74,0xDC73,0x0083,0xDC72,0xDC71,0x0089,0x0085,
+ 0x0083,0xDC70,0xB6E3,0x0083,0xDC6F,0xDC6E,0x0085,0x0083,0xC7FB,0xDC6D,0x0083,0xDC6C,0xB9AA,0x0091,0x0089,0x0085,
+ 0x0083,0xC9ED,0xDC6B,0x0083,0xDC6A,0xDC69,0x0085,0x0083,0xDC68,0xDC67,0x0083,0xDC66,0xDC65,0x0089,0x0085,0x0083,
+ 0xDC64,0xDC63,0x0083,0xDC62,0xDC61,0x0085,0x0083,0xDC60,0xF5F3,0x0083,0xDC5F,0xF5F2,0x00A1,0x0091,0x0089,0x0085,
+ 0x0083,0xDC5E,0xDC5D,0x0083,0xDC5C,0xDC5B,0x0085,0x0083,0xDC5A,0xDC59,0x0083,0xDC58,0xF5F0,0x0089,0x0085,0x0083,
+ 0xDC57,0xDC56,0x0083,0xDC55,0xF5F1,0x0085,0x0083,0xF5EF,0xDC54,0x0083,0xDC53,0xDC52,0x0091,0x0089,0x0085,0x0083,
+ 0xDC51,0xDC50,0x0083,0xDC4F,0xDC4E,0x0085,0x0083,0xB3F9,0xDC4D,0x0083,0xF5EE,0xDC4C,0x0089,0x0085,0x0083,0xDC4B,
+ 0xDC4A,0x0083,0xD4EA,0xDC49,0x0085,0x0083,0xB4DA,0xDC48,0x0083,0xDC47,0xF5EB,0x00C1,0x00A1,0x0091,0x0089,0x0085,
+ 0x0083,0xDC46,0xDC45,0x0083,0xDC44,0xDC43,0x0085,0x0083,0xDC42,0xF5EA,0x0083,0xDC41,0xF5ED,0x0089,0x0085,0x0083,
+ 0xDC40,0xB6D7,0x0083,0xDBA0,0xF5E9,0x0085,0x0083,0xF5EC,0xDB9F,0x0083,0xB2E4,0xB5C5,0x0091,0x0089,0x0085,0x0083,
+ 0xDB9E,0xDB9D,0x0083,0xF5BF,0xDB9C,0x0085,0x0083,0xDB9B,0xB1C4,0x0083,0xDB9A,0xDB99,0x0089,0x0085,0x0083,0xDB98,
+ 0xDB97,0x0083,0xDB96,0xDB95,0x0085,0x0083,0xDB94,0xDB93,0x0083,0xDB92,0xDB91,0x00A1,0x0091,0x0089,0x0085,0x0083,
+ 0xDB90,0xDB8F,0x0083,0xF5BE,0xDB8E,0x0085,0x0083,0xDB8D,0xDB8C,0x0083,0xDB8B,0xDB8A,0x0089,0x0085,0x0083,0xDB89,
+ 0xF5E7,0x0083,0xF5E6,0xDB88,0x0085,0x0083,0xDB87,0xDB86,0x0083,0xDB85,0xDB84,0x0091,0x0089,0x0085,0x0083,0xCCA3,
+ 0xF5E8,0x0083,0xF5E3,0xB5B8,0x0085,0x0083,0xE5BF,0xDB83,0x0083,0xDB82,0xCCE3,0x0089,0x0085,0x0083,0xDB81,0xF5E5,
+ 0x0083,0xF5E4,0xF5DE,0x0085,0x0083,0xDB80,0xDB7E,0x0083,0xF5E1,0xDB7D,0x027F,0x017F,0x00FF,0x00BF,0x009F,0x008F,
+ 0x0087,0x0083,0xDB7C,0x0083,0xF5DD,0xF5DF,0x0085,0x0083,0xDB7B,0xDB7A,0x0083,0xDB79,0xF5E0,0x0089,0x0085,0x0083,
+ 0xDB78,0xDB77,0x0083,0xDB76,0xF5E2,0x0085,0x0083,0xDB75,0xF5DC,0x0083,0xF5DA,0xDB74,0x0091,0x0089,0x0085,0x0083,
+ 0xF5D9,0xDB73,0x0083,0xD7D9,0xB2C8,0x0085,0x0083,0xDB72,0xDB71,0x0083,0xDB70,0xDB6F,0x0089,0x0085,0x0083,0xDB6E,
+ 0xF5DB,0x0083,0xCCDF,0xDB6D,0x0085,0x0083,0xDB6C,0xF5D8,0x0083,0xBEE1,0xF5D7,0x00A1,0x0091,0x0089,0x0085,0x0083,
+ 0xDB6B,0xDB6A,0x0083,0xDB69,0xDB68,0x0085,0x0083,0xDB67,0xDB66,0x0083,0xDB65,0xDB64,0x0089,0x0085,0x0083,0xF5D6,
+ 0xDB63,0x0083,0xDB62,0xDB61,0x0085,0x0083,0xDB60,0xCCA4,0x0083,0xDB5F,0xDB5E,0x0091,0x0089,0x0085,0x0083,0xB3EC,
+ 0xDB5D,0x0083,0xD3BB,0xF5D4,0x0085,0x0083,0xDB5C,0xDB5B,0x0083,0xDB5A,0xF5BD,0x0089,0x0085,0x0083,0xDB59,0xDB58,
+ 0x0083,0xDB57,0xDB56,0x0085,0x0083,0xDB55,0xDB54,0x0083,0xDB53,0xF5D5,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,
+ 0xDB52,0xF5D2,0x0083,0xB6E5,0xF5D1,0x0085,0x0083,0xF5CF,0xF5CE,0x0083,0xDB51,0xBCF9,0x0089,0x0085,0x0083,0xDB50,
+ 0xCCF8,0x0083,0xDB4F,0xDB4E,0x0085,0x0083,0xDB4D,0xC2B7,0x0083,0xDB4C,0xDB4B,0x0091,0x0089,0x0085,0x0083,0xF5CD,
+ 0xF5BC,0x0083,0xB9F2,0xDB4A,0x0085,0x0083,0xBFE7,0xDB49,0x0083,0xDB48,0xDB47,0x0089,0x0085,0x0083,0xF5D3,0xF5D0,
+ 0x0083,0xDB46,0xDB45,0x0085,0x0083,0xDB44,0xB8FA,0x0083,0xF5C8,0xBEE0,0x00A1,0x0091,0x0089,0x0085,0x0083,0xDB43,
+ 0xF5CB,0x0083,0xF5C7,0xDB42,0x0085,0x0083,0xDB41,0xF5C6,0x0083,0xF5C5,0xDB40,0x0089,0x0085,0x0083,0xDAA0,0xDA9F,
+ 0x0083,0xDA9E,0xC5DC,0x0085,0x0083,0xDA9D,0xF5CA,0x0083,0xF5C9,0xDA9C,0x0091,0x0089,0x0085,0x0083,0xB5F8,0xB0CF,
+ 0x0083,0xDA9B,0xDA9A,0x0085,0x0083,0xDA99,0xDA98,0x0083,0xF5CC,0xDA97,0x0089,0x0085,0x0083,0xF5C4,0xD4BE,0x0083,
+ 0xDA96,0xDA95,0x0085,0x0083,0xDA94,0xF5C1,0x0083,0xD6BA,0xDA93,0x0101,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,
+ 0xF5C2,0xDA92,0x0083,0xF5C3,0xDA91,0x0085,0x0083,0xF5BB,0xDA90,0x0083,0xDA8F,0xF5C0,0x0089,0x0085,0x0083,0xC5BF,
+ 0xD7E3,0x0083,0xDA8E,0xF4F5,0x0085,0x0083,0xDA8D,0xDA8C,0x0083,0xDA8B,0xDA8A,0x0091,0x0089,0x0085,0x0083,0xDA89,
+ 0xDA88,0x0083,0xDA87,0xDA86,0x0085,0x0083,0xDA85,0xDA84,0x0083,0xDA83,0xDA82,0x0089,0x0085,0x0083,0xDA81,0xC8A4,
+ 0x0083,0xDA80,0xDA7E,0x0085,0x0083,0xDA7D,0xCCCB,0x0083,0xDA7C,0xDA7B,0x00A1,0x0091,0x0089,0x0085,0x0083,0xDA7A,
+ 0xDA79,0x0083,0xDA78,0xDA77,0x0085,0x0083,0xDA76,0xDA75,0x0083,0xDA74,0xDA73,0x0089,0x0085,0x0083,0xF4F3,0xDA72,
+ 0x0083,0xDA71,0xF4F4,0x0085,0x0083,0xDA70,0xDA6F,0x0083,0xDA6E,0xDA6D,0x0091,0x0089,0x0085,0x0083,0xDA6C,0xC7F7,
+ 0x0083,0xD4BD,0xDA6B,0x0085,0x0083,0xDA6A,0xDA69,0x0083,0xDA68,0xB3AC,0x0089,0x0085,0x0083,0xF4F2,0xDA67,0x0083,
+ 0xDA66,0xB3C3,0x0085,0x0083,0xDA65,0xDA64,0x0083,0xDA63,0xDA62,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0xDA61,
+ 0xDA60,0x0083,0xDA5F,0xDA5E,0x0085,0x0083,0xDA5D,0xC6F0,0x0083,0xB8CF,0xD5D4,0x0089,0x0085,0x0083,0xB8B0,0xF4F1,
+ 0x0083,0xDA5C,0xDA5B,0x0085,0x0083,0xD7DF,0xDA5A,0x0083,0xDA59,0xF4F7,0x0091,0x0089,0x0085,0x0083,0xDA58,0xBAD5,
+ 0x0083,0xDA57,0xDA56,0x0085,0x0083,0xDA55,0xF4F6,0x0083,0xC9E2,0xDA54,0x0089,0x0085,0x0083,0xB3E0,0xB8D3,0x0083,
+ 0xD3AE,0xC9C4,0x0085,0x0083,0xD4F9,0xDA53,0x0083,0xD4DE,0xD8CD,0x00A1,0x0091,0x0089,0x0085,0x0083,0xD8D3,0xC8FC,
+ 0x0083,0xD7AC,0xEAE7,0x0085,0x0083,0xD7B8,0xDA52,0x0083,0xC0B5,0xEAE6,0x0089,0x0085,0x0083,0xC5E2,0xE2D9,0x0083,
+ 0xDA51,0xDA50,0x0085,0x0083,0xB4CD,0xC9CD,0x0083,0xCAEA,0xEAE5,0x0091,0x0089,0x0085,0x0083,0xB6C4,0xB8B3,0x0083,
+ 0xC9DE,0xEAE3,0x0085,0x0083,0xEAE2,0xEAE4,0x0083,0xEAE1,0xEAE0,0x0089,0x0085,0x0083,0xD7CA,0xD4DF,0x0083,0xC2B8,
+ 0xC1DE,0x0085,0x0083,0xEADF,0xBBDF,0x0083,0xBCD6,0xEADE,0x0181,0x0101,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,
+ 0xD4F4,0xEADD,0x0083,0xBAD8,0xB7D1,0x0085,0x0083,0xC3B3,0xB4FB,0x0083,0xEADC,0xB9F3,0x0089,0x0085,0x0083,0xCCF9,
+ 0xEADB,0x0083,0xEADA,0xBCFA,0x0085,0x0083,0xB7A1,0xB9E1,0x0083,0xD6FC,0xB9BA,0x0091,0x0089,0x0085,0x0083,0xB1E1,
+ 0xC6B6,0x0083,0xCCB0,0xB7B7,0x0085,0x0083,0xD6CA,0xBBF5,0x0083,0xD5CB,0xB0DC,0x0089,0x0085,0x0083,0xCFCD,0xD4F0,
+ 0x0083,0xB2C6,0xB9B1,0x0085,0x0083,0xDA4F,0xB8BA,0x0083,0xD5EA,0xB1B4,0x00A1,0x0091,0x0089,0x0085,0x0083,0xDA4E,
+ 0xDA4D,0x0083,0xDA4C,0xDA4B,0x0085,0x0083,0xDA4A,0xDA49,0x0083,0xDA48,0xDA47,0x0089,0x0085,0x0083,0xDA46,0xDA45,
+ 0x0083,0xDA44,0xDA43,0x0085,0x0083,0xDA42,0xDA41,0x0083,0xDA40,0xD9A0,0x0091,0x0089,0x0085,0x0083,0xD99F,0xD99E,
+ 0x0083,0xD99D,0xD99C,0x0085,0x0083,0xD99B,0xD99A,0x0083,0xD999,0xD998,0x0089,0x0085,0x0083,0xD997,0xD996,0x0083,
+ 0xD995,0xD994,0x0085,0x0083,0xD993,0xD992,0x0083,0xD991,0xD990,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0xD98F,
+ 0xD98E,0x0083,0xD98D,0xD98C,0x0085,0x0083,0xD98B,0xD98A,0x0083,0xD989,0xD988,0x0089,0x0085,0x0083,0xD987,0xD986,
+ 0x0083,0xD985,0xD984,0x0085,0x0083,0xD983,0xD982,0x0083,0xD981,0xD980,0x0091,0x0089,0x0085,0x0083,0xD97E,0xD97D,
+ 0x0083,0xD97C,0xD97B,0x0085,0x0083,0xD97A,0xD979,0x0083,0xD978,0xD977,0x0089,0x0085,0x0083,0xD976,0xD975,0x0083,
+ 0xD974,0xD973,0x0085,0x0083,0xD972,0xD971,0x0083,0xD970,0xD96F,0x00A1,0x0091,0x0089,0x0085,0x0083,0xD96E,0xD96D,
+ 0x0083,0xD96C,0xD96B,0x0085,0x0083,0xD96A,0xD969,0x0083,0xD968,0xD967,0x0089,0x0085,0x0083,0xD966,0xD965,0x0083,
+ 0xD964,0xD963,0x0085,0x0083,0xD962,0xD961,0x0083,0xD960,0xD95F,0x0091,0x0089,0x0085,0x0083,0xD95E,0xD95D,0x0083,
+ 0xD95C,0xD95B,0x0085,0x0083,0xD95A,0xD959,0x0083,0xD958,0xD957,0x0089,0x0085,0x0083,0xD956,0xD955,0x0083,0xD954,
+ 0xD953,0x0085,0x0083,0xD952,0xD951,0x0083,0xD950,0xD94F,0x0101,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0xD94E,
+ 0xD94D,0x0083,0xD94C,0xD94B,0x0085,0x0083,0xD94A,0xD949,0x0083,0xD948,0xD947,0x0089,0x0085,0x0083,0xD946,0xD945,
+ 0x0083,0xD944,0xD943,0x0085,0x0083,0xD942,0xD941,0x0083,0xD940,0xD8A0,0x0091,0x0089,0x0085,0x0083,0xD89F,0xD89E,
+ 0x0083,0xD89D,0xD89C,0x0085,0x0083,0xD89B,0xD89A,0x0083,0xD899,0xD898,0x0089,0x0085,0x0083,0xD897,0xD896,0x0083,
+ 0xD895,0xD894,0x0085,0x0083,0xD893,0xD892,0x0083,0xD891,0xD890,0x00A1,0x0091,0x0089,0x0085,0x0083,0xD88F,0xD88E,
+ 0x0083,0xD88D,0xD88C,0x0085,0x0083,0xF5F8,0xD88B,0x0083,0xD88A,0xD889,0x0089,0x0085,0x0083,0xF5F9,0xD888,0x0083,
+ 0xD887,0xD886,0x0085,0x0083,0xD885,0xD884,0x0083,0xD883,0xD882,0x0091,0x0089,0x0085,0x0083,0xC3B2,0xD881,0x0083,
+ 0xF5F6,0xBAD1,0x0085,0x0083,0xD880,0xD87E,0x0083,0xD87D,0xF5F7,0x0089,0x0085,0x0083,0xD87C,0xD87B,0x0083,0xF5F5,
+ 0xD87A,0x0085,0x0083,0xD879,0xD878,0x0083,0xD877,0xD876,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0xD875,0xD874,
+ 0x0083,0xB2F2,0xB1AA,0x0085,0x0083,0xF5F4,0xD873,0x0083,0xD872,0xD871,0x0089,0x0085,0x0083,0xD870,0xE1D9,0x0083,
+ 0xD86F,0xD86E,0x0085,0x0083,0xD86D,0xD86C,0x0083,0xD86B,0xD86A,0x0091,0x0089,0x0085,0x0083,0xD869,0xD4A5,0x0083,
+ 0xBAC0,0xD868,0x0085,0x0083,0xD867,0xD866,0x0083,0xD865,0xD864,0x0089,0x0085,0x0083,0xD863,0xD862,0x0083,0xBBBF,
+ 0xCFF3,0x0085,0x0083,0xD861,0xD860,0x0083,0xD85F,0xD85E,0x00A1,0x0091,0x0089,0x0085,0x0083,0xD85D,0xD85C,0x0083,
+ 0xEBE0,0xD85B,0x0085,0x0083,0xD85A,0xD859,0x0083,0xD858,0xF5B9,0x0089,0x0085,0x0083,0xD857,0xD856,0x0083,0xD855,
+ 0xD854,0x0085,0x0083,0xD853,0xD852,0x0083,0xD851,0xD850,0x0091,0x0089,0x0085,0x0083,0xCDE3,0xD84F,0x0083,0xD84E,
+ 0xF4F9,0x0085,0x0083,0xD84D,0xF4F8,0x0083,0xB6B9,0xD84C,0x0089,0x0085,0x0083,0xD84B,0xD84A,0x0083,0xD849,0xBBED,
+ 0x0085,0x0083,0xD848,0xD847,0x0083,0xD846,0xD845,0x047D,0x027F,0x017F,0x00FF,0x00BF,0x009F,0x008F,0x0087,0x0083,
+ 0xD844,0x0083,0xD843,0xD842,0x0085,0x0083,0xD841,0xD840,0x0083,0xB9C8,0xDADF,0x0089,0x0085,0x0083,0xDADE,0xC7B4,
+ 0x0083,0xDADD,0xDADC,0x0085,0x0083,0xC6D7,0xC0BE,0x0083,0xDADB,0xDADA,0x0091,0x0089,0x0085,0x0083,0xCCB7,0xC3FD,
+ 0x0083,0xDAD9,0xDAD8,0x0085,0x0083,0xC3A1,0xBDF7,0x0083,0xDAD7,0xC7AB,0x0089,0x0085,0x0083,0xDAD6,0xB0F9,0x0083,
+ 0xD2A5,0xD0BB,0x0085,0x0083,0xDAD5,0xDAD4,0x0083,0xDAD3,0xD7A0,0x00A1,0x0091,0x0089,0x0085,0x0083,0xDAD2,0xC3D5,
+ 0x0083,0xDAD0,0xD1E8,0x0085,0x0083,0xDACF,0xDAD1,0x0083,0xB2F7,0xDACE,0x0089,0x0085,0x0083,0xDACD,0xDACC,0x0083,
+ 0xCEBD,0xDACB,0x0085,0x0083,0xDACA,0xD0B3,0x0083,0xDAC9,0xBBD1,0x0091,0x0089,0x0085,0x0083,0xB5FD,0xDAC8,0x0083,
+ 0xC4B1,0xD2EA,0x0085,0x0083,0xD79F,0xCCB8,0x0083,0xDAC7,0xD7BB,0x0089,0x0085,0x0083,0xC1C2,0xDAC6,0x0083,0xB5F7,
+ 0xDAC5,0x0085,0x0083,0xCBAD,0xDAC4,0x0083,0xDAC3,0xBFCE,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0xB7CC,0xDAC2,
+ 0x0083,0xB6C1,0xC5B5,0x0085,0x0083,0xDAC1,0xD6EE,0x0083,0xC7EB,0xDAC0,0x0089,0x0085,0x0083,0xCBD0,0xCBB5,0x0083,
+ 0xDABF,0xBBE5,0x0085,0x0083,0xD3D5,0xDABE,0x0083,0xCEF3,0xDABD,0x0091,0x0089,0x0085,0x0083,0xD3EF,0xCEDC,0x0083,
+ 0xBDEB,0xD79E,0x0085,0x0083,0xDABC,0xDABB,0x0083,0xB2EF,0xCFEA,0x0089,0x0085,0x0083,0xB8C3,0xDABA,0x0083,0xD2E8,
+ 0xD1AF,0x0085,0x0083,0xB9EE,0xDAB9,0x0083,0xDAB8,0xB5AE,0x00A1,0x0091,0x0089,0x0085,0x0083,0xBBB0,0xDAB7,0x0083,
+ 0xD6EF,0xB3CF,0x0085,0x0083,0xDAB6,0xDAB5,0x0083,0xCAAB,0xDAB4,0x0089,0x0085,0x0083,0xCAD4,0xDAB3,0x0083,0xDAB2,
+ 0xDAB1,0x0085,0x0083,0xD2EB,0xD79D,0x0083,0xDAAF,0xDAB0,0x0091,0x0089,0x0085,0x0083,0xB4CA,0xD6DF,0x0083,0xDAAE,
+ 0xD5EF,0x0085,0x0083,0xCBDF,0xD5A9,0x0083,0xD79C,0xCAB6,0x0089,0x0085,0x0083,0xD7E7,0xC6C0,0x0083,0xDAAD,0xDAAC,
+ 0x0085,0x0083,0xD6A4,0xBEF7,0x0083,0xB7C3,0xC9E8,0x0101,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0xB7ED,0xCBCF,
+ 0x0083,0xD79B,0xC2DB,0x0085,0x0083,0xB6EF,0xD0ED,0x0083,0xDAAB,0xD1C8,0x0089,0x0085,0x0083,0xDAAA,0xDAA9,0x0083,
+ 0xBBE4,0xBDB2,0x0085,0x0083,0xD79A,0xBCC7,0x0083,0xD1B6,0xD2E9,0x0091,0x0089,0x0085,0x0083,0xD1B5,0xD799,0x0083,
+ 0xC6FD,0xDAA8,0x0085,0x0083,0xC8C3,0xCCD6,0x0083,0xDAA7,0xDAA6,0x0089,0x0085,0x0083,0xBCA5,0xC8CF,0x0083,0xB8BC,
+ 0xB6A9,0x0085,0x0083,0xBCC6,0xDAA5,0x0083,0xD798,0xD797,0x00A1,0x0091,0x0089,0x0085,0x0083,0xD796,0xD795,0x0083,
+ 0xD794,0xD793,0x0085,0x0083,0xD792,0xD791,0x0083,0xD790,0xD78F,0x0089,0x0085,0x0083,0xD78E,0xD78D,0x0083,0xD78C,
+ 0xD78B,0x0085,0x0083,0xD78A,0xD789,0x0083,0xD788,0xD787,0x0091,0x0089,0x0085,0x0083,0xD786,0xD785,0x0083,0xD784,
+ 0xD783,0x0085,0x0083,0xD782,0xD781,0x0083,0xD780,0xD77E,0x0089,0x0085,0x0083,0xD77D,0xD77C,0x0083,0xD77B,0xD77A,
+ 0x0085,0x0083,0xD779,0xD778,0x0083,0xD777,0xD776,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0xD775,0xD774,0x0083,
+ 0xD773,0xD772,0x0085,0x0083,0xD771,0xD770,0x0083,0xD76F,0xD76E,0x0089,0x0085,0x0083,0xD76D,0xD76C,0x0083,0xD76B,
+ 0xD76A,0x0085,0x0083,0xD769,0xD768,0x0083,0xD767,0xD766,0x0091,0x0089,0x0085,0x0083,0xD765,0xC6A9,0x0083,0xD764,
+ 0xD763,0x0085,0x0083,0xD762,0xD761,0x0083,0xD760,0xBEAF,0x0089,0x0085,0x0083,0xD75F,0xD75E,0x0083,0xD75D,0xD75C,
+ 0x0085,0x0083,0xD75B,0xD75A,0x0083,0xD759,0xD758,0x00A1,0x0091,0x0089,0x0085,0x0083,0xD757,0xD756,0x0083,0xD755,
+ 0xD754,0x0085,0x0083,0xD753,0xD752,0x0083,0xD751,0xD750,0x0089,0x0085,0x0083,0xD74F,0xD74E,0x0083,0xD74D,0xD74C,
+ 0x0085,0x0083,0xD74B,0xD74A,0x0083,0xD749,0xD748,0x0091,0x0089,0x0085,0x0083,0xD747,0xD746,0x0083,0xD745,0xD744,
+ 0x0085,0x0083,0xD743,0xD742,0x0083,0xD741,0xD740,0x0089,0x0085,0x0083,0xD6A0,0xD69F,0x0083,0xD69E,0xD69D,0x0085,
+ 0x0083,0xD69C,0xD69B,0x0083,0xD69A,0xD699,0x017F,0x00FF,0x00BF,0x009F,0x008F,0x0087,0x0083,0xD698,0x0083,0xD697,
+ 0xD696,0x0085,0x0083,0xD695,0xD694,0x0083,0xD693,0xD692,0x0089,0x0085,0x0083,0xD691,0xD690,0x0083,0xD68F,0xD68E,
+ 0x0085,0x0083,0xD68D,0xD68C,0x0083,0xD68B,0xD68A,0x0091,0x0089,0x0085,0x0083,0xD689,0xD688,0x0083,0xD687,0xD686,
+ 0x0085,0x0083,0xD685,0xD684,0x0083,0xD683,0xD682,0x0089,0x0085,0x0083,0xF6A5,0xD681,0x0083,0xD680,0xD67E,0x0085,
+ 0x0083,0xD67D,0xD67C,0x0083,0xD67B,0xD67A,0x00A1,0x0091,0x0089,0x0085,0x0083,0xD679,0xD678,0x0083,0xD677,0xD676,
+ 0x0085,0x0083,0xD675,0xD674,0x0083,0xD673,0xD672,0x0089,0x0085,0x0083,0xD671,0xD670,0x0083,0xD66F,0xD66E,0x0085,
+ 0x0083,0xD66D,0xD66C,0x0083,0xD66B,0xD66A,0x0091,0x0089,0x0085,0x0083,0xD669,0xD668,0x0083,0xD667,0xD666,0x0085,
+ 0x0083,0xD665,0xD664,0x0083,0xD663,0xE5C0,0x0089,0x0085,0x0083,0xD662,0xD661,0x0083,0xD660,0xD65F,0x0085,0x0083,
+ 0xD65E,0xD65D,0x0083,0xD65C,0xD65B,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0xD65A,0xD659,0x0083,0xD658,0xD657,
+ 0x0085,0x0083,0xD656,0xD655,0x0083,0xD654,0xD653,0x0089,0x0085,0x0083,0xD652,0xD651,0x0083,0xD650,0xD64F,0x0085,
+ 0x0083,0xD64E,0xD64D,0x0083,0xD64C,0xD64B,0x0091,0x0089,0x0085,0x0083,0xD64A,0xD649,0x0083,0xD648,0xD647,0x0085,
+ 0x0083,0xD646,0xD645,0x0083,0xD644,0xD643,0x0089,0x0085,0x0083,0xD642,0xD641,0x0083,0xD640,0xD5A0,0x0085,0x0083,
+ 0xD59F,0xD59E,0x0083,0xD59D,0xD59C,0x00A1,0x0091,0x0089,0x0085,0x0083,0xD59B,0xD59A,0x0083,0xD599,0xD598,0x0085,
+ 0x0083,0xD597,0xD596,0x0083,0xD595,0xD594,0x0089,0x0085,0x0083,0xD593,0xD592,0x0083,0xD591,0xD590,0x0085,0x0083,
+ 0xD58F,0xD58E,0x0083,0xD58D,0xD58C,0x0091,0x0089,0x0085,0x0083,0xD58B,0xD58A,0x0083,0xD589,0xD588,0x0085,0x0083,
+ 0xD587,0xD586,0x0083,0xD585,0xD584,0x0089,0x0085,0x0083,0xD583,0xD582,0x0083,0xD581,0xD580,0x0085,0x0083,0xD57E,
+ 0xD57D,0x0083,0xD57C,0xD57B,0x0101,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0xD57A,0xD579,0x0083,0xD578,0xD577,
+ 0x0085,0x0083,0xD576,0xD575,0x0083,0xD574,0xD573,0x0089,0x0085,0x0083,0xD572,0xD571,0x0083,0xD570,0xD56F,0x0085,
+ 0x0083,0xD56E,0xD56D,0x0083,0xD56C,0xD56B,0x0091,0x0089,0x0085,0x0083,0xD56A,0xD569,0x0083,0xD568,0xD567,0x0085,
+ 0x0083,0xD566,0xD565,0x0083,0xD564,0xD563,0x0089,0x0085,0x0083,0xD562,0xD561,0x0083,0xD560,0xD55F,0x0085,0x0083,
+ 0xD55E,0xD55D,0x0083,0xD55C,0xD55B,0x00A1,0x0091,0x0089,0x0085,0x0083,0xD55A,0xD559,0x0083,0xD558,0xD557,0x0085,
+ 0x0083,0xD556,0xD555,0x0083,0xD554,0xD553,0x0089,0x0085,0x0083,0xD552,0xD551,0x0083,0xD550,0xCAC4,0x0085,0x0083,
+ 0xD54F,0xD54E,0x0083,0xD54D,0xD54C,0x0091,0x0089,0x0085,0x0083,0xD54B,0xD54A,0x0083,0xD549,0xD548,0x0085,0x0083,
+ 0xCCDC,0xD3FE,0x0083,0xD547,0xD546,0x0089,0x0085,0x0083,0xD545,0xD544,0x0083,0xD543,0xD542,0x0085,0x0083,0xD541,
+ 0xD540,0x0083,0xD4A0,0xD49F,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0xD49E,0xD49D,0x0083,0xD49C,0xD49B,0x0085,
+ 0x0083,0xD49A,0xD5B2,0x0083,0xD499,0xD498,0x0089,0x0085,0x0083,0xD497,0xD496,0x0083,0xD495,0xD494,0x0085,0x0083,
+ 0xD493,0xD492,0x0083,0xD491,0xD490,0x0091,0x0089,0x0085,0x0083,0xD48F,0xD48E,0x0083,0xD48D,0xD48C,0x0085,0x0083,
+ 0xD48B,0xD48A,0x0083,0xD489,0xD488,0x0089,0x0085,0x0083,0xD487,0xD486,0x0083,0xD485,0xD484,0x0085,0x0083,0xD483,
+ 0xD482,0x0083,0xD481,0xD480,0x00A1,0x0091,0x0089,0x0085,0x0083,0xD47E,0xD47D,0x0083,0xD47C,0xD47B,0x0085,0x0083,
+ 0xD47A,0xD479,0x0083,0xD478,0xD477,0x0089,0x0085,0x0083,0xD476,0xD475,0x0083,0xD474,0xD473,0x0085,0x0083,0xD472,
+ 0xD471,0x0083,0xD470,0xD46F,0x0091,0x0089,0x0085,0x0083,0xD46E,0xD46D,0x0083,0xD46C,0xD46B,0x0085,0x0083,0xD46A,
+ 0xD469,0x0083,0xEEBA,0xD468,0x0089,0x0085,0x0083,0xD467,0xD466,0x0083,0xD465,0xD464,0x0085,0x0083,0xD463,0xD462,
+ 0x0083,0xD461,0xD460,0x027F,0x017F,0x00FF,0x00BF,0x009F,0x008F,0x0087,0x0083,0xF6A4,0x0083,0xD45F,0xD45E,0x0085,
+ 0x0083,0xD45D,0xD45C,0x0083,0xD45B,0xD45A,0x0089,0x0085,0x0083,0xD459,0xD458,0x0083,0xD457,0xD456,0x0085,0x0083,
+ 0xD455,0xD454,0x0083,0xD453,0xD452,0x0091,0x0089,0x0085,0x0083,0xD451,0xD450,0x0083,0xD44F,0xD44E,0x0085,0x0083,
+ 0xD44D,0xD44C,0x0083,0xD44B,0xD44A,0x0089,0x0085,0x0083,0xD449,0xD448,0x0083,0xD447,0xD446,0x0085,0x0083,0xD445,
+ 0xD444,0x0083,0xD443,0xD442,0x00A1,0x0091,0x0089,0x0085,0x0083,0xD441,0xD440,0x0083,0xD3A0,0xD39F,0x0085,0x0083,
+ 0xD39E,0xD39D,0x0083,0xD39C,0xD39B,0x0089,0x0085,0x0083,0xD39A,0xD399,0x0083,0xD398,0xD397,0x0085,0x0083,0xD396,
+ 0xD395,0x0083,0xD394,0xD393,0x0091,0x0089,0x0085,0x0083,0xD392,0xD391,0x0083,0xD390,0xD38F,0x0085,0x0083,0xD38E,
+ 0xD38D,0x0083,0xD38C,0xD38B,0x0089,0x0085,0x0083,0xD9EA,0xD38A,0x0083,0xD389,0xD388,0x0085,0x0083,0xD387,0xD386,
+ 0x0083,0xD385,0xD1D4,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0xD384,0xD383,0x0083,0xD382,0xD381,0x0085,0x0083,
+ 0xD380,0xD37E,0x0083,0xD37D,0xD37C,0x0089,0x0085,0x0083,0xD37B,0xD37A,0x0083,0xD379,0xD378,0x0085,0x0083,0xECB2,
+ 0xD377,0x0083,0xD376,0xD375,0x0091,0x0089,0x0085,0x0083,0xF6A3,0xD374,0x0083,0xD373,0xD372,0x0085,0x0083,0xF6A2,
+ 0xD371,0x0083,0xD370,0xD36F,0x0089,0x0085,0x0083,0xD36E,0xB4A5,0x0083,0xF6A1,0xD36D,0x0085,0x0083,0xBDE2,0xD36C,
+ 0x0083,0xD36B,0xD36A,0x00A1,0x0091,0x0089,0x0085,0x0083,0xD369,0xF5FC,0x0083,0xD368,0xF5FE,0x0085,0x0083,0xD367,
+ 0xF5FD,0x0083,0xD366,0xD365,0x0089,0x0085,0x0083,0xD364,0xF5FB,0x0083,0xD363,0xD362,0x0085,0x0083,0xD361,0xBDC7,
+ 0x0083,0xEAEF,0xEAEE,0x0091,0x0089,0x0085,0x0083,0xEAED,0xEAEC,0x0083,0xD360,0xEAEB,0x0085,0x0083,0xEAEA,0xEAE9,
+ 0x0083,0xBEF5,0xC0C0,0x0089,0x0085,0x0083,0xEAE8,0xCAD3,0x0083,0xC3D9,0xB9E6,0x0085,0x0083,0xD35F,0xB9DB,0x0083,
+ 0xBCFB,0xD35E,0x0101,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0xD35D,0xD35C,0x0083,0xD35B,0xD35A,0x0085,0x0083,
+ 0xD359,0xD358,0x0083,0xD357,0xD356,0x0089,0x0085,0x0083,0xD355,0xD354,0x0083,0xD353,0xD352,0x0085,0x0083,0xD351,
+ 0xD350,0x0083,0xD34F,0xD34E,0x0091,0x0089,0x0085,0x0083,0xD34D,0xD34C,0x0083,0xD34B,0xD34A,0x0085,0x0083,0xD349,
+ 0xD348,0x0083,0xD347,0xD346,0x0089,0x0085,0x0083,0xD345,0xD344,0x0083,0xD343,0xD342,0x0085,0x0083,0xD341,0xD340,
+ 0x0083,0xD2A0,0xD29F,0x00A1,0x0091,0x0089,0x0085,0x0083,0xD29E,0xD29D,0x0083,0xD29C,0xD29B,0x0085,0x0083,0xD29A,
+ 0xD299,0x0083,0xD298,0xD297,0x0089,0x0085,0x0083,0xD296,0xD295,0x0083,0xD294,0xD293,0x0085,0x0083,0xD292,0xD291,
+ 0x0083,0xD290,0xD28F,0x0091,0x0089,0x0085,0x0083,0xD28E,0xD28D,0x0083,0xD28C,0xD28B,0x0085,0x0083,0xD28A,0xD289,
+ 0x0083,0xD288,0xD287,0x0089,0x0085,0x0083,0xD286,0xB8B2,0x0083,0xD285,0xD284,0x0085,0x0083,0xF1FB,0xD283,0x0083,
+ 0xD2AA,0xD282,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0xCEF7,0xD281,0x0083,0xD280,0xD27E,0x0085,0x0083,0xF1E1,
+ 0xD27D,0x0083,0xD27C,0xD27B,0x0089,0x0085,0x0083,0xD27A,0xD279,0x0083,0xD278,0xD277,0x0085,0x0083,0xD276,0xD275,
+ 0x0083,0xD274,0xD273,0x0091,0x0089,0x0085,0x0083,0xD272,0xD271,0x0083,0xD270,0xD26F,0x0085,0x0083,0xD26E,0xD26D,
+ 0x0083,0xD26C,0xD26B,0x0089,0x0085,0x0083,0xD26A,0xF1E0,0x0083,0xD269,0xD268,0x0085,0x0083,0xD267,0xD266,0x0083,
+ 0xD265,0xD264,0x00A1,0x0091,0x0089,0x0085,0x0083,0xBDF3,0xF4C5,0x0083,0xD263,0xD262,0x0085,0x0083,0xD261,0xD260,
+ 0x0083,0xD25F,0xD25E,0x0089,0x0085,0x0083,0xD25D,0xD25C,0x0083,0xD25B,0xD25A,0x0085,0x0083,0xD259,0xD258,0x0083,
+ 0xD257,0xD256,0x0091,0x0089,0x0085,0x0083,0xD255,0xD254,0x0083,0xD253,0xD252,0x0085,0x0083,0xD251,0xD250,0x0083,
+ 0xD24F,0xD24E,0x0089,0x0085,0x0083,0xD24D,0xD24C,0x0083,0xD24B,0xCFE5,0x0085,0x0083,0xD24A,0xD249,0x0083,0xF1DF,
+ 0xD248,0x0181,0x0101,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0xD247,0xD246,0x0083,0xD245,0xD244,0x0085,0x0083,
+ 0xD243,0xD242,0x0083,0xD241,0xD240,0x0089,0x0085,0x0083,0xD1A0,0xF1DE,0x0083,0xD19F,0xF1DC,0x0085,0x0083,0xD19E,
+ 0xD19D,0x0083,0xD19C,0xE5BD,0x0091,0x0089,0x0085,0x0083,0xD19B,0xD19A,0x0083,0xD199,0xD198,0x0085,0x0083,0xF1DD,
+ 0xCDCA,0x0083,0xD197,0xD196,0x0089,0x0085,0x0083,0xD195,0xD194,0x0083,0xC8EC,0xD193,0x0085,0x0083,0xD192,0xD191,
+ 0x0083,0xF1D7,0xD190,0x00A1,0x0091,0x0089,0x0085,0x0083,0xD18F,0xD18E,0x0083,0xD18D,0xD18C,0x0085,0x0083,0xF1DA,
+ 0xF1D2,0x0083,0xF1D8,0xD18B,0x0089,0x0085,0x0083,0xD18A,0xD189,0x0083,0xD188,0xD187,0x0085,0x0083,0xF1D9,0xB0FD,
+ 0x0083,0xD186,0xBAD6,0x0091,0x0089,0x0085,0x0083,0xD185,0xD184,0x0083,0xD183,0xD182,0x0085,0x0083,0xD181,0xF1DB,
+ 0x0083,0xD180,0xD17E,0x0089,0x0085,0x0083,0xD17D,0xD17C,0x0083,0xD17B,0xD17A,0x0085,0x0083,0xD179,0xB9D3,0x0083,
+ 0xD178,0xD177,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0xD176,0xF1D5,0x0083,0xD175,0xF1D3,0x0085,0x0083,0xD174,
+ 0xD173,0x0083,0xB9FC,0xC2E3,0x0089,0x0085,0x0083,0xD172,0xD171,0x0083,0xD170,0xC5E1,0x0085,0x0083,0xC9D1,0xD16F,
+ 0x0083,0xF1D1,0xF1D6,0x0091,0x0089,0x0085,0x0083,0xD16E,0xD16D,0x0083,0xD16C,0xD16B,0x0085,0x0083,0xD16A,0xD169,
+ 0x0083,0xD168,0xF1D4,0x0089,0x0085,0x0083,0xD167,0xD166,0x0083,0xF1D0,0xBFE3,0x0085,0x0083,0xF1CF,0xF1CD,0x0083,
+ 0xD165,0xD164,0x00A1,0x0091,0x0089,0x0085,0x0083,0xF4C4,0xD163,0x0083,0xD162,0xD161,0x0085,0x0083,0xD160,0xD15F,
+ 0x0083,0xC8B9,0xF4C3,0x0089,0x0085,0x0083,0xD15E,0xD15D,0x0083,0xD4A3,0xD2E1,0x0085,0x0083,0xD15C,0xD9F6,0x0083,
+ 0xD15B,0xD15A,0x0091,0x0089,0x0085,0x0083,0xD159,0xF1CE,0x0083,0xD158,0xD157,0x0085,0x0083,0xD156,0xD155,0x0083,
+ 0xF1CC,0xD154,0x0089,0x0085,0x0083,0xD153,0xF1C9,0x0083,0xD7B0,0xD152,0x0085,0x0083,0xD151,0xC1D1,0x0083,0xB2C3,
+ 0xD150,0x0101,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0xD14F,0xD14E,0x0083,0xD14D,0xF1CB,0x0085,0x0083,0xD14C,
+ 0xD14B,0x0083,0xD14A,0xD149,0x0089,0x0085,0x0083,0xF1CA,0xD148,0x0083,0xD147,0xD146,0x0085,0x0083,0xD145,0xD144,
+ 0x0083,0xB8A4,0xD143,0x0091,0x0089,0x0085,0x0083,0xD142,0xD141,0x0083,0xCFAE,0xD140,0x0085,0x0083,0xB1BB,0xD0A0,
+ 0x0083,0xD09F,0xD09E,0x0089,0x0085,0x0083,0xD09D,0xD09C,0x0083,0xD09B,0xD9F3,0x0085,0x0083,0xD09A,0xF1C8,0x0083,
+ 0xD099,0xD098,0x00A1,0x0091,0x0089,0x0085,0x0083,0xD097,0xD096,0x0083,0xD095,0xCDE0,0x0085,0x0083,0xD094,0xD093,
+ 0x0083,0xD092,0xD091,0x0089,0x0085,0x0083,0xD090,0xD0E4,0x0083,0xD08F,0xD08E,0x0085,0x0083,0xD08D,0xCCBB,0x0083,
+ 0xD08C,0xD08B,0x0091,0x0089,0x0085,0x0083,0xD08A,0xD089,0x0083,0xC5DB,0xD088,0x0085,0x0083,0xB4FC,0xD087,0x0083,
+ 0xD086,0xF4C2,0x0089,0x0085,0x0083,0xD085,0xD084,0x0083,0xF4C1,0xB0C0,0x0085,0x0083,0xD083,0xF1C7,0x0083,0xD4AC,
+ 0xD082,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0xF1C6,0xF4C0,0x0083,0xF1C5,0xD081,0x0085,0x0083,0xD080,0xD07E,
+ 0x0083,0xD07D,0xD07C,0x0089,0x0085,0x0083,0xD6D4,0xD07B,0x0083,0xD07A,0xD079,0x0085,0x0083,0xD078,0xF1C4,0x0083,
+ 0xD077,0xCBA5,0x0091,0x0089,0x0085,0x0083,0xD076,0xD9F2,0x0083,0xD075,0xB3C4,0x0085,0x0083,0xC9C0,0xD074,0x0083,
+ 0xF1C3,0xB1ED,0x0089,0x0085,0x0083,0xD073,0xD072,0x0083,0xB2B9,0xF1C2,0x0085,0x0083,0xD2C2,0xE1E9,0x0083,0xBAE2,
+ 0xD071,0x00A1,0x0091,0x0089,0x0085,0x0083,0xD070,0xD06F,0x0083,0xD06E,0xD06D,0x0085,0x0083,0xD06C,0xD06B,0x0083,
+ 0xD1C3,0xD06A,0x0089,0x0085,0x0083,0xBDD6,0xD069,0x0083,0xD068,0xCFCE,0x0085,0x0083,0xD067,0xD066,0x0083,0xD065,
+ 0xD064,0x0091,0x0089,0x0085,0x0083,0xD063,0xD062,0x0083,0xD1DC,0xD0D0,0x0085,0x0083,0xD061,0xD060,0x0083,0xD05F,
+ 0xD05E,0x0089,0x0085,0x0083,0xD05D,0xD05C,0x0083,0xD0C6,0xF4AC,0x0085,0x0083,0xD05B,0xD05A,0x0083,0xD059,0xD1AA,
+ 0x087B,0x047D,0x027F,0x017F,0x00FF,0x00BF,0x009F,0x008F,0x0087,0x0083,0xD058,0x0083,0xD057,0xD056,0x0085,0x0083,
+ 0xF3BD,0xD055,0x0083,0xD054,0xF3BC,0x0089,0x0085,0x0083,0xD053,0xD052,0x0083,0xD051,0xD050,0x0085,0x0083,0xD04F,
+ 0xD04E,0x0083,0xEEC3,0xD04D,0x0091,0x0089,0x0085,0x0083,0xD04C,0xD04B,0x0083,0xD04A,0xD049,0x0085,0x0083,0xD048,
+ 0xD047,0x0083,0xD046,0xD045,0x0089,0x0085,0x0083,0xD044,0xD043,0x0083,0xD042,0xD041,0x0085,0x0083,0xD040,0xCFA0,
+ 0x0083,0xB4C0,0xF3BB,0x00A1,0x0091,0x0089,0x0085,0x0083,0xCF9F,0xCF9E,0x0083,0xCF9D,0xCF9C,0x0085,0x0083,0xCF9B,
+ 0xF3BA,0x0083,0xCF9A,0xCF99,0x0089,0x0085,0x0083,0xCF98,0xCF97,0x0083,0xF3B6,0xC8E4,0x0085,0x0083,0xCF96,0xF3B7,
+ 0x0083,0xCF95,0xCF94,0x0091,0x0089,0x0085,0x0083,0xCF93,0xCF92,0x0083,0xCF91,0xCF90,0x0085,0x0083,0xCF8F,0xCF8E,
+ 0x0083,0xF3B9,0xCF8D,0x0089,0x0085,0x0083,0xCF8C,0xCF8B,0x0083,0xCF8A,0xCF89,0x0085,0x0083,0xCF88,0xD9F9,0x0083,
+ 0xCF87,0xCF86,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0xCF85,0xCF84,0x0083,0xF3B8,0xCF83,0x0085,0x0083,0xCF82,
+ 0xCF81,0x0083,0xCF80,0xD0B7,0x0089,0x0085,0x0083,0xCF7E,0xCF7D,0x0083,0xCF7C,0xCF7B,0x0085,0x0083,0xCF7A,0xCF79,
+ 0x0083,0xCF78,0xCF77,0x0091,0x0089,0x0085,0x0083,0xCF76,0xCF75,0x0083,0xF3B5,0xCF74,0x0085,0x0083,0xCF73,0xCF72,
+ 0x0083,0xF3B3,0xCF71,0x0089,0x0085,0x0083,0xCF70,0xCF6F,0x0083,0xCF6E,0xF3A8,0x0085,0x0083,0xCF6D,0xCF6C,0x0083,
+ 0xCF6B,0xCF6A,0x00A1,0x0091,0x0089,0x0085,0x0083,0xF3B4,0xCF69,0x0083,0xCF68,0xCF67,0x0085,0x0083,0xCF66,0xF3B2,
+ 0x0083,0xCF65,0xCF64,0x0089,0x0085,0x0083,0xCF63,0xCF62,0x0083,0xCF61,0xCF60,0x0085,0x0083,0xCF5F,0xF3AD,0x0083,
+ 0xF2FE,0xF3AF,0x0091,0x0089,0x0085,0x0083,0xCF5E,0xCF5D,0x0083,0xCF5C,0xCF5B,0x0085,0x0083,0xCF5A,0xF3AC,0x0083,
+ 0xF3B1,0xCF59,0x0089,0x0085,0x0083,0xCF58,0xCF57,0x0083,0xF3A1,0xCF56,0x0085,0x0083,0xCF55,0xCF54,0x0083,0xCF53,
+ 0xCF52,0x0101,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0xF3B0,0xCF51,0x0083,0xCF50,0xF3AE,0x0085,0x0083,0xCF4F,
+ 0xCF4E,0x0083,0xC2DD,0xCF4D,0x0089,0x0085,0x0083,0xCF4C,0xCF4B,0x0083,0xCF4A,0xF3AA,0x0085,0x0083,0xCF49,0xF3AB,
+ 0x0083,0xCF48,0xCF47,0x0091,0x0089,0x0085,0x0083,0xCF46,0xF2FC,0x0083,0xCF45,0xF3A4,0x0085,0x0083,0xF3A9,0xF3A7,
+ 0x0083,0xCF44,0xCF43,0x0089,0x0085,0x0083,0xF2FD,0xCF42,0x0083,0xCF41,0xCF40,0x0085,0x0083,0xCEA0,0xCE9F,0x0083,
+ 0xCE9E,0xCE9D,0x00A1,0x0091,0x0089,0x0085,0x0083,0xCE9C,0xC3F8,0x0083,0xCE9B,0xCE9A,0x0085,0x0083,0xCE99,0xCE98,
+ 0x0083,0xCE97,0xCE96,0x0089,0x0085,0x0083,0xCE95,0xF3A5,0x0083,0xCE94,0xCE93,0x0085,0x0083,0xCE92,0xF2FB,0x0083,
+ 0xCE91,0xCE90,0x0091,0x0089,0x0085,0x0083,0xCE8F,0xCE8E,0x0083,0xCE8D,0xC8DA,0x0085,0x0083,0xCE8C,0xF2F4,0x0083,
+ 0xCE8B,0xCE8A,0x0089,0x0085,0x0083,0xF3A2,0xCE89,0x0083,0xCE88,0xF3A3,0x0085,0x0083,0xCE87,0xF3A6,0x0083,0xF2EB,
+ 0xCE86,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0xCE85,0xCE84,0x0083,0xF2EE,0xF2ED,0x0085,0x0083,0xF2F7,0xF2EF,
+ 0x0083,0xCE83,0xCE82,0x0089,0x0085,0x0083,0xCE81,0xCE80,0x0083,0xB5FB,0xCE7E,0x0085,0x0083,0xBAFB,0xCE7D,0x0083,
+ 0xCE7C,0xCE7B,0x0091,0x0089,0x0085,0x0083,0xF2F1,0xCE7A,0x0083,0xF2F3,0xCE79,0x0085,0x0083,0xCE78,0xCE77,0x0083,
+ 0xCE76,0xCE75,0x0089,0x0085,0x0083,0xCE74,0xCE73,0x0083,0xCE72,0xF2FA,0x0085,0x0083,0xF2F8,0xF2F6,0x0083,0xCE71,
+ 0xCE70,0x00A1,0x0091,0x0089,0x0085,0x0083,0xF2F0,0xCE6F,0x0083,0xCE6E,0xCE6D,0x0085,0x0083,0xCE6C,0xCE6B,0x0083,
+ 0xCE6A,0xF2F9,0x0089,0x0085,0x0083,0xCE69,0xBBC8,0x0083,0xCE68,0xCE67,0x0085,0x0083,0xCE66,0xF2F5,0x0083,0xCE65,
+ 0xCE64,0x0091,0x0089,0x0085,0x0083,0xCE63,0xCE62,0x0083,0xD0AB,0xCE61,0x0085,0x0083,0xF2F2,0xCE60,0x0083,0xCE5F,
+ 0xB2F5,0x0089,0x0085,0x0083,0xF2E5,0xD3AC,0x0083,0xCE5E,0xCE5D,0x0085,0x0083,0xCE5C,0xCE5B,0x0083,0xCE5A,0xCE59,
+ 0x017F,0x00FF,0x00BF,0x009F,0x008F,0x0087,0x0083,0xCE58,0x0083,0xF2EA,0xF2E4,0x0085,0x0083,0xCE57,0xCE56,0x0083,
+ 0xF2DF,0xCE55,0x0089,0x0085,0x0083,0xCE54,0xCE53,0x0083,0xF2E9,0xCE52,0x0085,0x0083,0xCE51,0xF2E6,0x0083,0xCE50,
+ 0xCE4F,0x0091,0x0089,0x0085,0x0083,0xF2E7,0xCE4E,0x0083,0xCE4D,0xF2E2,0x0085,0x0083,0xCE4C,0xCE4B,0x0083,0xCE4A,
+ 0xCE49,0x0089,0x0085,0x0083,0xF2E8,0xCE48,0x0083,0xCE47,0xCE46,0x0085,0x0083,0xF2E1,0xCE45,0x0083,0xF2DE,0xF2EC,
+ 0x00A1,0x0091,0x0089,0x0085,0x0083,0xC0AF,0xCE44,0x0083,0xCE43,0xF2E0,0x0085,0x0083,0xCE42,0xC3DB,0x0083,0xCE41,
+ 0xF2E3,0x0089,0x0085,0x0083,0xCE40,0xD6A9,0x0083,0xCECF,0xCDA0,0x0085,0x0083,0xCDC9,0xCD9F,0x0083,0xF2D1,0xD1D1,
+ 0x0091,0x0089,0x0085,0x0083,0xCD9E,0xCD9D,0x0083,0xCD9C,0xCD9B,0x0085,0x0083,0xF2DC,0xCD9A,0x0083,0xCD99,0xF2DB,
+ 0x0089,0x0085,0x0083,0xF2DD,0xF2DA,0x0083,0xF2D8,0xCD98,0x0085,0x0083,0xCD97,0xCD96,0x0083,0xF2D7,0xB7E4,0x00C1,
+ 0x00A1,0x0091,0x0089,0x0085,0x0083,0xCD95,0xCAF1,0x0083,0xCD94,0xB6EA,0x0085,0x0083,0xCD93,0xCD92,0x0083,0xCD91,
+ 0xCD90,0x0089,0x0085,0x0083,0xD3BC,0xF2D9,0x0083,0xCD8F,0xCD8E,0x0085,0x0083,0xCD8D,0xF2D3,0x0083,0xF2CF,0xF2CD,
+ 0x0091,0x0089,0x0085,0x0083,0xF2CC,0xD5DD,0x0083,0xCD8C,0xC2F9,0x0085,0x0083,0xF2CE,0xCD8B,0x0083,0xCD8A,0xCD89,
+ 0x0089,0x0085,0x0083,0xF2CB,0xCD88,0x0083,0xCD87,0xCD86,0x0085,0x0083,0xCD85,0xB8F2,0x0083,0xCD84,0xCD83,0x00A1,
+ 0x0091,0x0089,0x0085,0x0083,0xCD82,0xCD81,0x0083,0xF2D4,0xF2D2,0x0085,0x0083,0xCD80,0xCD7E,0x0083,0xD6EB,0xCD7D,
+ 0x0089,0x0085,0x0083,0xCDDC,0xF2D5,0x0083,0xCD7C,0xCD7B,0x0085,0x0083,0xCD7A,0xBBD7,0x0083,0xCD79,0xCD78,0x0091,
+ 0x0089,0x0085,0x0083,0xF2D6,0xF2D0,0x0083,0xF2C9,0xF2C3,0x0085,0x0083,0xCD77,0xCD76,0x0083,0xB5B0,0xB9C6,0x0089,
+ 0x0085,0x0083,0xF2C8,0xCD75,0x0083,0xC9DF,0xC7F9,0x0085,0x0083,0xCD74,0xF2C1,0x0083,0xCD73,0xCD72,0x0101,0x00C1,
+ 0x00A1,0x0091,0x0089,0x0085,0x0083,0xCD71,0xD6FB,0x0083,0xCD70,0xCD6F,0x0085,0x0083,0xCD6E,0xCD6D,0x0083,0xCD6C,
+ 0xF2C5,0x0089,0x0085,0x0083,0xCD6B,0xCD6A,0x0083,0xCD69,0xF2C0,0x0085,0x0083,0xF2C2,0xF2CA,0x0083,0xCD68,0xCD67,
+ 0x0091,0x0089,0x0085,0x0083,0xF2C6,0xF2C4,0x0083,0xF2C7,0xCD66,0x0085,0x0083,0xCD65,0xF2B9,0x0083,0xCD64,0xF2BD,
+ 0x0089,0x0085,0x0083,0xF2BF,0xF2B6,0x0083,0xF2BB,0xCD63,0x0085,0x0083,0xCD62,0xD4E9,0x0083,0xF2BC,0xCD61,0x00A1,
+ 0x0091,0x0089,0x0085,0x0083,0xCD60,0xCD5F,0x0083,0xCD5E,0xCD5D,0x0085,0x0083,0xF2BA,0xD1C1,0x0083,0xCD5C,0xCD5B,
+ 0x0089,0x0085,0x0083,0xCD5A,0xCD59,0x0083,0xCD58,0xCD57,0x0085,0x0083,0xB2CF,0xCD56,0x0083,0xF2BE,0xCD55,0x0091,
+ 0x0089,0x0085,0x0083,0xCD54,0xCD53,0x0083,0xCD52,0xCD51,0x0085,0x0083,0xF2B7,0xB0F6,0x0083,0xF2B8,0xCEC3,0x0089,
+ 0x0085,0x0083,0xCD50,0xCD4F,0x0083,0xCD4E,0xCD4D,0x0085,0x0083,0xCD4C,0xCD4B,0x0083,0xCD4A,0xC2EC,0x00C1,0x00A1,
+ 0x0091,0x0089,0x0085,0x0083,0xD2CF,0xCAB4,0x0083,0xF2B2,0xCFBA,0x0085,0x0083,0xCBE4,0xF2B4,0x0083,0xF2B5,0xF2B3,
+ 0x0089,0x0085,0x0083,0xBAE7,0xCD49,0x0083,0xCD48,0xCD47,0x0085,0x0083,0xCD46,0xCD45,0x0083,0xCD44,0xCD43,0x0091,
+ 0x0089,0x0085,0x0083,0xCAAD,0xCD42,0x0083,0xCD41,0xF2B1,0x0085,0x0083,0xCD40,0xF2B0,0x0083,0xB3E6,0xCCA0,0x0089,
+ 0x0085,0x0083,0xCC9F,0xCC9E,0x0083,0xCC9D,0xCC9C,0x0085,0x0083,0xCC9B,0xCC9A,0x0083,0xCC99,0xEBBD,0x00A1,0x0091,
+ 0x0089,0x0085,0x0083,0xCC98,0xCC97,0x0083,0xCC96,0xD3DD,0x0085,0x0083,0xCC95,0xCC94,0x0083,0xCC93,0xD0E9,0x0089,
+ 0x0085,0x0083,0xCC92,0xCC91,0x0083,0xCC90,0xCC8F,0x0085,0x0083,0xCC8E,0xF2AF,0x0083,0xCC8D,0xCC8C,0x0091,0x0089,
+ 0x0085,0x0083,0xC2C7,0xC5B0,0x0083,0xC2B2,0xBBA2,0x0085,0x0083,0xF2AE,0xCC8B,0x0083,0xCC8A,0xCC89,0x0089,0x0085,
+ 0x0083,0xCC88,0xCC87,0x0083,0xCC86,0xCC85,0x0085,0x0083,0xCC84,0xCC83,0x0083,0xCC82,0xCC81,0x027F,0x017F,0x00FF,
+ 0x00BF,0x009F,0x008F,0x0087,0x0083,0xCC80,0x0083,0xCC7E,0xCC7D,0x0085,0x0083,0xCC7C,0xCC7B,0x0083,0xDEC2,0xCC7A,
+ 0x0089,0x0085,0x0083,0xCC79,0xCC78,0x0083,0xD5BA,0xCC77,0x0085,0x0083,0xCC76,0xCC75,0x0083,0xCC74,0xCC73,0x0091,
+ 0x0089,0x0085,0x0083,0xCC72,0xCC71,0x0083,0xCC70,0xCC6F,0x0085,0x0083,0xCC6E,0xCC6D,0x0083,0xCC6C,0xCC6B,0x0089,
+ 0x0085,0x0083,0xCC6A,0xDEC0,0x0083,0xCC69,0xDEBE,0x0085,0x0083,0xCC68,0xCC67,0x0083,0xCC66,0xCC65,0x00A1,0x0091,
+ 0x0089,0x0085,0x0083,0xCC64,0xCC63,0x0083,0xCC62,0xCC61,0x0085,0x0083,0xCC60,0xCC5F,0x0083,0xCC5E,0xCC5D,0x0089,
+ 0x0085,0x0083,0xCC5C,0xCC5B,0x0083,0xCC5A,0xCC59,0x0085,0x0083,0xDEC1,0xCC58,0x0083,0xCC57,0xCC56,0x0091,0x0089,
+ 0x0085,0x0083,0xCC55,0xC4A2,0x0083,0xCC54,0xCC53,0x0085,0x0083,0xCC52,0xCC51,0x0083,0xCC50,0xCC4F,0x0089,0x0085,
+ 0x0083,0xCC4E,0xCC4D,0x0083,0xCC4C,0xCC4B,0x0085,0x0083,0xCC4A,0xDEBF,0x0083,0xCC49,0xCC48,0x00C1,0x00A1,0x0091,
+ 0x0089,0x0085,0x0083,0xCC47,0xCC46,0x0083,0xCC45,0xDEBD,0x0085,0x0083,0xCC44,0xCC43,0x0083,0xCC42,0xD4E5,0x0089,
+ 0x0085,0x0083,0xCC41,0xCC40,0x0083,0xCBA0,0xCB9F,0x0085,0x0083,0xCB9E,0xCB9D,0x0083,0xCB9C,0xCB9B,0x0091,0x0089,
+ 0x0085,0x0083,0xCB9A,0xCB99,0x0083,0xCB98,0xCB97,0x0085,0x0083,0xCB96,0xCB95,0x0083,0xCB94,0xCB93,0x0089,0x0085,
+ 0x0083,0xCB92,0xB7AA,0x0083,0xCB91,0xCB90,0x0085,0x0083,0xCB8F,0xCB8E,0x0083,0xCCD9,0xCB8D,0x00A1,0x0091,0x0089,
+ 0x0085,0x0083,0xCB8C,0xCB8B,0x0083,0xCB8A,0xCB89,0x0085,0x0083,0xCB88,0xCB87,0x0083,0xDEBC,0xCB86,0x0089,0x0085,
+ 0x0083,0xCB85,0xCB84,0x0083,0xCB83,0xCB82,0x0085,0x0083,0xCB81,0xC5BA,0x0083,0xCB80,0xDEBA,0x0091,0x0089,0x0085,
+ 0x0083,0xCB7E,0xCB7D,0x0083,0xC3EA,0xB2D8,0x0085,0x0083,0xCB7C,0xCB7B,0x0083,0xCB7A,0xCB79,0x0089,0x0085,0x0083,
+ 0xCB78,0xBDE5,0x0083,0xCB77,0xCB76,0x0085,0x0083,0xCB75,0xCB74,0x0083,0xCB73,0xCB72,0x0101,0x00C1,0x00A1,0x0091,
+ 0x0089,0x0085,0x0083,0xCB71,0xDEBB,0x0083,0xCB70,0xCB6F,0x0085,0x0083,0xCB6E,0xCB6D,0x0083,0xCB6C,0xCB6B,0x0089,
+ 0x0085,0x0083,0xCB6A,0xDEB7,0x0083,0xCB69,0xDEB8,0x0085,0x0083,0xCB68,0xCB67,0x0083,0xCB66,0xCB65,0x0091,0x0089,
+ 0x0085,0x0083,0xCB64,0xCB63,0x0083,0xDEB9,0xCAED,0x0085,0x0083,0xDEB4,0xCB62,0x0083,0xCB61,0xCB60,0x0089,0x0085,
+ 0x0083,0xD0BD,0xCB5F,0x0083,0xDEB0,0xCB5E,0x0085,0x0083,0xCB5D,0xCB5C,0x0083,0xDEAF,0xCB5B,0x00A1,0x0091,0x0089,
+ 0x0085,0x0083,0xCB5A,0xCB59,0x0083,0xCB58,0xCB57,0x0085,0x0083,0xCB56,0xCB55,0x0083,0xDEB5,0xD1A6,0x0089,0x0085,
+ 0x0083,0xCB54,0xCB53,0x0083,0xCB52,0xCB51,0x0085,0x0083,0xCB50,0xCB4F,0x0083,0xCB4E,0xCB4D,0x0091,0x0089,0x0085,
+ 0x0083,0xCB4C,0xCB4B,0x0083,0xCB4A,0xDEB2,0x0085,0x0083,0xCB49,0xCB48,0x0083,0xCB47,0xCB46,0x0089,0x0085,0x0083,
+ 0xCB45,0xCB44,0x0083,0xCB43,0xDEB1,0x0085,0x0083,0xCB42,0xDEB6,0x0083,0xB1A1,0xCB41,0x00C1,0x00A1,0x0091,0x0089,
+ 0x0085,0x0083,0xCB40,0xCAA0,0x0083,0xCA9F,0xCA9E,0x0085,0x0083,0xC0D9,0xCA9D,0x0083,0xCA9C,0xDEAE,0x0089,0x0085,
+ 0x0083,0xDEAA,0xDEB3,0x0083,0xCA9B,0xCA9A,0x0085,0x0083,0xCA99,0xCA98,0x0083,0xD4CC,0xCA97,0x0091,0x0089,0x0085,
+ 0x0083,0xDEAD,0xCA96,0x0083,0xCA95,0xCA94,0x0085,0x0083,0xCA93,0xCA92,0x0083,0xCA91,0xCA90,0x0089,0x0085,0x0083,
+ 0xCA8F,0xCA8E,0x0083,0xDEA7,0xCA8D,0x0085,0x0083,0xCA8C,0xCA8B,0x0083,0xDEA8,0xCA8A,0x00A1,0x0091,0x0089,0x0085,
+ 0x0083,0xCA89,0xCA88,0x0083,0xCA87,0xCA86,0x0085,0x0083,0xDEA9,0xCA85,0x0083,0xCA84,0xCA83,0x0089,0x0085,0x0083,
+ 0xCA82,0xDEA5,0x0083,0xCA81,0xCA80,0x0085,0x0083,0xDEA1,0xCA7E,0x0083,0xCA7D,0xCA7C,0x0091,0x0089,0x0085,0x0083,
+ 0xCA7B,0xCA7A,0x0083,0xCA79,0xCA78,0x0085,0x0083,0xCA77,0xCA76,0x0083,0xCA75,0xCA74,0x0089,0x0085,0x0083,0xC8EF,
+ 0xBDB6,0x0083,0xDEA6,0xCA73,0x0085,0x0083,0xCA72,0xCA71,0x0083,0xCA70,0xDEAC,0x0181,0x0101,0x00C1,0x00A1,0x0091,
+ 0x0089,0x0085,0x0083,0xCA6F,0xCA6E,0x0083,0xCA6D,0xCA6C,0x0085,0x0083,0xCA6B,0xB1CE,0x0083,0xB0AA,0xDEA2,0x0089,
+ 0x0085,0x0083,0xDDFE,0xDDFC,0x0083,0xDDFA,0xC7BE,0x0085,0x0083,0xCA6A,0xCA69,0x0083,0xCA68,0xCA67,0x0091,0x0089,
+ 0x0085,0x0083,0xCA66,0xCA65,0x0083,0xCA64,0xCA63,0x0085,0x0083,0xCA62,0xCA61,0x0083,0xCADF,0xC4E8,0x0089,0x0085,
+ 0x0083,0xCA60,0xCA5F,0x0083,0xCA5E,0xCA5D,0x0085,0x0083,0xCA5C,0xCA5B,0x0083,0xCA5A,0xCA59,0x00A1,0x0091,0x0089,
+ 0x0085,0x0083,0xCA58,0xB2CC,0x0083,0xCA57,0xDDFD,0x0085,0x0083,0xCA56,0xCA55,0x0083,0xCA54,0xCA53,0x0089,0x0085,
+ 0x0083,0xCEB5,0xCA52,0x0083,0xCA51,0xD5E1,0x0085,0x0083,0xCA50,0xCA4F,0x0083,0xCA4E,0xC2FB,0x0091,0x0089,0x0085,
+ 0x0083,0xCA4D,0xC3EF,0x0083,0xCA4C,0xCA4B,0x0085,0x0083,0xCA4A,0xCA49,0x0083,0xDDF8,0xCA48,0x0089,0x0085,0x0083,
+ 0xCA47,0xCA46,0x0083,0xCA45,0xCA44,0x0085,0x0083,0xCA43,0xCA42,0x0083,0xCA41,0xCA40,0x00C1,0x00A1,0x0091,0x0089,
+ 0x0085,0x0083,0xC9A0,0xC99F,0x0083,0xC99E,0xDEA3,0x0085,0x0083,0xC99D,0xC99C,0x0083,0xDEA4,0xC99B,0x0089,0x0085,
+ 0x0083,0xC99A,0xC999,0x0083,0xC998,0xC997,0x0085,0x0083,0xC996,0xC995,0x0083,0xC994,0xC993,0x0091,0x0089,0x0085,
+ 0x0083,0xC992,0xC991,0x0083,0xDDFB,0xC990,0x0085,0x0083,0xC98F,0xC98E,0x0083,0xC5EE,0xC98D,0x0089,0x0085,0x0083,
+ 0xC98C,0xC98B,0x0083,0xC98A,0xC989,0x0085,0x0083,0xDDEB,0xDDF6,0x0083,0xC988,0xDDF7,0x00A1,0x0091,0x0089,0x0085,
+ 0x0083,0xC987,0xC986,0x0083,0xDDF1,0xBCBB,0x0085,0x0083,0xC985,0xC0B6,0x0083,0xC984,0xC983,0x0089,0x0085,0x0083,
+ 0xC982,0xC981,0x0083,0xC980,0xC97E,0x0085,0x0083,0xB1CD,0xC97D,0x0083,0xC97C,0xDDED,0x0091,0x0089,0x0085,0x0083,
+ 0xC97B,0xCBF2,0x0083,0xDDEA,0xC97A,0x0085,0x0083,0xC979,0xDDE9,0x0083,0xC978,0xC977,0x0089,0x0085,0x0083,0xDDEE,
+ 0xC8D8,0x0083,0xC976,0xC975,0x0085,0x0083,0xC974,0xC973,0x0083,0xD0EE,0xC972,0x0101,0x00C1,0x00A1,0x0091,0x0089,
+ 0x0085,0x0083,0xC971,0xDDE8,0x0083,0xC970,0xDDEF,0x0085,0x0083,0xC96F,0xDDEC,0x0083,0xC96E,0xC96D,0x0089,0x0085,
+ 0x0083,0xDDF0,0xDDF3,0x0083,0xD5F4,0xC96C,0x0085,0x0083,0xC96B,0xC96A,0x0083,0xDDF4,0xC969,0x0091,0x0089,0x0085,
+ 0x0083,0xC6D1,0xC968,0x0083,0xC967,0xD8E1,0x0085,0x0083,0xC966,0xC965,0x0083,0xC964,0xC963,0x0089,0x0085,0x0083,
+ 0xC962,0xC961,0x0083,0xC960,0xC95F,0x0085,0x0083,0xC95E,0xC95D,0x0083,0xC95C,0xC95B,0x00A1,0x0091,0x0089,0x0085,
+ 0x0083,0xC95A,0xDDF2,0x0083,0xC959,0xC958,0x0085,0x0083,0xC957,0xC956,0x0083,0xCBE2,0xC955,0x0089,0x0085,0x0083,
+ 0xC954,0xC3C9,0x0083,0xC953,0xDDF5,0x0085,0x0083,0xC952,0xC951,0x0083,0xC950,0xC94F,0x0091,0x0089,0x0085,0x0083,
+ 0xC94E,0xC94D,0x0083,0xC94C,0xC94B,0x0085,0x0083,0xDDE5,0xC94A,0x0083,0xDDE4,0xBDAF,0x0089,0x0085,0x0083,0xC949,
+ 0xDDDE,0x0083,0xDDDC,0xDDDB,0x0085,0x0083,0xC948,0xC947,0x0083,0xC946,0xC945,0x00C1,0x00A1,0x0091,0x0089,0x0085,
+ 0x0083,0xB5D9,0xC944,0x0083,0xC943,0xC942,0x0085,0x0083,0xC941,0xC940,0x0083,0xC8A0,0xC89F,0x0089,0x0085,0x0083,
+ 0xDDDD,0xC89E,0x0083,0xDDDF,0xC89D,0x0085,0x0083,0xDDE3,0xBFFB,0x0083,0xC89C,0xDDDA,0x0091,0x0089,0x0085,0x0083,
+ 0xC89B,0xB4D0,0x0083,0xC89A,0xC899,0x0085,0x0083,0xC898,0xDDE7,0x0083,0xD4E1,0xBAF9,0x0089,0x0085,0x0083,0xC897,
+ 0xDDE2,0x0083,0xC896,0xC895,0x0085,0x0083,0xC894,0xC893,0x0083,0xC892,0xB6AD,0x00A1,0x0091,0x0089,0x0085,0x0083,
+ 0xC891,0xC6CF,0x0083,0xC890,0xC88F,0x0085,0x0083,0xC88E,0xC88D,0x0083,0xDDD6,0xB8F0,0x0089,0x0085,0x0083,0xDDD8,
+ 0xDDD9,0x0083,0xC88C,0xD6F8,0x0085,0x0083,0xC88B,0xC88A,0x0083,0xC889,0xC888,0x0091,0x0089,0x0085,0x0083,0xC887,
+ 0xDDD7,0x0083,0xC886,0xC885,0x0085,0x0083,0xC884,0xC883,0x0083,0xC882,0xC881,0x0089,0x0085,0x0083,0xC880,0xC87E,
+ 0x0083,0xC87D,0xC87C,0x0085,0x0083,0xDDE1,0xC87B,0x0083,0xC87A,0xC879,0x047F,0x027F,0x017F,0x00FF,0x00BF,0x009F,
+ 0x008F,0x0087,0x0083,0xC878,0x0083,0xC877,0xC876,0x0085,0x0083,0xC875,0xC874,0x0083,0xC2E4,0xDDE0,0x0089,0x0085,
+ 0x0083,0xC873,0xC872,0x0083,0xC871,0xDDC7,0x0085,0x0083,0xC870,0xC86F,0x0083,0xC86E,0xC86D,0x0091,0x0089,0x0085,
+ 0x0083,0xC86C,0xC86B,0x0083,0xDDE6,0xC86A,0x0085,0x0083,0xC869,0xC868,0x0083,0xC867,0xC866,0x0089,0x0085,0x0083,
+ 0xC865,0xC864,0x0083,0xC863,0xC8F8,0x0085,0x0083,0xCFF4,0xDDD3,0x0083,0xD3AA,0xD3A9,0x00A1,0x0091,0x0089,0x0085,
+ 0x0083,0xC862,0xC861,0x0083,0xC860,0xC85F,0x0085,0x0083,0xC85E,0xC85D,0x0083,0xC2DC,0xDDC6,0x0089,0x0085,0x0083,
+ 0xC85C,0xC85B,0x0083,0xC85A,0xDDC1,0x0085,0x0083,0xC859,0xC858,0x0083,0xC857,0xC856,0x0091,0x0089,0x0085,0x0083,
+ 0xC855,0xC854,0x0083,0xDDC8,0xC853,0x0085,0x0083,0xDDCC,0xCEAE,0x0083,0xC6BC,0xC3C8,0x0089,0x0085,0x0083,0xDDC2,
+ 0xC852,0x0083,0xC851,0xC850,0x0085,0x0083,0xC84F,0xDDC9,0x0083,0xC84E,0xCCD1,0x00C1,0x00A1,0x0091,0x0089,0x0085,
+ 0x0083,0xDDCD,0xC84D,0x0083,0xDDBD,0xC84C,0x0085,0x0083,0xC84B,0xC84A,0x0083,0xDDC4,0xC849,0x0089,0x0085,0x0083,
+ 0xC848,0xC847,0x0083,0xDDCF,0xDDCE,0x0085,0x0083,0xC846,0xC845,0x0083,0xC844,0xC843,0x0091,0x0089,0x0085,0x0083,
+ 0xC842,0xB7C6,0x0083,0xC1E2,0xDDD4,0x0085,0x0083,0xC841,0xC840,0x0083,0xC7A0,0xC79F,0x0089,0x0085,0x0083,0xC79E,
+ 0xDDD0,0x0083,0xC6D0,0xC79D,0x0085,0x0083,0xC79C,0xC79B,0x0083,0xDDBE,0xC79A,0x00A1,0x0091,0x0089,0x0085,0x0083,
+ 0xC799,0xC798,0x0083,0xDDD5,0xB2A4,0x0085,0x0083,0xDDCB,0xC797,0x0083,0xDDC3,0xB2CB,0x0089,0x0085,0x0083,0xC796,
+ 0xC795,0x0083,0xC794,0xDDBF,0x0085,0x0083,0xC793,0xDDC5,0x0083,0xC792,0xDDCA,0x0091,0x0089,0x0085,0x0083,0xC791,
+ 0xC790,0x0083,0xC78F,0xC78E,0x0085,0x0083,0xBACA,0xC78D,0x0083,0xC78C,0xBEFA,0x0089,0x0085,0x0083,0xC78B,0xBED5,
+ 0x0083,0xC78A,0xC789,0x0085,0x0083,0xB9BD,0xC788,0x0083,0xDDD1,0xC787,0x0101,0x00C1,0x00A1,0x0091,0x0089,0x0085,
+ 0x0083,0xC786,0xC785,0x0083,0xDDBC,0xDDD2,0x0085,0x0083,0xC784,0xC783,0x0083,0xC3A7,0xDDBB,0x0089,0x0085,0x0083,
+ 0xC782,0xDDBA,0x0083,0xD3A8,0xDDB5,0x0085,0x0083,0xBBF1,0xDDB2,0x0083,0xC781,0xDDAB,0x0091,0x0089,0x0085,0x0083,
+ 0xDDAA,0xC1AB,0x0083,0xC0B3,0xDDA8,0x0085,0x0083,0xC780,0xC77E,0x0083,0xC77D,0xC77C,0x0089,0x0085,0x0083,0xC4AA,
+ 0xDDAD,0x0083,0xDDB3,0xDDB9,0x0085,0x0083,0xC77B,0xC77A,0x0083,0xC779,0xC778,0x00A1,0x0091,0x0089,0x0085,0x0083,
+ 0xC777,0xC776,0x0083,0xC775,0xDDAC,0x0085,0x0083,0xC774,0xDDB8,0x0083,0xC773,0xDDAF,0x0089,0x0085,0x0083,0xDCF0,
+ 0xC772,0x0083,0xC771,0xDDB7,0x0085,0x0083,0xC770,0xC76F,0x0083,0xC76E,0xC76D,0x0091,0x0089,0x0085,0x0083,0xDDAE,
+ 0xDCEC,0x0083,0xC76C,0xC76B,0x0085,0x0083,0xC76A,0xC9AF,0x0083,0xC769,0xC768,0x0089,0x0085,0x0083,0xC767,0xC766,
+ 0x0083,0xC0F2,0xC765,0x0085,0x0083,0xC764,0xC6CE,0x0083,0xDDB0,0xC763,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,
+ 0xC762,0xC761,0x0083,0xC760,0xC75F,0x0085,0x0083,0xC75E,0xC75D,0x0083,0xDDB4,0xDDB1,0x0089,0x0085,0x0083,0xDDB6,
+ 0xC75C,0x0083,0xC75B,0xDDA9,0x0085,0x0083,0xBAC9,0xC75A,0x0083,0xC759,0xC758,0x0091,0x0089,0x0085,0x0083,0xC757,
+ 0xC756,0x0083,0xC755,0xC754,0x0085,0x0083,0xD2A9,0xDDA7,0x0083,0xDDA6,0xDDA4,0x0089,0x0085,0x0083,0xD2F1,0xDDA5,
+ 0x0083,0xDDA3,0xDDA1,0x0085,0x0083,0xD3AB,0xDCFD,0x0083,0xDCFE,0xBBE7,0x00A1,0x0091,0x0089,0x0085,0x0083,0xC8D9,
+ 0xC753,0x0083,0xB5B4,0xDCF9,0x0085,0x0083,0xDCF6,0xDCF1,0x0083,0xC752,0xDCEA,0x0089,0x0085,0x0083,0xDCE9,0xBCD4,
+ 0x0083,0xC751,0xC750,0x0085,0x0083,0xC74F,0xC74E,0x0083,0xC74D,0xC0F3,0x0091,0x0089,0x0085,0x0083,0xC74C,0xBBC4,
+ 0x0083,0xDCE8,0xBCF6,0x0085,0x0083,0xDCF3,0xC74B,0x0083,0xC74A,0xC749,0x0089,0x0085,0x0083,0xC748,0xC747,0x0083,
+ 0xB2DD,0xC746,0x0085,0x0083,0xDCF4,0xBEA3,0x0083,0xC745,0xC744,0x0181,0x0101,0x00C1,0x00A1,0x0091,0x0089,0x0085,
+ 0x0083,0xDCF5,0xC743,0x0083,0xC742,0xDCF7,0x0085,0x0083,0xC741,0xC740,0x0083,0xC6A0,0xDCED,0x0089,0x0085,0x0083,
+ 0xC69F,0xDCFB,0x0083,0xC8E3,0xC8D7,0x0085,0x0083,0xC69E,0xB2E8,0x0083,0xD2F0,0xDCEE,0x0091,0x0089,0x0085,0x0083,
+ 0xDCFC,0xC69D,0x0083,0xDCEF,0xC69C,0x0085,0x0083,0xDCF2,0xC69B,0x0083,0xDCFA,0xB2E7,0x0089,0x0085,0x0083,0xC3A3,
+ 0xC69A,0x0083,0xC699,0xB4C4,0x0085,0x0083,0xBCEB,0xC698,0x0083,0xC697,0xC696,0x00A1,0x0091,0x0089,0x0085,0x0083,
+ 0xC695,0xC694,0x0083,0xC693,0xC692,0x0085,0x0083,0xC691,0xC690,0x0083,0xC68F,0xDCE7,0x0089,0x0085,0x0083,0xDDA2,
+ 0xDCE1,0x0083,0xC68E,0xC68D,0x0085,0x0083,0xDCF8,0xC68C,0x0083,0xDCE4,0xDCE3,0x0091,0x0089,0x0085,0x0083,0xC68B,
+ 0xC68A,0x0083,0xDCE0,0xC689,0x0085,0x0083,0xDCD7,0xBEA5,0x0083,0xC688,0xDCDD,0x0089,0x0085,0x0083,0xC687,0xC686,
+ 0x0083,0xDCD4,0xDCEB,0x0085,0x0083,0xDCD8,0xDCE2,0x0083,0xC3A9,0xC7D1,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,
+ 0xB7B6,0xC3AF,0x0083,0xD7C2,0xC685,0x0085,0x0083,0xC684,0xC683,0x0083,0xC682,0xC681,0x0089,0x0085,0x0083,0xDCDE,
+ 0xC680,0x0083,0xC6BB,0xC67E,0x0085,0x0083,0xDCD5,0xC67D,0x0083,0xC67C,0xDCDA,0x0091,0x0089,0x0085,0x0083,0xC67B,
+ 0xC67A,0x0083,0xD3A2,0xC679,0x0085,0x0083,0xB1BD,0xC678,0x0083,0xC677,0xC676,0x0089,0x0085,0x0083,0xC9BB,0xC675,
+ 0x0083,0xC674,0xC673,0x0085,0x0083,0xC672,0xBFE0,0x0083,0xC8F4,0xDCD6,0x00A1,0x0091,0x0089,0x0085,0x0083,0xDCC4,
+ 0xC671,0x0083,0xDCD3,0xDCE5,0x0085,0x0083,0xB9B6,0xB0FA,0x0083,0xC670,0xDCD9,0x0089,0x0085,0x0083,0xBFC1,0xC66F,
+ 0x0083,0xC66E,0xDCDC,0x0085,0x0083,0xC3E7,0xC66D,0x0083,0xDCE6,0xCCA6,0x0091,0x0089,0x0085,0x0083,0xDCDF,0xDCDB,
+ 0x0083,0xD4B7,0xC66C,0x0085,0x0083,0xCBD5,0xDCD1,0x0083,0xB2D4,0xDCC9,0x0089,0x0085,0x0083,0xDCC8,0xDCC3,0x0083,
+ 0xC66B,0xDCC2,0x0085,0x0083,0xCEAD,0xC66A,0x0083,0xC669,0xDCD0,0x0101,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,
+ 0xC668,0xC667,0x0083,0xDCCA,0xC666,0x0085,0x0083,0xC665,0xDCC0,0x0083,0xD1BF,0xC664,0x0089,0x0085,0x0083,0xC663,
+ 0xC662,0x0083,0xC7DB,0xDCBF,0x0085,0x0083,0xDCC6,0xC661,0x0083,0xC660,0xDCCC,0x0091,0x0089,0x0085,0x0083,0xB7BC,
+ 0xC65F,0x0083,0xBBA8,0xDCC1,0x0085,0x0083,0xD0BE,0xDCC7,0x0083,0xB0C5,0xB7D2,0x0089,0x0085,0x0083,0xDCBE,0xDCCE,
+ 0x0083,0xDCCB,0xDCB8,0x0085,0x0083,0xC65E,0xC2AB,0x0083,0xBDE6,0xDCD2,0x00A1,0x0091,0x0089,0x0085,0x0083,0xC65D,
+ 0xC65C,0x0083,0xDCCD,0xC65B,0x0085,0x0083,0xDCCF,0xC65A,0x0083,0xD6A5,0xCEDF,0x0089,0x0085,0x0083,0xC659,0xC658,
+ 0x0083,0xDCBD,0xDCC5,0x0085,0x0083,0xDCBC,0xC657,0x0083,0xC656,0xC655,0x0091,0x0089,0x0085,0x0083,0xC654,0xC3A2,
+ 0x0083,0xDCBB,0xC653,0x0085,0x0083,0xDCB6,0xDCBA,0x0083,0xC9D6,0xC652,0x0089,0x0085,0x0083,0xD3F3,0xDCB7,0x0083,
+ 0xC651,0xD8C2,0x0085,0x0083,0xC650,0xC64F,0x0083,0xC64E,0xDCB9,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0xC64D,
+ 0xBDDA,0x0083,0xC64C,0xC64B,0x0085,0x0083,0xDCB5,0xB0AC,0x0083,0xDCB4,0xC64A,0x0089,0x0085,0x0083,0xC649,0xD2D5,
+ 0x0083,0xDCB3,0xC648,0x0085,0x0083,0xC647,0xC646,0x0083,0xC645,0xE5F5,0x0091,0x0089,0x0085,0x0083,0xD1DE,0xC9AB,
+ 0x0083,0xC644,0xBCE8,0x0085,0x0083,0xC1BC,0xF4DE,0x0083,0xC643,0xC642,0x0089,0x0085,0x0083,0xC641,0xC640,0x0083,
+ 0xC5A0,0xF4BF,0x0085,0x0083,0xC59F,0xC59E,0x0083,0xC59D,0xC59C,0x00A1,0x0091,0x0089,0x0085,0x0083,0xC59B,0xC59A,
+ 0x0083,0xC599,0xC598,0x0085,0x0083,0xF4BE,0xC597,0x0083,0xC596,0xC595,0x0089,0x0085,0x0083,0xC594,0xF4BD,0x0083,
+ 0xC593,0xCBD2,0x0085,0x0083,0xC592,0xC591,0x0083,0xC590,0xC58F,0x0091,0x0089,0x0085,0x0083,0xC58E,0xC58D,0x0083,
+ 0xC58C,0xC58B,0x0085,0x0083,0xF4BC,0xC58A,0x0083,0xC589,0xC588,0x0089,0x0085,0x0083,0xF4BB,0xC587,0x0083,0xF4BA,
+ 0xC586,0x0085,0x0083,0xCDA7,0xC585,0x0083,0xC584,0xF4B9,0x027F,0x017F,0x00FF,0x00BF,0x009F,0x008F,0x0087,0x0083,
+ 0xC583,0x0083,0xC582,0xC581,0x0085,0x0083,0xC580,0xC57E,0x0083,0xF4B8,0xC57D,0x0089,0x0085,0x0083,0xC57C,0xF4B5,
+ 0x0083,0xC57B,0xB4AC,0x0085,0x0083,0xF4B4,0xCFCF,0x0083,0xB2B0,0xB6E6,0x0091,0x0089,0x0085,0x0083,0xF4B7,0xF4B6,
+ 0x0083,0xC57A,0xB2D5,0x0085,0x0083,0xBDA2,0xF4B1,0x0083,0xC579,0xF4B0,0x0089,0x0085,0x0083,0xB0E3,0xF4B3,0x0083,
+ 0xBABD,0xC578,0x0085,0x0083,0xF4B2,0xC577,0x0083,0xC576,0xC575,0x00A1,0x0091,0x0089,0x0085,0x0083,0xC574,0xF4AF,
+ 0x0083,0xF4AE,0xF4AD,0x0085,0x0083,0xC573,0xD6DB,0x0083,0xCEE8,0xC572,0x0089,0x0085,0x0083,0xCBB4,0xE2B6,0x0083,
+ 0xC571,0xC570,0x0085,0x0083,0xC56F,0xC56E,0x0083,0xC56D,0xC56C,0x0091,0x0089,0x0085,0x0083,0xCCF2,0xC56B,0x0083,
+ 0xCAE6,0xC56A,0x0085,0x0083,0xF3C2,0xC569,0x0083,0xC568,0xC9E1,0x0089,0x0085,0x0083,0xC9E0,0xC567,0x0083,0xC566,
+ 0xC565,0x0085,0x0083,0xC564,0xC563,0x0083,0xD3DF,0xBECB,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0xF4AA,0xC562,
+ 0x0083,0xF4A9,0xF4A8,0x0085,0x0083,0xD2A8,0xC561,0x0083,0xF4A7,0xC560,0x0089,0x0085,0x0083,0xBECA,0xD5E9,0x0083,
+ 0xC55F,0xC55E,0x0085,0x0083,0xC55D,0xC55C,0x0083,0xC55B,0xC55A,0x0091,0x0089,0x0085,0x0083,0xD6C2,0xD6C1,0x0083,
+ 0xC559,0xC558,0x0085,0x0083,0xC557,0xC556,0x0083,0xC555,0xB3F4,0x0089,0x0085,0x0083,0xF4AB,0xC554,0x0083,0xD7D4,
+ 0xC553,0x0085,0x0083,0xC552,0xEAB0,0x0083,0xC551,0xC550,0x00A1,0x0091,0x0089,0x0085,0x0083,0xC54F,0xB3BC,0x0083,
+ 0xC54E,0xC54D,0x0085,0x0083,0xC54C,0xC54B,0x0083,0xC54A,0xC549,0x0089,0x0085,0x0083,0xC548,0xC547,0x0083,0xC546,
+ 0xC545,0x0085,0x0083,0xC544,0xC543,0x0083,0xC542,0xC541,0x0091,0x0089,0x0085,0x0083,0xC540,0xC4A0,0x0083,0xC49F,
+ 0xC49E,0x0085,0x0083,0xC49D,0xC49C,0x0083,0xC49B,0xC49A,0x0089,0x0085,0x0083,0xEBFB,0xC499,0x0083,0xEBFD,0xC498,
+ 0x0085,0x0083,0xC497,0xC496,0x0083,0xD2DC,0xC495,0x0101,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0xC494,0xD3B7,
+ 0x0083,0xB1DB,0xECA1,0x0085,0x0083,0xCDCE,0xC493,0x0083,0xC492,0xC491,0x0089,0x0085,0x0083,0xC490,0xEBFE,0x0083,
+ 0xE2DF,0xC48F,0x0085,0x0083,0xC48E,0xC48D,0x0083,0xC48C,0xC48B,0x0091,0x0089,0x0085,0x0083,0xC48A,0xC9C5,0x0083,
+ 0xC489,0xC488,0x0085,0x0083,0xC487,0xC486,0x0083,0xC485,0xC484,0x0089,0x0085,0x0083,0xC483,0xC482,0x0083,0xEBFA,
+ 0xC481,0x0085,0x0083,0xC5F2,0xC480,0x0083,0xECA2,0xC47E,0x00A1,0x0091,0x0089,0x0085,0x0083,0xC47D,0xEBF9,0x0083,
+ 0xC47C,0xC47B,0x0085,0x0083,0xC47A,0xC479,0x0083,0xC478,0xCFA5,0x0089,0x0085,0x0083,0xC4A4,0xCCC5,0x0083,0xC477,
+ 0xC476,0x0085,0x0083,0xB1EC,0xC475,0x0083,0xC474,0xC473,0x0091,0x0089,0x0085,0x0083,0xC472,0xC471,0x0083,0xC470,
+ 0xEBF7,0x0085,0x0083,0xC46F,0xB8E0,0x0083,0xC46E,0xC46D,0x0089,0x0085,0x0083,0xC46C,0xC46B,0x0083,0xB2B2,0xC46A,
+ 0x0085,0x0083,0xEBF5,0xC469,0x0083,0xC468,0xC467,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0xC466,0xC465,0x0083,
+ 0xEBF6,0xC464,0x0085,0x0083,0xB0F2,0xCDC8,0x0083,0xCCDA,0xEBF0,0x0089,0x0085,0x0083,0xEBEF,0xC4E5,0x0083,0xCFD9,
+ 0xB8B9,0x0085,0x0083,0xC463,0xC462,0x0083,0xC461,0xC460,0x0091,0x0089,0x0085,0x0083,0xEBE9,0xC45F,0x0083,0xC45E,
+ 0xEBEC,0x0085,0x0083,0xD1FC,0xC45D,0x0083,0xC8F9,0xEBF1,0x0089,0x0085,0x0083,0xC45C,0xC45B,0x0083,0xC45A,0xEBEE,
+ 0x0085,0x0083,0xC459,0xEBF2,0x0083,0xC458,0xD0C8,0x00A1,0x0091,0x0089,0x0085,0x0083,0xC457,0xC456,0x0083,0xC455,
+ 0xC454,0x0085,0x0083,0xEBED,0xC453,0x0083,0xC452,0xC451,0x0089,0x0085,0x0083,0xC450,0xC44F,0x0083,0xEBEB,0xEBEA,
+ 0x0085,0x0083,0xC44E,0xC44D,0x0083,0xC44C,0xCDF3,0x0091,0x0089,0x0085,0x0083,0xC7BB,0xEBE8,0x0083,0xC44B,0xB8AD,
+ 0x0085,0x0083,0xB8AF,0xC44A,0x0083,0xC449,0xC448,0x0089,0x0085,0x0083,0xEBE7,0xD2B8,0x0083,0xC0B0,0xC447,0x0085,
+ 0x0083,0xEBE6,0xC446,0x0083,0xCCF3,0xC445,0x0181,0x0101,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0xC444,0xC443,
+ 0x0083,0xC442,0xC441,0x0085,0x0083,0xC440,0xC3A0,0x0083,0xC6A2,0xC39F,0x0089,0x0085,0x0083,0xC39E,0xC39D,0x0083,
+ 0xC39C,0xC39B,0x0085,0x0083,0xC1B3,0xC39A,0x0083,0xEBE1,0xC399,0x0091,0x0089,0x0085,0x0083,0xC398,0xC397,0x0083,
+ 0xEBE5,0xCDD1,0x0085,0x0083,0xC396,0xB8AC,0x0083,0xC395,0xC394,0x0089,0x0085,0x0083,0xEBE3,0xC393,0x0083,0xC392,
+ 0xC391,0x0085,0x0083,0xC390,0xC38F,0x0083,0xC38E,0xC38D,0x00A1,0x0091,0x0089,0x0085,0x0083,0xC38C,0xC38B,0x0083,
+ 0xC38A,0xC389,0x0085,0x0083,0xC388,0xC387,0x0083,0xEBE2,0xC386,0x0089,0x0085,0x0083,0xC385,0xC384,0x0083,0xBDC5,
+ 0xC383,0x0085,0x0083,0xEBE4,0xC382,0x0083,0xB2B1,0xC381,0x0091,0x0089,0x0085,0x0083,0xD9F5,0xC5A7,0x0083,0xEBDF,
+ 0xC4D4,0x0085,0x0083,0xC6EA,0xD4E0,0x0083,0xEBDB,0xEBDA,0x0089,0x0085,0x0083,0xC380,0xC37E,0x0083,0xBCB9,0xC2F6,
+ 0x0085,0x0083,0xC37D,0xC37C,0x0083,0xB4E0,0xC37B,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0xC37A,0xC379,0x0083,
+ 0xD6AC,0xC378,0x0085,0x0083,0xC377,0xC376,0x0083,0xC375,0xC4DC,0x0089,0x0085,0x0083,0xEBDD,0xC374,0x0083,0xB0B7,
+ 0xC373,0x0085,0x0083,0xD0D8,0xC372,0x0083,0xBDBA,0xC371,0x0091,0x0089,0x0085,0x0083,0xEBD8,0xB8EC,0x0083,0xEBDC,
+ 0xEBD7,0x0085,0x0083,0xD2C8,0xBFE8,0x0083,0xC370,0xEBD9,0x0089,0x0085,0x0083,0xE6C0,0xEBD6,0x0083,0xEBCD,0xEBCC,
+ 0x0085,0x0083,0xEBCB,0xEBCA,0x0083,0xC36F,0xF1E3,0x00A1,0x0091,0x0089,0x0085,0x0083,0xD8B7,0xC36E,0x0083,0xC36D,
+ 0xBAFA,0x0085,0x0083,0xC36C,0xC36B,0x0083,0xB0FB,0xEBD5,0x0089,0x0085,0x0083,0xCAA4,0xEBCE,0x0083,0xC5DF,0xEBD1,
+ 0x0085,0x0083,0xC36A,0xEBD3,0x0083,0xC5D6,0xC369,0x0091,0x0089,0x0085,0x0083,0xC368,0xC367,0x0083,0xC366,0xC365,
+ 0x0085,0x0083,0xC364,0xC363,0x0083,0xCCA5,0xEBD2,0x0089,0x0085,0x0083,0xB1B3,0xC362,0x0083,0xC361,0xC360,0x0085,
+ 0x0083,0xC35F,0xC35E,0x0083,0xB5A8,0xC35D,0x0101,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0xEBD0,0xCEB8,0x0083,
+ 0xEBCF,0xD0B2,0x0085,0x0083,0xD5CD,0xD6D7,0x0083,0xC9F6,0xEBC4,0x0089,0x0085,0x0083,0xEBC2,0xC35C,0x0083,0xB7CE,
+ 0xC35B,0x0085,0x0083,0xC35A,0xEBC9,0x0083,0xC359,0xC358,0x0091,0x0089,0x0085,0x0083,0xEBC8,0xC357,0x0083,0xD3FD,
+ 0xEBC5,0x0085,0x0083,0xC356,0xBFCF,0x0083,0xB0B9,0xEBC7,0x0089,0x0085,0x0083,0xC355,0xEBC6,0x0083,0xB7BE,0xBCE7,
+ 0x0085,0x0083,0xC354,0xC353,0x0083,0xC352,0xB7CA,0x00A1,0x0091,0x0089,0x0085,0x0083,0xB7F4,0xC351,0x0083,0xD6AB,
+ 0xB9C9,0x0085,0x0083,0xB3A6,0xEBBF,0x0083,0xC350,0xB8CE,0x0089,0x0085,0x0083,0xEBC0,0xB8D8,0x0083,0xB6C7,0xC34F,
+ 0x0085,0x0083,0xD6E2,0xC34E,0x0083,0xD0A4,0xC34D,0x0091,0x0089,0x0085,0x0083,0xC34C,0xEBC1,0x0083,0xC34B,0xC34A,
+ 0x0085,0x0083,0xC349,0xC348,0x0083,0xC347,0xC346,0x0089,0x0085,0x0083,0xBCA1,0xC0DF,0x0083,0xC345,0xC8E2,0x0085,
+ 0x0083,0xC344,0xD5D8,0x0083,0xCBC1,0xC343,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0xD2DE,0xCBE0,0x0083,0xC342,
+ 0xC341,0x0085,0x0083,0xEDB1,0xEDB2,0x0083,0xC340,0xC2A0,0x0089,0x0085,0x0083,0xC29F,0xC29E,0x0083,0xC29D,0xC29C,
+ 0x0085,0x0083,0xC29B,0xC29A,0x0083,0xC299,0xC298,0x0091,0x0089,0x0085,0x0083,0xC297,0xC296,0x0083,0xC295,0xF1FA,
+ 0x0085,0x0083,0xC294,0xC293,0x0083,0xC292,0xC291,0x0089,0x0085,0x0083,0xC290,0xC28F,0x0083,0xB4CF,0xF1F9,0x0085,
+ 0x0083,0xC28E,0xC28D,0x0083,0xC28C,0xC28B,0x00A1,0x0091,0x0089,0x0085,0x0083,0xC28A,0xC289,0x0083,0xC288,0xC287,
+ 0x0085,0x0083,0xC286,0xC285,0x0083,0xC284,0xC283,0x0089,0x0085,0x0083,0xC282,0xC281,0x0083,0xBEDB,0xC280,0x0085,
+ 0x0083,0xC6B8,0xC27E,0x0083,0xC27D,0xC27C,0x0091,0x0089,0x0085,0x0083,0xC1AA,0xC27B,0x0083,0xF1F8,0xC27A,0x0085,
+ 0x0083,0xC279,0xC278,0x0083,0xC277,0xF1F7,0x0089,0x0085,0x0083,0xD6B0,0xC1FB,0x0083,0xC1C4,0xC276,0x0085,0x0083,
+ 0xC275,0xC274,0x0083,0xF1F6,0xC273,0x6511,0x4051,0x2069,0x1075,0x087B,0x047D,0x027F,0x017F,0x00FF,0x00BF,0x009F,
+ 0x008F,0x0087,0x0083,0xC272,0x0083,0xF1F5,0xC4F4,0x0085,0x0083,0xC271,0xC270,0x0083,0xB9A2,0xC26F,0x0089,0x0085,
+ 0x0083,0xB5A2,0xC26E,0x0083,0xB3DC,0xC26D,0x0085,0x0083,0xC26C,0xCBCA,0x0083,0xDEC7,0xD2AE,0x0091,0x0089,0x0085,
+ 0x0083,0xF1F4,0xC26B,0x0083,0xB6FA,0xC26A,0x0085,0x0083,0xF1F2,0xC269,0x0083,0xC268,0xC267,0x0089,0x0085,0x0083,
+ 0xC266,0xC265,0x0083,0xC264,0xC5D5,0x0085,0x0083,0xF1F0,0xF1F1,0x0083,0xF1EF,0xF1EE,0x00A1,0x0091,0x0089,0x0085,
+ 0x0083,0xF1ED,0xC263,0x0083,0xC262,0xF1EC,0x0085,0x0083,0xC261,0xF1EB,0x0083,0xC260,0xC25F,0x0089,0x0085,0x0083,
+ 0xC25E,0xF1EA,0x0083,0xC25D,0xC25C,0x0085,0x0083,0xB0D2,0xD4C5,0x0083,0xBAC4,0xF1E9,0x0091,0x0089,0x0085,0x0083,
+ 0xB8FB,0xF1E8,0x0083,0xC25B,0xF1E7,0x0085,0x0083,0xC25A,0xC4CD,0x0083,0xC259,0xC258,0x0089,0x0085,0x0083,0xCBA3,
+ 0xB6F8,0x0083,0xF1F3,0xC257,0x0085,0x0083,0xC256,0xC255,0x0083,0xC254,0xEAC8,0x00C1,0x00A1,0x0091,0x0089,0x0085,
+ 0x0083,0xD5DF,0xEBA3,0x0083,0xBFBC,0xC253,0x0085,0x0083,0xC0CF,0xD2AB,0x0083,0xC252,0xC251,0x0089,0x0085,0x0083,
+ 0xC250,0xD2ED,0x0083,0xB7AD,0xC24F,0x0085,0x0083,0xC24E,0xC24D,0x0083,0xC24C,0xC24B,0x0091,0x0089,0x0085,0x0083,
+ 0xC24A,0xC249,0x0083,0xF4E8,0xC248,0x0085,0x0083,0xB0BF,0xBAB2,0x0083,0xC247,0xF4E7,0x0089,0x0085,0x0083,0xC246,
+ 0xC245,0x0083,0xC244,0xC243,0x0085,0x0083,0xF4E6,0xC242,0x0083,0xC241,0xF4E5,0x00A1,0x0091,0x0089,0x0085,0x0083,
+ 0xF4E3,0xC240,0x0083,0xC1A0,0xC19F,0x0085,0x0083,0xF4E4,0xB4E4,0x0083,0xB5D4,0xC19E,0x0089,0x0085,0x0083,0xC19D,
+ 0xC19C,0x0083,0xC19B,0xC19A,0x0085,0x0083,0xC199,0xC7CC,0x0083,0xC198,0xC197,0x0091,0x0089,0x0085,0x0083,0xF4E2,
+ 0xCFE8,0x0083,0xC196,0xC195,0x0085,0x0083,0xC194,0xC193,0x0083,0xC192,0xF4E1,0x0089,0x0085,0x0083,0xC191,0xD2EE,
+ 0x0083,0xC190,0xF1B4,0x0085,0x0083,0xC18F,0xC18E,0x0083,0xC18D,0xC18C,0x0101,0x00C1,0x00A1,0x0091,0x0089,0x0085,
+ 0x0083,0xB3E1,0xC18B,0x0083,0xC18A,0xC189,0x0085,0x0083,0xCECC,0xC188,0x0083,0xF4E0,0xC187,0x0089,0x0085,0x0083,
+ 0xD3F0,0xE5F1,0x0083,0xC186,0xC185,0x0085,0x0083,0xB8FE,0xD9FA,0x0083,0xC184,0xC183,0x0091,0x0089,0x0085,0x0083,
+ 0xC182,0xC181,0x0083,0xC180,0xF4CB,0x0085,0x0083,0xC17E,0xF4CA,0x0083,0xF4C9,0xC17D,0x0089,0x0085,0x0083,0xC17C,
+ 0xC17B,0x0083,0xC17A,0xC179,0x0085,0x0083,0xC178,0xC177,0x0083,0xF4C8,0xC176,0x00A1,0x0091,0x0089,0x0085,0x0083,
+ 0xC175,0xC8BA,0x0083,0xC174,0xC173,0x0085,0x0083,0xCFDB,0xC172,0x0083,0xF4C7,0xD0DF,0x0089,0x0085,0x0083,0xF4C6,
+ 0xC171,0x0083,0xC170,0xC1E7,0x0085,0x0083,0xC16F,0xC16E,0x0083,0xC16D,0xC16C,0x0091,0x0089,0x0085,0x0083,0xC16B,
+ 0xB8E1,0x0083,0xC16A,0xC169,0x0085,0x0083,0xC168,0xC167,0x0083,0xC166,0xC3C0,0x0089,0x0085,0x0083,0xC165,0xC7BC,
+ 0x0083,0xC164,0xD1F2,0x0085,0x0083,0xC163,0xC162,0x0083,0xC161,0xC160,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,
+ 0xC15F,0xC15E,0x0083,0xC15D,0xC15C,0x0085,0x0083,0xEEBF,0xC15B,0x0083,0xC15A,0xEEC0,0x0089,0x0085,0x0083,0xC159,
+ 0xC158,0x0083,0xC157,0xC156,0x0085,0x0083,0xEEBE,0xC155,0x0083,0xC154,0xC153,0x0091,0x0089,0x0085,0x0083,0xC152,
+ 0xEEBC,0x0083,0xC151,0xCAF0,0x0085,0x0083,0xEEBD,0xC150,0x0083,0xC14F,0xD6C3,0x0089,0x0085,0x0083,0xC14E,0xC14D,
+ 0x0083,0xC14C,0xD7EF,0x0085,0x0083,0xD5D6,0xEEBB,0x0083,0xC14B,0xC14A,0x00A1,0x0091,0x0089,0x0085,0x0083,0xC149,
+ 0xC148,0x0083,0xC147,0xB0D5,0x0085,0x0083,0xEEB8,0xC146,0x0083,0xEEB9,0xC145,0x0089,0x0085,0x0083,0xC144,0xC143,
+ 0x0083,0xC142,0xB7A3,0x0085,0x0083,0xC141,0xEEB7,0x0083,0xC2DE,0xC140,0x0091,0x0089,0x0085,0x0083,0xBAB1,0xD8E8,
+ 0x0083,0xC0A0,0xC09F,0x0085,0x0083,0xCDF8,0xB9DE,0x0083,0xC09E,0xC09D,0x0089,0x0085,0x0083,0xC09C,0xC09B,0x0083,
+ 0xC09A,0xC099,0x0085,0x0083,0xC098,0xC097,0x0083,0xC096,0xC095,0x017F,0x00FF,0x00BF,0x009F,0x008F,0x0087,0x0083,
+ 0xF3C1,0x0083,0xF3C0,0xC094,0x0085,0x0083,0xF3BF,0xC093,0x0083,0xC092,0xC091,0x0089,0x0085,0x0083,0xC090,0xC08F,
+ 0x0083,0xC08E,0xC08D,0x0085,0x0083,0xC8B1,0xC08C,0x0083,0xB8D7,0xC08B,0x0091,0x0089,0x0085,0x0083,0xF3BE,0xE7DA,
+ 0x0083,0xBDC9,0xE7D9,0x0085,0x0083,0xE7D8,0xE7D7,0x0083,0xE7D6,0xE7D5,0x0089,0x0085,0x0083,0xC9C9,0xE7D4,0x0083,
+ 0xE7D3,0xE7D2,0x0085,0x0083,0xE7D1,0xCBF5,0x0083,0xD3A7,0xE7D0,0x00A1,0x0091,0x0089,0x0085,0x0083,0xE7CF,0xE7CE,
+ 0x0083,0xE7CD,0xE7CC,0x0085,0x0083,0xE7CB,0xE7CA,0x0083,0xB2F8,0xE7C9,0x0089,0x0085,0x0083,0xC08A,0xB7EC,0x0083,
+ 0xE7C7,0xE7C8,0x0085,0x0083,0xB8BF,0xE7C6,0x0083,0xD4B5,0xE7C5,0x0091,0x0089,0x0085,0x0083,0xB1E0,0xC2C6,0x0083,
+ 0xB5DE,0xBBBA,0x0085,0x0083,0xE7C4,0xE7C3,0x0083,0xC089,0xE7C2,0x0089,0x0085,0x0083,0xB6D0,0xE7B6,0x0083,0xE7C1,
+ 0xE7C0,0x0085,0x0083,0xC088,0xBCA9,0x0083,0xE7BF,0xE7BE,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0xC0C2,0xC3E5,
+ 0x0083,0xBCEA,0xE7BD,0x0085,0x0083,0xE7BC,0xE7BB,0x0083,0xD7BA,0xC2CC,0x0089,0x0085,0x0083,0xE7BA,0xD5C0,0x0083,
+ 0xD7DB,0xE7B9,0x0085,0x0083,0xE7B8,0xC087,0x0083,0xB3F1,0xB1C1,0x0091,0x0089,0x0085,0x0083,0xE7B7,0xC3E0,0x0083,
+ 0xCEAC,0xC9FE,0x0085,0x0083,0xE7B5,0xE7B4,0x0083,0xB4C2,0xE7B3,0x0089,0x0085,0x0083,0xE7B2,0xD0F8,0x0083,0xC086,
+ 0xE7B1,0x0085,0x0083,0xD0F7,0xBCA8,0x0083,0xE7B0,0xBCCC,0x00A1,0x0091,0x0089,0x0085,0x0083,0xCCD0,0xCBE7,0x0083,
+ 0xC085,0xD0E5,0x0085,0x0083,0xBEEE,0xE7AF,0x0083,0xE7AE,0xCDB3,0x0089,0x0085,0x0083,0xBDCA,0xBEF8,0x0083,0xC2E7,
+ 0xE7AD,0x0085,0x0083,0xD1A4,0xB8F8,0x0083,0xBBE6,0xE7AC,0x0091,0x0089,0x0085,0x0083,0xC084,0xC8C6,0x0083,0xE7AB,
+ 0xBDE1,0x0085,0x0083,0xC8DE,0xB0F3,0x0083,0xE7AA,0xBEAD,0x0089,0x0085,0x0083,0xD2EF,0xC9DC,0x0083,0xE7A9,0xE7A8,
+ 0x0085,0x0083,0xB0ED,0xE7A7,0x0083,0xD6D5,0xD6AF,0x0101,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0xCFB8,0xC9F0,
+ 0x0083,0xD7E9,0xC1B7,0x0085,0x0083,0xE7A6,0xE7A5,0x0083,0xE7A4,0xCFDF,0x0089,0x0085,0x0083,0xE7A3,0xC5A6,0x0083,
+ 0xC083,0xC082,0x0085,0x0083,0xB7C4,0xCEC6,0x0083,0xD6BD,0xB7D7,0x0091,0x0089,0x0085,0x0083,0xC2DA,0xD7DD,0x0083,
+ 0xC081,0xC4C9,0x0085,0x0083,0xB8D9,0xC9B4,0x0083,0xE7A2,0xB4BF,0x0089,0x0085,0x0083,0xC080,0xE7A1,0x0083,0xCEB3,
+ 0xC8D2,0x0085,0x0083,0xBCCD,0xE6FE,0x0083,0xE6FD,0xBCB6,0x00A1,0x0091,0x0089,0x0085,0x0083,0xD4BC,0xE6FC,0x0083,
+ 0xCFCB,0xE6FB,0x0085,0x0083,0xBAEC,0xE6FA,0x0083,0xBEC0,0xE6F9,0x0089,0x0085,0x0083,0xC07E,0xC07D,0x0083,0xC07C,
+ 0xF4EE,0x0085,0x0083,0xC07B,0xC07A,0x0083,0xC079,0xC078,0x0091,0x0089,0x0085,0x0083,0xC077,0xC076,0x0083,0xC075,
+ 0xC074,0x0085,0x0083,0xC073,0xC072,0x0083,0xC071,0xC070,0x0089,0x0085,0x0083,0xC06F,0xC06E,0x0083,0xC06D,0xC06C,
+ 0x0085,0x0083,0xC06B,0xC06A,0x0083,0xC069,0xC068,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0xC067,0xC066,0x0083,
+ 0xC065,0xC064,0x0085,0x0083,0xD7EB,0xC063,0x0083,0xC062,0xC061,0x0089,0x0085,0x0083,0xC060,0xC05F,0x0083,0xC05E,
+ 0xC05D,0x0085,0x0083,0xC05C,0xC05B,0x0083,0xC05A,0xC059,0x0091,0x0089,0x0085,0x0083,0xC058,0xC057,0x0083,0xC056,
+ 0xC055,0x0085,0x0083,0xC054,0xC053,0x0083,0xC052,0xC051,0x0089,0x0085,0x0083,0xC050,0xC04F,0x0083,0xC04E,0xC04D,
+ 0x0085,0x0083,0xC04C,0xC04B,0x0083,0xC04A,0xC049,0x00A1,0x0091,0x0089,0x0085,0x0083,0xC048,0xC047,0x0083,0xC046,
+ 0xC045,0x0085,0x0083,0xC044,0xC043,0x0083,0xC042,0xC041,0x0089,0x0085,0x0083,0xC040,0xBFA0,0x0083,0xBF9F,0xBF9E,
+ 0x0085,0x0083,0xBF9D,0xBF9C,0x0083,0xBF9B,0xBF9A,0x0091,0x0089,0x0085,0x0083,0xBF99,0xBF98,0x0083,0xBF97,0xBF96,
+ 0x0085,0x0083,0xBF95,0xBF94,0x0083,0xBF93,0xBF92,0x0089,0x0085,0x0083,0xBF91,0xBF90,0x0083,0xBF8F,0xBF8E,0x0085,
+ 0x0083,0xBF8D,0xBF8C,0x0083,0xBF8B,0xF4ED,0x027F,0x017F,0x00FF,0x00BF,0x009F,0x008F,0x0087,0x0083,0xBF8A,0x0083,
+ 0xBF89,0xBF88,0x0085,0x0083,0xBF87,0xBF86,0x0083,0xB7B1,0xBF85,0x0089,0x0085,0x0083,0xBF84,0xBF83,0x0083,0xBF82,
+ 0xBF81,0x0085,0x0083,0xF7E3,0xBF80,0x0083,0xBF7E,0xBF7D,0x0091,0x0089,0x0085,0x0083,0xBF7C,0xBF7B,0x0083,0xBF7A,
+ 0xBF79,0x0085,0x0083,0xBF78,0xBF77,0x0083,0xBF76,0xBF75,0x0089,0x0085,0x0083,0xBF74,0xBF73,0x0083,0xBF72,0xBF71,
+ 0x0085,0x0083,0xBF70,0xBF6F,0x0083,0xBF6E,0xBF6D,0x00A1,0x0091,0x0089,0x0085,0x0083,0xBF6C,0xBF6B,0x0083,0xBF6A,
+ 0xBF69,0x0085,0x0083,0xBF68,0xBF67,0x0083,0xBF66,0xBF65,0x0089,0x0085,0x0083,0xBF64,0xBF63,0x0083,0xBF62,0xBF61,
+ 0x0085,0x0083,0xBF60,0xBF5F,0x0083,0xBF5E,0xBF5D,0x0091,0x0089,0x0085,0x0083,0xBF5C,0xBF5B,0x0083,0xBF5A,0xBF59,
+ 0x0085,0x0083,0xBF58,0xBF57,0x0083,0xBF56,0xBF55,0x0089,0x0085,0x0083,0xBF54,0xBF53,0x0083,0xBF52,0xBF51,0x0085,
+ 0x0083,0xBF50,0xBF4F,0x0083,0xBF4E,0xBF4D,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0xBF4C,0xBF4B,0x0083,0xBF4A,
+ 0xBF49,0x0085,0x0083,0xBF48,0xBF47,0x0083,0xBF46,0xBF45,0x0089,0x0085,0x0083,0xBF44,0xBF43,0x0083,0xBF42,0xBF41,
+ 0x0085,0x0083,0xBF40,0xBEA0,0x0083,0xBE9F,0xBE9E,0x0091,0x0089,0x0085,0x0083,0xBE9D,0xBE9C,0x0083,0xBE9B,0xBE9A,
+ 0x0085,0x0083,0xBE99,0xBE98,0x0083,0xBE97,0xBE96,0x0089,0x0085,0x0083,0xBE95,0xBE94,0x0083,0xBE93,0xBE92,0x0085,
+ 0x0083,0xBE91,0xBE90,0x0083,0xBE8F,0xBE8E,0x00A1,0x0091,0x0089,0x0085,0x0083,0xBE8D,0xBE8C,0x0083,0xBE8B,0xBE8A,
+ 0x0085,0x0083,0xBE89,0xBE88,0x0083,0xBE87,0xBE86,0x0089,0x0085,0x0083,0xBE85,0xBE84,0x0083,0xBE83,0xBE82,0x0085,
+ 0x0083,0xBE81,0xBE80,0x0083,0xBE7E,0xBE7D,0x0091,0x0089,0x0085,0x0083,0xBE7C,0xBE7B,0x0083,0xBE7A,0xBE79,0x0085,
+ 0x0083,0xBE78,0xBE77,0x0083,0xBE76,0xBE75,0x0089,0x0085,0x0083,0xBE74,0xBE73,0x0083,0xBE72,0xBE71,0x0085,0x0083,
+ 0xBE70,0xBE6F,0x0083,0xBE6E,0xBE6D,0x0101,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0xBE6C,0xBE6B,0x0083,0xBE6A,
+ 0xBE69,0x0085,0x0083,0xBE68,0xBE67,0x0083,0xBE66,0xBE65,0x0089,0x0085,0x0083,0xBE64,0xBE63,0x0083,0xBE62,0xBE61,
+ 0x0085,0x0083,0xBE60,0xBE5F,0x0083,0xBE5E,0xBE5D,0x0091,0x0089,0x0085,0x0083,0xBE5C,0xBE5B,0x0083,0xBE5A,0xBE59,
+ 0x0085,0x0083,0xBE58,0xBE57,0x0083,0xBE56,0xBE55,0x0089,0x0085,0x0083,0xBE54,0xF4EC,0x0083,0xBE53,0xBE52,0x0085,
+ 0x0083,0xBE51,0xBE50,0x0083,0xBE4F,0xBE4E,0x00A1,0x0091,0x0089,0x0085,0x0083,0xBE4D,0xF4EB,0x0083,0xBE4C,0xBE4B,
+ 0x0085,0x0083,0xBE4A,0xBE49,0x0083,0xBE48,0xBE47,0x0089,0x0085,0x0083,0xBE46,0xBE45,0x0083,0xBE44,0xBE43,0x0085,
+ 0x0083,0xBE42,0xBE41,0x0083,0xBE40,0xBDA0,0x0091,0x0089,0x0085,0x0083,0xBD9F,0xBD9E,0x0083,0xBD9D,0xBD9C,0x0085,
+ 0x0083,0xBD9B,0xBD9A,0x0083,0xBD99,0xBD98,0x0089,0x0085,0x0083,0xBD97,0xBD96,0x0083,0xBD95,0xBD94,0x0085,0x0083,
+ 0xBD93,0xBD92,0x0083,0xBD91,0xBD90,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0xBD8F,0xBD8E,0x0083,0xBD8D,0xBD8C,
+ 0x0085,0x0083,0xBD8B,0xBD8A,0x0083,0xBD89,0xBD88,0x0089,0x0085,0x0083,0xBD87,0xBD86,0x0083,0xBD85,0xBD84,0x0085,
+ 0x0083,0xBD83,0xBD82,0x0083,0xBD81,0xBD80,0x0091,0x0089,0x0085,0x0083,0xF4EA,0xBD7E,0x0083,0xBD7D,0xBD7C,0x0085,
+ 0x0083,0xBD7B,0xBD7A,0x0083,0xBD79,0xBD78,0x0089,0x0085,0x0083,0xBD77,0xD0F5,0x0083,0xBD76,0xBD75,0x0085,0x0083,
+ 0xBD74,0xBD73,0x0083,0xBD72,0xBD71,0x00A1,0x0091,0x0089,0x0085,0x0083,0xBD70,0xBD6F,0x0083,0xBD6E,0xBD6D,0x0085,
+ 0x0083,0xBD6C,0xBD6B,0x0083,0xBD6A,0xBD69,0x0089,0x0085,0x0083,0xBD68,0xBD67,0x0083,0xBD66,0xBD65,0x0085,0x0083,
+ 0xBD64,0xBD63,0x0083,0xBD62,0xBD61,0x0091,0x0089,0x0085,0x0083,0xBD60,0xBD5F,0x0083,0xBD5E,0xBD5D,0x0085,0x0083,
+ 0xBD5C,0xBD5B,0x0083,0xBD5A,0xBD59,0x0089,0x0085,0x0083,0xBD58,0xBD57,0x0083,0xBD56,0xBD55,0x0085,0x0083,0xBD54,
+ 0xBD53,0x0083,0xBD52,0xBD51,0x0181,0x0101,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0xBD50,0xBD4F,0x0083,0xBD4E,
+ 0xBD4D,0x0085,0x0083,0xBD4C,0xBD4B,0x0083,0xBD4A,0xBD49,0x0089,0x0085,0x0083,0xBD48,0xBD47,0x0083,0xBD46,0xBD45,
+ 0x0085,0x0083,0xBD44,0xBD43,0x0083,0xBD42,0xBD41,0x0091,0x0089,0x0085,0x0083,0xBD40,0xBCA0,0x0083,0xBC9F,0xBC9E,
+ 0x0085,0x0083,0xBC9D,0xBC9C,0x0083,0xBC9B,0xBC9A,0x0089,0x0085,0x0083,0xC0DB,0xBC99,0x0083,0xBC98,0xBC97,0x0085,
+ 0x0083,0xD7CF,0xBC96,0x0083,0xBC95,0xBC94,0x00A1,0x0091,0x0089,0x0085,0x0083,0xBDF4,0xBC93,0x0083,0xBC92,0xBC91,
+ 0x0085,0x0083,0xBC90,0xCBF7,0x0083,0xBC8F,0xCBD8,0x0089,0x0085,0x0083,0xBC8E,0xBC8D,0x0083,0xBC8C,0xBC8B,0x0085,
+ 0x0083,0xBC8A,0xBC89,0x0083,0xBC88,0xBC87,0x0091,0x0089,0x0085,0x0083,0xBC86,0xBC85,0x0083,0xBC84,0xBC83,0x0085,
+ 0x0083,0xBC82,0xBC81,0x0083,0xBC80,0xBC7E,0x0089,0x0085,0x0083,0xBC7D,0xBC7C,0x0083,0xBC7B,0xBC7A,0x0085,0x0083,
+ 0xBC79,0xCEC9,0x0083,0xBC78,0xBC77,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0xBC76,0xBC75,0x0083,0xBC74,0xBC73,
+ 0x0085,0x0083,0xBC72,0xBC71,0x0083,0xBC70,0xBC6F,0x0089,0x0085,0x0083,0xBC6E,0xBC6D,0x0083,0xBC6C,0xBC6B,0x0085,
+ 0x0083,0xCFB5,0xBC6A,0x0083,0xBC69,0xF4E9,0x0091,0x0089,0x0085,0x0083,0xBC68,0xBC67,0x0083,0xBC66,0xBC65,0x0085,
+ 0x0083,0xBC64,0xBC63,0x0083,0xBC62,0xBC61,0x0089,0x0085,0x0083,0xC5B4,0xBC60,0x0083,0xBC5F,0xBC5E,0x0085,0x0083,
+ 0xBC5D,0xBC5C,0x0083,0xBC5B,0xF4DD,0x00A1,0x0091,0x0089,0x0085,0x0083,0xBC5A,0xBC59,0x0083,0xBC58,0xBC57,0x0085,
+ 0x0083,0xBC56,0xBC55,0x0083,0xBC54,0xBFB7,0x0089,0x0085,0x0083,0xD4E3,0xBC53,0x0083,0xBC52,0xC3D3,0x0085,0x0083,
+ 0xBC51,0xBC50,0x0083,0xB2DA,0xBC4F,0x0091,0x0089,0x0085,0x0083,0xF4DC,0xCCC7,0x0083,0xB8E2,0xBC4E,0x0085,0x0083,
+ 0xBC4D,0xBC4C,0x0083,0xBC4B,0xBC4A,0x0089,0x0085,0x0083,0xBC49,0xBC48,0x0083,0xF4D9,0xF4D8,0x0085,0x0083,0xBC47,
+ 0xBAFD,0x0083,0xBC46,0xF4DA,0x0101,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0xF4D7,0xBC45,0x0083,0xF4DB,0xBC44,
+ 0x0085,0x0083,0xBC43,0xBC42,0x0083,0xF4D6,0xBC41,0x0089,0x0085,0x0083,0xBC40,0xBEAB,0x0083,0xF4D5,0xF4D4,0x0085,
+ 0x0083,0xBBA0,0xBB9F,0x0083,0xB4E2,0xBB9E,0x0091,0x0089,0x0085,0x0083,0xBB9D,0xBB9C,0x0083,0xBB9B,0xBB9A,0x0085,
+ 0x0083,0xBEAC,0xF4D3,0x0083,0xC1BB,0xBB99,0x0089,0x0085,0x0083,0xBB98,0xC1B8,0x0083,0xBB97,0xBB96,0x0085,0x0083,
+ 0xBB95,0xB7E0,0x0083,0xBB94,0xBB93,0x00A1,0x0091,0x0089,0x0085,0x0083,0xBB92,0xBB91,0x0083,0xD6E0,0xD4C1,0x0085,
+ 0x0083,0xBB90,0xF4D2,0x0083,0xBB8F,0xBB8E,0x0089,0x0085,0x0083,0xCBDA,0xF4D1,0x0083,0xF4CF,0xF4D0,0x0085,0x0083,
+ 0xBB8D,0xBB8C,0x0083,0xBB8B,0xD5B3,0x0091,0x0089,0x0085,0x0083,0xB4D6,0xBB8A,0x0083,0xC6C9,0xBB89,0x0085,0x0083,
+ 0xBB88,0xC1A3,0x0083,0xF4CE,0xBB87,0x0089,0x0085,0x0083,0xBB86,0xBB85,0x0083,0xBB84,0xBB83,0x0085,0x0083,0xBB82,
+ 0xBB81,0x0083,0xB7DB,0xBB80,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0xBB7E,0xBB7D,0x0083,0xBB7C,0xBB7B,0x0085,
+ 0x0083,0xBB7A,0xBB79,0x0083,0xBB78,0xBB77,0x0089,0x0085,0x0083,0xBB76,0xBB75,0x0083,0xD7D1,0xF4CC,0x0085,0x0083,
+ 0xC0E0,0xBB74,0x0083,0xBB73,0xBB72,0x0091,0x0089,0x0085,0x0083,0xBB71,0xBB70,0x0083,0xBB6F,0xD9E1,0x0085,0x0083,
+ 0xC3D7,0xBB6E,0x0083,0xBB6D,0xBB6C,0x0089,0x0085,0x0083,0xBB6B,0xBB6A,0x0083,0xBB69,0xBB68,0x0085,0x0083,0xBB67,
+ 0xBB66,0x0083,0xBB65,0xBB64,0x00A1,0x0091,0x0089,0x0085,0x0083,0xBB63,0xBB62,0x0083,0xBB61,0xBB60,0x0085,0x0083,
+ 0xBB5F,0xBB5E,0x0083,0xBB5D,0xBB5C,0x0089,0x0085,0x0083,0xBB5B,0xBB5A,0x0083,0xBB59,0xBB58,0x0085,0x0083,0xBB57,
+ 0xBB56,0x0083,0xBB55,0xBB54,0x0091,0x0089,0x0085,0x0083,0xBB53,0xBB52,0x0083,0xBB51,0xBB50,0x0085,0x0083,0xBB4F,
+ 0xBB4E,0x0083,0xBB4D,0xBB4C,0x0089,0x0085,0x0083,0xBB4B,0xBB4A,0x0083,0xBCAE,0xBB49,0x0085,0x0083,0xBB48,0xBB47,
+ 0x0083,0xBB46,0xBB45,0x047D,0x027F,0x017F,0x00FF,0x00BF,0x009F,0x008F,0x0087,0x0083,0xBB44,0x0083,0xBB43,0xBB42,
+ 0x0085,0x0083,0xBB41,0xBB40,0x0083,0xBAA0,0xF4A5,0x0089,0x0085,0x0083,0xF4A6,0xB2BE,0x0083,0xBA9F,0xBA9E,0x0085,
+ 0x0083,0xBA9D,0xBA9C,0x0083,0xBA9B,0xBA9A,0x0091,0x0089,0x0085,0x0083,0xF4A4,0xBA99,0x0083,0xBA98,0xBA97,0x0085,
+ 0x0083,0xBA96,0xBA95,0x0083,0xBA94,0xBA93,0x0089,0x0085,0x0083,0xBA92,0xBA91,0x0083,0xBA90,0xBA8F,0x0085,0x0083,
+ 0xBA8E,0xBA8D,0x0083,0xF4A2,0xBA8C,0x00A1,0x0091,0x0089,0x0085,0x0083,0xBA8B,0xBBC9,0x0083,0xF4A3,0xBA8A,0x0085,
+ 0x0083,0xBA89,0xBA88,0x0083,0xBA87,0xBA86,0x0089,0x0085,0x0083,0xBA85,0xF4A1,0x0083,0xBA84,0xBA83,0x0085,0x0083,
+ 0xBA82,0xBA81,0x0083,0xBA80,0xBA7E,0x0091,0x0089,0x0085,0x0083,0xBA7D,0xBA7C,0x0083,0xF3FD,0xBA7B,0x0085,0x0083,
+ 0xBA7A,0xBA79,0x0083,0xBA78,0xBA77,0x0089,0x0085,0x0083,0xBA76,0xF3FC,0x0083,0xBA75,0xBA74,0x0085,0x0083,0xF3F9,
+ 0xF3FE,0x0083,0xBA73,0xBA72,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0xBA71,0xB4D8,0x0083,0xBA70,0xBA6F,0x0085,
+ 0x0083,0xBA6E,0xBA6D,0x0083,0xBA6C,0xBA6B,0x0089,0x0085,0x0083,0xBA6A,0xBA69,0x0083,0xF3FA,0xBA68,0x0085,0x0083,
+ 0xF3FB,0xBA67,0x0083,0xBA66,0xBA65,0x0091,0x0089,0x0085,0x0083,0xBA64,0xC5F1,0x0083,0xBA63,0xBA62,0x0085,0x0083,
+ 0xBA61,0xBA60,0x0083,0xBA5F,0xC0E9,0x0089,0x0085,0x0083,0xBA5E,0xBA5D,0x0083,0xC0BA,0xBA5C,0x0085,0x0083,0xBA5B,
+ 0xBA5A,0x0083,0xF3F8,0xBA59,0x00A1,0x0091,0x0089,0x0085,0x0083,0xBA58,0xBA57,0x0083,0xF3F7,0xF3F6,0x0085,0x0083,
+ 0xBA56,0xBA55,0x0083,0xBA54,0xB4DB,0x0089,0x0085,0x0083,0xBA53,0xBA52,0x0083,0xBA51,0xF3F4,0x0085,0x0083,0xBA50,
+ 0xBA4F,0x0083,0xF3F5,0xB8DD,0x0091,0x0089,0x0085,0x0083,0xBA4E,0xBA4D,0x0083,0xBA4C,0xBA4B,0x0085,0x0083,0xBA4A,
+ 0xC2A8,0x0083,0xBA49,0xF3F1,0x0089,0x0085,0x0083,0xBA48,0xBA47,0x0083,0xBA46,0xBA45,0x0085,0x0083,0xF3F3,0xBA44,
+ 0x0083,0xBA43,0xBA42,0x0101,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0xBA41,0xC6AA,0x0083,0xD7AD,0xBA40,0x0085,
+ 0x0083,0xB9A0,0xB99F,0x0083,0xB99E,0xF3F2,0x0089,0x0085,0x0083,0xB99D,0xB99C,0x0083,0xB99B,0xB99A,0x0085,0x0083,
+ 0xB999,0xB998,0x0083,0xB997,0xB996,0x0091,0x0089,0x0085,0x0083,0xF3E7,0xB995,0x0083,0xB994,0xB993,0x0085,0x0083,
+ 0xF3F0,0xB992,0x0083,0xB991,0xCFE4,0x0089,0x0085,0x0083,0xB990,0xB98F,0x0083,0xB98E,0xBCFD,0x0085,0x0083,0xF3E8,
+ 0xF3EF,0x0083,0xF3EC,0xC2E1,0x00A1,0x0091,0x0089,0x0085,0x0083,0xF3EA,0xF3E6,0x0083,0xF3E5,0xB98D,0x0085,0x0083,
+ 0xB98C,0xB98B,0x0083,0xF3EE,0xB9DC,0x0089,0x0085,0x0083,0xB98A,0xB989,0x0083,0xB988,0xF3E9,0x0085,0x0083,0xF3ED,
+ 0xB987,0x0083,0xB986,0xB985,0x0091,0x0089,0x0085,0x0083,0xB984,0xCBE3,0x0083,0xB983,0xBBFE,0x0085,0x0083,0xB2AD,
+ 0xB982,0x0083,0xB981,0xB980,0x0089,0x0085,0x0083,0xF3E4,0xB97E,0x0083,0xB97D,0xB9BF,0x0085,0x0083,0xB97C,0xB97B,
+ 0x0083,0xB97A,0xB979,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0xB978,0xB977,0x0083,0xB976,0xF3EB,0x0085,0x0083,
+ 0xB975,0xB974,0x0083,0xB973,0xB972,0x0089,0x0085,0x0083,0xBCF2,0xB971,0x0083,0xC7A9,0xB970,0x0085,0x0083,0xB96F,
+ 0xF3E0,0x0083,0xB96E,0xB3EF,0x0091,0x0089,0x0085,0x0083,0xB96D,0xBFEA,0x0083,0xB96C,0xF3DB,0x0085,0x0083,0xB96B,
+ 0xB96A,0x0083,0xF3E2,0xF3E3,0x0089,0x0085,0x0083,0xB969,0xB968,0x0083,0xF3DF,0xB967,0x0085,0x0083,0xB966,0xB965,
+ 0x0083,0xB964,0xB963,0x00A1,0x0091,0x0089,0x0085,0x0083,0xB962,0xB961,0x0083,0xB960,0xB95F,0x0085,0x0083,0xB95E,
+ 0xB95D,0x0083,0xF3E1,0xB95C,0x0089,0x0085,0x0083,0xF3DE,0xB95B,0x0083,0xB95A,0xF3DD,0x0085,0x0083,0xB959,0xC9B8,
+ 0x0083,0xF3D9,0xB958,0x0091,0x0089,0x0085,0x0083,0xF3D8,0xB957,0x0083,0xB2DF,0xB956,0x0085,0x0083,0xB4F0,0xB955,
+ 0x0083,0xCDB2,0xD6FE,0x0089,0x0085,0x0083,0xBFF0,0xB7A4,0x0083,0xB954,0xB953,0x0085,0x0083,0xF3DC,0xBDEE,0x0083,
+ 0xB952,0xB5C8,0x017F,0x00FF,0x00BF,0x009F,0x008F,0x0087,0x0083,0xB951,0x0083,0xF3CC,0xB950,0x0085,0x0083,0xF3DA,
+ 0xB94F,0x0083,0xB94E,0xB94D,0x0089,0x0085,0x0083,0xB94C,0xB94B,0x0083,0xB94A,0xF3D6,0x0085,0x0083,0xB949,0xC1FD,
+ 0x0083,0xB948,0xBCE3,0x0091,0x0089,0x0085,0x0083,0xB947,0xF3CD,0x0083,0xB946,0xB945,0x0085,0x0083,0xB944,0xB943,
+ 0x0083,0xF3D5,0xB942,0x0089,0x0085,0x0083,0xF3D1,0xB941,0x0083,0xB940,0xF3D0,0x0085,0x0083,0xB8A0,0xB5DA,0x0083,
+ 0xF3CA,0xF3CE,0x00A1,0x0091,0x0089,0x0085,0x0083,0xB89F,0xB1BF,0x0083,0xB89E,0xB7FB,0x0085,0x0083,0xF3D3,0xF3D4,
+ 0x0083,0xB89D,0xB89C,0x0089,0x0085,0x0083,0xB89B,0xF3D2,0x0083,0xB89A,0xF3D7,0x0085,0x0083,0xB899,0xB898,0x0083,
+ 0xB5D1,0xB897,0x0091,0x0089,0x0085,0x0083,0xF3CF,0xB896,0x0083,0xB895,0xB894,0x0085,0x0083,0xF3C8,0xB1CA,0x0083,
+ 0xB893,0xB892,0x0089,0x0085,0x0083,0xD0A6,0xB891,0x0083,0xF3CB,0xB890,0x0085,0x0083,0xB88F,0xB88E,0x0083,0xCBF1,
+ 0xF3C9,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0xB88D,0xF3C5,0x0083,0xB88C,0xB0CA,0x0085,0x0083,0xB88B,0xF3C7,
+ 0x0083,0xF3C6,0xB88A,0x0089,0x0085,0x0083,0xB889,0xB888,0x0083,0xB8CD,0xB887,0x0085,0x0083,0xF3C4,0xB886,0x0083,
+ 0xB885,0xF3C3,0x0091,0x0089,0x0085,0x0083,0xD6F1,0xB884,0x0083,0xB883,0xB882,0x0085,0x0083,0xB881,0xB880,0x0083,
+ 0xB87E,0xB87D,0x0089,0x0085,0x0083,0xB87C,0xB87B,0x0083,0xB6CB,0xB87A,0x0085,0x0083,0xBDDF,0xB879,0x0083,0xB878,
+ 0xB877,0x00A1,0x0091,0x0089,0x0085,0x0083,0xB876,0xB875,0x0083,0xB874,0xF1B5,0x0085,0x0083,0xCDAF,0xB873,0x0083,
+ 0xBFA2,0xB872,0x0089,0x0085,0x0083,0xB871,0xD5C2,0x0083,0xBEB9,0xBEBA,0x0085,0x0083,0xB870,0xB86F,0x0083,0xB86E,
+ 0xB86D,0x0091,0x0089,0x0085,0x0083,0xD5BE,0xB86C,0x0083,0xB86B,0xCAFA,0x0085,0x0083,0xB86A,0xB869,0x0083,0xB868,
+ 0xB867,0x0089,0x0085,0x0083,0xB866,0xB865,0x0083,0xB864,0xB863,0x0085,0x0083,0xB862,0xB861,0x0083,0xC1A2,0xB860,
+ 0x0101,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0xB85F,0xB85E,0x0083,0xB85D,0xB85C,0x0085,0x0083,0xB85B,0xB85A,
+ 0x0083,0xB859,0xB858,0x0089,0x0085,0x0083,0xB857,0xB856,0x0083,0xC1FE,0xB855,0x0085,0x0083,0xB854,0xB853,0x0083,
+ 0xB852,0xB851,0x0091,0x0089,0x0085,0x0083,0xB850,0xB84F,0x0083,0xB84E,0xB84D,0x0085,0x0083,0xB84C,0xB84B,0x0083,
+ 0xF1C1,0xB84A,0x0089,0x0085,0x0083,0xB849,0xB848,0x0083,0xB847,0xB846,0x0085,0x0083,0xF1C0,0xF1BE,0x0083,0xB845,
+ 0xB844,0x00A1,0x0091,0x0089,0x0085,0x0083,0xB843,0xF1BF,0x0083,0xB842,0xF1BC,0x0085,0x0083,0xBFFA,0xB841,0x0083,
+ 0xB840,0xB7A0,0x0089,0x0085,0x0083,0xB79F,0xF1BD,0x0083,0xBFDF,0xB79E,0x0085,0x0083,0xCED1,0xB4DC,0x0083,0xB79D,
+ 0xB79C,0x0091,0x0089,0x0085,0x0083,0xB79B,0xBEBD,0x0083,0xB4B0,0xBDD1,0x0085,0x0083,0xF1BB,0xB79A,0x0083,0xB799,
+ 0xD6CF,0x0089,0x0085,0x0083,0xD2A4,0xB798,0x0083,0xB797,0xB796,0x0085,0x0083,0xC7CF,0xB795,0x0083,0xB794,0xB793,
+ 0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0xB792,0xF1BA,0x0083,0xB791,0xF1B9,0x0085,0x0083,0xB790,0xD5AD,0x0083,
+ 0xC7D4,0xB78F,0x0089,0x0085,0x0083,0xCDBB,0xF1B8,0x0083,0xB4A9,0xB78E,0x0085,0x0083,0xB78D,0xB78C,0x0083,0xB78B,
+ 0xBFD5,0x0091,0x0089,0x0085,0x0083,0xF1B7,0xF1B6,0x0083,0xC7EE,0xBEBF,0x0085,0x0083,0xB78A,0xD1A8,0x0083,0xB789,
+ 0xB788,0x0089,0x0085,0x0083,0xB787,0xF0A6,0x0083,0xB786,0xB785,0x0085,0x0083,0xB784,0xB783,0x0083,0xB782,0xB781,
+ 0x00A1,0x0091,0x0089,0x0085,0x0083,0xB780,0xB77E,0x0083,0xB77D,0xB77C,0x0085,0x0083,0xB77B,0xB77A,0x0083,0xB779,
+ 0xB778,0x0089,0x0085,0x0083,0xB777,0xB776,0x0083,0xB775,0xB774,0x0085,0x0083,0xB773,0xB772,0x0083,0xB771,0xB770,
+ 0x0091,0x0089,0x0085,0x0083,0xB76F,0xB76E,0x0083,0xCBEB,0xB76D,0x0085,0x0083,0xB76C,0xB76B,0x0083,0xB76A,0xB769,
+ 0x0089,0x0085,0x0083,0xF0A3,0xB768,0x0083,0xB767,0xB766,0x0085,0x0083,0xB765,0xB764,0x0083,0xB763,0xB762,0x027F,
+ 0x017F,0x00FF,0x00BF,0x009F,0x008F,0x0087,0x0083,0xB761,0x0083,0xB760,0xB75F,0x0085,0x0083,0xC4C2,0xB75E,0x0083,
+ 0xB75D,0xB75C,0x0089,0x0085,0x0083,0xB75B,0xB75A,0x0083,0xB759,0xB8E5,0x0085,0x0083,0xB758,0xBBFC,0x0083,0xBCDA,
+ 0xB5BE,0x0091,0x0089,0x0085,0x0083,0xB757,0xF0A1,0x0083,0xB756,0xF0A2,0x0085,0x0083,0xB755,0xB754,0x0083,0xB753,
+ 0xCEC8,0x0089,0x0085,0x0083,0xB752,0xB751,0x0083,0xB750,0xB74F,0x0085,0x0083,0xB74E,0xB74D,0x0083,0xB74C,0xB74B,
+ 0x00A1,0x0091,0x0089,0x0085,0x0083,0xB74A,0xB749,0x0083,0xB748,0xB747,0x0085,0x0083,0xB746,0xB745,0x0083,0xB744,
+ 0xF6D5,0x0089,0x0085,0x0083,0xB743,0xB742,0x0083,0xB3ED,0xB741,0x0085,0x0083,0xEFFD,0xB740,0x0083,0xB6A0,0xB69F,
+ 0x0091,0x0089,0x0085,0x0083,0xD6C9,0xB69E,0x0083,0xB69D,0xB0DE,0x0085,0x0083,0xB69C,0xB69B,0x0083,0xEFFE,0xB69A,
+ 0x0089,0x0085,0x0083,0xB699,0xB698,0x0083,0xB697,0xB696,0x0085,0x0083,0xCBB0,0xC9D4,0x0083,0xB695,0xB3CC,0x00C1,
+ 0x00A1,0x0091,0x0089,0x0085,0x0083,0xB694,0xB693,0x0083,0xB692,0xB691,0x0085,0x0083,0xEFF9,0xB690,0x0083,0xB68F,
+ 0xEFFB,0x0089,0x0085,0x0083,0xEFFC,0xB68E,0x0083,0xCFA1,0xB68D,0x0085,0x0083,0xB68C,0xBBE0,0x0083,0xB68B,0xD2C6,
+ 0x0091,0x0089,0x0085,0x0083,0xB68A,0xB689,0x0083,0xBDD5,0xB688,0x0085,0x0083,0xB687,0xB686,0x0083,0xB685,0xB684,
+ 0x0089,0x0085,0x0083,0xB683,0xB682,0x0083,0xB3C6,0xBBFD,0x0085,0x0083,0xB681,0xEFF6,0x0083,0xB680,0xEFF8,0x00A1,
+ 0x0091,0x0089,0x0085,0x0083,0xB67E,0xD6C8,0x0083,0xB67D,0xD1ED,0x0085,0x0083,0xC7D8,0xB67C,0x0083,0xB3D3,0xEFF7,
+ 0x0089,0x0085,0x0083,0xB67B,0xB67A,0x0083,0xB679,0xD7E2,0x0085,0x0083,0xB678,0xB677,0x0083,0xB676,0xB675,0x0091,
+ 0x0089,0x0085,0x0083,0xB674,0xB673,0x0083,0xC3D8,0xB672,0x0085,0x0083,0xB671,0xEFF5,0x0083,0xB670,0xB66F,0x0089,
+ 0x0085,0x0083,0xC3EB,0xBFC6,0x0083,0xB66E,0xB66D,0x0085,0x0083,0xB66C,0xD6D6,0x0083,0xB66B,0xC7EF,0x0101,0x00C1,
+ 0x00A1,0x0091,0x0089,0x0085,0x0083,0xB66A,0xB1FC,0x0083,0xB669,0xB668,0x0085,0x0083,0xB8D1,0xB667,0x0083,0xB666,
+ 0xCDBA,0x0089,0x0085,0x0083,0xB665,0xCBBD,0x0083,0xD0E3,0xB664,0x0085,0x0083,0xBACC,0xC7DD,0x0083,0xB663,0xC0EB,
+ 0x0091,0x0089,0x0085,0x0083,0xD8AE,0xD3ED,0x0083,0xB662,0xB661,0x0085,0x0083,0xB660,0xB65F,0x0083,0xB65E,0xECFC,
+ 0x0089,0x0085,0x0083,0xB65D,0xB65C,0x0083,0xB65B,0xB65A,0x0085,0x0083,0xB659,0xB658,0x0083,0xB657,0xB656,0x00A1,
+ 0x0091,0x0089,0x0085,0x0083,0xB655,0xB654,0x0083,0xB653,0xECFB,0x0085,0x0083,0xB652,0xB651,0x0083,0xB650,0xB64F,
+ 0x0089,0x0085,0x0083,0xB64E,0xB64D,0x0083,0xB64C,0xB64B,0x0085,0x0083,0xB64A,0xB649,0x0083,0xB648,0xB647,0x0091,
+ 0x0089,0x0085,0x0083,0xECFA,0xB646,0x0083,0xB645,0xB644,0x0085,0x0083,0xB643,0xB642,0x0083,0xB641,0xB640,0x0089,
+ 0x0085,0x0083,0xB5A0,0xB59F,0x0083,0xB59E,0xB8A3,0x0085,0x0083,0xB59D,0xB59C,0x0083,0xB59B,0xB59A,0x00C1,0x00A1,
+ 0x0091,0x0089,0x0085,0x0083,0xECF9,0xB599,0x0083,0xB598,0xB597,0x0085,0x0083,0xB596,0xECF8,0x0083,0xC2BB,0xB595,
+ 0x0089,0x0085,0x0083,0xB594,0xBDFB,0x0083,0xD9F7,0xB593,0x0085,0x0083,0xB592,0xB591,0x0083,0xB590,0xB58F,0x0091,
+ 0x0089,0x0085,0x0083,0xECF7,0xB58E,0x0083,0xBBF6,0xB5BB,0x0085,0x0083,0xB58D,0xB58C,0x0083,0xB58B,0xB58A,0x0089,
+ 0x0085,0x0083,0xB589,0xB588,0x0083,0xB587,0xECF5,0x0085,0x0083,0xB586,0xBCC0,0x0083,0xB585,0xB584,0x00A1,0x0091,
+ 0x0089,0x0085,0x0083,0xB583,0xB582,0x0083,0xC6B1,0xECF6,0x0085,0x0083,0xB581,0xCFE9,0x0083,0xB580,0xB57E,0x0089,
+ 0x0085,0x0083,0xECF2,0xB57D,0x0083,0xECF4,0xCBEE,0x0085,0x0083,0xC9F1,0xD7A3,0x0083,0xECEF,0xECEE,0x0091,0x0089,
+ 0x0085,0x0083,0xECF1,0xB57C,0x0083,0xB57B,0xECF3,0x0085,0x0083,0xD7E6,0xB57A,0x0083,0xB579,0xECF0,0x0089,0x0085,
+ 0x0083,0xB578,0xB577,0x0083,0xB576,0xB575,0x0085,0x0083,0xB574,0xB573,0x0083,0xB572,0xB571,0x0181,0x0101,0x00C1,
+ 0x00A1,0x0091,0x0089,0x0085,0x0083,0xB570,0xECED,0x0083,0xC6ED,0xB56F,0x0085,0x0083,0xECEC,0xB56E,0x0083,0xB56D,
+ 0xB56C,0x0089,0x0085,0x0083,0xB56B,0xC6EE,0x0083,0xECEB,0xB56A,0x0085,0x0083,0xC9E7,0xB569,0x0083,0xC0F1,0xECEA,
+ 0x0091,0x0089,0x0085,0x0083,0xCABE,0xB568,0x0083,0xB567,0xB566,0x0085,0x0083,0xB565,0xB564,0x0083,0xEDE7,0xB563,
+ 0x0089,0x0085,0x0083,0xB562,0xB561,0x0083,0xB560,0xB55F,0x0085,0x0083,0xB55E,0xB55D,0x0083,0xB55C,0xB55B,0x00A1,
+ 0x0091,0x0089,0x0085,0x0083,0xB55A,0xB559,0x0083,0xB558,0xB557,0x0085,0x0083,0xB556,0xB555,0x0083,0xEDE5,0xB554,
+ 0x0089,0x0085,0x0083,0xB553,0xB552,0x0083,0xB551,0xB550,0x0085,0x0083,0xEDE6,0xB54F,0x0083,0xB54E,0xB54D,0x0091,
+ 0x0089,0x0085,0x0083,0xB54C,0xB54B,0x0083,0xB54A,0xB549,0x0085,0x0083,0xB548,0xB547,0x0083,0xB546,0xEDE4,0x0089,
+ 0x0085,0x0083,0xB545,0xB544,0x0083,0xB543,0xB542,0x0085,0x0083,0xB541,0xB540,0x0083,0xB4A0,0xB49F,0x00C1,0x00A1,
+ 0x0091,0x0089,0x0085,0x0083,0xB49E,0xB49D,0x0083,0xB49C,0xB49B,0x0085,0x0083,0xB49A,0xEDE2,0x0083,0xB499,0xB498,
+ 0x0089,0x0085,0x0083,0xB497,0xBDB8,0x0083,0xB496,0xB495,0x0085,0x0083,0xB494,0xB493,0x0083,0xB492,0xB491,0x0091,
+ 0x0089,0x0085,0x0083,0xBBC7,0xB490,0x0083,0xB48F,0xC1D7,0x0085,0x0083,0xB48E,0xB48D,0x0083,0xEDE3,0xB48C,0x0089,
+ 0x0085,0x0083,0xEDE1,0xB48B,0x0083,0xB48A,0xB489,0x0085,0x0083,0xB488,0xB487,0x0083,0xEDE0,0xB486,0x00A1,0x0091,
+ 0x0089,0x0085,0x0083,0xB485,0xB484,0x0083,0xC4A5,0xB483,0x0085,0x0083,0xB482,0xB481,0x0083,0xB480,0xB47E,0x0089,
+ 0x0085,0x0083,0xB47D,0xB47C,0x0083,0xB47B,0xB47A,0x0085,0x0083,0xB479,0xB478,0x0083,0xB477,0xB476,0x0091,0x0089,
+ 0x0085,0x0083,0xB475,0xEDDE,0x0083,0xB474,0xB473,0x0085,0x0083,0xB472,0xBFC4,0x0083,0xEDDD,0xB471,0x0089,0x0085,
+ 0x0083,0xB470,0xB46F,0x0083,0xC5CD,0xB46E,0x0085,0x0083,0xB46D,0xB46C,0x0083,0xB46B,0xB4E8,0x0101,0x00C1,0x00A1,
+ 0x0091,0x0089,0x0085,0x0083,0xC0DA,0xEDDF,0x0083,0xB46A,0xB469,0x0085,0x0083,0xB468,0xB0F5,0x0083,0xB467,0xB466,
+ 0x0089,0x0085,0x0083,0xB465,0xB4C5,0x0083,0xB464,0xB463,0x0085,0x0083,0xC4EB,0xB462,0x0083,0xB461,0xB460,0x0091,
+ 0x0089,0x0085,0x0083,0xB45F,0xEDDB,0x0083,0xB45E,0xB45D,0x0085,0x0083,0xB45C,0xB45B,0x0083,0xB2EA,0xCCBC,0x0089,
+ 0x0085,0x0083,0xEDDA,0xBCEE,0x0083,0xC5F6,0xB45A,0x0085,0x0083,0xB459,0xB458,0x0083,0xB457,0xB456,0x00A1,0x0091,
+ 0x0089,0x0085,0x0083,0xB455,0xB454,0x0083,0xB453,0xB1CC,0x0085,0x0083,0xB452,0xEDDC,0x0083,0xB451,0xEDD9,0x0089,
+ 0x0085,0x0083,0xB450,0xEDD8,0x0083,0xB44F,0xB5FA,0x0085,0x0083,0xB44E,0xB44D,0x0083,0xEDD7,0xEDD3,0x0091,0x0089,
+ 0x0085,0x0083,0xEDD5,0xB44C,0x0083,0xB5E2,0xCDEB,0x0085,0x0083,0xB44B,0xB44A,0x0083,0xB449,0xEDD4,0x0089,0x0085,
+ 0x0083,0xB448,0xB1AE,0x0083,0xB447,0xB446,0x0085,0x0083,0xCBE9,0xB0AD,0x0083,0xC2B5,0xB445,0x00C1,0x00A1,0x0091,
+ 0x0089,0x0085,0x0083,0xB444,0xB5EF,0x0083,0xB443,0xEDD6,0x0085,0x0083,0xB442,0xB441,0x0083,0xB440,0xB3A0,0x0089,
+ 0x0085,0x0083,0xB39F,0xB39E,0x0083,0xB39D,0xB39C,0x0085,0x0083,0xB39B,0xB39A,0x0083,0xC5F0,0xB399,0x0091,0x0089,
+ 0x0085,0x0083,0xB398,0xB397,0x0083,0xB396,0xBCEF,0x0085,0x0083,0xB395,0xB394,0x0083,0xB393,0xB392,0x0089,0x0085,
+ 0x0083,0xB391,0xB390,0x0083,0xB38F,0xB38E,0x0085,0x0083,0xC8B7,0xEDCB,0x0083,0xD3B2,0xC1F2,0x00A1,0x0091,0x0089,
+ 0x0085,0x0083,0xEDD2,0xB38D,0x0083,0xB38C,0xB38B,0x0085,0x0083,0xB38A,0xB389,0x0083,0xB388,0xB387,0x0089,0x0085,
+ 0x0083,0xB386,0xB385,0x0083,0xB384,0xB383,0x0085,0x0083,0xB382,0xCFF5,0x0083,0xB381,0xB380,0x0091,0x0089,0x0085,
+ 0x0083,0xB37E,0xB37D,0x0083,0xB37C,0xEDCD,0x0085,0x0083,0xEDCC,0xCBB6,0x0083,0xB37B,0xB37A,0x0089,0x0085,0x0083,
+ 0xCEF8,0xB379,0x0083,0xEDCF,0xB378,0x0085,0x0083,0xEDCA,0xB377,0x0083,0xEDD1,0xB376,0x087B,0x047D,0x027F,0x017F,
+ 0x00FF,0x00BF,0x009F,0x008F,0x0087,0x0083,0xB375,0x0083,0xB374,0xB373,0x0085,0x0083,0xEDD0,0xB372,0x0083,0xB9E8,
+ 0xB371,0x0089,0x0085,0x0083,0xB370,0xB36F,0x0083,0xB36E,0xB4A1,0x0085,0x0083,0xB36D,0xC0F9,0x0083,0xB36C,0xEDC5,
+ 0x0091,0x0089,0x0085,0x0083,0xEDC3,0xEDC2,0x0083,0xEDC1,0xD4D2,0x0085,0x0083,0xC9E9,0xB36B,0x0083,0xB36A,0xC6C6,
+ 0x0089,0x0085,0x0083,0xB369,0xB368,0x0083,0xB367,0xC5E9,0x0085,0x0083,0xB366,0xB365,0x0083,0xEDBE,0xEDC7,0x00A1,
+ 0x0091,0x0089,0x0085,0x0083,0xB364,0xB363,0x0083,0xEDC9,0xB362,0x0085,0x0083,0xD5E8,0xEDCE,0x0083,0xEDC6,0xB361,
+ 0x0089,0x0085,0x0083,0xEDC8,0xB360,0x0083,0xB35F,0xB35E,0x0085,0x0083,0xEDC4,0xB35D,0x0083,0xEDC0,0xEDBF,0x0091,
+ 0x0089,0x0085,0x0083,0xB35C,0xD1E2,0x0083,0xB35B,0xEDBB,0x0085,0x0083,0xEDBA,0xD7A9,0x0083,0xB35A,0xD1D0,0x0089,
+ 0x0085,0x0083,0xB359,0xC5F8,0x0083,0xEDBC,0xB358,0x0085,0x0083,0xB357,0xB356,0x0083,0xBFB3,0xC6F6,0x00C1,0x00A1,
+ 0x0091,0x0089,0x0085,0x0083,0xB355,0xB354,0x0083,0xEDB9,0xB353,0x0085,0x0083,0xB352,0xB351,0x0083,0xB350,0xB34F,
+ 0x0089,0x0085,0x0083,0xB34E,0xC9B0,0x0083,0xC2EB,0xEDB8,0x0085,0x0083,0xBFF3,0xB7AF,0x0083,0xCEF9,0xB34D,0x0091,
+ 0x0089,0x0085,0x0083,0xB34C,0xB34B,0x0083,0xB34A,0xEDB7,0x0085,0x0083,0xB349,0xEDB6,0x0083,0xB348,0xB347,0x0089,
+ 0x0085,0x0083,0xCAAF,0xB346,0x0083,0xB345,0xB344,0x0085,0x0083,0xB343,0xB0AB,0x0083,0xB6CC,0xEFF3,0x00A1,0x0091,
+ 0x0089,0x0085,0x0083,0xBDC3,0xB342,0x0083,0xBED8,0xB341,0x0085,0x0083,0xEFF2,0xB340,0x0083,0xD6AA,0xB2A0,0x0089,
+ 0x0085,0x0083,0xD2D3,0xCAB8,0x0083,0xB29F,0xB29E,0x0085,0x0083,0xB29D,0xB29C,0x0083,0xB29B,0xF1E6,0x0091,0x0089,
+ 0x0085,0x0083,0xC3AC,0xB29A,0x0083,0xB299,0xB298,0x0085,0x0083,0xB4A3,0xB297,0x0083,0xB296,0xB295,0x0089,0x0085,
+ 0x0083,0xB294,0xB293,0x0083,0xB292,0xB291,0x0085,0x0083,0xB290,0xB28F,0x0083,0xDBC7,0xB28E,0x0101,0x00C1,0x00A1,
+ 0x0091,0x0089,0x0085,0x0083,0xB28D,0xB28C,0x0083,0xB28B,0xB28A,0x0085,0x0083,0xB289,0xB288,0x0083,0xB287,0xB286,
+ 0x0089,0x0085,0x0083,0xB285,0xB284,0x0083,0xB283,0xB282,0x0085,0x0083,0xF6C4,0xB281,0x0083,0xEEAD,0xB280,0x0091,
+ 0x0089,0x0085,0x0083,0xD5B0,0xB27E,0x0083,0xB27D,0xB27C,0x0085,0x0083,0xB27B,0xB27A,0x0083,0xEEAC,0xB279,0x0089,
+ 0x0085,0x0083,0xCDAB,0xB278,0x0083,0xB277,0xEEAB,0x0085,0x0083,0xB276,0xB275,0x0083,0xB274,0xCBB2,0x00A1,0x0091,
+ 0x0089,0x0085,0x0083,0xB273,0xB5C9,0x0083,0xD6F5,0xB272,0x0085,0x0083,0xC7C6,0xB271,0x0083,0xC6B3,0xB270,0x0089,
+ 0x0085,0x0083,0xB26F,0xDEAB,0x0083,0xB26E,0xEEAA,0x0085,0x0083,0xEEA9,0xB26D,0x0083,0xB26C,0xB26B,0x0091,0x0089,
+ 0x0085,0x0083,0xB26A,0xB269,0x0083,0xB268,0xB267,0x0085,0x0083,0xB266,0xB265,0x0083,0xB264,0xB263,0x0089,0x0085,
+ 0x0083,0xB262,0xC2F7,0x0083,0xEEA8,0xB261,0x0085,0x0083,0xB260,0xCFB9,0x0083,0xEEA4,0xEEA7,0x00C1,0x00A1,0x0091,
+ 0x0089,0x0085,0x0083,0xB25F,0xB25E,0x0083,0xB25D,0xB25C,0x0085,0x0083,0xB25B,0xB25A,0x0083,0xB3F2,0xC3E9,0x0089,
+ 0x0085,0x0083,0xB259,0xB258,0x0083,0xB257,0xEEA6,0x0085,0x0083,0xEEA3,0xD8BA,0x0083,0xEEA5,0xB256,0x0091,0x0089,
+ 0x0085,0x0083,0xB255,0xB254,0x0083,0xB6C3,0xB253,0x0085,0x0083,0xB252,0xB251,0x0083,0xB250,0xB24F,0x0089,0x0085,
+ 0x0083,0xB24E,0xB24D,0x0083,0xB24C,0xB24B,0x0085,0x0083,0xB24A,0xB249,0x0083,0xB248,0xB2C7,0x00A1,0x0091,0x0089,
+ 0x0085,0x0083,0xBDDE,0xB247,0x0083,0xB246,0xEDFE,0x0085,0x0083,0xB245,0xC4C0,0x0083,0xEEA2,0xB244,0x0089,0x0085,
+ 0x0083,0xB6BD,0xEEA1,0x0083,0xCBAF,0xB243,0x0085,0x0083,0xB242,0xB241,0x0083,0xB240,0xB1A0,0x0091,0x0089,0x0085,
+ 0x0083,0xBEA6,0xEDFD,0x0083,0xB19F,0xB19E,0x0085,0x0083,0xB19D,0xB19C,0x0083,0xB19B,0xB19A,0x0089,0x0085,0x0083,
+ 0xB199,0xB198,0x0083,0xEDFA,0xEDF9,0x0085,0x0083,0xB197,0xB196,0x0083,0xB195,0xB194,0x017F,0x00FF,0x00BF,0x009F,
+ 0x008F,0x0087,0x0083,0xB193,0x0083,0xB192,0xB191,0x0085,0x0083,0xB190,0xEDFB,0x0083,0xB18F,0xB18E,0x0089,0x0085,
+ 0x0083,0xB18D,0xEDFC,0x0083,0xB18C,0xD5F6,0x0085,0x0083,0xD7C5,0xB18B,0x0083,0xB18A,0xB189,0x0091,0x0089,0x0085,
+ 0x0083,0xD1DB,0xB188,0x0083,0xCCF7,0xB187,0x0085,0x0083,0xEDF8,0xBEEC,0x0083,0xBFF4,0xEDF7,0x0089,0x0085,0x0083,
+ 0xB186,0xB185,0x0083,0xB184,0xB183,0x0085,0x0083,0xB182,0xC3D0,0x0083,0xB181,0xEDF5,0x00A1,0x0091,0x0089,0x0085,
+ 0x0083,0xB180,0xB17E,0x0083,0xB17D,0xD1A3,0x0085,0x0083,0xD5A3,0xB17C,0x0083,0xEDF6,0xB17B,0x0089,0x0085,0x0083,
+ 0xB17A,0xB179,0x0083,0xEDF3,0xB178,0x0085,0x0083,0xC3DF,0xD5E6,0x0083,0xB177,0xB176,0x0091,0x0089,0x0085,0x0083,
+ 0xB175,0xB174,0x0083,0xEDF2,0xEDF4,0x0085,0x0083,0xB173,0xB172,0x0083,0xB171,0xB170,0x0089,0x0085,0x0083,0xB16F,
+ 0xB16E,0x0083,0xB16D,0xB16C,0x0085,0x0083,0xB16B,0xB16A,0x0083,0xB169,0xEDEE,0x00C1,0x00A1,0x0091,0x0089,0x0085,
+ 0x0083,0xB168,0xBFB4,0x0083,0xB167,0xC3BC,0x0085,0x0083,0xEDF1,0xEDF0,0x0083,0xB166,0xB165,0x0089,0x0085,0x0083,
+ 0xEDED,0xB164,0x0083,0xB163,0xCAA1,0x0085,0x0083,0xB162,0xB161,0x0083,0xB6DC,0xB160,0x0091,0x0089,0x0085,0x0083,
+ 0xC5CE,0xB15F,0x0083,0xB15E,0xEDEF,0x0085,0x0083,0xCFE0,0xB15D,0x0083,0xB15C,0xB15B,0x0089,0x0085,0x0083,0xD6B1,
+ 0xB15A,0x0083,0xC3A4,0xEDEC,0x0085,0x0083,0xB159,0xB6A2,0x0083,0xC4BF,0xB158,0x00A1,0x0091,0x0089,0x0085,0x0083,
+ 0xB157,0xB156,0x0083,0xB155,0xB154,0x0085,0x0083,0xB153,0xB152,0x0083,0xB151,0xEEC2,0x0089,0x0085,0x0083,0xB150,
+ 0xB14F,0x0083,0xB14E,0xB14D,0x0085,0x0083,0xB14C,0xC3CB,0x0083,0xB14B,0xB14A,0x0091,0x0089,0x0085,0x0083,0xB149,
+ 0xCAA2,0x0083,0xB148,0xB147,0x0085,0x0083,0xC5CC,0xB5C1,0x0083,0xB8C7,0xB146,0x0089,0x0085,0x0083,0xBFF8,0xB145,
+ 0x0083,0xBAD0,0xBCE0,0x0085,0x0083,0xD1CE,0xD5B5,0x0083,0xB0BB,0xEEC1,0x0101,0x00C1,0x00A1,0x0091,0x0089,0x0085,
+ 0x0083,0xB144,0xB143,0x0083,0xD2E6,0xB142,0x0085,0x0083,0xD3AF,0xB141,0x0083,0xC5E8,0xD6D1,0x0089,0x0085,0x0083,
+ 0xB140,0xB0A0,0x0083,0xD3DB,0xB09F,0x0085,0x0083,0xB09E,0xC3F3,0x0083,0xB09D,0xB09C,0x0091,0x0089,0x0085,0x0083,
+ 0xB09B,0xB09A,0x0083,0xB099,0xB098,0x0085,0x0083,0xB097,0xB096,0x0083,0xB095,0xB094,0x0089,0x0085,0x0083,0xF1E5,
+ 0xB093,0x0083,0xF1E4,0xD6E5,0x0085,0x0083,0xB092,0xB091,0x0083,0xC6A4,0xB090,0x00A1,0x0091,0x0089,0x0085,0x0083,
+ 0xB08F,0xB08E,0x0083,0xB08D,0xB08C,0x0085,0x0083,0xB08B,0xB08A,0x0083,0xB089,0xB088,0x0089,0x0085,0x0083,0xF0AB,
+ 0xB087,0x0083,0xB086,0xB085,0x0085,0x0083,0xB084,0xB083,0x0083,0xB082,0xB081,0x0091,0x0089,0x0085,0x0083,0xB080,
+ 0xB07E,0x0083,0xB07D,0xF0AA,0x0085,0x0083,0xB07C,0xB07B,0x0083,0xCDEE,0xB07A,0x0089,0x0085,0x0083,0xB079,0xF0A9,
+ 0x0083,0xB078,0xB0A8,0x0085,0x0083,0xB077,0xB076,0x0083,0xF0A8,0xB075,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,
+ 0xB074,0xB8DE,0x0083,0xB073,0xB072,0x0085,0x0083,0xF0A7,0xBBCA,0x0083,0xBDD4,0xB071,0x0089,0x0085,0x0083,0xB5C4,
+ 0xB070,0x0083,0xD4ED,0xB06F,0x0085,0x0083,0xB06E,0xB06D,0x0083,0xB0D9,0xB0D7,0x0091,0x0089,0x0085,0x0083,0xB06C,
+ 0xB5C7,0x0083,0xB06B,0xB06A,0x0085,0x0083,0xB9EF,0xB069,0x0083,0xB068,0xB067,0x0089,0x0085,0x0083,0xB066,0xB065,
+ 0x0083,0xB064,0xB063,0x0085,0x0083,0xB062,0xF1B3,0x0083,0xB061,0xB060,0x00A1,0x0091,0x0089,0x0085,0x0083,0xB05F,
+ 0xF1B2,0x0083,0xB05E,0xB05D,0x0085,0x0083,0xB05C,0xB05B,0x0083,0xB05A,0xB059,0x0089,0x0085,0x0083,0xB058,0xD1A2,
+ 0x0083,0xB057,0xB056,0x0085,0x0083,0xB055,0xB054,0x0083,0xF1AE,0xB053,0x0091,0x0089,0x0085,0x0083,0xF1B0,0xB052,
+ 0x0083,0xB051,0xB050,0x0085,0x0083,0xB04F,0xB04E,0x0083,0xF1B1,0xB04D,0x0089,0x0085,0x0083,0xF1AF,0xB04C,0x0083,
+ 0xB04B,0xB04A,0x0085,0x0083,0xB049,0xB048,0x0083,0xB047,0xF1AD,0x027F,0x017F,0x00FF,0x00BF,0x009F,0x008F,0x0087,
+ 0x0083,0xB0A9,0x0083,0xB046,0xB045,0x0085,0x0083,0xB044,0xB043,0x0083,0xB042,0xB041,0x0089,0x0085,0x0083,0xB040,
+ 0xAFA0,0x0083,0xF1AA,0xAF9F,0x0085,0x0083,0xAF9E,0xF1A5,0x0083,0xF1A8,0xF1AB,0x0091,0x0089,0x0085,0x0083,0xAF9D,
+ 0xF1A2,0x0083,0xAF9C,0xAF9B,0x0085,0x0083,0xAF9A,0xC8B3,0x0083,0xAF99,0xAF98,0x0089,0x0085,0x0083,0xF1A9,0xD5CE,
+ 0x0083,0xF1AC,0xAF97,0x0085,0x0083,0xAF96,0xF1A7,0x0083,0xAF95,0xAF94,0x00A1,0x0091,0x0089,0x0085,0x0083,0xF1A6,
+ 0xAF93,0x0083,0xCCB1,0xB1F1,0x0085,0x0083,0xB4F1,0xAF92,0x0083,0xAF91,0xCADD,0x0089,0x0085,0x0083,0xF0FB,0xC1F6,
+ 0x0083,0xAF90,0xF1A3,0x0085,0x0083,0xAF8F,0xF1A4,0x0083,0xCEC1,0xAF8E,0x0091,0x0089,0x0085,0x0083,0xAF8D,0xAF8C,
+ 0x0083,0xF1A1,0xAF8B,0x0085,0x0083,0xF0FE,0xF0FC,0x0083,0xF0F9,0xAF8A,0x0089,0x0085,0x0083,0xF0FD,0xAF89,0x0083,
+ 0xAF88,0xAF87,0x0085,0x0083,0xAF86,0xF0F5,0x0083,0xAF85,0xAF84,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0xAF83,
+ 0xF0F8,0x0083,0xAF82,0xF0FA,0x0085,0x0083,0xAF81,0xAF80,0x0083,0xAF7E,0xAF7D,0x0089,0x0085,0x0083,0xF0F7,0xAF7C,
+ 0x0083,0xF0F1,0xAF7B,0x0085,0x0083,0xB4E1,0xF0F6,0x0083,0xF0F4,0xAF7A,0x0091,0x0089,0x0085,0x0083,0xAF79,0xF0F3,
+ 0x0083,0xAF78,0xAF77,0x0085,0x0083,0xB1D4,0xAF76,0x0083,0xAF75,0xAF74,0x0089,0x0085,0x0083,0xAF73,0xB3D5,0x0083,
+ 0xAF72,0xAF71,0x0085,0x0083,0xF0F2,0xCCB5,0x0083,0xAF70,0xAF6F,0x00A1,0x0091,0x0089,0x0085,0x0083,0xAF6E,0xAF6D,
+ 0x0083,0xF0EF,0xBBBE,0x0085,0x0083,0xAF6C,0xF0EC,0x0083,0xF0F0,0xF0ED,0x0089,0x0085,0x0083,0xAF6B,0xF0EE,0x0083,
+ 0xF0EB,0xC1A1,0x0085,0x0083,0xAF6A,0xAF69,0x0083,0xAF68,0xC6A6,0x0091,0x0089,0x0085,0x0083,0xAF67,0xAF66,0x0083,
+ 0xCDB4,0xAF65,0x0085,0x0083,0xAF64,0xB6BB,0x0083,0xAF63,0xF0E9,0x0089,0x0085,0x0083,0xBADB,0xD6CC,0x0083,0xAF62,
+ 0xD1F7,0x0085,0x0083,0xAF61,0xAF60,0x0083,0xAF5F,0xAF5E,0x0101,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0xF0EA,
+ 0xAF5D,0x0083,0xAF5C,0xC8AC,0x0085,0x0083,0xBEB7,0xD3B8,0x0083,0xD6A2,0xAF5B,0x0089,0x0085,0x0083,0xB2A1,0xF0E4,
+ 0x0083,0xF0E7,0xF0E8,0x0085,0x0083,0xAF5A,0xAF59,0x0083,0xAF58,0xBCB2,0x0091,0x0089,0x0085,0x0083,0xBED2,0xCCDB,
+ 0x0083,0xAF57,0xAF56,0x0085,0x0083,0xD5EE,0xF0E3,0x0083,0xAF55,0xAF54,0x0089,0x0085,0x0083,0xB4C3,0xF0E2,0x0083,
+ 0xF0E1,0xC6A3,0x0085,0x0083,0xF0E5,0xF0E6,0x0083,0xB7E8,0xB4AF,0x00A1,0x0091,0x0089,0x0085,0x0083,0xAF53,0xF0DF,
+ 0x0083,0xD2DF,0xAF52,0x0085,0x0083,0xAF51,0xAF50,0x0083,0xAF4F,0xAF4E,0x0089,0x0085,0x0083,0xBDEA,0xB0CC,0x0083,
+ 0xF0E0,0xAF4D,0x0085,0x0083,0xD1F1,0xF0DD,0x0083,0xC5B1,0xAF4C,0x0091,0x0089,0x0085,0x0083,0xF0DE,0xAF4B,0x0083,
+ 0xAF4A,0xBECE,0x0085,0x0083,0xB8ED,0xAF49,0x0083,0xC1C6,0xF0DC,0x0089,0x0085,0x0083,0xAF48,0xF0DB,0x0083,0xAF47,
+ 0xF0DA,0x0085,0x0083,0xD2C9,0xAF46,0x0083,0xCAE8,0xAF45,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0xAF44,0xAF43,
+ 0x0083,0xF1E2,0xAF42,0x0085,0x0083,0xAF41,0xAF40,0x0083,0xAEA0,0xBDAE,0x0089,0x0085,0x0083,0xAE9F,0xAE9E,0x0083,
+ 0xEEB6,0xAE9D,0x0085,0x0083,0xAE9C,0xAE9B,0x0083,0xE7DC,0xAE9A,0x0091,0x0089,0x0085,0x0083,0xAE99,0xAE98,0x0083,
+ 0xAE97,0xAE96,0x0085,0x0083,0xEEB5,0xBBFB,0x0083,0xAE95,0xAE94,0x0089,0x0085,0x0083,0xAE93,0xB3EB,0x0083,0xAE92,
+ 0xEEB4,0x0085,0x0083,0xAE91,0xAE90,0x0083,0xAE8F,0xAE8E,0x00A1,0x0091,0x0089,0x0085,0x0083,0xAE8D,0xAE8C,0x0083,
+ 0xAE8B,0xB7AC,0x0085,0x0083,0xAE8A,0xAE89,0x0083,0xAE88,0xC6E8,0x0089,0x0085,0x0083,0xC2D4,0xAE87,0x0083,0xAE86,
+ 0xAE85,0x0085,0x0083,0xAE84,0xAE83,0x0083,0xAE82,0xAE81,0x0091,0x0089,0x0085,0x0083,0xAE80,0xD0F3,0x0083,0xEEB3,
+ 0xDBCE,0x0085,0x0083,0xC1F4,0xAE7E,0x0083,0xAE7D,0xAE7C,0x0089,0x0085,0x0083,0xAE7B,0xC5CF,0x0083,0xAE7A,0xAE79,
+ 0x0085,0x0083,0xAE78,0xAE77,0x0083,0xCEB7,0xEEB0,0x0181,0x0101,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0xAE76,
+ 0xBDE7,0x0083,0xEEB1,0xAE75,0x0085,0x0083,0xAE74,0xEEB2,0x0083,0xAE73,0xAE72,0x0089,0x0085,0x0083,0xB3A9,0xAE71,
+ 0x0083,0xAE70,0xAE6F,0x0085,0x0083,0xAE6E,0xEEAF,0x0083,0xAE6D,0xE7DE,0x0091,0x0089,0x0085,0x0083,0xAE6C,0xAE6B,
+ 0x0083,0xBBAD,0xEEAE,0x0085,0x0083,0xAE6A,0xB5E9,0x0083,0xC4D0,0xAE69,0x0089,0x0085,0x0083,0xB5E7,0xAE68,0x0083,
+ 0xC9EA,0xBCD7,0x0085,0x0083,0xD3C9,0xCCEF,0x0083,0xE5B8,0xAE67,0x00A1,0x0091,0x0089,0x0085,0x0083,0xB1C2,0xF0AE,
+ 0x0083,0xB8A6,0xAE66,0x0085,0x0083,0xCBA6,0xD3C3,0x0083,0xAE65,0xAE64,0x0089,0x0085,0x0083,0xC9FB,0xAE63,0x0083,
+ 0xAE62,0xAE61,0x0085,0x0083,0xAE60,0xAE5F,0x0083,0xC9FA,0xAE5E,0x0091,0x0089,0x0085,0x0083,0xAE5D,0xCCF0,0x0083,
+ 0xAE5C,0xC9F5,0x0085,0x0083,0xDFB0,0xB8CA,0x0083,0xAE5B,0xAE5A,0x0089,0x0085,0x0083,0xAE59,0xAE58,0x0083,0xEAB6,
+ 0xAE57,0x0085,0x0083,0xEAB5,0xAE56,0x0083,0xEAB4,0xAE55,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0xDDF9,0xAE54,
+ 0x0083,0xAE53,0xAE52,0x0085,0x0083,0xAE51,0xAE50,0x0083,0xAE4F,0xAE4E,0x0089,0x0085,0x0083,0xAE4D,0xD5E7,0x0083,
+ 0xAE4C,0xAE4B,0x0085,0x0083,0xAE4A,0xAE49,0x0083,0xEAB3,0xAE48,0x0091,0x0089,0x0085,0x0083,0xAE47,0xAE46,0x0083,
+ 0xAE45,0xAE44,0x0085,0x0083,0xAE43,0xAE42,0x0083,0xB4C9,0xC6BF,0x0089,0x0085,0x0083,0xAE41,0xEAB2,0x0083,0xAE40,
+ 0xADA0,0x0085,0x0083,0xAD9F,0xAD9E,0x0083,0xEAB1,0xCECD,0x00A1,0x0091,0x0089,0x0085,0x0083,0xAD9D,0xAD9C,0x0083,
+ 0xAD9B,0xAD9A,0x0085,0x0083,0xAD99,0xAD98,0x0083,0xAD97,0xCDDF,0x0089,0x0085,0x0083,0xAD96,0xC8BF,0x0083,0xB0EA,
+ 0xC6B0,0x0085,0x0083,0xAD95,0xF0AD,0x0083,0xAD94,0xF0AC,0x0091,0x0089,0x0085,0x0083,0xAD93,0xB9CF,0x0083,0xAD92,
+ 0xAD91,0x0085,0x0083,0xAD90,0xAD8F,0x0083,0xAD8E,0xAD8D,0x0089,0x0085,0x0083,0xAD8C,0xAD8B,0x0083,0xAD8A,0xE8B6,
+ 0x0085,0x0083,0xAD89,0xAD88,0x0083,0xAD87,0xAD86,0x0101,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0xAD85,0xAD84,
+ 0x0083,0xAD83,0xAD82,0x0085,0x0083,0xAD81,0xAD80,0x0083,0xAD7E,0xAD7D,0x0089,0x0085,0x0083,0xAD7C,0xAD7B,0x0083,
+ 0xAD7A,0xAD79,0x0085,0x0083,0xAD78,0xAD77,0x0083,0xAD76,0xAD75,0x0091,0x0089,0x0085,0x0083,0xAD74,0xAD73,0x0083,
+ 0xAD72,0xE8B7,0x0085,0x0083,0xAD71,0xAD70,0x0083,0xAD6F,0xAD6E,0x0089,0x0085,0x0083,0xAD6D,0xAD6C,0x0083,0xAD6B,
+ 0xAD6A,0x0085,0x0083,0xAD69,0xAD68,0x0083,0xAD67,0xAD66,0x00A1,0x0091,0x0089,0x0085,0x0083,0xAD65,0xAD64,0x0083,
+ 0xAD63,0xAD62,0x0085,0x0083,0xE8B3,0xE8B2,0x0083,0xE8B5,0xAD61,0x0089,0x0085,0x0083,0xAD60,0xAD5F,0x0083,0xAD5E,
+ 0xAD5D,0x0085,0x0083,0xAD5C,0xAD5B,0x0083,0xAD5A,0xE8B1,0x0091,0x0089,0x0085,0x0083,0xAD59,0xE8AB,0x0083,0xAD58,
+ 0xAD57,0x0085,0x0083,0xAD56,0xAD55,0x0083,0xAD54,0xAD53,0x0089,0x0085,0x0083,0xAD52,0xAD51,0x0083,0xAD50,0xAD4F,
+ 0x0085,0x0083,0xAD4E,0xE8B4,0x0083,0xAD4D,0xE8AC,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0xAD4C,0xAD4B,0x0083,
+ 0xE8B0,0xAD4A,0x0085,0x0083,0xAD49,0xAD48,0x0083,0xE8AF,0xAD47,0x0089,0x0085,0x0083,0xAD46,0xAD45,0x0083,0xC1A7,
+ 0xAD44,0x0085,0x0083,0xE8AE,0xE8AD,0x0083,0xAD43,0xE8AA,0x0091,0x0089,0x0085,0x0083,0xAD42,0xAD41,0x0083,0xAD40,
+ 0xACA0,0x0085,0x0083,0xAC9F,0xAC9E,0x0083,0xE8A8,0xD1FE,0x0089,0x0085,0x0083,0xAC9D,0xAC9C,0x0083,0xAC9B,0xAC9A,
+ 0x0085,0x0083,0xAC99,0xB9E5,0x0083,0xAC98,0xAC97,0x00A1,0x0091,0x0089,0x0085,0x0083,0xE8A9,0xAC96,0x0083,0xAC95,
+ 0xAC94,0x0085,0x0083,0xAC93,0xAC92,0x0083,0xAC91,0xAC90,0x0089,0x0085,0x0083,0xAC8F,0xAC8E,0x0083,0xAC8D,0xAC8C,
+ 0x0085,0x0083,0xAC8B,0xAC8A,0x0083,0xC9AA,0xC8F0,0x0091,0x0089,0x0085,0x0083,0xAC89,0xE8A4,0x0083,0xE7F8,0xBAF7,
+ 0x0085,0x0083,0xE8A7,0xAC88,0x0083,0xE8A5,0xAC87,0x0089,0x0085,0x0083,0xE8A6,0xAC86,0x0083,0xAC85,0xAC84,0x0085,
+ 0x0083,0xAC83,0xAC82,0x0083,0xAC81,0xAC80,0x047D,0x027F,0x017F,0x00FF,0x00BF,0x009F,0x008F,0x0087,0x0083,0xAC7E,
+ 0x0083,0xAC7D,0xAC7C,0x0085,0x0083,0xAC7B,0xAC7A,0x0083,0xAC79,0xAC78,0x0089,0x0085,0x0083,0xAC77,0xAC76,0x0083,
+ 0xAC75,0xAC74,0x0085,0x0083,0xAC73,0xE8A3,0x0083,0xAC72,0xAC71,0x0091,0x0089,0x0085,0x0083,0xAC70,0xAC6F,0x0083,
+ 0xC7ED,0xAC6E,0x0085,0x0083,0xAC6D,0xAC6C,0x0083,0xAC6B,0xAC6A,0x0089,0x0085,0x0083,0xC5C3,0xC5FD,0x0083,0xC7D9,
+ 0xC1D5,0x0085,0x0083,0xAC69,0xAC68,0x0083,0xE7FC,0xAC67,0x00A1,0x0091,0x0089,0x0085,0x0083,0xE7FD,0xAC66,0x0083,
+ 0xE7FE,0xAC65,0x0085,0x0083,0xE7F7,0xAC64,0x0083,0xE7FB,0xAC63,0x0089,0x0085,0x0083,0xE7F9,0xE7FA,0x0083,0xAC62,
+ 0xAC61,0x0085,0x0083,0xD7C1,0xAC60,0x0083,0xAC5F,0xAC5E,0x0091,0x0089,0x0085,0x0083,0xAC5D,0xAC5C,0x0083,0xAC5B,
+ 0xE8A1,0x0085,0x0083,0xE8A2,0xAC5A,0x0083,0xAC59,0xAC58,0x0089,0x0085,0x0083,0xAC57,0xAC56,0x0083,0xAC55,0xAC54,
+ 0x0085,0x0083,0xAC53,0xAC52,0x0083,0xCBF6,0xE7F6,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0xAC51,0xAC50,0x0083,
+ 0xAC4F,0xAC4E,0x0085,0x0083,0xE7F0,0xC1F0,0x0083,0xAC4D,0xAC4C,0x0089,0x0085,0x0083,0xC0ED,0xC0C5,0x0083,0xAC4B,
+ 0xC7F2,0x0085,0x0083,0xAC4A,0xAC49,0x0083,0xAC48,0xAC47,0x0091,0x0089,0x0085,0x0083,0xAC46,0xAC45,0x0083,0xAC44,
+ 0xAC43,0x0085,0x0083,0xAC42,0xAC41,0x0083,0xAC40,0xABA0,0x0089,0x0085,0x0083,0xAB9F,0xAB9E,0x0083,0xAB9D,0xAB9C,
+ 0x0085,0x0083,0xE7F5,0xAB9B,0x0083,0xAB9A,0xAB99,0x00A1,0x0091,0x0089,0x0085,0x0083,0xAB98,0xB0E0,0x0083,0xAB97,
+ 0xAB96,0x0085,0x0083,0xAB95,0xE7F1,0x0083,0xAB94,0xE7F2,0x0089,0x0085,0x0083,0xAB93,0xE7ED,0x0083,0xAB92,0xAB91,
+ 0x0085,0x0083,0xAB90,0xAB8F,0x0083,0xD6E9,0xAB8E,0x0091,0x0089,0x0085,0x0083,0xE7F3,0xAB8D,0x0083,0xAB8C,0xAB8B,
+ 0x0085,0x0083,0xAB8A,0xE7EE,0x0083,0xAB89,0xAB88,0x0089,0x0085,0x0083,0xAB87,0xAB86,0x0083,0xAB85,0xAB84,0x0085,
+ 0x0083,0xAB83,0xE7E7,0x0083,0xB7A9,0xE7E5,0x0101,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0xAB82,0xD5E4,0x0083,
+ 0xAB81,0xAB80,0x0085,0x0083,0xC9BA,0xE7EB,0x0083,0xE7EC,0xAB7E,0x0089,0x0085,0x0083,0xAB7D,0xAB7C,0x0083,0xAB7B,
+ 0xAB7A,0x0085,0x0083,0xE7E6,0xAB79,0x0083,0xE7EA,0xAB78,0x0091,0x0089,0x0085,0x0083,0xAB77,0xAB76,0x0083,0xAB75,
+ 0xB2A3,0x0085,0x0083,0xE7F4,0xAB74,0x0083,0xAB73,0xE7E8,0x0089,0x0085,0x0083,0xAB72,0xAB71,0x0083,0xAB70,0xE7E9,
+ 0x0085,0x0083,0xC1E1,0xAB6F,0x0083,0xCFD6,0xBBB7,0x00A1,0x0091,0x0089,0x0085,0x0083,0xE7E2,0xAB6E,0x0083,0xAB6D,
+ 0xC3B5,0x0085,0x0083,0xAB6C,0xCDE6,0x0083,0xAB6B,0xAB6A,0x0089,0x0085,0x0083,0xAB69,0xAB68,0x0083,0xAB67,0xAB66,
+ 0x0085,0x0083,0xE7E3,0xAB65,0x0083,0xAB64,0xE7E4,0x0091,0x0089,0x0085,0x0083,0xAB63,0xAB62,0x0083,0xAB61,0xC2EA,
+ 0x0085,0x0083,0xAB60,0xAB5F,0x0083,0xAB5E,0xAB5D,0x0089,0x0085,0x0083,0xBEC1,0xAB5C,0x0083,0xAB5B,0xAB5A,0x0085,
+ 0x0083,0xAB59,0xE7E1,0x0083,0xAB58,0xAB57,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0xE7E0,0xAB56,0x0083,0xAB55,
+ 0xCDF5,0x0085,0x0083,0xAB54,0xD3F1,0x0083,0xAB53,0xC2CA,0x0089,0x0085,0x0083,0xAB52,0xAB51,0x0083,0xD0FE,0xAB50,
+ 0x0085,0x0083,0xAB4F,0xAB4E,0x0083,0xAB4D,0xAB4C,0x0091,0x0089,0x0085,0x0083,0xE2B5,0xAB4B,0x0083,0xAB4A,0xAB49,
+ 0x0085,0x0083,0xAB48,0xAB47,0x0083,0xAB46,0xAB45,0x0089,0x0085,0x0083,0xAB44,0xAB43,0x0083,0xAB42,0xAB41,0x0085,
+ 0x0083,0xAB40,0xAAA0,0x0083,0xAA9F,0xE2B4,0x00A1,0x0091,0x0089,0x0085,0x0083,0xAA9E,0xCCA1,0x0083,0xE2B3,0xAA9D,
+ 0x0085,0x0083,0xAA9C,0xAA9B,0x0083,0xAA9A,0xAA99,0x0089,0x0085,0x0083,0xAA98,0xAA97,0x0083,0xAA96,0xAA95,0x0085,
+ 0x0083,0xAA94,0xAA93,0x0083,0xE2B2,0xAA92,0x0091,0x0089,0x0085,0x0083,0xAA91,0xAA90,0x0083,0xAA8F,0xAA8E,0x0085,
+ 0x0083,0xAA8D,0xAA8C,0x0083,0xAA8B,0xE2B1,0x0089,0x0085,0x0083,0xAA8A,0xAA89,0x0083,0xAA88,0xAA87,0x0085,0x0083,
+ 0xE9E1,0xAA86,0x0083,0xE2AF,0xAA85,0x017F,0x00FF,0x00BF,0x009F,0x008F,0x0087,0x0083,0xAA84,0x0083,0xE2B0,0xAA83,
+ 0x0085,0x0083,0xAA82,0xAA81,0x0083,0xAA80,0xAA7E,0x0089,0x0085,0x0083,0xAA7D,0xAA7C,0x0083,0xAA7B,0xAA7A,0x0085,
+ 0x0083,0xAA79,0xAA78,0x0083,0xAA77,0xAA76,0x0091,0x0089,0x0085,0x0083,0xD4B3,0xBBAB,0x0083,0xAA75,0xAA74,0x0085,
+ 0x0083,0xAA73,0xAA72,0x0083,0xE2AA,0xE2AD,0x0089,0x0085,0x0083,0xE9E0,0xAA71,0x0083,0xAA70,0xBAEF,0x0085,0x0083,
+ 0xAA6F,0xAA6E,0x0083,0xE2AE,0xAA6D,0x00A1,0x0091,0x0089,0x0085,0x0083,0xAA6C,0xCFD7,0x0083,0xAA6B,0xE2AC,0x0085,
+ 0x0083,0xC3A8,0xD6ED,0x0083,0xD0C9,0xAA6A,0x0089,0x0085,0x0083,0xAA69,0xAA68,0x0083,0xE2AB,0xAA67,0x0085,0x0083,
+ 0xAA66,0xE2A9,0x0083,0xE2A4,0xAA65,0x0091,0x0089,0x0085,0x0083,0xAA64,0xE2A6,0x0083,0xE2A7,0xB2C2,0x0085,0x0083,
+ 0xC3CD,0xAA63,0x0083,0xAA62,0xAA61,0x0089,0x0085,0x0083,0xE2A2,0xB2FE,0x0083,0xE2A8,0xAA60,0x0085,0x0083,0xE2A3,
+ 0xAA5F,0x0083,0xAA5E,0xAA5D,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0xAA5C,0xC1D4,0x0083,0xAA5B,0xAA5A,0x0085,
+ 0x0083,0xAA59,0xE2A5,0x0083,0xAA58,0xAA57,0x0089,0x0085,0x0083,0xAA56,0xAA55,0x0083,0xAA54,0xAA53,0x0085,0x0083,
+ 0xE1FD,0xAA52,0x0083,0xE1FB,0xAA51,0x0091,0x0089,0x0085,0x0083,0xAA50,0xAA4F,0x0083,0xAA4E,0xC0C7,0x0085,0x0083,
+ 0xE2A1,0xE1FE,0x0083,0xAA4D,0xC0EA,0x0089,0x0085,0x0083,0xE1FA,0xAA4C,0x0083,0xAA4B,0xE1F9,0x0085,0x0083,0xE1FC,
+ 0xE1F8,0x0083,0xD3FC,0xD5F8,0x00A1,0x0091,0x0089,0x0085,0x0083,0xE1F6,0xCAA8,0x0083,0xCFC1,0xB6C0,0x0085,0x0083,
+ 0xAA4A,0xAA49,0x0083,0xE1F7,0xE1F5,0x0089,0x0085,0x0083,0xAA48,0xAA47,0x0083,0xAA46,0xAA45,0x0085,0x0083,0xAA44,
+ 0xAA43,0x0083,0xBDC6,0xBADD,0x0091,0x0089,0x0085,0x0083,0xAA42,0xC4FC,0x0083,0xAA41,0xAA40,0x0085,0x0083,0xA0FE,
+ 0xA0FD,0x0083,0xBED1,0xA0FC,0x0089,0x0085,0x0083,0xB9B7,0xA0FB,0x0083,0xA0FA,0xA0F9,0x0085,0x0083,0xA0F8,0xE1F4,
+ 0x0083,0xA0F7,0xBAFC,0x0101,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0xA0F6,0xE1F2,0x0083,0xE1F3,0xA0F5,0x0085,
+ 0x0083,0xA0F4,0xA0F3,0x0083,0xA0F2,0xB1B7,0x0089,0x0085,0x0083,0xA0F1,0xA0F0,0x0083,0xA0EF,0xB5D2,0x0085,0x0083,
+ 0xE1F0,0xBFF1,0x0083,0xE1F1,0xA0EE,0x0091,0x0089,0x0085,0x0083,0xA0ED,0xA0EC,0x0083,0xA0EB,0xA0EA,0x0085,0x0083,
+ 0xA0E9,0xA0E8,0x0083,0xD3CC,0xE1EF,0x0089,0x0085,0x0083,0xE1EE,0xD7B4,0x0083,0xA0E7,0xE1ED,0x0085,0x0083,0xA0E6,
+ 0xA0E5,0x0083,0xA0E4,0xE1EC,0x00A1,0x0091,0x0089,0x0085,0x0083,0xB7B8,0xA0E3,0x0083,0xE1EB,0xC8AE,0x0085,0x0083,
+ 0xA0E2,0xA0E1,0x0083,0xA0E0,0xA0DF,0x0089,0x0085,0x0083,0xA0DE,0xA0DD,0x0083,0xA0DC,0xA0DB,0x0085,0x0083,0xA0DA,
+ 0xA0D9,0x0083,0xA0D8,0xA0D7,0x0091,0x0089,0x0085,0x0083,0xEAF1,0xA0D6,0x0083,0xA0D5,0xA0D4,0x0085,0x0083,0xA0D3,
+ 0xA0D2,0x0083,0xA0D1,0xA0D0,0x0089,0x0085,0x0083,0xA0CF,0xA0CE,0x0083,0xA0CD,0xA0CC,0x0085,0x0083,0xA0CB,0xEAFB,
+ 0x0083,0xA0CA,0xA0C9,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0xEAFA,0xA0C8,0x0083,0xEAF9,0xA0C7,0x0085,0x0083,
+ 0xEAF8,0xB6BF,0x0083,0xA0C6,0xA0C5,0x0089,0x0085,0x0083,0xA0C4,0xA0C3,0x0083,0xA0C2,0xEAF7,0x0085,0x0083,0xA0C1,
+ 0xA0C0,0x0083,0xC0E7,0xCFAC,0x0091,0x0089,0x0085,0x0083,0xEAF6,0xEAF5,0x0083,0xA0BF,0xA0BE,0x0085,0x0083,0xA0BD,
+ 0xCEFE,0x0083,0xCCD8,0xA0BC,0x0089,0x0085,0x0083,0xA0BB,0xA0BA,0x0083,0xC7A3,0xA0B9,0x0085,0x0083,0xA0B8,0xC9FC,
+ 0x0083,0xA0B7,0xA0B6,0x00A1,0x0091,0x0089,0x0085,0x0083,0xEAF4,0xEAF0,0x0083,0xA0B5,0xA0B4,0x0085,0x0083,0xA0B3,
+ 0xA0B2,0x0083,0xCEEF,0xA0B1,0x0089,0x0085,0x0083,0xC4C1,0xEAF3,0x0083,0xA0B0,0xA0AF,0x0085,0x0083,0xA0AE,0xC0CE,
+ 0x0083,0xC4B5,0xA0AD,0x0091,0x0089,0x0085,0x0083,0xC4B2,0xA0AC,0x0083,0xEAF2,0xA0AB,0x0085,0x0083,0xC5A3,0xA0AA,
+ 0x0083,0xD1C0,0xA0A9,0x0089,0x0085,0x0083,0xA0A8,0xEBBB,0x0083,0xA0A7,0xA0A6,0x0085,0x0083,0xA0A5,0xEBBA,0x0083,
+ 0xA0A4,0xA0A3,0x027F,0x017F,0x00FF,0x00BF,0x009F,0x008F,0x0087,0x0083,0xA0A2,0x0083,0xA0A1,0xEBB9,0x0085,0x0083,
+ 0xC5C6,0xA0A0,0x0083,0xA09F,0xA09E,0x0089,0x0085,0x0083,0xB0E6,0xC6AC,0x0083,0xA09D,0xA09C,0x0085,0x0083,0xA09B,
+ 0xA09A,0x0083,0xA099,0xA098,0x0091,0x0089,0x0085,0x0083,0xA097,0xE3DD,0x0083,0xA096,0xCBAC,0x0085,0x0083,0xA095,
+ 0xD8B3,0x0083,0xA094,0xB5F9,0x0089,0x0085,0x0083,0xB0D6,0xD2AF,0x0083,0xB8B8,0xBEF4,0x0085,0x0083,0xA093,0xA092,
+ 0x0083,0xA091,0xB0AE,0x00A1,0x0091,0x0089,0x0085,0x0083,0xEBBC,0xA090,0x0083,0xA08F,0xA08E,0x0085,0x0083,0xC5C0,
+ 0xA08D,0x0083,0xD7A6,0xA08C,0x0089,0x0085,0x0083,0xECE0,0xA08B,0x0083,0xA08A,0xA089,0x0085,0x0083,0xA088,0xA087,
+ 0x0083,0xA086,0xA085,0x0091,0x0089,0x0085,0x0083,0xA084,0xA083,0x0083,0xA082,0xECDF,0x0085,0x0083,0xA081,0xA080,
+ 0x0083,0xA07E,0xA07D,0x0089,0x0085,0x0083,0xA07C,0xA07B,0x0083,0xA07A,0xA079,0x0085,0x0083,0xA078,0xA077,0x0083,
+ 0xA076,0xA075,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0xA074,0xA073,0x0083,0xA072,0xA071,0x0085,0x0083,0xA070,
+ 0xA06F,0x0083,0xA06E,0xA06D,0x0089,0x0085,0x0083,0xA06C,0xA06B,0x0083,0xB1AC,0xA06A,0x0085,0x0083,0xA069,0xA068,
+ 0x0083,0xA067,0xA066,0x0091,0x0089,0x0085,0x0083,0xA065,0xA064,0x0083,0xA063,0xA062,0x0085,0x0083,0xA061,0xA060,
+ 0x0083,0xA05F,0xECDE,0x0089,0x0085,0x0083,0xA05E,0xA05D,0x0083,0xA05C,0xA05B,0x0085,0x0083,0xA05A,0xA059,0x0083,
+ 0xA058,0xA057,0x00A1,0x0091,0x0089,0x0085,0x0083,0xA056,0xA055,0x0083,0xDBC6,0xA054,0x0085,0x0083,0xA053,0xA052,
+ 0x0083,0xA051,0xA050,0x0089,0x0085,0x0083,0xA04F,0xECDD,0x0083,0xA04E,0xD4EF,0x0085,0x0083,0xA04D,0xA04C,0x0083,
+ 0xA04B,0xA04A,0x0091,0x0089,0x0085,0x0083,0xECDB,0xA049,0x0083,0xA048,0xA047,0x0085,0x0083,0xA046,0xA045,0x0083,
+ 0xA044,0xA043,0x0089,0x0085,0x0083,0xA042,0xA041,0x0083,0xA040,0xD1E0,0x0085,0x0083,0xECDC,0x9FFE,0x0083,0x9FFD,
+ 0x9FFC,0x0101,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0x9FFB,0x9FFA,0x0083,0xC1C7,0x9FF9,0x0085,0x0083,0x9FF8,
+ 0x9FF7,0x0083,0x9FF6,0x9FF5,0x0089,0x0085,0x0083,0x9FF4,0x9FF3,0x0083,0x9FF2,0x9FF1,0x0085,0x0083,0x9FF0,0xC8BC,
+ 0x0083,0x9FEF,0x9FEE,0x0091,0x0089,0x0085,0x0083,0x9FED,0x9FEC,0x0083,0x9FEB,0x9FEA,0x0085,0x0083,0x9FE9,0x9FE8,
+ 0x0083,0x9FE7,0xECE4,0x0089,0x0085,0x0083,0x9FE6,0x9FE5,0x0083,0x9FE4,0xECD8,0x0085,0x0083,0x9FE3,0xECD7,0x0083,
+ 0x9FE2,0x9FE1,0x00A1,0x0091,0x0089,0x0085,0x0083,0x9FE0,0x9FDF,0x0083,0x9FDE,0x9FDD,0x0085,0x0083,0xB0BE,0x9FDC,
+ 0x0083,0x9FDB,0x9FDA,0x0089,0x0085,0x0083,0xECD9,0x9FD9,0x0083,0x9FD8,0x9FD7,0x0085,0x0083,0x9FD6,0x9FD5,0x0083,
+ 0x9FD4,0x9FD3,0x0091,0x0089,0x0085,0x0083,0xECDA,0xCAEC,0x0083,0x9FD2,0x9FD1,0x0085,0x0083,0x9FD0,0x9FCF,0x0083,
+ 0x9FCE,0xCEF5,0x0089,0x0085,0x0083,0xECD6,0x9FCD,0x0083,0x9FCC,0x9FCB,0x0085,0x0083,0xC8DB,0x9FCA,0x0083,0x9FC9,
+ 0x9FC8,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0x9FC7,0xD1AC,0x0083,0x9FC6,0x9FC5,0x0085,0x0083,0x9FC4,0x9FC3,
+ 0x0083,0xD0DC,0x9FC2,0x0089,0x0085,0x0083,0x9FC1,0x9FC0,0x0083,0x9FBF,0x9FBE,0x0085,0x0083,0xCFA8,0x9FBD,0x0083,
+ 0x9FBC,0x9FBB,0x0091,0x0089,0x0085,0x0083,0x9FBA,0x9FB9,0x0083,0x9FB8,0xC9BF,0x0085,0x0083,0x9FB7,0x9FB6,0x0083,
+ 0xECD5,0x9FB5,0x0089,0x0085,0x0083,0xECD4,0x9FB4,0x0083,0x9FB3,0x9FB2,0x0085,0x0083,0x9FB1,0xECCE,0x0083,0xECD2,
+ 0x9FB0,0x00A1,0x0091,0x0089,0x0085,0x0083,0x9FAF,0x9FAE,0x0083,0xD6F3,0x9FAD,0x0085,0x0083,0x9FAC,0x9FAB,0x0083,
+ 0x9FAA,0x9FA9,0x0089,0x0085,0x0083,0xECD0,0xD5D5,0x0083,0xECE3,0x9FA8,0x0085,0x0083,0xC3BA,0x9FA7,0x0083,0x9FA6,
+ 0x9FA5,0x0091,0x0089,0x0085,0x0083,0x9FA4,0x9FA3,0x0083,0xC9B7,0x9FA2,0x0085,0x0083,0xECCF,0x9FA1,0x0083,0x9FA0,
+ 0x9F9F,0x0089,0x0085,0x0083,0x9F9E,0x9F9D,0x0083,0x9F9C,0x9F9B,0x0085,0x0083,0x9F9A,0x9F99,0x0083,0x9F98,0x9F97,
+ 0x0181,0x0101,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0x9F96,0x9F95,0x0083,0xBCE5,0x9F94,0x0085,0x0083,0xBBCD,
+ 0x9F93,0x0083,0xECD3,0x9F92,0x0089,0x0085,0x0083,0x9F91,0x9F90,0x0083,0x9F8F,0xECD1,0x0085,0x0083,0x9F8E,0x9F8D,
+ 0x0083,0x9F8C,0x9F8B,0x0091,0x0089,0x0085,0x0083,0x9F8A,0x9F89,0x0083,0x9F88,0x9F87,0x0085,0x0083,0x9F86,0x9F85,
+ 0x0083,0x9F84,0x9F83,0x0089,0x0085,0x0083,0x9F82,0x9F81,0x0083,0xC8BB,0x9F80,0x0085,0x0083,0x9F7E,0x9F7D,0x0083,
+ 0x9F7C,0xECCD,0x00A1,0x0091,0x0089,0x0085,0x0083,0xD1E6,0xECCC,0x0083,0x9F7B,0x9F7A,0x0085,0x0083,0x9F79,0x9F78,
+ 0x0083,0x9F77,0x9F76,0x0089,0x0085,0x0083,0x9F75,0x9F74,0x0083,0xBDB9,0x9F73,0x0085,0x0083,0x9F72,0x9F71,0x0083,
+ 0x9F70,0x9F6F,0x0091,0x0089,0x0085,0x0083,0x9F6E,0x9F6D,0x0083,0x9F6C,0x9F6B,0x0085,0x0083,0x9F6A,0x9F69,0x0083,
+ 0xB7D9,0xB1BA,0x0089,0x0085,0x0083,0xECE2,0x9F68,0x0083,0xECCB,0xBBC0,0x0085,0x0083,0x9F67,0xECCA,0x0083,0x9F66,
+ 0x9F65,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0xECC9,0x9F64,0x0083,0x9F63,0x9F62,0x0085,0x0083,0x9F61,0x9F60,
+ 0x0083,0xBAB8,0xD1C9,0x0089,0x0085,0x0083,0x9F5F,0x9F5E,0x0083,0x9F5D,0x9F5C,0x0085,0x0083,0x9F5B,0x9F5A,0x0083,
+ 0x9F59,0x9F58,0x0091,0x0089,0x0085,0x0083,0x9F57,0x9F56,0x0083,0x9F55,0xB7E9,0x0085,0x0083,0x9F54,0x9F53,0x0083,
+ 0x9F52,0xC5EB,0x0089,0x0085,0x0083,0x9F51,0xCDE9,0x0083,0x9F50,0x9F4F,0x0085,0x0083,0x9F4E,0x9F4D,0x0083,0x9F4C,
+ 0x9F4B,0x00A1,0x0091,0x0089,0x0085,0x0083,0x9F4A,0xCFA9,0x0083,0x9F49,0xC8C8,0x0085,0x0083,0xBDFD,0xCCCC,0x0083,
+ 0x9F48,0xBBE2,0x0089,0x0085,0x0083,0xECC7,0xC9D5,0x0083,0xB7B3,0x9F47,0x0085,0x0083,0xBFBE,0x9F46,0x0083,0x9F45,
+ 0x9F44,0x0091,0x0089,0x0085,0x0083,0x9F43,0xD1CC,0x0083,0x9F42,0x9F41,0x0085,0x0083,0x9F40,0xD6F2,0x0083,0x9EFE,
+ 0xC0D3,0x0089,0x0085,0x0083,0xBAE6,0x9EFD,0x0083,0x9EFC,0x9EFB,0x0085,0x0083,0x9EFA,0x9EF9,0x0083,0x9EF8,0x9EF7,
+ 0x0101,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0x9EF6,0x9EF5,0x0083,0x9EF4,0x9EF3,0x0085,0x0083,0x9EF2,0x9EF1,
+ 0x0083,0xECC8,0x9EF0,0x0089,0x0085,0x0083,0xC1D2,0x9EEF,0x0083,0x9EEE,0x9EED,0x0085,0x0083,0x9EEC,0xCCFE,0x0083,
+ 0xC0C3,0xCBB8,0x0091,0x0089,0x0085,0x0083,0xECC3,0x9EEB,0x0083,0x9EEA,0xB3E3,0x0085,0x0083,0xC1B6,0xECC2,0x0083,
+ 0x9EE9,0xB5E3,0x0089,0x0085,0x0083,0xD5A8,0xECC4,0x0083,0x9EE8,0x9EE7,0x0085,0x0083,0x9EE6,0xB1FE,0x0083,0x9EE5,
+ 0xECC6,0x00A1,0x0091,0x0089,0x0085,0x0083,0x9EE4,0xBEBC,0x0083,0xC5DA,0xCCBF,0x0085,0x0083,0xBEE6,0xECC5,0x0083,
+ 0x9EE3,0x9EE2,0x0089,0x0085,0x0083,0x9EE1,0x9EE0,0x0083,0x9EDF,0x9EDE,0x0085,0x0083,0x9EDD,0x9EDC,0x0083,0x9EDB,
+ 0x9EDA,0x0091,0x0089,0x0085,0x0083,0x9ED9,0x9ED8,0x0083,0x9ED7,0xECC1,0x0085,0x0083,0xECBF,0x9ED6,0x0083,0x9ED5,
+ 0xD6CB,0x0089,0x0085,0x0083,0x9ED4,0x9ED3,0x0083,0xECC0,0xBFBB,0x0085,0x0083,0xC8B2,0x9ED2,0x0083,0xB3B4,0x9ED1,
+ 0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0x9ED0,0x9ECF,0x0083,0xD1D7,0x9ECE,0x0085,0x0083,0x9ECD,0x9ECC,0x0083,
+ 0xB4B6,0xC2AF,0x0089,0x0085,0x0083,0x9ECB,0x9ECA,0x0083,0x9EC9,0xEAC1,0x0085,0x0083,0x9EC8,0x9EC7,0x0083,0x9EC6,
+ 0x9EC5,0x0091,0x0089,0x0085,0x0083,0xECBE,0xB2D3,0x0083,0xD4D6,0x9EC4,0x0085,0x0083,0xD7C6,0x9EC3,0x0083,0x9EC2,
+ 0x9EC1,0x0089,0x0085,0x0083,0xBEC4,0x9EC0,0x0083,0xD4EE,0xC1E9,0x0085,0x0083,0x9EBF,0x9EBE,0x0083,0x9EBD,0x9EBC,
+ 0x00A1,0x0091,0x0089,0x0085,0x0083,0xBBD2,0xB5C6,0x0083,0x9EBB,0xC3F0,0x0085,0x0083,0xECE1,0xBBF0,0x0083,0x9EBA,
+ 0x9EB9,0x0089,0x0085,0x0083,0x9EB8,0x9EB7,0x0083,0x9EB6,0x9EB5,0x0085,0x0083,0x9EB4,0x9EB3,0x0083,0x9EB2,0x9EB1,
+ 0x0091,0x0089,0x0085,0x0083,0x9EB0,0x9EAF,0x0083,0xE5B1,0x9EAE,0x0085,0x0083,0x9EAD,0x9EAC,0x0083,0x9EAB,0x9EAA,
+ 0x0089,0x0085,0x0083,0x9EA9,0x9EA8,0x0083,0x9EA7,0x9EA6,0x0085,0x0083,0x9EA5,0x9EA4,0x0083,0x9EA3,0x9EA2,0x1075,
+ 0x087B,0x047D,0x027F,0x017F,0x00FF,0x00BF,0x009F,0x008F,0x0087,0x0083,0x9EA1,0x0083,0xE5B0,0x9EA0,0x0085,0x0083,
+ 0x9E9F,0xB9E0,0x0083,0x9E9E,0x9E9D,0x0089,0x0085,0x0083,0x9E9C,0x9E9B,0x0083,0x9E9A,0x9E99,0x0085,0x0083,0x9E98,
+ 0x9E97,0x0083,0x9E96,0x9E95,0x0091,0x0089,0x0085,0x0083,0x9E94,0x9E93,0x0083,0x9E92,0x9E91,0x0085,0x0083,0x9E90,
+ 0x9E8F,0x0083,0x9E8E,0x9E8D,0x0089,0x0085,0x0083,0xE5AE,0x9E8C,0x0083,0x9E8B,0x9E8A,0x0085,0x0083,0xE5AF,0x9E89,
+ 0x0083,0x9E88,0x9E87,0x00A1,0x0091,0x0089,0x0085,0x0083,0x9E86,0x9E85,0x0083,0x9E84,0x9E83,0x0085,0x0083,0x9E82,
+ 0x9E81,0x0083,0x9E80,0x9E7E,0x0089,0x0085,0x0083,0x9E7D,0x9E7C,0x0083,0x9E7B,0x9E7A,0x0085,0x0083,0x9E79,0x9E78,
+ 0x0083,0xE5AC,0x9E77,0x0091,0x0089,0x0085,0x0083,0x9E76,0x9E75,0x0083,0x9E74,0x9E73,0x0085,0x0083,0x9E72,0x9E71,
+ 0x0083,0xE5AD,0xE5AB,0x0089,0x0085,0x0083,0x9E70,0x9E6F,0x0083,0x9E6E,0x9E6D,0x0085,0x0083,0x9E6C,0x9E6B,0x0083,
+ 0x9E6A,0x9E69,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0xC6D9,0x9E68,0x0083,0x9E67,0x9E66,0x0085,0x0083,0x9E65,
+ 0x9E64,0x0083,0x9E63,0x9E62,0x0089,0x0085,0x0083,0x9E61,0x9E60,0x0083,0x9E5F,0x9E5E,0x0085,0x0083,0x9E5D,0x9E5C,
+ 0x0083,0x9E5B,0x9E5A,0x0091,0x0089,0x0085,0x0083,0x9E59,0x9E58,0x0083,0x9E57,0x9E56,0x0085,0x0083,0x9E55,0x9E54,
+ 0x0083,0x9E53,0x9E52,0x0089,0x0085,0x0083,0x9E51,0x9E50,0x0083,0x9E4F,0x9E4E,0x0085,0x0083,0x9E4D,0x9E4C,0x0083,
+ 0x9E4B,0x9E4A,0x00A1,0x0091,0x0089,0x0085,0x0083,0x9E49,0x9E48,0x0083,0xE5AA,0xE5A7,0x0085,0x0083,0x9E47,0x9E46,
+ 0x0083,0x9E45,0x9E44,0x0089,0x0085,0x0083,0x9E43,0x9E42,0x0083,0x9E41,0x9E40,0x0085,0x0083,0x9DFE,0x9DFD,0x0083,
+ 0x9DFC,0x9DFB,0x0091,0x0089,0x0085,0x0083,0xE5A6,0xE5A9,0x0083,0x9DFA,0xE5A8,0x0085,0x0083,0x9DF9,0x9DF8,0x0083,
+ 0x9DF7,0x9DF6,0x0089,0x0085,0x0083,0x9DF5,0x9DF4,0x0083,0x9DF3,0x9DF2,0x0085,0x0083,0x9DF1,0x9DF0,0x0083,0x9DEF,
+ 0xB1F4,0x0101,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0xE4FE,0x9DEE,0x0083,0x9DED,0x9DEC,0x0085,0x0083,0x9DEB,
+ 0x9DEA,0x0083,0x9DE9,0x9DE8,0x0089,0x0085,0x0083,0xE5A1,0x9DE7,0x0083,0x9DE6,0x9DE5,0x0085,0x0083,0x9DE4,0x9DE3,
+ 0x0083,0x9DE2,0xE5A5,0x0091,0x0089,0x0085,0x0083,0x9DE1,0xBCA4,0x0083,0x9DE0,0x9DDF,0x0085,0x0083,0x9DDE,0x9DDD,
+ 0x0083,0x9DDC,0x9DDB,0x0089,0x0085,0x0083,0xE5A3,0x9DDA,0x0083,0x9DD9,0xE5A4,0x0085,0x0083,0x9DD8,0x9DD7,0x0083,
+ 0xB0C4,0x9DD6,0x00A1,0x0091,0x0089,0x0085,0x0083,0x9DD5,0x9DD4,0x0083,0x9DD3,0x9DD2,0x0085,0x0083,0x9DD1,0x9DD0,
+ 0x0083,0x9DCF,0x9DCE,0x0089,0x0085,0x0083,0x9DCD,0x9DCC,0x0083,0xE5A2,0x9DCB,0x0085,0x0083,0x9DCA,0x9DC9,0x0083,
+ 0x9DC8,0x9DC7,0x0091,0x0089,0x0085,0x0083,0xD4E8,0x9DC6,0x0083,0x9DC5,0x9DC4,0x0085,0x0083,0x9DC3,0xC0BD,0x0083,
+ 0x9DC2,0x9DC1,0x0089,0x0085,0x0083,0x9DC0,0x9DBF,0x0083,0x9DBE,0x9DBD,0x0085,0x0083,0x9DBC,0x9DBB,0x0083,0x9DBA,
+ 0x9DB9,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0x9DB8,0x9DB7,0x0083,0x9DB6,0xC5EC,0x0085,0x0083,0xE4F8,0xE4F9,
+ 0x0083,0x9DB5,0x9DB4,0x0089,0x0085,0x0083,0xE4F7,0xB3BA,0x0083,0x9DB3,0x9DB2,0x0085,0x0083,0x9DB1,0xB3CE,0x0083,
+ 0x9DB0,0x9DAF,0x0091,0x0089,0x0085,0x0083,0x9DAE,0x9DAD,0x0083,0x9DAC,0x9DAB,0x0085,0x0083,0x9DAA,0xE4FC,0x0083,
+ 0x9DA9,0xE4FD,0x0089,0x0085,0x0083,0x9DA8,0xE4FA,0x0083,0x9DA7,0x9DA6,0x0085,0x0083,0x9DA5,0xE4F3,0x0083,0x9DA4,
+ 0xE4FB,0x00A1,0x0091,0x0089,0x0085,0x0083,0x9DA3,0x9DA2,0x0083,0x9DA1,0xB3B1,0x0085,0x0083,0xCCB6,0x9DA0,0x0083,
+ 0x9D9F,0x9D9E,0x0089,0x0085,0x0083,0x9D9D,0x9D9C,0x0083,0x9D9B,0xC1CA,0x0085,0x0083,0x9D9A,0x9D99,0x0083,0x9D98,
+ 0xE4EA,0x0091,0x0089,0x0085,0x0083,0x9D97,0x9D96,0x0083,0x9D95,0xC2BA,0x0085,0x0083,0x9D94,0xC7B1,0x0083,0x9D93,
+ 0x9D92,0x0089,0x0085,0x0083,0x9D91,0xC5CB,0x0083,0x9D90,0x9D8F,0x0085,0x0083,0x9D8E,0x9D8D,0x0083,0x9D8C,0x9D8B,
+ 0x017F,0x00FF,0x00BF,0x009F,0x008F,0x0087,0x0083,0x9D8A,0x0083,0x9D89,0x9D88,0x0085,0x0083,0x9D87,0xCEAB,0x0083,
+ 0x9D86,0xE4F2,0x0089,0x0085,0x0083,0x9D85,0x9D84,0x0083,0x9D83,0xE4EC,0x0085,0x0083,0xE4EB,0x9D82,0x0083,0x9D81,
+ 0x9D80,0x0091,0x0089,0x0085,0x0083,0x9D7E,0x9D7D,0x0083,0x9D7C,0x9D7B,0x0085,0x0083,0xD1FA,0x9D7A,0x0083,0x9D79,
+ 0x9D78,0x0089,0x0085,0x0083,0x9D77,0x9D76,0x0083,0x9D75,0x9D74,0x0085,0x0083,0xE4F1,0x9D73,0x0083,0x9D72,0xD5C4,
+ 0x00A1,0x0091,0x0089,0x0085,0x0083,0x9D71,0xCAFE,0x0083,0x9D70,0xE4F0,0x0085,0x0083,0x9D6F,0xE4DD,0x0083,0x9D6E,
+ 0xC2FE,0x0089,0x0085,0x0083,0xE4F4,0xE4F6,0x0083,0x9D6D,0x9D6C,0x0085,0x0083,0x9D6B,0x9D6A,0x0083,0xE4ED,0x9D69,
+ 0x0091,0x0089,0x0085,0x0083,0x9D68,0x9D67,0x0083,0xC4AE,0x9D66,0x0085,0x0083,0x9D65,0x9D64,0x0083,0x9D63,0x9D62,
+ 0x0089,0x0085,0x0083,0x9D61,0x9D60,0x0083,0x9D5F,0x9D5E,0x0085,0x0083,0x9D5D,0xE4EE,0x0083,0xD1DD,0xC0EC,0x00C1,
+ 0x00A1,0x0091,0x0089,0x0085,0x0083,0x9D5C,0x9D5B,0x0083,0x9D5A,0xC2A9,0x0085,0x0083,0x9D59,0x9D58,0x0083,0x9D57,
+ 0x9D56,0x0089,0x0085,0x0083,0x9D55,0xE4F5,0x0083,0x9D54,0x9D53,0x0085,0x0083,0xC6E1,0x9D52,0x0083,0x9D51,0x9D50,
+ 0x0091,0x0089,0x0085,0x0083,0xC6AF,0x9D4F,0x0083,0x9D4E,0x9D4D,0x0085,0x0083,0x9D4C,0x9D4B,0x0083,0x9D4A,0x9D49,
+ 0x0089,0x0085,0x0083,0x9D48,0xE4EF,0x0083,0x9D47,0x9D46,0x0085,0x0083,0x9D45,0x9D44,0x0083,0xB5CE,0x9D43,0x00A1,
+ 0x0091,0x0089,0x0085,0x0083,0x9D42,0x9D41,0x0083,0x9D40,0x9CFE,0x0085,0x0083,0x9CFD,0x9CFC,0x0083,0x9CFB,0x9CFA,
+ 0x0089,0x0085,0x0083,0x9CF9,0xCCB2,0x0083,0xB1F5,0x9CF8,0x0085,0x0083,0xC2D0,0xC0C4,0x0083,0xC2CB,0x9CF7,0x0091,
+ 0x0089,0x0085,0x0083,0xE4DE,0xC2FA,0x0083,0xE4DC,0xE4D9,0x0085,0x0083,0xD6CD,0x9CF6,0x0083,0x9CF5,0x9CF4,0x0089,
+ 0x0085,0x0083,0xB9F6,0x9CF3,0x0083,0x9CF2,0xE4E4,0x0085,0x0083,0x9CF1,0xEBF8,0x0083,0xCCCF,0xD7D2,0x0101,0x00C1,
+ 0x00A1,0x0091,0x0089,0x0085,0x0083,0x9CF0,0xBBAC,0x0083,0x9CEF,0xE4E6,0x0085,0x0083,0x9CEE,0x9CED,0x0083,0x9CEC,
+ 0xD7CC,0x0089,0x0085,0x0083,0x9CEB,0x9CEA,0x0083,0x9CE9,0xB5E1,0x0085,0x0083,0x9CE8,0x9CE7,0x0083,0x9CE6,0x9CE5,
+ 0x0091,0x0089,0x0085,0x0083,0xE4E8,0xB3FC,0x0083,0x9CE4,0x9CE3,0x0085,0x0083,0x9CE2,0xE4E1,0x0083,0x9CE1,0xE4E2,
+ 0x0089,0x0085,0x0083,0xC4E7,0x9CE0,0x0083,0x9CDF,0xE4E3,0x0085,0x0083,0xC8DC,0x9CDE,0x0083,0xE4E5,0x9CDD,0x00A1,
+ 0x0091,0x0089,0x0085,0x0083,0xE4D1,0xE4DA,0x0083,0x9CDC,0xCBDD,0x0085,0x0083,0x9CDB,0x9CDA,0x0083,0x9CD9,0x9CD8,
+ 0x0089,0x0085,0x0083,0xCFAA,0x9CD7,0x0083,0x9CD6,0xE4E0,0x0085,0x0083,0x9CD5,0xE4DF,0x0083,0x9CD4,0x9CD3,0x0091,
+ 0x0089,0x0085,0x0083,0xD2E7,0x9CD2,0x0083,0x9CD1,0xE4E9,0x0085,0x0083,0x9CD0,0x9CCF,0x0083,0xC1EF,0x9CCE,0x0089,
+ 0x0085,0x0083,0x9CCD,0x9CCC,0x0083,0xE4DB,0x9CCB,0x0085,0x0083,0x9CCA,0x9CC9,0x0083,0x9CC8,0x9CC7,0x00C1,0x00A1,
+ 0x0091,0x0089,0x0085,0x0083,0x9CC6,0x9CC5,0x0083,0xD4B4,0xE4E7,0x0085,0x0083,0x9CC4,0x9CC3,0x0083,0x9CC2,0x9CC1,
+ 0x0089,0x0085,0x0083,0x9CC0,0xB8C8,0x0083,0x9CBF,0x9CBE,0x0085,0x0083,0xE4D3,0xBDA6,0x0083,0x9CBD,0xC0A3,0x0091,
+ 0x0089,0x0085,0x0083,0x9CBC,0x9CBB,0x0083,0x9CBA,0xCAAA,0x0085,0x0083,0xCDE5,0x9CB9,0x0083,0x9CB8,0x9CB7,0x0089,
+ 0x0085,0x0083,0x9CB6,0x9CB5,0x0083,0x9CB4,0x9CB3,0x0085,0x0083,0x9CB2,0x9CB1,0x0083,0x9CB0,0x9CAF,0x00A1,0x0091,
+ 0x0089,0x0085,0x0083,0x9CAE,0x9CAD,0x0083,0x9CAC,0x9CAB,0x0085,0x0083,0xE4CE,0x9CAA,0x0083,0x9CA9,0xE4D0,0x0089,
+ 0x0085,0x0083,0x9CA8,0x9CA7,0x0083,0x9CA6,0x9CA5,0x0085,0x0083,0x9CA4,0x9CA3,0x0083,0x9CA2,0x9CA1,0x0091,0x0089,
+ 0x0085,0x0083,0x9CA0,0x9C9F,0x0083,0x9C9E,0xE4D2,0x0085,0x0083,0x9C9D,0x9C9C,0x0083,0x9C9B,0xD5BF,0x0089,0x0085,
+ 0x0083,0x9C9A,0x9C99,0x0083,0xCFE6,0x9C98,0x0085,0x0083,0xBAFE,0x9C97,0x0083,0xE4D5,0xE4D4,0x027F,0x017F,0x00FF,
+ 0x00BF,0x009F,0x008F,0x0087,0x0083,0x9C96,0x0083,0x9C95,0x9C94,0x0085,0x0083,0x9C93,0xE4CF,0x0083,0xCDC4,0x9C92,
+ 0x0089,0x0085,0x0083,0x9C91,0x9C90,0x0083,0x9C8F,0x9C8E,0x0085,0x0083,0x9C8D,0x9C8C,0x0083,0x9C8B,0xE4D8,0x0091,
+ 0x0089,0x0085,0x0083,0xC5C8,0x9C8A,0x0083,0x9C89,0x9C88,0x0085,0x0083,0x9C87,0x9C86,0x0083,0x9C85,0x9C84,0x0089,
+ 0x0085,0x0083,0x9C83,0xC3EC,0x0083,0x9C82,0xD3CE,0x0085,0x0083,0x9C81,0x9C80,0x0083,0x9C7E,0xBFCA,0x00A1,0x0091,
+ 0x0089,0x0085,0x0083,0x9C7D,0xE4D6,0x0083,0x9C7C,0x9C7B,0x0085,0x0083,0xB8DB,0x9C7A,0x0083,0xCEBC,0x9C79,0x0089,
+ 0x0085,0x0083,0xE4CD,0x9C78,0x0083,0xCEC2,0x9C77,0x0085,0x0083,0x9C76,0x9C75,0x0083,0xE4D7,0xB2B3,0x0091,0x0089,
+ 0x0085,0x0083,0xD4FC,0x9C74,0x0083,0xB6C9,0xC7FE,0x0085,0x0083,0x9C73,0x9C72,0x0083,0xD3E5,0x9C71,0x0089,0x0085,
+ 0x0083,0x9C70,0xE4BE,0x0083,0x9C6F,0x9C6E,0x0085,0x0083,0xC9F8,0xE4C9,0x0083,0x9C6D,0xD3E6,0x00C1,0x00A1,0x0091,
+ 0x0089,0x0085,0x0083,0x9C6C,0x9C6B,0x0083,0xE4C5,0xBDA5,0x0085,0x0083,0x9C6A,0xE4C2,0x0083,0xD7D5,0xE4CB,0x0089,
+ 0x0085,0x0083,0x9C69,0xD4A8,0x0083,0x9C68,0x9C67,0x0085,0x0083,0x9C66,0x9C65,0x0083,0xC7E5,0x9C64,0x0091,0x0089,
+ 0x0085,0x0083,0x9C63,0x9C62,0x0083,0x9C61,0x9C60,0x0085,0x0083,0x9C5F,0x9C5E,0x0083,0x9C5D,0xEDB5,0x0089,0x0085,
+ 0x0083,0xCCED,0x9C5C,0x0083,0xD1CD,0x9C5B,0x0085,0x0083,0xBBEC,0x9C5A,0x0083,0x9C59,0x9C58,0x00A1,0x0091,0x0089,
+ 0x0085,0x0083,0xB4BE,0x9C57,0x0083,0xC9EE,0x9C56,0x0085,0x0083,0x9C55,0xBBB4,0x0083,0x9C54,0xB4E3,0x0089,0x0085,
+ 0x0083,0xD2F9,0x9C53,0x0083,0x9C52,0x9C51,0x0085,0x0083,0x9C50,0xE4C6,0x0083,0x9C4F,0xD3D9,0x0091,0x0089,0x0085,
+ 0x0083,0x9C4E,0x9C4D,0x0083,0xB5AD,0xE4C4,0x0085,0x0083,0x9C4C,0xE4C1,0x0083,0xE4C7,0x9C4B,0x0089,0x0085,0x0083,
+ 0x9C4A,0x9C49,0x0083,0xE4C8,0xCCD4,0x0085,0x0083,0x9C48,0xC4D7,0x0083,0x9C47,0x9C46,0x0101,0x00C1,0x00A1,0x0091,
+ 0x0089,0x0085,0x0083,0x9C45,0x9C44,0x0083,0xCAE7,0x9C43,0x0085,0x0083,0x9C42,0x9C41,0x0083,0x9C40,0xCCCA,0x0089,
+ 0x0085,0x0083,0xC1DC,0x9BFE,0x0083,0x9BFD,0x9BFC,0x0085,0x0083,0xE4BF,0xCFFD,0x0083,0xE4C0,0xD7CD,0x0091,0x0089,
+ 0x0085,0x0083,0x9BFB,0x9BFA,0x0083,0x9BF9,0xB5ED,0x0085,0x0083,0xE4C3,0x9BF8,0x0083,0x9BF7,0x9BF6,0x0089,0x0085,
+ 0x0083,0x9BF5,0x9BF4,0x0083,0x9BF3,0xBAD4,0x0085,0x0083,0x9BF2,0x9BF1,0x0083,0xBAAD,0x9BF0,0x00A1,0x0091,0x0089,
+ 0x0085,0x0083,0x9BEF,0xD2BA,0x0083,0x9BEE,0x9BED,0x0085,0x0083,0xD1C4,0xE4CC,0x0083,0x9BEC,0x9BEB,0x0089,0x0085,
+ 0x0083,0xE4CA,0xB8A2,0x0083,0xC9AC,0xD5C7,0x0085,0x0083,0xBDA7,0xC8F3,0x0083,0x9BEA,0xB5D3,0x0091,0x0089,0x0085,
+ 0x0083,0xBBC1,0x9BE9,0x0083,0xCED0,0xE4B6,0x0085,0x0083,0xC1B0,0xE4B5,0x0083,0xC0D4,0x9BE8,0x0089,0x0085,0x0083,
+ 0xCCCE,0x9BE7,0x0083,0x9BE6,0x9BE5,0x0085,0x0083,0x9BE4,0x9BE3,0x0083,0xCCE9,0xE4B9,0x00C1,0x00A1,0x0091,0x0089,
+ 0x0085,0x0083,0xE4B8,0x9BE2,0x0083,0xE4B3,0x9BE1,0x0085,0x0083,0x9BE0,0xCFD1,0x0083,0x9BDF,0xD3BF,0x0089,0x0085,
+ 0x0083,0x9BDE,0x9BDD,0x0083,0xC9E6,0xCFFB,0x0085,0x0083,0x9BDC,0x9BDB,0x0083,0xC4F9,0x9BDA,0x0091,0x0089,0x0085,
+ 0x0083,0x9BD9,0xCDBF,0x0083,0x9BD8,0x9BD7,0x0085,0x0083,0x9BD6,0x9BD5,0x0083,0x9BD4,0xE4BC,0x0089,0x0085,0x0083,
+ 0x9BD3,0x9BD2,0x0083,0x9BD1,0xBDFE,0x0085,0x0083,0xBAA3,0x9BD0,0x0083,0x9BCF,0xD4A1,0x00A1,0x0091,0x0089,0x0085,
+ 0x0083,0x9BCE,0x9BCD,0x0083,0x9BCC,0x9BCB,0x0085,0x0083,0xE4B4,0xB8A1,0x0083,0x9BCA,0x9BC9,0x0089,0x0085,0x0083,
+ 0x9BC8,0xC0CB,0x0083,0xBAC6,0x9BC7,0x0085,0x0083,0x9BC6,0xC6D6,0x0083,0x9BC5,0x9BC4,0x0091,0x0089,0x0085,0x0083,
+ 0xE4BD,0x9BC3,0x0083,0x9BC2,0xE4BB,0x0085,0x0083,0x9BC1,0xE4B7,0x0083,0x9BC0,0xE4BA,0x0089,0x0085,0x0083,0x9BBF,
+ 0xBFA3,0x0083,0xD5E3,0x9BBE,0x0085,0x0083,0x9BBD,0x9BBC,0x0083,0x9BBB,0xE4B1,0x0181,0x0101,0x00C1,0x00A1,0x0091,
+ 0x0089,0x0085,0x0083,0xC5A8,0xE4B0,0x0083,0xBBEB,0x9BBA,0x0085,0x0083,0xE4AF,0xBCC3,0x0083,0xE4AB,0x9BB9,0x0089,
+ 0x0085,0x0083,0xB2E2,0xD7C7,0x0083,0x9BB8,0xE4A5,0x0085,0x0083,0xBDBD,0xBDAC,0x0083,0xC7B3,0x9BB7,0x0091,0x0089,
+ 0x0085,0x0083,0xE4A4,0x9BB6,0x0083,0xC1F7,0x9BB5,0x0085,0x0083,0x9BB4,0xC5C9,0x0083,0xC7A2,0xCDDD,0x0089,0x0085,
+ 0x0083,0xBBEE,0x9BB3,0x0083,0xE4A1,0x9BB2,0x0085,0x0083,0x9BB1,0x9BB0,0x0083,0xE4AD,0x9BAF,0x00A1,0x0091,0x0089,
+ 0x0085,0x0083,0xE4B2,0xD6DE,0x0083,0xB6FD,0x9BAE,0x0085,0x0083,0x9BAD,0xE4AC,0x0083,0x9BAC,0x9BAB,0x0089,0x0085,
+ 0x0083,0xE4AA,0xBAE9,0x0083,0x9BAA,0x9BA9,0x0085,0x0083,0xE4A2,0x9BA8,0x0083,0xBDF2,0x9BA7,0x0091,0x0089,0x0085,
+ 0x0083,0x9BA6,0x9BA5,0x0083,0x9BA4,0x9BA3,0x0085,0x0083,0x9BA2,0xB6B4,0x0083,0x9BA1,0x9BA0,0x0089,0x0085,0x0083,
+ 0xC2E5,0xE4AE,0x0083,0xE4A8,0x9B9F,0x0085,0x0083,0xCFB4,0x9B9E,0x0083,0x9B9D,0x9B9C,0x00C1,0x00A1,0x0091,0x0089,
+ 0x0085,0x0083,0x9B9B,0xC8F7,0x0083,0x9B9A,0x9B99,0x0085,0x0083,0x9B98,0xE4A9,0x0083,0x9B97,0xE4A3,0x0089,0x0085,
+ 0x0083,0xD1F3,0x9B96,0x0083,0x9B95,0x9B94,0x0085,0x0083,0xE4A6,0x9B93,0x0083,0x9B92,0xE4A7,0x0091,0x0089,0x0085,
+ 0x0083,0x9B91,0x9B90,0x0083,0xBDE0,0x9B8F,0x0085,0x0083,0x9B8E,0xE3FE,0x0083,0xD4F3,0xC6C3,0x0089,0x0085,0x0083,
+ 0xD0BA,0xE3F8,0x0083,0x9B8D,0xE3F2,0x0085,0x0083,0xE3F1,0xEDB4,0x0083,0xB1C3,0x9B8C,0x00A1,0x0091,0x0089,0x0085,
+ 0x0083,0xD3BE,0x9B8B,0x0083,0xE3F3,0xCCA9,0x0085,0x0083,0xE3FD,0xE3FA,0x0083,0x9B8A,0x9B89,0x0089,0x0085,0x0083,
+ 0xE3F9,0xC0E1,0x0083,0x9B88,0xD7A2,0x0085,0x0083,0x9B87,0x9B86,0x0083,0xC4E0,0x9B85,0x0091,0x0089,0x0085,0x0083,
+ 0xC6FC,0xB2A8,0x0083,0xC5DD,0xE3F6,0x0085,0x0083,0x9B84,0xC5A2,0x0083,0x9B83,0x9B82,0x0089,0x0085,0x0083,0xB7BA,
+ 0x9B81,0x0083,0x9B80,0x9B7E,0x0085,0x0083,0xE3F4,0xE3F7,0x0083,0xB7A8,0xE3EF,0x0101,0x00C1,0x00A1,0x0091,0x0089,
+ 0x0085,0x0083,0xE3FC,0x9B7D,0x0083,0x9B7C,0xE3EE,0x0085,0x0083,0x9B7B,0x9B7A,0x0083,0x9B79,0xC3DA,0x0089,0x0085,
+ 0x0083,0x9B78,0xB2B4,0x0083,0xC8AA,0x9B77,0x0085,0x0083,0x9B76,0x9B75,0x0083,0xC7F6,0xD0B9,0x0091,0x0089,0x0085,
+ 0x0083,0x9B74,0x9B73,0x0083,0x9B72,0x9B71,0x0085,0x0083,0xD1D8,0xD5B4,0x0083,0xB9C1,0xD5D3,0x0089,0x0085,0x0083,
+ 0xD6CE,0x9B70,0x0083,0xD3CD,0xB7D0,0x0085,0x0083,0x9B6F,0x9B6E,0x0083,0x9B6D,0x9B6C,0x00A1,0x0091,0x0089,0x0085,
+ 0x0083,0xBAD3,0xE3F5,0x0083,0xE3FB,0x9B6B,0x0085,0x0083,0x9B6A,0xBEDA,0x0083,0xE3F0,0x9B69,0x0089,0x0085,0x0083,
+ 0xC4AD,0xBBA6,0x0083,0xE3ED,0x9B68,0x0085,0x0083,0xB2D7,0xC2D9,0x0083,0xC1A4,0xC5BD,0x0091,0x0089,0x0085,0x0083,
+ 0xE3E3,0x9B67,0x0083,0xC3BB,0x9B66,0x0085,0x0083,0xB9B5,0x9B65,0x0083,0x9B64,0x9B63,0x0089,0x0085,0x0083,0xC5E6,
+ 0x9B62,0x0083,0xC9B3,0x9B61,0x0085,0x0083,0x9B60,0x9B5F,0x0083,0x9B5E,0xE3E6,0x00C1,0x00A1,0x0091,0x0089,0x0085,
+ 0x0083,0xEDB3,0x9B5D,0x0083,0x9B5C,0xE3E5,0x0085,0x0083,0xC6E3,0x9B5B,0x0083,0x9B5A,0xE3E7,0x0089,0x0085,0x0083,
+ 0x9B59,0x9B58,0x0083,0xB3C1,0xC9F2,0x0085,0x0083,0x9B57,0xE3EC,0x0083,0xE3E4,0x9B56,0x0091,0x0089,0x0085,0x0083,
+ 0xCED6,0xD2CA,0x0083,0xC7DF,0x9B55,0x0085,0x0083,0x9B54,0xB7DA,0x0083,0xC6FB,0x9B53,0x0089,0x0085,0x0083,0x9B52,
+ 0x9B51,0x0083,0xD0DA,0x9B50,0x0085,0x0083,0x9B4F,0xE3EB,0x0083,0x9B4E,0xE3EA,0x00A1,0x0091,0x0089,0x0085,0x0083,
+ 0x9B4D,0xBCB3,0x0083,0x9B4C,0xCCAD,0x0085,0x0083,0x9B4B,0x9B4A,0x0083,0x9B49,0x9B48,0x0089,0x0085,0x0083,0x9B47,
+ 0xCDF4,0x0083,0xE3E9,0xE3E8,0x0085,0x0083,0x9B46,0x9B45,0x0083,0x9B44,0xCCC0,0x0091,0x0089,0x0085,0x0083,0x9B43,
+ 0x9B42,0x0083,0xCEDB,0xB3D8,0x0085,0x0083,0xBDAD,0xB9AF,0x0083,0xC8EA,0xE3E1,0x0089,0x0085,0x0083,0xD1B4,0x9B41,
+ 0x0083,0x9B40,0x9AFE,0x0085,0x0083,0xBAB9,0x9AFD,0x0083,0xC9C7,0xE3E0,0x047D,0x027F,0x017F,0x00FF,0x00BF,0x009F,
+ 0x008F,0x0087,0x0083,0x9AFC,0x0083,0x9AFB,0x9AFA,0x0085,0x0083,0xCFAB,0x9AF9,0x0083,0x9AF8,0x9AF7,0x0089,0x0085,
+ 0x0083,0x9AF6,0x9AF5,0x0083,0xE3E2,0xBABA,0x0085,0x0083,0x9AF4,0xBBE3,0x0083,0xD9E0,0x9AF3,0x0091,0x0089,0x0085,
+ 0x0083,0x9AF2,0x9AF1,0x0083,0xC7F3,0xD6AD,0x0085,0x0083,0xCDA1,0x9AF0,0x0083,0x9AEF,0xD9DB,0x0089,0x0085,0x0083,
+ 0x9AEE,0x9AED,0x0083,0x9AEC,0x9AEB,0x0085,0x0083,0xD3C0,0x9AEA,0x0083,0x9AE9,0xE3DF,0x00A1,0x0091,0x0089,0x0085,
+ 0x0083,0xCBAE,0x9AE8,0x0083,0xEBB5,0x9AE7,0x0085,0x0083,0xC7E8,0xC2C8,0x0083,0xB5AA,0x9AE6,0x0089,0x0085,0x0083,
+ 0x9AE5,0x9AE4,0x0083,0xEBB4,0xEBB2,0x0085,0x0083,0xB0B1,0xD1F5,0x0083,0xBAA4,0x9AE3,0x0091,0x0089,0x0085,0x0083,
+ 0xEBB3,0x9AE2,0x0083,0xC7E2,0xEBB1,0x0085,0x0083,0x9AE1,0xB7FA,0x0083,0x9AE0,0x9ADF,0x0089,0x0085,0x0083,0x9ADE,
+ 0xB7D5,0x0083,0xEBB0,0xEBAF,0x0085,0x0083,0xEBAE,0x9ADD,0x0083,0xC4CA,0xEBAD,0x00C1,0x00A1,0x0091,0x0089,0x0085,
+ 0x0083,0xC6F8,0xC3A5,0x0083,0x9ADC,0xC3F1,0x0085,0x0083,0xD8B5,0xCACF,0x0083,0x9ADB,0xEBAC,0x0089,0x0085,0x0083,
+ 0x9ADA,0x9AD9,0x0083,0x9AD8,0x9AD7,0x0085,0x0083,0x9AD6,0xEBAA,0x0083,0xEBAB,0xEBA9,0x0091,0x0089,0x0085,0x0083,
+ 0x9AD5,0x9AD4,0x0083,0x9AD3,0x9AD2,0x0085,0x0083,0x9AD1,0x9AD0,0x0083,0x9ACF,0xEBA6,0x0089,0x0085,0x0083,0x9ACE,
+ 0x9ACD,0x0083,0x9ACC,0xEBA8,0x0085,0x0083,0x9ACB,0x9ACA,0x0083,0x9AC9,0xEBA7,0x00A1,0x0091,0x0089,0x0085,0x0083,
+ 0x9AC8,0xEBA5,0x0083,0x9AC7,0x9AC6,0x0085,0x0083,0x9AC5,0xCCBA,0x0083,0x9AC4,0x9AC3,0x0089,0x0085,0x0083,0x9AC2,
+ 0xBAC1,0x0083,0xEBA4,0x9AC1,0x0085,0x0083,0x9AC0,0x9ABF,0x0083,0x9ABE,0x9ABD,0x0091,0x0089,0x0085,0x0083,0x9ABC,
+ 0x9ABB,0x0083,0x9ABA,0xD5B1,0x0085,0x0083,0x9AB9,0x9AB8,0x0083,0x9AB7,0x9AB6,0x0089,0x0085,0x0083,0x9AB5,0xC3AB,
+ 0x0083,0x9AB4,0xB1D0,0x0085,0x0083,0x9AB3,0xC5FE,0x0083,0xB1D1,0xB1CF,0x0101,0x00C1,0x00A1,0x0091,0x0089,0x0085,
+ 0x0083,0xB1C8,0xD8B9,0x0083,0xB6BE,0x9AB2,0x0085,0x0083,0x9AB1,0xC3BF,0x0083,0x9AB0,0xC4B8,0x0089,0x0085,0x0083,
+ 0x9AAF,0xCEE3,0x0083,0x9AAE,0x9AAD,0x0085,0x0083,0x9AAC,0x9AAB,0x0083,0x9AAA,0xD2E3,0x0091,0x0089,0x0085,0x0083,
+ 0x9AA9,0x9AA8,0x0083,0xECB1,0xBBD9,0x0085,0x0083,0x9AA7,0xB5EE,0x0083,0x9AA6,0x9AA5,0x0089,0x0085,0x0083,0x9AA4,
+ 0x9AA3,0x0083,0x9AA2,0x9AA1,0x0085,0x0083,0x9AA0,0xD2F3,0x0083,0x9A9F,0xB6CE,0x00A1,0x0091,0x0089,0x0085,0x0083,
+ 0xC5B9,0xECAF,0x0083,0x9A9E,0x9A9D,0x0085,0x0083,0x9A9C,0x9A9B,0x0083,0x9A9A,0x9A99,0x0089,0x0085,0x0083,0x9A98,
+ 0x9A97,0x0083,0xE9EC,0x9A96,0x0085,0x0083,0x9A95,0x9A94,0x0083,0x9A93,0x9A92,0x0091,0x0089,0x0085,0x0083,0x9A91,
+ 0x9A90,0x0083,0x9A8F,0xE9EB,0x0085,0x0083,0x9A8E,0x9A8D,0x0083,0x9A8C,0x9A8B,0x0089,0x0085,0x0083,0x9A8A,0xE9EA,
+ 0x0083,0xE9E9,0x9A89,0x0085,0x0083,0x9A88,0x9A87,0x0083,0xD6B3,0x9A86,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,
+ 0x9A85,0xE9E7,0x0083,0xE9E6,0x9A84,0x0085,0x0083,0x9A83,0x9A82,0x0083,0x9A81,0xE9E8,0x0089,0x0085,0x0083,0x9A80,
+ 0xB2D0,0x0083,0xCAE2,0xD1B3,0x0085,0x0083,0x9A7E,0xE9E4,0x0083,0xB4F9,0x9A7D,0x0091,0x0089,0x0085,0x0083,0xE9E5,
+ 0xD1EA,0x0083,0xE9E3,0xE9E2,0x0085,0x0083,0x9A7C,0x9A7B,0x0083,0x9A7A,0x9A79,0x0089,0x0085,0x0083,0xBCDF,0xCBC0,
+ 0x0083,0x9A78,0xB4F5,0x0085,0x0083,0x9A77,0x9A76,0x0083,0x9A75,0x9A74,0x00A1,0x0091,0x0089,0x0085,0x0083,0x9A73,
+ 0x9A72,0x0083,0x9A71,0x9A70,0x0085,0x0083,0x9A6F,0x9A6E,0x0083,0x9A6D,0x9A6C,0x0089,0x0085,0x0083,0x9A6B,0x9A6A,
+ 0x0083,0xCDE1,0x9A69,0x0085,0x0083,0x9A68,0xC6E7,0x0083,0xCEE4,0xB2BD,0x0091,0x0089,0x0085,0x0083,0xB4CB,0xD5FD,
+ 0x0083,0xD6B9,0x9A67,0x0085,0x0083,0x9A66,0x9A65,0x0083,0x9A64,0x9A63,0x0089,0x0085,0x0083,0x9A62,0x9A61,0x0083,
+ 0x9A60,0xECA8,0x0085,0x0083,0x9A5F,0x9A5E,0x0083,0x9A5D,0x9A5C,0x017F,0x00FF,0x00BF,0x009F,0x008F,0x0087,0x0083,
+ 0x9A5B,0x0083,0x9A5A,0x9A59,0x0085,0x0083,0x9A58,0x9A57,0x0083,0x9A56,0x9A55,0x0089,0x0085,0x0083,0x9A54,0xB8E8,
+ 0x0083,0x9A53,0x9A52,0x0085,0x0083,0xC7B8,0x9A51,0x0083,0xD0AA,0xECA7,0x0091,0x0089,0x0085,0x0083,0x9A50,0x9A4F,
+ 0x0083,0xECA6,0x9A4E,0x0085,0x0083,0x9A4D,0x9A4C,0x0083,0x9A4B,0xBFEE,0x0089,0x0085,0x0083,0x9A4A,0x9A49,0x0083,
+ 0x9A48,0xC6DB,0x0085,0x0083,0xECA5,0x9A47,0x0083,0xECA4,0x9A46,0x00A1,0x0091,0x0089,0x0085,0x0083,0x9A45,0x9A44,
+ 0x0083,0x9A43,0xD3FB,0x0085,0x0083,0x9A42,0x9A41,0x0083,0x9A40,0x99FE,0x0089,0x0085,0x0083,0x99FD,0x99FC,0x0083,
+ 0x99FB,0x99FA,0x0085,0x0083,0x99F9,0x99F8,0x0083,0xC5B7,0x99F7,0x0091,0x0089,0x0085,0x0083,0x99F6,0xECA3,0x0083,
+ 0xD0C0,0xBBB6,0x0085,0x0083,0xB4CE,0xC7B7,0x0083,0x99F5,0x99F4,0x0089,0x0085,0x0083,0x99F3,0x99F2,0x0083,0x99F1,
+ 0x99F0,0x0085,0x0083,0x99EF,0x99EE,0x0083,0x99ED,0x99EC,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0x99EB,0x99EA,
+ 0x0083,0x99E9,0x99E8,0x0085,0x0083,0x99E7,0x99E6,0x0083,0x99E5,0x99E4,0x0089,0x0085,0x0083,0x99E3,0x99E2,0x0083,
+ 0x99E1,0x99E0,0x0085,0x0083,0x99DF,0x99DE,0x0083,0x99DD,0x99DC,0x0091,0x0089,0x0085,0x0083,0x99DB,0x99DA,0x0083,
+ 0x99D9,0x99D8,0x0085,0x0083,0x99D7,0x99D6,0x0083,0x99D5,0x99D4,0x0089,0x0085,0x0083,0x99D3,0x99D2,0x0083,0x99D1,
+ 0x99D0,0x0085,0x0083,0x99CF,0x99CE,0x0083,0x99CD,0x99CC,0x00A1,0x0091,0x0089,0x0085,0x0083,0x99CB,0x99CA,0x0083,
+ 0x99C9,0x99C8,0x0085,0x0083,0x99C7,0x99C6,0x0083,0x99C5,0x99C4,0x0089,0x0085,0x0083,0x99C3,0x99C2,0x0083,0x99C1,
+ 0x99C0,0x0085,0x0083,0x99BF,0x99BE,0x0083,0x99BD,0x99BC,0x0091,0x0089,0x0085,0x0083,0x99BB,0x99BA,0x0083,0x99B9,
+ 0x99B8,0x0085,0x0083,0x99B7,0x99B6,0x0083,0x99B5,0x99B4,0x0089,0x0085,0x0083,0x99B3,0x99B2,0x0083,0x99B1,0x99B0,
+ 0x0085,0x0083,0x99AF,0x99AE,0x0083,0x99AD,0x99AC,0x0101,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0x99AB,0x99AA,
+ 0x0083,0x99A9,0x99A8,0x0085,0x0083,0x99A7,0x99A6,0x0083,0x99A5,0x99A4,0x0089,0x0085,0x0083,0x99A3,0x99A2,0x0083,
+ 0x99A1,0x99A0,0x0085,0x0083,0x999F,0x999E,0x0083,0x999D,0x999C,0x0091,0x0089,0x0085,0x0083,0x999B,0x999A,0x0083,
+ 0x9999,0x9998,0x0085,0x0083,0x9997,0x9996,0x0083,0x9995,0x9994,0x0089,0x0085,0x0083,0x9993,0x9992,0x0083,0x9991,
+ 0x9990,0x0085,0x0083,0x998F,0x998E,0x0083,0x998D,0x998C,0x00A1,0x0091,0x0089,0x0085,0x0083,0x998B,0x998A,0x0083,
+ 0x9989,0x9988,0x0085,0x0083,0x9987,0x9986,0x0083,0x9985,0x9984,0x0089,0x0085,0x0083,0x9983,0xC3CA,0x0083,0xE9DF,
+ 0x9982,0x0085,0x0083,0xE9DD,0x9981,0x0083,0x9980,0x997E,0x0091,0x0089,0x0085,0x0083,0x997D,0x997C,0x0083,0x997B,
+ 0x997A,0x0085,0x0083,0x9979,0xE9D1,0x0083,0x9978,0x9977,0x0089,0x0085,0x0083,0x9976,0x9975,0x0083,0x9974,0x9973,
+ 0x0085,0x0083,0x9972,0x9971,0x0083,0xE9DE,0x9970,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0x996F,0x996E,0x0083,
+ 0x996D,0x996C,0x0085,0x0083,0xE9DB,0xE9DC,0x0083,0x996B,0xE9D5,0x0089,0x0085,0x0083,0x996A,0x9969,0x0083,0x9968,
+ 0x9967,0x0085,0x0083,0x9966,0x9965,0x0083,0x9964,0x9963,0x0091,0x0089,0x0085,0x0083,0x9962,0xCFAD,0x0083,0x9961,
+ 0x9960,0x0085,0x0083,0x995F,0xCCB4,0x0083,0x995E,0x995D,0x0089,0x0085,0x0083,0x995C,0xE9DA,0x0083,0x995B,0x995A,
+ 0x0085,0x0083,0xE9D6,0x9959,0x0083,0x9958,0x9957,0x00A1,0x0091,0x0089,0x0085,0x0083,0x9956,0x9955,0x0083,0x9954,
+ 0x9953,0x0085,0x0083,0xB3F7,0x9952,0x0083,0x9951,0x9950,0x0089,0x0085,0x0083,0x994F,0x994E,0x0083,0x994D,0x994C,
+ 0x0085,0x0083,0x994B,0x994A,0x0083,0x9949,0x9948,0x0091,0x0089,0x0085,0x0083,0xE9CD,0x9947,0x0083,0x9946,0x9945,
+ 0x0085,0x0083,0xCFF0,0x9944,0x0083,0x9943,0x9942,0x0089,0x0085,0x0083,0x9941,0x9940,0x0083,0xE9D3,0x98FE,0x0085,
+ 0x0083,0xB3C8,0xE9D9,0x0083,0x98FD,0x98FC,0x027F,0x017F,0x00FF,0x00BF,0x009F,0x008F,0x0087,0x0083,0x98FB,0x0083,
+ 0x98FA,0x98F9,0x0085,0x0083,0x98F8,0x98F7,0x0083,0xE9D2,0x98F6,0x0089,0x0085,0x0083,0x98F5,0x98F4,0x0083,0x98F3,
+ 0x98F2,0x0085,0x0083,0x98F1,0x98F0,0x0083,0x98EF,0xC7C1,0x0091,0x0089,0x0085,0x0083,0x98EE,0x98ED,0x0083,0xE9CF,
+ 0x98EC,0x0085,0x0083,0x98EB,0x98EA,0x0083,0x98E9,0x98E8,0x0089,0x0085,0x0083,0xE9D0,0xE9D7,0x0083,0x98E7,0x98E6,
+ 0x0085,0x0083,0x98E5,0x98E4,0x0083,0x98E3,0x98E2,0x00A1,0x0091,0x0089,0x0085,0x0083,0x98E1,0xE9D4,0x0083,0x98E0,
+ 0x98DF,0x0085,0x0083,0x98DE,0xD3A3,0x0083,0x98DD,0xE9C9,0x0089,0x0085,0x0083,0x98DC,0x98DB,0x0083,0x98DA,0x98D9,
+ 0x0085,0x0083,0xBAE1,0x98D8,0x0083,0xE9D8,0x98D7,0x0091,0x0089,0x0085,0x0083,0x98D6,0x98D5,0x0083,0x98D4,0x98D3,
+ 0x0085,0x0083,0x98D2,0xC4A3,0x0083,0x98D1,0xD5C1,0x0089,0x0085,0x0083,0x98D0,0x98CF,0x0083,0x98CE,0x98CD,0x0085,
+ 0x0083,0x98CC,0x98CB,0x0083,0xE9CC,0xE9CB,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0x98CA,0x98C9,0x0083,0x98C8,
+ 0x98C7,0x0085,0x0083,0x98C6,0x98C5,0x0083,0x98C4,0x98C3,0x0089,0x0085,0x0083,0x98C2,0x98C1,0x0083,0x98C0,0x98BF,
+ 0x0085,0x0083,0xB7AE,0x98BE,0x0083,0x98BD,0x98BC,0x0091,0x0089,0x0085,0x0083,0x98BB,0x98BA,0x0083,0x98B9,0x98B8,
+ 0x0085,0x0083,0x98B7,0x98B6,0x0083,0x98B5,0xE9C8,0x0089,0x0085,0x0083,0x98B4,0xB2DB,0x0083,0x98B3,0x98B2,0x0085,
+ 0x0083,0x98B1,0x98B0,0x0083,0x98AF,0x98AE,0x00A1,0x0091,0x0089,0x0085,0x0083,0x98AD,0x98AC,0x0083,0x98AB,0x98AA,
+ 0x0085,0x0083,0xE9CE,0x98A9,0x0083,0x98A8,0x98A7,0x0089,0x0085,0x0083,0x98A6,0xE9CA,0x0083,0x98A5,0x98A4,0x0085,
+ 0x0083,0x98A3,0x98A2,0x0083,0x98A1,0x98A0,0x0091,0x0089,0x0085,0x0083,0x989F,0x989E,0x0083,0x989D,0x989C,0x0085,
+ 0x0083,0x989B,0x989A,0x0083,0xE9C6,0xE9C4,0x0089,0x0085,0x0083,0x9899,0x9898,0x0083,0x9897,0xBCF7,0x0085,0x0083,
+ 0x9896,0x9895,0x0083,0x9894,0x9893,0x0101,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0x9892,0x9891,0x0083,0xE9C0,
+ 0x9890,0x0085,0x0083,0x988F,0x988E,0x0083,0xBBB1,0x988D,0x0089,0x0085,0x0083,0xE9B6,0x988C,0x0083,0xE9B3,0x988B,
+ 0x0085,0x0083,0xE9C3,0x988A,0x0083,0x9889,0x9888,0x0091,0x0089,0x0085,0x0083,0x9887,0x9886,0x0083,0x9885,0x9884,
+ 0x0085,0x0083,0x9883,0xE9C2,0x0083,0x9882,0x9881,0x0089,0x0085,0x0083,0x9880,0x987E,0x0083,0x987D,0xE9BD,0x0085,
+ 0x0083,0x987C,0x987B,0x0083,0x987A,0xC8B6,0x00A1,0x0091,0x0089,0x0085,0x0083,0x9879,0x9878,0x0083,0xC1F1,0x9877,
+ 0x0085,0x0083,0x9876,0xE9C1,0x0083,0x9875,0x9874,0x0089,0x0085,0x0083,0x9873,0xE9BF,0x0083,0x9872,0xE9BE,0x0085,
+ 0x0083,0x9871,0x9870,0x0083,0xD5A5,0xE9BC,0x0091,0x0089,0x0085,0x0083,0x986F,0x986E,0x0083,0x986D,0x986C,0x0085,
+ 0x0083,0x986B,0x986A,0x0083,0x9869,0x9868,0x0089,0x0085,0x0083,0x9867,0x9866,0x0083,0xB0F1,0xE9BB,0x0085,0x0083,
+ 0x9865,0x9864,0x0083,0xE9B0,0x9863,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0x9862,0xE9C5,0x0083,0xC0C6,0x9861,
+ 0x0085,0x0083,0x9860,0x985F,0x0083,0x985E,0x985D,0x0089,0x0085,0x0083,0x985C,0xE9C7,0x0083,0x985B,0x985A,0x0085,
+ 0x0083,0x9859,0xE9B7,0x0083,0xE9B5,0xE9B4,0x0091,0x0089,0x0085,0x0083,0xD3DC,0x9858,0x0083,0xE9AD,0x9857,0x0085,
+ 0x0083,0xB8C5,0x9856,0x0083,0xE9AF,0x9855,0x0089,0x0085,0x0083,0x9854,0x9853,0x0083,0xC2A5,0x9852,0x0085,0x0083,
+ 0x9851,0xE9BA,0x0083,0xE9B1,0xBFAC,0x00A1,0x0091,0x0089,0x0085,0x0083,0x9850,0x984F,0x0083,0x984E,0x984D,0x0085,
+ 0x0083,0x984C,0xE9A8,0x0083,0x984B,0x984A,0x0089,0x0085,0x0083,0xE8FA,0x9849,0x0083,0x9848,0xE9AE,0x0085,0x0083,
+ 0x9847,0x9846,0x0083,0x9845,0x9844,0x0091,0x0089,0x0085,0x0083,0xE9B8,0x9843,0x0083,0x9842,0xE9B9,0x0085,0x0083,
+ 0x9841,0x9840,0x0083,0xE9AA,0x97FE,0x0089,0x0085,0x0083,0xC0E3,0xE9AC,0x0083,0x97FD,0x97FC,0x0085,0x0083,0xB3FE,
+ 0x97FB,0x0083,0x97FA,0xE9A5,0x0181,0x0101,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0x97F9,0x97F8,0x0083,0xD0A8,
+ 0x97F7,0x0085,0x0083,0x97F6,0x97F5,0x0083,0x97F4,0x97F3,0x0089,0x0085,0x0083,0x97F2,0x97F1,0x0083,0x97F0,0x97EF,
+ 0x0085,0x0083,0x97EE,0x97ED,0x0083,0x97EC,0x97EB,0x0091,0x0089,0x0085,0x0083,0x97EA,0x97E9,0x0083,0x97E8,0x97E7,
+ 0x0085,0x0083,0xE9AB,0x97E6,0x0083,0x97E5,0xB4BB,0x0089,0x0085,0x0083,0x97E4,0xB4AA,0x0083,0x97E3,0x97E2,0x0085,
+ 0x0083,0x97E1,0xE9A9,0x0083,0x97E0,0x97DF,0x00A1,0x0091,0x0089,0x0085,0x0083,0x97DE,0x97DD,0x0083,0xE9B2,0x97DC,
+ 0x0085,0x0083,0x97DB,0x97DA,0x0083,0xD2AC,0x97D9,0x0089,0x0085,0x0083,0x97D8,0xCDD6,0x0083,0x97D7,0x97D6,0x0085,
+ 0x0083,0x97D5,0x97D4,0x0083,0x97D3,0x97D2,0x0091,0x0089,0x0085,0x0083,0x97D1,0x97D0,0x0083,0xE9A1,0x97CF,0x0085,
+ 0x0083,0x97CE,0x97CD,0x0083,0xE8FD,0xE8FC,0x0089,0x0085,0x0083,0x97CC,0x97CB,0x0083,0x97CA,0x97C9,0x0085,0x0083,
+ 0x97C8,0x97C7,0x0083,0x97C6,0x97C5,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0x97C4,0x97C3,0x0083,0x97C2,0x97C1,
+ 0x0085,0x0083,0xBDB7,0x97C0,0x0083,0xE9A7,0x97BF,0x0089,0x0085,0x0083,0xD7B5,0xD6B2,0x0083,0x97BE,0xE9A3,0x0085,
+ 0x0083,0x97BD,0x97BC,0x0083,0x97BB,0x97BA,0x0091,0x0089,0x0085,0x0083,0x97B9,0xD2CE,0x0083,0x97B8,0x97B7,0x0085,
+ 0x0083,0x97B6,0xE9A4,0x0083,0x97B5,0x97B4,0x0089,0x0085,0x0083,0x97B3,0x97B2,0x0083,0xE8FB,0x97B1,0x0085,0x0083,
+ 0xB9D7,0xE8FE,0x0083,0x97B0,0x97AF,0x00A1,0x0091,0x0089,0x0085,0x0083,0x97AE,0xBFC3,0x0083,0x97AD,0x97AC,0x0085,
+ 0x0083,0x97AB,0xC0E2,0x0083,0xE9A2,0x97AA,0x0089,0x0085,0x0083,0xC9AD,0x97A9,0x0083,0x97A8,0x97A7,0x0085,0x0083,
+ 0x97A6,0x97A5,0x0083,0x97A4,0x97A3,0x0091,0x0089,0x0085,0x0083,0x97A2,0x97A1,0x0083,0x97A0,0xE9A6,0x0085,0x0083,
+ 0x979F,0x979E,0x0083,0xCCC4,0x979D,0x0089,0x0085,0x0083,0x979C,0x979B,0x0083,0x979A,0x9799,0x0085,0x0083,0xC5EF,
+ 0x9798,0x0083,0xBCAC,0x9797,0x0101,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0x9796,0xD7D8,0x0083,0x9795,0x9794,
+ 0x0085,0x0083,0xB0F4,0x9793,0x0083,0x9792,0x9791,0x0089,0x0085,0x0083,0x9790,0xB9F7,0x0083,0x978F,0xC6E5,0x0085,
+ 0x0083,0x978E,0xC3DE,0x0083,0x978D,0x978C,0x0091,0x0089,0x0085,0x0083,0x978B,0x978A,0x0083,0x9789,0x9788,0x0085,
+ 0x0083,0xE8F9,0x9787,0x0083,0xBCEC,0x9786,0x0089,0x0085,0x0083,0x9785,0x9784,0x0083,0x9783,0x9782,0x0085,0x0083,
+ 0x9781,0x9780,0x0083,0x977E,0x977D,0x00A1,0x0091,0x0089,0x0085,0x0083,0x977C,0xE8F3,0x0083,0x977B,0xCAE1,0x0085,
+ 0x0083,0x977A,0x9779,0x0083,0xD0B5,0xCCDD,0x0089,0x0085,0x0083,0x9778,0xCBF3,0x0083,0x9777,0x9776,0x0085,0x0083,
+ 0x9775,0x9774,0x0083,0xC0E6,0xCEE0,0x0091,0x0089,0x0085,0x0083,0xC3CE,0x9773,0x0083,0x9772,0x9771,0x0085,0x0083,
+ 0xC9D2,0x9770,0x0083,0x976F,0x976E,0x0089,0x0085,0x0083,0x976D,0x976C,0x0083,0x976B,0x976A,0x0085,0x0083,0x9769,
+ 0x9768,0x0083,0x9767,0xB9A3,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0x9766,0x9765,0x0083,0x9764,0xE8F7,0x0085,
+ 0x0083,0x9763,0x9762,0x0083,0x9761,0xE8F4,0x0089,0x0085,0x0083,0x9760,0x975F,0x0083,0x975E,0x975D,0x0085,0x0083,
+ 0x975C,0x975B,0x0083,0x975A,0x9759,0x0091,0x0089,0x0085,0x0083,0xB0F0,0xC3B7,0x0083,0x9758,0xE8E8,0x0085,0x0083,
+ 0x9757,0xC1BA,0x0083,0x9756,0x9755,0x0089,0x0085,0x0083,0x9754,0x9753,0x0083,0x9752,0x9751,0x0085,0x0083,0x9750,
+ 0x974F,0x0083,0x974E,0xE8F6,0x00A1,0x0091,0x0089,0x0085,0x0083,0xCDB0,0x974D,0x0083,0xE8F5,0x974C,0x0085,0x0083,
+ 0x974B,0x974A,0x0083,0x9749,0x9748,0x0089,0x0085,0x0083,0x9747,0x9746,0x0083,0x9745,0xE8F8,0x0085,0x0083,0x9744,
+ 0xD7AE,0x0083,0xBDB0,0xE8ED,0x0091,0x0089,0x0085,0x0083,0xE8EB,0xC7C5,0x0083,0xE8E7,0xB5B5,0x0085,0x0083,0xE8E5,
+ 0xE8E3,0x0083,0xE8E2,0x9743,0x0089,0x0085,0x0083,0x9742,0x9741,0x0083,0x9740,0x96FE,0x0085,0x0083,0x96FD,0x96FC,
+ 0x0083,0x96FB,0x96FA,0x087B,0x047D,0x027F,0x017F,0x00FF,0x00BF,0x009F,0x008F,0x0087,0x0083,0x96F9,0x0083,0xE8EA,
+ 0xBDDB,0x0085,0x0083,0xBBB8,0x96F8,0x0083,0xC9A3,0xCDA9,0x0089,0x0085,0x0083,0x96F7,0xE8E4,0x0083,0x96F6,0xD7C0,
+ 0x0085,0x0083,0x96F5,0xE8F0,0x0083,0xE8F1,0xB0B8,0x0091,0x0089,0x0085,0x0083,0x96F4,0xBFF2,0x0083,0xCEA6,0xE8E6,
+ 0x0085,0x0083,0xCCD2,0xB9F0,0x0083,0xE8EC,0xE8EE,0x0089,0x0085,0x0083,0x96F3,0xE8EF,0x0083,0xD4D4,0xB8F1,0x0085,
+ 0x0083,0x96F2,0x96F1,0x0083,0xB8F9,0xBACB,0x00A1,0x0091,0x0089,0x0085,0x0083,0xD1F9,0x96F0,0x0083,0x96EF,0x96EE,
+ 0x0085,0x0083,0xE8E1,0xE8E0,0x0083,0x96ED,0x96EC,0x0089,0x0085,0x0083,0x96EB,0x96EA,0x0083,0x96E9,0x96E8,0x0085,
+ 0x0083,0x96E7,0xD6EA,0x0083,0xE8F2,0x96E6,0x0091,0x0089,0x0085,0x0083,0x96E5,0x96E4,0x0083,0x96E3,0x96E2,0x0085,
+ 0x0083,0x96E1,0x96E0,0x0083,0xD0A3,0x96DF,0x0089,0x0085,0x0083,0x96DE,0x96DD,0x0083,0xE8E9,0x96DC,0x0085,0x0083,
+ 0x96DB,0x96DA,0x0083,0x96D9,0x96D8,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0xC0F5,0xC6DC,0x0083,0x96D7,0x96D6,
+ 0x0085,0x0083,0xCBA8,0x96D5,0x0083,0xCAF7,0x96D4,0x0089,0x0085,0x0083,0xC0B8,0xE8DD,0x0083,0x96D3,0xE8D3,0x0085,
+ 0x0083,0xB6B0,0xE8D0,0x0083,0xE8CE,0xD5BB,0x0091,0x0089,0x0085,0x0083,0xB1EA,0x96D2,0x0083,0xD5A4,0x96D1,0x0085,
+ 0x0083,0x96D0,0x96CF,0x0083,0x96CE,0xE8D9,0x0089,0x0085,0x0083,0xCAC1,0x96CD,0x0083,0xE8DF,0x96CC,0x0085,0x0083,
+ 0x96CB,0x96CA,0x0083,0x96C9,0x96C8,0x00A1,0x0091,0x0089,0x0085,0x0083,0x96C7,0x96C6,0x0083,0x96C5,0xB2F1,0x0085,
+ 0x0083,0xC1F8,0x96C4,0x0083,0xD6F9,0xE8CD,0x0089,0x0085,0x0083,0xBFC2,0x96C3,0x0083,0x96C2,0xBCED,0x0085,0x0083,
+ 0x96C1,0x96C0,0x0083,0xE8D1,0x96BF,0x0091,0x0089,0x0085,0x0083,0x96BE,0x96BD,0x0083,0xB2E9,0x96BC,0x0085,0x0083,
+ 0x96BB,0xE8DC,0x0083,0x96BA,0xC4FB,0x0089,0x0085,0x0083,0x96B9,0xD7F5,0x0083,0xE8D8,0xB9F1,0x0085,0x0083,0x96B8,
+ 0xE8D6,0x0083,0xE8D4,0xE8CF,0x0101,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0x96B7,0x96B6,0x0083,0x96B5,0xC8E1,
+ 0x0085,0x0083,0xC8BE,0xC6E2,0x0083,0xB8CC,0xC4B3,0x0089,0x0085,0x0083,0xB0D8,0x96B4,0x0083,0x96B3,0x96B2,0x0085,
+ 0x0083,0x96B1,0x96B0,0x0083,0x96AF,0x96AE,0x0091,0x0089,0x0085,0x0083,0x96AD,0x96AC,0x0083,0x96AB,0xB1FA,0x0085,
+ 0x0083,0xE8DA,0x96AA,0x0083,0xE8DE,0x96A9,0x0089,0x0085,0x0083,0x96A8,0x96A7,0x0083,0x96A6,0x96A5,0x0085,0x0083,
+ 0x96A4,0x96A3,0x0083,0x96A2,0xE8DB,0x00A1,0x0091,0x0089,0x0085,0x0083,0xBCCF,0xBCDC,0x0083,0xE8D5,0x96A1,0x0085,
+ 0x0083,0xE8D7,0x96A0,0x0083,0x969F,0xE8D2,0x0089,0x0085,0x0083,0xBFDD,0x969E,0x0083,0xE8C9,0x969D,0x0085,0x0083,
+ 0xB7E3,0xC7B9,0x0083,0x969C,0xE8C7,0x0091,0x0089,0x0085,0x0083,0xE8C5,0x969B,0x0083,0xE8C0,0x969A,0x0085,0x0083,
+ 0xD4E6,0xCAE0,0x0083,0x9699,0x9698,0x0089,0x0085,0x0083,0x9697,0xE8C8,0x0083,0xD6A6,0xB9FB,0x0085,0x0083,0x9696,
+ 0xC3B6,0x0083,0x9695,0xE8C4,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0xC1D6,0x9694,0x0083,0xD5ED,0x9693,0x0085,
+ 0x0083,0x9692,0x9691,0x0083,0x9690,0xCEF6,0x0089,0x0085,0x0083,0x968F,0x968E,0x0083,0x968D,0x968C,0x0085,0x0083,
+ 0xE8CA,0x968B,0x0083,0xCDF7,0x968A,0x0091,0x0089,0x0085,0x0083,0xE8C1,0x9689,0x0083,0x9688,0xB9B9,0x0085,0x0083,
+ 0x9687,0x9686,0x0083,0xBCAB,0x9685,0x0089,0x0085,0x0083,0xB0E5,0xCBC9,0x0083,0x9684,0xE8CC,0x0085,0x0083,0x9683,
+ 0x9682,0x0083,0x9681,0x9680,0x00A1,0x0091,0x0089,0x0085,0x0083,0xE8CB,0x967E,0x0083,0xE8C6,0x967D,0x0085,0x0083,
+ 0xE8C3,0xEABD,0x0083,0x967C,0xBDDC,0x0089,0x0085,0x0083,0xB1AD,0x967B,0x0083,0xBABC,0x967A,0x0085,0x0083,0x9679,
+ 0xE8C2,0x0083,0xE8BF,0xD1EE,0x0091,0x0089,0x0085,0x0083,0x9678,0x9677,0x0083,0xC0B4,0x9676,0x0085,0x0083,0x9675,
+ 0x9674,0x0083,0xCCF5,0xB8DC,0x0089,0x0085,0x0083,0xCAF8,0xE8BD,0x0083,0x9673,0xB6C5,0x0085,0x0083,0x9672,0x9671,
+ 0x0083,0x9670,0x966F,0x017F,0x00FF,0x00BF,0x009F,0x008F,0x0087,0x0083,0x966E,0x0083,0xD5C8,0x966D,0x0085,0x0083,
+ 0x966C,0xE8BC,0x0083,0x966B,0xB4E5,0x0089,0x0085,0x0083,0xB2C4,0xD0D3,0x0083,0xC0EE,0x966A,0x0085,0x0083,0xE8BB,
+ 0x9669,0x0083,0x9668,0xC9BC,0x0091,0x0089,0x0085,0x0083,0xE8BE,0x9667,0x0083,0xB8CB,0x9666,0x0085,0x0083,0x9665,
+ 0xC8A8,0x0083,0xD4D3,0x9664,0x0089,0x0085,0x0083,0xC9B1,0x9663,0x0083,0x9662,0xD0E0,0x0085,0x0083,0x9661,0x9660,
+ 0x0083,0xBBFA,0x965F,0x00A1,0x0091,0x0089,0x0085,0x0083,0x965E,0x965D,0x0083,0x965C,0xB6E4,0x0085,0x0083,0xC6D3,
+ 0x965B,0x0083,0x965A,0xD6EC,0x0089,0x0085,0x0083,0x9659,0xCAF5,0x0083,0x9658,0xD4FD,0x0085,0x0083,0xB1BE,0xC4A9,
+ 0x0083,0xCEB4,0x9657,0x0091,0x0089,0x0085,0x0083,0xC4BE,0x9656,0x0083,0xEBFC,0x9655,0x0085,0x0083,0x9654,0x9653,
+ 0x0083,0x9652,0x9651,0x0089,0x0085,0x0083,0x9650,0xC6DA,0x0083,0x964F,0xB3AF,0x0085,0x0083,0x964E,0xCDFB,0x0083,
+ 0x964D,0x964C,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0x964B,0xC0CA,0x0083,0x964A,0xEBDE,0x0085,0x0083,0xCBB7,
+ 0x9649,0x0083,0x9648,0x9647,0x0089,0x0085,0x0083,0xEBD4,0x9646,0x0083,0x9645,0xB7FE,0x0085,0x0083,0x9644,0xC5F3,
+ 0x0083,0xEBC3,0xD3D0,0x0091,0x0089,0x0085,0x0083,0xD4C2,0x9643,0x0083,0x9642,0x9641,0x0085,0x0083,0x9640,0x95FE,
+ 0x0083,0x95FD,0x95FC,0x0089,0x0085,0x0083,0xD7EE,0xCCE6,0x0083,0xD4F8,0x95FB,0x0085,0x0083,0xC2FC,0x95FA,0x0083,
+ 0x95F9,0xB2DC,0x00A1,0x0091,0x0089,0x0085,0x0083,0x95F8,0xEAC2,0x0083,0x95F7,0x95F6,0x0085,0x0083,0xB8FC,0xD2B7,
+ 0x0083,0xC7FA,0x95F5,0x0089,0x0085,0x0083,0xD4BB,0x95F4,0x0083,0x95F3,0x95F2,0x0085,0x0083,0x95F1,0x95F0,0x0083,
+ 0x95EF,0xEAD9,0x0091,0x0089,0x0085,0x0083,0x95EE,0x95ED,0x0083,0xEAD8,0x95EC,0x0085,0x0083,0x95EB,0x95EA,0x0083,
+ 0x95E9,0x95E8,0x0089,0x0085,0x0083,0x95E7,0x95E6,0x0083,0x95E5,0xC6D8,0x0085,0x0083,0xEAD7,0xEAD6,0x0083,0x95E4,
+ 0xCAEF,0x0101,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0x95E3,0x95E2,0x0083,0x95E1,0x95E0,0x0085,0x0083,0x95DF,
+ 0x95DE,0x0083,0x95DD,0x95DC,0x0089,0x0085,0x0083,0x95DB,0x95DA,0x0083,0x95D9,0x95D8,0x0085,0x0083,0x95D7,0x95D6,
+ 0x0083,0x95D5,0x95D4,0x0091,0x0089,0x0085,0x0083,0x95D3,0x95D2,0x0083,0x95D1,0x95D0,0x0085,0x0083,0x95CF,0x95CE,
+ 0x0083,0x95CD,0x95CC,0x0089,0x0085,0x0083,0x95CB,0x95CA,0x0083,0xEAD5,0x95C9,0x0085,0x0083,0x95C8,0x95C7,0x0083,
+ 0x95C6,0xE5DF,0x00A1,0x0091,0x0089,0x0085,0x0083,0x95C5,0x95C4,0x0083,0x95C3,0x95C2,0x0085,0x0083,0xB1A9,0x95C1,
+ 0x0083,0x95C0,0x95BF,0x0089,0x0085,0x0083,0x95BE,0x95BD,0x0083,0xC4BA,0x95BC,0x0085,0x0083,0x95BB,0x95BA,0x0083,
+ 0x95B9,0x95B8,0x0091,0x0089,0x0085,0x0083,0xF4DF,0xEAD3,0x0083,0x95B7,0x95B6,0x0085,0x0083,0x95B5,0x95B4,0x0083,
+ 0x95B3,0x95B2,0x0089,0x0085,0x0083,0x95B1,0x95B0,0x0083,0x95AF,0xEAD4,0x0085,0x0083,0x95AE,0x95AD,0x0083,0x95AC,
+ 0x95AB,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0x95AA,0xB0B5,0x0083,0xC5AF,0x95A9,0x0085,0x0083,0x95A8,0x95A7,
+ 0x0083,0x95A6,0xCAEE,0x0089,0x0085,0x0083,0x95A5,0x95A4,0x0083,0x95A3,0x95A2,0x0085,0x0083,0xEAD2,0x95A1,0x0083,
+ 0x95A0,0x959F,0x0091,0x0089,0x0085,0x0083,0x959E,0xCFBE,0x0083,0x959D,0x959C,0x0085,0x0083,0xEAD1,0x959B,0x0083,
+ 0xD4DD,0x959A,0x0089,0x0085,0x0083,0x9599,0x9598,0x0083,0xC1C0,0x9597,0x0085,0x0083,0x9596,0x9595,0x0083,0xD6C7,
+ 0x9594,0x00A1,0x0091,0x0089,0x0085,0x0083,0x9593,0xEAD0,0x0083,0xBEA7,0x9592,0x0085,0x0083,0xC7E7,0x9591,0x0083,
+ 0x9590,0x958F,0x0089,0x0085,0x0083,0xCEFA,0xBEB0,0x0083,0xC6D5,0x958E,0x0085,0x0083,0x958D,0x958C,0x0083,0x958B,
+ 0x958A,0x0091,0x0089,0x0085,0x0083,0xB3BF,0x9589,0x0083,0xBBDE,0x9588,0x0085,0x0083,0xCEEE,0x9587,0x0083,0x9586,
+ 0xEACE,0x0089,0x0085,0x0083,0x9585,0xEAC9,0x0083,0x9584,0x9583,0x0085,0x0083,0x9582,0x9581,0x0083,0xCDED,0x9580,
+ 0x027F,0x017F,0x00FF,0x00BF,0x009F,0x008F,0x0087,0x0083,0x957E,0x0083,0xEACF,0xEACD,0x0085,0x0083,0xD4CE,0xEACA,
+ 0x0083,0xCFFE,0xC9B9,0x0089,0x0085,0x0083,0x957D,0x957C,0x0083,0xEACC,0x957B,0x0085,0x0083,0x957A,0xC9CE,0x0083,
+ 0xBDFA,0x9579,0x0091,0x0089,0x0085,0x0083,0x9578,0x9577,0x0083,0x9576,0x9575,0x0085,0x0083,0x9574,0x9573,0x0083,
+ 0xBBCE,0x9572,0x0089,0x0085,0x0083,0xEACB,0x9571,0x0083,0x9570,0xCFD4,0x0085,0x0083,0x956F,0xD6E7,0x0083,0x956E,
+ 0x956D,0x00A1,0x0091,0x0089,0x0085,0x0083,0x956C,0x956B,0x0083,0x956A,0xEAC6,0x0085,0x0083,0xEAC7,0xEAC4,0x0083,
+ 0x9569,0x9568,0x0089,0x0085,0x0083,0xEAC5,0x9567,0x0083,0xCAC7,0x9566,0x0085,0x0083,0xD5D1,0x9565,0x0083,0x9564,
+ 0x9563,0x0091,0x0089,0x0085,0x0083,0x9562,0xD7F2,0x0083,0xC3C1,0x9561,0x0085,0x0083,0xB4BA,0x9560,0x0083,0x955F,
+ 0x955E,0x0089,0x0085,0x0083,0x955D,0xD3B3,0x0083,0xD0C7,0x955C,0x0085,0x0083,0xEAC3,0x955B,0x0083,0x955A,0x9559,
+ 0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0xEABC,0x9558,0x0083,0x9557,0x9556,0x0085,0x0083,0xEABF,0xCEF4,0x0083,
+ 0xD2D7,0x9555,0x0089,0x0085,0x0083,0x9554,0x9553,0x0083,0xBBE8,0xC3F7,0x0085,0x0083,0x9552,0xB2FD,0x0083,0x9551,
+ 0xEABB,0x0091,0x0089,0x0085,0x0083,0x9550,0x954F,0x0083,0x954E,0xC0A5,0x0085,0x0083,0x954D,0x954C,0x0083,0xEABE,
+ 0xB0BA,0x0089,0x0085,0x0083,0x954B,0xEAC0,0x0083,0x954A,0x9549,0x0085,0x0083,0x9548,0x9547,0x0083,0x9546,0xCDFA,
+ 0x00A1,0x0091,0x0089,0x0085,0x0083,0x9545,0x9544,0x0083,0xBFF5,0xCAB1,0x0085,0x0083,0x9543,0x9542,0x0083,0x9541,
+ 0x9540,0x0089,0x0085,0x0083,0xBAB5,0xEABA,0x0083,0xEAB9,0xEAB8,0x0085,0x0083,0xD0F1,0xD1AE,0x0083,0x94FE,0x94FD,
+ 0x0091,0x0089,0x0085,0x0083,0xD4E7,0xD6BC,0x0083,0xBEC9,0xB5A9,0x0085,0x0083,0xC8D5,0x94FC,0x0083,0x94FB,0xBCC8,
+ 0x0089,0x0085,0x0083,0x94FA,0xCEDE,0x0083,0x94F9,0x94F8,0x0085,0x0083,0x94F7,0x94F6,0x0083,0x94F5,0x94F4,0x0101,
+ 0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0x94F3,0x94F2,0x0083,0xC6EC,0xECBD,0x0085,0x0083,0x94F1,0x94F0,0x0083,
+ 0x94EF,0xECBC,0x0089,0x0085,0x0083,0x94EE,0x94ED,0x0083,0xD7E5,0xECBB,0x0085,0x0083,0x94EC,0xECBA,0x0083,0xD0FD,
+ 0x94EB,0x0091,0x0089,0x0085,0x0083,0x94EA,0x94E9,0x0083,0x94E8,0xECB7,0x0085,0x0083,0xC2C3,0xECB8,0x0083,0xECB9,
+ 0x94E7,0x0089,0x0085,0x0083,0xC5D4,0x94E6,0x0083,0x94E5,0x94E4,0x0085,0x0083,0xCAA9,0xECB6,0x0083,0x94E3,0x94E2,
+ 0x00A1,0x0091,0x0089,0x0085,0x0083,0xB7BD,0x94E1,0x0083,0x94E0,0x94DF,0x0085,0x0083,0x94DE,0x94DD,0x0083,0x94DC,
+ 0x94DB,0x0089,0x0085,0x0083,0x94DA,0xD0C2,0x0083,0xCBB9,0x94D9,0x0085,0x0083,0xB6CF,0x94D8,0x0083,0xEDBD,0x94D7,
+ 0x0091,0x0089,0x0085,0x0083,0xD5B6,0x94D6,0x0083,0xB8AB,0x94D5,0x0085,0x0083,0xB3E2,0xBDEF,0x0083,0x94D4,0x94D3,
+ 0x0089,0x0085,0x0083,0xCED3,0x94D2,0x0083,0xD5E5,0x94D1,0x0085,0x0083,0x94D0,0xD0B1,0x0083,0xF5FA,0x94CF,0x00C1,
+ 0x00A1,0x0091,0x0089,0x0085,0x0083,0xC1CF,0x94CE,0x0083,0xB6B7,0x94CD,0x0085,0x0083,0x94CC,0x94CB,0x0083,0xECB5,
+ 0x94CA,0x0089,0x0085,0x0083,0xB0DF,0xECB3,0x0083,0x94C9,0x94C8,0x0085,0x0083,0x94C7,0xB1F3,0x0083,0xD5AB,0x94C6,
+ 0x0091,0x0089,0x0085,0x0083,0x94C5,0x94C4,0x0083,0xCEC4,0x94C3,0x0085,0x0083,0x94C2,0x94C1,0x0083,0x94C0,0x94BF,
+ 0x0089,0x0085,0x0083,0x94BE,0x94BD,0x0083,0x94BC,0x94BB,0x0085,0x0083,0x94BA,0x94B9,0x0083,0x94B8,0x94B7,0x00A1,
+ 0x0091,0x0089,0x0085,0x0083,0x94B6,0x94B5,0x0083,0xB7F3,0x94B4,0x0085,0x0083,0x94B3,0xD5FB,0x0083,0x94B2,0xC7C3,
+ 0x0089,0x0085,0x0083,0x94B1,0xCAFD,0x0083,0x94B0,0x94AF,0x0085,0x0083,0x94AE,0xBEB4,0x0083,0xEBB8,0x94AD,0x0091,
+ 0x0089,0x0085,0x0083,0x94AC,0x94AB,0x0083,0x94AA,0xB6D8,0x0085,0x0083,0x94A9,0x94A8,0x0083,0xC9A2,0xB8D2,0x0089,
+ 0x0085,0x0083,0x94A7,0x94A6,0x0083,0x94A5,0xB3A8,0x0085,0x0083,0xB1D6,0x94A4,0x0083,0xC1B2,0x94A3,0x0181,0x0101,
+ 0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0xBDCC,0x94A2,0x0083,0x94A1,0xB0BD,0x0085,0x0083,0xEBB7,0x94A0,0x0083,
+ 0x949F,0x949E,0x0089,0x0085,0x0083,0xBEC8,0x949D,0x0083,0xC3F4,0x949C,0x0085,0x0083,0x949B,0xB5D0,0x0083,0x949A,
+ 0x9499,0x0091,0x0089,0x0085,0x0083,0xF4CD,0xD0A7,0x0083,0x9498,0x9497,0x0085,0x0083,0xB9CA,0x9496,0x0083,0x9495,
+ 0x9494,0x0089,0x0085,0x0083,0x9493,0x9492,0x0083,0xD5FE,0xB7C5,0x0085,0x0083,0x9491,0x9490,0x0083,0xB9A5,0x948F,
+ 0x00A1,0x0091,0x0089,0x0085,0x0083,0xB8C4,0xD8FC,0x0083,0x948E,0xCAD5,0x0085,0x0083,0xEBB6,0xEAB7,0x0083,0x948D,
+ 0x948C,0x0089,0x0085,0x0083,0x948B,0x948A,0x0083,0xD6A7,0xDFAD,0x0085,0x0083,0x9489,0x9488,0x0083,0xBEF0,0x9487,
+ 0x0091,0x0089,0x0085,0x0083,0x9486,0x9485,0x0083,0x9484,0x9483,0x0085,0x0083,0xDFAC,0x9482,0x0083,0x9481,0x9480,
+ 0x0089,0x0085,0x0083,0x947E,0x947D,0x0083,0x947C,0x947B,0x0085,0x0083,0x947A,0x9479,0x0083,0x9478,0x9477,0x00C1,
+ 0x00A1,0x0091,0x0089,0x0085,0x0083,0x9476,0xC8C1,0x0083,0x9475,0x9474,0x0085,0x0083,0x9473,0x9472,0x0083,0x9471,
+ 0xD4DC,0x0089,0x0085,0x0083,0x9470,0x946F,0x0083,0x946E,0x946D,0x0085,0x0083,0x946C,0x946B,0x0083,0x946A,0x9469,
+ 0x0091,0x0089,0x0085,0x0083,0xDFAB,0x9468,0x0083,0x9467,0x9466,0x0085,0x0083,0x9465,0x9464,0x0083,0x9463,0x9462,
+ 0x0089,0x0085,0x0083,0x9461,0xC5CA,0x0083,0x9460,0x945F,0x0085,0x0083,0x945E,0x945D,0x0083,0x945C,0x945B,0x00A1,
+ 0x0091,0x0089,0x0085,0x0083,0x945A,0x9459,0x0083,0x9458,0x9457,0x0085,0x0083,0x9456,0x9455,0x0083,0x9454,0x9453,
+ 0x0089,0x0085,0x0083,0x9452,0x9451,0x0083,0x9450,0x944F,0x0085,0x0083,0x944E,0x944D,0x0083,0x944C,0x944B,0x0091,
+ 0x0089,0x0085,0x0083,0x944A,0x9449,0x0083,0x9448,0xB2C1,0x0085,0x0083,0x9447,0xDFA9,0x0083,0x9446,0xDFAA,0x0089,
+ 0x0085,0x0083,0x9445,0x9444,0x0083,0x9443,0xCBD3,0x0085,0x0083,0x9442,0x9441,0x0083,0x9440,0x93FE,0x0101,0x00C1,
+ 0x00A1,0x0091,0x0089,0x0085,0x0083,0x93FD,0xEBA2,0x0083,0xDFA8,0x93FC,0x0085,0x0083,0x93FB,0x93FA,0x0083,0x93F9,
+ 0xC7DC,0x0089,0x0085,0x0083,0x93F8,0xDFA7,0x0083,0x93F7,0xC7E6,0x0085,0x0083,0xB2D9,0x93F6,0x0083,0x93F5,0x93F4,
+ 0x0091,0x0089,0x0085,0x0083,0x93F3,0x93F2,0x0083,0x93F1,0x93F0,0x0085,0x0083,0xC9C3,0x93EF,0x0083,0x93EE,0xC0DE,
+ 0x0089,0x0085,0x0083,0x93ED,0xDFA6,0x0083,0x93EC,0x93EB,0x0085,0x0083,0x93EA,0xBAB3,0x0083,0x93E9,0xDFA5,0x00A1,
+ 0x0091,0x0089,0x0085,0x0083,0x93E8,0xDFA3,0x0083,0xDFA2,0x93E7,0x0085,0x0083,0xC4EC,0x93E6,0x0083,0x93E5,0x93E4,
+ 0x0089,0x0085,0x0083,0x93E3,0xD7AB,0x0083,0x93E2,0xB4E9,0x0085,0x0083,0xB2A5,0xC7CB,0x0083,0x93E1,0x93E0,0x0091,
+ 0x0089,0x0085,0x0083,0xC1C3,0x93DF,0x0083,0x93DE,0x93DD,0x0085,0x0083,0x93DC,0xB3B7,0x0083,0x93DB,0x93DA,0x0089,
+ 0x0085,0x0083,0x93D9,0x93D8,0x0083,0x93D7,0xD7B2,0x0085,0x0083,0x93D6,0x93D5,0x0083,0x93D4,0x93D3,0x00C1,0x00A1,
+ 0x0091,0x0089,0x0085,0x0083,0xDFA4,0x93D2,0x0083,0x93D1,0xDEFE,0x0085,0x0083,0xCBBA,0x93D0,0x0083,0x93CF,0xC8F6,
+ 0x0089,0x0085,0x0083,0xB3C5,0x93CE,0x0083,0x93CD,0x93CC,0x0085,0x0083,0x93CB,0x93CA,0x0083,0x93C9,0x93C8,0x0091,
+ 0x0089,0x0085,0x0083,0x93C7,0x93C6,0x0083,0xC6B2,0x93C5,0x0085,0x0083,0xBEEF,0xDEFC,0x0083,0x93C4,0xC1CC,0x0089,
+ 0x0085,0x0083,0x93C3,0x93C2,0x0083,0x93C1,0x93C0,0x0085,0x0083,0x93BF,0x93BE,0x0083,0x93BD,0xDFA1,0x00A1,0x0091,
+ 0x0089,0x0085,0x0083,0xC4A1,0xC3FE,0x0083,0x93BC,0x93BB,0x0085,0x0083,0x93BA,0x93B9,0x0083,0x93B8,0x93B7,0x0089,
+ 0x0085,0x0083,0x93B6,0x93B5,0x0083,0x93B4,0x93B3,0x0085,0x0083,0xDEFD,0x93B2,0x0083,0x93B1,0x93B0,0x0091,0x0089,
+ 0x0085,0x0083,0xC4A6,0x93AF,0x0083,0xB4DD,0x93AE,0x0085,0x0083,0x93AD,0x93AC,0x0083,0x93AB,0x93AA,0x0089,0x0085,
+ 0x0083,0x93A9,0x93A8,0x0083,0x93A7,0xDEFB,0x0085,0x0083,0x93A6,0x93A5,0x0083,0x93A4,0x93A3,0x047D,0x027F,0x017F,
+ 0x00FF,0x00BF,0x009F,0x008F,0x0087,0x0083,0x93A2,0x0083,0xD5AA,0x93A1,0x0085,0x0083,0x93A0,0x939F,0x0083,0xCBA4,
+ 0x939E,0x0089,0x0085,0x0083,0xDEF0,0x939D,0x0083,0x939C,0x939B,0x0085,0x0083,0x939A,0x9399,0x0083,0x9398,0x9397,
+ 0x0091,0x0089,0x0085,0x0083,0xCCAF,0x9396,0x0083,0xB1F7,0xD2A1,0x0085,0x0083,0xB0DA,0xDEF3,0x0083,0xC9E3,0x9395,
+ 0x0089,0x0085,0x0083,0x9394,0xDEF4,0x0083,0x9393,0xEBA1,0x0085,0x0083,0x9392,0xB2EB,0x0083,0x9391,0x9390,0x00A1,
+ 0x0091,0x0089,0x0085,0x0083,0xD0AF,0x938F,0x0083,0x938E,0x938D,0x0085,0x0083,0x938C,0x938B,0x0083,0xE5BA,0x938A,
+ 0x0089,0x0085,0x0083,0x9389,0x9388,0x0083,0x9387,0x9386,0x0085,0x0083,0x9385,0xB4EE,0x0083,0xB0E1,0x9384,0x0091,
+ 0x0089,0x0085,0x0083,0xCCC2,0x9383,0x0083,0x9382,0x9381,0x0085,0x0083,0xDEF9,0x9380,0x0083,0x937E,0x937D,0x0089,
+ 0x0085,0x0083,0x937C,0xDEFA,0x0083,0xDEF7,0x937B,0x0085,0x0083,0xB8E3,0x937A,0x0083,0xCBD1,0xDEF6,0x00C1,0x00A1,
+ 0x0091,0x0089,0x0085,0x0083,0x9379,0x9378,0x0083,0x9377,0x9376,0x0085,0x0083,0x9375,0x9374,0x0083,0xC9A6,0xB4EA,
+ 0x0089,0x0085,0x0083,0x9373,0x9372,0x0083,0xB4A4,0xB2AB,0x0085,0x0083,0x9371,0x9370,0x0083,0xDEF8,0xDEF5,0x0091,
+ 0x0089,0x0085,0x0083,0x936F,0x936E,0x0083,0x936D,0x936C,0x0085,0x0083,0x936B,0xBDC1,0x0083,0x936A,0x9369,0x0089,
+ 0x0085,0x0083,0xC2A7,0xB8E9,0x0083,0xB2F3,0xDEEC,0x0085,0x0083,0x9368,0xC0BF,0x0083,0x9367,0x9366,0x00A1,0x0091,
+ 0x0089,0x0085,0x0083,0x9365,0x9364,0x0083,0xDEEA,0x9363,0x0085,0x0083,0xDEDE,0x9362,0x0083,0xD4AE,0x9361,0x0089,
+ 0x0085,0x0083,0xDEE9,0x9360,0x0083,0x935F,0x935E,0x0085,0x0083,0x935D,0xBDD2,0x0083,0x935C,0x935B,0x0091,0x0089,
+ 0x0085,0x0083,0xBEBE,0xBFAB,0x0083,0x935A,0x9359,0x0085,0x0083,0x9358,0x9357,0x0083,0x9356,0xB4A7,0x0089,0x0085,
+ 0x0083,0x9355,0xCED5,0x0083,0xDEEB,0x9354,0x0085,0x0083,0xDEEE,0x9353,0x0083,0x9352,0x9351,0x0101,0x00C1,0x00A1,
+ 0x0091,0x0089,0x0085,0x0083,0x9350,0x934F,0x0083,0x934E,0x934D,0x0085,0x0083,0xD2BE,0x934C,0x0083,0x934B,0x934A,
+ 0x0089,0x0085,0x0083,0xB2E5,0x9349,0x0083,0xCCE1,0xC3E8,0x0085,0x0083,0xDEEF,0xD7E1,0x0083,0x9348,0x9347,0x0091,
+ 0x0089,0x0085,0x0083,0x9346,0xC8E0,0x0083,0x9345,0x9344,0x0085,0x0083,0xDEF1,0x9343,0x0083,0xDEED,0x9342,0x0089,
+ 0x0085,0x0083,0x9341,0x9340,0x0083,0x92FE,0x92FD,0x0085,0x0083,0xDEF2,0x92FC,0x0083,0xDEE8,0x92FB,0x00A1,0x0091,
+ 0x0089,0x0085,0x0083,0xB2F4,0x92FA,0x0083,0xB5A7,0xD6C0,0x0085,0x0083,0x92F9,0x92F8,0x0083,0xDEE2,0xC2B0,0x0089,
+ 0x0085,0x0083,0x92F7,0x92F6,0x0083,0xEAFE,0x92F5,0x0085,0x0083,0xDEE7,0xDEDD,0x0083,0xDEE4,0x92F4,0x0091,0x0089,
+ 0x0085,0x0083,0xB4EB,0xD1DA,0x0083,0xCDC6,0xBFD8,0x0085,0x0083,0x92F3,0xBDD3,0x0083,0x92F2,0xB3B8,0x0089,0x0085,
+ 0x0083,0xCCBD,0x92F1,0x0083,0xC2D3,0x92F0,0x0085,0x0083,0x92EF,0x92EE,0x0083,0x92ED,0x92EC,0x00C1,0x00A1,0x0091,
+ 0x0089,0x0085,0x0083,0x92EB,0x92EA,0x0083,0xBEF2,0x92E9,0x0085,0x0083,0xD2B4,0x92E8,0x0083,0x92E7,0x92E6,0x0089,
+ 0x0085,0x0083,0xC5C5,0x92E5,0x0083,0xC6FE,0xCCCD,0x0085,0x0083,0xDEE1,0x92E4,0x0083,0xD5C6,0x92E3,0x0091,0x0089,
+ 0x0085,0x0083,0xDEE5,0xB5F4,0x0083,0xCADA,0xB6DE,0x0085,0x0083,0x92E2,0x92E1,0x0083,0x92E0,0x92DF,0x0089,0x0085,
+ 0x0083,0xB5E0,0x92DE,0x0083,0xCFC6,0x92DD,0x0085,0x0083,0x92DC,0x92DB,0x0083,0x92DA,0xC4ED,0x00A1,0x0091,0x0089,
+ 0x0085,0x0083,0xDEE0,0x92D9,0x0083,0x92D8,0xBDDD,0x0085,0x0083,0xB4B7,0x92D7,0x0083,0x92D6,0x92D5,0x0089,0x0085,
+ 0x0083,0x92D4,0xDEDF,0x0083,0x92D3,0x92D2,0x0085,0x0083,0xBEDD,0xDEE3,0x0083,0x92D1,0x92D0,0x0091,0x0089,0x0085,
+ 0x0083,0x92CF,0xDEE6,0x0083,0x92CE,0xC5F5,0x0085,0x0083,0x92CD,0x92CC,0x0083,0x92CB,0xB5B7,0x0089,0x0085,0x0083,
+ 0xBBBB,0xBCF1,0x0083,0x92CA,0xCBF0,0x0085,0x0083,0xC0CC,0x92C9,0x0083,0x92C8,0x92C7,0x017F,0x00FF,0x00BF,0x009F,
+ 0x008F,0x0087,0x0083,0x92C6,0x0083,0x92C5,0x92C4,0x0085,0x0083,0x92C3,0x92C2,0x0083,0xB2B6,0x92C1,0x0089,0x0085,
+ 0x0083,0x92C0,0x92BF,0x0083,0x92BE,0xBEE8,0x0085,0x0083,0xC4F3,0xC9D3,0x0083,0xBAB4,0xB0C6,0x0091,0x0089,0x0085,
+ 0x0083,0xDEDB,0x92BD,0x0083,0xD7BD,0x92BC,0x0085,0x0083,0x92BB,0xC0A6,0x0083,0xCDB1,0x92BA,0x0089,0x0085,0x0083,
+ 0xDEDC,0xCEE6,0x0083,0x92B9,0x92B8,0x0085,0x0083,0x92B7,0x92B6,0x0083,0xCDEC,0x92B5,0x00A1,0x0091,0x0089,0x0085,
+ 0x0083,0x92B4,0xCDA6,0x0083,0xDEDA,0x92B3,0x0085,0x0083,0x92B2,0x92B1,0x0083,0x92B0,0x92AF,0x0089,0x0085,0x0083,
+ 0x92AE,0xEAFD,0x0083,0x92AD,0x92AC,0x0085,0x0083,0xD5F1,0x92AB,0x0083,0x92AA,0x92A9,0x0091,0x0089,0x0085,0x0083,
+ 0xB4EC,0xC5B2,0x0083,0x92A8,0xB0A4,0x0085,0x0083,0x92A7,0x92A6,0x0083,0xBBD3,0xBCB7,0x0089,0x0085,0x0083,0xD5F5,
+ 0xDED8,0x0083,0xB5B2,0xC4D3,0x0085,0x0083,0xD0AE,0xCCA2,0x0083,0xCECE,0x92A5,0x00C1,0x00A1,0x0091,0x0089,0x0085,
+ 0x0083,0xC2CE,0xD6BF,0x0083,0x92A4,0x92A3,0x0085,0x0083,0x92A2,0xCDDA,0x0083,0x92A1,0x92A0,0x0089,0x0085,0x0083,
+ 0x929F,0x929E,0x0083,0xCCF4,0x929D,0x0085,0x0083,0x929C,0xBFE6,0x0083,0x929B,0x929A,0x0091,0x0089,0x0085,0x0083,
+ 0x9299,0x9298,0x0083,0xB0B4,0xEAFC,0x0085,0x0083,0xD6B8,0x9297,0x0083,0x9296,0x9295,0x0089,0x0085,0x0083,0x9294,
+ 0xB9D2,0x0083,0xB3D6,0x9293,0x0085,0x0083,0xC4C3,0xCAB0,0x0083,0xD7A7,0xC6B4,0x00A1,0x0091,0x0089,0x0085,0x0083,
+ 0x9292,0x9291,0x0083,0x9290,0x928F,0x0085,0x0083,0xBFBD,0xDED9,0x0083,0x928E,0xCBA9,0x0089,0x0085,0x0083,0xC8AD,
+ 0x928D,0x0083,0xB9B0,0x928C,0x0085,0x0083,0xD5FC,0xDED7,0x0083,0xCAC3,0xC0A8,0x0091,0x0089,0x0085,0x0083,0x928B,
+ 0x928A,0x0083,0xD4F1,0xB2A6,0x0085,0x0083,0xC5A1,0xC0B9,0x0083,0xD3B5,0x9289,0x0089,0x0085,0x0083,0xBCF0,0xC2A3,
+ 0x0083,0x9288,0x9287,0x0085,0x0083,0xC4E2,0x9286,0x0083,0x9285,0xB0DD,0x0101,0x00C1,0x00A1,0x0091,0x0089,0x0085,
+ 0x0083,0xD5D0,0xDED5,0x0083,0xD7BE,0xBED0,0x0085,0x0083,0xDED6,0xCDCF,0x0083,0x9284,0xB0CE,0x0089,0x0085,0x0083,
+ 0xCDD8,0xBEDC,0x0083,0x9283,0xB9D5,0x0085,0x0083,0x9282,0xC1E0,0x0083,0xC5C4,0xB0E8,0x0091,0x0089,0x0085,0x0083,
+ 0x9281,0xDED4,0x0083,0xC0AD,0xC4E9,0x0085,0x0083,0xC4B4,0xB2F0,0x0083,0xB5A3,0xD6F4,0x0089,0x0085,0x0083,0x9280,
+ 0xB7F7,0x0083,0x927E,0x927D,0x0085,0x0083,0xC3F2,0x927C,0x0083,0xB3E9,0xD1BA,0x00A1,0x0091,0x0089,0x0085,0x0083,
+ 0xDED3,0x927B,0x0083,0xC4A8,0x927A,0x0085,0x0083,0x9279,0x9278,0x0083,0xB5D6,0x9277,0x0089,0x0085,0x0083,0x9276,
+ 0x9275,0x0083,0xB1A7,0x9274,0x0085,0x0083,0x9273,0x9272,0x0083,0x9271,0xCCA7,0x0091,0x0089,0x0085,0x0083,0xC5FB,
+ 0x9270,0x0083,0x926F,0xC5EA,0x0085,0x0083,0x926E,0x926D,0x0083,0xB1A8,0xBBA4,0x0089,0x0085,0x0083,0x926C,0xC7C0,
+ 0x0083,0xC2D5,0xBFD9,0x0085,0x0083,0xDED2,0x926B,0x0083,0x926A,0x9269,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,
+ 0xC5D7,0xB8A7,0x0083,0x9268,0xD5DB,0x0085,0x0083,0xBFB9,0xB6B6,0x0083,0xCDB6,0x9267,0x0089,0x0085,0x0083,0xD7A5,
+ 0xCAE3,0x0083,0xD2D6,0x9266,0x0085,0x0083,0x9265,0x9264,0x0083,0x9263,0x9262,0x0091,0x0089,0x0085,0x0083,0x9261,
+ 0xB0D1,0x0083,0xBEF1,0x9260,0x0085,0x0083,0x925F,0x925E,0x0083,0x925D,0xB3AD,0x0089,0x0085,0x0083,0x925C,0x925B,
+ 0x0083,0x925A,0xBCBC,0x0085,0x0083,0xB3D0,0xD5D2,0x0083,0x9259,0xB6F3,0x00A1,0x0091,0x0089,0x0085,0x0083,0x9258,
+ 0x9257,0x0083,0xC5FA,0x9256,0x0085,0x0083,0x9255,0xB7F6,0x0083,0x9254,0x9253,0x0089,0x0085,0x0083,0xB0E2,0x9252,
+ 0x0083,0x9251,0xC8C5,0x0085,0x0083,0xB3B6,0xB0E7,0x0083,0xC5A4,0xD1EF,0x0091,0x0089,0x0085,0x0083,0xC9A8,0xDED1,
+ 0x0083,0xC0A9,0x9250,0x0085,0x0083,0xD6B4,0xC7A4,0x0083,0x924F,0x924E,0x0089,0x0085,0x0083,0xBFDB,0x924D,0x0083,
+ 0x924C,0x924B,0x0085,0x0083,0x924A,0x9249,0x0083,0x9248,0x9247,0x027F,0x017F,0x00FF,0x00BF,0x009F,0x008F,0x0087,
+ 0x0083,0xBFB8,0x0083,0x9246,0x9245,0x0085,0x0083,0xCDD0,0x9244,0x0083,0x9243,0x9242,0x0089,0x0085,0x0083,0xC8D3,
+ 0xB4F2,0x0083,0xB0C7,0xC6CB,0x0085,0x0083,0x9241,0x9240,0x0083,0xD4FA,0xB2C5,0x0091,0x0089,0x0085,0x0083,0xDED0,
+ 0xCAD6,0x0083,0x91FE,0xECE9,0x0085,0x0083,0xECE8,0xC9C8,0x0083,0x91FD,0x91FC,0x0089,0x0085,0x0083,0x91FB,0xECE7,
+ 0x0083,0x91FA,0xB1E2,0x0085,0x0083,0xCBF9,0xB7BF,0x0083,0xECE5,0xECE6,0x00A1,0x0091,0x0089,0x0085,0x0083,0x91F9,
+ 0x91F8,0x0083,0x91F7,0x91F6,0x0085,0x0083,0x91F5,0xBBA7,0x0083,0x91F4,0x91F3,0x0089,0x0085,0x0083,0xB4F7,0xB4C1,
+ 0x0083,0x91F2,0x91F1,0x0085,0x0083,0x91F0,0x91EF,0x0083,0xC2BE,0x91EE,0x0091,0x0089,0x0085,0x0083,0xEAAF,0x91ED,
+ 0x0083,0xBDD8,0x91EC,0x0085,0x0083,0x91EB,0x91EA,0x0083,0x91E9,0xEAAD,0x0089,0x0085,0x0083,0xEAAE,0x91E8,0x0083,
+ 0xEAAB,0xEAAC,0x0085,0x0083,0x91E7,0xEAAA,0x0083,0x91E6,0x91E5,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0x91E4,
+ 0xEAA9,0x0083,0xC6DD,0x91E3,0x0085,0x0083,0xD5BD,0xEAA8,0x0083,0xBBF2,0xE3DE,0x0089,0x0085,0x0083,0x91E2,0x91E1,
+ 0x0083,0xBDE4,0xCED2,0x0085,0x0083,0xB3C9,0xCFB7,0x0083,0xC8D6,0xCAF9,0x0091,0x0089,0x0085,0x0083,0xD0E7,0xEAA7,
+ 0x0083,0xCEEC,0x91E0,0x0085,0x0083,0xB8EA,0x91DF,0x0083,0xEDB0,0x91DE,0x0089,0x0085,0x0083,0x91DD,0x91DC,0x0083,
+ 0x91DB,0x91DA,0x0085,0x0083,0x91D9,0xDCB2,0x0083,0x91D8,0x91D7,0x00A1,0x0091,0x0089,0x0085,0x0083,0x91D6,0x91D5,
+ 0x0083,0x91D4,0x91D3,0x0085,0x0083,0x91D2,0x91D1,0x0083,0x91D0,0xE3C2,0x0089,0x0085,0x0083,0x91CF,0x91CE,0x0083,
+ 0x91CD,0x91CC,0x0085,0x0083,0x91CB,0x91CA,0x0083,0x91C9,0x91C8,0x0091,0x0089,0x0085,0x0083,0x91C7,0x91C6,0x0083,
+ 0x91C5,0x91C4,0x0085,0x0083,0x91C3,0x91C2,0x0083,0xC5B3,0x91C1,0x0089,0x0085,0x0083,0x91C0,0x91BF,0x0083,0x91BE,
+ 0x91BD,0x0085,0x0083,0x91BC,0x91BB,0x0083,0x91BA,0x91B9,0x0101,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0x91B8,
+ 0x91B7,0x0083,0x91B6,0x91B5,0x0085,0x0083,0x91B4,0x91B3,0x0083,0x91B2,0x91B1,0x0089,0x0085,0x0083,0xE3C1,0x91B0,
+ 0x0083,0xC0C1,0xEDAF,0x0085,0x0083,0x91AF,0x91AE,0x0083,0x91AD,0x91AC,0x0091,0x0089,0x0085,0x0083,0x91AB,0xEDAE,
+ 0x0083,0xB0C3,0x91AA,0x0085,0x0083,0xD0B8,0x91A9,0x0083,0x91A8,0x91A7,0x0089,0x0085,0x0083,0x91A6,0x91A5,0x0083,
+ 0xB6AE,0x91A4,0x0085,0x0083,0x91A3,0x91A2,0x0083,0xBAB6,0x91A1,0x00A1,0x0091,0x0089,0x0085,0x0083,0x91A0,0x919F,
+ 0x0083,0x919E,0x919D,0x0085,0x0083,0x919C,0xE3C0,0x0083,0x919B,0x919A,0x0089,0x0085,0x0083,0x9199,0x9198,0x0083,
+ 0x9197,0x9196,0x0085,0x0083,0x9195,0x9194,0x0083,0x9193,0x9192,0x0091,0x0089,0x0085,0x0083,0xE3BD,0x9191,0x0083,
+ 0x9190,0xEDAC,0x0085,0x0083,0xBAA9,0xE3BF,0x0083,0x918F,0x918E,0x0089,0x0085,0x0083,0x918D,0x918C,0x0083,0x918B,
+ 0x918A,0x0085,0x0083,0x9189,0x9188,0x0083,0x9187,0xEDAD,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0x9186,0x9185,
+ 0x0083,0x9184,0x9183,0x0085,0x0083,0x9182,0x9181,0x0083,0x9180,0x917E,0x0089,0x0085,0x0083,0xE3BE,0x917D,0x0083,
+ 0x917C,0x917B,0x0085,0x0083,0x917A,0x9179,0x0083,0xD4F7,0x9178,0x0091,0x0089,0x0085,0x0083,0x9177,0xB1EF,0x0083,
+ 0x9176,0x9175,0x0085,0x0083,0x9174,0x9173,0x0083,0x9172,0x9171,0x0089,0x0085,0x0083,0x9170,0x916F,0x0083,0x916E,
+ 0x916D,0x0085,0x0083,0x916C,0x916B,0x0083,0x916A,0x9169,0x00A1,0x0091,0x0089,0x0085,0x0083,0x9168,0x9167,0x0083,
+ 0x9166,0x9165,0x0085,0x0083,0x9164,0xBFB6,0x0083,0x9163,0xE3BC,0x0089,0x0085,0x0083,0x9162,0x9161,0x0083,0x9160,
+ 0x915F,0x0085,0x0083,0xCEBF,0x915E,0x0083,0x915D,0x915C,0x0091,0x0089,0x0085,0x0083,0x915B,0x915A,0x0083,0x9159,
+ 0x9158,0x0085,0x0083,0xBFAE,0xBBDB,0x0083,0x9157,0x9156,0x0089,0x0085,0x0083,0x9155,0x9154,0x0083,0xC2FD,0x9153,
+ 0x0085,0x0083,0x9152,0x9151,0x0083,0x9150,0xEDAB,0x0181,0x0101,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0x914F,
+ 0x914E,0x0083,0x914D,0x914C,0x0085,0x0083,0x914B,0x914A,0x0083,0x9149,0xC4BD,0x0089,0x0085,0x0083,0x9148,0x9147,
+ 0x0083,0x9146,0xC9E5,0x0085,0x0083,0x9145,0x9144,0x0083,0xC9F7,0x9143,0x0091,0x0089,0x0085,0x0083,0xBBC5,0x9142,
+ 0x0083,0xE3BB,0x9141,0x0085,0x0083,0xB4C8,0x9140,0x0083,0x90FE,0x90FD,0x0089,0x0085,0x0083,0x90FC,0x90FB,0x0083,
+ 0x90FA,0x90F9,0x0085,0x0083,0x90F8,0xD4B8,0x0083,0x90F7,0x90F6,0x00A1,0x0091,0x0089,0x0085,0x0083,0x90F5,0x90F4,
+ 0x0083,0x90F3,0x90F2,0x0085,0x0083,0x90F1,0x90F0,0x0083,0x90EF,0x90EE,0x0089,0x0085,0x0083,0x90ED,0x90EC,0x0083,
+ 0x90EB,0x90EA,0x0085,0x0083,0x90E9,0x90E8,0x0083,0x90E7,0x90E6,0x0091,0x0089,0x0085,0x0083,0x90E5,0xE3BA,0x0083,
+ 0x90E4,0x90E3,0x0085,0x0083,0x90E2,0xC0A2,0x0083,0xE3B4,0x90E1,0x0089,0x0085,0x0083,0xB7DF,0xE3B6,0x0083,0x90E0,
+ 0x90DF,0x0085,0x0083,0xE3B3,0xB8D0,0x0083,0x90DE,0x90DD,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0x90DC,0x90DB,
+ 0x0083,0xD3DE,0x90DA,0x0085,0x0083,0x90D9,0x90D8,0x0083,0x90D7,0xE3B5,0x0089,0x0085,0x0083,0x90D6,0x90D5,0x0083,
+ 0x90D4,0x90D3,0x0085,0x0083,0x90D2,0xD2E2,0x0083,0xE3B9,0xEDAA,0x0091,0x0089,0x0085,0x0083,0x90D1,0x90D0,0x0083,
+ 0x90CF,0xD3E4,0x0085,0x0083,0xD3FA,0x90CE,0x0083,0xEDA9,0x90CD,0x0089,0x0085,0x0083,0x90CC,0x90CB,0x0083,0x90CA,
+ 0xB3EE,0x0085,0x0083,0xE3B8,0x90C9,0x0083,0x90C8,0x90C7,0x00A1,0x0091,0x0089,0x0085,0x0083,0x90C6,0x90C5,0x0083,
+ 0xD0CA,0xC8C7,0x0085,0x0083,0x90C4,0x90C3,0x0083,0xBBCC,0x90C2,0x0089,0x0085,0x0083,0xE3B7,0xCFEB,0x0083,0x90C1,
+ 0x90C0,0x0085,0x0083,0xB6E8,0xB9DF,0x0083,0xB5AC,0xB2D1,0x0091,0x0089,0x0085,0x0083,0xE3AB,0xB1B9,0x0083,0x90BF,
+ 0xB3CD,0x0085,0x0083,0xB2D2,0xBEE5,0x0083,0xB5EB,0x90BE,0x0089,0x0085,0x0083,0x90BD,0x90BC,0x0083,0x90BB,0x90BA,
+ 0x0085,0x0083,0xBBDD,0xCEA9,0x0083,0x90B9,0xE3AE,0x0101,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0xCFA7,0x90B8,
+ 0x0083,0xE3B1,0x90B7,0x0085,0x0083,0xE3AF,0x90B6,0x0083,0x90B5,0xCCE8,0x0089,0x0085,0x0083,0x90B4,0x90B3,0x0083,
+ 0x90B2,0xBBF3,0x0085,0x0083,0x90B1,0x90B0,0x0083,0x90AF,0x90AE,0x0091,0x0089,0x0085,0x0083,0x90AD,0xCDEF,0x0083,
+ 0xBEAA,0x90AC,0x0085,0x0083,0x90AB,0x90AA,0x0083,0xE3B0,0xC7E9,0x0089,0x0085,0x0083,0x90A9,0x90A8,0x0083,0x90A7,
+ 0x90A6,0x0085,0x0083,0x90A5,0x90A4,0x0083,0x90A3,0x90A2,0x00A1,0x0091,0x0089,0x0085,0x0083,0xB5BF,0xE3AC,0x0083,
+ 0x90A1,0x90A0,0x0085,0x0083,0xBCC2,0x909F,0x0083,0x909E,0x909D,0x0089,0x0085,0x0083,0xE3B2,0x909C,0x0083,0xB1AF,
+ 0xE3AD,0x0085,0x0083,0x909B,0xC3F5,0x0083,0x909A,0xE3A5,0x0091,0x0089,0x0085,0x0083,0xD0FC,0xEDA8,0x0083,0x9099,
+ 0x9098,0x0085,0x0083,0xC4FA,0x9097,0x0083,0xD4C3,0x9096,0x0089,0x0085,0x0083,0x9095,0xBBBC,0x0083,0x9094,0x9093,
+ 0x0085,0x0083,0xD3C6,0xCEF2,0x0083,0x9092,0xE3A6,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0x9091,0xE3AA,0x0083,
+ 0xE3A4,0x9090,0x0085,0x0083,0x908F,0x908E,0x0083,0xE3A3,0x908D,0x0089,0x0085,0x0083,0xBBDA,0x908C,0x0083,0xE3A8,
+ 0x908B,0x0085,0x0083,0x908A,0x9089,0x0083,0x9088,0xBAB7,0x0091,0x0089,0x0085,0x0083,0xE3A9,0x9087,0x0083,0x9086,
+ 0xCFA4,0x0085,0x0083,0x9085,0x9084,0x0083,0x9083,0x9082,0x0089,0x0085,0x0083,0xC7C4,0xE3A7,0x0083,0x9081,0x9080,
+ 0x0085,0x0083,0x907E,0xD3C1,0x0083,0x907D,0xE3A2,0x00A1,0x0091,0x0089,0x0085,0x0083,0xC4D5,0xE2FC,0x0083,0xE2FD,
+ 0xE2FB,0x0085,0x0083,0xE2FA,0x907C,0x0083,0xB6F1,0x907B,0x0089,0x0085,0x0083,0x907A,0xBFD2,0x0083,0x9079,0x9078,
+ 0x0085,0x0083,0xC7A1,0xCFA2,0x0083,0x9077,0xB9A7,0x0091,0x0089,0x0085,0x0083,0xCCF1,0xB6B2,0x0083,0xE3A1,0xB6F7,
+ 0x0085,0x0083,0xBADE,0xEDA4,0x0083,0x9076,0x9075,0x0089,0x0085,0x0083,0xD0F4,0xEDA7,0x0083,0xBBD6,0x9074,0x0085,
+ 0x0083,0x9073,0x9072,0x0083,0x9071,0xEDA2,0x2069,0x1075,0x087B,0x047D,0x027F,0x017F,0x00FF,0x00BF,0x009F,0x008F,
+ 0x0087,0x0083,0x9070,0x0083,0x906F,0xEDA3,0x0085,0x0083,0xEDA6,0x906E,0x0083,0x906D,0x906C,0x0089,0x0085,0x0083,
+ 0xCBA1,0x906B,0x0083,0x906A,0xBAE3,0x0085,0x0083,0x9069,0xBFD6,0x0083,0x9068,0x9067,0x0091,0x0089,0x0085,0x0083,
+ 0xBBD0,0x9066,0x0083,0xC1B5,0x9065,0x0085,0x0083,0x9064,0x9063,0x0083,0x9062,0x9061,0x0089,0x0085,0x0083,0x9060,
+ 0x905F,0x0083,0xCAD1,0xE2FE,0x0085,0x0083,0xEDA5,0x905E,0x0083,0xE2F8,0x905D,0x00A1,0x0091,0x0089,0x0085,0x0083,
+ 0x905C,0xEDA1,0x0083,0xD7DC,0x905B,0x0085,0x0083,0x905A,0x9059,0x0083,0x9058,0x9057,0x0089,0x0085,0x0083,0xE2F0,
+ 0x9056,0x0083,0x9055,0x9054,0x0085,0x0083,0x9053,0x9052,0x0083,0xC7D3,0x9051,0x0091,0x0089,0x0085,0x0083,0x9050,
+ 0x904F,0x0083,0xE2F6,0xB9D6,0x0085,0x0083,0xE2F5,0xD4B9,0x0083,0xD0D4,0xE2F1,0x0089,0x0085,0x0083,0xBCB1,0x904E,
+ 0x0083,0x904D,0x904C,0x0085,0x0083,0xE2F9,0xB5A1,0x0083,0x904B,0x904A,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,
+ 0xCBBC,0xC1AF,0x0083,0xE2F2,0x9049,0x0085,0x0083,0xE2EF,0x9048,0x0083,0x9047,0xB2C0,0x0089,0x0085,0x0083,0xC5C2,
+ 0xD5FA,0x0083,0x9046,0xC5AD,0x0085,0x0083,0x9045,0x9044,0x0083,0xE2F3,0xD4F5,0x0091,0x0089,0x0085,0x0083,0xE2F4,
+ 0x9043,0x0083,0x9042,0xE2F7,0x0085,0x0083,0x9041,0x9040,0x0083,0x8FFE,0xE2EB,0x0089,0x0085,0x0083,0xE2EA,0xE2E6,
+ 0x0083,0xE2E4,0xCBCB,0x0085,0x0083,0xCCAC,0xBBB3,0x0083,0xB7DE,0xE2E9,0x00A1,0x0091,0x0089,0x0085,0x0083,0xBAF6,
+ 0x8FFD,0x0083,0xD0C3,0x8FFC,0x0085,0x0083,0x8FFB,0xE2EE,0x0083,0x8FFA,0x8FF9,0x0089,0x0085,0x0083,0xC4EE,0x8FF8,
+ 0x0083,0x8FF7,0x8FF6,0x0085,0x0083,0xB3C0,0x8FF5,0x0083,0x8FF4,0xE2E5,0x0091,0x0089,0x0085,0x0083,0xE2ED,0x8FF3,
+ 0x0083,0xBFEC,0xE2EC,0x0085,0x0083,0x8FF2,0x8FF1,0x0083,0xD3C7,0x8FF0,0x0089,0x0085,0x0083,0x8FEF,0xE2E8,0x0083,
+ 0x8FEE,0x8FED,0x0085,0x0083,0xE2E7,0xD6D2,0x0083,0x8FEC,0x8FEB,0x0101,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,
+ 0xE3C3,0x8FEA,0x0083,0x8FE9,0x8FE8,0x0085,0x0083,0xC3A6,0xCDFC,0x0083,0xD6BE,0xE2E2,0x0089,0x0085,0x0083,0x8FE7,
+ 0x8FE6,0x0083,0x8FE5,0xDFAF,0x0085,0x0083,0xECFD,0xECFE,0x0083,0xE2E3,0x8FE4,0x0091,0x0089,0x0085,0x0083,0xC8CC,
+ 0xBCC9,0x0083,0x8FE3,0x8FE2,0x0085,0x0083,0xE2E1,0x8FE1,0x0083,0x8FE0,0xD2E4,0x0089,0x0085,0x0083,0xB1D8,0xE2E0,
+ 0x0083,0xD0C4,0x8FDF,0x0085,0x0083,0x8FDE,0x8FDD,0x0083,0x8FDC,0x8FDB,0x00A1,0x0091,0x0089,0x0085,0x0083,0xBBD5,
+ 0xE1E8,0x0083,0x8FDA,0x8FD9,0x0085,0x0083,0x8FD8,0x8FD7,0x0083,0xB5C2,0x8FD6,0x0089,0x0085,0x0083,0xE1E7,0x8FD5,
+ 0x0083,0x8FD4,0x8FD3,0x0085,0x0083,0x8FD2,0x8FD1,0x0083,0x8FD0,0xCEA2,0x0091,0x0089,0x0085,0x0083,0xE1E6,0x8FCF,
+ 0x0083,0x8FCE,0xD1AD,0x0085,0x0083,0x8FCD,0xE1E5,0x0083,0x8FCC,0x8FCB,0x0089,0x0085,0x0083,0x8FCA,0x8FC9,0x0083,
+ 0x8FC8,0x8FC7,0x0085,0x0083,0xD3F9,0x8FC6,0x0083,0x8FC5,0x8FC4,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0x8FC3,
+ 0xE1E4,0x0083,0x8FC2,0x8FC1,0x0085,0x0083,0xE1E3,0xC5C7,0x0083,0xB5C3,0x8FC0,0x0089,0x0085,0x0083,0xE1E2,0x8FBF,
+ 0x0083,0x8FBE,0xCDBD,0x0085,0x0083,0x8FBD,0xD0EC,0x0083,0x8FBC,0x8FBB,0x0091,0x0089,0x0085,0x0083,0x8FBA,0xE1E1,
+ 0x0083,0xC2C9,0xBBB2,0x0085,0x0083,0xE1E0,0xBADC,0x0083,0xE1DF,0x8FB9,0x0089,0x0085,0x0083,0xB4FD,0xBEB6,0x0083,
+ 0x8FB8,0xE1DE,0x0085,0x0083,0xD5F7,0xCDF9,0x0083,0x8FB7,0x8FB6,0x00A1,0x0091,0x0089,0x0085,0x0083,0x8FB5,0xB1CB,
+ 0x0083,0xB3B9,0x8FB4,0x0085,0x0083,0xD2DB,0x8FB3,0x0083,0xE1DD,0x8FB2,0x0089,0x0085,0x0083,0x8FB1,0x8FB0,0x0083,
+ 0xE1DC,0x8FAF,0x0085,0x0083,0xD3B0,0xD5C3,0x0083,0x8FAE,0x8FAD,0x0091,0x0089,0x0085,0x0083,0xC5ED,0xB1F2,0x0083,
+ 0x8FAC,0xB1EB,0x0085,0x0083,0xB2CA,0x8FAB,0x0083,0x8FAA,0xD1E5,0x0089,0x0085,0x0083,0x8FA9,0xCDAE,0x0083,0x8FA8,
+ 0xD0CE,0x0085,0x0083,0xE1EA,0x8FA7,0x0083,0x8FA6,0x8FA5,0x017F,0x00FF,0x00BF,0x009F,0x008F,0x0087,0x0083,0xD2CD,
+ 0x0083,0x8FA4,0x8FA3,0x0085,0x0083,0x8FA2,0x8FA1,0x0083,0xE5E9,0xE5E7,0x0089,0x0085,0x0083,0xE5E8,0xC2BC,0x0083,
+ 0x8FA0,0xB5B1,0x0085,0x0083,0xB9E9,0x8F9F,0x0083,0xE5E6,0x8F9E,0x0091,0x0089,0x0085,0x0083,0x8F9D,0x8F9C,0x0083,
+ 0x8F9B,0x8F9A,0x0085,0x0083,0x8F99,0x8F98,0x0083,0x8F97,0x8F96,0x0089,0x0085,0x0083,0x8F95,0x8F94,0x0083,0x8F93,
+ 0x8F92,0x0085,0x0083,0x8F91,0x8F90,0x0083,0xECB0,0x8F8F,0x00A1,0x0091,0x0089,0x0085,0x0083,0x8F8E,0x8F8D,0x0083,
+ 0xE5F6,0x8F8C,0x0085,0x0083,0xC7BF,0xB5AF,0x0083,0x8F8B,0x8F8A,0x0089,0x0085,0x0083,0x8F89,0x8F88,0x0083,0x8F87,
+ 0x8F86,0x0085,0x0083,0x8F85,0xC8F5,0x0083,0x8F84,0xCDE4,0x0091,0x0089,0x0085,0x0083,0x8F83,0xE5F4,0x0083,0x8F82,
+ 0x8F81,0x0085,0x0083,0xE5F2,0xE5F3,0x0083,0x8F80,0xBBA1,0x0089,0x0085,0x0083,0xCFD2,0xC3D6,0x0083,0x8F7E,0x8F7D,
+ 0x0085,0x0083,0x8F7C,0x8F7B,0x0083,0xD5C5,0xB5DC,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0x8F7A,0x8F79,0x0083,
+ 0x8F78,0xB3DA,0x0085,0x0083,0x8F77,0x8F76,0x0083,0xBAEB,0xB8A5,0x0089,0x0085,0x0083,0x8F75,0xD2FD,0x0083,0x8F74,
+ 0xB9AD,0x0085,0x0083,0x8F73,0xDFB1,0x0083,0x8F72,0xCABD,0x0091,0x0089,0x0085,0x0083,0x8F71,0x8F70,0x0083,0x8F6F,
+ 0xDFAE,0x0085,0x0083,0xB1D7,0x8F6E,0x0083,0xDEC4,0x8F6D,0x0089,0x0085,0x0083,0x8F6C,0x8F6B,0x0083,0xC5AA,0xC6FA,
+ 0x0085,0x0083,0xD2EC,0xDBCD,0x0083,0xBFAA,0xD8A5,0x00A1,0x0091,0x0089,0x0085,0x0083,0xDEC3,0x8F6A,0x0083,0x8F69,
+ 0x8F68,0x0085,0x0083,0xBDA8,0x8F67,0x0083,0x8F66,0xCDA2,0x0089,0x0085,0x0083,0xD1D3,0x8F65,0x0083,0xDBC8,0x8F64,
+ 0x0085,0x0083,0x8F63,0x8F62,0x0083,0x8F61,0x8F60,0x0091,0x0089,0x0085,0x0083,0x8F5F,0x8F5E,0x0083,0x8F5D,0x8F5C,
+ 0x0085,0x0083,0xE2DE,0x8F5B,0x0083,0xE2DD,0x8F5A,0x0089,0x0085,0x0083,0x8F59,0x8F58,0x0083,0x8F57,0x8F56,0x0085,
+ 0x0083,0x8F55,0x8F54,0x0083,0x8F53,0x8F52,0x0101,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0x8F51,0x8F50,0x0083,
+ 0x8F4F,0xE2DC,0x0085,0x0083,0x8F4E,0x8F4D,0x0083,0x8F4C,0x8F4B,0x0089,0x0085,0x0083,0xC1CE,0x8F4A,0x0083,0x8F49,
+ 0xC0AA,0x0085,0x0083,0xE2DA,0xE2DB,0x0083,0x8F48,0x8F47,0x0091,0x0089,0x0085,0x0083,0x8F46,0x8F45,0x0083,0x8F44,
+ 0x8F43,0x0085,0x0083,0xC0C8,0xC1AE,0x0083,0x8F42,0x8F41,0x0089,0x0085,0x0083,0x8F40,0x8EFE,0x0083,0x8EFD,0x8EFC,
+ 0x0085,0x0083,0x8EFB,0x8EFA,0x0083,0x8EF9,0x8EF8,0x00A1,0x0091,0x0089,0x0085,0x0083,0xE2D7,0x8EF7,0x0083,0x8EF6,
+ 0x8EF5,0x0085,0x0083,0x8EF4,0xE2D5,0x0083,0xD3B9,0xBFB5,0x0089,0x0085,0x0083,0xCAFC,0xE2D6,0x0083,0x8EF3,0xE2D8,
+ 0x0085,0x0083,0x8EF2,0x8EF1,0x0083,0x8EF0,0x8EEF,0x0091,0x0089,0x0085,0x0083,0x8EEE,0xCDA5,0x0083,0x8EED,0x8EEC,
+ 0x0085,0x0083,0x8EEB,0x8EEA,0x0083,0x8EE9,0xD7F9,0x0089,0x0085,0x0083,0xB6C8,0xE2D3,0x0083,0x8EE8,0x8EE7,0x0085,
+ 0x0083,0x8EE6,0x8EE5,0x0083,0xE2D4,0xB7CF,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0xC5D3,0x8EE4,0x0083,0xB8AE,
+ 0x8EE3,0x0085,0x0083,0xB8FD,0xC3ED,0x0083,0x8EE2,0xB5EA,0x0089,0x0085,0x0083,0xE2D2,0xB5D7,0x0083,0xD3A6,0xBFE2,
+ 0x0085,0x0083,0x8EE1,0xE2D0,0x0083,0xC2AE,0xD0F2,0x0091,0x0089,0x0085,0x0083,0x8EE0,0x8EDF,0x0083,0x8EDE,0xE2D1,
+ 0x0085,0x0083,0xB4B2,0x8EDD,0x0083,0x8EDC,0xB1D3,0x0089,0x0085,0x0083,0xC7EC,0x8EDB,0x0083,0xD7AF,0x8EDA,0x0085,
+ 0x0083,0x8ED9,0x8ED8,0x0083,0xE2CF,0xB9E3,0x00A1,0x0091,0x0089,0x0085,0x0083,0x8ED7,0xD3C4,0x0083,0xD3D7,0xBBC3,
+ 0x0085,0x0083,0xE7DB,0x8ED6,0x0083,0xD0D2,0x8ED5,0x0089,0x0085,0x0083,0xB2A2,0x8ED4,0x0083,0xC4EA,0xC6BD,0x0085,
+ 0x0083,0xB8C9,0x8ED3,0x0083,0x8ED2,0x8ED1,0x0091,0x0089,0x0085,0x0083,0x8ED0,0x8ECF,0x0083,0x8ECE,0x8ECD,0x0085,
+ 0x0083,0x8ECC,0x8ECB,0x0083,0x8ECA,0x8EC9,0x0089,0x0085,0x0083,0x8EC8,0x8EC7,0x0083,0x8EC6,0x8EC5,0x0085,0x0083,
+ 0xB4B1,0xE1A6,0x0083,0x8EC4,0x8EC3,0x027F,0x017F,0x00FF,0x00BF,0x009F,0x008F,0x0087,0x0083,0xE1A5,0x0083,0x8EC2,
+ 0x8EC1,0x0085,0x0083,0xE1A4,0x8EC0,0x0083,0x8EBF,0x8EBE,0x0089,0x0085,0x0083,0x8EBD,0x8EBC,0x0083,0xC4BB,0xE1A3,
+ 0x0085,0x0083,0x8EBB,0x8EBA,0x0083,0x8EB9,0x8EB8,0x0091,0x0089,0x0085,0x0083,0x8EB7,0x8EB6,0x0083,0x8EB5,0xBBCF,
+ 0x0085,0x0083,0x8EB4,0x8EB3,0x0083,0x8EB2,0x8EB1,0x0089,0x0085,0x0083,0x8EB0,0x8EAF,0x0083,0xB7F9,0xE1A2,0x0085,
+ 0x0083,0x8EAE,0xC3DD,0x0083,0x8EAD,0x8EAC,0x00A1,0x0091,0x0089,0x0085,0x0083,0x8EAB,0x8EAA,0x0083,0xC3B1,0xE0FE,
+ 0x0085,0x0083,0xE0FD,0x8EA9,0x0083,0x8EA8,0xB3A3,0x0089,0x0085,0x0083,0xE1A1,0x8EA7,0x0083,0x8EA6,0x8EA5,0x0085,
+ 0x0083,0x8EA4,0x8EA3,0x0083,0xE0FC,0x8EA2,0x0091,0x0089,0x0085,0x0083,0x8EA1,0xB0EF,0x0083,0xCFAF,0x8EA0,0x0085,
+ 0x0083,0x8E9F,0x8E9E,0x0083,0x8E9D,0x8E9C,0x0089,0x0085,0x0083,0xD6A1,0xB4F8,0x0083,0x8E9B,0x8E9A,0x0085,0x0083,
+ 0x8E99,0x8E98,0x0083,0x8E97,0x8E96,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0x8E95,0x8E94,0x0083,0xB5DB,0xD6C4,
+ 0x0085,0x0083,0xB2AF,0xD6E3,0x0083,0xE0F9,0xC1B1,0x0089,0x0085,0x0083,0x8E93,0xCCFB,0x0083,0xC5C1,0xE0FA,0x0085,
+ 0x0083,0x8E92,0x8E91,0x0083,0xE0FB,0xD5CA,0x0091,0x0089,0x0085,0x0083,0xE0F8,0x8E90,0x0083,0x8E8F,0xCFA3,0x0085,
+ 0x0083,0x8E8E,0x8E8D,0x0083,0x8E8C,0xCAA6,0x0089,0x0085,0x0083,0x8E8B,0xB7AB,0x0083,0xCBA7,0x8E8A,0x0085,0x0083,
+ 0xB2BC,0xCAD0,0x0083,0xB1D2,0x8E89,0x00A1,0x0091,0x0089,0x0085,0x0083,0x8E88,0xBDED,0x0083,0xD9E3,0x8E87,0x0085,
+ 0x0083,0x8E86,0x8E85,0x0083,0x8E84,0x8E83,0x0089,0x0085,0x0083,0xCFEF,0x8E82,0x0083,0x8E81,0xB0CD,0x0085,0x0083,
+ 0xCBC8,0xD2D1,0x0083,0xBCBA,0x8E80,0x0091,0x0089,0x0085,0x0083,0xDBCF,0xB2EE,0x0083,0x8E7E,0x8E7D,0x0085,0x0083,
+ 0xCED7,0x8E7C,0x0083,0xB9AE,0xBEDE,0x0089,0x0085,0x0083,0xC7C9,0xD7F3,0x0083,0xB9A4,0x8E7B,0x0085,0x0083,0x8E7A,
+ 0xB3B2,0x0083,0xD1B2,0x8E79,0x0101,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0x8E78,0xD6DD,0x0083,0xB4A8,0x8E77,
+ 0x0085,0x0083,0xE7DD,0x8E76,0x0083,0x8E75,0x8E74,0x0089,0x0085,0x0083,0x8E73,0x8E72,0x0083,0x8E71,0x8E70,0x0085,
+ 0x0083,0x8E6F,0x8E6E,0x0083,0x8E6D,0x8E6C,0x0091,0x0089,0x0085,0x0083,0x8E6B,0x8E6A,0x0083,0xCEA1,0x8E69,0x0085,
+ 0x0083,0x8E68,0x8E67,0x0083,0x8E66,0x8E65,0x0089,0x0085,0x0083,0x8E64,0x8E63,0x0083,0xE1DB,0x8E62,0x0085,0x0083,
+ 0x8E61,0x8E60,0x0083,0x8E5F,0x8E5E,0x00A1,0x0091,0x0089,0x0085,0x0083,0x8E5D,0x8E5C,0x0083,0x8E5B,0x8E5A,0x0085,
+ 0x0083,0x8E59,0x8E58,0x0083,0x8E57,0x8E56,0x0089,0x0085,0x0083,0xE1DA,0x8E55,0x0083,0x8E54,0x8E53,0x0085,0x0083,
+ 0x8E52,0x8E51,0x0083,0x8E50,0x8E4F,0x0091,0x0089,0x0085,0x0083,0x8E4E,0x8E4D,0x0083,0x8E4C,0x8E4B,0x0085,0x0083,
+ 0x8E4A,0x8E49,0x0083,0x8E48,0x8E47,0x0089,0x0085,0x0083,0x8E46,0x8E45,0x0083,0x8E44,0x8E43,0x0085,0x0083,0x8E42,
+ 0x8E41,0x0083,0x8E40,0x8DFE,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0x8DFD,0x8DFC,0x0083,0xE1D8,0x8DFB,0x0085,
+ 0x0083,0x8DFA,0x8DF9,0x0083,0xE1D7,0x8DF8,0x0089,0x0085,0x0083,0x8DF7,0x8DF6,0x0083,0x8DF5,0x8DF4,0x0085,0x0083,
+ 0x8DF3,0x8DF2,0x0083,0x8DF1,0x8DF0,0x0091,0x0089,0x0085,0x0083,0x8DEF,0x8DEE,0x0083,0x8DED,0x8DEC,0x0085,0x0083,
+ 0x8DEB,0x8DEA,0x0083,0x8DE9,0x8DE8,0x0089,0x0085,0x0083,0x8DE7,0x8DE6,0x0083,0x8DE5,0x8DE4,0x0085,0x0083,0x8DE3,
+ 0xE1D6,0x0083,0x8DE2,0x8DE1,0x00A1,0x0091,0x0089,0x0085,0x0083,0x8DE0,0x8DDF,0x0083,0x8DDE,0x8DDD,0x0085,0x0083,
+ 0x8DDC,0x8DDB,0x0083,0x8DDA,0x8DD9,0x0089,0x0085,0x0083,0x8DD8,0x8DD7,0x0083,0x8DD6,0xE1D5,0x0085,0x0083,0x8DD5,
+ 0x8DD4,0x0083,0x8DD3,0x8DD2,0x0091,0x0089,0x0085,0x0083,0xE1CF,0x8DD1,0x0083,0x8DD0,0xE1CD,0x0085,0x0083,0xE1D1,
+ 0x8DCF,0x0083,0xE1D4,0x8DCE,0x0089,0x0085,0x0083,0x8DCD,0x8DCC,0x0083,0x8DCB,0x8DCA,0x0085,0x0083,0x8DC9,0x8DC8,
+ 0x0083,0x8DC7,0x8DC6,0x0181,0x0101,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0x8DC5,0x8DC4,0x0083,0xE1D0,0x8DC3,
+ 0x0085,0x0083,0xE1CE,0x8DC2,0x0083,0x8DC1,0xE1C9,0x0089,0x0085,0x0083,0x8DC0,0x8DBF,0x0083,0x8DBE,0x8DBD,0x0085,
+ 0x0083,0x8DBC,0x8DBB,0x0083,0x8DBA,0x8DB9,0x0091,0x0089,0x0085,0x0083,0x8DB8,0x8DB7,0x0083,0x8DB6,0xC7B6,0x0085,
+ 0x0083,0xE1D2,0xE1D3,0x0083,0x8DB5,0x8DB4,0x0089,0x0085,0x0083,0xEFFA,0x8DB3,0x0083,0x8DB2,0x8DB1,0x0085,0x0083,
+ 0x8DB0,0x8DAF,0x0083,0x8DAE,0x8DAD,0x00A1,0x0091,0x0089,0x0085,0x0083,0x8DAC,0xE1CA,0x0083,0xE1CC,0x8DAB,0x0085,
+ 0x0083,0x8DAA,0x8DA9,0x0083,0x8DA8,0x8DA7,0x0089,0x0085,0x0083,0x8DA6,0x8DA5,0x0083,0x8DA4,0xE1CB,0x0085,0x0083,
+ 0x8DA3,0x8DA2,0x0083,0x8DA1,0x8DA0,0x0091,0x0089,0x0085,0x0083,0x8D9F,0xE1C4,0x0083,0xD5B8,0x8D9E,0x0085,0x0083,
+ 0x8D9D,0x8D9C,0x0083,0xB1C0,0x8D9B,0x0089,0x0085,0x0083,0xE1C2,0xE1C3,0x0083,0x8D9A,0xE1C5,0x0085,0x0083,0x8D99,
+ 0x8D98,0x0083,0x8D97,0x8D96,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0x8D95,0xE1C6,0x0083,0x8D94,0x8D93,0x0085,
+ 0x0083,0xE1C8,0x8D92,0x0083,0x8D91,0x8D90,0x0089,0x0085,0x0083,0x8D8F,0xD1C2,0x0083,0x8D8E,0xB4DE,0x0085,0x0083,
+ 0x8D8D,0x8D8C,0x0083,0x8D8B,0x8D8A,0x0091,0x0089,0x0085,0x0083,0x8D89,0xC6E9,0x0083,0x8D88,0x8D87,0x0085,0x0083,
+ 0x8D86,0x8D85,0x0083,0x8D84,0x8D83,0x0089,0x0085,0x0083,0xB3E7,0xE1C7,0x0083,0x8D82,0x8D81,0x0085,0x0083,0xE1C1,
+ 0xE1C0,0x0083,0x8D80,0x8D7E,0x00A1,0x0091,0x0089,0x0085,0x0083,0x8D7D,0x8D7C,0x0083,0x8D7B,0x8D7A,0x0085,0x0083,
+ 0xBEFE,0x8D79,0x0083,0x8D78,0x8D77,0x0089,0x0085,0x0083,0x8D76,0x8D75,0x0083,0x8D74,0x8D73,0x0085,0x0083,0x8D72,
+ 0x8D71,0x0083,0x8D70,0xB7E5,0x0091,0x0089,0x0085,0x0083,0x8D6F,0x8D6E,0x0083,0xC7CD,0x8D6D,0x0085,0x0083,0x8D6C,
+ 0xD3F8,0x0083,0x8D6B,0xB6EB,0x0089,0x0085,0x0083,0x8D6A,0xC2CD,0x0083,0xE1BF,0xE1BD,0x0085,0x0083,0x8D69,0x8D68,
+ 0x0083,0xCFBF,0x8D67,0x0101,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0x8D66,0x8D65,0x0083,0x8D64,0x8D63,0x0085,
+ 0x0083,0x8D62,0x8D61,0x0083,0xD6C5,0x8D60,0x0089,0x0085,0x0083,0x8D5F,0x8D5E,0x0083,0x8D5D,0x8D5C,0x0085,0x0083,
+ 0x8D5B,0xE1BC,0x0083,0x8D5A,0x8D59,0x0091,0x0089,0x0085,0x0083,0x8D58,0x8D57,0x0083,0x8D56,0x8D55,0x0085,0x0083,
+ 0xE1BE,0x8D54,0x0083,0x8D53,0x8D52,0x0089,0x0085,0x0083,0x8D51,0x8D50,0x0083,0x8D4F,0xE1BB,0x0085,0x0083,0x8D4E,
+ 0x8D4D,0x0083,0xE1B9,0x8D4C,0x00A1,0x0091,0x0089,0x0085,0x0083,0xBFF9,0x8D4B,0x0083,0xE1B4,0x8D4A,0x0085,0x0083,
+ 0x8D49,0x8D48,0x0083,0x8D47,0xB0B6,0x0089,0x0085,0x0083,0xE1BA,0x8D46,0x0083,0xE1B2,0x8D45,0x0085,0x0083,0xD4C0,
+ 0x8D44,0x0083,0xE1B7,0x8D43,0x0091,0x0089,0x0085,0x0083,0x8D42,0x8D41,0x0083,0xC1EB,0xE1B5,0x0085,0x0083,0xE1B6,
+ 0x8D40,0x0083,0xD1D2,0x8CFE,0x0089,0x0085,0x0083,0x8CFD,0x8CFC,0x0083,0x8CFB,0x8CFA,0x0085,0x0083,0xE1B8,0xE1B3,
+ 0x0083,0x8CF9,0x8CF8,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0x8CF7,0x8CF6,0x0083,0x8CF5,0xE1B1,0x0085,0x0083,
+ 0xB5BA,0xE1B0,0x0083,0xE1AE,0xE1AD,0x0089,0x0085,0x0083,0xB8DA,0xE1AB,0x0083,0x8CF4,0xB2ED,0x0085,0x0083,0x8CF3,
+ 0x8CF2,0x0083,0xE1AF,0xE1AA,0x0091,0x0089,0x0085,0x0083,0x8CF1,0x8CF0,0x0083,0xE1A9,0xE1A7,0x0085,0x0083,0x8CEF,
+ 0x8CEE,0x0083,0x8CED,0xE1AC,0x0089,0x0085,0x0083,0x8CEC,0x8CEB,0x0083,0x8CEA,0x8CE9,0x0085,0x0083,0x8CE8,0xC6F1,
+ 0x0083,0xCBEA,0x8CE7,0x00A1,0x0091,0x0089,0x0085,0x0083,0xD3EC,0x8CE6,0x0083,0x8CE5,0x8CE4,0x0085,0x0083,0x8CE3,
+ 0xE1A8,0x0083,0xD2D9,0x8CE2,0x0089,0x0085,0x0083,0x8CE1,0x8CE0,0x0083,0x8CDF,0x8CDE,0x0085,0x0083,0x8CDD,0x8CDC,
+ 0x0083,0xC9BD,0x8CDB,0x0091,0x0089,0x0085,0x0083,0xCDCD,0xE5F8,0x0083,0x8CDA,0x8CD9,0x0085,0x0083,0x8CD8,0x8CD7,
+ 0x0083,0x8CD6,0x8CD5,0x0089,0x0085,0x0083,0x8CD4,0xE5F0,0x0083,0xC2C4,0x8CD3,0x0085,0x0083,0xE5EF,0x8CD2,0x0083,
+ 0xC2C5,0xCDC0,0x047D,0x027F,0x017F,0x00FF,0x00BF,0x009F,0x008F,0x0087,0x0083,0x8CD1,0x0083,0xCAF4,0x8CD0,0x0085,
+ 0x0083,0x8CCF,0x8CCE,0x0083,0x8CCD,0xE5ED,0x0089,0x0085,0x0083,0x8CCC,0x8CCB,0x0083,0x8CCA,0xD5B9,0x0085,0x0083,
+ 0x8CC9,0x8CC8,0x0083,0x8CC7,0xD0BC,0x0091,0x0089,0x0085,0x0083,0xE5EC,0xC6C1,0x0083,0xCABA,0x8CC6,0x0085,0x0083,
+ 0x8CC5,0xCEDD,0x0083,0xBDEC,0xCCEB,0x0089,0x0085,0x0083,0xC7FC,0x8CC4,0x0083,0x8CC3,0xBED3,0x0085,0x0083,0x8CC2,
+ 0x8CC1,0x0083,0xB2E3,0xC6A8,0x00A1,0x0091,0x0089,0x0085,0x0083,0xBED6,0xC4F2,0x0083,0xCEB2,0xBEA1,0x0085,0x0083,
+ 0xC4E1,0xE5EA,0x0083,0xB3DF,0xD2FC,0x0089,0x0085,0x0083,0xCAAC,0x8CC0,0x0083,0x8CBF,0x8CBE,0x0085,0x0083,0xDECF,
+ 0x8CBD,0x0083,0x8CBC,0xBECD,0x0091,0x0089,0x0085,0x0083,0x8CBB,0x8CBA,0x0083,0x8CB9,0x8CB8,0x0085,0x0083,0xDECE,
+ 0x8CB7,0x0083,0x8CB6,0x8CB5,0x0089,0x0085,0x0083,0x8CB4,0xD2A2,0x0083,0x8CB3,0xDECD,0x0085,0x0083,0xD3C8,0x8CB2,
+ 0x0083,0xDECC,0x8CB1,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0x8CB0,0x8CAF,0x0083,0x8CAE,0xB3A2,0x0085,0x0083,
+ 0xE6D9,0x8CAD,0x0083,0xC9D0,0x8CAC,0x0089,0x0085,0x0083,0xB3BE,0x8CAB,0x0083,0xBCE2,0xE6D8,0x0085,0x0083,0xB6FB,
+ 0x8CAA,0x0083,0x8CA9,0xC9D9,0x0091,0x0089,0x0085,0x0083,0x8CA8,0xD0A1,0x0083,0x8CA7,0x8CA6,0x0085,0x0083,0x8CA5,
+ 0x8CA4,0x0083,0xD7F0,0xCEBE,0x0089,0x0085,0x0083,0x8CA3,0x8CA2,0x0083,0xBDAB,0x8CA1,0x0085,0x0083,0xC9E4,0x8CA0,
+ 0x0083,0x8C9F,0xB7E2,0x00A1,0x0091,0x0089,0x0085,0x0083,0x8C9E,0xCAD9,0x0083,0x8C9D,0x8C9C,0x0085,0x0083,0xB5BC,
+ 0xD1B0,0x0083,0xCBC2,0xB6D4,0x0089,0x0085,0x0083,0xB4E7,0x8C9B,0x0083,0x8C9A,0x8C99,0x0085,0x0083,0x8C98,0x8C97,
+ 0x0083,0x8C96,0x8C95,0x0091,0x0089,0x0085,0x0083,0xE5BE,0x8C94,0x0083,0xE5BC,0x8C93,0x0085,0x0083,0x8C92,0x8C91,
+ 0x0083,0x8C90,0x8C8F,0x0089,0x0085,0x0083,0xD5AF,0x8C8E,0x0083,0x8C8D,0xC1C8,0x0085,0x0083,0xE5BB,0x8C8C,0x0083,
+ 0x8C8B,0xB9D1,0x0101,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0x8C8A,0xB2EC,0x0083,0xC4AF,0xC7DE,0x0085,0x0083,
+ 0x8C89,0x8C88,0x0083,0x8C87,0x8C86,0x0089,0x0085,0x0083,0x8C85,0x8C84,0x0083,0x8C83,0x8C82,0x0085,0x0083,0x8C81,
+ 0xD4A2,0x0083,0xBAAE,0x8C80,0x0091,0x0089,0x0085,0x0083,0xC3C2,0x8C7E,0x0083,0x8C7D,0x8C7C,0x0085,0x0083,0xB8BB,
+ 0x8C7B,0x0083,0x8C7A,0x8C79,0x0089,0x0085,0x0083,0x8C78,0xBFDC,0x0083,0xC3DC,0xD2FA,0x0085,0x0083,0xBCC4,0x8C77,
+ 0x0083,0xBCC5,0x8C76,0x00A1,0x0091,0x0089,0x0085,0x0083,0x8C75,0xCBDE,0x0083,0xB1F6,0xBFED,0x0085,0x0083,0x8C74,
+ 0x8C73,0x0083,0x8C72,0xC8DD,0x0089,0x0085,0x0083,0xE5B7,0x8C71,0x0083,0xBCD2,0xCFFC,0x0085,0x0083,0xD1E7,0xBAA6,
+ 0x0083,0x8C70,0x8C6F,0x0091,0x0089,0x0085,0x0083,0xD4D7,0x8C6E,0x0083,0x8C6D,0x8C6C,0x0085,0x0083,0x8C6B,0xB9AC,
+ 0x0083,0xCFDC,0x8C6A,0x0089,0x0085,0x0083,0x8C69,0x8C68,0x0083,0xBBC2,0xE5B6,0x0085,0x0083,0xCAD2,0xD0FB,0x0083,
+ 0xBFCD,0xC9F3,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0xB3E8,0x8C67,0x0083,0xCAB5,0xB1A6,0x0085,0x0083,0xD2CB,
+ 0xCDF0,0x0083,0xB6A8,0xD6E6,0x0089,0x0085,0x0083,0xB9D9,0xD7DA,0x0083,0x8C66,0xE5B4,0x0085,0x0083,0x8C65,0xE5B5,
+ 0x0083,0x8C64,0x8C63,0x0091,0x0089,0x0085,0x0083,0x8C62,0xBAEA,0x0083,0x8C61,0x8C60,0x0085,0x0083,0xCDEA,0xCBCE,
+ 0x0083,0x8C5F,0xB0B2,0x0089,0x0085,0x0083,0xCAD8,0xD3EE,0x0083,0x8C5E,0xD5AC,0x0085,0x0083,0xE5B3,0xCBFC,0x0083,
+ 0x8C5D,0xC4FE,0x00A1,0x0091,0x0089,0x0085,0x0083,0xE5B2,0x8C5C,0x0083,0x8C5B,0xC4F5,0x0085,0x0083,0x8C5A,0x8C59,
+ 0x0083,0xC8E6,0x8C58,0x0089,0x0085,0x0083,0x8C57,0x8C56,0x0083,0x8C55,0xB7F5,0x0085,0x0083,0x8C54,0xE6DC,0x0083,
+ 0x8C53,0xE5EE,0x0091,0x0089,0x0085,0x0083,0xCAEB,0x8C52,0x0083,0x8C51,0x8C50,0x0085,0x0083,0xD8AB,0x8C4F,0x0083,
+ 0xC2CF,0xBAA2,0x0089,0x0085,0x0083,0x8C4E,0x8C4D,0x0083,0xD1A7,0xE6DB,0x0085,0x0083,0xB9C2,0xBCBE,0x0083,0xE6DF,
+ 0x8C4C,0x017F,0x00FF,0x00BF,0x009F,0x008F,0x0087,0x0083,0x8C4B,0x0083,0xC3CF,0x8C4A,0x0085,0x0083,0xD0A2,0xD7CE,
+ 0x0083,0xD8C3,0xE6DA,0x0089,0x0085,0x0083,0xCBEF,0xB4E6,0x0083,0xD7D6,0x8C49,0x0085,0x0083,0xD4D0,0xBFD7,0x0083,
+ 0xE6DE,0x8C48,0x0091,0x0089,0x0085,0x0083,0xE6DD,0xD7D3,0x0083,0x8C47,0x8C46,0x0085,0x0083,0x8C45,0x8C44,0x0083,
+ 0x8C43,0x8C42,0x0089,0x0085,0x0083,0x8C41,0x8C40,0x0083,0x8BFE,0x8BFD,0x0085,0x0083,0x8BFC,0x8BFB,0x0083,0x8BFA,
+ 0x8BF9,0x00A1,0x0091,0x0089,0x0085,0x0083,0x8BF8,0xE6D7,0x0083,0x8BF7,0x8BF6,0x0085,0x0083,0x8BF5,0x8BF4,0x0083,
+ 0x8BF3,0x8BF2,0x0089,0x0085,0x0083,0x8BF1,0x8BF0,0x0083,0xE6D6,0x8BEF,0x0085,0x0083,0x8BEE,0xD9F8,0x0083,0x8BED,
+ 0xE6D5,0x0091,0x0089,0x0085,0x0083,0x8BEC,0x8BEB,0x0083,0x8BEA,0x8BE9,0x0085,0x0083,0x8BE8,0x8BE7,0x0083,0x8BE6,
+ 0x8BE5,0x0089,0x0085,0x0083,0x8BE4,0x8BE3,0x0083,0x8BE2,0x8BE1,0x0085,0x0083,0x8BE0,0x8BDF,0x0083,0x8BDE,0x8BDD,
+ 0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0x8BDC,0x8BDB,0x0083,0x8BDA,0x8BD9,0x0085,0x0083,0x8BD8,0x8BD7,0x0083,
+ 0x8BD6,0x8BD5,0x0089,0x0085,0x0083,0x8BD4,0x8BD3,0x0083,0xE6D3,0xE6D4,0x0085,0x0083,0x8BD2,0x8BD1,0x0083,0x8BD0,
+ 0x8BCF,0x0091,0x0089,0x0085,0x0083,0x8BCE,0x8BCD,0x0083,0x8BCC,0x8BCB,0x0085,0x0083,0x8BCA,0x8BC9,0x0083,0x8BC8,
+ 0x8BC7,0x0089,0x0085,0x0083,0xE6D2,0x8BC6,0x0083,0x8BC5,0x8BC4,0x0085,0x0083,0x8BC3,0x8BC2,0x0083,0x8BC1,0x8BC0,
+ 0x00A1,0x0091,0x0089,0x0085,0x0083,0x8BBF,0x8BBE,0x0083,0x8BBD,0x8BBC,0x0085,0x0083,0x8BBB,0x8BBA,0x0083,0x8BB9,
+ 0x8BB8,0x0089,0x0085,0x0083,0x8BB7,0x8BB6,0x0083,0x8BB5,0x8BB4,0x0085,0x0083,0x8BB3,0x8BB2,0x0083,0x8BB1,0x8BB0,
+ 0x0091,0x0089,0x0085,0x0083,0xE6CD,0x8BAF,0x0083,0x8BAE,0x8BAD,0x0085,0x0083,0x8BAC,0x8BAB,0x0083,0xE6C6,0x8BAA,
+ 0x0089,0x0085,0x0083,0xC4DB,0x8BA9,0x0083,0x8BA8,0xE6CF,0x0085,0x0083,0x8BA7,0x8BA6,0x0083,0xE6CC,0x8BA5,0x0101,
+ 0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0xB5D5,0xE6CB,0x0083,0x8BA4,0x8BA3,0x0085,0x0083,0x8BA2,0xE6D1,0x0083,
+ 0x8BA1,0x8BA0,0x0089,0x0085,0x0083,0x8B9F,0xE6D0,0x0083,0x8B9E,0xE6CE,0x0085,0x0083,0x8B9D,0xE6C9,0x0083,0x8B9C,
+ 0xE6C8,0x0091,0x0089,0x0085,0x0083,0x8B9B,0x8B9A,0x0083,0x8B99,0x8B98,0x0085,0x0083,0x8B97,0xCFD3,0x0083,0x8B96,
+ 0x8B95,0x0089,0x0085,0x0083,0xBCB5,0x8B94,0x0083,0x8B93,0x8B92,0x0085,0x0083,0x8B91,0x8B90,0x0083,0x8B8F,0xC9A9,
+ 0x00A1,0x0091,0x0089,0x0085,0x0083,0xBCDE,0x8B8E,0x0083,0x8B8D,0xE6C5,0x0085,0x0083,0x8B8C,0x8B8B,0x0083,0x8B8A,
+ 0x8B89,0x0089,0x0085,0x0083,0x8B88,0xE6CA,0x0083,0x8B87,0x8B86,0x0085,0x0083,0xEBF4,0x8B85,0x0083,0xCFB1,0xE6C7,
+ 0x0091,0x0089,0x0085,0x0083,0x8B84,0x8B83,0x0083,0x8B82,0x8B81,0x0085,0x0083,0x8B80,0x8B7E,0x0083,0x8B7D,0xE6C1,
+ 0x0089,0x0085,0x0083,0x8B7C,0x8B7B,0x0083,0x8B7A,0x8B79,0x0085,0x0083,0x8B78,0x8B77,0x0083,0x8B76,0x8B75,0x00C1,
+ 0x00A1,0x0091,0x0089,0x0085,0x0083,0x8B74,0x8B73,0x0083,0x8B72,0x8B71,0x0085,0x0083,0x8B70,0x8B6F,0x0083,0xE6C2,
+ 0xC3C4,0x0089,0x0085,0x0083,0x8B6E,0x8B6D,0x0083,0x8B6C,0x8B6B,0x0085,0x0083,0x8B6A,0x8B69,0x0083,0x8B68,0xC3BD,
+ 0x0091,0x0089,0x0085,0x0083,0x8B67,0x8B66,0x0083,0x8B65,0x8B64,0x0085,0x0083,0x8B63,0x8B62,0x0083,0x8B61,0x8B60,
+ 0x0089,0x0085,0x0083,0x8B5F,0x8B5E,0x0083,0x8B5D,0x8B5C,0x0085,0x0083,0x8B5B,0x8B5A,0x0083,0x8B59,0x8B58,0x00A1,
+ 0x0091,0x0089,0x0085,0x0083,0x8B57,0x8B56,0x0083,0xD0F6,0x8B55,0x0085,0x0083,0x8B54,0x8B53,0x0083,0x8B52,0xE6C4,
+ 0x0089,0x0085,0x0083,0x8B51,0x8B50,0x0083,0xE6C3,0xC9F4,0x0085,0x0083,0xE6BF,0xD3A4,0x0083,0x8B4F,0x8B4E,0x0091,
+ 0x0089,0x0085,0x0083,0x8B4D,0x8B4C,0x0083,0x8B4B,0x8B4A,0x0085,0x0083,0x8B49,0x8B48,0x0083,0x8B47,0xC0B7,0x0089,
+ 0x0085,0x0083,0x8B46,0x8B45,0x0083,0xE6BA,0x8B44,0x0085,0x0083,0x8B43,0x8B42,0x0083,0x8B41,0xE6BE,0x027F,0x017F,
+ 0x00FF,0x00BF,0x009F,0x008F,0x0087,0x0083,0x8B40,0x0083,0x8AFE,0x8AFD,0x0085,0x0083,0x8AFC,0x8AFB,0x0083,0x8AFA,
+ 0x8AF9,0x0089,0x0085,0x0083,0xBBE9,0x8AF8,0x0083,0x8AF7,0x8AF6,0x0085,0x0083,0x8AF5,0xE6BC,0x0083,0x8AF4,0x8AF3,
+ 0x0091,0x0089,0x0085,0x0083,0x8AF2,0x8AF1,0x0083,0x8AF0,0x8AEF,0x0085,0x0083,0x8AEE,0x8AED,0x0083,0x8AEC,0x8AEB,
+ 0x0089,0x0085,0x0083,0xE6BB,0xCDF1,0x0083,0x8AEA,0x8AE9,0x0085,0x0083,0xC6C5,0x8AE8,0x0083,0x8AE7,0x8AE6,0x00A1,
+ 0x0091,0x0089,0x0085,0x0083,0x8AE5,0x8AE4,0x0083,0xE6B9,0x8AE3,0x0085,0x0083,0x8AE2,0x8AE1,0x0083,0xE6BD,0x8AE0,
+ 0x0089,0x0085,0x0083,0x8ADF,0x8ADE,0x0083,0x8ADD,0x8ADC,0x0085,0x0083,0xC8A2,0x8ADB,0x0083,0xE6B5,0x8ADA,0x0091,
+ 0x0089,0x0085,0x0083,0xE6B4,0xD3E9,0x0083,0x8AD9,0x8AD8,0x0085,0x0083,0x8AD7,0x8AD6,0x0083,0x8AD5,0x8AD4,0x0089,
+ 0x0085,0x0083,0x8AD3,0xC3E4,0x0083,0x8AD2,0x8AD1,0x0085,0x0083,0x8AD0,0xB6F0,0x0083,0x8ACF,0xE6B7,0x00C1,0x00A1,
+ 0x0091,0x0089,0x0085,0x0083,0x8ACE,0x8ACD,0x0083,0xC9EF,0xBEEA,0x0085,0x0083,0x8ACC,0x8ACB,0x0083,0xC4C8,0x8ACA,
+ 0x0089,0x0085,0x0083,0x8AC9,0x8AC8,0x0083,0xC4EF,0x8AC7,0x0085,0x0083,0x8AC6,0x8AC5,0x0083,0x8AC4,0xE6B8,0x0091,
+ 0x0089,0x0085,0x0083,0x8AC3,0xE6B6,0x0083,0x8AC2,0x8AC1,0x0085,0x0083,0x8AC0,0x8ABF,0x0083,0xE6B2,0x8ABE,0x0089,
+ 0x0085,0x0083,0x8ABD,0xE6B3,0x0083,0xE6AE,0xBDBF,0x0085,0x0083,0xE6AC,0xE6AB,0x0083,0xC2A6,0xCDDE,0x00A1,0x0091,
+ 0x0089,0x0085,0x0083,0x8ABC,0xCDFE,0x0083,0x8ABB,0xD7CB,0x0085,0x0083,0x8ABA,0x8AB9,0x0083,0x8AB8,0xD2F6,0x0089,
+ 0x0085,0x0083,0x8AB7,0xE6B1,0x0083,0x8AB6,0x8AB5,0x0085,0x0083,0x8AB4,0x8AB3,0x0083,0x8AB2,0x8AB1,0x0091,0x0089,
+ 0x0085,0x0083,0x8AB0,0x8AAF,0x0083,0x8AAE,0x8AAD,0x0085,0x0083,0x8AAC,0x8AAB,0x0083,0xBCA7,0x8AAA,0x0089,0x0085,
+ 0x0083,0x8AA9,0x8AA8,0x0083,0xD2CC,0x8AA7,0x0085,0x0083,0x8AA6,0xC0D1,0x0083,0x8AA5,0xE6AF,0x0101,0x00C1,0x00A1,
+ 0x0091,0x0089,0x0085,0x0083,0x8AA4,0x8AA3,0x0083,0x8AA2,0x8AA1,0x0085,0x0083,0x8AA0,0xE6AD,0x0083,0xBDAA,0x8A9F,
+ 0x0089,0x0085,0x0083,0xD2A6,0x8A9E,0x0083,0xE6B0,0xE6A9,0x0085,0x0083,0x8A9D,0x8A9C,0x0083,0xCEAF,0xD0D5,0x0091,
+ 0x0089,0x0085,0x0083,0xE6A6,0xB9C3,0x0083,0xBDE3,0x8A9B,0x0085,0x0083,0x8A9A,0x8A99,0x0083,0x8A98,0xCABC,0x0089,
+ 0x0085,0x0083,0xE6A2,0x8A97,0x0083,0x8A96,0x8A95,0x0085,0x0083,0xC4B7,0x8A94,0x0083,0x8A93,0x8A92,0x00A1,0x0091,
+ 0x0089,0x0085,0x0083,0x8A91,0x8A90,0x0083,0x8A8F,0x8A8E,0x0085,0x0083,0xE6AA,0x8A8D,0x0083,0x8A8C,0xC6DE,0x0089,
+ 0x0085,0x0083,0x8A8B,0xC3C3,0x0083,0x8A8A,0x8A89,0x0085,0x0083,0x8A88,0x8A87,0x0083,0x8A86,0x8A85,0x0091,0x0089,
+ 0x0085,0x0083,0xE6A7,0x8A84,0x0083,0x8A83,0xE6A8,0x0085,0x0083,0xC4DD,0x8A82,0x0083,0x8A81,0xE6A3,0x0089,0x0085,
+ 0x0083,0xE5FD,0xE5FC,0x0083,0xB7C1,0x8A80,0x0085,0x0083,0x8A7E,0xCDD7,0x0083,0xE6A5,0xE5FE,0x00C1,0x00A1,0x0091,
+ 0x0089,0x0085,0x0083,0x8A7D,0x8A7C,0x0083,0x8A7B,0x8A7A,0x0085,0x0083,0xE6A4,0x8A79,0x0083,0x8A78,0x8A77,0x0089,
+ 0x0085,0x0083,0x8A76,0xC3EE,0x0083,0x8A75,0xE6A1,0x0085,0x0083,0xD1FD,0x8A74,0x0083,0x8A73,0xBCCB,0x0091,0x0089,
+ 0x0085,0x0083,0xB6CA,0x8A72,0x0083,0x8A71,0x8A70,0x0085,0x0083,0x8A6F,0xE5FB,0x0083,0x8A6E,0x8A6D,0x0089,0x0085,
+ 0x0083,0xC8D1,0x8A6C,0x0083,0xC2E8,0xB8BE,0x0085,0x0083,0xD7B1,0x8A6B,0x0083,0xCDFD,0xE5FA,0x00A1,0x0091,0x0089,
+ 0x0085,0x0083,0xC8E7,0xE5F9,0x0083,0x8A6A,0x8A69,0x0085,0x0083,0x8A68,0xBAC3,0x0083,0x8A67,0x8A66,0x0089,0x0085,
+ 0x0083,0x8A65,0xCBFD,0x0083,0xBCE9,0x8A64,0x0085,0x0083,0xC4CC,0x8A63,0x0083,0xC5AB,0xC5AE,0x0091,0x0089,0x0085,
+ 0x0083,0x8A62,0x8A61,0x0083,0x8A60,0x8A5F,0x0085,0x0083,0x8A5E,0x8A5D,0x0083,0x8A5C,0x8A5B,0x0089,0x0085,0x0083,
+ 0x8A5A,0x8A59,0x0083,0x8A58,0x8A57,0x0085,0x0083,0x8A56,0xB0C2,0x0083,0x8A55,0x8A54,0x0181,0x0101,0x00C1,0x00A1,
+ 0x0091,0x0089,0x0085,0x0083,0xC9DD,0x8A53,0x0083,0xB5EC,0x8A52,0x0085,0x0083,0x8A51,0x8A50,0x0083,0x8A4F,0x8A4E,
+ 0x0089,0x0085,0x0083,0xDEC9,0x8A4D,0x0083,0xDECA,0xCCD7,0x0085,0x0083,0xBDB1,0xDEC8,0x0083,0xB1BC,0x8A4C,0x0091,
+ 0x0089,0x0085,0x0083,0x8A4B,0xC6F5,0x0083,0x8A4A,0xD7E0,0x0085,0x0083,0xBFFC,0x8A49,0x0083,0x8A48,0xB7DC,0x0089,
+ 0x0085,0x0083,0x8A47,0xB7EE,0x0083,0xC4CE,0xC6E6,0x0085,0x0083,0x8A46,0x8A45,0x0083,0xD1D9,0x8A44,0x00A1,0x0091,
+ 0x0089,0x0085,0x0083,0xDBBC,0xDEC6,0x0083,0x8A43,0x8A42,0x0085,0x0083,0x8A41,0x8A40,0x0083,0xDEC5,0x89FE,0x0089,
+ 0x0085,0x0083,0xB6E1,0xBCD0,0x0083,0xBFE4,0xD2C4,0x0085,0x0083,0x89FD,0x89FC,0x0083,0xCDB7,0x89FB,0x0091,0x0089,
+ 0x0085,0x0083,0x89FA,0xCAA7,0x0083,0x89F9,0xBABB,0x0085,0x0083,0xD1EB,0xD8B2,0x0083,0x89F8,0xB7F2,0x0089,0x0085,
+ 0x0083,0xCCAB,0xCCEC,0x0083,0x89F7,0xB4F3,0x0085,0x0083,0x89F6,0xE2B7,0x0083,0xE2B9,0x89F5,0x00C1,0x00A1,0x0091,
+ 0x0089,0x0085,0x0083,0x89F4,0x89F3,0x0083,0x89F2,0xB9BB,0x0085,0x0083,0x89F1,0x89F0,0x0083,0xD2B9,0x89EF,0x0089,
+ 0x0085,0x0083,0xB6E0,0xD9ED,0x0083,0x89EE,0x89ED,0x0085,0x0083,0xCDE2,0xCFA6,0x0083,0xD9E7,0x89EC,0x0091,0x0089,
+ 0x0085,0x0083,0x89EB,0x89EA,0x0083,0x89E9,0xCFC4,0x0085,0x0083,0x89E8,0xB8B4,0x0083,0x89E7,0x89E6,0x0089,0x0085,
+ 0x0083,0x89E5,0x89E4,0x0083,0x89E3,0xB1B8,0x0085,0x0083,0x89E2,0x89E1,0x0083,0xB4A6,0x89E0,0x00A1,0x0091,0x0089,
+ 0x0085,0x0083,0xE2BA,0x89DF,0x0083,0x89DE,0x89DD,0x0085,0x0083,0x89DC,0x89DB,0x0083,0x89DA,0x89D9,0x0089,0x0085,
+ 0x0083,0x89D8,0xD2BC,0x0083,0x89D7,0x89D6,0x0085,0x0083,0xBAF8,0x89D5,0x0083,0x89D4,0xBFC7,0x0091,0x0089,0x0085,
+ 0x0083,0x89D3,0x89D2,0x0083,0xC9F9,0x89D1,0x0085,0x0083,0xD7B3,0x89D0,0x0083,0xC8C9,0xCABF,0x0089,0x0085,0x0083,
+ 0x89CF,0x89CE,0x0083,0x89CD,0x89CC,0x0085,0x0083,0x89CB,0x89CA,0x0083,0xC8C0,0x89C9,0x0101,0x00C1,0x00A1,0x0091,
+ 0x0089,0x0085,0x0083,0x89C8,0x89C7,0x0083,0x89C6,0x89C5,0x0085,0x0083,0x89C4,0x89C3,0x0083,0x89C2,0x89C1,0x0089,
+ 0x0085,0x0083,0x89C0,0x89BF,0x0083,0x89BE,0x89BD,0x0085,0x0083,0x89BC,0xBABE,0x0083,0x89BB,0x89BA,0x0091,0x0089,
+ 0x0085,0x0083,0x89B9,0xDBD6,0x0083,0x89B8,0x89B7,0x0085,0x0083,0x89B6,0x89B5,0x0083,0x89B4,0x89B3,0x0089,0x0085,
+ 0x0083,0x89B2,0x89B1,0x0083,0x89B0,0x89AF,0x0085,0x0083,0x89AE,0xDBD5,0x0083,0x89AD,0x89AC,0x00A1,0x0091,0x0089,
+ 0x0085,0x0083,0x89AB,0xB1DA,0x0083,0x89AA,0x89A9,0x0085,0x0083,0x89A8,0x89A7,0x0083,0xDBD4,0x89A6,0x0089,0x0085,
+ 0x0083,0x89A5,0x89A4,0x0083,0x89A3,0x89A2,0x0085,0x0083,0x89A1,0x89A0,0x0083,0x899F,0x899E,0x0091,0x0089,0x0085,
+ 0x0083,0x899D,0x899C,0x0083,0x899B,0x899A,0x0085,0x0083,0x8999,0x8998,0x0083,0x8997,0x8996,0x0089,0x0085,0x0083,
+ 0x8995,0xB6D5,0x0083,0xC4AB,0x8994,0x0085,0x0083,0x8993,0x8992,0x0083,0x8991,0x8990,0x00C1,0x00A1,0x0091,0x0089,
+ 0x0085,0x0083,0x898F,0x898E,0x0083,0x898D,0xD0E6,0x0085,0x0083,0xD4F6,0x898C,0x0083,0x898B,0x898A,0x0089,0x0085,
+ 0x0083,0xDCAE,0xC7BD,0x0083,0x8989,0x8988,0x0085,0x0083,0x8987,0x8986,0x0083,0x8985,0xC4B9,0x0091,0x0089,0x0085,
+ 0x0083,0xC9CA,0x8984,0x0083,0x8983,0x8982,0x0085,0x0083,0x8981,0x8980,0x0083,0x897E,0x897D,0x0089,0x0085,0x0083,
+ 0x897C,0xDCAD,0x0083,0x897B,0x897A,0x0085,0x0083,0x8979,0xCAFB,0x0083,0x8978,0xBEB3,0x00A1,0x0091,0x0089,0x0085,
+ 0x0083,0x8977,0xDCAC,0x0083,0xDCAF,0x8976,0x0085,0x0083,0xDBD3,0x8975,0x0083,0x8974,0x8973,0x0089,0x0085,0x0083,
+ 0x8972,0x8971,0x0083,0x8970,0x896F,0x0085,0x0083,0x896E,0x896D,0x0083,0x896C,0x896B,0x0091,0x0089,0x0085,0x0083,
+ 0x896A,0x8969,0x0083,0x8968,0x8967,0x0085,0x0083,0x8966,0x8965,0x0083,0xDCAB,0xCCEE,0x0089,0x0085,0x0083,0x8964,
+ 0x8963,0x0083,0x8962,0x8961,0x0085,0x0083,0x8960,0xDCAA,0x0083,0x895F,0x895E,0x087B,0x047D,0x027F,0x017F,0x00FF,
+ 0x00BF,0x009F,0x008F,0x0087,0x0083,0x895D,0x0083,0x895C,0x895B,0x0085,0x0083,0x895A,0xC8FB,0x0083,0x8959,0x8958,
+ 0x0089,0x0085,0x0083,0x8957,0x8956,0x0083,0x8955,0xCCC1,0x0085,0x0083,0x8954,0x8953,0x0083,0x8952,0xCBFE,0x0091,
+ 0x0089,0x0085,0x0083,0x8951,0x8950,0x0083,0xCBDC,0x894F,0x0085,0x0083,0x894E,0x894D,0x0083,0xEBF3,0xCBFA,0x0089,
+ 0x0085,0x0083,0x894C,0x894B,0x0083,0x894A,0x8949,0x0085,0x0083,0x8948,0x8947,0x0083,0x8946,0xDCA8,0x00A1,0x0091,
+ 0x0089,0x0085,0x0083,0x8945,0x8944,0x0083,0x8943,0x8942,0x0085,0x0083,0x8941,0x8940,0x0083,0x88FE,0x88FD,0x0089,
+ 0x0085,0x0083,0x88FC,0x88FB,0x0083,0x88FA,0x88F9,0x0085,0x0083,0x88F8,0x88F7,0x0083,0xB6C2,0x88F6,0x0091,0x0089,
+ 0x0085,0x0083,0x88F5,0x88F4,0x0083,0x88F3,0xD1DF,0x0085,0x0083,0x88F2,0x88F1,0x0083,0x88F0,0x88EF,0x0089,0x0085,
+ 0x0083,0x88EE,0xBFB0,0x0083,0x88ED,0x88EC,0x0085,0x0083,0x88EB,0x88EA,0x0083,0x88E9,0xB5CC,0x00C1,0x00A1,0x0091,
+ 0x0089,0x0085,0x0083,0x88E8,0x88E7,0x0083,0xB1A4,0xDCA9,0x0085,0x0083,0x88E6,0xDCA6,0x0083,0x88E5,0x88E4,0x0089,
+ 0x0085,0x0083,0x88E3,0x88E2,0x0083,0xDCA7,0x88E1,0x0085,0x0083,0x88E0,0x88DF,0x0083,0xB6E9,0x88DE,0x0091,0x0089,
+ 0x0085,0x0083,0x88DD,0x88DC,0x0083,0xC7B5,0x88DB,0x0085,0x0083,0x88DA,0x88D9,0x0083,0xDCA2,0x88D8,0x0089,0x0085,
+ 0x0083,0xDCA1,0x88D7,0x0083,0x88D6,0x88D5,0x0085,0x0083,0xDDC0,0xB6D1,0x0083,0x88D4,0x88D3,0x00A1,0x0091,0x0089,
+ 0x0085,0x0083,0x88D2,0xCCC3,0x0083,0x88D1,0xDCA5,0x0085,0x0083,0x88D0,0x88CF,0x0083,0xDCA3,0x88CE,0x0089,0x0085,
+ 0x0083,0x88CD,0xBBF9,0x0083,0xC5E0,0xDBFC,0x0085,0x0083,0x88CC,0x88CB,0x0083,0x88CA,0xDBFA,0x0091,0x0089,0x0085,
+ 0x0083,0x88C9,0x88C8,0x0083,0x88C7,0x88C6,0x0085,0x0083,0xDBFB,0x88C5,0x0083,0xDCA4,0x88C4,0x0089,0x0085,0x0083,
+ 0x88C3,0x88C2,0x0083,0x88C1,0x88C0,0x0085,0x0083,0x88BF,0x88BE,0x0083,0x88BD,0xDBFD,0x0101,0x00C1,0x00A1,0x0091,
+ 0x0089,0x0085,0x0083,0x88BC,0x88BB,0x0083,0x88BA,0xB2BA,0x0085,0x0083,0xD3F2,0x88B9,0x0083,0xDBFE,0x88B8,0x0089,
+ 0x0085,0x0083,0x88B7,0xDBF6,0x0083,0xDBF7,0xDBF5,0x0085,0x0083,0x88B6,0x88B5,0x0083,0xDBF4,0xC6D2,0x0091,0x0089,
+ 0x0085,0x0083,0x88B4,0xDBF8,0x0083,0x88B3,0x88B2,0x0085,0x0083,0xDBEF,0xB3C7,0x0083,0x88B1,0x88B0,0x0089,0x0085,
+ 0x0083,0xC2F1,0x88AF,0x0083,0x88AE,0x88AD,0x0085,0x0083,0x88AC,0x88AB,0x0083,0x88AA,0x88A9,0x00A1,0x0091,0x0089,
+ 0x0085,0x0083,0xB0A3,0xB9A1,0x0083,0x88A8,0x88A7,0x0085,0x0083,0x88A6,0x88A5,0x0083,0x88A4,0x88A3,0x0089,0x0085,
+ 0x0083,0x88A2,0x88A1,0x0083,0x88A0,0xDBF9,0x0085,0x0083,0x889F,0x889E,0x0083,0x889D,0xDBF1,0x0091,0x0089,0x0085,
+ 0x0083,0x889C,0xDBEE,0x0083,0x889B,0x889A,0x0085,0x0083,0x8899,0xBFE5,0x0083,0xDBEB,0x8898,0x0089,0x0085,0x0083,
+ 0xB5E6,0x8897,0x0083,0xDBD1,0x8896,0x0085,0x0083,0xDBF0,0xBFD1,0x0083,0x8895,0xDBEC,0x00C1,0x00A1,0x0091,0x0089,
+ 0x0085,0x0083,0xD4AB,0xB9B8,0x0083,0xDBD2,0xDBF3,0x0085,0x0083,0x8894,0x8893,0x0083,0x8892,0x8891,0x0089,0x0085,
+ 0x0083,0xB6E2,0x8890,0x0083,0x888F,0x888E,0x0085,0x0083,0x888D,0x888C,0x0083,0x888B,0x888A,0x0091,0x0089,0x0085,
+ 0x0083,0xDBF2,0xC0DD,0x0083,0x8889,0x8888,0x0085,0x0083,0x8887,0x8886,0x0083,0x8885,0xDBED,0x0089,0x0085,0x0083,
+ 0xD0CD,0x8884,0x0083,0x8883,0x8882,0x0085,0x0083,0x8881,0xDBE4,0x0083,0xDBE2,0xC2A2,0x00A1,0x0091,0x0089,0x0085,
+ 0x0083,0xC0AC,0xB4B9,0x0083,0x8880,0x887E,0x0085,0x0083,0x887D,0x887C,0x0083,0x887B,0xDBE5,0x0089,0x0085,0x0083,
+ 0xDBE6,0x887A,0x0083,0x8879,0x8878,0x0085,0x0083,0xBFC0,0xDBE9,0x0083,0x8877,0x8876,0x0091,0x0089,0x0085,0x0083,
+ 0xDBEA,0x8875,0x0083,0x8874,0x8873,0x0085,0x0083,0xC5F7,0x8872,0x0083,0xDBE8,0x8871,0x0089,0x0085,0x0083,0xDBE3,
+ 0xC6BA,0x0083,0xDBE1,0xDBE7,0x0085,0x0083,0x8870,0xCCB9,0x0083,0x886F,0xC0A4,0x017F,0x00FF,0x00BF,0x009F,0x008F,
+ 0x0087,0x0083,0x886E,0x0083,0x886D,0xC6C2,0x0085,0x0083,0xD7B9,0xB7D8,0x0083,0xCEEB,0xB0D3,0x0089,0x0085,0x0083,
+ 0xDBDE,0xCCB3,0x0083,0xBCE1,0x886C,0x0085,0x0083,0x886B,0xBFE9,0x0083,0x886A,0x8869,0x0091,0x0089,0x0085,0x0083,
+ 0x8868,0x8867,0x0083,0x8866,0xBFD3,0x0085,0x0083,0xD7F8,0xBBB5,0x0083,0xBFB2,0xCCAE,0x0089,0x0085,0x0083,0xDBD0,
+ 0x8865,0x0083,0xB7BB,0x8864,0x0085,0x0083,0x8863,0xBEF9,0x0083,0x8862,0x8861,0x00A1,0x0091,0x0089,0x0085,0x0083,
+ 0x8860,0x885F,0x0083,0xDBE0,0x885E,0x0085,0x0083,0xD6B7,0x885D,0x0083,0xBBF8,0x885C,0x0089,0x0085,0x0083,0x885B,
+ 0xDBDF,0x0083,0xB3A1,0xDBDB,0x0085,0x0083,0x885A,0x8859,0x0083,0x8858,0x8857,0x0091,0x0089,0x0085,0x0083,0x8856,
+ 0xDBDA,0x0083,0x8855,0x8854,0x0085,0x0083,0xB5D8,0xDBDD,0x0083,0xDBDC,0xB9E7,0x0089,0x0085,0x0083,0xDBD8,0x8853,
+ 0x0083,0xDBD9,0xDBD7,0x0085,0x0083,0xD4DA,0x8852,0x0083,0x8851,0x8850,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,
+ 0x884F,0xCAA5,0x0083,0x884E,0x884D,0x0085,0x0083,0x884C,0xCDC1,0x0083,0x884B,0x884A,0x0089,0x0085,0x0083,0xE0F7,
+ 0x8849,0x0083,0x8848,0x8847,0x0085,0x0083,0x8846,0x8845,0x0083,0x8844,0x8843,0x0091,0x0089,0x0085,0x0083,0x8842,
+ 0x8841,0x0083,0x8840,0x87FE,0x0085,0x0083,0x87FD,0x87FC,0x0083,0x87FB,0x87FA,0x0089,0x0085,0x0083,0x87F9,0x87F8,
+ 0x0083,0xE0F5,0xE0F6,0x0085,0x0083,0xC8A6,0x87F7,0x0083,0xD4B2,0x87F6,0x00A1,0x0091,0x0089,0x0085,0x0083,0xE0F4,
+ 0xC6D4,0x0083,0x87F5,0x87F4,0x0085,0x0083,0x87F3,0xE0F3,0x0083,0xCDBC,0xB9FA,0x0089,0x0085,0x0083,0x87F2,0x87F1,
+ 0x0083,0xB9CC,0xE0F2,0x0085,0x0083,0x87F0,0x87EF,0x0083,0x87EE,0xE0F0,0x0091,0x0089,0x0085,0x0083,0xCEA7,0x87ED,
+ 0x0083,0x87EC,0xB4D1,0x0085,0x0083,0xC0A7,0x87EB,0x0083,0x87EA,0xD4B0,0x0089,0x0085,0x0083,0x87E9,0xE0F1,0x0083,
+ 0x87E8,0x87E7,0x0085,0x0083,0x87E6,0x87E5,0x0083,0x87E4,0x87E3,0x0101,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,
+ 0xB6DA,0x87E2,0x0083,0xCDC5,0xE0EF,0x0085,0x0083,0xD2F2,0xD8B6,0x0083,0xBBD8,0xE0EE,0x0089,0x0085,0x0083,0x87E1,
+ 0xCBC4,0x0083,0xC7F4,0x87E0,0x0085,0x0083,0x87DF,0xE0ED,0x0083,0x87DE,0x87DD,0x0091,0x0089,0x0085,0x0083,0xE0EC,
+ 0x87DC,0x0083,0x87DB,0x87DA,0x0085,0x0083,0x87D9,0x87D8,0x0083,0x87D7,0x87D6,0x0089,0x0085,0x0083,0x87D5,0x87D4,
+ 0x0083,0xC4D2,0x87D3,0x0085,0x0083,0x87D2,0x87D1,0x0083,0x87D0,0x87CF,0x00A1,0x0091,0x0089,0x0085,0x0083,0x87CE,
+ 0x87CD,0x0083,0x87CC,0x87CB,0x0085,0x0083,0x87CA,0x87C9,0x0083,0x87C8,0x87C7,0x0089,0x0085,0x0083,0xBDC0,0x87C6,
+ 0x0083,0x87C5,0x87C4,0x0085,0x0083,0x87C3,0xC8C2,0x0083,0x87C2,0x87C1,0x0091,0x0089,0x0085,0x0083,0x87C0,0x87BF,
+ 0x0083,0x87BE,0x87BD,0x0085,0x0083,0x87BC,0xE0EB,0x0083,0x87BB,0x87BA,0x0089,0x0085,0x0083,0x87B9,0x87B8,0x0083,
+ 0x87B7,0x87B6,0x0085,0x0083,0x87B5,0x87B4,0x0083,0x87B3,0x87B2,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0x87B1,
+ 0xCFF9,0x0083,0x87B0,0x87AF,0x0085,0x0083,0x87AE,0x87AD,0x0083,0x87AC,0x87AB,0x0089,0x0085,0x0083,0x87AA,0x87A9,
+ 0x0083,0x87A8,0x87A7,0x0085,0x0083,0x87A6,0x87A5,0x0083,0x87A4,0x87A3,0x0091,0x0089,0x0085,0x0083,0x87A2,0xE0EA,
+ 0x0083,0x87A1,0x87A0,0x0085,0x0083,0x879F,0xCCE7,0x0083,0xBABF,0x879E,0x0089,0x0085,0x0083,0x879D,0x879C,0x0083,
+ 0x879B,0x879A,0x0085,0x0083,0x8799,0x8798,0x0083,0xE0E3,0xE0E9,0x00A1,0x0091,0x0089,0x0085,0x0083,0x8797,0x8796,
+ 0x0083,0x8795,0x8794,0x0085,0x0083,0x8793,0x8792,0x0083,0x8791,0x8790,0x0089,0x0085,0x0083,0xE0E8,0xE0E7,0x0083,
+ 0x878F,0x878E,0x0085,0x0083,0x878D,0x878C,0x0083,0xB8C1,0x878B,0x0091,0x0089,0x0085,0x0083,0x878A,0x8789,0x0083,
+ 0x8788,0xE0E5,0x0085,0x0083,0x8787,0x8786,0x0083,0x8785,0x8784,0x0089,0x0085,0x0083,0xCAC9,0xE0E6,0x0083,0xD4EB,
+ 0xD8AC,0x0085,0x0083,0xC6F7,0x8783,0x0083,0x8782,0x8781,0x027F,0x017F,0x00FF,0x00BF,0x009F,0x008F,0x0087,0x0083,
+ 0xE0E4,0x0083,0x8780,0xE0DE,0x0085,0x0083,0x877E,0x877D,0x0083,0x877C,0x877B,0x0089,0x0085,0x0083,0x877A,0xE0E0,
+ 0x0083,0x8779,0x8778,0x0085,0x0083,0xE0DF,0xE0D9,0x0083,0xE0DB,0x8777,0x0091,0x0089,0x0085,0x0083,0x8776,0xE0E2,
+ 0x0083,0x8775,0x8774,0x0085,0x0083,0x8773,0x8772,0x0083,0x8771,0xD2AD,0x0089,0x0085,0x0083,0xE0DD,0xE0E1,0x0083,
+ 0x8770,0x876F,0x0085,0x0083,0x876E,0x876D,0x0083,0x876C,0x876B,0x00A1,0x0091,0x0089,0x0085,0x0083,0x876A,0x8769,
+ 0x0083,0x8768,0x8767,0x0085,0x0083,0x8766,0x8765,0x0083,0xBAD9,0x8764,0x0089,0x0085,0x0083,0x8763,0x8762,0x0083,
+ 0xCEFB,0x8761,0x0085,0x0083,0xE0DA,0x8760,0x0083,0x875F,0xCBBB,0x0091,0x0089,0x0085,0x0083,0x875E,0xD7EC,0x0083,
+ 0x875D,0xB3B0,0x0085,0x0083,0xD6F6,0x875C,0x0083,0x875B,0x875A,0x0089,0x0085,0x0083,0xE0D8,0xE0DC,0x0083,0x8759,
+ 0x8758,0x0085,0x0083,0x8757,0x8756,0x0083,0xE0D7,0x8755,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0x8754,0xE0D3,
+ 0x0083,0xE0D4,0x8753,0x0085,0x0083,0x8752,0x8751,0x0083,0xE0BD,0xE0CF,0x0089,0x0085,0x0083,0x8750,0x874F,0x0083,
+ 0xC2EF,0x874E,0x0085,0x0083,0x874D,0xD0EA,0x0083,0x874C,0x874B,0x0091,0x0089,0x0085,0x0083,0x874A,0x8749,0x0083,
+ 0x8748,0x8747,0x0085,0x0083,0x8746,0x8745,0x0083,0xD8C5,0xB8C2,0x0089,0x0085,0x0083,0x8744,0xE0D1,0x0083,0x8743,
+ 0x8742,0x0085,0x0083,0xBCCE,0xE0D0,0x0083,0x8741,0x8740,0x00A1,0x0091,0x0089,0x0085,0x0083,0x86FE,0x86FD,0x0083,
+ 0x86FC,0x86FB,0x0085,0x0083,0xE0D2,0xE0D6,0x0083,0x86FA,0xE0D5,0x0089,0x0085,0x0083,0xCBD4,0x86F9,0x0083,0x86F8,
+ 0x86F7,0x0085,0x0083,0x86F6,0x86F5,0x0083,0xE0BB,0x86F4,0x0091,0x0089,0x0085,0x0083,0xE0CC,0x86F3,0x0083,0xE0C8,
+ 0xE0C7,0x0085,0x0083,0x86F2,0x86F1,0x0083,0xE0C5,0x86F0,0x0089,0x0085,0x0083,0x86EF,0xE0C0,0x0083,0xE0BF,0xE0BA,
+ 0x0085,0x0083,0x86EE,0xE0CB,0x0083,0x86ED,0xE0C2,0x0101,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0xE0C6,0xE0CD,
+ 0x0083,0xCBC3,0x86EC,0x0085,0x0083,0xCECB,0x86EB,0x0083,0xE0B5,0x86EA,0x0089,0x0085,0x0083,0xE0C3,0xCAC8,0x0083,
+ 0x86E9,0x86E8,0x0085,0x0083,0x86E7,0x86E6,0x0083,0x86E5,0xE0B2,0x0091,0x0089,0x0085,0x0083,0x86E4,0xE0C1,0x0083,
+ 0xC9A4,0xE0AA,0x0085,0x0083,0xE0BE,0x86E3,0x0083,0x86E2,0x86E1,0x0089,0x0085,0x0083,0xE0CA,0xE0C9,0x0083,0x86E0,
+ 0x86DF,0x0085,0x0083,0xE0BC,0x86DE,0x0083,0x86DD,0x86DC,0x00A1,0x0091,0x0089,0x0085,0x0083,0xD0E1,0xE0C4,0x0083,
+ 0x86DB,0x86DA,0x0085,0x0083,0x86D9,0x86D8,0x0083,0x86D7,0xE0B7,0x0089,0x0085,0x0083,0xE0B6,0x86D6,0x0083,0xD3F7,
+ 0x86D5,0x0085,0x0083,0xE0AD,0x86D4,0x0083,0xC5E7,0x86D3,0x0091,0x0089,0x0085,0x0083,0xDFF7,0x86D2,0x0083,0xD4FB,
+ 0x86D1,0x0085,0x0083,0xE0AC,0x86D0,0x0083,0x86CF,0x86CE,0x0089,0x0085,0x0083,0x86CD,0x86CC,0x0083,0x86CB,0x86CA,
+ 0x0085,0x0083,0x86C9,0x86C8,0x0083,0xD0FA,0x86C7,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0x86C6,0x86C5,0x0083,
+ 0x86C4,0x86C3,0x0085,0x0083,0x86C2,0x86C1,0x0083,0xE0B0,0x86C0,0x0089,0x0085,0x0083,0xBAC8,0xCFB2,0x0083,0x86BF,
+ 0x86BE,0x0085,0x0083,0xE0B9,0xB4AD,0x0083,0x86BD,0x86BC,0x0091,0x0089,0x0085,0x0083,0x86BB,0xE0B8,0x0083,0x86BA,
+ 0x86B9,0x0085,0x0083,0xE0B3,0x86B8,0x0083,0xDFF6,0x86B7,0x0089,0x0085,0x0083,0x86B6,0x86B5,0x0083,0xE0A9,0xBAB0,
+ 0x0085,0x0083,0xBAED,0xE0AE,0x0083,0xC0AE,0x86B4,0x00A1,0x0091,0x0089,0x0085,0x0083,0x86B3,0xC9C6,0x0083,0xE0AB,
+ 0xCEB9,0x0085,0x0083,0xE0AF,0xBFA6,0x0083,0x86B2,0xE0B1,0x0089,0x0085,0x0083,0x86B1,0xCCE4,0x0083,0xE0B4,0x86B0,
+ 0x0085,0x0083,0x86AF,0xD0A5,0x0083,0xE0A5,0xE0A4,0x0091,0x0089,0x0085,0x0083,0xE0A3,0x86AE,0x0083,0x86AD,0x86AC,
+ 0x0085,0x0083,0x86AB,0x86AA,0x0083,0x86A9,0xC4F6,0x0089,0x0085,0x0083,0xDFF9,0xD8C4,0x0083,0x86A8,0xC5BE,0x0085,
+ 0x0083,0x86A7,0x86A6,0x0083,0xDFF5,0xC0B2,0x0181,0x0101,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0xC9B6,0xC6A1,
+ 0x0083,0x86A5,0x86A4,0x0085,0x0083,0xB7C8,0x86A3,0x0083,0x86A2,0x86A1,0x0089,0x0085,0x0083,0x86A0,0xE0A8,0x0083,
+ 0x869F,0x869E,0x0085,0x0083,0x869D,0x869C,0x0083,0x869B,0xE0A2,0x0091,0x0089,0x0085,0x0083,0xDFFB,0x869A,0x0083,
+ 0x8699,0x8698,0x0085,0x0083,0x8697,0xDFFD,0x0083,0x8696,0x8695,0x0089,0x0085,0x0083,0x8694,0x8693,0x0083,0x8692,
+ 0xB0A1,0x0085,0x0083,0xDFF8,0x8691,0x0083,0x8690,0xC9CC,0x00A1,0x0091,0x0089,0x0085,0x0083,0x868F,0xD7C4,0x0083,
+ 0xBFD0,0x868E,0x0085,0x0083,0xDFFA,0x868D,0x0083,0xDFFC,0xCDD9,0x0089,0x0085,0x0083,0x868C,0xDFFE,0x0083,0x868B,
+ 0x868A,0x0085,0x0083,0x8689,0x8688,0x0083,0xE0A1,0x8687,0x0091,0x0089,0x0085,0x0083,0x8686,0x8685,0x0083,0xE0A6,
+ 0x8684,0x0085,0x0083,0xB3AA,0xE0A7,0x0083,0xCEA8,0xCADB,0x0089,0x0085,0x0083,0x8683,0xBBA3,0x0083,0x8682,0xDFF4,
+ 0x0085,0x0083,0x8681,0x8680,0x0083,0xDFF3,0x867E,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0x867D,0xBBBD,0x0083,
+ 0xDFF0,0xDFEF,0x0085,0x0083,0x867C,0xDFEB,0x0083,0x867B,0x867A,0x0089,0x0085,0x0083,0x8679,0x8678,0x0083,0xDFE9,
+ 0x8677,0x0085,0x0083,0x8676,0x8675,0x0083,0x8674,0x8673,0x0091,0x0089,0x0085,0x0083,0x8672,0xDFED,0x0083,0x8671,
+ 0x8670,0x0085,0x0083,0xDFF2,0xCCC6,0x0083,0xDFF1,0x866F,0x0089,0x0085,0x0083,0x866E,0x866D,0x0083,0x866C,0x866B,
+ 0x0085,0x0083,0xB0A6,0x866A,0x0083,0xB4BD,0xCBF4,0x00A1,0x0091,0x0089,0x0085,0x0083,0x8669,0x8668,0x0083,0x8667,
+ 0x8666,0x0085,0x0083,0xD1E4,0x8665,0x0083,0xDBC1,0x8664,0x0089,0x0085,0x0083,0xDFEC,0xBADF,0x0083,0x8663,0xB2B8,
+ 0x0085,0x0083,0x8662,0x8661,0x0083,0x8660,0x865F,0x0091,0x0089,0x0085,0x0083,0x865E,0x865D,0x0083,0xDFEE,0xD5DC,
+ 0x0085,0x0083,0x865C,0x865B,0x0083,0x865A,0xCFF8,0x0089,0x0085,0x0083,0xBFDE,0x8659,0x0083,0x8658,0xC4C4,0x0085,
+ 0x0083,0xC1A8,0xC9DA,0x0083,0xDFEA,0xC5B6,0x0101,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0xB8E7,0x8657,0x0083,
+ 0x8656,0x8655,0x0085,0x0083,0x8654,0x8653,0x0083,0xD3B4,0xDFE8,0x0089,0x0085,0x0083,0xDFE6,0xDFE2,0x0083,0x8652,
+ 0xDFE1,0x0085,0x0083,0xDFE0,0x8651,0x0083,0xBBA9,0x8650,0x0091,0x0089,0x0085,0x0083,0xDFDC,0xDFD9,0x0083,0xDFD8,
+ 0xDFD5,0x0085,0x0083,0xD1C6,0xDFD1,0x0083,0xDFE7,0xB0A5,0x0089,0x0085,0x0083,0xCFEC,0xDFDF,0x0083,0x864F,0x864E,
+ 0x0085,0x0083,0xD4D5,0xB9FE,0x0083,0xCDDB,0xB6DF,0x00A1,0x0091,0x0089,0x0085,0x0083,0x864D,0xBAE5,0x0083,0x864C,
+ 0xDFD3,0x0085,0x0083,0xC6B7,0xB0A7,0x0083,0xDFDE,0x864B,0x0089,0x0085,0x0083,0xD1CA,0x864A,0x0083,0xDFDD,0x8649,
+ 0x0085,0x0083,0x8648,0xCFCC,0x0083,0x8647,0x8646,0x0091,0x0089,0x0085,0x0083,0x8645,0xDFD4,0x0083,0xBFC8,0x8644,
+ 0x0085,0x0083,0xD4DB,0x8643,0x0083,0xBFA9,0x8642,0x0089,0x0085,0x0083,0xDFD2,0xD2A7,0x0083,0xE5EB,0xDFE4,0x0085,
+ 0x0083,0xDFE3,0xD7C9,0x0083,0xDFD6,0xDFD7,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0x8641,0xDFE5,0x0083,0xDFDB,
+ 0x8640,0x0085,0x0083,0x85FE,0x85FD,0x0083,0x85FC,0x85FB,0x0089,0x0085,0x0083,0xDFD0,0x85FA,0x0083,0xDFCC,0xDFCB,
+ 0x0085,0x0083,0xC1FC,0x85F9,0x0083,0x85F8,0xBFA7,0x0091,0x0089,0x0085,0x0083,0xB9BE,0xDFC7,0x0083,0x85F7,0xD6E4,
+ 0x0085,0x0083,0x85F6,0xB8C0,0x0083,0xD3BD,0xBECC,0x0089,0x0085,0x0083,0x85F5,0xBACD,0x0083,0xD5A6,0x85F4,0x0085,
+ 0x0083,0x85F3,0x85F2,0x0083,0x85F1,0xC5D8,0x00A1,0x0091,0x0089,0x0085,0x0083,0x85F0,0xDFCD,0x0083,0x85EF,0xDFC6,
+ 0x0085,0x0083,0x85EE,0xBED7,0x0083,0x85ED,0x85EC,0x0089,0x0085,0x0083,0xC3FC,0xBAF4,0x0083,0xC9EB,0x85EB,0x0085,
+ 0x0083,0x85EA,0xC5DE,0x0083,0xDFC8,0xDFCE,0x0091,0x0089,0x0085,0x0083,0xBAC7,0x85E9,0x0083,0xCEB6,0xDFDA,0x0085,
+ 0x0083,0xDFC9,0x85E8,0x0083,0x85E7,0x85E6,0x0089,0x0085,0x0083,0x85E5,0x85E4,0x0083,0x85E3,0x85E2,0x0085,0x0083,
+ 0x85E1,0xD6DC,0x0083,0x85E0,0xDFCF,0x047D,0x027F,0x017F,0x00FF,0x00BF,0x009F,0x008F,0x0087,0x0083,0x85DF,0x0083,
+ 0xDFCA,0x85DE,0x0085,0x0083,0xC4D8,0x85DD,0x0083,0x85DC,0x85DB,0x0089,0x0085,0x0083,0x85DA,0x85D9,0x0083,0xCED8,
+ 0xC7BA,0x0085,0x0083,0x85D8,0xDFC3,0x0083,0xD4B1,0xDFC2,0x0091,0x0089,0x0085,0x0083,0xDFBF,0xC5BB,0x0083,0xDFBE,
+ 0xDFBD,0x0085,0x0083,0xDFBC,0x85D7,0x0083,0xC4C5,0x85D6,0x0089,0x0085,0x0083,0x85D5,0x85D4,0x0083,0x85D3,0xDFBB,
+ 0x0085,0x0083,0xB8E6,0x85D2,0x0083,0xB3CA,0x85D1,0x00A1,0x0091,0x0089,0x0085,0x0083,0xB4F4,0x85D0,0x0083,0x85CF,
+ 0xDFC0,0x0085,0x0083,0x85CE,0x85CD,0x0083,0xD1BD,0x85CC,0x0089,0x0085,0x0083,0xCEE1,0x85CB,0x0083,0xBAF0,0xCEC7,
+ 0x0085,0x0083,0x85CA,0xB4B5,0x0083,0xCEFC,0x85C9,0x0091,0x0089,0x0085,0x0083,0x85C8,0xB3B3,0x0083,0xCEE2,0x85C7,
+ 0x0085,0x0083,0xDFC5,0xD6A8,0x0083,0x85C6,0xC6F4,0x0089,0x0085,0x0083,0xCBB1,0xBFD4,0x0083,0xCCFD,0xBAAC,0x0085,
+ 0x0083,0x85C5,0xB7D4,0x0083,0xB6D6,0xB0C9,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0xB7F1,0x85C4,0x0083,0x85C3,
+ 0xDFC4,0x0085,0x0083,0x85C2,0xDFC1,0x0083,0xB7CD,0xD2F7,0x0089,0x0085,0x0083,0xCDCC,0xC1DF,0x0083,0x85C1,0xBEFD,
+ 0x0085,0x0083,0x85C0,0x85BF,0x0083,0x85BE,0xC2F0,0x0091,0x0089,0x0085,0x0083,0xDFB9,0xC2C0,0x0083,0x85BD,0xCFC5,
+ 0x0085,0x0083,0xDFB8,0xCFF2,0x0083,0xCDC2,0xC0F4,0x0089,0x0085,0x0083,0xBAF3,0xC3FB,0x0083,0xCDAC,0x85BC,0x0085,
+ 0x0083,0xB5F5,0xBCAA,0x0083,0xBACF,0x85BB,0x00A1,0x0091,0x0089,0x0085,0x0083,0xDFBA,0x85BA,0x0083,0xB8F7,0xB3D4,
+ 0x0085,0x0083,0x85B9,0xD3F5,0x0083,0x85B8,0x85B7,0x0089,0x0085,0x0083,0x85B6,0xDFB4,0x0083,0xB5F0,0xDFB7,0x0085,
+ 0x0083,0x85B5,0xCCBE,0x0083,0xCBBE,0xBAC5,0x0091,0x0089,0x0085,0x0083,0xD2B6,0xD8CF,0x0083,0x85B4,0xD3D2,0x0085,
+ 0x0083,0xCAB7,0xDFB3,0x0083,0xCCA8,0xBFC9,0x0089,0x0085,0x0083,0xB6A3,0xB0C8,0x0083,0xD5D9,0xBDD0,0x0085,0x0083,
+ 0xD6BB,0xDFB5,0x0083,0xDFB6,0x85B3,0x0101,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0xC1ED,0xBEE4,0x0083,0xB9C5,
+ 0xBFDA,0x0085,0x0083,0x85B2,0x85B1,0x0083,0xB5FE,0xDBC5,0x0089,0x0085,0x0083,0x85B0,0x85AF,0x0083,0x85AE,0xC5D1,
+ 0x0085,0x0083,0x85AD,0xD0F0,0x0083,0xB1E4,0xCADC,0x0091,0x0089,0x0085,0x0083,0xC8A1,0x85AC,0x0083,0xCAE5,0x85AB,
+ 0x0085,0x0083,0x85AA,0xB7A2,0x0083,0x85A9,0x85A8,0x0089,0x0085,0x0083,0x85A7,0xB7B4,0x0083,0xCBAB,0xD3D1,0x0085,
+ 0x0083,0xBCB0,0xB2E6,0x0083,0xD3D6,0x85A6,0x00A1,0x0091,0x0089,0x0085,0x0083,0x85A5,0x85A4,0x0083,0x85A3,0x85A2,
+ 0x0085,0x0083,0xB2CE,0xC8FE,0x0083,0x85A1,0xCFD8,0x0089,0x0085,0x0083,0x85A0,0x859F,0x0083,0x859E,0xC8A5,0x0085,
+ 0x0083,0x859D,0x859C,0x0083,0x859B,0x859A,0x0091,0x0089,0x0085,0x0083,0xDBCC,0x8599,0x0083,0x8598,0x8597,0x0085,
+ 0x0083,0x8596,0x8595,0x0083,0x8594,0x8593,0x0089,0x0085,0x0083,0xD8CB,0x8592,0x0083,0x8591,0x8590,0x0085,0x0083,
+ 0x858F,0xBEC7,0x0083,0xB3F8,0x858E,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0xCFC3,0xD8CA,0x0083,0x858D,0xD8C9,
+ 0x0085,0x0083,0xCFE1,0x858C,0x0083,0x858B,0xD4AD,0x0089,0x0085,0x0083,0x858A,0xD8C8,0x0083,0x8589,0x8588,0x0085,
+ 0x0083,0xBAF1,0x8587,0x0083,0xC0E5,0x8586,0x0091,0x0089,0x0085,0x0083,0x8585,0xB2DE,0x0083,0x8584,0x8583,0x0085,
+ 0x0083,0x8582,0x8581,0x0083,0x8580,0x857E,0x0089,0x0085,0x0083,0x857D,0xD8C7,0x0083,0xD1E1,0xD1B9,0x0085,0x0083,
+ 0x857C,0xC0F7,0x0083,0x857B,0x857A,0x00A1,0x0091,0x0089,0x0085,0x0083,0xC0FA,0xCCFC,0x0083,0xB6F2,0x8579,0x0085,
+ 0x0083,0xB3A7,0x8578,0x0083,0x8577,0xC7E4,0x0089,0x0085,0x0083,0x8576,0x8575,0x0083,0x8574,0x8573,0x0085,0x0083,
+ 0xDAE1,0x8572,0x0083,0xD0B6,0xBEED,0x0091,0x0089,0x0085,0x0083,0x8571,0xC2D1,0x0083,0xC8B4,0xBCB4,0x0085,0x0083,
+ 0x8570,0xCEA3,0x0083,0xD3A1,0xC3AE,0x0089,0x0085,0x0083,0xD8B4,0x856F,0x0083,0x856E,0xCEC0,0x0085,0x0083,0x856D,
+ 0xDAE0,0x0083,0x856C,0xCED4,0x017F,0x00FF,0x00BF,0x009F,0x008F,0x0087,0x0083,0xD8D4,0x0083,0x856B,0xC2B1,0x0085,
+ 0x0083,0xD8D5,0xC2AC,0x0083,0xBFA8,0xD5BC,0x0089,0x0085,0x0083,0xDFB2,0xB1E5,0x0083,0x856A,0xB2B7,0x0085,0x0083,
+ 0x8569,0xB2A9,0x0083,0x8568,0x8567,0x0091,0x0089,0x0085,0x0083,0xC4CF,0xC2F4,0x0083,0xB5A5,0x8566,0x0085,0x0083,
+ 0xD7BF,0xD7E4,0x0083,0xB1B0,0x8565,0x0089,0x0085,0x0083,0xD0AD,0xBBAA,0x0083,0x8564,0x8563,0x0085,0x0083,0x8562,
+ 0xB0EB,0x0083,0xBBDC,0xCEE7,0x00A1,0x0091,0x0089,0x0085,0x0083,0xC9FD,0x8561,0x0083,0xD8A6,0x8560,0x0085,0x0083,
+ 0xC7A7,0x855F,0x0083,0xCAAE,0x855E,0x0089,0x0085,0x0083,0xC4E4,0xD8D2,0x0083,0x855D,0x855C,0x0085,0x0083,0xD2BD,
+ 0xC7F8,0x0083,0xC6A5,0x855B,0x0091,0x0089,0x0085,0x0083,0x855A,0x8559,0x0083,0x8558,0x8557,0x0085,0x0083,0x8556,
+ 0x8555,0x0083,0x8554,0x8553,0x0089,0x0085,0x0083,0x8552,0xD8D1,0x0083,0x8551,0x8550,0x0085,0x0083,0x854F,0xB7CB,
+ 0x0083,0x854E,0x854D,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0x854C,0xD8D0,0x0083,0x854B,0x854A,0x0085,0x0083,
+ 0xCFBB,0x8549,0x0083,0xBFEF,0xBDB3,0x0089,0x0085,0x0083,0x8548,0x8547,0x0083,0xD4D1,0x8546,0x0085,0x0083,0x8545,
+ 0xD8CE,0x0083,0xB3D7,0x8544,0x0091,0x0089,0x0085,0x0083,0xB1B1,0xBBAF,0x0083,0xD8B0,0x8543,0x0085,0x0083,0x8542,
+ 0x8541,0x0083,0x8540,0xD9EB,0x0089,0x0085,0x0083,0xDECB,0x84FE,0x0083,0xD9E9,0x84FD,0x0085,0x0083,0x84FC,0x84FB,
+ 0x0083,0x84FA,0xD0D9,0x00A1,0x0091,0x0089,0x0085,0x0083,0x84F9,0xB4D2,0x0083,0xB0FC,0x84F8,0x0085,0x0083,0x84F7,
+ 0x84F6,0x0083,0x84F5,0xD4C8,0x0089,0x0085,0x0083,0xCEF0,0xB9B4,0x0083,0x84F4,0x84F3,0x0085,0x0083,0x84F2,0xC9D7,
+ 0x0083,0xD9E8,0x84F1,0x0091,0x0089,0x0085,0x0083,0x84F0,0x84EF,0x0083,0x84EE,0x84ED,0x0085,0x0083,0x84EC,0x84EB,
+ 0x0083,0x84EA,0xDBC4,0x0089,0x0085,0x0083,0x84E9,0x84E8,0x0083,0x84E7,0x84E6,0x0085,0x0083,0x84E5,0x84E4,0x0083,
+ 0x84E3,0x84E2,0x0101,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0x84E1,0x84E0,0x0083,0x84DF,0xC7DA,0x0085,0x0083,
+ 0x84DE,0x84DD,0x0083,0x84DC,0x84DB,0x0089,0x0085,0x0083,0xC4BC,0x84DA,0x0083,0x84D9,0x84D8,0x0085,0x0083,0x84D7,
+ 0x84D6,0x0083,0x84D5,0xBFB1,0x0091,0x0089,0x0085,0x0083,0x84D4,0xDBC3,0x0083,0x84D3,0x84D2,0x0085,0x0083,0x84D1,
+ 0xC0D5,0x0083,0x84D0,0xDBC2,0x0089,0x0085,0x0083,0x84CF,0x84CE,0x0083,0x84CD,0x84CC,0x0085,0x0083,0xD1AB,0x84CB,
+ 0x0083,0xC3E3,0x84CA,0x00A1,0x0091,0x0089,0x0085,0x0083,0xD3C2,0x84C9,0x0083,0x84C8,0x84C7,0x0085,0x0083,0xB2AA,
+ 0x84C6,0x0083,0x84C5,0x84C4,0x0089,0x0085,0x0083,0xCAC6,0xDBC0,0x0083,0x84C3,0x84C2,0x0085,0x0083,0x84C1,0x84C0,
+ 0x0083,0x84BF,0x84BE,0x0091,0x0089,0x0085,0x0083,0x84BD,0x84BC,0x0083,0x84BB,0x84BA,0x0085,0x0083,0xC0CD,0xBEA2,
+ 0x0083,0xC0F8,0x84B9,0x0089,0x0085,0x0083,0x84B8,0x84B7,0x0083,0xDBBF,0xDBBE,0x0085,0x0083,0xBDD9,0xC5AC,0x0083,
+ 0xD6FA,0xB6AF,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0x84B6,0x84B5,0x0083,0x84B4,0x84B3,0x0085,0x0083,0xC1D3,
+ 0xDBBD,0x0083,0xCEF1,0xBCD3,0x0089,0x0085,0x0083,0xB9A6,0xB0EC,0x0083,0xC8B0,0x84B2,0x0085,0x0083,0xC1A6,0x84B1,
+ 0x0083,0x84B0,0x84AF,0x0091,0x0089,0x0085,0x0083,0x84AE,0x84AD,0x0083,0x84AC,0x84AB,0x0085,0x0083,0xD8E6,0x84AA,
+ 0x0083,0x84A9,0xD8E5,0x0089,0x0085,0x0083,0x84A8,0x84A7,0x0083,0x84A6,0x84A5,0x0085,0x0083,0x84A4,0x84A3,0x0083,
+ 0x84A2,0xC5FC,0x00A1,0x0091,0x0089,0x0085,0x0083,0x84A1,0x84A0,0x0083,0x849F,0x849E,0x0085,0x0083,0x849D,0xD8E3,
+ 0x0083,0xD8E4,0x849C,0x0089,0x0085,0x0083,0xBDCB,0x849B,0x0083,0xD8E2,0x849A,0x0085,0x0083,0x8499,0x8498,0x0083,
+ 0x8497,0x8496,0x0091,0x0089,0x0085,0x0083,0x8495,0x8494,0x0083,0x8493,0x8492,0x0085,0x0083,0x8491,0xB8EE,0x0083,
+ 0x8490,0x848F,0x0089,0x0085,0x0083,0xB8B1,0x848E,0x0083,0x848D,0x848C,0x0085,0x0083,0x848B,0xBCF4,0x0083,0xCAA3,
+ 0x848A,0x027F,0x017F,0x00FF,0x00BF,0x009F,0x008F,0x0087,0x0083,0xBEE7,0x0083,0x8489,0xB0FE,0x0085,0x0083,0x8488,
+ 0x8487,0x0083,0x8486,0xD8DF,0x0089,0x0085,0x0083,0x8485,0x8484,0x0083,0xD8DE,0x8483,0x0085,0x0083,0xD8E0,0x8482,
+ 0x0083,0x8481,0x8480,0x0091,0x0089,0x0085,0x0083,0x847E,0x847D,0x0083,0xC6CA,0x847C,0x0085,0x0083,0xCCDE,0x847B,
+ 0x0083,0x847A,0xBDA3,0x0089,0x0085,0x0083,0xB9D0,0x8479,0x0083,0x8478,0xC7B0,0x0085,0x0083,0xD8DD,0x8477,0x0083,
+ 0xCFF7,0x8476,0x00A1,0x0091,0x0089,0x0085,0x0083,0x8475,0x8474,0x0083,0x8473,0x8472,0x0085,0x0083,0x8471,0xCCEA,
+ 0x0083,0xBCC1,0xB6E7,0x0089,0x0085,0x0083,0xD8DC,0xD8DB,0x0083,0x8470,0xB9F4,0x0085,0x0083,0x846F,0xBFCC,0x0083,
+ 0xB4CC,0xC9B2,0x0091,0x0089,0x0085,0x0083,0xC8AF,0xCBA2,0x0083,0xD6C6,0x846E,0x0085,0x0083,0x846D,0xD8DA,0x0083,
+ 0x846C,0x846B,0x0089,0x0085,0x0083,0xB5BD,0x846A,0x0083,0xB9CE,0xD8D9,0x0085,0x0083,0x8469,0xB1F0,0x0083,0x8468,
+ 0xC0FB,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0xC5D9,0x8467,0x0083,0x8466,0x8465,0x0085,0x0083,0xC5D0,0x8464,
+ 0x0083,0x8463,0x8462,0x0089,0x0085,0x0083,0xC9BE,0x8461,0x0083,0x8460,0xB3F5,0x0085,0x0083,0x845F,0xB4B4,0x0083,
+ 0xB8D5,0xD4F2,0x0091,0x0089,0x0085,0x0083,0xC1F5,0xC1D0,0x0083,0xEBBE,0x845E,0x0085,0x0083,0x845D,0x845C,0x0083,
+ 0xBBAE,0xD0CC,0x0089,0x0085,0x0083,0x845B,0x845A,0x0083,0xD8D8,0xDBBB,0x0085,0x0083,0x8459,0x8458,0x0083,0xBFAF,
+ 0x8457,0x00A1,0x0091,0x0089,0x0085,0x0083,0xD8D7,0xC7D0,0x0083,0xB7D6,0x8456,0x0085,0x0083,0x8455,0xC8D0,0x0083,
+ 0xD8D6,0xB5F3,0x0089,0x0085,0x0083,0xB5B6,0xD4E4,0x0083,0x8454,0xBAAF,0x0085,0x0083,0xDBCA,0xBBF7,0x0083,0xB3F6,
+ 0xB0BC,0x0091,0x0089,0x0085,0x0083,0xCDB9,0x8453,0x0083,0xD0D7,0xDBC9,0x0085,0x0083,0x8452,0xB5CA,0x0083,0x8451,
+ 0x8450,0x0089,0x0085,0x0083,0xBBCB,0xBFAD,0x0083,0x844F,0xC6BE,0x0085,0x0083,0x844E,0xD9EC,0x0083,0x844D,0x844C,
+ 0x0101,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0x844B,0x844A,0x0083,0x8449,0x8448,0x0085,0x0083,0xB7EF,0x8447,
+ 0x0083,0x8446,0xB7B2,0x0089,0x0085,0x0083,0xBCB8,0x8445,0x0083,0x8444,0xC4FD,0x0085,0x0083,0x8443,0xC1DD,0x0083,
+ 0x8442,0x8441,0x0091,0x0089,0x0085,0x0083,0x8440,0x83FE,0x0083,0x83FD,0x83FC,0x0085,0x0083,0x83FB,0x83FA,0x0083,
+ 0x83F9,0xB4D5,0x0089,0x0085,0x0083,0x83F8,0xBCF5,0x0083,0x83F7,0x83F6,0x0085,0x0083,0xC1E8,0xB5F2,0x0083,0x83F5,
+ 0xC1B9,0x00A1,0x0091,0x0089,0x0085,0x0083,0x83F4,0xDAA1,0x0083,0xD7BC,0x83F3,0x0085,0x0083,0xC6E0,0x83F2,0x0083,
+ 0x83F1,0x83F0,0x0089,0x0085,0x0083,0xBEBB,0x83EF,0x0083,0x83EE,0xD9FD,0x0085,0x0083,0xD9FE,0xB6B3,0x0083,0x83ED,
+ 0x83EC,0x0091,0x0089,0x0085,0x0083,0x83EB,0xC0E4,0x0083,0xD2B1,0xBFF6,0x0085,0x0083,0x83EA,0xBEF6,0x0083,0xB3E5,
+ 0xD9FC,0x0089,0x0085,0x0083,0xB1F9,0xB7EB,0x0083,0x83E9,0x83E8,0x0085,0x0083,0xB6AC,0xD9FB,0x0083,0x83E7,0x83E6,
+ 0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0x83E5,0x83E4,0x0083,0x83E3,0xDAA4,0x0085,0x0083,0xD4A9,0x83E2,0x0083,
+ 0xDAA3,0x83E1,0x0089,0x0085,0x0083,0xB9DA,0x83E0,0x0083,0x83DF,0x83DE,0x0085,0x0083,0xC5A9,0xBEFC,0x0083,0x83DD,
+ 0xD0B4,0x0091,0x0089,0x0085,0x0083,0x83DC,0xC8DF,0x0083,0xDAA2,0xC3E1,0x0085,0x0083,0x83DB,0x83DA,0x0083,0xC3B0,
+ 0x83D9,0x0089,0x0085,0x0083,0x83D8,0x83D7,0x0083,0x83D6,0xD4D9,0x0085,0x0083,0xB2E1,0x83D5,0x0083,0x83D4,0xC8BD,
+ 0x00A1,0x0091,0x0089,0x0085,0x0083,0xB8D4,0x83D3,0x0083,0x83D2,0xC4DA,0x0085,0x0083,0x83D1,0x83D0,0x0083,0xD8E7,
+ 0xD9E6,0x0089,0x0085,0x0083,0xBCBD,0x83CF,0x0083,0x83CE,0xCADE,0x0085,0x0083,0xBCE6,0xD1F8,0x0083,0x83CD,0xD7C8,
+ 0x0091,0x0089,0x0085,0x0083,0xB5E4,0xBEDF,0x0083,0xC6E4,0xB1F8,0x0085,0x0083,0xD0CB,0xB9D8,0x0083,0x83CC,0xB9B2,
+ 0x0089,0x0085,0x0083,0xC0BC,0x83CB,0x0083,0xD9E2,0xC1F9,0x0085,0x0083,0xB9AB,0xB0CB,0x0083,0x83CA,0x83C9,0x0181,
+ 0x0101,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0xC8AB,0x83C8,0x0083,0x83C7,0xC8EB,0x0085,0x0083,0x83C6,0x83C5,
+ 0x0083,0xBEA4,0x83C4,0x0089,0x0085,0x0083,0x83C3,0x83C2,0x0083,0x83C1,0x83C0,0x0085,0x0083,0xB6B5,0x83BF,0x0083,
+ 0xB5B3,0x83BE,0x0091,0x0089,0x0085,0x0083,0x83BD,0x83BC,0x0083,0xD9F0,0xD9EE,0x0085,0x0083,0xCDC3,0x83BB,0x0083,
+ 0x83BA,0xB6D2,0x0089,0x0085,0x0083,0x83B9,0x83B8,0x0083,0x83B7,0xC3E2,0x0085,0x0083,0x83B6,0xBFCB,0x0083,0x83B5,
+ 0xB9E2,0x00A1,0x0091,0x0089,0x0085,0x0083,0xCFC8,0x83B4,0x0083,0xD5D7,0xB3E4,0x0085,0x0083,0xD0D6,0xD4AA,0x0083,
+ 0x83B3,0xD4CA,0x0089,0x0085,0x0083,0xD8A3,0xB6F9,0x0083,0x83B2,0x83B1,0x0085,0x0083,0x83B0,0x83AF,0x0083,0x83AE,
+ 0x83AD,0x0091,0x0089,0x0085,0x0083,0x83AC,0x83AB,0x0083,0x83AA,0x83A9,0x0085,0x0083,0x83A8,0x83A7,0x0083,0x83A6,
+ 0x83A5,0x0089,0x0085,0x0083,0x83A4,0x83A3,0x0083,0x83A2,0x83A1,0x0085,0x0083,0x83A0,0x839F,0x0083,0x839E,0x839D,
+ 0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0x839C,0x839B,0x0083,0x839A,0x8399,0x0085,0x0083,0x8398,0x8397,0x0083,
+ 0x8396,0xC0DC,0x0089,0x0085,0x0083,0x8395,0x8394,0x0083,0x8393,0x8392,0x0085,0x0083,0x8391,0x8390,0x0083,0x838F,
+ 0x838E,0x0091,0x0089,0x0085,0x0083,0x838D,0x838C,0x0083,0x838B,0x838A,0x0085,0x0083,0x8389,0x8388,0x0083,0xC8E5,
+ 0x8387,0x0089,0x0085,0x0083,0x8386,0x8385,0x0083,0x8384,0x8383,0x0085,0x0083,0x8382,0xD9D9,0x0083,0x8381,0x8380,
+ 0x00A1,0x0091,0x0089,0x0085,0x0083,0x837E,0xD9D8,0x0083,0xD9D3,0x837D,0x0085,0x0083,0x837C,0x837B,0x0083,0x837A,
+ 0x8379,0x0089,0x0085,0x0083,0x8378,0x8377,0x0083,0x8376,0x8375,0x0085,0x0083,0x8374,0xC6A7,0x0083,0x8373,0x8372,
+ 0x0091,0x0089,0x0085,0x0083,0x8371,0x8370,0x0083,0x836F,0xBDA9,0x0085,0x0083,0x836E,0xCBDB,0x0083,0x836D,0x836C,
+ 0x0089,0x0085,0x0083,0x836B,0x836A,0x0083,0xD9D7,0xD9D4,0x0085,0x0083,0xD9D5,0x8369,0x0083,0x8368,0x8367,0x0101,
+ 0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0x8366,0xC9AE,0x0083,0xD9D6,0x8365,0x0085,0x0083,0x8364,0x8363,0x0083,
+ 0x8362,0x8361,0x0089,0x0085,0x0083,0x8360,0x835F,0x0083,0x835E,0x835D,0x0085,0x0083,0x835C,0x835B,0x0083,0xC1C5,
+ 0x835A,0x0091,0x0089,0x0085,0x0083,0x8359,0x8358,0x0083,0xD9D2,0x8357,0x0085,0x0083,0x8356,0x8355,0x0083,0x8354,
+ 0x8353,0x0089,0x0085,0x0083,0x8352,0xCFF1,0x0083,0x8351,0x8350,0x0085,0x0083,0x834F,0x834E,0x0083,0x834D,0x834C,
+ 0x00A1,0x0091,0x0089,0x0085,0x0083,0x834B,0x834A,0x0083,0x8349,0x8348,0x0085,0x0083,0x8347,0x8346,0x0083,0x8345,
+ 0x8344,0x0089,0x0085,0x0083,0x8343,0x8342,0x0083,0x8341,0x8340,0x0085,0x0083,0x82FE,0xC9B5,0x0083,0xD9D1,0x82FD,
+ 0x0091,0x0089,0x0085,0x0083,0x82FC,0x82FB,0x0083,0x82FA,0x82F9,0x0085,0x0083,0x82F8,0x82F7,0x0083,0xB0C1,0x82F6,
+ 0x0089,0x0085,0x0083,0x82F5,0x82F4,0x0083,0x82F3,0x82F2,0x0085,0x0083,0xB4DF,0x82F1,0x0083,0x82F0,0xD9D0,0x00C1,
+ 0x00A1,0x0091,0x0089,0x0085,0x0083,0xB4A2,0xD9CF,0x0083,0x82EF,0xD9CE,0x0085,0x0083,0x82EE,0xB4F6,0x0083,0x82ED,
+ 0x82EC,0x0089,0x0085,0x0083,0x82EB,0x82EA,0x0083,0x82E9,0x82E8,0x0085,0x0083,0x82E7,0x82E6,0x0083,0x82E5,0x82E4,
+ 0x0091,0x0089,0x0085,0x0083,0x82E3,0x82E2,0x0083,0x82E1,0x82E0,0x0085,0x0083,0x82DF,0x82DE,0x0083,0x82DD,0x82DC,
+ 0x0089,0x0085,0x0083,0x82DB,0x82DA,0x0083,0x82D9,0xB0F8,0x0085,0x0083,0x82D8,0x82D7,0x0083,0x82D6,0x82D5,0x00A1,
+ 0x0091,0x0089,0x0085,0x0083,0xC0FC,0x82D4,0x0083,0x82D3,0xB8B5,0x0085,0x0083,0x82D2,0x82D1,0x0083,0x82D0,0x82CF,
+ 0x0089,0x0085,0x0083,0xBFFE,0xB3A5,0x0083,0xD9C7,0x82CE,0x0085,0x0083,0x82CD,0xD9CD,0x0083,0x82CC,0x82CB,0x0091,
+ 0x0089,0x0085,0x0083,0x82CA,0xCDB5,0x0083,0xC5BC,0x82C9,0x0085,0x0083,0x82C8,0x82C7,0x0083,0x82C6,0x82C5,0x0089,
+ 0x0085,0x0083,0x82C4,0x82C3,0x0083,0x82C2,0x82C1,0x0085,0x0083,0xD9CC,0x82C0,0x0083,0x82BF,0x82BE,0x0081,0x0081,
+ 0x047D,0x027F,0x017F,0x00FF,0x00BF,0x009F,0x008F,0x0087,0x0083,0x82BD,0x0083,0x82BC,0x82BB,0x0085,0x0083,0xBDA1,
+ 0x82BA,0x0083,0x82B9,0x82B8,0x0089,0x0085,0x0083,0x82B7,0x82B6,0x0083,0x82B5,0x82B4,0x0085,0x0083,0x82B3,0xCDA3,
+ 0x0083,0x82B2,0xD7F6,0x0091,0x0089,0x0085,0x0083,0x82B1,0x82B0,0x0083,0x82AF,0x82AE,0x0085,0x0083,0xD9C9,0x82AD,
+ 0x0083,0x82AC,0x82AB,0x0089,0x0085,0x0083,0x82AA,0x82A9,0x0083,0xC6AB,0xD9CB,0x0085,0x0083,0x82A8,0xD9BC,0x0083,
+ 0x82A7,0x82A6,0x00A1,0x0091,0x0089,0x0085,0x0083,0x82A5,0xD9CA,0x0083,0xBCD9,0x82A4,0x0085,0x0083,0x82A3,0x82A2,
+ 0x0083,0xD9C8,0x82A1,0x0089,0x0085,0x0083,0x82A0,0x829F,0x0083,0x829E,0xC7E3,0x0085,0x0083,0x829D,0xD6B5,0x0083,
+ 0x829C,0xD5AE,0x0091,0x0089,0x0085,0x0083,0x829B,0x829A,0x0083,0x8299,0x8298,0x0085,0x0083,0x8297,0x8296,0x0083,
+ 0x8295,0x8294,0x0089,0x0085,0x0083,0x8293,0x8292,0x0083,0x8291,0xD9C0,0x0085,0x0083,0xD9C1,0xD9BE,0x0083,0x8290,
+ 0xC4DF,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0xD9BB,0xD9C6,0x0083,0x828F,0xBEEB,0x0085,0x0083,0xD9C5,0x828E,
+ 0x0083,0x828D,0x828C,0x0089,0x0085,0x0083,0xB3AB,0x828B,0x0083,0xBDE8,0x828A,0x0085,0x0083,0x8289,0xD9C3,0x0083,
+ 0x8288,0xD2D0,0x0091,0x0089,0x0085,0x0083,0xBAF2,0xCCC8,0x0083,0x8287,0x8286,0x0085,0x0083,0x8285,0xBEF3,0x0083,
+ 0x8284,0xB5B9,0x0089,0x0085,0x0083,0x8283,0x8282,0x0083,0xD9BF,0x8281,0x0085,0x0083,0xB1B6,0xD9C4,0x0083,0x8280,
+ 0x827E,0x00A1,0x0091,0x0089,0x0085,0x0083,0x827D,0x827C,0x0083,0x827B,0x827A,0x0085,0x0083,0x8279,0x8278,0x0083,
+ 0x8277,0x8276,0x0089,0x0085,0x0083,0x8275,0x8274,0x0083,0x8273,0xD9C2,0x0085,0x0083,0x8272,0x8271,0x0083,0x8270,
+ 0xB0B3,0x0091,0x0089,0x0085,0x0083,0x826F,0xD9BA,0x0083,0x826E,0x826D,0x0085,0x0083,0x826C,0x826B,0x0083,0xD9BD,
+ 0x826A,0x0089,0x0085,0x0083,0xBEE3,0x8269,0x0083,0xB8A9,0xD0DE,0x0085,0x0083,0xBCF3,0x8268,0x0083,0x8267,0xD9B3,
+ 0x0101,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0xC1A9,0xD9B2,0x0083,0x8266,0xD9B1,0x0085,0x0083,0x8265,0x8264,
+ 0x0083,0xD9B6,0x8263,0x0089,0x0085,0x0083,0xD0C5,0x8262,0x0083,0xD9B9,0xD3E1,0x0085,0x0083,0xB1A3,0xD9B7,0x0083,
+ 0x8261,0xD9B5,0x0091,0x0089,0x0085,0x0083,0x8260,0xB7FD,0x0083,0xCBD7,0x825F,0x0085,0x0083,0x825E,0x825D,0x0083,
+ 0x825C,0x825B,0x0089,0x0085,0x0083,0xD9B8,0xC0FE,0x0083,0xC7CE,0xD9DE,0x0085,0x0083,0x825A,0x8259,0x0083,0x8258,
+ 0xBFA1,0x00A1,0x0091,0x0089,0x0085,0x0083,0x8257,0x8256,0x0083,0x8255,0x8254,0x0085,0x0083,0xD9B4,0xB6ED,0x0083,
+ 0xB4D9,0x8253,0x0089,0x0085,0x0083,0x8252,0x8251,0x0083,0xB1E3,0x8250,0x0085,0x0083,0x824F,0x824E,0x0083,0x824D,
+ 0x824C,0x0091,0x0089,0x0085,0x0083,0x824B,0x824A,0x0083,0x8249,0x8248,0x0085,0x0083,0xC7D6,0x8247,0x0083,0x8246,
+ 0x8245,0x0089,0x0085,0x0083,0x8244,0x8243,0x0083,0xBAEE,0xCEEA,0x0085,0x0083,0x8242,0xD9AF,0x0083,0x8241,0xD9AD,
+ 0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,0xBFEB,0xC7C8,0x0083,0xB2E0,0xD5EC,0x0085,0x0083,0xBDC4,0x8240,0x0083,
+ 0xC2C2,0x81FE,0x0089,0x0085,0x0083,0x81FD,0xCFC0,0x0083,0x81FC,0x81FB,0x0085,0x0083,0xD2C0,0x81FA,0x0083,0xB9A9,
+ 0x81F9,0x0091,0x0089,0x0085,0x0083,0x81F8,0x81F7,0x0083,0xB6B1,0x81F6,0x0085,0x0083,0x81F5,0xD9B0,0x0083,0x81F4,
+ 0x81F3,0x0089,0x0085,0x0083,0xD9A7,0x81F2,0x0083,0xD9AA,0x81F1,0x0085,0x0083,0xCACC,0x81F0,0x0083,0xC0FD,0x81EF,
+ 0x00A1,0x0091,0x0089,0x0085,0x0083,0xD9A8,0xB3DE,0x0083,0x81EE,0x81ED,0x0085,0x0083,0x81EC,0xD6B6,0x0083,0xD9A9,
+ 0x81EB,0x0089,0x0085,0x0083,0x81EA,0x81E9,0x0083,0xCAB9,0xD9AB,0x0085,0x0083,0x81E8,0xD9AE,0x0083,0xD9AC,0x81E7,
+ 0x0091,0x0089,0x0085,0x0083,0x81E6,0x81E5,0x0083,0x81E4,0xD9A5,0x0085,0x0083,0x81E3,0xD9A6,0x0083,0xBCD1,0x81E2,
+ 0x0089,0x0085,0x0083,0x81E1,0xB0DB,0x0083,0xD1F0,0x81E0,0x0085,0x0083,0x81DF,0xC0D0,0x0083,0x81DE,0x81DD,0x017F,
+ 0x00FF,0x00BF,0x009F,0x008F,0x0087,0x0083,0xC5E5,0x0083,0x81DC,0xD8FB,0x0085,0x0083,0x81DB,0xD9DD,0x0083,0xD8F4,
+ 0xD3B6,0x0089,0x0085,0x0083,0x81DA,0x81D9,0x0083,0xC4E3,0xD9A1,0x0085,0x0083,0xD8FA,0xD8FE,0x0083,0xD7F7,0xB7F0,
+ 0x0091,0x0089,0x0085,0x0083,0xD8FD,0xD3E0,0x0083,0xD9DC,0xD9A2,0x0085,0x0083,0x81D8,0xBACE,0x0083,0x81D7,0xCCE5,
+ 0x0089,0x0085,0x0083,0x81D6,0xD3D3,0x0083,0xD7F4,0xD7A1,0x0085,0x0083,0xB5CD,0xCEBB,0x0083,0x81D5,0x81D4,0x00A1,
+ 0x0091,0x0089,0x0085,0x0083,0x81D3,0x81D2,0x0083,0x81D1,0x81D0,0x0085,0x0083,0xB5AB,0x81CF,0x0083,0x81CE,0xB5E8,
+ 0x0089,0x0085,0x0083,0x81CD,0x81CC,0x0083,0x81CB,0x81CA,0x0085,0x0083,0x81C9,0xD9A4,0x0083,0xCBC6,0x81C8,0x0091,
+ 0x0089,0x0085,0x0083,0xCBC5,0x81C7,0x0083,0xC9EC,0x81C6,0x0085,0x0083,0xC1E6,0x81C5,0x0083,0xB0E9,0x81C4,0x0089,
+ 0x0085,0x0083,0xD9A3,0x81C3,0x0083,0xB9C0,0xB2AE,0x0085,0x0083,0x81C2,0x81C1,0x0083,0x81C0,0xD8F9,0x00C1,0x00A1,
+ 0x0091,0x0089,0x0085,0x0083,0xCEB1,0x81BF,0x0083,0x81BE,0xD8F7,0x0085,0x0083,0xC2D7,0xD8F6,0x0083,0xC9CB,0x81BD,
+ 0x0089,0x0085,0x0083,0xD8F3,0x81BC,0x0083,0xB4AB,0xCEB0,0x0085,0x0083,0xC9A1,0x81BB,0x0083,0x81BA,0xD8F1,0x0091,
+ 0x0089,0x0085,0x0083,0xBBE1,0xBBEF,0x0083,0xD3C5,0xD6DA,0x0085,0x0083,0x81B9,0x81B8,0x0083,0x81B7,0x81B6,0x0089,
+ 0x0085,0x0083,0x81B5,0xD0DD,0x0083,0xB7A5,0xB7FC,0x0085,0x0083,0xBCBF,0xCEE9,0x0083,0x81B4,0x81B3,0x00A1,0x0091,
+ 0x0089,0x0085,0x0083,0xD2C1,0xD8F8,0x0083,0x81B2,0x81B1,0x0085,0x0083,0x81B0,0x81AF,0x0083,0x81AE,0x81AD,0x0089,
+ 0x0085,0x0083,0x81AC,0xC6F3,0x0083,0x81AB,0xB7C2,0x0085,0x0083,0x81AA,0xB7DD,0x0083,0x81A9,0xC8CE,0x0091,0x0089,
+ 0x0085,0x0083,0x81A8,0x81A7,0x0083,0x81A6,0xBCDB,0x0085,0x0083,0xBCFE,0xD8F5,0x0083,0x81A5,0xD8F2,0x0089,0x0085,
+ 0x0083,0xD6D9,0x81A4,0x0083,0xD1F6,0x81A3,0x0085,0x0083,0x81A2,0x81A1,0x0083,0xC3C7,0xD8EF,0x0101,0x00C1,0x00A1,
+ 0x0091,0x0089,0x0085,0x0083,0xD2C7,0x81A0,0x0083,0xD8ED,0x819F,0x0085,0x0083,0x819E,0xD2D4,0x0083,0xC1EE,0xB4FA,
+ 0x0089,0x0085,0x0083,0x819D,0xD8EE,0x0083,0x819C,0xC7AA,0x0085,0x0083,0xD8F0,0xD9DA,0x0083,0x819B,0x819A,0x0091,
+ 0x0089,0x0085,0x0083,0x8199,0xCFC9,0x0083,0xB8B6,0xD5CC,0x0085,0x0083,0xCBFB,0xCACB,0x0083,0xD7D0,0xB2D6,0x0089,
+ 0x0085,0x0083,0x8198,0xC2D8,0x0083,0x8197,0x8196,0x0085,0x0083,0xB4D3,0xC8D4,0x0083,0x8195,0xBDE9,0x00A1,0x0091,
+ 0x0089,0x0085,0x0083,0xBDF1,0xD8EB,0x0083,0x8194,0xB3F0,0x0085,0x0083,0xC6CD,0xBDF6,0x0083,0xD8C6,0xD8EA,0x0089,
+ 0x0085,0x0083,0xD8EC,0xC8CA,0x0083,0xCAB2,0xD2DA,0x0085,0x0083,0x8193,0x8192,0x0083,0x8191,0xD8E9,0x0091,0x0089,
+ 0x0085,0x0083,0xC8CB,0x8190,0x0083,0x818F,0x818E,0x0085,0x0083,0x818D,0xD9F4,0x0083,0x818C,0xD9F1,0x0089,0x0085,
+ 0x0083,0xC7D7,0x818B,0x0083,0x818A,0x8189,0x0085,0x0083,0xC1C1,0xCDA4,0x0083,0xBEA9,0xCFED,0x00C1,0x00A1,0x0091,
+ 0x0089,0x0085,0x0083,0x8188,0xC4B6,0x0083,0xBAE0,0xB2FA,0x0085,0x0083,0xD2E0,0xBAA5,0x0083,0xBDBB,0x8187,0x0089,
+ 0x0085,0x0083,0xBFBA,0xCDF6,0x0083,0xD9EF,0xD8BD,0x0085,0x0083,0x8186,0x8185,0x0083,0x8184,0xD0A9,0x0091,0x0089,
+ 0x0085,0x0083,0xD1C7,0x8183,0x0083,0xD8A8,0x8182,0x0085,0x0083,0x8181,0xBEAE,0x0083,0xCEE5,0xD8C1,0x0089,0x0085,
+ 0x0083,0xBBA5,0xD4C6,0x0083,0x8180,0xBFF7,0x0085,0x0083,0xD3DA,0xD8A1,0x0083,0xB6FE,0xCAC2,0x00A1,0x0091,0x0089,
+ 0x0085,0x0083,0x817E,0xD5F9,0x0083,0xD3E8,0x817D,0x0085,0x0083,0xC1CB,0x817C,0x0083,0x817B,0x817A,0x0089,0x0085,
+ 0x0083,0x8179,0x8178,0x0083,0x8177,0x8176,0x0085,0x0083,0xC7AC,0x8175,0x0083,0x8174,0x8173,0x0091,0x0089,0x0085,
+ 0x0083,0x8172,0x8171,0x0083,0x8170,0x816F,0x0085,0x0083,0x816E,0x816D,0x0083,0x816C,0xC8E9,0x0089,0x0085,0x0083,
+ 0x816B,0xC2D2,0x0083,0xC2F2,0x816A,0x0085,0x0083,0x8169,0x8168,0x0083,0x8167,0x8166,0x0081,0x0081,0x00FF,0x00BF,
+ 0x009F,0x008F,0x0087,0x0083,0x8165,0x0083,0xD8C0,0x8164,0x0085,0x0083,0x8163,0xCAE9,0x0083,0x8162,0x8161,0x0089,
+ 0x0085,0x0083,0x8160,0x815F,0x0083,0xCFE7,0xCFB0,0x0085,0x0083,0xD2B2,0xC6F2,0x0083,0xBEC5,0xD8BF,0x0091,0x0089,
+ 0x0085,0x0083,0x815E,0x815D,0x0083,0xD2D2,0xB3CB,0x0085,0x0083,0x815C,0xB9D4,0x0083,0x815B,0xC7C7,0x0089,0x0085,
+ 0x0083,0xC5D2,0xC6B9,0x0083,0x815A,0xC0D6,0x0085,0x0083,0xB7A6,0xBAF5,0x0083,0xD5A7,0xCEDA,0x00A1,0x0091,0x0089,
+ 0x0085,0x0083,0xD6AE,0x8159,0x0083,0xD2E5,0xC3B4,0x0085,0x0083,0xD8B1,0x8158,0x0083,0xBEC3,0x8157,0x0089,0x0085,
+ 0x0083,0xC4CB,0x8156,0x0083,0x8155,0x8154,0x0085,0x0083,0xD8AF,0xBED9,0x0083,0xC0F6,0x8153,0x0091,0x0089,0x0085,
+ 0x0083,0xD6F7,0xCEAA,0x0083,0xB5A4,0xCDE8,0x0085,0x0083,0x8152,0xD8BC,0x0083,0x8151,0xC1D9,0x0089,0x0085,0x0083,
+ 0x8150,0xB4AE,0x0083,0x814F,0xB7E1,0x0085,0x0083,0x814E,0x814D,0x0083,0xD6D0,0xE3DC,0x00C1,0x00A1,0x0091,0x0089,
+ 0x0085,0x0083,0xD1BE,0xB8F6,0x0083,0x814C,0xD8AD,0x0085,0x0083,0xC9A5,0x814B,0x0083,0xD1CF,0xC1BD,0x0089,0x0085,
+ 0x0083,0x814A,0xB6AA,0x0083,0x8149,0x8148,0x0085,0x0083,0x8147,0xD8A9,0x0083,0xCBBF,0xB6AB,0x0091,0x0089,0x0085,
+ 0x0083,0xB4D4,0xD2B5,0x0083,0xB1FB,0xC7F0,0x0085,0x0083,0x8146,0xCAC0,0x0083,0xD8A7,0xC7D2,0x0089,0x0085,0x0083,
+ 0xD7A8,0x8145,0x0083,0xB3F3,0xD8A4,0x0085,0x0083,0x8144,0xD3EB,0x0083,0xB2BB,0xD8A2,0x0081,0x0091,0x0089,0x0085,
+ 0x0083,0xCFC2,0xC9CF,0x0083,0xC8FD,0xD5C9,0x0085,0x0083,0xCDF2,0x8143,0x0083,0x8142,0x8141,0x0081,0x0085,0x0083,
+ 0xC6DF,0x8140,0x0083,0xB6A1,0xD2BB,0x05BC,0x0219,0x0082,0x0082,0x00BB,0x00B1,0x0082,0x0096,0x0082,0x008B,0x0082,
+ 0x0085,0x0081,0x0082,0xA954,0x0081,0x0083,0xA953,0xA952,0x0086,0x0081,0x0081,0x0081,0xA951,0x0081,0x0082,0x0081,
+ 0xA950,0x0088,0x0082,0x0082,0x0082,0x0081,0x0082,0xA94F,0x008E,0x0089,0x0081,0x0085,0x0083,0xA94E,0xA94D,0x0081,
+ 0xA94C,0x0082,0x0082,0x0082,0xA94B,0x0081,0x0081,0x0081,0x0081,0xA94A,0x0082,0x0082,0x0081,0x0082,0x0082,0x0082,
+ 0x0081,0x0081,0xA949,0x00A5,0x0081,0x0082,0x009C,0x0087,0x0082,0x0082,0x0082,0x0082,0xA95A,0x0086,0x0082,0x0082,
+ 0x0082,0xA2EE,0x0089,0x0085,0x0083,0xA2ED,0xA2EC,0x0083,0xA2EB,0xA2EA,0x0085,0x0083,0xA2E9,0xA2E8,0x0083,0xA2E7,
+ 0xA2E6,0x0081,0x0081,0x0081,0x0081,0x0081,0xA2E5,0x00D1,0x0082,0x0097,0x0082,0x0086,0x0082,0x0082,0x0082,0xA8E9,
+ 0x0089,0x0085,0x0083,0xA8E8,0xA8E7,0x0083,0xA8E6,0xA8E5,0x0085,0x0083,0xA8E4,0xA8E3,0x0083,0xA8E2,0xA8E1,0x00A1,
+ 0x0091,0x0089,0x0085,0x0083,0xA8E0,0xA8DF,0x0083,0xA8DE,0xA8DD,0x0085,0x0083,0xA8DC,0xA8DB,0x0083,0xA8DA,0xA8D9,
+ 0x0089,0x0085,0x0083,0xA8D8,0xA8D7,0x0083,0xA8D6,0xA8D5,0x0085,0x0083,0xA8D4,0xA8D3,0x0083,0xA8D2,0xA8D1,0x0091,
+ 0x0089,0x0085,0x0083,0xA8D0,0xA8CF,0x0083,0xA8CE,0xA8CD,0x0085,0x0083,0xA8CC,0xA8CB,0x0083,0xA8CA,0xA8C9,0x0081,
+ 0x0085,0x0083,0xA8C8,0xA8C7,0x0083,0xA8C6,0xA8C5,0x00F7,0x00B7,0x0097,0x008A,0x0086,0x0082,0x0083,0xA964,0xA963,
+ 0x0081,0x0081,0xA960,0x0086,0x0082,0x0083,0xA5F6,0xA5F5,0x0085,0x0083,0xA5F4,0xA5F3,0x0083,0xA5F2,0xA5F1,0x0091,
+ 0x0089,0x0085,0x0083,0xA5F0,0xA5EF,0x0083,0xA5EE,0xA5ED,0x0085,0x0083,0xA5EC,0xA5EB,0x0083,0xA5EA,0xA5E9,0x0089,
+ 0x0085,0x0083,0xA5E8,0xA5E7,0x0083,0xA5E6,0xA5E5,0x0085,0x0083,0xA5E4,0xA5E3,0x0083,0xA5E2,0xA5E1,0x00A1,0x0091,
+ 0x0089,0x0085,0x0083,0xA5E0,0xA5DF,0x0083,0xA5DE,0xA5DD,0x0085,0x0083,0xA5DC,0xA5DB,0x0083,0xA5DA,0xA5D9,0x0089,
+ 0x0085,0x0083,0xA5D8,0xA5D7,0x0083,0xA5D6,0xA5D5,0x0085,0x0083,0xA5D4,0xA5D3,0x0083,0xA5D2,0xA5D1,0x0091,0x0089,
+ 0x0085,0x0083,0xA5D0,0xA5CF,0x0083,0xA5CE,0xA5CD,0x0085,0x0083,0xA5CC,0xA5CB,0x0083,0xA5CA,0xA5C9,0x0089,0x0085,
+ 0x0083,0xA5C8,0xA5C7,0x0083,0xA5C6,0xA5C5,0x0085,0x0083,0xA5C4,0xA5C3,0x0083,0xA5C2,0xA5C1,0x00C1,0x00A1,0x0091,
+ 0x0089,0x0085,0x0083,0xA5C0,0xA5BF,0x0083,0xA5BE,0xA5BD,0x0085,0x0083,0xA5BC,0xA5BB,0x0083,0xA5BA,0xA5B9,0x0089,
+ 0x0085,0x0083,0xA5B8,0xA5B7,0x0083,0xA5B6,0xA5B5,0x0085,0x0083,0xA5B4,0xA5B3,0x0083,0xA5B2,0xA5B1,0x0091,0x0089,
+ 0x0085,0x0083,0xA5B0,0xA5AF,0x0083,0xA5AE,0xA5AD,0x0085,0x0083,0xA5AC,0xA5AB,0x0083,0xA5AA,0xA5A9,0x0089,0x0085,
+ 0x0083,0xA5A8,0xA5A7,0x0083,0xA5A6,0xA5A5,0x0085,0x0083,0xA5A4,0xA5A3,0x0083,0xA5A2,0xA5A1,0x0093,0x008B,0x0086,
+ 0x0082,0x0083,0xA967,0xA966,0x0081,0x0083,0xA962,0xA961,0x0082,0x0084,0x0082,0xA4F3,0x0083,0xA4F2,0xA4F1,0x0091,
+ 0x0089,0x0085,0x0083,0xA4F0,0xA4EF,0x0083,0xA4EE,0xA4ED,0x0085,0x0083,0xA4EC,0xA4EB,0x0083,0xA4EA,0xA4E9,0x0089,
+ 0x0085,0x0083,0xA4E8,0xA4E7,0x0083,0xA4E6,0xA4E5,0x0085,0x0083,0xA4E4,0xA4E3,0x0083,0xA4E2,0xA4E1,0x0159,0x0081,
+ 0x0081,0x0081,0x014C,0x00FF,0x00BF,0x009F,0x008F,0x0087,0x0083,0xA4E0,0x0083,0xA4DF,0xA4DE,0x0085,0x0083,0xA4DD,
+ 0xA4DC,0x0083,0xA4DB,0xA4DA,0x0089,0x0085,0x0083,0xA4D9,0xA4D8,0x0083,0xA4D7,0xA4D6,0x0085,0x0083,0xA4D5,0xA4D4,
+ 0x0083,0xA4D3,0xA4D2,0x0091,0x0089,0x0085,0x0083,0xA4D1,0xA4D0,0x0083,0xA4CF,0xA4CE,0x0085,0x0083,0xA4CD,0xA4CC,
+ 0x0083,0xA4CB,0xA4CA,0x0089,0x0085,0x0083,0xA4C9,0xA4C8,0x0083,0xA4C7,0xA4C6,0x0085,0x0083,0xA4C5,0xA4C4,0x0083,
+ 0xA4C3,0xA4C2,0x00A1,0x0091,0x0089,0x0085,0x0083,0xA4C1,0xA4C0,0x0083,0xA4BF,0xA4BE,0x0085,0x0083,0xA4BD,0xA4BC,
+ 0x0083,0xA4BB,0xA4BA,0x0089,0x0085,0x0083,0xA4B9,0xA4B8,0x0083,0xA4B7,0xA4B6,0x0085,0x0083,0xA4B5,0xA4B4,0x0083,
+ 0xA4B3,0xA4B2,0x0091,0x0089,0x0085,0x0083,0xA4B1,0xA4B0,0x0083,0xA4AF,0xA4AE,0x0085,0x0083,0xA4AD,0xA4AC,0x0083,
+ 0xA4AB,0xA4AA,0x0089,0x0085,0x0083,0xA4A9,0xA4A8,0x0083,0xA4A7,0xA4A6,0x0085,0x0083,0xA4A5,0xA4A4,0x0083,0xA4A3,
+ 0xA4A2,0x0098,0x0087,0x0081,0x0081,0x0081,0x0081,0xA4A1,0x0082,0x0089,0x0085,0x0083,0xA948,0xA947,0x0083,0xA946,
+ 0xA945,0x0085,0x0083,0xA944,0xA943,0x0083,0xA942,0xA941,0x0098,0x008B,0x0087,0x0084,0x0081,0xA940,0x0082,0xA895,
+ 0x0081,0x0081,0xA894,0x0086,0x0082,0x0083,0xA1BD,0xA1BC,0x0085,0x0083,0xA1B3,0xA1B2,0x0083,0xA1FE,0xA893,0x0091,
+ 0x0089,0x0085,0x0083,0xA1BF,0xA1BE,0x0083,0xA1BB,0xA1BA,0x0085,0x0083,0xA1B9,0xA1B8,0x0083,0xA1B7,0xA1B6,0x0089,
+ 0x0085,0x0083,0xA1B5,0xA1B4,0x0083,0xA996,0xA965,0x0084,0x0081,0xA1A9,0x0083,0xA1A8,0xA1A3,0x0081,0x0081,0x0081,
+ 0x0081,0x0081,0x0081,0x0083,0xA1A2,0xA1A1,0x0203,0x0082,0x00DD,0x008E,0x0082,0x0081,0x0081,0x0086,0x0082,0x0082,
+ 0x0082,0xA1E1,0x0081,0x0081,0x0082,0xA1E2,0x00A1,0x008D,0x0081,0x0081,0x0087,0x0084,0x0081,0xA891,0x0082,0xA1EE,
+ 0x0081,0x0081,0xA1EF,0x008B,0x0081,0x0082,0x0085,0x0083,0xA890,0xA88F,0x0083,0xA88E,0xA88D,0x0082,0x0086,0x0082,
+ 0x0083,0xA1F1,0xA1F2,0x0082,0x0081,0xA1F0,0x0094,0x008D,0x0087,0x0081,0x0082,0x0083,0xA1F3,0xA1F4,0x0082,0x0081,
+ 0x0083,0xA88C,0xA88B,0x0081,0x0082,0x0082,0x0083,0xA1F7,0xA1F8,0x0088,0x0082,0x0081,0x0081,0x0083,0xA1F5,0xA1F6,
+ 0x0089,0x0082,0x0085,0x0083,0xA88A,0xA889,0x0081,0xA888,0x0086,0x0082,0x0083,0xA887,0xA886,0x0085,0x0083,0xA885,
+ 0xA884,0x0083,0xA883,0xA882,0x0166,0x00E6,0x00AC,0x0096,0x0091,0x0089,0x0085,0x0083,0xA881,0xA880,0x0083,0xA87E,
+ 0xA87D,0x0085,0x0083,0xA87C,0xA87B,0x0083,0xA87A,0xA879,0x0081,0x0081,0x0081,0xA878,0x0087,0x0082,0x0082,0x0083,
+ 0xA877,0xA876,0x0089,0x0085,0x0083,0xA875,0xA874,0x0083,0xA873,0xA872,0x0085,0x0083,0xA871,0xA870,0x0083,0xA86F,
+ 0xA86E,0x00A1,0x0091,0x0089,0x0085,0x0083,0xA86D,0xA86C,0x0083,0xA86B,0xA86A,0x0085,0x0083,0xA869,0xA868,0x0083,
+ 0xA867,0xA866,0x0089,0x0085,0x0083,0xA865,0xA864,0x0083,0xA863,0xA862,0x0085,0x0083,0xA861,0xA860,0x0083,0xA85F,
+ 0xA85E,0x0091,0x0089,0x0085,0x0083,0xA85D,0xA85C,0x0083,0xA85B,0xA85A,0x0085,0x0083,0xA859,0xA858,0x0083,0xA857,
+ 0xA856,0x0086,0x0081,0x0083,0xA855,0xA854,0x0082,0x0083,0xA9EF,0xA9EE,0x00C1,0x00A1,0x0091,0x0089,0x0085,0x0083,
+ 0xA9ED,0xA9EC,0x0083,0xA9EB,0xA9EA,0x0085,0x0083,0xA9E9,0xA9E8,0x0083,0xA9E7,0xA9E6,0x0089,0x0085,0x0083,0xA9E5,
+ 0xA9E4,0x0083,0xA9E3,0xA9E2,0x0085,0x0083,0xA9E1,0xA9E0,0x0083,0xA9DF,0xA9DE,0x0091,0x0089,0x0085,0x0083,0xA9DD,
+ 0xA9DC,0x0083,0xA9DB,0xA9DA,0x0085,0x0083,0xA9D9,0xA9D8,0x0083,0xA9D7,0xA9D6,0x0089,0x0085,0x0083,0xA9D5,0xA9D4,
+ 0x0083,0xA9D3,0xA9D2,0x0085,0x0083,0xA9D1,0xA9D0,0x0083,0xA9CF,0xA9CE,0x00A1,0x0091,0x0089,0x0085,0x0083,0xA9CD,
+ 0xA9CC,0x0083,0xA9CB,0xA9CA,0x0085,0x0083,0xA9C9,0xA9C8,0x0083,0xA9C7,0xA9C6,0x0089,0x0085,0x0083,0xA9C5,0xA9C4,
+ 0x0083,0xA9C3,0xA9C2,0x0085,0x0083,0xA9C1,0xA9C0,0x0083,0xA9BF,0xA9BE,0x0091,0x0089,0x0085,0x0083,0xA9BD,0xA9BC,
+ 0x0083,0xA9BB,0xA9BA,0x0085,0x0083,0xA9B9,0xA9B8,0x0083,0xA9B7,0xA9B6,0x0089,0x0085,0x0083,0xA9B5,0xA9B4,0x0083,
+ 0xA9B3,0xA9B2,0x0085,0x0083,0xA9B1,0xA9B0,0x0083,0xA9AF,0xA9AE,0x0099,0x0081,0x0081,0x0091,0x0089,0x0085,0x0083,
+ 0xA9AD,0xA9AC,0x0083,0xA9AB,0xA9AA,0x0085,0x0083,0xA9A9,0xA9A8,0x0083,0xA9A7,0xA9A6,0x0081,0x0081,0x0083,0xA9A5,
+ 0xA9A4,0x0082,0x0088,0x0082,0x0082,0x0082,0x0083,0xA2C4,0xA2C3,0x0091,0x0089,0x0085,0x0083,0xA2C2,0xA2C1,0x0083,
+ 0xA2C0,0xA2BF,0x0085,0x0083,0xA2BE,0xA2BD,0x0083,0xA2BC,0xA2BB,0x0089,0x0085,0x0083,0xA2BA,0xA2B9,0x0083,0xA2B8,
+ 0xA2B7,0x0085,0x0083,0xA2B6,0xA2B5,0x0083,0xA2B4,0xA2B3,0x00E9,0x00C9,0x0081,0x0081,0x00AF,0x009F,0x008F,0x0087,
+ 0x0083,0xA2B2,0x0083,0xA2B1,0xA2D8,0x0085,0x0083,0xA2D7,0xA2D6,0x0083,0xA2D5,0xA2D4,0x0089,0x0085,0x0083,0xA2D3,
+ 0xA2D2,0x0083,0xA2D1,0xA2D0,0x0085,0x0083,0xA2CF,0xA2CE,0x0083,0xA2CD,0xA2CC,0x0081,0x0089,0x0085,0x0083,0xA2CB,
+ 0xA2CA,0x0083,0xA2C9,0xA2C8,0x0085,0x0083,0xA2C7,0xA2C6,0x0081,0xA2C5,0x0081,0x0090,0x0088,0x0084,0x0082,0xA2E2,
+ 0x0083,0xA2E1,0xA2E0,0x0085,0x0083,0xA2DF,0xA2DE,0x0083,0xA2DD,0xA2DC,0x0081,0x0085,0x0083,0xA2DB,0xA2DA,0x0081,
+ 0xA2D9,0x008A,0x0082,0x0082,0x0082,0x0082,0x0081,0x0081,0x0082,0xA1D0,0x0082,0x0088,0x0081,0x0082,0x0082,0x0081,
+ 0x0081,0xA853,0x0087,0x0081,0x0082,0x0082,0x0081,0xA1CD,0x0081,0x0085,0x0082,0x0081,0xA1D1,0x0082,0x0081,0xA892,
+ 0x0109,0x00E9,0x00A2,0x008A,0x0082,0x0082,0x0082,0x0084,0x0082,0xA1DB,0x0081,0xA1DA,0x0093,0x0089,0x0082,0x0084,
+ 0x0082,0xA852,0x0083,0xA851,0xA1DD,0x0087,0x0084,0x0081,0xA1DC,0x0082,0xA1D4,0x0081,0x0081,0xA1D9,0x0082,0x0081,
+ 0x0082,0x0081,0xA850,0x009E,0x008E,0x0089,0x0085,0x0081,0x0081,0xA1D5,0x0081,0x0081,0xA1D6,0x0082,0x0082,0x0082,
+ 0xA1D7,0x0089,0x0082,0x0084,0x0082,0xA1CB,0x0083,0xA1C3,0xA1DF,0x0085,0x0081,0x0081,0xA1E0,0x0082,0x0081,0xA1D3,
+ 0x009B,0x008F,0x0088,0x0084,0x0082,0xA1D2,0x0083,0xA1C8,0xA1C9,0x0085,0x0083,0xA1C5,0xA1C4,0x0082,0xA1CE,0x0085,
+ 0x0081,0x0082,0xA84F,0x0085,0x0083,0xA1CF,0xA84E,0x0083,0xA1DE,0xA1D8,0x0089,0x0085,0x0082,0x0081,0xA1CC,0x0082,
+ 0x0082,0xA84D,0x0085,0x0082,0x0082,0xA1C6,0x0081,0x0082,0xA1C7,0x0089,0x0081,0x0081,0x0081,0x0082,0x0081,0x0081,
+ 0xA1CA,0x0082,0x0082,0x008C,0x0085,0x0082,0x0082,0xA84C,0x0085,0x0083,0xA84B,0xA84A,0x0081,0xA849,0x0088,0x0084,
+ 0x0082,0xA1FD,0x0083,0xA1FA,0xA1FC,0x0081,0x0081,0xA1FB,0x00C3,0x00B6,0x009A,0x0082,0x008D,0x0085,0x0082,0x0082,
+ 0xA2AA,0x0085,0x0083,0xA2A9,0xA2A8,0x0083,0xA2A7,0xA2A6,0x0089,0x0085,0x0083,0xA2A5,0xA2A4,0x0083,0xA2A3,0xA2A2,
+ 0x0081,0x0081,0xA2A1,0x0081,0x0090,0x0088,0x0084,0x0082,0xA2FC,0x0083,0xA2FB,0xA2FA,0x0085,0x0083,0xA2F9,0xA2F8,
+ 0x0083,0xA2F7,0xA2F6,0x0089,0x0085,0x0083,0xA2F5,0xA2F4,0x0083,0xA2F3,0xA2F2,0x0081,0x0081,0xA2F1,0x0082,0x0087,
+ 0x0082,0x0081,0x0082,0x0082,0xA959,0x0081,0x0082,0x0082,0x0081,0xA1ED,0x0090,0x0081,0x0081,0x0089,0x0085,0x0082,
+ 0x0082,0xA848,0x0082,0x0082,0xA847,0x0081,0x0081,0x0082,0xA1E6,0x0082,0x0081,0x0081,0x0081,0x0081,0x0081,0x0080,
+ 0x00B9,0x0081,0x0081,0x0081,0x0081,0x0081,0x0082,0x0092,0x0082,0x0086,0x0081,0x0082,0x0081,0xA1F9,0x0088,0x0084,
+ 0x0081,0xA846,0x0083,0xA1E5,0xA1E4,0x0081,0x0082,0xA1EB,0x008B,0x0086,0x0082,0x0082,0x0082,0xA1AD,0x0081,0x0081,
+ 0x0081,0xA845,0x008D,0x0086,0x0081,0x0083,0xA1B1,0xA1B0,0x0085,0x0083,0xA1AF,0xA1AE,0x0082,0xA1AC,0x0088,0x0085,
+ 0x0083,0xA844,0xA1AA,0x0081,0xA843,0x0081,0x0082,0xA95C,0x0082,0x0082,0x0193,0x0156,0x00E1,0x0082,0x00A0,0x0082,
+ 0x008F,0x0087,0x0084,0x0082,0xA7D7,0x0082,0xA7F1,0x0085,0x0083,0xA7F0,0xA7EF,0x0083,0xA7EE,0xA7ED,0x0089,0x0085,
+ 0x0083,0xA7EC,0xA7EB,0x0083,0xA7EA,0xA7E9,0x0085,0x0083,0xA7E8,0xA7E7,0x0083,0xA7E6,0xA7E5,0x00A1,0x0091,0x0089,
+ 0x0085,0x0083,0xA7E4,0xA7E3,0x0083,0xA7E2,0xA7E1,0x0085,0x0083,0xA7E0,0xA7DF,0x0083,0xA7DE,0xA7DD,0x0089,0x0085,
+ 0x0083,0xA7DC,0xA7DB,0x0083,0xA7DA,0xA7D9,0x0085,0x0083,0xA7D8,0xA7D6,0x0083,0xA7D5,0xA7D4,0x0091,0x0089,0x0085,
+ 0x0083,0xA7D3,0xA7D2,0x0083,0xA7D1,0xA7C1,0x0085,0x0083,0xA7C0,0xA7BF,0x0083,0xA7BE,0xA7BD,0x0089,0x0085,0x0083,
+ 0xA7BC,0xA7BB,0x0083,0xA7BA,0xA7B9,0x0085,0x0083,0xA7B8,0xA7B7,0x0083,0xA7B6,0xA7B5,0x00B1,0x00AA,0x00A1,0x0091,
+ 0x0089,0x0085,0x0083,0xA7B4,0xA7B3,0x0083,0xA7B2,0xA7B1,0x0085,0x0083,0xA7B0,0xA7AF,0x0083,0xA7AE,0xA7AD,0x0089,
+ 0x0085,0x0083,0xA7AC,0xA7AB,0x0083,0xA7AA,0xA7A9,0x0085,0x0083,0xA7A8,0xA7A6,0x0083,0xA7A5,0xA7A4,0x0081,0x0081,
+ 0x0085,0x0083,0xA7A3,0xA7A2,0x0081,0xA7A1,0x0081,0x0081,0x0081,0x0081,0x0082,0xA7A7,0x0092,0x0082,0x0082,0x0088,
+ 0x0084,0x0082,0xA6D8,0x0083,0xA6D7,0xA6D6,0x0085,0x0083,0xA6D5,0xA6D4,0x0083,0xA6D3,0xA6D2,0x00A0,0x0090,0x0088,
+ 0x0084,0x0082,0xA6D1,0x0083,0xA6D0,0xA6CF,0x0085,0x0083,0xA6CE,0xA6CD,0x0083,0xA6CC,0xA6CB,0x0089,0x0085,0x0083,
+ 0xA6CA,0xA6C9,0x0083,0xA6C8,0xA6C7,0x0085,0x0083,0xA6C6,0xA6C5,0x0083,0xA6C4,0xA6C3,0x0087,0x0081,0x0081,0x0083,
+ 0xA6C2,0xA6C1,0x0088,0x0084,0x0082,0xA6B8,0x0083,0xA6B7,0xA6B6,0x0085,0x0083,0xA6B5,0xA6B4,0x0083,0xA6B3,0xA6B2,
+ 0x00A9,0x0081,0x0081,0x00A0,0x0090,0x0088,0x0084,0x0082,0xA6B1,0x0083,0xA6B0,0xA6AF,0x0085,0x0083,0xA6AE,0xA6AD,
+ 0x0083,0xA6AC,0xA6AB,0x0089,0x0085,0x0083,0xA6AA,0xA6A9,0x0083,0xA6A8,0xA6A7,0x0085,0x0083,0xA6A6,0xA6A5,0x0083,
+ 0xA6A4,0xA6A3,0x0081,0x0081,0x0081,0x0083,0xA6A2,0xA6A1,0x0082,0x0081,0x0087,0x0082,0x0081,0x0081,0x0082,0xA842,
+ 0x0086,0x0082,0x0082,0x0082,0xA841,0x0081,0x0085,0x0083,0xA840,0xA1A5,0x0082,0xA1A6,0x00AD,0x008F,0x0082,0x0081,
+ 0x0087,0x0081,0x0081,0x0082,0x0081,0xA8C0,0x0081,0x0081,0x0082,0x0081,0xA8BB,0x0082,0x0081,0x0092,0x0086,0x0082,
+ 0x0082,0x0082,0xA8B8,0x0087,0x0084,0x0082,0xA8B7,0x0082,0xA8B6,0x0084,0x0082,0xA8B5,0x0082,0xA8B3,0x0081,0x0087,
+ 0x0084,0x0082,0xA8AF,0x0082,0xA8AB,0x0081,0x0082,0xA8A3,0x009E,0x0089,0x0082,0x0082,0x0082,0x0081,0x0081,0x0081,
+ 0xA8B1,0x008F,0x0082,0x0086,0x0082,0x0082,0x0081,0xA8AD,0x0085,0x0082,0x0082,0xA8BE,0x0082,0x0082,0xA8BD,0x0082,
+ 0x0082,0x0081,0x0081,0x0081,0xA8A9,0x00B1,0x008D,0x0087,0x0082,0x0081,0x0081,0x0081,0xA8A7,0x0081,0x0081,0x0081,
+ 0x0081,0xA8A5,0x0093,0x0089,0x0085,0x0082,0x0081,0xA8A1,0x0082,0x0082,0xA8B9,0x0087,0x0084,0x0082,0xA8B2,0x0081,
+ 0xA8B4,0x0081,0x0081,0xA1C2,0x008B,0x0086,0x0081,0x0083,0xA8AE,0xA8B0,0x0082,0x0083,0xA8AA,0xA8AC,0x0081,0x0084,
+ 0x0082,0xA8BA,0x0083,0xA8A6,0xA8A8,0x008D,0x0081,0x0087,0x0081,0x0082,0x0083,0xA8A2,0xA8A4,0x0082,0x0081,0x0081,
+ 0xA1C1,0x0087,0x0082,0x0082,0x0081,0x0081,0xA1A4,0x0087,0x0081,0x0082,0x0083,0xA1C0,0xA1E3,0x0085,0x0082,0x0082,
+ 0xA1A7,0x0084,0x0081,0xA1EC,0x0082,0xA1E8,
+}; // ucs2_to_cp936_elements
+
+const TConvFlatTree ucs2_to_cp936 = {
+ 0x00A4, // min key
+ 0xFFE5, // max key
+ 0x0081, // links space start
+ 0x7FFF, // links space end
+ ucs2_to_cp936_numelems, // number of elements
+ (treeval_t *)&ucs2_to_cp936_elements // elements
+}; // ucs2_to_cp936
+
+
+const size_t cp936_to_ucs2_numelems = 44536;
+
+const treeval_t cp936_to_ucs2_elements[cp936_to_ucs2_numelems] = {
+ 0xEFFE,0xADE7,0xEFFE,0x5B47,0xCB80,0xB38E,0xA796,0xA310,0xA188,0xA0C4,0xA026,0xA000,0xA000,0xA01E,0xA00E,0xA006,
+ 0xA002,0xFA29,0xA002,0xFA28,0xFA27,0xA004,0xA002,0xFA24,0xFA23,0xA002,0xFA21,0xFA20,0xA008,0xA004,0xA002,0xFA1F,
+ 0xFA18,0xA002,0xFA14,0xFA13,0xA004,0xA002,0xFA11,0xFA0F,0xA002,0xFA0E,0xFA0D,0xA000,0xA000,0xA000,0xA000,0xFA0C,
+ 0xA01F,0xA001,0xA001,0xA00D,0xA005,0xA001,0xA002,0xF9F1,0xF9E7,0xA004,0xA002,0xF995,0xF979,0xA002,0xF92C,0x9FA5,
+ 0xA008,0xA004,0xA002,0x9FA4,0x9FA3,0xA002,0x9FA2,0x9FA1,0xA004,0xA002,0x9F9E,0x9F9D,0xA002,0x9F9C,0x9F98,0xA03F,
+ 0xA020,0xA010,0xA008,0xA004,0xA002,0x9F97,0x9F96,0xA002,0x9F95,0x9F94,0xA004,0xA002,0x9F93,0x9F92,0xA002,0x9F91,
+ 0x9F90,0xA008,0xA004,0xA002,0x9F8F,0x9F8E,0xA002,0x9F8D,0x9F82,0xA004,0xA002,0x9F81,0x9F7E,0xA002,0x9F7D,0x9F7C,
+ 0xA00F,0xA007,0xA004,0xA002,0x9F7B,0x9F7A,0xA000,0x9F79,0xA004,0xA002,0x9F78,0x9F77,0xA002,0x9F76,0x9F75,0xA008,
+ 0xA004,0xA002,0x9F74,0x9F73,0xA002,0x9F72,0x9F71,0xA004,0xA002,0x9F70,0x9F6F,0xA002,0x9F6E,0x9F6D,0xA020,0xA010,
+ 0xA008,0xA004,0xA002,0x9F6C,0x9F6B,0xA002,0x9F6A,0x9F69,0xA004,0xA002,0x9F68,0x9F67,0xA002,0x9F66,0x9F65,0xA008,
+ 0xA004,0xA002,0x9F64,0x9F63,0xA002,0x9F62,0x9F61,0xA004,0xA002,0x9F60,0x9F5F,0xA002,0x9F5E,0x9F5D,0xA010,0xA008,
+ 0xA004,0xA002,0x9F5C,0x9F5B,0xA002,0x9F5A,0x9F59,0xA004,0xA002,0x9F58,0x9F57,0xA002,0x9F56,0x9F55,0xA008,0xA004,
+ 0xA002,0x9F54,0x9F53,0xA002,0x9F52,0x9F4F,0xA004,0xA002,0x9F4E,0x9F4D,0xA002,0x9F4C,0x9F4B,0xA02A,0xA000,0xA000,
+ 0xA01E,0xA00E,0xA006,0xA002,0x9F4A,0xA002,0x9F49,0x9F48,0xA004,0xA002,0x9F47,0x9F46,0xA002,0x9F45,0x9F43,0xA008,
+ 0xA004,0xA002,0x9F42,0x9F41,0xA002,0x9F40,0x9F3F,0xA004,0xA002,0x9F3C,0x9F3A,0xA002,0x9F38,0x9F36,0xA000,0xA000,
+ 0xA004,0xA002,0x9F35,0x9F34,0xA002,0x9F33,0x9F32,0xA01B,0xA001,0xA001,0xA009,0xA001,0xA004,0xA002,0x9F31,0x9F30,
+ 0xA002,0x9F2E,0x9F2D,0xA008,0xA004,0xA002,0x9F2B,0x9F2A,0xA002,0x9F29,0x9F28,0xA004,0xA002,0x9F27,0x9F26,0xA002,
+ 0x9F25,0x9F24,0xA03F,0xA020,0xA010,0xA008,0xA004,0xA002,0x9F23,0x9F21,0xA002,0x9F1F,0x9F1E,0xA004,0xA002,0x9F1D,
+ 0x9F1C,0xA002,0x9F1B,0x9F1A,0xA008,0xA004,0xA002,0x9F18,0x9F16,0xA002,0x9F15,0x9F14,0xA004,0xA002,0x9F12,0x9F11,
+ 0xA002,0x9F0F,0x9F0C,0xA00F,0xA008,0xA004,0xA002,0x9F0A,0x9F09,0xA002,0x9F08,0x9F07,0xA003,0xA000,0x9F06,0xA002,
+ 0x9F05,0x9F04,0xA008,0xA004,0xA002,0x9F03,0x9F02,0xA002,0x9F01,0x9F00,0xA004,0xA002,0x9EFF,0x9EFD,0xA002,0x9EFA,
+ 0x9EF8,0xA020,0xA010,0xA008,0xA004,0xA002,0x9EF7,0x9EF6,0xA002,0x9EF5,0x9EF4,0xA004,0xA002,0x9EF3,0x9EF2,0xA002,
+ 0x9EF1,0x9EF0,0xA008,0xA004,0xA002,0x9EEE,0x9EED,0xA002,0x9EEC,0x9EEB,0xA004,0xA002,0x9EE8,0x9EE6,0xA002,0x9EE4,
+ 0x9EE3,0xA010,0xA008,0xA004,0xA002,0x9EE1,0x9EDE,0xA002,0x9EDA,0x9ED9,0xA004,0xA002,0x9ED7,0x9ED6,0xA002,0x9ED5,
+ 0x9ED3,0xA008,0xA004,0xA002,0x9ED2,0x9ED0,0xA002,0x9ECC,0x9ECB,0xA004,0xA002,0x9ECA,0x9EC8,0xA002,0x9EC7,0x9EC6,
+ 0xA0C5,0xA02E,0xA000,0xA000,0xA01E,0xA00E,0xA006,0xA002,0x9EC5,0xA002,0x9EC3,0x9EC2,0xA004,0xA002,0x9EC1,0x9EC0,
+ 0xA002,0x9EBF,0x9EBC,0xA008,0xA004,0xA002,0x9EBA,0x9EB9,0xA002,0x9EB7,0x9EB6,0xA004,0xA002,0x9EB5,0x9EB3,0xA002,
+ 0x9EB2,0x9EB1,0xA000,0xA008,0xA004,0xA002,0x9EB0,0x9EAF,0xA002,0x9EAE,0x9EAD,0xA000,0xA002,0x9EAC,0x9EAB,0xA018,
+ 0xA001,0xA001,0xA006,0xA001,0xA001,0xA002,0x9EAA,0x9EA9,0xA008,0xA004,0xA002,0x9EA8,0x9EA7,0xA002,0x9EA5,0x9EA4,
+ 0xA004,0xA002,0x9EA3,0x9EA2,0xA002,0x9EA1,0x9EA0,0xA03F,0xA020,0xA010,0xA008,0xA004,0xA002,0x9E9E,0x9E9C,0xA002,
+ 0x9E9B,0x9E9A,0xA004,0xA002,0x9E99,0x9E98,0xA002,0x9E97,0x9E96,0xA008,0xA004,0xA002,0x9E95,0x9E94,0xA002,0x9E91,
+ 0x9E90,0xA004,0xA002,0x9E8F,0x9E8E,0xA002,0x9E8D,0x9E8C,0xA00F,0xA008,0xA004,0xA002,0x9E8A,0x9E89,0xA002,0x9E86,
+ 0x9E85,0xA004,0xA002,0x9E84,0x9E83,0xA000,0x9E81,0xA008,0xA004,0xA002,0x9E80,0x9E7D,0xA002,0x9E7C,0x9E7B,0xA004,
+ 0xA002,0x9E7A,0x9E79,0xA002,0x9E78,0x9E77,0xA020,0xA010,0xA008,0xA004,0xA002,0x9E76,0x9E75,0xA002,0x9E74,0x9E72,
+ 0xA004,0xA002,0x9E6F,0x9E6E,0xA002,0x9E65,0x9E62,0xA008,0xA004,0xA002,0x9E61,0x9E60,0xA002,0x9E5F,0x9E5D,0xA004,
+ 0xA002,0x9E59,0x9E56,0xA002,0x9E54,0x9E53,0xA010,0xA008,0xA004,0xA002,0x9E52,0x9E50,0xA002,0x9E4D,0x9E40,0xA004,
+ 0xA002,0x9E3C,0x9E3B,0xA002,0x9E34,0x9E30,0xA008,0xA004,0xA002,0x9E2E,0x9E27,0xA002,0x9E24,0x9E1E,0xA004,0xA002,
+ 0x9E1D,0x9E1C,0xA002,0x9E1B,0x9E1A,0xA031,0xA000,0xA000,0xA01E,0xA00E,0xA006,0xA002,0x9E19,0xA002,0x9E18,0x9E17,
+ 0xA004,0xA002,0x9E16,0x9E15,0xA002,0x9E14,0x9E13,0xA008,0xA004,0xA002,0x9E12,0x9E11,0xA002,0x9E10,0x9E0F,0xA004,
+ 0xA002,0x9E0E,0x9E0D,0xA002,0x9E0C,0x9E0B,0xA000,0xA008,0xA004,0xA002,0x9E0A,0x9E09,0xA002,0x9E08,0x9E07,0xA004,
+ 0xA002,0x9E06,0x9E05,0xA002,0x9E04,0x9E03,0xA013,0xA001,0xA001,0xA001,0xA008,0xA004,0xA002,0x9E02,0x9E01,0xA002,
+ 0x9E00,0x9DFF,0xA004,0xA002,0x9DFE,0x9DFD,0xA002,0x9DFC,0x9DFB,0xA03F,0xA020,0xA010,0xA008,0xA004,0xA002,0x9DFA,
+ 0x9DF9,0xA002,0x9DF8,0x9DF7,0xA004,0xA002,0x9DF6,0x9DF5,0xA002,0x9DF4,0x9DF3,0xA008,0xA004,0xA002,0x9DF2,0x9DF1,
+ 0xA002,0x9DF0,0x9DEF,0xA004,0xA002,0x9DEE,0x9DED,0xA002,0x9DEC,0x9DEB,0xA010,0xA008,0xA004,0xA002,0x9DEA,0x9DE9,
+ 0xA002,0x9DE8,0x9DE7,0xA004,0xA002,0x9DE6,0x9DE5,0xA002,0x9DE4,0x9DE3,0xA007,0xA003,0xA000,0x9DE2,0xA002,0x9DE1,
+ 0x9DE0,0xA004,0xA002,0x9DDF,0x9DDE,0xA002,0x9DDD,0x9DDC,0xA020,0xA010,0xA008,0xA004,0xA002,0x9DDB,0x9DDA,0xA002,
+ 0x9DD9,0x9DD8,0xA004,0xA002,0x9DD7,0x9DD6,0xA002,0x9DD5,0x9DD4,0xA008,0xA004,0xA002,0x9DD3,0x9DD2,0xA002,0x9DD1,
+ 0x9DD0,0xA004,0xA002,0x9DCF,0x9DCE,0xA002,0x9DCD,0x9DCC,0xA010,0xA008,0xA004,0xA002,0x9DCB,0x9DCA,0xA002,0x9DC9,
+ 0x9DC8,0xA004,0xA002,0x9DC7,0x9DC6,0xA002,0x9DC5,0x9DC4,0xA008,0xA004,0xA002,0x9DC3,0x9DC2,0xA002,0x9DC1,0x9DC0,
+ 0xA004,0xA002,0x9DBF,0x9DBE,0xA002,0x9DBD,0x9DBC,0xA18A,0xA0C4,0xA036,0xA000,0xA000,0xA01E,0xA00E,0xA006,0xA002,
+ 0x9DBB,0xA002,0x9DBA,0x9DB9,0xA004,0xA002,0x9DB8,0x9DB7,0xA002,0x9DB6,0x9DB5,0xA008,0xA004,0xA002,0x9DB4,0x9DB3,
+ 0xA002,0x9DB2,0x9DB1,0xA004,0xA002,0x9DB0,0x9DAF,0xA002,0x9DAE,0x9DAD,0xA010,0xA008,0xA004,0xA002,0x9DAC,0x9DAB,
+ 0xA002,0x9DAA,0x9DA9,0xA004,0xA002,0x9DA8,0x9DA7,0xA002,0x9DA6,0x9DA5,0xA000,0xA000,0xA002,0x9DA4,0x9DA3,0xA00F,
+ 0xA001,0xA001,0xA001,0xA004,0xA001,0xA001,0x9DA2,0xA004,0xA002,0x9DA1,0x9DA0,0xA002,0x9D9F,0x9D9E,0xA03F,0xA020,
+ 0xA010,0xA008,0xA004,0xA002,0x9D9D,0x9D9C,0xA002,0x9D9B,0x9D9A,0xA004,0xA002,0x9D99,0x9D98,0xA002,0x9D97,0x9D96,
+ 0xA008,0xA004,0xA002,0x9D95,0x9D94,0xA002,0x9D93,0x9D92,0xA004,0xA002,0x9D91,0x9D90,0xA002,0x9D8F,0x9D8E,0xA010,
+ 0xA008,0xA004,0xA002,0x9D8D,0x9D8C,0xA002,0x9D8B,0x9D8A,0xA004,0xA002,0x9D89,0x9D88,0xA002,0x9D87,0x9D86,0xA008,
+ 0xA004,0xA002,0x9D85,0x9D84,0xA002,0x9D83,0x9D82,0xA003,0xA001,0x9D81,0xA002,0x9D80,0x9D7F,0xA020,0xA010,0xA008,
+ 0xA004,0xA002,0x9D7E,0x9D7D,0xA002,0x9D7C,0x9D7B,0xA004,0xA002,0x9D7A,0x9D79,0xA002,0x9D78,0x9D77,0xA008,0xA004,
+ 0xA002,0x9D76,0x9D75,0xA002,0x9D74,0x9D73,0xA004,0xA002,0x9D72,0x9D71,0xA002,0x9D70,0x9D6F,0xA010,0xA008,0xA004,
+ 0xA002,0x9D6E,0x9D6D,0xA002,0x9D6C,0x9D6B,0xA004,0xA002,0x9D6A,0x9D69,0xA002,0x9D68,0x9D67,0xA008,0xA004,0xA002,
+ 0x9D66,0x9D65,0xA002,0x9D64,0x9D63,0xA004,0xA002,0x9D62,0x9D61,0xA002,0x9D60,0x9D5F,0xA03C,0xA000,0xA000,0xA01E,
+ 0xA00E,0xA006,0xA002,0x9D5E,0xA002,0x9D5D,0x9D5C,0xA004,0xA002,0x9D5B,0x9D5A,0xA002,0x9D59,0x9D58,0xA008,0xA004,
+ 0xA002,0x9D57,0x9D56,0xA002,0x9D55,0x9D54,0xA004,0xA002,0x9D53,0x9D52,0xA002,0x9D51,0x9D50,0xA010,0xA008,0xA004,
+ 0xA002,0x9D4F,0x9D4E,0xA002,0x9D4D,0x9D4C,0xA004,0xA002,0x9D4B,0x9D4A,0xA002,0x9D49,0x9D48,0xA008,0xA004,0xA002,
+ 0x9D47,0x9D46,0xA002,0x9D45,0x9D44,0xA000,0xA000,0x9D43,0xA00B,0xA001,0xA001,0xA001,0xA001,0xA003,0xA001,0x9D42,
+ 0xA002,0x9D41,0x9D40,0xA03F,0xA020,0xA010,0xA008,0xA004,0xA002,0x9D3F,0x9D3E,0xA002,0x9D3D,0x9D3C,0xA004,0xA002,
+ 0x9D3B,0x9D3A,0xA002,0x9D39,0x9D38,0xA008,0xA004,0xA002,0x9D37,0x9D36,0xA002,0x9D35,0x9D34,0xA004,0xA002,0x9D33,
+ 0x9D32,0xA002,0x9D31,0x9D30,0xA010,0xA008,0xA004,0xA002,0x9D2F,0x9D2E,0xA002,0x9D2D,0x9D2C,0xA004,0xA002,0x9D2B,
+ 0x9D2A,0xA002,0x9D29,0x9D28,0xA008,0xA004,0xA002,0x9D27,0x9D26,0xA002,0x9D25,0x9D24,0xA004,0xA002,0x9D23,0x9D22,
+ 0xA001,0x9D21,0xA020,0xA010,0xA008,0xA004,0xA002,0x9D20,0x9D1F,0xA002,0x9D1E,0x9D1D,0xA004,0xA002,0x9D1C,0x9D1B,
+ 0xA002,0x9D1A,0x9D19,0xA008,0xA004,0xA002,0x9D18,0x9D17,0xA002,0x9D16,0x9D15,0xA004,0xA002,0x9D14,0x9D13,0xA002,
+ 0x9D12,0x9D11,0xA010,0xA008,0xA004,0xA002,0x9D10,0x9D0F,0xA002,0x9D0E,0x9D0D,0xA004,0xA002,0x9D0C,0x9D0B,0xA002,
+ 0x9D0A,0x9D09,0xA008,0xA004,0xA002,0x9D08,0x9D07,0xA002,0x9D06,0x9D05,0xA004,0xA002,0x9D04,0x9D03,0xA002,0x9D02,
+ 0x9D01,0xA17C,0xA07F,0xA03E,0xA000,0xA01E,0xA00E,0xA006,0xA002,0x9D00,0xA002,0x9CFF,0x9CFE,0xA004,0xA002,0x9CFD,
+ 0x9CFC,0xA002,0x9CFB,0x9CFA,0xA008,0xA004,0xA002,0x9CF9,0x9CF8,0xA002,0x9CF7,0x9CF6,0xA004,0xA002,0x9CF5,0x9CF4,
+ 0xA002,0x9CF3,0x9CF2,0xA010,0xA008,0xA004,0xA002,0x9CF1,0x9CF0,0xA002,0x9CEF,0x9CEE,0xA004,0xA002,0x9CED,0x9CEC,
+ 0xA002,0x9CEB,0x9CEA,0xA008,0xA004,0xA002,0x9CE9,0x9CE8,0xA002,0x9CE7,0x9CE6,0xA004,0xA002,0x9CE5,0x9CE4,0xA000,
+ 0x9CE3,0xA001,0xA020,0xA010,0xA008,0xA004,0xA002,0x9F44,0x9F3E,0xA002,0x9F3D,0x9F37,0xA004,0xA002,0x9F39,0x9F2F,
+ 0xA002,0x9F2C,0x9F22,0xA008,0xA004,0xA002,0x9EEF,0x9EEA,0xA002,0x9EE5,0x9EE7,0xA004,0xA002,0x9EE9,0x9EE2,0xA002,
+ 0x9EDF,0x9EE0,0xA010,0xA008,0xA004,0xA002,0x9EDD,0x9EDC,0xA002,0x9EDB,0x9E9F,0xA004,0xA002,0x9E9D,0x93D6,0xA002,
+ 0x9E92,0x9E8B,0xA008,0xA004,0xA002,0x9E88,0x9E87,0xA002,0x9E82,0x7E3B,0xA004,0xA002,0x9EBE,0x9EBD,0xA002,0x9B23,
+ 0x9B1F,0xA07E,0xA03E,0xA01E,0xA00E,0xA006,0xA002,0x9B13,0xA002,0x9B0F,0x9B08,0xA004,0xA002,0x9AF9,0x9AED,0xA002,
+ 0x9AFB,0x9AEB,0xA008,0xA004,0xA002,0x9AEF,0x9AE6,0xA002,0x9AE1,0x9ADF,0xA004,0xA002,0x9954,0x9955,0xA002,0x992E,
+ 0x990D,0xA010,0xA008,0xA004,0xA002,0x98E8,0x9B51,0xA002,0x9B4D,0x9B48,0xA004,0xA002,0x9B49,0x9B47,0xA002,0x9B43,
+ 0x9B45,0xA008,0xA004,0xA002,0x9AD1,0x9ACC,0xA002,0x9ACB,0x9AC2,0xA004,0xA002,0x9AC5,0x9AC0,0xA002,0x9AC1,0x9ABC,
+ 0xA020,0xA010,0xA008,0xA004,0xA002,0x9ABA,0x9AB6,0xA002,0x9E58,0x9AB7,0xA004,0xA002,0x9AB0,0x9AB1,0xA002,0x97B4,
+ 0x97B2,0xA008,0xA004,0xA002,0x97A3,0x97AB,0xA002,0x97AF,0x9794,0xA004,0xA002,0x9792,0x9791,0xA002,0x9785,0x977C,
+ 0xA010,0xA008,0xA004,0xA002,0x9CE2,0x9CDF,0xA002,0x9CDD,0x9CDC,0xA004,0xA002,0x9CD9,0x9CD8,0xA002,0x9CD7,0x9CD5,
+ 0xA008,0xA004,0xA002,0x9CD4,0x9CD3,0xA002,0x9CD0,0x9CCF,0xA004,0xA002,0x9CCE,0x9CCD,0xA002,0x9CCC,0x9CE1,0xA040,
+ 0xA020,0xA010,0xA008,0xA004,0xA002,0x9CE0,0x9CDB,0xA002,0x9CDA,0x9CD2,0xA004,0xA002,0x9CD1,0x9CC9,0xA002,0x9CC8,
+ 0x9CC2,0xA008,0xA004,0xA002,0x9CC1,0x9CC0,0xA002,0x9CBF,0x9CBE,0xA004,0xA002,0x9CB9,0x9CAF,0xA002,0x9CAC,0x9CAA,
+ 0xA010,0xA008,0xA004,0xA002,0x9C9D,0x9C99,0xA002,0x9C98,0x9C97,0xA004,0xA002,0x9C96,0x9C93,0xA002,0x9C8F,0x9C8C,
+ 0xA008,0xA004,0xA002,0x9C8A,0x9C89,0xA002,0x9C84,0x9C83,0xA004,0xA002,0x9C80,0x9C7E,0xA002,0x9C7D,0x9C7B,0xA01F,
+ 0xA00F,0xA007,0xA003,0xA001,0x9C7A,0xA002,0x9C79,0x9C78,0xA004,0xA002,0x9C77,0x9C76,0xA002,0x9C75,0x9C74,0xA008,
+ 0xA004,0xA002,0x9C73,0x9C72,0xA002,0x9C71,0x9C70,0xA004,0xA002,0x9C6F,0x9C6E,0xA002,0x9C6D,0x9C6C,0xA010,0xA008,
+ 0xA004,0xA002,0x9C6B,0x9C6A,0xA002,0x9C69,0x9C68,0xA004,0xA002,0x9C67,0x9C66,0xA002,0x9C65,0x9C64,0xA008,0xA004,
+ 0xA002,0x9C63,0x9C62,0xA002,0x9C61,0x9C60,0xA004,0xA002,0x9C5F,0x9C5E,0xA002,0x9C5D,0x9C5C,0xA083,0xA045,0xA03E,
+ 0xA01E,0xA00E,0xA006,0xA002,0x9C5B,0xA002,0x9C5A,0x9C59,0xA004,0xA002,0x9C58,0x9C57,0xA002,0x9C56,0x9C55,0xA008,
+ 0xA004,0xA002,0x9C54,0x9C53,0xA002,0x9C52,0x9C51,0xA004,0xA002,0x9C50,0x9C4F,0xA002,0x9C4E,0x9C4D,0xA010,0xA008,
+ 0xA004,0xA002,0x9C4C,0x9C4B,0xA002,0x9C4A,0x9C49,0xA004,0xA002,0x9C48,0x9C47,0xA002,0x9C46,0x9C45,0xA008,0xA004,
+ 0xA002,0x9C44,0x9C43,0xA002,0x9C42,0x9C41,0xA004,0xA002,0x9C40,0x9C3F,0xA002,0x9C3E,0x9C3D,0xA000,0xA000,0xA000,
+ 0xA000,0xA000,0x9C3C,0xA001,0xA01D,0xA00D,0xA005,0xA001,0xA002,0x9CCB,0x9CCA,0xA004,0xA002,0x9CC7,0x9CC6,0xA002,
+ 0x9CC5,0x9CC4,0xA008,0xA004,0xA002,0x9CBD,0x9CBC,0xA002,0x9CBB,0x9CBA,0xA004,0xA002,0x9CB7,0x9CB6,0xA002,0x9CB5,
+ 0x9CB4,0xA010,0xA008,0xA004,0xA002,0x9CB3,0x9CB2,0xA002,0x9CB1,0x9CB0,0xA004,0xA002,0x9CAE,0x9CAD,0xA002,0x9CAB,
+ 0x9CA9,0xA008,0xA004,0xA002,0x9CA8,0x9CA7,0xA002,0x9CA6,0x9CA5,0xA004,0xA002,0x9CA3,0x9CA2,0xA002,0x9CA1,0x9CA0,
+ 0xA07E,0xA03E,0xA01E,0xA00E,0xA006,0xA002,0x9C9F,0xA002,0x9C9E,0x9C9B,0xA004,0xA002,0x9C9A,0x9C95,0xA002,0x9C94,
+ 0x9C92,0xA008,0xA004,0xA002,0x9C91,0x9C90,0xA002,0x9C8E,0x9C8B,0xA004,0xA002,0x7A23,0x9C88,0xA002,0x9C87,0x9C86,
+ 0xA010,0xA008,0xA004,0xA002,0x9C85,0x9C82,0xA002,0x9C7F,0x946B,0xA004,0xA002,0x943E,0x938F,0xA002,0x93CA,0x936A,
+ 0xA008,0xA004,0xA002,0x933E,0x92C8,0xA002,0x92AE,0x928E,0xA004,0xA002,0x96E0,0x77BF,0xA002,0x96D2,0x96CE,0xA020,
+ 0xA010,0xA008,0xA004,0xA002,0x96BD,0x96BC,0xA002,0x96B9,0x9F0D,0xA004,0xA002,0x9F0B,0x9EFE,0xA002,0x9F8C,0x9F8A,
+ 0xA008,0xA004,0xA002,0x9F89,0x9F88,0xA002,0x9F87,0x9F86,0xA004,0xA002,0x9F85,0x9F83,0xA002,0x9F80,0x973E,0xA010,
+ 0xA008,0xA004,0xA002,0x9730,0x972D,0xA002,0x972A,0x970E,0xA004,0xA002,0x970F,0x9708,0xA002,0x9701,0x9706,0xA008,
+ 0xA004,0xA002,0x96EF,0x96F3,0xA002,0x96E9,0x9753,0xA004,0xA002,0x8B26,0x8A3E,0xA002,0x89EF,0x89EB,0xA040,0xA020,
+ 0xA010,0xA008,0xA004,0xA002,0x89E5,0x9C3B,0xA002,0x9C3A,0x9C39,0xA004,0xA002,0x9C38,0x9C37,0xA002,0x9C36,0x9C35,
+ 0xA008,0xA004,0xA002,0x9C34,0x9C33,0xA002,0x9C32,0x9C31,0xA004,0xA002,0x9C30,0x9C2F,0xA002,0x9C2E,0x9C2D,0xA010,
+ 0xA008,0xA004,0xA002,0x9C2C,0x9C2B,0xA002,0x9C2A,0x9C29,0xA004,0xA002,0x9C28,0x9C27,0xA002,0x9C26,0x9C25,0xA008,
+ 0xA004,0xA002,0x9C24,0x9C23,0xA002,0x9C22,0x9C21,0xA004,0xA002,0x9C20,0x9C1F,0xA002,0x9C1E,0x9C1D,0xA01F,0xA00F,
+ 0xA007,0xA004,0xA002,0x9C1C,0x9C1B,0xA001,0x9C1A,0xA004,0xA002,0x9C19,0x9C18,0xA002,0x9C17,0x9C16,0xA008,0xA004,
+ 0xA002,0x9C15,0x9C14,0xA002,0x9C13,0x9C12,0xA004,0xA002,0x9C11,0x9C10,0xA002,0x9C0F,0x9C0E,0xA010,0xA008,0xA004,
+ 0xA002,0x9C0D,0x9C0C,0xA002,0x9C0B,0x9C0A,0xA004,0xA002,0x9C09,0x9C08,0xA002,0x9C07,0x9C06,0xA008,0xA004,0xA002,
+ 0x9C05,0x9C04,0xA002,0x9C03,0x9C02,0xA004,0xA002,0x9C01,0x9C00,0xA002,0x9BFF,0x9BFE,0xA5FC,0xA2FE,0xA17E,0xA081,
+ 0xA048,0xA03E,0xA01E,0xA00E,0xA006,0xA002,0x9BFD,0xA002,0x9BFC,0x9BFB,0xA004,0xA002,0x9BFA,0x9BF9,0xA002,0x9BF8,
+ 0x9BF7,0xA008,0xA004,0xA002,0x9BF6,0x9BF5,0xA002,0x9BF4,0x9BF3,0xA004,0xA002,0x9BF2,0x9BF1,0xA002,0x9BF0,0x9BEF,
+ 0xA010,0xA008,0xA004,0xA002,0x9BEE,0x9BED,0xA002,0x9BEC,0x9BEB,0xA004,0xA002,0x9BEA,0x9BE9,0xA002,0x9BE8,0x9BE7,
+ 0xA008,0xA004,0xA002,0x9BE6,0x9BE5,0xA002,0x9BE4,0x9BE3,0xA004,0xA002,0x9BE2,0x9BE1,0xA002,0x9BE0,0x9BDF,0xA000,
+ 0xA000,0xA000,0xA004,0xA002,0x9BDE,0x9BDD,0xA000,0x9BDC,0xA001,0xA018,0xA008,0xA001,0xA003,0xA001,0x89DC,0xA002,
+ 0x89DA,0x89DE,0xA008,0xA004,0xA002,0x89D6,0x659B,0xA002,0x8C94,0x8C98,0xA004,0xA002,0x8C85,0x8C8A,0xA002,0x8C82,
+ 0x8C78,0xA010,0xA008,0xA004,0xA002,0x8E9E,0x8E9C,0xA002,0x8E90,0x8E94,0xA004,0xA002,0x8E8F,0x8E85,0xA002,0x8E74,
+ 0x8E6F,0xA008,0xA004,0xA002,0x8E7C,0x8E76,0xA002,0x8E70,0x8E4A,0xA004,0xA002,0x8E52,0x8E51,0xA002,0x8E42,0x8E41,
+ 0xA07E,0xA03E,0xA01E,0xA00E,0xA006,0xA002,0x8E49,0xA002,0x8E31,0x8E3D,0xA004,0xA002,0x8E35,0x8E39,0xA002,0x8E40,
+ 0x8E3A,0xA008,0xA004,0xA002,0x8E2F,0x8E23,0xA002,0x8E2E,0x8E2C,0xA004,0xA002,0x8E1F,0x8E1D,0xA002,0x8E14,0x8DFD,
+ 0xA010,0xA008,0xA004,0xA002,0x8E09,0x8DE4,0xA002,0x8DFB,0x8DF9,0xA004,0xA002,0x8DE3,0x8DF8,0xA002,0x8DF7,0x8DEC,
+ 0xA008,0xA004,0xA002,0x8DC6,0x8DDB,0xA002,0x8DCF,0x8DCE,0xA004,0xA002,0x8DDE,0x8DDA,0xA002,0x8DD7,0x8DD6,0xA020,
+ 0xA010,0xA008,0xA004,0xA002,0x8DC4,0x8DBA,0xA002,0x8DBC,0x8DBF,0xA004,0xA002,0x8DB5,0x8E69,0xA002,0x8E59,0x8E05,
+ 0xA008,0xA004,0xA002,0x8DEB,0x8DB8,0xA002,0x9E7E,0x8C55,0xA004,0xA002,0x91BA,0x91B4,0xA002,0x91B5,0x91AF,0xA010,
+ 0xA008,0xA004,0xA002,0x91AE,0x91AD,0xA002,0x91AA,0x91A3,0xA004,0xA002,0x91A2,0x9191,0xA002,0x918D,0x9190,0xA008,
+ 0xA004,0xA002,0x9185,0x918C,0xA002,0x9179,0x9174,0xA004,0xA002,0x9172,0x917E,0xA002,0x917D,0x916F,0xA040,0xA020,
+ 0xA010,0xA008,0xA004,0xA002,0x9169,0x9170,0xA002,0x9161,0x9162,0xA004,0xA002,0x9BDB,0x9BDA,0xA002,0x9BD9,0x9BD8,
+ 0xA008,0xA004,0xA002,0x9BD7,0x9BD6,0xA002,0x9BD5,0x9BD4,0xA004,0xA002,0x9BD3,0x9BD2,0xA002,0x9BD1,0x9BD0,0xA010,
+ 0xA008,0xA004,0xA002,0x9BCF,0x9BCE,0xA002,0x9BCD,0x9BCC,0xA004,0xA002,0x9BCB,0x9BCA,0xA002,0x9BC9,0x9BC8,0xA008,
+ 0xA004,0xA002,0x9BC7,0x9BC6,0xA002,0x9BC5,0x9BC4,0xA004,0xA002,0x9BC3,0x9BC2,0xA002,0x9BC1,0x9BC0,0xA01F,0xA00F,
+ 0xA008,0xA004,0xA002,0x9BBF,0x9BBE,0xA002,0x9BBD,0x9BBC,0xA003,0xA000,0x9BBB,0xA002,0x9BBA,0x9BB9,0xA008,0xA004,
+ 0xA002,0x9BB8,0x9BB7,0xA002,0x9BB6,0x9BB5,0xA004,0xA002,0x9BB4,0x9BB3,0xA002,0x9BB2,0x9BB1,0xA010,0xA008,0xA004,
+ 0xA002,0x9BB0,0x9BAF,0xA002,0x9BAE,0x9BAD,0xA004,0xA002,0x9BAC,0x9BAB,0xA002,0x9BAA,0x9BA9,0xA008,0xA004,0xA002,
+ 0x9BA8,0x9BA7,0xA002,0x9BA6,0x9BA5,0xA004,0xA002,0x9BA4,0x9BA3,0xA002,0x9BA2,0x9BA1,0xA083,0xA04D,0xA03E,0xA01E,
+ 0xA00E,0xA006,0xA002,0x9BA0,0xA002,0x9B9F,0x9B9E,0xA004,0xA002,0x9B9D,0x9B9C,0xA002,0x9B9B,0x9B9A,0xA008,0xA004,
+ 0xA002,0x9B99,0x9B98,0xA002,0x9B97,0x9B96,0xA004,0xA002,0x9B95,0x9B94,0xA002,0x9B93,0x9B92,0xA010,0xA008,0xA004,
+ 0xA002,0x9B91,0x9B90,0xA002,0x9B8F,0x9B8E,0xA004,0xA002,0x9B8D,0x9B8C,0xA002,0x9B8B,0x9B8A,0xA008,0xA004,0xA002,
+ 0x9B89,0x9B88,0xA002,0x9B87,0x9B86,0xA004,0xA002,0x9B85,0x9B84,0xA002,0x9B83,0x9B82,0xA000,0xA000,0xA008,0xA004,
+ 0xA002,0x9B81,0x9B80,0xA002,0x9B7F,0x9B7E,0xA000,0xA002,0x9B7D,0x9B7C,0xA001,0xA015,0xA005,0xA001,0xA001,0xA001,
+ 0x9164,0xA008,0xA004,0xA002,0x914F,0x914E,0xA002,0x9150,0x914A,0xA004,0xA002,0x8C49,0x8C47,0xA002,0x8D6D,0x8D67,
+ 0xA010,0xA008,0xA004,0xA002,0x8DB1,0x8D91,0xA002,0x8D94,0x8D84,0xA004,0xA002,0x8D73,0x9EB4,0xA002,0x9EB8,0x7E9B,
+ 0xA008,0xA004,0xA002,0x7E47,0x7DAE,0xA002,0x7DA6,0x7D77,0xA004,0xA002,0x7CF8,0x7FF3,0xA002,0x7FEE,0x7FE9,0xA07E,
+ 0xA03E,0xA01E,0xA00E,0xA006,0xA002,0x7FE6,0xA002,0x7FE1,0x7FE5,0xA004,0xA002,0x7FD5,0x7FCE,0xA002,0x7FBF,0x66A8,
+ 0xA008,0xA004,0xA002,0x826E,0x7CE8,0xA002,0x7CD7,0x7CC5,0xA004,0xA002,0x7CC8,0x7CCD,0xA002,0x7CCC,0x7CC7,0xA010,
+ 0xA008,0xA004,0xA002,0x7CC1,0x7CBD,0xA002,0x7CBC,0x7CB2,0xA004,0xA002,0x7CA2,0x7C9E,0xA002,0x7C9C,0x7C9D,0xA008,
+ 0xA004,0xA002,0x7C91,0x6549,0xA002,0x7C7C,0x7FB2,0xA004,0xA002,0x7FB0,0x7FAF,0xA002,0x7FA7,0x7F9F,0xA020,0xA010,
+ 0xA008,0xA004,0xA002,0x7F9D,0x895E,0xA002,0x88DF,0x88D8,0xA004,0xA002,0x8888,0x8885,0xA002,0x887E,0x8268,0xA008,
+ 0xA004,0xA002,0x825F,0x825A,0xA002,0x824F,0x824B,0xA004,0xA002,0x8249,0x8244,0xA002,0x823E,0x8234,0xA010,0xA008,
+ 0xA004,0xA002,0x8233,0x823B,0xA002,0x8238,0x822B,0xA004,0xA002,0x8228,0x822F,0xA002,0x822D,0x8223,0xA008,0xA004,
+ 0xA002,0x8222,0x8221,0xA002,0x8844,0x81EC,0xA004,0xA002,0x8204,0x8202,0xA002,0x8201,0x81FE,0xA040,0xA020,0xA010,
+ 0xA008,0xA004,0xA002,0x7C40,0x7C41,0xA002,0x7C38,0x7C26,0xA004,0xA002,0x7C2A,0x7C1F,0xA002,0x9B7B,0x9B7A,0xA008,
+ 0xA004,0xA002,0x9B79,0x9B78,0xA002,0x9B77,0x9B76,0xA004,0xA002,0x9B75,0x9B74,0xA002,0x9B73,0x9B72,0xA010,0xA008,
+ 0xA004,0xA002,0x9B71,0x9B70,0xA002,0x9B6F,0x9B6E,0xA004,0xA002,0x9B6D,0x9B6C,0xA002,0x9B6B,0x9B6A,0xA008,0xA004,
+ 0xA002,0x9B69,0x9B68,0xA002,0x9B67,0x9B66,0xA004,0xA002,0x9B65,0x9B64,0xA002,0x9B63,0x9B62,0xA01F,0xA00F,0xA008,
+ 0xA004,0xA002,0x9B61,0x9B60,0xA002,0x9B5F,0x9B5E,0xA004,0xA002,0x9B5D,0x9B5C,0xA000,0x9B5B,0xA008,0xA004,0xA002,
+ 0x9B5A,0x9B59,0xA002,0x9B58,0x9B57,0xA004,0xA002,0x9B56,0x9B55,0xA002,0x9B53,0x9B52,0xA010,0xA008,0xA004,0xA002,
+ 0x9B50,0x9B4E,0xA002,0x9B4C,0x9B4B,0xA004,0xA002,0x9B4A,0x9B46,0xA002,0x9B40,0x9B3F,0xA008,0xA004,0xA002,0x9B3E,
+ 0x9B3D,0xA002,0x9B3A,0x9B39,0xA004,0xA002,0x9B38,0x9B37,0xA002,0x9B36,0x9B35,0xA17E,0xA081,0xA050,0xA03E,0xA01E,
+ 0xA00E,0xA006,0xA002,0x9B34,0xA002,0x9B33,0x9B31,0xA004,0xA002,0x9B30,0x9B2E,0xA002,0x9B2D,0x9B2C,0xA008,0xA004,
+ 0xA002,0x9B2B,0x9B2A,0xA002,0x9B29,0x9B28,0xA004,0xA002,0x9B27,0x9B26,0xA002,0x9B25,0x9B24,0xA010,0xA008,0xA004,
+ 0xA002,0x9B22,0x9B21,0xA002,0x9B20,0x9B1E,0xA004,0xA002,0x9B1D,0x9B1C,0xA002,0x9B1B,0x9B1A,0xA008,0xA004,0xA002,
+ 0x9B19,0x9B18,0xA002,0x9B17,0x9B16,0xA004,0xA002,0x9B15,0x9B14,0xA002,0x9B12,0x9B11,0xA000,0xA000,0xA008,0xA004,
+ 0xA002,0x9B10,0x9B0E,0xA002,0x9B0D,0x9B0C,0xA004,0xA002,0x9B0B,0x9B0A,0xA002,0x9B09,0x9B07,0xA001,0xA010,0xA001,
+ 0xA007,0xA003,0xA001,0x7C0B,0xA002,0x7C16,0x7C0F,0xA004,0xA002,0x7BFC,0x7BFE,0xA002,0x7C0C,0x7BEA,0xA010,0xA008,
+ 0xA004,0xA002,0x7BE6,0x7BE5,0xA002,0x7BDA,0x7BDD,0xA004,0xA002,0x7BCC,0x7BC1,0xA002,0x7BD1,0x7BB4,0xA008,0xA004,
+ 0xA002,0x7BAB,0x7BA2,0xA002,0x7B9C,0x7BAA,0xA004,0xA002,0x7B85,0x7BA8,0xA002,0x7B9D,0x7BAC,0xA07E,0xA03E,0xA01E,
+ 0xA00E,0xA006,0xA002,0x7BB8,0xA002,0x7BA7,0x7BA6,0xA004,0xA002,0x7B90,0x7B71,0xA002,0x7B72,0x7B62,0xA008,0xA004,
+ 0xA002,0x7B7B,0x7B6E,0xA002,0x7B60,0x7B5D,0xA004,0xA002,0x7B4C,0x7B75,0xA002,0x7B45,0x7B5A,0xA010,0xA008,0xA004,
+ 0xA002,0x7B58,0x7B1E,0xA002,0x7B3E,0x7B33,0xA004,0xA002,0x7B24,0x7B25,0xA002,0x7B20,0x7B31,0xA008,0xA004,0xA002,
+ 0x7B2E,0x7B19,0xA002,0x7B2A,0x7B38,0xA004,0xA002,0x7B47,0x7B0F,0xA002,0x7B2B,0x7B0A,0xA020,0xA010,0xA008,0xA004,
+ 0xA002,0x7B15,0x7B04,0xA002,0x7B03,0x7B08,0xA004,0xA002,0x7AFD,0x7AFA,0xA002,0x8210,0x7F45,0xA008,0xA004,0xA002,
+ 0x7F44,0x7F42,0xA002,0x7F36,0x883C,0xA004,0xA002,0x8839,0x8821,0xA002,0x881B,0x880A,0xA010,0xA008,0xA004,0xA002,
+ 0x87FE,0x8813,0xA002,0x8816,0x87EE,0xA004,0xA002,0x87E0,0x87EA,0xA002,0x87DB,0x87CA,0xA008,0xA004,0xA002,0x87C0,
+ 0x87D1,0xA002,0x87BD,0x87D3,0xA004,0xA002,0x87CB,0x87B3,0xA002,0x87B5,0x87AC,0xA040,0xA020,0xA010,0xA008,0xA004,
+ 0xA002,0x87E5,0x87AB,0xA002,0x8783,0x8797,0xA004,0xA002,0x87AD,0x8785,0xA002,0x8788,0x87C6,0xA008,0xA004,0xA002,
+ 0x9B06,0x9B05,0xA002,0x9B04,0x9B02,0xA004,0xA002,0x9B01,0x9B00,0xA002,0x9AFF,0x9AFE,0xA010,0xA008,0xA004,0xA002,
+ 0x9AFD,0x9AFC,0xA002,0x9AFA,0x9AF8,0xA004,0xA002,0x9AF7,0x9AF6,0xA002,0x9AF5,0x9AF4,0xA008,0xA004,0xA002,0x9AF3,
+ 0x9AF2,0xA002,0x9AF1,0x9AF0,0xA004,0xA002,0x9AEE,0x9AEC,0xA002,0x9AEA,0x9AE9,0xA01F,0xA010,0xA008,0xA004,0xA002,
+ 0x9AE8,0x9AE7,0xA002,0x9AE5,0x9AE4,0xA004,0xA002,0x9AE3,0x9AE2,0xA002,0x9AE0,0x9ADE,0xA007,0xA003,0xA000,0x9ADD,
+ 0xA002,0x9ADC,0x9ADB,0xA004,0xA002,0x9ADA,0x9AD9,0xA002,0x9AD7,0x9AD6,0xA010,0xA008,0xA004,0xA002,0x9AD5,0x9AD4,
+ 0xA002,0x9AD2,0x9AD0,0xA004,0xA002,0x9ACF,0x9ACE,0xA002,0x9ACD,0x9ACA,0xA008,0xA004,0xA002,0x9AC9,0x9AC8,0xA002,
+ 0x9AC7,0x9AC6,0xA004,0xA002,0x9AC4,0x9AC3,0xA002,0x9ABF,0x9ABE,0xA083,0xA055,0xA03E,0xA01E,0xA00E,0xA006,0xA002,
+ 0x9ABD,0xA002,0x9ABB,0x9AB9,0xA004,0xA002,0x9AB5,0x9AB4,0xA002,0x9AB3,0x9AB2,0xA008,0xA004,0xA002,0x9AAF,0x9AAE,
+ 0xA002,0x9AAD,0x9AAC,0xA004,0xA002,0x9AAB,0x9AAA,0xA002,0x9AA9,0x9AA6,0xA010,0xA008,0xA004,0xA002,0x9A99,0x9A95,
+ 0xA002,0x9A94,0x9A8E,0xA004,0xA002,0x9A8D,0x9A89,0xA002,0x9A83,0x9A72,0xA008,0xA004,0xA002,0x9A6B,0x9A6A,0xA002,
+ 0x9A69,0x9A68,0xA004,0xA002,0x9A67,0x9A66,0xA002,0x9A65,0x9A64,0xA000,0xA010,0xA008,0xA004,0xA002,0x9A63,0x9A62,
+ 0xA002,0x9A61,0x9A60,0xA004,0xA002,0x9A5F,0x9A5E,0xA002,0x9A5D,0x9A5C,0xA000,0xA000,0xA002,0x9A5B,0x9A5A,0xA001,
+ 0xA00D,0xA001,0xA004,0xA001,0xA001,0x87D2,0xA004,0xA002,0x87A8,0x87AF,0xA002,0x8793,0x8765,0xA010,0xA008,0xA004,
+ 0xA002,0x8759,0x8764,0xA002,0x877C,0x8763,0xA004,0xA002,0x8753,0x878B,0xA002,0x876E,0x874C,0xA008,0xA004,0xA002,
+ 0x8770,0x8760,0xA002,0x877B,0x877E,0xA004,0xA002,0x877D,0x8722,0xA002,0x8782,0x873F,0xA07E,0xA03E,0xA01E,0xA00E,
+ 0xA006,0xA002,0x8737,0xA002,0x8729,0x8731,0xA004,0xA002,0x8734,0x8748,0xA002,0x873E,0x871A,0xA008,0xA004,0xA002,
+ 0x872E,0x8725,0xA002,0x871E,0x873B,0xA004,0xA002,0x8723,0x8709,0xA002,0x870D,0x870A,0xA010,0xA008,0xA004,0xA002,
+ 0x8708,0x86F8,0xA002,0x8707,0x8703,0xA004,0xA002,0x86D1,0x86D8,0xA002,0x86DF,0x86F4,0xA008,0xA004,0xA002,0x86DE,
+ 0x8713,0xA002,0x86D0,0x86F3,0xA004,0xA002,0x86ED,0x86F2,0xA002,0x86F1,0x86E9,0xA020,0xA010,0xA008,0xA004,0xA002,
+ 0x86B4,0x86CF,0xA002,0x86C9,0x86AF,0xA004,0xA002,0x86B1,0x86BA,0xA002,0x86B0,0x86CE,0xA008,0xA004,0xA002,0x86B5,
+ 0x86C4,0xA002,0x86B6,0x86A9,0xA004,0xA002,0x8693,0x86AA,0xA002,0x86A3,0x86A7,0xA010,0xA008,0xA004,0xA002,0x869D,
+ 0x86AC,0xA002,0x868B,0x868D,0xA004,0xA002,0x86A8,0x867B,0xA002,0x867C,0x867A,0xA008,0xA004,0xA002,0x867F,0x866E,
+ 0xA002,0x866C,0x8654,0xA004,0xA002,0x864D,0x98A6,0xA002,0x98A5,0x98A2,0xA040,0xA020,0xA010,0xA008,0xA004,0xA002,
+ 0x98A1,0x989F,0xA002,0x989E,0x989B,0xA004,0xA002,0x989A,0x9894,0xA002,0x988F,0x988D,0xA008,0xA004,0xA002,0x988C,
+ 0x9889,0xA002,0x9A59,0x9A58,0xA004,0xA002,0x9A57,0x9A56,0xA002,0x9A55,0x9A54,0xA010,0xA008,0xA004,0xA002,0x9A53,
+ 0x9A52,0xA002,0x9A51,0x9A50,0xA004,0xA002,0x9A4F,0x9A4E,0xA002,0x9A4D,0x9A4C,0xA008,0xA004,0xA002,0x9A4B,0x9A4A,
+ 0xA002,0x9A49,0x9A48,0xA004,0xA002,0x9A47,0x9A46,0xA002,0x9A45,0x9A44,0xA01F,0xA010,0xA008,0xA004,0xA002,0x9A43,
+ 0x9A42,0xA002,0x9A41,0x9A40,0xA004,0xA002,0x9A3F,0x9A3E,0xA002,0x9A3D,0x9A3C,0xA007,0xA004,0xA002,0x9A3B,0x9A3A,
+ 0xA000,0x9A39,0xA004,0xA002,0x9A38,0x9A37,0xA002,0x9A36,0x9A35,0xA010,0xA008,0xA004,0xA002,0x9A34,0x9A33,0xA002,
+ 0x9A32,0x9A31,0xA004,0xA002,0x9A30,0x9A2F,0xA002,0x9A2E,0x9A2D,0xA008,0xA004,0xA002,0x9A2C,0x9A2B,0xA002,0x9A2A,
+ 0x9A29,0xA004,0xA002,0x9A28,0x9A27,0xA002,0x9A26,0x9A25,0xA2FF,0xA17F,0xA082,0xA058,0xA03E,0xA01E,0xA00E,0xA006,
+ 0xA002,0x9A24,0xA002,0x9A23,0x9A22,0xA004,0xA002,0x9A21,0x9A20,0xA002,0x9A1F,0x9A1E,0xA008,0xA004,0xA002,0x9A1D,
+ 0x9A1C,0xA002,0x9A1B,0x9A1A,0xA004,0xA002,0x9A19,0x9A18,0xA002,0x9A17,0x9A16,0xA010,0xA008,0xA004,0xA002,0x9A15,
+ 0x9A14,0xA002,0x9A13,0x9A12,0xA004,0xA002,0x9A11,0x9A10,0xA002,0x9A0F,0x9A0E,0xA008,0xA004,0xA002,0x9A0D,0x9A0C,
+ 0xA002,0x9A0B,0x9A0A,0xA004,0xA002,0x9A09,0x9A08,0xA002,0x9A07,0x9A06,0xA000,0xA010,0xA008,0xA004,0xA002,0x9A05,
+ 0x9A04,0xA002,0x9A03,0x9A02,0xA004,0xA002,0x9A01,0x9A00,0xA002,0x99FF,0x99FE,0xA000,0xA004,0xA002,0x99FD,0x99FC,
+ 0xA002,0x99FB,0x99FA,0xA001,0xA009,0xA001,0xA001,0xA003,0xA001,0x9883,0xA002,0x9880,0x9878,0xA010,0xA008,0xA004,
+ 0xA002,0x8983,0x8071,0xA002,0x8069,0x8052,0xA004,0xA002,0x804D,0x8046,0xA002,0x8043,0x8035,0xA008,0xA004,0xA002,
+ 0x800B,0x8031,0xA002,0x8028,0x8029,0xA004,0xA002,0x8027,0x8026,0xA002,0x8025,0x8022,0xA07E,0xA03E,0xA01E,0xA00E,
+ 0xA006,0xA002,0x8020,0xA002,0x801C,0x8016,0xA004,0xA002,0x8014,0x8012,0xA002,0x77DC,0x76B4,0xA008,0xA004,0xA002,
+ 0x76B2,0x80E5,0xA002,0x758B,0x897B,0xA004,0xA002,0x8966,0x8941,0xA002,0x8936,0x892B,0xA010,0xA008,0xA004,0xA002,
+ 0x8934,0x890A,0xA002,0x891B,0x8913,0xA004,0xA002,0x8919,0x8921,0xA002,0x88F0,0x88FE,0xA008,0xA004,0xA002,0x88E8,
+ 0x88FC,0xA002,0x891A,0x88F1,0xA004,0xA002,0x88E5,0x88E3,0xA002,0x88CE,0x88E2,0xA020,0xA010,0xA008,0xA004,0xA002,
+ 0x88C9,0x88BC,0xA002,0x88B7,0x88C6,0xA004,0xA002,0x88A2,0x8882,0xA002,0x887F,0x887D,0xA008,0xA004,0xA002,0x8872,
+ 0x8869,0xA002,0x8864,0x7AB3,0xA004,0xA002,0x7AAD,0x7AA8,0xA002,0x7AAC,0x7AA0,0xA010,0xA008,0xA004,0xA002,0x7AA6,
+ 0x7A95,0xA002,0x7A88,0x7A86,0xA004,0xA002,0x7A80,0x7A79,0xA002,0x7A78,0x7AE6,0xA008,0xA004,0xA002,0x7FCA,0x766F,
+ 0xA002,0x766B,0x7656,0xA004,0xA002,0x765C,0x7654,0xA002,0x765E,0x764D,0xA040,0xA020,0xA010,0xA008,0xA004,0xA002,
+ 0x7633,0x763E,0xA002,0x7643,0x7635,0xA004,0xA002,0x763F,0x7630,0xA002,0x762D,0x7640,0xA008,0xA004,0xA002,0x7620,
+ 0x7622,0xA002,0x763C,0x761B,0xA004,0xA002,0x99F9,0x99F8,0xA002,0x99F7,0x99F6,0xA010,0xA008,0xA004,0xA002,0x99F5,
+ 0x99F4,0xA002,0x99F3,0x99F2,0xA004,0xA002,0x99F1,0x99F0,0xA002,0x99EF,0x99EE,0xA008,0xA004,0xA002,0x99ED,0x99EC,
+ 0xA002,0x99EB,0x99EA,0xA004,0xA002,0x99E9,0x99E8,0xA002,0x99E7,0x99E6,0xA01F,0xA010,0xA008,0xA004,0xA002,0x99E5,
+ 0x99E4,0xA002,0x99E3,0x99E2,0xA004,0xA002,0x99E1,0x99E0,0xA002,0x99DF,0x99DE,0xA008,0xA004,0xA002,0x99DD,0x99DC,
+ 0xA002,0x99DB,0x99DA,0xA003,0xA000,0x99D9,0xA002,0x99D8,0x99D7,0xA010,0xA008,0xA004,0xA002,0x99D6,0x99D5,0xA002,
+ 0x99D4,0x99D3,0xA004,0xA002,0x99D2,0x99D1,0xA002,0x99D0,0x99CF,0xA008,0xA004,0xA002,0x99CE,0x99CD,0xA002,0x99CC,
+ 0x99CB,0xA004,0xA002,0x99CA,0x99C9,0xA002,0x99C8,0x99C7,0xA083,0xA05C,0xA03E,0xA01E,0xA00E,0xA006,0xA002,0x99C6,
+ 0xA002,0x99C5,0x99C4,0xA004,0xA002,0x99C3,0x99C2,0xA002,0x99C1,0x99C0,0xA008,0xA004,0xA002,0x99BF,0x99BE,0xA002,
+ 0x99BD,0x99BC,0xA004,0xA002,0x99BB,0x99BA,0xA002,0x99B9,0x99B8,0xA010,0xA008,0xA004,0xA002,0x99B7,0x99B6,0xA002,
+ 0x99B5,0x99B4,0xA004,0xA002,0x99B3,0x99B2,0xA002,0x99B1,0x99B0,0xA008,0xA004,0xA002,0x99AF,0x99AE,0xA002,0x99AD,
+ 0x99AC,0xA004,0xA002,0x99AB,0x99AA,0xA002,0x99A9,0x99A7,0xA000,0xA010,0xA008,0xA004,0xA002,0x99A6,0x99A4,0xA002,
+ 0x99A3,0x99A2,0xA004,0xA002,0x99A1,0x99A0,0xA002,0x999F,0x999E,0xA008,0xA004,0xA002,0x999D,0x999C,0xA002,0x999B,
+ 0x999A,0xA000,0xA002,0x998E,0x998C,0xA001,0xA006,0xA001,0xA001,0xA001,0xA001,0x7619,0xA010,0xA008,0xA004,0xA002,
+ 0x7615,0x7618,0xA002,0x7625,0x760A,0xA004,0xA002,0x7617,0x760C,0xA002,0x7605,0x7600,0xA008,0xA004,0xA002,0x7610,
+ 0x75FF,0xA002,0x75FC,0x75F1,0xA004,0xA002,0x7603,0x75E7,0xA002,0x75EB,0x75E4,0xA07E,0xA03E,0xA01E,0xA00E,0xA006,
+ 0xA002,0x75E6,0xA002,0x75E8,0x75E3,0xA004,0xA002,0x75CD,0x75D6,0xA002,0x75C2,0x75C3,0xA008,0xA004,0xA002,0x75B0,
+ 0x75B1,0xA002,0x75C4,0x75B8,0xA004,0xA002,0x75B4,0x75B3,0xA002,0x75A3,0x75AC,0xA010,0xA008,0xA004,0xA002,0x759D,
+ 0x75A0,0xA002,0x7596,0x7594,0xA004,0xA002,0x7592,0x9E73,0xA002,0x9E6D,0x9E71,0xA008,0xA004,0xA002,0x9E6C,0x9E6B,
+ 0xA002,0x9E6A,0x9E69,0xA004,0xA002,0x9E68,0x9E67,0xA002,0x9E66,0x9E63,0xA020,0xA010,0xA008,0xA004,0xA002,0x9E5E,
+ 0x9E5C,0xA002,0x9E5B,0x9E5A,0xA004,0xA002,0x9E57,0x9E55,0xA002,0x9E51,0x9E4E,0xA008,0xA004,0xA002,0x9E4C,0x9E4B,
+ 0xA002,0x9E49,0x9E48,0xA004,0xA002,0x9E47,0x9E46,0xA002,0x9E44,0x9E42,0xA010,0xA008,0xA004,0xA002,0x9E41,0x9E3E,
+ 0xA002,0x9E3A,0x9E39,0xA004,0xA002,0x9E37,0x9E38,0xA002,0x9E36,0x9E31,0xA008,0xA004,0xA002,0x9E32,0x9E2C,0xA002,
+ 0x9E2B,0x9E2A,0xA004,0xA002,0x9E29,0x9E28,0xA002,0x9E22,0x9E20,0xA040,0xA020,0xA010,0xA008,0xA004,0xA002,0x752C,
+ 0x74E0,0xA002,0x74DE,0x76A4,0xA004,0xA002,0x7699,0x7693,0xA002,0x768E,0x7688,0xA008,0xA004,0xA002,0x7A70,0x99A5,
+ 0xA002,0x9ECF,0x7A51,0xA004,0xA002,0x7A37,0x7A39,0xA002,0x9989,0x9983,0xA010,0xA008,0xA004,0xA002,0x9982,0x997E,
+ 0xA002,0x997B,0x9979,0xA004,0xA002,0x9978,0x9973,0xA002,0x9966,0x9964,0xA008,0xA004,0xA002,0x9962,0x9961,0xA002,
+ 0x9960,0x995F,0xA004,0xA002,0x995E,0x995D,0xA002,0x995C,0x995B,0xA01F,0xA010,0xA008,0xA004,0xA002,0x995A,0x9959,
+ 0xA002,0x9958,0x9957,0xA004,0xA002,0x9956,0x9953,0xA002,0x9952,0x9951,0xA008,0xA004,0xA002,0x9950,0x994F,0xA002,
+ 0x994E,0x994D,0xA004,0xA002,0x994C,0x994B,0xA000,0x994A,0xA010,0xA008,0xA004,0xA002,0x9949,0x9948,0xA002,0x9947,
+ 0x9946,0xA004,0xA002,0x9945,0x9944,0xA002,0x9943,0x9942,0xA008,0xA004,0xA002,0x9941,0x9940,0xA002,0x993F,0x993E,
+ 0xA004,0xA002,0x993D,0x993C,0xA002,0x993B,0x993A,0xA17D,0xA080,0xA05F,0xA03E,0xA01E,0xA00E,0xA006,0xA002,0x9939,
+ 0xA002,0x9938,0x9937,0xA004,0xA002,0x9936,0x9935,0xA002,0x9934,0x9933,0xA008,0xA004,0xA002,0x9932,0x9931,0xA002,
+ 0x9930,0x992F,0xA004,0xA002,0x992D,0x992C,0xA002,0x992B,0x992A,0xA010,0xA008,0xA004,0xA002,0x9929,0x9928,0xA002,
+ 0x9927,0x9926,0xA004,0xA002,0x9925,0x9924,0xA002,0x9923,0x9922,0xA008,0xA004,0xA002,0x9921,0x9920,0xA002,0x991F,
+ 0x991E,0xA004,0xA002,0x991D,0x991C,0xA002,0x991B,0x991A,0xA000,0xA010,0xA008,0xA004,0xA002,0x9919,0x9918,0xA002,
+ 0x9917,0x9916,0xA004,0xA002,0x9915,0x9914,0xA002,0x9913,0x9912,0xA008,0xA004,0xA002,0x9911,0x990F,0xA002,0x990E,
+ 0x990C,0xA004,0xA002,0x990B,0x990A,0xA002,0x9909,0x9908,0xA001,0xA001,0xA00F,0xA007,0xA003,0xA001,0x7A14,0xA002,
+ 0x7A1E,0x7A02,0xA004,0xA002,0x7A03,0x5D47,0xA002,0x7A06,0x79EB,0xA008,0xA004,0xA002,0x79E3,0x79ED,0xA002,0x79D5,
+ 0x96C9,0xA004,0xA002,0x77EC,0x77E7,0xA002,0x953A,0x9573,0xA07E,0xA03E,0xA01E,0xA00E,0xA006,0xA002,0x9572,0xA002,
+ 0x9571,0x956F,0xA004,0xA002,0x956C,0x956B,0xA002,0x956A,0x9569,0xA008,0xA004,0xA002,0x9568,0x9567,0xA002,0x9566,
+ 0x9565,0xA004,0xA002,0x9564,0x9562,0xA002,0x9561,0x955D,0xA010,0xA008,0xA004,0xA002,0x955F,0x955E,0xA002,0x955B,
+ 0x9559,0xA004,0xA002,0x9558,0x9557,0xA002,0x9556,0x9554,0xA008,0xA004,0xA002,0x9553,0x9552,0xA002,0x954F,0x954E,
+ 0xA004,0xA002,0x954C,0x9549,0xA002,0x9546,0x9545,0xA020,0xA010,0xA008,0xA004,0xA002,0x9544,0x9535,0xA002,0x9542,
+ 0x953F,0xA004,0xA002,0x953E,0x953C,0xA002,0x9538,0x9537,0xA008,0xA004,0xA002,0x9536,0x9534,0xA002,0x9532,0x9531,
+ 0xA004,0xA002,0x952C,0x9529,0xA002,0x952B,0x952A,0xA010,0xA008,0xA004,0xA002,0x9522,0x951F,0xA002,0x951E,0x951D,
+ 0xA004,0xA002,0x951B,0x9518,0xA002,0x9516,0x9515,0xA008,0xA004,0xA002,0x9514,0x9513,0xA002,0x9512,0x950F,0xA004,
+ 0xA002,0x950E,0x950D,0xA002,0x950A,0x9509,0xA040,0xA020,0xA010,0xA008,0xA004,0xA002,0x9507,0x9506,0xA002,0x9502,
+ 0x9503,0xA004,0xA002,0x94FF,0x94FD,0xA002,0x94FC,0x94F9,0xA008,0xA004,0xA002,0x94F7,0x94F5,0xA002,0x94F4,0x94F3,
+ 0xA004,0xA002,0x94EF,0x94EE,0xA002,0x94EB,0x94E9,0xA010,0xA008,0xA004,0xA002,0x9907,0x9906,0xA002,0x9905,0x9904,
+ 0xA004,0xA002,0x9903,0x9902,0xA002,0x9901,0x9900,0xA008,0xA004,0xA002,0x98FF,0x98FE,0xA002,0x98FD,0x98FC,0xA004,
+ 0xA002,0x98FB,0x98FA,0xA002,0x98F9,0x98F8,0xA020,0xA010,0xA008,0xA004,0xA002,0x98F7,0x98F6,0xA002,0x98F5,0x98F4,
+ 0xA004,0xA002,0x98F3,0x98F2,0xA002,0x98F1,0x98F0,0xA008,0xA004,0xA002,0x98EF,0x98EE,0xA002,0x98ED,0x98EC,0xA004,
+ 0xA002,0x98EB,0x98EA,0xA002,0x98E9,0x98E6,0xA00F,0xA007,0xA003,0xA000,0x98E5,0xA002,0x98E4,0x98E3,0xA004,0xA002,
+ 0x98E2,0x98E1,0xA002,0x98E0,0x98DD,0xA008,0xA004,0xA002,0x98DC,0x98DB,0xA002,0x98D7,0x98D6,0xA004,0xA002,0x98D4,
+ 0x98D0,0xA002,0x98CF,0x98CD,0xA083,0xA065,0xA03E,0xA01E,0xA00E,0xA006,0xA002,0x98CC,0xA002,0x98CB,0x98CA,0xA004,
+ 0xA002,0x98C9,0x98C8,0xA002,0x98C7,0x98C6,0xA008,0xA004,0xA002,0x98C5,0x98C4,0xA002,0x98C3,0x98C2,0xA004,0xA002,
+ 0x98C1,0x98C0,0xA002,0x98BF,0x98BE,0xA010,0xA008,0xA004,0xA002,0x98BD,0x98BC,0xA002,0x98BB,0x98BA,0xA004,0xA002,
+ 0x98B9,0x98B8,0xA002,0x98B7,0x98B6,0xA008,0xA004,0xA002,0x98B5,0x98B4,0xA002,0x98B3,0x98B2,0xA004,0xA002,0x98B1,
+ 0x98B0,0xA002,0x98AF,0x98AE,0xA020,0xA010,0xA008,0xA004,0xA002,0x98AD,0x98AC,0xA002,0x98AB,0x98AA,0xA004,0xA002,
+ 0x98A9,0x98A8,0xA002,0x98A3,0x9899,0xA008,0xA004,0xA002,0x9895,0x9892,0xA002,0x988E,0x988B,0xA004,0xA002,0x9874,
+ 0x9873,0xA002,0x9872,0x9871,0xA000,0xA000,0xA000,0xA002,0x9870,0x986F,0xA001,0xA001,0xA00C,0xA004,0xA001,0xA001,
+ 0x94EA,0xA004,0xA002,0x94E8,0x94E7,0xA002,0x94E5,0x94E4,0xA008,0xA004,0xA002,0x94E2,0x94E0,0xA002,0x94DF,0x94DE,
+ 0xA004,0xA002,0x94DB,0x94D8,0xA002,0x94D9,0x94D7,0xA07E,0xA03E,0xA01E,0xA00E,0xA006,0xA002,0x94D6,0xA002,0x94D5,
+ 0x94D2,0xA004,0xA002,0x94D1,0x94D0,0xA002,0x94CE,0x94CD,0xA008,0xA004,0xA002,0x94CC,0x94CB,0xA002,0x94CA,0x94C9,
+ 0xA004,0xA002,0x94C8,0x94C4,0xA002,0x94BF,0x94BD,0xA010,0xA008,0xA004,0xA002,0x94BC,0x94BA,0xA002,0x94B9,0x94B8,
+ 0xA004,0xA002,0x94B7,0x94B6,0xA002,0x94B4,0x94B2,0xA008,0xA004,0xA002,0x94B0,0x94AF,0xA002,0x94AC,0x94AD,0xA004,
+ 0xA002,0x94AA,0x94AB,0xA002,0x94A4,0x94A3,0xA020,0xA010,0xA008,0xA004,0xA002,0x949C,0x949B,0xA002,0x949A,0x9495,
+ 0xA004,0xA002,0x9497,0x9494,0xA002,0x9490,0x948F,0xA008,0xA004,0xA002,0x948D,0x948C,0xA002,0x948A,0x948B,0xA004,
+ 0xA002,0x9487,0x9486,0xA002,0x9485,0x8832,0xA010,0xA008,0xA004,0xA002,0x76E5,0x76CD,0xA002,0x7F7E,0x7F81,0xA004,
+ 0xA002,0x7F79,0x7F71,0xA002,0x7F74,0x7F68,0xA008,0xA004,0xA002,0x8A48,0x7F5F,0xA002,0x7F61,0x7F58,0xA004,0xA002,
+ 0x7583,0x7579,0xA002,0x7572,0x755B,0xA040,0xA020,0xA010,0xA008,0xA004,0xA002,0x7548,0x754B,0xA002,0x754E,0x7540,
+ 0xA004,0xA002,0x753A,0x77BD,0xA002,0x77B5,0x77B0,0xA008,0xA004,0xA002,0x77A0,0x779F,0xA002,0x7791,0x778C,0xA004,
+ 0xA002,0x7780,0x777D,0xA002,0x778D,0x777F,0xA010,0xA008,0xA004,0xA002,0x7765,0x7762,0xA002,0x986E,0x986D,0xA004,
+ 0xA002,0x986C,0x986B,0xA002,0x986A,0x9869,0xA008,0xA004,0xA002,0x9868,0x9867,0xA002,0x9866,0x9865,0xA004,0xA002,
+ 0x9864,0x9863,0xA002,0x9862,0x9861,0xA020,0xA010,0xA008,0xA004,0xA002,0x9860,0x985F,0xA002,0x985E,0x985D,0xA004,
+ 0xA002,0x985C,0x985B,0xA002,0x985A,0x9859,0xA008,0xA004,0xA002,0x9858,0x9857,0xA002,0x9856,0x9855,0xA004,0xA002,
+ 0x9854,0x9853,0xA002,0x9852,0x9851,0xA00F,0xA007,0xA004,0xA002,0x9850,0x984F,0xA000,0x984E,0xA004,0xA002,0x984D,
+ 0x984C,0xA002,0x984B,0x984A,0xA008,0xA004,0xA002,0x9849,0x9848,0xA002,0x9847,0x9846,0xA004,0xA002,0x9845,0x9844,
+ 0xA002,0x9843,0x9842,0xABF3,0xA5FA,0xA2FB,0xA17D,0xA080,0xA068,0xA03E,0xA01E,0xA00E,0xA006,0xA002,0x9841,0xA002,
+ 0x9840,0x983F,0xA004,0xA002,0x983E,0x983D,0xA002,0x983C,0x983B,0xA008,0xA004,0xA002,0x983A,0x9839,0xA002,0x9838,
+ 0x9837,0xA004,0xA002,0x9836,0x9835,0xA002,0x9834,0x9833,0xA010,0xA008,0xA004,0xA002,0x9832,0x9831,0xA002,0x9830,
+ 0x982F,0xA004,0xA002,0x982E,0x982D,0xA002,0x982C,0x982B,0xA008,0xA004,0xA002,0x982A,0x9829,0xA002,0x9828,0x9827,
+ 0xA004,0xA002,0x9826,0x9825,0xA002,0x9824,0x9823,0xA020,0xA010,0xA008,0xA004,0xA002,0x9822,0x9821,0xA002,0x9820,
+ 0x981F,0xA004,0xA002,0x981E,0x981D,0xA002,0x981C,0x981B,0xA008,0xA004,0xA002,0x981A,0x9819,0xA002,0x9818,0x9817,
+ 0xA004,0xA002,0x9816,0x9815,0xA002,0x9814,0x9813,0xA000,0xA000,0xA004,0xA002,0x9812,0x9811,0xA002,0x9810,0x980F,
+ 0xA001,0xA001,0xA006,0xA001,0xA001,0xA002,0x7768,0x775A,0xA008,0xA004,0xA002,0x7743,0x7747,0xA002,0x7751,0x7750,
+ 0xA004,0xA002,0x7738,0x7735,0xA002,0x7726,0x772D,0xA07E,0xA03E,0xA01E,0xA00E,0xA006,0xA002,0x7719,0xA002,0x7722,
+ 0x771A,0xA004,0xA002,0x7708,0x7707,0xA002,0x76F9,0x770D,0xA008,0xA004,0xA002,0x7704,0x76F1,0xA002,0x9EFC,0x9EFB,
+ 0xA004,0xA002,0x9EF9,0x9F9B,0xA002,0x7934,0x791E,0xA010,0xA008,0xA004,0xA002,0x7924,0x7913,0xA002,0x78F4,0x7905,
+ 0xA004,0xA002,0x78F2,0x78EC,0xA002,0x78C9,0x78D9,0xA008,0xA004,0xA002,0x78D4,0x78A5,0xA002,0x78B9,0x78B2,0xA004,
+ 0xA002,0x78A3,0x78A1,0xA002,0x789C,0x7887,0xA020,0xA010,0xA008,0xA004,0xA002,0x789A,0x7893,0xA002,0x789B,0x786A,
+ 0xA004,0xA002,0x784C,0x7847,0xA002,0x7850,0x7826,0xA008,0xA004,0xA002,0x7857,0x7856,0xA002,0x786D,0x784E,0xA004,
+ 0xA002,0x7829,0x7823,0xA002,0x782C,0x7825,0xA010,0xA008,0xA004,0xA002,0x783C,0x781F,0xA002,0x783B,0x783A,0xA004,
+ 0xA002,0x7839,0x781D,0xA002,0x781C,0x782D,0xA008,0xA004,0xA002,0x65AB,0x7811,0xA002,0x7818,0x7817,0xA004,0xA002,
+ 0x7809,0x7800,0xA002,0x77F8,0x77F6,0xA040,0xA020,0xA010,0xA008,0xA004,0xA002,0x6DFC,0x6CF6,0xA002,0x6C93,0x807F,
+ 0xA004,0xA002,0x8080,0x6206,0xA002,0x61D1,0x61CB,0xA008,0xA004,0xA002,0x619D,0x61A9,0xA002,0x615D,0x610D,0xA004,
+ 0xA002,0x6106,0x60AB,0xA002,0x6063,0x6059,0xA010,0xA008,0xA004,0xA002,0x6041,0x6067,0xA002,0x605A,0x605D,0xA004,
+ 0xA002,0x603C,0x980E,0xA002,0x980D,0x980C,0xA008,0xA004,0xA002,0x980B,0x980A,0xA002,0x9809,0x9808,0xA004,0xA002,
+ 0x9807,0x9806,0xA002,0x9805,0x9804,0xA020,0xA010,0xA008,0xA004,0xA002,0x9803,0x9802,0xA002,0x9801,0x9800,0xA004,
+ 0xA002,0x97FF,0x97FE,0xA002,0x97FD,0x97FC,0xA008,0xA004,0xA002,0x97FB,0x97FA,0xA002,0x97F9,0x97F8,0xA004,0xA002,
+ 0x97F7,0x97F4,0xA002,0x97F2,0x97F1,0xA00F,0xA008,0xA004,0xA002,0x97F0,0x97EF,0xA002,0x97EE,0x97E8,0xA004,0xA002,
+ 0x97E5,0x97E4,0xA001,0x97E3,0xA008,0xA004,0xA002,0x97E2,0x97E1,0xA002,0x97E0,0x97DF,0xA004,0xA002,0x97DE,0x97DD,
+ 0xA002,0x97DC,0x97DB,0xA081,0xA06E,0xA03E,0xA01E,0xA00E,0xA006,0xA002,0x97DA,0xA002,0x97D9,0x97D8,0xA004,0xA002,
+ 0x97D7,0x97D6,0xA002,0x97D5,0x97D4,0xA008,0xA004,0xA002,0x97D3,0x97D2,0xA002,0x97D1,0x97D0,0xA004,0xA002,0x97CF,
+ 0x97CE,0xA002,0x97CD,0x97CC,0xA010,0xA008,0xA004,0xA002,0x97CB,0x97CA,0xA002,0x97C9,0x97C8,0xA004,0xA002,0x97C7,
+ 0x97C6,0xA002,0x97C5,0x97C4,0xA008,0xA004,0xA002,0x97C3,0x97C2,0xA002,0x97C1,0x97C0,0xA004,0xA002,0x97BF,0x97BE,
+ 0xA002,0x97BD,0x97BC,0xA020,0xA010,0xA008,0xA004,0xA002,0x97BB,0x97BA,0xA002,0x97B9,0x97B8,0xA004,0xA002,0x97B7,
+ 0x97B6,0xA002,0x97B5,0x97B3,0xA008,0xA004,0xA002,0x97B1,0x97B0,0xA002,0x97AE,0x97AC,0xA004,0xA002,0x97AA,0x97A9,
+ 0xA002,0x97A8,0x97A7,0xA000,0xA008,0xA004,0xA002,0x97A6,0x97A5,0xA002,0x97A4,0x97A2,0xA004,0xA002,0x97A1,0x979F,
+ 0xA000,0x979E,0xA001,0xA001,0xA001,0xA008,0xA004,0xA002,0x5FD0,0x5FD1,0xA002,0x79B3,0x79A7,0xA004,0xA002,0x799A,
+ 0x798A,0xA002,0x7985,0x797A,0xA07E,0xA03E,0xA01E,0xA00E,0xA006,0xA002,0x7967,0xA002,0x796F,0x7960,0xA004,0xA002,
+ 0x7957,0x7962,0xA002,0x795A,0x7953,0xA008,0xA004,0xA002,0x795C,0x795B,0xA002,0x7949,0x7946,0xA004,0xA002,0x7940,
+ 0x793B,0xA002,0x6249,0x6248,0xA010,0xA008,0xA004,0xA002,0x6243,0x623D,0xA002,0x623E,0x71B9,0xA004,0xA002,0x7166,
+ 0x7118,0xA002,0x706C,0x7228,0xA008,0xA004,0xA002,0x721D,0x71F9,0xA002,0x71E7,0x71D4,0xA004,0xA002,0x71E0,0x71A0,
+ 0xA002,0x71A8,0x71B5,0xA020,0xA010,0xA008,0xA004,0xA002,0x71B3,0x7198,0xA002,0x717A,0x7178,0xA004,0xA002,0x714A,
+ 0x7172,0xA002,0x7145,0x7168,0xA008,0xA004,0xA002,0x715C,0x7173,0xA002,0x7131,0x712F,0xA004,0xA002,0x7116,0x7113,
+ 0xA002,0x7110,0x70CA,0xA010,0xA008,0xA004,0xA002,0x70E8,0x70B1,0xA002,0x70AB,0x70B7,0xA004,0xA002,0x70C0,0x70BB,
+ 0xA002,0x709D,0x7096,0xA008,0xA004,0xA002,0x709C,0x7080,0xA002,0x65D6,0x65D2,0xA004,0xA002,0x65CE,0x65CC,0xA002,
+ 0x65C3,0x65C4,0xA040,0xA020,0xA010,0xA008,0xA004,0xA002,0x65C6,0x65BC,0xA002,0x6593,0x9F51,0xA004,0xA002,0x6590,
+ 0x89F3,0xA002,0x6BC2,0x5F40,0xA008,0xA004,0xA002,0x6BB3,0x98DA,0xA002,0x98D9,0x98D5,0xA004,0xA002,0x98D3,0x98D2,
+ 0xA002,0x98D1,0x6B59,0xA010,0xA008,0xA004,0xA002,0x6B46,0x6B43,0xA002,0x6B39,0x6B37,0xA004,0xA002,0x6B24,0x81A6,
+ 0xA002,0x81C1,0x979D,0xA008,0xA004,0xA002,0x979C,0x979B,0xA002,0x979A,0x9799,0xA004,0xA002,0x9797,0x9796,0xA002,
+ 0x9795,0x9793,0xA020,0xA010,0xA008,0xA004,0xA002,0x9790,0x978F,0xA002,0x978E,0x978C,0xA004,0xA002,0x978A,0x9789,
+ 0xA002,0x9788,0x9787,0xA008,0xA004,0xA002,0x9786,0x9784,0xA002,0x9783,0x9782,0xA004,0xA002,0x9781,0x9780,0xA002,
+ 0x977F,0x977E,0xA010,0xA008,0xA004,0xA002,0x977D,0x977B,0xA002,0x977A,0x9779,0xA004,0xA002,0x9778,0x9777,0xA002,
+ 0x9775,0x9772,0xA007,0xA003,0xA001,0x9771,0xA002,0x9770,0x976F,0xA004,0xA002,0x976E,0x976D,0xA002,0x976C,0x976B,
+ 0xA180,0xA083,0xA073,0xA03E,0xA01E,0xA00E,0xA006,0xA002,0x976A,0xA002,0x9768,0x9767,0xA004,0xA002,0x9766,0x9764,
+ 0xA002,0x9763,0x975F,0xA008,0xA004,0xA002,0x975D,0x975C,0xA002,0x975A,0x9758,0xA004,0xA002,0x9757,0x9755,0xA002,
+ 0x9754,0x9751,0xA010,0xA008,0xA004,0xA002,0x9750,0x974F,0xA002,0x974E,0x974D,0xA004,0xA002,0x974C,0x974B,0xA002,
+ 0x974A,0x9749,0xA008,0xA004,0xA002,0x9748,0x9747,0xA002,0x9746,0x9745,0xA004,0xA002,0x9744,0x9743,0xA002,0x9742,
+ 0x9741,0xA020,0xA010,0xA008,0xA004,0xA002,0x9740,0x973F,0xA002,0x973D,0x973C,0xA004,0xA002,0x973B,0x973A,0xA002,
+ 0x9737,0x9736,0xA008,0xA004,0xA002,0x9735,0x9734,0xA002,0x9733,0x9731,0xA004,0xA002,0x972F,0x972E,0xA002,0x972C,
+ 0x972B,0xA010,0xA008,0xA004,0xA002,0x9729,0x9728,0xA002,0x9727,0x9726,0xA004,0xA002,0x9725,0x9724,0xA002,0x9723,
+ 0x9722,0xA000,0xA000,0xA000,0x9721,0xA001,0xA001,0xA001,0xA005,0xA001,0xA002,0x81BB,0x81CA,0xA004,0xA002,0x6726,
+ 0x81CC,0xA002,0x81AA,0x81A3,0xA07E,0xA03E,0xA01E,0xA00E,0xA006,0xA002,0x6ED5,0xA002,0x8191,0x8182,0xA004,0xA002,
+ 0x8188,0x5AB5,0xA002,0x584D,0x8167,0xA008,0xA004,0xA002,0x816D,0x817D,0xA002,0x817C,0x8169,0xA004,0xA002,0x8160,
+ 0x8171,0xA002,0x815A,0x8159,0xA010,0xA008,0xA004,0xA002,0x8174,0x8153,0xA002,0x814C,0x8148,0xA004,0xA002,0x8132,
+ 0x8118,0xA002,0x812C,0x811E,0xA008,0xA004,0xA002,0x8136,0x8C5A,0xA002,0x8112,0x6715,0xA004,0xA002,0x80FC,0x80F2,
+ 0xA002,0x810E,0x810D,0xA020,0xA010,0xA008,0xA004,0xA002,0x80ED,0x80F4,0xA002,0x80F1,0x80EB,0xA004,0xA002,0x80DD,
+ 0x6710,0xA002,0x80D7,0x80CD,0xA008,0xA004,0xA002,0x80D9,0x80C4,0xA002,0x80C2,0x80DB,0xA004,0xA002,0x80EA,0x80E9,
+ 0xA002,0x80E8,0x80E7,0xA010,0xA008,0xA004,0xA002,0x80B7,0x80B4,0xA002,0x80AD,0x80AB,0xA004,0xA002,0x80B1,0x80BD,
+ 0xA002,0x670A,0x80BC,0xA008,0xA004,0xA002,0x8093,0x809C,0xA002,0x809F,0x5216,0xA004,0xA002,0x8662,0x7230,0xA002,
+ 0x7256,0x7252,0xA040,0xA020,0xA010,0xA008,0xA004,0xA002,0x724D,0x656B,0xA002,0x6555,0x6535,0xA004,0xA002,0x6C32,
+ 0x6C2A,0xA002,0x6C24,0x6C29,0xA008,0xA004,0xA002,0x6C21,0x6C1A,0xA002,0x6C19,0x6C18,0xA004,0xA002,0x6C15,0x6C0D,
+ 0xA002,0x6C06,0x6C07,0xA010,0xA008,0xA004,0xA002,0x6C05,0x6BF9,0xA002,0x6BF5,0x6BFD,0xA004,0xA002,0x6BF3,0x6BEA,
+ 0xA002,0x8004,0x64D8,0xA008,0xA004,0xA002,0x643F,0x9720,0xA002,0x971F,0x971D,0xA004,0xA002,0x971B,0x971A,0xA002,
+ 0x9719,0x9718,0xA020,0xA010,0xA008,0xA004,0xA002,0x9717,0x9715,0xA002,0x9714,0x9712,0xA004,0xA002,0x9711,0x9710,
+ 0xA002,0x970C,0x970B,0xA008,0xA004,0xA002,0x970A,0x9705,0xA002,0x9703,0x9702,0xA004,0xA002,0x96FF,0x96FD,0xA002,
+ 0x96FC,0x96FB,0xA010,0xA008,0xA004,0xA002,0x96FA,0x96F8,0xA002,0x96F5,0x96F4,0xA004,0xA002,0x96F2,0x96F1,0xA002,
+ 0x96F0,0x96EE,0xA007,0xA004,0xA002,0x96ED,0x96EC,0xA001,0x96EB,0xA004,0xA002,0x96E7,0x96E6,0xA002,0x96E5,0x96E4,
+ 0xA082,0xA076,0xA03E,0xA01E,0xA00E,0xA006,0xA002,0x96E3,0xA002,0x96E2,0x96E1,0xA004,0xA002,0x96DF,0x96DE,0xA002,
+ 0x96DD,0x96DC,0xA008,0xA004,0xA002,0x96DB,0x96DA,0xA002,0x96D9,0x96D8,0xA004,0xA002,0x96D7,0x96D6,0xA002,0x96D4,
+ 0x96D3,0xA010,0xA008,0xA004,0xA002,0x96D1,0x96D0,0xA002,0x96CB,0x96CA,0xA004,0xA002,0x96C8,0x96C3,0xA002,0x96C2,
+ 0x96BF,0xA008,0xA004,0xA002,0x96BB,0x96BA,0xA002,0x96B8,0x96B7,0xA004,0xA002,0x96B5,0x96B4,0xA002,0x96B2,0x96B1,
+ 0xA020,0xA010,0xA008,0xA004,0xA002,0x96AF,0x96AE,0xA002,0x96AD,0x96AC,0xA004,0xA002,0x96AB,0x96AA,0xA002,0x96A9,
+ 0x96A8,0xA008,0xA004,0xA002,0x96A6,0x96A5,0xA002,0x96A4,0x96A3,0xA004,0xA002,0x96A2,0x96A1,0xA002,0x96A0,0x969F,
+ 0xA010,0xA008,0xA004,0xA002,0x969E,0x969D,0xA002,0x969B,0x969A,0xA004,0xA002,0x9696,0x9695,0xA002,0x9693,0x9692,
+ 0xA000,0xA004,0xA002,0x9691,0x968E,0xA000,0x968C,0xA001,0xA001,0xA001,0xA001,0xA004,0xA002,0x63B0,0x6332,0xA002,
+ 0x6308,0x7292,0xA07E,0xA03E,0xA01E,0xA00E,0xA006,0xA002,0x728F,0xA002,0x728D,0x728B,0xA004,0xA002,0x7284,0x727F,
+ 0xA002,0x727E,0x726F,0xA008,0xA004,0xA002,0x7266,0x725D,0xA002,0x729F,0x726E,0xA004,0xA002,0x89D1,0x89D0,0xA002,
+ 0x89CF,0x89CE,0xA010,0xA008,0xA004,0xA002,0x89CC,0x89CB,0xA002,0x89CA,0x89C7,0xA004,0xA002,0x8D59,0x8D55,0xA002,
+ 0x8D4D,0x8D47,0xA008,0xA004,0xA002,0x8D49,0x8D48,0xA002,0x8D46,0x8D45,0xA004,0xA002,0x8D40,0x8D3D,0xA002,0x8D3B,
+ 0x8D36,0xA020,0xA010,0xA008,0xA004,0xA002,0x8D33,0x8D32,0xA002,0x66E9,0x66E6,0xA004,0xA002,0x66DC,0x66DB,0xA002,
+ 0x66BE,0x669D,0xA008,0xA004,0xA002,0x66A7,0x668C,0xA002,0x6684,0x6677,0xA004,0xA002,0x6657,0x6661,0xA002,0x6656,
+ 0x664F,0xA010,0xA008,0xA004,0xA002,0x6641,0x6654,0xA002,0x665F,0x8006,0xA004,0xA002,0x6635,0x6636,0xA002,0x6631,
+ 0x6634,0xA008,0xA004,0xA002,0x661D,0x66F7,0xA002,0x7085,0x6600,0xA004,0xA002,0x6615,0x6603,0xA002,0x6772,0x6619,
+ 0xA040,0xA020,0xA010,0xA008,0xA004,0xA002,0x660A,0x65F0,0xA002,0x65EF,0x65EE,0xA004,0xA002,0x6534,0x7513,0xA002,
+ 0x7511,0x750F,0xA008,0xA004,0xA002,0x74FF,0x74F4,0xA002,0x74EF,0x81E7,0xA004,0xA002,0x622C,0x6224,0xA002,0x6225,
+ 0x6221,0xA010,0xA008,0xA004,0xA002,0x6222,0x621F,0xA002,0x621B,0x6217,0xA004,0xA002,0x620B,0x8ECE,0xA002,0x8F9A,
+ 0x8F98,0xA008,0xA004,0xA002,0x8F8F,0x8F8E,0xA002,0x8F8D,0x968A,0xA004,0xA002,0x9689,0x9687,0xA002,0x9684,0x9683,
+ 0xA020,0xA010,0xA008,0xA004,0xA002,0x9682,0x9681,0xA002,0x9680,0x967F,0xA004,0xA002,0x967E,0x967D,0xA002,0x967C,
+ 0x967B,0xA008,0xA004,0xA002,0x967A,0x9679,0xA002,0x9678,0x9673,0xA004,0xA002,0x9671,0x9670,0xA002,0x966F,0x966E,
+ 0xA010,0xA008,0xA004,0xA002,0x966D,0x966B,0xA002,0x9666,0x9665,0xA004,0xA002,0x9663,0x9660,0xA002,0x965E,0x965D,
+ 0xA008,0xA004,0xA002,0x965C,0x965A,0xA002,0x9659,0x9658,0xA003,0xA001,0x9657,0xA002,0x9656,0x9653,0xA2FB,0xA17F,
+ 0xA082,0xA07A,0xA03E,0xA01E,0xA00E,0xA006,0xA002,0x9652,0xA002,0x9651,0x964F,0xA004,0xA002,0x964E,0x964A,0xA002,
+ 0x9643,0x9641,0xA008,0xA004,0xA002,0x963E,0x963A,0xA002,0x9639,0x9638,0xA004,0xA002,0x9637,0x9630,0xA002,0x962F,
+ 0x962D,0xA010,0xA008,0xA004,0xA002,0x962C,0x962B,0xA002,0x9629,0x9628,0xA004,0xA002,0x9627,0x9626,0xA002,0x9625,
+ 0x9624,0xA008,0xA004,0xA002,0x9623,0x9620,0xA002,0x961E,0x961B,0xA004,0xA002,0x9618,0x9613,0xA002,0x9607,0x95FF,
+ 0xA020,0xA010,0xA008,0xA004,0xA002,0x95EC,0x95E7,0xA002,0x95E6,0x95E5,0xA004,0xA002,0x95E4,0x95E3,0xA002,0x95E2,
+ 0x95E1,0xA008,0xA004,0xA002,0x95E0,0x95DF,0xA002,0x95DE,0x95DD,0xA004,0xA002,0x95DC,0x95DB,0xA002,0x95DA,0x95D9,
+ 0xA010,0xA008,0xA004,0xA002,0x95D8,0x95D7,0xA002,0x95D6,0x95D5,0xA004,0xA002,0x95D4,0x95D3,0xA002,0x95D2,0x95D1,
+ 0xA008,0xA004,0xA002,0x95D0,0x95CF,0xA002,0x95CE,0x95CD,0xA000,0xA000,0x95CC,0xA001,0xA001,0xA001,0xA001,0xA001,
+ 0xA001,0x8F8B,0xA07E,0xA03E,0xA01E,0xA00E,0xA006,0xA002,0x8F87,0xA002,0x8F84,0x8F82,0xA004,0xA002,0x8F81,0x8F7E,
+ 0xA002,0x8F7C,0x8F7A,0xA008,0xA004,0xA002,0x8F79,0x8F77,0xA002,0x8F78,0x8F76,0xA004,0xA002,0x8F75,0x8F73,0xA002,
+ 0x8F72,0x8F71,0xA010,0xA008,0xA004,0xA002,0x8F6D,0x8F6B,0xA002,0x6BAA,0x6BA1,0xA004,0xA002,0x6B9B,0x6B9A,0xA002,
+ 0x6B8D,0x6B93,0xA008,0xA004,0xA002,0x6B92,0x6B84,0xA002,0x6B87,0x6B82,0xA004,0xA002,0x6B81,0x7352,0xA002,0x7337,
+ 0x6AAB,0xA020,0xA010,0xA008,0xA004,0xA002,0x6A97,0x6AA9,0xA002,0x6A90,0x6A91,0xA004,0xA002,0x6A7C,0x6A58,0xA002,
+ 0x6A28,0x6A3D,0xA008,0xA004,0xA002,0x6A79,0x6A8E,0xA002,0x6A35,0x6A5B,0xA004,0xA002,0x6A50,0x6AA0,0xA002,0x6A3E,
+ 0x6A44,0xA010,0xA008,0xA004,0xA002,0x69F2,0x6A65,0xA002,0x6A18,0x6A17,0xA004,0xA002,0x69ED,0x6A2F,0xA002,0x69FF,
+ 0x698D,0xA008,0xA004,0xA002,0x69E0,0x6995,0xA002,0x69DF,0x69CA,0xA004,0xA002,0x69C1,0x69B1,0xA002,0x69D4,0x69AD,
+ 0xA040,0xA020,0xA010,0xA008,0xA004,0xA002,0x69AB,0x69BB,0xA002,0x69A7,0x699B,0xA004,0xA002,0x6979,0x6963,0xA002,
+ 0x6966,0x6989,0xA008,0xA004,0xA002,0x69CE,0x6988,0xA002,0x6987,0x69CC,0xA004,0xA002,0x6934,0x6978,0xA002,0x6998,
+ 0x6980,0xA010,0xA008,0xA004,0xA002,0x696B,0x6984,0xA002,0x695D,0x6942,0xA004,0xA002,0x6960,0x6939,0xA002,0x6971,
+ 0x6910,0xA008,0xA004,0xA002,0x68E3,0x6957,0xA002,0x6901,0x690B,0xA004,0xA002,0x68F0,0x6924,0xA002,0x95CB,0x95CA,
+ 0xA020,0xA010,0xA008,0xA004,0xA002,0x95C9,0x95C8,0xA002,0x95C7,0x95C6,0xA004,0xA002,0x95C5,0x95C4,0xA002,0x95C3,
+ 0x95C2,0xA008,0xA004,0xA002,0x95C1,0x95C0,0xA002,0x95BF,0x95BE,0xA004,0xA002,0x95BD,0x95BC,0xA002,0x95BB,0x95BA,
+ 0xA010,0xA008,0xA004,0xA002,0x95B9,0x95B8,0xA002,0x95B7,0x95B6,0xA004,0xA002,0x95B5,0x95B4,0xA002,0x95B3,0x95B2,
+ 0xA008,0xA004,0xA002,0x95B1,0x95B0,0xA002,0x95AF,0x95AE,0xA004,0xA002,0x95AD,0x95AC,0xA000,0x95AB,0xA07F,0xA000,
+ 0xA03E,0xA01E,0xA00E,0xA006,0xA002,0x95AA,0xA002,0x95A9,0x95A8,0xA004,0xA002,0x95A7,0x95A6,0xA002,0x95A5,0x95A4,
+ 0xA008,0xA004,0xA002,0x95A3,0x95A2,0xA002,0x95A1,0x95A0,0xA004,0xA002,0x959F,0x959E,0xA002,0x959D,0x959C,0xA010,
+ 0xA008,0xA004,0xA002,0x959B,0x959A,0xA002,0x9599,0x9598,0xA004,0xA002,0x9597,0x9596,0xA002,0x9595,0x9594,0xA008,
+ 0xA004,0xA002,0x9593,0x9592,0xA002,0x9591,0x9590,0xA004,0xA002,0x958F,0x958E,0xA002,0x958D,0x958C,0xA020,0xA010,
+ 0xA008,0xA004,0xA002,0x958B,0x958A,0xA002,0x9589,0x9588,0xA004,0xA002,0x9587,0x9586,0xA002,0x9585,0x9584,0xA008,
+ 0xA004,0xA002,0x9583,0x9582,0xA002,0x9581,0x9580,0xA004,0xA002,0x957E,0x957D,0xA002,0x957C,0x957B,0xA010,0xA008,
+ 0xA004,0xA002,0x957A,0x9579,0xA002,0x9578,0x9577,0xA004,0xA002,0x9575,0x9574,0xA002,0x956E,0x9560,0xA008,0xA004,
+ 0xA002,0x955A,0x9555,0xA002,0x954B,0x9548,0xA004,0xA002,0x9543,0x953D,0xA002,0x9533,0x9527,0xA07D,0xA03D,0xA01D,
+ 0xA00D,0xA005,0xA001,0xA002,0x68F9,0x6920,0xA004,0xA002,0x691F,0x68FC,0xA002,0x696E,0x68C2,0xA008,0xA004,0xA002,
+ 0x686B,0x6893,0xA002,0x6877,0x6874,0xA004,0xA002,0x688F,0x68B5,0xA002,0x6829,0x6849,0xA010,0xA008,0xA004,0xA002,
+ 0x684A,0x683E,0xA002,0x6840,0x6867,0xA004,0xA002,0x6841,0x6866,0xA002,0x6855,0x681D,0xA008,0xA004,0xA002,0x6883,
+ 0x6864,0xA002,0x6844,0x6862,0xA004,0xA002,0x684E,0x6861,0xA002,0x6860,0x6833,0xA020,0xA010,0xA008,0xA004,0xA002,
+ 0x6832,0x67FD,0xA002,0x67C1,0x680E,0xA004,0xA002,0x67E2,0x67B8,0xA002,0x67C3,0x6800,0xA008,0xA004,0xA002,0x67DD,
+ 0x67B3,0xA002,0x67DA,0x67B5,0xA004,0xA002,0x67D9,0x680C,0xA002,0x67B0,0x67E9,0xA010,0xA008,0xA004,0xA002,0x680A,
+ 0x67D8,0xA002,0x6809,0x67F0,0xA004,0xA002,0x677C,0x6777,0xA002,0x678B,0x67AD,0xA008,0xA004,0xA002,0x679E,0x67A8,
+ 0xA002,0x6775,0x67A7,0xA004,0xA002,0x6798,0x6773,0xA002,0x676A,0x6787,0xA040,0xA020,0xA010,0xA008,0xA004,0xA002,
+ 0x67A5,0x6769,0xA002,0x6748,0x675E,0xA004,0xA002,0x6753,0x674C,0xA002,0x97EC,0x97EB,0xA008,0xA004,0xA002,0x97EA,
+ 0x74BA,0xA002,0x74D2,0x74A7,0xA004,0xA002,0x7490,0x74A9,0xA002,0x74A8,0x749E,0xA010,0xA008,0xA004,0xA002,0x748B,
+ 0x7487,0xA002,0x7481,0x7480,0xA004,0xA002,0x748E,0x749C,0xA002,0x747E,0x746D,0xA008,0xA004,0xA002,0x7477,0x7459,
+ 0xA002,0x7455,0x7457,0xA004,0xA002,0x745C,0x7441,0xA002,0x741A,0x741B,0xA020,0xA010,0xA008,0xA004,0xA002,0x9520,
+ 0x951C,0xA002,0x94FB,0x94E6,0xA004,0xA002,0x94DA,0x94D4,0xA002,0x94D3,0x94CF,0xA008,0xA004,0xA002,0x94C7,0x9498,
+ 0xA002,0x9496,0x9491,0xA004,0xA002,0x9484,0x9483,0xA002,0x9482,0x9481,0xA010,0xA008,0xA004,0xA002,0x9480,0x947F,
+ 0xA002,0x947E,0x947D,0xA004,0xA002,0x947C,0x947B,0xA002,0x947A,0x9479,0xA008,0xA004,0xA002,0x9478,0x9477,0xA002,
+ 0x9476,0x9475,0xA004,0xA002,0x9474,0x9473,0xA002,0x9472,0x9471,0xA17F,0xA086,0xA07D,0xA03D,0xA01D,0xA00D,0xA005,
+ 0xA002,0x9470,0xA001,0x946F,0xA004,0xA002,0x946E,0x946D,0xA002,0x946C,0x946A,0xA008,0xA004,0xA002,0x9469,0x9468,
+ 0xA002,0x9467,0x9466,0xA004,0xA002,0x9465,0x9464,0xA002,0x9463,0x9462,0xA010,0xA008,0xA004,0xA002,0x9461,0x9460,
+ 0xA002,0x945F,0x945E,0xA004,0xA002,0x945D,0x945C,0xA002,0x945B,0x945A,0xA008,0xA004,0xA002,0x9459,0x9458,0xA002,
+ 0x9457,0x9456,0xA004,0xA002,0x9455,0x9454,0xA002,0x9453,0x9452,0xA020,0xA010,0xA008,0xA004,0xA002,0x9451,0x9450,
+ 0xA002,0x944F,0x944E,0xA004,0xA002,0x944D,0x944C,0xA002,0x944B,0x944A,0xA008,0xA004,0xA002,0x9449,0x9448,0xA002,
+ 0x9447,0x9446,0xA004,0xA002,0x9445,0x9444,0xA002,0x9443,0x9442,0xA010,0xA008,0xA004,0xA002,0x9441,0x9440,0xA002,
+ 0x943F,0x943D,0xA004,0xA002,0x943C,0x943B,0xA002,0x943A,0x9439,0xA008,0xA004,0xA002,0x9438,0x9437,0xA002,0x9436,
+ 0x9435,0xA004,0xA002,0x9434,0x9433,0xA002,0x9432,0x9431,0xA000,0xA000,0xA000,0xA000,0xA000,0xA002,0x9430,0x942F,
+ 0xA079,0xA039,0xA019,0xA009,0xA001,0xA004,0xA002,0x742C,0x742E,0xA002,0x7430,0x7428,0xA008,0xA004,0xA002,0x7425,
+ 0x7426,0xA002,0x745B,0x742A,0xA004,0xA002,0x740F,0x73F2,0xA002,0x73BA,0x73DE,0xA010,0xA008,0xA004,0xA002,0x73E7,
+ 0x73E9,0xA002,0x740A,0x987C,0xA004,0xA002,0x73D9,0x73E5,0xA002,0x73C8,0x73C9,0xA008,0xA004,0xA002,0x73C0,0x73B3,
+ 0xA002,0x73B7,0x73D1,0xA004,0xA002,0x73C2,0x73CF,0xA002,0x739F,0x73A2,0xA020,0xA010,0xA008,0xA004,0xA002,0x73AE,
+ 0x7391,0xA002,0x738E,0x9095,0xA004,0xA002,0x753E,0x5DDB,0xA002,0x757F,0x5E7A,0xA008,0xA004,0xA002,0x7F35,0x7F33,
+ 0xA002,0x7F32,0x7F31,0xA004,0xA002,0x7F30,0x7F2F,0xA002,0x7F2D,0x7F2C,0xA010,0xA008,0xA004,0xA002,0x7F2B,0x7F2A,
+ 0xA002,0x7F27,0x7F26,0xA004,0xA002,0x7F25,0x7F24,0xA002,0x7F23,0x7F22,0xA008,0xA004,0xA002,0x7F21,0x7F1F,0xA002,
+ 0x7F1B,0x7F1C,0xA004,0xA002,0x7F19,0x7F17,0xA002,0x7F12,0x7F11,0xA040,0xA020,0xA010,0xA008,0xA004,0xA002,0x7F0F,
+ 0x7F0C,0xA002,0x7F0B,0x7F08,0xA004,0xA002,0x7F07,0x7F03,0xA002,0x7F02,0x7F01,0xA008,0xA004,0xA002,0x7EFE,0x7EFB,
+ 0xA002,0x7EFA,0x7EF6,0xA004,0xA002,0x7F0D,0x7EF2,0xA002,0x7EF1,0x7EEF,0xA010,0xA008,0xA004,0xA002,0x7EEE,0x7EEB,
+ 0xA002,0x7EE8,0x7EE1,0xA004,0xA002,0x7EE0,0x7EDB,0xA002,0x7ED7,0x7ED4,0xA008,0xA004,0xA002,0x7ED0,0x7ECC,0xA002,
+ 0x7ECB,0x7EC9,0xA004,0xA002,0x7EC2,0x7EC1,0xA002,0x7EC0,0x7EBE,0xA020,0xA010,0xA008,0xA004,0xA002,0x7EB0,0x7EAD,
+ 0xA002,0x942E,0x942D,0xA004,0xA002,0x942C,0x942B,0xA002,0x942A,0x9429,0xA008,0xA004,0xA002,0x9428,0x9427,0xA002,
+ 0x9426,0x9425,0xA004,0xA002,0x9424,0x9423,0xA002,0x9422,0x9421,0xA010,0xA008,0xA004,0xA002,0x9420,0x941F,0xA002,
+ 0x941E,0x941D,0xA004,0xA002,0x941C,0x941B,0xA002,0x941A,0x9419,0xA008,0xA004,0xA002,0x9418,0x9417,0xA002,0x9416,
+ 0x9415,0xA004,0xA002,0x9414,0x9413,0xA002,0x9412,0x9411,0xA089,0xA07D,0xA03D,0xA01D,0xA00D,0xA006,0xA002,0x9410,
+ 0xA002,0x940F,0x940E,0xA003,0xA001,0x940D,0xA002,0x940C,0x940B,0xA008,0xA004,0xA002,0x940A,0x9409,0xA002,0x9408,
+ 0x9407,0xA004,0xA002,0x9406,0x9405,0xA002,0x9404,0x9403,0xA010,0xA008,0xA004,0xA002,0x9402,0x9401,0xA002,0x9400,
+ 0x93FF,0xA004,0xA002,0x93FE,0x93FD,0xA002,0x93FC,0x93FB,0xA008,0xA004,0xA002,0x93FA,0x93F9,0xA002,0x93F8,0x93F7,
+ 0xA004,0xA002,0x93F6,0x93F5,0xA002,0x93F4,0x93F3,0xA020,0xA010,0xA008,0xA004,0xA002,0x93F2,0x93F1,0xA002,0x93F0,
+ 0x93EF,0xA004,0xA002,0x93EE,0x93ED,0xA002,0x93EC,0x93EB,0xA008,0xA004,0xA002,0x93EA,0x93E9,0xA002,0x93E8,0x93E7,
+ 0xA004,0xA002,0x93E6,0x93E5,0xA002,0x93E4,0x93E3,0xA010,0xA008,0xA004,0xA002,0x93E2,0x93E1,0xA002,0x93E0,0x93DF,
+ 0xA004,0xA002,0x93DE,0x93DD,0xA002,0x93DC,0x93DB,0xA008,0xA004,0xA002,0x93DA,0x93D9,0xA002,0x93D8,0x93D7,0xA004,
+ 0xA002,0x93D5,0x93D4,0xA002,0x93D3,0x93D2,0xA000,0xA000,0xA000,0xA000,0xA004,0xA002,0x93D1,0x93D0,0xA002,0x93CF,
+ 0x93CE,0xA076,0xA036,0xA016,0xA006,0xA001,0xA001,0xA002,0x7EA9,0x7EA8,0xA008,0xA004,0xA002,0x7EA5,0x7EA3,0xA002,
+ 0x7EA1,0x7E9F,0xA004,0xA002,0x9AA7,0x9AA5,0xA002,0x9AA3,0x9AA2,0xA010,0xA008,0xA004,0xA002,0x9AA0,0x9A9F,0xA002,
+ 0x9A9D,0x9A9C,0xA004,0xA002,0x9A9B,0x9A98,0xA002,0x9A96,0x9A93,0xA008,0xA004,0xA002,0x9A92,0x9A90,0xA002,0x9A8A,
+ 0x9A88,0xA004,0xA002,0x9A85,0x9A81,0xA002,0x9A80,0x9A7D,0xA020,0xA010,0xA008,0xA004,0xA002,0x9A7F,0x9A7A,0xA002,
+ 0x9A78,0x9A77,0xA004,0xA002,0x9A75,0x5B62,0xA002,0x5B53,0x5B51,0xA008,0xA004,0xA002,0x5B73,0x5B65,0xA002,0x5B5A,
+ 0x5C1C,0xA004,0xA002,0x5C15,0x5B40,0xA002,0x5B37,0x5B32,0xA010,0xA008,0xA004,0xA002,0x5B16,0x5B17,0xA002,0x5B09,
+ 0x5ADC,0xA004,0xA002,0x5AD8,0x5AE6,0xA002,0x5AD6,0x5AF1,0xA008,0xA004,0xA002,0x5AE3,0x5AE0,0xA002,0x5AB8,0x5AD4,
+ 0xA004,0xA002,0x5AD2,0x5AB2,0xA002,0x5AEB,0x5ABE,0xA040,0xA020,0xA010,0xA008,0xA004,0xA002,0x5A7A,0x5A77,0xA002,
+ 0x5A9B,0x5AAA,0xA004,0xA002,0x80EC,0x5A75,0xA002,0x5A62,0x5A3C,0xA008,0xA004,0xA002,0x5A55,0x5A4A,0xA002,0x5A67,
+ 0x5A40,0xA004,0xA002,0x5A13,0x5A23,0xA002,0x5A11,0x5A34,0xA010,0xA008,0xA004,0xA002,0x5A32,0x5A09,0xA002,0x5A0C,
+ 0x59F9,0xA004,0xA002,0x59D8,0x59E3,0xA002,0x5A08,0x59DD,0xA008,0xA004,0xA002,0x5A06,0x5A05,0xA002,0x59BE,0x59D7,
+ 0xA004,0xA002,0x59AF,0x59B2,0xA002,0x59D2,0x59A4,0xA020,0xA010,0xA008,0xA004,0xA002,0x599E,0x59AB,0xA002,0x59CA,
+ 0x5997,0xA004,0xA002,0x93CD,0x93CC,0xA002,0x93CB,0x93C9,0xA008,0xA004,0xA002,0x93C8,0x93C7,0xA002,0x93C6,0x93C5,
+ 0xA004,0xA002,0x93C4,0x93C3,0xA002,0x93C2,0x93C1,0xA010,0xA008,0xA004,0xA002,0x93C0,0x93BF,0xA002,0x93BE,0x93BD,
+ 0xA004,0xA002,0x93BC,0x93BB,0xA002,0x93BA,0x93B9,0xA008,0xA004,0xA002,0x93B8,0x93B7,0xA002,0x93B6,0x93B5,0xA004,
+ 0xA002,0x93B4,0x93B3,0xA002,0x93B2,0x93B1,0xA5FF,0xA2FE,0xA17D,0xA08D,0xA07D,0xA03D,0xA01D,0xA00D,0xA006,0xA002,
+ 0x93B0,0xA002,0x93AF,0x93AE,0xA004,0xA002,0x93AD,0x93AC,0xA001,0x93AB,0xA008,0xA004,0xA002,0x93AA,0x93A9,0xA002,
+ 0x93A8,0x93A7,0xA004,0xA002,0x93A6,0x93A5,0xA002,0x93A4,0x93A3,0xA010,0xA008,0xA004,0xA002,0x93A2,0x93A1,0xA002,
+ 0x93A0,0x939F,0xA004,0xA002,0x939E,0x939D,0xA002,0x939C,0x939B,0xA008,0xA004,0xA002,0x939A,0x9399,0xA002,0x9398,
+ 0x9397,0xA004,0xA002,0x9396,0x9395,0xA002,0x9394,0x9393,0xA020,0xA010,0xA008,0xA004,0xA002,0x9392,0x9391,0xA002,
+ 0x9390,0x938E,0xA004,0xA002,0x938D,0x938C,0xA002,0x938B,0x938A,0xA008,0xA004,0xA002,0x9389,0x9388,0xA002,0x9387,
+ 0x9386,0xA004,0xA002,0x9385,0x9384,0xA002,0x9383,0x9382,0xA010,0xA008,0xA004,0xA002,0x9381,0x9380,0xA002,0x937F,
+ 0x937E,0xA004,0xA002,0x937D,0x937C,0xA002,0x937B,0x937A,0xA008,0xA004,0xA002,0x9379,0x9378,0xA002,0x9377,0x9376,
+ 0xA004,0xA002,0x9375,0x9374,0xA002,0x9373,0x9372,0xA000,0xA000,0xA000,0xA006,0xA002,0x9371,0xA002,0x9370,0x936F,
+ 0xA004,0xA002,0x936E,0x936D,0xA000,0x936C,0xA070,0xA030,0xA010,0xA001,0xA007,0xA003,0xA001,0x59A3,0xA002,0x59AA,
+ 0x59A9,0xA004,0xA002,0x598D,0x5983,0xA002,0x5981,0x5C6E,0xA010,0xA008,0xA004,0xA002,0x9B3B,0x5F3C,0xA002,0x8274,
+ 0x5F2D,0xA004,0xA002,0x5F29,0x5F2A,0xA002,0x7FBC,0x5C66,0xA008,0xA004,0xA002,0x5C63,0x5B71,0xA002,0x5C59,0x5C50,
+ 0xA004,0xA002,0x54AB,0x5C3B,0xA002,0x5F58,0x5F56,0xA020,0xA010,0xA008,0xA004,0xA002,0x5F57,0x5F50,0xA002,0x908B,
+ 0x9083,0xA004,0xA002,0x9088,0x9082,0xA002,0x907D,0x9074,0xA008,0xA004,0xA002,0x66B9,0x905B,0xA002,0x9062,0x9058,
+ 0xA004,0xA002,0x9068,0x9050,0xA002,0x9052,0x9051,0xA010,0xA008,0xA004,0xA002,0x9044,0x902F,0xA002,0x902D,0x9036,
+ 0xA004,0xA002,0x9035,0x9021,0xA002,0x9016,0x900D,0xA008,0xA004,0xA002,0x9011,0x9026,0xA002,0x900B,0x9004,0xA004,
+ 0xA002,0x9005,0x8FE8,0xA002,0x8FF3,0x8FE6,0xA040,0xA020,0xA010,0xA008,0xA004,0xA002,0x8FE9,0x8FE4,0xA002,0x8FEE,
+ 0x8FE5,0xA004,0xA002,0x8FD5,0x8FD3,0xA002,0x8FB6,0x8B07,0xA008,0xA004,0xA002,0x8E47,0x5BF0,0xA002,0x8930,0x5BEE,
+ 0xA004,0xA002,0x5BE4,0x6434,0xA002,0x9A9E,0x752F,0xA010,0xA008,0xA004,0xA002,0x5BB8,0x5BA5,0xA002,0x5B93,0x5B95,
+ 0xA004,0xA002,0x5B84,0x5B80,0xA002,0x705E,0x704F,0xA008,0xA004,0xA002,0x7035,0x7039,0xA002,0x701B,0x7023,0xA004,
+ 0xA002,0x701A,0x6FEF,0xA002,0x6FE0,0x6FDE,0xA020,0xA010,0xA008,0xA004,0xA002,0x6FEE,0x6FE1,0xA002,0x6FC2,0x6FB6,
+ 0xA004,0xA002,0x6FB9,0x6FA7,0xA002,0x6FC9,0x936B,0xA008,0xA004,0xA002,0x9369,0x9368,0xA002,0x9367,0x9366,0xA004,
+ 0xA002,0x9365,0x9364,0xA002,0x9363,0x9362,0xA010,0xA008,0xA004,0xA002,0x9361,0x9360,0xA002,0x935F,0x935E,0xA004,
+ 0xA002,0x935D,0x935C,0xA002,0x935B,0x935A,0xA008,0xA004,0xA002,0x9359,0x9358,0xA002,0x9357,0x9356,0xA004,0xA002,
+ 0x9355,0x9354,0xA002,0x9353,0x9352,0xA094,0xA07D,0xA03D,0xA01D,0xA00E,0xA006,0xA002,0x9351,0xA002,0x9350,0x934F,
+ 0xA004,0xA002,0x934E,0x934D,0xA002,0x934C,0x934B,0xA007,0xA003,0xA000,0x934A,0xA002,0x9349,0x9348,0xA004,0xA002,
+ 0x9347,0x9346,0xA002,0x9345,0x9344,0xA010,0xA008,0xA004,0xA002,0x9343,0x9342,0xA002,0x9341,0x9340,0xA004,0xA002,
+ 0x933F,0x933D,0xA002,0x933C,0x933B,0xA008,0xA004,0xA002,0x933A,0x9339,0xA002,0x9338,0x9337,0xA004,0xA002,0x9336,
+ 0x9335,0xA002,0x9334,0x9333,0xA020,0xA010,0xA008,0xA004,0xA002,0x9332,0x9331,0xA002,0x9330,0x932F,0xA004,0xA002,
+ 0x932E,0x932D,0xA002,0x932C,0x932B,0xA008,0xA004,0xA002,0x932A,0x9329,0xA002,0x9328,0x9327,0xA004,0xA002,0x9326,
+ 0x9325,0xA002,0x9324,0x9323,0xA010,0xA008,0xA004,0xA002,0x9322,0x9321,0xA002,0x9320,0x931F,0xA004,0xA002,0x931E,
+ 0x931D,0xA002,0x931C,0x931B,0xA008,0xA004,0xA002,0x931A,0x9319,0xA002,0x9318,0x9317,0xA004,0xA002,0x9316,0x9315,
+ 0xA002,0x9314,0x9313,0xA000,0xA000,0xA010,0xA008,0xA004,0xA002,0x9312,0x9311,0xA002,0x9310,0x930F,0xA004,0xA002,
+ 0x930E,0x930D,0xA002,0x930C,0x930B,0xA000,0xA000,0xA000,0x930A,0xA06D,0xA02D,0xA00D,0xA001,0xA004,0xA001,0xA001,
+ 0x6FD1,0xA004,0xA002,0x6F7A,0x6F7C,0xA002,0x6F72,0x6F78,0xA010,0xA008,0xA004,0xA002,0x6F8C,0x6F8D,0xA002,0x6F89,
+ 0x6F29,0xA004,0xA002,0x6F09,0x6F2A,0xA002,0x6F74,0x6F4B,0xA008,0xA004,0xA002,0x6F36,0x6F2F,0xA002,0x6EF9,0x6F15,
+ 0xA004,0xA002,0x6F24,0x6F47,0xA002,0x6F46,0x6F62,0xA020,0xA010,0xA008,0xA004,0xA002,0x6E9F,0x6EC2,0xA002,0x6E8F,
+ 0x6ECF,0xA004,0xA002,0x6EB4,0x6ED7,0xA002,0x6EB7,0x6EBB,0xA008,0xA004,0xA002,0x6EBD,0x6EA7,0xA002,0x6EA5,0x6EE2,
+ 0xA004,0xA002,0x6F2D,0x6EE0,0xA002,0x6E98,0x6EB1,0xA010,0xA008,0xA004,0xA002,0x6EDF,0x6E44,0xA002,0x6E25,0x6E32,
+ 0xA004,0xA002,0x6E54,0x6E53,0xA002,0x6E86,0x6E5F,0xA008,0xA004,0xA002,0x6EB2,0x6E6B,0xA002,0x6E4E,0x6E6E,0xA004,
+ 0xA002,0x6E2B,0x6DAE,0xA002,0x6E0C,0x6DAB,0xA040,0xA020,0xA010,0xA008,0xA004,0xA002,0x6E16,0x6DD9,0xA002,0x6DDD,
+ 0x6DE6,0xA004,0xA002,0x6E11,0x6DE0,0xA002,0x6DBF,0x6E0E,0xA008,0xA004,0xA002,0x6DDE,0x6DC5,0xA002,0x6DC7,0x6E1A,
+ 0xA004,0xA002,0x6D63,0x6D7C,0xA002,0x6D60,0x6D5C,0xA010,0xA008,0xA004,0xA002,0x6D94,0x6D93,0xA002,0x6D5E,0x6DA0,
+ 0xA004,0xA002,0x6D9E,0x6D6F,0xA002,0x6D91,0x6D33,0xA008,0xA004,0xA002,0x6D54,0x6D52,0xA002,0x6D4F,0x6D1A,0xA004,
+ 0xA002,0x6D35,0x6D2E,0xA002,0x6D4D,0x6D2B,0xA020,0xA010,0xA008,0xA004,0xA002,0x6D0E,0x6D19,0xA002,0x6D04,0x6D07,
+ 0xA004,0xA002,0x6D48,0x6D43,0xA002,0x6D0C,0x6D27,0xA008,0xA004,0xA002,0x6D39,0x9309,0xA002,0x9308,0x9307,0xA004,
+ 0xA002,0x9306,0x9305,0xA002,0x9304,0x9303,0xA010,0xA008,0xA004,0xA002,0x9302,0x9301,0xA002,0x9300,0x92FF,0xA004,
+ 0xA002,0x92FE,0x92FD,0xA002,0x92FC,0x92FB,0xA008,0xA004,0xA002,0x92FA,0x92F9,0xA002,0x92F8,0x92F7,0xA004,0xA002,
+ 0x92F6,0x92F5,0xA002,0x92F4,0x92F3,0xA180,0xA097,0xA07D,0xA03D,0xA01D,0xA00E,0xA006,0xA002,0x92F2,0xA002,0x92F1,
+ 0x92F0,0xA004,0xA002,0x92EF,0x92EE,0xA002,0x92ED,0x92EC,0xA007,0xA004,0xA002,0x92EB,0x92EA,0xA000,0x92E9,0xA004,
+ 0xA002,0x92E8,0x92E7,0xA002,0x92E6,0x92E5,0xA010,0xA008,0xA004,0xA002,0x92E4,0x92E3,0xA002,0x92E2,0x92E1,0xA004,
+ 0xA002,0x92E0,0x92DF,0xA002,0x92DE,0x92DD,0xA008,0xA004,0xA002,0x92DC,0x92DB,0xA002,0x92DA,0x92D9,0xA004,0xA002,
+ 0x92D8,0x92D7,0xA002,0x92D6,0x92D5,0xA020,0xA010,0xA008,0xA004,0xA002,0x92D4,0x92D3,0xA002,0x92D2,0x92D1,0xA004,
+ 0xA002,0x92D0,0x92CF,0xA002,0x92CE,0x92CD,0xA008,0xA004,0xA002,0x92CC,0x92CB,0xA002,0x92CA,0x92C9,0xA004,0xA002,
+ 0x92C7,0x92C6,0xA002,0x92C5,0x92C4,0xA010,0xA008,0xA004,0xA002,0x92C3,0x92C2,0xA002,0x92C1,0x92C0,0xA004,0xA002,
+ 0x92BF,0x92BE,0xA002,0x92BD,0x92BC,0xA008,0xA004,0xA002,0x92BB,0x92BA,0xA002,0x92B9,0x92B8,0xA004,0xA002,0x92B7,
+ 0x92B6,0xA002,0x92B5,0x92B4,0xA000,0xA000,0xA010,0xA008,0xA004,0xA002,0x92B3,0x92B2,0xA002,0x92B1,0x92B0,0xA004,
+ 0xA002,0x92AF,0x92AD,0xA002,0x92AC,0x92AB,0xA000,0xA004,0xA002,0x92AA,0x92A9,0xA000,0x92A8,0xA069,0xA029,0xA009,
+ 0xA001,0xA001,0xA003,0xA001,0x6CFE,0xA002,0x6CEF,0x6CD3,0xA010,0xA008,0xA004,0xA002,0x6CB1,0x6CEE,0xA002,0x6CEB,
+ 0x6CFA,0xA004,0xA002,0x6CD6,0x6CE0,0xA002,0x6CB2,0x6CD7,0xA008,0xA004,0xA002,0x6CF1,0x6CF8,0xA002,0x6CF7,0x6CAD,
+ 0xA004,0xA002,0x6CD4,0x6CD0,0xA002,0x6CA9,0x6C86,0xA020,0xA010,0xA008,0xA004,0xA002,0x6C76,0x6C74,0xA002,0x6C69,
+ 0x6C68,0xA004,0xA002,0x6C8C,0x6C94,0xA002,0x6C90,0x6C85,0xA008,0xA004,0xA002,0x6CA3,0x6C4A,0xA002,0x6C5C,0x6C54,
+ 0xA004,0xA002,0x6C35,0x6215,0xA002,0x723F,0x4E2C,0xA010,0xA008,0xA004,0xA002,0x961A,0x9619,0xA002,0x9617,0x9616,
+ 0xA004,0xA002,0x9615,0x9612,0xA002,0x960F,0x960D,0xA008,0xA004,0xA002,0x960C,0x960B,0xA002,0x960A,0x9608,0xA004,
+ 0xA002,0x9606,0x9604,0xA002,0x9603,0x95FE,0xA040,0xA020,0xA010,0xA008,0xA004,0xA002,0x95FC,0x95F6,0xA002,0x95F5,
+ 0x95F3,0xA004,0xA002,0x95F1,0x95EB,0xA002,0x95E9,0x96B3,0xA008,0xA004,0xA002,0x5FDD,0x61F5,0xA002,0x61D4,0x61B7,
+ 0xA004,0xA002,0x61A7,0x6194,0xA002,0x61AC,0x6175,0xA010,0xA008,0xA004,0xA002,0x614A,0x612B,0xA002,0x610E,0x6100,
+ 0xA004,0xA002,0x60F4,0x6123,0xA002,0x6115,0x6126,0xA008,0xA004,0xA002,0x6120,0x60B4,0xA002,0x60DA,0x60C6,0xA004,
+ 0xA002,0x60D8,0x60DD,0xA002,0x60B1,0x60BB,0xA020,0xA010,0xA008,0xA004,0xA002,0x60EC,0x609B,0xA002,0x608C,0x6092,
+ 0xA004,0xA002,0x6083,0x609D,0xA002,0x60AD,0x609A,0xA008,0xA004,0xA002,0x6096,0x607D,0xA002,0x606A,0x92A7,0xA004,
+ 0xA002,0x92A6,0x92A5,0xA002,0x92A4,0x92A3,0xA010,0xA008,0xA004,0xA002,0x92A2,0x92A1,0xA002,0x92A0,0x929F,0xA004,
+ 0xA002,0x929E,0x929D,0xA002,0x929C,0x929B,0xA008,0xA004,0xA002,0x929A,0x9299,0xA002,0x9298,0x9297,0xA004,0xA002,
+ 0x9296,0x9295,0xA002,0x9294,0x9293,0xA09B,0xA07D,0xA03D,0xA01D,0xA00E,0xA006,0xA002,0x9292,0xA002,0x9291,0x9290,
+ 0xA004,0xA002,0x928F,0x928D,0xA002,0x928C,0x928B,0xA008,0xA004,0xA002,0x928A,0x9289,0xA002,0x9288,0x9287,0xA003,
+ 0xA000,0x9286,0xA002,0x9285,0x9284,0xA010,0xA008,0xA004,0xA002,0x9283,0x9282,0xA002,0x9281,0x9280,0xA004,0xA002,
+ 0x927F,0x927E,0xA002,0x927D,0x927C,0xA008,0xA004,0xA002,0x927B,0x927A,0xA002,0x9279,0x9278,0xA004,0xA002,0x9277,
+ 0x9276,0xA002,0x9275,0x9273,0xA020,0xA010,0xA008,0xA004,0xA002,0x9272,0x9271,0xA002,0x9270,0x926F,0xA004,0xA002,
+ 0x926E,0x926D,0xA002,0x926C,0x926B,0xA008,0xA004,0xA002,0x926A,0x9269,0xA002,0x9268,0x9267,0xA004,0xA002,0x9266,
+ 0x9265,0xA002,0x9264,0x9263,0xA010,0xA008,0xA004,0xA002,0x9262,0x9261,0xA002,0x9260,0x925F,0xA004,0xA002,0x925E,
+ 0x925D,0xA002,0x925C,0x925B,0xA008,0xA004,0xA002,0x925A,0x9259,0xA002,0x9258,0x9257,0xA004,0xA002,0x9256,0x9255,
+ 0xA002,0x9254,0x9253,0xA000,0xA000,0xA010,0xA008,0xA004,0xA002,0x9252,0x9251,0xA002,0x9250,0x924F,0xA004,0xA002,
+ 0x924E,0x924D,0xA002,0x924C,0x924B,0xA008,0xA004,0xA002,0x924A,0x9249,0xA002,0x9248,0x9247,0xA000,0xA000,0x9246,
+ 0xA066,0xA026,0xA006,0xA001,0xA001,0xA001,0xA001,0x6042,0xA010,0xA008,0xA004,0xA002,0x607A,0x607B,0xA002,0x6079,
+ 0x6078,0xA004,0xA002,0x6021,0x603F,0xA002,0x600A,0x602B,0xA008,0xA004,0xA002,0x6029,0x600D,0xA002,0x600F,0x601B,
+ 0xA004,0xA002,0x6026,0x6035,0xA002,0x6019,0x5FF8,0xA020,0xA010,0xA008,0xA004,0xA002,0x5FED,0x5FEA,0xA002,0x6006,
+ 0x6005,0xA004,0xA002,0x5FFE,0x5FE4,0xA002,0x5FE1,0x6004,0xA008,0xA004,0xA002,0x5FEE,0x6003,0xA002,0x5FCF,0x5FD6,
+ 0xA004,0xA002,0x5FC9,0x5FC4,0xA002,0x81BA,0x5EEA,0xA010,0xA008,0xA004,0xA002,0x5EE8,0x5EDB,0xA002,0x5ED1,0x5ED2,
+ 0xA004,0xA002,0x8D53,0x5EB3,0xA002,0x5EBE,0x5EB5,0xA008,0xA004,0xA002,0x5EB9,0x5EA0,0xA002,0x5EA5,0x5E96,0xA004,
+ 0xA002,0x5E8B,0x5E91,0xA002,0x5E80,0x9995,0xA040,0xA020,0xA010,0xA008,0xA004,0xA002,0x9994,0x9993,0xA002,0x9991,
+ 0x9990,0xA004,0xA002,0x998D,0x998A,0xA002,0x9987,0x9984,0xA008,0xA004,0xA002,0x9980,0x997D,0xA002,0x9977,0x9974,
+ 0xA004,0xA002,0x996C,0x996B,0xA002,0x996A,0x9969,0xA010,0xA008,0xA004,0xA002,0x9968,0x9967,0xA002,0x9963,0x5902,
+ 0xA004,0xA002,0x5924,0x98E7,0xA002,0x5925,0x821B,0xA008,0xA004,0xA002,0x737E,0x736F,0xA002,0x736C,0x7360,0xA004,
+ 0xA002,0x7357,0x734D,0xA002,0x7350,0x7331,0xA020,0xA010,0xA008,0xA004,0xA002,0x7338,0x732C,0xA002,0x7325,0x7339,
+ 0xA004,0xA002,0x7322,0x7315,0xA002,0x731D,0x731E,0xA008,0xA004,0xA002,0x730A,0x7321,0xA002,0x7313,0x7317,0xA004,
+ 0xA002,0x72FB,0x9245,0xA002,0x9244,0x9243,0xA010,0xA008,0xA004,0xA002,0x9242,0x9241,0xA002,0x9240,0x923F,0xA004,
+ 0xA002,0x923E,0x923D,0xA002,0x923C,0x923B,0xA008,0xA004,0xA002,0x923A,0x9239,0xA002,0x9238,0x9237,0xA004,0xA002,
+ 0x9236,0x9235,0xA002,0x9234,0x9233,0xA2FF,0xA17E,0xA09E,0xA07D,0xA03D,0xA01D,0xA00E,0xA006,0xA002,0x9232,0xA002,
+ 0x9231,0x9230,0xA004,0xA002,0x922F,0x922E,0xA002,0x922D,0x922C,0xA008,0xA004,0xA002,0x922B,0x922A,0xA002,0x9229,
+ 0x9228,0xA004,0xA002,0x9227,0x9226,0xA000,0x9225,0xA010,0xA008,0xA004,0xA002,0x9224,0x9223,0xA002,0x9222,0x9221,
+ 0xA004,0xA002,0x9220,0x921F,0xA002,0x921E,0x921D,0xA008,0xA004,0xA002,0x921C,0x921B,0xA002,0x921A,0x9219,0xA004,
+ 0xA002,0x9218,0x9217,0xA002,0x9216,0x9215,0xA020,0xA010,0xA008,0xA004,0xA002,0x9214,0x9213,0xA002,0x9212,0x9211,
+ 0xA004,0xA002,0x9210,0x920F,0xA002,0x920E,0x920D,0xA008,0xA004,0xA002,0x920C,0x920B,0xA002,0x920A,0x9209,0xA004,
+ 0xA002,0x9208,0x9207,0xA002,0x9206,0x9205,0xA010,0xA008,0xA004,0xA002,0x9204,0x9203,0xA002,0x9202,0x9201,0xA004,
+ 0xA002,0x9200,0x91FF,0xA002,0x91FE,0x91FD,0xA008,0xA004,0xA002,0x91FC,0x91FB,0xA002,0x91FA,0x91F9,0xA004,0xA002,
+ 0x91F8,0x91F7,0xA002,0x91F6,0x91F5,0xA000,0xA000,0xA010,0xA008,0xA004,0xA002,0x91F4,0x91F3,0xA002,0x91F2,0x91F1,
+ 0xA004,0xA002,0x91F0,0x91EF,0xA002,0x91EE,0x91ED,0xA008,0xA004,0xA002,0x91EC,0x91EB,0xA002,0x91EA,0x91E9,0xA004,
+ 0xA002,0x91E8,0x91E7,0xA000,0x91E6,0xA060,0xA020,0xA001,0xA00F,0xA007,0xA003,0xA001,0x72FA,0xA002,0x7303,0x72F3,
+ 0xA004,0xA002,0x7301,0x72F7,0xA002,0x72F4,0x72F2,0xA008,0xA004,0xA002,0x72E9,0x72EF,0xA002,0x72E8,0x72D2,0xA004,
+ 0xA002,0x72CD,0x72CE,0xA002,0x72C1,0x72C3,0xA020,0xA010,0xA008,0xA004,0xA002,0x72B8,0x72B7,0xA002,0x72B4,0x72B0,
+ 0xA004,0xA002,0x72AD,0x5F61,0xA002,0x8862,0x5FBC,0xA008,0xA004,0xA002,0x5FB5,0x5FAD,0xA002,0x5FA8,0x5F9C,0xA004,
+ 0xA002,0x5F99,0x5F95,0xA002,0x5F8C,0x5F89,0xA010,0xA008,0xA004,0xA002,0x5F87,0x5F82,0xA002,0x5F77,0x5F73,0xA004,
+ 0xA002,0x5DC5,0x5DB7,0xA002,0x8C73,0x5D9D,0xA008,0xA004,0xA002,0x5D99,0x5D82,0xA002,0x5D74,0x5D69,0xA004,0xA002,
+ 0x5D4A,0x5D4B,0xA002,0x5D6B,0x5D5D,0xA040,0xA020,0xA010,0xA008,0xA004,0xA002,0x5D6F,0x5D5B,0xA002,0x5D6C,0x5D3D,
+ 0xA004,0xA002,0x5D34,0x5D3E,0xA002,0x5D58,0x5D1B,0xA008,0xA004,0xA002,0x5D06,0x5D1E,0xA002,0x5D24,0x5D2E,0xA004,
+ 0xA002,0x5D26,0x5D27,0xA002,0x5D03,0x5D02,0xA010,0xA008,0xA004,0xA002,0x5CE5,0x5CCB,0xA002,0x5CE4,0x5CD2,0xA004,
+ 0xA002,0x5CC4,0x5CB7,0xA002,0x5CC1,0x5CA3,0xA008,0xA004,0xA002,0x5CB1,0x5CAB,0xA002,0x5CAC,0x5CBD,0xA004,0xA002,
+ 0x5CA2,0x5CB5,0xA002,0x5C9C,0x5C9A,0xA020,0xA010,0xA008,0xA004,0xA002,0x5C91,0x5C99,0xA002,0x5C98,0x5C88,0xA004,
+ 0xA002,0x5C96,0x5C90,0xA002,0x5C8D,0x5C7A,0xA008,0xA004,0xA002,0x5C8C,0x5E61,0xA002,0x5E5E,0x5E5B,0xA004,0xA002,
+ 0x5E54,0x5E44,0xA002,0x5E37,0x91E5,0xA010,0xA008,0xA004,0xA002,0x91E4,0x91E3,0xA002,0x91E2,0x91E1,0xA004,0xA002,
+ 0x91E0,0x91DF,0xA002,0x91DE,0x91DD,0xA008,0xA004,0xA002,0x91DB,0x91DA,0xA002,0x91D9,0x91D8,0xA004,0xA002,0x91D7,
+ 0x91D6,0xA002,0x91D5,0x91D4,0xA0A4,0xA07D,0xA03D,0xA01E,0xA00E,0xA006,0xA002,0x91D3,0xA002,0x91D2,0x91D0,0xA004,
+ 0xA002,0x91CB,0x91C8,0xA002,0x91C6,0x91C5,0xA008,0xA004,0xA002,0x91C4,0x91C3,0xA002,0x91C2,0x91C1,0xA004,0xA002,
+ 0x91C0,0x91BF,0xA002,0x91BE,0x91BD,0xA00F,0xA007,0xA003,0xA000,0x91BC,0xA002,0x91BB,0x91B9,0xA004,0xA002,0x91B8,
+ 0x91B7,0xA002,0x91B6,0x91B3,0xA008,0xA004,0xA002,0x91B2,0x91B1,0xA002,0x91B0,0x91AC,0xA004,0xA002,0x91AB,0x91A9,
+ 0xA002,0x91A8,0x91A7,0xA020,0xA010,0xA008,0xA004,0xA002,0x91A6,0x91A5,0xA002,0x91A4,0x91A1,0xA004,0xA002,0x91A0,
+ 0x919F,0xA002,0x919E,0x919D,0xA008,0xA004,0xA002,0x919C,0x9199,0xA002,0x9198,0x9197,0xA004,0xA002,0x9196,0x9195,
+ 0xA002,0x9194,0x9193,0xA010,0xA008,0xA004,0xA002,0x918F,0x918E,0xA002,0x918A,0x9188,0xA004,0xA002,0x9186,0x9184,
+ 0xA002,0x9183,0x9182,0xA008,0xA004,0xA002,0x9181,0x9180,0xA002,0x917C,0x917B,0xA004,0xA002,0x917A,0x9173,0xA002,
+ 0x916D,0x916B,0xA000,0xA020,0xA010,0xA008,0xA004,0xA002,0x9168,0x9167,0xA002,0x9166,0x9160,0xA004,0xA002,0x915F,
+ 0x915C,0xA002,0x915B,0x9159,0xA008,0xA004,0xA002,0x9158,0x9156,0xA002,0x9155,0x9154,0xA004,0xA002,0x9153,0x9151,
+ 0xA002,0x9148,0x9147,0xA000,0xA000,0xA000,0xA000,0x9145,0xA05D,0xA01D,0xA001,0xA00C,0xA004,0xA001,0xA001,0x5E3C,
+ 0xA004,0xA002,0x5E3B,0x5E31,0xA002,0x5E11,0x5E14,0xA008,0xA004,0xA002,0x5E19,0x5E0F,0xA002,0x571C,0x5709,0xA004,
+ 0xA002,0x570A,0x5704,0xA002,0x56FF,0x56F9,0xA020,0xA010,0xA008,0xA004,0xA002,0x56EB,0x56F5,0xA002,0x56E1,0x56DD,
+ 0xA004,0xA002,0x56D7,0x56D4,0xA002,0x56AF,0x5693,0xA008,0xA004,0xA002,0x5685,0x567C,0xA002,0x567B,0x566B,0xA004,
+ 0xA002,0x5671,0x5664,0xA002,0x5686,0x5654,0xA010,0xA008,0xA004,0xA002,0x564C,0x565C,0xA002,0x5659,0x5662,0xA004,
+ 0xA002,0x564D,0x562C,0xA002,0x5657,0x5639,0xA008,0xA004,0xA002,0x5658,0x562D,0xA002,0x5627,0x5600,0xA004,0xA002,
+ 0x55FE,0x5623,0xA002,0x5624,0x5601,0xA040,0xA020,0xA010,0xA008,0xA004,0xA002,0x560C,0x5608,0xA002,0x561E,0x8F94,
+ 0xA004,0xA002,0x55E4,0x55F5,0xA002,0x55E8,0x55CD,0xA008,0xA004,0xA002,0x55CC,0x55F3,0xA002,0x55F2,0x55E5,0xA004,
+ 0xA002,0x55EF,0x55C4,0xA002,0x55DD,0x55E6,0xA010,0xA008,0xA004,0xA002,0x55D4,0x55EC,0xA002,0x55EB,0x55D1,0xA004,
+ 0xA002,0x561F,0x55C9,0xA002,0x55F7,0x55EA,0xA008,0xA004,0xA002,0x5599,0x5594,0xA002,0x55BE,0x55BD,0xA004,0xA002,
+ 0x55DF,0x557B,0xA002,0x5591,0x55D6,0xA020,0xA010,0xA008,0xA004,0xA002,0x557E,0x559F,0xA002,0x5581,0x5588,0xA004,
+ 0xA002,0x55B9,0x55B1,0xA002,0x5583,0x55D2,0xA008,0xA004,0xA002,0x558B,0x555C,0xA002,0x5530,0x5533,0xA004,0xA002,
+ 0x5577,0x5576,0xA002,0x5575,0x5556,0xA010,0xA008,0xA004,0xA002,0x5537,0x9144,0xA002,0x9142,0x9141,0xA004,0xA002,
+ 0x9140,0x913F,0xA002,0x913E,0x913D,0xA008,0xA004,0xA002,0x913C,0x913B,0xA002,0x913A,0x9138,0xA004,0xA002,0x9137,
+ 0x9136,0xA002,0x9135,0x9134,0xA180,0xA0A7,0xA07D,0xA03D,0xA01E,0xA00E,0xA006,0xA002,0x9133,0xA002,0x9132,0x9130,
+ 0xA004,0xA002,0x912E,0x912D,0xA002,0x912C,0x912B,0xA008,0xA004,0xA002,0x912A,0x9129,0xA002,0x9128,0x9127,0xA004,
+ 0xA002,0x9126,0x9125,0xA002,0x9124,0x9121,0xA00F,0xA007,0xA004,0xA002,0x9120,0x911F,0xA000,0x911D,0xA004,0xA002,
+ 0x911C,0x911B,0xA002,0x911A,0x9118,0xA008,0xA004,0xA002,0x9117,0x9116,0xA002,0x9115,0x9114,0xA004,0xA002,0x9113,
+ 0x9112,0xA002,0x9111,0x9110,0xA020,0xA010,0xA008,0xA004,0xA002,0x910F,0x910E,0xA002,0x910D,0x910C,0xA004,0xA002,
+ 0x910B,0x910A,0xA002,0x9109,0x9108,0xA008,0xA004,0xA002,0x9107,0x9106,0xA002,0x9105,0x9103,0xA004,0xA002,0x9101,
+ 0x9100,0xA002,0x90FF,0x90FC,0xA010,0xA008,0xA004,0xA002,0x90FB,0x90FA,0xA002,0x90F9,0x90F7,0xA004,0xA002,0x90F6,
+ 0x90F5,0xA002,0x90F3,0x90F2,0xA008,0xA004,0xA002,0x90F1,0x90F0,0xA002,0x90EE,0x90EC,0xA004,0xA002,0x90EA,0x90E9,
+ 0xA002,0x90E5,0x90E4,0xA000,0xA020,0xA010,0xA008,0xA004,0xA002,0x90E3,0x90E0,0xA002,0x90DF,0x90DE,0xA004,0xA002,
+ 0x90DA,0x90D9,0xA002,0x90D8,0x90D6,0xA008,0xA004,0xA002,0x90D5,0x90D4,0xA002,0x90D2,0x90CD,0xA004,0xA002,0x90CC,
+ 0x90CB,0xA002,0x90C9,0x90C8,0xA000,0xA000,0xA004,0xA002,0x90C6,0x90C3,0xA000,0x90C2,0xA059,0xA019,0xA001,0xA008,
+ 0xA001,0xA003,0xA001,0x553C,0xA002,0x5550,0x553F,0xA008,0xA004,0xA002,0x5555,0x5541,0xA002,0x556D,0x5549,0xA004,
+ 0xA002,0x55B5,0x558F,0xA002,0x5567,0x552A,0xA020,0xA010,0xA008,0xA004,0xA002,0x5527,0x5511,0xA002,0x550F,0x5523,
+ 0xA004,0xA002,0x5522,0x54F3,0xA002,0x5514,0x54FD,0xA008,0xA004,0xA002,0x5520,0x54E7,0xA002,0x551B,0x54DE,0xA004,
+ 0xA002,0x54CF,0x54DD,0xA002,0x54A4,0x54AA,0xA010,0xA008,0xA004,0xA002,0x54A9,0x54DC,0xA002,0x54DA,0x54D9,0xA004,
+ 0xA002,0x54CC,0x54BF,0xA002,0x54BB,0x54D5,0xA008,0xA004,0xA002,0x54A3,0x5472,0xA002,0x54D4,0x54D3,0xA004,0xA002,
+ 0x54A6,0x54A7,0xA002,0x54D2,0x54B4,0xA040,0xA020,0xA010,0xA008,0xA004,0xA002,0x54C2,0x54AD,0xA002,0x54D0,0x549D,
+ 0xA004,0xA002,0x5466,0x5476,0xA002,0x5484,0x549B,0xA008,0xA004,0xA002,0x549A,0x5464,0xA002,0x5471,0x5477,0xA004,
+ 0xA002,0x5494,0x5482,0xA002,0x5432,0x5423,0xA010,0xA008,0xA004,0xA002,0x5459,0x5457,0xA002,0x5421,0x5443,0xA004,
+ 0xA002,0x5456,0x5454,0xA002,0x5453,0x5452,0xA008,0xA004,0xA002,0x544B,0x5406,0xA002,0x5416,0x5412,0xA004,0xA002,
+ 0x53FB,0x53E8,0xA002,0x53E9,0x53FD,0xA020,0xA010,0xA008,0xA004,0xA002,0x53F1,0x535F,0xA002,0x5F11,0x7519,0xA004,
+ 0xA002,0x5FD2,0x5F0B,0xA002,0x652E,0x6525,0xA008,0xA004,0xA002,0x6509,0x64E2,0xA002,0x64E4,0x64D7,0xA004,0xA002,
+ 0x64D0,0x64C0,0xA002,0x64BA,0x6499,0xA010,0xA008,0xA004,0xA002,0x64B8,0x64B7,0xA002,0x647A,0x90C0,0xA004,0xA002,
+ 0x90BF,0x90BD,0xA002,0x90BC,0x90B7,0xA008,0xA004,0xA002,0x90B2,0x90AD,0xA002,0x90AB,0x90A9,0xA004,0xA002,0x90A8,
+ 0x90A7,0xA002,0x90A5,0x90A4,0xA0AB,0xA07D,0xA03D,0xA01E,0xA00E,0xA006,0xA002,0x90A0,0xA002,0x909F,0x909E,0xA004,
+ 0xA002,0x909C,0x909A,0xA002,0x9098,0x9096,0xA008,0xA004,0xA002,0x9094,0x9092,0xA002,0x9090,0x908F,0xA004,0xA002,
+ 0x908E,0x908D,0xA002,0x908C,0x908A,0xA00F,0xA008,0xA004,0xA002,0x9089,0x9087,0xA002,0x9086,0x9085,0xA003,0xA000,
+ 0x9084,0xA002,0x9081,0x907E,0xA008,0xA004,0xA002,0x907C,0x907B,0xA002,0x907A,0x9079,0xA004,0xA002,0x9078,0x9077,
+ 0xA002,0x9076,0x9073,0xA020,0xA010,0xA008,0xA004,0xA002,0x9072,0x9071,0xA002,0x9070,0x906F,0xA004,0xA002,0x906C,
+ 0x906B,0xA002,0x906A,0x9069,0xA008,0xA004,0xA002,0x9067,0x9066,0xA002,0x9064,0x9061,0xA004,0xA002,0x9060,0x905F,
+ 0xA002,0x905E,0x905D,0xA010,0xA008,0xA004,0xA002,0x905C,0x905A,0xA002,0x9059,0x9056,0xA004,0xA002,0x9055,0x9054,
+ 0xA002,0x904E,0x904C,0xA008,0xA004,0xA002,0x904B,0x904A,0xA002,0x9049,0x9048,0xA004,0xA002,0x9046,0x9045,0xA002,
+ 0x9043,0x9040,0xA000,0xA020,0xA010,0xA008,0xA004,0xA002,0x903F,0x903D,0xA002,0x903A,0x9039,0xA004,0xA002,0x9037,
+ 0x9034,0xA002,0x9033,0x9032,0xA008,0xA004,0xA002,0x9031,0x9030,0xA002,0x902C,0x902B,0xA004,0xA002,0x902A,0x9029,
+ 0xA002,0x9028,0x9027,0xA000,0xA008,0xA004,0xA002,0x9025,0x9024,0xA002,0x9023,0x901C,0xA000,0xA000,0x9019,0xA056,
+ 0xA016,0xA001,0xA005,0xA001,0xA001,0xA001,0x6496,0xA008,0xA004,0xA002,0x646D,0x6484,0xA002,0x645E,0x6421,0xA004,
+ 0xA002,0x6426,0x640C,0xA002,0x6420,0x641B,0xA020,0xA010,0xA008,0xA004,0xA002,0x640B,0x6441,0xA002,0x6445,0x63BE,
+ 0xA004,0xA002,0x63C6,0x6452,0xA002,0x63CE,0x63DE,0xA008,0xA004,0xA002,0x63C4,0x63FF,0xA002,0x63E0,0x63F8,0xA004,
+ 0xA002,0x63F2,0x63BC,0xA002,0x63AE,0x6369,0xA010,0xA008,0xA004,0xA002,0x638A,0x63AC,0xA002,0x636D,0x63B4,0xA004,
+ 0xA002,0x638E,0x637A,0xA002,0x6371,0x63F6,0xA008,0xA004,0xA002,0x63AD,0x6343,0xA002,0x634B,0x6339,0xA004,0xA002,
+ 0x62F6,0x6322,0xA002,0x62EE,0x62D7,0xA040,0xA020,0xA010,0xA008,0xA004,0xA002,0x62DA,0x62CA,0xA002,0x62BB,0x629F,
+ 0xA004,0xA002,0x626A,0x624C,0xA002,0x5C34,0x5C2C,0xA008,0xA004,0xA002,0x5C25,0x5C22,0xA002,0x530F,0x5958,0xA004,
+ 0xA002,0x595A,0x5955,0xA002,0x8037,0x5941,0xA010,0xA008,0xA004,0xA002,0x593C,0x5F08,0xA002,0x5EFE,0x863C,0xA004,
+ 0xA002,0x8616,0x8629,0xA002,0x8605,0x8627,0xA008,0xA004,0xA002,0x85FF,0x85DC,0xA002,0x85C1,0x85D3,0xA004,0xA002,
+ 0x85B0,0x85B7,0xA002,0x85B9,0x8585,0xA020,0xA010,0xA008,0xA004,0xA002,0x859C,0x85AE,0xA002,0x8579,0x858F,0xA004,
+ 0xA002,0x8587,0x85A8,0xA002,0x85A4,0x857B,0xA008,0xA004,0xA002,0x8572,0x8543,0xA002,0x77A2,0x857A,0xA004,0xA002,
+ 0x855E,0x8564,0xA002,0x8568,0x8548,0xA010,0xA008,0xA004,0xA002,0x8559,0x84FC,0xA002,0x84FF,0x853B,0xA004,0xA002,
+ 0x8556,0x9018,0xA002,0x9015,0x9013,0xA008,0xA004,0xA002,0x900E,0x900C,0xA002,0x9008,0x9007,0xA004,0xA002,0x8FFF,
+ 0x8FFE,0xA002,0x8FFC,0x8FFB,0xB7DA,0xABEA,0xA5F6,0xA2FB,0xA17C,0xA0AD,0xA07D,0xA03D,0xA01E,0xA00E,0xA006,0xA002,
+ 0x8FFA,0xA002,0x8FF6,0x8FF5,0xA004,0xA002,0x8FF4,0x8FF2,0xA002,0x8FF1,0x8FEF,0xA008,0xA004,0xA002,0x8FEC,0x8FE7,
+ 0xA002,0x8FE3,0x8FE1,0xA004,0xA002,0x8FE0,0x8FDA,0xA002,0x8FD7,0x8FD6,0xA00F,0xA008,0xA004,0xA002,0x8FD2,0x8FCF,
+ 0xA002,0x8FCD,0x8FCC,0xA004,0xA002,0x8FCB,0x8FCA,0xA000,0x8FC9,0xA008,0xA004,0xA002,0x8FC6,0x8FC3,0xA002,0x8FC0,
+ 0x8FBF,0xA004,0xA002,0x8FBC,0x8FBB,0xA002,0x8FBA,0x8FB8,0xA020,0xA010,0xA008,0xA004,0xA002,0x8FB7,0x8FB5,0xA002,
+ 0x8FB4,0x8FB3,0xA004,0xA002,0x8FB2,0x8FAF,0xA002,0x8FAE,0x8FAD,0xA008,0xA004,0xA002,0x8FAC,0x8FAA,0xA002,0x8FA7,
+ 0x8FA6,0xA004,0xA002,0x8FA5,0x8FA4,0xA002,0x8FA2,0x8FA1,0xA010,0xA008,0xA004,0xA002,0x8FA0,0x8F9D,0xA002,0x8F92,
+ 0x8F8C,0xA004,0xA002,0x8F80,0x8F6A,0xA002,0x8F65,0x8F64,0xA008,0xA004,0xA002,0x8F63,0x8F62,0xA002,0x8F61,0x8F60,
+ 0xA004,0xA002,0x8F5F,0x8F5E,0xA002,0x8F5D,0x8F5C,0xA000,0xA01E,0xA00E,0xA006,0xA002,0x8F5B,0xA002,0x8F5A,0x8F59,
+ 0xA004,0xA002,0x8F58,0x8F57,0xA002,0x8F56,0x8F55,0xA008,0xA004,0xA002,0x8F54,0x8F53,0xA002,0x8F52,0x8F51,0xA004,
+ 0xA002,0x8F50,0x8F4F,0xA002,0x8F4E,0x8F4D,0xA000,0xA008,0xA004,0xA002,0x8F4C,0x8F4B,0xA002,0x8F4A,0x8F49,0xA004,
+ 0xA002,0x8F48,0x8F47,0xA002,0x8F46,0x8F45,0xA04F,0xA00F,0xA001,0xA001,0xA005,0xA001,0xA002,0x853A,0x851F,0xA004,
+ 0xA002,0x8539,0x84F0,0xA002,0x8538,0x750D,0xA020,0xA010,0xA008,0xA004,0xA002,0x850C,0x84E3,0xA002,0x84E5,0x8497,
+ 0xA004,0xA002,0x84B4,0x84B9,0xA002,0x84A1,0x84E0,0xA008,0xA004,0xA002,0x84BA,0x84BF,0xA002,0x84CA,0x84D3,0xA004,
+ 0xA002,0x84BD,0x84E6,0xA002,0x84D0,0x84CD,0xA010,0xA008,0xA004,0xA002,0x84C1,0x846D,0xA002,0x8431,0x848E,0xA004,
+ 0xA002,0x848C,0x8476,0xA002,0x8469,0x8446,0xA008,0xA004,0xA002,0x843C,0x8478,0xA002,0x8489,0x847A,0xA004,0xA002,
+ 0x8488,0x8487,0xA002,0x8473,0x8459,0xA040,0xA020,0xA010,0xA008,0xA004,0xA002,0x845A,0x8451,0xA002,0x845C,0x83E1,
+ 0xA004,0xA002,0x83F0,0x8426,0xA002,0x83C0,0x83C5,0xA008,0xA004,0xA002,0x83EA,0x83F9,0xA002,0x83F8,0x8403,0xA004,
+ 0xA002,0x840F,0x83DF,0xA002,0x83D4,0x8406,0xA010,0xA008,0xA004,0xA002,0x8411,0x8438,0xA002,0x841C,0x83D6,0xA004,
+ 0xA002,0x83FD,0x83DD,0xA002,0x840B,0x8418,0xA008,0xA004,0xA002,0x5807,0x83D8,0xA002,0x83E5,0x8401,0xA004,0xA002,
+ 0x83C1,0x83BC,0xA002,0x83BA,0x83A8,0xA020,0xA010,0xA008,0xA004,0xA002,0x839E,0x8398,0xA002,0x837B,0x83B8,0xA004,
+ 0xA002,0x837D,0x83A9,0xA002,0x83B6,0x837C,0xA008,0xA004,0xA002,0x8385,0x839C,0xA002,0x8393,0x83AA,0xA004,0xA002,
+ 0x83A0,0x83B4,0xA002,0x83B3,0x8378,0xA010,0xA008,0xA004,0xA002,0x83B0,0x836E,0xA002,0x836D,0x836A,0xA004,0xA002,
+ 0x836C,0x8369,0xA002,0x831B,0x8368,0xA008,0xA004,0xA002,0x8F44,0x8F43,0xA002,0x8F42,0x8F41,0xA004,0xA002,0x8F40,
+ 0x8F3F,0xA002,0x8F3E,0x8F3D,0xA0B4,0xA07D,0xA03D,0xA01E,0xA00E,0xA006,0xA002,0x8F3C,0xA002,0x8F3B,0x8F3A,0xA004,
+ 0xA002,0x8F39,0x8F38,0xA002,0x8F37,0x8F36,0xA008,0xA004,0xA002,0x8F35,0x8F34,0xA002,0x8F33,0x8F32,0xA004,0xA002,
+ 0x8F31,0x8F30,0xA002,0x8F2F,0x8F2E,0xA010,0xA008,0xA004,0xA002,0x8F2D,0x8F2C,0xA002,0x8F2B,0x8F2A,0xA004,0xA002,
+ 0x8F29,0x8F28,0xA002,0x8F27,0x8F26,0xA007,0xA004,0xA002,0x8F25,0x8F24,0xA001,0x8F23,0xA004,0xA002,0x8F22,0x8F21,
+ 0xA002,0x8F20,0x8F1F,0xA020,0xA010,0xA008,0xA004,0xA002,0x8F1E,0x8F1D,0xA002,0x8F1C,0x8F1B,0xA004,0xA002,0x8F1A,
+ 0x8F19,0xA002,0x8F18,0x8F17,0xA008,0xA004,0xA002,0x8F16,0x8F15,0xA002,0x8F14,0x8F13,0xA004,0xA002,0x8F12,0x8F11,
+ 0xA002,0x8F10,0x8F0F,0xA010,0xA008,0xA004,0xA002,0x8F0E,0x8F0D,0xA002,0x8F0C,0x8F0B,0xA004,0xA002,0x8F0A,0x8F09,
+ 0xA002,0x8F08,0x8F07,0xA008,0xA004,0xA002,0x8F06,0x8F05,0xA002,0x8F04,0x8F03,0xA004,0xA002,0x8F02,0x8F01,0xA002,
+ 0x8F00,0x8EFF,0xA000,0xA020,0xA010,0xA008,0xA004,0xA002,0x8EFE,0x8EFD,0xA002,0x8EFC,0x8EFB,0xA004,0xA002,0x8EFA,
+ 0x8EF9,0xA002,0x8EF8,0x8EF7,0xA008,0xA004,0xA002,0x8EF6,0x8EF5,0xA002,0x8EF4,0x8EF3,0xA004,0xA002,0x8EF2,0x8EF1,
+ 0xA002,0x8EF0,0x8EEF,0xA010,0xA008,0xA004,0xA002,0x8EEE,0x8EED,0xA002,0x8EEC,0x8EEB,0xA004,0xA002,0x8EEA,0x8EE9,
+ 0xA002,0x8EE8,0x8EE7,0xA000,0xA000,0xA002,0x8EE6,0x8EE5,0xA04B,0xA00B,0xA001,0xA001,0xA001,0xA004,0xA002,0x8365,
+ 0x8366,0xA002,0x8333,0x833A,0xA020,0xA010,0xA008,0xA004,0xA002,0x832D,0x8360,0xA002,0x8317,0x8340,0xA004,0xA002,
+ 0x835F,0x8343,0xA002,0x8347,0x834F,0xA008,0xA004,0xA002,0x832F,0x835E,0xA002,0x839B,0x8331,0xA004,0xA002,0x8334,
+ 0x833C,0xA002,0x8392,0x8308,0xA010,0xA008,0xA004,0xA002,0x835C,0x835B,0xA002,0x8351,0x831C,0xA004,0xA002,0x82D5,
+ 0x82E0,0xA002,0x8315,0x8314,0xA008,0xA004,0xA002,0x8306,0x831A,0xA002,0x8311,0x82D3,0xA004,0xA002,0x82FB,0x830C,
+ 0xA002,0x82D8,0x82D2,0xA040,0xA020,0xA010,0xA008,0xA004,0xA002,0x82F4,0x82DC,0xA002,0x8307,0x830F,0xA004,0xA002,
+ 0x82E4,0x82F7,0xA002,0x8309,0x82E1,0xA008,0xA004,0xA002,0x82A4,0x82CE,0xA002,0x82C4,0x829F,0xA004,0xA002,0x82AA,
+ 0x82A1,0xA002,0x82B4,0x82A9,0xA010,0xA008,0xA004,0xA002,0x82C1,0x82CC,0xA002,0x82CB,0x82AE,0xA004,0xA002,0x82B7,
+ 0x8298,0xA002,0x82E3,0x82CA,0xA008,0xA004,0xA002,0x82C8,0x82B0,0xA002,0x82BE,0x82B8,0xA004,0xA002,0x82AB,0x8299,
+ 0xA002,0x8297,0x8291,0xA020,0xA010,0xA008,0xA004,0xA002,0x828E,0x8284,0xA002,0x82A8,0x828A,0xA004,0xA002,0x828F,
+ 0x827F,0xA002,0x827D,0x8279,0xA008,0xA004,0xA002,0x61FF,0x9F19,0xA002,0x99A8,0x5880,0xA004,0xA002,0x589A,0x5889,
+ 0xA002,0x5881,0x586C,0xA010,0xA008,0xA004,0xA002,0x5865,0x5820,0xA002,0x5844,0x5819,0xA004,0xA002,0x581E,0x5800,
+ 0xA002,0x57ED,0x57FD,0xA008,0xA004,0xA002,0x580D,0x580B,0xA002,0x8EE4,0x8EE3,0xA004,0xA002,0x8EE2,0x8EE1,0xA002,
+ 0x8EE0,0x8EDF,0xA17F,0xA0B7,0xA07D,0xA03D,0xA01E,0xA00E,0xA006,0xA002,0x8EDE,0xA002,0x8EDD,0x8EDC,0xA004,0xA002,
+ 0x8EDB,0x8EDA,0xA002,0x8ED9,0x8ED8,0xA008,0xA004,0xA002,0x8ED7,0x8ED6,0xA002,0x8ED5,0x8ED4,0xA004,0xA002,0x8ED3,
+ 0x8ED2,0xA002,0x8ED1,0x8ED0,0xA010,0xA008,0xA004,0xA002,0x8ECF,0x8ECD,0xA002,0x8ECC,0x8ECB,0xA004,0xA002,0x8ECA,
+ 0x8EC9,0xA002,0x8EC8,0x8EC7,0xA008,0xA004,0xA002,0x8EC6,0x8EC5,0xA002,0x8EC4,0x8EC3,0xA003,0xA001,0x8EC2,0xA002,
+ 0x8EC1,0x8EC0,0xA020,0xA010,0xA008,0xA004,0xA002,0x8EBF,0x8EBE,0xA002,0x8EBD,0x8EBC,0xA004,0xA002,0x8EBB,0x8EB9,
+ 0xA002,0x8EB8,0x8EB7,0xA008,0xA004,0xA002,0x8EB6,0x8EB5,0xA002,0x8EB4,0x8EB3,0xA004,0xA002,0x8EB1,0x8EB0,0xA002,
+ 0x8EAE,0x8EAD,0xA010,0xA008,0xA004,0xA002,0x8EAA,0x8EA9,0xA002,0x8EA8,0x8EA7,0xA004,0xA002,0x8EA6,0x8EA5,0xA002,
+ 0x8EA4,0x8EA3,0xA008,0xA004,0xA002,0x8EA2,0x8EA1,0xA002,0x8EA0,0x8E9F,0xA004,0xA002,0x8E9D,0x8E9B,0xA002,0x8E9A,
+ 0x8E99,0xA000,0xA020,0xA010,0xA008,0xA004,0xA002,0x8E98,0x8E97,0xA002,0x8E96,0x8E95,0xA004,0xA002,0x8E93,0x8E92,
+ 0xA002,0x8E91,0x8E8E,0xA008,0xA004,0xA002,0x8E8D,0x8E8C,0xA002,0x8E8B,0x8E8A,0xA004,0xA002,0x8E89,0x8E88,0xA002,
+ 0x8E86,0x8E84,0xA010,0xA008,0xA004,0xA002,0x8E83,0x8E82,0xA002,0x8E80,0x8E7E,0xA004,0xA002,0x8E7D,0x8E7B,0xA002,
+ 0x8E7A,0x8E79,0xA000,0xA004,0xA002,0x8E78,0x8E77,0xA002,0x8E75,0x8E73,0xA048,0xA008,0xA001,0xA001,0xA001,0xA001,
+ 0xA002,0x57DD,0x57E4,0xA020,0xA010,0xA008,0xA004,0xA002,0x57F8,0x57EF,0xA002,0x57F4,0x57B8,0xA004,0xA002,0x57D2,
+ 0x57D9,0xA002,0x57DA,0x57D8,0xA008,0xA004,0xA002,0x57D5,0x57A0,0xA002,0x5793,0x57B4,0xA004,0xA002,0x57A7,0x57CF,
+ 0xA002,0x57B2,0x578C,0xA010,0xA008,0xA004,0xA002,0x57A4,0x57AD,0xA002,0x5773,0x5776,0xA004,0xA002,0x576D,0x5768,
+ 0xA002,0x577B,0x577C,0xA008,0xA004,0xA002,0x5786,0x576B,0xA002,0x5785,0x5769,0xA004,0xA002,0x5742,0x573B,0xA002,
+ 0x575C,0x572F,0xA040,0xA020,0xA010,0xA008,0xA004,0xA002,0x572E,0x5739,0xA002,0x5733,0x572A,0xA004,0xA002,0x572C,
+ 0x5729,0xA002,0x58D1,0x58C5,0xA008,0xA004,0xA002,0x58BC,0x587E,0xA002,0x57A1,0x57A9,0xA004,0xA002,0x574C,0x5DEF,
+ 0xA002,0x755A,0x5F01,0xA010,0xA008,0xA004,0xA002,0x53B6,0x9B2F,0xA002,0x51FC,0x51F5,0xA004,0xA002,0x5EF4,0x77CD,
+ 0xA002,0x71EE,0x53DF,0xA008,0xA004,0xA002,0x52F0,0x52D6,0xA002,0x52D0,0x54FF,0xA004,0xA002,0x52BE,0x52AD,0xA002,
+ 0x52AC,0x52A2,0xA020,0xA010,0xA008,0xA004,0xA002,0x5942,0x520D,0xA002,0x9146,0x9143,0xA004,0xA002,0x9139,0x912F,
+ 0xA002,0x9131,0x9123,0xA008,0xA004,0xA002,0x911E,0x9122,0xA002,0x9104,0x90FE,0xA004,0xA002,0x90EF,0x90EB,0xA002,
+ 0x90DB,0x90D7,0xA010,0xA008,0xA004,0xA002,0x90DC,0x90E2,0xA002,0x90E6,0x90D3,0xA004,0xA002,0x90C7,0x90C4,0xA002,
+ 0x90D0,0x90BE,0xA008,0xA004,0xA002,0x90C5,0x90CF,0xA002,0x90B0,0x90B8,0xA004,0xA002,0x8E71,0x8E6E,0xA002,0x8E6B,
+ 0x8E6A,0xA0BB,0xA07D,0xA03D,0xA01E,0xA00E,0xA006,0xA002,0x8E68,0xA002,0x8E67,0x8E65,0xA004,0xA002,0x8E64,0x8E63,
+ 0xA002,0x8E62,0x8E61,0xA008,0xA004,0xA002,0x8E60,0x8E5F,0xA002,0x8E5E,0x8E5D,0xA004,0xA002,0x8E5C,0x8E5B,0xA002,
+ 0x8E5A,0x8E58,0xA010,0xA008,0xA004,0xA002,0x8E57,0x8E56,0xA002,0x8E55,0x8E54,0xA004,0xA002,0x8E53,0x8E50,0xA002,
+ 0x8E4F,0x8E4E,0xA008,0xA004,0xA002,0x8E4D,0x8E4C,0xA002,0x8E46,0x8E45,0xA004,0xA002,0x8E43,0x8E3F,0xA001,0x8E3E,
+ 0xA020,0xA010,0xA008,0xA004,0xA002,0x8E3C,0x8E3B,0xA002,0x8E38,0x8E37,0xA004,0xA002,0x8E36,0x8E34,0xA002,0x8E33,
+ 0x8E32,0xA008,0xA004,0xA002,0x8E30,0x8E2D,0xA002,0x8E2B,0x8E28,0xA004,0xA002,0x8E27,0x8E26,0xA002,0x8E25,0x8E24,
+ 0xA010,0xA008,0xA004,0xA002,0x8E21,0x8E20,0xA002,0x8E1C,0x8E1B,0xA004,0xA002,0x8E1A,0x8E19,0xA002,0x8E18,0x8E17,
+ 0xA008,0xA004,0xA002,0x8E16,0x8E15,0xA002,0x8E13,0x8E12,0xA004,0xA002,0x8E11,0x8E10,0xA002,0x8E0E,0x8E0D,0xA000,
+ 0xA020,0xA010,0xA008,0xA004,0xA002,0x8E0B,0x8E08,0xA002,0x8E07,0x8E06,0xA004,0xA002,0x8E04,0x8E03,0xA002,0x8E02,
+ 0x8E01,0xA008,0xA004,0xA002,0x8E00,0x8DFF,0xA002,0x8DFE,0x8DFC,0xA004,0xA002,0x8DF6,0x8DF4,0xA002,0x8DF2,0x8DF1,
+ 0xA010,0xA008,0xA004,0xA002,0x8DF0,0x8DEE,0xA002,0x8DED,0x8DE9,0xA004,0xA002,0x8DE7,0x8DE6,0xA002,0x8DE5,0x8DE2,
+ 0xA008,0xA004,0xA002,0x8DE1,0x8DE0,0xA002,0x8DDC,0x8DD9,0xA000,0xA002,0x8DD8,0x8DD5,0xA041,0xA001,0xA020,0xA010,
+ 0xA008,0xA004,0xA002,0x90BA,0x90B6,0xA002,0x90B3,0x90B4,0xA004,0xA002,0x90A1,0x90AC,0xA002,0x9099,0x909D,0xA008,
+ 0xA004,0xA002,0x909B,0x9097,0xA002,0x96B0,0x9697,0xA004,0xA002,0x968D,0x9688,0xA002,0x9674,0x9672,0xA010,0xA008,
+ 0xA004,0xA002,0x966C,0x9667,0xA002,0x965F,0x9654,0xA004,0xA002,0x9649,0x9642,0xA002,0x963C,0x963D,0xA008,0xA004,
+ 0xA002,0x962A,0x9631,0xA002,0x9621,0x9622,0xA004,0xA002,0x961D,0x537A,0xA002,0x5369,0x8C36,0xA040,0xA020,0xA010,
+ 0xA008,0xA004,0xA002,0x8C35,0x8C33,0xA002,0x8C32,0x8C2F,0xA004,0xA002,0x8C2E,0x8C2B,0xA002,0x8C2A,0x8C27,0xA008,
+ 0xA004,0xA002,0x8C25,0x8C21,0xA002,0x8C20,0x8C1F,0xA004,0xA002,0x8C1D,0x8C18,0xA002,0x8C1B,0x8C19,0xA010,0xA008,
+ 0xA004,0xA002,0x8C16,0x8C15,0xA002,0x8C14,0x8C12,0xA004,0xA002,0x8C11,0x8C0F,0xA002,0x8C0C,0x8C07,0xA008,0xA004,
+ 0xA002,0x8C04,0x8C02,0xA002,0x8C00,0x8BFF,0xA004,0xA002,0x8BFC,0x8BF9,0xA002,0x8BF6,0x8BF3,0xA020,0xA010,0xA008,
+ 0xA004,0xA002,0x8BF0,0x8BEE,0xA002,0x8BE9,0x8BE8,0xA004,0xA002,0x8BE4,0x8BE0,0xA002,0x8BDF,0x8BDC,0xA008,0xA004,
+ 0xA002,0x8BD9,0x8BD8,0xA002,0x8BD6,0x8BD4,0xA004,0xA002,0x8BD3,0x8BD2,0xA002,0x8BCE,0x8BCF,0xA010,0xA008,0xA004,
+ 0xA002,0x8BCB,0x8BC3,0xA002,0x8BC2,0x8BB7,0xA004,0xA002,0x8BB5,0x8BB4,0xA002,0x8BAA,0x8BA7,0xA008,0xA004,0xA002,
+ 0x8BA6,0x8BA0,0xA002,0x51A5,0x51A2,0xA004,0xA002,0x5196,0x51C7,0xA002,0x8DD4,0x8DD3,0xA2FF,0xA17F,0xA0C2,0xA07D,
+ 0xA03E,0xA01E,0xA00E,0xA006,0xA002,0x8DD2,0xA002,0x8DD0,0x8DCD,0xA004,0xA002,0x8DCA,0x8DC9,0xA002,0x8DC8,0x8DC7,
+ 0xA008,0xA004,0xA002,0x8DC5,0x8DC2,0xA002,0x8DC1,0x8DC0,0xA004,0xA002,0x8DBD,0x8DBB,0xA002,0x8DB9,0x8DB7,0xA010,
+ 0xA008,0xA004,0xA002,0x8DB6,0x8DB2,0xA002,0x8DB0,0x8DAF,0xA004,0xA002,0x8DAE,0x8DAD,0xA002,0x8DAC,0x8DAB,0xA008,
+ 0xA004,0xA002,0x8DAA,0x8DA9,0xA002,0x8DA8,0x8DA7,0xA004,0xA002,0x8DA6,0x8DA5,0xA002,0x8DA4,0x8DA2,0xA01F,0xA00F,
+ 0xA007,0xA003,0xA001,0x8DA1,0xA002,0x8DA0,0x8D9E,0xA004,0xA002,0x8D9D,0x8D9C,0xA002,0x8D9B,0x8D9A,0xA008,0xA004,
+ 0xA002,0x8D99,0x8D98,0xA002,0x8D97,0x8D96,0xA004,0xA002,0x8D95,0x8D93,0xA002,0x8D92,0x8D90,0xA010,0xA008,0xA004,
+ 0xA002,0x8D8F,0x8D8E,0xA002,0x8D8D,0x8D8C,0xA004,0xA002,0x8D89,0x8D88,0xA002,0x8D87,0x8D86,0xA008,0xA004,0xA002,
+ 0x8D83,0x8D82,0xA002,0x8D80,0x8D7F,0xA004,0xA002,0x8D7E,0x8D7D,0xA002,0x8D7C,0x8D7B,0xA03E,0xA01E,0xA00E,0xA006,
+ 0xA002,0x8D7A,0xA002,0x8D79,0x8D78,0xA004,0xA002,0x8D72,0x8D71,0xA002,0x8D6F,0x8D6E,0xA008,0xA004,0xA002,0x8D6C,
+ 0x8D6A,0xA002,0x8D69,0x8D68,0xA004,0xA002,0x8D65,0x8D5F,0xA002,0x8D57,0x8D52,0xA010,0xA008,0xA004,0xA002,0x8D51,
+ 0x8D20,0xA002,0x8D1C,0x8D1B,0xA004,0xA002,0x8D1A,0x8D19,0xA002,0x8D18,0x8D17,0xA008,0xA004,0xA002,0x8D16,0x8D15,
+ 0xA002,0x8D14,0x8D13,0xA004,0xA002,0x8D12,0x8D11,0xA002,0x8D10,0x8D0F,0xA000,0xA000,0xA000,0xA000,0xA000,0x8D0E,
+ 0xA03D,0xA001,0xA01C,0xA00C,0xA004,0xA001,0xA001,0x51BC,0xA004,0xA002,0x51BD,0x51B1,0xA002,0x51AB,0x7FB8,0xA008,
+ 0xA004,0xA002,0x8803,0x5B34,0xA002,0x7980,0x88D2,0xA004,0xA002,0x8114,0x4EB5,0xA002,0x88A4,0x886E,0xA010,0xA008,
+ 0xA004,0xA002,0x4EB3,0x5156,0xA002,0x4EA0,0x5155,0xA004,0xA002,0x5919,0x51EB,0xA002,0x5310,0x8A07,0xA008,0xA004,
+ 0xA002,0x530D,0x52F9,0xA002,0x5914,0x5181,0xA004,0xA002,0x9998,0x9EC9,0xA002,0x5DFD,0x516E,0xA040,0xA020,0xA010,
+ 0xA008,0xA004,0xA002,0x7C74,0x6C46,0xA002,0x9FA0,0x4FCE,0xA004,0xA002,0x4F65,0x4F58,0xA002,0x6C3D,0x4EDD,0xA008,
+ 0xA004,0xA002,0x510B,0x5107,0xA002,0x50EE,0x50E6,0xA004,0xA002,0x50EC,0x50ED,0xA002,0x5106,0x50D6,0xA010,0xA008,
+ 0xA004,0xA002,0x50BA,0x50A9,0xA002,0x50A7,0x50A5,0xA004,0xA002,0x507B,0x506C,0xA002,0x504E,0x5048,0xA008,0xA004,
+ 0xA002,0x5055,0x5043,0xA002,0x507E,0x5028,0xA004,0xA002,0x5025,0x500C,0xA002,0x501C,0x4FFE,0xA020,0xA010,0xA008,
+ 0xA004,0xA002,0x502D,0x502E,0xA002,0x500F,0x502C,0xA004,0xA002,0x4FF3,0x504C,0xA002,0x5029,0x4FF8,0xA008,0xA004,
+ 0xA002,0x4FDF,0x4FD1,0xA002,0x4FDC,0x4FE3,0xA004,0xA002,0x4FDA,0x4FC5,0xA002,0x4FEA,0x4FE8,0xA010,0xA008,0xA004,
+ 0xA002,0x4FE6,0x4F94,0xA002,0x4FAC,0x4F7C,0xA004,0xA002,0x4FAA,0x4F7B,0xA002,0x4F7E,0x4F8F,0xA008,0xA004,0xA002,
+ 0x4F83,0x4F89,0xA002,0x4F91,0x4F74,0xA004,0xA002,0x4F76,0x4F3D,0xA002,0x4F32,0x4F57,0xA0C7,0xA07D,0xA03E,0xA01E,
+ 0xA00E,0xA006,0xA002,0x4F5F,0xA002,0x8D0D,0x8D0C,0xA004,0xA002,0x8D0B,0x8D0A,0xA002,0x8D09,0x8D08,0xA008,0xA004,
+ 0xA002,0x8D07,0x8D06,0xA002,0x8D05,0x8D04,0xA004,0xA002,0x8D03,0x8D02,0xA002,0x8D01,0x8D00,0xA010,0xA008,0xA004,
+ 0xA002,0x8CFF,0x8CFE,0xA002,0x8CFD,0x8CFC,0xA004,0xA002,0x8CFB,0x8CFA,0xA002,0x8CF9,0x8CF8,0xA008,0xA004,0xA002,
+ 0x8CF7,0x8CF6,0xA002,0x8CF5,0x8CF4,0xA004,0xA002,0x8CF3,0x8CF2,0xA002,0x8CF1,0x8CF0,0xA01F,0xA00F,0xA007,0xA004,
+ 0xA002,0x8CEF,0x8CEE,0xA000,0x8CED,0xA004,0xA002,0x8CEC,0x8CEB,0xA002,0x8CEA,0x8CE9,0xA008,0xA004,0xA002,0x8CE8,
+ 0x8CE7,0xA002,0x8CE6,0x8CE5,0xA004,0xA002,0x8CE4,0x8CE3,0xA002,0x8CE2,0x8CE1,0xA010,0xA008,0xA004,0xA002,0x8CE0,
+ 0x8CDF,0xA002,0x8CDE,0x8CDD,0xA004,0xA002,0x8CDC,0x8CDB,0xA002,0x8CDA,0x8CD9,0xA008,0xA004,0xA002,0x8CD8,0x8CD7,
+ 0xA002,0x8CD6,0x8CD5,0xA004,0xA002,0x8CD4,0x8CD3,0xA002,0x8CD2,0x8CD1,0xA040,0xA020,0xA010,0xA008,0xA004,0xA002,
+ 0x8CD0,0x8CCF,0xA002,0x8CCE,0x8CCD,0xA004,0xA002,0x8CCC,0x8CCB,0xA002,0x8CCA,0x8CC9,0xA008,0xA004,0xA002,0x8CC8,
+ 0x8CC7,0xA002,0x8CC6,0x8CC5,0xA004,0xA002,0x8CC4,0x8CC3,0xA002,0x8CC2,0x8CC1,0xA010,0xA008,0xA004,0xA002,0x8CC0,
+ 0x8CBF,0xA002,0x8CBE,0x8CBD,0xA004,0xA002,0x8CBC,0x8CBB,0xA002,0x8CBA,0x8CB9,0xA008,0xA004,0xA002,0x8CB8,0x8CB7,
+ 0xA002,0x8CB6,0x8CB5,0xA004,0xA002,0x8CB4,0x8CB3,0xA002,0x8CB2,0x8CB1,0xA000,0xA000,0xA000,0xA004,0xA002,0x8CB0,
+ 0x8CAF,0xA000,0x8CAE,0xA039,0xA001,0xA018,0xA008,0xA001,0xA003,0xA001,0x4F5D,0xA002,0x4F5A,0x6538,0xA008,0xA004,
+ 0xA002,0x4F67,0x4F5E,0xA002,0x4F2B,0x4F09,0xA004,0xA002,0x4F27,0x4F25,0xA002,0x4EF5,0x4F64,0xA010,0xA008,0xA004,
+ 0xA002,0x4F22,0x4EF3,0xA002,0x4F1B,0x4EDE,0xA004,0xA002,0x4EEB,0x4EE1,0xA002,0x4EE8,0x4EC2,0xA008,0xA004,0xA002,
+ 0x4EC9,0x4EC3,0xA002,0x4EBB,0x7F54,0xA004,0xA002,0x5182,0x5293,0xA002,0x5290,0x5281,0xA040,0xA020,0xA010,0xA008,
+ 0xA004,0xA002,0x5282,0x527D,0xA002,0x84AF,0x525C,0xA004,0xA002,0x5261,0x525E,0xA002,0x524C,0x5240,0xA008,0xA004,
+ 0xA002,0x523F,0x5233,0xA002,0x522D,0x520E,0xA004,0xA002,0x5208,0x5202,0xA002,0x5363,0x5366,0xA010,0xA008,0xA004,
+ 0xA002,0x8D5C,0x533E,0xA002,0x532E,0x5326,0xA004,0xA002,0x53F5,0x531A,0xA002,0x8D5D,0x9765,0xA008,0xA004,0xA002,
+ 0x53AE,0x53A5,0xA002,0x53A3,0x539D,0xA004,0xA002,0x538D,0x4EC4,0xA002,0x560F,0x556C,0xA020,0xA010,0xA008,0xA004,
+ 0xA002,0x5B5B,0x8288,0xA002,0x4E93,0x4E69,0xA004,0xA002,0x4E5C,0x9F10,0xA002,0x4E9F,0x4E36,0xA008,0xA004,0xA002,
+ 0x9F17,0x777E,0xA002,0x6BD3,0x9997,0xA004,0xA002,0x80E4,0x56DF,0xA002,0x6C10,0x536E,0xA010,0xA008,0xA004,0xA002,
+ 0x723B,0x592D,0xA002,0x4E47,0x5315,0xA004,0xA002,0x4E3F,0x79BA,0xA002,0x4E28,0x5669,0xA008,0xA004,0xA002,0x5B6C,
+ 0x9B32,0xA002,0x4E1E,0x4E98,0xA004,0xA002,0x4E15,0x5345,0xA002,0x5EFF,0x4E10,0xA176,0xA0CB,0xA07D,0xA03E,0xA01E,
+ 0xA00E,0xA006,0xA002,0x5140,0xA002,0x4E0C,0x4E8D,0xA004,0xA002,0x8CAD,0x8CAC,0xA002,0x8CAB,0x8CAA,0xA008,0xA004,
+ 0xA002,0x8CA9,0x8CA8,0xA002,0x8CA7,0x8CA6,0xA004,0xA002,0x8CA5,0x8CA4,0xA002,0x8CA3,0x8CA2,0xA010,0xA008,0xA004,
+ 0xA002,0x8CA1,0x8CA0,0xA002,0x8C9F,0x8C9E,0xA004,0xA002,0x8C9D,0x8C9C,0xA002,0x8C9B,0x8C9A,0xA008,0xA004,0xA002,
+ 0x8C99,0x8C97,0xA002,0x8C96,0x8C95,0xA004,0xA002,0x8C93,0x8C92,0xA002,0x8C91,0x8C90,0xA01F,0xA00F,0xA008,0xA004,
+ 0xA002,0x8C8F,0x8C8E,0xA002,0x8C8D,0x8C8B,0xA003,0xA000,0x8C88,0xA002,0x8C87,0x8C86,0xA008,0xA004,0xA002,0x8C84,
+ 0x8C83,0xA002,0x8C81,0x8C80,0xA004,0xA002,0x8C7F,0x8C7E,0xA002,0x8C7D,0x8C7C,0xA010,0xA008,0xA004,0xA002,0x8C7B,
+ 0x8C77,0xA002,0x8C76,0x8C75,0xA004,0xA002,0x8C74,0x8C72,0xA002,0x8C71,0x8C70,0xA008,0xA004,0xA002,0x8C6F,0x8C6E,
+ 0xA002,0x8C6D,0x8C6C,0xA004,0xA002,0x8C69,0x8C68,0xA002,0x8C67,0x8C66,0xA040,0xA020,0xA010,0xA008,0xA004,0xA002,
+ 0x8C65,0x8C64,0xA002,0x8C63,0x8C60,0xA004,0xA002,0x8C5F,0x8C5E,0xA002,0x8C5D,0x8C5C,0xA008,0xA004,0xA002,0x8C5B,
+ 0x8C59,0xA002,0x8C58,0x8C57,0xA004,0xA002,0x8C56,0x8C54,0xA002,0x8C53,0x8C52,0xA010,0xA008,0xA004,0xA002,0x8C51,
+ 0x8C50,0xA002,0x8C4F,0x8C4E,0xA004,0xA002,0x8C4D,0x8C4B,0xA002,0x8C4A,0x8C48,0xA008,0xA004,0xA002,0x8C45,0x8C44,
+ 0xA002,0x8C43,0x8C42,0xA004,0xA002,0x8C40,0x8C3F,0xA002,0x8C3E,0x8C3D,0xA000,0xA000,0xA008,0xA004,0xA002,0x8C3C,
+ 0x8C3B,0xA002,0x8C3A,0x8C39,0xA000,0xA000,0x8C38,0xA02B,0xA001,0xA00A,0xA001,0xA001,0xA004,0xA002,0x5EA7,0x5750,
+ 0xA002,0x4F5C,0x505A,0xA010,0xA008,0xA004,0xA002,0x67DE,0x4F50,0xA002,0x5DE6,0x6628,0xA004,0xA002,0x9075,0x5C0A,
+ 0xA002,0x7F6A,0x6700,0xA008,0xA004,0xA002,0x9189,0x5634,0xA002,0x7E82,0x94BB,0xA004,0xA002,0x7EC4,0x963B,0xA002,
+ 0x8BC5,0x7956,0xA040,0xA020,0xA010,0xA008,0xA004,0xA002,0x65CF,0x5352,0xA002,0x8DB3,0x79DF,0xA004,0xA002,0x63CD,
+ 0x594F,0xA002,0x8D70,0x90B9,0xA008,0xA004,0xA002,0x7EB5,0x603B,0xA002,0x7EFC,0x5B97,0xA004,0xA002,0x8E2A,0x68D5,
+ 0xA002,0x9B03,0x5B57,0xA010,0xA008,0xA004,0xA002,0x6E0D,0x81EA,0xA002,0x5B50,0x6ED3,0xA004,0xA002,0x7C7D,0x4ED4,
+ 0xA002,0x7D2B,0x5B5C,0xA008,0xA004,0xA002,0x6DC4,0x6ECB,0xA002,0x59FF,0x8D44,0xA004,0xA002,0x54A8,0x5179,0xA002,
+ 0x6D4A,0x707C,0xA020,0xA010,0xA008,0xA004,0xA002,0x7740,0x5544,0xA002,0x914C,0x8301,0xA004,0xA002,0x7422,0x684C,
+ 0xA002,0x5353,0x62D9,0xA008,0xA004,0xA002,0x6349,0x51C6,0xA002,0x8C06,0x7F00,0xA004,0xA002,0x5760,0x8D58,0xA002,
+ 0x8FFD,0x9525,0xA010,0xA008,0xA004,0xA002,0x690E,0x72B6,0xA002,0x58EE,0x649E,0xA004,0xA002,0x5986,0x88C5,0xA002,
+ 0x5E84,0x6869,0xA008,0xA004,0xA002,0x7BC6,0x8D5A,0xA002,0x64B0,0x8F6C,0xA004,0xA002,0x7816,0x4E13,0xA002,0x62FD,
+ 0x722A,0xA0CE,0xA07D,0xA03E,0xA01E,0xA00E,0xA006,0xA002,0x6293,0xA002,0x9A7B,0x795D,0xA004,0xA002,0x6CE8,0x4F4F,
+ 0xA002,0x8C1E,0x8C09,0xA008,0xA004,0xA002,0x8BEA,0x8BD0,0xA002,0x8BC7,0x8BBB,0xA004,0xA002,0x8BB1,0x8BAC,0xA002,
+ 0x8B9F,0x8B9E,0xA010,0xA008,0xA004,0xA002,0x8B9D,0x8B9C,0xA002,0x8B9B,0x8B9A,0xA004,0xA002,0x8B99,0x8B98,0xA002,
+ 0x8B97,0x8B96,0xA008,0xA004,0xA002,0x8B95,0x8B94,0xA002,0x8B93,0x8B92,0xA004,0xA002,0x8B91,0x8B90,0xA002,0x8B8F,
+ 0x8B8E,0xA01F,0xA00F,0xA008,0xA004,0xA002,0x8B8D,0x8B8C,0xA002,0x8B8B,0x8B8A,0xA004,0xA002,0x8B89,0x8B88,0xA000,
+ 0x8B87,0xA008,0xA004,0xA002,0x8B86,0x8B85,0xA002,0x8B84,0x8B83,0xA004,0xA002,0x8B82,0x8B81,0xA002,0x8B80,0x8B7F,
+ 0xA010,0xA008,0xA004,0xA002,0x8B7E,0x8B7D,0xA002,0x8B7C,0x8B7B,0xA004,0xA002,0x8B7A,0x8B79,0xA002,0x8B78,0x8B77,
+ 0xA008,0xA004,0xA002,0x8B76,0x8B75,0xA002,0x8B74,0x8B73,0xA004,0xA002,0x8B72,0x8B71,0xA002,0x8B70,0x8B6F,0xA040,
+ 0xA020,0xA010,0xA008,0xA004,0xA002,0x8B6E,0x8B6D,0xA002,0x8B6B,0x8B6A,0xA004,0xA002,0x8B69,0x8B68,0xA002,0x8B67,
+ 0x8B65,0xA008,0xA004,0xA002,0x8B64,0x8B63,0xA002,0x8B62,0x8B61,0xA004,0xA002,0x8B60,0x8B5F,0xA002,0x8B5E,0x8B5D,
+ 0xA010,0xA008,0xA004,0xA002,0x8B5C,0x8B5B,0xA002,0x8B5A,0x8B59,0xA004,0xA002,0x8B58,0x8B57,0xA002,0x8B56,0x8B55,
+ 0xA008,0xA004,0xA002,0x8B54,0x8B53,0xA002,0x8B52,0x8B51,0xA004,0xA002,0x8B50,0x8B4F,0xA002,0x8B4E,0x8B4D,0xA000,
+ 0xA000,0xA008,0xA004,0xA002,0x8B4C,0x8B4B,0xA002,0x8B4A,0x8B49,0xA004,0xA002,0x8B48,0x8B47,0xA000,0x8B46,0xA031,
+ 0xA001,0xA010,0xA001,0xA007,0xA003,0xA001,0x7B51,0xA002,0x94F8,0x8D2E,0xA004,0xA002,0x86C0,0x52A9,0xA002,0x67F1,
+ 0x8457,0xA010,0xA008,0xA004,0xA002,0x4E3B,0x5631,0xA002,0x77A9,0x62C4,0xA004,0xA002,0x716E,0x70DB,0xA002,0x7AF9,
+ 0x9010,0xA008,0xA004,0xA002,0x8BDB,0x8BF8,0xA002,0x732A,0x6731,0xA004,0xA002,0x86DB,0x682A,0xA002,0x73E0,0x9AA4,
+ 0xA040,0xA020,0xA010,0xA008,0xA004,0xA002,0x663C,0x5B99,0xA002,0x76B1,0x5492,0xA004,0xA002,0x5E1A,0x8098,0xA002,
+ 0x8F74,0x7CA5,0xA008,0xA004,0xA002,0x8BCC,0x6D32,0xA002,0x5DDE,0x5468,0xA004,0xA002,0x821F,0x4F17,0xA002,0x4EF2,
+ 0x91CD,0xA010,0xA008,0xA004,0xA002,0x80BF,0x79CD,0xA002,0x7EC8,0x8877,0xA004,0xA002,0x949F,0x5FE0,0xA002,0x76C5,
+ 0x4E2D,0xA008,0xA004,0xA002,0x7A92,0x6CBB,0xA002,0x6EDE,0x75D4,0xA004,0xA002,0x7099,0x8D28,0xA002,0x7A1A,0x79E9,
+ 0xA020,0xA010,0xA008,0xA004,0xA002,0x667A,0x5236,0xA002,0x5CD9,0x5E1C,0xA004,0xA002,0x7F6E,0x81F4,0xA002,0x81F3,
+ 0x63B7,0xA008,0xA004,0xA002,0x631A,0x5FD7,0xA002,0x7EB8,0x65E8,0xA004,0xA002,0x53EA,0x8DBE,0xA002,0x6B62,0x6307,
+ 0xA010,0xA008,0xA004,0xA002,0x5740,0x4F84,0xA002,0x503C,0x6267,0xA004,0xA002,0x6B96,0x690D,0xA002,0x76F4,0x804C,
+ 0xA008,0xA004,0xA002,0x7EC7,0x4E4B,0xA002,0x6C41,0x8102,0xA004,0xA002,0x80A2,0x77E5,0xA002,0x8718,0x5431,0xA5F6,
+ 0xA2FC,0xA17D,0xA0D2,0xA07D,0xA03E,0xA01E,0xA00E,0xA006,0xA002,0x652F,0xA002,0x679D,0x829D,0xA004,0xA002,0x8BC1,
+ 0x90D1,0xA002,0x75C7,0x5E27,0xA008,0xA004,0xA002,0x8B45,0x8B44,0xA002,0x8B43,0x8B42,0xA004,0xA002,0x8B41,0x8B40,
+ 0xA002,0x8B3F,0x8B3E,0xA010,0xA008,0xA004,0xA002,0x8B3D,0x8B3C,0xA002,0x8B3B,0x8B3A,0xA004,0xA002,0x8B39,0x8B38,
+ 0xA002,0x8B37,0x8B36,0xA008,0xA004,0xA002,0x8B35,0x8B34,0xA002,0x8B33,0x8B32,0xA004,0xA002,0x8B31,0x8B30,0xA002,
+ 0x8B2F,0x8B2E,0xA01F,0xA010,0xA008,0xA004,0xA002,0x8B2D,0x8B2C,0xA002,0x8B2B,0x8B2A,0xA004,0xA002,0x8B29,0x8B28,
+ 0xA002,0x8B27,0x8B25,0xA007,0xA003,0xA000,0x8B24,0xA002,0x8B23,0x8B22,0xA004,0xA002,0x8B21,0x8B20,0xA002,0x8B1F,
+ 0x8B1E,0xA010,0xA008,0xA004,0xA002,0x8B1D,0x8B1C,0xA002,0x8B1B,0x8B1A,0xA004,0xA002,0x8B19,0x8B18,0xA002,0x8B17,
+ 0x8B16,0xA008,0xA004,0xA002,0x8B15,0x8B14,0xA002,0x8B13,0x8B12,0xA004,0xA002,0x8B11,0x8B10,0xA002,0x8B0F,0x8B0E,
+ 0xA03E,0xA01E,0xA00E,0xA006,0xA002,0x8B0D,0xA002,0x8B0C,0x8B0B,0xA004,0xA002,0x8B0A,0x8B09,0xA002,0x8B08,0x8B06,
+ 0xA008,0xA004,0xA002,0x8B05,0x8B04,0xA002,0x8B03,0x8B02,0xA004,0xA002,0x8B01,0x8B00,0xA002,0x8AFF,0x8AFE,0xA010,
+ 0xA008,0xA004,0xA002,0x8AFD,0x8AFC,0xA002,0x8AFB,0x8AFA,0xA004,0xA002,0x8AF9,0x8AF8,0xA002,0x8AF7,0x8AF6,0xA008,
+ 0xA004,0xA002,0x8AF5,0x8AF4,0xA002,0x8AF3,0x8AF2,0xA004,0xA002,0x8AF1,0x8AF0,0xA002,0x8AEF,0x8AEE,0xA000,0xA010,
+ 0xA008,0xA004,0xA002,0x8AED,0x8AEC,0xA002,0x8AEB,0x8AEA,0xA004,0xA002,0x8AE9,0x8AE8,0xA002,0x8AE7,0x8AE6,0xA000,
+ 0xA000,0xA002,0x8AE5,0x8AE4,0xA02B,0xA001,0xA00A,0xA001,0xA001,0xA004,0xA002,0x653F,0x6B63,0xA002,0x62EF,0x6574,
+ 0xA010,0xA008,0xA004,0xA002,0x6014,0x4E89,0xA002,0x72F0,0x5F81,0xA004,0xA002,0x7741,0x6323,0xA002,0x84B8,0x9635,
+ 0xA008,0xA004,0xA002,0x9547,0x632F,0xA002,0x9707,0x8BCA,0xA004,0xA002,0x75B9,0x6795,0xA002,0x4FA6,0x9488,0xA040,
+ 0xA020,0xA010,0xA008,0xA004,0xA002,0x8D1E,0x81FB,0xA002,0x7827,0x7504,0xA004,0xA002,0x771F,0x659F,0xA002,0x73CD,
+ 0x6D59,0xA008,0xA004,0xA002,0x8FD9,0x8517,0xA002,0x9517,0x8005,0xA004,0xA002,0x8F99,0x86F0,0xA002,0x54F2,0x6298,
+ 0xA010,0xA008,0xA004,0xA002,0x906E,0x53EC,0xA002,0x8087,0x5146,0xA004,0xA002,0x7F69,0x7167,0xA002,0x8D75,0x6CBC,
+ 0xA008,0xA004,0xA002,0x627E,0x662D,0xA002,0x62DB,0x969C,0xA004,0xA002,0x7634,0x80C0,0xA002,0x4ED7,0x8D26,0xA020,
+ 0xA010,0xA008,0xA004,0xA002,0x5E10,0x4E08,0xA002,0x6756,0x6DA8,0xA004,0xA002,0x638C,0x5F20,0xA002,0x6F33,0x5F70,
+ 0xA008,0xA004,0xA002,0x7AE0,0x6A1F,0xA002,0x7EFD,0x6E5B,0xA004,0xA002,0x7AD9,0x6218,0xA002,0x5360,0x6808,0xA010,
+ 0xA008,0xA004,0xA002,0x8638,0x5C55,0xA002,0x5D2D,0x8F97,0xA004,0xA002,0x65A9,0x76CF,0xA002,0x6CBE,0x7C98,0xA008,
+ 0xA004,0xA002,0x8A79,0x6BE1,0xA002,0x77BB,0x5BE8,0xA004,0xA002,0x503A,0x7A84,0xA002,0x5B85,0x658B,0xA0D7,0xA07D,
+ 0xA03E,0xA01E,0xA00E,0xA006,0xA002,0x6458,0xA002,0x8BC8,0x70B8,0xA004,0xA002,0x4E4D,0x548B,0xA002,0x69A8,0x6805,
+ 0xA008,0xA004,0xA002,0x7728,0x95F8,0xA002,0x94E1,0x8AE3,0xA004,0xA002,0x8AE2,0x8AE1,0xA002,0x8AE0,0x8ADF,0xA010,
+ 0xA008,0xA004,0xA002,0x8ADE,0x8ADD,0xA002,0x8ADC,0x8ADB,0xA004,0xA002,0x8ADA,0x8AD9,0xA002,0x8AD8,0x8AD7,0xA008,
+ 0xA004,0xA002,0x8AD6,0x8AD5,0xA002,0x8AD4,0x8AD3,0xA004,0xA002,0x8AD2,0x8AD1,0xA002,0x8AD0,0x8ACF,0xA01F,0xA010,
+ 0xA008,0xA004,0xA002,0x8ACE,0x8ACD,0xA002,0x8ACC,0x8ACB,0xA004,0xA002,0x8ACA,0x8AC9,0xA002,0x8AC8,0x8AC7,0xA008,
+ 0xA004,0xA002,0x8AC6,0x8AC5,0xA002,0x8AC4,0x8AC3,0xA003,0xA001,0x8AC2,0xA002,0x8AC1,0x8AC0,0xA010,0xA008,0xA004,
+ 0xA002,0x8ABF,0x8ABE,0xA002,0x8ABD,0x8ABC,0xA004,0xA002,0x8ABB,0x8ABA,0xA002,0x8AB9,0x8AB8,0xA008,0xA004,0xA002,
+ 0x8AB7,0x8AB6,0xA002,0x8AB5,0x8AB4,0xA004,0xA002,0x8AB3,0x8AB2,0xA002,0x8AB1,0x8AB0,0xA040,0xA020,0xA010,0xA008,
+ 0xA004,0xA002,0x8AAF,0x8AAE,0xA002,0x8AAD,0x8AAC,0xA004,0xA002,0x8AAB,0x8AAA,0xA002,0x8AA9,0x8AA8,0xA008,0xA004,
+ 0xA002,0x8AA7,0x8AA6,0xA002,0x8AA5,0x8AA4,0xA004,0xA002,0x8AA3,0x8AA2,0xA002,0x8AA1,0x8AA0,0xA010,0xA008,0xA004,
+ 0xA002,0x8A9F,0x8A9E,0xA002,0x8A9D,0x8A9C,0xA004,0xA002,0x8A9B,0x8A9A,0xA002,0x8A99,0x8A98,0xA008,0xA004,0xA002,
+ 0x8A97,0x8A96,0xA002,0x8A95,0x8A94,0xA004,0xA002,0x8A92,0x8A91,0xA002,0x8A90,0x8A8F,0xA000,0xA010,0xA008,0xA004,
+ 0xA002,0x8A8E,0x8A8D,0xA002,0x8A8C,0x8A8B,0xA004,0xA002,0x8A88,0x8A87,0xA002,0x8A86,0x8A85,0xA000,0xA004,0xA002,
+ 0x8A84,0x8A83,0xA002,0x8A82,0x8A81,0xA028,0xA001,0xA007,0xA001,0xA001,0xA001,0xA002,0x8F67,0x672D,0xA010,0xA008,
+ 0xA004,0xA002,0x6E23,0x55B3,0xA002,0x624E,0x8D60,0xA004,0xA002,0x66FE,0x618E,0xA002,0x589E,0x600E,0xA008,0xA004,
+ 0xA002,0x8D3C,0x6CFD,0xA002,0x5219,0x62E9,0xA004,0xA002,0x8D23,0x71E5,0xA002,0x7076,0x7682,0xA040,0xA020,0xA010,
+ 0xA008,0xA004,0xA002,0x9020,0x566A,0xA002,0x8E81,0x86A4,0xA004,0xA002,0x6FA1,0x65E9,0xA002,0x67A3,0x85FB,0xA008,
+ 0xA004,0xA002,0x51FF,0x7CDF,0xA002,0x906D,0x846C,0xA004,0xA002,0x810F,0x8D43,0xA002,0x8D5E,0x6682,0xA010,0xA008,
+ 0xA004,0xA002,0x6512,0x54B1,0xA002,0x5728,0x518D,0xA004,0xA002,0x8F7D,0x5BB0,0xA002,0x707E,0x54C9,0xA008,0xA004,
+ 0xA002,0x683D,0x6742,0xA002,0x7838,0x531D,0xA004,0xA002,0x5B55,0x97F5,0xA002,0x6655,0x915D,0xA020,0xA010,0xA008,
+ 0xA004,0xA002,0x8574,0x8FD0,0xA002,0x5141,0x9668,0xA004,0xA002,0x5300,0x90E7,0xA002,0x4E91,0x8018,0xA008,0xA004,
+ 0xA002,0x9605,0x60A6,0xA002,0x6708,0x7CA4,0xA004,0xA002,0x5CB3,0x94A5,0xA002,0x8DC3,0x8D8A,0xA010,0xA008,0xA004,
+ 0xA002,0x7EA6,0x66F0,0xA002,0x9662,0x6028,0xA004,0xA002,0x613F,0x82D1,0xA002,0x8FDC,0x7F18,0xA008,0xA004,0xA002,
+ 0x6E90,0x733F,0xA002,0x5706,0x5458,0xA004,0xA002,0x56ED,0x8F95,0xA002,0x63F4,0x539F,0xA17D,0xA0DB,0xA07D,0xA03E,
+ 0xA01E,0xA00E,0xA006,0xA002,0x8881,0xA002,0x57A3,0x5143,0xA004,0xA002,0x51A4,0x6E0A,0xA002,0x9E33,0x9A6D,0xA008,
+ 0xA004,0xA002,0x8C6B,0x9884,0xA002,0x88D5,0x5BD3,0xA004,0xA002,0x6D74,0x8A80,0xA002,0x8A7F,0x8A7E,0xA010,0xA008,
+ 0xA004,0xA002,0x8A7D,0x8A7C,0xA002,0x8A7B,0x8A7A,0xA004,0xA002,0x8A78,0x8A77,0xA002,0x8A76,0x8A75,0xA008,0xA004,
+ 0xA002,0x8A74,0x8A73,0xA002,0x8A72,0x8A71,0xA004,0xA002,0x8A70,0x8A6F,0xA002,0x8A6E,0x8A6D,0xA01F,0xA010,0xA008,
+ 0xA004,0xA002,0x8A6C,0x8A6B,0xA002,0x8A6A,0x8A69,0xA004,0xA002,0x8A68,0x8A67,0xA002,0x8A66,0x8A65,0xA008,0xA004,
+ 0xA002,0x8A64,0x8A63,0xA002,0x8A62,0x8A61,0xA004,0xA002,0x8A60,0x8A5F,0xA001,0x8A5E,0xA010,0xA008,0xA004,0xA002,
+ 0x8A5D,0x8A5C,0xA002,0x8A5B,0x8A5A,0xA004,0xA002,0x8A59,0x8A58,0xA002,0x8A57,0x8A56,0xA008,0xA004,0xA002,0x8A55,
+ 0x8A54,0xA002,0x8A53,0x8A52,0xA004,0xA002,0x8A51,0x8A50,0xA002,0x8A4F,0x8A4E,0xA040,0xA020,0xA010,0xA008,0xA004,
+ 0xA002,0x8A4D,0x8A4C,0xA002,0x8A4B,0x8A4A,0xA004,0xA002,0x8A49,0x8A47,0xA002,0x8A46,0x8A45,0xA008,0xA004,0xA002,
+ 0x8A44,0x8A43,0xA002,0x8A42,0x8A41,0xA004,0xA002,0x8A40,0x8A3F,0xA002,0x8A3D,0x8A3C,0xA010,0xA008,0xA004,0xA002,
+ 0x8A3B,0x8A3A,0xA002,0x8A39,0x8A38,0xA004,0xA002,0x8A37,0x8A36,0xA002,0x8A35,0x8A34,0xA008,0xA004,0xA002,0x8A33,
+ 0x8A32,0xA002,0x8A31,0x8A30,0xA004,0xA002,0x8A2F,0x8A2E,0xA002,0x8A2D,0x8A2C,0xA000,0xA010,0xA008,0xA004,0xA002,
+ 0x8A2B,0x8A2A,0xA002,0x8A29,0x8A28,0xA004,0xA002,0x8A27,0x8A26,0xA002,0x8A25,0x8A24,0xA008,0xA004,0xA002,0x8A23,
+ 0x8A22,0xA002,0x8A21,0x8A20,0xA000,0xA002,0x8A1F,0x8A1E,0xA022,0xA001,0xA001,0xA010,0xA008,0xA004,0xA002,0x8A89,
+ 0x80B2,0xA002,0x72F1,0x6B32,0xA004,0xA002,0x6108,0x5FA1,0xA002,0x5CEA,0x55BB,0xA008,0xA004,0xA002,0x9047,0x5401,
+ 0xA002,0x90C1,0x828B,0xA004,0xA002,0x57DF,0x7389,0xA002,0x7FBD,0x8BED,0xA040,0xA020,0xA010,0xA008,0xA004,0xA002,
+ 0x5B87,0x79B9,0xA002,0x5C7F,0x4E0E,0xA004,0xA002,0x96E8,0x5A31,0xA002,0x4E88,0x9685,0xA008,0xA004,0xA002,0x6E14,
+ 0x6E1D,0xA002,0x6109,0x9C7C,0xA004,0xA002,0x903E,0x4FDE,0xA002,0x4F59,0x8206,0xA010,0xA008,0xA004,0xA002,0x611A,
+ 0x865E,0xA002,0x6986,0x76C2,0xA004,0xA002,0x4E8E,0x6DE4,0xA002,0x8FC2,0x5E7C,0xA008,0xA004,0xA002,0x53C8,0x8BF1,
+ 0xA002,0x91C9,0x4F51,0xA004,0xA002,0x53F3,0x53CB,0xA002,0x6709,0x9149,0xA020,0xA010,0xA008,0xA004,0xA002,0x6E38,
+ 0x6CB9,0xA002,0x72B9,0x94C0,0xA004,0xA002,0x90AE,0x7531,0xA002,0x5C24,0x5FE7,0xA008,0xA004,0xA002,0x60A0,0x4F18,
+ 0xA002,0x5E7D,0x7528,0xA004,0xA002,0x52C7,0x607F,0xA002,0x6C38,0x6D8C,0xA010,0xA008,0xA004,0xA002,0x6CF3,0x548F,
+ 0xA002,0x86F9,0x8E0A,0xA004,0xA002,0x96CD,0x5EB8,0xA002,0x75C8,0x81C3,0xA008,0xA004,0xA002,0x4F63,0x62E5,0xA002,
+ 0x54DF,0x6620,0xA004,0xA002,0x786C,0x9896,0xA002,0x5F71,0x76C8,0xA0DE,0xA07D,0xA03E,0xA01E,0xA00E,0xA006,0xA002,
+ 0x8D62,0xA002,0x8FCE,0x8747,0xA004,0xA002,0x8367,0x8425,0xA002,0x8424,0x83B9,0xA008,0xA004,0xA002,0x7F28,0x5E94,
+ 0xA002,0x9E70,0x5A74,0xA004,0xA002,0x6A31,0x82F1,0xA002,0x5370,0x8A1D,0xA010,0xA008,0xA004,0xA002,0x8A1C,0x8A1B,
+ 0xA002,0x8A1A,0x8A19,0xA004,0xA002,0x8A18,0x8A17,0xA002,0x8A16,0x8A15,0xA008,0xA004,0xA002,0x8A14,0x8A13,0xA002,
+ 0x8A12,0x8A11,0xA004,0xA002,0x8A10,0x8A0F,0xA002,0x8A0E,0x8A0D,0xA020,0xA010,0xA008,0xA004,0xA002,0x8A0C,0x8A0B,
+ 0xA002,0x8A0A,0x8A09,0xA004,0xA002,0x8A08,0x8A06,0xA002,0x8A05,0x8A04,0xA008,0xA004,0xA002,0x8A03,0x8A02,0xA002,
+ 0x8A01,0x89FF,0xA004,0xA002,0x89FE,0x89FD,0xA002,0x89FC,0x89FB,0xA00F,0xA007,0xA003,0xA001,0x89FA,0xA002,0x89F9,
+ 0x89F8,0xA004,0xA002,0x89F7,0x89F6,0xA002,0x89F5,0x89F4,0xA008,0xA004,0xA002,0x89F2,0x89F1,0xA002,0x89F0,0x89EE,
+ 0xA004,0xA002,0x89ED,0x89EC,0xA002,0x89EA,0x89E9,0xA040,0xA020,0xA010,0xA008,0xA004,0xA002,0x89E8,0x89E7,0xA002,
+ 0x89E4,0x89E2,0xA004,0xA002,0x89E1,0x89E0,0xA002,0x89DF,0x89DD,0xA008,0xA004,0xA002,0x89DB,0x89D9,0xA002,0x89D8,
+ 0x89D7,0xA004,0xA002,0x89D5,0x89D4,0xA002,0x89D3,0x89CD,0xA010,0xA008,0xA004,0xA002,0x89C3,0x89C0,0xA002,0x89BF,
+ 0x89BE,0xA004,0xA002,0x89BD,0x89BC,0xA002,0x89BB,0x89BA,0xA008,0xA004,0xA002,0x89B9,0x89B8,0xA002,0x89B7,0x89B6,
+ 0xA004,0xA002,0x89B5,0x89B4,0xA002,0x89B3,0x89B2,0xA000,0xA010,0xA008,0xA004,0xA002,0x89B1,0x89B0,0xA002,0x89AF,
+ 0x89AE,0xA004,0xA002,0x89AD,0x89AC,0xA002,0x89AB,0x89AA,0xA008,0xA004,0xA002,0x89A9,0x89A8,0xA002,0x89A7,0x89A6,
+ 0xA004,0xA002,0x89A5,0x89A4,0xA002,0x89A3,0x89A2,0xA01F,0xA001,0xA001,0xA00D,0xA005,0xA001,0xA002,0x9690,0x5F15,
+ 0xA004,0xA002,0x5C39,0x996E,0xA002,0x5BC5,0x6DEB,0xA008,0xA004,0xA002,0x94F6,0x541F,0xA002,0x59FB,0x9634,0xA004,
+ 0xA002,0x97F3,0x6BB7,0xA002,0x56E0,0x836B,0xA040,0xA020,0xA010,0xA008,0xA004,0xA002,0x8335,0x7ECE,0xA002,0x7FCC,
+ 0x7FFC,0xA004,0xA002,0x5F02,0x8BD1,0xA002,0x8C0A,0x8BAE,0xA008,0xA004,0xA002,0x8BE3,0x6EA2,0xA002,0x76CA,0x4E49,
+ 0xA004,0xA002,0x5FC6,0x6BC5,0xA002,0x610F,0x88D4,0xA010,0xA008,0xA004,0xA002,0x4EA6,0x75AB,0xA002,0x8084,0x9038,
+ 0xA004,0xA002,0x81C6,0x5F79,0xA002,0x4EBF,0x5C79,0xA008,0xA004,0xA002,0x9091,0x6613,0xA002,0x6291,0x827A,0xA004,
+ 0xA002,0x4EE5,0x77E3,0xA002,0x4E59,0x5DF2,0xA020,0xA010,0xA008,0xA004,0xA002,0x501A,0x8681,0xA002,0x6905,0x5F5D,
+ 0xA004,0xA002,0x59E8,0x5B9C,0xA002,0x6C82,0x7591,0xA008,0xA004,0xA002,0x80F0,0x4EEA,0xA002,0x79FB,0x9057,0xA004,
+ 0xA002,0x5937,0x9890,0xA002,0x8863,0x4F0A,0xA010,0xA008,0xA004,0xA002,0x4F9D,0x94F1,0xA002,0x63D6,0x533B,0xA004,
+ 0xA002,0x58F9,0x4E00,0xA002,0x6DB2,0x591C,0xA008,0xA004,0xA002,0x814B,0x66F3,0xA002,0x53F6,0x4E1A,0xA004,0xA002,
+ 0x6396,0x9875,0xA002,0x4E5F,0x51B6,0xA2FE,0xA17F,0xA0E4,0xA07D,0xA03E,0xA01E,0xA00E,0xA006,0xA002,0x91CE,0xA002,
+ 0x7237,0x8036,0xA004,0xA002,0x564E,0x6930,0xA002,0x8000,0x8981,0xA008,0xA004,0xA002,0x836F,0x8200,0xA002,0x54AC,
+ 0x59DA,0xA004,0xA002,0x8C23,0x7A91,0xA002,0x9065,0x5C27,0xA010,0xA008,0xA004,0xA002,0x6447,0x89A1,0xA002,0x89A0,
+ 0x899F,0xA004,0xA002,0x899E,0x899D,0xA002,0x899C,0x899B,0xA008,0xA004,0xA002,0x899A,0x8999,0xA002,0x8998,0x8997,
+ 0xA004,0xA002,0x8996,0x8995,0xA002,0x8994,0x8993,0xA020,0xA010,0xA008,0xA004,0xA002,0x8992,0x8991,0xA002,0x8990,
+ 0x898F,0xA004,0xA002,0x898E,0x898D,0xA002,0x898C,0x898B,0xA008,0xA004,0xA002,0x898A,0x8989,0xA002,0x8988,0x8987,
+ 0xA004,0xA002,0x8985,0x8984,0xA002,0x8982,0x8980,0xA00F,0xA007,0xA004,0xA002,0x897E,0x897D,0xA001,0x897C,0xA004,
+ 0xA002,0x897A,0x8979,0xA002,0x8978,0x8977,0xA008,0xA004,0xA002,0x8976,0x8975,0xA002,0x8974,0x8973,0xA004,0xA002,
+ 0x8972,0x8971,0xA002,0x8970,0x896F,0xA040,0xA020,0xA010,0xA008,0xA004,0xA002,0x896E,0x896D,0xA002,0x896C,0x896B,
+ 0xA004,0xA002,0x896A,0x8969,0xA002,0x8968,0x8967,0xA008,0xA004,0xA002,0x8965,0x8964,0xA002,0x8963,0x8962,0xA004,
+ 0xA002,0x8961,0x8960,0xA002,0x895D,0x895C,0xA010,0xA008,0xA004,0xA002,0x895B,0x895A,0xA002,0x8959,0x8958,0xA004,
+ 0xA002,0x8957,0x8956,0xA002,0x8955,0x8954,0xA008,0xA004,0xA002,0x8953,0x8952,0xA002,0x8951,0x8950,0xA004,0xA002,
+ 0x894F,0x894E,0xA002,0x894D,0x894C,0xA020,0xA010,0xA008,0xA004,0xA002,0x894B,0x894A,0xA002,0x8949,0x8948,0xA004,
+ 0xA002,0x8947,0x8946,0xA002,0x8945,0x8943,0xA008,0xA004,0xA002,0x8942,0x8940,0xA002,0x893F,0x893E,0xA004,0xA002,
+ 0x893D,0x893C,0xA002,0x893B,0x893A,0xA000,0xA000,0xA000,0xA002,0x8939,0x8938,0xA01B,0xA001,0xA001,0xA009,0xA001,
+ 0xA004,0xA002,0x7476,0x5996,0xA002,0x8170,0x9080,0xA008,0xA004,0xA002,0x6F3E,0x6837,0xA002,0x517B,0x75D2,0xA004,
+ 0xA002,0x4EF0,0x6C27,0xA002,0x9633,0x6D0B,0xA040,0xA020,0xA010,0xA008,0xA004,0xA002,0x7F8A,0x75A1,0xA002,0x4F6F,
+ 0x626C,0xA004,0xA002,0x6768,0x79E7,0xA002,0x9E2F,0x592E,0xA008,0xA004,0xA002,0x6B83,0x9A8C,0xA002,0x8C1A,0x5BB4,
+ 0xA004,0xA002,0x7130,0x5F66,0xA002,0x5501,0x96C1,0xA010,0xA008,0xA004,0xA002,0x781A,0x538C,0xA002,0x71D5,0x5830,
+ 0xA004,0xA002,0x8273,0x6F14,0xA002,0x884D,0x773C,0xA008,0xA004,0xA002,0x63A9,0x5944,0xA002,0x6CBF,0x708E,0xA004,
+ 0xA002,0x960E,0x989C,0xA002,0x8A00,0x5EF6,0xA020,0xA010,0xA008,0xA004,0xA002,0x5CA9,0x8712,0xA002,0x7814,0x4E25,
+ 0xA004,0xA002,0x76D0,0x6DF9,0xA002,0x70DF,0x9609,0xA008,0xA004,0xA002,0x54BD,0x7109,0xA002,0x8BB6,0x4E9A,0xA004,
+ 0xA002,0x54D1,0x96C5,0xA002,0x6DAF,0x8859,0xA010,0xA008,0xA004,0xA002,0x5D16,0x869C,0xA002,0x7259,0x82BD,0xA004,
+ 0xA002,0x4E2B,0x5440,0xA002,0x9E2D,0x9E26,0xA008,0xA004,0xA002,0x62BC,0x538B,0xA002,0x8FC5,0x900A,0xA004,0xA002,
+ 0x8BAF,0x8BAD,0xA002,0x6C5B,0x6B89,0xA0E7,0xA07D,0xA03E,0xA01E,0xA00E,0xA006,0xA002,0x5DE1,0xA002,0x9A6F,0x5BFB,
+ 0xA004,0xA002,0x8BE2,0x65EC,0xA002,0x5FAA,0x718F,0xA008,0xA004,0xA002,0x52CB,0x8840,0xA002,0x96EA,0x7A74,0xA004,
+ 0xA002,0x5B66,0x859B,0xA002,0x9774,0x7EDA,0xA010,0xA008,0xA004,0xA002,0x7729,0x7663,0xA002,0x9009,0x8937,0xA004,
+ 0xA002,0x8935,0x8933,0xA002,0x8932,0x8931,0xA008,0xA004,0xA002,0x892F,0x892E,0xA002,0x892D,0x892C,0xA004,0xA002,
+ 0x8929,0x8928,0xA002,0x8927,0x8926,0xA020,0xA010,0xA008,0xA004,0xA002,0x8924,0x8923,0xA002,0x8922,0x8920,0xA004,
+ 0xA002,0x891F,0x891E,0xA002,0x891D,0x891C,0xA008,0xA004,0xA002,0x8918,0x8917,0xA002,0x8916,0x8915,0xA004,0xA002,
+ 0x8914,0x8911,0xA002,0x890F,0x890E,0xA00F,0xA008,0xA004,0xA002,0x890D,0x890C,0xA002,0x890B,0x8909,0xA003,0xA001,
+ 0x8908,0xA002,0x8907,0x8906,0xA008,0xA004,0xA002,0x8905,0x8904,0xA002,0x8903,0x8901,0xA004,0xA002,0x8900,0x88FF,
+ 0xA002,0x88FD,0x88FB,0xA040,0xA020,0xA010,0xA008,0xA004,0xA002,0x88FA,0x88F7,0xA002,0x88F6,0x88F5,0xA004,0xA002,
+ 0x88F2,0x88EF,0xA002,0x88EE,0x88ED,0xA008,0xA004,0xA002,0x88EC,0x88EB,0xA002,0x88EA,0x88E9,0xA004,0xA002,0x88E7,
+ 0x88E6,0xA002,0x88E1,0x88E0,0xA010,0xA008,0xA004,0xA002,0x88DE,0x88DD,0xA002,0x88DC,0x88DB,0xA004,0xA002,0x88DA,
+ 0x88D7,0xA002,0x88D6,0x88D3,0xA008,0xA004,0xA002,0x88D1,0x88D0,0xA002,0x88CF,0x88CD,0xA004,0xA002,0x88CC,0x88CB,
+ 0xA002,0x88CA,0x88C8,0xA020,0xA010,0xA008,0xA004,0xA002,0x88C7,0x88C4,0xA002,0x88C3,0x88C0,0xA004,0xA002,0x88BF,
+ 0x88BE,0xA002,0x88BD,0x88BB,0xA008,0xA004,0xA002,0x88BA,0x88B9,0xA002,0x88B8,0x88B6,0xA004,0xA002,0x88B5,0x88B4,
+ 0xA002,0x88B3,0x88B2,0xA000,0xA000,0xA004,0xA002,0x88B0,0x88AF,0xA002,0x88AE,0x88AC,0xA018,0xA001,0xA001,0xA006,
+ 0xA001,0xA001,0xA002,0x7384,0x65CB,0xA008,0xA004,0xA002,0x60AC,0x5BA3,0xA002,0x55A7,0x8F69,0xA004,0xA002,0x7EED,
+ 0x7EEA,0xA002,0x5A7F,0x7D6E,0xA040,0xA020,0xA010,0xA008,0xA004,0xA002,0x6064,0x755C,0xA002,0x5E8F,0x65ED,0xA004,
+ 0xA002,0x53D9,0x9157,0xA002,0x84C4,0x8BB8,0xA008,0xA004,0xA002,0x5F90,0x987B,0xA002,0x5618,0x865A,0xA004,0xA002,
+ 0x9700,0x620C,0xA002,0x589F,0x7EE3,0xA010,0xA008,0xA004,0xA002,0x8896,0x79C0,0xA002,0x9508,0x55C5,0xA004,0xA002,
+ 0x673D,0x7F9E,0xA002,0x4FEE,0x4F11,0xA008,0xA004,0xA002,0x718A,0x96C4,0xA002,0x6C79,0x5308,0xA004,0xA002,0x80F8,
+ 0x51F6,0xA002,0x5144,0x59D3,0xA020,0xA010,0xA008,0xA004,0xA002,0x6027,0x674F,0xA002,0x5E78,0x9192,0xA004,0xA002,
+ 0x884C,0x90A2,0xA002,0x5F62,0x578B,0xA008,0xA004,0xA002,0x5211,0x5174,0xA002,0x60FA,0x7329,0xA004,0xA002,0x8165,
+ 0x661F,0xA002,0x8845,0x4FE1,0xA010,0xA008,0xA004,0xA002,0x5FC3,0x5FFB,0xA002,0x65B0,0x8F9B,0xA004,0xA002,0x6B23,
+ 0x950C,0xA002,0x82AF,0x85AA,0xA008,0xA004,0xA002,0x5C51,0x8C22,0xA002,0x6CFB,0x6CC4,0xA004,0xA002,0x61C8,0x87F9,
+ 0xA002,0x5378,0x68B0,0xA17E,0xA0EB,0xA07D,0xA03E,0xA01E,0xA00E,0xA006,0xA002,0x5199,0xA002,0x8C10,0x80C1,0xA004,
+ 0xA002,0x659C,0x90AA,0xA002,0x643A,0x631F,0xA008,0xA004,0xA002,0x534F,0x978B,0xA002,0x874E,0x6B47,0xA004,0xA002,
+ 0x4E9B,0x6954,0xA002,0x6548,0x7B11,0xA010,0xA008,0xA004,0xA002,0x5578,0x8096,0xA002,0x6821,0x5B5D,0xA004,0xA002,
+ 0x5C0F,0x88AA,0xA002,0x88A9,0x88A8,0xA008,0xA004,0xA002,0x88A7,0x88A6,0xA002,0x88A5,0x88A3,0xA004,0xA002,0x88A1,
+ 0x88A0,0xA002,0x889F,0x889E,0xA020,0xA010,0xA008,0xA004,0xA002,0x889D,0x889B,0xA002,0x889A,0x8899,0xA004,0xA002,
+ 0x8898,0x8897,0xA002,0x8895,0x8894,0xA008,0xA004,0xA002,0x8893,0x8891,0xA002,0x8890,0x888F,0xA004,0xA002,0x888E,
+ 0x888C,0xA002,0x888A,0x8889,0xA00F,0xA008,0xA004,0xA002,0x8887,0x8886,0xA002,0x8883,0x8880,0xA004,0xA002,0x887C,
+ 0x887B,0xA001,0x887A,0xA008,0xA004,0xA002,0x8879,0x8878,0xA002,0x8876,0x8875,0xA004,0xA002,0x8874,0x8873,0xA002,
+ 0x8871,0x886F,0xA040,0xA020,0xA010,0xA008,0xA004,0xA002,0x886D,0x886A,0xA002,0x8867,0x8866,0xA004,0xA002,0x8860,
+ 0x885F,0xA002,0x885E,0x885D,0xA008,0xA004,0xA002,0x885C,0x885B,0xA002,0x885A,0x8858,0xA004,0xA002,0x8856,0x8855,
+ 0xA002,0x8853,0x8852,0xA010,0xA008,0xA004,0xA002,0x8851,0x8850,0xA002,0x884F,0x884E,0xA004,0xA002,0x884B,0x884A,
+ 0xA002,0x8849,0x8848,0xA008,0xA004,0xA002,0x8847,0x8846,0xA002,0x8843,0x8842,0xA004,0xA002,0x8841,0x883F,0xA002,
+ 0x883E,0x883D,0xA020,0xA010,0xA008,0xA004,0xA002,0x883B,0x883A,0xA002,0x8838,0x8837,0xA004,0xA002,0x8836,0x8835,
+ 0xA002,0x8834,0x8833,0xA008,0xA004,0xA002,0x8831,0x8830,0xA002,0x882F,0x882E,0xA004,0xA002,0x882D,0x882C,0xA002,
+ 0x882B,0x882A,0xA000,0xA008,0xA004,0xA002,0x8829,0x8828,0xA002,0x8827,0x8826,0xA000,0xA002,0x8825,0x8824,0xA013,
+ 0xA001,0xA001,0xA001,0xA008,0xA004,0xA002,0x6653,0x6DC6,0xA002,0x5BB5,0x6D88,0xA004,0xA002,0x9500,0x56A3,0xA002,
+ 0x54EE,0x524A,0xA040,0xA020,0xA010,0xA008,0xA004,0xA002,0x9704,0x785D,0xA002,0x8427,0x8C61,0xA004,0xA002,0x5411,
+ 0x50CF,0xA002,0x6A61,0x5DF7,0xA008,0xA004,0xA002,0x9879,0x4EAB,0xA002,0x54CD,0x60F3,0xA004,0xA002,0x8BE6,0x7965,
+ 0xA002,0x7FD4,0x4E61,0xA010,0xA008,0xA004,0xA002,0x6E58,0x8944,0xA002,0x7BB1,0x9999,0xA004,0xA002,0x9576,0x53A2,
+ 0xA002,0x76F8,0x7EBF,0xA008,0xA004,0xA002,0x9650,0x9677,0xA002,0x5BAA,0x7FA1,0xA004,0xA002,0x9985,0x817A,0xA002,
+ 0x53BF,0x732E,0xA020,0xA010,0xA008,0xA004,0xA002,0x73B0,0x9669,0xA002,0x663E,0x5ACC,0xA004,0xA002,0x5F26,0x6D8E,
+ 0xA002,0x95F2,0x8237,0xA008,0xA004,0xA002,0x8854,0x8D24,0xA002,0x54B8,0x7EA4,0xA004,0xA002,0x9C9C,0x4ED9,0xA002,
+ 0x5148,0x9528,0xA010,0xA008,0xA004,0xA002,0x6380,0x5413,0xA002,0x590F,0x53A6,0xA004,0xA002,0x4E0B,0x72ED,0xA002,
+ 0x4FA0,0x5CE1,0xA008,0xA004,0xA002,0x6687,0x8F96,0xA002,0x971E,0x5323,0xA004,0xA002,0x867E,0x778E,0xA002,0x7EC6,
+ 0x620F,0xA0EE,0xA07D,0xA03E,0xA01E,0xA00E,0xA006,0xA002,0x9699,0xA002,0x7CFB,0x6D17,0xA004,0xA002,0x94E3,0x559C,
+ 0xA002,0x5AB3,0x4E60,0xA008,0xA004,0xA002,0x5E2D,0x88AD,0xA002,0x6A84,0x7280,0xA004,0xA002,0x6C50,0x6EAA,0xA002,
+ 0x70EF,0x7184,0xA010,0xA008,0xA004,0xA002,0x60DC,0x5915,0xA002,0x819D,0x6089,0xA004,0xA002,0x5E0C,0x606F,0xA002,
+ 0x7A00,0x8823,0xA008,0xA004,0xA002,0x8820,0x881F,0xA002,0x881E,0x881D,0xA004,0xA002,0x881C,0x881A,0xA002,0x8819,
+ 0x8818,0xA020,0xA010,0xA008,0xA004,0xA002,0x8817,0x8814,0xA002,0x8812,0x8811,0xA004,0xA002,0x8810,0x880F,0xA002,
+ 0x880E,0x880D,0xA008,0xA004,0xA002,0x880C,0x880B,0xA002,0x8809,0x8808,0xA004,0xA002,0x8807,0x8806,0xA002,0x8805,
+ 0x8804,0xA010,0xA008,0xA004,0xA002,0x8802,0x8801,0xA002,0x8800,0x87FF,0xA004,0xA002,0x87FD,0x87FC,0xA002,0x87FB,
+ 0x87FA,0xA007,0xA003,0xA001,0x87F8,0xA002,0x87F7,0x87F6,0xA004,0xA002,0x87F5,0x87F4,0xA002,0x87F3,0x87F2,0xA040,
+ 0xA020,0xA010,0xA008,0xA004,0xA002,0x87F1,0x87F0,0xA002,0x87EF,0x87ED,0xA004,0xA002,0x87EC,0x87EB,0xA002,0x87E9,
+ 0x87E8,0xA008,0xA004,0xA002,0x87E7,0x87E6,0xA002,0x87E4,0x87E3,0xA004,0xA002,0x87E2,0x87E1,0xA002,0x87DF,0x87DE,
+ 0xA010,0xA008,0xA004,0xA002,0x87DD,0x87DC,0xA002,0x87DA,0x87D9,0xA004,0xA002,0x87D8,0x87D7,0xA002,0x87D6,0x87D5,
+ 0xA008,0xA004,0xA002,0x87D4,0x87D0,0xA002,0x87CF,0x87CE,0xA004,0xA002,0x87CD,0x87CC,0xA002,0x87C9,0x87C8,0xA020,
+ 0xA010,0xA008,0xA004,0xA002,0x87C7,0x87C5,0xA002,0x87C4,0x87C3,0xA004,0xA002,0x87C2,0x87C1,0xA002,0x87BF,0x87BE,
+ 0xA008,0xA004,0xA002,0x87BC,0x87BB,0xA002,0x87B9,0x87B8,0xA004,0xA002,0x87B7,0x87B6,0xA002,0x87B4,0x87B2,0xA000,
+ 0xA008,0xA004,0xA002,0x87B1,0x87B0,0xA002,0x87AE,0x87AA,0xA004,0xA002,0x87A9,0x87A7,0xA002,0x87A6,0x87A5,0xA010,
+ 0xA001,0xA001,0xA001,0xA005,0xA001,0xA002,0x727A,0x9521,0xA004,0xA002,0x5438,0x563B,0xA002,0x6670,0x77FD,0xA040,
+ 0xA020,0xA010,0xA008,0xA004,0xA002,0x7852,0x897F,0xA002,0x6790,0x7199,0xA004,0xA002,0x6614,0x8BEF,0xA002,0x609F,
+ 0x52A1,0xA008,0xA004,0xA002,0x52FF,0x7269,0xA002,0x6664,0x96FE,0xA004,0xA002,0x620A,0x575E,0xA002,0x4FAE,0x4F0D,
+ 0xA010,0xA008,0xA004,0xA002,0x821E,0x5348,0xA002,0x6342,0x4E94,0xA004,0xA002,0x6B66,0x6BCB,0xA002,0x5434,0x543E,
+ 0xA008,0xA004,0xA002,0x68A7,0x829C,0xA002,0x65E0,0x5C4B,0xA004,0xA002,0x8BEC,0x6C61,0xA002,0x4E4C,0x94A8,0xA020,
+ 0xA010,0xA008,0xA004,0xA002,0x545C,0x5DEB,0xA002,0x6C83,0x63E1,0xA004,0xA002,0x5367,0x65A1,0xA002,0x6211,0x7A9D,
+ 0xA008,0xA004,0xA002,0x6DA1,0x8717,0xA002,0x631D,0x74EE,0xA004,0xA002,0x7FC1,0x55E1,0xA002,0x95EE,0x7D0A,0xA010,
+ 0xA008,0xA004,0xA002,0x7A33,0x543B,0xA002,0x7EB9,0x95FB,0xA004,0xA002,0x6587,0x868A,0xA002,0x6E29,0x761F,0xA008,
+ 0xA004,0xA002,0x536B,0x6170,0xA002,0x5C09,0x8C13,0xA004,0xA002,0x6E2D,0x4F4D,0xA002,0x9B4F,0x5582,0xABF6,0xA5FB,
+ 0xA2FF,0xA17E,0xA0F3,0xA07D,0xA03E,0xA01E,0xA00E,0xA006,0xA002,0x80C3,0xA002,0x754F,0x5473,0xA004,0xA002,0x851A,
+ 0x672A,0xA002,0x7EAC,0x5C3E,0xA008,0xA004,0xA002,0x4F2A,0x4F1F,0xA002,0x59D4,0x840E,0xA004,0xA002,0x82C7,0x7EF4,
+ 0xA002,0x6F4D,0x4E3A,0xA010,0xA008,0xA004,0xA002,0x60DF,0x552F,0xA002,0x56F4,0x6845,0xA004,0xA002,0x8FDD,0x97E6,
+ 0xA002,0x5371,0x5FAE,0xA008,0xA004,0xA002,0x5DCD,0x87A4,0xA002,0x87A3,0x87A2,0xA004,0xA002,0x87A1,0x87A0,0xA002,
+ 0x879E,0x879D,0xA020,0xA010,0xA008,0xA004,0xA002,0x879C,0x879B,0xA002,0x879A,0x8799,0xA004,0xA002,0x8798,0x8796,
+ 0xA002,0x8795,0x8794,0xA008,0xA004,0xA002,0x8792,0x8791,0xA002,0x8790,0x878F,0xA004,0xA002,0x878E,0x878C,0xA002,
+ 0x878A,0x8789,0xA010,0xA008,0xA004,0xA002,0x8787,0x8786,0xA002,0x8784,0x8781,0xA004,0xA002,0x8780,0x877F,0xA002,
+ 0x877A,0x8779,0xA007,0xA004,0xA002,0x8778,0x8777,0xA001,0x8775,0xA004,0xA002,0x8773,0x8772,0xA002,0x8771,0x876F,
+ 0xA03E,0xA01E,0xA00E,0xA006,0xA002,0x876D,0xA002,0x876C,0x876B,0xA004,0xA002,0x876A,0x8769,0xA002,0x8768,0x8767,
+ 0xA008,0xA004,0xA002,0x8766,0x8762,0xA002,0x8761,0x875F,0xA004,0xA002,0x875E,0x875D,0xA002,0x875C,0x875B,0xA010,
+ 0xA008,0xA004,0xA002,0x875A,0x8758,0xA002,0x8756,0x8755,0xA004,0xA002,0x8754,0x8752,0xA002,0x8751,0x8750,0xA008,
+ 0xA004,0xA002,0x874F,0x874D,0xA002,0x874B,0x874A,0xA004,0xA002,0x8746,0x8745,0xA002,0x8744,0x8743,0xA020,0xA010,
+ 0xA008,0xA004,0xA002,0x8742,0x8741,0xA002,0x8740,0x873D,0xA004,0xA002,0x873C,0x873A,0xA002,0x8739,0x8738,0xA008,
+ 0xA004,0xA002,0x8736,0x8735,0xA002,0x8733,0x8732,0xA004,0xA002,0x8730,0x872F,0xA002,0x872D,0x872C,0xA010,0xA008,
+ 0xA004,0xA002,0x872B,0x872A,0xA002,0x8728,0x8727,0xA004,0xA002,0x8726,0x8724,0xA002,0x8720,0x871F,0xA000,0xA004,
+ 0xA002,0x871D,0x871B,0xA000,0x8719,0xA00B,0xA001,0xA001,0xA001,0xA001,0xA003,0xA001,0x5A01,0xA002,0x5984,0x5FD8,
+ 0xA040,0xA020,0xA010,0xA008,0xA004,0xA002,0x671B,0x65FA,0xA002,0x5F80,0x7F51,0xA004,0xA002,0x6789,0x4EA1,0xA002,
+ 0x738B,0x6C6A,0xA008,0xA004,0xA002,0x8155,0x4E07,0xA002,0x5A49,0x5B9B,0xA004,0xA002,0x60CB,0x7696,0xA002,0x665A,
+ 0x633D,0xA010,0xA008,0xA004,0xA002,0x7897,0x5B8C,0xA002,0x70F7,0x4E38,0xA004,0xA002,0x987D,0x73A9,0xA002,0x6E7E,
+ 0x5F2F,0xA008,0xA004,0xA002,0x8C4C,0x5916,0xA002,0x6B6A,0x889C,0xA004,0xA002,0x74E6,0x5A03,0xA002,0x6D3C,0x86D9,
+ 0xA020,0xA010,0xA008,0xA004,0xA002,0x54C7,0x6316,0xA002,0x553E,0x62D3,0xA004,0xA002,0x59A5,0x692D,0xA002,0x9A7C,
+ 0x9A6E,0xA008,0xA004,0xA002,0x9640,0x9E35,0xA002,0x8131,0x6258,0xA004,0xA002,0x62D6,0x81C0,0xA002,0x5C6F,0x541E,
+ 0xA010,0xA008,0xA004,0xA002,0x9000,0x892A,0xA002,0x8715,0x817F,0xA004,0xA002,0x9893,0x63A8,0xA002,0x56E2,0x6E4D,
+ 0xA008,0xA004,0xA002,0x5154,0x5410,0xA002,0x571F,0x5C60,0xA004,0xA002,0x6D82,0x9014,0xA002,0x5F92,0x56FE,0xA0F9,
+ 0xA07D,0xA03E,0xA01E,0xA00E,0xA006,0xA002,0x7A81,0xA002,0x79C3,0x51F8,0xA004,0xA002,0x900F,0x5934,0xA002,0x6295,
+ 0x5077,0xA008,0xA004,0xA002,0x75DB,0x7EDF,0xA002,0x7B52,0x6345,0xA004,0xA002,0x6876,0x7AE5,0xA002,0x5F64,0x94DC,
+ 0xA010,0xA008,0xA004,0xA002,0x540C,0x77B3,0xA002,0x916E,0x6850,0xA004,0xA002,0x901A,0x8247,0xA002,0x633A,0x5EAD,
+ 0xA008,0xA004,0xA002,0x4EAD,0x505C,0xA002,0x5EF7,0x6C40,0xA004,0xA002,0x8716,0x8714,0xA002,0x8711,0x8710,0xA020,
+ 0xA010,0xA008,0xA004,0xA002,0x870F,0x870E,0xA002,0x870C,0x870B,0xA004,0xA002,0x8706,0x8705,0xA002,0x8704,0x8701,
+ 0xA008,0xA004,0xA002,0x86FF,0x86FD,0xA002,0x86FC,0x86FB,0xA004,0xA002,0x86FA,0x86F7,0xA002,0x86F6,0x86F5,0xA010,
+ 0xA008,0xA004,0xA002,0x86EF,0x86EC,0xA002,0x86EB,0x86EA,0xA004,0xA002,0x86E8,0x86E7,0xA002,0x86E6,0x86E5,0xA008,
+ 0xA004,0xA002,0x86E3,0x86E2,0xA002,0x86E1,0x86E0,0xA003,0xA000,0x86DD,0xA002,0x86DC,0x86DA,0xA040,0xA020,0xA010,
+ 0xA008,0xA004,0xA002,0x86D7,0x86D6,0xA002,0x86D5,0x86D3,0xA004,0xA002,0x86D2,0x86CD,0xA002,0x86CC,0x86C8,0xA008,
+ 0xA004,0xA002,0x86C5,0x86C3,0xA002,0x86C2,0x86C1,0xA004,0xA002,0x86BF,0x86BE,0xA002,0x86BD,0x86BC,0xA010,0xA008,
+ 0xA004,0xA002,0x86BB,0x86B9,0xA002,0x86B8,0x86B7,0xA004,0xA002,0x86B3,0x86B2,0xA002,0x86AE,0x86AD,0xA008,0xA004,
+ 0xA002,0x86AB,0x86A6,0xA002,0x86A5,0x86A2,0xA004,0xA002,0x86A1,0x86A0,0xA002,0x869F,0x869E,0xA020,0xA010,0xA008,
+ 0xA004,0xA002,0x869B,0x869A,0xA002,0x8699,0x8698,0xA004,0xA002,0x8697,0x8696,0xA002,0x8694,0x8692,0xA008,0xA004,
+ 0xA002,0x8691,0x8690,0xA002,0x868F,0x868E,0xA004,0xA002,0x8689,0x8688,0xA002,0x8687,0x8686,0xA010,0xA008,0xA004,
+ 0xA002,0x8685,0x8684,0xA002,0x8683,0x8678,0xA004,0xA002,0x8677,0x8676,0xA002,0x8675,0x8674,0xA008,0xA004,0xA002,
+ 0x8673,0x8672,0xA002,0x8670,0x866F,0xA000,0xA000,0x866D,0xA008,0xA001,0xA001,0xA001,0xA001,0xA001,0xA001,0x70C3,
+ 0xA040,0xA020,0xA010,0xA008,0xA004,0xA002,0x542C,0x5385,0xA002,0x5E16,0x94C1,0xA004,0xA002,0x8D34,0x8DF3,0xA002,
+ 0x773A,0x8FE2,0xA008,0xA004,0xA002,0x6761,0x6311,0xA002,0x8146,0x8214,0xA004,0xA002,0x606C,0x751C,0xA002,0x7530,
+ 0x586B,0xA010,0xA008,0xA004,0xA002,0x6DFB,0x5929,0xA002,0x5C49,0x5243,0xA004,0xA002,0x6D95,0x60D5,0xA002,0x568F,
+ 0x66FF,0xA008,0xA004,0xA002,0x4F53,0x557C,0xA002,0x8E44,0x9898,0xA004,0xA002,0x63D0,0x9511,0xA002,0x8E22,0x5254,
+ 0xA020,0xA010,0xA008,0xA004,0xA002,0x68AF,0x8A8A,0xA002,0x75BC,0x817E,0xA004,0xA002,0x85E4,0x7279,0xA002,0x5957,
+ 0x8BA8,0xA008,0xA004,0xA002,0x9676,0x6DD8,0xA002,0x9003,0x6843,0xA004,0xA002,0x8404,0x7EE6,0xA002,0x6ED4,0x6D9B,
+ 0xA010,0xA008,0xA004,0xA002,0x638F,0x70EB,0xA002,0x8D9F,0x6DCC,0xA004,0xA002,0x8EBA,0x5018,0xA002,0x7CD6,0x5510,
+ 0xA008,0xA004,0xA002,0x819B,0x68E0,0xA002,0x5802,0x642A,0xA004,0xA002,0x5858,0x6C64,0xA002,0x70AD,0x53F9,0xA17C,
+ 0xA0FC,0xA07D,0xA03E,0xA01E,0xA00E,0xA006,0xA002,0x63A2,0xA002,0x78B3,0x8892,0xA004,0xA002,0x6BEF,0x5766,0xA002,
+ 0x8C08,0x8C2D,0xA008,0xA004,0xA002,0x6F6D,0x75F0,0xA002,0x6A80,0x575B,0xA004,0xA002,0x6EE9,0x762B,0xA002,0x8D2A,
+ 0x644A,0xA010,0xA008,0xA004,0xA002,0x574D,0x6C70,0xA002,0x6001,0x592A,0xA004,0xA002,0x915E,0x6CF0,0xA002,0x53F0,
+ 0x62AC,0xA008,0xA004,0xA002,0x82D4,0x80CE,0xA002,0x8E0F,0x8E4B,0xA004,0xA002,0x631E,0x736D,0xA002,0x866A,0x8669,
+ 0xA020,0xA010,0xA008,0xA004,0xA002,0x8668,0x8667,0xA002,0x8666,0x8665,0xA004,0xA002,0x8664,0x8663,0xA002,0x8661,
+ 0x8660,0xA008,0xA004,0xA002,0x865F,0x865D,0xA002,0x865C,0x865B,0xA004,0xA002,0x8659,0x8658,0xA002,0x8657,0x8656,
+ 0xA010,0xA008,0xA004,0xA002,0x8655,0x8653,0xA002,0x8652,0x864C,0xA004,0xA002,0x864B,0x864A,0xA002,0x8649,0x8648,
+ 0xA008,0xA004,0xA002,0x8647,0x8646,0xA002,0x8645,0x8644,0xA004,0xA002,0x8643,0x8642,0xA000,0x8641,0xA040,0xA020,
+ 0xA010,0xA008,0xA004,0xA002,0x8640,0x863F,0xA002,0x863E,0x863D,0xA004,0xA002,0x863B,0x863A,0xA002,0x8639,0x8637,
+ 0xA008,0xA004,0xA002,0x8636,0x8635,0xA002,0x8634,0x8633,0xA004,0xA002,0x8632,0x8631,0xA002,0x8630,0x862F,0xA010,
+ 0xA008,0xA004,0xA002,0x862E,0x862D,0xA002,0x862C,0x862B,0xA004,0xA002,0x862A,0x8628,0xA002,0x8626,0x8625,0xA008,
+ 0xA004,0xA002,0x8624,0x8623,0xA002,0x8622,0x8621,0xA004,0xA002,0x8620,0x861F,0xA002,0x861E,0x861D,0xA020,0xA010,
+ 0xA008,0xA004,0xA002,0x861C,0x861B,0xA002,0x861A,0x8619,0xA004,0xA002,0x8618,0x8617,0xA002,0x8615,0x8614,0xA008,
+ 0xA004,0xA002,0x8613,0x8612,0xA002,0x8610,0x860F,0xA004,0xA002,0x860E,0x860D,0xA002,0x860C,0x860B,0xA010,0xA008,
+ 0xA004,0xA002,0x860A,0x8609,0xA002,0x8608,0x8607,0xA004,0xA002,0x8606,0x8604,0xA002,0x8603,0x8602,0xA008,0xA004,
+ 0xA002,0x8601,0x8600,0xA002,0x85FE,0x85FD,0xA004,0xA002,0x85FC,0x85FA,0xA000,0x85F9,0xA001,0xA03F,0xA01F,0xA00F,
+ 0xA007,0xA003,0xA001,0x5854,0xA002,0x5979,0x5B83,0xA004,0xA002,0x4ED6,0x584C,0xA002,0x6240,0x9501,0xA008,0xA004,
+ 0xA002,0x7D22,0x7410,0xA002,0x7F29,0x5506,0xA004,0xA002,0x68AD,0x84D1,0xA002,0x7B0B,0x635F,0xA010,0xA008,0xA004,
+ 0xA002,0x5B59,0x795F,0xA002,0x96A7,0x9042,0xA004,0xA002,0x7A57,0x5C81,0xA002,0x788E,0x9AD3,0xA008,0xA004,0xA002,
+ 0x7EE5,0x968F,0xA002,0x968B,0x867D,0xA004,0xA002,0x7B97,0x849C,0xA002,0x9178,0x8083,0xA020,0xA010,0xA008,0xA004,
+ 0xA002,0x8BC9,0x5BBF,0xA002,0x6EAF,0x5851,0xA004,0xA002,0x50F3,0x7C9F,0xA002,0x901F,0x7D20,0xA008,0xA004,0xA002,
+ 0x4FD7,0x9165,0xA002,0x82CF,0x55FD,0xA004,0xA002,0x64DE,0x8258,0xA002,0x641C,0x8BF5,0xA010,0xA008,0xA004,0xA002,
+ 0x8BBC,0x5B8B,0xA002,0x9001,0x9882,0xA004,0xA002,0x6002,0x8038,0xA002,0x677E,0x5DF3,0xA008,0xA004,0xA002,0x9972,
+ 0x4F3C,0xA002,0x4F3A,0x56DB,0xA004,0xA002,0x55E3,0x5BFA,0xA002,0x8086,0x6B7B,0xA0FD,0xA07E,0xA03E,0xA01E,0xA00E,
+ 0xA006,0xA002,0x4E1D,0xA002,0x53F8,0x79C1,0xA004,0xA002,0x601D,0x5636,0xA002,0x6495,0x65AF,0xA008,0xA004,0xA002,
+ 0x70C1,0x6714,0xA002,0x7855,0x8BF4,0xA004,0xA002,0x821C,0x987A,0xA002,0x77AC,0x542E,0xA010,0xA008,0xA004,0xA002,
+ 0x7A0E,0x7761,0xA002,0x6C34,0x8C01,0xA004,0xA002,0x723D,0x53CC,0xA002,0x971C,0x62F4,0xA008,0xA004,0xA002,0x6813,
+ 0x5E05,0xA002,0x7529,0x8870,0xA004,0xA002,0x6454,0x800D,0xA002,0x5237,0x6055,0xA020,0xA010,0xA008,0xA004,0xA002,
+ 0x85F8,0x85F7,0xA002,0x85F6,0x85F5,0xA004,0xA002,0x85F4,0x85F3,0xA002,0x85F2,0x85F1,0xA008,0xA004,0xA002,0x85F0,
+ 0x85EF,0xA002,0x85EE,0x85ED,0xA004,0xA002,0x85EC,0x85EB,0xA002,0x85EA,0x85E8,0xA010,0xA008,0xA004,0xA002,0x85E7,
+ 0x85E6,0xA002,0x85E5,0x85E3,0xA004,0xA002,0x85E2,0x85E1,0xA002,0x85E0,0x85DF,0xA008,0xA004,0xA002,0x85DE,0x85DD,
+ 0xA002,0x85DB,0x85DA,0xA004,0xA002,0x85D9,0x85D8,0xA002,0x85D7,0x85D6,0xA03F,0xA01F,0xA00F,0xA007,0xA003,0xA000,
+ 0x85D4,0xA002,0x85D2,0x85D1,0xA004,0xA002,0x85CE,0x85CD,0xA002,0x85CC,0x85CB,0xA008,0xA004,0xA002,0x85CA,0x85C8,
+ 0xA002,0x85C7,0x85C6,0xA004,0xA002,0x85C5,0x85C4,0xA002,0x85C3,0x85C2,0xA010,0xA008,0xA004,0xA002,0x85C0,0x85BF,
+ 0xA002,0x85BE,0x85BD,0xA004,0xA002,0x85BC,0x85BB,0xA002,0x85BA,0x85B8,0xA008,0xA004,0xA002,0x85B6,0x85B5,0xA002,
+ 0x85B4,0x85B3,0xA004,0xA002,0x85B2,0x85B1,0xA002,0x85AD,0x85AC,0xA020,0xA010,0xA008,0xA004,0xA002,0x85AB,0x85A9,
+ 0xA002,0x85A7,0x85A6,0xA004,0xA002,0x85A5,0x85A3,0xA002,0x85A2,0x85A1,0xA008,0xA004,0xA002,0x85A0,0x859F,0xA002,
+ 0x859E,0x859D,0xA004,0xA002,0x859A,0x8599,0xA002,0x8598,0x8597,0xA010,0xA008,0xA004,0xA002,0x8596,0x8595,0xA002,
+ 0x8594,0x8593,0xA004,0xA002,0x8592,0x8591,0xA002,0x8590,0x858E,0xA008,0xA004,0xA002,0x858D,0x858C,0xA002,0x858B,
+ 0x858A,0xA004,0xA002,0x8589,0x8588,0xA002,0x8586,0x8583,0xA007,0xA000,0xA000,0xA000,0xA000,0xA000,0x8582,0xA03C,
+ 0xA01C,0xA00C,0xA004,0xA001,0xA001,0x6F31,0xA004,0xA002,0x6570,0x5EB6,0xA002,0x5885,0x7AD6,0xA008,0xA004,0xA002,
+ 0x620D,0x675F,0xA002,0x6811,0x8FF0,0xA004,0xA002,0x672F,0x5C5E,0xA002,0x9F20,0x9ECD,0xA010,0xA008,0xA004,0xA002,
+ 0x8700,0x7F72,0xA002,0x66D9,0x6691,0xA004,0xA002,0x85AF,0x719F,0xA002,0x5B70,0x8D4E,0xA008,0xA004,0xA002,0x4E66,
+ 0x758F,0xA002,0x6DD1,0x8212,0xA004,0xA002,0x53D4,0x8F93,0xA002,0x6292,0x6B8A,0xA020,0xA010,0xA008,0xA004,0xA002,
+ 0x68B3,0x67A2,0xA002,0x852C,0x517D,0xA004,0xA002,0x7626,0x53D7,0xA002,0x552E,0x6388,0xA008,0xA004,0xA002,0x5BFF,
+ 0x5B88,0xA002,0x9996,0x624B,0xA004,0xA002,0x6536,0x8BD5,0xA002,0x89C6,0x5BA4,0xA010,0xA008,0xA004,0xA002,0x6043,
+ 0x5E02,0xA002,0x6C0F,0x9970,0xA004,0xA002,0x91CA,0x4F8D,0xA002,0x4ED5,0x9002,0xA008,0xA004,0xA002,0x566C,0x55DC,
+ 0xA002,0x662F,0x52BF,0xA004,0xA002,0x901D,0x8A93,0xA002,0x62ED,0x4E8B,0xA2FC,0xA17E,0xA0FB,0xA07E,0xA03E,0xA01E,
+ 0xA00E,0xA006,0xA002,0x67FF,0xA002,0x4E16,0x58EB,0xA004,0xA002,0x793A,0x5F0F,0xA002,0x59CB,0x9A76,0xA008,0xA004,
+ 0xA002,0x5C4E,0x4F7F,0xA002,0x77E2,0x53F2,0xA004,0xA002,0x8BC6,0x5B9E,0xA002,0x8680,0x98DF,0xA010,0xA008,0xA004,
+ 0xA002,0x4EC0,0x65F6,0xA002,0x62FE,0x77F3,0xA004,0xA002,0x5341,0x8671,0xA002,0x5C38,0x8BD7,0xA008,0xA004,0xA002,
+ 0x6E7F,0x65BD,0xA002,0x72EE,0x5931,0xA004,0xA002,0x5E08,0x5723,0xA002,0x80DC,0x5269,0xA020,0xA010,0xA008,0xA004,
+ 0xA002,0x76DB,0x7701,0xA002,0x8581,0x8580,0xA004,0xA002,0x857F,0x857D,0xA002,0x857C,0x8578,0xA008,0xA004,0xA002,
+ 0x8577,0x8576,0xA002,0x8575,0x8573,0xA004,0xA002,0x8571,0x8570,0xA002,0x856F,0x856E,0xA010,0xA008,0xA004,0xA002,
+ 0x856D,0x856C,0xA002,0x856B,0x856A,0xA004,0xA002,0x8569,0x8567,0xA002,0x8566,0x8565,0xA008,0xA004,0xA002,0x8563,
+ 0x8562,0xA002,0x8561,0x8560,0xA004,0xA002,0x855F,0x855D,0xA002,0x855C,0x855B,0xA03D,0xA01D,0xA00D,0xA006,0xA002,
+ 0x855A,0xA002,0x8558,0x8557,0xA003,0xA001,0x8555,0xA002,0x8554,0x8553,0xA008,0xA004,0xA002,0x8552,0x8551,0xA002,
+ 0x8550,0x854F,0xA004,0xA002,0x854E,0x854D,0xA002,0x854C,0x854B,0xA010,0xA008,0xA004,0xA002,0x8547,0x8546,0xA002,
+ 0x8545,0x8544,0xA004,0xA002,0x8542,0x8541,0xA002,0x8540,0x853F,0xA008,0xA004,0xA002,0x853E,0x8536,0xA002,0x8535,
+ 0x8534,0xA004,0xA002,0x8533,0x8532,0xA002,0x8531,0x8530,0xA020,0xA010,0xA008,0xA004,0xA002,0x852F,0x852E,0xA002,
+ 0x852D,0x852A,0xA004,0xA002,0x8529,0x8528,0xA002,0x8527,0x8526,0xA008,0xA004,0xA002,0x8525,0x8524,0xA002,0x8523,
+ 0x8522,0xA004,0xA002,0x8520,0x851E,0xA002,0x851D,0x851C,0xA010,0xA008,0xA004,0xA002,0x851B,0x8519,0xA002,0x8518,
+ 0x8516,0xA004,0xA002,0x8515,0x8514,0xA002,0x8512,0x8510,0xA008,0xA004,0xA002,0x850F,0x850E,0xA002,0x850D,0x850B,
+ 0xA004,0xA002,0x850A,0x8509,0xA002,0x8508,0x8507,0xA00D,0xA000,0xA000,0xA000,0xA006,0xA002,0x8506,0xA002,0x8505,
+ 0x8504,0xA000,0xA000,0x8503,0xA036,0xA016,0xA006,0xA001,0xA001,0xA002,0x7EF3,0x5347,0xA008,0xA004,0xA002,0x7272,
+ 0x7525,0xA002,0x751F,0x58F0,0xA004,0xA002,0x6E17,0x614E,0xA002,0x80BE,0x751A,0xA010,0xA008,0xA004,0xA002,0x5A76,
+ 0x5BA1,0xA002,0x6C88,0x795E,0xA004,0xA002,0x7EC5,0x5A20,0xA002,0x6DF1,0x8EAB,0xA008,0xA004,0xA002,0x4F38,0x547B,
+ 0xA002,0x7533,0x7837,0xA004,0xA002,0x8BBE,0x793E,0xA002,0x6D89,0x6151,0xA020,0xA010,0xA008,0xA004,0xA002,0x5C04,
+ 0x6444,0xA002,0x8D66,0x820D,0xA004,0xA002,0x820C,0x86C7,0xA002,0x8D4A,0x5962,0xA008,0xA004,0xA002,0x7ECD,0x90B5,
+ 0xA002,0x54E8,0x5C11,0xA004,0xA002,0x97F6,0x52FA,0xA002,0x828D,0x70E7,0xA010,0xA008,0xA004,0xA002,0x7A0D,0x634E,
+ 0xA002,0x68A2,0x88F3,0xA004,0xA002,0x5C1A,0x4E0A,0xA002,0x664C,0x8D4F,0xA008,0xA004,0xA002,0x5546,0x4F24,0xA002,
+ 0x5892,0x7F2E,0xA004,0xA002,0x6247,0x6C55,0xA002,0x5584,0x81B3,0xA0FD,0xA07E,0xA03E,0xA01E,0xA00E,0xA006,0xA002,
+ 0x8D61,0xA002,0x64C5,0x9655,0xA004,0xA002,0x95EA,0x886B,0xA002,0x717D,0x5220,0xA008,0xA004,0xA002,0x5C71,0x6749,
+ 0xA002,0x82EB,0x73CA,0xA004,0xA002,0x6652,0x7B5B,0xA002,0x715E,0x5565,0xA010,0xA008,0xA004,0xA002,0x50BB,0x7EB1,
+ 0xA002,0x6C99,0x5239,0xA004,0xA002,0x6740,0x7802,0xA002,0x838E,0x50E7,0xA008,0xA004,0xA002,0x68EE,0x6DA9,0xA002,
+ 0x8272,0x745F,0xA004,0xA002,0x5AC2,0x626B,0xA002,0x9A9A,0x6414,0xA020,0xA010,0xA008,0xA004,0xA002,0x4E27,0x55D3,
+ 0xA002,0x6851,0x6563,0xA004,0xA002,0x4F1E,0x8502,0xA002,0x8501,0x8500,0xA008,0xA004,0xA002,0x84FE,0x84FD,0xA002,
+ 0x84FB,0x84FA,0xA004,0xA002,0x84F9,0x84F8,0xA002,0x84F7,0x84F6,0xA010,0xA008,0xA004,0xA002,0x84F5,0x84F4,0xA002,
+ 0x84F3,0x84F2,0xA004,0xA002,0x84F1,0x84EF,0xA002,0x84EE,0x84ED,0xA008,0xA004,0xA002,0x84EB,0x84EA,0xA002,0x84E9,
+ 0x84E8,0xA004,0xA002,0x84E7,0x84E4,0xA002,0x84E2,0x84E1,0xA03F,0xA01F,0xA00F,0xA008,0xA004,0xA002,0x84DE,0x84DC,
+ 0xA002,0x84DB,0x84DA,0xA004,0xA002,0x84D9,0x84D8,0xA001,0x84D7,0xA008,0xA004,0xA002,0x84D5,0x84D4,0xA002,0x84D2,
+ 0x84CF,0xA004,0xA002,0x84CE,0x84CC,0xA002,0x84CB,0x84C8,0xA010,0xA008,0xA004,0xA002,0x84C7,0x84C6,0xA002,0x84C5,
+ 0x84C3,0xA004,0xA002,0x84C2,0x84C0,0xA002,0x84BE,0x84BC,0xA008,0xA004,0xA002,0x84BB,0x84B7,0xA002,0x84B6,0x84B5,
+ 0xA004,0xA002,0x84B3,0x84B1,0xA002,0x84B0,0x84AE,0xA020,0xA010,0xA008,0xA004,0xA002,0x84AD,0x84AC,0xA002,0x84AB,
+ 0x84AA,0xA004,0xA002,0x84A9,0x84A8,0xA002,0x84A7,0x84A6,0xA008,0xA004,0xA002,0x84A5,0x84A4,0xA002,0x84A3,0x84A2,
+ 0xA004,0xA002,0x84A0,0x849F,0xA002,0x849E,0x849D,0xA010,0xA008,0xA004,0xA002,0x849B,0x849A,0xA002,0x8498,0x8496,
+ 0xA004,0xA002,0x8495,0x8494,0xA002,0x8493,0x8492,0xA008,0xA004,0xA002,0x8491,0x8490,0xA002,0x848F,0x848D,0xA004,
+ 0xA002,0x848A,0x8486,0xA002,0x8485,0x8484,0xA010,0xA000,0xA000,0xA000,0xA006,0xA002,0x8483,0xA002,0x8481,0x8480,
+ 0xA004,0xA002,0x847F,0x847E,0xA000,0x847D,0xA031,0xA011,0xA001,0xA008,0xA004,0xA002,0x53C1,0x4E09,0xA002,0x8D5B,
+ 0x585E,0xA004,0xA002,0x9CC3,0x816E,0xA002,0x8428,0x6D12,0xA010,0xA008,0xA004,0xA002,0x6492,0x5F31,0xA002,0x82E5,
+ 0x6DA6,0xA004,0xA002,0x95F0,0x9510,0xA002,0x745E,0x854A,0xA008,0xA004,0xA002,0x962E,0x8F6F,0xA002,0x8925,0x5165,
+ 0xA004,0xA002,0x6C5D,0x4E73,0xA002,0x8FB1,0x5982,0xA020,0xA010,0xA008,0xA004,0xA002,0x5B7A,0x5112,0xA002,0x8815,
+ 0x8339,0xA004,0xA002,0x8089,0x67D4,0xA002,0x63C9,0x5197,0xA008,0xA004,0xA002,0x7ED2,0x5BB9,0xA002,0x6EB6,0x7194,
+ 0xA004,0xA002,0x878D,0x8363,0xA002,0x84C9,0x8338,0xA010,0xA008,0xA004,0xA002,0x620E,0x65E5,0xA002,0x4ECD,0x6254,
+ 0xA004,0xA002,0x7EAB,0x598A,0xA002,0x5203,0x8BA4,0xA008,0xA004,0xA002,0x4EFB,0x97E7,0xA002,0x5FCD,0x4EBA,0xA004,
+ 0xA002,0x4EC1,0x58EC,0xA002,0x70ED,0x60F9,0xA180,0xA0FD,0xA07E,0xA03E,0xA01E,0xA00E,0xA006,0xA002,0x7ED5,0xA002,
+ 0x6270,0x9976,0xA004,0xA002,0x8BA9,0x56B7,0xA002,0x6518,0x58E4,0xA008,0xA004,0xA002,0x74E4,0x67D3,0xA002,0x5189,
+ 0x71C3,0xA004,0xA002,0x7136,0x7FA4,0xA002,0x88D9,0x96C0,0xA010,0xA008,0xA004,0xA002,0x786E,0x69B7,0xA002,0x9E4A,
+ 0x5374,0xA004,0xA002,0x7638,0x7094,0xA002,0x7F3A,0x529D,0xA008,0xA004,0xA002,0x5238,0x72AC,0xA002,0x62F3,0x75CA,
+ 0xA004,0xA002,0x5168,0x6CC9,0xA002,0x919B,0x6743,0xA020,0xA010,0xA008,0xA004,0xA002,0x98A7,0x5708,0xA002,0x53BB,
+ 0x8DA3,0xA004,0xA002,0x9F8B,0x5A36,0xA002,0x53D6,0x847C,0xA008,0xA004,0xA002,0x847B,0x8479,0xA002,0x8477,0x8474,
+ 0xA004,0xA002,0x8472,0x8470,0xA002,0x846F,0x846E,0xA010,0xA008,0xA004,0xA002,0x846A,0x8468,0xA002,0x8467,0x8466,
+ 0xA004,0xA002,0x8465,0x8464,0xA002,0x8462,0x8460,0xA008,0xA004,0xA002,0x845F,0x845E,0xA002,0x845D,0x8458,0xA004,
+ 0xA002,0x8456,0x8455,0xA002,0x8454,0x8453,0xA03F,0xA01F,0xA010,0xA008,0xA004,0xA002,0x8452,0x8450,0xA002,0x844F,
+ 0x844E,0xA004,0xA002,0x844D,0x844C,0xA002,0x844B,0x844A,0xA007,0xA003,0xA001,0x8449,0xA002,0x8448,0x8447,0xA004,
+ 0xA002,0x8445,0x8444,0xA002,0x8443,0x8442,0xA010,0xA008,0xA004,0xA002,0x8441,0x8440,0xA002,0x843F,0x843E,0xA004,
+ 0xA002,0x843B,0x843A,0xA002,0x8439,0x8437,0xA008,0xA004,0xA002,0x8436,0x8435,0xA002,0x8434,0x8433,0xA004,0xA002,
+ 0x8432,0x8430,0xA002,0x842F,0x842E,0xA020,0xA010,0xA008,0xA004,0xA002,0x842D,0x842C,0xA002,0x842B,0x842A,0xA004,
+ 0xA002,0x8429,0x8423,0xA002,0x8422,0x8421,0xA008,0xA004,0xA002,0x8420,0x841F,0xA002,0x841E,0x841B,0xA004,0xA002,
+ 0x841A,0x8419,0xA002,0x8417,0x8416,0xA010,0xA008,0xA004,0xA002,0x8415,0x8414,0xA002,0x8413,0x8412,0xA004,0xA002,
+ 0x8410,0x840A,0xA002,0x8409,0x8408,0xA008,0xA004,0xA002,0x8407,0x8405,0xA002,0x8402,0x8400,0xA004,0xA002,0x83FF,
+ 0x83FE,0xA002,0x83FC,0x83FB,0xA015,0xA000,0xA000,0xA00E,0xA006,0xA002,0x83FA,0xA002,0x83F7,0x83F6,0xA004,0xA002,
+ 0x83F5,0x83F4,0xA002,0x83F3,0x83EF,0xA000,0xA000,0xA000,0x83EE,0xA02E,0xA00E,0xA001,0xA005,0xA001,0xA002,0x6E20,
+ 0x9A71,0xA004,0xA002,0x5C48,0x8EAF,0xA002,0x66F2,0x86C6,0xA010,0xA008,0xA004,0xA002,0x533A,0x8D8B,0xA002,0x6CC5,
+ 0x914B,0xA004,0xA002,0x56DA,0x6C42,0xA002,0x7403,0x90B1,0xA008,0xA004,0xA002,0x4E18,0x79CB,0xA002,0x7A77,0x743C,
+ 0xA004,0xA002,0x5E86,0x8BF7,0xA002,0x9877,0x60C5,0xA020,0xA010,0xA008,0xA004,0xA002,0x6C30,0x6674,0xA002,0x64CE,
+ 0x6E05,0xA004,0xA002,0x537F,0x503E,0xA002,0x6C22,0x8F7B,0xA008,0xA004,0xA002,0x9752,0x6C81,0xA002,0x5BDD,0x79BD,
+ 0xA004,0xA002,0x64D2,0x82B9,0xA002,0x52E4,0x7434,0xA010,0xA008,0xA004,0xA002,0x79E6,0x4EB2,0xA002,0x4FB5,0x94A6,
+ 0xA004,0xA002,0x7A83,0x602F,0xA002,0x4E14,0x8304,0xA008,0xA004,0xA002,0x5207,0x7A8D,0xA002,0x4FCF,0x5CED,0xA004,
+ 0xA002,0x7FD8,0x64AC,0xA002,0x9798,0x5DE7,0xA0FD,0xA07E,0xA03E,0xA01E,0xA00E,0xA006,0xA002,0x4FA8,0xA002,0x4E54,
+ 0x77A7,0xA004,0xA002,0x6865,0x6084,0xA002,0x6572,0x9539,0xA008,0xA004,0xA002,0x6A47,0x62A2,0xA002,0x5F3A,0x8537,
+ 0xA004,0xA002,0x5899,0x7F8C,0xA002,0x8154,0x545B,0xA010,0xA008,0xA004,0xA002,0x67AA,0x6B49,0xA002,0x6B20,0x5D4C,
+ 0xA004,0xA002,0x5811,0x8C34,0xA002,0x6D45,0x9063,0xA008,0xA004,0xA002,0x6F5C,0x524D,0xA002,0x94B3,0x94B1,0xA004,
+ 0xA002,0x9ED4,0x4E7E,0xA002,0x8C26,0x4EDF,0xA020,0xA010,0xA008,0xA004,0xA002,0x7B7E,0x8FC1,0xA002,0x5343,0x94C5,
+ 0xA004,0xA002,0x948E,0x6266,0xA002,0x7275,0x6D3D,0xA008,0xA004,0xA002,0x6070,0x83ED,0xA002,0x83EC,0x83EB,0xA004,
+ 0xA002,0x83E8,0x83E7,0xA002,0x83E6,0x83E4,0xA010,0xA008,0xA004,0xA002,0x83E3,0x83E2,0xA002,0x83DE,0x83DB,0xA004,
+ 0xA002,0x83DA,0x83D9,0xA002,0x83D7,0x83D5,0xA008,0xA004,0xA002,0x83D3,0x83D2,0xA002,0x83D1,0x83D0,0xA004,0xA002,
+ 0x83CE,0x83CD,0xA002,0x83CB,0x83C9,0xA03F,0xA01F,0xA010,0xA008,0xA004,0xA002,0x83C8,0x83C6,0xA002,0x83C4,0x83C3,
+ 0xA004,0xA002,0x83C2,0x83BF,0xA002,0x83BE,0x83BB,0xA007,0xA004,0xA002,0x83B5,0x83AF,0xA001,0x83AE,0xA004,0xA002,
+ 0x83AD,0x83AC,0xA002,0x83A7,0x83A6,0xA010,0xA008,0xA004,0xA002,0x83A5,0x83A4,0xA002,0x83A3,0x83A2,0xA004,0xA002,
+ 0x83A1,0x839F,0xA002,0x839D,0x839A,0xA008,0xA004,0xA002,0x8399,0x8397,0xA002,0x8396,0x8395,0xA004,0xA002,0x8394,
+ 0x8391,0xA002,0x8390,0x838F,0xA020,0xA010,0xA008,0xA004,0xA002,0x838D,0x838C,0xA002,0x838B,0x838A,0xA004,0xA002,
+ 0x8388,0x8387,0xA002,0x8384,0x8383,0xA008,0xA004,0xA002,0x8382,0x8381,0xA002,0x8380,0x837F,0xA004,0xA002,0x837E,
+ 0x837A,0xA002,0x8379,0x8376,0xA010,0xA008,0xA004,0xA002,0x8375,0x8374,0xA002,0x8373,0x8372,0xA004,0xA002,0x8371,
+ 0x8370,0xA002,0x8362,0x835D,0xA008,0xA004,0xA002,0x8359,0x8358,0xA002,0x8357,0x8356,0xA004,0xA002,0x8355,0x8353,
+ 0xA002,0x834E,0x834D,0xA018,0xA000,0xA000,0xA00E,0xA006,0xA002,0x834C,0xA002,0x834B,0x834A,0xA004,0xA002,0x8348,
+ 0x8345,0xA002,0x8344,0x8342,0xA000,0xA004,0xA002,0x8341,0x833F,0xA000,0x833E,0xA02A,0xA00A,0xA001,0xA001,0xA004,
+ 0xA002,0x6390,0x8BAB,0xA002,0x6CE3,0x6C7D,0xA010,0xA008,0xA004,0xA002,0x5F03,0x8FC4,0xA002,0x6C14,0x5668,0xA004,
+ 0xA002,0x780C,0x5951,0xA002,0x542F,0x4F01,0xA008,0xA004,0xA002,0x4E5E,0x5C82,0xA002,0x8D77,0x9A91,0xA004,0xA002,
+ 0x7941,0x7948,0xA002,0x65D7,0x9F50,0xA020,0xA010,0xA008,0xA004,0xA002,0x8110,0x5D0E,0xA002,0x7566,0x6B67,0xA004,
+ 0xA002,0x5947,0x68CB,0xA002,0x5176,0x6C8F,0xA008,0xA004,0xA002,0x67D2,0x6F06,0xA002,0x51C4,0x4E03,0xA004,0xA002,
+ 0x59BB,0x621A,0xA002,0x6816,0x6B3A,0xA010,0xA008,0xA004,0xA002,0x671F,0x7011,0xA002,0x66DD,0x8C31,0xA004,0xA002,
+ 0x6D66,0x666E,0xA002,0x5703,0x6734,0xA008,0xA004,0xA002,0x57D4,0x84B2,0xA002,0x83E9,0x8461,0xA004,0xA002,0x8386,
+ 0x4EC6,0xA002,0x94FA,0x6251,0xA5FA,0xA2FB,0xA17E,0xA0FB,0xA07E,0xA03E,0xA01E,0xA00E,0xA006,0xA002,0x5256,0xA002,
+ 0x7C95,0x8FEB,0xA004,0xA002,0x9B44,0x7834,0xA002,0x5A46,0x9887,0xA008,0xA004,0xA002,0x6CFC,0x5761,0xA002,0x5C4F,
+ 0x8BC4,0xA004,0xA002,0x74F6,0x51ED,0xA002,0x5E73,0x840D,0xA010,0xA008,0xA004,0xA002,0x82F9,0x576A,0xA002,0x4E52,
+ 0x8058,0xA004,0xA002,0x54C1,0x8D2B,0xA002,0x9891,0x62FC,0xA008,0xA004,0xA002,0x77A5,0x6487,0xA002,0x7968,0x74E2,
+ 0xA004,0xA002,0x6F02,0x98D8,0xA002,0x9A97,0x7247,0xA020,0xA010,0xA008,0xA004,0xA002,0x504F,0x7BC7,0xA002,0x8B6C,
+ 0x5C41,0xA004,0xA002,0x50FB,0x75DE,0xA002,0x5339,0x76AE,0xA008,0xA004,0xA002,0x75B2,0x813E,0xA002,0x5564,0x833D,
+ 0xA004,0xA002,0x833B,0x8337,0xA002,0x8332,0x8330,0xA010,0xA008,0xA004,0xA002,0x832E,0x832A,0xA002,0x8329,0x8326,
+ 0xA004,0xA002,0x8325,0x8324,0xA002,0x8323,0x8322,0xA008,0xA004,0xA002,0x8321,0x8320,0xA002,0x831F,0x831E,0xA004,
+ 0xA002,0x831D,0x8319,0xA002,0x8318,0x8316,0xA03D,0xA01D,0xA00E,0xA006,0xA002,0x8313,0xA002,0x8312,0x8310,0xA004,
+ 0xA002,0x830D,0x830B,0xA002,0x830A,0x8300,0xA008,0xA004,0xA002,0x82FF,0x82FE,0xA002,0x82FD,0x82FC,0xA003,0xA000,
+ 0x82FA,0xA002,0x82F8,0x82F6,0xA010,0xA008,0xA004,0xA002,0x82F5,0x82F3,0xA002,0x82F2,0x82F0,0xA004,0xA002,0x82EE,
+ 0x82ED,0xA002,0x82EC,0x82EA,0xA008,0xA004,0xA002,0x82E9,0x82E8,0xA002,0x82E7,0x82E2,0xA004,0xA002,0x82DD,0x82DA,
+ 0xA002,0x82D9,0x82D6,0xA020,0xA010,0xA008,0xA004,0xA002,0x82D0,0x82C9,0xA002,0x82C6,0x82C5,0xA004,0xA002,0x82C3,
+ 0x82C2,0xA002,0x82C0,0x82BF,0xA008,0xA004,0xA002,0x82BC,0x82BB,0xA002,0x82BA,0x82B6,0xA004,0xA002,0x82B5,0x82B2,
+ 0xA002,0x82A7,0x82A3,0xA010,0xA008,0xA004,0xA002,0x82A2,0x82A0,0xA002,0x829E,0x829B,0xA004,0xA002,0x829A,0x8296,
+ 0xA002,0x8295,0x8294,0xA008,0xA004,0xA002,0x8293,0x8290,0xA002,0x828C,0x8289,0xA004,0xA002,0x8287,0x8286,0xA002,
+ 0x8285,0x8283,0xA01D,0xA000,0xA000,0xA00E,0xA006,0xA002,0x8281,0xA002,0x8280,0x827C,0xA004,0xA002,0x827B,0x8278,
+ 0xA002,0x8277,0x8276,0xA008,0xA004,0xA002,0x8275,0x8271,0xA002,0x826D,0x826C,0xA000,0xA002,0x826B,0x826A,0xA026,
+ 0xA006,0xA001,0xA001,0xA001,0xA001,0x6BD7,0xA010,0xA008,0xA004,0xA002,0x7435,0x5288,0xA002,0x62AB,0x6279,0xA004,
+ 0xA002,0x9739,0x7812,0xA002,0x576F,0x78B0,0xA008,0xA004,0xA002,0x6367,0x9E4F,0xA002,0x670B,0x81A8,0xA004,0xA002,
+ 0x7BF7,0x787C,0xA002,0x68DA,0x84EC,0xA020,0xA010,0xA008,0xA004,0xA002,0x5F6D,0x6F8E,0xA002,0x70F9,0x62A8,0xA004,
+ 0xA002,0x7830,0x76C6,0xA002,0x55B7,0x6C9B,0xA008,0xA004,0xA002,0x4F69,0x914D,0xA002,0x966A,0x8D54,0xA004,0xA002,
+ 0x88F4,0x57F9,0xA002,0x80DA,0x5478,0xA010,0xA008,0xA004,0xA002,0x6CE1,0x8DD1,0xA002,0x888D,0x70AE,0xA004,0xA002,
+ 0x5228,0x5486,0xA002,0x629B,0x80D6,0xA008,0xA004,0xA002,0x802A,0x65C1,0xA002,0x5E9E,0x4E53,0xA004,0xA002,0x53DB,
+ 0x5224,0xA002,0x7554,0x76FC,0xA0FD,0xA07E,0xA03E,0xA01E,0xA00E,0xA006,0xA002,0x78D0,0xA002,0x76D8,0x6F58,0xA004,
+ 0xA002,0x6500,0x6D3E,0xA002,0x6E43,0x5F98,0xA008,0xA004,0xA002,0x724C,0x6392,0xA002,0x62CD,0x7436,0xA004,0xA002,
+ 0x6015,0x5E15,0xA002,0x722C,0x8DB4,0xA010,0xA008,0xA004,0xA002,0x556A,0x6CA4,0xA002,0x5076,0x5455,0xA004,0xA002,
+ 0x85D5,0x6BB4,0xA002,0x9E25,0x6B27,0xA008,0xA004,0xA002,0x54E6,0x8BFA,0xA002,0x7CEF,0x61E6,0xA004,0xA002,0x632A,
+ 0x759F,0xA002,0x8650,0x6696,0xA020,0xA010,0xA008,0xA004,0xA002,0x5973,0x6012,0xA002,0x52AA,0x5974,0xA004,0xA002,
+ 0x5F04,0x519C,0xA002,0x6D53,0x8113,0xA008,0xA004,0xA002,0x7EBD,0x94AE,0xA002,0x626D,0x725B,0xA004,0xA002,0x6CDE,
+ 0x62E7,0xA002,0x8269,0x8267,0xA010,0xA008,0xA004,0xA002,0x8266,0x8265,0xA002,0x8264,0x8263,0xA004,0xA002,0x8262,
+ 0x8261,0xA002,0x8260,0x825E,0xA008,0xA004,0xA002,0x825D,0x825C,0xA002,0x825B,0x8259,0xA004,0xA002,0x8257,0x8256,
+ 0xA002,0x8255,0x8254,0xA03F,0xA01F,0xA010,0xA008,0xA004,0xA002,0x8253,0x8252,0xA002,0x8251,0x8250,0xA004,0xA002,
+ 0x824E,0x824D,0xA002,0x824C,0x824A,0xA008,0xA004,0xA002,0x8248,0x8246,0xA002,0x8245,0x8243,0xA004,0xA002,0x8242,
+ 0x8241,0xA000,0x8240,0xA010,0xA008,0xA004,0xA002,0x823F,0x823D,0xA002,0x823C,0x823A,0xA004,0xA002,0x8232,0x822E,
+ 0xA002,0x8229,0x8227,0xA008,0xA004,0xA002,0x8226,0x8225,0xA002,0x8224,0x8220,0xA004,0xA002,0x821D,0x821A,0xA002,
+ 0x8219,0x8218,0xA020,0xA010,0xA008,0xA004,0xA002,0x8217,0x8216,0xA002,0x8215,0x8213,0xA004,0xA002,0x8211,0x820F,
+ 0xA002,0x820E,0x820B,0xA008,0xA004,0xA002,0x820A,0x8209,0xA002,0x8208,0x8207,0xA004,0xA002,0x8203,0x81FF,0xA002,
+ 0x81FD,0x81FA,0xA010,0xA008,0xA004,0xA002,0x81F9,0x81F8,0xA002,0x81F7,0x81F6,0xA004,0xA002,0x81F5,0x81F2,0xA002,
+ 0x81F1,0x81F0,0xA008,0xA004,0xA002,0x81EF,0x81EE,0xA002,0x81EB,0x81E9,0xA004,0xA002,0x81E8,0x81E6,0xA002,0x81E5,
+ 0x81E4,0xA020,0xA000,0xA000,0xA00E,0xA006,0xA002,0x81E2,0xA002,0x81E1,0x81E0,0xA004,0xA002,0x81DF,0x81DE,0xA002,
+ 0x81DD,0x81DC,0xA008,0xA004,0xA002,0x81DB,0x81DA,0xA002,0x81D9,0x81D8,0xA004,0xA002,0x81D7,0x81D6,0xA002,0x81D5,
+ 0x81D4,0xA020,0xA001,0xA00F,0xA007,0xA003,0xA001,0x5B81,0xA002,0x51DD,0x72DE,0xA004,0xA002,0x67E0,0x60A8,0xA002,
+ 0x6D85,0x954D,0xA008,0xA004,0xA002,0x954A,0x556E,0xA002,0x5B7D,0x8042,0xA004,0xA002,0x634F,0x5C3F,0xA002,0x9E1F,
+ 0x917F,0xA020,0xA010,0xA008,0xA004,0xA002,0x5A18,0x5FF5,0xA002,0x637B,0x64B5,0xA004,0xA002,0x78BE,0x5E74,0xA002,
+ 0x62C8,0x852B,0xA008,0xA004,0xA002,0x6EBA,0x9006,0xA002,0x817B,0x533F,0xA004,0xA002,0x4F60,0x62DF,0xA002,0x5C3C,
+ 0x6CE5,0xA010,0xA008,0xA004,0xA002,0x502A,0x9713,0xA002,0x59AE,0x80FD,0xA004,0xA002,0x5AE9,0x5185,0xA002,0x9981,
+ 0x5462,0xA008,0xA004,0xA002,0x6DD6,0x95F9,0xA002,0x607C,0x8111,0xA004,0xA002,0x6320,0x56CA,0xA002,0x96BE,0x7537,
+ 0xA180,0xA0FD,0xA07E,0xA03E,0xA01E,0xA00E,0xA006,0xA002,0x5357,0xA002,0x5948,0x8010,0xA004,0xA002,0x5976,0x4E43,
+ 0xA002,0x6C16,0x7EB3,0xA008,0xA004,0xA002,0x5A1C,0x90A3,0xA002,0x94A0,0x5450,0xA004,0xA002,0x54EA,0x62FF,0xA002,
+ 0x7A46,0x7267,0xA010,0xA008,0xA004,0xA002,0x7766,0x76EE,0xA002,0x6728,0x6155,0xA004,0xA002,0x52DF,0x5E55,0xA002,
+ 0x66AE,0x5893,0xA008,0xA004,0xA002,0x6BCD,0x59C6,0xA002,0x4EA9,0x7261,0xA004,0xA002,0x62C7,0x67D0,0xA002,0x725F,
+ 0x8C0B,0xA020,0xA010,0xA008,0xA004,0xA002,0x964C,0x5BDE,0xA002,0x6F20,0x6CAB,0xA004,0xA002,0x9ED8,0x58A8,0xA002,
+ 0x83AB,0x672B,0xA008,0xA004,0xA002,0x62B9,0x9B54,0xA002,0x6469,0x78E8,0xA004,0xA002,0x819C,0x6A21,0xA002,0x8611,
+ 0x6479,0xA010,0xA008,0xA004,0xA002,0x81D3,0x81D2,0xA002,0x81D1,0x81D0,0xA004,0xA002,0x81CF,0x81CE,0xA002,0x81CD,
+ 0x81CB,0xA008,0xA004,0xA002,0x81C9,0x81C8,0xA002,0x81C7,0x81C5,0xA004,0xA002,0x81C4,0x81BF,0xA002,0x81BE,0x81BD,
+ 0xA03F,0xA020,0xA010,0xA008,0xA004,0xA002,0x81BC,0x81B9,0xA002,0x81B8,0x81B7,0xA004,0xA002,0x81B6,0x81B5,0xA002,
+ 0x81B4,0x81B2,0xA008,0xA004,0xA002,0x81B1,0x81B0,0xA002,0x81AF,0x81AE,0xA004,0xA002,0x81AD,0x81AC,0xA002,0x81AB,
+ 0x81A9,0xA00F,0xA007,0xA003,0xA000,0x81A7,0xA002,0x81A5,0x81A4,0xA004,0xA002,0x81A2,0x81A1,0xA002,0x81A0,0x819F,
+ 0xA008,0xA004,0xA002,0x819E,0x819A,0xA002,0x8199,0x8197,0xA004,0xA002,0x8196,0x8195,0xA002,0x8194,0x8193,0xA020,
+ 0xA010,0xA008,0xA004,0xA002,0x8192,0x8190,0xA002,0x818E,0x818D,0xA004,0xA002,0x818C,0x818B,0xA002,0x8189,0x8187,
+ 0xA008,0xA004,0xA002,0x8186,0x8185,0xA002,0x8184,0x8183,0xA004,0xA002,0x8181,0x8178,0xA002,0x8177,0x8176,0xA010,
+ 0xA008,0xA004,0xA002,0x8175,0x8173,0xA002,0x8172,0x816F,0xA004,0xA002,0x816C,0x816B,0xA002,0x816A,0x8168,0xA008,
+ 0xA004,0xA002,0x8166,0x8164,0xA002,0x8163,0x8162,0xA004,0xA002,0x8161,0x815F,0xA002,0x815E,0x815D,0xA026,0xA000,
+ 0xA01E,0xA00E,0xA006,0xA002,0x815C,0xA002,0x815B,0x8158,0xA004,0xA002,0x8157,0x8156,0xA002,0x8152,0x814F,0xA008,
+ 0xA004,0xA002,0x814E,0x814D,0xA002,0x8149,0x8147,0xA004,0xA002,0x8145,0x8144,0xA002,0x8143,0x8142,0xA000,0xA000,
+ 0xA000,0xA002,0x8141,0x8140,0xA01D,0xA001,0xA00C,0xA004,0xA001,0xA001,0x6478,0xA004,0xA002,0x8C2C,0x547D,0xA002,
+ 0x540D,0x94ED,0xA008,0xA004,0xA002,0x9E23,0x879F,0xA002,0x660E,0x95FD,0xA004,0xA002,0x60AF,0x654F,0xA002,0x76BF,
+ 0x62BF,0xA020,0xA010,0xA008,0xA004,0xA002,0x6C11,0x706D,0xA002,0x8511,0x5999,0xA004,0xA002,0x5E99,0x6E3A,0xA002,
+ 0x79D2,0x85D0,0xA008,0xA004,0xA002,0x7784,0x63CF,0xA002,0x82D7,0x9762,0xA004,0xA002,0x7F05,0x5A29,0xA002,0x52C9,
+ 0x514D,0xA010,0xA008,0xA004,0xA002,0x5195,0x7EF5,0xA002,0x7720,0x68C9,0xA004,0xA002,0x5E42,0x5BC6,0xA002,0x871C,
+ 0x6CCC,0xA008,0xA004,0xA002,0x89C5,0x79D8,0xA002,0x7C73,0x5F25,0xA004,0xA002,0x8C1C,0x8FF7,0xA002,0x7CDC,0x9761,
+ 0xA0FD,0xA07E,0xA03E,0xA01E,0xA00E,0xA006,0xA002,0x919A,0xA002,0x772F,0x5B5F,0xA004,0xA002,0x68A6,0x731B,0xA002,
+ 0x9530,0x76DF,0xA008,0xA004,0xA002,0x6AAC,0x8499,0xA002,0x840C,0x4EEC,0xA004,0xA002,0x95F7,0x95E8,0xA002,0x5A9A,
+ 0x59B9,0xA010,0xA008,0xA004,0xA002,0x5BD0,0x6627,0xA002,0x7F8E,0x6BCF,0xA004,0xA002,0x9541,0x5A92,0xA002,0x7709,
+ 0x6CA1,0xA008,0xA004,0xA002,0x7164,0x9709,0xA002,0x9176,0x6885,0xA004,0xA002,0x679A,0x73AB,0xA002,0x4E48,0x8D38,
+ 0xA020,0xA010,0xA008,0xA004,0xA002,0x8C8C,0x5E3D,0xA002,0x5192,0x8302,0xA004,0xA002,0x536F,0x94C6,0xA002,0x77DB,
+ 0x6BDB,0xA008,0xA004,0xA002,0x951A,0x8305,0xA002,0x732B,0x83BD,0xA004,0xA002,0x5FD9,0x6C13,0xA002,0x76F2,0x832B,
+ 0xA010,0xA008,0xA004,0xA002,0x8292,0x8C29,0xA002,0x813F,0x813D,0xA004,0xA002,0x813C,0x813B,0xA002,0x813A,0x8139,
+ 0xA008,0xA004,0xA002,0x8137,0x8135,0xA002,0x8134,0x8133,0xA004,0xA002,0x8130,0x812E,0xA002,0x812D,0x812B,0xA03F,
+ 0xA020,0xA010,0xA008,0xA004,0xA002,0x812A,0x8129,0xA002,0x8128,0x8127,0xA004,0xA002,0x8126,0x8125,0xA002,0x8124,
+ 0x8123,0xA008,0xA004,0xA002,0x8122,0x8121,0xA002,0x8120,0x811F,0xA004,0xA002,0x811D,0x811C,0xA002,0x811B,0x8119,
+ 0xA00F,0xA007,0xA004,0xA002,0x8117,0x8115,0xA000,0x810C,0xA004,0xA002,0x810B,0x8108,0xA002,0x8107,0x8105,0xA008,
+ 0xA004,0xA002,0x8104,0x8103,0xA002,0x8101,0x8100,0xA004,0xA002,0x80FF,0x80FE,0xA002,0x80FB,0x80F9,0xA020,0xA010,
+ 0xA008,0xA004,0xA002,0x80F7,0x80F5,0xA002,0x80EE,0x80E6,0xA004,0xA002,0x80E3,0x80E2,0xA002,0x80E0,0x80DF,0xA008,
+ 0xA004,0xA002,0x80D8,0x80D5,0xA002,0x80D4,0x80D3,0xA004,0xA002,0x80D2,0x80D1,0xA002,0x80D0,0x80CF,0xA010,0xA008,
+ 0xA004,0xA002,0x80CB,0x80CA,0xA002,0x80C9,0x80C8,0xA004,0xA002,0x80C7,0x80C5,0xA002,0x80BB,0x80B9,0xA008,0xA004,
+ 0xA002,0x80B8,0x80B6,0xA002,0x80B5,0x80B3,0xA004,0xA002,0x80B0,0x80AC,0xA002,0x80A8,0x80A7,0xA029,0xA000,0xA01E,
+ 0xA00E,0xA006,0xA002,0x80A6,0xA002,0x80A3,0x809E,0xA004,0xA002,0x8099,0x8097,0xA002,0x8095,0x8094,0xA008,0xA004,
+ 0xA002,0x8092,0x8091,0xA002,0x8090,0x808F,0xA004,0xA002,0x808E,0x808D,0xA002,0x808A,0x8088,0xA000,0xA000,0xA004,
+ 0xA002,0x8085,0x8082,0xA002,0x8081,0x807E,0xA019,0xA001,0xA008,0xA001,0xA003,0xA001,0x6F2B,0xA002,0x6162,0x66FC,
+ 0xA008,0xA004,0xA002,0x8513,0x6EE1,0xA002,0x86EE,0x9992,0xA004,0xA002,0x7792,0x8109,0xA002,0x8FC8,0x5356,0xA020,
+ 0xA010,0xA008,0xA004,0xA002,0x9EA6,0x4E70,0xA002,0x57CB,0x5417,0xA004,0xA002,0x561B,0x9A82,0xA002,0x9A6C,0x8682,
+ 0xA008,0xA004,0xA002,0x7801,0x739B,0xA002,0x9EBB,0x5988,0xA004,0xA002,0x7EDC,0x9A86,0xA002,0x6D1B,0x843D,0xA010,
+ 0xA008,0xA004,0xA002,0x88F8,0x9AA1,0xA002,0x7BA9,0x9523,0xA004,0xA002,0x903B,0x7F57,0xA002,0x87BA,0x841D,0xA008,
+ 0xA004,0xA002,0x8BBA,0x7EB6,0xA002,0x6CA6,0x4ED1,0xA004,0xA002,0x4F26,0x8F6E,0xA002,0x62A1,0x7565,0xA2FE,0xA180,
+ 0xA0FD,0xA07E,0xA03E,0xA01E,0xA00E,0xA006,0xA002,0x63A0,0xA002,0x4E71,0x5375,0xA004,0xA002,0x6EE6,0x5B6A,0xA002,
+ 0x631B,0x5CE6,0xA008,0xA004,0xA002,0x7EFF,0x6EE4,0xA002,0x7387,0x5F8B,0xA004,0xA002,0x6C2F,0x8651,0xA002,0x7F15,
+ 0x5C61,0xA010,0xA008,0xA004,0xA002,0x5C65,0x65C5,0xA002,0x4FA3,0x94DD,0xA004,0xA002,0x5415,0x9A74,0xA002,0x622E,
+ 0x9646,0xA008,0xA004,0xA002,0x5F55,0x7984,0xA002,0x6F5E,0x9E7F,0xA004,0xA002,0x8D42,0x8DEF,0xA002,0x9732,0x788C,
+ 0xA020,0xA010,0xA008,0xA004,0xA002,0x9E93,0x9C81,0xA002,0x864F,0x5364,0xA004,0xA002,0x63B3,0x7089,0xA002,0x5E90,
+ 0x9885,0xA008,0xA004,0xA002,0x5362,0x82A6,0xA002,0x964B,0x6F0F,0xA004,0xA002,0x7BD3,0x6402,0xA002,0x5A04,0x697C,
+ 0xA010,0xA008,0xA004,0xA002,0x9647,0x62E2,0xA002,0x5784,0x9686,0xA004,0xA002,0x807D,0x807C,0xA002,0x807B,0x807A,
+ 0xA008,0xA004,0xA002,0x8079,0x8078,0xA002,0x8077,0x8076,0xA004,0xA002,0x8075,0x8074,0xA002,0x8073,0x8072,0xA03F,
+ 0xA020,0xA010,0xA008,0xA004,0xA002,0x8070,0x806F,0xA002,0x806E,0x806D,0xA004,0xA002,0x806C,0x806B,0xA002,0x8068,
+ 0x8067,0xA008,0xA004,0xA002,0x8066,0x8065,0xA002,0x8064,0x8063,0xA004,0xA002,0x8062,0x8061,0xA002,0x8060,0x805F,
+ 0xA00F,0xA008,0xA004,0xA002,0x805E,0x805D,0xA002,0x805C,0x805B,0xA003,0xA000,0x8059,0xA002,0x8057,0x8056,0xA008,
+ 0xA004,0xA002,0x8055,0x8053,0xA002,0x8051,0x8050,0xA004,0xA002,0x804F,0x804E,0xA002,0x8049,0x8048,0xA020,0xA010,
+ 0xA008,0xA004,0xA002,0x8047,0x8045,0xA002,0x8044,0x8041,0xA004,0xA002,0x8040,0x803E,0xA002,0x803C,0x803A,0xA008,
+ 0xA004,0xA002,0x8039,0x8034,0xA002,0x8032,0x8030,0xA004,0xA002,0x802F,0x802E,0xA002,0x802D,0x802C,0xA010,0xA008,
+ 0xA004,0xA002,0x802B,0x8024,0xA002,0x8023,0x8021,0xA004,0xA002,0x801F,0x801E,0xA002,0x801D,0x801B,0xA008,0xA004,
+ 0xA002,0x801A,0x8013,0xA002,0x8011,0x800F,0xA004,0xA002,0x800E,0x800A,0xA002,0x8009,0x8008,0xA02D,0xA000,0xA01E,
+ 0xA00E,0xA006,0xA002,0x8007,0xA002,0x8002,0x7FFF,0xA004,0xA002,0x7FFE,0x7FFD,0xA002,0x7FFA,0x7FF9,0xA008,0xA004,
+ 0xA002,0x7FF8,0x7FF7,0xA002,0x7FF6,0x7FF5,0xA004,0xA002,0x7FF4,0x7FF2,0xA002,0x7FEF,0x7FED,0xA000,0xA008,0xA004,
+ 0xA002,0x7FEC,0x7FEB,0xA002,0x7FEA,0x7FE8,0xA000,0xA002,0x7FE7,0x7FE4,0xA016,0xA001,0xA005,0xA001,0xA001,0xA001,
+ 0x7ABF,0xA008,0xA004,0xA002,0x7B3C,0x5499,0xA002,0x804B,0x9F99,0xA004,0xA002,0x516D,0x67F3,0xA002,0x6D41,0x7624,
+ 0xA020,0xA010,0xA008,0xA004,0xA002,0x5218,0x7559,0xA002,0x998F,0x786B,0xA004,0xA002,0x69B4,0x7409,0xA002,0x6E9C,
+ 0x4EE4,0xA008,0xA004,0xA002,0x53E6,0x9886,0xA002,0x5CAD,0x9675,0xA004,0xA002,0x7075,0x51CC,0xA002,0x7F9A,0x4F36,
+ 0xA010,0xA008,0xA004,0xA002,0x94C3,0x9F84,0xA002,0x96F6,0x83F1,0xA004,0xA002,0x73B2,0x62CE,0xA002,0x541D,0x8D41,
+ 0xA008,0xA004,0xA002,0x51DB,0x6DCB,0xA002,0x9CDE,0x90BB,0xA004,0xA002,0x4E34,0x9716,0xA002,0x78F7,0x6797,0xA0FD,
+ 0xA07E,0xA03E,0xA01E,0xA00E,0xA006,0xA002,0x7433,0xA002,0x730E,0x52A3,0xA004,0xA002,0x70C8,0x88C2,0xA002,0x5217,
+ 0x6599,0xA008,0xA004,0xA002,0x5ED6,0x9563,0xA002,0x6482,0x4E86,0xA004,0xA002,0x6F66,0x8FBD,0xA002,0x5BE5,0x71CE,
+ 0xA010,0xA008,0xA004,0xA002,0x7597,0x50DA,0xA002,0x804A,0x64A9,0xA004,0xA002,0x8C05,0x4EAE,0xA002,0x667E,0x91CF,
+ 0xA008,0xA004,0xA002,0x8F86,0x4E24,0xA002,0x826F,0x7CB1,0xA004,0xA002,0x6881,0x51C9,0xA002,0x7CAE,0x7EC3,0xA020,
+ 0xA010,0xA008,0xA004,0xA002,0x70BC,0x604B,0xA002,0x94FE,0x8138,0xA004,0xA002,0x655B,0x5E18,0xA002,0x6D9F,0x601C,
+ 0xA008,0xA004,0xA002,0x5EC9,0x9570,0xA002,0x8FDE,0x83B2,0xA004,0xA002,0x8054,0x4FE9,0xA002,0x54E9,0x7483,0xA010,
+ 0xA008,0xA004,0xA002,0x529B,0x96B6,0xA002,0x6CA5,0x7C92,0xA004,0xA002,0x7ACB,0x75E2,0xA002,0x7FE3,0x7FE2,0xA008,
+ 0xA004,0xA002,0x7FDE,0x7FDD,0xA002,0x7FDC,0x7FDB,0xA004,0xA002,0x7FDA,0x7FD9,0xA002,0x7FD7,0x7FD6,0xA03F,0xA020,
+ 0xA010,0xA008,0xA004,0xA002,0x7FD3,0x7FD2,0xA002,0x7FD1,0x7FD0,0xA004,0xA002,0x7FCF,0x7FCD,0xA002,0x7FCB,0x7FC9,
+ 0xA008,0xA004,0xA002,0x7FC8,0x7FC7,0xA002,0x7FC6,0x7FC4,0xA004,0xA002,0x7FC3,0x7FC2,0xA002,0x7FC0,0x7FBE,0xA00F,
+ 0xA008,0xA004,0xA002,0x7FBB,0x7FBA,0xA002,0x7FB7,0x7FB6,0xA004,0xA002,0x7FB5,0x7FB4,0xA000,0x7FB3,0xA008,0xA004,
+ 0xA002,0x7FB1,0x7FAE,0xA002,0x7FAD,0x7FAC,0xA004,0xA002,0x7FAB,0x7FAA,0xA002,0x7FA9,0x7FA8,0xA020,0xA010,0xA008,
+ 0xA004,0xA002,0x7FA6,0x7FA5,0xA002,0x7FA3,0x7FA2,0xA004,0xA002,0x7FA0,0x7F9C,0xA002,0x7F9B,0x7F99,0xA008,0xA004,
+ 0xA002,0x7F98,0x7F97,0xA002,0x7F96,0x7F95,0xA004,0xA002,0x7F93,0x7F92,0xA002,0x7F91,0x7F90,0xA010,0xA008,0xA004,
+ 0xA002,0x7F8F,0x7F8D,0xA002,0x7F8B,0x7F89,0xA004,0xA002,0x7F88,0x7F87,0xA002,0x7F86,0x7F85,0xA008,0xA004,0xA002,
+ 0x7F84,0x7F83,0xA002,0x7F82,0x7F80,0xA004,0xA002,0x7F7F,0x7F7D,0xA002,0x7F7C,0x7F7B,0xA030,0xA000,0xA01E,0xA00E,
+ 0xA006,0xA002,0x7F7A,0xA002,0x7F78,0x7F77,0xA004,0xA002,0x7F76,0x7F75,0xA002,0x7F73,0x7F70,0xA008,0xA004,0xA002,
+ 0x7F6F,0x7F6D,0xA002,0x7F6C,0x7F6B,0xA004,0xA002,0x7F67,0x7F66,0xA002,0x7F65,0x7F64,0xA000,0xA008,0xA004,0xA002,
+ 0x7F63,0x7F60,0xA002,0x7F5E,0x7F5D,0xA004,0xA002,0x7F5C,0x7F5B,0xA002,0x7F59,0x7F56,0xA011,0xA001,0xA001,0xA007,
+ 0xA003,0xA001,0x4FD0,0xA002,0x4F8B,0x5088,0xA004,0xA002,0x5229,0x5386,0xA002,0x783E,0x52B1,0xA020,0xA010,0xA008,
+ 0xA004,0xA002,0x5389,0x4E3D,0xA002,0x6817,0x540F,0xA004,0xA002,0x8354,0x8389,0xA002,0x793C,0x9CA4,0xA008,0xA004,
+ 0xA002,0x91CC,0x674E,0xA002,0x7406,0x6F13,0xA004,0xA002,0x79BB,0x72F8,0xA002,0x7BF1,0x9ECE,0xA010,0xA008,0xA004,
+ 0xA002,0x7281,0x68A8,0xA002,0x5398,0x51B7,0xA004,0xA002,0x695E,0x68F1,0xA002,0x6CEA,0x7C7B,0xA008,0xA004,0xA002,
+ 0x808B,0x64C2,0xA002,0x5792,0x5121,0xA004,0xA002,0x7D2F,0x78CA,0xA002,0x857E,0x956D,0xA180,0xA0FD,0xA07E,0xA03E,
+ 0xA01E,0xA00E,0xA006,0xA002,0x96F7,0xA002,0x4E50,0x52D2,0xA004,0xA002,0x6D9D,0x70D9,0xA002,0x916A,0x59E5,0xA008,
+ 0xA004,0xA002,0x4F6C,0x8001,0xA002,0x7262,0x52B3,0xA004,0xA002,0x635E,0x6D6A,0xA002,0x6717,0x90CE,0xA010,0xA008,
+ 0xA004,0xA002,0x5ECA,0x72FC,0xA002,0x6994,0x7405,0xA004,0xA002,0x6EE5,0x70C2,0xA002,0x7F06,0x61D2,0xA008,0xA004,
+ 0xA002,0x89C8,0x63FD,0xA002,0x8C30,0x6F9C,0xA004,0xA002,0x5170,0x9611,0xA002,0x7BEE,0x62E6,0xA020,0xA010,0xA008,
+ 0xA004,0xA002,0x680F,0x5A6A,0xA002,0x84DD,0x8D56,0xA004,0xA002,0x6765,0x83B1,0xA002,0x5566,0x8FA3,0xA008,0xA004,
+ 0xA002,0x814A,0x8721,0xA002,0x5587,0x62C9,0xA004,0xA002,0x5783,0x9614,0xA002,0x5ED3,0x6269,0xA010,0xA008,0xA004,
+ 0xA002,0x62EC,0x56F0,0xA002,0x6346,0x6606,0xA004,0xA002,0x5764,0x6E83,0xA002,0x6127,0x9988,0xA008,0xA004,0xA002,
+ 0x7F53,0x7F52,0xA002,0x7F4F,0x7F4E,0xA004,0xA002,0x7F4D,0x7F4C,0xA002,0x7F4B,0x7F4A,0xA03F,0xA020,0xA010,0xA008,
+ 0xA004,0xA002,0x7F49,0x7F48,0xA002,0x7F47,0x7F46,0xA004,0xA002,0x7F43,0x7F41,0xA002,0x7F40,0x7F3F,0xA008,0xA004,
+ 0xA002,0x7F3E,0x7F3D,0xA002,0x7F3C,0x7F3B,0xA004,0xA002,0x7F39,0x7F37,0xA002,0x7F1E,0x7F10,0xA010,0xA008,0xA004,
+ 0xA002,0x7F0A,0x7EF9,0xA002,0x7EEC,0x7EE4,0xA004,0xA002,0x7ED6,0x7EBC,0xA002,0x7EBB,0x7EB4,0xA007,0xA003,0xA000,
+ 0x7EAE,0xA002,0x7E9E,0x7E9D,0xA004,0xA002,0x7E9C,0x7E9A,0xA002,0x7E99,0x7E98,0xA020,0xA010,0xA008,0xA004,0xA002,
+ 0x7E97,0x7E96,0xA002,0x7E95,0x7E94,0xA004,0xA002,0x7E93,0x7E92,0xA002,0x7E91,0x7E90,0xA008,0xA004,0xA002,0x7E8F,
+ 0x7E8E,0xA002,0x7E8D,0x7E8C,0xA004,0xA002,0x7E8B,0x7E8A,0xA002,0x7E89,0x7E88,0xA010,0xA008,0xA004,0xA002,0x7E87,
+ 0x7E86,0xA002,0x7E85,0x7E84,0xA004,0xA002,0x7E83,0x7E81,0xA002,0x7E80,0x7E7F,0xA008,0xA004,0xA002,0x7E7E,0x7E7D,
+ 0xA002,0x7E7C,0x7E7B,0xA004,0xA002,0x7E7A,0x7E79,0xA002,0x7E78,0x7E77,0xA035,0xA000,0xA01E,0xA00E,0xA006,0xA002,
+ 0x7E76,0xA002,0x7E75,0x7E74,0xA004,0xA002,0x7E73,0x7E72,0xA002,0x7E71,0x7E70,0xA008,0xA004,0xA002,0x7E6F,0x7E6E,
+ 0xA002,0x7E6D,0x7E6C,0xA004,0xA002,0x7E6B,0x7E6A,0xA002,0x7E69,0x7E68,0xA010,0xA008,0xA004,0xA002,0x7E67,0x7E66,
+ 0xA002,0x7E65,0x7E64,0xA004,0xA002,0x7E63,0x7E62,0xA002,0x7E61,0x7E60,0xA000,0xA000,0xA002,0x7E5F,0x7E5E,0xA00E,
+ 0xA001,0xA001,0xA004,0xA001,0xA001,0x5080,0xA004,0xA002,0x9B41,0x594E,0xA002,0x8475,0x7AA5,0xA020,0xA010,0xA008,
+ 0xA004,0xA002,0x5CBF,0x76D4,0xA002,0x4E8F,0x51B5,0xA004,0xA002,0x65F7,0x7736,0xA002,0x77FF,0x6846,0xA008,0xA004,
+ 0xA002,0x72C2,0x7B50,0xA002,0x5321,0x6B3E,0xA004,0xA002,0x5BBD,0x5FEB,0xA002,0x4FA9,0x7B77,0xA010,0xA008,0xA004,
+ 0xA002,0x5757,0x80EF,0xA002,0x8DE8,0x630E,0xA004,0xA002,0x57AE,0x5938,0xA002,0x88E4,0x5E93,0xA008,0xA004,0xA002,
+ 0x9177,0x82E6,0xA002,0x7A9F,0x54ED,0xA004,0xA002,0x67AF,0x5BC7,0xA002,0x6263,0x53E3,0xA0FD,0xA07E,0xA03E,0xA01E,
+ 0xA00E,0xA006,0xA002,0x62A0,0xA002,0x63A7,0x5B54,0xA004,0xA002,0x6050,0x7A7A,0xA002,0x542D,0x5751,0xA008,0xA004,
+ 0xA002,0x6073,0x57A6,0xA002,0x5543,0x80AF,0xA004,0xA002,0x8BFE,0x5BA2,0xA002,0x523B,0x514B,0xA010,0xA008,0xA004,
+ 0xA002,0x6E34,0x53EF,0xA002,0x54B3,0x58F3,0xA004,0xA002,0x79D1,0x9897,0xA002,0x78D5,0x68F5,0xA008,0xA004,0xA002,
+ 0x67EF,0x82DB,0xA002,0x5777,0x9760,0xA004,0xA002,0x70E4,0x62F7,0xA002,0x8003,0x7095,0xA020,0xA010,0xA008,0xA004,
+ 0xA002,0x4EA2,0x6297,0xA002,0x625B,0x7CE0,0xA004,0xA002,0x6177,0x5EB7,0xA002,0x770B,0x780D,0xA008,0xA004,0xA002,
+ 0x574E,0x52D8,0xA002,0x582A,0x520A,0xA004,0xA002,0x6168,0x51EF,0xA002,0x6977,0x63E9,0xA010,0xA008,0xA004,0xA002,
+ 0x5F00,0x54AF,0xA002,0x5361,0x5496,0xA004,0xA002,0x5580,0x9A8F,0xA002,0x90E1,0x6D5A,0xA008,0xA004,0xA002,0x7AE3,
+ 0x4FCA,0xA002,0x7E5D,0x7E5C,0xA004,0xA002,0x7E5B,0x7E5A,0xA002,0x7E59,0x7E58,0xA03F,0xA020,0xA010,0xA008,0xA004,
+ 0xA002,0x7E57,0x7E56,0xA002,0x7E55,0x7E54,0xA004,0xA002,0x7E53,0x7E52,0xA002,0x7E51,0x7E50,0xA008,0xA004,0xA002,
+ 0x7E4F,0x7E4E,0xA002,0x7E4D,0x7E4C,0xA004,0xA002,0x7E4B,0x7E4A,0xA002,0x7E49,0x7E48,0xA010,0xA008,0xA004,0xA002,
+ 0x7E46,0x7E45,0xA002,0x7E44,0x7E43,0xA004,0xA002,0x7E42,0x7E40,0xA002,0x7E3F,0x7E3E,0xA007,0xA004,0xA002,0x7E3D,
+ 0x7E3C,0xA000,0x7E3A,0xA004,0xA002,0x7E39,0x7E38,0xA002,0x7E37,0x7E36,0xA020,0xA010,0xA008,0xA004,0xA002,0x7E35,
+ 0x7E34,0xA002,0x7E33,0x7E32,0xA004,0xA002,0x7E31,0x7E30,0xA002,0x7E2F,0x7E2E,0xA008,0xA004,0xA002,0x7E2D,0x7E2C,
+ 0xA002,0x7E2B,0x7E2A,0xA004,0xA002,0x7E29,0x7E28,0xA002,0x7E27,0x7E26,0xA010,0xA008,0xA004,0xA002,0x7E25,0x7E24,
+ 0xA002,0x7E23,0x7E22,0xA004,0xA002,0x7E21,0x7E20,0xA002,0x7E1F,0x7E1E,0xA008,0xA004,0xA002,0x7E1D,0x7E1C,0xA002,
+ 0x7E1B,0x7E1A,0xA004,0xA002,0x7E19,0x7E18,0xA002,0x7E17,0x7E16,0xA038,0xA000,0xA01E,0xA00E,0xA006,0xA002,0x7E15,
+ 0xA002,0x7E14,0x7E13,0xA004,0xA002,0x7E12,0x7E11,0xA002,0x7E10,0x7E0F,0xA008,0xA004,0xA002,0x7E0E,0x7E0D,0xA002,
+ 0x7E0C,0x7E0B,0xA004,0xA002,0x7E0A,0x7E09,0xA002,0x7E08,0x7E07,0xA010,0xA008,0xA004,0xA002,0x7E06,0x7E05,0xA002,
+ 0x7E04,0x7E03,0xA004,0xA002,0x7E02,0x7E01,0xA002,0x7E00,0x7DFF,0xA000,0xA004,0xA002,0x7DFE,0x7DFD,0xA002,0x7DFC,
+ 0x7DFB,0xA00A,0xA001,0xA001,0xA001,0xA003,0xA001,0x5CFB,0xA002,0x541B,0x519B,0xA020,0xA010,0xA008,0xA004,0xA002,
+ 0x94A7,0x83CC,0xA002,0x5747,0x7EDD,0xA004,0xA002,0x8BC0,0x51B3,0xA002,0x89C9,0x7235,0xA008,0xA004,0xA002,0x5014,
+ 0x6398,0xA002,0x6289,0x652B,0xA004,0xA002,0x6485,0x7EE2,0xA002,0x5377,0x7737,0xA010,0xA008,0xA004,0xA002,0x5026,
+ 0x5A1F,0xA002,0x9E43,0x6350,0xA004,0xA002,0x5267,0x70AC,0xA002,0x60E7,0x53E5,0xA008,0xA004,0xA002,0x4FF1,0x952F,
+ 0xA002,0x8E1E,0x8DDD,0xA004,0xA002,0x5177,0x5DE8,0xA002,0x636E,0x62D2,0xC53E,0xB72E,0xABF4,0xA5F9,0xA2FA,0xA17A,
+ 0xA0FB,0xA07E,0xA03E,0xA01E,0xA00E,0xA006,0xA002,0x805A,0xA002,0x6CAE,0x4E3E,0xA004,0xA002,0x77E9,0x5480,0xA002,
+ 0x5C40,0x83CA,0xA008,0xA004,0xA002,0x9A79,0x5C45,0xA002,0x75BD,0x72D9,0xA004,0xA002,0x62D8,0x97A0,0xA002,0x759A,
+ 0x5C31,0xA010,0xA008,0xA004,0xA002,0x548E,0x8205,0xA002,0x81FC,0x65E7,0xA004,0xA002,0x6551,0x53A9,0xA002,0x9152,
+ 0x4E5D,0xA008,0xA004,0xA002,0x7078,0x4E45,0xA002,0x97ED,0x7396,0xA004,0xA002,0x7EA0,0x7A76,0xA002,0x63EA,0x7A98,
+ 0xA020,0xA010,0xA008,0xA004,0xA002,0x70AF,0x51C0,0xA002,0x7ADE,0x7ADF,0xA004,0xA002,0x9756,0x75C9,0xA002,0x5F84,
+ 0x955C,0xA008,0xA004,0xA002,0x656C,0x5883,0xA002,0x9759,0x9888,0xA004,0xA002,0x666F,0x8B66,0xA002,0x4E95,0x7ECF,
+ 0xA010,0xA008,0xA004,0xA002,0x7CB3,0x7CBE,0xA002,0x60CA,0x4EAC,0xA004,0xA002,0x9CB8,0x6676,0xA002,0x775B,0x830E,
+ 0xA008,0xA004,0xA002,0x5162,0x8346,0xA002,0x52B2,0x5C3D,0xA004,0xA002,0x7DFA,0x7DF9,0xA002,0x7DF8,0x7DF7,0xA03D,
+ 0xA01E,0xA00E,0xA006,0xA002,0x7DF6,0xA002,0x7DF5,0x7DF4,0xA004,0xA002,0x7DF3,0x7DF2,0xA002,0x7DF1,0x7DF0,0xA008,
+ 0xA004,0xA002,0x7DEF,0x7DEE,0xA002,0x7DED,0x7DEC,0xA004,0xA002,0x7DEB,0x7DEA,0xA002,0x7DE9,0x7DE8,0xA010,0xA008,
+ 0xA004,0xA002,0x7DE7,0x7DE6,0xA002,0x7DE5,0x7DE4,0xA004,0xA002,0x7DE3,0x7DE2,0xA002,0x7DE1,0x7DE0,0xA008,0xA004,
+ 0xA002,0x7DDF,0x7DDE,0xA002,0x7DDD,0x7DDC,0xA004,0xA002,0x7DDB,0x7DDA,0xA001,0x7DD9,0xA020,0xA010,0xA008,0xA004,
+ 0xA002,0x7DD8,0x7DD7,0xA002,0x7DD6,0x7DD5,0xA004,0xA002,0x7DD4,0x7DD3,0xA002,0x7DD2,0x7DD1,0xA008,0xA004,0xA002,
+ 0x7DD0,0x7DCF,0xA002,0x7DCE,0x7DCD,0xA004,0xA002,0x7DCC,0x7DCB,0xA002,0x7DCA,0x7DC9,0xA010,0xA008,0xA004,0xA002,
+ 0x7DC8,0x7DC7,0xA002,0x7DC6,0x7DC5,0xA004,0xA002,0x7DC4,0x7DC3,0xA002,0x7DC2,0x7DC1,0xA008,0xA004,0xA002,0x7DC0,
+ 0x7DBF,0xA002,0x7DBE,0x7DBD,0xA004,0xA002,0x7DBC,0x7DBB,0xA002,0x7DBA,0x7DB9,0xA03E,0xA000,0xA01E,0xA00E,0xA006,
+ 0xA002,0x7DB8,0xA002,0x7DB7,0x7DB6,0xA004,0xA002,0x7DB5,0x7DB4,0xA002,0x7DB3,0x7DB2,0xA008,0xA004,0xA002,0x7DB1,
+ 0x7DB0,0xA002,0x7DAF,0x7DAD,0xA004,0xA002,0x7DAC,0x7DAB,0xA002,0x7DAA,0x7DA9,0xA010,0xA008,0xA004,0xA002,0x7DA8,
+ 0x7DA7,0xA002,0x7DA5,0x7DA4,0xA004,0xA002,0x7DA3,0x7DA2,0xA002,0x7DA1,0x7DA0,0xA008,0xA004,0xA002,0x7D9F,0x7D9E,
+ 0xA002,0x7D9D,0x7D9C,0xA004,0xA002,0x7D9B,0x7D9A,0xA000,0x7D99,0xA001,0xA020,0xA010,0xA008,0xA004,0xA002,0x6D78,
+ 0x70EC,0xA002,0x8FD1,0x7981,0xA004,0xA002,0x664B,0x9773,0xA002,0x8FDB,0x8C28,0xA008,0xA004,0xA002,0x4EC5,0x9526,
+ 0xA002,0x7D27,0x895F,0xA004,0xA002,0x6D25,0x4ECA,0xA002,0x91D1,0x65A4,0xA010,0xA008,0xA004,0xA002,0x7B4B,0x5DFE,
+ 0xA002,0x5C4A,0x8BEB,0xA004,0xA002,0x75A5,0x4ECB,0xA002,0x501F,0x754C,0xA008,0xA004,0xA002,0x82A5,0x85C9,0xA002,
+ 0x6212,0x59D0,0xA004,0xA002,0x89E3,0x7ED3,0xA002,0x6D01,0x7AED,0xA0FD,0xA07E,0xA03E,0xA01E,0xA00E,0xA006,0xA002,
+ 0x776B,0xA002,0x6377,0x6770,0xA004,0xA002,0x6854,0x8282,0xA002,0x52AB,0x622A,0xA008,0xA004,0xA002,0x9636,0x8857,
+ 0xA002,0x79F8,0x7686,0xA004,0xA002,0x63A5,0x63ED,0xA002,0x7A96,0x53EB,0xA010,0xA008,0xA004,0xA002,0x8F83,0x8F7F,
+ 0xA002,0x9175,0x6559,0xA004,0xA002,0x527F,0x7EDE,0xA002,0x7F34,0x997A,0xA008,0xA004,0xA002,0x89D2,0x72E1,0xA002,
+ 0x811A,0x4FA5,0xA004,0xA002,0x77EB,0x94F0,0xA002,0x6405,0x56BC,0xA020,0xA010,0xA008,0xA004,0xA002,0x5A07,0x9A84,
+ 0xA002,0x6D47,0x90CA,0xA004,0xA002,0x4EA4,0x80F6,0xA002,0x7126,0x7901,0xA008,0xA004,0xA002,0x6912,0x8549,0xA002,
+ 0x964D,0x9171,0xA004,0xA002,0x5320,0x8BB2,0xA002,0x5956,0x6868,0xA010,0xA008,0xA004,0xA002,0x848B,0x7586,0xA002,
+ 0x6C5F,0x6D46,0xA004,0xA002,0x5C06,0x59DC,0xA002,0x50F5,0x5EFA,0xA008,0xA004,0xA002,0x6DA7,0x6E85,0xA002,0x6E10,
+ 0x996F,0xA004,0xA002,0x5251,0x8230,0xA002,0x5065,0x7D98,0xA040,0xA020,0xA010,0xA008,0xA004,0xA002,0x7D97,0x7D96,
+ 0xA002,0x7D95,0x7D94,0xA004,0xA002,0x7D93,0x7D92,0xA002,0x7D91,0x7D90,0xA008,0xA004,0xA002,0x7D8F,0x7D8E,0xA002,
+ 0x7D8D,0x7D8C,0xA004,0xA002,0x7D8B,0x7D8A,0xA002,0x7D89,0x7D88,0xA010,0xA008,0xA004,0xA002,0x7D87,0x7D86,0xA002,
+ 0x7D85,0x7D84,0xA004,0xA002,0x7D83,0x7D82,0xA002,0x7D81,0x7D80,0xA008,0xA004,0xA002,0x7D7F,0x7D7E,0xA002,0x7D7D,
+ 0x7D7C,0xA004,0xA002,0x7D7B,0x7D7A,0xA002,0x7D79,0x7D78,0xA01F,0xA00F,0xA007,0xA003,0xA001,0x7D76,0xA002,0x7D75,
+ 0x7D74,0xA004,0xA002,0x7D73,0x7D72,0xA002,0x7D71,0x7D70,0xA008,0xA004,0xA002,0x7D6F,0x7D6D,0xA002,0x7D6C,0x7D6B,
+ 0xA004,0xA002,0x7D6A,0x7D69,0xA002,0x7D68,0x7D67,0xA010,0xA008,0xA004,0xA002,0x7D66,0x7D65,0xA002,0x7D64,0x7D63,
+ 0xA004,0xA002,0x7D62,0x7D61,0xA002,0x7D60,0x7D5F,0xA008,0xA004,0xA002,0x7D5E,0x7D5D,0xA002,0x7D5C,0x7D5B,0xA004,
+ 0xA002,0x7D5A,0x7D59,0xA002,0x7D58,0x7D57,0xA045,0xA03E,0xA01E,0xA00E,0xA006,0xA002,0x7D56,0xA002,0x7D55,0x7D54,
+ 0xA004,0xA002,0x7D53,0x7D52,0xA002,0x7D51,0x7D50,0xA008,0xA004,0xA002,0x7D4F,0x7D4E,0xA002,0x7D4D,0x7D4C,0xA004,
+ 0xA002,0x7D4B,0x7D4A,0xA002,0x7D49,0x7D48,0xA010,0xA008,0xA004,0xA002,0x7D47,0x7D46,0xA002,0x7D45,0x7D44,0xA004,
+ 0xA002,0x7D43,0x7D42,0xA002,0x7D41,0x7D40,0xA008,0xA004,0xA002,0x7D3F,0x7D3E,0xA002,0x7D3D,0x7D3C,0xA004,0xA002,
+ 0x7D3B,0x7D3A,0xA002,0x7D39,0x7D38,0xA000,0xA000,0xA000,0xA000,0xA000,0x7D37,0xA001,0xA01D,0xA00D,0xA005,0xA001,
+ 0xA002,0x4EF6,0x7BAD,0xA004,0xA002,0x952E,0x89C1,0xA002,0x8D31,0x8DF5,0xA008,0xA004,0xA002,0x9274,0x69DB,0xA002,
+ 0x8350,0x51CF,0xA004,0xA002,0x526A,0x4FED,0xA002,0x7B80,0x6361,0xA010,0xA008,0xA004,0xA002,0x62E3,0x7877,0xA002,
+ 0x78B1,0x67EC,0xA004,0xA002,0x68C0,0x8327,0xA002,0x7F04,0x5978,0xA008,0xA004,0xA002,0x8270,0x80A9,0xA002,0x517C,
+ 0x714E,0xA004,0xA002,0x95F4,0x7B3A,0xA002,0x5C16,0x575A,0xA17F,0xA0FD,0xA07E,0xA03E,0xA01E,0xA00E,0xA006,0xA002,
+ 0x76D1,0xA002,0x6B7C,0x5AC1,0xA004,0xA002,0x9A7E,0x67B6,0xA002,0x4EF7,0x7A3C,0xA008,0xA004,0xA002,0x5047,0x94BE,
+ 0xA002,0x7532,0x8D3E,0xA004,0xA002,0x988A,0x835A,0xA002,0x52A0,0x5BB6,0xA010,0xA008,0xA004,0xA002,0x4F73,0x5939,
+ 0xA002,0x67B7,0x5609,0xA004,0xA002,0x7EAA,0x7EE7,0xA002,0x5993,0x9645,0xA008,0xA004,0xA002,0x5FCC,0x65E2,0xA002,
+ 0x8BB0,0x8BA1,0xA004,0xA002,0x5BC2,0x5BC4,0xA002,0x6D4E,0x60B8,0xA020,0xA010,0xA008,0xA004,0xA002,0x5242,0x796D,
+ 0xA002,0x4F0E,0x5B63,0xA004,0xA002,0x5180,0x6280,0xA002,0x84DF,0x5DF1,0xA008,0xA004,0xA002,0x810A,0x51E0,0xA002,
+ 0x6324,0x7EA7,0xA004,0xA002,0x5AC9,0x5373,0xA002,0x6C72,0x75BE,0xA010,0xA008,0xA004,0xA002,0x6025,0x53CA,0xA002,
+ 0x96C6,0x7C4D,0xA004,0xA002,0x8F91,0x68D8,0xA002,0x6781,0x5409,0xA008,0xA004,0xA002,0x7F09,0x7EE9,0xA002,0x59EC,
+ 0x9E21,0xA004,0xA002,0x8BA5,0x6FC0,0xA002,0x8FF9,0x9965,0xA040,0xA020,0xA010,0xA008,0xA004,0xA002,0x808C,0x7D36,
+ 0xA002,0x7D35,0x7D34,0xA004,0xA002,0x7D33,0x7D32,0xA002,0x7D31,0x7D30,0xA008,0xA004,0xA002,0x7D2E,0x7D2D,0xA002,
+ 0x7D2C,0x7D2A,0xA004,0xA002,0x7D29,0x7D28,0xA002,0x7D26,0x7D25,0xA010,0xA008,0xA004,0xA002,0x7D24,0x7D23,0xA002,
+ 0x7D21,0x7D1F,0xA004,0xA002,0x7D1E,0x7D1D,0xA002,0x7D1C,0x7D1B,0xA008,0xA004,0xA002,0x7D1A,0x7D19,0xA002,0x7D18,
+ 0x7D17,0xA004,0xA002,0x7D16,0x7D15,0xA002,0x7D14,0x7D13,0xA01F,0xA00F,0xA007,0xA004,0xA002,0x7D12,0x7D11,0xA001,
+ 0x7D10,0xA004,0xA002,0x7D0F,0x7D0E,0xA002,0x7D0D,0x7D0C,0xA008,0xA004,0xA002,0x7D0B,0x7D09,0xA002,0x7D08,0x7D07,
+ 0xA004,0xA002,0x7D06,0x7D05,0xA002,0x7D04,0x7D03,0xA010,0xA008,0xA004,0xA002,0x7D02,0x7D01,0xA002,0x7D00,0x7CFF,
+ 0xA004,0xA002,0x7CFE,0x7CFD,0xA002,0x7CFC,0x7CFA,0xA008,0xA004,0xA002,0x7CF9,0x7CF7,0xA002,0x7CF6,0x7CF5,0xA004,
+ 0xA002,0x7CF4,0x7CF3,0xA002,0x7CF2,0x7CF1,0xA048,0xA03E,0xA01E,0xA00E,0xA006,0xA002,0x7CF0,0xA002,0x7CEE,0x7CED,
+ 0xA004,0xA002,0x7CEC,0x7CEB,0xA002,0x7CEA,0x7CE9,0xA008,0xA004,0xA002,0x7CE7,0x7CE6,0xA002,0x7CE5,0x7CE4,0xA004,
+ 0xA002,0x7CE3,0x7CE2,0xA002,0x7CE1,0x7CDE,0xA010,0xA008,0xA004,0xA002,0x7CDD,0x7CDB,0xA002,0x7CDA,0x7CD8,0xA004,
+ 0xA002,0x7CD4,0x7CD3,0xA002,0x7CD2,0x7CD1,0xA008,0xA004,0xA002,0x7CD0,0x7CCF,0xA002,0x7CCE,0x7CCB,0xA004,0xA002,
+ 0x7CC9,0x7CC6,0xA002,0x7CC4,0x7CC3,0xA000,0xA000,0xA000,0xA004,0xA002,0x7CC2,0x7CC0,0xA000,0x7CBF,0xA001,0xA019,
+ 0xA009,0xA001,0xA004,0xA002,0x7B95,0x79EF,0xA002,0x7A3D,0x7578,0xA008,0xA004,0xA002,0x673A,0x57FA,0xA002,0x573E,
+ 0x51FB,0xA004,0xA002,0x7978,0x8D27,0xA002,0x970D,0x60D1,0xA010,0xA008,0xA004,0xA002,0x6216,0x83B7,0xA002,0x706B,
+ 0x4F19,0xA004,0xA002,0x6D3B,0x8C41,0xA002,0x6DF7,0x6D51,0xA008,0xA004,0xA002,0x9B42,0x5A5A,0xA002,0x660F,0x8364,
+ 0xA004,0xA002,0x7ED8,0x8BF2,0xA002,0x8BB3,0x6C47,0xA0FD,0xA07E,0xA03E,0xA01E,0xA00E,0xA006,0xA002,0x70E9,0xA002,
+ 0x4F1A,0x79FD,0xA004,0xA002,0x8D3F,0x6666,0xA002,0x60E0,0x5349,0xA008,0xA004,0xA002,0x6167,0x6094,0xA002,0x6BC1,
+ 0x56DE,0xA004,0xA002,0x86D4,0x6062,0xA002,0x5FBD,0x8F89,0xA010,0xA008,0xA004,0xA002,0x6325,0x7070,0xA002,0x8C0E,
+ 0x604D,0xA004,0xA002,0x5E4C,0x6643,0xA002,0x714C,0x60F6,0xA008,0xA004,0xA002,0x51F0,0x7687,0xA002,0x7C27,0x8757,
+ 0xA004,0xA002,0x78FA,0x9EC4,0xA002,0x614C,0x8352,0xA020,0xA010,0xA008,0xA004,0xA002,0x5E7B,0x5BA6,0xA002,0x6DA3,
+ 0x7115,0xA004,0xA002,0x8C62,0x75EA,0xA002,0x5524,0x60A3,0xA008,0xA004,0xA002,0x6362,0x7F13,0xA002,0x8FD8,0x6853,
+ 0xA004,0xA002,0x73AF,0x6B22,0xA002,0x574F,0x6DEE,0xA010,0xA008,0xA004,0xA002,0x6000,0x5F8A,0xA002,0x69D0,0x8BDD,
+ 0xA004,0xA002,0x5316,0x5212,0xA002,0x753B,0x6ED1,0xA008,0xA004,0xA002,0x733E,0x534E,0xA002,0x54D7,0x82B1,0xA004,
+ 0xA002,0x6237,0x6CAA,0xA002,0x4E92,0x62A4,0xA040,0xA020,0xA010,0xA008,0xA004,0xA002,0x552C,0x864E,0xA002,0x5F27,
+ 0x7CBB,0xA004,0xA002,0x7CBA,0x7CB8,0xA002,0x7CB7,0x7CB6,0xA008,0xA004,0xA002,0x7CB5,0x7CB4,0xA002,0x7CB0,0x7CAF,
+ 0xA004,0xA002,0x7CAD,0x7CAC,0xA002,0x7CAB,0x7CA9,0xA010,0xA008,0xA004,0xA002,0x7CA8,0x7CA7,0xA002,0x7CA6,0x7CA3,
+ 0xA004,0xA002,0x7CA1,0x7CA0,0xA002,0x7C9B,0x7C9A,0xA008,0xA004,0xA002,0x7C99,0x7C96,0xA002,0x7C94,0x7C93,0xA004,
+ 0xA002,0x7C90,0x7C8F,0xA002,0x7C8E,0x7C8D,0xA01F,0xA00F,0xA008,0xA004,0xA002,0x7C8C,0x7C8B,0xA002,0x7C8A,0x7C88,
+ 0xA003,0xA001,0x7C87,0xA002,0x7C86,0x7C85,0xA008,0xA004,0xA002,0x7C84,0x7C83,0xA002,0x7C82,0x7C81,0xA004,0xA002,
+ 0x7C80,0x7C7F,0xA002,0x7C7E,0x7C7A,0xA010,0xA008,0xA004,0xA002,0x7C79,0x7C78,0xA002,0x7C77,0x7C76,0xA004,0xA002,
+ 0x7C75,0x7C72,0xA002,0x7C71,0x7C70,0xA008,0xA004,0xA002,0x7C6F,0x7C6E,0xA002,0x7C6D,0x7C6C,0xA004,0xA002,0x7C6B,
+ 0x7C6A,0xA002,0x7C69,0x7C68,0xA04C,0xA03E,0xA01E,0xA00E,0xA006,0xA002,0x7C67,0xA002,0x7C66,0x7C65,0xA004,0xA002,
+ 0x7C64,0x7C63,0xA002,0x7C62,0x7C61,0xA008,0xA004,0xA002,0x7C60,0x7C5F,0xA002,0x7C5E,0x7C5D,0xA004,0xA002,0x7C5C,
+ 0x7C5B,0xA002,0x7C5A,0x7C59,0xA010,0xA008,0xA004,0xA002,0x7C58,0x7C57,0xA002,0x7C56,0x7C55,0xA004,0xA002,0x7C54,
+ 0x7C53,0xA002,0x7C52,0x7C51,0xA008,0xA004,0xA002,0x7C50,0x7C4F,0xA002,0x7C4E,0x7C4C,0xA004,0xA002,0x7C4B,0x7C4A,
+ 0xA002,0x7C49,0x7C48,0xA000,0xA000,0xA008,0xA004,0xA002,0x7C47,0x7C46,0xA002,0x7C45,0x7C44,0xA000,0xA000,0x7C43,
+ 0xA001,0xA016,0xA006,0xA001,0xA001,0xA002,0x6E56,0x7CCA,0xA008,0xA004,0xA002,0x72D0,0x8774,0xA002,0x80E1,0x846B,
+ 0xA004,0xA002,0x58F6,0x745A,0xA002,0x5FFD,0x4E4E,0xA010,0xA008,0xA004,0xA002,0x547C,0x540E,0xA002,0x5019,0x539A,
+ 0xA004,0xA002,0x543C,0x7334,0xA002,0x4FAF,0x5589,0xA008,0xA004,0xA002,0x7EA2,0x5F18,0xA002,0x5B8F,0x6D2A,0xA004,
+ 0xA002,0x9E3F,0x8679,0xA002,0x70D8,0x54C4,0xA2FC,0xA17C,0xA0FB,0xA07E,0xA03E,0xA01E,0xA00E,0xA006,0xA002,0x8F70,
+ 0xA002,0x6052,0x8861,0xA004,0xA002,0x6A2A,0x4EA8,0xA002,0x54FC,0x6068,0xA008,0xA004,0xA002,0x72E0,0x5F88,0xA002,
+ 0x75D5,0x9ED1,0xA004,0xA002,0x563F,0x8D3A,0xA002,0x9E64,0x8910,0xA010,0xA008,0xA004,0xA002,0x8D6B,0x6DB8,0xA002,
+ 0x6CB3,0x9602,0xA004,0xA002,0x8C89,0x76D2,0xA002,0x5408,0x4F55,0xA008,0xA004,0xA002,0x548C,0x79BE,0xA002,0x6838,
+ 0x83CF,0xA004,0xA002,0x8377,0x559D,0xA002,0x5475,0x6D69,0xA020,0xA010,0xA008,0xA004,0xA002,0x53F7,0x8017,0xA002,
+ 0x597D,0x90DD,0xA004,0xA002,0x6BEB,0x8C6A,0xA002,0x568E,0x58D5,0xA008,0xA004,0xA002,0x822A,0x676D,0xA002,0x592F,
+ 0x6C49,0xA004,0xA002,0x6C57,0x710A,0xA002,0x608D,0x61BE,0xA010,0xA008,0xA004,0xA002,0x65F1,0x634D,0xA002,0x64BC,
+ 0x7FF0,0xA004,0xA002,0x7F55,0x558A,0xA002,0x51FD,0x5BD2,0xA008,0xA004,0xA002,0x6DB5,0x542B,0xA002,0x97E9,0x90AF,
+ 0xA004,0xA002,0x61A8,0x9163,0xA002,0x9A87,0x5BB3,0xA03E,0xA01E,0xA00E,0xA006,0xA002,0x4EA5,0xA002,0x6C26,0x6D77,
+ 0xA004,0xA002,0x5B69,0x9AB8,0xA002,0x7C42,0x7C3E,0xA008,0xA004,0xA002,0x7C3D,0x7C3C,0xA002,0x7C3B,0x7C3A,0xA004,
+ 0xA002,0x7C39,0x7C37,0xA002,0x7C36,0x7C35,0xA010,0xA008,0xA004,0xA002,0x7C34,0x7C33,0xA002,0x7C32,0x7C31,0xA004,
+ 0xA002,0x7C30,0x7C2F,0xA002,0x7C2E,0x7C2D,0xA008,0xA004,0xA002,0x7C2C,0x7C2B,0xA002,0x7C29,0x7C28,0xA004,0xA002,
+ 0x7C25,0x7C24,0xA002,0x7C23,0x7C22,0xA01F,0xA00F,0xA008,0xA004,0xA002,0x7C21,0x7C20,0xA002,0x7C1E,0x7C1D,0xA004,
+ 0xA002,0x7C1C,0x7C1B,0xA000,0x7C1A,0xA008,0xA004,0xA002,0x7C19,0x7C18,0xA002,0x7C17,0x7C15,0xA004,0xA002,0x7C14,
+ 0x7C13,0xA002,0x7C12,0x7C11,0xA010,0xA008,0xA004,0xA002,0x7C10,0x7C0E,0xA002,0x7C0D,0x7C0A,0xA004,0xA002,0x7C09,
+ 0x7C08,0xA002,0x7C06,0x7C05,0xA008,0xA004,0xA002,0x7C04,0x7C03,0xA002,0x7C02,0x7C01,0xA004,0xA002,0x7C00,0x7BFF,
+ 0xA002,0x7BFD,0x7BFB,0xA050,0xA03E,0xA01E,0xA00E,0xA006,0xA002,0x7BFA,0xA002,0x7BF9,0x7BF8,0xA004,0xA002,0x7BF6,
+ 0x7BF5,0xA002,0x7BF4,0x7BF3,0xA008,0xA004,0xA002,0x7BF2,0x7BF0,0xA002,0x7BEF,0x7BED,0xA004,0xA002,0x7BEC,0x7BEB,
+ 0xA002,0x7BE9,0x7BE8,0xA010,0xA008,0xA004,0xA002,0x7BE7,0x7BE4,0xA002,0x7BE3,0x7BE2,0xA004,0xA002,0x7BE0,0x7BDF,
+ 0xA002,0x7BDE,0x7BDC,0xA008,0xA004,0xA002,0x7BDB,0x7BD8,0xA002,0x7BD7,0x7BD6,0xA004,0xA002,0x7BD5,0x7BD4,0xA002,
+ 0x7BD2,0x7BD0,0xA000,0xA000,0xA008,0xA004,0xA002,0x7BCF,0x7BCE,0xA002,0x7BCD,0x7BCB,0xA004,0xA002,0x7BCA,0x7BC9,
+ 0xA002,0x7BC8,0x7BC5,0xA001,0xA010,0xA001,0xA007,0xA003,0xA001,0x54C8,0xA002,0x8FC7,0x88F9,0xA004,0xA002,0x679C,
+ 0x56FD,0xA002,0x90ED,0x9505,0xA010,0xA008,0xA004,0xA002,0x68CD,0x6EDA,0xA002,0x8F8A,0x523D,0xA004,0xA002,0x8D35,
+ 0x8DEA,0xA002,0x67DC,0x6842,0xA008,0xA004,0xA002,0x7678,0x8BE1,0xA002,0x9B3C,0x8F68,0xA004,0xA002,0x95FA,0x9F9F,
+ 0xA002,0x5F52,0x7845,0xA0FD,0xA07E,0xA03E,0xA01E,0xA00E,0xA006,0xA002,0x572D,0xA002,0x89C4,0x7470,0xA004,0xA002,
+ 0x901B,0x5E7F,0xA002,0x5149,0x8D2F,0xA008,0xA004,0xA002,0x704C,0x60EF,0xA002,0x7F50,0x9986,0xA004,0xA002,0x7BA1,
+ 0x89C2,0xA002,0x51A0,0x5B98,0xA010,0xA008,0xA004,0xA002,0x5173,0x68FA,0xA002,0x602A,0x62D0,0xA004,0xA002,0x4E56,
+ 0x8902,0xA002,0x6302,0x5BE1,0xA008,0xA004,0xA002,0x5250,0x74DC,0xA002,0x522E,0x96C7,0xA004,0xA002,0x56FA,0x987E,
+ 0xA002,0x6545,0x80A1,0xA020,0xA010,0xA008,0xA004,0xA002,0x8C37,0x9AA8,0xA002,0x86CA,0x53E4,0xA004,0xA002,0x9F13,
+ 0x59D1,0xA002,0x5B64,0x6CBD,0xA008,0xA004,0xA002,0x4F30,0x7B8D,0xA002,0x5495,0x83C7,0xA004,0xA002,0x8F9C,0x591F,
+ 0xA002,0x8D2D,0x6784,0xA010,0xA008,0xA004,0xA002,0x57A2,0x72D7,0xA002,0x82DF,0x6C9F,0xA004,0xA002,0x52FE,0x94A9,
+ 0xA002,0x5171,0x8D21,0xA008,0xA004,0xA002,0x62F1,0x6C5E,0xA002,0x5DE9,0x5F13,0xA004,0xA002,0x5BAB,0x516C,0xA002,
+ 0x8EAC,0x4F9B,0xA040,0xA020,0xA010,0xA008,0xA004,0xA002,0x9F9A,0x606D,0xA002,0x529F,0x653B,0xA004,0xA002,0x5DE5,
+ 0x6897,0xA002,0x803F,0x57C2,0xA008,0xA004,0xA002,0x7BC4,0x7BC3,0xA002,0x7BC2,0x7BC0,0xA004,0xA002,0x7BBF,0x7BBE,
+ 0xA002,0x7BBD,0x7BBC,0xA010,0xA008,0xA004,0xA002,0x7BBB,0x7BBA,0xA002,0x7BB9,0x7BB7,0xA004,0xA002,0x7BB6,0x7BB5,
+ 0xA002,0x7BB3,0x7BB2,0xA008,0xA004,0xA002,0x7BB0,0x7BAF,0xA002,0x7BAE,0x7BA5,0xA004,0xA002,0x7BA4,0x7BA3,0xA002,
+ 0x7BA0,0x7B9F,0xA01F,0xA010,0xA008,0xA004,0xA002,0x7B9E,0x7B9B,0xA002,0x7B9A,0x7B99,0xA004,0xA002,0x7B98,0x7B96,
+ 0xA002,0x7B93,0x7B92,0xA007,0xA003,0xA000,0x7B91,0xA002,0x7B8F,0x7B8E,0xA004,0xA002,0x7B8C,0x7B8B,0xA002,0x7B8A,
+ 0x7B89,0xA010,0xA008,0xA004,0xA002,0x7B88,0x7B87,0xA002,0x7B86,0x7B84,0xA004,0xA002,0x7B83,0x7B82,0xA002,0x7B81,
+ 0x7B7F,0xA008,0xA004,0xA002,0x7B7D,0x7B7C,0xA002,0x7B7A,0x7B78,0xA004,0xA002,0x7B76,0x7B74,0xA002,0x7B73,0x7B70,
+ 0xA055,0xA03E,0xA01E,0xA00E,0xA006,0xA002,0x7B6F,0xA002,0x7B6D,0x7B6C,0xA004,0xA002,0x7B6B,0x7B6A,0xA002,0x7B69,
+ 0x7B68,0xA008,0xA004,0xA002,0x7B67,0x7B66,0xA002,0x7B65,0x7B64,0xA004,0xA002,0x7B63,0x7B61,0xA002,0x7B5F,0x7B5E,
+ 0xA010,0xA008,0xA004,0xA002,0x7B5C,0x7B59,0xA002,0x7B57,0x7B55,0xA004,0xA002,0x7B53,0x7B4E,0xA002,0x7B4D,0x7B4A,
+ 0xA008,0xA004,0xA002,0x7B48,0x7B46,0xA002,0x7B44,0x7B43,0xA004,0xA002,0x7B42,0x7B41,0xA002,0x7B40,0x7B3F,0xA000,
+ 0xA010,0xA008,0xA004,0xA002,0x7B3D,0x7B3B,0xA002,0x7B39,0x7B37,0xA004,0xA002,0x7B36,0x7B35,0xA002,0x7B34,0x7B32,
+ 0xA000,0xA000,0xA002,0x7B30,0x7B2F,0xA001,0xA00D,0xA001,0xA004,0xA001,0xA001,0x7FB9,0xA004,0xA002,0x5E9A,0x66F4,
+ 0xA002,0x8015,0x8DDF,0xA010,0xA008,0xA004,0xA002,0x6839,0x7ED9,0xA002,0x5404,0x4E2A,0xA004,0xA002,0x94EC,0x9694,
+ 0xA002,0x9601,0x86E4,0xA008,0xA004,0xA002,0x683C,0x845B,0xA002,0x9769,0x5272,0xA004,0xA002,0x7599,0x80F3,0xA002,
+ 0x9E3D,0x6208,0xA17F,0xA0FD,0xA07E,0xA03E,0xA01E,0xA00E,0xA006,0xA002,0x6401,0xA002,0x6B4C,0x54E5,0xA004,0xA002,
+ 0x544A,0x7A3F,0xA002,0x9550,0x641E,0xA008,0xA004,0xA002,0x7CD5,0x7F94,0xA002,0x818F,0x9AD8,0xA004,0xA002,0x768B,
+ 0x7BD9,0xA002,0x6760,0x6E2F,0xA010,0xA008,0xA004,0xA002,0x5C97,0x7EB2,0xA002,0x809B,0x7F38,0xA004,0xA002,0x94A2,
+ 0x521A,0xA002,0x5188,0x8D63,0xA008,0xA004,0xA002,0x6562,0x79C6,0xA002,0x611F,0x8D76,0xA004,0xA002,0x809D,0x7AFF,
+ 0xA002,0x67D1,0x6746,0xA020,0xA010,0xA008,0xA004,0xA002,0x7518,0x5E72,0xA002,0x6E89,0x76D6,0xA004,0xA002,0x9499,
+ 0x6982,0xA002,0x6539,0x8BE5,0xA008,0xA004,0xA002,0x560E,0x5676,0xA002,0x5490,0x7F1A,0xA004,0xA002,0x5987,0x9644,
+ 0xA002,0x8BA3,0x5BCC,0xA010,0xA008,0xA004,0xA002,0x8D1F,0x8179,0xA002,0x7236,0x961C,0xA004,0xA002,0x4ED8,0x5085,
+ 0xA002,0x590D,0x8D4B,0xA008,0xA004,0xA002,0x8986,0x526F,0xA002,0x8D74,0x8150,0xA004,0xA002,0x5E9C,0x8151,0xA002,
+ 0x812F,0x65A7,0xA040,0xA020,0xA010,0xA008,0xA004,0xA002,0x91DC,0x4FEF,0xA002,0x8F85,0x629A,0xA004,0xA002,0x752B,
+ 0x5F17,0xA002,0x88B1,0x798F,0xA008,0xA004,0xA002,0x6DAA,0x6D6E,0xA002,0x7B2D,0x7B29,0xA004,0xA002,0x7B27,0x7B23,
+ 0xA002,0x7B22,0x7B21,0xA010,0xA008,0xA004,0xA002,0x7B1F,0x7B1D,0xA002,0x7B1C,0x7B1A,0xA004,0xA002,0x7B18,0x7B17,
+ 0xA002,0x7B16,0x7B13,0xA008,0xA004,0xA002,0x7B12,0x7B10,0xA002,0x7B0E,0x7B0D,0xA004,0xA002,0x7B0C,0x7B09,0xA002,
+ 0x7B07,0x7B05,0xA01F,0xA010,0xA008,0xA004,0xA002,0x7B02,0x7B01,0xA002,0x7B00,0x7AFE,0xA004,0xA002,0x7AFC,0x7AFB,
+ 0xA002,0x7AF8,0x7AF7,0xA007,0xA004,0xA002,0x7AF6,0x7AF5,0xA000,0x7AF4,0xA004,0xA002,0x7AF3,0x7AF2,0xA002,0x7AF1,
+ 0x7AF0,0xA010,0xA008,0xA004,0xA002,0x7AEE,0x7AEC,0xA002,0x7AEB,0x7AEA,0xA004,0xA002,0x7AE9,0x7AE8,0xA002,0x7AE7,
+ 0x7AE4,0xA008,0xA004,0xA002,0x7AE2,0x7AE1,0xA002,0x7ADD,0x7ADC,0xA004,0xA002,0x7ADB,0x7ADA,0xA002,0x7AD8,0x7AD7,
+ 0xA058,0xA03E,0xA01E,0xA00E,0xA006,0xA002,0x7AD5,0xA002,0x7AD4,0x7AD3,0xA004,0xA002,0x7AD2,0x7AD1,0xA002,0x7AD0,
+ 0x7ACF,0xA008,0xA004,0xA002,0x7ACE,0x7ACD,0xA002,0x7ACC,0x7ACA,0xA004,0xA002,0x7AC9,0x7AC8,0xA002,0x7AC7,0x7AC6,
+ 0xA010,0xA008,0xA004,0xA002,0x7AC5,0x7AC4,0xA002,0x7AC3,0x7AC2,0xA004,0xA002,0x7AC1,0x7AC0,0xA002,0x7ABE,0x7ABD,
+ 0xA008,0xA004,0xA002,0x7ABC,0x7ABB,0xA002,0x7ABA,0x7AB9,0xA004,0xA002,0x7AB8,0x7AB7,0xA002,0x7AB6,0x7AB5,0xA000,
+ 0xA010,0xA008,0xA004,0xA002,0x7AB4,0x7AB2,0xA002,0x7AB1,0x7AB0,0xA004,0xA002,0x7AAF,0x7AAE,0xA002,0x7AAB,0x7AAA,
+ 0xA000,0xA004,0xA002,0x7AA9,0x7AA7,0xA002,0x7AA4,0x7AA3,0xA001,0xA009,0xA001,0xA001,0xA003,0xA001,0x670D,0xA002,
+ 0x4FD8,0x4F0F,0xA010,0xA008,0xA004,0xA002,0x7B26,0x6C1F,0xA002,0x5E45,0x8F90,0xA004,0xA002,0x62C2,0x6276,0xA002,
+ 0x5B75,0x80A4,0xA008,0xA004,0xA002,0x6577,0x592B,0xA002,0x5426,0x4F5B,0xA004,0xA002,0x51E4,0x5949,0xA002,0x8BBD,
+ 0x7F1D,0xA0FD,0xA07E,0xA03E,0xA01E,0xA00E,0xA006,0xA002,0x51AF,0xA002,0x9022,0x70FD,0xA004,0xA002,0x75AF,0x98CE,
+ 0xA002,0x950B,0x5CF0,0xA008,0xA004,0xA002,0x8702,0x67AB,0xA002,0x5C01,0x4E30,0xA004,0xA002,0x7CAA,0x6124,0xA002,
+ 0x5FFF,0x4EFD,0xA010,0xA008,0xA004,0xA002,0x594B,0x7C89,0xA002,0x6C7E,0x711A,0xA004,0xA002,0x575F,0x7EB7,0xA002,
+ 0x5206,0x6C1B,0xA008,0xA004,0xA002,0x5429,0x915A,0xA002,0x82AC,0x8D39,0xA004,0xA002,0x6CB8,0x5E9F,0xA002,0x80BA,
+ 0x5420,0xA020,0xA010,0xA008,0xA004,0xA002,0x8BFD,0x532A,0xA002,0x80A5,0x98DE,0xA004,0xA002,0x5561,0x975E,0xA002,
+ 0x83F2,0x653E,0xA008,0xA004,0xA002,0x7EBA,0x8BBF,0xA002,0x4EFF,0x59A8,0xA004,0xA002,0x9632,0x623F,0xA002,0x80AA,
+ 0x65B9,0xA010,0xA008,0xA004,0xA002,0x82B3,0x574A,0xA002,0x6CDB,0x996D,0xA004,0xA002,0x72AF,0x8D29,0xA002,0x8303,
+ 0x8FD4,0xA008,0xA004,0xA002,0x53CD,0x70E6,0xA002,0x51E1,0x7E41,0xA004,0xA002,0x9492,0x77FE,0xA002,0x6A0A,0x7FFB,
+ 0xA040,0xA020,0xA010,0xA008,0xA004,0xA002,0x756A,0x5E06,0xA002,0x85E9,0x73D0,0xA004,0xA002,0x6CD5,0x9600,0xA002,
+ 0x4E4F,0x4F10,0xA008,0xA004,0xA002,0x7B4F,0x7F5A,0xA002,0x53D1,0x8D30,0xA004,0xA002,0x7AA2,0x7AA1,0xA002,0x7A9E,
+ 0x7A9B,0xA010,0xA008,0xA004,0xA002,0x7A9A,0x7A99,0xA002,0x7A94,0x7A93,0xA004,0xA002,0x7A90,0x7A8F,0xA002,0x7A8E,
+ 0x7A8C,0xA008,0xA004,0xA002,0x7A8B,0x7A8A,0xA002,0x7A89,0x7A87,0xA004,0xA002,0x7A85,0x7A82,0xA002,0x7A7E,0x7A7D,
+ 0xA01F,0xA010,0xA008,0xA004,0xA002,0x7A7C,0x7A7B,0xA002,0x7A75,0x7A73,0xA004,0xA002,0x7A72,0x7A71,0xA002,0x7A6F,
+ 0x7A6E,0xA008,0xA004,0xA002,0x7A6D,0x7A6C,0xA002,0x7A6B,0x7A6A,0xA003,0xA000,0x7A69,0xA002,0x7A68,0x7A67,0xA010,
+ 0xA008,0xA004,0xA002,0x7A66,0x7A65,0xA002,0x7A64,0x7A63,0xA004,0xA002,0x7A62,0x7A61,0xA002,0x7A60,0x7A5F,0xA008,
+ 0xA004,0xA002,0x7A5E,0x7A5D,0xA002,0x7A5C,0x7A5B,0xA004,0xA002,0x7A5A,0x7A59,0xA002,0x7A58,0x7A56,0xA05C,0xA03E,
+ 0xA01E,0xA00E,0xA006,0xA002,0x7A55,0xA002,0x7A54,0x7A53,0xA004,0xA002,0x7A52,0x7A50,0xA002,0x7A4F,0x7A4E,0xA008,
+ 0xA004,0xA002,0x7A4D,0x7A4C,0xA002,0x7A4B,0x7A4A,0xA004,0xA002,0x7A49,0x7A48,0xA002,0x7A47,0x7A45,0xA010,0xA008,
+ 0xA004,0xA002,0x7A44,0x7A43,0xA002,0x7A42,0x7A41,0xA004,0xA002,0x7A40,0x7A3E,0xA002,0x7A3A,0x7A38,0xA008,0xA004,
+ 0xA002,0x7A36,0x7A35,0xA002,0x7A34,0x7A32,0xA004,0xA002,0x7A31,0x7A30,0xA002,0x7A2F,0x7A2E,0xA000,0xA010,0xA008,
+ 0xA004,0xA002,0x7A2D,0x7A2C,0xA002,0x7A2B,0x7A2A,0xA004,0xA002,0x7A29,0x7A28,0xA002,0x7A27,0x7A26,0xA008,0xA004,
+ 0xA002,0x7A25,0x7A24,0xA002,0x7A22,0x7A21,0xA000,0xA002,0x7A1F,0x7A1D,0xA001,0xA006,0xA001,0xA001,0xA001,0xA001,
+ 0x4E8C,0xA010,0xA008,0xA004,0xA002,0x6D31,0x9975,0xA002,0x5C14,0x8033,0xA004,0xA002,0x513F,0x800C,0xA002,0x6069,
+ 0x997F,0xA008,0xA004,0xA002,0x9102,0x904F,0xA002,0x627C,0x5384,0xA004,0xA002,0x6076,0x5A25,0xA002,0x8BB9,0x989D,
+ 0xA5FB,0xA2FD,0xA17E,0xA0FB,0xA07E,0xA03E,0xA01E,0xA00E,0xA006,0xA002,0x4FC4,0xA002,0x9E45,0x5CE8,0xA004,0xA002,
+ 0x86FE,0x5815,0xA002,0x60F0,0x5241,0xA008,0xA004,0xA002,0x8235,0x8DFA,0xA002,0x6735,0x8EB2,0xA004,0xA002,0x579B,
+ 0x593A,0xA002,0x591A,0x54C6,0xA010,0xA008,0xA004,0xA002,0x6387,0x9041,0xA002,0x76FE,0x949D,0xA004,0xA002,0x56E4,
+ 0x987F,0xA002,0x6566,0x8E72,0xA008,0xA004,0xA002,0x5428,0x58A9,0xA002,0x5BF9,0x961F,0xA004,0xA002,0x5151,0x5806,
+ 0xA002,0x7F0E,0x65AD,0xA020,0xA010,0xA008,0xA004,0xA002,0x6BB5,0x953B,0xA002,0x77ED,0x7AEF,0xA004,0xA002,0x5992,
+ 0x6E21,0xA002,0x5EA6,0x809A,0xA008,0xA004,0xA002,0x9540,0x675C,0xA002,0x8D4C,0x7779,0xA004,0xA002,0x5835,0x8BFB,
+ 0xA002,0x72EC,0x728A,0xA010,0xA008,0xA004,0xA002,0x6BD2,0x7763,0xA002,0x90FD,0x75D8,0xA004,0xA002,0x9017,0x8C46,
+ 0xA002,0x9661,0x6597,0xA008,0xA004,0xA002,0x6296,0x515C,0xA002,0x6D1E,0x51BB,0xA004,0xA002,0x606B,0x4F97,0xA002,
+ 0x680B,0x52A8,0xA03E,0xA01E,0xA00E,0xA006,0xA002,0x61C2,0xA002,0x8463,0x51AC,0xA004,0xA002,0x4E1C,0x4E22,0xA002,
+ 0x8BA2,0x5B9A,0xA008,0xA004,0xA002,0x952D,0x9F0E,0xA002,0x9876,0x9489,0xA004,0xA002,0x53EE,0x76EF,0xA002,0x4E01,
+ 0x7A1C,0xA010,0xA008,0xA004,0xA002,0x7A1B,0x7A19,0xA002,0x7A18,0x7A16,0xA004,0xA002,0x7A15,0x7A13,0xA002,0x7A12,
+ 0x7A11,0xA008,0xA004,0xA002,0x7A10,0x7A0F,0xA002,0x7A0C,0x7A0A,0xA004,0xA002,0x7A09,0x7A08,0xA002,0x7A07,0x7A05,
+ 0xA020,0xA010,0xA008,0xA004,0xA002,0x7A04,0x7A01,0xA002,0x79FF,0x79FE,0xA004,0xA002,0x79FC,0x79FA,0xA002,0x79F9,
+ 0x79F7,0xA008,0xA004,0xA002,0x79F6,0x79F5,0xA002,0x79F4,0x79F3,0xA004,0xA002,0x79F2,0x79F1,0xA002,0x79EE,0x79EC,
+ 0xA00F,0xA007,0xA003,0xA001,0x79EA,0xA002,0x79E8,0x79E5,0xA004,0xA002,0x79E2,0x79E1,0xA002,0x79E0,0x79DE,0xA008,
+ 0xA004,0xA002,0x79DD,0x79DC,0xA002,0x79DB,0x79DA,0xA004,0xA002,0x79D9,0x79D7,0xA002,0x79D6,0x79D4,0xA064,0xA03E,
+ 0xA01E,0xA00E,0xA006,0xA002,0x79D3,0xA002,0x79D0,0x79CF,0xA004,0xA002,0x79CE,0x79CC,0xA002,0x79CA,0x79C8,0xA008,
+ 0xA004,0xA002,0x79C7,0x79C5,0xA002,0x79C4,0x79C2,0xA004,0xA002,0x79BF,0x79BC,0xA002,0x79B8,0x79B7,0xA010,0xA008,
+ 0xA004,0xA002,0x79B6,0x79B5,0xA002,0x79B4,0x79B2,0xA004,0xA002,0x79B1,0x79B0,0xA002,0x79AF,0x79AE,0xA008,0xA004,
+ 0xA002,0x79AD,0x79AC,0xA002,0x79AB,0x79AA,0xA004,0xA002,0x79A9,0x79A8,0xA002,0x79A6,0x79A5,0xA020,0xA010,0xA008,
+ 0xA004,0xA002,0x79A4,0x79A3,0xA002,0x79A2,0x79A1,0xA004,0xA002,0x79A0,0x799F,0xA002,0x799E,0x799D,0xA008,0xA004,
+ 0xA002,0x799C,0x799B,0xA002,0x7999,0x7998,0xA004,0xA002,0x7997,0x7996,0xA002,0x7995,0x7994,0xA000,0xA000,0xA000,
+ 0xA000,0x7993,0xA001,0xA001,0xA00D,0xA005,0xA001,0xA002,0x53E0,0x8C0D,0xA004,0xA002,0x8FED,0x8776,0xA002,0x789F,
+ 0x7239,0xA008,0xA004,0xA002,0x8DCC,0x8C03,0xA002,0x9493,0x540A,0xA004,0xA002,0x6389,0x5201,0xA002,0x51CB,0x96D5,
+ 0xA0FD,0xA07E,0xA03E,0xA01E,0xA00E,0xA006,0xA002,0x53FC,0xA002,0x7889,0x6BBF,0xA004,0xA002,0x6DC0,0x5960,0xA002,
+ 0x60E6,0x5E97,0xA008,0xA004,0xA002,0x7538,0x4F43,0xA002,0x7535,0x57AB,0xA004,0xA002,0x975B,0x5178,0xA002,0x70B9,
+ 0x7898,0xA010,0xA008,0xA004,0xA002,0x6EC7,0x6382,0xA002,0x98A0,0x7F14,0xA004,0xA002,0x9012,0x5F1F,0xA002,0x5E1D,
+ 0x7B2C,0xA008,0xA004,0xA002,0x8482,0x5730,0xA002,0x5E95,0x62B5,0xA004,0xA002,0x5AE1,0x7FDF,0xA002,0x6DA4,0x72C4,
+ 0xA020,0xA010,0xA008,0xA004,0xA002,0x7B1B,0x654C,0xA002,0x8FEA,0x6EF4,0xA004,0xA002,0x4F4E,0x5824,0xA002,0x9093,
+ 0x51F3,0xA008,0xA004,0xA002,0x77AA,0x7B49,0xA002,0x767B,0x706F,0xA004,0xA002,0x8E6C,0x7684,0xA002,0x5F97,0x5FB7,
+ 0xA010,0xA008,0xA004,0xA002,0x76D7,0x9053,0xA002,0x60BC,0x7A3B,0xA004,0xA002,0x5230,0x5BFC,0xA002,0x7977,0x5C9B,
+ 0xA008,0xA004,0xA002,0x5012,0x8E48,0xA002,0x6363,0x5200,0xA004,0xA002,0x6863,0x8361,0xA002,0x515A,0x6321,0xA040,
+ 0xA020,0xA010,0xA008,0xA004,0xA002,0x5F53,0x86CB,0xA002,0x5F39,0x8BDE,0xA004,0xA002,0x6DE1,0x60EE,0xA002,0x4F46,
+ 0x6C2E,0xA008,0xA004,0xA002,0x65E6,0x80C6,0xA002,0x63B8,0x90F8,0xA004,0xA002,0x5355,0x4E39,0xA002,0x62C5,0x803D,
+ 0xA010,0xA008,0xA004,0xA002,0x6020,0x7992,0xA002,0x7991,0x7990,0xA004,0xA002,0x798E,0x798D,0xA002,0x798C,0x798B,
+ 0xA008,0xA004,0xA002,0x7989,0x7988,0xA002,0x7987,0x7986,0xA004,0xA002,0x7983,0x7982,0xA002,0x797F,0x797E,0xA020,
+ 0xA010,0xA008,0xA004,0xA002,0x797D,0x797C,0xA002,0x797B,0x7979,0xA004,0xA002,0x7976,0x7975,0xA002,0x7974,0x7973,
+ 0xA008,0xA004,0xA002,0x7972,0x7971,0xA002,0x7970,0x796E,0xA004,0xA002,0x796C,0x796B,0xA002,0x796A,0x7969,0xA00F,
+ 0xA007,0xA004,0xA002,0x7966,0x7964,0xA001,0x7963,0xA004,0xA002,0x7961,0x7959,0xA002,0x7958,0x7955,0xA008,0xA004,
+ 0xA002,0x7954,0x7952,0xA002,0x7951,0x7950,0xA004,0xA002,0x794F,0x794E,0xA002,0x794D,0x794C,0xA067,0xA03E,0xA01E,
+ 0xA00E,0xA006,0xA002,0x794B,0xA002,0x794A,0x7947,0xA004,0xA002,0x7945,0x7944,0xA002,0x7943,0x7942,0xA008,0xA004,
+ 0xA002,0x793F,0x793D,0xA002,0x7939,0x7938,0xA004,0xA002,0x7937,0x7936,0xA002,0x7935,0x7933,0xA010,0xA008,0xA004,
+ 0xA002,0x7932,0x7931,0xA002,0x7930,0x792F,0xA004,0xA002,0x792E,0x792D,0xA002,0x792C,0x792B,0xA008,0xA004,0xA002,
+ 0x792A,0x7929,0xA002,0x7928,0x7927,0xA004,0xA002,0x7926,0x7925,0xA002,0x7923,0x7922,0xA020,0xA010,0xA008,0xA004,
+ 0xA002,0x7921,0x7920,0xA002,0x791F,0x791D,0xA004,0xA002,0x791C,0x791B,0xA002,0x791A,0x7919,0xA008,0xA004,0xA002,
+ 0x7918,0x7917,0xA002,0x7916,0x7915,0xA004,0xA002,0x7914,0x7912,0xA002,0x7911,0x7910,0xA000,0xA000,0xA004,0xA002,
+ 0x790F,0x790E,0xA000,0x790D,0xA001,0xA001,0xA009,0xA001,0xA004,0xA002,0x902E,0x5F85,0xA002,0x888B,0x8D37,0xA008,
+ 0xA004,0xA002,0x4EE3,0x6B86,0xA002,0x5E26,0x6234,0xA004,0xA002,0x50A3,0x6B79,0xA002,0x5446,0x5927,0xA180,0xA0FD,
+ 0xA07E,0xA03E,0xA01E,0xA00E,0xA006,0xA002,0x6253,0xA002,0x7629,0x7B54,0xA004,0xA002,0x8FBE,0x642D,0xA002,0x9519,
+ 0x632B,0xA008,0xA004,0xA002,0x63AA,0x6413,0xA002,0x64AE,0x78CB,0xA004,0xA002,0x5BF8,0x5B58,0xA002,0x6751,0x7FE0,
+ 0xA010,0xA008,0xA004,0xA002,0x6DEC,0x7CB9,0xA002,0x7601,0x8106,0xA004,0xA002,0x50AC,0x5D14,0xA002,0x6467,0x7A9C,
+ 0xA008,0xA004,0xA002,0x7BE1,0x8E7F,0xA002,0x4FC3,0x7C07,0xA004,0xA002,0x918B,0x7C97,0xA002,0x51D1,0x4E1B,0xA020,
+ 0xA010,0xA008,0xA004,0xA002,0x4ECE,0x5306,0xA002,0x56F1,0x8471,0xA004,0xA002,0x806A,0x6B21,0xA002,0x8D50,0x523A,
+ 0xA008,0xA004,0xA002,0x6B64,0x8BCD,0xA002,0x74F7,0x6148,0xA004,0xA002,0x8F9E,0x96CC,0xA002,0x78C1,0x8328,0xA010,
+ 0xA008,0xA004,0xA002,0x75B5,0x7EF0,0xA002,0x6233,0x8822,0xA004,0xA002,0x7EAF,0x6DF3,0xA002,0x5507,0x9187,0xA008,
+ 0xA004,0xA002,0x693F,0x6625,0xA002,0x5782,0x9524,0xA004,0xA002,0x6376,0x708A,0xA002,0x5439,0x521B,0xA040,0xA020,
+ 0xA010,0xA008,0xA004,0xA002,0x95EF,0x5E8A,0xA002,0x5E62,0x7A97,0xA004,0xA002,0x75AE,0x4E32,0xA002,0x5598,0x8239,
+ 0xA008,0xA004,0xA002,0x4F20,0x693D,0xA002,0x7A7F,0x5DDD,0xA004,0xA002,0x63E3,0x5904,0xA002,0x89E6,0x6410,0xA010,
+ 0xA008,0xA004,0xA002,0x77D7,0x50A8,0xA002,0x7840,0x790C,0xA004,0xA002,0x790B,0x790A,0xA002,0x7909,0x7908,0xA008,
+ 0xA004,0xA002,0x7907,0x7906,0xA002,0x7904,0x7903,0xA004,0xA002,0x7902,0x7900,0xA002,0x78FF,0x78FE,0xA020,0xA010,
+ 0xA008,0xA004,0xA002,0x78FD,0x78FC,0xA002,0x78FB,0x78F9,0xA004,0xA002,0x78F8,0x78F6,0xA002,0x78F5,0x78F3,0xA008,
+ 0xA004,0xA002,0x78F1,0x78F0,0xA002,0x78EF,0x78EE,0xA004,0xA002,0x78ED,0x78EB,0xA002,0x78EA,0x78E9,0xA00F,0xA008,
+ 0xA004,0xA002,0x78E7,0x78E6,0xA002,0x78E5,0x78E4,0xA003,0xA001,0x78E3,0xA002,0x78E2,0x78E1,0xA008,0xA004,0xA002,
+ 0x78E0,0x78DF,0xA002,0x78DE,0x78DD,0xA004,0xA002,0x78DC,0x78DB,0xA002,0x78DA,0x78D8,0xA06B,0xA03E,0xA01E,0xA00E,
+ 0xA006,0xA002,0x78D7,0xA002,0x78D6,0x78D3,0xA004,0xA002,0x78D2,0x78D1,0xA002,0x78CF,0x78CE,0xA008,0xA004,0xA002,
+ 0x78CD,0x78CC,0xA002,0x78C8,0x78C7,0xA004,0xA002,0x78C6,0x78C4,0xA002,0x78C3,0x78C2,0xA010,0xA008,0xA004,0xA002,
+ 0x78C0,0x78BF,0xA002,0x78BD,0x78BC,0xA004,0xA002,0x78BB,0x78BA,0xA002,0x78B8,0x78B7,0xA008,0xA004,0xA002,0x78B6,
+ 0x78B5,0xA002,0x78AF,0x78AE,0xA004,0xA002,0x78AD,0x78AC,0xA002,0x78AB,0x78AA,0xA020,0xA010,0xA008,0xA004,0xA002,
+ 0x78A9,0x78A8,0xA002,0x78A6,0x78A4,0xA004,0xA002,0x78A2,0x78A0,0xA002,0x789E,0x789D,0xA008,0xA004,0xA002,0x7899,
+ 0x7896,0xA002,0x7895,0x7894,0xA004,0xA002,0x7892,0x7890,0xA002,0x788F,0x788B,0xA000,0xA008,0xA004,0xA002,0x788A,
+ 0x7888,0xA002,0x7886,0x7885,0xA000,0xA000,0x7884,0xA001,0xA001,0xA006,0xA001,0xA001,0xA002,0x695A,0x9664,0xA008,
+ 0xA004,0xA002,0x6EC1,0x96CF,0xA002,0x9504,0x8E87,0xA004,0xA002,0x53A8,0x6A71,0xA002,0x51FA,0x521D,0xA0FD,0xA07E,
+ 0xA03E,0xA01E,0xA00E,0xA006,0xA002,0x81ED,0xA002,0x4E11,0x7785,0xA004,0xA002,0x7EF8,0x4EC7,0xA002,0x7B79,0x6101,
+ 0xA008,0xA004,0xA002,0x7A20,0x8E0C,0xA002,0x7574,0x916C,0xA004,0xA002,0x62BD,0x5BA0,0xA002,0x5D07,0x866B,0xA010,
+ 0xA008,0xA004,0xA002,0x51B2,0x5145,0xA002,0x70BD,0x65A5,0xA004,0xA002,0x7FC5,0x8D64,0xA002,0x5C3A,0x4F88,0xA008,
+ 0xA004,0xA002,0x9F7F,0x803B,0xA002,0x9A70,0x5F1B,0xA004,0xA002,0x8FDF,0x6C60,0xA002,0x5319,0x6301,0xA020,0xA010,
+ 0xA008,0xA004,0xA002,0x75F4,0x5403,0xA002,0x79E4,0x9A8B,0xA004,0xA002,0x901E,0x627F,0xA002,0x8BDA,0x6F84,0xA008,
+ 0xA004,0xA002,0x60E9,0x7A0B,0xA002,0x4E58,0x5448,0xA004,0xA002,0x6210,0x6A59,0xA002,0x57CE,0x79F0,0xA010,0xA008,
+ 0xA004,0xA002,0x6491,0x886C,0xA002,0x8D81,0x9648,0xA004,0xA002,0x6C89,0x5FF1,0xA002,0x6668,0x5C18,0xA008,0xA004,
+ 0xA002,0x8FB0,0x81E3,0xA002,0x90F4,0x6F88,0xA004,0xA002,0x5F7B,0x63A3,0xA002,0x64A4,0x626F,0xA040,0xA020,0xA010,
+ 0xA008,0xA004,0xA002,0x8F66,0x7092,0xA002,0x5435,0x5DE2,0xA004,0xA002,0x6F6E,0x5632,0xA002,0x671D,0x949E,0xA008,
+ 0xA004,0xA002,0x6284,0x8D85,0xA002,0x5021,0x5531,0xA004,0xA002,0x7545,0x655E,0xA002,0x5382,0x80A0,0xA010,0xA008,
+ 0xA004,0xA002,0x507F,0x957F,0xA002,0x5E38,0x5C1D,0xA004,0xA002,0x573A,0x7883,0xA002,0x7882,0x7881,0xA008,0xA004,
+ 0xA002,0x7880,0x787F,0xA002,0x787E,0x787D,0xA004,0xA002,0x787B,0x787A,0xA002,0x7879,0x7878,0xA020,0xA010,0xA008,
+ 0xA004,0xA002,0x7876,0x7875,0xA002,0x7874,0x7873,0xA004,0xA002,0x7872,0x7871,0xA002,0x7870,0x786F,0xA008,0xA004,
+ 0xA002,0x7869,0x7868,0xA002,0x7867,0x7866,0xA004,0xA002,0x7865,0x7864,0xA002,0x7863,0x7862,0xA00F,0xA008,0xA004,
+ 0xA002,0x7861,0x7860,0xA002,0x785F,0x785E,0xA004,0xA002,0x785C,0x785B,0xA001,0x785A,0xA008,0xA004,0xA002,0x7859,
+ 0x7858,0xA002,0x7854,0x7853,0xA004,0xA002,0x7851,0x784F,0xA002,0x784D,0x784B,0xA06E,0xA03E,0xA01E,0xA00E,0xA006,
+ 0xA002,0x784A,0xA002,0x7849,0x7848,0xA004,0xA002,0x7846,0x7844,0xA002,0x7843,0x7842,0xA008,0xA004,0xA002,0x7841,
+ 0x783F,0xA002,0x783D,0x7836,0xA004,0xA002,0x7835,0x7833,0xA002,0x7832,0x7831,0xA010,0xA008,0xA004,0xA002,0x782F,
+ 0x782E,0xA002,0x782B,0x782A,0xA004,0xA002,0x7828,0x7824,0xA002,0x7822,0x7821,0xA008,0xA004,0xA002,0x7820,0x781E,
+ 0xA002,0x781B,0x7819,0xA004,0xA002,0x7815,0x7813,0xA002,0x7810,0x780F,0xA020,0xA010,0xA008,0xA004,0xA002,0x780E,
+ 0x780B,0xA002,0x780A,0x7808,0xA004,0xA002,0x7807,0x7806,0xA002,0x7805,0x7804,0xA008,0xA004,0xA002,0x7803,0x77FC,
+ 0xA002,0x77FB,0x77FA,0xA004,0xA002,0x77F9,0x77F7,0xA002,0x77F5,0x77F4,0xA000,0xA008,0xA004,0xA002,0x77F2,0x77F1,
+ 0xA002,0x77F0,0x77EF,0xA004,0xA002,0x77EA,0x77E8,0xA000,0x77E6,0xA001,0xA001,0xA001,0xA008,0xA004,0xA002,0x7316,
+ 0x660C,0xA002,0x98A4,0x9610,0xA004,0xA002,0x4EA7,0x94F2,0xA002,0x7F20,0x8C17,0xA2FF,0xA180,0xA0FD,0xA07E,0xA03E,
+ 0xA01E,0xA00E,0xA006,0xA002,0x998B,0xA002,0x8749,0x63BA,0xA004,0xA002,0x6400,0x8C7A,0xA002,0x67F4,0x62C6,0xA008,
+ 0xA004,0xA002,0x8BE7,0x5DEE,0xA002,0x5C94,0x5BDF,0xA004,0xA002,0x643D,0x78B4,0xA002,0x67E5,0x8336,0xA010,0xA008,
+ 0xA004,0xA002,0x832C,0x53C9,0xA002,0x63D2,0x8E6D,0xA004,0xA002,0x5C42,0x6D4B,0xA002,0x518C,0x4FA7,0xA008,0xA004,
+ 0xA002,0x7B56,0x5395,0xA002,0x8349,0x66F9,0xA004,0xA002,0x69FD,0x7CD9,0xA002,0x64CD,0x85CF,0xA020,0xA010,0xA008,
+ 0xA004,0xA002,0x6CA7,0x4ED3,0xA002,0x8231,0x82CD,0xA004,0xA002,0x707F,0x60E8,0xA002,0x60ED,0x6B8B,0xA008,0xA004,
+ 0xA002,0x8695,0x53C2,0xA002,0x9910,0x8521,0xA004,0xA002,0x83DC,0x5F69,0xA002,0x91C7,0x8E29,0xA010,0xA008,0xA004,
+ 0xA002,0x776C,0x8D22,0xA002,0x624D,0x6750,0xA004,0xA002,0x88C1,0x731C,0xA002,0x64E6,0x6016,0xA008,0xA004,0xA002,
+ 0x90E8,0x7C3F,0xA002,0x6B65,0x5E03,0xA004,0xA002,0x4E0D,0x57E0,0xA002,0x8865,0x54FA,0xA040,0xA020,0xA010,0xA008,
+ 0xA004,0xA002,0x535C,0x6355,0xA002,0x9A73,0x6CCA,0xA004,0xA002,0x6E24,0x818A,0xA002,0x8116,0x8236,0xA008,0xA004,
+ 0xA002,0x5E1B,0x4F2F,0xA002,0x7B94,0x94C2,0xA004,0xA002,0x640F,0x52C3,0xA002,0x535A,0x6CE2,0xA010,0xA008,0xA004,
+ 0xA002,0x94B5,0x62E8,0xA002,0x64AD,0x83E0,0xA004,0xA002,0x73BB,0x5E76,0xA002,0x75C5,0x77E4,0xA008,0xA004,0xA002,
+ 0x77E1,0x77E0,0xA002,0x77DF,0x77DE,0xA004,0xA002,0x77DD,0x77DA,0xA002,0x77D9,0x77D8,0xA020,0xA010,0xA008,0xA004,
+ 0xA002,0x77D6,0x77D5,0xA002,0x77D4,0x77D3,0xA004,0xA002,0x77D2,0x77D1,0xA002,0x77D0,0x77CF,0xA008,0xA004,0xA002,
+ 0x77CE,0x77CC,0xA002,0x77CB,0x77CA,0xA004,0xA002,0x77C9,0x77C8,0xA002,0x77C7,0x77C6,0xA010,0xA008,0xA004,0xA002,
+ 0x77C5,0x77C4,0xA002,0x77C3,0x77C2,0xA004,0xA002,0x77C1,0x77C0,0xA002,0x77BE,0x77BC,0xA007,0xA003,0xA001,0x77BA,
+ 0xA002,0x77B9,0x77B8,0xA004,0xA002,0x77B7,0x77B6,0xA002,0x77B4,0x77B2,0xA073,0xA03E,0xA01E,0xA00E,0xA006,0xA002,
+ 0x77B1,0xA002,0x77AF,0x77AE,0xA004,0xA002,0x77AD,0x77AB,0xA002,0x77A8,0x77A6,0xA008,0xA004,0xA002,0x77A4,0x77A3,
+ 0xA002,0x77A1,0x779E,0xA004,0xA002,0x779D,0x779C,0xA002,0x779B,0x779A,0xA010,0xA008,0xA004,0xA002,0x7799,0x7798,
+ 0xA002,0x7797,0x7796,0xA004,0xA002,0x7795,0x7794,0xA002,0x7793,0x7790,0xA008,0xA004,0xA002,0x778F,0x778B,0xA002,
+ 0x778A,0x7789,0xA004,0xA002,0x7788,0x7787,0xA002,0x7786,0x7783,0xA020,0xA010,0xA008,0xA004,0xA002,0x7782,0x7781,
+ 0xA002,0x777C,0x777B,0xA004,0xA002,0x777A,0x7778,0xA002,0x7777,0x7776,0xA008,0xA004,0xA002,0x7775,0x7774,0xA002,
+ 0x7773,0x7772,0xA004,0xA002,0x7771,0x7770,0xA002,0x776F,0x776E,0xA010,0xA008,0xA004,0xA002,0x776D,0x776A,0xA002,
+ 0x7769,0x7767,0xA004,0xA002,0x7764,0x7760,0xA002,0x775F,0x775E,0xA000,0xA000,0xA000,0x775D,0xA001,0xA001,0xA001,
+ 0xA005,0xA001,0xA002,0x70B3,0x997C,0xA004,0xA002,0x79C9,0x4E19,0xA002,0x67C4,0x51B0,0xA0FD,0xA07E,0xA03E,0xA01E,
+ 0xA00E,0xA006,0xA002,0x5175,0xA002,0x6448,0x5BBE,0xA004,0xA002,0x6EE8,0x6FD2,0xA002,0x658C,0x5F6C,0xA008,0xA004,
+ 0xA002,0x762A,0x522B,0xA002,0x618B,0x9CD6,0xA004,0xA002,0x8868,0x8198,0xA002,0x5F6A,0x6807,0xA010,0xA008,0xA004,
+ 0xA002,0x904D,0x8FAB,0xA002,0x8FA9,0x8FA8,0xA004,0xA002,0x535E,0x53D8,0xA002,0x4FBF,0x6241,0xA008,0xA004,0xA002,
+ 0x8D2C,0x7F16,0xA002,0x8FB9,0x97AD,0xA004,0xA002,0x965B,0x907F,0xA002,0x81C2,0x58C1,0xA020,0xA010,0xA008,0xA004,
+ 0xA002,0x8F9F,0x5FC5,0xA002,0x5F0A,0x655D,0xA004,0xA002,0x95ED,0x75F9,0xA002,0x5E87,0x5E01,0xA008,0xA004,0xA002,
+ 0x6BD6,0x6BD9,0xA002,0x6BD5,0x853D,0xA004,0xA002,0x84D6,0x78A7,0xA002,0x5F7C,0x7B14,0xA010,0xA008,0xA004,0xA002,
+ 0x9119,0x6BD4,0xA002,0x9F3B,0x903C,0xA004,0xA002,0x8FF8,0x8E66,0xA002,0x6CF5,0x752D,0xA008,0xA004,0xA002,0x7EF7,
+ 0x5D29,0xA002,0x7B28,0x672C,0xA004,0xA002,0x82EF,0x5954,0xA002,0x88AB,0x7119,0xA040,0xA020,0xA010,0xA008,0xA004,
+ 0xA002,0x60EB,0x5907,0xA002,0x72C8,0x500D,0xA004,0xA002,0x94A1,0x8D1D,0xA002,0x80CC,0x8F88,0xA008,0xA004,0xA002,
+ 0x5317,0x5351,0xA002,0x60B2,0x7891,0xA004,0xA002,0x676F,0x7206,0xA002,0x9C8D,0x8C79,0xA010,0xA008,0xA004,0xA002,
+ 0x66B4,0x62A5,0xA002,0x62B1,0x5B9D,0xA004,0xA002,0x9971,0x5821,0xA002,0x4FDD,0x96F9,0xA008,0xA004,0xA002,0x8584,
+ 0x775C,0xA002,0x7759,0x7758,0xA004,0xA002,0x7757,0x7756,0xA002,0x7755,0x7754,0xA020,0xA010,0xA008,0xA004,0xA002,
+ 0x7753,0x7752,0xA002,0x774F,0x774E,0xA004,0xA002,0x774D,0x774C,0xA002,0x774B,0x774A,0xA008,0xA004,0xA002,0x7749,
+ 0x7748,0xA002,0x7746,0x7745,0xA004,0xA002,0x7744,0x7742,0xA002,0x773F,0x773E,0xA010,0xA008,0xA004,0xA002,0x773D,
+ 0x773B,0xA002,0x7739,0x7734,0xA004,0xA002,0x7733,0x7732,0xA002,0x7731,0x7730,0xA007,0xA004,0xA002,0x772E,0x772C,
+ 0xA001,0x772B,0xA004,0xA002,0x772A,0x7727,0xA002,0x7725,0x7724,0xA076,0xA03E,0xA01E,0xA00E,0xA006,0xA002,0x7723,
+ 0xA002,0x7721,0x771E,0xA004,0xA002,0x771D,0x771C,0xA002,0x771B,0x7718,0xA008,0xA004,0xA002,0x7717,0x7716,0xA002,
+ 0x7715,0x7714,0xA004,0xA002,0x7713,0x7712,0xA002,0x7711,0x7710,0xA010,0xA008,0xA004,0xA002,0x770F,0x770E,0xA002,
+ 0x770C,0x770A,0xA004,0xA002,0x7706,0x7705,0xA002,0x7703,0x7702,0xA008,0xA004,0xA002,0x7700,0x76FF,0xA002,0x76FD,
+ 0x76FB,0xA004,0xA002,0x76FA,0x76F7,0xA002,0x76F6,0x76F5,0xA020,0xA010,0xA008,0xA004,0xA002,0x76F3,0x76F0,0xA002,
+ 0x76ED,0x76EC,0xA004,0xA002,0x76EB,0x76EA,0xA002,0x76E9,0x76E8,0xA008,0xA004,0xA002,0x76E7,0x76E6,0xA002,0x76E4,
+ 0x76E3,0xA004,0xA002,0x76E2,0x76E1,0xA002,0x76E0,0x76DE,0xA010,0xA008,0xA004,0xA002,0x76DD,0x76DC,0xA002,0x76DA,
+ 0x76D9,0xA004,0xA002,0x76D5,0x76D3,0xA002,0x76CC,0x76CB,0xA000,0xA004,0xA002,0x76C9,0x76C7,0xA000,0x76C4,0xA001,
+ 0xA001,0xA001,0xA001,0xA004,0xA002,0x5265,0x8912,0xA002,0x5305,0x80DE,0xA178,0xA0FD,0xA07E,0xA03E,0xA01E,0xA00E,
+ 0xA006,0xA002,0x82DE,0xA002,0x8C24,0x508D,0xA004,0xA002,0x9551,0x868C,0xA002,0x78C5,0x68D2,0xA008,0xA004,0xA002,
+ 0x7ED1,0x8180,0xA002,0x699C,0x6886,0xA004,0xA002,0x5E2E,0x90A6,0xA002,0x7ECA,0x529E,0xA010,0xA008,0xA004,0xA002,
+ 0x534A,0x74E3,0xA002,0x4F34,0x62CC,0xA004,0xA002,0x626E,0x7248,0xA002,0x677F,0x9881,0xA008,0xA004,0xA002,0x822C,
+ 0x6273,0xA002,0x642C,0x73ED,0xA004,0xA002,0x6591,0x7A17,0xA002,0x62DC,0x8D25,0xA020,0xA010,0xA008,0xA004,0xA002,
+ 0x4F70,0x6446,0xA002,0x767E,0x67CF,0xA004,0xA002,0x767D,0x7238,0xA002,0x7F62,0x9738,0xA008,0xA004,0xA002,0x575D,
+ 0x8019,0xA002,0x628A,0x9776,0xA004,0xA002,0x8DCB,0x62D4,0xA002,0x5DF4,0x75A4,0xA010,0xA008,0xA004,0xA002,0x516B,
+ 0x7B06,0xA002,0x5427,0x53ED,0xA004,0xA002,0x6252,0x634C,0xA002,0x82AD,0x6FB3,0xA008,0xA004,0xA002,0x61CA,0x5965,
+ 0xA002,0x50B2,0x8884,0xA004,0xA002,0x7FF1,0x71AC,0xA002,0x6556,0x51F9,0xA040,0xA020,0xA010,0xA008,0xA004,0xA002,
+ 0x76CE,0x6602,0xA002,0x80AE,0x6848,0xA004,0xA002,0x80FA,0x5CB8,0xA002,0x6697,0x6309,0xA008,0xA004,0xA002,0x4FFA,
+ 0x5B89,0xA002,0x6C28,0x978D,0xA004,0xA002,0x9698,0x7231,0xA002,0x788D,0x827E,0xA010,0xA008,0xA004,0xA002,0x77EE,
+ 0x853C,0xA002,0x764C,0x7691,0xA004,0xA002,0x54C0,0x5509,0xA002,0x54CE,0x6328,0xA008,0xA004,0xA002,0x57C3,0x963F,
+ 0xA002,0x554A,0x76C3,0xA004,0xA002,0x76C1,0x76C0,0xA002,0x76BE,0x76BD,0xA020,0xA010,0xA008,0xA004,0xA002,0x76BC,
+ 0x76BB,0xA002,0x76BA,0x76B9,0xA004,0xA002,0x76B8,0x76B7,0xA002,0x76B6,0x76B5,0xA008,0xA004,0xA002,0x76B3,0x76B0,
+ 0xA002,0x76AF,0x76AD,0xA004,0xA002,0x76AC,0x76AB,0xA002,0x76AA,0x76A9,0xA010,0xA008,0xA004,0xA002,0x76A8,0x76A7,
+ 0xA002,0x76A6,0x76A5,0xA004,0xA002,0x76A3,0x76A2,0xA002,0x76A1,0x76A0,0xA008,0xA004,0xA002,0x769F,0x769E,0xA002,
+ 0x769D,0x769C,0xA003,0xA001,0x769B,0xA002,0x769A,0x7698,0xA000,0xA03E,0xA01E,0xA00E,0xA006,0xA002,0x7697,0xA002,
+ 0x7695,0x7694,0xA004,0xA002,0x7692,0x7690,0xA002,0x768F,0x768D,0xA008,0xA004,0xA002,0x768C,0x768A,0xA002,0x7689,
+ 0x7685,0xA004,0xA002,0x7683,0x7681,0xA002,0x7680,0x767F,0xA010,0xA008,0xA004,0xA002,0x767C,0x767A,0xA002,0x7679,
+ 0x7677,0xA004,0xA002,0x7676,0x7675,0xA002,0x7674,0x7673,0xA008,0xA004,0xA002,0x7672,0x7671,0xA002,0x7670,0x766E,
+ 0xA004,0xA002,0x766D,0x766C,0xA002,0x766A,0x7669,0xA020,0xA010,0xA008,0xA004,0xA002,0x7668,0x7667,0xA002,0x7666,
+ 0x7665,0xA004,0xA002,0x7664,0x7662,0xA002,0x7661,0x7660,0xA008,0xA004,0xA002,0x765F,0x765D,0xA002,0x765B,0x765A,
+ 0xA004,0xA002,0x7659,0x7658,0xA002,0x7657,0x7655,0xA010,0xA008,0xA004,0xA002,0x7653,0x7652,0xA002,0x7651,0x7650,
+ 0xA004,0xA002,0x764F,0x764E,0xA002,0x764B,0x764A,0xA008,0xA004,0xA002,0x7649,0x7648,0xA002,0x7647,0x7646,0xA000,
+ 0xA000,0x7645,0xA04A,0xA001,0xA00A,0xA001,0xA001,0xA001,0xA003,0xA001,0x7644,0xA002,0x7642,0x7641,0xA020,0xA010,
+ 0xA008,0xA004,0xA002,0x763D,0x763B,0xA002,0x763A,0x7639,0xA004,0xA002,0x7637,0x7636,0xA002,0x7632,0x7631,0xA008,
+ 0xA004,0xA002,0x762F,0x762E,0xA002,0x762C,0x7628,0xA004,0xA002,0x7627,0x7623,0xA002,0x7621,0x761E,0xA010,0xA008,
+ 0xA004,0xA002,0x761D,0x761C,0xA002,0x761A,0x7616,0xA004,0xA002,0x7614,0x7613,0xA002,0x7612,0x7611,0xA008,0xA004,
+ 0xA002,0x760F,0x760E,0xA002,0x760D,0x760B,0xA004,0xA002,0x7609,0x7608,0xA001,0x7607,0xA000,0xA03E,0xA01E,0xA00E,
+ 0xA006,0xA002,0x7606,0xA002,0x7604,0x7602,0xA004,0xA002,0x75FE,0x75FD,0xA002,0x75FB,0x75FA,0xA008,0xA004,0xA002,
+ 0x75F8,0x75F7,0xA002,0x75F6,0x75F5,0xA004,0xA002,0x75F3,0x75F2,0xA002,0x75EF,0x75EE,0xA010,0xA008,0xA004,0xA002,
+ 0x75ED,0x75EC,0xA002,0x75E9,0x75E5,0xA004,0xA002,0x75E1,0x75E0,0xA002,0x75DF,0x75DD,0xA008,0xA004,0xA002,0x75DC,
+ 0x75DA,0xA002,0x75D9,0x75D7,0xA004,0xA002,0x75D3,0x75D1,0xA002,0x75D0,0x75CF,0xA020,0xA010,0xA008,0xA004,0xA002,
+ 0x75CE,0x75CC,0xA002,0x75CB,0x75C6,0xA004,0xA002,0x75C1,0x75C0,0xA002,0x75BF,0x75BB,0xA008,0xA004,0xA002,0x75BA,
+ 0x75B7,0xA002,0x75B6,0x75AD,0xA004,0xA002,0x75AA,0x75A9,0xA002,0x75A8,0x75A7,0xA010,0xA008,0xA004,0xA002,0x75A6,
+ 0x75A2,0xA002,0x759E,0x759C,0xA004,0xA002,0x759B,0x7598,0xA002,0x7595,0x7593,0xA008,0xA004,0xA002,0x7590,0x758E,
+ 0xA002,0x758D,0x758C,0xA004,0xA002,0x758A,0x7589,0xA000,0x7588,0xA6E8,0xA320,0xA190,0xA0C8,0xA042,0xA001,0xA001,
+ 0xA020,0xA010,0xA008,0xA004,0xA002,0x7587,0x7585,0xA002,0x7584,0x7582,0xA004,0xA002,0x7581,0x7580,0xA002,0x757E,
+ 0x757D,0xA008,0xA004,0xA002,0x757C,0x757B,0xA002,0x757A,0x7577,0xA004,0xA002,0x7576,0x7575,0xA002,0x7573,0x7571,
+ 0xA010,0xA008,0xA004,0xA002,0x7570,0x756F,0xA002,0x756E,0x756D,0xA004,0xA002,0x756C,0x756B,0xA002,0x7569,0x7568,
+ 0xA008,0xA004,0xA002,0x7567,0x7564,0xA002,0x7563,0x7562,0xA004,0xA002,0x7561,0x7560,0xA002,0x755F,0x755E,0xA07D,
+ 0xA03D,0xA01D,0xA00D,0xA005,0xA002,0x755D,0xA001,0x7558,0xA004,0xA002,0x7557,0x7556,0xA002,0x7555,0x7553,0xA008,
+ 0xA004,0xA002,0x7552,0x7551,0xA002,0x7550,0x754D,0xA004,0xA002,0x754A,0x7549,0xA002,0x7547,0x7546,0xA010,0xA008,
+ 0xA004,0xA002,0x7544,0x7543,0xA002,0x7542,0x7541,0xA004,0xA002,0x753F,0x753D,0xA002,0x753C,0x7539,0xA008,0xA004,
+ 0xA002,0x7536,0x7534,0xA002,0x752E,0x752A,0xA004,0xA002,0x7527,0x7526,0xA002,0x7524,0x7523,0xA020,0xA010,0xA008,
+ 0xA004,0xA002,0x7522,0x7521,0xA002,0x7520,0x751E,0xA004,0xA002,0x751D,0x751B,0xA002,0x7517,0x7516,0xA008,0xA004,
+ 0xA002,0x7515,0x7514,0xA002,0x7512,0x7510,0xA004,0xA002,0x750E,0x750C,0xA002,0x750B,0x750A,0xA010,0xA008,0xA004,
+ 0xA002,0x7509,0x7508,0xA002,0x7507,0x7506,0xA004,0xA002,0x7505,0x7503,0xA002,0x7502,0x7501,0xA008,0xA004,0xA002,
+ 0x7500,0x74FE,0xA002,0x74FD,0x74FC,0xA004,0xA002,0x74FB,0x74FA,0xA002,0x74F9,0x74F8,0xA000,0xA000,0xA000,0xA000,
+ 0xA000,0xA002,0x74F5,0x74F3,0xA03F,0xA001,0xA001,0xA01D,0xA00D,0xA005,0xA001,0xA002,0x74F2,0x74F1,0xA004,0xA002,
+ 0x74F0,0x74ED,0xA002,0x74EC,0x74EB,0xA008,0xA004,0xA002,0x74EA,0x74E9,0xA002,0x74E8,0x74E7,0xA004,0xA002,0x74E5,
+ 0x74E1,0xA002,0x74DF,0x74DD,0xA010,0xA008,0xA004,0xA002,0x74DB,0x74DA,0xA002,0x74D9,0x74D8,0xA004,0xA002,0x74D7,
+ 0x74D6,0xA002,0x74D5,0x74D4,0xA008,0xA004,0xA002,0x74D3,0x74D1,0xA002,0x74D0,0x74CF,0xA004,0xA002,0x74CE,0x74CD,
+ 0xA002,0x74CC,0x74CB,0xA07D,0xA03D,0xA01D,0xA00D,0xA006,0xA002,0x74CA,0xA002,0x74C9,0x74C8,0xA003,0xA001,0x74C7,
+ 0xA002,0x74C6,0x74C5,0xA008,0xA004,0xA002,0x74C4,0x74C3,0xA002,0x74C2,0x74C1,0xA004,0xA002,0x74C0,0x74BF,0xA002,
+ 0x74BE,0x74BD,0xA010,0xA008,0xA004,0xA002,0x74BC,0x74BB,0xA002,0x74B9,0x74B8,0xA004,0xA002,0x74B7,0x74B6,0xA002,
+ 0x74B5,0x74B4,0xA008,0xA004,0xA002,0x74B3,0x74B2,0xA002,0x74B1,0x74B0,0xA004,0xA002,0x74AF,0x74AE,0xA002,0x74AD,
+ 0x74AC,0xA020,0xA010,0xA008,0xA004,0xA002,0x74AB,0x74AA,0xA002,0x74A6,0x74A5,0xA004,0xA002,0x74A4,0x74A3,0xA002,
+ 0x74A2,0x74A1,0xA008,0xA004,0xA002,0x74A0,0x749F,0xA002,0x749D,0x749B,0xA004,0xA002,0x749A,0x7499,0xA002,0x7498,
+ 0x7497,0xA010,0xA008,0xA004,0xA002,0x7496,0x7495,0xA002,0x7494,0x7493,0xA004,0xA002,0x7492,0x7491,0xA002,0x748F,
+ 0x748D,0xA008,0xA004,0xA002,0x748C,0x748A,0xA002,0x7489,0x7488,0xA004,0xA002,0x7486,0x7485,0xA002,0x7484,0x7482,
+ 0xA000,0xA000,0xA000,0xA000,0xA004,0xA002,0x747F,0x747D,0xA002,0x747C,0x747B,0xA0C8,0xA03B,0xA001,0xA001,0xA019,
+ 0xA009,0xA001,0xA004,0xA002,0x747A,0x7479,0xA002,0x7478,0x7475,0xA008,0xA004,0xA002,0x7474,0x7473,0xA002,0x7472,
+ 0x7471,0xA004,0xA002,0x746F,0x746E,0xA002,0x746C,0x746B,0xA010,0xA008,0xA004,0xA002,0x746A,0x7469,0xA002,0x7468,
+ 0x7467,0xA004,0xA002,0x7466,0x7465,0xA002,0x7464,0x7463,0xA008,0xA004,0xA002,0x7462,0x7461,0xA002,0x7460,0x745D,
+ 0xA004,0xA002,0x7458,0x7456,0xA002,0x7454,0x7453,0xA07D,0xA03D,0xA01D,0xA00D,0xA006,0xA002,0x7452,0xA002,0x7451,
+ 0x7450,0xA004,0xA002,0x744F,0x744E,0xA001,0x744D,0xA008,0xA004,0xA002,0x744C,0x744B,0xA002,0x744A,0x7449,0xA004,
+ 0xA002,0x7448,0x7447,0xA002,0x7446,0x7445,0xA010,0xA008,0xA004,0xA002,0x7444,0x7443,0xA002,0x7442,0x7440,0xA004,
+ 0xA002,0x743F,0x743E,0xA002,0x743D,0x743B,0xA008,0xA004,0xA002,0x743A,0x7439,0xA002,0x7438,0x7437,0xA004,0xA002,
+ 0x7432,0x7431,0xA002,0x742F,0x742D,0xA020,0xA010,0xA008,0xA004,0xA002,0x742B,0x7429,0xA002,0x7427,0x7424,0xA004,
+ 0xA002,0x7423,0x7421,0xA002,0x7420,0x741F,0xA008,0xA004,0xA002,0x741E,0x741D,0xA002,0x741C,0x7419,0xA004,0xA002,
+ 0x7418,0x7417,0xA002,0x7416,0x7415,0xA010,0xA008,0xA004,0xA002,0x7414,0x7413,0xA002,0x7412,0x7411,0xA004,0xA002,
+ 0x740E,0x740D,0xA002,0x740C,0x740B,0xA008,0xA004,0xA002,0x7408,0x7407,0xA002,0x7404,0x7402,0xA004,0xA002,0x7401,
+ 0x7400,0xA002,0x73FF,0x73FE,0xA000,0xA000,0xA000,0xA008,0xA004,0xA002,0x73FD,0x73FC,0xA002,0x73FB,0x73FA,0xA000,
+ 0xA002,0x73F9,0x73F8,0xA038,0xA001,0xA001,0xA016,0xA006,0xA001,0xA001,0xA002,0x73F7,0x73F6,0xA008,0xA004,0xA002,
+ 0x73F5,0x73F4,0xA002,0x73F3,0x73F1,0xA004,0xA002,0x73F0,0x73EF,0xA002,0x73EE,0x73EC,0xA010,0xA008,0xA004,0xA002,
+ 0x73EB,0x73EA,0xA002,0x73E8,0x73E6,0xA004,0xA002,0x73E4,0x73E3,0xA002,0x73E2,0x73E1,0xA008,0xA004,0xA002,0x73DF,
+ 0x73DD,0xA002,0x73DC,0x73DB,0xA004,0xA002,0x73DA,0x73D8,0xA002,0x73D7,0x73D6,0xA07D,0xA03D,0xA01D,0xA00E,0xA006,
+ 0xA002,0x73D5,0xA002,0x73D4,0x73D3,0xA004,0xA002,0x73D2,0x73CE,0xA002,0x73CC,0x73CB,0xA007,0xA003,0xA001,0x73C7,
+ 0xA002,0x73C6,0x73C5,0xA004,0xA002,0x73C4,0x73C3,0xA002,0x73C1,0x73BF,0xA010,0xA008,0xA004,0xA002,0x73BE,0x73BD,
+ 0xA002,0x73BC,0x73B9,0xA004,0xA002,0x73B8,0x73B6,0xA002,0x73B5,0x73B4,0xA008,0xA004,0xA002,0x73B1,0x73AD,0xA002,
+ 0x73AC,0x73AA,0xA004,0xA002,0x73A8,0x73A7,0xA002,0x73A6,0x73A5,0xA020,0xA010,0xA008,0xA004,0xA002,0x73A4,0x73A3,
+ 0xA002,0x73A1,0x73A0,0xA004,0xA002,0x739E,0x739D,0xA002,0x739C,0x739A,0xA008,0xA004,0xA002,0x7399,0x7398,0xA002,
+ 0x7397,0x7395,0xA004,0xA002,0x7394,0x7393,0xA002,0x7392,0x7390,0xA010,0xA008,0xA004,0xA002,0x738F,0x738D,0xA002,
+ 0x738C,0x738A,0xA004,0xA002,0x7388,0x7386,0xA002,0x7385,0x7383,0xA008,0xA004,0xA002,0x7382,0x7381,0xA002,0x7380,
+ 0x737F,0xA004,0xA002,0x737D,0x737C,0xA002,0x737B,0x737A,0xA000,0xA000,0xA000,0xA008,0xA004,0xA002,0x7379,0x7378,
+ 0xA002,0x7377,0x7376,0xA004,0xA002,0x7375,0x7374,0xA002,0x7373,0x7372,0xA1FB,0xA0C9,0xA032,0xA001,0xA001,0xA010,
+ 0xA001,0xA007,0xA003,0xA001,0x7371,0xA002,0x7370,0x736E,0xA004,0xA002,0x736B,0x736A,0xA002,0x7369,0x7368,0xA010,
+ 0xA008,0xA004,0xA002,0x7367,0x7366,0xA002,0x7365,0x7364,0xA004,0xA002,0x7363,0x7362,0xA002,0x7361,0x735F,0xA008,
+ 0xA004,0xA002,0x735E,0x735D,0xA002,0x735C,0x735B,0xA004,0xA002,0x735A,0x7359,0xA002,0x7358,0x7356,0xA07D,0xA03D,
+ 0xA01D,0xA00E,0xA006,0xA002,0x7355,0xA002,0x7354,0x7353,0xA004,0xA002,0x7351,0x734F,0xA002,0x734E,0x734C,0xA007,
+ 0xA004,0xA002,0x734B,0x734A,0xA000,0x7349,0xA004,0xA002,0x7348,0x7347,0xA002,0x7346,0x7345,0xA010,0xA008,0xA004,
+ 0xA002,0x7344,0x7343,0xA002,0x7342,0x7341,0xA004,0xA002,0x7340,0x733D,0xA002,0x733C,0x733B,0xA008,0xA004,0xA002,
+ 0x733A,0x7336,0xA002,0x7335,0x7333,0xA004,0xA002,0x7332,0x7330,0xA002,0x732F,0x732D,0xA020,0xA010,0xA008,0xA004,
+ 0xA002,0x7328,0x7327,0xA002,0x7326,0x7324,0xA004,0xA002,0x7323,0x7320,0xA002,0x731F,0x731A,0xA008,0xA004,0xA002,
+ 0x7319,0x7318,0xA002,0x7314,0x7312,0xA004,0xA002,0x7311,0x7310,0xA002,0x730F,0x730D,0xA010,0xA008,0xA004,0xA002,
+ 0x730C,0x730B,0xA002,0x7309,0x7308,0xA004,0xA002,0x7307,0x7306,0xA002,0x7305,0x7304,0xA008,0xA004,0xA002,0x7302,
+ 0x7300,0xA002,0x72FF,0x72FE,0xA004,0xA002,0x72FD,0x72F9,0xA002,0x72F6,0x72F5,0xA000,0xA000,0xA010,0xA008,0xA004,
+ 0xA002,0x72EB,0x72EA,0xA002,0x72E7,0x72E6,0xA004,0xA002,0x72E5,0x72E4,0xA002,0x72E3,0x72E2,0xA000,0xA004,0xA002,
+ 0x72DF,0x72DD,0xA000,0x72DC,0xA0A2,0xA04B,0xA00B,0xA001,0xA001,0xA001,0xA004,0xA002,0x254B,0x254A,0xA002,0x2549,
+ 0x2548,0xA020,0xA010,0xA008,0xA004,0xA002,0x2547,0x2546,0xA002,0x2545,0x2544,0xA004,0xA002,0x2543,0x2542,0xA002,
+ 0x2541,0x2540,0xA008,0xA004,0xA002,0x253F,0x253E,0xA002,0x253D,0x253C,0xA004,0xA002,0x253B,0x253A,0xA002,0x2539,
+ 0x2538,0xA010,0xA008,0xA004,0xA002,0x2537,0x2536,0xA002,0x2535,0x2534,0xA004,0xA002,0x2533,0x2532,0xA002,0x2531,
+ 0x2530,0xA008,0xA004,0xA002,0x252F,0x252E,0xA002,0x252D,0x252C,0xA004,0xA002,0x252B,0x252A,0xA002,0x2529,0x2528,
+ 0xA040,0xA020,0xA010,0xA008,0xA004,0xA002,0x2527,0x2526,0xA002,0x2525,0x2524,0xA004,0xA002,0x2523,0x2522,0xA002,
+ 0x2521,0x2520,0xA008,0xA004,0xA002,0x251F,0x251E,0xA002,0x251D,0x251C,0xA004,0xA002,0x251B,0x251A,0xA002,0x2519,
+ 0x2518,0xA010,0xA008,0xA004,0xA002,0x2517,0x2516,0xA002,0x2515,0x2514,0xA004,0xA002,0x2513,0x2512,0xA002,0x2511,
+ 0x2510,0xA008,0xA004,0xA002,0x250F,0x250E,0xA002,0x250D,0x250C,0xA004,0xA002,0x250B,0x250A,0xA002,0x2509,0x2508,
+ 0xA011,0xA000,0xA008,0xA004,0xA002,0x2507,0x2506,0xA002,0x2505,0x2504,0xA004,0xA002,0x2503,0x2502,0xA002,0x2501,
+ 0x2500,0xA000,0xA001,0xA000,0xA001,0x3007,0xA072,0xA038,0xA018,0xA009,0xA001,0xA004,0xA002,0xFE6B,0xFE6A,0xA002,
+ 0xFE69,0xFE68,0xA008,0xA004,0xA002,0xFE66,0xFE65,0xA002,0xFE64,0xFE63,0xA003,0xA000,0xFE62,0xA002,0xFE61,0xFE60,
+ 0xA010,0xA008,0xA004,0xA002,0xFE5F,0xFE5E,0xA002,0xFE5D,0xFE5C,0xA004,0xA002,0xFE5B,0xFE5A,0xA002,0xFE59,0xFE57,
+ 0xA008,0xA004,0xA002,0xFE56,0xFE55,0xA002,0xFE54,0xFE52,0xA004,0xA002,0xFE51,0xFE50,0xA002,0xFE4F,0xFE4E,0xA01C,
+ 0xA010,0xA008,0xA004,0xA002,0xFE4D,0xFE4C,0xA002,0xFE4B,0xFE4A,0xA004,0xA002,0xFE49,0x309E,0xA002,0x309D,0x3006,
+ 0xA008,0xA004,0xA002,0x30FE,0x30FD,0xA002,0x309C,0x309B,0xA000,0xA000,0x30FC,0xA00E,0xA007,0xA003,0xA000,0x2010,
+ 0xA002,0x3231,0x2121,0xA003,0xA001,0xFFE4,0xA002,0xFFE2,0xFE30,0xA008,0xA004,0xA002,0x33D5,0x33D2,0xA002,0x33D1,
+ 0x33CE,0xA004,0xA002,0x33C4,0x33A1,0xA002,0x339E,0x339D,0xA000,0xA000,0xA010,0xA008,0xA004,0xA002,0x339C,0x338F,
+ 0xA002,0x338E,0x32A3,0xA004,0xA002,0x3029,0x3028,0xA002,0x3027,0x3026,0xA008,0xA004,0xA002,0x3025,0x3024,0xA002,
+ 0x3023,0x3022,0xA000,0xA000,0x3021,0xA13E,0xA0A0,0xA03A,0xA001,0xA019,0xA009,0xA001,0xA004,0xA002,0x3129,0x3128,
+ 0xA002,0x3127,0x3126,0xA008,0xA004,0xA002,0x3125,0x3124,0xA002,0x3123,0x3122,0xA004,0xA002,0x3121,0x3120,0xA002,
+ 0x311F,0x311E,0xA010,0xA008,0xA004,0xA002,0x311D,0x311C,0xA002,0x311B,0x311A,0xA004,0xA002,0x3119,0x3118,0xA002,
+ 0x3117,0x3116,0xA008,0xA004,0xA002,0x3115,0x3114,0xA002,0x3113,0x3112,0xA004,0xA002,0x3111,0x3110,0xA002,0x310F,
+ 0x310E,0xA039,0xA01A,0xA010,0xA008,0xA004,0xA002,0x310D,0x310C,0xA002,0x310B,0x310A,0xA004,0xA002,0x3109,0x3108,
+ 0xA002,0x3107,0x3106,0xA004,0xA000,0xA000,0x3105,0xA003,0xA001,0x0261,0xA001,0x0148,0xA00F,0xA007,0xA003,0xA000,
+ 0x0144,0xA002,0x0251,0x00EA,0xA004,0xA002,0x00FC,0x01DC,0xA002,0x01DA,0x01D8,0xA008,0xA004,0xA002,0x01D6,0x00F9,
+ 0xA002,0x01D4,0x00FA,0xA004,0xA002,0x016B,0x00F2,0xA002,0x01D2,0x00F3,0xA01C,0xA010,0xA008,0xA004,0xA002,0x014D,
+ 0x00EC,0xA002,0x01D0,0x00ED,0xA004,0xA002,0x012B,0x00E8,0xA002,0x011B,0x00E9,0xA008,0xA004,0xA002,0x0113,0x00E0,
+ 0xA002,0x01CE,0x00E1,0xA000,0xA000,0x0101,0xA001,0xA008,0xA004,0xA002,0x301E,0x301D,0xA002,0x3012,0x2295,0xA004,
+ 0xA002,0x2609,0x25E5,0xA002,0x25E4,0x25E3,0xA07D,0xA03D,0xA01D,0xA00E,0xA006,0xA002,0x25E2,0xA002,0x25BD,0x25BC,
+ 0xA004,0xA002,0x2595,0x2594,0xA002,0x2593,0x258F,0xA008,0xA004,0xA002,0x258E,0x258D,0xA002,0x258C,0x258B,0xA004,
+ 0xA002,0x258A,0x2589,0xA000,0x2588,0xA010,0xA008,0xA004,0xA002,0x2587,0x2586,0xA002,0x2585,0x2584,0xA004,0xA002,
+ 0x2583,0x2582,0xA002,0x2581,0x2573,0xA008,0xA004,0xA002,0x2572,0x2571,0xA002,0x2570,0x256F,0xA004,0xA002,0x256E,
+ 0x256D,0xA002,0x256C,0x256B,0xA020,0xA010,0xA008,0xA004,0xA002,0x256A,0x2569,0xA002,0x2568,0x2567,0xA004,0xA002,
+ 0x2566,0x2565,0xA002,0x2564,0x2563,0xA008,0xA004,0xA002,0x2562,0x2561,0xA002,0x2560,0x255F,0xA004,0xA002,0x255E,
+ 0x255D,0xA002,0x255C,0x255B,0xA010,0xA008,0xA004,0xA002,0x255A,0x2559,0xA002,0x2558,0x2557,0xA004,0xA002,0x2556,
+ 0x2555,0xA002,0x2554,0x2553,0xA008,0xA004,0xA002,0x2552,0x2551,0xA002,0x2550,0x22BF,0xA004,0xA002,0x2267,0x2266,
+ 0xA002,0x2252,0x2223,0xA000,0xA000,0xA010,0xA008,0xA004,0xA002,0x221F,0x2215,0xA002,0x2199,0x2198,0xA004,0xA002,
+ 0x2197,0x2196,0xA002,0x2109,0x2105,0xA008,0xA004,0xA002,0x2035,0x2025,0xA002,0x2015,0x2013,0xA004,0xA002,0x02D9,
+ 0x02CB,0xA000,0x02CA,0xA000,0xA047,0xA008,0xA001,0xA001,0xA001,0xA001,0xA002,0x044F,0x044E,0xA020,0xA010,0xA008,
+ 0xA004,0xA002,0x044D,0x044C,0xA002,0x044B,0x044A,0xA004,0xA002,0x0449,0x0448,0xA002,0x0447,0x0446,0xA008,0xA004,
+ 0xA002,0x0445,0x0444,0xA002,0x0443,0x0442,0xA004,0xA002,0x0441,0x0440,0xA002,0x043F,0x043E,0xA010,0xA008,0xA004,
+ 0xA002,0x043D,0x043C,0xA002,0x043B,0x043A,0xA004,0xA002,0x0439,0x0438,0xA002,0x0437,0x0436,0xA008,0xA004,0xA002,
+ 0x0451,0x0435,0xA002,0x0434,0x0433,0xA004,0xA002,0x0432,0x0431,0xA000,0x0430,0xA027,0xA007,0xA001,0xA001,0xA001,
+ 0xA002,0x042F,0x042E,0xA010,0xA008,0xA004,0xA002,0x042D,0x042C,0xA002,0x042B,0x042A,0xA004,0xA002,0x0429,0x0428,
+ 0xA002,0x0427,0x0426,0xA008,0xA004,0xA002,0x0425,0x0424,0xA002,0x0423,0x0422,0xA004,0xA002,0x0421,0x0420,0xA002,
+ 0x041F,0x041E,0xA000,0xA010,0xA008,0xA004,0xA002,0x041D,0x041C,0xA002,0x041B,0x041A,0xA004,0xA002,0x0419,0x0418,
+ 0xA002,0x0417,0x0416,0xA008,0xA004,0xA002,0x0401,0x0415,0xA002,0x0414,0x0413,0xA004,0xA002,0x0412,0x0411,0xA000,
+ 0x0410,0xA2B5,0xA148,0xA095,0xA000,0xA03C,0xA00A,0xA001,0xA001,0xA001,0xA004,0xA002,0xFE34,0xFE33,0xA001,0xFE31,
+ 0xA01D,0xA00D,0xA008,0xA004,0xA002,0xFE38,0xFE37,0xA002,0xFE3C,0xFE3B,0xA001,0xA002,0xFE44,0xFE43,0xA008,0xA004,
+ 0xA002,0xFE42,0xFE41,0xA002,0xFE3E,0xFE3D,0xA004,0xA002,0xFE40,0xFE3F,0xA002,0xFE3A,0xFE39,0xA006,0xA000,0xA000,
+ 0xA002,0xFE36,0xFE35,0xA007,0xA003,0xA001,0x03C9,0xA002,0x03C8,0x03C7,0xA004,0xA002,0x03C6,0x03C5,0xA002,0x03C4,
+ 0x03C3,0xA031,0xA01E,0xA00E,0xA006,0xA002,0x03C1,0xA002,0x03C0,0x03BF,0xA004,0xA002,0x03BE,0x03BD,0xA002,0x03BC,
+ 0x03BB,0xA008,0xA004,0xA002,0x03BA,0x03B9,0xA002,0x03B8,0x03B7,0xA004,0xA002,0x03B6,0x03B5,0xA002,0x03B4,0x03B3,
+ 0xA006,0xA000,0xA000,0xA002,0x03B2,0x03B1,0xA005,0xA001,0xA002,0x03A9,0x03A8,0xA004,0xA002,0x03A7,0x03A6,0xA002,
+ 0x03A5,0x03A4,0xA020,0xA010,0xA008,0xA004,0xA002,0x03A3,0x03A1,0xA002,0x03A0,0x039F,0xA004,0xA002,0x039E,0x039D,
+ 0xA002,0x039C,0x039B,0xA008,0xA004,0xA002,0x039A,0x0399,0xA002,0x0398,0x0397,0xA004,0xA002,0x0396,0x0395,0xA002,
+ 0x0394,0x0393,0xA000,0xA000,0xA000,0xA002,0x0392,0x0391,0xA000,0xA048,0xA008,0xA001,0xA001,0xA001,0xA001,0xA002,
+ 0x30F6,0x30F5,0xA020,0xA010,0xA008,0xA004,0xA002,0x30F4,0x30F3,0xA002,0x30F2,0x30F1,0xA004,0xA002,0x30F0,0x30EF,
+ 0xA002,0x30EE,0x30ED,0xA008,0xA004,0xA002,0x30EC,0x30EB,0xA002,0x30EA,0x30E9,0xA004,0xA002,0x30E8,0x30E7,0xA002,
+ 0x30E6,0x30E5,0xA010,0xA008,0xA004,0xA002,0x30E4,0x30E3,0xA002,0x30E2,0x30E1,0xA004,0xA002,0x30E0,0x30DF,0xA002,
+ 0x30DE,0x30DD,0xA008,0xA004,0xA002,0x30DC,0x30DB,0xA002,0x30DA,0x30D9,0xA004,0xA002,0x30D8,0x30D7,0xA002,0x30D6,
+ 0x30D5,0xA040,0xA020,0xA010,0xA008,0xA004,0xA002,0x30D4,0x30D3,0xA002,0x30D2,0x30D1,0xA004,0xA002,0x30D0,0x30CF,
+ 0xA002,0x30CE,0x30CD,0xA008,0xA004,0xA002,0x30CC,0x30CB,0xA002,0x30CA,0x30C9,0xA004,0xA002,0x30C8,0x30C7,0xA002,
+ 0x30C6,0x30C5,0xA010,0xA008,0xA004,0xA002,0x30C4,0x30C3,0xA002,0x30C2,0x30C1,0xA004,0xA002,0x30C0,0x30BF,0xA002,
+ 0x30BE,0x30BD,0xA008,0xA004,0xA002,0x30BC,0x30BB,0xA002,0x30BA,0x30B9,0xA004,0xA002,0x30B8,0x30B7,0xA002,0x30B6,
+ 0x30B5,0xA020,0xA010,0xA008,0xA004,0xA002,0x30B4,0x30B3,0xA002,0x30B2,0x30B1,0xA004,0xA002,0x30B0,0x30AF,0xA002,
+ 0x30AE,0x30AD,0xA008,0xA004,0xA002,0x30AC,0x30AB,0xA002,0x30AA,0x30A9,0xA004,0xA002,0x30A8,0x30A7,0xA002,0x30A6,
+ 0x30A5,0xA000,0xA000,0xA004,0xA002,0x30A4,0x30A3,0xA002,0x30A2,0x30A1,0xA0AC,0xA000,0xA03D,0xA001,0xA01C,0xA00C,
+ 0xA004,0xA001,0xA001,0x3093,0xA004,0xA002,0x3092,0x3091,0xA002,0x3090,0x308F,0xA008,0xA004,0xA002,0x308E,0x308D,
+ 0xA002,0x308C,0x308B,0xA004,0xA002,0x308A,0x3089,0xA002,0x3088,0x3087,0xA010,0xA008,0xA004,0xA002,0x3086,0x3085,
+ 0xA002,0x3084,0x3083,0xA004,0xA002,0x3082,0x3081,0xA002,0x3080,0x307F,0xA008,0xA004,0xA002,0x307E,0x307D,0xA002,
+ 0x307C,0x307B,0xA004,0xA002,0x307A,0x3079,0xA002,0x3078,0x3077,0xA040,0xA020,0xA010,0xA008,0xA004,0xA002,0x3076,
+ 0x3075,0xA002,0x3074,0x3073,0xA004,0xA002,0x3072,0x3071,0xA002,0x3070,0x306F,0xA008,0xA004,0xA002,0x306E,0x306D,
+ 0xA002,0x306C,0x306B,0xA004,0xA002,0x306A,0x3069,0xA002,0x3068,0x3067,0xA010,0xA008,0xA004,0xA002,0x3066,0x3065,
+ 0xA002,0x3064,0x3063,0xA004,0xA002,0x3062,0x3061,0xA002,0x3060,0x305F,0xA008,0xA004,0xA002,0x305E,0x305D,0xA002,
+ 0x305C,0x305B,0xA004,0xA002,0x305A,0x3059,0xA002,0x3058,0x3057,0xA020,0xA010,0xA008,0xA004,0xA002,0x3056,0x3055,
+ 0xA002,0x3054,0x3053,0xA004,0xA002,0x3052,0x3051,0xA002,0x3050,0x304F,0xA008,0xA004,0xA002,0x304E,0x304D,0xA002,
+ 0x304C,0x304B,0xA004,0xA002,0x304A,0x3049,0xA002,0x3048,0x3047,0xA000,0xA008,0xA004,0xA002,0x3046,0x3045,0xA002,
+ 0x3044,0x3043,0xA000,0xA002,0x3042,0x3041,0xA000,0xA04F,0xA00F,0xA001,0xA001,0xA005,0xA001,0xA002,0xFFE3,0xFF5D,
+ 0xA004,0xA002,0xFF5C,0xFF5B,0xA002,0xFF5A,0xFF59,0xA020,0xA010,0xA008,0xA004,0xA002,0xFF58,0xFF57,0xA002,0xFF56,
+ 0xFF55,0xA004,0xA002,0xFF54,0xFF53,0xA002,0xFF52,0xFF51,0xA008,0xA004,0xA002,0xFF50,0xFF4F,0xA002,0xFF4E,0xFF4D,
+ 0xA004,0xA002,0xFF4C,0xFF4B,0xA002,0xFF4A,0xFF49,0xA010,0xA008,0xA004,0xA002,0xFF48,0xFF47,0xA002,0xFF46,0xFF45,
+ 0xA004,0xA002,0xFF44,0xFF43,0xA002,0xFF42,0xFF41,0xA008,0xA004,0xA002,0xFF40,0xFF3F,0xA002,0xFF3E,0xFF3D,0xA004,
+ 0xA002,0xFF3C,0xFF3B,0xA002,0xFF3A,0xFF39,0xA040,0xA020,0xA010,0xA008,0xA004,0xA002,0xFF38,0xFF37,0xA002,0xFF36,
+ 0xFF35,0xA004,0xA002,0xFF34,0xFF33,0xA002,0xFF32,0xFF31,0xA008,0xA004,0xA002,0xFF30,0xFF2F,0xA002,0xFF2E,0xFF2D,
+ 0xA004,0xA002,0xFF2C,0xFF2B,0xA002,0xFF2A,0xFF29,0xA010,0xA008,0xA004,0xA002,0xFF28,0xFF27,0xA002,0xFF26,0xFF25,
+ 0xA004,0xA002,0xFF24,0xFF23,0xA002,0xFF22,0xFF21,0xA008,0xA004,0xA002,0xFF20,0xFF1F,0xA002,0xFF1E,0xFF1D,0xA004,
+ 0xA002,0xFF1C,0xFF1B,0xA002,0xFF1A,0xFF19,0xA020,0xA010,0xA008,0xA004,0xA002,0xFF18,0xFF17,0xA002,0xFF16,0xFF15,
+ 0xA004,0xA002,0xFF14,0xFF13,0xA002,0xFF12,0xFF11,0xA008,0xA004,0xA002,0xFF10,0xFF0F,0xA002,0xFF0E,0xFF0D,0xA004,
+ 0xA002,0xFF0C,0xFF0B,0xA002,0xFF0A,0xFF09,0xA000,0xA008,0xA004,0xA002,0xFF08,0xFF07,0xA002,0xFF06,0xFF05,0xA004,
+ 0xA002,0xFFE5,0xFF03,0xA002,0xFF02,0xFF01,0xA171,0xA0AF,0xA000,0xA042,0xA008,0xA001,0xA001,0xA001,0xA001,0xA002,
+ 0x216B,0x216A,0xA01D,0xA010,0xA008,0xA004,0xA002,0x2169,0x2168,0xA002,0x2167,0x2166,0xA004,0xA002,0x2165,0x2164,
+ 0xA002,0x2163,0x2162,0xA005,0xA000,0xA002,0x2161,0x2160,0xA004,0xA002,0x3229,0x3228,0xA002,0x3227,0x3226,0xA00D,
+ 0xA008,0xA004,0xA002,0x3225,0x3224,0xA002,0x3223,0x3222,0xA000,0xA002,0x3221,0x3220,0xA008,0xA004,0xA002,0x2469,
+ 0x2468,0xA002,0x2467,0x2466,0xA004,0xA002,0x2465,0x2464,0xA002,0x2463,0x2462,0xA040,0xA020,0xA010,0xA008,0xA004,
+ 0xA002,0x2461,0x2460,0xA002,0x2487,0x2486,0xA004,0xA002,0x2485,0x2484,0xA002,0x2483,0x2482,0xA008,0xA004,0xA002,
+ 0x2481,0x2480,0xA002,0x247F,0x247E,0xA004,0xA002,0x247D,0x247C,0xA002,0x247B,0x247A,0xA010,0xA008,0xA004,0xA002,
+ 0x2479,0x2478,0xA002,0x2477,0x2476,0xA004,0xA002,0x2475,0x2474,0xA002,0x249B,0x249A,0xA008,0xA004,0xA002,0x2499,
+ 0x2498,0xA002,0x2497,0x2496,0xA004,0xA002,0x2495,0x2494,0xA002,0x2493,0x2492,0xA016,0xA010,0xA008,0xA004,0xA002,
+ 0x2491,0x2490,0xA002,0x248F,0x248E,0xA004,0xA002,0x248D,0x248C,0xA002,0x248B,0x248A,0xA000,0xA000,0xA002,0x2489,
+ 0x2488,0xA010,0xA008,0xA004,0xA002,0x2179,0x2178,0xA002,0x2177,0x2176,0xA004,0xA002,0x2175,0x2174,0xA002,0x2173,
+ 0x2172,0xA000,0xA000,0xA002,0x2171,0x2170,0xA000,0xA048,0xA008,0xA001,0xA001,0xA001,0xA001,0xA002,0x3013,0x2193,
+ 0xA020,0xA010,0xA008,0xA004,0xA002,0x2191,0x2190,0xA002,0x2192,0x203B,0xA004,0xA002,0x25B2,0x25B3,0xA002,0x25A0,
+ 0x25A1,0xA008,0xA004,0xA002,0x25C6,0x25C7,0xA002,0x25CE,0x25CF,0xA004,0xA002,0x25CB,0x2605,0xA002,0x2606,0x2116,
+ 0xA010,0xA008,0xA004,0xA002,0x00A7,0x2030,0xA002,0xFFE1,0xFFE0,0xA004,0xA002,0x00A4,0xFF04,0xA002,0x2103,0x2033,
+ 0xA008,0xA004,0xA002,0x2032,0x00B0,0xA002,0x2640,0x2642,0xA004,0xA002,0x2234,0x2235,0xA002,0x221E,0x2265,0xA040,
+ 0xA020,0xA010,0xA008,0xA004,0xA002,0x2264,0x226F,0xA002,0x226E,0x2260,0xA004,0xA002,0x221D,0x223D,0xA002,0x2248,
+ 0x224C,0xA008,0xA004,0xA002,0x2261,0x222E,0xA002,0x222B,0x2299,0xA004,0xA002,0x2312,0x2220,0xA002,0x2225,0x22A5,
+ 0xA010,0xA008,0xA004,0xA002,0x221A,0x2237,0xA002,0x2208,0x2229,0xA004,0xA002,0x222A,0x220F,0xA002,0x2211,0x2228,
+ 0xA008,0xA004,0xA002,0x2227,0x2236,0xA002,0x00F7,0x00D7,0xA004,0xA002,0x00B1,0x3011,0xA002,0x3010,0x3017,0xA020,
+ 0xA010,0xA008,0xA004,0xA002,0x3016,0x300F,0xA002,0x300E,0x300D,0xA004,0xA002,0x300C,0x300B,0xA002,0x300A,0x3009,
+ 0xA008,0xA004,0xA002,0x3008,0x3015,0xA002,0x3014,0x201D,0xA004,0xA002,0x201C,0x2019,0xA002,0x2018,0x2026,0xA010,
+ 0xA008,0xA004,0xA002,0x2016,0xFF5E,0xA002,0x2014,0x3005,0xA004,0xA002,0x3003,0x00A8,0xA002,0x02C7,0x02C9,0xA000,
+ 0xA004,0xA002,0x00B7,0x3002,0xA002,0x3001,0x3000,0xA17F,0xA0C1,0xA041,0xA001,0xA020,0xA010,0xA008,0xA004,0xA002,
+ 0x72DB,0x72DA,0xA002,0x72D8,0x72D6,0xA004,0xA002,0x72D5,0x72D4,0xA002,0x72D3,0x72D1,0xA008,0xA004,0xA002,0x72CF,
+ 0x72CC,0xA002,0x72CB,0x72CA,0xA004,0xA002,0x72C9,0x72C7,0xA002,0x72C6,0x72C5,0xA010,0xA008,0xA004,0xA002,0x72C0,
+ 0x72BF,0xA002,0x72BE,0x72BD,0xA004,0xA002,0x72BC,0x72BB,0xA002,0x72BA,0x72B5,0xA008,0xA004,0xA002,0x72B3,0x72B2,
+ 0xA002,0x72B1,0x72AE,0xA004,0xA002,0x72AB,0x72AA,0xA002,0x72A9,0x72A8,0xA040,0xA020,0xA010,0xA008,0xA004,0xA002,
+ 0x72A7,0x72A6,0xA002,0x72A5,0x72A4,0xA004,0xA002,0x72A3,0x72A2,0xA002,0x72A1,0x72A0,0xA008,0xA004,0xA002,0x729E,
+ 0x729D,0xA002,0x729C,0x729B,0xA004,0xA002,0x729A,0x7299,0xA002,0x7298,0x7297,0xA010,0xA008,0xA004,0xA002,0x7296,
+ 0x7295,0xA002,0x7294,0x7293,0xA004,0xA002,0x7291,0x7290,0xA002,0x728E,0x728C,0xA008,0xA004,0xA002,0x7289,0x7288,
+ 0xA002,0x7287,0x7286,0xA004,0xA002,0x7285,0x7283,0xA002,0x7282,0x727D,0xA020,0xA010,0xA008,0xA004,0xA002,0x727C,
+ 0x727B,0xA002,0x7278,0x7277,0xA004,0xA002,0x7276,0x7274,0xA002,0x7273,0x7271,0xA008,0xA004,0xA002,0x7270,0x726D,
+ 0xA002,0x726C,0x726B,0xA004,0xA002,0x726A,0x7268,0xA002,0x7265,0x7264,0xA010,0xA008,0xA004,0xA002,0x7263,0x7260,
+ 0xA002,0x725E,0x725C,0xA004,0xA002,0x725A,0x7258,0xA002,0x7257,0x7255,0xA008,0xA004,0xA002,0x7254,0x7253,0xA002,
+ 0x7251,0x7250,0xA004,0xA002,0x724F,0x724E,0xA002,0x724B,0x724A,0xA07D,0xA03E,0xA01E,0xA00E,0xA006,0xA002,0x7249,
+ 0xA002,0x7246,0x7245,0xA004,0xA002,0x7244,0x7243,0xA002,0x7242,0x7241,0xA008,0xA004,0xA002,0x7240,0x723E,0xA002,
+ 0x723C,0x723A,0xA004,0xA002,0x7234,0x7233,0xA002,0x7232,0x722F,0xA010,0xA008,0xA004,0xA002,0x722E,0x722D,0xA002,
+ 0x722B,0x7229,0xA004,0xA002,0x7227,0x7226,0xA002,0x7225,0x7224,0xA008,0xA004,0xA002,0x7223,0x7222,0xA002,0x7221,
+ 0x7220,0xA004,0xA002,0x721F,0x721E,0xA002,0x721C,0x721B,0xA01F,0xA00F,0xA007,0xA003,0xA001,0x721A,0xA002,0x7219,
+ 0x7218,0xA004,0xA002,0x7217,0x7216,0xA002,0x7215,0x7214,0xA008,0xA004,0xA002,0x7213,0x7212,0xA002,0x7211,0x7210,
+ 0xA004,0xA002,0x720F,0x720E,0xA002,0x720D,0x720C,0xA010,0xA008,0xA004,0xA002,0x720B,0x720A,0xA002,0x7209,0x7208,
+ 0xA004,0xA002,0x7207,0x7205,0xA002,0x7204,0x7203,0xA008,0xA004,0xA002,0x7202,0x7201,0xA002,0x7200,0x71FF,0xA004,
+ 0xA002,0x71FE,0x71FD,0xA002,0x71FC,0x71FB,0xA000,0xA020,0xA010,0xA008,0xA004,0xA002,0x71FA,0x71F8,0xA002,0x71F7,
+ 0x71F6,0xA004,0xA002,0x71F5,0x71F4,0xA002,0x71F3,0x71F2,0xA008,0xA004,0xA002,0x71F1,0x71F0,0xA002,0x71EF,0x71ED,
+ 0xA004,0xA002,0x71EC,0x71EB,0xA002,0x71EA,0x71E9,0xA010,0xA008,0xA004,0xA002,0x71E8,0x71E6,0xA002,0x71E4,0x71E3,
+ 0xA004,0xA002,0x71E2,0x71E1,0xA002,0x71DF,0x71DE,0xA008,0xA004,0xA002,0x71DD,0x71DC,0xA002,0x71DB,0x71DA,0xA004,
+ 0xA002,0x71D9,0x71D8,0xA002,0x71D7,0x71D6,0xA0BE,0xA03E,0xA001,0xA01D,0xA00D,0xA005,0xA001,0xA002,0x71D3,0x71D2,
+ 0xA004,0xA002,0x71D1,0x71D0,0xA002,0x71CF,0x71CD,0xA008,0xA004,0xA002,0x71CC,0x71CB,0xA002,0x71CA,0x71C9,0xA004,
+ 0xA002,0x71C8,0x71C7,0xA002,0x71C6,0x71C5,0xA010,0xA008,0xA004,0xA002,0x71C4,0x71C2,0xA002,0x71C1,0x71C0,0xA004,
+ 0xA002,0x71BF,0x71BE,0xA002,0x71BD,0x71BC,0xA008,0xA004,0xA002,0x71BB,0x71BA,0xA002,0x71B8,0x71B7,0xA004,0xA002,
+ 0x71B6,0x71B4,0xA002,0x71B2,0x71B1,0xA040,0xA020,0xA010,0xA008,0xA004,0xA002,0x71B0,0x71AF,0xA002,0x71AE,0x71AD,
+ 0xA004,0xA002,0x71AB,0x71AA,0xA002,0x71A9,0x71A7,0xA008,0xA004,0xA002,0x71A6,0x71A5,0xA002,0x71A4,0x71A3,0xA004,
+ 0xA002,0x71A2,0x71A1,0xA002,0x719E,0x719D,0xA010,0xA008,0xA004,0xA002,0x719C,0x719B,0xA002,0x719A,0x7197,0xA004,
+ 0xA002,0x7196,0x7195,0xA002,0x7193,0x7192,0xA008,0xA004,0xA002,0x7191,0x7190,0xA002,0x718E,0x718D,0xA004,0xA002,
+ 0x718C,0x718B,0xA002,0x7189,0x7188,0xA020,0xA010,0xA008,0xA004,0xA002,0x7187,0x7186,0xA002,0x7185,0x7183,0xA004,
+ 0xA002,0x7182,0x7181,0xA002,0x7180,0x717F,0xA008,0xA004,0xA002,0x717E,0x717C,0xA002,0x717B,0x7179,0xA004,0xA002,
+ 0x7177,0x7176,0xA002,0x7175,0x7174,0xA010,0xA008,0xA004,0xA002,0x7171,0x7170,0xA002,0x716F,0x716D,0xA004,0xA002,
+ 0x716C,0x716B,0xA002,0x716A,0x7169,0xA008,0xA004,0xA002,0x7165,0x7163,0xA002,0x7162,0x7161,0xA004,0xA002,0x7160,
+ 0x715F,0xA002,0x715D,0x715B,0xA07D,0xA03E,0xA01E,0xA00E,0xA006,0xA002,0x715A,0xA002,0x7159,0x7158,0xA004,0xA002,
+ 0x7157,0x7156,0xA002,0x7155,0x7154,0xA008,0xA004,0xA002,0x7153,0x7152,0xA002,0x7151,0x7150,0xA004,0xA002,0x714F,
+ 0x714D,0xA002,0x714B,0x7149,0xA010,0xA008,0xA004,0xA002,0x7148,0x7147,0xA002,0x7146,0x7144,0xA004,0xA002,0x7143,
+ 0x7142,0xA002,0x7141,0x7140,0xA008,0xA004,0xA002,0x713F,0x713E,0xA002,0x713D,0x713C,0xA004,0xA002,0x713B,0x713A,
+ 0xA002,0x7139,0x7138,0xA01F,0xA00F,0xA007,0xA004,0xA002,0x7137,0x7135,0xA001,0x7134,0xA004,0xA002,0x7133,0x7132,
+ 0xA002,0x712E,0x712D,0xA008,0xA004,0xA002,0x712C,0x712B,0xA002,0x712A,0x7129,0xA004,0xA002,0x7128,0x7127,0xA002,
+ 0x7125,0x7124,0xA010,0xA008,0xA004,0xA002,0x7123,0x7122,0xA002,0x7121,0x7120,0xA004,0xA002,0x711F,0x711E,0xA002,
+ 0x711D,0x711C,0xA008,0xA004,0xA002,0x711B,0x7117,0xA002,0x7114,0x7112,0xA004,0xA002,0x7111,0x710F,0xA002,0x710E,
+ 0x710D,0xA040,0xA020,0xA010,0xA008,0xA004,0xA002,0x710C,0x710B,0xA002,0x7108,0x7107,0xA004,0xA002,0x7106,0x7105,
+ 0xA002,0x7104,0x7103,0xA008,0xA004,0xA002,0x7102,0x7101,0xA002,0x7100,0x70FF,0xA004,0xA002,0x70FE,0x70FC,0xA002,
+ 0x70FB,0x70FA,0xA010,0xA008,0xA004,0xA002,0x70F8,0x70F6,0xA002,0x70F5,0x70F4,0xA004,0xA002,0x70F3,0x70F2,0xA002,
+ 0x70F1,0x70F0,0xA008,0xA004,0xA002,0x70EE,0x70EA,0xA002,0x70E5,0x70E3,0xA004,0xA002,0x70E2,0x70E1,0xA002,0x70E0,
+ 0x70DE,0xA000,0xA000,0xA000,0xA000,0xA002,0x70DD,0x70DC,0xB826,0xAC17,0xA60F,0xA307,0xA183,0xA0B8,0xA03A,0xA001,
+ 0xA019,0xA009,0xA001,0xA004,0xA002,0x70DA,0x70D7,0xA002,0x70D6,0x70D5,0xA008,0xA004,0xA002,0x70D4,0x70D3,0xA002,
+ 0x70D2,0x70D1,0xA004,0xA002,0x70D0,0x70CF,0xA002,0x70CE,0x70CD,0xA010,0xA008,0xA004,0xA002,0x70CC,0x70CB,0xA002,
+ 0x70C9,0x70C7,0xA004,0xA002,0x70C6,0x70C5,0xA002,0x70C4,0x70BF,0xA008,0xA004,0xA002,0x70BE,0x70BA,0xA002,0x70B6,
+ 0x70B5,0xA004,0xA002,0x70B4,0x70B2,0xA002,0x70B0,0x70AA,0xA03E,0xA01E,0xA00E,0xA006,0xA002,0x70A9,0xA002,0x70A8,
+ 0x70A7,0xA004,0xA002,0x70A6,0x70A5,0xA002,0x70A4,0x70A3,0xA008,0xA004,0xA002,0x70A2,0x70A1,0xA002,0x70A0,0x709F,
+ 0xA004,0xA002,0x709E,0x709B,0xA002,0x709A,0x7098,0xA010,0xA008,0xA004,0xA002,0x7097,0x7093,0xA002,0x7091,0x7090,
+ 0xA004,0xA002,0x708F,0x708D,0xA002,0x708C,0x708B,0xA008,0xA004,0xA002,0x7088,0x7087,0xA002,0x7086,0x7084,0xA004,
+ 0xA002,0x7083,0x7082,0xA002,0x7081,0x707D,0xA020,0xA010,0xA008,0xA004,0xA002,0x707B,0x707A,0xA002,0x7079,0x7077,
+ 0xA004,0xA002,0x7074,0x7073,0xA002,0x7072,0x7071,0xA008,0xA004,0xA002,0x706E,0x706A,0xA002,0x7069,0x7068,0xA004,
+ 0xA002,0x7067,0x7066,0xA002,0x7065,0x7064,0xA010,0xA008,0xA004,0xA002,0x7063,0x7062,0xA002,0x7061,0x7060,0xA004,
+ 0xA002,0x705F,0x705D,0xA002,0x705C,0x705B,0xA008,0xA004,0xA002,0x705A,0x7059,0xA002,0x7058,0x7057,0xA004,0xA002,
+ 0x7056,0x7055,0xA002,0x7054,0x7053,0xA07D,0xA03E,0xA01E,0xA00E,0xA006,0xA002,0x7052,0xA002,0x7051,0x7050,0xA004,
+ 0xA002,0x704E,0x704D,0xA002,0x704B,0x704A,0xA008,0xA004,0xA002,0x7049,0x7048,0xA002,0x7047,0x7046,0xA004,0xA002,
+ 0x7045,0x7044,0xA002,0x7043,0x7042,0xA010,0xA008,0xA004,0xA002,0x7041,0x7040,0xA002,0x703F,0x703E,0xA004,0xA002,
+ 0x703D,0x703C,0xA002,0x703B,0x703A,0xA008,0xA004,0xA002,0x7038,0x7037,0xA002,0x7036,0x7034,0xA004,0xA002,0x7033,
+ 0x7032,0xA002,0x7031,0x7030,0xA01F,0xA00F,0xA008,0xA004,0xA002,0x702F,0x702E,0xA002,0x702D,0x702C,0xA003,0xA000,
+ 0x702B,0xA002,0x702A,0x7029,0xA008,0xA004,0xA002,0x7028,0x7027,0xA002,0x7026,0x7025,0xA004,0xA002,0x7024,0x7022,
+ 0xA002,0x7021,0x7020,0xA010,0xA008,0xA004,0xA002,0x701F,0x701E,0xA002,0x701D,0x701C,0xA004,0xA002,0x7019,0x7018,
+ 0xA002,0x7017,0x7016,0xA008,0xA004,0xA002,0x7015,0x7014,0xA002,0x7013,0x7012,0xA004,0xA002,0x7010,0x700F,0xA002,
+ 0x700E,0x700D,0xA040,0xA020,0xA010,0xA008,0xA004,0xA002,0x700C,0x700B,0xA002,0x700A,0x7009,0xA004,0xA002,0x7008,
+ 0x7007,0xA002,0x7006,0x7005,0xA008,0xA004,0xA002,0x7004,0x7003,0xA002,0x7002,0x7001,0xA004,0xA002,0x7000,0x6FFF,
+ 0xA002,0x6FFE,0x6FFD,0xA010,0xA008,0xA004,0xA002,0x6FFC,0x6FFB,0xA002,0x6FFA,0x6FF9,0xA004,0xA002,0x6FF8,0x6FF7,
+ 0xA002,0x6FF6,0x6FF5,0xA008,0xA004,0xA002,0x6FF4,0x6FF3,0xA002,0x6FF2,0x6FF1,0xA004,0xA002,0x6FF0,0x6FED,0xA002,
+ 0x6FEC,0x6FEB,0xA000,0xA000,0xA008,0xA004,0xA002,0x6FEA,0x6FE9,0xA002,0x6FE8,0x6FE7,0xA000,0xA000,0x6FE6,0xA0B6,
+ 0xA036,0xA001,0xA015,0xA005,0xA001,0xA001,0xA001,0x6FE5,0xA008,0xA004,0xA002,0x6FE4,0x6FE3,0xA002,0x6FE2,0x6FDF,
+ 0xA004,0xA002,0x6FDD,0x6FDC,0xA002,0x6FDB,0x6FDA,0xA010,0xA008,0xA004,0xA002,0x6FD9,0x6FD8,0xA002,0x6FD7,0x6FD6,
+ 0xA004,0xA002,0x6FD5,0x6FD4,0xA002,0x6FD3,0x6FD0,0xA008,0xA004,0xA002,0x6FCF,0x6FCE,0xA002,0x6FCD,0x6FCC,0xA004,
+ 0xA002,0x6FCB,0x6FCA,0xA002,0x6FC8,0x6FC7,0xA040,0xA020,0xA010,0xA008,0xA004,0xA002,0x6FC6,0x6FC5,0xA002,0x6FC4,
+ 0x6FC3,0xA004,0xA002,0x6FC1,0x6FBF,0xA002,0x6FBE,0x6FBD,0xA008,0xA004,0xA002,0x6FBC,0x6FBB,0xA002,0x6FBA,0x6FB8,
+ 0xA004,0xA002,0x6FB7,0x6FB5,0xA002,0x6FB4,0x6FB2,0xA010,0xA008,0xA004,0xA002,0x6FB1,0x6FB0,0xA002,0x6FAF,0x6FAE,
+ 0xA004,0xA002,0x6FAD,0x6FAC,0xA002,0x6FAB,0x6FAA,0xA008,0xA004,0xA002,0x6FA9,0x6FA8,0xA002,0x6FA6,0x6FA5,0xA004,
+ 0xA002,0x6FA4,0x6FA3,0xA002,0x6FA2,0x6FA0,0xA020,0xA010,0xA008,0xA004,0xA002,0x6F9F,0x6F9E,0xA002,0x6F9D,0x6F9B,
+ 0xA004,0xA002,0x6F9A,0x6F99,0xA002,0x6F98,0x6F97,0xA008,0xA004,0xA002,0x6F96,0x6F95,0xA002,0x6F94,0x6F93,0xA004,
+ 0xA002,0x6F92,0x6F91,0xA002,0x6F90,0x6F8F,0xA010,0xA008,0xA004,0xA002,0x6F8B,0x6F8A,0xA002,0x6F87,0x6F86,0xA004,
+ 0xA002,0x6F85,0x6F83,0xA002,0x6F82,0x6F81,0xA008,0xA004,0xA002,0x6F80,0x6F7F,0xA002,0x6F7E,0x6F7D,0xA004,0xA002,
+ 0x6F7B,0x6F79,0xA002,0x6F77,0x6F76,0xA07D,0xA03E,0xA01E,0xA00E,0xA006,0xA002,0x6F75,0xA002,0x6F73,0x6F71,0xA004,
+ 0xA002,0x6F70,0x6F6F,0xA002,0x6F6C,0x6F6B,0xA008,0xA004,0xA002,0x6F6A,0x6F69,0xA002,0x6F68,0x6F67,0xA004,0xA002,
+ 0x6F65,0x6F64,0xA002,0x6F63,0x6F61,0xA010,0xA008,0xA004,0xA002,0x6F60,0x6F5F,0xA002,0x6F5D,0x6F5B,0xA004,0xA002,
+ 0x6F5A,0x6F59,0xA002,0x6F57,0x6F56,0xA008,0xA004,0xA002,0x6F55,0x6F54,0xA002,0x6F53,0x6F52,0xA004,0xA002,0x6F51,
+ 0x6F50,0xA002,0x6F4F,0x6F4E,0xA01F,0xA00F,0xA008,0xA004,0xA002,0x6F4C,0x6F4A,0xA002,0x6F49,0x6F48,0xA004,0xA002,
+ 0x6F45,0x6F44,0xA000,0x6F43,0xA008,0xA004,0xA002,0x6F42,0x6F41,0xA002,0x6F40,0x6F3F,0xA004,0xA002,0x6F3D,0x6F3C,
+ 0xA002,0x6F3B,0x6F3A,0xA010,0xA008,0xA004,0xA002,0x6F39,0x6F38,0xA002,0x6F37,0x6F35,0xA004,0xA002,0x6F34,0x6F32,
+ 0xA002,0x6F30,0x6F2E,0xA008,0xA004,0xA002,0x6F2C,0x6F28,0xA002,0x6F27,0x6F26,0xA004,0xA002,0x6F25,0x6F23,0xA002,
+ 0x6F22,0x6F21,0xA040,0xA020,0xA010,0xA008,0xA004,0xA002,0x6F1F,0x6F1E,0xA002,0x6F1D,0x6F1C,0xA004,0xA002,0x6F1B,
+ 0x6F1A,0xA002,0x6F19,0x6F18,0xA008,0xA004,0xA002,0x6F17,0x6F16,0xA002,0x6F12,0x6F11,0xA004,0xA002,0x6F10,0x6F0E,
+ 0xA002,0x6F0D,0x6F0C,0xA010,0xA008,0xA004,0xA002,0x6F0B,0x6F0A,0xA002,0x6F08,0x6F07,0xA004,0xA002,0x6F05,0x6F04,
+ 0xA002,0x6F03,0x6F01,0xA008,0xA004,0xA002,0x6F00,0x6EFF,0xA002,0x6EFE,0x6EFD,0xA004,0xA002,0x6EFC,0x6EFB,0xA002,
+ 0x6EFA,0x6EF8,0xA000,0xA000,0xA008,0xA004,0xA002,0x6EF7,0x6EF6,0xA002,0x6EF5,0x6EF3,0xA004,0xA002,0x6EF2,0x6EF1,
+ 0xA000,0x6EF0,0xA184,0xA0B1,0xA031,0xA001,0xA010,0xA001,0xA007,0xA003,0xA001,0x6EEF,0xA002,0x6EEE,0x6EED,0xA004,
+ 0xA002,0x6EEC,0x6EEB,0xA002,0x6EEA,0x6EE7,0xA010,0xA008,0xA004,0xA002,0x6EE3,0x6EDD,0xA002,0x6EDC,0x6EDB,0xA004,
+ 0xA002,0x6ED9,0x6ED8,0xA002,0x6ED6,0x6ED2,0xA008,0xA004,0xA002,0x6ED0,0x6ECE,0xA002,0x6ECD,0x6ECC,0xA004,0xA002,
+ 0x6ECA,0x6EC9,0xA002,0x6EC8,0x6EC6,0xA040,0xA020,0xA010,0xA008,0xA004,0xA002,0x6EC5,0x6EC4,0xA002,0x6EC3,0x6EC0,
+ 0xA004,0xA002,0x6EBF,0x6EBE,0xA002,0x6EBC,0x6EB9,0xA008,0xA004,0xA002,0x6EB8,0x6EB5,0xA002,0x6EB3,0x6EB0,0xA004,
+ 0xA002,0x6EAE,0x6EAD,0xA002,0x6EAC,0x6EAB,0xA010,0xA008,0xA004,0xA002,0x6EA9,0x6EA8,0xA002,0x6EA6,0x6EA4,0xA004,
+ 0xA002,0x6EA3,0x6EA1,0xA002,0x6EA0,0x6E9E,0xA008,0xA004,0xA002,0x6E9D,0x6E9B,0xA002,0x6E9A,0x6E99,0xA004,0xA002,
+ 0x6E97,0x6E96,0xA002,0x6E95,0x6E94,0xA020,0xA010,0xA008,0xA004,0xA002,0x6E93,0x6E92,0xA002,0x6E91,0x6E8E,0xA004,
+ 0xA002,0x6E8D,0x6E8C,0xA002,0x6E8B,0x6E8A,0xA008,0xA004,0xA002,0x6E88,0x6E87,0xA002,0x6E84,0x6E82,0xA004,0xA002,
+ 0x6E81,0x6E80,0xA002,0x6E7D,0x6E7C,0xA010,0xA008,0xA004,0xA002,0x6E7B,0x6E7A,0xA002,0x6E79,0x6E78,0xA004,0xA002,
+ 0x6E77,0x6E76,0xA002,0x6E75,0x6E74,0xA008,0xA004,0xA002,0x6E73,0x6E72,0xA002,0x6E71,0x6E70,0xA004,0xA002,0x6E6F,
+ 0x6E6D,0xA002,0x6E6C,0x6E6A,0xA07D,0xA03E,0xA01E,0xA00E,0xA006,0xA002,0x6E69,0xA002,0x6E68,0x6E67,0xA004,0xA002,
+ 0x6E66,0x6E65,0xA002,0x6E64,0x6E63,0xA008,0xA004,0xA002,0x6E62,0x6E61,0xA002,0x6E60,0x6E5E,0xA004,0xA002,0x6E5D,
+ 0x6E5C,0xA002,0x6E5A,0x6E59,0xA010,0xA008,0xA004,0xA002,0x6E57,0x6E55,0xA002,0x6E52,0x6E51,0xA004,0xA002,0x6E50,
+ 0x6E4F,0xA002,0x6E4C,0x6E4B,0xA008,0xA004,0xA002,0x6E4A,0x6E49,0xA002,0x6E48,0x6E47,0xA004,0xA002,0x6E46,0x6E45,
+ 0xA002,0x6E42,0x6E41,0xA01F,0xA010,0xA008,0xA004,0xA002,0x6E40,0x6E3F,0xA002,0x6E3E,0x6E3D,0xA004,0xA002,0x6E3C,
+ 0x6E3B,0xA002,0x6E39,0x6E37,0xA007,0xA003,0xA000,0x6E36,0xA002,0x6E35,0x6E33,0xA004,0xA002,0x6E31,0x6E30,0xA002,
+ 0x6E2E,0x6E2C,0xA010,0xA008,0xA004,0xA002,0x6E2A,0x6E28,0xA002,0x6E27,0x6E26,0xA004,0xA002,0x6E22,0x6E1F,0xA002,
+ 0x6E1E,0x6E1C,0xA008,0xA004,0xA002,0x6E1B,0x6E19,0xA002,0x6E18,0x6E15,0xA004,0xA002,0x6E13,0x6E12,0xA002,0x6E0F,
+ 0x6E0B,0xA040,0xA020,0xA010,0xA008,0xA004,0xA002,0x6E09,0x6E08,0xA002,0x6E07,0x6E06,0xA004,0xA002,0x6E04,0x6E03,
+ 0xA002,0x6E02,0x6E01,0xA008,0xA004,0xA002,0x6E00,0x6DFF,0xA002,0x6DFE,0x6DFD,0xA004,0xA002,0x6DFA,0x6DF8,0xA002,
+ 0x6DF6,0x6DF5,0xA010,0xA008,0xA004,0xA002,0x6DF4,0x6DF2,0xA002,0x6DF0,0x6DEF,0xA004,0xA002,0x6DED,0x6DEA,0xA002,
+ 0x6DE9,0x6DE8,0xA008,0xA004,0xA002,0x6DE7,0x6DE5,0xA002,0x6DE3,0x6DE2,0xA004,0xA002,0x6DDF,0x6DDC,0xA002,0x6DDB,
+ 0x6DDA,0xA000,0xA010,0xA008,0xA004,0xA002,0x6DD7,0x6DD5,0xA002,0x6DD4,0x6DD3,0xA004,0xA002,0x6DD2,0x6DD0,0xA002,
+ 0x6DCF,0x6DCE,0xA000,0xA000,0xA000,0x6DCD,0xA0AE,0xA02E,0xA001,0xA00D,0xA001,0xA004,0xA001,0xA001,0x6DCA,0xA004,
+ 0xA002,0x6DC9,0x6DC8,0xA002,0x6DC3,0x6DC2,0xA010,0xA008,0xA004,0xA002,0x6DC1,0x6DBE,0xA002,0x6DBD,0x6DBC,0xA004,
+ 0xA002,0x6DBB,0x6DBA,0xA002,0x6DB9,0x6DB7,0xA008,0xA004,0xA002,0x6DB6,0x6DB4,0xA002,0x6DB3,0x6DB1,0xA004,0xA002,
+ 0x6DB0,0x6DAD,0xA002,0x6DAC,0x6DA5,0xA040,0xA020,0xA010,0xA008,0xA004,0xA002,0x6DA2,0x6D9C,0xA002,0x6D9A,0x6D99,
+ 0xA004,0xA002,0x6D98,0x6D97,0xA002,0x6D96,0x6D92,0xA008,0xA004,0xA002,0x6D90,0x6D8F,0xA002,0x6D8D,0x6D8B,0xA004,
+ 0xA002,0x6D8A,0x6D87,0xA002,0x6D86,0x6D84,0xA010,0xA008,0xA004,0xA002,0x6D83,0x6D81,0xA002,0x6D80,0x6D7F,0xA004,
+ 0xA002,0x6D7E,0x6D7D,0xA002,0x6D7B,0x6D7A,0xA008,0xA004,0xA002,0x6D79,0x6D76,0xA002,0x6D75,0x6D73,0xA004,0xA002,
+ 0x6D72,0x6D71,0xA002,0x6D70,0x6D6D,0xA020,0xA010,0xA008,0xA004,0xA002,0x6D6C,0x6D6B,0xA002,0x6D68,0x6D67,0xA004,
+ 0xA002,0x6D65,0x6D64,0xA002,0x6D62,0x6D61,0xA008,0xA004,0xA002,0x6D5F,0x6D5D,0xA002,0x6D5B,0x6D58,0xA004,0xA002,
+ 0x6D57,0x6D56,0xA002,0x6D55,0x6D50,0xA010,0xA008,0xA004,0xA002,0x6D4C,0x6D49,0xA002,0x6D44,0x6D42,0xA004,0xA002,
+ 0x6D40,0x6D3F,0xA002,0x6D3A,0x6D38,0xA008,0xA004,0xA002,0x6D37,0x6D36,0xA002,0x6D34,0x6D30,0xA004,0xA002,0x6D2F,
+ 0x6D2D,0xA002,0x6D2C,0x6D29,0xA07D,0xA03E,0xA01E,0xA00E,0xA006,0xA002,0x6D28,0xA002,0x6D26,0x6D24,0xA004,0xA002,
+ 0x6D23,0x6D22,0xA002,0x6D21,0x6D20,0xA008,0xA004,0xA002,0x6D1F,0x6D1D,0xA002,0x6D1C,0x6D18,0xA004,0xA002,0x6D16,
+ 0x6D15,0xA002,0x6D14,0x6D13,0xA010,0xA008,0xA004,0xA002,0x6D11,0x6D10,0xA002,0x6D0F,0x6D0D,0xA004,0xA002,0x6D0A,
+ 0x6D09,0xA002,0x6D08,0x6D06,0xA008,0xA004,0xA002,0x6D05,0x6D03,0xA002,0x6D02,0x6D00,0xA004,0xA002,0x6CFF,0x6CF9,
+ 0xA002,0x6CF4,0x6CF2,0xA01F,0xA010,0xA008,0xA004,0xA002,0x6CED,0x6CEC,0xA002,0x6CE9,0x6CE7,0xA004,0xA002,0x6CE6,
+ 0x6CE4,0xA002,0x6CDF,0x6CDD,0xA007,0xA004,0xA002,0x6CDC,0x6CDA,0xA000,0x6CD9,0xA004,0xA002,0x6CD8,0x6CD2,0xA002,
+ 0x6CD1,0x6CCF,0xA010,0xA008,0xA004,0xA002,0x6CCE,0x6CCD,0xA002,0x6CCB,0x6CC8,0xA004,0xA002,0x6CC7,0x6CC6,0xA002,
+ 0x6CC3,0x6CC2,0xA008,0xA004,0xA002,0x6CC1,0x6CC0,0xA002,0x6CBA,0x6CB7,0xA004,0xA002,0x6CB6,0x6CB5,0xA002,0x6CB4,
+ 0x6CB0,0xA040,0xA020,0xA010,0xA008,0xA004,0xA002,0x6CAF,0x6CAC,0xA002,0x6CA8,0x6CA2,0xA004,0xA002,0x6CA0,0x6C9E,
+ 0xA002,0x6C9D,0x6C9C,0xA008,0xA004,0xA002,0x6C9A,0x6C98,0xA002,0x6C97,0x6C96,0xA004,0xA002,0x6C95,0x6C92,0xA002,
+ 0x6C91,0x6C8E,0xA010,0xA008,0xA004,0xA002,0x6C8D,0x6C8B,0xA002,0x6C8A,0x6C87,0xA004,0xA002,0x6C84,0x6C80,0xA002,
+ 0x6C7F,0x6C7C,0xA008,0xA004,0xA002,0x6C7B,0x6C7A,0xA002,0x6C78,0x6C77,0xA004,0xA002,0x6C75,0x6C73,0xA002,0x6C71,
+ 0x6C6F,0xA000,0xA010,0xA008,0xA004,0xA002,0x6C6E,0x6C6D,0xA002,0x6C6C,0x6C6B,0xA004,0xA002,0x6C67,0x6C66,0xA002,
+ 0x6C65,0x6C63,0xA000,0xA004,0xA002,0x6C62,0x6C5A,0xA000,0x6C59,0xA303,0xA183,0xA0A8,0xA02A,0xA001,0xA009,0xA001,
+ 0xA001,0xA003,0xA001,0x6C58,0xA002,0x6C56,0x6C53,0xA010,0xA008,0xA004,0xA002,0x6C52,0x6C51,0xA002,0x6C4F,0x6C4E,
+ 0xA004,0xA002,0x6C4D,0x6C4C,0xA002,0x6C4B,0x6C48,0xA008,0xA004,0xA002,0x6C45,0x6C44,0xA002,0x6C43,0x6C3F,0xA004,
+ 0xA002,0x6C3E,0x6C3C,0xA002,0x6C3B,0x6C3A,0xA03E,0xA01E,0xA00E,0xA006,0xA002,0x6C39,0xA002,0x6C37,0x6C36,0xA004,
+ 0xA002,0x6C33,0x6C31,0xA002,0x6C2D,0x6C2C,0xA008,0xA004,0xA002,0x6C2B,0x6C25,0xA002,0x6C23,0x6C20,0xA004,0xA002,
+ 0x6C1E,0x6C1D,0xA002,0x6C1C,0x6C17,0xA010,0xA008,0xA004,0xA002,0x6C12,0x6C0E,0xA002,0x6C0C,0x6C0B,0xA004,0xA002,
+ 0x6C0A,0x6C09,0xA002,0x6C08,0x6C04,0xA008,0xA004,0xA002,0x6C03,0x6C02,0xA002,0x6C01,0x6C00,0xA004,0xA002,0x6BFF,
+ 0x6BFE,0xA002,0x6BFC,0x6BFB,0xA020,0xA010,0xA008,0xA004,0xA002,0x6BFA,0x6BF8,0xA002,0x6BF7,0x6BF6,0xA004,0xA002,
+ 0x6BF4,0x6BF2,0xA002,0x6BF1,0x6BF0,0xA008,0xA004,0xA002,0x6BEE,0x6BED,0xA002,0x6BEC,0x6BE9,0xA004,0xA002,0x6BE8,
+ 0x6BE7,0xA002,0x6BE6,0x6BE5,0xA010,0xA008,0xA004,0xA002,0x6BE4,0x6BE3,0xA002,0x6BE2,0x6BE0,0xA004,0xA002,0x6BDF,
+ 0x6BDE,0xA002,0x6BDD,0x6BDC,0xA008,0xA004,0xA002,0x6BDA,0x6BD8,0xA002,0x6BD1,0x6BD0,0xA004,0xA002,0x6BCE,0x6BCC,
+ 0xA002,0x6BCA,0x6BC9,0xA07D,0xA03E,0xA01E,0xA00E,0xA006,0xA002,0x6BC8,0xA002,0x6BC7,0x6BC6,0xA004,0xA002,0x6BC4,
+ 0x6BC3,0xA002,0x6BC0,0x6BBE,0xA008,0xA004,0xA002,0x6BBD,0x6BBC,0xA002,0x6BBB,0x6BBA,0xA004,0xA002,0x6BB9,0x6BB8,
+ 0xA002,0x6BB6,0x6BB2,0xA010,0xA008,0xA004,0xA002,0x6BB1,0x6BB0,0xA002,0x6BAF,0x6BAE,0xA004,0xA002,0x6BAD,0x6BAC,
+ 0xA002,0x6BAB,0x6BA9,0xA008,0xA004,0xA002,0x6BA8,0x6BA7,0xA002,0x6BA6,0x6BA5,0xA004,0xA002,0x6BA4,0x6BA3,0xA002,
+ 0x6BA2,0x6BA0,0xA01F,0xA010,0xA008,0xA004,0xA002,0x6B9F,0x6B9E,0xA002,0x6B9D,0x6B9C,0xA004,0xA002,0x6B99,0x6B98,
+ 0xA002,0x6B97,0x6B95,0xA008,0xA004,0xA002,0x6B94,0x6B91,0xA002,0x6B90,0x6B8F,0xA004,0xA002,0x6B8E,0x6B8C,0xA001,
+ 0x6B88,0xA010,0xA008,0xA004,0xA002,0x6B85,0x6B80,0xA002,0x6B7F,0x6B7E,0xA004,0xA002,0x6B7D,0x6B7A,0xA002,0x6B78,
+ 0x6B77,0xA008,0xA004,0xA002,0x6B76,0x6B75,0xA002,0x6B74,0x6B73,0xA004,0xA002,0x6B72,0x6B71,0xA002,0x6B70,0x6B6F,
+ 0xA040,0xA020,0xA010,0xA008,0xA004,0xA002,0x6B6E,0x6B6D,0xA002,0x6B6C,0x6B6B,0xA004,0xA002,0x6B69,0x6B68,0xA002,
+ 0x6B61,0x6B60,0xA008,0xA004,0xA002,0x6B5F,0x6B5E,0xA002,0x6B5D,0x6B5C,0xA004,0xA002,0x6B5B,0x6B5A,0xA002,0x6B58,
+ 0x6B57,0xA010,0xA008,0xA004,0xA002,0x6B56,0x6B55,0xA002,0x6B54,0x6B53,0xA004,0xA002,0x6B52,0x6B51,0xA002,0x6B50,
+ 0x6B4F,0xA008,0xA004,0xA002,0x6B4E,0x6B4D,0xA002,0x6B4B,0x6B4A,0xA004,0xA002,0x6B48,0x6B45,0xA002,0x6B44,0x6B42,
+ 0xA000,0xA010,0xA008,0xA004,0xA002,0x6B41,0x6B40,0xA002,0x6B3F,0x6B3D,0xA004,0xA002,0x6B3C,0x6B3B,0xA002,0x6B38,
+ 0x6B36,0xA008,0xA004,0xA002,0x6B35,0x6B34,0xA002,0x6B33,0x6B31,0xA000,0xA002,0x6B30,0x6B2F,0xA0A2,0xA022,0xA001,
+ 0xA001,0xA010,0xA008,0xA004,0xA002,0x6B2E,0x6B2D,0xA002,0x6B2C,0x6B2B,0xA004,0xA002,0x6B2A,0x6B29,0xA002,0x6B28,
+ 0x6B26,0xA008,0xA004,0xA002,0x6B25,0x6B1F,0xA002,0x6B1E,0x6B1D,0xA004,0xA002,0x6B1C,0x6B1B,0xA002,0x6B1A,0x6B19,
+ 0xA040,0xA020,0xA010,0xA008,0xA004,0xA002,0x6B18,0x6B17,0xA002,0x6B16,0x6B15,0xA004,0xA002,0x6B14,0x6B13,0xA002,
+ 0x6B12,0x6B11,0xA008,0xA004,0xA002,0x6B10,0x6B0F,0xA002,0x6B0E,0x6B0D,0xA004,0xA002,0x6B0C,0x6B0B,0xA002,0x6B0A,
+ 0x6B09,0xA010,0xA008,0xA004,0xA002,0x6B08,0x6B07,0xA002,0x6B06,0x6B05,0xA004,0xA002,0x6B04,0x6B03,0xA002,0x6B02,
+ 0x6B01,0xA008,0xA004,0xA002,0x6B00,0x6AFF,0xA002,0x6AFE,0x6AFD,0xA004,0xA002,0x6AFC,0x6AFB,0xA002,0x6AFA,0x6AF9,
+ 0xA020,0xA010,0xA008,0xA004,0xA002,0x6AF8,0x6AF7,0xA002,0x6AF6,0x6AF5,0xA004,0xA002,0x6AF4,0x6AF3,0xA002,0x6AF2,
+ 0x6AF1,0xA008,0xA004,0xA002,0x6AF0,0x6AEF,0xA002,0x6AEE,0x6AED,0xA004,0xA002,0x6AEC,0x6AEB,0xA002,0x6AEA,0x6AE9,
+ 0xA010,0xA008,0xA004,0xA002,0x6AE8,0x6AE7,0xA002,0x6AE6,0x6AE5,0xA004,0xA002,0x6AE4,0x6AE3,0xA002,0x6AE2,0x6AE1,
+ 0xA008,0xA004,0xA002,0x6AE0,0x6ADF,0xA002,0x6ADE,0x6ADD,0xA004,0xA002,0x6ADC,0x6ADB,0xA002,0x6ADA,0x6AD9,0xA07D,
+ 0xA03E,0xA01E,0xA00E,0xA006,0xA002,0x6AD8,0xA002,0x6AD7,0x6AD6,0xA004,0xA002,0x6AD5,0x6AD4,0xA002,0x6AD3,0x6AD2,
+ 0xA008,0xA004,0xA002,0x6AD1,0x6AD0,0xA002,0x6ACF,0x6ACE,0xA004,0xA002,0x6ACD,0x6ACC,0xA002,0x6ACB,0x6ACA,0xA010,
+ 0xA008,0xA004,0xA002,0x6AC9,0x6AC8,0xA002,0x6AC7,0x6AC6,0xA004,0xA002,0x6AC5,0x6AC4,0xA002,0x6AC3,0x6AC2,0xA008,
+ 0xA004,0xA002,0x6AC1,0x6AC0,0xA002,0x6ABF,0x6ABE,0xA004,0xA002,0x6ABD,0x6ABC,0xA002,0x6ABB,0x6ABA,0xA020,0xA010,
+ 0xA008,0xA004,0xA002,0x6AB9,0x6AB8,0xA002,0x6AB7,0x6AB6,0xA004,0xA002,0x6AB5,0x6AB4,0xA002,0x6AB3,0x6AB2,0xA008,
+ 0xA004,0xA002,0x6AB1,0x6AB0,0xA002,0x6AAF,0x6AAE,0xA004,0xA002,0x6AAD,0x6AAA,0xA002,0x6AA8,0x6AA7,0xA00F,0xA007,
+ 0xA003,0xA001,0x6AA6,0xA002,0x6AA5,0x6AA4,0xA004,0xA002,0x6AA3,0x6AA2,0xA002,0x6AA1,0x6A9F,0xA008,0xA004,0xA002,
+ 0x6A9E,0x6A9D,0xA002,0x6A9C,0x6A9B,0xA004,0xA002,0x6A9A,0x6A99,0xA002,0x6A98,0x6A96,0xA040,0xA020,0xA010,0xA008,
+ 0xA004,0xA002,0x6A95,0x6A94,0xA002,0x6A93,0x6A92,0xA004,0xA002,0x6A8F,0x6A8D,0xA002,0x6A8C,0x6A8B,0xA008,0xA004,
+ 0xA002,0x6A8A,0x6A89,0xA002,0x6A88,0x6A87,0xA004,0xA002,0x6A86,0x6A85,0xA002,0x6A83,0x6A82,0xA010,0xA008,0xA004,
+ 0xA002,0x6A81,0x6A7F,0xA002,0x6A7E,0x6A7D,0xA004,0xA002,0x6A7B,0x6A7A,0xA002,0x6A78,0x6A77,0xA008,0xA004,0xA002,
+ 0x6A76,0x6A75,0xA002,0x6A74,0x6A73,0xA004,0xA002,0x6A72,0x6A70,0xA002,0x6A6F,0x6A6E,0xA000,0xA010,0xA008,0xA004,
+ 0xA002,0x6A6D,0x6A6C,0xA002,0x6A6B,0x6A6A,0xA004,0xA002,0x6A69,0x6A68,0xA002,0x6A67,0x6A66,0xA008,0xA004,0xA002,
+ 0x6A64,0x6A63,0xA002,0x6A62,0x6A60,0xA004,0xA002,0x6A5F,0x6A5E,0xA002,0x6A5D,0x6A5C,0xA183,0xA09F,0xA01F,0xA001,
+ 0xA001,0xA00D,0xA005,0xA001,0xA002,0x6A5A,0x6A57,0xA004,0xA002,0x6A56,0x6A55,0xA002,0x6A54,0x6A53,0xA008,0xA004,
+ 0xA002,0x6A52,0x6A51,0xA002,0x6A4F,0x6A4E,0xA004,0xA002,0x6A4D,0x6A4C,0xA002,0x6A4B,0x6A4A,0xA040,0xA020,0xA010,
+ 0xA008,0xA004,0xA002,0x6A49,0x6A48,0xA002,0x6A46,0x6A45,0xA004,0xA002,0x6A43,0x6A42,0xA002,0x6A41,0x6A40,0xA008,
+ 0xA004,0xA002,0x6A3F,0x6A3C,0xA002,0x6A3B,0x6A3A,0xA004,0xA002,0x6A39,0x6A38,0xA002,0x6A37,0x6A36,0xA010,0xA008,
+ 0xA004,0xA002,0x6A34,0x6A33,0xA002,0x6A32,0x6A30,0xA004,0xA002,0x6A2E,0x6A2D,0xA002,0x6A2C,0x6A2B,0xA008,0xA004,
+ 0xA002,0x6A29,0x6A27,0xA002,0x6A26,0x6A25,0xA004,0xA002,0x6A24,0x6A23,0xA002,0x6A22,0x6A20,0xA020,0xA010,0xA008,
+ 0xA004,0xA002,0x6A1E,0x6A1D,0xA002,0x6A1C,0x6A1B,0xA004,0xA002,0x6A1A,0x6A19,0xA002,0x6A16,0x6A15,0xA008,0xA004,
+ 0xA002,0x6A14,0x6A13,0xA002,0x6A12,0x6A11,0xA004,0xA002,0x6A10,0x6A0F,0xA002,0x6A0E,0x6A0D,0xA010,0xA008,0xA004,
+ 0xA002,0x6A0C,0x6A0B,0xA002,0x6A09,0x6A08,0xA004,0xA002,0x6A07,0x6A06,0xA002,0x6A05,0x6A04,0xA008,0xA004,0xA002,
+ 0x6A03,0x6A02,0xA002,0x6A01,0x6A00,0xA004,0xA002,0x69FE,0x69FC,0xA002,0x69FB,0x69FA,0xA07D,0xA03E,0xA01E,0xA00E,
+ 0xA006,0xA002,0x69F9,0xA002,0x69F8,0x69F7,0xA004,0xA002,0x69F6,0x69F5,0xA002,0x69F4,0x69F3,0xA008,0xA004,0xA002,
+ 0x69F1,0x69F0,0xA002,0x69EF,0x69EE,0xA004,0xA002,0x69EC,0x69EB,0xA002,0x69EA,0x69E9,0xA010,0xA008,0xA004,0xA002,
+ 0x69E8,0x69E7,0xA002,0x69E6,0x69E5,0xA004,0xA002,0x69E4,0x69E3,0xA002,0x69E2,0x69E1,0xA008,0xA004,0xA002,0x69DE,
+ 0x69DD,0xA002,0x69DC,0x69DA,0xA004,0xA002,0x69D9,0x69D8,0xA002,0x69D7,0x69D6,0xA020,0xA010,0xA008,0xA004,0xA002,
+ 0x69D5,0x69D3,0xA002,0x69D2,0x69D1,0xA004,0xA002,0x69CF,0x69CD,0xA002,0x69CB,0x69C9,0xA008,0xA004,0xA002,0x69C8,
+ 0x69C7,0xA002,0x69C6,0x69C5,0xA004,0xA002,0x69C4,0x69C3,0xA002,0x69C2,0x69C0,0xA00F,0xA007,0xA004,0xA002,0x69BF,
+ 0x69BE,0xA001,0x69BD,0xA004,0xA002,0x69BC,0x69BA,0xA002,0x69B9,0x69B8,0xA008,0xA004,0xA002,0x69B6,0x69B5,0xA002,
+ 0x69B3,0x69B2,0xA004,0xA002,0x69B0,0x69AF,0xA002,0x69AE,0x69AC,0xA040,0xA020,0xA010,0xA008,0xA004,0xA002,0x69AA,
+ 0x69A9,0xA002,0x69A6,0x69A5,0xA004,0xA002,0x69A4,0x69A3,0xA002,0x69A2,0x69A1,0xA008,0xA004,0xA002,0x69A0,0x699F,
+ 0xA002,0x699E,0x699D,0xA004,0xA002,0x699A,0x6999,0xA002,0x6997,0x6996,0xA010,0xA008,0xA004,0xA002,0x6993,0x6992,
+ 0xA002,0x6991,0x6990,0xA004,0xA002,0x698F,0x698E,0xA002,0x698C,0x698B,0xA008,0xA004,0xA002,0x698A,0x6985,0xA002,
+ 0x6983,0x6981,0xA004,0xA002,0x697F,0x697E,0xA002,0x697D,0x697B,0xA020,0xA010,0xA008,0xA004,0xA002,0x697A,0x6976,
+ 0xA002,0x6975,0x6974,0xA004,0xA002,0x6973,0x6972,0xA002,0x6970,0x696F,0xA008,0xA004,0xA002,0x696D,0x696C,0xA002,
+ 0x696A,0x6969,0xA004,0xA002,0x6968,0x6967,0xA002,0x6965,0x6964,0xA000,0xA000,0xA000,0xA002,0x6962,0x6961,0xA09B,
+ 0xA01B,0xA001,0xA001,0xA009,0xA001,0xA004,0xA002,0x695F,0x695C,0xA002,0x695B,0x6959,0xA008,0xA004,0xA002,0x6958,
+ 0x6956,0xA002,0x6955,0x6953,0xA004,0xA002,0x6952,0x6951,0xA002,0x6950,0x694F,0xA040,0xA020,0xA010,0xA008,0xA004,
+ 0xA002,0x694E,0x694D,0xA002,0x694C,0x694B,0xA004,0xA002,0x694A,0x6949,0xA002,0x6948,0x6947,0xA008,0xA004,0xA002,
+ 0x6946,0x6945,0xA002,0x6944,0x6943,0xA004,0xA002,0x6941,0x6940,0xA002,0x693E,0x693C,0xA010,0xA008,0xA004,0xA002,
+ 0x693B,0x693A,0xA002,0x6938,0x6937,0xA004,0xA002,0x6936,0x6935,0xA002,0x6933,0x6932,0xA008,0xA004,0xA002,0x6931,
+ 0x692F,0xA002,0x692E,0x692C,0xA004,0xA002,0x692B,0x692A,0xA002,0x6929,0x6928,0xA020,0xA010,0xA008,0xA004,0xA002,
+ 0x6927,0x6926,0xA002,0x6925,0x6923,0xA004,0xA002,0x6922,0x6921,0xA002,0x691E,0x691D,0xA008,0xA004,0xA002,0x691C,
+ 0x691B,0xA002,0x691A,0x6919,0xA004,0xA002,0x6918,0x6917,0xA002,0x6916,0x6915,0xA010,0xA008,0xA004,0xA002,0x6914,
+ 0x6913,0xA002,0x6911,0x690F,0xA004,0xA002,0x690C,0x690A,0xA002,0x6909,0x6908,0xA008,0xA004,0xA002,0x6907,0x6906,
+ 0xA002,0x6904,0x6903,0xA004,0xA002,0x6902,0x6900,0xA002,0x68FF,0x68FE,0xA07D,0xA03E,0xA01E,0xA00E,0xA006,0xA002,
+ 0x68FD,0xA002,0x68FB,0x68F8,0xA004,0xA002,0x68F7,0x68F6,0xA002,0x68F4,0x68F3,0xA008,0xA004,0xA002,0x68F2,0x68EF,
+ 0xA002,0x68ED,0x68EC,0xA004,0xA002,0x68EB,0x68EA,0xA002,0x68E9,0x68E8,0xA010,0xA008,0xA004,0xA002,0x68E7,0x68E6,
+ 0xA002,0x68E5,0x68E4,0xA004,0xA002,0x68E2,0x68E1,0xA002,0x68DF,0x68DE,0xA008,0xA004,0xA002,0x68DD,0x68DC,0xA002,
+ 0x68DB,0x68D9,0xA004,0xA002,0x68D7,0x68D6,0xA002,0x68D4,0x68D3,0xA020,0xA010,0xA008,0xA004,0xA002,0x68D1,0x68D0,
+ 0xA002,0x68CF,0x68CE,0xA004,0xA002,0x68CC,0x68CA,0xA002,0x68C8,0x68C7,0xA008,0xA004,0xA002,0x68C6,0x68C5,0xA002,
+ 0x68C4,0x68C3,0xA004,0xA002,0x68C1,0x68BF,0xA002,0x68BE,0x68BD,0xA00F,0xA008,0xA004,0xA002,0x68BC,0x68BB,0xA002,
+ 0x68BA,0x68B9,0xA003,0xA001,0x68B8,0xA002,0x68B7,0x68B6,0xA008,0xA004,0xA002,0x68B4,0x68B2,0xA002,0x68B1,0x68AE,
+ 0xA004,0xA002,0x68AC,0x68AB,0xA002,0x68AA,0x68A9,0xA040,0xA020,0xA010,0xA008,0xA004,0xA002,0x68A5,0x68A4,0xA002,
+ 0x68A3,0x68A1,0xA004,0xA002,0x68A0,0x689F,0xA002,0x689E,0x689D,0xA008,0xA004,0xA002,0x689C,0x689B,0xA002,0x689A,
+ 0x6899,0xA004,0xA002,0x6898,0x6896,0xA002,0x6895,0x6894,0xA010,0xA008,0xA004,0xA002,0x6892,0x6891,0xA002,0x6890,
+ 0x688E,0xA004,0xA002,0x688D,0x688C,0xA002,0x688B,0x688A,0xA008,0xA004,0xA002,0x6889,0x6888,0xA002,0x6887,0x6884,
+ 0xA004,0xA002,0x6882,0x6880,0xA002,0x687F,0x687E,0xA020,0xA010,0xA008,0xA004,0xA002,0x687D,0x687C,0xA002,0x687B,
+ 0x687A,0xA004,0xA002,0x6879,0x6878,0xA002,0x6875,0x6873,0xA008,0xA004,0xA002,0x6872,0x6871,0xA002,0x6870,0x686F,
+ 0xA004,0xA002,0x686E,0x686D,0xA002,0x686C,0x686A,0xA000,0xA000,0xA004,0xA002,0x685F,0x685E,0xA002,0x685D,0x685C,
+ 0xA60F,0xA307,0xA183,0xA096,0xA018,0xA001,0xA001,0xA006,0xA001,0xA001,0xA002,0x685B,0x685A,0xA008,0xA004,0xA002,
+ 0x6859,0x6858,0xA002,0x6857,0x6856,0xA004,0xA002,0x6852,0x684F,0xA002,0x684D,0x684B,0xA03E,0xA01E,0xA00E,0xA006,
+ 0xA002,0x6847,0xA002,0x683F,0x683B,0xA004,0xA002,0x683A,0x6836,0xA002,0x6835,0x6834,0xA008,0xA004,0xA002,0x6831,
+ 0x6830,0xA002,0x682F,0x682E,0xA004,0xA002,0x682D,0x682C,0xA002,0x682B,0x6828,0xA010,0xA008,0xA004,0xA002,0x6827,
+ 0x6826,0xA002,0x6825,0x6824,0xA004,0xA002,0x6823,0x6822,0xA002,0x6820,0x681F,0xA008,0xA004,0xA002,0x681E,0x681C,
+ 0xA002,0x681B,0x681A,0xA004,0xA002,0x6819,0x6818,0xA002,0x6815,0x6814,0xA020,0xA010,0xA008,0xA004,0xA002,0x6812,
+ 0x6810,0xA002,0x680D,0x6806,0xA004,0xA002,0x6804,0x6803,0xA002,0x6802,0x6801,0xA008,0xA004,0xA002,0x67FE,0x67FC,
+ 0xA002,0x67FB,0x67FA,0xA004,0xA002,0x67F9,0x67F8,0xA002,0x67F7,0x67F6,0xA010,0xA008,0xA004,0xA002,0x67F5,0x67F2,
+ 0xA002,0x67EE,0x67ED,0xA004,0xA002,0x67EB,0x67EA,0xA002,0x67E8,0x67E7,0xA008,0xA004,0xA002,0x67E6,0x67E4,0xA002,
+ 0x67E3,0x67E1,0xA004,0xA002,0x67DF,0x67DB,0xA002,0x67D7,0x67D6,0xA07D,0xA03E,0xA01E,0xA00E,0xA006,0xA002,0x67D5,
+ 0xA002,0x67CE,0x67CD,0xA004,0xA002,0x67CC,0x67CB,0xA002,0x67CA,0x67C9,0xA008,0xA004,0xA002,0x67C8,0x67C7,0xA002,
+ 0x67C6,0x67C5,0xA004,0xA002,0x67C2,0x67C0,0xA002,0x67BF,0x67BE,0xA010,0xA008,0xA004,0xA002,0x67BD,0x67BC,0xA002,
+ 0x67BB,0x67BA,0xA004,0xA002,0x67B9,0x67B4,0xA002,0x67B2,0x67B1,0xA008,0xA004,0xA002,0x67AE,0x67AC,0xA002,0x67A9,
+ 0x67A6,0xA004,0xA002,0x67A4,0x67A1,0xA002,0x67A0,0x679F,0xA020,0xA010,0xA008,0xA004,0xA002,0x679B,0x6799,0xA002,
+ 0x6796,0x6794,0xA004,0xA002,0x6793,0x6792,0xA002,0x6791,0x678F,0xA008,0xA004,0xA002,0x678E,0x678D,0xA002,0x678C,
+ 0x678A,0xA004,0xA002,0x6788,0x6786,0xA002,0x6785,0x6783,0xA00F,0xA008,0xA004,0xA002,0x6782,0x6780,0xA002,0x677D,
+ 0x677B,0xA004,0xA002,0x677A,0x6779,0xA000,0x6778,0xA008,0xA004,0xA002,0x6776,0x6774,0xA002,0x6771,0x676E,0xA004,
+ 0xA002,0x676C,0x676B,0xA002,0x6767,0x6766,0xA040,0xA020,0xA010,0xA008,0xA004,0xA002,0x6764,0x6763,0xA002,0x6762,
+ 0x675D,0xA004,0xA002,0x675B,0x675A,0xA002,0x6759,0x6758,0xA008,0xA004,0xA002,0x6757,0x6755,0xA002,0x6754,0x6752,
+ 0xA004,0xA002,0x674D,0x674B,0xA002,0x674A,0x6747,0xA010,0xA008,0xA004,0xA002,0x6745,0x6744,0xA002,0x6741,0x673F,
+ 0xA004,0xA002,0x673E,0x673C,0xA002,0x673B,0x6739,0xA008,0xA004,0xA002,0x6738,0x6737,0xA002,0x6736,0x6733,0xA004,
+ 0xA002,0x6732,0x6730,0xA002,0x672E,0x6729,0xA020,0xA010,0xA008,0xA004,0xA002,0x6727,0x6725,0xA002,0x6724,0x6723,
+ 0xA004,0xA002,0x6722,0x6721,0xA002,0x6720,0x671E,0xA008,0xA004,0xA002,0x671C,0x671A,0xA002,0x6719,0x6718,0xA004,
+ 0xA002,0x6716,0x6713,0xA002,0x6712,0x6711,0xA000,0xA008,0xA004,0xA002,0x670F,0x670E,0xA002,0x670C,0x6707,0xA004,
+ 0xA002,0x6706,0x6705,0xA000,0x6704,0xA092,0xA012,0xA001,0xA001,0xA001,0xA007,0xA003,0xA001,0x6703,0xA002,0x6702,
+ 0x6701,0xA004,0xA002,0x66FD,0x66FB,0xA002,0x66FA,0x66F8,0xA040,0xA020,0xA010,0xA008,0xA004,0xA002,0x66F6,0x66F5,
+ 0xA002,0x66F1,0x66EF,0xA004,0xA002,0x66EE,0x66ED,0xA002,0x66EC,0x66EB,0xA008,0xA004,0xA002,0x66EA,0x66E8,0xA002,
+ 0x66E7,0x66E5,0xA004,0xA002,0x66E4,0x66E3,0xA002,0x66E2,0x66E1,0xA010,0xA008,0xA004,0xA002,0x66E0,0x66DF,0xA002,
+ 0x66DE,0x66DA,0xA004,0xA002,0x66D8,0x66D7,0xA002,0x66D6,0x66D5,0xA008,0xA004,0xA002,0x66D4,0x66D3,0xA002,0x66D2,
+ 0x66D1,0xA004,0xA002,0x66D0,0x66CF,0xA002,0x66CE,0x66CD,0xA020,0xA010,0xA008,0xA004,0xA002,0x66CC,0x66CB,0xA002,
+ 0x66CA,0x66C9,0xA004,0xA002,0x66C8,0x66C7,0xA002,0x66C6,0x66C5,0xA008,0xA004,0xA002,0x66C4,0x66C3,0xA002,0x66C2,
+ 0x66C1,0xA004,0xA002,0x66C0,0x66BF,0xA002,0x66BD,0x66BC,0xA010,0xA008,0xA004,0xA002,0x66BB,0x66BA,0xA002,0x66B8,
+ 0x66B7,0xA004,0xA002,0x66B6,0x66B5,0xA002,0x66B3,0x66B2,0xA008,0xA004,0xA002,0x66B1,0x66B0,0xA002,0x66AF,0x66AD,
+ 0xA004,0xA002,0x66AC,0x66AB,0xA002,0x66AA,0x66A9,0xA07D,0xA03E,0xA01E,0xA00E,0xA006,0xA002,0x66A6,0xA002,0x66A5,
+ 0x66A4,0xA004,0xA002,0x66A3,0x66A2,0xA002,0x66A1,0x66A0,0xA008,0xA004,0xA002,0x669F,0x669E,0xA002,0x669C,0x669B,
+ 0xA004,0xA002,0x669A,0x6699,0xA002,0x6698,0x6695,0xA010,0xA008,0xA004,0xA002,0x6694,0x6693,0xA002,0x6692,0x6690,
+ 0xA004,0xA002,0x668F,0x668E,0xA002,0x668D,0x668B,0xA008,0xA004,0xA002,0x668A,0x6689,0xA002,0x6688,0x6686,0xA004,
+ 0xA002,0x6685,0x6683,0xA002,0x6681,0x6680,0xA020,0xA010,0xA008,0xA004,0xA002,0x667F,0x667D,0xA002,0x667C,0x667B,
+ 0xA004,0xA002,0x6679,0x6678,0xA002,0x6675,0x6673,0xA008,0xA004,0xA002,0x6672,0x6671,0xA002,0x666D,0x666C,0xA004,
+ 0xA002,0x666B,0x666A,0xA002,0x6669,0x6667,0xA010,0xA008,0xA004,0xA002,0x6665,0x6663,0xA002,0x6662,0x6660,0xA004,
+ 0xA002,0x665E,0x665D,0xA002,0x665C,0x665B,0xA007,0xA003,0xA000,0x6659,0xA002,0x6658,0x6651,0xA004,0xA002,0x6650,
+ 0x664E,0xA002,0x664D,0x664A,0xA040,0xA020,0xA010,0xA008,0xA004,0xA002,0x6649,0x6648,0xA002,0x6647,0x6646,0xA004,
+ 0xA002,0x6645,0x6644,0xA002,0x6642,0x6640,0xA008,0xA004,0xA002,0x663F,0x663D,0xA002,0x663B,0x663A,0xA004,0xA002,
+ 0x6639,0x6638,0xA002,0x6637,0x6633,0xA010,0xA008,0xA004,0xA002,0x6632,0x6630,0xA002,0x662E,0x662C,0xA004,0xA002,
+ 0x662B,0x662A,0xA002,0x6629,0x6626,0xA008,0xA004,0xA002,0x6624,0x6623,0xA002,0x6622,0x6621,0xA004,0xA002,0x661E,
+ 0x661C,0xA002,0x661B,0x661A,0xA020,0xA010,0xA008,0xA004,0xA002,0x6618,0x6617,0xA002,0x6616,0x6612,0xA004,0xA002,
+ 0x6611,0x6610,0xA002,0x660D,0x660B,0xA008,0xA004,0xA002,0x6609,0x6608,0xA002,0x6607,0x6605,0xA004,0xA002,0x6604,
+ 0x6601,0xA002,0x65FF,0x65FE,0xA010,0xA008,0xA004,0xA002,0x65FD,0x65FC,0xA002,0x65FB,0x65F9,0xA004,0xA002,0x65F8,
+ 0x65F5,0xA002,0x65F4,0x65F3,0xA000,0xA000,0xA000,0x65F2,0xA184,0xA08F,0xA00F,0xA001,0xA001,0xA001,0xA004,0xA001,
+ 0xA001,0x65EB,0xA004,0xA002,0x65EA,0x65E4,0xA002,0x65E3,0x65E1,0xA040,0xA020,0xA010,0xA008,0xA004,0xA002,0x65DF,
+ 0x65DE,0xA002,0x65DD,0x65DC,0xA004,0xA002,0x65DB,0x65DA,0xA002,0x65D9,0x65D8,0xA008,0xA004,0xA002,0x65D5,0x65D4,
+ 0xA002,0x65D3,0x65D1,0xA004,0xA002,0x65D0,0x65CD,0xA002,0x65CA,0x65C9,0xA010,0xA008,0xA004,0xA002,0x65C8,0x65C7,
+ 0xA002,0x65C2,0x65C0,0xA004,0xA002,0x65BF,0x65BE,0xA002,0x65BB,0x65BA,0xA008,0xA004,0xA002,0x65B8,0x65B7,0xA002,
+ 0x65B6,0x65B5,0xA004,0xA002,0x65B4,0x65B3,0xA002,0x65B2,0x65B1,0xA020,0xA010,0xA008,0xA004,0xA002,0x65AE,0x65AC,
+ 0xA002,0x65AA,0x65A8,0xA004,0xA002,0x65A6,0x65A3,0xA002,0x65A2,0x65A0,0xA008,0xA004,0xA002,0x659E,0x659D,0xA002,
+ 0x659A,0x6598,0xA004,0xA002,0x6596,0x6595,0xA002,0x6594,0x6592,0xA010,0xA008,0xA004,0xA002,0x658F,0x658E,0xA002,
+ 0x658D,0x658A,0xA004,0xA002,0x6589,0x6588,0xA002,0x6586,0x6585,0xA008,0xA004,0xA002,0x6584,0x6583,0xA002,0x6582,
+ 0x6581,0xA004,0xA002,0x6580,0x657F,0xA002,0x657E,0x657D,0xA07D,0xA03E,0xA01E,0xA00E,0xA006,0xA002,0x657C,0xA002,
+ 0x657B,0x657A,0xA004,0xA002,0x6579,0x6578,0xA002,0x6576,0x6575,0xA008,0xA004,0xA002,0x6573,0x6571,0xA002,0x656F,
+ 0x656E,0xA004,0xA002,0x656D,0x656A,0xA002,0x6569,0x6568,0xA010,0xA008,0xA004,0xA002,0x6567,0x6565,0xA002,0x6564,
+ 0x6561,0xA004,0xA002,0x6560,0x655F,0xA002,0x655C,0x655A,0xA008,0xA004,0xA002,0x6558,0x6557,0xA002,0x6554,0x6553,
+ 0xA004,0xA002,0x6552,0x6550,0xA002,0x654E,0x654D,0xA020,0xA010,0xA008,0xA004,0xA002,0x654B,0x654A,0xA002,0x6547,
+ 0x6546,0xA004,0xA002,0x6544,0x6543,0xA002,0x6542,0x6541,0xA008,0xA004,0xA002,0x6540,0x653D,0xA002,0x653C,0x653A,
+ 0xA004,0xA002,0x6537,0x6533,0xA002,0x6532,0x6531,0xA010,0xA008,0xA004,0xA002,0x6530,0x652D,0xA002,0x652C,0x652A,
+ 0xA004,0xA002,0x6529,0x6528,0xA002,0x6527,0x6526,0xA007,0xA004,0xA002,0x6524,0x6523,0xA000,0x6522,0xA004,0xA002,
+ 0x6521,0x6520,0xA002,0x651F,0x651E,0xA040,0xA020,0xA010,0xA008,0xA004,0xA002,0x651D,0x651C,0xA002,0x651B,0x651A,
+ 0xA004,0xA002,0x6519,0x6517,0xA002,0x6516,0x6515,0xA008,0xA004,0xA002,0x6514,0x6513,0xA002,0x6511,0x6510,0xA004,
+ 0xA002,0x650F,0x650E,0xA002,0x650D,0x650C,0xA010,0xA008,0xA004,0xA002,0x650B,0x650A,0xA002,0x6508,0x6507,0xA004,
+ 0xA002,0x6506,0x6505,0xA002,0x6504,0x6503,0xA008,0xA004,0xA002,0x6502,0x6501,0xA002,0x64FF,0x64FE,0xA004,0xA002,
+ 0x64FD,0x64FC,0xA002,0x64FB,0x64FA,0xA020,0xA010,0xA008,0xA004,0xA002,0x64F9,0x64F8,0xA002,0x64F7,0x64F6,0xA004,
+ 0xA002,0x64F5,0x64F4,0xA002,0x64F3,0x64F2,0xA008,0xA004,0xA002,0x64F1,0x64F0,0xA002,0x64EF,0x64EE,0xA004,0xA002,
+ 0x64ED,0x64EC,0xA002,0x64EB,0x64EA,0xA010,0xA008,0xA004,0xA002,0x64E9,0x64E8,0xA002,0x64E7,0x64E5,0xA004,0xA002,
+ 0x64E3,0x64E1,0xA002,0x64E0,0x64DF,0xA000,0xA004,0xA002,0x64DD,0x64DC,0xA000,0x64DB,0xA08B,0xA00B,0xA001,0xA001,
+ 0xA001,0xA001,0xA003,0xA001,0x64DA,0xA002,0x64D9,0x64D6,0xA040,0xA020,0xA010,0xA008,0xA004,0xA002,0x64D5,0x64D4,
+ 0xA002,0x64D3,0x64D1,0xA004,0xA002,0x64CF,0x64CC,0xA002,0x64CB,0x64CA,0xA008,0xA004,0xA002,0x64C9,0x64C8,0xA002,
+ 0x64C7,0x64C6,0xA004,0xA002,0x64C4,0x64C3,0xA002,0x64C1,0x64BF,0xA010,0xA008,0xA004,0xA002,0x64BE,0x64BD,0xA002,
+ 0x64BB,0x64B9,0xA004,0xA002,0x64B6,0x64B4,0xA002,0x64B3,0x64B2,0xA008,0xA004,0xA002,0x64B1,0x64AF,0xA002,0x64AB,
+ 0x64AA,0xA004,0xA002,0x64A8,0x64A7,0xA002,0x64A6,0x64A5,0xA020,0xA010,0xA008,0xA004,0xA002,0x64A3,0x64A2,0xA002,
+ 0x64A1,0x64A0,0xA004,0xA002,0x649F,0x649D,0xA002,0x649C,0x649B,0xA008,0xA004,0xA002,0x649A,0x6498,0xA002,0x6497,
+ 0x6494,0xA004,0xA002,0x6493,0x6490,0xA002,0x648F,0x648E,0xA010,0xA008,0xA004,0xA002,0x648D,0x648C,0xA002,0x648B,
+ 0x648A,0xA004,0xA002,0x6489,0x6488,0xA002,0x6486,0x6483,0xA008,0xA004,0xA002,0x6481,0x6480,0xA002,0x647F,0x647E,
+ 0xA004,0xA002,0x647D,0x647C,0xA002,0x647B,0x6477,0xA07D,0xA03E,0xA01E,0xA00E,0xA006,0xA002,0x6476,0xA002,0x6475,
+ 0x6474,0xA004,0xA002,0x6473,0x6472,0xA002,0x6471,0x6470,0xA008,0xA004,0xA002,0x646F,0x646E,0xA002,0x646C,0x646B,
+ 0xA004,0xA002,0x646A,0x6468,0xA002,0x6466,0x6465,0xA010,0xA008,0xA004,0xA002,0x6464,0x6463,0xA002,0x6462,0x6461,
+ 0xA004,0xA002,0x6460,0x645F,0xA002,0x645D,0x645C,0xA008,0xA004,0xA002,0x645B,0x645A,0xA002,0x6459,0x6457,0xA004,
+ 0xA002,0x6456,0x6455,0xA002,0x6453,0x6451,0xA020,0xA010,0xA008,0xA004,0xA002,0x6450,0x644F,0xA002,0x644E,0x644D,
+ 0xA004,0xA002,0x644C,0x644B,0xA002,0x6449,0x6443,0xA008,0xA004,0xA002,0x6442,0x6440,0xA002,0x643E,0x643C,0xA004,
+ 0xA002,0x643B,0x6439,0xA002,0x6438,0x6437,0xA010,0xA008,0xA004,0xA002,0x6436,0x6435,0xA002,0x6433,0x6432,0xA004,
+ 0xA002,0x6431,0x6430,0xA002,0x642F,0x642E,0xA008,0xA004,0xA002,0x642B,0x6429,0xA002,0x6428,0x6427,0xA003,0xA000,
+ 0x6425,0xA002,0x6424,0x6423,0xA040,0xA020,0xA010,0xA008,0xA004,0xA002,0x6422,0x641F,0xA002,0x641D,0x641A,0xA004,
+ 0xA002,0x6419,0x6418,0xA002,0x6417,0x6416,0xA008,0xA004,0xA002,0x6415,0x6412,0xA002,0x6411,0x640E,0xA004,0xA002,
+ 0x640D,0x640A,0xA002,0x6409,0x6408,0xA010,0xA008,0xA004,0xA002,0x6407,0x6406,0xA002,0x6404,0x6403,0xA004,0xA002,
+ 0x63FE,0x63FC,0xA002,0x63FB,0x63FA,0xA008,0xA004,0xA002,0x63F9,0x63F7,0xA002,0x63F5,0x63F3,0xA004,0xA002,0x63F1,
+ 0x63F0,0xA002,0x63EF,0x63EE,0xA020,0xA010,0xA008,0xA004,0xA002,0x63EC,0x63EB,0xA002,0x63E8,0x63E7,0xA004,0xA002,
+ 0x63E6,0x63E5,0xA002,0x63E4,0x63E2,0xA008,0xA004,0xA002,0x63DF,0x63DD,0xA002,0x63DC,0x63DB,0xA004,0xA002,0x63DA,
+ 0x63D9,0xA002,0x63D8,0x63D7,0xA010,0xA008,0xA004,0xA002,0x63D5,0x63D4,0xA002,0x63D3,0x63D1,0xA004,0xA002,0x63CC,
+ 0x63CB,0xA002,0x63CA,0x63C8,0xA008,0xA004,0xA002,0x63C7,0x63C5,0xA002,0x63C3,0x63C2,0xA000,0xA000,0x63C1,0xA301,
+ 0xA184,0xA088,0xA008,0xA001,0xA001,0xA001,0xA001,0xA001,0xA001,0x63C0,0xA040,0xA020,0xA010,0xA008,0xA004,0xA002,
+ 0x63BF,0x63BD,0xA002,0x63BB,0x63B9,0xA004,0xA002,0x63B6,0x63B5,0xA002,0x63B2,0x63B1,0xA008,0xA004,0xA002,0x63AF,
+ 0x63AB,0xA002,0x63A6,0x63A4,0xA004,0xA002,0x63A1,0x639F,0xA002,0x639E,0x639D,0xA010,0xA008,0xA004,0xA002,0x639C,
+ 0x639B,0xA002,0x639A,0x6399,0xA004,0xA002,0x6397,0x6395,0xA002,0x6394,0x6393,0xA008,0xA004,0xA002,0x6391,0x638D,
+ 0xA002,0x638B,0x6386,0xA004,0xA002,0x6385,0x6384,0xA002,0x6383,0x6381,0xA020,0xA010,0xA008,0xA004,0xA002,0x637F,
+ 0x637E,0xA002,0x637D,0x637C,0xA004,0xA002,0x6379,0x6378,0xA002,0x6375,0x6374,0xA008,0xA004,0xA002,0x6373,0x6372,
+ 0xA002,0x6370,0x636F,0xA004,0xA002,0x636C,0x636B,0xA002,0x636A,0x6368,0xA010,0xA008,0xA004,0xA002,0x6366,0x6365,
+ 0xA002,0x6364,0x6360,0xA004,0xA002,0x635D,0x635C,0xA002,0x635B,0x635A,0xA008,0xA004,0xA002,0x6359,0x6358,0xA002,
+ 0x6357,0x6356,0xA004,0xA002,0x6354,0x6353,0xA002,0x6352,0x6351,0xA07D,0xA03E,0xA01E,0xA00E,0xA006,0xA002,0x634A,
+ 0xA002,0x6348,0x6347,0xA004,0xA002,0x6344,0x6341,0xA002,0x6340,0x633F,0xA008,0xA004,0xA002,0x633E,0x633C,0xA002,
+ 0x633B,0x6338,0xA004,0xA002,0x6337,0x6336,0xA002,0x6335,0x6334,0xA010,0xA008,0xA004,0xA002,0x6333,0x6331,0xA002,
+ 0x6330,0x632E,0xA004,0xA002,0x632D,0x632C,0xA002,0x6329,0x6327,0xA008,0xA004,0xA002,0x6326,0x631C,0xA002,0x6319,
+ 0x6318,0xA004,0xA002,0x6317,0x6315,0xA002,0x6314,0x6313,0xA020,0xA010,0xA008,0xA004,0xA002,0x6312,0x6310,0xA002,
+ 0x630F,0x630D,0xA004,0xA002,0x630C,0x630B,0xA002,0x630A,0x6306,0xA008,0xA004,0xA002,0x6305,0x6304,0xA002,0x6303,
+ 0x6300,0xA004,0xA002,0x62FB,0x62FA,0xA002,0x62F9,0x62F8,0xA010,0xA008,0xA004,0xA002,0x62F5,0x62F2,0xA002,0x62F0,
+ 0x62EB,0xA004,0xA002,0x62EA,0x62E4,0xA002,0x62E1,0x62E0,0xA008,0xA004,0xA002,0x62DE,0x62DD,0xA002,0x62D5,0x62D1,
+ 0xA004,0xA002,0x62CF,0x62CB,0xA000,0x62C3,0xA040,0xA020,0xA010,0xA008,0xA004,0xA002,0x62C1,0x62C0,0xA002,0x62BE,
+ 0x62BA,0xA004,0xA002,0x62B8,0x62B7,0xA002,0x62B6,0x62B4,0xA008,0xA004,0xA002,0x62B3,0x62B2,0xA002,0x62B0,0x62AF,
+ 0xA004,0xA002,0x62AE,0x62AD,0xA002,0x62AA,0x62A9,0xA010,0xA008,0xA004,0xA002,0x62A7,0x62A6,0xA002,0x62A3,0x629E,
+ 0xA004,0xA002,0x629D,0x629C,0xA002,0x6299,0x6294,0xA008,0xA004,0xA002,0x6290,0x628F,0xA002,0x628E,0x628D,0xA004,
+ 0xA002,0x628C,0x628B,0xA002,0x6288,0x6287,0xA020,0xA010,0xA008,0xA004,0xA002,0x6286,0x6285,0xA002,0x6283,0x6282,
+ 0xA004,0xA002,0x6281,0x627D,0xA002,0x627B,0x627A,0xA008,0xA004,0xA002,0x6278,0x6277,0xA002,0x6275,0x6274,0xA004,
+ 0xA002,0x6272,0x6271,0xA002,0x6268,0x6265,0xA010,0xA008,0xA004,0xA002,0x6264,0x6262,0xA002,0x6261,0x6260,0xA004,
+ 0xA002,0x625F,0x625E,0xA002,0x625D,0x625C,0xA008,0xA004,0xA002,0x625A,0x6259,0xA002,0x6257,0x6256,0xA004,0xA002,
+ 0x6255,0x6250,0xA000,0x624F,0xA080,0xA001,0xA03F,0xA01F,0xA00F,0xA007,0xA003,0xA001,0x624A,0xA002,0x6246,0x6245,
+ 0xA004,0xA002,0x6244,0x6242,0xA002,0x623C,0x623B,0xA008,0xA004,0xA002,0x623A,0x6239,0xA002,0x6238,0x6236,0xA004,
+ 0xA002,0x6235,0x6232,0xA002,0x6231,0x6230,0xA010,0xA008,0xA004,0xA002,0x622F,0x622D,0xA002,0x622B,0x6229,0xA004,
+ 0xA002,0x6228,0x6227,0xA002,0x6226,0x6223,0xA008,0xA004,0xA002,0x6220,0x621E,0xA002,0x621D,0x621C,0xA004,0xA002,
+ 0x6219,0x6214,0xA002,0x6213,0x6209,0xA020,0xA010,0xA008,0xA004,0xA002,0x6207,0x6205,0xA002,0x6204,0x6203,0xA004,
+ 0xA002,0x6202,0x6201,0xA002,0x6200,0x61FE,0xA008,0xA004,0xA002,0x61FD,0x61FC,0xA002,0x61FB,0x61FA,0xA004,0xA002,
+ 0x61F9,0x61F8,0xA002,0x61F7,0x61F6,0xA010,0xA008,0xA004,0xA002,0x61F4,0x61F3,0xA002,0x61F2,0x61F1,0xA004,0xA002,
+ 0x61F0,0x61EF,0xA002,0x61EE,0x61ED,0xA008,0xA004,0xA002,0x61EC,0x61EB,0xA002,0x61EA,0x61E9,0xA004,0xA002,0x61E8,
+ 0x61E7,0xA002,0x61E5,0x61E4,0xA07E,0xA03E,0xA01E,0xA00E,0xA006,0xA002,0x61E3,0xA002,0x61E2,0x61E1,0xA004,0xA002,
+ 0x61E0,0x61DF,0xA002,0x61DE,0x61DD,0xA008,0xA004,0xA002,0x61DC,0x61DB,0xA002,0x61DA,0x61D9,0xA004,0xA002,0x61D8,
+ 0x61D7,0xA002,0x61D6,0x61D5,0xA010,0xA008,0xA004,0xA002,0x61D3,0x61D0,0xA002,0x61CF,0x61CE,0xA004,0xA002,0x61CD,
+ 0x61CC,0xA002,0x61C9,0x61C7,0xA008,0xA004,0xA002,0x61C6,0x61C5,0xA002,0x61C4,0x61C3,0xA004,0xA002,0x61C1,0x61C0,
+ 0xA002,0x61BF,0x61BD,0xA020,0xA010,0xA008,0xA004,0xA002,0x61BC,0x61BB,0xA002,0x61BA,0x61B9,0xA004,0xA002,0x61B8,
+ 0x61B6,0xA002,0x61B5,0x61B4,0xA008,0xA004,0xA002,0x61B3,0x61B2,0xA002,0x61B1,0x61B0,0xA004,0xA002,0x61AF,0x61AE,
+ 0xA002,0x61AD,0x61AB,0xA010,0xA008,0xA004,0xA002,0x61AA,0x61A6,0xA002,0x61A5,0x61A4,0xA004,0xA002,0x61A3,0x61A2,
+ 0xA002,0x61A1,0x61A0,0xA008,0xA004,0xA002,0x619F,0x619E,0xA002,0x619C,0x619B,0xA004,0xA002,0x619A,0x6199,0xA002,
+ 0x6198,0x6197,0xA03F,0xA01F,0xA00F,0xA007,0xA003,0xA000,0x6196,0xA002,0x6195,0x6193,0xA004,0xA002,0x6192,0x6191,
+ 0xA002,0x6190,0x618F,0xA008,0xA004,0xA002,0x618D,0x618C,0xA002,0x618A,0x6189,0xA004,0xA002,0x6188,0x6187,0xA002,
+ 0x6186,0x6185,0xA010,0xA008,0xA004,0xA002,0x6184,0x6183,0xA002,0x6182,0x6181,0xA004,0xA002,0x6180,0x617F,0xA002,
+ 0x617E,0x617D,0xA008,0xA004,0xA002,0x617C,0x617B,0xA002,0x617A,0x6179,0xA004,0xA002,0x6178,0x6176,0xA002,0x6174,
+ 0x6173,0xA020,0xA010,0xA008,0xA004,0xA002,0x6172,0x6171,0xA002,0x616F,0x616E,0xA004,0xA002,0x616D,0x616C,0xA002,
+ 0x616B,0x616A,0xA008,0xA004,0xA002,0x6169,0x6166,0xA002,0x6165,0x6164,0xA004,0xA002,0x6163,0x6161,0xA002,0x6160,
+ 0x615F,0xA010,0xA008,0xA004,0xA002,0x615E,0x615C,0xA002,0x615B,0x615A,0xA004,0xA002,0x6159,0x6158,0xA002,0x6157,
+ 0x6156,0xA008,0xA004,0xA002,0x6154,0x6153,0xA002,0x6152,0x6150,0xA004,0xA002,0x614F,0x614D,0xA002,0x614B,0x6149,
+ 0xA180,0xA083,0xA007,0xA000,0xA000,0xA000,0xA000,0xA000,0x6147,0xA03C,0xA01C,0xA00C,0xA004,0xA001,0xA001,0x6146,
+ 0xA004,0xA002,0x6145,0x6144,0xA002,0x6143,0x6142,0xA008,0xA004,0xA002,0x6141,0x6140,0xA002,0x613E,0x613D,0xA004,
+ 0xA002,0x613C,0x613B,0xA002,0x613A,0x6139,0xA010,0xA008,0xA004,0xA002,0x6138,0x6137,0xA002,0x6136,0x6135,0xA004,
+ 0xA002,0x6134,0x6133,0xA002,0x6132,0x6131,0xA008,0xA004,0xA002,0x6130,0x612F,0xA002,0x612E,0x612D,0xA004,0xA002,
+ 0x612C,0x612A,0xA002,0x6129,0x6128,0xA020,0xA010,0xA008,0xA004,0xA002,0x6125,0x6122,0xA002,0x6121,0x611E,0xA004,
+ 0xA002,0x611D,0x611C,0xA002,0x611B,0x6119,0xA008,0xA004,0xA002,0x6118,0x6117,0xA002,0x6116,0x6114,0xA004,0xA002,
+ 0x6113,0x6112,0xA002,0x6111,0x6110,0xA010,0xA008,0xA004,0xA002,0x610C,0x610B,0xA002,0x610A,0x6107,0xA004,0xA002,
+ 0x6105,0x6104,0xA002,0x6103,0x6102,0xA008,0xA004,0xA002,0x60FF,0x60FE,0xA002,0x60FD,0x60FC,0xA004,0xA002,0x60FB,
+ 0x60F8,0xA002,0x60F7,0x60F5,0xA07E,0xA03E,0xA01E,0xA00E,0xA006,0xA002,0x60F2,0xA002,0x60F1,0x60EA,0xA004,0xA002,
+ 0x60E5,0x60E4,0xA002,0x60E3,0x60E2,0xA008,0xA004,0xA002,0x60E1,0x60DE,0xA002,0x60DB,0x60D9,0xA004,0xA002,0x60D7,
+ 0x60D6,0xA002,0x60D4,0x60D3,0xA010,0xA008,0xA004,0xA002,0x60D2,0x60D0,0xA002,0x60CF,0x60CE,0xA004,0xA002,0x60CD,
+ 0x60CC,0xA002,0x60C9,0x60C8,0xA008,0xA004,0xA002,0x60C7,0x60C4,0xA002,0x60C3,0x60C2,0xA004,0xA002,0x60C1,0x60C0,
+ 0xA002,0x60BF,0x60BE,0xA020,0xA010,0xA008,0xA004,0xA002,0x60BD,0x60BA,0xA002,0x60B9,0x60B7,0xA004,0xA002,0x60B6,
+ 0x60B5,0xA002,0x60B3,0x60B0,0xA008,0xA004,0xA002,0x60AE,0x60AA,0xA002,0x60A9,0x60A7,0xA004,0xA002,0x60A5,0x60A4,
+ 0xA002,0x60A2,0x60A1,0xA010,0xA008,0xA004,0xA002,0x609E,0x609C,0xA002,0x6099,0x6098,0xA004,0xA002,0x6097,0x6095,
+ 0xA002,0x6093,0x6091,0xA008,0xA004,0xA002,0x6090,0x608F,0xA002,0x608E,0x608B,0xA004,0xA002,0x608A,0x6088,0xA002,
+ 0x6087,0x6086,0xA03F,0xA01F,0xA00F,0xA007,0xA004,0xA002,0x6085,0x6082,0xA000,0x6081,0xA004,0xA002,0x6080,0x607E,
+ 0xA002,0x6077,0x6075,0xA008,0xA004,0xA002,0x6074,0x6072,0xA002,0x6071,0x606E,0xA004,0xA002,0x6066,0x6065,0xA002,
+ 0x6061,0x6060,0xA010,0xA008,0xA004,0xA002,0x605F,0x605E,0xA002,0x605C,0x605B,0xA004,0xA002,0x6058,0x6057,0xA002,
+ 0x6056,0x6054,0xA008,0xA004,0xA002,0x6053,0x6051,0xA002,0x604F,0x604E,0xA004,0xA002,0x604C,0x604A,0xA002,0x6049,
+ 0x6048,0xA020,0xA010,0xA008,0xA004,0xA002,0x6047,0x6046,0xA002,0x6045,0x6044,0xA004,0xA002,0x6040,0x603E,0xA002,
+ 0x603D,0x603A,0xA008,0xA004,0xA002,0x6039,0x6038,0xA002,0x6037,0x6036,0xA004,0xA002,0x6034,0x6033,0xA002,0x6032,
+ 0x6031,0xA010,0xA008,0xA004,0xA002,0x6030,0x602E,0xA002,0x602D,0x602C,0xA004,0xA002,0x6024,0x6023,0xA002,0x6022,
+ 0x601F,0xA008,0xA004,0xA002,0x601E,0x601A,0xA002,0x6018,0x6017,0xA004,0xA002,0x6013,0x6011,0xA002,0x6010,0x600C,
+ 0xA082,0xA00A,0xA000,0xA000,0xA000,0xA000,0xA002,0x600B,0xA002,0x6009,0x6008,0xA038,0xA018,0xA008,0xA001,0xA003,
+ 0xA001,0x6007,0xA002,0x5FFC,0x5FFA,0xA008,0xA004,0xA002,0x5FF9,0x5FF7,0xA002,0x5FF6,0x5FF4,0xA004,0xA002,0x5FF3,
+ 0x5FF2,0xA002,0x5FF0,0x5FEF,0xA010,0xA008,0xA004,0xA002,0x5FEC,0x5FE9,0xA002,0x5FE8,0x5FE6,0xA004,0xA002,0x5FE5,
+ 0x5FE3,0xA002,0x5FE2,0x5FDF,0xA008,0xA004,0xA002,0x5FDE,0x5FDC,0xA002,0x5FDB,0x5FDA,0xA004,0xA002,0x5FD5,0x5FD4,
+ 0xA002,0x5FD3,0x5FCE,0xA020,0xA010,0xA008,0xA004,0xA002,0x5FCB,0x5FCA,0xA002,0x5FC8,0x5FC7,0xA004,0xA002,0x5FC2,
+ 0x5FC1,0xA002,0x5FC0,0x5FBF,0xA008,0xA004,0xA002,0x5FBE,0x5FBB,0xA002,0x5FBA,0x5FB9,0xA004,0xA002,0x5FB8,0x5FB6,
+ 0xA002,0x5FB4,0x5FB3,0xA010,0xA008,0xA004,0xA002,0x5FB2,0x5FB1,0xA002,0x5FB0,0x5FAF,0xA004,0xA002,0x5FAC,0x5FAB,
+ 0xA002,0x5FA9,0x5FA7,0xA008,0xA004,0xA002,0x5FA6,0x5FA5,0xA002,0x5FA4,0x5FA3,0xA004,0xA002,0x5FA2,0x5FA0,0xA002,
+ 0x5F9F,0x5F9E,0xA07E,0xA03E,0xA01E,0xA00E,0xA006,0xA002,0x5F9D,0xA002,0x5F9B,0x5F9A,0xA004,0xA002,0x5F96,0x5F94,
+ 0xA002,0x5F93,0x5F91,0xA008,0xA004,0xA002,0x5F8F,0x5F8E,0xA002,0x5F8D,0x5F86,0xA004,0xA002,0x5F83,0x5F7F,0xA002,
+ 0x5F7E,0x5F7D,0xA010,0xA008,0xA004,0xA002,0x5F7A,0x5F78,0xA002,0x5F76,0x5F75,0xA004,0xA002,0x5F74,0x5F72,0xA002,
+ 0x5F6F,0x5F6E,0xA008,0xA004,0xA002,0x5F6B,0x5F68,0xA002,0x5F67,0x5F65,0xA004,0xA002,0x5F63,0x5F60,0xA002,0x5F5F,
+ 0x5F5E,0xA020,0xA010,0xA008,0xA004,0xA002,0x5F5C,0x5F5B,0xA002,0x5F5A,0x5F59,0xA004,0xA002,0x5F54,0x5F51,0xA002,
+ 0x5F4F,0x5F4E,0xA008,0xA004,0xA002,0x5F4D,0x5F4C,0xA002,0x5F4B,0x5F4A,0xA004,0xA002,0x5F49,0x5F48,0xA002,0x5F47,
+ 0x5F46,0xA010,0xA008,0xA004,0xA002,0x5F45,0x5F44,0xA002,0x5F43,0x5F42,0xA004,0xA002,0x5F41,0x5F3F,0xA002,0x5F3E,
+ 0x5F3D,0xA008,0xA004,0xA002,0x5F3B,0x5F38,0xA002,0x5F37,0x5F36,0xA004,0xA002,0x5F35,0x5F34,0xA002,0x5F33,0x5F32,
+ 0xA03F,0xA01F,0xA00F,0xA008,0xA004,0xA002,0x5F30,0x5F2E,0xA002,0x5F2C,0x5F2B,0xA003,0xA000,0x5F28,0xA002,0x5F24,
+ 0x5F23,0xA008,0xA004,0xA002,0x5F22,0x5F21,0xA002,0x5F1E,0x5F1D,0xA004,0xA002,0x5F1C,0x5F1A,0xA002,0x5F19,0x5F16,
+ 0xA010,0xA008,0xA004,0xA002,0x5F14,0x5F12,0xA002,0x5F10,0x5F0E,0xA004,0xA002,0x5F0D,0x5F0C,0xA002,0x5F09,0x5F07,
+ 0xA008,0xA004,0xA002,0x5F06,0x5F05,0xA002,0x5EFD,0x5EFC,0xA004,0xA002,0x5EFB,0x5EF9,0xA002,0x5EF8,0x5EF5,0xA020,
+ 0xA010,0xA008,0xA004,0xA002,0x5EF3,0x5EF2,0xA002,0x5EF1,0x5EF0,0xA004,0xA002,0x5EEF,0x5EEE,0xA002,0x5EED,0x5EEC,
+ 0xA008,0xA004,0xA002,0x5EEB,0x5EE9,0xA002,0x5EE7,0x5EE6,0xA004,0xA002,0x5EE5,0x5EE4,0xA002,0x5EE3,0x5EE2,0xA010,
+ 0xA008,0xA004,0xA002,0x5EE1,0x5EE0,0xA002,0x5EDF,0x5EDE,0xA004,0xA002,0x5EDD,0x5EDC,0xA002,0x5EDA,0x5ED9,0xA008,
+ 0xA004,0xA002,0x5ED8,0x5ED7,0xA002,0x5ED5,0x5ED4,0xA004,0xA002,0x5ED0,0x5ECF,0xA002,0x5ECE,0x5ECD,0xABF6,0xA5FB,
+ 0xA2FC,0xA17C,0xA07F,0xA00E,0xA000,0xA000,0xA000,0xA006,0xA002,0x5ECC,0xA002,0x5ECB,0x5EC8,0xA000,0xA002,0x5EC7,
+ 0x5EC6,0xA031,0xA011,0xA001,0xA008,0xA004,0xA002,0x5EC5,0x5EC4,0xA002,0x5EC3,0x5EC2,0xA004,0xA002,0x5EC1,0x5EC0,
+ 0xA002,0x5EBF,0x5EBD,0xA010,0xA008,0xA004,0xA002,0x5EBC,0x5EBB,0xA002,0x5EBA,0x5EB4,0xA004,0xA002,0x5EB2,0x5EB1,
+ 0xA002,0x5EB0,0x5EAF,0xA008,0xA004,0xA002,0x5EAE,0x5EAC,0xA002,0x5EAB,0x5EAA,0xA004,0xA002,0x5EA9,0x5EA8,0xA002,
+ 0x5EA4,0x5EA3,0xA020,0xA010,0xA008,0xA004,0xA002,0x5EA2,0x5EA1,0xA002,0x5E9D,0x5E9B,0xA004,0xA002,0x5E98,0x5E92,
+ 0xA002,0x5E8E,0x5E8D,0xA008,0xA004,0xA002,0x5E8C,0x5E89,0xA002,0x5E88,0x5E85,0xA004,0xA002,0x5E83,0x5E82,0xA002,
+ 0x5E81,0x5E7E,0xA010,0xA008,0xA004,0xA002,0x5E79,0x5E77,0xA002,0x5E75,0x5E71,0xA004,0xA002,0x5E70,0x5E6F,0xA002,
+ 0x5E6E,0x5E6D,0xA008,0xA004,0xA002,0x5E6C,0x5E6B,0xA002,0x5E6A,0x5E69,0xA004,0xA002,0x5E68,0x5E67,0xA002,0x5E66,
+ 0x5E65,0xA07E,0xA03E,0xA01E,0xA00E,0xA006,0xA002,0x5E64,0xA002,0x5E63,0x5E60,0xA004,0xA002,0x5E5F,0x5E5D,0xA002,
+ 0x5E5C,0x5E5A,0xA008,0xA004,0xA002,0x5E59,0x5E58,0xA002,0x5E57,0x5E56,0xA004,0xA002,0x5E53,0x5E52,0xA002,0x5E51,
+ 0x5E50,0xA010,0xA008,0xA004,0xA002,0x5E4F,0x5E4E,0xA002,0x5E4D,0x5E4B,0xA004,0xA002,0x5E4A,0x5E49,0xA002,0x5E48,
+ 0x5E47,0xA008,0xA004,0xA002,0x5E46,0x5E43,0xA002,0x5E41,0x5E40,0xA004,0xA002,0x5E3F,0x5E3E,0xA002,0x5E3A,0x5E39,
+ 0xA020,0xA010,0xA008,0xA004,0xA002,0x5E36,0x5E35,0xA002,0x5E34,0x5E33,0xA004,0xA002,0x5E32,0x5E30,0xA002,0x5E2F,
+ 0x5E2C,0xA008,0xA004,0xA002,0x5E2B,0x5E2A,0xA002,0x5E29,0x5E28,0xA004,0xA002,0x5E25,0x5E24,0xA002,0x5E23,0x5E22,
+ 0xA010,0xA008,0xA004,0xA002,0x5E21,0x5E20,0xA002,0x5E1F,0x5E1E,0xA004,0xA002,0x5E17,0x5E13,0xA002,0x5E12,0x5E0E,
+ 0xA008,0xA004,0xA002,0x5E0D,0x5E0B,0xA002,0x5E0A,0x5E09,0xA004,0xA002,0x5E07,0x5E04,0xA002,0x5E00,0x5DFF,0xA03F,
+ 0xA01F,0xA010,0xA008,0xA004,0xA002,0x5DFC,0x5DFB,0xA002,0x5DFA,0x5DF9,0xA004,0xA002,0x5DF8,0x5DF6,0xA002,0x5DF5,
+ 0x5DF0,0xA007,0xA003,0xA001,0x5DED,0xA002,0x5DEC,0x5DEA,0xA004,0xA002,0x5DE4,0x5DE3,0xA002,0x5DE0,0x5DDF,0xA010,
+ 0xA008,0xA004,0xA002,0x5DDC,0x5DDA,0xA002,0x5DD9,0x5DD8,0xA004,0xA002,0x5DD7,0x5DD6,0xA002,0x5DD5,0x5DD4,0xA008,
+ 0xA004,0xA002,0x5DD3,0x5DD2,0xA002,0x5DD1,0x5DD0,0xA004,0xA002,0x5DCF,0x5DCE,0xA002,0x5DCC,0x5DCB,0xA020,0xA010,
+ 0xA008,0xA004,0xA002,0x5DCA,0x5DC9,0xA002,0x5DC8,0x5DC7,0xA004,0xA002,0x5DC6,0x5DC4,0xA002,0x5DC3,0x5DC2,0xA008,
+ 0xA004,0xA002,0x5DC1,0x5DC0,0xA002,0x5DBF,0x5DBE,0xA004,0xA002,0x5DBD,0x5DBC,0xA002,0x5DBB,0x5DBA,0xA010,0xA008,
+ 0xA004,0xA002,0x5DB9,0x5DB8,0xA002,0x5DB6,0x5DB5,0xA004,0xA002,0x5DB4,0x5DB3,0xA002,0x5DB2,0x5DB1,0xA008,0xA004,
+ 0xA002,0x5DB0,0x5DAF,0xA002,0x5DAE,0x5DAD,0xA004,0xA002,0x5DAC,0x5DAB,0xA002,0x5DAA,0x5DA9,0xA083,0xA015,0xA000,
+ 0xA000,0xA00E,0xA006,0xA002,0x5DA8,0xA002,0x5DA7,0x5DA6,0xA004,0xA002,0x5DA5,0x5DA4,0xA002,0x5DA3,0x5DA2,0xA000,
+ 0xA000,0xA000,0x5DA1,0xA02E,0xA00E,0xA001,0xA005,0xA001,0xA002,0x5DA0,0x5D9F,0xA004,0xA002,0x5D9E,0x5D9C,0xA002,
+ 0x5D9B,0x5D9A,0xA010,0xA008,0xA004,0xA002,0x5D98,0x5D97,0xA002,0x5D96,0x5D95,0xA004,0xA002,0x5D94,0x5D93,0xA002,
+ 0x5D92,0x5D91,0xA008,0xA004,0xA002,0x5D90,0x5D8F,0xA002,0x5D8E,0x5D8D,0xA004,0xA002,0x5D8C,0x5D8B,0xA002,0x5D8A,
+ 0x5D89,0xA020,0xA010,0xA008,0xA004,0xA002,0x5D88,0x5D87,0xA002,0x5D86,0x5D85,0xA004,0xA002,0x5D84,0x5D83,0xA002,
+ 0x5D81,0x5D80,0xA008,0xA004,0xA002,0x5D7F,0x5D7E,0xA002,0x5D7D,0x5D7C,0xA004,0xA002,0x5D7B,0x5D7A,0xA002,0x5D79,
+ 0x5D78,0xA010,0xA008,0xA004,0xA002,0x5D77,0x5D76,0xA002,0x5D75,0x5D73,0xA004,0xA002,0x5D72,0x5D71,0xA002,0x5D70,
+ 0x5D6E,0xA008,0xA004,0xA002,0x5D6D,0x5D6A,0xA002,0x5D68,0x5D67,0xA004,0xA002,0x5D66,0x5D65,0xA002,0x5D64,0x5D63,
+ 0xA07E,0xA03E,0xA01E,0xA00E,0xA006,0xA002,0x5D62,0xA002,0x5D61,0x5D60,0xA004,0xA002,0x5D5F,0x5D5E,0xA002,0x5D5C,
+ 0x5D5A,0xA008,0xA004,0xA002,0x5D59,0x5D57,0xA002,0x5D56,0x5D55,0xA004,0xA002,0x5D54,0x5D53,0xA002,0x5D52,0x5D51,
+ 0xA010,0xA008,0xA004,0xA002,0x5D50,0x5D4F,0xA002,0x5D4E,0x5D4D,0xA004,0xA002,0x5D49,0x5D48,0xA002,0x5D46,0x5D45,
+ 0xA008,0xA004,0xA002,0x5D44,0x5D43,0xA002,0x5D42,0x5D41,0xA004,0xA002,0x5D40,0x5D3F,0xA002,0x5D3C,0x5D3B,0xA020,
+ 0xA010,0xA008,0xA004,0xA002,0x5D3A,0x5D39,0xA002,0x5D38,0x5D37,0xA004,0xA002,0x5D36,0x5D35,0xA002,0x5D33,0x5D32,
+ 0xA008,0xA004,0xA002,0x5D31,0x5D30,0xA002,0x5D2F,0x5D2C,0xA004,0xA002,0x5D2B,0x5D2A,0xA002,0x5D28,0x5D25,0xA010,
+ 0xA008,0xA004,0xA002,0x5D23,0x5D22,0xA002,0x5D21,0x5D20,0xA004,0xA002,0x5D1F,0x5D1D,0xA002,0x5D1C,0x5D1A,0xA008,
+ 0xA004,0xA002,0x5D19,0x5D18,0xA002,0x5D17,0x5D15,0xA004,0xA002,0x5D13,0x5D12,0xA002,0x5D11,0x5D10,0xA03F,0xA01F,
+ 0xA010,0xA008,0xA004,0xA002,0x5D0F,0x5D0D,0xA002,0x5D0C,0x5D0B,0xA004,0xA002,0x5D0A,0x5D09,0xA002,0x5D08,0x5D05,
+ 0xA007,0xA004,0xA002,0x5D04,0x5D01,0xA001,0x5D00,0xA004,0xA002,0x5CFF,0x5CFE,0xA002,0x5CFD,0x5CFC,0xA010,0xA008,
+ 0xA004,0xA002,0x5CFA,0x5CF9,0xA002,0x5CF8,0x5CF7,0xA004,0xA002,0x5CF6,0x5CF5,0xA002,0x5CF4,0x5CF3,0xA008,0xA004,
+ 0xA002,0x5CF2,0x5CF1,0xA002,0x5CEF,0x5CEE,0xA004,0xA002,0x5CEC,0x5CEB,0xA002,0x5CE9,0x5CE7,0xA020,0xA010,0xA008,
+ 0xA004,0xA002,0x5CE3,0x5CE2,0xA002,0x5CE0,0x5CDF,0xA004,0xA002,0x5CDE,0x5CDD,0xA002,0x5CDC,0x5CDB,0xA008,0xA004,
+ 0xA002,0x5CDA,0x5CD8,0xA002,0x5CD7,0x5CD6,0xA004,0xA002,0x5CD5,0x5CD4,0xA002,0x5CD3,0x5CD1,0xA010,0xA008,0xA004,
+ 0xA002,0x5CD0,0x5CCF,0xA002,0x5CCE,0x5CCD,0xA004,0xA002,0x5CCC,0x5CCA,0xA002,0x5CC9,0x5CC8,0xA008,0xA004,0xA002,
+ 0x5CC7,0x5CC6,0xA002,0x5CC5,0x5CC3,0xA004,0xA002,0x5CC2,0x5CC0,0xA002,0x5CBE,0x5CBC,0xA17F,0xA082,0xA018,0xA000,
+ 0xA000,0xA00E,0xA006,0xA002,0x5CBB,0xA002,0x5CBA,0x5CB9,0xA004,0xA002,0x5CB6,0x5CB4,0xA002,0x5CB2,0x5CB0,0xA000,
+ 0xA004,0xA002,0x5CAF,0x5CAE,0xA000,0x5CAA,0xA02A,0xA00A,0xA001,0xA001,0xA004,0xA002,0x5CA8,0x5CA7,0xA002,0x5CA6,
+ 0x5CA5,0xA010,0xA008,0xA004,0xA002,0x5CA4,0x5CA1,0xA002,0x5CA0,0x5C9F,0xA004,0xA002,0x5C9E,0x5C9D,0xA002,0x5C95,
+ 0x5C93,0xA008,0xA004,0xA002,0x5C92,0x5C8F,0xA002,0x5C8E,0x5C8B,0xA004,0xA002,0x5C8A,0x5C89,0xA002,0x5C87,0x5C86,
+ 0xA020,0xA010,0xA008,0xA004,0xA002,0x5C85,0x5C84,0xA002,0x5C83,0x5C80,0xA004,0xA002,0x5C7E,0x5C7D,0xA002,0x5C7C,
+ 0x5C7B,0xA008,0xA004,0xA002,0x5C78,0x5C77,0xA002,0x5C76,0x5C75,0xA004,0xA002,0x5C74,0x5C73,0xA002,0x5C72,0x5C70,
+ 0xA010,0xA008,0xA004,0xA002,0x5C6D,0x5C6C,0xA002,0x5C6B,0x5C6A,0xA004,0xA002,0x5C69,0x5C68,0xA002,0x5C67,0x5C64,
+ 0xA008,0xA004,0xA002,0x5C62,0x5C5F,0xA002,0x5C5D,0x5C5C,0xA004,0xA002,0x5C5B,0x5C5A,0xA002,0x5C58,0x5C57,0xA07E,
+ 0xA03E,0xA01E,0xA00E,0xA006,0xA002,0x5C56,0xA002,0x5C54,0x5C53,0xA004,0xA002,0x5C52,0x5C4D,0xA002,0x5C4C,0x5C47,
+ 0xA008,0xA004,0xA002,0x5C46,0x5C44,0xA002,0x5C43,0x5C37,0xA004,0xA002,0x5C36,0x5C35,0xA002,0x5C33,0x5C32,0xA010,
+ 0xA008,0xA004,0xA002,0x5C30,0x5C2F,0xA002,0x5C2E,0x5C2D,0xA004,0xA002,0x5C2B,0x5C2A,0xA002,0x5C29,0x5C28,0xA008,
+ 0xA004,0xA002,0x5C26,0x5C23,0xA002,0x5C21,0x5C20,0xA004,0xA002,0x5C1F,0x5C1E,0xA002,0x5C1B,0x5C19,0xA020,0xA010,
+ 0xA008,0xA004,0xA002,0x5C17,0x5C13,0xA002,0x5C12,0x5C10,0xA004,0xA002,0x5C0E,0x5C0D,0xA002,0x5C0C,0x5C0B,0xA008,
+ 0xA004,0xA002,0x5C08,0x5C07,0xA002,0x5C05,0x5C03,0xA004,0xA002,0x5C02,0x5C00,0xA002,0x5BFE,0x5BFD,0xA010,0xA008,
+ 0xA004,0xA002,0x5BF7,0x5BF6,0xA002,0x5BF5,0x5BF4,0xA004,0xA002,0x5BF3,0x5BF2,0xA002,0x5BF1,0x5BEF,0xA008,0xA004,
+ 0xA002,0x5BED,0x5BEC,0xA002,0x5BEB,0x5BEA,0xA004,0xA002,0x5BE9,0x5BE7,0xA002,0x5BE6,0x5BE3,0xA03F,0xA01F,0xA010,
+ 0xA008,0xA004,0xA002,0x5BE2,0x5BE0,0xA002,0x5BDC,0x5BDB,0xA004,0xA002,0x5BDA,0x5BD9,0xA002,0x5BD8,0x5BD7,0xA008,
+ 0xA004,0xA002,0x5BD6,0x5BD5,0xA002,0x5BD4,0x5BD1,0xA003,0xA001,0x5BCF,0xA002,0x5BCE,0x5BCD,0xA010,0xA008,0xA004,
+ 0xA002,0x5BCB,0x5BCA,0xA002,0x5BC9,0x5BC8,0xA004,0xA002,0x5BC3,0x5BC1,0xA002,0x5BC0,0x5BBC,0xA008,0xA004,0xA002,
+ 0x5BBB,0x5BBA,0xA002,0x5BB7,0x5BB2,0xA004,0xA002,0x5BB1,0x5BAF,0xA002,0x5BAE,0x5BAD,0xA020,0xA010,0xA008,0xA004,
+ 0xA002,0x5BAC,0x5BA9,0xA002,0x5BA8,0x5BA7,0xA004,0xA002,0x5B9F,0x5B96,0xA002,0x5B94,0x5B92,0xA008,0xA004,0xA002,
+ 0x5B91,0x5B90,0xA002,0x5B8E,0x5B8D,0xA004,0xA002,0x5B8A,0x5B86,0xA002,0x5B82,0x5B7F,0xA010,0xA008,0xA004,0xA002,
+ 0x5B7E,0x5B7C,0xA002,0x5B7B,0x5B79,0xA004,0xA002,0x5B78,0x5B77,0xA002,0x5B76,0x5B74,0xA008,0xA004,0xA002,0x5B72,
+ 0x5B6F,0xA002,0x5B6E,0x5B6D,0xA004,0xA002,0x5B6B,0x5B68,0xA002,0x5B67,0x5B61,0xA083,0xA01C,0xA000,0xA000,0xA00E,
+ 0xA006,0xA002,0x5B60,0xA002,0x5B5E,0x5B56,0xA004,0xA002,0x5B52,0x5B4F,0xA002,0x5B4E,0x5B4D,0xA008,0xA004,0xA002,
+ 0x5B4C,0x5B4B,0xA002,0x5B4A,0x5B49,0xA000,0xA000,0x5B48,0xA027,0xA007,0xA001,0xA001,0xA001,0xA002,0x5B47,0x5B46,
+ 0xA010,0xA008,0xA004,0xA002,0x5B45,0x5B44,0xA002,0x5B43,0x5B42,0xA004,0xA002,0x5B41,0x5B3F,0xA002,0x5B3E,0x5B3D,
+ 0xA008,0xA004,0xA002,0x5B3C,0x5B3B,0xA002,0x5B3A,0x5B39,0xA004,0xA002,0x5B38,0x5B36,0xA002,0x5B35,0x5B33,0xA020,
+ 0xA010,0xA008,0xA004,0xA002,0x5B31,0x5B30,0xA002,0x5B2F,0x5B2E,0xA004,0xA002,0x5B2D,0x5B2C,0xA002,0x5B2B,0x5B2A,
+ 0xA008,0xA004,0xA002,0x5B29,0x5B28,0xA002,0x5B27,0x5B26,0xA004,0xA002,0x5B25,0x5B24,0xA002,0x5B23,0x5B22,0xA010,
+ 0xA008,0xA004,0xA002,0x5B21,0x5B20,0xA002,0x5B1F,0x5B1E,0xA004,0xA002,0x5B1D,0x5B1C,0xA002,0x5B1B,0x5B1A,0xA008,
+ 0xA004,0xA002,0x5B19,0x5B18,0xA002,0x5B15,0x5B14,0xA004,0xA002,0x5B13,0x5B12,0xA002,0x5B11,0x5B10,0xA07E,0xA03E,
+ 0xA01E,0xA00E,0xA006,0xA002,0x5B0F,0xA002,0x5B0E,0x5B0D,0xA004,0xA002,0x5B0C,0x5B0B,0xA002,0x5B0A,0x5B08,0xA008,
+ 0xA004,0xA002,0x5B07,0x5B06,0xA002,0x5B05,0x5B04,0xA004,0xA002,0x5B03,0x5B02,0xA002,0x5B01,0x5B00,0xA010,0xA008,
+ 0xA004,0xA002,0x5AFF,0x5AFE,0xA002,0x5AFD,0x5AFC,0xA004,0xA002,0x5AFB,0x5AFA,0xA002,0x5AF9,0x5AF8,0xA008,0xA004,
+ 0xA002,0x5AF7,0x5AF6,0xA002,0x5AF5,0x5AF4,0xA004,0xA002,0x5AF3,0x5AF2,0xA002,0x5AF0,0x5AEF,0xA020,0xA010,0xA008,
+ 0xA004,0xA002,0x5AEE,0x5AED,0xA002,0x5AEC,0x5AEA,0xA004,0xA002,0x5AE8,0x5AE7,0xA002,0x5AE5,0x5AE4,0xA008,0xA004,
+ 0xA002,0x5AE2,0x5ADF,0xA002,0x5ADE,0x5ADD,0xA004,0xA002,0x5ADB,0x5ADA,0xA002,0x5AD9,0x5AD7,0xA010,0xA008,0xA004,
+ 0xA002,0x5AD5,0x5AD3,0xA002,0x5AD1,0x5AD0,0xA004,0xA002,0x5ACF,0x5ACE,0xA002,0x5ACD,0x5ACB,0xA008,0xA004,0xA002,
+ 0x5ACA,0x5AC8,0xA002,0x5AC7,0x5AC6,0xA004,0xA002,0x5AC5,0x5AC4,0xA002,0x5AC3,0x5AC0,0xA03F,0xA01F,0xA010,0xA008,
+ 0xA004,0xA002,0x5ABF,0x5ABD,0xA002,0x5ABC,0x5ABB,0xA004,0xA002,0x5ABA,0x5AB9,0xA002,0x5AB7,0x5AB6,0xA008,0xA004,
+ 0xA002,0x5AB4,0x5AB1,0xA002,0x5AB0,0x5AAF,0xA004,0xA002,0x5AAE,0x5AAD,0xA001,0x5AAC,0xA010,0xA008,0xA004,0xA002,
+ 0x5AAB,0x5AA9,0xA002,0x5AA8,0x5AA7,0xA004,0xA002,0x5AA6,0x5AA5,0xA002,0x5AA4,0x5AA3,0xA008,0xA004,0xA002,0x5AA2,
+ 0x5AA1,0xA002,0x5AA0,0x5A9F,0xA004,0xA002,0x5A9E,0x5A9D,0xA002,0x5A9C,0x5A99,0xA020,0xA010,0xA008,0xA004,0xA002,
+ 0x5A98,0x5A97,0xA002,0x5A96,0x5A95,0xA004,0xA002,0x5A94,0x5A93,0xA002,0x5A91,0x5A90,0xA008,0xA004,0xA002,0x5A8F,
+ 0x5A8E,0xA002,0x5A8D,0x5A8C,0xA004,0xA002,0x5A8B,0x5A8A,0xA002,0x5A89,0x5A88,0xA010,0xA008,0xA004,0xA002,0x5A87,
+ 0x5A86,0xA002,0x5A85,0x5A84,0xA004,0xA002,0x5A83,0x5A82,0xA002,0x5A81,0x5A80,0xA008,0xA004,0xA002,0x5A7E,0x5A7D,
+ 0xA002,0x5A7C,0x5A7B,0xA004,0xA002,0x5A79,0x5A78,0xA002,0x5A73,0x5A72,0xA2FC,0xA17C,0xA07F,0xA01F,0xA000,0xA000,
+ 0xA00E,0xA006,0xA002,0x5A71,0xA002,0x5A70,0x5A6F,0xA004,0xA002,0x5A6E,0x5A6D,0xA002,0x5A6C,0x5A6B,0xA008,0xA004,
+ 0xA002,0x5A69,0x5A68,0xA002,0x5A66,0x5A65,0xA004,0xA002,0x5A64,0x5A63,0xA000,0x5A61,0xA020,0xA001,0xA00F,0xA007,
+ 0xA003,0xA001,0x5A60,0xA002,0x5A5F,0x5A5E,0xA004,0xA002,0x5A5D,0x5A5C,0xA002,0x5A5B,0x5A59,0xA008,0xA004,0xA002,
+ 0x5A58,0x5A57,0xA002,0x5A56,0x5A54,0xA004,0xA002,0x5A53,0x5A52,0xA002,0x5A51,0x5A50,0xA020,0xA010,0xA008,0xA004,
+ 0xA002,0x5A4F,0x5A4E,0xA002,0x5A4D,0x5A4C,0xA004,0xA002,0x5A4B,0x5A48,0xA002,0x5A47,0x5A45,0xA008,0xA004,0xA002,
+ 0x5A44,0x5A43,0xA002,0x5A42,0x5A41,0xA004,0xA002,0x5A3F,0x5A3E,0xA002,0x5A3D,0x5A3B,0xA010,0xA008,0xA004,0xA002,
+ 0x5A3A,0x5A39,0xA002,0x5A38,0x5A37,0xA004,0xA002,0x5A35,0x5A33,0xA002,0x5A30,0x5A2F,0xA008,0xA004,0xA002,0x5A2E,
+ 0x5A2D,0xA002,0x5A2C,0x5A2B,0xA004,0xA002,0x5A2A,0x5A28,0xA002,0x5A27,0x5A26,0xA07E,0xA03E,0xA01E,0xA00E,0xA006,
+ 0xA002,0x5A24,0xA002,0x5A22,0x5A21,0xA004,0xA002,0x5A1E,0x5A1D,0xA002,0x5A1B,0x5A1A,0xA008,0xA004,0xA002,0x5A19,
+ 0x5A17,0xA002,0x5A16,0x5A15,0xA004,0xA002,0x5A14,0x5A12,0xA002,0x5A10,0x5A0F,0xA010,0xA008,0xA004,0xA002,0x5A0E,
+ 0x5A0D,0xA002,0x5A0B,0x5A0A,0xA004,0xA002,0x5A02,0x5A00,0xA002,0x59FE,0x59FD,0xA008,0xA004,0xA002,0x59FC,0x59FA,
+ 0xA002,0x59F8,0x59F7,0xA004,0xA002,0x59F6,0x59F5,0xA002,0x59F4,0x59F3,0xA020,0xA010,0xA008,0xA004,0xA002,0x59F2,
+ 0x59F1,0xA002,0x59F0,0x59EF,0xA004,0xA002,0x59EE,0x59ED,0xA002,0x59EB,0x59EA,0xA008,0xA004,0xA002,0x59E9,0x59E7,
+ 0xA002,0x59E6,0x59E4,0xA004,0xA002,0x59E2,0x59E1,0xA002,0x59E0,0x59DF,0xA010,0xA008,0xA004,0xA002,0x59DE,0x59DB,
+ 0xA002,0x59D9,0x59D6,0xA004,0xA002,0x59D5,0x59CF,0xA002,0x59CE,0x59CD,0xA008,0xA004,0xA002,0x59CC,0x59C9,0xA002,
+ 0x59C8,0x59C7,0xA004,0xA002,0x59C5,0x59C4,0xA002,0x59C3,0x59C2,0xA03F,0xA020,0xA010,0xA008,0xA004,0xA002,0x59C1,
+ 0x59C0,0xA002,0x59BF,0x59BD,0xA004,0xA002,0x59BC,0x59BA,0xA002,0x59B8,0x59B7,0xA008,0xA004,0xA002,0x59B6,0x59B5,
+ 0xA002,0x59B4,0x59B3,0xA004,0xA002,0x59B1,0x59B0,0xA002,0x59AD,0x59AC,0xA00F,0xA007,0xA003,0xA000,0x59A7,0xA002,
+ 0x59A6,0x59A2,0xA004,0xA002,0x59A1,0x59A0,0xA002,0x599F,0x599D,0xA008,0xA004,0xA002,0x599C,0x599B,0xA002,0x599A,
+ 0x5998,0xA004,0xA002,0x5995,0x5994,0xA002,0x5991,0x5990,0xA020,0xA010,0xA008,0xA004,0xA002,0x598F,0x598E,0xA002,
+ 0x598C,0x598B,0xA004,0xA002,0x5989,0x5985,0xA002,0x5980,0x597F,0xA008,0xA004,0xA002,0x597E,0x597C,0xA002,0x597B,
+ 0x597A,0xA004,0xA002,0x5977,0x5975,0xA002,0x5972,0x5971,0xA010,0xA008,0xA004,0xA002,0x5970,0x596F,0xA002,0x596E,
+ 0x596D,0xA004,0xA002,0x596C,0x596B,0xA002,0x596A,0x5969,0xA008,0xA004,0xA002,0x5968,0x5967,0xA002,0x5966,0x5964,
+ 0xA004,0xA002,0x5963,0x5961,0xA002,0x595F,0x595E,0xA083,0xA026,0xA000,0xA01E,0xA00E,0xA006,0xA002,0x595D,0xA002,
+ 0x595C,0x595B,0xA004,0xA002,0x5959,0x5953,0xA002,0x5952,0x5950,0xA008,0xA004,0xA002,0x594D,0x594C,0xA002,0x594A,
+ 0x5946,0xA004,0xA002,0x5945,0x5943,0xA002,0x5940,0x593F,0xA000,0xA000,0xA000,0xA002,0x593E,0x593D,0xA01D,0xA001,
+ 0xA00C,0xA004,0xA001,0xA001,0x593B,0xA004,0xA002,0x5936,0x5935,0xA002,0x5933,0x5932,0xA008,0xA004,0xA002,0x5930,
+ 0x592C,0xA002,0x5928,0x5926,0xA004,0xA002,0x5923,0x5922,0xA002,0x5921,0x5920,0xA020,0xA010,0xA008,0xA004,0xA002,
+ 0x591E,0x591D,0xA002,0x591B,0x5918,0xA004,0xA002,0x5917,0x5913,0xA002,0x5912,0x5911,0xA008,0xA004,0xA002,0x5910,
+ 0x590E,0xA002,0x590C,0x590B,0xA004,0xA002,0x590A,0x5909,0xA002,0x5908,0x5906,0xA010,0xA008,0xA004,0xA002,0x5905,
+ 0x5903,0xA002,0x5901,0x5900,0xA004,0xA002,0x58FF,0x58FE,0xA002,0x58FD,0x58FC,0xA008,0xA004,0xA002,0x58FB,0x58FA,
+ 0xA002,0x58F8,0x58F7,0xA004,0xA002,0x58F5,0x58F4,0xA002,0x58F2,0x58F1,0xA07E,0xA03E,0xA01E,0xA00E,0xA006,0xA002,
+ 0x58EF,0xA002,0x58ED,0x58EA,0xA004,0xA002,0x58E9,0x58E8,0xA002,0x58E7,0x58E6,0xA008,0xA004,0xA002,0x58E5,0x58E3,
+ 0xA002,0x58E2,0x58E1,0xA004,0xA002,0x58E0,0x58DF,0xA002,0x58DE,0x58DD,0xA010,0xA008,0xA004,0xA002,0x58DC,0x58DB,
+ 0xA002,0x58DA,0x58D9,0xA004,0xA002,0x58D8,0x58D7,0xA002,0x58D6,0x58D4,0xA008,0xA004,0xA002,0x58D3,0x58D2,0xA002,
+ 0x58D0,0x58CF,0xA004,0xA002,0x58CE,0x58CD,0xA002,0x58CC,0x58CB,0xA020,0xA010,0xA008,0xA004,0xA002,0x58CA,0x58C9,
+ 0xA002,0x58C8,0x58C7,0xA004,0xA002,0x58C6,0x58C4,0xA002,0x58C3,0x58C2,0xA008,0xA004,0xA002,0x58C0,0x58BF,0xA002,
+ 0x58BE,0x58BD,0xA004,0xA002,0x58BB,0x58BA,0xA002,0x58B9,0x58B8,0xA010,0xA008,0xA004,0xA002,0x58B7,0x58B6,0xA002,
+ 0x58B5,0x58B4,0xA004,0xA002,0x58B3,0x58B2,0xA002,0x58B1,0x58B0,0xA008,0xA004,0xA002,0x58AF,0x58AE,0xA002,0x58AD,
+ 0x58AC,0xA004,0xA002,0x58AB,0x58AA,0xA002,0x58A7,0x58A6,0xA03F,0xA020,0xA010,0xA008,0xA004,0xA002,0x58A5,0x58A4,
+ 0xA002,0x58A3,0x58A2,0xA004,0xA002,0x58A1,0x58A0,0xA002,0x589D,0x589C,0xA008,0xA004,0xA002,0x589B,0x5898,0xA002,
+ 0x5897,0x5896,0xA004,0xA002,0x5895,0x5894,0xA002,0x5891,0x5890,0xA00F,0xA007,0xA004,0xA002,0x588F,0x588E,0xA000,
+ 0x588D,0xA004,0xA002,0x588C,0x588B,0xA002,0x588A,0x5888,0xA008,0xA004,0xA002,0x5887,0x5886,0xA002,0x5884,0x5882,
+ 0xA004,0xA002,0x587F,0x587D,0xA002,0x587C,0x587B,0xA020,0xA010,0xA008,0xA004,0xA002,0x587A,0x5879,0xA002,0x5878,
+ 0x5877,0xA004,0xA002,0x5876,0x5875,0xA002,0x5874,0x5873,0xA008,0xA004,0xA002,0x5872,0x5871,0xA002,0x5870,0x586F,
+ 0xA004,0xA002,0x586E,0x586D,0xA002,0x586A,0x5869,0xA010,0xA008,0xA004,0xA002,0x5868,0x5867,0xA002,0x5866,0x5864,
+ 0xA004,0xA002,0x5863,0x5862,0xA002,0x5861,0x5860,0xA008,0xA004,0xA002,0x585F,0x585D,0xA002,0x585C,0x585B,0xA004,
+ 0xA002,0x585A,0x5859,0xA002,0x5857,0x5856,0xA17F,0xA082,0xA029,0xA000,0xA01E,0xA00E,0xA006,0xA002,0x5855,0xA002,
+ 0x5853,0x5852,0xA004,0xA002,0x5850,0x584F,0xA002,0x584E,0x584B,0xA008,0xA004,0xA002,0x584A,0x5849,0xA002,0x5848,
+ 0x5847,0xA004,0xA002,0x5846,0x5845,0xA002,0x5843,0x5842,0xA000,0xA000,0xA004,0xA002,0x5841,0x5840,0xA002,0x583F,
+ 0x583E,0xA019,0xA001,0xA008,0xA001,0xA003,0xA001,0x583D,0xA002,0x583C,0x583B,0xA008,0xA004,0xA002,0x583A,0x5839,
+ 0xA002,0x5838,0x5837,0xA004,0xA002,0x5836,0x5834,0xA002,0x5833,0x5832,0xA020,0xA010,0xA008,0xA004,0xA002,0x5831,
+ 0x582F,0xA002,0x582E,0x582D,0xA004,0xA002,0x582C,0x582B,0xA002,0x5829,0x5828,0xA008,0xA004,0xA002,0x5827,0x5826,
+ 0xA002,0x5825,0x5823,0xA004,0xA002,0x5822,0x581F,0xA002,0x581D,0x581C,0xA010,0xA008,0xA004,0xA002,0x581B,0x581A,
+ 0xA002,0x5818,0x5817,0xA004,0xA002,0x5816,0x5814,0xA002,0x5813,0x5812,0xA008,0xA004,0xA002,0x5810,0x580F,0xA002,
+ 0x580E,0x580C,0xA004,0xA002,0x580A,0x5809,0xA002,0x5808,0x5805,0xA07E,0xA03E,0xA01E,0xA00E,0xA006,0xA002,0x5804,
+ 0xA002,0x5803,0x5801,0xA004,0xA002,0x57FF,0x57FE,0xA002,0x57FC,0x57FB,0xA008,0xA004,0xA002,0x57F7,0x57F6,0xA002,
+ 0x57F5,0x57F3,0xA004,0xA002,0x57F2,0x57F1,0xA002,0x57F0,0x57EE,0xA010,0xA008,0xA004,0xA002,0x57EC,0x57EB,0xA002,
+ 0x57EA,0x57E9,0xA004,0xA002,0x57E8,0x57E7,0xA002,0x57E6,0x57E5,0xA008,0xA004,0xA002,0x57E3,0x57E2,0xA002,0x57E1,
+ 0x57DE,0xA004,0xA002,0x57DC,0x57DB,0xA002,0x57D7,0x57D6,0xA020,0xA010,0xA008,0xA004,0xA002,0x57D3,0x57D1,0xA002,
+ 0x57D0,0x57CD,0xA004,0xA002,0x57CC,0x57CA,0xA002,0x57C9,0x57C8,0xA008,0xA004,0xA002,0x57C7,0x57C6,0xA002,0x57C5,
+ 0x57C4,0xA004,0xA002,0x57C1,0x57C0,0xA002,0x57BF,0x57BE,0xA010,0xA008,0xA004,0xA002,0x57BD,0x57BC,0xA002,0x57BB,
+ 0x57BA,0xA004,0xA002,0x57B9,0x57B7,0xA002,0x57B6,0x57B5,0xA008,0xA004,0xA002,0x57B3,0x57B1,0xA002,0x57B0,0x57AF,
+ 0xA004,0xA002,0x57AC,0x57AA,0xA002,0x57A8,0x57A5,0xA03F,0xA020,0xA010,0xA008,0xA004,0xA002,0x579F,0x579E,0xA002,
+ 0x579D,0x579C,0xA004,0xA002,0x579A,0x5799,0xA002,0x5798,0x5797,0xA008,0xA004,0xA002,0x5796,0x5795,0xA002,0x5794,
+ 0x5791,0xA004,0xA002,0x5790,0x578F,0xA002,0x578E,0x578D,0xA00F,0xA008,0xA004,0xA002,0x578A,0x5789,0xA002,0x5788,
+ 0x5787,0xA003,0xA000,0x5781,0xA002,0x5780,0x577F,0xA008,0xA004,0xA002,0x577E,0x577D,0xA002,0x577A,0x5779,0xA004,
+ 0xA002,0x5778,0x5775,0xA002,0x5774,0x5772,0xA020,0xA010,0xA008,0xA004,0xA002,0x5771,0x5770,0xA002,0x576E,0x576C,
+ 0xA004,0xA002,0x5767,0x5765,0xA002,0x5763,0x5762,0xA008,0xA004,0xA002,0x5759,0x5758,0xA002,0x5756,0x5755,0xA004,
+ 0xA002,0x5754,0x5753,0xA002,0x5752,0x574B,0xA010,0xA008,0xA004,0xA002,0x5749,0x5748,0xA002,0x5746,0x5745,0xA004,
+ 0xA002,0x5744,0x5743,0xA002,0x5741,0x573F,0xA008,0xA004,0xA002,0x573D,0x573C,0xA002,0x5738,0x5737,0xA004,0xA002,
+ 0x5736,0x5735,0xA002,0x5734,0x5732,0xA083,0xA02D,0xA000,0xA01E,0xA00E,0xA006,0xA002,0x5731,0xA002,0x572B,0x5727,
+ 0xA004,0xA002,0x5726,0x5725,0xA002,0x5724,0x5722,0xA008,0xA004,0xA002,0x5721,0x5720,0xA002,0x571E,0x571D,0xA004,
+ 0xA002,0x571B,0x571A,0xA002,0x5719,0x5718,0xA000,0xA008,0xA004,0xA002,0x5717,0x5716,0xA002,0x5715,0x5714,0xA000,
+ 0xA002,0x5713,0x5712,0xA016,0xA001,0xA005,0xA001,0xA001,0xA001,0x5711,0xA008,0xA004,0xA002,0x5710,0x570F,0xA002,
+ 0x570E,0x570D,0xA004,0xA002,0x570C,0x570B,0xA002,0x5707,0x5705,0xA020,0xA010,0xA008,0xA004,0xA002,0x5702,0x5701,
+ 0xA002,0x5700,0x56FC,0xA004,0xA002,0x56FB,0x56F8,0xA002,0x56F7,0x56F6,0xA008,0xA004,0xA002,0x56F3,0x56F2,0xA002,
+ 0x56EF,0x56EE,0xA004,0xA002,0x56EC,0x56EA,0xA002,0x56E9,0x56E8,0xA010,0xA008,0xA004,0xA002,0x56E7,0x56E6,0xA002,
+ 0x56E5,0x56E3,0xA004,0xA002,0x56DC,0x56D9,0xA002,0x56D8,0x56D6,0xA008,0xA004,0xA002,0x56D5,0x56D3,0xA002,0x56D2,
+ 0x56D1,0xA004,0xA002,0x56D0,0x56CF,0xA002,0x56CE,0x56CD,0xA07E,0xA03E,0xA01E,0xA00E,0xA006,0xA002,0x56CC,0xA002,
+ 0x56CB,0x56C9,0xA004,0xA002,0x56C8,0x56C7,0xA002,0x56C6,0x56C5,0xA008,0xA004,0xA002,0x56C4,0x56C3,0xA002,0x56C2,
+ 0x56C1,0xA004,0xA002,0x56C0,0x56BF,0xA002,0x56BE,0x56BD,0xA010,0xA008,0xA004,0xA002,0x56BB,0x56BA,0xA002,0x56B9,
+ 0x56B8,0xA004,0xA002,0x56B6,0x56B5,0xA002,0x56B4,0x56B3,0xA008,0xA004,0xA002,0x56B2,0x56B1,0xA002,0x56B0,0x56AE,
+ 0xA004,0xA002,0x56AD,0x56AC,0xA002,0x56AB,0x56AA,0xA020,0xA010,0xA008,0xA004,0xA002,0x56A9,0x56A8,0xA002,0x56A7,
+ 0x56A6,0xA004,0xA002,0x56A5,0x56A4,0xA002,0x56A2,0x56A1,0xA008,0xA004,0xA002,0x56A0,0x569F,0xA002,0x569E,0x569D,
+ 0xA004,0xA002,0x569C,0x569B,0xA002,0x569A,0x5699,0xA010,0xA008,0xA004,0xA002,0x5698,0x5697,0xA002,0x5696,0x5695,
+ 0xA004,0xA002,0x5694,0x5692,0xA002,0x5691,0x5690,0xA008,0xA004,0xA002,0x568D,0x568C,0xA002,0x568B,0x568A,0xA004,
+ 0xA002,0x5689,0x5688,0xA002,0x5687,0x5684,0xA03F,0xA020,0xA010,0xA008,0xA004,0xA002,0x5683,0x5682,0xA002,0x5681,
+ 0x5680,0xA004,0xA002,0x567F,0x567E,0xA002,0x567D,0x567A,0xA008,0xA004,0xA002,0x5679,0x5678,0xA002,0x5677,0x5675,
+ 0xA004,0xA002,0x5674,0x5673,0xA002,0x5672,0x5670,0xA00F,0xA008,0xA004,0xA002,0x566F,0x566E,0xA002,0x566D,0x5667,
+ 0xA004,0xA002,0x5666,0x5665,0xA000,0x5663,0xA008,0xA004,0xA002,0x5661,0x5660,0xA002,0x565F,0x565E,0xA004,0xA002,
+ 0x565D,0x565B,0xA002,0x565A,0x5656,0xA020,0xA010,0xA008,0xA004,0xA002,0x5655,0x5653,0xA002,0x5652,0x5651,0xA004,
+ 0xA002,0x5650,0x564F,0xA002,0x564B,0x564A,0xA008,0xA004,0xA002,0x5649,0x5648,0xA002,0x5647,0x5646,0xA004,0xA002,
+ 0x5645,0x5644,0xA002,0x5643,0x5642,0xA010,0xA008,0xA004,0xA002,0x5641,0x5640,0xA002,0x563E,0x563D,0xA004,0xA002,
+ 0x563C,0x563A,0xA002,0x5638,0x5637,0xA008,0xA004,0xA002,0x5635,0x5633,0xA002,0x5630,0x562F,0xA004,0xA002,0x562E,
+ 0x562B,0xA002,0x562A,0x5629,0xA5F7,0xA2FB,0xA17C,0xA07F,0xA030,0xA000,0xA01E,0xA00E,0xA006,0xA002,0x5628,0xA002,
+ 0x5626,0x5625,0xA004,0xA002,0x5622,0x5621,0xA002,0x5620,0x561D,0xA008,0xA004,0xA002,0x561C,0x561A,0xA002,0x5619,
+ 0x5617,0xA004,0xA002,0x5616,0x5615,0xA002,0x5614,0x5613,0xA000,0xA008,0xA004,0xA002,0x5612,0x5611,0xA002,0x5610,
+ 0x560D,0xA004,0xA002,0x560B,0x560A,0xA002,0x5607,0x5606,0xA00F,0xA001,0xA001,0xA005,0xA001,0xA002,0x5605,0x5604,
+ 0xA004,0xA002,0x5603,0x5602,0xA002,0x55FF,0x55FC,0xA020,0xA010,0xA008,0xA004,0xA002,0x55FB,0x55FA,0xA002,0x55F9,
+ 0x55F8,0xA004,0xA002,0x55F6,0x55F4,0xA002,0x55F1,0x55F0,0xA008,0xA004,0xA002,0x55EE,0x55ED,0xA002,0x55E9,0x55E7,
+ 0xA004,0xA002,0x55E2,0x55E0,0xA002,0x55DE,0x55DB,0xA010,0xA008,0xA004,0xA002,0x55DA,0x55D9,0xA002,0x55D8,0x55D7,
+ 0xA004,0xA002,0x55D5,0x55D0,0xA002,0x55CF,0x55CE,0xA008,0xA004,0xA002,0x55CB,0x55CA,0xA002,0x55C8,0x55C7,0xA004,
+ 0xA002,0x55C6,0x55C3,0xA002,0x55C2,0x55C1,0xA07E,0xA03E,0xA01E,0xA00E,0xA006,0xA002,0x55C0,0xA002,0x55BF,0x55BC,
+ 0xA004,0xA002,0x55BA,0x55B8,0xA002,0x55B6,0x55B4,0xA008,0xA004,0xA002,0x55B2,0x55B0,0xA002,0x55AF,0x55AE,0xA004,
+ 0xA002,0x55AD,0x55AC,0xA002,0x55AB,0x55AA,0xA010,0xA008,0xA004,0xA002,0x55A9,0x55A8,0xA002,0x55A6,0x55A5,0xA004,
+ 0xA002,0x55A4,0x55A3,0xA002,0x55A2,0x55A1,0xA008,0xA004,0xA002,0x55A0,0x559E,0xA002,0x559B,0x559A,0xA004,0xA002,
+ 0x5597,0x5596,0xA002,0x5595,0x5593,0xA020,0xA010,0xA008,0xA004,0xA002,0x5592,0x5590,0xA002,0x558E,0x558D,0xA004,
+ 0xA002,0x558C,0x5586,0xA002,0x5585,0x557F,0xA008,0xA004,0xA002,0x557D,0x557A,0xA002,0x5579,0x5574,0xA004,0xA002,
+ 0x5573,0x5572,0xA002,0x5571,0x5570,0xA010,0xA008,0xA004,0xA002,0x556F,0x556B,0xA002,0x5569,0x5568,0xA004,0xA002,
+ 0x5563,0x5562,0xA002,0x5560,0x555F,0xA008,0xA004,0xA002,0x555E,0x555D,0xA002,0x555B,0x555A,0xA004,0xA002,0x5559,
+ 0x5558,0xA002,0x5557,0x5554,0xA03F,0xA020,0xA010,0xA008,0xA004,0xA002,0x5553,0x5552,0xA002,0x5551,0x554F,0xA004,
+ 0xA002,0x554E,0x554D,0xA002,0x554C,0x554B,0xA008,0xA004,0xA002,0x5548,0x5547,0xA002,0x5545,0x5542,0xA004,0xA002,
+ 0x5540,0x553D,0xA002,0x553B,0x553A,0xA010,0xA008,0xA004,0xA002,0x5539,0x5538,0xA002,0x5536,0x5535,0xA004,0xA002,
+ 0x5534,0x5532,0xA002,0x552D,0x552B,0xA007,0xA004,0xA002,0x5529,0x5528,0xA001,0x5526,0xA004,0xA002,0x5525,0x5521,
+ 0xA002,0x551F,0x551E,0xA020,0xA010,0xA008,0xA004,0xA002,0x551D,0x551C,0xA002,0x551A,0x5519,0xA004,0xA002,0x5518,
+ 0x5517,0xA002,0x5516,0x5515,0xA008,0xA004,0xA002,0x5513,0x5512,0xA002,0x550E,0x550D,0xA004,0xA002,0x550C,0x550B,
+ 0xA002,0x550A,0x5508,0xA010,0xA008,0xA004,0xA002,0x5505,0x5504,0xA002,0x5503,0x5502,0xA004,0xA002,0x5500,0x54FE,
+ 0xA002,0x54FB,0x54F9,0xA008,0xA004,0xA002,0x54F8,0x54F7,0xA002,0x54F6,0x54F5,0xA004,0xA002,0x54F4,0x54F1,0xA002,
+ 0x54F0,0x54EF,0xA082,0xA037,0xA000,0xA01E,0xA00E,0xA006,0xA002,0x54EC,0xA002,0x54EB,0x54E4,0xA004,0xA002,0x54E3,
+ 0x54E2,0xA002,0x54E1,0x54E0,0xA008,0xA004,0xA002,0x54DB,0x54D8,0xA002,0x54D6,0x54CB,0xA004,0xA002,0x54CA,0x54C5,
+ 0xA002,0x54C3,0x54BE,0xA010,0xA008,0xA004,0xA002,0x54BC,0x54BA,0xA002,0x54B9,0x54B7,0xA004,0xA002,0x54B6,0x54B5,
+ 0xA002,0x54B2,0x54B0,0xA000,0xA004,0xA002,0x54AE,0x54A5,0xA000,0x54A2,0xA00B,0xA001,0xA001,0xA001,0xA004,0xA002,
+ 0x54A1,0x54A0,0xA002,0x549F,0x549E,0xA020,0xA010,0xA008,0xA004,0xA002,0x549C,0x5498,0xA002,0x5497,0x5493,0xA004,
+ 0xA002,0x5491,0x548D,0xA002,0x548A,0x5489,0xA008,0xA004,0xA002,0x5488,0x5487,0xA002,0x5485,0x5483,0xA004,0xA002,
+ 0x5481,0x547F,0xA002,0x547E,0x547A,0xA010,0xA008,0xA004,0xA002,0x5479,0x5474,0xA002,0x5470,0x546F,0xA004,0xA002,
+ 0x546E,0x546D,0xA002,0x546C,0x546B,0xA008,0xA004,0xA002,0x546A,0x5469,0xA002,0x5467,0x5465,0xA004,0xA002,0x5463,
+ 0x5461,0xA002,0x5460,0x545F,0xA07E,0xA03E,0xA01E,0xA00E,0xA006,0xA002,0x545E,0xA002,0x545D,0x545A,0xA004,0xA002,
+ 0x5451,0x544F,0xA002,0x544E,0x544D,0xA008,0xA004,0xA002,0x544C,0x5449,0xA002,0x5447,0x5445,0xA004,0xA002,0x5444,
+ 0x5442,0xA002,0x5441,0x543F,0xA010,0xA008,0xA004,0xA002,0x543D,0x543A,0xA002,0x5437,0x5436,0xA004,0xA002,0x5433,
+ 0x5430,0xA002,0x542A,0x5425,0xA008,0xA004,0xA002,0x5424,0x5422,0xA002,0x541C,0x541A,0xA004,0xA002,0x5419,0x5418,
+ 0xA002,0x5414,0x540B,0xA020,0xA010,0xA008,0xA004,0xA002,0x5407,0x5405,0xA002,0x5402,0x5400,0xA004,0xA002,0x53FF,
+ 0x53FE,0xA002,0x53FA,0x53F4,0xA008,0xA004,0xA002,0x53E7,0x53E2,0xA002,0x53E1,0x53DE,0xA004,0xA002,0x53DD,0x53DC,
+ 0xA002,0x53DA,0x53D5,0xA010,0xA008,0xA004,0xA002,0x53D3,0x53D2,0xA002,0x53D0,0x53CF,0xA004,0xA002,0x53CE,0x53C7,
+ 0xA002,0x53C6,0x53C5,0xA008,0xA004,0xA002,0x53C4,0x53C3,0xA002,0x53C0,0x53BE,0xA004,0xA002,0x53BD,0x53BC,0xA002,
+ 0x53BA,0x53B9,0xA03F,0xA020,0xA010,0xA008,0xA004,0xA002,0x53B8,0x53B7,0xA002,0x53B5,0x53B4,0xA004,0xA002,0x53B3,
+ 0x53B2,0xA002,0x53B1,0x53B0,0xA008,0xA004,0xA002,0x53AF,0x53AD,0xA002,0x53AC,0x53AB,0xA004,0xA002,0x53AA,0x53A7,
+ 0xA002,0x53A4,0x53A1,0xA010,0xA008,0xA004,0xA002,0x53A0,0x539E,0xA002,0x539C,0x539B,0xA004,0xA002,0x5399,0x5397,
+ 0xA002,0x5396,0x5394,0xA008,0xA004,0xA002,0x5393,0x5392,0xA002,0x5391,0x5390,0xA003,0xA001,0x538F,0xA002,0x538E,
+ 0x538A,0xA020,0xA010,0xA008,0xA004,0xA002,0x5388,0x5387,0xA002,0x5383,0x5381,0xA004,0xA002,0x5380,0x537E,0xA002,
+ 0x537D,0x537C,0xA008,0xA004,0xA002,0x537B,0x5379,0xA002,0x5376,0x5372,0xA004,0xA002,0x536D,0x536C,0xA002,0x536A,
+ 0x5368,0xA010,0xA008,0xA004,0xA002,0x5365,0x535D,0xA002,0x535B,0x5359,0xA004,0xA002,0x5358,0x5354,0xA002,0x5350,
+ 0x534D,0xA008,0xA004,0xA002,0x534C,0x534B,0xA002,0x5346,0x5344,0xA004,0xA002,0x5342,0x5340,0xA002,0x533D,0x533C,
+ 0xA180,0xA083,0xA03B,0xA000,0xA01E,0xA00E,0xA006,0xA002,0x5338,0xA002,0x5337,0x5336,0xA004,0xA002,0x5335,0x5334,
+ 0xA002,0x5333,0x5332,0xA008,0xA004,0xA002,0x5331,0x5330,0xA002,0x532F,0x532D,0xA004,0xA002,0x532C,0x532B,0xA002,
+ 0x5329,0x5328,0xA010,0xA008,0xA004,0xA002,0x5327,0x5325,0xA002,0x5324,0x5322,0xA004,0xA002,0x531F,0x531E,0xA002,
+ 0x531C,0x531B,0xA008,0xA004,0xA002,0x5318,0x5314,0xA002,0x5313,0x5312,0xA000,0xA000,0x5311,0xA008,0xA001,0xA001,
+ 0xA001,0xA001,0xA002,0x530E,0x530C,0xA020,0xA010,0xA008,0xA004,0xA002,0x530B,0x530A,0xA002,0x5309,0x5307,0xA004,
+ 0xA002,0x5304,0x5303,0xA002,0x5302,0x5301,0xA008,0xA004,0xA002,0x52FD,0x52FC,0xA002,0x52FB,0x52F8,0xA004,0xA002,
+ 0x52F7,0x52F6,0xA002,0x52F5,0x52F4,0xA010,0xA008,0xA004,0xA002,0x52F3,0x52F2,0xA002,0x52F1,0x52EF,0xA004,0xA002,
+ 0x52EE,0x52ED,0xA002,0x52EC,0x52EB,0xA008,0xA004,0xA002,0x52EA,0x52E9,0xA002,0x52E8,0x52E7,0xA004,0xA002,0x52E6,
+ 0x52E5,0xA002,0x52E3,0x52E2,0xA07E,0xA03E,0xA01E,0xA00E,0xA006,0xA002,0x52E1,0xA002,0x52E0,0x52DE,0xA004,0xA002,
+ 0x52DD,0x52DC,0xA002,0x52DB,0x52DA,0xA008,0xA004,0xA002,0x52D9,0x52D7,0xA002,0x52D5,0x52D4,0xA004,0xA002,0x52D3,
+ 0x52D1,0xA002,0x52CF,0x52CE,0xA010,0xA008,0xA004,0xA002,0x52CD,0x52CC,0xA002,0x52CA,0x52C8,0xA004,0xA002,0x52C6,
+ 0x52C5,0xA002,0x52C4,0x52C2,0xA008,0xA004,0xA002,0x52C1,0x52C0,0xA002,0x52BD,0x52BC,0xA004,0xA002,0x52BB,0x52BA,
+ 0xA002,0x52B9,0x52B8,0xA020,0xA010,0xA008,0xA004,0xA002,0x52B7,0x52B6,0xA002,0x52B5,0x52B4,0xA004,0xA002,0x52B0,
+ 0x52AF,0xA002,0x52AE,0x52A7,0xA008,0xA004,0xA002,0x52A6,0x52A5,0xA002,0x52A4,0x529C,0xA004,0xA002,0x529A,0x5299,
+ 0xA002,0x5298,0x5297,0xA010,0xA008,0xA004,0xA002,0x5296,0x5295,0xA002,0x5294,0x5292,0xA004,0xA002,0x5291,0x528F,
+ 0xA002,0x528E,0x528D,0xA008,0xA004,0xA002,0x528C,0x528B,0xA002,0x528A,0x5289,0xA004,0xA002,0x5287,0x5286,0xA002,
+ 0x5285,0x5284,0xA03F,0xA020,0xA010,0xA008,0xA004,0xA002,0x5283,0x5280,0xA002,0x527E,0x527C,0xA004,0xA002,0x527B,
+ 0x527A,0xA002,0x5279,0x5278,0xA008,0xA004,0xA002,0x5277,0x5276,0xA002,0x5275,0x5274,0xA004,0xA002,0x5273,0x5271,
+ 0xA002,0x5270,0x526E,0xA010,0xA008,0xA004,0xA002,0x526D,0x526C,0xA002,0x526B,0x5268,0xA004,0xA002,0x5266,0x5264,
+ 0xA002,0x5263,0x5262,0xA008,0xA004,0xA002,0x5260,0x525F,0xA002,0x525D,0x525B,0xA004,0xA002,0x525A,0x5259,0xA001,
+ 0x5258,0xA020,0xA010,0xA008,0xA004,0xA002,0x5257,0x5255,0xA002,0x5253,0x5252,0xA004,0xA002,0x524F,0x524E,0xA002,
+ 0x524B,0x5249,0xA008,0xA004,0xA002,0x5248,0x5247,0xA002,0x5246,0x5245,0xA004,0xA002,0x5244,0x523E,0xA002,0x523C,
+ 0x5235,0xA010,0xA008,0xA004,0xA002,0x5234,0x5232,0xA002,0x5231,0x522F,0xA004,0xA002,0x522C,0x522A,0xA002,0x5227,
+ 0x5226,0xA008,0xA004,0xA002,0x5225,0x5223,0xA002,0x5222,0x5221,0xA004,0xA002,0x521F,0x521E,0xA002,0x521C,0x5215,
+ 0xA07F,0xA03E,0xA000,0xA01E,0xA00E,0xA006,0xA002,0x5214,0xA002,0x5213,0x5210,0xA004,0xA002,0x520F,0x520C,0xA002,
+ 0x520B,0x5209,0xA008,0xA004,0xA002,0x5205,0x5204,0xA002,0x51FE,0x51F7,0xA004,0xA002,0x51F4,0x51F2,0xA002,0x51F1,
+ 0x51EE,0xA010,0xA008,0xA004,0xA002,0x51EC,0x51EA,0xA002,0x51E9,0x51E8,0xA004,0xA002,0x51E7,0x51E6,0xA002,0x51E5,
+ 0x51E3,0xA008,0xA004,0xA002,0x51E2,0x51DF,0xA002,0x51DE,0x51DC,0xA004,0xA002,0x51DA,0x51D9,0xA000,0x51D8,0xA001,
+ 0xA020,0xA010,0xA008,0xA004,0xA002,0x51D7,0x51D6,0xA002,0x51D5,0x51D4,0xA004,0xA002,0x51D3,0x51D2,0xA002,0x51D0,
+ 0x51CE,0xA008,0xA004,0xA002,0x51CD,0x51CA,0xA002,0x51C8,0x51C5,0xA004,0xA002,0x51C3,0x51C2,0xA002,0x51C1,0x51BF,
+ 0xA010,0xA008,0xA004,0xA002,0x51BE,0x51BA,0xA002,0x51B9,0x51B8,0xA004,0xA002,0x51B4,0x51AE,0xA002,0x51AD,0x51AA,
+ 0xA008,0xA004,0xA002,0x51A9,0x51A8,0xA002,0x51A7,0x51A6,0xA004,0xA002,0x51A3,0x51A1,0xA002,0x519F,0x519E,0xA07E,
+ 0xA03E,0xA01E,0xA00E,0xA006,0xA002,0x519D,0xA002,0x519A,0x5198,0xA004,0xA002,0x5194,0x5193,0xA002,0x5191,0x5190,
+ 0xA008,0xA004,0xA002,0x518F,0x518E,0xA002,0x518B,0x518A,0xA004,0xA002,0x5187,0x5186,0xA002,0x5184,0x5183,0xA010,
+ 0xA008,0xA004,0xA002,0x517F,0x517E,0xA002,0x517A,0x5172,0xA004,0xA002,0x516F,0x516A,0xA002,0x5169,0x5167,0xA008,
+ 0xA004,0xA002,0x5166,0x5164,0xA002,0x5163,0x5161,0xA004,0xA002,0x5160,0x515F,0xA002,0x515E,0x515D,0xA020,0xA010,
+ 0xA008,0xA004,0xA002,0x515B,0x5159,0xA002,0x5158,0x5157,0xA004,0xA002,0x5153,0x5152,0xA002,0x5150,0x514F,0xA008,
+ 0xA004,0xA002,0x514E,0x514C,0xA002,0x514A,0x5147,0xA004,0xA002,0x5142,0x513E,0xA002,0x513D,0x513C,0xA010,0xA008,
+ 0xA004,0xA002,0x513B,0x513A,0xA002,0x5139,0x5138,0xA004,0xA002,0x5137,0x5136,0xA002,0x5135,0x5134,0xA008,0xA004,
+ 0xA002,0x5133,0x5132,0xA002,0x5131,0x5130,0xA004,0xA002,0x512F,0x512E,0xA002,0x512D,0x512C,0xA040,0xA020,0xA010,
+ 0xA008,0xA004,0xA002,0x512B,0x512A,0xA002,0x5129,0x5128,0xA004,0xA002,0x5127,0x5126,0xA002,0x5125,0x5124,0xA008,
+ 0xA004,0xA002,0x5123,0x5122,0xA002,0x5120,0x511F,0xA004,0xA002,0x511E,0x511D,0xA002,0x511C,0x511B,0xA010,0xA008,
+ 0xA004,0xA002,0x511A,0x5119,0xA002,0x5118,0x5117,0xA004,0xA002,0x5116,0x5115,0xA002,0x5114,0x5113,0xA008,0xA004,
+ 0xA002,0x5111,0x5110,0xA002,0x510F,0x510E,0xA004,0xA002,0x510D,0x510C,0xA002,0x510A,0x5109,0xA01F,0xA00F,0xA007,
+ 0xA003,0xA001,0x5108,0xA002,0x5105,0x5104,0xA004,0xA002,0x5103,0x5102,0xA002,0x5101,0x5100,0xA008,0xA004,0xA002,
+ 0x50FF,0x50FE,0xA002,0x50FD,0x50FC,0xA004,0xA002,0x50FA,0x50F9,0xA002,0x50F8,0x50F7,0xA010,0xA008,0xA004,0xA002,
+ 0x50F6,0x50F4,0xA002,0x50F2,0x50F1,0xA004,0xA002,0x50F0,0x50EF,0xA002,0x50EB,0x50EA,0xA008,0xA004,0xA002,0x50E9,
+ 0x50E8,0xA002,0x50E5,0x50E4,0xA004,0xA002,0x50E3,0x50E2,0xA002,0x50E1,0x50E0,0xA2FF,0xA180,0xA083,0xA045,0xA03E,
+ 0xA01E,0xA00E,0xA006,0xA002,0x50DF,0xA002,0x50DE,0x50DD,0xA004,0xA002,0x50DC,0x50DB,0xA002,0x50D9,0x50D8,0xA008,
+ 0xA004,0xA002,0x50D7,0x50D5,0xA002,0x50D4,0x50D3,0xA004,0xA002,0x50D2,0x50D1,0xA002,0x50D0,0x50CE,0xA010,0xA008,
+ 0xA004,0xA002,0x50CD,0x50CC,0xA002,0x50CB,0x50CA,0xA004,0xA002,0x50C9,0x50C8,0xA002,0x50C7,0x50C6,0xA008,0xA004,
+ 0xA002,0x50C5,0x50C4,0xA002,0x50C3,0x50C2,0xA004,0xA002,0x50C1,0x50C0,0xA002,0x50BF,0x50BE,0xA000,0xA000,0xA000,
+ 0xA000,0xA000,0x50BD,0xA001,0xA01D,0xA00D,0xA005,0xA001,0xA002,0x50BC,0x50B9,0xA004,0xA002,0x50B8,0x50B7,0xA002,
+ 0x50B6,0x50B5,0xA008,0xA004,0xA002,0x50B4,0x50B3,0xA002,0x50B1,0x50B0,0xA004,0xA002,0x50AF,0x50AE,0xA002,0x50AD,
+ 0x50AB,0xA010,0xA008,0xA004,0xA002,0x50AA,0x50A6,0xA002,0x50A4,0x50A2,0xA004,0xA002,0x50A1,0x50A0,0xA002,0x509F,
+ 0x509E,0xA008,0xA004,0xA002,0x509D,0x509C,0xA002,0x509B,0x509A,0xA004,0xA002,0x5099,0x5098,0xA002,0x5097,0x5096,
+ 0xA07E,0xA03E,0xA01E,0xA00E,0xA006,0xA002,0x5095,0xA002,0x5094,0x5093,0xA004,0xA002,0x5092,0x5091,0xA002,0x5090,
+ 0x508F,0xA008,0xA004,0xA002,0x508E,0x508C,0xA002,0x508B,0x508A,0xA004,0xA002,0x5089,0x5087,0xA002,0x5086,0x5084,
+ 0xA010,0xA008,0xA004,0xA002,0x5083,0x5082,0xA002,0x5081,0x507D,0xA004,0xA002,0x507C,0x507A,0xA002,0x5079,0x5078,
+ 0xA008,0xA004,0xA002,0x5075,0x5074,0xA002,0x5073,0x5072,0xA004,0xA002,0x5071,0x5070,0xA002,0x506F,0x506E,0xA020,
+ 0xA010,0xA008,0xA004,0xA002,0x506D,0x506B,0xA002,0x506A,0x5069,0xA004,0xA002,0x5068,0x5067,0xA002,0x5066,0x5064,
+ 0xA008,0xA004,0xA002,0x5063,0x5062,0xA002,0x5061,0x5060,0xA004,0xA002,0x505F,0x505E,0xA002,0x505D,0x505B,0xA010,
+ 0xA008,0xA004,0xA002,0x5059,0x5058,0xA002,0x5057,0x5056,0xA004,0xA002,0x5054,0x5053,0xA002,0x5052,0x5051,0xA008,
+ 0xA004,0xA002,0x5050,0x504D,0xA002,0x504B,0x504A,0xA004,0xA002,0x5049,0x5046,0xA002,0x5045,0x5044,0xA040,0xA020,
+ 0xA010,0xA008,0xA004,0xA002,0x5042,0x5041,0xA002,0x5040,0x503F,0xA004,0xA002,0x503D,0x503B,0xA002,0x5039,0x5038,
+ 0xA008,0xA004,0xA002,0x5037,0x5036,0xA002,0x5035,0x5034,0xA004,0xA002,0x5033,0x5032,0xA002,0x5031,0x5030,0xA010,
+ 0xA008,0xA004,0xA002,0x502F,0x502B,0xA002,0x5027,0x5024,0xA004,0xA002,0x5023,0x5022,0xA002,0x5020,0x501E,0xA008,
+ 0xA004,0xA002,0x501D,0x501B,0xA002,0x5017,0x5016,0xA004,0xA002,0x5015,0x5013,0xA002,0x5011,0x5010,0xA01F,0xA00F,
+ 0xA007,0xA004,0xA002,0x500E,0x500B,0xA001,0x500A,0xA004,0xA002,0x5009,0x5008,0xA002,0x5007,0x5006,0xA008,0xA004,
+ 0xA002,0x5005,0x5004,0xA002,0x5003,0x5002,0xA004,0xA002,0x5001,0x5000,0xA002,0x4FFF,0x4FFD,0xA010,0xA008,0xA004,
+ 0xA002,0x4FFC,0x4FFB,0xA002,0x4FF9,0x4FF7,0xA004,0xA002,0x4FF6,0x4FF5,0xA002,0x4FF4,0x4FF2,0xA008,0xA004,0xA002,
+ 0x4FF0,0x4FEC,0xA002,0x4FEB,0x4FE7,0xA004,0xA002,0x4FE5,0x4FE4,0xA002,0x4FE2,0x4FE0,0xA082,0xA048,0xA03E,0xA01E,
+ 0xA00E,0xA006,0xA002,0x4FDB,0xA002,0x4FD9,0x4FD6,0xA004,0xA002,0x4FD5,0x4FD4,0xA002,0x4FD3,0x4FD2,0xA008,0xA004,
+ 0xA002,0x4FCD,0x4FCC,0xA002,0x4FCB,0x4FC9,0xA004,0xA002,0x4FC8,0x4FC7,0xA002,0x4FC6,0x4FC2,0xA010,0xA008,0xA004,
+ 0xA002,0x4FC1,0x4FC0,0xA002,0x4FBE,0x4FBD,0xA004,0xA002,0x4FBC,0x4FBB,0xA002,0x4FBA,0x4FB9,0xA008,0xA004,0xA002,
+ 0x4FB8,0x4FB7,0xA002,0x4FB6,0x4FB4,0xA004,0xA002,0x4FB3,0x4FB2,0xA002,0x4FB1,0x4FB0,0xA000,0xA000,0xA000,0xA004,
+ 0xA002,0x4FAD,0x4FAB,0xA000,0x4FA4,0xA001,0xA019,0xA009,0xA001,0xA004,0xA002,0x4FA2,0x4FA1,0xA002,0x4F9F,0x4F9E,
+ 0xA008,0xA004,0xA002,0x4F9C,0x4F9A,0xA002,0x4F99,0x4F98,0xA004,0xA002,0x4F96,0x4F95,0xA002,0x4F93,0x4F92,0xA010,
+ 0xA008,0xA004,0xA002,0x4F90,0x4F8E,0xA002,0x4F8C,0x4F8A,0xA004,0xA002,0x4F87,0x4F86,0xA002,0x4F85,0x4F82,0xA008,
+ 0xA004,0xA002,0x4F81,0x4F80,0xA002,0x4F7D,0x4F7A,0xA004,0xA002,0x4F79,0x4F78,0xA002,0x4F77,0x4F75,0xA07E,0xA03E,
+ 0xA01E,0xA00E,0xA006,0xA002,0x4F72,0xA002,0x4F71,0x4F6E,0xA004,0xA002,0x4F6D,0x4F6B,0xA002,0x4F6A,0x4F68,0xA008,
+ 0xA004,0xA002,0x4F66,0x4F62,0xA002,0x4F61,0x4F56,0xA004,0xA002,0x4F54,0x4F52,0xA002,0x4F4C,0x4F4B,0xA010,0xA008,
+ 0xA004,0xA002,0x4F4A,0x4F49,0xA002,0x4F48,0x4F47,0xA004,0xA002,0x4F45,0x4F44,0xA002,0x4F42,0x4F41,0xA008,0xA004,
+ 0xA002,0x4F40,0x4F3F,0xA002,0x4F3E,0x4F3B,0xA004,0xA002,0x4F39,0x4F37,0xA002,0x4F35,0x4F33,0xA020,0xA010,0xA008,
+ 0xA004,0xA002,0x4F31,0x4F2E,0xA002,0x4F2D,0x4F2C,0xA004,0xA002,0x4F29,0x4F28,0xA002,0x4F23,0x4F21,0xA008,0xA004,
+ 0xA002,0x4F1D,0x4F1C,0xA002,0x4F16,0x4F15,0xA004,0xA002,0x4F14,0x4F13,0xA002,0x4F12,0x4F0C,0xA010,0xA008,0xA004,
+ 0xA002,0x4F0B,0x4F08,0xA002,0x4F07,0x4F06,0xA004,0xA002,0x4F05,0x4F04,0xA002,0x4F03,0x4F02,0xA008,0xA004,0xA002,
+ 0x4F00,0x4EFE,0xA002,0x4EFC,0x4EFA,0xA004,0xA002,0x4EF9,0x4EF8,0xA002,0x4EF4,0x4EF1,0xA040,0xA020,0xA010,0xA008,
+ 0xA004,0xA002,0x4EEF,0x4EEE,0xA002,0x4EED,0x4EE9,0xA004,0xA002,0x4EE7,0x4EE6,0xA002,0x4EE2,0x4EE0,0xA008,0xA004,
+ 0xA002,0x4EDC,0x4EDB,0xA002,0x4EDA,0x4ED2,0xA004,0xA002,0x4ED0,0x4ECF,0xA002,0x4ECC,0x4EC8,0xA010,0xA008,0xA004,
+ 0xA002,0x4EBE,0x4EBD,0xA002,0x4EBC,0x4EB9,0xA004,0xA002,0x4EB8,0x4EB7,0xA002,0x4EB6,0x4EB4,0xA008,0xA004,0xA002,
+ 0x4EB1,0x4EB0,0xA002,0x4EAF,0x4EAA,0xA004,0xA002,0x4EA3,0x4E9E,0xA002,0x4E9D,0x4E9C,0xA01F,0xA00F,0xA008,0xA004,
+ 0xA002,0x4E99,0x4E97,0xA002,0x4E96,0x4E90,0xA003,0xA001,0x4E8A,0xA002,0x4E87,0x4E85,0xA008,0xA004,0xA002,0x4E84,
+ 0x4E83,0xA002,0x4E82,0x4E81,0xA004,0xA002,0x4E80,0x4E7F,0xA002,0x4E7D,0x4E7C,0xA010,0xA008,0xA004,0xA002,0x4E7B,
+ 0x4E7A,0xA002,0x4E79,0x4E78,0xA004,0xA002,0x4E77,0x4E76,0xA002,0x4E75,0x4E74,0xA008,0xA004,0xA002,0x4E72,0x4E6F,
+ 0xA002,0x4E6E,0x4E6D,0xA004,0xA002,0x4E6C,0x4E6B,0xA002,0x4E6A,0x4E68,0xA000,0xA000,0xA000,0xA03E,0xA01E,0xA00E,
+ 0xA006,0xA002,0x4E67,0xA002,0x4E65,0x4E64,0xA004,0xA002,0x4E63,0x4E62,0xA002,0x4E5B,0x4E5A,0xA008,0xA004,0xA002,
+ 0x4E57,0x4E55,0xA002,0x4E51,0x4E4A,0xA004,0xA002,0x4E46,0x4E44,0xA002,0x4E42,0x4E41,0xA010,0xA008,0xA004,0xA002,
+ 0x4E40,0x4E3C,0xA002,0x4E37,0x4E35,0xA004,0xA002,0x4E33,0x4E31,0xA002,0x4E2F,0x4E2E,0xA008,0xA004,0xA002,0x4E29,
+ 0x4E26,0xA002,0x4E23,0x4E21,0xA004,0xA002,0x4E20,0x4E1F,0xA002,0x4E17,0x4E12,0xA000,0xA000,0xA008,0xA004,0xA002,
+ 0x4E0F,0x4E06,0xA002,0x4E05,0x4E04,0xA000,0xA000,0x4E02,0xA001,0xA001,0xA001,0xA001,0xA001,0xA001,0xA001,0xA001,
+ 0xA001,0xA001,0xA001,0xA001,0xA001,0xA001,0xA001,0x20AC,
+}; // cp936_to_ucs2_elements
+
+const TConvFlatTree cp936_to_ucs2 = {
+ 0x0080, // min key
+ 0xFE4F, // max key
+ 0xA000, // links space start
+ 0xEFFF, // links space end
+ cp936_to_ucs2_numelems, // number of elements
+ (treeval_t *)&cp936_to_ucs2_elements // elements
+}; // cp936_to_ucs2
+
+
diff --git a/src/sysync/customimplagent.cpp b/src/sysync/customimplagent.cpp
new file mode 100755
index 0000000..e5160c1
--- /dev/null
+++ b/src/sysync/customimplagent.cpp
@@ -0,0 +1,869 @@
+/**
+ * @File customimplagent.cpp
+ *
+ * @Author Lukas Zeller (luz@synthesis.ch)
+ *
+ * @brief TCustomAgent
+ * Base class for agenst (servers or clients) with customizable datastores
+ * based on TCustomImplDS
+ *
+ * Copyright (c) 2001-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ * @Date 2005-12-05 : luz : separated from odbcdbagent
+ */
+
+
+// includes
+#include "sysync.h"
+#include "multifielditem.h"
+#include "mimediritemtype.h"
+
+#include "customimplagent.h"
+#include "customimplds.h"
+
+namespace sysync {
+
+
+// Charset names for DB charset specification
+// Note: numCharSets and the TCharSets type enum is defined in sysync_utils
+const char * const DBCharSetNames[numCharSets] = {
+ "unknown",
+ "ASCII",
+ "ANSI",
+ "ISO-8859-1",
+ "UTF-8",
+ "UTF-16",
+ #ifdef CHINESE_SUPPORT
+ "GB2312",
+ "CP936",
+ #endif
+};
+
+
+// database field type names
+const char * const DBFieldTypeNames[numDBfieldTypes] = {
+ "string",
+ "blob",
+ "date",
+ "time",
+ "timefordate",
+ "timestamp",
+ "dateonly",
+ "zoneoffset_hours",
+ "zoneoffset_mins",
+ "zoneoffset_secs",
+ "zonename",
+ "numeric",
+ "lineartime",
+ "lineardate",
+ "unixtime_s",
+ "unixtime_ms",
+ "unixtime_us",
+ "unixdate_s",
+ "unixdate_ms",
+ "unixdate_us",
+};
+
+
+
+// Generic Utils
+// =============
+
+// integer in DB to lineartime conversion
+lineartime_t dbIntToLineartime(sInt64 aDBInt, TDBFieldType aDbfty)
+{
+ switch (aDbfty) {
+ case dbft_unixtime_s:
+ case dbft_unixdate_s:
+ // integer value representing UNIX epoch date in seconds
+ return secondToLinearTimeFactor*aDBInt+UnixToLineartimeOffset;
+ case dbft_unixtime_ms:
+ case dbft_unixdate_ms:
+ // integer value representing UNIX epoch date in milliseconds
+ return aDBInt*secondToLinearTimeFactor/1000+UnixToLineartimeOffset;
+ case dbft_unixdate_us:
+ case dbft_unixtime_us:
+ // integer value representing UNIX epoch time stamp in microseconds
+ return aDBInt*secondToLinearTimeFactor/1000000+UnixToLineartimeOffset;
+ case dbft_lineardate:
+ // linear date as-is
+ return aDBInt*linearDateToTimeFactor;
+ case dbft_lineartime:
+ default:
+ // linear time as-is
+ return aDBInt;
+ }
+} // dbIntToLineartime
+
+
+// lineartime to integer in DB conversion
+sInt64 lineartimeToDbInt(lineartime_t aLinearTime, TDBFieldType aDbfty)
+{
+ switch (aDbfty) {
+ case dbft_unixtime_s:
+ case dbft_unixdate_s:
+ // integer value representing UNIX epoch date in seconds
+ return (aLinearTime-UnixToLineartimeOffset)/secondToLinearTimeFactor;
+ case dbft_unixtime_ms:
+ case dbft_unixdate_ms:
+ // integer value representing UNIX epoch date in milliseconds
+ return (aLinearTime-UnixToLineartimeOffset)*1000/secondToLinearTimeFactor;
+ case dbft_unixdate_us:
+ case dbft_unixtime_us:
+ // integer value representing UNIX epoch time stamp in microseconds
+ return (aLinearTime-UnixToLineartimeOffset)*1000000/secondToLinearTimeFactor;
+ case dbft_lineardate:
+ // linear date as-is
+ return aLinearTime/linearDateToTimeFactor;
+ case dbft_lineartime:
+ default:
+ // linear time as-is
+ return aLinearTime;
+ }
+} // lineartimeToDbInt
+
+// Config
+// ======
+
+TCustomAgentConfig::TCustomAgentConfig(TConfigElement *aParentElement) :
+ #ifdef BASED_ON_BINFILE_CLIENT
+ TBinfileClientConfig(aParentElement)
+ #elif defined(SYSYNC_CLIENT)
+ TClientConfig("CustomClient",aParentElement)
+ #else
+ TServerConfig("CustomServer",aParentElement)
+ #endif
+ #ifdef SCRIPT_SUPPORT
+ , fResolverContext(NULL)
+ #endif
+{
+ // nop so far
+} // TCustomAgentConfig::TCustomAgentConfig
+
+
+TCustomAgentConfig::~TCustomAgentConfig()
+{
+ clear();
+} // TCustomAgentConfig::~TCustomAgentConfig
+
+
+// init defaults
+void TCustomAgentConfig::clear(void)
+{
+ // init defaults
+ fCurrentDateIsUTC=false; // compatibility flag only, will set fCurrentDateTimeZone to TCTX_UTC at Resolve if set
+ fCurrentDateTimeZone=TCTX_SYSTEM; // assume system local time
+ fDataCharSet=chs_ansi; // assume ANSI, is probable for ODBC connection
+ fDataLineEndMode=lem_dos; // default to CRLF, as this seems to be safest assumption
+ // resolver context
+ #ifndef BASED_ON_BINFILE_CLIENT
+ #ifdef SCRIPT_SUPPORT
+ fLoginInitScript.erase();
+ fLoginCheckScript.erase();
+ fLoginFinishScript.erase();
+ if (fResolverContext) {
+ delete fResolverContext;
+ fResolverContext=NULL;
+ }
+ #endif
+ #endif
+ // clear inherited
+ inherited::clear();
+} // TCustomAgentConfig::clear
+
+
+#ifdef SCRIPT_SUPPORT
+
+
+// Custom agent specific script functions
+// ======================================
+
+
+class TCustomAgentFuncs {
+public:
+
+ #ifndef BASED_ON_BINFILE_CLIENT
+ // Login context functions
+
+ // integer CHECKAUTH(string user, string secret, integer secretismd5)
+ // returns auth status of checking given user name and given secret against user/secret from remote
+ static void func_CheckAuth(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ // get local side info
+ string dbuser,dbsecret,nonce;
+ bool secretismd5,authok;
+ aFuncContextP->getLocalVar(0)->getAsString(dbuser);
+ aFuncContextP->getLocalVar(1)->getAsString(dbsecret);
+ secretismd5=aFuncContextP->getLocalVar(2)->getAsBoolean();
+ // check against remote side info.
+ TCustomImplAgent *agentP = static_cast<TCustomImplAgent *>(aFuncContextP->getCallerContext());
+ if (agentP->fAuthType==sectyp_md5_V10 || agentP->fAuthType==sectyp_md5_V11)
+ agentP->getAuthNonce(agentP->fAuthDevice,nonce);
+ if (!secretismd5) {
+ // local secret is clear text password, check against what was transmitted from remote
+ authok=agentP->checkAuthPlain(
+ dbuser.c_str(), // user name as specified by caller
+ dbsecret.c_str(), // secret as specfied by caller (usually retrieved from DB)
+ nonce.c_str(), // the nonce
+ agentP->fAuthSecret, // the auth secret as transmitted from remote
+ agentP->fAuthType // the type of the auth secret
+ );
+ }
+ else {
+ // local secret is b64(md5(user:password))
+ authok=
+ agentP->fAuthType==sectyp_md5_V11 && // auth secret from remote MUST be V1.1 type MD5
+ agentP->checkMD5WithNonce(
+ dbsecret.c_str(), // b64(md5(user:password)) as provided by caller to check against
+ nonce.c_str(), // the nonce
+ agentP->fAuthSecret // auth secret according to SyncML 1.1 (b64(md5(b64(md5(user:pw)):nonce))
+ );
+ }
+ // return auth checking result
+ aTermP->setAsInteger(authok ? 1 : 0);
+ }; // func_CheckAuth
+
+
+ // integer AUTHOK()
+ // returns current auth ok status (standard checking enabled)
+ static void func_AuthOK(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ aTermP->setAsInteger(
+ static_cast<TCustomImplAgent *>(aFuncContextP->getCallerContext())->fStandardAuthOK ? 1 : 0
+ );
+ }; // func_AuthOK
+
+
+ // string AUTHUSER()
+ // returns name of user that tries to authenticate
+ static void func_AuthUser(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ aTermP->setAsString(
+ static_cast<TCustomImplAgent *>(aFuncContextP->getCallerContext())->fAuthUser
+ );
+ }; // func_AuthUser
+
+
+ // SETUSERNAME(string username)
+ // set user name that will be used to find user in DB
+ // (but note that original username will be used to check auth with MD5 auth)
+ static void func_SetUserName(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ aFuncContextP->getLocalVar(0)->getAsString(
+ static_cast<TCustomImplAgent *>(aFuncContextP->getCallerContext())->fUserName
+ );
+ }; // func_SetUserName
+
+
+ // SETDOMAIN(string domainname)
+ // set "domain" name that can be used to differentiate user domains
+ // (mainly an xml2go requirement)
+ static void func_SetDomain(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ aFuncContextP->getLocalVar(0)->getAsString(
+ static_cast<TCustomImplAgent *>(aFuncContextP->getCallerContext())->fDomainName
+ );
+ }; // func_SetDomain
+
+
+
+ // string AUTHSTRING()
+ // returns auth string (MD5 digest or plain text, according to AUTHTYPE())
+ // sent by user trying to authenticate
+ static void func_AuthString(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ aTermP->setAsString(
+ static_cast<TCustomImplAgent *>(aFuncContextP->getCallerContext())->fAuthSecret
+ );
+ }; // func_AuthString
+
+
+ // string AUTHDEVICEID()
+ // returns remote device ID
+ static void func_AuthDeviceID(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ aTermP->setAsString(
+ static_cast<TCustomImplAgent *>(aFuncContextP->getCallerContext())->fAuthDevice
+ );
+ }; // func_AuthDeviceID
+
+
+ // integer AUTHTYPE()
+ // returns
+ static void func_AuthType(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ aTermP->setAsInteger(
+ (fieldinteger_t) static_cast<TCustomImplAgent *>(aFuncContextP->getCallerContext())->fAuthType
+ );
+ }; // func_AuthType
+
+
+ // integer UNKNOWNDEVICE()
+ // returns if device was not yet in device table (valid in fLoginCheckScript only)
+ static void func_Unknowndevice(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ aTermP->setAsInteger(
+ static_cast<TCustomImplAgent *>(aFuncContextP->getCallerContext())->fUnknowndevice ? 1 : 0
+ );
+ }; // func_Unknowndevice
+
+
+ // SETREADONLY(integer readonly)
+ // set readonly option of this sync session
+ static void func_SetReadOnly(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ static_cast<TSyncSession *>(aFuncContextP->getCallerContext())->setReadOnly(
+ aFuncContextP->getLocalVar(0)->getAsBoolean()
+ );
+ }; // func_SetReadOnly
+
+
+ // SETDEBUGLOG(integer enabled)
+ // set debug log output for this sync session
+ static void func_SetDebugLog(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ #ifdef SYDEBUG
+ static_cast<TSyncSession *>(aFuncContextP->getCallerContext())->getDbgLogger()->setEnabled(
+ aFuncContextP->getLocalVar(0)->getAsBoolean()
+ );
+ /// @todo: remove this
+ // %%% for now, we also need to set this separate flag
+ static_cast<TSyncSession *>(aFuncContextP->getCallerContext())->fSessionDebugLogs=
+ aFuncContextP->getLocalVar(0)->getAsBoolean();
+ #endif
+ }; // func_SetDebugLog
+
+
+ // SETLOG(integer enabled)
+ // set debug log output for this sync session
+ static void func_SetLog(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ #ifndef MINIMAL_CODE
+ static_cast<TSyncSession *>(aFuncContextP->getCallerContext())->fLogEnabled=
+ aFuncContextP->getLocalVar(0)->getAsBoolean();
+ #endif
+ }; // func_SetLog
+
+
+ // string USERKEY()
+ // returns user key
+ static void func_UserKey(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ aTermP->setAsString(
+ static_cast<TCustomImplAgent *>(aFuncContextP->getCallerContext())->fUserKey
+ );
+ }; // func_UserKey
+
+
+ // SETUSERKEY(variant userkey)
+ // set user key for this sync session
+ static void func_SetUserKey(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ aFuncContextP->getLocalVar(0)->getAsString(
+ static_cast<TCustomImplAgent *>(aFuncContextP->getCallerContext())->fUserKey
+ );
+ }; // func_SetUserKey
+
+
+ // string DEVICEKEY()
+ // returns device key
+ static void func_DeviceKey(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ aTermP->setAsString(
+ static_cast<TCustomImplAgent *>(aFuncContextP->getCallerContext())->fDeviceKey
+ );
+ }; // func_DeviceKey
+
+
+ // SETDEVICEKEY(variant devicekey)
+ // set device key for this sync session
+ static void func_SetDeviceKey(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ aFuncContextP->getLocalVar(0)->getAsString(
+ static_cast<TCustomImplAgent *>(aFuncContextP->getCallerContext())->fDeviceKey
+ );
+ }; // func_SetDeviceKey
+
+ #endif // not BASED_ON_BINFILE_CLIENT
+
+
+ // timestamp DBINTTOTIMESTAMP(integer dbint,string dbfieldtype)
+ // convert database integer to timestamp
+ static void func_DBIntToTimestamp(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ string tname,literal;
+ aFuncContextP->getLocalVar(1)->getAsString(tname); // DB type string
+ // search DB type, default to lineartime
+ sInt16 ty;
+ TDBFieldType dbfty=dbft_lineartime;
+ if (StrToEnum(DBFieldTypeNames,numDBfieldTypes,ty,tname.c_str()))
+ dbfty=(TDBFieldType)ty;
+ // now set timestamp
+ static_cast<TTimestampField *>(aTermP)->setTimestampAndContext(
+ dbIntToLineartime(aFuncContextP->getLocalVar(0)->getAsInteger(), dbfty), // timestamp
+ TCTX_UNKNOWN // unknown zone
+ );
+ }; // func_DBIntToTimestamp
+
+
+ // integer TIMESTAMPTODBINT(timestamp ts,string dbfieldtype)
+ // convert database integer to timestamp
+ static void func_TimestampToDBInt(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ string tname,literal;
+ aFuncContextP->getLocalVar(1)->getAsString(tname); // DB type string
+ // search DB type, default to lineartime
+ sInt16 ty;
+ TDBFieldType dbfty=dbft_lineartime;
+ if (StrToEnum(DBFieldTypeNames,numDBfieldTypes,ty,tname.c_str()))
+ dbfty=(TDBFieldType)ty;
+ // now set timestamp
+ aTermP->setAsInteger(
+ lineartimeToDbInt(
+ static_cast<TTimestampField *>(aFuncContextP->getLocalVar(0))->getTimestampAs(TCTX_UNKNOWN),
+ dbfty
+ )
+ );
+ }; // func_TimestampToDBInt
+
+
+ // timestamp CONVERTTODATAZONE(timestamp atime [,boolean doUnfloat])
+ // returns timestamp converted to database time zone.
+ // - If doUnfloat, floating timestamps will be fixed in the new zone w/o conversion of the timestamp itself.
+ static void func_ConvertToDataZone(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ // get DB zone
+ TCustomImplAgent *agentP = static_cast<TCustomImplAgent *>(aFuncContextP->getCallerContext());
+ TCustomImplDS *datastoreP = static_cast<TCustomImplDS *>(agentP->fScriptContextDatastore);
+ timecontext_t actual,tctx;
+ if (datastoreP)
+ tctx = static_cast<TCustomDSConfig *>(datastoreP->getDSConfig())->fDataTimeZone;
+ else
+ tctx = agentP->fConfigP->fCurrentDateTimeZone;
+ // get timestamp
+ TTimestampField *tsP = static_cast<TTimestampField *>(aFuncContextP->getLocalVar(0));
+ // convert and get actually resulting context back
+ lineartime_t ts = tsP->getTimestampAs(tctx,&actual);
+ // unfloat floats if selected
+ if (aFuncContextP->getLocalVar(1)->getAsBoolean() && TCTX_IS_UNKNOWN(actual)) actual=tctx; // unfloat
+ // assign it to result
+ static_cast<TTimestampField *>(aTermP)->setTimestampAndContext(ts,actual);
+ }; // func_ConvertToDataZone
+
+
+}; // TCustomAgentFuncs
+
+
+const uInt8 param_CheckAuth[] = { VAL(fty_string), VAL(fty_string), VAL(fty_integer) };
+const uInt8 param_DBIntToTimestamp[] = { VAL(fty_integer), VAL(fty_string) };
+const uInt8 param_TimestampToDBInt[] = { VAL(fty_timestamp), VAL(fty_string) };
+const uInt8 param_ConvertToDataZone[] = { VAL(fty_timestamp), OPTVAL(fty_integer) };
+
+const uInt8 param_oneString[] = { VAL(fty_string) };
+const uInt8 param_oneInteger[] = { VAL(fty_integer) };
+const uInt8 param_variant[] = { VAL(fty_none) };
+
+
+#ifndef BASED_ON_BINFILE_CLIENT
+
+// builtin function table for login context
+const TBuiltInFuncDef CustomAgentFuncDefs[numCustomAgentFuncs] = {
+ { "AUTHOK", TCustomAgentFuncs::func_AuthOK, fty_integer, 0, NULL },
+ { "CHECKAUTH", TCustomAgentFuncs::func_CheckAuth, fty_integer, 3, param_CheckAuth },
+ { "AUTHUSER", TCustomAgentFuncs::func_AuthUser, fty_string, 0, NULL },
+ { "SETUSERNAME", TCustomAgentFuncs::func_SetUserName, fty_none, 1, param_oneString },
+ { "SETDOMAIN", TCustomAgentFuncs::func_SetDomain, fty_none, 1, param_oneString },
+ { "AUTHSTRING", TCustomAgentFuncs::func_AuthString, fty_string, 0, NULL },
+ { "AUTHDEVICEID", TCustomAgentFuncs::func_AuthDeviceID, fty_string, 0, NULL },
+ { "AUTHTYPE", TCustomAgentFuncs::func_AuthType, fty_integer, 0, NULL },
+ { "UNKNOWNDEVICE", TCustomAgentFuncs::func_Unknowndevice, fty_integer, 0, NULL },
+ { "SETREADONLY", TCustomAgentFuncs::func_SetReadOnly, fty_none, 1, param_oneInteger },
+ { "SETDEBUGLOG", TCustomAgentFuncs::func_SetDebugLog, fty_none, 1, param_oneInteger },
+ { "SETLOG", TCustomAgentFuncs::func_SetLog, fty_none, 1, param_oneInteger },
+ { "SETUSERKEY", TCustomAgentFuncs::func_SetUserKey, fty_none, 1, param_variant },
+ { "SETDEVICEKEY", TCustomAgentFuncs::func_SetDeviceKey, fty_none, 1, param_variant },
+};
+
+#endif // not BASED_ON_BINFILE_CLIENT
+
+
+// builtin function defs for customImpl database and login contexts
+const TBuiltInFuncDef CustomAgentAndDSFuncDefs[numCustomAgentAndDSFuncs] = {
+ #ifndef BASED_ON_BINFILE_CLIENT
+ { "DEVICEKEY", TCustomAgentFuncs::func_DeviceKey, fty_string, 0, NULL },
+ { "USERKEY", TCustomAgentFuncs::func_UserKey, fty_string, 0, NULL },
+ #endif
+ { "DBINTTOTIMESTAMP", TCustomAgentFuncs::func_DBIntToTimestamp, fty_timestamp, 2, param_DBIntToTimestamp },
+ { "TIMESTAMPTODBINT", TCustomAgentFuncs::func_TimestampToDBInt, fty_integer, 2, param_TimestampToDBInt },
+ { "CONVERTTODATAZONE", TCustomAgentFuncs::func_ConvertToDataZone, fty_timestamp, 2, param_ConvertToDataZone },
+};
+
+
+#ifndef BASED_ON_BINFILE_CLIENT
+
+// function table which is chained from login-context function table
+const TFuncTable CustomAgentFuncTable2 = {
+ sizeof(CustomAgentAndDSFuncDefs) / sizeof(TBuiltInFuncDef), // size of table
+ CustomAgentAndDSFuncDefs, // table pointer
+ NULL // no chain func
+};
+
+
+// chain from login context agent funcs to general agent funcs
+extern const TFuncTable CustomDSFuncTable2;
+static void *CustomAgentChainFunc(void *&aCtx)
+{
+ // caller context remains unchanged
+ // -> no change needed
+ // next table is Agent's general function table
+ return (void *)&CustomAgentFuncTable2;
+} // CustomAgentChainFunc
+
+// function table for login context scripts
+const TFuncTable CustomAgentFuncTable = {
+ sizeof(CustomAgentFuncDefs) / sizeof(TBuiltInFuncDef), // size of table
+ CustomAgentFuncDefs, // table pointer
+ CustomAgentChainFunc // chain to general agent funcs.
+};
+
+#endif // not BASED_ON_BINFILE_CLIENT
+
+
+// chain from agent funcs to custom datastore funcs (when chained via CustomDSFuncTable1
+extern const TFuncTable CustomDSFuncTable2;
+static void *CustomDSChainFunc1(void *&aCtx)
+{
+ // caller context for datastore-level functions is the datastore pointer
+ if (aCtx)
+ aCtx = static_cast<TCustomImplAgent *>(aCtx)->fScriptContextDatastore;
+ // next table is custom datastore's
+ return (void *)&CustomDSFuncTable2;
+} // CustomDSChainFunc1
+
+// function table which is used by CustomImplDS scripts to access agent-level funcs and then chain
+// back to datastore level funcs
+const TFuncTable CustomDSFuncTable1 = {
+ sizeof(CustomAgentAndDSFuncDefs) / sizeof(TBuiltInFuncDef), // size of agent's table
+ CustomAgentAndDSFuncDefs, // table pointer to agent's general purpose (non login-context specific) funcs
+ CustomDSChainFunc1 // chain to ODBC datastore level DB functions
+};
+
+
+#endif // SCRIPT_SUPPORT
+
+
+
+// config element parsing
+bool TCustomAgentConfig::localStartElement(const char *aElementName, const char **aAttributes, sInt32 aLine)
+{
+ // checking the elements
+ #ifndef BASED_ON_BINFILE_CLIENT
+ #ifdef SCRIPT_SUPPORT
+ if (strucmp(aElementName,"logininitscript")==0)
+ expectScript(fLoginInitScript,aLine,getAgentFuncTableP());
+ else if (strucmp(aElementName,"logincheckscript")==0)
+ expectScript(fLoginCheckScript,aLine,getAgentFuncTableP());
+ else if (strucmp(aElementName,"loginfinishscript")==0)
+ expectScript(fLoginFinishScript,aLine,getAgentFuncTableP());
+ else
+ #endif // SCRIPT_SUPPORT
+ #endif // BASED_ON_BINFILE_CLIENT
+ // - session level Date/Time info
+ if (
+ strucmp(aElementName,"timestamputc")==0 || // old 2.1 compatible
+ strucmp(aElementName,"timeutc")==0 // new 3.0 variant, unified with datastore level setting
+ ) {
+ // - warn for usage of old timeutc
+ ReportError(false,"Warning: <timestamputc>/<timeutc> is deprecated - please use <datatimezone> instead",aElementName);
+ expectBool(fCurrentDateIsUTC);
+ }
+ else if (strucmp(aElementName,"datatimezone")==0)
+ expectTimezone(fCurrentDateTimeZone);
+ // - session level charset and line ends
+ else if (strucmp(aElementName,"datacharset")==0)
+ expectEnum(sizeof(fDataCharSet),&fDataCharSet,DBCharSetNames,numCharSets);
+ else if (strucmp(aElementName,"datalineends")==0)
+ expectEnum(sizeof(fDataLineEndMode),&fDataLineEndMode,lineEndModeNames,numLineEndModes);
+ // - none known here
+ else
+ return inherited::localStartElement(aElementName,aAttributes,aLine);
+ // ok
+ return true;
+} // TCustomAgentConfig::localStartElement
+
+
+// resolve
+void TCustomAgentConfig::localResolve(bool aLastPass)
+{
+ // convert legacy UTC flag to timezone setting
+ if (fCurrentDateIsUTC)
+ fCurrentDateTimeZone = TCTX_UTC;
+ // Scripts etc.
+ if (aLastPass) {
+ #ifndef BASED_ON_BINFILE_CLIENT
+ #ifdef SCRIPT_SUPPORT
+ // login scripting
+ TScriptContext::resolveScript(getSyncAppBase(),fLoginInitScript,fResolverContext,NULL);
+ TScriptContext::resolveScript(getSyncAppBase(),fLoginCheckScript,fResolverContext,NULL);
+ TScriptContext::resolveScript(getSyncAppBase(),fLoginFinishScript,fResolverContext,NULL);
+ ResolveAPIScripts();
+ // - derivates' scripts are resolved by now, we can dispose of the resolver context
+ // NOTE: this is true
+ if (fResolverContext) delete fResolverContext;
+ fResolverContext=NULL;
+ #endif
+ #endif // not BASED_ON_BINFILE_CLIENT
+ }
+ // resolve inherited
+ inherited::localResolve(aLastPass);
+} // TCustomAgentConfig::localResolve
+
+
+
+
+/* public TCustomImplAgent members */
+
+
+#ifdef SYSYNC_CLIENT
+TCustomImplAgent::TCustomImplAgent(TSyncClientBase *aSyncClientBaseP, const char *aSessionID) :
+ inherited(aSyncClientBaseP, aSessionID),
+#else
+TCustomImplAgent::TCustomImplAgent(TSyncAppBase *aAppBaseP, TSyncSessionHandle *aSessionHandleP, const char *aSessionID) :
+ inherited(aAppBaseP, aSessionHandleP, aSessionID),
+#endif
+ fConfigP(NULL)
+ #ifdef SCRIPT_SUPPORT
+ ,fScriptContextDatastore(NULL)
+ #endif
+ #ifdef SCRIPT_SUPPORT
+ ,fAgentContext(NULL)
+ #endif
+ #ifdef DBAPI_TUNNEL_SUPPORT
+ ,fTunnelDatastoreP(NULL)
+ #endif
+{
+ // get config for agent and save direct link to agent config for easy reference
+ fConfigP = static_cast<TCustomAgentConfig *>(getRootConfig()->fAgentConfigP);
+ #ifndef BASED_ON_BINFILE_CLIENT
+ #ifdef SCRIPT_SUPPORT
+ // create login script context if there are scripts
+ // Note: derivates might already have initialized fAgentContext here, that's why we
+ // now NULL it via ctor.
+ TScriptContext::rebuildContext(getSyncAppBase(),fConfigP->fLoginInitScript,fAgentContext,this);
+ TScriptContext::rebuildContext(getSyncAppBase(),fConfigP->fLoginCheckScript,fAgentContext,this);
+ TScriptContext::rebuildContext(getSyncAppBase(),fConfigP->fLoginFinishScript,fAgentContext,this,true); // now build vars
+ // Note: derivates will rebuild NOW, AFTER our rebuilds, in the derived constructor
+ #endif
+ #endif
+ // Note: Datastores are already created from config
+} // TCustomImplAgent::TCustomImplAgent
+
+
+// destructor
+TCustomImplAgent::~TCustomImplAgent()
+{
+ // make sure everything is terminated BEFORE destruction of hierarchy begins
+ TerminateSession();
+} // TCustomImplAgent::~TCustomImplAgent
+
+
+// Terminate session
+void TCustomImplAgent::TerminateSession()
+{
+ if (!fTerminated) {
+ // Note that the following will happen BEFORE destruction of
+ // individual datastores, so make sure datastores are already
+ // independent of the agnet's ressources
+ InternalResetSession();
+ #ifdef SCRIPT_SUPPORT
+ // get rid of login context
+ if (fAgentContext) delete fAgentContext;
+ #endif
+ // Make sure datastores know that the agent will go down soon
+ announceDestruction();
+ }
+ inherited::TerminateSession();
+} // TCustomImplAgent::TerminateSession
+
+
+// Reset session
+void TCustomImplAgent::InternalResetSession(void)
+{
+ // reset all datastores now to make everything is done which might need the
+ // Agent before it is destroyed
+ // (Note: TerminateDatastores() will be called again by ancestors)
+ TerminateDatastores();
+} // TCustomImplAgent::InternalResetSession
+
+
+// Virtual version
+void TCustomImplAgent::ResetSession(void)
+{
+ // do my own stuff
+ InternalResetSession();
+ // let ancestor do its stuff
+ inherited::ResetSession();
+} // TCustomImplAgent::ResetSession
+
+
+
+#ifdef DBAPI_TUNNEL_SUPPORT
+
+// initialize session for DBAPI tunnel usage
+localstatus TCustomImplAgent::InitializeTunnelSession(cAppCharP aDatastoreName)
+{
+ localstatus sta;
+
+ // find datastore to work with
+ TLocalDSConfig *dsCfgP = getSessionConfig()->getLocalDS(aDatastoreName);
+ if (!dsCfgP) {
+ // no such datastore found
+ sta = DB_NotFound;
+ }
+ else {
+ // found config for given name, instantiate the datastore object
+ fTunnelDatastoreP = static_cast<TCustomImplDS *>(dsCfgP->newLocalDataStore(this));
+ if (!fTunnelDatastoreP)
+ sta = DB_Error;
+ }
+ // done
+ return sta;
+} // TCustomImplAgent::CreateTunnelSession
+
+#endif // DBAPI_TUNNEL_SUPPORT
+
+
+
+
+
+
+
+#ifndef BASED_ON_BINFILE_CLIENT
+
+// check credential string
+// Note: if authentication is successful, odbcDBServer session
+// saves the user key and device key for later reference in subsequent
+// DB accesses.
+bool TCustomImplAgent::SessionLogin(const char *aUserName, const char *aAuthString, TAuthSecretTypes aAuthStringType, const char *aDeviceID)
+{
+ bool authok = false;
+ string nonce;
+ bool neednonce = aAuthStringType==sectyp_md5_V10 || aAuthStringType==sectyp_md5_V11;
+
+ if (!fConfigP) return false; // no config -> fail early (no need for cleanup)
+
+ #ifdef SYSYNC_CLIENT
+ #ifndef NO_LOCAL_DBLOGIN
+ // check for eventual client without need for local DB login
+ if (fNoLocalDBLogin) {
+ // just use local DB login name as user key (userkey is probably not needed anyway)
+ fUserKey=fLocalDBUser;
+ // accept as auth ok
+ return true; // return early, no need for cleanup
+ }
+ #else
+ // client without need for local login
+ return true;
+ #warning "we could probably eliminate much more code here"
+ #endif
+ #endif
+ // first step: set defaults
+ fUserKey=aUserName; // user key is equal to user name
+ fDeviceKey=aDeviceID; // device key is equal to device name
+ #ifndef SCRIPT_SUPPORT
+ #define DB_USERNAME aUserName
+ #else
+ #define DB_USERNAME fUserName.c_str()
+ fDomainName.erase(); // no domain name
+ // second step: run script to eventually grant auth before any other method is
+ // needed at all
+ // - set status vars that can be referenced by context funcs in script
+ fStandardAuthOK=false; // not yet authorized, for AUTHOK() func
+ fAuthUser=aUserName; // for AUTHUSER() func
+ fUserName=aUserName; // copy into string var that can be modified by SETUSERNAME()
+ fAuthSecret=aAuthString; // for AUTHSECRET() func
+ fAuthDevice=aDeviceID; // for AUTHDEVICEID() func
+ fAuthType=aAuthStringType; // for AUTHTYPE() func
+ fUnknowndevice=true; // we do not know the device here already
+ // now call and evaluate boolean result
+ TItemField *resP=NULL;
+ fScriptContextDatastore=NULL;
+ if (!TScriptContext::executeWithResult(
+ resP, // can be default result or NULL, will contain result or NULL if no result
+ fAgentContext,
+ fConfigP->fLoginInitScript,
+ fConfigP->getAgentFuncTableP(), // context function table
+ this, // context data (myself)
+ NULL, false, NULL, false
+ )) {
+ authok=false; // script failed, auth failed
+ goto cleanup;
+ }
+ else {
+ if (resP) {
+ // explicit auth or reject at this stage
+ SYSYNC_TRY {
+ // - first let device register itself (and find devicekey eventually)
+ CheckDevice(aDeviceID);
+ // - now get auth result
+ authok = resP->getAsBoolean();
+ }
+ SYSYNC_CATCH(exception &e)
+ // log error
+ PDEBUGPRINTFX(DBG_ERROR,("Exception during CheckDevice after initscript has accepted/rejected auth: %s",e.what()));
+ // fail auth
+ authok=false;
+ SYSYNC_ENDCATCH
+ delete resP;
+ goto cleanup;
+ }
+ // otherwise, we haven't authorized yet
+ authok=false;
+ }
+ #endif
+ SYSYNC_TRY {
+ // fourth step: get device info, if any
+ CheckDevice(aDeviceID);
+ // fifth step: check simpleauth (in base class)
+ // NOTE: overrides any API level checks if simpleauth is set!
+ if (TSyncSession::SessionLogin(aUserName,aAuthString,aAuthStringType,aDeviceID)) {
+ authok=true;
+ goto cleanup;
+ }
+ // sixth step: let DB API level check authorisation
+ authok=CheckLogin(aUserName, DB_USERNAME, aAuthString, aAuthStringType, aDeviceID);
+ #ifdef SCRIPT_SUPPORT
+ // finalize login
+ // - refresh auth status
+ fStandardAuthOK=authok; // for AUTHOK() func
+ // - call script (if any)
+ fScriptContextDatastore=NULL;
+ authok=TScriptContext::executeTest(
+ authok, // default for no script, no result or script error is current auth
+ fAgentContext,
+ fConfigP->fLoginFinishScript,
+ fConfigP->getAgentFuncTableP(), // context function table
+ (void *)this // context data (myself)
+ );
+ #endif
+ }
+ SYSYNC_CATCH(exception &e)
+ // log error
+ PDEBUGPRINTFX(DBG_ERROR,("Exception while trying to access DB for SessionLogin: %s",e.what()));
+ authok=false;
+ SYSYNC_ENDCATCH
+cleanup:
+ // clean up login stuff
+ LoginCleanUp(); // let derived class clean up (like closing transactions etc.)
+ // return status
+ return authok;
+} // TCustomImplAgent::SessionLogin
+
+#endif // BASED_ON_BINFILE_CLIENT
+
+
+} // namespace sysync
+
+/* end of TCustomImplAgent implementation */
+
+// eof
diff --git a/src/sysync/customimplagent.h b/src/sysync/customimplagent.h
new file mode 100755
index 0000000..80ffe0e
--- /dev/null
+++ b/src/sysync/customimplagent.h
@@ -0,0 +1,229 @@
+/**
+ * @File customimplagent.h
+ *
+ * @Author Lukas Zeller (luz@synthesis.ch)
+ *
+ * @brief TCustomImplAgent
+ * Base class for agenst (servers or clients) with customizable datastores
+ * based on TCustomImplDS
+ *
+ * Copyright (c) 2001-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ * @Date 2005-12-05 : luz : separated from odbcdbagent
+ */
+
+#ifndef CUSTOMIMPLAGENT_H
+#define CUSTOMIMPLAGENT_H
+
+// includes
+
+#include "stdlogicagent.h"
+#include "stdlogicds.h"
+#include "multifielditem.h"
+#include "scriptcontext.h"
+
+#ifdef BASED_ON_BINFILE_CLIENT
+ #include "binfileimplclient.h"
+#endif
+
+using namespace sysync;
+
+namespace sysync {
+
+#ifdef SCRIPT_SUPPORT
+
+// publish as derivates might need it
+extern const TFuncTable CustomAgentFuncTable;
+extern const TFuncTable CustomAgentFuncTable2;
+extern const TFuncTable CustomDSFuncTable1;
+// - define count here as derivates will access the funcdefs table directly
+const int numCustomAgentFuncs=14;
+extern const TBuiltInFuncDef CustomAgentFuncDefs[numCustomAgentFuncs];
+#ifndef BASED_ON_BINFILE_CLIENT
+ const int numCustomAgentAndDSFuncs=5;
+#else
+ const int numCustomAgentAndDSFuncs=3;
+#endif
+extern const TBuiltInFuncDef CustomAgentAndDSFuncDefs[numCustomAgentAndDSFuncs];
+
+#endif
+
+
+// config names for character sets
+extern const char * const DBCharSetNames[numCharSets];
+
+// database field types
+typedef enum {
+ dbft_string,
+ dbft_blob, // binary contents of string will be used, no translations
+ dbft_date,
+ dbft_time,
+ dbft_timefordate, // to add time to a date
+ dbft_timestamp,
+ dbft_dateonly, // timestamp, but carrying only a date, so no zone correction is applied
+ dbft_uctoffsfortime_hours, // to add UTC offset to a time or timestamp
+ dbft_uctoffsfortime_mins, // to add UTC offset to a time or timestamp
+ dbft_uctoffsfortime_secs, // to add UTC offset to a time or timestamp
+ dbft_zonename, // to get zone name of a timestamp
+ dbft_numeric, // string value is used w/o any quotes in SQL
+ dbft_lineartime, // linear time as-is
+ dbft_lineardate, // linear date as-is
+ dbft_unixtime_s, // integer value representing UNIX epoch time stamp in seconds
+ dbft_unixtime_ms, // integer value representing UNIX epoch time stamp in milliseconds
+ dbft_unixtime_us, // integer value representing UNIX epoch time stamp in microseconds
+ dbft_unixdate_s, // integer value representing UNIX epoch date in seconds
+ dbft_unixdate_ms, // integer value representing UNIX epoch date in milliseconds
+ dbft_unixdate_us, // integer value representing UNIX epoch date in microseconds
+ numDBfieldTypes
+} TDBFieldType;
+
+extern const char * const DBFieldTypeNames[numDBfieldTypes];
+
+// integer in DB <-> lineartime conversions
+lineartime_t dbIntToLineartime(sInt64 aDBInt, TDBFieldType aDbfty);
+sInt64 lineartimeToDbInt(lineartime_t aLinearTime, TDBFieldType aDbfty);
+
+
+// forward
+class TCustomDSConfig;
+#ifdef SCRIPT_SUPPORT
+class TScriptContext;
+#endif
+
+
+class TCustomAgentConfig:
+ #ifdef BASED_ON_BINFILE_CLIENT
+ public TBinfileClientConfig
+ #elif defined(SYSYNC_CLIENT)
+ public TClientConfig
+ #else
+ public TServerConfig
+ #endif
+{
+ #ifdef BASED_ON_BINFILE_CLIENT
+ typedef TBinfileClientConfig inherited;
+ #elif defined(SYSYNC_CLIENT)
+ typedef TClientConfig inherited;
+ #else
+ typedef TServerConfig inherited;
+ #endif
+public:
+ TCustomAgentConfig(TConfigElement *aParentElement);
+ virtual ~TCustomAgentConfig();
+ // properties
+ #ifdef SCRIPT_SUPPORT
+ // - login scripts
+ string fLoginInitScript; // called to initialize variables and early check for auth
+ string fLoginCheckScript; // called to check permission for all users in question
+ string fLoginFinishScript; // called when login is finished (failed or successful)
+ #endif // SCRIPT_SUPPORT
+ // - Date/Time info
+ bool fCurrentDateIsUTC; //%%% legacy flag for compatibility, superseded by fCurrentDateTimeZone
+ timecontext_t fCurrentDateTimeZone; // time zone for storing/retrieving timestamps in DB
+ // - charset to be used in the data table
+ TCharSets fDataCharSet;
+ // - line end mode to be used in the data table for multiline data
+ TLineEndModes fDataLineEndMode;
+ #ifdef SCRIPT_SUPPORT
+ // provided to allow derivates to add API specific script functions to scripts called from customagent
+ virtual const TFuncTable *getAgentFuncTableP(void) {
+ #ifndef BASED_ON_BINFILE_CLIENT
+ return &CustomAgentFuncTable;
+ #else
+ return NULL;
+ #endif
+ };
+ TScriptContext *fResolverContext;
+ #endif
+protected:
+ // check config elements
+ virtual bool localStartElement(const char *aElementName, const char **aAttributes, sInt32 aLine);
+ virtual void clear();
+ virtual void localResolve(bool aLastPass);
+ #ifdef SCRIPT_SUPPORT
+ virtual void ResolveAPIScripts(void) { /* NOP */ };
+ #endif
+}; // TCustomAgentConfig
+
+
+class TCustomImplDS;
+
+class TCustomImplAgent:
+ #ifdef BASED_ON_BINFILE_CLIENT
+ public TBinfileImplClient
+ #else
+ public TStdLogicAgent
+ #endif
+{
+ friend class TCustomCommonFuncs;
+ friend class TCustomAgentFuncs;
+ #ifdef BASED_ON_BINFILE_CLIENT
+ typedef TBinfileImplClient inherited;
+ #else
+ typedef TStdLogicAgent inherited;
+ #endif
+public:
+ #ifdef SYSYNC_CLIENT
+ TCustomImplAgent(TSyncClientBase *aSyncClientBaseP, const char *aSessionID);
+ #else
+ TCustomImplAgent(TSyncAppBase *aAppBaseP, TSyncSessionHandle *aSessionHandleP, const char *aSessionID);
+ #endif
+ virtual ~TCustomImplAgent();
+ virtual void TerminateSession(void); // Terminate session, like destructor, but without actually destructing object itself
+ virtual void ResetSession(void); // Resets session (but unlike TerminateSession, session might be re-used)
+ void InternalResetSession(void); // static implementation for calling through virtual destructor and virtual ResetSession();
+ // Custom agent config
+ TCustomAgentConfig *fConfigP;
+ // Needed for general script support
+ #ifdef SCRIPT_SUPPORT
+ TCustomImplDS *fScriptContextDatastore; // needed because datastore chains agent's script funcs
+ #endif // SCRIPT_SUPPORT
+ #ifdef DBAPI_TUNNEL_SUPPORT
+ // Initialize a datastore tunnel session
+ virtual localstatus InitializeTunnelSession(cAppCharP aDatastoreName);
+ #endif
+ // Login and device management only if not based on binfile client
+ #ifndef BASED_ON_BINFILE_CLIENT
+ // - login for this session
+ virtual bool SessionLogin(const char *aUserName, const char *aAuthString, TAuthSecretTypes aAuthStringType, const char *aDeviceID);
+ // - clean up after all login activity is over (including finishscript)
+ virtual void LoginCleanUp(void) { /* NOP at this level */ };
+ // - get user key
+ const char *getUserKey(void) { return fUserKey.c_str(); };
+ // status vars, valid during login context script execution only!
+ #ifdef SCRIPT_SUPPORT
+ bool fStandardAuthOK; // result of standard auth checking
+ string fUserName; // possibly modified user name used to find user in DB
+ string fDomainName; // domain name possibly derived from LOCALURI or similar
+ const char *fAuthUser; // original username as sent by remote
+ const char *fAuthSecret; // secret from remote (normally, MD5 of user:password)
+ const char *fAuthDevice; // device ID
+ TAuthSecretTypes fAuthType; // set if V11-type secret
+ bool fUnknowndevice; // set if device is unknown so far
+ #endif // SCRIPT_SUPPORT
+ // session vars
+ string fUserKey; // user key obtained at SessionLogin
+ string fDeviceKey; // device key obtained at SessionLogin
+protected:
+ // - check device ID related stuff
+ virtual void CheckDevice(const char *aDeviceID) { /* NOP here */ };
+ // - check user/pw (part of SessionLogin process)
+ virtual bool CheckLogin(const char *aOriginalUserName, const char *aModifiedUserName, const char *aAuthString, TAuthSecretTypes aAuthStringType, const char *aDeviceID) { return false; /* cannot login */ };
+ // - remote device is analyzed, eventually save status
+ virtual void remoteAnalyzed(void) { /* NOP at this level */ };
+ // script contexts
+ #endif // BASED_ON_BINFILE_CLIENT
+ #ifdef SCRIPT_SUPPORT
+ TScriptContext *fAgentContext;
+ #endif // SCRIPT_SUPPORT
+ #ifdef DBAPI_TUNNEL_SUPPORT
+ TCustomImplDS *fTunnelDatastoreP;
+ #endif // DBAPI_TUNNEL_SUPPORT
+}; // TCustomImplAgent
+
+
+} // namespace sysync
+
+#endif // CUSTOMIMPLAGENT_H
+
+// eof
diff --git a/src/sysync/customimplds.cpp b/src/sysync/customimplds.cpp
new file mode 100755
index 0000000..b1b7589
--- /dev/null
+++ b/src/sysync/customimplds.cpp
@@ -0,0 +1,3272 @@
+/**
+ * @File customimpl.cpp
+ *
+ * @Author Lukas Zeller (luz@synthesis.ch)
+ *
+ * @brief TCustomImplDS
+ * Base class for customizable datastores (mainly extended DB mapping features
+ * common to all derived classes like ODBC, DBAPI etc.).
+ *
+ * Copyright (c) 2001-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ * @Date 2005-12-05 : luz : separated from odbcapids
+ */
+
+
+// includes
+#include "sysync.h"
+#include "multifielditem.h"
+#include "mimediritemtype.h"
+#include "customimplds.h"
+#include "customimplagent.h"
+
+
+namespace sysync {
+
+#ifndef BASED_ON_BINFILE_CLIENT
+#ifdef SYDEBUG
+const char * const MapEntryTypeNames[numMapEntryTypes] = {
+ "invalid",
+ "normal",
+ "tempidmap",
+ "pendingmap"
+};
+#endif
+#endif // not BASED_ON_BINFILE_CLIENTs
+
+
+#ifdef SCRIPT_SUPPORT
+
+class TCustomDSfuncs {
+public:
+
+ // Custom Impl datastore specific script functions
+ // ===============================================
+
+ // string FOLDERKEY()
+ // returns folder key
+ static void func_FolderKey(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ aTermP->setAsString(
+ static_cast<TCustomImplDS *>(aFuncContextP->getCallerContext())->fFolderKey.c_str()
+ );
+ }; // func_FolderKey
+
+
+ // string TARGETKEY()
+ // returns target key
+ static void func_TargetKey(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ aTermP->setAsString(
+ static_cast<TCustomImplDS *>(aFuncContextP->getCallerContext())->fTargetKey.c_str()
+ );
+ }; // func_TargetKey
+
+
+ // integer ARRAYINDEX()
+ // returns current array index when reading or writing an array
+ // in the finish function it denotes the number of array items totally
+ static void func_ArrayIndex(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ aTermP->setAsInteger(
+ static_cast<TCustomImplDS *>(aFuncContextP->getCallerContext())->fArrIdx
+ );
+ }; // func_ArrayIndex
+
+
+ // string PARENTKEY()
+ // returns key of (array) parent object (like %k)
+ static void func_ParentKey(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ aTermP->setAsString(
+ static_cast<TCustomImplDS *>(aFuncContextP->getCallerContext())->fParentKey.c_str()
+ );
+ }; // func_ParentKey
+
+
+ // integer WRITING()
+ // returns true if script is called while writing to DB
+ static void func_Writing(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ aTermP->setAsBoolean(
+ static_cast<TCustomImplDS *>(aFuncContextP->getCallerContext())->fWriting
+ );
+ }; // func_Writing
+
+
+ // integer DELETING()
+ // returns true if script is called while deleting in DB
+ static void func_Deleting(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ aTermP->setAsBoolean(
+ static_cast<TCustomImplDS *>(aFuncContextP->getCallerContext())->fDeleting
+ );
+ }; // func_Deleting
+
+
+ // integer INSERTING()
+ // returns true if script is called while inserting new data to DB
+ static void func_Inserting(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ aTermP->setAsBoolean(
+ static_cast<TCustomImplDS *>(aFuncContextP->getCallerContext())->fInserting
+ );
+ }; // func_Inserting
+
+
+ // string LOGSUBST(string logtext)
+ // returns log placeholders substituted in logtext
+ static void func_LogSubst(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ string logtext;
+ aFuncContextP->getLocalVar(0)->getAsString(logtext); // log text
+ // perform substitutions
+ static_cast<TCustomImplDS *>(aFuncContextP->getCallerContext())->DoLogSubstitutions(logtext,false);
+ // return it
+ aTermP->setAsString(logtext);
+ }; // func_LogSubst
+
+}; // TCustomDSfuncs
+
+const uInt8 param_LogSubst[] = { VAL(fty_string) };
+
+// builtin function table for datastore level
+const TBuiltInFuncDef CustomDSFuncDefs[] = {
+ { "FOLDERKEY", TCustomDSfuncs::func_FolderKey, fty_string, 0, NULL },
+ { "TARGETKEY", TCustomDSfuncs::func_TargetKey, fty_string, 0, NULL },
+ { "ARRAYINDEX", TCustomDSfuncs::func_ArrayIndex, fty_integer, 0, NULL },
+ { "PARENTKEY", TCustomDSfuncs::func_ParentKey, fty_string, 0, NULL },
+ { "WRITING", TCustomDSfuncs::func_Writing, fty_integer, 0, NULL },
+ { "INSERTING", TCustomDSfuncs::func_Inserting, fty_integer, 0, NULL },
+ { "DELETING", TCustomDSfuncs::func_Deleting, fty_integer, 0, NULL },
+ { "LOGSUBST", TCustomDSfuncs::func_LogSubst, fty_string, 1, param_LogSubst }
+};
+
+
+// chain to generic local engine datastore funcs
+static void *CustomDSChainFunc2(void *&aCtx)
+{
+ // context pointer for datastore-level funcs is the datastore
+ // -> no change needed
+ // next table is localEngineDS's
+ return (void *)&DBFuncTable;
+} // CustomDSChainFunc2
+
+const TFuncTable CustomDSFuncTable2 = {
+ sizeof(CustomDSFuncDefs) / sizeof(TBuiltInFuncDef), // size of table
+ CustomDSFuncDefs, // table pointer
+ CustomDSChainFunc2 // chain generic DB functions
+};
+
+
+
+#endif
+
+
+
+// Config
+// ======
+
+TCustomDSConfig::TCustomDSConfig(const char* aName, TConfigElement *aParentElement) :
+ inherited(aName,aParentElement),
+ fFieldMappings("mappings",this)
+ #ifdef SCRIPT_SUPPORT
+ ,fResolveContextP(NULL)
+ ,fDSScriptsResolved(false)
+ #endif
+{
+ // nop so far
+ clear();
+} // TCustomDSConfig::TCustomDSConfig
+
+
+TCustomDSConfig::~TCustomDSConfig()
+{
+ // clear
+ clear();
+} // TCustomDSConfig::~TCustomDSConfig
+
+
+// init defaults
+void TCustomDSConfig::clear(void)
+{
+ // init defaults
+ // - multi-folder support
+ fMultiFolderDB=false;
+ // Data table options
+ // - charset to be used in the data table
+ fDataCharSet=chs_ansi; // suitable default for ODBC
+ // - line end mode to be used in the data table for multiline data
+ fDataLineEndMode=lem_dos; // default to CRLF, as this seems to be safest assumption
+ // - if set, causes that data is read from DB first and then merged
+ // with updated fields. Not needed in normal SQL DBs, as they can
+ // update a subset of all columns. However, might still be needed
+ // for special cases like Achil that needs record size calculation
+ // or sortfeldXXX generation.
+ fUpdateAllFields=false;
+ // - Date/Time info
+ fDataIsUTC=false; // compatibility flag only, will set fDataTimeZone to TCTX_UTC at Resolve if set
+ fDataTimeZone=TCTX_SYSTEM; // default to local system time
+ fUserZoneOutput=true; // by default, non-floating timestamps are moved to user zone after reading from DB. Only if zone context for timestamp fields is really retrieved from the DB on a per record level, this can be switched off
+ #ifndef BASED_ON_BINFILE_CLIENT
+ // - flag indicating that admin tables have DS 1.2 support (map entrytype, map flags, fResumeAlertCode, fLastSuspend, fLastSuspendIdentifier
+ fResumeSupport=false;
+ fResumeItemSupport=false; // no item resume as well
+ // - admin capability info
+ fSyncTimeStampAtEnd=false; // if set, time point of sync is taken AFTER last write to DB (for single-user DBs like FMPro). Note that target table layout is different in this case!
+ fOneWayFromRemoteSupported=false; // compatible with old layout of target tables, no support
+ #endif // not BASED_ON_BINFILE_CLIENT
+ fStoreSyncIdentifiers=false; // compatible with old layout of target tables, no support
+ // clear embedded
+ fFieldMappings.clear();
+ #ifdef SCRIPT_SUPPORT
+ // - script called after admin data is loaded (before any data access takes place)
+ fAdminReadyScript.erase();
+ // - script called at end of sync session
+ fSyncEndScript.erase();
+ // - clear script resolve context
+ if (fResolveContextP) {
+ delete fResolveContextP;
+ fResolveContextP=NULL;
+ }
+ fDSScriptsResolved=false;
+ #endif
+ // clear inherited
+ inherited::clear();
+} // TCustomDSConfig::clear
+
+
+// config element parsing
+bool TCustomDSConfig::localStartElement(const char *aElementName, const char **aAttributes, sInt32 aLine)
+{
+ // multi-folder-support
+ if (strucmp(aElementName,"multifolder")==0)
+ expectBool(fMultiFolderDB);
+ // user data related properties
+ else if (strucmp(aElementName,"datacharset")==0)
+ expectEnum(sizeof(fDataCharSet),&fDataCharSet,DBCharSetNames,numCharSets);
+ else if (strucmp(aElementName,"datalineends")==0)
+ expectEnum(sizeof(fDataLineEndMode),&fDataLineEndMode,lineEndModeNames,numLineEndModes);
+ else if (strucmp(aElementName,"updateallfields")==0)
+ expectBool(fUpdateAllFields);
+ // - Date/Time info
+ else if (strucmp(aElementName,"timeutc")==0) {
+ // - warn for usage of old timeutc
+ ReportError(false,"Warning: <timeutc> is deprecated - please use <datatimezone> instead",aElementName);
+ expectBool(fDataIsUTC);
+ }
+ else if (strucmp(aElementName,"datatimezone")==0)
+ expectTimezone(fDataTimeZone);
+ else if (strucmp(aElementName,"userzoneoutput")==0)
+ expectBool(fUserZoneOutput);
+ #ifndef BASED_ON_BINFILE_CLIENT
+ // - admin capability info
+ else if (strucmp(aElementName,"synctimestampatend")==0)
+ expectBool(fSyncTimeStampAtEnd);
+ else if (strucmp(aElementName,"fromremoteonlysupport")==0)
+ expectBool(fOneWayFromRemoteSupported);
+ else if (strucmp(aElementName,"resumesupport")==0)
+ expectBool(fResumeSupport);
+ else if (strucmp(aElementName,"resumeitemsupport")==0)
+ expectBool(fResumeItemSupport);
+ #endif
+ else if (
+ strucmp(aElementName,"storelastsyncidentifier")==0 ||
+ strucmp(aElementName,"storesyncidentifiers")==0
+ )
+ expectBool(fStoreSyncIdentifiers);
+ #ifdef SCRIPT_SUPPORT
+ else if (strucmp(aElementName,"adminreadyscript")==0)
+ expectScript(fAdminReadyScript, aLine, getDSFuncTableP());
+ else if (strucmp(aElementName,"syncendscript")==0)
+ expectScript(fSyncEndScript, aLine, getDSFuncTableP());
+ #endif
+ // - field mappings
+ else if (strucmp(aElementName,"fieldmap")==0) {
+ // check reference argument
+ const char* ref = getAttr(aAttributes,"fieldlist");
+ if (!ref) {
+ ReportError(true,"fieldmap missing 'fieldlist' attribute");
+ }
+ else {
+ // look for field list
+ TMultiFieldDatatypesConfig *mfcfgP =
+ dynamic_cast<TMultiFieldDatatypesConfig *>(getSyncAppBase()->getRootConfig()->fDatatypesConfigP);
+ if (!mfcfgP) SYSYNC_THROW(TConfigParseException("no multifield config"));
+ TFieldListConfig *cfgP = mfcfgP->getFieldList(ref);
+ if (!cfgP)
+ return fail("fieldlist '%s' not defined for fieldmap",ref);
+ // - store field list reference in map
+ fFieldMappings.fFieldListP=cfgP;
+ // - let element handle parsing
+ expectChildParsing(fFieldMappings);
+ }
+ }
+ // - none known here
+ else
+ return inherited::localStartElement(aElementName,aAttributes,aLine);
+ // ok
+ return true;
+} // TCustomDSConfig::localStartElement
+
+
+// resolve
+void TCustomDSConfig::localResolve(bool aLastPass)
+{
+ // convert legacy UTC flag to timezone setting
+ if (fDataIsUTC)
+ fDataTimeZone = TCTX_UTC;
+ // scripts
+ #ifdef SCRIPT_SUPPORT
+ if (aLastPass) {
+ // resolve map scripts now in case they haven't been resolved already
+ ResolveDSScripts();
+ }
+ #endif
+ // resolve children
+ fFieldMappings.Resolve(aLastPass);
+ #ifdef SCRIPT_SUPPORT
+ // Now everything is resolved, we can forget the resolve context
+ if (aLastPass && fResolveContextP) {
+ delete fResolveContextP;
+ fResolveContextP = NULL;
+ }
+ #endif
+ // resolve inherited
+ inherited::localResolve(aLastPass);
+} // TCustomDSConfig::localResolve
+
+
+#ifdef SCRIPT_SUPPORT
+
+// resolve DS related scripts, but make sure we do that only once
+// Note: MAKE SURE that order of resolving is same as rebuilding order!
+void TCustomDSConfig::ResolveDSScripts(void)
+{
+ // resolve
+ if (!fDSScriptsResolved) {
+ // resolve eventual API level scripts first
+ apiResolveScripts();
+ // resolve start and end scripts first
+ TScriptContext::resolveScript(getSyncAppBase(),fAdminReadyScript,fResolveContextP,NULL);
+ TScriptContext::resolveScript(getSyncAppBase(),fSyncEndScript,fResolveContextP,NULL);
+ // - option filter generator script
+ TScriptContext::resolveScript(getSyncAppBase(),fFieldMappings.fOptionFilterScript,fResolveContextP,fFieldMappings.fFieldListP);
+ // - map scripts
+ TScriptContext::resolveScript(getSyncAppBase(),fFieldMappings.fInitScript,fResolveContextP,fFieldMappings.fFieldListP);
+ TScriptContext::resolveScript(getSyncAppBase(),fFieldMappings.fAfterReadScript,fResolveContextP,fFieldMappings.fFieldListP);
+ TScriptContext::resolveScript(getSyncAppBase(),fFieldMappings.fBeforeWriteScript,fResolveContextP,fFieldMappings.fFieldListP);
+ TScriptContext::resolveScript(getSyncAppBase(),fFieldMappings.fAfterWriteScript,fResolveContextP,fFieldMappings.fFieldListP);
+ TScriptContext::resolveScript(getSyncAppBase(),fFieldMappings.fFinishScript,fResolveContextP,fFieldMappings.fFieldListP);
+ TScriptContext::resolveScript(getSyncAppBase(),fFieldMappings.fFinalisationScript,fResolveContextP,fFieldMappings.fFieldListP);
+ fDSScriptsResolved=true;
+ }
+} // TCustomDSConfig::ResolveDSScripts
+
+#endif
+
+
+// transfer size limits from map to type
+static void transferMapOptionsToType(TFieldMapList &aFml, TMultiFieldItemType *aItemTypeP, sInt16 aMaxRepeat, sInt16 aRepInc)
+{
+ TFieldMapList::iterator pos;
+ TFieldMapItem *fmiP;
+
+ for (pos=aFml.begin(); pos!=aFml.end(); pos++) {
+ fmiP = *pos;
+ #ifdef ARRAYDBTABLES_SUPPORT
+ if (fmiP->isArray()) {
+ // is array, recurse into fields of array
+ TFieldMapArrayItem *fmaiP = static_cast<TFieldMapArrayItem *>(fmiP);
+ transferMapOptionsToType(
+ fmaiP->fArrayFieldMapList,
+ aItemTypeP,
+ fmaiP->fMaxRepeat,
+ fmaiP->fRepeatInc
+ );
+ }
+ else
+ #endif
+ if (fmiP->fid>=0) {
+ // normal map, apply to all related fields
+ if (aMaxRepeat==0) aMaxRepeat=1; // unlimited repeat is only applied to first field
+ #ifdef ARRAYFIELD_SUPPORT
+ TFieldDefinition *fdP=aItemTypeP->getFieldDefinition(fmiP->fid);
+ if (fdP && fdP->array) {
+ // this is an array field, disable fid offsetting
+ aMaxRepeat=1;
+ }
+ #endif
+ for (sInt16 k=0; k<aMaxRepeat; k++) {
+ TFieldOptions *optP = aItemTypeP->getFieldOptions(fmiP->fid+k*aRepInc);
+ if (!optP) break;
+ if (
+ optP->maxsize==FIELD_OPT_MAXSIZE_NONE || // no size defined yet
+ optP->maxsize==FIELD_OPT_MAXSIZE_UNKNOWN || // or defined as unknown
+ (optP->maxsize>sInt32(fmiP->maxsize) && // or defined size is larger than that one set in the mapping...
+ fmiP->maxsize!=0) // ..but map size is not unlimited
+ ) {
+ // set new max size
+ optP->maxsize = fmiP->maxsize;
+ }
+ if (fmiP->notruncate)
+ optP->notruncate=true;
+ }
+ }
+ }
+} // transferMapOptionsToType
+
+
+// Add (probably datastore-specific) limits such as MaxSize and NoTruncate to types
+void TCustomDSConfig::addTypeLimits(TLocalEngineDS *aLocalDatastoreP, TSyncSession *aSessionP)
+{
+ // add field size limitations from map to all types
+ TSyncItemTypePContainer::iterator pos;
+ TSyncItemTypePContainer *typesP = &(aLocalDatastoreP->fRxItemTypes);
+ for (uInt8 i=0; i<2; i++) {
+ for (pos=typesP->begin(); pos!=typesP->end(); pos++) {
+ // apply maps to type
+ transferMapOptionsToType(
+ fFieldMappings.fFieldMapList,
+ static_cast<TMultiFieldItemType *>(*pos),
+ 1,1 // single instance only
+ );
+ }
+ typesP = &(aLocalDatastoreP->fTxItemTypes);
+ }
+} // TCustomDSConfig::addTypeLimits
+
+
+// proptotype to make compiler happy
+bool parseMap(TCustomDSConfig *aCustomDSConfig, TConfigElement *cfgP, bool aIsArray, TFieldListConfig *aFieldListP, TFieldMapList &aFieldMapList, const char **aAttributes);
+
+// parse map items
+bool parseMap(TCustomDSConfig *aCustomDSConfig, TConfigElement *cfgP, bool aIsArray, TFieldListConfig *aFieldListP, TFieldMapList &aFieldMapList, const char **aAttributes)
+{
+ TFieldMapItem *mapitemP;
+ sInt16 fid = VARIDX_UNDEFINED;
+
+ // base field reference is possible for arrays too, to specify the relevant array size
+ const char* ref = cfgP->getAttr(aAttributes,aIsArray ? "sizefrom" : "references");
+ if (ref) {
+ // get fid for referenced field
+ if (!aCustomDSConfig->fFieldMappings.fFieldListP) SYSYNC_THROW(TConfigParseException("map with no field list defined"));
+ #ifdef SCRIPT_SUPPORT
+ fid = TConfigElement::getFieldIndex(ref,aCustomDSConfig->fFieldMappings.fFieldListP,aCustomDSConfig->fResolveContextP);
+ #else
+ fid = TConfigElement::getFieldIndex(ref,aCustomDSConfig->fFieldMappings.fFieldListP);
+ #endif
+ }
+ #ifdef ARRAYDBTABLES_SUPPORT
+ if (aIsArray) {
+ // array container
+ mapitemP = aCustomDSConfig->newFieldMapArrayItem(aCustomDSConfig,cfgP);
+ // save size field reference if any
+ mapitemP->fid=fid;
+ // extra attributes for derived classes
+ mapitemP->checkAttrs(aAttributes);
+ // let array container parse details
+ cfgP->expectChildParsing(*mapitemP);
+ }
+ else
+ #endif
+ {
+ // simple map
+ cfgP->expectEmpty(); // plain maps may not have content
+ // process creation of map item
+ const char* nam = cfgP->getAttr(aAttributes,"name");
+ const char* type = cfgP->getAttr(aAttributes,"type");
+ const char* mode = cfgP->getAttr(aAttributes,"mode");
+ bool truncate = true;
+ cfgP->getAttrBool(aAttributes,"truncate",truncate,true);
+ if (!nam || !ref || !type)
+ return cfgP->fail("map must have 'name', 'references', 'type' and 'mode' attributes at least");
+ if (fid==VARIDX_UNDEFINED)
+ return cfgP->fail("map references unknown field '%s'",ref);
+ // convert type
+ sInt16 ty;
+ if (!StrToEnum(DBFieldTypeNames,numDBfieldTypes,ty,type))
+ return cfgP->fail("unknown type '%s'",type);
+ // convert mode
+ // - needed flags
+ bool rd,wr,fins,fupd;
+ // - optional flags
+ bool asparam=false,floatingts=false,needsfinalisation=false;
+ if (mode) {
+ rd=false,wr=false; fins=false; fupd=false;
+ while (*mode) {
+ if (tolower(*mode)=='r') rd=true;
+ else if (tolower(*mode)=='w') { wr=true; fins=true; fupd=true; } // for both insert and update
+ else if (tolower(*mode)=='i') { wr=true; fins=true; } // insert only
+ else if (tolower(*mode)=='u') { wr=true; fupd=true; } // update only
+ else if (tolower(*mode)=='p') { asparam=true; } // map as parameter (e.g. ODBC parameter mechanism for INSERT/UPDATE statements)
+ else if (tolower(*mode)=='f') { floatingts=true; } // map as floating time field (will be written as-is, no conversion from/to DB time zone takes place)
+ else if (tolower(*mode)=='x') { needsfinalisation=true; } // needs to be kept for finalisation at end of session (for relational link updates etc.)
+ else
+ return cfgP->fail("invalid mode '%c'",*mode);
+ // next char
+ mode++;
+ }
+ }
+ else {
+ // default mode for needed flags
+ rd=true; wr=true; fins=true; fupd=true;
+ }
+ // Optionals
+ // - size
+ sInt32 sz=0; // default to unlimited
+ if (!cfgP->getAttrLong(aAttributes,"size",sz,true))
+ cfgP->fail("invalid size specification");
+ // - statement index
+ sInt16 setno=0; // default to 0
+ if (!cfgP->getAttrShort(aAttributes,"set_no",setno,true))
+ cfgP->fail("invalid set_no specification");
+ // create mapitem, name is SQL field name
+ mapitemP = aCustomDSConfig->newFieldMapItem(nam,cfgP);
+ mapitemP->fid=fid;
+ mapitemP->dbfieldtype=(TDBFieldType)ty;
+ mapitemP->readable=rd;
+ mapitemP->writable=wr;
+ mapitemP->for_insert=fins;
+ mapitemP->for_update=fupd;
+ mapitemP->as_param=asparam;
+ mapitemP->floating_ts=floatingts;
+ mapitemP->needs_finalisation=needsfinalisation;
+ mapitemP->maxsize=(uInt32)sz;
+ mapitemP->notruncate=!truncate;
+ mapitemP->setNo=(uInt16)setno;
+ // extra attributes for derived classes
+ mapitemP->checkAttrs(aAttributes);
+ } // if normal map
+ // - and add it to the list
+ aFieldMapList.push_back(mapitemP);
+ return true;
+} // parseMap
+
+
+// Field Map item
+// ==============
+
+TFieldMapItem::TFieldMapItem(const char *aElementName, TConfigElement *aParentElement) :
+ TConfigElement(aElementName,aParentElement)
+{
+ // default suitable for array container
+ readable=false;
+ writable=false;
+ for_insert=false;
+ for_update=false;
+ as_param=false;
+ floating_ts=false;
+ needs_finalisation=false;
+ setNo=0; // default set is 0
+ maxsize=0; // no max size
+ notruncate=false; // allow truncation by default
+ fid=VARIDX_UNDEFINED; // no field
+ dbfieldtype=dbft_string; // default to string
+} // TFieldMapItem::TFieldMapItem
+
+
+
+#ifdef ARRAYDBTABLES_SUPPORT
+
+// array container
+// ===============
+
+TFieldMapArrayItem::TFieldMapArrayItem(TCustomDSConfig *aCustomDSConfigP, TConfigElement *aParentElement) :
+ TFieldMapItem("array",aParentElement),
+ fCustomDSConfigP(aCustomDSConfigP)
+{
+ clear();
+} // TFieldMapArrayItem::TFieldMapArrayItem
+
+
+TFieldMapArrayItem::~TFieldMapArrayItem()
+{
+ // nop so far
+ clear();
+} // TFieldMapArrayItem::~TFieldMapArrayItem
+
+
+// init defaults
+void TFieldMapArrayItem::clear(void)
+{
+ // init defaults
+ // - clear values
+ #ifdef OBJECT_FILTERING
+ fNoItemsFilter.erase();
+ #endif
+ fMaxRepeat=1;
+ fRepeatInc=1;
+ fStoreEmpty=false;
+ #ifdef SCRIPT_SUPPORT
+ // - clear scripts
+ fInitScript.erase();
+ fAfterReadScript.erase();
+ fBeforeWriteScript.erase();
+ fAfterWriteScript.erase();
+ fFinishScript.erase();
+ fScriptsResolved=false;
+ #endif
+ // - clear map items
+ TFieldMapList::iterator pos;
+ for (pos=fArrayFieldMapList.begin(); pos!=fArrayFieldMapList.end(); pos++)
+ delete (*pos);
+ fArrayFieldMapList.clear();
+ // clear inherited
+ inherited::clear();
+} // TFieldMapArrayItem::clear
+
+
+
+#ifdef SCRIPT_SUPPORT
+
+void TFieldMapArrayItem::expectScriptUnresolved(string &aTScript,sInt32 aLine, const TFuncTable *aContextFuncs)
+{
+ if (fScriptsResolved) {
+ fail("array scripts must be defined before first <map> within array");
+ }
+ else {
+ expectScript(aTScript,aLine,aContextFuncs);
+ }
+} // TFieldMapArrayItem::expectScriptUnresolved
+
+#endif
+
+
+
+// config element parsing
+bool TFieldMapArrayItem::localStartElement(const char *aElementName, const char **aAttributes, sInt32 aLine)
+{
+ // checking the elements
+ if (strucmp(aElementName,"map")==0) {
+ #ifdef SCRIPT_SUPPORT
+ // early resolve basic map scripts so map entries can refer to local vars
+ if (!fScriptsResolved) ResolveArrayScripts();
+ #endif
+ // now parse map
+ return parseMap(fCustomDSConfigP,this,false,fCustomDSConfigP->fFieldMappings.fFieldListP,fArrayFieldMapList,aAttributes);
+ }
+ /* nested arrays not yet supported
+ // %%%% Note: if we do support them, we need to update
+ // the script resolution stuff above and make ProcessArrayScripts recursive
+ else if (strucmp(aElementName,"array")==0)
+ #ifdef SCRIPT_SUPPORT
+ // early resolve basic map scripts so map entries can refer to local vars
+ if (!fScriptsResolved) ResolveArrayScripts();
+ #endif
+ // now parse nested array map
+ return parseMap(fBaseFieldMappings,this,true,fFieldListP,fArrayFieldMapList,aAttributes);
+ */
+ else if (strucmp(aElementName,"maxrepeat")==0)
+ expectInt16(fMaxRepeat);
+ else if (strucmp(aElementName,"repeatinc")==0)
+ expectInt16(fRepeatInc);
+ else if (strucmp(aElementName,"storeempty")==0)
+ expectBool(fStoreEmpty);
+ #ifdef OBJECT_FILTERING
+ else if (strucmp(aElementName,"noitemsfilter")==0)
+ expectString(fNoItemsFilter);
+ #endif
+ #ifdef SCRIPT_SUPPORT
+ else if (strucmp(aElementName,"initscript")==0)
+ expectScriptUnresolved(fInitScript, aLine, fCustomDSConfigP->fFieldMappings.getDSFuncTableP());
+ else if (strucmp(aElementName,"afterreadscript")==0)
+ expectScriptUnresolved(fAfterReadScript, aLine, fCustomDSConfigP->fFieldMappings.getDSFuncTableP());
+ else if (strucmp(aElementName,"beforewritescript")==0)
+ expectScriptUnresolved(fBeforeWriteScript, aLine, fCustomDSConfigP->fFieldMappings.getDSFuncTableP());
+ else if (strucmp(aElementName,"afterwritescript")==0)
+ expectScriptUnresolved(fAfterWriteScript, aLine, fCustomDSConfigP->fFieldMappings.getDSFuncTableP());
+ else if (strucmp(aElementName,"finishscript")==0)
+ expectScriptUnresolved(fFinishScript, aLine, fCustomDSConfigP->fFieldMappings.getDSFuncTableP());
+ #endif
+ // - none known here
+ else
+ return inherited::localStartElement(aElementName,aAttributes,aLine);
+ // ok
+ return true;
+} // TFieldMapArrayItem::localStartElement
+
+
+
+#ifdef SCRIPT_SUPPORT
+
+// process single array's scripts (resolve or rebuild them)
+void TFieldMapArrayItem::ResolveArrayScripts(void)
+{
+ // resolve
+ TScriptContext::resolveScript(getSyncAppBase(),fInitScript,fCustomDSConfigP->fResolveContextP,fCustomDSConfigP->fFieldMappings.fFieldListP);
+ TScriptContext::resolveScript(getSyncAppBase(),fAfterReadScript,fCustomDSConfigP->fResolveContextP,fCustomDSConfigP->fFieldMappings.fFieldListP);
+ TScriptContext::resolveScript(getSyncAppBase(),fBeforeWriteScript,fCustomDSConfigP->fResolveContextP,fCustomDSConfigP->fFieldMappings.fFieldListP);
+ TScriptContext::resolveScript(getSyncAppBase(),fAfterWriteScript,fCustomDSConfigP->fResolveContextP,fCustomDSConfigP->fFieldMappings.fFieldListP);
+ TScriptContext::resolveScript(getSyncAppBase(),fFinishScript,fCustomDSConfigP->fResolveContextP,fCustomDSConfigP->fFieldMappings.fFieldListP);
+ fScriptsResolved=true;
+} // TFieldMapArrayItem::ResolveArrayScripts
+
+#endif
+
+
+// resolve
+void TFieldMapArrayItem::localResolve(bool aLastPass)
+{
+ if (aLastPass) {
+ #ifdef SCRIPT_SUPPORT
+ // resolve map scripts now in case they haven't been resolved already
+ if (!fScriptsResolved) ResolveArrayScripts();
+ #endif
+ }
+ // resolve inherited
+ inherited::localResolve(aLastPass);
+} // TFieldMapArrayItem::localResolve
+
+#endif
+
+
+// field mappings
+// ==============
+
+
+TFieldMappings::TFieldMappings(const char* aName, TConfigElement *aParentElement) :
+ TConfigElement(aName,aParentElement),
+ fFieldListP(NULL)
+{
+ clear();
+} // TFieldMappings::TFieldMappings
+
+
+TFieldMappings::~TFieldMappings()
+{
+ // nop so far
+ clear();
+} // TFieldMappings::~TFieldMappings
+
+
+// init defaults
+void TFieldMappings::clear(void)
+{
+ // init defaults
+ // - clear map items
+ TFieldMapList::iterator pos;
+ for (pos=fFieldMapList.begin(); pos!=fFieldMapList.end(); pos++)
+ delete (*pos);
+ fFieldMapList.clear();
+ #ifdef SCRIPT_SUPPORT
+ // - clear scripts
+ fOptionFilterScript.erase();
+ fInitScript.erase();
+ fAfterReadScript.erase();
+ fBeforeWriteScript.erase();
+ fAfterWriteScript.erase();
+ fFinishScript.erase(); // fFinishScript is now in use for exit call of datastore handling
+ fFinalisationScript.erase(); // called for each item with fields having the needs_finalisation set (BEFORE fFinishScript)
+ #endif
+ // - clear reference
+ fFieldListP=NULL;
+ // clear inherited
+ inherited::clear();
+} // TFieldMappings::clear
+
+
+#ifdef SCRIPT_SUPPORT
+
+void TFieldMappings::expectScriptUnresolved(string &aTScript,sInt32 aLine, const TFuncTable *aContextFuncs)
+{
+ TCustomDSConfig *dscfgP = static_cast<TCustomDSConfig *>(getParentElement());
+ if (dscfgP->fDSScriptsResolved) {
+ fail("database scripts must be defined before first <map>");
+ }
+ else {
+ expectScript(aTScript,aLine,aContextFuncs);
+ }
+} // TFieldMappings::expectScriptUnresolved
+
+#endif
+
+
+// config element parsing
+bool TFieldMappings::localStartElement(const char *aElementName, const char **aAttributes, sInt32 aLine)
+{
+ TCustomDSConfig *dscfgP = static_cast<TCustomDSConfig *>(getParentElement());
+
+ // checking the elements
+ if (strucmp(aElementName,"map")==0) {
+ #ifdef SCRIPT_SUPPORT
+ // early resolve basic datastore scripts so map entries can refer to local vars already
+ dscfgP->ResolveDSScripts();
+ #endif
+ // now parse map
+ return parseMap(dscfgP,this,false,fFieldListP,fFieldMapList,aAttributes);
+ }
+ else if (strucmp(aElementName,"automap")==0) {
+ // auto-create map entries for all fields in the field list
+ // (this is a convenience function to allow nearly identical usage of textdb and dbapi with text module)
+ if (!fFieldListP) SYSYNC_THROW(TConfigParseException("automap with no field list defined"));
+ // check mode option
+ bool indexAsName=false;
+ if (!getAttrBool(aAttributes,"indexasname",indexAsName,true))
+ fail("invalid indexasname value");
+ // iterate through field definitions and create a string mapping
+ TFieldDefinitionList::iterator pos;
+ sInt16 fid=0;
+ string fieldname;
+ for (pos=fFieldListP->fFields.begin(); pos!=fFieldListP->fFields.end(); ++pos, ++fid) {
+ // create DB field name
+ if (indexAsName)
+ StringObjPrintf(fieldname,"%hd",fid); // use fid as field name
+ else
+ fieldname=TCFG_CSTR(pos->fieldname); // use internal field's name
+ // create mapitem using field index/itemfield name as DB field name
+ TFieldMapItem *mapitemP = static_cast<TCustomDSConfig *>(getParentElement())->newFieldMapItem(fieldname.c_str(),this);
+ mapitemP->fid=fid; // fid corresponds with position in field definitions list
+ mapitemP->dbfieldtype = pos->type==fty_timestamp ? dbft_timestamp : dbft_string; // map timestamps as such, otherwise all are strings
+ mapitemP->readable=true;
+ mapitemP->writable=true;
+ mapitemP->for_insert=true;
+ mapitemP->for_update=true;
+ // - and add it to the list
+ fFieldMapList.push_back(mapitemP);
+ }
+ // that's it
+ expectEmpty();
+ }
+ #ifdef ARRAYDBTABLES_SUPPORT
+ else if (strucmp(aElementName,"array")==0) {
+ #ifdef SCRIPT_SUPPORT
+ // early resolve basic datastore scripts so array map entries can refer to local vars already!
+ dscfgP->ResolveDSScripts();
+ #endif
+ // now parse array map
+ return parseMap(dscfgP,this,true,fFieldListP,fFieldMapList,aAttributes);
+ }
+ #endif
+ #ifdef SCRIPT_SUPPORT
+ else if (strucmp(aElementName,"optionfilterscript")==0)
+ expectScriptUnresolved(fOptionFilterScript, aLine, getDSFuncTableP());
+ else if (strucmp(aElementName,"initscript")==0)
+ expectScriptUnresolved(fInitScript, aLine, getDSFuncTableP());
+ else if (strucmp(aElementName,"afterreadscript")==0)
+ expectScriptUnresolved(fAfterReadScript, aLine, getDSFuncTableP());
+ else if (strucmp(aElementName,"beforewritescript")==0)
+ expectScriptUnresolved(fBeforeWriteScript, aLine, getDSFuncTableP());
+ else if (strucmp(aElementName,"afterwritescript")==0)
+ expectScriptUnresolved(fAfterWriteScript, aLine, getDSFuncTableP());
+ else if (strucmp(aElementName,"finishscript")==0)
+ expectScriptUnresolved(fFinishScript, aLine, getDSFuncTableP());
+ else if (strucmp(aElementName,"finalisationscript")==0)
+ expectScriptUnresolved(fFinalisationScript, aLine, getDSFuncTableP());
+ #endif
+ // - none known here
+ else
+ return inherited::localStartElement(aElementName,aAttributes,aLine);
+ // ok
+ return true;
+} // TFieldMappings::localStartElement
+
+
+
+// resolve
+void TFieldMappings::localResolve(bool aLastPass)
+{
+ // resolve each map
+ TFieldMapList::iterator pos;
+ for (pos=fFieldMapList.begin(); pos!=fFieldMapList.end(); pos++) {
+ (*pos)->Resolve(aLastPass);
+ }
+ // resolve inherited
+ inherited::localResolve(aLastPass);
+} // TFieldMappings::localResolve
+
+
+#ifdef SCRIPT_SUPPORT
+
+// access to DS func table pointer
+const TFuncTable *TFieldMappings::getDSFuncTableP(void)
+{
+ return static_cast<TCustomDSConfig *>(getParentElement())->getDSFuncTableP();
+} // TFieldMappings::getDSFuncTableP
+
+#endif
+
+/*
+ * Implementation of TCustomImplDS
+ */
+
+
+// constructor
+TCustomImplDS::TCustomImplDS(
+ TCustomDSConfig *aConfigP,
+ sysync::TSyncSession *aSessionP,
+ const char *aName,
+ uInt32 aCommonSyncCapMask
+) :
+ inherited(aConfigP,aSessionP, aName, aCommonSyncCapMask)
+{
+ fNeedFinalisation=false;
+ // save pointer to config record
+ fConfigP=aConfigP;
+ fMultiFolderDB = fConfigP->fMultiFolderDB;
+ // make a local copy of the typed agent pointer
+ fAgentP=static_cast<TCustomImplAgent *>(fSessionP);
+ // make a local copy of the typed agent config pointer
+ fAgentConfigP = dynamic_cast<TCustomAgentConfig *>(
+ aSessionP->getRootConfig()->fAgentConfigP
+ );
+ if (!fAgentConfigP) SYSYNC_THROW(TSyncException(DEBUGTEXT("TCustomImplDS finds no AgentConfig","odds7")));
+ #ifdef SCRIPT_SUPPORT
+ fScriptContextP=NULL; // no context yet
+ #endif
+ // init these keys - these might or might not be used by descendants
+ fFolderKey.erase(); // the folder key is undefined
+ fTargetKey.erase(); // the target key is undefined
+ // clear rest
+ InternalResetDataStore();
+} // TCustomImplDS::TCustomImplDS
+
+
+TCustomImplDS::~TCustomImplDS()
+{
+ InternalResetDataStore();
+} // TCustomImplDS::~TCustomImplDS
+
+
+/// @brief called while agent is still fully ok, so we must clean up such that later call of destructor does NOT access agent any more
+void TCustomImplDS::announceAgentDestruction(void)
+{
+ // reset myself
+ InternalResetDataStore();
+ // make sure we don't access the agent any more
+ // Note: as CustomImplDS always needs to be derived, we don't call
+ // engTerminateDatastore() here, but rely on descendants having done that already
+ fAgentP = NULL;
+ // call inherited
+ inherited::announceAgentDestruction();
+} // TCustomImplDS::announceAgentDestruction
+
+
+/// @brief called to reset datastore
+/// @note must be safe to be called multiple times and even after announceAgentDestruction()
+void TCustomImplDS::InternalResetDataStore(void)
+{
+ #ifdef SCRIPT_SUPPORT
+ fOptionFilterTested=false; // not tested yet
+ fOptionFilterWorksOnDBLevel=true; // assume true
+ #endif
+ // delete sync set
+ DeleteSyncSet();
+ // delete finalisation queue
+ TMultiFieldItemList::iterator pos;
+ for (pos=fFinalisationQueue.begin();pos!=fFinalisationQueue.end();pos++)
+ delete (*pos); // delete the item
+ fFinalisationQueue.clear();
+ #ifndef BASED_ON_BINFILE_CLIENT
+ fGetPhase=gph_done; // must be initialized first by startDataRead
+ fGetPhasePrepared=false;
+ // Clear map table and sync set lists
+ fMapTable.clear();
+ #else // not BASED_ON_BINFILE_CLIENT
+ fSyncSetLoaded=false;
+ #endif // BASED_ON_BINFILE_CLIENT
+ fNoSingleItemRead=false; // assume we can read single items
+ if (fAgentP) {
+ // forget script context
+ #ifdef SCRIPT_SUPPORT
+ if (fScriptContextP) {
+ delete fScriptContextP; // forget context
+ fScriptContextP=NULL;
+ }
+ #endif
+ }
+} // TCustomImplDS::InternalResetDataStore
+
+
+
+// helper for getting a field pointer (local script var or item's field)
+TItemField *TCustomImplDS::getMappedBaseFieldOrVar(TMultiFieldItem &aItem, sInt16 aFid)
+{
+ // get base field (array itself for array fields, not an element)
+ #ifdef SCRIPT_SUPPORT
+ if (fScriptContextP)
+ return fScriptContextP->getFieldOrVar(&aItem,aFid);
+ else
+ return aItem.getField(aFid);
+ #else
+ return aItem.getField(aFid);
+ #endif
+} // TCustomImplDS::getMappedBaseFieldOrVar
+
+
+
+// helper for getting a field pointer (local script var or item's field)
+TItemField *TCustomImplDS::getMappedFieldOrVar(TMultiFieldItem &aItem, sInt16 aFid, sInt16 aRepOffset, bool aExistingOnly)
+{
+ TItemField *fieldP=NULL;
+ // get field (or base field)
+ #ifdef ARRAYFIELD_SUPPORT
+ fieldP = getMappedBaseFieldOrVar(aItem,aFid);
+ if (!fieldP) return NULL; // no field
+ if (fieldP->isArray()) {
+ // use aRepOffset as array index
+ fieldP = fieldP->getArrayField(aRepOffset,aExistingOnly);
+ }
+ else
+ #endif
+ {
+ // use aRepOffset as fid offset
+ #ifdef SCRIPT_SUPPORT
+ if (aFid<0) aRepOffset=0; // locals are never offset
+ #endif
+ fieldP = getMappedBaseFieldOrVar(aItem,aFid+aRepOffset);
+ }
+ return fieldP;
+} // TCustomImplDS::getMappedFieldOrVar
+
+
+
+// inform logic of coming state change
+localstatus TCustomImplDS::dsBeforeStateChange(TLocalEngineDSState aOldState,TLocalEngineDSState aNewState)
+{
+ if (aNewState>=dssta_dataaccessdone && aOldState<dssta_dataaccessdone) {
+ // ending data access
+ #ifdef SCRIPT_SUPPORT
+ // Call the finalisation script for added or updated items
+ if (fNeedFinalisation) {
+ PDEBUGBLOCKFMT(("Finalisation","Finalizing written items","datastore=%s",getName()));
+ PDEBUGPRINTFX(DBG_DATA,(
+ "Finalizing %ld written items",
+ (long)fFinalisationQueue.size()
+ ));
+ fAgentP->fScriptContextDatastore=this;
+ while (fFinalisationQueue.size()>0) {
+ // process finalisation script
+ TMultiFieldItem *itemP = *(fFinalisationQueue.begin());
+ TScriptContext::execute(
+ fScriptContextP,fConfigP->fFieldMappings.fFinalisationScript,fConfigP->getDSFuncTableP(),fAgentP,
+ itemP,true // pass the item from the queue, is writable (mainly to allow fields to be passed as by-ref params)
+ );
+ // no longer needed
+ delete itemP;
+ // remove from queue
+ fFinalisationQueue.erase(fFinalisationQueue.begin());
+ }
+ PDEBUGENDBLOCK("Finalisation");
+ }
+ // Finally, call the finish script of the field mappings
+ fWriting=false;
+ fInserting=false;
+ fDeleting=false;
+ fAgentP->fScriptContextDatastore=this;
+ if (!TScriptContext::execute(fScriptContextP,fConfigP->fFieldMappings.fFinishScript,fConfigP->getDSFuncTableP(),fAgentP))
+ return 510; // script error -> DB error
+ #endif
+ }
+ // let inherited do its stuff as well
+ return inherited::dsBeforeStateChange(aOldState,aNewState);
+} // TCustomImplDS::dsBeforeStateChange
+
+
+
+// inform logic of happened state change
+localstatus TCustomImplDS::dsAfterStateChange(TLocalEngineDSState aOldState,TLocalEngineDSState aNewState)
+{
+ if (aNewState==dssta_completed) {
+ // completed now, call finish script
+ #ifdef SCRIPT_SUPPORT
+ // process sync end script
+ fAgentP->fScriptContextDatastore=this;
+ TScriptContext::execute(fScriptContextP,fConfigP->fSyncEndScript,fConfigP->getDSFuncTableP(),fAgentP);
+ #endif
+ }
+ // let inherited do its stuff as well
+ return inherited::dsAfterStateChange(aOldState,aNewState);
+} // TCustomImplDS::dsAfterStateChange
+
+
+#ifndef BASED_ON_BINFILE_CLIENT
+
+// mark all map entries as deleted
+bool TCustomImplDS::deleteAllMaps(void)
+{
+ string sql,s;
+ bool allok=true;
+
+ TMapContainer::iterator pos;
+ for (pos=fMapTable.begin();pos!=fMapTable.end();pos++) {
+ (*pos).deleted=true; // deleted
+ }
+ PDEBUGPRINTFX(DBG_ADMIN+DBG_EXOTIC,("deleteAllMaps: all existing map entries (%ld) now marked deleted=1",fMapTable.size()));
+ return allok;
+} // TCustomImplDS::deleteAllMaps
+
+
+// find non-deleted map entry by local ID/maptype
+TMapContainer::iterator TCustomImplDS::findMapByLocalID(const char *aLocalID,TMapEntryType aEntryType, bool aDeletedAsWell)
+{
+ TMapContainer::iterator pos;
+ if (aLocalID) {
+ for (pos=fMapTable.begin();pos!=fMapTable.end();pos++) {
+ if (
+ (*pos).localid==aLocalID && (*pos).entrytype==aEntryType
+ // && !(*pos).remoteid.empty() // Note: was ok in old versions, but now we can have map entries from resume with empty localID
+ && (aDeletedAsWell || !(*pos).deleted) // if selected, don't show deleted entries
+ ) {
+ // found
+ return pos;
+ }
+ }
+ }
+ return fMapTable.end();
+} // TCustomImplDS::findMapByLocalID
+
+
+// find map entry by remote ID
+TMapContainer::iterator TCustomImplDS::findMapByRemoteID(const char *aRemoteID)
+{
+ TMapContainer::iterator pos;
+ if (aRemoteID) {
+ for (pos=fMapTable.begin();pos!=fMapTable.end();pos++) {
+ if (
+ (*pos).remoteid==aRemoteID && (*pos).entrytype == mapentry_normal && !(*pos).deleted // only plain normal non-deleted maps (no tempid or mapforresume)
+ ) {
+ // found
+ return pos;
+ }
+ }
+ }
+ return fMapTable.end();
+} // TCustomImplDS::findMapByRemoteID
+
+
+#ifndef SYSYNC_CLIENT
+
+// - called when a item in the sync set changes its localID (due to local DB internals)
+// Datastore must make sure that eventually cached items get updated
+void TCustomImplDS::dsLocalIdHasChanged(const char *aOldID, const char *aNewID)
+{
+ // find item in map
+ TMapContainer::iterator pos=findMapByLocalID(aOldID,mapentry_normal); // only plain maps, no deleted ones
+ if (pos!=fMapTable.end()) {
+ // found, modify now
+ // NOTE: we may not modify a localid, but must delete the entry and add a new one
+ // - get remote ID
+ string remoteid = (*pos).remoteid;
+ uInt32 mapflags = (*pos).mapflags;
+ // - mark old map entry as deleted
+ (*pos).deleted=true;
+ // - create new one
+ modifyMap(mapentry_normal,aNewID,remoteid.c_str(),mapflags,false);
+ }
+ // let base class do what is needed to update the item itself
+ inherited::dsLocalIdHasChanged(aOldID, aNewID);
+} // TCustomImplDS::dsLocalIdHasChanged
+
+#endif
+
+/// @brief modify internal map table
+/// @note
+/// - if aDelete is set, map entry will be deleted
+/// - aClearFlags (default=all) will be cleared when updating only
+/// - aMapFlags will be set when updating
+/// - if aRemoteID is NULL when updating an existing (not marked deleted) item, existing remoteID will NOT be modified
+/// - otherwise, map item will be added or updated.
+/// - routine makes sure that there is no more than one map for each localID/entrytype and
+/// each remoteID.
+/// - remoteID can also be a temporary localID used by server to send to clients with too small MaxGUIDSize (mapflag_tempidmap set)
+/// - routine will re-activate deleted entries to avoid unnecessary delete/insert
+void TCustomImplDS::modifyMap(TMapEntryType aEntryType, const char *aLocalID, const char *aRemoteID, uInt32 aMapFlags, bool aDelete, uInt32 aClearFlags)
+{
+ TMapContainer::iterator pos=fMapTable.end();
+
+ DEBUGPRINTFX(DBG_ADMIN+DBG_EXOTIC,(
+ "ModifyMap called: aEntryType=%s, aLocalID='%s, aRemoteid='%s', aMapflags=0x%lX, aDelete=%d",
+ MapEntryTypeNames[aEntryType],
+ aLocalID && *aLocalID ? aLocalID : "<none>",
+ aRemoteID ? (*aRemoteID ? aRemoteID : "<set none>") : "<do not change>",
+ aMapFlags,
+ (int)aDelete
+ ));
+ // - if there is a localID, search map entry (even if it is deleted)
+ if (aLocalID && *aLocalID!=0) {
+ for (pos=fMapTable.begin();pos!=fMapTable.end();pos++) {
+ if (
+ // localID and entrytype matches
+ (*pos).localid==aLocalID && (*pos).entrytype==aEntryType
+ ) {
+ PDEBUGPRINTFX(DBG_ADMIN+DBG_EXOTIC,(
+ "- found entry by entrytype/localID='%s' - remoteid='%s', mapflags=0x%lX, changed=%d, deleted=%d, added=%d, markforresume=%d, savedmark=%d",
+ aLocalID,
+ (*pos).remoteid.c_str(),
+ (*pos).mapflags,
+ (int)(*pos).changed,
+ (int)(*pos).deleted,
+ (int)(*pos).added,
+ (int)(*pos).markforresume,
+ (int)(*pos).savedmark
+ ));
+ break;
+ }
+ }
+ }
+ else aLocalID=NULL;
+ // decide what to do
+ if (aDelete) {
+ // delete
+ if (!aLocalID) {
+ if (!aRemoteID) return; // nop
+ // we need to search by remoteID first
+ pos=findMapByRemoteID(aRemoteID);
+ #ifdef SYDEBUG
+ if (pos!=fMapTable.end()) {
+ DEBUGPRINTFX(DBG_ADMIN+DBG_EXOTIC,(
+ "- found entry by remoteID='%s' - localid='%s', mapflags=0x%lX, changed=%d, deleted=%d, added=%d, markforresume=%d, savedmark=%d",
+ aRemoteID,
+ (*pos).localid.c_str(),
+ (*pos).mapflags,
+ (int)(*pos).changed,
+ (int)(*pos).deleted,
+ (int)(*pos).added,
+ (int)(*pos).markforresume,
+ (int)(*pos).savedmark
+ ));
+ }
+ #endif
+ }
+ if (pos==fMapTable.end()) return; // not found, nop
+ // mark deleted
+ if ((*pos).added) {
+ // has been added in this session and not yet saved
+ // so it does not yet exist in the DB at all
+ // - simply forget entry
+ fMapTable.erase(pos);
+ // - done, ok
+ return;
+ }
+ // entry has already been saved to DB before - only mark deleted
+ (*pos).deleted=true;
+ } // delete
+ else {
+ // update or add
+ if (pos==fMapTable.end()) {
+ PDEBUGPRINTFX(DBG_ADMIN+DBG_EXOTIC,(
+ "- found no matching entry for localID '%s' - creating new one, added=true",
+ aLocalID
+ ));
+ // add, because there is not yet an item with that localid/entrytype
+ TMapEntry entry;
+ entry.entrytype=aEntryType;
+ entry.added=true;
+ entry.changed=true;
+ entry.localid=aLocalID;
+ AssignString(entry.remoteid,aRemoteID); // if NULL, remoteID will be empty
+ entry.savedmark=false;
+ entry.markforresume=false;
+ entry.mapflags=0; // none set by default
+ fMapTable.push_front(entry);
+ pos=fMapTable.begin(); // first entry is new entry
+ }
+ else {
+ PDEBUGPRINTFX(DBG_ADMIN+DBG_EXOTIC,(
+ "- matching entry found - re-activating deleted and/or updating contents if needed"
+ ));
+ /* %%% NO!!!! plain wrong - because this causes added and later changed entries never be added to the DB!
+ // %%% No room for paranoia here - makes things worse!
+ // just to make sure - added should not be set here
+ // (if we delete an added one before, it will be completely deleted from the list again)
+ (*pos).added=false;
+ */
+ // check if contents change, update if so
+ if (
+ (((*pos).mapflags & ~mapflag_useforresume) != aMapFlags) || // flags different (useForResume not tested!)
+ (aRemoteID && (*pos).remoteid!=aRemoteID) // remoteID different
+ ) {
+ // new RemoteID (but not NULL = keep existing) or different mapflags were passed -> this is a real change
+ if (aRemoteID)
+ (*pos).remoteid=aRemoteID;
+ (*pos).changed=true; // really changed compared to what is already in DB
+ }
+ }
+ // now item exists, set details
+ (*pos).deleted=false; // in case we had it deleted before, but not yet saved
+ // clear those flags shown in aClearFlags (by default: all) and set those in aMapFlags
+ (*pos).mapflags = (*pos).mapflags & ~aClearFlags | aMapFlags;
+ // now remove all other items with same remoteID (except if we have no or empty remoteID)
+ if (aEntryType==mapentry_normal && aRemoteID && *aRemoteID) {
+ // %%% note: this is strictly necessary only for add, but cleans up for update
+ TMapContainer::iterator pos2;
+ for (pos2=fMapTable.begin();pos2!=fMapTable.end();pos2++) {
+ if (pos2!=pos && (*pos2).remoteid==aRemoteID && (*pos2).entrytype==aEntryType) {
+ // found another one with same remoteID/entrytype
+ PDEBUGPRINTFX(DBG_ADMIN+DBG_EXOTIC,(
+ "- cleanup: removing same remoteID from other entry with localid='%s', mapflags=0x%lX, changed=%d, deleted=%d, added=%d, markforresume=%d, savedmark=%d",
+ (*pos2).localid.c_str(),
+ (*pos2).mapflags,
+ (int)(*pos2).changed,
+ (int)(*pos2).deleted,
+ (int)(*pos2).added,
+ (int)(*pos2).markforresume,
+ (int)(*pos2).savedmark
+ ));
+ // this remoteID is invalid for sure as we just have assigned it to another item - remove it
+ (*pos2).remoteid.erase();
+ (*pos2).changed=true; // make sure it gets saved
+ }
+ }
+ }
+ } // modify or add
+} // TCustomImplDS::modifyMap
+
+
+#endif // not BASED_ON_BINFILE_CLIENT
+
+
+// delete syncset
+// - if aContentsOnly, only item data will be deleted, but localID/containerid will
+// be retained
+void TCustomImplDS::DeleteSyncSet(bool aContentsOnly)
+{
+ TSyncSetList::iterator pos;
+ for (pos=fSyncSetList.begin();pos!=fSyncSetList.end();pos++) {
+ // delete contained item, if any
+ if ((*pos)->itemP) {
+ delete ((*pos)->itemP);
+ (*pos)->itemP=NULL;
+ }
+ if (!aContentsOnly)
+ delete (*pos); // delete syncsetitem itself
+ }
+ if (!aContentsOnly) fSyncSetList.clear();
+} // TCustomImplDS::DeleteSyncSet
+
+
+// - get container ID for specified localid
+bool TCustomImplDS::getContainerID(const char *aLocalID, string &aContainerID)
+{
+ TSyncSetList::iterator pos = findInSyncSet(aLocalID);
+ if (pos!=fSyncSetList.end()) {
+ aContainerID = (*pos)->containerid;
+ return true; // found
+ }
+ return false; // not found
+} // TCustomImplDS::getContainerID
+
+
+// find entry in sync set by localid
+TSyncSetList::iterator TCustomImplDS::findInSyncSet(const char *aLocalID)
+{
+ TSyncSetList::iterator pos;
+ for (pos=fSyncSetList.begin();pos!=fSyncSetList.end();pos++) {
+ if ((*pos)->localid==aLocalID) {
+ // found
+ return pos;
+ }
+ }
+ return fSyncSetList.end();
+} // TCustomImplDS::findInSyncSet
+
+
+// called when message processing
+void TCustomImplDS::dsEndOfMessage(void)
+{
+ // let ancestor do things
+ inherited::dsEndOfMessage();
+} // TCustomImplDS::dsEndOfMessage
+
+
+
+// Simple DB access interface methods
+
+
+// - returns true if database implementation can only update all fields of a record at once
+bool TCustomImplDS::dsReplaceWritesAllDBFields(void)
+{
+ // return true if we should read record from DB before replacing.
+ return fConfigP->fUpdateAllFields;
+} // TCustomImplDS::dsReplaceWritesAllDBFields
+
+
+#ifdef OBJECT_FILTERING
+
+// - returns true if DB implementation can also apply special filters like CGI-options
+// /dr(x,y) etc. during fetching
+bool TCustomImplDS::dsOptionFilterFetchesFromDB(void)
+{
+ #ifndef SYSYNC_TARGET_OPTIONS
+ // there are no ranges to filter at all
+ return true; // we can "filter" this (nothing)
+ #else
+ // no filter range set: yes, we can filter
+ if (fDateRangeStart==0 && fDateRangeEnd==0) return true; // we can "filter" this
+ // see if a script provides a solution
+ #ifdef SCRIPT_SUPPORT
+ if (!fOptionFilterTested) {
+ fOptionFilterTested=true;
+ // call script to take measures such that database implementation can
+ // filter, returns true if filtering is entirely possible
+ // (e.g. for ODBC, script should generate appropriate WHERE clause and set it with SETSQLFILTER())
+ fAgentP->fScriptContextDatastore=this;
+ fOptionFilterWorksOnDBLevel = TScriptContext::executeTest(
+ false, // assume we cannot filter if no script or script returns nothing
+ fScriptContextP, // context
+ fConfigP->fFieldMappings.fOptionFilterScript, // the script
+ fConfigP->getDSFuncTableP(),fAgentP // funcdefs/context
+ );
+ }
+ if (fOptionFilterWorksOnDBLevel) return true;
+ #endif
+ // we can't filter, let anchestor try
+ return inherited::dsOptionFilterFetchesFromDB();
+ #endif
+} // TCustomImplDS::dsOptionFilterFetchesFromDB
+
+
+#endif
+
+
+
+/// sync login (into this database)
+/// @note might be called several times (auth retries at beginning of session)
+/// @note must update the following saved AND current state variables
+/// - in TLocalEngineDS: fLastRemoteAnchor, fLastLocalAnchor, fResumeAlertCode, fFirstTimeSync
+/// - for client: fPendingAddMaps
+/// - for server: fTempGUIDMap
+/// - in TStdLogicDS: fPreviousSyncTime, fCurrentSyncTime
+/// - in TCustomImplDS: fCurrentSyncCmpRef, fCurrentSyncIdentifier, fPreviousToRemoteSyncCmpRef,
+/// fPreviousToRemoteSyncIdentifier, fPreviousSuspendCmpRef, fPreviousSuspendIdentifier
+/// - in derived classes: whatever else belongs to dsSavedAdmin and dsCurrentAdmin state
+localstatus TCustomImplDS::implMakeAdminReady(
+ const char *aDeviceID, // remote device URI (device ID)
+ const char *aDatabaseID, // database ID
+ const char *aRemoteDBID // database ID of remote device
+)
+{
+ localstatus sta=LOCERR_OK; // assume ok
+ string sql;
+
+
+ // init state variables
+ // dsSavedAdmin
+ // - of TLocalEngineDS:
+ fFirstTimeSync=true; // assume first time sync
+ fLastRemoteAnchor.erase(); // remote anchor not known yet
+ fResumeAlertCode=0; // no session to resume
+ // - of TStdLogicDS:
+ fPreviousSyncTime=0;
+ // - of TCustomImplDS:
+ fPreviousToRemoteSyncCmpRef=0; // no previous session
+ fPreviousToRemoteSyncIdentifier.erase();
+ fPreviousSuspendCmpRef=0; // no previous suspend
+ fPreviousSuspendIdentifier.erase();
+
+ // dsCurrentAdmin
+ // - of TLocalEngineDS:
+ // - of TStdLogicDS:
+ fCurrentSyncTime=0;
+ // - of TCustomImplDS:
+ fCurrentSyncCmpRef=0;
+ fCurrentSyncIdentifier.erase();
+
+ #ifndef BASED_ON_BINFILE_CLIENT
+ fMapTable.clear(); // map is empty to begin with
+ #endif
+ // now get admin data
+ SYSYNC_TRY {
+ #ifdef SCRIPT_SUPPORT
+ // rebuild context for all scripts (if not already resolved)
+ // Note: unlike while reading config, here all maps and scripts are already available
+ // so this will build the entire context at once.
+ if (!fScriptContextP) {
+ // Rebuild order MUST be same as resolving order (see ResolveDSScripts())
+ // - scripts in eventual derivates
+ apiRebuildScriptContexts();
+ // - adminready and end scripts outside the fieldmappings
+ TScriptContext::rebuildContext(fSessionP->getSyncAppBase(),fConfigP->fAdminReadyScript,fScriptContextP,fSessionP);
+ TScriptContext::rebuildContext(fSessionP->getSyncAppBase(),fConfigP->fSyncEndScript,fScriptContextP,fSessionP);
+ // - scripts within fieldmappings
+ TScriptContext::rebuildContext(fSessionP->getSyncAppBase(),fConfigP->fFieldMappings.fOptionFilterScript,fScriptContextP,fSessionP);
+ TScriptContext::rebuildContext(fSessionP->getSyncAppBase(),fConfigP->fFieldMappings.fInitScript,fScriptContextP,fSessionP);
+ TScriptContext::rebuildContext(fSessionP->getSyncAppBase(),fConfigP->fFieldMappings.fAfterReadScript,fScriptContextP,fSessionP);
+ TScriptContext::rebuildContext(fSessionP->getSyncAppBase(),fConfigP->fFieldMappings.fBeforeWriteScript,fScriptContextP,fSessionP);
+ TScriptContext::rebuildContext(fSessionP->getSyncAppBase(),fConfigP->fFieldMappings.fAfterWriteScript,fScriptContextP,fSessionP);
+ TScriptContext::rebuildContext(fSessionP->getSyncAppBase(),fConfigP->fFieldMappings.fFinishScript,fScriptContextP,fSessionP);
+ TScriptContext::rebuildContext(fSessionP->getSyncAppBase(),fConfigP->fFieldMappings.fFinalisationScript,fScriptContextP,fSessionP);
+ #ifdef ARRAYDBTABLES_SUPPORT
+ // - rebuild array script vars
+ TFieldMapList::iterator pos;
+ for (pos=fConfigP->fFieldMappings.fFieldMapList.begin(); pos!=fConfigP->fFieldMappings.fFieldMapList.end(); pos++) {
+ if ((*pos)->isArray()) {
+ TFieldMapArrayItem *fmaiP = dynamic_cast<TFieldMapArrayItem *>(*pos);
+ if (fmaiP) {
+ // rebuild
+ // %%% note, this is not capable of nested arrays yet
+ TScriptContext::rebuildContext(fSessionP->getSyncAppBase(),fmaiP->fInitScript,fScriptContextP,fSessionP);
+ TScriptContext::rebuildContext(fSessionP->getSyncAppBase(),fmaiP->fAfterReadScript,fScriptContextP,fSessionP);
+ TScriptContext::rebuildContext(fSessionP->getSyncAppBase(),fmaiP->fBeforeWriteScript,fScriptContextP,fSessionP);
+ TScriptContext::rebuildContext(fSessionP->getSyncAppBase(),fmaiP->fAfterWriteScript,fScriptContextP,fSessionP);
+ TScriptContext::rebuildContext(fSessionP->getSyncAppBase(),fmaiP->fFinishScript,fScriptContextP,fSessionP);
+ }
+ }
+ }
+ #endif
+ // now instantiate variables
+ TScriptContext::buildVars(fScriptContextP);
+ }
+ #endif
+ #ifdef BASED_ON_BINFILE_CLIENT
+ // binfile's implLoadAdminData will do the job
+ sta = inherited::implMakeAdminReady(aDeviceID, aDatabaseID, aRemoteDBID);
+
+ #else
+ // Load admin data from TXXXApiDS (ODBC, text or derived class' special implementation)
+ sta = apiLoadAdminData(
+ aDeviceID, // remote device URI (device ID)
+ aDatabaseID, // database ID
+ aRemoteDBID // database ID of remote device
+ );
+ #endif
+ // set error if one occurred during load
+ if (sta==LOCERR_OK) {
+ // extra check: if we get empty remote anchor, this is a first-time sync even if DB claims the opposite
+ if (fLastRemoteAnchor.empty())
+ fFirstTimeSync=true;
+ // create identifier if we don't have it stored in the DB
+ if (!fConfigP->fStoreSyncIdentifiers) {
+ // we generate the identifiers as ISO8601 UTC string from the timestamp
+ TimestampToISO8601Str(fPreviousToRemoteSyncIdentifier,fPreviousToRemoteSyncCmpRef,TCTX_UTC,false,false);
+ TimestampToISO8601Str(fPreviousSuspendIdentifier,fPreviousSuspendCmpRef,TCTX_UTC,false,false);
+ }
+ // determine time of this sync
+ fCurrentSyncTime=
+ fAgentP->getDatabaseNowAs(TCTX_UTC);
+ // by default, these two are equal
+ // (but if DB cannot write items with timestamp exactly==fCurrentSyncTime, fCurrentSyncCmpRef might be a later time, like end-of-session)
+ fCurrentSyncCmpRef = fCurrentSyncTime;
+ // admin ready now, call script that may access DB to fetch some extra options
+ #ifdef SCRIPT_SUPPORT
+ fAgentP->fScriptContextDatastore=this;
+ if (!TScriptContext::executeTest(true,fScriptContextP,fConfigP->fAdminReadyScript,fConfigP->getDSFuncTableP(),fAgentP))
+ sta=510; // script returns false or fails -> DB error
+ #endif
+ } // if apiLoadAdminData successful
+ }
+ SYSYNC_CATCH(exception &e)
+ PDEBUGPRINTFX(DBG_ERROR,("implMakeAdminReady exception: %s",e.what()));
+ sta=510;
+ SYSYNC_ENDCATCH
+ // done
+ return sta;
+} // TCustomImplDS::implMakeAdminReady
+
+
+
+// start data read
+localstatus TCustomImplDS::implStartDataRead()
+{
+ localstatus sta = LOCERR_OK;
+
+ // get field map list
+ TFieldMapList &fml = fConfigP->fFieldMappings.fFieldMapList;
+
+ // check if we have fileds that must be finalized or array fields at all (to avoid unneeded operations if not)
+ TFieldMapList::iterator pos;
+ #ifdef ARRAYDBTABLES_SUPPORT
+ fHasArrayFields=false; // until we KNOW otherwise
+ #endif
+ fNeedFinalisation=false; // until we KNOW otherwise
+ for (pos=fml.begin(); pos!=fml.end(); pos++) {
+ // - check finalisation
+ if ((*pos)->needs_finalisation)
+ fNeedFinalisation=true;
+ // - check array mappings
+ #ifdef ARRAYDBTABLES_SUPPORT
+ if ((*pos)->isArray())
+ fHasArrayFields=true;
+ #endif
+ }
+ #ifdef BASED_ON_BINFILE_CLIENT
+ // further preparation is in binfileds
+ sta = inherited::implStartDataRead();
+ if (sta==LOCERR_OK) {
+ // now make sure the syncset is loaded
+ if (!makeSyncSetLoaded(
+ fSlowSync
+ #ifdef OBJECT_FILTERING
+ || fFilteringNeededForAll
+ #endif
+ ))
+ sta = 510; // error
+ }
+ #else
+ // kill all map entries if slow sync (but not if resuming!!)
+ if (fSlowSync && !isResuming()) {
+ // mark all map entries as deleted
+ deleteAllMaps();
+ }
+ // - count entire read as database read
+ TP_DEFIDX(li);
+ TP_SWITCH(li,fSessionP->fTPInfo,TP_database);
+ PDEBUGBLOCKFMTCOLL(("ReadSyncSet","Reading Sync Set from Database","datastore=%s",getName()));
+ SYSYNC_TRY {
+ // read sync set (maybe from derived non-odbc data source)
+ // - in slow sync, we need all items (so allow ReadSyncSet to read them all here)
+ // - if all items must be filtered, we also need all data
+ // Note: ReadSyncSet will decide if it actually needs to load the syncset or not (depends on refresh, slowsync and needs of apiZapSyncSet())
+ sta = apiReadSyncSet(
+ fSlowSync
+ #ifdef OBJECT_FILTERING
+ || fFilteringNeededForAll
+ #endif
+ );
+ // determine how GetItem will start
+ fGetPhase = fSlowSync ? gph_added_changed : gph_deleted; // just report added (not-in-map, map is cleared already) for slowsync
+ // phase not yet prepared
+ fGetPhasePrepared = false;
+ // end of DB read
+ PDEBUGENDBLOCK("ReadSyncSet");
+ TP_START(fSessionP->fTPInfo,li);
+ }
+ SYSYNC_CATCH(exception &e)
+ PDEBUGPRINTFX(DBG_ERROR,("StartDataRead exception: %s",e.what()));
+ sta=510;
+ // end of DB read
+ PDEBUGENDBLOCK("ReadSyncSet");
+ TP_START(fSessionP->fTPInfo,li);
+ SYSYNC_ENDCATCH
+ #endif // not BASED_ON_BINFILE_CLIENT
+ return sta;
+} // TCustomImplDS::implStartDataRead
+
+
+
+// Queue the data needed for finalisation (usually - relational link updates)
+// as a item copy with only finalisation-required fields
+void TCustomImplDS::queueForFinalisation(TMultiFieldItem *aItemP)
+{
+ sInt16 fid;
+ // create a same-typed copy of the original item (initially empty)
+ TMultiFieldItem *itemP = new TMultiFieldItem(aItemP->getItemType(),aItemP->getTargetItemType());
+ // copy localID and syncop
+ itemP->setLocalID(aItemP->getLocalID());
+ itemP->setSyncOp(aItemP->getSyncOp());
+ // copy fields that are marked for finalisation
+ TFieldMapList &fml = fConfigP->fFieldMappings.fFieldMapList;
+ TFieldMapList::iterator pos;
+ for (pos=fml.begin(); pos!=fml.end(); pos++) {
+ TFieldMapItem *fmiP = *pos;
+ #ifdef ARRAYDBTABLES_SUPPORT
+ if (fmiP->isArray()) {
+ TFieldMapList::iterator pos2;
+ TFieldMapList &afml = static_cast<TFieldMapArrayItem *>(fmiP)->fArrayFieldMapList;
+ for (pos2=afml.begin(); pos2!=afml.end(); pos2++) {
+ TFieldMapItem *fmi2P = *pos2;
+ fid = fmi2P->fid;
+ if (fmi2P->needs_finalisation && fid>=0) {
+ // this mapping indicates need for finalisation and references a fieldlist field, copy referenced field
+ *(itemP->getField(fid))=*(aItemP->getField(fid));
+ }
+ }
+ }
+ else
+ #endif // ARRAYDBTABLES_SUPPORT
+ {
+ fid = fmiP->fid;
+ if (fmiP->needs_finalisation && fid>=0) {
+ // this mapping indicates need for finalisation and references a fieldlist field, copy referenced field
+ *(itemP->getField(fid))=*(aItemP->getField(fid));
+ }
+ }
+ }
+ // put the finalisation item into the queue
+ fFinalisationQueue.push_back(itemP);
+} // TCustomImplDS::queueForFinalisation
+
+
+
+#ifndef BASED_ON_BINFILE_CLIENT
+
+/// @brief called to have all non-yet-generated sync commands as "to-be-resumed"
+void TCustomImplDS::implMarkOnlyUngeneratedForResume(void)
+{
+ // Note: all "markforresume" flags (but NOT the actual mapflag_useforresume!) are cleared
+ // after loading or saving admin, so we can start adding resume marks BEFORE
+ // implMarkOnlyUngeneratedForResume is called (needed to re-add items that got
+ // an unsuccessful status from remote that suggests re-trying in next resume, such as 514)
+ // add all not-yet-got items
+ TMapContainer::iterator pos;
+ TGetPhases getPhase = fGetPhase; // start at current get phase
+ bool getPrepared = fGetPhasePrepared;
+ // now flag all deletes that need resuming
+ if (getPhase==gph_deleted) {
+ // if we are still in "deleted" phase, add these first
+ if (!getPrepared)
+ pos=fMapTable.begin();
+ else
+ pos=fDeleteMapPos;
+ // now mark pending deletes
+ while (pos!=fMapTable.end()) {
+ // check only undeleted map entries
+ // Note: non-normal maps are always in deleted state in fMapTable, so these will be skipped as well
+ if ((*pos).deleted) continue;
+ // check if deleted
+ if (findInSyncSet((*pos).localid.c_str())==fSyncSetList.end()) {
+ // mark this as pending for resume
+ (*pos).markforresume=true;
+ }
+ // next
+ ++pos;
+ }
+ getPhase=gph_added_changed;
+ getPrepared=false;
+ }
+ // now flag all changes and adds that need resuming
+ // if we are already gph_done, no items need to be flagged here
+ if (getPhase==gph_added_changed) {
+ // if we are in the add/change phase, add not-yet handled adds/changes
+ TSyncSetList::iterator syncsetpos;
+ if (!getPrepared)
+ syncsetpos=fSyncSetList.begin();
+ else
+ syncsetpos=fSyncSetPos;
+ // now mark pending changes and adds
+ while (syncsetpos!=fSyncSetList.end()) {
+ // check if we need to mark it
+ bool needMark=false;
+ pos=findMapByLocalID((*syncsetpos)->localid.c_str(),mapentry_normal,true); // find deleted ones as well
+ if (fSlowSync) {
+ #ifdef SYSYNC_CLIENT
+ // for client, there are no reference-only: mark all leftovers in a slow sync
+ needMark=true;
+ #else
+ // for server, make sure not to mark reference-only.
+ if (!isResuming() || pos==fMapTable.end()) {
+ // if not resuming, or we have no map for this one at all - we'll need it again for resume
+ needMark=true;
+ }
+ else {
+ // for slowsync resume which have already a map:
+ // - items that are not marked for resume, but already have a remoteID mapped
+ // are reference-only and must NOT be marked
+ if (((*pos).mapflags & mapflag_useforresume) || (*pos).remoteid.empty())
+ needMark=true;
+ }
+ #endif
+ }
+ else if (!isRefreshOnly()) {
+ // not slow sync, and not refresh from remote only - mark those that are actually are involved
+ if (pos!=fMapTable.end()) {
+ // known item, needs a mark only if record is modified (and updates reported at all)
+ needMark=((*syncsetpos)->isModified) && fReportUpdates;
+ }
+ else {
+ // adds need marking, anyway
+ needMark=true;
+ }
+ }
+ // now apply mark if needed
+ if (needMark) {
+ if (pos==fMapTable.end()) {
+ // no map entry for this item yet (this means that this is a new detected add
+ // - add pendingAddConfirm item now, and mark it for resume
+ TMapEntry entry;
+ entry.entrytype=mapentry_normal;
+ entry.localid=(*syncsetpos)->localid.c_str();
+ entry.remoteid.erase();
+ entry.mapflags=mapflag_pendingAddConfirm;
+ entry.added=true;
+ entry.changed=true;
+ entry.deleted=false;
+ entry.markforresume=true;
+ entry.savedmark=false;
+ fMapTable.push_back(entry);
+ }
+ else {
+ // add flag to existing map item
+ if ((*pos).deleted) {
+ // undelete (re-use existing, but currently invalid entry)
+ (*pos).remoteid.erase();
+ (*pos).changed=true;
+ (*pos).deleted=false;
+ (*pos).mapflags=0;
+ }
+ (*pos).markforresume=true;
+ }
+ }
+ // next
+ ++syncsetpos;
+ }
+ }
+} // TCustomImplDS::implMarkOnlyUngeneratedForResume
+
+
+// called to confirm a sync operation's completion (status from remote received)
+// @note aSyncOp passed not necessarily reflects what was sent to remote, but what actually happened
+void TCustomImplDS::dsConfirmItemOp(TSyncOperation aSyncOp, cAppCharP aLocalID, cAppCharP aRemoteID, bool aSuccess, localstatus aErrorStatus)
+{
+ if (aSyncOp==sop_delete || aSyncOp==sop_archive_delete) {
+ // a confirmed delete causes the entire map entry to be removed (item no longer exists (or is visible) locally or remotely)
+ if (aSuccess) {
+ PDEBUGPRINTFX(DBG_ADMIN+DBG_EXOTIC,("successful status for delete received -> delete map entry now"));
+ modifyMap(mapentry_normal,aLocalID,aRemoteID,0,true);
+ }
+ }
+ else {
+ TMapContainer::iterator pos;
+ #ifdef SYSYNC_CLIENT
+ // for client, always find by localid
+ pos=findMapByLocalID(aLocalID,mapentry_normal);
+ #else
+ // for server, only add can be found by localid
+ if (aSyncOp==sop_add)
+ pos=findMapByLocalID(aLocalID,mapentry_normal);
+ else
+ pos=findMapByRemoteID(aRemoteID);
+ #endif
+ if (pos!=fMapTable.end()) {
+ // Anyway, clear the status pending flag
+ // Note: we do not set the "changed" bit here because we don't really need to make this persistent between sessions
+ (*pos).mapflags &= ~mapflag_pendingStatus;
+ #ifdef SYSYNC_CLIENT
+ if (aSuccess) {
+ // Note: we do not check for sop here - any successfully statused sop will clear the pending add status
+ // (e.g. in slow sync, items reported as add to engine are actually sent as replaces, but still
+ // seeing a ok status means that they are not any longer pending as adds)
+ // Note: the same functionality formerly was in TStdLogicDS::startDataWrite() - making sure that a
+ // add sent to the server is not repeated. As every item reported by implGetItem now already
+ // has a map entry (adds get one with mapflag_pendingAddConfirm set), we just need to clear
+ // the flag here now that we know the add has reached the server.
+ // Note: For the server, we can clear the mapflag_pendingAddConfirm not before we have received a <Map> item for it!
+ PDEBUGPRINTFX(DBG_ADMIN+DBG_EXOTIC,("successful status for non-delete received -> clear mapflag_pendingAddConfirm"));
+ (*pos).mapflags &= ~mapflag_pendingAddConfirm;
+ (*pos).changed = true; // this MUST be made persistent!
+ }
+ #endif
+ }
+ else {
+ PDEBUGPRINTFX(DBG_ERROR+DBG_EXOTIC,("dsConfirmItemOp - INTERNAL ERROR: no map entry exists for item"));
+ }
+ }
+ // let inherited know as well
+ inherited::dsConfirmItemOp(aSyncOp, aLocalID, aRemoteID, aSuccess, aErrorStatus);
+} // TCustomImplDS::confirmItemOp
+
+
+// called to mark an already sent item as "to-be-resent", e.g. due to temporary
+// error status conditions, by localID or remoteID (latter only in server case).
+void TCustomImplDS::implMarkItemForResend(cAppCharP aLocalID, cAppCharP aRemoteID)
+{
+ // Note: this is only relevant for replaces and some adds:
+ // - some adds will not have a map entry yet
+ // - deletes will not have their map entry deleted until they are confirmed
+ // But replaces would not get re-sent as they would not get detected
+ // modified any more once a session has completed.
+ TMapContainer::iterator pos;
+ if (aLocalID && *aLocalID)
+ pos=findMapByLocalID(aLocalID,mapentry_normal,false); // only undeleted ones
+ else if (aRemoteID && *aRemoteID)
+ pos=findMapByRemoteID(aRemoteID);
+ else
+ return; // neither local nor remote ID specified -> nop
+ if (pos==fMapTable.end()) {
+ PDEBUGPRINTFX(DBG_ADMIN+DBG_EXOTIC,("implMarkItemForResend: no map entry found -> item does not exist or is an add which will be sent anyway"));
+ return; // no item can be searched or created
+ }
+ // set resend flag now (if not already set)
+ if (!((*pos).mapflags & mapflag_resend)) {
+ (*pos).mapflags |= mapflag_resend;
+ (*pos).changed=true;
+ }
+ // and make sure it gets resent in case of a suspend as well (mark for resume)
+ // This is needed for suspends where unprocessed items gets statused with 514 BEFORE
+ // the server knows that this is going to be a suspend.
+ (*pos).markforresume=true;
+ PDEBUGPRINTFX(DBG_ADMIN+DBG_EXOTIC+DBG_HOT,(
+ "localID='%s' marked for resending by setting mapflag_resend (AND mark for eventual resume!), flags now=0x%lX",
+ (*pos).localid.c_str(),
+ (*pos).mapflags
+ ));
+} // TCustomImplDS::implMarkItemForResend
+
+
+// called to mark an already generated (but unsent or sent but not yet statused) item
+// as "to-be-resumed", by localID or remoteID (latter only in server case).
+void TCustomImplDS::implMarkItemForResume(cAppCharP aLocalID, cAppCharP aRemoteID, bool aUnSent)
+{
+ TMapContainer::iterator pos;
+ if (aLocalID && *aLocalID)
+ pos=findMapByLocalID(aLocalID,mapentry_normal,true); // also find deleted ones
+ else if (aRemoteID && *aRemoteID)
+ pos=findMapByRemoteID(aRemoteID);
+ else
+ return; // no item can be searched or created
+ if (pos!=fMapTable.end()) {
+ // we have an entry for this item, mark it for resume
+ if ((*pos).deleted) {
+ // undelete (re-use existing, but currently invalid entry)
+ (*pos).remoteid.erase();
+ (*pos).changed=true;
+ (*pos).deleted=false;
+ (*pos).mapflags=0;
+ }
+ // for unsent ones, the status is not pending any more
+ else if (aUnSent && ((*pos).mapflags & (mapflag_pendingStatus+mapflag_pendingDeleteStatus))) {
+ // reset the status pending flags
+ (*pos).changed=true;
+ (*pos).mapflags &= ~(mapflag_pendingStatus+mapflag_pendingDeleteStatus);
+ }
+ // For Server: those that have mapflag_pendingAddConfirm (adds) may be marked for
+ // resume ONLY if we can rely on early maps or if they are completely unsent.
+ // Sent adds will just keep their mapflag_pendingAddConfirm until they receive their map
+ // For Client: all items will be marked for resume
+ #ifndef SYSYNC_CLIENT
+ if (
+ ((*pos).mapflags & mapflag_pendingAddConfirm) && // is an add...
+ !aUnSent && // ...and already sent out
+ !fSessionP->getSessionConfig()->fRelyOnEarlyMaps // and we can't rely on the client sending the maps before
+ ) {
+ // for server: already sent adds may not be repeated unless we can rely on early maps
+ PDEBUGPRINTFX(DBG_ADMIN+DBG_EXOTIC,(
+ "implMarkItemForResume: localID='%s', has mapFlags=0x%lX and was probably executed at remote -> NOT marked for resume",
+ (*pos).localid.c_str(),
+ (*pos).mapflags
+ ));
+ (*pos).markforresume=false;
+ }
+ else
+ #endif
+ {
+ // for client: everything may be repeated and therefore marked for resume
+ // for server: unsent adds will also be marked, or all if we can rely on early maps (which is the default)
+ PDEBUGPRINTFX(DBG_ADMIN+DBG_EXOTIC,(
+ "implMarkItemForResume: localID='%s', has mapFlags=0x%lX and was %s executed at remote%s -> mark for resume",
+ (*pos).localid.c_str(),
+ (*pos).mapflags,
+ aUnSent ? "NOT" : "probably",
+ fSessionP->getSessionConfig()->fRelyOnEarlyMaps ? " (relying on early maps)" : ""
+ ));
+ (*pos).markforresume=true;
+ }
+ }
+ else if (aLocalID && *aLocalID) {
+ PDEBUGPRINTFX(DBG_ADMIN+DBG_EXOTIC,(
+ "implMarkItemForResume: localID='%s', was not yet in map and was %sexecuted at remote -> created map and marked for resume",
+ aLocalID,
+ aUnSent ? "NOT " : "probably"
+ ));
+ // we have no entry for this item, make one (only if this is not a remoteID-only item,
+ // but this should not occur here anyway - items without localID can only be replaces
+ // from server to client, in which case we have a map entry anyway)
+ TMapEntry entry;
+ entry.entrytype=mapentry_normal;
+ entry.localid=aLocalID;
+ entry.remoteid.erase();
+ entry.mapflags=0;
+ entry.added=true;
+ entry.changed=true;
+ entry.deleted=false;
+ entry.markforresume=true;
+ entry.savedmark=false;
+ fMapTable.push_back(entry);
+ }
+} // TCustomImplDS::implMarkItemForResume
+
+
+
+// Get next item from database
+localstatus TCustomImplDS::implGetItem(
+ bool &aEof,
+ bool &aChanged, // if set on entry, only changed ones will be reported, otherwise all will be returned and aChanged contains flag if entry has changed or not
+ TSyncItem* &aSyncItemP
+)
+{
+ localstatus sta = LOCERR_OK;
+
+ bool reportChangedOnly = aChanged; // save initial state, as we might repeat...
+ bool rep=true; // to start-up lower part
+ TSyncOperation sop=sop_none;
+ aEof=true; // assume we have nothing to report
+ string remid;
+ TMultiFieldItem *myitemP=NULL;
+
+ // short-cut if refreshing only and not slowsync resuming (then we need the items for comparison)
+ if (isRefreshOnly() && !(isResuming() && isSlowSync()))
+ return sta; // aEof is set, return nothing
+
+ TP_DEFIDX(li);
+ TP_SWITCH(li,fSessionP->fTPInfo,TP_database);
+ SYSYNC_TRY {
+ // check mode
+ if (fGetPhase==gph_deleted) {
+ // report deleted items, that is those in the map that are not
+ // any more in the sync set of local IDs
+ // Note: we need no extra database access any more for this
+ if (!fGetPhasePrepared) {
+ // start at beginning of map table
+ fDeleteMapPos=fMapTable.begin();
+ fGetPhasePrepared=true;
+ }
+ do {
+ rep=false;
+ // report all those map entries that have no data entry (any more)
+ // as deleted records
+ // - check if there is more data in map table to process
+ if (fDeleteMapPos==fMapTable.end()) {
+ fGetPhase=gph_added_changed; // end of this phase, now report additions and modifications
+ rep=true; // continue in second part (get updated records)
+ fGetPhasePrepared=false; // next phase must be prepared
+ break;
+ }
+ else {
+ // search if there is a local ID still existing for that ID
+ TMapEntry entry = (*fDeleteMapPos);
+ // check only undeleted map entries
+ if (entry.deleted || entry.entrytype!=mapentry_normal) {
+ // deleted or non-normal map entry - simply skip
+ rep=true;
+ }
+ else if (isResuming() && !(entry.mapflags & mapflag_useforresume)) {
+ // this delete has already been reported deleted in previous suspended session
+ // (or it was deleted from the datastore WHILE or after start of a suspended session,
+ // in this case the item will be reported deleted in the NEXT complete sync session)
+ PDEBUGPRINTFX(DBG_ADMIN+DBG_EXOTIC,("Resuming and found item not marked for resume -> ignore for delete checking"));
+ rep=true;
+ }
+ else if (findInSyncSet(entry.localid.c_str())==fSyncSetList.end()) {
+ // this item has been deleted (and not yet been reported if this is a resume) -> report it
+ PDEBUGPRINTFX(DBG_ADMIN+DBG_EXOTIC,("Normal sync and found item in map which is not in syncset -> delete and mark with mapflag_pendingDeleteStatus"));
+ // - create new empty TMultiFieldItem
+ myitemP =
+ (TMultiFieldItem *) newItemForRemote(ity_multifield);
+ // - add IDs
+ myitemP->setRemoteID(entry.remoteid.c_str());
+ myitemP->setLocalID(entry.localid.c_str());
+ // - set operation
+ myitemP->setSyncOp(sop_delete);
+ // - set item
+ aSyncItemP = myitemP;
+ aEof=false; // report something
+ // mark entry as waiting for delete status
+ // NOTES: - we cannot delete the map entry until we get a confirmItemOp() for it
+ // - the pendingStatus flag does not need to be persistent between sessions, so we don't set the changed flag here!
+ entry.mapflags |= mapflag_pendingDeleteStatus;
+ (*fDeleteMapPos)=entry; // save updated entry in list
+ // found one to report
+ }
+ else {
+ rep=true; // must repeat again
+ }
+ }
+ // go to next
+ fDeleteMapPos++;
+ } while(rep);
+ } // report deleted phase
+ if (rep && fGetPhase==gph_added_changed) {
+ // report changed and added items. Those that are in the sync set but
+ // not yet in the map are added items, those that are already in the
+ // map are changed items if the mod date is newer than last sync
+ // and deleted if they don't pass extra filters
+ if (!fGetPhasePrepared) {
+ // start at beginning of map table
+ fSyncSetPos=fSyncSetList.begin();
+ fGetPhasePrepared=true;
+ }
+ do {
+ rep=false;
+ remid.erase();
+ // - check if there is more data in the syncset to process
+ if (fSyncSetPos==fSyncSetList.end()) {
+ fGetPhase=gph_done; // end of this phase, now report additions and modifications
+ rep=true; // continue in next phase (if any)
+ fGetPhasePrepared=false; // next phase must be prepared
+ break;
+ }
+ else {
+ // get syncset entry
+ TSyncSetItem *syncsetitemP = (*fSyncSetPos);
+ sop=sop_none;
+ TMapContainer::iterator pos;
+ // search if there is a map entry for this item already
+ pos=findMapByLocalID(syncsetitemP->localid.c_str(),mapentry_normal); // do not find deleted ones, only valid entries!
+ #ifdef SYDEBUG
+ if (pos!=fMapTable.end()) {
+ // Debug
+ PDEBUGPRINTFX(DBG_ADMIN+DBG_EXOTIC,(
+ "Item localID='%s' already has map entry: remoteid='%s', mapflags=0x%lX, changed=%d, deleted=%d, added=%d, markforresume=%d, savedmark=%d",
+ syncsetitemP->localid.c_str(),
+ (*pos).remoteid.c_str(),
+ (*pos).mapflags,
+ (int)(*pos).changed,
+ (int)(*pos).deleted,
+ (int)(*pos).added,
+ (int)(*pos).markforresume,
+ (int)(*pos).savedmark
+ ));
+ }
+ #endif
+ // now find what syncop results
+ if (fSlowSync && !isResuming()) {
+ // all items in local sync set are to be reported
+ PDEBUGPRINTFX(DBG_ADMIN+DBG_EXOTIC,("Slow sync and not resuming -> all items are first reported sop_wants_replace (will become add later)"));
+ sop=sop_wants_replace;
+ aChanged=true;
+ // clear the resend flags if any
+ if (pos!=fMapTable.end()) {
+ if ((*pos).mapflags & mapflag_resend) {
+ (*pos).mapflags &= ~mapflag_resend;
+ (*pos).changed = true;
+ }
+ }
+ }
+ else {
+ if (pos!=fMapTable.end()) {
+ #ifndef SYSYNC_CLIENT
+ // for slowsync resume - items that are not marked for resume, but already have a remoteID mapped
+ // must be presented for re-match with sop_reference_only
+ if (fSlowSync && isResuming() && !((*pos).mapflags & mapflag_useforresume) && !(*pos).remoteid.empty()) {
+ // this item apparently was already slow-sync-matched before the suspend - still show it for reference to avoid re-adding it
+ sop=sop_reference_only;
+ }
+ else
+ #endif
+ if (!isRefreshOnly()) {
+ // item is already in map: check if this is an already detected, but unfinished add
+ if (!((*pos).mapflags & mapflag_pendingAddConfirm)) {
+ // is a replace (not an add): changed if mod date newer or resend flagged (AND updates enabled)
+ // Note: For reporting modifications, the date of last sending-data-to-remote date is relevant
+ bool hasChanged=
+ (((*fSyncSetPos)->isModified) || ((*pos).mapflags & mapflag_resend))
+ && fReportUpdates;
+ // reset resend flag here if it acutually causes a resend here (otherwise, keep it for later)
+ if (hasChanged && (*pos).mapflags & mapflag_resend) {
+ PDEBUGPRINTFX(DBG_ADMIN+DBG_HOT,("Item '%s' treated as changed because resend-flag was set",syncsetitemP->localid.c_str()));
+ (*pos).mapflags &= ~mapflag_resend;
+ (*pos).changed = true;
+ }
+ if (isResuming()) {
+ // Basically, on resume just report those that have the mapflag_useforresume flag set.
+ // However, there is one difficult problem here:
+ // - Those that were successfully modified in the suspended part of a session, but get modified
+ // AGAIN between suspend and this resume will NOT be detected as changes any more. Therefore, for
+ // items we see here that don't have the mapflag_useforresume, we need to check additionally if
+ // they eventually have changed AFTER THE LAST SUSPEND SAVE
+ if ((*pos).mapflags & mapflag_useforresume) {
+ PDEBUGPRINTFX(DBG_ADMIN+DBG_EXOTIC,("Resuming and found marked-for-resume -> send replace"));
+ sop=sop_wants_replace;
+ hasChanged=true; // mark this as change (again, because marked for resume)
+ }
+ else if (!reportChangedOnly || hasChanged) {
+ // this one does not have the flag set, but it would be reported as a change if this was not a resume
+ // so this could be a change happened between suspend and resume
+ if (syncsetitemP->isModifiedAfterSuspend) {
+ // yes, this one was modified AFTER the suspended session, so we'll send it, too
+ PDEBUGPRINTFX(DBG_ADMIN+DBG_EXOTIC,("Resuming and found NOT marked-for-resume, but changed after last suspend -> send replace again"));
+ sop=sop_wants_replace;
+ hasChanged=true; // mark this as change (since last suspend, that is)
+ }
+ }
+ }
+ else if (!reportChangedOnly || hasChanged) {
+ // report only if aChanged was false on entry (=reportChangedOnly is false) or if modified anyway
+ PDEBUGPRINTFX(DBG_ADMIN+DBG_EXOTIC,("Normal sync, item changed -> send replace"));
+ sop=sop_wants_replace;
+ }
+ remid=(*pos).remoteid;
+ aChanged=hasChanged; // return changed status anyway
+ } // if not add
+ else {
+ // already detected, but unfinished add
+ // For server: Might be resent ONLY if resuming and marked for resume (otherwise, we MUST wait for map or we'll get duplicates)
+ // For client: resend always except if resuming and not marked for it
+ if (isResuming()) {
+ if ((*pos).mapflags & mapflag_useforresume) {
+ PDEBUGPRINTFX(DBG_ADMIN+DBG_EXOTIC,("Resuming and found marked-for-resume with mapflag_pendingAddConfirm -> unsent add, send it again"));
+ sop=sop_wants_add;
+ }
+ else {
+ PDEBUGPRINTFX(DBG_ADMIN+DBG_EXOTIC,("Resuming and NOT marked-for-resume with mapflag_pendingAddConfirm -> ignore"));
+ }
+ }
+ else {
+ #ifdef SYSYNC_CLIENT
+ // for client - repeating an add does not harm (but helps if it did not reach the server in the previous attempt
+ PDEBUGPRINTFX(DBG_ADMIN+DBG_EXOTIC,("Non-resume sync found item with mapflag_pendingAddConfirm -> send it again"));
+ sop=sop_wants_add;
+ #else
+ // for server - repeating an add potentially DOES harm (duplicate if client already got the add, but didn't send a map yet)
+ // but it's ok if it's flagged as an explicit resend (this happens only if we have got error status from remote)
+ if ((*pos).mapflags & mapflag_resend) {
+ PDEBUGPRINTFX(DBG_ADMIN+DBG_EXOTIC,("Item with mapflag_pendingAddConfirm (add) also has mapflag_resend -> we can safely resend"));
+ sop=sop_wants_add;
+ // - reset resend flag here
+ (*pos).mapflags &= ~mapflag_resend;
+ (*pos).changed = true;
+ }
+ else {
+ PDEBUGPRINTFX(DBG_ADMIN+DBG_EXOTIC,("Non-resume sync found item with mapflag_pendingAddConfirm (add) -> ignore until map is found"));
+ }
+ #endif
+ }
+ }
+
+
+ } // if not refreshonly
+ } // a map entry already exists
+ else {
+ // item is not yet in map: this is a new one. Report it if we are not resuming a previous session
+ // (in this case, items added after start of the original session will be detected at NEXT full
+ // session, so just leave it out for now)
+ // Note: this is first-time add detection. If we get this reported as sop_wants_add below, a map with
+ // mapflag_pendingAddConfirm will be created for it.
+ if (isRefreshOnly()) {
+ PDEBUGPRINTFX(DBG_ADMIN+DBG_EXOTIC,("New item (no map yet) detected during Refresh only -> ignore for now, will be added in next two-way sync"));
+ sop=sop_none;
+ }
+ else if (isResuming()) {
+ PDEBUGPRINTFX(DBG_ADMIN+DBG_EXOTIC,("Resuming and found new item which was not present in original session -> ignore for now, will be added in next regular sync"));
+ sop=sop_none;
+ }
+ else {
+ PDEBUGPRINTFX(DBG_ADMIN+DBG_EXOTIC,("Normal sync, item not yet in map -> add to remote"));
+ sop=sop_wants_add;
+ }
+ }
+ }
+ if (sop!=sop_none) {
+ // we need the item from the database
+ SYSYNC_TRY {
+ bool fetched=false;
+ // - check if we've already read it (at apiReadSyncSet())
+ if (syncsetitemP->itemP) {
+ // yes, take it out of the syncset
+ if (fNoSingleItemRead) {
+ // we may need it later, so we can only pass a copy to caller
+ myitemP =
+ (TMultiFieldItem *) newItemForRemote(ity_multifield);
+ if (!myitemP)
+ SYSYNC_THROW(TSyncException("newItemForRemote could not create new Item"));
+ // copy item (id, op, contents)
+ (*myitemP) = (*(syncsetitemP->itemP));
+ }
+ else {
+ // we don't need it later, pass original to caller and remove link in syncsetitem
+ myitemP = syncsetitemP->itemP;
+ syncsetitemP->itemP = NULL; // syncsetitem does not own it any longer
+ }
+ fetched=true; // we have the item
+ // Make sure item is fully equipped
+ // - assign local id, as it is required by DoDataSubstitutions
+ myitemP->setLocalID(syncsetitemP->localid.c_str());
+ // - assign remote id if we know one
+ myitemP->setRemoteID(remid.c_str());
+ // - set operation
+ myitemP->setSyncOp(sop);
+ }
+ else {
+ // We need to read the item here
+ // - create new empty TMultiFieldItem
+ myitemP =
+ (TMultiFieldItem *) newItemForRemote(ity_multifield);
+ if (!myitemP)
+ SYSYNC_THROW(TSyncException("newItemForRemote could not create new Item"));
+ // - assign local id, as it is required by DoDataSubstitutions
+ myitemP->setLocalID(syncsetitemP->localid.c_str());
+ // - assign remote id if we know one
+ myitemP->setRemoteID(remid.c_str());
+ // - set operation
+ myitemP->setSyncOp(sop);
+ // Now fetch item (read phase)
+ localstatus sta=apiFetchItem(*myitemP,true,syncsetitemP);
+ if (sta==LOCERR_OK) {
+ // successfully fetched
+ fetched=true;
+ }
+ else if (sta==404) {
+ // this record has been deleted since we have read the
+ // localid list. If this is was an add, we can simply
+ // ignore it
+ // - decide what this means now
+ if (sop==sop_reference_only || sop==sop_wants_add) {
+ // was deleted before we could fetch it for adding (or reference), just ignore
+ PDEBUGPRINTFX(DBG_DATA+DBG_DETAILS,("to-be-added record localID=%s was deleted during this sync session -> ignore",myitemP->getLocalID()));
+ rep=true;
+ delete myitemP;
+ // could still be that we have a mapflag_useforresume map entry, make sure we get rid of it
+ if (pos!=fMapTable.end()) {
+ // mark deleted
+ (*pos).deleted=true;
+ }
+ goto nextchanged;
+ }
+ else {
+ // was changed, but now doesn't exist any more -> treat as delete
+ // - adjust operation
+ PDEBUGPRINTFX(DBG_DATA+DBG_DETAILS,("to-be-changed record localID=%s was deleted during this sync session -> delete",myitemP->getLocalID()));
+ myitemP->setSyncOp(sop_delete);
+ myitemP->cleardata(); // make sure it is empty
+ // - set item to return to caller
+ aSyncItemP = myitemP;
+ aEof=false; // report something
+ if (!fSlowSync) {
+ // Note: we can safely assume that the map entry exists - otherwise sop would be sop_wants_add
+ // map entry must be marked to show that this now is a pending delete
+ (*pos).changed=true;
+ (*pos).mapflags &= ~(mapflag_pendingStatus);
+ (*pos).mapflags |= mapflag_pendingDeleteStatus;
+ }
+ fetched=false; // prevent map manipulations below
+ }
+ }
+ else {
+ // other error
+ SYSYNC_THROW(TSyncException("Error fetching data from DB",sta));
+ }
+ } // else: fetch from DB needed
+ if (fetched) {
+ /* %%% moved map adjustments to implReviewReadItem, as maps must not be created
+ * for items that get filtered out of the syncset (which happens later after
+ * calling this routine)!
+ *
+ // if we don't have a map entry here, this MUST be a potential add
+ // NOTE: Don't touch map if this is a for-reference-only (meaning that the map is
+ // already ok, and it is included here ONLY to find eventual slowsync matches)!
+ if (sop!=sop_reference_only) {
+ if (pos==fMapTable.end()) {
+ // this MUST be an add - create new map entry
+ modifyMap(mapentry_normal,myitemP->getLocalID(),NULL,mapflag_pendingAddConfirm+mapflag_pendingStatus,false);
+ }
+ else {
+ // only adjust map flags
+ if (fSlowSync || sop==sop_add || sop==sop_wants_add) {
+ // for slowsync, all items are kind of "adds", that is, not yet mapped (server case)
+ // or not yet statused (client case)
+ // for normal sync, make sure adds get mapflag_pendingAddConfirm set
+ (*pos).mapflags &= ~mapflag_pendingDeleteStatus;
+ (*pos).mapflags |= mapflag_pendingAddConfirm+mapflag_pendingStatus;
+ (*pos).changed=true;
+ }
+ else {
+ // not add (and never a delete here) -> is replace. Set status pending flag (which doesn't need to be saved to DB)
+ (*pos).mapflags &= ~(mapflag_pendingAddConfirm+mapflag_pendingDeleteStatus);
+ (*pos).mapflags |= mapflag_pendingStatus;
+ }
+ }
+ }
+ */
+ // set item to return to caller
+ aSyncItemP = myitemP;
+ aEof=false; // report something
+ // info
+ PDEBUGPRINTFX(DBG_DATA+DBG_DETAILS,("Fetched record data from DB with localID=%s",myitemP->getLocalID()));
+ }
+ }
+ SYSYNC_CATCH(...)
+ if (myitemP) delete myitemP;
+ SYSYNC_RETHROW;
+ SYSYNC_ENDCATCH
+ } // item to report
+ else {
+ // item read must not be reported, try to get next
+ rep=true;
+ }
+ } // not end of syncset
+ nextchanged:
+ // go to next
+ fSyncSetPos++;
+ } while(rep);
+ } // report added and changed phase
+ if (rep) {
+ // end of items
+ aEof=true;
+ // syncset can be deleted only if we can retrieve individual items later from DB
+ // if not, we must keep the syncset in memory
+ if (!fNoSingleItemRead) {
+ // we don't need the SyncSet list any more
+ // (and especially the items that did NOT get reported to the caller of GetItem
+ // can be deleted now to free memory. Reported items are now owned by the caller)
+ DeleteSyncSet(fMultiFolderDB);
+ }
+ }
+ // done
+ TP_START(fSessionP->fTPInfo,li);
+ // show item fetched
+ #ifdef SYDEBUG
+ if (PDEBUGTEST(DBG_DATA+DBG_SCRIPTS) && aSyncItemP)
+ aSyncItemP->debugShowItem(DBG_DATA); // show item fetched
+ #endif
+ }
+ SYSYNC_CATCH(exception &e)
+ PDEBUGPRINTFX(DBG_ERROR,("GetItem exception: %s",e.what()));
+ TP_START(fSessionP->fTPInfo,li);
+ sta=510;
+ SYSYNC_ENDCATCH
+ // done
+ return sta;
+} // TCustomImplDS::implGetItem
+
+#endif
+
+
+// end of read
+localstatus TCustomImplDS::implEndDataRead(void)
+{
+ #ifdef BASED_ON_BINFILE_CLIENT
+ // let binfile handle it
+ return inherited::implEndDataRead();
+ #else
+ return apiEndDataRead();
+ #endif
+} // TCustomImplDS::implEndDataRead
+
+
+// start of write
+localstatus TCustomImplDS::implStartDataWrite()
+{
+ localstatus sta = LOCERR_OK;
+
+ #ifdef BASED_ON_BINFILE_CLIENT
+ // let binfile handle it
+ sta = inherited::implStartDataWrite();
+ #else
+ SYSYNC_TRY {
+ // let actual data implementation prepare
+ sta = apiStartDataWrite();
+ if (sta==LOCERR_OK) {
+ // Notes:
+ // - transaction starts implicitly when first INSERT / UPDATE / DELETE occurs
+ // - resumed slow refreshes must NOT zap the sync set again!
+ // - prevent zapping when datastore is in readonly mode!
+ if (fRefreshOnly && fSlowSync && !isResuming() && !fReadOnly) {
+ // now, we need to zap the DB first
+ PDEBUGBLOCKFMTCOLL(("ZapSyncSet","Zapping sync set in database","datastore=%s",getName()));
+ SYSYNC_TRY {
+ sta=apiZapSyncSet();
+ PDEBUGENDBLOCK("ZapSyncSet");
+ }
+ SYSYNC_CATCH(exception &e)
+ PDEBUGPRINTFX(DBG_ERROR,("ZapSyncSet exception: %s",e.what()));
+ sta=510;
+ // end of DB read
+ PDEBUGENDBLOCK("ZapSyncSet");
+ SYSYNC_ENDCATCH
+ if (sta!=LOCERR_OK) {
+ PDEBUGPRINTFX(DBG_ERROR,("implStartDataWrite: cannot zap data for refresh, status=%hd",sta));
+ }
+ // ok, now that the old data is zapped, we MUST forget the former sync set, it is now for sure invalid
+ DeleteSyncSet(false);
+ }
+ }
+ }
+ SYSYNC_CATCH(exception &e)
+ PDEBUGPRINTFX(DBG_ERROR,("implStartDataWrite exception: %s",e.what()));
+ sta=510;
+ SYSYNC_ENDCATCH
+ #endif
+ // done
+ return sta;
+} // TCustomImplDS::implStartDataWrite
+
+
+#ifndef BASED_ON_BINFILE_CLIENT
+
+// review reported entry (allows post-processing such as map deleting)
+// MUST be called after StartDataWrite, before any actual writing,
+// for each item obtained in GetItem
+localstatus TCustomImplDS::implReviewReadItem(
+ TSyncItem &aItem // the item
+)
+{
+ // get the operation
+ TSyncOperation sop = aItem.getSyncOp();
+ // NOTE: Don't touch map if this is a for-reference-only (meaning that the map is
+ // already ok, and it is included here ONLY to find eventual slowsync matches)!
+ if (sop!=sop_reference_only) {
+ // Adjust map flags or create map if needed
+ if (fSlowSync || sop==sop_add || sop==sop_wants_add) {
+ // for slowsync, all items are kind of "adds", that is, not yet mapped (server case)
+ // or not yet statused (client case)
+ // for normal sync, make sure adds get mapflag_pendingAddConfirm set and remoteID gets cleared (is not valid in any case)
+ modifyMap(mapentry_normal,aItem.getLocalID(),"",mapflag_pendingAddConfirm+mapflag_pendingStatus,false,mapflag_pendingDeleteStatus);
+ }
+ else if (sop==sop_delete) {
+ // In case when postFetch filtering changed an item from replace to delete,
+ // we must make sure that the map entry gets the deleted status set
+ // - simply make sure deleted item's map entry will get deleted once the delete is confirmed
+ modifyMap(mapentry_normal,aItem.getLocalID(),NULL,mapflag_pendingDeleteStatus,false,0);
+ }
+ else {
+ // not add (and never a delete here) -> is replace. Set status pending flag (which doesn't need to be saved to DB)
+ modifyMap(mapentry_normal,aItem.getLocalID(),NULL,mapflag_pendingStatus,false,mapflag_pendingAddConfirm+mapflag_pendingDeleteStatus);
+ }
+ }
+ return LOCERR_OK;
+} // TCustomImplDS::implReviewReadItem
+
+
+// - retrieve specified item from database
+bool TCustomImplDS::implRetrieveItemByID(
+ TSyncItem &aItem, // the item
+ TStatusCommand &aStatusCommand
+)
+{
+ bool ok=true;
+
+ // determine item's local ID
+ if (!aItem.hasLocalID()) {
+ #ifdef SYSYNC_CLIENT
+ // client case: MUST have local ID
+ aStatusCommand.setStatusCode(400); // bad request (no address)
+ return false;
+ #else
+ // no local ID specified directly, address by remote ID
+ if (!aItem.hasRemoteID()) {
+ aStatusCommand.setStatusCode(400); // bad request (no address)
+ return false;
+ }
+ // lookup remote ID in map
+ TMapContainer::iterator mappos = findMapByRemoteID(aItem.getRemoteID());
+ if (mappos==fMapTable.end()) {
+ aStatusCommand.setStatusCode(404); // not found
+ return false;
+ }
+ // set local ID
+ aItem.setLocalID(mappos->localid.c_str());
+ // check if we have a local ID now
+ if (!aItem.hasLocalID()) {
+ aStatusCommand.setStatusCode(400); // bad request (no address)
+ return false;
+ }
+ #endif
+ }
+ TP_DEFIDX(li);
+ TP_SWITCH(li,fSessionP->fTPInfo,TP_database);
+ // if we can't fetch single items from DB, we should have all
+ // in the syncset and can get them from there
+ if (fNoSingleItemRead) {
+ // search sync set by localID
+ TSyncSetList::iterator pos=findInSyncSet(aItem.getLocalID());
+ if (pos!=fSyncSetList.end() && (*pos)->itemP) {
+ // found, copy data to item passed
+ aItem.replaceDataFrom(*((*pos)->itemP));
+ }
+ else {
+ // not found
+ aStatusCommand.setStatusCode(404); // not found
+ ok=false;
+ }
+ }
+ else {
+ // fetch from DB
+ SYSYNC_TRY {
+ // fetch data from DB
+ localstatus sta=apiFetchItem(*((TMultiFieldItem *)&aItem),false,NULL); // no syncsetitem known
+ if (sta!=LOCERR_OK) {
+ aStatusCommand.setStatusCode(sta);
+ ok=false;
+ }
+ }
+ SYSYNC_CATCH(exception &e)
+ PDEBUGPRINTFX(DBG_ERROR,("implRetrieveItemByID exception: %s",e.what()));
+ aStatusCommand.setStatusCode(510);
+ ok=false;
+ SYSYNC_ENDCATCH
+ }
+ // return status
+ TP_START(fSessionP->fTPInfo,li);
+ return ok;
+} // TCustomImplDS::implRetrieveItemByID
+
+
+
+/// called to set maps.
+/// @note aRemoteID or aLocalID can be NULL - which signifies deletion of a map entry
+/// @note that this might be needed for clients accessing a server-style database as well
+localstatus TCustomImplDS::implProcessMap(cAppCharP aRemoteID, cAppCharP aLocalID)
+{
+ localstatus sta = 510; // error
+ // Note: Map must be ready to have either empty local or remote ID to delete an entry
+ if (!aLocalID) {
+ // delete by remote ID
+ modifyMap(mapentry_normal,NULL,aRemoteID,0,true);
+ PDEBUGPRINTFX(DBG_ADMIN,("Map entry (or entries) for RemoteID='%s' removed",aRemoteID));
+ sta=LOCERR_OK;
+ }
+ else {
+ // - if RemoteID is empty, this means that Map should be deleted
+ if (!aRemoteID) {
+ // Map delete request
+ modifyMap(mapentry_normal,aLocalID,NULL,0,true);
+ PDEBUGPRINTFX(DBG_ADMIN,("Map entry (or entries) for LocalID='%s' removed",aLocalID));
+ sta=LOCERR_OK;
+ }
+ else {
+ // Map modify or add request, automatically clears mapflag_pendingAddConfirm (and all other)
+ // flag(s), INLCUDING resume mark. So even if all sent adds (and not only the unsent ones)
+ // get marked for resume in a suspend (fRelyOnEarlyMaps set), those that actually got added
+ // in the previous session will not be re-sent for sure.
+ modifyMap(mapentry_normal,aLocalID,aRemoteID,0,false);
+ PDEBUGPRINTFX(DBG_ADMIN,("Map entry updated: LocalID='%s', RemoteID='%s'",aLocalID,aRemoteID));
+ sta=LOCERR_OK;
+ } // if not map delete
+ }
+ // return status
+ return sta;
+} // TCustomImplDS::implProcessMap
+
+
+
+/// process item (according to operation: add/delete/replace - and for future: copy/move)
+/// @note data items will be sent only after StartWrite()
+bool TCustomImplDS::implProcessItem(
+ TSyncItem *aItemP, // the item
+ TStatusCommand &aStatusCommand
+) {
+ bool ok=true;
+ localstatus sta=LOCERR_OK;
+ string localID;
+ const char *remoteID;
+ // %%% bool RemoteIDKnown=false;
+ TMapContainer::iterator mappos;
+ TSyncOperation sop=sop_none;
+
+ TP_DEFIDX(li);
+ TP_SWITCH(li,fSessionP->fTPInfo,TP_database);
+ // get field map list
+ TFieldMapList &fml = fConfigP->fFieldMappings.fFieldMapList;
+ SYSYNC_TRY {
+ // get casted item pointer
+ TMultiFieldItem *myitemP = (TMultiFieldItem *)aItemP;
+ // - get op
+ sop = myitemP->getSyncOp();
+ // - check IDs
+ #ifdef SYSYNC_CLIENT
+ // Client case: we always get the local ID, except for add
+ localID=myitemP->getLocalID();
+ remoteID=myitemP->getRemoteID();
+ if (!localID.empty() && sop!=sop_add && sop!=sop_wants_add)
+ mappos=findMapByLocalID(localID.c_str(),mapentry_normal); // for all but sop == sop_add
+ else
+ mappos=fMapTable.end(); // if there is no localid or it is an add, we have no map entry yet
+ #else
+ // Server case: we only know the remote ID
+ // - get remoteID
+ remoteID=myitemP->getRemoteID();
+ // first see if we have a map entry for this remote ID
+ localID.erase(); // none yet
+ // Note: even items detected for deletion still have a map item until deletion is confirmed by the remote party,
+ // so we'll be able to update already "deleted" items (in case they are not really gone, but only invisible in the sync set)
+ mappos=findMapByRemoteID(remoteID); // search for it
+ if (mappos!=fMapTable.end()) {
+ localID = (*mappos).localid; // assign it if we have it
+ }
+ #endif
+ // - now perform op
+ aStatusCommand.setStatusCode(510); // default DB error
+ switch (sop) {
+ /// @todo sop_copy is now implemented by read/add sequence
+ /// in localEngineDS, but will be moved here later eventually
+ case sop_add :
+ // add item and retrieve new localID for it
+ sta = apiAddItem(*myitemP,localID);
+ #ifndef SYSYNC_CLIENT
+ if (sta==DB_DataMerged) {
+ // while adding, data was merged with pre-existing data (external from the sync set)
+ // so we should retrieve the full data and send an update back to the client
+ // - this is like forcing a conflict, i.e. this loads the item by local/remoteid and adds it to
+ // the to-be-sent list of the server.
+ PDEBUGPRINTFX(DBG_DATA,("Database adapter indicates that added item was merged with pre-existing data (status 207), so update client with merged item"));
+ forceConflict(myitemP);
+ sta = LOCERR_OK; // otherwise, treat as ok
+ }
+ #endif
+ if (sta!=LOCERR_OK) {
+ aStatusCommand.setStatusCode(sta);
+ ok=false;
+ }
+ else {
+ // added ok
+ // - save what is needed for finalisation
+ if (fNeedFinalisation) {
+ myitemP->setLocalID(localID.c_str()); // finalisation needs to know the local ID
+ queueForFinalisation(myitemP);
+ }
+ // - status ok
+ aStatusCommand.setStatusCode(201); // item added
+ // - add or update map entry (in client case, remoteID is irrelevant and eventually is not saved)
+ modifyMap(mapentry_normal,localID.c_str(),remoteID,0,false);
+ ok=true;
+ }
+ break;
+ case sop_replace :
+ if (mappos==fMapTable.end()) {
+ // not found in map table
+ aStatusCommand.setStatusCode(404);
+ ok=false;
+ }
+ else {
+ // - make sure item has local ID set
+ myitemP->setLocalID(localID.c_str());
+ // update item
+ sta = apiUpdateItem(*myitemP);
+ if (sta!=LOCERR_OK) {
+ aStatusCommand.setStatusCode(sta);
+ ok=false;
+ }
+ else {
+ // updated ok
+ // - save what is needed for finalisation
+ if (fNeedFinalisation)
+ queueForFinalisation(myitemP);
+ // - status ok
+ aStatusCommand.setStatusCode(200); // item replaced ok
+ ok=true;
+ }
+ }
+ break;
+ case sop_delete :
+ case sop_archive_delete :
+ case sop_soft_delete :
+ if (mappos==fMapTable.end()) {
+ // not found in map table means that remote is trying to
+ // delete an item that wasn't mapped before. This is different from
+ // the case below when the actual item is not there any more, but
+ // the map still existed (-> 211)
+ aStatusCommand.setStatusCode(404);
+ ok=false;
+ }
+ else {
+ // - make sure item has local ID set
+ myitemP->setLocalID(localID.c_str());
+ // delete item
+ sta = apiDeleteItem(*myitemP);
+ if (sta!=LOCERR_OK) {
+ // not found is reported as successful 211 status, because result is ok (item deleted, whatever reason)
+ if (sta==404)
+ sta=211; // ok, but item was not there any more
+ else
+ ok=false; // others are real errors
+ aStatusCommand.setStatusCode(sta);
+ }
+ else {
+ // - ok
+ aStatusCommand.setStatusCode(200); // item deleted ok
+ ok=true;
+ }
+ if (ok) {
+ // delete map entry anyway
+ // - mark the map entry for deletion
+ modifyMap(mapentry_normal,localID.c_str(),NULL,0,true);
+ }
+ }
+ break;
+ default :
+ SYSYNC_THROW(TSyncException("Unknown sync op in TCustomImplDS::implProcessItem"));
+ } // switch
+ if (ok) {
+ // successful, save new localID in item
+ myitemP->setLocalID(localID.c_str());
+ // make sure transaction is complete after processing the item
+ TP_START(fSessionP->fTPInfo,li);
+ return true;
+ }
+ else {
+ // make sure transaction is rolled back for this item
+ TP_START(fSessionP->fTPInfo,li);
+ return false;
+ }
+ }
+ SYSYNC_CATCH (exception &e)
+ PDEBUGPRINTFX(DBG_ERROR,("******** TCustomImplDS::implProcessItem exception: %s",e.what()));
+ if (sop==sop_delete) {
+ // %%% use 500 instead of 510 here to make SCTS happy
+ #if defined WINCE || !defined _MSC_VER
+ #warning "is this SCTS hack still required??"
+ #endif
+ aStatusCommand.setStatusCode(500);
+ }
+ else
+ aStatusCommand.setStatusCode(510);
+ goto error;
+ SYSYNC_ENDCATCH
+ SYSYNC_CATCH (...)
+ PDEBUGPRINTFX(DBG_ERROR,("******** TCustomImplDS::implProcessItem unknown exception"));
+ goto error;
+ SYSYNC_ENDCATCH
+error:
+ // Switch back to previous timer
+ TP_START(fSessionP->fTPInfo,li);
+ return false;
+} // TCustomImplDS::implProcessItem
+
+
+
+// private helper to prepare for apiSaveAdminData()
+localstatus TCustomImplDS::SaveAdminData(bool aSessionFinished, bool aSuccessful)
+{
+ TMapContainer::iterator pos;
+ localstatus sta=LOCERR_OK;
+ // calculate difference between current and previous state of tempGUID maps or pending maps
+ // - mark all non-main map entries as deleted (those that still exist will be re-added later)
+ // - also do some clean-up in case of successful end-of-session
+ pos=fMapTable.begin();
+ while (pos!=fMapTable.end()) {
+ if ((*pos).entrytype!=mapentry_normal) {
+ // Note: this is not strictly needed any more, as non-normal maps are
+ // now already entered into fMapTable with deleted flag set
+ // (to make sure they don't get used by accident)
+ (*pos).deleted=true;
+ }
+ else if (!(*pos).deleted) {
+ // in case of map table without flags, we must get rid of all non-real maps
+ if (!dsResumeSupportedInDB() && aSessionFinished) {
+ #ifndef SYSYNC_CLIENT
+ // For client, remoteid is irrelevant and can well be empty
+ // Map entries exist for those items that are not newly added on the client
+ // For server, maps w/o remoteid are not really mapped and must not be saved when
+ // we have no flags to mark this special conditon (mapflag_pendingAddConfirm)
+ if ((*pos).remoteid.empty()) {
+ // no remoteid -> this is not a real map, we cannot represent it w/o resume support (=flags) in map table
+ DEBUGPRINTFX(DBG_ADMIN+DBG_EXOTIC,("LocalID='%s' has no remoteID - cannot be stored in non-DS-1.2 Map DB -> removed map",(*pos).localid.c_str()));
+ if ((*pos).added) {
+ // was never added to DB, so no need to delete it in DB either - just forget it
+ TMapContainer::iterator delpos=pos++;
+ fMapTable.erase(delpos);
+ continue;
+ }
+ else {
+ // is already in DB - mark deleted
+ (*pos).deleted=true;
+ }
+ }
+ else
+ #endif
+ {
+ // clear all specials
+ (*pos).mapflags=0;
+ (*pos).savedmark=false;
+ (*pos).markforresume=false;
+ }
+ }
+ // normal, undeleted map entry, check for cleanup
+ else if (
+ aSessionFinished && aSuccessful &&
+ ((*pos).mapflags & mapflag_pendingAddConfirm)
+ ) {
+ // successful end of session - we can forget pending add confirmations (as the add commands apparently never reached the remote at all)
+ #ifndef SYSYNC_CLIENT
+ // Note: for clients, maps can well have an empty remoteid (because it does not need to be saved)
+ if ((*pos).remoteid.empty()) {
+ PDEBUGPRINTFX(DBG_ADMIN+DBG_EXOTIC,("Successful end of session but localID='%s' has no remoteID and pendingAddConfirm still set -> removed map",(*pos).localid.c_str()));
+ // if not mapped, this will be a re-add in the next session, so forget it for now
+ if ((*pos).added) {
+ // was never added to DB, so no need to delete it in DB either - just forget it
+ TMapContainer::iterator delpos=pos++;
+ fMapTable.erase(delpos);
+ continue;
+ }
+ else {
+ // is already in DB - mark deleted
+ (*pos).deleted=true;
+ }
+ }
+ else
+ #endif
+ {
+ // For server: is mapped, which means that it now exists in the client - just clean mapflag_pendingAddConfirm
+ // For client: just clean the pendingAddConfirm
+ // Note: maps like this should not exist at this time - as at end of a successful session all items should
+ // have got confirmation.
+ PDEBUGPRINTFX(DBG_ERROR,("Apparently successful end of session - but localID='%s' has pendingAddConfirm still set: sync may not be fully complete",(*pos).localid.c_str()));
+ (*pos).mapflags &= ~mapflag_pendingAddConfirm; // keep mapflag_pendingStatus for documentary/debug purposes
+ (*pos).changed = true; // make sure it gets written to DB
+ }
+ }
+ }
+ // increment here only so we can do "continue" w/o pos increment after delete
+ pos++;
+ } // for all map entries
+ if (dsResumeSupportedInDB()) {
+ // If we have different map entry types - re-add the special entries from the separate lists
+ // Note: these entries are already in the global map table, but with the deleted flag set.
+ // Here those that still exist now will be re-activated (without saving them again if not needed)
+ TStringToStringMap::iterator spos;
+ #ifdef SYSYNC_CLIENT
+ // - now pending maps (unsent ones)
+ PDEBUGPRINTFX(DBG_ADMIN+DBG_EXOTIC,("SaveAdminData: adding %ld entries from fPendingAddMap as mapentry_pendingmap",fPendingAddMaps.size()));
+ for (spos=fPendingAddMaps.begin();spos!=fPendingAddMaps.end();spos++) {
+ string locID = (*spos).first;
+ dsFinalizeLocalID(locID); // make sure we have the permanent version in case datastore implementation did deliver temp IDs
+ modifyMap(mapentry_pendingmap, locID.c_str(), (*spos).second.c_str(), 0, false);
+ }
+ // - now pending maps (sent, but not seen status yet)
+ PDEBUGPRINTFX(DBG_ADMIN+DBG_EXOTIC,("SaveAdminData: adding %ld entries from fUnconfirmedMaps as mapentry_pendingmap/mapflag_pendingMapStatus",fUnconfirmedMaps.size()));
+ for (spos=fUnconfirmedMaps.begin();spos!=fUnconfirmedMaps.end();spos++) {
+ modifyMap(mapentry_pendingmap, (*spos).first.c_str(), (*spos).second.c_str(), mapflag_pendingMapStatus, false);
+ }
+ #else
+ // - the tempguid maps
+ PDEBUGPRINTFX(DBG_ADMIN+DBG_EXOTIC,("SaveAdminData: adding %ld entries from fTempGUIDMap as mapentry_tempidmap",fTempGUIDMap.size()));
+ for (spos=fTempGUIDMap.begin();spos!=fTempGUIDMap.end();spos++) {
+ modifyMap(mapentry_tempidmap, (*spos).second.c_str(), (*spos).first.c_str(), 0, false);
+ }
+ #endif
+ }
+ sta=apiSaveAdminData(aSessionFinished,aSuccessful);
+ if (sta!=LOCERR_OK) {
+ PDEBUGPRINTFX(DBG_ERROR,("SaveAdminData failed, err=%hd",sta));
+ }
+ return sta;
+} // TCustomImplDS::SaveAdminData
+
+#endif // not BASED_ON_BINFILE_CLIENT
+
+
+// save end of session state
+localstatus TCustomImplDS::implSaveEndOfSession(bool aUpdateAnchors)
+{
+ localstatus sta=LOCERR_OK;
+ PDEBUGBLOCKCOLL("SaveEndOfSession");
+ // update TCustomImplDS dsSavedAdmin variables (other levels have already updated their variables
+ if (aUpdateAnchors) {
+ if (!fRefreshOnly || fSlowSync) {
+ // This was really a two-way sync or we implicitly know that
+ // we are now in sync with remote (like after one-way-from-remote refresh = reload local)
+ #ifndef BASED_ON_BINFILE_CLIENT
+ // Note: in case of BASED_ON_BINFILE_CLIENT, these updates will be done by binfileds
+ if (fConfigP->fSyncTimeStampAtEnd) {
+ // if datastore cannot explicitly set modification timestamps, best time to save is current time
+ fPreviousToRemoteSyncCmpRef = fAgentP->getDatabaseNowAs(TCTX_UTC);
+ }
+ else {
+ // if datastore can set modification timestamps, best time to save is start of sync
+ fPreviousToRemoteSyncCmpRef = fCurrentSyncTime;
+ }
+ #endif
+ // also update opaque reference string eventually needed in DS API implementations
+ fPreviousToRemoteSyncIdentifier = fCurrentSyncIdentifier;
+ }
+ // updating anchor means invalidating last Suspend
+ fPreviousSuspendCmpRef = fPreviousToRemoteSyncCmpRef; // setting to current reference can do less harm than setting it to zero
+ fPreviousSuspendIdentifier.erase();
+ }
+ #ifdef BASED_ON_BINFILE_CLIENT
+ // if we sit on top of binfile, let binfile do the actual end-if-session work
+ // (updates of cmprefs etc. are done at binfile level again).
+ sta = inherited::implSaveEndOfSession(aUpdateAnchors);
+ #else
+ // save admin data now
+ sta=SaveAdminData(true,aUpdateAnchors); // end of session
+ // we can foget the maps now
+ fMapTable.clear();
+ #endif
+ PDEBUGENDBLOCK("SaveEndOfSession");
+ return sta;
+} // TCustomImplDS::implSaveEndOfSession
+
+
+// - end write with commit
+bool TCustomImplDS::implEndDataWrite(void)
+{
+ localstatus sta=LOCERR_OK;
+ TP_DEFIDX(li);
+ TP_SWITCH(li,fSessionP->fTPInfo,TP_database);
+ SYSYNC_TRY {
+ // first make sure data writing ends (and obtain current sync identifier)
+ sta = apiEndDataWrite(fCurrentSyncIdentifier);
+ }
+ SYSYNC_CATCH (exception &e)
+ PDEBUGPRINTFX(DBG_ERROR,("******** implEndDataWrite exception: %s",e.what()));
+ TP_START(fSessionP->fTPInfo,li);
+ return false;
+ SYSYNC_ENDCATCH
+ TP_START(fSessionP->fTPInfo,li);
+ #ifdef BASED_ON_BINFILE_CLIENT
+ // binfile level must be called as well
+ sta = inherited::implEndDataWrite();
+ #endif
+ return sta;
+} // TCustomImplDS::implEndDataWrite
+
+
+// delete sync set one by one
+localstatus TCustomImplDS::zapSyncSet(void)
+{
+ TSyncSetList::iterator pos;
+ localstatus sta;
+ // - create dummy item
+ TStatusCommand dummy(getSession());
+ TMultiFieldItem *delitemP =
+ static_cast<TMultiFieldItem *>(newItemForRemote(ity_multifield));
+ delitemP->setSyncOp(sop_delete);
+ PDEBUGPRINTFX(DBG_DATA,(
+ "Zapping datastore: deleting %ld items from database",
+ (long)fSyncSetList.size()
+ ));
+ for (pos=fSyncSetList.begin(); pos!=fSyncSetList.end(); ++pos) {
+ // delete
+ delitemP->setLocalID((*pos)->localid.c_str());
+ sta = apiDeleteItem(*delitemP);
+ // success or "211 - not deleted" is ok.
+ if (sta!=LOCERR_OK && sta!=211) return sta;
+ }
+ delete delitemP;
+ return LOCERR_OK; // zapped ok
+} // TCustomImplDS::zapSyncSet
+
+
+#ifndef BASED_ON_BINFILE_CLIENT
+
+// - save status information required to eventually perform a resume (as passed to datastore with
+// implMarkOnlyUngeneratedForResume() and implMarkItemForResume())
+// (or, in case the session is really complete, make sure that no resume state is left)
+localstatus TCustomImplDS::implSaveResumeMarks(void)
+{
+ PDEBUGBLOCKCOLL("SaveResumeMarks");
+ // update anchoring info for resume
+ if (fConfigP->fSyncTimeStampAtEnd) {
+ // if datastore cannot explicitly set modification timestamps, best time to save is current time
+ fPreviousSuspendCmpRef = fAgentP->getDatabaseNowAs(TCTX_UTC);
+ }
+ else {
+ // if datastore can set modification timestamps, best time to save is start of sync
+ fPreviousSuspendCmpRef = fCurrentSyncTime;
+ }
+ // also update opaque reference string eventually needed in DS API implementations
+ fPreviousSuspendIdentifier = fCurrentSyncIdentifier;
+ // save admin data now
+ localstatus sta=SaveAdminData(false,false); // not end of session, not successful end either
+ PDEBUGENDBLOCK("SaveResumeMarks");
+ return sta;
+} // TCustomImplDS::implSaveResumeMarks
+
+
+#else // not BASED_ON_BINFILE_CLIENT
+
+
+// Connecting methods when CustomImplDS is used on top of BinFileImplDS
+
+
+bool TCustomImplDS::makeSyncSetLoaded(bool aNeedAll)
+{
+ localstatus sta = LOCERR_OK;
+
+ if (!fSyncSetLoaded) {
+ PDEBUGBLOCKFMTCOLL(("ReadSyncSet","Reading Sync Set from Database","datastore=%s",getName()));
+ SYSYNC_TRY {
+ sta = apiReadSyncSet(aNeedAll);
+ PDEBUGENDBLOCK("ReadSyncSet");
+ }
+ SYSYNC_CATCH(exception &e)
+ PDEBUGPRINTFX(DBG_ERROR,("makeSyncSetLoaded exception: %s",e.what()));
+ sta=510;
+ // end of DB read
+ PDEBUGENDBLOCK("ReadSyncSet");
+ SYSYNC_ENDCATCH
+ if (sta==LOCERR_OK)
+ fSyncSetLoaded=true; // is now loaded
+ }
+ return fSyncSetLoaded; // ok only if now loaded
+} // TCustomImplDS::makeSyncSetLoaded
+
+
+/// get first item's ID and modification status from the sync set
+/// @return false if no item found
+bool TCustomImplDS::getFirstItemInfo(localid_out_t &aLocalID, bool &aItemHasChanged)
+{
+ /*
+ // make sure we have the sync set
+ if (!makeSyncSetLoaded(
+ fSlowSync
+ #ifdef OBJECT_FILTERING
+ || fFilteringNeededForAll
+ #endif
+ ))
+ return false; // failed = no item to return
+ */
+ // reset the iterator
+ fSyncSetPos = fSyncSetList.begin();
+ // now get first item's info
+ return getNextItemInfo(aLocalID, aItemHasChanged);
+} // TCustomImplDS::getFirstItemInfo
+
+
+
+/// get next item's ID and modification status from the sync set.
+/// @return false if no item found
+bool TCustomImplDS::getNextItemInfo(localid_out_t &aLocalID, bool &aItemHasChanged)
+{
+ if (!fSyncSetLoaded)
+ return false; // no syncset, nothing to report
+ if (fSyncSetPos!=fSyncSetList.end()) {
+ // get the info
+ TSyncSetItem *syncsetitemP = (*fSyncSetPos);
+ // - ID
+ STR_TO_LOCALID(syncsetitemP->localid.c_str(),aLocalID);
+ // - changeflag
+ aItemHasChanged = syncsetitemP->isModified;
+ // advance to next item in sync set
+ fSyncSetPos++;
+ // ok
+ return true;
+ }
+ // no more items
+ return false;
+} // TCustomImplDS::getNextItemInfo
+
+
+
+/// get item by local ID from the sync set. Caller obtains ownership if aItemP is not NULL after return
+/// @return != LOCERR_OK if item with specified ID is not found.
+localstatus TCustomImplDS::getItemByID(localid_t aLocalID, TSyncItem *&aItemP)
+{
+ if (!fSyncSetLoaded)
+ return 510; // syncset should be loaded here!
+ // search in syncset
+ string localid;
+ LOCALID_TO_STRING(aLocalID,localid);
+ TSyncSetList::iterator syncsetpos = findInSyncSet(localid.c_str());
+ if (syncsetpos==fSyncSetList.end())
+ return 404; // not found
+ // return item already fetched or fetch it if not fetched before
+ TSyncSetItem *syncsetitemP = (*syncsetpos);
+ if (syncsetitemP->itemP) {
+ // already fetched - pass it to caller and remove link in syncsetitem
+ aItemP = syncsetitemP->itemP;
+ syncsetitemP->itemP = NULL; // syncsetitem does not own it any longer
+ }
+ else {
+ // item not yet fetched (or already retrieved once), fetch it now
+ // - create new empty TMultiFieldItem
+ aItemP =
+ (TMultiFieldItem *) newItemForRemote(ity_multifield);
+ if (!aItemP)
+ return 510;
+ // - assign local id, as it is required by DoDataSubstitutions
+ aItemP->setLocalID(syncsetitemP->localid.c_str());
+ // - set default operation
+ aItemP->setSyncOp(sop_replace);
+ // Now fetch item (read phase)
+ localstatus sta=apiFetchItem(*((TMultiFieldItem *)aItemP),true,syncsetitemP);
+ if (sta!=LOCERR_OK)
+ return sta; // error
+ }
+ // ok
+ return LOCERR_OK;
+} // TCustomImplDS::getItemByID
+
+
+/* no need to implement this here, calling API level directly from binfile is enough
+/// signal start of data write phase
+localstatus TCustomImplDS::apiStartDataWrite(void);
+*/
+
+/* must not be implemented here, as TCustomImplDS also derives
+ implEndDataWrite() which makes sure apiEndDataWrite(cmpRef) is called
+ at the right time
+/// signal end of data write phase
+localstatus TCustomImplDS::apiEndDataWrite(void)
+{
+ // call customImplDS branch's version of apiEndDataWrite
+ string thisSyncIdentifier;
+ return apiEndDataWrite(thisSyncIdentifier);
+} // TCustomImplDS::apiEndDataWrite
+*/
+
+
+/// update item by local ID in the sync set. Caller retains ownership of aItemP
+/// @return != LOCERR_OK if item with specified ID is not found.
+localstatus TCustomImplDS::updateItemByID(localid_t aLocalID, TSyncItem *aItemP)
+{
+ if (!aItemP) return 510; // error
+ if (!aItemP->isBasedOn(ity_multifield)) return 415; // must be multifield item
+ TMultiFieldItem *myItemP = static_cast<TMultiFieldItem *>(aItemP);
+ // - assign localid
+ string localid;
+ LOCALID_TO_STRING(aLocalID,localid);
+ myItemP->setLocalID(localid.c_str());
+ // have API handle it
+ localstatus sta = apiUpdateItem(*myItemP);
+ if (sta==LOCERR_OK) {
+ // updated ok
+ // - save what is needed for finalisation
+ if (fNeedFinalisation) {
+ queueForFinalisation(myItemP);
+ }
+ }
+ return sta;
+} // TCustomImplDS::updateItemByID
+
+
+/// delete item by local ID in the sync set.
+/// @return != LOCERR_OK if item with specified ID is not found.
+localstatus TCustomImplDS::deleteItemByID(localid_t aLocalID)
+{
+ // create new dummy TMultiFieldItem
+ TMultiFieldItem *myItemP =
+ (TMultiFieldItem *) newItemForRemote(ity_multifield);
+ // assign localid
+ string localid;
+ LOCALID_TO_STRING(aLocalID,localid);
+ myItemP->setLocalID(localid.c_str());
+ // have API delete it
+ localstatus sta = apiDeleteItem(*myItemP);
+ delete myItemP; // delete dummy item
+ // return status
+ return sta;
+} // TCustomImplDS::deleteItemByID
+
+
+/// create new item in the sync set. Caller retains ownership of aItemP.
+/// @return LOCERR_OK or error code.
+/// @param[out] aNewLocalID local ID assigned to new item
+/// @param[out] aReceiveOnly is set to true if local changes/deletion of this item should not be
+/// reported to the server in normal syncs.
+localstatus TCustomImplDS::createItem(TSyncItem *aItemP,localid_out_t &aNewLocalID, bool &aReceiveOnly)
+{
+ if (!aItemP) return 510; // error
+ if (!aItemP->isBasedOn(ity_multifield)) return 415; // must be multifield item
+ TMultiFieldItem *myItemP = static_cast<TMultiFieldItem *>(aItemP);
+ // add it to the database
+ string newLocalID;
+ localstatus sta = apiAddItem(*myItemP,newLocalID);
+ // return assigned ID
+ STR_TO_LOCALID(newLocalID.c_str(),aNewLocalID);
+ if (sta==LOCERR_OK) {
+ // added ok
+ // - save what is needed for finalisation
+ if (fNeedFinalisation) {
+ myItemP->setLocalID(newLocalID.c_str()); // finalisation needs to know the local ID
+ queueForFinalisation(myItemP);
+ }
+ }
+ // so far, we don't have receive-only items
+ aReceiveOnly = false;
+ // return status
+ return sta;
+} // TCustomImplDS::createItem
+
+
+/// zaps the entire datastore, returns LOCERR_OK if ok
+/// @return LOCERR_OK or error code.
+localstatus TCustomImplDS::zapDatastore(void)
+{
+ // make sure we have the sync set if we need it to zap it
+ if (apiNeedSyncSetToZap()) {
+ // make sure we have the sync set
+ if (!makeSyncSetLoaded(false)) return 510; // error
+ }
+ // Zap the sync set in this datastore (will eventually call zapSyncSet)
+ return apiZapSyncSet();
+} // TCustomImplDS::zapDatastore
+
+
+/// get error code for last routine call that returned !=LOCERR_OK
+/// @return platform specific DB error code
+uInt32 TCustomImplDS::lastDBError(void)
+{
+ return 0; // none available
+} // TCustomImplDS::lastDBError
+
+
+#endif // BASED_ON_BINFILE_CLIENT
+
+/* end of TCustomImplDS implementation */
+
+} // namespace
+
+// eof
diff --git a/src/sysync/customimplds.h b/src/sysync/customimplds.h
new file mode 100755
index 0000000..9be6974
--- /dev/null
+++ b/src/sysync/customimplds.h
@@ -0,0 +1,768 @@
+/**
+ * @File customimpl.h
+ *
+ * @Author Lukas Zeller (luz@synthesis.ch)
+ *
+ * @brief TCustomImplDS
+ * Base class for customizable datastores (mainly extended DB mapping features
+ * common to all derived classes like ODBC, DBAPI etc.).
+ *
+ * Copyright (c) 2001-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ * @Date 2005-12-05 : luz : separated from odbcapids
+ */
+
+#ifndef CUSTOMIMPLDS_H
+#define CUSTOMIMPLDS_H
+
+
+// includes
+#include "stdlogicds.h"
+#include "multifielditemtype.h"
+#include "customimplagent.h"
+
+#ifdef BASED_ON_BINFILE_CLIENT
+ #include "binfileimplds.h"
+#endif
+
+using namespace sysync;
+
+namespace sysync {
+
+#ifdef SCRIPT_SUPPORT
+// publish as derivates might need it
+extern const TFuncTable CustomDSFuncTable2;
+#endif
+
+// the datastore config
+
+// field mapping base class
+class TFieldMapItem : public TConfigElement
+{
+ typedef TConfigElement inherited;
+public:
+ TFieldMapItem(const char *aElementName, TConfigElement *aParentElement);
+ virtual bool isArray(void) { return false; };
+ // - parser for extra attributes (for derived classes)
+ virtual void checkAttrs(const char **aAttributes) { /* nop in base class */ };
+ // - field mode, here in base class to simplify things
+ bool readable; // used to read (for SELECT)
+ bool writable; // used to write (for UPDATE,INSERT)
+ // - more detailed control
+ bool for_insert;
+ bool for_update;
+ // - map as parameter (rather than literally inserting field values in INSERT and UPDATE statements)
+ bool as_param;
+ // - map as floating time field (will be written as-is, no conversion from/to DB time zone takes place)
+ bool floating_ts;
+ // - if set, this field's value needs to be saved for finalisation run at the very end of the sync session
+ bool needs_finalisation;
+ // - set number (for having different sets in different statement parts)
+ uInt16 setNo;
+ // - name of the field in SQL statements is the name of the item
+ // - field ID in MultiFieldItem
+ sInt16 fid;
+ // - type
+ // Note: Fields that are separate date/time in the DB but one
+ // field in MultiFieldItem can be specified as dbft_date FIRST and
+ // then as dbft_timefordate in this list, causing time to be combined
+ // with date.
+ TDBFieldType dbfieldtype; // database field type
+ // - field size
+ uInt32 maxsize; // number of chars field can hold (if string or BLOB), 0=unlimited
+ bool notruncate; // set if this field should never be sent truncated by the remote
+}; // TFieldMapItem
+
+
+typedef std::list<TFieldMapItem *> TFieldMapList;
+
+
+#ifdef ARRAYDBTABLES_SUPPORT
+
+class TFieldMappings;
+class TCustomDSConfig;
+
+// special field map item: Array map
+class TFieldMapArrayItem : public TFieldMapItem
+{
+ typedef TFieldMapItem inherited;
+public:
+ TFieldMapArrayItem(TCustomDSConfig *aCustomDSConfigP, TConfigElement *aParentElement);
+ virtual ~TFieldMapArrayItem();
+ // properties
+ // - the map for the fields in the array. fid of maps are used as base fid
+ // if they do not reference array fields
+ TFieldMapList fArrayFieldMapList;
+ // - config of related datastore
+ TCustomDSConfig *fCustomDSConfigP;
+ // - base TFieldMappings %%% not used any more, we take it from fCustomDSConfigP
+ //TFieldMappings *fBaseFieldMappings;
+ // - referenced fieldlist %%% not used any more, we take it from fBaseFieldMappings
+ // TFieldListConfig *fFieldListP;
+ #ifdef OBJECT_FILTERING
+ // - filter expression:
+ // if filter evaluates as true, array is not written (or deleted at update)
+ // if array has no items on read, filter is applied to item with makepass()
+ string fNoItemsFilter;
+ #endif
+ #ifdef SCRIPT_SUPPORT
+ // array record's scripts
+ string fInitScript;
+ string fAfterReadScript;
+ string fBeforeWriteScript;
+ string fAfterWriteScript;
+ string fFinishScript;
+ #endif
+ // - repeating params
+ sInt16 fMaxRepeat; // if==0, unlimited repeats (for array fields only)
+ sInt16 fRepeatInc; // increment per repetition
+ bool fStoreEmpty; // if set, empty leaf fields will also be stored in the array
+ // Methods
+ virtual bool isArray(void) { return true; };
+ virtual void clear();
+protected:
+ // check config elements
+ virtual bool localStartElement(const char *aElementName, const char **aAttributes, sInt32 aLine);
+ virtual void localResolve(bool aLastPass);
+ #ifdef SCRIPT_SUPPORT
+ // process map scripts (resolve or rebuild them)
+ bool fScriptsResolved;
+ void ResolveArrayScripts(void);
+ void expectScriptUnresolved(string &aTScript,sInt32 aLine, const TFuncTable *aContextFuncs);
+ #endif
+}; // TFieldMapArrayItem
+
+#endif
+
+
+
+
+class TCustomDSConfig;
+
+class TFieldMappings: public TConfigElement
+{
+ typedef TConfigElement inherited;
+public:
+ TFieldMappings(const char* aName, TConfigElement *aParentElement);
+ virtual ~TFieldMappings();
+ // properties
+ // - the map itself
+ TFieldMapList fFieldMapList;
+ #ifdef SCRIPT_SUPPORT
+ // - script that evaluates the syncset options such as STARTDATE() and ENDDATE()
+ // and configures the DB such that the filtering takes place during fetch.
+ // Must return TRUE if DB level filter can perform requested options
+ string fOptionFilterScript;
+ // - main record's scripts
+ string fInitScript;
+ string fAfterReadScript;
+ string fBeforeWriteScript;
+ string fAfterWriteScript;
+ string fFinishScript;
+ string fFinalisationScript;
+ #endif
+ // - a reference to a field list
+ TFieldListConfig *fFieldListP;
+ virtual void clear();
+ #ifdef SCRIPT_SUPPORT
+ // processing of map scripts (resolve or rebuild them)
+ virtual const TFuncTable *getDSFuncTableP(void);
+ //%%% moved to fDSScriptsResolved: bool fScriptsResolved;
+ //%%% moved to ResolveDSScripts: void ResolveMapScripts(void);
+ #endif
+protected:
+ #ifdef SCRIPT_SUPPORT
+ void expectScriptUnresolved(string &aTScript,sInt32 aLine, const TFuncTable *aContextFuncs);
+ #endif
+ // check config elements
+ virtual bool localStartElement(const char *aElementName, const char **aAttributes, sInt32 aLine);
+ virtual void localResolve(bool aLastPass);
+}; // TFieldMappings
+
+
+
+class TCustomDSConfig:
+ #ifdef BASED_ON_BINFILE_CLIENT
+ public TBinfileDSConfig
+ #else
+ public TLocalDSConfig
+ #endif
+{
+ #ifdef BASED_ON_BINFILE_CLIENT
+ typedef TBinfileDSConfig inherited;
+ #else
+ typedef TLocalDSConfig inherited;
+ #endif
+public:
+ TCustomDSConfig(const char* aName, TConfigElement *aParentElement);
+ virtual ~TCustomDSConfig();
+ // properties
+ // - if set, multi-folder (datastore consisting of multiple folders, such as email)
+ // features are enabled for this datastore
+ bool fMultiFolderDB;
+ // - charset to be used in the data table
+ TCharSets fDataCharSet;
+ // - line end mode to be used in the data table for multiline data
+ TLineEndModes fDataLineEndMode;
+ // - if set, causes that data is read from DB first and then merged
+ // with updated fields. Not needed in normal DBs that can
+ // update a subset of all columns.
+ bool fUpdateAllFields;
+ // - Date/Time info
+ bool fDataIsUTC; //%%% legacy flag for compatibility, superseded by fDataTimeZone
+ timecontext_t fDataTimeZone; // time zone for storing/retrieving timestamps in DB
+ bool fUserZoneOutput; // if set, all non-floating timestamps are moved to user time zone (probably from datatimezone)
+ // - admin capability info
+ bool fStoreSyncIdentifiers; // if set, database separately stores "last sync with data sent to remote" and "last suspend" identifiers (stored as string, not necessarily a date)
+ #ifndef BASED_ON_BINFILE_CLIENT
+ bool fSyncTimeStampAtEnd; // if set, time point of sync is taken AFTER last write to DB (for single-user DBs like FMPro)
+ bool fOneWayFromRemoteSupported; // if set, database has a separate "last sync with data sent to remote" timestamp
+ bool fResumeSupport; // if set, admin tables have DS 1.2 support needed for resume (map entrytype, map flags, fResumeAlertCode, fLastSuspend, fLastSuspendIdentifier
+ bool fResumeItemSupport; // if set, admin tables have support for storing data to resume a partially transferred item
+ // - one-way support is always given for binfile based DS
+ virtual bool isOneWayFromRemoteSupported() { return fOneWayFromRemoteSupported; }
+ #endif // not BASED_ON_BINFILE_CLIENT
+ // - Database field to item field mappings
+ TFieldMappings fFieldMappings;
+ #ifdef SCRIPT_SUPPORT
+ // provided to allow derivates to add API specific script functions to scripts called from CustomImplDS
+ virtual const TFuncTable *getDSFuncTableP(void) { return &CustomDSFuncTable1; };
+ // - script called after admin data is loaded (before any data access takes place)
+ string fAdminReadyScript;
+ // - script called after sync with this datastore is ended (before writing the log)
+ string fSyncEndScript;
+ // - context for resolving scripts in datastore context
+ TScriptContext *fResolveContextP;
+ // - resolve DS scripts, this may be called before entire DS config is resolved to allow map's accessing local vars
+ bool fDSScriptsResolved;
+ void ResolveDSScripts(void);
+ virtual void apiResolveScripts(void) { /* nop here */ };
+ #endif
+ // factory functions for field map items
+ virtual TFieldMapItem *newFieldMapItem(const char *aElementName, TConfigElement *aParentElement)
+ { return new TFieldMapItem(aElementName,aParentElement); };
+ #ifdef ARRAYDBTABLES_SUPPORT
+ virtual TFieldMapArrayItem *newFieldMapArrayItem(TCustomDSConfig *aCustomDSConfig, TConfigElement *aParentElement)
+ { return new TFieldMapArrayItem(aCustomDSConfig,aParentElement); };
+ #endif
+protected:
+ // Add (probably datastore-specific) limits such as MaxSize and NoTruncate to types
+ virtual void addTypeLimits(TLocalEngineDS *aLocalDatastoreP, TSyncSession *aSessionP);
+ // check config elements
+ virtual bool localStartElement(const char *aElementName, const char **aAttributes, sInt32 aLine);
+ virtual void clear();
+ virtual void localResolve(bool aLastPass);
+}; // TCustomDSConfig
+
+
+#ifndef BASED_ON_BINFILE_CLIENT
+
+// getitem phase
+typedef enum {
+ gph_deleted,
+ gph_added_changed,
+ gph_done
+} TGetPhases;
+
+
+/// Map flags
+#define mapflag_useforresume 0x00000001 ///< set on map items that were marked for resume in the previous session
+#define mapflag_pendingMapStatus 0x00000002 ///< set on pending maps that have been sent, but not seen status yet
+#define mapflag_pendingStatus 0x00000004 ///< set on map items that have generated a add/update/move, but not seen status yet
+#define mapflag_pendingDeleteStatus 0x00000008 ///< set on map items that have generated a delete, but not seen status yet
+#define mapflag_pendingAddConfirm 0x00000010 ///< set on items that have generated a add op, and have not yet received full confirmation (=status for client, =map for server)
+#define mapflag_resend 0x00000020 ///< set on items that should be re-sent to remote in next session
+
+
+/// Map entry types
+typedef enum {
+ mapentry_invalid, ///< should not ever be present
+ mapentry_normal, ///< normal localID to remoteID map item
+ mapentry_tempidmap, ///< pseudo-map items that is not a local/remote maps, but saved local/adjustedLocal map (server only)
+ mapentry_pendingmap, ///< pseudo-map item that represents pending maps to be sent after resume (client only)
+ numMapEntryTypes
+} TMapEntryType;
+
+#ifdef SYDEBUG
+extern const char * const MapEntryTypeNames[];
+#endif
+
+/// local in-memory map entry
+/// @Note This mapentry can hold main maps, as well as tempGUID and pendingMap pseudo maps
+typedef struct {
+ TMapEntryType entrytype; ///< type of mapentry
+ string localid; ///< localID
+ string remoteid; ///< remoteID
+ uInt32 mapflags; ///< mapflag_xxxx
+ bool changed; ///< set for map items changed in this session
+ bool deleted; ///< set for map items deleted in this session
+ bool added; ///< set for map items added in this session
+ bool markforresume; ///< set for map items that must be saved with mapflag_useforresume
+ bool savedmark; ///< set for map items that are already saved with mapflag_useforresume (optimisation)
+} TMapEntry;
+
+
+// container for map entries
+typedef list<TMapEntry> TMapContainer;
+
+#endif // BASED_ON_BINFILE_CLIENT
+
+
+// local SyncSet entry
+typedef struct {
+ string localid;
+ string containerid; // for multi-folder DBs
+ bool isModified; // set if modified since last sync
+ bool isModifiedAfterSuspend; // set if modified since last suspend
+ // optional item contents (depends on implementation of ReadSyncSet())
+ TMultiFieldItem *itemP;
+} TSyncSetItem;
+
+
+// container for sync set information
+typedef list<TSyncSetItem *> TSyncSetList;
+
+// container for finalisation
+typedef list<TMultiFieldItem *> TMultiFieldItemList;
+
+
+class TCustomImplDS:
+ #ifdef BASED_ON_BINFILE_CLIENT
+ public TBinfileImplDS
+ #else
+ public TStdLogicDS
+ #endif
+{
+ #ifdef BASED_ON_BINFILE_CLIENT
+ typedef TBinfileImplDS inherited;
+ #else
+ typedef TStdLogicDS inherited;
+ #endif
+ friend class TCustomDSfuncs;
+ friend class TCustomCommonFuncs;
+private:
+ void InternalResetDataStore(void); // reset for re-use without re-creation
+protected:
+ /// @name dsSavedAdmin administrative data (anchors, timestamps, maps) as saved or to-be-saved
+ /// @Note These will be loaded and saved be derived classes
+ /// @Note Some of these will be updated from resp. @ref dsCurrentAdmin members at distinct events (suspend, session end, etc.)
+ /// @Note Some of these will be updated during the session, but in a way that does NOT affect the anchoring of current/last session
+ //
+ /// @{
+ /// Reference time of previous sync which sent data to remote to compare modification dates against
+ /// @note normally==fPreviousSyncTime, but can be end of sync for datastore that can't write modified timestamps at will
+ #ifndef BASED_ON_BINFILE_CLIENT
+ /// @note for BASED_ON_BINFILE_CLIENT case, these already exist at the binfile level, so we MUST NOT have them here again!!
+ lineartime_t fPreviousToRemoteSyncCmpRef;
+ /// Reference string used by database API level to determine modifications since last to-remote-sync
+ string fPreviousToRemoteSyncIdentifier;
+ /// Reference time of last suspend, needed to detect modifications that took place between last suspend and current resume
+ lineartime_t fPreviousSuspendCmpRef;
+ /// Reference string used by database API level to detect modifications that took place between last suspend and current resume
+ string fPreviousSuspendIdentifier;
+ /// @}
+
+ /// @name dsCurrentAdmin current session's admin data (anchors, timestamps, maps)
+ /// @Note These will be copied to @ref dsSavedAdmin members ONLY when a session completes successfully/suspends.
+ /// @Note Admin data is NEVER directly saved or loaded from these
+ /// @Note Derivates will update some of these at dssta_adminready with current time/anchor values
+ //
+ /// @{
+ /// Reference time of current sync to compare modification dates against
+ /// @note initially==fCurrentSyncTime, but might be set to end-of-session time for databases which cannot explicitly set modification timestamps
+ lineartime_t fCurrentSyncCmpRef;
+ /// Reference string returned by database API level identifying this session's time (for detecting changes taking place after this session)
+ string fCurrentSyncIdentifier;
+ /// @}
+ #endif
+
+public:
+ TCustomImplDS(
+ TCustomDSConfig *aConfigP,
+ sysync::TSyncSession *aSessionP,
+ const char *aName,
+ uInt32 aCommonSyncCapMask=0);
+ virtual void announceAgentDestruction(void);
+ virtual void dsResetDataStore(void) { InternalResetDataStore(); inherited::dsResetDataStore(); };
+ virtual ~TCustomImplDS();
+
+ /// @name apiXXXX methods defining the interface from TCustomImplDS to TXXXApi actual API implementations
+ /// @{
+ //
+
+ #ifndef BASED_ON_BINFILE_CLIENT
+ /// @brief Load admin data from ODBC database
+ /// @param aDeviceID[in] remote device URI (device ID)
+ /// @param aDatabaseID[in] local database ID
+ /// @param aRemoteDBID[in] database ID of remote device
+ /// Must search for existing target record matching the triple (aDeviceID,aDatabaseID,aRemoteDBID)
+ /// - if there is a matching record: load it
+ /// - if there is no matching record, set fFirstTimeSync=true. The implementation may already create a
+ /// new record with the key (aDeviceID,aDatabaseID,aRemoteDBID) and initialize it with the data from
+ /// the items as shown below. At least, fTargetKey must be set to a value that will allow apiSaveAdminData to
+ /// update the record. In case implementation chooses not create the record only in apiSaveAdminData, it must
+ /// buffer the triple (aDeviceID,aDatabaseID,aRemoteDBID) such that it is available at apiSaveAdminData.
+ /// If a record exists implementation must load the following items:
+ /// - fTargetKey = some key value that can be used to re-identify the target record later at SaveAdminData.
+ /// If the database implementation has other means to re-identify the target, this can be
+ /// left unassigned.
+ /// - fLastRemoteAnchor = anchor string used by remote party for last session (and saved to DB then)
+ /// - fPreviousSyncTime = anchor (beginning of session) timestamp of last session.
+ /// - fPreviousToRemoteSyncCmpRef = Reference time to determine items modified since last time sending data to remote
+ /// - fPreviousToRemoteSyncIdentifier = string identifying last session that sent data to remote (needs only be saved
+ /// if derived datastore cannot work with timestamps and has its own identifier).
+ /// - fMapTable = list<TMapEntry> containing map entries. The implementation must load all map entries
+ /// related to the current sync target identified by the triple of (aDeviceID,aDatabaseID,aRemoteDBID)
+ /// or by fTargetKey. The entries added to fMapTable must have "changed", "added" and "deleted" flags
+ /// set to false.
+ /// For resumable datastores:
+ /// - fMapTable = In addition to the above, the markforresume flag must be saved in the mapflags
+ // when it is not equal to the savedmark flag - independently of added/deleted/changed.
+ /// - fResumeAlertCode = alert code of current suspend state, 0 if none
+ /// - fPreviousSuspendCmpRef = reference time of last suspend (used to detect items modified during a suspend / resume)
+ /// - fPreviousSuspendIdentifier = identifier of last suspend (used to detect items modified during a suspend / resume)
+ /// (needs only be saved if derived datastore cannot work with timestamps and has
+ /// its own identifier)
+ /// - fPendingAddMaps = map<string,string>. The implementation must load all all pending maps (client only) into
+ /// fPendingAddMaps (and fUnconfirmedMaps must be left empty).
+ /// - fTempGUIDMap = map<string,string>. The implementation must save all entries as temporary LUID to GUID mappings
+ /// (server only)
+ virtual localstatus apiLoadAdminData(
+ const char *aDeviceID, // remote device URI (device ID)
+ const char *aDatabaseID, // database ID
+ const char *aRemoteDBID // database ID of remote device
+ ) = 0;
+ /// @brief Save admin data to ODBC database
+ /// @param[in] aSessionFinished if true, this is a end-of-session save (and not only a suspend save) - but not necessarily a successful one
+ /// @param[in] aSuccessful if true, this is a successful end-of-session
+ /// Must save to the target record addressed at LoadAdminData() by the triple (aDeviceID,aDatabaseID,aRemoteDBID)
+ /// Implementation must save the following items:
+ /// - fLastRemoteAnchor = anchor string used by remote party for this session (and saved to DB then)
+ /// - fPreviousSyncTime = anchor (beginning of session) timestamp of this session.
+ /// - fPreviousToRemoteSyncCmpRef = Reference time to determine items modified since last time sending data to remote
+ /// - fPreviousToRemoteSyncIdentifier = string identifying last session that sent data to remote (needs only be saved
+ /// if derived datastore cannot work with timestamps and has its own identifier).
+ /// - fMapTable = list<TMapEntry> containing map entries. The implementation must save all map entries
+ /// that have changed, are new or are deleted. See below for additional resume requirements.
+ /// For resumable datastores:
+ /// - fMapTable = In addition to the above, the markforresume flag must be saved in the mapflags
+ // when it is not equal to the savedmark flag - independently of added/deleted/changed.
+ /// - fResumeAlertCode = alert code of current suspend state, 0 if none
+ /// - fPreviousSuspendCmpRef = reference time of last suspend (used to detect items modified during a suspend / resume)
+ /// - fPreviousSuspendIdentifier = identifier of last suspend (used to detect items modified during a suspend / resume)
+ /// (needs only be saved if derived datastore cannot work with timestamps and has
+ /// its own identifier)
+ /// - fPendingAddMaps and fUnconfirmedMaps = map<string,string>. The implementation must save all entries as
+ /// pending maps (client only). localIDs might be temporary, so call dsFinalizeLocalID() to
+ /// ensure these are final.
+ /// - fTempGUIDMap = map<string,string>. The implementation must save all entries as temporary LUID to GUID mappings
+ /// (server only)
+ virtual localstatus apiSaveAdminData(bool aSessionFinished, bool aSuccessful) = 0;
+ /* %%% luz 2008-04-01: no, this is NOT needed
+ #else // not BASED_ON_BINFILE_CLIENT
+ // when based on binfile, we need this apiEndDataWrite signature as glue
+ virtual localstatus apiEndDataWrite(void);
+ */
+ #endif // BASED_ON_BINFILE_CLIENT
+
+ /// read sync set IDs and mod dates.
+ /// @param[in] if set, all data fields are needed, so ReadSyncSet MAY
+ /// read items here already. Note that ReadSyncSet MAY read items here
+ /// even if aNeedAll is not set (if it is more efficient than reading
+ /// them separately afterwards).
+ virtual localstatus apiReadSyncSet(bool aNeedAll) = 0;
+ /// Zap all data in syncset (note that everything outside the sync set will remain intact)
+ virtual localstatus apiZapSyncSet(void) = 0;
+ virtual bool apiNeedSyncSetToZap(void) = 0; // derivate must define this so we can prepare the sync set before zapping if needed
+ /// fetch record contents from DB by localID.
+ virtual localstatus apiFetchItem(TMultiFieldItem &aItem, bool aReadPhase, TSyncSetItem *aSyncSetItemP) = 0;
+ /// add new item to datastore, returns created localID
+ virtual localstatus apiAddItem(TMultiFieldItem &aItem, string &aLocalID) = 0;
+ /// update existing item in datastore, returns 404 if item not found
+ virtual localstatus apiUpdateItem(TMultiFieldItem &aItem) = 0;
+ /// delete existing item in datastore, returns 211 if not existing any more
+ virtual localstatus apiDeleteItem(TMultiFieldItem &aItem) = 0;
+ /// end of syncset reading phase
+ virtual localstatus apiEndDataRead(void) = 0;
+ /// start of write
+ virtual localstatus apiStartDataWrite(void) = 0;
+ /// end DB data write sequence (but not yet admin data)
+ virtual localstatus apiEndDataWrite(string &aThisSyncIdentifier) = 0;
+
+ /// @}
+
+
+public:
+ /// @name dsXXXX virtuals defined by TLocalEngineDS
+ /// These are usually designed such that they should always call inherited::dsXXX to let the entire chain
+ /// of ancestors see the calls
+ /// @{
+ //
+ /// end of message handling
+ virtual void dsEndOfMessage(void);
+ /// inform logic of coming state change
+ virtual localstatus dsBeforeStateChange(TLocalEngineDSState aOldState,TLocalEngineDSState aNewState);
+ /// inform logic of happened state change
+ virtual localstatus dsAfterStateChange(TLocalEngineDSState aOldState,TLocalEngineDSState aNewState);
+ #ifndef BASED_ON_BINFILE_CLIENT
+ /// called to confirm a sync operation's completion (ok status from remote received)
+ /// @note aSyncOp passed not necessarily reflects what was sent to remote, but what actually happened
+ virtual void dsConfirmItemOp(TSyncOperation aSyncOp, cAppCharP aLocalID, cAppCharP aRemoteID, bool aSuccess, localstatus aErrorStatus=0);
+ #endif
+
+ /// @}
+
+
+ /// @name dsHelpers private/protected helper routines
+ /// @{
+ //
+private:
+ /// private helper to prepare for apiSaveAdminData()
+ localstatus SaveAdminData(bool aSessionFinished, bool aSuccessful);
+ /// @}
+
+ // agent
+ TCustomImplAgent *fAgentP; // access to agent (casted fSessionP for convenience)
+ // config (typed pointers for convenience)
+ TCustomDSConfig *fConfigP;
+ TCustomAgentConfig *fAgentConfigP;
+
+protected:
+ // some vars
+ sInt16 fArrIdx;
+ #ifdef SCRIPT_SUPPORT
+ // - temp vars while running scripts
+ string fParentKey;
+ bool fWriting;
+ bool fInserting;
+ bool fDeleting;
+ #endif
+ #ifdef ARRAYDBTABLES_SUPPORT
+ bool fHasArrayFields; // set if array table access needed
+ #endif
+ bool fNeedFinalisation; // set if fields which need finalisation exist in field mappings
+ // script context
+ #ifdef SCRIPT_SUPPORT
+ TScriptContext *fScriptContextP;
+ virtual void apiRebuildScriptContexts(void) { /* nop here */ };
+ #endif
+ // Simple custom DB access interface methods
+ // - returns true if database implementation can only update all fields of a record at once
+ virtual bool dsReplaceWritesAllDBFields(void);
+ #ifndef BASED_ON_BINFILE_CLIENT
+ // - returns true if DB implementation supports resume (saving of resume marks, alert code, pending maps, tempGUIDs)
+ virtual bool dsResumeSupportedInDB(void) { return fConfigP && fConfigP->fResumeSupport; };
+ /// returns true if DB implementation supports resuming in midst of a chunked item (can save fPIxxx.. and related admin data)
+ virtual bool dsResumeChunkedSupportedInDB(void) { return fConfigP && fConfigP->fResumeItemSupport; };
+ #endif // not BASED_ON_BINFILE_CLIENT
+ #ifdef OBJECT_FILTERING
+ // - returns true if DB implementation can also apply special filters like CGI-options
+ // /dr(x,y) etc. during fetching
+ virtual bool dsOptionFilterFetchesFromDB(void);
+ #endif
+
+ /// @name implXXX methods used when based on StdLogicDS
+ /// @{
+
+ /// @brief sync login (into this database)
+ /// @note also exists in BASED_ON_BINFILE_CLIENT, will do the job together with
+ /// inhertited binfile version (instead of using DB api)
+ /// @note might be called several times (auth retries at beginning of session)
+ /// @note must update the following state variables
+ /// - in TLocalEngineDS: fLastRemoteAnchor, fLastLocalAnchor, fResumeAlertCode, fFirstTimeSync
+ /// - for client: fPendingAddMaps
+ /// - for server: fTempGUIDMap
+ /// - in TStdLogicDS: fPreviousSyncTime, fCurrentSyncTime
+ /// - in derived classes: whatever else belongs to dsSavedAdmin and dsCurrentAdmin state
+ /// (for example fTargetKey, fFolderKey)
+ virtual localstatus implMakeAdminReady(
+ const char *aDeviceID, ///< remote device URI (device ID)
+ const char *aDatabaseID, ///< database ID
+ const char *aRemoteDBID ///< database ID of remote device
+ );
+ /// save end of session state
+ /// @note also exists in BASED_ON_BINFILE_CLIENT, will do the job together with
+ /// inhertited binfile version (instead of using DB api)
+ virtual localstatus implSaveEndOfSession(bool aUpdateAnchors);
+ /// start data read
+ /// @note: fSlowSync and fRefreshOnly must be valid before calling this method
+ virtual localstatus implStartDataRead();
+ /// end of read
+ /// @note also exists in BASED_ON_BINFILE_CLIENT, will do the job together with
+ /// inhertited binfile version.
+ virtual localstatus implEndDataRead(void);
+ /// start of write
+ /// @note also exists in BASED_ON_BINFILE_CLIENT, will do the job together with
+ /// inhertited binfile version.
+ virtual localstatus implStartDataWrite(void);
+ /// end write sequence
+ /// @note also exists in BASED_ON_BINFILE_CLIENT, will do the job together with
+ /// inhertited binfile version.
+ virtual bool implEndDataWrite(void);
+
+ #ifndef BASED_ON_BINFILE_CLIENT
+
+ /// when based on binfile client, we don't need the syncset loaded to be able to retrieve items
+ bool implNeedSyncSetToRetrieve(void) { return false; };
+ /// get item from DB
+ virtual localstatus implGetItem(
+ bool &aEof,
+ bool &aChanged,
+ TSyncItem* &aSyncItemP
+ );
+ /// review reported entry (allows post-processing such as map deleting)
+ /// MUST be called after implStartDataWrite, before any actual writing,
+ /// for each item obtained in implGetItem
+ virtual localstatus implReviewReadItem(
+ TSyncItem &aItem // the item
+ );
+ /// called to set maps.
+ /// @note aLocalID or aRemoteID can be NULL - which signifies deletion of a map entry
+ /// @note that this might be needed for clients accessing a server-style database as well
+ virtual localstatus implProcessMap(cAppCharP aRemoteID, cAppCharP aLocalID);
+ /// retrieve specified item from database
+ virtual bool implRetrieveItemByID(
+ TSyncItem &aItem, // the item
+ TStatusCommand &aStatusCommand
+ );
+ /// process item (according to operation: add/delete/replace - and for future: copy/move)
+ virtual bool implProcessItem(
+ TSyncItem *aItemP, // the item
+ TStatusCommand &aStatusCommand
+ );
+ /// called to mark an already generated (but probably not sent or not yet statused) item
+ /// as "to-be-resumed", by localID or remoteID (latter only in server case).
+ /// @note This must be repeatable without side effects, as server must mark/save suspend state
+ /// after every request (and not just at end of session)
+ virtual void implMarkItemForResume(cAppCharP aLocalID, cAppCharP aRemoteID, bool aUnSent);
+ /// called to mark an already sent item as "to-be-resent", e.g. due to temporary
+ /// error status conditions, by localID or remoteID (latter only in server case).
+ virtual void implMarkItemForResend(cAppCharP aLocalID, cAppCharP aRemoteID);
+ /// called to have all non-yet-generated sync commands as "to-be-resumed"
+ virtual void implMarkOnlyUngeneratedForResume(void);
+ /// save status information required to eventually perform a resume (as passed to datastore with
+ /// markOnlyUngeneratedForResume() and markItemForResume())
+ /// (or, in case the session is really complete, make sure that no resume state is left)
+ virtual localstatus implSaveResumeMarks(void);
+
+ /// @}
+ #else // not BASED_ON_BINFILE_CLIENT
+ /// @name methods used when based on BinfileImplDS
+ /// @{
+
+ #ifndef CHANGEDETECTION_AVAILABLE
+ #error "CustomImplDS can be built only on BinFileImplDS with CHANGEDETECTION_AVAILABLE"
+ #endif
+ /// when based on binfile client, we need the syncset loaded to be able to retrieve items
+ bool implNeedSyncSetToRetrieve(void) { return true; };
+ /// get first item's ID and modification status from the sync set
+ /// @return false if no item found
+ virtual bool getFirstItemInfo(localid_out_t &aLocalID, bool &aItemHasChanged);
+ /// get next item's ID and modification status from the sync set.
+ /// @return false if no item found
+ virtual bool getNextItemInfo(localid_out_t &aLocalID, bool &aItemHasChanged);
+ /// get item by local ID from the sync set. Caller obtains ownership if aItemP is not NULL after return
+ /// @return != LOCERR_OK if item with specified ID is not found.
+ virtual localstatus getItemByID(localid_t aLocalID, TSyncItem *&aItemP);
+ /* no need to implement these here, calling API level directly from binfile is enough
+ /// signal start of data write phase
+ virtual localstatus apiStartDataWrite(void);
+ /// signal end of data write phase
+ virtual localstatus apiEndDataWrite(void);
+ */
+ /// update item by local ID in the sync set. Caller retains ownership of aItemP
+ /// @return != LOCERR_OK if item with specified ID is not found.
+ virtual localstatus updateItemByID(localid_t aLocalID, TSyncItem *aItemP);
+ /// delete item by local ID in the sync set.
+ /// @return != LOCERR_OK if item with specified ID is not found.
+ virtual localstatus deleteItemByID(localid_t aLocalID);
+ /// create new item in the sync set. Caller retains ownership of aItemP.
+ /// @return LOCERR_OK or error code.
+ /// @param[out] aNewLocalID local ID assigned to new item
+ /// @param[out] aReceiveOnly is set to true if local changes/deletion of this item should not be
+ /// reported to the server in normal syncs.
+ virtual localstatus createItem(TSyncItem *aItemP,localid_out_t &aNewLocalID, bool &aReceiveOnly);
+ /// zaps the entire datastore, returns LOCERR_OK if ok
+ /// @return LOCERR_OK or error code.
+ virtual localstatus zapDatastore(void);
+ /// get error code for last routine call that returned !=LOCERR_OK
+ /// @return platform specific DB error code
+ virtual uInt32 lastDBError(void);
+
+ /// @}
+ #endif // BASED_ON_BINFILE_CLIENT
+
+protected:
+ // - helper for getting a base field pointer (not resolved down to array element)
+ TItemField *getMappedBaseFieldOrVar(TMultiFieldItem &aItem, sInt16 aFid);
+ // - helper for getting a field pointer (local script var or item's field)
+ TItemField *getMappedFieldOrVar(TMultiFieldItem &aItem, sInt16 aFid, sInt16 aRepOffset, bool aExistingOnly=false);
+ // In-memory map and syncset access
+ // - mark all map entries as deleted
+ bool deleteAllMaps(void);
+ // - delete syncset
+ void DeleteSyncSet(bool aContentsOnly=false);
+ // - get container ID for specified localid
+ bool getContainerID(const char *aLocalID, string &aContainerID);
+ // - delete sync set one by one
+ localstatus zapSyncSet(void);
+ // - Queue the data needed for finalisation (usually - relational link updates)
+ // as a item copy with only finalisation-required fields
+ void queueForFinalisation(TMultiFieldItem *aItemP);
+public:
+ // - get last to-remote sync time
+ lineartime_t getPreviousToRemoteSyncCmpRef(void) { return fPreviousToRemoteSyncCmpRef; };
+ lineartime_t getPreviousSuspendCmpRef(void) { return fPreviousSuspendCmpRef; };
+ // - get syncset list
+ TSyncSetList *getSyncSetList(void) { return &fSyncSetList; };
+ // - find entry in sync set by localid
+ TSyncSetList::iterator findInSyncSet(const char *aLocalID);
+protected:
+ #ifndef BASED_ON_BINFILE_CLIENT
+ // - find non-deleted map entry by local ID / entry type
+ TMapContainer::iterator findMapByLocalID(const char *aLocalID,TMapEntryType aEntryType, bool aDeletedAsWell=false);
+ // - find map entry by remote ID
+ TMapContainer::iterator findMapByRemoteID(const char *aRemoteID);
+ // - modify map, if remoteID or localID is NULL or empty, map item will be deleted (if it exists at all)
+ void modifyMap(TMapEntryType aEntryType, const char *aLocalID, const char *aRemoteID, uInt32 aMapFlags, bool aDelete, uInt32 aClearFlags=0xFFFFFFFF);
+ #endif // not BASED_ON_BINFILE_CLIENT
+ #ifndef SYSYNC_CLIENT
+ // - called when a item in the sync set changes its localID (due to local DB internals)
+ // Datastore must make sure that eventually cached items get updated
+ virtual void dsLocalIdHasChanged(const char *aOldID, const char *aNewID);
+ #endif
+ // - target key (if needed by descendant)
+ string fTargetKey;
+ // - folder key (key value for subselecting in datastore, determined at implMakeAdminReady())
+ string fFolderKey;
+ // local list of local IDs/mod timestamps of current sync set for speedup and avoiding LEFT OUTER JOIN
+ TSyncSetList fSyncSetList;
+ // - iterator for reporting new and added items in GetItem
+ TSyncSetList::iterator fSyncSetPos;
+ // - list of items that must be processed in finalisation at end of sync
+ TMultiFieldItemList fFinalisationQueue;
+ #ifndef BASED_ON_BINFILE_CLIENT
+ // local map list
+ TMapContainer fMapTable;
+ // - iterator for reporting deleted items in GetItem
+ TMapContainer::iterator fDeleteMapPos;
+ bool fReportDeleted;
+ TGetPhases fGetPhase; // phase of get
+ bool fGetPhasePrepared; // set if phase is prepared (select or list iterator init)
+ #else // not BASED_ON_BINFILE_CLIENT
+ bool fSyncSetLoaded; // set if sync set is currently loaded
+ bool makeSyncSetLoaded(bool aNeedAll);
+ #endif // BASED_ON_BINFILE_CLIENT
+ bool fNoSingleItemRead; // if set, syncset list will also contain items
+ bool fMultiFolderDB; // if set, we need the syncset list for finding container IDs later
+ #ifdef SCRIPT_SUPPORT
+ bool fOptionFilterTested;
+ bool fOptionFilterWorksOnDBLevel; // set if option filters can be executed by DB
+ #endif
+}; // TCustomImplDS
+
+} // namespace sysync
+
+#endif // CUSTOMIMPLDS_H
+
+// eof
diff --git a/src/sysync/dataconversion.cpp b/src/sysync/dataconversion.cpp
new file mode 100644
index 0000000..028d76e
--- /dev/null
+++ b/src/sysync/dataconversion.cpp
@@ -0,0 +1,148 @@
+#include <dataconversion.h>
+#include <syncsession.h>
+#include <multifielditemtype.h>
+
+using namespace sysync;
+
+namespace sysync {
+
+class TDummyLocalDSConfig : public TLocalDSConfig {
+public:
+ TDummyLocalDSConfig(TConfigElement *element) :
+ TLocalDSConfig("dummy", element)
+ {}
+ virtual TLocalEngineDS* newLocalDataStore(sysync::TSyncSession*) { return NULL; }
+};
+class TDummyTLocalEngineDS : public TLocalEngineDS {
+public:
+ TDummyTLocalEngineDS(TLocalDSConfig *config, TSyncSession *session) :
+ TLocalEngineDS(config, session, "dummy")
+ {}
+ virtual bool logicProcessRemoteItem(TSyncItem*, TStatusCommand&, bool&, std::string*) { return false; }
+ virtual bool logicRetrieveItemByID(TSyncItem&, TStatusCommand&) { return false; }
+ virtual bool logicGenerateSyncCommandsAsClient(TSmlCommandPContainer&, TSmlCommand*&, const char*) { return false; }
+ virtual void logicMarkOnlyUngeneratedForResume() {}
+ virtual void logicMarkItemForResume(const char*, const char*, bool) {}
+ virtual void logicMarkItemForResend(const char*, const char*) {}
+};
+
+bool DataConversion(SessionH aSession,
+ const char *aFromTypeName,
+ const char *aToTypeName,
+ std::string &aItemData)
+{
+ bool res = false;
+ TProfileHandler *fromProfile = NULL;
+ TProfileHandler *toProfile = NULL;
+ TSyncItemType *fromItemType = NULL;
+ TSyncItemType *toItemType = NULL;
+ TSyncItem *fromItem = NULL;
+ TSyncItem *toItem = NULL;
+ SmlItemPtr_t smlitem = NULL;
+ TSyncSession *session = reinterpret_cast<TSyncSession *>(aSession);
+ TConfigElement dummyElement("dummy", NULL);
+ class TDummyLocalDSConfig dummyConfig(&dummyElement);
+ class TDummyTLocalEngineDS dummy(&dummyConfig, session);
+
+ // Temporarily disable per-session workarounds for our peer's
+ // time handling. Eventually we want to make this configurable
+ // per function call, not per session, but for that more
+ // code restructuring is necessary.
+ bool oldRemoteCanHandleUTC = session->fRemoteCanHandleUTC;
+ session->fRemoteCanHandleUTC = true;
+ timecontext_t oldUserTimeContext = session->fUserTimeContext;
+ session->fUserTimeContext = TCTX_UNKNOWN;
+
+ try {
+ TRootConfig *config = session->getRootConfig();
+ TDatatypesConfig *types = config->fDatatypesConfigP;
+ TDataTypeConfig *fromTypeConfig = types->getDataType(aFromTypeName);
+ TDataTypeConfig *toTypeConfig = types->getDataType(aToTypeName);
+ TStatusCommand statusCmd(session);
+
+ if (!fromTypeConfig) {
+ LOGDEBUGPRINTFX(session->getDbgLogger(), DBG_PARSE, ("%s: unknown type", aFromTypeName));
+ goto done;
+ }
+ if (!toTypeConfig) {
+ LOGDEBUGPRINTFX(session->getDbgLogger(), DBG_PARSE, ("%s: unknown type", aToTypeName));
+ goto done;
+ }
+
+ fromItemType = fromTypeConfig->newSyncItemType(session, NULL);
+ toItemType = toTypeConfig->newSyncItemType(session, NULL);
+
+ if (!fromItemType || !toItemType) {
+ LOGDEBUGPRINTFX(session->getDbgLogger(), DBG_PARSE, ("could not create item types"));
+ goto done;
+ }
+
+ // parse data and create item from it
+ smlitem = newItem();
+ if (!smlitem) {
+ LOGDEBUGPRINTFX(session->getDbgLogger(), DBG_PARSE, ("SML item allocation failed"));
+ goto done;
+ }
+ smlitem->data = newPCDataStringX(reinterpret_cast<const uInt8 *>(aItemData.c_str()), true, aItemData.size());
+ if (!smlitem->data) {
+ LOGDEBUGPRINTFX(session->getDbgLogger(), DBG_PARSE, ("SML data allocation failed"));
+ goto done;
+ }
+ fromItem = fromItemType->newSyncItem(smlitem,
+ sop_replace,
+ fmt_chr,
+ fromItemType,
+ &dummy,
+ statusCmd);
+ smlFreeItemPtr(smlitem);
+ smlitem = NULL;
+ if (!fromItem) {
+ LOGDEBUGPRINTFX(session->getDbgLogger(), DBG_PARSE, ("could not parse data as %s", aFromTypeName));
+ goto done;
+ }
+
+ // create item of target format, fill it with the parsed data, then encode
+ toItem = toItemType->newSyncItem(toItemType, &dummy);
+ if (!toItem) {
+ LOGDEBUGPRINTFX(session->getDbgLogger(), DBG_PARSE, ("could not allocate item"));
+ goto done;
+ }
+ if (!toItem->replaceDataFrom(*fromItem)) {
+ LOGDEBUGPRINTFX(session->getDbgLogger(), DBG_PARSE, ("%s and %s are not compatible", aFromTypeName, aToTypeName));
+ goto done;
+ }
+ smlitem = toItemType->newSmlItem(toItem, &dummy);
+ if (!smlitem) {
+ LOGDEBUGPRINTFX(session->getDbgLogger(), DBG_PARSE, ("could not encode data as %s", aToTypeName));
+ goto done;
+ }
+ stringSize size;
+ const char *data = smlPCDataToCharP(smlitem->data, &size);
+ if (!data && size) {
+ LOGDEBUGPRINTFX(session->getDbgLogger(), DBG_PARSE, ("string allocation failed"));
+ goto done;
+ }
+
+ aItemData.assign(data, size);
+ res = true;
+ } catch(...) {
+ // TODO: error logging
+ res = false;
+ }
+
+ done:
+ if (smlitem)
+ smlFreeItemPtr(smlitem);
+ delete fromItem;
+ delete toItem;
+ delete fromProfile;
+ delete toProfile;
+ delete fromItemType;
+ delete toItemType;
+ session->fRemoteCanHandleUTC = oldRemoteCanHandleUTC;
+ session->fUserTimeContext = oldUserTimeContext;
+
+ return res;
+}
+
+} // namespace sysync
diff --git a/src/sysync/dataobjtype.cpp b/src/sysync/dataobjtype.cpp
new file mode 100755
index 0000000..b21e0ce
--- /dev/null
+++ b/src/sysync/dataobjtype.cpp
@@ -0,0 +1,972 @@
+/*
+ * File: DataObjType.cpp
+ *
+ * Author: Beat Forster (bfo@synthesis.ch)
+ *
+ * TDataObjType
+ * base class for data object based items (EMAILOBJ, FILEOBJ, FOLDEROBJ)
+ * implemented for OMA DS V1.2
+ *
+ * Copyright (c) 2005-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ * 2005-07-20 : bfo : created from TextItemType
+ *
+ */
+
+// includes
+#include "prefix_file.h"
+
+#include "sysync.h"
+#include "dataobjtype.h"
+
+
+using namespace sysync;
+
+
+const char* BeginCDATA= "<![CDATA[";
+const char* EndCDATA= "]]>";
+const char* LowerCDATA= "]]]]>&gt;<![CDATA["; // only for the xml representation
+const char* DoubleCEnd= "]]]]";
+
+
+// Config
+// ======
+TTagMapDefinition::TTagMapDefinition(TConfigElement *aParentElementP, sInt16 aFid) :
+ TConfigElement("lm",aParentElementP)
+{
+ fFid= aFid; // save field ID
+ clear(); // init others
+} // TTagMapDefinition::TTagMapDefinition
+
+
+
+void TTagMapDefinition::clear(void)
+{
+ // clear
+ TCFG_CLEAR(fXmlTag);
+ TCFG_CLEAR(fXmlAttr);
+ fBoolType= false;
+ fEmbedded= false;
+ TCFG_CLEAR(fParent);
+ #ifdef OBJECT_FILTERING
+ TCFG_CLEAR(fFilterKeyword);
+ #endif
+} // TTagMapDefinition::clear
+
+
+
+#ifdef CONFIGURABLE_TYPE_SUPPORT
+// server config element parsing
+bool TTagMapDefinition::localStartElement(const char *aElementName, const char **aAttributes, sInt32 aLine)
+{
+ // checking the elements
+ if (strucmp(aElementName,"xmltag" )==0) expectString(fXmlTag); // the tag's name
+ else if (strucmp(aElementName,"xmlattr" )==0) expectString(fXmlAttr); // and its (optional) attribute(s)
+ else if (strucmp(aElementName,"booltype" )==0) expectBool (fBoolType); // a boolean field
+ else if (strucmp(aElementName,"embeddeditem" )==0) expectBool (fEmbedded); // an embedded item
+ else if (strucmp(aElementName,"parent" )==0) expectString(fParent); // the parent's tag name
+ #ifdef OBJECT_FILTERING
+ else if (strucmp(aElementName,"filterkeyword")==0) expectString(fFilterKeyword); // the parent's tag name
+ #endif
+
+ // - none known here
+ else return TConfigElement::localStartElement(aElementName,aAttributes,aLine);
+
+ // ok
+ return true;
+} // TTagMapDefinition::localStartElement
+#endif
+
+
+TTagMapDefinition::~TTagMapDefinition() {
+ // nop so far
+} // TTagMapDefinition::~TTagMapDefinition
+
+
+
+// -------------------------------------------------------------------------------------------
+// XML based datatype config
+TDataObjConfig::TDataObjConfig(const char* aName, TConfigElement *aParentElement) :
+ TMultiFieldTypeConfig(aName,aParentElement)
+{
+ clear();
+} // TDataObjConfig::TDataObjConfig
+
+
+TDataObjConfig::~TDataObjConfig()
+{
+ // make sure everything is deleted (was missing long time and caused mem leaks!)
+ clear();
+} // TDataObjConfig::~TDataObjConfig
+
+
+// init defaults
+void TDataObjConfig::clear(void)
+{
+ // clear properties
+ // - remove tag maps
+ TTagMapList::iterator pos;
+ for (pos= fTagMaps.begin();
+ pos!=fTagMaps.end(); pos++) delete *pos;
+
+ fTagMaps.clear();
+ // clear inherited
+ inherited::clear();
+} // TDataObjConfig::clear
+
+
+// create Sync Item Type of appropriate type from config
+TSyncItemType *TDataObjConfig::newSyncItemType(TSyncSession *aSessionP, TSyncDataStore *aDatastoreP)
+{
+ if (!fFieldListP)
+ SYSYNC_THROW(TSyncException(DEBUGTEXT("TDataObjConfig::newSyncItemType: no fFieldListP","txit1")));
+ return
+ new TDataObjType(
+ aSessionP,
+ this,
+ fTypeName.c_str(),
+ fTypeVersion.c_str(),
+ aDatastoreP,
+ fFieldListP
+ );
+} // TDataObjConfig::newSyncItemType
+
+
+#ifdef CONFIGURABLE_TYPE_SUPPORT
+// config element parsing
+bool TDataObjConfig::localStartElement(const char *aElementName, const char **aAttributes, sInt32 aLine)
+{
+ // checking the elements
+ if (strucmp(aElementName,"tagmap")==0) {
+ // <tagmap field="SUBJECT">
+ const char* nam = getAttr(aAttributes,"field");
+
+ if (!fFieldListP)
+ return fail("'use' must be specfied before first <tagmap>");
+ // search field
+ // %%% add context here if we have any
+ sInt16 fid = TConfigElement::getFieldIndex(nam,fFieldListP);
+ if (fid==VARIDX_UNDEFINED) return fail("'field' references unknown field '%s'",nam);
+
+ // create new tagmap
+ TTagMapDefinition *tagmapP= new TTagMapDefinition(this,fid);
+ // - save in list
+ fTagMaps.push_back(tagmapP);
+ // - let element handle parsing
+ expectChildParsing(*tagmapP);
+ return true;
+ } // if
+
+ // - none known here
+ if (TMultiFieldTypeConfig::localStartElement(aElementName,aAttributes,aLine)) return true;
+
+ // let the profile parse as if it was inside a "textprofile"
+ if (fProfileConfigP)
+ return delegateParsingTo(fProfileConfigP,aElementName,aAttributes,aLine);
+
+ return false; // cannot parse
+} // TDataObjConfig::localStartElement
+
+
+// resolve
+void TDataObjConfig::localResolve(bool aLastPass)
+{
+ // nop
+ // resolve inherited
+ inherited::localResolve(aLastPass);
+} // TDataObjConfig::localResolve
+#endif
+
+
+#ifdef HARDCODED_TYPE_SUPPORT
+TTagMapDefinition *TDataObjConfig::addTagMap( sInt16 aFid, const char* aXmlTag,
+ bool aBoolType, bool aEmbedded,
+ const char* aParent )
+{
+ // create new tagmap
+ TTagMapDefinition *tagmapP = new TTagMapDefinition(this,aFid);
+
+ // save the options
+ TCFG_ASSIGN(tagmapP->fXmlTag,aXmlTag);
+ tagmapP->fBoolType= aBoolType;
+ tagmapP->fEmbedded= aEmbedded;
+ tagmapP->fParent = aParent;
+
+ // save in list
+ fTagMaps.push_back(tagmapP);
+ // return pointer
+ return tagmapP;
+} // TDataObjConfig::addTagMap
+#endif
+
+
+
+/*
+ * Implementation of TDataObjType
+ */
+TDataObjType::TDataObjType(
+ TSyncSession *aSessionP,
+ TDataTypeConfig *aTypeCfgP, // type config
+ const char *aCTType,
+ const char *aVerCT,
+ TSyncDataStore *aRelatedDatastoreP,
+ TFieldListConfig *aFieldDefinitions // field definitions
+) :
+ TMultiFieldItemType(aSessionP,aTypeCfgP,aCTType,aVerCT,aRelatedDatastoreP,aFieldDefinitions),
+ fProfileHandlerP(NULL)
+{
+ // save typed config pointer again
+ fTypeCfgP = static_cast<TDataObjConfig *>(aTypeCfgP);
+ // create the profile handler
+ fProfileHandlerP = static_cast<TDataObjConfig *>(aTypeCfgP)->fProfileConfigP->newProfileHandler(this);
+ // set profile mode
+ fProfileHandlerP->setProfileMode(fTypeCfgP->fProfileMode);
+} // TDataObjType::TDataObjType
+
+
+TDataObjType::~TDataObjType()
+{
+ // handler not needed any more
+ if (fProfileHandlerP)
+ delete fProfileHandlerP;
+} // TDataObjType::~TDataObjType
+
+
+
+#ifdef OBJECT_FILTERING
+// get field index of given filter expression identifier.
+sInt16 TDataObjType::getFilterIdentifierFieldIndex(const char *aIdentifier, uInt16 aIndex)
+{
+ // check if explicit field level identifier
+ if (strucmp(aIdentifier,"F.",2)==0) {
+ // explicit field identifier, skip property lookup
+ aIdentifier+=2;
+ }
+ else {
+ // search tagmaps for tagged fields
+ TTagMapList::iterator pos;
+ for (pos= fTypeCfgP->fTagMaps.begin();
+ pos!=fTypeCfgP->fTagMaps.end(); pos++) {
+ // first priority: compare with explicit filterkeyword, if any
+ if (!TCFG_ISEMPTY((*pos)->fFilterKeyword)) {
+ // compare with filterkeyword
+ if (strucmp(TCFG_CSTR((*pos)->fFilterKeyword),aIdentifier)==0)
+ return (*pos)->fFid;
+ }
+ else if (!TCFG_ISEMPTY((*pos)->fXmlTag)) {
+ // compare with xml tag name itself
+ if (strucmp(TCFG_CSTR((*pos)->fXmlTag),aIdentifier)==0)
+ return (*pos)->fFid;
+ } // if
+ } // for
+ // no matching tag found, let TTextProfileHandler search for its own filter identifiers
+ if (fProfileHandlerP) {
+ sInt16 fid = fProfileHandlerP->getFilterIdentifierFieldIndex(aIdentifier,aIndex);
+ if (fid!=FID_NOT_SUPPORTED)
+ return fid;
+ }
+ // no tagged field found, search underlying field list
+ }
+ // if no field ID found so far, look up in field list
+ return TMultiFieldItemType::getFilterIdentifierFieldIndex(aIdentifier, aIndex);
+} // TDataObjType::getFilterIdentifierFieldIndex
+#endif
+
+
+
+// helper to create same-typed instance via base class
+TSyncItemType *TDataObjType::newCopyForSameType(
+ TSyncSession *aSessionP, // the session
+ TSyncDataStore *aDatastoreP // the datastore
+)
+{
+ // create new itemtype of appropriate derived class type that can handle
+ // this type
+ MP_RETURN_NEW(TDataObjType,DBG_OBJINST,"TDataObjType",TDataObjType(
+ aSessionP,
+ fTypeConfigP,
+ getTypeName(),
+ getTypeVers(),
+ aDatastoreP,
+ fFieldDefinitionsP
+ ));
+} // TDataObjType::newCopyForSameType
+
+
+// create new sync item of proper type and optimization for specified target
+TSyncItem *TDataObjType::internalNewSyncItem(TSyncItemType *aTargetItemTypeP, TLocalEngineDS *aLocalDatastoreP)
+{
+ // All DataObjs are stored in MultiFieldItems
+ if (!aTargetItemTypeP->isBasedOn(ity_multifield))
+ SYSYNC_THROW(TSyncException(DEBUGTEXT("TDataObjType::internalNewSyncItem with bad-typed target","txit3")));
+ TMultiFieldItemType *targetitemtypeP =
+ static_cast<TMultiFieldItemType *> (aTargetItemTypeP);
+ return new TMultiFieldItem(this,targetitemtypeP);
+} // TDataObjType::internalNewSyncItem
+
+
+// fill in SyncML data (but leaves IDs empty)
+bool TDataObjType::internalFillInData(
+ TSyncItem *aSyncItemP, // SyncItem to be filled with data
+ SmlItemPtr_t aItemP, // SyncML toolkit item Data to be converted into SyncItem (may be NULL if no data, in case of Delete or Map)
+ TLocalEngineDS *aLocalDatastoreP, // local datastore
+ TStatusCommand &aStatusCmd // status command that might be modified in case of error
+)
+{
+ // check type
+ if (!aSyncItemP->isBasedOn(ity_multifield))
+ SYSYNC_THROW(TSyncException(DEBUGTEXT("TDataObjType::internalFillInData: incompatible item class","txit4")));
+ TMultiFieldItem *itemP = static_cast<TMultiFieldItem *> (aSyncItemP);
+ // process data if any
+ if (aItemP->data) {
+ // parse data
+ stringSize sz;
+ cAppCharP t = smlPCDataToCharP(aItemP->data,&sz);
+ if (!parseData(t,sz,*itemP,aLocalDatastoreP)) {
+ // format error
+ aStatusCmd.setStatusCode(415); // Unsupported media type or format
+ ADDDEBUGITEM(aStatusCmd,"Error parsing Text content");
+ return false;
+ }
+ }
+ else {
+ // no data
+ aStatusCmd.setStatusCode(412); // incomplete command
+ ADDDEBUGITEM(aStatusCmd,"No data found in item");
+ return false;
+ }
+ // ok, let ancestor process data as well
+ return TMultiFieldItemType::internalFillInData(aSyncItemP,aItemP,aLocalDatastoreP,aStatusCmd);
+} // TDataObjType::internalFillInData
+
+
+// sets data and meta from SyncItem data, but leaves source & target untouched
+bool TDataObjType::internalSetItemData(
+ TSyncItem *aSyncItemP, // the syncitem to be represented as SyncML
+ SmlItemPtr_t aItem, // item with NULL meta and NULL data
+ TLocalEngineDS *aLocalDatastoreP // local datastore
+)
+{
+ // check type
+ if (!aSyncItemP->isBasedOn(ity_multifield))
+ SYSYNC_THROW(TSyncException(DEBUGTEXT("TDataObjType::internalSetItemData: incompatible item class","txit4")));
+ TMultiFieldItem *itemP = static_cast<TMultiFieldItem *> (aSyncItemP);
+ // let ancestor prepare first
+ if (!TMultiFieldItemType::internalSetItemData(aSyncItemP,aItem,aLocalDatastoreP)) return false;
+ // generate data item
+ string dataitem;
+ generateData(aLocalDatastoreP,*itemP,dataitem);
+ // put data item into opaque/cdata PCData (note that dataitem could be BINARY string, so we need to pass size!)
+ aItem->data=newPCDataStringX((const uInt8 *)dataitem.c_str(),true,dataitem.size());
+ // can't go wrong
+ return true;
+} // TDataObjType::internalSetItemData
+
+
+
+static void AddXmlTag( string &aString, string aTag, bool newline= false )
+{
+ aString+= "<" + aTag + ">";
+ if (newline) aString+= "\n";
+} // AddXmlTag
+
+
+static void AddXmlTagEnd( string &aString, string aTag, bool newline= true )
+{
+ aString+= "</" + aTag + ">";
+ if (newline) aString+= "\n";
+} // AddXmlTagEnd
+
+
+static void AppendXml( string &aString, string aTag, const char* value, bool tagDone= false )
+{
+ if (*value!='\0') {
+ if (!tagDone) AddXmlTag( aString, aTag );
+
+ aString+= value;
+ AddXmlTagEnd( aString, aTag );
+ } // if
+} // AppendXml
+
+
+static void AppendCDATA( string &aString, const char* aTag, const char* aValue )
+{
+ AddXmlTag( aString, aTag ); // must be shown according to OMA DS 1.2,
+ // even if the body is empty
+ if (*aValue!='\0') {
+ aString+= BeginCDATA; // only for the xml representation
+ aString+= aValue; // the body
+ //%%% luz: not needed here as there is no enclosing CData any more (or this will be done when needed by the SML encoder!)
+ //aString+= LowerCDATA; // only for the xml representation
+ aString+= '\n';
+ } // if
+
+ AddXmlTagEnd( aString, aTag );
+} // AppendCDATA
+
+
+string TDataObjType::getAttr( TMultiFieldItem &aItem, const char* aXmlTag, const char* aXmlAttr )
+{
+ TItemField* fieldP;
+ TTagMapList::iterator pos;
+ string v;
+
+ // search for an element, which matches for both <aXmlTag> and <aXmlAttr>
+ for (pos= fTypeCfgP->fTagMaps.begin();
+ pos!=fTypeCfgP->fTagMaps.end(); pos++) {
+ fieldP= aItem.getField( (*pos)->fFid ); // this is the element we're looking for
+ if (strcmp( aXmlTag, TCFG_CSTR((*pos)->fXmlTag ))==0 &&
+ strcmp( aXmlAttr,TCFG_CSTR((*pos)->fXmlAttr))==0) {
+ fieldP->getAsString( v ); // this is the attribute we're looking for
+ break;
+ } // if
+ } // for
+
+ return v; // <v> is "", if no attribute found
+} // TDataObjType::getAttr
+
+
+// generate Data item (includes header and footer)
+void TDataObjType::generateData(TLocalEngineDS *aDatastoreP, TMultiFieldItem &aItem, string &aString)
+{
+ #ifdef SYDEBUG
+ PDEBUGPRINTFX(DBG_GEN+DBG_HOT,("Generating:") );
+ aItem.debugShowItem(DBG_GEN);
+ #endif
+
+ TItemField* fieldP;
+ TItemField* fieldDocType; // the reference to the "DOCTYPE" (File/Folder/Email) field
+ TTagMapList::iterator pos;
+
+ string docT, v;
+ string inParent;
+ bool tagOpened= false;
+
+ //%%% luz: do not add extra CData here, this will be done when needed by the SML encoder!
+ //aString= BeginCDATA; // start a CDATA sequence
+
+ fieldDocType= aItem.getField( "DOCTYPE" );
+ if (fieldDocType) {
+ fieldDocType->getAsString( docT ); // main tag, straight forward implementation
+ AddXmlTag ( aString, docT, true );
+ } // if
+
+ // go thru the tag element list and get the matching element
+ for (pos= fTypeCfgP->fTagMaps.begin();
+ pos!=fTypeCfgP->fTagMaps.end(); pos++) {
+ fieldP= aItem.getField( (*pos)->fFid ); // this is the element we're looking for
+ if (!fieldP || // not a valid field
+ fieldP==fieldDocType) continue; // has been done outside already, ignore it
+
+ bool isArr= fieldP->isArray();
+ sInt16 maxI= 1; // do it once w/o array
+ if (isArr) maxI= fieldP->arraySize();
+ if (maxI==0) continue; // it is an array w/o any items, skip it
+
+ if ((*pos)->fEmbedded && fProfileHandlerP) {
+ // set related datastore so handler can access session specific datastore state
+ fProfileHandlerP->setRelatedDatastore(aDatastoreP);
+ fProfileHandlerP->generateText(aItem, v);
+ AppendCDATA( aString, TCFG_CSTR((*pos)->fXmlTag), v.c_str());
+ continue; // it's done now for this element
+ } // if
+
+ // the standard case (not embedded): cover both cases: array AND no array
+ sInt16 i;
+ for (i= 0; i<maxI; i++) {
+ TItemField* fieldAP= fieldP;
+ if (isArr) fieldAP= fieldP->getArrayField( i );
+
+ if (fieldAP->isAssigned() &&
+ TCFG_ISEMPTY((*pos)->fParent) &&
+ !inParent.empty()) { AddXmlTagEnd( aString,inParent.c_str() ); inParent= ""; }
+
+ // there is no native bool representation for the field lists
+ // So do it with the help of an additional flag
+ v= "";
+ if (fieldAP->isAssigned()) {
+ if (fieldAP->isBasedOn( fty_blob ) &&
+ fieldAP->hasProxy()) {
+ #ifdef STREAMFIELD_SUPPORT
+ string enc= getAttr( aItem, TCFG_CSTR((*pos)->fXmlTag), "enc" );
+
+ // according OMA DS 1.2, the endcoding attribute "enc" must be
+ // either not present (no encoding), "quoted-printable" or "base64".
+ // The implementation SHOULD NOT use other enc attribute values.
+ const char* ptr = fieldAP->getCStr();
+ size_t size= fieldAP->getStreamSize();
+
+ if (enc.empty()) { // no encoding
+ v.assign( ptr,size ); // => assign directly
+ }
+ else if (strucmp( enc.c_str(),"base64" )==0) {
+ char* bb= b64::encode( (uInt8*)ptr,size );
+ v= bb; // maybe one copy operation can be avoided in future
+ sysync_free( bb ); // does never contain '\0' => can be treated as normal string
+ }
+ else if (strucmp( enc.c_str(),"quoted-printable")==0) {
+ sysync::appendEncoded( (uInt8*)ptr,size, v, enc_quoted_printable,
+ MIME_MAXLINESIZE,0,true,true );
+ }
+ else {
+ v = "unknown_encoding: \"";
+ v+= enc;
+ v+= "\"";
+ }
+ #endif
+ }
+ else if ((*pos)->fBoolType) { // best fitting for integer and string
+ int i= fieldAP->getAsInteger();
+ switch ( i ) { // this is the integer - boolean mapping
+ case -1: v= "false"; break; // a strange value for "false"
+ case 0: v= ""; break; // 0 is the empty value (not assigned at parsing)
+ case 1: v= "true"; break; // the 'normal' value for "true"
+ } // switch
+ } // if
+ else fieldAP->getAsString( v );
+
+ if (!v.empty()) {
+ // avoid multiple identical elements (even if it's allowed in OMA DS 1.2)
+ if (!TCFG_ISEMPTY((*pos)->fParent)) {
+ bool ok= inParent.empty(); // either iParent==0 or strings are different
+ if (!ok && strucmp( inParent.c_str(),TCFG_CSTR((*pos)->fParent) )!=0) {
+ AddXmlTagEnd( aString,inParent.c_str() ); // switch off old tag first, if different
+ ok= true;
+ } // if
+
+ if (ok) { inParent= (*pos)->fParent;
+ AddXmlTag( aString,inParent.c_str(), true );
+ } // if
+ } // if
+ } // if
+ } // if
+
+ if (TCFG_ISEMPTY((*pos)->fXmlAttr)) { // w/o attributes
+ if (tagOpened) aString+= ">";
+ AppendXml ( aString, (*pos)->fXmlTag, v.c_str(), tagOpened );
+ tagOpened= false;
+ }
+ else if (!v.empty()) { // with XML attribute handling
+ if (!tagOpened) {
+ aString+= "<";
+ aString+= TCFG_CSTR((*pos)->fXmlTag);
+ } // if
+
+ aString+= " ";
+ aString+= TCFG_CSTR((*pos)->fXmlAttr);
+ aString+= "=\"" + v + "\"";
+ tagOpened= true;
+ } // if
+ } // for
+
+ } // for
+ // Leave the block finally
+ if (!inParent.empty()) AddXmlTagEnd( aString,inParent.c_str());
+
+ if (fieldDocType) {
+ fieldDocType->getAsString( docT ); // end of main tag, straight forward implementation
+ AddXmlTagEnd ( aString, docT );
+ } // if
+
+ //%%% luz: do not add extra CData here, this will be done when needed by the SML encoder!
+ //aString+= EndCDATA; // terminate the sequence
+
+ #ifdef SYDEBUG
+ if (PDEBUGTEST(DBG_GEN)) {
+ // note, do not use debugprintf because string is too long
+ PDEBUGPRINTFX(DBG_GEN,("%s", "") );
+ PDEBUGPRINTFX(DBG_GEN,("Successfully generated:") );
+ PDEBUGPUTSX (DBG_GEN+DBG_USERDATA, aString.c_str() );
+ PDEBUGPRINTFX(DBG_GEN+DBG_USERDATA,("%s", "") );
+ } // if
+ #endif
+} // TDataObjType::generateData
+
+
+/*! Skip white spaces */
+static void SkipWhiteSP( const char* &p )
+{
+ while (*p==0x0D || // CR
+ *p==0x0A || // LF
+ *p==0x09 || // HT
+ *p==' ') p++; // skip leading CR/LF/Tabs and spaces
+} // SkipWhiteSP
+
+
+// simple straight forward letter recognition
+static bool IsLetter( char ch )
+{
+ ch= toupper(ch);
+ return ch>='A' && ch<='Z';
+} // IsLetter
+
+
+/*! Get the next tag and value of <p>
+ *
+ * @param <aTag> may contain attributes, e.g. <body enc="base64">
+ * @param <direction> +1 go down in the hierarchy
+ * 0 stay on the same level
+ * -1 go up in the hierarchy
+ */
+static bool NextTag( const char* &p, string &aTag, string &aAttr, const char* &aPos, int &aSize,
+ int &level, int &direction )
+{
+ const char* BeginCom= "<!--";
+ const char* EndCom= "-->";
+ const char* q;
+
+ // default values
+ bool vDone= false;
+ aAttr = "";
+ aPos = "";
+ aSize = 0;
+ direction = 0;
+
+ SkipWhiteSP( p );
+ q= strstr( p,BeginCom ); // comment ?
+ if (q && q==p) {
+ }
+ else {
+ if (*p!='<') return false; // must start with a tag
+ p++;
+ if (*p=='/') { p++; direction= -1; }
+
+ q= strstr( p,">" ); if (!q) return false;
+ aTag.assign( p, (unsigned int)( q-p ) ); // this is the tag
+
+ int pos= aTag.find(" ");
+ if (pos>=0) { // the tag contains some attributes
+ aAttr= aTag.substr( pos+1 ); // ==> get them
+ aTag = aTag.substr( 0,pos ); // the real tag
+ } // if
+
+ p= q+1;
+ } // if
+
+ do {
+ SkipWhiteSP( p );
+ if (direction==-1) return true; // end tag reached
+
+ if (*p!='<') break; // a value is coming now, not nested
+
+ // is it a comment ?
+ q= strstr( p,BeginCom );
+ if (q && q==p) {
+ q= strstr( q, EndCom ); if (!q) return false;
+ p= q + strlen ( EndCom ); continue; // comment is skipped
+ } // if
+
+ q= strstr( p,"</" );
+ if (q && q==p) break; // an empty value
+
+ // is it a CDATA section ?
+ q= strstr( p,BeginCDATA );
+ if (q && q==p) {
+ p+= strlen( BeginCDATA );
+ q= strstr( p,DoubleCEnd ); if (!q) return false;
+ aPos = p;
+ aSize= (unsigned int)( q-p );
+ vDone= true; // don't do it later again
+
+ p= q + strlen( DoubleCEnd );
+
+ q= strstr( p,BeginCDATA ); if (!q) return false; // nested CDATA skipped
+ p= q + strlen( BeginCDATA );
+ break;
+ } // if
+
+ if (!IsLetter( p[ 1 ] )) break; // no nested tag ?
+
+ level++; direction= 1; // nested tags -> do handling outside
+ return true;
+ } while (true);
+
+ SkipWhiteSP( p );
+ q= strstr( p, "</" ); if (!q) return false;
+
+ if (!vDone) {
+ aPos = p;
+ aSize= (unsigned int)( q-p );
+ vDone= true; // don't do it later again
+ } // if
+
+ p= q+2;
+ q= strstr( p, aTag.c_str() ); if (!q || p!=q) return false;
+ q+= aTag.size();
+ q= strstr( p,">" ); if (!q) return false;
+
+ p= q+1;
+ return true;
+} // NextTag
+
+
+// the structure for attributes with values
+struct TAField {
+ string name;
+ string value;
+}; // AField
+
+
+/*
+struct VVV {
+ VVV() { printf( "hinein\n" ); }
+ ~VVV() { printf( "hinaus '%s'\n", a.c_str() ); }
+ string a;
+};
+*/
+
+
+// parse Data item (includes header and footer)
+bool TDataObjType::parseData(const char *aText, stringSize aSize, TMultiFieldItem &aItem, TLocalEngineDS *aDatastoreP)
+{
+ TItemField* fieldP;
+ TTagMapList::iterator pos;
+ string tag, attr, value, docType;
+ const char* vPos;
+ int vSize;
+
+ // Hierarchical tags
+ typedef std::list<string> TStrList;
+ TStrList tagHierarchy;
+ TStrList::iterator px;
+
+ TAField a;
+ typedef std::list<TAField> TAList;
+ TAList aList;
+ TAList::iterator ax;
+
+ #ifdef SYDEBUG
+ if (PDEBUGTEST(DBG_PARSE)) {
+ // very detailed, show item being parsed
+ PDEBUGPRINTFX(DBG_PARSE+DBG_HOT, ("Parsing: ") );
+ PDEBUGPUTSX (DBG_PARSE+DBG_USERDATA, aText);
+ }
+ #endif
+
+ // Go thru all the tags and build temporary stack at <tList>
+ int level= 0;
+ int direction;
+ // bool found;
+
+ /*
+ VVV vEl;
+ typedef std::list<VVV> VList;
+ VList vv;
+ VList::iterator vx;
+
+ vEl.a= "beat"; vv.push_back( vEl );
+ vEl.a= "ee"; vv.push_back( vEl );
+ vEl.a= "luz"; vv.push_back( vEl );
+ vEl.a= "gugus"; vv.push_back( vEl );
+ vEl.a= "gurk";
+
+ for (vx= vv.begin();
+ vx!=vv.end(); vx++) {
+ printf( "soso '%s'\n", (*vx).a.c_str() );
+ }
+ */
+
+ const char* p= aText;
+ if (strstr( p, BeginCDATA )==p) p+= strlen( BeginCDATA );
+ while (NextTag( p, tag,attr, vPos,vSize, level,direction )) {
+ // parse all attributes and put them into <aList>
+ aList.clear();
+ while (!attr.empty()) {
+ int pos= attr.find( "=\"" );
+ if (pos<0) { attr= ""; break; }
+
+ a.name = attr.substr( 0,pos );
+ attr = attr.substr( pos+2 );
+
+ pos= attr.find( "\"" );
+ if (pos<0) { attr= ""; break; }
+
+ a.value= attr.substr( 0,pos );
+ attr = attr.substr( pos+1 );
+ while (attr.find(" ")==0) attr= attr.substr( 1 );
+
+ aList.push_back( a );
+ } // while
+
+ switch (direction) {
+ case -1 : px= tagHierarchy.end(); // are we going up in the hierarchy ?
+ if (px!=tagHierarchy.begin()) // if the list is not empty ...
+ tagHierarchy.erase( --px ); // erase last element
+ break;
+
+ case 1 : tagHierarchy.push_back( tag ); // are we going down in the hierarchy ?
+ if (level>1) break;
+
+ docType= tag;
+ vPos = docType.c_str();
+ vSize= docType.length();
+ tag = "DOCTYPE"; // assign the document type
+ // go on for this special case
+
+ default:
+ // go thru the tag element list and get the matching element
+ for (pos= fTypeCfgP->fTagMaps.begin();
+ pos!=fTypeCfgP->fTagMaps.end(); pos++) {
+ if (strucmp( tag.c_str(),TCFG_CSTR((*pos)->fXmlTag) )==0) {
+ fieldP= aItem.getField( (*pos)->fFid ); // this is the element we're looking for
+ if (fieldP) {
+ if (fieldP->isAssigned() && // -> allow multiple fields with the same name
+ !fieldP->isArray()) continue; // it is assigned already !
+
+ // for embedded structures, the content shouldn't be copied
+ // in all other cases, it can still be used as <value> string
+ if (!(*pos)->fEmbedded) value.assign( vPos,vSize ); // this is the value of this tag
+
+ bool xAtt= !TCFG_ISEMPTY((*pos)->fXmlAttr);
+ if (aList.size()==0) {
+ if (xAtt) continue; // does not fit, both must be either empty
+ }
+ else {
+ if (xAtt) {
+ value= "";
+
+ for (ax= aList.begin();
+ ax!=aList.end(); ax++) {
+ if (strucmp( (*ax).name.c_str(),TCFG_CSTR((*pos)->fXmlAttr) )==0) {
+ value= (*ax).value.c_str(); break; // for attribute handling take as <value>.
+ } // if
+ } // for
+
+ if (value.empty()) continue;
+ } // if
+ } // if
+
+ do {
+ // there must be either no parent, or the parent must match
+ if ( !TCFG_ISEMPTY((*pos)->fParent) &&
+ strucmp( TCFG_CSTR ((*pos)->fParent),tagHierarchy.back().c_str() )!=0) break;
+
+ if (fieldP->isBasedOn( fty_blob )) {
+ #ifdef STREAMFIELD_SUPPORT
+ string enc= getAttr( aItem, TCFG_CSTR((*pos)->fXmlTag), "enc" );
+
+ if (enc.empty()) {
+ // no encoding
+ }
+ else if (strucmp( enc.c_str(),"base64" )==0) {
+ uInt32 oLen;
+ uInt8* bb= b64::decode( value.c_str(), 0, &oLen );
+ fieldP->setAsString( (const char*)bb, oLen ); // assign the value
+ sysync_free ( bb );
+ break; // already done now (blob assigned correctly
+ }
+ else if (strucmp( enc.c_str(),"quoted-printable")==0) {
+ string v;
+ sysync::appendDecoded( value.c_str(),value.length(), v, enc_quoted_printable );
+ fieldP->setAsString( v.c_str(), v.size() ); // assign the value
+ break; // already done now (blob assigned correctly
+ }
+ else {
+ value = "unknown_encoding: \"";
+ value+= enc;
+ value+= "\"";
+ }
+ #endif
+ } // if
+
+ // we don't have a boolean type directly, so make a special conversion for it
+ if ((*pos)->fBoolType) { // assuming the field list item is integer for boolean
+ if (strucmp( value.c_str(),"0" )==0) break; // as defined in OMA DS 1.2
+ else if (strucmp( value.c_str(),"false" )==0) value= "-1"; // special boolean treatement
+ else if (strucmp( value.c_str(),"true" )==0) value= "1";
+ } // if
+
+ // delegate parsing of embedded object
+ if ((*pos)->fEmbedded && fProfileHandlerP) {
+ // set related datastore so handler can access session specific datastore state
+ fProfileHandlerP->setRelatedDatastore(aDatastoreP);
+ // vPos,vSize is not a copy, but a direct reference into <aText>
+ bool ok= fProfileHandlerP->parseText(vPos,vSize, aItem);
+ if (!ok) {
+ // format error
+ // aStatusCmd.setStatusCode(415); // Unsupported media type or format
+ // ADDDEBUGITEM(aStatusCmd,"Error parsing Text content");
+ return false;
+ } // if
+
+ break; // already done (at profile handler)
+ } // if
+
+ // it's ok now to fill in <value>
+ if (fieldP->isArray()) fieldP->appendString( value.c_str() );
+ else fieldP->setAsString ( value.c_str() ); // assign the value
+ } while (false);
+ } // if
+
+ if (aList.size()==0) break;
+ } // if
+ } // for
+ } // switch
+ } // while
+
+ #ifdef SYDEBUG
+ PDEBUGPRINTFX(DBG_PARSE,("Successfully parsed: "));
+//%%%not again! PDEBUGPUTSX(DBG_PARSE+DBG_USERDATA+DBG_EXOTIC, aText );
+ aItem.debugShowItem(DBG_DATA+DBG_PARSE);
+ #endif
+
+ return true;
+} // TDataObjType::parseData
+
+
+// generates SyncML-Devinf property list for type
+SmlDevInfCTDataPropListPtr_t TDataObjType::newCTDataPropList(TTypeVariantDescriptor aVariantDescriptor)
+{
+ // no properties here
+ return NULL;
+} // TDataObjType::newCTDataPropList
+
+
+// Filtering: add keywords and property names to filterCap
+void TDataObjType::addFilterCapPropsAndKeywords(SmlPcdataListPtr_t &aFilterKeywords, SmlPcdataListPtr_t &aFilterProps, TTypeVariantDescriptor aVariantDescriptor)
+{
+ // add keywords from tagmaps
+ #ifdef OBJECT_FILTERING
+ TTagMapList::iterator pos;
+ for(pos=fTypeCfgP->fTagMaps.begin();pos!=fTypeCfgP->fTagMaps.end();pos++) {
+ // first priority: compare with explicit filterkeyword, if any
+ if (!TCFG_ISEMPTY((*pos)->fFilterKeyword)) {
+ // has a filterkeyword, show it
+ addPCDataStringToList(TCFG_CSTR((*pos)->fFilterKeyword), &aFilterKeywords);
+ }
+ }
+ // let embedded profile add the keywords
+ if (fProfileHandlerP) {
+ fProfileHandlerP->addFilterCapPropsAndKeywords(aFilterKeywords, aFilterProps, aVariantDescriptor, this);
+ }
+ // let base class add own keywords/props
+ inherited::addFilterCapPropsAndKeywords(aFilterKeywords, aFilterProps, aVariantDescriptor);
+ #endif
+} // TDataObjType::addFilterCapPropsAndKeywords
+
+
+// intended for creating SyncItemTypes for remote databases from
+// transmitted DevInf.
+// SyncItemType MUST NOT take ownership of devinf structure passed
+// (because multiple types might be created from a single CTCap entry)
+bool TDataObjType::analyzeCTCap(SmlDevInfCTCapPtr_t aCTCapP)
+{
+ // just let parent handle
+ return inherited::analyzeCTCap(aCTCapP);
+} // TDataObjType::analyzeCTCap
+
+
+/// @brief copy CTCap derived info from another SyncItemType
+/// @return false if item not compatible
+/// @note required to create remote type variants from ruleMatch type alternatives
+bool TDataObjType::copyCTCapInfoFrom(TSyncItemType &aSourceItem)
+{
+ // just let parent handle
+ return inherited::copyCTCapInfoFrom(aSourceItem);
+} // TDataObjType::copyCTCapInfoFrom
+
+
+
+
+/* end of TDataObjType implementation */
+// eof
diff --git a/src/sysync/dataobjtype.h b/src/sysync/dataobjtype.h
new file mode 100755
index 0000000..637c4b6
--- /dev/null
+++ b/src/sysync/dataobjtype.h
@@ -0,0 +1,183 @@
+/*
+ * File: DataObjType.h
+ *
+ * Author: Beat Forster (bfo@synthesis.ch)
+ *
+ * TDataObjType
+ * base class for data object based items (EMAILOBJ, FILEOBJ, FOLDEROBJ)
+ * implemented for OMA DS V1.2
+ *
+ * Copyright (c) 2005-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ * 2005-07-20 : bfo : created from TextItemType
+ *
+ */
+
+#ifndef DataObjType_H
+#define DataObjType_H
+
+// includes
+#include "syncitemtype.h"
+#include "multifielditemtype.h"
+#include "textprofile.h"
+
+// for test
+//#define HARDCODED_TYPE_SUPPORT
+
+
+namespace sysync {
+
+// property definition
+class TTagMapDefinition : public TConfigElement {
+ typedef TConfigElement inherited;
+public:
+ // constructor/destructor
+ TTagMapDefinition(TConfigElement *aParentElementP, sInt16 aFid);
+ virtual ~TTagMapDefinition();
+ // properties
+
+ // - tagged field
+ TCFG_STRING fXmlTag;
+ // - attribute field of tag
+ TCFG_STRING fXmlAttr;
+
+ // - specifies the field as boolean
+ bool fBoolType;
+ // - specifies an embedded profile (for RFC2822)
+ bool fEmbedded;
+
+ // - the expected parent tag (if not empty)
+ TCFG_STRING fParent;
+
+ // - field id
+ sInt16 fFid;
+
+ #ifdef OBJECT_FILTERING
+ // - no filterkeyword
+ TCFG_STRING fFilterKeyword;
+ #endif
+
+ #ifdef CONFIGURABLE_TYPE_SUPPORT
+ // check config elements
+ virtual bool localStartElement(const char *aElementName, const char **aAttributes, sInt32 aLine);
+ #endif
+
+ virtual void clear();
+}; // TTagMapDefinition
+
+
+// tag mapping definitions
+typedef std::list<TTagMapDefinition *> TTagMapList;
+
+// Text based datatype
+class TDataObjConfig : public TMultiFieldTypeConfig
+{
+ typedef TMultiFieldTypeConfig inherited;
+public:
+ TDataObjConfig(const char *aElementName, TConfigElement *aParentElementP);
+ virtual ~TDataObjConfig();
+ // properties
+ // - Note, field list is parsed here, but is a property of TMultiFieldTypeConfig
+ // - text-line based field definitions
+ TTagMapList fTagMaps;
+
+ // public functions
+ // - create Sync Item Type of appropriate type from config
+ virtual TSyncItemType *newSyncItemType(TSyncSession *aSessionP, TSyncDataStore *aDatastoreP);
+
+ #ifdef HARDCODED_TYPE_SUPPORT
+ TTagMapDefinition *addTagMap( sInt16 aFid, const char* aXmlTag,
+ bool aBoolType, bool aEmbedded,
+ const char* aParent );
+ #endif
+
+protected:
+ #ifdef CONFIGURABLE_TYPE_SUPPORT
+ // check config elements
+ virtual bool localStartElement( const char *aElementName,
+ const char **aAttributes, sInt32 aLine );
+ virtual void localResolve( bool aLastPass );
+ #endif
+
+ virtual void clear();
+}; // TDataObjConfig
+
+
+const uInt16 ity_dataobj= 200; // must be unique
+
+class TDataObjType: public TMultiFieldItemType
+{
+ typedef TMultiFieldItemType inherited;
+public:
+ // constructor
+ TDataObjType(
+ TSyncSession *aSessionP,
+ TDataTypeConfig *aTypeCfgP, // type config
+ const char *aCTType,
+ const char *aVerCT,
+ TSyncDataStore *aRelatedDatastoreP,
+ TFieldListConfig *aFieldDefinitions // field definitions
+ );
+ // destructor
+ virtual ~TDataObjType();
+ // access to type
+ virtual uInt16 getTypeID(void) const { return ity_dataobj; };
+ virtual bool isBasedOn(uInt16 aItemTypeID) const { return aItemTypeID==ity_dataobj ? true : TMultiFieldItemType::isBasedOn(aItemTypeID); };
+ // differentiation between implemented and just descriptive TSyncTypeItems
+ virtual bool isImplemented(void) { return true; }; // MIME-DIR is an implementation
+ // helper to create same-typed instance via base class
+ // MUST BE IMPLEMENTED IN ALL DERIVED CLASSES!
+ virtual TSyncItemType *newCopyForSameType(
+ TSyncSession *aSessionP, // the session
+ TSyncDataStore *aDatastoreP // the datastore
+ );
+ /// @brief copy CTCap derived info from another SyncItemType
+ virtual bool copyCTCapInfoFrom(TSyncItemType &aSourceItem);
+ #ifdef OBJECT_FILTERING
+ // filtering
+ // - get field index of given filter expression identifier.
+ virtual sInt16 getFilterIdentifierFieldIndex(const char *aIdentifier, uInt16 aIndex);
+ #endif
+protected:
+ // - analyze CTCap for specific type
+ virtual bool analyzeCTCap(SmlDevInfCTCapPtr_t aCTCapP);
+ // - obtain property list for type, returns NULL if none available
+ virtual SmlDevInfCTDataPropListPtr_t newCTDataPropList(TTypeVariantDescriptor aVariantDescriptor);
+ // - Filtering: add keywords and property names to filterCap
+ virtual void addFilterCapPropsAndKeywords(SmlPcdataListPtr_t &aFilterKeywords, SmlPcdataListPtr_t &aFilterProps, TTypeVariantDescriptor aVariantDesc);
+ // Item data management
+ // - create new sync item of proper type and optimization for specified target
+ virtual TSyncItem *internalNewSyncItem(TSyncItemType *aTargetItemTypeP, TLocalEngineDS *aLocalDatastoreP);
+ // - fill in SyncML data (but leaves IDs empty)
+ virtual bool internalFillInData(
+ TSyncItem *aSyncItemP, // SyncItem to be filled with data
+ SmlItemPtr_t aItemP, // SyncML toolkit item Data to be converted into SyncItem (may be NULL if no data, in case of Delete or Map)
+ TLocalEngineDS *aLocalDatastoreP, // local datastore
+ TStatusCommand &aStatusCmd // status command that might be modified in case of error
+ );
+ // - sets data and meta from SyncItem data, but leaves source & target untouched
+ virtual bool internalSetItemData(
+ TSyncItem *aSyncItemP, // the syncitem to be represented as SyncML
+ SmlItemPtr_t aItem, // item with NULL meta and NULL data
+ TLocalEngineDS *aLocalDatastoreP // local datastore
+ );
+
+ // get attribute <aAttr> of <aXmlTag>. If not available, return ""
+ virtual string getAttr( TMultiFieldItem &aItem, const char* aXmlTag, const char* aXmlAttr );
+
+ // generate Data item (includes header and footer)
+ virtual void generateData(TLocalEngineDS *aDatastoreP, TMultiFieldItem &aItem, string &aString);
+ // parse Data item (includes header and footer)
+ virtual bool parseData(const char *aText, stringSize aSize, TMultiFieldItem &aItem, TLocalEngineDS *aDatastoreP);
+private:
+ // member fields
+ TProfileHandler *fProfileHandlerP;
+ TDataObjConfig *fTypeCfgP; // the text type config element
+}; // TDataObjType
+
+
+} // namespace sysync
+
+#endif // DataObjType_H
+
+// eof
diff --git a/src/sysync/debuglogger.cpp b/src/sysync/debuglogger.cpp
new file mode 100755
index 0000000..f99a818
--- /dev/null
+++ b/src/sysync/debuglogger.cpp
@@ -0,0 +1,1519 @@
+/*
+ * File: debuglogger.cpp
+ *
+ * Author: Lukas Zeller (luz@synthesis.ch)
+ *
+ * Global debug mechanisms
+ *
+ * Copyright (c) 2005-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ * 2005-08-04 : luz : created
+ *
+ */
+
+
+#include "prefix_file.h"
+
+#ifdef SYDEBUG
+
+#include "debuglogger.h"
+
+
+#ifdef MULTI_THREAD_SUPPORT
+#include "platform_thread.h"
+#endif
+
+namespace sysync {
+
+#ifndef HARDCODED_CONFIG
+
+// debug format modes
+cAppCharP const DbgOutFormatNames[numDbgOutFormats] = {
+ "text", // plain text format (but can be indented)
+ "xml", // XML format
+ "html" // HTML format
+};
+
+
+// HTML dynamic folding modes
+cAppCharP const DbgFoldingModeNames[numDbgFoldingModes] = {
+ "none", // do not include dynamic folding into HTML logs
+ "collapsed", // include folding - all collapsed by default
+ "expanded", // include folding - all expanded by default
+ "auto" // include folding - collapse/expand state predefined on a block-by-block basis
+};
+
+
+// debug flush modes
+cAppCharP const DbgFlushModeNames[numDbgFlushModes] = {
+ "buffered", // no flush, keep open as long as possible, output buffered (fast, needed for network drives)
+ "flush", // flush every debug message
+ "openclose" // open and close debug channel separately for every message (as in 2.x engine)
+};
+
+// debug subthread isolation modes
+cAppCharP const DbgSubthreadModeNames[numDbgSubthreadModes] = {
+ "none", // do not handle output from subthread specially
+ "suppress", // suppress output from subthreads
+ "separate", // create separate output stream (=file) for each subthread
+ "mix", // mix on a line by line basis
+ "mixblocks" // buffer thread's output and mix block-wise it into main stream when appropriate
+};
+
+#endif
+
+// file extentsions for debug format modes
+cAppCharP const DbgOutFormatExtensions[numDbgOutFormats] = {
+ ".log", // plain text format (but can be indented)
+ ".xml", // XML format
+ ".html" // HTML format
+};
+
+
+cAppCharP const DbgOutDefaultPrefixes[numDbgOutFormats] = {
+ "*** Start of log",
+ "<?xml version=\"1.0\"?>\n"
+ "<sysync_log version=\"1.0\">",
+ "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\n"
+ "<html><head><title>Synthesis SyncML Engine " SYSYNC_FULL_VERSION_STRING " Log</title>\n"
+ "<meta http-equiv=\"content-type\" content=\"text/html;charset=UTF-8\">\n"
+ "<style type=\"text/css\" media=\"screen\"><!--\n"
+ ".block { color: #0000FF; font-weight: bold; }\n"
+ ".attribute { color: #A5002C; }\n"
+ ".attrval { color: #D80039; font-weight: bold; }\n"
+ ".error { color: red; font-weight: bold; }\n"
+ ".hotalone { color: #000000; font-weight: bold; }\n"
+ ".hot { font-weight: bold; }\n"
+ ".script { color: #996633; }\n" // brownish
+ ".source { color: #3333FF; font-family:courier,monospace; font-size: 90%; font-weight: bold; }\n" // keyword blue
+ ".comment { color: #669933; font-family:courier,monospace; font-size: 90%; font-weight: bold; }\n" // comment green
+ ".skipped { color: #BBBBBB; font-family:courier,monospace; font-size: 90%; font-weight: bold; }\n" // skipped code
+ ".value { color: #FF3300; }\n" // bright orange
+ ".filter { color: #997F66; }\n" // brownish pale
+ ".match { color: #A95E38; }\n" // brownish orange
+ ".dbapi { color: #CC3366; }\n" // dark reddish/pink (pink/violet = database)
+ ".plugin { color: #9151A3; }\n" // dark violet (pink/violet = database)
+ ".incoming { color: #196D00; }\n" // really dark green (green = remote)
+ ".outgoing { color: #002C84; }\n" // really dark blue (blue = local)
+ ".conflict { color: #990000; }\n" // dark red
+ ".remote { color: #709900; }\n" // greenish (green = remote)
+ ".proto { color: #777100; }\n" // dark yellowish/brown
+ ".rest { color: #AAAAAA; }\n" // greyed
+ ".exotic { color: #FF9900; }\n" // mango
+ "a.jump { color: #5D82BA; }\n"
+ "pre { font-size: 90%; }\n"
+ // for folding (always included, as it must be in header)
+ ".exp {\n"
+ " color: #FF0000;\n"
+ " font-weight: bold;\n"
+ " font-size: 90%;\n"
+ " width: 1em;\n"
+ " height: 1em;\n"
+ " display: inline;\n"
+ " border-width: 0.2em;\n"
+ " border-style: solid;\n"
+ " text-align: center;\n"
+ " vertical-align: middle;\n"
+ " padding: 0px 0.2em 0px 0.2em;\n"
+ " margin: 0 4px 2px 0;\n"
+ "}\n"
+ ".coll {\n"
+ " color: #754242;\n"
+ " font-weight: bold;\n"
+ " font-size: 90%;\n"
+ " width: 1em;\n"
+ " height: 1em;\n"
+ " display: inline;\n"
+ " border-width: 0.2em;\n"
+ " border-style: solid;\n"
+ " text-align: center;\n"
+ " vertical-align: middle;\n"
+ " padding: 0px 0.2em 0px 0.2em;\n"
+ " margin: 0 4px 2px 0;\n"
+ "}\n"
+ ".doall { color: #754242; }\n"
+ "--></style>\n"
+ "</head><body><h2>Start of log - Synthesis SyncML Engine " SYSYNC_FULL_VERSION_STRING "</h2>\n<ul>\n"
+};
+
+cAppCharP const DbgOutDefaultSuffixes[numDbgOutFormats] = {
+ "*** End of log",
+ "</sysync_log>",
+ "</ul><h2>End of log</h2></html>"
+};
+
+
+cAppCharP FoldingPrefix =
+ "<script language=javascript1.2 type=text/javascript><!--\n"
+ "function div_ref_style (id) {\n"
+ " if (document.layers) return document.layers[id];\n"
+ " else if (document.all) return document.all[id].style;\n"
+ " else if (document.getElementById) return document.getElementById(id).style;\n"
+ " else return null;\n"
+ "}\n"
+ "function exp(id) {\n"
+ " if(div_ref_style('B'+id).display!='block') {\n"
+ " div_ref_style('B'+id).display='block';\n"
+ " div_ref_style('E'+id).display='none';\n"
+ " div_ref_style('C'+id).display='inline';\n"
+ " }\n"
+ "}\n"
+ "function coll(id) {\n"
+ " if(div_ref_style('B'+id).display!='none') {\n"
+ " div_ref_style('B'+id).display='none';\n"
+ " div_ref_style('E'+id).display='inline';\n"
+ " div_ref_style('C'+id).display='none';\n"
+ " }\n"
+ "}\n"
+ "function doall(id,collapse) {\n"
+ " // get parent element\n"
+ " if (id=='') {\n"
+ " mydiv=document;\n"
+ " }\n"
+ " else {\n"
+ " mydiv=document.getElementById('B'+id); // get div to collapse or expand\n"
+ " if (collapse) {\n"
+ " coll(id);\n"
+ " }\n"
+ " else {\n"
+ " exp(id);\n"
+ " }\n"
+ " }\n"
+ " // get all contained blocks\n"
+ " divs=mydiv.getElementsByTagName('div') // all divs\n"
+ " for (i=0 ; i<divs.length ; i++) {\n"
+ " if (divs[i].className=='blk') {\n"
+ " // this is a foldable block div\n"
+ " bid = divs[i].id.substring(1);\n"
+ " if (collapse) {\n"
+ " coll(bid);\n"
+ " }\n"
+ " else {\n"
+ " exp(bid);\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ "}\n"
+ "--></script>\n"
+ "<li><span class=\"doall\" onclick=\"doall('',true)\">[-- collapse all --]</span><span class=\"doall\" onclick=\"doall('',false)\">[++ expand all ++]</span></li>\n";
+
+
+// externals
+
+#ifdef CONSOLEINFO
+// privately redefined here to avoid circular headers (would need syncappbase.h)
+extern "C" void ConsolePuts(const char *text);
+#endif
+
+// TDbgOptions implementation
+// --------------------------
+
+TDbgOptions::TDbgOptions()
+{
+ // set defaults
+ clear();
+} // TDbgOptions::TDbgOptions
+
+
+void TDbgOptions::clear(void)
+{
+ fOutputFormat=dbgfmt_html; // most universally readable and convenient
+ fIndentString=" "; // two spaces
+ fCustomPrefix.erase(); // no custom prefix
+ fCustomSuffix.erase(); // no custom suffix
+ fSeparateMsgs=true; // separate text message lines (<msg></msg> in xml)
+ fTimestampStructure=true; // timestamps in structure...
+ fTimestampForAll=false; // ..but not for every line
+ fThreadIDForAll=false; // not by default
+ fFlushMode=dbgflush_none; // no special flush or openclose (fast, but might loose info on process abort)
+ fFoldingMode=dbgfold_auto; // dynamic folding enabled, expanded/collapsed defaults automatically set on block-by-block basis
+ fAppend=false; // default to overwrite existing logfiles
+ fSubThreadMode=dbgsubthread_suppress; // simply suppress subthread info
+ fSubThreadBufferMax=1024*1024; // don't buffer more than one meg.
+} // TDbgOptions::clear
+
+
+
+// TDbgOut implementation
+// ----------------------
+
+TDbgOut::TDbgOut() :
+ fDestructed(false)
+{
+ // init
+ fIsOpen=false;
+} // TDbgOut::TDbgOut
+
+
+TDbgOut::~TDbgOut()
+{
+ destruct();
+} // TDbgOut::~TDbgOut
+
+
+void TDbgOut::destruct(void)
+{
+ if (!fDestructed) doDestruct();
+ fDestructed=true;
+} // TDbgOut::destruct
+
+
+void TDbgOut::doDestruct(void)
+{
+ // make sure files are closed
+ closeDbg();
+} // TDbgOut::doDestruct
+
+
+// TStdFileDbgOut implementation
+// -----------------------------
+
+#ifndef NO_C_FILES
+
+TStdFileDbgOut::TStdFileDbgOut()
+{
+ // init
+ fFileName.erase();
+ fFile=NULL;
+} // TStdFileDbgOut::TStdFileDbgOut
+
+
+TStdFileDbgOut::~TStdFileDbgOut()
+{
+ destruct();
+} // TStdFileDbgOut::~TStdFileDbgOut
+
+
+// open standard C file based debug output channel
+bool TStdFileDbgOut::openDbg(cAppCharP aDbgOutputName, cAppCharP aSuggestedExtension, TDbgFlushModes aFlushMode, bool aOverWrite, bool aRawMode)
+{
+ if (fIsOpen) {
+ // first close
+ closeDbg();
+ }
+ // now apply new flush mode
+ fFlushMode=aFlushMode;
+ // save new file name
+ fFileName=aDbgOutputName;
+ // for C files, use the extension provided
+ fFileName+=aSuggestedExtension;
+ // open
+ fFile=fopen(fFileName.c_str(),aRawMode ? (aOverWrite ? "wb" : "ab") : (aOverWrite ? "w" : "a"));
+ // in case this fails, we'll have a NULL fFile. We can't do anything more here
+ fIsOpen=fFile!=NULL;
+ // For openclose mode, we have opened here only to check for logfile writability - close again
+ if (fIsOpen && fFlushMode==dbgflush_openclose) {
+ fclose(fFile);
+ fFile=NULL;
+ }
+ // return false if we haven't been successful opening the channel
+ return fIsOpen;
+} // TStdFileDbgOut::openDbg
+
+
+// return current size of debug file
+uInt32 TStdFileDbgOut::dbgFileSize(void)
+{
+ if (!fIsOpen) return 0; // no file, no size
+ uInt32 sz;
+ if (fFlushMode==dbgflush_openclose) {
+ // we need to open the file for append first
+ fFile=fopen(fFileName.c_str(),"a");
+ fseek(fFile,0,SEEK_END); // move to end (needed, otherwise ftell may return 0 despite "a" fopen mode)
+ sz=ftell(fFile);
+ fclose(fFile);
+ fFile=NULL;
+ }
+ else {
+ fseek(fFile,0,SEEK_END); // move to end (needed, otherwise ftell may return 0 despite "a" fopen mode)
+ sz=ftell(fFile); // return size
+ }
+ return sz;
+} // TStdFileDbgOut::dbgFileSize
+
+
+// close standard C file based debug channel
+void TStdFileDbgOut::closeDbg(void)
+{
+ if (fIsOpen) {
+ if (fFile) {
+ fclose(fFile);
+ fFile=NULL;
+ }
+ fIsOpen=false;
+ }
+} // TStdFileDbgOut::closeDbg
+
+
+// write single line to standard file based output channel
+void TStdFileDbgOut::putLine(cAppCharP aLine, bool aForceFlush)
+{
+ // if not open, just NOP
+ if (fIsOpen) {
+ if (fFlushMode==dbgflush_openclose) {
+ // we need to open the file for append first
+ fFile=fopen(fFileName.c_str(),"a");
+ }
+ if (fFile) {
+ // now output
+ fputs(aLine,fFile);
+ fputs("\n",fFile);
+ }
+ // do required flushing
+ if (fFlushMode==dbgflush_openclose) {
+ // we need to close the file after every line of output
+ fclose(fFile);
+ fFile=NULL;
+ }
+ else if (aForceFlush || fFlushMode==dbgflush_flush) {
+ // simply flush
+ fflush(fFile);
+ }
+ }
+} // TStdFileDbgOut::putLine
+
+
+// write raw data to output file
+void TStdFileDbgOut::putRawData(cAppPointer aData, memSize aSize)
+{
+ if (fIsOpen) {
+ if (fFlushMode==dbgflush_openclose) {
+ // we need to open the file for append first
+ fFile=fopen(fFileName.c_str(),"a");
+ }
+ if (fFile) {
+ if (fwrite(aData, 1, aSize, fFile) != 1) {
+ // error ignored
+ }
+ }
+ // do required flushing
+ if (fFlushMode==dbgflush_openclose) {
+ // we need to close the file after every line of output
+ fclose(fFile);
+ fFile=NULL;
+ }
+ else if (fFlushMode==dbgflush_flush) {
+ // simply flush
+ fflush(fFile);
+ }
+ }
+} // TStdFileDbgOut::putRawData
+
+
+#endif
+
+
+// TConsoleDbgOut implementation
+// -----------------------------
+
+TConsoleDbgOut::TConsoleDbgOut()
+{
+ // init
+} // TStdFileDbgOut::TStdFileDbgOut
+
+
+// open standard C file based debug output channel
+bool TConsoleDbgOut::openDbg(cAppCharP aDbgOutputName, cAppCharP aSuggestedExtension, TDbgFlushModes aFlushMode, bool aOverWrite, bool aRawMode)
+{
+ if (fIsOpen) {
+ // first close
+ closeDbg();
+ }
+ // raw mode is not supported
+ if (!aRawMode)
+ fIsOpen=true;
+ // return false if we haven't been successful opening the channel
+ return fIsOpen;
+} // TConsoleDbgOut::openDbg
+
+
+// close standard C file based debug channel
+void TConsoleDbgOut::closeDbg(void)
+{
+ fIsOpen=false;
+} // TConsoleDbgOut::closeDbg
+
+
+// write single line to standard file based output channel
+void TConsoleDbgOut::putLine(cAppCharP aLine, bool aForceFlush)
+{
+ // if not open, just NOP
+ if (fIsOpen) {
+ CONSOLEPUTS(aLine);
+ }
+} // TConsoleDbgOut::putLine
+
+
+
+
+// TDebugLoggerBase implementation
+// -------------------------------
+
+// constructor
+TDebugLoggerBase::TDebugLoggerBase(GZones *aGZonesP) :
+ fGZonesP(aGZonesP)
+{
+ fDebugMask=0;
+ fDebugEnabled=true; // enabled by default
+ fNextDebugMask=0;
+ fDbgOutP=NULL;
+ fDbgOptionsP=NULL;
+ fIndent=0;
+ fBlockHistory=NULL; // no Block open yet
+ fOutStarted=false; // not yet started
+} // TDebugLoggerBase::TDebugLoggerBase
+
+
+// destructor
+TDebugLoggerBase::~TDebugLoggerBase()
+{
+ // make sure debug is finalized
+ DebugFinalizeOutput();
+ // make sure eventually left-over history elements are erased
+ while (fBlockHistory) {
+ TBlockLevel *bl=fBlockHistory;
+ fBlockHistory=bl->fNext;
+ delete bl;
+ }
+ // get rid of output object
+ if (fDbgOutP) delete fDbgOutP;
+ fDbgOutP=NULL;
+} // TDebugLoggerBase::TDebugLoggerBase
+
+
+// @brief convenience version for getting time
+lineartime_t TDebugLoggerBase::getSystemNowAs(timecontext_t aContext)
+{
+ return sysync::getSystemNowAs(aContext,fGZonesP);
+} // TDebugLoggerBase::getSystemNowAs
+
+
+// install outputter
+void TDebugLoggerBase::installOutput(TDbgOut *aDbgOutP)
+{
+ // get rid of eventually installed previous outputter
+ if (fDbgOutP) delete fDbgOutP;
+ fDbgOutP=aDbgOutP;
+} // TDebugLoggerBase::installOutput
+
+
+// output formatted text
+void TDebugLoggerBase::DebugVPrintf(uInt32 aDbgMask, cAppCharP aFormat, va_list aArgs)
+{
+ // we need a format and debug not completely off
+ if ((getMask() & aDbgMask)==aDbgMask && aFormat) {
+ const sInt16 maxmsglen=1024;
+ char msg[maxmsglen];
+ msg[0]='\0';
+ // assemble the message string
+ vsnprintf(msg, maxmsglen, aFormat, aArgs);
+ // write the string
+ DebugPuts(aDbgMask,msg);
+ }
+} // TDebugLoggerBase::DebugVPrintf
+
+
+// helper needed for maintaining old DEBUGPRINTFX() macro syntax
+TDebugLoggerBase &TDebugLoggerBase::setNextMask(uInt32 aDbgMask)
+{
+ fNextDebugMask=aDbgMask;
+ return *this;
+} // TDebugLoggerBase::setNextMask
+
+
+// like DebugPrintf(), but using mask previously set by setNextMask()
+void TDebugLoggerBase::DebugPrintfLastMask(cAppCharP aFormat, ...)
+{
+ va_list args;
+ // we need a format and debug not completely off
+ if ((getMask() & fNextDebugMask)==fNextDebugMask && aFormat) {
+ va_start(args, aFormat);
+ DebugVPrintf(fNextDebugMask,aFormat,args);
+ va_end(args);
+ }
+ fNextDebugMask=0;
+} // TDebugLoggerBase::DebugPrintfLastMask
+
+
+// output formatted text
+void TDebugLoggerBase::DebugPrintf(uInt32 aDbgMask, cAppCharP aFormat, ...)
+{
+ va_list args;
+ // we need a format and debug not completely off
+ if ((getMask() & aDbgMask)==aDbgMask && aFormat) {
+ va_start(args, aFormat);
+ DebugVPrintf(aDbgMask,aFormat,args);
+ va_end(args);
+ }
+} // TDebugLoggerBase::DebugVPrintf
+
+
+// open new Block with attribute list, printf style
+void TDebugLoggerBase::DebugOpenBlock(cAppCharP aBlockName, cAppCharP aBlockTitle, bool aCollapsed, cAppCharP aBlockFmt, ...)
+{
+ va_list args;
+ // we need a format and debug not completely off
+ if (getMask() && aBlockName) {
+ va_start(args, aBlockFmt);
+ DebugVOpenBlock(aBlockName,aBlockTitle,aCollapsed,aBlockFmt,args);
+ va_end(args);
+ }
+} // TDebugLoggerBase::DebugOpenBlock
+
+
+// open new Block with attribute list, printf style, expanded by default
+void TDebugLoggerBase::DebugOpenBlockExpanded(cAppCharP aBlockName, cAppCharP aBlockTitle, cAppCharP aBlockFmt, ...)
+{
+ va_list args;
+ // we need a format and debug not completely off
+ if (getMask() && aBlockName) {
+ va_start(args, aBlockFmt);
+ DebugVOpenBlock(aBlockName,aBlockTitle,false,aBlockFmt,args);
+ va_end(args);
+ }
+} // TDebugLoggerBase::DebugOpenBlockExpanded
+
+
+// open new Block with attribute list, printf style, collapsed by default
+void TDebugLoggerBase::DebugOpenBlockCollapsed(cAppCharP aBlockName, cAppCharP aBlockTitle, cAppCharP aBlockFmt, ...)
+{
+ va_list args;
+ // we need a format and debug not completely off
+ if (getMask() && aBlockName) {
+ va_start(args, aBlockFmt);
+ DebugVOpenBlock(aBlockName,aBlockTitle,true,aBlockFmt,args);
+ va_end(args);
+ }
+} // TDebugLoggerBase::DebugOpenBlockCollapsed
+
+
+// open new Block without attribute list
+void TDebugLoggerBase::DebugOpenBlock(cAppCharP aBlockName, cAppCharP aBlockTitle, bool aCollapsed)
+{
+ // we need a format and debug not completely off
+ if (getMask() && aBlockName) {
+ DebugVOpenBlock(aBlockName,aBlockTitle,aCollapsed,NULL,NULL);
+ }
+} // TDebugLoggerBase::DebugOpenBlock
+
+
+// output text to debug channel
+void TDebugLoggerBase::DebugPuts(uInt32 aDbgMask, cAppCharP aText, stringSize aTextSize, bool aPreFormatted)
+{
+ // we need a text and debug not completely off
+ if (!((getMask() & aDbgMask)==aDbgMask && aText && fDbgOptionsP)) {
+ // cannot output
+ //#ifdef __MWERKS__
+ //#warning "ugly hack"
+ DebugPutLine("<li><span class=\"error\">Warning: Dbg output system already half shut down (limited formatting)!</span></li><li>");
+ if (aText) DebugPutLine(aText);
+ DebugPutLine("</li>");
+ //#endif
+ }
+ else {
+ // make sure output is started
+ if (!fOutStarted) {
+ // try starting output
+ DebugStartOutput();
+ // disable debugging in this logger if starting output failed
+ // (prevents endless re-trying to open debug logs e.g. when log directory does not exist)
+ if (!fOutStarted) {
+ fDebugEnabled = false;
+ return; // stop all efforts here
+ }
+ }
+ // dissect into lines
+ cAppCharP end=aTextSize ? aText+aTextSize : NULL;
+ bool firstLine=true;
+ // check for preformatted message
+ bool pre=strnncmp(aText,"&pre;",5)==0;
+ if (pre) aText+=5;
+ pre = pre || aPreFormatted;
+ // now process text
+ while ((!end || aText<end) && *aText) {
+ // search for line end or end of string
+ cAppCharP p=aText;
+ while ((!end || p<end) && *p && *p!='\n' && *p!='\r') p++;
+ // output this line, properly formatted
+ string line;
+ line.erase();
+ cAppCharP q,s;
+ string ts;
+ switch (fDbgOptionsP->fOutputFormat) {
+ // HTML
+ case dbgfmt_html:
+ // prefix first line with <li>, second and further with <br/>
+ if (firstLine) {
+ line="<li>";
+ // add timestamp if needed for every line
+ if (fDbgOptionsP->fTimestampForAll || fDbgOptionsP->fThreadIDForAll) {
+ line+="<i>[";
+ #ifdef MULTI_THREAD_SUPPORT
+ if (fDbgOptionsP->fThreadIDForAll) {
+ StringObjAppendPrintf(line,"%09lu",myThreadID());
+ if (fDbgOptionsP->fTimestampForAll) line+=", ";
+ }
+ #endif
+ if (fDbgOptionsP->fTimestampForAll) {
+ StringObjTimestamp(ts,getSystemNowAs(TCTX_SYSTEM));
+ line+=ts;
+ }
+ line+="]</i>&nbsp;";
+ }
+ // colorize some messages
+ string cl="";
+ // colors, not mixable, most relevant first
+ if (aDbgMask & DBG_ERROR) {
+ cl="error";
+ }
+ else if (aDbgMask & DBG_EXOTIC) {
+ cl="exotic";
+ }
+ else if (aDbgMask & DBG_SCRIPTS) {
+ cl="script";
+ }
+ else if (aDbgMask & DBG_PLUGIN) {
+ cl="plugin";
+ }
+ else if (aDbgMask & DBG_DBAPI) {
+ cl="dbapi";
+ }
+ else if (aDbgMask & DBG_CONFLICT) {
+ cl="conflict";
+ }
+ else if (aDbgMask & DBG_MATCH) {
+ cl="match";
+ }
+ else if (aDbgMask & DBG_REMOTEINFO) {
+ cl="remote";
+ }
+ else if (aDbgMask & DBG_PROTO) {
+ cl="proto";
+ }
+ else if (aDbgMask & DBG_FILTER) {
+ cl="filter";
+ }
+ else if (aDbgMask & DBG_PARSE) {
+ cl="incoming";
+ }
+ else if (aDbgMask & DBG_GEN) {
+ cl="outgoing";
+ }
+ else if (aDbgMask & DBG_REST) {
+ cl="rest";
+ }
+ // apply basic color style
+ if (!cl.empty()) {
+ line+="<span class=\""; line+=cl; line+="\">";
+ }
+ // aditional style modifiers that can be combined with colors
+ if (aDbgMask & DBG_HOT) {
+ if (cl.empty())
+ line+="<span class=\"hotalone\">";
+ else
+ line+="<span class=\"hot\">";
+ }
+ // start preformatted if selected
+ if (pre)
+ line+="<pre>";
+ }
+ else {
+ if (!pre) line="<br/>";
+ }
+ goto xmlize;
+ // XML, just output and replace special chars as needed
+ case dbgfmt_xml:
+ if (firstLine) {
+ #ifdef MULTI_THREAD_SUPPORT
+ if (fDbgOptionsP->fThreadIDForAll) {
+ line+="<thread>";
+ StringObjAppendPrintf(line,"%09lu",myThreadID());
+ line+="</thread>";
+ }
+ #endif
+ if (fDbgOptionsP->fTimestampForAll) {
+ StringObjTimestamp(ts,getSystemNowAs(TCTX_SYSTEM));
+ line+="<time>";
+ line+=ts;
+ line+="</time>";
+ }
+ DebugPutLine(line.c_str(),line.size());
+ line.erase();
+ }
+ if (fDbgOptionsP->fSeparateMsgs) {
+ line+="<msg>";
+ }
+ xmlize:
+ q=aText;
+ s=q;
+ while (q<p) {
+ if (*q=='&') {
+ if (strucmp(q,"&html;",6)==0) {
+ if (q>s) line.append(s,q-s); // flush stuff scanned so far
+ // everything until next &html; does not need or want escaping, copy it as is
+ // - search next &html;
+ s=q=q+6;
+ while(*q && strucmp(q,"&html;",6)!=0) q++;
+ // - append everything between if we are in HTML mode
+ if (fDbgOptionsP->fOutputFormat==dbgfmt_html && q>s)
+ line.append(s,q-s);
+ s=q=q+6;
+ }
+ else if (strucmp(q,"&sp;",4)==0) {
+ if (q>s) line.append(s,q-s); // flush stuff scanned so far
+ // non-breaking space in HTML, normal space otherwise
+ if (fDbgOptionsP->fOutputFormat==dbgfmt_html)
+ line += "&nbsp;";
+ else
+ line += ' ';
+ s=q=q+4; // skip &sp;
+ }
+ else {
+ if (q>s) line.append(s,q-s);
+ line+="&amp;";
+ s=++q;
+ }
+ }
+ else if (*q=='<') {
+ if (q>s) line.append(s,q-s);
+ line+="&lt;";
+ s=++q;
+ }
+ else if (*q=='>') {
+ if (q>s) line.append(s,q-s);
+ line+="&gt;";
+ s=++q;
+ }
+ else {
+ q++;
+ }
+ }
+ if (q>s) line.append(s,q-s);
+ if (fDbgOptionsP->fSeparateMsgs && fDbgOptionsP->fOutputFormat==dbgfmt_xml) {
+ line+="</msg>";
+ }
+ break;
+ // plain text
+ default:
+ case dbgfmt_text:
+ q=aText;
+ s=q;
+ while (q<p) {
+ if (*q=='&') {
+ if (strucmp(q,"&html;",6)==0) {
+ if (q>s) line.append(s,q-s);
+ // everything until next &html; must be filtered out
+ // - search next &html;
+ s=q=q+6;
+ while(*q && strucmp(q,"&html;",6)!=0) q++;
+ s=q=q+6;
+ }
+ else if (strucmp(q,"&sp;",4)==0) {
+ if (q>s) line.append(s,q-s);
+ s=q=q+4;
+ line += ' '; // convert to plain space
+ }
+ else
+ q++;
+ }
+ else {
+ q++;
+ }
+ }
+ if (q>s) line.append(s,q-s);
+ break;
+ } // switch text output
+ firstLine=false;
+ // skip the lineend, if any
+ while (((!end || p<end) && *p=='\n') || *p=='\r') p++;
+ if (fDbgOptionsP->fOutputFormat==dbgfmt_html && ((end && p>=end) || *p==0)) {
+ // end preformatted
+ if (pre)
+ line+="</pre>";
+ // colors
+ if (aDbgMask & (
+ DBG_ERROR |
+ DBG_SCRIPTS |
+ DBG_REST |
+ DBG_EXOTIC |
+ DBG_DBAPI |
+ DBG_PLUGIN |
+ DBG_PARSE |
+ DBG_GEN |
+ DBG_CONFLICT |
+ DBG_MATCH |
+ DBG_REMOTEINFO |
+ DBG_PROTO |
+ DBG_FILTER
+ )) {
+ line+="</span>"; // end special style
+ }
+ // HOT modifier
+ if (aDbgMask & (
+ DBG_HOT
+ )) {
+ line+="</span>"; // end special style
+ }
+ line+="</li>"; // we need to close the list entry
+ }
+ DebugPutLine(line.c_str(),line.size(),pre);
+ // next line, if any
+ aText=p;
+ } // loop until all text done
+ }
+} // TDebugLoggerBase::DebugPuts
+
+
+// open new Block with attribute list, varargs passed
+void TDebugLoggerBase::DebugVOpenBlock(cAppCharP aBlockName, cAppCharP aBlockTitle, bool aCollapsed, cAppCharP aBlockFmt, va_list aArgs)
+{
+ if (!fDbgOptionsP)
+ return;
+ if (fDbgOptionsP->fFoldingMode==dbgfold_collapsed)
+ aCollapsed=true;
+ else if (fDbgOptionsP->fFoldingMode==dbgfold_expanded)
+ aCollapsed=false;
+ if (getMask() && aBlockName && fDbgOptionsP) {
+ // make sure output is started
+ if (!fOutStarted) DebugStartOutput();
+ // create Block line on current indent level
+ string bl;
+ string ts;
+ // - preamble, eventually with timestamp
+ bool withTime = fDbgOptionsP->fTimestampStructure;
+ if (withTime)
+ StringObjTimestamp(ts,getSystemNowAs(TCTX_SYSTEM));
+ switch (fDbgOptionsP->fOutputFormat) {
+ // XML
+ case dbgfmt_xml:
+ bl="<"; bl+=aBlockName;
+ if (withTime) {
+ bl+=" time=\"" + ts + "\"";
+ }
+ if (aBlockTitle) {
+ bl+=" title=\"";
+ bl+=aBlockTitle;
+ bl+="\"";
+ }
+ break;
+ // HTML
+ case dbgfmt_html:
+ bl="<li><span class=\"block\">";
+ if (fDbgOptionsP->fFoldingMode!=dbgfold_none) {
+ StringObjAppendPrintf(bl,
+ "<div id=\"E%ld\" style=\"display:%s\" class=\"exp\" onclick=\"exp('%ld')\">+</div><div id=\"C%ld\" style=\"display:%s\" class=\"coll\" onclick=\"coll('%ld')\">&ndash;</div>",
+ long(fBlockNo), aCollapsed ? "inline" : "none", long(fBlockNo),
+ long(fBlockNo), aCollapsed ? "none" : "inline", long(fBlockNo)
+ );
+ }
+ StringObjAppendPrintf(bl,"<a name=\"H%ld\">", long(fBlockNo));
+ if (withTime) {
+ bl+="[" + ts + "] ";
+ }
+ bl+="'";
+ bl+=aBlockName;
+ bl+="'";
+ if (aBlockTitle) {
+ bl+=" - ";
+ bl+=aBlockTitle;
+ }
+ bl+="</a></span><span class=\"attribute\">";
+ break;
+ // plain text
+ default:
+ case dbgfmt_text:
+ bl.erase();
+ if (!fDbgOptionsP->fTimestampForAll && withTime) { // avoid timestamp here if all lines get timestamped anyway
+ bl+="[" + ts + "] ";
+ }
+ bl+=aBlockName;
+ if (aBlockTitle) {
+ bl+=" - ";
+ bl+=aBlockTitle;
+ }
+ break;
+ } // switch preamble
+ // - attributes
+ if (aBlockFmt) {
+ // first expand all printf parameters
+ string attrs;
+ vStringObjPrintf(attrs,aBlockFmt,true,aArgs);
+ // isolate |-separated attribute format strings
+ cAppCharP q,r,s,p=attrs.c_str();
+ while (*p) {
+ // search for beginning of value
+ q=p;
+ while(*q && *q!='=' && *q!='|') q++;
+ // search for end of value
+ r=q;
+ s=q; // in case we don't have a =
+ if (*q=='=') {
+ s=q+1;
+ r=s;
+ while (*r && *r!='|') r++;
+ }
+ // now: p=start of attrname, q=end of attrname
+ // s=start of value, r=end of value
+ // output an attribute now
+ if (q>p && r>s) {
+ switch (fDbgOptionsP->fOutputFormat) {
+ // XML
+ case dbgfmt_xml:
+ bl+=" ";
+ bl.append(p,q-p);
+ bl+="=\"";
+ bl.append(s,r-s);
+ bl+="\"";
+ break;
+ case dbgfmt_html:
+ bl+=", ";
+ bl.append(p,q-p);
+ bl+="=<span class=\"attrval\">";
+ bl.append(s,r-s);
+ bl+="</span>";
+ break;
+ case dbgfmt_text:
+ default:
+ bl+=", ";
+ bl.append(p,q-p);
+ bl+="=";
+ bl.append(s,r-s);
+ break;
+ } // switch attribute
+ } // non-empty attribute
+ // more attributes to come?
+ if (*r=='|') r++; // skip separator
+ p=r;
+ } // while
+ } // attributes present
+ // - finalize Block
+ switch (fDbgOptionsP->fOutputFormat) {
+ // XML
+ case dbgfmt_xml:
+ bl+=">";
+ break;
+ // HTML
+ case dbgfmt_html:
+ bl+="</span>"; // end span for attributes
+ if (fDbgOptionsP->fFoldingMode!=dbgfold_none) {
+ StringObjAppendPrintf(bl,
+ "&nbsp;<span class=\"doall\" onclick=\"doall('%ld',true)\">[--]</span><span class=\"doall\" onclick=\"doall('%ld',false)\">[++]</span>",
+ long(fBlockNo), long(fBlockNo)
+ );
+ }
+ // link to end of block
+ StringObjAppendPrintf(bl,"&nbsp;<a class=\"jump\" href=\"#F%ld\">[->end]</a>", long(fBlockNo));
+ // link to start of enclosing block (if any)
+ if (fBlockHistory) {
+ StringObjAppendPrintf(bl,"&nbsp;<a class=\"jump\" href=\"#H%ld\">[->enclosing]</a>", long(fBlockHistory->fBlockNo));
+ }
+ // start div for content folding
+ if (fDbgOptionsP->fFoldingMode!=dbgfold_none) {
+ StringObjAppendPrintf(bl,
+ "<div class=\"blk\" id=\"B%ld\" style=\"display:%s\">",
+ long(fBlockNo),
+ aCollapsed ? "none" : "inline"
+ );
+ }
+ bl+="<ul>"; // now start list for block's contents
+ break;
+ // plain text
+ default:
+ case dbgfmt_text:
+ break;
+ } // switch preamble
+ // now output Block line (on current indent level)
+ DebugPutLine(bl.c_str(), bl.size());
+ // increase indent level (applies to all Block contents)
+ fIndent++;
+ // save Block on stack
+ TBlockLevel *newLevel = new TBlockLevel;
+ newLevel->fBlockName=aBlockName;
+ newLevel->fNext=fBlockHistory;
+ newLevel->fBlockNo=fBlockNo++; // save block number to reference block in collapse box at end of block
+ fBlockHistory=newLevel; // insert new level at start of list
+ }
+} // TDebugLoggerBase::DebugVOpenBlock
+
+
+// close named Block. If no name given, topmost Block will be closed
+void TDebugLoggerBase::DebugCloseBlock(cAppCharP aBlockName)
+{
+ if (fOutStarted && getMask() && fDbgOptionsP && fBlockHistory) {
+ if (aBlockName==NULL) {
+ #if SYDEBUG>1
+ internalCloseBlocks(fBlockHistory->fBlockName.c_str(),"Block Nest Warning: Missing Block name at close");
+ #else
+ internalCloseBlocks(fBlockHistory->fBlockName.c_str(),NULL);
+ #endif
+ }
+ else {
+ internalCloseBlocks(aBlockName,NULL);
+ }
+ }
+} // TDebugLoggerBase::DebugCloseBlock
+
+
+
+// internal helper used to close all or some Blocks
+void TDebugLoggerBase::internalCloseBlocks(cAppCharP aBlockName, cAppCharP aCloseComment)
+{
+ if (!fDbgOptionsP) return; // security
+ bool withTime = fDbgOptionsP->fTimestampStructure;
+ string comment;
+ #if SYDEBUG>1
+ if (!fBlockHistory && aBlockName) {
+ // no blocks open any more and not close-all-remaining call (log close...)
+ DebugPrintf(DBG_EXOTIC+DBG_ERROR,"Block Nest Warning: Trying to close block '%s', but no block is open",aBlockName);
+ }
+ #endif
+ while (fBlockHistory) {
+ // prepare comment
+ comment.erase();
+ if (aCloseComment) {
+ comment += " - ";
+ comment += aCloseComment;
+ }
+ // check if closing top-of-stack Block now
+ bool found=
+ (aBlockName && strucmp(aBlockName,fBlockHistory->fBlockName.c_str())==0);
+ if (!found && fBlockHistory->fNext==NULL) {
+ // last Block always counts as "found"...
+ found = true;
+ #if SYDEBUG>1
+ // ...but issue warning as name is not what we would have expected
+ StringObjAppendPrintf(comment, " - Block Nest Warning: closing '%s', but expected '%s'",aBlockName ? aBlockName : "<unknown>", fBlockHistory->fBlockName.c_str());
+ #endif
+ }
+ // now close topmost Block
+ string ts,bl;
+ // - get time if needed and eventually put it within indented block
+ if (withTime) {
+ StringObjTimestamp(ts,getSystemNowAs(TCTX_SYSTEM));
+ // for XML, the time must be shown before the close tag on a separate line
+ if (fDbgOptionsP->fOutputFormat == dbgfmt_xml) {
+ StringObjPrintf(bl,"<endblock time=\"%s\"/>",ts.c_str());
+ DebugPutLine(bl.c_str(), bl.size()); // still within block, indented
+ }
+ }
+ // - now unindent
+ if (fIndent>0) fIndent--;
+ // - then create closing Block
+ #if SYDEBUG>1
+ if (!found) StringObjAppendPrintf(comment," - Block Nest Warning: implicitly closed (by explicitly closing '%s')",aBlockName ? aBlockName : "<unknown parent>");
+ #endif
+ switch (fDbgOptionsP->fOutputFormat) {
+ // XML
+ case dbgfmt_xml:
+ bl="</";
+ bl+=fBlockHistory->fBlockName;
+ bl+=">";
+ if (!comment.empty()) {
+ bl+=" <!-- ";
+ bl+=comment;
+ bl+=" -->";
+ }
+ break;
+ // HTML
+ case dbgfmt_html:
+ bl="</ul><span class=\"block\">"; // end of content list
+ if (fDbgOptionsP->fFoldingMode!=dbgfold_none) {
+ StringObjAppendPrintf(bl,
+ "<span class=\"coll\" onclick=\"coll('%ld')\">&ndash;</span>",
+ long(fBlockHistory->fBlockNo)
+ );
+ }
+ StringObjAppendPrintf(bl,"<a name=\"F%ld\">",long(fBlockHistory->fBlockNo));
+ if (withTime) {
+ bl+="[" + ts + "] ";
+ }
+ bl += "End of '";
+ bl+=fBlockHistory->fBlockName;
+ bl+="'";
+ bl+=comment;
+ bl+="</a></span>";
+ // link to top of block
+ StringObjAppendPrintf(bl,"&nbsp;<a class=\"jump\" href=\"#H%ld\">[->top]</a>",long(fBlockHistory->fBlockNo));
+ // link to end of enclosing block (if any)
+ if (fBlockHistory->fNext) {
+ StringObjAppendPrintf(bl,"&nbsp;<a class=\"jump\" href=\"#F%ld\">[->enclosing]</a>",long(fBlockHistory->fNext->fBlockNo));
+ }
+ if (fDbgOptionsP->fFoldingMode!=dbgfold_none) {
+ bl+="</div>"; // end of folding division
+ }
+ bl+="</li>"; // end of list entry containing entire block
+ break;
+ // plain text
+ default:
+ case dbgfmt_text:
+ bl.erase();
+ if (!fDbgOptionsP->fTimestampForAll && withTime) { // avoid timestamp here if all lines get timestamped anyway
+ bl+="[" + ts + "] ";
+ }
+ bl+="End of '";
+ bl+=fBlockHistory->fBlockName;
+ bl+="'";
+ bl+=comment;
+ break;
+ } // switch Block close
+ // - output closing Block line
+ DebugPutLine(bl.c_str(), bl.size());
+ // - remove Block level
+ TBlockLevel *closedLevel = fBlockHistory;
+ fBlockHistory = closedLevel->fNext;
+ delete closedLevel;
+ // if we have found the Block, exit here
+ if (found) break;
+ }
+} // TDebugLoggerBase::internalCloseBlocks
+
+
+// start debugging output if needed and sets fOutStarted
+void TDebugLoggerBase::DebugStartOutput(void)
+{
+ if (!fOutStarted && fDbgOptionsP && fDbgOutP && !fDbgPath.empty()) {
+ // try to open the debug channel (force to openclose if we have multiple threads mixed in one file)
+ if (fDbgOutP->openDbg(
+ fDbgPath.c_str(),
+ DbgOutFormatExtensions[fDbgOptionsP->fOutputFormat],
+ fDbgOptionsP->fSubThreadMode==dbgsubthread_linemix ? dbgflush_openclose : fDbgOptionsP->fFlushMode,
+ !fDbgOptionsP->fAppend
+ )) {
+ // make sure we don't recurse when we produce some output
+ fOutStarted=true;
+ fIndent=0; // reset to make sure
+ // create a block number that is unique in the file, even if we append multiple times.
+ // We assume that a block consumes at least 256 bytes, so size_of_file/256 always gets
+ // an unused block ID within that file
+ // 256 is a safe assumption because the "fold" button <divs> alone are around 250 bytes
+ fBlockNo = 1 + (fDbgOutP->dbgFileSize()/256);
+ // now create required prefix
+ DebugPutLine(fDbgOptionsP->fCustomPrefix.empty() ? DbgOutDefaultPrefixes[fDbgOptionsP->fOutputFormat] : fDbgOptionsP->fCustomPrefix.c_str());
+ // add folding javascript if needed
+ if (fDbgOptionsP->fOutputFormat==dbgfmt_html && fDbgOptionsP->fFoldingMode!=dbgfold_none) {
+ DebugPutLine(FoldingPrefix);
+ }
+ } // debug channel opened successfully
+ } // environment ready to start output
+} // TDebugLoggerBase::DebugStartOutput
+
+
+// @brief finalize debugging output (close Blocks, close output channel)
+void TDebugLoggerBase::DebugFinalizeOutput(void)
+{
+ if (fOutStarted && fDbgOptionsP && fDbgOutP) {
+ // close all left-open open Blocks
+ internalCloseBlocks(NULL,"closed because log ends here");
+ // now finalize output
+ // - special stuff before
+ if (fDbgOptionsP->fOutputFormat == dbgfmt_xml)
+ fIndent=0; // unindent to zero (document is not a real Block)
+ // - then suffix
+ DebugPutLine(fDbgOptionsP->fCustomSuffix.empty() ? DbgOutDefaultSuffixes[fDbgOptionsP->fOutputFormat] : fDbgOptionsP->fCustomSuffix.c_str());
+ // now close the debug channel
+ fDbgOutP->closeDbg();
+ }
+ // whatever happened, we are not started any more
+ fOutStarted=false;
+} // TDebugLoggerBase::DebugFinalizeOutput
+
+
+// Output single line to debug channel (includes indenting and other prefixing, but no further formatting)
+void TDebugLoggerBase::DebugPutLine(cAppCharP aText, stringSize aTextSize, bool aPre)
+{
+ if (!aText || !fDbgOutP) return;
+ if (*aText) {
+ // not an empty line
+ string msg;
+ msg.erase();
+ // prefix with timestamp if selected in text format
+ if (fDbgOptionsP && fDbgOptionsP->fOutputFormat==dbgfmt_text && fDbgOptionsP->fTimestampForAll) {
+ // prefix each line (before the indent!) with a timestamp
+ string ts;
+ StringObjTimestamp(ts,getSystemNowAs(TCTX_SYSTEM));
+ msg='[';
+ msg+=ts;
+ msg+="] ";
+ }
+ // Indent if selected
+ if (fDbgOptionsP && !fDbgOptionsP->fIndentString.empty() && !(fDbgOptionsP->fOutputFormat==dbgfmt_html && aPre)) {
+ // with indent
+ for (uInt16 n=0; n<fIndent; n++) {
+ msg+=fDbgOptionsP->fIndentString;
+ }
+ }
+ // add message itself
+ if (aTextSize)
+ msg.append(aText,aTextSize);
+ else
+ msg.append(aText);
+ // now output
+ fDbgOutP->putLine(msg.c_str(),false); // %%% no forceflush for now
+ }
+} // TDebugLoggerBase::DebugPutLine
+
+
+// TDebugLogger implementation
+// ---------------------------
+
+// constructor
+TDebugLogger::TDebugLogger(GZones *aGZonesP) :
+ inherited(aGZonesP)
+{
+ #ifdef MULTI_THREAD_SUPPORT
+ fMainThreadID=0;
+ fSubThreadLogs=NULL;
+ fSilentLoggerP=NULL;
+ #endif
+} // TDebugLogger::TDebugLogger
+
+
+// destructor
+TDebugLogger::~TDebugLogger()
+{
+ #ifdef MULTI_THREAD_SUPPORT
+ // remove subthread loggers
+ TSubThreadLog* subThreadP = fSubThreadLogs;
+ fSubThreadLogs = NULL;
+ while (subThreadP) {
+ // delete logger if any
+ if (subThreadP->fSubThreadLogger) {
+ SYSYNC_TRY {
+ delete subThreadP->fSubThreadLogger;
+ }
+ SYSYNC_CATCH(...)
+ // nop
+ SYSYNC_ENDCATCH
+ }
+ TSubThreadLog* delP = subThreadP;
+ subThreadP = subThreadP->fNext;
+ delete delP;
+ }
+ //
+ if (fSilentLoggerP) {
+ delete fSilentLoggerP;
+ fSilentLoggerP = NULL;
+ }
+ #endif
+} // TDebugLogger::~TDebugLogger
+
+
+#ifdef MULTI_THREAD_SUPPORT
+
+/// @brief find (and possibly delete) subthread record
+/// @param aAndRemove[in] if set, the subthread record will be removed in a thread safe way
+/// IF AND ONLY IF aThreadID is the calling thread (i.e. only own thread may be removed from the list)!
+/// Note that the caller must take care of deleting the subthread record
+TSubThreadLog *TDebugLogger::findSubThread(uInt32 aThreadID, bool aAndRemove)
+{
+ TSubThreadLog* subThreadP = fSubThreadLogs;
+ TSubThreadLog** subThreadLinkPP = &fSubThreadLogs;
+ while (subThreadP) {
+ if (subThreadP->fThreadID == aThreadID) {
+ if (aAndRemove) {
+ // bridge previous with next in one single assignment (i.e. thread safe)
+ *subThreadLinkPP = subThreadP->fNext;
+ }
+ // return found record (note that it MUST BE DELETED by caller if no longer used)
+ return subThreadP;
+ }
+ subThreadLinkPP = &subThreadP->fNext;
+ subThreadP = *subThreadLinkPP;
+ }
+ return NULL; // none found
+} // TDebugLogger::findSubThread
+
+
+/// @brief find or create logger for subthread
+TDebugLoggerBase *TDebugLogger::getThreadLogger(bool aCreateNew)
+{
+ if (!fDbgOptionsP || fDbgOptionsP->fSubThreadMode==dbgsubthread_none)
+ return this; // no options, do not handle subthreads specially
+ uInt32 threadID = myThreadID();
+ if (fDbgOptionsP->fSubThreadMode==dbgsubthread_linemix || threadID==fMainThreadID) {
+ // In line mix and for mainthread - I am the logger for this thread!
+ return this;
+ }
+ TSubThreadLog* subThreadP = findSubThread(threadID);
+ if (subThreadP) {
+ // we know this subthread, return its logger
+ return subThreadP->fSubThreadLogger; // can be NULL if subthread logging is disabled
+ }
+ // unknown subthread
+ if (fMainThreadID==0) {
+ // no current mainthread, let subthread write to main log
+ // Note: this makes sure log info eventually trailing the DebugThreadOutputDone()
+ // also lands in the main log. This is not critical - the only thing that must be
+ // ensured is that starting new threads is made only with DebugDefineMainThread set.
+ return this;
+ }
+ // new subthread, create entry in list
+ if (aCreateNew) {
+ string s;
+ // create new entry
+ subThreadP = new TSubThreadLog;
+ subThreadP->fThreadID=threadID;
+ subThreadP->fNext=fSubThreadLogs; // link current list behind this new entry
+ // create logger for the thread (or none)
+ switch (fDbgOptionsP->fSubThreadMode) {
+ case dbgsubthread_separate:
+ // separate file for subthread output
+ // - create new base logger
+ subThreadP->fSubThreadLogger = new TDebugLoggerBase(fGZonesP);
+ // - install output (copy)
+ subThreadP->fSubThreadLogger->installOutput(fDbgOutP->clone());
+ // - same options
+ subThreadP->fSubThreadLogger->setOptions(getOptions());
+ // - inherit current mask/enable
+ subThreadP->fSubThreadLogger->setMask(getMask());
+ subThreadP->fSubThreadLogger->setEnabled(fDebugEnabled);
+ // - debug path is same as myself plus Thread ID
+ subThreadP->fSubThreadLogger->setDebugPath(fDbgPath.c_str());
+ StringObjPrintf(s,"_%lu",threadID);
+ subThreadP->fSubThreadLogger->appendToDebugPath(s.c_str());
+ break;
+ case dbgsubthread_suppress:
+ default:
+ // no output from subthreads
+ subThreadP->fSubThreadLogger=NULL; // no logger
+ break;
+ }
+ // now activate by linking it at top of list (this is thread safe)
+ fSubThreadLogs = subThreadP;
+ // return the logger
+ return subThreadP->fSubThreadLogger;
+ }
+ return NULL; // no logger for this thread
+} // TDebugLogger::getThreadLogger
+
+
+// helper needed for maintaining old DEBUGPRINTFX() macro syntax
+TDebugLoggerBase &TDebugLogger::setNextMask(uInt32 aDbgMask)
+{
+ TDebugLoggerBase *loggerP = getThreadLogger();
+ if (loggerP) {
+ // return pointer to loggerbase whose DebugPrintfLastMask() must be called
+ return loggerP->inherited::setNextMask(aDbgMask);
+ }
+ else {
+ // we have no logger but still need to return something
+ if (!fSilentLoggerP) {
+ fSilentLoggerP = new TDebugLoggerBase(fGZonesP);
+ fSilentLoggerP->setEnabled(false);
+ }
+ fSilentLoggerP->setNextMask(DBG_ERROR); // must set non-zero to make sure it is NOT output!
+ return *fSilentLoggerP;
+ }
+} // TDebugLoggerBase::setNextMask
+
+
+// output text to debug channel, with checking for subthreads
+void TDebugLogger::DebugPuts(uInt32 aDbgMask, cAppCharP aText, stringSize aTextSize, bool aPreFormatted)
+{
+ TDebugLoggerBase *loggerP = getThreadLogger();
+ if (loggerP) loggerP->inherited::DebugPuts(aDbgMask,aText,aTextSize,aPreFormatted);
+} // TDebugLogger::DebugPuts
+
+
+void TDebugLogger::DebugVPrintf(uInt32 aDbgMask, cAppCharP aFormat, va_list aArgs)
+{
+ TDebugLoggerBase *loggerP = getThreadLogger();
+ if (loggerP) loggerP->inherited::DebugVPrintf(aDbgMask,aFormat,aArgs);
+} // TDebugLogger::DebugVPrintf
+
+
+void TDebugLogger::DebugVOpenBlock(cAppCharP aBlockName, cAppCharP aBlockTitle, bool aCollapsed, cAppCharP aBlockFmt, va_list aArgs)
+{
+ TDebugLoggerBase *loggerP = getThreadLogger();
+ if (loggerP) loggerP->inherited::DebugVOpenBlock(aBlockName, aBlockTitle, aCollapsed, aBlockFmt, aArgs);
+} // TDebugLogger::DebugVOpenBlock
+
+
+void TDebugLogger:: DebugCloseBlock(cAppCharP aBlockName)
+{
+ TDebugLoggerBase *loggerP = getThreadLogger();
+ if (loggerP) loggerP->inherited::DebugCloseBlock(aBlockName);
+} // TDebugLogger::DebugCloseBlock
+
+#endif
+
+
+// output all buffered subthread's output in a special subthread Block in the main output
+void TDebugLogger::DebugShowSubThreadOutput(void)
+{
+ #ifdef MULTI_THREAD_SUPPORT
+ // nop as long mixed-block mode is not implemented
+ #endif
+} // TDebugLogger::DebugShowSubThreadOutput
+
+
+// the calling thread signals that it is done with doing output for now. If the main
+// thread is doing this and we have bufferandmix mode, the next subthread will be allowed
+// to write into the output channel until a new main thread gains control via
+// DebugDefineMainThread();
+void TDebugLogger::DebugThreadOutputDone(bool aRemoveIt)
+{
+ #ifdef MULTI_THREAD_SUPPORT
+ uInt32 threadID = myThreadID();
+ if (threadID==fMainThreadID) {
+ // current main thread done
+ fMainThreadID = 0;
+ }
+ // for session logs, subthreads are usually left in the list at this time (aRemoveIt==false)
+ // (as they will get deleted with the session logger later anyway)
+ if (aRemoveIt) {
+ TSubThreadLog* tP = findSubThread(threadID,true);
+ if (tP) {
+ if (tP->fSubThreadLogger) {
+ SYSYNC_TRY {
+ delete tP->fSubThreadLogger;
+ }
+ SYSYNC_CATCH(...)
+ // nop
+ SYSYNC_ENDCATCH
+ }
+ delete tP;
+ }
+ }
+ #endif
+} // TDebugLogger::DebugThreadOutputDone
+
+
+// Used to regain control as main thread (e.g. for the next request of a session which
+// eventually occurs from another thread).
+void TDebugLogger::DebugDefineMainThread(void)
+{
+ #ifdef MULTI_THREAD_SUPPORT
+ uInt32 threadID = myThreadID();
+ // if this is already the main thread, no op
+ if (threadID == fMainThreadID)
+ return; // nop, done
+ // thread is not the current main thread
+ // - search if it is a registered subthread
+ TSubThreadLog *subThreadP = findSubThread(threadID);
+ if (fMainThreadID==0) {
+ // no main thread currently registered
+ if (subThreadP!=NULL) {
+ // this is not a new thread, but a known subthread, can't get main thread now
+ return; // no further op
+ }
+ else {
+ // this is not a known subthread, so it can become the main thread
+ fMainThreadID = threadID;
+ return; // done
+ }
+ }
+ else {
+ // cannot become main thread, will be treated as subthread if it generates output
+ // - no op required
+ }
+ #endif
+} // TDebugLogger::DebugDefineMainThread
+
+
+} // namespace sysync
+
+#endif // SYDEBUG
+
+// eof
+
diff --git a/src/sysync/debuglogger.h b/src/sysync/debuglogger.h
new file mode 100755
index 0000000..b66414a
--- /dev/null
+++ b/src/sysync/debuglogger.h
@@ -0,0 +1,381 @@
+/*
+ * File: debuglogger.h
+ *
+ * Author: Lukas Zeller (luz@synthesis.ch)
+ *
+ * Global debug mechanisms
+ *
+ * Copyright (c) 2005-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ * 2005-08-04 : luz : created
+ *
+ */
+
+#ifndef DEBUGLOGGER_H
+#define DEBUGLOGGER_H
+
+#ifdef SYDEBUG
+
+#include "generic_types.h"
+#include "sysync.h"
+
+namespace sysync {
+
+
+/// @brief Debug output formats
+typedef enum {
+ dbgfmt_text, ///< plain text format (but can be indented)
+ dbgfmt_xml, ///< XML format
+ dbgfmt_html, ///< HTML format
+ numDbgOutFormats
+} TDbgOutFormats;
+
+/// @brief HTML dynamic folding
+typedef enum {
+ dbgfold_none, ///< do not include dynamic folding into HTML logs
+ dbgfold_collapsed, ///< include folding - all collapsed by default
+ dbgfold_expanded, ///< include folding - all expanded by default
+ dbgfold_auto, ///< include folding - collapse/expand state predefined on a block-by-block basis
+ numDbgFoldingModes
+} TDbgFoldingModes;
+
+/// @brief Debug flush modes
+typedef enum {
+ dbgflush_none, ///< no flush, keep open as long as possible
+ dbgflush_flush, ///< flush every debug message
+ dbgflush_openclose, ///< open and close debug channel separately for every message (as in 2.x engine)
+ numDbgFlushModes
+} TDbgFlushModes;
+
+/// @brief Debug subthread logging modes
+typedef enum {
+ dbgsubthread_none, ///< do not handle output from subthread specially
+ dbgsubthread_suppress, ///< suppress output from subthreads
+ dbgsubthread_separate, ///< create separate output stream (=file) for each subthread
+ dbgsubthread_linemix, ///< mix output on a line by line basis (forcing output to slow openclose mode)
+ dbgsubthread_bufferandmix, ///< buffer thread's output and mix it into main stream when appropriate
+ numDbgSubthreadModes
+} TDbgSubthreadModes;
+
+
+#ifndef HARDCODED_CONFIG
+extern cAppCharP const DbgOutFormatNames[numDbgOutFormats];
+extern cAppCharP const DbgFoldingModeNames[numDbgFoldingModes];
+extern cAppCharP const DbgFlushModeNames[numDbgFlushModes];
+extern cAppCharP const DbgSubthreadModeNames[numDbgSubthreadModes];
+#endif
+extern cAppCharP const DbgOutFormatExtensions[numDbgOutFormats];
+
+/// @brief Debug options container
+class TDbgOptions {
+public:
+ // constructor
+ TDbgOptions();
+ // methods
+ void clear(void);
+ // properties
+ TDbgOutFormats fOutputFormat; ///< format
+ string fIndentString; ///< indent string
+ string fCustomPrefix; ///< custom prefix (different xml header or html with different styles for example)
+ string fCustomSuffix; ///< custom suffix (should match prefix)
+ bool fSeparateMsgs; ///< separate message lines (needed especially in XML to avoid unformatted PCDATA block)
+ bool fTimestampStructure; ///< include timestamp for structure elements (blocks)
+ bool fTimestampForAll; ///< include timestamp information for every message
+ bool fThreadIDForAll; ///< include thread ID information for every message
+ TDbgFlushModes fFlushMode; ///< how and when to flush
+ TDbgFoldingModes fFoldingMode; ///< if and how to fold HTML output
+ bool fAppend; ///< if set, existing debug files will not be overwritten, but appended to
+ TDbgSubthreadModes fSubThreadMode; ///< how to handle debug messages from subthreads
+ uInt32 fSubThreadBufferMax; ///< how much to buffer for subthread maximally
+}; // TDbgOptions
+
+
+/// @brief Debug output channel
+class TDbgOut {
+ // construction/destruction
+private:
+ bool fDestructed; // flag which will be set once destruct() has been called - by the outermost derivate's destructor
+public:
+ TDbgOut();
+ virtual ~TDbgOut();
+ virtual void doDestruct(void); // will be called by destruct, derived must call inherited if they implement it
+ void destruct(void); // to be called by ALL destructors of derivates.
+ // methods
+ /// @brief duplicate output channel
+ virtual TDbgOut *clone(void) { return new TDbgOut; };
+ /// @brief open debug output channel
+ /// Notes:
+ /// - Autocloses current channel if already open
+ /// - May not actually open the channel, but should test if channel is writable
+ /// @return false if debug channel cannot be opened and written to
+ /// @param aDbgOutputName[in] name (usually file name) of debug output channel
+ /// @param aSuggestedExtension[in] file extension suggested (may not be used depending on channel type)
+ /// @param aFlushMode[in] flush mode to be used on the channel
+ /// @param aOverWrite[in] if true, debug output channel (=file) will be overwritten (otherwise: appended to)
+ /// @param aRawMode[in] if true, debug output channel (=file) is opened in binary raw mode (for message dumps etc.)
+ virtual bool openDbg(cAppCharP aDbgOutputName, cAppCharP aSuggestedExtension, TDbgFlushModes aFlushMode, bool aOverWrite, bool aRawMode=false) { return true; };
+ /// @brief get current size of output file
+ /// @return number of bytes, 0 if file is empty or for non-files (like console)
+ virtual uInt32 dbgFileSize(void) { return 0; };
+ /// @brief close and flush all log output
+ virtual void closeDbg(void) { /* nop */ };
+ /// @brief write single line to debug output channel
+ /// @param aLine[in] text for line to be written out (must not contain line ends)
+ /// @param aForceFlush[in] if true, debug output will be flushed to permanent storage regardless of current flush mode
+ virtual void putLine(cAppCharP aLine, bool aForceFlush) { /* nop */};
+ /// @brief write raw data to debug output channel (usually makes sense only when channel is opened in raw mode)
+ /// @param aData[in] pointer to data to be written
+ /// @param aSize[in] size in bytes of data block at aData to be written
+ virtual void putRawData(cAppPointer aData, memSize aSize) { /* nop */};
+protected:
+ bool fIsOpen;
+}; // TDbgOut
+
+
+
+#ifndef NO_C_FILES
+
+/// @brief Standard file debug output channel
+class TStdFileDbgOut : public TDbgOut {
+ typedef TDbgOut inherited;
+public:
+ // constructor/destructor
+ TStdFileDbgOut();
+ virtual ~TStdFileDbgOut();
+ // methods
+ virtual TDbgOut *clone(void) { return new TStdFileDbgOut; };
+ virtual bool openDbg(cAppCharP aDbgOutputName, cAppCharP aSuggestedExtension, TDbgFlushModes aFlushMode, bool aOverWrite, bool aRawMode=false);
+ virtual uInt32 dbgFileSize(void);
+ virtual void closeDbg(void);
+ virtual void putLine(cAppCharP aLine, bool aForceFlush);
+ virtual void putRawData(cAppPointer aData, memSize aSize);
+private:
+ TDbgFlushModes fFlushMode;
+ string fFileName;
+ FILE * fFile;
+}; // TStdFileDbgOut
+
+#endif
+
+
+/// @brief Output to console
+class TConsoleDbgOut : public TDbgOut {
+ typedef TDbgOut inherited;
+public:
+ // constructor/destructor
+ TConsoleDbgOut();
+ // methods
+ virtual TDbgOut *clone(void) { return new TConsoleDbgOut; };
+ virtual bool openDbg(cAppCharP aDbgOutputName, cAppCharP aSuggestedExtension, TDbgFlushModes aFlushMode, bool aOverWrite, bool aRawMode=false);
+ virtual void closeDbg(void);
+ virtual void putLine(cAppCharP aLine, bool aForceFlush);
+ virtual void putRawData(cAppPointer aData, memSize aSize) { /* not supported on console, just NOP */ };
+}; // TConsoleDbgOut
+
+
+// Debug logger class
+// ------------------
+
+/// @brief hierachical block history
+typedef struct BlockLevel {
+ string fBlockName;
+ uInt32 fBlockNo;
+ struct BlockLevel *fNext;
+} TBlockLevel;
+
+class TDebugLogger;
+class GZones;
+
+/// @brief Debug logger base class (without subthread handling)
+class TDebugLoggerBase {
+public:
+ // constructor/destructor
+ TDebugLoggerBase(GZones *aGZonesP);
+ virtual ~TDebugLoggerBase();
+ // methods
+ /// @brief install output channel handler object (and pass it's ownership!)
+ /// @param aDbgOutP[in] output channel to be used for this logger (will be owned and finally destroyed by the logger)
+ void installOutput(TDbgOut *aDbgOutP);
+ /// @brief set debug options
+ void setOptions(const TDbgOptions *aDbgOptionsP) { fDbgOptionsP=aDbgOptionsP; };
+ /// @brief get debug options pointer
+ const TDbgOptions *getOptions(void) { return fDbgOptionsP; };
+ // @brief convenience version for getting time
+ lineartime_t getSystemNowAs(timecontext_t aContext);
+ /// @brief get current debug mask for this logger.
+ /// Note that setEnabled(false) will cause this to return 0 even if the mask itself is non-zero
+ uInt32 getMask(void) { return fDebugEnabled ? fDebugMask : 0; };
+ uInt32 getRealMask(void) { return fDebugMask; };
+ /// @brief set new debug mask for this logger
+ void setMask(uInt32 aDbgMask)
+ { fDebugMask=aDbgMask; };
+ /// @brief enable or disable this logger (but leave dbgMask intact)
+ void setEnabled(bool aEnabled)
+ { fDebugEnabled=aEnabled; };
+ /// @brief set debug output path + filename (no extension, please)
+ void setDebugPath(cAppCharP aPath) { fDbgPath = aPath; };
+ /// @brief append to debug output path + filename (no extension, please)
+ void appendToDebugPath(cAppCharP aPathElement) { fDbgPath += aPathElement; };
+ /// @brief get debug output file path (w/o extension)
+ cAppCharP getDebugPath(void) { return fDbgPath.c_str(); };
+ /// @brief get debug output file name (w/o path or extension)
+ cAppCharP getDebugFilename(void) { size_t n=fDbgPath.find_last_of("\\/:"); return fDbgPath.c_str()+(n!=string::npos ? n+1 : 0); };
+ /// @brief get debug output file extension
+ cAppCharP getDebugExt(void) { return fDbgOptionsP ? DbgOutFormatExtensions[fDbgOptionsP->fOutputFormat] : ""; };
+ // - normal output
+ /// @brief Write text to debug output channel.
+ /// Notes:
+ /// - Line will be terminated by linefeed automatically (no need to include a linefeed for single line message)
+ /// - \n chars can be used to separate multi-line output. Formatter will take care that
+ /// all lines are equally indented/formatted/prefixed
+ /// @param aDbgMask debug mask, bits set here must be set in the debuglogger's own mask in order to display the debug text
+ /// @param aText[in] text to be written out
+ /// @param aTextSize[in] if>0, this is the maximum number of chars to output from aText
+ virtual void DebugPuts(uInt32 aDbgMask, cAppCharP aText, stringSize aTextSize=0, bool aPreFormatted=false);
+ /// @brief Write formatted text to debug output channel.
+ /// @param aDbgMask debug mask, bits set here must be set in the debuglogger's own mask in order to display the debug text
+ /// @param aFormat[in] format text in vprintf style to be written out
+ /// @param aArgs[in] varargs in vprintf style
+ virtual void DebugVPrintf(uInt32 aDbgMask, cAppCharP aFormat, va_list aArgs);
+ /// @brief Write formatted text to debug output channel.
+ /// @param aDbgMask debug mask, bits set here must be set in the debuglogger's own mask in order to display the debug text
+ /// @param aFormat[in] format text in printf style to be written out
+ void DebugPrintf(uInt32 aDbgMask, cAppCharP aFormat, ...)
+ #ifdef __GNUC__
+ __attribute__((format(printf, 3, 4)))
+#endif
+ ;
+ /// @brief set debug mask to be used for next DebugPrintfLastMask() call
+ /// @param aDbgMask debug mask, bits set here must be set in the debuglogger's own mask in order to display the debug text
+ virtual TDebugLoggerBase &setNextMask(uInt32 aDbgMask);
+ /// @brief like DebugPrintf(), but using mask previously set by setNextMask()
+ /// @param aFormat[in] format text in printf style to be written out
+ void DebugPrintfLastMask(cAppCharP aFormat, ...)
+#ifdef __GNUC__
+ __attribute__((format(printf, 2, 3)))
+#endif
+ ;
+ // - Blocks
+ /// @brief Open structure Block. Depending on the output format, this will generate indent, XML tags, HTML headers etc.
+ /// @param aBlockName[in] Name of Block. Will be used e.g. for tag name in XML. Intention is to group similar entities with the same BlockName
+ /// @param aBlockTitle[in] Title (descriptive text) of Block.
+ /// @param aCollapsed[in] If set, and folding mode is auto, block will be initially collapsed when log is opened in browser.
+ /// @param aBlockFmt[in] Format string for additional Block info. Should contain one or multiple tag=value pairs, separated by the pipe char |.
+ /// This will be used to generate XML attributes or other identifiers.
+ /// @param aArgs[in] varargs in vprintf style for aBlockFmt
+ virtual void DebugVOpenBlock(cAppCharP aBlockName, cAppCharP aBlockTitle, bool aCollapsed, cAppCharP aBlockFmt, va_list aArgs);
+ /// @brief Open structure Block, printf style variant
+ void DebugOpenBlock(cAppCharP aBlockName, cAppCharP aBlockTitle, bool aCollapsed, cAppCharP aBlockFmt, ...)
+#ifdef __GNUC__
+ __attribute__((format(printf, 5, 6)))
+#endif
+ ;
+ void DebugOpenBlockExpanded(cAppCharP aBlockName, cAppCharP aBlockTitle, cAppCharP aBlockFmt, ...)
+#ifdef __GNUC__
+ __attribute__((format(printf, 4, 5)))
+#endif
+ ;
+ void DebugOpenBlockCollapsed(cAppCharP aBlockName, cAppCharP aBlockTitle, cAppCharP aBlockFmt, ...)
+#ifdef __GNUC__
+ __attribute__((format(printf, 4, 5)))
+#endif
+ ;
+ /// @brief Open structure Block, without any attributes
+ virtual void DebugOpenBlock(cAppCharP aBlockName, cAppCharP aBlockTitle=NULL, bool aCollapsed=false);
+ /// @brief Close structure Block. Name is used to close eventually unclosed contained Blocks automatically.
+ virtual void DebugCloseBlock(cAppCharP aBlockName);
+protected:
+ // helper methods
+ /// @brief start debugging output if needed and sets fOutStarted
+ void DebugStartOutput(void);
+ /// @brief Output single line to debug channel (includes indenting, but no other formatting)
+ void DebugPutLine(cAppCharP aText, stringSize aTextSize=0, bool aPre=false);
+ /// @brief finalize debugging output
+ void DebugFinalizeOutput(void);
+ /// @brief internal helper for closing debug Blocks
+ /// @param aBlockName[in] Name of Block to close. All Blocks including the first with given name will be closed. If NULL, all Blocks will be closed.
+ /// @param aCloseComment[in] Comment about closing Block. If NULL, no comment will be shown (unless implicit closes occur, which auto-creates a comment)
+ void internalCloseBlocks(cAppCharP aBlockName, cAppCharP aCloseComment);
+ // Variables
+ TDbgOut *fDbgOutP; // the debug output
+ string fDbgPath; // the output path+filename (w/o extension)
+ const TDbgOptions *fDbgOptionsP; // the debug options
+ uInt32 fDebugMask; // the debug mask
+ bool fDebugEnabled; // on-off-switch for debugging output
+ uInt32 fNextDebugMask; // debug mask to be used for next DebugPrintfLastMask()
+ uInt16 fIndent; // the current indent
+ TBlockLevel *fBlockHistory; // the linked list of Block history entries
+ bool fOutStarted; // set if output has started
+ uInt32 fBlockNo; // block count for folding
+ GZones *fGZonesP; // zones list for time conversions
+}; // TDebugLoggerBase
+
+
+#ifdef MULTI_THREAD_SUPPORT
+/// @brief Subthread log handling
+typedef struct SubThreadLog {
+ uInt32 fThreadID;
+ struct SubThreadLog *fNext;
+ TDebugLoggerBase *fSubThreadLogger;
+} TSubThreadLog;
+#endif
+
+
+
+/// @brief Debug logger class
+class TDebugLogger : public TDebugLoggerBase {
+ typedef TDebugLoggerBase inherited;
+public:
+ // constructor/destructor
+ TDebugLogger(GZones *aGZonesP);
+ virtual ~TDebugLogger();
+ // methods
+ #ifdef MULTI_THREAD_SUPPORT
+ virtual void DebugPuts(uInt32 aDbgMask, cAppCharP aText, stringSize aTextSize=0, bool aPreFormatted=false);
+ virtual void DebugVPrintf(uInt32 aDbgMask, cAppCharP aFormat, va_list aArgs);
+ virtual void DebugVOpenBlock(cAppCharP aBlockName, cAppCharP aBlockTitle, bool aCollapsed, cAppCharP aBlockFmt, va_list aArgs);
+ virtual void DebugCloseBlock(cAppCharP aBlockName);
+ virtual TDebugLoggerBase &setNextMask(uInt32 aDbgMask);
+ #endif
+ // - thread debug output serializing
+ /// @brief output all buffered subthread's output in a special subthread Block in the main output
+ void DebugShowSubThreadOutput(void);
+ /// @brief signals the calling thread that it is done with doing output for now.
+ /// @param aRemoveIt[in] if set, do remove thread from the subthread logger list
+ /// Notes:
+ /// - If the main thread is doing this and we have bufferandmix mode, the next subthread will be allowed
+ /// to write into the output channel until a new main thread gains control via DebugDefineMainThread();
+ void DebugThreadOutputDone(bool aRemoveIt=false);
+ /// @brief Define the current calling thread as the main debug thread
+ /// Note: This is used for example when starting to process the next request of a session which eventually
+ // occurs from another thread).
+ void DebugDefineMainThread(void);
+private:
+ #ifdef MULTI_THREAD_SUPPORT
+ // helpers
+ /// @brief find (and possibly delete) subthread record
+ /// @param aAndRemove[in] if set, the subthread record will be removed in a thread safe way
+ /// IF AND ONLY IF aThreadID is the calling thread (i.e. only own thread may be removed from the list)!
+ /// Note that the caller must take care of deleting the subthread record
+ TSubThreadLog *findSubThread(uInt32 aThreadID, bool aAndRemove=false);
+ /// @brief find or create logger for subthread
+ TDebugLoggerBase *getThreadLogger(bool aCreateNew=true);
+ // Variables
+ uInt32 fMainThreadID;
+ TSubThreadLog *fSubThreadLogs; // the linked list of active subthreads
+ TDebugLoggerBase *fSilentLoggerP; // a silent (inactive) logger required for suppressed subthreads
+ #endif
+}; // TDebugLogger
+
+
+
+
+} // namespace sysync
+
+#endif // SYDEBUG
+
+#endif // DEBUGLOGGER_H
+
+
+// eof
+
diff --git a/src/sysync/engineentry.cpp b/src/sysync/engineentry.cpp
new file mode 100755
index 0000000..4991dd4
--- /dev/null
+++ b/src/sysync/engineentry.cpp
@@ -0,0 +1,334 @@
+/**
+ * @File enginementry.cpp
+ *
+ * @Author Beat Forster (bfo@synthesis.ch)
+ *
+ * @brief TEngineModuleBase
+ * engine bus bar class
+ *
+ * Copyright (c) 2007-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ */
+
+/*
+ */
+
+
+#include "prefix_file.h"
+#include "SDK_util.h"
+#include "engineentry.h"
+#include "enginemodulebase.h"
+
+
+namespace sysync {
+
+
+// Get object reference
+static DB_Callback DBC ( void* aCB ) { return (DB_Callback) aCB; }
+static TEngineModuleBase* URef( void* aCB ) { return (TEngineModuleBase*)DBC( aCB )->thisBase; }
+static TunnelWrapper* TW ( CContext aContext ) { return (TunnelWrapper*)aContext; }
+static TEngineModuleBase* TRef( CContext aContext ) { return URef( TW( aContext )->tCB ); }
+static CContext TCon( CContext aContext ) { return TW( aContext )->tContext; }
+
+
+// --- Callback entries --------------------------
+void DebugDB ( void* aCB, cAppCharP aParams ) {
+ DBC( aCB )->DB_DebugPuts ( aCB, aParams );
+} // DebugDB
+
+void DebugExotic ( void* aCB, cAppCharP aParams ) {
+ DBC( aCB )->DB_DebugExotic ( aCB, aParams );
+} // DebugExotic
+
+void DebugBlock ( void* aCB, cAppCharP aTag, cAppCharP aDesc, cAppCharP aAttrText ) {
+ DBC( aCB )->DB_DebugBlock ( aCB, aTag, aDesc, aAttrText );
+} // DebugBlock
+
+void DebugEndBlock ( void* aCB, cAppCharP aTag ) {
+ DBC( aCB )->DB_DebugEndBlock ( aCB, aTag );
+} // DebugEndBlock
+
+void DebugEndThread ( void* aCB ) {
+ DBC( aCB )->DB_DebugEndThread( aCB );
+} // DebugEndThread
+
+
+
+// ----------------------------------------------------------------------------------------
+TSyError SetStringMode ( void* aCB, uInt16 aCharSet,
+ uInt16 aLineEndMode, bool aBigEndian ) {
+ return URef( aCB )->SetStringMode ( aCharSet, aLineEndMode, aBigEndian );
+} // SetStringMode
+
+TSyError InitEngineXML ( void* aCB, cAppCharP aConfigXML ) {
+ return URef( aCB )->InitEngineXML ( aConfigXML );
+} // InitEngineXML
+
+TSyError InitEngineFile( void* aCB, cAppCharP aConfigFilePath ) {
+ return URef( aCB )->InitEngineFile ( aConfigFilePath );
+} // InitEngineFile
+
+TSyError InitEngineCB ( void* aCB, TXMLConfigReadFunc aReaderFunc, void* aContext ) {
+ return URef( aCB )->InitEngineCB ( aReaderFunc, aContext );
+} // InitEngineCB
+
+
+// ----------------------------------------------------------------------------------------
+TSyError OpenSession( void* aCB, SessionH *aSessionH, uInt32 aSelector,
+ cAppCharP aSessionName ) {
+ return URef( aCB )->OpenSession( *aSessionH, aSelector, aSessionName );
+} // OpenSession
+
+TSyError OpenSessionKey( void* aCB, SessionH aSessionH, KeyH *aKeyH, uInt16 aMode ) {
+ return URef( aCB )->OpenSessionKey ( aSessionH, *aKeyH, aMode );
+} // OpenSessionKey
+
+TSyError SessionStep( void* aCB, SessionH aSessionH, uInt16 *aStepCmd,
+ TEngineProgressInfo *aInfoP ) {
+ return URef( aCB )->SessionStep ( aSessionH, *aStepCmd, aInfoP );
+} // SessionStep
+
+TSyError GetSyncMLBuffer( void* aCB, SessionH aSessionH, bool aForSend,
+ appPointer *aBuffer, memSize *aBufSize ) {
+ return URef( aCB )->GetSyncMLBuffer ( aSessionH, aForSend, *aBuffer, *aBufSize );
+} // GetSyncMLBuffer
+
+TSyError RetSyncMLBuffer( void* aCB, SessionH aSessionH, bool aForSend,
+ memSize aRetSize ) {
+ return URef( aCB )->RetSyncMLBuffer ( aSessionH, aForSend, aRetSize );
+} // RetSyncMLBuffer
+
+TSyError ReadSyncMLBuffer ( void* aCB, SessionH aSessionH,
+ appPointer aBuffer, memSize aBufSize,
+ memSize *aValSize ) {
+ return URef( aCB )->ReadSyncMLBuffer ( aSessionH, aBuffer, aBufSize, *aValSize );
+} // ReadSyncMLBuffer
+
+TSyError WriteSyncMLBuffer( void* aCB, SessionH aSessionH,
+ appPointer aBuffer, memSize aValSize ) {
+ return URef( aCB )->WriteSyncMLBuffer ( aSessionH, aBuffer, aValSize );
+} // WriteSyncMLBuffer
+
+TSyError CloseSession( void* aCB, SessionH aSessionH ) {
+ return URef( aCB )->CloseSession ( aSessionH );
+} // CloseSession
+
+
+
+// ----------------------------------------------------------------------------------------
+TSyError OpenKeyByPath( void* aCB, KeyH *aKeyH,
+ KeyH aParentKeyH, cAppCharP aPath, uInt16 aMode ) {
+ return URef( aCB )->OpenKeyByPath ( *aKeyH, aParentKeyH, aPath, aMode );
+} // OpenKeyByPath
+
+TSyError OpenSubkey ( void* aCB, KeyH *aKeyH,
+ KeyH aParentKeyH, sInt32 aID, uInt16 aMode ) {
+ return URef( aCB )->OpenSubkey ( *aKeyH, aParentKeyH, aID, aMode );
+} // OpenSubkey
+
+TSyError DeleteSubkey( void* aCB, KeyH aParentKeyH, sInt32 aID ) {
+ return URef( aCB )->DeleteSubkey ( aParentKeyH, aID );
+} // DeleteSubkey
+
+TSyError GetKeyID( void* aCB, KeyH aKeyH, sInt32 *aID ) {
+ return URef( aCB )->GetKeyID ( aKeyH, *aID );
+} // GetKeyID
+
+TSyError SetTextMode( void* aCB, KeyH aKeyH, uInt16 aCharSet, uInt16 aLineEndMode,
+ bool aBigEndian ) {
+ return URef( aCB )->SetTextMode ( aKeyH, aCharSet, aLineEndMode, aBigEndian );
+} // SetTextMode
+
+TSyError SetTimeMode( void* aCB, KeyH aKeyH, uInt16 aTimeMode ) {
+ return URef( aCB )->SetTimeMode ( aKeyH, aTimeMode );
+} // SetTimeMode
+
+TSyError CloseKey( void* aCB, KeyH aKeyH ) {
+ return URef( aCB )->CloseKey ( aKeyH );
+} // CloseKey
+
+
+
+// ----------------------------------------------------------------------------------------
+TSyError GetValue ( void* aCB, KeyH aKeyH, cAppCharP aValName,
+ uInt16 aValType,
+ appPointer aBuffer, memSize aBufSize,
+ memSize *aValSize ) {
+ return URef( aCB )->GetValue( aKeyH, aValName, aValType, aBuffer, aBufSize,*aValSize );
+} // GetValue
+
+TSyError GetValueByID( void* aCB, KeyH aKeyH, sInt32 aID,
+ sInt32 arrIndex, uInt16 aValType,
+ appPointer aBuffer, memSize aBufSize,
+ memSize *aValSize ) {
+ return URef( aCB )->GetValueByID( aKeyH, aID, arrIndex, aValType, aBuffer, aBufSize, *aValSize );
+} // GetValueByID
+
+sInt32 GetValueID ( void* aCB, KeyH aKeyH, cAppCharP aName ) {
+ return URef( aCB )->GetValueID ( aKeyH, aName );
+} // GetValueID
+
+TSyError SetValue ( void* aCB, KeyH aKeyH, cAppCharP aValName,
+ uInt16 aValType,
+ cAppPointer aBuffer, memSize aValSize ) {
+ return URef( aCB )->SetValue ( aKeyH, aValName, aValType, aBuffer, aValSize );
+} // SetValue
+
+TSyError SetValueByID( void* aCB, KeyH aKeyH, sInt32 aID,
+ sInt32 arrIndex, uInt16 aValType,
+ cAppPointer aBuffer, memSize aValSize ) {
+ return URef( aCB )->SetValueByID( aKeyH, aID, arrIndex, aValType, aBuffer, aValSize );
+} // SetValueByID
+
+
+
+// ----------------------------------------------------------------------------------------
+TSyError StartDataRead ( CContext ac, cAppCharP lastToken, cAppCharP resumeToken ) {
+ return TRef( ac )->StartDataRead ( TCon( ac ), lastToken, resumeToken );
+} // StartDataRead
+
+TSyError ReadNextItem ( CContext ac, ItemID aID, appCharP *aItemData,
+ sInt32 *aStatus, bool aFirst ) {
+ return TRef( ac )->ReadNextItem ( TCon( ac ), aID, aItemData, aStatus, aFirst );
+} // ReadNextItem
+
+TSyError ReadItem ( CContext ac, cItemID aID, appCharP *aItemData ) {
+ return TRef( ac )->ReadItem ( TCon( ac ), aID, aItemData );
+} // ReadItem
+
+TSyError EndDataRead ( CContext ac ) {
+ return TRef( ac )->EndDataRead ( TCon( ac ) );
+} // EndDataRead
+
+TSyError StartDataWrite ( CContext ac ) {
+ return TRef( ac )->StartDataWrite ( TCon( ac ) );
+} // StartDataWrite
+
+TSyError InsertItem ( CContext ac, cAppCharP aItemData, cItemID aID ) {
+ return TRef( ac )->InsertItem ( TCon( ac ), aItemData, aID );
+} // InsertItem
+
+TSyError UpdateItem ( CContext ac, cAppCharP aItemData, cItemID aID,
+ ItemID updID ) {
+ return TRef( ac )->UpdateItem ( TCon( ac ), aItemData, aID, updID );
+} // UpdateItem
+
+TSyError MoveItem ( CContext ac, cItemID aID, cAppCharP newParID ) {
+ return TRef( ac )->MoveItem ( TCon( ac ), aID, newParID );
+} // MoveItem
+
+TSyError DeleteItem ( CContext ac, cItemID aID ) {
+ return TRef( ac )->DeleteItem ( TCon( ac ), aID );
+} // DeleteItem
+
+TSyError EndDataWrite ( CContext ac, bool success, appCharP *newToken ) {
+ return TRef( ac )->EndDataWrite ( TCon( ac ), success, newToken );
+} // EndDataWrite
+
+void DisposeObj ( CContext ac, void* memory ) {
+ TRef( ac )->DisposeObj ( TCon( ac ), memory );
+} // DisposeObj
+
+// ---- asKey functions ----
+TSyError ReadNextItemAsKey( CContext ac, ItemID aID, KeyH aItemKey, sInt32 *aStatus,
+ bool aFirst ) {
+ return TRef( ac )->ReadNextItemAsKey( TCon( ac ), aID, aItemKey, aStatus, aFirst );
+} // ReadNextItemAsKey
+
+TSyError ReadItemAsKey ( CContext ac, cItemID aID, KeyH aItemKey ) {
+ return TRef( ac )->ReadItemAsKey ( TCon( ac ), aID, aItemKey );
+} // ReadItemAsKey
+
+TSyError InsertItemAsKey ( CContext ac, KeyH aItemKey, cItemID aID ) {
+ return TRef( ac )->InsertItemAsKey ( TCon( ac ), aItemKey, aID );
+} // InsertItemAsKey
+
+TSyError UpdateItemAsKey ( CContext ac, KeyH aItemKey, cItemID aID,
+ ItemID updID ) {
+ return TRef( ac )->UpdateItemAsKey ( TCon( ac ), aItemKey, aID, updID );
+} // UpdateItemAsKey
+
+
+
+// ----------------------------------------------------------------------------------------
+// connect generic key access routines
+void CB_Connect_KeyAccess( void* aCB )
+{
+ DB_Callback cb= (DB_Callback)aCB;
+ if (!CB_OK( cb,8 )) return; // minimum callback version required
+
+ // calls used by both DBApi and UIApi
+ cb->ui.OpenKeyByPath = OpenKeyByPath;
+ cb->ui.OpenSubkey = OpenSubkey;
+ cb->ui.DeleteSubkey = DeleteSubkey;
+ cb->ui.GetKeyID = GetKeyID;
+ cb->ui.SetTextMode = SetTextMode;
+ cb->ui.SetTimeMode = SetTimeMode;
+ cb->ui.CloseKey = CloseKey;
+
+ cb->ui.GetValue = GetValue;
+ cb->ui.GetValueByID = GetValueByID;
+ cb->ui.GetValueID = GetValueID;
+ cb->ui.SetValue = SetValue;
+ cb->ui.SetValueByID = SetValueByID;
+} // CB_Connect_KeyAccess
+
+
+static void CB_Connect_Tunnel( void* aCB )
+{
+ DB_Callback cb= (DB_Callback)aCB;
+ if (!CB_OK( cb, 9 )) return; // minimum callback version required
+
+ cb->dt.StartDataRead = StartDataRead;
+ cb->dt.ReadNextItem = ReadNextItem;
+ cb->dt.ReadItem = ReadItem;
+ cb->dt.EndDataRead = EndDataRead;
+
+ cb->dt.StartDataWrite = StartDataWrite;
+ cb->dt.InsertItem = InsertItem;
+ cb->dt.UpdateItem = UpdateItem;
+ cb->dt.MoveItem = MoveItem;
+ cb->dt.DeleteItem = DeleteItem;
+ cb->dt.EndDataWrite = EndDataWrite;
+
+
+ if (!CB_OK( cb,11 )) return; // minimum callback version required
+
+ cb->dt.DisposeObj = DisposeObj;
+
+ cb->dt.ReadNextItemAsKey= ReadNextItemAsKey;
+ cb->dt.ReadItemAsKey = ReadItemAsKey;
+ cb->dt.InsertItemAsKey = InsertItemAsKey;
+ cb->dt.UpdateItemAsKey = UpdateItemAsKey;
+} // CB_Connect_Tunnel
+
+
+void CB_Connect( void* aCB )
+{
+ DB_Callback cb= (DB_Callback)aCB;
+ if (!CB_OK( cb,8 )) return; // minimum callback version required
+
+ // calls needed for accessing engine from the outside (UIApi)
+ cb->ui.SetStringMode = SetStringMode;
+ cb->ui.InitEngineXML = InitEngineXML;
+ cb->ui.InitEngineFile = InitEngineFile;
+ cb->ui.InitEngineCB = InitEngineCB;
+
+ cb->ui.OpenSession = OpenSession;
+ cb->ui.OpenSessionKey = OpenSessionKey;
+ cb->ui.SessionStep = SessionStep;
+ cb->ui.GetSyncMLBuffer = GetSyncMLBuffer;
+ cb->ui.RetSyncMLBuffer = RetSyncMLBuffer;
+ cb->ui.ReadSyncMLBuffer = ReadSyncMLBuffer;
+ cb->ui.WriteSyncMLBuffer= WriteSyncMLBuffer;
+ cb->ui.CloseSession = CloseSession;
+
+ // generic key access calls used by both DBApi and UIApi
+ CB_Connect_KeyAccess( cb );
+ CB_Connect_Tunnel ( cb );
+} // CB_Connect
+
+
+} // namespace sysync
+
+// eof
diff --git a/src/sysync/engineentry.h b/src/sysync/engineentry.h
new file mode 100755
index 0000000..89f4cc5
--- /dev/null
+++ b/src/sysync/engineentry.h
@@ -0,0 +1,117 @@
+/**
+ * @File engineentry.h
+ *
+ * @Author Beat Forster (bfo@synthesis.ch)
+ *
+ * Copyright (c) 2007-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ */
+
+/*
+ */
+
+#ifndef ENGINEENTRY_H
+#define ENGINEENTRY_H
+
+#include "generic_types.h"
+#include "sync_dbapidef.h"
+
+// we need STL strings
+#include <string>
+using namespace std;
+
+
+namespace sysync {
+
+
+ENGINE_ENTRY void DebugDB ( void* aCB, cAppCharP aParams ) ENTRY_ATTR;
+ENGINE_ENTRY void DebugExotic ( void* aCB, cAppCharP aParams ) ENTRY_ATTR;
+ENGINE_ENTRY void DebugBlock ( void* aCB, cAppCharP aTag,
+ cAppCharP aDesc,
+ cAppCharP aAttrText ) ENTRY_ATTR;
+ENGINE_ENTRY void DebugEndBlock ( void* aCB, cAppCharP aTag ) ENTRY_ATTR;
+ENGINE_ENTRY void DebugEndThread ( void* aCB ) ENTRY_ATTR;
+
+// ----------------------------------------------------------------------------------------
+ENGINE_ENTRY TSyError SetStringMode ( void* aCB, uInt16 aCharSet,
+ uInt16 aLineEndMode, bool aBigEndian ) ENTRY_ATTR;
+ENGINE_ENTRY TSyError InitEngineXML ( void* aCB, cAppCharP aConfigXML ) ENTRY_ATTR;
+ENGINE_ENTRY TSyError InitEngineFile ( void* aCB, cAppCharP aConfigFilePath ) ENTRY_ATTR;
+ENGINE_ENTRY TSyError InitEngineCB ( void* aCB, TXMLConfigReadFunc aReaderFunc, void* aContext ) ENTRY_ATTR;
+
+
+ENGINE_ENTRY TSyError OpenSession ( void* aCB, SessionH *aSessionH, uInt32 aSelector,
+ cAppCharP aSessionName ) ENTRY_ATTR;
+ENGINE_ENTRY TSyError OpenSessionKey ( void* aCB, SessionH aSessionH,
+ KeyH *aKeyH, uInt16 aMode ) ENTRY_ATTR;
+ENGINE_ENTRY TSyError SessionStep ( void* aCB, SessionH aSessionH, uInt16 *aStepCmd,
+ TEngineProgressInfo *aInfoP ) ENTRY_ATTR;
+ENGINE_ENTRY TSyError GetSyncMLBuffer ( void* aCB, SessionH aSessionH, bool aForSend,
+ appPointer *aBuffer, memSize *aBufSize ) ENTRY_ATTR;
+ENGINE_ENTRY TSyError RetSyncMLBuffer ( void* aCB, SessionH aSessionH, bool aForSend,
+ memSize aRetSize ) ENTRY_ATTR;
+ENGINE_ENTRY TSyError ReadSyncMLBuffer ( void* aCB, SessionH aSessionH,
+ appPointer aBuffer, memSize aBufSize,
+ memSize *aValSize ) ENTRY_ATTR;
+ENGINE_ENTRY TSyError WriteSyncMLBuffer( void* aCB, SessionH aSessionH,
+ appPointer aBuffer, memSize aValSize ) ENTRY_ATTR;
+ENGINE_ENTRY TSyError CloseSession ( void* aCB, SessionH aSessionH ) ENTRY_ATTR;
+
+
+ENGINE_ENTRY TSyError OpenKeyByPath ( void* aCB, KeyH *aKeyH,
+ KeyH aParentKeyH, cAppCharP aPath, uInt16 aMode ) ENTRY_ATTR;
+ENGINE_ENTRY TSyError OpenSubkey ( void* aCB, KeyH *aKeyH,
+ KeyH aParentKeyH, sInt32 aID, uInt16 aMode ) ENTRY_ATTR;
+ENGINE_ENTRY TSyError DeleteSubkey ( void* aCB, KeyH aParentKeyH, sInt32 aID ) ENTRY_ATTR;
+ENGINE_ENTRY TSyError GetKeyID ( void* aCB, KeyH aKeyH, sInt32 *aID ) ENTRY_ATTR;
+ENGINE_ENTRY TSyError SetTextMode ( void* aCB, KeyH aKeyH, uInt16 aCharSet,
+ uInt16 aLineEndMode, bool aBigEndian ) ENTRY_ATTR;
+ENGINE_ENTRY TSyError SetTimeMode ( void* aCB, KeyH aKeyH, uInt16 aTimeMode ) ENTRY_ATTR;
+ENGINE_ENTRY TSyError CloseKey ( void* aCB, KeyH aKeyH ) ENTRY_ATTR;
+
+ENGINE_ENTRY TSyError GetValue ( void* aCB, KeyH aKeyH, cAppCharP aValName, uInt16 aValType,
+ appPointer aBuffer, memSize aBufSize, memSize *aValSize ) ENTRY_ATTR;
+ENGINE_ENTRY TSyError GetValueByID ( void* aCB, KeyH aKeyH, sInt32 aID, sInt32 arrIndex,
+ uInt16 aValType,
+ appPointer aBuffer, memSize aBufSize, memSize *aValSize ) ENTRY_ATTR;
+ENGINE_ENTRY sInt32 GetValueID ( void* aCB, KeyH aKeyH, cAppCharP aName ) ENTRY_ATTR;
+ENGINE_ENTRY TSyError SetValue ( void* aCB, KeyH aKeyH, cAppCharP aValName, uInt16 aValType,
+ cAppPointer aBuffer, memSize aValSize ) ENTRY_ATTR;
+ENGINE_ENTRY TSyError SetValueByID ( void* aCB, KeyH aKeyH, sInt32 aID, sInt32 arrIndex,
+ uInt16 aValType,
+ cAppPointer aBuffer, memSize aValSize ) ENTRY_ATTR;
+
+
+// ---- tunnel --------------------------------------------------------------------------------------------------------
+ENGINE_ENTRY TSyError StartDataRead ( CContext ac, cAppCharP lastToken, cAppCharP resumeToken ) ENTRY_ATTR;
+ENGINE_ENTRY TSyError ReadNextItem ( CContext ac, ItemID aID, appCharP *aItemData,
+ sInt32 *aStatus, bool aFirst ) ENTRY_ATTR;
+ENGINE_ENTRY TSyError ReadItem ( CContext ac, cItemID aID, appCharP *aItemData ) ENTRY_ATTR;
+ENGINE_ENTRY TSyError EndDataRead ( CContext ac ) ENTRY_ATTR;
+ENGINE_ENTRY TSyError StartDataWrite ( CContext ac ) ENTRY_ATTR;
+ENGINE_ENTRY TSyError InsertItem ( CContext ac, cAppCharP aItemData, cItemID aID ) ENTRY_ATTR;
+ENGINE_ENTRY TSyError UpdateItem ( CContext ac, cAppCharP aItemData, cItemID aID, ItemID updID ) ENTRY_ATTR;
+ENGINE_ENTRY TSyError MoveItem ( CContext ac, cItemID aID, cAppCharP newParID ) ENTRY_ATTR;
+ENGINE_ENTRY TSyError DeleteItem ( CContext ac, cItemID aID ) ENTRY_ATTR;
+ENGINE_ENTRY TSyError EndDataWrite ( CContext ac, bool success, appCharP *newToken ) ENTRY_ATTR;
+ENGINE_ENTRY void DisposeObj ( CContext ac, void* memory ) ENTRY_ATTR;
+
+// ---- asKey ----
+ENGINE_ENTRY TSyError ReadNextItemAsKey( CContext ac, ItemID aID, KeyH aItemKey,
+ sInt32 *aStatus, bool aFirst ) ENTRY_ATTR;
+ENGINE_ENTRY TSyError ReadItemAsKey ( CContext ac, cItemID aID, KeyH aItemKey ) ENTRY_ATTR;
+ENGINE_ENTRY TSyError InsertItemAsKey ( CContext ac, KeyH aItemKey, cItemID aID ) ENTRY_ATTR;
+ENGINE_ENTRY TSyError UpdateItemAsKey ( CContext ac, KeyH aItemKey, cItemID aID, ItemID updID ) ENTRY_ATTR;
+
+
+
+// ----------------------------------------------------------------------------------------
+ENGINE_ENTRY void CB_Connect( void* aCB ) ENTRY_ATTR;
+
+// engine local helper, used e.g. from PluginDS/Agent
+void CB_Connect_KeyAccess ( void* aCB );
+
+
+} // namespace sysync
+#endif // ENGINEENTRY_H
+// eof
diff --git a/src/sysync/engineinterface.cpp b/src/sysync/engineinterface.cpp
new file mode 100644
index 0000000..6076bc6
--- /dev/null
+++ b/src/sysync/engineinterface.cpp
@@ -0,0 +1,1719 @@
+/**
+ * @File engineinterface.cpp
+ *
+ * @Author Lukas Zeller (luz@synthesis.ch)
+ *
+ * @brief TEngineInterface - common interface to SySync engine for SDK
+ *
+ * Copyright (c) 2007-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ */
+
+
+#include "prefix_file.h"
+
+#ifdef ENGINEINTERFACE_SUPPORT
+
+#include "engineinterface.h"
+#include "syserial.h"
+#include "syncappbase.h"
+#include "SDK_util.h"
+
+namespace sysync {
+
+
+// TSettingsKeyImpl
+// ================
+
+
+TSettingsKeyImpl::TSettingsKeyImpl(
+ TEngineInterface *aEngineInterfaceP
+) :
+ fEngineInterfaceP(aEngineInterfaceP),
+ fImplicitParentKeyP(NULL)
+{
+ // init defaults from engine interface
+ fCharSet = fEngineInterfaceP->fCharSet;
+ fBigEndian = fEngineInterfaceP->fBigEndian;
+ fLineEndMode = fEngineInterfaceP->fLineEndMode;
+ // init hardcoded defaults
+ fTimeMode = TMODE_LINEARTIME+TMODE_FLAG_FLOATING; // linear time (as floating, which means UTC for engine timestamps)
+} // TSettingsKeyImpl::TSettingsKeyImpl
+
+
+TSettingsKeyImpl::~TSettingsKeyImpl()
+{
+ // deleting object means closing key
+ // - At this point (in baseclass), actual key implementation is closed
+ // already, so we can delete implicitly opened ancestors now recursively
+ if (fImplicitParentKeyP) {
+ delete fImplicitParentKeyP;
+ fImplicitParentKeyP=NULL;
+ }
+} // TSettingsKeyImpl::TSettingsKeyImpl
+
+
+// open subkey(chain) by path
+// - walks down through needed subkeys
+TSyError TSettingsKeyImpl::OpenKeyByPath(
+ TSettingsKeyImpl *&aSettingsKeyP, // might be set even if overall open fails - caller must delete object passed back in case of failure
+ cAppCharP aPath, uInt16 aMode,
+ bool aImplicit // if set, this means that THIS key was implicitly opened, and should be implicitly closed as well
+)
+{
+ TSyError sta;
+ TSettingsKeyImpl *subKeyP=NULL;
+
+ // as long as we haven't openend anything, make sure pointer returned is NULL
+ aSettingsKeyP=NULL;
+ // open by relative path, starting at myself
+ if (aPath==NULL) return LOCERR_WRONGUSAGE;
+ // strip leading separators
+ while (*aPath==SETTINGSKEY_PATH_SEPARATOR) aPath++;
+ // search end of string or next separator
+ cAppCharP e = aPath;
+ while (*e && *e!=SETTINGSKEY_PATH_SEPARATOR) e++;
+ // open the element we have found
+ sta = OpenSubKeyByName(aSettingsKeyP,aPath,e-aPath,aMode);
+ if (sta == LOCERR_OK) {
+ // immediate subkey opened successfully
+ // - if this is an implicit open, link back to myself as I must be deleted when subkey is deleted
+ if (aImplicit) {
+ aSettingsKeyP->fImplicitParentKeyP = this;
+ }
+ // new opened key inherits base key's value formatting settings
+ aSettingsKeyP->SetTextMode(fCharSet, fLineEndMode,fBigEndian);
+ aSettingsKeyP->SetTimeMode(fTimeMode);
+ // - check if chain continues
+ while (*e==SETTINGSKEY_PATH_SEPARATOR) e++; // strip leading separators
+ if (*e) {
+ // another path element follows, open it recursively
+ // - this is implicit in any case!
+ sta = aSettingsKeyP->OpenKeyByPath(subKeyP,e,aMode,true);
+ if (subKeyP != NULL) {
+ // more subkeys were opened, possibly not entire chain as requested. But in
+ // any case, we need to pass back the rightmost opened key in the path as this
+ // must be deleted by the caller (causing all implicitly related objects to delete as well)
+ aSettingsKeyP = subKeyP;
+ }
+ }
+ }
+ // return status
+ return sta;
+} // TSettingsKeyImpl::OpenKeyByPath
+
+
+const uInt16 engineCharSets[numCharSets] = {
+ CHS_UNKNOWN,
+ CHS_ASCII,
+ CHS_ANSI,
+ CHS_ISO_8859_1,
+ CHS_UTF8,
+ CHS_UTF16,
+ #ifdef CHINESE_SUPPORT
+ CHS_GB2312,
+ CHS_CP936
+ #endif
+};
+
+const uInt16 engineLineEndModes[numLineEndModes] = {
+ LEM_NONE,
+ LEM_UNIX,
+ LEM_MAC,
+ LEM_DOS,
+ LEM_CSTR,
+ LEM_FILEMAKER
+};
+
+
+// Set text format parameters
+TSyError TSettingsKeyImpl::SetTextMode(uInt16 aCharSet, uInt16 aLineEndMode, bool aBigEndian)
+{
+ // translate charset
+ uInt16 chs;
+ for (chs=0; chs<numCharSets; chs++) {
+ if (engineCharSets[chs]==aCharSet) break;
+ }
+ if (chs==numCharSets) return LOCERR_BADPARAM;
+ // translate lineendmode
+ uInt16 lem;
+ for (lem=0; lem<numLineEndModes; lem++) {
+ if (engineLineEndModes[lem]==aLineEndMode) break;
+ }
+ if (lem==numLineEndModes) return LOCERR_BADPARAM;
+ // assign them now they are checked ok
+ fCharSet = (TCharSets)chs;
+ fLineEndMode = (TLineEndModes)lem;
+ // big endian flag
+ fBigEndian = aBigEndian;
+ return LOCERR_OK;
+} // TSettingsKeyImpl::SetTextMode
+
+
+
+// Reads a named value in specified format into passed memory buffer
+TSyError TSettingsKeyImpl::GetValueByID(
+ sInt32 aID, sInt32 aArrayIndex, uInt16 aValType,
+ appPointer aBuffer, memSize aBufSize, memSize &aValSize
+)
+{
+ TSyError sta;
+ string valStr;
+ cAppCharP txt;
+ string convStr;
+ sInt64 tempInt=0;
+ lineartime_t tempTime;
+ timecontext_t tctx;
+
+ // get native type of value (and check validity of ID - invalid IDs must return VALTYPE_UNKNOWN)
+ uInt16 valType = GetValueType(aID);
+ if (valType==VALTYPE_UNKNOWN) return DB_NotFound;
+ // direct return in case requested type matches native type
+ if (
+ (aValType == VALTYPE_BUF) ||
+ ((valType == aValType) &&
+ (valType != VALTYPE_TEXT || (fCharSet==chs_utf8 && fLineEndMode==lem_cstr)) &&
+ (valType != VALTYPE_TIME64 || ((fTimeMode & TMODE_MODEMASK)==TMODE_LINEARTIME)) )
+ ) {
+ // requested type&format matches native or we are requesting a buffer -> we can return native value directly
+ if (aValType == VALTYPE_TEXT) {
+ if (!aBuffer || aBufSize==0) {
+ // only measure size
+ return GetValueInternal(aID,aArrayIndex,NULL,0,aValSize);
+ }
+ else {
+ // low level text routines do not set terminators for simplicity, so make sure we have one here
+ sta=GetValueInternal(aID,aArrayIndex,aBuffer,aBufSize-1,aValSize);
+ // make sure we have a NUL terminator
+ ((appCharP)aBuffer)[aBufSize-1]=0; // ultimate terminator
+ if (sta==LOCERR_OK && aValSize<aBufSize)
+ ((appCharP)aBuffer)[aValSize]=0;
+ // return LOCERR_TRUNCATED if ok but buffer is not large enough
+ if (sta==LOCERR_OK && aValSize>aBufSize-1) {
+ sta = LOCERR_TRUNCATED;
+ aValSize=aBufSize-1; // return actual size, not untruncated one
+ }
+ return sta;
+ }
+ }
+ else
+ return GetValueInternal(aID,aArrayIndex,aBuffer,aBufSize,aValSize);
+ }
+ // some kind of conversion needed
+ // - get native size of value first
+ memSize valSiz;
+ sta = GetValueInternal(aID,aArrayIndex,NULL,0,valSiz);
+ if (sta!=LOCERR_OK) return sta;
+ // - allocate matching buffer
+ memSize bufSiz = valSiz+1; // extra room for terminator
+ uInt8P bufP = (uInt8P) malloc(bufSiz);
+ bufP[valSiz]=0; // make sure we have a terminator
+ if (bufP==NULL) return LOCERR_OUTOFMEM;
+ // - get value in native type
+ sta = GetValueInternal(aID,aArrayIndex,bufP,bufSiz,valSiz);
+ if (sta==LOCERR_OK) {
+ // value conversion matrix
+ // - switch by native value type
+ switch (valType) {
+ // Timestamp native type (always VALTYPE_TIME64)
+ case VALTYPE_TIME64:
+ tempTime = *((lineartime_t *)bufP);
+ tctx = (fTimeMode & TMODE_FLAG_UTC)==0 ? TCTX_SYSTEM : TCTX_UTC;
+ // convert it from internal format (UTC), unless we request floating
+ if ((fTimeMode & TMODE_FLAG_FLOATING)==0)
+ TzConvertTimestamp(tempTime,TCTX_UTC,tctx,getEngineInterface()->getSyncAppBase()->getAppZones());
+ else
+ tctx = TCTX_UNKNOWN; // make sure that ISO8601 representation has no TZ info in floating mode
+ // now return
+ if (aValType==VALTYPE_TEXT) {
+ // return time as text
+ TimestampToISO8601Str(valStr, tempTime, tctx, false, false);
+ txt = valStr.c_str();
+ goto textConv;
+ }
+ // no time will return an error code
+ if (tempTime==noLinearTime) {
+ // no time
+ sta=DB_NoContent; // indicates NO value
+ break;
+ }
+ // return time as integer number
+ switch (fTimeMode & TMODE_MODEMASK) {
+ case TMODE_LINEARDATE:
+ tempInt = tempTime / linearDateToTimeFactor;
+ goto intConv;
+ case TMODE_UNIXTIME_MS:
+ tempInt = tempTime-UnixToLineartimeOffset; // lineartime_t (that is, milliseconds), but with unix epoch origin
+ goto intConv;
+ case TMODE_UNIXTIME:
+ tempInt = (tempTime-UnixToLineartimeOffset)/secondToLinearTimeFactor;
+ goto intConv;
+ default :
+ tempInt = tempTime;
+ goto intConv;
+ }
+ break;
+ // Integer native types
+ case VALTYPE_INT8:
+ tempInt = *((sInt8 *)bufP);
+ goto intConv;
+ case VALTYPE_INT16:
+ tempInt = *((sInt16 *)bufP);
+ goto intConv;
+ case VALTYPE_INT32:
+ tempInt = *((sInt32 *)bufP);
+ goto intConv;
+ case VALTYPE_INT64:
+ tempInt = *((sInt64 *)bufP);
+ intConv:
+ // convert integer into desired target type
+ switch (aValType) {
+ case VALTYPE_INT8:
+ case VALTYPE_ENUM:
+ aValSize=1;
+ if (aBufSize<aValSize) sta=LOCERR_BUFTOOSMALL;
+ else *((sInt8 *)aBuffer) = tempInt;
+ break;
+ case VALTYPE_INT16:
+ aValSize=2;
+ if (aBufSize<aValSize) sta=LOCERR_BUFTOOSMALL;
+ else *((sInt16 *)aBuffer) = tempInt;
+ break;
+ case VALTYPE_INT32:
+ case VALTYPE_TIME32:
+ aValSize=4;
+ if (aBufSize<aValSize) sta=LOCERR_BUFTOOSMALL;
+ else *((sInt32 *)aBuffer) = tempInt;
+ break;
+ case VALTYPE_INT64:
+ case VALTYPE_TIME64:
+ aValSize=8;
+ if (aBufSize<aValSize) sta=LOCERR_BUFTOOSMALL;
+ else *((sInt64 *)aBuffer) = tempInt;
+ break;
+ case VALTYPE_TEXT:
+ StringObjPrintf(valStr,PRINTF_LLD,PRINTF_LLD_ARG(tempInt));
+ txt = valStr.c_str();
+ goto textConv;
+ break;
+ }
+ break;
+ // Text native type
+ case VALTYPE_TEXT:
+ txt = (cAppCharP)bufP;
+ textConv:
+ // text can only be returned as text (implicit Int/time scanning makes no sense)
+ if (aValType!=VALTYPE_TEXT)
+ sta = LOCERR_WRONGUSAGE; // conversion not allowed
+ else {
+ // converted to requested charset/lineend
+ if (fCharSet==chs_utf16) {
+ if (!appendUTF8ToUTF16ByteString(
+ txt,
+ convStr,
+ fBigEndian,
+ fLineEndMode,
+ aBufSize>0 ? aBufSize-2 : 0 // max output size (but no limit if measuring actual size)
+ ))
+ sta = LOCERR_TRUNCATED;
+ // return (eventually truncated) size
+ aValSize=convStr.size();
+ // this will be added as part of the content, and gives a 16bit NUL terminator
+ convStr+=(char)0;
+ }
+ else {
+ // 8-bit character encoding
+ if (!appendUTF8ToString(
+ txt,
+ convStr,
+ fCharSet,
+ fLineEndMode,
+ qm_none,
+ aBufSize>0 ? aBufSize-1 : 0 // max output size (but no limit if measuring actual size)
+ ))
+ sta = LOCERR_TRUNCATED;
+ // return (eventually truncated) size
+ aValSize=convStr.size();
+ }
+ // if requested, return value into buffer
+ if (aBufSize>0) {
+ // copy including implicit NUL terminator BYTE (in case of UTF16, there is an extra explicit NUL char in the convStr itself to make a 16-bit NUL!)
+ memcpy(aBuffer,(appPointer)convStr.c_str(),convStr.size()+1>aBufSize ? aBufSize : convStr.size()+1);
+ }
+ else {
+ // measuring size always returns OK
+ sta = LOCERR_OK;
+ }
+ }
+ break;
+ } // switch by native type
+ } // if value returned
+
+ // - return temp buffer
+ free(bufP);
+ // - return status
+ return sta;
+} // TSettingsKeyImpl::GetValueByID
+
+
+// Writes a named value in specified format passed in memory buffer
+TSyError TSettingsKeyImpl::SetValueByID(
+ sInt32 aID, sInt32 aArrayIndex, uInt16 aValType,
+ cAppPointer aBuffer, memSize aValSize
+)
+{
+ TSyError sta = LOCERR_OK;
+ sInt64 tempInt;
+ lineartime_t tempTime;
+ timecontext_t tctx;
+ appPointer bP;
+ memSize siz;
+ string convStr;
+
+ // get native type of value (and check validity of ID - invalid IDs must return VALTYPE_UNKNOWN)
+ uInt16 valType = GetValueType(aID);
+ if (valType==VALTYPE_UNKNOWN) return DB_NotFound;
+ // measure value for strings
+ if (aValType == VALTYPE_TEXT && aValSize == memSize(-1)) {
+ if (fCharSet==chs_utf16)
+ return LOCERR_NOTIMP; // measuring UTF-16 not supported yet
+ //aValSize = wcslen((short *)aBuffer)/2;
+ else
+ aValSize = strlen((char *)aBuffer);
+ }
+ // we can set directly if input type matches native type
+ if (
+ (aValType == VALTYPE_BUF) ||
+ ((valType == aValType) &&
+ (valType != VALTYPE_TEXT || (fCharSet==chs_utf8 && fLineEndMode==lem_cstr)) &&
+ (valType != VALTYPE_TIME64 || ((fTimeMode & TMODE_MODEMASK)==TMODE_LINEARTIME)) )
+ ) {
+ // presented type&format matches native or we are setting bytes 1:1 anyway -> we can set native value directly
+ return SetValueInternal(aID,aArrayIndex,aBuffer,aValSize);
+ }
+ // some kind of conversion needed
+ // - switch by presented value type
+ switch (aValType) {
+
+ // Text presented
+ case VALTYPE_TEXT:
+ // first convert into internal text format
+ if (fCharSet==chs_utf16) {
+ appendUTF16AsUTF8((uInt16 *)aBuffer,aValSize/2,fBigEndian,convStr, true, true);
+ }
+ else {
+ appendStringAsUTF8((cAppCharP)aBuffer, convStr, fCharSet, lem_cstr, true);
+ }
+ // eventually convert text to native
+ switch (valType) {
+ case VALTYPE_TIME64:
+ // convert text to internal time format
+ ISO8601StrToTimestamp(convStr.c_str(), tempTime, tctx);
+ // if not floating, assume target timestamp is UTC, so ISO string w/o time zone is treated as system time for convenience
+ if ((fTimeMode & TMODE_FLAG_FLOATING)==0)
+ TzConvertTimestamp(tempTime, tctx, TCTX_UTC, getEngineInterface()->getSyncAppBase()->getAppZones(), TCTX_SYSTEM);
+ siz=8; bP = &tempTime;
+ break;
+ case VALTYPE_INT8:
+ case VALTYPE_INT16:
+ case VALTYPE_INT32:
+ case VALTYPE_INT64:
+ // convert text to integer
+ StrToLongLong(convStr.c_str(),tempInt);
+ goto intConv;
+ case VALTYPE_TEXT:
+ siz=convStr.size();
+ bP=(appPointer)convStr.c_str();
+ break;
+ default:
+ // cannot return as text
+ return LOCERR_WRONGUSAGE;
+ }
+ // set it
+ sta = SetValueInternal(aID,aArrayIndex,bP,siz);
+ break;
+
+ // Timestamp presented
+ case VALTYPE_TIME32:
+ if (aValSize<4) return LOCERR_BUFTOOSMALL;
+ tempTime = *((sInt32 *)aBuffer);
+ goto timeConv;
+ case VALTYPE_TIME64:
+ if (aValSize<8) return LOCERR_BUFTOOSMALL;
+ tempTime = *((sInt64 *)aBuffer);
+ timeConv:
+ switch (fTimeMode & TMODE_MODEMASK) {
+ case TMODE_LINEARDATE:
+ tempInt = tempTime * linearDateToTimeFactor;
+ break;
+ case TMODE_UNIXTIME_MS:
+ tempInt = tempTime+UnixToLineartimeOffset;
+ break;
+ case TMODE_UNIXTIME:
+ tempInt = secondToLinearTimeFactor*tempTime+UnixToLineartimeOffset;
+ break;
+ case TMODE_LINEARTIME:
+ default :
+ tempInt = tempTime;
+ break;
+ }
+ tctx = (fTimeMode & TMODE_FLAG_UTC)==0 ? TCTX_SYSTEM : TCTX_UTC;
+ // convert to UTC if needed
+ if ((fTimeMode & TMODE_FLAG_FLOATING)==0)
+ TzConvertTimestamp(tempInt,tctx,TCTX_UTC,getEngineInterface()->getSyncAppBase()->getAppZones());
+ goto intConv;
+
+ // Integer presented types
+ case VALTYPE_INT8:
+ case VALTYPE_ENUM:
+ if (aValSize<1) return LOCERR_BUFTOOSMALL;
+ tempInt = *((sInt8 *)aBuffer);
+ goto intConv;
+ case VALTYPE_INT16:
+ if (aValSize<2) return LOCERR_BUFTOOSMALL;
+ tempInt = *((sInt16 *)aBuffer);
+ goto intConv;
+ case VALTYPE_INT32:
+ if (aValSize<4) return LOCERR_BUFTOOSMALL;
+ tempInt = *((sInt32 *)aBuffer);
+ goto intConv;
+ case VALTYPE_INT64:
+ if (aValSize<8) return LOCERR_BUFTOOSMALL;
+ tempInt = *((sInt64 *)aBuffer);
+ intConv:
+ // convert integer into native type
+ bP = &tempInt; // re-use as temp buffer
+ switch (valType) {
+ case VALTYPE_INT8:
+ siz=1; *((sInt8 *)bP) = tempInt;
+ break;
+ case VALTYPE_INT16:
+ siz=2; *((sInt16 *)bP) = tempInt;
+ break;
+ case VALTYPE_INT32:
+ siz=4; *((sInt32 *)bP) = tempInt;
+ break;
+ case VALTYPE_INT64:
+ case VALTYPE_TIME64: // native timestamp
+ siz=8; *((sInt64 *)bP) = tempInt;
+ break;
+ default:
+ // other types (like text) cannot set as integer
+ return LOCERR_WRONGUSAGE; // conversion not allowed
+ break;
+ }
+ // now write int/time value
+ sta = SetValueInternal(aID,aArrayIndex,bP,siz);
+ break;
+
+ } // switch by presented type
+
+ return sta;
+} // TSettingsKeyImpl::SetValueByID
+
+
+
+// TReadOnlyInfoKey
+// ================
+
+
+// get value's ID (e.g. internal index)
+sInt32 TReadOnlyInfoKey::GetValueID(cAppCharP aName)
+{
+ const TReadOnlyInfo *tblP = getInfoTable();
+
+ sInt32 idx=0;
+ while(tblP && idx<numInfos()) {
+ if (strucmp(aName,tblP[idx].valName)==0) {
+ return idx; // found
+ }
+ // next
+ idx++;
+ }
+ // now check for iterator commands
+ if (strucmp(aName,VALNAME_FIRST)==0) {
+ fIterator=0;
+ if (fIterator<numInfos()) return fIterator;
+ }
+ else if (strucmp(aName,VALNAME_NEXT)==0) {
+ if (fIterator<numInfos()) fIterator++;
+ if (fIterator<numInfos()) return fIterator;
+ }
+ // not found
+ return KEYVAL_ID_UNKNOWN; // unknown
+} // TReadOnlyInfoKey::GetValueID
+
+
+// get value's native type
+uInt16 TReadOnlyInfoKey::GetValueType(sInt32 aID)
+{
+ if (aID>=numInfos()) return VALTYPE_UNKNOWN;
+ return getInfoTable()[aID].valType;
+} // TReadOnlyInfoKey::GetValueType
+
+
+// get value
+TSyError TReadOnlyInfoKey::GetValueInternal(
+ sInt32 aID, sInt32 aArrayIndex,
+ appPointer aBuffer, memSize aBufSize, memSize &aValSize
+)
+{
+ const TReadOnlyInfo *infoP = &(getInfoTable()[aID]);
+ memSize siz = infoP->valSiz;
+
+ if (siz==0 && infoP->valType==VALTYPE_TEXT)
+ siz = strlen((cAppCharP)(infoP->valPtr));
+ // return actual size
+ aValSize = siz;
+ // return requested amount of data or all we have, whatever is smaller
+ if (siz>aBufSize)
+ siz=aBufSize; // limit to buffer size (aBufSiz is compensated for NUL terminator already, so we can use memcpy for strings as well)
+ memcpy(aBuffer,infoP->valPtr,siz);
+ return LOCERR_OK;
+} // TReadOnlyInfoKey::GetValueInternal
+
+
+
+// TConfigVarKey
+// =============
+
+
+// get value's native type (all config vars are text)
+uInt16 TConfigVarKey::GetValueType(sInt32 aID) {
+ return VALTYPE_TEXT;
+} // TConfigVarKey::GetValueType
+
+
+// get value
+TSyError TConfigVarKey::GetValueInternal(
+ sInt32 aID, sInt32 aArrayIndex,
+ appPointer aBuffer, memSize aBufSize, memSize &aValSize
+) {
+ string s;
+ if (!fEngineInterfaceP->getSyncAppBase()->getConfigVar(fVarName.c_str(),s))
+ return DB_NotFound;
+ // copy string into buffer
+ aValSize=s.size();
+ strncpy((appCharP)aBuffer,s.c_str(),aBufSize);
+ return LOCERR_OK;
+} // TConfigVarKey::GetValueInternal
+
+
+// set value
+TSyError TConfigVarKey::SetValueInternal(
+ sInt32 aID, sInt32 aArrayIndex,
+ cAppPointer aBuffer, memSize aValSize
+) {
+ if (!fEngineInterfaceP->getSyncAppBase()->setConfigVar(fVarName.c_str(),(cAppCharP)aBuffer))
+ return DB_NotFound;
+ return LOCERR_OK;
+} // TConfigVarKey::SetValueInternal
+
+
+
+
+// TStructFieldsKey
+// ================
+
+
+// static helper for procedural string readers
+TSyError TStructFieldsKey::returnString(cAppCharP aReturnString, appPointer aBuffer, memSize aBufSize, memSize &aValSize)
+{
+ aValSize=strlen(aReturnString);
+ if (aBufSize>=aValSize) {
+ // copy string
+ strncpy((appCharP)aBuffer,aReturnString,aValSize);
+ }
+ return LOCERR_OK;
+} // returnString
+
+
+// static helper for procedural int readers
+TSyError TStructFieldsKey::returnInt(sInt32 aInt, memSize aIntSize, appPointer aBuffer, memSize aBufSize, memSize &aValSize)
+{
+ aValSize=aIntSize;
+ if (aBufSize==0) return LOCERR_OK; // measuring size
+ if (aBufSize<aIntSize) return LOCERR_BUFTOOSMALL;
+ if (aValSize>=4)
+ *((sInt32 *)aBuffer) = (sInt32)aInt;
+ else if (aValSize>=2)
+ *((sInt16 *)aBuffer) = (sInt16)aInt;
+ else
+ *((sInt8 *)aBuffer) = (sInt8)aInt;
+ return LOCERR_OK;
+} // returnInt
+
+
+
+// get value's ID (e.g. internal index)
+sInt32 TStructFieldsKey::GetValueID(cAppCharP aName)
+{
+ const TStructFieldInfo *tblP = getFieldsTable();
+
+ sInt32 idx=0;
+ while(tblP && idx<numFields()) {
+ if (strucmp(aName,tblP[idx].valName)==0) {
+ return idx; // found
+ }
+ // next
+ idx++;
+ }
+ // now check for iterator commands
+ if (strucmp(aName,VALNAME_FIRST)==0) {
+ fIterator=0;
+ if (fIterator<numFields()) return fIterator;
+ }
+ else if (strucmp(aName,VALNAME_NEXT)==0) {
+ if (fIterator<numFields()) fIterator++;
+ if (fIterator<numFields()) return fIterator;
+ }
+ // not found
+ return KEYVAL_ID_UNKNOWN; // unknown
+} // TStructFieldsKey::GetValueID
+
+
+// get value's native type
+uInt16 TStructFieldsKey::GetValueType(sInt32 aID)
+{
+ if (aID>=numFields()) return VALTYPE_UNKNOWN;
+ const TStructFieldInfo *fldinfoP = &(getFieldsTable()[aID]);
+ uInt16 valType = fldinfoP->valType;
+ if (valType == VALTYPE_ENUM) {
+ // VALTYPE_ENUM: determine integer type from actual size of variable
+ switch (fldinfoP->valSiz) {
+ default : return VALTYPE_INT8; // smallest causes least harm: use as default
+ case 2 : return VALTYPE_INT16;
+ case 4 : return VALTYPE_INT32;
+ case 8 : return VALTYPE_INT64;
+ }
+ }
+ else if (valType == VALTYPE_TEXT_OBFUS)
+ return VALTYPE_TEXT; // obfuscated fields are text for SDK user
+ else
+ return valType; // just return specified type
+} // TStructFieldsKey::GetValueType
+
+
+// get value
+TSyError TStructFieldsKey::GetValueInternal(
+ sInt32 aID, sInt32 aArrayIndex,
+ appPointer aBuffer, memSize aBufSize, memSize &aValSize
+)
+{
+ const TStructFieldInfo *fldinfoP = &(getFieldsTable()[aID]);
+ // check for programmatic access to value
+ if (fldinfoP->getValueProc) {
+ // get value programmatically
+ return fldinfoP->getValueProc(this,fldinfoP,aBuffer,aBufSize,aValSize);
+ }
+ // fetch value from structure
+ memSize siz = fldinfoP->valSiz; // get (max) size
+ if (siz==0) return DB_Forbidden; // read access not allowed
+ uInt8P valPtr = getStructAddr()+fldinfoP->fieldOffs;
+ // for text fields, measure actual size
+ if (fldinfoP->valType==VALTYPE_TEXT)
+ siz = strlen((cAppCharP)valPtr);
+ // return actual size
+ aValSize = siz;
+ // return requested amount of data or all we have, whatever is smaller
+ if (siz>aBufSize)
+ siz=aBufSize; // limit to buffer size (aBufSiz is compensated for NUL terminator already, so we can use memcpy for strings as well)
+ // for obfuscated fields, use special routine
+ if (fldinfoP->valType==VALTYPE_TEXT_OBFUS && siz>0) {
+ getUnmangledAsBuf((appCharP)aBuffer, siz, (cAppCharP)valPtr);
+ // measure decoded
+ aValSize = strlen((cAppCharP)aBuffer);
+ }
+ else
+ memcpy(aBuffer,valPtr,siz);
+ return LOCERR_OK;
+} // TStructFieldsKey::GetValueInternal
+
+
+// set value
+TSyError TStructFieldsKey::SetValueInternal(
+ sInt32 aID, sInt32 aArrayIndex,
+ cAppPointer aBuffer, memSize aValSize
+)
+{
+ TSyError sta = LOCERR_OK;
+ const TStructFieldInfo *fldinfoP = &(getFieldsTable()[aID]);
+ // refuse writing to read-only fields
+ if (!fldinfoP->writable) return DB_Forbidden;
+ // check for programmatic access to value
+ if (fldinfoP->setValueProc) {
+ // set value programmatically
+ return fldinfoP->setValueProc(this,fldinfoP,aBuffer,aValSize);
+ }
+ // write value to structure
+ memSize siz = fldinfoP->valSiz; // get (max) size (for strings: buffer including NUL)
+ if (siz==0) return DB_Forbidden; // write access not allowed
+ uInt8P valPtr = getStructAddr()+fldinfoP->fieldOffs;
+ // adjust usable size for strings
+ if (fldinfoP->valType == VALTYPE_TEXT)
+ siz--; // reserve one for terminator
+ // check and signal truncation
+ if (aValSize>siz)
+ sta=LOCERR_TRUNCATED;
+ else
+ siz=aValSize;
+ // copy data into struct
+ if (fldinfoP->valType==VALTYPE_TEXT_OBFUS)
+ assignMangledToCString((appCharP)valPtr, (cAppCharP)aBuffer, fldinfoP->valSiz, true); // always use entire buffer and fill it with garbage beyond end of actual data
+ else
+ memcpy(valPtr,aBuffer,siz);
+ // - struct modified, signal that (for derivates that might want to save the record on close)
+ fDirty=true;
+ // append terminator in case of text
+ if (fldinfoP->valType == VALTYPE_TEXT)
+ *(valPtr+siz)=0;
+ // done
+ return sta;
+} // TStructFieldsKey::SetValueInternal
+
+
+
+#ifdef SYSER_REGISTRATION
+
+// Licensing key
+// -------------
+
+// Accessors for license text and code
+
+// - read license text
+static TSyError readLicenseText(
+ TStructFieldsKey *aStructFieldsKeyP, const TStructFieldInfo *aFldInfoP,
+ appPointer aBuffer, memSize aBufSize, memSize &aValSize
+)
+{
+ // copy from config
+ aValSize=aStructFieldsKeyP->getEngineInterface()->getSyncAppBase()->getRootConfig()->fLicenseName.size();
+ if (aBufSize>0)
+ strncpy((char *)aBuffer,aStructFieldsKeyP->getEngineInterface()->getSyncAppBase()->getRootConfig()->fLicenseName.c_str(),aBufSize);
+ return LOCERR_OK;
+} // readLicenseText
+
+
+// - write license text
+static TSyError writeLicenseText(
+ TStructFieldsKey *aStructFieldsKeyP, const TStructFieldInfo *aFldInfoP,
+ cAppPointer aBuffer, memSize aValSize
+)
+{
+ // assign to config
+ aStructFieldsKeyP->getEngineInterface()->getSyncAppBase()->getRootConfig()->fLicenseName.assign((cAppCharP)aBuffer,aValSize);
+ /* DO NOT recalculate, only when setting code
+ // recalculate app enabled status
+ fEngineInterfaceP->getSyncAppBase()->isRegistered();
+ */
+ return LOCERR_OK;
+} // writeLicenseText
+
+
+// - write license code
+static TSyError writeLicenseCode(
+ TStructFieldsKey *aStructFieldsKeyP, const TStructFieldInfo *aFldInfoP,
+ cAppPointer aBuffer, memSize aValSize
+)
+{
+ // assign to config
+ aStructFieldsKeyP->getEngineInterface()->getSyncAppBase()->getRootConfig()->fLicenseCode.assign((cAppCharP)aBuffer,aValSize);
+ // recalculate all license dependent variables
+ aStructFieldsKeyP->getEngineInterface()->getSyncAppBase()->isRegistered();
+ // always ok
+ return LOCERR_OK;
+} // writeLicenseCode
+
+
+// - read registration status code
+static TSyError readRegStatus(
+ TStructFieldsKey *aStructFieldsKeyP, const TStructFieldInfo *aFldInfoP,
+ appPointer aBuffer, memSize aBufSize, memSize &aValSize
+)
+{
+ aValSize=2;
+ if (aBufSize>=aValSize) {
+ // recalculate all license dependent variables
+ localstatus sta = aStructFieldsKeyP->getEngineInterface()->getSyncAppBase()->isRegistered();
+ // copy from config
+ *((uInt16*)aBuffer)=sta;
+ }
+ return LOCERR_OK;
+} // readRegStatus
+
+
+
+// macro simplifying typing in the table below
+#define OFFS_SZ_AB(n) (offsetof(TSyncAppBase,n)), sizeof(dP_ab->n)
+// dummy pointer needed for sizeof
+static const TSyncAppBase *dP_ab=NULL;
+
+// accessor table for licensing info
+static const TStructFieldInfo LicensingFieldInfos[] =
+{
+ // valName, valType, writable, fieldOffs, valSiz
+ // - license text and code (writable)
+ { "licensetext", VALTYPE_TEXT, true, 0, 0, &readLicenseText, &writeLicenseText },
+ { "licensecode", VALTYPE_TEXT, true, 0, 0, NULL, &writeLicenseCode },
+ // - read-only info about licensing status from syncappbase
+ { "regStatus", VALTYPE_INT16, false, 0, 0, &readRegStatus, NULL },
+ { "regOK", VALTYPE_ENUM, false, OFFS_SZ_AB(fRegOK) },
+ { "productCode", VALTYPE_ENUM, false, OFFS_SZ_AB(fRegProductCode) },
+ { "productFlags", VALTYPE_ENUM, false, OFFS_SZ_AB(fRegProductFlags) },
+ { "quantity", VALTYPE_ENUM, false, OFFS_SZ_AB(fRegQuantity) },
+ { "licenseType", VALTYPE_ENUM, false, OFFS_SZ_AB(fRegLicenseType) },
+ { "daysleft", VALTYPE_ENUM, false, OFFS_SZ_AB(fDaysLeft) },
+};
+
+
+// get table describing the fields in the struct
+const TStructFieldInfo *TLicensingKey::getFieldsTable(void)
+{
+ return LicensingFieldInfos;
+} // TLicensingKey::getFieldsTable
+
+sInt32 TLicensingKey::numFields(void)
+{
+ return sizeof(LicensingFieldInfos)/sizeof(TStructFieldInfo);
+} // TLicensingKey::numFields
+
+
+
+// get actual struct base address
+uInt8P TLicensingKey::getStructAddr(void)
+{
+ return (uInt8P)fEngineInterfaceP->getSyncAppBase(); // we are accessing SyncAppBase fields
+} // TLicensingKey::getStructAddr
+
+
+#endif // SYSER_REGISTRATION
+
+
+// TEngineInterface
+// ================
+
+// constructor
+TEngineInterface::TEngineInterface() :
+ fAppBaseP(NULL)
+{
+ // init string modes to default
+ fCharSet = chs_utf8;
+ fLineEndMode = lem_cstr;
+ fBigEndian = false; // default to Intel order
+} // TEngineInterface
+
+
+// constructor
+TSyError TEngineInterface::Init()
+{
+ #ifdef DIRECT_APPBASE_GLOBALACCESS
+ // global anchor only exists in old-style targets; pure engines do not have it any more
+ sysync_glob_setanchor(this);
+ #endif
+ // create the appropriate SyncAppBase object
+ fAppBaseP = newSyncAppBase();
+ if (fAppBaseP==NULL)
+ return LOCERR_UNDEFINED;
+ // set myself as the Master
+ fAppBaseP->fEngineInterfaceP = this;
+ // connect the debug routines to the appbase logger
+ #ifdef SYDEBUG
+ if (fCI) {
+ fCI->callbackRef = fAppBaseP;
+ fCI->DB_DebugPuts = AppBaseLogDebugPuts;
+ fCI->DB_DebugBlock = AppBaseLogDebugBlock;
+ fCI->DB_DebugEndBlock = AppBaseLogDebugEndBlock;
+ fCI->DB_DebugEndThread = AppBaseLogDebugEndThread;
+ fCI->DB_DebugExotic = AppBaseLogDebugExotic;
+ }
+ #endif
+ // we have an appbase now
+ return LOCERR_OK;
+} // TEngineInterface::Init
+
+
+TSyError TEngineInterface::Term()
+{ // empty for the moment
+ return LOCERR_OK;
+} // TEnigneInterface::Term
+
+
+// destructor
+TEngineInterface::~TEngineInterface()
+{
+ // delete the SyncAppBase
+ if (fAppBaseP) {
+ delete fAppBaseP;
+ fAppBaseP=NULL;
+ }
+ #ifdef DIRECT_APPBASE_GLOBALACCESS
+ // global anchor only exists in old-style targets; pure engines do not have it any more
+ sysync_glob_setanchor(NULL);
+ #endif
+} // ~TEngineInterface
+
+
+// Set text format parameters
+TSyError TEngineInterface::SetStringMode(uInt16 aCharSet, uInt16 aLineEndMode, bool aBigEndian)
+{
+ // translate charset
+ uInt16 chs;
+ for (chs=0; chs<numCharSets; chs++) {
+ if (engineCharSets[chs]==aCharSet) break;
+ }
+ if (chs==numCharSets) return LOCERR_BADPARAM;
+ // translate lineendmode
+ uInt16 lem;
+ for (lem=0; lem<numLineEndModes; lem++) {
+ if (engineLineEndModes[lem]==aLineEndMode) break;
+ }
+ if (lem==numLineEndModes) return LOCERR_BADPARAM;
+ // assign them now they are checked ok
+ fCharSet = (TCharSets)chs;
+ fLineEndMode = (TLineEndModes)lem;
+ // big endian flag
+ fBigEndian = aBigEndian;
+ return LOCERR_OK;
+} // TEngineInterface::SetStringMode
+
+
+
+// conversion helper
+// - converts byte stream (which can be an 8-bit char set or unicode in intel or motorola order)
+// to internal UTF-8, according to text mode set with SetStringMode()
+cAppCharP TEngineInterface::makeAppString(cAppCharP aTextP, string &aString)
+{
+ // return as-is if input is UTF-8 already
+ if (fCharSet==chs_utf8)
+ return cAppCharP(aTextP);
+ // convert into passed string
+ if (fCharSet==chs_utf16) {
+ appendUTF16AsUTF8((uInt16 *)aTextP,0x7FFFFFFF,fBigEndian,aString, true, true);
+ }
+ else {
+ appendStringAsUTF8((cAppCharP)aTextP, aString, fCharSet, fLineEndMode, true);
+ }
+ // return pointer to just converted string
+ return aString.c_str();
+} // TEngineInterface::makeAppString
+
+
+
+/// @brief init object, optionally passing XML config text in memory
+/// @param aConfigXML[in] NULL if no external config needed, config text otherwise
+/// @return LOCERR_OK on success, SyncML or LOCERR_xxx error code on failure
+TSyError TEngineInterface::InitEngineXML(cAppCharP aConfigXML)
+{
+ #ifndef CONSTANTXML_CONFIG
+ return LOCERR_NOTIMP;
+ #else
+ // get the global context for pooling ressources like Java VM
+ fAppBaseP->fApiInterModuleContext = fCI->gContext;
+ // make sure we have string as UTF-8
+ string tempStr;
+ aConfigXML = makeAppString(aConfigXML, tempStr);
+ // feed config string directly into engine
+ return getSyncAppBase()->readXMLConfigConstant(aConfigXML);
+ #endif
+} // TEngineInterface::InitEngineXML
+
+
+/// @brief init object, optionally passing a open FILE for reading config
+/// @param aCfgFile[in] open file containing XML config
+/// @return LOCERR_OK on success, SyncML or LOCERR_xxx error code on failure
+TSyError TEngineInterface::InitEngineFile(cAppCharP aConfigFilePath)
+{
+ // make sure we have string as UTF-8
+ string tempStr;
+ aConfigFilePath = makeAppString(aConfigFilePath, tempStr);
+ // get the global context for pooling ressources like Java VM
+ fAppBaseP->fApiInterModuleContext = fCI->gContext;
+ // read config from a file
+ string pathStr = aConfigFilePath;
+ getSyncAppBase()->expandConfigVars(pathStr,2);
+ // now parse config
+ return getSyncAppBase()->readXMLConfigFile(pathStr.c_str());
+} // TEngineInterface::InitEngineFile
+
+
+/// @brief init object, optionally passing a callback for reading config
+/// @param aReaderFunc[in] callback function which can deliver next chunk of XML config data
+/// @param aContext[in] free context pointer passed back with callback
+/// @return LOCERR_OK on success, SyncML or LOCERR_xxx error code on failure
+TSyError TEngineInterface::InitEngineCB (TXMLConfigReadFunc aReaderFunc, void *aContext)
+{
+ // get the global context for pooling ressources like Java VM
+ fAppBaseP->fApiInterModuleContext = fCI->gContext;
+ // now parse config
+ return getSyncAppBase()->readXMLConfigStream(aReaderFunc, aContext);
+} // TEngineInterface::InitEngineCB
+
+
+/// @brief Open a session
+/// @param aNewSessionH[out] receives session handle for all session execution calls
+/// @param aSelector[in] selector, depending on session type. For multi-profile clients: profile ID to use
+/// @param aSessionName[in] a text name/id to identify a session, useage depending on session type.
+/// @return LOCERR_OK on success, SyncML or LOCERR_xxx error code on failure
+TSyError TEngineInterface::OpenSession(SessionH &aNewSessionH, uInt32 aSelector, cAppCharP aSessionName)
+{
+ // safety check - app must be enabled (initialized with config and ok license before we can open a session)
+ localstatus sta=getSyncAppBase()->appEnableStatus();
+ if (sta!=LOCERR_OK) return sta; // app not enabled (and maybe not initialized properly) - prevent any session opening
+ // make sure we have string as UTF-8
+ string tempStr;
+ aSessionName = makeAppString(aSessionName, tempStr);
+
+ return OpenSessionInternal(aNewSessionH, aSelector, aSessionName);
+} // TEngineInterface::OpenSession
+
+/// @brief internal implementation, derived in actual clients or servers
+TSyError TEngineInterface::OpenSessionInternal(SessionH &aNewSessionH, uInt32 aSelector, cAppCharP aSessionName)
+{
+ // must be implemented in derived class
+ return LOCERR_NOTIMP;
+} // TEngineInterface::OpenSessionInternal
+
+
+/// @brief open session specific runtime parameter/settings key
+/// @note key handle obtained with this call must be closed BEFORE SESSION IS CLOSED!
+/// @param aNewKeyH[out] receives the opened key's handle on success
+/// @param aSessionH[in] session handle obtained with OpenSession
+/// @param aMode[in] the open mode
+/// @return LOCERR_OK on success, SyncML or LOCERR_xxx error code on failure
+TSyError TEngineInterface::OpenSessionKey(SessionH aSessionH, KeyH &aNewKeyH, uInt16 aMode)
+{
+ // no session key available in base class
+ return DB_NotFound;
+} // TEngineInterface::OpenSessionKey
+
+
+
+/// @brief Close a session
+/// @note It depends on session type if this also destroys the session or if it may persist and can be re-opened.
+/// @param aSessionH[in] session handle obtained with OpenSession
+/// @return LOCERR_OK on success, SyncML or LOCERR_xxx error code on failure
+TSyError TEngineInterface::CloseSession(SessionH aSessionH)
+{
+ // must be implemented in derived class
+ return LOCERR_NOTIMP;
+} // TEngineInterface::CloseSession
+
+
+/// @brief Executes next step of the session
+/// @param aSessionH[in] session handle obtained with OpenSession
+/// @param aStepCmd[in/out] step command (STEPCMD_xxx):
+/// - tells caller to send or receive data or end the session etc.
+/// - instructs engine to suspend or abort the session etc.
+/// @param aInfoP[in] pointer to a TEngineProgressInfo structure, NULL if no progress info needed
+/// @return LOCERR_OK on success, SyncML or LOCERR_xxx error code on failure
+TSyError TEngineInterface::SessionStep (SessionH aSessionH, uInt16 &aStepCmd, TEngineProgressInfo *aInfoP)
+{
+ // must be implemented in derived class
+ return LOCERR_NOTIMP;
+} // TEngineInterface::SessionStep
+
+
+/// @brief Get access to SyncML message buffer
+/// @param aSessionH[in] session handle obtained with OpenSession
+/// @param aForSend[in] send mode (true) / receive mode (false)
+/// @param aBuffer[out] receives pointer to buffer (empty for receive, full for send)
+/// @param aBufSize[out] receives size of empty or full buffer
+/// @return LOCERR_OK on success, SyncML or LOCERR_xxx error code on failure
+TSyError TEngineInterface::GetSyncMLBuffer(SessionH aSessionH, bool aForSend, appPointer &aBuffer, memSize &aBufSize)
+{
+ Ret_t rc;
+ // get SML instance for this session (note that "session" could be a SAN checker as well)
+ InstanceID_t myInstance = getSmlInstanceOfSession(aSessionH);
+ if (myInstance==0)
+ return LOCERR_WRONGUSAGE; // no instance, usage must be wrong
+ // provide access to the buffer
+ if (aForSend) {
+ // we want to read the SyncML buffer
+ rc=smlLockReadBuffer(
+ myInstance,
+ (unsigned char **)&aBuffer, // receives address of buffer containing SyncML to send
+ (long *)&aBufSize // receives size of SyncML to send
+ );
+ }
+ else {
+ // we want to write the SyncML buffer
+ rc=smlLockWriteBuffer(
+ myInstance,
+ (unsigned char **)&aBuffer, // receives address of buffer where received SyncML can be put
+ (long *)&aBufSize // capacity of the buffer
+ );
+ }
+ // check error
+ if (rc!=SML_ERR_OK)
+ return LOCERR_SMLFATAL; // problem with SML toolkit
+ // done
+ return LOCERR_OK;
+} // TEngineInterface::GetSyncMLBuffer
+
+
+
+/// @brief Return SyncML message buffer to engine
+/// @param aSessionH[in] session handle obtained with OpenSession
+/// @param aForSend[in] send mode (true) / receive mode (false)
+/// @param aProcessed[in] number of bytes put into or read from the buffer
+/// @return LOCERR_OK on success, SyncML or LOCERR_xxx error code on failure
+TSyError TEngineInterface::RetSyncMLBuffer(SessionH aSessionH, bool aForSend, memSize aProcessed)
+{
+ Ret_t rc;
+
+ // get SML instance for this session (note that "session" could be a SAN checker as well)
+ InstanceID_t myInstance = getSmlInstanceOfSession(aSessionH);
+ if (myInstance==0)
+ return LOCERR_WRONGUSAGE; // no instance, usage must be wrong
+ // return buffer
+ if (aForSend) {
+ // we have read the SyncML from the buffer (and usually sent to the remote)
+ rc=smlUnlockReadBuffer(
+ myInstance,
+ aProcessed // number of bytes read from the buffer (and sent to the remote, hopefully...)
+ );
+ }
+ else {
+ // we have written some data (usually received from the remote) into the SyncML buffer
+ rc=smlUnlockWriteBuffer(
+ myInstance,
+ aProcessed // number of SyncML bytes put into the buffer (coming from the remote, usually...)
+ );
+ }
+ // check error
+ if (rc!=SML_ERR_OK)
+ return LOCERR_SMLFATAL; // problem with SML toolkit
+ // done
+ return LOCERR_OK;
+} // TEngineInterface::RetSyncMLBuffer
+
+
+/// @brief Read data from SyncML message buffer
+/// @param aSessionH[in] session handle obtained with OpenSession
+/// @param aBuffer[in] pointer to buffer
+/// @param aBufSize[in] size of buffer, maximum to be read
+/// @param aMsgSize[out] size of data available in the buffer for read INCLUDING just returned data.
+/// @note If the aBufSize is too small to return all available data LOCERR_TRUNCATED will be returned, and the
+/// caller can repeat calls to ReadSyncMLBuffer to get the next chunk.
+/// @return LOCERR_OK on success, SyncML or LOCERR_xxx error code on failure
+TSyError TEngineInterface::ReadSyncMLBuffer(SessionH aSessionH, appPointer aBuffer, memSize aBufSize, memSize &aMsgSize)
+{
+ TSyError sta;
+ appPointer bufP;
+ memSize msgsiz;
+
+ // get buffer for read
+ sta = GetSyncMLBuffer(aSessionH,true,bufP,msgsiz);
+ if (sta!=LOCERR_OK) return sta;
+ // anyway: pass size of data in the buffer
+ aMsgSize = msgsiz;
+ // check if buffer passed is large enough
+ if (aBufSize<msgsiz) {
+ sta = LOCERR_TRUNCATED;
+ // adjust number of bytes to copy
+ msgsiz=aBufSize;
+ }
+ if (msgsiz>0) {
+ // copy data
+ memcpy(aBuffer,bufP,msgsiz);
+ }
+ // return SyncML buffer to engine
+ RetSyncMLBuffer(aSessionH,true,msgsiz);
+ // return status
+ return sta;
+} // TEngineInterface::ReadSyncMLBuffer
+
+
+/// @brief Write data to SyncML message buffer
+/// @param aSessionH[in] session handle obtained with OpenSession
+/// @param aBuffer[in] pointer to buffer
+/// @param aMsgSize[in] size of message to write to the buffer
+/// @return LOCERR_OK on success, SyncML or LOCERR_xxx error code on failure
+TSyError TEngineInterface::WriteSyncMLBuffer(SessionH aSessionH, appPointer aBuffer, memSize aMsgSize)
+{
+ TSyError sta;
+ appPointer bufP;
+ memSize bufsiz;
+
+ // get buffer for write
+ sta = GetSyncMLBuffer(aSessionH,false,bufP,bufsiz);
+ if (sta!=LOCERR_OK) return sta;
+ // check if buffer large enough to write data
+ if (aMsgSize>bufsiz) {
+ // can't write data - message too large
+ sta = LOCERR_BUFTOOSMALL;
+ aMsgSize=0; // don't write anything
+ }
+ else {
+ if (aMsgSize>0) {
+ // copy data
+ memcpy(bufP,aBuffer,aMsgSize);
+ }
+ }
+ // return SyncML buffer to engine
+ RetSyncMLBuffer(aSessionH,false,aMsgSize);
+ // return status
+ return sta;
+} // TEngineInterface::WriteSyncMLBuffer
+
+
+
+
+
+/// @brief open Settings key by path specification
+/// @param aNewKeyH[out] receives the opened key's handle on success
+/// @param aParentKeyH[in] NULL if path is absolute from root, handle to an open key for relative access
+/// @param aPath[in] the path specification as null terminated string
+/// @param aMode[in] the open mode
+/// @return LOCERR_OK on success, SyncML or LOCERR_xxx error code on failure
+TSyError TEngineInterface::OpenKeyByPath (
+ KeyH &aNewKeyH,
+ KeyH aParentKeyH,
+ cAppCharP aPath, uInt16 aMode
+) {
+ TSettingsKeyImpl *aBaseKeyP = NULL;
+ TSettingsKeyImpl *aKeyP = NULL;
+ bool implicit = false;
+ TSyError sta;
+
+ // make sure we have string as UTF-8
+ string tempStr;
+ aPath = makeAppString(aPath, tempStr);
+
+ // determine base key
+ if (aParentKeyH) {
+ // already opened base key passed
+ aBaseKeyP = reinterpret_cast<TSettingsKeyImpl *>(aParentKeyH);
+ implicit = false; // we have the parent already, do not delete it implicitly
+ }
+ else {
+ // we need to implicitly create the root key
+ aBaseKeyP = newSettingsRootKey();
+ implicit = true; // the root key is implicitly created and must be deleted implicitly as well
+ }
+ // open key
+ if (implicit && (aPath==NULL || *aPath==0 || (*aPath==SETTINGSKEY_PATH_SEPARATOR && *(aPath+1)==0))) {
+ // opening root requested
+ aKeyP = aBaseKeyP;
+ sta = LOCERR_OK;
+ }
+ else {
+ // open subkey of current key
+ sta = aBaseKeyP->OpenKeyByPath(aKeyP,aPath,aMode,implicit);
+ }
+ if (sta!=LOCERR_OK) {
+ if (aKeyP) {
+ // make sure all implicitly opened elements of the chain are deleted
+ // (includes the TSettingsRootKey, if one was created above)
+ delete aKeyP;
+ }
+ }
+ else {
+ // pass back pointer to opened key
+ aNewKeyH = (KeyH)aKeyP;
+ }
+ return sta;
+} // TEngineInterface::OpenKeyByPath
+
+
+
+/// @brief open Settings subkey key by ID or iterating over all subkeys
+/// @param aNewKeyH[out] receives the opened key's handle on success
+/// @param aParentKeyH[in] handle to the parent key
+/// @param aID[in] the ID of the subkey to open,
+/// or KEYVAL_ID_FIRST/KEYVAL_ID_NEXT to iterate over existing subkeys
+/// or KEYVAL_ID_NEW to create a new subkey
+/// @param aMode[in] the open mode
+/// @return LOCERR_OK on success, DB_NoContent when no more subkeys are found with
+/// KEYVAL_ID_FIRST/KEYVAL_ID_NEXT
+/// or any other SyncML or LOCERR_xxx error code on failure
+TSyError TEngineInterface::OpenSubkey(
+ KeyH &aNewKeyH,
+ KeyH aParentKeyH,
+ sInt32 aID, uInt16 aMode
+) {
+ TSettingsKeyImpl *baseKeyP = NULL;
+ TSettingsKeyImpl *keyP = NULL;
+ TSyError sta;
+
+ // We need a parent key
+ if (!aParentKeyH) return LOCERR_WRONGUSAGE;
+ // Get key
+ baseKeyP = reinterpret_cast<TSettingsKeyImpl *>(aParentKeyH);
+ // now open subkey
+ sta = baseKeyP->OpenSubkey(keyP,aID,aMode);
+ if (sta == LOCERR_OK) {
+ // return key handle
+ aNewKeyH = (KeyH)keyP;
+ }
+ return sta;
+} // TEngineInterface::OpenSubkey
+
+
+/// @brief delete Settings subkey key by ID
+/// @param aID[in] the ID of the subkey to delete
+/// @param aParentKeyH[in] handle to the parent key
+/// @return LOCERR_OK on success
+/// or any other SyncML or LOCERR_xxx error code on failure
+TSyError TEngineInterface::DeleteSubkey(KeyH aParentKeyH, sInt32 aID)
+{
+ // We need a parent key
+ if (!aParentKeyH) return LOCERR_WRONGUSAGE;
+ // Get key
+ return reinterpret_cast<TSettingsKeyImpl *>(aParentKeyH)->DeleteSubkey(aID);
+} // TEngineInterface::DeleteSubkey
+
+
+/// @brief Get key ID of currently open key. Note that the Key ID is only locally unique within
+/// the parent key.
+/// @param aKeyH[in] an open key handle
+/// @param aID[out] receives the ID of the open key, which can be used to re-access the
+/// key within its parent using OpenSubkey()
+/// @return LOCERR_OK on success, SyncML or LOCERR_xxx error code on failure
+TSyError TEngineInterface::GetKeyID(KeyH aKeyH, sInt32 &aID)
+{
+ if (!aKeyH) return LOCERR_WRONGUSAGE;
+ return reinterpret_cast<TSettingsKeyImpl *>(aKeyH)->GetKeyID(aID);
+} // TEngineInterface::GetKeyID
+
+
+
+
+/// @brief Set text format parameters
+/// @param aKeyH[in] an open key handle
+/// @param aCharSet[in] charset (default is UTF-8 when SetTextMode() is not used)
+/// @param aLineEndMode[in] line end mode (default is C-lineends of the platform (almost always LF) when SetTextMode() is not used)
+/// @param aBigEndian[in] determines endianness of UTF16 text (defaults to little endian = intel order)
+/// @return LOCERR_OK on success, SyncML or LOCERR_xxx error code on failure
+TSyError TEngineInterface::SetTextMode(KeyH aKeyH, uInt16 aCharSet, uInt16 aLineEndMode, bool aBigEndian)
+{
+ if (!aKeyH) return LOCERR_WRONGUSAGE;
+ return reinterpret_cast<TSettingsKeyImpl *>(aKeyH)->SetTextMode(aCharSet,aLineEndMode,aBigEndian);
+} // TEngineInterface::SetTextMode
+
+
+
+/// @brief Set time format parameters
+/// @param aKeyH[in] an open key handle
+/// @param aTimeMode[in] time mode, see TMODE_xxx (default is platform's lineratime_t when SetTimeMode() is not used)
+/// @return LOCERR_OK on success, SyncML or LOCERR_xxx error code on failure
+TSyError TEngineInterface::SetTimeMode(KeyH aKeyH, uInt16 aTimeMode)
+{
+ if (!aKeyH) return LOCERR_WRONGUSAGE;
+ return reinterpret_cast<TSettingsKeyImpl *>(aKeyH)->SetTimeMode(aTimeMode);
+} // TEngineInterface::SetTimeMode
+
+
+
+/// @brief Closes a key opened by OpenKeyByPath() or OpenSubKey()
+/// @param aKeyH[in] an open key handle. Will be invalid when call returns with LOCERR_OK. Do not re-use!
+/// @return LOCERR_OK on success, SyncML or LOCERR_xxx error code on failure
+TSyError TEngineInterface::CloseKey(KeyH aKeyH)
+{
+ if (aKeyH) {
+ // deleting key will close it (including all implicitly opened parents)
+ delete reinterpret_cast<TSettingsKeyImpl *>(aKeyH);
+ }
+ return LOCERR_OK;
+} // TEngineInterface::CloseKey
+
+
+/// @brief get value's ID for use with Get/SetValueByID()
+/// @return KEYVAL_ID_UNKNOWN when name is unknown,
+/// KEYVAL_NO_ID when name is known, but no ID exists for it (ID access not possible)
+/// ID of value otherwise
+sInt32 TEngineInterface::GetValueID(KeyH aKeyH, cAppCharP aName)
+{
+ if (!aKeyH) return KEYVAL_ID_UNKNOWN;
+
+ // make sure we have string as UTF-8
+ string tempStr;
+ aName = makeAppString(aName, tempStr);
+
+ return reinterpret_cast<TSettingsKeyImpl *>(aKeyH)->GetValueID(aName);
+} // TEngineInterface::GetValueID
+
+
+/// @brief Reads a named value in specified format into passed memory buffer
+/// @param aKeyH[in] an open key handle
+/// @param aValueName[in] name of the value to read
+/// @param aValType[in] desired return type, see VALTYPE_xxxx
+/// @param aBuffer[in/out] buffer where to store the data
+/// @param aBufSize[in] size of buffer in bytes (ALWAYS in bytes, even if value is Unicode string)
+/// @param aValSize[out] actual size of value.
+/// For VALTYPE_TEXT, size is string length (IN BYTES) excluding NULL terminator
+/// Note that this will be set also when return value is LOCERR_BUFTOOSMALL, to indicate the required buffer size
+/// @return LOCERR_OK on success, SyncML or LOCERR_xxx error code on failure
+TSyError TEngineInterface::GetValue(
+ KeyH aKeyH, cAppCharP aValueName,
+ uInt16 aValType,
+ appPointer aBuffer, memSize aBufSize, memSize &aValSize
+)
+{
+ if (!aKeyH) return LOCERR_WRONGUSAGE;
+
+ // GetValueID will convert aValueName to app format if needed
+ sInt32 valId = GetValueID(aKeyH,aValueName);
+ if (valId==KEYVAL_ID_UNKNOWN) return DB_NotFound;
+ // directly call settingsKeyImpl, for cases where value has no ID
+ return reinterpret_cast<TSettingsKeyImpl *>(aKeyH)->GetValueByID(
+ valId,0,aValType,aBuffer,aBufSize,aValSize
+ );
+} // GetValue
+
+
+/// @brief Reads value by ID in specified format into passed memory buffer
+/// @param aKeyH[in] an open key handle
+/// @param aID[in] ID of the value to read
+/// @param aArrayIndex[in] 0-based array element index for array values.
+/// @param aValType[in] desired return type, see VALTYPE_xxxx
+/// @param aBuffer[in/out] buffer where to store the data
+/// @param aBufSize[in] size of buffer in bytes (ALWAYS in bytes, even if value is Unicode string)
+/// @param aValSize[out] actual size of value.
+/// For VALTYPE_TEXT, size is string length (IN BYTES) excluding NULL terminator
+/// Note that this will be set also when return value is LOCERR_BUFTOOSMALL, to indicate the required buffer size
+/// @return LOCERR_OK on success, LOCERR_OUTOFRANGE when array index is out of range
+/// SyncML or LOCERR_xxx error code on other failure
+TSyError TEngineInterface::GetValueByID(
+ KeyH aKeyH, sInt32 aID, sInt32 aArrayIndex,
+ uInt16 aValType,
+ appPointer aBuffer, memSize aBufSize, memSize &aValSize
+)
+{
+ if (!aKeyH) return LOCERR_WRONGUSAGE;
+ if (aID==KEYVAL_NO_ID) return LOCERR_BADPARAM;
+ return reinterpret_cast<TSettingsKeyImpl *>(aKeyH)->GetValueByID(
+ aID,aArrayIndex,aValType,aBuffer,aBufSize,aValSize
+ );
+} // TEngineInterface::GetValueByID
+
+
+
+/// @brief Writes a named value in specified format passed in memory buffer
+/// @param aKeyH[in] an open key handle
+/// @param aValueName[in] name of the value to write
+/// @param aValType[in] type of value passed in, see VALTYPE_xxxx
+/// @param aBuffer[in] buffer containing the data
+/// @param aValSize[in] size of value. For VALTYPE_TEXT, size can be passed as -1 if string is null terminated
+/// @return LOCERR_OK on success, SyncML or LOCERR_xxx error code on failure
+TSyError TEngineInterface::SetValue(
+ KeyH aKeyH, cAppCharP aValueName,
+ uInt16 aValType,
+ cAppPointer aBuffer, memSize aValSize
+) {
+ if (!aKeyH) return LOCERR_WRONGUSAGE;
+
+ // GetValueID will convert aValueName to app format if needed
+ sInt32 valId = GetValueID(aKeyH,aValueName);
+ if (valId==KEYVAL_ID_UNKNOWN) return DB_NotFound;
+ return reinterpret_cast<TSettingsKeyImpl *>(aKeyH)->SetValueByID(
+ valId,0,aValType,aBuffer,aValSize
+ );
+} // TEngineInterface::SetValue
+
+
+/// @brief Writes a named value in specified format passed in memory buffer
+/// @param aKeyH[in] an open key handle
+/// @param aID[in] ID of the value to read
+/// @param aArrayIndex[in] 0-based array element index for array values.
+/// @param aValType[in] type of value passed in, see VALTYPE_xxxx
+/// @param aBuffer[in] buffer containing the data
+/// @param aValSize[in] size of value. For VALTYPE_TEXT, size can be passed as -1 if string is null terminated
+/// @return LOCERR_OK on success, SyncML or LOCERR_xxx error code on failure
+TSyError TEngineInterface::SetValueByID(
+ KeyH aKeyH, sInt32 aID, sInt32 aArrayIndex,
+ uInt16 aValType,
+ cAppPointer aBuffer, memSize aValSize
+)
+{
+ if (!aKeyH) return LOCERR_WRONGUSAGE;
+ if (aID==KEYVAL_NO_ID) return LOCERR_BADPARAM;
+ return reinterpret_cast<TSettingsKeyImpl *>(aKeyH)->SetValueByID(
+ aID,aArrayIndex,aValType,aBuffer,aValSize
+ );
+} // TEngineInterface::SetValueByID
+
+
+
+#ifdef DBAPI_TUNNEL_SUPPORT
+
+// DBApi-like access to datastores
+// ===============================
+
+TSyError TEngineInterface::StartDataRead( CContext ac, cAppCharP lastToken, cAppCharP resumeToken )
+{
+ #error "complete these for real DBAPI_TUNNEL_SUPPORT (or do not define DBAPI_TUNNEL_SUPPORT to switch it off)"
+ return DB_Error;
+} // StartDataRead
+
+
+TSyError TEngineInterface::ReadNextItem ( CContext ac, ItemID aID, appCharP *aItemData,
+ sInt32 *aStatus, bool aFirst )
+{
+ return DB_Error;
+} // ReadNextItem
+
+
+TSyError TEngineInterface::ReadItem( CContext ac, cItemID aID, appCharP *aItemData )
+{
+ return DB_Error;
+} // ReadItem
+
+
+TSyError TEngineInterface::EndDataRead( CContext ac )
+{
+ return DB_Error;
+} // EndDataRead
+
+
+TSyError TEngineInterface::StartDataWrite( CContext ac )
+{
+ return DB_Error;
+} // StartDataWrite
+
+
+TSyError TEngineInterface::InsertItem( CContext ac, cAppCharP aItemData, cItemID aID )
+{
+ return DB_Error;
+} // InsertItem
+
+
+TSyError TEngineInterface::UpdateItem( CContext ac, cAppCharP aItemData, cItemID aID,
+ ItemID updID )
+{
+ return DB_Error;
+} // UpdateItem
+
+
+TSyError TEngineInterface::MoveItem( CContext ac, cItemID aID, cAppCharP newParID )
+{
+ return DB_Error;
+} // MoveItem
+
+
+TSyError TEngineInterface::DeleteItem( CContext ac, cItemID aID )
+{
+ return DB_Error;
+} // DeleteItem
+
+
+TSyError TEngineInterface::EndDataWrite( CContext ac, bool success, appCharP *newToken )
+{
+ return DB_Error;
+} // EndDataWrite
+
+
+void TEngineInterface::DisposeObj( CContext ac, void* memory )
+{
+} // DisposeObj
+
+
+// ---- asKey ----
+TSyError TEngineInterface::ReadNextItemAsKey( CContext ac, ItemID aID, KeyH aItemKey,
+ sInt32 *aStatus, bool aFirst )
+{
+ return DB_Error;
+} // ReadNextItemAsKey
+
+
+TSyError TEngineInterface::ReadItemAsKey( CContext ac, cItemID aID, KeyH aItemKey )
+{
+ return DB_Error;
+} // ReadItemAsKey
+
+
+TSyError TEngineInterface::InsertItemAsKey( CContext ac, KeyH aItemKey, cItemID aID )
+{
+ return DB_Error;
+} // InsertItemAsKey
+
+
+TSyError TEngineInterface::UpdateItemAsKey( CContext ac, KeyH aItemKey, cItemID aID,
+ ItemID updID )
+{
+ return DB_Error;
+} // UpdateItemAsKey
+
+#endif // DBAPI_TUNNEL_SUPPORT
+
+
+#ifndef SIMPLE_LINKING
+
+// Callback "factory" function implementation
+
+// Main entry point
+TSyError SYSYNC_EXTERNAL(ConnectEngine)(
+ UI_Call_In *aCI,
+ CVersion *aEngVersion,
+ CVersion aPrgVersion,
+ uInt16 aDebugFlags
+)
+{
+ // create new engine
+ TEngineModuleBase *engine = newEngine();
+ // connect callback
+ TSyError err= engine->Connect("", aPrgVersion, aDebugFlags);
+ *aCI = engine->fCI;
+ *aEngVersion= Plugin_Version(0);
+ return err;
+} // ConnectEngine
+
+
+// The same, but coming in with a valid <aCI> */
+TSyError SYSYNC_EXTERNAL(ConnectEngineS)(
+ UI_Call_In aCI,
+ uInt16 aCallbackVersion,
+ CVersion *aEngVersion,
+ CVersion aPrgVersion,
+ uInt16 aDebugFlags
+)
+{
+ // create new engine
+ TEngineModuleBase *engine = newEngine();
+ engine->fCIisStatic= true;
+
+ // connect callback
+ aCI->callbackVersion= aCallbackVersion;
+ engine->fCI = aCI;
+ TSyError err= engine->Connect( "", aPrgVersion, aDebugFlags );
+ *aEngVersion= Plugin_Version(0);
+ return err;
+} // ConnectEngineS
+
+
+/* Entry point to disconnect engine */
+TSyError SYSYNC_EXTERNAL(DisconnectEngine)( UI_Call_In aCI )
+{
+ TSyError err= LOCERR_OK;
+
+ if (aCI && aCI->thisBase) { // structure must still exist
+ // first get pointer to engine
+ TEngineModuleBase *engine = static_cast<TEngineModuleBase *>(aCI->thisBase);
+ err= engine->Disconnect();
+ aCI->thisBase= NULL; // no longer valid
+
+ // delete the engine (this also deletes the callback!)
+ SYSYNC_TRY {
+ delete engine;
+ }
+ SYSYNC_CATCH (...)
+ err = LOCERR_EXCEPTION;
+ SYSYNC_ENDCATCH
+ // done
+ } // if
+
+ return err;
+} // DisconnectEngine
+
+
+
+#endif // SIMPLE_LINKING
+
+
+} // namespace sysync
+
+/* end of TEngineInterface implementation */
+
+#endif // ENGINEINTERFACE_SUPPORT
+
+// eof
diff --git a/src/sysync/engineinterface.h b/src/sysync/engineinterface.h
new file mode 100755
index 0000000..693f114
--- /dev/null
+++ b/src/sysync/engineinterface.h
@@ -0,0 +1,791 @@
+/**
+ * @File engineinterface.h
+ *
+ * @Author Lukas Zeller (luz@synthesis.ch)
+ *
+ * @brief TEngineInterface - common interface to SySync engine for SDK
+ *
+ * Copyright (c) 2007-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ */
+
+/*
+ */
+
+#ifndef ENGINEINTERFACE_H
+#define ENGINEINTERFACE_H
+
+#ifdef ENGINEINTERFACE_SUPPORT
+
+// published engine definitions
+#include "engine_defs.h"
+
+// internal sysync engine definitions
+#include "sysync.h"
+#include "syserial.h"
+
+// decide if using with EngineModuleBase class
+#ifdef SIMPLE_LINKING
+ #define EMBVIRTUAL
+#else
+ #include "enginemodulebase.h"
+ #define EMBVIRTUAL virtual
+#endif
+
+// DBAPI tunnel support
+#ifdef DBAPI_TUNNEL_SUPPORT
+ #define TUNNEL_IMPL
+ #define TUNNEL_IMPL_VOID
+#else
+ #define TUNNEL_IMPL { return LOCERR_NOTIMP; }
+ #define TUNNEL_IMPL_VOID {}
+#endif
+
+
+
+using namespace std;
+
+namespace sysync {
+
+// forward
+class TEngineInterface;
+class TSyncAppBase;
+
+
+
+#ifdef SIMPLE_LINKING
+ // for using the engineInterface without EngineModuleBase, engine base class
+ // for the newEngine() function is TEngineInterface
+ #define ENGINE_IF_CLASS TEngineInterface
+ // factory function declaration is here, as we have no EngineModuleBase
+ ENGINE_IF_CLASS *newEngine(void);
+#else
+ // with EngineModuleBase, use it as base class. newEngine is declared in
+ // enginemodulebase.h
+ #define ENGINE_IF_CLASS TEngineModuleBase
+#endif
+
+
+// settings key implementation base class
+class TSettingsKeyImpl
+{
+public:
+ TSettingsKeyImpl(TEngineInterface *aEngineInterfaceP);
+ virtual ~TSettingsKeyImpl();
+
+ // open subkey(chain) by relative(!) path
+ // - walks down through needed subkeys, always starting at myself
+ TSyError OpenKeyByPath(
+ TSettingsKeyImpl *&aSettingsKeyP,
+ cAppCharP aPath, uInt16 aMode,
+ bool aImplicit
+ );
+
+ // open Settings subkey key by ID or iterating over all subkeys
+ virtual TSyError OpenSubkey(
+ TSettingsKeyImpl *&aSettingsKeyP,
+ sInt32 aID, uInt16 aMode
+ ) {
+ // by default, opening by key is not allowed (only if derived class implements it)
+ return DB_NotAllowed;
+ };
+
+ // delete Settings subkey key by ID
+ virtual TSyError DeleteSubkey(sInt32 aID)
+ {
+ // by default, keys cannot be deleted
+ return DB_NotAllowed;
+ };
+
+ // return ID of current key
+ virtual TSyError GetKeyID(sInt32 &aID) {
+ // by default, key has no ID access to subkeys
+ return DB_NotAllowed;
+ };
+
+ // get value's ID (e.g. internal index)
+ virtual sInt32 GetValueID(cAppCharP aName) {
+ return KEYVAL_ID_UNKNOWN; // unknown
+ };
+
+ // Set text format parameters
+ TSyError SetTextMode(uInt16 aCharSet, uInt16 aLineEndMode, bool aBigEndian);
+
+ // Set text format parameters
+ TSyError SetTimeMode(uInt16 aTimeMode) {
+ fTimeMode = aTimeMode;
+ return LOCERR_OK;
+ };
+
+ // Reads a named value in specified format into passed memory buffer
+ TSyError GetValueByID(
+ sInt32 aID, sInt32 aArrayIndex, uInt16 aValType,
+ appPointer aBuffer, memSize aBufSize, memSize &aValSize
+ );
+
+ // Writes a named value in specified format passed in memory buffer
+ TSyError SetValueByID(
+ sInt32 aID, sInt32 aArrayIndex, uInt16 aValType,
+ cAppPointer aBuffer, memSize aValSize
+ );
+
+ // get engine interface
+ TEngineInterface *getEngineInterface(void) { return fEngineInterfaceP; };
+
+protected:
+ // open subkey by name (not by path!)
+ // - this is the actual implementation
+ virtual TSyError OpenSubKeyByName(
+ TSettingsKeyImpl *&aSettingsKeyP,
+ cAppCharP aName, stringSize aNameSize,
+ uInt16 aMode
+ ) {
+ // by default, no key can be found (derived class must implement it)
+ return DB_NotFound;
+ };
+
+ // get value's native type
+ // Note: this must be safe if aID is out of range and must return VALTYPE_UNKNOWN if so.
+ virtual uInt16 GetValueType(sInt32 aID) {
+ return VALTYPE_UNKNOWN; // unknown
+ };
+
+ // get value
+ virtual TSyError GetValueInternal(
+ sInt32 aID, sInt32 aArrayIndex,
+ appPointer aBuffer, memSize aBufSize, memSize &aValSize
+ ) {
+ // By default, there are no values
+ return DB_NotAllowed;
+ };
+
+ // set value
+ virtual TSyError SetValueInternal(
+ sInt32 aID, sInt32 aArrayIndex,
+ cAppPointer aBuffer, memSize aValSize
+ ) {
+ // By default, there are no values
+ return DB_NotAllowed;
+ };
+
+ // the engine interface
+ TEngineInterface *fEngineInterfaceP;
+ // Value format modes
+ TCharSets fCharSet;
+ bool fBigEndian;
+ TLineEndModes fLineEndMode;
+ uInt16 fTimeMode;
+
+private:
+ // link to implicitly opened parent (when opening via OpenKeyByPath)
+ TSettingsKeyImpl *fImplicitParentKeyP;
+
+}; // TSettingsKeyImpl
+
+
+// Table for TReadOnlyInfoKey
+typedef struct {
+ cAppCharP valName; // null for terminator
+ uInt16 valType;
+ appPointer valPtr;
+ memSize valSiz; // size of var or 0 for autosize for null terminated strings
+} TReadOnlyInfo;
+
+
+// key delivering constant infos
+class TReadOnlyInfoKey :
+ public TSettingsKeyImpl
+{
+ typedef TSettingsKeyImpl inherited;
+public:
+ TReadOnlyInfoKey(TEngineInterface *aEngineInterfaceP) :
+ inherited(aEngineInterfaceP) {};
+
+ // get value's ID (e.g. internal index)
+ virtual sInt32 GetValueID(cAppCharP aName);
+
+protected:
+
+ // get value's native type
+ virtual uInt16 GetValueType(sInt32 aID);
+
+ // get value
+ virtual TSyError GetValueInternal(
+ sInt32 aID, sInt32 aArrayIndex,
+ appPointer aBuffer, memSize aBufSize, memSize &aValSize
+ );
+
+ virtual const TReadOnlyInfo *getInfoTable(void) = 0;
+ virtual sInt32 numInfos(void) = 0;
+
+private:
+ // iterator
+ uInt16 fIterator;
+}; // TReadOnlyInfoKey
+
+
+
+// key for accessing global config vars
+class TConfigVarKey :
+ public TSettingsKeyImpl
+{
+ typedef TSettingsKeyImpl inherited;
+public:
+ TConfigVarKey(TEngineInterface *aEngineInterfaceP) :
+ inherited(aEngineInterfaceP) {};
+
+ // get value's ID (config vars do NOT have an ID!)
+ virtual sInt32 GetValueID(cAppCharP aName) {
+ fVarName = aName; // cache name
+ return KEYVAL_NO_ID;
+ };
+
+protected:
+
+ // get value's native type (all config vars are text)
+ virtual uInt16 GetValueType(sInt32 aID);
+
+ // get value
+ virtual TSyError GetValueInternal(
+ sInt32 aID, sInt32 aArrayIndex,
+ appPointer aBuffer, memSize aBufSize, memSize &aValSize
+ );
+
+ // set value
+ virtual TSyError SetValueInternal(
+ sInt32 aID, sInt32 aArrayIndex,
+ cAppPointer aBuffer, memSize aValSize
+ );
+
+private:
+ // cached name of last variable name used (to get around IDs)
+ string fVarName;
+
+}; // TConfigVarKey
+
+
+
+// forward
+class TStructFieldsKey;
+typedef struct TStructFieldInfo TStructFieldInfo;
+
+
+// function prototype to get values
+typedef TSyError (*TGetValueProc)(
+ TStructFieldsKey *aStructFieldsKeyP, const TStructFieldInfo *aFldInfoP,
+ appPointer aBuffer, memSize aBufSize, memSize &aValSize
+);
+
+// function prototype to set values
+typedef TSyError (*TSetValueProc)(
+ TStructFieldsKey *aStructFieldsKeyP, const TStructFieldInfo *aFldInfoP,
+ cAppPointer aBuffer, memSize aValSize
+);
+
+
+// Table for TStructFieldsKey
+typedef struct TStructFieldInfo {
+ cAppCharP valName; // null for terminator
+ uInt16 valType;
+ bool writable; // set if field can be written
+ size_t fieldOffs; // offset of field from beginning of structure
+ size_t valSiz; // size of field in bytes, if 0, only get/setValueProc access is possible (and if these are not defined, access is not allowed)
+ TGetValueProc getValueProc; // if not NULL, this routine is called instead of reading data directly
+ TSetValueProc setValueProc; // if not NULL, this routine is called instead of writing data directly
+} TStructFieldInfo;
+
+
+// key for access to a struct containing settings fields
+class TStructFieldsKey :
+ public TSettingsKeyImpl
+{
+ typedef TSettingsKeyImpl inherited;
+public:
+ TStructFieldsKey(TEngineInterface *aEngineInterfaceP) :
+ inherited(aEngineInterfaceP),
+ fDirty(false),
+ fIterator(0)
+ {};
+
+ // get value's ID (e.g. internal index)
+ virtual sInt32 GetValueID(cAppCharP aName);
+
+ // Static helpers
+ // - static helper for procedural string readers
+ static TSyError returnString(cAppCharP aReturnString, appPointer aBuffer, memSize aBufSize, memSize &aValSize);
+ static TSyError returnInt(sInt32 aInt, memSize aIntSize, appPointer aBuffer, memSize aBufSize, memSize &aValSize);
+
+protected:
+
+ // get value's native type
+ virtual uInt16 GetValueType(sInt32 aID);
+
+ // get value
+ virtual TSyError GetValueInternal(
+ sInt32 aID, sInt32 aArrayIndex,
+ appPointer aBuffer, memSize aBufSize, memSize &aValSize
+ );
+
+ // set value
+ virtual TSyError SetValueInternal(
+ sInt32 aID, sInt32 aArrayIndex,
+ cAppPointer aBuffer, memSize aValSize
+ );
+
+ // get table describing the fields in the struct
+ virtual const TStructFieldInfo *getFieldsTable(void) { return NULL; };
+ virtual sInt32 numFields(void) { return 0; };
+
+ // get actual struct base address
+ virtual uInt8P getStructAddr(void) { return NULL; };
+
+ // flag that will be set on first write access
+ bool fDirty;
+
+private:
+ // iterator
+ uInt16 fIterator;
+
+}; // TStructFieldsKey
+
+
+
+// Engine info
+
+static const uInt16 variantCode = SYSER_VARIANT_CODE;
+
+static const TReadOnlyInfo EngineInfoTable[] = {
+ // name, type, ptr, siz
+ { "version", VALTYPE_TEXT, (appPointer)SYSYNC_FULL_VERSION_STRING, 0 },
+ { "platform", VALTYPE_TEXT, (appPointer)SYSYNC_PLATFORM_NAME, 0 },
+ { "name", VALTYPE_TEXT, (appPointer)CUST_SYNC_MODEL, 0 },
+ { "manufacturer", VALTYPE_TEXT, (appPointer)CUST_SYNC_MAN, 0 },
+ { "comment", VALTYPE_TEXT, (appPointer)VERSION_COMMENTS, 0 },
+ // other build infos
+ { "variantcode", VALTYPE_INT16, (appPointer)&variantCode, 2 },
+ // Note: productcode and extraid are already available as configvars
+};
+
+class TEngineInfoKey :
+ public TReadOnlyInfoKey
+{
+ typedef TReadOnlyInfoKey inherited;
+public:
+ TEngineInfoKey(TEngineInterface *aEngineInterfaceP) :
+ inherited(aEngineInterfaceP) {};
+
+protected:
+ virtual const TReadOnlyInfo *getInfoTable(void) { return EngineInfoTable; };
+ virtual sInt32 numInfos(void) { return sizeof(EngineInfoTable)/sizeof(TReadOnlyInfo); };
+
+}; // TEngineInfoKey
+
+
+// Licensing
+
+#ifdef SYSER_REGISTRATION
+
+class TLicensingKey :
+ public TStructFieldsKey
+{
+ typedef TStructFieldsKey inherited;
+public:
+ TLicensingKey(TEngineInterface *aEngineInterfaceP) :
+ inherited(aEngineInterfaceP) {};
+
+protected:
+ // get table describing the fields in the struct
+ virtual const TStructFieldInfo *getFieldsTable(void);
+ virtual sInt32 numFields(void);
+ // get actual struct base address
+ virtual uInt8P getStructAddr(void);
+
+}; // TLicensingKey
+
+#endif
+
+
+// Generic root key, providing settings keys common to all
+// SySync applications
+class TSettingsRootKey :
+ public TSettingsKeyImpl
+{
+ typedef TSettingsKeyImpl inherited;
+
+public:
+ TSettingsRootKey(TEngineInterface *aEngineInterfaceP) :
+ inherited(aEngineInterfaceP) {};
+
+protected:
+ // open subkey by name (not by path!)
+ // - this is the actual implementation
+ virtual TSyError OpenSubKeyByName(
+ TSettingsKeyImpl *&aSettingsKeyP,
+ cAppCharP aName, stringSize aNameSize,
+ uInt16 aMode
+ ) {
+ if (strucmp(aName,"engineinfo",aNameSize)==0)
+ aSettingsKeyP = new TEngineInfoKey(fEngineInterfaceP);
+ else if (strucmp(aName,"configvars",aNameSize)==0)
+ aSettingsKeyP = new TConfigVarKey(fEngineInterfaceP);
+ #ifdef SYSER_REGISTRATION
+ else if (strucmp(aName,"licensing",aNameSize)==0)
+ aSettingsKeyP = new TLicensingKey(fEngineInterfaceP);
+ #endif
+ else
+ return inherited::OpenSubKeyByName(aSettingsKeyP,aName,aNameSize,aMode);
+ // opened a key
+ return LOCERR_OK;
+ };
+}; // TSettingsRootKey
+
+
+
+// Common Engine Interface
+class TEngineInterface
+#ifndef SIMPLE_LINKING
+ : public TEngineModuleBase
+#endif
+{
+ #ifndef SIMPLE_LINKING
+ typedef TEngineModuleBase inherited;
+ #endif
+ friend class TSettingsKeyImpl;
+
+ public:
+ TEngineInterface(); // constructor
+ virtual ~TEngineInterface(); // destructor
+
+ /// @brief Object init, usually called at connect time
+ /// @note needed to allow virtual call to newSyncAppBase() factory function
+ EMBVIRTUAL TSyError Init(void);
+ EMBVIRTUAL TSyError Term(void);
+
+ // Engine init
+ // -----------
+
+ /// @brief Set the global mode for string paramaters (when never called, default params are UTF-8 with C-style line ends)
+ /// @param aCharSet[in] charset
+ /// @param aLineEndMode[in] line end mode (default is C-lineends of the platform (almost always LF))
+ /// @param aBigEndian[in] determines endianness of UTF16 text (defaults to little endian = intel order)
+ /// @return LOCERR_OK on success, SyncML or LOCERR_xxx error code on failure
+ EMBVIRTUAL TSyError SetStringMode(uInt16 aCharSet, uInt16 aLineEndMode=LEM_CSTR, bool aBigEndian=false);
+
+ /// @brief init object, optionally passing XML config text in memory
+ /// @param aConfigXML[in] NULL or empty string if no external config needed, config text otherwise
+ /// @return LOCERR_OK on success, SyncML or LOCERR_xxx error code on failure
+ EMBVIRTUAL TSyError InitEngineXML(cAppCharP aConfigXML);
+
+ /// @brief init object, optionally passing a open FILE for reading config
+ /// @param aConfigFilePath[in] path to config file
+ /// @return LOCERR_OK on success, SyncML or LOCERR_xxx error code on failure
+ EMBVIRTUAL TSyError InitEngineFile(cAppCharP aConfigFilePath);
+
+ /// @brief init object, optionally passing a callback for reading config
+ /// @param aReaderFunc[in] callback function which can deliver next chunk of XML config data
+ /// @param aContext[in] free context pointer passed back with callback
+ /// @return LOCERR_OK on success, SyncML or LOCERR_xxx error code on failure
+ EMBVIRTUAL TSyError InitEngineCB(TXMLConfigReadFunc aReaderFunc, void *aContext);
+
+
+ // Running a Sync Session
+ // ----------------------
+
+ /// @brief Open a session
+ /// @param aNewSessionH[out] receives session handle for all session execution calls
+ /// @param aSelector[in] selector, depending on session type. For multi-profile clients: profile ID to use
+ /// @param aSessionName[in] a text name/id to identify a session, useage depending on session type.
+ /// @return LOCERR_OK on success, SyncML or LOCERR_xxx error code on failure
+ EMBVIRTUAL TSyError OpenSession(SessionH &aNewSessionH, uInt32 aSelector=0, cAppCharP aSessionName=NULL);
+protected:
+ // internal implementation, sessionname already in application format
+ virtual TSyError OpenSessionInternal(SessionH &aNewSessionH, uInt32 aSelector, cAppCharP aSessionName);
+public:
+
+ /// @brief open session specific runtime parameter/settings key
+ /// @note key handle obtained with this call must be closed BEFORE SESSION IS CLOSED!
+ /// @param aNewKeyH[out] receives the opened key's handle on success
+ /// @param aSessionH[in] session handle obtained with OpenSession.
+ /// When used as callback from DBApi, this parameter is irrelevant and
+ /// must be set to NULL as a callback from DBApi has an implicit session context
+ /// automatically.
+ /// @param aMode[in] the open mode
+ /// @return LOCERR_OK on success, SyncML or LOCERR_xxx error code on failure
+ virtual TSyError OpenSessionKey(SessionH aSessionH, KeyH &aNewKeyH, uInt16 aMode);
+
+ /// @brief Close a session
+ /// @note It depends on session type if this also destroys the session or if it may persist and can be re-opened.
+ /// @param aSessionH[in] session handle obtained with OpenSession
+ /// @return LOCERR_OK on success, SyncML or LOCERR_xxx error code on failure
+ virtual TSyError CloseSession(SessionH aSessionH);
+
+ /// @brief Executes sync session or other sync related activity step by step
+ /// @param aSessionH[in] session handle obtained with OpenSession
+ /// @param aStepCmd[in/out] step command (STEPCMD_xxx):
+ /// - tells caller to send or receive data or end the session etc.
+ /// - instructs engine to suspend or abort the session etc.
+ /// @param aInfoP[in] pointer to a TEngineProgressInfo structure, NULL if no progress info needed
+ /// @return LOCERR_OK on success, SyncML or LOCERR_xxx error code on failure
+ virtual TSyError SessionStep(SessionH aSessionH, uInt16 &aStepCmd, TEngineProgressInfo *aInfoP = NULL);
+
+ /// @brief Get access to SyncML message buffer
+ /// @param aSessionH[in] session handle obtained with OpenSession
+ /// @param aBuffer[out] receives pointer to buffer (empty for receive, full for send)
+ /// @param aBufSize[out] receives size of empty or full buffer
+ /// @return LOCERR_OK on success, SyncML or LOCERR_xxx error code on failure
+ EMBVIRTUAL TSyError GetSyncMLBuffer(SessionH aSessionH, bool aForSend, appPointer &aBuffer, memSize &aBufSize);
+
+ /// @brief Return SyncML message buffer to engine
+ /// @param aSessionH[in] session handle obtained with OpenSession
+ /// @param aProcessed[in] number of bytes put into or read from the buffer
+ /// @return LOCERR_OK on success, SyncML or LOCERR_xxx error code on failure
+ EMBVIRTUAL TSyError RetSyncMLBuffer(SessionH aSessionH, bool aForSend, memSize aProcessed);
+
+ /// @brief Read data from SyncML message buffer
+ /// @param aSessionH[in] session handle obtained with OpenSession
+ /// @param aBuffer[in] pointer to buffer
+ /// @param aBufSize[in] size of buffer, maximum to be read
+ /// @param aMsgSize[out] size of data available in the buffer for read INCLUDING just returned data.
+ /// @note If the aBufSize is too small to return all available data LOCERR_TRUNCATED will be returned, and the
+ /// caller can repeat calls to ReadSyncMLBuffer to get the next chunk.
+ /// @return LOCERR_OK on success, SyncML or LOCERR_xxx error code on failure
+ EMBVIRTUAL TSyError ReadSyncMLBuffer (SessionH aSessionH, appPointer aBuffer, memSize aBufSize, memSize &aMsgSize);
+
+ /// @brief Write data to SyncML message buffer
+ /// @param aSessionH[in] session handle obtained with OpenSession
+ /// @param aBuffer[in] pointer to buffer
+ /// @param aMsgSize[in] size of message to write to the buffer
+ /// @return LOCERR_OK on success, SyncML or LOCERR_xxx error code on failure
+ EMBVIRTUAL TSyError WriteSyncMLBuffer(SessionH aSessionH, appPointer aBuffer, memSize aMsgSize);
+
+
+ // Settings access
+ // ---------------
+
+ /// @brief open Settings key by path specification
+ /// @param aNewKeyH[out] receives the opened key's handle on success
+ /// @param aParentKeyH[in] NULL if path is absolute from root, handle to an open key for relative access
+ /// @param aPath[in] the path specification as null terminated string
+ /// @param aMode[in] the open mode
+ /// @return LOCERR_OK on success, SyncML or LOCERR_xxx error code on failure
+ EMBVIRTUAL TSyError OpenKeyByPath (
+ KeyH &aNewKeyH,
+ KeyH aParentKeyH,
+ cAppCharP aPath, uInt16 aMode
+ );
+
+
+ /// @brief open Settings subkey key by ID or iterating over all subkeys
+ /// @param aNewKeyH[out] receives the opened key's handle on success
+ /// @param aParentKeyH[in] handle to the parent key (NULL = root key)
+ /// @param aID[in] the ID of the subkey to open,
+ /// or KEYVAL_ID_FIRST/KEYVAL_ID_NEXT to iterate over existing subkeys, returns DB_NoContent when no more found
+ /// or KEYVAL_ID_NEW(_xxx) to create a new subkey
+ /// @param aMode[in] the open mode
+ /// @return LOCERR_OK on success, DB_NoContent when no more subkeys are found with
+ /// KEYVAL_ID_FIRST/KEYVAL_ID_NEXT
+ /// or any other SyncML or LOCERR_xxx error code on failure
+ EMBVIRTUAL TSyError OpenSubkey(
+ KeyH &aNewKeyH,
+ KeyH aParentKeyH,
+ sInt32 aID, uInt16 aMode
+ );
+
+
+ /// @brief delete Settings subkey key by ID
+ /// @param aParentKeyH[in] handle to the parent key
+ /// @param aID[in] the ID of the subkey to delete
+ /// @return LOCERR_OK on success
+ /// or any other SyncML or LOCERR_xxx error code on failure
+ EMBVIRTUAL TSyError DeleteSubkey(KeyH aParentKeyH, sInt32 aID);
+
+ /// @brief Get key ID of currently open key. Note that the Key ID is only locally unique within
+ /// the parent key.
+ /// @param aKeyH[in] an open key handle
+ /// @param aID[out] receives the ID of the open key, which can be used to re-access the
+ /// key within its parent using OpenSubkey()
+ /// @return LOCERR_OK on success, SyncML or LOCERR_xxx error code on failure
+ EMBVIRTUAL TSyError GetKeyID(KeyH aKeyH, sInt32 &aID);
+
+
+ /// @brief Set text format parameters (when never called, default params are those set with global SetStringMode())
+ /// @param aKeyH[in] an open key handle
+ /// @param aCharSet[in] charset
+ /// @param aLineEndMode[in] line end mode (defaults to C-lineends of the platform (almost always LF))
+ /// @param aBigEndian[in] determines endianness of UTF16 text (defaults to little endian = intel order)
+ /// @return LOCERR_OK on success, SyncML or LOCERR_xxx error code on failure
+ EMBVIRTUAL TSyError SetTextMode(KeyH aKeyH, uInt16 aCharSet, uInt16 aLineEndMode=LEM_CSTR, bool aBigEndian=false);
+
+ /// @brief Set time format parameters
+ /// @param aKeyH[in] an open key handle
+ /// @param aTimeMode[in] time mode, see TMODE_xxx (default is platform's lineratime_t when SetTimeMode() is not used)
+ /// @return LOCERR_OK on success, SyncML or LOCERR_xxx error code on failure
+ EMBVIRTUAL TSyError SetTimeMode(KeyH aKeyH, uInt16 aTimeMode);
+
+
+ /// @brief Closes a key opened by OpenKeyByPath() or OpenSubKey()
+ /// @param aKeyH[in] an open key handle. Will be invalid when call returns with LOCERR_OK. Do not re-use!
+ /// @return LOCERR_OK on success, SyncML or LOCERR_xxx error code on failure
+ EMBVIRTUAL TSyError CloseKey(KeyH aKeyH);
+
+ /// @brief Reads a named value in specified format into passed memory buffer
+ /// @param aKeyH[in] an open key handle
+ /// @param aValueName[in] name of the value to read. Some keys offer special ".XXX"
+ /// suffixes (VALSUFF_XXX) to value names, which return alternate values (like a timestamp field's
+ /// time zone name with ".TZNAME").
+ /// @param aValType[in] desired return type, see VALTYPE_xxxx
+ /// @param aBuffer[in/out] buffer where to store the data
+ /// @param aBufSize[in] size of buffer in bytes (ALWAYS in bytes, even if value is Unicode string)
+ /// Note: to get only size of a value (useful especially for strings), pass 0 as buffer size.
+ /// @param aValSize[out] actual size of value.
+ /// For VALTYPE_TEXT, size is string length (IN BYTES) excluding NULL terminator
+ /// Note that this will be set also when return value is LOCERR_BUFTOOSMALL,
+ /// to indicate the required buffer size
+ /// For values that can be truncated (strings), LOCERR_TRUNCATED will be returned
+ /// when returned value is not entire value - aValSize is truncated size then.
+ /// @return LOCERR_OK on success, SyncML or LOCERR_xxx error code on failure
+ EMBVIRTUAL TSyError GetValue(
+ KeyH aKeyH, cAppCharP aValueName,
+ uInt16 aValType,
+ appPointer aBuffer, memSize aBufSize, memSize &aValSize
+ );
+
+ /// @brief Writes a named value in specified format passed in memory buffer
+ /// @param aKeyH[in] an open key handle
+ /// @param aValueName[in] name of the value to write. Some keys offer special ".XXX"
+ /// suffixes (VALSUFF_XXX) to value names, which are used to set alternate values (like a
+ /// timestamp field's time zone name with ".TZNAME").
+ /// @param aValType[in] type of value passed in, see VALTYPE_xxxx
+ /// @param aBuffer[in] buffer containing the data
+ /// @param aValSize[in] size of value. For VALTYPE_TEXT, size can be passed as -1 if string is null terminated
+ /// @return LOCERR_OK on success, SyncML or LOCERR_xxx error code on failure. If value
+ /// buffer passed in is too small for aValType (such as only 2 bytes for a VALTYPE_INT32,
+ /// LOCERR_BUFTOOSMALL will be returned and nothing stored. If buffer passed in is too
+ /// long (e.g. for strings) to be entirely stored, only the beginning is stored and
+ /// LOCERR_TRUNCATED is returned.
+ EMBVIRTUAL TSyError SetValue(
+ KeyH aKeyH, cAppCharP aValueName,
+ uInt16 aValType,
+ cAppPointer aBuffer, memSize aValSize
+ );
+
+ /// @brief get value's ID for use with Get/SetValueByID()
+ /// @param aKeyH[in] an open key handle
+ /// @param aName[in] name of the value to write. Some keys offer special ".FLAG.XXX"
+ /// values, which return flag bits which can be added to the regular ID to obtain
+ /// alternate values (like the value name with ".FLAG.VALNAME", useful when iterating
+ /// over values).
+ /// @return KEYVAL_ID_UNKNOWN when no ID available for name, ID of value otherwise
+ EMBVIRTUAL sInt32 GetValueID(KeyH aKeyH, cAppCharP aName);
+
+
+ /// @brief Reads a named value in specified format into passed memory buffer
+ /// @param aKeyH[in] an open key handle
+ /// @param aID[in] ID of the value to read (evenually plus flag mask obtained by GetValueID(".FLAG.XXX") call)
+ /// @param aArrayIndex[in] 0-based array element index for array values.
+ /// @param aValType[in] desired return type, see VALTYPE_xxxx
+ /// @param aBuffer[in/out] buffer where to store the data
+ /// @param aBufSize[in] size of buffer in bytes (ALWAYS in bytes, even if value is Unicode string)
+ /// Note: to get only size of a value (useful especially for strings), pass 0 as buffer size.
+ /// @param aValSize[out] actual size of value.
+ /// For VALTYPE_TEXT, size is string length (IN BYTES) excluding NULL terminator
+ /// Note that this will be set also when return value is LOCERR_BUFTOOSMALL,
+ /// to indicate the required buffer size
+ /// For values that can be truncated (strings), LOCERR_TRUNCATED will be returned
+ /// when returned value is not entire value - aValSize is truncated size then.
+ /// @return LOCERR_OK on success, LOCERR_OUTOFRANGE when array index is out of range
+ /// SyncML or LOCERR_xxx error code on other failure
+ EMBVIRTUAL TSyError GetValueByID(
+ KeyH aKeyH, sInt32 aID, sInt32 aArrayIndex,
+ uInt16 aValType,
+ appPointer aBuffer, memSize aBufSize, memSize &aValSize
+ );
+
+ /// @brief Writes a named value in specified format passed in memory buffer
+ /// @param aKeyH[in] an open key handle
+ /// @param aID[in] ID of the value to write (evenually plus flag mask obtained by GetValueID(".FLAG.XXX") call)
+ /// @param aArrayIndex[in] 0-based array element index for array values.
+ /// @param aValType[in] type of value passed in, see VALTYPE_xxxx
+ /// @param aBuffer[in] buffer containing the data
+ /// @param aValSize[in] size of value. For VALTYPE_TEXT, size can be passed as -1 if string is null terminated
+ /// @return LOCERR_OK on success, SyncML or LOCERR_xxx error code on failure. If value
+ /// buffer passed in is too small for aValType (such as only 2 bytes for a VALTYPE_INT32,
+ /// LOCERR_BUFTOOSMALL will be returned and nothing stored. If buffer passed in is too
+ /// long (e.g. for strings) to be entirely stored, only the beginning is stored and
+ /// LOCERR_TRUNCATED is returned.
+ EMBVIRTUAL TSyError SetValueByID(
+ KeyH aKeyH, sInt32 aID, sInt32 aArrayIndex,
+ uInt16 aValType,
+ cAppPointer aBuffer, memSize aValSize
+ );
+
+ /// @name DB-Api-like access to datastore adaptor (eg. when using tunnel plugin)
+ /// @note These all return LOCERR_NOTIMP when DBAPI_TUNNEL_SUPPORT is not defined
+ ///
+ /// @{
+ EMBVIRTUAL TSyError StartDataRead ( CContext ac, cAppCharP lastToken, cAppCharP resumeToken ) TUNNEL_IMPL;
+ EMBVIRTUAL TSyError ReadNextItem ( CContext ac, ItemID aID, appCharP *aItemData, sInt32 *aStatus, bool aFirst ) TUNNEL_IMPL;
+ EMBVIRTUAL TSyError ReadItem ( CContext ac, cItemID aID, appCharP *aItemData ) TUNNEL_IMPL;
+ EMBVIRTUAL TSyError EndDataRead ( CContext ac ) TUNNEL_IMPL;
+ EMBVIRTUAL TSyError StartDataWrite ( CContext ac ) TUNNEL_IMPL;
+ EMBVIRTUAL TSyError InsertItem ( CContext ac, cAppCharP aItemData, cItemID aID ) TUNNEL_IMPL;
+ EMBVIRTUAL TSyError UpdateItem ( CContext ac, cAppCharP aItemData, cItemID aID, ItemID updID ) TUNNEL_IMPL;
+ EMBVIRTUAL TSyError MoveItem ( CContext ac, cItemID aID, cAppCharP newParID ) TUNNEL_IMPL;
+ EMBVIRTUAL TSyError DeleteItem ( CContext ac, cItemID aID ) TUNNEL_IMPL;
+ EMBVIRTUAL TSyError EndDataWrite ( CContext ac, bool success, appCharP *newToken ) TUNNEL_IMPL;
+ EMBVIRTUAL void DisposeObj ( CContext ac, void* memory ) TUNNEL_IMPL_VOID;
+
+ // ---- asKey ----
+ EMBVIRTUAL TSyError ReadNextItemAsKey( CContext ac, ItemID aID, KeyH aItemKey, sInt32 *aStatus, bool aFirst ) TUNNEL_IMPL;
+ EMBVIRTUAL TSyError ReadItemAsKey ( CContext ac, cItemID aID, KeyH aItemKey ) TUNNEL_IMPL;
+ EMBVIRTUAL TSyError InsertItemAsKey ( CContext ac, KeyH aItemKey, cItemID aID ) TUNNEL_IMPL;
+ EMBVIRTUAL TSyError UpdateItemAsKey ( CContext ac, KeyH aItemKey, cItemID aID, ItemID updID ) TUNNEL_IMPL;
+ /// @}
+
+ /// @brief returns the current application base object
+ TSyncAppBase *getSyncAppBase(void) { return fAppBaseP; };
+
+ #ifdef DIRECT_APPBASE_GLOBALACCESS
+ // only as a hack until we are completely free of global anchors
+ void setSyncAppBase(TSyncAppBase *aAppBaseP) { fAppBaseP=aAppBaseP; };
+ #endif
+
+ protected:
+ /// @brief Must be derived in engineBase derivates for generating root for the appropriate settings tree
+ /// @return root settings object, or NULL if failure
+ virtual TSettingsKeyImpl *newSettingsRootKey(void) {
+ return new TSettingsRootKey(this); // return base class which can return some engine infos
+ };
+
+ /// @brief returns a new application base.
+ /// @note in engineInterface based targets, this is the replacement for the formerly
+ /// global newSyncAppBase() factory function.
+ virtual TSyncAppBase *newSyncAppBase(void) = 0;
+
+ /// @brief returns the SML instance for a given session handle
+ virtual InstanceID_t getSmlInstanceOfSession(SessionH aSessionH) { return 0; /* no instance in base class */ };
+
+ private:
+
+ // conversion helper
+ // - converts byte stream (which can be an 8-bit char set or unicode in intel or motorola order)
+ // to internal UTF-8, according to text mode set with SetStringMode()
+ cAppCharP makeAppString(cAppCharP aTextP, string &aString);
+
+ // the application base
+ TSyncAppBase *fAppBaseP;
+
+ // String format modes
+ TCharSets fCharSet;
+ bool fBigEndian;
+ TLineEndModes fLineEndMode;
+
+}; // TEngineInterface
+
+
+} // namespace sysync
+
+#endif // ENGINEINTERFACE_SUPPORT
+
+#endif // ENGINEINTERFACE_H
+// eof
diff --git a/src/sysync/gb2312_tables_inc.cpp b/src/sysync/gb2312_tables_inc.cpp
new file mode 100755
index 0000000..66aa457
--- /dev/null
+++ b/src/sysync/gb2312_tables_inc.cpp
@@ -0,0 +1,2777 @@
+const size_t ucs2_to_gb2312_numelems = 28470;
+
+const treeval_t ucs2_to_gb2312_elements[ucs2_to_gb2312_numelems] = {
+ 0x284A,0x00E0,0x0000,0x0000,0x0000,0x00D1,0x0000,0x0000,0x000F,0x0000,0x0000,0x0000,0x0000,0x0005,0x0002,0xA3A4,
+ 0x0001,0xA3FE,0x0003,0x0001,0xA1EA,0x0000,0xA1E9,0x0071,0x0031,0x0011,0x0001,0x0008,0x0004,0x0002,0xA1AB,0xA3FD,
+ 0x0002,0xA3FC,0xA3FB,0x0004,0x0002,0xA3FA,0xA3F9,0x0002,0xA3F8,0xA3F7,0x0010,0x0008,0x0004,0x0002,0xA3F6,0xA3F5,
+ 0x0002,0xA3F4,0xA3F3,0x0004,0x0002,0xA3F2,0xA3F1,0x0002,0xA3F0,0xA3EF,0x0008,0x0004,0x0002,0xA3EE,0xA3ED,0x0002,
+ 0xA3EC,0xA3EB,0x0004,0x0002,0xA3EA,0xA3E9,0x0002,0xA3E8,0xA3E7,0x0020,0x0010,0x0008,0x0004,0x0002,0xA3E6,0xA3E5,
+ 0x0002,0xA3E4,0xA3E3,0x0004,0x0002,0xA3E2,0xA3E1,0x0002,0xA3E0,0xA3DF,0x0008,0x0004,0x0002,0xA3DE,0xA3DD,0x0002,
+ 0xA3DC,0xA3DB,0x0004,0x0002,0xA3DA,0xA3D9,0x0002,0xA3D8,0xA3D7,0x0010,0x0008,0x0004,0x0002,0xA3D6,0xA3D5,0x0002,
+ 0xA3D4,0xA3D3,0x0004,0x0002,0xA3D2,0xA3D1,0x0002,0xA3D0,0xA3CF,0x0008,0x0004,0x0002,0xA3CE,0xA3CD,0x0002,0xA3CC,
+ 0xA3CB,0x0004,0x0002,0xA3CA,0xA3C9,0x0002,0xA3C8,0xA3C7,0x0040,0x0020,0x0010,0x0008,0x0004,0x0002,0xA3C6,0xA3C5,
+ 0x0002,0xA3C4,0xA3C3,0x0004,0x0002,0xA3C2,0xA3C1,0x0002,0xA3C0,0xA3BF,0x0008,0x0004,0x0002,0xA3BE,0xA3BD,0x0002,
+ 0xA3BC,0xA3BB,0x0004,0x0002,0xA1C3,0xA3B9,0x0002,0xA3B8,0xA3B7,0x0010,0x0008,0x0004,0x0002,0xA3B6,0xA3B5,0x0002,
+ 0xA3B4,0xA3B3,0x0004,0x0002,0xA3B2,0xA3B1,0x0002,0xA3B0,0xA3AF,0x0008,0x0004,0x0002,0xA3AE,0xA1AA,0x0002,0xA3AC,
+ 0xA3AB,0x0004,0x0002,0xA3AA,0xA3A9,0x0002,0xA3A8,0xA3A7,0x0000,0x0000,0x0008,0x0004,0x0002,0xA3A6,0xA3A5,0x0002,
+ 0xA1E7,0xA3A3,0x0000,0x0002,0xA3A2,0xA3A1,0x0000,0x0001,0x0001,0x0001,0x0000,0x0001,0x0001,0x0001,0x0001,0x0000,
+ 0xD8A3,0x0001,0x1292,0x081E,0x03BD,0x01D8,0x0090,0x0001,0x0043,0x001D,0x0009,0x0001,0x0001,0x0003,0x0000,0xBACD,
+ 0x0002,0xD9DF,0xB9EA,0x000A,0x0005,0x0001,0x0002,0xB9EA,0xEDE8,0x0000,0x0002,0xB9A8,0xC1FA,0x0006,0x0003,0x0001,
+ 0xEDE8,0x0000,0xB9A8,0x0001,0x0000,0xC5D3,0x001C,0x000F,0x0007,0x0003,0x0001,0xC1FA,0x0002,0xF6BB,0xC8A3,0x0004,
+ 0x0002,0xF6BA,0xF6B9,0x0002,0xF6B8,0xF6B7,0x0008,0x0004,0x0002,0xF6B6,0xF6B5,0x0002,0xC1E4,0xF6B4,0x0001,0x0002,
+ 0xF6B3,0xB3DD,0x0005,0x0001,0x0001,0x0001,0xF6BB,0x0001,0x0000,0x0000,0xC8A3,0x002A,0x0017,0x000A,0x0004,0x0001,
+ 0x0000,0xF6B9,0x0003,0x0000,0xF6BA,0x0001,0xC4F6,0x0006,0x0003,0x0000,0xF6B8,0x0001,0xB3F6,0x0003,0x0001,0xC1E4,
+ 0x0002,0xF6B6,0xF6B4,0x0008,0x0004,0x0001,0x0000,0xF6B7,0x0000,0x0001,0xF6B5,0x0004,0x0001,0x0000,0xF6B3,0x0004,
+ 0x0002,0xB3DD,0xECB4,0x0000,0xC6EB,0x000D,0x0008,0x0004,0x0001,0x0001,0xD5AB,0x0000,0x0000,0xC6EB,0x0000,0x0001,
+ 0x0000,0xF7FE,0x000D,0x0007,0x0004,0x0002,0xF7FD,0xF7FC,0x0001,0xB1C7,0x0003,0x0001,0xF7FA,0x0001,0xF7FB,0x0004,
+ 0x0001,0x0000,0xF7FA,0x0001,0x0001,0xF7F9,0x009A,0x0046,0x001D,0x000C,0x0005,0x0000,0x0001,0x0001,0xF7F8,0x0001,
+ 0x0003,0x0001,0xF7F7,0x0001,0xCAF3,0x0005,0x0001,0x0001,0x0000,0xDCB1,0x0006,0x0003,0x0000,0xD8BB,0x0000,0xB6AC,
+ 0x0003,0x0000,0xB9C4,0x0001,0xD8BE,0x0011,0x000C,0x0006,0x0003,0x0001,0xB6A6,0x0000,0xF6BE,0x0003,0x0000,0xF6BD,
+ 0x0000,0xF6BE,0x0000,0x0000,0x0000,0xF7A1,0x000E,0x0008,0x0004,0x0002,0xF6BD,0xF6BC,0x0002,0xF6BC,0xEDEB,0x0003,
+ 0x0000,0xEDEA,0x0000,0xEDE9,0x0006,0x0003,0x0000,0xF7F2,0x0001,0xC3B9,0x0000,0x0001,0xF7F5,0x002D,0x0017,0x000B,
+ 0x0004,0x0000,0x0000,0xF7F6,0x0003,0x0001,0xF7F5,0x0002,0xF7F2,0xB5B3,0x0006,0x0003,0x0000,0xF7F3,0x0000,0xF7F4,
+ 0x0003,0x0001,0xF7F1,0x0001,0xF7EF,0x000E,0x0008,0x0004,0x0002,0xF7F0,0xB5E3,0x0002,0xF7EE,0xF7ED,0x0003,0x0000,
+ 0xF7EC,0x0001,0xC4AC,0x0004,0x0001,0x0001,0xC7AD,0x0001,0x0000,0xBADA,0x0014,0x000C,0x0008,0x0004,0x0002,0xF0A4,
+ 0xC0E8,0x0002,0xCAF2,0xD9E4,0x0001,0x0000,0xD9E4,0x0004,0x0001,0x0001,0xBBC6,0x0000,0x0000,0xBBC6,0x000D,0x0007,
+ 0x0003,0x0001,0xF7E2,0x0002,0xF7E1,0xC3B4,0x0003,0x0000,0xC2E9,0x0001,0xF4EF,0x0000,0x0001,0x0002,0xC3E6,0xF4F0,
+ 0x0045,0x001D,0x000D,0x0005,0x0001,0x0001,0x0000,0xF4EF,0x0000,0x0003,0x0001,0xC2F3,0x0002,0xC2F3,0xB4D6,0x0007,
+ 0x0000,0x0003,0x0000,0xF7EB,0x0000,0xF7EA,0x0004,0x0000,0x0000,0xC0F6,0x0000,0x0002,0xC2B4,0xF7E8,0x000F,0x0007,
+ 0x0001,0x0003,0x0000,0xF7E7,0x0001,0xF7E6,0x0004,0x0000,0x0000,0xF7E5,0x0000,0x0001,0xF7E4,0x000E,0x0008,0x0004,
+ 0x0002,0xC2B9,0xF5BA,0x0002,0xD1CE,0xBCEF,0x0003,0x0001,0xF5BA,0x0000,0xCFCC,0x0004,0x0001,0x0000,0xC2B1,0x0003,
+ 0x0000,0xF0D9,0x0002,0xF0D7,0xD3A5,0x002F,0x0018,0x000D,0x0005,0x0001,0x0002,0xF0D8,0xF0D6,0x0004,0x0002,0xF0D5,
+ 0xF0D4,0x0002,0xF0D3,0xF0D2,0x0007,0x0004,0x0002,0xF0D1,0xF0D0,0x0001,0xBAD7,0x0000,0x0000,0xF0CF,0x000D,0x0006,
+ 0x0003,0x0001,0xF0CE,0x0001,0xF0CD,0x0004,0x0002,0xF0CC,0xF0CB,0x0001,0xF7BD,0x0006,0x0003,0x0000,0xF0CA,0x0000,
+ 0xF0C9,0x0001,0x0000,0xF0C8,0x001E,0x000F,0x0007,0x0004,0x0002,0xC5F4,0xF0C7,0x0001,0xF0C6,0x0004,0x0002,0xF0C5,
+ 0xC8B5,0x0002,0xF0C4,0xF0C3,0x0008,0x0004,0x0002,0xF0C2,0xF0C1,0x0002,0xB6EC,0xF0C0,0x0004,0x0002,0xBEE9,0xF0BF,
+ 0x0000,0xF0BE,0x000E,0x0007,0x0004,0x0002,0xBAE8,0xF0BD,0x0000,0xB8EB,0x0003,0x0001,0xF0BC,0x0002,0xF0BB,0xF0B9,
+ 0x0007,0x0004,0x0002,0xF0BA,0xF0B8,0x0000,0xCDD2,0x0004,0x0002,0xD4A7,0xF0B6,0x0000,0xF0B7,0x00B8,0x0069,0x003F,
+ 0x002E,0x001B,0x000D,0x0005,0x0002,0xD1EC,0x0001,0xD1BC,0x0004,0x0002,0xF0B5,0xF0B4,0x0002,0xF0B3,0xF0B2,0x0007,
+ 0x0003,0x0000,0xF0B1,0x0002,0xD1BB,0xC5B8,0x0003,0x0001,0xC3F9,0x0002,0xF0B0,0xBCA6,0x000E,0x0008,0x0004,0x0002,
+ 0xF0AF,0xC4F1,0x0002,0xF0BD,0xF0BF,0x0003,0x0001,0xF0D9,0x0000,0xF0D0,0x0000,0x0001,0x0001,0xF0B5,0x0001,0x0006,
+ 0x0001,0x0001,0x0002,0xF0D8,0xD3A5,0x0004,0x0000,0x0000,0xF0D6,0x0003,0x0000,0xF0C2,0x0000,0xF0D5,0x0019,0x000B,
+ 0x0005,0x0000,0x0000,0x0001,0xF0D3,0x0000,0x0001,0x0002,0xF0D4,0xF0B8,0x0006,0x0001,0x0001,0x0002,0xF0D2,0xF0BA,
+ 0x0004,0x0000,0x0001,0xC5B8,0x0000,0x0001,0xF0D1,0x0006,0x0001,0x0001,0x0001,0x0000,0xF0CE,0x0006,0x0001,0x0000,
+ 0x0002,0xF0CF,0xF7BD,0x0001,0x0000,0x0000,0xBAD7,0x001F,0x0013,0x0009,0x0000,0x0004,0x0000,0x0001,0xDDBA,0x0001,
+ 0x0001,0xF0CD,0x0005,0x0001,0x0001,0x0000,0xF0CA,0x0000,0x0000,0x0000,0xF0C9,0x0006,0x0000,0x0001,0x0001,0x0001,
+ 0xF0C8,0x0001,0x0001,0x0001,0x0000,0xC8B5,0x0021,0x000F,0x000A,0x0004,0x0000,0x0000,0xB5F1,0x0003,0x0000,0xC5F4,
+ 0x0000,0xF0C6,0x0001,0x0001,0x0001,0xF0C4,0x000A,0x0006,0x0003,0x0000,0xF0C0,0x0001,0xB6EC,0x0000,0x0000,0xF0C3,
+ 0x0001,0x0003,0x0001,0xF0BE,0x0002,0xF0C1,0xBEE9,0x0006,0x0001,0x0001,0x0001,0x0000,0xF0BC,0x0000,0x0004,0x0000,
+ 0x0001,0xB8EB,0x0000,0x0001,0xBAE8,0x0094,0x0035,0x001F,0x0010,0x0006,0x0000,0x0000,0x0002,0xF0BB,0xF0B9,0x0006,
+ 0x0003,0x0000,0xD1BC,0x0000,0xD1EC,0x0000,0x0001,0xF0B3,0x000A,0x0006,0x0003,0x0001,0xF0B7,0x0001,0xF0B6,0x0000,
+ 0x0001,0xD4A7,0x0000,0x0001,0x0001,0xCDD2,0x000C,0x0005,0x0001,0x0001,0x0001,0xD1BB,0x0000,0x0003,0x0001,0xF0B1,
+ 0x0000,0xF0B2,0x0001,0x0004,0x0001,0x0000,0xF0B0,0x0000,0x0002,0xC3F9,0xB7EF,0x0029,0x000F,0x0005,0x0001,0x0001,
+ 0x0001,0xF0AF,0x0006,0x0003,0x0001,0xD9EC,0x0001,0xC4F1,0x0001,0x0000,0xF7AF,0x000D,0x0007,0x0003,0x0001,0xF7AE,
+ 0x0002,0xC1DB,0xF7AD,0x0003,0x0000,0xF7AC,0x0001,0xF7AB,0x0008,0x0004,0x0002,0xF7AA,0xF7A9,0x0002,0xB1EE,0xF7A8,
+ 0x0000,0x0002,0xF7A7,0xF7A6,0x001B,0x000F,0x0008,0x0004,0x0002,0xF7A5,0xF7A4,0x0002,0xF7A3,0xF7A2,0x0004,0x0002,
+ 0xF7A1,0xF6FE,0x0000,0xF6FD,0x0007,0x0003,0x0001,0xF6FC,0x0002,0xF6FB,0xF6FA,0x0000,0x0002,0xF6F9,0xC8FA,0x000B,
+ 0x0004,0x0001,0x0001,0xF6F8,0x0004,0x0002,0xF6F7,0xF6F6,0x0000,0xF6F5,0x0008,0x0004,0x0002,0xBEA8,0xF6F4,0x0002,
+ 0xF6F3,0xF6F2,0x0004,0x0002,0xF6F1,0xF6F0,0x0002,0xF6EF,0xF6EE,0x005E,0x0036,0x001D,0x000D,0x0007,0x0003,0x0000,
+ 0xF6ED,0x0002,0xF6EC,0xF6EB,0x0003,0x0001,0xF6EA,0x0001,0xF6E9,0x0008,0x0004,0x0002,0xF6E8,0xF6E7,0x0002,0xF6E6,
+ 0xF6E5,0x0004,0x0002,0xC0F0,0xF6E4,0x0002,0xF6E3,0xF6E2,0x000E,0x0007,0x0004,0x0002,0xF6E1,0xF6E0,0x0000,0xF6DF,
+ 0x0004,0x0002,0xCFCA,0xF6DE,0x0000,0xF6DD,0x0004,0x0001,0x0001,0xF6DC,0x0003,0x0000,0xF6DB,0x0002,0xF6DA,0xF6D9,
+ 0x0018,0x000B,0x0007,0x0003,0x0000,0xF6D8,0x0002,0xF6D7,0xB1AB,0x0000,0x0001,0xF6D6,0x0008,0x0004,0x0002,0xF6D4,
+ 0xF6D3,0x0002,0xF6D2,0xF6D1,0x0001,0x0002,0xF6D0,0xC2B3,0x000A,0x0004,0x0000,0x0001,0xF6CF,0x0003,0x0000,0xD3E3,
+ 0x0000,0xF6E2,0x0000,0x0000,0x0002,0xF6D4,0xF6F9,0x001C,0x000A,0x0005,0x0000,0x0001,0x0001,0xF6DD,0x0000,0x0000,
+ 0x0001,0xF7AF,0x0005,0x0000,0x0000,0x0001,0xF6D7,0x0007,0x0004,0x0002,0xF6E0,0xC1DB,0x0000,0xF7AC,0x0003,0x0000,
+ 0xF7AD,0x0000,0xF7AE,0x000A,0x0005,0x0001,0x0001,0x0001,0xB1EE,0x0000,0x0000,0x0000,0xF7A8,0x000A,0x0004,0x0001,
+ 0x0000,0xF7A7,0x0003,0x0001,0xF7A9,0x0001,0xF6E4,0x0004,0x0000,0x0001,0xF6E6,0x0003,0x0001,0xF7A6,0x0002,0xF7A1,
+ 0xF6E3,0x020F,0x00C3,0x005E,0x0028,0x0017,0x0010,0x0005,0x0001,0x0000,0x0000,0xF7A2,0x0005,0x0000,0x0002,0xF7A5,
+ 0xF7A3,0x0003,0x0000,0xF7A4,0x0000,0xF6E5,0x0001,0x0001,0x0001,0x0002,0xC8FA,0xF6FB,0x000B,0x0005,0x0001,0x0000,
+ 0x0000,0xF6FA,0x0000,0x0000,0x0002,0xF6FC,0xF6F8,0x0000,0x0001,0x0000,0x0000,0xF6EA,0x0023,0x0015,0x0009,0x0004,
+ 0x0000,0x0001,0xF6F3,0x0001,0x0002,0xF6ED,0xF6EC,0x0006,0x0003,0x0001,0xBEA8,0x0000,0xF6F0,0x0003,0x0001,0xF6EF,
+ 0x0001,0xF6F2,0x0005,0x0001,0x0001,0x0000,0xF6F4,0x0005,0x0001,0x0002,0xF6DF,0xF6EB,0x0000,0x0001,0xF6F6,0x000C,
+ 0x0005,0x0001,0x0001,0x0001,0xF6E8,0x0000,0x0003,0x0000,0xC0F0,0x0000,0xF6E9,0x0000,0x0000,0x0000,0x0002,0xF6E1,
+ 0xF6E7,0x0029,0x0019,0x000C,0x0000,0x0004,0x0001,0x0001,0xCFCA,0x0003,0x0000,0xF6D9,0x0002,0xF6DE,0xF6DB,0x0008,
+ 0x0004,0x0001,0x0001,0xF6DC,0x0001,0x0001,0xF6DA,0x0001,0x0001,0x0001,0xF6D6,0x0007,0x0000,0x0000,0x0000,0x0002,
+ 0xB1AB,0xF6D8,0x0001,0x0004,0x0001,0x0000,0xF6CF,0x0000,0x0001,0xF6D0,0x0013,0x0006,0x0000,0x0000,0x0001,0x0000,
+ 0xC2B3,0x0005,0x0001,0x0001,0x0001,0xD3E3,0x0004,0x0000,0x0001,0xF7CA,0x0000,0x0001,0xC4A7,0x001A,0x000B,0x0007,
+ 0x0003,0x0000,0xF7CE,0x0002,0xCEBA,0xF7CB,0x0000,0x0000,0xF7CD,0x0007,0x0004,0x0002,0xF7CB,0xF7CC,0x0000,0xF7CA,
+ 0x0004,0x0002,0xF7C8,0xC6C7,0x0002,0xF7C9,0xBBEA,0x000A,0x0004,0x0000,0x0000,0xBFFD,0x0003,0x0001,0xB9ED,0x0000,
+ 0xE5F7,0x0001,0x0001,0x0001,0xD8AA,0x008D,0x0041,0x0025,0x0016,0x0009,0x0005,0x0002,0xD3F4,0x0001,0xDBCB,0x0000,
+ 0x0000,0xE3CE,0x0007,0x0003,0x0001,0xE3D2,0x0002,0xBAE5,0xC4D6,0x0003,0x0001,0xB6B7,0x0001,0xF7E0,0x0007,0x0000,
+ 0x0003,0x0000,0xF7DE,0x0001,0xF7DF,0x0004,0x0000,0x0000,0xD0EB,0x0001,0x0001,0xF7DE,0x0012,0x0008,0x0004,0x0001,
+ 0x0001,0xF7DD,0x0000,0x0001,0xBAFA,0x0004,0x0001,0x0000,0xF7DC,0x0003,0x0000,0xCBC9,0x0001,0xD7D7,0x0005,0x0001,
+ 0x0001,0x0001,0xF7D9,0x0000,0x0000,0x0001,0xF7DB,0x0025,0x0012,0x000B,0x0004,0x0001,0x0001,0xF7D7,0x0004,0x0002,
+ 0xB7A2,0xF7DA,0x0001,0xF7D8,0x0001,0x0003,0x0000,0xF7D6,0x0001,0xB7C2,0x0007,0x0000,0x0003,0x0001,0xF7D5,0x0001,
+ 0xF7D4,0x0004,0x0001,0x0000,0xB8DF,0x0004,0x0002,0xF7C5,0xF7C6,0x0002,0xCCE5,0xCBE8,0x0011,0x000C,0x0007,0x0004,
+ 0x0002,0xD4E0,0xF7C7,0x0001,0xF7C3,0x0001,0x0002,0xF7C6,0xF7C5,0x0001,0x0000,0x0001,0xF7C3,0x000B,0x0007,0x0004,
+ 0x0002,0xF7C4,0xF7C1,0x0000,0xF7C2,0x0001,0x0000,0xF7C0,0x0007,0x0003,0x0000,0xF7BF,0x0002,0xBAA1,0xF7BC,0x0000,
+ 0x0000,0xF7BE,0x006C,0x0031,0x0014,0x0008,0x0000,0x0003,0x0001,0xF7BA,0x0002,0xF7BB,0xB0B9,0x0005,0x0001,0x0002,
+ 0xB9C7,0xE6F8,0x0003,0x0001,0xE6F7,0x0002,0xD6E8,0xE6F6,0x0010,0x0008,0x0004,0x0002,0xE6F5,0xC2E2,0x0002,0xE6F4,
+ 0xE6F3,0x0004,0x0002,0xE5B9,0xE6F2,0x0002,0xE6F1,0xE6F0,0x0007,0x0003,0x0000,0xC9A7,0x0002,0xE6EF,0xC6AD,0x0003,
+ 0x0000,0xE6EE,0x0001,0xE6ED,0x001B,0x000D,0x0008,0x0004,0x0002,0xE6EC,0xC6EF,0x0002,0xE6EB,0xBFA5,0x0001,0x0002,
+ 0xD1E9,0xB3D2,0x0007,0x0003,0x0000,0xE6EA,0x0002,0xE6E9,0xBAA7,0x0004,0x0002,0xC2E6,0xE6E8,0x0000,0xBDBE,0x0010,
+ 0x0008,0x0004,0x0002,0xC2EE,0xE6E7,0x0002,0xE6E6,0xE6E4,0x0004,0x0002,0xBCDD,0xE6E5,0x0002,0xCDD5,0xD7A4,0x0008,
+ 0x0004,0x0002,0xE6E3,0xBED4,0x0002,0xE6E2,0xE6E1,0x0004,0x0002,0xCABB,0xE6E0,0x0002,0xC2BF,0xB2B5,0x002C,0x0018,
+ 0x000E,0x0007,0x0003,0x0001,0xC7FD,0x0002,0xB3DB,0xD1B1,0x0004,0x0002,0xCDD4,0xD4A6,0x0000,0xC2ED,0x0004,0x0000,
+ 0x0000,0xE6EA,0x0003,0x0001,0xE6F7,0x0000,0xE6F8,0x000A,0x0006,0x0003,0x0000,0xC2BF,0x0001,0xD6E8,0x0001,0x0001,
+ 0xE6E4,0x0006,0x0003,0x0000,0xBEAA,0x0001,0xD1E9,0x0000,0x0001,0xBDBE,0x0013,0x0008,0x0004,0x0001,0x0001,0xE6F6,
+ 0x0000,0x0001,0xE6E7,0x0004,0x0000,0x0000,0xE6E8,0x0003,0x0001,0xC7FD,0x0002,0xE6F5,0xE6F4,0x000B,0x0007,0x0004,
+ 0x0002,0xE6EE,0xE6F1,0x0000,0xDDEB,0x0000,0x0000,0xC2E2,0x0005,0x0001,0x0002,0xE6F3,0xC9A7,0x0000,0x0000,0xE6E3,
+ 0x010E,0x0061,0x002F,0x0019,0x0010,0x000B,0x0005,0x0002,0xB2DD,0x0001,0xCCDA,0x0003,0x0001,0xE6F2,0x0000,0xE6EF,
+ 0x0000,0x0000,0x0000,0xE5B9,0x0001,0x0004,0x0001,0x0000,0xC6AD,0x0000,0x0001,0xE6F0,0x000D,0x0008,0x0001,0x0004,
+ 0x0002,0xE6EB,0xC6EF,0x0000,0xE6EC,0x0001,0x0001,0x0000,0xE6ED,0x0000,0x0004,0x0001,0x0000,0xB3D2,0x0000,0x0000,
+ 0xBFA5,0x001F,0x0009,0x0000,0x0004,0x0001,0x0000,0xC2E6,0x0001,0x0000,0xBAA7,0x000A,0x0004,0x0000,0x0001,0xE6E9,
+ 0x0003,0x0000,0xE6E1,0x0000,0xCDD5,0x0007,0x0003,0x0000,0xCABB,0x0002,0xE6E2,0xE6E6,0x0001,0x0002,0xBCDD,0xE6E0,
+ 0x0009,0x0000,0x0000,0x0003,0x0001,0xBED4,0x0002,0xE6E5,0xD7A4,0x0005,0x0000,0x0001,0x0000,0xB2B5,0x0001,0x0001,
+ 0x0001,0xD1B1,0x0058,0x0023,0x0015,0x000D,0x0006,0x0003,0x0000,0xB3DB,0x0000,0xCDD4,0x0003,0x0001,0xB7EB,0x0002,
+ 0xD4A6,0xC2ED,0x0004,0x0001,0x0001,0xDCB0,0x0001,0x0000,0xF0A5,0x0001,0x0005,0x0001,0x0002,0xCFE3,0xD9E5,0x0004,
+ 0x0002,0xD8B8,0xCAD7,0x0002,0xE2CE,0xE2CD,0x001D,0x000E,0x0008,0x0004,0x0002,0xE2CC,0xC2F8,0x0002,0xE2CB,0xE2CA,
+ 0x0003,0x0000,0xC1F3,0x0000,0xE2C9,0x0007,0x0004,0x0002,0xB2F6,0xE2C8,0x0001,0xC0A1,0x0004,0x0002,0xE2C7,0xB9DD,
+ 0x0002,0xCFDA,0xE2C6,0x000C,0x0005,0x0001,0x0002,0xC4D9,0xE2C5,0x0003,0x0000,0xB6F6,0x0002,0xE2C4,0xB1FD,0x0004,
+ 0x0000,0x0001,0xBDC8,0x0004,0x0002,0xE2C3,0xC8C4,0x0002,0xB6FC,0xE2C2,0x002F,0x001D,0x000F,0x0007,0x0003,0x0001,
+ 0xCBC7,0x0002,0xB1A5,0xCACE,0x0004,0x0002,0xBDA4,0xD2FB,0x0002,0xB7B9,0xE2C1,0x0008,0x0004,0x0002,0xE2C0,0xE2BF,
+ 0x0002,0xE2BE,0xE2BD,0x0003,0x0000,0xE2BC,0x0000,0xBCA2,0x000A,0x0004,0x0000,0x0000,0xE2BB,0x0003,0x0001,0xB2F6,
+ 0x0001,0xF7D0,0x0001,0x0003,0x0000,0xF7CF,0x0002,0xF7D2,0xF7D3,0x0015,0x000A,0x0006,0x0003,0x0001,0xC8C4,0x0000,
+ 0xBCA2,0x0001,0x0001,0xE2CD,0x0007,0x0003,0x0000,0xC0A1,0x0002,0xE2CB,0xE2CA,0x0001,0x0000,0xC2F8,0x000C,0x0004,
+ 0x0000,0x0000,0xE2C9,0x0004,0x0002,0xE2C8,0xC1F3,0x0002,0xC0A1,0xE2BE,0x0001,0x0001,0x0000,0xCEB9,0x0091,0x0047,
+ 0x0020,0x0010,0x0008,0x0004,0x0000,0x0000,0xE2BC,0x0000,0x0001,0xF7D1,0x0004,0x0001,0x0001,0xB9DD,0x0000,0x0000,
+ 0xCEB9,0x0008,0x0004,0x0001,0x0000,0xCFDA,0x0000,0x0001,0xBDA4,0x0000,0x0004,0x0002,0xE2C6,0xEBC8,0x0001,0xD3E0,
+ 0x0017,0x000D,0x0008,0x0004,0x0002,0xB6F6,0xC4D9,0x0002,0xE2C4,0xB2CD,0x0001,0x0002,0xF7D0,0xB6FC,0x0006,0x0003,
+ 0x0001,0xD1F8,0x0000,0xE2C3,0x0001,0x0000,0xB1FD,0x000B,0x0004,0x0000,0x0000,0xBDC8,0x0003,0x0001,0xCACE,0x0002,
+ 0xB1A5,0xCBC7,0x0001,0x0001,0x0001,0xE2C2,0x002B,0x0016,0x000A,0x0004,0x0000,0x0001,0xD2FB,0x0003,0x0000,0xB7B9,
+ 0x0000,0xE2C1,0x0008,0x0004,0x0002,0xE2C0,0xE2BF,0x0002,0xE2BD,0xF7CF,0x0000,0x0000,0xE2B8,0x0009,0x0004,0x0000,
+ 0x0001,0xBCA2,0x0000,0x0002,0xCAB3,0xB7C9,0x0008,0x0004,0x0002,0xB7C9,0xECAE,0x0002,0xECAD,0xC6AE,0x0001,0x0000,
+ 0xECAC,0x0012,0x000B,0x0007,0x0004,0x0002,0xECAB,0xECAA,0x0000,0xECA9,0x0000,0x0001,0xB7E7,0x0001,0x0003,0x0001,
+ 0xECAD,0x0001,0xC6AE,0x0005,0x0001,0x0001,0x0001,0xECAC,0x0004,0x0000,0x0001,0xD1EF,0x0000,0x0001,0xECAB,0x0071,
+ 0x0034,0x0017,0x000B,0x0006,0x0003,0x0000,0xB9CE,0x0000,0xCCA8,0x0000,0x0002,0xECAA,0xECA9,0x0004,0x0001,0x0001,
+ 0xB7E7,0x0004,0x0002,0xC8A7,0xF2AD,0x0002,0xF2AC,0xB2FC,0x000F,0x0007,0x0003,0x0001,0xF2AB,0x0002,0xF2AA,0xB5DF,
+ 0x0004,0x0002,0xF2A9,0xF2A8,0x0002,0xB6EE,0xD1D5,0x0007,0x0004,0x0002,0xF2A7,0xF2A6,0x0001,0xCCE2,0x0004,0x0002,
+ 0xBFC5,0xD3B1,0x0001,0xF2A5,0x001D,0x000E,0x0007,0x0003,0x0000,0xCDC7,0x0002,0xC6B5,0xD2C3,0x0003,0x0000,0xF2A4,
+ 0x0002,0xF2A3,0xF2A2,0x0007,0x0003,0x0001,0xBCD5,0x0002,0xF2A1,0xBEB1,0x0004,0x0002,0xC6C4,0xC1EC,0x0002,0xC2AD,
+ 0xD4A4,0x0010,0x0008,0x0004,0x0002,0xF1FE,0xCBCC,0x0002,0xB0E4,0xF1FD,0x0004,0x0002,0xB6D9,0xB9CB,0x0002,0xCDE7,
+ 0xE7EF,0x0008,0x0004,0x0002,0xD0EB,0xCBB3,0x0002,0xCFEE,0xF1FC,0x0004,0x0002,0xC7EA,0xB6A5,0x0002,0xD2B3,0xC8A7,
+ 0x0025,0x0015,0x000B,0x0007,0x0003,0x0000,0xF2A8,0x0002,0xC2AD,0xF2AD,0x0000,0x0000,0xCFD4,0x0004,0x0000,0x0000,
+ 0xB2FC,0x0003,0x0000,0xB9CB,0x0000,0xF2AB,0x0008,0x0004,0x0000,0x0001,0xF2A9,0x0000,0x0001,0xC0E0,0x0000,0x0003,
+ 0x0000,0xB5DF,0x0002,0xF2AA,0xD4B8,0x0011,0x000C,0x0004,0x0000,0x0000,0xF2A7,0x0004,0x0002,0xD1D5,0xF2A6,0x0002,
+ 0xB6EE,0xCCE2,0x0001,0x0000,0x0001,0xBFC5,0x0001,0x0007,0x0003,0x0000,0xC6B5,0x0002,0xCDC7,0xBEB1,0x0000,0x0000,
+ 0xF2A5,0x05A8,0x0279,0x0117,0x0089,0x004F,0x001F,0x000F,0x0007,0x0001,0x0003,0x0000,0xBCD5,0x0001,0xCDB7,0x0004,
+ 0x0000,0x0001,0xB8A9,0x0001,0x0000,0xF2A4,0x0007,0x0000,0x0003,0x0000,0xD2C3,0x0001,0xF2A1,0x0004,0x0000,0x0000,
+ 0xF2A2,0x0000,0x0002,0xC1EC,0xC6C4,0x001C,0x000E,0x0007,0x0003,0x0001,0xB6D9,0x0002,0xB0E4,0xCDE7,0x0004,0x0002,
+ 0xD4A4,0xF1FE,0x0000,0xF1FD,0x0006,0x0003,0x0000,0xCBCC,0x0000,0xE7EF,0x0004,0x0002,0xD0EB,0xF1FC,0x0002,0xCBB3,
+ 0xCFEE,0x000B,0x0007,0x0003,0x0001,0xC7EA,0x0002,0xB6A5,0xD2B3,0x0000,0x0001,0xCFEC,0x0004,0x0000,0x0001,0xD4CF,
+ 0x0001,0x0002,0xC9D8,0xD4CF,0x0022,0x0016,0x0008,0x0004,0x0000,0x0001,0xD2F4,0x0001,0x0001,0xBEC2,0x0008,0x0004,
+ 0x0002,0xE8BA,0xE8B9,0x0002,0xE8B8,0xBAAB,0x0003,0x0001,0xC8CD,0x0000,0xCEA4,0x0005,0x0001,0x0001,0x0000,0xE8B9,
+ 0x0000,0x0003,0x0000,0xE8BA,0x0001,0xE8B8,0x0010,0x0005,0x0000,0x0000,0x0001,0xBAAB,0x0007,0x0004,0x0002,0xC8CD,
+ 0xCEA4,0x0001,0xF7B5,0x0001,0x0000,0xC7A7,0x0000,0x0000,0x0003,0x0001,0xF7B2,0x0001,0xE7D6,0x0048,0x0021,0x0014,
+ 0x000C,0x0006,0x0003,0x0000,0xF7B9,0x0000,0xF7B8,0x0003,0x0001,0xF7B5,0x0001,0xB1DE,0x0004,0x0000,0x0001,0xF7B6,
+ 0x0001,0x0000,0xC7EF,0x0008,0x0004,0x0000,0x0001,0xF7B7,0x0000,0x0000,0xBECF,0x0001,0x0000,0x0000,0xC7CA,0x0015,
+ 0x000D,0x0007,0x0003,0x0000,0xF7B4,0x0002,0xF7B3,0xF7B2,0x0003,0x0001,0xB9AE,0x0001,0xB0B0,0x0004,0x0000,0x0001,
+ 0xD0AC,0x0001,0x0001,0xF7B1,0x0008,0x0004,0x0001,0x0000,0xBDD5,0x0001,0x0000,0xE7A5,0x0006,0x0003,0x0000,0xF7B0,
+ 0x0000,0xCDE0,0x0001,0x0000,0xB0D0,0x0027,0x0010,0x0006,0x0000,0x0000,0x0002,0xD1A5,0xBDF9,0x0004,0x0001,0x0001,
+ 0xB8EF,0x0003,0x0000,0xD8CC,0x0001,0xD8CC,0x000B,0x0005,0x0001,0x0002,0xC3E6,0xC3D2,0x0003,0x0000,0xBFBF,0x0000,
+ 0xB7C7,0x0008,0x0004,0x0002,0xBEB2,0xB5E5,0x0002,0xF6A6,0xBEB2,0x0001,0x0000,0xBEB8,0x000C,0x0007,0x0000,0x0003,
+ 0x0001,0xF6A6,0x0000,0xC7E0,0x0001,0x0000,0x0000,0xC1E9,0x000B,0x0006,0x0003,0x0000,0xF6B0,0x0000,0xF6A8,0x0001,
+ 0x0002,0xF6B2,0xF6AB,0x0004,0x0001,0x0001,0xC5F9,0x0000,0x0000,0xB0D4,0x00AE,0x004F,0x0022,0x0012,0x0008,0x0004,
+ 0x0001,0x0001,0xC2B6,0x0000,0x0001,0xF6B1,0x0006,0x0003,0x0000,0xF6B0,0x0001,0xF6AF,0x0001,0x0000,0xCEED,0x0008,
+ 0x0004,0x0000,0x0001,0xC1EF,0x0001,0x0001,0xCFBC,0x0004,0x0000,0x0001,0xCBAA,0x0001,0x0001,0xC1D8,0x0017,0x000B,
+ 0x0004,0x0001,0x0000,0xC4DE,0x0003,0x0000,0xD5B4,0x0002,0xF6AD,0xF6AE,0x0004,0x0000,0x0000,0xBBF4,0x0004,0x0002,
+ 0xC3B9,0xF6AC,0x0002,0xD5F0,0xF6AA,0x000B,0x0004,0x0000,0x0001,0xCFF6,0x0004,0x0002,0xF6AB,0xD0E8,0x0001,0xCEED,
+ 0x0004,0x0001,0x0000,0xB5E7,0x0003,0x0000,0xB1A2,0x0002,0xC0D7,0xC1E3,0x002B,0x0014,0x000B,0x0005,0x0001,0x0002,
+ 0xF6A8,0xD4C6,0x0003,0x0001,0xB7D5,0x0000,0xF6A9,0x0004,0x0001,0x0001,0xD1A9,0x0000,0x0002,0xF6A7,0xD3EA,0x000B,
+ 0x0005,0x0001,0x0002,0xC4D1,0xC0EB,0x0003,0x0001,0xF6C5,0x0001,0xBCA6,0x0006,0x0003,0x0001,0xD4D3,0x0000,0xB3FB,
+ 0x0003,0x0000,0xCBAB,0x0001,0xCBE4,0x001A,0x000B,0x0006,0x0003,0x0000,0xB5F1,0x0001,0xF6C3,0x0001,0x0002,0xB3FB,
+ 0xEEA1,0x0007,0x0004,0x0002,0xD3BA,0xB4C6,0x0000,0xF6C1,0x0004,0x0002,0xEFF4,0xDDC8,0x0002,0xB9CD,0xBCAF,0x000C,
+ 0x0005,0x0000,0x0002,0xD1C5,0xD0DB,0x0004,0x0002,0xD1E3,0xC8B8,0x0001,0xC4D1,0x0007,0x0004,0x0002,0xF6C1,0xF6C0,
+ 0x0000,0xD6BB,0x0004,0x0002,0xF6BF,0xC1A5,0x0001,0xC1A5,0x0051,0x0023,0x0015,0x000B,0x0006,0x0003,0x0001,0xC2A4,
+ 0x0000,0xE3C4,0x0000,0x0002,0xD2FE,0xDAF4,0x0004,0x0001,0x0001,0xCFD5,0x0003,0x0001,0xCBE6,0x0000,0xCBED,0x0001,
+ 0x0006,0x0003,0x0001,0xD5CF,0x0000,0xBCCA,0x0004,0x0002,0xCFB6,0xB0AF,0x0000,0xDAF3,0x001A,0x000C,0x0005,0x0000,
+ 0x0002,0xD4C9,0xB8F4,0x0003,0x0001,0xD2FE,0x0002,0xCBE6,0xBDD7,0x0007,0x0003,0x0000,0xDAF2,0x0002,0xCBE5,0xB6D3,
+ 0x0004,0x0002,0xDAED,0xDAF1,0x0001,0xC2A1,0x0008,0x0000,0x0004,0x0002,0xD3E7,0xB5CC,0x0000,0xD2A3,0x0005,0x0000,
+ 0x0002,0xD1F4,0xE4BE,0x0003,0x0001,0xC2BD,0x0002,0xCFDD,0xCCD5,0x0032,0x0019,0x000C,0x0008,0x0004,0x0002,0xC1EA,
+ 0xDAF0,0x0002,0xB3C2,0xDAEF,0x0000,0x0001,0xD2F5,0x0006,0x0003,0x0001,0xDAEE,0x0001,0xC5E3,0x0004,0x0002,0xCFD5,
+ 0xD4C9,0x0000,0xDAED,0x000E,0x0007,0x0003,0x0001,0xB3FD,0x0002,0xD5F3,0xD4BA,0x0003,0x0000,0xB6B8,0x0002,0xDAEC,
+ 0xC9FD,0x0007,0x0004,0x0002,0xC9C2,0xC9C2,0x0000,0xB1DD,0x0000,0x0001,0xDAEA,0x0018,0x0009,0x0005,0x0000,0x0002,
+ 0xC9C2,0xDAEB,0x0000,0x0001,0xCFDE,0x0007,0x0004,0x0002,0xBDB5,0xC4B0,0x0000,0xC2AA,0x0004,0x0002,0xDAEA,0xB3C2,
+ 0x0002,0xC2A4,0xC2BD,0x000D,0x0007,0x0004,0x0002,0xBCCA,0xB8BD,0x0001,0xDAE9,0x0003,0x0001,0xCDD3,0x0000,0xB0A2,
+ 0x0008,0x0004,0x0002,0xDAE7,0xDAE8,0x0002,0xD7E8,0xDBE6,0x0001,0x0001,0xBDD7,0x018F,0x00C9,0x006F,0x0032,0x0019,
+ 0x000D,0x0006,0x0002,0xD5F3,0x0002,0xD2F5,0xD1F4,0x0004,0x0002,0xB7C0,0xDAE5,0x0001,0xD6B7,0x0006,0x0003,0x0000,
+ 0xC8EE,0x0000,0xBFD3,0x0003,0x0000,0xDAE6,0x0000,0xB6F2,0x000B,0x0004,0x0001,0x0000,0xCDD3,0x0004,0x0002,0xDAE3,
+ 0xDAE4,0x0001,0xB6D3,0x0007,0x0004,0x0002,0xD8EC,0xDAE2,0x0000,0xB8B7,0x0004,0x0002,0xE3DB,0xE3DA,0x0001,0xE3D9,
+ 0x001E,0x000F,0x0007,0x0004,0x0002,0xE3D8,0xE3D7,0x0000,0xC0AB,0x0004,0x0002,0xE3D6,0xC0BB,0x0002,0xB2FB,0xE3D5,
+ 0x0008,0x0004,0x0002,0xD1D6,0xE3D4,0x0002,0xE3D3,0xE3D2,0x0004,0x0002,0xE3D1,0xD1CB,0x0000,0xE3D0,0x000F,0x0008,
+ 0x0004,0x0002,0xE3CF,0xD4C4,0x0002,0xE3CE,0xE3CD,0x0004,0x0002,0xBAD2,0xB8F3,0x0000,0xB7A7,0x0008,0x0004,0x0002,
+ 0xE3CC,0xC3F6,0x0002,0xE3CB,0xCEC5,0x0004,0x0002,0xB9EB,0xC4D6,0x0002,0xD5A2,0xC3C6,0x002E,0x001E,0x0010,0x0008,
+ 0x0004,0x0002,0xE3CA,0xE3C9,0x0002,0xBCE4,0xE3C8,0x0004,0x0002,0xCFD0,0xE3C7,0x0002,0xC8F2,0xB4B3,0x0007,0x0004,
+ 0x0002,0xCECA,0xB1D5,0x0001,0xE3C6,0x0004,0x0002,0xC9C1,0xE3C5,0x0000,0xC3C5,0x0009,0x0004,0x0000,0x0001,0xE3CB,
+ 0x0000,0x0002,0xB1D9,0xB2FB,0x0000,0x0003,0x0000,0xE3DB,0x0000,0xB9D8,0x0016,0x000B,0x0007,0x0004,0x0002,0xB4B3,
+ 0xE3DA,0x0000,0xE3D8,0x0001,0x0000,0xE3D9,0x0005,0x0001,0x0002,0xC0BB,0xE3D7,0x0003,0x0000,0xC0AB,0x0000,0xE3C7,
+ 0x000A,0x0006,0x0003,0x0000,0xB0E5,0x0001,0xE3D6,0x0001,0x0001,0xE3D3,0x0008,0x0004,0x0002,0xE3D0,0xE3D4,0x0002,
+ 0xE3D5,0xD1D6,0x0000,0x0001,0xD1CB,0x0050,0x0027,0x0015,0x0008,0x0004,0x0000,0x0000,0xE3D1,0x0000,0x0001,0xD4C4,
+ 0x0007,0x0003,0x0001,0xE3CC,0x0002,0xE3CF,0xE3CD,0x0003,0x0001,0xC3F6,0x0000,0xB9EB,0x000D,0x0007,0x0003,0x0001,
+ 0xB7A7,0x0002,0xE3D8,0xB8F3,0x0003,0x0001,0xBAD2,0x0001,0xC3D8,0x0001,0x0001,0x0000,0xD5A2,0x0017,0x000C,0x0005,
+ 0x0001,0x0002,0xE3C9,0xBCE4,0x0004,0x0002,0xCFD0,0xCFD0,0x0001,0xC8F2,0x0007,0x0003,0x0000,0xE3C8,0x0002,0xE3CA,
+ 0xBFAA,0x0000,0x0001,0xB1D5,0x000D,0x0006,0x0003,0x0000,0xE3C6,0x0001,0xC9C1,0x0003,0x0000,0xE3C5,0x0002,0xC3C5,
+ 0xB3A4,0x0001,0x0001,0x0001,0xB3A4,0x003B,0x001D,0x000E,0x0006,0x0003,0x0000,0xCFE2,0x0001,0xEFF0,0x0004,0x0002,
+ 0xEFEF,0xEFEE,0x0002,0xC1AD,0xEFED,0x0007,0x0003,0x0001,0xC0D8,0x0002,0xEFEC,0xEFEB,0x0004,0x0002,0xEFEA,0xEFE9,
+ 0x0002,0xEFE8,0xEFE7,0x000F,0x0008,0x0004,0x0002,0xEFE6,0xEFE5,0x0002,0xEFE4,0xC1CD,0x0004,0x0002,0xEFE3,0xEFE2,
+ 0x0001,0xEFE0,0x0008,0x0004,0x0002,0xEFDF,0xEFE1,0x0002,0xBEB5,0xEFDE,0x0003,0x0001,0xEFDD,0x0002,0xEFDC,0xEFDB,
+ 0x001D,0x000F,0x0007,0x0003,0x0000,0xEFDA,0x0002,0xEFD9,0xEFD8,0x0004,0x0002,0xEFD7,0xB0F7,0x0002,0xB8E4,0xEFD6,
+ 0x0007,0x0004,0x0002,0xEFD5,0xC4F8,0x0000,0xEFD4,0x0004,0x0002,0xC4F7,0xEFD3,0x0001,0xD5F2,0x000F,0x0007,0x0004,
+ 0x0002,0xEFD2,0xEFD1,0x0000,0xEFD0,0x0004,0x0002,0xEFCE,0xC3BE,0x0002,0xB6C6,0xEFCD,0x0007,0x0003,0x0000,0xEFCC,
+ 0x0002,0xEFCB,0xB6CD,0x0004,0x0002,0xEFF1,0xC7C2,0x0002,0xEFCA,0xEFC9,0x00F4,0x007B,0x003C,0x001E,0x000F,0x0007,
+ 0x0004,0x0002,0xEFC8,0xEFCF,0x0000,0xEFC7,0x0004,0x0002,0xEFC6,0xEFC5,0x0002,0xC3CC,0xBEE2,0x0008,0x0004,0x0002,
+ 0xBCFC,0xB6A7,0x0002,0xEFC4,0xEFC2,0x0004,0x0002,0xEFC1,0xEFC3,0x0000,0xCFC7,0x000F,0x0008,0x0004,0x0002,0xBDF5,
+ 0xD7B6,0x0002,0xB4B8,0xC2E0,0x0004,0x0002,0xEFC0,0xCEFD,0x0001,0xEFBF,0x0007,0x0004,0x0002,0xEFBE,0xEFBD,0x0001,
+ 0xEFBC,0x0004,0x0002,0xC3AA,0xB4ED,0x0002,0xEFBB,0xD5E0,0x0020,0x0010,0x0008,0x0004,0x0002,0xEFBA,0xEFB9,0x0002,
+ 0xEFB8,0xEFB7,0x0004,0x0002,0xEFB6,0xCCE0,0x0002,0xC8F1,0xEFB5,0x0008,0x0004,0x0002,0xEFB4,0xEFB3,0x0002,0xD0BF,
+ 0xB7E6,0x0004,0x0002,0xEFB2,0xEFB1,0x0002,0xD0E2,0xEFB0,0x0010,0x0008,0x0004,0x0002,0xEFAF,0xB9F8,0x0002,0xB3FA,
+ 0xEFAD,0x0004,0x0002,0xEFAE,0xCBF8,0x0002,0xCFFA,0xEFAC,0x0007,0x0004,0x0002,0xC1B4,0xEFAB,0x0000,0xEFAA,0x0004,
+ 0x0002,0xC6CC,0xEFA9,0x0002,0xD6FD,0xEFA8,0x003E,0x0020,0x0010,0x0008,0x0004,0x0002,0xD2F8,0xEFA7,0x0002,0xEFA6,
+ 0xEFA5,0x0004,0x0002,0xB2F9,0xD2BF,0x0002,0xBDC2,0xEFA4,0x0008,0x0004,0x0002,0xEFA3,0xC3FA,0x0002,0xB8F5,0xEFA2,
+ 0x0004,0x0002,0xEEFE,0xEFA1,0x0002,0xEEFD,0xEEFC,0x000F,0x0007,0x0003,0x0001,0xEEFB,0x0002,0xEEFA,0xCFB3,0x0004,
+ 0x0002,0xEEF9,0xD5A1,0x0002,0xEEF8,0xEEF7,0x0008,0x0004,0x0002,0xEEF6,0xC2C1,0x0002,0xCDAD,0xEEF5,0x0003,0x0001,
+ 0xEEF3,0x0002,0xEEF4,0xEEF2,0x001B,0x000C,0x0005,0x0000,0x0002,0xEEF1,0xEEF0,0x0004,0x0002,0xEEEF,0xEEEE,0x0000,
+ 0xEEED,0x0008,0x0004,0x0002,0xEEEC,0xEEEB,0x0002,0xEEEA,0xEEE9,0x0004,0x0002,0xEEE8,0xEEE7,0x0000,0xEEE6,0x0010,
+ 0x0008,0x0004,0x0002,0xC3AD,0xC7A6,0x0002,0xEEE5,0xC1E5,0x0004,0x0002,0xB2AC,0xCCFA,0x0002,0xD3CB,0xEEE4,0x0008,
+ 0x0004,0x0002,0xBCD8,0xEEE3,0x0002,0xEEE2,0xD7EA,0x0004,0x0002,0xEEE1,0xEEE0,0x0002,0xEEDF,0xEEDE,0x0071,0x003F,
+ 0x0020,0x0010,0x0008,0x0004,0x0002,0xEEDD,0xB2A7,0x0002,0xEEDC,0xC7AF,0x0004,0x0002,0xEEDB,0xC7AE,0x0002,0xEEDA,
+ 0xEED9,0x0008,0x0004,0x0002,0xC5A5,0xEED7,0x0002,0xEED8,0xEED5,0x0004,0x0002,0xEED6,0xB9B3,0x0002,0xCED9,0xBEFB,
+ 0x0010,0x0008,0x0004,0x0002,0xC7D5,0xD4BF,0x0002,0xEED4,0xEED3,0x0004,0x0002,0xB8D6,0xB1B5,0x0002,0xC4C6,0xD6D3,
+ 0x0008,0x0004,0x0002,0xB3AE,0xB6DB,0x0002,0xEED2,0xEED1,0x0004,0x0002,0xEED0,0xB8C6,0x0001,0xEECE,0x001E,0x000E,
+ 0x0007,0x0003,0x0001,0xEECF,0x0002,0xEECD,0xB5F6,0x0003,0x0000,0xB7B0,0x0002,0xEECC,0xEECB,0x0008,0x0004,0x0002,
+ 0xC7A5,0xEECA,0x0002,0xEEC9,0xEEC7,0x0004,0x0002,0xEEC8,0xB6A4,0x0002,0xD5EB,0xEEC6,0x0009,0x0005,0x0000,0x0002,
+ 0xEEC5,0xEEC4,0x0001,0x0001,0xD4E4,0x0007,0x0004,0x0002,0xF6C7,0xD7EA,0x0000,0xC2E0,0x0001,0x0001,0xC4F7,0x001B,
+ 0x0011,0x0007,0x0001,0x0003,0x0000,0xCFE2,0x0000,0xD4BF,0x0006,0x0003,0x0001,0xEFE7,0x0001,0xF6CE,0x0000,0x0000,
+ 0xC2AF,0x0000,0x0005,0x0001,0x0002,0xC5D9,0xEFF0,0x0001,0x0000,0xEEE5,0x000E,0x0006,0x0001,0x0000,0x0002,0xBCF8,
+ 0xBCF8,0x0004,0x0001,0x0000,0xEFD9,0x0000,0x0000,0xEFEC,0x0008,0x0004,0x0001,0x0000,0xD6FD,0x0001,0x0001,0xEFEE,
+ 0x0004,0x0000,0x0000,0xF6CD,0x0003,0x0000,0xEEF5,0x0000,0xEEEC,0x021C,0x00F5,0x0078,0x0037,0x001E,0x0013,0x0009,
+ 0x0004,0x0001,0x0000,0xCCFA,0x0000,0x0002,0xC0D8,0xEFED,0x0004,0x0000,0x0001,0xC1AD,0x0003,0x0000,0xEFD4,0x0001,
+ 0xEFD0,0x0005,0x0001,0x0001,0x0001,0xEFE8,0x0001,0x0001,0x0002,0xEFEB,0xD6D3,0x000C,0x0007,0x0001,0x0003,0x0001,
+ 0xEFA9,0x0001,0xC1CD,0x0001,0x0000,0x0000,0xEFA6,0x0005,0x0001,0x0000,0x0000,0xEEF3,0x0004,0x0001,0x0000,0xD0E2,
+ 0x0001,0x0000,0xEFEA,0x0024,0x000C,0x0007,0x0000,0x0003,0x0000,0xEFE4,0x0000,0xEEFC,0x0001,0x0001,0x0001,0xF6C9,
+ 0x000A,0x0004,0x0001,0x0001,0xEFCE,0x0003,0x0001,0xEFDA,0x0000,0xBEB5,0x0008,0x0004,0x0002,0xB2F9,0xEFDE,0x0002,
+ 0xEFDC,0xEFDB,0x0003,0x0001,0xC6DD,0x0001,0xEFCF,0x0014,0x0009,0x0005,0x0000,0x0002,0xEFAC,0xF7E9,0x0001,0x0000,
+ 0xEFE1,0x0005,0x0001,0x0002,0xEFDD,0xEFD2,0x0003,0x0001,0xF6CB,0x0001,0xC1B4,0x0000,0x0004,0x0000,0x0000,0xD0FD,
+ 0x0000,0x0000,0xEFDF,0x0043,0x0026,0x0016,0x000A,0x0004,0x0001,0x0000,0xEFD8,0x0003,0x0000,0xC4F8,0x0001,0xEFD7,
+ 0x0006,0x0003,0x0001,0xD5F2,0x0001,0xB8E4,0x0003,0x0001,0xEFCB,0x0000,0xEFA1,0x0009,0x0005,0x0000,0x0002,0xEEF8,
+ 0xEFD6,0x0000,0x0001,0xCED9,0x0001,0x0003,0x0001,0xB4B8,0x0001,0xEFD3,0x0010,0x0008,0x0000,0x0004,0x0002,0xC7B9,
+ 0xCBF8,0x0001,0xC8DB,0x0004,0x0000,0x0000,0xF6CC,0x0000,0x0001,0xB0F7,0x0005,0x0001,0x0000,0x0001,0xC3BE,0x0004,
+ 0x0000,0x0001,0xD6D3,0x0000,0x0001,0xD5E0,0x001F,0x0012,0x000A,0x0006,0x0003,0x0001,0xEFC8,0x0000,0xBCFC,0x0001,
+ 0x0001,0xEFCC,0x0004,0x0001,0x0001,0xC7C2,0x0000,0x0001,0xF6CA,0x0006,0x0000,0x0001,0x0002,0xEFC6,0xEFCA,0x0001,
+ 0x0003,0x0000,0xB6CD,0x0001,0xD5A1,0x000D,0x0005,0x0000,0x0001,0x0001,0xEFC9,0x0004,0x0001,0x0000,0xB6C6,0x0000,
+ 0x0000,0xB9F8,0x0006,0x0000,0x0000,0x0002,0xEFC7,0xEECD,0x0004,0x0000,0x0001,0xF6C9,0x0001,0x0001,0xEFAA,0x0089,
+ 0x0045,0x0027,0x0012,0x0008,0x0004,0x0001,0x0000,0xB1ED,0x0000,0x0001,0xC3CC,0x0006,0x0003,0x0001,0xB4ED,0x0000,
+ 0xEFC0,0x0000,0x0001,0xCEFD,0x000A,0x0006,0x0003,0x0000,0xC3AA,0x0000,0xBDF5,0x0001,0x0000,0xC7AE,0x0004,0x0000,
+ 0x0000,0xB6A7,0x0003,0x0001,0xEFBC,0x0002,0xEFA3,0xEFC5,0x000F,0x000A,0x0006,0x0003,0x0000,0xB4B8,0x0001,0xEFBF,
+ 0x0001,0x0000,0xEFB9,0x0000,0x0000,0x0000,0xD7B6,0x000A,0x0004,0x0000,0x0000,0xEFC3,0x0003,0x0000,0xC2BC,0x0001,
+ 0xEFBE,0x0001,0x0000,0x0000,0xB8D6,0x0024,0x0013,0x0008,0x0004,0x0000,0x0000,0xBEE2,0x0001,0x0001,0xEFAB,0x0007,
+ 0x0004,0x0002,0xEFAE,0xEFAF,0x0000,0xEEF1,0x0001,0x0000,0xC6CC,0x000A,0x0006,0x0003,0x0000,0xEFB0,0x0000,0xEFB8,
+ 0x0000,0x0000,0xB3FA,0x0000,0x0003,0x0001,0xEFB7,0x0001,0xEFB2,0x000D,0x0005,0x0001,0x0001,0x0000,0xB7E6,0x0004,
+ 0x0000,0x0001,0xEEF2,0x0000,0x0000,0xEEFA,0x000D,0x0007,0x0004,0x0002,0xF6C8,0xB1B5,0x0001,0xD0BF,0x0003,0x0001,
+ 0xEFB6,0x0001,0xC2C1,0x0001,0x0000,0x0002,0xEFB1,0xCCE0,0x0050,0x002B,0x0016,0x000A,0x0004,0x0000,0x0001,0xCFFA,
+ 0x0003,0x0001,0xC8F1,0x0000,0xBAB8,0x0004,0x0001,0x0000,0xF6C7,0x0004,0x0002,0xEEED,0xEFA4,0x0002,0xEEF0,0xEEFB,
+ 0x000B,0x0007,0x0003,0x0000,0xEFA7,0x0002,0xEEF7,0xD2BF,0x0000,0x0001,0xEFA8,0x0004,0x0000,0x0000,0xEEEE,0x0003,
+ 0x0000,0xCFCE,0x0000,0xEFA2,0x0012,0x000D,0x0007,0x0003,0x0000,0xC3FA,0x0002,0xEEF9,0xD2C4,0x0003,0x0001,0xEEFD,
+ 0x0001,0xCFB3,0x0000,0x0001,0x0000,0xF6C6,0x0008,0x0004,0x0001,0x0001,0xCDAD,0x0000,0x0001,0xEFA5,0x0005,0x0000,
+ 0x0002,0xD2F8,0xEEFE,0x0003,0x0001,0xB8F5,0x0000,0xEEEF,0x0024,0x0010,0x0008,0x0004,0x0000,0x0000,0xBDC2,0x0000,
+ 0x0000,0xBCF8,0x0004,0x0001,0x0001,0xEEE3,0x0000,0x0000,0xEEE2,0x0008,0x0004,0x0001,0x0000,0xEEDB,0x0000,0x0000,
+ 0xB9B3,0x0006,0x0003,0x0000,0xD1EB,0x0000,0xEEE1,0x0003,0x0001,0xC7A6,0x0000,0xC3AD,0x0015,0x0009,0x0004,0x0000,
+ 0x0001,0xC7AF,0x0001,0x0002,0xD7A2,0xB2AC,0x0006,0x0003,0x0001,0xB3FA,0x0001,0xEEE9,0x0003,0x0001,0xC5D9,0x0001,
+ 0xEEE7,0x0008,0x0000,0x0003,0x0000,0xEEE8,0x0002,0xD7EA,0xEED2,0x0008,0x0004,0x0002,0xBCD8,0xEEE4,0x0002,0xD3CB,
+ 0xEEDF,0x0001,0x0002,0xEEDA,0xEEEB,0x0148,0x00A0,0x0047,0x0023,0x0013,0x000C,0x0006,0x0002,0xEEE0,0x0002,0xEEDC,
+ 0xCCA8,0x0003,0x0001,0xC1E5,0x0000,0xEEDD,0x0000,0x0003,0x0001,0xEEE6,0x0001,0xEEEA,0x000B,0x0005,0x0001,0x0002,
+ 0xEED6,0xEED1,0x0003,0x0000,0xEED8,0x0000,0xB8C6,0x0000,0x0001,0x0001,0xBEFB,0x0011,0x0006,0x0001,0x0000,0x0002,
+ 0xC5A5,0xB3AE,0x0007,0x0004,0x0002,0xEED3,0xEED4,0x0000,0xCEFD,0x0000,0x0000,0xB6DB,0x000B,0x0006,0x0003,0x0000,
+ 0xC4C6,0x0001,0xC7A6,0x0000,0x0002,0xD4BF,0xEED7,0x0000,0x0004,0x0002,0xEED5,0xEED9,0x0000,0xBDEF,0x0025,0x0012,
+ 0x000A,0x0006,0x0003,0x0000,0xEECF,0x0000,0xEECA,0x0000,0x0000,0xEECE,0x0004,0x0000,0x0000,0xEED1,0x0000,0x0001,
+ 0xBAB8,0x000D,0x0007,0x0003,0x0000,0xB7B0,0x0002,0xEECB,0xBFDB,0x0003,0x0001,0xEECC,0x0000,0xB5F6,0x0001,0x0000,
+ 0x0002,0xD5EB,0xB8AA,0x001E,0x000E,0x0007,0x0004,0x0002,0xEEC7,0xB6A4,0x0000,0xEEC8,0x0004,0x0002,0xEEC9,0xEEC6,
+ 0x0000,0xEEC5,0x0008,0x0004,0x0002,0xBDF0,0xC0E5,0x0002,0xC1BF,0xD2B0,0x0004,0x0002,0xD6D8,0xC0EF,0x0002,0xCACD,
+ 0xCACD,0x000D,0x0007,0x0003,0x0000,0xD3D4,0x0002,0xB2C9,0xB2C9,0x0003,0x0000,0xF5A6,0x0000,0xF5A7,0x0005,0x0000,
+ 0x0002,0xD0C6,0xC4F0,0x0001,0x0001,0xF5B8,0x004A,0x0024,0x0013,0x0006,0x0001,0x0000,0x0002,0xF5B6,0xF5B7,0x0005,
+ 0x0001,0x0002,0xF5B5,0xF5B4,0x0004,0x0002,0xF5B3,0xBDB4,0x0002,0xD2BD,0xF5B2,0x0006,0x0001,0x0001,0x0002,0xF5B1,
+ 0xF5B0,0x0004,0x0001,0x0001,0xD4CD,0x0003,0x0001,0xB3F3,0x0002,0xC8A9,0xC3D1,0x0011,0x0005,0x0001,0x0001,0x0001,
+ 0xD0D1,0x0005,0x0000,0x0002,0xF5AF,0xF5AD,0x0004,0x0002,0xF5AE,0xF5AB,0x0000,0xB4D7,0x000C,0x0006,0x0003,0x0000,
+ 0xD7ED,0x0000,0xB4BC,0x0003,0x0000,0xF5AC,0x0000,0xEBE7,0x0005,0x0001,0x0002,0xC4F0,0xF5A7,0x0000,0x0000,0xF5A6,
+ 0x0036,0x001D,0x000F,0x0008,0x0004,0x0002,0xF5AA,0xCBE1,0x0002,0xBFE1,0xC3B8,0x0004,0x0002,0xBDCD,0xF5A9,0x0001,
+ 0xF5A8,0x0008,0x0004,0x0002,0xBDB4,0xF5A3,0x0002,0xF5A5,0xCDAA,0x0003,0x0001,0xB3EA,0x0001,0xC0D2,0x000C,0x0004,
+ 0x0000,0x0000,0xF5A4,0x0004,0x0002,0xCBD6,0xF4FE,0x0002,0xBAA8,0xF5A1,0x0007,0x0004,0x0002,0xF5A2,0xCDAA,0x0001,
+ 0xCCAA,0x0003,0x0000,0xD4CD,0x0001,0xB7D3,0x0018,0x0009,0x0005,0x0001,0x0002,0xD0EF,0xF0B2,0x0001,0x0001,0xBEC6,
+ 0x0007,0x0003,0x0001,0xF4FB,0x0002,0xF4FD,0xF4FC,0x0004,0x0002,0xC5E4,0xD7C3,0x0002,0xC7F5,0xF4FA,0x000B,0x0007,
+ 0x0004,0x0002,0xD3CF,0xDBAA,0x0001,0xDBBA,0x0001,0x0000,0xDBB9,0x0001,0x0001,0x0001,0xDAF7,0x00A2,0x004D,0x0028,
+ 0x0017,0x000C,0x0006,0x0003,0x0000,0xDBB8,0x0001,0xDBA6,0x0003,0x0001,0xDAFE,0x0001,0xB5A6,0x0007,0x0004,0x0002,
+ 0xDBB6,0xC1DA,0x0000,0xDBB7,0x0000,0x0000,0xD6A3,0x0009,0x0004,0x0001,0x0000,0xB5CB,0x0001,0x0002,0xDBB5,0xDBB3,
+ 0x0004,0x0001,0x0001,0xDBB4,0x0000,0x0000,0xDEA4,0x0011,0x000C,0x0006,0x0003,0x0000,0xB1C9,0x0001,0xD4C7,0x0003,
+ 0x0001,0xDAF9,0x0001,0xD7DE,0x0000,0x0001,0x0001,0xCFA2,0x000C,0x0006,0x0003,0x0000,0xCFE7,0x0001,0xDBA9,0x0003,
+ 0x0001,0xDBB2,0x0001,0xB6F5,0x0004,0x0001,0x0001,0xDBB1,0x0000,0x0000,0xB6BC,0x002D,0x0015,0x000B,0x0004,0x0000,
+ 0x0001,0xB5A6,0x0004,0x0002,0xD3CA,0xB3BB,0x0000,0xC4DF,0x0004,0x0001,0x0000,0xDBB0,0x0003,0x0000,0xB9F9,0x0000,
+ 0xDBAF,0x000B,0x0007,0x0003,0x0001,0xB2BF,0x0002,0xD4C7,0xDBAA,0x0001,0x0001,0xDBAB,0x0006,0x0003,0x0000,0xBFA4,
+ 0x0000,0xDBA3,0x0004,0x0002,0xBAC2,0xDBAC,0x0000,0xDBAE,0x0014,0x0008,0x0004,0x0001,0x0000,0xDBAD,0x0001,0x0000,
+ 0xDBA9,0x0008,0x0004,0x0002,0xD6A3,0xDBA6,0x0002,0xDBA3,0xC0C9,0x0001,0x0001,0xBDBC,0x0009,0x0004,0x0001,0x0000,
+ 0xDBA8,0x0000,0x0002,0xDBA4,0xDBA7,0x0006,0x0003,0x0000,0xD3F4,0x0001,0xDBA5,0x0001,0x0002,0xC1DA,0xDAFE,0x0063,
+ 0x002F,0x001C,0x000E,0x0007,0x0004,0x0002,0xD7DE,0xDBA1,0x0001,0xDAFD,0x0004,0x0002,0xC9DB,0xDAFB,0x0000,0xDAFC,
+ 0x0008,0x0004,0x0002,0xC7F1,0xDBA2,0x0002,0xBAAA,0xD3CA,0x0003,0x0001,0xDAF9,0x0001,0xD0B0,0x0009,0x0004,0x0001,
+ 0x0001,0xB0EE,0x0001,0x0002,0xC4C7,0xD0CF,0x0004,0x0000,0x0000,0xDAFA,0x0003,0x0000,0xDAF7,0x0000,0xDAF6,0x0019,
+ 0x000D,0x0007,0x0004,0x0002,0xDAF8,0xD3DA,0x0000,0xDAF5,0x0003,0x0000,0xE7DF,0x0000,0xB5CB,0x0007,0x0004,0x0002,
+ 0xD2D8,0xE5CE,0x0000,0xC2DF,0x0001,0x0002,0xE5E5,0xB1DF,0x000D,0x0006,0x0003,0x0001,0xE5E3,0x0000,0xE5C7,0x0003,
+ 0x0001,0xBBB9,0x0002,0xE5E4,0xE5E2,0x0007,0x0004,0x0002,0xC2F5,0xD1FB,0x0000,0xB1DC,0x0004,0x0002,0xE5E1,0xC1C9,
+ 0x0001,0xD2C5,0x002D,0x0015,0x000D,0x0006,0x0003,0x0001,0xD1A1,0x0000,0xC7A8,0x0004,0x0002,0xD7F1,0xE5E0,0x0001,
+ 0xB3D9,0x0004,0x0001,0x0001,0xD5DA,0x0000,0x0000,0xD4E2,0x000C,0x0005,0x0000,0x0002,0xCACA,0xE5DB,0x0003,0x0000,
+ 0xD2A3,0x0002,0xC7B2,0xE5DD,0x0006,0x0003,0x0001,0xD4B6,0x0001,0xB5DD,0x0003,0x0001,0xD1B7,0x0000,0xE5DE,0x001E,
+ 0x000F,0x0007,0x0004,0x0002,0xD2A3,0xE5DC,0x0000,0xD2C5,0x0004,0x0002,0xCEA5,0xB4EF,0x0002,0xB5C0,0xE5D9,0x0008,
+ 0x0004,0x0002,0xE5D8,0xE5DA,0x0002,0xB6F4,0xB9FD,0x0003,0x0000,0xB1E9,0x0002,0xD4CB,0xD3CE,0x000C,0x0006,0x0003,
+ 0x0000,0xD5EC,0x0000,0xD3F6,0x0003,0x0001,0xE5D7,0x0001,0xCBEC,0x0006,0x0003,0x0000,0xB6DD,0x0001,0xD3E2,0x0003,
+ 0x0001,0xB1C6,0x0000,0xC2DF,0x0A97,0x0556,0x02A9,0x0185,0x00C3,0x0062,0x002B,0x0015,0x0009,0x0004,0x0001,0x0000,
+ 0xD2DD,0x0000,0x0002,0xE5D4,0xE5D3,0x0007,0x0004,0x0002,0xBDF8,0xD6DC,0x0001,0xE5D6,0x0000,0x0002,0xB4FE,0xE5D5,
+ 0x0007,0x0001,0x0003,0x0000,0xE5CE,0x0001,0xC1AC,0x0008,0x0004,0x0002,0xB7EA,0xE5D2,0x0002,0xD4EC,0xCBD9,0x0004,
+ 0x0002,0xB3D1,0xCAC5,0x0001,0xB9E4,0x001D,0x000E,0x0007,0x0004,0x0002,0xCDA8,0xD5E2,0x0001,0xB6BA,0x0004,0x0002,
+ 0xE5D1,0xE5C9,0x0000,0xCDBE,0x0008,0x0004,0x0002,0xB5DD,0xE5CF,0x0002,0xD6F0,0xCDB8,0x0003,0x0001,0xE5D0,0x0002,
+ 0xD3C9,0xE5CD,0x000D,0x0005,0x0000,0x0002,0xD1B7,0xD1A1,0x0004,0x0002,0xC4E6,0xE5CB,0x0002,0xE5CC,0xCCD3,0x0007,
+ 0x0004,0x0002,0xCACA,0xCBCD,0x0000,0xCDCB,0x0003,0x0001,0xD7B7,0x0001,0xD2C6,0x0034,0x0018,0x000D,0x0008,0x0004,
+ 0x0002,0xC4CB,0xBCA3,0x0002,0xB1C5,0xC3D4,0x0001,0x0002,0xBBD8,0xE5C9,0x0004,0x0001,0x0000,0xCAF6,0x0004,0x0002,
+ 0xE5C5,0xB5FC,0x0001,0xC6C8,0x000E,0x0007,0x0004,0x0002,0xB5CF,0xE5C7,0x0000,0xE5CA,0x0004,0x0002,0xE5C8,0xE5C4,
+ 0x0000,0xE5C6,0x0006,0x0003,0x0000,0xCCF6,0x0001,0xB3D9,0x0004,0x0002,0xC1AC,0xCEA5,0x0002,0xD4B6,0xBDF8,0x0017,
+ 0x000D,0x0006,0x0003,0x0001,0xD5E2,0x0000,0xBBB9,0x0003,0x0001,0xE5C3,0x0002,0xB7B5,0xE5C2,0x0006,0x0003,0x0001,
+ 0xBDFC,0x0000,0xD4CB,0x0000,0x0000,0xD3AD,0x000C,0x0005,0x0001,0x0002,0xC2F5,0xB9FD,0x0004,0x0002,0xE5C6,0xD1B8,
+ 0x0000,0xC6F9,0x0005,0x0000,0x0002,0xD3D8,0xC7A8,0x0000,0x0002,0xB4EF,0xC1C9,0x006B,0x002E,0x0017,0x0008,0x0004,
+ 0x0000,0x0001,0xB1DF,0x0000,0x0000,0xE5C1,0x0008,0x0004,0x0002,0xC5A9,0xC8E8,0x0002,0xB3BD,0xB1E7,0x0004,0x0002,
+ 0xB1E8,0xB4C7,0x0001,0xB1E8,0x000C,0x0006,0x0003,0x0001,0xB1E7,0x0000,0xB1E6,0x0003,0x0000,0xB0EC,0x0001,0xC0B1,
+ 0x0004,0x0001,0x0001,0xB1D9,0x0003,0x0000,0xB4C7,0x0002,0xB9BC,0xD0C1,0x001E,0x0010,0x0008,0x0004,0x0002,0xEAA5,
+ 0xD5DE,0x0002,0xEAA4,0xD5B7,0x0004,0x0002,0xCFBD,0xD4AF,0x0002,0xE0CE,0xCAE4,0x0007,0x0003,0x0001,0xBCAD,0x0002,
+ 0xB7F8,0xEAA3,0x0004,0x0002,0xEAA2,0xEAA1,0x0001,0xE9FE,0x0010,0x0008,0x0004,0x0002,0xB9F5,0xBBD4,0x0002,0xB1B2,
+ 0xE9FD,0x0004,0x0002,0xC1BE,0xB8A8,0x0002,0xE9FC,0xBDCF,0x0007,0x0004,0x0002,0xE9FB,0xE9FA,0x0001,0xBDCE,0x0004,
+ 0x0002,0xE9F9,0xD4D8,0x0002,0xE9F8,0xC7E1,0x0035,0x0020,0x0010,0x0008,0x0004,0x0002,0xE9F7,0xE9F6,0x0002,0xE9F4,
+ 0xE9F5,0x0004,0x0002,0xE9F3,0xE9F2,0x0002,0xD6E1,0xE9F1,0x0008,0x0004,0x0002,0xE9F0,0xE9EF,0x0002,0xBAE4,0xC8ED,
+ 0x0004,0x0002,0xC2D6,0xE9EE,0x0002,0xD7AA,0xE9ED,0x000D,0x0007,0x0003,0x0001,0xD0F9,0x0002,0xB9EC,0xD4FE,0x0003,
+ 0x0000,0xB3B5,0x0000,0xE9F1,0x0000,0x0004,0x0002,0xE9F6,0xE0CE,0x0001,0xBAE4,0x000B,0x0005,0x0001,0x0001,0x0000,
+ 0xEAA5,0x0001,0x0000,0x0002,0xBDCE,0xD5DE,0x000B,0x0004,0x0000,0x0001,0xD7AA,0x0004,0x0002,0xEAA4,0xD4AF,0x0000,
+ 0xCFBD,0x0006,0x0003,0x0000,0xECB1,0x0001,0xD3DF,0x0003,0x0000,0xD5B7,0x0001,0xB7F8,0x007F,0x0047,0x0022,0x000D,
+ 0x0005,0x0000,0x0001,0x0001,0xCAE4,0x0004,0x0000,0x0000,0xEAA3,0x0000,0x0000,0xBCAD,0x000C,0x0006,0x0003,0x0001,
+ 0xC2D6,0x0000,0xB1B2,0x0003,0x0001,0xE9FD,0x0000,0xB9F5,0x0001,0x0004,0x0002,0xEAA1,0xE9FE,0x0002,0xBBD4,0xEAA2,
+ 0x000F,0x0009,0x0004,0x0000,0x0000,0xC1BE,0x0001,0x0002,0xC7E1,0xB8A8,0x0000,0x0000,0x0002,0xCDEC,0xE9FC,0x000C,
+ 0x0006,0x0003,0x0001,0xE9F9,0x0000,0xD4D8,0x0003,0x0000,0xE9FA,0x0000,0xE9FB,0x0004,0x0000,0x0000,0xBDCF,0x0003,
+ 0x0001,0xE9F8,0x0001,0xE9F3,0x001A,0x000D,0x0000,0x0008,0x0004,0x0002,0xE9F0,0xE9F7,0x0002,0xE9F2,0xD6E1,0x0000,
+ 0x0001,0xE9EE,0x0008,0x0004,0x0000,0x0000,0xE9F4,0x0001,0x0000,0xBED0,0x0001,0x0000,0x0000,0xC8ED,0x0013,0x0008,
+ 0x0004,0x0000,0x0000,0xE9EE,0x0001,0x0001,0xE9ED,0x0004,0x0000,0x0001,0xD0F9,0x0003,0x0001,0xEAA6,0x0002,0xBEFC,
+ 0xB9EC,0x0006,0x0000,0x0000,0x0002,0xD4FE,0xB3B5,0x0000,0x0001,0x0001,0xC7FB,0x0051,0x0024,0x000F,0x0005,0x0000,
+ 0x0000,0x0001,0xCCC9,0x0004,0x0000,0x0001,0xB6E3,0x0003,0x0000,0xC7FB,0x0001,0xB9AA,0x000B,0x0005,0x0000,0x0002,
+ 0xC9ED,0xF5EF,0x0003,0x0001,0xF5F2,0x0000,0xB4DA,0x0004,0x0001,0x0000,0xF5E6,0x0003,0x0001,0xF5F3,0x0001,0xF5F2,
+ 0x0016,0x0008,0x0004,0x0000,0x0001,0xF5D1,0x0001,0x0001,0xF5F0,0x0008,0x0004,0x0002,0xF5D9,0xF5C8,0x0002,0xF5DC,
+ 0xF5F1,0x0003,0x0000,0xF5EF,0x0000,0xD4BE,0x000D,0x0007,0x0004,0x0002,0xF5D2,0xB3EC,0x0000,0xF5BB,0x0003,0x0000,
+ 0xB3F9,0x0000,0xF5EE,0x0004,0x0001,0x0000,0xD4EA,0x0003,0x0000,0xB4DA,0x0001,0xF5EB,0x0029,0x0017,0x000A,0x0004,
+ 0x0000,0x0001,0xF5CE,0x0003,0x0001,0xF5EA,0x0001,0xF5ED,0x0006,0x0003,0x0001,0xB6D7,0x0001,0xF5E9,0x0003,0x0000,
+ 0xF5EC,0x0002,0xB2E4,0xB5C5,0x000A,0x0004,0x0001,0x0000,0xF5BF,0x0003,0x0001,0xB1C4,0x0001,0xD7D9,0x0004,0x0000,
+ 0x0000,0xF5E7,0x0000,0x0000,0xBCA3,0x0012,0x0008,0x0004,0x0001,0x0000,0xF5BE,0x0001,0x0000,0xF5CF,0x0006,0x0003,
+ 0x0001,0xF5E7,0x0000,0xF5E6,0x0001,0x0001,0xF5C4,0x000E,0x0008,0x0004,0x0002,0xCCA3,0xF5E8,0x0002,0xF5E3,0xB5B8,
+ 0x0003,0x0000,0xE5BF,0x0001,0xCCE3,0x0007,0x0003,0x0001,0xF5E5,0x0002,0xF5E4,0xF5DE,0x0001,0x0000,0xF5E1,0x0152,
+ 0x00A5,0x0046,0x002B,0x0015,0x0009,0x0005,0x0001,0x0002,0xF5DD,0xF5DF,0x0001,0x0001,0xF5E0,0x0006,0x0003,0x0000,
+ 0xD3BB,0x0001,0xF5E2,0x0003,0x0001,0xF5DC,0x0000,0xF5DA,0x0008,0x0000,0x0003,0x0000,0xF5D9,0x0002,0xD7D9,0xB2C8,
+ 0x0007,0x0003,0x0001,0xF5DB,0x0002,0xCCDF,0xF2E9,0x0003,0x0001,0xF5D8,0x0002,0xBEE1,0xF5D7,0x000A,0x0001,0x0004,
+ 0x0000,0x0000,0xF5D6,0x0000,0x0002,0xBCF9,0xCCA4,0x000C,0x0007,0x0003,0x0000,0xB3EC,0x0002,0xD3BB,0xF5D4,0x0001,
+ 0x0002,0xB6D7,0xF5BD,0x0001,0x0001,0x0001,0xF5D5,0x0032,0x0018,0x000F,0x0008,0x0004,0x0002,0xBED6,0xF5D2,0x0002,
+ 0xB6E5,0xF5D1,0x0004,0x0002,0xF5CF,0xF5CE,0x0001,0xBCF9,0x0005,0x0000,0x0002,0xB2C8,0xCCF8,0x0000,0x0001,0xC2B7,
+ 0x000B,0x0007,0x0004,0x0002,0xF5CD,0xF5BC,0x0000,0xB9F2,0x0000,0x0000,0xBFE7,0x0008,0x0004,0x0002,0xF5D3,0xF5D0,
+ 0x0002,0xF5DC,0xBCA3,0x0003,0x0001,0xB8FA,0x0002,0xF5C8,0xBEE0,0x0017,0x000C,0x0006,0x0003,0x0001,0xF5CB,0x0000,
+ 0xF5C7,0x0003,0x0001,0xF5C6,0x0000,0xF5C5,0x0004,0x0001,0x0001,0xC5DC,0x0004,0x0002,0xB2C8,0xF5CA,0x0000,0xF5C9,
+ 0x0009,0x0005,0x0000,0x0002,0xB5F8,0xB0CF,0x0001,0x0000,0xF5CC,0x0007,0x0004,0x0002,0xF5C4,0xD4BE,0x0000,0xC6E7,
+ 0x0003,0x0001,0xF5C1,0x0000,0xD6BA,0x0045,0x0022,0x0015,0x000C,0x0006,0x0003,0x0000,0xF5C2,0x0000,0xF5C3,0x0003,
+ 0x0000,0xF5BB,0x0001,0xF5C0,0x0000,0x0004,0x0002,0xC5BF,0xD7E3,0x0002,0xF4F5,0xF4F5,0x0005,0x0001,0x0000,0x0000,
+ 0xC7F7,0x0004,0x0000,0x0001,0xC8A4,0x0000,0x0001,0xCCCB,0x0012,0x0008,0x0004,0x0001,0x0001,0xD5D4,0x0001,0x0001,
+ 0xB8CF,0x0006,0x0003,0x0000,0xF4F3,0x0001,0xF4F4,0x0001,0x0001,0xC7F7,0x000A,0x0006,0x0003,0x0001,0xC7F7,0x0000,
+ 0xD4BD,0x0001,0x0001,0xB3AC,0x0000,0x0003,0x0000,0xF4F2,0x0001,0xB3C3,0x002C,0x0013,0x0008,0x0001,0x0003,0x0001,
+ 0xC6F0,0x0002,0xB8CF,0xD5D4,0x0005,0x0000,0x0002,0xB8B0,0xF4F1,0x0003,0x0000,0xD7DF,0x0001,0xF4F7,0x000A,0x0004,
+ 0x0000,0x0001,0xBAD5,0x0003,0x0001,0xF4F6,0x0000,0xC9E2,0x0008,0x0004,0x0002,0xB3E0,0xB8D3,0x0002,0xD3AE,0xC9C4,
+ 0x0003,0x0000,0xD4F9,0x0002,0xD4DE,0xD8CD,0x001C,0x000F,0x0008,0x0004,0x0002,0xD8D3,0xC8FC,0x0002,0xD7AC,0xEAE7,
+ 0x0003,0x0000,0xD7B8,0x0002,0xC0B5,0xEAE6,0x0005,0x0000,0x0002,0xC5E2,0xE2D9,0x0004,0x0002,0xB4CD,0xC9CD,0x0002,
+ 0xCAEA,0xEAE5,0x0010,0x0008,0x0004,0x0002,0xB6C4,0xB8B3,0x0002,0xC9DE,0xEAE3,0x0004,0x0002,0xEAE2,0xEAE4,0x0002,
+ 0xEAE1,0xEAE0,0x0008,0x0004,0x0002,0xD7CA,0xD4DF,0x0002,0xC2B8,0xC1DE,0x0004,0x0002,0xEADF,0xBBDF,0x0002,0xBCD6,
+ 0xEADE,0x00C2,0x0067,0x003F,0x0020,0x0010,0x0008,0x0004,0x0002,0xD4F4,0xEADD,0x0002,0xBAD8,0xB7D1,0x0004,0x0002,
+ 0xC3B3,0xB4FB,0x0002,0xEADC,0xB9F3,0x0008,0x0004,0x0002,0xCCF9,0xEADB,0x0002,0xEADA,0xBCFA,0x0004,0x0002,0xB7A1,
+ 0xB9E1,0x0002,0xD6FC,0xB9BA,0x0010,0x0008,0x0004,0x0002,0xB1E1,0xC6B6,0x0002,0xCCB0,0xB7B7,0x0004,0x0002,0xD6CA,
+ 0xBBF5,0x0002,0xD5CB,0xB0DC,0x0008,0x0004,0x0002,0xCFCD,0xD4F0,0x0002,0xB2C6,0xB9B1,0x0003,0x0001,0xB8BA,0x0002,
+ 0xD5EA,0xB1B4,0x0015,0x000A,0x0004,0x0000,0x0001,0xB8D3,0x0003,0x0001,0xD8CD,0x0000,0xCAEA,0x0004,0x0000,0x0001,
+ 0xD4DF,0x0004,0x0002,0xEAE1,0xD3AE,0x0001,0xC9C4,0x000A,0x0004,0x0001,0x0000,0xD4DE,0x0003,0x0000,0xD4F9,0x0001,
+ 0xD7B8,0x0004,0x0000,0x0000,0xEADE,0x0001,0x0002,0xD8D3,0xC8FC,0x002D,0x0013,0x000B,0x0007,0x0004,0x0002,0xB9BA,
+ 0xEAE7,0x0000,0xD7AC,0x0000,0x0000,0xCAA3,0x0004,0x0000,0x0000,0xC0B5,0x0001,0x0001,0xB6C4,0x000C,0x0006,0x0003,
+ 0x0000,0xD5CB,0x0000,0xD6CA,0x0003,0x0001,0xEAE6,0x0000,0xB8B3,0x0008,0x0004,0x0002,0xBCFA,0xC2F4,0x0002,0xCFCD,
+ 0xE2D9,0x0003,0x0000,0xC5E2,0x0000,0xC9CD,0x0012,0x000A,0x0006,0x0003,0x0000,0xB4CD,0x0001,0xD6DC,0x0001,0x0001,
+ 0xEAE4,0x0000,0x0003,0x0001,0xB1F6,0x0002,0xC9DE,0xEAE2,0x000D,0x0006,0x0003,0x0000,0xEAE0,0x0000,0xD4F4,0x0004,
+ 0x0002,0xBCD6,0xD7CA,0x0001,0xEAE0,0x0008,0x0004,0x0002,0xBBDF,0xC1DE,0x0002,0xC2B8,0xEADA,0x0004,0x0002,0xBAD8,
+ 0xC3B3,0x0001,0xEADD,0x0052,0x0036,0x001A,0x000E,0x0007,0x0004,0x0002,0xCCF9,0xB7D1,0x0000,0xEADC,0x0004,0x0002,
+ 0xB4FB,0xC2F2,0x0000,0xB1E1,0x0007,0x0004,0x0002,0xB9F3,0xB7A1,0x0000,0xEADF,0x0000,0x0002,0xEADB,0xD6FC,0x000D,
+ 0x0008,0x0004,0x0002,0xD4F0,0xB9E1,0x0002,0xCCB0,0xB7B7,0x0000,0x0002,0xBBF5,0xC6B6,0x0008,0x0004,0x0002,0xD2C6,
+ 0xDFAF,0x0002,0xB9B1,0xB2C6,0x0003,0x0000,0xB8BA,0x0002,0xD5EA,0xB1B4,0x000B,0x0005,0x0001,0x0000,0x0000,0xF5F8,
+ 0x0000,0x0000,0x0002,0xF5F9,0xC3A8,0x000C,0x0007,0x0003,0x0000,0xC3B2,0x0002,0xF5F6,0xBAD1,0x0001,0x0002,0xE2B5,
+ 0xF5F7,0x0000,0x0001,0x0000,0xF5F5,0x001E,0x000E,0x0009,0x0005,0x0001,0x0002,0xB2F2,0xB1AA,0x0000,0x0000,0xF5F4,
+ 0x0000,0x0000,0x0001,0xE1D9,0x0008,0x0000,0x0004,0x0002,0xD6ED,0xD4A5,0x0000,0xBAC0,0x0000,0x0003,0x0000,0xBFD0,
+ 0x0002,0xBBBF,0xCFF3,0x0012,0x0008,0x0004,0x0001,0x0000,0xEBE0,0x0001,0x0001,0xF5B9,0x0004,0x0000,0x0000,0xD1DE,
+ 0x0003,0x0000,0xB7E1,0x0000,0xCAFA,0x000F,0x0008,0x0004,0x0002,0xCDE3,0xB5C7,0x0002,0xB7E1,0xF4F9,0x0004,0x0002,
+ 0xC6F1,0xF4F8,0x0000,0xB6B9,0x0004,0x0001,0x0001,0xBBED,0x0000,0x0001,0xCFAA,0x02DD,0x018F,0x00F1,0x0074,0x0035,
+ 0x0016,0x0006,0x0001,0x0001,0x0002,0xB9C8,0xDADF,0x0008,0x0004,0x0002,0xDADE,0xC7B4,0x0002,0xDADD,0xDADC,0x0004,
+ 0x0002,0xC6D7,0xC0BE,0x0002,0xDADB,0xDADA,0x0010,0x0008,0x0004,0x0002,0xCCB7,0xC3FD,0x0002,0xDAD9,0xDAD8,0x0004,
+ 0x0002,0xC3A1,0xBDF7,0x0002,0xDAD7,0xC7AB,0x0008,0x0004,0x0002,0xDAD6,0xB0F9,0x0002,0xD2A5,0xD0BB,0x0004,0x0002,
+ 0xDAD5,0xDAD4,0x0000,0xDAD3,0x0020,0x0010,0x0008,0x0004,0x0002,0xDAD2,0xC3D5,0x0002,0xDAD0,0xD1E8,0x0004,0x0002,
+ 0xDACF,0xDAD1,0x0002,0xB2F7,0xDACE,0x0008,0x0004,0x0002,0xDACD,0xDACC,0x0002,0xCEBD,0xDACB,0x0004,0x0002,0xDACA,
+ 0xD0B3,0x0002,0xDAC9,0xBBD1,0x000F,0x0008,0x0004,0x0002,0xB5FD,0xDAC8,0x0002,0xC4B1,0xD2EA,0x0003,0x0001,0xCCB8,
+ 0x0002,0xDAC7,0xD7BB,0x0008,0x0004,0x0002,0xC1C2,0xDAC6,0x0002,0xB5F7,0xDAC5,0x0004,0x0002,0xCBAD,0xDAC4,0x0002,
+ 0xDAC3,0xBFCE,0x003F,0x0020,0x0010,0x0008,0x0004,0x0002,0xB7CC,0xDAC2,0x0002,0xB6C1,0xC5B5,0x0004,0x0002,0xDAC1,
+ 0xD6EE,0x0002,0xC7EB,0xDAC0,0x0008,0x0004,0x0002,0xCBD0,0xCBB5,0x0002,0xDABF,0xBBE5,0x0004,0x0002,0xD3D5,0xDABE,
+ 0x0002,0xCEF3,0xDABD,0x000F,0x0007,0x0004,0x0002,0xD3EF,0xCEDC,0x0000,0xBDEB,0x0004,0x0002,0xDABC,0xDABB,0x0002,
+ 0xB2EF,0xCFEA,0x0008,0x0004,0x0002,0xB8C3,0xDABA,0x0002,0xD2E8,0xD1AF,0x0004,0x0002,0xB9EE,0xDAB9,0x0002,0xDAB8,
+ 0xB5AE,0x001F,0x0010,0x0008,0x0004,0x0002,0xBBB0,0xDAB7,0x0002,0xD6EF,0xB3CF,0x0004,0x0002,0xDAB6,0xDAB5,0x0002,
+ 0xCAAB,0xDAB4,0x0008,0x0004,0x0002,0xCAD4,0xDAB3,0x0002,0xDAB2,0xDAB1,0x0003,0x0000,0xD2EB,0x0002,0xDAAF,0xDAB0,
+ 0x000F,0x0008,0x0004,0x0002,0xB4CA,0xD6DF,0x0002,0xDAAE,0xD5EF,0x0004,0x0002,0xCBDF,0xD5A9,0x0001,0xCAB6,0x0008,
+ 0x0004,0x0002,0xD7E7,0xC6C0,0x0002,0xDAAD,0xDAAC,0x0004,0x0002,0xD6A4,0xBEF7,0x0002,0xB7C3,0xC9E8,0x005B,0x003C,
+ 0x001E,0x000F,0x0007,0x0004,0x0002,0xB7ED,0xCBCF,0x0001,0xC2DB,0x0004,0x0002,0xB6EF,0xD0ED,0x0002,0xDAAB,0xD1C8,
+ 0x0008,0x0004,0x0002,0xDAAA,0xDAA9,0x0002,0xBBE4,0xBDB2,0x0003,0x0001,0xBCC7,0x0002,0xD1B6,0xD2E9,0x000F,0x0007,
+ 0x0003,0x0000,0xD1B5,0x0002,0xC6FD,0xDAA8,0x0004,0x0002,0xC8C3,0xCCD6,0x0002,0xDAA7,0xDAA6,0x0008,0x0004,0x0002,
+ 0xBCA5,0xC8CF,0x0002,0xB8BC,0xB6A9,0x0004,0x0002,0xBCC6,0xDAA5,0x0001,0xDADD,0x0015,0x000A,0x0006,0x0003,0x0001,
+ 0xDAD4,0x0001,0xD4DE,0x0001,0x0001,0xDADF,0x0007,0x0003,0x0000,0xC0BE,0x0002,0xC8C3,0xB2F7,0x0001,0x0001,0xF6C5,
+ 0x0005,0x0000,0x0001,0x0001,0xB1E4,0x0001,0x0000,0x0001,0xB6C1,0x0022,0x0012,0x0008,0x0004,0x0000,0x0000,0xD3FE,
+ 0x0001,0x0000,0xBBA4,0x0004,0x0000,0x0001,0xC7B4,0x0003,0x0001,0xD2E9,0x0000,0xD2EB,0x000B,0x0007,0x0004,0x0002,
+ 0xBBD9,0xC6A9,0x0000,0xDADE,0x0001,0x0001,0xBEAF,0x0001,0x0001,0x0000,0xD4EB,0x0013,0x000D,0x0006,0x0003,0x0001,
+ 0xC6D7,0x0001,0xCCB7,0x0004,0x0002,0xDADB,0xCAB6,0x0001,0xDADA,0x0001,0x0001,0x0002,0xBCA5,0xDADC,0x0007,0x0001,
+ 0x0003,0x0000,0xD6A4,0x0001,0xCEFB,0x0001,0x0003,0x0000,0xBBA9,0x0001,0xC3A1,0x009F,0x0047,0x001B,0x000A,0x0005,
+ 0x0001,0x0000,0x0001,0xBDF7,0x0000,0x0001,0x0001,0xDAA9,0x0009,0x0005,0x0001,0x0002,0xC3FD,0xDAD8,0x0001,0x0000,
+ 0xDAD3,0x0004,0x0000,0x0000,0xF6A5,0x0001,0x0000,0xD2A5,0x0015,0x000D,0x0006,0x0003,0x0001,0xD0BB,0x0001,0xBDB2,
+ 0x0004,0x0002,0xDAD6,0xC7AB,0x0001,0xB0F9,0x0004,0x0001,0x0000,0xDACA,0x0001,0x0000,0xDAD7,0x000A,0x0004,0x0000,
+ 0x0000,0xC3D5,0x0003,0x0000,0xBBD1,0x0001,0xE5C0,0x0006,0x0003,0x0001,0xD6DF,0x0000,0xCCDC,0x0004,0x0002,0xCEBD,
+ 0xDACB,0x0000,0xC4B1,0x002C,0x0017,0x000D,0x0006,0x0003,0x0000,0xC5B5,0x0000,0xDACE,0x0003,0x0000,0xD1E8,0x0002,
+ 0xD6EE,0xB7ED,0x0006,0x0003,0x0000,0xDAC8,0x0001,0xDACF,0x0000,0x0001,0xBBE4,0x000B,0x0007,0x0004,0x0002,0xDAD1,
+ 0xDACD,0x0001,0xDAC9,0x0001,0x0001,0xD0B3,0x0006,0x0003,0x0000,0xDAD0,0x0000,0xDACC,0x0000,0x0000,0xDABB,0x0016,
+ 0x000B,0x0007,0x0003,0x0000,0xDAD2,0x0002,0xB5FD,0xDAC4,0x0001,0x0001,0xDAC5,0x0004,0x0000,0x0000,0xC2DB,0x0004,
+ 0x0002,0xC1C2,0xDAC2,0x0001,0xDAC1,0x000C,0x0006,0x0003,0x0001,0xDABA,0x0001,0xC7EB,0x0003,0x0001,0xDAC3,0x0001,
+ 0xCCB8,0x0004,0x0001,0x0000,0xD7BB,0x0003,0x0000,0xDAC6,0x0001,0xB5F7,0x0058,0x0027,0x0012,0x0008,0x0004,0x0001,
+ 0x0000,0xD2EA,0x0000,0x0001,0xB7CC,0x0004,0x0000,0x0000,0xDAC7,0x0003,0x0000,0xBFCE,0x0000,0xCBAD,0x0007,0x0001,
+ 0x0003,0x0000,0xCBB5,0x0000,0xBBE5,0x0008,0x0004,0x0002,0xCBD0,0xDABE,0x0002,0xCEF3,0xCEDC,0x0003,0x0001,0xBDEB,
+ 0x0000,0xB3CF,0x0016,0x000A,0x0004,0x0000,0x0000,0xD3EF,0x0003,0x0000,0xDABD,0x0000,0xD3D5,0x0007,0x0004,0x0002,
+ 0xE3A3,0xB5AE,0x0001,0xCAC4,0x0000,0x0002,0xDAC0,0xDABF,0x000D,0x0006,0x0003,0x0001,0xC8CF,0x0000,0xD6BE,0x0004,
+ 0x0002,0xCCDC,0xD3FE,0x0001,0xBFE4,0x0008,0x0004,0x0002,0xDAB2,0xD6EF,0x0002,0xDAB3,0xB6DF,0x0003,0x0000,0xD9AC,
+ 0x0001,0xDAB4,0x002F,0x0016,0x0008,0x0004,0x0001,0x0000,0xDAB6,0x0000,0x0001,0xD5B2,0x0007,0x0004,0x0002,0xB3EA,
+ 0xDAB7,0x0001,0xCFEA,0x0004,0x0002,0xB8C3,0xBBB0,0x0000,0xDAB5,0x000C,0x0008,0x0004,0x0002,0xDAB9,0xB9EE,0x0002,
+ 0xDAB8,0xB2EF,0x0000,0x0001,0xCAAB,0x0006,0x0003,0x0000,0xCAD4,0x0001,0xD2E8,0x0004,0x0002,0xD1AF,0xDABC,0x0000,
+ 0xD3BD,0x0016,0x000A,0x0006,0x0003,0x0000,0xB4CA,0x0001,0xD7E7,0x0001,0x0000,0xDAB0,0x0006,0x0003,0x0001,0xC6C0,
+ 0x0000,0xDAAF,0x0003,0x0000,0xDAB1,0x0000,0xD5A9,0x0008,0x0004,0x0000,0x0000,0xDAAA,0x0001,0x0000,0xEEBA,0x0004,
+ 0x0000,0x0000,0xDAAE,0x0003,0x0001,0xDAAC,0x0001,0xF6A4,0x0133,0x00AF,0x0058,0x0028,0x0014,0x000A,0x0005,0x0002,
+ 0xF6A4,0x0001,0xD6A4,0x0000,0x0002,0xD7A2,0xD5EF,0x0006,0x0003,0x0001,0xDAAD,0x0001,0xCBDF,0x0001,0x0000,0xD0ED,
+ 0x0009,0x0005,0x0001,0x0002,0xC9E8,0xB3B3,0x0000,0x0001,0xB7C3,0x0006,0x0003,0x0000,0xD3C8,0x0000,0xDAAB,0x0000,
+ 0x0002,0xBEF7,0xD0C0,0x0019,0x000C,0x0006,0x0003,0x0000,0xCBCF,0x0000,0xD1C8,0x0003,0x0000,0xB6EF,0x0001,0xBCC7,
+ 0x0007,0x0004,0x0002,0xCDD0,0xC6FD,0x0000,0xDAA8,0x0003,0x0000,0xD1B5,0x0001,0xDAA6,0x000C,0x0006,0x0003,0x0001,
+ 0xCCD6,0x0001,0xDAA7,0x0003,0x0001,0xD1B6,0x0001,0xBCC6,0x0004,0x0000,0x0000,0xD9EA,0x0004,0x0002,0xB8BC,0xB6A9,
+ 0x0001,0xD1D4,0x001F,0x000D,0x0005,0x0001,0x0001,0x0001,0xB4A5,0x0004,0x0001,0x0001,0xF5FC,0x0000,0x0000,0xECB2,
+ 0x0008,0x0004,0x0000,0x0000,0xF6A3,0x0000,0x0000,0xF6A2,0x0006,0x0003,0x0001,0xB4A5,0x0000,0xF6A1,0x0000,0x0000,
+ 0xBDE2,0x001A,0x000C,0x0008,0x0004,0x0002,0xE2B3,0xF5FC,0x0002,0xB5D6,0xF5FE,0x0000,0x0001,0xF5FD,0x0007,0x0003,
+ 0x0001,0xF5FB,0x0002,0xB4D6,0xBDEF,0x0003,0x0001,0xBDC7,0x0002,0xEAEF,0xEAEE,0x000F,0x0007,0x0004,0x0002,0xEAED,
+ 0xEAEC,0x0001,0xEAEB,0x0004,0x0002,0xEAEA,0xEAE9,0x0002,0xBEF5,0xC0C0,0x0008,0x0004,0x0002,0xEAE8,0xCAD3,0x0002,
+ 0xC3D9,0xB9E6,0x0003,0x0001,0xB9DB,0x0002,0xBCFB,0xB9DB,0x0049,0x0024,0x0012,0x000A,0x0006,0x0003,0x0000,0xEAEB,
+ 0x0000,0xC0C0,0x0000,0x0001,0xBEF5,0x0004,0x0000,0x0000,0xEAEF,0x0000,0x0001,0xEAEE,0x000A,0x0006,0x0003,0x0000,
+ 0xEAED,0x0001,0xEAE9,0x0000,0x0001,0xC7D7,0x0004,0x0000,0x0001,0xEAEC,0x0001,0x0000,0xEAEA,0x0012,0x0007,0x0001,
+ 0x0003,0x0000,0xC3D9,0x0001,0xEAE8,0x0007,0x0004,0x0002,0xCBC5,0xCAD3,0x0000,0xC6B3,0x0000,0x0000,0xC3D9,0x0008,
+ 0x0004,0x0000,0x0000,0xB9E6,0x0000,0x0000,0xBCFB,0x0004,0x0000,0x0001,0xB8B2,0x0004,0x0002,0xF1FB,0xB7A6,0x0000,
+ 0xD2AA,0x0021,0x000D,0x0008,0x0004,0x0000,0x0000,0xCEF7,0x0000,0x0000,0xF1E1,0x0001,0x0000,0x0001,0xCFAE,0x000A,
+ 0x0006,0x0003,0x0000,0xB3C4,0x0001,0xB0DA,0x0000,0x0001,0xCDE0,0x0006,0x0003,0x0001,0xF1E0,0x0001,0xF1DC,0x0001,
+ 0x0001,0xF1C9,0x000D,0x0008,0x0000,0x0004,0x0002,0xBDF3,0xF4C5,0x0000,0xF1CF,0x0000,0x0000,0x0001,0xB0C0,0x0005,
+ 0x0001,0x0001,0x0000,0xF1D0,0x0004,0x0001,0x0001,0xCFE5,0x0001,0x0000,0xF1DF,0x0095,0x003F,0x0022,0x0014,0x0007,
+ 0x0001,0x0003,0x0000,0xD9F4,0x0001,0xF1DA,0x0006,0x0003,0x0001,0xF1DE,0x0001,0xF1DC,0x0004,0x0002,0xF1CD,0xBFE3,
+ 0x0001,0xE5BD,0x0006,0x0001,0x0000,0x0002,0xF1DD,0xCDCA,0x0004,0x0001,0x0000,0xC8EC,0x0001,0x0000,0xF1D7,0x0010,
+ 0x0008,0x0001,0x0004,0x0002,0xF1DA,0xF1D2,0x0000,0xF1D8,0x0001,0x0004,0x0002,0xF1D9,0xB0FD,0x0001,0xBAD6,0x0005,
+ 0x0001,0x0000,0x0001,0xF1DB,0x0004,0x0000,0x0000,0xB8B4,0x0000,0x0001,0xB9D3,0x002A,0x0019,0x000C,0x0007,0x0003,
+ 0x0001,0xF1D5,0x0002,0xD6C6,0xF1D3,0x0001,0x0002,0xB9FC,0xC2E3,0x0006,0x0003,0x0000,0xD9F2,0x0001,0xC5E1,0x0003,
+ 0x0000,0xC9D1,0x0002,0xF1D1,0xF1D6,0x0005,0x0001,0x0001,0x0001,0xF1D4,0x0005,0x0001,0x0002,0xF1D0,0xBFE3,0x0004,
+ 0x0002,0xF1CF,0xF1CD,0x0000,0xC0EF,0x0015,0x000C,0x0007,0x0003,0x0000,0xF4C4,0x0002,0xD7B0,0xB2B9,0x0001,0x0002,
+ 0xC8B9,0xF4C3,0x0005,0x0001,0x0002,0xD4A3,0xD2E1,0x0000,0x0001,0xD9F6,0x000A,0x0004,0x0000,0x0001,0xF1CE,0x0003,
+ 0x0001,0xF4C1,0x0000,0xF1CC,0x0006,0x0003,0x0001,0xF1C9,0x0000,0xD7B0,0x0003,0x0001,0xC1D1,0x0002,0xB2C3,0xD2F0,
+ 0x0043,0x001D,0x000D,0x0005,0x0000,0x0001,0x0001,0xF1CB,0x0004,0x0000,0x0000,0xF1CA,0x0001,0x0000,0xB8A4,0x0008,
+ 0x0004,0x0001,0x0000,0xCFAE,0x0000,0x0000,0xB1BB,0x0004,0x0001,0x0001,0xD9F3,0x0000,0x0001,0xF1C8,0x0011,0x0008,
+ 0x0000,0x0004,0x0002,0xE0F9,0xD9F2,0x0001,0xCDE0,0x0005,0x0000,0x0002,0xD5E4,0xD0E4,0x0000,0x0001,0xCCBB,0x000A,
+ 0x0004,0x0001,0x0000,0xC5DB,0x0003,0x0000,0xB4FC,0x0001,0xF4C2,0x0005,0x0001,0x0002,0xF4C1,0xB0C0,0x0003,0x0001,
+ 0xF1C7,0x0000,0xD4AC,0x002F,0x0016,0x000B,0x0007,0x0004,0x0002,0xF1C6,0xF4C0,0x0000,0xF1C5,0x0001,0x0000,0xD6BB,
+ 0x0005,0x0000,0x0002,0xD6D4,0xB3E5,0x0003,0x0001,0xF1C4,0x0001,0xCBA5,0x000D,0x0006,0x0003,0x0001,0xD9F2,0x0001,
+ 0xB3C4,0x0003,0x0000,0xC9C0,0x0002,0xF1C3,0xB1ED,0x0005,0x0001,0x0002,0xB2B9,0xF1C2,0x0004,0x0002,0xD2C2,0xE1E9,
+ 0x0000,0xBAE2,0x0016,0x000A,0x0004,0x0001,0x0000,0xB3E5,0x0003,0x0000,0xCEC0,0x0000,0xD1C3,0x0008,0x0004,0x0002,
+ 0xBDD6,0xC5AA,0x0002,0xCDAC,0xCFCE,0x0000,0x0000,0xCAF5,0x000B,0x0007,0x0003,0x0001,0xD9A9,0x0002,0xD1DC,0xD0D0,
+ 0x0000,0x0001,0xC3EF,0x0005,0x0001,0x0002,0xD0C6,0xF4AC,0x0001,0x0001,0xD1AA,0x04AA,0x0272,0x0121,0x0088,0x0041,
+ 0x001A,0x0011,0x0008,0x0001,0x0004,0x0002,0xF3BD,0xC2F9,0x0001,0xF3BC,0x0004,0x0001,0x0000,0xB2CF,0x0001,0x0002,
+ 0xEEC3,0xB9C6,0x0001,0x0001,0x0003,0x0001,0xF2C3,0x0002,0xB4C0,0xF3BB,0x0014,0x0008,0x0004,0x0000,0x0001,0xC0AF,
+ 0x0000,0x0001,0xF3BA,0x0005,0x0001,0x0002,0xF3B6,0xC8E4,0x0004,0x0002,0xF2BA,0xF3B7,0x0001,0xF2EE,0x000A,0x0006,
+ 0x0003,0x0000,0xF2D3,0x0001,0xD0AB,0x0001,0x0000,0xF3B9,0x0005,0x0001,0x0002,0xF2B2,0xD3AC,0x0000,0x0001,0xD9F9,
+ 0x0026,0x0012,0x000A,0x0004,0x0001,0x0000,0xF3B8,0x0003,0x0001,0xD2CF,0x0001,0xD0B7,0x0004,0x0001,0x0000,0xF2C9,
+ 0x0001,0x0000,0xB3E6,0x000C,0x0006,0x0003,0x0001,0xF2CD,0x0000,0xF3B5,0x0003,0x0000,0xB2F5,0x0000,0xF3B3,0x0004,
+ 0x0001,0x0001,0xF3A8,0x0000,0x0001,0xF2B1,0x0010,0x0008,0x0004,0x0000,0x0000,0xF3B4,0x0000,0x0001,0xF3B2,0x0001,
+ 0x0003,0x0001,0xF3AD,0x0002,0xF2FE,0xF3AF,0x0007,0x0001,0x0003,0x0001,0xF3AC,0x0000,0xF3B1,0x0006,0x0003,0x0000,
+ 0xF2E5,0x0000,0xF3A1,0x0000,0x0000,0xD5DD,0x004B,0x0027,0x0014,0x000C,0x0006,0x0003,0x0000,0xF3B0,0x0001,0xF3AE,
+ 0x0003,0x0001,0xF2F7,0x0000,0xC2DD,0x0004,0x0001,0x0001,0xF3AA,0x0000,0x0001,0xF3AB,0x000B,0x0006,0x0003,0x0001,
+ 0xF2FC,0x0001,0xF3A4,0x0000,0x0002,0xF3A9,0xF3A7,0x0004,0x0000,0x0000,0xF2FD,0x0001,0x0000,0xD3A9,0x000F,0x0007,
+ 0x0000,0x0003,0x0001,0xC3F8,0x0000,0xC2EC,0x0004,0x0000,0x0001,0xF3A5,0x0000,0x0001,0xF2FB,0x0008,0x0004,0x0001,
+ 0x0001,0xC8DA,0x0000,0x0001,0xF2F4,0x0006,0x0003,0x0000,0xF3A2,0x0001,0xF3A3,0x0004,0x0002,0xF2CF,0xF3A6,0x0000,
+ 0xF2EB,0x0027,0x0014,0x000A,0x0005,0x0001,0x0002,0xF2EE,0xF2ED,0x0000,0x0002,0xF2F7,0xF2EF,0x0006,0x0003,0x0000,
+ 0xCECF,0x0000,0xB5FB,0x0000,0x0000,0xBAFB,0x0007,0x0000,0x0003,0x0000,0xF2F1,0x0000,0xF2F3,0x0007,0x0003,0x0000,
+ 0xCAAD,0x0002,0xCFBA,0xF2FA,0x0000,0x0002,0xF2F8,0xF2F6,0x0012,0x0008,0x0004,0x0000,0x0000,0xF2F0,0x0001,0x0001,
+ 0xF2F9,0x0006,0x0003,0x0001,0xBBC8,0x0001,0xCAB4,0x0000,0x0001,0xF2F5,0x000A,0x0004,0x0001,0x0000,0xD0AB,0x0003,
+ 0x0000,0xF2F2,0x0001,0xB2F5,0x0007,0x0004,0x0002,0xF2E5,0xD3AC,0x0000,0xF2D5,0x0000,0x0001,0xBAE7,0x00AE,0x0059,
+ 0x0026,0x0012,0x000A,0x0005,0x0001,0x0002,0xF2EA,0xF2E4,0x0001,0x0002,0xF2DF,0xC4DE,0x0004,0x0001,0x0000,0xF2E9,
+ 0x0000,0x0001,0xF2E6,0x0007,0x0000,0x0003,0x0000,0xF2E7,0x0001,0xF2E2,0x0005,0x0000,0x0002,0xF2E8,0xB5FB,0x0004,
+ 0x0002,0xF2E1,0xF2E1,0x0002,0xF2DE,0xF2EC,0x0019,0x000C,0x0006,0x0003,0x0000,0xC0AF,0x0001,0xF2E0,0x0003,0x0001,
+ 0xC3DB,0x0001,0xF2E3,0x0006,0x0003,0x0001,0xD6A9,0x0000,0xCECF,0x0003,0x0000,0xCDC9,0x0002,0xF2D1,0xD1D1,0x000B,
+ 0x0004,0x0000,0x0000,0xD1D1,0x0003,0x0000,0xF2DC,0x0002,0xF2EB,0xF2DB,0x0008,0x0004,0x0002,0xF2DD,0xF2DA,0x0002,
+ 0xF2D8,0xF2B9,0x0003,0x0001,0xF2D7,0x0002,0xF2D7,0xB7E4,0x002B,0x0017,0x000B,0x0006,0x0003,0x0001,0xCAF1,0x0001,
+ 0xB6EA,0x0001,0x0002,0xCDC9,0xF2CC,0x0005,0x0000,0x0002,0xD3BC,0xF2D9,0x0003,0x0001,0xF2D3,0x0002,0xF2CF,0xF2CD,
+ 0x000C,0x0007,0x0004,0x0002,0xF2CC,0xD5DD,0x0001,0xC2F9,0x0000,0x0002,0xF2CE,0xF2CB,0x0004,0x0000,0x0000,0xF2CB,
+ 0x0000,0x0001,0xB8F2,0x0012,0x0009,0x0005,0x0001,0x0002,0xF2D4,0xF2D2,0x0001,0x0000,0xD6EB,0x0005,0x0000,0x0002,
+ 0xCDDC,0xF2D5,0x0000,0x0001,0xBBD7,0x000D,0x0008,0x0004,0x0002,0xF2D6,0xF2D0,0x0002,0xF2C9,0xF2C3,0x0001,0x0002,
+ 0xB5B0,0xB9C6,0x0007,0x0003,0x0000,0xF2C8,0x0002,0xC9DF,0xC7F9,0x0000,0x0001,0xF2C1,0x0050,0x002D,0x0013,0x000A,
+ 0x0004,0x0000,0x0001,0xD6FB,0x0003,0x0001,0xBED4,0x0001,0xF2C5,0x0004,0x0001,0x0001,0xF2C0,0x0000,0x0002,0xF2C2,
+ 0xF2CA,0x000D,0x0007,0x0004,0x0002,0xF2C6,0xF2C4,0x0000,0xF2C7,0x0003,0x0001,0xF2B9,0x0001,0xF2BD,0x0007,0x0004,
+ 0x0002,0xF2BF,0xF2B6,0x0000,0xF2BB,0x0003,0x0001,0xD4E9,0x0000,0xF2BC,0x0015,0x0009,0x0004,0x0000,0x0000,0xF7F7,
+ 0x0000,0x0002,0xF2BA,0xD1C1,0x0006,0x0003,0x0001,0xBBD7,0x0001,0xF2B3,0x0003,0x0000,0xB2CF,0x0000,0xF2BE,0x0009,
+ 0x0001,0x0004,0x0002,0xF2B7,0xB0F6,0x0002,0xF2B8,0xCEC3,0x0001,0x0001,0x0001,0xC2EC,0x002F,0x001A,0x0010,0x0008,
+ 0x0004,0x0002,0xD2CF,0xCAB4,0x0002,0xF2B2,0xCFBA,0x0004,0x0002,0xCBE4,0xF2B4,0x0002,0xF2B5,0xF2B3,0x0006,0x0003,
+ 0x0000,0xBAE7,0x0000,0xB8C9,0x0000,0x0001,0xF2C6,0x000D,0x0007,0x0003,0x0000,0xCAAD,0x0002,0xF2B0,0xF2B1,0x0003,
+ 0x0001,0xF2B0,0x0000,0xB3E6,0x0004,0x0001,0x0000,0xBFF7,0x0001,0x0001,0xEBBD,0x0017,0x000C,0x0005,0x0001,0x0002,
+ 0xBAC5,0xD3DD,0x0003,0x0001,0xC2B2,0x0002,0xD0E9,0xD0E9,0x0006,0x0003,0x0000,0xB7FC,0x0001,0xBAF5,0x0000,0x0002,
+ 0xB4A6,0xF2AF,0x0000,0x0008,0x0004,0x0002,0xC2C7,0xC5B0,0x0002,0xC2B2,0xBBA2,0x0000,0x0000,0xF2AE,0x0112,0x007E,
+ 0x0042,0x001B,0x000F,0x0008,0x0004,0x0001,0x0001,0xC2DC,0x0001,0x0000,0xDEC2,0x0000,0x0003,0x0000,0xDDF1,0x0000,
+ 0xD5BA,0x0005,0x0001,0x0000,0x0001,0xC0BC,0x0000,0x0003,0x0001,0xDEC0,0x0001,0xDEBE,0x0012,0x0008,0x0004,0x0000,
+ 0x0000,0xDCD7,0x0000,0x0000,0xDDFC,0x0006,0x0003,0x0000,0xDEBA,0x0001,0xE9DE,0x0000,0x0000,0xDEC1,0x0008,0x0004,
+ 0x0000,0x0001,0xC4A2,0x0001,0x0001,0xC6BB,0x0006,0x0003,0x0000,0xD4CC,0x0001,0xCBD5,0x0004,0x0002,0xC2AB,0xDEBF,
+ 0x0000,0xDEAD,0x001F,0x0013,0x0008,0x0004,0x0001,0x0001,0xDEBD,0x0001,0x0001,0xD4E5,0x0007,0x0004,0x0002,0xDDFE,
+ 0xB0AA,0x0001,0xCAED,0x0000,0x0000,0xDCC2,0x0001,0x0005,0x0000,0x0002,0xDEB4,0xB7AA,0x0003,0x0001,0xD2A9,0x0000,
+ 0xCCD9,0x000E,0x0007,0x0001,0x0003,0x0001,0xD2D5,0x0000,0xDEBC,0x0001,0x0003,0x0001,0xC5BA,0x0001,0xDEBA,0x000A,
+ 0x0005,0x0001,0x0002,0xC3EA,0xB2D8,0x0000,0x0002,0xDDA3,0xC0B6,0x0000,0x0000,0x0001,0xBDE5,0x0048,0x0023,0x000D,
+ 0x0005,0x0000,0x0000,0x0001,0xDEBB,0x0000,0x0004,0x0002,0xDCF9,0xDEB7,0x0001,0xDEB8,0x0009,0x0005,0x0001,0x0002,
+ 0xDEB9,0xCAED,0x0000,0x0000,0xDEB4,0x0007,0x0004,0x0002,0xD0BD,0xC8F8,0x0000,0xDEB0,0x0003,0x0000,0xBCF6,0x0000,
+ 0xDEAF,0x000E,0x0009,0x0004,0x0001,0x0001,0xDDB2,0x0001,0x0002,0xDEB5,0xD1A6,0x0001,0x0001,0x0000,0xC7BE,0x000A,
+ 0x0006,0x0003,0x0001,0xBDAA,0x0001,0xDEB2,0x0001,0x0000,0xDCBC,0x0007,0x0003,0x0000,0xBCBB,0x0002,0xDCF6,0xDEB1,
+ 0x0003,0x0001,0xDEB6,0x0000,0xB1A1,0x0025,0x0012,0x0007,0x0001,0x0003,0x0000,0xC0D9,0x0001,0xDEAE,0x0007,0x0004,
+ 0x0002,0xDEAA,0xDEB3,0x0001,0xDDF7,0x0001,0x0000,0xD4CC,0x0008,0x0004,0x0000,0x0000,0xDEAD,0x0000,0x0001,0xCFF4,
+ 0x0007,0x0004,0x0002,0xCEDF,0xB5B4,0x0000,0xDEA7,0x0001,0x0000,0xDEA8,0x0015,0x0008,0x0004,0x0000,0x0000,0xDDDE,
+ 0x0000,0x0000,0xDEA9,0x0006,0x0003,0x0001,0xDEA5,0x0000,0xDCE9,0x0004,0x0002,0xDEA1,0xDDB5,0x0001,0xDCBF,0x0005,
+ 0x0001,0x0000,0x0000,0xDCF1,0x0007,0x0004,0x0002,0xC8EF,0xBDB6,0x0000,0xDEA6,0x0003,0x0000,0xDDDB,0x0001,0xDEAC,
+ 0x008C,0x0042,0x0024,0x0014,0x000B,0x0004,0x0000,0x0001,0xDDA1,0x0003,0x0001,0xB1CE,0x0002,0xB0AA,0xDEA2,0x0000,
+ 0x0004,0x0002,0xDDFE,0xDDFC,0x0002,0xDDFA,0xC7BE,0x0008,0x0001,0x0003,0x0001,0xD2F1,0x0002,0xCADF,0xC4E8,0x0001,
+ 0x0004,0x0002,0xDCE0,0xB4D0,0x0001,0xBDAF,0x0015,0x000A,0x0006,0x0003,0x0001,0xB2CC,0x0001,0xDDFD,0x0000,0x0000,
+ 0xDDE4,0x0006,0x0003,0x0000,0xCEB5,0x0001,0xD5E1,0x0001,0x0002,0xB2B7,0xC2FB,0x0000,0x0004,0x0000,0x0001,0xC3EF,
+ 0x0001,0x0000,0xDDF8,0x0020,0x000B,0x0000,0x0004,0x0001,0x0001,0xDEA3,0x0003,0x0001,0xDCEA,0x0000,0xDEA4,0x000D,
+ 0x0007,0x0003,0x0001,0xC6C1,0x0002,0xDDFB,0xDCCA,0x0003,0x0000,0xC1AB,0x0000,0xC5EE,0x0001,0x0004,0x0002,0xDDEB,
+ 0xDDF6,0x0001,0xDDF7,0x0013,0x0009,0x0005,0x0001,0x0002,0xDDF1,0xBCBB,0x0000,0x0001,0xC0B6,0x0004,0x0001,0x0001,
+ 0xDDFB,0x0003,0x0000,0xB1CD,0x0001,0xDDED,0x000C,0x0006,0x0003,0x0001,0xCBF2,0x0000,0xDDEA,0x0003,0x0001,0xDDE9,
+ 0x0001,0xB8C7,0x0005,0x0000,0x0002,0xDDEE,0xC8D8,0x0003,0x0000,0xCFAF,0x0000,0xD0EE,0x004D,0x0026,0x0018,0x000D,
+ 0x0007,0x0003,0x0001,0xDDE8,0x0002,0xDDA5,0xDDEF,0x0003,0x0001,0xDDEC,0x0000,0xB2D4,0x0007,0x0004,0x0002,0xDDF0,
+ 0xDDF3,0x0000,0xD5F4,0x0001,0x0000,0xDDF4,0x0008,0x0000,0x0004,0x0002,0xC6D1,0xC6D1,0x0001,0xD8E1,0x0000,0x0001,
+ 0x0002,0xDCE7,0xB5E3,0x0014,0x000A,0x0004,0x0000,0x0001,0xDDF2,0x0003,0x0000,0xDDB0,0x0000,0xCBE2,0x0006,0x0003,
+ 0x0001,0xC3C9,0x0001,0xDDF5,0x0001,0x0000,0xDDAA,0x000B,0x0004,0x0001,0x0000,0xCBD1,0x0003,0x0000,0xDDE5,0x0002,
+ 0xDDE4,0xBDAF,0x0000,0x0003,0x0001,0xDDDE,0x0002,0xDDDC,0xDDDB,0x002B,0x0013,0x0005,0x0000,0x0000,0x0000,0xB5D9,
+ 0x0007,0x0003,0x0000,0xDDDD,0x0002,0xDDDF,0xBBE7,0x0004,0x0002,0xDDE3,0xBFFB,0x0001,0xDDDA,0x000E,0x0007,0x0003,
+ 0x0001,0xB4D0,0x0002,0xDDB4,0xD2A9,0x0003,0x0001,0xDDE7,0x0002,0xD4E1,0xBAF9,0x0004,0x0000,0x0001,0xDDE2,0x0003,
+ 0x0000,0xCEAD,0x0001,0xB6AD,0x0013,0x000B,0x0006,0x0003,0x0001,0xC6CF,0x0000,0xB2CE,0x0001,0x0002,0xDDD6,0xB8F0,
+ 0x0000,0x0004,0x0002,0xDDD8,0xDDD9,0x0001,0xD7C5,0x0006,0x0000,0x0000,0x0002,0xDDA6,0xDDD7,0x0004,0x0000,0x0001,
+ 0xD2B6,0x0000,0x0002,0xDDE1,0xDDCF,0x02D3,0x015C,0x00A3,0x004D,0x0026,0x000E,0x0006,0x0001,0x0001,0x0002,0xC2E4,
+ 0xDDE0,0x0004,0x0001,0x0001,0xDDC7,0x0001,0x0000,0xDDAB,0x000B,0x0006,0x0003,0x0001,0xDDE6,0x0000,0xDDE6,0x0001,
+ 0x0002,0xBED8,0xCDF2,0x0005,0x0001,0x0002,0xE9B1,0xC8F8,0x0004,0x0002,0xCFF4,0xDDD3,0x0002,0xD3AA,0xD3A9,0x000B,
+ 0x0006,0x0001,0x0001,0x0002,0xC2DC,0xDDC6,0x0000,0x0001,0x0001,0xDDC1,0x000E,0x0006,0x0003,0x0000,0xDDE6,0x0000,
+ 0xDDC8,0x0004,0x0002,0xDDCC,0xCEAE,0x0002,0xC6BC,0xC3C8,0x0007,0x0004,0x0002,0xDDC2,0xC0B3,0x0000,0xDDCA,0x0004,
+ 0x0002,0xDCC9,0xDDC9,0x0001,0xCCD1,0x0029,0x0013,0x000A,0x0006,0x0003,0x0000,0xDDCD,0x0000,0xDDBD,0x0001,0x0000,
+ 0xDDC4,0x0005,0x0001,0x0002,0xDDCF,0xDDCE,0x0001,0x0001,0xE2D6,0x000B,0x0007,0x0003,0x0001,0xB7C6,0x0002,0xC1E2,
+ 0xDDD4,0x0000,0x0000,0xBBAA,0x0007,0x0004,0x0002,0xBDF6,0xDDD0,0x0000,0xC6D0,0x0001,0x0000,0xDDBE,0x0016,0x000C,
+ 0x0005,0x0001,0x0002,0xDDD5,0xB2A4,0x0003,0x0000,0xDDCB,0x0002,0xDDC3,0xB2CB,0x0004,0x0001,0x0001,0xDDBF,0x0003,
+ 0x0001,0xDDC5,0x0001,0xDDCA,0x000B,0x0004,0x0001,0x0000,0xD4D6,0x0004,0x0002,0xBACA,0xE7FB,0x0001,0xBEFA,0x0006,
+ 0x0003,0x0001,0xBED5,0x0000,0xC2CC,0x0003,0x0000,0xB9BD,0x0000,0xDDD1,0x005C,0x0031,0x001A,0x000C,0x0005,0x0001,
+ 0x0002,0xDDBC,0xDDD2,0x0003,0x0000,0xB4CC,0x0002,0xC3A7,0xDDBB,0x0007,0x0003,0x0001,0xDDBA,0x0002,0xD3A8,0xDDB5,
+ 0x0004,0x0002,0xBBF1,0xDDB2,0x0001,0xDDAB,0x0009,0x0000,0x0004,0x0002,0xDDAA,0xC1AB,0x0002,0xC0B3,0xDDA8,0x0008,
+ 0x0004,0x0002,0xC4AA,0xDDAD,0x0002,0xDDB3,0xDDB9,0x0003,0x0000,0xDCC8,0x0001,0xDDB5,0x0016,0x000C,0x0006,0x0003,
+ 0x0001,0xBCD4,0x0001,0xDDAC,0x0003,0x0001,0xDDB8,0x0001,0xDDAF,0x0006,0x0003,0x0000,0xDCF0,0x0001,0xDDB7,0x0000,
+ 0x0001,0xBEA5,0x0009,0x0005,0x0000,0x0002,0xDDAE,0xDCEC,0x0000,0x0001,0xC9AF,0x0006,0x0003,0x0001,0xD7AF,0x0000,
+ 0xC0F2,0x0003,0x0001,0xC6CE,0x0000,0xDDB0,0x002C,0x0010,0x0006,0x0001,0x0001,0x0002,0xDDB4,0xDDB1,0x0006,0x0003,
+ 0x0000,0xDDB6,0x0001,0xDDA9,0x0000,0x0000,0xBAC9,0x000C,0x0004,0x0000,0x0000,0xB6B9,0x0004,0x0002,0xD2A9,0xDDA7,
+ 0x0002,0xDDA6,0xDDA4,0x0008,0x0004,0x0002,0xD2F1,0xDDA5,0x0002,0xDDA3,0xDDA1,0x0004,0x0002,0xD3AB,0xDCFD,0x0002,
+ 0xDCFE,0xBBE7,0x0017,0x000E,0x0007,0x0003,0x0000,0xC8D9,0x0002,0xB5B4,0xDCF9,0x0004,0x0002,0xDCF6,0xDCF1,0x0001,
+ 0xDCEA,0x0005,0x0000,0x0002,0xDCE9,0xBCD4,0x0001,0x0001,0xC0F3,0x000D,0x0007,0x0003,0x0001,0xBBC4,0x0002,0xDCE8,
+ 0xBCF6,0x0003,0x0000,0xDCF3,0x0000,0xDCF1,0x0006,0x0003,0x0001,0xBEA3,0x0000,0xB2DD,0x0004,0x0002,0xDCF4,0xBEA3,
+ 0x0000,0xB4F0,0x00C0,0x005F,0x0032,0x001A,0x000B,0x0007,0x0004,0x0002,0xDCF5,0xBBAA,0x0001,0xDCF7,0x0001,0x0001,
+ 0xDCED,0x0007,0x0003,0x0001,0xDCFB,0x0002,0xC8E3,0xC8D7,0x0004,0x0002,0xECB7,0xB2E8,0x0002,0xD2F0,0xDCEE,0x000E,
+ 0x0007,0x0004,0x0002,0xDCFC,0xD7C8,0x0000,0xDCEF,0x0003,0x0000,0xDCF2,0x0002,0xDCFA,0xB2E7,0x0006,0x0003,0x0000,
+ 0xC3A3,0x0001,0xB4C4,0x0000,0x0000,0xBCEB,0x0014,0x0008,0x0004,0x0001,0x0001,0xDEB6,0x0001,0x0001,0xDCE7,0x0005,
+ 0x0000,0x0002,0xDDA2,0xDCE1,0x0003,0x0000,0xDCF8,0x0002,0xDCE4,0xDCE3,0x000C,0x0004,0x0001,0x0000,0xDCE0,0x0004,
+ 0x0002,0xDCD7,0xBEA5,0x0002,0xB9B6,0xDCDD,0x0005,0x0001,0x0002,0xDCD4,0xDCEB,0x0004,0x0002,0xDCD8,0xDCE2,0x0002,
+ 0xC3A9,0xC7D1,0x002A,0x0016,0x0009,0x0000,0x0004,0x0002,0xB7B6,0xC3AF,0x0002,0xD7C2,0xB8A3,0x0007,0x0004,0x0002,
+ 0xDCDE,0xDDAE,0x0000,0xC6BB,0x0003,0x0000,0xDCD5,0x0001,0xDCDA,0x0008,0x0004,0x0001,0x0000,0xD3A2,0x0000,0x0000,
+ 0xB1BD,0x0004,0x0000,0x0000,0xC9BB,0x0004,0x0002,0xDCD1,0xBFE0,0x0002,0xC8F4,0xDCD6,0x001B,0x000E,0x0007,0x0003,
+ 0x0000,0xDCC4,0x0002,0xDCD3,0xDCE5,0x0004,0x0002,0xB9B6,0xB0FA,0x0001,0xDCD9,0x0006,0x0003,0x0000,0xBFC1,0x0001,
+ 0xDCDC,0x0003,0x0000,0xC3E7,0x0002,0xDCE6,0xCCA6,0x000F,0x0007,0x0004,0x0002,0xDCDF,0xDCDB,0x0000,0xD4B7,0x0004,
+ 0x0002,0xCBD5,0xDCD1,0x0002,0xB2D4,0xDCC9,0x0007,0x0004,0x0002,0xDCC8,0xDCC3,0x0001,0xDCC2,0x0003,0x0000,0xCEAD,
+ 0x0001,0xDCD0,0x0066,0x0038,0x001A,0x000D,0x0006,0x0003,0x0000,0xDCD8,0x0000,0xDCCA,0x0003,0x0001,0xDCC0,0x0002,
+ 0xD1BF,0xC3AB,0x0007,0x0003,0x0000,0xDBBB,0x0002,0xC7DB,0xDCBF,0x0003,0x0000,0xDCC6,0x0001,0xDCCC,0x000F,0x0007,
+ 0x0003,0x0000,0xB7BC,0x0002,0xBBA8,0xDCC1,0x0004,0x0002,0xD0BE,0xDCC7,0x0002,0xB0C5,0xB7D2,0x0008,0x0004,0x0002,
+ 0xDCBE,0xDCCE,0x0002,0xDCCB,0xDCB8,0x0003,0x0001,0xC2AB,0x0002,0xBDE6,0xDCD2,0x0014,0x000B,0x0004,0x0001,0x0000,
+ 0xDCCD,0x0003,0x0000,0xDCCF,0x0002,0xD6A5,0xCEDF,0x0005,0x0001,0x0002,0xDCBD,0xDCC5,0x0000,0x0000,0xDCBC,0x000F,
+ 0x0008,0x0004,0x0002,0xDDA9,0xC3A2,0x0002,0xDCBB,0xDCD0,0x0004,0x0002,0xDCB6,0xDCBA,0x0000,0xC9D6,0x0007,0x0004,
+ 0x0002,0xD3F3,0xDCB7,0x0001,0xD8C2,0x0001,0x0001,0xDCB9,0x002F,0x001A,0x000D,0x0006,0x0003,0x0001,0xBDDA,0x0001,
+ 0xDCE6,0x0004,0x0002,0xDCB5,0xB0AC,0x0000,0xDCB4,0x0007,0x0003,0x0001,0xD2D5,0x0002,0xDCB3,0xB2DD,0x0003,0x0000,
+ 0xD1DE,0x0001,0xE5F5,0x000D,0x0008,0x0004,0x0002,0xD1DE,0xC9AB,0x0002,0xBCE8,0xBCE8,0x0000,0x0002,0xC1BC,0xF4DE,
+ 0x0004,0x0001,0x0001,0xF4BF,0x0000,0x0001,0xBDA2,0x0010,0x0008,0x0004,0x0000,0x0000,0xF4B5,0x0000,0x0000,0xF4BE,
+ 0x0000,0x0003,0x0001,0xCBD2,0x0002,0xB2D5,0xCBD2,0x0005,0x0001,0x0000,0x0000,0xF4BC,0x0006,0x0003,0x0000,0xF4BB,
+ 0x0000,0xF4BA,0x0003,0x0000,0xCDA7,0x0002,0xE4E9,0xF4B9,0x015D,0x00AE,0x005A,0x002C,0x0013,0x0005,0x0001,0x0001,
+ 0x0000,0xF4B8,0x0006,0x0003,0x0001,0xF4B5,0x0001,0xB4AC,0x0004,0x0002,0xF4B4,0xCFCF,0x0002,0xB2B0,0xB6E6,0x000E,
+ 0x0007,0x0004,0x0002,0xF4B7,0xF4B6,0x0001,0xB2D5,0x0004,0x0002,0xBDA2,0xF4B1,0x0001,0xF4B0,0x0007,0x0004,0x0002,
+ 0xB0E3,0xF4B3,0x0000,0xBABD,0x0000,0x0000,0xF4B2,0x0013,0x000D,0x0007,0x0003,0x0001,0xF4AF,0x0002,0xF4AE,0xF4AD,
+ 0x0003,0x0001,0xD6DB,0x0000,0xCEE8,0x0000,0x0000,0x0002,0xCBB4,0xE2B6,0x000C,0x0006,0x0003,0x0000,0xCCF2,0x0000,
+ 0xCAE6,0x0003,0x0000,0xF3C2,0x0001,0xC9E1,0x0007,0x0003,0x0000,0xC9E0,0x0002,0xBEC9,0xBED9,0x0004,0x0002,0xD0CB,
+ 0xD3EB,0x0002,0xD3DF,0xBECB,0x002F,0x0018,0x000D,0x0007,0x0003,0x0000,0xF4AA,0x0002,0xF4A9,0xF4A8,0x0003,0x0000,
+ 0xD2A8,0x0000,0xF4A7,0x0007,0x0004,0x0002,0xBECA,0xD5E9,0x0000,0xCCA8,0x0000,0x0001,0xF1F3,0x000A,0x0005,0x0000,
+ 0x0002,0xD6C2,0xD6C1,0x0001,0x0002,0xF4DF,0xB3F4,0x0006,0x0003,0x0000,0xF4AB,0x0000,0xD7D4,0x0004,0x0002,0xC1D9,
+ 0xEAB0,0x0001,0xCED4,0x0013,0x000B,0x0004,0x0000,0x0001,0xB3BC,0x0004,0x0002,0xD9F5,0xD4E0,0x0001,0xC2E3,0x0004,
+ 0x0001,0x0000,0xEBCD,0x0000,0x0000,0xC0B0,0x0007,0x0001,0x0003,0x0001,0xEBF7,0x0001,0xC6EA,0x0007,0x0003,0x0000,
+ 0xEBFB,0x0002,0xEBFD,0xC1B3,0x0001,0x0000,0xD2DC,0x004F,0x0026,0x0016,0x000F,0x0007,0x0003,0x0001,0xD3B7,0x0002,
+ 0xB1DB,0xECA1,0x0004,0x0002,0xCDCE,0xC5A7,0x0002,0xEBDA,0xB5A8,0x0000,0x0003,0x0001,0xEBFE,0x0000,0xE2DF,0x0005,
+ 0x0000,0x0000,0x0001,0xC9C5,0x0005,0x0001,0x0002,0xEBFA,0xC4E5,0x0003,0x0000,0xC5F2,0x0000,0xECA2,0x0015,0x000A,
+ 0x0004,0x0000,0x0001,0xEBF9,0x0003,0x0000,0xBDBA,0x0001,0xCFA5,0x0007,0x0004,0x0002,0xC4A4,0xCCC5,0x0000,0xB7F4,
+ 0x0000,0x0000,0xB1EC,0x0008,0x0004,0x0001,0x0001,0xEBF7,0x0000,0x0001,0xB8E0,0x0006,0x0003,0x0000,0xF1A4,0x0000,
+ 0xB2B2,0x0003,0x0000,0xEBF5,0x0000,0xE0BC,0x0035,0x001A,0x000E,0x0006,0x0003,0x0001,0xEBF0,0x0000,0xEBF6,0x0004,
+ 0x0002,0xB0F2,0xCDC8,0x0002,0xCCDA,0xEBF0,0x0008,0x0004,0x0002,0xEBEF,0xC4E5,0x0002,0xCFD9,0xB8B9,0x0000,0x0000,
+ 0xB3A6,0x000E,0x0007,0x0004,0x0002,0xEBE9,0xBDC5,0x0001,0xEBEC,0x0003,0x0000,0xD1FC,0x0002,0xC8F9,0xEBF1,0x0006,
+ 0x0003,0x0001,0xD6D7,0x0001,0xEBEE,0x0003,0x0001,0xEBF2,0x0002,0xC4D4,0xD0C8,0x0011,0x0008,0x0004,0x0001,0x0001,
+ 0xEBE1,0x0000,0x0000,0xEBED,0x0005,0x0001,0x0002,0xEBEB,0xEBEA,0x0001,0x0001,0xCDF3,0x000D,0x0007,0x0004,0x0002,
+ 0xC7BB,0xEBE8,0x0001,0xB8AD,0x0003,0x0000,0xB8AF,0x0000,0xC9F6,0x0007,0x0004,0x0002,0xEBE7,0xD2B8,0x0000,0xC0B0,
+ 0x0003,0x0000,0xEBE6,0x0000,0xCCF3,0x00B3,0x0052,0x0020,0x000F,0x0005,0x0001,0x0001,0x0000,0xC6A2,0x0004,0x0001,
+ 0x0001,0xD5CD,0x0003,0x0000,0xC1B3,0x0000,0xEBE1,0x0009,0x0005,0x0001,0x0002,0xEBE5,0xCDD1,0x0000,0x0001,0xB8AC,
+ 0x0000,0x0004,0x0002,0xEBE3,0xCDD1,0x0001,0xD0DE,0x0016,0x000A,0x0004,0x0000,0x0001,0xB4BD,0x0003,0x0001,0xD9F5,
+ 0x0000,0xEBE2,0x0006,0x0003,0x0001,0xEBD6,0x0000,0xBDC5,0x0003,0x0000,0xEBE4,0x0000,0xB2B1,0x0010,0x0008,0x0004,
+ 0x0002,0xD9F5,0xC5A7,0x0002,0xEBDF,0xC4D4,0x0004,0x0002,0xC6EA,0xD4E0,0x0002,0xEBDB,0xEBDA,0x0005,0x0001,0x0002,
+ 0xBCB9,0xC2F6,0x0003,0x0000,0xC2F6,0x0002,0xB4E0,0xD0B2,0x0033,0x0015,0x0008,0x0004,0x0001,0x0000,0xD6AC,0x0001,
+ 0x0001,0xC4DC,0x0007,0x0003,0x0000,0xEBDD,0x0002,0xB0B7,0xEBEF,0x0003,0x0000,0xD0D8,0x0000,0xBDBA,0x000F,0x0008,
+ 0x0004,0x0002,0xEBD8,0xB8EC,0x0002,0xEBDC,0xEBD7,0x0004,0x0002,0xD2C8,0xBFE8,0x0001,0xEBD9,0x0008,0x0004,0x0002,
+ 0xE6C0,0xEBD6,0x0002,0xEBCD,0xEBCC,0x0004,0x0002,0xEBCB,0xEBCA,0x0001,0xF1E3,0x001A,0x000B,0x0006,0x0003,0x0000,
+ 0xD8B7,0x0001,0xBAFA,0x0001,0x0002,0xB0FB,0xEBD5,0x0008,0x0004,0x0002,0xCAA4,0xEBCE,0x0002,0xC5DF,0xEBD1,0x0003,
+ 0x0001,0xEBD3,0x0002,0xC5D6,0xB7F4,0x0009,0x0004,0x0001,0x0001,0xD6AB,0x0001,0x0002,0xCCA5,0xEBD2,0x0007,0x0003,
+ 0x0000,0xB1B3,0x0002,0xEBD4,0xB2B2,0x0001,0x0000,0xB5A8,0x0065,0x0033,0x001A,0x0010,0x0008,0x0004,0x0002,0xEBD0,
+ 0xCEB8,0x0002,0xEBCF,0xD0B2,0x0004,0x0002,0xD5CD,0xD6D7,0x0002,0xC9F6,0xEBC4,0x0006,0x0003,0x0000,0xEBC2,0x0000,
+ 0xB7CE,0x0000,0x0001,0xEBC9,0x000E,0x0007,0x0003,0x0000,0xEBC8,0x0002,0xD3FD,0xEBC5,0x0003,0x0001,0xBFCF,0x0002,
+ 0xB0B9,0xEBC7,0x0007,0x0003,0x0001,0xEBC6,0x0002,0xB7BE,0xBCE7,0x0001,0x0001,0xB7CA,0x001C,0x000E,0x0007,0x0003,
+ 0x0000,0xB7F4,0x0002,0xD6AB,0xB9C9,0x0004,0x0002,0xB3A6,0xEBBF,0x0001,0xB8CE,0x0007,0x0004,0x0002,0xEBC0,0xB8D8,
+ 0x0000,0xB6C7,0x0003,0x0000,0xD6E2,0x0002,0xD0A4,0xC8CD,0x0008,0x0004,0x0000,0x0001,0xEBC1,0x0000,0x0000,0xB8EC,
+ 0x0007,0x0004,0x0002,0xBCA1,0xC0DF,0x0001,0xC8E2,0x0003,0x0001,0xD5D8,0x0002,0xCBC1,0xCBE0,0x002A,0x0018,0x000D,
+ 0x0005,0x0000,0x0002,0xD2DE,0xCBE0,0x0004,0x0002,0xEDB1,0xEDB2,0x0002,0xC1FB,0xCCFD,0x0004,0x0001,0x0001,0xF1F7,
+ 0x0003,0x0001,0xD6B0,0x0002,0xC4F4,0xF1F9,0x000C,0x0007,0x0003,0x0001,0xCBCA,0x0002,0xC9F9,0xF1FA,0x0000,0x0002,
+ 0xB4CF,0xC1AA,0x0000,0x0001,0x0002,0xB4CF,0xF1F9,0x000F,0x0005,0x0001,0x0001,0x0000,0xCEC5,0x0004,0x0001,0x0000,
+ 0xBEDB,0x0003,0x0000,0xC6B8,0x0000,0xCAA5,0x000A,0x0006,0x0003,0x0000,0xC1AA,0x0000,0xF1F8,0x0001,0x0001,0xF1F7,
+ 0x0007,0x0004,0x0002,0xD6B0,0xC1FB,0x0000,0xC1C4,0x0001,0x0000,0xF1F6,0x4108,0x287E,0x13B9,0x0A0C,0x0557,0x02EC,
+ 0x0151,0x00AB,0x005E,0x002E,0x0016,0x0009,0x0005,0x0001,0x0002,0xF1F5,0xC4F4,0x0001,0x0000,0xB9A2,0x0006,0x0003,
+ 0x0000,0xB5A2,0x0000,0xB3DC,0x0003,0x0001,0xCBCA,0x0002,0xDEC7,0xD2AE,0x000A,0x0006,0x0003,0x0000,0xF1F4,0x0000,
+ 0xB6FA,0x0000,0x0000,0xF1F2,0x0006,0x0003,0x0001,0xF1EF,0x0001,0xC5D5,0x0004,0x0002,0xF1F0,0xF1F1,0x0002,0xF1EF,
+ 0xF1EE,0x0017,0x000B,0x0006,0x0003,0x0000,0xF1ED,0x0001,0xF1EC,0x0000,0x0002,0xB3FA,0xF1EB,0x0004,0x0000,0x0001,
+ 0xF1EA,0x0004,0x0002,0xB0D2,0xD4C5,0x0002,0xBAC4,0xF1E9,0x000E,0x0007,0x0004,0x0002,0xB8FB,0xF1E8,0x0001,0xF1E7,
+ 0x0004,0x0002,0xD7A8,0xC4CD,0x0000,0xC4CD,0x0007,0x0004,0x0002,0xCBA3,0xB6F8,0x0000,0xF1F3,0x0001,0x0001,0xEAC8,
+ 0x002B,0x0016,0x000C,0x0007,0x0004,0x0002,0xD5DF,0xEBA3,0x0000,0xBFBC,0x0000,0x0002,0xC0CF,0xD2AB,0x0006,0x0003,
+ 0x0001,0xD2ED,0x0000,0xB7AD,0x0000,0x0000,0xC7CC,0x000B,0x0004,0x0001,0x0000,0xF4E8,0x0004,0x0002,0xB0BF,0xBAB2,
+ 0x0001,0xF4E7,0x0004,0x0001,0x0000,0xCDE6,0x0003,0x0000,0xF4E6,0x0001,0xF4E5,0x0010,0x000B,0x0004,0x0000,0x0000,
+ 0xF4E3,0x0004,0x0002,0xF4E4,0xB4E4,0x0000,0xB5D4,0x0001,0x0000,0x0001,0xC7CC,0x000B,0x0007,0x0004,0x0002,0xF4E2,
+ 0xCFE8,0x0001,0xCFB0,0x0001,0x0001,0xF4E1,0x0000,0x0003,0x0001,0xD2EE,0x0001,0xF1B4,0x0053,0x0028,0x0016,0x000A,
+ 0x0004,0x0000,0x0000,0xB3E1,0x0003,0x0000,0xCECC,0x0000,0xF4E0,0x0005,0x0000,0x0002,0xD3F0,0xE5F1,0x0004,0x0002,
+ 0xB8FE,0xD9FA,0x0001,0xEBFE,0x000A,0x0004,0x0001,0x0001,0xF4CB,0x0003,0x0001,0xF4CA,0x0000,0xF4C9,0x0001,0x0004,
+ 0x0002,0xD2E5,0xCFDB,0x0000,0xF4C8,0x0015,0x000E,0x0007,0x0004,0x0002,0xF4C7,0xC8BA,0x0001,0xC8DE,0x0003,0x0000,
+ 0xCFDB,0x0002,0xF4C7,0xD0DF,0x0000,0x0003,0x0000,0xF4C6,0x0001,0xC1E7,0x0008,0x0004,0x0000,0x0001,0xB8E1,0x0001,
+ 0x0001,0xC3C0,0x0008,0x0004,0x0002,0xB4EF,0xC7BC,0x0002,0xD8C2,0xD1F2,0x0003,0x0001,0xEEBF,0x0001,0xEEBC,0x002A,
+ 0x0011,0x000A,0x0004,0x0000,0x0000,0xC2DE,0x0003,0x0000,0xEEBF,0x0001,0xEEC0,0x0001,0x0003,0x0000,0xEEBE,0x0000,
+ 0xB0D5,0x000E,0x0007,0x0004,0x0002,0xC2EE,0xEEBC,0x0001,0xCAF0,0x0004,0x0002,0xEEBD,0xB7A3,0x0001,0xD6C3,0x0004,
+ 0x0001,0x0001,0xD7EF,0x0004,0x0002,0xD5D6,0xEEBB,0x0001,0xEEB7,0x0015,0x000B,0x0005,0x0001,0x0002,0xB9D2,0xB0D5,
+ 0x0003,0x0000,0xEEB8,0x0000,0xEEB9,0x0004,0x0001,0x0001,0xB7A3,0x0003,0x0001,0xEEB7,0x0000,0xC2DE,0x000A,0x0005,
+ 0x0000,0x0002,0xBAB1,0xD8E8,0x0000,0x0002,0xCDF8,0xB9DE,0x0006,0x0003,0x0001,0xF3BF,0x0000,0xCECD,0x0000,0x0001,
+ 0xCCB3,0x00ED,0x0071,0x0034,0x0014,0x0009,0x0005,0x0002,0xF3C1,0x0000,0xF3C0,0x0000,0x0000,0xF3BF,0x0005,0x0000,
+ 0x0002,0xC6BF,0xB2A7,0x0003,0x0000,0xC8B1,0x0000,0xB8D7,0x0010,0x0008,0x0004,0x0002,0xF3BE,0xE7DA,0x0002,0xBDC9,
+ 0xE7D9,0x0004,0x0002,0xE7D8,0xE7D7,0x0002,0xE7D6,0xE7D5,0x0008,0x0004,0x0002,0xC9C9,0xE7D4,0x0002,0xE7D3,0xE7D2,
+ 0x0004,0x0002,0xE7D1,0xCBF5,0x0002,0xD3A7,0xE7D0,0x001F,0x0010,0x0008,0x0004,0x0002,0xE7CF,0xE7CE,0x0002,0xE7CD,
+ 0xE7CC,0x0004,0x0002,0xE7CB,0xE7CA,0x0002,0xB2F8,0xE7C9,0x0007,0x0003,0x0001,0xB7EC,0x0002,0xE7C7,0xE7C8,0x0004,
+ 0x0002,0xB8BF,0xE7C6,0x0002,0xD4B5,0xE7C5,0x000F,0x0008,0x0004,0x0002,0xB1E0,0xC2C6,0x0002,0xB5DE,0xBBBA,0x0004,
+ 0x0002,0xE7C4,0xE7C3,0x0001,0xE7C2,0x0008,0x0004,0x0002,0xB6D0,0xE7B6,0x0002,0xE7C1,0xE7C0,0x0003,0x0001,0xBCA9,
+ 0x0002,0xE7BF,0xE7BE,0x003E,0x001F,0x0010,0x0008,0x0004,0x0002,0xC0C2,0xC3E5,0x0002,0xBCEA,0xE7BD,0x0004,0x0002,
+ 0xE7BC,0xE7BB,0x0002,0xD7BA,0xC2CC,0x0008,0x0004,0x0002,0xE7BA,0xD5C0,0x0002,0xD7DB,0xE7B9,0x0003,0x0000,0xE7B8,
+ 0x0002,0xB3F1,0xB1C1,0x0010,0x0008,0x0004,0x0002,0xE7B7,0xC3E0,0x0002,0xCEAC,0xC9FE,0x0004,0x0002,0xE7B5,0xE7B4,
+ 0x0002,0xB4C2,0xE7B3,0x0007,0x0004,0x0002,0xE7B2,0xD0F8,0x0001,0xE7B1,0x0004,0x0002,0xD0F7,0xBCA8,0x0002,0xE7B0,
+ 0xBCCC,0x001F,0x000F,0x0007,0x0004,0x0002,0xCCD0,0xCBE7,0x0001,0xD0E5,0x0004,0x0002,0xBEEE,0xE7AF,0x0002,0xE7AE,
+ 0xCDB3,0x0008,0x0004,0x0002,0xBDCA,0xBEF8,0x0002,0xC2E7,0xE7AD,0x0004,0x0002,0xD1A4,0xB8F8,0x0002,0xBBE6,0xE7AC,
+ 0x000F,0x0007,0x0003,0x0001,0xC8C6,0x0002,0xE7AB,0xBDE1,0x0004,0x0002,0xC8DE,0xB0F3,0x0002,0xE7AA,0xBEAD,0x0008,
+ 0x0004,0x0002,0xD2EF,0xC9DC,0x0002,0xE7A9,0xE7A8,0x0004,0x0002,0xB0ED,0xE7A7,0x0002,0xD6D5,0xD6AF,0x006B,0x003B,
+ 0x001D,0x0010,0x0008,0x0004,0x0002,0xCFB8,0xC9F0,0x0002,0xD7E9,0xC1B7,0x0004,0x0002,0xE7A6,0xE7A5,0x0002,0xE7A4,
+ 0xCFDF,0x0005,0x0000,0x0002,0xE7A3,0xC5A6,0x0004,0x0002,0xB7C4,0xCEC6,0x0002,0xD6BD,0xB7D7,0x000F,0x0007,0x0004,
+ 0x0002,0xC2DA,0xD7DD,0x0001,0xC4C9,0x0004,0x0002,0xB8D9,0xC9B4,0x0002,0xE7A2,0xB4BF,0x0007,0x0003,0x0001,0xE7A1,
+ 0x0002,0xCEB3,0xC8D2,0x0004,0x0002,0xBCCD,0xE6FE,0x0002,0xE6FD,0xBCB6,0x0019,0x0010,0x0008,0x0004,0x0002,0xD4BC,
+ 0xE6FC,0x0002,0xCFCB,0xE6FB,0x0004,0x0002,0xBAEC,0xE6FA,0x0002,0xBEC0,0xE6F9,0x0005,0x0001,0x0002,0xC0C2,0xF4EE,
+ 0x0001,0x0000,0xE7DA,0x000B,0x0007,0x0003,0x0000,0xCFCB,0x0002,0xB2C5,0xD3A7,0x0001,0x0001,0xB2F8,0x0006,0x0003,
+ 0x0001,0xC0DB,0x0000,0xD0F8,0x0003,0x0000,0xE6FE,0x0000,0xE7D3,0x0024,0x0010,0x0005,0x0001,0x0000,0x0000,0xD7EB,
+ 0x0007,0x0004,0x0002,0xE7D7,0xE7CD,0x0000,0xBCCC,0x0000,0x0001,0xD2EF,0x0009,0x0004,0x0001,0x0001,0xBDC9,0x0001,
+ 0x0002,0xE7D8,0xE7D9,0x0006,0x0003,0x0001,0xBCEB,0x0001,0xCFB5,0x0000,0x0002,0xBBE6,0xC9FE,0x000F,0x0006,0x0001,
+ 0x0000,0x0002,0xE7C0,0xD0E5,0x0004,0x0000,0x0000,0xC8C6,0x0000,0x0002,0xE7D4,0xB7AD,0x000A,0x0006,0x0003,0x0001,
+ 0xC9C9,0x0000,0xD6AF,0x0000,0x0000,0xE7D5,0x0001,0x0001,0x0002,0xF1DF,0xF4ED,0x012F,0x0093,0x004E,0x002F,0x0017,
+ 0x000B,0x0005,0x0002,0xE7D1,0x0000,0xE7D2,0x0003,0x0000,0xB1C1,0x0000,0xB7B1,0x0006,0x0003,0x0001,0xBCA8,0x0000,
+ 0xD7DC,0x0003,0x0000,0xF7E3,0x0000,0xE7CE,0x000E,0x0008,0x0004,0x0002,0xC2C6,0xF4EA,0x0002,0xE7CF,0xCFCB,0x0003,
+ 0x0001,0xE7D0,0x0000,0xD7DD,0x0006,0x0003,0x0001,0xCBF5,0x0000,0xE7CA,0x0000,0x0000,0xB7EC,0x0010,0x0005,0x0001,
+ 0x0000,0x0000,0xCFD8,0x0007,0x0004,0x0002,0xE7C8,0xE7C9,0x0000,0xE7C7,0x0000,0x0000,0xB8BF,0x0006,0x0001,0x0001,
+ 0x0002,0xE7CC,0xE7A7,0x0001,0x0004,0x0002,0xE7C4,0xE7CB,0x0002,0xE7C6,0xDDD3,0x001F,0x0008,0x0001,0x0001,0x0003,
+ 0x0000,0xD6C2,0x0000,0xE7BE,0x000C,0x0006,0x0003,0x0001,0xE7C2,0x0001,0xC1B7,0x0003,0x0001,0xE7BF,0x0000,0xE7C3,
+ 0x0006,0x0003,0x0000,0xCEB3,0x0001,0xC3E5,0x0001,0x0002,0xBBBA,0xB1E0,0x0018,0x000B,0x0004,0x0000,0x0001,0xE7C1,
+ 0x0003,0x0000,0xD4B5,0x0002,0xE7C5,0xB5DE,0x0006,0x0003,0x0001,0xB6D0,0x0000,0xBCA9,0x0003,0x0001,0xCFDF,0x0002,
+ 0xE7BC,0xBCEA,0x0008,0x0004,0x0000,0x0000,0xE7BD,0x0000,0x0001,0xD0F7,0x0001,0x0000,0x0002,0xE7B3,0xBDF4,0x0051,
+ 0x002D,0x0019,0x000A,0x0006,0x0003,0x0000,0xE7BB,0x0001,0xE7B5,0x0001,0x0000,0xBCA9,0x0007,0x0004,0x0002,0xC3E0,
+ 0xE7B1,0x0000,0xB4C2,0x0004,0x0002,0xD5C0,0xE7B2,0x0002,0xE7B8,0xC2DA,0x000C,0x0005,0x0001,0x0002,0xB2CA,0xD7BA,
+ 0x0003,0x0001,0xCDF8,0x0002,0xB8D9,0xE7BA,0x0000,0x0003,0x0001,0xF4EC,0x0002,0xCEAC,0xE7B7,0x0013,0x000C,0x0005,
+ 0x0000,0x0002,0xD7BC,0xF4EB,0x0004,0x0002,0xE7B9,0xB3F1,0x0001,0xC2CC,0x0000,0x0003,0x0001,0xE7B6,0x0001,0xD7DB,
+ 0x0007,0x0001,0x0003,0x0000,0xBEAD,0x0000,0xC0A6,0x0006,0x0003,0x0000,0xCBE7,0x0000,0xE7A8,0x0001,0x0001,0xE7B0,
+ 0x0027,0x0011,0x000A,0x0004,0x0000,0x0001,0xE7AE,0x0003,0x0000,0xE7AF,0x0000,0xB0F3,0x0001,0x0003,0x0000,0xC3E1,
+ 0x0000,0xBEEE,0x000B,0x0004,0x0000,0x0000,0xF4EA,0x0004,0x0002,0xE7AD,0xCBBF,0x0000,0xCDB3,0x0005,0x0000,0x0002,
+ 0xBAA7,0xD0F5,0x0003,0x0001,0xEBB3,0x0001,0xC8DE,0x0014,0x000A,0x0004,0x0000,0x0001,0xB8F8,0x0003,0x0001,0xD1A4,
+ 0x0000,0xC2E7,0x0006,0x0003,0x0001,0xBDCA,0x0001,0xBDE0,0x0000,0x0000,0xCCD0,0x000A,0x0006,0x0003,0x0001,0xE6FE,
+ 0x0000,0xBEF8,0x0001,0x0001,0xBDE1,0x0000,0x0000,0x0002,0xE7A5,0xE7AC,0x00B3,0x005F,0x002D,0x0019,0x000D,0x0006,
+ 0x0003,0x0001,0xB0ED,0x0001,0xD7E9,0x0004,0x0002,0xCFD2,0xD6D5,0x0001,0xE7A9,0x0006,0x0003,0x0000,0xE7AA,0x0001,
+ 0xE7A8,0x0003,0x0001,0xE7A4,0x0000,0xC9DC,0x0009,0x0001,0x0004,0x0002,0xC9F0,0xE7A5,0x0002,0xE7A6,0xCFB8,0x0007,
+ 0x0004,0x0002,0xC0DB,0xD4FA,0x0001,0xB3F1,0x0000,0x0000,0xD7CF,0x0019,0x000B,0x0004,0x0000,0x0000,0xBDF4,0x0003,
+ 0x0001,0xCBF7,0x0002,0xB7C4,0xCBD8,0x0006,0x0003,0x0000,0xF1C6,0x0001,0xE7A1,0x0004,0x0002,0xB7D7,0xBCB6,0x0002,
+ 0xD6BD,0xBAEA,0x000D,0x0007,0x0003,0x0000,0xC9B4,0x0002,0xE7A2,0xB4BF,0x0003,0x0000,0xE7A3,0x0001,0xC5A6,0x0004,
+ 0x0001,0x0000,0xC4C9,0x0004,0x0002,0xCEC6,0xCEC9,0x0002,0xC8D2,0xE6FD,0x002D,0x0019,0x000F,0x0008,0x0004,0x0002,
+ 0xE6FC,0xE6FA,0x0002,0xBAEC,0xD4BC,0x0004,0x0002,0xD1AD,0xE6FB,0x0001,0xBCCD,0x0004,0x0000,0x0001,0xBEC0,0x0003,
+ 0x0000,0xCFB5,0x0001,0xF4E9,0x000C,0x0006,0x0003,0x0001,0xF4D0,0x0001,0xD9E1,0x0003,0x0001,0xF4CF,0x0001,0xCDC5,
+ 0x0004,0x0000,0x0000,0xC5B4,0x0001,0x0001,0xF4DD,0x0014,0x0008,0x0004,0x0000,0x0000,0xC1B8,0x0001,0x0001,0xBFB7,
+ 0x0008,0x0004,0x0002,0xD4E3,0xB7E0,0x0002,0xF4D6,0xC3D3,0x0001,0x0000,0xB2DA,0x0008,0x0000,0x0004,0x0002,0xF4DC,
+ 0xCCC7,0x0000,0xB8E2,0x0005,0x0001,0x0002,0xF4D9,0xF4D8,0x0003,0x0001,0xBAFD,0x0001,0xF4DA,0x004F,0x0028,0x0015,
+ 0x000A,0x0006,0x0003,0x0000,0xF4D7,0x0000,0xF4DB,0x0001,0x0000,0xF4D6,0x0007,0x0003,0x0001,0xBEAB,0x0002,0xF4D5,
+ 0xF4D4,0x0001,0x0000,0xB4E2,0x000B,0x0004,0x0001,0x0000,0xD4C1,0x0004,0x0002,0xBEAC,0xF4D3,0x0000,0xC1BB,0x0004,
+ 0x0000,0x0001,0xC1B8,0x0000,0x0001,0xB7E0,0x0016,0x000A,0x0005,0x0001,0x0002,0xD6E0,0xD4C1,0x0000,0x0002,0xE2CC,
+ 0xF4D2,0x0008,0x0004,0x0002,0xCBDA,0xF4D1,0x0002,0xF4CF,0xF4D0,0x0001,0x0001,0xD5B3,0x000C,0x0006,0x0003,0x0000,
+ 0xB4D6,0x0000,0xC6C9,0x0003,0x0001,0xC1A3,0x0000,0xF4CE,0x0001,0x0001,0x0000,0xB7DB,0x001F,0x000A,0x0001,0x0005,
+ 0x0001,0x0002,0xD7D1,0xF4CC,0x0000,0x0000,0xC0E0,0x0009,0x0004,0x0001,0x0001,0xD9E1,0x0000,0x0002,0xC3D7,0xD3F5,
+ 0x0006,0x0003,0x0001,0xC2E1,0x0001,0xC0E9,0x0003,0x0001,0xF3FD,0x0000,0xF3D6,0x0010,0x0009,0x0005,0x0001,0x0002,
+ 0xD9DF,0xC7A9,0x0001,0x0001,0xC1FD,0x0000,0x0003,0x0000,0xF4A5,0x0001,0xF3EA,0x0005,0x0001,0x0001,0x0001,0xCCD9,
+ 0x0000,0x0001,0x0002,0xBCAE,0xB3EF,0x0272,0x013D,0x008C,0x0043,0x001D,0x0010,0x0007,0x0001,0x0003,0x0001,0xC0BA,
+ 0x0001,0xF4A5,0x0000,0x0004,0x0002,0xF4A6,0xB2BE,0x0002,0xC1B1,0xC7A9,0x0006,0x0000,0x0000,0x0002,0xF4A4,0xE9DC,
+ 0x0001,0x0003,0x0001,0xF3EF,0x0000,0xF4A2,0x0013,0x000C,0x0006,0x0003,0x0001,0xBBC9,0x0000,0xF4A3,0x0003,0x0001,
+ 0xF3F1,0x0001,0xBCF2,0x0000,0x0003,0x0001,0xF4A1,0x0000,0xF3EC,0x0008,0x0004,0x0001,0x0000,0xF3FD,0x0001,0x0001,
+ 0xCBF2,0x0006,0x0003,0x0001,0xF3FC,0x0001,0xC2A8,0x0000,0x0002,0xF3F9,0xF3FE,0x0022,0x000F,0x0005,0x0000,0x0000,
+ 0x0001,0xB4D8,0x0006,0x0003,0x0000,0xF3E5,0x0000,0xF3FA,0x0000,0x0000,0xF3FB,0x000A,0x0004,0x0000,0x0001,0xC5F1,
+ 0x0003,0x0001,0xF3D9,0x0001,0xC0E9,0x0004,0x0001,0x0000,0xC0BA,0x0001,0x0002,0xF3F8,0xC9B8,0x0018,0x000B,0x0005,
+ 0x0001,0x0002,0xF3F7,0xF3F6,0x0003,0x0000,0xF3C6,0x0001,0xB4DB,0x0006,0x0003,0x0000,0xF3E3,0x0001,0xF3F4,0x0003,
+ 0x0001,0xF3E8,0x0002,0xF3F5,0xB8DD,0x0007,0x0001,0x0003,0x0001,0xC2A8,0x0001,0xF3F1,0x0001,0x0004,0x0002,0xF3F3,
+ 0xF3E6,0x0001,0xD6FE,0x0053,0x0027,0x0011,0x000C,0x0006,0x0003,0x0001,0xC6AA,0x0000,0xD7AD,0x0003,0x0000,0xB7B6,
+ 0x0001,0xF3F2,0x0000,0x0000,0x0000,0xBDDA,0x000A,0x0004,0x0000,0x0000,0xF3E7,0x0003,0x0000,0xF3F0,0x0001,0xCFE4,
+ 0x0004,0x0001,0x0001,0xBCFD,0x0004,0x0002,0xF3E8,0xF3EF,0x0002,0xF3EC,0xC2E1,0x0016,0x000C,0x0007,0x0004,0x0002,
+ 0xF3EA,0xF3E6,0x0000,0xF3E5,0x0001,0x0002,0xF3EE,0xB9DC,0x0006,0x0003,0x0000,0xE9A2,0x0001,0xF3E9,0x0000,0x0000,
+ 0xF3ED,0x000A,0x0006,0x0003,0x0001,0xCBE3,0x0001,0xBBFE,0x0000,0x0000,0xB2AD,0x0008,0x0004,0x0002,0xF3E4,0xF3DD,
+ 0x0002,0xF3F8,0xB9BF,0x0000,0x0001,0xBCE3,0x002B,0x0014,0x0008,0x0000,0x0004,0x0002,0xB2AD,0xB8F6,0x0001,0xF3EB,
+ 0x0006,0x0003,0x0000,0xBCF2,0x0000,0xC7A9,0x0003,0x0001,0xF3E0,0x0001,0xB3EF,0x000E,0x0006,0x0003,0x0001,0xBFEA,
+ 0x0001,0xF3DB,0x0004,0x0002,0xB2DF,0xDCF0,0x0002,0xF3E2,0xF3E3,0x0005,0x0001,0x0002,0xF3DF,0xCBE3,0x0001,0x0001,
+ 0xCDB2,0x0018,0x000C,0x0006,0x0003,0x0001,0xF3C8,0x0000,0xB9DC,0x0003,0x0001,0xC0E9,0x0000,0xF3E1,0x0006,0x0003,
+ 0x0000,0xF3DE,0x0001,0xF3DD,0x0003,0x0001,0xC9B8,0x0000,0xF3D9,0x000D,0x0006,0x0003,0x0000,0xF3D8,0x0000,0xB2DF,
+ 0x0003,0x0000,0xB4F0,0x0002,0xCDB2,0xD6FE,0x0007,0x0004,0x0002,0xBFF0,0xB7A4,0x0001,0xCBF1,0x0004,0x0002,0xF3DC,
+ 0xBDEE,0x0001,0xB5C8,0x0099,0x0055,0x002C,0x0014,0x0009,0x0005,0x0001,0x0002,0xF3CC,0xB1CA,0x0000,0x0000,0xF3DA,
+ 0x0004,0x0001,0x0001,0xF3D6,0x0003,0x0001,0xC1FD,0x0002,0xF3CC,0xBCE3,0x000B,0x0004,0x0000,0x0001,0xF3CD,0x0003,
+ 0x0000,0xB7B6,0x0002,0xF3D5,0xF3C7,0x0006,0x0003,0x0000,0xF3D1,0x0001,0xF3D0,0x0003,0x0001,0xB5DA,0x0002,0xF3CA,
+ 0xF3CE,0x0015,0x000B,0x0006,0x0003,0x0001,0xB1BF,0x0001,0xB7FB,0x0000,0x0002,0xF3D3,0xF3D4,0x0006,0x0003,0x0001,
+ 0xF3D2,0x0001,0xF3D7,0x0001,0x0000,0xB5D1,0x0009,0x0004,0x0000,0x0000,0xF3CF,0x0000,0x0002,0xF3C8,0xB1CA,0x0006,
+ 0x0003,0x0000,0xD0A6,0x0000,0xF3CB,0x0001,0x0002,0xCBF1,0xF3C9,0x0025,0x0016,0x000C,0x0006,0x0003,0x0001,0xF3C5,
+ 0x0001,0xB0CA,0x0003,0x0001,0xF3C7,0x0000,0xF3C6,0x0004,0x0001,0x0000,0xB8CD,0x0003,0x0000,0xF3C4,0x0001,0xF3C3,
+ 0x0007,0x0000,0x0003,0x0000,0xD6F1,0x0001,0xBEBA,0x0004,0x0001,0x0000,0xB6CB,0x0000,0x0000,0xBDDF,0x0012,0x000A,
+ 0x0004,0x0001,0x0001,0xF1B5,0x0003,0x0000,0xCDAF,0x0000,0xBFA2,0x0000,0x0003,0x0001,0xD5C2,0x0002,0xBEB9,0xBEBA,
+ 0x0007,0x0000,0x0003,0x0000,0xD5BE,0x0001,0xCAFA,0x0001,0x0001,0x0002,0xC1A2,0xC7D4,0x0053,0x0025,0x0011,0x0009,
+ 0x0004,0x0001,0x0000,0xF1BC,0x0000,0x0002,0xC7CF,0xB4DC,0x0004,0x0001,0x0000,0xC1FE,0x0001,0x0001,0xBFFA,0x0008,
+ 0x0004,0x0001,0x0001,0xF1C0,0x0001,0x0000,0xF1C1,0x0005,0x0001,0x0002,0xD2A4,0xC7EE,0x0004,0x0002,0xF1C0,0xF1BE,
+ 0x0001,0xCDDD,0x0016,0x000B,0x0007,0x0004,0x0002,0xCED1,0xF1BF,0x0001,0xF1BC,0x0000,0x0000,0xBFFA,0x0006,0x0003,
+ 0x0001,0xF1BD,0x0000,0xBFDF,0x0000,0x0002,0xCED1,0xB4DC,0x000D,0x0007,0x0003,0x0001,0xBEBD,0x0002,0xB4B0,0xBDD1,
+ 0x0003,0x0000,0xF1BB,0x0001,0xD6CF,0x0004,0x0000,0x0000,0xD2A4,0x0003,0x0000,0xC7CF,0x0002,0xBFDF,0xCDDD,0x002C,
+ 0x0017,0x000C,0x0006,0x0003,0x0001,0xF1BA,0x0001,0xF1B9,0x0003,0x0001,0xD5AD,0x0000,0xC7D4,0x0007,0x0004,0x0002,
+ 0xCDBB,0xF1B8,0x0000,0xB4A9,0x0001,0x0001,0xBFD5,0x000D,0x0008,0x0004,0x0002,0xF1B7,0xF1B6,0x0002,0xC7EE,0xBEBF,
+ 0x0000,0x0002,0xCDDA,0xD1A8,0x0004,0x0000,0x0001,0xF0A6,0x0001,0x0000,0xBBF1,0x000D,0x0008,0x0004,0x0000,0x0000,
+ 0xCEC8,0x0001,0x0001,0xBBE0,0x0000,0x0000,0x0000,0xF0A3,0x0005,0x0000,0x0001,0x0000,0xCBEB,0x0006,0x0003,0x0000,
+ 0xF0A3,0x0001,0xD3B1,0x0000,0x0002,0xBBFD,0xF6D5,0x0125,0x0097,0x0046,0x0023,0x0011,0x0005,0x0001,0x0000,0x0000,
+ 0xC4C2,0x0005,0x0001,0x0002,0xB9C8,0xB8E5,0x0003,0x0001,0xBBFC,0x0002,0xBCDA,0xB5BE,0x000A,0x0006,0x0003,0x0001,
+ 0xF0A1,0x0001,0xF0A2,0x0001,0x0001,0xCEC8,0x0004,0x0000,0x0001,0xB3C6,0x0000,0x0000,0xD6D6,0x0010,0x0005,0x0001,
+ 0x0001,0x0001,0xF6D5,0x0005,0x0001,0x0002,0xB3ED,0xD9F7,0x0003,0x0000,0xEFFD,0x0000,0xC0E2,0x000B,0x0007,0x0003,
+ 0x0000,0xD6C9,0x0002,0xC6DA,0xB0DE,0x0001,0x0000,0xEFFE,0x0001,0x0004,0x0002,0xCBB0,0xC9D4,0x0001,0xB3CC,0x0029,
+ 0x0019,0x000D,0x0006,0x0003,0x0000,0xDCE8,0x0000,0xB8D1,0x0004,0x0002,0xEFF9,0xCBB0,0x0001,0xEFFB,0x0006,0x0003,
+ 0x0000,0xEFFC,0x0000,0xCFA1,0x0003,0x0001,0xBBE0,0x0001,0xD2C6,0x0005,0x0000,0x0001,0x0000,0xBDD5,0x0005,0x0001,
+ 0x0002,0xB3C6,0xBBFD,0x0003,0x0001,0xEFF6,0x0001,0xEFF7,0x0012,0x000D,0x0006,0x0003,0x0001,0xD6C8,0x0001,0xD1ED,
+ 0x0003,0x0000,0xC7D8,0x0002,0xB3D3,0xEFF7,0x0000,0x0001,0x0001,0xD7E2,0x0009,0x0004,0x0001,0x0000,0xC3D8,0x0000,
+ 0x0002,0xD6BB,0xEFF5,0x0007,0x0004,0x0002,0xC3EB,0xBFC6,0x0001,0xBAC4,0x0003,0x0001,0xD6D6,0x0001,0xC7EF,0x0046,
+ 0x002D,0x001A,0x000C,0x0006,0x0003,0x0001,0xB1FC,0x0000,0xF4CC,0x0003,0x0000,0xB8D1,0x0001,0xCDBA,0x0007,0x0003,
+ 0x0001,0xCBBD,0x0002,0xD0E3,0xCDBA,0x0004,0x0002,0xBACC,0xC7DD,0x0001,0xC0EB,0x0009,0x0005,0x0000,0x0002,0xD8AE,
+ 0xD3ED,0x0001,0x0001,0xECFC,0x0006,0x0003,0x0001,0xB5BB,0x0000,0xECF2,0x0000,0x0000,0xC0F1,0x000B,0x0000,0x0006,
+ 0x0003,0x0000,0xECF8,0x0001,0xECFB,0x0000,0x0000,0xD3F9,0x0005,0x0000,0x0000,0x0000,0xECFA,0x0004,0x0001,0x0001,
+ 0xB8A3,0x0000,0x0002,0xECF5,0xBBF6,0x0022,0x0012,0x000A,0x0004,0x0000,0x0000,0xECF9,0x0003,0x0001,0xECF8,0x0000,
+ 0xC2BB,0x0000,0x0003,0x0001,0xBDFB,0x0002,0xD9F7,0xC2BB,0x0008,0x0000,0x0003,0x0000,0xECF7,0x0002,0xBBF6,0xB5BB,
+ 0x0004,0x0001,0x0001,0xECF5,0x0000,0x0001,0xBCC0,0x0018,0x0009,0x0005,0x0001,0x0002,0xC6B1,0xECF6,0x0000,0x0001,
+ 0xCFE9,0x0007,0x0003,0x0000,0xECF2,0x0002,0xECF4,0xCBEE,0x0004,0x0002,0xC9F1,0xD7A3,0x0002,0xECEF,0xECEE,0x0000,
+ 0x0006,0x0003,0x0000,0xECF1,0x0001,0xECF3,0x0004,0x0002,0xD7E6,0xC3D8,0x0001,0xECF0,0x0085,0x0047,0x002B,0x001A,
+ 0x000C,0x0007,0x0003,0x0001,0xECED,0x0002,0xC6ED,0xD6BB,0x0000,0x0002,0xECEC,0xB0C0,0x0006,0x0003,0x0001,0xC6EE,
+ 0x0000,0xECEB,0x0004,0x0002,0xC9E7,0xC8D4,0x0002,0xC0F1,0xECEA,0x0008,0x0004,0x0000,0x0000,0xCABE,0x0001,0x0000,
+ 0xEDE7,0x0004,0x0000,0x0001,0xEDC3,0x0001,0x0002,0xB7AF,0xC0F9,0x000F,0x000A,0x0004,0x0000,0x0000,0xEDC2,0x0003,
+ 0x0000,0xBFF3,0x0000,0xEDE5,0x0001,0x0000,0x0000,0xEDE6,0x0008,0x0004,0x0000,0x0001,0xB0AD,0x0001,0x0001,0xEDE4,
+ 0x0001,0x0000,0x0000,0xB4A1,0x0021,0x000D,0x0005,0x0001,0x0000,0x0001,0xEDE2,0x0004,0x0000,0x0001,0xBDB8,0x0000,
+ 0x0001,0xEDCD,0x000A,0x0006,0x0003,0x0000,0xBBC7,0x0001,0xC1D7,0x0001,0x0000,0xEDE3,0x0006,0x0003,0x0000,0xEDE1,
+ 0x0001,0xEDB6,0x0001,0x0000,0xEDE0,0x000A,0x0000,0x0005,0x0001,0x0002,0xC4A5,0xEDD3,0x0001,0x0001,0xEDD7,0x000B,
+ 0x0005,0x0000,0x0002,0xD7A9,0xEDDE,0x0003,0x0001,0xBFC4,0x0000,0xEDDD,0x0004,0x0001,0x0000,0xC5CD,0x0001,0x0001,
+ 0xB4E8,0x0058,0x0028,0x0013,0x0009,0x0005,0x0000,0x0002,0xC0DA,0xEDDF,0x0000,0x0001,0xB0F5,0x0004,0x0000,0x0001,
+ 0xB4C5,0x0003,0x0000,0xC4EB,0x0000,0xC2EB,0x000A,0x0005,0x0000,0x0002,0xC8B7,0xEDDB,0x0001,0x0002,0xB2EA,0xCCBC,
+ 0x0007,0x0004,0x0002,0xEDDA,0xBCEE,0x0000,0xC5F6,0x0000,0x0001,0xEDB8,0x001A,0x000D,0x0007,0x0004,0x0002,0xD5E8,
+ 0xCBB6,0x0001,0xB1CC,0x0003,0x0001,0xEDDC,0x0001,0xEDD9,0x0006,0x0003,0x0001,0xEDD8,0x0001,0xB5FA,0x0003,0x0000,
+ 0xD1D2,0x0002,0xEDD7,0xEDD3,0x000B,0x0007,0x0003,0x0000,0xEDD5,0x0002,0xB5E2,0xCDEB,0x0001,0x0001,0xEDD4,0x0004,
+ 0x0000,0x0001,0xB1AE,0x0004,0x0002,0xCBE9,0xB0AD,0x0000,0xC2B5,0x001D,0x000C,0x0007,0x0000,0x0003,0x0001,0xB5EF,
+ 0x0001,0xEDD6,0x0001,0x0001,0x0000,0xC5F0,0x0005,0x0000,0x0001,0x0001,0xBCEF,0x0004,0x0001,0x0001,0xD1E2,0x0004,
+ 0x0002,0xC8B7,0xEDCB,0x0002,0xD3B2,0xC1F2,0x000F,0x000A,0x0006,0x0003,0x0000,0xEDD2,0x0000,0xEDBA,0x0001,0x0000,
+ 0xEDCC,0x0001,0x0000,0x0001,0xCFF5,0x0009,0x0004,0x0001,0x0001,0xEDCD,0x0000,0x0002,0xEDCC,0xCBB6,0x0006,0x0003,
+ 0x0000,0xCEF8,0x0000,0xEDCF,0x0003,0x0000,0xEDCA,0x0000,0xEDD1,0x0506,0x027E,0x0135,0x00A9,0x005A,0x002C,0x0013,
+ 0x0007,0x0001,0x0003,0x0000,0xEDD0,0x0000,0xB9E8,0x0006,0x0003,0x0000,0xD6EC,0x0001,0xB4A1,0x0003,0x0001,0xC0F9,
+ 0x0001,0xEDC5,0x000E,0x0008,0x0004,0x0002,0xEDC3,0xEDC2,0x0002,0xEDC1,0xD4D2,0x0003,0x0000,0xC9E9,0x0001,0xC6C6,
+ 0x0006,0x0003,0x0001,0xC5DA,0x0001,0xC5E9,0x0001,0x0002,0xEDBE,0xEDC7,0x0016,0x000B,0x0004,0x0001,0x0000,0xEDC9,
+ 0x0004,0x0002,0xD5E8,0xEDCE,0x0000,0xEDC6,0x0004,0x0000,0x0000,0xEDC8,0x0003,0x0000,0xEDC4,0x0002,0xEDC0,0xEDBF,
+ 0x000D,0x0006,0x0003,0x0001,0xD1E2,0x0001,0xEDBB,0x0004,0x0002,0xEDBA,0xD7A9,0x0001,0xD1D0,0x0006,0x0003,0x0001,
+ 0xC5F8,0x0000,0xEDBC,0x0001,0x0002,0xBFB3,0xC6F6,0x002A,0x0016,0x0008,0x0004,0x0001,0x0000,0xEDB9,0x0001,0x0000,
+ 0xC0F7,0x0007,0x0003,0x0001,0xC9B0,0x0002,0xC2EB,0xEDB8,0x0004,0x0002,0xBFF3,0xB7AF,0x0000,0xCEF9,0x0008,0x0004,
+ 0x0001,0x0001,0xEDB7,0x0000,0x0001,0xEDB6,0x0004,0x0000,0x0000,0xCAAF,0x0004,0x0002,0xBDC3,0xB0AB,0x0002,0xB6CC,
+ 0xEFF3,0x0017,0x000C,0x0006,0x0003,0x0000,0xBDC3,0x0000,0xBED8,0x0003,0x0000,0xEFF2,0x0000,0xD6AA,0x0005,0x0000,
+ 0x0002,0xD2D3,0xCAB8,0x0003,0x0001,0xDADC,0x0001,0xF1E6,0x0009,0x0005,0x0000,0x0002,0xC3AC,0xD6F5,0x0000,0x0000,
+ 0xB4A3,0x0001,0x0001,0x0000,0xDBC7,0x0045,0x0020,0x000D,0x0005,0x0001,0x0000,0x0000,0xC3C9,0x0001,0x0003,0x0000,
+ 0xF6C4,0x0002,0xEEAD,0xEDFA,0x0008,0x0004,0x0000,0x0000,0xD5B0,0x0001,0x0000,0xEEAC,0x0006,0x0003,0x0000,0xCDAB,
+ 0x0001,0xEEAB,0x0001,0x0002,0xC1CB,0xCBB2,0x0017,0x000C,0x0006,0x0003,0x0001,0xB5C9,0x0000,0xD6F5,0x0003,0x0000,
+ 0xC7C6,0x0000,0xC6B3,0x0006,0x0003,0x0001,0xDEAB,0x0001,0xEEAA,0x0000,0x0002,0xEEA9,0xC2F7,0x0001,0x0006,0x0003,
+ 0x0001,0xC2F7,0x0000,0xEEA8,0x0003,0x0001,0xCFB9,0x0002,0xEEA4,0xEEA7,0x001D,0x0013,0x0008,0x0001,0x0003,0x0000,
+ 0xC3D0,0x0002,0xB3F2,0xC3E9,0x0004,0x0001,0x0001,0xEEA6,0x0004,0x0002,0xEEA3,0xD8BA,0x0000,0xEEA5,0x0005,0x0000,
+ 0x0001,0x0000,0xB6C3,0x0001,0x0001,0x0001,0xB2C7,0x001B,0x000D,0x0007,0x0004,0x0002,0xBDDE,0xD8BA,0x0001,0xEDFE,
+ 0x0003,0x0001,0xC4C0,0x0000,0xEEA2,0x0008,0x0004,0x0002,0xB6BD,0xEEA1,0x0002,0xCBAF,0xBEEC,0x0003,0x0001,0xEDF9,
+ 0x0001,0xD5F6,0x0006,0x0000,0x0000,0x0002,0xBEA6,0xEDFD,0x0005,0x0001,0x0002,0xEDFA,0xEDF9,0x0000,0x0000,0xC0A7,
+ 0x00AC,0x004C,0x002D,0x0014,0x0007,0x0003,0x0000,0xB6ED,0x0000,0x0001,0xEDFB,0x0007,0x0004,0x0002,0xC7C6,0xEDFC,
+ 0x0001,0xD5F6,0x0003,0x0000,0xD7C5,0x0000,0xD6DA,0x000F,0x0007,0x0003,0x0000,0xD1DB,0x0002,0xCCF7,0xEBDE,0x0004,
+ 0x0002,0xEDF8,0xBEEC,0x0002,0xBFF4,0xEDF7,0x0004,0x0000,0x0000,0xD1A3,0x0003,0x0001,0xC3D0,0x0001,0xEDF5,0x0014,
+ 0x000B,0x0004,0x0001,0x0001,0xD1A3,0x0003,0x0000,0xD5A3,0x0002,0xEDF6,0xEDF6,0x0004,0x0001,0x0000,0xEDF3,0x0000,
+ 0x0002,0xC3DF,0xD5E6,0x0006,0x0000,0x0001,0x0002,0xEDF2,0xEDF4,0x0001,0x0001,0x0001,0xEDEE,0x002E,0x0016,0x000C,
+ 0x0007,0x0003,0x0001,0xBFB4,0x0002,0xEBA3,0xC3BC,0x0000,0x0002,0xEDF1,0xEDF0,0x0006,0x0003,0x0000,0xEDED,0x0001,
+ 0xCAA1,0x0001,0x0000,0xB6DC,0x000A,0x0006,0x0003,0x0000,0xC5CE,0x0001,0xEDEF,0x0000,0x0000,0xCFE0,0x0008,0x0004,
+ 0x0002,0xD6B1,0xC3A4,0x0002,0xC3A4,0xEDEC,0x0003,0x0001,0xB6A2,0x0000,0xC4BF,0x0017,0x000A,0x0004,0x0001,0x0000,
+ 0xB5B4,0x0003,0x0001,0xC2AC,0x0001,0xEEC2,0x0007,0x0004,0x0002,0xC5CC,0xBCE0,0x0001,0xBEA1,0x0003,0x0001,0xC3CB,
+ 0x0000,0xD5B5,0x000C,0x0005,0x0000,0x0002,0xB5C1,0xCAA2,0x0004,0x0002,0xC5CC,0xB5C1,0x0000,0xB8C7,0x0007,0x0003,
+ 0x0000,0xBFF8,0x0002,0xBAD0,0xBCE0,0x0004,0x0002,0xD1CE,0xD5B5,0x0002,0xB0BB,0xEEC1,0x0043,0x002A,0x0015,0x000B,
+ 0x0004,0x0001,0x0000,0xD2E6,0x0003,0x0000,0xD3AF,0x0002,0xC5E8,0xD6D1,0x0006,0x0003,0x0001,0xB1AD,0x0000,0xD3DB,
+ 0x0000,0x0001,0xC3F3,0x0008,0x0004,0x0001,0x0000,0xD6E5,0x0000,0x0000,0xF1E4,0x0007,0x0003,0x0000,0xF1E5,0x0002,
+ 0xF1E4,0xD6E5,0x0003,0x0000,0xF0E5,0x0000,0xC6A4,0x0006,0x0001,0x0000,0x0000,0x0000,0xF0AB,0x0009,0x0005,0x0001,
+ 0x0002,0xB0A8,0xCEFA,0x0001,0x0000,0xCDEE,0x0006,0x0003,0x0001,0xF0A9,0x0001,0xB0A8,0x0001,0x0000,0xF0A8,0x002D,
+ 0x0017,0x000B,0x0004,0x0000,0x0001,0xB8DE,0x0004,0x0002,0xF0A7,0xBBCA,0x0000,0xBDD4,0x0007,0x0003,0x0000,0xB5C4,
+ 0x0002,0xD4ED,0xD4ED,0x0001,0x0002,0xB0D9,0xB0D7,0x0009,0x0005,0x0000,0x0002,0xB7A2,0xB5C7,0x0000,0x0000,0xB9EF,
+ 0x0005,0x0001,0x0002,0xF1B2,0xCCB1,0x0004,0x0002,0xD3B8,0xF1B3,0x0002,0xF1AB,0xF1A8,0x0019,0x000B,0x0007,0x0004,
+ 0x0002,0xD1A2,0xF1B2,0x0001,0xF1AE,0x0001,0x0001,0xD6A2,0x0008,0x0004,0x0002,0xF0DC,0xD1A2,0x0002,0xD1F7,0xB3D5,
+ 0x0003,0x0001,0xB1F1,0x0000,0xF1AE,0x000A,0x0004,0x0000,0x0000,0xF1B0,0x0003,0x0000,0xF0DD,0x0000,0xF1B1,0x0006,
+ 0x0003,0x0000,0xF1AF,0x0000,0xD3FA,0x0001,0x0001,0xF1AD,0x0169,0x00BA,0x005C,0x0029,0x0016,0x000A,0x0003,0x0000,
+ 0xB0A9,0x0003,0x0000,0xF0F7,0x0002,0xF0EF,0xF0EC,0x0005,0x0001,0x0002,0xF1AA,0xC1C6,0x0003,0x0001,0xF1A5,0x0002,
+ 0xF1A8,0xF1AB,0x0008,0x0004,0x0000,0x0001,0xF1A2,0x0000,0x0001,0xC8B3,0x0007,0x0004,0x0002,0xF1A9,0xD5CE,0x0000,
+ 0xF1AC,0x0000,0x0001,0xF1A7,0x001D,0x000E,0x0007,0x0003,0x0000,0xF1A6,0x0002,0xCCB1,0xB1F1,0x0003,0x0000,0xB4F1,
+ 0x0002,0xC5B1,0xCADD,0x0007,0x0004,0x0002,0xF0FB,0xC1F6,0x0001,0xF1A3,0x0004,0x0002,0xB4AF,0xF1A4,0x0002,0xCEC1,
+ 0xF0F9,0x000C,0x0004,0x0001,0x0000,0xF1A1,0x0004,0x0002,0xF0FE,0xF0FC,0x0002,0xF0F9,0xE0B3,0x0006,0x0003,0x0000,
+ 0xF0FD,0x0000,0xBBBE,0x0000,0x0001,0xF0F5,0x0031,0x001B,0x000D,0x0008,0x0004,0x0002,0xD1F1,0xF0F8,0x0002,0xB7E8,
+ 0xF0FA,0x0000,0x0002,0xD3FA,0xF1A1,0x0006,0x0003,0x0000,0xF0F7,0x0000,0xF0F1,0x0004,0x0002,0xB4E1,0xF0F6,0x0002,
+ 0xF0F4,0xF0E2,0x000A,0x0006,0x0003,0x0001,0xF0F3,0x0001,0xB1D4,0x0000,0x0000,0xB1D4,0x0007,0x0003,0x0001,0xB3D5,
+ 0x0002,0xC2E9,0xC2E9,0x0000,0x0002,0xF0F2,0xCCB5,0x0017,0x000C,0x0005,0x0001,0x0002,0xF0EF,0xBBBE,0x0003,0x0001,
+ 0xF0EC,0x0002,0xF0F0,0xF0ED,0x0007,0x0003,0x0001,0xF0EE,0x0002,0xF0EB,0xC1A1,0x0001,0x0001,0xC6A6,0x000B,0x0004,
+ 0x0001,0x0000,0xCDB4,0x0004,0x0002,0xBEB7,0xB6BB,0x0001,0xF0E9,0x0007,0x0004,0x0002,0xBADB,0xD6CC,0x0001,0xD1F7,
+ 0x0000,0x0001,0xBBD7,0x0065,0x0037,0x001D,0x000E,0x0007,0x0004,0x0002,0xF0EA,0xB6B2,0x0001,0xC8AC,0x0004,0x0002,
+ 0xBEB7,0xD3B8,0x0000,0xD6A2,0x0008,0x0004,0x0002,0xB2A1,0xF0E4,0x0002,0xF0E7,0xF0E8,0x0003,0x0000,0xDAE7,0x0002,
+ 0xF0F2,0xBCB2,0x000A,0x0005,0x0000,0x0002,0xBED2,0xCCDB,0x0000,0x0002,0xD5EE,0xF0E3,0x0008,0x0004,0x0002,0xB4C3,
+ 0xF0E2,0x0002,0xF0E1,0xC6A3,0x0004,0x0002,0xF0E5,0xF0E6,0x0002,0xB7E8,0xB4AF,0x0015,0x0007,0x0000,0x0003,0x0001,
+ 0xF0DF,0x0000,0xD2DF,0x0007,0x0004,0x0002,0xBDEA,0xB0CC,0x0000,0xF0E0,0x0004,0x0002,0xD1F1,0xF0DD,0x0000,0xC5B1,
+ 0x000D,0x0006,0x0003,0x0000,0xF0DE,0x0001,0xBECE,0x0003,0x0000,0xB8ED,0x0002,0xC1C6,0xF0DC,0x0006,0x0003,0x0001,
+ 0xF0DB,0x0001,0xF0DA,0x0003,0x0000,0xD2C9,0x0000,0xCAE8,0x0024,0x0012,0x000A,0x0005,0x0001,0x0002,0xF1E2,0xB5FE,
+ 0x0001,0x0002,0xB3EB,0xBDAE,0x0004,0x0001,0x0000,0xEEB6,0x0001,0x0000,0xE7DC,0x0008,0x0001,0x0004,0x0002,0xEEB5,
+ 0xBBFB,0x0001,0xB5B1,0x0006,0x0003,0x0001,0xB3EB,0x0001,0xEEB4,0x0000,0x0001,0xD2EC,0x0011,0x0009,0x0005,0x0001,
+ 0x0002,0xBBAD,0xB7AC,0x0001,0x0001,0xC6E8,0x0000,0x0003,0x0000,0xC2D4,0x0002,0xB4F0,0xB1CF,0x000C,0x0008,0x0004,
+ 0x0002,0xC4B6,0xD0F3,0x0002,0xEEB3,0xDBCE,0x0000,0x0000,0xC1F4,0x0004,0x0000,0x0001,0xC5CF,0x0001,0x0002,0xCEB7,
+ 0xEEB0,0x00A2,0x005D,0x002D,0x0014,0x000A,0x0006,0x0003,0x0001,0xBDE7,0x0000,0xEEB1,0x0000,0x0001,0xEEB2,0x0004,
+ 0x0000,0x0000,0xB3A9,0x0003,0x0001,0xEEAF,0x0001,0xE7DE,0x000B,0x0005,0x0001,0x0002,0xBBAD,0xEEAE,0x0003,0x0001,
+ 0xB5E9,0x0000,0xC4D0,0x0007,0x0003,0x0000,0xB5E7,0x0002,0xC9EA,0xBCD7,0x0004,0x0002,0xD3C9,0xCCEF,0x0000,0xE5B8,
+ 0x0019,0x000F,0x0008,0x0004,0x0002,0xB1C2,0xF0AE,0x0002,0xB8A6,0xBDC7,0x0004,0x0002,0xCBA6,0xD3C3,0x0001,0xCBD5,
+ 0x0006,0x0003,0x0000,0xC9FB,0x0001,0xB2FA,0x0001,0x0000,0xC9FA,0x000B,0x0006,0x0003,0x0001,0xCCF0,0x0001,0xC9F5,
+ 0x0000,0x0002,0xDFB0,0xB8CA,0x0006,0x0003,0x0000,0xCECD,0x0000,0xEAB6,0x0003,0x0000,0xEAB5,0x0000,0xEAB4,0x001D,
+ 0x000E,0x0006,0x0000,0x0000,0x0002,0xDDF9,0xEAB1,0x0004,0x0000,0x0001,0xD5E7,0x0001,0x0000,0xEAB3,0x0006,0x0001,
+ 0x0001,0x0002,0xB4C9,0xC6BF,0x0004,0x0000,0x0001,0xEAB2,0x0001,0x0002,0xEAB1,0xCECD,0x0014,0x0007,0x0001,0x0003,
+ 0x0001,0xB8D7,0x0001,0xCDDF,0x0007,0x0003,0x0001,0xC8BF,0x0002,0xB0EA,0xC6B0,0x0003,0x0001,0xF0AD,0x0001,0xF0AC,
+ 0x000A,0x0006,0x0003,0x0001,0xB9CF,0x0001,0xE8B6,0x0001,0x0001,0xCFE2,0x0006,0x0003,0x0001,0xE8AC,0x0001,0xE8B6,
+ 0x0001,0x0000,0xE7E7,0x0033,0x0016,0x000A,0x0005,0x0000,0x0001,0x0001,0xC7ED,0x0001,0x0001,0x0000,0xE8AF,0x0007,
+ 0x0000,0x0003,0x0000,0xE7F4,0x0001,0xE8B7,0x0001,0x0000,0x0001,0xBBB7,0x0011,0x0009,0x0001,0x0004,0x0002,0xE8B3,
+ 0xE8B2,0x0002,0xE8B5,0xE8A8,0x0004,0x0001,0x0000,0xE7E1,0x0001,0x0001,0xE8B1,0x0005,0x0000,0x0000,0x0001,0xE8AB,
+ 0x0001,0x0003,0x0001,0xE8B4,0x0001,0xE8AC,0x0023,0x0015,0x000A,0x0004,0x0001,0x0000,0xE8B0,0x0003,0x0000,0xE7F6,
+ 0x0000,0xE8AF,0x0004,0x0001,0x0000,0xC1A7,0x0004,0x0002,0xE8AE,0xE8AD,0x0001,0xE8AA,0x0006,0x0001,0x0001,0x0002,
+ 0xE8A8,0xD1FE,0x0001,0x0004,0x0002,0xD5F2,0xB9E5,0x0000,0xC0C5,0x0015,0x000A,0x0006,0x0003,0x0000,0xE8A9,0x0001,
+ 0xC2EA,0x0000,0x0000,0xD3A8,0x0006,0x0003,0x0001,0xD1FE,0x0000,0xCBF6,0x0001,0x0002,0xC9AA,0xC8F0,0x000D,0x0007,
+ 0x0003,0x0001,0xE8A4,0x0002,0xE7F8,0xBAF7,0x0003,0x0000,0xE8A7,0x0000,0xE8A5,0x0000,0x0000,0x0000,0xE8A6,0x0267,
+ 0x0123,0x0087,0x0045,0x0022,0x000C,0x0005,0x0000,0x0001,0x0001,0xE7E2,0x0001,0x0003,0x0001,0xE8A3,0x0001,0xE7F5,
+ 0x0008,0x0004,0x0001,0x0000,0xC7ED,0x0000,0x0000,0xB7A9,0x0008,0x0004,0x0002,0xC5C3,0xC5FD,0x0002,0xC7D9,0xC1D5,
+ 0x0003,0x0001,0xB5F1,0x0000,0xE7FC,0x0015,0x000C,0x0006,0x0003,0x0000,0xE7FD,0x0000,0xE7FE,0x0003,0x0000,0xE7F7,
+ 0x0000,0xE7FB,0x0005,0x0000,0x0002,0xE7F9,0xE7FA,0x0000,0x0000,0xD7C1,0x0008,0x0004,0x0001,0x0001,0xE8A1,0x0000,
+ 0x0000,0xE8A2,0x0001,0x0001,0x0002,0xCBF6,0xE7F6,0x001E,0x0014,0x0009,0x0004,0x0000,0x0001,0xC1A7,0x0000,0x0002,
+ 0xE7F0,0xC1F0,0x0007,0x0004,0x0002,0xC0ED,0xC0C5,0x0001,0xC7F2,0x0000,0x0001,0xE8AF,0x0005,0x0000,0x0000,0x0000,
+ 0xCFD6,0x0001,0x0000,0x0000,0xE7F5,0x0014,0x000C,0x0005,0x0000,0x0002,0xC5E5,0xB0E0,0x0004,0x0002,0xB9E7,0xE7F1,
+ 0x0001,0xE7F2,0x0004,0x0000,0x0001,0xE7ED,0x0001,0x0000,0xD6E9,0x0008,0x0004,0x0000,0x0000,0xE7F3,0x0000,0x0001,
+ 0xE7EE,0x0001,0x0003,0x0001,0xE7E7,0x0002,0xB7A9,0xE7E5,0x004D,0x0028,0x0012,0x000B,0x0004,0x0000,0x0001,0xD5E4,
+ 0x0004,0x0002,0xC9BA,0xE7EB,0x0000,0xE7EC,0x0001,0x0003,0x0000,0xE7E6,0x0000,0xE7EA,0x000B,0x0005,0x0001,0x0002,
+ 0xB4C3,0xB2A3,0x0003,0x0000,0xE7F4,0x0001,0xE7E8,0x0004,0x0001,0x0001,0xE7E9,0x0003,0x0000,0xC1E1,0x0002,0xCFD6,
+ 0xBBB7,0x0016,0x000C,0x0006,0x0003,0x0000,0xE7E2,0x0001,0xC3B5,0x0003,0x0001,0xCDE6,0x0000,0xE7E5,0x0004,0x0000,
+ 0x0000,0xBEF6,0x0003,0x0000,0xE7E3,0x0001,0xE7E4,0x0005,0x0000,0x0001,0x0001,0xC2EA,0x0006,0x0003,0x0000,0xBEC1,
+ 0x0000,0xEECB,0x0000,0x0001,0xE7E1,0x0030,0x0017,0x000C,0x0006,0x0003,0x0000,0xE7E0,0x0001,0xCDF5,0x0003,0x0001,
+ 0xD3F1,0x0001,0xC2CA,0x0007,0x0004,0x0002,0xD7C8,0xC3EE,0x0000,0xD0FE,0x0001,0x0000,0xE2A4,0x000E,0x0007,0x0003,
+ 0x0000,0xE2B5,0x0002,0xE2A8,0xCFD7,0x0003,0x0000,0xCCA1,0x0002,0xCADE,0xE1EE,0x0004,0x0000,0x0001,0xC1D4,0x0003,
+ 0x0000,0xBBF1,0x0002,0xC4FC,0xE2B4,0x0013,0x000E,0x0008,0x0004,0x0002,0xE2A8,0xCCA1,0x0002,0xE2B3,0xE1FD,0x0003,
+ 0x0000,0xE1F6,0x0000,0xB6C0,0x0001,0x0001,0x0000,0xE2B2,0x0005,0x0001,0x0001,0x0001,0xE2B1,0x0001,0x0003,0x0000,
+ 0xE9E1,0x0000,0xE2AF,0x00A5,0x0056,0x0026,0x000F,0x0006,0x0000,0x0002,0xBDB1,0x0000,0xE2B0,0x0005,0x0001,0x0002,
+ 0xCAA8,0xD3FC,0x0000,0x0000,0xB4F4,0x000C,0x0005,0x0000,0x0002,0xD4B3,0xBBAB,0x0003,0x0000,0xE1F8,0x0002,0xE2AA,
+ 0xE2AD,0x0007,0x0004,0x0002,0xE9E0,0xD3CC,0x0001,0xBAEF,0x0001,0x0000,0xE2AE,0x0017,0x000D,0x0006,0x0003,0x0001,
+ 0xCFD7,0x0001,0xE2AC,0x0004,0x0002,0xC3A8,0xD6ED,0x0000,0xD0C9,0x0004,0x0001,0x0000,0xE2AB,0x0003,0x0001,0xE2A9,
+ 0x0000,0xE2A4,0x000D,0x0007,0x0003,0x0001,0xE2A6,0x0002,0xE2A7,0xB2C2,0x0003,0x0000,0xC3CD,0x0000,0xD5F8,0x0007,
+ 0x0004,0x0002,0xE2A2,0xB2FE,0x0000,0xE2A8,0x0000,0x0002,0xE2A3,0xF7D0,0x002A,0x000F,0x0008,0x0004,0x0000,0x0001,
+ 0xC1D4,0x0000,0x0001,0xE2A5,0x0001,0x0003,0x0000,0xE1FD,0x0000,0xE1FB,0x000D,0x0005,0x0001,0x0002,0xB1B7,0xC0C7,
+ 0x0004,0x0002,0xE2A1,0xE1FE,0x0002,0xCFC1,0xC0EA,0x0006,0x0003,0x0000,0xE1FA,0x0001,0xE1F9,0x0004,0x0002,0xE1FC,
+ 0xE1F8,0x0002,0xD3FC,0xD5F8,0x0013,0x000D,0x0008,0x0004,0x0002,0xE1F6,0xCAA8,0x0002,0xCFC1,0xB6C0,0x0001,0x0002,
+ 0xE1F7,0xE1F5,0x0001,0x0001,0x0002,0xBDC6,0xBADD,0x0008,0x0004,0x0000,0x0001,0xC4FC,0x0001,0x0000,0xBED1,0x0004,
+ 0x0000,0x0000,0xB9B7,0x0003,0x0001,0xE1F4,0x0001,0xBAFC,0x0048,0x0028,0x0017,0x000B,0x0007,0x0003,0x0001,0xE1F2,
+ 0x0002,0xE1F3,0xD0C9,0x0001,0x0001,0xB1B7,0x0004,0x0001,0x0001,0xB5D2,0x0004,0x0002,0xE1F0,0xBFF1,0x0002,0xE1F1,
+ 0xD7B4,0x0006,0x0001,0x0001,0x0002,0xD3CC,0xE1EF,0x0007,0x0004,0x0002,0xE1EE,0xD7B4,0x0001,0xE1ED,0x0001,0x0001,
+ 0xE1EC,0x0010,0x0008,0x0000,0x0003,0x0000,0xB7B8,0x0002,0xE1EB,0xC8AE,0x0004,0x0000,0x0000,0xCEFE,0x0000,0x0001,
+ 0xB6BF,0x0005,0x0000,0x0000,0x0000,0xEAF1,0x0004,0x0000,0x0001,0xDCFD,0x0004,0x0002,0xDBBB,0xEAFB,0x0000,0xEAF8,
+ 0x002C,0x0016,0x000B,0x0006,0x0003,0x0000,0xEAFA,0x0000,0xEAF9,0x0000,0x0002,0xEAF8,0xB6BF,0x0006,0x0003,0x0001,
+ 0xCCD8,0x0001,0xEAF7,0x0001,0x0002,0xC0E7,0xCFAC,0x000D,0x0007,0x0004,0x0002,0xEAF6,0xEAF5,0x0000,0xC7A3,0x0003,
+ 0x0001,0xCEFE,0x0000,0xCCD8,0x0005,0x0001,0x0002,0xC7A3,0xB5D6,0x0000,0x0001,0xC9FC,0x0016,0x0009,0x0005,0x0000,
+ 0x0002,0xEAF4,0xEAF0,0x0001,0x0000,0xCEEF,0x0005,0x0000,0x0002,0xC4C1,0xEAF3,0x0004,0x0002,0xC8CD,0xC0CE,0x0002,
+ 0xC4B5,0xCBFC,0x000D,0x0006,0x0003,0x0000,0xC4B2,0x0000,0xEAF2,0x0003,0x0000,0xC5A3,0x0002,0xD1C0,0xEBB9,0x0004,
+ 0x0000,0x0001,0xEBBB,0x0000,0x0001,0xEBBA,0x011F,0x008B,0x0049,0x002F,0x0015,0x000A,0x0005,0x0002,0xF1BE,0x0001,
+ 0xEBB9,0x0000,0x0002,0xC5C6,0xBCE3,0x0007,0x0004,0x0002,0xB0E6,0xC6AC,0x0000,0xC7BD,0x0001,0x0001,0xBFC2,0x000E,
+ 0x0007,0x0003,0x0001,0xE3DD,0x0002,0xB6FB,0xCBAC,0x0003,0x0001,0xD8B3,0x0002,0xD2AF,0xB5F9,0x0008,0x0004,0x0002,
+ 0xB0D6,0xD2AF,0x0002,0xB8B8,0xBEF4,0x0001,0x0001,0xB0AE,0x0011,0x000C,0x0006,0x0003,0x0000,0xEBBC,0x0001,0xD5F9,
+ 0x0003,0x0000,0xC5C0,0x0000,0xD7A6,0x0000,0x0000,0x0000,0xECE0,0x0000,0x0004,0x0001,0x0001,0xECDF,0x0000,0x0001,
+ 0xC0C3,0x001C,0x000C,0x0007,0x0000,0x0003,0x0000,0xC2AF,0x0001,0xCBB8,0x0000,0x0001,0x0000,0xB1AC,0x000B,0x0004,
+ 0x0001,0x0000,0xECE2,0x0004,0x0002,0xBDFD,0xD1AC,0x0001,0xECDE,0x0001,0x0000,0x0000,0xBBE2,0x0011,0x0009,0x0005,
+ 0x0001,0x0002,0xDBC6,0xD6F2,0x0000,0x0000,0xBBD9,0x0000,0x0003,0x0001,0xECDD,0x0002,0xB2D3,0xD4EF,0x000B,0x0005,
+ 0x0000,0x0002,0xECDB,0xD3AA,0x0003,0x0000,0xECCB,0x0001,0xCCCC,0x0004,0x0001,0x0001,0xD1E0,0x0003,0x0000,0xECDC,
+ 0x0000,0xC9D5,0x0045,0x0027,0x0015,0x000A,0x0006,0x0003,0x0000,0xC1D7,0x0000,0xC1C7,0x0001,0x0001,0xECC0,0x0004,
+ 0x0000,0x0000,0xB5C6,0x0004,0x0002,0xD1E6,0xC8BC,0x0001,0xECC7,0x0008,0x0004,0x0001,0x0000,0xB3E3,0x0001,0x0001,
+ 0xECE4,0x0004,0x0001,0x0001,0xECD8,0x0003,0x0001,0xECD7,0x0001,0xC8C8,0x000A,0x0005,0x0001,0x0000,0x0000,0xB0BE,
+ 0x0000,0x0000,0x0000,0xECD9,0x0009,0x0005,0x0000,0x0002,0xECDA,0xCAEC,0x0001,0x0001,0xCEF5,0x0005,0x0000,0x0002,
+ 0xECD6,0xECC1,0x0003,0x0000,0xC8DB,0x0000,0xD3AB,0x001F,0x000D,0x0008,0x0004,0x0000,0x0001,0xD1AC,0x0001,0x0000,
+ 0xD0DC,0x0001,0x0000,0x0000,0xCFA8,0x0008,0x0004,0x0001,0x0001,0xC9BF,0x0001,0x0000,0xECD5,0x0004,0x0000,0x0000,
+ 0xECD4,0x0003,0x0001,0xECCE,0x0000,0xECD2,0x0019,0x000A,0x0004,0x0001,0x0000,0xD6F3,0x0003,0x0000,0xECBE,0x0001,
+ 0xB7B3,0x0008,0x0004,0x0002,0xECD0,0xD5D5,0x0002,0xECE3,0xBBC0,0x0004,0x0002,0xC3BA,0xC8E0,0x0000,0xDCE4,0x000D,
+ 0x0007,0x0004,0x0002,0xD5A8,0xDFDC,0x0000,0xC9B7,0x0003,0x0000,0xECCF,0x0001,0xD1CC,0x0004,0x0001,0x0000,0xC5AF,
+ 0x0003,0x0000,0xC9BC,0x0000,0xECBF,0x0086,0x003D,0x001B,0x0012,0x000B,0x0004,0x0001,0x0000,0xBCE5,0x0003,0x0000,
+ 0xBBCD,0x0002,0xECD3,0xC1B6,0x0000,0x0003,0x0001,0xBBD4,0x0001,0xECD1,0x0001,0x0004,0x0001,0x0000,0xC8BB,0x0001,
+ 0x0001,0xECCD,0x000E,0x0006,0x0000,0x0000,0x0002,0xD1E6,0xECCC,0x0004,0x0001,0x0000,0xBDB9,0x0001,0x0001,0xCEDE,
+ 0x0009,0x0004,0x0000,0x0000,0xB4E3,0x0001,0x0002,0xB7D9,0xB1BA,0x0007,0x0003,0x0000,0xECE2,0x0002,0xECCB,0xBBC0,
+ 0x0000,0x0001,0xECCA,0x0021,0x0011,0x0009,0x0004,0x0000,0x0000,0xECC9,0x0001,0x0002,0xBAB8,0xD1C9,0x0004,0x0001,
+ 0x0000,0xE4B8,0x0000,0x0000,0xD1AC,0x0008,0x0004,0x0001,0x0001,0xB7E9,0x0001,0x0001,0xC5EB,0x0004,0x0000,0x0001,
+ 0xCDE9,0x0000,0x0000,0xCCFE,0x0019,0x000E,0x0007,0x0004,0x0002,0xE2D2,0xCFA9,0x0001,0xC8C8,0x0004,0x0002,0xBDFD,
+ 0xCCCC,0x0001,0xBBE2,0x0007,0x0004,0x0002,0xECC7,0xC9D5,0x0000,0xB7B3,0x0000,0x0000,0xBFBE,0x000A,0x0004,0x0000,
+ 0x0001,0xD1CC,0x0003,0x0001,0xD6F2,0x0001,0xC0D3,0x0000,0x0000,0x0000,0xBAE6,0x0054,0x002E,0x0015,0x000A,0x0004,
+ 0x0000,0x0001,0xCEDA,0x0003,0x0001,0xD0DD,0x0000,0xECC8,0x0004,0x0000,0x0000,0xC1D2,0x0003,0x0001,0xCCFE,0x0002,
+ 0xC0C3,0xCBB8,0x000E,0x0006,0x0003,0x0000,0xECC3,0x0001,0xB3E3,0x0004,0x0002,0xC1B6,0xECC2,0x0002,0xCEAA,0xB5E3,
+ 0x0005,0x0000,0x0002,0xD5A8,0xECC4,0x0003,0x0001,0xB1FE,0x0001,0xECC6,0x0011,0x000C,0x0007,0x0003,0x0001,0xBEBC,
+ 0x0002,0xC5DA,0xCCBF,0x0000,0x0002,0xBEE6,0xECC5,0x0001,0x0000,0x0000,0xD5D5,0x000A,0x0004,0x0001,0x0001,0xECC1,
+ 0x0003,0x0000,0xECBF,0x0001,0xD6CB,0x0005,0x0001,0x0002,0xECC0,0xBFBB,0x0003,0x0000,0xC8B2,0x0000,0xB3B4,0x0025,
+ 0x000E,0x0009,0x0004,0x0001,0x0000,0xD1D7,0x0001,0x0002,0xB4B6,0xC2AF,0x0000,0x0001,0x0001,0xEAC1,0x000C,0x0008,
+ 0x0004,0x0002,0xECBE,0xB2D3,0x0002,0xD4D6,0xD4D6,0x0000,0x0000,0xD7C6,0x0007,0x0003,0x0000,0xBEC4,0x0002,0xD4EE,
+ 0xC1E9,0x0000,0x0000,0xBAE6,0x0012,0x000C,0x0007,0x0004,0x0002,0xBBD2,0xB5C6,0x0001,0xC3F0,0x0000,0x0002,0xECE1,
+ 0xBBF0,0x0001,0x0000,0x0002,0xC2D0,0xCDE5,0x0006,0x0000,0x0001,0x0002,0xE5B1,0xE5B0,0x0006,0x0003,0x0000,0xCCB2,
+ 0x0001,0xC0EC,0x0001,0x0001,0xC8F7,0x09EA,0x0541,0x0265,0x011A,0x0087,0x0042,0x001E,0x000F,0x0008,0x0004,0x0001,
+ 0x0000,0xE5B0,0x0000,0x0001,0xB9E0,0x0001,0x0003,0x0001,0xE4DC,0x0000,0xE3E3,0x0005,0x0000,0x0001,0x0001,0xC0BD,
+ 0x0004,0x0000,0x0000,0xE5AE,0x0003,0x0000,0xE5AF,0x0001,0xE4F2,0x000F,0x0005,0x0000,0x0000,0x0001,0xC3D6,0x0006,
+ 0x0003,0x0001,0xE4FE,0x0000,0xE3F1,0x0001,0x0000,0xE5AC,0x000D,0x0006,0x0003,0x0001,0xE4EB,0x0000,0xE4EC,0x0003,
+ 0x0000,0xC1A4,0x0002,0xE5AD,0xE5AB,0x0004,0x0000,0x0001,0xE3F2,0x0000,0x0000,0xB1F4,0x001C,0x0011,0x000A,0x0006,
+ 0x0003,0x0000,0xC6D9,0x0000,0xE4AF,0x0001,0x0000,0xC9F2,0x0000,0x0003,0x0000,0xD0BA,0x0001,0xE4C2,0x0000,0x0004,
+ 0x0001,0x0001,0xC2CB,0x0003,0x0001,0xE3F8,0x0001,0xBDA6,0x0013,0x000E,0x0008,0x0004,0x0002,0xB1F5,0xCEAB,0x0002,
+ 0xE5AA,0xE5A7,0x0003,0x0001,0xBFA3,0x0000,0xC0C4,0x0001,0x0000,0x0001,0xCCCE,0x000C,0x0008,0x0004,0x0002,0xE5A6,
+ 0xE5A9,0x0002,0xBCC3,0xE5A8,0x0001,0x0000,0xC3C9,0x0004,0x0000,0x0001,0xC5A2,0x0003,0x0000,0xCAAA,0x0001,0xB1F4,
+ 0x003D,0x001E,0x000E,0x0005,0x0000,0x0000,0x0000,0xE4FE,0x0004,0x0000,0x0000,0xE5A1,0x0001,0x0002,0xC5A8,0xE5A5,
+ 0x0006,0x0000,0x0000,0x0002,0xD7C7,0xBCA4,0x0006,0x0003,0x0000,0xE5A3,0x0001,0xE5A4,0x0001,0x0000,0xB0C4,0x0011,
+ 0x0007,0x0000,0x0003,0x0000,0xB5ED,0x0001,0xE4AB,0x0006,0x0003,0x0000,0xEDB4,0x0000,0xE5A2,0x0000,0x0001,0xD4F3,
+ 0x0009,0x0005,0x0000,0x0002,0xD4E8,0xE4C5,0x0000,0x0001,0xC0BD,0x0000,0x0001,0x0000,0xBDA7,0x002F,0x0017,0x0009,
+ 0x0004,0x0001,0x0001,0xC5EC,0x0000,0x0002,0xE4F8,0xE4F9,0x0008,0x0004,0x0002,0xE4F7,0xB3BA,0x0002,0xC0D4,0xBDBD,
+ 0x0003,0x0001,0xB3CE,0x0001,0xB3CE,0x000C,0x0006,0x0003,0x0001,0xC9AC,0x0000,0xE4B6,0x0003,0x0001,0xE4FC,0x0001,
+ 0xE4FD,0x0006,0x0003,0x0001,0xE4FA,0x0000,0xE4E4,0x0003,0x0001,0xE4F3,0x0001,0xE4FB,0x0015,0x000B,0x0007,0x0003,
+ 0x0001,0xC0A3,0x0002,0xE4B1,0xB3B1,0x0000,0x0000,0xCCB6,0x0004,0x0001,0x0001,0xC1CA,0x0003,0x0001,0xC8F3,0x0001,
+ 0xE4EA,0x000A,0x0004,0x0001,0x0001,0xC2BA,0x0003,0x0001,0xC7B1,0x0000,0xC7B1,0x0004,0x0000,0x0001,0xC5CB,0x0000,
+ 0x0001,0xBDE0,0x00AD,0x0056,0x0028,0x0011,0x0009,0x0003,0x0000,0xC6C3,0x0003,0x0001,0xCEAB,0x0001,0xE4F2,0x0004,
+ 0x0001,0x0001,0xE4EC,0x0000,0x0000,0xE4EB,0x000C,0x0006,0x0003,0x0001,0xF2A3,0x0001,0xBDAC,0x0003,0x0000,0xD1FA,
+ 0x0000,0xB4DD,0x0004,0x0001,0x0000,0xBDA5,0x0004,0x0002,0xE4F1,0xE4D3,0x0001,0xD5C4,0x001C,0x000E,0x0007,0x0004,
+ 0x0002,0xD5C7,0xCAFE,0x0001,0xE4F0,0x0003,0x0001,0xE4DD,0x0002,0xD7D5,0xC2FE,0x0007,0x0004,0x0002,0xE4F4,0xE4F6,
+ 0x0001,0xB8C9,0x0003,0x0001,0xCDDD,0x0002,0xE4ED,0xC1B0,0x0007,0x0000,0x0003,0x0000,0xBABA,0x0000,0xC4AE,0x0004,
+ 0x0000,0x0000,0xC5BD,0x0003,0x0001,0xE4EE,0x0002,0xD1DD,0xC0EC,0x0025,0x000F,0x0005,0x0000,0x0001,0x0001,0xC2A9,
+ 0x0004,0x0000,0x0001,0xE4F5,0x0003,0x0000,0xC6E1,0x0001,0xBCC5,0x000B,0x0007,0x0004,0x0002,0xC6AF,0xD3E6,0x0001,
+ 0xC2FA,0x0000,0x0000,0xB9F6,0x0007,0x0003,0x0001,0xE4EF,0x0002,0xE4B0,0xC2B1,0x0001,0x0000,0xB5CE,0x0017,0x000A,
+ 0x0006,0x0003,0x0000,0xC9F8,0x0001,0xD6CD,0x0001,0x0000,0xBBA6,0x0006,0x0003,0x0001,0xCCB2,0x0000,0xB1F5,0x0004,
+ 0x0002,0xC2D0,0xC0C4,0x0000,0xC2CB,0x000E,0x0008,0x0004,0x0002,0xE4DE,0xC2FA,0x0002,0xE4DC,0xE4D9,0x0003,0x0000,
+ 0xD6CD,0x0000,0xE0C6,0x0006,0x0003,0x0000,0xB9F6,0x0001,0xE4E4,0x0003,0x0001,0xEBF8,0x0002,0xCCCF,0xD7D2,0x005E,
+ 0x0032,0x001A,0x000D,0x0006,0x0003,0x0001,0xBBAC,0x0001,0xE4E6,0x0003,0x0000,0xDCFE,0x0002,0xB5D3,0xD7CC,0x0007,
+ 0x0003,0x0000,0xC6FB,0x0002,0xB8E4,0xB5E1,0x0003,0x0001,0xC3F0,0x0000,0xB2D7,0x000C,0x0005,0x0000,0x0002,0xE4E8,
+ 0xB3FC,0x0003,0x0001,0xE4E1,0x0002,0xCAAA,0xE4E2,0x0006,0x0003,0x0000,0xC4E7,0x0001,0xE4E3,0x0003,0x0000,0xC8DC,
+ 0x0000,0xE4E5,0x0015,0x000B,0x0007,0x0004,0x0002,0xE4D1,0xE4DA,0x0001,0xCBDD,0x0001,0x0001,0xCEC2,0x0006,0x0003,
+ 0x0000,0xCFAA,0x0001,0xE4E0,0x0000,0x0001,0xE4DF,0x000D,0x0006,0x0003,0x0000,0xD2E7,0x0001,0xE4E9,0x0004,0x0002,
+ 0xE4D1,0xB9B5,0x0000,0xC1EF,0x0004,0x0001,0x0000,0xE4DB,0x0003,0x0000,0xD7BC,0x0001,0xD5B3,0x001C,0x0013,0x0006,
+ 0x0000,0x0001,0x0002,0xD4B4,0xE4E7,0x0006,0x0003,0x0001,0xB8C8,0x0000,0xE3ED,0x0004,0x0002,0xE4D3,0xBDA6,0x0001,
+ 0xC0A3,0x0000,0x0004,0x0001,0x0001,0xCAAA,0x0000,0x0000,0xCDE5,0x000F,0x000A,0x0004,0x0001,0x0001,0xCCC0,0x0003,
+ 0x0000,0xE4CE,0x0001,0xE4D0,0x0000,0x0001,0x0001,0xD3BF,0x000A,0x0004,0x0001,0x0001,0xE4D2,0x0003,0x0000,0xE4A5,
+ 0x0001,0xD5BF,0x0004,0x0001,0x0000,0xCFE6,0x0003,0x0000,0xBAFE,0x0002,0xE4D5,0xE4D4,0x015A,0x00B1,0x0056,0x0021,
+ 0x000F,0x0007,0x0001,0x0003,0x0001,0xE4CF,0x0000,0xCDC4,0x0004,0x0000,0x0001,0xB4D5,0x0001,0x0001,0xE4D8,0x0008,
+ 0x0004,0x0000,0x0000,0xC5C8,0x0000,0x0001,0xBBEB,0x0006,0x0003,0x0001,0xC3EC,0x0001,0xD3CE,0x0001,0x0001,0xBFCA,
+ 0x001C,0x000F,0x0007,0x0004,0x0002,0xB6FD,0xE4D6,0x0001,0xD1CD,0x0004,0x0002,0xB8DB,0xBACA,0x0002,0xCEBC,0xB2E2,
+ 0x0006,0x0003,0x0000,0xE4CD,0x0000,0xCEC2,0x0003,0x0001,0xCED0,0x0002,0xE4D7,0xB2B3,0x000B,0x0007,0x0003,0x0000,
+ 0xD4FC,0x0002,0xB6C9,0xC7FE,0x0001,0x0000,0xD3E5,0x0007,0x0004,0x0002,0xBCF5,0xE4BE,0x0000,0xBBC1,0x0004,0x0002,
+ 0xC9F8,0xE4C9,0x0001,0xD3E6,0x0027,0x0014,0x000C,0x0005,0x0001,0x0002,0xE4C5,0xBDA5,0x0003,0x0001,0xE4C2,0x0002,
+ 0xD7D5,0xE4CB,0x0004,0x0000,0x0001,0xD4A8,0x0001,0x0000,0xC7E5,0x0005,0x0001,0x0001,0x0001,0xEDB5,0x0007,0x0004,
+ 0x0002,0xCCED,0xC7B3,0x0000,0xD1CD,0x0004,0x0002,0xBBEC,0xE4B5,0x0000,0xD4A8,0x001C,0x000E,0x0007,0x0003,0x0000,
+ 0xB4BE,0x0002,0xC9EE,0xEEBD,0x0004,0x0002,0xD3FD,0xBBB4,0x0001,0xB4E3,0x0008,0x0004,0x0002,0xD2F9,0xC2D9,0x0002,
+ 0xC1E8,0xBEBB,0x0003,0x0001,0xE4C6,0x0001,0xD3D9,0x000C,0x0005,0x0001,0x0002,0xB5AD,0xE4C4,0x0003,0x0001,0xE4C1,
+ 0x0002,0xE4C7,0xB7EB,0x0008,0x0004,0x0002,0xE4C0,0xC0E1,0x0002,0xE4C8,0xCCD4,0x0000,0x0001,0xC4D7,0x0059,0x002A,
+ 0x0016,0x000A,0x0006,0x0003,0x0001,0xC6E0,0x0000,0xCAE7,0x0001,0x0001,0xCCCA,0x0004,0x0000,0x0000,0xC1DC,0x0004,
+ 0x0002,0xE4BF,0xCFFD,0x0002,0xE4C0,0xD7CD,0x000C,0x0006,0x0003,0x0001,0xB5C3,0x0001,0xB5ED,0x0003,0x0000,0xE4C3,
+ 0x0001,0xC1B9,0x0004,0x0001,0x0001,0xBAD4,0x0001,0x0000,0xBAAD,0x0018,0x0009,0x0004,0x0000,0x0001,0xD2BA,0x0000,
+ 0x0002,0xD1C4,0xE4CC,0x0008,0x0004,0x0002,0xE4CA,0xB8A2,0x0002,0xC9AC,0xD5C7,0x0004,0x0002,0xBDA7,0xC8F3,0x0001,
+ 0xB5D3,0x000E,0x0007,0x0003,0x0000,0xBBC1,0x0002,0xCED0,0xE4B6,0x0004,0x0002,0xC1B0,0xE4B5,0x0000,0xC0D4,0x0004,
+ 0x0000,0x0000,0xCCCE,0x0001,0x0002,0xCCE9,0xE4B9,0x002A,0x0017,0x000C,0x0006,0x0003,0x0000,0xE4B8,0x0000,0xE4B3,
+ 0x0003,0x0001,0xCFD1,0x0001,0xD3BF,0x0005,0x0001,0x0002,0xC9E6,0xCFFB,0x0003,0x0000,0xE3FE,0x0000,0xC4F9,0x0008,
+ 0x0004,0x0000,0x0001,0xCDBF,0x0001,0x0001,0xE4BC,0x0005,0x0001,0x0002,0xE4A4,0xBDFE,0x0003,0x0000,0xBAA3,0x0001,
+ 0xD4A1,0x0010,0x0006,0x0001,0x0000,0x0002,0xE4B4,0xB8A1,0x0006,0x0003,0x0001,0xC0CB,0x0000,0xBAC6,0x0000,0x0001,
+ 0xC6D6,0x000C,0x0006,0x0003,0x0000,0xE4BD,0x0001,0xE4BB,0x0003,0x0001,0xE4B7,0x0001,0xE4BA,0x0006,0x0003,0x0001,
+ 0xBFA3,0x0000,0xD5E3,0x0001,0x0001,0xE4B1,0x00C0,0x005E,0x0036,0x001C,0x000E,0x0007,0x0004,0x0002,0xC5A8,0xE4B0,
+ 0x0000,0xBBEB,0x0004,0x0002,0xE4AF,0xBCC3,0x0000,0xE4AB,0x0007,0x0004,0x0002,0xB2E2,0xD7C7,0x0001,0xE4A5,0x0004,
+ 0x0002,0xBDBD,0xBDAC,0x0000,0xC7B3,0x000E,0x0006,0x0003,0x0000,0xE4A4,0x0000,0xC1F7,0x0004,0x0002,0xCEDB,0xC5C9,
+ 0x0002,0xC7A2,0xCDDD,0x0006,0x0003,0x0000,0xBBEE,0x0000,0xE4A1,0x0003,0x0001,0xD0DA,0x0000,0xE4AD,0x0018,0x000B,
+ 0x0007,0x0004,0x0002,0xE4B2,0xD6DE,0x0000,0xB6FD,0x0000,0x0001,0xE4AC,0x0007,0x0004,0x0002,0xE4AA,0xBAE9,0x0000,
+ 0xD0B9,0x0003,0x0000,0xE4A2,0x0000,0xBDF2,0x0005,0x0001,0x0000,0x0001,0xB6B4,0x0007,0x0004,0x0002,0xC2E5,0xE4AE,
+ 0x0000,0xE4A8,0x0000,0x0000,0xCFB4,0x002F,0x0015,0x000A,0x0004,0x0000,0x0001,0xC8F7,0x0003,0x0001,0xE4A9,0x0001,
+ 0xE4A3,0x0005,0x0000,0x0002,0xD1F3,0xBCF6,0x0003,0x0000,0xE4A6,0x0001,0xE4A7,0x000C,0x0005,0x0001,0x0002,0xBDE0,
+ 0xB0E3,0x0003,0x0001,0xE3FE,0x0002,0xD4F3,0xC6C3,0x0007,0x0004,0x0002,0xD0BA,0xE3F8,0x0001,0xE3F2,0x0004,0x0002,
+ 0xE3F1,0xEDB4,0x0000,0xB1C3,0x0019,0x000E,0x0007,0x0003,0x0000,0xD3BE,0x0002,0xE3F3,0xCCA9,0x0004,0x0002,0xE3FD,
+ 0xE3FA,0x0000,0xE8F5,0x0007,0x0004,0x0002,0xE3F9,0xC0E1,0x0001,0xD7A2,0x0001,0x0000,0xC4E0,0x000E,0x0008,0x0004,
+ 0x0002,0xC6FC,0xB2A8,0x0002,0xC5DD,0xE3F6,0x0003,0x0001,0xC5A2,0x0000,0xCBDD,0x0004,0x0000,0x0000,0xB7BA,0x0004,
+ 0x0002,0xE3F4,0xE3F7,0x0002,0xB7A8,0xE3EF,0x0061,0x002D,0x0019,0x000C,0x0006,0x0003,0x0000,0xE3FC,0x0001,0xE3EE,
+ 0x0003,0x0000,0xC9E6,0x0001,0xC3DA,0x0006,0x0003,0x0001,0xB2B4,0x0000,0xC8AA,0x0003,0x0001,0xD2E7,0x0002,0xC7F6,
+ 0xD0B9,0x000C,0x0004,0x0001,0x0000,0xBFF6,0x0004,0x0002,0xD1D8,0xD5B4,0x0002,0xB9C1,0xD5D3,0x0000,0x0003,0x0000,
+ 0xD6CE,0x0002,0xD3CD,0xB7D0,0x001C,0x000D,0x0007,0x0004,0x0002,0xBAD3,0xE3F5,0x0000,0xE3FB,0x0003,0x0001,0xBEDA,
+ 0x0000,0xE3F0,0x0007,0x0004,0x0002,0xC4AD,0xBBA6,0x0000,0xE3ED,0x0004,0x0002,0xB2D7,0xC2D9,0x0002,0xC1A4,0xC5BD,
+ 0x000C,0x0006,0x0003,0x0000,0xE3E3,0x0000,0xC3BB,0x0003,0x0000,0xB9B5,0x0001,0xE3FA,0x0006,0x0003,0x0000,0xC5E6,
+ 0x0000,0xC9B3,0x0003,0x0001,0xB3E5,0x0001,0xE3E6,0x0033,0x001A,0x000E,0x0007,0x0004,0x0002,0xEDB3,0xC3BB,0x0001,
+ 0xE3E5,0x0003,0x0000,0xC6E3,0x0002,0xD9FC,0xE3E7,0x0005,0x0001,0x0002,0xB3C1,0xC9F2,0x0004,0x0002,0xD9F0,0xE3EC,
+ 0x0000,0xE3E4,0x000D,0x0007,0x0004,0x0002,0xCED6,0xD2CA,0x0000,0xC7DF,0x0003,0x0001,0xB7DA,0x0000,0xC6FB,0x0006,
+ 0x0003,0x0001,0xBEF6,0x0000,0xD0DA,0x0003,0x0001,0xE3EB,0x0001,0xE3EA,0x0014,0x0007,0x0000,0x0003,0x0001,0xBCB3,
+ 0x0001,0xCCAD,0x0007,0x0003,0x0001,0xCDF4,0x0002,0xE3E8,0xE3E8,0x0003,0x0000,0xE1A9,0x0001,0xCCC0,0x000D,0x0005,
+ 0x0001,0x0002,0xCEDB,0xB3D8,0x0004,0x0002,0xBDAD,0xB9AF,0x0002,0xC8EA,0xE3E1,0x0006,0x0003,0x0000,0xD1B4,0x0000,
+ 0xCEDB,0x0003,0x0000,0xBAB9,0x0002,0xC9C7,0xE3E0,0x0268,0x0162,0x00B4,0x0061,0x002A,0x0017,0x000A,0x0004,0x0001,
+ 0x0000,0xC3A3,0x0003,0x0000,0xCFAB,0x0000,0xB7BA,0x0007,0x0003,0x0001,0xE5AE,0x0002,0xE3E2,0xBABA,0x0003,0x0001,
+ 0xBBE3,0x0000,0xD9DB,0x000C,0x0005,0x0001,0x0002,0xC7F3,0xD6AD,0x0003,0x0000,0xCDA1,0x0002,0xB7BA,0xD9DB,0x0001,
+ 0x0003,0x0000,0xD3C0,0x0001,0xE3DF,0x001D,0x000E,0x0007,0x0004,0x0002,0xCBAE,0xEBB5,0x0000,0xEBB5,0x0004,0x0002,
+ 0xC7E8,0xC2C8,0x0000,0xB5AA,0x0008,0x0004,0x0002,0xEBB2,0xC7E2,0x0002,0xEBB4,0xEBB2,0x0004,0x0002,0xB0B1,0xD1F5,
+ 0x0000,0xBAA4,0x000C,0x0008,0x0004,0x0002,0xEBB3,0xC6F8,0x0002,0xC7E2,0xEBB1,0x0000,0x0001,0xB7FA,0x0007,0x0003,
+ 0x0001,0xB7D5,0x0002,0xEBB0,0xEBAF,0x0003,0x0000,0xEBAE,0x0002,0xC4CA,0xEBAD,0x002C,0x001A,0x000E,0x0007,0x0004,
+ 0x0002,0xC6F8,0xC3A5,0x0001,0xC3F1,0x0004,0x0002,0xD8B5,0xCACF,0x0001,0xEBAC,0x0004,0x0000,0x0000,0xEBAA,0x0004,
+ 0x0002,0xD5B1,0xEBAA,0x0002,0xEBAB,0xEBA9,0x000A,0x0004,0x0001,0x0000,0xEAF3,0x0003,0x0001,0xEBA7,0x0001,0xEBA6,
+ 0x0004,0x0001,0x0001,0xEBA8,0x0001,0x0001,0xEBA7,0x0013,0x0008,0x0004,0x0000,0x0001,0xEBA5,0x0000,0x0001,0xCCBA,
+ 0x0007,0x0004,0x0002,0xC7F2,0xBAC1,0x0000,0xEBA4,0x0000,0x0001,0xC8DE,0x0005,0x0000,0x0001,0x0001,0xD5B1,0x0007,
+ 0x0003,0x0001,0xC3AB,0x0002,0xB2C5,0xB1D0,0x0004,0x0002,0xC5FE,0xC5FE,0x0002,0xB1D1,0xB1CF,0x005A,0x002F,0x0017,
+ 0x000D,0x0007,0x0004,0x0002,0xB1C8,0xD8B9,0x0000,0xB6BE,0x0003,0x0001,0xC3BF,0x0001,0xC4B8,0x0005,0x0000,0x0002,
+ 0xCEE3,0xCEE3,0x0001,0x0002,0xC5B9,0xD2E3,0x000C,0x0005,0x0001,0x0002,0xECB1,0xBBD9,0x0004,0x0002,0xBBD9,0xB5EE,
+ 0x0001,0xCFFD,0x0006,0x0003,0x0000,0xBFC7,0x0000,0xC9B1,0x0003,0x0001,0xD2F3,0x0001,0xB6CE,0x0014,0x000D,0x0007,
+ 0x0004,0x0002,0xC5B9,0xECAF,0x0000,0xBCDF,0x0003,0x0001,0xE9EB,0x0000,0xE9E7,0x0000,0x0003,0x0001,0xE9E9,0x0000,
+ 0xE9EC,0x000A,0x0006,0x0003,0x0000,0xE9E4,0x0001,0xE9EB,0x0001,0x0000,0xE9E6,0x0006,0x0003,0x0001,0xE9EA,0x0000,
+ 0xE9E9,0x0004,0x0002,0xB2D0,0xD1DA,0x0000,0xD6B3,0x0030,0x0017,0x000A,0x0006,0x0003,0x0001,0xE9E7,0x0000,0xE9E6,
+ 0x0001,0x0001,0xE9E8,0x0007,0x0003,0x0001,0xB2D0,0x0002,0xCAE2,0xD1B3,0x0003,0x0001,0xE9E4,0x0000,0xB4F9,0x000D,
+ 0x0008,0x0004,0x0002,0xE9E5,0xD1EA,0x0002,0xE9E3,0xE9E2,0x0000,0x0002,0xD8B2,0xE9E2,0x0007,0x0004,0x0002,0xBCDF,
+ 0xCBC0,0x0001,0xB4F5,0x0000,0x0002,0xB9E9,0xC0FA,0x0013,0x0008,0x0004,0x0001,0x0000,0xCBEA,0x0001,0x0001,0xF5D8,
+ 0x0004,0x0001,0x0000,0xCDE1,0x0003,0x0001,0xC6E7,0x0002,0xCEE4,0xB2BD,0x000C,0x0008,0x0004,0x0002,0xB4CB,0xD5FD,
+ 0x0002,0xD6B9,0xBBB6,0x0000,0x0001,0xECA3,0x0000,0x0001,0x0001,0xECA8,0x008D,0x0050,0x002D,0x0014,0x0007,0x0001,
+ 0x0003,0x0001,0xC5B7,0x0001,0xCCBE,0x0005,0x0000,0x0002,0xCED8,0xB8E8,0x0004,0x0002,0xC7B8,0xD3E4,0x0002,0xD0AA,
+ 0xECA7,0x000C,0x0007,0x0003,0x0000,0xE4CE,0x0002,0xECA6,0xF2A7,0x0001,0x0002,0xBFB2,0xBFEE,0x0006,0x0003,0x0000,
+ 0xC7D5,0x0001,0xC6DB,0x0003,0x0000,0xECA5,0x0002,0xECA4,0xE0CA,0x0010,0x0008,0x0004,0x0001,0x0001,0xD3FB,0x0000,
+ 0x0000,0xBAC8,0x0004,0x0000,0x0001,0xBFC8,0x0001,0x0000,0xC5B7,0x000E,0x0007,0x0003,0x0001,0xECA3,0x0002,0xD0C0,
+ 0xBBB6,0x0004,0x0002,0xB4CE,0xC7B7,0x0001,0xE8F9,0x0001,0x0001,0x0001,0xE9AD,0x001B,0x000D,0x0008,0x0004,0x0001,
+ 0x0001,0xE8EF,0x0001,0x0000,0xE9A1,0x0000,0x0001,0x0001,0xC8A8,0x0005,0x0000,0x0000,0x0001,0xC0B8,0x0005,0x0001,
+ 0x0002,0xD3A3,0xE8F9,0x0000,0x0001,0xE9B7,0x0011,0x0005,0x0000,0x0001,0x0000,0xE8D0,0x0006,0x0003,0x0001,0xE9B4,
+ 0x0001,0xE8C0,0x0003,0x0001,0xE8D3,0x0000,0xE9C6,0x0009,0x0004,0x0000,0x0000,0xB3F7,0x0001,0x0002,0xE8DD,0xE9DA,
+ 0x0000,0x0003,0x0000,0xE8FC,0x0002,0xE8CE,0xE9B5,0x0037,0x0014,0x0006,0x0000,0x0000,0x0001,0x0000,0xE9D6,0x0006,
+ 0x0000,0x0001,0x0002,0xB9F1,0xE8FE,0x0004,0x0001,0x0000,0xBCF7,0x0000,0x0001,0xC4FB,0x0012,0x0008,0x0004,0x0001,
+ 0x0000,0xE9C4,0x0001,0x0000,0xCCA8,0x0006,0x0003,0x0001,0xC3CA,0x0000,0xE9DF,0x0000,0x0000,0xE9DD,0x0009,0x0005,
+ 0x0001,0x0002,0xE9C9,0xBCEC,0x0000,0x0001,0xE9D1,0x0004,0x0000,0x0001,0xE8ED,0x0001,0x0000,0xE9DE,0x0023,0x0010,
+ 0x000B,0x0004,0x0000,0x0001,0xB5B5,0x0004,0x0002,0xE9DB,0xE9DC,0x0001,0xE9D5,0x0001,0x0000,0x0000,0xE8DF,0x000B,
+ 0x0004,0x0000,0x0001,0xCFAD,0x0004,0x0002,0xE9DD,0xCCB4,0x0001,0xC7C2,0x0004,0x0000,0x0001,0xE9DA,0x0000,0x0000,
+ 0xE9D6,0x000A,0x0005,0x0001,0x0000,0x0000,0xB3F7,0x0000,0x0001,0x0000,0xBAE1,0x000C,0x0006,0x0003,0x0000,0xE9CD,
+ 0x0001,0xCDD6,0x0003,0x0000,0xCFF0,0x0000,0xBBFA,0x0004,0x0001,0x0000,0xE9D3,0x0000,0x0002,0xB3C8,0xE9D9,0x012F,
+ 0x008A,0x0048,0x001F,0x000E,0x0005,0x0001,0x0001,0x0000,0xE9D2,0x0004,0x0001,0x0001,0xC7C5,0x0001,0x0002,0xE8E3,
+ 0xC7C1,0x0005,0x0000,0x0001,0x0000,0xE9CF,0x0005,0x0000,0x0002,0xE9D0,0xE9D7,0x0004,0x0002,0xE8EB,0xCAF7,0x0000,
+ 0xC6D3,0x0011,0x000A,0x0004,0x0000,0x0001,0xE9D4,0x0003,0x0001,0xD3A3,0x0001,0xE9C9,0x0001,0x0003,0x0000,0xBAE1,
+ 0x0000,0xE9D8,0x000A,0x0004,0x0001,0x0001,0xD1F9,0x0003,0x0001,0xC4A3,0x0001,0xD5C1,0x0007,0x0004,0x0002,0xCAE0,
+ 0xE9AB,0x0001,0xC7F3,0x0003,0x0001,0xB1EA,0x0002,0xE9CC,0xE9CB,0x0024,0x000E,0x0009,0x0005,0x0001,0x0002,0xB3B2,
+ 0xC2A5,0x0000,0x0001,0xC1BA,0x0001,0x0000,0x0000,0xB7AE,0x000B,0x0004,0x0000,0x0001,0xE8C8,0x0004,0x0002,0xC0D6,
+ 0xD7AE,0x0001,0xE9C8,0x0007,0x0004,0x0002,0xEFDC,0xB2DB,0x0000,0xB9E6,0x0001,0x0001,0xF4AB,0x0011,0x0008,0x0004,
+ 0x0001,0x0001,0xBDB0,0x0000,0x0000,0xE9CE,0x0004,0x0000,0x0001,0xE9CA,0x0001,0x0002,0xE9A4,0xE8FD,0x0008,0x0001,
+ 0x0003,0x0000,0xD0A8,0x0002,0xE9C6,0xE9C4,0x0000,0x0001,0x0001,0xBCF7,0x004D,0x0027,0x0015,0x0009,0x0005,0x0001,
+ 0x0002,0xE9C0,0xB8DC,0x0001,0x0000,0xBBB1,0x0008,0x0004,0x0002,0xE9B6,0xC7B9,0x0002,0xE9B3,0xB9B9,0x0000,0x0000,
+ 0xE9C3,0x000A,0x0004,0x0001,0x0001,0xC5CC,0x0003,0x0001,0xE9C2,0x0001,0xE8E7,0x0004,0x0001,0x0001,0xE9BD,0x0001,
+ 0x0001,0xC8B6,0x0016,0x0008,0x0004,0x0001,0x0000,0xC1F1,0x0000,0x0001,0xE9C1,0x0007,0x0004,0x0002,0xC8D9,0xE9BF,
+ 0x0001,0xE9BE,0x0003,0x0000,0xE8BF,0x0002,0xD5A5,0xE9BC,0x0007,0x0000,0x0003,0x0000,0xB8C9,0x0000,0xE8EE,0x0005,
+ 0x0001,0x0002,0xB0F1,0xE9BB,0x0001,0x0000,0xE9B0,0x002A,0x0013,0x0007,0x0000,0x0003,0x0001,0xE9C5,0x0000,0xC0C6,
+ 0x0005,0x0000,0x0002,0xE9B1,0xE9C7,0x0003,0x0001,0xE9B7,0x0002,0xE9B5,0xE9B4,0x000C,0x0006,0x0003,0x0000,0xD3DC,
+ 0x0000,0xE9AD,0x0003,0x0000,0xB8C5,0x0000,0xE9AF,0x0004,0x0001,0x0000,0xC2A5,0x0003,0x0001,0xE9BA,0x0002,0xE9B1,
+ 0xBFAC,0x0017,0x000A,0x0004,0x0000,0x0001,0xBCAB,0x0003,0x0001,0xE9A8,0x0001,0xB6DC,0x0007,0x0004,0x0002,0xE8FA,
+ 0xD2B5,0x0001,0xE9AE,0x0003,0x0000,0xB5FA,0x0000,0xE8E5,0x000B,0x0007,0x0004,0x0002,0xE9B8,0xE9B8,0x0001,0xE9B9,
+ 0x0001,0x0000,0xE9AA,0x0005,0x0000,0x0002,0xC0E3,0xE9AC,0x0004,0x0002,0xB3FE,0xC3AF,0x0001,0xE9A5,0x0083,0x003E,
+ 0x0021,0x0010,0x000B,0x0007,0x0003,0x0000,0xE8CE,0x0002,0xD0A8,0xB7E3,0x0000,0x0000,0xCBBC,0x0001,0x0000,0x0000,
+ 0xD1EE,0x0007,0x0001,0x0003,0x0000,0xE9AB,0x0001,0xB4BB,0x0004,0x0000,0x0001,0xB4AA,0x0003,0x0001,0xE9A9,0x0001,
+ 0xBCEA,0x000F,0x000A,0x0006,0x0003,0x0001,0xBCCF,0x0000,0xE9B2,0x0001,0x0000,0xD2AC,0x0000,0x0000,0x0001,0xCDD6,
+ 0x0009,0x0004,0x0001,0x0000,0xE9A1,0x0001,0x0002,0xE8FD,0xE8FC,0x0001,0x0001,0x0001,0xEDD6,0x0021,0x0010,0x0008,
+ 0x0001,0x0003,0x0000,0xBDB7,0x0002,0xE9A7,0xE8E2,0x0000,0x0004,0x0002,0xD7B5,0xD6B2,0x0001,0xE9A3,0x0008,0x0004,
+ 0x0000,0x0001,0xD2CE,0x0000,0x0001,0xE9A4,0x0004,0x0001,0x0000,0xE8FB,0x0000,0x0002,0xB9D7,0xE8FE,0x0013,0x000B,
+ 0x0004,0x0000,0x0001,0xBFC3,0x0004,0x0002,0xC6DC,0xC0E2,0x0000,0xE9A2,0x0004,0x0000,0x0000,0xC9AD,0x0001,0x0001,
+ 0xD5BB,0x0009,0x0004,0x0001,0x0001,0xE9A6,0x0001,0x0002,0xCCC4,0xB6B0,0x0001,0x0003,0x0000,0xC5EF,0x0002,0xBCAC,
+ 0xD4E6,0x004A,0x0020,0x0015,0x000B,0x0005,0x0000,0x0002,0xE8C7,0xD7D8,0x0003,0x0000,0xB0F4,0x0000,0xF3F5,0x0006,
+ 0x0003,0x0001,0xB9F7,0x0001,0xC6E5,0x0000,0x0001,0xC3DE,0x0000,0x0004,0x0001,0x0000,0xC6FA,0x0003,0x0000,0xE8F9,
+ 0x0000,0xBCEC,0x0018,0x000D,0x0006,0x0003,0x0001,0xE8F3,0x0001,0xCAE1,0x0003,0x0000,0xCDD1,0x0002,0xD0B5,0xCCDD,
+ 0x0004,0x0000,0x0001,0xCBF3,0x0003,0x0000,0xB6B9,0x0002,0xC0E6,0xCEE0,0x000A,0x0004,0x0000,0x0000,0xC3CE,0x0003,
+ 0x0000,0xC9D2,0x0001,0xE8C9,0x0004,0x0000,0x0001,0xCCF5,0x0001,0x0001,0xB9A3,0x0021,0x000A,0x0000,0x0005,0x0001,
+ 0x0002,0xE8D9,0xE8F7,0x0001,0x0001,0xE8F4,0x000D,0x0007,0x0004,0x0002,0xB0F0,0xC3B7,0x0001,0xE8E8,0x0003,0x0001,
+ 0xC1BA,0x0001,0xB8CB,0x0004,0x0001,0x0000,0xC6E1,0x0003,0x0001,0xC0C6,0x0001,0xE8F6,0x0014,0x0007,0x0000,0x0003,
+ 0x0000,0xCDB0,0x0000,0xE8F5,0x0006,0x0003,0x0000,0xB1AD,0x0001,0xE8F8,0x0003,0x0001,0xD7AE,0x0002,0xBDB0,0xE8ED,
+ 0x0000,0x0008,0x0004,0x0002,0xE8EB,0xC7C5,0x0002,0xE8E7,0xB5B5,0x0004,0x0002,0xE8E5,0xE8E3,0x0000,0xE8E2,0x0543,
+ 0x029D,0x0159,0x00AD,0x004E,0x0034,0x0019,0x000C,0x0005,0x0001,0x0002,0xE8EA,0xBDDB,0x0003,0x0000,0xBBB8,0x0002,
+ 0xC9A3,0xCDA9,0x0006,0x0003,0x0001,0xE8E4,0x0001,0xD7C0,0x0003,0x0001,0xE8F0,0x0002,0xE8F1,0xB0B8,0x000F,0x0007,
+ 0x0003,0x0001,0xBFF2,0x0002,0xCEA6,0xE8E6,0x0004,0x0002,0xCCD2,0xB9F0,0x0002,0xE8EC,0xE8EE,0x0007,0x0003,0x0001,
+ 0xE8EF,0x0002,0xD4D4,0xB8F1,0x0001,0x0002,0xB8F9,0xBACB,0x0010,0x0009,0x0004,0x0000,0x0000,0xD1F9,0x0000,0x0002,
+ 0xE8E1,0xE8E0,0x0001,0x0003,0x0001,0xD6EA,0x0000,0xE8F2,0x0005,0x0001,0x0001,0x0000,0xD0A3,0x0000,0x0001,0x0000,
+ 0xE8E9,0x002D,0x001C,0x000D,0x0007,0x0004,0x0002,0xC0F5,0xC6DC,0x0001,0xC6F5,0x0003,0x0000,0xCBA8,0x0000,0xCAF7,
+ 0x0007,0x0004,0x0002,0xC0B8,0xE8DD,0x0001,0xE8D3,0x0004,0x0002,0xB6B0,0xE8D0,0x0002,0xE8CE,0xD5BB,0x000A,0x0006,
+ 0x0003,0x0000,0xB1EA,0x0000,0xD5A4,0x0001,0x0001,0xE8D9,0x0000,0x0003,0x0000,0xCAC1,0x0000,0xE8DF,0x0016,0x000C,
+ 0x0005,0x0001,0x0002,0xD5A4,0xB2F1,0x0003,0x0000,0xC1F8,0x0002,0xD6F9,0xE8CD,0x0006,0x0003,0x0000,0xBFC2,0x0001,
+ 0xBCED,0x0001,0x0000,0xE8D1,0x000D,0x0007,0x0003,0x0000,0xF5FD,0x0002,0xB2E9,0xD9DE,0x0003,0x0001,0xE8DC,0x0001,
+ 0xC4FB,0x0008,0x0004,0x0002,0xE9AA,0xD7F5,0x0002,0xE8D8,0xB9F1,0x0003,0x0001,0xE8D6,0x0002,0xE8D4,0xE8CF,0x0057,
+ 0x0024,0x0014,0x000C,0x0004,0x0001,0x0001,0xC8E1,0x0004,0x0002,0xC8BE,0xC6E2,0x0002,0xB8CC,0xC4B3,0x0004,0x0000,
+ 0x0000,0xB0D8,0x0001,0x0001,0xC5CC,0x000A,0x0004,0x0001,0x0001,0xB1FA,0x0003,0x0000,0xE8DA,0x0000,0xE8DE,0x0001,
+ 0x0001,0x0002,0xE8F5,0xE8DB,0x001B,0x000E,0x0008,0x0004,0x0002,0xBCCF,0xBCDC,0x0002,0xE8D5,0xB9D5,0x0003,0x0000,
+ 0xE8D7,0x0001,0xE8D2,0x0006,0x0003,0x0000,0xBFDD,0x0000,0xE8C9,0x0004,0x0002,0xB7E3,0xC7B9,0x0001,0xE8C7,0x000B,
+ 0x0006,0x0003,0x0000,0xE8C5,0x0000,0xE8C0,0x0000,0x0002,0xD4E6,0xCAE0,0x0007,0x0003,0x0001,0xE8C8,0x0002,0xD6A6,
+ 0xB9FB,0x0003,0x0001,0xC3B6,0x0001,0xE8C4,0x0028,0x0016,0x000C,0x0006,0x0003,0x0000,0xC1D6,0x0000,0xD5ED,0x0003,
+ 0x0001,0xE8E2,0x0001,0xCEF6,0x0004,0x0001,0x0001,0xE8FB,0x0003,0x0000,0xE8CA,0x0000,0xCDF7,0x000A,0x0006,0x0003,
+ 0x0000,0xE8C1,0x0001,0xB9B9,0x0001,0x0000,0xBCAB,0x0000,0x0004,0x0002,0xB0E5,0xCBC9,0x0001,0xE8CC,0x001B,0x000E,
+ 0x0006,0x0003,0x0000,0xE8CB,0x0000,0xE8C6,0x0004,0x0002,0xE8C3,0xEABD,0x0002,0xB6AB,0xBDDC,0x0006,0x0003,0x0000,
+ 0xB1AD,0x0000,0xBABC,0x0003,0x0001,0xE8C2,0x0002,0xE8BF,0xD1EE,0x0009,0x0004,0x0001,0x0000,0xC0B4,0x0001,0x0002,
+ 0xCCF5,0xB8DC,0x0000,0x0004,0x0002,0xCAF8,0xE8BD,0x0002,0xC0E9,0xB6C5,0x00B4,0x005D,0x0030,0x0017,0x000A,0x0004,
+ 0x0001,0x0000,0xD5C8,0x0003,0x0001,0xE8BC,0x0001,0xB4E5,0x0007,0x0004,0x0002,0xB2C4,0xD0D3,0x0000,0xC0EE,0x0003,
+ 0x0000,0xE8BB,0x0001,0xC9BC,0x000D,0x0007,0x0003,0x0000,0xE8BE,0x0002,0xB8CB,0xD3DB,0x0003,0x0001,0xC8A8,0x0000,
+ 0xD4D3,0x0007,0x0004,0x0002,0xC9B1,0xCAF8,0x0001,0xD0E0,0x0001,0x0002,0xBBFA,0xF3FE,0x0018,0x000A,0x0004,0x0001,
+ 0x0001,0xB6E4,0x0003,0x0000,0xC6D3,0x0001,0xD6EC,0x0007,0x0003,0x0001,0xCAF5,0x0002,0xCAF5,0xD4FD,0x0004,0x0002,
+ 0xB1BE,0xC4A9,0x0000,0xCEB4,0x000B,0x0007,0x0004,0x0002,0xC4BE,0xEBCA,0x0000,0xEBFC,0x0001,0x0000,0xCDFB,0x0006,
+ 0x0003,0x0001,0xC6DA,0x0001,0xB3AF,0x0000,0x0001,0xCDFB,0x002F,0x0018,0x000B,0x0006,0x0003,0x0001,0xC0CA,0x0001,
+ 0xEBDE,0x0000,0x0002,0xCBB7,0xECF6,0x0006,0x0003,0x0000,0xEBD4,0x0001,0xB7FE,0x0003,0x0001,0xC5F3,0x0002,0xEBC3,
+ 0xD3D0,0x000A,0x0006,0x0003,0x0000,0xD4C2,0x0001,0xEAC2,0x0000,0x0001,0xBBE1,0x0007,0x0004,0x0002,0xD7EE,0xCCE6,
+ 0x0000,0xD4F8,0x0003,0x0000,0xC2FC,0x0001,0xB2DC,0x0016,0x000C,0x0005,0x0000,0x0002,0xCAE9,0xEAC2,0x0004,0x0002,
+ 0xB8FC,0xD2B7,0x0000,0xC7FA,0x0004,0x0000,0x0000,0xD4BB,0x0003,0x0000,0xC9B9,0x0001,0xEAD9,0x0005,0x0000,0x0001,
+ 0x0000,0xEAD8,0x0006,0x0003,0x0000,0xBFF5,0x0001,0xC6D8,0x0004,0x0002,0xEAD7,0xEAD6,0x0001,0xCAEF,0x003D,0x001C,
+ 0x000A,0x0005,0x0000,0x0001,0x0000,0xEAD3,0x0001,0x0001,0x0001,0xCFFE,0x000A,0x0006,0x0003,0x0001,0xEABC,0x0000,
+ 0xC0FA,0x0000,0x0000,0xEACA,0x0004,0x0001,0x0000,0xEAD5,0x0001,0x0001,0xE5DF,0x0011,0x0007,0x0001,0x0003,0x0000,
+ 0xB1A9,0x0001,0xEAC7,0x0004,0x0001,0x0000,0xC4BA,0x0003,0x0001,0xD4DD,0x0000,0xC2F7,0x0009,0x0005,0x0000,0x0002,
+ 0xF4DF,0xEAD3,0x0001,0x0000,0xB3A9,0x0000,0x0003,0x0000,0xF0A9,0x0001,0xEAD4,0x0028,0x0011,0x000A,0x0006,0x0003,
+ 0x0001,0xB0B5,0x0000,0xC5AF,0x0001,0x0001,0xCAEE,0x0001,0x0003,0x0000,0xEAD2,0x0001,0xEACD,0x000B,0x0005,0x0000,
+ 0x0002,0xD4CE,0xCFBE,0x0003,0x0000,0xEAD1,0x0000,0xD4DD,0x0006,0x0003,0x0000,0xCDFA,0x0000,0xC1C0,0x0003,0x0001,
+ 0xB0B5,0x0000,0xD6C7,0x0012,0x000A,0x0006,0x0003,0x0001,0xEAD0,0x0000,0xBEA7,0x0000,0x0000,0xC7E7,0x0000,0x0004,
+ 0x0002,0xCEFA,0xBEB0,0x0000,0xC6D5,0x000D,0x0006,0x0003,0x0000,0xB3BF,0x0000,0xBBDE,0x0003,0x0000,0xCEEE,0x0002,
+ 0xCEFA,0xEACE,0x0006,0x0003,0x0001,0xEAC9,0x0001,0xD6E7,0x0003,0x0000,0xC0A5,0x0000,0xCDED,0x014F,0x00A2,0x0054,
+ 0x002A,0x0017,0x000D,0x0005,0x0001,0x0002,0xEACF,0xEACD,0x0004,0x0002,0xD4CE,0xEACA,0x0002,0xCFFE,0xC9B9,0x0004,
+ 0x0001,0x0000,0xEACC,0x0003,0x0001,0xC9CE,0x0000,0xBDFA,0x0009,0x0004,0x0000,0x0000,0xBDFA,0x0001,0x0002,0xBBCE,
+ 0xCAB1,0x0006,0x0003,0x0000,0xEACB,0x0001,0xCFD4,0x0000,0x0001,0xD6E7,0x0015,0x000B,0x0004,0x0001,0x0001,0xEAC6,
+ 0x0004,0x0002,0xEAC7,0xEAC4,0x0000,0xD2DD,0x0006,0x0003,0x0000,0xEAC5,0x0000,0xCAC7,0x0000,0x0000,0xD5D1,0x000A,
+ 0x0006,0x0003,0x0001,0xD7F2,0x0000,0xC3C1,0x0000,0x0000,0xB4BA,0x0007,0x0004,0x0002,0xEAC5,0xD3B3,0x0000,0xD0C7,
+ 0x0000,0x0000,0xEAC3,0x0028,0x0016,0x000B,0x0004,0x0000,0x0000,0xEABC,0x0004,0x0002,0xEABF,0xCEF4,0x0000,0xD2D7,
+ 0x0005,0x0001,0x0002,0xBBE8,0xC3F7,0x0003,0x0001,0xB2FD,0x0001,0xEABB,0x000A,0x0005,0x0001,0x0002,0xC9FD,0xC0A5,
+ 0x0001,0x0002,0xEABE,0xB0BA,0x0004,0x0000,0x0001,0xEAC0,0x0001,0x0001,0xCDFA,0x0013,0x0006,0x0000,0x0001,0x0002,
+ 0xBFF5,0xCAB1,0x0008,0x0004,0x0002,0xBAB5,0xEABA,0x0002,0xEAB9,0xEAB8,0x0000,0x0002,0xD0F1,0xD1AE,0x000E,0x0008,
+ 0x0004,0x0002,0xD4E7,0xD6BC,0x0002,0xBEC9,0xB5A9,0x0003,0x0000,0xC8D5,0x0001,0xBCC8,0x0000,0x0000,0x0001,0xCEDE,
+ 0x005C,0x002C,0x0015,0x0009,0x0005,0x0001,0x0002,0xC6EC,0xECBD,0x0001,0x0001,0xECBC,0x0005,0x0001,0x0002,0xD7E5,
+ 0xECBB,0x0004,0x0002,0xECBA,0xECBA,0x0000,0xD0FD,0x000C,0x0004,0x0001,0x0001,0xECB7,0x0004,0x0002,0xC2C3,0xECB8,
+ 0x0002,0xECB9,0xC6EC,0x0006,0x0003,0x0000,0xC5D4,0x0000,0xD3CE,0x0000,0x0002,0xCAA9,0xD3DA,0x0019,0x000B,0x0006,
+ 0x0003,0x0000,0xB7BD,0x0000,0xB6CF,0x0001,0x0002,0xC7DB,0xEDBD,0x0007,0x0003,0x0001,0xD0C2,0x0002,0xCBB9,0xEDBD,
+ 0x0004,0x0002,0xB6CF,0xD5B6,0x0000,0xEDBD,0x000B,0x0006,0x0003,0x0000,0xD5B6,0x0000,0xB8AB,0x0000,0x0002,0xB3E2,
+ 0xBDEF,0x0006,0x0003,0x0000,0xCED3,0x0000,0xD5E5,0x0003,0x0001,0xD0B1,0x0000,0xF5FA,0x0026,0x0017,0x000C,0x0006,
+ 0x0003,0x0000,0xC1CF,0x0000,0xB6B7,0x0003,0x0000,0xECB5,0x0000,0xECB5,0x0005,0x0000,0x0002,0xB0DF,0xECB3,0x0003,
+ 0x0001,0xB1F3,0x0000,0xD5AB,0x0009,0x0004,0x0001,0x0000,0xCEC4,0x0001,0x0002,0xB1D0,0xC1B2,0x0001,0x0001,0x0002,
+ 0xE5C4,0xC7FD,0x0018,0x000D,0x0006,0x0003,0x0001,0xCAFD,0x0000,0xB7F3,0x0004,0x0002,0xB5D0,0xD5FB,0x0001,0xC7C3,
+ 0x0004,0x0000,0x0001,0xCAFD,0x0003,0x0001,0xBEB4,0x0002,0xEBB8,0xB6DE,0x0009,0x0004,0x0001,0x0001,0xB6D8,0x0001,
+ 0x0002,0xC9A2,0xB8D2,0x0004,0x0001,0x0001,0xB3A8,0x0003,0x0000,0xB1D6,0x0000,0xC1B2,0x00A6,0x0054,0x0028,0x0016,
+ 0x000C,0x0008,0x0004,0x0002,0xBDCC,0xD0F0,0x0002,0xB0DC,0xB0BD,0x0000,0x0000,0xEBB7,0x0006,0x0003,0x0000,0xBEC8,
+ 0x0000,0xC3F4,0x0000,0x0001,0xB5D0,0x0009,0x0005,0x0000,0x0002,0xF4CD,0xD0A7,0x0000,0x0000,0xB9CA,0x0005,0x0001,
+ 0x0002,0xD5FE,0xB7C5,0x0001,0x0000,0xB9A5,0x001B,0x000F,0x0008,0x0004,0x0002,0xB8C4,0xD8FC,0x0002,0xBFBC,0xCAD5,
+ 0x0004,0x0002,0xEBB6,0xEAB7,0x0001,0xECA5,0x0005,0x0001,0x0002,0xD6A7,0xDFAD,0x0003,0x0001,0xC0BF,0x0002,0xBEF0,
+ 0xBDC1,0x0009,0x0001,0x0004,0x0002,0xDFAC,0xCCAF,0x0002,0xC2CE,0xD4DC,0x0001,0x0004,0x0002,0xC9E3,0xD0AF,0x0000,
+ 0xDFA5,0x0029,0x0012,0x000D,0x0007,0x0004,0x0002,0xB2F3,0xC8C1,0x0001,0xDEFC,0x0003,0x0001,0xC0B9,0x0001,0xD4DC,
+ 0x0000,0x0001,0x0000,0xC2A3,0x000A,0x0006,0x0003,0x0000,0xDFAB,0x0001,0xC4EC,0x0000,0x0001,0xDEF3,0x0006,0x0003,
+ 0x0001,0xC5CA,0x0001,0xC8C5,0x0003,0x0001,0xDFA3,0x0002,0xCBD3,0xB0DA,0x0015,0x000A,0x0004,0x0001,0x0000,0xDFA2,
+ 0x0003,0x0001,0xC0A9,0x0001,0xD6C0,0x0007,0x0004,0x0002,0xB8E9,0xC5A1,0x0000,0xB1F7,0x0000,0x0001,0xC4E2,0x000A,
+ 0x0004,0x0001,0x0001,0xB2C1,0x0003,0x0001,0xDFA9,0x0001,0xDFAA,0x0006,0x0003,0x0001,0xBCB7,0x0001,0xCBD3,0x0001,
+ 0x0001,0xBEDD,0x0066,0x0033,0x0019,0x000C,0x0006,0x0003,0x0001,0xEBA2,0x0000,0xDFA8,0x0003,0x0001,0xB5A3,0x0001,
+ 0xC7DC,0x0006,0x0003,0x0001,0xDFA7,0x0001,0xC7E6,0x0003,0x0000,0xB2D9,0x0002,0xB5B2,0xBBF7,0x000B,0x0004,0x0001,
+ 0x0000,0xD4F1,0x0004,0x0002,0xC9C3,0xC2B0,0x0001,0xC0DE,0x0008,0x0004,0x0002,0xD3B5,0xDFA6,0x0002,0xBCF1,0xCECE,
+ 0x0003,0x0001,0xBAB3,0x0002,0xCCA2,0xDFA5,0x001B,0x000D,0x0006,0x0003,0x0001,0xDFA3,0x0000,0xDFA2,0x0003,0x0000,
+ 0xC4EC,0x0002,0xDEEC,0xC6CB,0x0007,0x0004,0x0002,0xCDD6,0xD7AB,0x0001,0xB4E9,0x0004,0x0002,0xB2A5,0xC7CB,0x0000,
+ 0xB8A7,0x000D,0x0006,0x0003,0x0000,0xC1C3,0x0001,0xB3B6,0x0004,0x0002,0xB2A6,0xB3B7,0x0000,0xB5A7,0x0005,0x0001,
+ 0x0002,0xDED8,0xD7B2,0x0003,0x0001,0xD5FC,0x0001,0xC4ED,0x0026,0x0013,0x000D,0x0006,0x0003,0x0000,0xDFA4,0x0001,
+ 0xDEFE,0x0003,0x0000,0xCBBA,0x0002,0xC4D3,0xC8F6,0x0000,0x0000,0x0002,0xB3C5,0xB3C5,0x000D,0x0006,0x0003,0x0001,
+ 0xC0CC,0x0000,0xC6B2,0x0004,0x0002,0xBEEF,0xDEFC,0x0001,0xC1CC,0x0001,0x0001,0x0002,0xB2F4,0xD5DB,0x0013,0x000B,
+ 0x0007,0x0004,0x0002,0xC4A1,0xC3FE,0x0001,0xDED2,0x0001,0x0000,0xBFD9,0x0004,0x0001,0x0000,0xD6BF,0x0000,0x0000,
+ 0xDEFD,0x0007,0x0000,0x0003,0x0000,0xC4A6,0x0000,0xB4DD,0x0007,0x0003,0x0001,0xD7DC,0x0002,0xC2A7,0xDEFB,0x0000,
+ 0x0001,0xDEE8,0x02E8,0x0172,0x00B7,0x005B,0x002C,0x0012,0x0009,0x0004,0x0001,0x0000,0xD5AA,0x0001,0x0002,0xCBA4,
+ 0xB7EC,0x0005,0x0000,0x0002,0xDEF0,0xDEE2,0x0000,0x0000,0xC7F3,0x000F,0x0007,0x0003,0x0000,0xCCAF,0x0002,0xB1F7,
+ 0xD2A1,0x0004,0x0002,0xB0DA,0xDEF3,0x0002,0xC9E3,0xBFB8,0x0006,0x0003,0x0001,0xDEF4,0x0001,0xEBA1,0x0000,0x0002,
+ 0xD5A5,0xB2EB,0x0015,0x000B,0x0005,0x0000,0x0002,0xD0AF,0xCED5,0x0003,0x0000,0xC7C0,0x0000,0xE5BA,0x0004,0x0001,
+ 0x0001,0xCCCD,0x0003,0x0001,0xB4EE,0x0000,0xB0E1,0x000D,0x0006,0x0003,0x0000,0xCCC2,0x0000,0xCDD8,0x0004,0x0002,
+ 0xDEF9,0xB4B7,0x0000,0xB6F3,0x0006,0x0003,0x0001,0xDEFA,0x0000,0xDEF7,0x0003,0x0000,0xB8E3,0x0002,0xCBD1,0xDEF6,
+ 0x002C,0x0017,0x000B,0x0004,0x0001,0x0001,0xB5B7,0x0003,0x0000,0xD2A1,0x0002,0xC9A6,0xB4EA,0x0005,0x0001,0x0002,
+ 0xB4A4,0xB2AB,0x0003,0x0001,0xCBF0,0x0002,0xDEF8,0xDEF5,0x0009,0x0004,0x0000,0x0001,0xC8B6,0x0000,0x0002,0xB9B9,
+ 0xBDC1,0x0008,0x0004,0x0002,0xC2A7,0xB8E9,0x0002,0xB2F3,0xDEEC,0x0000,0x0001,0xC0BF,0x0019,0x000E,0x0006,0x0003,
+ 0x0001,0xB1B3,0x0000,0xDEEA,0x0004,0x0002,0xDEDE,0xDEE7,0x0002,0xD4AE,0xD0A8,0x0004,0x0000,0x0000,0xDEE9,0x0004,
+ 0x0002,0xBBD3,0xBDD2,0x0001,0xBEBE,0x0009,0x0005,0x0000,0x0002,0xBEBE,0xBFAB,0x0001,0x0001,0xB4A7,0x0006,0x0003,
+ 0x0001,0xCED5,0x0000,0xDEEB,0x0004,0x0002,0xDEEE,0xDFAC,0x0002,0xD1DA,0xBBBB,0x005D,0x002B,0x0014,0x0008,0x0004,
+ 0x0000,0x0000,0xD1EF,0x0000,0x0000,0xD2BE,0x0007,0x0003,0x0000,0xB2E5,0x0002,0xCCE1,0xC3E8,0x0000,0x0002,0xDEEF,
+ 0xD7E1,0x000C,0x0004,0x0000,0x0001,0xC8E0,0x0004,0x0002,0xDEF1,0xD1D0,0x0002,0xDEED,0xBCE3,0x0004,0x0001,0x0000,
+ 0xBCF0,0x0004,0x0002,0xDEF2,0xC5F6,0x0000,0xDEE8,0x0017,0x000C,0x0007,0x0003,0x0000,0xB2F4,0x0002,0xB5A7,0xD6C0,
+ 0x0001,0x0002,0xDEE2,0xC2B0,0x0004,0x0001,0x0000,0xEAFE,0x0004,0x0002,0xDEE7,0xDEDD,0x0000,0xDEE4,0x000E,0x0008,
+ 0x0004,0x0002,0xB4EB,0xD1DA,0x0002,0xCDC6,0xBFD8,0x0003,0x0001,0xBDD3,0x0001,0xB3B8,0x0007,0x0004,0x0002,0xCCBD,
+ 0xB2C9,0x0000,0xC2D3,0x0003,0x0000,0xD8DF,0x0001,0xB9D2,0x002F,0x0017,0x000A,0x0006,0x0003,0x0001,0xD5F5,0x0000,
+ 0xBEF2,0x0000,0x0000,0xD2B4,0x0007,0x0003,0x0000,0xC5C5,0x0002,0xC6FE,0xCCCD,0x0003,0x0000,0xDEE1,0x0000,0xD5C6,
+ 0x000D,0x0008,0x0004,0x0002,0xDEE5,0xB5F4,0x0002,0xCADA,0xB6DE,0x0001,0x0002,0xC2D5,0xC9A8,0x0006,0x0003,0x0000,
+ 0xB5E0,0x0000,0xCFC6,0x0001,0x0002,0xC5B2,0xC4ED,0x0017,0x000B,0x0006,0x0003,0x0000,0xDEE0,0x0001,0xBDDD,0x0000,
+ 0x0002,0xB4B7,0xDED3,0x0005,0x0000,0x0002,0xBEED,0xDEDF,0x0004,0x0002,0xBEDD,0xDEE3,0x0001,0xDED1,0x000D,0x0007,
+ 0x0003,0x0001,0xDEE6,0x0002,0xC9E1,0xC5F5,0x0003,0x0001,0xCDF3,0x0001,0xB5B7,0x0007,0x0004,0x0002,0xBBBB,0xBCF1,
+ 0x0001,0xCBF0,0x0000,0x0000,0xC0CC,0x00B3,0x0058,0x002E,0x0013,0x0007,0x0001,0x0003,0x0001,0xB9CE,0x0000,0xB2B6,
+ 0x0004,0x0001,0x0001,0xBEE8,0x0004,0x0002,0xC4F3,0xC9D3,0x0002,0xBAB4,0xB0C6,0x000D,0x0006,0x0003,0x0000,0xDEDB,
+ 0x0000,0xD7BD,0x0003,0x0001,0xC0A6,0x0002,0xCDB1,0xBEC8,0x0007,0x0004,0x0002,0xDEDC,0xCEE6,0x0000,0xBDC1,0x0003,
+ 0x0001,0xD0AE,0x0002,0xCDEC,0xC5B2,0x000F,0x0007,0x0000,0x0003,0x0001,0xCDA6,0x0000,0xDEDA,0x0004,0x0000,0x0001,
+ 0xEAFD,0x0000,0x0000,0xD5F1,0x000C,0x0007,0x0004,0x0002,0xB4EC,0xC5B2,0x0001,0xB0A4,0x0001,0x0002,0xBBD3,0xBCB7,
+ 0x0008,0x0004,0x0002,0xD5F5,0xDED8,0x0002,0xB5B2,0xC4D3,0x0004,0x0002,0xD0AE,0xCCA2,0x0000,0xCECE,0x002B,0x0014,
+ 0x0009,0x0005,0x0000,0x0002,0xC2CE,0xD6BF,0x0000,0x0001,0xCDDA,0x0005,0x0001,0x0002,0xCCF4,0xC4C3,0x0003,0x0001,
+ 0xBFE6,0x0000,0xD0A3,0x0009,0x0005,0x0001,0x0002,0xB0B4,0xEAFC,0x0000,0x0000,0xD6B8,0x0006,0x0003,0x0001,0xB9D2,
+ 0x0000,0xB3D6,0x0004,0x0002,0xC4C3,0xCAB0,0x0002,0xD7A7,0xC6B4,0x0019,0x000B,0x0004,0x0001,0x0000,0xC0AD,0x0004,
+ 0x0002,0xBFBD,0xDED9,0x0001,0xCBA9,0x0006,0x0003,0x0000,0xC8AD,0x0000,0xB9B0,0x0004,0x0002,0xD5FC,0xDED7,0x0002,
+ 0xCAC3,0xC0A8,0x000C,0x0005,0x0001,0x0002,0xD4F1,0xB2A6,0x0004,0x0002,0xC5A1,0xC0B9,0x0000,0xD3B5,0x0005,0x0000,
+ 0x0002,0xBCF0,0xC2A3,0x0003,0x0000,0xC4E2,0x0001,0xB0DD,0x0064,0x003B,0x001F,0x000F,0x0008,0x0004,0x0002,0xD5D0,
+ 0xDED5,0x0002,0xD7BE,0xBED0,0x0004,0x0002,0xDED6,0xCDCF,0x0001,0xB0CE,0x0008,0x0004,0x0002,0xCDD8,0xBEDC,0x0002,
+ 0xC7AF,0xB9D5,0x0004,0x0002,0xC4C3,0xC1E0,0x0002,0xC5C4,0xB0E8,0x0010,0x0008,0x0004,0x0002,0xC5D7,0xDED4,0x0002,
+ 0xC0AD,0xC4E9,0x0004,0x0002,0xC4B4,0xB2F0,0x0002,0xB5A3,0xD6F4,0x0004,0x0000,0x0001,0xB7F7,0x0004,0x0002,0xC3F2,
+ 0xBDD9,0x0002,0xB3E9,0xD1BA,0x0013,0x000B,0x0006,0x0003,0x0000,0xDED3,0x0000,0xC4A8,0x0001,0x0002,0xB5D6,0xD7A7,
+ 0x0004,0x0001,0x0000,0xB1A7,0x0001,0x0001,0xCCA7,0x000B,0x0006,0x0003,0x0000,0xC5FB,0x0001,0xC5EA,0x0001,0x0002,
+ 0xB1A8,0xBBA4,0x0007,0x0003,0x0001,0xC7C0,0x0002,0xC2D5,0xBFD9,0x0000,0x0000,0xDED2,0x002D,0x0016,0x000E,0x0007,
+ 0x0004,0x0002,0xC5D7,0xB8A7,0x0001,0xD5DB,0x0004,0x0002,0xBFB9,0xB6B6,0x0000,0xCDB6,0x0000,0x0004,0x0002,0xD7A5,
+ 0xCAE3,0x0000,0xD2D6,0x000C,0x0006,0x0003,0x0001,0xB0D1,0x0000,0xBEF1,0x0003,0x0000,0xBFDB,0x0001,0xB3AD,0x0004,
+ 0x0001,0x0001,0xBCBC,0x0004,0x0002,0xB3D0,0xD5D2,0x0001,0xB6F3,0x0019,0x000B,0x0007,0x0004,0x0002,0xE8CE,0xB5D6,
+ 0x0000,0xC5FA,0x0000,0x0001,0xB7F6,0x0006,0x0003,0x0000,0xB0E2,0x0001,0xC8C5,0x0004,0x0002,0xB3B6,0xB0E7,0x0002,
+ 0xC5A4,0xD1EF,0x000E,0x0007,0x0004,0x0002,0xC9A8,0xDED1,0x0000,0xC0A9,0x0004,0x0002,0xD6B4,0xC7A4,0x0001,0xBAB4,
+ 0x0005,0x0000,0x0002,0xBFDB,0xC7A4,0x0003,0x0001,0xDFA6,0x0001,0xCDD0,0x014B,0x00B4,0x005C,0x0030,0x0016,0x0009,
+ 0x0005,0x0002,0xBFB8,0x0001,0xB8ED,0x0000,0x0000,0xCDD0,0x0008,0x0004,0x0002,0xC8D3,0xB4F2,0x0002,0xB0C7,0xC6CB,
+ 0x0001,0x0002,0xD4FA,0xB2C5,0x000C,0x0007,0x0004,0x0002,0xDED0,0xCAD6,0x0001,0xECE9,0x0000,0x0002,0xECE8,0xC9C8,
+ 0x0006,0x0003,0x0001,0xECE7,0x0001,0xB1E2,0x0004,0x0002,0xCBF9,0xB7BF,0x0002,0xECE5,0xECE6,0x0014,0x0007,0x0001,
+ 0x0003,0x0001,0xBBA7,0x0000,0xBBA7,0x0007,0x0004,0x0002,0xB4F7,0xB4C1,0x0000,0xCFB7,0x0003,0x0000,0xD5BD,0x0000,
+ 0xC2BE,0x000D,0x0007,0x0003,0x0000,0xEAAF,0x0002,0xBDD8,0xEAAF,0x0003,0x0001,0xEAA8,0x0001,0xEAAD,0x0007,0x0003,
+ 0x0000,0xEAAE,0x0002,0xEAAB,0xEAAC,0x0000,0x0001,0xEAAA,0x0034,0x001D,0x000E,0x0006,0x0003,0x0001,0xEAA9,0x0000,
+ 0xC6DD,0x0004,0x0002,0xD5BD,0xEAA8,0x0002,0xBBF2,0xE3DE,0x0007,0x0003,0x0000,0xEAA7,0x0002,0xBDE4,0xCED2,0x0004,
+ 0x0002,0xB3C9,0xCFB7,0x0002,0xC8D6,0xCAF9,0x000F,0x0008,0x0004,0x0002,0xD0E7,0xEAA7,0x0002,0xCEEC,0xCEEC,0x0004,
+ 0x0002,0xB8EA,0xEDB0,0x0000,0xEDB0,0x0001,0x0004,0x0002,0xC1B5,0xDCB2,0x0000,0xC9E5,0x0013,0x000E,0x0006,0x0003,
+ 0x0000,0xBEE5,0x0000,0xE2E3,0x0004,0x0002,0xD0FC,0xBBB3,0x0002,0xC0C1,0xE3C2,0x0000,0x0001,0x0000,0xB3CD,0x0007,
+ 0x0001,0x0003,0x0000,0xE2FB,0x0000,0xC5B3,0x0004,0x0000,0x0001,0xEDAF,0x0003,0x0001,0xEDA1,0x0000,0xC3C9,0x0048,
+ 0x0023,0x000C,0x0001,0x0007,0x0003,0x0000,0xE3C1,0x0002,0xC0C1,0xEDAF,0x0001,0x0001,0xE3C1,0x000D,0x0008,0x0004,
+ 0x0002,0xE2F8,0xEDAE,0x0002,0xB0C3,0xD3A6,0x0000,0x0002,0xD0B8,0xBFD2,0x0006,0x0003,0x0001,0xC7DA,0x0000,0xB6AE,
+ 0x0001,0x0000,0xBAB6,0x0011,0x0007,0x0001,0x0003,0x0001,0xE3C0,0x0000,0xD2E4,0x0004,0x0001,0x0000,0xCFDC,0x0003,
+ 0x0001,0xB2D2,0x0000,0xE2E4,0x000C,0x0007,0x0004,0x0002,0xE3BD,0xC3F5,0x0001,0xEDAC,0x0000,0x0002,0xBAA9,0xE3BF,
+ 0x0004,0x0000,0x0000,0xB7DF,0x0001,0x0001,0xEDAD,0x0022,0x0013,0x0005,0x0000,0x0001,0x0000,0xB5AC,0x0007,0x0003,
+ 0x0000,0xE3BE,0x0002,0xE3B4,0xC6BE,0x0003,0x0000,0xC1AF,0x0002,0xD4F7,0xBDBE,0x0007,0x0000,0x0003,0x0001,0xB1EF,
+ 0x0000,0xB1B9,0x0004,0x0001,0x0000,0xD3C7,0x0001,0x0000,0xD3FB,0x0016,0x000B,0x0004,0x0000,0x0000,0xC6DD,0x0003,
+ 0x0001,0xBFB6,0x0002,0xC7EC,0xE3BC,0x0005,0x0000,0x0002,0xC9E5,0xE3A5,0x0003,0x0000,0xCEBF,0x0000,0xC2C7,0x000B,
+ 0x0006,0x0003,0x0001,0xCBCB,0x0000,0xE2E6,0x0000,0x0002,0xBFAE,0xBBDB,0x0006,0x0003,0x0001,0xB9DF,0x0000,0xC2FD,
+ 0x0003,0x0001,0xE2FA,0x0001,0xEDAB,0x00AE,0x004E,0x002E,0x0013,0x000A,0x0004,0x0001,0x0000,0xB2D1,0x0003,0x0000,
+ 0xB2D2,0x0001,0xC4BD,0x0004,0x0001,0x0001,0xC9E5,0x0001,0x0002,0xC9F7,0xE3B3,0x000F,0x0008,0x0004,0x0002,0xBBC5,
+ 0xCCAC,0x0002,0xE3BB,0xD0F3,0x0004,0x0002,0xB4C8,0xD2F3,0x0000,0xE8BA,0x0006,0x0003,0x0000,0xC0F5,0x0001,0xBBEC,
+ 0x0003,0x0001,0xD4B8,0x0000,0xE2E9,0x000A,0x0005,0x0001,0x0000,0x0001,0xE2FD,0x0000,0x0000,0x0000,0xE2EB,0x000C,
+ 0x0005,0x0000,0x0002,0xCBDF,0xE3BA,0x0004,0x0002,0xEDA8,0xC0A2,0x0000,0xE3B4,0x0005,0x0000,0x0002,0xB7DF,0xE3B6,
+ 0x0000,0x0002,0xE3B3,0xB8D0,0x0029,0x0017,0x000B,0x0007,0x0004,0x0002,0xE3AB,0xB0AE,0x0000,0xD3DE,0x0001,0x0001,
+ 0xE3B5,0x0004,0x0000,0x0001,0xCCE8,0x0004,0x0002,0xC3E5,0xD2E2,0x0002,0xE3B9,0xEDAA,0x000A,0x0004,0x0001,0x0001,
+ 0xD3E4,0x0003,0x0000,0xD3FA,0x0000,0xEDA9,0x0004,0x0001,0x0001,0xB3EE,0x0000,0x0000,0xE3B8,0x001E,0x000E,0x0007,
+ 0x0003,0x0001,0xE2FC,0x0002,0xD0CA,0xC8C7,0x0004,0x0002,0xDCE4,0xB4C0,0x0000,0xBBCC,0x0008,0x0004,0x0002,0xE3B7,
+ 0xCFEB,0x0002,0xE3A2,0xC4D5,0x0004,0x0002,0xB6E8,0xB9DF,0x0002,0xB5AC,0xB2D1,0x000E,0x0007,0x0004,0x0002,0xE3AB,
+ 0xB1B9,0x0001,0xB3CD,0x0004,0x0002,0xB2D2,0xBEE5,0x0000,0xB5EB,0x0004,0x0001,0x0001,0xB6F1,0x0004,0x0002,0xBBDD,
+ 0xCEA9,0x0001,0xE3AE,0x0057,0x0028,0x0018,0x000E,0x0008,0x0004,0x0002,0xCFA7,0xC3C6,0x0002,0xE3B1,0xEAA1,0x0003,
+ 0x0000,0xE3AF,0x0001,0xCCE8,0x0006,0x0003,0x0000,0xB5AD,0x0001,0xBBF3,0x0000,0x0001,0xC0B7,0x000B,0x0006,0x0003,
+ 0x0001,0xCDEF,0x0000,0xBEAA,0x0001,0x0002,0xE3B0,0xC7E9,0x0001,0x0001,0x0001,0xC6E0,0x0019,0x000C,0x0005,0x0000,
+ 0x0002,0xB5BF,0xE3AC,0x0003,0x0000,0xBCC2,0x0002,0xC3C6,0xE2EA,0x0007,0x0003,0x0000,0xE3B2,0x0002,0xB1AF,0xE3AD,
+ 0x0003,0x0001,0xC3F5,0x0001,0xE3A5,0x000B,0x0005,0x0000,0x0002,0xD0FC,0xEDA8,0x0003,0x0000,0xC4FA,0x0000,0xD4C3,
+ 0x0004,0x0000,0x0001,0xBBBC,0x0004,0x0002,0xD3C6,0xCEF2,0x0001,0xE3A6,0x002D,0x0016,0x000A,0x0006,0x0003,0x0001,
+ 0xE3AA,0x0000,0xE3A4,0x0001,0x0000,0xE3A3,0x0006,0x0003,0x0000,0xBBDA,0x0000,0xE3A8,0x0003,0x0000,0xCCE8,0x0001,
+ 0xBAB7,0x000C,0x0007,0x0003,0x0000,0xE3A9,0x0002,0xD5DC,0xCFA4,0x0001,0x0002,0xD4A5,0xD4C3,0x0005,0x0000,0x0002,
+ 0xC7C4,0xE3A7,0x0003,0x0001,0xD3C1,0x0001,0xE3A2,0x0019,0x000E,0x0008,0x0004,0x0002,0xC4D5,0xE2FC,0x0002,0xE2FD,
+ 0xE2FB,0x0003,0x0000,0xE2FA,0x0000,0xB6F1,0x0004,0x0000,0x0001,0xBFD2,0x0004,0x0002,0xC7A1,0xCFA2,0x0001,0xB9A7,
+ 0x000F,0x0008,0x0004,0x0002,0xCCF1,0xB6B2,0x0002,0xE3A1,0xB6F7,0x0004,0x0002,0xBADE,0xEDA4,0x0001,0xB3DC,0x0007,
+ 0x0004,0x0002,0xD0F4,0xEDA7,0x0000,0xBBD6,0x0001,0x0001,0xEDA2,0x150C,0x0A08,0x0515,0x02CC,0x0164,0x00B1,0x0052,
+ 0x0029,0x0012,0x0008,0x0004,0x0001,0x0001,0xEDA3,0x0000,0x0000,0xEDA6,0x0006,0x0003,0x0000,0xCBA1,0x0001,0xBAE3,
+ 0x0000,0x0001,0xBFD6,0x000C,0x0006,0x0003,0x0000,0xBBD0,0x0000,0xC1B5,0x0003,0x0000,0xD6BC,0x0001,0xBAE3,0x0005,
+ 0x0001,0x0002,0xCAD1,0xE2FE,0x0003,0x0000,0xEDA5,0x0000,0xE2F8,0x0011,0x0007,0x0000,0x0003,0x0001,0xEDA1,0x0000,
+ 0xD7DC,0x0006,0x0003,0x0000,0xE2F0,0x0000,0xBBD0,0x0001,0x0000,0xC7D3,0x000D,0x0005,0x0001,0x0002,0xE2F6,0xB9D6,
+ 0x0004,0x0002,0xE2F5,0xD4B9,0x0002,0xD0D4,0xE2F1,0x0004,0x0000,0x0000,0xBCB1,0x0004,0x0002,0xE2F9,0xB5A1,0x0001,
+ 0xE6A8,0x0035,0x001B,0x000F,0x0008,0x0004,0x0002,0xCBBC,0xC1AF,0x0002,0xE2F2,0xBDE3,0x0003,0x0000,0xE2EF,0x0002,
+ 0xCCFB,0xB2C0,0x0007,0x0004,0x0002,0xC5C2,0xD5FA,0x0001,0xC5AD,0x0001,0x0002,0xE2F3,0xD4F5,0x000A,0x0006,0x0003,
+ 0x0000,0xE2F4,0x0001,0xE2F7,0x0001,0x0001,0xE2EB,0x0008,0x0004,0x0002,0xE2EA,0xE2E6,0x0002,0xE2E4,0xCBCB,0x0004,
+ 0x0002,0xCCAC,0xBBB3,0x0002,0xB7DE,0xE2E9,0x0014,0x000A,0x0006,0x0003,0x0000,0xBAF6,0x0000,0xD0C3,0x0000,0x0001,
+ 0xE2EE,0x0004,0x0000,0x0000,0xC4EE,0x0003,0x0000,0xB3C0,0x0001,0xE2E5,0x000D,0x0007,0x0003,0x0000,0xE2ED,0x0002,
+ 0xBFEC,0xE2EC,0x0003,0x0001,0xCDE6,0x0000,0xD3C7,0x0004,0x0000,0x0001,0xE2E8,0x0000,0x0002,0xE2E7,0xD6D2,0x0051,
+ 0x002A,0x0017,0x000C,0x0004,0x0000,0x0000,0xE3C3,0x0004,0x0002,0xC3A6,0xCDFC,0x0002,0xD6BE,0xE2E2,0x0004,0x0001,
+ 0x0001,0xDFAF,0x0004,0x0002,0xECFD,0xECFE,0x0000,0xE2E3,0x000B,0x0005,0x0000,0x0002,0xC8CC,0xBCC9,0x0003,0x0000,
+ 0xE2E1,0x0001,0xD2E4,0x0000,0x0004,0x0002,0xB1D8,0xE2E0,0x0000,0xD0C4,0x0014,0x000B,0x0005,0x0000,0x0002,0xBBD5,
+ 0xE1E8,0x0003,0x0000,0xB3B9,0x0000,0xB5C2,0x0004,0x0000,0x0000,0xD5F7,0x0001,0x0002,0xF5E8,0xCEA2,0x000B,0x0006,
+ 0x0003,0x0000,0xE1E6,0x0001,0xD1AD,0x0000,0x0002,0xB8B4,0xE1E5,0x0001,0x0004,0x0002,0xD3F9,0xE1E2,0x0001,0xB4D3,
+ 0x0032,0x0016,0x000B,0x0004,0x0000,0x0001,0xE1E4,0x0004,0x0002,0xE1E3,0xC5C7,0x0000,0xB5C3,0x0006,0x0003,0x0000,
+ 0xE1E2,0x0001,0xCDBD,0x0000,0x0002,0xBEB6,0xD0EC,0x000E,0x0007,0x0003,0x0001,0xBAF3,0x0002,0xC2C9,0xBBB2,0x0004,
+ 0x0002,0xE1E0,0xBADC,0x0000,0xE1DF,0x0007,0x0004,0x0002,0xB4FD,0xBEB6,0x0001,0xE1DE,0x0004,0x0002,0xD5F7,0xCDF9,
+ 0x0000,0xB7F0,0x0017,0x000E,0x0006,0x0003,0x0001,0xB1CB,0x0000,0xB3B9,0x0004,0x0002,0xD2DB,0xE2EC,0x0002,0xE1DD,
+ 0xBCB3,0x0004,0x0001,0x0000,0xE1DC,0x0000,0x0002,0xD3B0,0xD5C3,0x000E,0x0008,0x0004,0x0002,0xC5ED,0xB1F2,0x0002,
+ 0xB5F1,0xB1EB,0x0003,0x0000,0xB2CA,0x0001,0xD1E5,0x0007,0x0004,0x0002,0xD1E5,0xCDAE,0x0001,0xD0CE,0x0000,0x0000,
+ 0xE1EA,0x00B0,0x0057,0x002B,0x0017,0x000A,0x0003,0x0000,0xD2CD,0x0003,0x0001,0xBBE3,0x0002,0xE5E9,0xE5E7,0x0007,
+ 0x0004,0x0002,0xE5E8,0xC2BC,0x0001,0xB5B1,0x0003,0x0000,0xB9E9,0x0000,0xE5E6,0x000C,0x0006,0x0003,0x0000,0xCDE4,
+ 0x0000,0xC3D6,0x0003,0x0000,0xC7BF,0x0000,0xB5AF,0x0004,0x0000,0x0000,0xB1F0,0x0001,0x0000,0xECB0,0x0017,0x000B,
+ 0x0004,0x0001,0x0000,0xE5F6,0x0004,0x0002,0xC7BF,0xB5AF,0x0001,0xC7BF,0x0006,0x0003,0x0001,0xD5C5,0x0001,0xE5F2,
+ 0x0003,0x0001,0xC8F5,0x0001,0xCDE4,0x000B,0x0004,0x0000,0x0001,0xE5F4,0x0004,0x0002,0xE5F2,0xE5F3,0x0001,0xBBA1,
+ 0x0005,0x0000,0x0002,0xCFD2,0xC3D6,0x0001,0x0002,0xD5C5,0xB5DC,0x0030,0x0019,0x000B,0x0006,0x0003,0x0001,0xB0D0,
+ 0x0001,0xB3DA,0x0001,0x0002,0xBAEB,0xB8A5,0x0007,0x0003,0x0001,0xD2FD,0x0002,0xB5F5,0xB9AD,0x0004,0x0002,0xDFB1,
+ 0xDFB1,0x0001,0xCABD,0x000A,0x0004,0x0001,0x0001,0xDFAE,0x0003,0x0000,0xB1D7,0x0000,0xDEC4,0x0005,0x0001,0x0002,
+ 0xC5AA,0xC6FA,0x0004,0x0002,0xD2EC,0xDBCD,0x0002,0xBFAA,0xD8A5,0x0012,0x000A,0x0004,0x0000,0x0000,0xDEC3,0x0003,
+ 0x0000,0xBDA8,0x0001,0xCDA2,0x0000,0x0003,0x0000,0xD1D3,0x0002,0xDBC8,0xCCFC,0x000B,0x0004,0x0001,0x0000,0xC2AE,
+ 0x0004,0x0002,0xE2DE,0xE2DE,0x0000,0xE2DD,0x0004,0x0001,0x0001,0xB9E3,0x0004,0x0002,0xB7CF,0xE2D0,0x0002,0xB3A7,
+ 0xC3ED,0x0059,0x002E,0x0019,0x000C,0x0006,0x0003,0x0001,0xD8CB,0x0001,0xE2DC,0x0003,0x0000,0xB3F8,0x0001,0xD5B1,
+ 0x0008,0x0004,0x0002,0xC1CE,0xD2F1,0x0002,0xC2A5,0xC0AA,0x0000,0x0002,0xE2DA,0xE2DB,0x000C,0x0005,0x0001,0x0002,
+ 0xF5F4,0xCBD1,0x0004,0x0002,0xC0C8,0xC1AE,0x0000,0xCFC3,0x0004,0x0001,0x0000,0xBEC7,0x0000,0x0002,0xCFE1,0xB2DE,
+ 0x0013,0x000B,0x0004,0x0000,0x0000,0xE2D7,0x0003,0x0001,0xE2D5,0x0002,0xD3B9,0xBFB5,0x0000,0x0004,0x0002,0xCAFC,
+ 0xE2D6,0x0001,0xE2D8,0x000C,0x0006,0x0003,0x0001,0xCDA5,0x0001,0xBFE2,0x0003,0x0000,0xE2D1,0x0001,0xD7F9,0x0007,
+ 0x0004,0x0002,0xB6C8,0xE2D3,0x0000,0xD6C5,0x0001,0x0002,0xE2D4,0xB7CF,0x0033,0x001C,0x000D,0x0006,0x0003,0x0000,
+ 0xC5D3,0x0000,0xB8AE,0x0004,0x0002,0xB8FD,0xC3ED,0x0001,0xB5EA,0x0008,0x0004,0x0002,0xE2D2,0xB5D7,0x0002,0xD3A6,
+ 0xBFE2,0x0003,0x0001,0xE2D0,0x0002,0xC2AE,0xD0F2,0x000A,0x0004,0x0001,0x0001,0xE2D1,0x0003,0x0000,0xB4B2,0x0001,
+ 0xB1D3,0x0006,0x0003,0x0000,0xC7EC,0x0000,0xD7AF,0x0003,0x0000,0xB2E0,0x0002,0xE2CF,0xB9E3,0x001B,0x000F,0x0008,
+ 0x0004,0x0002,0xBCB8,0xD3C4,0x0002,0xD3D7,0xBBC3,0x0004,0x0002,0xE7DB,0xB8C9,0x0000,0xD0D2,0x0008,0x0004,0x0002,
+ 0xB2A2,0xBFAA,0x0002,0xC4EA,0xC6BD,0x0000,0x0000,0xB8C9,0x0006,0x0000,0x0001,0x0002,0xE0FC,0xB0EF,0x0004,0x0001,
+ 0x0001,0xB1D2,0x0004,0x0002,0xB4B1,0xE1A6,0x0001,0xD6C4,0x011C,0x00AF,0x004D,0x0025,0x0014,0x000C,0x0005,0x0002,
+ 0xE1A5,0x0001,0xBEB0,0x0003,0x0000,0xE1A4,0x0002,0xC4BB,0xE0FD,0x0000,0x0003,0x0000,0xE0FE,0x0002,0xC4BB,0xE1A3,
+ 0x0005,0x0000,0x0001,0x0001,0xBBCF,0x0005,0x0001,0x0002,0xB7F9,0xE1A2,0x0004,0x0002,0xE0F8,0xC3DD,0x0001,0xD6A1,
+ 0x0016,0x000B,0x0005,0x0001,0x0002,0xC3B1,0xE0FE,0x0003,0x0000,0xE0FD,0x0001,0xB3A3,0x0005,0x0000,0x0002,0xE1A1,
+ 0xB4F8,0x0003,0x0000,0xD5CA,0x0000,0xE0FC,0x000A,0x0006,0x0003,0x0001,0xB0EF,0x0000,0xCFAF,0x0000,0x0000,0xCAA6,
+ 0x0000,0x0004,0x0002,0xD6A1,0xB4F8,0x0000,0xCBA7,0x0030,0x0019,0x000D,0x0005,0x0001,0x0002,0xB5DB,0xD6C4,0x0004,
+ 0x0002,0xB2AF,0xD6E3,0x0002,0xE0F9,0xC1B1,0x0007,0x0003,0x0001,0xCCFB,0x0002,0xC5C1,0xE0FA,0x0001,0x0002,0xE0FB,
+ 0xD5CA,0x000A,0x0006,0x0003,0x0000,0xE0F8,0x0001,0xCFA3,0x0001,0x0001,0xCAA6,0x0006,0x0003,0x0001,0xB7AB,0x0000,
+ 0xCBA7,0x0004,0x0002,0xB2BC,0xCAD0,0x0000,0xB1D2,0x0019,0x000B,0x0007,0x0004,0x0002,0xCAD0,0xBDED,0x0000,0xD9E3,
+ 0x0001,0x0000,0xDAE1,0x0006,0x0003,0x0000,0xCFEF,0x0001,0xB0CD,0x0004,0x0002,0xCBC8,0xD2D1,0x0002,0xBCBA,0xDBCF,
+ 0x000C,0x0005,0x0000,0x0002,0xDBCF,0xB2EE,0x0003,0x0000,0xCED7,0x0002,0xB9AE,0xBEDE,0x0007,0x0004,0x0002,0xC7C9,
+ 0xD7F3,0x0000,0xB9A4,0x0003,0x0001,0xB3B2,0x0000,0xD1B2,0x003C,0x0021,0x0014,0x000A,0x0006,0x0003,0x0001,0xD6DD,
+ 0x0000,0xB4A8,0x0000,0x0000,0xE7DD,0x0006,0x0003,0x0001,0xD1D2,0x0001,0xE1DB,0x0000,0x0001,0xC2CD,0x0008,0x0004,
+ 0x0001,0x0000,0xCEA1,0x0000,0x0000,0xBFF9,0x0000,0x0001,0x0000,0xE1DB,0x0012,0x000B,0x0005,0x0001,0x0002,0xD4C0,
+ 0xD3EC,0x0003,0x0001,0xC1EB,0x0001,0xE1C9,0x0000,0x0003,0x0000,0xE1DA,0x0001,0xE1AE,0x0001,0x0004,0x0000,0x0000,
+ 0xE1BB,0x0001,0x0001,0xE1BD,0x001E,0x000D,0x0008,0x0004,0x0001,0x0000,0xE1D8,0x0001,0x0000,0xE1D7,0x0000,0x0000,
+ 0x0000,0xE1C0,0x0005,0x0001,0x0000,0x0001,0xB4DD,0x0006,0x0003,0x0000,0xE1AB,0x0001,0xD5B8,0x0003,0x0001,0xE1D6,
+ 0x0000,0xE1D0,0x0006,0x0001,0x0000,0x0001,0x0001,0xE1D5,0x0000,0x0006,0x0003,0x0000,0xE1CF,0x0001,0xE1CD,0x0003,
+ 0x0000,0xE1D1,0x0000,0xE1D4,0x0090,0x0040,0x001F,0x0011,0x000A,0x0004,0x0001,0x0000,0xE1D0,0x0003,0x0000,0xE1CE,
+ 0x0001,0xE1C9,0x0001,0x0003,0x0001,0xD1D2,0x0001,0xE1B0,0x0009,0x0004,0x0001,0x0001,0xC7B6,0x0000,0x0002,0xE1D2,
+ 0xE1D3,0x0000,0x0000,0x0000,0xEFFA,0x000C,0x0007,0x0000,0x0003,0x0001,0xE1CA,0x0000,0xE1CC,0x0000,0x0001,0x0001,
+ 0xE1CB,0x000A,0x0006,0x0003,0x0001,0xE1C4,0x0000,0xD5B8,0x0001,0x0000,0xB1C0,0x0007,0x0004,0x0002,0xE1C2,0xE1C3,
+ 0x0001,0xE1C5,0x0000,0x0001,0xE1BF,0x0027,0x0016,0x000B,0x0004,0x0000,0x0001,0xE1C6,0x0004,0x0002,0xE1C8,0xBEFE,
+ 0x0000,0xC2D8,0x0007,0x0004,0x0002,0xB8DA,0xD1C2,0x0001,0xB4DE,0x0001,0x0000,0xC0A5,0x0007,0x0000,0x0003,0x0001,
+ 0xC6E9,0x0000,0xE1C1,0x0005,0x0000,0x0002,0xB3E7,0xE1C7,0x0000,0x0002,0xE1C1,0xE1C0,0x0014,0x000A,0x0006,0x0003,
+ 0x0000,0xF6B9,0x0000,0xCFBF,0x0000,0x0000,0xBEFE,0x0006,0x0003,0x0001,0xB5BA,0x0001,0xE1AD,0x0001,0x0001,0xB7E5,
+ 0x000A,0x0004,0x0001,0x0000,0xC7CD,0x0003,0x0001,0xD3F8,0x0001,0xB6EB,0x0007,0x0003,0x0001,0xC2CD,0x0002,0xE1BF,
+ 0xE1BD,0x0001,0x0000,0xCFBF,0x004A,0x001B,0x000D,0x0008,0x0004,0x0000,0x0001,0xE1CD,0x0001,0x0000,0xD6C5,0x0001,
+ 0x0000,0x0001,0xE1BC,0x0006,0x0001,0x0000,0x0002,0xE1BE,0xB8B7,0x0004,0x0001,0x0001,0xE1BB,0x0001,0x0000,0xE1B9,
+ 0x0016,0x000A,0x0006,0x0003,0x0000,0xBFF9,0x0000,0xE1B4,0x0001,0x0001,0xB0B6,0x0006,0x0003,0x0000,0xE1BA,0x0000,
+ 0xE1B2,0x0003,0x0000,0xD4C0,0x0000,0xE1B7,0x000E,0x0007,0x0003,0x0001,0xCDD3,0x0002,0xC1EB,0xE1B5,0x0003,0x0000,
+ 0xE1B6,0x0002,0xD1D2,0xF6B4,0x0004,0x0001,0x0000,0xDAE9,0x0004,0x0002,0xE1B8,0xE1B3,0x0000,0xB8D4,0x002A,0x001A,
+ 0x000E,0x0006,0x0003,0x0000,0xCFBF,0x0001,0xE1B1,0x0004,0x0002,0xB5BA,0xE1B0,0x0002,0xE1AE,0xE1AD,0x0007,0x0004,
+ 0x0002,0xB8DA,0xE1AB,0x0001,0xB2ED,0x0001,0x0002,0xE1AF,0xE1AA,0x0009,0x0005,0x0001,0x0002,0xE1A9,0xE1A7,0x0001,
+ 0x0001,0xE1AC,0x0001,0x0003,0x0001,0xC6F1,0x0000,0xCBEA,0x000F,0x000A,0x0004,0x0000,0x0000,0xD3EC,0x0003,0x0001,
+ 0xE1A8,0x0000,0xD2D9,0x0001,0x0001,0x0000,0xC9BD,0x000B,0x0007,0x0004,0x0002,0xCDCD,0xE5F8,0x0001,0xCAF4,0x0001,
+ 0x0001,0xE5F0,0x0007,0x0003,0x0001,0xE5F0,0x0002,0xC2C4,0xB2E3,0x0004,0x0002,0xE5EF,0xC2C5,0x0002,0xC2C5,0xCDC0,
+ 0x0284,0x0189,0x00C3,0x005B,0x002E,0x0012,0x000A,0x0004,0x0001,0x0000,0xCAF4,0x0003,0x0000,0xCCEB,0x0001,0xE5ED,
+ 0x0004,0x0001,0x0001,0xD5B9,0x0001,0x0001,0xD0BC,0x000F,0x0008,0x0004,0x0002,0xE5EC,0xC6C1,0x0002,0xCABA,0xCAAC,
+ 0x0003,0x0001,0xCEDD,0x0002,0xBDEC,0xCCEB,0x0008,0x0004,0x0002,0xC7FC,0xBDEC,0x0002,0xBDEC,0xBED3,0x0001,0x0002,
+ 0xB2E3,0xC6A8,0x001B,0x0010,0x0008,0x0004,0x0002,0xBED6,0xC4F2,0x0002,0xCEB2,0xBEA1,0x0004,0x0002,0xC4E1,0xE5EA,
+ 0x0002,0xB3DF,0xD2FC,0x0005,0x0000,0x0002,0xCAAC,0xDECF,0x0003,0x0000,0xDECF,0x0001,0xBECD,0x0005,0x0001,0x0000,
+ 0x0000,0xDECE,0x0007,0x0004,0x0002,0xC5D3,0xD2A2,0x0001,0xDECD,0x0003,0x0000,0xD3C8,0x0000,0xDECC,0x0036,0x001A,
+ 0x000C,0x0006,0x0003,0x0001,0xCFCA,0x0001,0xB3A2,0x0003,0x0000,0xE6D9,0x0000,0xC9D0,0x0007,0x0003,0x0000,0xB3BE,
+ 0x0002,0xBCE2,0xE6D8,0x0003,0x0000,0xB6FB,0x0002,0xB6FB,0xC9D9,0x000E,0x0007,0x0003,0x0001,0xD0A1,0x0002,0xB5BC,
+ 0xB6D4,0x0003,0x0001,0xD1B0,0x0002,0xD7F0,0xCEBE,0x0007,0x0004,0x0002,0xD7A8,0xBDAB,0x0000,0xBDAB,0x0004,0x0002,
+ 0xC9E4,0xB7F3,0x0001,0xB7E2,0x0014,0x000C,0x0004,0x0000,0x0001,0xCAD9,0x0004,0x0002,0xB5BC,0xD1B0,0x0002,0xCBC2,
+ 0xB6D4,0x0000,0x0003,0x0000,0xB4E7,0x0002,0xB1A6,0xB3E8,0x000E,0x0007,0x0004,0x0002,0xE5BE,0xBFA1,0x0000,0xE5BC,
+ 0x0004,0x0002,0xBFED,0xD0B4,0x0001,0xC9F3,0x0008,0x0004,0x0002,0xD5AF,0xC4FE,0x0002,0xCAB5,0xC1C8,0x0004,0x0002,
+ 0xE5BB,0xCEF2,0x0002,0xC7DE,0xB9D1,0x0064,0x0031,0x001A,0x000C,0x0008,0x0004,0x0002,0xF1C0,0xB2EC,0x0002,0xC4AF,
+ 0xC7DE,0x0001,0x0001,0xF1C1,0x0006,0x0003,0x0000,0xD6C3,0x0000,0xBDFE,0x0004,0x0002,0xCAB5,0xD4A2,0x0002,0xBAAE,
+ 0xC7DE,0x000A,0x0006,0x0003,0x0000,0xC3C2,0x0001,0xC4FE,0x0000,0x0000,0xB8BB,0x0007,0x0003,0x0001,0xBFDC,0x0002,
+ 0xC3DC,0xD2FA,0x0003,0x0000,0xBCC4,0x0000,0xBCC5,0x0018,0x000C,0x0008,0x0004,0x0002,0xB2C9,0xCBDE,0x0002,0xB1F6,
+ 0xBFED,0x0001,0x0001,0xC8DD,0x0007,0x0003,0x0000,0xE5B7,0x0002,0xBCD2,0xCFFC,0x0000,0x0002,0xD1E7,0xBAA6,0x000C,
+ 0x0006,0x0003,0x0000,0xD4D7,0x0000,0xB9AC,0x0003,0x0001,0xB9AC,0x0000,0xCFDC,0x0007,0x0003,0x0001,0xBBC2,0x0002,
+ 0xBBC2,0xE5B6,0x0004,0x0002,0xCAD2,0xD0FB,0x0002,0xBFCD,0xC9F3,0x0033,0x001A,0x000F,0x0007,0x0003,0x0000,0xB3E8,
+ 0x0002,0xCAB5,0xB1A6,0x0004,0x0002,0xD2CB,0xCDF0,0x0002,0xB6A8,0xD6E6,0x0007,0x0004,0x0002,0xB9D9,0xD7DA,0x0001,
+ 0xE5B4,0x0000,0x0001,0xE5B5,0x000B,0x0004,0x0000,0x0001,0xBAEA,0x0004,0x0002,0xCDEA,0xCBCE,0x0001,0xB0B2,0x0007,
+ 0x0004,0x0002,0xCAD8,0xD3EE,0x0001,0xD5AC,0x0004,0x0002,0xE5B3,0xCBFC,0x0001,0xC4FE,0x0017,0x000B,0x0007,0x0004,
+ 0x0002,0xE5B2,0xC2CF,0x0001,0xC4F5,0x0001,0x0000,0xC8E6,0x0006,0x0003,0x0000,0xD1A7,0x0001,0xB7F5,0x0003,0x0001,
+ 0xE6DC,0x0001,0xE5EE,0x000C,0x0004,0x0000,0x0000,0xCAEB,0x0004,0x0002,0xD8AB,0xCBEF,0x0002,0xC2CF,0xBAA2,0x0005,
+ 0x0001,0x0002,0xD1A7,0xE6DB,0x0004,0x0002,0xB9C2,0xBCBE,0x0000,0xE6DF,0x007F,0x0046,0x0028,0x001A,0x000C,0x0004,
+ 0x0001,0x0000,0xC3CF,0x0004,0x0002,0xD0A2,0xD7CE,0x0002,0xD8C3,0xE6DA,0x0007,0x0004,0x0002,0xCBEF,0xB4E6,0x0000,
+ 0xD7D6,0x0004,0x0002,0xD4D0,0xBFD7,0x0000,0xE6DE,0x0009,0x0005,0x0000,0x0002,0xE6DD,0xD7D3,0x0000,0x0001,0xE6AE,
+ 0x0001,0x0001,0x0000,0xC4EF,0x0011,0x0005,0x0000,0x0000,0x0001,0xE6D7,0x0006,0x0003,0x0001,0xC9F4,0x0000,0xE6D6,
+ 0x0003,0x0001,0xD9F8,0x0001,0xE6D5,0x0008,0x0004,0x0000,0x0001,0xD3A4,0x0001,0x0001,0xE6C9,0x0001,0x0000,0x0001,
+ 0xE6D6,0x001D,0x0010,0x0008,0x0004,0x0000,0x0000,0xE6C8,0x0000,0x0000,0xF4C1,0x0000,0x0003,0x0000,0xE6CD,0x0002,
+ 0xE6D3,0xE6D4,0x0007,0x0001,0x0003,0x0001,0xBDBF,0x0000,0xE6BF,0x0000,0x0000,0x0002,0xE6D2,0xE6AC,0x000A,0x0005,
+ 0x0001,0x0001,0x0000,0xE6B5,0x0001,0x0000,0x0000,0xE5FC,0x0008,0x0004,0x0000,0x0000,0xE6CD,0x0001,0x0000,0xE6C6,
+ 0x0006,0x0003,0x0000,0xC4DB,0x0001,0xE6CF,0x0001,0x0000,0xE6CC,0x004D,0x0029,0x001A,0x000D,0x0007,0x0004,0x0002,
+ 0xB5D5,0xE6CB,0x0000,0xEAC7,0x0003,0x0001,0xE6D1,0x0001,0xC2FD,0x0007,0x0003,0x0001,0xE6D0,0x0002,0xE5FD,0xE6CE,
+ 0x0003,0x0001,0xE6C9,0x0001,0xE6C8,0x0007,0x0001,0x0003,0x0001,0xCFD3,0x0000,0xF4C1,0x0004,0x0000,0x0000,0xBCB5,
+ 0x0001,0x0001,0xC9A9,0x0016,0x000B,0x0006,0x0003,0x0000,0xBCDE,0x0001,0xE6C5,0x0000,0x0002,0xC2E8,0xE6C1,0x0004,
+ 0x0000,0x0001,0xE6CA,0x0003,0x0000,0xEBF4,0x0002,0xCFB1,0xE6C7,0x0009,0x0005,0x0001,0x0002,0xE6A3,0xCDB5,0x0001,
+ 0x0001,0xE6C1,0x0000,0x0001,0x0000,0xE6B4,0x000C,0x0000,0x0006,0x0001,0x0001,0x0002,0xE6C2,0xC3C4,0x0001,0x0001,
+ 0x0001,0xC3BD,0x0012,0x0008,0x0004,0x0001,0x0000,0xD0F6,0x0001,0x0001,0xE6C4,0x0005,0x0001,0x0002,0xE6C3,0xC9F4,
+ 0x0000,0x0002,0xE6BF,0xD3A4,0x0008,0x0001,0x0004,0x0002,0xE6AB,0xD2F9,0x0001,0xC0B7,0x0005,0x0001,0x0002,0xE6BA,
+ 0xB8BE,0x0001,0x0001,0xE6BE,0x012C,0x0089,0x003D,0x0019,0x000B,0x0001,0x0006,0x0003,0x0000,0xBBE9,0x0001,0xC4DD,
+ 0x0000,0x0001,0xE6BC,0x0005,0x0000,0x0001,0x0000,0xE6B9,0x0005,0x0000,0x0002,0xE6BB,0xCDF1,0x0000,0x0000,0xC6C5,
+ 0x0011,0x000A,0x0006,0x0003,0x0001,0xC2A6,0x0000,0xE6B9,0x0001,0x0000,0xE6BD,0x0001,0x0003,0x0000,0xC8A2,0x0000,
+ 0xE6B5,0x0009,0x0005,0x0000,0x0002,0xE6B4,0xD3E9,0x0000,0x0001,0xE6D2,0x0004,0x0000,0x0001,0xC3E4,0x0003,0x0001,
+ 0xB6F0,0x0001,0xE6B7,0x0029,0x0012,0x000A,0x0005,0x0001,0x0002,0xC9EF,0xBEEA,0x0001,0x0002,0xC4C8,0xD3E9,0x0004,
+ 0x0001,0x0000,0xC4EF,0x0001,0x0001,0xE6B8,0x0008,0x0004,0x0000,0x0001,0xE6B6,0x0001,0x0000,0xE6B2,0x0007,0x0003,
+ 0x0001,0xE6B3,0x0002,0xE6AE,0xBDBF,0x0004,0x0002,0xE6AC,0xE6AB,0x0002,0xC2A6,0xCDDE,0x0011,0x000A,0x0006,0x0003,
+ 0x0001,0xCDFE,0x0001,0xD7CB,0x0001,0x0001,0xD2F6,0x0000,0x0003,0x0001,0xE6B1,0x0001,0xD9A7,0x0005,0x0001,0x0001,
+ 0x0000,0xBCA7,0x0006,0x0003,0x0000,0xD6B6,0x0000,0xD2CC,0x0004,0x0002,0xBCE9,0xC0D1,0x0001,0xE6AF,0x0053,0x0028,
+ 0x0013,0x0007,0x0001,0x0003,0x0001,0xE6AD,0x0000,0xBDAA,0x0007,0x0003,0x0000,0xD2A6,0x0002,0xE6B0,0xE6A9,0x0001,
+ 0x0002,0xCEAF,0xD0D5,0x000D,0x0007,0x0004,0x0002,0xE6A6,0xB9C3,0x0000,0xBDE3,0x0003,0x0001,0xE6A9,0x0001,0xCABC,
+ 0x0004,0x0000,0x0000,0xE6A2,0x0000,0x0000,0xC4B7,0x0010,0x0007,0x0001,0x0003,0x0000,0xE6AA,0x0001,0xC6DE,0x0005,
+ 0x0000,0x0002,0xC3C3,0xC3C3,0x0001,0x0001,0xC4E3,0x000C,0x0006,0x0003,0x0000,0xE6A7,0x0001,0xE6A8,0x0003,0x0000,
+ 0xC4DD,0x0001,0xE6A3,0x0007,0x0004,0x0002,0xE5FD,0xE5FC,0x0000,0xB7C1,0x0004,0x0002,0xB7E1,0xCDD7,0x0002,0xE6A5,
+ 0xE5FE,0x0028,0x0012,0x0006,0x0001,0x0000,0x0002,0xE6A4,0xD7B1,0x0006,0x0003,0x0001,0xC3EE,0x0001,0xE6A1,0x0003,
+ 0x0000,0xD1FD,0x0001,0xBCCB,0x0008,0x0004,0x0000,0x0000,0xB6CA,0x0000,0x0001,0xE5FB,0x0007,0x0003,0x0000,0xC8D1,
+ 0x0002,0xC2E8,0xB8BE,0x0003,0x0000,0xD7B1,0x0002,0xCDFD,0xE5FA,0x0018,0x000B,0x0005,0x0000,0x0002,0xC8E7,0xE5F9,
+ 0x0003,0x0001,0xBAC3,0x0000,0xE6B1,0x0006,0x0003,0x0001,0xCBFD,0x0000,0xBCE9,0x0003,0x0000,0xC4CC,0x0002,0xC5AB,
+ 0xC5AE,0x0005,0x0001,0x0000,0x0000,0xB7DC,0x0007,0x0004,0x0002,0xB6E1,0xDEC6,0x0001,0xB0C2,0x0000,0x0001,0xB0C2,
+ 0x00B1,0x005F,0x002E,0x0016,0x0007,0x0000,0x0003,0x0000,0xC9DD,0x0000,0xB5EC,0x0007,0x0003,0x0000,0xDEC9,0x0002,
+ 0xDECA,0xCCD7,0x0004,0x0002,0xBDB1,0xDEC8,0x0002,0xB1BC,0xC9DD,0x000D,0x0007,0x0003,0x0001,0xC6F5,0x0002,0xDBBC,
+ 0xD7E0,0x0003,0x0000,0xBFFC,0x0001,0xB7DC,0x0007,0x0003,0x0001,0xB7EE,0x0002,0xC4CE,0xC6E6,0x0001,0x0000,0xD1D9,
+ 0x0017,0x000B,0x0005,0x0000,0x0002,0xDBBC,0xDEC6,0x0003,0x0000,0xBCD0,0x0000,0xDEC5,0x0008,0x0004,0x0002,0xB6E1,
+ 0xBCD0,0x0002,0xBFE4,0xD2C4,0x0001,0x0000,0xCDB7,0x000D,0x0006,0x0003,0x0001,0xCAA7,0x0001,0xBABB,0x0004,0x0002,
+ 0xD1EB,0xD8B2,0x0001,0xB7F2,0x0007,0x0004,0x0002,0xCCAB,0xCCEC,0x0001,0xB4F3,0x0003,0x0001,0xE2B7,0x0000,0xE2B9,
+ 0x0027,0x0017,0x000B,0x0007,0x0003,0x0000,0xC3CE,0x0002,0xB9BB,0xB9BB,0x0001,0x0000,0xD2B9,0x0005,0x0000,0x0002,
+ 0xB6E0,0xD9ED,0x0004,0x0002,0xCDE2,0xCFA6,0x0000,0xD9E7,0x0008,0x0004,0x0001,0x0001,0xCFC4,0x0000,0x0001,0xB8B4,
+ 0x0004,0x0001,0x0001,0xB1B8,0x0001,0x0000,0xB4A6,0x0017,0x000C,0x0006,0x0003,0x0000,0xE2BA,0x0001,0xB6D7,0x0003,
+ 0x0001,0xCAD9,0x0000,0xBAF8,0x0005,0x0000,0x0002,0xBAF8,0xD2BC,0x0003,0x0000,0xBAF8,0x0001,0xBFC7,0x000C,0x0005,
+ 0x0001,0x0002,0xC9F9,0xD7B3,0x0003,0x0000,0xD7B3,0x0002,0xC8C9,0xCABF,0x0004,0x0000,0x0001,0xB0D3,0x0001,0x0000,
+ 0xC8C0,0x0048,0x0027,0x0017,0x000A,0x0006,0x0003,0x0000,0xDBDE,0x0001,0xC2A2,0x0000,0x0000,0xBBB5,0x0007,0x0004,
+ 0x0002,0xDBE4,0xDBDB,0x0000,0xC0DD,0x0003,0x0001,0xBABE,0x0001,0xD1B9,0x0008,0x0004,0x0000,0x0001,0xDBD6,0x0000,
+ 0x0000,0xDBF7,0x0004,0x0001,0x0001,0xCCB3,0x0000,0x0001,0xDBD5,0x000F,0x000A,0x0004,0x0000,0x0001,0xB1DA,0x0003,
+ 0x0000,0xBFD1,0x0000,0xDBD4,0x0001,0x0001,0x0001,0xB7D8,0x000B,0x0004,0x0000,0x0001,0xEDE3,0x0003,0x0000,0xB6E9,
+ 0x0002,0xB5D8,0xB6D7,0x0000,0x0003,0x0001,0xB6D5,0x0000,0xC4AB,0x0027,0x0014,0x000B,0x0004,0x0001,0x0001,0xD0E6,
+ 0x0004,0x0002,0xD4F6,0xEDCD,0x0000,0xD7B9,0x0005,0x0000,0x0002,0xDCAE,0xC7BD,0x0001,0x0001,0xC4B9,0x0008,0x0004,
+ 0x0000,0x0000,0xC9CA,0x0000,0x0000,0xB9F9,0x0005,0x0000,0x0002,0xB5E6,0xDCAD,0x0003,0x0001,0xCAFB,0x0001,0xBEB3,
+ 0x0016,0x000C,0x0006,0x0003,0x0001,0xDCAC,0x0000,0xDCAF,0x0003,0x0000,0xDBD3,0x0000,0xD7A9,0x0004,0x0000,0x0001,
+ 0xC7B5,0x0003,0x0001,0xB3BE,0x0000,0xDCA1,0x0006,0x0001,0x0001,0x0002,0xDCAB,0xCCEE,0x0001,0x0003,0x0001,0xDCAA,
+ 0x0000,0xDBF7,0x0562,0x0287,0x013D,0x0099,0x004B,0x0029,0x0013,0x0007,0x0003,0x0000,0xCEEB,0x0000,0x0001,0xC8FB,
+ 0x0006,0x0003,0x0001,0xDAA3,0x0001,0xCCC1,0x0003,0x0000,0xCDBF,0x0001,0xCBFE,0x000D,0x0006,0x0003,0x0001,0xDBF5,
+ 0x0000,0xCBDC,0x0003,0x0000,0xDBEE,0x0002,0xEBF3,0xCBFA,0x0005,0x0000,0x0002,0xDCE3,0xBFE9,0x0001,0x0001,0xDCA8,
+ 0x000D,0x0007,0x0001,0x0003,0x0000,0xBCEE,0x0000,0xB8D4,0x0001,0x0001,0x0002,0xB6C2,0xB3A1,0x000D,0x0007,0x0003,
+ 0x0001,0xBCB2,0x0002,0xB1A8,0xD1DF,0x0003,0x0000,0xD2A2,0x0000,0xBBCA,0x0004,0x0000,0x0001,0xBFB0,0x0001,0x0001,
+ 0xB5CC,0x0027,0x0013,0x000B,0x0005,0x0001,0x0002,0xB1A4,0xDCA9,0x0003,0x0001,0xDCA6,0x0000,0xDBF6,0x0004,0x0001,
+ 0x0000,0xDCA7,0x0001,0x0000,0xB6E9,0x0008,0x0004,0x0001,0x0000,0xC7B5,0x0001,0x0000,0xDCA2,0x0005,0x0000,0x0002,
+ 0xDCA1,0xDBD1,0x0004,0x0002,0xDDC0,0xB6D1,0x0000,0xBCE1,0x0018,0x000A,0x0006,0x0003,0x0001,0xCCC3,0x0001,0xDCA5,
+ 0x0001,0x0000,0xDCA3,0x0007,0x0003,0x0001,0xBBF9,0x0002,0xC5E0,0xDBFC,0x0004,0x0002,0xD6B4,0xD6B4,0x0001,0xDBFA,
+ 0x000A,0x0004,0x0000,0x0000,0xBFB2,0x0003,0x0000,0xDBFB,0x0000,0xDCA4,0x0001,0x0001,0x0001,0xDBFD,0x0051,0x0029,
+ 0x0018,0x000C,0x0005,0x0001,0x0002,0xDBEB,0xB2BA,0x0003,0x0000,0xD3F2,0x0002,0xDBFE,0xD2B0,0x0007,0x0003,0x0001,
+ 0xDBF6,0x0002,0xDBF7,0xDBF5,0x0001,0x0002,0xDBF4,0xC6D2,0x0009,0x0004,0x0000,0x0001,0xDBF8,0x0000,0x0002,0xDBEF,
+ 0xB3C7,0x0004,0x0000,0x0000,0xC2F1,0x0000,0x0000,0xF0AE,0x0011,0x0006,0x0000,0x0000,0x0002,0xB0A3,0xB9A1,0x0006,
+ 0x0003,0x0001,0xDBAE,0x0001,0xDBF9,0x0001,0x0002,0xDBFB,0xDBF1,0x000A,0x0004,0x0000,0x0001,0xDBEE,0x0003,0x0001,
+ 0xBFE5,0x0000,0xDBEB,0x0006,0x0003,0x0000,0xB5E6,0x0000,0xDBD1,0x0004,0x0002,0xDBF0,0xBFD1,0x0001,0xDBEC,0x0026,
+ 0x0012,0x0009,0x0000,0x0004,0x0002,0xD4AB,0xB9B8,0x0002,0xDBD2,0xDBF3,0x0004,0x0000,0x0000,0xB6E2,0x0001,0x0002,
+ 0xBAF1,0xDCA7,0x0009,0x0005,0x0000,0x0002,0xDBF2,0xC0DD,0x0001,0x0001,0xDBED,0x0004,0x0000,0x0000,0xD0CD,0x0003,
+ 0x0001,0xDBE4,0x0002,0xDBE2,0xC2A2,0x0015,0x0009,0x0005,0x0000,0x0002,0xC0AC,0xB4B9,0x0001,0x0001,0xDBE5,0x0004,
+ 0x0000,0x0000,0xDBE6,0x0004,0x0002,0xBFC0,0xDBE9,0x0002,0xC7F0,0xC2BD,0x000A,0x0004,0x0000,0x0000,0xDBEA,0x0003,
+ 0x0000,0xC5F7,0x0000,0xDBE8,0x0008,0x0004,0x0002,0xDBE3,0xC6BA,0x0002,0xDBE1,0xDBE7,0x0003,0x0001,0xCCB9,0x0001,
+ 0xC0A4,0x00AC,0x0057,0x002D,0x0017,0x000C,0x0004,0x0001,0x0001,0xC6C2,0x0004,0x0002,0xD7B9,0xB7D8,0x0002,0xCEEB,
+ 0xB0D3,0x0007,0x0004,0x0002,0xDBDE,0xCCB3,0x0000,0xBCE1,0x0000,0x0001,0xBFE9,0x000C,0x0004,0x0001,0x0001,0xBFD3,
+ 0x0004,0x0002,0xD7F8,0xBBB5,0x0002,0xBFB2,0xCCAE,0x0006,0x0003,0x0000,0xDBD0,0x0000,0xB7BB,0x0000,0x0001,0xBEF9,
+ 0x0013,0x000B,0x0005,0x0001,0x0002,0xDBE0,0xDBE6,0x0003,0x0000,0xD6B7,0x0000,0xBBF8,0x0000,0x0003,0x0001,0xDBDF,
+ 0x0002,0xB3A1,0xDBDB,0x000C,0x0004,0x0000,0x0001,0xDBDA,0x0004,0x0002,0xB5D8,0xDBDD,0x0002,0xDBDC,0xB9E7,0x0007,
+ 0x0003,0x0000,0xDBD8,0x0002,0xDBD9,0xDBD7,0x0000,0x0000,0xD4DA,0x002C,0x0014,0x000A,0x0006,0x0003,0x0001,0xCAA5,
+ 0x0000,0xEEAE,0x0000,0x0001,0xCDC1,0x0004,0x0000,0x0000,0xE0F7,0x0003,0x0000,0xCDC5,0x0000,0xCDBC,0x000A,0x0006,
+ 0x0003,0x0001,0xD4B2,0x0000,0xD4B0,0x0001,0x0001,0xCEA7,0x0007,0x0003,0x0001,0xB9FA,0x0002,0xE0F5,0xE0F6,0x0004,
+ 0x0002,0xC8A6,0xE0F0,0x0000,0xD4B2,0x0015,0x000C,0x0005,0x0000,0x0002,0xE0F4,0xC6D4,0x0003,0x0001,0xE0F3,0x0002,
+ 0xCDBC,0xB9FA,0x0005,0x0001,0x0002,0xB9CC,0xE0F2,0x0001,0x0001,0xE0F0,0x000D,0x0006,0x0003,0x0000,0xCEA7,0x0001,
+ 0xB4D1,0x0003,0x0000,0xC0A7,0x0002,0xB6EF,0xD4B0,0x0000,0x0003,0x0001,0xE0F1,0x0000,0xB4D1,0x0052,0x002C,0x0019,
+ 0x000F,0x0007,0x0003,0x0000,0xB6DA,0x0002,0xCDC5,0xE0EF,0x0004,0x0002,0xD2F2,0xD8B6,0x0002,0xBBD8,0xE0EE,0x0006,
+ 0x0003,0x0001,0xCBC4,0x0000,0xC7F4,0x0000,0x0001,0xBFDA,0x0008,0x0000,0x0004,0x0002,0xE0EC,0xC4F6,0x0001,0xD6F6,
+ 0x0007,0x0003,0x0000,0xCBD5,0x0002,0xC4D2,0xC2DE,0x0000,0x0000,0xDFBD,0x0013,0x0009,0x0005,0x0001,0x0002,0xCFF9,
+ 0xE0BF,0x0000,0x0000,0xDFF9,0x0004,0x0000,0x0000,0xBDC0,0x0003,0x0001,0xC8C2,0x0000,0xE0D3,0x000B,0x0005,0x0000,
+ 0x0002,0xD1CF,0xE0B7,0x0003,0x0001,0xE0EB,0x0000,0xCFF2,0x0001,0x0003,0x0000,0xC1FC,0x0002,0xDFBF,0xD1CA,0x0020,
+ 0x000D,0x0005,0x0000,0x0000,0x0001,0xCFF9,0x0004,0x0001,0x0001,0xC4F6,0x0001,0x0001,0xE0E0,0x000B,0x0004,0x0000,
+ 0x0001,0xE0EA,0x0004,0x0002,0xB3A2,0xCCE7,0x0000,0xBABF,0x0001,0x0003,0x0001,0xCFC5,0x0002,0xE0E3,0xE0E9,0x0012,
+ 0x0005,0x0001,0x0000,0x0000,0xDFCC,0x0007,0x0004,0x0002,0xE0E8,0xE0E7,0x0001,0xB5B1,0x0003,0x0000,0xB6D6,0x0000,
+ 0xB8C1,0x000B,0x0007,0x0003,0x0000,0xC5E7,0x0002,0xDFE0,0xE0E5,0x0000,0x0001,0xE0C8,0x0008,0x0004,0x0002,0xCAC9,
+ 0xE0E6,0x0002,0xD4EB,0xD8AC,0x0003,0x0000,0xC6F7,0x0002,0xDFDC,0xDFE6,0x016A,0x00AD,0x0054,0x0027,0x0014,0x0009,
+ 0x0005,0x0002,0xE0E4,0x0001,0xE0DE,0x0000,0x0001,0xDFD5,0x0004,0x0000,0x0001,0xE0E0,0x0004,0x0002,0xE0DF,0xE0D9,
+ 0x0000,0xE0DB,0x000A,0x0006,0x0003,0x0001,0xE0E2,0x0000,0xD0EA,0x0001,0x0001,0xD2AD,0x0005,0x0000,0x0002,0xE0DD,
+ 0xE0E1,0x0000,0x0001,0xF5BE,0x0012,0x0007,0x0001,0x0003,0x0000,0xB6F1,0x0000,0xBAD9,0x0004,0x0001,0x0000,0xCEFB,
+ 0x0004,0x0002,0xE0DA,0xDFBC,0x0001,0xCBBB,0x0010,0x0008,0x0004,0x0002,0xDFD8,0xD7EC,0x0002,0xE0B0,0xB3B0,0x0004,
+ 0x0002,0xD6F6,0xDFB4,0x0002,0xD0A5,0xDFEB,0x0005,0x0000,0x0002,0xE0D8,0xE0DC,0x0003,0x0000,0xBBA9,0x0000,0xE0D7,
+ 0x002D,0x0018,0x000B,0x0006,0x0003,0x0001,0xE0D3,0x0000,0xE0D4,0x0001,0x0002,0xE0BD,0xE0CF,0x0006,0x0003,0x0001,
+ 0xDFE9,0x0000,0xC2EF,0x0003,0x0001,0xD0EA,0x0002,0xB3A2,0xDFF5,0x0009,0x0004,0x0000,0x0001,0xC5BB,0x0001,0x0002,
+ 0xD8C5,0xB8C2,0x0005,0x0000,0x0002,0xE0B6,0xE0D1,0x0004,0x0002,0xBCCE,0xE0D0,0x0001,0xCCBE,0x0013,0x0008,0x0001,
+ 0x0004,0x0002,0xE0D2,0xE0D6,0x0001,0xE0D5,0x0006,0x0003,0x0000,0xCBD4,0x0001,0xB4DF,0x0001,0x0002,0xE0BB,0xDFD9,
+ 0x000B,0x0007,0x0003,0x0000,0xE0CC,0x0002,0xE0C8,0xE0C7,0x0001,0x0000,0xE0C5,0x0007,0x0003,0x0001,0xE0C0,0x0002,
+ 0xE0BF,0xE0BA,0x0004,0x0002,0xDFEF,0xE0CB,0x0001,0xE0C2,0x0062,0x0032,0x0019,0x000D,0x0007,0x0004,0x0002,0xE0C6,
+ 0xE0CD,0x0000,0xCBC3,0x0003,0x0000,0xCECB,0x0000,0xE0B5,0x0008,0x0004,0x0002,0xE0C3,0xCAC8,0x0002,0xC7B8,0xCED8,
+ 0x0001,0x0001,0xE0B2,0x000D,0x0007,0x0003,0x0001,0xE0C1,0x0002,0xC9A4,0xE0AA,0x0003,0x0000,0xE0BE,0x0001,0xC2F0,
+ 0x0005,0x0000,0x0002,0xE0CA,0xE0C9,0x0003,0x0000,0xE0BC,0x0002,0xD8C4,0xC7BA,0x0016,0x000A,0x0005,0x0000,0x0002,
+ 0xD0E1,0xE0C4,0x0001,0x0002,0xD4EB,0xE0B7,0x0006,0x0003,0x0000,0xE0B6,0x0000,0xD3F7,0x0003,0x0000,0xE0AD,0x0000,
+ 0xC5E7,0x000D,0x0007,0x0003,0x0000,0xDFF7,0x0002,0xD4FB,0xD3B4,0x0003,0x0000,0xE0AC,0x0001,0xB5A5,0x0008,0x0004,
+ 0x0002,0xD1E4,0xC7C7,0x0002,0xB3D4,0xC9A5,0x0001,0x0002,0xD0FA,0xD1D2,0x002C,0x0014,0x0008,0x0004,0x0001,0x0001,
+ 0xECA6,0x0001,0x0000,0xE0B0,0x0007,0x0004,0x0002,0xBAC8,0xCFB2,0x0001,0xBBBD,0x0000,0x0002,0xE0B9,0xB4AD,0x000C,
+ 0x0006,0x0003,0x0001,0xE0B8,0x0001,0xD4DB,0x0003,0x0000,0xE0B3,0x0000,0xDFF6,0x0005,0x0001,0x0002,0xE0A9,0xBAB0,
+ 0x0004,0x0002,0xBAED,0xE0AE,0x0000,0xC0AE,0x001B,0x000E,0x0007,0x0003,0x0001,0xC9C6,0x0002,0xE0AB,0xCEB9,0x0004,
+ 0x0002,0xE0AF,0xBFA6,0x0001,0xE0B1,0x0006,0x0003,0x0001,0xCCE4,0x0000,0xE0B4,0x0003,0x0001,0xD0A5,0x0002,0xE0A5,
+ 0xE0A4,0x0008,0x0004,0x0000,0x0000,0xE0A3,0x0001,0x0001,0xC4F6,0x0007,0x0004,0x0002,0xDFF9,0xD8C4,0x0001,0xC5BE,
+ 0x0001,0x0002,0xDFF5,0xC0B2,0x00A9,0x005B,0x002D,0x0017,0x000E,0x0007,0x0004,0x0002,0xC9B6,0xC6A1,0x0000,0xCFCE,
+ 0x0003,0x0000,0xB7C8,0x0002,0xC6F4,0xD1C6,0x0004,0x0000,0x0001,0xE0A8,0x0001,0x0002,0xE0A2,0xE0A2,0x000C,0x0004,
+ 0x0000,0x0000,0xDFFB,0x0004,0x0002,0xE0A9,0xDFFD,0x0002,0xCECA,0xE2E8,0x0004,0x0001,0x0001,0xB0A1,0x0003,0x0000,
+ 0xDFF8,0x0001,0xC9CC,0x0018,0x000E,0x0007,0x0004,0x0002,0xD7C4,0xD7C4,0x0000,0xBFD0,0x0003,0x0000,0xDFFA,0x0002,
+ 0xDFFC,0xCDD9,0x0004,0x0000,0x0001,0xDFFE,0x0003,0x0001,0xC4EE,0x0000,0xE0A1,0x000C,0x0004,0x0001,0x0000,0xE0A6,
+ 0x0004,0x0002,0xB3AA,0xE0A7,0x0002,0xCEA8,0xCADB,0x0006,0x0003,0x0001,0xBBA3,0x0001,0xDFF4,0x0001,0x0000,0xDFF3,
+ 0x0023,0x0010,0x000B,0x0007,0x0003,0x0001,0xBBBD,0x0002,0xDFF0,0xDFEF,0x0000,0x0001,0xDFEB,0x0000,0x0001,0x0000,
+ 0xDFE9,0x000B,0x0004,0x0000,0x0001,0xDFED,0x0004,0x0002,0xDFF2,0xCCC6,0x0000,0xDFF1,0x0001,0x0003,0x0000,0xB0A6,
+ 0x0002,0xB4BD,0xCBF4,0x0014,0x000B,0x0005,0x0000,0x0002,0xBAAC,0xDFC2,0x0003,0x0000,0xD1E4,0x0000,0xDBC1,0x0000,
+ 0x0004,0x0002,0xDFEC,0xBADF,0x0002,0xF7FD,0xB2B8,0x0009,0x0005,0x0001,0x0002,0xDFEE,0xD5DC,0x0001,0x0001,0xCFF8,
+ 0x0006,0x0003,0x0000,0xBFDE,0x0001,0xC4C4,0x0004,0x0002,0xC1A8,0xC9DA,0x0002,0xDFEA,0xC5B6,0x006B,0x0035,0x0018,
+ 0x000B,0x0004,0x0000,0x0000,0xB8E7,0x0003,0x0000,0xD4B1,0x0002,0xD3B4,0xDFE8,0x0007,0x0004,0x0002,0xDFE6,0xDFE2,
+ 0x0001,0xDFE1,0x0003,0x0000,0xDFE0,0x0000,0xBBA9,0x0010,0x0008,0x0004,0x0002,0xDFDC,0xDFD9,0x0002,0xDFD8,0xDFD5,
+ 0x0004,0x0002,0xD1C6,0xDFD1,0x0002,0xDFE7,0xB0A5,0x0005,0x0000,0x0002,0xCFEC,0xDFDF,0x0004,0x0002,0xD4D5,0xB9FE,
+ 0x0002,0xCDDB,0xB6DF,0x001A,0x000D,0x0006,0x0003,0x0001,0xBAE5,0x0001,0xDFD3,0x0004,0x0002,0xC6B7,0xB0A7,0x0000,
+ 0xDFDE,0x0007,0x0004,0x0002,0xD1CA,0xDFC3,0x0000,0xDFDD,0x0003,0x0001,0xCFCC,0x0001,0xF3C2,0x000C,0x0006,0x0003,
+ 0x0001,0xDFD4,0x0000,0xBFC8,0x0003,0x0000,0xD4DB,0x0000,0xBFA9,0x0008,0x0004,0x0002,0xDFD2,0xD2A7,0x0002,0xE5EB,
+ 0xDFE4,0x0004,0x0002,0xDFE3,0xD7C9,0x0002,0xDFD6,0xDFD7,0x0031,0x0018,0x000B,0x0007,0x0003,0x0001,0xDFE5,0x0002,
+ 0xDFDB,0xDACC,0x0000,0x0001,0xBCA9,0x0007,0x0003,0x0000,0xDFD0,0x0002,0xDFCC,0xDFCB,0x0003,0x0000,0xC1FC,0x0001,
+ 0xBFA7,0x000E,0x0007,0x0004,0x0002,0xB9BE,0xDFC7,0x0001,0xD6E4,0x0003,0x0001,0xB8C0,0x0002,0xD3BD,0xBECC,0x0007,
+ 0x0004,0x0002,0xBFC8,0xBACD,0x0000,0xD5A6,0x0001,0x0001,0xC5D8,0x0018,0x000A,0x0006,0x0003,0x0001,0xDFCD,0x0001,
+ 0xDFC6,0x0000,0x0001,0xBED7,0x0007,0x0004,0x0002,0xC3FC,0xBAF4,0x0000,0xC9EB,0x0003,0x0001,0xC5DE,0x0002,0xDFC8,
+ 0xDFCE,0x000D,0x0008,0x0004,0x0002,0xBAC7,0xBAF0,0x0002,0xCEB6,0xDFDA,0x0000,0x0002,0xDFC9,0xF6A4,0x0001,0x0003,
+ 0x0001,0xD6DC,0x0001,0xDFCF,0x02DF,0x0185,0x00CF,0x0063,0x002C,0x0014,0x0008,0x0004,0x0001,0x0000,0xDFCA,0x0000,
+ 0x0000,0xC4D8,0x0005,0x0001,0x0002,0xCED8,0xC7BA,0x0003,0x0001,0xDFC3,0x0002,0xD4B1,0xDFC2,0x000E,0x0008,0x0004,
+ 0x0002,0xDFBF,0xC5BB,0x0002,0xDFBE,0xDFBD,0x0003,0x0000,0xDFBC,0x0000,0xC4C5,0x0004,0x0001,0x0001,0xDFBB,0x0003,
+ 0x0000,0xB8E6,0x0000,0xB3CA,0x0019,0x000C,0x0006,0x0003,0x0000,0xB4F4,0x0001,0xDFC0,0x0003,0x0000,0xC2C0,0x0000,
+ 0xD1BD,0x0007,0x0003,0x0000,0xCEE1,0x0002,0xBAF0,0xCEC7,0x0003,0x0001,0xB4B5,0x0000,0xCEFC,0x000F,0x0008,0x0004,
+ 0x0002,0xC4C5,0xB3B3,0x0002,0xCEE2,0xCEE2,0x0004,0x0002,0xDFC5,0xD6A8,0x0001,0xC6F4,0x0008,0x0004,0x0002,0xCBB1,
+ 0xBFD4,0x0002,0xCCFD,0xBAAC,0x0003,0x0001,0xB7D4,0x0002,0xB6D6,0xB0C9,0x0035,0x0018,0x000D,0x0006,0x0003,0x0000,
+ 0xB7F1,0x0001,0xDFC4,0x0003,0x0001,0xDFC1,0x0002,0xB7CD,0xD2F7,0x0007,0x0004,0x0002,0xCDCC,0xC1DF,0x0001,0xBEFD,
+ 0x0001,0x0001,0xC2F0,0x000F,0x0007,0x0004,0x0002,0xDFB9,0xC2C0,0x0001,0xCFC5,0x0004,0x0002,0xDFB8,0xCFF2,0x0002,
+ 0xCDC2,0xC0F4,0x0007,0x0004,0x0002,0xBAF3,0xC3FB,0x0000,0xCDAC,0x0004,0x0002,0xB5F5,0xBCAA,0x0000,0xBACF,0x0019,
+ 0x000B,0x0007,0x0003,0x0000,0xDFBA,0x0002,0xB8F7,0xB3D4,0x0000,0x0001,0xD3F5,0x0007,0x0003,0x0001,0xDFB4,0x0002,
+ 0xB5F0,0xDFB7,0x0003,0x0001,0xCCBE,0x0002,0xCBBE,0xBAC5,0x000F,0x0007,0x0004,0x0002,0xD2B6,0xD8CF,0x0001,0xD3D2,
+ 0x0004,0x0002,0xCAB7,0xDFB3,0x0002,0xCCA8,0xBFC9,0x0008,0x0004,0x0002,0xB6A3,0xB0C8,0x0002,0xD5D9,0xBDD0,0x0004,
+ 0x0002,0xD6BB,0xDFB5,0x0000,0xDFB6,0x0057,0x0032,0x001A,0x000F,0x0008,0x0004,0x0002,0xC1ED,0xBEE4,0x0002,0xB9C5,
+ 0xBFDA,0x0003,0x0000,0xB4D4,0x0002,0xB5FE,0xDBC5,0x0004,0x0001,0x0001,0xC5D1,0x0003,0x0001,0xD0F0,0x0002,0xB1E4,
+ 0xCADC,0x000A,0x0006,0x0003,0x0000,0xC8A1,0x0000,0xCAE5,0x0000,0x0001,0xB7A2,0x0007,0x0003,0x0001,0xB7B4,0x0002,
+ 0xCBAB,0xD3D1,0x0004,0x0002,0xBCB0,0xB2E6,0x0000,0xD3D6,0x0010,0x000B,0x0004,0x0001,0x0001,0xB2CE,0x0004,0x0002,
+ 0xB2CE,0xC8FE,0x0001,0xCFD8,0x0000,0x0001,0x0001,0xC8A5,0x000A,0x0006,0x0003,0x0000,0xDBCC,0x0000,0xD8C9,0x0000,
+ 0x0000,0xC0F7,0x0005,0x0000,0x0002,0xD8CB,0xD1E1,0x0003,0x0001,0xBEC7,0x0000,0xB3F8,0x002B,0x0019,0x000E,0x0008,
+ 0x0004,0x0002,0xCFC3,0xD8CA,0x0002,0xC0FA,0xD8C9,0x0003,0x0000,0xCFE1,0x0001,0xD4AD,0x0004,0x0000,0x0001,0xD8C8,
+ 0x0004,0x0002,0xBAF1,0xD8C7,0x0000,0xC0E5,0x0006,0x0000,0x0000,0x0002,0xC5D3,0xB2DE,0x0008,0x0004,0x0002,0xEDC6,
+ 0xD8C7,0x0002,0xD1E1,0xD1B9,0x0000,0x0001,0xC0F7,0x0019,0x000D,0x0007,0x0004,0x0002,0xC0FA,0xCCFC,0x0000,0xB6F2,
+ 0x0003,0x0000,0xB3A7,0x0001,0xC7E4,0x0004,0x0001,0x0001,0xC8B4,0x0004,0x0002,0xDAE1,0xD0F4,0x0002,0xD0B6,0xBEED,
+ 0x000E,0x0007,0x0003,0x0001,0xC2D1,0x0002,0xC8B4,0xBCB4,0x0003,0x0001,0xCEA3,0x0002,0xD3A1,0xC3AE,0x0007,0x0003,
+ 0x0000,0xD8B4,0x0002,0xD1F6,0xCEC0,0x0003,0x0001,0xDAE0,0x0001,0xCED4,0x00B0,0x005E,0x0033,0x0018,0x000D,0x0005,
+ 0x0002,0xD8D4,0x0001,0xC2B1,0x0004,0x0002,0xD8D5,0xC2AC,0x0002,0xBFA8,0xD5BC,0x0007,0x0004,0x0002,0xDFB2,0xB1E5,
+ 0x0001,0xB2B7,0x0000,0x0001,0xB2A9,0x000F,0x0008,0x0004,0x0002,0xC4CF,0xC2F4,0x0002,0xB5A5,0xD0AD,0x0004,0x0002,
+ 0xD7BF,0xD7E4,0x0000,0xB1B0,0x0005,0x0000,0x0002,0xD0AD,0xBBAA,0x0003,0x0001,0xB0EB,0x0002,0xBBDC,0xCEE7,0x001B,
+ 0x000D,0x0006,0x0003,0x0000,0xC9FD,0x0000,0xD8A6,0x0003,0x0000,0xC7A7,0x0002,0xCAAE,0xC7F8,0x0007,0x0004,0x0002,
+ 0xC4E4,0xD8D2,0x0000,0xD9C8,0x0004,0x0002,0xD2BD,0xC7F8,0x0000,0xC6A5,0x0005,0x0001,0x0001,0x0000,0xD8D1,0x0007,
+ 0x0004,0x0002,0xBBE3,0xD8D1,0x0000,0xD8D0,0x0000,0x0001,0xB7CB,0x002F,0x0017,0x000B,0x0004,0x0000,0x0001,0xD8D0,
+ 0x0003,0x0000,0xCFBB,0x0002,0xBFEF,0xBDB3,0x0006,0x0003,0x0000,0xBFBB,0x0000,0xD4D1,0x0003,0x0001,0xD8CE,0x0000,
+ 0xB3D7,0x000B,0x0007,0x0004,0x0002,0xB1B1,0xBBAF,0x0000,0xD8B0,0x0001,0x0001,0xD9EB,0x0006,0x0003,0x0000,0xDECB,
+ 0x0000,0xD9E9,0x0004,0x0002,0xCCD5,0xDEE4,0x0001,0xD0D9,0x0017,0x000A,0x0006,0x0003,0x0001,0xB4D2,0x0000,0xB0FC,
+ 0x0001,0x0001,0xD4C8,0x0005,0x0000,0x0002,0xCEF0,0xB9B4,0x0004,0x0002,0xD4C8,0xC9D7,0x0002,0xD9E8,0xC8B0,0x0000,
+ 0x0004,0x0001,0x0000,0xC0F8,0x0003,0x0000,0xD1AB,0x0002,0xDBBD,0xDBC4,0x0052,0x002D,0x0019,0x000B,0x0006,0x0003,
+ 0x0001,0xBDCB,0x0001,0xC7DA,0x0000,0x0002,0xBCA8,0xCAC6,0x0007,0x0004,0x0002,0xC4BC,0xC0CD,0x0000,0xCAA4,0x0003,
+ 0x0000,0xD1AB,0x0002,0xCEF1,0xBFB1,0x000D,0x0007,0x0004,0x0002,0xDBC3,0xDBC3,0x0000,0xB6AF,0x0003,0x0001,0xC0D5,
+ 0x0001,0xDBC2,0x0001,0x0003,0x0000,0xD1AB,0x0000,0xC3E3,0x0010,0x000A,0x0004,0x0000,0x0000,0xD3C2,0x0003,0x0000,
+ 0xB2AA,0x0000,0xBEA2,0x0000,0x0000,0x0002,0xCAC6,0xDBC0,0x0008,0x0001,0x0004,0x0002,0xC0CD,0xBEA2,0x0000,0xC0F8,
+ 0x0005,0x0001,0x0002,0xDBBF,0xDBBE,0x0004,0x0002,0xBDD9,0xC5AC,0x0002,0xD6FA,0xB6AF,0x002C,0x0017,0x000C,0x0004,
+ 0x0000,0x0001,0xC1A6,0x0004,0x0002,0xC1D3,0xDBBD,0x0002,0xCEF1,0xBCD3,0x0007,0x0004,0x0002,0xB9A6,0xB0EC,0x0000,
+ 0xC8B0,0x0000,0x0000,0xC1A6,0x0008,0x0001,0x0003,0x0000,0xD8E6,0x0002,0xBCC1,0xD8E5,0x0005,0x0001,0x0002,0xBDA3,
+ 0xD8DB,0x0004,0x0002,0xBDCB,0xB9F4,0x0002,0xC1F5,0xC5FC,0x0014,0x000D,0x0006,0x0003,0x0000,0xBEE7,0x0001,0xD4FD,
+ 0x0004,0x0002,0xBBAE,0xD8E3,0x0000,0xD8E4,0x0000,0x0003,0x0000,0xBDCB,0x0000,0xD8E2,0x000B,0x0007,0x0003,0x0000,
+ 0xB2F9,0x0002,0xB4B4,0xD8DC,0x0000,0x0001,0xB8EE,0x0007,0x0004,0x0002,0xB8B1,0xB9D0,0x0001,0xD6C6,0x0003,0x0001,
+ 0xBCF4,0x0000,0xCAA3,0x017A,0x00C3,0x0062,0x002D,0x0015,0x0009,0x0005,0x0002,0xBEE7,0x0001,0xB0FE,0x0001,0x0001,
+ 0xD8DF,0x0005,0x0001,0x0002,0xD8DE,0xB0FE,0x0004,0x0002,0xD8E0,0xB8D5,0x0000,0xCAC2,0x000A,0x0004,0x0001,0x0000,
+ 0xC6CA,0x0003,0x0000,0xCCDE,0x0001,0xBDA3,0x0007,0x0003,0x0000,0xB9D0,0x0002,0xC9B2,0xC7B0,0x0004,0x0002,0xD8DD,
+ 0xBFCB,0x0000,0xCFF7,0x001A,0x000C,0x0004,0x0000,0x0001,0xD4F2,0x0004,0x0002,0xD8D9,0xCCEA,0x0002,0xBCC1,0xB6E7,
+ 0x0007,0x0004,0x0002,0xD8DC,0xD8DB,0x0001,0xB9F4,0x0003,0x0001,0xBFCC,0x0002,0xB4CC,0xC9B2,0x000D,0x0007,0x0004,
+ 0x0002,0xC8AF,0xCBA2,0x0000,0xD6C6,0x0003,0x0001,0xD8DA,0x0001,0xB4B4,0x0007,0x0003,0x0000,0xB5BD,0x0002,0xB9CE,
+ 0xD8D9,0x0003,0x0001,0xB1F0,0x0002,0xC9BE,0xC0FB,0x002C,0x0017,0x000A,0x0006,0x0003,0x0000,0xC5D9,0x0001,0xB1F0,
+ 0x0000,0x0000,0xC5D0,0x0006,0x0003,0x0000,0xC9BE,0x0001,0xB3F5,0x0003,0x0001,0xB4B4,0x0002,0xB8D5,0xD4F2,0x000C,
+ 0x0007,0x0004,0x0002,0xC1F5,0xC1D0,0x0000,0xEBBE,0x0001,0x0002,0xBBAE,0xD0CC,0x0005,0x0001,0x0002,0xD8D8,0xDBBB,
+ 0x0001,0x0000,0xBFAF,0x001D,0x000E,0x0007,0x0004,0x0002,0xD8D7,0xC7D0,0x0000,0xB7D6,0x0003,0x0001,0xC8D0,0x0002,
+ 0xD8D6,0xB5F3,0x0007,0x0004,0x0002,0xB5B6,0xD4E4,0x0001,0xBAAF,0x0004,0x0002,0xDBCA,0xBBF7,0x0002,0xB3F6,0xB0BC,
+ 0x000D,0x0007,0x0003,0x0000,0xCDB9,0x0002,0xD0D7,0xDBC9,0x0003,0x0001,0xB5CA,0x0001,0xBFAD,0x0007,0x0004,0x0002,
+ 0xBBCB,0xBFAD,0x0001,0xC6BE,0x0000,0x0001,0xD9EC,0x0054,0x0024,0x0012,0x0007,0x0001,0x0003,0x0000,0xB7EF,0x0001,
+ 0xB7B2,0x0006,0x0003,0x0000,0xBCB8,0x0001,0xC4FD,0x0000,0x0002,0xC1DD,0xC1DD,0x0005,0x0001,0x0001,0x0001,0xB4D5,
+ 0x0006,0x0003,0x0001,0xBCF5,0x0001,0xB6B3,0x0004,0x0002,0xC1E8,0xB5F2,0x0001,0xC1B9,0x0016,0x000B,0x0007,0x0004,
+ 0x0002,0xBEBB,0xDAA1,0x0000,0xD7BC,0x0000,0x0000,0xC6E0,0x0006,0x0003,0x0000,0xBEBB,0x0001,0xD9FD,0x0000,0x0002,
+ 0xD9FE,0xB6B3,0x000E,0x0007,0x0003,0x0001,0xC0E4,0x0002,0xD2B1,0xBFF6,0x0003,0x0001,0xBEF6,0x0002,0xB3E5,0xD9FC,
+ 0x0005,0x0000,0x0002,0xB1F9,0xB7EB,0x0004,0x0002,0xB6AC,0xD9FB,0x0000,0xC3DD,0x002E,0x0015,0x000A,0x0004,0x0001,
+ 0x0001,0xDAA4,0x0003,0x0000,0xD4A9,0x0000,0xDAA3,0x0004,0x0000,0x0000,0xB9DA,0x0004,0x0002,0xC5A9,0xBEFC,0x0001,
+ 0xD0B4,0x000E,0x0007,0x0003,0x0001,0xC8DF,0x0002,0xDAA2,0xC3E1,0x0003,0x0001,0xB9B9,0x0002,0xC3B0,0xEBD0,0x0004,
+ 0x0001,0x0001,0xD4D9,0x0003,0x0000,0xB2E1,0x0002,0xB2E1,0xC8BD,0x0018,0x000B,0x0006,0x0003,0x0000,0xB8D4,0x0001,
+ 0xC4DA,0x0001,0x0002,0xD8E7,0xD9E6,0x0006,0x0003,0x0000,0xBCBD,0x0001,0xCADE,0x0004,0x0002,0xBCE6,0xD1F8,0x0001,
+ 0xD7C8,0x000F,0x0008,0x0004,0x0002,0xB5E4,0xBEDF,0x0002,0xC6E4,0xB1F8,0x0004,0x0002,0xD0CB,0xB9D8,0x0001,0xB9B2,
+ 0x0007,0x0003,0x0000,0xC0BC,0x0002,0xD9E2,0xC1F9,0x0004,0x0002,0xB9AB,0xB0CB,0x0001,0xC1BD,0x009F,0x0055,0x002B,
+ 0x0012,0x000B,0x0007,0x0004,0x0002,0xC8AB,0xC4DA,0x0001,0xC8EB,0x0001,0x0000,0xBEA4,0x0001,0x0003,0x0000,0xB6B5,
+ 0x0000,0xB5B3,0x000E,0x0007,0x0003,0x0001,0xD9F0,0x0002,0xD9F0,0xD9EE,0x0003,0x0000,0xCDC3,0x0002,0xB6F9,0xB6D2,
+ 0x0004,0x0001,0x0001,0xC3E2,0x0004,0x0002,0xB6D2,0xBFCB,0x0001,0xB9E2,0x001B,0x000F,0x0008,0x0004,0x0002,0xCFC8,
+ 0xD0D7,0x0002,0xD5D7,0xB3E4,0x0004,0x0002,0xD0D6,0xD4AA,0x0001,0xD4CA,0x0005,0x0000,0x0002,0xD8A3,0xB6F9,0x0004,
+ 0x0002,0xD9B2,0xD9CE,0x0000,0xD9D0,0x000A,0x0004,0x0000,0x0001,0xD9B3,0x0003,0x0001,0xB2F3,0x0000,0xB4A2,0x0001,
+ 0x0001,0x0000,0xD3C5,0x0020,0x000A,0x0005,0x0001,0x0001,0x0001,0xC0DC,0x0000,0x0000,0x0001,0xB3A5,0x000C,0x0006,
+ 0x0003,0x0000,0xBEA1,0x0001,0xD9AD,0x0003,0x0000,0xD9B1,0x0000,0xC8E5,0x0004,0x0000,0x0000,0xD9CF,0x0003,0x0001,
+ 0xD9D9,0x0001,0xBCF3,0x0017,0x000D,0x0007,0x0004,0x0002,0xBFEB,0xD9D8,0x0000,0xD9D3,0x0003,0x0000,0xD2DA,0x0000,
+ 0xD9AF,0x0004,0x0000,0x0000,0xD2C7,0x0003,0x0001,0xC6A7,0x0001,0xBCDB,0x000A,0x0004,0x0001,0x0001,0xBDA9,0x0003,
+ 0x0001,0xCBDB,0x0001,0xB9CD,0x0005,0x0001,0x0002,0xD9D7,0xD9D4,0x0000,0x0000,0xD9D5,0x0053,0x0023,0x0011,0x0009,
+ 0x0000,0x0004,0x0002,0xD9C7,0xC9AE,0x0002,0xD9D6,0xBDC4,0x0004,0x0001,0x0001,0xBEDF,0x0001,0x0000,0xC1C5,0x0009,
+ 0x0005,0x0001,0x0002,0xD9D2,0xC6CD,0x0001,0x0001,0xC7C8,0x0004,0x0000,0x0001,0xCFF3,0x0001,0x0002,0xCFC9,0xD9DD,
+ 0x0015,0x000A,0x0006,0x0003,0x0000,0xC2FD,0x0001,0xBDF6,0x0001,0x0000,0xD9CD,0x0005,0x0001,0x0002,0xC7E3,0xD5C2,
+ 0x0003,0x0001,0xC9B5,0x0000,0xD9D1,0x000E,0x0007,0x0003,0x0001,0xC9CB,0x0002,0xEBED,0xD5AE,0x0004,0x0002,0xD8F1,
+ 0xB4AB,0x0000,0xB0C1,0x0007,0x0004,0x0002,0xC5F3,0xD9CC,0x0001,0xD3B6,0x0003,0x0000,0xB4DF,0x0001,0xD9D0,0x002A,
+ 0x0015,0x000D,0x0007,0x0004,0x0002,0xB4A2,0xD9CF,0x0001,0xD9CE,0x0003,0x0001,0xB4F6,0x0000,0xBCD2,0x0001,0x0003,
+ 0x0001,0xC8DD,0x0002,0xD0A7,0xB1B8,0x000D,0x0006,0x0003,0x0000,0xC9A1,0x0000,0xD8F7,0x0003,0x0000,0xE3BB,0x0002,
+ 0xE1E6,0xBDDC,0x0004,0x0001,0x0001,0xB0F8,0x0000,0x0000,0xC2EE,0x0018,0x000A,0x0006,0x0003,0x0000,0xC0FC,0x0001,
+ 0xB8B5,0x0001,0x0000,0xCFB5,0x0008,0x0004,0x0002,0xBFFE,0xB3A5,0x0002,0xD9C7,0xCEB1,0x0003,0x0001,0xD9CD,0x0000,
+ 0xD4DB,0x000B,0x0007,0x0003,0x0001,0xCDB5,0x0002,0xC5BC,0xD5EC,0x0000,0x0000,0xB2E0,0x0004,0x0000,0x0000,0xC6F5,
+ 0x0003,0x0000,0xD9CC,0x0000,0xB1C6,0x0000,0x0000,0x02CC,0x0159,0x009C,0x003F,0x001D,0x000C,0x0005,0x0001,0x0000,
+ 0x0000,0xBDA1,0x0001,0x0003,0x0001,0xCDA3,0x0001,0xD7F6,0x0005,0x0001,0x0000,0x0000,0xD9C9,0x0005,0x0001,0x0002,
+ 0xC6AB,0xD9CB,0x0003,0x0001,0xD9BC,0x0002,0xC6C1,0xF5E1,0x0015,0x000B,0x0007,0x0004,0x0002,0xCEB0,0xD9CA,0x0000,
+ 0xBCD9,0x0001,0x0000,0xD9C8,0x0004,0x0001,0x0001,0xC7E3,0x0003,0x0001,0xD6B5,0x0001,0xD5AE,0x0001,0x0004,0x0001,
+ 0x0001,0xD9C0,0x0004,0x0002,0xD9C1,0xD9BE,0x0002,0xC2D7,0xC4DF,0x0035,0x001B,0x000E,0x0007,0x0004,0x0002,0xD9BB,
+ 0xD9C6,0x0001,0xBEEB,0x0003,0x0000,0xD9C5,0x0002,0xB7C2,0xE6BC,0x0007,0x0003,0x0000,0xB3AB,0x0002,0xBDE8,0xBEA2,
+ 0x0003,0x0001,0xD9C3,0x0001,0xD2D0,0x000D,0x0007,0x0004,0x0002,0xBAF2,0xCCC8,0x0001,0xD0D2,0x0003,0x0001,0xBEF3,
+ 0x0001,0xB5B9,0x0006,0x0003,0x0000,0xC3C7,0x0000,0xD9BF,0x0004,0x0002,0xB1B6,0xD9C4,0x0000,0xB8F6,0x0011,0x0007,
+ 0x0000,0x0003,0x0000,0xB2D6,0x0001,0xC1A9,0x0006,0x0003,0x0001,0xD8F6,0x0001,0xD9C2,0x0001,0x0001,0xB0B3,0x000A,
+ 0x0006,0x0003,0x0001,0xD9BA,0x0001,0xD9C3,0x0001,0x0000,0xD9BD,0x0007,0x0003,0x0000,0xBEE3,0x0002,0xB8A9,0xD0DE,
+ 0x0003,0x0000,0xBCF3,0x0001,0xD9B3,0x005D,0x002F,0x001B,0x000B,0x0007,0x0004,0x0002,0xC1A9,0xD9B2,0x0001,0xD9B1,
+ 0x0001,0x0000,0xD9B6,0x0008,0x0004,0x0002,0xD0C5,0xCFC0,0x0002,0xD9B9,0xD3E1,0x0004,0x0002,0xB1A3,0xD9B7,0x0002,
+ 0xB8A9,0xD9B5,0x0008,0x0000,0x0004,0x0002,0xCFA1,0xB7FD,0x0000,0xCBD7,0x0008,0x0004,0x0002,0xD9B8,0xC0FE,0x0002,
+ 0xC7CE,0xD9DE,0x0001,0x0001,0xBFA1,0x0016,0x000C,0x0004,0x0000,0x0000,0xE5C3,0x0004,0x0002,0xD9B4,0xB6ED,0x0002,
+ 0xB4D9,0xCFB5,0x0006,0x0003,0x0000,0xD9B6,0x0000,0xB1E3,0x0001,0x0000,0xCDD1,0x000D,0x0007,0x0003,0x0000,0xCDA6,
+ 0x0002,0xBED6,0xC2C2,0x0003,0x0000,0xC7D6,0x0000,0xD7F8,0x0005,0x0001,0x0002,0xBAEE,0xCEEA,0x0003,0x0001,0xD9AF,
+ 0x0001,0xD9AD,0x002F,0x0018,0x000E,0x0008,0x0004,0x0002,0xBFEB,0xC7C8,0x0002,0xB2E0,0xD5EC,0x0003,0x0000,0xBDC4,
+ 0x0000,0xC2C2,0x0004,0x0000,0x0001,0xCFC0,0x0003,0x0000,0xD2C0,0x0000,0xB9A9,0x000B,0x0007,0x0003,0x0001,0xB2EF,
+ 0x0002,0xB6B1,0xC2D8,0x0000,0x0001,0xD9B0,0x0006,0x0003,0x0000,0xD9A7,0x0000,0xD9AA,0x0003,0x0000,0xCACC,0x0000,
+ 0xC0FD,0x001C,0x000E,0x0007,0x0004,0x0002,0xD9A8,0xB3DE,0x0001,0xC0B4,0x0004,0x0002,0xEBDC,0xD6B6,0x0000,0xD9A9,
+ 0x0007,0x0003,0x0001,0xD0CD,0x0002,0xCAB9,0xD9AB,0x0004,0x0002,0xB4CE,0xD9AE,0x0000,0xD9AC,0x000B,0x0004,0x0001,
+ 0x0001,0xD9A5,0x0004,0x0002,0xB2A2,0xD9A6,0x0000,0xBCD1,0x0006,0x0003,0x0001,0xB0DB,0x0000,0xD1F0,0x0000,0x0001,
+ 0xC0D0,0x00B8,0x005E,0x0035,0x0019,0x000C,0x0005,0x0002,0xC5E5,0x0001,0xD8FB,0x0003,0x0001,0xD9DD,0x0002,0xD8F4,
+ 0xD3B6,0x0005,0x0001,0x0002,0xC4E3,0xD9A1,0x0004,0x0002,0xD8FA,0xD8FE,0x0002,0xD7F7,0xB7F0,0x000F,0x0008,0x0004,
+ 0x0002,0xD8FD,0xD3E0,0x0002,0xD9DC,0xD9A2,0x0003,0x0001,0xBACE,0x0002,0xD5BC,0xCCE5,0x0008,0x0004,0x0002,0xD1F6,
+ 0xD3D3,0x0002,0xD7F4,0xD7A1,0x0000,0x0002,0xB5CD,0xCEBB,0x0012,0x000B,0x0005,0x0001,0x0002,0xB2BC,0xD8F9,0x0003,
+ 0x0000,0xB5AB,0x0001,0xB5E8,0x0001,0x0003,0x0001,0xD9A4,0x0000,0xCBC6,0x000C,0x0006,0x0003,0x0000,0xCBC5,0x0000,
+ 0xC9EC,0x0003,0x0000,0xC1E6,0x0000,0xB0E9,0x0007,0x0003,0x0000,0xD9A3,0x0002,0xB9C0,0xB2AE,0x0001,0x0001,0xD8F9,
+ 0x0032,0x001A,0x000D,0x0006,0x0003,0x0000,0xCEB1,0x0001,0xD8F7,0x0004,0x0002,0xC2D7,0xD8F6,0x0000,0xC9CB,0x0007,
+ 0x0003,0x0000,0xD8F3,0x0002,0xB4AB,0xCEB0,0x0003,0x0000,0xC9A1,0x0001,0xD8F1,0x000C,0x0008,0x0004,0x0002,0xBBE1,
+ 0xBBEF,0x0002,0xD3C5,0xD6DA,0x0000,0x0001,0xB7F2,0x0007,0x0003,0x0001,0xD0DD,0x0002,0xB7A5,0xB7FC,0x0000,0x0002,
+ 0xBCBF,0xCEE9,0x0012,0x0006,0x0000,0x0000,0x0002,0xD2C1,0xD8F8,0x0006,0x0003,0x0001,0xC6F3,0x0001,0xB7C2,0x0003,
+ 0x0001,0xB7DD,0x0001,0xC8CE,0x000B,0x0004,0x0001,0x0001,0xBCDB,0x0004,0x0002,0xBCFE,0xD8F5,0x0001,0xD8F2,0x0006,
+ 0x0003,0x0000,0xD6D9,0x0000,0xD1F6,0x0001,0x0002,0xC3C7,0xD8EF,0x0064,0x0032,0x0018,0x000D,0x0006,0x0003,0x0000,
+ 0xD2C7,0x0000,0xD8ED,0x0003,0x0001,0xD2D4,0x0002,0xC1EE,0xB4FA,0x0006,0x0003,0x0001,0xD8EE,0x0001,0xC7AA,0x0000,
+ 0x0002,0xD8F0,0xD9DA,0x000F,0x0007,0x0003,0x0001,0xCFC9,0x0002,0xB8B6,0xD5CC,0x0004,0x0002,0xCBFB,0xCACB,0x0002,
+ 0xD7D0,0xB2D6,0x0004,0x0000,0x0001,0xC2D8,0x0004,0x0002,0xB4D3,0xC8D4,0x0001,0xBDE9,0x001B,0x000F,0x0007,0x0004,
+ 0x0002,0xBDF1,0xD8EB,0x0001,0xB3F0,0x0004,0x0002,0xC6CD,0xBDF6,0x0002,0xD8C6,0xD8EA,0x0008,0x0004,0x0002,0xD8EC,
+ 0xC8CA,0x0002,0xCAB2,0xD2DA,0x0001,0x0001,0xD8E9,0x000B,0x0004,0x0000,0x0000,0xC8CB,0x0004,0x0002,0xB5AB,0xD9F4,
+ 0x0001,0xD9F1,0x0004,0x0000,0x0000,0xC7D7,0x0004,0x0002,0xC1C1,0xCDA4,0x0002,0xBEA9,0xCFED,0x0039,0x001C,0x000E,
+ 0x0007,0x0003,0x0001,0xC4B6,0x0002,0xBAE0,0xB2FA,0x0004,0x0002,0xD2E0,0xBAA5,0x0000,0xBDBB,0x0008,0x0004,0x0002,
+ 0xBFBA,0xCDF6,0x0002,0xD9EF,0xD8BD,0x0003,0x0000,0xD1C7,0x0001,0xD0A9,0x000E,0x0007,0x0004,0x0002,0xD1C7,0xD8A8,
+ 0x0000,0xD8A8,0x0003,0x0001,0xBEAE,0x0002,0xCEE5,0xD8C1,0x0007,0x0004,0x0002,0xBBA5,0xD4C6,0x0001,0xBFF7,0x0004,
+ 0x0002,0xD3DA,0xD8A1,0x0002,0xB6FE,0xCAC2,0x0012,0x000A,0x0006,0x0003,0x0001,0xD5F9,0x0000,0xD3E8,0x0000,0x0000,
+ 0xC1CB,0x0004,0x0000,0x0000,0xC2D2,0x0000,0x0000,0xB8C9,0x0005,0x0001,0x0001,0x0001,0xC8E9,0x0000,0x0003,0x0001,
+ 0xC2D2,0x0000,0xC2F2,0x0000,0x0000,0x0061,0x002F,0x0015,0x0008,0x0004,0x0001,0x0000,0xD8C0,0x0000,0x0001,0xCAE9,
+ 0x0005,0x0001,0x0002,0xCFE7,0xCFB0,0x0004,0x0002,0xD2B2,0xC6F2,0x0002,0xBEC5,0xD8BF,0x000B,0x0005,0x0001,0x0002,
+ 0xD2D2,0xB3CB,0x0003,0x0001,0xB9D4,0x0001,0xC7C7,0x0007,0x0004,0x0002,0xC5D2,0xC6B9,0x0001,0xC0D6,0x0004,0x0002,
+ 0xB7A6,0xBAF5,0x0002,0xD5A7,0xCEDA,0x0019,0x000D,0x0007,0x0003,0x0000,0xD6AE,0x0002,0xD2E5,0xC3B4,0x0003,0x0000,
+ 0xD8B1,0x0000,0xBEC3,0x0005,0x0000,0x0002,0xC4CB,0xD8D7,0x0004,0x0002,0xD8AF,0xBED9,0x0000,0xC0F6,0x000E,0x0008,
+ 0x0004,0x0002,0xD6F7,0xCEAA,0x0002,0xB5A4,0xCDE8,0x0003,0x0001,0xD8BC,0x0001,0xC1D9,0x0006,0x0003,0x0001,0xB4AE,
+ 0x0001,0xB7E1,0x0001,0x0002,0xD6D0,0xE3DC,0x0038,0x001B,0x000F,0x0007,0x0004,0x0002,0xD1BE,0xB8F6,0x0001,0xD8AD,
+ 0x0004,0x0002,0xC9A5,0xB2A2,0x0002,0xD1CF,0xC1BD,0x0004,0x0000,0x0001,0xB6AA,0x0004,0x0002,0xB6AA,0xD8A9,0x0002,
+ 0xCBBF,0xB6AB,0x000F,0x0008,0x0004,0x0002,0xB4D4,0xD2B5,0x0002,0xB1FB,0xC7F0,0x0003,0x0001,0xCAC0,0x0002,0xD8A7,
+ 0xC7D2,0x0007,0x0003,0x0000,0xD7A8,0x0002,0xB3F3,0xD8A4,0x0003,0x0001,0xD3EB,0x0002,0xB2BB,0xD8A2,0x0000,0x000C,
+ 0x0008,0x0004,0x0002,0xCFC2,0xC9CF,0x0002,0xC8FD,0xD5C9,0x0000,0x0000,0xCDF2,0x0000,0x0003,0x0000,0xC6DF,0x0002,
+ 0xB6A1,0xD2BB,0x0427,0x014D,0x0001,0x0001,0x0001,0x001F,0x0000,0x0001,0x0016,0x0001,0x0005,0x0001,0x0001,0x0001,
+ 0xA2EE,0x0008,0x0004,0x0002,0xA2ED,0xA2EC,0x0002,0xA2EB,0xA2EA,0x0004,0x0002,0xA2E9,0xA2E8,0x0002,0xA2E7,0xA2E6,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0xA2E5,0x0050,0x0001,0x0016,0x0001,0x0005,0x0001,0x0001,0x0001,0xA8E9,0x0008,
+ 0x0004,0x0002,0xA8E8,0xA8E7,0x0002,0xA8E6,0xA8E5,0x0004,0x0002,0xA8E4,0xA8E3,0x0002,0xA8E2,0xA8E1,0x0020,0x0010,
+ 0x0008,0x0004,0x0002,0xA8E0,0xA8DF,0x0002,0xA8DE,0xA8DD,0x0004,0x0002,0xA8DC,0xA8DB,0x0002,0xA8DA,0xA8D9,0x0008,
+ 0x0004,0x0002,0xA8D8,0xA8D7,0x0002,0xA8D6,0xA8D5,0x0004,0x0002,0xA8D4,0xA8D3,0x0002,0xA8D2,0xA8D1,0x0010,0x0008,
+ 0x0004,0x0002,0xA8D0,0xA8CF,0x0002,0xA8CE,0xA8CD,0x0004,0x0002,0xA8CC,0xA8CB,0x0002,0xA8CA,0xA8C9,0x0000,0x0004,
+ 0x0002,0xA8C8,0xA8C7,0x0002,0xA8C6,0xA8C5,0x0072,0x0032,0x0012,0x0005,0x0001,0x0000,0x0001,0xA1A4,0x0005,0x0001,
+ 0x0002,0xA5F6,0xA5F5,0x0004,0x0002,0xA5F4,0xA5F3,0x0002,0xA5F2,0xA5F1,0x0010,0x0008,0x0004,0x0002,0xA5F0,0xA5EF,
+ 0x0002,0xA5EE,0xA5ED,0x0004,0x0002,0xA5EC,0xA5EB,0x0002,0xA5EA,0xA5E9,0x0008,0x0004,0x0002,0xA5E8,0xA5E7,0x0002,
+ 0xA5E6,0xA5E5,0x0004,0x0002,0xA5E4,0xA5E3,0x0002,0xA5E2,0xA5E1,0x0020,0x0010,0x0008,0x0004,0x0002,0xA5E0,0xA5DF,
+ 0x0002,0xA5DE,0xA5DD,0x0004,0x0002,0xA5DC,0xA5DB,0x0002,0xA5DA,0xA5D9,0x0008,0x0004,0x0002,0xA5D8,0xA5D7,0x0002,
+ 0xA5D6,0xA5D5,0x0004,0x0002,0xA5D4,0xA5D3,0x0002,0xA5D2,0xA5D1,0x0010,0x0008,0x0004,0x0002,0xA5D0,0xA5CF,0x0002,
+ 0xA5CE,0xA5CD,0x0004,0x0002,0xA5CC,0xA5CB,0x0002,0xA5CA,0xA5C9,0x0008,0x0004,0x0002,0xA5C8,0xA5C7,0x0002,0xA5C6,
+ 0xA5C5,0x0004,0x0002,0xA5C4,0xA5C3,0x0002,0xA5C2,0xA5C1,0x0040,0x0020,0x0010,0x0008,0x0004,0x0002,0xA5C0,0xA5BF,
+ 0x0002,0xA5BE,0xA5BD,0x0004,0x0002,0xA5BC,0xA5BB,0x0002,0xA5BA,0xA5B9,0x0008,0x0004,0x0002,0xA5B8,0xA5B7,0x0002,
+ 0xA5B6,0xA5B5,0x0004,0x0002,0xA5B4,0xA5B3,0x0002,0xA5B2,0xA5B1,0x0010,0x0008,0x0004,0x0002,0xA5B0,0xA5AF,0x0002,
+ 0xA5AE,0xA5AD,0x0004,0x0002,0xA5AC,0xA5AB,0x0002,0xA5AA,0xA5A9,0x0008,0x0004,0x0002,0xA5A8,0xA5A7,0x0002,0xA5A6,
+ 0xA5A5,0x0004,0x0002,0xA5A4,0xA5A3,0x0002,0xA5A2,0xA5A1,0x0009,0x0001,0x0001,0x0003,0x0001,0xA4F3,0x0002,0xA4F2,
+ 0xA4F1,0x0010,0x0008,0x0004,0x0002,0xA4F0,0xA4EF,0x0002,0xA4EE,0xA4ED,0x0004,0x0002,0xA4EC,0xA4EB,0x0002,0xA4EA,
+ 0xA4E9,0x0008,0x0004,0x0002,0xA4E8,0xA4E7,0x0002,0xA4E6,0xA4E5,0x0004,0x0002,0xA4E4,0xA4E3,0x0002,0xA4E2,0xA4E1,
+ 0x00C4,0x0000,0x0000,0x0000,0x00B7,0x007E,0x003E,0x001E,0x000E,0x0006,0x0002,0xA4E0,0x0002,0xA4DF,0xA4DE,0x0004,
+ 0x0002,0xA4DD,0xA4DC,0x0002,0xA4DB,0xA4DA,0x0008,0x0004,0x0002,0xA4D9,0xA4D8,0x0002,0xA4D7,0xA4D6,0x0004,0x0002,
+ 0xA4D5,0xA4D4,0x0002,0xA4D3,0xA4D2,0x0010,0x0008,0x0004,0x0002,0xA4D1,0xA4D0,0x0002,0xA4CF,0xA4CE,0x0004,0x0002,
+ 0xA4CD,0xA4CC,0x0002,0xA4CB,0xA4CA,0x0008,0x0004,0x0002,0xA4C9,0xA4C8,0x0002,0xA4C7,0xA4C6,0x0004,0x0002,0xA4C5,
+ 0xA4C4,0x0002,0xA4C3,0xA4C2,0x0020,0x0010,0x0008,0x0004,0x0002,0xA4C1,0xA4C0,0x0002,0xA4BF,0xA4BE,0x0004,0x0002,
+ 0xA4BD,0xA4BC,0x0002,0xA4BB,0xA4BA,0x0008,0x0004,0x0002,0xA4B9,0xA4B8,0x0002,0xA4B7,0xA4B6,0x0004,0x0002,0xA4B5,
+ 0xA4B4,0x0002,0xA4B3,0xA4B2,0x0010,0x0008,0x0004,0x0002,0xA4B1,0xA4B0,0x0002,0xA4AF,0xA4AE,0x0004,0x0002,0xA4AD,
+ 0xA4AC,0x0002,0xA4AB,0xA4AA,0x0008,0x0004,0x0002,0xA4A9,0xA4A8,0x0002,0xA4A7,0xA4A6,0x0004,0x0002,0xA4A5,0xA4A4,
+ 0x0002,0xA4A3,0xA4A2,0x000C,0x0006,0x0000,0x0000,0x0000,0x0000,0xA4A1,0x0001,0x0000,0x0000,0x0000,0xA8C6,0x0011,
+ 0x0005,0x0000,0x0001,0x0001,0xA1E5,0x0005,0x0001,0x0002,0xA1BD,0xA1BC,0x0004,0x0002,0xA1B3,0xA1B2,0x0000,0xA1FE,
+ 0x0010,0x0008,0x0004,0x0002,0xA1BF,0xA1BE,0x0002,0xA1BB,0xA1BA,0x0004,0x0002,0xA1B9,0xA1B8,0x0002,0xA1B7,0xA1B6,
+ 0x0005,0x0000,0x0002,0xA1B5,0xA1B4,0x0003,0x0000,0xA1A9,0x0002,0xA1A8,0xA1A3,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0002,0xA1A2,0xA1A1,0x010C,0x0001,0x0039,0x000D,0x0001,0x0000,0x0000,0x0005,0x0001,0x0001,0x0001,0xA1E1,
+ 0x0000,0x0000,0x0001,0xA1E2,0x0017,0x000C,0x0000,0x0000,0x0006,0x0003,0x0000,0xA1D1,0x0001,0xA1EE,0x0000,0x0000,
+ 0xA1EF,0x0001,0x0001,0x0005,0x0001,0x0002,0xA1F1,0xA1F2,0x0001,0x0000,0xA1F0,0x000E,0x0007,0x0000,0x0000,0x0001,
+ 0x0002,0xA1F3,0xA1F4,0x0000,0x0001,0x0001,0x0002,0xA1F7,0xA1F8,0x0000,0x0001,0x0000,0x0000,0x0001,0xA1F6,0x0092,
+ 0x0012,0x0007,0x0001,0x0001,0x0000,0x0000,0x0000,0xA3AF,0x0001,0x0001,0x0004,0x0000,0x0001,0xA1FE,0x0001,0x0002,
+ 0xA9EF,0xA9EE,0x0040,0x0020,0x0010,0x0008,0x0004,0x0002,0xA9ED,0xA9EC,0x0002,0xA9EB,0xA9EA,0x0004,0x0002,0xA9E9,
+ 0xA9E8,0x0002,0xA9E7,0xA9E6,0x0008,0x0004,0x0002,0xA9E5,0xA9E4,0x0002,0xA9E3,0xA9E2,0x0004,0x0002,0xA9E1,0xA9EF,
+ 0x0002,0xA9DF,0xA9DE,0x0010,0x0008,0x0004,0x0002,0xA9DD,0xA9DC,0x0002,0xA9DB,0xA9DA,0x0004,0x0002,0xA9D9,0xA9DF,
+ 0x0002,0xA9D7,0xA9D6,0x0008,0x0004,0x0002,0xA9D5,0xA9D4,0x0002,0xA9D3,0xA9D2,0x0004,0x0002,0xA9D1,0xA9D7,0x0002,
+ 0xA9CF,0xA9CE,0x0020,0x0010,0x0008,0x0004,0x0002,0xA9CD,0xA9CC,0x0002,0xA9CB,0xA9CA,0x0004,0x0002,0xA9C9,0xA9CF,
+ 0x0002,0xA9C7,0xA9C6,0x0008,0x0004,0x0002,0xA9C5,0xA9C4,0x0002,0xA9C3,0xA9C2,0x0004,0x0002,0xA9C1,0xA9C7,0x0002,
+ 0xA9BF,0xA9BE,0x0010,0x0008,0x0004,0x0002,0xA9BD,0xA9BC,0x0002,0xA9BB,0xA9BA,0x0004,0x0002,0xA9B9,0xA9B8,0x0002,
+ 0xA9B7,0xA9B6,0x0008,0x0004,0x0002,0xA9B5,0xA9B4,0x0002,0xA9B3,0xA9B2,0x0004,0x0002,0xA9B1,0xA9B0,0x0002,0xA9AF,
+ 0xA9AE,0x0018,0x0000,0x0000,0x0010,0x0008,0x0004,0x0002,0xA9AD,0xA9AC,0x0002,0xA9AB,0xA9AA,0x0004,0x0002,0xA9A9,
+ 0xA9A8,0x0002,0xA9A7,0xA9A7,0x0000,0x0000,0x0002,0xA9A5,0xA1AA,0x0001,0x0007,0x0001,0x0001,0x0001,0x0002,0xA2C4,
+ 0xA2C3,0x0010,0x0008,0x0004,0x0002,0xA2C2,0xA2C1,0x0002,0xA2C0,0xA2BF,0x0004,0x0002,0xA2BE,0xA2BD,0x0002,0xA2BC,
+ 0xA2BB,0x0008,0x0004,0x0002,0xA2BA,0xA2B9,0x0002,0xA2B8,0xA2B7,0x0004,0x0002,0xA2B6,0xA2B5,0x0002,0xA2B4,0xA2B3,
+ 0x005F,0x0048,0x0000,0x0000,0x002E,0x001E,0x000E,0x0006,0x0002,0xA2B2,0x0002,0xA2B1,0xA2D8,0x0004,0x0002,0xA2D7,
+ 0xA2D6,0x0002,0xA2D5,0xA2D4,0x0008,0x0004,0x0002,0xA2D3,0xA2D2,0x0002,0xA2D1,0xA2D0,0x0004,0x0002,0xA2CF,0xA2CE,
+ 0x0002,0xA2CD,0xA2CC,0x0000,0x0008,0x0004,0x0002,0xA2CB,0xA2CA,0x0002,0xA2C9,0xA2C8,0x0004,0x0002,0xA2C7,0xA2C6,
+ 0x0000,0xA2C5,0x0000,0x000F,0x0007,0x0003,0x0001,0xA2E2,0x0002,0xA2E1,0xA2E0,0x0004,0x0002,0xA2DF,0xA2DE,0x0002,
+ 0xA2DD,0xA2DC,0x0000,0x0004,0x0002,0xA2DB,0xA2DA,0x0000,0xA2D9,0x0009,0x0001,0x0001,0x0001,0x0001,0x0000,0x0000,
+ 0x0001,0xA1D0,0x0001,0x0001,0x0006,0x0000,0x0001,0x0001,0x0000,0xA1CD,0x0000,0x0000,0x0001,0x0000,0xA1D1,0x007D,
+ 0x0067,0x0021,0x0009,0x0001,0x0001,0x0001,0x0003,0x0001,0xA1DB,0x0000,0xA1DA,0x0012,0x0008,0x0001,0x0003,0x0001,
+ 0xA1DD,0x0002,0xA1DC,0xA1DD,0x0006,0x0003,0x0000,0xA1DC,0x0001,0xA1D4,0x0000,0x0000,0xA1D9,0x0001,0x0000,0x0001,
+ 0x0000,0xA1D5,0x0020,0x000D,0x0008,0x0004,0x0000,0x0000,0xA1D5,0x0000,0x0000,0xA1D6,0x0001,0x0001,0x0001,0xA1D7,
+ 0x000B,0x0004,0x0000,0x0000,0xA1AB,0x0003,0x0001,0xA1CB,0x0002,0xA1C3,0xA1DF,0x0004,0x0000,0x0000,0xA1E0,0x0001,
+ 0x0000,0xA1D3,0x0019,0x000E,0x0007,0x0003,0x0001,0xA1D2,0x0002,0xA1C8,0xA1C9,0x0004,0x0002,0xA1C5,0xA1C4,0x0001,
+ 0xA1AC,0x0004,0x0000,0x0001,0xA9A6,0x0003,0x0000,0xA1CF,0x0002,0xA1DE,0xA1D8,0x0005,0x0000,0x0001,0x0000,0xA1CC,
+ 0x0004,0x0001,0x0001,0xA1C6,0x0000,0x0001,0xA1C7,0x0008,0x0000,0x0000,0x0000,0x0001,0x0000,0x0000,0xA1CA,0x0001,
+ 0x0001,0x0001,0x0007,0x0003,0x0001,0xA1FD,0x0002,0xA1FA,0xA1FC,0x0000,0x0000,0xA1FB,0x0025,0x001D,0x0001,0x0000,
+ 0x000F,0x0007,0x0003,0x0001,0xA2FC,0x0002,0xA2FB,0xA2FA,0x0004,0x0002,0xA2F9,0xA2F8,0x0002,0xA2F7,0xA2F6,0x0008,
+ 0x0004,0x0002,0xA2F5,0xA2F4,0x0002,0xA2F3,0xA2F2,0x0000,0x0000,0xA2F1,0x0001,0x0001,0x0000,0x0001,0x0001,0x0000,
+ 0xA1ED,0x0000,0x0000,0x0000,0x0001,0x0000,0x0000,0x0001,0xA1E6,0x0032,0x0000,0x0000,0x0000,0x0000,0x0000,0x0001,
+ 0x0011,0x0001,0x0005,0x0000,0x0001,0x0000,0xA1F9,0x0007,0x0003,0x0000,0xA3E0,0x0002,0xA1E5,0xA1E4,0x0000,0x0001,
+ 0xA1EB,0x000C,0x0005,0x0001,0x0001,0x0001,0xA1AD,0x0000,0x0003,0x0000,0xA1A7,0x0001,0xA1A4,0x000A,0x0005,0x0000,
+ 0x0002,0xA1B1,0xA1B0,0x0000,0x0002,0xA1AF,0xA1AE,0x0000,0x0000,0x0000,0xA1AA,0x0001,0x0001,0x0108,0x00D5,0x0060,
+ 0x0001,0x001F,0x0001,0x000E,0x0006,0x0003,0x0001,0xA7D7,0x0001,0xA7F1,0x0004,0x0002,0xA7F0,0xA7EF,0x0002,0xA7EE,
+ 0xA7ED,0x0008,0x0004,0x0002,0xA7EC,0xA7EB,0x0002,0xA7EA,0xA7E9,0x0004,0x0002,0xA7E8,0xA7E7,0x0002,0xA7E6,0xA7E5,
+ 0x0020,0x0010,0x0008,0x0004,0x0002,0xA7E4,0xA7E3,0x0002,0xA7E2,0xA7E1,0x0004,0x0002,0xA7E0,0xA7DF,0x0002,0xA7DE,
+ 0xA7DD,0x0008,0x0004,0x0002,0xA7DC,0xA7DB,0x0002,0xA7DA,0xA7D9,0x0004,0x0002,0xA7D8,0xA7D6,0x0002,0xA7D5,0xA7D4,
+ 0x0010,0x0008,0x0004,0x0002,0xA7D3,0xA7D2,0x0002,0xA7D1,0xA7C1,0x0004,0x0002,0xA7C0,0xA7BF,0x0002,0xA7BE,0xA7BD,
+ 0x0008,0x0004,0x0002,0xA7BC,0xA7BB,0x0002,0xA7BA,0xA7B9,0x0004,0x0002,0xA7B8,0xA7B7,0x0002,0xA7B6,0xA7B5,0x0030,
+ 0x0029,0x0020,0x0010,0x0008,0x0004,0x0002,0xA7B4,0xA7B3,0x0002,0xA7B2,0xA7B1,0x0004,0x0002,0xA7B0,0xA7AF,0x0002,
+ 0xA7AE,0xA7AD,0x0008,0x0004,0x0002,0xA7AC,0xA7AB,0x0002,0xA7AA,0xA7A9,0x0004,0x0002,0xA7A8,0xA7A6,0x0002,0xA7A5,
+ 0xA7A4,0x0000,0x0000,0x0004,0x0002,0xA7A3,0xA7A2,0x0000,0xA7A1,0x0000,0x0000,0x0000,0x0000,0x0001,0xA7A7,0x0011,
+ 0x0001,0x0001,0x0007,0x0003,0x0001,0xA6D8,0x0002,0xA6D7,0xA6D6,0x0004,0x0002,0xA6D5,0xA6D4,0x0002,0xA6D3,0xA6D2,
+ 0x001F,0x000F,0x0007,0x0003,0x0001,0xA6D1,0x0002,0xA6D0,0xA6CF,0x0004,0x0002,0xA6CE,0xA6CD,0x0002,0xA6CC,0xA6CB,
+ 0x0008,0x0004,0x0002,0xA6CA,0xA6C9,0x0002,0xA6C8,0xA6C7,0x0004,0x0002,0xA6C6,0xA6C5,0x0002,0xA6C4,0xA6C3,0x0006,
+ 0x0000,0x0000,0x0002,0xA6C2,0xA6C1,0x0007,0x0003,0x0001,0xA6B8,0x0002,0xA6B7,0xA6B6,0x0004,0x0002,0xA6B5,0xA6B4,
+ 0x0002,0xA6B3,0xA1C6,0x0028,0x0000,0x0000,0x001F,0x000F,0x0007,0x0003,0x0001,0xA6B1,0x0002,0xA1C7,0xA6AF,0x0004,
+ 0x0002,0xA6AE,0xA6AD,0x0002,0xA6AC,0xA1C4,0x0008,0x0004,0x0002,0xA6AA,0xA6A9,0x0002,0xA6A8,0xA6A7,0x0004,0x0002,
+ 0xA6A6,0xA6A5,0x0002,0xA6A4,0xA6A3,0x0000,0x0000,0x0000,0x0002,0xA6A2,0xA6A1,0x0001,0x0000,0x0001,0x0001,0x0000,
+ 0x0003,0x0001,0xA1A5,0x0001,0xA1A6,0x001F,0x0001,0x0001,0x0000,0x0011,0x0005,0x0001,0x0001,0x0001,0xA8B8,0x0006,
+ 0x0003,0x0001,0xA8B7,0x0001,0xA8B6,0x0003,0x0001,0xA8B5,0x0001,0xA8B3,0x0000,0x0006,0x0003,0x0001,0xA8AF,0x0001,
+ 0xA8AB,0x0000,0x0001,0xA8A3,0x0016,0x0008,0x0001,0x0001,0x0001,0x0000,0x0000,0x0000,0xA8B1,0x0007,0x0001,0x0000,
+ 0x0001,0x0001,0x0000,0xA8AD,0x0001,0x0001,0x0000,0x0000,0x0000,0xA8A9,0x0030,0x000C,0x0006,0x0001,0x0000,0x0000,
+ 0x0000,0xA8A7,0x0000,0x0000,0x0000,0x0000,0xA8A5,0x0012,0x0008,0x0004,0x0001,0x0000,0xA8A1,0x0001,0x0001,0xA8B9,
+ 0x0006,0x0003,0x0001,0xA8B2,0x0000,0xA8B4,0x0000,0x0000,0xA1C2,0x000A,0x0005,0x0000,0x0002,0xA8AE,0xA8B0,0x0001,
+ 0x0002,0xA8AA,0xA8AC,0x0000,0x0003,0x0001,0xA8BA,0x0002,0xA8A6,0xA8A8,0x000C,0x0000,0x0006,0x0000,0x0001,0x0002,
+ 0xA8A2,0xA8A4,0x0001,0x0000,0x0000,0xA1C1,0x0001,0x0006,0x0000,0x0001,0x0002,0xA1C0,0xA1E3,0x0004,0x0001,0x0001,
+ 0xA1A7,0x0003,0x0000,0xA1EC,0x0001,0xA1E8,
+}; // ucs2_to_gb2312_elements
+
+const TConvFlatTree ucs2_to_gb2312 = {
+ 0x00A4, // min key
+ 0xFFE5, // max key
+ 0x0000, // links space start
+ 0x7FFF, // links space end
+ ucs2_to_gb2312_numelems, // number of elements
+ (treeval_t *)&ucs2_to_gb2312_elements // elements
+}; // ucs2_to_gb2312
+
+
+const size_t gb2312_to_ucs2_numelems = 15470;
+
+const treeval_t gb2312_to_ucs2_elements[gb2312_to_ucs2_numelems] = {
+ 0xC112,0xB0BD,0xA85E,0xA48F,0xA24B,0xA177,0xA0C0,0xA0AC,0xA056,0xA02A,0xA014,0xA00A,0xA004,0xA002,0x9F44,0x9F3E,
+ 0xA002,0x9F3D,0xA002,0x9F37,0x9F39,0xA004,0xA002,0x9F2F,0x9F2C,0xA002,0x9F22,0xA002,0x9EEF,0x9EF2,0xA00A,0xA004,
+ 0xA002,0x9EE5,0x9EE7,0xA002,0x9EF7,0xA002,0x9EE2,0x9EDF,0xA006,0xA002,0x9EE0,0xA002,0x9EDD,0x9EDC,0xA002,0x9EDB,
+ 0xA002,0x9E9F,0x9E9D,0xA016,0xA00A,0xA004,0xA002,0x93D6,0x9E92,0xA002,0x9E8B,0xA002,0x9E88,0x9E87,0xA006,0xA002,
+ 0x9E82,0xA002,0x7E3B,0x9EBE,0xA002,0x9EBD,0xA002,0x9B23,0x9B1F,0xA00A,0xA004,0xA002,0x9B22,0x9B0F,0xA002,0x9B08,
+ 0xA002,0x9AF9,0x9AED,0xA006,0xA002,0x9AFB,0xA002,0x9AEB,0x9AEF,0xA002,0x9AE6,0xA002,0x9AE1,0x9ADF,0xA02A,0xA014,
+ 0xA00A,0xA004,0xA002,0x9954,0x9955,0xA002,0x992E,0xA002,0x995C,0x9957,0xA004,0xA002,0x9B51,0x9B4D,0xA002,0x9B48,
+ 0xA002,0x9B4E,0x9B58,0xA00A,0xA004,0xA002,0x9B43,0x9B45,0xA002,0x9AD1,0xA002,0x9AD5,0x9AD6,0xA006,0xA002,0x9AC2,
+ 0xA002,0x9ACF,0x9AC0,0xA002,0x9AC1,0xA002,0x9ABC,0x9ABA,0xA016,0xA00A,0xA004,0xA002,0x9AB6,0x9E58,0xA002,0x9AB7,
+ 0xA002,0x9AB0,0x9AB1,0xA006,0xA002,0x97B4,0xA002,0x97B2,0x97A3,0xA002,0x97AB,0xA002,0x97C9,0x9794,0xA00A,0xA004,
+ 0xA002,0x9792,0x97C3,0xA002,0x9785,0xA002,0x977C,0x9CE2,0xA006,0xA002,0x9CDF,0xA002,0x9CDD,0x9CDC,0xA002,0x9CD9,
+ 0xA002,0x9CD8,0x9CD7,0xA000,0xA000,0xA000,0xA00A,0xA004,0xA002,0x9CD5,0x9CD4,0xA002,0x9CD3,0xA002,0x9CD0,0x9CCF,
+ 0xA004,0xA002,0x9CCE,0x9CCD,0xA000,0x9F07,0xA00A,0xA001,0xA001,0xA001,0xA001,0xA001,0xA001,0xA002,0x9CCB,0x9CCA,
+ 0xA056,0xA02A,0xA014,0xA00A,0xA004,0xA002,0x9CC7,0x9CC6,0xA002,0x9CC5,0xA002,0x9CC4,0x9CBD,0xA004,0xA002,0x9CBC,
+ 0x9CBB,0xA002,0x9CBA,0xA002,0x9CB7,0x9CB6,0xA00A,0xA004,0xA002,0x9CB5,0x9CB4,0xA002,0x9CB3,0xA002,0x9CB2,0x9CB1,
+ 0xA006,0xA002,0x9CB0,0xA002,0x9CAE,0x9CAD,0xA002,0x9CAB,0xA002,0x9CA9,0x9CA8,0xA016,0xA00A,0xA004,0xA002,0x9CA7,
+ 0x9CA6,0xA002,0x9CA5,0xA002,0x9CA3,0x9CA2,0xA006,0xA002,0x9CA1,0xA002,0x9CA0,0x9C9F,0xA002,0x9C9E,0xA002,0x9C9B,
+ 0x9C9A,0xA00A,0xA004,0xA002,0x9C95,0x9C94,0xA002,0x9C92,0xA002,0x9C91,0x9C90,0xA006,0xA002,0x9C8E,0xA002,0x9C8B,
+ 0x7A4C,0xA002,0x9C88,0xA002,0x9C87,0x9C86,0xA02B,0xA016,0xA00A,0xA004,0xA002,0x9C85,0x9C82,0xA002,0x9C7F,0xA002,
+ 0x946B,0x943E,0xA006,0xA002,0x938F,0xA002,0x93CA,0x936A,0xA002,0x93E8,0xA002,0x92C8,0x947E,0xA009,0xA004,0xA002,
+ 0x928E,0x96E0,0xA002,0x77BF,0xA000,0x96D2,0xA006,0xA002,0x96CB,0xA002,0x96BC,0x96B9,0xA002,0x9F0D,0xA002,0x9F0B,
+ 0x9EFE,0xA016,0xA00A,0xA004,0xA002,0x9F8C,0x9F8A,0xA002,0x9F89,0xA002,0x9F88,0x9F87,0xA006,0xA002,0x9F86,0xA002,
+ 0x9F85,0x9F83,0xA002,0x9F80,0xA002,0x973E,0x9730,0xA00A,0xA004,0xA002,0x9744,0x972A,0xA002,0x970E,0xA002,0x970F,
+ 0x9708,0xA006,0xA002,0x973D,0xA002,0x9706,0x96EF,0xA002,0x9742,0xA002,0x96E9,0x975A,0xA01D,0xA00E,0xA000,0xA000,
+ 0xA000,0xA000,0xA004,0xA002,0x8B26,0x8A3F,0xA002,0x89EF,0xA002,0x89EB,0x89E5,0xA001,0xA001,0xA001,0xA001,0xA005,
+ 0xA001,0xA002,0x89DC,0x89DA,0xA002,0x89F4,0xA002,0x89D6,0x659B,0xA0AC,0xA056,0xA02A,0xA014,0xA00A,0xA004,0xA002,
+ 0x8C94,0x8C98,0xA002,0x8C85,0xA002,0x8C8A,0x8C82,0xA004,0xA002,0x8C78,0x8E9E,0xA002,0x8EA6,0xA002,0x8E90,0x8E94,
+ 0xA00A,0xA004,0xA002,0x8EAA,0x8E85,0xA002,0x8E74,0xA002,0x8E6F,0x8E7C,0xA006,0xA002,0x8E76,0xA002,0x8E70,0x8E4A,
+ 0xA002,0x8E63,0xA002,0x8EA1,0x8E42,0xA016,0xA00A,0xA004,0xA002,0x8E41,0x8E49,0xA002,0x8E31,0xA002,0x8E3D,0x8E35,
+ 0xA006,0xA002,0x8E39,0xA002,0x8E40,0x8E3A,0xA002,0x8E91,0xA002,0x8E23,0x8E2E,0xA00A,0xA004,0xA002,0x8E93,0x8E1F,
+ 0xA002,0x8E1D,0xA002,0x8E14,0x8DFD,0xA006,0xA002,0x8E09,0xA002,0x8DE4,0x8E8B,0xA002,0x8E9A,0xA002,0x8DE3,0x8E55,
+ 0xA02A,0xA014,0xA00A,0xA004,0xA002,0x8E7A,0x8DEC,0xA002,0x8DC6,0xA002,0x8DDB,0x8DCF,0xA004,0xA002,0x8DCE,0x8E92,
+ 0xA002,0x8DDA,0xA002,0x8DD7,0x8DD6,0xA00A,0xA004,0xA002,0x8E4C,0x8DBA,0xA002,0x8DBC,0xA002,0x8DBF,0x8DB5,0xA006,
+ 0xA002,0x8E69,0xA002,0x8E59,0x8E05,0xA002,0x8DEB,0xA002,0x8E89,0x9E7E,0xA016,0xA00A,0xA004,0xA002,0x8C55,0x91BA,
+ 0xA002,0x91B4,0xA002,0x91B5,0x91AF,0xA006,0xA002,0x91AE,0xA002,0x91AD,0x91AA,0xA002,0x91A3,0xA002,0x91A2,0x9191,
+ 0xA00A,0xA004,0xA002,0x918D,0x9190,0xA002,0x9185,0xA002,0x918C,0x9179,0xA006,0xA002,0x9174,0xA002,0x9172,0x91C3,
+ 0xA002,0x91C5,0xA002,0x916F,0x9169,0xA000,0xA000,0xA000,0xA000,0xA004,0xA002,0x9170,0x9161,0xA000,0x9162,0xA0DA,
+ 0xA0C0,0xA015,0xA001,0xA001,0xA001,0xA006,0xA001,0xA001,0xA002,0x9164,0x914F,0xA006,0xA002,0x914E,0xA002,0x9150,
+ 0x914A,0xA002,0x8C49,0xA002,0x8C47,0x8D6D,0xA056,0xA02A,0xA014,0xA00A,0xA004,0xA002,0x8D67,0x8DB2,0xA002,0x8D91,
+ 0xA002,0x8D94,0x8D84,0xA004,0xA002,0x8D73,0x9EB4,0xA002,0x9EB8,0xA002,0x7E9B,0x7E47,0xA00A,0xA004,0xA002,0x7DAE,
+ 0x7DA6,0xA002,0x7E36,0xA002,0x7CF8,0x7FF3,0xA006,0xA002,0x7FEE,0xA002,0x7FE9,0x7FE6,0xA002,0x7FE1,0xA002,0x7FE5,
+ 0x7FD5,0xA016,0xA00A,0xA004,0xA002,0x7FCE,0x7FBF,0xA002,0x81EE,0xA002,0x826E,0x7CE8,0xA006,0xA002,0x7CD7,0xA002,
+ 0x7CC5,0x7CC8,0xA002,0x7CCD,0xA002,0x7CCC,0x7CC7,0xA00A,0xA004,0xA002,0x7CDD,0x7CBD,0xA002,0x7CBC,0xA002,0x7CB2,
+ 0x7CA2,0xA006,0xA002,0x7C9E,0xA002,0x7CF6,0x7CF2,0xA002,0x7C91,0xA002,0x6549,0x7C7C,0xA029,0xA014,0xA00A,0xA004,
+ 0xA002,0x7FB2,0x7FB0,0xA002,0x7FAF,0xA002,0x7FA7,0x7FA5,0xA004,0xA002,0x7F9D,0x895E,0xA002,0x88DF,0xA002,0x88D8,
+ 0x8888,0xA009,0xA004,0xA002,0x88CA,0x887E,0xA002,0x8268,0xA000,0x825F,0xA006,0xA002,0x824F,0xA002,0x824B,0x8249,
+ 0xA002,0x8244,0xA002,0x823E,0x8234,0xA016,0xA00A,0xA004,0xA002,0x8233,0x8263,0xA002,0x8238,0xA002,0x822B,0x8228,
+ 0xA006,0xA002,0x822F,0xA002,0x822D,0x8223,0xA002,0x8222,0xA002,0x8221,0x8844,0xA00A,0xA004,0xA002,0x81EC,0x8204,
+ 0xA002,0x8202,0xA002,0x8201,0x81FE,0xA006,0xA002,0x7C40,0xA002,0x7C5F,0x7C38,0xA002,0x7C26,0xA002,0x7C2A,0x7C1F,
+ 0xA001,0xA001,0xA001,0xA001,0xA00A,0xA004,0xA002,0x7C0B,0x7C6A,0xA002,0x7C0F,0xA002,0x7BFC,0x7BFE,0xA006,0xA002,
+ 0x7C0C,0xA002,0x7BEA,0x7BE6,0xA002,0x7BE5,0xA002,0x7BDA,0x7BDD,0xA0A8,0xA000,0xA056,0xA02A,0xA014,0xA00A,0xA004,
+ 0xA002,0x7BCC,0x7BC1,0xA002,0x7C23,0xA002,0x7BB4,0x7C2B,0xA004,0xA002,0x7BA2,0x7B9C,0xA002,0x7C1E,0xA002,0x7B85,
+ 0x7C5C,0xA00A,0xA004,0xA002,0x7B9D,0x7BDB,0xA002,0x7BB8,0xA002,0x7BCB,0x7C00,0xA006,0xA002,0x7B90,0xA002,0x7BE0,
+ 0x7B72,0xA002,0x7B62,0xA002,0x7B7B,0x7B6E,0xA016,0xA00A,0xA004,0xA002,0x7B60,0x7B8F,0xA002,0x7B4C,0xA002,0x7B75,
+ 0x7B45,0xA006,0xA002,0x7BF3,0xA002,0x7B58,0x7B1E,0xA002,0x7C69,0xA002,0x7B33,0x7B24,0xA00A,0xA004,0xA002,0x7B25,
+ 0x7B20,0xA002,0x7B31,0xA002,0x7B2E,0x7B19,0xA006,0xA002,0x7B2A,0xA002,0x7B38,0x7B47,0xA002,0x7B0F,0xA002,0x7B2B,
+ 0x7B0A,0xA02A,0xA014,0xA00A,0xA004,0xA002,0x7B67,0x7B32,0xA002,0x7BE4,0xA002,0x7B08,0x7AFD,0xA004,0xA002,0x7AFA,
+ 0x8210,0xA002,0x7F45,0xA002,0x7F44,0x7F4C,0xA00A,0xA004,0xA002,0x7F36,0x883C,0xA002,0x8839,0xA002,0x8821,0x881B,
+ 0xA006,0xA002,0x880A,0xA002,0x87FE,0x8813,0xA002,0x8816,0xA002,0x87EE,0x87E0,0xA016,0xA00A,0xA004,0xA002,0x87EA,
+ 0x87DB,0xA002,0x87CA,0xA002,0x87C0,0x87D1,0xA006,0xA002,0x87BD,0xA002,0x87D3,0x87CB,0xA002,0x87B3,0xA002,0x87B5,
+ 0x87AC,0xA00A,0xA004,0xA002,0x87E5,0x87AB,0xA002,0x8783,0xA002,0x8797,0x87AD,0xA000,0xA002,0x8785,0xA002,0x8788,
+ 0x87C6,0xA020,0xA001,0xA001,0xA008,0xA001,0xA001,0xA002,0x87D2,0xA002,0x87A8,0x87AF,0xA00A,0xA004,0xA002,0x8793,
+ 0x8765,0xA002,0x8759,0xA002,0x8764,0x87BB,0xA006,0xA002,0x8763,0xA002,0x8753,0x878B,0xA002,0x876E,0xA002,0x874C,
+ 0x8770,0xA056,0xA02A,0xA014,0xA00A,0xA004,0xA002,0x8760,0x877B,0xA002,0x8811,0xA002,0x877D,0x8722,0xA004,0xA002,
+ 0x8782,0x873F,0xA002,0x8E21,0xA002,0x8729,0x8731,0xA00A,0xA004,0xA002,0x8734,0x87C8,0xA002,0x873E,0xA002,0x871A,
+ 0x872E,0xA006,0xA002,0x8725,0xA002,0x871E,0x873B,0xA002,0x8723,0xA002,0x8709,0x870D,0xA016,0xA00A,0xA004,0xA002,
+ 0x870A,0x8708,0xA002,0x86F8,0xA002,0x8707,0x8704,0xA006,0xA002,0x86D1,0xA002,0x8746,0x86DF,0xA002,0x8810,0xA002,
+ 0x86DE,0x8713,0xA00A,0xA004,0xA002,0x86D0,0x8784,0xA002,0x86ED,0xA002,0x87EF,0x86FA,0xA006,0xA002,0x86EC,0xA002,
+ 0x86B4,0x87F6,0xA002,0x86C9,0xA002,0x86AF,0x86B1,0xA02C,0xA016,0xA00A,0xA004,0xA002,0x86BA,0x86B0,0xA002,0x8823,
+ 0xA002,0x86B5,0x86C4,0xA006,0xA002,0x86B6,0xA002,0x86A9,0x8693,0xA002,0x86AA,0xA002,0x86A3,0x86A7,0xA00A,0xA004,
+ 0xA002,0x8814,0x8706,0xA002,0x868B,0xA002,0x868D,0x86A8,0xA006,0xA002,0x867B,0xA002,0x867C,0x8696,0xA002,0x8806,
+ 0xA002,0x87E3,0x866F,0xA016,0xA00A,0xA004,0xA002,0x8654,0x864D,0xA002,0x98A6,0xA002,0x98A5,0x98A2,0xA006,0xA002,
+ 0x98A1,0xA002,0x989F,0x989E,0xA002,0x989B,0xA002,0x989A,0x9894,0xA000,0xA004,0xA002,0x988F,0x988D,0xA002,0x988C,
+ 0xA000,0x9889,0xA1B8,0xA0C3,0xA026,0xA001,0xA001,0xA001,0xA00D,0xA001,0xA006,0xA002,0x9883,0xA002,0x9880,0x9878,
+ 0xA002,0x8983,0xA002,0x8071,0x8075,0xA00A,0xA004,0xA002,0x8052,0x8079,0xA002,0x8046,0xA002,0x8043,0x8035,0xA006,
+ 0xA002,0x81F7,0xA002,0x8031,0x8028,0xA002,0x8029,0xA002,0x802C,0x8026,0xA000,0xA056,0xA02A,0xA014,0xA00A,0xA004,
+ 0xA002,0x8025,0x8022,0xA002,0x8020,0xA002,0x801C,0x8016,0xA004,0xA002,0x8014,0x8012,0xA002,0x77DC,0xA002,0x76B4,
+ 0x76B8,0xA00A,0xA004,0xA002,0x80E5,0x758B,0xA002,0x897B,0xA002,0x8966,0x8941,0xA006,0xA002,0x8936,0xA002,0x892B,
+ 0x8964,0xA002,0x890A,0xA002,0x8938,0x8913,0xA016,0xA00A,0xA004,0xA002,0x8919,0x8921,0xA002,0x88F0,0xA002,0x88FE,
+ 0x88E8,0xA006,0xA002,0x88FC,0xA002,0x891A,0x88F1,0xA002,0x8949,0xA002,0x895D,0x88CE,0xA00A,0xA004,0xA002,0x8933,
+ 0x88C9,0xA002,0x88BC,0xA002,0x88B7,0x8960,0xA006,0xA002,0x88A2,0xA002,0x8882,0x887F,0xA002,0x887D,0xA002,0x8872,
+ 0x8869,0xA02A,0xA014,0xA00A,0xA004,0xA002,0x8864,0x7AB3,0xA002,0x7AB6,0xA002,0x7AA8,0x7AAC,0xA004,0xA002,0x7AA0,
+ 0x7AC7,0xA002,0x7A95,0xA002,0x7A88,0x7A86,0xA00A,0xA004,0xA002,0x7A80,0x7A79,0xA002,0x7A78,0xA002,0x7AE6,0x7FCA,
+ 0xA006,0xA002,0x766F,0xA002,0x7672,0x7656,0xA002,0x765C,0xA002,0x7654,0x7669,0xA016,0xA00A,0xA004,0xA002,0x764D,
+ 0x7633,0xA002,0x766E,0xA002,0x7643,0x7635,0xA006,0xA002,0x766D,0xA002,0x7630,0x762D,0xA002,0x7640,0xA002,0x818C,
+ 0x7622,0xA000,0xA000,0xA002,0x763C,0x761B,0xA0C0,0xA02B,0xA001,0xA001,0xA013,0xA007,0xA001,0xA002,0x7619,0xA002,
+ 0x7615,0x7618,0xA006,0xA002,0x7625,0xA002,0x760A,0x761E,0xA002,0x760C,0xA002,0x7649,0x7600,0xA00A,0xA004,0xA002,
+ 0x7610,0x75FF,0xA002,0x75FC,0xA002,0x75F1,0x7603,0xA006,0xA002,0x75E7,0xA002,0x7647,0x75E4,0xA002,0x75E6,0xA002,
+ 0x7646,0x75E3,0xA056,0xA02A,0xA014,0xA00A,0xA004,0xA002,0x75CD,0x75D6,0xA002,0x75C2,0xA002,0x75C3,0x75B0,0xA004,
+ 0xA002,0x76B0,0x75C4,0xA002,0x75B8,0xA002,0x75FE,0x75B3,0xA00A,0xA004,0xA002,0x75A3,0x75AC,0xA002,0x759D,0xA002,
+ 0x7658,0x7664,0xA006,0xA002,0x7594,0xA002,0x7592,0x9E73,0xA002,0x9E6D,0xA002,0x9E71,0x9E6C,0xA016,0xA00A,0xA004,
+ 0xA002,0x9E6B,0x9E6A,0xA002,0x9E69,0xA002,0x9E68,0x9E67,0xA006,0xA002,0x9E66,0xA002,0x9E63,0x9E5E,0xA002,0x9E5C,
+ 0xA002,0x9E5B,0x9E5A,0xA00A,0xA004,0xA002,0x9E57,0x9E55,0xA002,0x9E51,0xA002,0x9E4E,0x9E4C,0xA006,0xA002,0x9E4B,
+ 0xA002,0x9E49,0x9E48,0xA002,0x9E47,0xA002,0x9E46,0x9E44,0xA02B,0xA016,0xA00A,0xA004,0xA002,0x9E42,0x9E41,0xA002,
+ 0x9E3E,0xA002,0x9E3A,0x9E39,0xA006,0xA002,0x9E37,0xA002,0x9E38,0x9E36,0xA002,0x9E31,0xA002,0x9E32,0x9E2C,0xA00A,
+ 0xA004,0xA002,0x9E2B,0x9E2A,0xA002,0x9E29,0xA002,0x9E28,0x9E22,0xA006,0xA002,0x9E20,0xA002,0x752C,0x74E0,0xA002,
+ 0x74DE,0xA000,0x76A4,0xA000,0xA00A,0xA004,0xA002,0x7693,0x768E,0xA002,0x7688,0xA002,0x7A70,0x99A5,0xA006,0xA002,
+ 0x9ECF,0xA002,0x7A61,0x7A37,0xA000,0x7A39,0xA001,0xA001,0xA008,0xA001,0xA001,0xA001,0xA001,0xA002,0x7A14,0x7A1E,
+ 0xA015,0xA009,0xA004,0xA002,0x7A02,0x7A03,0xA002,0x5D47,0xA000,0x7A06,0xA006,0xA002,0x79EB,0xA002,0x79ED,0x79D5,
+ 0xA002,0x96C9,0xA002,0x77EC,0x77E7,0xA00A,0xA004,0xA002,0x953A,0x9573,0xA002,0x9572,0xA002,0x9571,0x956F,0xA006,
+ 0xA002,0x956C,0xA002,0x956B,0x956A,0xA002,0x9569,0xA002,0x9568,0x9567,0xA154,0xA090,0xA000,0xA056,0xA02A,0xA014,
+ 0xA00A,0xA004,0xA002,0x9566,0x9565,0xA002,0x9564,0xA002,0x9562,0x9561,0xA004,0xA002,0x955D,0x955F,0xA002,0x955E,
+ 0xA002,0x955B,0x9559,0xA00A,0xA004,0xA002,0x9558,0x9557,0xA002,0x9556,0xA002,0x9554,0x9553,0xA006,0xA002,0x9552,
+ 0xA002,0x954F,0x954E,0xA002,0x954C,0xA002,0x9549,0x9546,0xA016,0xA00A,0xA004,0xA002,0x9545,0x9544,0xA002,0x9535,
+ 0xA002,0x9542,0x953F,0xA006,0xA002,0x953E,0xA002,0x953C,0x9538,0xA002,0x9537,0xA002,0x9536,0x9534,0xA00A,0xA004,
+ 0xA002,0x9532,0x9531,0xA002,0x952C,0xA002,0x9529,0x952B,0xA006,0xA002,0x952A,0xA002,0x9522,0x951F,0xA002,0x951E,
+ 0xA002,0x951D,0x951B,0xA02A,0xA014,0xA00A,0xA004,0xA002,0x9518,0x9516,0xA002,0x9515,0xA002,0x9514,0x9513,0xA004,
+ 0xA002,0x9512,0x950F,0xA002,0x950E,0xA002,0x950D,0x950A,0xA00A,0xA004,0xA002,0x9509,0x9507,0xA002,0x9506,0xA002,
+ 0x9502,0x9503,0xA006,0xA002,0x94FF,0xA002,0x94FD,0x94FC,0xA002,0x94F9,0xA002,0x94F7,0x94F5,0xA000,0xA00A,0xA004,
+ 0xA002,0x94F4,0x94F3,0xA002,0x94EF,0xA002,0x94EE,0x94EB,0xA000,0xA000,0x94E9,0xA039,0xA001,0xA00C,0xA001,0xA001,
+ 0xA004,0xA001,0xA001,0x94EA,0xA002,0x94E8,0xA002,0x94E7,0x94E5,0xA016,0xA00A,0xA004,0xA002,0x94E4,0x94E2,0xA002,
+ 0x94E0,0xA002,0x94DF,0x94DE,0xA006,0xA002,0x94DB,0xA002,0x94D8,0x94D9,0xA002,0x94D7,0xA002,0x94D6,0x94D5,0xA00A,
+ 0xA004,0xA002,0x94D2,0x94D1,0xA002,0x94D0,0xA002,0x94CE,0x94CD,0xA006,0xA002,0x94CC,0xA002,0x94CB,0x94CA,0xA002,
+ 0x94C9,0xA002,0x94C8,0x94C4,0xA056,0xA02A,0xA014,0xA00A,0xA004,0xA002,0x94BF,0x94BD,0xA002,0x94BC,0xA002,0x94BA,
+ 0x94B9,0xA004,0xA002,0x94B8,0x94B7,0xA002,0x94B6,0xA002,0x94B4,0x94B2,0xA00A,0xA004,0xA002,0x94B0,0x94AF,0xA002,
+ 0x94AC,0xA002,0x94AD,0x94AA,0xA006,0xA002,0x94AB,0xA002,0x94A4,0x94A3,0xA002,0x949C,0xA002,0x949B,0x949A,0xA016,
+ 0xA00A,0xA004,0xA002,0x9495,0x9497,0xA002,0x9494,0xA002,0x9490,0x948F,0xA006,0xA002,0x948D,0xA002,0x948C,0x948A,
+ 0xA002,0x948B,0xA002,0x9487,0x9486,0xA00A,0xA004,0xA002,0x9485,0x8832,0xA002,0x76E5,0xA002,0x76CD,0x7F7E,0xA006,
+ 0xA002,0x7F88,0xA002,0x7F79,0x7F71,0xA002,0x7F86,0xA002,0x7F68,0x8A48,0xA02C,0xA016,0xA00A,0xA004,0xA002,0x7F5F,
+ 0x7F61,0xA002,0x7F66,0xA002,0x7583,0x7579,0xA006,0xA002,0x7572,0xA002,0x755B,0x7548,0xA002,0x754B,0xA002,0x754E,
+ 0x7540,0xA00A,0xA004,0xA002,0x753A,0x77BD,0xA002,0x77B5,0xA002,0x77B0,0x77A0,0xA006,0xA002,0x779F,0xA002,0x7791,
+ 0x778C,0xA002,0x7780,0xA002,0x777D,0x778D,0xA000,0xA000,0xA004,0xA002,0x777F,0x7765,0xA000,0x96CE,0xA041,0xA001,
+ 0xA001,0xA013,0xA001,0xA006,0xA001,0xA001,0xA002,0x7768,0x775A,0xA006,0xA002,0x7743,0xA002,0x7747,0x77BC,0xA002,
+ 0x775E,0xA002,0x7738,0x7735,0xA016,0xA00A,0xA004,0xA002,0x7726,0x772D,0xA002,0x7719,0xA002,0x7722,0x771A,0xA006,
+ 0xA002,0x7708,0xA002,0x7707,0x76F9,0xA002,0x770D,0xA002,0x7704,0x76F1,0xA00A,0xA004,0xA002,0x9EFC,0x9EFB,0xA002,
+ 0x9EF9,0xA002,0x9F9B,0x7934,0xA006,0xA002,0x791E,0xA002,0x7924,0x7913,0xA002,0x78F4,0xA002,0x7905,0x78F2,0xA000,
+ 0xA056,0xA02A,0xA014,0xA00A,0xA004,0xA002,0x78EC,0x78C9,0xA002,0x78D9,0xA002,0x78D4,0x78A5,0xA004,0xA002,0x78B9,
+ 0x78B2,0xA002,0x78A3,0xA002,0x78A1,0x78E3,0xA00A,0xA004,0xA002,0x7887,0x789A,0xA002,0x7893,0xA002,0x78E7,0x786A,
+ 0xA006,0xA002,0x784C,0xA002,0x7847,0x7850,0xA002,0x7826,0xA002,0x78FD,0x7864,0xA016,0xA00A,0xA004,0xA002,0x786D,
+ 0x784E,0xA002,0x7829,0xA002,0x7823,0x782C,0xA006,0xA002,0x7825,0xA002,0x783C,0x781F,0xA002,0x7931,0xA002,0x792A,
+ 0x7839,0xA00A,0xA004,0xA002,0x781D,0x781C,0xA002,0x782D,0xA002,0x65B2,0x7811,0xA006,0xA002,0x7818,0xA002,0x7868,
+ 0x7809,0xA002,0x78AD,0xA002,0x77F8,0x78EF,0xA000,0xA014,0xA00A,0xA004,0xA002,0x6DFC,0x6FA9,0xA002,0x6C93,0xA002,
+ 0x807F,0x8080,0xA004,0xA002,0x6207,0x61E3,0xA002,0x61CB,0xA002,0x619D,0x61A9,0xA00A,0xA004,0xA002,0x615D,0x610D,
+ 0xA002,0x6106,0xA002,0x6128,0x6063,0xA006,0xA002,0x6059,0xA002,0x6041,0x6067,0xA002,0x605A,0xA002,0x605D,0x61DF,
+ 0xA435,0xA246,0xA10B,0xA0BF,0xA044,0xA001,0xA017,0xA001,0xA00A,0xA004,0xA002,0x5FD0,0x5FD1,0xA002,0x79B3,0xA002,
+ 0x79A7,0x799A,0xA006,0xA002,0x798A,0xA002,0x79AA,0x797A,0xA002,0x7967,0xA002,0x798E,0x7960,0xA016,0xA00A,0xA004,
+ 0xA002,0x7957,0x79B0,0xA002,0x795A,0xA002,0x7953,0x795C,0xA006,0xA002,0x795B,0xA002,0x7949,0x7946,0xA002,0x7940,
+ 0xA002,0x793B,0x6249,0xA00A,0xA004,0xA002,0x6248,0x6243,0xA002,0x623D,0xA002,0x623E,0x71B9,0xA006,0xA002,0x7166,
+ 0xA002,0x71FE,0x706C,0xA002,0x7228,0xA002,0x721D,0x71F9,0xA055,0xA02A,0xA014,0xA00A,0xA004,0xA002,0x71E7,0x71D4,
+ 0xA002,0x71E0,0xA002,0x71A0,0x71A8,0xA004,0xA002,0x71B5,0x71B3,0xA002,0x7198,0xA002,0x717A,0x7178,0xA00A,0xA004,
+ 0xA002,0x714A,0x7172,0xA002,0x7145,0xA002,0x7168,0x715C,0xA006,0xA002,0x7173,0xA002,0x7131,0x712F,0xA002,0x71DC,
+ 0xA002,0x7113,0x7110,0xA016,0xA00A,0xA004,0xA002,0x70CA,0x71C1,0xA002,0x70B1,0xA002,0x70AB,0x70B7,0xA006,0xA002,
+ 0x70C0,0xA002,0x70BB,0x7197,0xA002,0x71C9,0xA002,0x7152,0x716C,0xA00A,0xA004,0xA002,0x65D6,0x65D2,0xA002,0x65CE,
+ 0xA002,0x65CD,0x65C3,0xA005,0xA002,0x65C4,0xA000,0x8337,0xA002,0x6595,0xA002,0x9F51,0x6590,0xA000,0xA014,0xA00A,
+ 0xA004,0xA002,0x89F3,0x8F42,0xA002,0x5F40,0xA002,0x6BB3,0x98DA,0xA004,0xA002,0x98D9,0x98D5,0xA002,0x98D3,0xA002,
+ 0x98D2,0x98D1,0xA00A,0xA004,0xA002,0x6B59,0x6B46,0xA002,0x6B43,0xA002,0x6B39,0x6B37,0xA000,0xA002,0x6B5F,0xA002,
+ 0x81A6,0x81C1,0xA001,0xA001,0xA01E,0xA008,0xA001,0xA001,0xA002,0x81BB,0xA002,0x81CA,0x6726,0xA00A,0xA004,0xA002,
+ 0x81CC,0x81AA,0xA002,0x81A3,0xA002,0x6ED5,0x81CF,0xA006,0xA002,0x8182,0xA002,0x8188,0x5AB5,0xA002,0x584D,0xA002,
+ 0x8167,0x816D,0xA016,0xA00A,0xA004,0xA002,0x8183,0x817C,0xA002,0x8169,0xA002,0x8160,0x8171,0xA006,0xA002,0x815A,
+ 0xA002,0x8159,0x8174,0xA002,0x8153,0xA002,0x9183,0x8148,0xA00A,0xA004,0xA002,0x8132,0x8118,0xA002,0x812C,0xA002,
+ 0x811E,0x8161,0xA006,0xA002,0x8C5A,0xA002,0x8112,0x7739,0xA002,0x80FC,0xA002,0x80F2,0x810E,0xA077,0xA000,0xA056,
+ 0xA02A,0xA014,0xA00A,0xA004,0xA002,0x81BE,0x80ED,0xA002,0x80F4,0xA002,0x80F1,0x811B,0xA004,0xA002,0x80DD,0x80CA,
+ 0xA002,0x80D7,0xA002,0x80CD,0x80D9,0xA00A,0xA004,0xA002,0x80C4,0x80C2,0xA002,0x80DB,0xA002,0x81DA,0x80E9,0xA006,
+ 0xA002,0x80E8,0xA002,0x80E7,0x80B7,0xA002,0x991A,0xA002,0x80AD,0x80AB,0xA016,0xA00A,0xA004,0xA002,0x80B1,0x80BD,
+ 0xA002,0x670A,0xA002,0x80BC,0x8093,0xA006,0xA002,0x809C,0xA002,0x809F,0x5216,0xA002,0x8662,0xA002,0x7230,0x7256,
+ 0xA00A,0xA004,0xA002,0x7252,0x7258,0xA002,0x656B,0xA002,0x6555,0x6535,0xA006,0xA002,0x6C33,0xA002,0x6C2A,0x7D6A,
+ 0xA002,0x6C2C,0xA002,0x6C21,0x6C1A,0xA000,0xA014,0xA00A,0xA004,0xA002,0x6C19,0x6C18,0xA002,0x6C15,0xA002,0x6C0D,
+ 0x6C06,0xA004,0xA002,0x6C0C,0x6C05,0xA002,0x6BF9,0xA002,0x6BFF,0x6BFD,0xA000,0xA004,0xA002,0x6BF3,0x6BEA,0xA002,
+ 0x8004,0xA002,0x64D8,0x643F,0xA052,0xA001,0xA025,0xA00F,0xA005,0xA001,0xA001,0xA001,0x63B0,0xA004,0xA002,0x6332,
+ 0x6308,0xA002,0x7292,0xA002,0x728F,0x728D,0xA00A,0xA004,0xA002,0x7291,0x7284,0xA002,0x727F,0xA002,0x727E,0x726F,
+ 0xA006,0xA002,0x7266,0xA002,0x725D,0x729F,0xA002,0x726E,0xA002,0x89D1,0x89D0,0xA016,0xA00A,0xA004,0xA002,0x89CF,
+ 0x89CE,0xA002,0x89CC,0xA002,0x89CB,0x89CA,0xA006,0xA002,0x89C7,0xA002,0x8D59,0x8D55,0xA002,0x8D4D,0xA002,0x8D47,
+ 0x8D49,0xA00A,0xA004,0xA002,0x8D48,0x8D46,0xA002,0x8D45,0xA002,0x8D40,0x8D3D,0xA006,0xA002,0x8D3B,0xA002,0x8D36,
+ 0x8D33,0xA002,0x8D32,0xA002,0x66E9,0x66E6,0xA056,0xA02A,0xA014,0xA00A,0xA004,0xA002,0x66DC,0x66DB,0xA002,0x66BE,
+ 0xA002,0x669D,0x66D6,0xA004,0xA002,0x668C,0x6684,0xA002,0x6677,0xA002,0x6657,0x6661,0xA00A,0xA004,0xA002,0x6689,
+ 0x664F,0xA002,0x6641,0xA002,0x66C4,0x665F,0xA006,0xA002,0x8006,0xA002,0x66B1,0x6636,0xA002,0x6631,0xA002,0x6634,
+ 0x661D,0xA016,0xA00A,0xA004,0xA002,0x6705,0x7085,0xA002,0x6600,0xA002,0x6615,0x6603,0xA006,0xA002,0x6772,0xA002,
+ 0x66C7,0x660A,0xA002,0x65F0,0xA002,0x65EF,0x65EE,0xA00A,0xA004,0xA002,0x6534,0x7513,0xA002,0x7511,0xA002,0x750F,
+ 0x74FF,0xA006,0xA002,0x74F4,0xA002,0x750C,0x81E7,0xA002,0x622C,0xA002,0x6224,0x6225,0xA000,0xA016,0xA00A,0xA004,
+ 0xA002,0x6221,0x6222,0xA002,0x621F,0xA002,0x621B,0x6227,0xA006,0xA002,0x6214,0xA002,0x8ECE,0x8F9A,0xA002,0x8F98,
+ 0xA002,0x8F8F,0x8F8E,0xA000,0xA000,0xA000,0x8F8D,0xA0C3,0xA057,0xA001,0xA001,0xA029,0xA013,0xA009,0xA003,0xA001,
+ 0x8F8B,0xA002,0x8F87,0xA002,0x8F84,0x8F82,0xA004,0xA002,0x8F81,0x8F7E,0xA002,0x8F7C,0xA002,0x8F7A,0x8F79,0xA00A,
+ 0xA004,0xA002,0x8F77,0x8F78,0xA002,0x8F76,0xA002,0x8F75,0x8F73,0xA006,0xA002,0x8F72,0xA002,0x8F71,0x8F6D,0xA002,
+ 0x8F6B,0xA002,0x6BAA,0x6BAF,0xA016,0xA00A,0xA004,0xA002,0x6B9B,0x6BAB,0xA002,0x6B8D,0xA002,0x6BAE,0x6B9E,0xA006,
+ 0xA002,0x6B84,0xA002,0x6BA4,0x6B82,0xA002,0x6B81,0xA002,0x7352,0x7337,0xA00A,0xA004,0xA002,0x6AAB,0x8617,0xA002,
+ 0x6AA9,0xA002,0x7C37,0x6A91,0xA006,0xA002,0x6ADE,0xA002,0x6A58,0x6A28,0xA002,0x6A3D,0xA002,0x6AD3,0x6A8E,0xA000,
+ 0xA056,0xA02A,0xA014,0xA00A,0xA004,0xA002,0x6A35,0x6A5B,0xA002,0x6A50,0xA002,0x6AA0,0x6A3E,0xA004,0xA002,0x6A44,
+ 0x69F2,0xA002,0x6A65,0xA002,0x6A18,0x6A17,0xA00A,0xA004,0xA002,0x69ED,0x6AA3,0xA002,0x69FF,0xA002,0x698D,0x6AE7,
+ 0xA006,0xA002,0x6995,0xA002,0x6AB3,0x69CA,0xA002,0x69C1,0xA002,0x69B1,0x69D4,0xA016,0xA00A,0xA004,0xA002,0x69AD,
+ 0x69AB,0xA002,0x69BB,0xA002,0x69A7,0x699B,0xA006,0xA002,0x6979,0xA002,0x6963,0x6966,0xA002,0x6AF8,0xA002,0x69CE,
+ 0x6ADA,0xA00A,0xA004,0xA002,0x6AEC,0x69CC,0xA002,0x6934,0xA002,0x8429,0x6998,0xA006,0xA002,0x6980,0xA002,0x696B,
+ 0x6B16,0xA002,0x695D,0xA002,0x6A1D,0x6960,0xA000,0xA000,0xA00A,0xA004,0xA002,0x6939,0x6971,0xA002,0x6910,0xA002,
+ 0x68E3,0x6957,0xA004,0xA002,0x69E8,0x690B,0xA002,0x7BA0,0xA000,0x6B0F,0xA0C5,0xA05F,0xA009,0xA001,0xA001,0xA001,
+ 0xA001,0xA001,0xA002,0x6AC2,0x69E7,0xA02A,0xA014,0xA00A,0xA004,0xA002,0x6ADD,0x68FC,0xA002,0x696E,0xA002,0x6B1E,
+ 0x686B,0xA004,0xA002,0x6893,0x6877,0xA002,0x6CED,0xA002,0x688F,0x68B5,0xA00A,0xA004,0xA002,0x6829,0x6849,0xA002,
+ 0x684A,0xA002,0x6B12,0x69A4,0xA006,0xA002,0x6A9C,0xA002,0x6841,0x6A3A,0xA002,0x6855,0xA002,0x681D,0x6883,0xA016,
+ 0xA00A,0xA004,0xA002,0x69BF,0x6844,0xA002,0x6968,0xA002,0x684E,0x6A48,0xA006,0xA002,0x690F,0xA002,0x6833,0x6832,
+ 0xA002,0x6A89,0xA002,0x67C1,0x6ADF,0xA00A,0xA004,0xA002,0x67E2,0x67B8,0xA002,0x67C3,0xA002,0x6894,0x67DD,0xA006,
+ 0xA002,0x67B3,0xA002,0x67DA,0x67B5,0xA002,0x67D9,0xA002,0x6AE8,0x67B0,0xA056,0xA02A,0xA014,0xA00A,0xA004,0xA002,
+ 0x67E9,0x6AF3,0xA002,0x67D8,0xA002,0x6ADB,0x67F0,0xA004,0xA002,0x677C,0x6777,0xA002,0x678B,0xA002,0x689F,0x6A05,
+ 0xA00A,0xA004,0xA002,0x68D6,0x6775,0xA002,0x67A7,0xA002,0x6798,0x6773,0xA006,0xA002,0x676A,0xA002,0x6787,0x6AEA,
+ 0xA002,0x69AA,0xA002,0x6748,0x675E,0xA016,0xA00A,0xA004,0xA002,0x6753,0x674C,0xA002,0x97EC,0xA002,0x97EB,0x97EA,
+ 0xA006,0xA002,0x74BA,0xA002,0x74DA,0x74A7,0xA002,0x7490,0xA002,0x74A9,0x74A8,0xA00A,0xA004,0xA002,0x749E,0x748B,
+ 0xA002,0x74BF,0xA002,0x7481,0x7480,0xA006,0xA002,0x74D4,0xA002,0x749C,0x747E,0xA002,0x746D,0xA002,0x74A6,0x7459,
+ 0xA000,0xA000,0xA00A,0xA004,0xA002,0x7455,0x7457,0xA002,0x745C,0xA002,0x7441,0x741A,0xA000,0xA000,0x741B,0xA001,
+ 0xA00E,0xA001,0xA001,0xA001,0xA005,0xA001,0xA002,0x742C,0x742E,0xA002,0x7430,0xA002,0x83CE,0x7425,0xA02C,0xA016,
+ 0xA00A,0xA004,0xA002,0x7426,0x745B,0xA002,0x742A,0xA002,0x7489,0x743F,0xA006,0xA002,0x74BD,0xA002,0x73DE,0x73E7,
+ 0xA002,0x73E9,0xA002,0x740A,0x987C,0xA00A,0xA004,0xA002,0x73D9,0x73E5,0xA002,0x73C8,0xA002,0x73C9,0x73C0,0xA006,
+ 0xA002,0x73B3,0xA002,0x73B7,0x74CF,0xA002,0x73C2,0xA002,0x73CF,0x739F,0xA016,0xA00A,0xA004,0xA002,0x73A2,0x744B,
+ 0xA002,0x74A3,0xA002,0x738E,0x9095,0xA006,0xA002,0x753E,0xA002,0x5DDB,0x757F,0xA002,0x5E7A,0xA002,0x7F35,0x7F33,
+ 0xA00A,0xA004,0xA002,0x7F32,0x7F31,0xA002,0x97C1,0xA002,0x7F2F,0x7F2D,0xA006,0xA002,0x7F2C,0xA002,0x7F2B,0x7F2A,
+ 0xA002,0x7F27,0xA002,0x7F26,0x7F25,0xA1E3,0xA120,0xA05F,0xA000,0xA056,0xA02A,0xA014,0xA00A,0xA004,0xA002,0x7F24,
+ 0x7F23,0xA002,0x7F22,0xA002,0x7F21,0x7F1F,0xA004,0xA002,0x7F1B,0x7F1C,0xA002,0x7F19,0xA002,0x7F17,0x7F12,0xA00A,
+ 0xA004,0xA002,0x7F11,0x7F0F,0xA002,0x7F0C,0xA002,0x7F0B,0x7F08,0xA006,0xA002,0x7F07,0xA002,0x7F03,0x7F02,0xA002,
+ 0x7F01,0xA002,0x7EFE,0x7EFB,0xA016,0xA00A,0xA004,0xA002,0x7EFA,0x7EF6,0xA002,0x7F0D,0xA002,0x7EF2,0x7EF1,0xA006,
+ 0xA002,0x7EEF,0xA002,0x7EEE,0x7EEB,0xA002,0x7EE8,0xA002,0x7EE1,0x7EE0,0xA00A,0xA004,0xA002,0x7EDB,0x7ED7,0xA002,
+ 0x7ED4,0xA002,0x7ED0,0x7ECC,0xA006,0xA002,0x7ECB,0xA002,0x7EC9,0x7EC2,0xA002,0x977E,0xA002,0x7EC0,0x7EBE,0xA000,
+ 0xA000,0xA000,0xA000,0xA002,0x7EB0,0x7EAD,0xA06A,0xA014,0xA001,0xA001,0xA006,0xA001,0xA001,0xA002,0x7EA9,0x7EA8,
+ 0xA006,0xA002,0x7EA5,0xA002,0x7EA3,0x7EA1,0xA002,0x7E9F,0xA002,0x9AA7,0x9AA5,0xA02A,0xA014,0xA00A,0xA004,0xA002,
+ 0x9AA3,0x9AA2,0xA002,0x9AA0,0xA002,0x9A9F,0x9A9D,0xA004,0xA002,0x9A9C,0x9A9B,0xA002,0x9A98,0xA002,0x9A96,0x9A93,
+ 0xA00A,0xA004,0xA002,0x9A92,0x9A90,0xA002,0x9A8A,0xA002,0x9A88,0x9A85,0xA006,0xA002,0x9A81,0xA002,0x9A80,0x9A7D,
+ 0xA002,0x9A7F,0xA002,0x9A7A,0x9A78,0xA016,0xA00A,0xA004,0xA002,0x9A77,0x9A75,0xA002,0x5B62,0xA002,0x5B53,0x5B51,
+ 0xA006,0xA002,0x5B73,0xA002,0x5B65,0x5B5A,0xA002,0x5C1C,0xA002,0x5C15,0x5B40,0xA00A,0xA004,0xA002,0x5B37,0x5B32,
+ 0xA002,0x5B16,0xA002,0x5B17,0x5B09,0xA006,0xA002,0x5ADC,0xA002,0x5AD8,0x5AE6,0xA002,0x5AD6,0xA002,0x5B19,0x5AE3,
+ 0xA000,0xA02A,0xA014,0xA00A,0xA004,0xA002,0x5AE0,0x5AB8,0xA002,0x5B2A,0xA002,0x5B21,0x5AB2,0xA004,0xA002,0x5AEB,
+ 0x5ABE,0xA002,0x5A7A,0xA002,0x5A77,0x5A9B,0xA00A,0xA004,0xA002,0x5ABC,0x80EC,0xA002,0x5B0B,0xA002,0x5A62,0x5A3C,
+ 0xA006,0xA002,0x5A55,0xA002,0x5A4A,0x5A67,0xA002,0x5A50,0xA002,0x5A13,0x5A23,0xA016,0xA00A,0xA004,0xA002,0x5A11,
+ 0x5AFB,0xA002,0x5AA7,0xA002,0x5A09,0x5A0C,0xA006,0xA002,0x59F9,0xA002,0x59D8,0x59E3,0xA002,0x5B4C,0xA002,0x59DD,
+ 0x5B08,0xA00A,0xA004,0xA002,0x5A6D,0x59BE,0xA002,0x59D7,0xA002,0x601E,0x59B2,0xA006,0xA002,0x59D2,0xA002,0x59A4,
+ 0x599E,0xA002,0x5AAF,0xA002,0x59CA,0x5997,0xA071,0xA001,0xA018,0xA001,0xA001,0xA00A,0xA004,0xA002,0x59A3,0x5AD7,
+ 0xA002,0x5AF5,0xA002,0x598D,0x5983,0xA006,0xA002,0x5981,0xA002,0x5C6E,0x9B3B,0xA002,0x5F3C,0xA002,0x8274,0x5F2D,
+ 0xA02C,0xA016,0xA00A,0xA004,0xA002,0x5F29,0x5F33,0xA002,0x7FBC,0xA002,0x5C68,0x5C63,0xA006,0xA002,0x5B71,0xA002,
+ 0x5C59,0x5C50,0xA002,0x54AB,0xA002,0x5C3B,0x5F58,0xA00A,0xA004,0xA002,0x5F56,0x5F57,0xA002,0x5F50,0xA002,0x908B,
+ 0x9083,0xA006,0xA002,0x9088,0xA002,0x9082,0x907D,0xA002,0x9074,0xA002,0x66B9,0x905B,0xA016,0xA00A,0xA004,0xA002,
+ 0x9062,0x9058,0xA002,0x9068,0xA002,0x9050,0x9052,0xA006,0xA002,0x9051,0xA002,0x9044,0x902F,0xA002,0x902D,0xA002,
+ 0x9036,0x9035,0xA00A,0xA004,0xA002,0x9021,0x9016,0xA002,0x900D,0xA002,0x9011,0x9090,0xA006,0xA002,0x900B,0xA002,
+ 0x9004,0x9005,0xA002,0x8FE8,0xA002,0x9015,0x8FE6,0xA000,0xA000,0xA02A,0xA014,0xA00A,0xA004,0xA002,0x9087,0x8FE4,
+ 0xA002,0x8FEE,0xA002,0x8FE5,0x8FD5,0xA004,0xA002,0x8FD3,0x8FB6,0xA002,0x8B07,0xA002,0x8E47,0x5BF0,0xA00A,0xA004,
+ 0xA002,0x8930,0x5BEE,0xA002,0x5BE4,0xA002,0x6434,0x9A9E,0xA006,0xA002,0x752F,0xA002,0x5BB8,0x5BA5,0xA002,0x5B93,
+ 0xA002,0x5B95,0x5B84,0xA016,0xA00A,0xA004,0xA002,0x5B80,0x705E,0xA002,0x705D,0xA002,0x7035,0x7039,0xA006,0xA002,
+ 0x701B,0xA002,0x7023,0x701A,0xA002,0x6FEF,0xA002,0x6FE0,0x6FDE,0xA00A,0xA004,0xA002,0x6FEE,0x6FE1,0xA002,0x6FC2,
+ 0xA002,0x6FB6,0x6FB9,0xA000,0xA002,0x6FA7,0xA000,0x6FC9,0xA13F,0xA0C3,0xA078,0xA022,0xA001,0xA00B,0xA001,0xA004,
+ 0xA001,0xA001,0x7028,0xA002,0x6F7A,0xA002,0x6F7C,0x6F72,0xA00A,0xA004,0xA002,0x6F78,0x6F8C,0xA002,0x6F8D,0xA002,
+ 0x6F89,0x6F29,0xA006,0xA002,0x6F09,0xA002,0x6F2A,0x6F74,0xA002,0x7032,0xA002,0x6F36,0x6F2F,0xA02A,0xA014,0xA00A,
+ 0xA004,0xA002,0x6EF9,0x6F15,0xA002,0x6F24,0xA002,0x701F,0x7020,0xA004,0xA002,0x6F62,0x8245,0xA002,0x6EC2,0xA002,
+ 0x6E8F,0x6ECF,0xA00A,0xA004,0xA002,0x6EB4,0x6F77,0xA002,0x6EB7,0xA002,0x6EBB,0x6EBD,0xA006,0xA002,0x6EA7,0xA002,
+ 0x6EA5,0x6EE2,0xA002,0x6F2D,0xA002,0x7044,0x6E98,0xA016,0xA00A,0xA004,0xA002,0x6EB1,0x6EDF,0xA002,0x6E44,0xA002,
+ 0x6E25,0x6E32,0xA006,0xA002,0x6E54,0xA002,0x6E53,0x6F35,0xA002,0x6E5F,0xA002,0x6EB2,0x6E6B,0xA00A,0xA004,0xA002,
+ 0x6E4E,0x6E6E,0xA002,0x6E2B,0xA002,0x6DAE,0x6E0C,0xA006,0xA002,0x6DAB,0xA002,0x6E16,0x6DD9,0xA002,0x6DDD,0xA002,
+ 0x6DE6,0x6FA0,0xA000,0xA02A,0xA014,0xA00A,0xA004,0xA002,0x6DE0,0x6DBF,0xA002,0x7006,0xA002,0x6DDE,0x6DDB,0xA004,
+ 0xA002,0x6DC7,0x967C,0xA002,0x6D63,0xA002,0x6D7C,0x6D60,0xA00A,0xA004,0xA002,0x6D5C,0x6D94,0xA002,0x7106,0xA002,
+ 0x6D5E,0x6F7F,0xA006,0xA002,0x6DF6,0xA002,0x6D6F,0x6D91,0xA002,0x6D33,0xA002,0x6F6F,0x6EF8,0xA016,0xA00A,0xA004,
+ 0xA002,0x700F,0x6D1A,0xA002,0x6D35,0xA002,0x6D2E,0x6FAE,0xA006,0xA002,0x6D2B,0xA002,0x6D0E,0x6D19,0xA002,0x6D04,
+ 0xA002,0x6D07,0x6E5E,0xA000,0xA004,0xA002,0x6D79,0x6D0C,0xA002,0x6D27,0xA000,0x6D39,0xA001,0xA024,0xA001,0xA00D,
+ 0xA001,0xA006,0xA002,0x6D87,0xA002,0x6CEF,0x6CD3,0xA002,0x6CB1,0xA002,0x6CEE,0x6CEB,0xA00A,0xA004,0xA002,0x6FFC,
+ 0x6CD6,0xA002,0x6CE0,0xA002,0x6CB2,0x6CD7,0xA006,0xA002,0x6CF1,0xA002,0x7018,0x7027,0xA002,0x6CAD,0xA002,0x6CD4,
+ 0x6CD0,0xA02B,0xA015,0xA009,0xA004,0xA002,0x6E88,0x6C86,0xA002,0x6C76,0xA000,0x6C74,0xA006,0xA002,0x6C69,0xA002,
+ 0x6C8C,0x6C94,0xA002,0x6C90,0xA002,0x6C85,0x7043,0xA00A,0xA004,0xA002,0x6C4A,0x6C5C,0xA002,0x6C54,0xA002,0x6C35,
+ 0x6215,0xA006,0xA002,0x723F,0xA002,0x4E2C,0x961A,0xA002,0x9619,0xA002,0x9617,0x9616,0xA016,0xA00A,0xA004,0xA002,
+ 0x9615,0x9612,0xA002,0x960F,0xA002,0x960D,0x960C,0xA006,0xA002,0x9B29,0xA002,0x960A,0x9608,0xA002,0x9606,0xA002,
+ 0x9B2E,0x9603,0xA00A,0xA004,0xA002,0x95FE,0x95FC,0xA002,0x95F6,0xA002,0x95F5,0x95F3,0xA006,0xA002,0x95F1,0xA002,
+ 0x95EB,0x95E9,0xA002,0x96B3,0xA002,0x5FDD,0x61F5,0xA047,0xA000,0xA000,0xA02A,0xA014,0xA00A,0xA004,0xA002,0x61D4,
+ 0x61B7,0xA002,0x61A7,0xA002,0x6194,0x61AC,0xA004,0xA002,0x6175,0x614A,0xA002,0x612B,0xA002,0x610E,0x6100,0xA00A,
+ 0xA004,0xA002,0x60F4,0x6123,0xA002,0x6115,0xA002,0x6192,0x614D,0xA006,0xA002,0x60B4,0xA002,0x60DA,0x60C6,0xA002,
+ 0x60D8,0xA002,0x60DD,0x60B1,0xA016,0xA00A,0xA004,0xA002,0x60BB,0x611C,0xA002,0x609B,0xA002,0x608C,0x6092,0xA006,
+ 0xA002,0x6083,0xA002,0x609D,0x6173,0xA002,0x609A,0xA002,0x8A96,0x60F2,0xA000,0xA000,0xA000,0x606A,0xA082,0xA02C,
+ 0xA001,0xA015,0xA009,0xA003,0xA001,0x6042,0xA002,0x6137,0xA002,0x60FB,0x61E8,0xA006,0xA002,0x615F,0xA002,0x6021,
+ 0x61CC,0xA002,0x600A,0xA002,0x602B,0x6029,0xA00A,0xA004,0xA002,0x600D,0x600F,0xA002,0x601B,0xA002,0x6026,0x6035,
+ 0xA006,0xA002,0x6019,0xA002,0x5FF8,0x5FED,0xA002,0x5FEA,0xA002,0x6134,0x60B5,0xA02A,0xA014,0xA00A,0xA004,0xA002,
+ 0x613E,0x5FE4,0xA002,0x5FE1,0xA002,0x616A,0x5FEE,0xA004,0xA002,0x61AE,0x61FA,0xA002,0x5FD6,0xA002,0x5FC9,0x5FC4,
+ 0xA00A,0xA004,0xA002,0x81BA,0x5EEA,0xA002,0x5EE8,0xA002,0x5EDB,0x5ED1,0xA006,0xA002,0x5ED2,0xA002,0x8D53,0x5EB3,
+ 0xA002,0x5EBE,0xA002,0x83F4,0x5EB9,0xA016,0xA00A,0xA004,0xA002,0x5EA0,0x5EA5,0xA002,0x70F0,0xA002,0x5EAA,0x5EE1,
+ 0xA006,0xA002,0x5E80,0xA002,0x9995,0x9994,0xA002,0x9993,0xA002,0x9991,0x9990,0xA00A,0xA004,0xA002,0x998D,0x998A,
+ 0xA002,0x9987,0xA002,0x9984,0x9980,0xA006,0xA002,0x997D,0xA002,0x9977,0x9974,0xA002,0x996C,0xA002,0x996B,0x996A,
+ 0xA000,0xA02A,0xA014,0xA00A,0xA004,0xA002,0x9969,0x9968,0xA002,0x9967,0xA002,0x9963,0x5902,0xA004,0xA002,0x5924,
+ 0x98E7,0xA002,0x5925,0xA002,0x821B,0x8C86,0xA00A,0xA004,0xA002,0x736F,0x89DF,0xA002,0x7360,0xA002,0x7357,0x734D,
+ 0xA006,0xA002,0x7350,0xA002,0x7331,0x7338,0xA002,0x732C,0xA002,0x7325,0x7339,0xA000,0xA00A,0xA004,0xA002,0x7322,
+ 0x737C,0xA002,0x731D,0xA002,0x731E,0x730A,0xA006,0xA002,0x7380,0xA002,0x7313,0x7317,0xA000,0x72FB,0xA856,0xA3CC,
+ 0xA21E,0xA0C4,0xA089,0xA001,0xA033,0xA008,0xA001,0xA001,0xA001,0xA001,0xA002,0x72FA,0x736B,0xA016,0xA00A,0xA004,
+ 0xA002,0x72F3,0x7301,0xA002,0x72F7,0xA002,0x72F4,0x733B,0xA006,0xA002,0x72E9,0xA002,0x736A,0x72E8,0xA002,0x72D2,
+ 0xA002,0x72CD,0x72CE,0xA00A,0xA004,0xA002,0x72C1,0x72C3,0xA002,0x72B8,0xA002,0x7377,0x72B4,0xA006,0xA002,0x72B0,
+ 0xA002,0x72AD,0x5F61,0xA002,0x8862,0xA000,0x5FBC,0xA029,0xA013,0xA00A,0xA004,0xA002,0x5FAD,0x5FA8,0xA002,0x5F9C,
+ 0xA002,0x5F99,0x5FA0,0xA003,0xA001,0x5F89,0xA002,0x5F87,0xA002,0x5F82,0x5F77,0xA00A,0xA004,0xA002,0x5F73,0x5DD4,
+ 0xA002,0x5DB7,0xA002,0x8C73,0x5D9D,0xA006,0xA002,0x5D99,0xA002,0x5D82,0x5D74,0xA002,0x5D69,0xA002,0x5D4A,0x5D4B,
+ 0xA016,0xA00A,0xA004,0xA002,0x5D6B,0x5D81,0xA002,0x5D6F,0xA002,0x5D5B,0x5D6C,0xA006,0xA002,0x5D3D,0xA002,0x5D34,
+ 0x5D3E,0xA002,0x5DB8,0xA002,0x5D1B,0x5D06,0xA00A,0xA004,0xA002,0x5D1E,0x5D24,0xA002,0x5D2E,0xA002,0x5D26,0x5D27,
+ 0xA006,0xA002,0x5D0D,0xA002,0x5D97,0x5D22,0xA002,0x5CCB,0xA002,0x5DA0,0x5CD2,0xA000,0xA000,0xA02A,0xA014,0xA00A,
+ 0xA004,0xA002,0x5DA7,0x5CB7,0xA002,0x5CC1,0xA002,0x5CA3,0x5CB1,0xA004,0xA002,0x5CAB,0x5CAC,0xA002,0x5CBD,0xA002,
+ 0x5CA2,0x5CB5,0xA00A,0xA004,0xA002,0x5C9C,0x5D50,0xA002,0x5C91,0xA002,0x5DB4,0x5CF4,0xA006,0xA002,0x5C88,0xA002,
+ 0x5D87,0x5C90,0xA002,0x6C67,0xA002,0x5C7A,0x5C8C,0xA000,0xA00A,0xA004,0xA002,0x5E61,0x5E5E,0xA002,0x5E5B,0xA002,
+ 0x5E54,0x5E44,0xA000,0xA000,0x5E37,0xA0C2,0xA08E,0xA038,0xA00D,0xA001,0xA001,0xA005,0xA001,0xA002,0x5E57,0x5E58,
+ 0xA002,0x5E6C,0xA002,0x5E11,0x5E14,0xA016,0xA00A,0xA004,0xA002,0x889F,0x5E43,0xA002,0x571C,0xA002,0x5709,0x570A,
+ 0xA006,0xA002,0x5704,0xA002,0x56FF,0x56F9,0xA002,0x56EB,0xA002,0x5707,0x56E1,0xA009,0xA003,0xA000,0x56DD,0xA002,
+ 0x56D4,0xA002,0x56AF,0x5693,0xA006,0xA002,0x5685,0xA002,0x567C,0x567B,0xA002,0x566B,0xA002,0x5671,0x5664,0xA02A,
+ 0xA014,0xA00A,0xA004,0xA002,0x5686,0x5654,0xA002,0x564C,0xA002,0x5695,0x5659,0xA004,0xA002,0x5662,0x564D,0xA002,
+ 0x562C,0xA002,0x5657,0x5639,0xA00A,0xA004,0xA002,0x5658,0x562D,0xA002,0x5627,0xA002,0x5600,0x55FE,0xA006,0xA002,
+ 0x5623,0xA002,0x56B6,0x5601,0xA002,0x560C,0xA002,0x5608,0x561E,0xA016,0xA00A,0xA004,0xA002,0x8F94,0x55E4,0xA002,
+ 0x55F5,0xA002,0x55E8,0x6B36,0xA006,0xA002,0x55CC,0xA002,0x566F,0x55F2,0xA002,0x6EDC,0xA002,0x55EF,0x55C4,0xA00A,
+ 0xA004,0xA002,0x55DD,0x55E6,0xA002,0x55D4,0xA002,0x55EC,0x56C1,0xA006,0xA002,0x55D1,0xA002,0x561F,0x8186,0xA002,
+ 0x55F7,0xA002,0x55EA,0x5599,0xA000,0xA02A,0xA014,0xA00A,0xA004,0xA002,0x5594,0x56B3,0xA002,0x560D,0xA002,0x55DF,
+ 0x557B,0xA004,0xA002,0x7616,0x55D6,0xA002,0x557E,0xA002,0x5633,0x5581,0xA00A,0xA004,0xA002,0x5588,0x55B9,0xA002,
+ 0x55B1,0xA002,0x5583,0x55D2,0xA006,0xA002,0x558B,0xA002,0x555C,0x5530,0xA002,0x5533,0xA002,0x5577,0x5576,0xA000,
+ 0xA000,0xA004,0xA002,0x5575,0x5557,0xA000,0x5537,0xA001,0xA03F,0xA013,0xA001,0xA006,0xA001,0xA001,0xA002,0x553C,
+ 0x5550,0xA006,0xA002,0x553F,0xA002,0x5555,0x5541,0xA002,0x56C0,0xA002,0x5549,0x55B5,0xA016,0xA00A,0xA004,0xA002,
+ 0x558F,0x5616,0xA002,0x552A,0xA002,0x5527,0x5511,0xA006,0xA002,0x550F,0xA002,0x5523,0x55E9,0xA002,0x54F3,0xA002,
+ 0x5514,0x54FD,0xA00A,0xA004,0xA002,0x562E,0x54E7,0xA002,0x561C,0xA002,0x54DE,0x54CF,0xA006,0xA002,0x5665,0xA002,
+ 0x54A4,0x54AA,0xA002,0x54A9,0xA002,0x54DC,0x54DA,0xA02C,0xA016,0xA00A,0xA004,0xA002,0x5672,0x54CC,0xA002,0x54BF,
+ 0xA002,0x54BB,0x715F,0xA006,0xA002,0x54A3,0xA002,0x5472,0x55F6,0xA002,0x5635,0xA002,0x54A6,0x54A7,0xA00A,0xA004,
+ 0xA002,0x5660,0x54B4,0xA002,0x54C2,0xA002,0x54AD,0x54D0,0xA006,0xA002,0x549D,0xA002,0x5466,0x5476,0xA002,0x5484,
+ 0xA002,0x5680,0x549A,0xA016,0xA00A,0xA004,0xA002,0x5464,0x5471,0xA002,0x5477,0xA002,0x5494,0x5482,0xA006,0xA002,
+ 0x5432,0xA002,0x5423,0x54BC,0xA002,0x5504,0xA002,0x5421,0x5443,0xA00A,0xA004,0xA002,0x56A6,0x5454,0xA002,0x56C8,
+ 0xA002,0x5638,0x544B,0xA006,0xA002,0x5406,0xA002,0x5416,0x5412,0xA002,0x53FB,0xA002,0x53E8,0x53E9,0xA0EA,0xA02A,
+ 0xA000,0xA000,0xA000,0xA014,0xA00A,0xA004,0xA002,0x5630,0x53F1,0xA002,0x535F,0xA002,0x5F12,0x7519,0xA004,0xA002,
+ 0x8CA3,0x5F0B,0xA002,0x652E,0xA002,0x6525,0x6509,0xA00A,0xA004,0xA002,0x64E2,0x64E4,0xA002,0x64D7,0xA002,0x64D0,
+ 0x64C0,0xA006,0xA002,0x651B,0xA002,0x6499,0x64FC,0xA000,0x64F7,0xA099,0xA043,0xA017,0xA001,0xA00A,0xA004,0xA002,
+ 0x6496,0x646D,0xA002,0x6516,0xA002,0x645E,0x6421,0xA006,0xA002,0x6426,0xA002,0x640C,0x6420,0xA002,0x641B,0xA002,
+ 0x640B,0x6441,0xA016,0xA00A,0xA004,0xA002,0x6504,0x63BE,0xA002,0x63C6,0xA002,0x6452,0x63CE,0xA006,0xA002,0x63DE,
+ 0xA002,0x63C4,0x64B3,0xA002,0x63E0,0xA002,0x63F8,0x63F2,0xA00A,0xA004,0xA002,0x645C,0x63F5,0xA002,0x6369,0xA002,
+ 0x638A,0x63AC,0xA006,0xA002,0x636D,0xA002,0x6451,0x638E,0xA002,0x637A,0xA002,0x6371,0x63F6,0xA02A,0xA014,0xA00A,
+ 0xA004,0xA002,0x63AD,0x6343,0xA002,0x634B,0xA002,0x6339,0x62F6,0xA004,0xA002,0x649F,0x62EE,0xA002,0x62D7,0xA002,
+ 0x62DA,0x62CA,0xA00A,0xA004,0xA002,0x6375,0x6476,0xA002,0x636B,0xA002,0x624C,0x5C37,0xA006,0xA002,0x5C2C,0xA002,
+ 0x5C25,0x5C22,0xA002,0x530F,0xA002,0x5958,0x595A,0xA016,0xA00A,0xA004,0xA002,0x5955,0x8037,0xA002,0x5969,0xA002,
+ 0x593C,0x5F08,0xA006,0xA002,0x5EFE,0xA002,0x863C,0x8616,0xA002,0x8629,0xA002,0x8605,0x8627,0xA00A,0xA004,0xA002,
+ 0x85FF,0x85DC,0xA002,0x85C1,0xA002,0x861A,0x85B0,0xA006,0xA002,0x85B7,0xA002,0x85B9,0x8585,0xA002,0x859C,0xA002,
+ 0x85EA,0x8579,0xA000,0xA000,0xA014,0xA00A,0xA004,0xA002,0x858F,0x8587,0xA002,0x85A8,0xA002,0x85A4,0x857B,0xA004,
+ 0xA002,0x8604,0x8543,0xA002,0x77A2,0xA002,0x857A,0x855E,0xA00A,0xA004,0xA002,0x8564,0x8568,0xA002,0x8548,0xA002,
+ 0x8559,0x911D,0xA000,0xA002,0x84FF,0xA002,0x853B,0x8556,0xA0A3,0xA001,0xA04A,0xA01E,0xA008,0xA001,0xA001,0xA002,
+ 0x85FA,0xA002,0x851F,0x861E,0xA00A,0xA004,0xA002,0x84F0,0x8538,0xA002,0x750D,0xA002,0x850C,0x8577,0xA006,0xA002,
+ 0x84E5,0xA002,0x8497,0x84B4,0xA002,0x84B9,0xA002,0x84A1,0x863A,0xA016,0xA00A,0xA004,0xA002,0x84BA,0x84BF,0xA002,
+ 0x84CA,0xA002,0x84D3,0x84BD,0xA006,0xA002,0x9A40,0xA002,0x84D0,0x84CD,0xA002,0x84C1,0xA002,0x846D,0x8432,0xA00A,
+ 0xA004,0xA002,0x848E,0x851E,0xA002,0x8476,0xA002,0x8469,0x8446,0xA006,0xA002,0x843C,0xA002,0x8478,0x8562,0xA002,
+ 0x847A,0xA002,0x8488,0x8546,0xA02C,0xA016,0xA00A,0xA004,0xA002,0x8473,0x8459,0xA002,0x845A,0xA002,0x8451,0x845C,
+ 0xA006,0xA002,0x83E1,0xA002,0x83F0,0x8426,0xA002,0x83C0,0xA002,0x83C5,0x83EA,0xA00A,0xA004,0xA002,0x8445,0x83F8,
+ 0xA002,0x8403,0xA002,0x840F,0x83DF,0xA006,0xA002,0x8409,0xA002,0x8406,0x96C8,0xA002,0x8438,0xA002,0x841C,0x83D6,
+ 0xA016,0xA00A,0xA004,0xA002,0x83FD,0x83DD,0xA002,0x840B,0xA002,0x8418,0x5807,0xA006,0xA002,0x83D8,0xA002,0x83E5,
+ 0x8401,0xA002,0x83C1,0xA002,0x83BC,0x9DAF,0xA00A,0xA004,0xA002,0x83A8,0x839E,0xA002,0x8398,0xA002,0x837B,0x8555,
+ 0xA006,0xA002,0x8470,0xA002,0x83A9,0x859F,0xA002,0x837C,0xA002,0x849E,0x839C,0xA000,0xA000,0xA000,0xA014,0xA00A,
+ 0xA004,0xA002,0x8393,0x83AA,0xA002,0x83A0,0xA002,0x8435,0x8494,0xA004,0xA002,0x8378,0x83B0,0xA002,0x836E,0xA002,
+ 0x8452,0x84C0,0xA000,0xA004,0xA002,0x836C,0x85CE,0xA002,0x831B,0xA000,0x8541,0xA24A,0xA171,0xA0C3,0xA0A8,0xA052,
+ 0xA026,0xA010,0xA006,0xA001,0xA001,0xA002,0x8365,0x8366,0xA004,0xA002,0x8333,0x833A,0xA002,0x832D,0xA002,0x85BA,
+ 0x8317,0xA00A,0xA004,0xA002,0x8340,0x8588,0xA002,0x8343,0xA002,0x8347,0x834F,0xA006,0xA002,0x832F,0xA002,0x854E,
+ 0x839B,0xA002,0x8331,0xA002,0x8334,0x833C,0xA016,0xA00A,0xA004,0xA002,0x8392,0x8308,0xA002,0x84FD,0xA002,0x8558,
+ 0x8351,0xA006,0xA002,0x84A8,0xA002,0x82D5,0x82E0,0xA002,0x8315,0xA002,0x8314,0x8306,0xA00A,0xA004,0xA002,0x831A,
+ 0x8526,0xA002,0x82D3,0xA002,0x82FB,0x830C,0xA006,0xA002,0x82D8,0xA002,0x82D2,0x82F4,0xA002,0x82DC,0xA002,0x8307,
+ 0x8622,0xA02A,0xA014,0xA00A,0xA004,0xA002,0x82E4,0x82F7,0xA002,0x8309,0xA002,0x82E1,0x82A4,0xA004,0xA002,0x82E7,
+ 0x82C4,0xA002,0x829F,0xA002,0x82AA,0x82A1,0xA00A,0xA004,0xA002,0x82B4,0x82A9,0xA002,0x84EF,0xA002,0x8407,0x83A7,
+ 0xA006,0xA002,0x82AE,0xA002,0x82B7,0x8298,0xA002,0x82E3,0xA002,0x82CA,0x85F6,0xA016,0xA00A,0xA004,0xA002,0x82B0,
+ 0x82BE,0xA002,0x8553,0xA002,0x82AB,0x8299,0xA006,0xA002,0x858C,0xA002,0x8291,0x828E,0xA002,0x8284,0xA002,0x82A8,
+ 0x828A,0xA00A,0xA004,0xA002,0x828F,0x827F,0xA002,0x827D,0xA002,0x8279,0x61FF,0xA006,0xA002,0x9F19,0xA002,0x99A8,
+ 0x5880,0xA002,0x589A,0xA002,0x5889,0x5881,0xA000,0xA000,0xA014,0xA00A,0xA004,0xA002,0x586C,0x5865,0xA002,0x5820,
+ 0xA002,0x5844,0x5819,0xA004,0xA002,0x581E,0x5800,0xA002,0x57ED,0xA002,0x57FD,0x580D,0xA000,0xA000,0xA000,0x5874,
+ 0xA001,0xA055,0xA029,0xA013,0xA009,0xA003,0xA001,0x57DD,0xA002,0x57E4,0xA002,0x57F8,0x57EF,0xA004,0xA002,0x57F4,
+ 0x57B8,0xA002,0x57D2,0xA002,0x58CE,0x581D,0xA00A,0xA004,0xA002,0x5852,0x57D5,0xA002,0x57A0,0xA002,0x5793,0x57B4,
+ 0xA006,0xA002,0x57A7,0xA002,0x57CF,0x584F,0xA002,0x578C,0xA002,0x57A4,0x57E1,0xA016,0xA00A,0xA004,0xA002,0x5773,
+ 0x5776,0xA002,0x576D,0xA002,0x5768,0x963A,0xA006,0xA002,0x577C,0xA002,0x58DA,0x576B,0xA002,0x5785,0xA002,0x5769,
+ 0x5742,0xA00A,0xA004,0xA002,0x573B,0x58E2,0xA002,0x572F,0xA002,0x572E,0x58D9,0xA006,0xA002,0x5733,0xA002,0x572A,
+ 0x572C,0xA002,0x5729,0xA002,0x58D1,0x58C5,0xA02C,0xA016,0xA00A,0xA004,0xA002,0x58BC,0x587E,0xA002,0x57A1,0xA002,
+ 0x580A,0x574C,0xA006,0xA002,0x5DF0,0xA002,0x755A,0x5F01,0xA002,0x53B6,0xA002,0x9B2F,0x51FC,0xA00A,0xA004,0xA002,
+ 0x51F5,0x5EF4,0xA002,0x77CD,0xA002,0x71EE,0x53DF,0xA006,0xA002,0x52F0,0xA002,0x52D7,0x52D0,0xA002,0x54FF,0xA002,
+ 0x52BE,0x52AD,0xA016,0xA00A,0xA004,0xA002,0x52AC,0x52F1,0xA002,0x5950,0xA002,0x82BB,0x9146,0xA006,0xA002,0x9143,
+ 0xA002,0x9139,0x912F,0xA002,0x9131,0xA002,0x9123,0x911E,0xA00A,0xA004,0xA002,0x9122,0x9104,0xA002,0x90FE,0xA002,
+ 0x90EF,0x90EB,0xA006,0xA002,0x90DB,0xA002,0x90D7,0x90DC,0xA002,0x90E2,0xA002,0x9148,0x9106,0xA01F,0xA014,0xA000,
+ 0xA000,0xA000,0xA00A,0xA004,0xA002,0x90C7,0x90C4,0xA002,0x9136,0xA002,0x90BE,0x90C5,0xA004,0xA002,0x90DF,0x90B0,
+ 0xA000,0x90B8,0xA001,0xA001,0xA001,0xA001,0xA001,0xA002,0x9134,0xA002,0x90B6,0x90B3,0xA0AC,0xA056,0xA02A,0xA014,
+ 0xA00A,0xA004,0xA002,0x90B4,0x90A1,0xA002,0x9114,0xA002,0x9099,0x913A,0xA004,0xA002,0x909B,0x9097,0xA002,0x96B0,
+ 0xA002,0x9697,0x968D,0xA00A,0xA004,0xA002,0x9688,0x9674,0xA002,0x9672,0xA002,0x966C,0x9689,0xA006,0xA002,0x965F,
+ 0xA002,0x9654,0x9658,0xA002,0x9642,0xA002,0x963C,0x963D,0xA016,0xA00A,0xA004,0xA002,0x962A,0x9631,0xA002,0x9621,
+ 0xA002,0x9622,0x961D,0xA006,0xA002,0x5DF9,0xA002,0x5369,0x8C36,0xA002,0x8C35,0xA002,0x8C33,0x8C32,0xA00A,0xA004,
+ 0xA002,0x8C2F,0x8C2E,0xA002,0x8C2B,0xA002,0x8C2A,0x8C27,0xA006,0xA002,0x8C25,0xA002,0x8C21,0x8C20,0xA002,0x8C1F,
+ 0xA002,0x8C1D,0x8C18,0xA02A,0xA014,0xA00A,0xA004,0xA002,0x8C1B,0x8C19,0xA002,0x8C16,0xA002,0x8C15,0x8C14,0xA004,
+ 0xA002,0x8C12,0x8C11,0xA002,0x8C0F,0xA002,0x8C0C,0x8C07,0xA00A,0xA004,0xA002,0x8C04,0x8C02,0xA002,0x8C00,0xA002,
+ 0x8BFF,0x8BFC,0xA006,0xA002,0x8BF9,0xA002,0x8BF6,0x8BF3,0xA002,0x8BF0,0xA002,0x8BEE,0x8BE9,0xA016,0xA00A,0xA004,
+ 0xA002,0x8BE8,0x8BE4,0xA002,0x8BE0,0xA002,0x8BDF,0x8BDC,0xA006,0xA002,0x8BD9,0xA002,0x8BD8,0x8BD6,0xA002,0x8BD4,
+ 0xA002,0x8BD3,0x8BD2,0xA00A,0xA004,0xA002,0x8BCE,0x8BCF,0xA002,0x8BCB,0xA002,0x8BC3,0x8BC2,0xA006,0xA002,0x8BB7,
+ 0xA002,0x8BB5,0x8BB4,0xA002,0x8BAA,0xA002,0x8BA7,0x8BA6,0xA000,0xA000,0xA000,0xA000,0xA004,0xA002,0x8BA0,0x51A5,
+ 0xA002,0x585A,0xA002,0x5196,0x51C7,0xA0DA,0xA0BB,0xA010,0xA001,0xA001,0xA001,0xA001,0xA006,0xA002,0x51BC,0xA002,
+ 0x51BD,0x6C8D,0xA002,0x51AB,0xA002,0x7FB8,0x8803,0xA055,0xA02A,0xA014,0xA00A,0xA004,0xA002,0x5B34,0x7A1F,0xA002,
+ 0x88D2,0xA002,0x81E0,0x893B,0xA004,0xA002,0x88A4,0x88F7,0xA002,0x4EB3,0xA002,0x6C87,0x4EA0,0xA00A,0xA004,0xA002,
+ 0x5155,0x5919,0xA002,0x9CE7,0xA002,0x5310,0x8A07,0xA006,0xA002,0x530D,0xA002,0x52F9,0x5914,0xA002,0x5181,0xA002,
+ 0x9998,0x9ECC,0xA015,0xA009,0xA004,0xA002,0x5DFD,0x516E,0xA002,0x7CF4,0xA001,0x9FA0,0xA006,0xA002,0x67E4,0xA002,
+ 0x50C9,0x4F58,0xA002,0x6C46,0xA002,0x4EDD,0x510B,0xA00A,0xA004,0xA002,0x5107,0x50EE,0xA002,0x50E6,0xA002,0x50EC,
+ 0x50ED,0xA006,0xA002,0x5106,0xA002,0x50D6,0x50BA,0xA002,0x513A,0xA002,0x5110,0x513B,0xA02A,0xA014,0xA00A,0xA004,
+ 0xA002,0x50C2,0x50AF,0xA002,0x504E,0xA002,0x5048,0x5055,0xA004,0xA002,0x533D,0x50E8,0xA002,0x5028,0xA002,0x5025,
+ 0x500C,0xA00A,0xA004,0xA002,0x501C,0x4FFE,0xA002,0x502D,0xA002,0x502E,0x500F,0xA006,0xA002,0x502C,0xA002,0x4FF3,
+ 0x504C,0xA002,0x5029,0xA002,0x4FF8,0x4FDF,0xA016,0xA00A,0xA004,0xA002,0x4FD1,0x4FDC,0xA002,0x4FE3,0xA002,0x4FDA,
+ 0x4FC5,0xA006,0xA002,0x5137,0xA002,0x513C,0x5114,0xA002,0x4F94,0xA002,0x5102,0x4F7C,0xA00A,0xA004,0xA002,0x5115,
+ 0x8A82,0xA002,0x4F7E,0xA002,0x4F8F,0x884E,0xA006,0xA002,0x4F89,0xA002,0x59F7,0x4F74,0xA002,0x4F76,0xA002,0x4F3D,
+ 0x4F32,0xA009,0xA000,0xA000,0xA000,0xA000,0xA000,0xA002,0x4F57,0x4F5F,0xA001,0xA001,0xA001,0xA007,0xA001,0xA002,
+ 0x4F5D,0xA002,0x4F5A,0x6538,0xA006,0xA002,0x4F67,0xA002,0x4F5E,0x4F47,0xA002,0x4F09,0xA002,0x5096,0x5000,0xA0AC,
+ 0xA000,0xA056,0xA02A,0xA014,0xA00A,0xA004,0xA002,0x4EF5,0x4F64,0xA002,0x4F22,0xA002,0x4EF3,0x50B4,0xA004,0xA002,
+ 0x4EDE,0x4EEB,0xA002,0x4EE1,0xA002,0x4EE8,0x961E,0xA00A,0xA004,0xA002,0x4EC9,0x4EC3,0xA002,0x4EBB,0xA002,0x7F54,
+ 0x5182,0xA006,0xA002,0x5293,0xA002,0x5290,0x5281,0xA002,0x5282,0xA002,0x527D,0x84AF,0xA016,0xA00A,0xA004,0xA002,
+ 0x525C,0x639E,0xA002,0x525E,0xA002,0x524C,0x5274,0xA006,0xA002,0x528C,0xA002,0x5233,0x5244,0xA002,0x520E,0xA002,
+ 0x5208,0x5202,0xA00A,0xA004,0xA002,0x5363,0x5366,0xA002,0x8D5C,0xA002,0x533E,0x5331,0xA006,0xA002,0x532D,0xA002,
+ 0x53F5,0x531A,0xA002,0x8D5D,0xA002,0x9768,0x5EDD,0xA02A,0xA014,0xA00A,0xA004,0xA002,0x53A5,0x53B4,0xA002,0x539D,
+ 0xA002,0x5399,0x4EC4,0xA004,0xA002,0x560F,0x55C7,0xA002,0x5B5B,0xA002,0x8288,0x4E93,0xA00A,0xA004,0xA002,0x4E69,
+ 0x4E5C,0xA002,0x9F10,0xA002,0x4E9F,0x4E36,0xA006,0xA002,0x9F17,0xA002,0x777E,0x6BD3,0xA002,0x9997,0xA002,0x80E4,
+ 0x56DF,0xA016,0xA00A,0xA004,0xA002,0x6C10,0x536E,0xA002,0x723B,0xA002,0x6B80,0x4E47,0xA006,0xA002,0x5315,0xA002,
+ 0x4E3F,0x79BA,0xA002,0x4E28,0xA002,0x5669,0x5B6C,0xA00A,0xA004,0xA002,0x9B32,0x4E1E,0xA002,0x4E99,0xA002,0x4E15,
+ 0x5345,0xA006,0xA002,0x5EFF,0xA002,0x4E10,0xFA0C,0xA002,0x4E0C,0xA000,0x4E8D,0xA014,0xA001,0xA001,0xA001,0xA005,
+ 0xA001,0xA001,0xA001,0x5EA7,0xA006,0xA002,0x5750,0xA002,0x4F5C,0x505A,0xA002,0x67DE,0xA002,0x4F50,0x5DE6,0xA056,
+ 0xA02A,0xA014,0xA00A,0xA004,0xA002,0x6628,0x9075,0xA002,0x5C0A,0xA002,0x7F6A,0x6700,0xA004,0xA002,0x9189,0x5634,
+ 0xA002,0x7E82,0xA002,0x94BB,0x7EC4,0xA00A,0xA004,0xA002,0x963B,0x8BC5,0xA002,0x7956,0xA002,0x65CF,0x5352,0xA006,
+ 0xA002,0x8DB3,0xA002,0x79DF,0x63CD,0xA002,0x594F,0xA002,0x8D70,0x9112,0xA016,0xA00A,0xA004,0xA002,0x7EB5,0x7E3D,
+ 0xA002,0x7EFC,0xA002,0x5B97,0x8E64,0xA006,0xA002,0x68D5,0xA002,0x9B03,0x5B57,0xA002,0x6F2C,0xA002,0x81EA,0x5B50,
+ 0xA00A,0xA004,0xA002,0x6ED3,0x7C7D,0xA002,0x4ED4,0xA002,0x7D2B,0x5B5C,0xA006,0xA002,0x6DC4,0xA002,0x6ECB,0x59FF,
+ 0xA002,0x8D44,0xA002,0x54A8,0x8332,0xA02C,0xA016,0xA00A,0xA004,0xA002,0x6FC1,0x707C,0xA002,0x8457,0xA002,0x5545,
+ 0x914C,0xA006,0xA002,0x8301,0xA002,0x7422,0x684C,0xA002,0x5353,0xA002,0x62D9,0x6349,0xA00A,0xA004,0xA002,0x7DA7,
+ 0x8C06,0xA002,0x7F00,0xA002,0x589C,0x8D58,0xA006,0xA002,0x8FFD,0xA002,0x9525,0x690E,0xA002,0x72C0,0xA002,0x58EF,
+ 0x649E,0xA016,0xA00A,0xA004,0xA002,0x599D,0x88DD,0xA002,0x838A,0xA002,0x6A01,0x7BC6,0xA006,0xA002,0x8D5A,0xA002,
+ 0x64B0,0x8F6C,0xA002,0x78DA,0xA002,0x8011,0x62FD,0xA00A,0xA004,0xA002,0x722A,0x6293,0xA002,0x9A7B,0xA002,0x795D,
+ 0x9252,0xA000,0xA000,0x4F4F,0xA3CF,0xA1B5,0xA0C4,0xA023,0xA001,0xA001,0xA001,0xA00B,0xA001,0xA004,0xA001,0xA001,
+ 0x7BC9,0xA002,0x94F8,0xA002,0x8D2E,0x86C0,0xA009,0xA004,0xA002,0x52A9,0x67F1,0xA001,0xA002,0x4E3B,0x56D1,0xA006,
+ 0xA002,0x77DA,0xA002,0x62C4,0x716E,0xA002,0x71ED,0xA002,0x7AF9,0x9010,0xA000,0xA056,0xA02A,0xA014,0xA00A,0xA004,
+ 0xA002,0x8BDB,0x8BF8,0xA002,0x8C6C,0xA002,0x7843,0x86DB,0xA004,0xA002,0x682A,0x73E0,0xA002,0x9AA4,0xA002,0x665D,
+ 0x5B99,0xA00A,0xA004,0xA002,0x76BA,0x5492,0xA002,0x5E1A,0xA002,0x8098,0x8F74,0xA006,0xA002,0x7CA5,0xA002,0x8BCC,
+ 0x6D32,0xA002,0x5DDE,0xA002,0x9031,0x821F,0xA016,0xA00A,0xA004,0xA002,0x773E,0x4EF2,0xA002,0x91CD,0xA002,0x816B,
+ 0x7A2E,0xA006,0xA002,0x7EC8,0xA002,0x8877,0x949F,0xA002,0x5FE0,0xA002,0x76C5,0x4E2D,0xA00A,0xA004,0xA002,0x7A92,
+ 0x6CBB,0xA002,0x6EEF,0xA002,0x75D4,0x7099,0xA006,0xA002,0x8D28,0xA002,0x7A1A,0x79E9,0xA002,0x667A,0xA002,0x88FD,
+ 0x5EA4,0xA02A,0xA014,0xA00A,0xA004,0xA002,0x5E5F,0x7F6E,0xA002,0x81F4,0xA002,0x81F3,0x64F2,0xA004,0xA002,0x646F,
+ 0x8A8C,0xA002,0x7EB8,0xA002,0x65E8,0x96BB,0xA00A,0xA004,0xA002,0x8DBE,0x6B62,0xA002,0x6307,0xA002,0x962F,0x59EA,
+ 0xA006,0xA002,0x503C,0xA002,0x6267,0x6B96,0xA002,0x690D,0xA002,0x76F4,0x8077,0xA016,0xA00A,0xA004,0xA002,0x7EC7,
+ 0x4E4B,0xA002,0x6C41,0xA002,0x8102,0x80D1,0xA006,0xA002,0x77E5,0xA002,0x8718,0x5431,0xA002,0x652F,0xA002,0x679D,
+ 0x829D,0xA000,0xA004,0xA002,0x8BC1,0x912D,0xA002,0x7665,0xA000,0x5E40,0xA0C2,0xA029,0xA001,0xA001,0xA011,0xA005,
+ 0xA001,0xA001,0xA001,0x653F,0xA006,0xA002,0x6B63,0xA002,0x649C,0x6574,0xA002,0x6014,0xA002,0x722D,0x7319,0xA00A,
+ 0xA004,0xA002,0x5FB5,0x775C,0xA002,0x6399,0xA002,0x84B8,0x9663,0xA006,0xA002,0x9547,0xA002,0x632F,0x9707,0xA002,
+ 0x8BCA,0xA002,0x75B9,0x6795,0xA056,0xA02A,0xA014,0xA00A,0xA004,0xA002,0x9049,0x9488,0xA002,0x8D1E,0xA002,0x81FB,
+ 0x78AA,0xA004,0xA002,0x7504,0x771F,0xA002,0x659F,0xA002,0x8897,0x6D59,0xA00A,0xA004,0xA002,0x9019,0x8517,0xA002,
+ 0x9517,0xA002,0x8005,0x8F99,0xA006,0xA002,0x87C4,0xA002,0x608A,0x647A,0xA002,0x906E,0xA002,0x53EC,0x8087,0xA016,
+ 0xA00A,0xA004,0xA002,0x5146,0x7F69,0xA002,0x7167,0xA002,0x8D99,0x6CBC,0xA006,0xA002,0x627E,0xA002,0x662D,0x62DB,
+ 0xA002,0x969C,0xA002,0x7634,0x8139,0xA00A,0xA004,0xA002,0x4ED7,0x8D26,0xA002,0x5E33,0xA002,0x4E08,0x6756,0xA006,
+ 0xA002,0x6F32,0xA002,0x638C,0x5F35,0xA002,0x6F33,0xA002,0x5F70,0x7AE0,0xA02C,0xA016,0xA00A,0xA004,0xA002,0x6A1F,
+ 0x7EFD,0xA002,0x6E5B,0xA002,0x7AD9,0x6230,0xA006,0xA002,0x5360,0xA002,0x68E7,0x8638,0xA002,0x5C55,0xA002,0x5D84,
+ 0x8F97,0xA00A,0xA004,0xA002,0x65AC,0x76DE,0xA002,0x9711,0xA002,0x7C98,0x8A79,0xA006,0xA002,0x6C08,0xA002,0x77BB,
+ 0x5BE8,0xA002,0x50B5,0xA002,0x7A84,0x5B85,0xA000,0xA00A,0xA004,0xA002,0x9F4B,0x6458,0xA002,0x8BC8,0xA002,0x7160,
+ 0x4E4D,0xA006,0xA002,0x548B,0xA002,0x69A8,0x6805,0xA002,0x7728,0xA002,0x95F8,0x94E1,0xA001,0xA001,0xA001,0xA016,
+ 0xA00A,0xA004,0xA002,0x8F67,0x672D,0xA002,0x6E23,0xA002,0x55B3,0x7D2E,0xA006,0xA002,0x8D60,0xA002,0x66FE,0x618E,
+ 0xA002,0x589E,0xA002,0x600E,0x8D3C,0xA00A,0xA004,0xA002,0x6FA4,0x5247,0xA002,0x64C7,0xA002,0x8D23,0x71E5,0xA006,
+ 0xA002,0x7076,0xA002,0x7682,0x9020,0xA002,0x8B5F,0xA002,0x8E81,0x86A4,0xA156,0xA093,0xA000,0xA056,0xA02A,0xA014,
+ 0xA00A,0xA004,0xA002,0x6FA1,0x65E9,0xA002,0x68D7,0xA002,0x85FB,0x947F,0xA004,0xA002,0x7CDF,0x906D,0xA002,0x846C,
+ 0xA002,0x9AD2,0x8D43,0xA00A,0xA004,0xA002,0x8D5E,0x66AB,0xA002,0x6522,0xA002,0x5592,0x5728,0xA006,0xA002,0x518D,
+ 0xA002,0x8F7D,0x5BB0,0xA002,0x83D1,0xA002,0x54C9,0x683D,0xA016,0xA00A,0xA004,0xA002,0x96DC,0x7838,0xA002,0x531D,
+ 0xA002,0x5B55,0x97FB,0xA006,0xA002,0x6688,0xA002,0x919E,0x860A,0xA002,0x904B,0xA002,0x5141,0x9695,0xA00A,0xA004,
+ 0xA002,0x5300,0x9116,0xA002,0x96F2,0xA002,0x8018,0x9605,0xA006,0xA002,0x60A6,0xA002,0x6708,0x7CB5,0xA002,0x5DBD,
+ 0xA002,0x94A5,0x8E8D,0xA02A,0xA014,0xA00A,0xA004,0xA002,0x8D8A,0x7EA6,0xA002,0x66F0,0xA002,0x9662,0x6028,0xA004,
+ 0xA002,0x9858,0x82D1,0xA002,0x9060,0xA002,0x7F18,0x6E90,0xA00A,0xA004,0xA002,0x733F,0x5713,0xA002,0x54E1,0xA002,
+ 0x5712,0x8F95,0xA006,0xA002,0x63F4,0xA002,0x539F,0x8881,0xA002,0x57A3,0xA002,0x5143,0x51A4,0xA000,0xA00A,0xA004,
+ 0xA002,0x6E0A,0x9E33,0xA002,0x9A6D,0xA002,0x8C6B,0x9884,0xA000,0xA002,0x88D5,0xA002,0x5BD3,0x6D74,0xA035,0xA001,
+ 0xA008,0xA001,0xA001,0xA001,0xA001,0xA002,0x8B7D,0x80B2,0xA016,0xA00A,0xA004,0xA002,0x7344,0x6B32,0xA002,0x7652,
+ 0xA002,0x79A6,0x5CEA,0xA006,0xA002,0x55BB,0xA002,0x9047,0x7C72,0xA002,0x9B31,0xA002,0x828B,0x57DF,0xA00A,0xA004,
+ 0xA002,0x7389,0x7FBD,0xA002,0x8BED,0xA002,0x5B87,0x79B9,0xA006,0xA002,0x5DBC,0xA002,0x8207,0x96E8,0xA002,0x5A31,
+ 0xA002,0x4E88,0x9685,0xA056,0xA02A,0xA014,0xA00A,0xA004,0xA002,0x6F01,0x6E1D,0xA002,0x6B48,0xA002,0x9C7C,0x903E,
+ 0xA004,0xA002,0x4FDE,0x9918,0xA002,0x8F3F,0xA002,0x611A,0x865E,0xA00A,0xA004,0xA002,0x6986,0x76C2,0xA002,0x9098,
+ 0xA002,0x6DE4,0x8FC2,0xA006,0xA002,0x5E7C,0xA002,0x53C8,0x8BF1,0xA002,0x91C9,0xA002,0x4F51,0x53F3,0xA016,0xA00A,
+ 0xA004,0xA002,0x53CB,0x6709,0xA002,0x9149,0xA002,0x904A,0x6CB9,0xA006,0xA002,0x7336,0xA002,0x94C0,0x90F5,0xA002,
+ 0x900C,0xA002,0x8A27,0x6182,0xA00A,0xA004,0xA002,0x60A0,0x512A,0xA002,0x5E7D,0xA002,0x7528,0x52C7,0xA006,0xA002,
+ 0x607F,0xA002,0x6C38,0x6E67,0xA002,0x6CF3,0xA002,0x8A60,0x86F9,0xA02C,0xA016,0xA00A,0xA004,0xA002,0x8E34,0x96CD,
+ 0xA002,0x5EB8,0xA002,0x7670,0x81C3,0xA006,0xA002,0x50AD,0xA002,0x64C1,0x55B2,0xA002,0x6620,0xA002,0x786C,0x9896,
+ 0xA00A,0xA004,0xA002,0x5F71,0x76C8,0xA002,0x8D62,0xA002,0x8FCE,0x8805,0xA006,0xA002,0x8367,0xA002,0x8425,0x87A2,
+ 0xA002,0x83B9,0xA002,0x7F28,0x61C9,0xA000,0xA000,0xA004,0xA002,0x9E70,0x5B30,0xA002,0x6AFB,0xA002,0x82F1,0x5370,
+ 0xA03C,0xA001,0xA001,0xA00E,0xA001,0xA001,0xA006,0xA002,0x96B1,0xA002,0x5F15,0x5C39,0xA002,0x996E,0xA002,0x5BC5,
+ 0x6DEB,0xA016,0xA00A,0xA004,0xA002,0x94F6,0x541F,0xA002,0x59FB,0xA002,0x9670,0x97F3,0xA006,0xA002,0x6BB7,0xA002,
+ 0x56E0,0x852D,0xA002,0x88C0,0xA002,0x7ECE,0x7FCC,0xA00A,0xA004,0xA002,0x7FFC,0x7570,0xA002,0x8BD1,0xA002,0x8C0A,
+ 0x8BAE,0xA006,0xA002,0x8BE3,0xA002,0x6EA2,0x76CA,0xA002,0x7FA9,0xA002,0x61B6,0x6BC5,0xA000,0xA056,0xA02A,0xA014,
+ 0xA00A,0xA004,0xA002,0x610F,0x88D4,0xA002,0x4EA6,0xA002,0x75AB,0x8084,0xA004,0xA002,0x9038,0x81C6,0xA002,0x5F79,
+ 0xA002,0x5104,0x5C79,0xA00A,0xA004,0xA002,0x9091,0x6613,0xA002,0x6291,0xA002,0x85DD,0x4EE5,0xA006,0xA002,0x77E3,
+ 0xA002,0x4E59,0x5DF2,0xA002,0x501A,0xA002,0x87FB,0x6905,0xA016,0xA00A,0xA004,0xA002,0x5F5D,0x59E8,0xA002,0x5B9C,
+ 0xA002,0x6C82,0x7591,0xA006,0xA002,0x80F0,0xA002,0x5100,0x8FFB,0xA002,0x907A,0xA002,0x9295,0x9890,0xA00A,0xA004,
+ 0xA002,0x8863,0x4F0A,0xA002,0x4F9D,0xA002,0x94F1,0x63D6,0xA006,0xA002,0x91AB,0xA002,0x58F9,0x4E00,0xA002,0x6DB2,
+ 0xA002,0x591C,0x814B,0xA02A,0xA014,0xA00A,0xA004,0xA002,0x66F3,0x8449,0xA002,0x696D,0xA002,0x6396,0x9875,0xA004,
+ 0xA002,0x4E5F,0x51B6,0xA002,0x91CE,0xA002,0x723A,0x8036,0xA00A,0xA004,0xA002,0x564E,0x6930,0xA002,0x8000,0xA002,
+ 0x8981,0x85E5,0xA006,0xA002,0x8200,0xA002,0x54AC,0x59DA,0xA002,0x8C23,0xA002,0x7AAF,0x9683,0xA000,0xA000,0xA000,
+ 0xA002,0x5C27,0x6447,0xA248,0xA10B,0xA0C1,0xA041,0xA001,0xA014,0xA001,0xA007,0xA001,0xA002,0x7476,0xA002,0x5996,
+ 0x8170,0xA006,0xA002,0x9080,0xA002,0x6F3E,0x6A23,0xA002,0x990A,0xA002,0x7662,0x536C,0xA016,0xA00A,0xA004,0xA002,
+ 0x6C27,0x967D,0xA002,0x6D0B,0xA002,0x7F8A,0x760D,0xA006,0xA002,0x4F6F,0xA002,0x98BA,0x694A,0xA002,0x79E7,0xA002,
+ 0x9E2F,0x9260,0xA00A,0xA004,0xA002,0x6B83,0x9A8C,0xA002,0x8C1A,0xA002,0x5BB4,0x71C4,0xA006,0xA002,0x5F66,0xA002,
+ 0x55AD,0x96C1,0xA002,0x786F,0xA002,0x53AD,0x71D5,0xA056,0xA02A,0xA014,0xA00A,0xA004,0xA002,0x5830,0x8C54,0xA002,
+ 0x6F14,0xA002,0x884D,0x773C,0xA004,0xA002,0x6B97,0x5944,0xA002,0x6CBF,0xA002,0x708E,0x960E,0xA00A,0xA004,0xA002,
+ 0x989C,0x8A00,0xA002,0x5EF6,0xA002,0x789E,0x8712,0xA006,0xA002,0x7814,0xA002,0x56B4,0x9E7D,0xA002,0x6E30,0xA002,
+ 0x7159,0x9609,0xA016,0xA00A,0xA004,0xA002,0x56A5,0x7109,0xA002,0x8BB6,0xA002,0x4E9E,0x555E,0xA006,0xA002,0x96C5,
+ 0xA002,0x6DAF,0x8859,0xA002,0x5D16,0xA002,0x869C,0x7259,0xA00A,0xA004,0xA002,0x82BD,0x4E2B,0xA002,0x5440,0xA002,
+ 0x9E2D,0x9E26,0xA006,0xA002,0x62BC,0xA002,0x58D3,0x8FC5,0xA002,0x905C,0xA002,0x8BAF,0x8BAD,0xA000,0xA014,0xA00A,
+ 0xA004,0xA002,0x6C5B,0x6B89,0xA002,0x5DE1,0xA002,0x9A6F,0x5C0B,0xA004,0xA002,0x8BE2,0x65EC,0xA002,0x7D03,0xA002,
+ 0x71FB,0x52F3,0xA00A,0xA004,0xA002,0x8840,0x96EA,0xA002,0x7A74,0xA002,0x5B78,0x859B,0xA006,0xA002,0x9774,0xA002,
+ 0x7EDA,0x7734,0xA002,0x766C,0xA000,0x9078,0xA001,0xA001,0xA01C,0xA006,0xA001,0xA001,0xA001,0xA001,0x7384,0xA00A,
+ 0xA004,0xA002,0x93C7,0x61F8,0xA002,0x5BA3,0xA002,0x55A7,0x8F69,0xA006,0xA002,0x7EED,0xA002,0x7EEA,0x5A7F,0xA002,
+ 0x7D6E,0xA002,0x6064,0x755C,0xA016,0xA00A,0xA004,0xA002,0x5E8F,0x65ED,0xA002,0x6558,0xA002,0x9157,0x84C4,0xA006,
+ 0xA002,0x8BB8,0xA002,0x5F90,0x9B1A,0xA002,0x5653,0xA002,0x865B,0x9700,0xA00A,0xA004,0xA002,0x620C,0x589F,0xA002,
+ 0x7EE3,0xA002,0x8896,0x79C0,0xA006,0xA002,0x9508,0xA002,0x55C5,0x673D,0xA002,0x7F9E,0xA002,0x8129,0x70CB,0xA07C,
+ 0xA000,0xA056,0xA02A,0xA014,0xA00A,0xA004,0xA002,0x718A,0x96C4,0xA002,0x6D36,0xA002,0x5308,0x80F8,0xA004,0xA002,
+ 0x51F6,0x5144,0xA002,0x59D3,0xA002,0x6027,0x674F,0xA00A,0xA004,0xA002,0x5E78,0x9192,0xA002,0x884C,0xA002,0x90A2,
+ 0x5F62,0xA006,0xA002,0x578B,0xA002,0x5211,0x8208,0xA002,0x60FA,0xA002,0x7329,0x8165,0xA016,0xA00A,0xA004,0xA002,
+ 0x661F,0x91C1,0xA002,0x4FE1,0xA002,0x5FC3,0x5FFB,0xA006,0xA002,0x65B0,0xA002,0x8F9B,0x8A22,0xA002,0x950C,0xA002,
+ 0x82AF,0x85AA,0xA00A,0xA004,0xA002,0x5C51,0x8C22,0xA002,0x7009,0xA002,0x6D29,0x61C8,0xA006,0xA002,0x87F9,0xA002,
+ 0x5378,0x68B0,0xA002,0x5BEB,0xA002,0x8C10,0x8105,0xA000,0xA014,0xA00A,0xA004,0xA002,0x659C,0x90AA,0xA002,0x651C,
+ 0xA002,0x633E,0x5354,0xA004,0xA002,0x978B,0x880D,0xA002,0x6B47,0xA002,0x4E9B,0x69E2,0xA00A,0xA004,0xA002,0x6548,
+ 0x7B11,0xA002,0x562F,0xA002,0x8096,0x6821,0xA000,0xA002,0x5B5D,0xA000,0x5C0F,0xA04C,0xA001,0xA01F,0xA00A,0xA001,
+ 0xA003,0xA001,0x66C9,0xA002,0x6DC6,0xA002,0x5BB5,0x6D88,0xA00A,0xA004,0xA002,0x9500,0x56C2,0xA002,0x54EE,0xA002,
+ 0x524A,0x9704,0xA006,0xA002,0x785D,0xA002,0x856D,0x8C61,0xA002,0x56AE,0xA001,0x6A61,0xA016,0xA00A,0xA004,0xA002,
+ 0x5DF7,0x9879,0xA002,0x4EAB,0xA002,0x97FF,0x60F3,0xA006,0xA002,0x8BE6,0xA002,0x7965,0x7FD4,0xA002,0x9109,0xA002,
+ 0x6E58,0x8944,0xA00A,0xA004,0xA002,0x7BB1,0x9999,0xA002,0x9576,0xA002,0x5EC2,0x76F8,0xA006,0xA002,0x7EBF,0xA002,
+ 0x9650,0x9677,0xA002,0x61B2,0xA002,0x7FA8,0x9985,0xA056,0xA02A,0xA014,0xA00A,0xA004,0xA002,0x817A,0x7E23,0xA002,
+ 0x737B,0xA002,0x73FE,0x96AA,0xA004,0xA002,0x986F,0x5ACC,0xA002,0x7D43,0xA002,0x6D8E,0x95F2,0xA00A,0xA004,0xA002,
+ 0x8237,0x929C,0xA002,0x8D24,0xA002,0x9E79,0x7EA4,0xA006,0xA002,0x9C9C,0xA002,0x50CA,0x5148,0xA002,0x9528,0xA002,
+ 0x6380,0x5687,0xA016,0xA00A,0xA004,0xA002,0x590F,0x5EC8,0xA002,0x4E0B,0xA002,0x72F9,0x4FE0,0xA006,0xA002,0x5CFD,
+ 0xA002,0x6687,0x8F96,0xA002,0x971E,0xA002,0x5323,0x8766,0xA00A,0xA004,0xA002,0x778E,0x7EC6,0xA002,0x6232,0xA002,
+ 0x9699,0x7E6B,0xA006,0xA002,0x6D17,0xA002,0x94E3,0x559C,0xA002,0x5AB3,0xA002,0x7FD2,0x84C6,0xA000,0xA016,0xA00A,
+ 0xA004,0xA002,0x8972,0x6A84,0xA002,0x7280,0xA002,0x6C50,0x8C3F,0xA006,0xA002,0x70EF,0xA002,0x7184,0x60DC,0xA002,
+ 0x5915,0xA002,0x819D,0x6089,0xA000,0xA004,0xA002,0x5E0C,0x910E,0xA000,0x7A00,0xA0C5,0xA054,0xA001,0xA001,0xA026,
+ 0xA010,0xA006,0xA001,0xA001,0xA002,0x72A7,0x9521,0xA004,0xA002,0x5438,0x8B46,0xA002,0x7699,0xA002,0x77FD,0x7852,
+ 0xA00A,0xA004,0xA002,0x897F,0x6790,0xA002,0x7199,0xA002,0x6614,0x8BEF,0xA006,0xA002,0x609F,0xA002,0x52D9,0x52FF,
+ 0xA002,0x7269,0xA002,0x6664,0x9727,0xA016,0xA00A,0xA004,0xA002,0x620A,0x5862,0xA002,0x4FAE,0xA002,0x4F0D,0x821E,
+ 0xA006,0xA002,0x5348,0xA002,0x6342,0x4E94,0xA002,0x6B66,0xA002,0x6BCC,0x5434,0xA00A,0xA004,0xA002,0x543E,0x68A7,
+ 0xA002,0x856A,0xA002,0x7121,0x5C4B,0xA006,0xA002,0x8BEC,0xA002,0x6D3F,0x70CF,0xA002,0x94A8,0xA002,0x6B4D,0x5DEB,
+ 0xA000,0xA056,0xA02A,0xA014,0xA00A,0xA004,0xA002,0x6C83,0x6439,0xA002,0x81E5,0xA002,0x65A1,0x6211,0xA004,0xA002,
+ 0x7AA9,0x6E26,0xA002,0x8778,0xA002,0x64BE,0x7F4B,0xA00A,0xA004,0xA002,0x7FC1,0x55E1,0xA002,0x95EE,0xA002,0x7D0A,
+ 0x7A69,0xA006,0xA002,0x543B,0xA002,0x7EB9,0x95FB,0xA002,0x6587,0xA002,0x868A,0x6EAB,0xA016,0xA00A,0xA004,0xA002,
+ 0x761F,0x885B,0xA002,0x6170,0xA002,0x5C09,0x8C13,0xA006,0xA002,0x6E2D,0xA002,0x4F4D,0x9B4F,0xA002,0x9935,0xA002,
+ 0x80C3,0x754F,0xA00A,0xA004,0xA002,0x5473,0x851A,0xA002,0x672A,0xA002,0x7EAC,0x5C3E,0xA006,0xA002,0x507D,0xA002,
+ 0x5049,0x59D4,0xA002,0x840E,0xA002,0x8466,0x7EF4,0xA000,0xA014,0xA00A,0xA004,0xA002,0x6FF0,0x70BA,0xA002,0x60DF,
+ 0xA002,0x552F,0x570D,0xA004,0xA002,0x6845,0x9055,0xA002,0x97E6,0xA002,0x5371,0x5FAE,0xA000,0xA000,0xA000,0x5DCD,
+ 0xA0C0,0xA057,0xA001,0xA02A,0xA014,0xA00A,0xA004,0xA002,0x5A01,0x5984,0xA002,0x5FD8,0xA002,0x6722,0x6680,0xA004,
+ 0xA002,0x5F80,0x7F51,0xA002,0x6789,0xA002,0x4EA1,0x738B,0xA00A,0xA004,0xA002,0x6C6A,0x8155,0xA002,0x842C,0xA002,
+ 0x5A49,0x5B9B,0xA006,0xA002,0x60CB,0xA002,0x7696,0x665A,0xA002,0x8F13,0xA002,0x7897,0x5B8C,0xA016,0xA00A,0xA004,
+ 0xA002,0x70F7,0x4E38,0xA002,0x987D,0xA002,0x7FEB,0x7063,0xA006,0xA002,0x5F4E,0xA002,0x8C4C,0x5916,0xA002,0x6B6A,
+ 0xA002,0x977A,0x74E6,0xA00A,0xA004,0xA002,0x5A03,0x7AAA,0xA002,0x86D9,0xA002,0x54C7,0x7A75,0xA006,0xA002,0x553E,
+ 0xA002,0x6428,0x59A5,0xA002,0x6A62,0xA002,0x9A7C,0x9A6E,0xA056,0xA02A,0xA014,0xA00A,0xA004,0xA002,0x9640,0x9E35,
+ 0xA002,0x8131,0xA002,0x8A17,0x62D6,0xA004,0xA002,0x81C0,0x5C6F,0xA002,0x541E,0xA002,0x9000,0x892A,0xA00A,0xA004,
+ 0xA002,0x8715,0x817F,0xA002,0x9893,0xA002,0x63A8,0x7CF0,0xA006,0xA002,0x6E4D,0xA002,0x5154,0x5410,0xA002,0x571F,
+ 0xA002,0x5C60,0x6D82,0xA016,0xA00A,0xA004,0xA002,0x9014,0x5F92,0xA002,0x5716,0xA002,0x7A81,0x79C3,0xA006,0xA002,
+ 0x51F8,0xA002,0x900F,0x982D,0xA002,0x6295,0xA002,0x5AAE,0x75DB,0xA00A,0xA004,0xA002,0x7EDF,0x7B69,0xA002,0x6345,
+ 0xA002,0x6876,0x7AE5,0xA006,0xA002,0x5F64,0xA002,0x94DC,0x8855,0xA002,0x77B3,0xA002,0x916E,0x6850,0xA000,0xA000,
+ 0xA00A,0xA004,0xA002,0x901A,0x8247,0xA002,0x633A,0xA002,0x5EAD,0x4EAD,0xA000,0xA002,0x505C,0xA002,0x5EF7,0x6C40,
+ 0xA001,0xA00A,0xA001,0xA001,0xA001,0xA001,0xA002,0x70F4,0xA002,0x807D,0x5EF3,0xA02C,0xA016,0xA00A,0xA004,0xA002,
+ 0x6017,0x94C1,0xA002,0x8D34,0xA002,0x8DF3,0x773A,0xA006,0xA002,0x8FE2,0xA002,0x689D,0x6311,0xA002,0x8146,0xA002,
+ 0x8214,0x606C,0xA00A,0xA004,0xA002,0x751C,0x7530,0xA002,0x586B,0xA002,0x6DFB,0x5929,0xA006,0xA002,0x5C5C,0xA002,
+ 0x5243,0x6D95,0xA002,0x6113,0xA002,0x568F,0x66FF,0xA016,0xA00A,0xA004,0xA002,0x9AD4,0x557C,0xA002,0x8E44,0xA002,
+ 0x9898,0x63D0,0xA006,0xA002,0x9511,0xA002,0x8E22,0x5254,0xA002,0x68AF,0xA002,0x8B04,0x75BC,0xA00A,0xA004,0xA002,
+ 0x9A30,0x85E4,0xA002,0x7286,0xA002,0x5957,0x8BA8,0xA006,0xA002,0x9676,0xA002,0x6DD8,0x9003,0xA002,0x6843,0xA002,
+ 0x8404,0x7EE6,0xB068,0xA804,0xA434,0xA1E9,0xA126,0xA063,0xA000,0xA056,0xA02A,0xA014,0xA00A,0xA004,0xA002,0x6ED4,
+ 0x6FE4,0xA002,0x642F,0xA002,0x71D9,0x8D9F,0xA004,0xA002,0x6DCC,0x8EBA,0xA002,0x5018,0xA002,0x7CD6,0x5510,0xA00A,
+ 0xA004,0xA002,0x819B,0x68E0,0xA002,0x5802,0xA002,0x642A,0x5858,0xA006,0xA002,0x6E6F,0xA002,0x70AD,0x6B4E,0xA002,
+ 0x63A2,0xA002,0x78B3,0x8892,0xA016,0xA00A,0xA004,0xA002,0x6BEF,0x5766,0xA002,0x8C08,0xA002,0x8C2D,0x6F6D,0xA006,
+ 0xA002,0x75F0,0xA002,0x6A80,0x7F48,0xA002,0x7058,0xA002,0x7671,0x8D2A,0xA00A,0xA004,0xA002,0x6524,0x574D,0xA002,
+ 0x6C70,0xA002,0x614B,0x592A,0xA006,0xA002,0x915E,0xA002,0x6CF0,0x98B1,0xA002,0x62AC,0xA002,0x82D4,0x80CE,0xA000,
+ 0xA000,0xA000,0xA004,0xA002,0x8E0F,0x8E4B,0xA002,0x64BB,0xA000,0x737A,0xA065,0xA00F,0xA001,0xA001,0xA001,0xA006,
+ 0xA002,0x5854,0xA002,0x5979,0x7260,0xA002,0x4ED6,0xA002,0x584C,0x6240,0xA02A,0xA014,0xA00A,0xA004,0xA002,0x9501,
+ 0x7D22,0xA002,0x7463,0xA002,0x7F29,0x5506,0xA004,0xA002,0x68AD,0x84D1,0xA002,0x7B4D,0xA002,0x640D,0x5B6B,0xA00A,
+ 0xA004,0xA002,0x795F,0x96A7,0xA002,0x9042,0xA002,0x7A57,0x6B72,0xA006,0xA002,0x788E,0xA002,0x9AD3,0x7EE5,0xA002,
+ 0x96A8,0xA002,0x968B,0x96D6,0xA016,0xA00A,0xA004,0xA002,0x7B97,0x849C,0xA002,0x9178,0xA002,0x8085,0x8BC9,0xA006,
+ 0xA002,0x5BBF,0xA002,0x6EAF,0x5851,0xA002,0x50F3,0xA002,0x7C9F,0x901F,0xA00A,0xA004,0xA002,0x7D20,0x4FD7,0xA002,
+ 0x9165,0xA002,0x8607,0x55FD,0xA006,0xA002,0x64FB,0xA002,0x825A,0x8490,0xA002,0x8BF5,0xA002,0x8BBC,0x5B8B,0xA056,
+ 0xA02A,0xA014,0xA00A,0xA004,0xA002,0x9001,0x9882,0xA002,0x616B,0xA002,0x8073,0x9B06,0xA004,0xA002,0x5DF3,0x9972,
+ 0xA002,0x4F3C,0xA002,0x8997,0x56DB,0xA00A,0xA004,0xA002,0x55E3,0x5BFA,0xA002,0x8086,0xA002,0x6B7B,0x7D72,0xA006,
+ 0xA002,0x53F8,0xA002,0x79C1,0x6952,0xA002,0x5636,0xA002,0x6495,0x65AF,0xA016,0xA00A,0xA004,0xA002,0x720D,0x6714,
+ 0xA002,0x78A9,0xA002,0x8BF4,0x821C,0xA006,0xA002,0x987A,0xA002,0x77AC,0x542E,0xA002,0x7A0E,0xA002,0x7761,0x6C34,
+ 0xA00A,0xA004,0xA002,0x8C01,0x723D,0xA002,0x96D9,0xA002,0x971C,0x62F4,0xA006,0xA002,0x6813,0xA002,0x5E25,0x7529,
+ 0xA002,0x8870,0xA002,0x6454,0x800D,0xA000,0xA000,0xA000,0xA000,0xA002,0x5237,0x6055,0xA06C,0xA001,0xA015,0xA001,
+ 0xA001,0xA007,0xA001,0xA002,0x6F31,0xA002,0x6578,0x5EB6,0xA006,0xA002,0x5885,0xA002,0x8C4E,0x620D,0xA002,0x675F,
+ 0xA002,0x6A39,0x8FF0,0xA02A,0xA014,0xA00A,0xA004,0xA002,0x8853,0x5C6C,0xA002,0x9F20,0xA002,0x9ECD,0x8700,0xA004,
+ 0xA002,0x7F72,0x66D9,0xA002,0x6691,0xA002,0x85F7,0x719F,0xA00A,0xA004,0xA002,0x5B70,0x8D4E,0xA002,0x66F8,0xA002,
+ 0x758F,0x6DD1,0xA006,0xA002,0x8212,0xA002,0x53D4,0x8F93,0xA002,0x6292,0xA002,0x6B8A,0x68B3,0xA016,0xA00A,0xA004,
+ 0xA002,0x6A1E,0x852C,0xA002,0x7378,0xA002,0x7626,0x53D7,0xA006,0xA002,0x552E,0xA002,0x6388,0x5BFF,0xA002,0x5B88,
+ 0xA002,0x9996,0x624B,0xA00A,0xA004,0xA002,0x6536,0x8BD5,0xA002,0x89C6,0xA002,0x5BA4,0x6043,0xA006,0xA002,0x5E02,
+ 0xA002,0x6C0F,0x9970,0xA002,0x91CB,0xA002,0x4F8D,0x4ED5,0xA000,0xA000,0xA02A,0xA014,0xA00A,0xA004,0xA002,0x9069,
+ 0x566C,0xA002,0x55DC,0xA002,0x662F,0x52E2,0xA004,0xA002,0x901D,0x8A93,0xA002,0x62ED,0xA002,0x525A,0x67FF,0xA00A,
+ 0xA004,0xA002,0x4E16,0x58EB,0xA002,0x793A,0xA002,0x5F0F,0x59CB,0xA006,0xA002,0x9A76,0xA002,0x5C4E,0x4F7F,0xA002,
+ 0x77E2,0xA002,0x53F2,0x8BC6,0xA016,0xA00A,0xA004,0xA002,0x5BE6,0x8755,0xA002,0x98DF,0xA002,0x4EC0,0x6642,0xA006,
+ 0xA002,0x62FE,0xA002,0x77F3,0x5341,0xA002,0x8768,0xA002,0x5C4D,0x8BD7,0xA00A,0xA004,0xA002,0x6FD5,0x65BD,0xA002,
+ 0x7345,0xA002,0x5931,0x5E2B,0xA006,0xA002,0x8056,0xA002,0x80DC,0x8CF8,0xA002,0x76DB,0xA000,0x7701,0xA13C,0xA0C4,
+ 0xA073,0xA01D,0xA001,0xA006,0xA001,0xA001,0xA001,0xA001,0x7EF3,0xA00A,0xA004,0xA002,0x965E,0x7272,0xA002,0x7525,
+ 0xA002,0x751F,0x8072,0xA006,0xA002,0x6EF2,0xA002,0x614E,0x814E,0xA002,0x751A,0xA002,0x5B38,0x5BE9,0xA02A,0xA014,
+ 0xA00A,0xA004,0xA002,0x700B,0x795E,0xA002,0x7EC5,0xA002,0x5A20,0x6DF1,0xA004,0xA002,0x8EAB,0x4F38,0xA002,0x547B,
+ 0xA002,0x7533,0x7837,0xA00A,0xA004,0xA002,0x8BBE,0x793E,0xA002,0x6D89,0xA002,0x61FE,0x5C04,0xA006,0xA002,0x651D,
+ 0xA002,0x8D66,0x820D,0xA002,0x820C,0xA002,0x86C7,0x8D4A,0xA016,0xA00A,0xA004,0xA002,0x5962,0x7ECD,0xA002,0x90B5,
+ 0xA002,0x54E8,0x5C11,0xA006,0xA002,0x97F6,0xA002,0x52FA,0x828D,0xA002,0x71D2,0xA002,0x7A0D,0x634E,0xA00A,0xA004,
+ 0xA002,0x68A2,0x88F3,0xA002,0x5C1A,0xA002,0x4E0A,0x664C,0xA006,0xA002,0x8D4F,0xA002,0x5546,0x50B7,0xA002,0x5892,
+ 0xA002,0x7F2E,0x6247,0xA000,0xA02A,0xA014,0xA00A,0xA004,0xA002,0x6C55,0x5584,0xA002,0x81B3,0xA002,0x8D61,0x64C5,
+ 0xA004,0xA002,0x965D,0x95EA,0xA002,0x886B,0xA002,0x717D,0x522A,0xA00A,0xA004,0xA002,0x5C71,0x7154,0xA002,0x82EB,
+ 0xA002,0x73CA,0x66EC,0xA006,0xA002,0x7BE9,0xA002,0x715E,0x5565,0xA002,0x50BB,0xA002,0x7EB1,0x6C99,0xA016,0xA00A,
+ 0xA004,0xA002,0x524E,0x6BBA,0xA002,0x7802,0xA002,0x838E,0x50E7,0xA006,0xA002,0x68EE,0xA002,0x6F80,0x8272,0xA002,
+ 0x745F,0xA002,0x5AC2,0x6383,0xA00A,0xA004,0xA002,0x9A9A,0x6414,0xA002,0x55AA,0xA002,0x55D3,0x6851,0xA000,0xA002,
+ 0x6563,0xA000,0x5098,0xA001,0xA01F,0xA001,0xA008,0xA001,0xA001,0xA002,0x53C1,0xA002,0x4E09,0x8D5B,0xA00A,0xA004,
+ 0xA002,0x585E,0x9CC3,0xA002,0x816E,0xA002,0x85A9,0x7051,0xA006,0xA002,0x6492,0xA002,0x5F31,0x82E5,0xA002,0x6F64,
+ 0xA002,0x95F0,0x9510,0xA02C,0xA016,0xA00A,0xA004,0xA002,0x745E,0x854A,0xA002,0x962E,0xA002,0x8F6F,0x8925,0xA006,
+ 0xA002,0x5165,0xA002,0x6C5D,0x4E73,0xA002,0x8FB1,0xA002,0x5982,0x5B7A,0xA00A,0xA004,0xA002,0x5112,0x8815,0xA002,
+ 0x8339,0xA002,0x8089,0x67D4,0xA006,0xA002,0x7163,0xA002,0x5197,0x7FA2,0xA002,0x5BB9,0xA002,0x6EB6,0x9394,0xA016,
+ 0xA00A,0xA004,0xA002,0x878D,0x8363,0xA002,0x84C9,0xA002,0x8338,0x620E,0xA006,0xA002,0x65E5,0xA002,0x793D,0x6254,
+ 0xA002,0x7EAB,0xA002,0x598A,0x5203,0xA00A,0xA004,0xA002,0x8BA4,0x4EFB,0xA002,0x97E7,0xA002,0x5FCD,0x4EBA,0xA006,
+ 0xA002,0x4EC1,0xA002,0x58EC,0x71B1,0xA002,0x60F9,0xA002,0x7ED5,0x64FE,0xA04C,0xA000,0xA000,0xA02A,0xA014,0xA00A,
+ 0xA004,0xA002,0x9976,0x8BA9,0xA002,0x56B7,0xA002,0x6518,0x58E4,0xA004,0xA002,0x74E4,0x67D3,0xA002,0x5189,0xA002,
+ 0x71C3,0x7136,0xA00A,0xA004,0xA002,0x7FA4,0x88D9,0xA002,0x96C0,0xA002,0x78BA,0x69B7,0xA006,0xA002,0x9E4A,0xA002,
+ 0x537B,0x7638,0xA002,0x7094,0xA002,0x7F3A,0x52F8,0xA016,0xA00A,0xA004,0xA002,0x5238,0x72AC,0xA002,0x62F3,0xA002,
+ 0x75CA,0x5168,0xA006,0xA002,0x6CC9,0xA002,0x919B,0x6B0A,0xA002,0x98A7,0xA002,0x5708,0x53BB,0xA000,0xA004,0xA002,
+ 0x8DA3,0x9F8B,0xA002,0x5A36,0xA000,0x53D6,0xA07E,0xA028,0xA001,0xA011,0xA005,0xA001,0xA001,0xA001,0x6E20,0xA006,
+ 0xA002,0x9A71,0xA002,0x5C48,0x8EC0,0xA002,0x66F2,0xA002,0x86C6,0x5340,0xA00A,0xA004,0xA002,0x8DA8,0x6CC5,0xA002,
+ 0x914B,0xA002,0x56DA,0x6C42,0xA006,0xA002,0x7403,0xA002,0x90B1,0x5775,0xA002,0x97A6,0xA002,0x7AAE,0x74CA,0xA02A,
+ 0xA014,0xA00A,0xA004,0xA002,0x6176,0x8BF7,0xA002,0x9877,0xA002,0x60C5,0x6C30,0xA004,0xA002,0x6674,0x64CE,0xA002,
+ 0x6E05,0xA002,0x537F,0x50BE,0xA00A,0xA004,0xA002,0x6C2B,0x8F7B,0xA002,0x9752,0xA002,0x6C81,0x5BE2,0xA006,0xA002,
+ 0x79BD,0xA002,0x64D2,0x82B9,0xA002,0x61C3,0xA002,0x7434,0x79E6,0xA016,0xA00A,0xA004,0xA002,0x89AA,0x4FB5,0xA002,
+ 0x94A6,0xA002,0x7ACA,0x602F,0xA006,0xA002,0x4E14,0xA002,0x8304,0x5207,0xA002,0x7AC5,0xA002,0x4FCF,0x5CED,0xA00A,
+ 0xA004,0xA002,0x7FF9,0x64AC,0xA002,0x9798,0xA002,0x5DE7,0x50D1,0xA006,0xA002,0x55AC,0xA002,0x77A7,0x6A4B,0xA002,
+ 0x6084,0xA002,0x6572,0x9539,0xA000,0xA02A,0xA014,0xA00A,0xA004,0xA002,0x6A47,0x6436,0xA002,0x5F4A,0xA002,0x8594,
+ 0x7246,0xA004,0xA002,0x7F8C,0x8154,0xA002,0x55C6,0xA002,0x9397,0x6B49,0xA00A,0xA004,0xA002,0x6B20,0x5D4C,0xA002,
+ 0x5879,0xA002,0x8C34,0x6DFA,0xA006,0xA002,0x9063,0xA002,0x6F5C,0x524D,0xA002,0x94B3,0xA002,0x94B1,0x9ED4,0xA015,
+ 0xA009,0xA003,0xA001,0x8C26,0xA002,0x4EDF,0xA002,0x7C64,0x9077,0xA006,0xA002,0x97C6,0xA002,0x94C5,0x948E,0xA002,
+ 0x6266,0xA002,0x727D,0x6D3D,0xA000,0xA000,0xA000,0x6070,0xA219,0xA0C3,0xA083,0xA001,0xA02C,0xA001,0xA015,0xA009,
+ 0xA003,0xA001,0x6390,0xA002,0x8BAB,0xA002,0x6CE3,0x6ECA,0xA006,0xA002,0x68C4,0xA002,0x8FC4,0x6C23,0xA002,0x5668,
+ 0xA002,0x780C,0x6814,0xA00A,0xA004,0xA002,0x555F,0x4F01,0xA002,0x4E5E,0xA002,0x8C48,0x8D77,0xA006,0xA002,0x9A91,
+ 0xA002,0x7941,0x7948,0xA002,0x65D7,0xA002,0x9F50,0x81CD,0xA02A,0xA014,0xA00A,0xA004,0xA002,0x5D0E,0x7566,0xA002,
+ 0x8DC2,0xA002,0x5947,0x68CB,0xA004,0xA002,0x5176,0x6C8F,0xA002,0x67D2,0xA002,0x6F06,0x6DD2,0xA00A,0xA004,0xA002,
+ 0x4E03,0x59BB,0xA002,0x93DA,0xA002,0x68F2,0x6B3A,0xA006,0xA002,0x7A18,0xA002,0x7011,0x66DD,0xA002,0x8C31,0xA002,
+ 0x6D66,0x666E,0xA016,0xA00A,0xA004,0xA002,0x5703,0x6A38,0xA002,0x57D4,0xA002,0x84B2,0x83E9,0xA006,0xA002,0x8461,
+ 0xA002,0x8386,0x50D5,0xA002,0x94FA,0xA002,0x64B2,0x5256,0xA00A,0xA004,0xA002,0x7C95,0x8FEB,0xA002,0x9B44,0xA002,
+ 0x7834,0x5A46,0xA006,0xA002,0x9887,0xA002,0x6F51,0x5761,0xA002,0x84F1,0xA002,0x8BC4,0x7F3E,0xA000,0xA000,0xA02A,
+ 0xA014,0xA00A,0xA004,0xA002,0x6191,0x5E73,0xA002,0x840D,0xA002,0x860B,0x576A,0xA004,0xA002,0x4E52,0x8058,0xA002,
+ 0x54C1,0xA002,0x8D2B,0x9891,0xA00A,0xA004,0xA002,0x62FC,0x8995,0xA002,0x6487,0xA002,0x7968,0x74E2,0xA006,0xA002,
+ 0x6F02,0xA002,0x98D8,0x9A97,0xA002,0x7247,0xA002,0x504F,0x7BC7,0xA000,0xA00A,0xA004,0xA002,0x8B6C,0x5C41,0xA002,
+ 0x50FB,0xA002,0x75DE,0x5339,0xA006,0xA002,0x76AE,0xA002,0x75B2,0x813E,0xA000,0x5564,0xA0C4,0xA08A,0xA034,0xA008,
+ 0xA001,0xA001,0xA001,0xA001,0xA002,0x6BD8,0x7435,0xA016,0xA00A,0xA004,0xA002,0x5288,0x62AB,0xA002,0x6279,0xA002,
+ 0x9739,0x7812,0xA006,0xA002,0x576F,0xA002,0x78B0,0x6367,0xA002,0x9E4F,0xA002,0x670B,0x81A8,0xA00A,0xA004,0xA002,
+ 0x7BF7,0x787C,0xA002,0x68DA,0xA002,0x84EC,0x5F6D,0xA006,0xA002,0x6F8E,0xA002,0x70F9,0x62A8,0xA002,0x7830,0xA002,
+ 0x76C6,0x5674,0xA02A,0xA014,0xA00A,0xA004,0xA002,0x6C9B,0x73EE,0xA002,0x914D,0xA002,0x966A,0x8D54,0xA004,0xA002,
+ 0x88F4,0x57F9,0xA002,0x80DA,0xA002,0x5478,0x6CE1,0xA00A,0xA004,0xA002,0x8DD1,0x888D,0xA002,0x7832,0xA002,0x9464,
+ 0x5486,0xA006,0xA002,0x62CB,0xA002,0x80D6,0x802A,0xA002,0x65C1,0xA002,0x9F90,0x4E53,0xA016,0xA00A,0xA004,0xA002,
+ 0x53DB,0x5224,0xA002,0x7554,0xA002,0x76FC,0x78D0,0xA006,0xA002,0x76E4,0xA002,0x6F58,0x6500,0xA002,0x6D3E,0xA002,
+ 0x6E43,0x5F98,0xA00A,0xA004,0xA002,0x724C,0x6392,0xA002,0x62CD,0xA002,0x7436,0x6015,0xA006,0xA002,0x5E15,0xA002,
+ 0x722C,0x8DB4,0xA002,0x556A,0xA002,0x6F1A,0x5076,0xA000,0xA02A,0xA014,0xA00A,0xA004,0xA002,0x5614,0x85D5,0xA002,
+ 0x6BC6,0xA002,0x9E25,0x6B50,0xA004,0xA002,0x54E6,0x8BFA,0xA002,0x7CEF,0xA002,0x61E6,0x637C,0xA00A,0xA004,0xA002,
+ 0x7627,0x8650,0xA002,0x7156,0xA002,0x5973,0x6012,0xA006,0xA002,0x52AA,0xA002,0x5974,0x8856,0xA002,0x8FB2,0xA002,
+ 0x6FC3,0x81BF,0xA000,0xA00A,0xA004,0xA002,0x7EBD,0x94AE,0xA002,0x626D,0xA002,0x725B,0x6FD8,0xA000,0xA000,0x64F0,
+ 0xA001,0xA039,0xA00D,0xA001,0xA001,0xA005,0xA001,0xA002,0x5BE7,0x51DD,0xA002,0x7370,0xA002,0x6AB8,0x60A8,0xA016,
+ 0xA00A,0xA004,0xA002,0x6D85,0x954D,0xA002,0x954A,0xA002,0x9F67,0x5B7D,0xA006,0xA002,0x8076,0xA002,0x634F,0x5C3F,
+ 0xA002,0x9E1F,0xA002,0x91C0,0x5B43,0xA00A,0xA004,0xA002,0x5FF5,0x649A,0xA002,0x6506,0xA002,0x78BE,0x5E74,0xA006,
+ 0xA002,0x62C8,0xA002,0x852B,0x6EBA,0xA002,0x9006,0xA002,0x81A9,0x533F,0xA02C,0xA016,0xA00A,0xA004,0xA002,0x59B3,
+ 0x64EC,0xA002,0x5C3C,0xA002,0x6CE5,0x90F3,0xA006,0xA002,0x9713,0xA002,0x5A57,0x80FD,0xA002,0x5AE9,0xA002,0x5185,
+ 0x9981,0xA00A,0xA004,0xA002,0x5462,0x6DD6,0xA002,0x9B27,0xA002,0x60F1,0x8166,0xA006,0xA002,0x6493,0xA002,0x56CA,
+ 0x96E3,0xA002,0x7537,0xA002,0x5357,0x5948,0xA016,0xA00A,0xA004,0xA002,0x8010,0x5976,0xA002,0x8FFA,0xA002,0x6C16,
+ 0x7EB3,0xA006,0xA002,0x5A1C,0xA002,0x90A3,0x94A0,0xA002,0x5450,0xA002,0x54EA,0x6310,0xA00A,0xA004,0xA002,0x7A46,
+ 0x7267,0xA002,0x7766,0xA002,0x76EE,0x6728,0xA006,0xA002,0x6155,0xA002,0x52DF,0x5E59,0xA002,0x66AE,0xA002,0x5893,
+ 0x6BCD,0xA0F4,0xA033,0xA000,0xA000,0xA02A,0xA014,0xA00A,0xA004,0xA002,0x59C6,0x755D,0xA002,0x7261,0xA002,0x62C7,
+ 0x67D0,0xA004,0xA002,0x725F,0x8C0B,0xA002,0x964C,0xA002,0x5BDE,0x6F20,0xA00A,0xA004,0xA002,0x6CAB,0x9ED8,0xA002,
+ 0x58A8,0xA002,0x83AB,0x672B,0xA006,0xA002,0x62B9,0xA002,0x9B54,0x6469,0xA002,0x78E8,0xA002,0x819C,0x6A21,0xA000,
+ 0xA000,0xA000,0xA002,0x8611,0x6479,0xA095,0xA03F,0xA013,0xA001,0xA006,0xA001,0xA001,0xA002,0x6478,0x8C2C,0xA006,
+ 0xA002,0x547D,0xA002,0x540D,0x94ED,0xA002,0x9E23,0xA002,0x879F,0x660E,0xA016,0xA00A,0xA004,0xA002,0x95FD,0x61AB,
+ 0xA002,0x654F,0xA002,0x76BF,0x62BF,0xA006,0xA002,0x6C11,0xA002,0x706D,0x884A,0xA002,0x7385,0xA002,0x5EDF,0x6E3A,
+ 0xA00A,0xA004,0xA002,0x79D2,0x85D0,0xA002,0x7784,0xA002,0x63CF,0x82D7,0xA006,0xA002,0x9EB5,0xA002,0x7F05,0x5A29,
+ 0xA002,0x52C9,0xA002,0x514D,0x7D7B,0xA02A,0xA014,0xA00A,0xA004,0xA002,0x7EF5,0x7720,0xA002,0x68C9,0xA002,0x5E42,
+ 0x5BC6,0xA004,0xA002,0x871C,0x6CCC,0xA002,0x89C5,0xA002,0x959F,0x7C73,0xA00A,0xA004,0xA002,0x7030,0x8C1C,0xA002,
+ 0x8FF7,0xA002,0x7CDC,0x9761,0xA006,0xA002,0x919A,0xA002,0x7787,0x5B5F,0xA002,0x68A6,0xA002,0x731B,0x9530,0xA016,
+ 0xA00A,0xA004,0xA002,0x76DF,0x6AAC,0xA002,0x8499,0xA002,0x840C,0x5011,0xA006,0xA002,0x95F7,0xA002,0x95E8,0x5A9A,
+ 0xA002,0x59BA,0xA002,0x5BD0,0x6627,0xA00A,0xA004,0xA002,0x7F8E,0x6BCF,0xA002,0x9541,0xA002,0x5A92,0x7709,0xA006,
+ 0xA002,0x6CA1,0xA002,0x7164,0x9EF4,0xA002,0x9176,0xA002,0x6885,0x679A,0xA000,0xA000,0xA014,0xA00A,0xA004,0xA002,
+ 0x73AB,0x9EBC,0xA002,0x8D38,0xA002,0x8C8C,0x5E3D,0xA004,0xA002,0x5192,0x8302,0xA002,0x536F,0xA002,0x94C6,0x77DB,
+ 0xA00A,0xA004,0xA002,0x82BC,0x951A,0xA002,0x8305,0xA002,0x8C93,0x83BD,0xA006,0xA002,0x5FD9,0xA002,0x6C13,0x76F3,
+ 0xA002,0x832B,0xA002,0x8292,0x8C29,0xA09C,0xA001,0xA043,0xA017,0xA001,0xA00A,0xA004,0xA002,0x6F2B,0x6162,0xA002,
+ 0x66FC,0xA002,0x8513,0x6EFF,0xA006,0xA002,0x883B,0xA002,0x9992,0x779E,0xA002,0x8109,0xA002,0x9081,0x8CE3,0xA016,
+ 0xA00A,0xA004,0xA002,0x9EA6,0x8CB7,0xA002,0x57CB,0xA002,0x55CE,0x561B,0xA006,0xA002,0x9A82,0xA002,0x9A6C,0x879E,
+ 0xA002,0x78BC,0xA002,0x746A,0x9EBB,0xA00A,0xA004,0xA002,0x5ABD,0x7EDC,0xA002,0x9A86,0xA002,0x6D1B,0x843D,0xA006,
+ 0xA002,0x88F8,0xA002,0x9AA1,0x7C6E,0xA002,0x9523,0xA002,0x908F,0x7F85,0xA02C,0xA016,0xA00A,0xA004,0xA002,0x87BA,
+ 0x863F,0xA002,0x8BBA,0xA002,0x7EB6,0x6DEA,0xA006,0xA002,0x5D19,0xA002,0x502B,0x8F6E,0xA002,0x6384,0xA002,0x7565,
+ 0x63A0,0xA00A,0xA004,0xA002,0x4E82,0x5375,0xA002,0x7064,0xA002,0x5B7F,0x6523,0xA006,0xA002,0x5DD2,0xA002,0x83C9,
+ 0x6FFE,0xA002,0x7387,0xA002,0x5F8B,0x6C2F,0xA016,0xA00A,0xA004,0xA002,0x8651,0x7F15,0xA002,0x5C62,0xA002,0x5C65,
+ 0x65C5,0xA006,0xA002,0x4FB6,0xA002,0x94DD,0x5442,0xA002,0x9A74,0xA002,0x622E,0x9678,0xA00A,0xA004,0xA002,0x9304,
+ 0x7984,0xA002,0x6F5E,0xA002,0x9E7F,0x8D42,0xA006,0xA002,0x8DEF,0xA002,0x9732,0x788C,0xA002,0x9E93,0xA002,0x9C81,
+ 0x865C,0xA000,0xA000,0xA000,0xA014,0xA00A,0xA004,0xA002,0x9E75,0x64C4,0xA002,0x946A,0xA002,0x5EEC,0x9885,0xA004,
+ 0xA002,0x76E7,0x8606,0xA002,0x964B,0xA002,0x6F0F,0x7C0D,0xA00A,0xA004,0xA002,0x645F,0x5A41,0xA002,0x6A13,0xA002,
+ 0x96B4,0x650F,0xA000,0xA002,0x58DF,0xA000,0x9686,0xA493,0xA248,0xA16C,0xA0C2,0xA0A2,0xA04C,0xA020,0xA00A,0xA001,
+ 0xA003,0xA001,0x7ABF,0xA002,0x7C60,0xA002,0x56A8,0x807E,0xA00A,0xA004,0xA002,0x9F99,0x516D,0xA002,0x67F3,0xA002,
+ 0x6D41,0x7624,0xA006,0xA002,0x5289,0xA002,0x7559,0x998F,0xA002,0x786B,0xA002,0x69B4,0x7409,0xA016,0xA00A,0xA004,
+ 0xA002,0x9724,0x4EE4,0xA002,0x53E6,0xA002,0x9886,0x5DBA,0xA006,0xA002,0x9675,0xA002,0x9748,0x6DE9,0xA002,0x7F9A,
+ 0xA002,0x4F36,0x94C3,0xA00A,0xA004,0xA002,0x9F84,0x96F6,0xA002,0x83F1,0xA002,0x73B2,0x62CE,0xA006,0xA002,0x541D,
+ 0xA002,0x8D41,0x51DC,0xA002,0x6DCB,0xA002,0x9CDE,0x9130,0xA02A,0xA014,0xA00A,0xA004,0xA002,0x81E8,0x9716,0xA002,
+ 0x78F7,0xA002,0x6797,0x7433,0xA004,0xA002,0x7375,0x52A3,0xA002,0x70C8,0xA002,0x88C2,0x5217,0xA00A,0xA004,0xA002,
+ 0x6599,0x5ED6,0xA002,0x9563,0xA002,0x6482,0x77AD,0xA006,0xA002,0x6F66,0xA002,0x907C,0x5BE5,0xA002,0x71CE,0xA002,
+ 0x7642,0x50DA,0xA016,0xA00A,0xA004,0xA002,0x804A,0x64A9,0xA002,0x8C05,0xA002,0x4EAE,0x667E,0xA006,0xA002,0x91CF,
+ 0xA002,0x8F86,0x5169,0xA002,0x826F,0xA002,0x7CB1,0x6A11,0xA00A,0xA004,0xA002,0x6DBC,0x7CE7,0xA002,0x7EC3,0xA002,
+ 0x7149,0x6200,0xA006,0xA002,0x94FE,0xA002,0x81C9,0x6582,0xA002,0x7C3E,0xA002,0x6F23,0x6190,0xA000,0xA000,0xA014,
+ 0xA00A,0xA004,0xA002,0x5EC9,0x9570,0xA002,0x9023,0xA002,0x84EE,0x806F,0xA004,0xA002,0x5006,0x54E9,0xA002,0x7483,
+ 0xA002,0x52A6,0x96B8,0xA000,0xA004,0xA002,0x701D,0x7C92,0xA002,0x7ACB,0xA000,0x75E2,0xA001,0xA051,0xA025,0xA00F,
+ 0xA005,0xA001,0xA001,0xA001,0x4FD0,0xA004,0xA002,0x4F8B,0x5088,0xA002,0x5229,0xA002,0x6B77,0x792B,0xA00A,0xA004,
+ 0xA002,0x52F5,0x7805,0xA002,0x9E97,0xA002,0x6817,0x540F,0xA006,0xA002,0x8354,0xA002,0x8389,0x79AE,0xA002,0x9CA4,
+ 0xA002,0x91CC,0x674E,0xA016,0xA00A,0xA004,0xA002,0x7406,0x7055,0xA002,0x96E2,0xA002,0x72F8,0x7C6C,0xA006,0xA002,
+ 0x9ECE,0xA002,0x7281,0x68A8,0xA002,0x91D0,0xA002,0x51B7,0x695E,0xA00A,0xA004,0xA002,0x7A1C,0x6DDA,0xA002,0x985E,
+ 0xA002,0x808B,0x64C2,0xA006,0xA002,0x58D8,0xA002,0x5121,0x7E8D,0xA002,0x78CA,0xA002,0x857E,0x956D,0xA02C,0xA016,
+ 0xA00A,0xA004,0xA002,0x96F7,0x6A02,0xA002,0x52D2,0xA002,0x6F87,0x70D9,0xA006,0xA002,0x916A,0xA002,0x59E5,0x4F6C,
+ 0xA002,0x8001,0xA002,0x7262,0x52DE,0xA00A,0xA004,0xA002,0x6488,0x6D6A,0xA002,0x6717,0xA002,0x90CE,0x5ECA,0xA006,
+ 0xA002,0x72FC,0xA002,0x6994,0x746F,0xA002,0x6FEB,0xA002,0x721B,0x7F06,0xA016,0xA00A,0xA004,0xA002,0x61F6,0x89C8,
+ 0xA002,0x652C,0xA002,0x8C30,0x703E,0xA006,0xA002,0x862D,0xA002,0x9611,0x7C43,0xA002,0x6514,0xA002,0x6B04,0x60CF,
+ 0xA00A,0xA004,0xA002,0x85CD,0x8D56,0xA002,0x6765,0xA002,0x840A,0x5566,0xA006,0xA002,0x8FA3,0xA002,0x81D8,0x881F,
+ 0xA002,0x5587,0xA002,0x62F9,0x5783,0xA01C,0xA000,0xA000,0xA000,0xA014,0xA00A,0xA004,0xA002,0x9614,0x5ED3,0xA002,
+ 0x64F4,0xA002,0x62EC,0x774F,0xA004,0xA002,0x7D91,0x665C,0xA002,0x5764,0xA002,0x6F70,0x6127,0xA000,0xA000,0xA000,
+ 0x9988,0xA0AC,0xA056,0xA02A,0xA014,0xA00A,0xA004,0xA002,0x5080,0x9B41,0xA002,0x594E,0xA002,0x8475,0x7ABA,0xA004,
+ 0xA002,0x5DCB,0x76D4,0xA002,0x8667,0xA002,0x6CC1,0x66E0,0xA00A,0xA004,0xA002,0x7736,0x7926,0xA002,0x6846,0xA002,
+ 0x72C2,0x7B50,0xA006,0xA002,0x5321,0xA002,0x6B3E,0x5BEC,0xA002,0x5FEB,0xA002,0x5108,0x7B77,0xA016,0xA00A,0xA004,
+ 0xA002,0x584A,0x80EF,0xA002,0x8DE8,0xA002,0x630E,0x57AE,0xA006,0xA002,0x8A87,0xA002,0x8932,0x5EAB,0xA002,0x9177,
+ 0xA002,0x82E6,0x7A9F,0xA00A,0xA004,0xA002,0x54ED,0x67AF,0xA002,0x5BC7,0xA002,0x91E6,0x56D7,0xA006,0xA002,0x6473,
+ 0xA002,0x63A7,0x5B54,0xA002,0x6050,0xA002,0x7A7A,0x542D,0xA02A,0xA014,0xA00A,0xA004,0xA002,0x962C,0x61C7,0xA002,
+ 0x58BE,0xA002,0x8C64,0x80AF,0xA004,0xA002,0x8BFE,0x5BA2,0xA002,0x523B,0xA002,0x524B,0x6E34,0xA00A,0xA004,0xA002,
+ 0x53EF,0x6B2C,0xA002,0x6BBC,0xA002,0x79D1,0x9897,0xA006,0xA002,0x78D5,0xA002,0x68F5,0x7241,0xA002,0x82DB,0xA002,
+ 0x5777,0x9760,0xA016,0xA00A,0xA004,0xA002,0x70E4,0x62F7,0xA002,0x8003,0xA002,0x7095,0x4EA2,0xA006,0xA002,0x6297,
+ 0xA002,0x6443,0x7CE0,0xA002,0x6177,0xA002,0x5EB7,0x770B,0xA00A,0xA004,0xA002,0x780D,0x6B3F,0xA002,0x52D8,0xA002,
+ 0x582A,0x520A,0xA006,0xA002,0x6168,0xA002,0x51F1,0x6977,0xA002,0x63E9,0xA002,0x958B,0x54AF,0xA000,0xA000,0xA000,
+ 0xA00A,0xA004,0xA002,0x5361,0x5496,0xA002,0x5580,0xA002,0x9A8F,0x90E1,0xA004,0xA002,0x6FEC,0x7AE3,0xA000,0x5BEF,
+ 0xA0D5,0xA0B7,0xA00B,0xA001,0xA001,0xA001,0xA001,0xA001,0xA002,0x5D1A,0xA002,0x541B,0x8ECD,0xA056,0xA02A,0xA014,
+ 0xA00A,0xA004,0xA002,0x94A7,0x83CC,0xA002,0x5747,0xA002,0x7EDD,0x8BC0,0xA004,0xA002,0x73A6,0x89C9,0xA002,0x7235,
+ 0xA002,0x5014,0x6398,0xA00A,0xA004,0xA002,0x6289,0x652B,0xA002,0x6485,0xA002,0x7EE2,0x6372,0xA006,0xA002,0x7760,
+ 0xA002,0x5026,0x5A1F,0xA002,0x9E43,0xA002,0x6350,0x5287,0xA016,0xA00A,0xA004,0xA002,0x70AC,0x61FC,0xA002,0x53E5,
+ 0xA002,0x4FF1,0x952F,0xA006,0xA002,0x8E1E,0xA002,0x8DDD,0x5177,0xA002,0x5DE8,0xA002,0x64DA,0x62D2,0xA00A,0xA004,
+ 0xA002,0x805A,0x6CAE,0xA002,0x8209,0xA002,0x842D,0x5480,0xA006,0xA002,0x8DFC,0xA002,0x83CA,0x9A79,0xA002,0x5C45,
+ 0xA002,0x75BD,0x72D9,0xA02A,0xA014,0xA00A,0xA004,0xA002,0x8EE5,0x97A0,0xA002,0x759A,0xA002,0x5C31,0x548E,0xA004,
+ 0xA002,0x8205,0x81FC,0xA002,0x820A,0xA002,0x6551,0x5EC4,0xA00A,0xA004,0xA002,0x9152,0x4E5D,0xA002,0x7078,0xA002,
+ 0x4E45,0x97ED,0xA006,0xA002,0x7396,0xA002,0x7EA0,0x7A76,0xA002,0x63EB,0xA002,0x7A98,0x70AF,0xA016,0xA00A,0xA004,
+ 0xA002,0x6DE8,0x7AF6,0xA002,0x7ADF,0xA002,0x9756,0x75D9,0xA006,0xA002,0x5F91,0xA002,0x955C,0x656C,0xA002,0x5883,
+ 0xA002,0x975C,0x9888,0xA00A,0xA004,0xA002,0x666F,0x8B66,0xA002,0x4E95,0xA002,0x7ECF,0x7CB3,0xA006,0xA002,0x7CBE,
+ 0xA002,0x9A5A,0x4EAC,0xA002,0x9CB8,0xA002,0x6676,0x775B,0xA00E,0xA000,0xA000,0xA000,0xA000,0xA004,0xA002,0x8396,
+ 0x5162,0xA002,0x834A,0xA002,0x52C1,0x76E1,0xA001,0xA001,0xA001,0xA001,0xA006,0xA002,0x6D78,0xA002,0x71FC,0x8FD1,
+ 0xA002,0x7981,0xA002,0x664B,0x9773,0xA0B5,0xA0AC,0xA056,0xA02A,0xA014,0xA00A,0xA004,0xA002,0x9032,0x8C28,0xA002,
+ 0x83EB,0xA002,0x9526,0x7DCA,0xA004,0xA002,0x895F,0x6D25,0xA002,0x4ECA,0xA002,0x91D1,0x91FF,0xA00A,0xA004,0xA002,
+ 0x7B4B,0x5DFE,0xA002,0x5C4A,0xA002,0x8BEB,0x75A5,0xA006,0xA002,0x4ECB,0xA002,0x501F,0x754C,0xA002,0x82A5,0xA002,
+ 0x85C9,0x6212,0xA016,0xA00A,0xA004,0xA002,0x601A,0x89E3,0xA002,0x7ED3,0xA002,0x7D5C,0x7AED,0xA006,0xA002,0x776B,
+ 0xA002,0x6377,0x6770,0xA002,0x6854,0xA002,0x8282,0x62BE,0xA00A,0xA004,0xA002,0x622A,0x968E,0xA002,0x8857,0xA002,
+ 0x9782,0x7686,0xA006,0xA002,0x63A5,0xA002,0x63ED,0x7A96,0xA002,0x53EB,0xA002,0x8F83,0x8F7F,0xA02A,0xA014,0xA00A,
+ 0xA004,0xA002,0x9175,0x6559,0xA002,0x52E6,0xA002,0x7EDE,0x7F34,0xA004,0xA002,0x997A,0x89D2,0xA002,0x72E1,0xA002,
+ 0x8173,0x50E5,0xA00A,0xA004,0xA002,0x77EF,0x94F0,0xA002,0x652A,0xA002,0x56BC,0x5B0C,0xA006,0xA002,0x9A84,0xA002,
+ 0x6F86,0x90CA,0xA002,0x4EA4,0xA002,0x81A0,0x7126,0xA016,0xA00A,0xA004,0xA002,0x7901,0x6912,0xA002,0x8549,0xA002,
+ 0x964D,0x91AC,0xA006,0xA002,0x5320,0xA002,0x8BB2,0x734E,0xA002,0x69F3,0xA002,0x8523,0x7586,0xA00A,0xA004,0xA002,
+ 0x6C5F,0x6F3F,0xA002,0x5C07,0xA002,0x8591,0x50F5,0xA006,0xA002,0x5EFA,0xA002,0x6F97,0x6FFA,0xA002,0x6F38,0xA002,
+ 0x996F,0x528D,0xA000,0xA000,0xA000,0xA000,0xA000,0xA002,0x8266,0x5065,0xA016,0xA001,0xA001,0xA001,0xA007,0xA001,
+ 0xA002,0x4EF6,0xA002,0x7BAD,0x952E,0xA006,0xA002,0x89C1,0xA002,0x8D31,0x8E10,0xA002,0x9452,0xA002,0x6ABB,0x85A6,
+ 0xA056,0xA02A,0xA014,0xA00A,0xA004,0xA002,0x6E1B,0x526A,0xA002,0x5109,0xA002,0x7C21,0x64BF,0xA004,0xA002,0x63C0,
+ 0x9E7C,0xA002,0x78B1,0xA002,0x67EC,0x6AA2,0xA00A,0xA004,0xA002,0x8327,0x7F04,0xA002,0x59E6,0xA002,0x8271,0x80A9,
+ 0xA006,0xA002,0x517C,0xA002,0x714E,0x95F4,0xA002,0x7B8B,0xA002,0x5C16,0x5805,0xA016,0xA00A,0xA004,0xA002,0x76E3,
+ 0x6BB2,0xA002,0x5AC1,0xA002,0x9A7E,0x67B6,0xA006,0xA002,0x50F9,0xA002,0x7A3C,0x5047,0xA002,0x94BE,0xA002,0x7532,
+ 0x8D3E,0xA00A,0xA004,0xA002,0x988A,0x83A2,0xA002,0x52A0,0xA002,0x5BB6,0x4F73,0xA006,0xA002,0x593E,0xA002,0x6935,
+ 0x5609,0xA002,0x7EAA,0xA002,0x7EE7,0x5993,0xA02C,0xA016,0xA00A,0xA004,0xA002,0x969B,0x5FCC,0xA002,0x65E2,0xA002,
+ 0x8BB0,0x8BA1,0xA006,0xA002,0x6F03,0xA002,0x5BC4,0x6FDF,0xA002,0x60B8,0xA002,0x5291,0x796D,0xA00A,0xA004,0xA002,
+ 0x4F0E,0x5B63,0xA002,0x5180,0xA002,0x6280,0x858A,0xA006,0xA002,0x5DF1,0xA002,0x810A,0x5E7E,0xA002,0x64E0,0xA002,
+ 0x7EA7,0x5AC9,0xA016,0xA00A,0xA004,0xA002,0x5373,0x6C72,0xA002,0x75BE,0xA002,0x6025,0x53CA,0xA006,0xA002,0x96C6,
+ 0xA002,0x7C4D,0x8F91,0xA002,0x68D8,0xA002,0x6975,0x5409,0xA00A,0xA004,0xA002,0x7F09,0x7EE9,0xA002,0x59EC,0xA002,
+ 0x9E21,0x8BA5,0xA006,0xA002,0x6FC0,0xA002,0x8FF9,0x9965,0xA000,0x808C,0xA1B4,0xA0C6,0xA01F,0xA001,0xA001,0xA001,
+ 0xA006,0xA001,0xA001,0xA001,0xA001,0x7B95,0xA00A,0xA004,0xA002,0x7A4D,0x7A3D,0xA002,0x7578,0xA002,0x6A5F,0x57FA,
+ 0xA006,0xA002,0x573E,0xA002,0x64CA,0x798D,0xA002,0x8D27,0xA002,0x970D,0x60D1,0xA000,0xA056,0xA02A,0xA014,0xA00A,
+ 0xA004,0xA002,0x6216,0x83B7,0xA002,0x706B,0xA002,0x4F19,0x6D3B,0xA004,0xA002,0x8C41,0x6DF7,0xA002,0x6E3E,0xA002,
+ 0x9B42,0x5A5A,0xA00A,0xA004,0xA002,0x660F,0x8477,0xA002,0x7ED8,0xA002,0x8BF2,0x8BB3,0xA006,0xA002,0x6C47,0xA002,
+ 0x71F4,0x6703,0xA002,0x7A62,0xA002,0x8D3F,0x6666,0xA016,0xA00A,0xA004,0xA002,0x60E0,0x5349,0xA002,0x6167,0xA002,
+ 0x6094,0x8B6D,0xA006,0xA002,0x8FF4,0xA002,0x86D4,0x6062,0xA002,0x5FBD,0xA002,0x8F89,0x63EE,0xA00A,0xA004,0xA002,
+ 0x7070,0x8C0E,0xA002,0x604D,0xA002,0x5E4C,0x6643,0xA006,0xA002,0x714C,0xA002,0x60F6,0x51F0,0xA002,0x7687,0xA002,
+ 0x7C27,0x8757,0xA02A,0xA014,0xA00A,0xA004,0xA002,0x78FA,0x9EC4,0xA002,0x614C,0xA002,0x8352,0x5E7B,0xA004,0xA002,
+ 0x5BA7,0x6E19,0xA002,0x7165,0xA002,0x8C62,0x7613,0xA00A,0xA004,0xA002,0x559A,0x60A3,0xA002,0x63DB,0xA002,0x7F13,
+ 0x9084,0xA006,0xA002,0x6853,0xA002,0x74B0,0x6B61,0xA002,0x58DE,0xA002,0x6DEE,0x61F7,0xA016,0xA00A,0xA004,0xA002,
+ 0x5F8A,0x69D0,0xA002,0x8BDD,0xA002,0x5316,0x5283,0xA006,0xA002,0x756B,0xA002,0x6ED1,0x733E,0xA002,0x83EF,0xA002,
+ 0x8B41,0x82B1,0xA00A,0xA004,0xA002,0x6237,0x6EEC,0xA002,0x4E92,0xA002,0x8B77,0x552C,0xA000,0xA002,0x864E,0xA000,
+ 0x5F27,0xA0C3,0xA023,0xA001,0xA001,0xA00B,0xA001,0xA004,0xA001,0xA001,0x6E56,0xA002,0x7CCA,0xA002,0x72D0,0x8774,
+ 0xA00A,0xA004,0xA002,0x9B0D,0x846B,0xA002,0x58FC,0xA002,0x745A,0x5FFD,0xA006,0xA002,0x8656,0xA002,0x547C,0x5F8C,
+ 0xA002,0x5019,0xA002,0x5795,0x5474,0xA056,0xA02A,0xA014,0xA00A,0xA004,0xA002,0x7334,0x4FAF,0xA002,0x5589,0xA002,
+ 0x7EA2,0x5F18,0xA004,0xA002,0x7D18,0x6D2A,0xA002,0x9E3F,0xA002,0x8743,0x70D8,0xA00A,0xA004,0xA002,0x9B28,0x8F70,
+ 0xA002,0x6052,0xA002,0x8861,0x6A6B,0xA006,0xA002,0x4EA8,0xA002,0x54FC,0x6068,0xA002,0x72E0,0xA002,0x5F88,0x75D5,
+ 0xA016,0xA00A,0xA004,0xA002,0x9ED1,0x563F,0xA002,0x8D3A,0xA002,0x9E64,0x8910,0xA006,0xA002,0x8D6B,0xA002,0x6DB8,
+ 0x6CB3,0xA002,0x9602,0xA002,0x8C89,0x76D2,0xA00A,0xA004,0xA002,0x5408,0x4F55,0xA002,0x9FA2,0xA002,0x79BE,0x6838,
+ 0xA006,0xA002,0x83CF,0xA002,0x8377,0x6B31,0xA002,0x5475,0xA002,0x6D69,0x865F,0xA02C,0xA016,0xA00A,0xA004,0xA002,
+ 0x8017,0x597D,0xA002,0x90DD,0xA002,0x6BEB,0x8C6A,0xA006,0xA002,0x568E,0xA002,0x58D5,0x822A,0xA002,0x676D,0xA002,
+ 0x592F,0x6F22,0xA00A,0xA004,0xA002,0x6C57,0x92B2,0xA002,0x608D,0xA002,0x61BE,0x65F1,0xA006,0xA002,0x634D,0xA002,
+ 0x64BC,0x7FF0,0xA002,0x7F55,0xA002,0x558A,0x51FD,0xA016,0xA00A,0xA004,0xA002,0x5BD2,0x6DB5,0xA002,0x5505,0xA002,
+ 0x97E9,0x90AF,0xA006,0xA002,0x61A8,0xA002,0x9163,0x9A87,0xA002,0x5BB3,0xA002,0x4EA5,0x6C26,0xA000,0xA004,0xA002,
+ 0x6D77,0x5B69,0xA000,0x9AB8,0xA001,0xA001,0xA001,0xA012,0xA006,0xA001,0xA001,0xA002,0x54C8,0x904E,0xA006,0xA002,
+ 0x88F9,0xA002,0x679C,0x570B,0xA002,0x90ED,0xA002,0x9505,0x68CD,0xA00A,0xA004,0xA002,0x6EFE,0x8F8A,0xA002,0x528A,
+ 0xA002,0x8D35,0x8DEA,0xA006,0xA002,0x6AC3,0xA002,0x6842,0x7678,0xA002,0x8BE1,0xA002,0x9B3C,0x8F68,0xA159,0xA098,
+ 0xA000,0xA056,0xA02A,0xA014,0xA00A,0xA004,0xA002,0x95FA,0x9F9F,0xA002,0x6B78,0xA002,0x7845,0x73EA,0xA004,0xA002,
+ 0x89C4,0x7470,0xA002,0x901B,0xA002,0x5EE3,0x5149,0xA00A,0xA004,0xA002,0x8D2F,0x704C,0xA002,0x6163,0xA002,0x7F50,
+ 0x9986,0xA006,0xA002,0x7BA1,0xA002,0x89C2,0x51A0,0xA002,0x5B98,0xA002,0x95DC,0x68FA,0xA016,0xA00A,0xA004,0xA002,
+ 0x602A,0x67B4,0xA002,0x4E56,0xA002,0x8902,0x7F63,0xA006,0xA002,0x5BE1,0xA002,0x526E,0x74DC,0xA002,0x98B3,0xA002,
+ 0x96C7,0x56FA,0xA00A,0xA004,0xA002,0x987E,0x6545,0xA002,0x80A1,0xA002,0x8C37,0x9AA8,0xA006,0xA002,0x8831,0xA002,
+ 0x53E4,0x9F13,0xA002,0x59D1,0xA002,0x5B64,0x6CBD,0xA02A,0xA014,0xA00A,0xA004,0xA002,0x4F30,0x7B8D,0xA002,0x5495,
+ 0xA002,0x83C7,0x8F9C,0xA004,0xA002,0x5920,0x8D2D,0xA002,0x69CB,0xA002,0x57A2,0x72D7,0xA00A,0xA004,0xA002,0x830D,
+ 0x6E9D,0xA002,0x52FE,0xA002,0x94A9,0x5171,0xA006,0xA002,0x8D21,0xA002,0x62F1,0x6C5E,0xA002,0x978F,0xA002,0x5F13,
+ 0x5BAE,0xA000,0xA00A,0xA004,0xA002,0x516C,0x8EAC,0xA002,0x4F9B,0xA002,0x9F9A,0x606D,0xA006,0xA002,0x529F,0xA002,
+ 0x653B,0x5DE5,0xA002,0x6897,0xA002,0x803F,0x57C2,0xA02D,0xA001,0xA001,0xA015,0xA009,0xA003,0xA001,0x7FB9,0xA002,
+ 0x5E9A,0xA002,0x66F4,0x8015,0xA006,0xA002,0x8DDF,0xA002,0x6839,0x7ED9,0xA002,0x5404,0xA002,0x7B87,0x94EC,0xA00A,
+ 0xA004,0xA002,0x9694,0x9601,0xA002,0x86E4,0xA002,0x683C,0x845B,0xA006,0xA002,0x9769,0xA002,0x5272,0x7599,0xA002,
+ 0x80F3,0xA002,0x9E3D,0x6208,0xA056,0xA02A,0xA014,0xA00A,0xA004,0xA002,0x64F1,0x6B4C,0xA002,0x54E5,0xA002,0x544A,
+ 0x7A3F,0xA004,0xA002,0x9550,0x641E,0xA002,0x7CD5,0xA002,0x7F94,0x818F,0xA00A,0xA004,0xA002,0x9AD8,0x768B,0xA002,
+ 0x7BD9,0xA002,0x69D3,0x6E2F,0xA006,0xA002,0x5D17,0xA002,0x7EB2,0x809B,0xA002,0x7F38,0xA002,0x94A2,0x525B,0xA016,
+ 0xA00A,0xA004,0xA002,0x5CA1,0x8D63,0xA002,0x6562,0xA002,0x7A08,0x611F,0xA006,0xA002,0x8D95,0xA002,0x809D,0x7AFF,
+ 0xA002,0x67D1,0xA002,0x687F,0x7518,0xA00A,0xA004,0xA002,0x8677,0x6E89,0xA002,0x84CB,0xA002,0x9499,0x6982,0xA006,
+ 0xA002,0x6539,0xA002,0x8BE5,0x560E,0xA002,0x5676,0xA002,0x5490,0x7F1A,0xA02C,0xA016,0xA00A,0xA004,0xA002,0x5A66,
+ 0x9644,0xA002,0x8BA3,0xA002,0x5BCC,0x8D1F,0xA006,0xA002,0x8179,0xA002,0x7236,0x961C,0xA002,0x4ED8,0xA002,0x5085,
+ 0x8907,0xA00A,0xA004,0xA002,0x8D4B,0x8986,0xA002,0x526F,0xA002,0x8D74,0x8150,0xA006,0xA002,0x5E9C,0xA002,0x8151,
+ 0x812F,0xA002,0x65A7,0xA002,0x91DC,0x982B,0xA000,0xA00A,0xA004,0xA002,0x8F85,0x64AB,0xA002,0x752B,0xA002,0x5F17,
+ 0x88B1,0xA000,0xA002,0x8300,0xA002,0x6DAA,0x6D6E,0xA037,0xA001,0xA001,0xA009,0xA001,0xA001,0xA001,0xA002,0x670D,
+ 0xA002,0x4FD8,0x8659,0xA016,0xA00A,0xA004,0xA002,0x7B26,0x6C1F,0xA002,0x5E45,0xA002,0x8F90,0x62C2,0xA006,0xA002,
+ 0x6276,0xA002,0x5B75,0x819A,0xA002,0x6577,0xA002,0x592B,0x5426,0xA00A,0xA004,0xA002,0x5F7F,0x9CF3,0xA002,0x5949,
+ 0xA002,0x8BBD,0x7F1D,0xA006,0xA002,0x99AE,0xA002,0x9022,0x70FD,0xA002,0x760B,0xA002,0x98CE,0x950B,0xA000,0xA056,
+ 0xA02A,0xA014,0xA00A,0xA004,0xA002,0x5CF0,0x8702,0xA002,0x6953,0xA002,0x5C01,0x8C50,0xA004,0xA002,0x7CDE,0x61A4,
+ 0xA002,0x5FFF,0xA002,0x4EFD,0x596E,0xA00A,0xA004,0xA002,0x7C89,0x6C7E,0xA002,0x711A,0xA002,0x58B3,0x7EB7,0xA006,
+ 0xA002,0x5206,0xA002,0x96F0,0x5429,0xA002,0x915A,0xA002,0x82AC,0x8D39,0xA016,0xA00A,0xA004,0xA002,0x6CB8,0x5EE2,
+ 0xA002,0x80BA,0xA002,0x5420,0x8BFD,0xA006,0xA002,0x532A,0xA002,0x80A5,0x98DE,0xA002,0x5561,0xA002,0x975E,0x83F2,
+ 0xA00A,0xA004,0xA002,0x653E,0x7EBA,0xA002,0x8BBF,0xA002,0x9AE3,0x59A8,0xA006,0xA002,0x9632,0xA002,0x623F,0x80AA,
+ 0xA002,0x65B9,0xA002,0x82B3,0x574A,0xA02A,0xA014,0xA00A,0xA004,0xA002,0x6CDB,0x996D,0xA002,0x72AF,0xA002,0x8D29,
+ 0x8303,0xA004,0xA002,0x8FD4,0x53CD,0xA002,0x7169,0xA002,0x51E1,0x7E41,0xA00A,0xA004,0xA002,0x9492,0x792C,0xA002,
+ 0x6A0A,0xA002,0x7FFB,0x756A,0xA006,0xA002,0x5E06,0xA002,0x85E9,0x743A,0xA002,0x6CD5,0xA002,0x9600,0x8982,0xA000,
+ 0xA000,0xA004,0xA002,0x4F10,0x7B4F,0xA002,0x7F70,0xA002,0x9AEE,0x8D30,0xA555,0xA428,0xA249,0xA104,0xA0C2,0xA03B,
+ 0xA001,0xA00E,0xA001,0xA001,0xA006,0xA002,0x4E8C,0xA002,0x6E33,0x9975,0xA002,0x723E,0xA002,0x8033,0x5152,0xA016,
+ 0xA00A,0xA004,0xA002,0x800C,0x6069,0xA002,0x997F,0xA002,0x9102,0x904F,0xA006,0xA002,0x6424,0xA002,0x9628,0x60E1,
+ 0xA002,0x5A25,0xA002,0x8BB9,0x989D,0xA00A,0xA004,0xA002,0x774B,0x9E45,0xA002,0x5CE8,0xA002,0x86FE,0x58AE,0xA006,
+ 0xA002,0x60F0,0xA002,0x5241,0x8235,0xA002,0x8DFA,0xA002,0x6735,0x8EB2,0xA056,0xA02A,0xA014,0xA00A,0xA004,0xA002,
+ 0x579B,0x596A,0xA002,0x591A,0xA002,0x8A83,0x656A,0xA004,0xA002,0x9041,0x76FE,0xA002,0x949D,0xA002,0x56E4,0x987F,
+ 0xA00A,0xA004,0xA002,0x6566,0x8E72,0xA002,0x5678,0xA002,0x58A9,0x5C0D,0xA006,0xA002,0x968A,0xA002,0x5151,0x5806,
+ 0xA002,0x7F0E,0xA002,0x65B7,0x6BB5,0xA016,0xA00A,0xA004,0xA002,0x953B,0x77ED,0xA002,0x7AEF,0xA002,0x5992,0x6E21,
+ 0xA006,0xA002,0x5EA6,0xA002,0x809A,0x9540,0xA002,0x675C,0xA002,0x8D4C,0x7779,0xA00A,0xA004,0xA002,0x5835,0x8BFB,
+ 0xA002,0x7368,0xA002,0x72A2,0x6BD2,0xA006,0xA002,0x7763,0xA002,0x90FD,0x75D8,0xA002,0x9017,0xA002,0x8C46,0x9661,
+ 0xA02A,0xA014,0xA00A,0xA004,0xA002,0x9B25,0x6296,0xA002,0x515C,0xA002,0x6D1E,0x51CD,0xA004,0xA002,0x75CC,0x4F97,
+ 0xA002,0x68DF,0xA002,0x52D5,0x61C2,0xA00A,0xA004,0xA002,0x8463,0x9F15,0xA002,0x6771,0xA002,0x4E22,0x8BA2,0xA006,
+ 0xA002,0x5B9A,0xA002,0x952D,0x9F0E,0xA002,0x9876,0xA002,0x9489,0x53EE,0xA000,0xA000,0xA000,0xA002,0x76EF,0x4E01,
+ 0xA001,0xA001,0xA014,0xA001,0xA007,0xA001,0xA002,0x758A,0xA002,0x8C0D,0x8FED,0xA006,0xA002,0x8776,0xA002,0x789F,
+ 0x7239,0xA002,0x8DCC,0xA002,0x8C03,0x9493,0xA016,0xA00A,0xA004,0xA002,0x5F14,0x6389,0xA002,0x5201,0xA002,0x51CB,
+ 0x9D70,0xA006,0xA002,0x53FC,0xA002,0x7889,0x6BBF,0xA002,0x6FB1,0xA002,0x5960,0x60E6,0xA00A,0xA004,0xA002,0x5E97,
+ 0x7538,0xA002,0x4F43,0xA002,0x96FB,0x588A,0xA006,0xA002,0x975B,0xA002,0x5178,0x9EDE,0xA002,0x7898,0xA002,0x6EC7,
+ 0x6382,0xA081,0xA000,0xA056,0xA02A,0xA014,0xA00A,0xA004,0xA002,0x98A0,0x7F14,0xA002,0x905E,0xA002,0x5F1F,0x5E1D,
+ 0xA004,0xA002,0x7B2C,0x8482,0xA002,0x58AC,0xA002,0x5E95,0x89DD,0xA00A,0xA004,0xA002,0x5AE1,0x7FDF,0xA002,0x6ECC,
+ 0xA002,0x72C4,0x7B1B,0xA006,0xA002,0x6575,0xA002,0x8FEA,0x6EF4,0xA002,0x4F4E,0xA002,0x9684,0x9127,0xA016,0xA00A,
+ 0xA004,0xA002,0x51F3,0x77AA,0xA002,0x7B49,0xA002,0x8C4B,0x71C8,0xA006,0xA002,0x8E6C,0xA002,0x7684,0x6DC2,0xA002,
+ 0x5FB7,0xA002,0x76DC,0x9053,0xA00A,0xA004,0xA002,0x60BC,0x7A3B,0xA002,0x5230,0xA002,0x5C0E,0x79B1,0xA006,0xA002,
+ 0x5CF6,0xA002,0x5012,0x8E48,0xA002,0x6417,0xA002,0x5200,0x6A94,0xA000,0xA014,0xA00A,0xA004,0xA002,0x8569,0x9EE8,
+ 0xA002,0x64CB,0xA002,0x7576,0x86CB,0xA004,0xA002,0x5F48,0x8BDE,0xA002,0x6DE1,0xA002,0x619A,0x4F46,0xA00A,0xA004,
+ 0xA002,0x6C2E,0x65E6,0xA002,0x81BD,0xA002,0x64A3,0x9132,0xA006,0xA002,0x55AE,0xA002,0x4E39,0x64D4,0xA002,0x803D,
+ 0xA000,0x6020,0xA049,0xA001,0xA01C,0xA006,0xA001,0xA001,0xA001,0xA001,0x902E,0xA00A,0xA004,0xA002,0x5F85,0x888B,
+ 0xA002,0x8D37,0xA002,0x4EE3,0x6B86,0xA006,0xA002,0x5E36,0xA002,0x6234,0x50A3,0xA002,0x6B79,0xA002,0x7343,0x5927,
+ 0xA016,0xA00A,0xA004,0xA002,0x6253,0x7629,0xA002,0x8345,0xA002,0x9054,0x642D,0xA006,0xA002,0x9519,0xA002,0x632B,
+ 0x63AA,0xA002,0x6413,0xA002,0x64AE,0x78CB,0xA00A,0xA004,0xA002,0x5BF8,0x5B58,0xA002,0x6751,0xA002,0x7FE0,0x7120,
+ 0xA006,0xA002,0x7CB9,0xA002,0x7601,0x8106,0xA002,0x55FA,0xA002,0x5D14,0x6F3C,0xA056,0xA02A,0xA014,0xA00A,0xA004,
+ 0xA002,0x7AC4,0x7BE1,0xA002,0x8EA5,0xA002,0x4FC3,0x7C07,0xA004,0xA002,0x918B,0x9EA4,0xA002,0x6E4A,0xA002,0x53E2,
+ 0x5F9E,0xA00A,0xA004,0xA002,0x5306,0x56F1,0xA002,0x8525,0xA002,0x8070,0x6B21,0xA006,0xA002,0x8D50,0xA002,0x83BF,
+ 0x6B64,0xA002,0x8BCD,0xA002,0x74F7,0x6148,0xA016,0xA00A,0xA004,0xA002,0x8FAD,0x96CC,0xA002,0x78C1,0xA002,0x8328,
+ 0x75B5,0xA006,0xA002,0x7EF0,0xA002,0x6233,0x8822,0xA002,0x7EAF,0xA002,0x6DF3,0x8123,0xA00A,0xA004,0xA002,0x9187,
+ 0x693F,0xA002,0x6625,0xA002,0x5782,0x9524,0xA006,0xA002,0x6425,0xA002,0x708A,0x5439,0xA002,0x5275,0xA002,0x95EF,
+ 0x5E8A,0xA000,0xA016,0xA00A,0xA004,0xA002,0x5E62,0x7A97,0xA002,0x7621,0xA002,0x4E32,0x5598,0xA006,0xA002,0x8239,
+ 0xA002,0x50B3,0x693D,0xA002,0x7A7F,0xA002,0x5DDD,0x63E3,0xA00A,0xA004,0xA002,0x8655,0x89F8,0xA002,0x6410,0xA002,
+ 0x77D7,0x5132,0xA000,0xA000,0x790E,0xA0C4,0xA04E,0xA001,0xA001,0xA020,0xA00A,0xA001,0xA003,0xA001,0x695A,0xA002,
+ 0x9664,0xA002,0x6EC1,0x96DB,0xA00A,0xA004,0xA002,0x9504,0x8E87,0xA002,0x5EDA,0xA002,0x6AE5,0x9F63,0xA006,0xA002,
+ 0x521D,0xA002,0x81ED,0x919C,0xA002,0x7785,0xA002,0x7EF8,0x4EC7,0xA016,0xA00A,0xA004,0xA002,0x7C4C,0x6101,0xA002,
+ 0x7A20,0xA002,0x8E8A,0x7587,0xA006,0xA002,0x916C,0xA002,0x62BD,0x5BF5,0xA002,0x5D07,0xA002,0x87F2,0x8876,0xA00A,
+ 0xA004,0xA002,0x5145,0x71BE,0xA002,0x65A5,0xA002,0x7FC5,0x8D64,0xA006,0xA002,0x5C3A,0xA002,0x4F88,0x9F7F,0xA002,
+ 0x803B,0xA002,0x9A70,0x5F1B,0xA000,0xA056,0xA02A,0xA014,0xA00A,0xA004,0xA002,0x9072,0x6C60,0xA002,0x5319,0xA002,
+ 0x6301,0x7661,0xA004,0xA002,0x55AB,0x79E4,0xA002,0x9A8B,0xA002,0x901E,0x627F,0xA00A,0xA004,0xA002,0x8BDA,0x6F84,
+ 0xA002,0x61F2,0xA002,0x7A0B,0x4E58,0xA006,0xA002,0x5448,0xA002,0x6210,0x6A59,0xA002,0x57CE,0xA002,0x7A31,0x6491,
+ 0xA016,0xA00A,0xA004,0xA002,0x896F,0x8D81,0xA002,0x9673,0xA002,0x6C89,0x5FF1,0xA006,0xA002,0x6668,0xA002,0x5C18,
+ 0x8FB0,0xA002,0x81E3,0xA002,0x90F4,0x6F88,0xA00A,0xA004,0xA002,0x5FB9,0x63A3,0xA002,0x64A4,0xA002,0x64A6,0x8F66,
+ 0xA006,0xA002,0x7092,0xA002,0x8A2C,0x6A14,0xA002,0x6F6E,0xA002,0x5632,0x671D,0xA000,0xA014,0xA00A,0xA004,0xA002,
+ 0x949E,0x6284,0xA002,0x8D85,0xA002,0x5021,0x5531,0xA004,0xA002,0x7545,0x655E,0xA002,0x5EE0,0xA002,0x8178,0x511F,
+ 0xA000,0xA004,0xA002,0x957F,0x5E38,0xA002,0x5C1D,0xA000,0x5834,0xA0C1,0xA053,0xA001,0xA026,0xA010,0xA006,0xA001,
+ 0xA001,0xA002,0x7316,0x660C,0xA004,0xA002,0x98A4,0x9610,0xA002,0x7522,0xA002,0x94F2,0x7F20,0xA00A,0xA004,0xA002,
+ 0x8C17,0x998B,0xA002,0x87EC,0xA002,0x647B,0x6519,0xA006,0xA002,0x8C7A,0xA002,0x67F4,0x62C6,0xA002,0x8BE7,0xA002,
+ 0x5DEE,0x5C94,0xA016,0xA00A,0xA004,0xA002,0x5BDF,0x643D,0xA002,0x78B4,0xA002,0x67E5,0x8336,0xA006,0xA002,0x832C,
+ 0xA002,0x53C9,0x63D2,0xA002,0x8E6D,0xA002,0x5C64,0x6E2C,0xA00A,0xA004,0xA002,0x518C,0x5E82,0xA002,0x7B74,0xA002,
+ 0x5EC1,0x9A32,0xA006,0xA002,0x66F9,0xA002,0x69FD,0x7CD9,0xA002,0x64CD,0xA002,0x85CF,0x6EC4,0xA056,0xA02A,0xA014,
+ 0xA00A,0xA004,0xA002,0x5009,0x8259,0xA002,0x84BC,0xA002,0x71E6,0x61AF,0xA004,0xA002,0x615A,0x6B98,0xA002,0x8836,
+ 0xA002,0x8460,0x9910,0xA00A,0xA004,0xA002,0x8521,0x83DC,0xA002,0x7DB5,0xA002,0x91C7,0x8E29,0xA006,0xA002,0x776C,
+ 0xA002,0x8D22,0x7E94,0xA002,0x6750,0xA002,0x88C1,0x731C,0xA016,0xA00A,0xA004,0xA002,0x64E6,0x6016,0xA002,0x90E8,
+ 0xA002,0x7C3F,0x6B65,0xA006,0xA002,0x5E03,0xA002,0x4E0D,0x57E0,0xA002,0x88DC,0xA002,0x54FA,0x8514,0xA00A,0xA004,
+ 0xA002,0x6355,0x9A73,0xA002,0x6CCA,0xA002,0x6E24,0x818A,0xA006,0xA002,0x8116,0xA002,0x8236,0x5E1B,0xA002,0x4F2F,
+ 0xA002,0x7B94,0x94C2,0xA000,0xA000,0xA00A,0xA004,0xA002,0x640F,0x52C3,0xA002,0x535A,0xA002,0x6CE2,0x94B5,0xA006,
+ 0xA002,0x64A5,0xA002,0x64AD,0x83E0,0xA002,0x73BB,0xA002,0x5E76,0x75C5,0xA001,0xA001,0xA02C,0xA016,0xA00A,0xA004,
+ 0xA002,0x70B3,0x997C,0xA002,0x79C9,0xA002,0x4E19,0x67C4,0xA006,0xA002,0x51B0,0xA002,0x5175,0x64EF,0xA002,0x8CD3,
+ 0xA002,0x6FF1,0x7015,0xA00A,0xA004,0xA002,0x658C,0x5F6C,0xA002,0x765F,0xA002,0x5F46,0x618B,0xA006,0xA002,0x9CD6,
+ 0xA002,0x9336,0x8198,0xA002,0x5F6A,0xA002,0x6A19,0x904D,0xA016,0xA00A,0xA004,0xA002,0x8FAE,0x8FAF,0xA002,0x8FA8,
+ 0xA002,0x535E,0x8B8A,0xA006,0xA002,0x4FBF,0xA002,0x6241,0x8D2C,0xA002,0x7F16,0xA002,0x908A,0x97AD,0xA00A,0xA004,
+ 0xA002,0x965B,0x907F,0xA002,0x81C2,0xA002,0x58C1,0x95E2,0xA006,0xA002,0x5FC5,0xA002,0x5F0A,0x655D,0xA002,0x95ED,
+ 0xA002,0x75FA,0x5E87,0xA000,0xA000,0xA068,0xA000,0xA056,0xA02A,0xA014,0xA00A,0xA004,0xA002,0x5E63,0x6BD6,0xA002,
+ 0x6BD9,0xA002,0x7562,0x853D,0xA004,0xA002,0x84D6,0x78A7,0xA002,0x5F7C,0xA002,0x7B46,0x9119,0xA00A,0xA004,0xA002,
+ 0x6BD4,0x9F3B,0xA002,0x903C,0xA002,0x8FF8,0x8E66,0xA006,0xA002,0x6CF5,0xA002,0x752D,0x7EF7,0xA002,0x5D29,0xA002,
+ 0x7B28,0x672C,0xA016,0xA00A,0xA004,0xA002,0x82EF,0x5954,0xA002,0x88AB,0xA002,0x7119,0x618A,0xA006,0xA002,0x5907,
+ 0xA002,0x72FD,0x500D,0xA002,0x94A1,0xA002,0x8D1D,0x80CC,0xA00A,0xA004,0xA002,0x8F88,0x5317,0xA002,0x5351,0xA002,
+ 0x60B2,0x7891,0xA006,0xA002,0x76C3,0xA002,0x7206,0x9C8D,0xA002,0x8C79,0xA002,0x66B4,0x62A5,0xA000,0xA000,0xA00A,
+ 0xA004,0xA002,0x62B1,0x5BF6,0xA002,0x9971,0xA002,0x5821,0x4FDD,0xA000,0xA002,0x96F9,0x8584,0xA060,0xA00A,0xA001,
+ 0xA001,0xA001,0xA001,0xA002,0x5265,0xA002,0x8912,0x5305,0xA02A,0xA014,0xA00A,0xA004,0xA002,0x80DE,0x82DE,0xA002,
+ 0x8C24,0xA002,0x508D,0x9551,0xA004,0xA002,0x868C,0x78C5,0xA002,0x68D2,0xA002,0x7ED1,0x8180,0xA00A,0xA004,0xA002,
+ 0x699C,0x6886,0xA002,0x5E6B,0xA002,0x90A6,0x7ECA,0xA006,0xA002,0x8FA6,0xA002,0x534A,0x74E3,0xA002,0x4F34,0xA002,
+ 0x62CC,0x626E,0xA016,0xA00A,0xA004,0xA002,0x7248,0x95C6,0xA002,0x9881,0xA002,0x822C,0x6273,0xA006,0xA002,0x642C,
+ 0xA002,0x73ED,0x6591,0xA002,0x7A17,0xA002,0x62DC,0x8D25,0xA00A,0xA004,0xA002,0x4F70,0x896C,0xA002,0x767E,0xA002,
+ 0x67CF,0x767D,0xA006,0xA002,0x7238,0xA002,0x7F77,0x9738,0xA002,0x58E9,0xA002,0x8019,0x628A,0xA056,0xA02A,0xA014,
+ 0xA00A,0xA004,0xA002,0x9776,0x8DCB,0xA002,0x62D4,0xA002,0x5DF4,0x75A4,0xA004,0xA002,0x516B,0x7B06,0xA002,0x5427,
+ 0xA002,0x53ED,0x6252,0xA00A,0xA004,0xA002,0x634C,0x82AD,0xA002,0x6FB3,0xA002,0x61CA,0x5967,0xA006,0xA002,0x50B2,
+ 0xA002,0x8956,0x7FF1,0xA002,0x71AC,0xA002,0x6556,0x51F9,0xA016,0xA00A,0xA004,0xA002,0x76CE,0x6602,0xA002,0x9AAF,
+ 0xA002,0x6848,0x80FA,0xA006,0xA002,0x5CB8,0xA002,0x6697,0x6309,0xA002,0x4FFA,0xA002,0x5B89,0x6C28,0xA00A,0xA004,
+ 0xA002,0x978D,0x9698,0xA002,0x7231,0xA002,0x7919,0x827E,0xA006,0xA002,0x77EE,0xA002,0x85F9,0x764C,0xA002,0x769A,
+ 0xA002,0x54C0,0x5509,0xA000,0xA000,0xA000,0xA004,0xA002,0x54CE,0x6328,0xA002,0x57C3,0xA002,0x963F,0x554A,0xA1B5,
+ 0xA06D,0xA001,0xA001,0xA001,0xA017,0xA001,0xA001,0xA009,0xA003,0xA001,0x254B,0xA002,0x254A,0xA002,0x2549,0x2548,
+ 0xA006,0xA002,0x2547,0xA002,0x2546,0x2545,0xA002,0x2544,0xA002,0x2543,0x2542,0xA029,0xA015,0xA00A,0xA004,0xA002,
+ 0x2541,0x2540,0xA002,0x253F,0xA002,0x253E,0x253D,0xA005,0xA001,0xA002,0x253B,0x253A,0xA002,0x2539,0xA002,0x2538,
+ 0x2537,0xA009,0xA004,0xA002,0x2536,0x2535,0xA001,0xA002,0x2533,0x2532,0xA006,0xA002,0x2531,0xA002,0x2530,0x252F,
+ 0xA002,0x252E,0xA000,0x252D,0xA015,0xA00A,0xA004,0xA002,0x252B,0x252A,0xA002,0x2529,0xA002,0x2528,0x2527,0xA005,
+ 0xA002,0x2526,0xA000,0x2525,0xA002,0x2523,0xA002,0x2522,0x2521,0xA009,0xA004,0xA002,0x2520,0x251F,0xA002,0x251E,
+ 0xA000,0x251D,0xA006,0xA002,0x251B,0xA002,0x251A,0x2519,0xA002,0x2518,0xA002,0x2517,0x2516,0xA0B7,0xA02D,0xA000,
+ 0xA000,0xA000,0xA014,0xA00A,0xA004,0xA002,0x2515,0x2514,0xA002,0x2513,0xA002,0x2512,0x2511,0xA004,0xA002,0x2510,
+ 0x250F,0xA002,0x250E,0xA002,0x250D,0x250C,0xA00A,0xA004,0xA002,0x250B,0x250A,0xA002,0x2509,0xA002,0x2508,0x2507,
+ 0xA006,0xA002,0x2506,0xA002,0x2505,0x2504,0xA002,0x2503,0xA002,0x2223,0x2501,0xA058,0xA013,0xA001,0xA001,0xA005,
+ 0xA001,0xA001,0xA001,0x3129,0xA006,0xA002,0x3128,0xA002,0x3127,0x3126,0xA002,0x3125,0xA002,0x3124,0x3123,0xA02A,
+ 0xA014,0xA00A,0xA004,0xA002,0x3122,0x3121,0xA002,0x3120,0xA002,0x311F,0x311E,0xA004,0xA002,0x311D,0x311C,0xA002,
+ 0x311B,0xA002,0x311A,0x3119,0xA00A,0xA004,0xA002,0x3118,0x3117,0xA002,0x3116,0xA002,0x3115,0x3114,0xA006,0xA002,
+ 0x3113,0xA002,0x3112,0x3111,0xA002,0x3110,0xA002,0x310F,0x310E,0xA013,0xA00A,0xA004,0xA002,0x310D,0x310C,0xA002,
+ 0x310B,0xA002,0x310A,0x3109,0xA006,0xA002,0x3108,0xA002,0x3107,0x3106,0xA000,0x3105,0xA001,0xA001,0xA002,0x00EA,
+ 0xA002,0x00FC,0x01DC,0xA000,0xA02A,0xA014,0xA00A,0xA004,0xA002,0x01DA,0x01D8,0xA002,0x01D6,0xA002,0x00F9,0x01D4,
+ 0xA004,0xA002,0x00FA,0x016B,0xA002,0x00F2,0xA002,0x01D2,0x00F3,0xA00A,0xA004,0xA002,0x014D,0x00EC,0xA002,0x01D0,
+ 0xA002,0x00ED,0x012B,0xA006,0xA002,0x00E8,0xA002,0x011B,0x00E9,0xA002,0x0113,0xA002,0x00E0,0x01CE,0xA000,0xA000,
+ 0xA000,0xA002,0x00E1,0x0101,0xA067,0xA001,0xA028,0xA001,0xA011,0xA005,0xA001,0xA001,0xA001,0x044F,0xA006,0xA002,
+ 0x044E,0xA002,0x044D,0x044C,0xA002,0x044B,0xA002,0x044A,0x0449,0xA00A,0xA004,0xA002,0x0448,0x0447,0xA002,0x0446,
+ 0xA002,0x0445,0x0444,0xA006,0xA002,0x0443,0xA002,0x0442,0x0441,0xA002,0x0440,0xA002,0x043F,0x043E,0xA020,0xA016,
+ 0xA00A,0xA004,0xA002,0x043D,0x043C,0xA002,0x043B,0xA002,0x043A,0x0439,0xA006,0xA002,0x0438,0xA002,0x0437,0x0436,
+ 0xA002,0x0451,0xA002,0x0435,0x0434,0xA000,0xA004,0xA002,0x0433,0x0432,0xA002,0x0431,0xA000,0x0430,0xA008,0xA001,
+ 0xA001,0xA002,0x042F,0xA002,0x042E,0x042D,0xA00A,0xA004,0xA002,0x042C,0x042B,0xA002,0x042A,0xA002,0x0429,0x0428,
+ 0xA006,0xA002,0x0427,0xA002,0x0426,0x0425,0xA002,0x0424,0xA002,0x0423,0x0422,0xA000,0xA000,0xA000,0xA014,0xA00A,
+ 0xA004,0xA002,0x0421,0x0420,0xA002,0x041F,0xA002,0x041E,0x041D,0xA004,0xA002,0x041C,0x041B,0xA002,0x041A,0xA002,
+ 0x0419,0x0418,0xA00A,0xA004,0xA002,0x0417,0x0416,0xA002,0x0401,0xA002,0x0415,0x0414,0xA006,0xA002,0x0413,0xA002,
+ 0x0412,0x0411,0xA000,0x0410,0xA1CA,0xA0FF,0xA068,0xA046,0xA001,0xA026,0xA010,0xA006,0xA001,0xA001,0xA002,0x03C9,
+ 0x03C8,0xA004,0xA002,0x03C7,0x03C6,0xA002,0x03C5,0xA002,0x03C4,0x03C3,0xA00A,0xA004,0xA002,0x03C1,0x03C0,0xA002,
+ 0x03BF,0xA002,0x03BE,0x03BD,0xA006,0xA002,0x03BC,0xA002,0x03BB,0x03BA,0xA002,0x03B9,0xA002,0x03B8,0x03B7,0xA00E,
+ 0xA00A,0xA004,0xA002,0x03B6,0x03B5,0xA002,0x03B4,0xA002,0x03B3,0x03B2,0xA000,0xA000,0x03B1,0xA006,0xA001,0xA001,
+ 0xA002,0x03A9,0x03A8,0xA006,0xA002,0x03A7,0xA002,0x03A6,0x03A5,0xA002,0x03A4,0xA001,0x03A1,0xA000,0xA000,0xA012,
+ 0xA009,0xA003,0xA001,0x039F,0xA002,0x039E,0xA002,0x039D,0x039C,0xA003,0xA001,0x039A,0xA002,0x0399,0xA002,0x0398,
+ 0x0397,0xA00A,0xA004,0xA002,0x0396,0x0395,0xA002,0x0394,0xA002,0x0393,0x0392,0xA000,0xA000,0x0391,0xA001,0xA03E,
+ 0xA012,0xA001,0xA005,0xA001,0xA001,0xA001,0x30F6,0xA006,0xA002,0x30F5,0xA002,0x30F4,0x30F3,0xA002,0x30F2,0xA002,
+ 0x30F1,0x30F0,0xA016,0xA00A,0xA004,0xA002,0x30EF,0x30EE,0xA002,0x30ED,0xA002,0x30EC,0x30EB,0xA006,0xA002,0x30EA,
+ 0xA002,0x30E9,0x30E8,0xA002,0x30E7,0xA002,0x30E6,0x30E5,0xA00A,0xA004,0xA002,0x30E4,0x30E3,0xA002,0x30E2,0xA002,
+ 0x30E1,0x30E0,0xA006,0xA002,0x30DF,0xA002,0x30DE,0x30DD,0xA002,0x30DC,0xA002,0x30DB,0x30DA,0xA02C,0xA016,0xA00A,
+ 0xA004,0xA002,0x30D9,0x30D8,0xA002,0x30D7,0xA002,0x30D6,0x30D5,0xA006,0xA002,0x30D4,0xA002,0x30D3,0x30D2,0xA002,
+ 0x30D1,0xA002,0x30D0,0x30CF,0xA00A,0xA004,0xA002,0x30CE,0x30CD,0xA002,0x30CC,0xA002,0x30CB,0x30CA,0xA006,0xA002,
+ 0x30C9,0xA002,0x30C8,0x30C7,0xA002,0x30C6,0xA002,0x30C5,0x30C4,0xA016,0xA00A,0xA004,0xA002,0x30C3,0x30C2,0xA002,
+ 0x30C1,0xA002,0x30C0,0x30BF,0xA006,0xA002,0x30BE,0xA002,0x30BD,0x30BC,0xA002,0x30BB,0xA002,0x30BA,0x30B9,0xA00A,
+ 0xA004,0xA002,0x30B8,0x30B7,0xA002,0x30B6,0xA002,0x30B5,0x30B4,0xA006,0xA002,0x30B3,0xA002,0x30B2,0x30B1,0xA002,
+ 0x30B0,0xA002,0x30AF,0x30AE,0xA01F,0xA000,0xA000,0xA000,0xA014,0xA00A,0xA004,0xA002,0x30AD,0x30AC,0xA002,0x30AB,
+ 0xA002,0x30AA,0x30A9,0xA004,0xA002,0x30A8,0x30A7,0xA002,0x30A6,0xA002,0x30A5,0x30A4,0xA000,0xA004,0xA002,0x30A3,
+ 0x30A2,0xA000,0x30A1,0xA095,0xA03F,0xA013,0xA001,0xA006,0xA001,0xA001,0xA002,0x3093,0x3092,0xA006,0xA002,0x3091,
+ 0xA002,0x3090,0x308F,0xA002,0x308E,0xA002,0x308D,0x308C,0xA016,0xA00A,0xA004,0xA002,0x308B,0x308A,0xA002,0x3089,
+ 0xA002,0x3088,0x3087,0xA006,0xA002,0x3086,0xA002,0x3085,0x3084,0xA002,0x3083,0xA002,0x3082,0x3081,0xA00A,0xA004,
+ 0xA002,0x3080,0x307F,0xA002,0x307E,0xA002,0x307D,0x307C,0xA006,0xA002,0x307B,0xA002,0x307A,0x3079,0xA002,0x3078,
+ 0xA002,0x3077,0x3076,0xA02A,0xA014,0xA00A,0xA004,0xA002,0x3075,0x3074,0xA002,0x3073,0xA002,0x3072,0x3071,0xA004,
+ 0xA002,0x3070,0x306F,0xA002,0x306E,0xA002,0x306D,0x306C,0xA00A,0xA004,0xA002,0x306B,0x306A,0xA002,0x3069,0xA002,
+ 0x3068,0x3067,0xA006,0xA002,0x3066,0xA002,0x3065,0x3064,0xA002,0x3063,0xA002,0x3062,0x3061,0xA016,0xA00A,0xA004,
+ 0xA002,0x3060,0x305F,0xA002,0x305E,0xA002,0x305D,0x305C,0xA006,0xA002,0x305B,0xA002,0x305A,0x3059,0xA002,0x3058,
+ 0xA002,0x3057,0x3056,0xA00A,0xA004,0xA002,0x3055,0x3054,0xA002,0x3053,0xA002,0x3052,0x3051,0xA006,0xA002,0x3050,
+ 0xA002,0x304F,0x304E,0xA002,0x304D,0xA002,0x304C,0x304B,0xA000,0xA000,0xA000,0xA00A,0xA004,0xA002,0x304A,0x3049,
+ 0xA002,0x3048,0xA002,0x3047,0x3046,0xA004,0xA002,0x3045,0x3044,0xA002,0x3043,0xA002,0x3042,0x3041,0xA0CF,0xA0B3,
+ 0xA009,0xA001,0xA001,0xA001,0xA001,0xA001,0xA001,0xA001,0xFFE3,0xA056,0xA02A,0xA014,0xA00A,0xA004,0xA002,0xFF5D,
+ 0xFF5C,0xA002,0xFF5B,0xA002,0xFF5A,0xFF59,0xA004,0xA002,0xFF58,0xFF57,0xA002,0xFF56,0xA002,0xFF55,0xFF54,0xA00A,
+ 0xA004,0xA002,0xFF53,0xFF52,0xA002,0xFF51,0xA002,0xFF50,0xFF4F,0xA006,0xA002,0xFF4E,0xA002,0xFF4D,0xFF4C,0xA002,
+ 0xFF4B,0xA002,0xFF4A,0xFF49,0xA016,0xA00A,0xA004,0xA002,0xFF48,0xFF47,0xA002,0xFF46,0xA002,0xFF45,0xFF44,0xA006,
+ 0xA002,0xFF43,0xA002,0xFF42,0xFF41,0xA002,0xFF40,0xA002,0xFF3F,0xFF3E,0xA00A,0xA004,0xA002,0xFF3D,0xFF3C,0xA002,
+ 0xFF3B,0xA002,0xFF3A,0xFF39,0xA006,0xA002,0xFF38,0xA002,0xFF37,0xFF36,0xA002,0xFF35,0xA002,0xFF34,0xFF33,0xA02A,
+ 0xA014,0xA00A,0xA004,0xA002,0xFF32,0xFF31,0xA002,0xFF30,0xA002,0xFF2F,0xFF2E,0xA004,0xA002,0xFF2D,0xFF2C,0xA002,
+ 0xFF2B,0xA002,0xFF2A,0xFF29,0xA00A,0xA004,0xA002,0xFF28,0xFF27,0xA002,0xFF26,0xA002,0xFF25,0xFF24,0xA006,0xA002,
+ 0xFF23,0xA002,0xFF22,0xFF21,0xA002,0xFF20,0xA002,0xFF1F,0xFF1E,0xA015,0xA009,0xA004,0xA002,0xFF1D,0xFF1C,0xA002,
+ 0xFF1B,0xA001,0xFF19,0xA006,0xA002,0xFF18,0xA002,0xFF17,0xFF16,0xA002,0xFF15,0xA002,0xFF14,0xFF13,0xA00A,0xA004,
+ 0xA002,0xFF12,0xFF11,0xA002,0xFF10,0xA002,0xFF0F,0xFF0E,0xA005,0xA001,0xA002,0xFF0C,0xFF0B,0xA002,0xFF0A,0xA002,
+ 0xFF09,0xFF08,0xA012,0xA000,0xA000,0xA000,0xA00A,0xA004,0xA002,0xFF07,0xFF06,0xA002,0xFF05,0xA002,0xFFE5,0xFF03,
+ 0xA000,0xA002,0xFF02,0xFF01,0xA001,0xA001,0xA001,0xA001,0xA001,0xA001,0xA002,0x216B,0x216A,0xA092,0xA000,0xA051,
+ 0xA027,0xA014,0xA00A,0xA004,0xA002,0x2169,0x2168,0xA002,0x2167,0xA002,0x2166,0x2165,0xA004,0xA002,0x2164,0x2163,
+ 0xA002,0x2162,0xA002,0x2161,0x2160,0xA007,0xA001,0xA002,0x3229,0xA002,0x3228,0x3227,0xA006,0xA002,0x3226,0xA002,
+ 0x3225,0x3224,0xA002,0x3223,0xA002,0x3222,0x3221,0xA014,0xA008,0xA003,0xA000,0x3220,0xA001,0xA002,0x2469,0x2468,
+ 0xA006,0xA002,0x2467,0xA002,0x2466,0x2465,0xA002,0x2464,0xA002,0x2463,0x2462,0xA00A,0xA004,0xA002,0x2461,0x2460,
+ 0xA002,0x2487,0xA002,0x2486,0x2485,0xA006,0xA002,0x2484,0xA002,0x2483,0x2482,0xA002,0x2481,0xA002,0x2480,0x247F,
+ 0xA02A,0xA014,0xA00A,0xA004,0xA002,0x247E,0x247D,0xA002,0x247C,0xA002,0x247B,0x247A,0xA004,0xA002,0x2479,0x2478,
+ 0xA002,0x2477,0xA002,0x2476,0x2475,0xA00A,0xA004,0xA002,0x2474,0x249B,0xA002,0x249A,0xA002,0x2499,0x2498,0xA006,
+ 0xA002,0x2497,0xA002,0x2496,0x2495,0xA002,0x2494,0xA002,0x2493,0x2492,0xA000,0xA00A,0xA004,0xA002,0x2491,0x2490,
+ 0xA002,0x248F,0xA002,0x248E,0x248D,0xA006,0xA002,0x248C,0xA002,0x248B,0x248A,0xA002,0x2489,0xA000,0x2488,0xA014,
+ 0xA001,0xA001,0xA001,0xA005,0xA001,0xA001,0xA001,0x3013,0xA006,0xA002,0x2193,0xA002,0x2191,0x2190,0xA002,0x2192,
+ 0xA002,0x203B,0x25B2,0xA054,0xA029,0xA013,0xA009,0xA004,0xA002,0x25B3,0x25A0,0xA001,0xA002,0x25C6,0x25C7,0xA004,
+ 0xA002,0x25CE,0x25CF,0xA002,0x25CB,0xA002,0x2605,0x2606,0xA00A,0xA004,0xA002,0x2116,0x00A7,0xA002,0x2030,0xA002,
+ 0xFFE1,0xFFE0,0xA006,0xA002,0x00A4,0xA002,0xFF04,0x2103,0xA002,0x301E,0xA002,0x2032,0x00B0,0xA016,0xA00A,0xA004,
+ 0xA002,0x2640,0x2642,0xA002,0x2234,0xA002,0x2235,0x221E,0xA006,0xA002,0x2267,0xA002,0x2266,0x226F,0xA002,0x226E,
+ 0xA002,0x2260,0x221D,0xA00A,0xA004,0xA002,0x223D,0x2248,0xA002,0x2252,0xA002,0x2261,0x222E,0xA006,0xA002,0x222B,
+ 0xA002,0x2609,0x2312,0xA002,0x2220,0xA001,0x22A5,0xA02C,0xA016,0xA00A,0xA004,0xA002,0x221A,0x2237,0xA002,0x2208,
+ 0xA002,0x2229,0x222A,0xA006,0xA002,0x220F,0xA002,0x2211,0x2228,0xA002,0x2227,0xA002,0xFF1A,0x00F7,0xA00A,0xA004,
+ 0xA002,0x00D7,0x00B1,0xA002,0x3011,0xA002,0x3010,0x3017,0xA006,0xA002,0x3016,0xA002,0x300F,0x300E,0xA002,0x300D,
+ 0xA002,0x300C,0x300B,0xA016,0xA00A,0xA004,0xA002,0x300A,0x3009,0xA002,0x3008,0xA002,0x3015,0x3014,0xA006,0xA002,
+ 0x201D,0xA002,0x201C,0x2019,0xA002,0x2018,0xA002,0x2026,0x2225,0xA00A,0xA004,0xA002,0xFF5E,0xFF0D,0xA002,0x3005,
+ 0xA002,0x3003,0x2025,0xA006,0xA002,0x02C7,0xA002,0x02C9,0x30FB,0xA002,0x3002,0xA002,0x3001,0x3000,
+}; // gb2312_to_ucs2_elements
+
+const TConvFlatTree gb2312_to_ucs2 = {
+ 0xA1A1, // min key
+ 0xF7FE, // max key
+ 0xA000, // links space start
+ 0xEFFF, // links space end
+ gb2312_to_ucs2_numelems, // number of elements
+ (treeval_t *)&gb2312_to_ucs2_elements // elements
+}; // gb2312_to_ucs2
+
+
diff --git a/src/sysync/global_progress.h b/src/sysync/global_progress.h
new file mode 100755
index 0000000..39f3765
--- /dev/null
+++ b/src/sysync/global_progress.h
@@ -0,0 +1,83 @@
+/*
+ * Include file to allow SyncML RTK to use progress callback
+ *
+ * Copyright (c) 2003-2009 by Synthesis AG (www.synthesis.ch)
+ */
+
+#ifndef GLOBAL_PROGRESS_H
+#define GLOBAL_PROGRESS_H
+
+#include "generic_types.h"
+
+#ifdef __cplusplus
+namespace sysync {
+#endif
+
+
+// - progress event types
+typedef enum {
+ // global
+ pev_error, // some fatal aborting error
+ pev_message, // extra messages
+ pev_errcode, // extra error code
+ pev_nop, // no extra message, just called to allow aborting
+ pev_wait, // called to signal main program that caller would want to wait for extra1 milliseconds
+ pev_debug, // called to allow debug interactions, extra1=code
+ // transport-related
+ pev_sendstart,
+ pev_sendend,
+ pev_recvstart,
+ pev_recvend,
+ pev_ssl_expired, // expired
+ pev_ssl_notrust, // not completely trusted
+ pev_conncheck, // sent periodically when waiting for network, allows application to check connection
+ pev_suspendcheck, // sent when client could initiate a explicit suspend
+ // General
+ pev_display100, // alert 100 received from remote, extra1=char * to message text in UTF8
+ // Session-related
+ pev_sessionstart,
+ pev_sessionend, // session ended, probably with error in extra
+ // Datastore-related
+ pev_preparing, // preparing (e.g. preflight in some clients), extra1=progress, extra2=total
+ pev_deleting, // deleting (zapping datastore), extra1=progress, extra2=total
+ pev_alerted, // datastore alerted (extra1=0 for normal, 1 for slow, 2 for first time slow, extra2=1 for resumed session, extra3=syncmode)
+ pev_syncstart, // sync started
+ pev_itemreceived, // item received, extra1=current item count, extra2=number of expected changes (if >= 0)
+ pev_itemsent, // item sent, extra1=current item count, extra2=number of expected items to be sent (if >=0)
+ pev_itemprocessed, // item locally processed, extra1=# added, extra2=# updated, extra3=# deleted
+ pev_syncend, // sync finished, probably with error in extra1 (0=ok), syncmode in extra2 (0=normal, 1=slow, 2=first time), extra3=1 for resumed session)
+ pev_dsstats_l, // datastore statistics for local (extra1=# added, extra2=# updated, extra3=# deleted)
+ pev_dsstats_r, // datastore statistics for remote (extra1=# added, extra2=# updated, extra3=# deleted)
+ pev_dsstats_e, // datastore statistics for local/remote rejects (extra1=# locally rejected, extra2=# remotely rejected)
+ pev_dsstats_s, // datastore statistics for server slowsync (extra1=# slowsync matches)
+ pev_dsstats_c, // datastore statistics for server conflicts (extra1=# server won, extra2=# client won, extra3=# duplicated)
+ pev_dsstats_d, // datastore statistics for data volume (extra1=outgoing bytes, extra2=incoming bytes)
+ // number of enums
+ numProgressEventTypes
+} TProgressEventType;
+
+
+#ifndef ENGINE_LIBRARY
+
+// globally accessible progress event posting
+#ifdef __cplusplus
+extern "C" int GlobalNotifyProgressEvent
+#else
+extern int GlobalNotifyProgressEvent
+#endif
+(
+ TProgressEventType aEventType,
+ sInt32 aExtra1,
+ sInt32 aExtra2,
+ sInt32 aExtra3
+);
+
+#endif // not ENGINE_LIBRARY
+
+#ifdef __cplusplus
+} // namespace sysync
+#endif
+
+#endif // GLOBAL_PROGRESS_H
+
+/* eof */
diff --git a/src/sysync/iso8601.cpp b/src/sysync/iso8601.cpp
new file mode 100755
index 0000000..c0d096c
--- /dev/null
+++ b/src/sysync/iso8601.cpp
@@ -0,0 +1,383 @@
+/*
+ * File: iso8601.cpp
+ *
+ * Author: Lukas Zeller (luz@synthesis.ch)
+ *
+ * conversion from/to linear time scale.
+ *
+ * Copyright (c) 2002-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ * 2002-05-02 : luz : extracted from sysync_utils
+ *
+ */
+
+#include "prefix_file.h"
+
+#include "iso8601.h"
+#include "stringutils.h"
+#include "timezones.h"
+
+#if defined(EXPIRES_AFTER_DATE) && !defined(FULLY_STANDALONE)
+ // only if used in sysync context
+ #include "sysync.h"
+#endif
+
+
+namespace sysync {
+
+/// @brief convert ISO8601 to timestamp and timezone
+/// @return number of successfully converted characters or 0 if no valid ISO8601 specification could be decoded
+/// @param[in] aISOString input string in ISO8601
+/// @param[out] aTimestamp representation of ISO time spec as is (no time zone conversions)
+/// @param[out] aWithTime if set, time specification was found in input string
+/// @param[out] aTimeContext:
+/// TCTX_DURATION if ISO8601 is a duration format (PnDTnHnMnS.MS)
+/// TCTX_UNKNOWN if ISO8601 does not include a time zone specification
+/// TCTX_UTC if ISO8601 ends with the "Z" specifier
+/// TCTX_DATEONLY if ISO8601 only contains a date, but no time
+/// TCTX_OFFSCONTEXT(xx) if ISO8601 explicitly specifies a UTC offset
+sInt16 ISO8601StrToTimestamp(cAppCharP aISOString, lineartime_t &aTimestamp, timecontext_t &aTimeContext)
+{
+ uInt16 y,m,d,hr,mi,s,ms; // unsigned because these can't be signed when parsing
+ bool isExtended=false;
+ sInt16 n,h,sg;
+
+ n=0;
+ // check if it might be duration
+ sg=0; // duration sign
+ // - sign
+ if (*aISOString=='-') {
+ sg=-1; // negative duration
+ aISOString++; n++;
+ }
+ else if(*aISOString=='+') {
+ sg=1; // positive duration
+ aISOString++; n++;
+ }
+ if (*aISOString=='P') {
+ if (sg==0) sg=1;
+ aISOString++; n++;
+ }
+ else if (sg!=0)
+ return 0; // we had a sign, but no 'P' -> invalid ISO8601 date
+ // if y!=0 here, this is a duration
+ if (sg!=0) {
+ // duration
+ aTimestamp = 0;
+ aTimeContext = TCTX_UNKNOWN + TCTX_DURATION;
+ bool timepart = false;
+ // parse duration parts
+ while (true) {
+ if (*aISOString=='T' && !timepart) {
+ timepart = true;
+ aISOString++; n++;
+ }
+ #ifdef NO_FLOATS
+ long part;
+ h = StrToLong(aISOString,part);
+ #else
+ double part;
+ h = StrToDouble(aISOString,part);
+ #endif
+ if (h>0) {
+ aISOString+=h; n+=h;
+ // this is a part, must be followed by a designator
+ switch (*aISOString) {
+ case 'Y':
+ part*=365; goto days; // approximate year
+ case 'M': // month or minute
+ if (timepart) {
+ aTimestamp += (lineartime_t)(part*sg*secondToLinearTimeFactor*SecsPerMin); // minute
+ break;
+ }
+ else {
+ part*=30; // approximate month
+ goto days;
+ }
+ case 'W':
+ part *= 7; goto days; // one week
+ case 'D':
+ days:
+ aTimestamp += (lineartime_t)(part*sg*linearDateToTimeFactor);
+ break;
+ case 'H':
+ aTimestamp += (lineartime_t)(part*sg*secondToLinearTimeFactor*SecsPerHour);
+ break;
+ case 'S':
+ aTimestamp += (lineartime_t)(part*sg*secondToLinearTimeFactor);
+ break;
+ default:
+ return 0; // bad designator, error
+ }
+ aISOString++; n++;
+ } // if number
+ else {
+ // no more digits -> end of duration string
+ return n; // number of chars processed
+ }
+ } // while
+ } // if duration
+ // try to parse date
+ // - first should be 4 digit year
+ h = StrToUShort(aISOString,y,4);
+ if (h!=4) return 0; // no ISO8601 date
+ aISOString+=h; n+=h;
+ // - test for format
+ if (*aISOString=='-') {
+ isExtended=true;
+ aISOString++; n++;
+ }
+ // - next must be 2 digit month
+ h = StrToUShort(aISOString,m,2);
+ if (h!=2) return 0; // no ISO8601 date
+ aISOString+=h; n+=h;
+ // - check separator in case of extended format
+ if (isExtended) {
+ if (*aISOString != '-') return 0; // missing separator, no ISO8601 date
+ aISOString++; n++;
+ }
+ // - next must be 2 digit day
+ h = StrToUShort(aISOString,d,2);
+ if (h!=2) return 0; // no ISO8601 date
+ aISOString+=h; n+=h;
+ // convert date to timestamp
+ aTimestamp = date2lineartime(y,m,d);
+ // Now next must be "T" if time spec is included
+ if (*aISOString!='T') {
+ // date-only, floating
+ aTimeContext = TCTX_DATEONLY|TCTX_UNKNOWN;
+ }
+ else {
+ // parse time as well
+ aISOString++; n++; // skip "T"
+ mi=0; s=0; ms=0; // reset optional time components
+ // - next must be 2 digit hour
+ h = StrToUShort(aISOString,hr,2);
+ if (h!=2) return 0; // no ISO8601 time, we need the hour, minimally
+ aISOString+=h; n+=h;
+ // - check separator in case of extended format
+ if (isExtended) {
+ if (*aISOString != ':') return 0; // missing separator, no ISO8601 time (Note: hour-only reduced precision does not exist for extended format)
+ aISOString++; n++;
+ }
+ // - next must be 2 digit minute (or nothing for hour-only reduced precision basic format)
+ h = StrToUShort(aISOString,mi,2);
+ if (!isExtended && h==0) goto timeok;
+ if (h!=2) return 0; // no ISO8601 time, must be 2 digits here
+ aISOString+=h; n+=h;
+ // - check separator in case of extended format
+ if (isExtended) {
+ if (*aISOString != ':') goto timeok; // no separator means hour:minute reduced precision for extended format
+ aISOString++; n++;
+ }
+ // - next must be 2 digit second (or nothing for reduced precision without seconds)
+ h = StrToUShort(aISOString,s,2);
+ if (!isExtended && h==0) goto timeok; // no seconds is ok for basic format only (in extended format, the separator must be omitted as well, which is checked above)
+ if (h!=2) return 0; // no ISO8601 time, must be 2 digits here
+ aISOString+=h; n+=h;
+ // optional fractions of seconds
+ if (*aISOString=='.') {
+ aISOString++; n++;
+ h = StrToUShort(aISOString,ms,3);
+ if (h==0) return 0; // invalid fraction specified
+ aISOString+=h; n+=h;
+ while (h<3) { ms *= 10; h++; } // make milliseconds
+ }
+ timeok:
+ // add to timestamp
+ aTimestamp += time2lineartime(hr,mi,s,ms);
+ // check for zone specification
+ h = ISO8601StrToContext(aISOString,aTimeContext);
+ aISOString+=h; n+=h;
+ }
+ // return number of characters converted
+ return n;
+} // ISO8601StrToTimestamp
+
+
+
+
+/// @brief convert ISO8601 zone offset to internal time zone
+/// @return number of successfully converted characters or 0 if no valid ISO8601 time zone spec could be decoded
+/// @param[in] aISOString input string in ISO8601 time zone offset format (or just "Z" for UTC)
+/// @param[out] aTimeContext:
+/// TCTX_UNKNOWN if no time zone specification is found
+/// TCTX_UTC if "Z" specifier found
+/// TCTX_OFFSCONTEXT(xx) if explicit UTC offset is found
+sInt16 ISO8601StrToContext(cAppCharP aISOString, timecontext_t &aTimeContext)
+{
+ sInt16 n=0,h;
+ sInt16 minoffs;
+ uInt16 offs;
+
+ aTimeContext = TCTX_UNKNOWN;
+ bool western=false;
+ // check for UTC special case
+ if (*aISOString=='Z') {
+ n++;
+ aTimeContext = TCTX_UTC;
+ }
+ else {
+ // check for time zone offset
+ if (*aISOString!='+' && *aISOString!='-')
+ return 0; // error, nothing converted
+ western = *aISOString=='-'; // time is behind of UTC in the west
+ aISOString++;
+ n++;
+ h=StrToUShort(aISOString,offs,2);
+ if (h!=2)
+ return 0; // not +HH format with 2 digits, nothing converted
+ aISOString+=h; n+=h;
+ // make minutes
+ minoffs = offs*60;
+ // hour offset ok, now check minutes
+ if (*aISOString==':') { aISOString++; n++; }; // extended format
+ // get minutes, if any
+ h = StrToUShort(aISOString,offs,2);
+ if (h==2) {
+ // minute specified
+ minoffs += offs; // add minutes
+ }
+ // adjust sign
+ if (western)
+ minoffs=-minoffs; // western offset is negative
+ // return non-symbolic minute offset
+ aTimeContext = TCTX_OFFSCONTEXT(minoffs);
+ }
+ // return number of characters converted
+ return n;
+} // ISO8601StrToContext
+
+
+
+/// @brief convert timestamp to ISO8601 representation
+/// @param[out] aISOString will receive ISO8601 formatted date/time
+/// @param[in] aTimestamp representation of ISO time spec as is (no time zone conversions)
+/// @param[in] aExtFormat if set, extended format is generated
+/// @param[in] aWithFracSecs if set, fractional seconds are displayed (3 digits, milliseconds)
+/// @param[in] aTimeContext:
+/// TCTX_DURATION: timestamp is a duration and is shown in ISO8601 duration format (PnDTnHnMnS.MS)
+/// TCTX_UNKNOWN : time is shown as relative format (no "Z", no explicit offset)
+/// TCTX_TIMEONLY
+/// TCTX_UTC : time is shown with "Z" specifier
+/// TCTX_DATEONLY: only date part is shown (of date or duration)
+/// TCTX_OFFSCONTEXT(xx) : time is shown with explicit UTC offset (but not with "Z", even if offset is 0:00)
+void TimestampToISO8601Str(string &aISOString, lineartime_t aTimestamp, timecontext_t aTimeContext, bool aExtFormat, bool aWithFracSecs)
+{
+ // check for duration (should be rendered even if value is 0)
+ if (TCTX_IS_DURATION(aTimeContext)) {
+ // render as duration
+ // - sign
+ if (aTimestamp<0) {
+ aTimestamp=-aTimestamp;
+ aISOString = "-";
+ }
+ else {
+ aISOString.erase();
+ }
+ // - "P" duration designator
+ aISOString += "P";
+ // - days
+ lineardate_t days = aTimestamp / linearDateToTimeFactor;
+ if (days!=0) StringObjAppendPrintf(aISOString,"%ldD",(long)days);
+ // - time part if needed
+ if (!TCTX_IS_DATEONLY(aTimeContext) && lineartime2timeonly(aTimestamp)>(aWithFracSecs ? 0 : secondToLinearTimeFactor-1)) {
+ aISOString += "T"; // we have a time part
+ sInt16 h,m,s,ms;
+ lineartime2time(aTimestamp,&h,&m,&s,&ms);
+ if (h!=0) StringObjAppendPrintf(aISOString,"%hdH",h);
+ if (m!=0) StringObjAppendPrintf(aISOString,"%hdM",m);
+ if (aWithFracSecs && ms!=0) {
+ // with milliseconds, implies seconds as well, even if they are zero
+ StringObjAppendPrintf(aISOString,"%hd.%03hdS",s,ms);
+ }
+ else {
+ // no milliseconds, show seconds if not zero
+ if (s!=0) StringObjAppendPrintf(aISOString,"%hdS",s);
+ }
+ }
+ else if(days==0) {
+ aISOString="PT0S"; // no time part and no days, just display 0 seconds (and not negative, even if fraction might be)
+ }
+ // done
+ return;
+ }
+ // no timestamp, no string
+ if (aTimestamp==0) {
+ aISOString.erase();
+ return;
+ }
+ // Date if we want date
+ bool hasDate=false;
+ if (!TCTX_IS_TIMEONLY(aTimeContext)) {
+ // we want the date part
+ sInt16 y,m,d;
+ lineartime2date(aTimestamp,&y,&m,&d);
+ if (!TCTX_IS_DURATION(aTimeContext) || !(y==0 && m==0 && d==0)) {
+ if (aExtFormat)
+ StringObjPrintf(aISOString,"%04d-%02d-%02d",y,m,d); // Extended format
+ else
+ StringObjPrintf(aISOString,"%04d%02d%02d",y,m,d); // Basic format
+ hasDate=true;
+ }
+ }
+ else {
+ aISOString.erase();
+ }
+ // Add time if we want time
+ if (!TCTX_IS_DATEONLY(aTimeContext)) {
+ // we want the time part
+ // - add separator
+ if (hasDate) aISOString+='T';
+ // - now add the time
+ sInt16 h,m,s,ms;
+ lineartime2time(aTimestamp,&h,&m,&s,&ms);
+ if (aExtFormat)
+ StringObjAppendPrintf(aISOString,"%02hd:%02hd:%02hd",h,m,s); // Extended format
+ else
+ StringObjAppendPrintf(aISOString,"%02hd%02hd%02hd",h,m,s); // Basic format
+ // - add fractions of the second if selected and not 0
+ if (aWithFracSecs && (ms!=0)) {
+ StringObjAppendPrintf(aISOString,".%03hd",ms); // 3 decimal fraction digits for milliseconds
+ }
+ if (!TCTX_IS_DURATION(aTimeContext)) {
+ // add explicit time zone specification (or UTC "Z") if aTimecontext is a non-symbolic offset
+ ContextToISO8601StrAppend(aISOString, aTimeContext, aExtFormat);
+ }
+ }
+} // TimestampToISO8601Str
+
+
+
+/// @brief append internal time zone as ISO8601 zone offset to string
+/// @param[out] aISOString ISO8601 time zone spec will be appended to this string
+/// @param[in] aTimeContext
+/// @param[in] aExtFormat if set, extended format is generated
+bool ContextToISO8601StrAppend(string &aISOString, timecontext_t aTimeContext, bool aExtFormat)
+{
+ // check for UTC special case
+ if (TCTX_IS_UTC(aTimeContext)) {
+ aISOString += 'Z';
+ return true; // has time zone
+ }
+ // check if this is a resolved or a symbolic time zone
+ if (TCTX_IS_TZ(aTimeContext))
+ return false; // symbolic (includes unknown) - cannot append minute offset
+ // offset specified, show it
+ long moffs = TCTX_MINOFFSET(aTimeContext);
+ long hoffs = moffs / MinsPerHour;
+ moffs = abs( moffs % MinsPerHour );
+ StringObjAppendPrintf(aISOString, "%+03ld", hoffs);
+ if (moffs!=0 || aExtFormat) {
+ // minute specification required (always so for extended format)
+ if (aExtFormat)
+ aISOString+=':'; // add separator for extended format
+ StringObjAppendPrintf(aISOString, "%02ld", moffs);
+ }
+ return true; // has time zone
+} // ContextToISO8601StrAppend
+
+
+} // namespace sysync
+
+/* eof */
diff --git a/src/sysync/iso8601.h b/src/sysync/iso8601.h
new file mode 100755
index 0000000..f977709
--- /dev/null
+++ b/src/sysync/iso8601.h
@@ -0,0 +1,67 @@
+/*
+ * File: iso8601.h
+ *
+ * Author: Lukas Zeller (luz@synthesis.ch)
+ *
+ * conversion from/to linear time scale.
+ *
+ * Copyright (c) 2002-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ * 2002-05-02 : luz : extracted from sysync_utils
+ *
+ */
+
+#ifndef ISO8601_H
+#define ISO8601_H
+
+#include <string>
+#include "lineartime.h"
+#include "timezones.h"
+
+using namespace std;
+
+namespace sysync {
+
+/// @brief convert ISO8601 to timestamp
+/// @return number of successfully converted characters
+/// @param[in] aISOString input string in ISO8601
+/// @param[out] aTimestamp representation of ISO time spec as is (no time zone conversions)
+/// @param[out] aTimeContext:
+/// TCTX_UNKNOWN if ISO8601 does not include a time zone specification
+/// TCTX_UTC if ISO8601 ends with the "Z" specifier
+/// TCTX_DATEONLY if ISO8601 only contains a date, but no time
+/// TCTX_OFFSCONTEXT(xx) if ISO8601 explicitly specifies a UTC offset
+sInt16 ISO8601StrToTimestamp(cAppCharP aISOString, lineartime_t &aTimestamp, timecontext_t &aTimeContext);
+
+
+/// @brief convert ISO8601 zone offset to internal time context
+/// @param[in] aISOString input string in ISO8601
+/// @param[out] aTimeContext context, TCTX_UNKNOWN if none found
+sInt16 ISO8601StrToContext(cAppCharP aISOString, timecontext_t &aTimeContext);
+
+
+/// @brief convert timestamp to ISO8601 representation
+/// @param[out] aISOString will receive ISO8601 formatted date/time
+/// @param[in] aTimestamp representation of ISO time spec as is (no time zone conversions)
+/// @param[in] aExtFormat if set, extended format is generated
+/// @param[in] aTimeContext:
+/// TCTX_UNKNOWN : time is shown as relative format (no "Z", no explicit offset)
+/// TCTX_UTC : time is shown with "Z" specifier
+/// TCTX_DATEONLY: only date part is shown
+/// TCTX_OFFSCONTEXT(xx) : time is shown with explicit UTC offset (but not with "Z", even if offset is 0:00)
+/// @param[in] aWithFracSecs if set, factional parts of the second are shown in the output
+void TimestampToISO8601Str(string &aISOString, lineartime_t aTimestamp, timecontext_t aTimeContext, bool aExtFormat=false, bool aWithFracSecs=false);
+
+
+/// @brief append internal time context as ISO8601 zone offset to string
+/// @param[out] aISOString ISO8601 time zone spec will be appended to this string
+/// @param[in] aTimeContext
+/// @param[in] aExtFormat if set, extended format is generated
+/// @return true if time zone spec appended, false if not
+bool ContextToISO8601StrAppend(string &aISOString, timecontext_t aTimeContext, bool aExtFormat);
+
+} // namespace sysync
+
+#endif // ISO8601_H
+
+/* eof */
diff --git a/src/sysync/itemfield.cpp b/src/sysync/itemfield.cpp
new file mode 100755
index 0000000..06f8233
--- /dev/null
+++ b/src/sysync/itemfield.cpp
@@ -0,0 +1,2180 @@
+/*
+ * File: itemfield.cpp
+ *
+ * Author: Lukas Zeller (luz@synthesis.ch)
+ *
+ * TItemField
+ * Abstract class, holds a single field value
+ * TStringField, TIntegerField, TTimeStampField etc.
+ * Implementations of field types
+ *
+ * Copyright (c) 2001-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ * 2001-08-08 : luz : created
+ *
+ */
+
+// includes
+#include "prefix_file.h"
+#include "sysync.h"
+#include "itemfield.h"
+#include "multifielditemtype.h"
+
+#if defined(CHECKSUM_CHANGELOG) && !defined(RECORDHASH_FROM_DBAPI)
+#include "sysync_crc16.h"
+#endif
+
+using namespace sysync;
+
+
+namespace sysync {
+
+// type names
+cAppCharP const ItemFieldTypeNames[numFieldTypes] = {
+ "string",
+ "telephone",
+ "integer",
+ "timestamp",
+ "date",
+ "url",
+ "multiline",
+ "blob",
+ "none"
+};
+
+// corresponding SyncML devInf <DataType>
+const TPropDataTypes devInfPropTypes[numFieldTypes] = {
+ proptype_chr, // string -> Character
+ proptype_phonenum, // telephone -> Phone number
+ proptype_int, // integer -> Integer
+ proptype_datetime, // Timestamp -> Date and time of day
+ proptype_datetime, // Date -> Date and time of day
+ proptype_chr, // URL -> Character
+ proptype_text, // multiline -> plain text
+ proptype_bin, // BLOB -> Binary
+ proptype_unknown, // none -> unknown
+};
+
+
+
+/*
+ * Implementation of TItemField
+ */
+
+
+TItemField::TItemField() :
+ fAssigned(false) // unassigned at creation
+{
+} // TItemField::TItemField
+
+
+TItemField::~TItemField()
+{
+} // TItemField::~TItemField
+
+
+#ifdef SYDEBUG
+
+// show field contents as string for debug output
+size_t TItemField::StringObjFieldAppend(string &s, uInt16 aMaxStrLen)
+{
+ if (isUnassigned())
+ s+="<unassigned>";
+ else if (isEmpty())
+ s+="<empty>";
+ else
+ appendToString(s, aMaxStrLen);
+ return 0; // non-strings do not have a real size
+} // TItemField::StringObjFieldAppend
+
+#endif
+
+
+
+// append (default to appending string value of other field)
+void TItemField::append(TItemField &aItemField)
+{
+ string s;
+ aItemField.getAsString(s);
+ appendString(s);
+} // TItemField::append
+
+
+// generic cross-type assignment via string format
+TItemField& TItemField::operator=(TItemField &aItemField)
+{
+ string s;
+
+ if (aItemField.isUnassigned()) unAssign(); // copy unassigned status
+ else if (aItemField.isEmpty()) assignEmpty(); // copy empty status
+ else {
+ aItemField.getAsString(s);
+ setAsString(s);
+ }
+ return *this;
+} // TItemField::operator=
+
+
+// get any field as integer
+fieldinteger_t TItemField::getAsInteger(void)
+{
+ // convert to integer number
+ string s;
+ fieldinteger_t i;
+ getAsString(s);
+ if (!
+ #ifndef NO64BITINT
+ StrToLongLong(s.c_str(),i)
+ #else
+ StrToLong(s.c_str(),i)
+ #endif
+ )
+ i=0; // defined value
+ return i;
+} // TItemField::getAsInteger
+
+
+// get integer as numeric string
+void TItemField::setAsInteger(fieldinteger_t aInteger)
+{
+ string s;
+ #ifndef NO64BITINT
+ LONGLONGTOSTR(s,(long long)aInteger);
+ #else
+ StringObjPrintf(s,"%ld",(long)aInteger);
+ #endif
+ // set as string
+ setAsString(s);
+} // TItemField::setAsInteger
+
+
+// set as string, max number of chars = aLen
+void TItemField::setAsString(cAppCharP aString, size_t aLen)
+{
+ if (!aString)
+ assignEmpty();
+ else {
+ string t(aString,aLen);
+ // now call basic setter
+ setAsString(t.c_str());
+ }
+} // TItemField::setAsString
+
+
+size_t TItemField::getStringSize(void)
+{
+ if (getType()==fty_none) return 0; // empty/unassigned field has no size (avoid unneeded getAsString)
+ string s;
+ getAsString(s);
+ return s.size();
+} // TItemField::getStringSize
+
+
+bool TItemField::contains(TItemField &aItemField, bool aCaseInsensitive)
+{
+ string s;
+ aItemField.getAsString(s);
+ return findInString(s.c_str(), aCaseInsensitive)>=0;
+}
+
+
+#ifdef STREAMFIELD_SUPPORT
+
+// reset stream (start reading and/or writing at specified position)
+void TItemField::resetStream(size_t aPos)
+{
+ if (aPos==0)
+ fStreamPos=0; // optimized short-cut
+ else {
+ size_t sz=getStreamSize();
+ if (aPos>sz)
+ fStreamPos=sz;
+ else
+ fStreamPos=aPos;
+ }
+} // TItemField::resetStream
+
+
+// read from stream
+size_t TItemField::readStream(void *aBuffer, size_t aMaxBytes)
+{
+ string s;
+ getAsString(s);
+ size_t sz=s.size();
+ if (fStreamPos>sz) fStreamPos=sz;
+ if (fStreamPos+aMaxBytes > sz) aMaxBytes=sz-fStreamPos;
+ if (aMaxBytes>0) {
+ // copy memory
+ memcpy(aBuffer,s.c_str()+fStreamPos,aMaxBytes);
+ }
+ // return number of chars actually read
+ fStreamPos+=aMaxBytes;
+ return aMaxBytes;
+} // TItemField::readStream
+
+
+// write to stream
+size_t TItemField::writeStream(void *aBuffer, size_t aNumBytes)
+{
+ if (aNumBytes==0) return 0;
+ if (fStreamPos!=0) {
+ // not replacing entire contents, need read-modify-write of string
+ string s;
+ getAsString(s);
+ if (fStreamPos>s.size()) fStreamPos=s.size();
+ s.resize(fStreamPos);
+ s.append((cAppCharP)aBuffer,aNumBytes);
+ setAsString(s);
+ }
+ else {
+ setAsString((cAppCharP)aBuffer,aNumBytes);
+ }
+ fStreamPos+=aNumBytes;
+ return aNumBytes;
+} // TItemField::writeStream
+
+#endif
+
+
+/* end of TItemField implementation */
+
+
+#ifdef ARRAYFIELD_SUPPORT
+/*
+ * Implementation of TArrayField
+ */
+
+
+// constructor
+TArrayField::TArrayField(TItemFieldTypes aLeafFieldType)
+{
+ fLeafFieldType=aLeafFieldType;
+} // TArrayField::TArrayField
+
+
+// destructor
+TArrayField::~TArrayField()
+{
+ // make sure leaf fields are all gone
+ unAssign();
+} // TArrayField::~TArrayField
+
+
+#ifdef SYDEBUG
+
+// show field contents as string for debug output
+size_t TArrayField::StringObjFieldAppend(string &s, uInt16)
+{
+ if (!isAssigned())
+ return inherited::StringObjFieldAppend(s,0); // let ancestor show
+ StringObjAppendPrintf(s,"<array with %ld elements>",(long int)(arraySize()));
+ return 0;
+} // TArrayField::StringObjFieldAppend
+
+#endif
+
+
+// get field from array (creates new if index is larger than current array size)
+TItemField *TArrayField::getArrayField(sInt16 aArrIdx, bool aExistingOnly)
+{
+ TItemField *fldP = NULL;
+ // check index, negative index means array field itself
+ if (aArrIdx<0) return this;
+ // check if we have that field already
+ if (aArrIdx<arraySize()) {
+ // pointer array is large enough
+ fldP = fArray[aArrIdx];
+ if (fldP==NULL) {
+ // but element does not exist yet, create field for it
+ fldP=newItemField(fLeafFieldType,false);
+ fArray[aArrIdx]=fldP;
+ }
+ }
+ else if (aExistingOnly) {
+ // element does not exist and we want not to create new elements:
+ return NULL;
+ }
+ else {
+ // element does not exist yet, create new ones up to requested index
+ fArray.reserve(aArrIdx+1);
+ for (sInt16 idx=arraySize(); idx<=aArrIdx; idx++) {
+ fArray.push_back(NULL);
+ }
+ // actually create last field only
+ fldP=newItemField(fLeafFieldType,false);
+ fArray[aArrIdx]=fldP;
+ }
+ // return field
+ return fldP;
+} // TArrayField::getArrayField
+
+
+#if defined(CHECKSUM_CHANGELOG) && !defined(RECORDHASH_FROM_DBAPI)
+// calc CRC over all fields
+uInt16 TArrayField::getDataCRC(uInt16 crc)
+{
+ for (sInt16 idx=0; idx<arraySize(); idx++) {
+ crc=getArrayField(idx)->getDataCRC(crc);
+ }
+ return crc;
+} // TArrayField::getDataCRC
+#endif
+
+
+// clear all leaf fields
+void TArrayField::unAssign(void)
+{
+ for (sInt16 idx=0; idx<arraySize(); idx++) {
+ if (fArray[idx]) {
+ delete fArray[idx];
+ fArray[idx]=NULL;
+ }
+ }
+ // clear list now
+ fArray.clear();
+ // clear flag as well that could be set in case of an explicitly assigned empty array
+ fAssigned = false;
+} // TArrayField::unAssign
+
+
+// assign
+TItemField& TArrayField::operator=(TItemField &aItemField)
+{
+ // assignment of empty (could be EMPTY or UNASSIGNED) must be handled by base class
+ if (aItemField.isEmpty()) return TItemField::operator=(aItemField);
+ // handle array-to-array assignments
+ if (aItemField.isArray()) {
+ // delete my current contents
+ unAssign();
+ // copy leaf fields from other field
+ for (sInt16 idx=0; idx<aItemField.arraySize(); idx++) {
+ // assign array members
+ *(getArrayField(idx)) = *(((TItemField *)(&aItemField))->getArrayField(idx));
+ }
+ }
+ else {
+ // non-array (non-empty) assigned to array: just assign value to first element
+ unAssign();
+ *(getArrayField(0)) = aItemField;
+ }
+ return *this;
+} // TArrayField::operator=
+
+
+// append values (only assigned ones)
+// - if other field is an array, too, all elements of other array
+// will be appended at end of this array.
+// - if other field is a scalar, it's value will be appended to
+// the array.
+void TArrayField::append(TItemField &aItemField)
+{
+ if (aItemField.isArray()) {
+ // append elements of another array to this array
+ for (sInt16 idx=0; idx<aItemField.arraySize(); idx++) {
+ // append all array members to this array
+ // Note: calling append recursively!
+ append(*(aItemField.getArrayField(idx)));
+ }
+ }
+ else {
+ // append value of another field to this array
+ // - do not append unassigned fields
+ if (aItemField.isAssigned()) {
+ *(getArrayField(arraySize())) = aItemField;
+ }
+ }
+} // TArrayField::append
+
+
+
+// append string to array = append string as last element of array
+void TArrayField::appendString(cAppCharP aString, size_t aMaxChars)
+{
+ getArrayField(arraySize())->setAsString(aString,aMaxChars);
+} // TArrayField::appendString
+
+
+// contains for arrays means "contains in any of the elements"
+// (if aItemField is an array as well, this means that every element must be
+// contained somewhere in my own array)
+bool TArrayField::contains(TItemField &aItemField, bool aCaseInsensitive)
+{
+ bool contained = false;
+ if (aItemField.isArray()) {
+ contained=true;
+ // array: all array elements must be contained in at least one of my elements
+ for (sInt16 idx=0; idx<aItemField.arraySize(); idx++) {
+ if (!contains(*(aItemField.getArrayField(idx)),aCaseInsensitive)) {
+ // one of the elements of aItemField is not contained in myself -> not contained
+ contained = false;
+ break;
+ }
+ }
+ }
+ else {
+ // leaf element: must be contained in at least one of my elements
+ contained = false;
+ for (sInt16 idx=0; idx<arraySize(); idx++) {
+ if (getArrayField(idx)->contains(aItemField,aCaseInsensitive)) {
+ // the value of aItemField is contained in one of my elements -> contained
+ contained = true;
+ break;
+ }
+ }
+ }
+ return contained;
+} // TItemField::contains
+
+
+
+// compare: returns 0 if equal, 1 if this > aItem, -1 if this < aItem,
+// SYSYNC_NOT_COMPARABLE if not comparable at all or not equal and no ordering known
+// Note: array fields are only comparable with other array fields
+sInt16 TArrayField::compareWith(TItemField &aItemField, bool aCaseInsensitive)
+{
+ if (!aItemField.isArray()) return SYSYNC_NOT_COMPARABLE;
+ // get sizes of arrays
+ sInt16 mysz = arraySize();
+ sInt16 othersz = aItemField.arraySize();
+ sInt16 commonsz = mysz>othersz ? othersz : mysz;
+ // compare common array elements
+ for (sInt16 idx=0; idx<commonsz; idx++) {
+ sInt16 res = getArrayField(idx)->compareWith(*(aItemField.getArrayField(idx)), aCaseInsensitive);
+ if (res!=0) return res; // all non-equal return
+ }
+ // all compared fields are equal
+ if (mysz==othersz) return 0; // same size : equal
+ // larger array is greater
+ return mysz>othersz ? 1 : -1;
+} // TArrayField::compareWith
+
+/* end of TArrayField implementation */
+#endif
+
+
+
+/*
+ * Implementation of TStringField
+ */
+
+
+TStringField::TStringField()
+{
+ #ifdef STREAMFIELD_SUPPORT
+ fBlobProxyP=NULL;
+ #endif
+} // TStringField::TStringField
+
+
+TStringField::~TStringField()
+{
+ #ifdef STREAMFIELD_SUPPORT
+ // remove proxy if any
+ TBlobProxy::unlink(fBlobProxyP);
+ #endif
+} // TStringField::~TStringField
+
+
+#if defined(CHECKSUM_CHANGELOG) && !defined(RECORDHASH_FROM_DBAPI)
+
+// changelog support: calculate CRC over contents
+uInt16 TStringField::getDataCRC(uInt16 crc)
+{
+ // CRC over characters in the string
+ return sysync_crc16_block(getCStr(),getStringSize(),crc);
+} // TStringField::getDataCRC
+
+#endif
+
+
+// assignment
+TItemField& TStringField::operator=(TItemField &aItemField)
+{
+ DELETEPROXY; // forget old value and old proxy as well
+ // handle myself only if other is string field as well
+ if (aItemField.isBasedOn(fty_string)) {
+ // copy fields 1:1
+ const TStringField *sfP = static_cast<const TStringField *>(&aItemField);
+ fString=sfP->fString;
+ fAssigned=sfP->fAssigned;
+ #ifdef STREAMFIELD_SUPPORT
+ fBlobProxyP=sfP->fBlobProxyP; // copy proxy as well
+ if (fBlobProxyP) fBlobProxyP->link(); // link again, now both fields use the proxy
+ #endif
+ }
+ else
+ TItemField::operator=(aItemField); // generic cross-type assignment (via string)
+ return *this;
+} // TStringField::operator=
+
+
+// get size of string
+size_t TStringField::getStringSize(void)
+{
+ #ifdef STREAMFIELD_SUPPORT
+ return getStreamSize();
+ #else
+ return fString.size();
+ #endif
+} // TStringField::getStringSize
+
+
+// normalized string is contents without leading or trailing control chars or spaces
+void TStringField::getAsNormalizedString(string &aString)
+{
+ size_t nsiz = getStringSize();
+ // we need the actual string to do this
+ PULLFROMPROXY;
+ // find first non-control or non-WSP char
+ size_t start = 0;
+ while (start<nsiz && ((uInt8)fString[start])<=' ') start++;
+ // find last non-control or non-WSP char
+ while (nsiz>0 && ((uInt8)fString[nsiz-1])<=' ') nsiz--;
+ // take string without any leading or trailing white space or other control chars
+ aString.assign(fString,start,nsiz-start);
+} // TStringField::getAsNormalizedString
+
+
+
+
+#ifdef SYDEBUG
+
+// show field contents as string for debug output
+size_t TStringField::StringObjFieldAppend(string &s, uInt16 aMaxStrLen)
+{
+ size_t n = 0;
+ // empty or unassigned is handled by base class
+ if (isEmpty()) { return inherited::StringObjFieldAppend(s,aMaxStrLen); }
+ // with proxy installed, do not pull the string
+ if (PROXYINSTALLED) {
+ s+="<proxy installed>";
+ n=0; // unknown size at this time
+ }
+ else {
+ // no proxy installed
+ s+='"';
+ // - check if display length is sufficient
+ size_t i;
+ n = getStringSize();
+ if (aMaxStrLen==0 || n<=10 || n<=size_t(aMaxStrLen-2)) {
+ // strings below 11 chars are always shown in full
+ s.append(fString);
+ }
+ else {
+ i = (aMaxStrLen-5)/2; // half of the size that can be displayed
+ s.append(fString,0,i);
+ s.append("...");
+ s.append(fString,n-i,i);
+ }
+ s+='"';
+ }
+ // return actual string size (not shortened one!)
+ return n;
+} // TStringField::StringObjFieldAppend
+
+#endif
+
+
+
+
+#ifdef STREAMFIELD_SUPPORT
+
+// set blob loader proxy (ownership is passed to field)
+void TStringField::setBlobProxy(TBlobProxy *aBlobProxyP)
+{
+ if (fBlobProxyP==aBlobProxyP) return; // same proxy, just ignore
+ // unlink previous proxy, if any
+ TBlobProxy::unlink(fBlobProxyP);
+ // assign new proxy
+ fBlobProxyP=aBlobProxyP;
+ // assigning a proxy means that the field is now assigned
+ fAssigned=true;
+} // TStringField::setBlobProxy
+
+
+// pull entire string from proxy and forget proxy
+void TStringField::pullFromProxy(void)
+{
+ if (fBlobProxyP) {
+ const size_t bufsiz=2048;
+ cAppCharP bufP = new char[bufsiz];
+ resetStream();
+ size_t by;
+ SYSYNC_TRY {
+ do {
+ by=fBlobProxyP->readBlobStream(this, fStreamPos, (void *)bufP, bufsiz);
+ fString.append(bufP,by);
+ } while (by==bufsiz);
+ }
+ SYSYNC_CATCH(exception &e)
+ // avoid crashing session if proxy pull fails
+ fString="Server error while getting data from proxy object: ";
+ fString+=e.what();
+ SYSYNC_ENDCATCH
+ delete bufP;
+ // proxy is no longer needed
+ TBlobProxy::unlink(fBlobProxyP);
+ }
+} // TStringField::pullFromProxy
+
+
+// return size of stream
+size_t TStringField::getStreamSize(void)
+{
+ if (fBlobProxyP)
+ SYSYNC_TRY {
+ return fBlobProxyP->getBlobSize(this); // return size of entire blob
+ }
+ SYSYNC_CATCH(exception &e)
+ return 0; // cannot return actual size
+ SYSYNC_ENDCATCH
+ else
+ return fString.size(); // just return size of already stored string
+} // TStringField::getStreamSize
+
+
+// read as stream
+size_t TStringField::readStream(void *aBuffer, size_t aMaxBytes)
+{
+ if (fBlobProxyP) {
+ SYSYNC_TRY {
+ // let proxy handle this
+ return fBlobProxyP->readBlobStream(this, fStreamPos, aBuffer, aMaxBytes);
+ }
+ SYSYNC_CATCH(...)
+ // do not return anything
+ return 0; // do not return anything
+ SYSYNC_ENDCATCH
+ }
+ else {
+ // read from string itself
+ size_t sz=fString.size();
+ if (fStreamPos>sz) fStreamPos=sz;
+ if (fStreamPos+aMaxBytes > sz) aMaxBytes=sz-fStreamPos;
+ if (aMaxBytes>0) {
+ // copy memory
+ memcpy(aBuffer,fString.c_str()+fStreamPos,aMaxBytes);
+ }
+ // return number of chars actually read
+ fStreamPos+=aMaxBytes;
+ return aMaxBytes;
+ }
+} // TStringField::readStream
+
+
+// write as stream
+size_t TStringField::writeStream(void *aBuffer, size_t aNumBytes)
+{
+ if (aNumBytes==0) return 0;
+ if (fStreamPos>fString.size()) fStreamPos=fString.size();
+ fString.resize(fStreamPos);
+ fString.append((cAppCharP)aBuffer,aNumBytes);
+ fStreamPos+=aNumBytes;
+ return aNumBytes;
+} // TStringField::writeStream
+
+#endif
+
+
+
+// set as string, max number of chars to assign = aLen
+void TStringField::setAsString(cAppCharP aString, size_t aLen)
+{
+ DELETEPROXY; // forget old value and old proxy as well
+ if (!aString)
+ fString.erase();
+ else
+ fString.assign(aString,aLen);
+ stringWasAssigned();
+} // TStringField::setAsString
+
+
+// append to string
+void TStringField::appendString(cAppCharP aString, size_t aMaxChars)
+{
+ PULLFROMPROXY; // make sure we have all chars
+ if (aString) {
+ fString.append(aString,aMaxChars);
+ }
+ stringWasAssigned();
+} // TStringField::appendString
+
+
+// merge field contents into this field
+bool TStringField::merge(TItemField &aItemField, const char aSep)
+{
+ bool mergedsomething=false;
+
+ if (aItemField.isBasedOn(fty_string)) {
+ TStringField *sfP = static_cast<TStringField *>(&aItemField);
+ #ifdef STREAMFIELD_SUPPORT
+ pullFromProxy(); // make sure we have all chars
+ sfP->pullFromProxy(); // make sure we have all chars
+ #endif
+ if (aSep) {
+ // take each aSep separated part of source (aItemField), and if it is not
+ // yet part of target (this), then add it
+ string::size_type j,i=0;
+ string part;
+ do {
+ // extract part from source
+ j=sfP->fString.find(aSep,i);
+ if (j==string::npos)
+ part.assign(sfP->fString,i,sfP->fString.size()-i);
+ else
+ part.assign(sfP->fString,i,j-i);
+ // see if it is contained in target already
+ if (!part.empty() && fString.find(part)==string::npos) {
+ // not contained, add
+ if (!fString.empty()) fString+=aSep;
+ inherited::appendString(part);
+ mergedsomething=true;
+ }
+ // check next part
+ i=j+1; // char after last separator
+ } while (j!=string::npos);
+ }
+ else {
+ // no intelligent separator based merge, just append if not equal
+ if (sfP->fString != fString) {
+ inherited::appendString(sfP->fString);
+ mergedsomething=true;
+ }
+ }
+ }
+ return mergedsomething;
+} // TStringField::merge
+
+
+// compare: returns 0 if equal, 1 if this > aItem, -1 if this < aItem,
+// SYSYNC_NOT_COMPARABLE if not equal and no ordering known or if field
+// types do not match.
+// Note: ordering may NOT be age relevant; it just means that an ordering
+// for this field type exists.
+// Note: Both fields must be assigned. NO TEST IS DONE HERE!
+sInt16 TStringField::compareWith(TItemField &aItemField, bool aCaseInsensitive)
+{
+ sInt16 result;
+ PULLFROMPROXY;
+ if (aItemField.isBasedOn(fty_string)) {
+ TStringField *sfP = static_cast<TStringField *>(&aItemField);
+ #ifdef STREAMFIELD_SUPPORT
+ sfP->pullFromProxy(); // make sure we have all chars
+ #endif
+ // direct compare possible, return strcmp
+ if (aCaseInsensitive)
+ result=strucmp(fString.c_str(),sfP->fString.c_str());
+ else
+ result=strcmp(fString.c_str(),sfP->fString.c_str());
+ }
+ else {
+ // convert other field to string
+ string s;
+ aItemField.getAsString(s);
+ if (aCaseInsensitive)
+ result=strucmp(fString.c_str(),s.c_str());
+ else
+ result=strcmp(fString.c_str(),s.c_str());
+ }
+ return result >0 ? 1 : (result<0 ? -1 : 0);
+} // TStringField::compareWith
+
+
+// check if specified field is shortened version of this one
+bool TStringField::isShortVers(TItemField &aItemField, sInt32 aOthersMax)
+{
+ if (!aItemField.isBasedOn(fty_string)) return false; // different types
+ TStringField *sfP = static_cast<TStringField *>(&aItemField);
+ #ifdef STREAMFIELD_SUPPORT
+ pullFromProxy(); // make sure we have all chars
+ sfP->pullFromProxy(); // make sure we have all chars
+ #endif
+ // same type
+ // - if other field is empty, it does not count as a shortened version in any case
+ if (sfP->isEmpty()) return false;
+ // - show if other field is shortened version of this one
+ return (
+ aOthersMax!=FIELD_OPT_MAXSIZE_NONE && // other field is limited (if not, cannot be short version)
+ (aOthersMax==FIELD_OPT_MAXSIZE_UNKNOWN || uInt32(aOthersMax)==sfP->getStringSize()) && // size is unknown or other value is max size
+ findInString(sfP->fString.c_str())==0 // other value is contained at beginning of this value
+ );
+} // TStringField::isShortVers
+
+
+// - check if String is contained in value and returns position
+sInt16 TStringField::findInString(cAppCharP aString, bool aCaseInsensitive)
+{
+ PULLFROMPROXY;
+ if (aString==NULL || *aString==0)
+ return fString.empty() ? 0 : -1; // if I am empty myself,treat empty string as contained at beginning
+ // no empty reference string
+ if (!aCaseInsensitive) {
+ return fString.find(aString);
+ }
+ else {
+ return strupos(fString.c_str(),aString,fString.size());
+ }
+};
+
+
+/* end of TStringField implementation */
+
+
+/*
+ * Implementation of TBlobField
+ */
+
+
+TBlobField::TBlobField()
+{
+ // nothing known about contents yet
+ fHasEncoding = enc_none;
+ fWantsEncoding = enc_none;
+ fCharset = chs_utf8;
+} // TBlobField::TBlobField
+
+
+TBlobField::~TBlobField()
+{
+} // TBlobField::~TBlobField
+
+
+// assignment
+TItemField& TBlobField::operator=(TItemField &aItemField)
+{
+ DELETEPROXY; // forget old value and old proxy as well
+ // assignment of empty (could be EMPTY or UNASSIGNED) must be handled by base class
+ if (aItemField.isEmpty()) return TItemField::operator=(aItemField);
+ // handle non-empty myself if other is based on string (blob is just a enhanced string)
+ if (aItemField.isBasedOn(fty_string)) {
+ // assign string portions
+ TStringField::operator=(aItemField);
+ if(aItemField.isBasedOn(fty_blob)) {
+ // other is a blob, copy blob specific options
+ const TBlobField *bfP = static_cast<const TBlobField *>(&aItemField);
+ fHasEncoding=bfP->fHasEncoding;
+ fWantsEncoding=bfP->fWantsEncoding;
+ fCharset=bfP->fCharset;
+ }
+ else {
+ // reset blob options to default for string content
+ fHasEncoding = enc_none;
+ fWantsEncoding = enc_none;
+ fCharset = chs_utf8;
+ }
+ }
+ else
+ TItemField::operator=(aItemField); // generic cross-type assignment (via string)
+ return *this;
+} // TBlobField::operator=
+
+
+
+#ifdef SYDEBUG
+
+// debug support
+size_t TBlobField::StringObjFieldAppend(string &s, uInt16 aMaxStrLen)
+{
+ // empty or unassigned is handled by base class
+ if (isEmpty()) { return inherited::StringObjFieldAppend(s, aMaxStrLen); }
+ // avoid pulling a proxy for debug
+ if (!PROXYINSTALLED) {
+ appendToString(s); // show standard string which is pseudo-content
+ return getStringSize(); // return actual size of BLOB
+ }
+ else
+ return inherited::StringObjFieldAppend(s, aMaxStrLen); // with proxy installed, base class will show that
+} // TBlobField::StringObjFieldAppend
+
+#endif // SYDEBUG
+
+
+/* end of TBlobField implementation */
+
+
+
+/*
+ * Implementation of TTelephoneField
+ */
+
+
+TTelephoneField::TTelephoneField()
+{
+} // TTelephoneField::TTelephoneField
+
+
+TTelephoneField::~TTelephoneField()
+{
+} // TTelephoneField::~TTelephoneField
+
+
+void TTelephoneField::getAsNormalizedString(string &aString)
+{
+ cAppCharP p = getCStr();
+ char c;
+ aString.erase();
+ // only returns alphanum and +,*,#, rest is filtered out
+ while ((c=*p++)!=0) {
+ if (isalnum(c) || c=='+' || c=='#' || c=='*') aString+=c;
+ }
+} // TTelephoneField::getAsNormalizedString
+
+
+// compare: returns 0 if equal, 1 if this > aItem, -1 if this < aItem,
+// SYSYNC_NOT_COMPARABLE if not equal and no ordering known or if field
+// types do not match.
+// Note: ordering may NOT be age relevant; it just means that an ordering
+// for this field type exists.
+// Note: Both fields must be assigned. NO TEST IS DONE HERE!
+sInt16 TTelephoneField::compareWith(TItemField &aItemField, bool aCaseInsensitive)
+{
+ if (!aItemField.isBasedOn(fty_telephone)) {
+ // if other field is not telephone, try normal string comparison
+ return TStringField::compareWith(aItemField, aCaseInsensitive);
+ }
+ // compare possible, return strcmp of normalized numbers
+ TTelephoneField *tfP = static_cast<TTelephoneField *>(&aItemField);
+ string s1,s2;
+ getAsNormalizedString(s1);
+ tfP->getAsNormalizedString(s2);
+ if (s1==s2) return 0; // equal
+ else return SYSYNC_NOT_COMPARABLE; // not equal, ordering makes no sense for tel numbers
+} // TTelephoneField::compareWith
+
+
+/* end of TTelephoneField implementation */
+
+
+/*
+ * Implementation of TTelephoneField
+ */
+
+TMultilineField::TMultilineField()
+{
+} // TMultilineField::TMultilineField
+
+
+TMultilineField::~TMultilineField()
+{
+} // TMultilineField::~TMultilineField
+
+
+// compare: returns 0 if equal, 1 if this > aItem, -1 if this < aItem,
+// SYSYNC_NOT_COMPARABLE if not equal and no ordering known or if field
+// types do not match.
+// Note: ordering may NOT be age relevant; it just means that an ordering
+// for this field type exists.
+// Note: Both fields must be assigned. NO TEST IS DONE HERE!
+sInt16 TMultilineField::compareWith(TItemField &aItemField, bool aCaseInsensitive)
+{
+ if (!aItemField.isBasedOn(fty_multiline)) {
+ // if other field is not multiline, try normal string comparison
+ return TStringField::compareWith(aItemField);
+ }
+ // compare possible, return strcmp of normalized texts
+ TMultilineField *mfP = static_cast<TMultilineField *>(&aItemField);
+ string s1,s2;
+ getAsNormalizedString(s1);
+ mfP->getAsNormalizedString(s2);
+ // compare
+ sInt8 result;
+ if (aCaseInsensitive)
+ result=strucmp(s1.c_str(),s2.c_str());
+ else
+ result=strcmp(s1.c_str(),s2.c_str());
+ return result >0 ? 1 : (result<0 ? -1 : 0);
+} // TMultilineField::compareWith
+
+
+/* end of TMultilineField implementation */
+
+/*
+ * Implementation of TURLField
+ */
+
+TURLField::TURLField()
+{
+} // TURLField::TURLField
+
+
+TURLField::~TURLField()
+{
+} // TURLField::~TURLField
+
+
+
+void TURLField::stringWasAssigned(void)
+{
+ // post-process string that was just assigned
+ string proto;
+ if (!fString.empty()) {
+ // make sure we have a URL with protocol
+ splitURL(fString.c_str() ,&proto, NULL, NULL, NULL, NULL);
+ if (proto.empty()) {
+ // no protocol set, but string not empty --> assume http
+ fString.insert(0, "http://");
+ }
+ }
+ inherited::stringWasAssigned();
+} // TURLField::stringWasAssigned
+
+
+/*%%% old, was not called with all variants of setAsString()
+// must check and update URL on write
+void TURLField::setAsString(cAppCharP aString)
+{
+ string proto;
+
+ if (!aString) {
+ fString.erase();
+ }
+ else {
+ splitURL(aString,&proto,NULL,NULL,NULL,NULL);
+ if (proto.empty() && *aString!=0) {
+ // no protocol set, but string not empty --> assume http
+ fString="http://";
+ fString+=aString;
+ }
+ else {
+ // protocol is there (or empty string), assume ok
+ fString=aString;
+ }
+ }
+ fAssigned=true;
+} // TURLField::setAsString
+*/
+
+/* end of TURLField implementation */
+
+
+
+/*
+ * Implementation of TTimestampField
+ */
+
+
+TTimestampField::TTimestampField(GZones *aGZonesP)
+{
+ fGZonesP = aGZonesP;
+ fTimestamp=0;
+ fTimecontext=TCTX_UNKNOWN;
+} // TTimestampField::TTimestampField
+
+
+TTimestampField::~TTimestampField()
+{
+} // TTimestampField::~TTimestampField
+
+
+#if defined(CHECKSUM_CHANGELOG) && !defined(RECORDHASH_FROM_DBAPI)
+
+// changelog support: calculate CRC over contents
+uInt16 TTimestampField::getDataCRC(uInt16 crc)
+{
+ // calculate CRC such that only a effective change in the time zone will cause a
+ // CRC change: TCTX_SYSTEM must be converted to UTC as changing the system time zone
+ // should not re-sync the entire calendar
+ lineartime_t crcts = fTimestamp;
+ timecontext_t crcctx = fTimecontext;
+ if (TCTX_IS_SYSTEM(crcctx)) {
+ if (TzConvertTimestamp(crcts,crcctx,TCTX_UTC,fGZonesP))
+ crcctx=TCTX_UTC; // use UTC representation for CRC (= same as in 3.0)
+ }
+ // try to avoid re-sync when upgrading from 3.0 to 3.1
+ if (TCTX_IS_UTC(crcctx)) crcctx=0; // this is what we had in 3.0 for non-floating timestamps
+ // CRC over timestamp itself and zone offset
+ crc=sysync_crc16_block(&crcts,sizeof(crcts),crc);
+ return sysync_crc16_block(&crcctx,sizeof(crcctx),crc);
+} // TTimestampField::getDataCRC
+
+#endif
+
+
+// assignment
+TItemField& TTimestampField::operator=(TItemField &aItemField)
+{
+ // assignment of empty (could be EMPTY or UNASSIGNED) must be handled by base class
+ if (aItemField.isEmpty()) return TItemField::operator=(aItemField);
+ // handle non-empty myself
+ if (aItemField.isBasedOn(fty_timestamp)) {
+ const TTimestampField *sfP = static_cast<const TTimestampField *>(&aItemField);
+ fTimestamp=sfP->fTimestamp;
+ fTimecontext=sfP->fTimecontext;
+ fAssigned=sfP->fAssigned;
+ }
+ else if (aItemField.getCalcType()==fty_integer) {
+ setAsInteger(aItemField.getAsInteger());
+ }
+ else
+ TItemField::operator=(aItemField); // generic cross-type assignment (via string)
+ return *this;
+} // TTimestampField::operator=
+
+
+// compare: returns 0 if equal, 1 if this > aItem, -1 if this < aItem,
+// SYSYNC_NOT_COMPARABLE if not equal and no ordering known or if field
+// types do not match.
+// Note: ordering may NOT be age relevant; it just means that an ordering
+// for this field type exists.
+// Note: Both fields must be assigned. NO TEST IS DONE HERE!
+sInt16 TTimestampField::compareWith(TItemField &aItemField, bool aCaseInsensitive)
+{
+ sInt16 res;
+ lineartime_t cmpval;
+ timecontext_t cmpcontext;
+
+ // determine value to compare with
+ if (aItemField.isBasedOn(fty_timestamp)) {
+ cmpval=static_cast<TTimestampField *>(&aItemField)->fTimestamp;
+ cmpcontext=static_cast<TTimestampField *>(&aItemField)->fTimecontext;
+ }
+ else {
+ // not same type
+ if (aItemField.getCalcType()==fty_integer) {
+ // use second argument as lineartime units number
+ cmpval=aItemField.getAsInteger();
+ cmpcontext=fTimecontext; // treat number as timestamp value in my own context
+ }
+ else {
+ // use second argument as ISO8601 string
+ string s;
+ aItemField.getAsString(s);
+ ISO8601StrToTimestamp(s.c_str(), cmpval, cmpcontext);
+ if (TCTX_IS_UNKNOWN(cmpcontext)) cmpcontext=fTimecontext; // treat unqualified ISO timestamp in my own context
+ }
+ }
+ // convert compare value into my own context (if not already so)
+ if (!TzConvertTimestamp(cmpval,cmpcontext,fTimecontext,fGZonesP))
+ return SYSYNC_NOT_COMPARABLE; // contexts are not compatible to compare
+ // compare possible, return strcmp-type result
+ res=fTimestamp==cmpval ? 0 : (fTimestamp>cmpval ? 1 : -1);
+ if (res!=0) {
+ // greater/less-type result is valid only if both sides are not empty
+ if (fTimestamp==0) return SYSYNC_NOT_COMPARABLE; // empty value, cannot compare
+ if (cmpval==0) return SYSYNC_NOT_COMPARABLE; // empty value, cannot compare
+ }
+ return res;
+} // TTimestampField::compareWith
+
+
+#ifdef SYDEBUG
+
+// debug support
+size_t TTimestampField::StringObjFieldAppend(string &s, uInt16 aMaxStrLen)
+{
+ // empty or unassigned is handled by base class
+ if (isEmpty()) { return inherited::StringObjFieldAppend(s, aMaxStrLen); }
+
+ string str;
+ timecontext_t tctx;
+ TimestampToISO8601Str(str,fTimestamp,fTimecontext,true,true);
+ s.append(str);
+ tctx=fTimecontext;
+ if (TCTX_IS_TZ(tctx)) {
+ // symbolic time zone not represented in ISO8601, show extra
+ s.append(" (");
+ if (TCTX_IS_UNKNOWN(tctx)) {
+ s.append("floating");
+ }
+ else {
+ if (TCTX_IS_SYSTEM(tctx)) {
+ // is the system zone
+ s.append("System ");
+ // resolve from meta to symbolic zone
+ TzResolveMetaContext(tctx,fGZonesP);
+ }
+ if (!TCTX_IS_BUILTIN(tctx)) {
+ s.append("imported ");
+ }
+ s.append("TZ: ");
+ // show time zone name
+ TimeZoneContextToName(tctx,str,fGZonesP);
+ s.append(str);
+ }
+ s.append(")");
+ }
+ // size not known
+ return 0;
+} // TTimestampField::StringObjFieldAppend
+
+#endif // SYDEBUG
+
+
+// set timestamp from ISO8601 string
+void TTimestampField::setAsString(cAppCharP aString)
+{
+ // - set context if contained in ISO string, otherwise leave it as TCTX_UNKNOWN
+ setAsISO8601(aString, TCTX_UNKNOWN, false);
+} // TTimestampField::setAsString
+
+
+// get timestamp as ISO8601 string (default representation)
+void TTimestampField::getAsString(string &aString)
+{
+ // use default rendering
+ // - show with current context, show UTC with Z but other zones without zone specifier
+ getAsISO8601(aString,TCTX_UNKNOWN,true,false,false,false);
+} // getAsString
+
+
+
+
+/// @brief add a delta time to the timestamp
+/// @param aDeltaTime[in] : delta time value in lineartime_t units
+void TTimestampField::addTime(lineartime_t aDeltaTime)
+{
+ fTimestamp=fTimestamp+aDeltaTime;
+ fAssigned=true;
+} // TTimestampField::addTime
+
+
+/// @brief get time context
+/// @return minute offset east of UTC, returns 0 for floating timestamps (and UTC, of course)
+sInt16 TTimestampField::getMinuteOffset(void)
+{
+ sInt16 moffs;
+
+ if (TzResolveToOffset(fTimecontext, moffs, fTimestamp, false, fGZonesP))
+ return moffs; // found offset
+ else
+ return 0; // no offset (e.g. floating timestamp)
+} // TTimestampField::getMinuteOffset
+
+
+/// @brief test for floating time (=time not in a specified zone context)
+/// @return true if context is TCTX_UNKNOWN
+bool TTimestampField::isFloating(void)
+{
+ return TCTX_IS_UNKNOWN(fTimecontext);
+} // TTimestampField::isFloating
+
+
+/// @brief make timestamp floating (i.e. remove time zone info from context)
+void TTimestampField::makeFloating(void)
+{
+ // rendering flags from original context, new zone is UNKNOWN (=floating)
+ fTimecontext = TCTX_JOIN_RFLAGS_TZ(fTimecontext, TCTX_UNKNOWN);
+} // TTimestampField::makeFloating
+
+
+/// @brief test for duration
+/// @return true if context has TCTX_DURATION rendering flag set
+bool TTimestampField::isDuration(void)
+{
+ return TCTX_IS_DURATION(fTimecontext);
+} // TTimestampField::isDuration
+
+
+/// @brief make timestamp a duration (also implies making it floating)
+void TTimestampField::makeDuration(void)
+{
+ // rendering flags from original context, new zone is UNKNOWN (=floating)
+ fTimecontext |= TCTX_DURATION;
+ makeFloating();
+} // TTimestampField::makeDuration
+
+
+/// @brief get timestamp converted to a specified time context
+/// @param aTargetContext[in] : requests output context for timestamp.
+/// Use TCTX_UNKNOWN to get timestamp as-is.
+/// If timestamp is floating, it will always be returned as-is
+/// @param aActualContext[out] : if not NULL, the actual context of the returned value
+/// will be returned here. This might be another
+// context than specified with aTargetContext depending on floating/notime status.
+/// @return timestamp in lineartime
+lineartime_t TTimestampField::getTimestampAs(timecontext_t aTargetContext, timecontext_t *aActualContext)
+{
+ // default context is that of the field (will be overwritten if result is moved to another context)
+ if (aActualContext) *aActualContext = fTimecontext; // current field's context
+ // if no time set or TCTX_UNKNOWN specified, just return the timestamp as-is
+ if (fTimestamp==noLinearTime || TCTX_IS_UNKNOWN(aTargetContext)) {
+ return
+ TCTX_IS_DATEONLY(aTargetContext) ?
+ lineartime2dateonlyTime(fTimestamp) : // truncated to date-only
+ fTimestamp; // as-is
+ }
+ // convert to the requested context
+ lineartime_t ts = fTimestamp;
+ if (TCTX_IS_DATEONLY(fTimecontext)) {
+ // is date only, connot convert to another zone, just return date part (and original context)
+ return lineartime2dateonlyTime(ts);
+ }
+ else {
+ // datetime or time
+ if (!TzConvertTimestamp(ts,fTimecontext,aTargetContext,fGZonesP)) {
+ // cannot convert, but we return unconverted timestamp if we can also return the actual context
+ if (aActualContext)
+ return fTimestamp; // return as-is
+ else
+ return noLinearTime; // invalid conversion, can't return a value
+ }
+ }
+ // return target context as actual context
+ if (aActualContext) *aActualContext = aTargetContext;
+ // return timestamp, eventually truncated to date-only or time-only if requested
+ if (TCTX_IS_DATEONLY(aTargetContext))
+ return lineartime2dateonlyTime(ts);
+ else if (TCTX_IS_TIMEONLY(aTargetContext))
+ return lineartime2timeonly(ts);
+ else
+ return ts; // date and time
+} // TTimestampField::getTimestampAs
+
+
+
+/// @brief get timestamp as ISO8601 string.
+/// @param aISOString[out] : timestamp in ISO8601 format
+/// @param aTargetContext[in] : requests output context for timestamp. Use TCTX_UNKNOWN to show timestamp (or dateonly or timeonly) as-is.
+/// @param aShowWithZ[in] : if set and timezone is UTC, value will be shown with "Z" appended
+/// @param aShowWithZone[in] : if set and timestamp is not floating, zone offset will be appended in +-xx:xx form
+/// @param aExtFormat[in] : if set, ISO8601 extended format is used
+/// @param aWithFracSecs[in] : if set, fractions of seconds will be shown (millisecond resolution)
+void TTimestampField::getAsISO8601(string &aISOString, timecontext_t aTargetContext, bool aShowWithZ, bool aShowWithZone, bool aExtFormat, bool aWithFracSecs)
+{
+ // get time in requested context
+ lineartime_t ts = getTimestampAs(aTargetContext);
+ // if target context was set to TCTX_UNKNOWN, this means that we want to use the stored context
+ if (aTargetContext==TCTX_UNKNOWN) // really explicitly TCTX_UNKNOWN without any rendering flags!
+ aTargetContext = fTimecontext;
+ // return empty string if no timestamp or conversion impossible (but show empty duration as such!)
+ if (ts==noLinearTime && !TCTX_IS_DURATION(fTimecontext)) {
+ aISOString.erase();
+ return;
+ }
+ // check if time zone should be included or not
+ if (!TCTX_IS_UNKNOWN(aTargetContext)) {
+ if (
+ TCTX_IS_UTC(aTargetContext) ?
+ !aShowWithZ :
+ !aShowWithZone
+ ) {
+ // prevent showing with time zone
+ aTargetContext = TCTX_JOIN_RFLAGS_TZ(aTargetContext,TCTX_UNKNOWN);
+ }
+ }
+ // now convert
+ TimestampToISO8601Str(aISOString, ts, aTargetContext, aExtFormat, aWithFracSecs);
+} // TTimestampField::getAsISO8601
+
+
+
+/// @brief move timestamp to specified context (i.e. convert the timestamp value from current to
+/// specified context). Floating timestamps cannot and will not be moved.
+/// @param aNewcontext[in] : context to move timestamp to.
+/// timestamp will be converted to represent the same point in time in the new context
+/// @param aSetUnmovables : if set, non-movable timestamps will be just assigned the new context,
+// that is floating timestamps will be bound to specified context or
+// non-floating timestamps will be made floating if new context is TCTX_UNKNOWN
+bool TTimestampField::moveToContext(timecontext_t aNewcontext, bool aSetUnmovables)
+{
+ bool ok=true;
+ if (isFloating() || fTimestamp==noLinearTime) {
+ // floating and empty timestamp cannot be moved
+ if (aSetUnmovables) {
+ // bind floating timestamp to specified zone
+ fTimecontext = aNewcontext;
+ }
+ else
+ ok=false; // floating/empty timestamps can only be fixed, not moved
+ }
+ else {
+ // timestamp is not floating or empty
+ if (TCTX_IS_UNKNOWN(aNewcontext)) {
+ if (aSetUnmovables)
+ fTimecontext = aNewcontext; // make floating
+ else
+ ok = false;
+ }
+ else {
+ // new context is not floating and old context isn't, either -> convert
+ ok = TzConvertTimestamp(fTimestamp, fTimecontext, aNewcontext, fGZonesP);
+ }
+ }
+ if (ok)
+ fTimecontext = aNewcontext; // we are now in the new context
+ return ok;
+} // TTimestampField::moveToContext
+
+
+
+/// @brief set timestamp from ISO8601 string.
+/// @return true if successful
+/// @param aISOString[in] : timestamp in ISO8601 basic or extended format, optionally including Z or +xx:xx zone specifier
+/// @param aDefaultContext[in] : timezone context to use when ISO8601 does not specify a zone context or when aIgnoreZone is true
+/// @param aIgnoreZone[in] : if set, timezone specification contained in ISO8601 is ignored. Resulting time context will be aDefaultContext (or TCTX_UNKNOWN for date-only)
+bool TTimestampField::setAsISO8601(cAppCharP aISOString, timecontext_t aDefaultContext, bool aIgnoreZone)
+{
+ bool ok = ISO8601StrToTimestamp(aISOString, fTimestamp, fTimecontext);
+ if (ok) {
+ fAssigned=true;
+ // if timestamp has unknown zone because it is a date-only or a duration, do NOT assign the default zone!
+ if (aIgnoreZone || (TCTX_IS_UNKNOWN(fTimecontext) && !TCTX_IS_DATEONLY(fTimecontext) && !TCTX_IS_DURATION(fTimecontext))) {
+ // set default context
+ fTimecontext = aDefaultContext;
+ }
+ }
+ else
+ assignEmpty();
+ // in all cases, this is now assigned (but probably empty if string was not ok)
+ return ok;
+} // TTimestampField::setAsISO8601
+
+
+
+#ifdef EMAIL_FORMAT_SUPPORT
+
+static cAppCharP const rfc822_weekdays[7] = {
+ "Sun","Mon","Tue","Wed","Thu","Fri","Sat"
+};
+
+const size_t rfc822maxMonthLen = 3;
+static cAppCharP const rfc822_months[12] = {
+ "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
+};
+
+
+/// @brief get timestamp as RFC(2)822 style date
+/// @param aRFC822String[out] : timestamp in RFC(2)822 format
+/// @param aTargetContext[in] : requests output context for timestamp. Use TCTX_UNKNOWN to show timestamp as-is.
+/// @param aShowWithZone[in] : if set and timestamp is not floating, zone offset will be shown
+void TTimestampField::getAsRFC822date(string &aRFC822String, timecontext_t aTargetContext, bool aShowWithZone)
+{
+ // get time in requested context
+ lineartime_t ts = getTimestampAs(aTargetContext);
+ // if target context was set to TCTX_UNKNOWN, this means that we want to use the stored context
+ if (TCTX_IS_UNKNOWN(aTargetContext))
+ aTargetContext = fTimecontext;
+ // get elements
+ sInt16 y,mo,d,h,mi,s,ms,w, moffs;
+ lineartime2date(ts,&y,&mo,&d);
+ lineartime2time(ts,&h,&mi,&s,&ms);
+ w = lineartime2weekday(ts);
+ // now print
+ StringObjPrintf(
+ aRFC822String,
+ "%s, %02hd %s %04hd %02hd:%02hd:%02hd",
+ rfc822_weekdays[w],
+ d,rfc822_months[mo-1],y,
+ h,mi,s
+ );
+ if (aShowWithZone && !TCTX_IS_UNKNOWN(aTargetContext)) {
+ // get offset
+ TzResolveToOffset(aTargetContext, moffs, ts, false, fGZonesP);
+ StringObjAppendPrintf(
+ aRFC822String,
+ " %c%02hd%02hd",
+ moffs>=0 ? '+' : '-',
+ (uInt16)(abs(moffs) / MinsPerHour),
+ (uInt16)(abs(moffs) % MinsPerHour)
+ );
+ }
+} // TTimestampField::getAsRFC822date
+
+
+
+/// @brief set timestamp as RFC(2)822 style date
+/// @return true if successful
+/// @param aRFC822String[in] : timestamp in RFC(2)822 format
+/// @param aDefaultContext[in] : timezone context to use when RFC822 date does not specify a time zone
+/// @param aIgnoreZone[in] : if set, timezone specification contained in input string is ignored. Resulting time context will be aDefaultContext
+bool TTimestampField::setAsRFC822date(cAppCharP aRFC822String, timecontext_t aDefaultContext, bool aIgnoreZone)
+{
+ cAppCharP p;
+ size_t inpSiz = strlen(aRFC822String);
+ cAppCharP eot=aRFC822String+inpSiz;
+ sInt16 minoffs=0;
+ // check for weekday
+ p = (cAppCharP) memchr(aRFC822String,',',inpSiz);
+ if (p)
+ p++; // skip comma
+ else
+ p=aRFC822String; // start at beginning
+ // scan elements
+ char month[rfc822maxMonthLen+1];
+ sInt16 y,mo,d,h,m,s;
+ s=0; // optional second
+ // scan day
+ while (p<eot && *p==' ') p++;
+ if (p+2>eot) return false; // string too short
+ p+=StrToShort(p,d,2);
+ // scan month
+ while (p<eot && *p==' ') p++;
+ uInt8 mi=0;
+ while (p<eot && *p!=' ' && mi<rfc822maxMonthLen) month[mi++]=*p++;
+ month[mi]=0;
+ // scan year
+ while (p<eot && *p==' ') p++;
+ if (p+4>eot) return false; // string too short
+ p+=StrToShort(p,y,4);
+ // scan time
+ while (p<eot && *p==' ') p++;
+ if (p+2>eot) return false; // string too short
+ p+=StrToShort(p,h,2);
+ if (p+3>eot || *p++ !=':') return false; // one for colon, two for minutes
+ p+=StrToShort(p,m,2);
+ if (p+3<=eot && *p==':') { // one for colon, two for minutes
+ // optional second
+ p++;
+ p+=StrToShort(p,s,2);
+ }
+ // convert month
+ for (mo=0; mo<12; mo++) if (strucmp(month,rfc822_months[mo])==0) break;
+ if (mo==12) return false; // bad format
+ mo++; // make month number
+ // make timestamp
+ fTimestamp = date2lineartime(y,mo,d) + time2lineartime(h,m,s,0);
+ // check time zone
+ bool neg=false;
+ while (p<eot && *p==' ') p++;
+ if (!aIgnoreZone) {
+ aIgnoreZone=true; // assume invalid zone spec
+ // check for numeric time zone
+ if (*p=='-' || *p=='+') {
+ // there is a timezone and we don't want to ignore it
+ if (*p=='-') neg=true;
+ if (p>=eot) return false; // string too short
+ p++;
+ sInt16 tzh,tzm;
+ // scan timezone
+ if (
+ p+4<=eot &&
+ StrToShort(p,tzh,2)==2 &&
+ StrToShort(p+2,tzm,2)==2
+ )
+ {
+ p+=4;
+ // set time zone
+ minoffs=((tzh*MinsPerHour)+tzm);
+ if (neg) minoffs = -minoffs;
+ // make zone
+ fTimecontext = TCTX_OFFSCONTEXT(minoffs);
+ aIgnoreZone=false; // zone spec valid
+ }
+ }
+ else if (isalpha(*p)) {
+ // could be time zone name (if not, ignore zone spec)
+ aIgnoreZone = !TimeZoneNameToContext(p,fTimecontext,fGZonesP);
+ }
+ }
+ // if no valid zone, use default
+ if (aIgnoreZone) {
+ // assume default time
+ fTimecontext = aDefaultContext;
+ }
+ return true;
+} // RFC822dateToTimeStamp
+
+
+#endif // EMAIL_FORMAT_SUPPORT
+
+
+
+/* end of TTimestampField implementation */
+
+
+/*
+ * Implementation of TDateField
+ */
+
+
+TDateField::TDateField(GZones *aGZonesP) :
+ TTimestampField(aGZonesP)
+{
+ fTimecontext |= TCTX_DATEONLY;
+} // TDateField::TDateField
+
+
+TDateField::~TDateField()
+{
+} // TDateField::~TDateField
+
+
+// compare: returns 0 if equal, 1 if this > aItem, -1 if this < aItem,
+// SYSYNC_NOT_COMPARABLE if not equal and no ordering known or if field
+// types do not match.
+// Note: ordering may NOT be age relevant; it just means that an ordering
+// for this field type exists.
+// Note: Both fields must be assigned. NO TEST IS DONE HERE!
+sInt16 TDateField::compareWith(TItemField &aItemField, bool aCaseInsensitive)
+{
+ sInt16 res;
+
+ // check comparison with non-date
+ if (!aItemField.isBasedOn(fty_timestamp))
+ return TTimestampField::compareWith(aItemField); // handles all comparisons with other types
+ // date comparison is context independent
+ TTimestampField *sfP = static_cast<TTimestampField *>(&aItemField);
+ // compare possible, return strcmp-type result
+ // - convert to date only
+ lineardate_t a = getTimestampAs(TCTX_UNKNOWN+TCTX_DATEONLY) / linearDateToTimeFactor;
+ lineardate_t b = sfP->getTimestampAs(TCTX_UNKNOWN+TCTX_DATEONLY) / linearDateToTimeFactor;
+ // - compare dates
+ res=a==b ? 0 : (a>b ? 1 : -1);
+ if (res!=0) {
+ // greater/less-type result is valid only if both sides are not empty
+ if ( isEmpty()) return SYSYNC_NOT_COMPARABLE; // empty value, cannot compare
+ if (sfP->isEmpty()) return SYSYNC_NOT_COMPARABLE; // empty value, cannot compare
+ }
+ return res;
+} // TDateField::compareWith
+
+
+// get date as ISO8601 string (default representation)
+void TDateField::getAsString(string &aString)
+{
+ // use default rendering
+ // - show with current context, but always date-only, no Z or zone info
+ inherited::getAsISO8601(aString,TCTX_UNKNOWN+TCTX_DATEONLY,false,false,false,false);
+} // TDateField::getAsString
+
+
+// set date from ISO8601 string (default interpretation)
+void TDateField::setAsString(cAppCharP aString)
+{
+ // default interpretation is floating date
+ // - parse timestamp, ignore timezone
+ if (setAsISO8601(aString, TCTX_UNKNOWN+TCTX_DATEONLY, true)) {
+ // truncate to date-only
+ fTimestamp = lineartime2dateonlyTime(fTimestamp);
+ }
+ // in all cases, this is now assigned (but probably empty)
+ fAssigned=true;
+} // TDateField::setAsString
+
+
+
+/* end of TDateField implementation */
+
+
+/*
+ * Implementation of TIntegerField
+ */
+
+
+TIntegerField::TIntegerField()
+{
+ fInteger=0;
+ fEmpty=true;
+} // TIntegerField::TIntegerField
+
+
+TIntegerField::~TIntegerField()
+{
+} // TIntegerField::~TIntegerField
+
+
+#if defined(CHECKSUM_CHANGELOG) && !defined(RECORDHASH_FROM_DBAPI)
+
+// changelog support: calculate CRC over contents
+uInt16 TIntegerField::getDataCRC(uInt16 crc)
+{
+ // CRC over integer number
+ return sysync_crc16_block(&fInteger,sizeof(fInteger),crc);
+} // TIntegerField::getDataCRC
+
+#endif
+
+
+// assignment
+TItemField& TIntegerField::operator=(TItemField &aItemField)
+{
+ // assignment of empty (could be EMPTY or UNASSIGNED) must be handled by base class
+ if (aItemField.isEmpty()) return TItemField::operator=(aItemField);
+ // handle non-empty myself
+ if (aItemField.isBasedOn(fty_integer)) {
+ const TIntegerField *sfP = static_cast<const TIntegerField *>(&aItemField);
+ fInteger=sfP->fInteger;
+ fEmpty=sfP->fEmpty;
+ fAssigned=sfP->fAssigned;
+ }
+ else if (aItemField.getCalcType()==fty_integer) {
+ fInteger=aItemField.getAsInteger();
+ fEmpty=false;
+ fAssigned=aItemField.isAssigned();
+ }
+ else
+ TItemField::operator=(aItemField); // generic cross-type assignment (via string)
+ return *this;
+} // TIntegerField::operator=
+
+
+// compare: returns 0 if equal, 1 if this > aItem, -1 if this < aItem,
+// SYSYNC_NOT_COMPARABLE if not equal and no ordering known or if field
+// types do not match.
+// Note: ordering may NOT be age relevant; it just means that an ordering
+// for this field type exists.
+// Notes: - Both fields must be assigned. NO TEST IS DONE HERE!
+// - emptyness is not taken into account (empty counts as 0 here)
+sInt16 TIntegerField::compareWith(TItemField &aItemField, bool aCaseInsensitive)
+{
+ if (aItemField.getCalcType()!=fty_integer) return TItemField::compareWith(aItemField); // handles generic comparisons
+ fieldinteger_t otherInt = aItemField.getAsInteger();
+ // compare possible, return strcmp-type result
+ return fInteger==otherInt ? 0 : (fInteger>otherInt ? 1 : -1);
+} // TIntegerField::compareWith
+
+
+// get integer as integer
+fieldinteger_t TIntegerField::getAsInteger(void)
+{
+ return fInteger;
+} // TIntegerField::getAsInteger
+
+
+// set integer
+void TIntegerField::setAsInteger(fieldinteger_t aInteger)
+{
+ fInteger=aInteger;
+ fAssigned=true;
+ fEmpty=false;
+} // TIntegerField::setAsInteger
+
+
+
+// set integer from numeric string
+void TIntegerField::setAsString(cAppCharP aString)
+{
+ // check for hex
+ bool ok;
+ if (strucmp(aString,"0x",2)==0) {
+ // hex number
+ #ifndef NO64BITINT
+ ok = HexStrToULongLong(aString+2,*((uInt64 *)&fInteger));
+ #else
+ ok = HexStrToULong(aString+2,*((uInt32 *)&fInteger));
+ #endif
+ }
+ else {
+ // decimal integer number
+ #ifndef NO64BITINT
+ ok = StrToLongLong(aString,fInteger);
+ #else
+ ok = StrToLong(aString,fInteger);
+ #endif
+ }
+ if (!ok)
+ fInteger=0; // defined value
+ // setting with a non-integer value makes the integer empty (but having integer value 0)
+ fEmpty = !ok;
+ // in all cases, this is now assigned (but probably with the default=0)
+ fAssigned=true;
+} // TIntegerField::setAsString
+
+
+// get integer as numeric string
+void TIntegerField::getAsString(string &aString)
+{
+ if (fEmpty)
+ aString.erase();
+ else {
+ #ifndef NO64BITINT
+ LONGLONGTOSTR(aString,(long long)fInteger);
+ #else
+ StringObjPrintf(aString,"%ld",(long)fInteger);
+ #endif
+ }
+} // TIntegerField::getAsString
+
+
+/* end of TIntegerField implementation */
+
+
+// factory function
+TItemField *newItemField(const TItemFieldTypes aType, GZones *aGZonesP, bool aAsArray)
+{
+ #ifdef ARRAYFIELD_SUPPORT
+ if (aAsArray) {
+ return new TArrayField(aType);
+ }
+ else
+ #endif
+ {
+ switch (aType) {
+ case fty_string: return new TStringField;
+ case fty_telephone: return new TTelephoneField;
+ case fty_integer: return new TIntegerField;
+ case fty_timestamp: return new TTimestampField(aGZonesP);
+ case fty_date: return new TDateField(aGZonesP);
+ case fty_url: return new TURLField;
+ case fty_multiline: return new TMultilineField;
+ case fty_blob: return new TBlobField;
+ case fty_none: return new TItemField; // base class, can represent EMPTY and UNASSIGNED
+ default: return NULL;
+ }
+ }
+} // newItemField
+
+
+#ifdef ENGINEINTERFACE_SUPPORT
+
+// TItemFieldKey
+// =============
+
+// get value's ID (e.g. internal index)
+sInt32 TItemFieldKey::GetValueID(cAppCharP aName)
+{
+ // check special suffixes
+ size_t namsz = strlen(aName);
+ sInt32 fldID = 0; // basic
+ if (strucmp(aName+namsz-strlen(VALSUFF_TZNAME),VALSUFF_TZNAME)==0) {
+ // time zone name as string requested
+ namsz-=7;
+ fldID += VALID_FLAG_TZNAME;
+ }
+ else if (strucmp(aName+namsz-strlen(VALSUFF_TZOFFS),VALSUFF_TZOFFS)==0) {
+ // time zone offset in minutes
+ namsz-=7;
+ fldID += VALID_FLAG_TZOFFS;
+ }
+ else if (strucmp(aName+namsz-strlen(VALSUFF_NORM),VALSUFF_NORM)==0) {
+ // normalized value
+ namsz-=5;
+ fldID += VALID_FLAG_NORM;
+ }
+ else if (strucmp(aName+namsz-strlen(VALSUFF_ARRSZ),VALSUFF_ARRSZ)==0) {
+ // array size
+ namsz-=10;
+ fldID += VALID_FLAG_ARRSIZ;
+ }
+ else if (strucmp(aName+namsz-strlen(VALSUFF_NAME),VALSUFF_NAME)==0) {
+ // value name
+ namsz-=8;
+ fldID += VALID_FLAG_VALNAME;
+ }
+ else if (strucmp(aName+namsz-strlen(VALSUFF_TYPE),VALSUFF_TYPE)==0) {
+ // value type
+ namsz-=8;
+ fldID += VALID_FLAG_VALTYPE;
+ }
+ // check if this is only a query for the flags alone
+ if (strucmp(aName,VALNAME_FLAG,namsz)==0)
+ return fldID; // return flags alone
+ // get fid for given name
+ sInt16 fid = getFidFor(aName,namsz);
+ if (fid==VARIDX_UNDEFINED)
+ return KEYVAL_ID_UNKNOWN; // unknown field
+ else
+ return fldID + (sInt32)((uInt16)fid); // return FID in lo word, flags in highword
+} // TItemFieldKey::GetValueID
+
+
+
+// get value's native type
+uInt16 TItemFieldKey::GetValueType(sInt32 aID)
+{
+ // fid is lower 16 bits of aID (and gets negative if bit15 of aID is set)
+ sInt16 fid;
+ *((uInt16 *)(&fid)) = aID & VALID_MASK_FID; // assign without sign extension
+ // now get base field
+ TItemField *baseFieldP = getBaseFieldFromFid(fid);
+ if (baseFieldP) {
+ // field exists
+ // - check special queries
+ if (aID & VALID_FLAG_ARRSIZ)
+ return VALTYPE_INT16; // size is a 16-bit integer, regardless of field type
+ else if (aID & VALID_FLAG_VALNAME)
+ return VALTYPE_TEXT; // name of the value is text
+ else if (aID & VALID_FLAG_VALTYPE)
+ return VALTYPE_INT16; // VALTYPE_XXX are 16-bit integers
+ else if (aID & VALID_FLAG_NORM)
+ return VALTYPE_TEXT; // normalized value is text
+ // - otherwise, valtype depends on field type
+ TItemFieldTypes fty = baseFieldP->getElementType();
+ // return native type
+ switch (fty) {
+ case fty_none:
+ return VALTYPE_UNKNOWN;
+ case fty_blob:
+ return VALTYPE_BUF;
+ case fty_string:
+ case fty_multiline:
+ case fty_telephone:
+ case fty_url:
+ return VALTYPE_TEXT;
+ case fty_integer:
+ return VALTYPE_INT64;
+ case fty_timestamp:
+ case fty_date:
+ if (aID & VALID_FLAG_TZNAME)
+ return VALTYPE_TEXT; // time zone name is a text
+ else if (aID & VALID_FLAG_TZOFFS)
+ return VALTYPE_INT16; // minute offset is 16-bit integer
+ else
+ return VALTYPE_TIME64; // time values
+ case numFieldTypes:
+ // invalid
+ break;
+ } // switch
+ }
+ // no field, no type
+ return VALTYPE_UNKNOWN;
+} // TItemFieldKey::GetValueType
+
+
+// helper for getting a leaf field pointer (interpretation of aRepOffset depends on array field or not)
+TItemField *TItemFieldKey::getFieldFromFid(sInt16 aFid, sInt16 aRepOffset, bool aExistingOnly)
+{
+ TItemField *fieldP=NULL;
+ // get field (or base field)
+ #ifdef ARRAYFIELD_SUPPORT
+ fieldP = getBaseFieldFromFid(aFid);
+ if (!fieldP) return NULL; // no field
+ if (fieldP->isArray()) {
+ // use aRepOffset as array index
+ fieldP = fieldP->getArrayField(aRepOffset,aExistingOnly);
+ }
+ else
+ #endif
+ {
+ // use aRepOffset as fid offset
+ #ifdef SCRIPT_SUPPORT
+ if (aFid<0) aRepOffset=0; // locals are never offset
+ #endif
+ fieldP = getBaseFieldFromFid(aFid+aRepOffset);
+ }
+ return fieldP;
+} // TItemFieldKey::getFieldFromFid
+
+
+
+
+// get value
+TSyError TItemFieldKey::GetValueInternal(
+ sInt32 aID, sInt32 aArrayIndex,
+ appPointer aBuffer, memSize aBufSize, memSize &aValSize
+)
+{
+ string sval;
+ TItemField *fieldP;
+ // fid is lower 16 bits of aID (and gets negative if bit15 of aID is set)
+ sInt16 fid;
+ *((uInt16 *)(&fid)) = aID & VALID_MASK_FID; // assign without sign extension
+
+ // check for special queries not actually accessing a leaf field
+ if (aID & VALID_FLAG_ARRSIZ) {
+ // return array size (returns DB_NotFound for non-arrays)
+ fieldP = getBaseFieldFromFid(fid);
+ // if not an array, return DB_NotFound
+ if (!fieldP || !fieldP->isArray())
+ return DB_NotFound; // there is no array size for non-arrays
+ // return size
+ aValSize=sizeof(sInt16);
+ // value if enough room
+ if (aBufSize>=aValSize)
+ *((sInt16 *)aBuffer)=fieldP->arraySize();
+ // ok
+ return LOCERR_OK;
+ }
+ else if (aID & VALID_FLAG_VALNAME) {
+ // only return name of field
+ if (!getFieldNameFromFid(fid,sval)) return DB_NotFound;
+ if (aBufSize>=sval.size())
+ memcpy(aBuffer,sval.c_str(),sval.size()); // copy name data
+ aValSize=sval.size();
+ return LOCERR_OK;
+ }
+ else if (aID & VALID_FLAG_VALTYPE) {
+ // only return VALTYPE of field
+ uInt16 valtype = GetValueType(aID & ~VALID_FLAG_VALTYPE);
+ aValSize = sizeof(valtype);
+ if (aBufSize>=aValSize)
+ memcpy(aBuffer,&valtype,aValSize); // copy valtype uInt16
+ return LOCERR_OK;
+ }
+ // Get actual data - we need the leaf field
+ fieldP = getFieldFromFid(fid, aArrayIndex, true); // existing array elements only
+ if (!fieldP) {
+ // array instance does not exist
+ return LOCERR_OUTOFRANGE;
+ }
+ else {
+ // leaf field exists
+ if (!fieldP->isAssigned()) return DB_NotFound; // no content found because none assigned
+ if (fieldP->isEmpty()) return DB_NoContent; // empty
+ // assigned and not empty, return actual value
+ TItemFieldTypes fty = fieldP->getType();
+ appPointer valPtr = NULL;
+ size_t sz;
+ fieldinteger_t intVal;
+ lineartime_t ts;
+ timecontext_t tctx;
+ sInt16 minOffs;
+ TTimestampField *tsFldP;
+ if (aID & VALID_FLAG_NORM) {
+ // for all field types: get normalized string value
+ fieldP->getAsNormalizedString(sval);
+ aValSize = sval.size();
+ valPtr = (appPointer)sval.c_str();
+ }
+ else {
+ switch (fty) {
+ case fty_timestamp:
+ case fty_date:
+ tsFldP = static_cast<TTimestampField *>(fieldP);
+ if (aID & VALID_FLAG_TZNAME) {
+ // time zone name is a text
+ sval.erase(); // no zone
+ if (!tsFldP->isFloating()) {
+ // has a zone, get name
+ TimeZoneContextToName(tsFldP->getTimeContext(), sval, tsFldP->getGZones());
+ }
+ aValSize = sval.size();
+ valPtr = (appPointer)sval.c_str();
+ }
+ else if (aID & VALID_FLAG_TZOFFS) {
+ // minute offset as 16-bit integer
+ aValSize = sizeof(sInt16);
+ if (
+ tsFldP->isFloating() ||
+ !TzResolveToOffset(tsFldP->getTimeContext(), minOffs, tsFldP->getTimestampAs(TCTX_UNKNOWN), false, tsFldP->getGZones())
+ )
+ return DB_NotFound; // cannot get minute offset or floating timestamp -> no result
+ // return minute offset
+ valPtr = &minOffs;
+ }
+ else {
+ // return timestamp itself (either as-is or in UTC, if TMODE_FLAG_FLOATING not set)
+ aValSize = sizeof(lineartime_t);
+ if (fTimeMode & TMODE_FLAG_FLOATING)
+ ts = tsFldP->getTimestampAs(TCTX_UNKNOWN); // as-is
+ else
+ ts = tsFldP->getTimestampAs(TCTX_UTC,&tctx); // always as UTC, will be converted to SYSTEM eventually by caller
+ // return timestamp
+ valPtr = &ts;
+ }
+ break;
+ case fty_integer:
+ aValSize = sizeof(fieldinteger_t);
+ intVal = fieldP->getAsInteger();
+ valPtr = &intVal;
+ break;
+ case fty_none:
+ aValSize = 0;
+ break;
+ case fty_blob:
+ static_cast<TBlobField *>(fieldP)->getBlobDataPtrSz(valPtr,sz);
+ aValSize = sz;
+ break;
+ case fty_string:
+ case fty_multiline:
+ case fty_telephone:
+ case fty_url:
+ aValSize = fieldP->getStringSize();
+ valPtr = (appPointer)static_cast<TStringField *>(fieldP)->getCStr();
+ break;
+ case numFieldTypes:
+ // invalid
+ break;
+ } // switch
+ }
+ // now we have valid aValSize and valPtr
+ if (aBuffer && aBufSize>=aValSize) {
+ // copy value data
+ memcpy(aBuffer,valPtr,aValSize);
+ }
+ }
+ // ok
+ return LOCERR_OK;
+} // TItemFieldKey::GetValueInternal
+
+
+
+// set value
+TSyError TItemFieldKey::SetValueInternal(
+ sInt32 aID, sInt32 aArrayIndex,
+ cAppPointer aBuffer, memSize aValSize
+)
+{
+ TItemField *fieldP;
+ // fid is lower 16 bits of aID (and gets negative if bit15 of aID is set)
+ sInt16 fid;
+ *((uInt16 *)(&fid)) = aID & VALID_MASK_FID; // assign without sign extension
+
+ // check for special array size query
+ if (aID & VALID_FLAG_ARRSIZ) {
+ // array size is not writable
+ return DB_NotAllowed;
+ }
+ // now get leaf field
+ fieldP = getFieldFromFid(fid, aArrayIndex, false); // create element if needed
+ if (!fieldP) {
+ // should not happen
+ return DB_Error;
+ }
+ else {
+ // leaf field exists, set value
+ fWritten = true;
+ TItemFieldTypes fty = fieldP->getType();
+ fieldinteger_t intVal;
+ lineartime_t ts;
+ timecontext_t tctx;
+ sInt16 minOffs;
+ string sval;
+ TTimestampField *tsFldP;
+ // treat setting normalized value like setting as string
+ if (aID & VALID_FLAG_NORM)
+ fty = fty_string; // treat like string
+ // now handle according to type
+ switch (fty) {
+ case fty_timestamp:
+ case fty_date:
+ tsFldP = static_cast<TTimestampField *>(fieldP);
+ if (aID & VALID_FLAG_TZNAME) {
+ // set time zone by name
+ sval.assign((cAppCharP)aBuffer,aValSize);
+ tctx = TCTX_UNKNOWN;
+ if (!sval.empty()) {
+ // convert
+ if (!TimeZoneNameToContext(sval.c_str(), tctx, tsFldP->getGZones()))
+ return LOCERR_BADPARAM; // bad timezone name
+ }
+ // set context
+ tsFldP->setTimeContext(tctx);
+ }
+ else if (aID & VALID_FLAG_TZOFFS) {
+ // minute offset as 16-bit integer
+ minOffs = *((sInt16 *)aBuffer);
+ // set context
+ tsFldP->setTimeContext(TCTX_MINOFFSET(minOffs));
+ }
+ else {
+ // set timestamp itself (either as-is or in UTC, if TMODE_FLAG_FLOATING not set)
+ ts = *((lineartime_t *)aBuffer);
+ if ((fTimeMode & TMODE_FLAG_FLOATING)==0)
+ tsFldP->setTimestampAndContext(ts,TCTX_UTC); // incoming timestamp is UTC, set it as such
+ else
+ tsFldP->setTimestamp(ts); // just set timestamp as-is and don't touch rest
+ }
+ break;
+ case fty_integer:
+ intVal = *((fieldinteger_t *)aBuffer);
+ fieldP->setAsInteger(intVal);
+ break;
+ case fty_blob:
+ static_cast<TBlobField *>(fieldP)->setBlobDataPtrSz((void *)aBuffer,aValSize);
+ break;
+ case fty_string:
+ case fty_multiline:
+ case fty_telephone:
+ case fty_url:
+ fieldP->setAsString((cAppCharP)aBuffer,aValSize);
+ break;
+ case fty_none:
+ case numFieldTypes:
+ // invalid
+ break;
+ } // switch
+ }
+ // ok
+ return LOCERR_OK;
+} // TItemFieldKey::SetValueInternal
+
+
+#endif // ENGINEINTERFACE_SUPPORT
+
+
+
+} // namespace sysync
+
+// eof
diff --git a/src/sysync/itemfield.h b/src/sysync/itemfield.h
new file mode 100755
index 0000000..c984b83
--- /dev/null
+++ b/src/sysync/itemfield.h
@@ -0,0 +1,679 @@
+/*
+ * File: itemfield.h
+ *
+ * Author: Lukas Zeller (luz@synthesis.ch)
+ *
+ * TItemField
+ * Abstract class, holds a single field value
+ * TStringField, TIntegerField, TTelephoneField, TTimeStampField etc.
+ * Implementations of field types
+ *
+ * Copyright (c) 2001-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ * 2001-08-08 : luz : created
+ *
+ */
+
+#ifndef ItemField_H
+#define ItemField_H
+
+// includes
+#include "sysync_globs.h"
+#include "sysync_utils.h"
+
+#include "engineinterface.h"
+
+#include <string>
+
+#ifdef ARRAYFIELD_SUPPORT
+#include <vector>
+#endif
+
+namespace sysync {
+
+extern cAppCharP const ItemFieldTypeNames[numFieldTypes];
+
+extern const TPropDataTypes devInfPropTypes[numFieldTypes];
+
+
+#ifndef NO64BITINT
+typedef sInt64 fieldinteger_t;
+#define StrToFieldinteger(s,i) StrToLongLong(s,i)
+#else
+typedef sInt32 fieldinteger_t;
+#define StrToFieldinteger(s,i) StrToLong(s,i)
+#endif
+
+// our own implementation for dynamic casts for Items without needing RTTI
+#define ITEMFIELD_DYNAMIC_CAST_PTR(ty,tyid,src) (src->isBasedOn(tyid) ? static_cast<ty *>(src) : NULL)
+
+
+// basically abstract class, but can be used to represent EMPTY and ASSIGNED values
+class TItemField
+{
+public:
+ TItemField();
+ virtual ~TItemField();
+ #ifdef ARRAYFIELD_SUPPORT
+ // check array
+ virtual bool isArray(void) const { return false; }
+ virtual TItemField *getArrayField(sInt16 aArrIdx, bool /* aExistingOnly */=false) { return aArrIdx==0 ? this : NULL; };
+ virtual sInt16 arraySize(void) const { return 1; } // non-array has one element
+ virtual TItemFieldTypes getElementType(void) const { return getType(); } // non array element is same as base type
+ #else
+ // non-virtual versions if we have no array fields at all
+ bool isArray(void) const { return false; }
+ TItemField *getArrayField(sInt16 aArrIdx, bool aExistingOnly=false) { return aArrIdx==0 ? this : NULL; };
+ sInt16 arraySize(void) const { return 1; } // non-array has one element
+ TItemFieldTypes getElementType(void) const { return getType(); }
+ #endif
+ // changelog support
+ #if defined(CHECKSUM_CHANGELOG) && !defined(RECORDHASH_FROM_DBAPI)
+ virtual uInt16 getDataCRC(uInt16 crc=0) { return crc; }; // base class is always empty
+ #endif
+ // access to type
+ virtual TItemFieldTypes getType(void) const { return fty_none; } // no real type
+ virtual TItemFieldTypes getCalcType(void) const { return getType(); };
+ virtual bool isBasedOn(TItemFieldTypes aFieldType) const { return aFieldType==fty_none; };
+ // dependency on a local ID
+ virtual void setParentLocalID(cAppCharP /* aParentLocalID */) { /* nop */ };
+ // access to field contents
+ #ifdef STREAMFIELD_SUPPORT
+ // - stream interface (default implementation accesses string representation of field)
+ virtual void resetStream(size_t aPos=0);
+ virtual size_t getStreamSize(void) { return getStringSize(); };
+ virtual size_t getStreamPos(void) const { return fStreamPos; };
+ virtual size_t readStream(void *aBuffer, size_t aMaxBytes);
+ virtual size_t writeStream(void *aBuffer, size_t aNumBytes);
+ void appendStream(void) { resetStream(getStreamSize()); };
+ #endif
+ virtual bool hasProxy(void) { return false; }; // normal fields don't have a proxy
+ // - as string
+ virtual void setAsString(cAppCharP /* aString */) { fAssigned=true; }; // basic setter, this one must be derived for all non-string descendants
+ virtual void setAsString(cAppCharP aString, size_t aLen);
+ virtual void setAsString(const string &aString) { setAsString(aString.c_str(),aString.size()); };
+ virtual void getAsString(string &aString) { aString.erase(); };
+ virtual void appendToString(string &aString, size_t aMaxLen=0) { string s; getAsString(s); if (aMaxLen) aString.append(s,0,aMaxLen); else aString.append(s); }; // generic append
+ virtual cAppCharP getCStr(void) { return NULL; } // only real strings can return CStr (used for PalmOS optimization)
+ virtual void getAsNormalizedString(string &aString) { getAsString(aString); };
+ virtual size_t getStringSize(void);
+ // - as boolean (default: non-empty is true)
+ virtual bool getAsBoolean(void) { return !isEmpty(); };
+ virtual void setAsBoolean(bool aBool) { if (aBool) setAsString("1"); else assignEmpty(); };
+ // - as integer
+ virtual fieldinteger_t getAsInteger(void);
+ virtual void setAsInteger(fieldinteger_t aInteger);
+ // some string operations
+ // - append string/char
+ virtual void appendString(cAppCharP /* aString */, size_t /* aMaxChars */) { /* nop */ };
+ void appendString(cAppCharP aString) { appendString(aString,strlen(aString)); };
+ void appendString(const string &aString) { appendString(aString.c_str(),aString.size()); };
+ void appendChar(const char aChar) { appendString(&aChar,1); };
+ // - check if specified value is contained in myself
+ virtual bool contains(TItemField &aItemField, bool aCaseInsensitive);
+ // - find string in contents
+ virtual sInt16 findInString(cAppCharP /* aString */, bool /* aCaseInsensitive */) { return -1; };
+ // - check if specified field is shortened version of this one
+ virtual bool isShortVers(TItemField & /* aItemField */, sInt32 /* aOthersMax */) { return false; };
+ // - assignment
+ virtual bool isAssigned(void) { return fAssigned; };
+ bool isUnassigned(void) { return !isAssigned(); };
+ virtual void unAssign(void) { fAssigned=false; };
+ // empty is NOT always same as unassigned; e.g. for strings empty can be empty string assigned
+ virtual bool isEmpty(void) { return getType()==fty_none ? true : isUnassigned(); }
+ // - make assigned, but empty (explicit "" string assigned)
+ virtual void assignEmpty(void) { fAssigned=true; };
+ // assignment
+ virtual TItemField& operator=(TItemField &aItemField);
+ // field comparison
+ bool operator==(TItemField &aItem) { return compareWith(aItem)==0; }
+ bool operator!=(TItemField &aItem) { return compareWith(aItem)!=0; }
+ bool operator>(TItemField &aItem) { return compareWith(aItem)>0; }
+ bool operator>=(TItemField &aItem) { return compareWith(aItem)>=0; }
+ bool operator<(TItemField &aItem) { return compareWith(aItem)<0; }
+ bool operator<=(TItemField &aItem) { return compareWith(aItem)<=0; }
+ // append (default to appending string value of other field)
+ virtual void append(TItemField &aItemField);
+ // merge fields
+ virtual bool merge(TItemField & /* aItemField */, const char /* aSep */=0) { return false; /* nop */ };
+ // compare: returns 0 if equal, 1 if this > aItem, -1 if this < aItem,
+ // SYSYNC_NOT_COMPARABLE if not comparable at all or not equal and no ordering known
+ virtual sInt16 compareWith(TItemField & /* aItemField */, bool =false /* aCaseInsensitive */) { return SYSYNC_NOT_COMPARABLE; };
+ // debug support
+ #ifdef SYDEBUG
+ virtual size_t StringObjFieldAppend(string &s, uInt16 aMaxStrLen); // show field contents as string for debug output
+ #endif
+protected:
+ // assigned flag
+ bool fAssigned;
+ #ifdef STREAMFIELD_SUPPORT
+ // stream position
+ size_t fStreamPos;
+ #endif
+}; // TItemField
+
+typedef TItemField *TItemFieldP;
+
+
+
+#ifdef ARRAYFIELD_SUPPORT
+
+typedef std::vector<TItemField *> TFieldArray;
+
+// array field, contains a list of fields of TItemFields
+class TArrayField : public TItemField
+{
+ typedef TItemField inherited;
+public:
+ TArrayField(TItemFieldTypes aLeafFieldType);
+ virtual ~TArrayField();
+ // check array
+ virtual bool isArray(void) const { return true; }
+ virtual TItemField *getArrayField(sInt16 aArrIdx, bool aExistingOnly=false);
+ virtual sInt16 arraySize(void) const { return fArray.size(); } // return size of array
+ // changelog support
+ #if defined(CHECKSUM_CHANGELOG) && !defined(RECORDHASH_FROM_DBAPI)
+ virtual uInt16 getDataCRC(uInt16 crc=0);
+ #endif
+ // access to type
+ virtual TItemFieldTypes getType(void) const { return fty_none; } // array has no type
+ virtual TItemFieldTypes getElementType(void) const { return fLeafFieldType; } // type of leaf fields (accessible even if array is empty)
+ // some string operations
+ // - assignment
+ virtual bool isAssigned(void) { return !isEmpty() || fAssigned; }; // empty, but explicitly assigned so is assigned as well
+ virtual void unAssign(void); // clear all elements and clear fAssigned
+ // - append string to array = append string as last element of array
+ virtual void appendString(cAppCharP aString, size_t aMaxChars);
+ // empty is array with no elements
+ virtual bool isEmpty(void) { return arraySize()==0; };
+ // - make assigned
+ virtual void assignEmpty(void) { unAssign(); fAssigned = true; }; // assigning emptyness must make isAssigned true!
+ // assignment
+ virtual TItemField& operator=(TItemField &aItemField);
+ // check if specified value is contained in myself
+ virtual bool contains(TItemField &aItemField, bool aCaseInsensitive);
+ // append (default to appending value of other field as a new array element)
+ virtual void append(TItemField &aItemField);
+ // merge fields
+ virtual bool merge(TItemField & /* aItemField */, const char /* aSep */=0) { return false; /* nop */ };
+ // compare: returns 0 if equal, 1 if this > aItem, -1 if this < aItem,
+ // SYSYNC_NOT_COMPARABLE if not comparable at all or not equal and no ordering known
+ virtual sInt16 compareWith(TItemField &aItemField, bool aCaseInsensitive=false);
+ // debug support
+ #ifdef SYDEBUG
+ virtual size_t StringObjFieldAppend(string &s, uInt16); // show field contents as string for debug output
+ #endif
+protected:
+ // type of contained leaf fields
+ TItemFieldTypes fLeafFieldType;
+ // actual field vector
+ TFieldArray fArray;
+}; // TArrayField
+#endif
+
+
+#ifndef STREAMFIELD_SUPPORT
+ #define PULLFROMPROXY
+ #define DELETEPROXY
+ #define PROXYINSTALLED false
+#else
+ #define PULLFROMPROXY pullFromProxy()
+ #define DELETEPROXY setBlobProxy(NULL)
+ #define PROXYINSTALLED (fBlobProxyP!=NULL)
+
+// forward
+class TStringField;
+
+// abstract Proxy object for Blob contents, allows loading a BLOB from DB at the moment it is
+// actually required.
+// Intended to be derived by database implementations
+class TBlobProxy
+{
+public:
+ TBlobProxy() { fUsage=1; } // initial creator also gets owner
+ virtual ~TBlobProxy() {};
+ // - returns size of entire blob
+ virtual size_t getBlobSize(TStringField *aFieldP) = 0;
+ // - read from Blob from specified stream position and update stream pos
+ virtual size_t readBlobStream(TStringField *aFieldP, size_t &aPos, void *aBuffer, size_t aMaxBytes) = 0;
+ // - dependency on a local ID
+ virtual void setParentLocalID(cAppCharP aParentLocalID) { /* nop */ };
+ // - usage control
+ void link(void) { fUsage++; };
+ static void unlink(TBlobProxy *&aProxyP) { if (aProxyP) { if (--(aProxyP->fUsage) == 0) delete aProxyP; aProxyP=NULL; } };
+private:
+ uInt16 fUsage;
+}; // TBlobProxy
+
+#endif
+
+
+// string field.
+// Note that a string can also be a string of binary data,
+// not only a NUL-terminated string (as used in derived blob class)
+class TStringField: public TItemField
+{
+ typedef TItemField inherited;
+ friend class TBlobProxy;
+public:
+ TStringField();
+ virtual ~TStringField();
+ // access to type
+ virtual TItemFieldTypes getType(void) const { return fty_string; };
+ virtual bool isBasedOn(TItemFieldTypes aFieldType) const { return aFieldType==fty_string ? true : TItemField::isBasedOn(aFieldType); };
+ // changelog support
+ #if defined(CHECKSUM_CHANGELOG) && !defined(RECORDHASH_FROM_DBAPI)
+ virtual uInt16 getDataCRC(uInt16 crc=0);
+ #endif
+ // assignment
+ virtual TItemField& operator=(TItemField &aItemField);
+ // access to normalized version of content
+ virtual void getAsNormalizedString(string &aString);
+ // access to field contents
+ #ifdef STREAMFIELD_SUPPORT
+ // - set blob loader proxy (ownership is passed to field)
+ void setBlobProxy(TBlobProxy *aBlobProxyP);
+ // - stream interface
+ virtual size_t getStreamSize(void);
+ virtual size_t readStream(void *aBuffer, size_t aMaxBytes);
+ virtual size_t writeStream(void *aBuffer, size_t aNumBytes);
+ virtual bool hasProxy(void) { return fBlobProxyP!=NULL; };
+ // - stream field proxies actually can have dependencies on a local ID
+ virtual void setParentLocalID(cAppCharP aParentLocalID) { if (fBlobProxyP) fBlobProxyP->setParentLocalID(aParentLocalID); };
+ #endif
+ // - as string
+ virtual void setAsString(cAppCharP aString) { DELETEPROXY; if (aString) fString=aString; else fString.erase(); stringWasAssigned(); };
+ virtual void setAsString(const string &aString) { DELETEPROXY; fString=aString; stringWasAssigned(); }; // works even if string contains NULs
+ virtual void setAsString(cAppCharP aString, size_t aLen);
+ virtual void getAsString(string &aString) { PULLFROMPROXY; aString=fString; };
+ virtual void appendToString(string &aString, size_t aMaxLen=0) { PULLFROMPROXY; if (aMaxLen) aString.append(fString,0,aMaxLen); else aString.append(fString); };
+ virtual cAppCharP getCStr(void) { PULLFROMPROXY; return fString.c_str(); } // string can return CStr (used for PalmOS optimization)
+ virtual size_t getStringSize(void); // can cause proxied values to be retrieved, so use with care with BLOBS
+ virtual void unAssign(void) { DELETEPROXY; fString.erase(); TItemField::unAssign(); };
+ // empty test
+ virtual bool isEmpty(void) { return isUnassigned() || getStringSize()==0; }
+ virtual void assignEmpty(void) { DELETEPROXY; fString.erase(); TItemField::assignEmpty(); };
+ // some string operations
+ // - append string
+ virtual void appendString(cAppCharP aString, size_t aMaxChars);
+ // - check if specified field is shortened version of this one
+ virtual bool isShortVers(TItemField &aItemField, sInt32 aOthersMax);
+ // - check if String is contained in value and returns position
+ virtual sInt16 findInString(cAppCharP aString, bool aCaseInsensitive=false);
+ // merge fields
+ virtual bool merge(TItemField &aItemField, const char aSep=0);
+ // compare: returns 0 if equal, 1 if this > aItem, -1 if this < aItem
+ // SYSYNC_NOT_COMPARABLE if not equal and no ordering known
+ virtual sInt16 compareWith(TItemField &aItemField, bool aCaseInsensitive=false);
+ #ifdef STREAMFIELD_SUPPORT
+ void pullFromProxy(void);
+ #endif
+ // debug support
+ #ifdef SYDEBUG
+ virtual size_t StringObjFieldAppend(string &s, uInt16 aMaxStrLen);
+ #endif
+protected:
+ virtual void stringWasAssigned(void) { fAssigned=true; }; // post-process string that was just assigned
+ #ifdef STREAMFIELD_SUPPORT
+ TBlobProxy *fBlobProxyP;
+ #endif
+ string fString;
+}; // TStringField
+
+
+// BLOB field. This is a string field but has many direct access functions
+// disabled, contents should only be read using stream interface,
+// comparing is not possible
+class TBlobField: public TStringField
+{
+ typedef TStringField inherited;
+public:
+ TBlobField();
+ virtual ~TBlobField();
+ // access to type
+ virtual TItemFieldTypes getType(void) const { return fty_blob; };
+ virtual bool isBasedOn(TItemFieldTypes aFieldType) const { return aFieldType==fty_blob ? true : TStringField::isBasedOn(aFieldType); };
+ // changelog support
+ // Note: uses inherited implementation of TStringField
+ // assignment
+ virtual TItemField& operator=(TItemField &aItemField);
+ // - as string
+ virtual void getAsString(string &aString) { StringObjPrintf(aString,"<BLOB size=%ld>", long(getStringSize())); }; // must be read with stream interface
+ void getBlobAsString(string &aString) { TStringField::getAsString(aString); }; // must use this one to get as string
+ // - as Pointer/Data
+ void getBlobDataPtrSz(void *&aPtr, size_t &aSize) { aPtr=(void *)TStringField::getCStr(); aSize=TStringField::getStringSize(); };
+ void setBlobDataPtrSz(void *aPtr, size_t aSize) { setAsString((cAppCharP)aPtr, aSize); };
+ // - test
+ virtual bool isEmpty(void) { return isUnassigned(); } // do not test for empty string as this would cause BLOB to be read
+ // some string operations
+ // - check if specified field is shortened version of this one
+ virtual bool isShortVers(TItemField & /* aItemField */, sInt32 /* aOthersMax */) { return false; }; // cannot compare
+ // - check if String is contained in value and returns position
+ virtual sInt16 findInString(cAppCharP /* aString */, bool /* aCaseInsensitive */) { return -1; }; // cannot search
+ // merge fields
+ virtual bool merge(TItemField & /* aItemField */, const char /* aSep */=0) { return false; }; // cannot merge
+ // compare: returns 0 if equal, 1 if this > aItem, -1 if this < aItem
+ // SYSYNC_NOT_COMPARABLE if not equal and no ordering known
+ virtual sInt16 compareWith(TItemField & /* aItemField */, bool /* aCaseInsensitive */) { return SYSYNC_NOT_COMPARABLE; }; // cannot compare
+ // make contents and encoding/charset valid
+ void makeContentsValid(void) { PULLFROMPROXY; };
+ // debug support
+ #ifdef SYDEBUG
+ virtual size_t StringObjFieldAppend(string &s, uInt16 aMaxStrLen); // show field contents as string for debug output
+ #endif
+ // extra info about BLOB contents
+ TEncodingTypes fHasEncoding;
+ TEncodingTypes fWantsEncoding;
+ TCharSets fCharset;
+}; // TBlobField
+
+
+// telephone number string field
+// compares normalized version of number text (only +*# and digits)
+class TTelephoneField: public TStringField
+{
+ typedef TStringField inherited;
+public:
+ TTelephoneField();
+ virtual ~TTelephoneField();
+ // access to type
+ virtual TItemFieldTypes getType(void) const { return fty_telephone; };
+ virtual TItemFieldTypes getCalcType(void) const { return fty_string; };
+ virtual bool isBasedOn(TItemFieldTypes aFieldType) const { return aFieldType==fty_telephone ? true : TStringField::isBasedOn(aFieldType); };
+ // assignment
+ virtual TItemField& operator=(TItemField &aItemField) { return TStringField::operator=(aItemField); };
+ // access to normalized version of content
+ virtual void getAsNormalizedString(string &aString);
+ // compare: returns 0 if equal, 1 if this > aItem, -1 if this < aItem
+ // SYSYNC_NOT_COMPARABLE if not equal and no ordering known
+ virtual sInt16 compareWith(TItemField &aItemField, bool aCaseInsensitive=false);
+}; // TTelephoneField
+
+
+// multiline string field
+// compares without any leading or trailing whitespace, linefeeds, controlchars
+class TMultilineField: public TStringField
+{
+ typedef TStringField inherited;
+public:
+ TMultilineField();
+ virtual ~TMultilineField();
+ // access to type
+ virtual TItemFieldTypes getType(void) const { return fty_multiline; };
+ virtual TItemFieldTypes getCalcType(void) const { return fty_string; };
+ virtual bool isBasedOn(TItemFieldTypes aFieldType) const { return aFieldType==fty_multiline ? true : TStringField::isBasedOn(aFieldType); };
+ // assignment
+ virtual TItemField& operator=(TItemField &aItemField) { return TStringField::operator=(aItemField); };
+ // compare: returns 0 if equal, 1 if this > aItem, -1 if this < aItem
+ // SYSYNC_NOT_COMPARABLE if not equal and no ordering known
+ virtual sInt16 compareWith(TItemField &aItemField, bool aCaseInsensitive=false);
+}; // TMultilineField
+
+
+// URL field
+// normalizes URL (appends http:// if no protocol part specified)
+class TURLField: public TStringField
+{
+ typedef TStringField inherited;
+public:
+ TURLField();
+ virtual ~TURLField();
+ // access to type
+ virtual TItemFieldTypes getType(void) const { return fty_url; };
+ virtual TItemFieldTypes getCalcType(void) const { return fty_string; };
+ virtual bool isBasedOn(TItemFieldTypes aFieldType) const { return aFieldType==fty_url ? true : TStringField::isBasedOn(aFieldType); };
+ // assignment
+ virtual TItemField& operator=(TItemField &aItemField) { return TStringField::operator=(aItemField); };
+ //%%%virtual void setAsString(cAppCharP aString);
+ virtual void stringWasAssigned(void); // post-process string that was just assigned
+}; // TURLField
+
+
+class TTimestampField: public TItemField
+{
+ typedef TItemField inherited;
+public:
+ TTimestampField(GZones *aGZonesP);
+ virtual ~TTimestampField();
+ // access to type
+ virtual TItemFieldTypes getType(void) const { return fty_timestamp; };
+ virtual TItemFieldTypes getCalcType(void) const { return fty_integer; };
+ virtual bool isBasedOn(TItemFieldTypes aFieldType) const { return aFieldType==fty_timestamp ? true : TItemField::isBasedOn(aFieldType); };
+ // assignment
+ virtual TItemField& operator=(TItemField &aItemField);
+ // changelog support
+ #if defined(CHECKSUM_CHANGELOG) && !defined(RECORDHASH_FROM_DBAPI)
+ virtual uInt16 getDataCRC(uInt16 crc=0);
+ #endif
+ // access to field contents
+ virtual void setAsString(cAppCharP aString);
+ virtual void getAsString(string &aString);
+ virtual fieldinteger_t getAsInteger(void) { return (fieldinteger_t)fTimestamp; };
+ virtual void setAsInteger(fieldinteger_t aInteger) { fTimestamp = (lineartime_t)aInteger; }; // does not touch the timecontext!
+ virtual void unAssign(void) { fTimestamp=noLinearTime; fTimecontext=TCTX_UNKNOWN; TItemField::unAssign(); };
+ // empty test (zero timestamp means empty, unless it is a duration)
+ virtual bool isEmpty(void) { return isUnassigned() || (fTimestamp==noLinearTime && !TCTX_IS_DURATION(fTimecontext)); }
+ virtual void assignEmpty(void) { fTimestamp=noLinearTime; fTimecontext=TCTX_UNKNOWN; TItemField::assignEmpty(); };
+ // compare: returns 0 if equal, 1 if this > aItem, -1 if this < aItem
+ // SYSYNC_NOT_COMPARABLE if not equal and no ordering known
+ virtual sInt16 compareWith(TItemField &aItemField, bool aCaseInsensitive=false);
+ // type specific access and utilities
+ /// @brief add a delta time to the timestamp
+ /// @param aDeltaTime[in] : delta time value in lineartime_t units
+ void addTime(lineartime_t aDeltaTime);
+ /// @brief get time context
+ /// @return time context
+ timecontext_t getTimeContext(void) { return fTimecontext; }
+ /// @brief get time context
+ /// @return minute offset east of UTC, returns 0 for floating timestamps (and UTC, of course)
+ sInt16 getMinuteOffset(void);
+ /// @brief test for floating time (=time not in a specified zone context)
+ /// @return true if context is TCTX_UNKNOWN
+ bool isFloating(void);
+ /// @brief make timestamp floating (i.e. remove time zone info from context)
+ void makeFloating(void);
+ /// @brief test for duration
+ /// @return true if context has TCTX_DURATION rendering flag set
+ bool isDuration(void);
+ /// @brief make timestamp a duration (also implies making it floating)
+ void makeDuration(void);
+ /// @brief get timestamp converted to a specified time context
+ /// @param aTargetContext[in] : requests output context for timestamp.
+ /// Use TCTX_UNKNOWN to get timestamp as-is.
+ /// If timestamp is floating, it will always be returned as-is
+ /// @param aActualContext[out] : if not NULL, the actual context of the returned value
+ /// will be returned here. This might be another
+ // context than specified with aTargetContext depending on floating/notime status.
+ /// @return timestamp in lineartime
+ lineartime_t getTimestampAs(timecontext_t aTargetContext, timecontext_t *aActualContext=NULL);
+ /// @brief get timestamp as ISO8601 string.
+ /// @param aISOString[out] : timestamp in ISO8601 format
+ /// @param aTargetContext[in] : requests output context for timestamp. Use TCTX_UNKNOWN to show timestamp as-is.
+ /// @param aShowWithZ[in] : if set and timezone is UTC, value will be shown with "Z" appended
+ /// @param aShowWithZone[in] : if set and timestamp is not floating, zone offset will be appended in +-xx:xx form
+ /// @param aExtFormat[in] : if set, ISO8601 extended format is used
+ /// @param aWithFracSecs[in] : if set, fractions of seconds will be shown (millisecond resolution)
+ void getAsISO8601(string &aISOString, timecontext_t aTargetContext, bool aShowWithZ=true, bool aShowWithZone=false, bool aExtFormat=false, bool aWithFracSecs=false);
+ /// @brief set timestamp value and context
+ /// @param aTimestamp[in] : timestamp to set
+ /// @param aTimecontext[in] : context to set
+ void setTimestampAndContext(lineartime_t aTimestamp, timecontext_t aTimecontext) { setTimestamp(aTimestamp); setTimeContext(aTimecontext); };
+ /// @brief set timestamp value without context
+ /// @param aTimestamp[in] : timestamp to set
+ void setTimestamp(lineartime_t aTimestamp) { fTimestamp=aTimestamp; fAssigned=true; };
+ /// @brief set timestamp value and context
+ /// @param aTimecontext[in] : context to set (timestamp will not be touched or converted)
+ virtual void setTimeContext(timecontext_t aTimecontext) { fTimecontext=aTimecontext; fAssigned=true; };
+ /// @brief move timestamp to specified context (i.e. convert the timestamp value from current to
+ /// specified context). Floating timestamps cannot and will not be moved.
+ /// @param aNewcontext[in] : context to move timestamp to.
+ /// timestamp will be converted to represent the same point in time in the new context
+ /// @param aSetUnmovables : if set, non-movable timestamps will be just assigned the new context,
+ // that is floating timestamps will be bound to specified context or
+ // non-floating timestamps will be made floating if new context is TCTX_UNKNOWN
+ bool moveToContext(timecontext_t aNewcontext, bool aSetUnmovables=false);
+ /// @brief set timestamp from ISO8601 string.
+ /// @return true if successful
+ /// @param aISOString[in] : timestamp in ISO8601 basic or extended format, optionally including Z or +xx:xx zone specifier
+ /// @param aDefaultContext[in] : timezone context to use when ISO8601 does not specify a zone context or when aIgnoreZone is true
+ /// @param aIgnoreZone[in] : if set, timezone specification contained in ISO8601 is ignored. Resulting time context will be aDefaultContext
+ bool setAsISO8601(cAppCharP aISOString, timecontext_t aDefaultContext=TCTX_UNKNOWN, bool aIgnoreZone=false);
+ #ifdef EMAIL_FORMAT_SUPPORT
+ /// @brief get timestamp as RFC(2)822 style date
+ /// @param aRFC822String[out] : timestamp in RFC(2)822 format
+ /// @param aTargetContext[in] : requests output context for timestamp. Use TCTX_UNKNOWN to show timestamp as-is.
+ /// @param aShowWithZone[in] : if set and timestamp is not floating, zone offset will be shown
+ void getAsRFC822date(string &aRFC822String, timecontext_t aTargetContext, bool aShowWithZone=false);
+ /// @brief set timestamp as RFC(2)822 style date
+ /// @return true if successful
+ /// @param aRFC822String[in] : timestamp in RFC(2)822 format
+ /// @param aDefaultContext[in] : timezone context to use when RFC822 date does not specify a time zone
+ /// @param aIgnoreZone[in] : if set, timezone specification contained in input string is ignored. Resulting time context will be aDefaultContext
+ bool setAsRFC822date(cAppCharP aRFC822String, timecontext_t aDefaultContext=TCTX_UNKNOWN, bool aIgnoreZone=false);
+ #endif // EMAIL_FORMAT_SUPPORT
+ // debug support
+ #ifdef SYDEBUG
+ virtual size_t StringObjFieldAppend(string &s, uInt16 aMaxStrLen);
+ #endif
+ GZones *getGZones(void) { return fGZonesP; };
+protected:
+ lineartime_t fTimestamp; // timestamp in context indicated by fTimecontext
+ timecontext_t fTimecontext; // context/options of timestamp
+ GZones *fGZonesP; // zones
+}; // TTimestampField
+
+
+class TDateField: public TTimestampField
+{
+ typedef TTimestampField inherited;
+public:
+ TDateField(GZones *aGZonesP);
+ virtual ~TDateField();
+ // assignment
+ virtual TItemField& operator=(TItemField &aItemField) { return TTimestampField::operator=(aItemField); };
+ // access to type
+ virtual TItemFieldTypes getType(void) const { return fty_date; };
+ virtual TItemFieldTypes getCalcType(void) const { return fty_integer; };
+ virtual bool isBasedOn(TItemFieldTypes aFieldType) const { return aFieldType==fty_date ? true : TTimestampField::isBasedOn(aFieldType); };
+ // access to field contents
+ virtual void setAsString(cAppCharP aString);
+ virtual void getAsString(string &aString);
+ // compare: returns 0 if equal, 1 if this > aItem, -1 if this < aItem
+ // SYSYNC_NOT_COMPARABLE if not equal and no ordering known
+ virtual sInt16 compareWith(TItemField &aItemField, bool aCaseInsensitive=false);
+ /// @brief set timestamp value and context
+ /// @param aTimecontext[in] : context to set (timestamp will not be touched or converted)
+ virtual void setTimeContext(timecontext_t aTimecontext) { fTimecontext=aTimecontext | TCTX_DATEONLY; fAssigned=true; };
+}; // TDateField
+
+
+class TIntegerField: public TItemField
+{
+ typedef TItemField inherited;
+public:
+ TIntegerField();
+ virtual ~TIntegerField();
+ // access to type
+ virtual TItemFieldTypes getType(void) const { return fty_integer; };
+ virtual bool isBasedOn(TItemFieldTypes aFieldType) const { return aFieldType==fty_integer ? true : TItemField::isBasedOn(aFieldType); };
+ // assignment
+ virtual TItemField& operator=(TItemField &aItemField);
+ // changelog support
+ #if defined(CHECKSUM_CHANGELOG) && !defined(RECORDHASH_FROM_DBAPI)
+ virtual uInt16 getDataCRC(uInt16 crc=0);
+ #endif
+ // access to field contents
+ // - as string
+ virtual void setAsString(cAppCharP aString);
+ virtual void getAsString(string &aString);
+ virtual void unAssign(void) { fInteger=0; TItemField::unAssign(); };
+ // - as boolean (empty is false, zero value is false, other values are true)
+ virtual bool getAsBoolean(void) { return !(isEmpty() || fInteger==0); };
+ virtual void setAsBoolean(bool aBool) { fAssigned=true; fEmpty=false; if (aBool) fInteger=1; else fInteger=0; };
+ virtual fieldinteger_t getAsInteger(void);
+ virtual void setAsInteger(fieldinteger_t aInteger);
+ // empty test and assignment
+ virtual bool isEmpty(void) { return isUnassigned() || fEmpty; };
+ virtual void assignEmpty(void) { fInteger=0; fEmpty=true; TItemField::assignEmpty(); };
+ // compare: returns 0 if equal, 1 if this > aItem, -1 if this < aItem
+ // SYSYNC_NOT_COMPARABLE if not equal and no ordering known
+ virtual sInt16 compareWith(TItemField &aItemField, bool aCaseInsensitive=false);
+protected:
+ fieldinteger_t fInteger; // integer value
+ bool fEmpty; // extra empty flag
+}; // TIntegerField
+
+
+
+// factory function
+TItemField *newItemField(const TItemFieldTypes aType, GZones *aGZonesP, bool aAsArray=false);
+
+
+#ifdef ENGINEINTERFACE_SUPPORT
+
+
+// special flags coded into value ID
+#define VALID_FLAG_TZNAME 0x010000
+#define VALID_FLAG_TZOFFS 0x020000
+#define VALID_FLAG_ARRSIZ 0x040000
+#define VALID_FLAG_VALNAME 0x080000
+#define VALID_FLAG_VALTYPE 0x100000
+#define VALID_FLAG_NORM 0x200000
+#define VALID_MASK_FID 0x00FFFF
+
+
+// key for access to a item using the settings key API
+class TItemFieldKey :
+ public TSettingsKeyImpl
+{
+ typedef TSettingsKeyImpl inherited;
+public:
+ TItemFieldKey(TEngineInterface *aEngineInterfaceP) :
+ inherited(aEngineInterfaceP),
+ fWritten(false)
+ {};
+
+ // get value's ID (e.g. internal index)
+ virtual sInt32 GetValueID(cAppCharP aName);
+
+ bool isWritten(void) { return fWritten; };
+protected:
+
+ // get value's native type
+ virtual uInt16 GetValueType(sInt32 aID);
+
+ // get value
+ virtual TSyError GetValueInternal(
+ sInt32 aID, sInt32 aArrayIndex,
+ appPointer aBuffer, memSize aBufSize, memSize &aValSize
+ );
+
+ // set value
+ virtual TSyError SetValueInternal(
+ sInt32 aID, sInt32 aArrayIndex,
+ cAppPointer aBuffer, memSize aValSize
+ );
+
+ // flag that will be set on first write access
+ bool fWritten;
+
+ // abstract methods to actually access a TItemField
+ virtual sInt16 getFidFor(cAppCharP aName, stringSize aNameSz) = 0;
+ virtual TItemField *getBaseFieldFromFid(sInt16 aFid) = 0;
+ virtual bool getFieldNameFromFid(sInt16 aFid, string &aFieldName) { return false; /* no name */ };
+
+private:
+ // utility
+ TItemField *getFieldFromFid(sInt16 aFid, sInt16 aRepOffset, bool aExistingOnly=false);
+
+
+}; // TItemFieldKey
+
+#endif // ENGINEINTERFACE_SUPPORT
+
+
+} // namespace sysync
+
+#endif // ItemField_H
+
+// eof
diff --git a/src/sysync/lineartime.cpp b/src/sysync/lineartime.cpp
new file mode 100755
index 0000000..2a03013
--- /dev/null
+++ b/src/sysync/lineartime.cpp
@@ -0,0 +1,403 @@
+/*
+ * File: lineartime.c
+ *
+ * Author: Lukas Zeller (luz@synthesis.ch)
+ *
+ * conversion from/to linear time scale.
+ *
+ * Copyright (c) 2002-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ * 2002-04-14 : luz : created from pascal source (plani.ch)
+ *
+ */
+#include "prefix_file.h"
+
+#include "lineartime.h"
+#include "timezones.h"
+
+#if defined(SYSYNC_TOOL)
+ #include "syncappbase.h" // for CONSOLEPRINTF
+ #include "vtimezone.h" // for CONSOLEPRINTF
+#endif
+
+namespace sysync {
+
+// Support for SySync Diagnostic Tool
+#ifdef SYSYNC_TOOL
+
+// convert between different time formats and zones
+int timeConv(int argc, const char *argv[])
+{
+ if (argc<0) {
+ // help requested
+ CONSOLEPRINTF((" time <input zone> <output zone> [<input in ISO8601 or lineartime>]"));
+ CONSOLEPRINTF((" Convert between time zone representations:"));
+ CONSOLEPRINTF((" special input zone names: "));
+ CONSOLEPRINTF((" - \"now\" : input is current system time (no <input> required)"));
+ CONSOLEPRINTF((" - \"floating\" : floating time (when no zone from ISO8601 input"));
+ CONSOLEPRINTF((" - \"vtimezone\" : output zone as VTIMEZONE"));
+ return EXIT_SUCCESS;
+ }
+
+ // check for argument
+ if (argc<2 || argc>3) {
+ CONSOLEPRINTF(("2 or 3 arguments required"));
+ return EXIT_FAILURE;
+ }
+ // mode
+ string inzone,outzone,s,z;
+ // internal representation
+ lineartime_t intime;
+ sInt16 minOffs;
+ timecontext_t incontext,outcontext;
+ GZones zones;
+ // get input mode
+ inzone=argv[0];
+ outzone=argv[1];
+ // check special "now" case
+ if (strucmp(inzone.c_str(),"now")==0) {
+ // input time is current time in system zone
+ incontext = TCTX_SYSTEM;
+ intime = getSystemNowAs(incontext,&zones);
+ }
+ else {
+ // get input context from name
+ if (!TimeZoneNameToContext(inzone.c_str(),incontext,&zones))
+ incontext=TCTX_UNKNOWN;
+ // input time from 3rd argument
+ if (argc!=3) {
+ CONSOLEPRINTF(("input time required as 3rd argument"));
+ return EXIT_FAILURE;
+ }
+ // try to parse as ISO8601
+ timecontext_t inpctx;
+ uInt16 n=ISO8601StrToTimestamp(argv[2],intime,inpctx);
+ if (n==0 || argv[2][n]!=0) {
+ // no ISO, read as lineartime
+ inpctx=TCTX_UNKNOWN;
+ n=StrToLongLong(argv[2],intime);
+ if (n==0) {
+ CONSOLEPRINTF(("input time must be either ISO8601 or decimal lineartime units"));
+ return EXIT_FAILURE;
+ }
+ }
+ if (!TCTX_IS_UNKNOWN(inpctx))
+ incontext=inpctx;
+ }
+ // get output context
+ if (strucmp(outzone.c_str(),"vtimezone")==0) {
+ // show input time zone as VTIMEZONE and DAYLIGHT (for current date)
+ intime = getSystemNowAs(incontext,&zones);
+ internalToVTIMEZONE(incontext,z,&zones);
+ CONSOLEPRINTF(("Input time zone represented as VTIMEZONE :\n\nBEGIN:VTIMEZONE\n%sEND:VTIMEZONE\n",z.c_str()));
+ timecontext_t stdoffs;
+ ContextToTzDaylight(incontext,intime,z,stdoffs,&zones);
+ s.erase(); ContextToISO8601StrAppend(s, stdoffs, true);
+ CONSOLEPRINTF(("Input time zone represented as TZ/DAYLIGHT :\n\nTZ:%s\nDAYLIGHT:%s\n",s.c_str(),z.c_str()));
+ }
+ else if (!TimeZoneNameToContext(outzone.c_str(),outcontext,&zones))
+ outcontext=TCTX_UNKNOWN;
+ // now show
+ CONSOLEPRINTF((""));
+ // - input
+ TimestampToISO8601Str(s, intime, incontext, true, true);
+ TimeZoneContextToName(incontext, z, &zones);
+ TzResolveToOffset(incontext, minOffs, intime, false, &zones);
+ CONSOLEPRINTF(("Input : %-25s (%+03hd:%02hd - '%s')", s.c_str(), minOffs/MinsPerHour, abs(minOffs)%MinsPerHour, z.c_str()));
+ // - convert to output
+ if (!TzConvertTimestamp(intime,incontext,outcontext,&zones)) {
+ CONSOLEPRINTF(("input zone cannot be converted to output zone"));
+ return EXIT_FAILURE;
+ }
+ else {
+ // - input
+ TimestampToISO8601Str(s, intime, outcontext, true, true);
+ TimeZoneContextToName(outcontext, z, &zones);
+ TzResolveToOffset(outcontext, minOffs, intime, false, &zones);
+ CONSOLEPRINTF(("Output : %-25s (%+03hd:%02hd - '%s')", s.c_str(), minOffs/MinsPerHour, abs(minOffs)%MinsPerHour, z.c_str()));
+ }
+ return EXIT_SUCCESS;
+} // timeConv
+
+#endif // SYSYNC_TOOL
+
+
+
+
+#ifndef PLATFORM_LROUND
+// use generic implementation of lround
+static sInt32 lround(double x) {
+ sInt32 l;
+ l=(sInt32)(x+0.5);
+ return l;
+}
+#endif
+
+#ifndef PLATFORM_TRUNC
+// use generic implementation of trunc
+static double trunc(double x) {
+ sInt64 ll;
+ ll=(sInt64)(x);
+ return (double)ll;
+}
+#endif
+
+
+#ifndef PLATFORM_DATE2LINEARDATE
+
+// helper: Returns the biggest integer smaller than x
+static sInt32 lfloor(double x) {
+ #if ( defined __MACH__ && defined __GNUC__ ) || defined _MSC_VER // XCode or Visual Studio
+ if (x<0) x= x-1;
+ return lround( x-0.5 );
+ #else
+ return (sInt32)floor( x );
+ #endif
+} // lfloor
+
+// convert date to linear date (generic version using our internal scale)
+/* procedure calcEphTime(year:integer;month,day:byte;hour:single;var julDat:double);
+ { Berechnet Julianisches Datum aus Weltzeit } */
+lineardate_t date2lineardate(sInt16 aYear, sInt16 aMonth, sInt16 aDay)
+{
+ // use custom algorithm that goes back to year -4712...
+
+ /* const MinYear= -4712; */
+ const sInt32 MinYear = -4712;
+
+ /* var a,b:integer; */
+ sInt32 a,b;
+
+ /* if aMonth<3 then begin year:=year-1; aMonth:=aMonth+12; end; */
+ if (aMonth<3) { aYear--; aMonth+=12; }
+ /* if (aYear<1582) or
+ ((aYear=1582) and (aMonth<10)) or
+ ((aYear=1582) and (aMonth=10) and (aDay<15)) */
+ if (
+ aYear<1582 ||
+ (aYear==1582 && aMonth<10) ||
+ (aYear==1582 && aMonth==10 && aDay<15)
+ ) {
+ // julian
+ /* then b:=0 { julianisch } */
+ b=0;
+ }
+ else {
+ // gregorian
+ /* else begin a:=floor(aYear/100); b:=2-a+floor(a/4) end; { gregorianisch } */
+ a=lfloor(aYear/100);
+ b=2-a+lfloor(a/4);
+ }
+ // now calc julian date
+ /*JulDat:=floor(365.25*(aYear-minYear)+1E-6)+round(30.6*(aMonth-3))+aDay+b+58.5;
+ JulDat:=JulDat+hour/24; */
+ return(
+ (lfloor(365.25*(aYear-MinYear)+1E-6)+lround(30.6*(aMonth-3))+aDay+b+59)
+ - linearDateOriginOffset // apply offset used for this target platform
+ );
+} // date2lineardate
+
+#endif // PLATFORM_DATE2LINEARDATE
+
+
+// convert date to linear time
+lineartime_t date2lineartime(sInt16 aYear, sInt16 aMonth, sInt16 aDay)
+{
+ return date2lineardate(aYear,aMonth,aDay) * linearDateToTimeFactor;
+} // date2lineartime
+
+
+// convert time to linear time
+lineartime_t time2lineartime(sInt16 aHour, sInt16 aMinute, sInt16 aSecond, sInt16 aMS)
+{
+ lineartime_t ti =
+ ((((lineartime_t)aHour)*60 +
+ (lineartime_t)aMinute)*60 +
+ (lineartime_t)aSecond)*secondToLinearTimeFactor;
+ if (secondToLinearTimeFactor==1000)
+ ti+=aMS;
+ return ti;
+} // time2lineartime
+
+
+// convert lineardate to weekday
+// 0=sunday, 1=monday ... 6=saturday
+sInt16 lineardate2weekday(lineardate_t aLinearDate)
+{
+ return (aLinearDate+linearDateOriginWeekday) % 7;
+} // lineardate2weekday
+
+
+// convert lineartime to weekday
+// 0=sunday, 1=monday ... 6=saturday
+sInt16 lineartime2weekday(lineartime_t aLinearTime)
+{
+ // juldat seems to be sunday-based :-)
+ return lineardate2weekday(aLinearTime / linearDateToTimeFactor);
+} // lineardate2weekday
+
+
+// get number of days in a month
+sInt16 getMonthDays(lineardate_t aLinearDate)
+{
+ sInt16 y,m,d;
+ lineardate_t ld;
+
+ // get year and month of given date
+ lineardate2date(aLinearDate,&y,&m,&d);
+ // get first of this month
+ ld = date2lineardate(y,m,1);
+ // calculate next month
+ m++;
+ if (m>12) { m=1; y++; }
+ // return difference between 1st of current and 1st of next month = number of days in month
+ return date2lineardate(y,m,1) - ld;
+} // getMonthDays
+
+
+
+#ifndef PLATFORM_LINEARDATE2DATE
+
+// convert lineardate to year/month/day
+/*
+procedure calcDat(zeitZone,julDat:double;var year:integer;var month,day,hour,min:byte;var sec:single);
+{ Berechnet das Kalenderdatum und Weltzeit aus Julianischem Datum }
+*/
+void lineardate2date(lineardate_t aLinearDate,sInt16 *aYearP, sInt16 *aMonthP, sInt16 *aDayP)
+{
+ // custom algorithm
+
+ /*
+ var JD0,JD,C,E:double;
+ B,D,F:integer;
+ hh:single;
+ */
+ double C,E;
+ sInt32 B,D,F;
+
+ // apply offset correction
+ aLinearDate+=linearDateOriginOffset;
+
+ // no time, no "correction" for date change at noon
+ /* JD:=julDat+zeitZone/24;
+ JD0:=sInt32(Jd+0.5); */
+ /*
+ if JD0<2299161 then begin
+ B:=0;
+ C:=JD0+1524;
+ end
+ else begin
+ B:=trunc((JD0-1867216.25)/36524.25);
+ C:=Jd0+(B-trunc(B/4))+1525.0;
+ end;
+ */
+ if (aLinearDate<2299161) {
+ B=0;
+ C=aLinearDate+1524;
+ }
+ else {
+ B=(sInt32)(trunc((aLinearDate-1867216.25)/36524.25));
+ C=aLinearDate+(B-trunc((double)B/4))+1525.0;
+ }
+ /*
+ D:=trunc((C-122.1)/365.25);
+ E:=365.0*D+trunc(D/4);
+ F:=trunc((C-E)/30.6001);
+ day:=trunc(C-E+0.5)-trunc(30.6001*F);
+ month:=F-1-12*trunc(F/14);
+ year:=D-4715-trunc((7+month)/10);
+ */
+ D=(sInt32)(trunc((C-122.1)/365.25));
+ E=365.0*D+trunc((double)D/4);
+ F=(sInt32)(trunc((C-E)/30.6001));
+ // return date
+ sInt16 month = (sInt16)(F-1-12*trunc((double)F/14));
+ if (aDayP) *aDayP=(sInt16)(trunc(C-E+0.5)-trunc(30.6001*F));
+ if (aMonthP) *aMonthP=month;
+ if (aYearP) *aYearP=(sInt16)(D-4715-trunc((double)(7+month)/10.0));
+ // no time
+ /*
+ hh:=24*(JD+0.5-JD0);
+ hour:=trunc(hh);
+ hh:=(hh-hour)*60;
+ min:=trunc(hh);
+ hh:=(hh-min)*60;
+ sec:=trunc(hh);
+ */
+} // lineardate2date
+
+#endif // PLATFORM_LINEARDATE2DATE
+
+
+// convert lineartime to year/month/day
+void lineartime2date(lineartime_t aLinearTime, sInt16 *aYearP, sInt16 *aMonthP, sInt16 *aDayP)
+{
+ lineardate2date(lineartime2dateonly(aLinearTime), aYearP, aMonthP, aDayP);
+} // lineartime2date
+
+
+// convert lineartime to h,m,s,ms
+void lineartime2time(lineartime_t aLinearTime, sInt16 *aHourP, sInt16 *aMinP, sInt16 *aSecP, sInt16 *aMSP)
+{
+ if (aLinearTime<0) {
+ // negative time, create wrap around to make sure time remains positive
+ aLinearTime = lineartime2timeonly(aLinearTime);
+ }
+ if (secondToLinearTimeFactor==1) {
+ // no sub-seconds
+ if (aMSP) *aMSP = 0;
+ }
+ else {
+ // we have sub-seconds
+ if (aMSP) *aMSP = aLinearTime % secondToLinearTimeFactor;
+ aLinearTime /= secondToLinearTimeFactor;
+ }
+ if (aSecP) *aSecP = aLinearTime % 60;
+ aLinearTime /= 60;
+ if (aMinP) *aMinP = aLinearTime % 60;
+ aLinearTime /= 60;
+ if (aHourP) *aHourP = aLinearTime % 24; // to make sure we don't convert date part
+} // lineartime2time
+
+
+// convert seconds to linear time
+lineartime_t seconds2lineartime(sInt32 aSeconds)
+{
+ return aSeconds*secondToLinearTimeFactor;
+} // seconds2lineartime
+
+
+// convert linear time to seconds
+sInt32 lineartime2seconds(lineartime_t aLinearTime)
+{
+ return aLinearTime/secondToLinearTimeFactor;
+} // lineartime2seconds
+
+
+// get time-only part of a linear time
+lineartime_t lineartime2timeonly(lineartime_t aLinearTime)
+{
+ //return aLinearTime % linearDateToTimeFactor;
+ return aLinearTime-lineartime2dateonlyTime(aLinearTime);
+} // lineartime2timeonly
+
+
+// get date-only part of a linear time
+lineardate_t lineartime2dateonly(lineartime_t aLinearTime)
+{
+ return aLinearTime/linearDateToTimeFactor - (aLinearTime<0 ? 1 : 0);
+} // lineartime2dateonly
+
+
+// get date-only part of a linear time, in lineartime_t units
+lineartime_t lineartime2dateonlyTime(lineartime_t aLinearTime)
+{
+ lineartime_t ts = lineartime2dateonly(aLinearTime);
+ ts *= linearDateToTimeFactor;
+ return ts;
+} // lineartime2dateonlyTime
+
+
+} // namespace sysync
+
+/* eof */
diff --git a/src/sysync/lineartime.h b/src/sysync/lineartime.h
new file mode 100755
index 0000000..39c9fe6
--- /dev/null
+++ b/src/sysync/lineartime.h
@@ -0,0 +1,195 @@
+/*
+ * File: lineartime.h
+ *
+ * Author: Lukas Zeller (luz@synthesis.ch)
+ *
+ * conversion from/to linear time scale.
+ *
+ * Copyright (c) 2002-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ * 2002-04-14 : luz : created from pascal source (plani.ch)
+ *
+ */
+
+
+#ifndef LINEARTIME_H
+#define LINEARTIME_H
+
+#include "prefix_file.h"
+#include "generic_types.h"
+
+
+#ifndef PLATFORM_LINEARTIMEDEF
+
+// Standard lineartime_t definition as 64bit integer,
+// in milliseconds since -4712-01-01 00:00:00
+// --------------------------------------------------
+
+#ifdef __cplusplus
+ namespace sysync {
+#endif
+
+// Linear date and time types
+typedef sInt32 lineardate_t;
+typedef sInt64 lineartime_t;
+
+// max and min constants
+const lineartime_t noLinearTime = 0x0; ///< undefined lineartime value
+#ifdef _MSC_VER
+const lineartime_t maxLinearTime = 0x7FFFFFFFFFFFFFFFi64; ///< maximum future lineartime (signed 64 bit)
+#else
+const lineartime_t maxLinearTime = 0x7FFFFFFFFFFFFFFFLL; ///< maximum future lineartime (signed 64 bit)
+#endif
+
+// date origin definition relative to algorithm's origin -4712-01-01 00:00:00
+const lineardate_t linearDateOriginOffset=0; ///< offset between algorithm's origin (-4712-01-01) and lineardate_t's zero
+const sInt16 linearDateOriginWeekday=1; ///< weekday of lineartime origin: Monday
+
+// scaling of lineartime relative to seconds
+const lineartime_t secondToLinearTimeFactor = 1000; ///< how many lineartime_t units make a seconds
+const lineartime_t nanosecondsPerLinearTime = 1000000; ///< duration of one lineartime_t in nanoseconds
+
+
+#ifdef __cplusplus
+ } // namespace sysync
+#endif
+
+#endif // not PLATFORM_LINEARTIMEDEF
+
+
+// the platform specific definitions of the time support
+// Note: if PLATFORM_LINEARTIMEDEF is set, this must define lineartime_t and related
+// constants.
+// If it defines PLATFORM_LINEARDATE2DATE etc.,
+// implementation of these routines must be implemented platform-specific as well.
+#include "platform_time.h"
+
+
+#ifdef __cplusplus
+ namespace sysync {
+#endif
+
+
+#ifdef SYSYNC_TOOL
+
+// convert between different time formats and zones
+int timeConv(int argc, const char *argv[]);
+
+#endif
+
+// Time context type definition. Defined here to avoid mutual inclusion need of
+// this file and timezones.h.
+typedef uInt32 timecontext_t; ///< define a time context (dateonly,time zone, etc.)
+
+
+// Generic utility factors and routines
+// ------------------------------------
+
+/// useful time/date definitions
+const int SecsPerMin = 60;
+const int MinsPerHour= 60;
+const int SecsPerHour= SecsPerMin*MinsPerHour;
+const int HoursPerDay= 24;
+const int DaysPerWk = 7;
+
+
+/// @brief conversion factor for lineardate_t to lineartime_t
+const lineartime_t linearDateToTimeFactor = (secondToLinearTimeFactor*SecsPerHour*HoursPerDay);
+
+/// @brief offset from lineartime_t to UNIX time(), which is based 1970-01-01 00:00:00
+/// @Note units of this constants are still lineartime_t units and need
+/// division by secondToLinearTimeFactor to get actual UNIX time in seconds
+const lineartime_t UnixToLineartimeOffset =
+ (
+ 2440588 // offset between algorithm base and 1970-01-01
+ - linearDateOriginOffset // offset between lineardate_t base and algorithm base
+ ) * linearDateToTimeFactor;
+
+
+/// @brief convert date to linear date
+/// @return specified date converted to lineardate_t (unit=days)
+/// @param[in] aYear,aMonth,aDay : date specification
+lineardate_t date2lineardate(sInt16 aYear, sInt16 aMonth, sInt16 aDay);
+
+/// @brief convert date to linear time
+/// @return specified date converted to lineartime_t (unit=lineartime units)
+/// @param[in] aYear,aMonth,aDay : date specification
+lineartime_t date2lineartime(sInt16 aYear, sInt16 aMonth, sInt16 aDay);
+
+/// @brief convert time to linear time
+/// @return specified time converted to lineartime_t units
+/// @param[in] aMinute,aSecond,aMS : time specification
+lineartime_t time2lineartime(sInt16 aHour, sInt16 aMinute, sInt16 aSecond, sInt16 aMS);
+
+/// @brief convert lineardate to weekday
+/// @return 0=sunday, 1=monday ... 6=saturday
+/// @param[in] aLinearDate linear date (in days)
+sInt16 lineardate2weekday(lineardate_t aLinearDate);
+
+/// @brief convert lineartime to weekday
+/// @return 0=sunday, 1=monday ... 6=saturday
+/// @param[in] aLinearTime linear time (in lineartime_t units)
+sInt16 lineartime2weekday(lineartime_t aLinearTime);
+
+/// @brief convert lineardate to year/month/day
+/// @param[in] aLinearDate linear date (in days)
+/// @param[out] aYearP,aMonthP,aDayP : date components, may be NULL if component not needed
+void lineardate2date(lineardate_t aLinearDate,sInt16 *aYearP, sInt16 *aMonthP, sInt16 *aDayP);
+
+/// @brief convert lineartime to year/month/day
+/// @param[in] aLinearTime linear time (in lineartime_t units)
+/// @param[out] aYearP,aMonthP,aDayP : date components, may be NULL if component not needed
+void lineartime2date(lineartime_t aLinearTime, sInt16 *aYearP, sInt16 *aMonthP, sInt16 *aDayP);
+
+/// @brief get number of days in a month
+/// @return number of days in month (28..31)
+/// @param[in] aLinearDate linear date (in days)
+sInt16 getMonthDays(lineardate_t aLinearDate);
+
+/// @brief convert lineartime to h,m,s,ms
+/// @param[in] aLinearTime linear time (in lineartime_t units)
+/// @param[out] aHourP,aMinP,aSecP,aMSP : time components, may be NULL if component not needed
+void lineartime2time(lineartime_t aLinearTime, sInt16 *aHourP, sInt16 *aMinP, sInt16 *aSecP, sInt16 *aMSP);
+
+/// @brief convert seconds to linear time
+/// @return number of lineartime_t units
+/// @param[in] aSeconds a number of seconds
+lineartime_t seconds2lineartime(sInt32 aSeconds);
+
+/// @brief convert linear time to seconds
+/// @return number of seconds
+/// @param[in] aLinearTime lineartime_t units
+sInt32 lineartime2seconds(lineartime_t aLinearTime);
+
+/// @brief get time-only part of a linear time
+/// @return time only in lineartime_t units since midnight
+/// @param[in] aLinearTime a date/timestamp in lineartime_t units
+lineartime_t lineartime2timeonly(lineartime_t aLinearTime);
+
+/// @brief get date-only part of a linear time
+/// @return date only in lineardate_t units (days)
+/// @param[in] aLinearTime a date/timestamp in lineartime_t units
+lineardate_t lineartime2dateonly(lineartime_t aLinearTime);
+
+/// @brief get date-only part, but IN LINEARTIME
+/// @return date only in lineartime_t units
+/// @param[in] aLinearTime a date/timestamp in lineartime_t units
+lineartime_t lineartime2dateonlyTime(lineartime_t aLinearTime);
+
+
+
+// Implementation of the following routines is platform specific
+// -------------------------------------------------------------
+
+/// @brief fine resolution sleep support
+/// @param[in] aHowLong desired time to wait in lineartime_t units
+void sleepLineartime(lineartime_t aHowLong);
+
+
+#ifdef __cplusplus
+ } // namespace sysync
+#endif
+
+#endif // LINEARTIME_H
+
+/* eof */
diff --git a/src/sysync/localengineds.cpp b/src/sysync/localengineds.cpp
new file mode 100644
index 0000000..ee2d1c5
--- /dev/null
+++ b/src/sysync/localengineds.cpp
@@ -0,0 +1,6358 @@
+/**
+ * @File localengineds.h
+ *
+ * @Author Lukas Zeller (luz@synthesis.ch)
+ *
+ * @brief TLocalEngineDS
+ * Abstraction of the local datastore - interface class to the
+ * sync engine.
+ *
+ * Copyright (c) 2001-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ * @Date 2005-09-15 : luz : created from localdatastore
+ */
+
+// includes
+#include "prefix_file.h"
+#include "sysync.h"
+#include "syncappbase.h"
+#include "localengineds.h"
+#include "scriptcontext.h"
+#include "superdatastore.h"
+
+#ifdef SYSYNC_CLIENT
+#include "syncclient.h"
+#endif
+
+
+using namespace sysync;
+
+namespace sysync {
+
+#ifdef SYDEBUG
+
+cAppCharP const LocalDSStateNames[numDSStates] = {
+ "idle",
+ "client_initialized",
+ "admin_ready",
+ "client_sent_alert",
+ "server_alerted",
+ "server_answered_alert",
+ "client_alert_statused",
+ "client_alerted",
+ "sync_mode_stable",
+ "data_access_started",
+ "sync_set_ready",
+ "client_sync_gen_started",
+ "server_seen_client_mods",
+ "server_sync_gen_started",
+ "sync_gen_done",
+ "data_access_done",
+ "client_maps_sent",
+ "admin_done",
+ "completed"
+};
+#endif
+
+
+#ifdef OBJECT_FILTERING
+
+// add a new expression to an existing filter
+static void addToFilter(const char *aNewFilter, string &aFilter, bool aORChain=false)
+{
+ if (aNewFilter && *aNewFilter) {
+ // just assign if current filter expression is empty
+ if (aFilter.empty())
+ aFilter=aNewFilter;
+ else {
+ // construct new filter
+ StringObjPrintf(aFilter,"(%s)%c(%s)",aFilter.c_str(),aORChain ? '|' : '&',aNewFilter);
+ }
+ }
+} // addToFilter
+
+#endif
+
+#ifdef SCRIPT_SUPPORT
+
+// Script functions
+// ================
+
+class TLDSfuncs {
+public:
+
+ #ifdef OBJECT_FILTERING
+ #ifdef SYNCML_TAF_SUPPORT
+
+ // string GETCGITARGETFILTER()
+ // returns current CGI-specified target address filter expression
+ static void func_GetCGITargetFilter(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ TLocalEngineDS *dsP = static_cast<TLocalEngineDS *>(aFuncContextP->getCallerContext());
+ aTermP->setAsString(dsP->fTargetAddressFilter.c_str());
+ }; // func_GetCGITargetFilter
+
+
+ // string GETTARGETFILTER()
+ // returns current internal target address filter expression
+ static void func_GetTargetFilter(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ TLocalEngineDS *dsP = static_cast<TLocalEngineDS *>(aFuncContextP->getCallerContext());
+ aTermP->setAsString(dsP->fIntTargetAddressFilter.c_str());
+ }; // func_GetTargetFilter
+
+
+ // SETTARGETFILTER(string filter)
+ // sets (overwrites) the internal target address filter
+ static void func_SetTargetFilter(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ TLocalEngineDS *dsP = static_cast<TLocalEngineDS *>(aFuncContextP->getCallerContext());
+ aFuncContextP->getLocalVar(0)->getAsString(dsP->fIntTargetAddressFilter);
+ }; // func_SetTargetFilter
+
+
+ // ADDTARGETFILTER(string filter)
+ // adds a filter expression to the existing internal targetfilter (automatically paranthesizing and adding AND)
+ static void func_AddTargetFilter(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ string f;
+ TLocalEngineDS *dsP = static_cast<TLocalEngineDS *>(aFuncContextP->getCallerContext());
+ aFuncContextP->getLocalVar(0)->getAsString(f);
+ addToFilter(f.c_str(),dsP->fIntTargetAddressFilter,false); // AND-chaining
+ }; // func_AddTargetFilter
+
+ #endif // SYNCML_TAF_SUPPORT
+
+
+ // string GETFILTER()
+ // returns current sync set filter expression
+ static void func_GetFilter(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ TLocalEngineDS *dsP = static_cast<TLocalEngineDS *>(aFuncContextP->getCallerContext());
+ aTermP->setAsString(dsP->fSyncSetFilter.c_str());
+ }; // func_GetFilter
+
+
+ // SETFILTER(string filter)
+ // sets (overwrites) the current sync set filter
+ static void func_SetFilter(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ TLocalEngineDS *dsP = static_cast<TLocalEngineDS *>(aFuncContextP->getCallerContext());
+ aFuncContextP->getLocalVar(0)->getAsString(dsP->fSyncSetFilter);
+ dsP->engFilteredFetchesFromDB(true); // update filter dependencies
+ }; // func_SetFilter
+
+
+ // ADDFILTER(string filter)
+ // adds a filter expression to the existing (dynamic) targetfilter (automatically paranthesizing and adding AND)
+ static void func_AddFilter(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ string f;
+ TLocalEngineDS *dsP = static_cast<TLocalEngineDS *>(aFuncContextP->getCallerContext());
+ aFuncContextP->getLocalVar(0)->getAsString(f);
+ addToFilter(f.c_str(),dsP->fSyncSetFilter,false); // AND-chaining
+ dsP->engFilteredFetchesFromDB(true); // update filter dependencies
+ }; // func_AddFilter
+
+
+ // ADDSTATICFILTER(string filter)
+ // adds a filter expression to the existing (static) localdbfilter (automatically paranthesizing and adding AND)
+ static void func_AddStaticFilter(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ string f;
+ TLocalEngineDS *dsP = static_cast<TLocalEngineDS *>(aFuncContextP->getCallerContext());
+ aFuncContextP->getLocalVar(0)->getAsString(f);
+ addToFilter(f.c_str(),dsP->fLocalDBFilter,false); // AND-chaining
+ dsP->engFilteredFetchesFromDB(true); // update filter dependencies
+ }; // func_AddStaticFilter
+
+ #endif // OBJECT_FILTERING
+
+ #ifdef SYSYNC_TARGET_OPTIONS
+
+ // string DBOPTIONS()
+ // returns current DB options
+ static void func_DBOptions(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ TLocalEngineDS *dsP = static_cast<TLocalEngineDS *>(aFuncContextP->getCallerContext());
+ aTermP->setAsString(dsP->fDBOptions.c_str());
+ }; // func_DBOptions
+
+
+ // integer DBHANDLESOPTS()
+ // returns true if database can completely handle options like /dr() and /li during fetching
+ static void func_DBHandlesOpts(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ aTermP->setAsBoolean(
+ static_cast<TLocalEngineDS *>(aFuncContextP->getCallerContext())->dsOptionFilterFetchesFromDB()
+ );
+ }; // func_DBHandlesOpts
+
+
+ // timestamp STARTDATE()
+ // returns startdate if one is set in datastore
+ static void func_StartDate(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ lineartime_t d = static_cast<TLocalEngineDS *>(aFuncContextP->getCallerContext())->fDateRangeStart;
+ TTimestampField *resP = static_cast<TTimestampField *>(aTermP);
+ if (d==noLinearTime)
+ resP->assignEmpty();
+ else
+ resP->setTimestampAndContext(d,TCTX_UTC);
+ }; // func_StartDate
+
+
+ // SETSTARTDATE(timestamp startdate)
+ // sets startdate for datastore
+ static void func_SetStartDate(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ TTimestampField *tsP = static_cast<TTimestampField *>(aFuncContextP->getLocalVar(0));
+ timecontext_t tctx;
+
+ static_cast<TLocalEngineDS *>(aFuncContextP->getCallerContext())->fDateRangeStart =
+ tsP->getTimestampAs(TCTX_UTC,&tctx); // floating will also be treated as UTC
+ }; // func_SetStartDate
+
+
+ // timestamp ENDDATE()
+ // returns enddate if one is set in datastore
+ static void func_EndDate(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ lineartime_t d = static_cast<TLocalEngineDS *>(aFuncContextP->getCallerContext())->fDateRangeEnd;
+ TTimestampField *resP = static_cast<TTimestampField *>(aTermP);
+ if (d==noLinearTime)
+ resP->assignEmpty();
+ else
+ resP->setTimestampAndContext(d,TCTX_UTC);
+ }; // func_EndDate
+
+
+ // SETENDDATE(timestamp startdate)
+ // sets enddate for datastore
+ static void func_SetEndDate(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ TTimestampField *tsP = static_cast<TTimestampField *>(aFuncContextP->getLocalVar(0));
+ timecontext_t tctx;
+
+ static_cast<TLocalEngineDS *>(aFuncContextP->getCallerContext())->fDateRangeEnd =
+ tsP->getTimestampAs(TCTX_UTC,&tctx); // floating will also be treated as UTC
+ }; // func_SetEndDate
+
+
+ // integer DEFAULTSIZELIMIT()
+ // returns limit set for all items in this datastore (the /li(xxx) CGI option value)
+ static void func_DefaultLimit(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ fieldinteger_t i = static_cast<TLocalEngineDS *>(aFuncContextP->getCallerContext())->fSizeLimit;
+ if (i<0)
+ aTermP->unAssign(); // no limit
+ else
+ aTermP->setAsInteger(i);
+ }; // func_DefaultLimit
+
+
+ // SETDEFAULTSIZELIMIT(integer limit)
+ // sets limit for all items in this datastore (the /li(xxx) CGI option value)
+ static void func_SetDefaultLimit(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ static_cast<TLocalEngineDS *>(aFuncContextP->getCallerContext())->fSizeLimit =
+ aFuncContextP->getLocalVar(0)->getAsInteger();
+ }; // func_SetDefaultLimit
+
+
+ // integer NOATTACHMENTS()
+ // returns true if attachments should be suppressed (/na CGI option)
+ static void func_NoAttachments(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ aTermP->setAsBoolean(
+ static_cast<TLocalEngineDS *>(aFuncContextP->getCallerContext())->fNoAttachments
+ );
+ }; // func_NoAttachments
+
+
+ // SETNOATTACHMENTS(integer flag)
+ // if true, attachments will be suppressed (/na CGI option)
+ static void func_SetNoAttachments(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ static_cast<TLocalEngineDS *>(aFuncContextP->getCallerContext())->fNoAttachments =
+ aFuncContextP->getLocalVar(0)->getAsBoolean();
+ }; // func_SetNoAttachments
+
+
+ // integer MAXITEMCOUNT()
+ // returns item count limit (0=none) as set by /max(n) CGI option
+ static void func_MaxItemCount(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ aTermP->setAsInteger(
+ static_cast<TLocalEngineDS *>(aFuncContextP->getCallerContext())->fMaxItemCount
+ );
+ }; // func_MaxItemCount
+
+
+ // SETMAXITEMCOUNT(integer maxcount)
+ // set item count limit (0=none) as set by /max(n) CGI option
+ static void func_SetMaxItemCount(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ static_cast<TLocalEngineDS *>(aFuncContextP->getCallerContext())->fMaxItemCount =
+ aFuncContextP->getLocalVar(0)->getAsInteger();
+ }; // func_SetMaxItemCount
+
+ #endif // SYSYNC_TARGET_OPTIONS
+
+
+ // string DBNAME()
+ // returns name of DB
+ static void func_DBName(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ aTermP->setAsString(
+ static_cast<TLocalEngineDS *>(aFuncContextP->getCallerContext())->getName()
+ );
+ }; // func_DBName
+
+
+ // integer SLOWSYNC()
+ // returns true if we are in slow sync
+ static void func_SlowSync(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ aTermP->setAsBoolean(
+ static_cast<TLocalEngineDS *>(aFuncContextP->getCallerContext())->isSlowSync()
+ );
+ }; // func_SlowSync
+
+
+ // FORCESLOWSYNC()
+ // force a slow sync (like with /na CGI option)
+ static void func_ForceSlowSync(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ static_cast<TLocalEngineDS *>(aFuncContextP->getCallerContext())->engForceSlowSync();
+ }; // func_ForceSlowSync
+
+
+
+ // integer ALERTCODE()
+ // returns the alert code as currently know by datastore (might change from normal to slow while processing)
+ static void func_AlertCode(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ aTermP->setAsInteger(
+ static_cast<TLocalEngineDS *>(aFuncContextP->getCallerContext())->fAlertCode
+ );
+ }; // func_AlertCode
+
+
+ // SETALERTCODE(integer maxcount)
+ // set the alert code (makes sense in alertscript to modify the incoming code to something different)
+ static void func_SetAlertCode(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ static_cast<TLocalEngineDS *>(aFuncContextP->getCallerContext())->fAlertCode =
+ aFuncContextP->getLocalVar(0)->getAsInteger();
+ }; // func_SetAlertCode
+
+
+
+ // integer REFRESHONLY()
+ // returns true if sync is only refreshing local (note that alert code might be different, as local
+ // refresh can take place without telling the remote so, for compatibility with clients that do not support the mode)
+ static void func_RefreshOnly(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ aTermP->setAsBoolean(
+ static_cast<TLocalEngineDS *>(aFuncContextP->getCallerContext())->isRefreshOnly()
+ );
+ }; // func_RefreshOnly
+
+
+ // SETREFRESHONLY(integer flag)
+ // modifies the refresh only flag (one way sync from remote to local only)
+ // Note that clearing this flag when a client has alerted one-way will probably lead to an error
+ static void func_SetRefreshOnly(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ static_cast<TLocalEngineDS *>(aFuncContextP->getCallerContext())->engSetRefreshOnly(
+ aFuncContextP->getLocalVar(0)->getAsBoolean()
+ );
+ }; // func_SetRefreshOnly
+
+
+ // integer READONLY()
+ // returns true if sync is read-only (only reading from local datastore)
+ static void func_ReadOnly(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ aTermP->setAsBoolean(
+ static_cast<TLocalEngineDS *>(aFuncContextP->getCallerContext())->isReadOnly()
+ );
+ }; // func_ReadOnly
+
+
+ // SETREADONLY(integer flag)
+ // modifies the read only flag (only reading from local datastore)
+ static void func_SetReadOnly(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ static_cast<TLocalEngineDS *>(aFuncContextP->getCallerContext())->engSetReadOnly(
+ aFuncContextP->getLocalVar(0)->getAsBoolean()
+ );
+ }; // func_SetReadOnly
+
+
+
+ // integer FIRSTTIMESYNC()
+ // returns true if we are in first time slow sync
+ static void func_FirstTimeSync(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ aTermP->setAsBoolean(
+ static_cast<TLocalEngineDS *>(aFuncContextP->getCallerContext())->isFirstTimeSync()
+ );
+ }; // func_FirstTimeSync
+
+
+ // void SETCONFLICTSTRATEGY(string strategy)
+ // sets conflict strategy for this session
+ static void func_SetConflictStrategy(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ // convert to syncop
+ string s;
+ aFuncContextP->getLocalVar(0)->getAsString(s);
+ sInt16 strategy;
+ StrToEnum(conflictStrategyNames,numConflictStrategies, strategy, s.c_str());
+ static_cast<TLocalEngineDS *>(aFuncContextP->getCallerContext())->fSessionConflictStrategy =
+ (TConflictResolution) strategy;
+ }; // func_SetConflictStrategy
+
+
+ // string REMOTEDBNAME()
+ // returns remote datastore's full name (as used by the remote in <sync> command, may contain subpath and CGI)
+ static void func_RemoteDBName(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ TLocalEngineDS *dsP = static_cast<TLocalEngineDS *>(aFuncContextP->getCallerContext());
+ aTermP->setAsString(dsP->getRemoteDatastore()->getFullName());
+ }; // func_RemoteDBName
+
+
+ #ifdef SYSYNC_CLIENT
+
+ // ADDTARGETCGI(string cgi)
+ // adds CGI to the target URI. If target URI already contains a ?, string will be just
+ // appended, otherwise a ? is added, then the new CGI.
+ // Note: if string to be added is already contained, it will not be added again
+ static void func_AddTargetCGI(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ TLocalEngineDS *dsP = static_cast<TLocalEngineDS *>(aFuncContextP->getCallerContext());
+ // Add extra CGI specified
+ string cgi;
+ aFuncContextP->getLocalVar(0)->getAsString(cgi);
+ addCGItoString(dsP->fRemoteDBPath,cgi.c_str(),true);
+ }; // func_AddTargetCGI
+
+
+ // SETRECORDFILTER(string filter, boolean inclusive)
+ // Sets record level filter expression for remote
+ static void func_SetRecordFilter(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ TLocalEngineDS *dsP = static_cast<TLocalEngineDS *>(aFuncContextP->getCallerContext());
+ // put into record level filter
+ if (dsP->getSession()->getSyncMLVersion()>=syncml_vers_1_2) {
+ // DS 1.2: use <filter>
+ aFuncContextP->getLocalVar(0)->getAsString(dsP->fRemoteRecordFilterQuery);
+ dsP->fRemoteFilterInclusive = aFuncContextP->getLocalVar(1)->getAsBoolean();
+ }
+ else if (!aFuncContextP->getLocalVar(0)->isEmpty()) {
+ // DS 1.1 and below and not empty filter: add as cgi
+ string filtercgi;
+ if (aFuncContextP->getLocalVar(1)->getAsBoolean())
+ filtercgi = "/tf("; // exclusive, use TAF
+ else
+ filtercgi = "/fi("; // inclusive, use sync set filter
+ aFuncContextP->getLocalVar(0)->appendToString(filtercgi);
+ filtercgi += ')';
+ addCGItoString(dsP->fRemoteDBPath,filtercgi.c_str(),true);
+ }
+ }; // func_SetRecordFilter
+
+
+ // SETDAYSRANGE(integer daysbefore, integer daysafter)
+ // Sets type of record filter
+ static void func_SetDaysRange(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ TLocalEngineDS *dsP = static_cast<TLocalEngineDS *>(aFuncContextP->getCallerContext());
+ // get params
+ int daysbefore = aFuncContextP->getLocalVar(0)->getAsInteger();
+ int daysafter = aFuncContextP->getLocalVar(1)->getAsInteger();
+ // depending on SyncML version, create a SINCE/BEFORE filter or use the /dr(x,y) syntax
+ if (dsP->getSession()->getSyncMLVersion()>=syncml_vers_1_2 && static_cast<TSyncClient *>(dsP->getSession())->fServerHasSINCEBEFORE) {
+ // use the SINCE/BEFORE syntax
+ // BEFORE&EQ;20070808T000000Z&AND;SINCE&EQ;20070807T000000Z
+ lineartime_t now = getSystemNowAs(TCTX_UTC,aFuncContextP->getSessionZones());
+ string ts;
+ // AND-chain with eventually existing filter
+ cAppCharP sep = "";
+ if (!dsP->fRemoteRecordFilterQuery.empty())
+ sep = "&AND;";
+ if (daysbefore>=0) {
+ dsP->fRemoteRecordFilterQuery += sep;
+ dsP->fRemoteRecordFilterQuery += "SINCE&EQ;";
+ TimestampToISO8601Str(ts,now-daysbefore*linearDateToTimeFactor,TCTX_UTC,false,false);
+ dsP->fRemoteRecordFilterQuery += ts;
+ sep = "&AND;";
+ }
+ if (daysafter>=0) {
+ dsP->fRemoteRecordFilterQuery += sep;
+ dsP->fRemoteRecordFilterQuery += "BEFORE&EQ;";
+ TimestampToISO8601Str(ts,now+daysafter*linearDateToTimeFactor,TCTX_UTC,false,false);
+ dsP->fRemoteRecordFilterQuery += ts;
+ }
+ }
+ else {
+ // use the /dr(-x,y) syntax
+ string rangecgi;
+ StringObjPrintf(rangecgi,"/dr(%ld,%ld)",(long int)(-daysbefore),(long int)(daysafter));
+ addCGItoString(dsP->fRemoteDBPath,rangecgi.c_str(),true);
+ }
+ }; // func_SetDaysRange
+
+
+ #endif // SYSYNC_CLIENT
+
+}; // TLDSfuncs
+
+const uInt8 param_FilterArg[] = { VAL(fty_string) };
+const uInt8 param_DateArg[] = { VAL(fty_timestamp) };
+const uInt8 param_IntArg[] = { VAL(fty_integer) };
+const uInt8 param_StrArg[] = { VAL(fty_string) };
+
+const TBuiltInFuncDef DBFuncDefs[] = {
+ #ifdef OBJECT_FILTERING
+ #ifdef SYNCML_TAF_SUPPORT
+ { "GETCGITARGETFILTER", TLDSfuncs::func_GetCGITargetFilter, fty_string, 0, NULL },
+ { "GETTARGETFILTER", TLDSfuncs::func_GetTargetFilter, fty_string, 0, NULL },
+ { "SETTARGETFILTER", TLDSfuncs::func_SetTargetFilter, fty_none, 1, param_FilterArg },
+ { "ADDTARGETFILTER", TLDSfuncs::func_AddTargetFilter, fty_none, 1, param_FilterArg },
+ #endif
+ { "GETFILTER", TLDSfuncs::func_GetFilter, fty_string, 0, NULL },
+ { "SETFILTER", TLDSfuncs::func_SetFilter, fty_none, 1, param_FilterArg },
+ { "ADDFILTER", TLDSfuncs::func_AddFilter, fty_none, 1, param_FilterArg },
+ { "ADDSTATICFILTER", TLDSfuncs::func_AddStaticFilter, fty_none, 1, param_FilterArg },
+ #endif
+ #ifdef SYSYNC_TARGET_OPTIONS
+ { "DBOPTIONS", TLDSfuncs::func_DBOptions, fty_string, 0, NULL },
+ { "STARTDATE", TLDSfuncs::func_StartDate, fty_timestamp, 0, NULL },
+ { "ENDDATE", TLDSfuncs::func_EndDate, fty_timestamp, 0, NULL },
+ { "SETSTARTDATE", TLDSfuncs::func_SetStartDate, fty_none, 1, param_DateArg },
+ { "SETENDDATE", TLDSfuncs::func_SetEndDate, fty_none, 1, param_DateArg },
+ { "MAXITEMCOUNT", TLDSfuncs::func_MaxItemCount, fty_integer, 0, NULL },
+ { "SETMAXITEMCOUNT", TLDSfuncs::func_SetMaxItemCount, fty_none, 1, param_IntArg },
+ { "NOATTACHMENTS", TLDSfuncs::func_NoAttachments, fty_integer, 0, NULL },
+ { "SETNOATTACHMENTS", TLDSfuncs::func_SetNoAttachments, fty_none, 1, param_IntArg },
+ { "DEFAULTSIZELIMIT", TLDSfuncs::func_DefaultLimit, fty_integer, 0, NULL },
+ { "SETDEFAULTSIZELIMIT", TLDSfuncs::func_SetDefaultLimit, fty_none, 1, param_IntArg },
+ { "DBHANDLESOPTS", TLDSfuncs::func_DBHandlesOpts, fty_integer, 0, NULL },
+ #endif
+ { "DBNAME", TLDSfuncs::func_DBName, fty_string, 0, NULL },
+ { "ALERTCODE", TLDSfuncs::func_AlertCode, fty_integer, 0, NULL },
+ { "SETALERTCODE", TLDSfuncs::func_SetAlertCode, fty_none, 1, param_IntArg },
+ { "SLOWSYNC", TLDSfuncs::func_SlowSync, fty_integer, 0, NULL },
+ { "FORCESLOWSYNC", TLDSfuncs::func_ForceSlowSync, fty_none, 0, NULL },
+ { "REFRESHONLY", TLDSfuncs::func_RefreshOnly, fty_integer, 0, NULL },
+ { "SETREFRESHONLY", TLDSfuncs::func_SetRefreshOnly, fty_none, 1, param_IntArg },
+ { "READONLY", TLDSfuncs::func_ReadOnly, fty_integer, 0, NULL },
+ { "SETREADONLY", TLDSfuncs::func_SetReadOnly, fty_none, 1, param_IntArg },
+ { "FIRSTTIMESYNC", TLDSfuncs::func_FirstTimeSync, fty_integer, 0, NULL },
+ { "SETCONFLICTSTRATEGY", TLDSfuncs::func_SetConflictStrategy, fty_none, 1, param_StrArg },
+ { "REMOTEDBNAME", TLDSfuncs::func_RemoteDBName, fty_string, 0, NULL },
+};
+
+// functions for all datastores
+const TFuncTable DBFuncTable = {
+ sizeof(DBFuncDefs) / sizeof(TBuiltInFuncDef), // size of table
+ DBFuncDefs, // table pointer
+ NULL // no chain func
+};
+
+
+#ifdef SYSYNC_CLIENT
+
+const uInt8 param_OneStr[] = { VAL(fty_string) };
+const uInt8 param_OneInt[] = { VAL(fty_integer) };
+const uInt8 param_TwoInt[] = { VAL(fty_integer), VAL(fty_integer) };
+const uInt8 param_SetRecordFilter[] = { VAL(fty_string), VAL(fty_integer) };
+
+const TBuiltInFuncDef ClientDBFuncDefs[] = {
+ { "ADDTARGETCGI", TLDSfuncs::func_AddTargetCGI, fty_none, 1, param_OneStr },
+ { "SETRECORDFILTER", TLDSfuncs::func_SetRecordFilter, fty_none, 2, param_SetRecordFilter },
+ { "SETDAYSRANGE", TLDSfuncs::func_SetDaysRange, fty_none, 2, param_TwoInt },
+};
+
+
+// chain to general DB functions
+static void *ClientDBChainFunc(void *&aCtx)
+{
+ // caller context remains unchanged
+ // -> no change needed
+ // next table is general DS func table
+ return (void *)&DBFuncTable;
+} // ClientDBChainFunc
+
+
+// function table for client-only script functions
+const TFuncTable ClientDBFuncTable = {
+ sizeof(ClientDBFuncDefs) / sizeof(TBuiltInFuncDef), // size of table
+ ClientDBFuncDefs, // table pointer
+ ClientDBChainFunc // chain to general agent funcs.
+};
+
+
+#endif // SYSYNC_CLIENT
+
+#endif // SCRIPT_SUPPORT
+
+
+
+
+// config
+// ======
+
+// conflict strategy names
+// bfo: Problems with XCode (expicit qualification), already within namespace ?
+//const char * const sysync::conflictStrategyNames[numConflictStrategies] = {
+const char * const conflictStrategyNames[numConflictStrategies] = {
+ "duplicate", // add conflicting counterpart to both databases
+ "newer-wins", // newer version wins (if date/version comparison is possible, like sst_duplicate otherwise)
+ "server-wins", // server version wins (and is written to client)
+ "client-wins" // client version wins (and is written to server)
+};
+
+
+// type support config
+TTypeSupportConfig::TTypeSupportConfig(const char* aName, TConfigElement *aParentElement) :
+ TConfigElement(aName,aParentElement)
+{
+ clear();
+} // TTypeSupportConfig::TTypeSupportConfig
+
+
+TTypeSupportConfig::~TTypeSupportConfig()
+{
+ clear();
+} // TTypeSupportConfig::~TTypeSupportConfig
+
+
+// init defaults
+void TTypeSupportConfig::clear(void)
+{
+ // init defaults
+ fPreferredTx = NULL;
+ fPreferredRx = NULL;
+ fPreferredLegacy = NULL;
+ fAdditionalTypes.clear();
+ #ifndef NO_REMOTE_RULES
+ fRuleMatchTypes.clear();
+ #endif
+ // clear inherited
+ inherited::clear();
+} // TTypeSupportConfig::clear
+
+
+#ifdef HARDCODED_CONFIG
+
+// add type support
+bool TTypeSupportConfig::addTypeSupport(
+ cAppCharP aTypeName,
+ bool aForRead,
+ bool aForWrite,
+ bool aPreferred,
+ cAppCharP aVariant,
+ cAppCharP aRuleMatch
+) {
+ // search datatype
+ TDataTypeConfig *typecfgP =
+ static_cast<TRootConfig *>(getRootElement())->fDatatypesConfigP->getDataType(aTypeName);
+ if (!typecfgP) return false;
+ // search variant
+ TTypeVariantDescriptor variantDescP = NULL;
+ if (aVariant && *aVariant)
+ variantDescP = typecfgP->getVariantDescriptor(aVariant);
+ // now add datatype
+ if (aPreferred) {
+ // - preferred
+ if (aForRead) {
+ if (!fPreferredRx) {
+ fPreferredRx=typecfgP; // set it
+ fPrefRxVariantDescP=variantDescP;
+ }
+ }
+ if (aForWrite) {
+ if (!fPreferredTx) {
+ fPreferredTx=typecfgP; // set it
+ fPrefTxVariantDescP=variantDescP;
+ }
+ }
+ } // if preferred
+ else {
+ // - additional
+ TAdditionalDataType adt;
+ adt.datatypeconfig=typecfgP;
+ adt.forRead=aForRead;
+ adt.forWrite=aForWrite;
+ adt.variantDescP=variantDescP; // variant of that type
+ #ifndef NO_REMOTE_RULES
+ if (aRuleMatch) {
+ // this is a rulematch type (which overrides normal type selection mechanism)
+ AssignString(atd.remoteRuleMatch,aRuleMatch); // remote rule match string
+ fRuleMatchTypes.push_back(adt); // save it in the list
+ }
+ else
+ #endif
+ {
+ // standard type
+ fAdditionalTypes.push_back(adt); // save it in the list
+ }
+ }
+ return true;
+} // TTypeSupportConfig::addTypeSupport
+
+#else
+
+// config element parsing
+bool TTypeSupportConfig::localStartElement(const char *aElementName, const char **aAttributes, sInt32 aLine)
+{
+ // checking the elements
+ if (strucmp(aElementName,"use")==0) {
+ expectEmpty(); // datatype usage specs may not have
+ // process arguments
+ const char* nam = getAttr(aAttributes,"datatype");
+ if (!nam)
+ return fail("use must have 'datatype' attribute");
+ // search datatype
+ TDataTypeConfig *typecfgP =
+ static_cast<TRootConfig *>(getRootElement())->fDatatypesConfigP->getDataType(nam);
+ if (!typecfgP)
+ return fail("unknown datatype '%s' specified",nam);
+ #ifndef NO_REMOTE_RULES
+ // get rulematch string, if any
+ cAppCharP ruleMatch = getAttr(aAttributes,"rulematch");
+ #endif
+ // convert variant
+ TTypeVariantDescriptor variantDescP=NULL; // no variant descriptor by default
+ cAppCharP variant = getAttr(aAttributes,"variant");
+ if (variant) {
+ // get a type-specific descriptor which describes the variant of a type to be used with this datastore
+ variantDescP = typecfgP->getVariantDescriptor(variant);
+ if (!variantDescP)
+ return fail("unknown variant '%s' specified",variant);
+ }
+ // convert mode
+ bool rd=true,wr=true;
+ const char* mode = getAttr(aAttributes,"mode");
+ if (mode) {
+ rd=false;
+ wr=false;
+ while (*mode) {
+ if (tolower(*mode)=='r') rd=true;
+ else if (tolower(*mode)=='w') wr=true;
+ else {
+ ReportError(true,"invalid mode '%c'",*mode);
+ return true;
+ }
+ // next char
+ mode++;
+ }
+ if (!rd && !wr)
+ return fail("mode must specify 'r', 'w' or 'rw' at least");
+ }
+ // get preferred
+ bool preferred=false;
+ const char* pref = getAttr(aAttributes,"preferred");
+ if (pref) {
+ if (!StrToBool(pref, preferred)) {
+ if (strucmp(pref,"legacy")==0) {
+ // this is the preferred type for blind and legacy mode sync attempts
+ fPreferredLegacy=typecfgP; // remember (note that there is only ONE preferred type, mode is ignored)
+ preferred=false; // not officially preferred
+ }
+ else
+ return fail("bad value for 'preferred'");
+ }
+ }
+ // now add datatype
+ if (preferred) {
+ // - preferred
+ if (rd) {
+ if (fPreferredRx)
+ return fail("preferred read type already defined");
+ else {
+ fPreferredRx=typecfgP; // set it
+ fPrefRxVariantDescP=variantDescP;
+ }
+ }
+ if (wr) {
+ if (fPreferredTx)
+ return fail("preferred write type already defined");
+ else {
+ fPreferredTx=typecfgP; // set it
+ fPrefTxVariantDescP=variantDescP;
+ }
+ }
+ } // if preferred
+ else {
+ // - additional
+ TAdditionalDataType adt;
+ adt.datatypeconfig=typecfgP;
+ adt.forRead=rd;
+ adt.forWrite=wr;
+ adt.variantDescP=variantDescP;
+ #ifndef NO_REMOTE_RULES
+ if (ruleMatch) {
+ // this is a rulematch type (which overrides normal type selection mechanism)
+ AssignString(adt.remoteRuleMatch,ruleMatch); // remote rule match string
+ fRuleMatchTypes.push_back(adt); // save it in the list
+ }
+ else
+ #endif
+ {
+ // standard type
+ fAdditionalTypes.push_back(adt); // save it in the list
+ }
+ }
+ }
+ // - none known here
+ else
+ return inherited::localStartElement(aElementName,aAttributes,aLine);
+ // ok
+ return true;
+} // TTypeSupportConfig::localStartElement
+
+#endif
+
+
+// resolve
+void TTypeSupportConfig::localResolve(bool aLastPass)
+{
+ #ifndef HARDCODED_CONFIG
+ if (aLastPass) {
+ // check for required settings
+ if (!fPreferredTx || !fPreferredRx)
+ SYSYNC_THROW(TConfigParseException("'typesupport' must contain at least one preferred type for read and write"));
+ }
+ #endif
+ // resolve inherited
+ inherited::localResolve(aLastPass);
+} // TTypeSupportConfig::localResolve
+
+
+
+
+// datastore config
+TLocalDSConfig::TLocalDSConfig(const char* aName, TConfigElement *aParentElement) :
+ TConfigElement(aName,aParentElement),
+ fTypeSupport("typesupport",this)
+{
+ clear();
+} // TLocalDSConfig::TLocalDSConfig
+
+
+TLocalDSConfig::~TLocalDSConfig()
+{
+ // nop so far
+} // TLocalDSConfig::~TLocalDSConfig
+
+
+// init defaults
+void TLocalDSConfig::clear(void)
+{
+ // init defaults
+ // - conflict resolution strategy
+ fConflictStrategy=cr_newer_wins;
+ fSlowSyncStrategy=cr_newer_wins;
+ fFirstTimeStrategy=cr_newer_wins;
+ // options
+ fLocalDBTypeID=0;
+ fReadOnly=false;
+ fReportUpdates=true;
+ fDeleteWins=false; // replace wins over delete by default
+ fResendFailing=true; // resend failing items in next session by default
+ #ifndef SYSYNC_CLIENT
+ fTryUpdateDeleted=false; // no attempt to update already deleted items (assuming they are invisible only)
+ fAlwaysSendLocalID=false; // off as it used to be not SCTS conformant (but would give clients chances to remap IDs)
+ #endif
+ fMaxItemsPerMessage=0; // no limit
+ #ifdef OBJECT_FILTERING
+ // - filters
+ fRemoteAcceptFilter.erase();
+ fLocalDBFilterConf.erase();
+ fMakePassFilter.erase();
+ fInvisibleFilter.erase();
+ fMakeVisibleFilter.erase();
+ // - DS 1.2 Filter support (<filter> allowed in Alert, <filter-rx>/<filterCap> shown in devInf)
+ fDS12FilterSupport=false; // off by default, as clients usually don't have it
+ // - Set if date range support is available in this datastore
+ fDateRangeSupported=false;
+ #endif
+ #ifdef SCRIPT_SUPPORT
+ fDBInitScript.erase();
+ fSentItemStatusScript.erase();
+ fReceivedItemStatusScript.erase();
+ fAlertScript.erase();
+ #ifdef SYSYNC_CLIENT
+ fAlertPrepScript.erase();
+ #endif
+ fDBFinishScript.erase();
+ #endif
+ // clear embedded
+ fTypeSupport.clear();
+ // clear inherited
+ inherited::clear();
+} // TLocalDSConfig::clear
+
+
+#ifndef HARDCODED_CONFIG
+
+// config element parsing
+bool TLocalDSConfig::localStartElement(const char *aElementName, const char **aAttributes, sInt32 aLine)
+{
+ // checking the elements
+ if (strucmp(aElementName,"dbtypeid")==0)
+ expectUInt32(fLocalDBTypeID);
+ else if (strucmp(aElementName,"typesupport")==0)
+ expectChildParsing(fTypeSupport);
+ else if (strucmp(aElementName,"conflictstrategy")==0)
+ expectEnum(sizeof(fConflictStrategy),&fConflictStrategy,conflictStrategyNames,numConflictStrategies);
+ else if (strucmp(aElementName,"slowsyncstrategy")==0)
+ expectEnum(sizeof(fSlowSyncStrategy),&fSlowSyncStrategy,conflictStrategyNames,numConflictStrategies);
+ else if (strucmp(aElementName,"firsttimestrategy")==0)
+ expectEnum(sizeof(fFirstTimeStrategy),&fFirstTimeStrategy,conflictStrategyNames,numConflictStrategies);
+ else if (strucmp(aElementName,"readonly")==0)
+ expectBool(fReadOnly);
+ else if (strucmp(aElementName,"reportupdates")==0)
+ expectBool(fReportUpdates);
+ else if (strucmp(aElementName,"deletewins")==0)
+ expectBool(fDeleteWins);
+ else if (strucmp(aElementName,"resendfailing")==0)
+ expectBool(fResendFailing);
+ #ifndef SYSYNC_CLIENT
+ else if (strucmp(aElementName,"tryupdatedeleted")==0)
+ expectBool(fTryUpdateDeleted);
+ else if (strucmp(aElementName,"alwayssendlocalid")==0)
+ expectBool(fAlwaysSendLocalID);
+ #endif
+ else if (strucmp(aElementName,"maxitemspermessage")==0)
+ expectUInt32(fMaxItemsPerMessage);
+ #ifdef OBJECT_FILTERING
+ // filtering
+ else if (strucmp(aElementName,"acceptfilter")==0)
+ expectString(fRemoteAcceptFilter);
+ else if (strucmp(aElementName,"localdbfilter")==0)
+ expectString(fLocalDBFilterConf);
+ else if (strucmp(aElementName,"makepassfilter")==0)
+ expectString(fMakePassFilter);
+ else if (strucmp(aElementName,"invisiblefilter")==0)
+ expectString(fInvisibleFilter);
+ else if (strucmp(aElementName,"makevisiblefilter")==0)
+ expectString(fMakeVisibleFilter);
+ else if (strucmp(aElementName,"ds12filters")==0)
+ expectBool(fDS12FilterSupport);
+ else if (strucmp(aElementName,"daterangesupport")==0)
+ expectBool(fDateRangeSupported);
+ #endif
+ #ifdef SCRIPT_SUPPORT
+ else if (strucmp(aElementName,"datastoreinitscript")==0)
+ expectScript(fDBInitScript,aLine,&DBFuncTable);
+ else if (strucmp(aElementName,"sentitemstatusscript")==0)
+ expectScript(fSentItemStatusScript,aLine,&ErrorFuncTable);
+ else if (strucmp(aElementName,"receiveditemstatusscript")==0)
+ expectScript(fReceivedItemStatusScript,aLine,&ErrorFuncTable);
+ else if (strucmp(aElementName,"alertscript")==0)
+ expectScript(fAlertScript,aLine,&DBFuncTable);
+ #ifdef SYSYNC_CLIENT
+ else if (strucmp(aElementName,"alertprepscript")==0)
+ expectScript(fAlertPrepScript,aLine,getClientDBFuncTable());
+ #endif
+ else if (strucmp(aElementName,"datastorefinishscript")==0)
+ expectScript(fDBFinishScript,aLine,&DBFuncTable);
+ #endif
+ #ifndef MINIMAL_CODE
+ else if (strucmp(aElementName,"displayname")==0)
+ expectString(fDisplayName);
+ #endif
+ // - none known here
+ else
+ return inherited::localStartElement(aElementName,aAttributes,aLine);
+ // ok
+ return true;
+} // TLocalDSConfig::localStartElement
+
+#endif
+
+// resolve
+void TLocalDSConfig::localResolve(bool aLastPass)
+{
+ if (aLastPass) {
+ #ifdef SCRIPT_SUPPORT
+ TScriptContext *sccP = NULL;
+ SYSYNC_TRY {
+ // resolve all scripts in same context
+ // - first script needed (when alert is created)
+ #ifdef SYSYNC_CLIENT
+ TScriptContext::resolveScript(getSyncAppBase(),fAlertPrepScript,sccP,NULL);
+ #endif
+ // - scripts needed when database is made ready
+ TScriptContext::resolveScript(getSyncAppBase(),fDBInitScript,sccP,NULL);
+ TScriptContext::resolveScript(getSyncAppBase(),fSentItemStatusScript,sccP,NULL);
+ TScriptContext::resolveScript(getSyncAppBase(),fReceivedItemStatusScript,sccP,NULL);
+ TScriptContext::resolveScript(getSyncAppBase(),fAlertScript,sccP,NULL);
+ TScriptContext::resolveScript(getSyncAppBase(),fDBFinishScript,sccP,NULL);
+ // - forget this context
+ if (sccP) delete sccP;
+ }
+ SYSYNC_CATCH (...)
+ if (sccP) delete sccP;
+ SYSYNC_RETHROW;
+ SYSYNC_ENDCATCH
+ #endif
+ }
+ // resolve embedded
+ fTypeSupport.Resolve(aLastPass);
+ // resolve inherited
+ inherited::localResolve(aLastPass);
+} // TLocalDSConfig::localResolve
+
+
+// - add type support to datatstore from config
+void TLocalDSConfig::addTypes(TLocalEngineDS *aDatastore, TSyncSession *aSessionP)
+{
+ TSyncItemType *typeP;
+ TSyncItemType *writetypeP;
+
+ // preferred types, create instances only if not already existing
+ // - preferred receive
+ typeP = aSessionP->findLocalType(fTypeSupport.fPreferredRx);
+ if (!typeP) {
+ typeP = fTypeSupport.fPreferredRx->newSyncItemType(aSessionP,NULL); // local types are never exclusively related to a datastore
+ aSessionP->addLocalItemType(typeP); // add to session
+ }
+ // - preferred send
+ writetypeP = aSessionP->findLocalType(fTypeSupport.fPreferredTx);
+ if (!writetypeP) {
+ writetypeP = fTypeSupport.fPreferredTx->newSyncItemType(aSessionP,NULL); // local types are never exclusively related to a datastore
+ aSessionP->addLocalItemType(writetypeP);
+ }
+ // - set preferred types
+ aDatastore->setPreferredTypes(typeP,writetypeP);
+ // additional types
+ TAdditionalTypesList::iterator pos;
+ for (pos=fTypeSupport.fAdditionalTypes.begin(); pos!=fTypeSupport.fAdditionalTypes.end(); pos++) {
+ // - search for type already created from same config item
+ typeP = aSessionP->findLocalType((*pos).datatypeconfig);
+ if (!typeP) {
+ // - does not exist yet, create the type
+ typeP = (*pos).datatypeconfig->newSyncItemType(aSessionP,NULL); // local types are never exclusively related to a datastore
+ // - add type to session types
+ aSessionP->addLocalItemType(typeP);
+ }
+ // - add type to datastore's supported types
+ aDatastore->addTypeSupport(typeP,(*pos).forRead,(*pos).forWrite);
+ }
+ #ifndef NO_REMOTE_RULES
+ // rulematch types
+ for (pos=fTypeSupport.fRuleMatchTypes.begin(); pos!=fTypeSupport.fRuleMatchTypes.end(); pos++) {
+ // - search for type already created from same config item
+ typeP = aSessionP->findLocalType((*pos).datatypeconfig);
+ if (!typeP) {
+ // - does not exist yet, create the type
+ typeP = (*pos).datatypeconfig->newSyncItemType(aSessionP,NULL); // local types are never exclusively related to a datastore
+ // - add type to session types
+ aSessionP->addLocalItemType(typeP);
+ }
+ // - add type to datastore's rulematch types
+ aDatastore->addRuleMatchTypeSupport(typeP,(*pos).remoteRuleMatch.c_str());
+ }
+ #endif
+ // now apply type limits
+ // Note: this is usually derived, as limits are often defined within the datastore,
+ // not the type itself (however, for hardcoded template-based fieldlists,
+ // the limits are taken from the template, see TLocalDSConfig::addTypeLimits()
+ addTypeLimits(aDatastore, aSessionP);
+} // TLocalDSConfig::addTypes
+
+
+// Add (probably datastore-specific) limits such as MaxSize and NoTruncate to types
+void TLocalDSConfig::addTypeLimits(TLocalEngineDS *aLocalDatastoreP, TSyncSession *aSessionP)
+{
+ // add field size limitations from map to all types
+ TSyncItemTypePContainer::iterator pos;
+ TSyncItemTypePContainer *typesP = &(aLocalDatastoreP->fRxItemTypes);
+ for (uInt8 i=0; i<2; i++) {
+ for (pos=typesP->begin(); pos!=typesP->end(); pos++) {
+ // apply default limits to type (e.g. from hard-coded template in config)
+ (*pos)->addDefaultTypeLimits();
+ }
+ typesP = &(aLocalDatastoreP->fTxItemTypes);
+ }
+} // TLocalDSConfig::addTypeLimits
+
+
+
+
+/*
+ * Implementation of TLocalEngineDS
+ */
+
+
+/// @Note InternalResetDataStore() must also be callable from destructor
+/// (care not to call other objects which will refer to the already
+/// half-destructed datastore!)
+void TLocalEngineDS::InternalResetDataStore(void)
+{
+ // eventually complete, if not already done (should be, by engFinishDataStoreSync() !)
+ if (fLocalDSState>dssta_idle)
+ changeState(dssta_completed); // complete NOW, opportunity to show stats, etc.
+ // switch down to idle
+ changeState(dssta_idle);
+ /// @todo obsolete: fState=dss_idle;
+ fAbortStatusCode=LOCERR_OK; // not aborted yet
+ fLocalAbortCause=true; // assume local cause
+ fRemoteAddingStopped=false;
+ fAlertCode=0; // not yet alerted
+
+ /// Init Sync mode @ref dsSyncMode
+ fSyncMode=smo_twoway; // default to twoway
+ fForceSlowSync=false;
+ fSlowSync=false;
+ fRefreshOnly=false;
+ fReadOnly=false;
+ fReportUpdates=fDSConfigP->fReportUpdates; // update reporting according to what is configured
+ fServerAlerted=false;
+ fResuming=false;
+ #ifdef SUPERDATASTORES
+ fAsSubDatastoreOf=NULL; // is not a subdatastore
+ #endif
+
+
+ /// Init administrative data to defaults @ref dsAdminData
+ // - last
+ fLastRemoteAnchor.erase();
+ fLastLocalAnchor.erase();
+ // - current
+ fNextRemoteAnchor.erase();
+ fNextLocalAnchor.erase();
+ // suspend state
+ fResumeAlertCode=0; // none
+ fPreventResuming=false;
+ // suspend of chunked items
+ fPartialItemState=pi_state_none;
+ fLastSourceURI.erase();
+ fLastTargetURI.erase();
+ fLastItemStatus=0;
+ fPITotalSize=0;
+ fPIStoredSize=0;
+ fPIUnconfirmedSize=0;
+ if (fPIStoredDataAllocated) {
+ smlLibFree(fPIStoredDataP);
+ fPIStoredDataAllocated=false;
+ }
+ fPIStoredDataP=NULL;
+
+ // other state info
+ fFirstTimeSync=false; // not first sync by default
+
+ #ifdef SYSYNC_CLIENT
+ // - maps for add commands
+ fPendingAddMaps.clear();
+ fUnconfirmedMaps.clear();
+ fLastSessionMaps.clear();
+ #else
+ fTempGUIDMap.clear();
+ #endif
+
+
+ /// Init Filtering @ref dsFiltering
+ #ifdef OBJECT_FILTERING
+ // - dynamic sync set filter
+ fSyncSetFilter.erase();
+ // - static filter
+ fLocalDBFilter=fDSConfigP->fLocalDBFilterConf; // use configured localDB filter
+ // - TAF filters
+ #ifdef SYNCML_TAF_SUPPORT
+ fTargetAddressFilter.erase(); // none from <sync> yet
+ fIntTargetAddressFilter.erase(); // none from internal source (script)
+ #endif
+ #ifdef SYSYNC_TARGET_OPTIONS
+ // - Other filtering options
+ fDateRangeStart=0; // no date range
+ fDateRangeEnd=0;
+ fSizeLimit=-1; // no size limit
+ fMaxItemCount=0; // no item count limit
+ fNoAttachments=false; // attachments not suppressed
+ fDBOptions.erase(); // no options
+ #endif
+ // - Filtering requirements
+ fTypeFilteringNeeded=false;
+ fFilteringNeededForAll=false;
+ fFilteringNeeded=false;
+ #endif
+
+ /// Init item processing @ref dsItemProcessing
+ fSessionConflictStrategy=cr_duplicate; // will be updated later when sync mode is known
+ fItemSizeLimit=-1; // no limit yet
+ fCurrentSyncOp = sop_none; // will be set at engProcessItem()
+ fEchoItemOp = sop_none; // will be set at engProcessItem()
+ fItemConflictStrategy=fSessionConflictStrategy; // will be set at engProcessItem()
+ fForceConflict = false; // will be set at engProcessItem()
+ fDeleteWins = false; // will be set at engProcessItem()
+ fRejectStatus = -1; // will be set at engProcessItem()
+ #ifdef SCRIPT_SUPPORT
+ // - delete the script context if any
+ if (fSendingTypeScriptContextP) {
+ delete fSendingTypeScriptContextP;
+ fSendingTypeScriptContextP=NULL;
+ }
+ if (fReceivingTypeScriptContextP) {
+ delete fReceivingTypeScriptContextP;
+ fReceivingTypeScriptContextP=NULL;
+ }
+ if (fDataStoreScriptContextP) {
+ delete fDataStoreScriptContextP;
+ fDataStoreScriptContextP=NULL;
+ }
+ #endif
+
+ /// Init other vars @ref dsOther
+
+
+ /// Init Counters and statistics @ref dsCountStats
+ // - NOC from remote
+ fRemoteNumberOfChanges=-1; // none known yet
+ // - data transferred
+ fIncomingDataBytes=0;
+ fOutgoingDataBytes=0;
+ // - locally performed ops
+ fLocalItemsAdded=0;
+ fLocalItemsUpdated=0;
+ fLocalItemsDeleted=0;
+ fLocalItemsError=0;
+ // - remotely performed ops
+ fRemoteItemsAdded=0;
+ fRemoteItemsUpdated=0;
+ fRemoteItemsDeleted=0;
+ fRemoteItemsError=0;
+ #ifndef SYSYNC_CLIENT
+ // - conflicts
+ fConflictsServerWins=0;
+ fConflictsClientWins=0;
+ fConflictsDuplicated=0;
+ // - slow sync matches
+ fSlowSyncMatches=0;
+ #endif
+
+ // Override defaults from ancestor
+ // - generally, limit GUID size to reasonable size (even if we can
+ // theoretically handle unlimited GUIDs in client, except for
+ // some DEBUGPRINTF statements that will crash for example with
+ // the brain-damaged GUIDs that Exchange server uses.
+ fMaxGUIDSize = 64;
+} // TLocalEngineDS::InternalResetDataStore
+
+
+/// constructor
+TLocalEngineDS::TLocalEngineDS(TLocalDSConfig *aDSConfigP, TSyncSession *aSessionP, const char *aName, uInt32 aCommonSyncCapMask) :
+ TSyncDataStore(aSessionP, aName, aCommonSyncCapMask)
+ ,fPIStoredDataP(NULL)
+ ,fPIStoredDataAllocated(false)
+ #ifdef SCRIPT_SUPPORT
+ ,fSendingTypeScriptContextP(NULL) // no associated script context
+ ,fReceivingTypeScriptContextP(NULL) // no associated script context
+ ,fDataStoreScriptContextP(NULL) // no datastore level context
+ #endif
+ ,fRemoteDatastoreP(NULL) // no associated remote
+{
+ // set config ptr
+ fDSConfigP = aDSConfigP;
+ if (!fDSConfigP)
+ SYSYNC_THROW(TSyncException(DEBUGTEXT("TLocalEngineDS::TLocalEngineDS called with NULL config","lds1")));
+ /// Init Sync state @ref dsSyncState
+ fLocalDSState=dssta_idle; // idle to begin with
+ // now reset
+ InternalResetDataStore();
+} // TLocalEngineDS::TLocalEngineDS
+
+
+
+
+TLocalEngineDS::~TLocalEngineDS()
+{
+ // reset everything
+ InternalResetDataStore();
+} // TLocalEngineDS::~TLocalEngineDS
+
+
+#ifdef SYDEBUG
+
+// return datastore state name
+cAppCharP TLocalEngineDS::getDSStateName(void)
+{
+ return LocalDSStateNames[fLocalDSState];
+} // TLocalEngineDS::getDSStateName
+
+
+// return datastore state name
+cAppCharP TLocalEngineDS::getDSStateName(TLocalEngineDSState aState)
+{
+ return LocalDSStateNames[aState];
+} // TLocalEngineDS::getDSStateName
+
+#endif
+
+// reset datastore (after use)
+void TLocalEngineDS::engResetDataStore(void)
+{
+ // now reset
+ // - logic layer and above
+ dsResetDataStore();
+ // - myself
+ InternalResetDataStore();
+ // - anchestors
+ inherited::engResetDataStore();
+} // TLocalEngineDS::engResetDataStore
+
+
+
+// check if this datastore is accessible with given URI
+// NOTE: By default, local datastore type is addressed with
+// first path element of URI, rest of path might be used
+// by derivates to subselect data folders etc.
+uInt16 TLocalEngineDS::isDatastore(const char *aDatastoreURI)
+{
+ // extract base name
+ string basename;
+ analyzeName(aDatastoreURI,&basename);
+ // compare only base name
+ return TSyncDataStore::isDatastore(basename.c_str());
+} // TLocalEngineDS::isDatastore
+
+
+#ifdef SYSYNC_CLIENT
+
+// - init Sync Parameters (client case)
+// Derivates might override this to pre-process and modify parameters
+// (such as adding client settings as CGI to remoteDBPath)
+bool TLocalEngineDS::dsSetClientSyncParams(
+ TSyncModes aSyncMode,
+ bool aSlowSync,
+ const char *aRemoteDBPath,
+ const char *aDBUser,
+ const char *aDBPassword,
+ const char *aLocalPathExtension,
+ const char *aRecordFilterQuery,
+ bool aFilterInclusive
+)
+{
+ // sync mode
+ fSyncMode=aSyncMode;
+ fSlowSync=aSlowSync;
+ fRefreshOnly=fSyncMode==smo_fromserver; // here to make sure, should be set by setSyncMode(FromAlertCode) later anyway
+ // DS 1.2 filters
+ AssignString(fRemoteRecordFilterQuery,aRecordFilterQuery);
+ fRemoteFilterInclusive=aFilterInclusive;
+ // params
+ // - build local path
+ fLocalDBPath=URI_RELPREFIX;
+ fLocalDBPath+=getName();
+ if (aLocalPathExtension && *aLocalPathExtension) {
+ fLocalDBPath+='/';
+ fLocalDBPath+=aLocalPathExtension;
+ }
+ // - set remote params
+ fRemoteDBPath=aRemoteDBPath;
+ AssignString(fDBUser,aDBUser);
+ AssignString(fDBPassword,aDBPassword);
+ // - we have the params for syncing now
+ return changeState(dssta_clientparamset)==LOCERR_OK;
+} // TLocalEngineDS::dsSetClientSyncParams
+
+#endif
+
+
+
+// add support for more data types
+// (for programatically creating local datastores from specialized TSyncSession derivates)
+void TLocalEngineDS::addTypeSupport(TSyncItemType *aItemTypeP,bool aForRx, bool aForTx)
+{
+ if (aForRx) fRxItemTypes.push_back(aItemTypeP);
+ if (aForTx) fTxItemTypes.push_back(aItemTypeP);
+} // TLocalEngineDS::addTypeSupport
+
+
+#ifndef NO_REMOTE_RULES
+// add data type that overrides normal type selection if string matches active remote rule
+void TLocalEngineDS::addRuleMatchTypeSupport(TSyncItemType *aItemTypeP,cAppCharP aRuleMatchString)
+{
+ TRuleMatchTypeEntry rme;
+ rme.itemTypeP = aItemTypeP;
+ rme.ruleMatchString = aRuleMatchString;
+ fRuleMatchItemTypes.push_back(rme);
+} // TLocalEngineDS::addRuleMatchTypeSupport
+#endif
+
+
+TTypeVariantDescriptor TLocalEngineDS::getVariantDescForType(TSyncItemType *aItemTypeP)
+{
+ // search in config for specific variant descriptor
+ // - first check preferred rx
+ if (fDSConfigP->fTypeSupport.fPreferredRx == aItemTypeP->getTypeConfig())
+ return fDSConfigP->fTypeSupport.fPrefRxVariantDescP;
+ // - then check preferred tx
+ if (fDSConfigP->fTypeSupport.fPreferredTx == aItemTypeP->getTypeConfig())
+ return fDSConfigP->fTypeSupport.fPrefTxVariantDescP;
+ // - then check additional types
+ TAdditionalTypesList::iterator pos;
+ for (pos=fDSConfigP->fTypeSupport.fAdditionalTypes.begin(); pos!=fDSConfigP->fTypeSupport.fAdditionalTypes.end(); pos++) {
+ if ((*pos).datatypeconfig == aItemTypeP->getTypeConfig())
+ return (*pos).variantDescP;
+ }
+ // none found
+ return NULL;
+} // TLocalEngineDS::getVariantDescForType
+
+
+
+
+// - called when a item in the sync set changes its localID (due to local DB internals)
+void TLocalEngineDS::dsLocalIdHasChanged(const char *aOldID, const char *aNewID)
+{
+ #ifndef SYSYNC_CLIENT
+ // make sure remapped localIDs get updated as well
+ TStringToStringMap::iterator pos;
+ for (pos=fTempGUIDMap.begin(); pos!=fTempGUIDMap.end(); pos++) {
+ if (pos->second == aOldID) {
+ // update ID
+ pos->second = aNewID;
+ break;
+ }
+ }
+ #endif
+} // TLocalEngineDS::dsLocalIdHasChanged
+
+
+#ifndef SYSYNC_CLIENT
+
+// for received GUIDs (Map command), obtain real GUID (might be temp GUID due to maxguidsize restrictions)
+void TLocalEngineDS::obtainRealLocalID(string &aLocalID)
+{
+ if (aLocalID.size()>0 && aLocalID[0]=='#') {
+ // seems to be a temp GUID
+ TStringToStringMap::iterator pos =
+ fTempGUIDMap.find(aLocalID);
+ if (pos!=fTempGUIDMap.end()) {
+ // found temp GUID mapping, replace it
+ PDEBUGPRINTFX(DBG_DATA,(
+ "translated tempLocalID='%s' back to real localID='%s'",
+ aLocalID.c_str(),
+ (*pos).second.c_str()
+ ));
+ aLocalID = (*pos).second;
+ }
+ else {
+ PDEBUGPRINTFX(DBG_ERROR,("No realLocalID found for tempLocalID='%s'",aLocalID.c_str()));
+ }
+ }
+} // TLocalEngineDS::obtainRealLocalID
+
+
+// for sending GUIDs (Add command), generate temp GUID which conforms to maxguidsize of remote datastore if needed
+void TLocalEngineDS::adjustLocalIDforSize(string &aLocalID, sInt32 maxguidsize, sInt32 prefixsize)
+{
+ if (maxguidsize>0) {
+ if (aLocalID.length()+prefixsize>(uInt32)maxguidsize) { //BCPPB needed unsigned cast
+ // real GUID is too long, we need to create a temp
+ string tempguid;
+ StringObjPrintf(tempguid,"#%ld",fTempGUIDMap.size()+1); // as list only grows, we have unique tempuids for sure
+ fTempGUIDMap[tempguid]=aLocalID;
+ PDEBUGPRINTFX(DBG_DATA,(
+ "translated realLocalID='%s' to tempLocalID='%s'",
+ aLocalID.c_str(),
+ tempguid.c_str()
+ ));
+ aLocalID=tempguid;
+ }
+ }
+} // TLocalEngineDS::adjustLocalIDforSize
+
+#endif
+
+
+// set Sync types needed for sending local data to remote DB
+void TLocalEngineDS::setSendTypeInfo(
+ TSyncItemType *aLocalSendToRemoteTypeP,
+ TSyncItemType *aRemoteReceiveFromLocalTypeP
+)
+{
+ fLocalSendToRemoteTypeP=aLocalSendToRemoteTypeP;
+ fRemoteReceiveFromLocalTypeP=aRemoteReceiveFromLocalTypeP;
+} // TLocalEngineDS::setSendTypeInfo
+
+
+// set Sync types needed for receiving remote data in local DB
+void TLocalEngineDS::setReceiveTypeInfo(
+ TSyncItemType *aLocalReceiveFromRemoteTypeP,
+ TSyncItemType *aRemoteSendToLocalTypeP
+)
+{
+ fLocalReceiveFromRemoteTypeP=aLocalReceiveFromRemoteTypeP;
+ fRemoteSendToLocalTypeP=aRemoteSendToLocalTypeP;
+} // TLocalEngineDS::setReceiveTypeInfo
+
+
+// - init usage of datatypes set with setSendTypeInfo/setReceiveTypeInfo
+localstatus TLocalEngineDS::initDataTypeUse(void)
+{
+ localstatus sta = LOCERR_OK;
+
+ // check compatibility
+ if (
+ !fLocalSendToRemoteTypeP || !fLocalReceiveFromRemoteTypeP ||
+ !fLocalSendToRemoteTypeP->isCompatibleWith(fLocalReceiveFromRemoteTypeP)
+ ) {
+ // send and receive types not compatible
+ sta = 415;
+ PDEBUGPRINTFX(DBG_ERROR,("Incompatible send and receive types -> cannot sync (415)"));
+ engAbortDataStoreSync(sta,true,false); // do not proceed with sync of this datastore, local problem, not resumable
+ return sta;
+ }
+ #ifdef SCRIPT_SUPPORT
+ // let types initialize themselves for being used by this datastore
+ // - optimization: if both types are same, initialize only once
+ if (fLocalSendToRemoteTypeP == fLocalReceiveFromRemoteTypeP)
+ fLocalReceiveFromRemoteTypeP->initDataTypeUse(this,true,true); // send and receive
+ else {
+ fLocalSendToRemoteTypeP->initDataTypeUse(this,true,false); // for sending, not receiving
+ fLocalReceiveFromRemoteTypeP->initDataTypeUse(this,false,true); // not sending, for receiving
+ }
+ #endif
+ // ok
+ return sta;
+} // TLocalEngineDS::initDataTypeUse
+
+
+
+// conflict resolution strategy. Conservative here
+TConflictResolution TLocalEngineDS::getConflictStrategy(bool aForSlowSync, bool aForFirstTime)
+{
+ return aForSlowSync ?
+ (aForFirstTime ? fDSConfigP->fFirstTimeStrategy : fDSConfigP->fSlowSyncStrategy) :
+ fDSConfigP->fConflictStrategy;
+} // TLocalEngineDS::getConflictStrategy
+
+
+
+// add filter keywords and property names to filterCap
+void TLocalEngineDS::addFilterCapPropsAndKeywords(SmlPcdataListPtr_t &aFilterKeywords, SmlPcdataListPtr_t &aFilterProps)
+{
+ #ifdef OBJECT_FILTERING
+ // add my own properties
+ if (fDSConfigP->fDateRangeSupported) {
+ addPCDataStringToList("BEFORE", &aFilterKeywords);
+ addPCDataStringToList("SINCE", &aFilterKeywords);
+ }
+ // get default send type
+ TSyncItemType *itemTypeP = fSessionP->findLocalType(fDSConfigP->fTypeSupport.fPreferredTx);
+ TTypeVariantDescriptor variantDesc = NULL;
+ doesUseType(itemTypeP, &variantDesc);
+ // have it add it's keywords and properties
+ itemTypeP->addFilterCapPropsAndKeywords(aFilterKeywords,aFilterProps,variantDesc);
+ #endif
+} // TLocalEngineDS::addFilterCapPropsAndKeywords
+
+
+
+#ifdef OBJECT_FILTERING
+
+
+/// @brief check single filter term for DS 1.2 filterkeywords.
+/// @return true if term still needs to be added to normal filter expression, false if term will be handled otherwise
+bool TLocalEngineDS::checkFilterkeywordTerm(
+ cAppCharP aIdent, bool aAssignToMakeTrue,
+ cAppCharP aOp, bool aCaseInsensitive,
+ cAppCharP aVal, bool aSpecialValue,
+ TSyncItemType *aItemTypeP
+)
+{
+ // show it to the datatype (if any)
+ if (aItemTypeP) {
+ if (!aItemTypeP->checkFilterkeywordTerm(aIdent, aAssignToMakeTrue, aOp, aCaseInsensitive, aVal, aSpecialValue))
+ return false; // type fully handles it, no need to check it further or add it to the filter expression
+ }
+ // we generally implement BEFORE and SINCE on the datastore level
+ // This might not make sense depending on the actual datatype, but does not harm either
+ #ifdef SYSYNC_TARGET_OPTIONS
+ timecontext_t tctx;
+
+ if (strucmp(aIdent,"BEFORE")==0) {
+ if (ISO8601StrToTimestamp(aVal,fDateRangeEnd,tctx)) {
+ TzConvertTimestamp(fDateRangeEnd,tctx,TCTX_UTC,getSessionZones(),fSessionP->fUserTimeContext);
+ }
+ else {
+ PDEBUGPRINTFX(DBG_ERROR,("invalid ISO datetime for BEFORE: '%s'",aVal));
+ return true; // add it to filter, eventually this is not meant to be a filterkeyword
+ }
+ }
+ else if (strucmp(aIdent,"SINCE")==0) {
+ if (ISO8601StrToTimestamp(aVal,fDateRangeStart,tctx)) {
+ TzConvertTimestamp(fDateRangeStart,tctx,TCTX_UTC,getSessionZones(),fSessionP->fUserTimeContext);
+ }
+ else {
+ PDEBUGPRINTFX(DBG_ERROR,("invalid ISO datetime for SINCE: '%s'",aVal));
+ return true; // add it to filter, eventually this is not meant to be a filterkeyword
+ }
+ }
+ else if (strucmp(aIdent,"MAXSIZE")==0) {
+ if (StrToFieldinteger(aVal,fSizeLimit)==0) {
+ PDEBUGPRINTFX(DBG_ERROR,("invalid integer for MAXSIZE: '%s'",aVal));
+ return true; // add it to filter, eventually this is not meant to be a filterkeyword
+ }
+ }
+ else if (strucmp(aIdent,"MAXCOUNT")==0) {
+ if (StrToULong(aVal,fMaxItemCount)==0) {
+ PDEBUGPRINTFX(DBG_ERROR,("invalid integer for MAXSIZE: '%s'",aVal));
+ return true; // add it to filter, eventually this is not meant to be a filterkeyword
+ }
+ }
+ else if (strucmp(aIdent,"NOATT")==0) {
+ if (!StrToBool(aVal,fNoAttachments)==0) {
+ PDEBUGPRINTFX(DBG_ERROR,("invalid boolean for NOATT: '%s'",aVal));
+ return true; // add it to filter, eventually this is not meant to be a filterkeyword
+ }
+ }
+ else if (strucmp(aIdent,"DBOPTIONS")==0) {
+ fDBOptions = aVal; // just get DB options
+ }
+ #endif
+ else {
+ // unknown identifier, add to filter expression
+ return true;
+ }
+ // this term will be processed by special mechanism like fDateRangeStart/fDateRangeEnd
+ // or fSizeLimit, so there is no need for normal filtering
+ return false; // do not include into filter
+} // TLocalEngineDS::checkFilterkeywordTerm
+
+
+/// @brief parse "syncml:filtertype-cgi" filter, convert into internal filter syntax
+/// and eventually sets some special filter options (fDateRangeStart, fDateRangeEnd)
+/// based on "filterkeywords" available for the type passed (DS 1.2).
+/// For parsing DS 1.1/1.0 TAF-style filters, aItemType can be NULL, no type-specific
+/// filterkeywords can be parsed then.
+/// @return pointer to next character after processing (usually points to terminator)
+/// @param[in] aCGI the NUL-terminated filter string
+/// @param[in] aItemTypeP if not NULL, this is the item type the filter applies to
+const char *TLocalEngineDS::parseFilterCGI(cAppCharP aCGI, TSyncItemType *aItemTypeP, string &aFilter)
+{
+ const char *p=aCGI, *q;
+ sInt16 paraNest=0; // nested paranthesis
+ string ident;
+ char op[3];
+ char logop;
+ string val;
+ bool termtofilter;
+ bool assigntomaketrue;
+ bool specialvalue;
+ bool caseinsensitive;
+
+ PDEBUGPRINTFX(DBG_FILTER+DBG_EXOTIC,("Parsing syncml:filtertype-cgi filter: %s",aCGI));
+ aFilter.erase();
+ logop=0;
+ while (p && *p) {
+ if (aFilter.empty()) logop=0; // ignore logical operation that would be at beginning of an expression
+ // skip spaces
+ while (isspace(*p)) p++;
+ // now we need an ident or paranthesis
+ if (*p=='(') {
+ if (logop) aFilter+=logop; // expression continues, we need the logop now
+ logop=0; // now consumed
+ paraNest++;
+ aFilter+='(';
+ }
+ else {
+ // must be term: ident op val
+ // - check special case pseudo-identifiers
+ if (strucmp(p,"&LUID;",6)==0) {
+ ident="LUID";
+ p+=6;
+ }
+ else {
+ // normal identifier
+ // - search end
+ q=p;
+ while (isalnum(*q) || *q=='[' || *q==']' || *q=='.' || *q=='_') q++;
+ // - assign
+ if (q==p) {
+ PDEBUGPRINTFX(DBG_ERROR,("Expected identifier but found '%s'",p));
+ break;
+ }
+ ident.assign(p,q-p);
+ p=q;
+ }
+ // skip spaces
+ while (isspace(*p)) p++;
+ // next must be comparison operator, eventually preceeded by modifiers
+ op[0]=0; op[1]=0; op[2]=0;
+ assigntomaketrue=false;
+ specialvalue=false;
+ caseinsensitive=false;
+ // - check modifiers first
+ if (*p==':') { assigntomaketrue=true; p++; }
+ if (*p=='*') { specialvalue=true; p++; }
+ if (*p=='^') { caseinsensitive=true; p++; }
+ // - now OP either in internal form or as pseudo-entity
+ if (*p=='>' || *p=='<') {
+ // possible two-char ops (>=, <=, <>)
+ op[0]=*p++;
+ if (*p=='>' || *p=='=') {
+ op[1]=*p++;
+ }
+ }
+ else if (*p=='=' || *p=='%' || *p=='$') {
+ // single char ops, just use them as is
+ op[0]=*p++;
+ }
+ else if (*p=='&') {
+ p++;
+ if (tolower(*p)=='i') { caseinsensitive=true; p++; }
+ if (strucmp(p,"eq;",3)==0) { op[0]='='; p+=3; }
+ else if (strucmp(p,"gt;",3)==0) { op[0]='>'; p+=3; }
+ else if (strucmp(p,"ge;",3)==0) { op[0]='>'; op[1]='='; p+=3; }
+ else if (strucmp(p,"lt;",3)==0) { op[0]='<'; p+=3; }
+ else if (strucmp(p,"le;",3)==0) { op[0]='<'; op[1]='='; p+=3; }
+ else if (strucmp(p,"ne;",3)==0) { op[0]='<'; op[1]='>'; p+=3; }
+ else if (strucmp(p,"con;",4)==0) { op[0]='%'; p+=4; }
+ else if (strucmp(p,"ncon;",5)==0) { op[0]='$'; p+=5; }
+ else {
+ PDEBUGPRINTFX(DBG_ERROR,("Expected comparison operator pseudo-entity but found '%s'",p-1));
+ break;
+ }
+ }
+ else {
+ PDEBUGPRINTFX(DBG_ERROR,("Expected comparison operator but found '%s'",p));
+ break;
+ }
+ // next must be value
+ // - check for special value cases
+ if (strucmp(p,"&NULL;",6)==0) {
+ // SyncML DS 1.2
+ p+=6;
+ val='E';
+ specialvalue=true;
+ }
+ else if (strucmp(p,"&UNASSIGNED;",12)==0) {
+ // Synthesis extension
+ p+=12;
+ val='U';
+ specialvalue=true;
+ }
+ else {
+ val.erase();
+ }
+ // - get value chars
+ while (*p && *p!='&' && *p!='|' && *p!=')') {
+ // value char, possibly hex escaped
+ uInt16 c;
+ if (*p=='%') {
+ // convert from hex
+ if (HexStrToUShort(p+1,c,2)==2) {
+ p+=3;
+ }
+ else
+ c=*p++;
+ }
+ else
+ c=*p++;
+ // add to value
+ val += (char)c;
+ } // value
+ // now we have identifier, op and value
+ // - check and eventually sort out filterkeyword terms
+ termtofilter = checkFilterkeywordTerm(ident.c_str(),assigntomaketrue,op,caseinsensitive,val.c_str(),specialvalue,aItemTypeP);
+ // - add to filter if not handled already by other mechanism
+ if (termtofilter) {
+ if (logop) aFilter+=logop; // if this is a continuation, add logical operator now
+ aFilter += ident;
+ if (assigntomaketrue) aFilter+=':';
+ if (specialvalue) aFilter+='*';
+ if (caseinsensitive) aFilter+='^';
+ aFilter += op;
+ aFilter += val;
+ }
+ else {
+ PDEBUGPRINTFX(DBG_FILTER+DBG_EXOTIC,(
+ "checkFilterkeywordTerm(%s,%hd,%s,%hd,%s,%hd) prevents adding to filter",
+ ident.c_str(),(uInt16)assigntomaketrue,op,(uInt16)caseinsensitive,val.c_str(),(uInt16)specialvalue
+ ));
+ if (logop) {
+ PDEBUGPRINTFX(DBG_FILTER,("Ignored logical operation '%c' due to always-ANDed filterkeyword",logop));
+ }
+ }
+ // now check for continuation: optional closing paranthesis plus logical op
+ // - closing paranthesis
+ do {
+ // skip spaces
+ while (isspace(*p)) p++;
+ if (*p!=')') break;
+ if (paraNest==0) {
+ // as we might parse filters as part of /fi() or /tf() options,
+ // this is not an error but only means end of filter expression
+ goto endFilter;
+ }
+ paraNest--;
+ p++;
+ } while (true);
+ // - logical op
+ if (*p==0)
+ break; // done
+ else if (*p=='&') {
+ logop=*p++; // if no entity matches, & by itself is treated as AND
+ if (strucmp(p,"amp;",4)==0) { logop='&'; p+=4; }
+ else if (strucmp(p,"and;",4)==0) { logop='&'; p+=4; }
+ else if (strucmp(p,"or;",3)==0) { logop='|'; p+=3; }
+ }
+ else if (*p=='|') {
+ logop='|';
+ }
+ else {
+ PDEBUGPRINTFX(DBG_ERROR,("Expected logical operator or end of filter but found '%s'",p));
+ break;
+ }
+ } // not opening paranthesis
+ } // while not end of filter
+endFilter:
+ PDEBUGPRINTFX(DBG_FILTER+DBG_EXOTIC,("Resulting internal filter: %s",aFilter.c_str()));
+ // return pointer to terminating character
+ return p;
+} // TLocalEngineDS::parseFilterCGI
+
+
+#endif
+
+
+// analyze database name
+void TLocalEngineDS::analyzeName(
+ const char *aDatastoreURI,
+ string *aBaseNameP,
+ string *aTableNameP,
+ string *aCGIP
+)
+{
+ const char *p,*q=NULL, *r; // BCPPB needed const, gcc3.2.2 needed NULL
+ r=strchr(aDatastoreURI,'?');
+ p=strchr(aDatastoreURI,'/');
+ if (r && p>r) p=NULL; // if slash is in CGI, ignore it
+ else q=p+1; // slash exclusive
+ if (p!=NULL) {
+ // we have more than just the first element
+ if (aBaseNameP) aBaseNameP->assign(aDatastoreURI,p-aDatastoreURI);
+ // rest is table name and probably CGI
+ if (aTableNameP) {
+ if (r) aTableNameP->assign(q,r-q); // we have CGI
+ else *aTableNameP=q; // entire rest is tablename
+ }
+ }
+ else {
+ // no second path element, but possibly CGI
+ // - assign base name
+ if (aBaseNameP) {
+ if (r)
+ (*aBaseNameP).assign(aDatastoreURI,r-aDatastoreURI); // only up to CGI
+ else
+ (*aBaseNameP)=aDatastoreURI; // complete name
+ }
+ // - there is no table name
+ if (aTableNameP) aTableNameP->erase();
+ }
+ // return CGI (w/o question mark) if any
+ if (aCGIP) {
+ if (r) *aCGIP=r+1; else aCGIP->erase();
+ }
+} // TLocalEngineDS::analyzeName
+
+
+#ifdef SYSYNC_TARGET_OPTIONS
+
+// parses single option, returns pointer to terminating char of argument string
+// or NULL on error
+// Note: if aArguments is passed NULL, this is an option without arguments,
+// and an arbitrary non-NULL will be returned if parsing is ok
+const char *TLocalEngineDS::parseOption(
+ const char *aOptName,
+ const char *aArguments,
+ bool aFromSyncCommand
+)
+{
+ #ifdef OBJECT_FILTERING
+ if (strucmp(aOptName,"fi")==0) {
+ if (!aArguments) return NULL;
+ bool filterok=false; // assume invalid
+ // make sync set filter expression
+ string f;
+ aArguments=parseFilterCGI(aArguments,fLocalSendToRemoteTypeP,f); // if type being used for sending to remote is known here, use it
+ if (!aFromSyncCommand) {
+ addToFilter(f.c_str(),fSyncSetFilter,false); // AND chaining
+ // call this once to give derivate a chance to see if it can filter the now set fSyncSetFilter
+ engFilteredFetchesFromDB(true);
+ }
+ return aArguments; // end of filter pattern
+ }
+ #ifdef SYNCML_TAF_SUPPORT
+ else if (strucmp(aOptName,"tf")==0) {
+ if (!aArguments) return NULL;
+ bool filterok=false; // assume invalid
+ // make temporary filter (or TAF) expression
+ aArguments=parseFilterCGI(aArguments,fLocalSendToRemoteTypeP,fTargetAddressFilter); // if type being used for sending to remote is known here, use it
+ // Note: TAF filters are always evaluated internally as we need all SyncSet records
+ // regardless of eventual TAF suppression (for slowsync matching etc.)
+ return aArguments; // end of filter pattern
+ }
+ #endif
+ else if (aArguments && strucmp(aOptName,"dr")==0) {
+ // date range limit
+ sInt16 dstart,dend;
+ if (sscanf(aArguments,"%hd,%hd",&dstart,&dend)==2) {
+ // - find end of arguments
+ aArguments=strchr(aArguments,')');
+ // - calculate start and end
+ fDateRangeStart=getSystemNowAs(TCTX_UTC,getSessionZones());
+ fDateRangeEnd=fDateRangeStart;
+ // - now use offsets
+ fDateRangeStart+=dstart*linearDateToTimeFactor;
+ fDateRangeEnd+=dend*linearDateToTimeFactor;
+ return aArguments;
+ }
+ else return NULL;
+ }
+ else if (aArguments && strucmp(aOptName,"li")==0) {
+ // size limit
+ sInt16 n=StrToFieldinteger(aArguments,fSizeLimit);
+ if (n>0) {
+ // - find end of arguments
+ aArguments+=n;
+ return aArguments;
+ }
+ else return NULL;
+ }
+ else
+ if (!aArguments && strucmp(aOptName,"na")==0) {
+ // no attachments
+ fNoAttachments=true;
+ return (const char *)1; // non-zero
+ }
+ else
+ if (aArguments && strucmp(aOptName,"max")==0) {
+ // maximum number of items (for email for example)
+ sInt16 n=StrToULong(aArguments,fMaxItemCount);
+ if (n>0) {
+ // - find end of arguments
+ aArguments+=n;
+ return aArguments;
+ }
+ else return NULL;
+ }
+ else
+ #endif
+ #ifndef SYSYNC_CLIENT
+ if (!aArguments && strucmp(aOptName,"slow")==0) {
+ // force a slow sync
+ PDEBUGPRINTFX(DBG_HOT,("Slowsync forced by CGI-option in db path"));
+ fForceSlowSync=true;
+ return (const char *)1; // non-zero
+ }
+ else
+ #endif
+ if (aArguments && strucmp(aOptName,"o")==0) {
+ // datastore options
+ // - find end of arguments
+ const char *p=strchr(aArguments,')');
+ if (p) fDBOptions.assign(aArguments,p-aArguments);
+ return p;
+ }
+ else
+ return NULL; // not parsed
+} // TLocalEngineDS::parseOption
+
+#endif
+
+// parse options
+localstatus TLocalEngineDS::engParseOptions(
+ const char *aTargetURIOptions, // option string contained in target URI
+ bool aFromSyncCommand // must be set when parsing options from <sync> target URI
+)
+{
+ localstatus sta=LOCERR_OK;
+ if (aTargetURIOptions) {
+ const char *p = aTargetURIOptions;
+ #ifdef SYSYNC_TARGET_OPTIONS
+ const char *q;
+ #endif
+ char c;
+ string taf; // official TAF
+ while ((c=*p)) {
+ #ifdef SYSYNC_TARGET_OPTIONS
+ if (c=='/') {
+ // proprietary option lead-in
+ // - get option name
+ string optname;
+ optname.erase();
+ while(isalnum(c=*(++p)))
+ optname+=c;
+ // - get arguments
+ if (c=='(') {
+ q=p; // save
+ p++; // skip "("
+ p=parseOption(optname.c_str(),p,aFromSyncCommand);
+ if (!p) {
+ // unrecognized or badly formatted option, just add it to TAF
+ taf+='/';
+ taf+=optname;
+ p=q; // restart after option name
+ continue;
+ }
+ if (*p!=')') {
+ sta=406;
+ PDEBUGPRINTFX(DBG_ERROR,("Syntax error in target options"));
+ break;
+ }
+ }
+ else {
+ // option without arguments
+ if (!parseOption(optname.c_str(),NULL,aFromSyncCommand)) {
+ sta=406;
+ PDEBUGPRINTFX(DBG_ERROR,("Unknown target option"));
+ break;
+ } // error, not parsed
+ p--; // will be incremented once again below
+ }
+ }
+ else
+ #endif
+ {
+ // char not part of an option
+ taf+=c;
+ }
+ // next
+ p++;
+ }
+ // check if we have TAF
+ if (taf.size()>0) {
+ #if defined(TAF_AS_SYNCSETFILTER) && defined(SYSYNC_TARGET_OPTIONS)
+ // treat as "fi(<filterexpression>)" option like before 1.0.8.10
+ if (!parseOption("fi",taf.c_str(),aFromSyncCommand)) { sta=406; } // error, not parsed
+ #else
+ #ifdef SYNCML_TAF_SUPPORT
+ // treat as "tf(<filterexpression>)" = real TAF
+ if (!parseOption("tf",taf.c_str(),aFromSyncCommand)) { sta=406; } // error, not parsed
+ #else
+ sta=406;
+ PDEBUGPRINTFX(DBG_ERROR,("TAF not supported"));
+ #endif
+ #endif
+ }
+ }
+ // return status
+ return sta;
+} // TLocalEngineDS::engParseOptions
+
+
+// process SyncML 1.2 style filter
+localstatus TLocalEngineDS::engProcessDS12Filter(SmlFilterPtr_t aTargetFilter)
+{
+ localstatus sta=LOCERR_OK;
+
+ if (aTargetFilter) {
+ // check general availability
+ #ifdef OBJECT_FILTERING
+ if (!fDSConfigP->fDS12FilterSupport)
+ #endif
+ {
+ PDEBUGPRINTFX(DBG_ERROR,("DS 1.2 style filtering is not available or disabled in config (<ds12filters>)"));
+ sta=406;
+ goto error;
+ }
+ // check filter
+ TSyncItemType *itemTypeP=NULL; // no associated type so far
+ bool inclusiveFilter=false; // default is EXCLUSIVE
+ // - meta
+ if (aTargetFilter->meta) {
+ SmlMetInfMetInfPtr_t metaP = smlPCDataToMetInfP(aTargetFilter->meta);
+ const char *typestr = smlMetaTypeToCharP(metaP);
+ // get sync item type for it
+ // - filter mostly applies to items SENT, so we search these first
+ itemTypeP = getSendType(typestr,NULL);
+ if (!itemTypeP)
+ itemTypeP = getReceiveType(typestr,NULL);
+ PDEBUGPRINTFX(DBG_FILTER,("DS12 <Filter> <Type> is '%s' -> %sfound",typestr,itemTypeP ? "" : "NOT "));
+ if (!itemTypeP) {
+ sta=415;
+ goto error;
+ }
+ }
+ // - filtertype
+ if (aTargetFilter->filtertype) {
+ const char *ftystr = smlPCDataToCharP(aTargetFilter->filtertype);
+ if (strucmp(ftystr,SYNCML_FILTERTYPE_INCLUSIVE)==0) {
+ inclusiveFilter=true;
+ }
+ else if (strucmp(ftystr,SYNCML_FILTERTYPE_EXCLUSIVE)==0) {
+ inclusiveFilter=false;
+ }
+ else {
+ PDEBUGPRINTFX(DBG_ERROR,("Invalid <FilterType> '%s'",ftystr));
+ sta=422;
+ goto error;
+ }
+ }
+ // - field level filter
+ if (aTargetFilter->field) {
+ /// @todo %%% to be implemented
+ PDEBUGPRINTFX(DBG_ERROR,("Field-level filtering not supported"));
+ sta=406;
+ goto error;
+ }
+ // - record level filter
+ if (aTargetFilter->record) {
+ #ifdef OBJECT_FILTERING
+ SmlItemPtr_t recordItemP = aTargetFilter->record->item;
+ if (recordItemP) {
+ // - check grammar
+ const char *grammarstr = smlMetaTypeToCharP(smlPCDataToMetInfP(recordItemP->meta));
+ if (strucmp(grammarstr,SYNCML_FILTERTYPE_CGI)!=0) {
+ PDEBUGPRINTFX(DBG_ERROR,("Invalid filter grammar '%s'",grammarstr));
+ sta=422;
+ goto error;
+ }
+ // now get the actual filter string
+ const char *filterstring = smlPCDataToCharP(recordItemP->data);
+ PDEBUGPRINTFX(DBG_HOT,(
+ "Remote specified %sCLUSIVE filter query: '%s'",
+ inclusiveFilter ? "IN" : "EX",
+ filterstring
+ ));
+ if (*filterstring) {
+ string f;
+ // parse it
+ filterstring = parseFilterCGI(filterstring,itemTypeP,f);
+ if (*filterstring) {
+ // not read to end
+ PDEBUGPRINTFX(DBG_ERROR,("filter query syntax error at: '%s'",filterstring));
+ sta=422;
+ goto error;
+ }
+ /// @todo: %%% check if this is correct interpretation
+ // - exclusive is what we used to call "sync set" filtering
+ // - inclusive seems to be former TAF
+ if (inclusiveFilter) {
+ // INCLUSIVE
+ #ifdef SYNCML_TAF_SUPPORT
+ fTargetAddressFilter=f;
+ #else
+ PDEBUGPRINTFX(DBG_ERROR,("This SyncML engine version has no INCLUSIVE filter support"));
+ #endif
+ }
+ else {
+ // EXCLUSIVE
+ addToFilter(f.c_str(),fSyncSetFilter,false); // AND chaining
+ PDEBUGPRINTFX(DBG_FILTER,("complete sync set filter is now: '%s'",fSyncSetFilter.c_str()));
+ // call this once to give derivate a chance to see if it can filter the now set fSyncSetFilter
+ engFilteredFetchesFromDB(true);
+ }
+ }
+ } // if item
+ #else
+ // no object filtering
+ PDEBUGPRINTFX(DBG_ERROR,("This SyncML engine version has no filter support (only PRO has)"));
+ sta=406;
+ goto error;
+ #endif
+ } // record
+ } // filter at all
+error:
+ return sta;
+} // TLocalEngineDS::engProcessDS12Filter
+
+
+// process Sync alert from remote party: check if alert code is supported,
+// check if slow sync is needed due to anchor mismatch
+// - server case: also generate appropriate Alert acknowledge command
+TAlertCommand *TLocalEngineDS::engProcessSyncAlert(
+ TSuperDataStore *aAsSubDatastoreOf, // if acting as subdatastore
+ uInt16 aAlertCode, // the alert code
+ const char *aLastRemoteAnchor, // last anchor of remote
+ const char *aNextRemoteAnchor, // next anchor of remote
+ const char *aTargetURI, // target URI as sent by remote, no processing at all
+ const char *aIdentifyingTargetURI, // target URI that was used to identify datastore
+ const char *aTargetURIOptions, // option string contained in target URI
+ SmlFilterPtr_t aTargetFilter, // DS 1.2 filter, NULL if none
+ const char *aSourceURI, // source URI
+ TStatusCommand &aStatusCommand // status that might be modified
+)
+{
+ TAlertCommand *alertcmdP=NULL;
+ localstatus sta=LOCERR_OK;
+
+ SYSYNC_TRY {
+ // determine status of read-only option
+ fReadOnly=
+ fSessionP->getReadOnly() || // session level read-only flag (probably set by login)
+ fDSConfigP->fReadOnly; // or datastore config
+ #ifdef SUPERDATASTORES
+ // check if not already alerted as subdatastore
+ if (fAsSubDatastoreOf) {
+ // bad, cannot be alerted directly AND as subdatastore
+ aStatusCommand.setStatusCode(400);
+ ADDDEBUGITEM(aStatusCommand,"trying to alert already alerted subdatastore");
+ PDEBUGPRINTFX(DBG_ERROR,("Already alerted as subdatastore of '%s'",fAsSubDatastoreOf->getName()));
+ return NULL;
+ }
+ // set subdatastore mode
+ fAsSubDatastoreOf=aAsSubDatastoreOf;
+ #endif
+ // reset type info
+ fLocalSendToRemoteTypeP = NULL;
+ fLocalReceiveFromRemoteTypeP = NULL;
+ fRemoteReceiveFromLocalTypeP=NULL;
+ fRemoteSendToLocalTypeP=NULL;
+ // prepare database-level scripts
+ // NOTE: in client case, alertprepscript is already rebuilt here!
+ #ifdef SCRIPT_SUPPORT
+ TScriptContext::rebuildContext(fSessionP->getSyncAppBase(),fDSConfigP->fDBInitScript,fDataStoreScriptContextP,fSessionP);
+ TScriptContext::rebuildContext(fSessionP->getSyncAppBase(),fDSConfigP->fSentItemStatusScript,fDataStoreScriptContextP,fSessionP);
+ TScriptContext::rebuildContext(fSessionP->getSyncAppBase(),fDSConfigP->fReceivedItemStatusScript,fDataStoreScriptContextP,fSessionP);
+ TScriptContext::rebuildContext(fSessionP->getSyncAppBase(),fDSConfigP->fAlertScript,fDataStoreScriptContextP,fSessionP);
+ TScriptContext::rebuildContext(fSessionP->getSyncAppBase(),fDSConfigP->fDBFinishScript,fDataStoreScriptContextP,fSessionP,true); // now instantiate vars
+ #endif
+ // NOTE for client case:
+ // ALL instantiated datastores have already sent an Alert to the server by now here
+
+ // check DS 1.2 filter
+ sta = engProcessDS12Filter(aTargetFilter);
+ if (sta != LOCERR_OK) {
+ aStatusCommand.setStatusCode(sta);
+ ADDDEBUGITEM(aStatusCommand,"Invalid <Filter> in target options");
+ return NULL; // error in options
+ }
+
+ // Filter CGI is now a combination of TAF and Synthesis-Style
+ // extras (options).
+ if (aTargetURIOptions && *aTargetURIOptions) {
+ // there are target address options (such as filter CGI and TAF)
+ sta = engParseOptions(aTargetURIOptions,false);
+ if (sta != LOCERR_OK) {
+ aStatusCommand.setStatusCode(sta);
+ ADDDEBUGITEM(aStatusCommand,"Invalid CGI target URI options");
+ return NULL; // error in options
+ }
+ }
+ #ifndef SYSYNC_CLIENT
+ // server case: initially we are not in refresh only mode. Alert code or alert script could change this
+ fRefreshOnly=false;
+ #endif
+
+ // save it for suspend and reference in scripts
+ fAlertCode=aAlertCode;
+ #ifdef SCRIPT_SUPPORT
+ // call the alert script, which might want to force a slow sync and/or a server sync set zap
+ TScriptContext::execute(
+ fDataStoreScriptContextP,
+ fDSConfigP->fAlertScript,
+ &DBFuncTable,
+ this // caller context
+ );
+ aAlertCode=fAlertCode; // get eventually modified version back (SETALERTCODE)
+ #endif
+ // if we process a sync alert now, we haven't started sync or map generation
+ #ifndef SYSYNC_CLIENT
+ // server case: forget Temp GUID mapping
+ fTempGUIDMap.clear(); // forget all previous temp GUID mappings
+ #endif
+ // save remote's next anchor for saving at end of session
+ fNextRemoteAnchor = aNextRemoteAnchor;
+ // get target info in case we are server
+ #ifndef SYSYNC_CLIENT
+ // now get anchor info out of database
+ // - make sure other anchor variables are set
+ sta = engInitSyncAnchors(
+ aIdentifyingTargetURI, // use processed form, not as sent by remote
+ aSourceURI
+ );
+ if (sta!=LOCERR_OK) {
+ // error getting anchors
+ aStatusCommand.setStatusCode(syncmlError(sta));
+ PDEBUGPRINTFX(DBG_ERROR,("Could not get Sync Anchor info, status=%hd",sta));
+ return NULL; // no alert to send back
+ }
+ // Server ok until here
+ PDEBUGPRINTFX(DBG_PROTO,(
+ "Saved Last Remote Client Anchor='%s', received <last> Remote Client Anchor='%s' (must match for normal sync)",
+ fLastRemoteAnchor.c_str(),
+ aLastRemoteAnchor
+ ));
+ PDEBUGPRINTFX(DBG_PROTO,(
+ "Received <next> Remote Client Anchor='%s' (to be compared with <last> in NEXT session)",
+ fNextRemoteAnchor.c_str()
+ ));
+ PDEBUGPRINTFX(DBG_PROTO,(
+ "(Saved) Last Local Server Anchor='%s', (generated) Next Local Server Anchor='%s' (sent to client as <last>/<next> in <alert>)",
+ fLastLocalAnchor.c_str(),
+ fNextLocalAnchor.c_str()
+ ));
+ #else
+ // Client ok until here
+ PDEBUGPRINTFX(DBG_PROTO,(
+ "Saved Last Remote Server Anchor='%s', received <last> Remote Server Anchor='%s' (must match for normal sync)",
+ fLastRemoteAnchor.c_str(),
+ aLastRemoteAnchor
+ ));
+ PDEBUGPRINTFX(DBG_PROTO,(
+ "Received <next> Remote Server Anchor='%s' (to be compared with <last> in NEXT session)",
+ fNextRemoteAnchor.c_str()
+ ));
+ #endif
+ PDEBUGPRINTFX(DBG_PROTO,(
+ "(Saved) fResumeAlertCode = %hd (valid for >DS 1.2 only)",
+ fResumeAlertCode
+ ));
+ // Now check for resume
+ // - default to what was actually alerted
+ uInt16 effectiveAlertCode=aAlertCode;
+ #ifndef SYSYNC_CLIENT
+ // - check if resuming server session
+ fResuming=false;
+ if (aAlertCode==225) {
+ if (fSessionP->getSyncMLVersion()<syncml_vers_1_2) {
+ aStatusCommand.setStatusCode(406);
+ ADDDEBUGITEM(aStatusCommand,"Resume not supported in SyncML prior to 1.2");
+ PDEBUGPRINTFX(DBG_ERROR,("Resume not supported in SyncML prior to 1.2"));
+ return NULL;
+ }
+ // Resume requested
+ if (fResumeAlertCode==0 || !dsResumeSupportedInDB()) {
+ // cannot resume, suggest a normal sync (in case anchors do not match, this will become a 508 below)
+ aStatusCommand.setStatusCode(509); // cannot resume, override
+ effectiveAlertCode=200; // suggest normal sync
+ ADDDEBUGITEM(aStatusCommand,"Cannot resume, suggesting a normal sync");
+ PDEBUGPRINTFX(DBG_ERROR,("Cannot resume, suggesting a normal sync"));
+ }
+ else {
+ // we can resume, use the saved alert code
+ effectiveAlertCode=fResumeAlertCode;
+ PDEBUGPRINTFX(DBG_HOT,("Alerted to resume previous session, Switching to alert Code = %hd",fResumeAlertCode));
+ fResuming=true;
+ }
+ }
+ #endif
+ // now do the actual alert internally
+ if (sta==LOCERR_OK) {
+ // check if we can process the alert
+ // NOTE: for client this might cause a change in Sync mode, if server
+ // alerts something different than client alerted before.
+ // Note that a client will keep fromserver mode even if server
+ // changes to two-way, as it might be that we have sent the server
+ // a two-way alert even if we want fromserver due to compatibility with
+ // servers that cannot do fromserver.
+ #ifndef SYSYNC_CLIENT
+ // - Server always obeys what client requests (that is, if alertscript does not modify it)
+ sta = setSyncModeFromAlertCode(effectiveAlertCode,false); // as server
+ #else
+ // - for client, check that server can't switch to a client writing mode
+ // (we had a case when mobical did that for a user and erased all his data)
+ TSyncModes prevMode = fSyncMode; // remember previous mode
+ sta = setSyncModeFromAlertCode(effectiveAlertCode,true); // as client
+ if (prevMode==smo_fromclient && fSyncMode!=smo_fromclient) {
+ // server tries to switch to a mode that could be writing data to the client
+ // - forbidden
+ sta=403;
+ aStatusCommand.setStatusCode(syncmlError(sta));
+ ADDDEBUGITEM(aStatusCommand,"Server may not write to client");
+ PDEBUGPRINTFX(DBG_ERROR,("From-Client only: Server may not alert mode that writes client data (%hd) - blocked",effectiveAlertCode));
+ // - also abort sync of this datastore without chance to resume, as this problem might
+ // originate from a resume attempt, which the server
+ // tried to convert to a normal or slow sync. With cancelling the resume here, we make sure
+ // next sync will start over and sending the desired (one-way) sync mode again.
+ engAbortDataStoreSync(sta, false, false); // remote problem, not resumable
+ }
+ else
+ #endif
+ {
+ if (sta!=LOCERR_OK) {
+ // Sync type not supported
+ // - go back to idle
+ changeState(dssta_idle,true); // force to idle
+ aStatusCommand.setStatusCode(syncmlError(sta));
+ ADDDEBUGITEM(aStatusCommand,"Sync type not supported");
+ PDEBUGPRINTFX(DBG_ERROR,("Sync type (Alert code %hd) not supported, err=%hd",effectiveAlertCode,sta));
+ }
+ }
+ }
+ if (sta==LOCERR_OK) {
+ // Sync type supported
+ // - set new state to alerted
+ #ifdef SYSYNC_CLIENT
+ changeState(dssta_clientalerted,true); // force it
+ #else
+ changeState(dssta_serveralerted,true); // force it
+ #endif
+ // - datastore state is now dss_alerted
+ PDEBUGPRINTFX(DBG_HOT,(
+ "Alerted (code=%hd) for %s%s %s%s%s ",
+ aAlertCode,
+ fResuming ? "Resumed " : "",
+ SyncModeDescriptions[fSyncMode],
+ fSlowSync ? (fSyncMode==smo_twoway ? "Slow Sync" : "Refresh") : "Normal Sync",
+ fReadOnly ? " (Readonly)" : "",
+ fRefreshOnly ? " (Refreshonly)" : ""
+ ));
+ CONSOLEPRINTF((
+ "- Remote alerts (%hd): %s%s %s%s%s for '%s' (source='%s')",
+ aAlertCode,
+ fResuming ? "Resumed " : "",
+ SyncModeDescriptions[fSyncMode],
+ fSlowSync ? (fSyncMode==smo_twoway ? "Slow Sync" : "Refresh") : "Normal Sync",
+ fReadOnly ? " (Readonly)" : "",
+ fRefreshOnly ? " (Refreshonly)" : "",
+ aTargetURI,
+ aSourceURI
+ ));
+ // - now test if the sync state is same in client & server
+ // (if saved anchor is empty, slow sync is needed anyway)
+ if (
+ (
+ (
+ (!fLastRemoteAnchor.empty() && fLastRemoteAnchor==aLastRemoteAnchor) || // either anchors must match...
+ (fResuming && *aLastRemoteAnchor==0) // ...or in case of resume, remote not sending anchor is ok as well
+ )
+ && !fForceSlowSync // ...but no force for slowsync may be set internally
+ )
+ || fSlowSync // if slow sync is requested by the remote anyway, we don't need to be in sync anyway, so just go on
+ ) {
+ // sync state ok or Slow sync requested anyway:
+ #ifndef SYSYNC_CLIENT
+ // we can generate Alert with same code as sent
+ // %%% Note: this is not entirely clear, as SCTS sends
+ // corresponding SERVER ALERTED code back.
+ // Specs suggest that we send the code back unmodified
+ uInt16 alertCode = getSyncStateAlertCode(fServerAlerted);
+ alertcmdP = new TAlertCommand(fSessionP,this,alertCode);
+ fAlertCode=alertCode; // save it for reference in scripts and for suspend/resume
+ #endif
+ }
+ else {
+ // switch to slow sync
+ fSlowSync=true;
+ PDEBUGPRINTFX(DBG_HOT,("Switched to SlowSync because of Anchor mismatch or server-side user option"));
+ CONSOLEPRINTF(("- switched to SlowSync because of Sync Anchor mismatch"));
+ // sync state not ok, we need slow sync
+ aStatusCommand.setStatusCode(508); // Refresh required
+ // update effective alert code
+ uInt16 alertCode = getSyncStateAlertCode(false);
+ fAlertCode=alertCode; // save it for reference in scripts and for suspend
+ // NOTE: if client detected slow-sync not before here, status 508 alone
+ // (without another Alert 201 sent to the server) is sufficient for
+ // server to switch to slow sync.
+ #ifndef SYSYNC_CLIENT
+ // generate Alert for Slow sync
+ alertcmdP = new TAlertCommand(fSessionP,this,alertCode);
+ #endif
+ }
+ // Now we are alerted for a sync
+ // - reset item counters
+ fItemsSent = 0;
+ fItemsReceived = 0;
+ #ifndef SYSYNC_CLIENT
+ // Server case
+ // - show info
+ PDEBUGPRINTFX(DBG_HOT,(
+ "ALERTED from client for %s%s%s Sync",
+ fResuming ? "resumed " : "",
+ fSlowSync ? "slow" : "normal",
+ fFirstTimeSync ? " first time" : ""
+ ));
+ // server: add Item with Anchors and URIs
+ SmlItemPtr_t itemP = newItem();
+ // - anchors
+ itemP->meta=newMetaAnchor(fNextLocalAnchor.c_str(),fLastLocalAnchor.c_str());
+ // - MaxObjSize here again to make SCTS happy
+ if (
+ (fSessionP->getRootConfig()->fLocalMaxObjSize>0) &&
+ (fSessionP->getSyncMLVersion()>=syncml_vers_1_1)
+ ) {
+ // SyncML 1.1 has object size and we need to put it here for SCTS
+ smlPCDataToMetInfP(itemP->meta)->maxobjsize=newPCDataLong(
+ fSessionP->getRootConfig()->fLocalMaxObjSize
+ );
+ }
+ // - URIs (reversed from what was received in Alert)
+ itemP->source=newLocation(aTargetURI); // use unprocessed form as sent by remote
+ itemP->target=newLocation(aSourceURI);
+ // - add to alert command
+ alertcmdP->addItem(itemP);
+ // - set new state, alert now answered
+ changeState(dssta_serveransweredalert,true); // force it
+ #else
+ // Client case
+ // - now sync mode is stable (late switch to slowsync has now occurred if any)
+ changeState(dssta_syncmodestable,true);
+ // - show info
+ PDEBUGPRINTFX(DBG_HOT,(
+ "ALERTED from server for %s%s%s Sync",
+ fResuming ? "resumed " : "",
+ fSlowSync ? "slow" : "normal",
+ fFirstTimeSync ? " first time" : ""
+ ));
+ #ifdef PROGRESS_EVENTS
+ // - progress event
+ OBJ_PROGRESS_EVENT(
+ fSessionP->getSyncAppBase(),
+ pev_alerted,
+ fDSConfigP,
+ fSlowSync ? (fFirstTimeSync ? 2 : 1) : 0,
+ fResuming ? 1 : 0,
+ fSyncMode
+ );
+ #endif // PROGRESS_EVENTS
+ //%%% To make client-side filtering work, determining send/receive types must be done
+ // before loading the sync set.
+ // Therefore these two init steps are now in the new engInitForClientSync() routine, which is now called
+ // in syncclient.cpp immediately before starting to generate sync commands.
+ // (Alternatively, engInitForSyncOps() could be placed here before switching to dssta_dataaccessstarted.
+ // Tried that, works, but has the disadvantage that in case server sends devInf after answering alerts,
+ // type resolution would fail or be forced to blind flight)
+ /*
+ // - prepare engine for sync (determining types)
+ // - let local datastore (derived DB-specific class) prepare for sync
+ sta = changeState(dssta_dataaccessstarted);
+ if (sta==LOCERR_OK && isStarted(false)) {
+ // already started now, change state
+ sta = changeState(dssta_syncsetready);
+ }
+ */
+ #endif // client Case
+ }
+ // clear partial item if we definitely know we are not resuming
+ if (!fResuming) {
+ // not resuming - prevent that partial item is used in TSyncOpCommand
+ fPartialItemState=pi_state_none;
+ // free this space early (would be freed at session end anyway, but we don't need it any more now)
+ if (fPIStoredDataAllocated) {
+ smlLibFree(fPIStoredDataP);
+ fPIStoredDataAllocated=false;
+ }
+ fPIStoredDataP=NULL;
+ }
+ // save name how remote adresses local database
+ // (for sending same URI back in own Sync)
+ fRemoteViewOfLocalURI = aTargetURI; // save it
+ #ifndef SYSYNC_CLIENT
+ fRemoteDBPath = aSourceURI;
+ #endif
+ if (sta!=LOCERR_OK) {
+ // no alert command
+ if (alertcmdP) delete alertcmdP;
+ alertcmdP=NULL;
+ aStatusCommand.setStatusCode(syncmlError(sta));
+ PDEBUGPRINTFX(DBG_HOT,("engProcessSyncAlert failed with status=%hd",sta));
+ }
+ }
+ SYSYNC_CATCH (...)
+ // clean up locally owned objects
+ if (alertcmdP) delete alertcmdP;
+ SYSYNC_RETHROW;
+ SYSYNC_ENDCATCH
+ // return alert command, if any
+ return alertcmdP;
+} // TLocalEngineDS::engProcessSyncAlert
+
+
+// process status received for sync alert
+bool TLocalEngineDS::engHandleAlertStatus(TSyError aStatusCode)
+{
+ bool handled=false;
+ #ifdef SYSYNC_CLIENT
+ // for client, make sure we have just sent the alert
+ if (!testState(dssta_clientsentalert,true)) return false; // cannot switch if server not alerted
+ // anyway, we have seen the status
+ changeState(dssta_clientalertstatused,true); // force it
+ #else
+ // for server, check if client did combined init&sync
+ if (fLocalDSState>=dssta_syncmodestable) {
+ // must be combined init&sync
+ if (aStatusCode!=200) {
+ // everything except ok is not allowed here
+ PDEBUGPRINTFX(DBG_ERROR,("In combined init&sync, Alert status must be ok (but is %hd)",aStatusCode));
+ dsAbortDatastoreSync(400,false); // remote problem
+ }
+ // aborted or not, status is handled
+ return true;
+ }
+ // normal case with separate init: we need to have answered the alert here
+ if (!testState(dssta_serveransweredalert,true)) return false; // cannot switch if server not alerted
+ #endif
+ // now check status code
+ if (aStatusCode==508) {
+ // remote party needs slow sync
+ PDEBUGPRINTFX(DBG_HOT,("engHandleAlertStatus: Remote party needs SlowSync, switching to slowsync (AFTER alert, cancelling eventual Resume)"));
+ // Note: in server and client cases, this mode change may happen AFTER alert command exchange
+ // - switch to slow sync
+ fSlowSync=true;
+ // - if we are late-forced to slow sync, this means that this cannot be a resume
+ fResuming=false;
+ // - update effective alert code that will be saved when this session gets suspended
+ fAlertCode=getSyncStateAlertCode(fServerAlerted);
+ handled=true;
+ }
+ else if (aStatusCode==200) {
+ handled=true;
+ }
+ #ifdef SYSYNC_CLIENT
+ // check for resume override by server
+ if (!handled && fResuming) {
+ // we have requested resume
+ if (aStatusCode==509) {
+ // resume not accepted by server, but overridden by another sync type
+ fResuming=false;
+ PDEBUGPRINTFX(DBG_ERROR,("engHandleAlertStatus: Server rejected Resume"));
+ handled=true;
+ }
+ }
+ #else
+ // if we have handled it here, sync mode is now stable
+ if (handled) {
+ // if we get that far, sync mode for server is now stable AND we can receive cached maps
+ changeState(dssta_syncmodestable,true); // force it, sync mode is now stable, no further changes are possible
+ }
+ #endif
+ // no other status codes are supported at the datastore level
+ if (!handled && aStatusCode>=400) {
+ /** @deprecated no longer needed because we are never dssta_idle here
+ #ifdef SYSYNC_CLIENT
+ fState=dss_alerted; // normally, we are still dss_idle here. But we want an explicit abort, so pretend we were alerted
+ #endif
+ */
+ engAbortDataStoreSync(aStatusCode, false); // remote problem
+ handled=true;
+ }
+ return handled; // status handled
+} // TLocalEngineDS::engHandleAlertStatus
+
+
+// initialize reception of syncop commands for datastore
+// Note: previously, this was implemented as initLocalDatastoreSync in syncsession
+localstatus TLocalEngineDS::engInitForSyncOps(
+ const char *aRemoteDatastoreURI // URI of remote datastore
+)
+{
+ localstatus sta = LOCERR_OK;
+
+ // no default types
+ TSyncItemType *LocalSendToRemoteTypeP=NULL; // used by local to send to remote
+ TSyncItemType *RemoteReceiveFromLocalTypeP=NULL; // used by remote to receive from local
+ TSyncItemType *LocalReceiveFromRemoteTypeP=NULL; // used by local to receive from remote
+ TSyncItemType *RemoteSendToLocalTypeP=NULL; // used by remote to send to local
+
+ // Now determine remote datastore
+ // Note: It might be that this was called already earlier in the session, so
+ // the link between local and remote datastore might already exist
+ if (fRemoteDatastoreP==NULL) {
+ // try to locate it by name and set it - in case of superdatastore, it will be set in all subdatastores
+ engSetRemoteDatastore(fSessionP->findRemoteDataStore(aRemoteDatastoreURI));
+ }
+ else {
+ // There is a remote datastore already associated
+ #ifdef SYDEBUG
+ // - make a sanity check to see if sepcified remote URI matches
+ if(fRemoteDatastoreP!=fSessionP->findRemoteDataStore(aRemoteDatastoreURI)) {
+ PDEBUGPRINTFX(DBG_ERROR,(
+ "Warning: Received remote DS LocURI '%s' does not match already associated DS '%s'. We use the associated DS.",
+ aRemoteDatastoreURI,
+ fRemoteDatastoreP->getName()
+ ));
+ }
+ #endif
+ }
+ // Now create a dummy remote data store for a blind sync attempt
+ if (!fRemoteDatastoreP) {
+ // no such remote datastore for this local datastore known, create one (or fail)
+ #ifdef REMOTE_DS_MUST_BE_IN_DEVINF
+ if (fSessionP->fRemoteDataStoresKnown) {
+ // we have received devinf, but still can't find remote data store: error
+ // Note: we had to disable this because of bugs in smartner server
+ PDEBUGPRINTFX(DBG_ERROR,("Remote datastore name '%s' not found in received DevInf",aRemoteDatastoreURI));
+ return 404;
+ }
+ else
+ #else
+ if (fSessionP->fRemoteDataStoresKnown) {
+ // we have received devinf, but still can't find remote data store:
+ // just show in log, but continue as if there was no devInf received at all
+ PDEBUGPRINTFX(DBG_ERROR,("Warning: Remote datastore name '%s' not found in received DevInf.",aRemoteDatastoreURI));
+ }
+ #endif
+ {
+ // We couldn't retrieve DevInf (or !REMOTE_DS_MUST_BE_IN_DEVINF), so we have to try blind
+ // - check remote specifics here if we had no devinf (there might be default remote
+ // rules to apply or checking license restrictions
+ // - this is executed only once per session, after that, we'll be fRemoteDevInfLock-ed
+ if (!fSessionP->fRemoteDevInfKnown && !fSessionP->fRemoteDevInfLock) {
+ // detect client specific server behaviour if needed
+ sta = fSessionP->checkRemoteSpecifics(NULL);
+ fSessionP->remoteAnalyzed(); // analyzed now (accepted or not does not matter)
+ if (sta!=LOCERR_OK)
+ return sta; // not ok, device rejected
+ }
+ // default data types are those preferred by local datastore (or explicitly marked for blind sync attempts)
+ if (getDSConfig()->fTypeSupport.fPreferredLegacy) {
+ // we have a preferred type for blind sync attempts
+ LocalSendToRemoteTypeP = getSession()->findLocalType(getDSConfig()->fTypeSupport.fPreferredLegacy);
+ LocalReceiveFromRemoteTypeP = LocalSendToRemoteTypeP;
+ }
+ else {
+ // no specific "blind" preference, use my own normally preferred types
+ LocalSendToRemoteTypeP = getPreferredTxItemType(); // send in preferred tx type of local datastore
+ LocalReceiveFromRemoteTypeP = getPreferredRxItemType(); // receive in preferred rx type of local datastore
+ }
+ // same type on both end (as only local type exists)
+ RemoteReceiveFromLocalTypeP = LocalSendToRemoteTypeP; // same on both end
+ RemoteSendToLocalTypeP = LocalReceiveFromRemoteTypeP; // same on both end (as only local type exists)
+ // create "remote" datastore with matching properties to local one
+ PDEBUGPRINTFX(DBG_ERROR,("Warning: No DevInf for remote datastore, running blind sync attempt"));
+ TRemoteDataStore *remDsP;
+ MP_NEW(remDsP,DBG_OBJINST,"TRemoteDataStore",TRemoteDataStore(
+ fSessionP,
+ aRemoteDatastoreURI, // remote name of datastore
+ 0 // standard Sync caps
+ ));
+ // - set it (in case of superdatastore in all subdatastores as well)
+ engSetRemoteDatastore(remDsP);
+ // add type support
+ fRemoteDatastoreP->setPreferredTypes(
+ RemoteReceiveFromLocalTypeP, // remote receives in preferred tx type of local datastore
+ RemoteSendToLocalTypeP // remote sends in preferred rx type of local datastore
+ );
+ // add it to the remote datastore list
+ fSessionP->fRemoteDataStores.push_back(fRemoteDatastoreP);
+ // make sure late devInf arriving won't supersede our artificially created remote datastore any more
+ fSessionP->fRemoteDevInfLock=true;
+ }
+ }
+ else {
+ // found remote DB, determine default data exchange types
+ // - common types for sending data to remote
+ LocalSendToRemoteTypeP=getTypesForTxTo(fRemoteDatastoreP,&RemoteReceiveFromLocalTypeP);
+ // - common types for receiving data from remote
+ LocalReceiveFromRemoteTypeP=getTypesForRxFrom(fRemoteDatastoreP,&RemoteSendToLocalTypeP);
+ }
+ #ifndef NO_REMOTE_RULES
+ // check if rule match type will override what we found so far
+ if (fSessionP->fAppliedRemoteRuleP) {
+ // have a look at our rulematch types
+ TRuleMatchTypesContainer::iterator pos;
+ TSyncItemType *ruleMatchTypeP = NULL;
+ for (pos=fRuleMatchItemTypes.begin();pos!=fRuleMatchItemTypes.end();++pos) {
+ // there is a rule applied
+ // - parse match string in format "rule[,rule]..." with * and ? wildcards allowed in "rule"
+ cAppCharP p=(*pos).ruleMatchString;
+ while (*p!=0) {
+ // split at commas
+ cAppCharP e=strchr(p,',');
+ size_t n;
+ if (e) {
+ n=e-p;
+ e++;
+ }
+ else {
+ n=strlen(p);
+ e=p+n;
+ }
+ // compare
+ if (strwildcmp(fSessionP->fAppliedRemoteRuleP->getName(), p, 0, n)==0) {
+ ruleMatchTypeP=(*pos).itemTypeP; // get the matching type
+ break;
+ }
+ // test next match target
+ p=e;
+ }
+ // apply if found one already
+ if (ruleMatchTypeP) {
+ // use this instead of normal types
+ // - local types
+ LocalSendToRemoteTypeP=ruleMatchTypeP; // used by local to send to remote
+ LocalReceiveFromRemoteTypeP=ruleMatchTypeP; // used by local to receive from remote
+ // Find matching remote types
+ // - first look for existing remote type with same config as local one
+ TSyncItemType *remCorrTypeP = fSessionP->findRemoteType(ruleMatchTypeP->getTypeConfig(),fRemoteDatastoreP);
+ // - if none found, create one and have it inherit the CTCap options of the generic version that is already there
+ if (!remCorrTypeP) {
+ // none found: need to create one
+ remCorrTypeP = ruleMatchTypeP->newCopyForSameType(fSessionP,fRemoteDatastoreP);
+ if (remCorrTypeP) {
+ // - get generic remote type (the one that might have received CTCap already)
+ TSyncItemType *remGenericTypeP = fRemoteDatastoreP->getSendType(ruleMatchTypeP);
+ // - copy options
+ if (remGenericTypeP) remCorrTypeP->copyCTCapInfoFrom(*remGenericTypeP);
+ }
+ }
+ // now assign
+ RemoteReceiveFromLocalTypeP=remCorrTypeP;
+ RemoteSendToLocalTypeP=remCorrTypeP;
+ // Show that we are using ruleMatch type
+ PDEBUGPRINTFX(DBG_DATA+DBG_HOT,(
+ "Remote rule '%s' overrides default type usage - forcing type '%s' for send and receive",
+ fSessionP->fAppliedRemoteRuleP->getName(),
+ ruleMatchTypeP->getTypeConfig()->getName()
+ ));
+ // done
+ break;
+ }
+ }
+ }
+ #endif
+ // check if we are sync compatible (common type for both directions)
+ if (LocalSendToRemoteTypeP && LocalReceiveFromRemoteTypeP && RemoteReceiveFromLocalTypeP && RemoteSendToLocalTypeP) {
+ // avoid further changes in remote devInf (e.g. by late result of GET, sent *after* first <sync>)
+ fSessionP->fRemoteDevInfLock=true;
+ // there is a common data type for each of both directions
+ // - show local types
+ PDEBUGPRINTFX(DBG_DATA,(
+ "Local Datastore '%s' - Types: tx to remote: '%s': %s (%s), rx from remote: '%s': %s (%s)",
+ getName(),
+ LocalSendToRemoteTypeP->getTypeConfig()->getName(),LocalSendToRemoteTypeP->getTypeName(), LocalSendToRemoteTypeP->getTypeVers(),
+ LocalReceiveFromRemoteTypeP->getTypeConfig()->getName(),LocalReceiveFromRemoteTypeP->getTypeName(), LocalReceiveFromRemoteTypeP->getTypeVers()
+ ));
+ // - show remote types
+ PDEBUGPRINTFX(DBG_DATA+DBG_DETAILS,(
+ "Remote Datastore '%s' - Types: tx to local: '%s': %s (%s), rx from local: '%s': %s (%s)",
+ fRemoteDatastoreP->getName(),
+ RemoteSendToLocalTypeP->getTypeConfig()->getName(),RemoteSendToLocalTypeP->getTypeName(), RemoteSendToLocalTypeP->getTypeVers(),
+ RemoteReceiveFromLocalTypeP->getTypeConfig()->getName(),RemoteReceiveFromLocalTypeP->getTypeName(), RemoteReceiveFromLocalTypeP->getTypeVers()
+ ));
+ }
+ else {
+ // datastores are not sync compatible
+ sta=415;
+ PDEBUGPRINTFX(DBG_ERROR,("No common datastore formats -> cannot sync (415)"));
+ PDEBUGPRINTFX(DBG_EXOTIC,("- LocalSendToRemoteTypeP = '%s'", LocalSendToRemoteTypeP ? LocalSendToRemoteTypeP->getTypeName() : "<missing>"));
+ PDEBUGPRINTFX(DBG_EXOTIC,("- LocalReceiveFromRemoteTypeP = '%s'", LocalReceiveFromRemoteTypeP ? LocalReceiveFromRemoteTypeP->getTypeName() : "<missing>"));
+ PDEBUGPRINTFX(DBG_EXOTIC,("- RemoteSendToLocalTypeP = '%s'", RemoteSendToLocalTypeP ? RemoteSendToLocalTypeP->getTypeName() : "<missing>"));
+ PDEBUGPRINTFX(DBG_EXOTIC,("- RemoteReceiveFromLocalTypeP = '%s'", RemoteReceiveFromLocalTypeP ? RemoteReceiveFromLocalTypeP->getTypeName() : "<missing>"));
+ engAbortDataStoreSync(sta,true,false); // do not proceed with sync of this datastore, local problem, not resumable
+ return sta;
+ }
+ // set type info in local datastore
+ setSendTypeInfo(LocalSendToRemoteTypeP,RemoteReceiveFromLocalTypeP);
+ setReceiveTypeInfo(LocalReceiveFromRemoteTypeP,RemoteSendToLocalTypeP);
+ // - initialize usage of types (checks compatibility as well)
+ return initDataTypeUse();
+} // TLocalEngineDS::engInitForSyncOps
+
+
+// called from <sync> command to generate sync sub-commands to be sent to remote
+// Returns true if now finished for this datastore
+// also changes state to dssta_syncgendone when all sync commands have been generated
+bool TLocalEngineDS::engGenerateSyncCommands(
+ TSmlCommandPContainer &aNextMessageCommands,
+ TSmlCommand * &aInterruptedCommandP,
+ const char *aLocalIDPrefix
+)
+{
+ PDEBUGBLOCKFMT(("SyncGen","Now generating sync commands","datastore=%s",getName()));
+ bool finished=false;
+ #ifdef SYSYNC_CLIENT
+ finished = logicGenerateSyncCommandsAsClient(aNextMessageCommands, aInterruptedCommandP, aLocalIDPrefix);
+ #else
+ finished = logicGenerateSyncCommandsAsServer(aNextMessageCommands, aInterruptedCommandP, aLocalIDPrefix);
+ #endif
+ // change state when finished
+ if (finished) {
+ changeState(dssta_syncgendone,true);
+ #ifdef SYSYNC_CLIENT
+ // from client only skips to clientmapssent without any server communication
+ // (except if we are in old synthesis-compatible mode which runs from-client-only
+ // with empty sync-from-server and map phases.
+ if (getSyncMode()==smo_fromclient && !fSessionP->fCompleteFromClientOnly) {
+ // data access ends with all sync commands generated in from-client-only
+ PDEBUGPRINTFX(DBG_PROTO,("From-Client-Only sync: skipping directly to end of map phase now"));
+ changeState(dssta_dataaccessdone,true);
+ changeState(dssta_clientmapssent,true);
+ }
+ #endif
+ }
+ PDEBUGPRINTFX(DBG_DATA,(
+ "engGenerateSyncCommands ended, state='%s', sync generation %sdone",
+ getDSStateName(),
+ fLocalDSState>=dssta_syncgendone ? "" : "NOT "
+ ));
+ PDEBUGENDBLOCK("SyncGen");
+ return finished;
+} // TLocalEngineDS::engGenerateSyncCommands
+
+
+// called to confirm a sync operation's completion (status from remote received)
+// @note aSyncOp passed not necessarily reflects what was sent to remote, but what actually happened
+void TLocalEngineDS::dsConfirmItemOp(TSyncOperation aSyncOp, cAppCharP aLocalID, cAppCharP aRemoteID, bool aSuccess, localstatus aErrorStatus)
+{
+ // commands failed with "cancelled" should be re-sent for resume
+ if (!aSuccess && aErrorStatus==514 && dsResumeSupportedInDB() && fSessionP->isSuspending()) {
+ // cancelled syncop as result of explicit suspend: mark for resume as it was never really processed at the other end
+ PDEBUGPRINTFX(DBG_DATA+DBG_EXOTIC,("Cancelled SyncOp during suspend -> mark for resume"));
+ engMarkItemForResume(aLocalID,aRemoteID,true);
+ }
+ PDEBUGPRINTFX(DBG_DATA+DBG_EXOTIC,(
+ "dsConfirmItemOp completed, syncop=%s, localID='%s', remoteID='%s', %s, errorstatus=%hd",
+ SyncOpNames[aSyncOp],
+ aLocalID ? aLocalID : "<none>",
+ aRemoteID ? aRemoteID : "<none>",
+ aSuccess ? "SUCCESS" : "FAILURE",
+ aErrorStatus
+ ));
+} // TLocalEngineDS::dsConfirmItemOp
+
+
+
+// handle status of sync operation
+// Note: in case of superdatastore, status is always directed to the originating subdatastore, as
+// the fDataStoreP of the SyncOpCommand is set to subdatastore when generating the SyncOps.
+bool TLocalEngineDS::engHandleSyncOpStatus(TStatusCommand *aStatusCmdP,TSyncOpCommand *aSyncOpCmdP)
+{
+ TSyError statuscode = aStatusCmdP->getStatusCode();
+ // we can make it simple here because we KNOW that we do not send multiple items per SyncOp, so we
+ // just need to look at the first item's target and source
+ const char *localID = aSyncOpCmdP->getSourceLocalID();
+ const char *remoteID = aSyncOpCmdP->getTargetRemoteID();
+ #ifndef SYSYNC_CLIENT
+ string realLocID;
+ #endif
+ if (localID) {
+ #ifdef SUPERDATASTORES
+ // remove eventual prefix if this item was sent in the <sync> command context of a superdatastore
+ if (fAsSubDatastoreOf) {
+ // let superdatastore remove the prefix for me
+ localID = fAsSubDatastoreOf->removeSubDSPrefix(localID,this);
+ }
+ #endif
+ #ifndef SYSYNC_CLIENT
+ // for server only: convert to internal representation
+ realLocID=localID;
+ obtainRealLocalID(realLocID);
+ localID=realLocID.c_str();
+ #endif
+ }
+ // handle special cases for Add/Replace/Delete
+ TSyncOperation sop = aSyncOpCmdP->getSyncOp();
+ switch (sop) {
+ case sop_wants_add:
+ case sop_add:
+ if (statuscode==201) {
+ fRemoteItemsAdded++;
+ dsConfirmItemOp(sop_add,localID,remoteID,true); // ok added
+ }
+ else if (statuscode==200) {
+ fRemoteItemsUpdated++;
+ dsConfirmItemOp(sop_replace,localID,remoteID,true); // ok replaced
+ }
+ else if (
+ statuscode==418 &&
+ (isResuming()
+ #ifdef SYSYNC_CLIENT
+ || isSlowSync()
+ #endif
+ )
+ ) {
+ // "Already exists"/418 is acceptable...
+ // ... in slow sync as client, as some servers use it instead of 200/419 for slow sync match
+ // ... during resumed sync as server with clients like Symbian which
+ // can detect duplicate adds themselves. Should not generally
+ // occur, as we shouldn't re-send them as long as we haven't seen
+ // a map. But symbian cannot send early maps - it instead does
+ // it's own duplicate checking.
+ // ... during resumed sync as client (as servers might issue 418 for
+ // items send a second time after an implicit suspend)
+ PDEBUGPRINTFX(DBG_ERROR,("Warning: received 418 status for add in resumed/slowsync session -> treat it as ok (200)"));
+ dsConfirmItemOp(sop_replace,localID,remoteID,true); // kind of ok
+ statuscode=200; // convert to ok (but no count incremented, as nothing changed)
+ }
+ else {
+ dsConfirmItemOp(sop_add,localID,remoteID,false,statuscode); // failed add
+ }
+ // adding with 420 error: device full
+ if (statuscode==420) {
+ // special case: device indicates that it is full, so stop adding in this session
+ PDEBUGPRINTFX(DBG_ERROR,("Warning: Status %hd: Remote device full -> preventing further adds in this session",statuscode));
+ engStopAddingToRemote();
+ fRemoteItemsError++; // this is considered a remote item error
+ }
+ break;
+ // case sop_copy: break;
+ case sop_wants_replace:
+ case sop_replace:
+ #ifndef SYSYNC_CLIENT
+ if (statuscode==404 || statuscode==410) {
+ // obviously, remote item that we wanted to change does not exist any more.
+ // Instead of aborting the session we'll just remove the map item for that
+ // server item, such that it will be re-added in the next sync session
+ PDEBUGPRINTFX(DBG_DATA,("Status %hd: Replace target not found on client -> silently ignore but remove map in server (item will be added in next session), ",statuscode));
+ // remove map for remote item(s), targetRef contain remoteIDs
+ SmlTargetRefListPtr_t targetrefP = aStatusCmdP->getStatusElement()->targetRefList;
+ while (targetrefP) {
+ // target ref available
+ engProcessMap(smlPCDataToCharP(targetrefP->targetRef),NULL);
+ // next
+ targetrefP=targetrefP->next;
+ }
+ statuscode=410; // always use "gone" status (even if we might have received a 404)
+ dsConfirmItemOp(sop_replace,localID,remoteID,false,statuscode);
+ break;
+ }
+ else
+ #endif
+ if (statuscode==201) {
+ fRemoteItemsAdded++;
+ dsConfirmItemOp(sop_add,localID,remoteID,true); // ok as add
+ }
+ else if (statuscode<300 || statuscode==419) { // conflict resolved counts as ok as well
+ fRemoteItemsUpdated++;
+ dsConfirmItemOp(sop_replace,localID,remoteID,true); // ok as replace
+ }
+ #ifdef SYSYNC_CLIENT
+ else if (isSlowSync() && statuscode==418) {
+ // "Already exists"/418 is acceptable as client in slow sync because some
+ // servers use it instead of 200/419 for slow sync match
+ PDEBUGPRINTFX(DBG_ERROR,("Warning: received 418 for for replace during slow sync - treat it as ok (200), but don't count as update"));
+ dsConfirmItemOp(sop_replace,localID,remoteID,true); // 418 is acceptable in slow sync (not really conformant, but e.g. happening with Scheduleworld)
+ statuscode=200; // always use "gone" status (even if we might have received a 404)
+ }
+ #endif // SYSYNC_CLIENT
+ else {
+ dsConfirmItemOp(sop_replace,localID,remoteID,false,statuscode); // failed replace
+ }
+ break;
+ case sop_archive_delete:
+ case sop_soft_delete:
+ case sop_delete:
+ if (statuscode<211) fRemoteItemsDeleted++;
+ // allow 211 and 404 for delete - after all, the record is not there
+ // any more on the remote
+ if (statuscode==404 || statuscode==211) {
+ PDEBUGPRINTFX(DBG_DATA,("Status: %hd: To-be-deleted item not found, but accepted this (changed status to 200)",statuscode));
+ statuscode=200;
+ }
+ // if ok (explicit or implicit), we can confirm the delete
+ dsConfirmItemOp(sop_delete,localID,remoteID,statuscode<300,statuscode); // counts as ok delete
+ break;
+ default: break;
+ } // switch
+ // check if we want to mark failed items for resend in the next session or abort
+ bool resend = fDSConfigP->fResendFailing; // get default from config
+ #ifdef SCRIPT_SUPPORT
+ // let script check status code
+ TErrorFuncContext errctx;
+ errctx.statuscode = statuscode;
+ errctx.resend = resend;
+ errctx.newstatuscode = statuscode;
+ errctx.syncop = sop;
+ errctx.datastoreP = this;
+ // - first check datastore level
+ if (
+ TScriptContext::executeTest(
+ false, // assume script does NOT handle status entirely
+ fDataStoreScriptContextP,
+ fDSConfigP->fSentItemStatusScript,
+ &ErrorFuncTable,
+ &errctx // caller context
+ )
+ ) {
+ // completely handled
+ PDEBUGPRINTFX(DBG_ERROR,("Status: %hd: Handled by datastore script (original op was %s)",statuscode,SyncOpNames[sop]));
+ return true;
+ }
+ errctx.statuscode = errctx.newstatuscode;
+ // - then check session level
+ if (
+ TScriptContext::executeTest(
+ false, // assume script does NOT handle status entirely
+ fSessionP->fSessionScriptContextP,
+ fSessionP->getSessionConfig()->fSentItemStatusScript,
+ &ErrorFuncTable,
+ &errctx // caller context
+ )
+ ) {
+ // completely handled
+ PDEBUGPRINTFX(DBG_ERROR,("Status: %hd: Handled by session script (original op was %s)",statuscode,SyncOpNames[sop]));
+ return true;
+ }
+ // not completely handled, use eventually modified status code
+ #ifdef SYDEBUG
+ if (statuscode != errctx.newstatuscode) {
+ PDEBUGPRINTFX(DBG_ERROR,("Status: Script changed original status=%hd to %hd (original op was %s)",statuscode,errctx.newstatuscode,SyncOpNames[errctx.syncop]));
+ }
+ #endif
+ statuscode = errctx.newstatuscode;
+ resend = errctx.resend;
+ #endif
+ // now perform default action according to status code
+ switch (statuscode) {
+ case 200:
+ break;
+ case 201:
+ PDEBUGPRINTFX(DBG_PROTO,("Status: %hd: Item added (original op was %s)",statuscode,SyncOpNames[sop]));
+ break;
+ case 204:
+ PDEBUGPRINTFX(DBG_PROTO,("Status: %hd: No content (original op was %s)",statuscode,SyncOpNames[sop]));
+ break;
+ case 207:
+ case 208:
+ case 209:
+ case 419:
+ PDEBUGPRINTFX(DBG_HOT,("Status: %hd: Conflict resolved (original op was %s)",statuscode,SyncOpNames[sop]));
+ break;
+ case 210:
+ PDEBUGPRINTFX(DBG_HOT,("Status: %hd: Delete without archive (original op was %s)",statuscode,SyncOpNames[sop]));
+ break;
+ case 211:
+ PDEBUGPRINTFX(DBG_ERROR,("Status: %hd: nothing deleted, item not found (original op was %s)",statuscode,SyncOpNames[sop]));
+ break;
+ case 410: // gone
+ case 420: // device full
+ // these have been handled above and are considered ok now
+ break;
+ case 514: // cancelled
+ // ignore cancelling while suspending, as these are CAUSED by the suspend
+ if (fSessionP->isSuspending() && dsResumeSupportedInDB()) {
+ // don't do anything here - we'll be suspended later (but process commands until then)
+ // dsConfirmItemOp() has already caused the item to be marked for resume
+ break;
+ }
+ // for non-DS-1.2 sessions, we treat 514 like the other errors below (that is - retry might help)
+ case 424: // size mismatch (e.g. due to faild partial item resume attempt -> retry will help)
+ case 417: // retry later (remote says that retry will probably work)
+ case 506: // processing error, retry later (remote says that retry will probably work)
+ case 404: // not found (retry is not likely to help, but does not harm too much, either)
+ case 408: // timeout (if that happens on a single item, retry probably helps)
+ case 415: // bad type (retry is not likely to help, but does not harm too much, either)
+ case 510: // datastore failure (too unspecific to know if retry might help, but why not?)
+ case 500: // general failure (too unspecific to know if retry might help, but why not?)
+ // these errors cause either a resend in a later session
+ // or only abort the datastore, but not the session
+ if (resend && dsResumeSupportedInDB()) {
+ PDEBUGPRINTFX(DBG_ERROR,("Status: General error %hd (original op was %s) -> marking item for resend in next session",statuscode,SyncOpNames[sop]));
+ engMarkItemForResend(localID,remoteID); // Note: includes incrementing fRemoteItemsError
+ }
+ else {
+ PDEBUGPRINTFX(DBG_ERROR,("Status: General error %hd (original op was %s) -> aborting sync with this datastore",statuscode,SyncOpNames[sop]));
+ engAbortDataStoreSync(statuscode,false); // remote problem
+ }
+ break;
+ default:
+ // let command handle it
+ return false;
+ //break;
+ }
+ // status handled
+ return true; // handled status
+} // TLocalEngineDS::engHandleSyncOpStatus
+
+
+/// Internal events during sync for derived classes
+/// @Note local DB authorisation must be established already before calling these
+/// - cause loading of all session anchoring info and other admin data (logicMakeAdminReady())
+/// fLastRemoteAnchor,fLastLocalAnchor,fNextLocalAnchor; isFirstTimeSync() will be valid after the call
+/// - in case of superdatastore, consolidates the anchor information from the subdatastores
+localstatus TLocalEngineDS::engInitSyncAnchors(
+ cAppCharP aDatastoreURI, ///< local datastore URI
+ cAppCharP aRemoteDBID ///< ID of remote datastore (to find session information in local DB)
+)
+{
+ // nothing more to do than making admin data ready
+ // - this will fill all dsSavedAdminData members here and in all derived classes
+ localstatus sta=logicMakeAdminReady(aDatastoreURI, aRemoteDBID);
+ if (sta==LOCERR_OK) {
+ changeState(dssta_adminready); // admin data is now ready
+ }
+ // return on error
+ return sta;
+} // TLocalEngineDS::engInitSyncAnchors
+
+
+#ifdef SYSYNC_CLIENT
+
+// initialize Sync alert for datastore according to Parameters set with dsSetClientSyncParams()
+localstatus TLocalEngineDS::engPrepareClientSyncAlert(TSuperDataStore *aAsSubDatastoreOf)
+{
+ localstatus sta;
+
+ #ifdef SUPERDATASTORES
+ // check if not already alerted as subdatastore
+ if (fAsSubDatastoreOf) {
+ // bad, cannot be alerted directly AND as subdatastore
+ DEBUGPRINTFX(DBG_ERROR,("trying to prepare alert for already alerted subdatastore"));
+ return LOCERR_WRONGUSAGE;
+ }
+ // set subdatastore mode
+ fAsSubDatastoreOf=aAsSubDatastoreOf;
+ #endif
+ #ifdef SCRIPT_SUPPORT
+ // AlertPrepareScript to add filters and CGI
+ // - rebuild early (before all of the other DS scripts in makeAdminReady caused by engInitSyncAnchors below!)
+ TScriptContext::rebuildContext(fSessionP->getSyncAppBase(),fDSConfigP->fAlertPrepScript,fDataStoreScriptContextP,fSessionP);
+ // - add custom DS 1.2 filters and/or custom CGI to fRemoteDBPath
+ TScriptContext::execute(
+ fDataStoreScriptContextP,
+ fDSConfigP->fAlertPrepScript,
+ fDSConfigP->getClientDBFuncTable(), // function table with extra
+ this // datastore pointer needed for context
+ );
+ #endif
+ // - get information about last session out of database
+ sta=engInitSyncAnchors(
+ relativeURI(fLocalDBPath.c_str()),
+ fRemoteDBPath.c_str()
+ );
+ if (sta!=LOCERR_OK) {
+ // error getting anchors
+ PDEBUGPRINTFX(DBG_ERROR,("Could not get Sync Anchor info"));
+ return localError(sta);
+ }
+ // check if we are forced to slowsync (otherwise, fSlowSync is pre-set from dsSetClientSyncParams()
+ fSlowSync = fSlowSync || fLastLocalAnchor.empty() || fFirstTimeSync;
+ // check for resume
+ if (fResumeAlertCode!=0 && fSessionP->getSyncMLVersion()>=syncml_vers_1_2) {
+ // we have a suspended session, try to resume
+ PDEBUGPRINTFX(DBG_PROTO,("Found suspended session with Alert Code = %hd",fResumeAlertCode));
+ fResuming=true;
+ }
+ return LOCERR_OK; // ok
+} // TLocalEngineDS::engPrepareClientSyncAlert
+
+
+// generate Sync alert for datastore after initialisation with engPrepareClientSyncAlert()
+// Note: this could be repeatedly called due to auth failures at beginning of session
+// Note: this is a NOP for subDatastores (should not be called in this case, anyway)
+localstatus TLocalEngineDS::engGenerateClientSyncAlert(
+ TAlertCommand *&aAlertCommandP
+)
+{
+ aAlertCommandP=NULL;
+ #ifdef SUPERDATASTORES
+ if (fAsSubDatastoreOf) return LOCERR_OK; // NOP, ok
+ #endif
+
+ PDEBUGPRINTFX(DBG_PROTO,(
+ "(Saved) Last Local Client Anchor='%s', (generated) Next Local Client Anchor='%s' (sent to server as <last>/<next> in <alert>)",
+ fLastLocalAnchor.c_str(),
+ fNextLocalAnchor.c_str()
+ ));
+ // create appropriate initial alert command
+ uInt16 alertCode = getSyncStateAlertCode(fServerAlerted,true);
+ // check for resume
+ if (fResuming) {
+ // check if what we resume is same as what we wanted to do
+ if (alertCode != fResumeAlertCode) {
+ // this is ok for client, just show in log
+ PDEBUGPRINTFX(DBG_PROTO,(
+ "Sync mode seems to have changed (alert code = %hd) since last Suspend (alert code = %hd)",
+ alertCode,
+ fResumeAlertCode
+ ));
+ }
+ // resume
+ alertCode=225; // resume
+ PDEBUGPRINTFX(DBG_PROTO,(
+ "Alerting resume of last sync session (original alert code = %hd)",
+ fResumeAlertCode
+ ));
+ }
+ aAlertCommandP = new TAlertCommand(fSessionP,this,alertCode);
+ PDEBUGPRINTFX(DBG_HOT,(
+ "%s: ALERTING server for %s%s%s Sync",
+ getName(),
+ fResuming ? "RESUMED " : "",
+ fSlowSync ? "slow" : "normal",
+ fFirstTimeSync ? " first time" : ""
+ ));
+ // add Item with Anchors and URIs
+ SmlItemPtr_t itemP = newItem();
+ // - anchors
+ itemP->meta=newMetaAnchor(fNextLocalAnchor.c_str(),fLastLocalAnchor.c_str());
+ // - MaxObjSize here again to make SCTS happy
+ if (
+ (fSessionP->getSyncMLVersion()>=syncml_vers_1_1) &&
+ (fSessionP->getRootConfig()->fLocalMaxObjSize>0)
+ ) {
+ // SyncML 1.1 has object size and we need to put it here for SCTS
+ smlPCDataToMetInfP(itemP->meta)->maxobjsize=newPCDataLong(
+ fSessionP->getRootConfig()->fLocalMaxObjSize
+ );
+ }
+ // - URIs
+ itemP->source=newLocation(fLocalDBPath.c_str());
+ itemP->target=newLocation(fRemoteDBPath.c_str()); // use remote path as configured in client settings
+ // - add DS 1.2 filters
+ if (!fRemoteRecordFilterQuery.empty() || false /* %%% field level filter */) {
+ if (fSessionP->getSyncMLVersion()<syncml_vers_1_2) {
+ PDEBUGPRINTFX(DBG_ERROR,("Filter specified, but SyncML version is < 1.2"));
+ return 406; // feature not supported
+ }
+ SmlFilterPtr_t filterP = SML_NEW(SmlFilter_t);
+ memset(filterP,0,sizeof(SmlFilter_t));
+ if (filterP) {
+ // Must have a meta with the content type
+ // - get the preferred receive type
+ TSyncItemType *itemTypeP = fSessionP->findLocalType(fDSConfigP->fTypeSupport.fPreferredRx);
+ if (itemTypeP) {
+ // add meta type
+ filterP->meta = newMetaType(itemTypeP->getTypeName());
+ }
+ // add filtertype if needed (=not EXCLUSIVE)
+ if (fRemoteFilterInclusive) {
+ filterP->filtertype=newPCDataString(SYNCML_FILTERTYPE_INCLUSIVE);
+ }
+ // record level
+ if (!fRemoteRecordFilterQuery.empty()) {
+ // add <record>
+ filterP->record = SML_NEW(SmlRecordOrFieldFilter_t);
+ // - add item with data=filterquery
+ filterP->record->item = newStringDataItem(fRemoteRecordFilterQuery.c_str());
+ // - add meta type with grammar
+ filterP->record->item->meta = newMetaType(SYNCML_FILTERTYPE_CGI);
+ PDEBUGPRINTFX(DBG_HOT,(
+ "Alerting with %sCLUSIVE Record Level Filter Query = '%s'",
+ fRemoteFilterInclusive ? "IN" : "EX",
+ fRemoteRecordFilterQuery.c_str()
+ ));
+ }
+ // field level
+ /// @todo %%% to be implemented
+ if (false) {
+ // !!! remember to add real check (now: false) in outer if-statement as well!!!!!
+ // %%% tbd
+ }
+ }
+ // add it to item
+ itemP->target->filter = filterP;
+ }
+ // - add to alert command
+ aAlertCommandP->addItem(itemP);
+ // we have now produced the client alert command, change state
+ return changeState(dssta_clientsentalert);
+} // TLocalEngineDS::engGenerateClientSyncAlert
+
+
+// Init engine for client sync
+// - determine types to exchange
+// - make sync set ready
+localstatus TLocalEngineDS::engInitForClientSync(void)
+{
+ // - prepare engine for sync (determining types)
+ localstatus sta = engInitForSyncOps(getRemoteDBPath());
+ if (sta==LOCERR_OK) {
+ // - let local datastore (derived DB-specific class) prepare for sync
+ sta = changeState(dssta_dataaccessstarted);
+ if (sta==LOCERR_OK && isStarted(false)) {
+ // already started now, change state
+ sta = changeState(dssta_syncsetready);
+ }
+ }
+ return sta;
+} // TLocalEngineDS::engInitForClientSync
+
+
+#endif // Client
+
+
+// get Alert code for current Sync State
+uInt16 TLocalEngineDS::getSyncStateAlertCode(bool aServerAlerted, bool aClientMinimal)
+{
+ uInt16 alertcode=0;
+
+ switch (fSyncMode) {
+ case smo_twoway :
+ alertcode = aServerAlerted ? 206 : 200;
+ break;
+ case smo_fromclient :
+ alertcode = aServerAlerted ? 207 : 202; // fully specified
+ break;
+ case smo_fromserver :
+ if (aClientMinimal) {
+ // refresh from server is just client not sending any data, so we can alert like two-way
+ alertcode = aServerAlerted ? 206 : 200;
+ }
+ else {
+ // correctly alert it
+ alertcode = aServerAlerted ? 209 : 204;
+ }
+ break;
+ case numSyncModes:
+ // invalid
+ break;
+ }
+ // slowsync/refresh variants are always plus one, except 206 --> 201 (same as client initiated slow sync)
+ if (fSlowSync) alertcode = (alertcode!=206 ? alertcode+1 : 201);
+ return alertcode;
+} // TLocalEngineDS::getSyncStateAlertCode
+
+
+/// initializes Sync state variables and returns false if alert is not supported
+localstatus TLocalEngineDS::setSyncModeFromAlertCode(uInt16 aAlertCode, bool aAsClient)
+{
+ localstatus sta;
+ TSyncModes syncMode;
+ bool slowSync, serverAlerted;
+ // - translate into mode and flags
+ sta=getSyncModeFromAlertCode(aAlertCode,syncMode,slowSync,serverAlerted);
+ if (sta==LOCERR_OK) {
+ // - set them
+ sta=setSyncMode(aAsClient,syncMode,slowSync,serverAlerted);
+ }
+ return sta;
+} // TLocalEngineDS::setSyncModeFromAlertCode
+
+
+/// initializes Sync mode variables
+localstatus TLocalEngineDS::setSyncMode(bool aAsClient, TSyncModes aSyncMode, bool aIsSlowSync, bool aIsServerAlerted)
+{
+ // get sync caps of this datastore
+ uInt32 synccapmask=getSyncCapMask();
+ // check if mode supported
+ if (aIsServerAlerted) {
+ // check if we support server alerted modes, SyncCap/Bit=7
+ if (~synccapmask & (1<<7)) return 406; // not supported
+ }
+ switch(aSyncMode) {
+ case smo_twoway:
+ // Two-way Sync, SyncCap/Bit=1
+ // or Two-way slow Sync, SyncCap/Bit=2
+ if (~synccapmask & (1<< (aIsSlowSync ? 2 : 1))) return 406; // not supported
+ if (fSyncMode==smo_fromserver && aAsClient)
+ aSyncMode=smo_fromserver; // for client, if already fromserver mode set, keep it
+ break;
+ case smo_fromclient:
+ // One-way from client, SyncCap/Bit=3
+ // or Refresh (=slow one-way) from client, SyncCap/Bit=4
+ if (~synccapmask & (1<< (aIsSlowSync ? 4 : 3))) return 406; // not supported
+ if (!aAsClient) fRefreshOnly=true; // as server, we are in refresh-only-mode if we get one-way from client
+ break;
+ case smo_fromserver:
+ // One-way from server, SyncCap/Bit=5
+ // or Refresh (=slow one-way) from server, SyncCap/Bit=6
+ if (~synccapmask & (1<< (aIsSlowSync ? 6 : 5))) return 406; // not supported
+ if (aAsClient) fRefreshOnly=true; // as client, we are in refresh-only-mode if we get one-way fromm server
+ break;
+ default:
+ return 400; // bad request
+ }
+ // now set mode and flags (possibly adjusted above)
+ fSyncMode=aSyncMode;
+ fSlowSync=aIsSlowSync;
+ fServerAlerted=aIsServerAlerted;
+ // ok
+ return LOCERR_OK;
+} // TLocalEngineDS::setSyncMode
+
+
+/// get Sync mode variables from given alert code
+localstatus TLocalEngineDS::getSyncModeFromAlertCode(uInt16 aAlertCode, TSyncModes &aSyncMode, bool &aIsSlowSync, bool &aIsServerAlerted)
+{
+ // these might be pre-defined)
+ /// @deprecated state change does not belong here
+ aIsSlowSync=false;
+ aIsServerAlerted=false;
+ aSyncMode=smo_twoway; // to make sure it is valid
+ // first test if server-alerted
+ if (aAlertCode>=206 && aAlertCode<210) {
+ // Server alerted modes
+ aIsServerAlerted=true;
+ }
+ // test for compatibility with alert code
+ switch(aAlertCode) {
+ case 200:
+ case 206:
+ // Two-way Sync
+ aSyncMode=smo_twoway;
+ aIsSlowSync=false;
+ break;
+ case 201:
+ // Two-way slow Sync
+ aSyncMode=smo_twoway;
+ aIsSlowSync=true;
+ break;
+ case 202:
+ case 207:
+ // One-way from client
+ aSyncMode=smo_fromclient;
+ aIsSlowSync=false;
+ break;
+ case 203:
+ case 208:
+ // refresh (=slow one-way) from client
+ aSyncMode=smo_fromclient;
+ aIsSlowSync=true;
+ break;
+ case 204:
+ case 209:
+ // One-way from server
+ aSyncMode=smo_fromserver;
+ aIsSlowSync=false;
+ break;
+ case 205:
+ case 210:
+ // refresh (=slow one-way) from server
+ aSyncMode=smo_fromserver;
+ aIsSlowSync=true;
+ break;
+ default:
+ // bad alert
+ return 400;
+ }
+ return LOCERR_OK;
+} // TLocalEngineDS::getSyncModeFromAlertCode
+
+
+// create new Sync capabilities info from capabilities mask
+// Bit0=reserved, Bit1..Bitn = SyncType 1..n available
+// Note: derived classes might add special sync codes and/or mask standard ones
+SmlDevInfSyncCapPtr_t TLocalEngineDS::newDevInfSyncCap(uInt32 aSyncCapMask)
+{
+ SmlDevInfSyncCapPtr_t synccapP;
+ SmlPcdataPtr_t synctypeP;
+
+ // new synccap structure
+ synccapP = SML_NEW(SmlDevInfSyncCap_t);
+ // synccap list is empty
+ synccapP->synctype=NULL;
+ // now add standard synccaps
+ for (sInt16 k=0; k<32; k++) {
+ if (aSyncCapMask & (1<<k)) {
+ // capability available
+ synctypeP=newPCDataLong(k);
+ addPCDataToList(synctypeP,&(synccapP->synctype));
+ }
+ }
+ // return it
+ return synccapP;
+} // TLocalEngineDS::newDevInfSyncCap
+
+
+// obtain new datastore info, returns NULL if none available
+SmlDevInfDatastorePtr_t TLocalEngineDS::newDevInfDatastore(bool aAsServer, bool aWithoutCTCapProps)
+{
+ SmlDevInfDatastorePtr_t datastoreP;
+
+ // create new
+ datastoreP=SML_NEW(SmlDevInfDatastore_t);
+ // set only basic info, details must be added in derived class
+ // - sourceref is the name of the datastore,
+ // or for server, if already alerted, the name used in the alert
+ // (this is to allow /dsname/foldername with clients that expect the
+ // devInf to contain exactly the same full path as name, like newer Nokias)
+ string dotname;
+ #ifndef SYSYNC_CLIENT
+ if (testState(dssta_serveralerted,false) && fSessionP->fDSPathInDevInf) {
+ // server and already alerted - use datastore spec as sent from remote, minus CGI, as relative spec
+ dotname = URI_RELPREFIX;
+ dotname += fSessionP->SessionRelativeURI(fRemoteViewOfLocalURI.c_str());
+ if (!fSessionP->fDSCgiInDevInf) {
+ // remove CGI
+ int n=dotname.find("?");
+ if (n!=string::npos)
+ dotname.resize(n); // remove CGI
+ }
+ }
+ else
+ #endif
+ {
+ // client or not yet alerted - just use datastore base name
+ StringObjPrintf(dotname,URI_RELPREFIX "%s",fName.c_str());
+ }
+ datastoreP->sourceref=newPCDataString(dotname);
+ #ifndef MINIMAL_CODE
+ // - Optional display name
+ datastoreP->displayname=newPCDataOptString(getDisplayName());
+ #else
+ datastoreP->displayname=NULL;
+ #endif
+ // - MaxGUIDsize (for client only)
+ if (aAsServer)
+ datastoreP->maxguidsize=NULL;
+ else
+ datastoreP->maxguidsize=newPCDataLong(fMaxGUIDSize);
+ // - check for legacy mode type (that is to be used as "preferred" instead of normal preferred)
+ TSyncItemType *legacyTypeP = NULL;
+ if (getSession()->fLegacyMode && getDSConfig()->fTypeSupport.fPreferredLegacy) {
+ // get the type marked as blind
+ legacyTypeP = getSession()->findLocalType(getDSConfig()->fTypeSupport.fPreferredLegacy);
+ }
+ // - RxPref
+ if (!fRxPrefItemTypeP) SYSYNC_THROW(TStructException("Datastore has no RxPref ItemType"));
+ datastoreP->rxpref = (legacyTypeP ? legacyTypeP : fRxPrefItemTypeP)->newXMitDevInf();
+ // - Rx (excluding the type we report as preferred)
+ datastoreP->rx=TSyncItemType::newXMitListDevInf(fRxItemTypes,legacyTypeP ? legacyTypeP : fRxPrefItemTypeP);
+ // - TxPref
+ if (!fTxPrefItemTypeP) SYSYNC_THROW(TStructException("Datastore has no TxPref ItemType"));
+ datastoreP->txpref = (legacyTypeP ? legacyTypeP : fTxPrefItemTypeP)->newXMitDevInf();
+ // - Tx (excluding the type we report as preferred)
+ datastoreP->tx=TSyncItemType::newXMitListDevInf(fTxItemTypes,legacyTypeP ? legacyTypeP : fTxPrefItemTypeP);
+ // - DSMem
+ /// @todo %%% tbd: add dsmem
+ datastoreP->dsmem=NULL;
+ // - SyncML DS 1.2 datastore-local CTCap
+ if (fSessionP->getSyncMLVersion()>=syncml_vers_1_2) {
+ // CTCap is local to datastore, get only CTCaps relevant for this datastore, but independently from alerted state
+ datastoreP->ctcap = fSessionP->newLocalCTCapList(false, this, aWithoutCTCapProps);
+ }
+ else
+ datastoreP->ctcap=NULL; // before SyncML 1.2, there was no datastore-local CTCap
+ // - SyncML DS 1.2 flags (SmlDevInfHierarchical_f)
+ /// @todo %%% tbd: add SmlDevInfHierarchical_f
+ datastoreP->flags=0;
+ // - SyncML DS 1.2 filters
+ datastoreP->filterrx=NULL;
+ datastoreP->filtercap=NULL;
+ #if defined(OBJECT_FILTERING) && !defined(SYSYNC_CLIENT)
+ if (fDSConfigP->fDS12FilterSupport && fSessionP->getSyncMLVersion()>=syncml_vers_1_2) {
+ // Show Filter info in 1.2 devInf if this is not a client
+ // - FilterRx constant
+ datastoreP->filterrx = SML_NEW(SmlDevInfXmitList_t);
+ datastoreP->filterrx->next = NULL;
+ datastoreP->filterrx->data = SML_NEW(SmlDevInfXmit_t);
+ datastoreP->filterrx->data->cttype=newPCDataString(SYNCML_FILTERTYPE_CGI);
+ datastoreP->filterrx->data->verct=newPCDataString(SYNCML_FILTERTYPE_CGI_VERS);
+ // build filtercap
+ SmlPcdataListPtr_t filterkeywords = NULL;
+ SmlPcdataListPtr_t filterprops = NULL;
+ // - fill the lists from types
+ addFilterCapPropsAndKeywords(filterkeywords,filterprops);
+ // - if we have something, actually build a filtercap
+ if (filterkeywords || filterprops) {
+ // we have filtercaps, add them
+ // - FilterCap list
+ datastoreP->filtercap = SML_NEW(SmlDevInfFilterCapList_t);
+ datastoreP->filtercap->next = NULL;
+ datastoreP->filtercap->data = SML_NEW(SmlDevInfFilterCap_t);
+ datastoreP->filtercap->data->cttype=newPCDataString(SYNCML_FILTERTYPE_CGI);
+ datastoreP->filtercap->data->verct=newPCDataString(SYNCML_FILTERTYPE_CGI_VERS);
+ // - add list we've got
+ datastoreP->filtercap->data->filterkeyword=filterkeywords;
+ datastoreP->filtercap->data->propname=filterprops;
+ }
+ }
+ #endif
+ // - Sync capabilities of this datastore
+ datastoreP->synccap=newDevInfSyncCap(getSyncCapMask());
+ // return it
+ return datastoreP;
+} // TLocalEngineDS::newDevInfDatastore
+
+
+// Set remote datastore for local
+void TLocalEngineDS::engSetRemoteDatastore(
+ TRemoteDataStore *aRemoteDatastoreP // the remote datastore involved
+)
+{
+ // save link to remote datastore
+ if (fRemoteDatastoreP) {
+ if (fRemoteDatastoreP!=aRemoteDatastoreP)
+ SYSYNC_THROW(TSyncException("Sync continues with different datastore"));
+ }
+ fRemoteDatastoreP=aRemoteDatastoreP;
+} // TLocalEngineDS::engSetRemoteDatastore
+
+
+// SYNC command bracket start (check credentials if needed)
+bool TLocalEngineDS::engProcessSyncCmd(
+ SmlSyncPtr_t aSyncP, // the Sync element
+ TStatusCommand &aStatusCommand, // status that might be modified
+ bool &aQueueForLater // will be set if command must be queued for later (re-)execution
+)
+{
+ // get number of changes info if available
+ if (aSyncP->noc) {
+ StrToLong(smlPCDataToCharP(aSyncP->noc),fRemoteNumberOfChanges);
+ }
+ // check if datastore is aborted
+ if (CheckAborted(aStatusCommand)) return false;
+ // check
+ if (!fRemoteDatastoreP)
+ SYSYNC_THROW(TSyncException("No remote datastore linked"));
+ // let remote datastore process it first
+ if (!fRemoteDatastoreP->remoteProcessSyncCmd(aSyncP,aStatusCommand,aQueueForLater)) {
+ PDEBUGPRINTFX(DBG_ERROR,("TLocalEngineDS::engProcessSyncCmd: remote datastore failed processing <sync>"));
+ changeState(dssta_idle,true); // force it
+ return false;
+ }
+ // check for combined init&sync
+ if (!testState(dssta_syncmodestable,false)) {
+ // <sync> encountered before sync mode stable: could be combined init&sync session
+ if (fLocalDSState>=dssta_serveransweredalert) {
+ // ok for switching to combined init&sync
+ PDEBUGPRINTFX(DBG_HOT,("TLocalEngineDS::engProcessSyncCmd: detected combined init&sync, freeze sync mode already now"));
+ // - freeze sync mode as it is now
+ changeState(dssta_syncmodestable,true); // force it, sync mode is now stable, no further changes are possible
+ }
+ }
+ // now init if this is the first <sync> command
+ bool startingNow = false; // assume start already initiated
+ if (testState(dssta_syncmodestable,true)) {
+ // all alert and alert status must be done by now, sync mode must be stable
+ CONSOLEPRINTF(("- Starting Sync with Datastore '%s', %s sync",fRemoteViewOfLocalURI.c_str(),fSlowSync ? "slow" : "normal"));
+ startingNow = true; // initiating start now
+ #ifndef SYSYNC_CLIENT
+ // - let local datastore (derived DB-specific class) prepare for sync
+ localstatus sta = changeState(dssta_dataaccessstarted);
+ if (sta!=LOCERR_OK) {
+ // abort session (old comment: %%% aborting datastore only does not work, will loop, why? %%%)
+ aStatusCommand.setStatusCode(syncmlError(sta));
+ PDEBUGPRINTFX(DBG_ERROR,("TLocalEngineDS::engProcessSyncCmd: could not change state to dsssta_dataaccessstarted -> abort"));
+ engAbortDataStoreSync(sta,true); // local problem
+ return false;
+ }
+ #endif
+ }
+ // if data access started (finished or not), check start status
+ // for every execution and re-execution of the sync command
+ if (testState(dssta_dataaccessstarted)) {
+ // queue <sync> command if datastore is not yet started already
+ if (engIsStarted(!startingNow)) { // wait only if start was already initiated
+ // - is now initialized
+ #ifndef SYSYNC_CLIENT
+ // - for server, make the sync set ready now (as engine is now started)
+ changeState(dssta_syncsetready,true); // force it
+ #else
+ // - for client, we need at least dssta_syncgendone (sync set has been ready long ago, we've already sent changes to server!)
+ if (!testState(dssta_syncgendone)) {
+ // bad sequence of commands (<sync> from server too early!)
+ aStatusCommand.setStatusCode(400);
+ PDEBUGPRINTFX(DBG_ERROR,("TLocalEngineDS::engProcessSyncCmd: client received SYNC before sending SYNC complete"));
+ engAbortDataStoreSync(400,false,false); // remote problem, not resumable
+ return false;
+ }
+ #endif
+ PDEBUGPRINTFX(DBG_HOT,(
+ "- Started %s Sync (first <sync> command)",
+ fSlowSync ? "slow" : "normal"
+ ));
+ if (fRemoteNumberOfChanges>=0) PDEBUGPRINTFX(DBG_HOT,("- NumberOfChanges announced by remote = %ld",(long)fRemoteNumberOfChanges));
+ OBJ_PROGRESS_EVENT(fSessionP->getSyncAppBase(),pev_syncstart,fDSConfigP,0,0,0);
+ }
+ else {
+ // - not yet started
+ PDEBUGPRINTFX(DBG_HOT,(
+ "- Starting sync not yet complete - re-execute <sync> command again in next message"
+ ));
+ aQueueForLater=true;
+ return true; // ok so far
+ }
+ }
+ // must be syncready here (otherwise we return before we reach this)
+ if (!testState(dssta_syncsetready)) {
+ aStatusCommand.setStatusCode(403); // forbidden
+ ADDDEBUGITEM(aStatusCommand,"SYNC received too early");
+ PDEBUGPRINTFX(DBG_ERROR,("TLocalEngineDS::engProcessSyncCmd: SYNC received too early"));
+ engAbortDataStoreSync(403,false,false); // remote problem, not resumable
+ return false;
+ }
+ // state is now syncing
+ /// @deprecated - dssta_syncsetready is enough
+ //fState=dss_syncing;
+ return true;
+} // TLocalEngineDS::engProcessSyncCmd
+
+
+// SYNC command bracket end (but another might follow in next message)
+bool TLocalEngineDS::engProcessSyncCmdEnd(bool &aQueueForLater)
+{
+ bool ok=true;
+ // queue it for later as long as datastore is not ready now
+ if (!engIsStarted(false)) { // not waiting if not started
+ // no state change, just postpone execution
+ aQueueForLater=true;
+ }
+ // also inform remote
+ if (fRemoteDatastoreP) ok=fRemoteDatastoreP->remoteProcessSyncCmdEnd();
+ return ok;
+} // TLocalEngineDS::engProcessSyncCmdEnd
+
+
+#ifndef SYSYNC_CLIENT
+
+// server case: called whenever outgoing Message of Sync Package starts
+void TLocalEngineDS::engServerStartOfSyncMessage(void)
+{
+ // this is where we might start our own <Sync> command (all
+ // received commands are now processed)
+ // - Note that this might be a subdatastore -> if so, do NOT
+ // start a sync (superdatastore will handle this)
+ // - Note that this will be called even if current message is
+ // already full, so it could well be that this sync command
+ // is queued.
+ if (!fSessionP->fCompleteFromClientOnly && testState(dssta_serverseenclientmods) && getSyncMode()==smo_fromclient) {
+ // from-client only does not send back a <sync> command, simply end data access here
+ PDEBUGPRINTFX(DBG_PROTO,("from-client-only:do not send <sync> command back to client, data access ends here"));
+ changeState(dssta_syncgendone,true);
+ changeState(dssta_dataaccessdone,true);
+ }
+ // ### SyncFest #5, found with Tactel Jazz Client:
+ // - do not send anything when remote datastore is not known
+ else if (fRemoteDatastoreP) {
+ if (!testState(dssta_serversyncgenstarted) && testState(dssta_serverseenclientmods)) {
+ changeState(dssta_serversyncgenstarted,true);
+ if (!isSubDatastore()) {
+ // - Note: if sync command was already started, the
+ // finished(), continueIssue() mechanism will make sure that
+ // more commands are generated
+ // - Note2: if all sync commands can be sent at once,
+ // fState will be modified by issuing <sync>, so
+ // it must be ok for issuing syncops here already!
+ TSyncCommand *synccmdP =
+ new TSyncCommand(
+ fSessionP,
+ this, // local datastore
+ fRemoteDatastoreP // remote datastore
+ );
+ // issue
+ ISSUE_COMMAND_ROOT(fSessionP,synccmdP);
+ }
+ }
+ }
+ else {
+ changeState(dssta_idle,true); // force it
+ }
+} // TLocalEngineDS::engServerStartOfSyncMessage
+
+
+#endif // server only
+
+
+// called whenever Message of Sync Package ends or after last queued Sync command is executed
+// - aEndOfAllSyncCommands is true when at end of Sync-data-from-remote packet
+// AND all eventually queued sync/syncop commands have been processed.
+void TLocalEngineDS::engEndOfSyncFromRemote(bool aEndOfAllSyncCommands)
+{
+ // is called for all local datastores, including superdatastore, even inactive ones, so check state first
+ if (testState(dssta_syncsetready)) {
+ if (aEndOfAllSyncCommands) {
+ // we are at end of sync-data-from-remote for THIS datastore
+ #ifdef SYSYNC_CLIENT
+ // - we are done with <Sync> from server, that is, data access is done now
+ changeState(dssta_dataaccessdone,true); // force it
+ #else
+ // - server has seen all client modifications now
+ // Note: in case of the simulated-empty-sync-hack in action, we
+ // allow that we are already in server_sync_gen_started and
+ // won't try to force down to dssta_serverseenclientmods
+ if (!fSessionP->fFakeFinalFlag || getDSState()<dssta_serverseenclientmods) {
+ // under normal circumstances, wee need dssta_serverseenclientmods here
+ changeState(dssta_serverseenclientmods,true); // force it
+ }
+ else {
+ // hack exception
+ PDEBUGPRINTFX(DBG_ERROR,("Warning: simulated </final> active - allowing state>server_seen_client_mods"));
+ }
+ #endif
+ }
+ #ifndef SYSYNC_CLIENT
+ engServerStartOfSyncMessage();
+ #endif
+ // now do final things
+ if (testState(dssta_dataaccessdone,true)) {
+ // @todo: I think, as long as we need to send maps, we're not done yet!!!
+ // sync packets in both directions done, forget remote datastore
+ fRemoteDatastoreP=NULL;
+ }
+ } // dssta_syncsetready
+} // TLocalEngineDS::engEndOfSyncFromRemote
+
+
+// - must return true if this datastore is finished with <sync>
+// (if all datastores return true,
+// session is allowed to finish sync packet with outgoing message
+bool TLocalEngineDS::isSyncDone(void)
+{
+ // is called for all local datastores, even inactive ones, which must signal sync done, too
+ // - only datastores currently receiving or sending <sync> commands are not done with sync
+ // - aborted datastores are also done with sync, no matter what status they have
+ return (
+ fAbortStatusCode!=0 ||
+ //(fState!=dss_syncsend && fState!=dss_syncing && fState!=dss_syncready && fState!=dss_syncfinish)
+ !testState(dssta_clientsentalert) || // nothing really happened yet...
+ testState(dssta_syncgendone) // ...or already completed generating <sync>
+ );
+} // TLocalEngineDS::isSyncDone
+
+
+// test datastore state for minimal or exact state
+bool TLocalEngineDS::testState(TLocalEngineDSState aMinState, bool aNeedExactMatch)
+{
+ bool result =
+ (!aNeedExactMatch || (fLocalDSState==aMinState)) &&
+ (fLocalDSState>=aMinState);
+ DEBUGPRINTFX(DBG_EXOTIC,(
+ "%s: testState=%s - expected state%c='%s', found state=='%s'",
+ getName(),
+ result ? "TRUE" : "FALSE",
+ aNeedExactMatch ? '=' : '>',
+ getDSStateName(aMinState),
+ getDSStateName()
+ ));
+ return result;
+} // TLocalEngineDS::testState
+
+
+// change datastore state, calls logic layer before and after change
+localstatus TLocalEngineDS::changeState(TLocalEngineDSState aNewState, bool aForceOnError)
+{
+ localstatus err1,err2;
+ TLocalEngineDSState oldState = fLocalDSState;
+
+ // nop if no change in state
+ if (aNewState==oldState) return LOCERR_OK;
+ // state cannot be decremented except down to adminready and below
+ if ((aNewState<oldState) && (aNewState>dssta_adminready)) {
+ PDEBUGPRINTFX(DBG_ERROR,(
+ "%s: Internal error: attempt to reduce state from '%s' to '%s' - aborting",
+ getName(),
+ getDSStateName(oldState),
+ getDSStateName(aNewState)
+ ));
+ err1 = 500;
+ dsAbortDatastoreSync(err1,true);
+ return err1;
+ }
+ // give logic opportunity to react before state changes
+ PDEBUGBLOCKFMT((
+ "DSStateChange",
+ "Datastore changes state",
+ "datastore=%s|oldstate=%s|newstate=%s",
+ getName(),
+ getDSStateName(oldState),
+ getDSStateName(aNewState)
+ ));
+ err2 = LOCERR_OK;
+ err1 = dsBeforeStateChange(oldState,aNewState);
+ if (!aForceOnError && err1) goto endchange;
+ // switch state
+ fLocalDSState = aNewState;
+ // now give logic opportunity to react again
+ err2 = dsAfterStateChange(oldState,aNewState);
+endchange:
+ PDEBUGENDBLOCK("DSStateChange");
+ // return most recent error
+ return err2 ? err2 : err1;
+} // TLocalEngineDS::changeState
+
+
+
+// test abort status, datastore is aborted also when session is just suspended
+bool TLocalEngineDS::isAborted(void)
+{
+ return fAbortStatusCode!=0 || fSessionP->isSuspending();
+} // TLocalEngineDS::isAborted
+
+
+// abort sync with this datastore
+void TLocalEngineDS::engAbortDataStoreSync(TSyError aStatusCode, bool aLocalProblem, bool aResumable)
+{
+ if (fLocalDSState!=dssta_idle && !fAbortStatusCode) {
+ // prepare status
+ fAbortStatusCode = aStatusCode ? aStatusCode : 514; // make sure we have a non-zero fAbortStatusCode
+ fLocalAbortCause = aLocalProblem;
+ if (!aResumable) preventResuming(); // prevent resuming
+ PDEBUGBLOCKFMT((
+ "DSAbort","Aborting datastore sync","abortStatusCode=%hd|localProblem=%s|resumable=%s",
+ aStatusCode,
+ aLocalProblem ? "yes" : "no",
+ aResumable ? "yes" : "no"
+ ));
+ // tell that to the session
+ fSessionP->DatastoreFailed(aStatusCode,aLocalProblem);
+ // as soon as sync set is ready, we have potentially started the sync and resume makes sense
+ // NOTE: before we have made the sync set ready, WE MUST NOT resume, because making the sync
+ // set ready includes zapping it on slow refreshes, and this is only done when not resuming
+ // (so saving a suspend state before dssta_syncsetready would cause that the zapping is
+ // eventually skipped)
+ if (!testState(dssta_syncsetready)) preventResuming(); // prevent resuming before sync set is ready
+ // save resume (or non-resumable!) status only if this is NOT A TIMEOUT, because if it is a
+ // (server) timeout, suspend state was saved at end of last request, and writing again here would destroy
+ // the state.
+ if (aStatusCode!=408) {
+ engSaveSuspendState(true); // save even if already aborted
+ }
+ // let derivates know
+ dsAbortDatastoreSync(aStatusCode, aLocalProblem);
+ // show abort
+ PDEBUGPRINTFX(DBG_ERROR,(
+ "*************** Warning: Datastore flagged aborted (after %ld sec. request processing, %ld sec. total) with %s Status %hd",
+ (long)((getSession()->getSystemNowAs(TCTX_UTC)-fSessionP->getLastRequestStarted()) / secondToLinearTimeFactor),
+ (long)((getSession()->getSystemNowAs(TCTX_UTC)-fSessionP->getSessionStarted()) / secondToLinearTimeFactor),
+ aLocalProblem ? "LOCAL" : "REMOTE",
+ aStatusCode
+ ));
+ OBJ_PROGRESS_EVENT(
+ fSessionP->getSyncAppBase(),
+ pev_syncend,
+ fDSConfigP,
+ getAbortStatusCode(), //%%%aLocalProblem ? localError(aStatusCode) : syncmlError(aStatusCode),
+ fSlowSync ? (fFirstTimeSync ? 2 : 1) : 0,
+ fResuming ? 1 : 0
+ );
+ PDEBUGENDBLOCK("DSAbort");
+ }
+} // TLocalEngineDS::engAbortDataStoreSync
+
+
+// check if aborted, set status to abort reason code if yes
+bool TLocalEngineDS::CheckAborted(TStatusCommand &aStatusCommand)
+{
+ if (fAbortStatusCode!=0) {
+ aStatusCommand.setStatusCode(
+ fSessionP->getSyncMLVersion()>=syncml_vers_1_1 ? 514 : // cancelled
+ (fAbortStatusCode<LOCAL_STATUS_CODE ? fAbortStatusCode : 512) // sync failed
+ );
+ PDEBUGPRINTFX(DBG_DATA,("This datastore is in aborted state, rejects all commands with %hd",aStatusCommand.getStatusCode()));
+ return true; // aborted, status set
+ }
+ return false; // not aborted
+} // TLocalEngineDS::CheckAborted
+
+
+
+// Do common logfile substitutions
+void TLocalEngineDS::DoLogSubstitutions(string &aLog,bool aPlaintext)
+{
+ #ifndef MINIMAL_CODE
+ string s;
+
+ if (aPlaintext) {
+ StringObjTimestamp(s,fEndOfSyncTime);
+ // %T Time of sync (in derived datastores, this is the point of reference for newer/older comparisons) as plain text
+ StringSubst(aLog,"%T",s,2);
+ // %seT Time of session end (with this datastore) as plain text
+ StringSubst(aLog,"%seT",s,4);
+ // %ssT Time of session start as plain text
+ StringObjTimestamp(s,fSessionP->getSessionStarted());
+ StringSubst(aLog,"%ssT",s,4);
+ }
+ // %sdT sync duration (in seconds) for this datastore (start of session until datastore finished)
+ StringSubst(aLog,"%sdT",((sInt32)(fEndOfSyncTime-fSessionP->getSessionStarted())/secondToLinearTimeFactor),4);
+ // %nD Datastore name
+ StringSubst(aLog,"%nD",getName(),3);
+ // %rD Datastore remote path
+ StringSubst(aLog,"%rD",fRemoteDBPath,3);
+ // %lD Datastore local path (complete with all CGI)
+ StringSubst(aLog,"%lD",fRemoteViewOfLocalURI,3);
+ // %iR Remote Device ID (URI)
+ StringSubst(aLog,"%iR",fSessionP->getRemoteURI(),3);
+ // %nR Remote name: [Manufacturer ]Model")
+ StringSubst(aLog,"%nR",fSessionP->getRemoteDescName(),3);
+ // %vR Remote Device Version Info ("Type (HWV, FWV, SWV) Oem")
+ StringSubst(aLog,"%vR",fSessionP->getRemoteInfoString(),3);
+ // %U User Name
+ StringSubst(aLog,"%U",fSessionP->getSyncUserName(),2);
+ // %iS local Session ID
+ StringSubst(aLog,"%iS",fSessionP->getLocalSessionID(),3);
+ // %sS Status code (0 if successful)
+ StringSubst(aLog,"%sS",fAbortStatusCode,3);
+ // %ssS Session Status code (0 if successful)
+ StringSubst(aLog,"%ssS",fSessionP->getAbortReasonStatus(),4);
+ // %syV SyncML version (as text) of session
+ StringSubst(aLog,"%syV",SyncMLVerDTDNames[fSessionP->getSyncMLVersion()],4);
+ // %syV SyncML version numeric (0=unknown, 1=1.0, 2=1.1, 3=1.2) of session
+ StringSubst(aLog,"%syVn",(long)fSessionP->getSyncMLVersion(),5);
+ // %mS Syncmode (0=twoway, 1=fromclient 2=fromserver)
+ StringSubst(aLog,"%mS",(sInt32)fSyncMode,3);
+ // %tS Synctype (0=normal,1=slow,2=firsttime slow, +10 if resumed session)
+ StringSubst(aLog,"%tS",(fSlowSync ? (fFirstTimeSync ? 2 : 1) : 0) + (isResuming() ? 10 : 0),3);
+ // %laI locally added Items
+ StringSubst(aLog,"%laI",fLocalItemsAdded,4);
+ // %raI remotely added Items
+ StringSubst(aLog,"%raI",fRemoteItemsAdded,4);
+ // %ldI locally deleted Items
+ StringSubst(aLog,"%ldI",fLocalItemsDeleted,4);
+ // %rdI remotely deleted Items
+ StringSubst(aLog,"%rdI",fRemoteItemsDeleted,4);
+ // %luI locally updated Items
+ StringSubst(aLog,"%luI",fLocalItemsUpdated,4);
+ // %ruI remotely updated Items
+ StringSubst(aLog,"%ruI",fRemoteItemsUpdated,4);
+ // %reI locally not accepted Items (sent error to remote, remote MAY resend them or abort the session)
+ StringSubst(aLog,"%leI",fLocalItemsError,4);
+ // %leI remotely not accepted Items (got error from remote, local will resend them later)
+ StringSubst(aLog,"%reI",fRemoteItemsError,4);
+ #ifndef SYSYNC_CLIENT
+ // %smI Slowsync matched Items
+ StringSubst(aLog,"%smI",fSlowSyncMatches,4);
+ // %scI Server won Conflicts
+ StringSubst(aLog,"%scI",fConflictsServerWins,4);
+ // %ccI Client won Conflicts
+ StringSubst(aLog,"%ccI",fConflictsClientWins,4);
+ // %dcI Conflicts with duplications
+ StringSubst(aLog,"%dcI",fConflictsDuplicated,4);
+ // %tiB total incoming bytes
+ StringSubst(aLog,"%tiB",fSessionP->getIncomingBytes(),4);
+ // %toB total outgoing bytes
+ StringSubst(aLog,"%toB",fSessionP->getOutgoingBytes(),4);
+ #endif
+ // %niB net incoming data bytes for this datastore
+ StringSubst(aLog,"%diB",fIncomingDataBytes,4);
+ // %noB net incoming data bytes for this datastore
+ StringSubst(aLog,"%doB",fOutgoingDataBytes,4);
+ #endif
+} // TLocalEngineDS::DoLogSubstitutions
+
+
+// log datastore sync result
+// - Called at end of sync with this datastore
+void TLocalEngineDS::dsLogSyncResult(void)
+{
+ #ifndef MINIMAL_CODE
+ if (fSessionP->logEnabled()) {
+ string logtext;
+ logtext=fSessionP->getSessionConfig()->fLogFileFormat;
+ if (!logtext.empty()) {
+ // substitute
+ DoLogSubstitutions(logtext,true); // plaintext
+ // show
+ fSessionP->WriteLogLine(logtext.c_str());
+ }
+ }
+ #endif
+} // TLocalEngineDS::dsLogSyncResult
+
+
+
+// Terminate all activity with this datastore
+// Note: may be called repeatedly, must only execute relevant shutdown code once
+void TLocalEngineDS::engTerminateDatastore(localstatus aAbortStatusCode)
+{
+ // now abort (if not already aborted), then finish activities
+ engFinishDataStoreSync(aAbortStatusCode);
+ // and finally reset completely
+ engResetDataStore();
+} // TLocalEngineDS::TerminateDatastore
+
+
+// called at very end of sync session, when everything is done
+// Note: is also called before deleting a datastore (so aborted sessions
+// can do cleanup and/or statistics display as well)
+void TLocalEngineDS::engFinishDataStoreSync(localstatus aErrorStatus)
+{
+ // set end of sync time
+ fEndOfSyncTime = getSession()->getSystemNowAs(TCTX_UTC);
+ // check if we have something to do at all
+ if (fLocalDSState!=dssta_idle && fLocalDSState!=dssta_completed) {
+ if (aErrorStatus==LOCERR_OK) {
+ // Check if we need to abort now due to failed items only
+ if (fRemoteItemsError>0) {
+ // remote reported errors
+ if (fSlowSync && fRemoteItemsAdded==0 && fRemoteItemsDeleted==0 && fRemoteItemsUpdated==0 && fSessionP->getSessionConfig()->fAbortOnAllItemsFailed) {
+ PDEBUGPRINTFX(DBG_ERROR+DBG_DETAILS,("All remote item operations failed -> abort sync"));
+ engAbortDataStoreSync(512,false,false); // remote problems (only failed items in a slow sync) caused sync to fail, not resumable
+ }
+ else
+ fSessionP->DatastoreHadErrors(); // at least SOME items were successful, so it's not a completely unsuccessful sync
+ }
+ }
+ // abort, if requested from caller or only-failed-items
+ if (aErrorStatus!=LOCERR_OK)
+ engAbortDataStoreSync(aErrorStatus,true); // if we have an error here, this is considered a local problem
+ else {
+ OBJ_PROGRESS_EVENT(
+ fSessionP->getSyncAppBase(),
+ pev_syncend,
+ fDSConfigP,
+ fAbortStatusCode,
+ fSlowSync ? (fFirstTimeSync ? 2 : 1) : 0,
+ fResuming ? 1 : 0
+ );
+ }
+ #ifdef SUPERDATASTORES
+ // if this is part of a superdatastore, include its statistics into mine, as
+ // superdatastore can not save any statistics.
+ // This ensures that the result sum over all subdatastores is correct,
+ // however the assignment of error and byte counts is not (all non-related
+ // counts go to first subdatastores with the following code)
+ if (fAsSubDatastoreOf) {
+ fOutgoingDataBytes += fAsSubDatastoreOf->fOutgoingDataBytes;
+ fIncomingDataBytes += fAsSubDatastoreOf->fIncomingDataBytes;
+ fRemoteItemsError += fAsSubDatastoreOf->fRemoteItemsError;
+ fLocalItemsError += fAsSubDatastoreOf->fLocalItemsError;
+ // consumed now, clear in superdatastore
+ fAsSubDatastoreOf->fOutgoingDataBytes=0;
+ fAsSubDatastoreOf->fIncomingDataBytes=0;
+ fAsSubDatastoreOf->fRemoteItemsError=0;
+ fAsSubDatastoreOf->fLocalItemsError=0;
+ }
+ #endif
+ // make log entry
+ dsLogSyncResult();
+ // update my session state vars for successful sessions
+ if (aErrorStatus==LOCERR_OK) {
+ // update anchor
+ fLastRemoteAnchor=fNextRemoteAnchor;
+ fLastLocalAnchor=fNextLocalAnchor; // note: when using TStdLogicDS, this is not saved, but re-generated at next sync from timestamp
+ // no resume
+ fResumeAlertCode=0;
+ // no resume item (just to make sure we don't get strange effects later)
+ fLastItemStatus = 0;
+ fLastSourceURI.erase();
+ fLastTargetURI.erase();
+ fPartialItemState = pi_state_none;
+ fPIStoredSize = 0;
+ }
+ // now shift state to complete, let logic and implementation save the state
+ changeState(dssta_completed,true);
+ #ifdef SCRIPT_SUPPORT
+ // - call DB finish script
+ TScriptContext::execute(
+ fDataStoreScriptContextP,
+ fDSConfigP->fDBFinishScript,
+ &DBFuncTable, // context's function table
+ this // datastore pointer needed for context
+ );
+ #endif
+ }
+ // in any case: idle now again (note: could be shift from dssta_completed to dssta_idle)
+ changeState(dssta_idle,true);
+} // TLocalEngineDS::engFinishDataStoreSync
+
+
+/// inform everyone of coming state change
+localstatus TLocalEngineDS::dsBeforeStateChange(TLocalEngineDSState aOldState,TLocalEngineDSState aNewState)
+{
+ localstatus sta = LOCERR_OK;
+ return sta;
+} // TLocalEngineDS::dsBeforeStateChange
+
+
+/// inform everyone of happened state change
+localstatus TLocalEngineDS::dsAfterStateChange(TLocalEngineDSState aOldState,TLocalEngineDSState aNewState)
+{
+ localstatus sta = LOCERR_OK;
+ if (aOldState>dssta_idle && aNewState==dssta_completed) {
+ // we are going from a non-idle state to completed
+ // - show statistics
+ showStatistics();
+ }
+ return sta;
+} // TLocalEngineDS::dsAfterStateChange
+
+
+// show statistics or error of current sync
+void TLocalEngineDS::showStatistics(void)
+{
+ // Console
+ CONSOLEPRINTF((""));
+ CONSOLEPRINTF(("- Sync Statistics for '%s' (%s), %s sync",
+ getName(),
+ fRemoteViewOfLocalURI.c_str(),
+ fSlowSync ? "slow" : "normal"
+ ));
+ // now show results
+ if (isAborted()) {
+ // failed
+ CONSOLEPRINTF((" ************ Failed with status code=%hd",fAbortStatusCode));
+ }
+ else {
+ // successful: show statistics on console
+ CONSOLEPRINTF((" =================================================="));
+ #ifndef SYSYNC_CLIENT
+ CONSOLEPRINTF((" on Server on Client"));
+ #else
+ CONSOLEPRINTF((" on Client on Server"));
+ #endif
+ CONSOLEPRINTF((" Added: %9ld %9ld",fLocalItemsAdded,fRemoteItemsAdded));
+ CONSOLEPRINTF((" Deleted: %9ld %9ld",fLocalItemsDeleted,fRemoteItemsDeleted));
+ CONSOLEPRINTF((" Updated: %9ld %9ld",fLocalItemsUpdated,fRemoteItemsUpdated));
+ CONSOLEPRINTF((" Rejected with error: %9ld %9ld",fLocalItemsError,fRemoteItemsError));
+ #ifndef SYSYNC_CLIENT
+ CONSOLEPRINTF((" SlowSync Matches: %9ld",fSlowSyncMatches));
+ CONSOLEPRINTF((" Server won Conflicts: %9ld",fConflictsServerWins));
+ CONSOLEPRINTF((" Client won Conflicts: %9ld",fConflictsClientWins));
+ CONSOLEPRINTF((" Conflicts with Duplication: %9ld",fConflictsDuplicated));
+ #endif
+ }
+ CONSOLEPRINTF((""));
+ // Always provide statistics as events
+ OBJ_PROGRESS_EVENT(fSessionP->getSyncAppBase(),pev_dsstats_l,fDSConfigP,fLocalItemsAdded,fLocalItemsUpdated,fLocalItemsDeleted);
+ OBJ_PROGRESS_EVENT(fSessionP->getSyncAppBase(),pev_dsstats_r,fDSConfigP,fRemoteItemsAdded,fRemoteItemsUpdated,fRemoteItemsDeleted);
+ OBJ_PROGRESS_EVENT(fSessionP->getSyncAppBase(),pev_dsstats_e,fDSConfigP,fLocalItemsError,fRemoteItemsError,0);
+ #ifndef SYSYNC_CLIENT
+ OBJ_PROGRESS_EVENT(fSessionP->getSyncAppBase(),pev_dsstats_s,fDSConfigP,fSlowSyncMatches,0,0);
+ OBJ_PROGRESS_EVENT(fSessionP->getSyncAppBase(),pev_dsstats_c,fDSConfigP,fConflictsServerWins,fConflictsClientWins,fConflictsDuplicated);
+ #endif
+ // NOTE: pev_dsstats_d should remain the last log data event sent (as it terminates collecting data in some GUIs)
+ OBJ_PROGRESS_EVENT(fSessionP->getSyncAppBase(),pev_dsstats_d,fDSConfigP,fOutgoingDataBytes,fIncomingDataBytes,fRemoteItemsError);
+ // Always show statistics in debug log
+ #ifdef SYDEBUG
+ PDEBUGPRINTFX(DBG_HOT,("Sync Statistics for '%s' (%s), %s sync",
+ getName(),
+ fRemoteViewOfLocalURI.c_str(),
+ fSlowSync ? "slow" : "normal"
+ ));
+ if (PDEBUGTEST(DBG_HOT)) {
+ string stats =
+ "==================================================\n";
+ #ifndef SYSYNC_CLIENT
+ stats += " on Server on Client\n";
+ #else
+ stats += " on Client on Server\n";
+ #endif
+ StringObjAppendPrintf(stats,"Added: %9ld %9ld\n",(long)fLocalItemsAdded,(long)fRemoteItemsAdded);
+ StringObjAppendPrintf(stats,"Deleted: %9ld %9ld\n",(long)fLocalItemsDeleted,(long)fRemoteItemsDeleted);
+ StringObjAppendPrintf(stats,"Updated: %9ld %9ld\n",(long)fLocalItemsUpdated,(long)fRemoteItemsUpdated);
+ StringObjAppendPrintf(stats,"Rejected with error: %9ld %9ld\n\n",(long)fLocalItemsError,(long)fRemoteItemsError);
+ #ifndef SYSYNC_CLIENT
+ StringObjAppendPrintf(stats,"SlowSync Matches: %9ld\n",fSlowSyncMatches);
+ StringObjAppendPrintf(stats,"Server won Conflicts: %9ld\n",fConflictsServerWins);
+ StringObjAppendPrintf(stats,"Client won Conflicts: %9ld\n",fConflictsClientWins);
+ StringObjAppendPrintf(stats,"Conflicts with Duplication: %9ld\n\n",fConflictsDuplicated);
+ #endif
+ StringObjAppendPrintf(stats,"Content Data Bytes sent: %9ld\n",(long)fOutgoingDataBytes);
+ StringObjAppendPrintf(stats,"Content Data Bytes received: %9ld\n\n",(long)fIncomingDataBytes);
+ StringObjAppendPrintf(stats,"Duration of sync [seconds]: %9ld\n",(long)((fEndOfSyncTime-fSessionP->getSessionStarted())/secondToLinearTimeFactor));
+ PDEBUGPUTSXX(DBG_HOT,stats.c_str(),0,true);
+ }
+ if (isAborted()) {
+ // failed
+ PDEBUGPRINTFX(DBG_ERROR,("Warning: Failed with status code=%hd, statistics are incomplete!!",fAbortStatusCode));
+ }
+ #endif
+} // TLocalEngineDS::showStatistics
+
+
+
+// create a new syncop command for sending to remote
+TSyncOpCommand *TLocalEngineDS::newSyncOpCommand(
+ TSyncItem *aSyncItemP, // the sync item
+ TSyncItemType *aSyncItemTypeP // the sync item type
+)
+{
+ // get operation
+ TSyncOperation syncop=aSyncItemP->getSyncOp();
+ // obtain meta
+ SmlPcdataPtr_t metaP = newMetaType(aSyncItemTypeP->getTypeName());
+ // create command
+ TSyncOpCommand *syncopcmdP = new TSyncOpCommand(fSessionP,this,syncop,metaP);
+ // make sure item does not have stuff it is not allowed to have
+ // %%% SCTS does not like SourceURI in Replace and Delete commands sent to Client
+ // there are the only ones allowed to carry a GUID
+ #ifndef SYSYNC_CLIENT
+ // Server: commands only have remote IDs, except add which only has target ID
+ if (syncop==sop_add || syncop==sop_wants_add)
+ aSyncItemP->clearRemoteID(); // no remote ID
+ else {
+ if (!fDSConfigP->fAlwaysSendLocalID) {
+ // only if localID may not be included in all syncops
+ aSyncItemP->clearLocalID(); // no local ID
+ }
+ }
+ #else
+ // Client: all commands only have local IDs
+ aSyncItemP->clearRemoteID(); // no remote ID
+ #endif
+ #ifdef SYSYNC_TARGET_OPTIONS
+ // init item generation variables
+ fItemSizeLimit=fSizeLimit;
+ #else
+ fItemSizeLimit=-1; // no limit
+ #endif
+ // now add item
+ SmlItemPtr_t itemP = aSyncItemTypeP->newSmlItem(aSyncItemP,this);
+ // check if data size is ok
+ if (itemP && fSessionP->fMaxOutgoingObjSize) {
+ if (itemP->data && itemP->data->content && itemP->data->length) {
+ // there is data, check if size is ok
+ if (itemP->data->length > fSessionP->fMaxOutgoingObjSize) {
+ // too large, suppress it
+ PDEBUGPRINTFX(DBG_ERROR,(
+ "WARNING: outgoing item is larger (%ld) than MaxObjSize (%ld) of remote -> suppress now/mark for resend",
+ (long)itemP->data->length,
+ (long)fSessionP->fMaxOutgoingObjSize
+ ));
+ smlFreeItemPtr(itemP);
+ itemP=NULL;
+ // mark item for resend
+ // For datastores without resume support, this will just have no effect at all
+ engMarkItemForResend(aSyncItemP->getLocalID(),aSyncItemP->getRemoteID());
+ }
+ }
+ }
+ if (itemP) {
+ // add it to the command
+ syncopcmdP->addItem(itemP);
+ }
+ else {
+ // no item - command is invalid, delete it
+ delete syncopcmdP;
+ syncopcmdP=NULL;
+ }
+ // return command
+ return syncopcmdP;
+} // TLocalEngineDS::newSyncOpCommand
+
+
+// create SyncItem suitable for being sent from local to remote
+TSyncItem *TLocalEngineDS::newItemForRemote(
+ uInt16 aExpectedTypeID // typeid of expected type
+)
+{
+ // safety
+ if (!canCreateItemForRemote())
+ SYSYNC_THROW(TSyncException("newItemForRemote called without sufficient type information ready"));
+ // create
+ TSyncItem *itemP = fLocalSendToRemoteTypeP->newSyncItem(fRemoteReceiveFromLocalTypeP,this);
+ if (!itemP)
+ SYSYNC_THROW(TSyncException("newItemForRemote could not create item"));
+ // check type
+ if (!itemP->isBasedOn(aExpectedTypeID)) {
+ PDEBUGPRINTFX(DBG_ERROR,(
+ "newItemForRemote created item of typeID %hd, caller expects %hd",
+ itemP->getTypeID(),
+ aExpectedTypeID
+ ));
+ SYSYNC_THROW(TSyncException("newItemForRemote created wrong item type"));
+ }
+ return itemP;
+} // TLocalEngineDS::newItemForRemote
+
+
+// return pure relative (item) URI (removes absolute part or ./ prefix)
+const char *TLocalEngineDS::DatastoreRelativeURI(const char *aURI)
+{
+ return relativeURI(relativeURI(aURI,fSessionP->getLocalURI()),getName());
+} // TLocalEngineDS::DatastoreRelativeURI
+
+
+
+// - init filtering and check if needed (sets fTypeFilteringNeeded, fFilteringNeeded and fFilteringNeededForAll)
+void TLocalEngineDS::initPostFetchFiltering(void)
+{
+ #ifdef OBJECT_FILTERING
+ if (!fLocalSendToRemoteTypeP) {
+ fTypeFilteringNeeded=false;
+ fFilteringNeeded=false;
+ fFilteringNeededForAll=false;
+ }
+ else {
+ // get basic settings from type
+ fLocalSendToRemoteTypeP->initPostFetchFiltering(fTypeFilteringNeeded,fFilteringNeededForAll,this);
+ fFilteringNeeded=fTypeFilteringNeeded;
+ // NOTE: if type filtering is needed, it's the responsibility of initPostFetchFiltering() of
+ // the type to check (using the DBHANDLESOPTS() script func) if DB does already handle
+ // the range filters and such and eventually avoid type filtering then.
+ // then check for standard filter requirements
+ #ifdef SYDEBUG
+ #ifdef SYNCML_TAF_SUPPORT
+ if (!fTargetAddressFilter.empty()) PDEBUGPRINTFX(DBG_DATA,("using (dynamic, temporary) TAF expression from CGI : %s",fTargetAddressFilter.c_str()));
+ if (!fIntTargetAddressFilter.empty()) PDEBUGPRINTFX(DBG_DATA,("using (dynamic, temporary) internally set TAF expression : %s",fIntTargetAddressFilter.c_str()));
+ #endif
+ if (!fSyncSetFilter.empty()) PDEBUGPRINTFX(DBG_DATA,("using (dynamic) sync set filter expression : %s",fSyncSetFilter.c_str()));
+ if (!fLocalDBFilter.empty()) PDEBUGPRINTFX(DBG_DATA,("using (static) local db filter expression : %s",fLocalDBFilter.c_str()));
+ #endif
+ // - if DB does the standard filters, we don't need to check them here again
+ if (!engFilteredFetchesFromDB(true)) {
+ // If DB does NOT do the standard filters, we have to do them here
+ // - this is the case if we have an (old-style) sync set filter, but not filtered by DB
+ // we need to filter all because sync set filter can be dynamic
+ if (!fSyncSetFilter.empty())
+ fFilteringNeededForAll=true;
+ // always return true if there is something to filter at all
+ if (
+ fLocalDBFilter.empty() ||
+ !fDSConfigP->fInvisibleFilter.empty() ||
+ !fSyncSetFilter.empty() ||
+ !fDSConfigP->fRemoteAcceptFilter.empty()
+ )
+ fFilteringNeeded=true;
+ }
+ }
+ PDEBUGPRINTFX(DBG_FILTER+DBG_HOT,(
+ "Datastore-level postfetch filtering %sneeded%s",
+ fFilteringNeeded ? "" : "NOT ",
+ fFilteringNeeded ? (fFilteringNeededForAll ? " and to be applied to all records" : " only for changed records") : ""
+ ));
+ #endif
+} // TLocalEngineDS::initPostFetchFiltering
+
+
+// filter fetched record
+bool TLocalEngineDS::postFetchFiltering(TSyncItem *aSyncItemP)
+{
+ #ifndef OBJECT_FILTERING
+ return true; // no filters, always pass
+ #else
+ if (!aSyncItemP) return false; // null item does not pass
+ // first do standard filters
+ // - if DB has filtered the
+ bool passes=true;
+ if (fFilteringNeeded) {
+ // - first make sure outgoing object has all properties set
+ // such that it would pass the acceptance filter (for example KIND for calendar...)
+ if (!aSyncItemP->makePassFilter(fDSConfigP->fRemoteAcceptFilter.c_str())) {
+ // we could not make item pass acceptance filters
+ PDEBUGPRINTFX(DBG_ERROR,("- item localid='%s' cannot be made passing <acceptfilter> -> ignored",aSyncItemP->getLocalID()));
+ passes=false;
+ }
+ // now check for field-level filters
+ if (passes && !engFilteredFetchesFromDB()) {
+ // DB has not already filtered these, so we need to do it here
+ // - "moving target" first
+ passes=fSyncSetFilter.empty() || aSyncItemP->testFilter(fSyncSetFilter.c_str());
+ if (passes) {
+ // - static filters
+ passes =
+ aSyncItemP->testFilter(fLocalDBFilter.c_str()) && // local filter
+ (
+ fDSConfigP->fInvisibleFilter.empty() || // and either no invisibility defined...
+ !aSyncItemP->testFilter(fDSConfigP->fInvisibleFilter.c_str()) // ...or NOT passed
+ );
+ }
+ }
+ if (passes && fTypeFilteringNeeded) {
+ // finally, apply type's filter
+ passes=aSyncItemP->postFetchFiltering(this);
+ }
+ }
+ else {
+ // no filtering needed, DB has already filtered out those that would not pass
+ // BUT: make sure outgoing items WILL pass the acceptance filter. If this
+ // cannot be done, item will be filtered out.
+ if (!aSyncItemP->makePassFilter(fDSConfigP->fRemoteAcceptFilter.c_str())) {
+ // we could not make item pass acceptance filters
+ PDEBUGPRINTFX(DBG_ERROR,("- item localid='%s' cannot be made passing <acceptfilter> -> ignored",aSyncItemP->getLocalID()));
+ passes=false;
+ }
+ }
+ #ifdef SYDEBUG
+ if (!passes) {
+ PDEBUGPRINTFX(DBG_DATA,("- item localid='%s' does not pass filters -> ignored",aSyncItemP->getLocalID()));
+ }
+ #endif
+ // return result
+ return passes;
+ #endif
+} // TLocalEngineDS::postFetchFiltering
+
+
+#ifdef OBJECT_FILTERING
+
+// - called to check if incoming item passes acception filters
+bool TLocalEngineDS::isAcceptable(TSyncItem *aSyncItemP, TStatusCommand &aStatusCommand)
+{
+ // test acceptance
+ if (aSyncItemP->testFilter(fDSConfigP->fRemoteAcceptFilter.c_str())) return true; // ok
+ // not accepted, set 415 error
+ aStatusCommand.setStatusCode(415);
+ ADDDEBUGITEM(aStatusCommand,"Received item does not pass acceptance filter");
+ PDEBUGPRINTFX(DBG_ERROR,(
+ "Received item does not pass acceptance filter: %s",
+ fDSConfigP->fRemoteAcceptFilter.c_str()
+ ));
+ return false;
+} // TLocalEngineDS::isAcceptable
+
+
+/// @brief called to make incoming item visible
+/// @return true if now visible
+bool TLocalEngineDS::makeVisible(TSyncItem *aSyncItemP)
+{
+ bool invisible=false;
+ if (!fDSConfigP->fInvisibleFilter.empty()) {
+ invisible=aSyncItemP->testFilter(fDSConfigP->fInvisibleFilter.c_str());
+ }
+ if (invisible) {
+ return aSyncItemP->makePassFilter(fDSConfigP->fMakeVisibleFilter.c_str());
+ }
+ return true; // is already visible
+} // TLocalEngineDS::makeVisible
+
+
+/// @brief called to make incoming item INvisible
+/// @return true if now INvisible
+bool TLocalEngineDS::makeInvisible(TSyncItem *aSyncItemP)
+{
+ // return true if could make invisible or already was invisible
+ if (fDSConfigP->fInvisibleFilter.empty())
+ return false; // no invisible filter, cannot make invisible
+ // make pass invisible filter - if successful, we're now invisible
+ return aSyncItemP->makePassFilter(fDSConfigP->fInvisibleFilter.c_str()); // try to make invisible (and return result)
+} // TLocalEngineDS::makeInvisible
+
+
+
+// - called to make incoming item pass sync set filtering
+bool TLocalEngineDS::makePassSyncSetFilter(TSyncItem *aSyncItemP)
+{
+ bool pass=true;
+
+ // make sure we pass sync set filtering and stay visible
+ if (!fSyncSetFilter.empty()) {
+ // try to make pass sync set filter (modifies item only if it would not pass otherwise)
+ pass=aSyncItemP->makePassFilter(fSyncSetFilter.c_str());
+ }
+ if (!pass || fSyncSetFilter.empty()) {
+ // specified sync set filter cannot make item pass, or no sync set filter at all:
+ // - apply makePassFilter default expression
+ if (!fDSConfigP->fMakePassFilter.empty()) {
+ pass=aSyncItemP->makePassFilter(fDSConfigP->fMakePassFilter.c_str());
+ if (pass) {
+ // check again to check if item would pass the syncset filter now
+ pass=aSyncItemP->testFilter(fSyncSetFilter.c_str());
+ }
+ }
+ }
+ return pass;
+} // TLocalEngineDS::makePassSyncSetFilter
+
+#endif
+
+
+// process remote item
+bool TLocalEngineDS::engProcessRemoteItem(
+ TSyncItem *syncitemP,
+ TStatusCommand &aStatusCommand
+)
+{
+ #ifdef SYSYNC_CLIENT
+ return engProcessRemoteItemAsClient(syncitemP,aStatusCommand); // status, must be set to correct status code (ok / error)
+ #else
+ return engProcessRemoteItemAsServer(syncitemP,aStatusCommand); // status, must be set to correct status code (ok / error)
+ #endif
+} // TLocalEngineDS::engProcessRemoteItem
+
+
+// process SyncML SyncOp command for this datastore
+bool TLocalEngineDS::engProcessSyncOpItem(
+ TSyncOperation aSyncOp, // the operation
+ SmlItemPtr_t aItemP, // the item to be processed
+ SmlMetInfMetInfPtr_t aMetaP, // command-wide meta, if any
+ TStatusCommand &aStatusCommand // pre-set 200 status, can be modified in case of errors
+)
+{
+ bool regular = false;
+ // determine SyncItemType that can handle this item data
+ if (fRemoteDatastoreP==NULL) {
+ PDEBUGPRINTFX(DBG_ERROR,("engProcessSyncOpItem: Remote Datastore not known"));
+ aStatusCommand.setStatusCode(500);
+ }
+ // - start with default
+ TSyncItemType *remoteTypeP=getRemoteSendType();
+ TSyncItemType *localTypeP=getLocalReceiveType();
+ // - see if command-wide meta plus item contents specify another type
+ // (item meta, if present, overrides command wide meta)
+ // see if item itself or command meta specify a type name or format
+ SmlMetInfMetInfPtr_t itemmetaP = smlPCDataToMetInfP(aItemP->meta);
+ // - format
+ TFmtTypes fmt=fmt_chr;
+ if (itemmetaP && itemmetaP->format)
+ smlPCDataToFormat(itemmetaP->format,fmt); // use type name from item's meta
+ else if (aMetaP && aMetaP->format)
+ smlPCDataToFormat(aMetaP->format,fmt); // use type name from command-wide meta
+ // - type
+ string versstr;
+ const char *typestr = NULL;
+ if (itemmetaP && itemmetaP->type)
+ typestr = smlPCDataToCharP(itemmetaP->type); // use type name from item's meta
+ else if (aMetaP && aMetaP->type)
+ typestr = smlPCDataToCharP(aMetaP->type); // use type name from command-wide meta
+ // check if there is a type specified
+ if (typestr) {
+ PDEBUGPRINTFX(DBG_DATA,("Explicit type '%s' specified in command or item meta",typestr));
+ if (strcmp(remoteTypeP->getTypeName(),typestr)!=0) {
+ // specified type is NOT default type: search appropriate remote type
+ remoteTypeP=fRemoteDatastoreP->getSendType(typestr,NULL); // no version known so far
+ if (!remoteTypeP) {
+ // specified type is not a remote type listed in remote's devInf.
+ // But as remote is actually using it, we can assume it does support it, so use local type of same name instead
+ PDEBUGPRINTFX(DBG_ERROR,("According to remote devInf, '%s' is not supported, but obviously it is used here so we try to handle it",typestr));
+ // look it up in local datastore's list
+ remoteTypeP=getReceiveType(typestr,NULL);
+ }
+ }
+ if (remoteTypeP) {
+ #ifdef APP_CAN_EXPIRE
+ // get modified date of item
+ lineardate_t moddat=0; // IMPORTANT, must be initialized in case expiryFromData returns nothing!
+ bool ok = remoteTypeP->expiryFromData(aItemP,moddat)<=MAX_EXPIRY_DIFF+5;
+ // ok==true: we are within hard expiry
+ // ok==false: we are out of hard expiry
+ #ifdef SYSER_REGISTRATION
+ if (getSession()->getSyncAppBase()->fRegOK) {
+ // we have a license (permanent or timed) --> hard expiry is irrelevant
+ // (so override ok according to validity of current license)
+ ok=true; // assume ok
+ // check if license is timed, and if so, check if mod date is within timed range
+ // (if not, set ok to false)
+ uInt8 rd = getSession()->getSyncAppBase()->fRegDuration;
+ if (rd) {
+ lineardate_t ending = date2lineardate(rd/12+2000,rd%12+1,1);
+ ok = ending>=moddat; // ok if not modified after end of license period
+ }
+ }
+ #endif
+ // when we have no license (neither permanent nor timed), hard expiry decides as is
+ // (so just use ok as is)
+ if (!ok) {
+ aStatusCommand.setStatusCode(403); // forbidden to hack this expiry stuff!
+ fSessionP->AbortSession(403,true); // local problem
+ return false;
+ }
+ #endif // APP_CAN_EXPIRE
+ // we have a type, which should be able to determine version from data
+ if (remoteTypeP->versionFromData(aItemP,versstr)) {
+ // version found, Make sure version matches as well
+ PDEBUGPRINTFX(DBG_DATA,("Version '%s' obtained from item data",versstr.c_str()));
+ // check if current remotetype already has correct version (and type, but we know this already)
+ if (!remoteTypeP->supportsType(remoteTypeP->getTypeName(),versstr.c_str(),true)) {
+ // no, type/vers do not match, search again
+ remoteTypeP=fRemoteDatastoreP->getSendType(typestr,versstr.c_str());
+ if (!remoteTypeP) {
+ // specified type is not a remote type listed in remote's devInf.
+ // But as remote is actually using it, we can assume it does support it, so use local type of same name instead
+ PDEBUGPRINTFX(DBG_ERROR,("According to remote devInf, '%s' version '%s' is not supported, but obviously it is used here so we try to handle it",typestr,versstr.c_str()));
+ // look it up in local datastore's list
+ remoteTypeP=getReceiveType(typestr,versstr.c_str());
+ }
+ }
+ }
+ else {
+ PDEBUGPRINTFX(DBG_HOT,("Version could not be obtained from item data"));
+ }
+ }
+ if (!remoteTypeP) {
+ // no matching remote type: fail
+ aStatusCommand.setStatusCode(415);
+ ADDDEBUGITEM(aStatusCommand,"Incompatible content type specified in command or item meta");
+ PDEBUGPRINTFX(DBG_ERROR,(
+ "Incompatible content type '%s' version '%s' specified in command or item meta",
+ typestr,
+ versstr.empty() ? "[none]" : versstr.c_str()
+ ));
+ return false; // irregular
+ }
+ else {
+ // we have the remote type, now determine matching local type
+ // - first check if this is compatible with the existing localTypeP (which
+ // was eventually selected by remote rule match
+ if (!localTypeP->supportsType(remoteTypeP->getTypeName(),remoteTypeP->getTypeVers(),false)) {
+ // current default local type does not support specified remote type
+ // - find a matching local type
+ localTypeP=getReceiveType(remoteTypeP);
+ #ifdef SYDEBUG
+ if (localTypeP) {
+ PDEBUGPRINTFX(DBG_DATA+DBG_HOT,(
+ "Explicit type '%s' does not match default type -> switching to local type '%s' for processing item",
+ typestr,
+ localTypeP->getTypeConfig()->getName()
+ ));
+ }
+ #endif
+ }
+
+ }
+ }
+ // now process
+ if (localTypeP && remoteTypeP) {
+ TSyncItem *syncitemP = NULL;
+ // create the item (might have empty data in case of delete)
+ syncitemP=remoteTypeP->newSyncItem(aItemP,aSyncOp,fmt,localTypeP,this,aStatusCommand);
+ if (!syncitemP) {
+ // failed to create item
+ return false; // irregular
+ }
+ // Now start the real processing
+ PDEBUGBLOCKFMT(("Process_Item","processing remote item",
+ "SyncOp=%s|LocalID=%s|RemoteID=%s",
+ SyncOpNames[syncitemP->getSyncOp()],
+ syncitemP->getLocalID(),
+ syncitemP->getRemoteID()
+ ));
+ #ifdef SCRIPT_SUPPORT
+ TErrorFuncContext errctx;
+ errctx.syncop = syncitemP->getSyncOp();
+ #endif
+ SYSYNC_TRY {
+ // this call frees the item
+ regular =
+ engProcessRemoteItem(syncitemP,aStatusCommand);
+ syncitemP = NULL;
+ PDEBUGENDBLOCK("Process_Item");
+ }
+ SYSYNC_CATCH (...)
+ // Hmm, was the item freed? Not sure, so assume that it was freed.
+ PDEBUGENDBLOCK("Process_Item");
+ SYSYNC_RETHROW;
+ SYSYNC_ENDCATCH
+ // Check for datastore level scripts that might change the status code and/or regular status
+ #ifdef SCRIPT_SUPPORT
+ errctx.statuscode = aStatusCommand.getStatusCode();
+ errctx.newstatuscode = errctx.statuscode;
+ errctx.datastoreP = this;
+ // call script
+ regular =
+ TScriptContext::executeTest(
+ regular, // pass through regular status
+ fDataStoreScriptContextP,
+ fDSConfigP->fReceivedItemStatusScript,
+ &ErrorFuncTable,
+ &errctx // caller context
+ );
+ // use eventually modified status code
+ #ifdef SYDEBUG
+ if (aStatusCommand.getStatusCode() != errctx.newstatuscode) {
+ PDEBUGPRINTFX(DBG_ERROR,("Status: Datastore script changed original status=%hd to %hd (original op was %s)",aStatusCommand.getStatusCode(),errctx.newstatuscode,SyncOpNames[errctx.syncop]));
+ }
+ #endif
+ aStatusCommand.setStatusCode(errctx.newstatuscode);
+ #endif
+ if (regular) {
+ // item 100% successfully processed
+ // - set new defaults to same type as current item
+ setReceiveTypeInfo(localTypeP,remoteTypeP);
+ }
+ }
+ else {
+ // missing remote or local type: fail
+ aStatusCommand.setStatusCode(415);
+ ADDDEBUGITEM(aStatusCommand,"Unknown content type");
+ PDEBUGPRINTFX(DBG_ERROR,(
+ "Missing remote or local SyncItemType"
+ ));
+ regular=false; // irregular
+ }
+ return regular;
+} // TLocalEngineDS::engProcessSyncOpItem
+
+
+#ifndef SYSYNC_CLIENT
+
+
+// Server Case
+// ===========
+
+// helper to force a conflict
+TSyncItem *TLocalEngineDS::forceConflict(TSyncItem *aSyncItemP)
+{
+ TStatusCommand dummy(fSessionP);
+ // - create new item
+ TSyncItem *conflictingItemP =
+ newItemForRemote(aSyncItemP->getTypeID());
+ if (!conflictingItemP) return NULL;
+ // - set IDs
+ conflictingItemP->setLocalID(aSyncItemP->getLocalID());
+ conflictingItemP->setRemoteID(aSyncItemP->getRemoteID());
+ // - this is always a replace conflict (item exists in DB)
+ conflictingItemP->setSyncOp(sop_wants_replace);
+ // - try to get from DB
+ bool ok=logicRetrieveItemByID(*conflictingItemP,dummy);
+ if (ok && dummy.getStatusCode()!=404) {
+ // item found in DB, add it to the sync set so it can be sent to remote
+ // if not cancelled by dontSendItemAsServer()
+ SendItemAsServer(conflictingItemP);
+ PDEBUGPRINTFX(DBG_DATA,("Forced conflict with corresponding item from server DB"));
+ }
+ else {
+ // no item found, we cannot force a conflict
+ delete conflictingItemP;
+ conflictingItemP=NULL;
+ }
+ return conflictingItemP;
+} // TLocalEngineDS::forceConflict
+
+
+
+// process map
+localstatus TLocalEngineDS::engProcessMap(cAppCharP aRemoteID, cAppCharP aLocalID)
+{
+ if (!testState(dssta_syncmodestable)) {
+ // Map received when not appropriate
+ PDEBUGPRINTFX(DBG_ERROR,("Map not allowed in this stage of sync"));
+ return 403;
+ }
+ // pre-process localID
+ string realLocalID;
+ if (aLocalID && *aLocalID) {
+ // Note: Map must be ready to have either empty local or remote ID to delete an entry
+ // perform reverse lookup of received GUID to real GUID
+ realLocalID = aLocalID;
+ obtainRealLocalID(realLocalID);
+ aLocalID=realLocalID.c_str();
+ }
+ else {
+ aLocalID=NULL;
+ }
+ // pre-process remoteID
+ if (!aRemoteID || *aRemoteID==0)
+ aRemoteID=NULL;
+ // let implementation process the map command
+ return logicProcessMap(aRemoteID, aLocalID);
+} // TLocalEngineDS::engProcessMap
+
+
+
+// process sync operation from client with specified sync item
+// (according to current sync mode of local datastore)
+// - returns true (and unmodified or non-200-successful status) if
+// operation could be processed regularily
+// - returns false (but probably still successful status) if
+// operation was processed with internal irregularities, such as
+// trying to delete non-existant item in datastore with
+// incomplete Rollbacks (which returns status 200 in this case!).
+bool TLocalEngineDS::engProcessRemoteItemAsServer(
+ TSyncItem *aSyncItemP,
+ TStatusCommand &aStatusCommand // status, must be set to correct status code (ok / error)
+)
+{
+ TSyncItem *conflictingItemP=NULL;
+ TSyncItem *echoItemP=NULL;
+ TSyncItem *delitemP=NULL;
+ bool changedincoming=false;
+ bool changedexisting=false;
+ bool remainsvisible=true; // usually, we want the item to remain visible in the sync set
+ TStatusCommand dummy(fSessionP);
+
+ // get some info out of item (we might need it after item is already consumed)
+ TSyncOperation syncop=aSyncItemP->getSyncOp();
+ uInt16 itemtypeid=aSyncItemP->getTypeID();
+ string remoteid=aSyncItemP->getRemoteID();
+ // check if datastore is aborted
+ if(CheckAborted(aStatusCommand))
+ return false;
+ // send event (but no abort checking)
+ OBJ_PROGRESS_EVENT(fSessionP->getSyncAppBase(),pev_itemreceived,fDSConfigP,++fItemsReceived,fRemoteNumberOfChanges,0);
+ fPreventAdd = false;
+ fIgnoreUpdate = false;
+ // show
+ PDEBUGPRINTFX(DBG_DATA,("%s item operation received",SyncOpNames[syncop]));
+ // check if receiving commands is allowed at all
+ if (fSyncMode==smo_fromserver) {
+ // Modifications from client not allowed during update from server only
+ aStatusCommand.setStatusCode(403);
+ ADDDEBUGITEM(aStatusCommand,"Client command not allowed in one-way/refresh from server");
+ PDEBUGPRINTFX(DBG_ERROR,("Client command not allowed in one-way/refresh from server"));
+ delete aSyncItemP;
+ return false;
+ }
+ // let item check itself to catch special cases
+ // - init variables which are used/modified by item checking
+ #ifdef SYSYNC_TARGET_OPTIONS
+ // init item generation variables
+ fItemSizeLimit=fSizeLimit;
+ #else
+ fItemSizeLimit=-1; // no limit
+ #endif
+ fCurrentSyncOp = syncop;
+ fEchoItemOp = sop_none;
+ fItemConflictStrategy=fSessionConflictStrategy; // get default strategy for this item
+ fForceConflict = false;
+ fDeleteWins = fDSConfigP->fDeleteWins; // default to configured value
+ fRejectStatus = -1; // no rejection
+ // - now check
+ // check reads and eventually modifies:
+ // - fEchoItemOp : if not sop_none, the incoming item is echoed back to the remote with the specified syncop
+ // - fItemConflictStrategy : might be changed from the pre-set datastore default
+ // - fForceConflict : if set, a conflict is forced by adding the corresponding local item (if any) to the list of items to be sent
+ // - fDeleteWins : if set, in a replace/delete conflict delete will win (regardless of strategy)
+ // - fPreventAdd : if set, attempt to add item from remote (even implicitly trough replace) will cause no add but delete of remote item
+ // - fIgnoreUpdate : if set, attempt to update item from remote will be ignored, only adds (also implicit ones) are executed
+ // - fRejectStatus : if set>=0, incoming item is irgnored silently(==0) or with error(!=0)
+ aSyncItemP->checkItem(this);
+ // - create echo item if we need one
+ if (fEchoItemOp!=sop_none) {
+ // Note: sop_add makes no sense at all.
+ // Note: If echo is enabled, conflicts are not checked, as echo makes only sense in
+ // cases where we know that a conflict cannot occur or is irrelevant
+ // - artifically create a "conflicting" item, that is, one to be sent back to remote
+ echoItemP=newItemForRemote(aSyncItemP->getTypeID());
+ // - assign data from incoming item if echo is not a delete
+ if (fEchoItemOp!=sop_delete && fEchoItemOp!=sop_archive_delete && fEchoItemOp!=sop_soft_delete)
+ echoItemP->replaceDataFrom(*aSyncItemP);
+ // - set remote ID (note again: sop_add makes no sense here)
+ echoItemP->setRemoteID(aSyncItemP->getRemoteID());
+ // - set sop
+ echoItemP->setSyncOp(fEchoItemOp);
+ // - now check for eventual conflict
+ if (!fSlowSync) {
+ conflictingItemP = getConflictingItemByRemoteID(aSyncItemP);
+ // remove item if there is one that would conflict with the echo
+ if (conflictingItemP) dontSendItemAsServer(conflictingItemP);
+ conflictingItemP = NULL;
+ }
+ // - add echo to the list of items to be sent (DB takes ownership)
+ SendItemAsServer(echoItemP);
+ PDEBUGPRINTFX(DBG_DATA,("Echoed item back to remote with sop=%s",SyncOpNames[fEchoItemOp]));
+ // process item normally (except that we don't check for LUID conflicts)
+ }
+ // - check if incoming item should be processed at all
+ if (fRejectStatus>=0) {
+ // Note: a forced conflict can still occur even if item is rejected
+ // (this has the effect of unconditionally letting the server item win)
+ if (fForceConflict && syncop!=sop_add) {
+ conflictingItemP = forceConflict(aSyncItemP);
+ // Note: conflictingitem is always a replace
+ if (conflictingItemP) {
+ if (syncop==sop_delete) {
+ // original was delete, forced conflict means re-adding to remote
+ conflictingItemP->setSyncOp(sop_wants_add);
+ }
+ else {
+ // merge here because we'll not process the item further
+ conflictingItemP->mergeWith(*aSyncItemP,changedexisting,changedincoming,this);
+ }
+ }
+ }
+ // now discard the incoming item
+ delete aSyncItemP;
+ PDEBUGPRINTFX(fRejectStatus>=400 ? DBG_ERROR : DBG_DATA,("Item rejected with Status=%hd",fRejectStatus));
+ if (fRejectStatus>0) {
+ // rejected with status code (not necessarily error)
+ aStatusCommand.setStatusCode(fRejectStatus);
+ if (fRejectStatus>=300) {
+ // non 200-codes are errors
+ ADDDEBUGITEM(aStatusCommand,"Item rejected");
+ return false;
+ }
+ }
+ // silently rejected
+ return true;
+ }
+ // now perform requested operation
+ bool ok=false;
+ localstatus sta;
+ switch (syncop) {
+ readonly_delete:
+ // read-only handling of delete is like soft delete: remove map entry, but nothing else
+ PDEBUGPRINTFX(DBG_DATA,("Read-Only Datastore: Prevented actual deletion, just removing map entry"));
+ case sop_soft_delete:
+ // Readonly: allowed, as only map is touched
+ // soft delete from client is treated as an indication that the item was
+ // removed from the client's datastore, but is still in the set
+ // of sync data for that client.
+ // This means that the map item must be removed.
+ // - when the item is hard-deleted on the server, nothing will happen at next sync
+ // - when the item is modified on the server, it will be re-added to the client at next sync
+ // - when slow sync is performed, the item will be re-added, too.
+ // %%%%% Note that this does NOT work as it is now, as adds also occur for non-modified
+ // items that have no map AND are visible under current targetFilter.
+ // probably we should use a map entry with no remoteID for soft-deleted items later....
+ // Delete Map entry by remote ID
+ aSyncItemP->clearLocalID(); // none
+ sta=engProcessMap(aSyncItemP->getRemoteID(),NULL);
+ ok=sta==LOCERR_OK;
+ aStatusCommand.setStatusCode(ok ? 200 : sta);
+ break;
+ case sop_archive_delete:
+ if (fReadOnly) goto readonly_delete; // register removal of item in map, but do nothing to data itself
+ #ifdef OBJECT_FILTERING
+ if (!fDSConfigP->fInvisibleFilter.empty()) {
+ // turn into replace with all fields unavailable but made to pass invisible filter
+ // - make sure that no data field is assigned
+ aSyncItemP->cleardata();
+ // - make item pass "invisible" filter
+ if (aSyncItemP->makePassFilter(fDSConfigP->fInvisibleFilter.c_str())) {
+ // item now passes invisible rule, that is, it is invisible -> replace in DB
+ goto archive_delete;
+ }
+ }
+ // fall trough, no archive delete supported
+ #endif
+ // No archive delete support if there is no filter to detect/generate invisibles
+ // before SyncML 1.1 : we could return 210 here and still process the delete op.
+ // SyncML 1.1 : we must return 501 (not implemented) here
+ aStatusCommand.setStatusCode(501);
+ PDEBUGPRINTFX(DBG_ERROR,("Datastore does not support Archive-Delete, error status = 501"));
+ delete aSyncItemP;
+ ok=false;
+ break;
+ case sop_delete:
+ // delete item by LUID
+ if (fSlowSync) {
+ aStatusCommand.setStatusCode(403);
+ ADDDEBUGITEM(aStatusCommand,"Delete during slow sync not allowed");
+ PDEBUGPRINTFX(DBG_ERROR,("Delete during slow sync not allowed"));
+ delete aSyncItemP;
+ ok=false;
+ break;
+ }
+ // check for conflict with replace from server
+ // Note: conflict cases do not change local DB, so they are allowed before checking fReadOnly
+ if (!echoItemP) conflictingItemP = getConflictingItemByRemoteID(aSyncItemP); // do not check conflicts if we have already created an echo
+ // - check if we must force the conflict
+ if (!conflictingItemP && fForceConflict) {
+ conflictingItemP=forceConflict(aSyncItemP);
+ }
+ if (conflictingItemP) {
+ // conflict only if other party has replace
+ if (conflictingItemP->getSyncOp()==sop_replace || conflictingItemP->getSyncOp()==sop_wants_replace) {
+ if (!fDeleteWins) {
+ // act as if successfully deleted and cause re-adding of still existing server item
+ // - discard deletion
+ delete aSyncItemP;
+ // - remove map entry for this item (it no longer exists on the client)
+ sta = engProcessMap(NULL,conflictingItemP->getLocalID());
+ aStatusCommand.setStatusCode(sta==LOCERR_OK ? 200 : sta);
+ // - change replace to add (as to-be-replaced item is already deleted on remote)
+ conflictingItemP->setSyncOp(sop_add);
+ // - remove remote ID (will be assigned a new ID because the item is now re-added)
+ conflictingItemP->setRemoteID("");
+ // - no server operation needed
+ PDEBUGPRINTFX(DBG_DATA,("Conflict of Client Delete with Server replace -> discarded delete, re-added server item to client"));
+ ok=true;
+ break;
+ }
+ else {
+ // delete preceedes replace
+ // - avoid sending item from server
+ dontSendItemAsServer(conflictingItemP);
+ // - let delete happen
+ }
+ }
+ // if both have deleted the item, we should remove the map
+ // and avoid sending a delete to the client
+ else if (conflictingItemP->getSyncOp()==sop_delete) {
+ // - discard deletion
+ delete aSyncItemP;
+ // - remove map entry for this item (it no longer exists)
+ sta = engProcessMap(NULL,conflictingItemP->getLocalID());
+ aStatusCommand.setStatusCode(sta==LOCERR_OK ? 200 : sta);
+ // - make sure delete from server is not sent
+ dontSendItemAsServer(conflictingItemP);
+ PDEBUGPRINTFX(DBG_DATA,("Client and Server have deleted same item -> just removed map entry"));
+ ok=true;
+ break;
+ }
+ }
+ // real delete is discarded silently when fReadOnly is set
+ if (fReadOnly) goto readonly_delete; // register removal of item in map, but do nothing to data itself
+ // really delete
+ fLocalItemsDeleted++;
+ remainsvisible=false; // deleted not visible any more
+ ok=logicProcessRemoteItem(aSyncItemP,aStatusCommand,remainsvisible); // delete in local database NOW
+ break;
+ case sop_copy:
+ if (fReadOnly) {
+ delete aSyncItemP; // we don't need it
+ aStatusCommand.setStatusCode(200);
+ PDEBUGPRINTFX(DBG_DATA,("Read-Only: copy command silently discarded"));
+ ok=true;
+ break;
+ }
+ // %%% note: this would belong into specific datastore implementation, but is here
+ // now for simplicity as copy isn't used heavily het
+ // retrieve data from local datastore
+ if (!logicRetrieveItemByID(*aSyncItemP,aStatusCommand)) { ok=false; break; }
+ // process like add
+ /// @todo %%%%%%%%%%%%%%%% NOTE: MISSING SENDING BACK MAP COMMAND for new GUID created
+ goto normal_add;
+ case sop_add:
+ // test for slow sync
+ if (fSlowSync) goto sop_slow_add; // add in slow sync is like replace
+ normal_add:
+ // add as new item to server DB
+ aStatusCommand.setStatusCode(201); // item added (if no error occurs)
+ if (fReadOnly) {
+ delete aSyncItemP; // we don't need it
+ PDEBUGPRINTFX(DBG_DATA,("Read-Only: add command silently discarded"));
+ ok=true;
+ break;
+ }
+ // check if adds are prevented
+ if (!fPreventAdd) {
+ // add allowed
+ fLocalItemsAdded++;
+ #ifdef OBJECT_FILTERING
+ // test if acceptable
+ if (!isAcceptable(aSyncItemP,aStatusCommand)) { ok=false; break; } // cannot be accepted
+ // Note: making item to pass sync set filter is implemented in derived DB implementation
+ // as criteria for passing might be in data that must first be read from the DB
+ #endif
+ remainsvisible=true; // should remain visible
+ ok=logicProcessRemoteItem(aSyncItemP,aStatusCommand,remainsvisible); // add to local database NOW
+ if (!remainsvisible && fSessionP->getSyncMLVersion()>=syncml_vers_1_2) {
+ PDEBUGPRINTFX(DBG_DATA+DBG_HOT,("Added item is not visible under current filters -> remove it on client"));
+ goto removefromremoteandsyncset;
+ }
+ break;
+ }
+ goto preventadd;
+ archive_delete:
+ sop_slow_add:
+ aSyncItemP->setSyncOp(sop_replace); // set correct op
+ // ...and process like replace
+ case sop_reference_only:
+ case sop_replace:
+ #ifdef OBJECT_FILTERING
+ // test if acceptable
+ if (!isAcceptable(aSyncItemP,aStatusCommand)) { ok=false; break; } // cannot be accepted
+ // Note: making item to pass sync set filter is implemented in derived DB implementation
+ // as criteria for passing might be in data that must first be read from the DB
+ #endif
+ // check for conflict with server side modifications
+ if (!fSlowSync) {
+ if (!echoItemP) conflictingItemP = getConflictingItemByRemoteID(aSyncItemP);
+ // - check if we must force the conflict
+ if (!conflictingItemP && fForceConflict) {
+ conflictingItemP=forceConflict(aSyncItemP);
+ }
+ bool deleteconflict=false;
+ if (conflictingItemP) {
+ // Note: if there is a conflict, this replace cannot be an
+ // implicit add, so we don't need to check for fPreventAdd
+ // here.
+ // Note: if we are in ignoreUpdate mode, the only conflict resolution
+ // possible is unconditional server win
+ sInt16 cmpRes = SYSYNC_NOT_COMPARABLE;
+ // assume we can resolve the conflict
+ aStatusCommand.setStatusCode(419); // default to server win
+ ADDDEBUGITEM(aStatusCommand,"Conflict resolved by server");
+ PDEBUGPRINTFX(DBG_HOT,(
+ "Conflict: Remote <%s> with remoteID=%s <--> Local <%s> with localID=%s, remoteID=%s",
+ SyncOpNames[aSyncItemP->getSyncOp()],
+ aSyncItemP->getRemoteID(),
+ SyncOpNames[conflictingItemP->getSyncOp()],
+ conflictingItemP->getLocalID(),
+ conflictingItemP->getRemoteID()
+ ));
+ // we have a conflict, decide what to do
+ TConflictResolution crstrategy;
+ if (fReadOnly || fIgnoreUpdate) {
+ // server always wins and overwrites modified client version
+ PDEBUGPRINTFX(DBG_DATA,("Read-Only or IgnoreUpdate: server always wins"));
+ crstrategy=cr_server_wins;
+ }
+ else {
+ // two-way
+ crstrategy = fItemConflictStrategy; // get conflict strategy pre-set for this item
+ if (conflictingItemP->getSyncOp()==sop_delete) {
+ // server wants to delete item, client wants to replace
+ if (fDSConfigP->fTryUpdateDeleted) {
+ // if items are not really deleted, but only made invisible,
+ // we can assume we can update the "deleted" item
+ // BUT ONLY if the conflict strategy is not "server always wins"
+ if (crstrategy==cr_server_wins) {
+ PDEBUGPRINTFX(DBG_PROTO+DBG_HOT,("Conflict of Client Replace with Server delete and strategy is server-wins -> delete from client"));
+ aStatusCommand.setStatusCode(419); // server wins, client command ignored
+ break; // done
+ }
+ else {
+ PDEBUGPRINTFX(DBG_PROTO+DBG_HOT,("Conflict of Client Replace with Server delete -> try to update already deleted item (as it might still exist in syncset)"));
+ // apply replace (and in case of !fDeleteWins, eventual implicit add)
+ fPreventAdd=fDeleteWins; // we want implicit add only if delete cannot win
+ remainsvisible=!fDeleteWins; // we want to see the item in the sync set if delete does not win!
+ ok=logicProcessRemoteItem(aSyncItemP,aStatusCommand,remainsvisible);
+ }
+ if (fDeleteWins) {
+ if (!ok) {
+ // could not update already deleted item
+ PDEBUGPRINTFX(DBG_PROTO,("Could not update already deleted server item (seems to be really deleted, not just invisible)"));
+ aStatusCommand.setStatusCode(419); // server wins, client command ignored
+ }
+ else {
+ // update of invisible item successful, but it will still be deleted from client
+ // Note: eventually, the update was apparently successful, but only because an UPDATE with no
+ // target does not report an error. So effectively, no update might have happened.
+ PDEBUGPRINTFX(DBG_PROTO,("Updated already deleted server item, but delete still wins -> client item will be deleted"));
+ fLocalItemsUpdated++;
+ aStatusCommand.setStatusCode(200); // client command successful (but same item will still be deleted)
+ }
+ // nothing more to do, let delete happen on the client (conflictingItemP delete will be sent)
+ ok=true;
+ }
+ else {
+ // not fDeleteWins - item failed, updated or implicitly added
+ if (ok) {
+ // update (or implicit add) successful
+ if (aStatusCommand.getStatusCode()==201) {
+ PDEBUGPRINTFX(DBG_PROTO,("Client Update wins and has re-added already deleted server item -> prevent delete on client"));
+ fLocalItemsAdded++;
+ }
+ else {
+ PDEBUGPRINTFX(DBG_PROTO,("Client Update wins and has updated still existing server item -> prevent delete on client"));
+ fLocalItemsUpdated++;
+ }
+ // and client item wins - prevent sending delete to client
+ // - don't send delete to client
+ conflictingItemP->setSyncOp(sop_none); // just in case...
+ dontSendItemAsServer(conflictingItemP);
+ }
+ }
+ // done
+ break;
+ }
+ else {
+ // Normal delete conflict processing (assuming deleted items REALLY deleted)
+ if (!fDeleteWins) {
+ // - client always wins (replace over delete)
+ crstrategy=cr_client_wins;
+ deleteconflict=true; // flag condition for processing below
+ // - change from replace to add, because item is already deleted in server and must be re-added
+ fLocalItemsAdded++;
+ aSyncItemP->setSyncOp(sop_add);
+ PDEBUGPRINTFX(DBG_PROTO,("Conflict of Client Replace with Server delete -> client wins, client item is re-added to server"));
+ }
+ else {
+ // delete wins, just discard incoming item
+ delete aSyncItemP;
+ PDEBUGPRINTFX(DBG_PROTO,("Conflict of Client Replace with Server delete -> DELETEWINS() set -> ignore client replace"));
+ ok=true;
+ break;
+ }
+ }
+ }
+ else {
+ // replace from client conflicts with replace from server
+ // - compare items for further conflict resolution
+ // NOTE: it is serveritem.compareWith(clientitem)
+ cmpRes = conflictingItemP->compareWith(
+ *aSyncItemP,eqm_conflict,this
+ #ifdef SYDEBUG
+ ,PDEBUGTEST(DBG_CONFLICT) // show conflict comparisons in normal sync if conflict details are enabled
+ #endif
+ );
+ PDEBUGPRINTFX(DBG_DATA,(
+ "Compared conflicting items with eqm_conflict: remoteItem %s localItem",
+ cmpRes==0 ? "==" : (cmpRes>0 ? "<" : ">")
+ ));
+ // see if we can determine newer item
+ if (crstrategy==cr_newer_wins) {
+ if (cmpRes!=0 && conflictingItemP->sortable(*aSyncItemP)) {
+ // newer item wins
+ // (comparison was: serveritem.compareWith(clientitem), so
+ // cmpRes<0 means that client is newer
+ PDEBUGPRINTFX(DBG_PROTO,("Conflict resolved by identifying newer item"));
+ if (cmpRes > 0)
+ crstrategy=cr_server_wins; // server has newer item
+ else
+ crstrategy=cr_client_wins; // client has newer item
+ }
+ else {
+ // newer item cannot be determined, duplicate items
+ crstrategy=cr_duplicate;
+ }
+ PDEBUGPRINTFX(DBG_DATA,(
+ "Newer item %sdetermined: %s",
+ crstrategy==cr_duplicate ? "NOT " : "",
+ crstrategy==cr_client_wins ? "Client item is newer and wins" :
+ (crstrategy==cr_server_wins ? "Server item is newer ans wins" : "item is duplicated if different")
+ ));
+ }
+ }
+ // modify strategy based on compare
+ if (cmpRes==0 && crstrategy==cr_duplicate) {
+ // items are equal by definition of item comparison,
+ // but obviously both changed, this means that changes should be
+ // mergeable
+ // So, by deciding arbitrarily that server has won, we will not loose any data
+ crstrategy=cr_server_wins; // does not matter, because merge will be attempted
+ PDEBUGPRINTFX(DBG_DATA,("Duplication avoided because items are equal by their own definition, just merge"));
+ }
+ // if adds prevented, we cannot duplicate, let server win
+ if (fPreventAdd && crstrategy==cr_duplicate) crstrategy=cr_server_wins;
+ } // not fReadOnly
+ // now apply strategy
+ if (crstrategy==cr_duplicate) {
+ // add items vice versa
+ PDEBUGPRINTFX(DBG_PROTO,("Conflict resolved by duplicating items in both databases"));
+ aStatusCommand.setStatusCode(209);
+ fConflictsDuplicated++;
+ // - set server item such that it will be added as new item to client DB
+ conflictingItemP->setSyncOp(sop_add);
+ // - break up mapping between client and server item BEFORE adding to server
+ // because else adding of item with already existing remoteID can fail.
+ // In addition, item now being sent to client may not have a map before
+ // it receives a map command from the client!
+ sta = engProcessMap(NULL,conflictingItemP->getLocalID());
+ if(sta!=LOCERR_OK) {
+ PDEBUGPRINTFX(DBG_ERROR,(
+ "Problem (status=%hd) removing map entry for LocalID='%s'",
+ sta,
+ conflictingItemP->getLocalID()
+ ));
+ }
+ // - add client item as new item to server DB
+ fLocalItemsAdded++;
+ aSyncItemP->setSyncOp(sop_add); // set correct op
+ remainsvisible=true; // should remain visible
+ ok=logicProcessRemoteItem(aSyncItemP,aStatusCommand,remainsvisible); // add to local database NOW
+ break;
+ }
+ else if (crstrategy==cr_server_wins) {
+ // Note: for fReadOnly, this is always the case!
+ // server item wins and is sent to client
+ PDEBUGPRINTFX(DBG_PROTO,("Conflict resolved: server item replaces client item"));
+ aStatusCommand.setStatusCode(419); // server wins, client command ignored
+ fConflictsServerWins++;
+ // - make sure item is set to replace data in client
+ conflictingItemP->setSyncOp(sop_replace);
+ // - attempt to merge data from loosing item (accumulating fields)
+ if (!fReadOnly) {
+ conflictingItemP->mergeWith(*aSyncItemP,changedexisting,changedincoming,this);
+ }
+ if (fIgnoreUpdate) changedexisting=false; // never try to update existing item
+ if (changedexisting) {
+ // we have merged something, so server must be updated, too
+ // Note: after merge, both items are equal. We check if conflictingitem
+ // has changed, but if yes, we write the incoming item. Conflicting item
+ // will get sent to client later
+ PDEBUGPRINTFX(DBG_DATA,("*** Merged some data from loosing client item into winning server item"));
+ // set correct status for conflict resultion by merge
+ aStatusCommand.setStatusCode(207); // merged
+ // process update in local database
+ fLocalItemsUpdated++;
+ aSyncItemP->setSyncOp(sop_replace); // update
+ remainsvisible=true; // should remain visible
+ ok=logicProcessRemoteItem(aSyncItemP,aStatusCommand,remainsvisible); // update in local database NOW
+ break;
+ }
+ else {
+ // - item sent by client has lost and can be deleted now
+ // %%% eventually add option here to archive item in some way
+ // BUT ONLY IF NOT fReadOnly
+ delete aSyncItemP;
+ }
+ }
+ else if (crstrategy==cr_client_wins) {
+ // client item wins and is sent to server
+ PDEBUGPRINTFX(DBG_PROTO,("Conflict resolved: client item replaces server item"));
+ aStatusCommand.setStatusCode(208); // client wins
+ fConflictsClientWins++;
+ // - attempt to merge data from loosing item (accumulating fields)
+ if (!deleteconflict) {
+ aSyncItemP->mergeWith(*conflictingItemP,changedincoming,changedexisting,this);
+ }
+ if (changedincoming) {
+ // we have merged something, so client must be updated even if it has won
+ // Note: after merge, both items are equal. We check if aSyncItemP
+ // has changed, but if yes, we make sure the conflicting item gets
+ // sent to the client
+ PDEBUGPRINTFX(DBG_DATA,("*** Merged some data from loosing server item into winning client item"));
+ conflictingItemP->setSyncOp(sop_replace); // update
+ // set correct status for conflict resultion by merge
+ aStatusCommand.setStatusCode(207); // merged
+ // will be sent because it is in the list
+ }
+ else {
+ // - make sure conflicting item from server is NOT sent to client
+ conflictingItemP->setSyncOp(sop_none); // just in case...
+ dontSendItemAsServer(conflictingItemP);
+ aStatusCommand.setStatusCode(200); // ok
+ }
+ // - replace item in server (or leave it as is, if conflict was with delete)
+ if (!deleteconflict) {
+ fLocalItemsUpdated++;
+ aSyncItemP->setSyncOp(sop_replace);
+ }
+ remainsvisible=true; // should remain visible
+ ok=logicProcessRemoteItem(aSyncItemP,aStatusCommand,remainsvisible); // replace in local database NOW
+ break;
+ }
+ } // replace conflict
+ else {
+ // normal replace without any conflict
+ if (fReadOnly) {
+ delete aSyncItemP; // we don't need it
+ aStatusCommand.setStatusCode(200);
+ PDEBUGPRINTFX(DBG_DATA,("Read-Only: replace command silently discarded"));
+ ok=true;
+ break;
+ }
+ // no conflict, just let client replace server's item
+ PDEBUGPRINTFX(DBG_DATA+DBG_CONFLICT,("No Conflict: client item replaces server item"));
+ // - replace item in server (or add if item does not exist and not fPreventAdd)
+ aSyncItemP->setSyncOp(sop_replace);
+ remainsvisible=true; // should remain visible
+ if (!logicProcessRemoteItem(aSyncItemP,aStatusCommand,remainsvisible)) {
+ // check if this is a 404 or 410 and fPreventAdd
+ if (fPreventAdd && (aStatusCommand.getStatusCode()==404 || aStatusCommand.getStatusCode()==410))
+ goto preventadd2; // to-be-replaced item not found and implicit add prevented -> delete from remote
+ // simply failed
+ ok=false;
+ break;
+ }
+ // still visible in sync set?
+ if (!remainsvisible && fSessionP->getSyncMLVersion()>=syncml_vers_1_2) {
+ // -> cause item to be deleted on remote
+ PDEBUGPRINTFX(DBG_DATA+DBG_HOT,("Item replaced no longer visible in syncset -> returning delete to remote"));
+ goto removefromremoteandsyncset;
+ }
+ // processed ok
+ if (aStatusCommand.getStatusCode()==201)
+ fLocalItemsAdded++;
+ else
+ fLocalItemsUpdated++;
+ ok=true;
+ break;
+ }
+ } // normal sync
+ else {
+ // slow sync (replaces AND adds are treated the same)
+ // - first do a strict search for identical item. This is required to
+ // prevent that in case of multiple (loosely compared) matches we
+ // catch the wrong item and cause a mess at slowsync
+ // NOTE: we do compare only relevant fields (eqm_conflict)
+ TSyncItem *matchingItemP = getMatchingItem(aSyncItemP,eqm_conflict);
+ if (!matchingItemP) {
+ // try again with less strict comparison (eqm_slowsync or eqm_always for firsttimesync)
+ DEBUGPRINTFX(DBG_DATA+DBG_MATCH,("Strict search for matching item failed, try with configured EqMode now"));
+ matchingItemP = getMatchingItem(aSyncItemP,fFirstTimeSync ? eqm_always : eqm_slowsync);
+ }
+ if (matchingItemP) {
+ // both sides already have this item
+ PDEBUGPRINTFX(DBG_DATA+DBG_HOT,(
+ "Slow Sync Match detected - localID='%s' matches incoming item",
+ matchingItemP->getLocalID()
+ ));
+ fSlowSyncMatches++;
+ aStatusCommand.setStatusCode(syncop==sop_add ? 201 : 200); // default is: simply ok. But if original op was Add, MUST return 201 status (SCTS requires it)
+ bool matchingok=false;
+ // - do not update map yet, as we still don't know if client item will
+ // eventually be added instead of mapped
+ // Note: ONLY in case this is a reference-only item, the map is already updated!
+ bool mapupdated = syncop==sop_reference_only;
+ // - determine which one is winning
+ bool needserverupdate=false;
+ bool needclientupdate=false;
+ // if updates are ignored, we can short-cut here
+ // Note: if this is a reference-only item, it was already updated (if needed) before last suspend
+ // so skip updating now!
+ if (syncop!=sop_reference_only && !fIgnoreUpdate) {
+ // Not a reference-only and also updates not suppressed
+ // - for a read-only datastore, this defaults to server always winning
+ TConflictResolution crstrategy =
+ fReadOnly ?
+ cr_server_wins : // server always wins for read-only
+ fItemConflictStrategy; // pre-set strategy for this item
+ // Determine what data to use
+ if (crstrategy==cr_newer_wins) {
+ // use newer
+ // Note: comparison is clientitem.compareWith(serveritem)
+ // so if result>0, client is newer than server
+ sInt16 cmpRes = aSyncItemP->compareWith(
+ *matchingItemP,eqm_nocompare,this
+ #ifdef SYDEBUG
+ ,PDEBUGTEST(DBG_CONFLICT+DBG_DETAILS) // show age comparisons only if we want to see details
+ #endif
+ );
+ if (cmpRes==1) crstrategy=cr_client_wins;
+ else if (cmpRes==-1 || fPreventAdd) crstrategy=cr_server_wins; // server wins if adds prevented
+ else crstrategy=cr_duplicate; // fall back to duplication if we can't determine newer item
+ PDEBUGPRINTFX(DBG_DATA,(
+ "Newer item %sdetermined: %s",
+ crstrategy==cr_duplicate ? "NOT " : "",
+ crstrategy==cr_client_wins ? "Client item is newer and wins" :
+ (crstrategy==cr_server_wins ? "Server item is newer and wins" : "item is duplicated if different")
+ ));
+ }
+ // if adds prevented, we cannot duplicate, let server win
+ if (fPreventAdd && crstrategy==cr_duplicate) crstrategy=cr_server_wins;
+ // now execute chosen strategy
+ if (crstrategy==cr_client_wins) {
+ // - merge server's data into client item
+ PDEBUGPRINTFX(DBG_DATA,("Trying to merge some data from loosing server item into winning client item"));
+ aSyncItemP->mergeWith(*matchingItemP,changedincoming,changedexisting,this);
+ // only count if server gets updated
+ if (changedexisting) fConflictsClientWins++;
+ // Note: changedexisting will cause needserverupdate to be set below
+ }
+ else if (crstrategy==cr_server_wins) {
+ // - merge client data into server item
+ PDEBUGPRINTFX(DBG_DATA,("Trying to merge some data from loosing client item into winning server item"));
+ matchingItemP->mergeWith(*aSyncItemP,changedexisting,changedincoming,this);
+ // only count if client gets updated
+ if (changedincoming) fConflictsServerWins++;
+ // Note: changedincoming will cause needclientupdate to be set below
+ }
+ else if (crstrategy==cr_duplicate) {
+ // test if items are equal enough
+ // (Note: for first-sync, compare mode for item matching can be looser
+ // (eqm_always), so re-comparison here makes sense)
+ if (
+ matchingItemP->compareWith(
+ *aSyncItemP,eqm_slowsync,this
+ #ifdef SYDEBUG
+ ,PDEBUGTEST(DBG_CONFLICT+DBG_DETAILS) // show equality re-test only for details enabled
+ #endif
+ )!=0
+ ) {
+ string guid;
+ // items are not really equal in content, so duplicate them on both sides
+ PDEBUGPRINTFX(DBG_PROTO,("Matching items are not fully equal, duplicate them on both sides"));
+ fConflictsDuplicated++;
+ // - duplicates contain merged data
+ matchingItemP->mergeWith(*aSyncItemP,changedexisting,changedincoming,this);
+ // - add client item (with server data merged) as new item to server
+ fLocalItemsAdded++;
+ aSyncItemP->setSyncOp(sop_add);
+ aStatusCommand.setStatusCode(201); // item added (if no error occurs)
+ remainsvisible=true; // should remain visible
+ matchingok=logicProcessRemoteItem(aSyncItemP,aStatusCommand,remainsvisible,&guid); // add item in local database NOW
+ aSyncItemP=NULL; // is already deleted!
+ if (matchingok) { // do it only if server add successful, because otherwise we don't have a GUID
+ // - make sure same item is ADDED as new item to client
+ matchingItemP->setSyncOp(sop_add); // add it, prevent it from re-match (is sop_wants_add now)
+ matchingItemP->setLocalID(guid.c_str()); // this is now a pair with the newly added item (not the original one)
+ matchingItemP->setRemoteID(""); // we don't know the remote ID yet
+ }
+ // adding duplicate to server (add) is already done
+ changedexisting=false;
+ changedincoming=false;
+ mapupdated=true; // no need to update map
+ needserverupdate=false; // already done
+ // client must received the (updated) server item as an add (set above)
+ needclientupdate=true;
+ }
+ }
+ } // if not ignoreUpdate
+ // Update server map now if required
+ // - NOTE THAT THIS IS VERY IMPORTANT TO DO BEFORE any eventual
+ // replaces, because replacing the matchingItem can only be
+ // done via its remoteID, which is, at this moment, probably not
+ // valid. After Mapping, it is ensured that the mapped remoteID
+ // uniquely identifies the matchingItem.
+ if (!mapupdated) {
+ // - update map in server
+ sta = engProcessMap(
+ aSyncItemP->getRemoteID(), // remote ID (LUID) of item received from client
+ matchingItemP->getLocalID() // local ID (GUID) of item already stored in server
+ );
+ matchingok = sta==LOCERR_OK;
+ if (!matchingok) {
+ // failed
+ ok=false;
+ aStatusCommand.setStatusCode(sta);
+ break;
+ }
+ }
+ // Now prepare updates of (already mapped) server and client items if needed
+ if (changedexisting) {
+ // matched item in server's sync set was changed and must be update in server DB
+ // update server only if updates during non-first-time slowsync are enabled
+ if (fFirstTimeSync || fSessionP->fUpdateServerDuringSlowsync) {
+ needserverupdate=true; // note: ineffective if fReadOnly
+ PDEBUGPRINTFX(DBG_DATA+DBG_HOT,("Server data was updated by record sent by client, REPLACE it in server DB"));
+ }
+ else {
+ PDEBUGPRINTFX(DBG_DATA+DBG_HOT,("Server data was updated by record sent by client, but server updates during non-firsttime slowsyncs are disabled"));
+ }
+ }
+ if (changedincoming) {
+ // incoming item from remote was changed after receiving and must be reflected
+ // back to client
+ // NOTE: this can also happen with sop_reference_only items
+ // - client will be updated because matchingItemP is still in list
+ matchingItemP->setSyncOp(sop_replace); // cancel wants_add state, prevent re-match
+ // - matchingItem was retrieved BEFORE map was done, and has no valid remote ID
+ // so remote ID must be added now.
+ matchingItemP->setRemoteID(aSyncItemP->getRemoteID());
+ // update client only if updates during non-first-time slowsync are enabled
+ if (fFirstTimeSync || fSessionP->fUpdateClientDuringSlowsync) {
+ PDEBUGPRINTFX(DBG_DATA+DBG_HOT,("Record sent by client was updated with server data, client will receive a REPLACE"));
+ needclientupdate=true;
+ }
+ else {
+ PDEBUGPRINTFX(DBG_DATA+DBG_HOT,("Record sent by client was updated, but client updates during non-firsttime slowsyncs are disabled"));
+ }
+ }
+ // update
+ // - check if client must be updated
+ if (!needclientupdate) {
+ // prevent updating client
+ // - make sure matching item from server is NOT sent to client
+ matchingItemP->setSyncOp(sop_none); // just in case...
+ dontSendItemAsServer(matchingItemP);
+ }
+ else {
+ // check if replaces may be sent to client during slowsync
+ if (matchingItemP->getSyncOp()==sop_replace && fSessionP->fNoReplaceInSlowsync) {
+ PDEBUGPRINTFX(DBG_DATA+DBG_HOT,("Update of client item suppressed because of <noreplaceinslowsync>"));
+ matchingItemP->setSyncOp(sop_none); // just in case...
+ dontSendItemAsServer(matchingItemP);
+ }
+ else {
+ PDEBUGPRINTFX(DBG_DATA+DBG_HOT,("Update of client item enabled (will be sent later)"));
+ }
+ }
+ // - check if server must be updated (NEVER for fReadOnly)
+ if (!fReadOnly && needserverupdate && aSyncItemP) {
+ // update server
+ // - update server side (NOTE: processItemAsServer takes ownership, pointer gets invalid!)
+ fLocalItemsUpdated++;
+ aSyncItemP->setSyncOp(sop_replace);
+ remainsvisible=true; // should remain visible
+ matchingok=logicProcessRemoteItem(aSyncItemP,aStatusCommand,remainsvisible); // replace item in local database NOW
+ PDEBUGPRINTFX(DBG_DATA+DBG_HOT,("Updated server item"));
+ }
+ else {
+ // - delete incoming item unprocessed (if not already deleted)
+ if (aSyncItemP) delete aSyncItemP;
+ }
+ // check if we need to actively delete item from the client as it falls out of the filter
+ if (!remainsvisible && fSessionP->getSyncMLVersion()>=syncml_vers_1_2) {
+ // -> cause item to be deleted on remote
+ PDEBUGPRINTFX(DBG_DATA+DBG_HOT,("Slow sync matched item no longer visible in syncset -> returning delete to remote"));
+ goto removefromremoteandsyncset;
+ }
+ ok=matchingok;
+ break;
+ }
+ else {
+ PDEBUGPRINTFX(DBG_DATA+DBG_HOT,("No matching item found - add it to local database"));
+ // this item is not yet on the server, add it normally
+ aStatusCommand.setStatusCode(201); // item added (if no error occurs)
+ if (fReadOnly) {
+ delete aSyncItemP; // we don't need it
+ PDEBUGPRINTFX(DBG_DATA,("Read-Only: slow-sync add/replace command silently discarded"));
+ ok=true;
+ break;
+ }
+ if (fPreventAdd) goto preventadd;
+ fLocalItemsAdded++;
+ aSyncItemP->setSyncOp(sop_add); // set correct op
+ remainsvisible=true; // should remain visible
+ ok=logicProcessRemoteItem(aSyncItemP,aStatusCommand,remainsvisible); // add to local database NOW
+ break;
+ }
+ } // slow sync
+ break; // just in case....
+ preventadd:
+ // add not allowed, delete remote item instead
+ // - consume original
+ delete aSyncItemP;
+ preventadd2:
+ aStatusCommand.setStatusCode(201); // pretend adding item was successful
+ PDEBUGPRINTFX(DBG_DATA,("Prevented explicit add, returning delete to remote"));
+ ok=true;
+ goto removefromremote; // as we have PREVENTED adding the item, it is not in the map
+ removefromremoteandsyncset:
+ // remove item from local map first
+ engProcessMap(remoteid.c_str(),NULL);
+ removefromremote:
+ // have item removed from remote
+ delitemP = newItemForRemote(itemtypeid);
+ delitemP->setRemoteID(remoteid.c_str());
+ delitemP->setSyncOp(sop_delete);
+ SendItemAsServer(delitemP); // pass ownership
+ break;
+ default :
+ SYSYNC_THROW(TSyncException("Unknown sync op in TLocalEngineDS::processRemoteItemAsServer"));
+ } // switch
+ if (ok)
+ OBJ_PROGRESS_EVENT(fSessionP->getSyncAppBase(),pev_itemprocessed,fDSConfigP,fLocalItemsAdded,fLocalItemsUpdated,fLocalItemsDeleted);
+ // done
+ return ok;
+} // TLocalEngineDS::engProcessRemoteItemAsServer
+
+
+/// @brief called at end of request processing, should be used to save suspend state
+/// @note superdatastore does it itself to have correct order of things happening
+void TLocalEngineDS::engRequestEnded(void)
+{
+ // variant for independent non-super/non-sub datastore
+ if (!fAsSubDatastoreOf) {
+ // If DS 1.2: Make sure everything is ready for a resume in case there's an abort (implicit Suspend)
+ // before the next request. Note that the we cannot wait for session timeout, as the resume attempt
+ // from the client probably arrives much earlier.
+ // Note: It is ESSENTIAL not to save the state until sync set is ready, because saving state will
+ // cause DB access, and DB access is not permitted while sync set is eventually still loading
+ // (possibly in a separate thread!). So dssta_syncmodestable (as in <=3.0.0.2) is NOT enough here!
+ if (testState(dssta_syncsetready)) {
+ // make sure all unsent items are marked for resume
+ engSaveSuspendState(false); // only if not already aborted
+ }
+ // let datastore prepare for end of request
+ dsRequestEnded();
+ // and let it prepare for end of this thread as well
+ dsThreadMayChangeNow();
+ }
+} // TLocalEngineDS::engRequestEnded
+
+
+
+#else
+
+// Client Case
+// ===========
+
+
+// process sync operation from server with specified sync item
+// (according to current sync mode of local datastore)
+// - returns true (and unmodified or non-200-successful status) if
+// operation could be processed regularily
+// - returns false (but probably still successful status) if
+// operation was processed with internal irregularities, such as
+// trying to delete non-existant item in datastore with
+// incomplete Rollbacks (which returns status 200 in this case!).
+bool TLocalEngineDS::engProcessRemoteItemAsClient(
+ TSyncItem *aSyncItemP,
+ TStatusCommand &aStatusCommand // status, must be set to correct status code (ok / error)
+)
+{
+ string remoteid,localid;
+ bool ok;
+ bool remainsvisible;
+
+ // send event and check for user abort
+ #ifdef PROGRESS_EVENTS
+ if (!fSessionP->getSyncAppBase()->NotifyProgressEvent(pev_itemreceived,fDSConfigP,++fItemsReceived,fRemoteNumberOfChanges)) {
+ fSessionP->AbortSession(500,true,LOCERR_USERABORT); // this also causes datastore to be aborted
+ }
+ if (!fSessionP->getSyncAppBase()->NotifyProgressEvent(pev_suspendcheck)) {
+ fSessionP->SuspendSession(LOCERR_USERSUSPEND);
+ }
+ #endif
+ // check if datastore is aborted
+ if (CheckAborted(aStatusCommand)) return false;
+ // init behaviour vars (these are normally used by server only,
+ // but must be initialized correctly for client as well as descendants might test them
+ fPreventAdd = false;
+ fIgnoreUpdate = false;
+ // get operation out of item
+ TSyncOperation syncop=aSyncItemP->getSyncOp();
+ // show
+ DEBUGPRINTFX(DBG_DATA,("%s item operation received",SyncOpNames[syncop]));
+ // check if receiving commands is allowed at all
+ // - must be in correct sync state
+ if (!testState(dssta_syncgendone)) {
+ // Modifications from server not allowed before client has done sync gen
+ // %%% we could eventually relax this one, depending on the DB
+ aStatusCommand.setStatusCode(403);
+ PDEBUGPRINTFX(DBG_ERROR,("Server command not allowed before client has sent entire <sync>"));
+ delete aSyncItemP;
+ return false;
+ }
+ // - must not be one-way
+ if (fSyncMode==smo_fromclient) {
+ // Modifications from server not allowed during update from client only
+ aStatusCommand.setStatusCode(403);
+ ADDDEBUGITEM(aStatusCommand,"Server command not allowed in one-way/refresh from client");
+ PDEBUGPRINTFX(DBG_ERROR,("Server command not allowed in one-way/refresh from client"));
+ delete aSyncItemP;
+ return false;
+ }
+ else {
+ // silently discard all modifications if readonly
+ if (fReadOnly) {
+ PDEBUGPRINTFX(DBG_ERROR,("Read-Only: silently discarding all modifications"));
+ aStatusCommand.setStatusCode(200); // always ok (but never "added"), so server cannot expect Map
+ delete aSyncItemP;
+ return true;
+ }
+ // let item check itself to catch special cases
+ // - init variables which are used/modified by item checking
+ fItemSizeLimit=-1; // no limit
+ fCurrentSyncOp = syncop;
+ fEchoItemOp = sop_none;
+ fItemConflictStrategy=fSessionConflictStrategy; // get default strategy for this item
+ fForceConflict = false;
+ fDeleteWins = fDSConfigP->fDeleteWins; // default to configured value
+ fRejectStatus = -1; // no rejection
+ // - now check
+ // check reads and eventually modifies:
+ // - fEchoItemOp : if not sop_none, the incoming item is echoed back to the remote with the specified syncop
+ // - fItemConflictStrategy : might be changed from the pre-set datastore default
+ // - fForceConflict : if set, a conflict is forced by adding the corresponding local item (if any) to the list of items to be sent
+ // - fDeleteWins : if set, in a replace/delete conflict delete will win (regardless of strategy)
+ // - fPreventAdd : if set, attempt to add item from remote (even implicitly trough replace) will cause no add but delete of remote item
+ // - fIgnoreUpdate : if set, attempt to update item from remote will be ignored, only adds (also implicit ones) are executed
+ // - fRejectStatus : if set>=0, incoming item is irgnored silently(==0) or with error(!=0)
+ aSyncItemP->checkItem(this);
+ // - check if incoming item should be processed at all
+ if (fRejectStatus>=0) {
+ // now discard the incoming item
+ delete aSyncItemP;
+ PDEBUGPRINTFX(fRejectStatus>=400 ? DBG_ERROR : DBG_DATA,("Item rejected with Status=%hd",fRejectStatus));
+ if (fRejectStatus>0) {
+ // rejected with status code (not necessarily error)
+ aStatusCommand.setStatusCode(fRejectStatus);
+ if (fRejectStatus>=300) {
+ // non 200-codes are errors
+ ADDDEBUGITEM(aStatusCommand,"Item rejected");
+ return false;
+ }
+ }
+ // silently rejected
+ return true;
+ }
+ // now perform requested operation
+ switch (syncop) {
+ case sop_soft_delete:
+ case sop_archive_delete:
+ case sop_delete:
+ // delete item
+ fLocalItemsDeleted++;
+ remainsvisible=false; // deleted not visible any more
+ ok=logicProcessRemoteItem(aSyncItemP,aStatusCommand,remainsvisible); // delete in local database NOW
+ break;
+ case sop_copy:
+ // %%% note: this would belong into specific datastore implementation, but is here
+ // now for simplicity as copy isn't used heavily het
+ // retrieve data from local datastore
+ if (!logicRetrieveItemByID(*aSyncItemP,aStatusCommand)) {
+ ok=false;
+ break;
+ }
+ // process like add
+ case sop_add:
+ // add as new item to client DB
+ aStatusCommand.setStatusCode(201); // item added (if no error occurs)
+ fLocalItemsAdded++;
+ // add to local datastore
+ // - get remoteid BEFORE processing item (as logicProcessRemoteItem consumes the item!!)
+ remoteid=aSyncItemP->getRemoteID(); // get remote ID
+ // check if this remoteid is already known from last session - if so, this means that
+ // this add was re-sent and must NOT be executed
+ if (isAddFromLastSession(remoteid.c_str())) {
+ aStatusCommand.setStatusCode(418); // already exists
+ PDEBUGPRINTFX(DBG_ERROR,("Warning: Item with same server-side ID (GUID) was already added in previous session - ignore add now"));
+ delete aSyncItemP; // forget item
+ ok=false;
+ break;
+ }
+ #ifdef OBJECT_FILTERING
+ if (!isAcceptable(aSyncItemP,aStatusCommand)) {
+ delete aSyncItemP; // forget item
+ ok=false; // cannot be accepted
+ break;
+ }
+ #endif
+ remainsvisible=true; // should remain visible
+ ok=logicProcessRemoteItem(aSyncItemP,aStatusCommand,remainsvisible,&localid); // add to local database NOW, get back local GUID
+ if (!ok) break;
+ // if added (not replaced), we need to send map
+ if (aStatusCommand.getStatusCode()==201) {
+ // really added: remember map entry
+ DEBUGPRINTFX(DBG_ADMIN+DBG_DETAILS,(
+ "engProcessRemoteItemAsClient: add command: adding new entry to fPendingAddMaps - localid='%s', remoteID='%s'",
+ localid.c_str(),
+ remoteid.c_str()
+ ));
+ fPendingAddMaps[localid]=remoteid;
+ }
+ ok=true; // fine
+ break;
+ case sop_replace:
+ // - replace item in client
+ fLocalItemsUpdated++;
+ aSyncItemP->setSyncOp(sop_replace);
+ #ifdef OBJECT_FILTERING
+ if (!isAcceptable(aSyncItemP,aStatusCommand)) {
+ ok=false; // cannot be accepted
+ break;
+ }
+ #endif
+ // - get remoteid BEFORE processing item (as logicProcessRemoteItem consumes the item!!),
+ // in case replace is converted to add and we need to register a map entry.
+ remoteid=aSyncItemP->getRemoteID(); // get remote ID
+ remainsvisible=true; // should remain visible
+ ok=logicProcessRemoteItem(aSyncItemP,aStatusCommand,remainsvisible,&localid); // replace in local database NOW
+ // if added (not replaced), we need to send map
+ if (aStatusCommand.getStatusCode()==201) {
+ // Note: logicProcessRemoteItem should NOT do an add if we have no remoteid, but return 404.
+ // The following check is just additional security.
+ // really added: remember map entry if server sent remoteID (normally, it won't for Replace)
+ if (!remoteid.empty()) {
+ // we can handle that, as remote sent us the remoteID we need to map it correctly
+ DEBUGPRINTFX(DBG_ADMIN+DBG_DETAILS,(
+ "engProcessRemoteItemAsClient: replace command for unknown localID but with remoteID -> adding new entry to fPendingAddMaps - localid='%s', remoteID='%s'",
+ localid.c_str(),
+ remoteid.c_str()
+ ));
+ fPendingAddMaps[localid]=remoteid;
+ }
+ else {
+ // we cannot handle this (we shouldn't have, in logicProcessRemoteItem!!)
+ aStatusCommand.setStatusCode(510);
+ ok=false;
+ }
+ }
+ break; // fine, ok = irregularity status
+ default:
+ SYSYNC_THROW(TSyncException("Unknown sync op in TLocalEngineDS::processRemoteItemAsClient"));
+ } // switch
+ // processed
+ if (ok) OBJ_PROGRESS_EVENT(fSessionP->getSyncAppBase(),pev_itemprocessed,fDSConfigP,fLocalItemsAdded,fLocalItemsUpdated,fLocalItemsDeleted);
+ return ok;
+ }
+} // TLocalEngineDS::processRemoteItemAsClient
+
+
+
+// client case: called whenever outgoing Message of Sync Package starts
+void TLocalEngineDS::engClientStartOfSyncMessage(void)
+{
+ // is called for all local datastores, even inactive ones, so check state first
+ if (testState(dssta_dataaccessstarted) && !isAborted()) {
+ // only alerted non-sub datastores will start a <sync> command here, ONCE
+ // if there are pending maps, they will be sent FIRST
+ if (fRemoteDatastoreP) {
+ if (!testState(dssta_clientsyncgenstarted)) {
+ // shift state to syncgen started
+ // NOTE: if all sync commands can be sent at once,
+ // state will change again by issuing <sync>, so
+ // it MUST be changed here (not after issuing!)
+ changeState(dssta_clientsyncgenstarted,true);
+ // - make sure remotedatastore has correct full name
+ fRemoteDatastoreP->setFullName(getRemoteDBPath());
+ // an interrupted command at this point is a map command - continueIssue() will take care
+ if (!fSessionP->isInterrupedCmdPending() && numUnsentMaps()>0) {
+ // Check for pending maps from previous session (even in DS 1.0..1.1 case this is possible)
+ fUnconfirmedMaps.clear(); // no unconfirmed maps left...
+ fLastSessionMaps.clear(); // ...and no lastSessionMaps yet: all are in pendingMaps
+ // if this is a not-resumed slow sync, pending maps must not be sent, but discarded
+ if (isSlowSync() && !isResuming()) {
+ // forget the pending maps
+ PDEBUGPRINTFX(DBG_HOT,("There are %ld cached map entries from last Session, but this is a non-resumed slow sync: discard them",(long)numUnsentMaps()));
+ fPendingAddMaps.clear();
+ }
+ else {
+ // - now sending pending (cached) map commands from previous session
+ // Note: if map command was already started, the
+ // finished(), continueIssue() mechanism will make sure that
+ // more commands are generated. This mechanism will also make
+ // sure that outgoing package cannot get <final/> until
+ // map is completely sent.
+ // Note2: subdatastores do not generate their own map commands,
+ // but are called by superdatastore for contributing to united map
+ if (!isSubDatastore()) {
+ PDEBUGBLOCKFMT(("LastSessionMaps","Now sending cached map entries from last Session","datastore=%s",getName()));
+ TMapCommand *mapcmdP =
+ new TMapCommand(
+ fSessionP,
+ this, // local datastore
+ fRemoteDatastoreP // remote datastore
+ );
+ // issue
+ ISSUE_COMMAND_ROOT(fSessionP,mapcmdP);
+ PDEBUGENDBLOCK("LastSessionMaps");
+ }
+ }
+ }
+ // Now, send sync command (unless we are a subdatastore, in this case the superdatastore will take care)
+ if (!isSubDatastore()) {
+ // Note: if sync command was already started, the
+ // finished(), continueIssue() mechanism will make sure that
+ // more commands are generated
+ // Note2: subdatastores do not generate their own sync commands,
+ // but switch to dssta_client/serversyncgenstarted for contributing to united sync command
+ TSyncCommand *synccmdP =
+ new TSyncCommand(
+ fSessionP,
+ this, // local datastore
+ fRemoteDatastoreP // remote datastore
+ );
+ // issue
+ ISSUE_COMMAND_ROOT(fSessionP,synccmdP);
+ }
+ }
+ }
+ else {
+ PDEBUGPRINTFX(DBG_ERROR,("engClientStartOfSyncMessage can't start sync phase - missing remotedatastore"));
+ changeState(dssta_idle,true); // force to idle, nothing happened yet
+ }
+ } // if dsssta_dataaccessstarted and not aborted
+} // TLocalEngineDS::engClientStartOfSyncMessage
+
+
+// Client only: returns number of unsent map items
+sInt32 TLocalEngineDS::numUnsentMaps(void)
+{
+ return fPendingAddMaps.size();
+} // TLocalEngineDS::numUnsentMaps
+
+
+// Client only: called whenever outgoing Message starts that may contain <Map> commands
+// @param[in] aNotYetInMapPackage set if we are still in sync-from-server package
+// @note usually, client starts sending maps while still receiving syncops from server
+void TLocalEngineDS::engClientStartOfMapMessage(bool aNotYetInMapPackage)
+{
+ // is called for all local datastores, even inactive ones, so check state first
+ if (testState(dssta_syncgendone) && !isAborted()) {
+ // datastores that have finished generating their <sync>
+ // now can start a <map> command here, once (if not isInterrupedCmdPending(), that is, not a map already started)
+ if (fRemoteDatastoreP) {
+ // start a new map command if we don't have any yet and we are not done (dssta_clientmapssent) yet
+ if (!fSessionP->isInterrupedCmdPending() && !testState(dssta_clientmapssent)) {
+ // check if we should start one (that is, if the list is not empty)
+ if (numUnsentMaps()>0) {
+ // - now sending map command
+ // NOTE: if all map commands can be sent at once,
+ // fState will be modified again by issuing <map>, so
+ // it MUST be set here (not after issuing!)
+ // - data access done only if we are in Map package now
+ if (!aNotYetInMapPackage)
+ changeState(dssta_dataaccessdone); // usually already set in engEndOfSyncFromRemote(), but does not harm here
+ // Note: if map command was already started, the
+ // finished(), continueIssue() mechanism will make sure that
+ // more commands are generated. This mechanism will also make
+ // sure that outgoing package cannot get <final/> until
+ // map is completely sent.
+ // Note2: subdatastores do not generate their own map commands,
+ // but superdatastore calls their generateMapItem for contributing to united map
+ if (!isSubDatastore()) {
+ TMapCommand *mapcmdP =
+ new TMapCommand(
+ fSessionP,
+ this, // local datastore
+ fRemoteDatastoreP // remote datastore
+ );
+ // issue
+ ISSUE_COMMAND_ROOT(fSessionP,mapcmdP);
+ }
+ }
+ else {
+ // we need no map items now, but if this is still sync-from-server-package,
+ // we are not done yet
+ if (aNotYetInMapPackage) {
+ DEBUGPRINTFX(DBG_PROTO,("No map command need to be generated now, but still in <sync> from server package"));
+ }
+ else {
+ // we are done now
+ DEBUGPRINTFX(DBG_PROTO,("All map commands sending complete"));
+ changeState(dssta_clientmapssent);
+ }
+ }
+ }
+ } // if fRemoteDataStoreP
+ } // if >=dssta_syncgendone
+} // TLocalEngineDS::engClientStartOfMapMessage
+
+
+
+// called to mark maps confirmed, that is, we have received ok status for them
+void TLocalEngineDS::engMarkMapConfirmed(cAppCharP aLocalID, cAppCharP aRemoteID)
+{
+ // As this is client-only, we don't need to check for tempGUIDs here.
+ // Note: superdatastore has an implementation which dispatches by prefix
+ TStringToStringMap::iterator pos=fUnconfirmedMaps.find(aLocalID);
+ if (pos!=fUnconfirmedMaps.end()) {
+ DEBUGPRINTFX(DBG_ADMIN+DBG_EXOTIC,(
+ "engMarkMapConfirmed: deleting confirmed entry localID='%s', remoteID='%s' from fUnconfirmedMaps",
+ aLocalID,
+ aRemoteID
+ ));
+ // move it to lastsessionmap
+ fLastSessionMaps[(*pos).first]=(*pos).second;
+ // remove it from unconfirmed map
+ fUnconfirmedMaps.erase(pos);
+ }
+} // TLocalEngineDS::engMarkMapConfirmed
+
+
+
+// Check if the remoteid was used by an add command not
+// fully mapped&confirmed in the previous session
+bool TLocalEngineDS::isAddFromLastSession(cAppCharP aRemoteID)
+{
+ TStringToStringMap::iterator pos;
+ TStringToStringMap *mapListP;
+
+ for (int i=0; i<3; i++) {
+ // determine next list to search
+ mapListP = i==0 ? &fPendingAddMaps : (i==1 ? &fUnconfirmedMaps : &fLastSessionMaps);
+ // search it
+ for (pos=mapListP->begin(); pos!=mapListP->end(); ++pos) {
+ if (strcmp((*pos).second.c_str(),aRemoteID)==0)
+ return true; // remoteID known -> is add from last session
+ }
+ }
+ // not found in any of the lists
+ return false;
+} // TLocalEngineDS::isAddFromLastSession
+
+
+
+// - called to generate Map items
+// Returns true if now finished for this datastore
+// also sets fState to dss_done when finished
+bool TLocalEngineDS::engGenerateMapItems(TMapCommand *aMapCommandP)
+{
+ #ifdef USE_SML_EVALUATION
+ sInt32 leavefree = fSessionP->getNotUsableBufferBytes();
+ #else
+ sInt32 freeroom = fSessionP->getFreeCommandSize();
+ #endif
+
+ TStringToStringMap::iterator pos=fPendingAddMaps.begin();
+ PDEBUGBLOCKFMT(("MapGenerate","Generating Map items...","datastore=%s",getName()));
+ do {
+ // check if already done
+ if (pos==fPendingAddMaps.end()) break; // done
+ // add item
+ string locID = (*pos).first;
+ dsFinalizeLocalID(locID); // make sure we have the permanent version in case datastore implementation did deliver temp IDs
+ aMapCommandP->addMapItem(locID.c_str(),(*pos).second.c_str());
+ // check if we could send this command
+ #ifdef USE_SML_EVALUATION
+ if (
+ (aMapCommandP->evalIssue(
+ fSessionP->peekNextOutgoingCmdID(),
+ fSessionP->getOutgoingMsgID()
+ ) // what is left in buffer after sending Map so far
+ >=leavefree) // all this must still be more than what we MUST leave free
+ )
+ #else
+ if (freeroom>aMapCommandP->messageSize())
+ #endif
+ {
+ // yes, it should work
+ PDEBUGPRINTFX(DBG_PROTO,(
+ "Mapitem generated: localID='%s', remoteID='%s'",
+ locID.c_str(),
+ (*pos).second.c_str()
+ ));
+ // move sent ones to unconfirmed list
+ fUnconfirmedMaps[locID]=(*pos).second;
+ // remove item from to-be-sent list
+ TStringToStringMap::iterator temp_pos = pos++; // make copy and set iterator to next
+ fPendingAddMaps.erase(temp_pos); // now entry can be deleted (N.M. Josuttis, pg204)
+ }
+ else {
+ // no room for this item in this message, interrupt now
+ // - delete already added item again
+ aMapCommandP->deleteLastMapItem();
+ // - interrupt here
+ PDEBUGPRINTFX(DBG_PROTO,("Interrupted generating Map items because max message size reached"))
+ PDEBUGENDBLOCK("MapGenerate");
+ return false;
+ }
+ } while(true);
+ // done
+ // if we are dataaccessdone or more -> end of map phase for this datastore
+ if (testState(dssta_dataaccessdone)) {
+ changeState(dssta_clientmapssent,true);
+ PDEBUGPRINTFX(DBG_PROTO,("Finished generating Map items, server has finished <sync>, we are done now"))
+ }
+ #ifdef SYDEBUG
+ // else if we are not yet dssta_syncgendone -> this is the end of a early pending map send
+ else if (!dbgTestState(dssta_syncgendone)) {
+ PDEBUGPRINTFX(DBG_PROTO,("Finished sending cached Map items from last session"))
+ }
+ // otherwise, we are not really finished with the maps yet (but with the current map command)
+ else {
+ PDEBUGPRINTFX(DBG_PROTO,("Finished generating Map items for now, but server still sending <Sync>"))
+ }
+ #endif
+ PDEBUGENDBLOCK("MapGenerate");
+ return true;
+} // TLocalEngineDS::engGenerateMapItems
+
+
+#endif // SYSYNC_CLIENT
+
+// - called to mark an already generated (but probably not sent or not yet statused) item
+// as "to-be-resumed", by localID or remoteID (latter only in server case).
+// NOTE: This must be repeatable without side effects, as server must mark/save suspend state
+// after every request (and not just at end of session)
+void TLocalEngineDS::engMarkItemForResume(cAppCharP aLocalID, cAppCharP aRemoteID, bool aUnSent)
+{
+ #ifdef SUPERDATASTORES
+ // if we are acting as a subdatastore, aLocalID might be prefixed
+ if (fAsSubDatastoreOf && aLocalID) {
+ // remove prefix
+ aLocalID = fAsSubDatastoreOf->removeSubDSPrefix(aLocalID, this);
+ }
+ #endif
+ #ifndef SYSYNC_CLIENT
+ // Now mark for resume
+ if (aLocalID && *aLocalID) {
+ // localID can be a translated localid at this point (for adds), so check for it
+ string localid=aLocalID;
+ obtainRealLocalID(localid);
+ logicMarkItemForResume(localid.c_str(), aRemoteID, aUnSent);
+ }
+ else
+ #endif
+ {
+ // localID not used or client (which never has tempGUIDs)
+ logicMarkItemForResume(aLocalID, aRemoteID, aUnSent);
+ }
+} // TLocalEngineDS::engMarkItemForResume
+
+
+// - called to mark an already generated (but probably not sent or not yet statused) item
+// as "to-be-resent", by localID or remoteID (latter only in server case).
+void TLocalEngineDS::engMarkItemForResend(cAppCharP aLocalID, cAppCharP aRemoteID)
+{
+ #ifdef SUPERDATASTORES
+ // if we are acting as a subdatastore, aLocalID might be prefixed
+ if (fAsSubDatastoreOf && aLocalID) {
+ // remove prefix
+ aLocalID = fAsSubDatastoreOf->removeSubDSPrefix(aLocalID, this);
+ }
+ #endif
+ // a need to resend is always a problem with the remote (either explicit non-ok status
+ // received or implicit like data size too big to be sent at all due to maxmsgsize or
+ // maxobjsize restrictions.
+ fRemoteItemsError++;
+ // now mark for resend
+ #ifndef SYSYNC_CLIENT
+ if (aLocalID && *aLocalID) {
+ // localID can be a translated localid at this point (for adds), so check for it
+ string localid=aLocalID;
+ obtainRealLocalID(localid);
+ logicMarkItemForResend(localid.c_str(), aRemoteID);
+ }
+ else
+ #endif
+ {
+ // localID not used or client (which never has tempGUIDs)
+ logicMarkItemForResend(aLocalID, aRemoteID);
+ }
+} // TLocalEngineDS::engMarkItemForResend
+
+
+
+
+
+// @brief save everything needed to resume later, in case we get suspended
+/// - Might be called multiple times during a session, must make sure every time
+/// that the status is correct, that is, previous suspend state is erased
+localstatus TLocalEngineDS::engSaveSuspendState(bool aAnyway)
+{
+ // only save here if not aborted already (aborting saves the state immediately)
+ // or explicitly requested
+ if (aAnyway || !isAborted()) {
+ // only save if DS 1.2 and supported by DB
+ if ((fSessionP->getSyncMLVersion()>=syncml_vers_1_2) && dsResumeSupportedInDB()) {
+ PDEBUGBLOCKDESC("SaveSuspendState","Saving state for suspend/resume");
+ // save alert state (if not explicitly prevented)
+ fResumeAlertCode=fPreventResuming ? 0 : fAlertCode;
+ if (fResumeAlertCode) {
+ if (fPartialItemState!=pi_state_save_outgoing) {
+ // ONLY if we have no request for saving an outgoing item state already,
+ // we eventually need to save a pending incoming item
+ // if there is an incompletely received item, let it update Partial Item (fPIxxx) state
+ // (if it is an item of this datastore, that is).
+ if (fSessionP->fIncompleteDataCommandP)
+ fSessionP->fIncompleteDataCommandP->updatePartialItemState(this);
+ }
+ // this makes sure that only ungenerated (but to-be generated) items will be
+ // marked for resume. Items that have been generated in this session (but might
+ // have been marked for resume in a previous session must no longer be marked
+ // after this call.
+ // This also includes saving state for a partially sent item so we could resume it (fPIxxx)
+ logicMarkOnlyUngeneratedForResume();
+ // then, we need to additionally mark those items for resume which have been
+ // generated, but not yet sent or sent but not received status so far.
+ fSessionP->markPendingForResume(this);
+ }
+ // let datastore make all this persistent
+ // NOTE: this must happen even if we have no suspend state here,
+ // as marked-for-resends need to be saved here as well.
+ localstatus sta=logicSaveResumeMarks();
+ if (sta!=LOCERR_OK) {
+ PDEBUGPRINTFX(DBG_ERROR,("Error saving suspend state with logicSaveResumeMarks(), status=%hd",sta));
+ }
+ PDEBUGENDBLOCK("SaveSuspendState");
+ return sta;
+ }
+ // resume not supported due to datastore or SyncML version<1.2 -> ok anyway
+ PDEBUGPRINTFX(DBG_PROTO,("SaveSuspendState not possible (SyncML<1.2 or not supported by DB)"));
+ }
+ return LOCERR_OK;
+} // TLocalEngineDS::engSaveSuspendState
+
+
+
+} // namespace sysync
+
+/* end of TLocalEngineDS implementation */
+
+// eof
diff --git a/src/sysync/localengineds.h b/src/sysync/localengineds.h
new file mode 100755
index 0000000..07d249e
--- /dev/null
+++ b/src/sysync/localengineds.h
@@ -0,0 +1,1125 @@
+/**
+ * @File localengineds.h
+ *
+ * @Author Lukas Zeller (luz@synthesis.ch)
+ *
+ * @brief TLocalEngineDS
+ * Abstraction of the local datastore - interface class to the
+ * sync engine.
+ *
+ * Copyright (c) 2001-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ * @Date 2005-09-15 : luz : created from localdatastore
+ */
+
+#ifndef LocalEngineDS_H
+#define LocalEngineDS_H
+
+// includes
+#include "configelement.h"
+#include "syncitem.h"
+#include "syncdatastore.h"
+#include "itemfield.h"
+// more includes after config classes definitions
+
+
+using namespace sysync;
+
+
+namespace sysync {
+
+extern const char * const conflictStrategyNames[];
+
+#ifdef SCRIPT_SUPPORT
+// publish as derivates might need it
+extern const TFuncTable DBFuncTable;
+#endif
+
+class TLocalEngineDS; // forward
+
+
+// datatype list entry
+class TAdditionalDataType
+{
+public:
+ TDataTypeConfig *datatypeconfig;
+ bool forRead;
+ bool forWrite;
+ TTypeVariantDescriptor variantDescP;
+ #ifndef NO_REMOTE_RULES
+ string remoteRuleMatch; // remote rule match string
+ #endif
+};
+
+// datatypes list
+typedef std::list<TAdditionalDataType> TAdditionalTypesList;
+
+
+#ifndef NO_REMOTE_RULES
+
+// rule match type list entry
+typedef struct {
+ TSyncItemType *itemTypeP;
+ cAppCharP ruleMatchString;
+} TRuleMatchTypeEntry;
+
+// container types
+typedef std::list<TRuleMatchTypeEntry> TRuleMatchTypesContainer; // contains rule match item types
+
+#endif
+
+
+// type support configuration of that database
+class TTypeSupportConfig : public TConfigElement
+{
+ typedef TConfigElement inherited;
+public:
+ TTypeSupportConfig(const char* aName, TConfigElement *aParentElement);
+ virtual ~TTypeSupportConfig();
+ // properties
+ // - preferred data types
+ TDataTypeConfig *fPreferredTx;
+ TTypeVariantDescriptor fPrefTxVariantDescP;
+ TDataTypeConfig *fPreferredRx;
+ TTypeVariantDescriptor fPrefRxVariantDescP;
+ // - preferred data type for blind and legacy mode sync attempts
+ TDataTypeConfig *fPreferredLegacy;
+ // - other data types
+ TAdditionalTypesList fAdditionalTypes;
+ #ifndef NO_REMOTE_RULES
+ // - types that are selected when matching with a remote rule
+ TAdditionalTypesList fRuleMatchTypes;
+ #endif
+ // public methods
+ virtual void clear();
+ #ifdef HARDCODED_CONFIG
+ // add type support
+ bool addTypeSupport(
+ cAppCharP aTypeName,
+ bool aForRead,
+ bool aForWrite,
+ bool aPreferred,
+ cAppCharP aVariant,
+ cAppCharP aRuleMatch=NULL
+ );
+ #endif
+protected:
+ // check config elements
+ #ifndef HARDCODED_CONFIG
+ virtual bool localStartElement(const char *aElementName, const char **aAttributes, sInt32 aLine);
+ #endif
+ virtual void localResolve(bool aLastPass);
+}; // TTypeSupportConfig
+
+
+#if defined(SCRIPT_SUPPORT) && defined(SYSYNC_CLIENT)
+// publish func table for chaining
+extern const TFuncTable ClientDBFuncTable;
+#endif
+
+// local datastore config
+class TLocalDSConfig: public TConfigElement
+{
+ typedef TConfigElement inherited;
+public:
+ TLocalDSConfig(const char* aName, TConfigElement *aParentElement);
+ virtual ~TLocalDSConfig();
+ // properties
+ // - ID to identify datastore instance in callbacks and derived
+ // classes implementing multiple interface variants (such as PalmDbDatastore)
+ // and to relate client (profile) target settings with datastores
+ uInt32 fLocalDBTypeID;
+ // - supported datatypes
+ TTypeSupportConfig fTypeSupport;
+ // - conflict strategy
+ TConflictResolution fConflictStrategy;
+ TConflictResolution fSlowSyncStrategy;
+ TConflictResolution fFirstTimeStrategy;
+ // - options
+ bool fReadOnly; // if set, datastore will not write any data (only maps) to local DB (but fake successful status to remote)
+ bool fReportUpdates; // if set(normal case), updates of server items will be sent to client (can be set to false for example for emails)
+ bool fResendFailing; // if set, items that receive a failure status from the remote will be resent in the next session (if DS 1.2 suspend marks supported by the DB)
+ bool fDeleteWins; // if set, in a replace/delete conflict the delete wins (also see DELETEWINS())
+ #ifndef SYSYNC_CLIENT
+ bool fTryUpdateDeleted; // if set, in a client update with server delete conflict, server tries to update the already deleted item (in case it is just invisible)
+ bool fAlwaysSendLocalID; // always send localID to clients (which gives them opportunity to remap IDs on Replace)
+ #endif
+ uInt32 fMaxItemsPerMessage; // if >0, limits the number of items sent per SyncML message (useful in case of slow datastores where collecting data might exceed client timeout)
+ #ifdef OBJECT_FILTERING
+ // filtering
+ // - filter applied to items coming from remote party, non-matching
+ // items will be rejected with 415 (unknown format) status
+ // (outgoing items will be made pass this filter with makePassFilter)
+ string fRemoteAcceptFilter;
+ // - filter will be applied to items read from the local database,
+ // IN ADDITION to eventually specified target address filtering
+ string fLocalDBFilterConf;
+ // - filter applied to incoming items with makePassFilter
+ // when there is no fSyncSetFilter or when applying fSyncSetFilter
+ // with makePassFilter still does not make item pass.
+ string fMakePassFilter;
+ // - filter to check if item is to be treated as "invisible",
+ // also applied to incoming items to make them invisible
+ // (archive-delete)
+ // %%%%% these notes are from earlier syncVisibility stuff that never
+ // %%%%% worked:
+ // - for delete items going to remote, fSyncVisibiliy=false means that Soft Delete should be
+ // - for delete items coming from remote, fSyncVisibility=false means that Archive delete is requested
+ string fInvisibleFilter;
+ // - filter applied to incoming items if they do pass fInvisibleFilter (i.e. would be invisible)
+ string fMakeVisibleFilter;
+ // - DS 1.2 Filter support (<filter> allowed in Alert, <filter-rx>/<filterCap> shown in devInf)
+ bool fDS12FilterSupport;
+ // - Set if date range support is available in this datastore
+ bool fDateRangeSupported;
+ #endif
+ #ifdef SCRIPT_SUPPORT
+ // General DB scripts
+ string fDBInitScript;
+ string fSentItemStatusScript;
+ string fReceivedItemStatusScript;
+ string fAlertScript;
+ string fDBFinishScript;
+ #ifdef SYSYNC_CLIENT
+ string fAlertPrepScript;
+ // function table
+ virtual const TFuncTable *getClientDBFuncTable(void) { return &ClientDBFuncTable; };
+ #endif // SYSYNC_CLIENT
+ #endif // SCRIPT_SUPPORT
+ #ifndef MINIMAL_CODE
+ // display name
+ string fDisplayName;
+ #endif
+ // public methods
+ // - create appropriate datastore from config, calls addTypeSupport as well
+ virtual TLocalEngineDS *newLocalDataStore(TSyncSession *aSessionP) = 0;
+ // - add type support to datatstore from config
+ virtual void addTypes(TLocalEngineDS *aDatastore, TSyncSession *aSessionP);
+ // - add (probably datastore-specific) limits such as MaxSize and NoTruncate to types
+ // (called by addTypes), usually derived as type limits come from DS implementation
+ virtual void addTypeLimits(TLocalEngineDS *aLocalDatastoreP, TSyncSession *aSessionP);
+ // - reset config to defaults
+ virtual void clear();
+protected:
+ // check config elements
+ #ifndef HARDCODED_CONFIG
+ virtual bool localStartElement(const char *aElementName, const char **aAttributes, sInt32 aLine);
+ #endif
+ virtual void localResolve(bool aLastPass);
+}; // TLocalDSConfig
+
+
+// datastore config list
+typedef std::list<TLocalDSConfig *> TLocalDSList;
+
+
+// forward
+class TAlertCommand;
+class TRemoteDataStore;
+class TSyncOpCommand;
+
+/// @brief Local Engine Datastore States
+/// @Note these states are strictly in sequence as they happen during a sync session, so
+/// we can use < and > comparisons to check stage of sync process
+typedef enum {
+ dssta_idle, ///< client&server, inactive, idle (after reset or creation)
+ dssta_clientparamset, ///< client only, client has received params for syncing (dsSetClientSyncParams())
+ dssta_adminready, ///< client&server, administration data ready (all last & next timestamps/anchors/ids/maps available)
+ dssta_clientsentalert, ///< client only, client has sent alert for sync to server
+ dssta_serveralerted, ///< server only, server has received alert for sync from client
+ dssta_serveransweredalert, ///< server only, server has sent status and response alert for client
+ dssta_clientalertstatused, ///< client only, client has received status for sync alert
+ dssta_clientalerted, ///< client only, client has received response alert from server
+ dssta_syncmodestable, ///< client&server, sync mode is now stable (all alerts and alert statuses are exchanged and processed), server is ready for early maps
+ dssta_dataaccessstarted, ///< client&server, user data access has started (e.g. loading of sync set in progress)
+ dssta_syncsetready, ///< client&server, sync set is ready and can be accessed with logicXXXX sync op calls
+ dssta_clientsyncgenstarted, ///< client only, generation of sync command(s) in client for server has started (eventually, we send some pending maps first)
+ dssta_serverseenclientmods, ///< server only, server has seen all client modifications now
+ dssta_serversyncgenstarted, ///< server only, generation of sync command(s) in server for client has started
+ dssta_syncgendone, ///< client&server, generation of sync command(s) is complete
+ dssta_dataaccessdone, ///< client&server, user data access has ended (no more user data reads or writes, but still maps)
+ dssta_clientmapssent, ///< client only, all maps have been sent to server
+ dssta_admindone, ///< client&server, administration data access done (all anchors, timestamps, maps and stuff saved)
+ dssta_completed, ///< client&server, sync session complete (not necessarily successfully, check isAborted()!), including showing logs etc.
+ numDSStates
+} TLocalEngineDSState;
+
+
+typedef enum {
+ pi_state_none, ///< no partial item
+ pi_state_loaded_incoming, ///< we have an incoming item loaded from admin data (but not changed)
+ pi_state_loaded_outgoing, ///< we have an outgoing item loaded from admin data (but not changed)
+ pi_state_save_incoming, ///< we must save the incoming item at next suspend
+ pi_state_save_outgoing ///< we must save the outgoing item at next suspend
+} TPartialItemState;
+
+
+} // end namespace sysync
+
+
+// additional includes here as they need config classes already defined
+#include "synccommand.h"
+#include "syncsession.h"
+
+
+namespace sysync {
+
+#ifdef SCRIPT_SUPPORT
+class TScriptContext;
+class TLDSfuncs;
+#endif
+
+// for engXXXX methods that need only be virtual in case we have superdatastores
+#ifdef SUPERDATASTORES
+ #define SUPERDS_VIRTUAL virtual
+ class TSuperDataStore; // forward declaration
+#else
+ #define SUPERDS_VIRTUAL
+ #define TSuperDataStore TLocalEngineDS // to allow parameter for engProcessSyncAlert()
+#endif
+
+/// @brief Local datastore engine and abstraction of actual implementation
+/// - session and commands only call non-virtual engXXXX members of this class to
+/// perform any operations on the datastore
+/// - to perform actual access logic (which might be implemented in different flavors),
+/// this class calls virtual (usually abstract) logicXXXX members, which are
+/// implemented in derived classes.
+class TLocalEngineDS: public TSyncDataStore
+{
+ typedef TSyncDataStore inherited;
+
+ friend class TLDSfuncs;
+ friend class TMFTypeFuncs;
+ friend class TSyncSession;
+ friend class TSyncCommand;
+ friend class TSyncOpCommand;
+ friend class TMultiFieldItemType;
+ friend class TTextProfileHandler;
+ #ifndef SYSYNC_CLIENT
+ friend class TSyncServer;
+ #else
+ friend class TSyncClient;
+ #endif
+ #ifdef SUPERDATASTORES
+ friend class TSuperDataStore;
+ #endif
+
+protected:
+ /// @name dsSyncState member fields defining the sync state of the datastore
+ //
+ /// @{
+ TLocalEngineDSState fLocalDSState; ///< internal state of the datastore sync process
+ localstatus fAbortStatusCode; ///< status code when engAbortDatastoreSync() was called
+ bool fLocalAbortCause; ///< flag signalling if abort cause was local or remote
+ bool fRemoteAddingStopped; ///< set when no more add commands should be sent to remote (e.g. device full)
+ localstatus fAlertCode; ///< alert code in use by this datastore (for scripts and for suspend)
+ /// @}
+
+ /// @name dsSyncMode member fields defining the sync mode of the datastore
+ /// @Note These get valid for the first time in dssta_clientinit (for client) or
+ /// dssta_serveralerted (for server), but might change again due to
+ /// detection of slowsync or resume until dssta_syncmodestable is reached
+ //
+ /// @{
+ TSyncModes fSyncMode; ///< basic Sync mode (twoway, fromclient, fromserver)
+ bool fForceSlowSync; ///< set if external reason wants to force a slow sync even if it is not needed
+ bool fSlowSync; ///< set if slow sync or refresh
+ bool fRefreshOnly; ///< set if local data is refreshed from remote only, that is, no local changes will be sent to remote (can be set independently of apparent fSyncMode)
+ bool fReadOnly; ///< if set, datastore will not write any user data (but fake successful status to remote)
+ bool fReportUpdates; ///< if NOT set, datastore will not report updates to client (e.g. for email)
+ bool fServerAlerted; ///< set if sync was server alerted
+ bool fResuming; ///< set if this is a resume of a previous session
+ #ifdef SUPERDATASTORES
+ TSuperDataStore *fAsSubDatastoreOf; ///< if set, this points to the superdatastore, and MUST NOT receive any commands from the session directly
+ #endif
+ /// @}
+
+ /// @name dsSavedAdmin administrative data (anchors, timestamps, maps) as saved or to-be-saved
+ /// @Note These will be loaded and saved be derived classes
+ /// @Note Some of these will be updated from resp. @ref dsCurrentAdmin members at distinct events (suspend, session end, etc.)
+ /// @Note Some of these will be updated during the session, but in a way that does NOT affect the anchoring of current/last session
+ //
+ /// @{
+ // - anchors, valid at dssta_adminready
+ string fLastRemoteAnchor; ///< last remote anchor (saved remote's next anchor in DB at end of last session)
+ string fLastLocalAnchor; ///< last local anchor (saved local next anchor in DB at end of last session) @note this will be generated as
+ // - suspend state
+ uInt16 fResumeAlertCode; ///< alert code saved at suspend of previous session, 0 if none
+ bool fPreventResuming; ///< can be set by running session to prevent that session can be resumed (e.g. in case of incomplete zapping at "reload device")
+ // - other state info
+ bool fFirstTimeSync; ///< set to true if this is the first sync
+ // - item ID lists (maps)
+ #ifndef SYSYNC_CLIENT
+ TStringToStringMap fTempGUIDMap; ///< container for temp GUID to real GUID mapping
+ #else
+ TStringToStringMap fPendingAddMaps; ///< container for map items to be sent to server: fPendingAddMaps[localid]=remoteid; Note: might contain temporary localIDs that must be converted to final ones before using in <map> or saving with dsFinalizeLocalID()
+ TStringToStringMap fUnconfirmedMaps; ///< container for map items already sent to server, but not yet confirmed: fUnconfirmedMaps[localid]=remoteid;
+ TStringToStringMap fLastSessionMaps; ///< container for map items already confirmed, but still needed for duplicate checking:: fLastSessionMaps[localid]=remoteid;
+ #endif
+ // - information about last item sent or received in previously suspended session
+ TSyError fLastItemStatus; ///< final status sent for last item in previously suspended session.
+ string fLastSourceURI; ///< Source LocURI of last item sent in previously suspended session.
+ string fLastTargetURI; ///< Target LocURI of partial item
+ // - resuming chunked item data (send or receive)
+ TPartialItemState fPartialItemState; ///< state of partial item vars
+ uInt32 fPITotalSize; ///< total size of the partially transmitted item, 0 if none
+ uInt32 fPIUnconfirmedSize; ///< size of already received, but probably repeated data - or size of data pending to be (re-)sent
+ uInt32 fPIStoredSize; ///< total size of stored data (some of it might not be confirmed)
+ appPointer fPIStoredDataP; ///< stored data, allocated with smlLibMalloc().
+ bool fPIStoredDataAllocated; ///< set if stored data was allocated by the session. Otherwise, it is owned by the currently incomplete command
+
+ /// @}
+
+ /// @name dsCurrentAdmin current session's admin data (anchors, timestamps, maps)
+ /// @Note These will be copied to @ref dsSavedAdmin members ONLY when a session completes successfully/suspends.
+ /// @Note Admin data is NEVER directly saved or loaded from these
+ /// @Note Derivates will update some of these at dssta_adminready with current time/anchor values
+ //
+ /// @{
+
+ // - anchors, valid at dssta_adminready
+ string fNextRemoteAnchor; ///< next remote anchor (sent at Alert by remote)
+ string fNextLocalAnchor; ///< local anchor of this session
+ /// @}
+
+ /// @name dsFiltering item and syncset filtering
+ /// @Note syncset restriction filtering expressions and limits
+ /// must be evaluated by derived datastore implementations
+ //
+ /// @{
+ #ifdef OBJECT_FILTERING
+ /// dynamic filter, if set and the datastore cannot handle it directly, all records need to be read
+ /// this filter is good for filter expressions that might change between sync sessions
+ string fSyncSetFilter;
+ /// static filter, only applied to already read items, no need to filter all items
+ string fLocalDBFilter;
+ #ifdef SYNCML_TAF_SUPPORT
+ /// real SyncML-like Target Address Filtering (TAF), set from <sync> commands
+ string fTargetAddressFilter;
+ /// internal SyncML-like Target Address Filtering (TAF), set internally by scripts
+ string fIntTargetAddressFilter;
+ #endif // SYNCML_TAF_SUPPORT
+ #ifdef SYSYNC_TARGET_OPTIONS
+ // Date range
+ lineartime_t fDateRangeStart; ///< date range start, 0=none, must be evaluated by derived datastore implementations
+ lineartime_t fDateRangeEnd; ///< date range end, 0=none, must be evaluated by derived datastore implementations
+ /// size limit, -1=none, must be evaluated by derived datastore implementations
+ fieldinteger_t fSizeLimit;
+ /// max item count, 0=none (for example for emails)
+ uInt32 fMaxItemCount;
+ /// no attachments (for example emails)
+ bool fNoAttachments;
+ /// datastore options, can be evaluated in scripts, obtained from /o() CGI in database path
+ string fDBOptions; // free database options
+ #endif // SYSYNC_TARGET_OPTIONS
+ // - filtering needs, initialized by initPostFetchFiltering()
+ bool fTypeFilteringNeeded;
+ bool fFilteringNeeded;
+ bool fFilteringNeededForAll;
+ #endif // OBJECT_FILTERING
+ /// @}
+
+ /// @name dsItemProcessing item processing parameters and options
+ /// @note these can be influenced by various script functions during the session
+ //
+ /// @{
+ /// conflict strategy for this datastore in this session
+ /// @note can be modified by datastoreinitscript
+ TConflictResolution fSessionConflictStrategy;
+ fieldinteger_t fItemSizeLimit; ///< size limit for item being processed or generated (initally=fSizeLimit) but can be changed by scripts
+ TSyncOperation fEchoItemOp; ///< if not sop_none, processed item will be echoed back to remote
+ TSyncOperation fCurrentSyncOp; ///< current sync-operation
+ TConflictResolution fItemConflictStrategy; ///< conflict strategy for currently processed item
+ bool fForceConflict; ///< if set, a conflict will be forced
+ bool fDeleteWins; ///< if set, in a replace/delete conflict delete will win (regardless of strategy)
+ bool fPreventAdd; ///< if set, attempt to add item from remote will cause no add but delete of remote item
+ bool fIgnoreUpdate; ///< if set, attempt to update item from remote will be ignored (only adds, also implicit ones) are executed)
+ sInt16 fRejectStatus; ///< if >=0, incoming item will be discarded with this status code (0=silently)
+ #ifdef SCRIPT_SUPPORT
+ /// @{ Type Script support (actual scripts are in the datatypes, but contexts are here as
+ /// same datatype can be used in parallel in different datastores)
+ TScriptContext *fSendingTypeScriptContextP;
+ TScriptContext *fReceivingTypeScriptContextP; ///< @note can be NULL if sending and receiving uses the same context
+ /// @}
+ /// @{ generic datastore-level scripts
+ TScriptContext *fDataStoreScriptContextP; // for executing status check scripts
+ /// @}
+ #endif
+ /// @}
+
+
+ /// @name dsCountStats item counting and statistics
+ /// @{
+ //
+ /// number of changes remote will send (valid if >=0), SyncML 1.1
+ sInt32 fRemoteNumberOfChanges;
+ /// @{ net data transferred (item data, without any SyncML protocol overhead, meta etc.)
+ sInt32 fIncomingDataBytes;
+ sInt32 fOutgoingDataBytes;
+ /// }@
+ /// @{ locally performed ops
+ sInt32 fLocalItemsAdded;
+ sInt32 fLocalItemsUpdated;
+ sInt32 fLocalItemsDeleted;
+ sInt32 fLocalItemsError; // items that caused an error to be sent to the remote (and SHOULD be sent again by the remote, or abort the session)
+ /// }@
+ // @{ remotely performed ops
+ sInt32 fRemoteItemsAdded;
+ sInt32 fRemoteItemsUpdated;
+ sInt32 fRemoteItemsDeleted;
+ sInt32 fRemoteItemsError; // items that had a remote error (and will be resent later)
+ /// }@
+ #ifndef SYSYNC_CLIENT
+ // @{ conflicts
+ sInt32 fConflictsServerWins;
+ sInt32 fConflictsClientWins;
+ sInt32 fConflictsDuplicated;
+ /// slow sync matches
+ sInt32 fSlowSyncMatches;
+ #endif
+ /// @{ item transmission counts
+ sInt32 fItemsSent;
+ sInt32 fItemsReceived;
+ /// }@
+ /// @}
+
+private:
+ /// @name dsOther other members needed for handling the sync
+ /// @{
+ /// datastore config pointer
+ TLocalDSConfig *fDSConfigP;
+ #ifdef SYSYNC_CLIENT
+ // parameters for syncing with remote DB as client
+ // - local
+ string fLocalDBPath; ///< client, entire path to the local database, which might include subpaths/CGI to subselect records in the local DB
+ // - remote
+ string fRemoteDBPath; ///< client, path of remote DB, including all CGI params etc.
+ string fDBUser; ///< client, DB layer user name
+ string fDBPassword; ///< client, DB layer password
+ string fRemoteRecordFilterQuery; ///< client, record level filter query
+ bool fRemoteFilterInclusive; ///< client, inclusive filter flag
+ #else
+ #ifndef MINIMAL_CODE
+ string fRemoteDBPath; ///< server, path of remote DB, for documentary purposes
+ #endif
+ #endif
+ /// remote datastore involved, valid after processSyncCmdAsServer()
+ TRemoteDataStore *fRemoteDatastoreP;
+ /// Remote view if local URI
+ /// - Server case: URI how remote has accessed local, for sending Sync commands back and to
+ /// create devInf datastore name when devInf is queried AFTER being alerted from client
+ /// - Client case: URI of local DB (only used to match commands coming back)
+ string fRemoteViewOfLocalURI;
+ /// time when datastore terminated sync, for statistics only
+ lineartime_t fEndOfSyncTime;
+ // default types of remote datastore for sending/receiving during Sync
+ TSyncItemType *fRemoteReceiveFromLocalTypeP; ///< type used by remote to receive from local
+ TSyncItemType *fRemoteSendToLocalTypeP; ///< type used by remote to send to local
+ TSyncItemType *fLocalSendToRemoteTypeP; ///< type used by local to send to remote
+ TSyncItemType *fLocalReceiveFromRemoteTypeP; ///< type used by local to receive from remote
+ /// @}
+
+private:
+ /// reset for re-use without re-creation, used by constructor as well
+ void InternalResetDataStore(void);
+public:
+ /// constructor
+ TLocalEngineDS(TLocalDSConfig *aDSConfigP, TSyncSession *aSessionP, cAppCharP aName, uInt32 aCommonSyncCapMask=0);
+ /// destructor
+ virtual ~TLocalEngineDS();
+
+ /// @name dsProperty property and state querying methods
+ /// @{
+ #ifndef MINIMAL_CODE
+ // - return display name (descriptive)
+ virtual const char *getDisplayName(void)
+ { return fDSConfigP->fDisplayName.empty() ? getName() : fDSConfigP->fDisplayName.c_str(); };
+ #endif
+ TLocalEngineDSState getDSState(void) { return fLocalDSState; }
+ #ifdef SYDEBUG
+ cAppCharP getDSStateName(void);
+ static cAppCharP getDSStateName(TLocalEngineDSState aState);
+ bool dbgTestState(TLocalEngineDSState aMinState, bool aNeedExactMatch=false)
+ { return aNeedExactMatch ? fLocalDSState==aMinState : fLocalDSState>=aMinState; };
+ #endif
+ /// calculate Sync mode and flags from given alert code
+ /// @note does not affect DS state in any way, nor checks if DS can handle this code.
+ /// (this will be done only when calling setSyncMode())
+ static localstatus getSyncModeFromAlertCode(TSyError aAlertCode, TSyncModes &aSyncMode, bool &aIsSlowSync, bool &aIsServerAlerted);
+ /// determine if this is a first time sync situation
+ bool isFirstTimeSync(void) { return fFirstTimeSync; };
+ /// check if sync is started (e.g. background loading of syncset)
+ virtual bool isStarted(bool aWait) { return fLocalDSState>=dssta_dataaccessstarted; }; // single thread version is started when data access is started (and never waits)
+ // called for >=SyncML 1.1 if remote wants number of changes.
+ // Must return -1 if no NOC value can be returned
+ virtual sInt32 getNumberOfChanges(void) { return -1; /* no NOC supported */ };
+ /// test abort status, datastore is aborted also when session is just suspended
+ bool isAborted(void);
+ /// abort status code with local error code prefix if cause was local
+ TSyError getAbortStatusCode(void) { return fLocalAbortCause ? localError(fAbortStatusCode) : syncmlError(fAbortStatusCode); };
+ /// test if active
+ bool isActive(void) { return fLocalDSState!=dssta_idle && !isAborted(); }; // test active-in-sync status
+ /// Returns true if this datastore is done with <sync> commands (but not necessarily done with maps)
+ virtual bool isSyncDone(void);
+ lineartime_t getEndOfSyncTime() { return fEndOfSyncTime; }; // documentary only!
+ // - sync mode
+ TSyncModes getSyncMode(void) { return fSyncMode; }; ///< get sync mode
+ bool isSlowSync(void) { return fSlowSync; }; ///< true if slow sync
+ bool isResuming(void) { return fResuming; }; ///< true if resuming a previous session
+ bool isRefreshOnly(void) { return fRefreshOnly; }; ///< true if only refreshing with data from remote (no send to remote)
+ bool isReadOnly(void) { return fReadOnly; }; ///< true if only reading from local datastore (and ignoring any updates from remote)
+ /// get remote datastore related to this datastore
+ TRemoteDataStore *getRemoteDatastore(void) { return fRemoteDatastoreP; };
+ /// return remote view of local URI (might be different from what we might think it is locally)
+ cAppCharP getRemoteViewOfLocalURI(void) { return fRemoteViewOfLocalURI.c_str(); };
+ /// check if this datastore is accessible with given URI
+ /// @note By default, local datastore type is addressed with
+ /// first path element of URI, rest of path might be used
+ /// by derivates to subselect data folders etc.
+ /// @note returns number of characters matched to allow searching to
+ /// detect "better" matches (Oracle server case with /Calendar AND /Calendar/Events)
+ virtual uInt16 isDatastore(const char *aDatastoreURI);
+ /// returns true if this datastore is currently in use as subdatastore of a superdatastore
+ #ifdef SUPERDATASTORES
+ bool isSubDatastore(void) { return fAsSubDatastoreOf!=NULL; };
+ #else
+ bool isSubDatastore(void) { return false; };
+ #endif
+ // - data type information
+ TSyncItemType *getLocalSendType(void) { return fLocalSendToRemoteTypeP; }; ///< type used by local to send to remote
+ TSyncItemType *getLocalReceiveType(void) { return fLocalReceiveFromRemoteTypeP; }; ///< type used by local to receive from remote
+ TSyncItemType *getRemoteSendType(void) { return fRemoteSendToLocalTypeP; }; ///< type used by remote to send to local
+ TSyncItemType *getRemoteReceiveType(void) { return fRemoteReceiveFromLocalTypeP; }; ///< type used by remote to receive from local
+ #if defined(SYSYNC_CLIENT) || !defined(MINIMAL_CODE)
+ /// get remote DB path
+ cAppCharP getRemoteDBPath(void) { return fRemoteDBPath.c_str(); };
+ #endif
+ #ifdef SYSYNC_CLIENT
+ /// get local DB path
+ cAppCharP getLocalDBPath(void) { return fLocalDBPath.c_str(); };
+ #endif
+ /// get datastore config pointer
+ TLocalDSConfig *getDSConfig(void) { return fDSConfigP; };
+ /// datastore options
+ fieldinteger_t getItemSizeLimit(void) { return fItemSizeLimit; };
+ bool getNoAttachments(void) {
+ #ifdef SYSYNC_TARGET_OPTIONS
+ return fNoAttachments;
+ #else
+ return false;
+ #endif
+ };
+
+ /// @}
+
+ /// @name engXXXX methods defining the interface to other engine components (session, commands)
+ /// Only these will be called by the engine - the engine will no longer call directly into logicXXXX
+ /// @Note some of these are virtuals ONLY for being derived by superdatastore, NEVER by locic or other derivates
+ /// We use the SUPERDS_VIRTUAL macro for these, which is empty in case we don't have superdatastores, then
+ /// these can be non-virtual.
+ /// @{
+ //
+ /// reset datastore, but does not end sync nicely
+ /// @note is virtual because derived from TSyncDataStore
+ virtual void engResetDataStore(void);
+ /// flag that sync with this datastore is aborted. Datastore is not yet terminated by this.
+ /// @note calling engAbortDatastoreSync should not cause large activities - all terminating
+ /// activity should be in engTerminateSync(), which will be called later
+ virtual void engAbortDataStoreSync(TSyError aReason, bool aLocalProblem, bool aResumable=true);
+ /// Finish all activity with this datastore
+ /// @note functionality is that of former TLocalDataStore::endOfSync()
+ /// @note if not yet aborted with other reason, datastore now gets aborted with given aErrorStatus
+ /// @note the datastore should perform all activity needed to end the sync
+ SUPERDS_VIRTUAL void engFinishDataStoreSync(localstatus aErrorStatus=LOCERR_OK);
+ /// Terminate all activity with this datastore
+ /// @note may be called repeatedly, relevant shutdown code is executed only once
+ void engTerminateDatastore(localstatus aAbortStatusCode=408); // default to timeout
+ /// called at end of incoming message for all localEngineDS (super or not)
+ /// @note is not virtual, as superdatastore MUST NOT implement it
+ void engEndOfMessage(void) { dsEndOfMessage(); }; // simply call protected virtual chain
+ /// force slow sync
+ void engForceSlowSync(void) { fForceSlowSync=true; };
+ /// set refresh only mode (do not send to remote)
+ void engSetRefreshOnly(bool b) { fRefreshOnly=b; };
+ /// set read only mode (do not receive from remote)
+ void engSetReadOnly(bool b) { fReadOnly=b; };
+ /// can be called to avoid further ADD commands to be sent to remote (device full case, e.g.)
+ void engStopAddingToRemote(void) { fRemoteAddingStopped=true; };
+ /// can be called to prevent that current session can be resumed later (e.g. when user aborts zapping datastore in client)
+ void preventResuming(void) { fPreventResuming=true; }
+ #ifdef OBJECT_FILTERING
+ /// returns true if DB implementation can filter the standard filters
+ /// (LocalDBFilter, TargetFilter and InvisibleFilter) during database fetch -
+ /// otherwise, fetched items will be filtered after being read from DB.
+ /// Defined as abstract here to make sure derived datastores do implement filtering
+ SUPERDS_VIRTUAL bool engFilteredFetchesFromDB(bool aFilterChanged=false) { return dsFilteredFetchesFromDB(aFilterChanged); };
+ #endif
+ // called for >=SyncML 1.1 if remote wants number of changes.
+ // Must return -1 if no NOC value can be returned
+ SUPERDS_VIRTUAL sInt32 engGetNumberOfChanges(void) { return getNumberOfChanges(); };
+ /// Set remote datastore for local datastore
+ /// (must be called before engProcessSyncCmd and engGenerateSyncCmd)
+ SUPERDS_VIRTUAL void engSetRemoteDatastore(TRemoteDataStore *aRemoteDatastoreP);
+
+ #ifdef SYSYNC_CLIENT
+ /// initialize Sync alert for datastore according to Parameters set with dsSetClientSyncParams()
+ /// @note initializes anchors and makes calls to isFirstTimeSync() valid
+ localstatus engPrepareClientSyncAlert(TSuperDataStore *aAsSubDatastoreOf);
+ /// generate Sync alert for datastore
+ /// @note this could be repeatedly called due to auth failures at beginning of session
+ /// @note this is a NOP for subDatastores (should not be called in this case, anyway)
+ localstatus engGenerateClientSyncAlert(TAlertCommand *&aAlertCommandP);
+ // Init engine for client sync
+ // - determine types to exchange
+ // - make sync set ready
+ localstatus engInitForClientSync(void);
+ #endif
+ /// Internal events during sync for derived classes
+ /// @note local DB authorisation must be established already before calling these
+ /// - cause loading of all session anchoring info and other admin data (logicMakeAdminReady())
+ /// fLastRemoteAnchor,fLastLocalAnchor,fNextLocalAnchor; isFirstTimeSync() will be valid after the call
+ /// - in case of superdatastore, consolidates the anchor information from the subdatastores
+ SUPERDS_VIRTUAL localstatus engInitSyncAnchors(
+ cAppCharP aDatastoreURI, ///< local datastore URI
+ cAppCharP aRemoteDBID ///< ID of remote datastore (to find session information in local DB)
+ );
+ /// process Sync alert for a datastore
+ SUPERDS_VIRTUAL TAlertCommand *engProcessSyncAlert(
+ TSuperDataStore *aAsSubDatastoreOf, ///< if acting as subdatastore, this is the pointer to the superdatastore
+ uInt16 aAlertCode, ///< the alert code
+ const char *aLastRemoteAnchor, ///< last anchor of client
+ const char *aNextRemoteAnchor, ///< next anchor of client
+ const char *aTargetURI, ///< target URI as sent by remote, no processing at all
+ const char *aIdentifyingTargetURI, ///< target URI that was used to identify datastore
+ const char *aTargetURIOptions, ///< option string contained in target URI
+ SmlFilterPtr_t aTargetFilter, ///< DS 1.2 filter, NULL if none
+ const char *aSourceURI, ///< source URI
+ TStatusCommand &aStatusCommand ///< status that might be modified
+ );
+ /// process status received for sync alert
+ SUPERDS_VIRTUAL bool engHandleAlertStatus(TSyError aStatusCode);
+ /// parse target address options
+ /// @note this will be called BEFORE engInitForSyncOps()
+ localstatus engParseOptions(
+ const char *aTargetURIOptions, ///< option string contained in target URI
+ bool aFromSyncCommand=false ///< must be set when parsing options from <sync> target URI
+ );
+ /// process SyncML 1.2 style filter
+ localstatus engProcessDS12Filter(SmlFilterPtr_t aTargetFilter);
+ /// initialize reception of syncop commands for datastore
+ /// @note previously, this was implemented as initLocalDatastoreSync in syncsession
+ localstatus engInitForSyncOps(
+ const char *aRemoteDatastoreURI ///< URI of remote datastore
+ );
+ /// called from <sync> command to generate sync sub-commands to be sent to remote
+ /// Returns true if now finished for this datastore
+ /// also changes state to dssta_syncgendone when all sync commands have been generated
+ SUPERDS_VIRTUAL bool engGenerateSyncCommands(
+ TSmlCommandPContainer &aNextMessageCommands,
+ TSmlCommand * &aInterruptedCommandP,
+ const char *aLocalIDPrefix=NULL
+ );
+ #ifdef SYSYNC_CLIENT
+ /// client only: called whenever outgoing Message of Sync Package starts
+ /// @note should start a sync command for all alerted datastores
+ SUPERDS_VIRTUAL void engClientStartOfSyncMessage(void);
+ /// Client only: called whenever outgoing Message starts that may contain <Map> commands
+ /// @param[in] aNotYetInMapPackage set if we are still in sync-from-server package
+ /// @note usually, client starts sending maps while still receiving syncops from server
+ void engClientStartOfMapMessage(bool aNotYetInMapPackage);
+ /// called to generate Map items
+ /// @note Returns true if now finished for this datastore
+ /// @note also sets fState to dss_done when finished
+ SUPERDS_VIRTUAL bool generateMapItems(TMapCommand *aMapCommandP);
+ /// Client only: returns number of unsent map items
+ SUPERDS_VIRTUAL sInt32 numUnsentMaps(void);
+ #else
+ /// server only: called whenever we should start a sync command for all alerted datastores
+ /// if not already started
+ void engServerStartOfSyncMessage(void);
+ /// called to process map commands from client to server
+ /// @note aLocalID or aRemoteID can be NULL - which signifies deletion of a map entry
+ SUPERDS_VIRTUAL localstatus engProcessMap(cAppCharP aRemoteID, cAppCharP aLocalID);
+ #endif
+ /// check is datastore is completely started.
+ /// @param[in] aWait if set, call will not return until either started state is reached
+ /// or cannot be reached within the maximally allowed request processing time left.
+ SUPERDS_VIRTUAL bool engIsStarted(bool aWait) { return isStarted(aWait); };
+ /// SYNC command bracket start (check credentials if needed)
+ SUPERDS_VIRTUAL bool engProcessSyncCmd(
+ SmlSyncPtr_t aSyncP, // the Sync element
+ TStatusCommand &aStatusCommand, // status that might be modified
+ bool &aQueueForLater // will be set if command must be queued for later (re-)execution
+ );
+ /// SYNC command bracket end (but another might follow in next message)
+ SUPERDS_VIRTUAL bool engProcessSyncCmdEnd(bool &aQueueForLater);
+ /// called whenever Message of Sync Package ends or after last queued Sync command is executed
+ void engEndOfSyncFromRemote(bool aEndOfAllSyncCommands);
+ /// process SyncML SyncOp command for this datastore
+ /// @return true if regular processing (status can still be non-200!)
+ /// @note this routine does the common pre-processing like conversion to internal item format
+ /// and type checking. The actual work is then done by @ref engProcessRemoteItemAsClient
+ /// or @ref engProcessRemoteItemAsServer
+ bool engProcessSyncOpItem(
+ TSyncOperation aSyncOp, // the operation
+ SmlItemPtr_t aItemP, // the item to be processed
+ SmlMetInfMetInfPtr_t aMetaP, // command-wide meta, if any
+ TStatusCommand &aStatusCommand // pre-set 200 status, can be modified in case of errors
+ );
+
+ /// @{
+ /// process sync operation from remote with specified sync item
+ /// (according to current sync mode of local datastore)
+ /// @note
+ /// - returns true (and unmodified or non-200-successful status) if
+ /// operation could be processed regularily
+ /// - returns false (but probably still successful status) if
+ /// operation was processed with internal irregularities, such as
+ /// trying to delete non-existant item in datastore with
+ /// incomplete Rollbacks (which returns status 200 in this case!).
+ #ifdef SYSYNC_CLIENT
+ bool engProcessRemoteItemAsClient(
+ TSyncItem *aSyncItemP, ///< the item to be processed
+ TStatusCommand &aStatusCommand ///< status, must be set to correct status code (ok / error)
+ );
+ #else
+ bool engProcessRemoteItemAsServer(
+ TSyncItem *aSyncItemP, ///< the item to be processed
+ TStatusCommand &aStatusCommand ///< status, must be set to correct status code (ok / error)
+ );
+ #endif
+ SUPERDS_VIRTUAL bool engProcessRemoteItem(
+ TSyncItem *syncitemP,
+ TStatusCommand &aStatusCommand
+ );
+ /// handle status of sync operation
+ bool engHandleSyncOpStatus(TStatusCommand *aStatusCmdP,TSyncOpCommand *aSyncOpCmdP);
+ /// called to mark maps confirmed, that is, we have received ok status for them
+ #ifdef SYSYNC_CLIENT
+ SUPERDS_VIRTUAL void engMarkMapConfirmed(cAppCharP aLocalID, cAppCharP aRemoteID);
+ /// Client only: called to generate Map items
+ /// - Returns true if now finished for this datastore
+ /// - also sets fState to dss_done when finished
+ SUPERDS_VIRTUAL bool engGenerateMapItems(TMapCommand *aMapCommandP);
+ /// Client only: Check if the remoteid was used by an add command not
+ /// fully mapped&confirmed in the previous session
+ bool isAddFromLastSession(cAppCharP aRemoteID);
+ #endif
+ /// called to mark an already generated (but probably not sent or not yet statused) item
+ /// as "to-be-resumed", by localID or remoteID (latter only in server case).
+ /// @note This must be repeatable without side effects, as server must mark/save suspend state
+ /// after every request (and not just at end of session)
+ void engMarkItemForResume(cAppCharP aLocalID, cAppCharP aRemoteID, bool aUnSent);
+ // - called to mark an already generated (but probably not sent or not yet statused) item
+ // as "to-be-resent", by localID or remoteID (latter only in server case).
+ void engMarkItemForResend(cAppCharP aLocalID, cAppCharP aRemoteID);
+
+
+ // - Set types to be used for sending and receiving items
+ SUPERDS_VIRTUAL void setSendTypeInfo(
+ TSyncItemType *aLocalSendToRemoteTypeP,
+ TSyncItemType *aRemoteReceiveFromLocalTypeP
+ );
+ SUPERDS_VIRTUAL void setReceiveTypeInfo(
+ TSyncItemType *aLocalReceiveFromRemoteTypeP,
+ TSyncItemType *aRemoteSendToLocalTypeP
+ );
+ // - init usage of datatypes set with setSendTypeInfo/setReceiveTypeInfo
+ SUPERDS_VIRTUAL localstatus initDataTypeUse(void);
+ // "modern" filtering (in contrast to the "old" fSyncSetFilter et.al. stuff)
+ // - init filtering and check if needed (sets fFilteringNeeded and fFilteringNeededForAll)
+ void initPostFetchFiltering(void);
+ // - check if item passes filter and probably apply some modifications to it
+ bool postFetchFiltering(TSyncItem *aSyncItemP);
+ // add filter keywords and property names to filterCap
+ void addFilterCapPropsAndKeywords(SmlPcdataListPtr_t &aFilterKeywords, SmlPcdataListPtr_t &aFilterProps);
+ #ifdef OBJECT_FILTERING
+ /* %%% outdated
+ // - converts CGI-style filter string to internal filter string syntax
+ // and expands special functions
+ const char *filterCGIToString(cAppCharP aCGI, string &aFilter);
+ */
+ /// @brief parse "syncml:filtertype-cgi" filter, convert into internal filter syntax
+ /// and eventually sets some special filter options (fDateRangeStart, fDateRangeEnd)
+ /// based on "filterkeywords" available for the type passed (DS 1.2).
+ /// For parsing DS 1.1/1.0 TAF-style filters, aItemType can be NULL, no type-specific
+ /// filterkeywords can be parsed then.
+ /// @return pointer to next character after processing (usually points to terminator)
+ /// @param[in] aCGI the NUL-terminated filter string
+ /// @param[in] aItemTypeP if not NULL, this is the item type the filter applies to
+ const char *parseFilterCGI(cAppCharP aCGI, TSyncItemType *aItemTypeP, string &aFilter);
+ /// @brief check single filter term for DS 1.2 filterkeywords.
+ /// @return true if term still needs to be added to normal filter expression, false if term will be handled otherwise
+ virtual bool checkFilterkeywordTerm(
+ cAppCharP aIdent, bool aAssignToMakeTrue,
+ cAppCharP aOp, bool aCaseInsensitive,
+ cAppCharP aVal, bool aSpecialValue,
+ TSyncItemType *aItemTypeP
+ );
+ // - called to check if incoming item passes acception filters
+ bool isAcceptable(TSyncItem *aSyncItemP, TStatusCommand &aStatusCommand);
+ // - called to make incoming item pass sync set filtering
+ bool makePassSyncSetFilter(TSyncItem *aSyncItemP);
+ /// @brief called to make incoming item visible
+ /// @return true if now visible
+ bool makeVisible(TSyncItem *aSyncItemP);
+ /// @brief called to make incoming item INvisible
+ /// @return true if now INvisible
+ bool makeInvisible(TSyncItem *aSyncItemP);
+ #endif
+ // log datastore sync result
+ // - Called at end of sync with this datastore
+ virtual void dsLogSyncResult(void);
+ // add support for more data types
+ // (for programatically creating local datastores from specialized TSyncSession derivates)
+ void addTypeSupport(TSyncItemType *aItemTypeP,bool aForRx=true, bool aForTx=true);
+ #ifndef NO_REMOTE_RULES
+ // add data type that overrides normal type selection if string matches active remote rule
+ void addRuleMatchTypeSupport(TSyncItemType *aItemTypeP,cAppCharP aRuleMatchString);
+ #endif
+ // get usage variant for a specified type usage
+ virtual TTypeVariantDescriptor getVariantDescForType(TSyncItemType *aItemTypeP);
+ #ifndef SYSYNC_CLIENT
+ /// called at end of request processing in server (derived only by superdatastore)
+ SUPERDS_VIRTUAL void engRequestEnded(void);
+ /// end map operation (derived class might want to rollback)
+ bool MapFinishAsServer(
+ bool aDoCommit, // if not set, entire map operation must be undone
+ TStatusCommand &aStatusCommand // status, must be set on error or non-200-status
+ ) { return true; }
+ #endif
+ /// @}
+
+
+ /// @name dsXXXX (usually abstract) virtuals defining the interface to derived datastore classes (logic, implementation, api)
+ /// These are usually designed such that they should always call inherited::dsXXX to let the entire chain
+ /// of ancestors see the calls
+ /// @{
+ //
+public:
+ /// alert possible thread change
+ virtual void dsThreadMayChangeNow(void) {}; // nop at this level
+ #ifdef SYSYNC_CLIENT
+ /// set Sync Parameters. Derivates might override this to pre-process and modify parameters
+ /// (such as adding client settings as CGI to remoteDBPath)
+ virtual bool dsSetClientSyncParams(
+ TSyncModes aSyncMode,
+ bool aSlowSync,
+ const char *aRemoteDBPath,
+ const char *aDBUser = NULL,
+ const char *aDBPassword = NULL,
+ const char *aLocalPathExtension = NULL,
+ const char *aRecordFilterQuery = NULL,
+ bool aFilterInclusive = false
+ );
+ #endif
+protected:
+ /// reset datastore to a re-usable, like new-created state.
+ virtual void dsResetDataStore(void) {};
+ /// make sure datastore does not need agent any more.
+ /// @note Called while agent is still fully ok, so we must clean up such
+ /// that later call of destructor does NOT access agent any more
+ virtual void announceAgentDestruction(void) { engTerminateDatastore(); };
+ /// abort datastore (no reset yet, everything is just frozen as it is)
+ virtual void dsAbortDatastoreSync(TSyError aStatusCode, bool aLocalProblem) {}; // nop here
+ /// inform everyone of coming state change
+ virtual localstatus dsBeforeStateChange(TLocalEngineDSState aOldState,TLocalEngineDSState aNewState);
+ /// inform everyone of happened state change
+ virtual localstatus dsAfterStateChange(TLocalEngineDSState aOldState,TLocalEngineDSState aNewState);
+ #ifdef SYSYNC_CLIENT
+ /// finalize local ID (for datastores that can't efficiently produce these at insert)
+ virtual bool dsFinalizeLocalID(string &aLocalID) { return false; /* no change, ID is ok */};
+ #endif
+ #ifdef OBJECT_FILTERING
+ /// tests if DB implementation can filter the standard filters
+ /// (LocalDBFilter, TargetFilter and InvisibleFilter) during database fetch -
+ /// otherwise, fetched items will be filtered after being read from DB.
+ /// Defined as abstract here to make sure derived datastores do implement filtering
+ /// @param[in] aFilterChanged if true, this signals to the datastore that filter expression might have changed in between
+ virtual bool dsFilteredFetchesFromDB(bool aFilterChanged=false) { return false; }; // datastores can't do this by default
+ /// returns true if DB implementation can also apply special filters like CGI-options
+ /// /dr(x,y) etc. during fetching
+ virtual bool dsOptionFilterFetchesFromDB(void) { return false; }; // datastores can't do this by default
+ #endif
+ /// returns true if DB implementation supports resume (saving of resume marks, alert code, pending maps, tempGUIDs)
+ virtual bool dsResumeSupportedInDB(void) { return false; };
+ /// returns true if DB implementation supports resuming in midst of a chunked item (can save fPIxxx.. and related admin data)
+ virtual bool dsResumeChunkedSupportedInDB(void) { return false; };
+ /// called when a item in the sync set changes its localID (due to local DB internals)
+ /// Datastore must make sure that eventually cached items get updated
+ virtual void dsLocalIdHasChanged(const char *aOldID, const char *aNewID);
+ /// called when request processing ends
+ virtual void dsEndOfMessage(void) {}; // nop at this level
+ /// called at end of request processing in server
+ /// @note handling of suspend state saving and calling dsThreadMayChangeNow MUST NOT
+ /// be implemented here - engEndOfMessage() takes care of this
+ virtual void dsRequestEnded(void) {}; // nop at this level
+ /// called to confirm a sync operation's completion (ok status from remote received)
+ /// @note aSyncOp passed not necessarily reflects what was sent to remote, but what actually happened
+ virtual void dsConfirmItemOp(TSyncOperation aSyncOp, cAppCharP aLocalID, cAppCharP aRemoteID, bool aSuccess, localstatus aErrorStatus=0);
+ /// @}
+
+protected:
+ /// @name logicXXXX (usually abstract) virtuals defining the interface to the datastore logic implementation layer
+ /// These will never be called directly by the engine, but only from engXXX members of this class
+ /// @{
+ //
+ /// - might be called several times (auth retries at beginning of session)
+ virtual localstatus logicMakeAdminReady(cAppCharP aDataStoreURI, cAppCharP aRemoteDBID) { return LOCERR_NOTIMP; };
+ /// returns true if logic layer has running threads in the background
+ virtual bool logicHasBackgroundThreads(void) { return false; }; // single thread never has background
+ /// returns true if DB implementation cannot guarantee complete
+ /// rollback of all writes (e.g. if DB must commit at end of message)
+ /// @todo rollbacks are obsolete, kick this one out
+ virtual bool incompleteRollbacksPossible(void) { return false; }
+
+ /// get conflict resolution strategy.
+ virtual TConflictResolution getConflictStrategy(bool aForSlowSync, bool aForFirstTime=false);
+ #ifndef SYSYNC_CLIENT
+ /// called to check if conflicting replace command from server exists
+ virtual TSyncItem *getConflictingItemByRemoteID(TSyncItem *syncitemP) = 0;
+ /// called to check if content-matching item from server exists
+ virtual TSyncItem *getMatchingItem(TSyncItem *syncitemP, TEqualityMode aEqMode) = 0;
+ /// called to prevent item to be sent to client in subsequent engGenerateSyncCommands()
+ // item in question should be an item that was returned by getConflictingItemByRemoteID() or getMatchingItem()
+ virtual void dontSendItemAsServer(TSyncItem *syncitemP) = 0;
+ /// called to have additional item sent to remote (DB takes ownership of item)
+ virtual void SendItemAsServer(TSyncItem *aSyncitemP) = 0;
+ /// called for servers when receiving map from client
+ virtual localstatus logicProcessMap(cAppCharP aRemoteID, cAppCharP aLocalID) = 0;
+ #endif
+ /// called to process incoming item operation.
+ /// Method must take ownership of syncitemP in all cases
+ virtual bool logicProcessRemoteItem(
+ TSyncItem *syncitemP,
+ TStatusCommand &aStatusCommand,
+ bool &aVisibleInSyncset, // on entry: tells if resulting item SHOULD be visible; on exit: set if processed item remains visible in the sync set.
+ string *aGUID=NULL // GUID is stored here if not NULL
+ ) = 0;
+ /// called to read a specified item from the server DB (not restricted to set of conflicting items)
+ virtual bool logicRetrieveItemByID(
+ TSyncItem &aSyncItem, ///< item to be filled with data from server. Local or Remote ID must already be set
+ TStatusCommand &aStatusCommand ///< status, must be set on error or non-200-status
+ ) = 0;
+
+
+ #ifdef SYSYNC_CLIENT
+ /// client: called to generate sync sub-commands to be sent to server
+ /// Returns true if now finished for this datastore
+ /// also sets fState to dss_syncdone(server)/dss_syncready(client) when finished
+ virtual bool logicGenerateSyncCommandsAsClient(
+ TSmlCommandPContainer &aNextMessageCommands,
+ TSmlCommand * &aInterruptedCommandP,
+ const char *aLocalIDPrefix=NULL
+ ) = 0;
+ #else
+ /// server: called to generate sync sub-commands to be sent to client
+ /// Returns true if now finished for this datastore
+ /// also sets fState to dss_syncdone(server)/dss_syncready(client) when finished
+ virtual bool logicGenerateSyncCommandsAsServer(
+ TSmlCommandPContainer &aNextMessageCommands,
+ TSmlCommand * &aInterruptedCommandP,
+ const char *aLocalIDPrefix=NULL
+ ) = 0;
+ #endif
+ /// called to have all non-yet-generated sync commands as "to-be-resumed"
+ virtual void logicMarkOnlyUngeneratedForResume(void) = 0;
+ /// called to mark an already generated (but probably not sent or not yet statused) item
+ /// as "to-be-resumed", by localID or remoteID (latter only in server case).
+ /// @note This must be repeatable without side effects, as server must mark/save suspend state
+ /// after every request (and not just at end of session)
+ virtual void logicMarkItemForResume(cAppCharP aLocalID, cAppCharP aRemoteID, bool aUnSent) = 0;
+ /// called to mark an already sent item as "to-be-resent", e.g. due to temporary
+ /// error status conditions, by localID or remoteID (latter only in server case).
+ virtual void logicMarkItemForResend(cAppCharP aLocalID, cAppCharP aRemoteID) = 0;
+ /// save status information required to eventually perform a resume (as passed to datastore with
+ /// markOnlyUngeneratedForResume() and markItemForResume())
+ /// (or, in case the session is really complete, make sure that no resume state is left)
+ /// @note Must also save tempGUIDs (for server) and pending/unconfirmed maps (for client)
+ virtual localstatus logicSaveResumeMarks(void) { return LOCERR_NOTIMP; }; // must be derived (or avoided, as in superdatastore)
+ /// do log substitutions
+ virtual void DoLogSubstitutions(string &aLog,bool aPlaintext);
+ /// @}
+
+ /// @name dsHelpers private/protected helper routines
+ /// These will never be called directly by the engine, but only from engXXX members of this class
+ /// @{
+ //
+#ifdef SUPERDATASTORES
+protected:
+#else
+private:
+#endif
+ /// change datastore state, calls logic layer before and after change
+ localstatus changeState(TLocalEngineDSState aNewState, bool aForceOnError=false);
+ /// test datastore state for minimal or exact state
+ bool testState(TLocalEngineDSState aMinState, bool aNeedExactMatch=false);
+private:
+ /// get Alert code for current Sync State
+ uInt16 getSyncStateAlertCode(bool aServerAlerted=false, bool aClientMinimal=false);
+ /// initializes Sync state variables
+ localstatus setSyncMode(bool aAsClient, TSyncModes aSyncMode, bool aIsSlowSync, bool aIsServerAlerted);
+ /// initializes Sync state variables and returns error if alert is not supported
+ localstatus setSyncModeFromAlertCode(uInt16 aAlertCode, bool aAsClient);
+ /// check if aborted and if yes, set status with appropriate error code
+ bool CheckAborted(TStatusCommand &aStatusCommand);
+ /// called to show sync statistics in debug log and on console
+ SUPERDS_VIRTUAL void showStatistics(void);
+ /// Parse CGI option
+ #ifdef SYSYNC_TARGET_OPTIONS
+ cAppCharP parseOption(
+ const char *aOptName,
+ const char *aArguments,
+ bool aFromSyncCommand
+ );
+ #endif
+ #ifndef NO_REMOTE_RULES
+ /// rule match types container
+ TRuleMatchTypesContainer fRuleMatchItemTypes; // contains rule match item types
+ #endif
+protected:
+ #ifndef SYSYNC_CLIENT
+ /// for sending GUIDs (Add command), generate temp GUID which conforms to maxguidsize of remote datastore if needed
+ void adjustLocalIDforSize(string &aLocalID, sInt32 maxguidsize, sInt32 prefixsize);
+ /// for received GUIDs (Map command), obtain real GUID (might be temp GUID due to maxguidsize restrictions)
+ void obtainRealLocalID(string &aLocalID);
+ /// helper to force a conflict (i.e. have a particular item in the sync set)
+ TSyncItem *forceConflict(TSyncItem *aSyncItemP);
+ #endif
+ /// helper to save resume state either at end of request or explicitly at reception of a "suspend"
+ SUPERDS_VIRTUAL localstatus engSaveSuspendState(bool aAnyway);
+ /// Returns true if type information is sufficient to create items to be sent to remote party
+ bool canCreateItemForRemote(void) { return (fLocalSendToRemoteTypeP && fRemoteReceiveFromLocalTypeP); };
+ /// create SyncItem suitable for being sent from local to remote
+ TSyncItem *newItemForRemote(
+ uInt16 aExpectedTypeID ///< typeid of expected type
+ );
+ /// helper for derived classes to generate sync op commands
+ TSyncOpCommand *newSyncOpCommand(
+ TSyncItem *aSyncItemP, // the sync item
+ TSyncItemType *aSyncItemTypeP // the sync item type
+ );
+ /// return pure relative (item) URI (removes absolute part or ./ prefix)
+ /// @note this one is virtual because it is defined in TSyncDataStore
+ virtual cAppCharP DatastoreRelativeURI(cAppCharP aURI);
+ /// obtain new datastore info, returns NULL if none available
+ /// @note this one is virtual because it is defined in TSyncDataStore
+ virtual SmlDevInfDatastorePtr_t newDevInfDatastore(bool aAsServer, bool aWithoutCTCapProps);
+ /// obtain sync capabilities list for datastore
+ /// @note this one is virtual because it is defined in TSyncDataStore
+ virtual SmlDevInfSyncCapPtr_t newDevInfSyncCap(uInt32 aSyncCapMask);
+ /// analyze database name
+ void analyzeName(
+ const char *aDatastoreURI,
+ string *aBaseNameP,
+ string *aTableNameP=NULL,
+ string *aCGIP=NULL
+ );
+ /// @}
+}; // TLocalEngineDS
+
+} // namespace sysync
+
+#endif // LocalEngineDS_H
+
+// eof
diff --git a/src/sysync/mimediritemtype.cpp b/src/sysync/mimediritemtype.cpp
new file mode 100755
index 0000000..823fff9
--- /dev/null
+++ b/src/sysync/mimediritemtype.cpp
@@ -0,0 +1,395 @@
+/*
+ * File: mimediritemtype.cpp
+ *
+ * Author: Lukas Zeller (luz@synthesis.ch)
+ *
+ * TMimeDirItemType
+ * base class for MIME DIR based content types (vCard, vCalendar...)
+ *
+ * Copyright (c) 2001-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ * 2001-06-12 : luz : created
+ *
+ */
+
+// includes
+#include "prefix_file.h"
+#include "sysync.h"
+#include "mimediritemtype.h"
+#include "vtimezone.h"
+
+using namespace sysync;
+
+
+namespace sysync {
+
+// mime-DIR mode names
+const char * const mimeModeNames[numMimeModes] = {
+ "old",
+ "standard"
+};
+
+
+// enumeration modes
+const char * const EnumModeNames[numEnumModes] = {
+ "translate", // translation from value to name and vice versa
+ "prefix", // translation of prefix while copying rest of string
+ "defaultname", // default name when translating from value to name
+ "defaultvalue", // default value when translating from name to value
+ "ignore" // ignore value or name
+};
+
+
+// profile modes
+const char * const ProfileModeNames[numProfileModes] = {
+ "custom", // custom profile
+ "vtimezones", // VTIMEZONE profile(s), expands to a VTIMEZONE for every time zone referenced by convmode TZID fields
+};
+
+
+// Config
+// ======
+
+
+// MIMEDir-based datatype config
+
+TMIMEDirTypeConfig::TMIMEDirTypeConfig(const char* aName, TConfigElement *aParentElement) :
+ TMultiFieldTypeConfig(aName,aParentElement)
+{
+ clear();
+} // TMIMEDirTypeConfig::TMIMEDirTypeConfig
+
+
+TMIMEDirTypeConfig::~TMIMEDirTypeConfig()
+{
+ clear();
+} // TMIMEDirTypeConfig::~TMIMEDirTypeConfig
+
+
+// init defaults
+void TMIMEDirTypeConfig::clear(void)
+{
+ // clear inherited
+ inherited::clear();
+} // TMIMEDirTypeConfig::clear
+
+
+// init defaults
+void TMIMEDirTypeConfig::localResolve(bool aLastPass)
+{
+ if (aLastPass) {
+ // make a casted copy of the profile pointer
+ GET_CASTED_PTR(fMIMEProfileP,TMIMEProfileConfig,fProfileConfigP,DEBUGTEXT("TMIMEDirTypeConfig with non-TMIMEProfileConfig profile","mdit2"));
+ #ifdef CONFIGURABLE_TYPE_SUPPORT
+ if (!fMIMEProfileP)
+ SYSYNC_THROW(TConfigParseException("no 'mimeprofile' found"));
+ #endif
+ }
+ // resolve inherited
+ inherited::localResolve(aLastPass);
+} // TMIMEDirTypeConfig::localResolve
+
+
+
+// create Sync Item Type of appropriate type from config
+TSyncItemType *TMIMEDirTypeConfig::newSyncItemType(TSyncSession *aSessionP, TSyncDataStore *aDatastoreP)
+{
+ if (!fMIMEProfileP)
+ SYSYNC_THROW(TSyncException(DEBUGTEXT("TMIMEDirTypeConfig: no or wrong-typed profile","mdit1")));
+ return
+ new TMimeDirItemType(
+ aSessionP,
+ this,
+ fTypeName.c_str(),
+ fTypeVersion.c_str(),
+ aDatastoreP,
+ fMIMEProfileP->fFieldListP
+ );
+} // TMIMEDirTypeConfig::newSyncItemType
+
+
+// get a descriptor for selecting a variant of a datatype (if any), NULL=no variant with this name
+TTypeVariantDescriptor TMIMEDirTypeConfig::getVariantDescriptor(const char *aVariantName)
+{
+ // variants in MIME-Dir are currently level 1 subprofiles
+ // - we need it here already (we calculate it once again in Resolve)
+ GET_CASTED_PTR(fMIMEProfileP,TMIMEProfileConfig,fProfileConfigP,DEBUGTEXT("TMIMEDirTypeConfig with non-TMIMEProfileConfig profile","mdit2"));
+ const TProfileDefinition *subprofileP =
+ fMIMEProfileP->fRootProfileP->subLevels;
+ while (subprofileP) {
+ if (strucmp(aVariantName,TCFG_CSTR(subprofileP->levelName))==0) {
+ return (TTypeVariantDescriptor) subprofileP; // pointer to subProfile is our descriptor
+ }
+ // next
+ subprofileP=subprofileP->next;
+ }
+ // not found
+ return NULL;
+} // TMIMEDirTypeConfig::getVariantDescriptor
+
+
+#ifdef CONFIGURABLE_TYPE_SUPPORT
+
+// config element parsing
+bool TMIMEDirTypeConfig::localStartElement(const char *aElementName, const char **aAttributes, sInt32 aLine)
+{
+ // checking the elements
+ /* use profilemode instead
+ if (strucmp(aElementName,"mimedirmode")==0)
+ expectEnum(sizeof(fMimeDirMode),&fMimeDirMode,mimeModeNames,numMimeModes);
+ else
+ */
+ // - none known here
+ return TMultiFieldTypeConfig::localStartElement(aElementName,aAttributes,aLine);
+ // ok
+ return true;
+} // TMIMEDirTypeConfig::localStartElement
+
+#endif
+
+
+#pragma exceptions reset
+#undef EXCEPTIONS_HERE
+#define EXCEPTIONS_HERE TARGET_HAS_EXCEPTIONS
+
+
+/*
+ * Implementation of TMimeDirItemType
+ */
+
+
+TMimeDirItemType::TMimeDirItemType(
+ TSyncSession *aSessionP,
+ TDataTypeConfig *aTypeConfigP,
+ const char *aCTType,
+ const char *aVerCT,
+ TSyncDataStore *aRelatedDatastoreP,
+ TFieldListConfig *aFieldDefinitions // field definitions
+) :
+ TMultiFieldItemType(aSessionP,aTypeConfigP,aCTType,aVerCT,aRelatedDatastoreP,aFieldDefinitions)
+{
+ // create the profile handler
+ fProfileHandlerP = static_cast<TMimeDirProfileHandler *>(static_cast<TMIMEDirTypeConfig *>(aTypeConfigP)->fProfileConfigP->newProfileHandler(this));
+ // set profile mode
+ fProfileHandlerP->setProfileMode(static_cast<TMIMEDirTypeConfig *>(aTypeConfigP)->fProfileMode);
+ fReceivedFieldDefs=false;
+} // TMimeDirItemType::TMimeDirItemType
+
+
+TMimeDirItemType::~TMimeDirItemType()
+{
+ // release the profile handler
+ delete fProfileHandlerP;
+} // TMimeDirItemType::~TMimeDirItemType
+
+
+#ifdef OBJECT_FILTERING
+
+
+// get field index of given filter expression identifier.
+sInt16 TMimeDirItemType::getFilterIdentifierFieldIndex(const char *aIdentifier, uInt16 aIndex)
+{
+ // check if explicit field level identifier
+ if (strucmp(aIdentifier,"F.",2)==0) {
+ // explicit field identifier, skip property lookup
+ aIdentifier+=2;
+ }
+ else if (fProfileHandlerP) {
+ // let profile search for fields by profile-defined alternative names
+ sInt16 fid = fProfileHandlerP->getFilterIdentifierFieldIndex(aIdentifier, aIndex);
+ if (fid!=FID_NOT_SUPPORTED)
+ return fid;
+ }
+ // if no field ID found so far, look up in field list
+ return TMultiFieldItemType::getFilterIdentifierFieldIndex(aIdentifier, aIndex);
+} // TMimeDirItemType::getFilterIdentifierFieldIndex
+
+
+#endif
+
+
+
+// create new sync item of proper type and optimization for specified target
+TSyncItem *TMimeDirItemType::internalNewSyncItem(TSyncItemType *aTargetItemTypeP, TLocalEngineDS *aLocalDatastoreP)
+{
+ // All MimeDirs are stored in MultiFieldItems
+ TMultiFieldItemType *targetitemtypeP;
+ GET_CASTED_PTR(targetitemtypeP,TMultiFieldItemType,aTargetItemTypeP,DEBUGTEXT("TMimeDirItemType::internalNewSyncItem with bad-typed target","mdit6"));
+ MP_RETURN_NEW(TMultiFieldItem,DBG_OBJINST,"TMultiFieldItem",TMultiFieldItem(this,targetitemtypeP));
+} // TMimeDirItemType::internalNewSyncItem
+
+
+// fill in SyncML data (but leaves IDs empty)
+bool TMimeDirItemType::internalFillInData(
+ TSyncItem *aSyncItemP, // SyncItem to be filled with data
+ SmlItemPtr_t aItemP, // SyncML toolkit item Data to be converted into SyncItem (may be NULL if no data, in case of Delete or Map)
+ TLocalEngineDS *aLocalDatastoreP, // local datastore
+ TStatusCommand &aStatusCmd // status command that might be modified in case of error
+)
+{
+ // check type
+ TMultiFieldItem *itemP;
+ GET_CASTED_PTR(itemP,TMultiFieldItem,aSyncItemP,DEBUGTEXT("TMimeDirItemType::internalFillInData: incompatible item class","mdit7"));
+ // process data if any
+ if (aItemP->data) {
+ // set related datastore so handler can access datastore/session specific datastore state
+ fProfileHandlerP->setRelatedDatastore(aLocalDatastoreP);
+ // parse data
+ stringSize sz;
+ cAppCharP t = smlPCDataToCharP(aItemP->data,&sz);
+ if (!fProfileHandlerP->parseText(t,sz,*itemP)) {
+ // format error
+ aStatusCmd.setStatusCode(415); // Unsupported media type or format
+ ADDDEBUGITEM(aStatusCmd,"Error parsing MIME-DIR content");
+ return false;
+ }
+ }
+ else {
+ // no data
+ aStatusCmd.setStatusCode(412); // incomplete command
+ ADDDEBUGITEM(aStatusCmd,"No data found in item");
+ return false;
+ }
+ // let ancestor process data as well
+ return TMultiFieldItemType::internalFillInData(aSyncItemP,aItemP,aLocalDatastoreP,aStatusCmd);
+} // TMimeDirItemType::internalFillInData
+
+
+// sets data and meta from SyncItem data, but leaves source & target untouched
+bool TMimeDirItemType::internalSetItemData(
+ TSyncItem *aSyncItemP, // the syncitem to be represented as SyncML
+ SmlItemPtr_t aItem, // item with NULL meta and NULL data
+ TLocalEngineDS *aLocalDatastoreP // local datastore
+)
+{
+ // check type
+ TMultiFieldItem *itemP;
+ GET_CASTED_PTR(itemP,TMultiFieldItem,aSyncItemP,DEBUGTEXT("TMimeDirItemType::internalSetItemData: incompatible item class","mdit8"));
+ // let ancestor prepare first
+ if (!TMultiFieldItemType::internalSetItemData(aSyncItemP,aItem,aLocalDatastoreP)) return false;
+ // set related datastore so handler can access datastore/session specific datastore state
+ fProfileHandlerP->setRelatedDatastore(aLocalDatastoreP);
+ // generate data item
+ string dataitem;
+ fProfileHandlerP->generateText(*itemP,dataitem);
+ // put data item into opaque/cdata PCData
+ aItem->data=newPCDataStringX((const uInt8 *)dataitem.c_str(),true);
+ // can't go wrong
+ return true;
+} // TMimeDirItemType::internalSetItemData
+
+
+
+
+bool TMimeDirItemType::parseForProperty(SmlItemPtr_t aItemP, const char *aPropName, string &aString)
+{
+ if (aItemP && aItemP->data)
+ return parseForProperty(smlPCDataToCharP(aItemP->data),aPropName,aString);
+ else
+ return false;
+} // TMimeDirItemType::parseForProperty
+
+
+// scan Data item for specific property (used for quick type tests)
+bool TMimeDirItemType::parseForProperty(const char *aText, const char *aPropName, string &aString)
+{
+ return static_cast<TMimeDirProfileHandler *>(fProfileHandlerP)->parseForProperty(aText, aPropName, aString);
+} // TMimeDirItemType::parseForProperty
+
+
+
+// generates SyncML-Devinf property list for type
+SmlDevInfCTDataPropListPtr_t TMimeDirItemType::newCTDataPropList(TTypeVariantDescriptor aVariantDescriptor)
+{
+ // let profile handle that
+ return fProfileHandlerP->newCTDataPropList(aVariantDescriptor, this);
+}
+
+
+#ifdef OBJECT_FILTERING
+
+// Filtering: add keywords and property names to filterCap
+void TMimeDirItemType::addFilterCapPropsAndKeywords(SmlPcdataListPtr_t &aFilterKeywords, SmlPcdataListPtr_t &aFilterProps, TTypeVariantDescriptor aVariantDescriptor)
+{
+ fProfileHandlerP->addFilterCapPropsAndKeywords(aFilterKeywords, aFilterProps, aVariantDescriptor, this);
+ // add basics
+ inherited::addFilterCapPropsAndKeywords(aFilterKeywords,aFilterProps,aVariantDescriptor);
+} // TMimeDirItemType::addFilterCapPropsAndKeywords
+
+#endif // OBJECT_FILTERING
+
+
+// Analyze CTCap part of devInf
+bool TMimeDirItemType::analyzeCTCap(SmlDevInfCTCapPtr_t aCTCapP)
+{
+ if (TMultiFieldItemType::analyzeCTCap(aCTCapP)) {
+ // let profile check
+ fProfileHandlerP->analyzeCTCap(aCTCapP, this);
+ }
+ #ifdef SYDEBUG
+ if (PDEBUGTEST(DBG_REMOTEINFO+DBG_DETAILS)) {
+ // Show which fields are enabled
+ PDEBUGPRINTFX(DBG_REMOTEINFO,("Field options after CTCap analyzing:"));
+ string finfo;
+ for (sInt16 i=0; i<fFieldDefinitionsP->numFields(); i++) {
+ StringObjAppendPrintf(finfo,
+ "- %-20s : %-12s maxoccur=%ld, maxsize=%ld %s%s\n",
+ fFieldDefinitionsP->fFields[i].TCFG_CSTR(fieldname),
+ getFieldOptions(i)->available ? "AVAILABLE" : "n/a",
+ (long)getFieldOptions(i)->maxoccur,
+ (long)getFieldOptions(i)->maxsize,
+ getFieldOptions(i)->maxsize == FIELD_OPT_MAXSIZE_UNKNOWN ?
+ "(limited, but unknown size)" :
+ (getFieldOptions(i)->maxsize == FIELD_OPT_MAXSIZE_NONE ? "(unlimited)" : ""),
+ getFieldOptions(i)->notruncate ? ", noTruncate" : ""
+ );
+ }
+ PDEBUGPUTSXX(DBG_REMOTEINFO+DBG_DETAILS,finfo.c_str(),0,true);
+ }
+ #endif
+ return true;
+}
+
+
+
+/// @brief helper to create same-typed instance via base class
+TSyncItemType *TMimeDirItemType::newCopyForSameType(
+ TSyncSession *aSessionP, // the session
+ TSyncDataStore *aDatastoreP // the datastore
+)
+{
+ // create new itemtype of appropriate derived class type that can handle
+ // this type
+ MP_RETURN_NEW(TMimeDirItemType,DBG_OBJINST,"TMimeDirItemType",TMimeDirItemType(
+ aSessionP,
+ fTypeConfigP,
+ getTypeName(),
+ getTypeVers(),
+ aDatastoreP,
+ fFieldDefinitionsP
+ ));
+} // TMimeDirItemType::newCopyForSameType
+
+
+/// @brief copy CTCap derived info from another SyncItemType
+/// @return false if item not compatible
+/// @note required to create remote type variants from ruleMatch type alternatives
+bool TMimeDirItemType::copyCTCapInfoFrom(TSyncItemType &aSourceItem)
+{
+ // must be same type as myself or based on the type of myself
+ if (!aSourceItem.isBasedOn(getTypeID()))
+ return false; // not compatible
+ TMimeDirItemType *itemTypeP = static_cast<TMimeDirItemType *>(&aSourceItem);
+ // copy the fieldDefs flag
+ fReceivedFieldDefs = itemTypeP->fReceivedFieldDefs;
+ // other CTCap info is in the field options of MultiFieldItemType
+ return inherited::copyCTCapInfoFrom(aSourceItem);
+} // TMimeDirItemType::copyCTCapInfoFrom
+
+
+/* end of TMimeDirItemType implementation */
+
+} // namespace sysync
+
+
+// eof
diff --git a/src/sysync/mimediritemtype.h b/src/sysync/mimediritemtype.h
new file mode 100755
index 0000000..a56ca72
--- /dev/null
+++ b/src/sysync/mimediritemtype.h
@@ -0,0 +1,134 @@
+/*
+ * File: mimediritemtype.h
+ *
+ * Author: Lukas Zeller (luz@synthesis.ch)
+ *
+ * TMimeDirItemType
+ * base class for MIME DIR based content types (vCard, vCalendar...)
+ *
+ * Copyright (c) 2001-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ * 2001-06-12 : luz : created
+ *
+ */
+
+#ifndef MimeDirItemType_H
+#define MimeDirItemType_H
+
+// includes
+#include "syncitemtype.h"
+#include "multifielditemtype.h"
+
+#include "mimedirprofile.h"
+
+#include <set>
+
+namespace sysync {
+
+
+// MIME-dir based datatype
+class TMIMEDirTypeConfig : public TMultiFieldTypeConfig
+{
+ typedef TMultiFieldTypeConfig inherited;
+public:
+ TMIMEDirTypeConfig(const char *aElementName, TConfigElement *aParentElementP);
+ virtual ~TMIMEDirTypeConfig();
+ // properties
+ // - associated MIME profile (casted version of ancestor's fProfileConfigP)
+ TMIMEProfileConfig *fMIMEProfileP;
+ // public functions
+ // - create Sync Item Type of appropriate type from config
+ virtual TSyncItemType *newSyncItemType(TSyncSession *aSessionP, TSyncDataStore *aDatastoreP);
+ // get a descriptor for selecting a variant of a datatype (if any), NULL=no variant with this name
+ virtual TTypeVariantDescriptor getVariantDescriptor(const char *aVariantName);
+protected:
+ #ifdef CONFIGURABLE_TYPE_SUPPORT
+ // check config elements
+ virtual bool localStartElement(const char *aElementName, const char **aAttributes, sInt32 aLine);
+ #endif
+ virtual void localResolve(bool aLastPass);
+ virtual void clear();
+}; // TMIMEDirTypeConfig
+
+
+const uInt16 ity_mimedir=101; // must be unique
+
+class TMimeDirItemType: public TMultiFieldItemType
+{
+ typedef TMultiFieldItemType inherited;
+ friend class TMimeDirProfileHandler;
+public:
+ // constructor
+ TMimeDirItemType(
+ TSyncSession *aSessionP,
+ TDataTypeConfig *aTypeConfigP,
+ const char *aCTType,
+ const char *aVerCT,
+ TSyncDataStore *aRelatedDatastoreP,
+ TFieldListConfig *aFieldDefinitions // field definitions
+ );
+ // destructor
+ virtual ~TMimeDirItemType();
+ // access to type
+ virtual uInt16 getTypeID(void) const { return ity_mimedir; };
+ virtual bool isBasedOn(uInt16 aItemTypeID) const { return aItemTypeID==ity_mimedir ? true : TMultiFieldItemType::isBasedOn(aItemTypeID); };
+ // differentiation between implemented and just descriptive TSyncTypeItems
+ virtual bool isImplemented(void) { return true; }; // MIME-DIR is an implementati on
+ // returns true if field options are based on remote devinf (and not just defaults)
+ virtual bool hasReceivedFieldOptions(void) { return fReceivedFieldDefs; };
+ // helper to create same-typed instance via base class
+ // MUST BE IMPLEMENTED IN ALL DERIVED CLASSES!
+ virtual TSyncItemType *newCopyForSameType(
+ TSyncSession *aSessionP, // the session
+ TSyncDataStore *aDatastoreP // the datastore
+ );
+ #ifdef OBJECT_FILTERING
+ // filtering
+ // - get field index of given filter expression identifier.
+ virtual sInt16 getFilterIdentifierFieldIndex(const char *aIdentifier, uInt16 aIndex);
+ #endif
+protected:
+ // member fields
+ TMimeDirProfileHandler *fProfileHandlerP;
+ // CTCap parsing/generation
+ // - analyze CTCap for specific type
+ virtual bool analyzeCTCap(SmlDevInfCTCapPtr_t aCTCapP);
+ /// @brief copy CTCap derived info from another SyncItemType
+ virtual bool copyCTCapInfoFrom(TSyncItemType &aSourceItemP);
+ // - obtain property list for type, returns NULL if none available
+ virtual SmlDevInfCTDataPropListPtr_t newCTDataPropList(TTypeVariantDescriptor aVariantDescriptor);
+ #ifdef OBJECT_FILTERING
+ // - Filtering: add keywords and property names to filterCap
+ virtual void addFilterCapPropsAndKeywords(SmlPcdataListPtr_t &aFilterKeywords, SmlPcdataListPtr_t &aFilterProps, TTypeVariantDescriptor aVariantDesc);
+ #endif
+ // scan for specific property value string (for version check)
+ bool parseForProperty(const char *aText, const char *aPropName, string &aString);
+ bool parseForProperty(SmlItemPtr_t aItemP, const char *aPropName, string &aString);
+ // Item data management
+ // - create new sync item of proper type and optimization for specified target
+ virtual TSyncItem *internalNewSyncItem(TSyncItemType *aTargetItemTypeP, TLocalEngineDS *aLocalDatastoreP);
+ // - fill in SyncML data (but leaves IDs empty)
+ virtual bool internalFillInData(
+ TSyncItem *aSyncItemP, // SyncItem to be filled with data
+ SmlItemPtr_t aItemP, // SyncML toolkit item Data to be converted into SyncItem (may be NULL if no data, in case of Delete or Map)
+ TLocalEngineDS *aLocalDatastoreP, // local datastore
+ TStatusCommand &aStatusCmd // status command that might be modified in case of error
+ );
+ // - sets data and meta from SyncItem data, but leaves source & target untouched
+ virtual bool internalSetItemData(
+ TSyncItem *aSyncItemP, // the syncitem to be represented as SyncML
+ SmlItemPtr_t aItem, // item with NULL meta and NULL data
+ TLocalEngineDS *aLocalDatastoreP // local datastore
+ );
+private:
+ bool fReceivedFieldDefs;
+}; // TMimeDirItemType
+
+
+
+} // namespace sysync
+
+#endif // MimeDirItemType_H
+
+// eof
+
diff --git a/src/sysync/mimedirprofile.cpp b/src/sysync/mimedirprofile.cpp
new file mode 100644
index 0000000..ff20015
--- /dev/null
+++ b/src/sysync/mimedirprofile.cpp
@@ -0,0 +1,5120 @@
+/*
+ * File: mimedirprofile.cpp
+ *
+ * Author: Lukas Zeller (luz@synthesis.ch)
+ *
+ * TMimeDirItemType
+ * base class for MIME DIR based content types (vCard, vCalendar...)
+ *
+ * Copyright (c) 2001-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ * 2009-01-09 : luz : created from mimediritemtype.cpp
+ *
+ */
+
+// includes
+#include "prefix_file.h"
+#include "sysync.h"
+#include "vtimezone.h"
+#include "rrules.h"
+
+#include "mimedirprofile.h"
+#include "mimediritemtype.h"
+
+
+using namespace sysync;
+
+
+namespace sysync {
+
+
+// mime-DIR mode names
+const char * const mimeModeNames[numMimeModes] = {
+ "old",
+ "standard"
+};
+
+
+// VTIMEZONE generation modes
+const char * const VTimeZoneGenModes[numVTimeZoneGenModes] = {
+ "current",
+ "start",
+ "end",
+ "range",
+ "openend"
+};
+
+
+// VTIMEZONE generation modes
+const char * const VTzIdGenModes[numTzIdGenModes] = {
+ "default",
+ "olson"
+};
+
+
+// enumeration modes
+const char * const EnumModeNames[numEnumModes] = {
+ "translate", // translation from value to name and vice versa
+ "prefix", // translation of prefix while copying rest of string
+ "defaultname", // default name when translating from value to name
+ "defaultvalue", // default value when translating from name to value
+ "ignore" // ignore value or name
+};
+
+
+// profile modes
+const char * const ProfileModeNames[numProfileModes] = {
+ "custom", // custom profile
+ "vtimezones", // VTIMEZONE profile(s), expands to a VTIMEZONE for every time zone referenced by convmode TZID fields
+};
+
+
+// Config
+// ======
+
+#pragma exceptions off
+
+// Type registry
+
+TMIMEProfileConfig::TMIMEProfileConfig(const char* aName, TConfigElement *aParentElement) :
+ TProfileConfig(aName,aParentElement)
+{
+ fRootProfileP=NULL; // no profile yet
+ clear();
+} // TMIMEProfileConfig::TMIMEProfileConfig
+
+
+TMIMEProfileConfig::~TMIMEProfileConfig()
+{
+ clear();
+} // TMIMEProfileConfig::~TMIMEProfileConfig
+
+
+// init defaults
+void TMIMEProfileConfig::clear(void)
+{
+ // init defaults
+ if (fRootProfileP) {
+ delete fRootProfileP;
+ fRootProfileP=NULL; // no profile any more
+ }
+ // init options
+ fUnfloatFloating = false;
+ fVTimeZoneGenMode = vtzgen_current; // show VTIMEZONE valid for current year (i.e. not dependent on record's time stamps)
+ fTzIdGenMode = tzidgen_default; // default TZIDs
+ // reset group building mechanism
+ #ifdef CONFIGURABLE_TYPE_SUPPORT
+ fLastProperty = NULL;
+ fPropertyGroupID = 1; // start at 1 (0=no group)
+ #endif
+ // clear inherited
+ inherited::clear();
+} // TMIMEProfileConfig::clear
+
+#pragma exceptions reset
+
+
+// handler factory
+TProfileHandler *TMIMEProfileConfig::newProfileHandler(TMultiFieldItemType *aItemTypeP)
+{
+ // check if fieldlists match as they should
+ if (aItemTypeP->getFieldDefinitions()!=fFieldListP) {
+ // profile is for another field list, cannot be used for this item type
+ return NULL;
+ }
+ // our handler is the text profile handler
+ return (TProfileHandler *)(new TMimeDirProfileHandler(this,aItemTypeP));
+}
+
+
+#ifdef CONFIGURABLE_TYPE_SUPPORT
+
+
+// get conversion mode, virtual, can be overridden by derivates
+bool TMIMEProfileConfig::getConvMode(const char *aText, sInt16 &aConvMode)
+{
+ // basic modes
+ if (strucmp(aText,"none")==0)
+ aConvMode=CONVMODE_NONE;
+ else if (strucmp(aText,"version")==0)
+ aConvMode=CONVMODE_VERSION;
+ else if (strucmp(aText,"prodid")==0)
+ aConvMode=CONVMODE_PRODID;
+ else if (strucmp(aText,"timestamp")==0)
+ aConvMode=CONVMODE_TIMESTAMP;
+ else if (strucmp(aText,"date")==0)
+ aConvMode=CONVMODE_DATE;
+ else if (strucmp(aText,"autodate")==0)
+ aConvMode=CONVMODE_AUTODATE;
+ else if (strucmp(aText,"autoenddate")==0)
+ aConvMode=CONVMODE_AUTOENDDATE;
+ else if (strucmp(aText,"tz")==0)
+ aConvMode=CONVMODE_TZ;
+ else if (strucmp(aText,"daylight")==0)
+ aConvMode=CONVMODE_DAYLIGHT;
+ else if (strucmp(aText,"tzid")==0)
+ aConvMode=CONVMODE_TZID;
+ else if (strucmp(aText,"emptyonly")==0)
+ aConvMode=CONVMODE_EMPTYONLY;
+ else if (strucmp(aText,"bitmap")==0)
+ aConvMode=CONVMODE_BITMAP;
+ else if (strucmp(aText,"multimix")==0)
+ aConvMode=CONVMODE_MULTIMIX;
+ else if (strucmp(aText,"blob_b64")==0)
+ aConvMode=CONVMODE_BLOB_B64;
+ else if (strucmp(aText,"mailto")==0)
+ aConvMode=CONVMODE_MAILTO;
+ else if (strucmp(aText,"valuetype")==0)
+ aConvMode=CONVMODE_VALUETYPE;
+ else if (strucmp(aText,"fullvaluetype")==0)
+ aConvMode=CONVMODE_FULLVALUETYPE;
+ else if (strucmp(aText,"rrule")==0)
+ aConvMode=CONVMODE_RRULE;
+ else {
+ fail("'conversion' value '%s' is invalid",aText);
+ return false;
+ }
+ return true;
+} // TMIMEProfileConfig::getConvMode
+
+
+
+
+// private helper
+bool TMIMEProfileConfig::getConvAttrs(const char **aAttributes, sInt16 &aFid, sInt16 &aConvMode, char &aCombSep)
+{
+ // - get options
+ const char *fnam = getAttr(aAttributes,"field");
+ if (fnam && *fnam!=0) {
+ // has field spec
+ // - find field ID
+ aFid = fFieldListP->fieldIndex(fnam);
+ if (aFid==VARIDX_UNDEFINED) {
+ fail("'field' '%s' does not exist in field list '%s'",fnam,fFieldListP->getName());
+ return false;
+ }
+ }
+ // - get conversion mode
+ const char *conv = getAttr(aAttributes,"conversion");
+ if (conv) {
+ if (!getConvMode(conv,aConvMode)) return false;
+ }
+ // - get combination char
+ const char *comb = getAttr(aAttributes,"combine");
+ if (comb) {
+ if (strucmp(comb,"no")==0)
+ aCombSep=0;
+ else if (strucmp(comb,"lines")==0)
+ aCombSep='\n';
+ else if (strlen(comb)==1)
+ aCombSep=*comb;
+ else {
+ fail("'combine' value '%s' is invalid",comb);
+ return false;
+ }
+ }
+ return true; // ok
+} // TMIMEProfileConfig::getConvAttrs
+
+
+bool TMIMEProfileConfig::getMask(const char **aAttributes, const char *aName, TParameterDefinition *aParamP, TNameExtIDMap &aMask)
+{
+ const char *m=getAttr(aAttributes,aName);
+ if (m) {
+ while (*m) {
+ // skip comma separators and spaces
+ if (*m==',' || *m<=0x20) { m++; continue; }
+ // decode substring
+ TParameterDefinition *paramP = aParamP; // default param
+ size_t n=0;
+ while(m[n]>0x20 && m[n]!=',') {
+ if (m[n]=='.') {
+ // qualified enum, search param
+ paramP=fOpenProperty->findParameter(m,n);
+ if (!paramP) {
+ fail("Unknown param '%s' referenced in '%s'",m,aName);
+ return false;
+ }
+ m+=n+1; // set start to enum name
+ n=0; // start anew
+ continue; // prevent increment
+ }
+ n++;
+ }
+ if (!paramP) {
+ fail("Enum value must be qualified with parameter name: '%s'",m);
+ return false;
+ }
+ TNameExtIDMap msk = paramP->getExtIDbit(m,n);
+ if (msk==0) {
+ fail("'%s' is not an enum of parameter '%s'",m,paramP->paramname.c_str());
+ return false;
+ }
+ aMask = aMask | msk;
+ m+=n; // advance pointer
+ }
+ }
+ return true; // ok;
+} // TMIMEProfileConfig::getMask
+
+
+bool TMIMEProfileConfig::processPosition(TParameterDefinition *aParamP, const char **aAttributes)
+{
+ // <position has="TYPE.HOME" hasnot="FAX,CELL" shows="VOICE"
+ // field="TEL_HOME" repeat="4" increment="1" minshow="0"/>
+ // - get maps
+ TNameExtIDMap hasmap=0;
+ TNameExtIDMap hasnotmap=0;
+ TNameExtIDMap showsmap=0;
+ if (!getMask(aAttributes,"has",aParamP,hasmap)) return false; // failed
+ if (!getMask(aAttributes,"hasnot",aParamP,hasnotmap)) return false; // failed
+ if (!getMask(aAttributes,"shows",aParamP,showsmap)) return false; // failed
+ // - get field
+ sInt16 fid=FID_NOT_SUPPORTED;
+ const char *fnam = getAttr(aAttributes,"field");
+ if (fnam) {
+ fid = fFieldListP->fieldIndex(fnam);
+ if (fid==VARIDX_UNDEFINED)
+ return !fail("'field' '%s' does not exist in field list '%s'",fnam,fFieldListP->getName());
+ }
+ // - calculate offset from first specified value field in property
+ sInt16 fidoffs=FID_NOT_SUPPORTED;
+ if (fid>=0) {
+ for (sInt16 k=0; k<fOpenProperty->numValues; k++) {
+ fidoffs=fOpenProperty->convdefs[k].fieldid;
+ if (fidoffs>=0) break; // found field offset
+ }
+ if (fidoffs<0)
+ return !fail("property '%s' does not have any field assigned, cannot use 'position'",fOpenProperty->propname.c_str());
+ // calc now
+ fidoffs=fid-fidoffs;
+ }
+ // - get repeat and increment
+ sInt16 repeat=1; // no repeat, no rewrite, but check other <position>s when property occurs again.
+ sInt16 incr=1; // inc by 1
+ sInt16 minshow=-1; // auto mode, same as repeat
+ bool overwriteempty=true; // do not store empty repetitions
+ bool readonly=false; // not just a parsing alternative
+ sInt16 sharecountoffs=0; // no repeat counter sharing
+ // - check special maxrepeat values
+ const char *repval = getAttr(aAttributes,"repeat");
+ if (repval) {
+ if (strucmp(repval,"rewrite")==0) repeat=REP_REWRITE;
+ #ifdef ARRAYFIELD_SUPPORT
+ else if (strucmp(repval,"array")==0) repeat=REP_ARRAY;
+ #endif
+ else if (!StrToShort(repval,repeat))
+ return !fail("expected number, 'rewrite' or 'array' in 'repeat'");
+ }
+ // - increment and minshow
+ if (
+ !getAttrShort(aAttributes,"increment",incr,true) ||
+ !getAttrShort(aAttributes,"minshow",minshow,true) ||
+ !getAttrShort(aAttributes,"sharepreviouscount",sharecountoffs,true)
+ )
+ return !fail("number expected in 'increment', 'minshow' and 'sharepreviouscount'");
+ // - overwrite empty
+ if (!getAttrBool(aAttributes,"overwriteempty",overwriteempty,true))
+ return !fail("expected boolean value in 'overwriteempty'");
+ // - read only position
+ if (!getAttrBool(aAttributes,"readonly",readonly,true))
+ return !fail("expected boolean value in 'readonly'");
+ // - create name extension (position)
+ fOpenProperty->addNameExt(
+ fRootProfileP,hasmap,hasnotmap,showsmap,fidoffs,
+ repeat,incr,minshow,overwriteempty,readonly,sharecountoffs
+ );
+ expectEmpty(); // no contents (and not a separte nest level)
+ return true; // ok
+} // TMIMEProfileConfig::processPosition
+
+
+// called at end of nested parsing level
+void TMIMEProfileConfig::nestedElementEnd(void)
+{
+ // - change mode
+ if (fOpenConvDef) fOpenConvDef=NULL; // done with value spec
+ else if (fOpenParameter) fOpenParameter=NULL; // done with paramater
+ else if (fOpenProperty) fOpenProperty=NULL; // done with property
+ else if (fOpenProfile) {
+ fOpenProfile=fOpenProfile->parentProfile; // back to parent profile (or NULL if root)
+ // groups do not span profiles
+ fLastProperty=NULL;
+ }
+} // TMIMEProfileConfig::nestedElementEnd
+
+
+// config element parsing
+bool TMIMEProfileConfig::localStartElement(const char *aElementName, const char **aAttributes, sInt32 aLine)
+{
+ sInt16 nummand;
+ const char *nam;
+ const char *val;
+ const char *fnam;
+ sInt16 fid;
+ sInt16 convmode;
+ char combsep;
+
+ // MIME profile. This is multi-level and therefore needs
+ // complicated parser.
+ if (fNest==0) {
+ // reset to root level
+ fOpenProfile=NULL;
+ fOpenProperty=NULL;
+ fOpenParameter=NULL;
+ fOpenConvDef=NULL;
+ }
+ // - parse generics
+ // - get MIME-DIR type dependency
+ TMimeDirMode modeDep = numMimeModes; // no mode dependency by default
+ sInt16 m;
+ cAppCharP modeDepName = getAttr(aAttributes,"onlyformode");
+ if (modeDepName) {
+ if (!StrToEnum(mimeModeNames,numMimeModes,m,modeDepName))
+ return fail("unknown 'onlyformode' attribute value '%s'",modeDepName);
+ else
+ modeDep=(TMimeDirMode)m;
+ }
+ // - now parse specifics
+ if (fOpenConvDef) {
+ if (strucmp(aElementName,"enum")==0) {
+ // <enum name="nam" value="val" mode="translate" positional="yes">
+ // - get name and value
+ nam = getAttr(aAttributes,"name");
+ val = getAttr(aAttributes,"value");
+ // - get mode
+ TEnumMode mode=enm_translate; // default to translate
+ sInt16 m;
+ const char *mod=getAttr(aAttributes,"mode");
+ if (mod) {
+ if (!StrToEnum(EnumModeNames,numEnumModes,m,mod))
+ return fail("unknown 'mode' '%s'",mod);
+ else
+ mode=(TEnumMode)m;
+ }
+ // - get options
+ bool positional=fOpenParameter && fOpenParameter->extendsname; // default to parameter
+ if (!getAttrBool(aAttributes,"positional",positional,true))
+ return fail("bad boolean value for 'positional'");
+ if (fOpenParameter && positional && !fOpenParameter->extendsname)
+ return fail("'parameter' must have set 'positional' to use positional 'enum's");
+ // check logic
+ if (mode!=enm_default_value && mode!=enm_ignore && (!nam || *nam==0))
+ return fail("non-default/non-positional 'enum' must have 'name' attribute");
+ // 1:1 translation shortcut: if no value specified, use name as value
+ if (mode==enm_translate && !val)
+ val=nam; // use name as value
+ // default name and ignore can have no value (prefix can have empty value)
+ if (!positional && mode!=enm_default_name && mode!=enm_ignore && (!val || (*val==0 && mode!=enm_prefix)))
+ return fail("non-default 'enum' must have (possibly non-empty) 'value' attribute");
+ // - create enum
+ if (positional)
+ fOpenConvDef->addEnumNameExt(fOpenProperty, nam,val,mode);
+ else
+ fOpenConvDef->addEnum(nam,val,mode);
+ expectEmpty(); // no contents (and not a separate nest level)
+ }
+ // none known here
+ else
+ return false; // parent is TConfigElement, no need to call inherited
+ }
+ else if (fOpenParameter) {
+ if (strucmp(aElementName,"value")==0) {
+ // <value field="N_FIRST" conversion="none" combine="no"/>
+ // - set default options
+ fid=FID_NOT_SUPPORTED;
+ convmode=CONVMODE_NONE;
+ combsep=0;
+ // - get other options of convdef
+ if (!getConvAttrs(aAttributes,fid,convmode,combsep)) return true; // failed
+ // - set convdef
+ fOpenConvDef=fOpenParameter->setConvDef(fid,convmode,combsep);
+ startNestedParsing();
+ }
+ else if (strucmp(aElementName,"position")==0) {
+ // Position within parameter, enums reference this parameter without
+ // explicitly qualified enum names. To reference enums of other params,
+ // qualified names are allowed.
+ // <position has="TYPE.HOME" hasnot="FAX,CELL" shows="VOICE" field="TEL_HOME" repeat="4" increment="1"/>
+ if (!processPosition(fOpenParameter,aAttributes))
+ return true; // failed
+ }
+ // none known here
+ else
+ return inherited::localStartElement(aElementName, aAttributes, aLine); // call inherited
+ }
+ else if (fOpenProperty) {
+ if (strucmp(aElementName,"value")==0) {
+ // <value index="1" field="N_FIRST" conversion="none" combine="no"/>
+ // - get index
+ sInt16 idx=0;
+ if (!getAttrShort(aAttributes,"index",idx,fOpenProperty->numValues==1)) // optional only for 1-value properties (or lists)
+ return fail("'index' missing or with invalid value");
+ if (idx>=fOpenProperty->numValues)
+ return fail("'index' out of range (0..%hd)",fOpenProperty->numValues);
+ // - set default options
+ fid=FID_NOT_SUPPORTED;
+ convmode=CONVMODE_NONE;
+ combsep=0;
+ // - get other options of convdef
+ if (!getConvAttrs(aAttributes,fid,convmode,combsep))
+ return true; // failed
+ // - set convdef
+ fOpenConvDef=fOpenProperty->setConvDef(idx,fid,convmode,combsep);
+ startNestedParsing();
+ }
+ else if (strucmp(aElementName,"parameter")==0) {
+ // <parameter name="TYPE" default="yes" positional="yes">
+ // - get name
+ nam = getAttr(aAttributes,"name");
+ if (!nam || *nam==0)
+ return fail("'parameter' must have 'name' attribute");
+ // - get options
+ bool positional=false;
+ bool defparam=false;
+ bool shownonempty=false; // don't show properties that have only param values, but no main value
+ bool showinctcap=false; // don't show parameter in CTCap by default
+ if (
+ !getAttrBool(aAttributes,"positional",positional,true) ||
+ !getAttrBool(aAttributes,"default",defparam,true) ||
+ !getAttrBool(aAttributes,"shownonempty",shownonempty,true) ||
+ !getAttrBool(aAttributes,"show",showinctcap,true) ||
+ !getAttrBool(aAttributes,"showindevinf",showinctcap,true) // synonymous with "show" for parameters (note that "show" on properties is no longer effective on purpose!)
+ )
+ return fail("bad boolean value");
+ // - add parameter
+ fOpenParameter = fOpenProperty->addParam(nam,defparam,positional,shownonempty,showinctcap,modeDep);
+ startNestedParsing();
+ }
+ else if (strucmp(aElementName,"position")==0) {
+ // Position outside parameter, enums must reference parameters using
+ // explicitly qualified enum names like "TYPE.HOME"
+ // <position has="TYPE.HOME" hasnot="TYPE.FAX,TYPE.CELL" shows="TYPE.VOICE" field="TEL_HOME" repeat="4" increment="1"/>
+ if (!processPosition(NULL,aAttributes))
+ return true; // failed
+ }
+ // none known here
+ else
+ return inherited::localStartElement(aElementName, aAttributes, aLine); // call inherited
+ }
+ else if (fOpenProfile) {
+ if (strucmp(aElementName,"subprofile")==0) {
+ // <subprofile name="VTODO" nummandatory="1" field="KIND" value="TODO" showlevel="yes" showprops="yes">
+ // <subprofile name="VTODO" nummandatory="1" useproperties="VEVENT" field="KIND" value="TODO" showlevel="yes" showprops="yes">
+ // - starting a new subprofile starts a new property group anyway
+ fLastProperty=NULL;
+ // - get name
+ nam = getAttr(aAttributes,"name");
+ if (!nam || *nam==0)
+ return fail("'subprofile' must have 'name' attribute");
+ // - get profile mode
+ TProfileModes mode = profm_custom;
+ sInt16 m;
+ cAppCharP pfmode = getAttr(aAttributes,"mode");
+ if (pfmode) {
+ if (!StrToEnum(ProfileModeNames,numProfileModes,m,pfmode))
+ return fail("unknown profile 'mode' '%s'",pfmode);
+ else
+ mode=(TProfileModes)m;
+ }
+ // - check mode dependent params
+ TProfileDefinition *profileP = NULL; // no foreign properties by default
+ if (mode==profm_custom) {
+ // Custom profile
+ // - get number of mandatory properties
+ if (!getAttrShort(aAttributes,"nummandatory",nummand,false))
+ return fail ("missing or bad 'nummandatory' specification");
+ // - check if using properties of other profile
+ const char *use = getAttr(aAttributes,"useproperties");
+ if (use) {
+ profileP = fRootProfileP->findProfile(use);
+ if (!profileP)
+ return fail("unknown profile '%s' specified in 'useproperties'",use);
+ expectEmpty(true); // subprofile is a nest level, so we need to flag that (otherwise, nestedElementEnd() would not get called)
+ }
+ else {
+ // parsing nested elements in this TConfigElement
+ startNestedParsing();
+ }
+ }
+ else {
+ // non-custom profiles are expected to be empty
+ expectEmpty(true); // subprofile is a nest level, so we need to flag that (otherwise, nestedElementEnd() would not get called)
+ }
+ // - get DevInf visibility options
+ bool showifselectedonly = false; // default: show anyway
+ if (!getAttrBool(aAttributes,"showifselectedonly",showifselectedonly,true))
+ return fail("bad boolean value for showifselectedonly");
+ // - create subprofile now
+ fOpenProfile = fOpenProfile->addSubProfile(nam,nummand,showifselectedonly,mode,modeDep);
+ // - add properties of other level if any
+ if (profileP) fOpenProfile->usePropertiesOf(profileP);
+ // - add level control field stuff, if any
+ fnam = getAttr(aAttributes,"field");
+ if (fnam) {
+ // - "value" is optional, without a value subprofile is activated if field is non-empty
+ val = getAttr(aAttributes,"value");
+ // - find field
+ fid = fFieldListP->fieldIndex(fnam);
+ if (fid==VARIDX_UNDEFINED)
+ return fail("'field' '%s' does not exist in field list '%s'",fnam,fFieldListP->getName());
+ // - set level control convdef
+ TConversionDef *cdP = fOpenProfile->setConvDef(fid); // set field ID of level control field
+ if (val)
+ cdP->addEnum("",val,enm_translate); // set value to be set into level control field when level is entered
+ }
+ }
+ else if (strucmp(aElementName,"property")==0) {
+ // <property name="VERSION" rule="other" values="1" mandatory="no" show="yes" suppressempty="no" delayedparsing="0">
+ // - get name
+ nam = getAttr(aAttributes,"name");
+ if (!nam || *nam==0)
+ return fail("'property' must have 'name' attribute");
+ // - check grouping
+ if (!fLastProperty || strucmp(fLastProperty->TCFG_CSTR(propname),nam)!=0) {
+ // first property in group
+ fPropertyGroupID++; // new group ID
+ }
+ #ifndef NO_REMOTE_RULES
+ // - get rule dependency
+ bool isRuleDep=false;
+ const char *depRuleName = getAttr(aAttributes,"rule");
+ if (depRuleName) {
+ isRuleDep=true;
+ if (strucmp(depRuleName,"other")==0) {
+ // "other" rule (property is active if no other property from the group gets active)
+ depRuleName=NULL;
+ }
+ }
+ #endif
+ // - get number of values
+ sInt16 numval=1; // default to 1
+ const char *nvs = getAttr(aAttributes,"values");
+ if (nvs) {
+ if (strucmp(nvs,"list")==0) numval=NUMVAL_LIST;
+ else if (!StrToShort(nvs,numval))
+ return fail("invalid value in 'values' attribute");
+ }
+ // - get options
+ bool mandatory = false;
+ bool showprop = true; // show property in devInf by default
+ bool suppressempty = false;
+ bool canfilter = false; // 3.2.0.9 onwards: do not show filter caps by default (devInf gets too large)
+ if (
+ !getAttrBool(aAttributes,"mandatory",mandatory,true) ||
+ !getAttrBool(aAttributes,"showindevinf",showprop,true) || // formerly just called "show" (but renamed to make it ineffective in old configs as new engine prevents duplicates automatically)
+ !getAttrBool(aAttributes,"suppressempty",suppressempty,true) ||
+ !getAttrBool(aAttributes,"filter",canfilter,true)
+ ) return fail("bad boolean value");
+ const char *valsep= getAttr(aAttributes,"valueseparator");
+ if (!valsep) valsep=";"; // default to semicolon if not defined
+ const char *altvalsep= getAttr(aAttributes,"altvalueseparator");
+ if (!altvalsep) altvalsep=""; // default to none if not defined
+ // - delayed processing
+ sInt16 delayedprocessing=0; // default to 0
+ if (!getAttrShort(aAttributes,"delayedparsing",delayedprocessing,true))
+ return fail ("bad 'delayedparsing' specification");
+ // - create property now and open new level of parsing
+ fOpenProperty=fOpenProfile->addProperty(nam,numval,mandatory,showprop,suppressempty,delayedprocessing,*valsep,fPropertyGroupID,canfilter,modeDep, *altvalsep);
+ fLastProperty=fOpenProperty; // for group checking
+ #ifndef NO_REMOTE_RULES
+ // - add rule dependency (pointer will be resolved later)
+ fOpenProperty->dependsOnRemoterule=isRuleDep;
+ fOpenProperty->ruleDependency=NULL; // not known yet
+ TCFG_ASSIGN(fOpenProperty->dependencyRuleName,depRuleName); // save name for later resolving
+ #endif
+ startNestedParsing();
+ }
+ // none known here
+ else
+ return inherited::localStartElement(aElementName, aAttributes, aLine); // call inherited
+ }
+ else {
+ if (strucmp(aElementName,"profile")==0) {
+ // <profile name="VCARD" nummandatory="2">
+ if (fRootProfileP)
+ return fail("'profile' cannot be defined more than once");
+ // new profile starts new property group
+ fLastProperty=NULL;
+ // get name
+ nam = getAttr(aAttributes,"name");
+ if (!nam || *nam==0)
+ return fail("'profile' must have 'name' attribute");
+ // - get number of mandatory properties
+ if (!getAttrShort(aAttributes,"nummandatory",nummand,false))
+ return fail ("missing or bad 'nummandatory' specification");
+ // create root profile
+ fRootProfileP = new TProfileDefinition(NULL,nam,nummand,false,profm_custom,numMimeModes); // root needs no selection to be shown, is always a custom profile, and not mode dependent
+ // parsing nested elements in this TConfigElement
+ fOpenProfile=fRootProfileP; // current open profile
+ startNestedParsing();
+ }
+ else if (strucmp(aElementName,"unfloattimestamps")==0)
+ expectBool(fUnfloatFloating);
+ else if (strucmp(aElementName,"vtimezonegenmode")==0)
+ expectEnum(sizeof(fVTimeZoneGenMode),&fVTimeZoneGenMode,VTimeZoneGenModes,numVTimeZoneGenModes);
+ else if (strucmp(aElementName,"tzidgenmode")==0)
+ expectEnum(sizeof(fTzIdGenMode),&fTzIdGenMode,VTzIdGenModes,numTzIdGenModes);
+ // none known here
+ else
+ return inherited::localStartElement(aElementName, aAttributes, aLine);
+ }
+ // ok
+ return true;
+} // TMIMEProfileConfig::localStartElement
+
+
+
+#ifndef NO_REMOTE_RULES
+// resolve remote rule dependencies in profile (recursive)
+static void resolveRemoteRuleDeps(TProfileDefinition *aProfileP, TSessionConfig *aSessionConfigP)
+{
+ TProfileDefinition *profileP = aProfileP;
+ while (profileP) {
+ // resolve properties
+ TPropertyDefinition *propP = profileP->propertyDefs;
+ while (propP) {
+ // check for rule-dependent props
+ if (propP->dependsOnRemoterule) {
+ propP->ruleDependency=NULL; // assume the "other" rule entry
+ if (!propP->TCFG_ISEMPTY(dependencyRuleName)) {
+ // find remote rule
+ TRemoteRulesList::iterator pos;
+ for(pos=aSessionConfigP->fRemoteRulesList.begin();pos!=aSessionConfigP->fRemoteRulesList.end();pos++) {
+ if (strucmp(TCFG_CSTR(propP->dependencyRuleName),(*pos)->getName())==0) {
+ // found rule by name
+ propP->ruleDependency=(*pos);
+ break;
+ }
+ }
+ if (propP->ruleDependency==NULL) {
+ string s;
+ StringObjPrintf(s,"property '%s' depends on unknown rule '%s'",TCFG_CSTR(propP->propname),TCFG_CSTR(propP->dependencyRuleName));
+ SYSYNC_THROW(TConfigParseException(s.c_str()));
+ }
+ } // rule specified
+ }
+ // next
+ propP=propP->next;
+ }
+ // resolve subprofiles
+ resolveRemoteRuleDeps(profileP->subLevels,aSessionConfigP);
+ // next
+ profileP=profileP->next;
+ }
+} // resolveRemoteRuleDeps
+#endif
+
+// resolve
+void TMIMEProfileConfig::localResolve(bool aLastPass)
+{
+ if (aLastPass) {
+ // check for required settings
+ if (!fRootProfileP)
+ SYSYNC_THROW(TConfigParseException("empty 'mimeprofile' not allowed"));
+ #ifndef NO_REMOTE_RULES
+ // recursively resolve remote rule dependencies in all properties
+ resolveRemoteRuleDeps(
+ fRootProfileP,
+ static_cast<TSessionConfig *>(static_cast<TRootConfig *>(getRootElement())->fAgentConfigP)
+ );
+ #endif
+ }
+ // resolve inherited
+ inherited::localResolve(aLastPass);
+} // TMIMEProfileConfig::localResolve
+
+#endif // CONFIGURABLE_TYPE_SUPPORT
+
+
+
+
+
+
+// implementation of MIME-DIR info classes
+
+#pragma exceptions off
+#define EXCEPTIONS_HERE 0
+
+
+TEnumerationDef::TEnumerationDef(const char *aEnumName, const char *aEnumVal, TEnumMode aMode, sInt16 aNameExtID)
+{
+ next=NULL;
+ TCFG_ASSIGN(enumtext,aEnumName);
+ TCFG_ASSIGN(enumval,aEnumVal);
+ enummode=aMode;
+ nameextid=aNameExtID;
+} // TEnumerationDef::TEnumerationDef
+
+
+TEnumerationDef::~TEnumerationDef()
+{
+ // make sure entire chain gets deleted
+ if (next) delete next;
+} // TEnumerationDef::~TEnumerationDef
+
+
+
+TConversionDef::TConversionDef()
+{
+ fieldid=FID_NOT_SUPPORTED;
+ enumdefs=NULL;
+ convmode=0;
+ combineSep=0;
+} // TConversionDef::TConversionDef
+
+
+TConversionDef::~TConversionDef()
+{
+ // make sure enum list gets deleted
+ if (enumdefs) delete enumdefs;
+} // TEnumerationDef::~TEnumerationDef
+
+
+TConversionDef *TConversionDef::setConvDef(
+ sInt16 aFieldId,
+ sInt16 aConvMode,
+ char aCombSep
+)
+{
+ fieldid=aFieldId;
+ convmode=aConvMode;
+ combineSep=aCombSep;
+ return this;
+} // TConversionDef::setConvDef
+
+
+const TEnumerationDef *TConversionDef::findEnumByName(const char *aName, sInt16 n)
+const
+{
+ TEnumerationDef *enumP = enumdefs;
+ TEnumerationDef *defaultenumP = NULL;
+ while(enumP) {
+ // check plain match
+ if (
+ (enumP->enummode==enm_translate || enumP->enummode==enm_ignore) &&
+ strucmp(aName,enumP->TCFG_CSTR(enumtext),n)==0
+ ) break; // found full match
+ // check prefix match
+ else if (
+ enumP->enummode==enm_prefix &&
+ (TCFG_SIZE(enumP->enumtext)==0 || strucmp(aName,TCFG_CSTR(enumP->enumtext),TCFG_SIZE(enumP->enumtext))==0)
+ ) break; // found prefix match (or prefix entry with no text, which means match as well)
+ // otherwise: remember if this is a default
+ else if (enumP->enummode==enm_default_value) {
+ // default value entry
+ defaultenumP=enumP; // anyway: remember default value entry
+ // allow searching default value by name (for "has","hasnot" parsing via getExtIDbit())
+ if (!(enumP->TCFG_ISEMPTY(enumtext)) && strucmp(aName,enumP->TCFG_CSTR(enumtext),n)==0)
+ break; // found named default value
+ }
+ // check next
+ enumP=enumP->next;
+ }
+ return enumP ? enumP : defaultenumP;
+} // TConversionDef::findEnumByName
+
+
+const TEnumerationDef *TConversionDef::findEnumByVal(const char *aVal, sInt16 n)
+const
+{
+ TEnumerationDef *enumP = enumdefs;
+ TEnumerationDef *defaultenumP = NULL;
+ while(enumP) {
+ // check full match
+ if (
+ (enumP->enummode==enm_translate || enumP->enummode==enm_ignore) &&
+ strucmp(aVal,TCFG_CSTR(enumP->enumval),n)==0
+ ) break; // found
+ // check prefix match
+ else if (
+ enumP->enummode==enm_prefix &&
+ (TCFG_SIZE(enumP->enumval)==0 || strucmp(aVal,TCFG_CSTR(enumP->enumval),TCFG_SIZE(enumP->enumval))==0)
+ ) break; // found prefix match (or prefix entry with no value, which means match as well)
+ // remember if this is a default
+ else if (enumP->enummode == enm_default_name) defaultenumP=enumP; // remember default
+ // check next
+ enumP=enumP->next;
+ }
+ return enumP ? enumP : defaultenumP;
+} // TConversionDef::findEnumByVal
+
+
+void TConversionDef::addEnum(const char *aEnumName, const char *aEnumVal, TEnumMode aMode)
+{
+ TEnumerationDef **enumPP = &enumdefs;
+ while(*enumPP!=NULL) enumPP=&((*enumPP)->next); // find last in chain
+ *enumPP = new TEnumerationDef(aEnumName,aEnumVal,aMode); // w/o name extension
+} // TConversionDef::addEnum
+
+
+
+// add enum for name extension, auto-creates property-unique name extension ID
+void TConversionDef::addEnumNameExt(TPropertyDefinition *aProp, const char *aEnumName, const char *aEnumVal, TEnumMode aMode)
+{
+ TEnumerationDef **enumPP = &enumdefs;
+ while(*enumPP!=NULL) enumPP=&((*enumPP)->next); // find last in chain
+ if (aProp->nextNameExt>31)
+ #if EXCEPTIONS_HERE
+ SYSYNC_THROW(TSyncException(DEBUGTEXT("more than 32 name extensions","mdit3")));
+ #else
+ return; // silently ignore
+ #endif
+ *enumPP = new TEnumerationDef(aEnumName,aEnumVal, aMode, aProp->nextNameExt++);
+} // TConversionDef::addEnumNameExt
+
+
+TParameterDefinition::TParameterDefinition(
+ const char *aName, bool aDefault, bool aExtendsName, bool aShowNonEmpty, bool aShowInCTCap, TMimeDirMode aModeDep
+) {
+ next=NULL;
+ TCFG_ASSIGN(paramname,aName);
+ defaultparam=aDefault;
+ extendsname=aExtendsName;
+ shownonempty=aShowNonEmpty;
+ showInCTCap=aShowInCTCap;
+ modeDependency=aModeDep;
+} // TParameterDefinition::TParameterDefinition
+
+
+TParameterDefinition::~TParameterDefinition()
+{
+ if (next) delete next;
+} // TParameterDefinition::~TParameterDefinition
+
+
+TNameExtIDMap TParameterDefinition::getExtIDbit(const char *aEnumName, sInt16 n)
+{
+ const TEnumerationDef *enumP=convdef.findEnumByName(aEnumName,n);
+ if (enumP) {
+ return ((TNameExtIDMap)1<<enumP->nameextid);
+ }
+ return 0;
+} // TParameterDefinition::getExtIDbit
+
+
+TPropNameExtension::TPropNameExtension(
+ TNameExtIDMap aMusthave_ids, TNameExtIDMap aForbidden_ids, TNameExtIDMap aAddtlSend_ids,
+ sInt16 aFieldidoffs, sInt16 aMaxRepeat, sInt16 aRepeatInc, sInt16 aMinShow,
+ bool aOverwriteEmpty, bool aReadOnly, sInt16 aRepeatID
+) {
+ next=NULL;
+ musthave_ids=aMusthave_ids;
+ forbidden_ids=aForbidden_ids;
+ addtlSend_ids=aAddtlSend_ids;
+ fieldidoffs=aFieldidoffs;
+ maxRepeat=aMaxRepeat;
+ repeatInc=aRepeatInc;
+ minShow=aMinShow;
+ overwriteEmpty=aOverwriteEmpty;
+ readOnly=aReadOnly;
+ repeatID=aRepeatID;
+} // TPropNameExtension::TPropNameExtension
+
+
+TPropNameExtension::~TPropNameExtension()
+{
+ if (next) delete next;
+} // TPropNameExtension::~TPropNameExtension
+
+
+TPropertyDefinition::TPropertyDefinition(const char* aName, sInt16 aNumVals, bool aMandatory, bool aShowInCTCap, bool aSuppressEmpty, uInt16 aDelayedProcessing, char aValuesep, char aAltValuesep, uInt16 aPropertyGroupID, bool aCanFilter, TMimeDirMode aModeDep)
+{
+ next=NULL;
+ TCFG_ASSIGN(propname,aName);
+ nameExts=NULL; // none yet
+ nextNameExt=0; // no enums with name extensions defined yet for this property
+ valuelist=false; // no value list by default
+ valuesep=aValuesep; // separator for structured-value and value-list properties
+ altvaluesep=aAltValuesep; // alternate separator for structured-value and value-list properties (for parsing only)
+ propGroup=aPropertyGroupID; // property group ID
+ if (aNumVals==NUMVAL_LIST) {
+ // value list
+ valuelist=true;
+ numValues=1; // we accept a single convdef only
+ }
+ else {
+ // individual values
+ numValues=aNumVals;
+ }
+ // create convdefs array
+ convdefs = new TConversionDef[numValues];
+ parameterDefs=NULL; // none yet
+ mandatory=aMandatory;
+ showInCTCap=aShowInCTCap;
+ canFilter=aCanFilter;
+ suppressEmpty=aSuppressEmpty;
+ delayedProcessing=aDelayedProcessing;
+ modeDependency=aModeDep;
+ #ifndef NO_REMOTE_RULES
+ // not dependent on rule yet (as rules do not exists at TPropertyDefinition creation,
+ // dependency will be added later, if any)
+ dependsOnRemoterule=false;
+ ruleDependency=NULL;
+ #endif
+} // TPropertyDefinition::TPropertyDefinition
+
+
+TPropertyDefinition::~TPropertyDefinition()
+{
+ // delete name extensions
+ if (nameExts) delete nameExts;
+ // delete convdefs array
+ if (convdefs) delete [] convdefs;
+ // delete parameter definitions
+ if (parameterDefs) delete parameterDefs;
+ // delete rest of chain
+ if (next) delete next;
+} // TPropertyDefinition::~TPropertyDefinition
+
+
+TConversionDef *TPropertyDefinition::setConvDef(sInt16 aValNum, sInt16 aFieldId,sInt16 aConvMode,char aCombSep)
+{
+ if (aValNum<0 || aValNum>=numValues)
+ #if EXCEPTIONS_HERE
+ SYSYNC_THROW(TSyncException(DEBUGTEXT("setConvDef for Property with bad value number","mdit4")));
+ #else
+ return NULL; // silently ignore
+ #endif
+ return convdefs[aValNum].setConvDef(aFieldId,aConvMode,aCombSep);
+}; // TPropertyDefinition::TConversionDef
+
+
+void TPropertyDefinition::addNameExt(TProfileDefinition *aRootProfile, // for profile-global RepID generation
+ TNameExtIDMap aMusthave_ids, TNameExtIDMap aForbidden_ids, TNameExtIDMap aAddtlSend_ids,
+ sInt16 aFieldidoffs, sInt16 aMaxRepeat, sInt16 aRepeatInc, sInt16 aMinShow,
+ bool aOverwriteEmpty, bool aReadOnly, sInt16 aShareCountOffs
+)
+{
+ TPropNameExtension **namextPP = &nameExts;
+ while(*namextPP!=NULL) namextPP=&((*namextPP)->next); // find last in chain
+ if (aMinShow<0) {
+ if (aMaxRepeat==REP_ARRAY)
+ aMinShow=0; // by default, show nothing if array is empty
+ else
+ aMinShow=aMaxRepeat; // auto mode, show all repetitions
+ }
+ *namextPP = new TPropNameExtension(
+ aMusthave_ids,aForbidden_ids,aAddtlSend_ids,aFieldidoffs,
+ aMaxRepeat,aRepeatInc,aMinShow,aOverwriteEmpty,aReadOnly,
+ // readOnly alternative parsing <position> might want to share the
+ // repeat count with previous <position> occurrences
+ aShareCountOffs ? aRootProfile->nextRepID-aShareCountOffs : aRootProfile->nextRepID++
+ );
+} // TPropertyDefinition::addNameExt
+
+
+TParameterDefinition *TPropertyDefinition::addParam(
+ const char *aName, bool aDefault, bool aExtendsName, bool aShowNonEmpty, bool aShowInCTCap, TMimeDirMode aModeDep
+)
+{
+ TParameterDefinition **paramPP = &parameterDefs;
+ while(*paramPP!=NULL) paramPP=&((*paramPP)->next); // find last in chain
+ *paramPP = new TParameterDefinition(aName,aDefault,aExtendsName,aShowNonEmpty,aShowInCTCap, aModeDep);
+ return *paramPP;
+} // TPropertyDefinition::addParam
+
+
+// find parameter by name
+TParameterDefinition *TPropertyDefinition::findParameter(const char *aNam, sInt16 aLen)
+{
+ TParameterDefinition *paramP = parameterDefs;
+ while (paramP) {
+ if (strucmp(aNam,paramP->TCFG_CSTR(paramname),aLen)==0)
+ return paramP; // found
+ paramP=paramP->next; // next
+ }
+ // not found
+ return NULL;
+} // TPropertyDefinition::findParameter
+
+
+TProfileDefinition::TProfileDefinition(
+ TProfileDefinition *aParentProfileP, // parent profile
+ const char *aProfileName, // name
+ sInt16 aNumMandatory,
+ bool aShowInCTCapIfSelectedOnly,
+ TProfileModes aProfileMode,
+ TMimeDirMode aModeDep
+)
+{
+ parentProfile=aParentProfileP; // NULL if root
+ next=NULL;
+ // set fields
+ TCFG_ASSIGN(levelName,aProfileName);
+ shownIfSelectedOnly = aShowInCTCapIfSelectedOnly;
+ profileMode = aProfileMode;
+ modeDependency = aModeDep;
+ // init
+ numMandatoryProperties=aNumMandatory;
+ propertyDefs=NULL;
+ subLevels=NULL;
+ ownsProps=true;
+ nextRepID=0;
+} // TProfileDefinition::TProfileDefinition
+
+
+TProfileDefinition::~TProfileDefinition()
+{
+ if (propertyDefs && ownsProps) delete propertyDefs;
+ if (subLevels) delete subLevels;
+ if (next) delete next;
+} // TProfileDefinition::~TProfileDefinition
+
+
+TProfileDefinition *TProfileDefinition::addSubProfile(
+ const char *aProfileName, // name
+ sInt16 aNumMandatory,
+ bool aShowInCTCapIfSelectedOnly,
+ TProfileModes aProfileMode,
+ TMimeDirMode aModeDep
+)
+{
+ TProfileDefinition **profilePP=&subLevels;
+ while (*profilePP!=NULL) profilePP=&((*profilePP)->next);
+ *profilePP=new TProfileDefinition(this,aProfileName,aNumMandatory,aShowInCTCapIfSelectedOnly,aProfileMode,aModeDep);
+ return *profilePP;
+} // TProfileDefinition::addSubProfile
+
+
+TPropertyDefinition *TProfileDefinition::addProperty(
+ const char *aName, // name
+ sInt16 aNumValues, // number of values
+ bool aMandatory, // mandatory
+ bool aShowInCTCap, // show in CTCap
+ bool aSuppressEmpty, // suppress empty ones on send
+ uInt16 aDelayedProcessing, // delayed processing when parsed, 0=immediate processing, 1..n=delayed
+ char aValuesep, // value separator
+ uInt16 aPropertyGroupID, // property group ID (alternatives for same-named properties should have same ID>0)
+ bool aCanFilter, // can be filtered -> show in filter cap
+ TMimeDirMode aModeDep, // property valid only for specific MIME mode
+ char aAltValuesep // alternate separator (for parsing)
+)
+{
+ TPropertyDefinition **propPP=&propertyDefs;
+ while (*propPP!=NULL) propPP=&((*propPP)->next);
+ *propPP=new TPropertyDefinition(aName,aNumValues,aMandatory,aShowInCTCap,aSuppressEmpty,aDelayedProcessing,aValuesep,aAltValuesep,aPropertyGroupID,aCanFilter,aModeDep);
+ // return new property
+ return *propPP;
+} // TProfileDefinition::addProperty
+
+
+void TProfileDefinition::usePropertiesOf(TProfileDefinition *aProfile)
+{
+ ownsProps=false;
+ propertyDefs=aProfile->propertyDefs;
+} // TProfileDefinition::usePropertiesOf
+
+
+// find (sub)profile by name, recursively
+TProfileDefinition *TProfileDefinition::findProfile(const char *aNam)
+{
+ // check myself
+ if (levelName==aNam) return this;
+ // check sublevels
+ TProfileDefinition *lvlP = subLevels;
+ TProfileDefinition *foundlvlP;
+ while(lvlP) {
+ foundlvlP=lvlP->findProfile(aNam);
+ if (foundlvlP) return foundlvlP;
+ lvlP=lvlP->next;
+ }
+ // does not match myself nor one of my sublevels
+ return NULL;
+} // TProfileDefinition::findProfile
+
+#pragma exceptions reset
+#undef EXCEPTIONS_HERE
+#define EXCEPTIONS_HERE TARGET_HAS_EXCEPTIONS
+
+
+#ifdef OBJECT_FILTERING
+
+// get property definition of given filter expression identifier.
+TPropertyDefinition *TProfileDefinition::getPropertyDef(const char *aPropName)
+{
+ TPropertyDefinition *propP = NULL;
+
+ if (!aPropName) return propP; // no name, no fid
+ // Depth first: search in subprofiles, if any
+ TProfileDefinition *profileP = subLevels;
+ while (profileP) {
+ // search depth first
+ if ((propP=profileP->getPropertyDef(aPropName))!=NULL)
+ return propP; // found
+ // test next profile
+ profileP=profileP->next;
+ }
+ // now search my own properties
+ propP = propertyDefs;
+ while (propP) {
+ // compare names
+ if (strucmp(aPropName,TCFG_CSTR(propP->propname))==0) {
+ return propP;
+ }
+ // test next property
+ propP=propP->next;
+ }
+ // not found
+ return NULL;
+} // TProfileDefinition::getPropertyDef
+
+
+// get field index of given filter expression identifier.
+sInt16 TProfileDefinition::getPropertyMainFid(const char *aPropName, uInt16 aIndex)
+{
+ sInt16 fid = VARIDX_UNDEFINED;
+
+ // search property definition with matching name
+ TPropertyDefinition *propP = getPropertyDef(aPropName);
+ // search for first value with a field assigned
+ if (propP) {
+ // found property with matching name
+ if (propP->convdefs) {
+ if (aIndex==0) {
+ // no index specified -> search first with a valid FID
+ for (uInt16 i=0; i<propP->numValues; i++) {
+ if ((fid=propP->convdefs[i].fieldid)!=VARIDX_UNDEFINED)
+ return fid; // found a field index
+ }
+ }
+ else {
+ // index specified for multivalued properties -> return specified value's ID
+ if (aIndex<=propP->numValues) {
+ return propP->convdefs[aIndex-1].fieldid;
+ }
+ }
+ }
+ }
+ // not found
+ return VARIDX_UNDEFINED;
+} // TProfileDefinition::getPropertyMainFid
+
+
+#endif // OBJECT_FILTERING
+
+
+
+/*
+ * Implementation of TMimeDirProfileHandler
+ */
+
+
+TMimeDirProfileHandler::TMimeDirProfileHandler(
+ TMIMEProfileConfig *aMIMEProfileCfgP,
+ TMultiFieldItemType *aItemTypeP
+) : TProfileHandler(aMIMEProfileCfgP, aItemTypeP)
+{
+ // save profile config pointer
+ fProfileCfgP = aMIMEProfileCfgP;
+ fProfileDefinitionP = fProfileCfgP->fRootProfileP;
+ // settable options defaults
+ fMimeDirMode=mimo_standard;
+ fReceiverCanHandleUTC = true;
+ fVCal10EnddatesSameDay = false; // avoid 23:59:59 style end date by default
+ fReceiverTimeContext = TCTX_UNKNOWN; // none in particular
+ fDontSendEmptyProperties = false; // send all defined properties
+ fDefaultOutCharset = chs_utf8; // standard
+ fDoQuote8BitContent = false; // no quoting needed per se
+ fDoNotFoldContent = false; // standard requires folding
+ fTreatRemoteTimeAsLocal = false; // only for broken implementations
+ fTreatRemoteTimeAsUTC = false; // only for broken implementations
+ fAppliedRemoteRuleP = NULL; // no dependency
+} // TMimeDirProfileHandler::TMimeDirProfileHandler
+
+
+TMimeDirProfileHandler::~TMimeDirProfileHandler()
+{
+ // nop for now
+} // TMimeDirProfileHandler::~TTextProfileHandler
+
+
+
+#ifdef OBJECT_FILTERING
+
+// get field index of given filter expression identifier.
+sInt16 TMimeDirProfileHandler::getFilterIdentifierFieldIndex(const char *aIdentifier, uInt16 aIndex)
+{
+ // search properties for field index
+ return fProfileDefinitionP->getPropertyMainFid(aIdentifier, aIndex);
+} // TMimeDirProfileHandler::getFilterIdentifierFieldIndex
+
+#endif // OBJECT_FILTERING
+
+
+
+// parses enum value for CONVMODE_MULTIMIX
+// [offs.](Bx|Lzzzzzzz)
+// aN returns the bit number or the offset of the zzzzz literal within aMixVal, depending on aIsBitMap
+static bool mixvalparse(cAppCharP aMixVal, uInt16 &aOffs, bool &aIsBitMap, uInt16 &aN)
+{
+ aOffs = 0;
+ cAppCharP p = aMixVal;
+ // check offset (2 digit max)
+ if (isdigit(*p)) {
+ p+=StrToUShort(p,aOffs,2);
+ if (*p++ != '.') return false; // wrong syntax
+ }
+ // check command
+ if (*p == 'B') {
+ // bit number
+ aIsBitMap = true;
+ if (StrToUShort(p+1,aN,2)<1) return false; // wrong syntax
+ }
+ else if (*p=='L') {
+ // literal, return position within string
+ aIsBitMap = false;
+ aN = p+1-aMixVal; // literal starts at this position
+ }
+ else
+ return false; // unknown command
+ return true;
+} // mixvalparse
+
+
+
+// returns the size of the field block (how many fids in sequence) related
+// to a given convdef (for multi-field conversion modes such as CONVMODE_RRULE
+sInt16 TMimeDirProfileHandler::fieldBlockSize(const TConversionDef &aConvDef)
+{
+ if (aConvDef.convmode==CONVMODE_RRULE)
+ return 6; // RRULE fieldblock: DTSTART,FREQ,INTERVAL,FIRSTMASK,LASTMASK,UNTIL = 6 fields
+ else
+ return 1; // single field
+} // TMimeDirProfileHandler::fieldBlockSize
+
+
+
+// special field translation (to be extended in derived classes)
+// Note: the string returned by this function will be scanned as a
+// value list if combinesep is set, and every single value will be
+// enum-translated if enums defined.
+bool TMimeDirProfileHandler::fieldToMIMEString(
+ TMultiFieldItem &aItem, // the item where data comes from
+ sInt16 aFid, // the field ID (can be NULL for special conversion modes)
+ sInt16 aArrIndex, // the repeat offset to handle array fields
+ const TConversionDef *aConvDefP, // the conversion definition record
+ string &aString // output string
+)
+{
+ const int maxmix = 10;
+ uInt16 mixOffs[maxmix];
+ bool mixIsFlags[maxmix];
+ TEnumerationDef *enumP;
+ uInt16 offs; bool isFlags;
+ int nummix, i;
+ fieldinteger_t flags;
+ uInt16 bitNo;
+ TTimestampField *tsFldP;
+ TIntegerField *ifP;
+ TStringField *sfP;
+ timecontext_t tctx;
+ lineartime_t ts;
+ string s;
+ // RRULE field block values
+ char freq; // frequency
+ char freqmod; // frequency modifier
+ sInt16 interval; // interval
+ fieldinteger_t firstmask; // day mask counted from the first day of the period
+ fieldinteger_t lastmask; // day mask counted from the last day of the period
+ lineartime_t until; // last day
+ timecontext_t untilcontext;
+
+
+ // get pointer to leaf field
+ TItemField *fldP = aItem.getArrayField(aFid,aArrIndex,true); // existing array elements only
+
+ bool dateonly=false; // assume timestamp mode
+ bool autodate=true; // show date-only values automatically as date-only, even if stored in a timestamp field
+ switch (aConvDefP->convmode) {
+ // no special mode
+ case CONVMODE_NONE:
+ case CONVMODE_EMPTYONLY:
+ // just get field as string
+ if (!fldP) return false; // no field, no value
+ if (!fldP->isBasedOn(fty_timestamp)) goto normal;
+ // Based on timestamp
+ // - handle date-only specially
+ if (fldP->getType()==fty_date)
+ goto dateonly; // date-only
+ else
+ goto timestamp; // others are treated as timestamps
+ // date & time modes
+ case CONVMODE_DATE: // always show as date
+ dateonly:
+ dateonly = true; // render as date in all cases
+ goto timestamp;
+ case CONVMODE_AUTOENDDATE:
+ case CONVMODE_AUTODATE: // show date-only as date in iCal 2.0 (mimo_standard), but always as timestamp for vCal 1.0 (mimo_old)
+ if (fMimeDirMode==mimo_standard) goto timestamp; // use autodate if MIME-DIR format is not vCal 1.0 style
+ case CONVMODE_TIMESTAMP: // always show as timestamp
+ // get explictly as timestamp (even if field or field contents is date)
+ autodate = false; // do not show as date, even if it is a date-only
+ timestamp:
+ if (!fldP) return false; // no field, no value
+ if (!fldP->isBasedOn(fty_timestamp)) goto normal;
+ // show as timestamp
+ tsFldP = static_cast<TTimestampField *>(fldP);
+ tctx = tsFldP->getTimeContext();
+ // check for auto-date
+ if (autodate) {
+ if (TCTX_IS_DATEONLY(tctx))
+ dateonly=true;
+ }
+ // check for special cases
+ if (TCTX_IS_DURATION(tctx)) {
+ // duration is shown as such
+ tsFldP->getAsISO8601(aString, TCTX_UNKNOWN | TCTX_DURATION, false, false, false, false);
+ }
+ else if (dateonly) {
+ // date-only are either floating or shown as date-only part of original timestamp
+ tsFldP->getAsISO8601(aString, TCTX_UNKNOWN | TCTX_DATEONLY, false, false, false, false);
+ }
+ else if (fReceiverCanHandleUTC && !tsFldP->isFloating()) {
+ // remote can handle UTC and the timestamp is not floating
+ if (!TCTX_IS_UNKNOWN(fPropTZIDtctx)) {
+ // if we have rendered a TZID for this property, this means that apparently the remote
+ // supports TZID (otherwise the field would not be marked available in the devInf).
+ // - show it as floating, explicitly with both date AND time (both flags set)
+ tsFldP->getAsISO8601(aString, TCTX_UNKNOWN | TCTX_TIMEONLY | TCTX_DATEONLY, false, false, false, false);
+ }
+ else {
+ // - show it as UTC
+ tsFldP->getAsISO8601(aString, TCTX_UTC, true, false, false, false);
+ }
+ }
+ else {
+ // remote cannot handle UTC or time is floating (eventually dateonly or duration)
+ if (tsFldP->isFloating()) {
+ // floating, show as-is
+ lineartime_t ts = tsFldP->getTimestampAs(TCTX_UNKNOWN);
+ if (ts==noLinearTime)
+ aString.erase();
+ else {
+ if (TCTX_IS_DATEONLY(tctx)) {
+ // value is a date-only, but we must render it a datetime
+ ts=lineartime2dateonlyTime(ts); // make time part 0:00:00
+ }
+ // first check for auto-end-date (which must be floating)
+ // Note: we don't get here with a date only mimo_standard because it will be catched above, so test is not really needed
+ if (aConvDefP->convmode==CONVMODE_AUTOENDDATE && fVCal10EnddatesSameDay && TCTX_IS_DATEONLY(tctx) && fMimeDirMode==mimo_old)
+ ts-=1; // subtract one unit to make end show last time unit of previous day
+ // now show as floating ISO8601
+ TimestampToISO8601Str(aString, ts, TCTX_UNKNOWN, false, false);
+ }
+ }
+ else {
+ // not floating (=not a enddateonly), but we can't send UTC - render as localtime
+ // in item time zone (which defaults to session time zone)
+ tsFldP->getAsISO8601(aString, fItemTimeContext, false, false, false, false);
+ }
+ }
+ return true; // found
+ normal:
+ // simply as string
+ fldP->getAsString(aString);
+ return true; // found
+ case CONVMODE_TZ:
+ case CONVMODE_TZID:
+ case CONVMODE_DAYLIGHT:
+ // use now as default point in time for eventual offset calculations
+ ts = getSession()->getSystemNowAs(TCTX_SYSTEM);
+ // if no field is specified, the item context is used (which defaults to
+ // the session's user context)
+ // Note that testing fldP is not enough, because an empty array will also cause fldP==NULL
+ if (!fldP) {
+ if (aFid!=FID_NOT_SUPPORTED)
+ return false; // field not available (but conversion definition DOES refer to a field --> no time zone)
+ // conversion definition does not refer to a field: use item context
+ tctx = fItemTimeContext;
+ }
+ else if (fldP->isBasedOn(fty_timestamp)) {
+ // time zone of a timestamp
+ tsFldP = static_cast<TTimestampField *>(fldP);
+ // - if floating time, we have no time zone
+ if (tsFldP->isFloating() || tsFldP->isDuration()) return false; // floating or duration -> no time zone
+ // - get context
+ tctx = tsFldP->getTimeContext(); // get the context
+ // - get the value
+ ts = tsFldP->getTimestampAs(TCTX_UNKNOWN);
+ // prevent generating TZID (and associated VTIMEZONES later) for empty timestamp
+ if (ts==noLinearTime) return false; // no timestamp -> no time zone
+ }
+ else if (fldP->getCalcType()==fty_integer) {
+ // integer field is simply a time zone offset in minutes
+ tctx = TCTX_MINOFFSET(fldP->getAsInteger());
+ }
+ else if (!fldP->isEmpty()) {
+ // string field can be timezone name or numeric minute offset
+ fldP->getAsString(s);
+ if (!TimeZoneNameToContext(s.c_str(),tctx,getSessionZones())) {
+ // if not recognized as time zone name, use integer value
+ tctx = TCTX_MINOFFSET(fldP->getAsInteger());
+ }
+ }
+ else
+ return false; // no TZ to show
+ // if remote cannot handle UTC (i.e. only understands localtime), then make sure
+ // the time zone shown is the general item zone (user zone).
+ if (!fReceiverCanHandleUTC) {
+ TzConvertTimestamp(ts,tctx,fItemTimeContext,getSessionZones());
+ tctx = fItemTimeContext; // use item zone
+ }
+ // now render context as selected
+ if (aConvDefP->convmode==CONVMODE_TZID) {
+ // time zone ID for iCal 2.0 TZID parameter
+ // - make sure meta context is resolved (we don't want "SYSTEM" as TZID!)
+ if (!TzResolveMetaContext(tctx, getSessionZones())) return false; // cannot resolve, no time zone ID
+ // - if time zone is not UTC (which is represented as "Z" and needs no TZID), show name
+ if (!TCTX_IS_UTC(tctx) && !TCTX_IS_UNKNOWN(tctx) && !TCTX_IS_DATEONLY(tctx)) {
+ // - show name of zone as TZID
+ if (!TimeZoneContextToName(tctx, aString, getSessionZones(), fProfileCfgP->fTzIdGenMode==tzidgen_olson ? "o" : NULL)) return false; // cannot get name/ID
+ // - flag property-level TZID generated now
+ fPropTZIDtctx=tctx;
+ // - add to set of TZID-referenced time zones (for vTimezone generation)
+ fUsedTCtxSet.insert(fUsedTCtxSet.end(),tctx);
+ // - update range of time covered for generating VTIMEZONE later
+ if (ts) {
+ if (fEarliestTZDate==noLinearTime || fEarliestTZDate>ts) fEarliestTZDate = ts; // new minimum
+ if (fLatestTZDate==noLinearTime || fLatestTZDate<ts) fLatestTZDate = ts; // new maximum
+ }
+ }
+ }
+ else {
+ // CONVMODE_TZ or CONVMODE_DAYLIGHT
+ // - there's only one TZ/DAYLIGHT per item, so set it as item context
+ if (!fReceiverCanHandleUTC) {
+ // devices that can't handle UTC should not be bothered with TZ info
+ // (e.g. for N-Gage/3650 presence of a TZ shifts the data by the TZ value!?)
+ return false; // prevent generation of TZ or DAYLIGHT props
+ }
+ else {
+ // only if remote can handle UTC we may change the item time context
+ // (otherwise, the timestamp must be rendered in itemzone/userzone)
+ fItemTimeContext = tctx;
+ fHasExplicitTZ = true; // flag setting explicit time zone for item
+ }
+ // - get resolved TZ offset and DAYLIGHT string for vCal 1.0
+ ContextToTzDaylight(tctx,ts,s,tctx,getSessionZones());
+ if (aConvDefP->convmode==CONVMODE_TZ) {
+ // time zone in +/-hh[:mm] format for vCal 1.0 TZ property
+ // - render offset in extended format
+ aString.erase();
+ // - return true only if we actually have a TZ
+ return ContextToISO8601StrAppend(aString, tctx, true);
+ }
+ else if (aConvDefP->convmode==CONVMODE_DAYLIGHT) {
+ // TZ and DAYLIGHT property for vCal 1.0
+ aString = s;
+ // - return true only if we actually have a DAYLIGHT
+ return !s.empty();
+ }
+ }
+ // done
+ return true;
+ case CONVMODE_MAILTO:
+ // make sure we have a mailto: prefix (but not if string is empty)
+ if (!fldP) return false; // no field, no value
+ fldP->getAsString(s);
+ aString.erase();
+ if (strucmp(s.c_str(),"mailto:",7)!=0 && s.size()>0)
+ aString="mailto:";
+ aString+=s;
+ return true;
+ case CONVMODE_VALUETYPE:
+ case CONVMODE_FULLVALUETYPE:
+ // specify value type of field if needed
+ if (!fldP) return false; // no field -> no VALUE param
+ if (fldP->isBasedOn(fty_timestamp)) {
+ // show VALUE=DATE if we have date-only or time-only
+ tctx = static_cast<TTimestampField *>(fldP)->getTimeContext();
+ if (TCTX_IS_DURATION(tctx)) aString="DURATION";
+ else if (TCTX_IS_DATEONLY(tctx)) aString="DATE";
+ else if (TCTX_IS_TIMEONLY(tctx)) aString="TIME";
+ else {
+ // only show type if full value type requested
+ if (aConvDefP->convmode==CONVMODE_FULLVALUETYPE)
+ aString="DATE-TIME";
+ else
+ return false; // we don't need a VALUE param for normal datetimes
+ }
+ }
+ else
+ return false; // no field type that needs VALUE param
+ // valuetype generated
+ return true;
+ case CONVMODE_VERSION:
+ // version string
+ aString=aItem.getItemType()->getTypeVers(fProfileMode);
+ return true;
+ case CONVMODE_PRODID:
+ // PRODID ISO9070 non-registered FPI
+ // -//ABC Corporation//NONSGML My Product//EN
+ aString = SYSYNC_FPI;
+ return true;
+ case CONVMODE_BITMAP:
+ // bitmap is a special case of multimix, set up params
+ nummix = 1;
+ mixOffs[0]=0;
+ mixIsFlags[0]=true;
+ goto genmix;
+ case CONVMODE_MULTIMIX:
+ // list of special values that can be either literals or bit masks, and can optionally affect more than one field
+ // Syntax:
+ // Bx : Bit number x (like in CONVMODE_BITMAP, x = 0..63)
+ // Lxxxx : Literal xxxxx (xxxxx will just be copied from the source field)
+ // y.Bx or y.Lxxxx : use y as field offset to use (no y means 0 offset)
+ // - collect parameters to generate mix from enums
+ nummix = 0;
+ enumP = aConvDefP->enumdefs;
+ while(enumP) {
+ if (mixvalparse(TCFG_CSTR(enumP->enumval),offs,isFlags,bitNo)) {
+ // check if this field is in list already
+ for (i=0; i<nummix; i++) {
+ if (mixOffs[i] == offs) goto next; // referring to same field again, skip
+ }
+ // is a new field, add it to list
+ mixOffs[nummix] = offs;
+ mixIsFlags[nummix] = isFlags;
+ nummix++;
+ if (nummix>=maxmix) break; // no more mixes allowed, stop scanning
+ }
+ next:
+ // check next enum
+ enumP=enumP->next;
+ }
+ genmix:
+ // now generate strings from collected data
+ aString.erase();
+ for (i=0; i<nummix; i++) {
+ // get target field
+ fldP = aItem.getArrayField(aFid+mixOffs[i],aArrIndex,true); // existing array elements only
+ if (fldP) {
+ if (mixIsFlags[i]) {
+ // use target as bitmask to create bit numbers
+ flags=fldP->getAsInteger();
+ bitNo=0;
+ while (flags) {
+ if (flags & 1) {
+ // create bit representation
+ if (!aString.empty() && aConvDefP->combineSep)
+ aString+=aConvDefP->combineSep; // separator first if not first item
+ if (aConvDefP->convmode==CONVMODE_MULTIMIX) {
+ // multimix mode, use full syntax
+ if (mixOffs[i]>0)
+ StringObjAppendPrintf(aString,"%d.",mixOffs[i]);
+ aString += 'B';
+ }
+ // add bit number
+ StringObjAppendPrintf(aString,"%hd",bitNo);
+ }
+ flags >>= 1; // consume this one
+ bitNo++;
+ }
+ }
+ else {
+ // literal
+ if (!fldP->isEmpty()) {
+ if (!aString.empty() && aConvDefP->combineSep)
+ aString+=aConvDefP->combineSep; // append separator if there are more flags
+ if (mixOffs[i]>0)
+ StringObjAppendPrintf(aString,"%d.",mixOffs[i]);
+ aString += 'L'; // literal
+ fldP->appendToString(aString);
+ }
+ }
+ } // field available
+ } // for each mix
+ return true;
+ case CONVMODE_RRULE: {
+ // get values from field block
+ if (aFid<0) return false; // no field, no string
+ // - freq/freqmod
+ if (!(sfP = ITEMFIELD_DYNAMIC_CAST_PTR(TStringField,fty_string,aItem.getArrayField(aFid,aArrIndex,true)))) return false;
+ aFid++; // do NOT INCREMENT in macro, as it would get incremented twice
+ sfP->getAsString(s);
+ freq='0'; // none
+ freqmod=' '; // no modifier
+ if (s.size()>0) freq=s[0];
+ if (s.size()>1) freqmod=s[1];
+ // - interval
+ if (!(ifP = ITEMFIELD_DYNAMIC_CAST_PTR(TIntegerField,fty_integer,aItem.getArrayField(aFid,aArrIndex,true)))) return false;
+ aFid++; // do NOT INCREMENT in macro, as it would get incremented twice
+ interval=(sInt16)ifP->getAsInteger();
+ // - firstmask
+ if (!(ifP = ITEMFIELD_DYNAMIC_CAST_PTR(TIntegerField,fty_integer,aItem.getArrayField(aFid,aArrIndex,true)))) return false;
+ aFid++; // do NOT INCREMENT in macro, as it would get incremented twice
+ firstmask=ifP->getAsInteger();
+ // - lastmask
+ if (!(ifP = ITEMFIELD_DYNAMIC_CAST_PTR(TIntegerField,fty_integer,aItem.getArrayField(aFid,aArrIndex,true)))) return false;
+ aFid++; // do NOT INCREMENT in macro, as it would get incremented twice
+ lastmask=ifP->getAsInteger();
+ // - until
+ if (!(tsFldP = ITEMFIELD_DYNAMIC_CAST_PTR(TTimestampField,fty_timestamp,aItem.getArrayField(aFid,aArrIndex,true)))) return false;
+ aFid++; // do NOT INCREMENT in macro, as it would get incremented twice
+ // Until
+ // - UTC preferred as output format if basically possible and not actively disabled
+ untilcontext=
+ fReceiverCanHandleUTC && getSession()->canHandleUTC() ?
+ TCTX_UTC :
+ fItemTimeContext;
+ // - get in preferred zone (or floating)
+ until=tsFldP->getTimestampAs(untilcontext,&untilcontext);
+ lineartime_t tzend = until;
+ // A RRULE with no end extends at least into current time (for tz range update, see below)
+ if (until==noLinearTime) {
+ tzend = getSession()->getSystemNowAs(TCTX_UTC);
+ }
+ // Now do the conversion
+ bool ok;
+ if (fMimeDirMode==mimo_old) {
+ // vCalendar 1.0 type RRULE
+ ok = internalToRRULE1(
+ aString,
+ freq,
+ freqmod,
+ interval,
+ firstmask,
+ lastmask,
+ until,
+ untilcontext,
+ GETDBGLOGGER
+ );
+ }
+ else {
+ // iCalendar 2.0 type RRULE
+ ok = internalToRRULE2(
+ aString,
+ freq,
+ freqmod,
+ interval,
+ firstmask,
+ lastmask,
+ until,
+ untilcontext,
+ GETDBGLOGGER
+ );
+ }
+ // if we actually generated a RRULE, the range of used time zones must be updated according
+ // to the recurrence end (date or open end, see tzend calculation above)
+ if (!aString.empty()) {
+ if (fEarliestTZDate==noLinearTime || tzend<fEarliestTZDate) fEarliestTZDate = tzend;
+ if (fLatestTZDate==noLinearTime || tzend>fLatestTZDate) fLatestTZDate = tzend;
+ }
+ return ok;
+ break; // just in case
+ }
+ default:
+ // unknown mode, no value
+ return false;
+ }
+ return false;
+} // TMimeDirProfileHandler::fieldToMIMEString
+
+
+
+/// @brief test if char is part of a line end
+/// @return true if aChar is a line end char
+/// @param [in] aChar charcter to check
+static bool isLineEndChar(appChar aChar)
+{
+ return (aChar=='\x0D') || (aChar=='\x0A');
+} // isLineEndChar
+
+
+/// @brief test if char is end of a line or end of the text (NUL)
+/// @return true if aChar is a line end char or NUL
+/// @param [in] aChar charcter to check
+static bool isEndOfLineOrText(appChar aChar)
+{
+ return (aChar==0) || isLineEndChar(aChar);
+} // isEndOfLineOrText
+
+
+/// @brief test if a line end of any kind is at aText
+/// @note CR,LF,CRLF and CR...CRLF sequences are all considered one line end
+/// @return true if line end found
+/// @param [in/out] aText advance past line end sequence
+static bool testAndSkipLineEnd(cAppCharP &aText)
+{
+ cAppCharP p = aText;
+ bool crFound = false;
+ // skip sequence of CRs
+ while (*p=='\x0D') {
+ p++;
+ crFound = true;
+ }
+ // past all CRs in a row
+ if (*p=='\x0A') {
+ // independent of the number of CRs preceeding, this is a line end including the LF
+ aText = p+1; // past LF
+ return true;
+ }
+ else if (crFound) {
+ // we previously found at least one CR at the beginning, but no LF is following
+ // -> assume CR only line ends, consider first CR as a line end by itself
+ aText++; // skip first CR
+ return true;
+ }
+ // not a line end
+ return false;
+} // testAndSkipLineEnd
+
+
+
+// return incremented pointer pointing to original char or next non-folded char
+static cAppCharP skipfolded(cAppCharP aText, TMimeDirMode aMimeMode, bool qpSoftBreakCancel=false)
+{
+ cAppCharP p = aText;
+ if (testAndSkipLineEnd(p)) {
+ // check for folding sequence
+ if (*p==' ' || *p=='\x09') {
+ // line end followed by space: folding sequence
+ if (aMimeMode==mimo_standard) {
+ // ignore entire sequence (CR,LF,SPACE/TAB)
+ return p+1;
+ }
+ else {
+ // old folding type, LWSP must be preserved
+ return p;
+ }
+ }
+ }
+ else if (qpSoftBreakCancel && *p=='=') {
+ // could be soft break sequence, check for line end
+ p++;
+ if (testAndSkipLineEnd(p)) {
+ return p;
+ }
+ }
+ // not folding sequence, return ptr to char as is
+ return aText;
+} // skipfolded
+
+
+// get next character, while skipping MIME-DIR folding sequences
+// if qpSoftBreakCancel, QUOTED-PRINTABLE encoding style soft-line-break sequences
+// will be eliminated
+static const char *nextunfolded(const char *p, TMimeDirMode aMimeMode, bool qpSoftBreakCancel=false)
+{
+ if (*p==0) return p; // at end of string, do not advance
+ p++; // point to next
+ return skipfolded(p,aMimeMode,qpSoftBreakCancel);
+} // nextunfolded
+
+
+// helper for MIME DIR generation:
+// - apply encoding and charset conversion to values part of property if needed
+static void decodeValue(
+ TEncodingTypes aEncoding, // the encoding to be used
+ TCharSets aCharset, // charset to be applied to 8-bit chars
+ TMimeDirMode aMimeMode, // the MIME mode
+ char aStructSep, // input is structured value, stop when aStructSep is encountered
+ char aAltSep, // alternate separator, also stop when encountering this one (but only if aStructSep is !=0)
+ const char *&aText, // where to start decoding, updated past last char added to aVal
+ string &aVal // decoded data is stored here (possibly some binary data)
+)
+{
+ const int maxseqlen=6;
+ int seqlen;
+ char c,chrs[maxseqlen];
+ const char *p,*q;
+
+ aVal.erase();
+ bool escaped=false;
+ if (aEncoding==enc_quoted_printable) {
+ // decode quoted-printable content
+ p = skipfolded(aText,aMimeMode,true); // get unfolded start point (in case value starts with folding sequence)
+ do {
+ // decode standard content
+ c=*p;
+ if (isEndOfLineOrText(c) || (!escaped && aStructSep!=0 && (c==aStructSep || c==aAltSep))) break; // EOLN and struct separators terminate value
+ // test if escape char (but do not filter it out, as actual de-escaping is done in parseValue() later
+ escaped=(!escaped) && (c=='\\'); // escape next only if we are not escaped already
+ // char found
+ if (c=='=') {
+ uInt16 code;
+ const char *s;
+ char hex[2];
+ s=nextunfolded(p,aMimeMode,true);
+ if (*s==0) break; // end of string
+ hex[0]=*s; // first digit
+ s=nextunfolded(s,aMimeMode,true);
+ if (*s==0) break; // end of string
+ hex[1]=*s; // second digit
+ if (HexStrToUShort(hex,code,2)==2) {
+ p=s; // continue with next char after second digit
+ c=code; // decoded char
+ if (c=='\x0D') {
+ c='\n'; // make newline
+ }
+ else if (c=='\x0A') {
+ p=nextunfolded(p,aMimeMode,true); // advance to char after second digit
+ continue; // ignore LF
+ }
+ }
+ }
+ seqlen=1; // assume logical char consists of single byte
+ chrs[0]=c;
+ do {
+ seqlen=appendCharsAsUTF8(chrs,aVal,aCharset,seqlen); // add char (eventually with UTF8 expansion) to aVal
+ if (seqlen<=1) break; // done
+ // need more bytes to encode entire char
+ for (int i=1;i<seqlen;i++) {
+ p=nextunfolded(p,aMimeMode,true);
+ chrs[i]=*p;
+ }
+ } while(true);
+ p=nextunfolded(p,aMimeMode,true);
+ } while(true);
+ } // quoted printable
+ else if (aEncoding==enc_base64 || aEncoding==enc_b) {
+ // Decode b64
+ // - find end of property value
+ p = skipfolded(aText,aMimeMode,false); // get unfolded start point (in case value starts with folding sequence
+ q=p;
+ while (*q) {
+ if (aStructSep!=0 && (*q==aStructSep || *q==aAltSep))
+ break; // structure separator terminates B64 as well (colon, semicolon and comma never appear in B64)
+ if (isLineEndChar(*q)) {
+ // end of line. Check if this is folding or end of property
+ const char *r=skipfolded(q,aMimeMode,false);
+ if (r==q) {
+ // no folding skipped -> this is the end of the property
+ break;
+ }
+ // skip folding
+ q=r;
+ }
+ else
+ q++;
+ }
+ // - decode base 64
+ uInt32 binsz=0;
+ uInt8 *binP = b64::decode(p, q-p, &binsz);
+ aVal.append((const char *)binP,binsz);
+ sysync_free(binP);
+ // - continue at next char after b64 value
+ p=q;
+ }
+ else {
+ // no (known) encoding
+ p = skipfolded(aText,aMimeMode,false); // get unfolded start point (in case value starts with folding sequence
+ do {
+ c=*p;
+ if (isEndOfLineOrText(c) || (!escaped && aStructSep!=0 && (c==aStructSep || c==aAltSep))) break; // EOLN and structure-sep (usually ;) terminate value
+ // test if escape char (but do not filter it out, as actual de-escaping is done in parseValue() later
+ escaped=(!escaped) && (c=='\\'); // escape next only if we are not escaped already
+ // process char
+ seqlen=1; // assume logical char consists of single byte
+ chrs[0]=c;
+ do {
+ seqlen=appendCharsAsUTF8(chrs,aVal,aCharset,seqlen); // add char (eventually with UTF8 expansion) to aVal
+ if (seqlen<=1) break; // done
+ // need more bytes to encode entire char
+ for (int i=1;i<seqlen;i++) {
+ p=nextunfolded(p,aMimeMode,false);
+ chrs[i]=*p;
+ }
+ } while(true);
+ p=nextunfolded(p,aMimeMode,false);
+ } while(true);
+ } // no encoding
+ // return pointer to terminating char
+ aText=p;
+} // decodeValue
+
+
+// helper for MIME DIR generation:
+// - apply encoding to values part of property if needed
+static void encodeValues(
+ TEncodingTypes aEncoding, // the encoding to be used
+ TCharSets aCharSet, // charset to be applied to 8-bit chars
+ const string &aValuedata, // the data to be encoded (possibly some binary data)
+ string &aPropertytext, // the property string where encoded data is appended
+ bool aDoNotFoldContent // special override for folding
+)
+{
+ const uInt8 *valPtr = (const uInt8 *)aValuedata.c_str();
+ size_t valSz = aValuedata.size();
+ string s;
+ if (aCharSet!=chs_utf8) {
+ // we need to convert to target charset first
+ appendUTF8ToString((const char *)valPtr,s,aCharSet,lem_none,qm_none);
+ valPtr = (const uInt8 *)s.c_str();
+ valSz = s.size();
+ }
+ // - apply encoding if needed
+ appendEncoded(
+ valPtr, // input
+ valSz,
+ aPropertytext, // append output here
+ aEncoding, // desired encoding
+ aDoNotFoldContent ?
+ 0 // disable insertion of soft line breaks
+ : MIME_MAXLINESIZE-1, // limit to standard MIME-linesize, leave one free for eventual extra folding space
+ aPropertytext.size() % MIME_MAXLINESIZE, // current line size
+ true // insert CRs only for softbreaks (for post-processing by folding)
+ );
+} // encodeValues
+
+
+// helper for MIME DIR generation:
+// - fold, copy and terminate (CRLF) property into aString output
+// - \n in input is explicit "fold here" indicator
+// - \b in input is an optional "fold here" indicator, which will appear as space in the
+// output when needed, but will otherwise be discarded
+// - \r in input indicates that a line end must be inserted
+// if aDoSoftBreak==true, only a line break is inserted (QUOTED-PRINTABLE soft line break)
+// otherwise, a full folding sequence (CRLF + space) is inserted. In case of MIME-DIR,
+// QP softbreaks are nothing special, and still need an extra space (as this is reversed on parsing).
+static void finalizeProperty(
+ const char *proptext,
+ string &aString,
+ TMimeDirMode aMimeMode, // MIME mode (older or newer vXXX format compatibility)
+ bool aDoNotFold, // set to prevent folding
+ bool aDoSoftBreak // set to insert QP-softbreaks when \r is encountered, otherwise do a full hard break (which essentially inserts a space for mimo_old)
+)
+{
+ // make sure that allocation does not increase char by char
+ aString.reserve(aString.size()+strlen(proptext)+100);
+ char c;
+ ssize_t n=0,llen=0;
+ ssize_t lastlwsp=-1; // no linear white space found so far
+ bool explf;
+ const char *firstunwritten=proptext; // none written yet
+ while (proptext && (c=*proptext)!=0) {
+ // remember position of last lwsp (space or TAB)
+ if (c==' ' || c==0x09) lastlwsp=n;
+ // next char
+ n++;
+ proptext++;
+ // check for optional break indicator
+ if (c=='\b') {
+ aString.append(firstunwritten,n-1); // copy what we have up to that '\b'
+ firstunwritten+=n; // now pointing to next char after '\b'
+ lastlwsp=0; // usually now pointing to a NON-LWSP, except if by accident a LWSP follows, which is ok as well
+ n=0; // continue checking from here
+ continue; // check next
+ }
+ // update line length
+ llen++;
+ // explicit linefeed flag
+ explf=(c=='\n' || c=='\r');
+ if (aDoNotFold) {
+ // prohibit folding for ugly devices like V3i
+ if (explf) {
+ // append what we have until here
+ n--; // explicit \n or \r is ignored
+ aString.append(firstunwritten,n);
+ // forget the explicit linefeed - and continue
+ n=0;
+ llen=0;
+ firstunwritten=proptext;
+ }
+ }
+ else if ((llen>=MIME_MAXLINESIZE && *proptext) || explf) { // avoid unnecessary folding (there must be something more coming)
+ // folding needed (line gets longer than MIME_MAXLINESIZE or '\n' found in input string)
+ if (aMimeMode==mimo_old && !explf) {
+ // vCard 2.1 type folding, must occur before an LWSP
+ #ifdef DONT_FORCE_FOLD_ITEMS_WITHOUT_LWSP
+ if (lastlwsp<0) continue; // no LWSP found, cannot fold
+ #else
+ if (lastlwsp<0) {
+ // emergency force fold and accept data being shredded
+ // - copy all we have by now
+ aString.append(firstunwritten,n);
+ firstunwritten+=n; // now pointing to next
+ n=0; // none left (not needed, would be reset below anyway)
+ // - insert line break
+ aString.append("\x0D\x0A "); // line break AND an extra shredding space
+ }
+ else
+ #endif
+ {
+ // - copy all up to (but not including) last LWSP
+ aString.append(firstunwritten,lastlwsp);
+ firstunwritten+=lastlwsp; // now pointing to LWSP (or non-LWSP in case of '\b')
+ n-=lastlwsp; // number of chars left (including LWSP)
+ // - insert line break
+ aString.append("\x0D\x0A"); // line break
+ if (*firstunwritten!=' ' && *firstunwritten!=0x09)
+ aString+=' '; // breaking at location indicated by '\b', LWSP must be added
+ // - copy rest scanned so far (except in '\b' case, this begins with an LWSP)
+ aString.append(firstunwritten,n);
+ }
+ // we are on a new line now
+ n=0;
+ lastlwsp=-1;
+ llen=0;
+ firstunwritten=proptext;
+ }
+ else {
+ // MIME-DIR type folding, can occur anywhere and *adds* a LWSP (which is removed at unfolding later)
+ // or mimo-old type folding containing explicit CR(LF)s -> break here
+ // - copy line so far to output
+ if (explf)
+ n--; // explicit \n or \r is not copied, but only causes line break to occur
+ aString.append(firstunwritten,n);
+ aString.append("\x0D\x0A"); // line break
+ if (
+ (c!='\r' && aMimeMode==mimo_standard) || // folding indicator and MIME-DIR -> folding always must insert extra space
+ (c=='\r' && !aDoSoftBreak) // soft-break indicator, but not in softbreak mode (i.e. B64 input) -> always insert extra space
+ )
+ aString+=' '; // not only soft line break, but MIMD-DIR type folding
+ n=0;
+ llen=0;
+ firstunwritten=proptext;
+ }
+ }
+ }
+ // append rest
+ aString.append(firstunwritten,n);
+ // terminate property
+ aString.append("\x0D\x0A"); // CRLF
+} // finalizeProperty
+
+
+// results for generateValue:
+#define GENVALUE_NOTSUPPORTED 0 // field not supported
+#define GENVALUE_EXHAUSTED 1 // array field exhausted
+#define GENVALUE_EMPTYELEMENT 2 // array field empty
+#define GENVALUE_EMPTY 3 // non-array field empty
+#define GENVALUE_ELEMENT 4 // non-empty array element
+#define GENVALUE_NONEMPTY 5 // non-empty non-array value
+
+// helper for generateMimeDir()
+// - generate parameter or property value(list),
+// returns: GENVALUE_xxx
+sInt16 TMimeDirProfileHandler::generateValue(
+ TMultiFieldItem &aItem, // the item where data comes from
+ const TConversionDef *aConvDefP,
+ sInt16 aBaseOffset, // basic fid offset to use
+ sInt16 aRepOffset, // repeat offset, adds to aBaseOffset for non-array fields, is array index for array fields
+ string &aString, // where value is ADDED
+ char aSeparator, // separator to be used between values if field contains multiple values in a list separated by confdef->combineSep
+ TMimeDirMode aMimeMode, // MIME mode (older or newer vXXX format compatibility)
+ bool aParamValue, // set if generating parameter value (different escaping rules, i.e. colon must be escaped, or entire value double-quoted)
+ bool aStructured, // set if value consists of multiple values (needs semicolon content escaping)
+ bool aCommaEscape, // set if "," content escaping is needed (for values in valuelists like TYPE=TEL,WORK etc.)
+ TEncodingTypes &aEncoding, // modified if special value encoding is required
+ bool &aNonASCII, // set if any non standard 7bit ASCII-char is contained
+ char aFirstChar // will be appended before value if there is any value (and a '\b' optional break indicator is appended as well)
+)
+{
+ string vallist; // as received from fieldToMIMEString()
+ string val; // single value
+ string outval; // entire value (list) escaped
+ char c;
+
+ // determine field ID
+ bool isarray = false; // no array by default
+ sInt16 fid=aConvDefP->fieldid;
+ if (fid>=0) {
+ // field has storage
+ // - fid is always offset by baseoffset
+ fid += aBaseOffset;
+ // - adjust now
+ isarray = aItem.adjustFidAndIndex(fid,aRepOffset);
+ // generate only if available in both source and target (or non-SyncML context)
+ if (isFieldAvailable(aItem,fid)) {
+ // find out if value exists
+ if (aItem.isAssigned(fid)) {
+ // - field has a value assigned (altough this might be empty string)
+ // determine max size to truncate value if needed
+ outval.erase();
+ sInt32 valsiz=0; // net size of value
+ //%%%% getTargetItemType???
+ sInt32 maxSiz=aItem.getTargetItemType()->getFieldOptions(fid)->maxsize;
+ if (maxSiz==FIELD_OPT_MAXSIZE_UNKNOWN || maxSiz==FIELD_OPT_MAXSIZE_NONE)
+ maxSiz = 0; // no size restriction
+ bool noTruncate=aItem.getTargetItemType()->getFieldOptions(fid)->notruncate;
+ // check for BLOB values
+ if (aConvDefP->convmode==CONVMODE_BLOB_B64) {
+ // no value lists, escaping, enums. Simply set value and encoding
+ TItemField *fldP = aItem.getArrayField(fid,aRepOffset,true); // existing array elements only
+ if (!fldP) return GENVALUE_EXHAUSTED; // no leaf field - must be exhausted array (fldP==NULL is not possible here for non-arrays)
+ if (fldP->isUnassigned()) return GENVALUE_EMPTYELEMENT; // must be empty element empty element, but field supported (fldP==NULL is not possible here for non-arrays)
+ // check max size and truncate if needed
+ if (maxSiz && sInt32(fldP->getStringSize())>maxSiz) {
+ if (noTruncate || getSession()->getSyncMLVersion()<syncml_vers_1_2) {
+ // truncate not allowed (default for pre-SyncML 1.2 for BLOB fields)
+ PDEBUGPRINTFX(DBG_ERROR+DBG_GEN,("BLOB value exceeds max size (%ld) and cannot be truncated -> omit", (long)maxSiz));
+ return GENVALUE_NOTSUPPORTED; // treat it as if field was not supported locally
+ }
+ }
+ // append to existing string
+ fldP->appendToString(outval,maxSiz);
+ // force B64 encoding
+ aEncoding=enc_base64;
+ aNonASCII=false;
+ }
+ else {
+ // apply custom field(s)-to-string translation if needed
+ if (!fieldToMIMEString(aItem,fid,aRepOffset,aConvDefP,vallist)) {
+ // check if no value because array was exhausted
+ if (aItem.getArrayField(fid,aRepOffset,true))
+ return isarray ? GENVALUE_EMPTYELEMENT : GENVALUE_EMPTY; // no value (but field supported)
+ else
+ return GENVALUE_EXHAUSTED; // no leaf field - must be exhausted array
+ }
+ // separate value list into multiple values if needed
+ const char *lp = vallist.c_str(); // list item pointer
+ const char *sp; // start of item pointer (helper)
+ sInt32 n;
+ while (*lp!=0) {
+ // find (single) input value string's end
+ for (sp=lp,n=0; (c=*lp)!=0; lp++, n++) {
+ if (c==aConvDefP->combineSep) break;
+ }
+ // - n=size of input value, p=ptr to end of value (0 or sep)
+ val.assign(sp,n);
+ // perform enum translation if needed
+ if (aConvDefP->enumdefs) {
+ const TEnumerationDef *enumP = aConvDefP->findEnumByVal(val.c_str());
+ if (enumP) {
+ PDEBUGPRINTFX(DBG_GEN+DBG_EXOTIC,("Val='%s' translated to enumName='%s' mode=%s", val.c_str(), TCFG_CSTR(enumP->enumtext), EnumModeNames[enumP->enummode]));
+ if (enumP->enummode==enm_ignore)
+ val.erase(); // ignore -> make value empty as empty values are never stored
+ else if (enumP->enummode==enm_prefix) {
+ // replace value prefix by text prefix
+ size_t n=TCFG_SIZE(enumP->enumval);
+ val.replace(0,n,TCFG_CSTR(enumP->enumtext)); // replace val prefix by text prefix
+ }
+ else {
+ // simply use translated value
+ val=enumP->enumtext;
+ }
+ }
+ else {
+ PDEBUGPRINTFX(DBG_GEN+DBG_EXOTIC,("No translation found for Val='%s'", val.c_str()));
+ }
+ }
+ // - val is now translated enum (or original value if value does not match any enum text)
+ valsiz+=val.size();
+ // perform escaping and determine need for encoding
+ bool spaceonly = true;
+ bool firstchar = true;
+ sInt32 wordSize=0;
+ for (const char *p=val.c_str();(c=*p)!=0 && (c!=aConvDefP->combineSep);p++) {
+ // process char
+ // - check for whitespace
+ if (!isspace(c)) {
+ spaceonly = false; // does not consist of whitespace only
+ wordSize++; // count consecutive non-spaces
+ if (aMimeMode==mimo_old && aEncoding==enc_none && wordSize>MIME_MAXLINESIZE/2) {
+ // If text contains words with critical (probably unfoldable) size in mimo-old, select quoted printable encoding
+ aEncoding=enc_quoted_printable;
+ }
+ }
+ else {
+ wordSize = 0; // new word starts
+ }
+ // only text must be fully escaped, turn escaping off for RRULE (RECUR type)
+ bool noescape = aConvDefP->convmode==CONVMODE_RRULE;
+ // escape reserved chars
+ switch (c) {
+ case '"':
+ if (firstchar && aParamValue && aMimeMode==mimo_standard) goto do_escape; // if param value starts with a double quote, we need to escape it because param value can be in double-quote-enclosed form
+ goto add_char; // otherwise, just add
+ case ',':
+ // in MIME-DIR, always escape commas, in pre-MIME-DIR only if usage in value list requires it
+ if (noescape || (!aCommaEscape && aMimeMode==mimo_old)) goto add_char;
+ goto do_escape;
+ case ':':
+ // always escape colon in parameters
+ if (!aParamValue) goto add_char;
+ goto do_escape;
+ case '\\':
+ // Backslash must always be escaped
+ // - for MIMO-old: at least Nokia 9210 does it this way
+ // - for MIME-DIR: specified in the standard
+ goto do_escape;
+ case ';':
+ // in MIME-DIR, always escape semicolons, in pre-MIME-DIR only in parameters and structured values
+ if (noescape || (!aParamValue && !aStructured && aMimeMode==mimo_old)) goto add_char;
+ do_escape:
+ // escape chars with backslash
+ outval+='\\';
+ goto out_char;
+ case '\r':
+ // ignore returns
+ break;
+ case '\n':
+ // quote linefeeds
+ if (aMimeMode==mimo_old) {
+ if (aEncoding==enc_none) {
+ // For line ends in mimo_old: select quoted printable encoding
+ aEncoding=enc_quoted_printable;
+ }
+ // just pass it, will be encoded later
+ goto add_char;
+ }
+ else {
+ // MIME-DIR: use quoted C-style notation
+ outval.append("\\n");
+ }
+ break;
+ default:
+ add_char:
+ // prevent adding space-only for params
+ if (spaceonly && aParamValue) break; // just check next
+ out_char:
+ // check for non ASCII and set flag if found
+ if ((uInt8)c > 0x7F) aNonASCII=true;
+ // just copy to output
+ outval+=c;
+ firstchar = false; // first char is out
+ break;
+ }
+ } // for all chars in val item
+ // go to next item in the val list (if any)
+ if (*lp!=0) {
+ // more items in the list
+ // - add separator if previous one is not empty param value
+ if (!(spaceonly && aParamValue)) {
+ outval+=aSeparator;
+ valsiz++; // count it as part of the value
+ }
+ lp++; // skip input list separator
+ }
+ // check for truncation needs (do not truncate parameters, ever)
+ if (maxSiz && valsiz>maxSiz && !aParamValue) {
+ // size exceeded
+ if (noTruncate) {
+ // truncate not allowed
+ PDEBUGPRINTFX(DBG_ERROR+DBG_GEN,(
+ "Value '%" FMT_LENGTH(".40") "s' exceeds %ld chars net length but is noTruncate -> omit",
+ FMT_LENGTH_LIMITED(40,outval.c_str()),
+ (long)maxSiz
+ ));
+ // treat it as if field was not supported locally
+ return GENVALUE_NOTSUPPORTED;
+ }
+ else {
+ // truncate allowed, shorten output accordingly
+ outval.erase(outval.size()-(valsiz-maxSiz));
+ PDEBUGPRINTFX(DBG_GEN,(
+ "Truncated value '%" FMT_LENGTH(".40") "s' to %ld chars net length (maxSize)",
+ FMT_LENGTH_LIMITED(40,outval.c_str()),
+ (long)maxSiz
+ ));
+ // do not add more chars
+ break;
+ }
+ }
+ } // while value chars available
+ } // not BLOB conversion
+ // value generated in outval (altough it might be an empty string)
+ } // if field assigned
+ else {
+ // not assigned. However a not assigned array means an array with no elements, which
+ // is the same as an exhausted array
+ return isarray ? GENVALUE_EXHAUSTED : GENVALUE_NOTSUPPORTED; // array is exhaused, non-array unassigned means not available
+ }
+ } // source and target both support the field (or field belongs to mandatory property)
+ else
+ return GENVALUE_NOTSUPPORTED; // field not supported by either source or target (and not mandatory) -> do not generate value
+ } // if fieldid exists
+ else {
+ // could be special conversion using no data or data from
+ // internal object variables (such as VERSION value)
+ if (fieldToMIMEString(aItem,FID_NOT_SUPPORTED,0,aConvDefP,vallist)) {
+ // got some output, use it as value
+ outval=vallist;
+ }
+ else
+ // no value, no output
+ return GENVALUE_NOTSUPPORTED; // field not supported
+ }
+ // now we have a value in outval, check if encoding needs to be applied
+ // - check if we should select QUOTED-PRINTABLE because of nonASCII
+ if (aNonASCII && fDoQuote8BitContent && aEncoding==enc_none)
+ aEncoding=enc_quoted_printable;
+ // just append
+ if (!outval.empty() && aFirstChar!=0) {
+ aString+=aFirstChar; // we have a value, add sep char first
+ aString+='\b'; // and an optional break indicator
+ }
+ aString.append(outval);
+ // done
+ return outval.empty()
+ ? (isarray ? GENVALUE_EMPTYELEMENT : GENVALUE_EMPTY) // empty
+ : (isarray ? GENVALUE_ELEMENT : GENVALUE_NONEMPTY); // non empty
+} // TMimeDirProfileHandler::generateValue
+
+
+
+// generate parameters for one property instance
+// - returns true if parameters with shownonempty=true were generated
+bool TMimeDirProfileHandler::generateParams(
+ TMultiFieldItem &aItem, // the item where data comes from
+ string &aString, // the string to add parameters to
+ const TPropertyDefinition *aPropP, // the property to generate (all instances)
+ TMimeDirMode aMimeMode, // MIME mode (older or newer vXXX format compatibility)
+ sInt16 aBaseOffset,
+ sInt16 aRepOffset,
+ TPropNameExtension *aPropNameExt // propname extension for generating musthave param values
+)
+{
+ const TParameterDefinition *paramP;
+ bool paramstarted;
+ char sep=0; // separator for value lists
+ bool nonasc=false;
+ TEncodingTypes encoding;
+ string paramstr;
+ bool showalways=false;
+
+ // Generate parameters
+ // Note: altough positional values are always the same, non-positional values
+ // can vary from repetition to repetition and can be mixed with the
+ // positional values. So we must generate the mixture again for
+ // every repetition.
+ // - check all parameters for musthave values
+ paramP = aPropP->parameterDefs;
+ while (paramP) {
+ // parameter not started yet
+ paramstarted=false;
+ // process param only if matching mode
+ if (mimeModeMatch(paramP->modeDependency)) {
+ // first append extendsname param values
+ if (paramP->extendsname && aPropNameExt) {
+ const TEnumerationDef *enumP = paramP->convdef.enumdefs;
+ while (enumP) {
+ if (enumP->nameextid>=0) {
+ // value is relevant for name extension, check if required for this param
+ if ((((TNameExtIDMap)1<<enumP->nameextid) & (aPropNameExt->musthave_ids | aPropNameExt->addtlSend_ids))!=0) {
+ // found param value which is required or flagged to be sent additionally as name extension
+ if (!paramstarted) {
+ paramstarted=true;
+ aString+=';'; // param always starts with ;
+ if (paramP->defaultparam && (aMimeMode==mimo_old)) {
+ // default param, values are written like a list of params
+ sep=';'; // separator, in case other values follow
+ }
+ else {
+ // normal parameter, first add param separator and name
+ // - lead-in
+ aString.append(paramP->paramname);
+ aString+='=';
+ // - separator, in case other values follow
+ sep=','; // value list separator is comma by default
+ }
+ }
+ else {
+ // add separator for one more value
+ aString+=sep;
+ }
+ // add value
+ aString.append(enumP->enumtext);
+ } // if enum value is a "must have" value for name extension
+ } // if enum value is relevant to name extension
+ // next enum value
+ enumP=enumP->next;
+ } // while enum values
+ } // if extendsname
+ // append value(s) if there is an associated field
+ paramstr.erase(); // none to start with
+ if (paramP->convdef.fieldid!=FID_NOT_SUPPORTED) {
+ if (!paramstarted) {
+ // Note: paramstarted must not be set here, as empty value might prevent param from being written
+ // parameter starts with ";"
+ paramstr+=';';
+ if (paramP->defaultparam && (aMimeMode==mimo_old)) {
+ // default param, values are written like a list of params
+ sep=';';
+ }
+ else {
+ // normal parameter, first add name
+ paramstr.append(paramP->paramname);
+ paramstr+='=';
+ sep=','; // value list separator is comma by default
+ }
+ }
+ else {
+ // already started values, just add more
+ // - next value starts with a separator
+ paramstr+=sep;
+ }
+ // add parameter value(list)
+ encoding=enc_none; // parameters are not encoded
+ // NOTE: only non-empty parameters are generated
+ // NOTE: parameters themselves cannot have a value list that is stored in an array,
+ // but parameters of repeating properties can be stored in array elements (using the
+ // same index as for the property itself)
+ // Note: Escape commas if separator is a comma
+ if (generateValue(aItem,&(paramP->convdef),aBaseOffset,aRepOffset,paramstr,sep,aMimeMode,true,false,sep==',',encoding,nonasc)>=GENVALUE_ELEMENT) {
+ // value generated, add parameter name/value (or separator/value for already started params)
+ aString.append(paramstr);
+ paramstarted=true; // started only if we really have appended something at all
+ }
+ } // if field defined for this param
+ // update show status
+ if (paramP->shownonempty && paramstarted)
+ showalways=true; // param has a value and must make property show
+ }
+ // next param
+ paramP=paramP->next;
+ } // while params
+ return showalways;
+} // TMimeDirProfileHandler::generateParams
+
+
+// generateProperty return codes:
+#define GENPROP_EXHAUSTED 0 // nothing generated because data source exhausted (or field not supported)
+#define GENPROP_EMPTY 1 // nothing generated because empty value (but field supported)
+#define GENPROP_NONEMPTY 2 // something generated
+
+
+
+// helper for generateMimeDir(), expansion of property according to nameExts
+void TMimeDirProfileHandler::expandProperty(
+ TMultiFieldItem &aItem, // the item where data comes from
+ string &aString, // the string to add properties to
+ const char *aPrefix, // the prefix (property name)
+ const TPropertyDefinition *aPropP, // the property to generate (all instances)
+ TMimeDirMode aMimeMode // MIME mode (older or newer vXXX format compatibility)
+)
+{
+ // scan nameExts to generate name-extended variants and repetitions
+ TPropNameExtension *propnameextP = aPropP->nameExts;
+ if (!propnameextP) {
+ // no name extensions -> this is a non-repeating property
+ // just generate once, even if empty (except if it has suppressempty set)
+ generateProperty(
+ aItem, // the item where data comes from
+ aString, // the string to add properties to
+ aPrefix, // the prefix (property name)
+ aPropP, // the property to generate
+ 0, // field ID offset to be used
+ 0, // additional repeat offset / array index
+ aMimeMode, // MIME mode (older or newer vXXX format compatibility)
+ false // if set, a property with only empty values will never be generated
+ );
+ }
+ else {
+ // scan name extensions
+ sInt16 generated=0;
+ sInt16 maxOccur=0; // default to no limit
+ while (propnameextP) {
+ sInt16 baseoffs=propnameextP->fieldidoffs;
+ sInt16 repoffs=0; // no repeat offset yet
+ if (baseoffs!=OFFS_NOSTORE && !propnameextP->readOnly) {
+ // we can address fields for this property and it's not readonly (parsing variant)
+ // generate value part
+ sInt16 n=propnameextP->maxRepeat;
+ // check for value list
+ if (aPropP->valuelist) {
+ // property contains a value list -> all repetitions are shown within ONE property instance
+ // NOTE: generateProperty will exhaust possible repeats
+ generateProperty(
+ aItem, // the item where data comes from
+ aString, // the string to add properties to
+ aPrefix, // the prefix (property name)
+ aPropP, // the property to generate
+ baseoffs, // field ID offset to be used
+ repoffs, // additional repeat offset / array index
+ aMimeMode, // MIME mode (older or newer vXXX format compatibility)
+ propnameextP->minShow<1, // suppress if fewer to show than 1 (that is, like suppressempty in this case)
+ propnameextP // propname extension for generating musthave param values and maxrep/repinc for valuelists
+ );
+ }
+ else {
+ // now generate separate properties for all repetitions
+ // Note: strategy is to keep order as much as possible (completely if
+ // minShow is >= maxRepeat
+ sInt16 emptyRepOffs=-1;
+ // get occurrence limit as provided by remote
+ for (sInt16 i=0; i<aPropP->numValues; i++) {
+ sInt16 fid=aPropP->convdefs[0].fieldid;
+ if (fid>=0) {
+ if (fRelatedDatastoreP) {
+ // only if datastore is related we are in SyncML context, otherwise we should not check maxOccur
+ maxOccur = aItem.getItemType()->getFieldOptions(fid)->maxoccur;
+ }
+ else
+ maxOccur = 0; // no limit
+ // Note: all value fields of the property will have the same maxOccur, so we can stop here
+ break;
+ }
+ }
+ do {
+ // generate property for this repetition
+ // - no repeating within generateProperty takes place!
+ sInt16 genres = generateProperty(
+ aItem, // the item where data comes from
+ aString, // the string to add properties to
+ aPrefix, // the prefix (property name)
+ aPropP, // the property to generate
+ baseoffs, // field ID offset to be used
+ repoffs, // additional repeat offset / array index
+ aMimeMode, // MIME mode (older or newer vXXX format compatibility)
+ propnameextP->minShow-generated<n, // suppress if fewer to show than remaining repeats
+ propnameextP // propname extension for generating musthave param values
+ );
+ if (genres==GENPROP_NONEMPTY) {
+ // generated a property
+ generated++;
+ }
+ else {
+ // nothing generated
+ if (emptyRepOffs<0) emptyRepOffs=repoffs; // remember empty
+ // check for array repeat, in which case exhausted array or non-supported field will stop generating
+ if (propnameextP->maxRepeat==REP_ARRAY && genres==GENPROP_EXHAUSTED) break; // exit loop if any only if array exhausted
+ }
+ // one more generated of the maximum possible (note: REP_ARRAY=32k, so this will not limit an array)
+ n--;
+ repoffs+=propnameextP->repeatInc;
+ // end generation if remote's maxOccur limit is reached
+ if (maxOccur && generated>=maxOccur) {
+ PDEBUGPRINTFX(DBG_GEN,(
+ "maxOccur (%hd) for Property '%s' reached - no more instances will be generated",
+ maxOccur,
+ TCFG_CSTR(aPropP->propname)
+ ));
+ break;
+ }
+ } while(n>0);
+ // add empty ones if needed
+ while (generated<propnameextP->minShow && emptyRepOffs>=0 && !(maxOccur && generated>=maxOccur)) {
+ // generate empty ones (no suppression)
+ generateProperty(aItem,aString,aPrefix,aPropP,baseoffs,emptyRepOffs,aMimeMode,false);
+ generated++; // count as generated anyway (even in case generation of empty is globally turned off)
+ }
+ } // repeat properties when we have repeating enabled
+ } // if name extension is stored
+ propnameextP=propnameextP->next;
+ // stop if maxOccur reached
+ if (maxOccur && generated>=maxOccur) break;
+ } // while nameexts
+ } // if nameexts at all
+} // TMimeDirProfileHandler::expandProperty
+
+
+// helper for expandProperty: generates property
+// returns: GENPROP_xxx
+sInt16 TMimeDirProfileHandler::generateProperty(
+ TMultiFieldItem &aItem, // the item where data comes from
+ string &aString, // the string to add properties to
+ const char *aPrefix, // the prefix (property name)
+ const TPropertyDefinition *aPropP, // the property to generate (all instances)
+ sInt16 aBaseOffset, // field ID offset to be used
+ sInt16 aRepeatOffset, // additional repeat offset / array index
+ TMimeDirMode aMimeMode, // MIME mode (older or newer vXXX format compatibility)
+ bool aSuppressEmpty, // if set, a property with only empty values will not be generated
+ TPropNameExtension *aPropNameExt // propname extension for generating musthave param values and maxrep/repinc for valuelists
+)
+{
+ string proptext; // unfolded property text
+ proptext.reserve(300); // not too small
+ string elemtext; // single element (value or param) text
+ TEncodingTypes encoding;
+ bool nonasc=false;
+
+ // - reset TZID presence flag
+ fPropTZIDtctx = TCTX_UNKNOWN;
+ // - init string with name and (eventually) parameters that are constant over all repetitions
+ proptext=aPrefix;
+ bool anyvaluessupported=false; // at least one of the main values must be supported by the remote in order to generate property at all
+ bool arrayexhausted=false; // flag will be set if a main value was not generated because array exhausted
+ // - append parameter values
+ // anyvalues gets set if a parameter with shownonempty attribute was generated
+ bool anyvalues=generateParams(
+ aItem, // the item where data comes from
+ proptext, // where params will be appended
+ aPropP, // the property definition
+ aMimeMode,
+ aBaseOffset,
+ aRepeatOffset,
+ aPropNameExt
+ );
+ // - append value(s)
+ encoding=enc_none; // default is no encoding
+ sInt16 v=0; // value counter
+ nonasc=false; // assume plain ASCII
+ sInt16 genres;
+ TEncodingTypes enc;
+ bool na;
+ const TConversionDef *convP;
+ sInt16 maxrep=1,repinc=1;
+ if (aPropNameExt) {
+ maxrep=aPropNameExt->maxRepeat;
+ repinc=aPropNameExt->repeatInc;
+ }
+ // generate property contents
+ if (aPropP->valuelist) {
+ // property with value list
+ // NOTE: convdef[0] is used for all values, aRepeatOffset changes
+ convP = &(aPropP->convdefs[0]);
+ // - now iterate over available repeats or array contents
+ while(aRepeatOffset<maxrep*repinc || maxrep==REP_ARRAY) {
+ // generate one value
+ enc=encoding;
+ na=false;
+ genres=generateValue(
+ aItem,
+ convP,
+ aBaseOffset, // offset relative to base field
+ aRepeatOffset, // additional offset or array index
+ elemtext,
+ aPropP->valuesep, // use valuelist separator between multiple values eventually generated from a list in a single field (e.g. CATEGORIES)
+ aMimeMode,
+ false, // not a param
+ true, // always escape ; in valuelist properties
+ aPropP->valuesep==',' || aPropP->altvaluesep==',', // escape commas if one of the separators is a comma
+ enc,
+ na,
+ v>0 ? aPropP->valuesep : 0 // separate with specified multi-value-delimiter if not first value
+ );
+ // check if something was generated
+ if (genres>=GENVALUE_ELEMENT) {
+ // generated something, might have caused encoding/noasc change
+ encoding=enc;
+ nonasc=nonasc || na;
+ }
+ // update if we have at least one value of this property supported (even if empty) by the remote party
+ if (genres>GENVALUE_NOTSUPPORTED) anyvaluessupported=true;
+ if (genres==GENVALUE_EXHAUSTED) arrayexhausted=true; // for at least one component of the property, the array is exhausted
+ // update if we have any value now (even if only empty)
+ // - generate empty property according to
+ // - aSuppressEmpty
+ // - session-global fDontSendEmptyProperties
+ // - supressEmpty property flag in property definition
+ // - if no repeat (i.e. no aPropNameExt), exhausted array is treated like empty value (i.e. rendered unless suppressempty set)
+ anyvalues = anyvalues ||
+ (genres>=(aSuppressEmpty || fDontSendEmptyProperties || aPropP->suppressEmpty ? GENVALUE_ELEMENT : (aPropNameExt ? GENVALUE_EMPTYELEMENT : GENVALUE_EXHAUSTED)));
+ // count effective value appended
+ v++;
+ // update repeat offset
+ aRepeatOffset+=repinc;
+ // check for array mode - stop if array is exhausted or field not supported
+ if (maxrep==REP_ARRAY && genres<=GENVALUE_EXHAUSTED) break;
+ }
+ }
+ else {
+ // property with individual values (like N)
+ // NOTE: field changes with different convdefs, offsets remain stable
+ arrayexhausted = true; // assume all arrays exhausted unless we find at least one non-exhausted array
+ bool somearrays = false; // no arrays yet
+ do {
+ convP = &(aPropP->convdefs[v]);
+ // generate one value
+ enc=encoding;
+ na=false;
+ genres=generateValue(
+ aItem,
+ convP,
+ aBaseOffset, // base offset, relative to
+ aRepeatOffset, // repeat offset or array index
+ elemtext, // value will be stored here (might be binary in case of BLOBs, but then encoding will be set)
+ ',', // should for some exotic reason values consist of a list, separate it by "," (";" is reserved for structured values)
+ aMimeMode,
+ false,
+ aPropP->numValues>1, // structured value, escape ";"
+ aPropP->altvaluesep==',', // escape commas if alternate separator is a comma
+ enc, // will receive needed encoding (usually B64 for binary values)
+ na
+ );
+ //* %%% */ PDEBUGPRINTFX(DBG_EXOTIC,("generateValue #%hd for property '%s' returns genres==%hd",v,TCFG_CSTR(aPropP->propname),genres));
+ // check if something was generated
+ if (genres>=GENVALUE_ELEMENT) {
+ // generated something, might have caused encoding/noasc change
+ encoding=enc;
+ nonasc=nonasc || na;
+ }
+ // update if we have at least one value of this property supported (even if empty) by the remote party
+ if (genres>GENVALUE_NOTSUPPORTED) anyvaluessupported=true;
+ if (genres==GENVALUE_ELEMENT || genres==GENVALUE_EMPTYELEMENT) {
+ arrayexhausted = false; // there is at least one non-exhausted array we're reading from (even if only empty value)
+ somearrays = true; // generating from array
+ }
+ else if (genres==GENVALUE_EXHAUSTED)
+ somearrays = true; // generating from array
+ // update if we have any value now (even if only empty)
+ // - generate empty property according to
+ // - aSuppressEmpty
+ // - session-global fDontSendEmptyProperties
+ // - supressEmpty property flag in property definition
+ // - if no repeat (i.e. no aPropNameExt), exhausted array is treated like empty value (i.e. rendered unless suppressempty set)
+ anyvalues = anyvalues ||
+ (genres>=(aSuppressEmpty || fDontSendEmptyProperties || aPropP->suppressEmpty ? GENVALUE_ELEMENT : (aPropNameExt ? GENVALUE_EMPTYELEMENT : GENVALUE_EXHAUSTED)));
+ // insert delimiter if not last value
+ v++;
+ if (v>=aPropP->numValues) break; // done with all values
+ // add delimiter for next value
+ elemtext+=aPropP->valuesep;
+ // add break indicator
+ elemtext+='\b';
+ } while(true);
+ // if none of the data sources is an array, we can't be exhausted.
+ if (!somearrays) arrayexhausted = false;
+ }
+ // - finalize property if it contains supported fields at all (or is mandatory)
+ if ((anyvaluessupported && anyvalues) || aPropP->mandatory) {
+ // - generate encoding parameter if needed
+ if (encoding!=enc_none) {
+ // in MIME-DIR, only "B" is allowed for binary, for vCard 2.1 it is "BASE64"
+ if (encoding==enc_base64 || encoding==enc_b) {
+ encoding = aMimeMode==mimo_standard ? enc_b : enc_base64;
+ }
+ // add the parameter
+ proptext.append(";ENCODING=");
+ proptext.append(MIMEEncodingNames[encoding]);
+ }
+ // - generate charset parameter if needed
+ // NOTE: MIME-DIR based formats do NOT have the CHARSET attribute any more!
+ if (nonasc && aMimeMode==mimo_old && fDefaultOutCharset!=chs_ansi) {
+ // non-ASCII chars contained, generate property telling what charset is used
+ proptext.append(";CHARSET=");
+ proptext.append(MIMECharSetNames[fDefaultOutCharset]);
+ }
+ // - separate value from property text
+ proptext+=':';
+ // - append (probably encoded) values now, always in UTF-8
+ encodeValues(encoding,fDefaultOutCharset,elemtext,proptext,fDoNotFoldContent);
+ // - fold, copy and terminate (CRLF) property into aString output
+ finalizeProperty(proptext.c_str(),aString,aMimeMode,fDoNotFoldContent,encoding==enc_quoted_printable);
+ // - property generated
+ return GENPROP_NONEMPTY;
+ }
+ else {
+ // Note: it is essential to return GENPROP_EXHAUSTED if no values are supported for this property at
+ // all (otherwise caller might loop endless trying to generate a non-empty property
+ return
+ anyvaluessupported
+ ? (arrayexhausted ? GENPROP_EXHAUSTED : GENPROP_EMPTY) // no property generated
+ : GENPROP_EXHAUSTED; // no values supported means "exhausted" as well
+ }
+} // TMimeDirProfileHandler::generateProperty
+
+
+
+// generate MIME-DIR from item into string object
+void TMimeDirProfileHandler::generateMimeDir(TMultiFieldItem &aItem, string &aString)
+{
+ // clear string
+ aString.reserve(3000); // not too small
+ aString.erase();
+ // reset item time zone before generating
+ fHasExplicitTZ = false; // none set explicitly
+ fItemTimeContext = fReceiverTimeContext; // default to receiver context
+ fUsedTCtxSet.clear(); // no TZIDs used yet
+ fEarliestTZDate = noLinearTime; // reset range of generated timestamps related to a TZID or TZ/DAYLIGHT
+ fLatestTZDate = noLinearTime;
+ fVTimeZonePendingProfileP = NULL; // no VTIMEZONE pending for generation
+ fVTimeZoneInsertPos = 0; // no insert position yet
+ // recursively generate levels
+ generateLevels(aItem,aString,fProfileDefinitionP);
+ // now generate VTIMEZONE, if needed
+ if (fVTimeZonePendingProfileP) {
+ string s, val, vtz;
+ vtz.erase();
+ // generate needed vTimeZones (according to fUsedTCtxSet)
+ for (TTCtxSet::iterator pos=fUsedTCtxSet.begin(); pos!=fUsedTCtxSet.end(); pos++) {
+ // - calculate first and last year covered by timestamps in this record
+ sInt16 startYear=0,endYear=0;
+ if (fEarliestTZDate && fProfileCfgP->fVTimeZoneGenMode!=vtzgen_current) {
+ // dependent on actually created dates
+ lineartime2date(fEarliestTZDate, &startYear, NULL, NULL);
+ lineartime2date(fLatestTZDate, &endYear, NULL, NULL);
+ // there is at least one date in the record
+ switch (fProfileCfgP->fVTimeZoneGenMode) {
+ case vtzgen_start:
+ endYear = startYear; // only show for start of range
+ break;
+ case vtzgen_end:
+ startYear = endYear; // only show for end of range
+ break;
+ case vtzgen_range:
+ // pass both start and end year
+ break;
+ case vtzgen_openend:
+ // pass start year but request that all rules from start up to the current date are inlcuded
+ endYear = 0;
+ break;
+ case vtzgen_current:
+ case numVTimeZoneGenModes:
+ // case statement to keep gcc happy, will not be reached because of if() above
+ break;
+ }
+ }
+ // - lead-in
+ s="BEGIN:";
+ s.append(fVTimeZonePendingProfileP->levelName);
+ finalizeProperty(s.c_str(),vtz,fMimeDirMode,false,false);
+ // - generate raw string
+ //%%% endYear is not yet implemented in internalToVTIMEZONE(), fTzIdGenMode has only the olson option for now
+ internalToVTIMEZONE(*pos, val, getSessionZones(), NULL, startYear, endYear, fProfileCfgP->fTzIdGenMode==tzidgen_olson ? "o" : NULL);
+ size_t i,n = 0;
+ while (val.size()>n) {
+ i = val.find('\n',n); // next line end
+ if (i==string::npos) i=val.size();
+ if (i-n>1) {
+ // more than one char = not only a trailing line end
+ s.assign(val,n,i-n);
+ // finalize and add property
+ finalizeProperty(s.c_str(),vtz,fMimeDirMode,false,false);
+ // advance cursor beyond terminating LF
+ n=i+1;
+ }
+ }
+ // - lead out
+ s="END:";
+ s.append(fVTimeZonePendingProfileP->levelName);
+ finalizeProperty(s.c_str(),vtz,fMimeDirMode,false,false);
+ } // for
+ // now insert the VTIMEZONE into the output string (so eventually making it appear BEFORE the
+ // properties that use TZIDs)
+ aString.insert(fVTimeZoneInsertPos, vtz);
+ // done
+ fVTimeZonePendingProfileP = NULL;
+ } // if pending VTIMEZONE
+} // TMimeDirProfileHandler::generateMimeDir
+
+
+// generate nested levels of MIME-DIR content
+void TMimeDirProfileHandler::generateLevels(
+ TMultiFieldItem &aItem,
+ string &aString,
+ const TProfileDefinition *aProfileP
+)
+{
+ //* %%% */ PDEBUGBLOCKDESC("generateLevels",TCFG_CSTR(aProfileP->levelName));
+ // check if level must be generated
+ bool dolevel=false;
+ string s,val;
+ sInt16 fid=aProfileP->levelConvdef.fieldid;
+ if (fid<0) dolevel=true; // if no controlling field there, generate anyway
+ else {
+ // check field contents to determine if generation is needed
+ if (aItem.isAssigned(fid)) {
+ const TEnumerationDef *enumP = aProfileP->levelConvdef.enumdefs;
+ aItem.getField(fid)->getAsString(val);
+ if (enumP) {
+ // if enumdefs, content must match first enumdef's enumval (NOT enumtext!!)
+ dolevel = strucmp(val.c_str(),enumP->TCFG_CSTR(enumval))==0;
+ }
+ else {
+ // just being not empty enables level
+ dolevel = !aItem.getField(fid)->isEmpty();
+ }
+ }
+ }
+ // check for MIME mode dependency
+ dolevel = dolevel && mimeModeMatch(aProfileP->modeDependency);
+ // generate level if enabled
+ if (dolevel) {
+ // generate level start
+ if (aProfileP->profileMode==profm_vtimezones) {
+ // don't generate now, just remember the string position where we should add the
+ // VTIMEZONEs when we're done generating the record.
+ fVTimeZonePendingProfileP = aProfileP;
+ fVTimeZoneInsertPos = aString.size();
+ }
+ else {
+ // standard custom level
+ s="BEGIN:";
+ s.append(aProfileP->levelName);
+ finalizeProperty(s.c_str(),aString,fMimeDirMode,false,false);
+ // loop through all properties of that level
+ const TPropertyDefinition *propP = aProfileP->propertyDefs;
+ #ifndef NO_REMOTE_RULES
+ uInt16 propGroup=0; // group identifier (all props with same name have same group ID)
+ const TPropertyDefinition *otherRulePropP = NULL; // default property which is used if none of the rule-dependent in the group was used
+ bool ruleSpecificExpanded = false;
+ #endif
+ const TPropertyDefinition *expandPropP;
+ while (propP) {
+ // check for mode dependency
+ if (!mimeModeMatch(propP->modeDependency)) {
+ // no mode match -> just skip this one
+ propP=propP->next;
+ continue;
+ }
+ //* %%% */ PDEBUGBLOCKDESC("expand_property",TCFG_CSTR(propP->propname));
+ #ifndef NO_REMOTE_RULES
+ // check for beginning of new group (no or different property group number)
+ if (propP->propGroup==0 || propP->propGroup!=propGroup) {
+ // end of last group - start of new group
+ propGroup = propP->propGroup; // remember new group number
+ // expand "other"-rule dependent variant from last group
+ if (!ruleSpecificExpanded && otherRulePropP) {
+ expandProperty(
+ aItem,
+ aString,
+ TCFG_CSTR(otherRulePropP->propname), // the prefix consists of the property name
+ otherRulePropP, // the property definition
+ fMimeDirMode // MIME-DIR mode
+ );
+ }
+ // for next group, no rule-specific version has been expanded yet
+ ruleSpecificExpanded = false;
+ // for next group, we don't have a "other"-rule variant
+ otherRulePropP=NULL;
+ }
+ // check if entry is rule-specific
+ expandPropP=NULL; // do not expand by default
+ if (propP->dependsOnRemoterule) {
+ // check if depends on current rule
+ if (propP->ruleDependency==NULL) {
+ // this is the "other"-rule dependent variant
+ // - just remember
+ otherRulePropP=propP;
+ }
+ else if (propP->ruleDependency==fAppliedRemoteRuleP) {
+ // specific for the applied rule
+ expandPropP=propP; // default to expand current prop
+ // now we have expanded a rule-specific property (blocks expanding of "other"-rule dependent prop)
+ ruleSpecificExpanded=true;
+ }
+ }
+ else {
+ // does not depend on rule, expand anyway
+ expandPropP=propP;
+ }
+ // check if this is last prop of list
+ propP=propP->next;
+ if (!propP && otherRulePropP && !ruleSpecificExpanded) {
+ // End of prop list, no rule-specific expand yet, and there is a otherRuleProp
+ // expand "other"-rule's property instead
+ expandPropP=otherRulePropP;
+ }
+ #else
+ // simply expand it
+ expandPropP=propP;
+ propP=propP->next;
+ #endif
+ // now expand if selected
+ if (expandPropP)
+ {
+ // recursively generate all properties that expand from this entry
+ // (includes extendsfieldid-parameters and repetitions
+ expandProperty(
+ aItem,
+ aString,
+ expandPropP->TCFG_CSTR(propname), // the prefix consists of the property name
+ expandPropP, // the property definition
+ fMimeDirMode // MIME-DIR mode
+ );
+ }
+ //* %%% */ PDEBUGENDBLOCK("expand_property");
+ } // properties loop
+ // generate sublevels, if any
+ const TProfileDefinition *subprofileP = aProfileP->subLevels;
+ while (subprofileP) {
+ // generate sublevels (eventually, none is generated)
+ generateLevels(aItem,aString,subprofileP);
+ // next
+ subprofileP=subprofileP->next;
+ }
+ // generate level end
+ s="END:";
+ s.append(aProfileP->levelName);
+ finalizeProperty(s.c_str(),aString,fMimeDirMode,false,false);
+ } // normal level
+ } // if level must be generated
+ //* %%% */ PDEBUGENDBLOCK("generateLevels");
+} // TMimeDirProfileHandler::generateLevels
+
+
+// Convert string from MIME-format into field value(s).
+// - the string passed to this function is already a translated value
+// list if combinesep is set, and every single value is already
+// enum-translated if enums are defined.
+// - returns false if field(s) could not be assigned because aText has
+// a bad syntax.
+// - returns true if field(s) assigned something useful or no field is
+// available to assign anything to.
+bool TMimeDirProfileHandler::MIMEStringToField(
+ const char *aText, // the value text to assign or add to the field
+ const TConversionDef *aConvDefP, // the conversion definition record
+ TMultiFieldItem &aItem, // the item where data goes to
+ sInt16 aFid, // the field ID (can be NULL for special conversion modes)
+ sInt16 aArrIndex // the repeat offset to handle array fields
+)
+{
+ sInt16 moffs;
+ uInt16 offs,n;
+ bool isBitMap;
+ fieldinteger_t flags = 0;
+ TTimestampField *tsFldP;
+ timecontext_t tctx;
+ TParsedTzidSet::iterator tz;
+ string s;
+ // RRULE
+ lineartime_t dtstart;
+ timecontext_t startcontext, untilcontext;
+ char freq;
+ char freqmod;
+ sInt16 interval;
+ fieldinteger_t firstmask;
+ fieldinteger_t lastmask;
+ lineartime_t until;
+ bool dostore;
+
+ // get pointer to leaf field
+ TItemField *fldP = aItem.getArrayField(aFid,aArrIndex);
+ switch (aConvDefP->convmode) {
+ case CONVMODE_MAILTO:
+ // remove the mailto: prefix if there is one
+ if (strucmp(aText,"mailto:",7)==0)
+ aText+=7; // remove leading "mailto:"
+ goto normal;
+ case CONVMODE_EMPTYONLY:
+ // same as CONVMODE_NONE, but assigns only first occurrence (that is,
+ // when field is still empty)
+ if (!fldP) return true; // no field, assignment "ok" (=nop)
+ if (!fldP->isEmpty()) return true; // field not empty, discard new assignment
+ case CONVMODE_TIMESTAMP: // nothing special for parsing
+ case CONVMODE_AUTODATE: // nothing special for parsing
+ case CONVMODE_AUTOENDDATE: // check for "last minute of the day"
+ case CONVMODE_DATE: // dates will be made floating
+ case CONVMODE_NONE:
+ normal:
+ if (!fldP) return true; // no field, assignment "ok" (=nop)
+ // just set as string or add if combine mode
+ if (aConvDefP->combineSep) {
+ // combine mode
+ if (!fldP->isEmpty()) {
+ // not empty, append with separator
+ char s[2];
+ s[0]=aConvDefP->combineSep;
+ s[1]=0;
+ fldP->appendString(s);
+ }
+ fldP->appendString(aText);
+ }
+ else {
+ // for non-strings, skip leading spaces before trying to parse
+ if (!fldP->isBasedOn(fty_string)) {
+ while (*aText && *aText==' ') aText++; // skip leading spaces
+ }
+ // simple assign mode
+ if (fldP->isBasedOn(fty_timestamp)) {
+ // read as ISO8601 timestamp
+ tsFldP = static_cast<TTimestampField *>(fldP);
+ // if field already has a non-unknown context (e.g. set via TZID, or TZ/DAYLIGHT),
+ // use that as context for floating ISO date (i.e. no "Z" or "+/-hh:mm" suffix)
+ if (!tsFldP->isFloating()) {
+ // field already has a TZ specified (e.g. by a TZID param), use that instead of item level context
+ tctx = tsFldP->getTimeContext();
+ }
+ else {
+ // no pre-known zone for this specific field, check if property has a specific zone
+ if (!TCTX_IS_UNKNOWN(fPropTZIDtctx)) {
+ // property has a specified time zone context from a TZID, use it
+ tctx = fPropTZIDtctx; // default to property's TZID (if one was parsed, otherwise this will be left floating)
+ }
+ else if (fHasExplicitTZ) {
+ // item has an explicitly specified time zone context (e.g. set via TZ: property),
+ // treat all timestamps w/o own time zone ("Z" suffix) in that context
+ tctx = fItemTimeContext;
+ }
+ else {
+ // item has no explicitly specified time zone context,
+ // parse and leave floating float for now
+ tctx = TCTX_UNKNOWN; // default to floating
+ }
+ }
+ // Now tctx is the default zone to bet set for ALL values that are in floating notation
+ // - check for special handling of misbehaving remotes
+ if (fTreatRemoteTimeAsLocal || fTreatRemoteTimeAsUTC) {
+ // ignore time zone specs which might be present eventually
+ tsFldP->setAsISO8601(aText, tctx, true);
+ // now force time zone to item/user context or UTC depending on flag settings
+ tctx = fTreatRemoteTimeAsLocal ? fItemTimeContext : TCTX_UTC;
+ // set it
+ tsFldP->setTimeContext(tctx);
+ }
+ else {
+ // read with time zone, if present, and default to tctx set above
+ tsFldP->setAsISO8601(aText, tctx, false);
+ // check if still floating now
+ if (tsFldP->isFloating()) {
+ // unfloat only if remote cannot handle UTC and therefore ALWAYS uses localtime.
+ // otherwise, assume that floating status is intentional and must be retained.
+ // Note: TZID and TZ, if present, are already applied by now
+ // Note: DURATION and DATE floating will always be retained, as they are always intentional
+ if ((!fReceiverCanHandleUTC || fProfileCfgP->fUnfloatFloating) && !TCTX_IS_DATEONLY(tsFldP->getTimeContext()) && !tsFldP->isDuration()) {
+ // not intentionally floating, but just not capable otherwise
+ // - put it into context of item (which is in this case session's user context)
+ tsFldP->setTimeContext(fItemTimeContext);
+ }
+ }
+ else {
+ // non-floating
+ if (fHasExplicitTZ) {
+ // item has explicit zone - move timestamp to it (e.g. if timestamps are sent
+ // in ISO8601 Z notation, but a TZ/DAYLIGHT or TZID is present)
+ tsFldP->moveToContext(tctx,false);
+ }
+ }
+ }
+ // special conversions
+ if (aConvDefP->convmode==CONVMODE_DATE) {
+ tsFldP->makeFloating(); // date-only is forced floating
+ }
+ else if (aConvDefP->convmode==CONVMODE_AUTOENDDATE && fMimeDirMode==mimo_old) {
+ // check if this could be a 23:59 type end-of-day
+ lineartime_t ts = tsFldP->getTimestampAs(fItemTimeContext,&tctx); // get in item context or floating
+ lineartime_t ts0 = lineartime2dateonlyTime(ts);
+ if (ts0!=ts && AlldayCount(ts0,ts)>0) { // only if not already a 0:00
+ // this is a 23:59 type end-of-day, convert it to midnight of next day
+ tsFldP->setTimestamp(lineartime2dateonlyTime(ts)+linearDateToTimeFactor);
+ }
+ }
+ }
+ else {
+ // read as text
+ fldP->setAsString(aText);
+ }
+ }
+ return true; // found
+
+ // Time zones
+ case CONVMODE_TZ:
+ // parse time zone
+ if (ISO8601StrToContext(aText, tctx)!=0) {
+ // this is always global for the entire item, so set the item context
+ // (which is then used when parsing dates (which should be delayed to make sure TZ is seen first)
+ fItemTimeContext = tctx;
+ fHasExplicitTZ = true; // zone explicitly set, not only copied from session's user zone
+ goto timecontext;
+ }
+ return true; // not set, is ok
+ case CONVMODE_DAYLIGHT:
+ // parse DAYLIGHT zone description property, prefer user zone (among multiple zones matching the Tz/daylight info)
+ if (TzDaylightToContext(aText,fItemTimeContext,tctx,getSessionZones(),fReceiverTimeContext)) {
+ // this is always global for the entire item, so set the item context
+ // (which is then used when parsing dates (which should be delayed to make sure TZ is seen first)
+ fItemTimeContext = tctx;
+ fHasExplicitTZ = true; // zone explicitly set, not only copied from session's user zone
+ goto timecontext;
+ }
+ return true; // not set, is ok
+ case CONVMODE_TZID:
+ // try to get context for named zone
+ // - look up in TZIDs we've parsed so far from VTIMEZONE
+ tz = fParsedTzidSet.find(aText);
+ if (tz!=fParsedTzidSet.end()) {
+ tctx = tz->second; // get tctx resolved from VTIMEZONE
+ // use tctx for all values from this property
+ fPropTZIDtctx = tctx;
+ goto timecontext;
+ }
+ else if (TimeZoneNameToContext(aText, tctx, getSessionZones())) {
+ // found valid TZID property, save it so we can use it for all values of this property that don't specify their own TZ
+ PDEBUGPRINTFX(DBG_ERROR,("Warning: TZID %s could be resolved against internal name, but appropriate VTIMEZONE is missing",aText));
+ fPropTZIDtctx=tctx;
+ goto timecontext;
+ }
+ else {
+ PDEBUGPRINTFX(DBG_ERROR,("Invalid TZID value '%s' found (no related VTIMEZONES found and not referring to an internal time zone name)",aText));
+ }
+ return true; // not set, is ok
+ timecontext:
+ // if no field, we still have the zone as fItemTimeContext
+ if (!fldP) return true; // no field, is ok
+ else if (fldP->isBasedOn(fty_timestamp)) {
+ // based on timestamp, assign context to that timestamp
+ tsFldP = static_cast<TTimestampField *>(fldP);
+ tsFldP->setTimeContext(tctx);
+ }
+ else if (fldP->getCalcType()==fty_integer || !TCTX_IS_TZ(tctx)) {
+ // integer field or non-symbolic time zone:
+ // assign minute offset as number (calculated for now)
+ TzResolveToOffset(tctx, moffs, getSession()->getSystemNowAs(TCTX_UTC), true, getSessionZones());
+ fldP->setAsInteger(moffs);
+ }
+ else {
+ // assign symbolic time zone name
+ TimeZoneContextToName(tctx, s, getSessionZones());
+ fldP->setAsString(s);
+ }
+ return true;
+
+ case CONVMODE_MULTIMIX:
+ case CONVMODE_BITMAP:
+ while (*aText && *aText==' ') aText++; // skip leading spaces
+ if (aConvDefP->convmode==CONVMODE_MULTIMIX) {
+ // parse value to determine field
+ if (!mixvalparse(aText, offs, isBitMap, n)) return true; // syntax not ok, nop
+ fldP = aItem.getArrayField(aFid+offs,aArrIndex);
+ }
+ else {
+ // just bit number
+ isBitMap=true;
+ if (StrToUShort(aText,n,2)<1) return true; // no integer convertible value, nop
+ }
+ if (!fldP) return true; // no field, assignment "ok" (=nop)
+ if (isBitMap) {
+ // store or add to bitmap
+ // - get current bitmap value if we have a spearator (means that we can have multiple values)
+ if (aConvDefP->combineSep)
+ flags=fldP->getAsInteger();
+ flags = flags | ((fieldinteger_t)1<<n);
+ // - save updated flags
+ fldP->setAsInteger(flags);
+ }
+ else {
+ // store as literal
+ fldP->setAsString(aText+n);
+ }
+ return true; // ok
+ case CONVMODE_VERSION:
+ // version string
+ // - return true if correct version string
+ return strucmp(aText,aItem.getItemType()->getTypeVers(fProfileMode))==0;
+ case CONVMODE_PRODID:
+ case CONVMODE_VALUETYPE:
+ case CONVMODE_FULLVALUETYPE:
+ return true; // simply ignore, always ok
+ case CONVMODE_RRULE:
+ // helpers
+ TTimestampField *tfP;
+ TIntegerField *ifP;
+ TStringField *sfP;
+ if (aFid<0) return true; // no field block, assignment "ok" (=nop)
+ // read DTSTART (last=6th field in block) as reference for converting count to end time point
+ dtstart=0; // start date/time, as reference
+ if (!(tfP = ITEMFIELD_DYNAMIC_CAST_PTR(TTimestampField,fty_timestamp,aItem.getArrayField(aFid+5,aArrIndex)))) return false;
+ // TZ and TZID should be applied to dates by now, so dtstart should be in right zone
+ dtstart = tfP->getTimestampAs(TCTX_UNKNOWN,&startcontext);
+ if (TCTX_IS_UTC(startcontext)) {
+ // UTC is probably not the correct zone to resolve weekdays -> convert to item zone
+ dtstart = tfP->getTimestampAs(fItemTimeContext,&startcontext);
+ }
+ // init field block values
+ freq='0'; // frequency
+ freqmod=' '; // frequency modifier
+ interval=0; // unspecified interval
+ firstmask=0; // day mask counted from the first day of the period
+ lastmask=0; // day mask counted from the last day of the period
+ until=0; // last day
+ // do the conversion here
+ dostore=false;
+ if (fMimeDirMode==mimo_old) {
+ // vCalendar 1.0 type RRULE
+ dostore=RRULE1toInternal(
+ aText, // RRULE string to be parsed
+ dtstart, // reference date for parsing RRULE
+ startcontext,
+ freq,
+ freqmod,
+ interval,
+ firstmask,
+ lastmask,
+ until,
+ untilcontext,
+ GETDBGLOGGER
+ );
+ }
+ else {
+ // iCalendar 2.0 type RRULE
+ dostore=RRULE2toInternal(
+ aText, // RRULE string to be parsed
+ dtstart, // reference date for parsing RRULE
+ startcontext,
+ freq,
+ freqmod,
+ interval,
+ firstmask,
+ lastmask,
+ until,
+ untilcontext,
+ GETDBGLOGGER
+ );
+ }
+ if (dostore) {
+ // store values into field block
+ // - freq/freqmod
+ if (!(sfP = ITEMFIELD_DYNAMIC_CAST_PTR(TStringField,fty_string,aItem.getArrayField(aFid,aArrIndex)))) return false;
+ aFid++; // do NOT INCREMENT in macro, as it would get incremented twice
+ sfP->assignEmpty();
+ if (freq!='0') {
+ sfP->appendChar(freq);
+ sfP->appendChar(freqmod);
+ }
+ // - interval
+ if (!(ifP = ITEMFIELD_DYNAMIC_CAST_PTR(TIntegerField,fty_integer,aItem.getArrayField(aFid,aArrIndex)))) return false;
+ aFid++; // do NOT INCREMENT in macro, as it would get incremented twice
+ ifP->setAsInteger(interval);
+ // - firstmask
+ if (!(ifP = ITEMFIELD_DYNAMIC_CAST_PTR(TIntegerField,fty_integer,aItem.getArrayField(aFid,aArrIndex)))) return false;
+ aFid++; // do NOT INCREMENT in macro, as it would get incremented twice
+ ifP->setAsInteger(firstmask);
+ // - lastmask
+ if (!(ifP = ITEMFIELD_DYNAMIC_CAST_PTR(TIntegerField,fty_integer,aItem.getArrayField(aFid,aArrIndex)))) return false;
+ aFid++; // do NOT INCREMENT in macro, as it would get incremented twice
+ ifP->setAsInteger(lastmask);
+ // - until
+ if (!(tfP = ITEMFIELD_DYNAMIC_CAST_PTR(TTimestampField,fty_timestamp,aItem.getArrayField(aFid,aArrIndex)))) return false;
+ aFid++; // do NOT INCREMENT in macro, as it would get incremented twice
+ tfP->setTimestampAndContext(until,untilcontext);
+ // - dtstart is not stored, but only read above for reference
+ // done
+ return true;
+ }
+ else {
+ return false;
+ }
+ break; // just in case
+ default:
+ // unknown mode, cannot convert
+ return false;
+ }
+ return false;
+} // TMimeDirProfileHandler::MIMEStringToField
+
+
+// helper for parseMimeDir()
+// - parse parameter or property value(list), returns false if no value(list)
+bool TMimeDirProfileHandler::parseValue(
+ const string &aText, // string to parse as value (could be binary content)
+ const TConversionDef *aConvDefP,
+ sInt16 aBaseOffset, // base offset
+ sInt16 aRepOffset, // repeat offset, adds to aBaseOffset for non-array fields, is array index for array fileds
+ TMultiFieldItem &aItem, // the item where data goes to
+ bool &aNotEmpty, // is set true (but never set false) if property contained any (non-positional) values
+ char aSeparator, // separator between values
+ TMimeDirMode aMimeMode, // MIME mode (older or newer vXXX format compatibility)
+ bool aParamValue, // set if parsing parameter value (different escaping rules)
+ bool aStructured // set if value consists of multiple values (has semicolon content escaping)
+)
+{
+ string val,val2;
+ char c;
+ const char *p;
+
+ // determine field ID
+ sInt16 fid=aConvDefP->fieldid;
+ if (fid>=0) {
+ // value has field where it can be stored
+ // - fid is ALWAYS offset by baseoffset
+ fid += aBaseOffset;
+ // - adjust fid and repoffset (add them and reset aRepOffset if no array field)
+ aItem.adjustFidAndIndex(fid,aRepOffset);
+ // find out if value exists (available in source and target)
+ if (isFieldAvailable(aItem,fid)) {
+ // parse only if field available in both source and target
+ if (aConvDefP->convmode==CONVMODE_BLOB_B64) {
+ // move 1:1 into field
+ // - get pointer to leaf field
+ TItemField *fldP = aItem.getArrayField(fid,aRepOffset);
+ // - directly set field with entire (possiby binary) string content
+ if (fldP) fldP->setAsString(aText);
+ // parsed successfully
+ return true;
+ }
+ // normal text value, apply de-escaping, charset transformation, value list and enum conversion
+ p = aText.c_str(); // start here
+ while (*p) {
+ // value list loop
+ // - get next value
+ val.erase();
+ while ((c=*p)!=0) {
+ // check for field list separator (if field allows list at all)
+ if (c==aSeparator && aConvDefP->combineSep) {
+ p++; // skip separator
+ break;
+ }
+ // check for escaped chars
+ if (c=='\\') {
+ p++;
+ c=*p;
+ if (!c) break; // half escape sequence, ignore
+ else if (c=='n') c='\n';
+ // other escaped chars are shown as themselves
+ }
+ // add char
+ val+=c;
+ // next
+ p++;
+ }
+ // find first non-space and number of chars excluding leading and trailing spaces
+ const char* valnospc = val.c_str();
+ size_t numnospc=val.size();
+ while (*valnospc && *valnospc==' ') { valnospc++; numnospc--; }
+ while (*(valnospc+numnospc-1)==' ') { numnospc--; }
+ // - counts as non-empty if there is a non-empty (and not space-only) value string (even if
+ // it might be converted to empty-value in enum conversion)
+ if (*valnospc) aNotEmpty=true;
+ // - apply enum translation if any
+ const TEnumerationDef *enumP = aConvDefP->findEnumByName(valnospc,numnospc);
+ if (enumP) {
+ // we have an explicit value (can be default if there is a enm_defaultvalue enum)
+ if (enumP->enummode==enm_ignore)
+ continue; // do not assign anything, get next value
+ else {
+ if (enumP->enummode==enm_prefix) {
+ // append original value minus prefix to translation
+ size_t n=TCFG_SIZE(enumP->enumtext);
+ val2.assign(valnospc+n,numnospc-n); // copying from original val
+ val=enumP->enumval; // assign the prefix
+ val+=val2; // and append the original value sans prefix
+ }
+ else {
+ val=enumP->enumval; // just use translated value
+ }
+ }
+ }
+ // assign (or add) value to field
+ if (!MIMEStringToField(
+ val.c_str(), // the value text to assign or add to the field
+ aConvDefP, // the conversion definition
+ aItem,
+ fid, // field ID, can be -1
+ aRepOffset // 0 or array index
+ )) {
+ // field conversion error
+ PDEBUGPRINTFX(DBG_ERROR,(
+ "TMimeDirProfileHandler::parseValue: MIMEStringToField assignment (fid=%hd, arrindex=%hd) failed",
+ fid,
+ aRepOffset
+ ));
+ return false;
+ }
+ } // while(more chars in value text)
+ } // if source and target fields available
+ else {
+ // show this in log, as most probably it's a remote devInf bug
+ PDEBUGPRINTFX(DBG_PARSE,("No value stored for field index %hd because remote indicates not supported in devInf",fid));
+ }
+ } // if fieldid exists
+ else {
+ // could be special conversion using no data or data from
+ // internal object variables (such as VERSION value)
+ if (!MIMEStringToField(
+ aText.c_str(), // the value text to process
+ aConvDefP, // the conversion definition
+ aItem,
+ FID_NOT_SUPPORTED,
+ 0
+ )) {
+ // field conversion error
+ PDEBUGPRINTFX(DBG_ERROR,(
+ "TMimeDirProfileHandler::parseValue: MIMEStringToField in check mode (no field) failed with val=%s",
+ aText.c_str()
+ ));
+ return false;
+ }
+ }
+ // parsed successfully
+ return true;
+} // TMimeDirProfileHandler::parseValue
+
+
+
+// parse given property
+bool TMimeDirProfileHandler::parseProperty(
+ const char *&aText, // where to start interpreting property, will be updated past end of what was scanned
+ TMultiFieldItem &aItem, // item to store data into
+ const TPropertyDefinition *aPropP, // the property definition
+ sInt16 *aRepArray, // array[repeatID], holding current repetition COUNT for a certain nameExts entry
+ sInt16 aRepArraySize, // size of array (for security)
+ TMimeDirMode aMimeMode // MIME mode (older or newer vXXX format compatibility)
+)
+{
+ TNameExtIDMap nameextmap;
+ const TParameterDefinition *paramP;
+ const char *p,*ep,*vp;
+ char c;
+ string pname;
+ string val;
+ bool defaultparam;
+ bool fieldoffsetfound;
+ bool notempty = false;
+ bool valuelist;
+ sInt16 pidx; // parameter index
+ TEncodingTypes encoding;
+ TCharSets charset;
+ // field storage info vars, defaults are used if property has no TPropNameExtension
+ sInt16 baseoffset=0;
+ sInt16 repoffset=0;
+ sInt16 maxrep=1; // no repeat by default
+ sInt16 repinc=1; // inc by 1
+ sInt16 repid=-1; // invalid by default
+ bool overwriteempty=false; // do not overwrite empty values by default
+
+ // init
+ encoding=enc_none; // no encoding by default
+ charset=aMimeMode==mimo_standard ? chs_utf8 : chs_ansi; // UTF8 for real MIME-DIR (same as enclosing SyncML doc), ANSI encoding for pre-MIME-DIR (as used by T39m or V3i e.g.)
+ nameextmap=0; // no name extensions detected so far
+ fieldoffsetfound=(aPropP->nameExts==NULL); // no first pass needed at all w/o nameExts, just use offs=0
+ valuelist=aPropP->valuelist; // cache flag
+ // scan parameter list
+ do {
+ p=aText;
+ while (*p==';') {
+ // param follows
+ defaultparam=false;
+ pname.erase();
+ p=nextunfolded(p,aMimeMode);
+ // parameter expected here
+ // - find end of parameter name
+ vp=NULL; // no param name found
+ for (ep=p; *ep; ep=nextunfolded(ep,aMimeMode)) {
+ if (*ep=='=') {
+ // param value follows at vp
+ vp=nextunfolded(ep,aMimeMode);
+ break;
+ }
+ else if (*ep==':' || *ep==';') {
+ // end of parameter name w/o equal sign
+ if (aMimeMode!=mimo_old) {
+ // only mimo_old allows default params, but as e.g. Nokia Intellisync (Synchrologic) does this completely wrong, we now tolerate it
+ POBJDEBUGPRINTFX(getSession(),DBG_ERROR,(
+ "Parameter without value: %s - is wrong in MIME-DIR, but we tolerate it and parse as default param name",
+ pname.c_str()
+ ));
+ }
+ // treat this as a value of the default parameter (correct syntax in old vCard 2.1/vCal 1.0, wrong in MIME-DIR)
+ defaultparam=true; // default param
+ // value is equal to param name and starts at p
+ vp=p;
+ break;
+ }
+ // add char to param name (unfolded!)
+ pname+=*ep;
+ }
+ if (!vp) {
+ POBJDEBUGPRINTFX(getSession(),DBG_ERROR,("parseProperty: bad parameter %s (missing value)",pname.c_str()));
+ return false;
+ }
+ // parameter name & value isolated, pname=name (if not defaultparam), vp points to value
+ // - obtain unfolded value
+ val.erase();
+ bool dquoted = false;
+ if (*vp=='"' && aMimeMode==mimo_standard) {
+ dquoted = true;
+ vp=nextunfolded(vp,aMimeMode);
+ }
+ do {
+ c=*vp;
+ if (isEndOfLineOrText(c)) break;
+ if (dquoted) {
+ // within double quoted value, only closing dquote can end it
+ if (c=='"') {
+ // swallow closing double quote and proceed (next should be end of value anyway)
+ vp = nextunfolded(vp,aMimeMode);
+ dquoted = false;
+ continue;
+ }
+ }
+ else {
+ // not within double quoted value
+ if (c==':' || c==';') break; // end of value
+ // check escaped characters
+ if (c=='\\') {
+ // escape char, do not check next char for end-of-value (but DO NOT expand \-escaped chars here!!)
+ vp=nextunfolded(vp,aMimeMode);
+ c=*vp; // get next
+ if (c) {
+ val+='\\'; // keep the escaped sequence for later when value is actually processed!
+ }
+ else {
+ // half-finished escape at end of value, ignore
+ break;
+ }
+ }
+ }
+ val+=c;
+ // cancel QP softbreaks if encoding is already switched to QP at this point
+ vp=nextunfolded(vp,aMimeMode,encoding==enc_quoted_printable);
+ } while(true);
+ // - processing of next param starts here
+ p=vp;
+ // check for global parameters
+ if ((aMimeMode==mimo_old && defaultparam) || strucmp(pname.c_str(),"ENCODING")==0) {
+ // get encoding (if valid encoding)
+ for (sInt16 k=0; k<numMIMEencodings; k++) {
+ if (strucmp(val.c_str(),MIMEEncodingNames[k])==0) {
+ encoding=static_cast <TEncodingTypes> (k);
+ }
+ }
+ }
+ else if (strucmp(pname.c_str(),"CHARSET")==0) {
+ // charset specified (mimo_old value-only not supported)
+ sInt16 k;
+ for (k=1; k<numCharSets; k++) {
+ if (strucmp(val.c_str(),MIMECharSetNames[k])==0) {
+ // charset found
+ charset=TCharSets(k);
+ break;
+ }
+ }
+ if (k>=numCharSets) {
+ // unknown charset
+ POBJDEBUGPRINTFX(getSession(),DBG_ERROR,("========== WARNING: Unknown Charset '%s'",val.c_str()));
+ // %%% replace 8bit chars with underscore
+ charset=chs_unknown;
+ }
+ }
+ // find param in list now
+ paramP = aPropP->parameterDefs;
+ pidx=0; // parameter index
+ while (paramP) {
+ // check for match
+ if (
+ mimeModeMatch(paramP->modeDependency) &&
+ ((defaultparam && paramP->defaultparam) || strucmp(pname.c_str(),paramP->TCFG_CSTR(paramname))==0)
+ ) {
+ // param name found
+ // - process value (list)
+ if (!fieldoffsetfound) {
+ // first pass, check for extendsname parameters
+ if (paramP->extendsname) {
+ // - for each value in the value list, check if it has a nameextid
+ if (!paramP->convdef.enumdefs) {
+ DEBUGPRINTFX(DBG_PARSE,(
+ "parseProperty: extendsname param w/o enum : %s;%s",
+ aPropP->TCFG_CSTR(propname),
+ paramP->TCFG_CSTR(paramname)
+ ));
+ return false;
+ }
+ // - loop through value list
+ ep=val.c_str();
+ while (*ep) {
+ sInt32 n;
+ const char *pp;
+ // find end of next value in list
+ for (n=0,pp=ep; *pp; pp++) {
+ if (*pp==',') {
+ pp++; // skip the comma
+ break;
+ }
+ n++;
+ }
+ // search in enums list
+ const TEnumerationDef *enumP = paramP->convdef.findEnumByName(ep,n);
+ if (enumP && enumP->nameextid>=0) {
+ // set name extension map bit
+ nameextmap |= ((TNameExtIDMap)1<<enumP->nameextid);
+ }
+ // next value in list
+ ep=pp;
+ }
+ } // if extendsname
+ } // first pass
+ else {
+ // second pass: read param value(s)
+ if (!parseValue(
+ val, // input string, possibly binary (e.g. in case of B64 encoded PHOTO)
+ &(paramP->convdef),
+ baseoffset, // base offset (as determined by position)
+ repoffset, // repetition offset or array index
+ aItem, // the item where data goes to
+ notempty, // set true if value(s) parsed are not all empty
+ defaultparam ? ';' : ',', // value list separator
+ aMimeMode, // MIME mode (older or newer vXXX format compatibility)
+ true, // parsing a parameter
+ false // no structured value
+ )) {
+ DEBUGPRINTFX(DBG_PARSE,(
+ "TMimeDirProfileHandler::parseProperty: %s: value not parsed: %s",
+ pname.c_str(),
+ val.c_str()
+ ));
+ return false;
+ }
+ } // second pass
+ } // if (param known)
+ // test next param
+ paramP=paramP->next;
+ pidx++;
+ } // while more params
+ // p points to ';' of next param or ':' of value
+ } // while more parameters (*p==';')
+ // check if both passes done or if property storage is explicitly blocked already (baseoffset=-1)
+ if (fieldoffsetfound) break;
+ // start second pass
+ fieldoffsetfound=true;
+ // - assume empty to start with
+ notempty=false;
+ // - prepare for second pass: check if set of param values match
+ // an entry in the nameexts list
+ TPropNameExtension *propnameextP = aPropP->nameExts;
+ if (propnameextP) {
+ bool dostore=false;
+ while (propnameextP) {
+ // check if entry matches parsed extendsname param values
+ if (
+ ((propnameextP->musthave_ids & nameextmap) == propnameextP->musthave_ids) && // needed there
+ ((propnameextP->forbidden_ids & nameextmap) == 0) // none of the forbidden ones there
+ ) {
+ // found match, get offset
+ baseoffset=propnameextP->fieldidoffs;
+ if (baseoffset==OFFS_NOSTORE) break; // abort with dostore=false
+ // check if repeat needed/allowed
+ maxrep=propnameextP->maxRepeat;
+ if (maxrep==REP_REWRITE) {
+ dostore=true; // we can store
+ break; // unlimited repeat allowed but stored in same fields (overwrite)
+ }
+ // check current repetition
+ repid=propnameextP->repeatID;
+ if (repid>=aRepArraySize)
+ SYSYNC_THROW(TSyncException(DEBUGTEXT("TMimeDirProfileHandler::parseProperty: repID too high","mdit11")));
+ if (aRepArray[repid]<maxrep || maxrep==REP_ARRAY) {
+ // not exhausted, we can use this entry
+ // - calculate repeat offset to be used
+ repinc=propnameextP->repeatInc;
+ // note: repArray will be updated below (if property not empty or !overwriteempty)
+ dostore=true; // we can store
+ do {
+ repoffset=aRepArray[repid] * repinc;
+ // - set flag if repeat offset should be incremented after storing an empty property or not
+ overwriteempty=propnameextP->overwriteEmpty;
+ // - check if target property main value is empty (must be, or we will skip that repetition)
+ dostore=false; // if no field exists, we do not store
+ for (sInt16 e=0; e<aPropP->numValues; e++) {
+ if (aPropP->convdefs[e].fieldid==FID_NOT_SUPPORTED)
+ continue; // no field, no need to check it
+ sInt16 e_fid=aPropP->convdefs[e].fieldid+baseoffset;
+ sInt16 e_rep=repoffset;
+ aItem.adjustFidAndIndex(e_fid,e_rep);
+ // - get base field
+ TItemField *e_basefldP = aItem.getField(e_fid);
+ TItemField *e_fldP = NULL;
+ if (e_basefldP)
+ e_fldP=e_basefldP->getArrayField(e_rep,true); // get leaf field, if it exists
+ if (!e_basefldP || (e_fldP && e_fldP->isAssigned())) {
+ // base field of one of the main fields does not exist or leaf field is already assigned
+ // -> skip that repetition
+ dostore=false;
+ break;
+ }
+ else
+ dostore=true; // at least one field exists, we might store
+ }
+ // check if we can test more repetitions
+ if (!dostore) {
+ if (aRepArray[repid]+1<maxrep || maxrep==REP_ARRAY) {
+ // we can increment and try next repetition
+ aRepArray[repid]++;
+ }
+ else
+ break; // no more possible repetitions with this position rule (check next rule)
+ }
+ } while (!dostore);
+ if (dostore) break; // we can store now
+ } // if repeat not yet exhausted
+ } // if position rule matches
+ // next
+ propnameextP=propnameextP->next;
+ } // while search for matching nameExts entry
+ // abort if we can't store
+ if (!dostore) {
+ aText=p; // this is what we've read so far
+ return false;
+ }
+ } // if name extension list not empty
+ // Now baseoffset/repoffset are valid to be used for storage
+ } while(true); // until parameter pass 1 & pass 2 done
+ // parameters are all processed by now
+ // - read value(s)
+ char sep=':'; // first value starts with colon
+ // repeat until we have all values
+ for (sInt16 i=0; i<aPropP->numValues || valuelist; i++) {
+ if (*p!=sep && (aPropP->altvaluesep==0 || *p!=aPropP->altvaluesep)) {
+ #ifdef SYDEBUG
+ // Note: for valuelists, this is the normal loop exit case as we are not limited by numValues
+ if (!valuelist) {
+ // New behaviour: omitting values is ok (needed e.g. for T39m)
+ DEBUGPRINTFX(DBG_PARSE,("TMimeDirProfileHandler::parseProperty: %s does not specify all values",aPropP->TCFG_CSTR(propname)));
+ }
+ #endif
+ break; // all available values read
+ }
+ // skip separatore
+ p++;
+ // get value(list) unfolded
+ decodeValue(encoding,charset,aMimeMode,aPropP->numValues > 1 || valuelist ? aPropP->valuesep : 0,aPropP->altvaluesep,p,val);
+ // check if we can store, otherwise just read over value
+ // - get the conversion def for the value
+ TConversionDef *convDef = &(aPropP->convdefs[valuelist ? 0 : i]); // always use convdef[0] for value lists
+ // - store value if not a value list (but simple value or part of structured value), or store if
+ // valuelist and repeat not yet exhausted, or if valuelist without repetition but combination separator
+ // which allows to put multiple values into a single field
+ if (!valuelist || repoffset<maxrep*repinc || maxrep==REP_ARRAY || (valuelist && convDef->combineSep)) {
+ // convert and store value (or comma separated value-list, not to mix with valuelist-property!!)
+ if (!parseValue(
+ val,
+ convDef,
+ baseoffset, // identifies base field
+ repoffset, // repeat offset to base field / array index
+ aItem, // the item where data goes to
+ notempty, // set true if value(s) parsed are not all empty
+ ',',
+ aMimeMode, // MIME mode (older or newer vXXX format compatibility)
+ false, // no parameter
+ aPropP->numValues > 1 // structured if multiple values
+ )) {
+ PDEBUGPRINTFX(DBG_PARSE+DBG_EXOTIC,(
+ "TMimeDirProfileHandler::parseProperty: %s: value not parsed: %s",
+ aPropP->TCFG_CSTR(propname),
+ val.c_str()
+ ));
+ return false;
+ }
+ // update repeat offset and repeat count if this is a value list
+ if (valuelist && convDef->combineSep==0 && (notempty || !overwriteempty)) {
+ // - update count for every non-empty value (for empty values only if overwriteempty is not set)
+ if (repid>=0) aRepArray[repid]++; // next repetition
+ repoffset+=repinc; // also update repeat offset
+ }
+ }
+ else {
+ // value cannot be stored
+ PDEBUGPRINTFX(DBG_PARSE+DBG_EXOTIC,(
+ "TMimeDirProfileHandler::parseProperty: %s: value not stored because repeat exhausted: %s",
+ aPropP->TCFG_CSTR(propname),
+ val.c_str()
+ ));
+ }
+ // more values must be separated by the value sep char (default=';' but can be ',' e.g. for iCalendar 2.0 CATEGORIES)
+ sep = aPropP->valuesep;
+ } // for all values
+ if (notempty && !valuelist) {
+ // at least one of the components is not empty. Make sure all components are "touched" such that
+ // in case of arrays, these are assigned even if empty
+ for (sInt16 j=0; j<aPropP->numValues; j++) {
+ sInt16 fid=aPropP->convdefs[j].fieldid;
+ if (fid>=0) {
+ fid += baseoffset;
+ aItem.adjustFidAndIndex(fid,repoffset);
+ // requesting the pointer creates the field if it does not already exist
+ aItem.getArrayField(fid,repoffset,false);
+ }
+ }
+ }
+ if (!valuelist && repid>=0 && (notempty || !overwriteempty)) {
+ // we have used this repetition and actually stored values, so count it now
+ // (unless we have stored an empty value only and overwriteempty is true, in
+ // this case we don't increment, so next value found for this repetition will
+ // overwrite empty value
+ aRepArray[repid]++;
+ }
+ // update read pointer past end of what we've scanned (but not necessarily up
+ // to next property beginning)
+ aText=p;
+ // done, ok
+ return true;
+} // TMimeDirProfileHandler::parseProperty
+
+
+// parse MIME-DIR from specified string into item
+bool TMimeDirProfileHandler::parseMimeDir(const char *aText, TMultiFieldItem &aItem)
+{
+ // start with empty item
+ aItem.cleardata();
+ // reset item time zone before parsing
+ fHasExplicitTZ = false; // none set explicitly
+ fItemTimeContext = fReceiverTimeContext; // default to user context
+ fDelayedProps.clear(); // start w/o delayed props
+ fParsedTzidSet.clear(); // start w/o time zones
+ // start parsing on root level
+ if (parseLevels(aText,aItem,fProfileDefinitionP,true)) {
+ // make sure all supported (=available) fields are at least empty (but not missing!)
+ aItem.assignAvailables();
+ return true;
+ }
+ else
+ return false;
+} // TMimeDirProfileHandler::parseMimeDir
+
+
+// parameter string for QP encoding. Needed when skipping otherwise unknown properties
+#define QP_ENCODING_PARAM "ENCODING=QUOTED-PRINTABLE"
+
+// parse MIME-DIR level from specified string into item
+bool TMimeDirProfileHandler::parseLevels(
+ const char *&aText,
+ TMultiFieldItem &aItem,
+ const TProfileDefinition *aProfileP,
+ bool aRootLevel
+)
+{
+ char c;
+ const char *p,*propname;
+ sInt32 n;
+ sInt16 foundmandatory=0;
+ const sInt16 maxreps = 50;
+ sInt16 repArray[maxreps];
+ bool atStart = aRootLevel;
+
+ // reset repetition counts
+ for (sInt16 k=0; k<maxreps; k++) repArray[k]=0;
+ // level is known
+ sInt16 disabledLevels=0;
+ // set level marker field, if any is defined
+ sInt16 fid=aProfileP->levelConvdef.fieldid;
+ if (fid>=0) {
+ // field defined for level entry
+ // - make sure field exists and is assigned empty value at least
+ aItem.getFieldRef(fid).assignEmpty();
+ const TEnumerationDef *enumP = aProfileP->levelConvdef.enumdefs;
+ if (enumP) {
+ // if enumdefs, content is set to first enumdef's enumval (NOT enumtext!!)
+ aItem.getField(fid)->setAsString(enumP->TCFG_CSTR(enumval));
+ }
+ }
+ // skip eventual leading extra LF and CR and whitespace here
+ // NOTE: Magically server sends XML CDATA with 0x0D 0x0D 0x0A for example
+ while (isspace(*aText)) aText++;
+ // parse input text property by property
+ do {
+ // start of property parsing
+ // - reset TZID flag
+ fPropTZIDtctx = TCTX_UNKNOWN;
+ // - prepare scanning
+ p=aText;
+ propname = p; // assume name starts at beginning of text
+ n = 0;
+ // determine property name end
+ do {
+ c=*p;
+ if (!c) {
+ // end of text reached w/o property name
+ POBJDEBUGPRINTFX(getSession(),DBG_ERROR,("parseMimeDir: no property name found, text=%s",aText));
+ return false;
+ }
+ if (c==':' || c==';') break;
+ // handle grouping
+ if (c=='.') {
+ // %%% add capability to save group names in fields
+ propname = p+1; // skip group
+ n = 0;
+ }
+ // next char
+ p++; n++;
+ } while(true);
+ // propname points to start, p points to end of property name, n=name size
+ // - search through all properties
+ bool propparsed=false;
+ // - check for BEGIN and END
+ if (strucmp(propname,"BEGIN",n)==0) {
+ // BEGIN encountered
+ p = propname+n;
+ // - skip eventual parameters for broken implementations like Intellisync/Synchrologic
+ if (*p==';') while (*p && *p!=':') p++;
+ // - isolate value
+ size_t l=0; const char *lnam=p+1;
+ while (*(lnam+l)>=0x20) l++; // calculate length of value
+ p=lnam+l; // advance scanning pointer to terminator
+ n=0; // prevent false advancing at end of prop loop
+ if (atStart) {
+ // value must be level name, else this is a bad profile
+ if (strucmp(lnam,aProfileP->TCFG_CSTR(levelName),l)!=0) {
+ POBJDEBUGPRINTFX(getSession(),DBG_ERROR,("parseMimeDir: root level BEGIN has bad value: %s",aText));
+ return false;
+ }
+ atStart=false; // no special lead-in check any more
+ propparsed=true;
+ }
+ else {
+ // value determines new level to enter
+ if (disabledLevels==0) {
+ // search for sublevel
+ const TProfileDefinition *subprofileP = aProfileP->subLevels;
+ while (subprofileP) {
+ // check
+ if (
+ mimeModeMatch(subprofileP->modeDependency) &&
+ strucmp(lnam,subprofileP->TCFG_CSTR(levelName),l)==0
+ ) {
+ // sublevel found, process
+ while ((uInt8)(*p)<0x20) p++; // advance scanning pointer to beginning of next property
+ // check special case first
+ if (subprofileP->profileMode==profm_vtimezones) {
+ // vTimeZone is handled specially
+ string s2;
+ string s = "END:";
+ s.append(subprofileP->levelName);
+ size_t n = s.size(); // size of lead-out
+ cAppCharP e = strstr(p,s.c_str());
+ if (e==NULL) return false; // unterminated vTimeZone sublevel
+ s.assign(p,e-p); // everything between lead-in and lead-out
+ p = e+n; // advance pointer beyond VTIMEZONES
+ appendStringAsUTF8(s.c_str(), s2, chs_utf8, lem_cstr, false);
+ timecontext_t tctx;
+ // identify or add this in the session zones
+ string tzid;
+ if (VTIMEZONEtoInternal(s2.c_str(), tctx, getSessionZones(), NULL, &tzid)) {
+ // time zone identified
+ #ifdef SYDEBUG
+ string tzname;
+ TimeZoneContextToName(tctx, tzname, getSessionZones());
+ PDEBUGPRINTFX(DBG_PARSE+DBG_EXOTIC,("parseMimeDir: VTIMEZONE with ID='%s' parsed to internal time zone '%s'",tzid.c_str(),tzname.c_str()));
+ #endif
+ // remember it by original name for TZID parsing
+ fParsedTzidSet[tzid] = tctx;
+ }
+ else {
+ POBJDEBUGPRINTFX(getSession(),DBG_ERROR,("parseMimeDir: could not parse VTIMEZONE: %s",s.c_str()));
+ }
+ }
+ else {
+ // ordinary non-root level
+ if (!parseLevels(p,aItem,subprofileP,false)) return false;
+ }
+ // - now continue on this level
+ propparsed=true;
+ break;
+ }
+ // next
+ subprofileP=subprofileP->next;
+ }
+ if (!propparsed) {
+ // no matching sublevel found, disable this level
+ disabledLevels=1;
+ }
+ }
+ else {
+ // already disabled, just nest
+ disabledLevels++;
+ }
+ } // BEGIN not on rootlevel
+ } // BEGIN found
+ else if (strucmp(propname,"END",n)==0) {
+ // END encountered
+ p = propname+n;
+ // - skip eventual parameters for broken implementations like Intellisync/Synchrologic
+ if (*p==';') while (*p && *p!=':') p++;
+ // - isolate value
+ size_t l=0; const char *lnam=p+1;
+ while (*(lnam+l)>=0x20) l++; // calculate length of value
+ p=lnam+l; // advance scanning pointer to terminator
+ n=0; // prevent false advancing at end of prop loop
+ // check
+ if (disabledLevels>0) {
+ // end of a disabled level, just un-nest
+ disabledLevels--;
+ }
+ else {
+ // should be end of active level, check name
+ if (strucmp(lnam,aProfileP->TCFG_CSTR(levelName),l)!=0) {
+ POBJDEBUGPRINTFX(getSession(),DBG_ERROR,("parseMimeDir: unexpected END value: %s",aText));
+ return false;
+ }
+ // correct end of level
+ aText=p; // points to terminator, which is correct for end-of-level
+ // break scanner loop
+ break;
+ }
+ } // END found
+ else if (disabledLevels==0) {
+ if (atStart) {
+ POBJDEBUGPRINTFX(getSession(),DBG_ERROR,("parseMimeDir: root level does not start with BEGIN: %s",aText));
+ return false;
+ }
+ // not disabled level
+ const TPropertyDefinition *propP = aProfileP->propertyDefs;
+ #ifndef NO_REMOTE_RULES
+ const TPropertyDefinition *otherRulePropP = NULL; // default property which is used if none of the rule-dependent in the group was used
+ bool ruleSpecificParsed = false;
+ uInt16 propGroup=0; // group identifier (all props with same name have same group ID)
+ #endif
+ const TPropertyDefinition *parsePropP;
+ while(propP) {
+ // compare
+ if (
+ mimeModeMatch(propP->modeDependency) && // none or matching mode dependency
+ strucmp(propname,propP->TCFG_CSTR(propname),n)==0
+ ) {
+ // found property def with matching name (and MIME mode)
+ // check all in group (=all subsequent with same name)
+ #ifndef NO_REMOTE_RULES
+ propGroup=propP->propGroup;
+ ruleSpecificParsed=false;
+ otherRulePropP=NULL;
+ while (propP && propP->propGroup==propGroup && propP->propGroup!=0)
+ #else
+ do
+ #endif
+ {
+ // still in same group (= same name)
+ #ifndef NO_REMOTE_RULES
+ // check if this property should be used for parsing
+ parsePropP=NULL; // do not parse by default
+ if (propP->dependsOnRemoterule) {
+ // check if depends on current rule
+ if (propP->ruleDependency==NULL) {
+ // this is the "other"-rule dependent variant
+ // - just remember for now
+ otherRulePropP=propP;
+ }
+ else if (propP->ruleDependency==fAppliedRemoteRuleP) {
+ // specific for the applied rule
+ parsePropP=propP; // default to expand current prop
+ // now we have expanded a rule-specific property (blocks parsing of "other"-rule dependent prop)
+ ruleSpecificParsed=true;
+ }
+ }
+ else {
+ // does not depend on rule, parse anyway
+ parsePropP=propP;
+ }
+ // check if this is last prop of list
+ propP=propP->next;
+ if (!(propP && propP->propGroup==propGroup) && otherRulePropP && !ruleSpecificParsed) {
+ // End of alternatives for parsing this property, no rule-specific parsed yet, and there is a otherRuleProp
+ // parse "other"-rule's property instead
+ parsePropP=otherRulePropP;
+ }
+ #else
+ // simply parse it
+ parsePropP=propP;
+ propP=propP->next;
+ #endif
+ // now parse (or save for delayed parsing later)
+ if (parsePropP) {
+ if (parsePropP->delayedProcessing) {
+ // buffer parameters needed to parse later
+ PDEBUGPRINTFX(DBG_PARSE+DBG_EXOTIC,("parseMimeDir: property %s parsing delayed, rank=%hd",parsePropP->TCFG_CSTR(propname),parsePropP->delayedProcessing));
+ TDelayedPropParseParams dppp;
+ dppp.delaylevel = parsePropP->delayedProcessing;
+ dppp.start = p;
+ dppp.propDefP = parsePropP;
+ TDelayedParsingPropsList::iterator pos;
+ for (pos=fDelayedProps.begin(); pos!=fDelayedProps.end(); pos++) {
+ // insert at end or before first occurrence of higer delay
+ if ((*pos).delaylevel>dppp.delaylevel) {
+ fDelayedProps.insert(pos,dppp);
+ break;
+ }
+ }
+ if (pos==fDelayedProps.end())
+ fDelayedProps.push_back(dppp);
+ // update mandatory count (even if we haven't parsed it yet)
+ if (parsePropP->mandatory) foundmandatory++;
+ // skip for now
+ p=propname+n;
+ propparsed=true; // but is "parsed" for loop
+ break; // parse next
+ }
+ if (parseProperty(
+ p, // where to start interpreting property, will be updated past end of poperty
+ aItem, // item to store data into
+ parsePropP, // the (matching) property definition
+ repArray,
+ maxreps,
+ fMimeDirMode // MIME-DIR mode
+ )) {
+ // property parsed successfully
+ propparsed=true;
+ // count mandarory properties found
+ if (parsePropP->mandatory) foundmandatory++;
+ break; // parse next
+ }
+ // if not successfully parsed, continue with next property which
+ // can have the same name, but different parameter definitions
+ // eventually
+ } // if parseProp
+ } // while same property group (poperties with same name)
+ #ifdef NO_REMOTE_RULES
+ while(false); // if no remote rules, we do not loop
+ #endif
+ if (propparsed) break; // do not continue outer loop if inner loop has parsed a prop successfully
+ } // if name matches (=start of group found)
+ else {
+ // not start of group
+ // - next property
+ propP=propP->next;
+ }
+ } // while all properties
+ } // else: neither BEGIN nor END
+ if (!propparsed) {
+ // unknown property
+ PDEBUGPRINTFX(DBG_PARSE,("parseMimeDir: property unknown: %" FMT_LENGTH(".30") "s",FMT_LENGTH_LIMITED(30,aText)));
+ // skip parsed part (the name)
+ p=propname+n;
+ }
+ // p is now end of parsed part
+ // - skip rest up to EOLN (=any ctrl char)
+ // Note: we need to check if this is quoted-printable, otherwise we might NOT cancel soft breaks
+ char c;
+ bool isqp = false;
+ while ((c=*p)!=0) {
+ if (isEndOfLineOrText(c)) break; // end of line or string
+ if (c==';' && *(p+1)) {
+ if (strucmp(p+1, QP_ENCODING_PARAM, strlen(QP_ENCODING_PARAM))==0) {
+ c = *(p+1+strlen(QP_ENCODING_PARAM));
+ isqp = c==':' || c==';'; // the property is QP encoded, we need to cancel QP softbreaks while looking for end of property
+ }
+ }
+ p=nextunfolded(p,fMimeDirMode,isqp); // cancel soft breaks if we are in QP encoded property
+ }
+ // - skip entire EOLN (=all control chars in sequence %%%)
+ while (*p && (uInt8)(*p)<'\x20') p=nextunfolded(p,fMimeDirMode);
+ // set next property start point
+ aText=p;
+ } while (*aText); // exit if end of string
+ // now parse delayed ones (list is in delay order already)
+ if (aRootLevel) {
+ // process delayed properties only after entire record is parsed (i.e. when we are at root level here)
+ TDelayedParsingPropsList::iterator pos;
+ for (pos=fDelayedProps.begin(); pos!=fDelayedProps.end(); pos++) {
+ const char *p = (*pos).start; // where to start parsing
+ PDEBUGPRINTFX(DBG_PARSE+DBG_EXOTIC,(
+ "parseMimeDir: now parsing delayed property rank=%hd: %" FMT_LENGTH(".30") "s",
+ (*pos).delaylevel,
+ FMT_LENGTH_LIMITED(30,(*pos).start)
+ ));
+ if (parseProperty(
+ p, // where to start interpreting property, will be updated past end of property
+ aItem, // item to store data into
+ (*pos).propDefP, // the (matching) property definition
+ repArray,
+ maxreps,
+ fMimeDirMode // MIME-DIR mode
+ )) {
+ // count mandarory properties found
+ //%%% moved this to when we queue the delayed props, as mandatory count is per-profile
+ //if ((*pos).propDefP->mandatory) foundmandatory++;
+ }
+ else {
+ // delayed parsing failed
+ PDEBUGPRINTFX(DBG_PARSE,("parseMimeDir: failed delayed parsing of property %" FMT_LENGTH(".30") "s",FMT_LENGTH_LIMITED(30,(*pos).start)));
+ }
+ }
+ // we don't need them any more - clear delayed props
+ fDelayedProps.clear();
+ }
+ // verify integrity
+ if (foundmandatory<aProfileP->numMandatoryProperties) {
+ // not all mandatory properties found
+ POBJDEBUGPRINTFX(getSession(),DBG_ERROR,(
+ "parseMimeDir: missing %hd of %hd mandatory properies",
+ aProfileP->numMandatoryProperties-foundmandatory,
+ aProfileP->numMandatoryProperties
+ ));
+ // unsuccessful parsing
+ return false;
+ }
+ // successful parsing done
+ return true;
+ // %%%%% NOTE: exactly those fields in aItem should be assigned
+ // which are available in source and target.
+ // possibly this should be done in prepareForSendTo (o.‰) of
+ // MultiFieldItem...
+} // TMimeDirProfileHandler::parseLevels
+
+
+void TMimeDirProfileHandler::getOptionsFromDatastore(void)
+{
+ // get options datastore if one is related
+ if (fRelatedDatastoreP) {
+ fReceiverCanHandleUTC = fRelatedDatastoreP->getSession()->fRemoteCanHandleUTC;
+ fVCal10EnddatesSameDay = fRelatedDatastoreP->getSession()->fVCal10EnddatesSameDay;
+ fReceiverTimeContext = fRelatedDatastoreP->getSession()->fUserTimeContext; // default to user context
+ fDontSendEmptyProperties = fRelatedDatastoreP->getSession()->fDontSendEmptyProperties;
+ fDefaultOutCharset = fRelatedDatastoreP->getSession()->fDefaultOutCharset;
+ fDoQuote8BitContent = fRelatedDatastoreP->getSession()->fDoQuote8BitContent;
+ fDoNotFoldContent = fRelatedDatastoreP->getSession()->fDoNotFoldContent;
+ fTreatRemoteTimeAsLocal = fRelatedDatastoreP->getSession()->fTreatRemoteTimeAsLocal;
+ fTreatRemoteTimeAsUTC = fRelatedDatastoreP->getSession()->fTreatRemoteTimeAsUTC;
+ fAppliedRemoteRuleP =
+ #ifndef NO_REMOTE_RULES
+ fRelatedDatastoreP->getSession()->fAppliedRemoteRuleP;
+ #else
+ NULL;
+ #endif
+ }
+}
+
+
+// generate Data item (includes header and footer)
+void TMimeDirProfileHandler::generateText(TMultiFieldItem &aItem, string &aString)
+{
+ // get options datastore if one is related
+ getOptionsFromDatastore();
+ #ifdef SYDEBUG
+ PDEBUGPRINTFX(DBG_GEN+DBG_HOT,("Generating...."));
+ aItem.debugShowItem(DBG_DATA+DBG_GEN);
+ #endif
+ // baseclass just generates MIME-DIR
+ fBeginEndNesting=0; // no BEGIN out yet
+ generateMimeDir(aItem,aString);
+ #ifdef SYDEBUG
+ if (PDEBUGTEST(DBG_GEN+DBG_USERDATA)) {
+ // note, do not use debugprintf because string is too long
+ PDEBUGPRINTFX(DBG_GEN,("Generated: "));
+ PDEBUGPUTSXX(DBG_GEN+DBG_USERDATA,aString.c_str(),0,true);
+ }
+ #endif
+} // TMimeDirProfileHandler::generateText
+
+
+// parse Data item (includes header and footer)
+bool TMimeDirProfileHandler::parseText(const char *aText, stringSize aTextSize, TMultiFieldItem &aItem)
+{
+ //#warning "aTextSize must be checked!"
+ // get options datastore if one is related
+ getOptionsFromDatastore();
+ // baseclass just parses MIME-DIR
+ fBeginEndNesting = 0; // no BEGIN found yet
+ #ifdef SYDEBUG
+ if (PDEBUGTEST(DBG_PARSE)) {
+ // very detailed, show item being parsed
+ PDEBUGPRINTFX(DBG_PARSE+DBG_HOT,("Parsing: "));
+ PDEBUGPUTSXX(DBG_PARSE+DBG_USERDATA,aText,0,true);
+ }
+ #endif
+ if (parseMimeDir(aText,aItem)) {
+ if (fBeginEndNesting) {
+ PDEBUGPRINTFX(DBG_ERROR,("TMimeDirProfileHandler parsing ended with NestCount<>0: %hd",fBeginEndNesting));
+ return false; // unmatched BEGIN/END
+ }
+ #ifdef SYDEBUG
+ PDEBUGPRINTFX(DBG_PARSE,("Successfully parsed: "));
+ aItem.debugShowItem(DBG_DATA+DBG_PARSE);
+ #endif
+ return true;
+ }
+ else {
+ PDEBUGPRINTFX(DBG_ERROR,("Failed parsing item"));
+ return false;
+ }
+} // TMimeDirProfileHandler::parseText
+
+
+bool TMimeDirProfileHandler::parseForProperty(SmlItemPtr_t aItemP, const char *aPropName, string &aString)
+{
+ if (aItemP && aItemP->data)
+ return parseForProperty(smlPCDataToCharP(aItemP->data),aPropName,aString);
+ else
+ return false;
+} // TMimeDirProfileHandler::parseForProperty
+
+
+// scan Data item for specific property (used for quick type tests)
+bool TMimeDirProfileHandler::parseForProperty(const char *aText, const char *aPropName, string &aString)
+{
+ uInt16 n=strlen(aPropName);
+ while (*aText) {
+ const char *p=aText;
+ // find property end
+ do {
+ p=nextunfolded(p,fMimeDirMode,true);
+ } while ((*p)>=0x20);
+ // p now points to property end
+ if (strucmp(aText,aPropName,n)==0 && aText[n]==':') {
+ aText+=n+1; // start of value
+ aString.assign(aText,p-aText); // save value
+ return true;
+ }
+ // find next property beginning
+ do {
+ p=nextunfolded(p,fMimeDirMode,true);
+ } while (*p && ((*p)<0x20));
+ // set to beginning of next
+ aText=p;
+ }
+ // not found
+ return false;
+} // TMimeDirProfileHandler::parseForProperty
+
+
+
+// helper for newCTDataPropList
+void TMimeDirProfileHandler::enumerateLevels(const TProfileDefinition *aProfileP, SmlPcdataListPtr_t *&aPcdataListPP, const TProfileDefinition *aSelectedProfileP, TMimeDirItemType *aItemTypeP)
+{
+ // only if mode matches
+ if (!mimeModeMatch(aProfileP->modeDependency)) return;
+ // add name of this profile if...
+ // ...generally enabled for CTCap (shownIfSelectedOnly=false), independent of what other profiles might be selected (e.g. VALARM)
+ // ...this is the explicitly selected profile (like VTODO while creating DS 1.2 devinf for the tasks datastor)
+ // ...no profile is specifically selected, which means we want to see ALL profiles (like a DS 1.1 vCalendar type outside <datastore>)
+ // This means, the only case a name is NOT added are those with those having showlevel="no" when ANOTHER profile is explicitly selected.
+ if (!aProfileP->shownIfSelectedOnly || aProfileP==aSelectedProfileP || aSelectedProfileP==NULL) {
+ aPcdataListPP = addPCDataStringToList(TCFG_CSTR(aProfileP->levelName),aPcdataListPP);
+ // check for special subprofiles
+ if (aProfileP->profileMode==profm_vtimezones) {
+ // has STANDARD and DAYLIGHT subprofiles
+ aPcdataListPP = addPCDataStringToList("STANDARD",aPcdataListPP);
+ aPcdataListPP = addPCDataStringToList("DAYLIGHT",aPcdataListPP);
+ }
+ // add names of subprofiles, if any
+ const TProfileDefinition *subprofileP = aProfileP->subLevels;
+ while (subprofileP) {
+ // If this profile is the selected profile, ALL subprofiles must be shown in all cases (so we pass NULL)
+ enumerateLevels(subprofileP,aPcdataListPP,aProfileP==aSelectedProfileP ? NULL : aSelectedProfileP, aItemTypeP);
+ // next
+ subprofileP=subprofileP->next;
+ }
+ }
+} // TMimeDirProfileHandler::enumerateLevels
+
+
+
+// add a CTDataProp item to a CTDataPropList
+static void addCTDataPropToListIfNotExists(
+ SmlDevInfCTDataPropPtr_t aCTDataPropP, // existing CTDataProp item data structure, ownership is passed to list
+ SmlDevInfCTDataPropListPtr_t *aCTDataPropListPP // adress of list root pointer (which points to existing item list or NULL)
+)
+{
+ // add it to the list (but only if we don't already have it)
+ while (*aCTDataPropListPP) {
+ // check name
+ if (strcmp(smlPCDataToCharP(aCTDataPropP->prop->name),smlPCDataToCharP((*aCTDataPropListPP)->data->prop->name))==0) {
+ //%%% we can add merging parameters here as well
+ // same property already exists, forget this one
+ smlFreeDevInfCTDataProp(aCTDataPropP);
+ aCTDataPropP = NULL;
+ break;
+ }
+ aCTDataPropListPP = &((*aCTDataPropListPP)->next);
+ }
+ // if not detected duplicate, add it now
+ if (aCTDataPropP) {
+ addCTDataPropToList(aCTDataPropP,aCTDataPropListPP);
+ }
+} // addCTDataPropToListIfNotExists
+
+
+// add a CTData describing a property (as returned by newDevInfCTData())
+// as a new property without parameters to a CTDataPropList
+static void addNewPropToListIfNotExists(
+ SmlDevInfCTDataPtr_t aPropCTData, // CTData describing property
+ SmlDevInfCTDataPropListPtr_t *aCTDataPropListPP // adress of list root pointer (which points to existing item list or NULL)
+)
+{
+ SmlDevInfCTDataPropPtr_t propdataP = SML_NEW(SmlDevInfCTDataProp_t);
+ propdataP->param = NULL; // no params
+ propdataP->prop = aPropCTData;
+ addCTDataPropToListIfNotExists(propdataP, aCTDataPropListPP);
+} // addNewPropToListIfNotExists
+
+
+
+// helper for newCTDataPropList
+void TMimeDirProfileHandler::enumerateProperties(const TProfileDefinition *aProfileP, SmlDevInfCTDataPropListPtr_t *&aPropListPP, const TProfileDefinition *aSelectedProfileP, TMimeDirItemType *aItemTypeP)
+{
+ // remember start of properties
+ // add all properties of this level (if enabled)
+ // Note: if this is the explicitly selected (sub)profile, it will be shown under any circumstances
+ if ((!aProfileP->shownIfSelectedOnly || aProfileP==aSelectedProfileP || aSelectedProfileP==NULL) && mimeModeMatch(aProfileP->modeDependency)) {
+ if (aProfileP->profileMode==profm_vtimezones) {
+ // Add properties of VTIMEZONE here
+ addNewPropToListIfNotExists(newDevInfCTData("TZID"),aPropListPP);
+ addNewPropToListIfNotExists(newDevInfCTData("DTSTART"),aPropListPP);
+ addNewPropToListIfNotExists(newDevInfCTData("RRULE"),aPropListPP);
+ addNewPropToListIfNotExists(newDevInfCTData("TZOFFSETFROM"),aPropListPP);
+ addNewPropToListIfNotExists(newDevInfCTData("TZOFFSETTO"),aPropListPP);
+ addNewPropToListIfNotExists(newDevInfCTData("TZNAME"),aPropListPP);
+ }
+ else {
+ // normal profile defined in config, add properties as defined in profile, avoid duplicates
+ const TPropertyDefinition *propP = aProfileP->propertyDefs;
+ while (propP) {
+ if (propP->showInCTCap && mimeModeMatch(propP->modeDependency)) {
+ // - new list entry in CTCap (if property to be shown)
+ SmlDevInfCTDataPropPtr_t propdataP = SML_NEW(SmlDevInfCTDataProp_t);
+ propdataP->param = NULL; // default to no params
+ // - add params, if needed
+ SmlDevInfCTDataListPtr_t *nextParamPP = &(propdataP->param);
+ const TParameterDefinition *paramP = propP->parameterDefs;
+ while(paramP) {
+ // check if parameter is enabled for being shown in CTCap
+ if (paramP->showInCTCap && mimeModeMatch(paramP->modeDependency)) {
+ // For some older 1.1 devices (in particular Nokia 7610), enum values of default params
+ // in pre-MIME-DIR must be shown as param NAMES (not enums).
+ // But newer 1.2 Nokias like E90 need proper TYPE param with valEnums (when run in 1.2 mode. E90 is fine with 7610 style for 1.1)
+ // So: normally (fEnumDefaultPropParams==undefined==-1), we show 7610 style for 1.1 and E90 style for 1.2.
+ // <enumdefaultpropparams> and ENUMDEFAULTPROPPARAMS() can be used to control this behaviour when needed
+ if (
+ paramP->defaultparam &&
+ fMimeDirMode==mimo_old &&
+ (
+ (getSession()->fEnumDefaultPropParams==-1 && getSession()->getSyncMLVersion()<syncml_vers_1_2) || // auto mode and SyncML 1.1 or older
+ (getSession()->fEnumDefaultPropParams==1) // ..or explicitly enabled
+ )
+ ) {
+ // add the name extending enum values as param names
+ TEnumerationDef *enumP = paramP->convdef.enumdefs;
+ while(enumP) {
+ if (!TCFG_ISEMPTY(enumP->enumtext) && enumP->enummode==enm_translate) {
+ // create new param list entry
+ nextParamPP = addCTDataToList(newDevInfCTData(TCFG_CSTR(enumP->enumtext)),nextParamPP);
+ }
+ enumP=enumP->next;
+ }
+ }
+ else {
+ // - proper parameter with valEnum list
+ SmlDevInfCTDataPtr_t paramdataP = newDevInfCTData(TCFG_CSTR(paramP->paramname));
+ // - add valenums if any
+ SmlPcdataListPtr_t *nextValenumPP = &(paramdataP->valenum);
+ TEnumerationDef *enumP = paramP->convdef.enumdefs;
+ while(enumP) {
+ if (!TCFG_ISEMPTY(enumP->enumtext) && enumP->enummode==enm_translate) {
+ // create new valenum list entry
+ nextValenumPP = addPCDataStringToList(TCFG_CSTR(enumP->enumtext),nextValenumPP);
+ }
+ enumP=enumP->next;
+ }
+ // - add it to the params list
+ nextParamPP = addCTDataToList(paramdataP,nextParamPP);
+ }
+ } // if param to be shown
+ paramP=paramP->next;
+ }
+ // - get possible size limit and notruncate flag
+ uInt32 sz=0; // no size limit by default
+ bool noTruncate=false; // by default, truncation is ok
+ TFieldDefinition *fieldDefP = NULL;
+ for (sInt16 i=0; i<propP->numValues; i++) {
+ sInt16 fid=propP->convdefs[0].fieldid;
+ if (fid>=0) {
+ // Field type (we need it later when we have a maxsize, which is only allowed together with a datatype in 1.1 DTD)
+ if (!fieldDefP)
+ fieldDefP = fItemTypeP->getFieldDefinition(fid);
+ // Size
+ uInt32 fsz = fItemTypeP->getFieldOptions(fid)->maxsize; // only if related datastore (i.e. SyncML context)
+ // - smallest non-fieldblock (excludes RRULE-type special conversions), not-unknown and not-unlimited maxsize is used
+ if (fieldBlockSize(propP->convdefs[0])==1 && (sz==0 || sz>fsz) && fsz!=FIELD_OPT_MAXSIZE_NONE && sInt32(fsz)!=FIELD_OPT_MAXSIZE_UNKNOWN)
+ sz=fsz;
+ // If any field requests no truncation, report noTruncate
+ if (getSession()->getSyncMLVersion()>=syncml_vers_1_2 && fItemTypeP->getFieldOptions(fid)->notruncate)
+ noTruncate=true;
+ }
+ }
+ // - calculate our own maxoccur (value in our field options is not used for now %%%)
+ uInt32 maxOccur=0;
+ if (getSession()->getSyncMLVersion()>=syncml_vers_1_2) {
+ if (propP->nameExts) {
+ // name extensions determine repeat count
+ TPropNameExtension *extP = propP->nameExts;
+ while (extP) {
+ if (!extP->readOnly) {
+ if (extP->maxRepeat==REP_ARRAY) {
+ // no limit
+ maxOccur=0; // unlimited
+ break; // prevent other name extensions to intervene
+ }
+ else {
+ // limited number of occurrences, add to count
+ maxOccur+=extP->maxRepeat;
+ }
+ }
+ // next
+ extP=extP->next;
+ }
+ }
+ else {
+ // not repeating: property may not occur more than once
+ maxOccur=1;
+ }
+ }
+ // - some SyncML 1.0 clients crash when they see type/size
+ if (!(getSession()->fShowTypeSzInCTCap10) && getSession()->getSyncMLVersion()<=syncml_vers_1_0) {
+ sz = 0; // prevent size/type in SyncML 1.0 (as old clients like S55 crash if it is included)
+ }
+ // - find out if we need to show the type (before SyncML 1.2, Size MUST be preceeded by DataType)
+ // On the other hand, DataType MUST NOT be used in 1.2 for VersIt types!!!
+ cAppCharP dataType=NULL;
+ if (sz!=0 && fieldDefP && getSession()->getSyncMLVersion()<syncml_vers_1_2) {
+ // we have a size, so we NEED a datatype
+ TPropDataTypes dt = devInfPropTypes[fieldDefP->type];
+ if (dt==proptype_text) dt=proptype_chr; // SyncML 1.1 does not have "text" type
+ if (dt!=proptype_unknown)
+ dataType = propDataTypeNames[dt];
+ }
+ // - add property data descriptor
+ propdataP->prop = newDevInfCTData(propP->TCFG_CSTR(propname),sz,noTruncate,maxOccur,dataType);
+ if (propP->convdefs && propP->convdefs->convmode==CONVMODE_VERSION) {
+ // special case: add version valenum
+ addPCDataStringToList(aItemTypeP->getTypeVers(),&(propdataP->prop->valenum));
+ }
+ // add it if not already same-named property in the list, otherwise discard it
+ addCTDataPropToListIfNotExists(propdataP,aPropListPP);
+ } // if to be shown in CTCap
+ propP=propP->next;
+ } // while properties
+ } // normal profile defined in config
+ // add properties of other levels
+ const TProfileDefinition *subprofileP = aProfileP->subLevels;
+ while (subprofileP) {
+ // only if the current profile is the selected profile, properties of ALL contained subprofiles will be shown
+ // (otherwise, selection might be within the current profile, so we need to pass on the selection)
+ enumerateProperties(subprofileP,aPropListPP,aProfileP==aSelectedProfileP ? NULL : aSelectedProfileP, aItemTypeP);
+ // next
+ subprofileP=subprofileP->next;
+ }
+ }
+} // TMimeDirProfileHandler::enumerateProperties
+
+
+// helper: enumerate filter properties
+void TMimeDirProfileHandler::enumeratePropFilters(const TProfileDefinition *aProfileP, SmlPcdataListPtr_t &aFilterProps, const TProfileDefinition *aSelectedProfileP, TMimeDirItemType *aItemTypeP)
+{
+ // add all properties of this level (if enabled)
+ // Note: if this is the explicitly selected (sub)profile, it will be shown under any circumstances
+ if (!aProfileP->shownIfSelectedOnly || aProfileP==aSelectedProfileP) {
+ const TPropertyDefinition *propP = aProfileP->propertyDefs;
+ while (propP) {
+ if (
+ propP->canFilter &&
+ (propP->showInCTCap || aProfileP==aSelectedProfileP) &&
+ propP->convdefs && propP->convdefs[0].convmode!=CONVMODE_VERSION && propP->convdefs[0].convmode!=CONVMODE_PRODID
+ ) {
+ // Note: properties of explicitly selected (sub)profiles will be shown anyway,
+ // as only purpose of suppressing properties in devInf is to avoid
+ // duplicate listing in case of multiple subprofiles in ONE CTCap.
+ // - add property name to filter property list
+ addPCDataStringToList(TCFG_CSTR(propP->propname), &aFilterProps);
+ } // if to be shown in filterCap
+ propP=propP->next;
+ }
+ }
+ // add properties of other levels
+ const TProfileDefinition *subprofileP = aProfileP->subLevels;
+ while (subprofileP) {
+ if (aSelectedProfileP==NULL || subprofileP==aSelectedProfileP) {
+ // only if the current profile is the selected profile, filter properties of ALL contained subprofiles will be shown
+ enumeratePropFilters(subprofileP,aFilterProps,aProfileP==aSelectedProfileP ? NULL : aSelectedProfileP, aItemTypeP);
+ }
+ // next
+ subprofileP=subprofileP->next;
+ }
+} // TMimeDirProfileHandler::enumeratePropFilters
+
+
+#ifdef OBJECT_FILTERING
+
+// Filtering: add keywords and property names to filterCap
+void TMimeDirProfileHandler::addFilterCapPropsAndKeywords(SmlPcdataListPtr_t &aFilterKeywords, SmlPcdataListPtr_t &aFilterProps, TTypeVariantDescriptor aVariantDescriptor, TSyncItemType *aItemTypeP)
+{
+ // get pointer to selected variant (if none, all variants will be shown)
+ const TProfileDefinition *selectedSubprofileP = (const TProfileDefinition *)aVariantDescriptor;
+ // get pointer to mimedir item type
+ TMimeDirItemType *mimeDirItemTypeP;
+ GET_CASTED_PTR(mimeDirItemTypeP,TMimeDirItemType,aItemTypeP,"MIME-DIR profile used with non-MIME-DIR type");
+ // add name of all properties that have canFilter attribute set
+ enumeratePropFilters(fProfileDefinitionP,aFilterProps,selectedSubprofileP, mimeDirItemTypeP);
+} // TMimeDirProfileHandler::addFilterCapPropsAndKeywords
+
+#endif // OBJECT_FILTERING
+
+
+
+// generates SyncML-Devinf property list for type
+SmlDevInfCTDataPropListPtr_t TMimeDirProfileHandler::newCTDataPropList(TTypeVariantDescriptor aVariantDescriptor, TSyncItemType *aItemTypeP)
+{
+ TMimeDirItemType *itemTypeP = static_cast<TMimeDirItemType *>(aItemTypeP);
+ // get pointer to selected variant (if none, all variants will be shown)
+ const TProfileDefinition *selectedSubprofileP = (const TProfileDefinition *)aVariantDescriptor;
+ // generate new list
+ SmlDevInfCTDataPropListPtr_t proplistP = SML_NEW(SmlDevInfCTDataPropList_t);
+ SmlDevInfCTDataPropListPtr_t nextpropP = proplistP;
+ // generate BEGIN property
+ // - add property contents
+ nextpropP->data = SML_NEW(SmlDevInfCTDataProp_t);
+ nextpropP->data->param=NULL; // no params
+ // - property data descriptor
+ SmlDevInfCTDataPtr_t pdataP = newDevInfCTData("BEGIN");
+ nextpropP->data->prop = pdataP;
+ // - add valenums for all profiles and subprofiles
+ SmlPcdataListPtr_t *liststartPP = &pdataP->valenum;
+ enumerateLevels(fProfileDefinitionP,liststartPP,selectedSubprofileP,itemTypeP);
+ // generate END property
+ nextpropP->next = SML_NEW(SmlDevInfCTDataPropList_t);
+ nextpropP=nextpropP->next;
+ // - add property contents
+ nextpropP->data = SML_NEW(SmlDevInfCTDataProp_t);
+ nextpropP->data->param=NULL; // no params
+ // - property data descriptor
+ pdataP = newDevInfCTData("END");
+ nextpropP->data->prop = pdataP;
+ // - add valenums for all profiles and subprofiles
+ liststartPP = &pdataP->valenum;
+ enumerateLevels(fProfileDefinitionP,liststartPP,selectedSubprofileP,itemTypeP);
+ // generate all other properties of all levels
+ nextpropP->next=NULL; // in case no properties are found
+ SmlDevInfCTDataPropListPtr_t *propstartPP = &nextpropP->next;
+ enumerateProperties(fProfileDefinitionP,propstartPP,selectedSubprofileP,itemTypeP);
+ // done
+ return proplistP;
+} // TMimeDirProfileHandler::newCTDataPropList
+
+
+// Analyze CTCap part of devInf
+bool TMimeDirProfileHandler::analyzeCTCap(SmlDevInfCTCapPtr_t aCTCapP, TSyncItemType *aItemTypeP)
+{
+ TMimeDirItemType *itemTypeP = static_cast<TMimeDirItemType *>(aItemTypeP);
+ // assume all sublevels enabled (as long as we don't get a
+ // BEGIN CTCap listing all the available levels.
+ //aItemTypeP->setLevelOptions(NULL,true);
+ // check details
+ SmlDevInfCTDataPropListPtr_t proplistP = aCTCapP->prop;
+ if (proplistP) {
+ if (!itemTypeP->fReceivedFieldDefs) {
+ // there is a propList, and we haven't scanned one already for this type
+ // (could be the case for DS 1.2 vCalendar where we get events & tasks separately)
+ // so disable all non-mandatory fields first (available ones will be re-enabled)
+ for (sInt16 i=0; i<itemTypeP->fFieldDefinitionsP->numFields(); i++) {
+ itemTypeP->getFieldOptions(i)->available=false;
+ }
+ // force mandatory properties to be always "available"
+ setfieldoptions(NULL,fProfileDefinitionP,itemTypeP);
+ }
+ // now we have received fields
+ itemTypeP->fReceivedFieldDefs=true;
+ }
+ while (proplistP) {
+ // get property descriptor
+ SmlDevInfCTDataPtr_t propP = proplistP->data->prop;
+ // see if we have this property in any of the levels
+ setfieldoptions(propP,fProfileDefinitionP,itemTypeP);
+ // next property in CTCap
+ proplistP=proplistP->next;
+ } // properties in CTCap
+ return true;
+} // TMimeDirProfileHandler::analyzeCTCap
+
+
+
+// %%%%% dummy for now
+bool TMimeDirProfileHandler::setLevelOptions(const char *aLevelName, bool aEnable, TMimeDirItemType *aItemTypeP)
+{
+ // do it recursively.
+ // %%% we need to have a flag somewhere for these
+ // don't we have one already???
+ // YES, its fSubLevelRestrictions (supposedly a bitmask for max 32 levels)
+ // %%% checking the levels and ignoring incoming/outgoing items with
+ // wrong levels must be added later
+ return true;
+} // TMimeDirProfileHandler::setLevelOptions
+
+
+
+
+// enable fields related to aPropP property in profiles recursively
+// or (if aPropP is NULL), enable fields of all mandatory properties
+void TMimeDirProfileHandler::setfieldoptions(
+ const SmlDevInfCTDataPtr_t aPropP, // property to enable fields for, NULL if all mandatory properties should be enabled
+ const TProfileDefinition *aProfileP,
+ TMimeDirItemType *aItemTypeP
+)
+{
+ // set defaults
+ sInt32 propsize = FIELD_OPT_MAXSIZE_NONE;
+ sInt32 maxOccur = 0; // none by default
+ bool noTruncate=false;
+ const char* propname = NULL;
+ TFieldOptions *fo;
+ // get params from CTCap property definition (if any)
+ if (aPropP) {
+ // get name of CTCap property
+ propname = smlPCDataToCharP(aPropP->name);
+ // get eventual maxSize
+ if (aPropP->maxsize) {
+ if (getSession()->fIgnoreDevInfMaxSize) {
+ // remote rule flags maxsize as invalid (like in E90), flag it as unknown (but possibly limited)
+ propsize = FIELD_OPT_MAXSIZE_UNKNOWN;
+ }
+ else {
+ // treat as valid
+ StrToLong(smlPCDataToCharP(aPropP->maxsize),propsize);
+ }
+ }
+ // get eventual maxOccur
+ if (aPropP->maxoccur) {
+ StrToLong(smlPCDataToCharP(aPropP->maxoccur),maxOccur);
+ }
+ // get eventual noTruncate
+ if (aPropP->flags & SmlDevInfNoTruncate_f)
+ noTruncate=true;
+ // check for BEGIN to check for enabled sublevels
+ if (strucmp(propname,"BEGIN")==0) {
+ // check ValEnums that denote supported levels
+ SmlPcdataListPtr_t valenumP = aPropP->valenum;
+ if (valenumP) {
+ // we HAVE supported BEGINs listed, so disable all levels first
+ // and have them individually enabled below according to ValEnums
+ setLevelOptions(NULL,false,aItemTypeP);
+ }
+ while (valenumP) {
+ // get sublevel name
+ const char *slname = smlPCDataToCharP(valenumP->data);
+ setLevelOptions(slname,true,aItemTypeP); // enable this one
+ // check next
+ valenumP=valenumP->next;
+ }
+ }
+ } // if enabling for specified property
+ // enable all fields related to this property and set options
+ const TPropertyDefinition *propdefP = aProfileP->propertyDefs;
+ sInt16 j,q,i,o,r,bs;
+ while (propdefP) {
+ // compare
+ if (
+ (propname==NULL && propdefP->mandatory) ||
+ (propname && (strucmp(propname,propdefP->TCFG_CSTR(propname))==0))
+ ) {
+ // match (or enabling mandatory) -> enable all fields that are related to this property
+ // - values
+ for (i=0; i<propdefP->numValues; i++) {
+ // base field ID
+ j=propdefP->convdefs[i].fieldid;
+ bs=fieldBlockSize(propdefP->convdefs[i]);
+ if (j>=0) {
+ // field supported
+ TPropNameExtension *pneP = propdefP->nameExts;
+ if (pneP) {
+ while(pneP) {
+ o = pneP->fieldidoffs;
+ if (o>=0) {
+ r=0;
+ // for all repetitions (but only for first if mode is REP_ARRAY
+ // or field is an array)
+ do {
+ // make entire field block addressed by this convdef available
+ // and set maxoccur/notruncate
+ for (q=0; q<bs; q++) {
+ // flag available
+ fo = aItemTypeP->getFieldOptions(j+o+q);
+ if (fo) fo->available=true;
+ }
+ // set size if specified (only for first field in block)
+ fo = aItemTypeP->getFieldOptions(j+o);
+ if (fo) {
+ if (propsize!=FIELD_OPT_MAXSIZE_NONE) fo->maxsize=propsize;
+ // set maxoccur if specified
+ if (maxOccur!=0) fo->maxoccur=maxOccur;
+ // set noTruncate
+ if (noTruncate) fo->notruncate=true;
+ }
+ // next
+ o+=pneP->repeatInc;
+ } while (
+ ++r < pneP->maxRepeat &&
+ pneP->maxRepeat!=REP_ARRAY
+ #ifdef ARRAYFIELD_SUPPORT
+ && !aItemTypeP->getFieldDefinition(j)->array
+ #endif
+ );
+ }
+ pneP=pneP->next;
+ }
+ }
+ else {
+ // single variant, non-repeating property
+ // make entire field block addressed by this convdef available
+ for (q=0; q<bs; q++) { fo = aItemTypeP->getFieldOptions(j+q); if (fo) fo->available=true; }
+ // set size if specified
+ fo = aItemTypeP->getFieldOptions(j);
+ if (fo) {
+ if (propsize!=FIELD_OPT_MAXSIZE_NONE) fo->maxsize=propsize;
+ // set maxoccur if specified
+ if (maxOccur!=0) fo->maxoccur=maxOccur;
+ // set noTruncate
+ if (noTruncate) fo->notruncate=true;
+ }
+ }
+ }
+ } // enable values
+ // - parameter values
+ const TParameterDefinition *paramdefP = propdefP->parameterDefs;
+ while(paramdefP) {
+ // base field ID
+ j=paramdefP->convdef.fieldid;
+ bs=fieldBlockSize(paramdefP->convdef);
+ if (j>=0) {
+ // field supported
+ TPropNameExtension *pneP = propdefP->nameExts;
+ if (pneP) {
+ while(pneP) {
+ o = pneP->fieldidoffs;
+ if (o>=0) {
+ r=0;
+ // for all repetitions
+ do {
+ // make entire field block addressed by this convdef available
+ for (q=0; q<bs; q++) { fo = aItemTypeP->getFieldOptions(j+o+q); if (fo) fo->available=true; }
+ // set size if specified
+ fo = aItemTypeP->getFieldOptions(j+o);
+ if (propsize!=FIELD_OPT_MAXSIZE_NONE && fo) fo->maxsize=propsize;
+ // Note: MaxOccur and NoTruncate are not relevant for parameter values
+ // next
+ o+=pneP->repeatInc;
+ } while (
+ ++r < pneP->maxRepeat &&
+ pneP->maxRepeat!=REP_ARRAY
+ #ifdef ARRAYFIELD_SUPPORT
+ && !aItemTypeP->getFieldDefinition(j)->array
+ #endif
+ );
+ }
+ pneP=pneP->next;
+ }
+ }
+ else {
+ // single variant, non-repeating property
+ // make entire field block addressed by this convdef available
+ for (q=0; q<bs; q++) { fo=aItemTypeP->getFieldOptions(j+q); if (fo) fo->available=true; }
+ // set size if specified
+ fo = aItemTypeP->getFieldOptions(j);
+ if (propsize!=FIELD_OPT_MAXSIZE_NONE && fo) fo->maxsize=propsize;
+ }
+ }
+ paramdefP=paramdefP->next;
+ } // while
+ } // if known property
+ propdefP=propdefP->next;
+ }
+ // now enable fields in all subprofiles
+ const TProfileDefinition *subprofileP = aProfileP->subLevels;
+ while (subprofileP) {
+ setfieldoptions(aPropP,subprofileP,aItemTypeP);
+ // next
+ subprofileP=subprofileP->next;
+ }
+} // TMimeDirProfileHandler::setfieldoptions
+
+
+
+// set mode (for those profiles that have more than one, like MIME-DIR's old/standard)
+void TMimeDirProfileHandler::setProfileMode(sInt32 aMode)
+{
+ fProfileMode = aMode;
+ // determine derived mime mode
+ switch (aMode) {
+ case PROFILEMODE_OLD : fMimeDirMode=mimo_old; break; // 1 = old = vCard 2.1 / vCalendar 1.0
+ default : fMimeDirMode=mimo_standard; break; // anything else = standard = vCard 3.0 / iCalendar 2.0 style
+ }
+} // TMimeDirProfileHandler::setProfileMode
+
+
+
+// - check mode
+bool TMimeDirProfileHandler::mimeModeMatch(TMimeDirMode aMimeMode)
+{
+ return
+ aMimeMode==numMimeModes || // not dependent on MIME mode
+ aMimeMode==fMimeDirMode;
+} // TMimeDirProfileHandler::mimeModeMatch
+
+
+
+/* end of TMimeDirProfileHandler implementation */
+
+
+// Utility functions
+// -----------------
+
+
+/// @brief checks two timestamps if they represent an all-day event
+/// @param[in] aStart start time
+/// @param[in] aEnd end time
+/// @return 0 if not allday, x=1..n if allday (spanning x days) by one of the
+/// following criteria:
+/// - both start and end at midnight of the same day (= 1 day)
+/// - both start and end at midnight of different days (= 1..n days)
+/// - start at midnight and end between 23:59:00 and 23:59:59 of
+/// same or different days (= 1..n days)
+uInt16 AlldayCount(lineartime_t aStart, lineartime_t aEnd)
+{
+ lineartime_t startTime = lineartime2timeonly(aStart);
+ if (startTime!=0) return 0; // start not at midnight -> no allday
+ lineartime_t endTime = lineartime2timeonly(aEnd);
+ if (endTime==0) {
+ if (aStart==aEnd) aEnd += linearDateToTimeFactor; // one day
+ }
+ else if (endTime>= (23*MinsPerHour+59)*SecsPerMin*secondToLinearTimeFactor) {
+ // add one minute to make sure we reach into next day
+ aEnd += SecsPerMin*secondToLinearTimeFactor;
+ }
+ else
+ return 0; // allday criteria not met
+ // now calculate number of days
+ return (aEnd-aStart) / linearDateToTimeFactor;
+} // AlldayCount
+
+
+/// @brief checks two timestamps if they represent an all-day event
+/// @param[in] aStartFldP start time field
+/// @param[in] aEndFldP end time field
+/// @param[in] aTimecontext context to use to check allday criteria for all non-floating timestamps
+/// or UTC timestamps only (if aContextForUTC is set).
+/// @param[in] aContextForUTC if set, context is only applied for UTC timestamps, other non-floatings are checked as-is
+/// @return 0 if not allday, x=1..n if allday (spanning x days)
+uInt16 AlldayCount(TItemField *aStartFldP, TItemField *aEndFldP, timecontext_t aTimecontext, bool aContextForUTC)
+{
+ if (!aStartFldP->isBasedOn(fty_timestamp)) return 0;
+ if (!aEndFldP->isBasedOn(fty_timestamp)) return 0;
+ TTimestampField *startFldP = static_cast<TTimestampField *>(aStartFldP);
+ TTimestampField *endFldP = static_cast<TTimestampField *>(aEndFldP);
+ // check in specified time zone if originally UTC (or aContextForUTC not set), otherwise check as-is
+ timecontext_t tctx;
+ lineartime_t start = startFldP->getTimestampAs(!aContextForUTC || TCTX_IS_UTC(startFldP->getTimeContext()) ? aTimecontext : TCTX_UNKNOWN, &tctx);
+ lineartime_t end = endFldP->getTimestampAs(!aContextForUTC || TCTX_IS_UTC(endFldP->getTimeContext()) ? aTimecontext : TCTX_UNKNOWN, &tctx);
+ return AlldayCount(start,end);
+} // AlldayCount
+
+
+/// @brief makes two timestamps represent an all-day event
+/// @param[in/out] aStart start time within the first day, will be set to midnight (00:00:00)
+/// @param[in/out] aEnd end time within the last day or at midnight of the next day,
+/// will be set to midnight of the next day
+/// @param[in] aDays if>0, this is used to calculate the aEnd timestamp (aEnd input is
+/// ignored then)
+void MakeAllday(lineartime_t &aStart, lineartime_t &aEnd, sInt16 aDays)
+{
+ lineartime_t duration = 0;
+
+ // first calculate duration (assuming that even if there's a timezone problem, both
+ // timestamps will be affected so duration is still correct)
+ if (aDays<=0) {
+ // use implicit duration
+ duration = aEnd-aStart;
+ } else {
+ // use explicit duration
+ duration = aDays * linearDateToTimeFactor;
+ }
+ // truncate start to midnight
+ aStart = lineartime2dateonlyTime(aStart);
+ // calculate timestamp that for sure is in next day
+ aEnd = aStart + duration + linearDateToTimeFactor-1; // one unit less than a full day, ensures that 00:00:00 input will remain same day
+ // make day-only of next day
+ aEnd = lineartime2dateonlyTime(aEnd);
+} // MakeAllday
+
+
+/// @brief makes two timestamp fields represent an all-day event
+/// @param[in/out] aStartFldP start time within the first day, will be set to dateonly
+/// @param[in/out] aEndFldP end time within the last day or at midnight of the next day, will be set to dateonly of the next day
+/// @param[in] aTimecontext context to calculate day boundaries in (if timestamp is not already floating), can be floating to treat in context of start date
+/// @param[in] aDays if>0, this is used to calculate the aEnd timestamp (aEnd input is
+/// ignored then)
+/// @note fields will be made floating and dateonly
+void MakeAllday(TItemField *aStartFldP, TItemField *aEndFldP, timecontext_t aTimecontext, sInt16 aDays)
+{
+ if (!aStartFldP->isBasedOn(fty_timestamp)) return;
+ if (!aEndFldP->isBasedOn(fty_timestamp)) return;
+ TTimestampField *startFldP = static_cast<TTimestampField *>(aStartFldP);
+ TTimestampField *endFldP = static_cast<TTimestampField *>(aEndFldP);
+ // adjust in specified time zone (or floating)
+ timecontext_t tctx;
+ lineartime_t start = startFldP->getTimestampAs(aTimecontext,&tctx);
+ // context must match, unless either requested-as-is or timestamp is already floating
+ if (tctx!=aTimecontext && !TCTX_IS_UNKNOWN(aTimecontext) && !TCTX_IS_UNKNOWN(tctx)) return; // cannot do anything
+ // get end in same context as start is
+ lineartime_t end = endFldP->getTimestampAs(tctx);
+ // make allday
+ MakeAllday(start,end,aDays);
+ // store back and floating + dateonly
+ tctx = TCTX_UNKNOWN | TCTX_DATEONLY;
+ // for output format capable of date-only
+ startFldP->setTimestampAndContext(start,tctx);
+ endFldP->setTimestampAndContext(end,tctx);
+} // MakeAllday
+
+
+
+} // namespace sysync
+
+
+// eof
diff --git a/src/sysync/mimedirprofile.h b/src/sysync/mimedirprofile.h
new file mode 100755
index 0000000..e841d43
--- /dev/null
+++ b/src/sysync/mimedirprofile.h
@@ -0,0 +1,694 @@
+/*
+ * File: mimedirprofile.h
+ *
+ * Author: Lukas Zeller (luz@synthesis.ch)
+ *
+ * TMimeDirItemType
+ * base class for MIME DIR based content types (vCard, vCalendar...)
+ *
+ * Copyright (c) 2001-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ * 2009-01-09 : luz : created from mimediritemtype.h
+ *
+ */
+
+#ifndef MimeDirProfile_H
+#define MimeDirProfile_H
+
+// includes
+#include "syncitemtype.h"
+#include "multifielditemtype.h"
+#include "engine_defs.h"
+
+#include <set>
+
+namespace sysync {
+
+// special field conversion modes
+#define CONVMODE_NONE 0 // no conversion (just string copy), but includes value list parsing and enum conversion
+#define CONVMODE_VERSION 1 // version
+#define CONVMODE_PRODID 2 // PRODID
+#define CONVMODE_TIMESTAMP 3 // forced full timestamp, even if this is a date field
+#define CONVMODE_DATE 4 // forced date-only, even if this is a timestamp field
+#define CONVMODE_AUTODATE 5 // in vCal 1.0 style formats, force to timestamp representation, in MIME-DIR, show depending on actual value
+#define CONVMODE_AUTOENDDATE 6 // in vCal 1.0 style formats, force to timestamp representation - if actual value is a date, subtract 1 unit (to show end of previous day rather than midnight of next)
+#define CONVMODE_TZ 7 // time zone offset in ISO8601 hh:mm representation for vCalendar 1.0 representation
+#define CONVMODE_DAYLIGHT 8 // DAYLIGHT property to describe time zone with DST in vCalendar 1.0
+#define CONVMODE_TZID 9 // time zone ID for iCalendar 2.0
+#define CONVMODE_EMPTYONLY 10 // same as CONVMODE_NONE, except that assignment occurs only if field is still empty
+#define CONVMODE_BITMAP 11 // values, converted to integer, are interpreted as bit numbers
+#define CONVMODE_BLOB_B64 12 // 1:1 storage of (decoded) value into field. When encoding, b64 is used
+#define CONVMODE_MAILTO 13 // 1:1 storage of (decoded) value into field. When encoding, b64 is used
+#define CONVMODE_VALUETYPE 14 // automatic VALUE parameter e.g. for timestamp fields that contain a date-only value (VALUE=DATE) or duration (VALUE=DURATION)
+#define CONVMODE_MULTIMIX 15 // special mode for mapping enums to bits (like CONVMODE_BITMAP), but mixed from multiple fields and with option to store as-is (special enum "value" syntax needed)
+#define CONVMODE_FULLVALUETYPE 16 // explicit VALUE parameter, does not assume a default
+
+// derived type modes start here
+#define CONVMODE_MIME_DERIVATES 20
+
+// define those that we want to implement (also work as getConfMode conditionals)
+#define CONVMODE_RRULE CONVMODE_MIME_DERIVATES+0
+
+
+// special numvals
+#define NUMVAL_LIST -1 // property contains a value list (like EXDATE) rather than individual values (like N)
+
+
+
+// profile internal MIME-DIR mode (externally, profile mode is set by PROFILEMODE_xxx and setProfileMode()
+typedef enum {
+ mimo_old, // vCard 2.1 type encoding, CRLF, default params
+ mimo_standard, // MIME DIR conformant, vCard 3.0 mode
+ numMimeModes
+} TMimeDirMode;
+
+
+// VTIMEZONE generation mode (what timezone definition rules to include)
+typedef enum {
+ vtzgen_current,
+ vtzgen_start,
+ vtzgen_end,
+ vtzgen_range,
+ vtzgen_openend,
+ numVTimeZoneGenModes
+} TVTimeZoneGenMode;
+
+
+typedef enum {
+ tzidgen_default,
+ tzidgen_olson,
+ numTzIdGenModes
+} TTzIdGenMode;
+
+
+
+// name extension map
+typedef uInt32 TNameExtIDMap;
+
+// forward
+class TProfileDefinition;
+class TPropertyDefinition;
+class TMimeDirItemType;
+class TRemoteRuleConfig;
+
+// enumeration modes
+typedef enum {
+ enm_translate, // translation from value to name and vice versa
+ enm_prefix, // enumtext/enumval are prefixes of
+ enm_default_name, // default name when translating from value to name
+ enm_default_value, // default value when translating from name to value
+ enm_ignore, // ignore value or name
+ numEnumModes
+} TEnumMode;
+
+
+// enumeration element definition
+class TEnumerationDef {
+public:
+ // constructor/destructor
+ TEnumerationDef(const char *aEnumName, const char *aEnumVal, TEnumMode aMode, sInt16 aNameExtID=-1);
+ ~TEnumerationDef();
+ // next item
+ TEnumerationDef *next;
+ // enum text
+ TCFG_STRING enumtext;
+ // enum translation (value to be stored in DB field), NULL if no translation
+ TCFG_STRING enumval;
+ // enum mode
+ TEnumMode enummode;
+ // ID (0..31) identifying ID of this value for property name extension purposes
+ // -1 means value is irrelevant to name extension
+ sInt16 nameextid;
+}; // TEnumerationDef
+
+
+// conversion & storage definition
+class TConversionDef {
+public:
+ // constructor/destructor
+ TConversionDef();
+ ~TConversionDef();
+ // tools
+ void addEnum(const char *aEnumName, const char *aEnumVal, TEnumMode aMode=enm_translate);
+ void addEnumNameExt(TPropertyDefinition *aProp, const char *aEnumName, const char *aEnumVal=NULL, TEnumMode aMode=enm_translate);
+ TConversionDef *setConvDef(sInt16 aFieldId=FID_NOT_SUPPORTED,sInt16 aConvMode=0,char aCombSep=0);
+ const TEnumerationDef *findEnumByName(const char *aName, sInt16 n=0) const;
+ const TEnumerationDef *findEnumByVal(const char *aVal, sInt16 n=0) const;
+ // base field id for parameter (will be offset for name-extended and repeated properties)
+ sInt16 fieldid; // VARIDX_UNDEFINED (negative) means value is not supported
+ // enumeration list, NULL if none
+ TEnumerationDef *enumdefs;
+ // conversion
+ sInt16 convmode; // 0=direct, 1..n=special procedure needed
+ char combineSep; // 0=no combination, char=char to be used to combine multiple values in field
+}; // TConversionDef
+
+
+// parameter definition
+class TParameterDefinition {
+public:
+ // constructor/destructor
+ TParameterDefinition(const char *aName, bool aDefault, bool aExtendsName, bool aShowNonEmpty, bool aShowInCTCap, TMimeDirMode aModeDep);
+ ~TParameterDefinition();
+ // tools
+ TConversionDef *setConvDef(sInt16 aFieldId=FID_NOT_SUPPORTED,sInt16 aConvMode=0,char aCombSep=0)
+ { return convdef.setConvDef(aFieldId,aConvMode,aCombSep); };
+ TNameExtIDMap getExtIDbit(const char *aEnumName, sInt16 n=0);
+ // next
+ TParameterDefinition *next;
+ // parameter name
+ TCFG_STRING paramname; // NULL for terminator
+ // used as default param, for example for type tags which have no explicit TYPE= in older vXX formats
+ bool defaultparam;
+ // parameter exists only in specific MIME-DIR mode (set to numMimeModes for non-dependent parameter)
+ TMimeDirMode modeDependency;
+ // parameter can extend property name by enumerated values with nameextid's
+ bool extendsname;
+ // if parameter has non-empty value, property will be treated as non-empty
+ bool shownonempty;
+ // flag if parameter should be (not necessarily IS, depending on SyncML version) shown in CTCap
+ bool showInCTCap;
+ // conversion information
+ TConversionDef convdef;
+}; // TParameterDefinition
+
+
+// property name extension by values of parameters
+class TPropNameExtension {
+public:
+ // constructor/destructor
+ TPropNameExtension(
+ TNameExtIDMap aMusthave_ids, TNameExtIDMap aForbidden_ids, TNameExtIDMap aAddtlSend_ids,
+ sInt16 aFieldidoffs, sInt16 aMaxRepeat, sInt16 aRepeatInc, sInt16 aMinShow,
+ bool aOverwriteEmpty, bool aReadOnly, sInt16 aRepeatID
+ );
+ ~TPropNameExtension();
+ // link to next, NULL if end
+ TPropNameExtension *next;
+ // Bitmap, has bit set for each nameextid which must be present for property
+ // in order to store it with given fid offset.
+ // - This nameextids will also be used to generate properties with
+ // corresponding parameter values.
+ TNameExtIDMap musthave_ids;
+ // Bitmap, has bit set for each nameextid which MAY NOT be present for
+ // property in order to store it with given fid offset.
+ TNameExtIDMap forbidden_ids;
+ // Bitmap, has bit set for each nameextid which should additionally be present
+ // when sending the property (such as VOICE for telephone), but is not a musthave.
+ TNameExtIDMap addtlSend_ids;
+ // field ID offset to be used for storage of property value(s) and
+ // non-name extending parameter value(s) on musthave_ids/forbidden_ids matches
+ // OFFS_NOSTORE : prevents storing/generating of this property value
+ sInt16 fieldidoffs;
+ // allowable repeat count (adding repeatInc to all related field ids when property is repeated)
+ // - if set to REP_REWRITE (=0), value can occur multiple times, but later occurrences
+ // override earlier ones (no offset incrementing)
+ // - if set to REP_ARRAY, repeat is unlimited (should be used with array fields only)
+ sInt16 maxRepeat;
+ // if maxRepeat>1, the offset is incremented by the given value
+ // (to allow multi-value properties to be repeated blockwise, not field-by-field)
+ sInt16 repeatInc;
+ // minimal number of times the property should be shown, even if repetitions do not contain
+ // any values. If set to 0, property will not show at all if no values are there
+ sInt16 minShow;
+ // flag to allow overwriting empty instances with next non-empty instance of same property
+ bool overwriteEmpty;
+ // flag for name extension variants only used for parsing, not for generating
+ bool readOnly;
+ // unique (over all TPropNameExtension) ID used for keeping track of repetitions at parsing
+ sInt16 repeatID;
+}; // TPropNameExtension
+
+
+// property definition
+class TPropertyDefinition {
+public:
+ // constructor/destructor
+ TPropertyDefinition(const char* aName, sInt16 aNumVals, bool aMandatory, bool aShowInCTCap, bool aSuppressEmpty, uInt16 aDelayedProcessing, char aValuesep, char aAltValuesep, uInt16 aPropertyGroupID, bool aCanFilter, TMimeDirMode aModeDep);
+ ~TPropertyDefinition();
+ // tools
+ TParameterDefinition *addParam(const char *aName, bool aDefault, bool aExtendsName, bool aShowNonEmpty=false, bool aShowInCTCap=false, TMimeDirMode aModeDep=numMimeModes);
+ void addNameExt(TProfileDefinition *aRootProfile, // for profile-global RepID generation
+ TNameExtIDMap aMusthave_ids, TNameExtIDMap aForbidden_ids, TNameExtIDMap aAddtlSend_ids,
+ sInt16 aFieldidoffs, sInt16 aMaxRepeat=1, sInt16 aRepeatInc=1, sInt16 aMinShow=-1,
+ bool aOverwriteEmpty=true, // show all, but overwrite empty repetitions
+ bool aReadOnly=false, // not a parsing alternative
+ sInt16 aShareCountOffs=0 // not sharing the repeat ID with a previous name extension
+ );
+ TConversionDef *setConvDef(sInt16 aValNum, sInt16 aFieldId=FID_NOT_SUPPORTED,sInt16 aConvMode=0,char aCombSep=0);
+ TParameterDefinition *findParameter(const char *aNam, sInt16 aLen=0);
+ // next in list
+ TPropertyDefinition *next;
+ // property name
+ TCFG_STRING propname;
+ // property name extension list.
+ // - If NULL, property is non-repeatable and has no name extensions at all
+ TPropNameExtension *nameExts;
+ // number of values
+ sInt16 numValues;
+ // conversion specification(s) for each value
+ TConversionDef *convdefs;
+ // if set, property has a list of values that are stored in an array field or
+ // by offseting fid. Note that a PropNameExtension is needed to allow storing more
+ // than a single value. If valuelist=true, convdefs should only contain a single entry,
+ // other entries are not used
+ bool valuelist;
+ // char to separate value list items (defaults to semicolon)
+ char valuesep;
+ char altvaluesep; // second value separator to respect when parsing (generating always uses valuesep)
+ // parameter listm
+ TParameterDefinition *parameterDefs;
+ // mandatory
+ bool mandatory;
+ // flag if property should be shown in CTCap
+ bool showInCTCap;
+ // flag if property can be used in filters (and will be shown in FilterCap, when showInCTCap is true as well
+ bool canFilter;
+ // flag if property should be suppressed if it has only empty values
+ bool suppressEmpty;
+ // delayed processing order (for things like RRULE that must be processed at the end)
+ uInt16 delayedProcessing;
+ // property exists only in specific MIME-DIR mode (set to numMimeModes for non-dependent properties)
+ TMimeDirMode modeDependency;
+ // internal for creation of name extension IDs
+ sInt16 nextNameExt;
+ // property group ID
+ uInt16 propGroup; // starting at 1, groups subsequent props that have the same name
+ #ifndef NO_REMOTE_RULES
+ // set if property enabled only if ruleDependency matches session's applied rule (even if NULL = no rule must be applied)
+ bool dependsOnRemoterule;
+ // the rule that must be selected to enable this property. If NULL, this property
+ // is used only if NO rule is selected.
+ TRemoteRuleConfig *ruleDependency;
+ #ifdef CONFIGURABLE_TYPE_SUPPORT
+ // name of remote rule dependency (will be resolved to set ruleDependency)
+ TCFG_STRING dependencyRuleName;
+ #endif
+ #endif
+}; // TPropertyDefinition
+
+
+// enumeration modes
+typedef enum {
+ profm_custom, // custom defined profile/subprofile
+ profm_vtimezones, // VTIMEZONE profile(s), expands to a VTIMEZONE for every time zone referenced by convmode TZID fields
+ numProfileModes
+} TProfileModes;
+
+
+// Profile level definition
+class TProfileDefinition {
+public:
+ // constructor/destructor
+ TProfileDefinition(
+ TProfileDefinition *aParentProfileP, // parent profile
+ const char *aProfileName, // name
+ sInt16 aNumMandatory,
+ bool aShowInCTCapIfSelectedOnly,
+ TProfileModes aProfileMode,
+ TMimeDirMode aModeDep
+ );
+ ~TProfileDefinition();
+ // tools
+ TConversionDef *setConvDef(sInt16 aFieldId=FID_NOT_SUPPORTED,sInt16 aConvMode=0,char aCombSep=0)
+ { return levelConvdef.setConvDef(aFieldId,aConvMode,aCombSep); };
+ TProfileDefinition *addSubProfile(
+ const char *aProfileName, // name
+ sInt16 aNumMandatory,
+ bool aShowInCTCapIfSelectedOnly,
+ TProfileModes aProfileMode = profm_custom,
+ TMimeDirMode aModeDep = numMimeModes
+ );
+ TPropertyDefinition *addProperty(
+ const char *aName, // name
+ sInt16 aNumValues, // number of values, NUMVAL_LIST if it is a value list
+ bool aMandatory, // mandatory
+ bool aShowInCTCap, // show in CTCap
+ bool aSuppressEmpty, // suppress empty ones on send
+ uInt16 aDelayedProcessing=0, // delayed processing when parsed, 0=immediate processing, 1..n=delayed
+ char aValuesep=';', // value separator
+ uInt16 aPropertyGroupID=0, // property group ID (alternatives for same-named properties should have same ID>0)
+ bool aCanFilter=false, // can be filtered -> show in filter cap
+ TMimeDirMode aModeDep=numMimeModes, // property valid only for specific MIME mode
+ char aAltValuesep=0 // no alternate separator
+ );
+ void usePropertiesOf(TProfileDefinition *aProfile);
+ TPropertyDefinition *getPropertyDef(const char *aPropName);
+ sInt16 getPropertyMainFid(const char *aPropName, uInt16 aIndex);
+ TProfileDefinition *findProfile(const char *aNam);
+ // next in chain
+ TProfileDefinition *next;
+ // parent profile
+ TProfileDefinition *parentProfile; // NULL if root
+ // Profile Level name
+ TCFG_STRING levelName;
+ // Level existence control field (affected/tested when level entered)
+ // NOTE: levelConvdef.enumdefs is used specially: it points to a SINGLE entry,
+ // not an array (no terminator!!). This entry contains IN THE enumval
+ // field (NOT enumtext!!!) the value to be stored in the field
+ // indicated by fieldid when this level is entered with BEGIN
+ TConversionDef levelConvdef;
+ // this subprofile (and related properties and sub-sub-profiles)
+ // is shown in devInf only if it is explictly selected or if all are selected
+ bool shownIfSelectedOnly;
+ // number of mandatory properties in this level
+ sInt16 numMandatoryProperties;
+ // property list, NULL if none
+ TPropertyDefinition *propertyDefs;
+ // sublevel list, NULL if none
+ TProfileDefinition *subLevels;
+ // next repeat ID for this (root) profile
+ sInt16 nextRepID;
+ // profile mode (custom profile or predefined profile like vTIMEZONE)
+ TProfileModes profileMode;
+ // dependency on MIME-dir mode
+ TMimeDirMode modeDependency;
+private:
+ bool ownsProps;
+}; // TProfileDefinition
+
+
+// MIME profile definition config
+class TMIMEProfileConfig : public TProfileConfig
+{
+ typedef TProfileConfig inherited;
+public:
+ TMIMEProfileConfig(const char* aName, TConfigElement *aParentElement);
+ virtual ~TMIMEProfileConfig();
+ // handler factory
+ virtual TProfileHandler *newProfileHandler(TMultiFieldItemType *aItemTypeP);
+ // properties
+ // - root profile
+ TProfileDefinition *fRootProfileP;
+ // - options
+ bool fUnfloatFloating; // set if floating timestamps should always be unfloated into item time zone
+ TVTimeZoneGenMode fVTimeZoneGenMode; // how outgoing VTIMEZONE records should be generated
+ TTzIdGenMode fTzIdGenMode; // what type of TZIDs should be generated
+protected:
+ // check config elements
+ #ifdef CONFIGURABLE_TYPE_SUPPORT
+ virtual bool localStartElement(const char *aElementName, const char **aAttributes, sInt32 aLine);
+public:
+ virtual void localResolve(bool aLastPass);
+protected:
+ virtual void nestedElementEnd(void);
+ // - check conversion mode
+ virtual bool getConvMode(const char *aText, sInt16 &aConvMode);
+ #endif
+ virtual void clear();
+private:
+ #ifdef CONFIGURABLE_TYPE_SUPPORT
+ // parsing help
+ bool getConvAttrs(const char **aAttributes, sInt16 &aFid, sInt16 &aConvMode, char &aCombSep);
+ bool getMask(const char **aAttributes, const char *aName, TParameterDefinition *aParamP, TNameExtIDMap &aMask);
+ bool processPosition(TParameterDefinition *aParamP, const char **aAttributes);
+ // parsing vars
+ TProfileDefinition *fOpenProfile; // profile being parsed
+ TPropertyDefinition *fOpenProperty; // property being parsed
+ TParameterDefinition *fOpenParameter; // parameter being parsed
+ TConversionDef *fOpenConvDef; // conversion definition being parsed
+ TPropertyDefinition *fLastProperty; // last property added in profile (to build groups)
+ uInt16 fPropertyGroupID; // property grouping
+ #endif
+}; // TMIMEProfileConfig
+
+
+
+// delayed property parsing info
+typedef struct {
+ uInt16 delaylevel;
+ const char *start;
+ const TPropertyDefinition *propDefP;
+} TDelayedPropParseParams;
+// delayed property parsing list
+typedef std::list<TDelayedPropParseParams> TDelayedParsingPropsList;
+
+// used time context set
+typedef std::set<timecontext_t> TTCtxSet;
+// parsed TZID map
+typedef std::map<string,timecontext_t> TParsedTzidSet;
+
+
+
+class TMimeDirProfileHandler : public TProfileHandler
+{
+ typedef TProfileHandler inherited;
+public:
+ // constructor
+ TMimeDirProfileHandler(
+ TMIMEProfileConfig *aMIMEProfileCfgP,
+ TMultiFieldItemType *aItemTypeP
+ );
+ // destructor
+ virtual ~TMimeDirProfileHandler();
+ #ifdef OBJECT_FILTERING
+ // filtering
+ // - get field index of given filter expression identifier.
+ virtual sInt16 getFilterIdentifierFieldIndex(const char *aIdentifier, uInt16 aIndex);
+ // - add keywords and property names to filterCap
+ virtual void addFilterCapPropsAndKeywords(SmlPcdataListPtr_t &aFilterKeywords, SmlPcdataListPtr_t &aFilterProps, TTypeVariantDescriptor aVariantDescriptor, TSyncItemType *aItemTypeP);
+ #endif
+ // - obtain property list for type, returns NULL if none available
+ virtual SmlDevInfCTDataPropListPtr_t newCTDataPropList(TTypeVariantDescriptor aVariantDescriptor, TSyncItemType *aItemTypeP);
+ // - Analyze CTCap part of devInf
+ virtual bool analyzeCTCap(SmlDevInfCTCapPtr_t aCTCapP, TSyncItemType *aItemTypeP);
+ // set profile options
+ // - mode (for those profiles that have more than one, like MIME-DIR's old/standard)
+ virtual void setProfileMode(sInt32 aMode);
+ // generate Text Data (includes header and footer)
+ virtual void generateText(TMultiFieldItem &aItem, string &aString);
+ // parse Data item (includes header and footer)
+ virtual bool parseText(const char *aText, stringSize aTextSize, TMultiFieldItem &aItem);
+ // scan for specific property value string (for version check)
+ bool parseForProperty(const char *aText, const char *aPropName, string &aString);
+ bool parseForProperty(SmlItemPtr_t aItemP, const char *aPropName, string &aString);
+ // get profile definition
+ TProfileDefinition *getProfileDefinition(void) { return fProfileDefinitionP; };
+private:
+ // Settable options
+ // - profile mode
+ sInt32 fProfileMode;
+ // - derived from profile mode: MIME dir mode
+ TMimeDirMode fMimeDirMode;
+ // - ability of receiver to handle UTC
+ bool fReceiverCanHandleUTC;
+ // - how end dates should be formatted
+ bool fVCal10EnddatesSameDay;
+ // - empty property policy
+ bool fDontSendEmptyProperties;
+ // - default output charset
+ TCharSets fDefaultOutCharset;
+ // - user time context
+ timecontext_t fReceiverTimeContext;
+ // - set if any 8-bit content must be encoded QUOTED-PRINTABLE
+ bool fDoQuote8BitContent;
+ // - set if no line folding should be done
+ bool fDoNotFoldContent;
+ // - time handling
+ bool fTreatRemoteTimeAsLocal;
+ bool fTreatRemoteTimeAsUTC;
+ // - dependency on certain remote rule
+ TRemoteRuleConfig *fAppliedRemoteRuleP;
+ // vars
+ TMIMEProfileConfig *fProfileCfgP; // the MIME-DIR profile config element
+ // property definitions
+ TProfileDefinition *fProfileDefinitionP;
+ // BEGIN/END nesting
+ sInt16 fBeginEndNesting;
+ // time zone management
+ bool fHasExplicitTZ; // parsed or generated explicit time zone for entire item (CONVMODE_TZ)
+ timecontext_t fItemTimeContext; // time zone context for entire item, is copy of session's user context as long as fHasExplicitTZ not set
+ timecontext_t fPropTZIDtctx; // property level time zone context - is set whenever a CONVMODE_TZID is successfully parsed or generated, reset at property start
+ TTCtxSet fUsedTCtxSet; // all time contexts used in this item (for generating)
+ lineartime_t fEarliestTZDate; // earliest date/time generated using a time context from fUsedTCtxSet
+ lineartime_t fLatestTZDate; // latest date/time generated using a time context from fUsedTCtxSet
+ TParsedTzidSet fParsedTzidSet; // all time contexts parsed in from VTIMEZONE
+ // delayed generation of VTIMEZONE subprofile (after all TZID are collected in fUserTctxSet)
+ const TProfileDefinition *fVTimeZonePendingProfileP; // the profile definition, NULL if none
+ size_t fVTimeZoneInsertPos; // where to insert VTIMEZONE
+ // delayed processing
+ TDelayedParsingPropsList fDelayedProps; // list of properties to parse out-of-order
+ // helper
+ void getOptionsFromDatastore(void);
+protected:
+ // generate MIME-DIR from item into string object
+ void generateMimeDir(TMultiFieldItem &aItem, string &aString);
+ // parse MIME-DIR from specified string into item
+ bool parseMimeDir(const char *aText, TMultiFieldItem &aItem);
+ // special field translations (to be overridden in derived classes)
+ // - returns the size of the field block (how many fids in sequence) related
+ // to a given convdef (for multi-field conversion modes such as CONVMODE_RRULE
+ virtual sInt16 fieldBlockSize(const TConversionDef &aConvDef);
+ // - field value to string for further MIME-DIR generation processing
+ virtual bool fieldToMIMEString(
+ TMultiFieldItem &aItem, // the item where data goes to
+ sInt16 aFid, // the field ID (can be NULL for special conversion modes)
+ sInt16 aArrIndex, // the repeat offset to handle array fields
+ const TConversionDef *aConvDefP, // the conversion definition record
+ string &aString // output string
+ );
+ // - single MIME-DIR value string to field (to be overridden in derived classes)
+ virtual bool MIMEStringToField(
+ const char *aText, // the value text to assign or add to the field
+ const TConversionDef *aConvDefP, // the conversion definition record
+ TMultiFieldItem &aItem, // the item where data goes to
+ sInt16 aFid, // the field ID (can be NULL for special conversion modes)
+ sInt16 aArrIndex // the repeat offset to handle array fields
+ );
+private:
+ // helpers for CTCap/FilterCap
+ // - set field options (enabled, maxsize, maxoccur, notruncate) of fields related to aPropP property in profiles recursively
+ // or (if aPropP is NULL), enable fields of all mandatory properties
+ void setfieldoptions(
+ const SmlDevInfCTDataPtr_t aPropP, // property to enable fields for, NULL if all mandatory properties should be enabled
+ const TProfileDefinition *aProfileP,
+ TMimeDirItemType *aItemTypeP
+ );
+ // - set level
+ bool setLevelOptions(const char *aLevelName, bool aEnable, TMimeDirItemType *aItemTypeP);
+ // - add level description to CTCap list
+ void enumerateLevels(const TProfileDefinition *aProfileP, SmlPcdataListPtr_t *&aPcdataListPP, const TProfileDefinition *aSelectedProfileP, TMimeDirItemType *aItemTypeP);
+ // - add property description to CTCap list
+ void enumerateProperties(const TProfileDefinition *aProfileP, SmlDevInfCTDataPropListPtr_t *&aPropListPP, const TProfileDefinition *aSelectedProfileP, TMimeDirItemType *aItemTypeP);
+ // - enumerate filter properties
+ void enumeratePropFilters(const TProfileDefinition *aProfileP, SmlPcdataListPtr_t &aFilterProps, const TProfileDefinition *aSelectedProfileP, TMimeDirItemType *aItemTypeP);
+ // - check mode
+ bool mimeModeMatch(TMimeDirMode aMimeMode);
+ // helpers for generateMimeDir()
+ // - generate parameter or property value(list),
+ // returns: 2 if value found, 1 if no value (but field supported), 0 if field not supported
+ sInt16 generateValue(
+ TMultiFieldItem &aItem, // the item where data comes from
+ const TConversionDef *aConvDefP,
+ sInt16 aBaseOffset, // basic fid offset to use
+ sInt16 aArrayOffset, // additional offset to use, or array index in case of array field
+ string &aString, // where value is ADDED
+ char aSeparator,
+ TMimeDirMode aMimeMode, // MIME mode (older or newer vXXX format compatibility)
+ bool aParamValue, // set if generating parameter value (different escaping rules, i.e. ";" and ":" must be escaped)
+ bool aStructured, // set if value consists of multiple values (needs ";" escaping)
+ bool aCommaEscape, // set if "," content escaping is needed (for values in valuelists like TYPE=TEL,WORK etc.)
+ TEncodingTypes &aEncoding, // modified if special value encoding is required
+ bool &aNonASCII, // set if any non standard 7bit ASCII-char is contained
+ char aFirstChar=0 // will be appended before value if there is any value
+ );
+ // - recursive expansion of properties
+ void expandProperty(
+ TMultiFieldItem &aItem, // the item where data comes from
+ string &aString, // the string to add properties to
+ const char *aPrefix, // the prefix (property name)
+ const TPropertyDefinition *aPropP, // the property to generate (all instances)
+ TMimeDirMode aMimeMode // MIME mode (older or newer vXXX format compatibility)
+ );
+ // - generate single property (except for valuelist-type properties like EXDATE
+ sInt16 generateProperty(
+ TMultiFieldItem &aItem, // the item where data comes from
+ string &aString, // the string to add properties to
+ const char *aPrefix, // the prefix (property name)
+ const TPropertyDefinition *aPropP, // the property to generate (single instance)
+ sInt16 aBaseOffset, // field ID offset to be used
+ sInt16 aRepeatOffset, // additional repeat offset / array index
+ TMimeDirMode aMimeMode, // MIME mode (older or newer vXXX format compatibility)
+ bool aSuppressEmpty, // if set, a property with only empty values will not be generated
+ TPropNameExtension *aPropNameExt=NULL // propname extension for generating musthave param values and maxrep/repinc for valuelists
+ );
+ // - generate parameters for one property instance
+ // returns true if parameters with shownonempty=true were generated
+ bool generateParams(
+ TMultiFieldItem &aItem, // the item where data comes from
+ string &aString, // the string to add parameters to
+ const TPropertyDefinition *aPropP, // the property to generate (all instances)
+ TMimeDirMode aMimeMode, // MIME mode (older or newer vXXX format compatibility)
+ sInt16 aBaseOffset,
+ sInt16 aRepOffset,
+ TPropNameExtension *aPropNameExt // propname extension for generating musthave param values
+ );
+ // - recursively generate levels
+ void generateLevels(
+ TMultiFieldItem &aItem,
+ string &aString,
+ const TProfileDefinition *aProfileP
+ );
+ // - parse parameter or property value(list), returns false if no value(list)
+ bool parseValue(
+ const string &aText, // string to parse as value (could be binary content)
+ const TConversionDef *aConvDefP,
+ sInt16 aBaseOffset, // base offset
+ sInt16 aRepOffset, // repeat offset, adds to aBaseOffset for non-array fields, is array index for array fileds
+ TMultiFieldItem &aItem, // the item where data goes to
+ bool &aNotEmpty, // is set true (but never set false) if property contained any (non-positional) values
+ char aSeparator, // separator between values that consist of a list of enums etc. (more common for params than for values)
+ TMimeDirMode aMimeMode, // MIME mode (older or newer vXXX format compatibility)
+ bool aParamValue, // set if parsing parameter value (different escaping rules)
+ bool aStructured // set if value consists of multiple values (has semicolon content escaping)
+ );
+ // - parse given property
+ bool parseProperty(
+ const char *&aText, // where to start interpreting property, will be updated past end of poperty
+ TMultiFieldItem &aItem, // item to store data into
+ const TPropertyDefinition *aPropP, // the property definition
+ sInt16 *aRepArray, // array[repeatID], holding current repetition COUNT for a certain nameExts entry
+ sInt16 aRepArraySize, // size of array (for security)
+ TMimeDirMode aMimeMode // MIME mode (older or newer vXXX format compatibility)
+ );
+ // parse MIME-DIR level from specified string into item
+ bool parseLevels(
+ const char *&aText,
+ TMultiFieldItem &aItem,
+ const TProfileDefinition *aProfileP,
+ bool aRootLevel
+ );
+}; // TMimeDirProfileHandler
+
+
+
+// Utility functions
+// -----------------
+
+
+/// @brief checks two timestamps if they represent an all-day event
+/// @param[in] aStart start time
+/// @param[in] aEnd end time
+/// @return 0 if not allday, x=1..n if allday (spanning x days) by one of the
+/// following criteria:
+/// - both start and end at midnight of the same day (= 1 day)
+/// - both start and end at midnight of different days (= 1..n days)
+/// - start at midnight and end between 23:59:00 and 23:59:59 of
+/// same or different days (= 1..n days)
+uInt16 AlldayCount(lineartime_t aStart, lineartime_t aEnd);
+
+/// @brief checks two timestamps if they represent an all-day event
+/// @param[in] aStartFldP start time field
+/// @param[in] aEndFldP end time field
+/// @param[in] aTimecontext context to use to check allday criteria for all non-floating timestamps
+/// or UTC timestamps only (if aContextForUTC is set).
+/// @param[in] aContextForUTC if set, context is only applied for UTC timestamps, other non-floatings are checked as-is
+/// @return 0 if not allday, x=1..n if allday (spanning x days)
+uInt16 AlldayCount(TItemField *aStartFldP, TItemField *aEndFldP, timecontext_t aTimecontext, bool aContextForUTC);
+
+
+/// @brief makes two timestamps represent an all-day event
+/// @param[in/out] aStart start time within the first day, will be set to midnight (00:00:00)
+/// @param[in/out] aEnd end time within the last day or at midnight of the next day,
+/// will be set to midnight of the next day
+/// @param[in] aDays if>0, this is used to calculate the aEnd timestamp (aEnd input is
+/// ignored then)
+void MakeAllday(lineartime_t &aStart, lineartime_t &aEnd, sInt16 aDays=0);
+
+/// @brief makes two timestamp fields represent an all-day event
+/// @param[in/out] aStartFldP start time within the first day, will be set to dateonly
+/// @param[in/out] aEndFldP end time within the last day or at midnight of the next day, will be set to dateonly of the next day
+/// @param[in] aTimecontext context to calculate day boundaries in
+/// @param[in] aDays if>0, this is used to calculate the aEnd timestamp (aEnd input is
+/// ignored then)
+/// @note fields will be made floating and dateonly
+void MakeAllday(TItemField *aStartFldP, TItemField *aEndFldP, timecontext_t aTimecontext, sInt16 aDays=0);
+
+
+
+} // namespace sysync
+
+#endif // MimeDirProfile_H
+
+// eof
+
diff --git a/src/sysync/multifielditem.cpp b/src/sysync/multifielditem.cpp
new file mode 100755
index 0000000..a15eb22
--- /dev/null
+++ b/src/sysync/multifielditem.cpp
@@ -0,0 +1,1645 @@
+/*
+ * File: MultiFieldItem.cpp
+ *
+ * Author: Lukas Zeller (luz@synthesis.ch)
+ *
+ * TMultiFieldItem
+ * Item consisting of multiple data fields (TItemField objects)
+ *
+ * Copyright (c) 2001-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ * 2001-08-08 : luz : created
+ *
+ */
+
+// includes
+#include "prefix_file.h"
+#include "sysync.h"
+#include "multifielditem.h"
+#include "multifielditemtype.h"
+
+
+using namespace sysync;
+
+namespace sysync {
+
+// Config
+// ======
+
+
+TFieldListConfig::TFieldListConfig(const char* aName, TConfigElement *aParentElement) :
+ TConfigElement(aName,aParentElement)
+{
+ clear();
+} // TFieldListConfig::TFieldListConfig
+
+
+TFieldListConfig::~TFieldListConfig()
+{
+ clear();
+} // TFieldListConfig::~TFieldListConfig
+
+
+// init defaults
+void TFieldListConfig::clear(void)
+{
+ // init defaults
+ fAgeSortable=false;
+ fFields.clear();
+ #ifdef HARDCODED_TYPE_SUPPORT
+ fFieldListTemplateP=NULL;
+ #endif
+ // clear inherited
+ inherited::clear();
+} // TFieldListConfig::clear
+
+
+#ifdef CONFIGURABLE_TYPE_SUPPORT
+
+// config element parsing
+bool TFieldListConfig::localStartElement(const char *aElementName, const char **aAttributes, sInt32 aLine)
+{
+ // - fieldlist entry
+ // <field name="REV" type="timestamp" compare="never" age="yes" merge="no"/>
+ if (strucmp(aElementName,"field")==0) {
+ // may not contain anything
+ expectEmpty();
+ // check attributes
+ const char *nam = getAttr(aAttributes,"name");
+ const char *type = getAttr(aAttributes,"type");
+ const char *rel = getAttr(aAttributes,"compare");
+ if (!(nam && *nam && type && rel))
+ return fail("'field' must have 'name', 'type' and 'compare' attributes");
+ // parse enums
+ sInt16 ty;
+ if (!StrToEnum(ItemFieldTypeNames,numFieldTypes,ty,type))
+ return fail("Unknown 'type' attribute: '%s'",type);
+ sInt16 eqrel;
+ if (!StrToEnum(compareRelevanceNames,numEQmodes,eqrel,rel))
+ return fail("Unknown 'compare' attribute: '%s'",rel);
+ // set defaults
+ bool agerelevant=false; // not age relevant by default
+ sInt16 mergemode=mem_none; // no merge by default
+ // get optional attributes
+ if (!getAttrBool(aAttributes,"age",agerelevant,true))
+ return fail("Bad boolean value");
+ #ifdef ARRAYFIELD_SUPPORT
+ bool array=false; // not an array
+ if (!getAttrBool(aAttributes,"array",array,true))
+ return fail("Bad boolean value");
+ #endif
+ const char *p = getAttr(aAttributes,"merge");
+ if (p) {
+ // sort out special cases
+ if (strucmp(p,"no")==0)
+ mergemode=mem_none;
+ else if (strucmp(p,"fillempty")==0)
+ mergemode=mem_fillempty;
+ else if (strucmp(p,"addunassigned")==0)
+ mergemode=mem_addunassigned;
+ else if (strucmp(p,"append")==0)
+ mergemode=mem_concat;
+ else if (strucmp(p,"lines")==0)
+ mergemode='\n';
+ else if (strlen(p)==1)
+ mergemode=*p; // single char is merge char
+ else
+ return fail("Invalid value '%s' for 'merge' attribute",p);
+ }
+ // now add new field specification
+ TFieldDefinition fielddef;
+ // prepare template element
+ fielddef.type=(TItemFieldTypes)ty;
+ #ifdef ARRAYFIELD_SUPPORT
+ fielddef.array = array;
+ #endif
+ TCFG_ASSIGN(fielddef.fieldname,nam);
+ fielddef.eqRelevant=(TEqualityMode)eqrel;
+ fielddef.ageRelevant=agerelevant;
+ fAgeSortable=fAgeSortable || fielddef.ageRelevant; // if at least one field is age-relevant, we can sort items
+ fielddef.mergeMode=mergemode;
+ // copy into array
+ fFields.push_back(fielddef);
+ }
+ // - none known here
+ else
+ return inherited::localStartElement(aElementName,aAttributes,aLine);
+ // ok
+ return true;
+} // TFieldListConfig::localStartElement
+
+
+// resolve
+void TFieldListConfig::localResolve(bool aLastPass)
+{
+ if (aLastPass) {
+ // check for required settings
+ if (fFields.size()==0)
+ SYSYNC_THROW(TSyncException("fieldlist must contain at least one field"));
+ }
+ // resolve inherited
+ inherited::localResolve(aLastPass);
+} // TFieldListConfig::localResolve
+
+#endif
+
+
+// get index of a field
+sInt16 TFieldListConfig::fieldIndex(const char *aName, size_t aLen)
+{
+ TFieldDefinitionList::iterator pos;
+ sInt16 n;
+ for (n=0,pos=fFields.begin(); pos!=fFields.end(); ++n,pos++) {
+ if (strucmp(aName,pos->TCFG_CSTR(fieldname),aLen)==0) {
+ return n; // return field ID
+ }
+ }
+ return VARIDX_UNDEFINED; // not found
+} // TFieldListConfig::fieldIndex
+
+
+// profile handler
+
+TProfileHandler::TProfileHandler(TProfileConfig *aProfileCfgP, TMultiFieldItemType *aItemTypeP)
+{
+ // save profile config pointer
+ fItemTypeP = aItemTypeP;
+ // no related datastore yet
+ fRelatedDatastoreP = NULL;
+} // TProfileHandler::TProfileHandler
+
+
+TProfileHandler::~TProfileHandler()
+{
+ // nop for now
+} // TProfileHandler::~TProfileHandler
+
+
+// - get session pointer
+TSyncSession *TProfileHandler::getSession(void)
+{
+ return fItemTypeP ? fItemTypeP->getSession() : NULL;
+} // TProfileHandler::getSession
+
+// - get session zones pointer
+GZones *TProfileHandler::getSessionZones(void)
+{
+ return fItemTypeP ? fItemTypeP->getSessionZones() : NULL;
+} // TProfileHandler::getSessionZones
+
+
+#ifdef SYDEBUG
+
+TDebugLogger *TProfileHandler::getDbgLogger(void)
+{
+ // commands log to session's logger
+ return fItemTypeP ? fItemTypeP->getDbgLogger() : NULL;
+} // TProfileHandler::getDbgLogger
+
+uInt32 TProfileHandler::getDbgMask(void)
+{
+ if (!fItemTypeP) return 0; // no item type, no debug
+ return fItemTypeP->getDbgMask();
+} // TProfileHandler::getDbgMask
+
+#endif
+
+
+// - check availability (depends on item "supported" flags only in SyncML datastore context)
+bool TProfileHandler::isFieldAvailable(TMultiFieldItem &aItem, sInt16 aFieldIndex)
+{
+ if (fRelatedDatastoreP) {
+ // in datastore/SyncML context, only fields supported on both sides are considered "available"
+ return aItem.isAvailable(aFieldIndex);
+ }
+ else {
+ // in non-datastore context, all fields are considered available, as long as
+ // the field index is in range
+ TMultiFieldItemType *mfitP = aItem.getItemType();
+ return mfitP && mfitP->isFieldIndexValid(aFieldIndex);
+ }
+} // TProfileHandler::isFieldAvailable
+
+
+
+
+
+
+// Profile config root element
+
+TProfileConfig::TProfileConfig(const char* aName, TConfigElement *aParentElement) :
+ TConfigElement(aName,aParentElement)
+{
+ clear();
+} // TProfileConfig::TProfileConfig
+
+
+TProfileConfig::~TProfileConfig()
+{
+ clear();
+} // TProfileConfig::~TProfileConfig
+
+
+// init defaults
+void TProfileConfig::clear(void)
+{
+ // init defaults
+ fFieldListP=NULL; // no field list linked
+ // clear inherited
+ inherited::clear();
+} // TProfileConfig::clear
+
+
+
+#ifdef HARDCODED_TYPE_SUPPORT
+
+// read hardcoded fieldlist config
+void TFieldListConfig::readFieldListTemplate(const TFieldDefinitionsTemplate *aTemplateP)
+{
+ if (!aTemplateP) return;
+ // save the link to the template as well (we'll need it for maxsize etc. later)
+ fFieldListTemplateP=aTemplateP;
+ fFields.clear();
+ // copy values
+ fAgeSortable=false;
+ TFieldDefinition fielddef;
+ for (sInt16 i=0; i<aTemplateP->numFields; i++) {
+ const TFieldDefinitionTemplate *afieldP= &(aTemplateP->fieldDefs[i]);
+ // prepare template element
+ fielddef.type=afieldP->type;
+ #ifdef ARRAYFIELD_SUPPORT
+ fielddef.array = afieldP->array;
+ #endif
+ TCFG_ASSIGN(fielddef.fieldname,afieldP->fieldname);
+ fielddef.eqRelevant=afieldP->eqRelevant;
+ fielddef.ageRelevant=afieldP->ageRelevant;
+ fAgeSortable=fAgeSortable || fielddef.ageRelevant; // if at least one field is age-relevant, we can sort
+ fielddef.mergeMode=afieldP->mergeMode;
+ // copy into array
+ fFields.push_back(fielddef);
+ }
+} // TFieldListConfig::readFieldListTemplate
+
+#endif
+
+
+
+TMultiFieldDatatypesConfig::TMultiFieldDatatypesConfig(TConfigElement *aParentElement) :
+ TDatatypesConfig("datatypes",aParentElement)
+{
+ clear();
+} // TMultiFieldDatatypesConfig::TMultiFieldDatatypesConfig
+
+
+TMultiFieldDatatypesConfig::~TMultiFieldDatatypesConfig()
+{
+ // make sure we don't re-build types (createHardcodedTypes() in clear())
+ // so we call internalClear() here!
+ internalClear();
+} // TMultiFieldDatatypesConfig::~TMultiFieldDatatypesConfig
+
+
+// init defaults
+void TMultiFieldDatatypesConfig::clear(void)
+{
+ // remove internals
+ internalClear();
+ // Now datatypes registry is really empty
+ #ifdef HARDCODED_TYPE_SUPPORT
+ // - add hard-coded default type information (if any)
+ static_cast<TRootConfig *>(getRootElement())->createHardcodedTypes(this);
+ #endif
+} // TMultiFieldDatatypesConfig::clear
+
+
+// init defaults
+void TMultiFieldDatatypesConfig::internalClear(void)
+{
+ // remove fieldlists
+ TFieldListsList::iterator pos1;
+ for(pos1=fFieldLists.begin();pos1!=fFieldLists.end();pos1++)
+ delete *pos1;
+ fFieldLists.clear();
+ // remove profiles
+ TProfilesList::iterator pos2;
+ for(pos2=fProfiles.begin();pos2!=fProfiles.end();pos2++)
+ delete *pos2;
+ fProfiles.clear();
+ // clear inherited
+ inherited::clear();
+} // TMultiFieldDatatypesConfig::internalClear
+
+
+// get a field list by name
+TFieldListConfig *TMultiFieldDatatypesConfig::getFieldList(const char *aName)
+{
+ TFieldListsList::iterator pos;
+ for(pos=fFieldLists.begin();pos!=fFieldLists.end();pos++) {
+ if (strucmp((*pos)->getName(),aName)==0) {
+ // found
+ return *pos;
+ }
+ }
+ return NULL; // not found
+} // TMultiFieldDatatypesConfig::getFieldList
+
+
+// get a profile by name
+TProfileConfig *TMultiFieldDatatypesConfig::getProfile(const char *aName)
+{
+ TProfilesList::iterator pos;
+ for(pos=fProfiles.begin();pos!=fProfiles.end();pos++) {
+ if (strucmp((*pos)->getName(),aName)==0) {
+ // found
+ return *pos;
+ }
+ }
+ return NULL; // not found
+} // TMultiFieldDatatypesConfig::getProfile
+
+
+
+#ifdef CONFIGURABLE_TYPE_SUPPORT
+
+// config element parsing
+bool TMultiFieldDatatypesConfig::localStartElement(const char *aElementName, const char **aAttributes, sInt32 aLine)
+{
+ // checking the elements
+ // - field lists or profiles can appear at this level
+ bool newFieldList=false;
+ TProfileConfig *newProfileP=NULL;
+ TFieldListConfig *flP = NULL;
+ // in case it is a field list or a profile - check for name
+ const char* nam = getAttr(aAttributes,"name");
+ // now check if fieldlist or profile
+ if (strucmp(aElementName,"fieldlist")==0) {
+ newFieldList=true;
+ }
+ // the xml tag itself is used as the profile's typename (historical reasons/compatibility with existing config)
+ else if ((newProfileP=getSyncAppBase()->getRootConfig()->newProfileConfig(nam,aElementName,this))!=NULL) {
+ const char* flnam = getAttr(aAttributes,"fieldlist");
+ if (!flnam)
+ return fail("%s is missing 'fieldlist' attribute",aElementName);
+ else {
+ flP = getFieldList(flnam);
+ if (!flP)
+ return fail("fieldlist '%s' unknown in %s",flnam,aElementName);
+ }
+ }
+ // - tag not known here
+ else
+ return TDatatypesConfig::localStartElement(aElementName,aAttributes,aLine);
+ // known tag, check if we need further processing
+ if (newFieldList || newProfileP) {
+ if (!nam)
+ return fail("%s is missing 'name' attribute",aElementName);
+ // create new named field list or use already created profile
+ if (newFieldList) {
+ // new field list
+ TFieldListConfig *fieldlistcfgP = new TFieldListConfig(nam,this);
+ fFieldLists.push_back(fieldlistcfgP); // save in list
+ expectChildParsing(*fieldlistcfgP); // let element handle parsing
+ }
+ else {
+ // new profile
+ newProfileP->fFieldListP=flP; // set field list for profile
+ fProfiles.push_back(newProfileP); // save in list
+ expectChildParsing(*newProfileP); // let element handle parsing
+ }
+ }
+ // ok
+ return true;
+} // TMultiFieldDatatypesConfig::localStartElement
+
+
+// resolve
+void TMultiFieldDatatypesConfig::localResolve(bool aLastPass)
+{
+ // resolve profiles
+ TProfilesList::iterator pos1;
+ for(pos1=fProfiles.begin();pos1!=fProfiles.end();pos1++) {
+ (*pos1)->localResolve(aLastPass);
+ }
+ // resolve field lists
+ TFieldListsList::iterator pos2;
+ for(pos2=fFieldLists.begin();pos2!=fFieldLists.end();pos2++) {
+ (*pos2)->localResolve(aLastPass);
+ }
+ // resolve inherited
+ inherited::localResolve(aLastPass);
+} // TMultiFieldDatatypesConfig::localResolve
+
+
+#endif
+
+
+/*
+ * Implementation of TMultiFieldItem
+ */
+
+/* public TMultiFieldItem members */
+
+
+TMultiFieldItem::TMultiFieldItem(
+ TMultiFieldItemType *aItemTypeP, // owner's (=source) type
+ TMultiFieldItemType *aTargetItemTypeP // target type (for optimization)
+) :
+ TSyncItem(aItemTypeP)
+{
+ // save types
+ fItemTypeP = aItemTypeP; // owner (source) type
+ fTargetItemTypeP = aTargetItemTypeP; // target (destination) type
+ // copy field definitions pointer for fast access
+ fFieldDefinitionsP = fItemTypeP->getFieldDefinitions();
+ if (!fFieldDefinitionsP)
+ SYSYNC_THROW(TSyncException(DEBUGTEXT("MultiFieldItem without FieldDefinitions","mfi3")));
+ // test if target has same field defs
+ if (fTargetItemTypeP->getFieldDefinitions()!=fFieldDefinitionsP)
+ SYSYNC_THROW(TSyncException(DEBUGTEXT("MultiFieldItem with non-matching target field definitions","mfi1")));
+ // create fields array
+ fFieldsP = new TItemFieldP[fFieldDefinitionsP->numFields()];
+ // - init it with null pointers
+ for (sInt16 i=0; i<fFieldDefinitionsP->numFields(); i++) fFieldsP[i]=NULL;
+} // TMultiFieldItem::TMultiFieldItem
+
+
+TMultiFieldItem::~TMultiFieldItem()
+{
+ // remove fields
+ cleardata();
+ // remove fields list
+ delete[] fFieldsP;
+} // TMultiFieldItem::~TMultiFieldItem
+
+
+
+// remove all data from item
+void TMultiFieldItem::cleardata(void)
+{
+ if (fFieldDefinitionsP) {
+ for (sInt16 i=0; i<fFieldDefinitionsP->numFields(); i++) {
+ if (fFieldsP[i]) {
+ delete fFieldsP[i]; // delete field object
+ fFieldsP[i]=NULL;
+ }
+ }
+ }
+} // TMultiFieldItem::cleardata
+
+
+#if defined(CHECKSUM_CHANGELOG) && !defined(RECORDHASH_FROM_DBAPI)
+
+// changelog support: calculate CRC over contents
+uInt16 TMultiFieldItem::getDataCRC(uInt16 crc, bool aEQRelevantOnly)
+{
+ // iterate over all fields
+ if (fFieldDefinitionsP) {
+ for (sInt16 i=0; i<fFieldDefinitionsP->numFields(); i++) {
+ if (!aEQRelevantOnly || fFieldDefinitionsP->fFields[i].eqRelevant!=eqm_none) {
+ if (fFieldsP[i]) {
+ crc=fFieldsP[i]->getDataCRC(crc);
+ }
+ }
+ }
+ }
+ return crc;
+} // TMultiFieldItem::getDataCRC
+
+#endif
+
+
+
+// adjust fid and repeat offset to access array element if
+// base fid is an array field or to offset fid accordingly
+// if based fid is NOT an array field
+// - returns adjusted aFid and aIndex ready to be used with getArrayField()
+// - returns true if aFid IS an array field
+bool TMultiFieldItem::adjustFidAndIndex(sInt16 &aFid, sInt16 &aIndex)
+{
+ bool arrfield;
+
+ #ifdef ARRAYFIELD_SUPPORT
+ // fid is offset repoffset only if not an array field
+ arrfield=true;
+ // check for array field first
+ TItemField *fldP = getField(aFid);
+ if (fldP) {
+ if (!(fldP->isArray())) {
+ // no array field
+ arrfield=false;
+ aFid += aIndex; // use array offset as additional field ID offset
+ aIndex=0; // no array index
+ }
+ }
+ else {
+ // Note: if field does not exist, do not apply offset, but don't report array field either!
+ arrfield = false;
+ }
+ #else
+ // without array support, fid is always offset by rep offset
+ aFid += aIndex;
+ aIndex=0; // no array index
+ arrfield=false;
+ #endif
+ // return true if this is really an array field
+ return arrfield;
+} // adjustFidAndIndex
+
+
+// return specified leaf field of array field
+TItemField *TMultiFieldItem::getArrayField(sInt16 aFid, sInt16 aIndex, bool aExistingOnly)
+{
+ #ifdef ARRAYFIELD_SUPPORT
+ TItemField *fiP = getField(aFid);
+ if (!fiP) return NULL;
+ return fiP->getArrayField(aIndex,aExistingOnly);
+ #else
+ // without array support, we can only access index==0
+ if (aIndex>0) return NULL; // other indices don't exist
+ return getField(aFid);
+ #endif
+} // TMultiFieldItem::getArrayField
+
+
+// get field by name (returns NULL if not known, creates if known but not existing yet)
+TItemField *TMultiFieldItem::getArrayField(const char *aFieldName, sInt16 aIndex, bool aExistingOnly)
+{
+ return getArrayField(fItemTypeP->getFieldIndex(aFieldName),aIndex,aExistingOnly);
+} // TMultiFieldItem::getArrayField
+
+
+// get field by index (returns NULL if not known, creates if known but not existing yet)
+TItemField *TMultiFieldItem::getField(sInt16 aFieldIndex)
+{
+ if (!fItemTypeP->isFieldIndexValid(aFieldIndex)) return NULL; // invalid index
+ TItemField *fiP = fFieldsP[aFieldIndex];
+ if (!fiP) {
+ // we must create the field first
+ fiP=newItemField(
+ fFieldDefinitionsP->fFields[aFieldIndex].type,
+ getSessionZones()
+ #ifdef ARRAYFIELD_SUPPORT
+ ,fFieldDefinitionsP->fFields[aFieldIndex].array
+ #endif
+ );
+ // save in array
+ fFieldsP[aFieldIndex] = fiP;
+ }
+ return fiP;
+} // TMultiFieldItem::getField
+
+
+// find index of field (returns FID_NOT_SUPPORTED if field is not a field of this item)
+sInt16 TMultiFieldItem::getIndexOfField(const TItemField *aFieldP)
+{
+ for (sInt16 i=0; i<fFieldDefinitionsP->numFields(); i++) {
+ if (fFieldsP[i]==aFieldP) {
+ // found field, return it's index
+ return i;
+ }
+ }
+ return FID_NOT_SUPPORTED; // not found
+} // TMultiFieldItem::getIndexOfField
+
+
+
+// get field by name (returns NULL if not known, creates if known but not existing yet)
+TItemField *TMultiFieldItem::getField(const char *aFieldName)
+{
+ return getField(fItemTypeP->getFieldIndex(aFieldName));
+} // TMultiFieldItem::getField
+
+
+// get field reference (create if not yet created)
+// throws if bad index
+TItemField &TMultiFieldItem::getFieldRef(sInt16 aFieldIndex)
+{
+ TItemField *fiP = getField(aFieldIndex);
+ if (!fiP)
+ SYSYNC_THROW(TSyncException(DEBUGTEXT("getFieldRef with bad index called","mfi2"))); // invalid index
+ return *fiP;
+} // TMultiFieldItem::getFieldRef
+
+
+// check if field is assigned (exists and has a value)
+bool const TMultiFieldItem::isAssigned(const char *aFieldName)
+{
+ return isAssigned(fItemTypeP->getFieldIndex(aFieldName));
+} // TMultiFieldItem::isAssigned
+
+
+// check if field is assigned (exists and has a value)
+bool const TMultiFieldItem::isAssigned(sInt16 aFieldIndex)
+{
+ // check if field object exists at all
+ if (!fItemTypeP->isFieldIndexValid(aFieldIndex)) return false; // invalid index
+ TItemField *fiP = fFieldsP[aFieldIndex];
+ if (!fiP) return false; // field object does not exist
+ // return if field object is assigned
+ return fiP->isAssigned();
+} // TMultiFieldItem::isAssigned
+
+
+// field availability (combined source & target)
+bool TMultiFieldItem::isAvailable(const char *aFieldName)
+{
+ return isAvailable(fItemTypeP->getFieldIndex(aFieldName));
+} // TMultiFieldItem::isAvailable
+
+
+// field availability (combined source & target)
+bool TMultiFieldItem::isAvailable(sInt16 aFieldIndex)
+{
+ if (fItemTypeP && fTargetItemTypeP) {
+ if (!fItemTypeP->isFieldIndexValid(aFieldIndex)) return false; // invalid index
+ return
+ fItemTypeP->getFieldOptions(aFieldIndex)->available &&
+ fTargetItemTypeP->getFieldOptions(aFieldIndex)->available;
+ }
+ else
+ return false; // source or target missing, not available
+} // TMultiFieldItem::isAvailable
+
+
+bool TMultiFieldItem::knowsRemoteFieldOptions(void)
+{
+ // knows them if either myself or the other side has received devInf
+ // (depends: received item has it in its own type, to be sent one in the target type)
+ return
+ (fItemTypeP && fItemTypeP->hasReceivedFieldOptions()) ||
+ (fTargetItemTypeP && fTargetItemTypeP->hasReceivedFieldOptions());
+} // TMultiFieldItem::knowsRemoteFieldOptions
+
+
+
+// make sure that all fields that are available in source and target are
+// assigned at least an empty value
+void TMultiFieldItem::assignAvailables(void)
+{
+ if (fFieldDefinitionsP) {
+ for (sInt16 k=0; k<fFieldDefinitionsP->numFields(); k++) {
+ if (isAvailable(k)) {
+ TItemField *fldP=getField(k); // force creation
+ if (fldP) {
+ // make sure it is assigned a "empty" value
+ if (fldP->isUnassigned())
+ fldP->assignEmpty();
+ }
+ }
+ }
+ }
+} // TMultiFieldItem::assignAvailables
+
+
+
+
+// cast pointer to same type, returns NULL if incompatible
+TMultiFieldItem *TMultiFieldItem::castToSameTypeP(TSyncItem *aItemP)
+{
+ if (aItemP->isBasedOn(ity_multifield)) {
+ TMultiFieldItem *multifielditemP=static_cast<TMultiFieldItem *> (aItemP);
+ // class compatible, now test type compatibility
+ // - field definition list must be the same instance(!) in both items
+ if (fFieldDefinitionsP==multifielditemP->fFieldDefinitionsP)
+ return multifielditemP;
+ else
+ return NULL;
+ }
+ // not even class compatible
+ return NULL;
+} // TMultiFieldItem::castToSameTypeP
+
+
+// test if comparable (at least for equality)
+bool TMultiFieldItem::comparable(TSyncItem &aItem)
+{
+ // test if comparable: other type must be same type of multifield
+ return castToSameTypeP(&aItem)!=NULL;
+} // TMultiFieldItem::comparable
+
+
+// test if sortable (by age, newer are > than older)
+bool TMultiFieldItem::sortable(TSyncItem &aItem)
+{
+ if (!fFieldDefinitionsP->fAgeSortable) return false; // not sortable at all
+ if (comparable(aItem)) {
+ // item is comparable (has same FieldDefinitions)
+ // Now check if all ageRelevant fields are assigned on both sides
+ // Note: we can static-cast here because comparable() has verified aItem's type
+ TMultiFieldItem *multifielditemP=static_cast<TMultiFieldItem *> (&aItem);
+ // search for ageRelevant fields
+ for (sInt16 i=0; i<fFieldDefinitionsP->numFields(); i++) {
+ if (fFieldDefinitionsP->fFields[i].ageRelevant) {
+ // check if available for both types
+ if (!(
+ isAssigned(i) && // my own
+ multifielditemP->isAssigned(i) // aItem's
+ ))
+ return false; // missing needed field on one side
+ }
+ }
+ return true; // all ageRelevant fields are assigned in both sides
+ }
+ else return false; // not comparable is not sortable either
+} // TMultiFieldItem::sortable
+
+
+#ifdef OBJECT_FILTERING
+
+
+// check post-fetch filter
+bool TMultiFieldItem::postFetchFiltering(TLocalEngineDS *aDatastoreP)
+{
+ return fItemTypeP->postFetchFiltering(this,aDatastoreP);
+} // TMultiFieldItem::postFetchFiltering
+
+
+// test if item passes filter
+bool TMultiFieldItem::testFilter(const char *aFilterString)
+{
+ // process filter without modifying
+ #ifdef SYDEBUG
+ PDEBUGPRINTFX(DBG_DATA+DBG_FILTER+DBG_HOT,(
+ "Testing filter '%s' against item:",
+ aFilterString
+ ));
+ if (*aFilterString && PDEBUGTEST(DBG_DATA+DBG_FILTER+DBG_USERDATA)) {
+ debugShowItem(DBG_DATA+DBG_FILTER);
+ }
+ #endif
+ bool result=processFilter(false,aFilterString);
+ PDEBUGPRINTFX(DBG_DATA+DBG_FILTER+DBG_HOT,(
+ "Filter test result is %s",
+ result ? "TRUE" : "FALSE"
+ ));
+ // false on syntax error
+ if (*aFilterString) {
+ PDEBUGPRINTFX(DBG_ERROR,("unexpected chars in filter expression: %s",aFilterString));
+ return false;
+ }
+ return result;
+} // TMultiFieldItem::testFilter
+
+
+// make item pass filter
+bool TMultiFieldItem::makePassFilter(const char *aFilterString)
+{
+ // process filter with making modifications such that item passes filter condition
+ bool result = processFilter(true,aFilterString);
+ // false on syntax error
+ if (*aFilterString) {
+ PDEBUGPRINTFX(DBG_ERROR+DBG_FILTER,("unexpected chars in filter expression: %s",aFilterString));
+ return false;
+ }
+ return result;
+} // TMultiFieldItem::makePassFilter
+
+
+// process filter expression
+bool TMultiFieldItem::processFilter(bool aMakePass, const char *&aPos, const char *aStop, sInt16 aLastOpPrec)
+{
+ char c=0;
+ const char *st;
+ string str;
+ bool result;
+ sInt16 fid;
+ sInt16 cmpres=0;
+ bool neg;
+ bool assignToMakeTrue;
+ bool specialValue;
+ bool caseinsensitive;
+ TStringField idfield;
+ TItemField *fldP;
+
+ // determine max length
+ if (aStop==NULL) aStop=aPos+strlen(aPos);
+ // empty expression is true
+ result=true;
+ // process simple term (<ident><op><value>)
+ // Note: do not allow negation to make sure
+ // that TRUE of a comparison always
+ // adds to TRUE of the entire expression
+ neg=false; // not negated
+ // - get first non-space
+ while (aPos<=aStop) {
+ c=*aPos;
+ if (c!=' ') break;
+ aPos++;
+ }
+ // Term starts here, first char is c, aPos points to it
+ // - check subexpression paranthesis
+ if (c=='(') {
+ // boolean term is grouped subexpression, don't stop at logical operation
+ aPos++;
+ result=processFilter(aMakePass,aPos,aStop,0); // dont stop at any logical operator
+ // check if matching paranthesis
+ if (*(aPos++)!=')') {
+ PDEBUGPRINTFX(DBG_ERROR+DBG_FILTER,("Filter expression error (missing \")\") at: %s",--aPos));
+ //%%% no, don't skip rest, as otherwise caller will not know that filter processing failed!%%% aPos=aStop; // skip rest
+ return false; // always fail
+ }
+ if (neg) result=!result;
+ }
+ else if (c==0) {
+ // empty term, counts as true
+ return result;
+ }
+ else {
+ // must be simple boolean term
+ // - remember start of ident
+ st=aPos;
+ // - search end of ident
+ while (isFilterIdent(c)) c=*(++aPos);
+ // - c/aPos=char after ident, get ident
+ str.assign(st,aPos-st);
+ // - check for subscript index
+ uInt16 subsIndex=0; // no index (index is 1-based in DS 1.2 filter specs)
+ if (c=='[') {
+ // expect numeric index
+ aPos++; // next
+ aPos+=StrToUShort(aPos,subsIndex);
+ if (*aPos!=']') {
+ PDEBUGPRINTFX(DBG_ERROR+DBG_FILTER,("Filter expression error (missing \"]\") at: %s",--aPos));
+ return false; // syntax error, does not pass
+ }
+ c=*(++aPos); // process next after subscript
+ }
+ // - get field ID for that ident (can be -1 if none found)
+ // - check special idents first
+ if (str=="LUID") {
+ // this is SyncML-TAF Standard
+ // it is also produced by DS 1.2 &LUID; pseudo-identifier
+ #ifdef SYSYNC_CLIENT
+ idfield.setAsString(getLocalID());
+ #else
+ idfield.setAsString(getRemoteID());
+ #endif
+ fldP=&idfield;
+ }
+ else if (str=="LOCALID") {
+ // this is a Synthesis extension
+ idfield.setAsString(getLocalID());
+ fldP=&idfield;
+ }
+ #ifndef SYSYNC_CLIENT
+ else if (str=="GUID") {
+ // this is a Synthesis extension, added for symmetry to LUID
+ idfield.setAsString(getLocalID());
+ fldP=&idfield;
+ }
+ else if (str=="REMOTEID") {
+ // this is a Synthesis extension
+ idfield.setAsString(getRemoteID());
+ fldP=&idfield;
+ }
+ #endif
+ else {
+ // must be a field
+ if (fItemTypeP)
+ fid=fItemTypeP->getFilterIdentifierFieldIndex(str.c_str(),subsIndex);
+ else
+ fid=VARIDX_UNDEFINED; // none
+ // now get field pointer (or NULL if field not found)
+ fldP = getField(fid);
+ }
+ // - skip spaces
+ while (isspace(c)) c=*(++aPos);
+ // - check for makepass-assignment modifier ":"
+ assignToMakeTrue=c==':';
+ if (assignToMakeTrue) c=*(++aPos);
+ // - check for special-value modifier "*"
+ specialValue=c=='*';
+ if (specialValue) c=*(++aPos);
+ // - check for case-insensitive comparison mode
+ caseinsensitive=c=='^';
+ if (caseinsensitive) c=*(++aPos);
+ // - now find comparison mode
+ // cmpres = expected strcmp-style result:
+ // 0 if equal, 1 if ident > value, -1 if ident < value,
+ aPos++; // consume first char of comparison anyway
+ if (c=='%') { cmpres=2; } // special flag for CONTAINS
+ else if (c=='$') { cmpres=2; neg=!neg; }
+ else if (c=='=') { cmpres=0; } // equal
+ else if (c=='>') {
+ if (*aPos=='=') { aPos++; neg=!neg; cmpres=-1; } // >= is not <
+ else { cmpres=1; } // >
+ }
+ else if (c=='<') {
+ if (*aPos=='>') { aPos++; neg=!neg; cmpres=0; } // <> is not =
+ else if (*aPos=='=') { aPos++; neg=!neg; cmpres=1; } // <= is not >
+ else { cmpres=-1; } // <
+ }
+ // - now read value
+ st=aPos; // should start here
+ // - find end (end of string, closing paranthesis or logical op)
+ while (aPos<aStop && *aPos!='&' && *aPos!='|' && *aPos!=')') aPos++;
+ // - assign st string
+ str.assign(st,aPos-st);
+ // - check field
+ if (!fldP) {
+ // field does not exist -> result of term, negated or not, is always FALSE
+ result=false;
+ }
+ else {
+ if (cmpres==2) {
+ // "contains"
+ // - create a reference field
+ TItemField *valfldP = newItemField(fldP->getElementType(),getSessionZones());
+ // - assign value as string
+ valfldP->setAsString(str.c_str());
+ result = fldP->contains(*valfldP,caseinsensitive);
+ // assign to make pass if enabled
+ if (!result && aMakePass && assignToMakeTrue) {
+ if (fldP->isArray())
+ fldP->append(*valfldP); // just append another element to make it contained
+ else
+ *fldP = *valfldP; // just overwrite value with to-be-contained value
+ result=true; // now passes
+ }
+ delete valfldP; // no longer needed
+ }
+ else {
+ if (specialValue) {
+ if (cmpres!=0)
+ result=false; // can only compare for equal
+ else {
+ if (str=="E") {
+ // empty
+ result = fldP->isEmpty();
+ }
+ else if (str=="N") {
+ // NULL, unassigned
+ result = fldP->isAssigned();
+ }
+ if (neg) result=!result;
+ // make empty or unassigned to pass filter (make non-empty is not possible)
+ if (!result && aMakePass && assignToMakeTrue && !neg) {
+ if (str=="E") {
+ fldP->assignEmpty();
+ }
+ else if (str=="N") {
+ fldP->unAssign();
+ }
+ result=true; // now passes
+ }
+ }
+ }
+ else {
+ // create a reference field
+ TItemField *valfldP = newItemField(fldP->getElementType(),getSessionZones());
+ // assign value as string
+ valfldP->setAsString(str.c_str());
+ // compare fields, then compare result with what was expected
+ result = (fldP->compareWith(*valfldP,caseinsensitive) == cmpres);
+ // negate result if needed
+ if (neg) result=!result;
+ // if field not assigned, comparison is always false
+ if (fldP->isUnassigned()) result=false;
+ // assign to make pass if enabled
+ if (!result && aMakePass && assignToMakeTrue) {
+ (*fldP) = (*valfldP);
+ result=true; // now passes
+ }
+ // now clear again
+ delete valfldP;
+ }
+ }
+ }
+ }
+ // term is now evaluated, show what follows
+ // - check for boolean op chain, aPos points now to eventual logical operator
+ do {
+ // - skip spaces
+ c=*aPos;
+ while (c==' ') c=*(++aPos);
+ // - check char at aPos
+ if (c=='&') {
+ // AND
+ if (2<=aLastOpPrec) return result; // evaluation continues in caller (always as long as we don't have higher prec than AND)
+ // skip op
+ aPos++;
+ // next term must be true as well
+ // - return when encountering AND, OR and end of expression
+ // - next term must also be modified to make pass
+ bool termres = processFilter(aMakePass, aPos, aStop, 2);
+ result = result && termres;
+ }
+ else if (c=='|') {
+ // OR
+ if (1<=aLastOpPrec) return result; // evaluation continues in caller
+ // skip op
+ aPos++;
+ // next term must be true only if this one is not true
+ // - return only when encountering AND or
+ // - if first term is already true, next must never be modifed to pass
+ bool termres = processFilter(result ? false : aMakePass, aPos, aStop, 1);
+ result = result || termres;
+ }
+ else {
+ // End of Expression
+ // would be: if (0<=aLastOpPrec)
+ return result;
+ }
+ } while(true);
+}
+
+#endif
+
+#ifndef SYSYNC_CLIENT
+
+// compare function, returns 0 if equal, 1 if this > aItem, -1 if this < aItem
+sInt16 TMultiFieldItem::compareWith(
+ TSyncItem &aItem,
+ TEqualityMode aEqMode,
+ TLocalEngineDS *aDatastoreP
+ #ifdef SYDEBUG
+ ,bool aDebugShow
+ #endif
+)
+{
+ #ifndef SYDEBUG
+ const aDebugShow = false;
+ #endif
+ sInt16 cmpres;
+ TMultiFieldItem *multifielditemP = castToSameTypeP(&aItem);
+ if (!multifielditemP) {
+ cmpres = SYSYNC_NOT_COMPARABLE;
+ goto exit;
+ }
+ // do the compare
+ if (fItemTypeP)
+ cmpres=fItemTypeP->compareItems(*this,*multifielditemP,aEqMode,aDebugShow,aDatastoreP);
+ else
+ cmpres=standardCompareWith(*multifielditemP,aEqMode,aDebugShow);
+exit:
+ #ifdef SYDEBUG
+ if (aDebugShow) {
+ OBJDEBUGPRINTFX(getItemType()->getSession(),DBG_DATA,(
+ "Compared [LOC=%s,REM=%s] with [LOC=%s,REM=%s] (eqMode=%hd), cmpres=%hd",
+ getLocalID(),
+ getRemoteID(),
+ aItem.getLocalID(),
+ aItem.getRemoteID(),
+ (sInt16) aEqMode,
+ cmpres
+ ));
+ }
+ #endif
+ return cmpres;
+} // TMultiFieldItem::compareWith
+
+
+// compare function, returns 0 if equal, 1 if this > aItem, -1 if this < aItem
+sInt16 TMultiFieldItem::standardCompareWith(
+ TMultiFieldItem &aItem,
+ TEqualityMode aEqMode,
+ bool aDebugShow
+)
+{
+ sInt16 commonfound=0;
+ sInt16 result=0; // default to equal
+ // we should test for comparable() before!
+ if (!comparable(aItem)) {
+ result=SYSYNC_NOT_COMPARABLE;
+ goto exit;
+ }
+ // now compare field-by-field
+ // - equal means equality of all eqRelevant fields (both non-existing is
+ // equality, too)
+ // (but possibly differences in ageRelevant fields)
+ // - larger/smaller means not equal in eqRelevant fields but
+ // older/newer by ageRelevant fields
+ // - SYSYNC_NOT_COMPARABLE means not equal and not ageSortable either
+ if (aEqMode!=eqm_nocompare) {
+ for (sInt16 i=0; i<fFieldDefinitionsP->numFields(); i++) {
+ // both fields must be available in their respective ItemType
+ if (!getItemType()->getFieldOptions(i)->available ||
+ !aItem.getItemType()->getFieldOptions(i)->available)
+ continue; // not available in both items, do not compare
+ // then test for equality (if relevant in given context)
+ if (fFieldDefinitionsP->fFields[i].eqRelevant>=aEqMode) {
+ // at least one is available and relevant in both items
+ commonfound++;
+ // this is an EQ-relevant field
+ // - get fields
+ TItemField &f1=getFieldRef(i);
+ TItemField &f2=aItem.getFieldRef(i);
+ // - For slowsync and firstsync matching, non-ASSIGNED fields will not
+ // be compared (to allow matching a less-equipped clinet record with its
+ // better equipped server record and vice versa. Example is the S55
+ // which discards private addresses, old method rendered lots of
+ // duplicates on slow sync
+ if (aEqMode>=eqm_slowsync) {
+ if (f1.isUnassigned() || f2.isUnassigned())
+ continue; // omit comparing fields where one side is unassigned
+ }
+ // - get assigned status of both fields
+ // BCPPB revealed bad error: isAssigned was not called (forgot ())!!!
+ // %%% Note: I think that isAssigned() is the wrong function here, we will use
+ // !isEmpty(), which returns true for unassigned fields as well as for empty ones
+ //bool a1 = f1.isAssigned(); // assignment status of field in this item
+ //bool a2 = f2.isAssigned(); // assignment status of same field in other item
+ bool a1 = !f1.isEmpty(); // non-empty status of field in this item
+ bool a2 = !f2.isEmpty(); // non-empty status of same field in other item
+ // - if both are unassigned -> equal
+ if (!a1 && !a2) continue; // we are staying equal, test next field
+ // - if one of them is unassigned -> not equal
+ // - if both are assigned, fields must be equal
+ if (!a1 || !a2) {
+ // one not assigned
+ result=SYSYNC_NOT_COMPARABLE;
+ #ifdef SYDEBUG
+ if (aDebugShow) {
+ // not assigned
+ if (!a1) {
+ PDEBUGPRINTFX(DBG_DATA+DBG_MATCH,("- not equal because fid=%hd not assigned/empty in this item",i));
+ } else if (!a2) {
+ PDEBUGPRINTFX(DBG_DATA+DBG_MATCH,("- not equal because fid=%hd not assigned/empty in other item",i));
+ }
+ }
+ #endif
+ break;
+ }
+ else if (f1 != f2) {
+ // content not equal
+ #ifdef SYDEBUG
+ string ds;
+ if (aDebugShow) {
+ // assigned but not equal
+ PDEBUGPRINTFX(DBG_DATA+DBG_MATCH,("- not equal because fid=%hd not same in both items:",i));
+ getField(i)->getAsString(ds);
+ PDEBUGPRINTFX(DBG_DATA+DBG_MATCH+DBG_USERDATA,(
+ "- this item : '%-0.1000s'",ds.c_str()
+ ));
+ aItem.getField(i)->getAsString(ds);
+ PDEBUGPRINTFX(DBG_DATA+DBG_MATCH+DBG_USERDATA,(
+ "- other item : '%-0.1000s'",ds.c_str()
+ ));
+ PDEBUGPRINTFX(DBG_DATA+DBG_MATCH,(
+ "- thisItem.CompareWith(otherItem) = %hd",
+ getFieldRef(i).compareWith(aItem.getFieldRef(i))
+ ));
+ }
+ #endif
+ // now check for cut-off situation
+ sInt32 s1=getItemType()->getFieldOptions(i)->maxsize;
+ sInt32 s2=aItem.getItemType()->getFieldOptions(i)->maxsize;
+ // Note: (2002-12-01) do not actually use size, as it will probably not be accurate enough,
+ // but always pass FIELD_OPT_MAXSIZE_UNKNOWN.
+ if (s1!=FIELD_OPT_MAXSIZE_NONE) s1=FIELD_OPT_MAXSIZE_UNKNOWN;
+ if (s2!=FIELD_OPT_MAXSIZE_NONE) s2=FIELD_OPT_MAXSIZE_UNKNOWN;
+ // Now check short versions
+ if (f1.isShortVers(f2,s2) || f2.isShortVers(f1,s1)) {
+ // cutoff detected, counts as equal
+ #ifdef SYDEBUG
+ if (aDebugShow) {
+ PDEBUGPRINTFX(DBG_DATA+DBG_MATCH,(
+ "- Cutoff detected, field considered equal, maxsize(thisitem)=%ld, maxsize(otheritem)=%ld",
+ s1,s2
+ ));
+ }
+ #endif
+ }
+ else {
+ // no cutoff, not equal
+ result=SYSYNC_NOT_COMPARABLE;
+ break;
+ }
+ } // else if not equal
+ } // if eq-relevant
+ } // for all fields
+ } // if EQ-compare at all
+ if (!commonfound) result=SYSYNC_NOT_COMPARABLE;
+ // if not equal, try to compare age (if age-sortable item at all)
+ if (result!=0 && fFieldDefinitionsP->fAgeSortable) {
+ for (sInt16 i=0; i<fFieldDefinitionsP->numFields(); i++) {
+ // then test for age (if relevant)
+ if (fFieldDefinitionsP->fFields[i].ageRelevant) {
+ // this is an age relevant field
+ // - get assigned status of both fields
+ bool a1 = isAssigned(i); // assignment status of field in this item
+ bool a2 = aItem.isAssigned(i); // assignment status of same field in other item
+ // - if both are unassigned -> cannot decide, continue
+ if (!a1 && !a2) continue; // test next field
+ // - if one of them is unassigned -> not age-comparable
+ if (!a1 || !a2) {
+ result=SYSYNC_NOT_COMPARABLE;
+ goto exit;
+ }
+ // - if both are assigned, return field comparison value
+ result=getFieldRef(i).compareWith(aItem.getFieldRef(i));
+ if (result!=0) {
+ // not equal, newer item determined
+ goto exit;
+ }
+ // continue to resolve age with next fields
+ }
+ }
+ // no age relevant fields or all age relevant fields equal (possibly all unassigned)
+ result=SYSYNC_NOT_COMPARABLE;
+ }
+ // done
+exit:
+ return result;
+} // TMultiFieldItem::standardCompareWith
+
+#endif // server only
+
+
+
+// update dependencies of fields (such as BLOB proxies) on localID
+void TMultiFieldItem::updateLocalIDDependencies(void)
+{
+ const char *localid = getLocalID();
+ // go through all fields
+ for (sInt16 k=0; k<fFieldDefinitionsP->numFields(); k++) {
+ TItemField *fldP = getField(k);
+ for (sInt16 i=0; i<fldP->arraySize(); i++) {
+ TItemField *leaffldP = fldP->getArrayField(i);
+ if (leaffldP) leaffldP->setParentLocalID(localid);
+ }
+ }
+} // TMultiFieldItem::updateLocalIDDependencies
+
+
+
+
+/// @brief replace data contents from specified item
+/// @param aAvailableOnly: only replace contents actually available in aItem, leave rest untouched
+/// NOTE: this was changed slightly between 1.x.8.5 and 1.x.8.6:
+/// If the source type has not received devinf saying which fields are available,
+/// only fields that are actually ASSIGNED are written. If aAssignedOnly is
+/// additionally set, only assigned fields will be written anyway.
+/// @param aDetectCutOffs: use field's maxsize specs to detect contents cut off by limited field
+/// lengths and do not replace data if target is equal with source up to field length
+/// @param aAssignedOnly: just copy assigned fields (no check for availability)
+/// @param aTransferUnassigned: transfer unassigned status from source item (i.e. unassign those
+/// in target that are unassigned in source, no check for availability)
+bool TMultiFieldItem::replaceDataFrom(TSyncItem &aItem, bool aAvailableOnly, bool aDetectCutoffs, bool aAssignedOnly, bool aTransferUnassigned)
+{
+ TMultiFieldItem *multifielditemP = castToSameTypeP(&aItem);
+ if (!multifielditemP) return false;
+ // ok, same type, copy data
+ for (sInt16 i=0; i<fFieldDefinitionsP->numFields(); i++) {
+ if (
+ !aAvailableOnly || (
+ !aAssignedOnly && // availability is relevant only if not aAssignedOnly
+ multifielditemP->fItemTypeP->hasReceivedFieldOptions() &&
+ multifielditemP->fItemTypeP->getFieldOptions(i)->available
+ )
+ || multifielditemP->isAssigned(i)
+ ) {
+ // copy field
+ sInt32 siz=multifielditemP->fItemTypeP->getFieldOptions(i)->maxsize;
+ if (aDetectCutoffs && siz!=FIELD_OPT_MAXSIZE_NONE) {
+ // check if source fields's content is fully contained at beginning of
+ // target string, if yes, don't do anything
+ // Note: (2002-12-01) do not actually use size, as it will probably not be accurate enough,
+ // but always pass FIELD_OPT_MAXSIZE_UNKNOWN.
+ if (getFieldRef(i).isShortVers(multifielditemP->getFieldRef(i),FIELD_OPT_MAXSIZE_UNKNOWN)) {
+ // yes, we think that this is a cut-off,
+ // so leave target untouched as it has more complete version of this field
+ continue;
+ }
+ }
+ // copy source field into this target field
+ getFieldRef(i)=multifielditemP->getFieldRef(i);
+ }
+ else if (aTransferUnassigned && !multifielditemP->isAssigned(i)) {
+ // explicitly transfer unassigned status
+ // Note: this is useful in read-modify-write done exclusively for cutoff prevention,
+ // as it prevents re-writing fields that were not actually transmitted from the remote
+ // (i.e. no get-from-DB-and-write-same-value-back). Might be essential in case of special
+ // fields where the datastore MUST know if these were sent with the data, like FN in pocketpc)
+ getFieldRef(i).unAssign();
+ }
+ }
+ return true;
+} // TMultiFieldItem::replaceDataFrom
+
+
+// check item before processing it
+bool TMultiFieldItem::checkItem(TLocalEngineDS *aDatastoreP)
+{
+ return fItemTypeP->checkItem(*this,aDatastoreP);
+} // TMultiFieldItem::checkItem
+
+
+#ifndef SYSYNC_CLIENT
+
+// merge this item with specified item.
+// Notes:
+// - specified item is treated as loosing item, this item is winning item
+// - also updates other item to make sure it is equal to the winning after the merge
+// sets (but does not reset) change status of this and other item.
+// Note that changes of non-relevant fields are not reported here.
+void TMultiFieldItem::mergeWith(TSyncItem &aItem, bool &aChangedThis, bool &aChangedOther, TLocalEngineDS *aDatastoreP)
+{
+ TMultiFieldItem *multifielditemP = castToSameTypeP(&aItem);
+ if (!multifielditemP) return;
+ // do the merge
+ if (fItemTypeP)
+ fItemTypeP->mergeItems(*this,*multifielditemP,aChangedThis,aChangedOther,aDatastoreP);
+ else
+ standardMergeWith(*multifielditemP,aChangedThis,aChangedOther);
+ // show result
+ OBJDEBUGPRINTFX(getItemType()->getSession(),DBG_DATA+DBG_CONFLICT,(
+ "mergeWith() final status: thisitem: %schanged, otheritem: %schanged (relevant; eqm_none field changes are not indicated)",
+ aChangedThis ? "" : "not ",
+ aChangedOther ? "" : "not "
+ ));
+} // TMultiFieldItem::mergeWith
+
+
+// merge this item with specified item.
+// Notes:
+// - specified item is treated as loosing item, this item is winning item
+// - also updates other item to make sure it is equal to the winning after the merge
+// returns update status of this and other item. Note that changes of non-relevant fields are
+// not reported here.
+void TMultiFieldItem::standardMergeWith(TMultiFieldItem &aItem, bool &aChangedThis, bool &aChangedOther)
+{
+ // same type of multifield, try to merge
+ for (sInt16 i=0; i<fFieldDefinitionsP->numFields(); i++) {
+ // get merge mode
+ char sep=fFieldDefinitionsP->fFields[i].mergeMode;
+ // eventual merging is only relevant (=to be reported) for fields that are not eqm_none
+ bool mergerelevant = fFieldDefinitionsP->fFields[i].eqRelevant!=eqm_none;
+ // check if available in both items at all
+ if (
+ fItemTypeP->getFieldOptions(i)->available && // winning
+ aItem.fItemTypeP->getFieldOptions(i)->available // loosing
+ ) {
+ // fields available in both items
+ // - get both fields
+ TItemField &winningField = getFieldRef(i);
+ TItemField &loosingField = aItem.getFieldRef(i);
+ // - get assigned status of both fields
+ bool winning = winningField.isAssigned();
+ bool loosing = loosingField.isAssigned();
+ // - now decide what to do
+ if (sep!=mem_none) {
+ // merge enabled
+ PDEBUGPRINTFX(DBG_DATA+DBG_CONFLICT,(
+ "Field '%s' available and enabled for merging, mode/sep=0x%04hX, %srelevant",
+ fFieldDefinitionsP->fFields[i].TCFG_CSTR(fieldname),
+ sep,
+ mergerelevant ? "" : "NOT "
+ ));
+ PDEBUGPRINTFX(DBG_DATA+DBG_CONFLICT,(
+ "- %sassigned in winning / %sassigned in loosing",
+ winning ? "" : "not ",
+ loosing ? "" : "not "
+ ));
+ // - if both are unassigned -> nop
+ if (!winning && !loosing) continue; // test next field
+ // - if this item has field unassigned and other has it assigned: copy contents
+ else if (!winning && loosing && (sep==mem_fillempty || sep==mem_addunassigned)) {
+ // assign loosing item's content to non-assigned winning item
+ getFieldRef(i)=aItem.getFieldRef(i);
+ #ifdef SYDEBUG
+ string ds;
+ getFieldRef(i).getAsString(ds);
+ PDEBUGPRINTFX(DBG_DATA+DBG_CONFLICT+DBG_USERDATA,(
+ "- assigned value '%" FMT_LENGTH("0.40") "s' to winning (which had nothing assigned here)",
+ FMT_LENGTH_LIMITED(40,ds.c_str())
+ ));
+ #endif
+ // count only if relevant and not assigned empty value
+ // (so empty and unassigned are treated equally)
+ if (mergerelevant && !aItem.getFieldRef(i).isEmpty())
+ aChangedThis=true; // merged something into this item
+ }
+ else if (winning && loosing) {
+ // merge loosing field into winning field
+ if (sep==mem_fillempty) {
+ // only fill up empty winning fields
+ if (winningField.isEmpty() && !loosingField.isEmpty()) {
+ // only copy value from loosing if winning is empty
+ winningField=loosingField;
+ #ifdef SYDEBUG
+ string ds;
+ winningField.getAsString(ds);
+ PDEBUGPRINTFX(DBG_DATA+DBG_CONFLICT+DBG_USERDATA,(
+ "- copied value '%" FMT_LENGTH("0.40") "s' from loosing to empty winning",
+ FMT_LENGTH_LIMITED(40,ds.c_str())
+ ));
+ #endif
+ if (mergerelevant) aChangedThis=true;
+ }
+ }
+ else {
+ // try real merge (sep might be 0 (mem_concat) or a separator char)
+ #ifdef SYDEBUG
+ string ds1,ds2;
+ winningField.getAsString(ds1);
+ loosingField.getAsString(ds2);
+ PDEBUGPRINTFX(DBG_DATA+DBG_CONFLICT+DBG_USERDATA,(
+ "- try merging winning value '%" FMT_LENGTH("0.40") "s' with loosing value '%" FMT_LENGTH("0.40") "s'",
+ FMT_LENGTH_LIMITED(40,ds1.c_str()),
+ FMT_LENGTH_LIMITED(40,ds2.c_str())
+ ));
+ #endif
+ if (winningField.merge(loosingField,sep))
+ aChangedThis=true;
+ #ifdef SYDEBUG
+ winningField.getAsString(ds1);
+ PDEBUGPRINTFX(DBG_DATA+DBG_CONFLICT+DBG_USERDATA,(
+ " merged %sthing, winning value is '%" FMT_LENGTH("0.40") "s'",
+ aChangedThis ? "some" : "no",
+ FMT_LENGTH_LIMITED(40,ds1.c_str())
+ ));
+ #endif
+ }
+ }
+ } // merge enabled
+ // with or without merge, loosing fields must be equal to winning ones
+ // - for non merge-relevant (that is, never-compared) fields,
+ // just assign winning value to loosing and do not compare (this
+ // is important to avoid pulling large blobs and strings here -
+ // assignment just passes the proxy)
+ if (!mergerelevant) {
+ // everything is handled by the field assignment mechanisms
+ loosingField = winningField;
+ }
+ else if (winningField!=loosingField) {
+ // merge relevant fields will get more sophisticated treatment, such
+ // as checking if a change has occurred and cutoff detection
+ #ifdef SYDEBUG
+ string wfv,lfv;
+ winningField.getAsString(wfv);
+ loosingField.getAsString(lfv);
+ PDEBUGPRINTFX(DBG_DATA+DBG_CONFLICT+DBG_USERDATA,(
+ "Winning and loosing Field '%s' not equal: '%" FMT_LENGTH("0.30") "s' <> '%" FMT_LENGTH("0.30") "s'",
+ fFieldDefinitionsP->fFields[i].TCFG_CSTR(fieldname),
+ FMT_LENGTH_LIMITED(30,wfv.c_str()),FMT_LENGTH_LIMITED(30,lfv.c_str())
+ ));
+ #endif
+ // update loosing item, too
+ if (loosingField.isShortVers(winningField,fItemTypeP->getFieldOptions(i)->maxsize)) {
+ // winning field is short version of loosing field -> loosing field is "better", use it
+ winningField=loosingField;
+ if (mergerelevant) aChangedThis=true;
+ }
+ else {
+ // standard case, loosing field is replaced by winning field
+ loosingField=winningField;
+ if (mergerelevant) aChangedOther=true;
+ }
+ // this is some kind of item-level merge as well
+ #ifdef SYDEBUG
+ string ds;
+ winningField.getAsString(ds);
+ PDEBUGPRINTFX(DBG_DATA+DBG_CONFLICT+DBG_USERDATA,(
+ "- updated fields such that both have same value '%" FMT_LENGTH("0.40") "s'",
+ FMT_LENGTH_LIMITED(40,ds.c_str())
+ ));
+ #endif
+ }
+ } // field available in both items
+ } // field loop
+} // TMultiFieldItem::standardMergeWith
+
+#endif // server only
+
+#ifdef SYDEBUG
+// show item contents for debug
+void TMultiFieldItem::debugShowItem(uInt32 aDbgMask)
+{
+ TFieldListConfig *fielddefsP = getFieldDefinitions();
+ string val,fshow,oloc,orem;
+ TFieldOptions *foptP;
+ TItemField *fldP;
+
+ if (PDEBUGTEST(aDbgMask|DBG_DETAILS)) {
+ // very detailed
+ bool hasdata =
+ getSyncOp()!=sop_archive_delete &&
+ getSyncOp()!=sop_soft_delete &&
+ getSyncOp()!=sop_delete &&
+ getSyncOp()!=sop_copy &&
+ getSyncOp()!=sop_move;
+ // - item header info
+ PDEBUGPRINTFX(aDbgMask|DBG_DETAILS|DBG_HOT,(
+ "Item LocalID='%s', RemoteID='%s', operation=%s%s",
+ getLocalID(),
+ getRemoteID(),
+ SyncOpNames[getSyncOp()],
+ hasdata && PDEBUGTEST(aDbgMask|DBG_USERDATA) ? ", size: [maxlocal,maxremote,actual]" : ""
+ ));
+ if (!hasdata)
+ return; // do not show data for delete
+ if (!PDEBUGTEST(aDbgMask|DBG_USERDATA)) {
+ PDEBUGPRINTFX(aDbgMask|DBG_DETAILS,("*** field data not shown because userdata log is disabled ***"));
+ return; // do not show any field data
+ }
+ // - fields
+ fshow.erase();
+ for (sInt16 k=0; k<fielddefsP->numFields(); k++) {
+ SYSYNC_TRY {
+ const TFieldDefinition *fdP = &(fielddefsP->fFields[k]);
+ // get options
+ oloc="?";
+ if (fItemTypeP) {
+ foptP=fItemTypeP->getFieldOptions(k);
+ if (!foptP->available)
+ oloc="n/a";
+ else
+ StringObjPrintf(oloc,"%ld",(long)foptP->maxsize);
+ }
+ orem="?";
+ if (fTargetItemTypeP) {
+ foptP=fTargetItemTypeP->getFieldOptions(k);
+ if (!foptP->available)
+ orem="n/a";
+ else
+ StringObjPrintf(orem,"%ld",(long)foptP->maxsize);
+ }
+ // get value (but prevent pulling proxies)
+ fldP=NULL;
+ size_t n=0; // empty or unknown size
+ if (isAssigned(k)) {
+ fldP = getField(k);
+ val.erase();
+ n = fldP->StringObjFieldAppend(val,99); // get string representation and size
+ }
+ else
+ val="<unassigned>";
+ // now show main info
+ StringObjAppendPrintf(fshow,
+ "- %2d : %10s %-15s [%4s,%4s,%6ld] : %s\n",
+ k, // fid
+ ItemFieldTypeNames[fdP->type], // field type
+ TCFG_CSTR(fdP->fieldname), // field name
+ oloc.c_str(),
+ orem.c_str(),
+ long(n),
+ val.c_str()
+ );
+ // show array contents if any
+ if (fldP && fldP->isArray()) {
+ int arridx;
+ for (arridx=0; arridx<fldP->arraySize(); arridx++) {
+ // show array elements
+ TItemField *elemP = fldP->getArrayField(arridx,true);
+ val.erase();
+ elemP->StringObjFieldAppend(val,99);
+ StringObjAppendPrintf(fshow,
+ " -- element %4d : %s\n",
+ arridx,
+ val.c_str()
+ );
+ }
+ } // isArray
+ }
+ SYSYNC_CATCH(exception &e)
+ PDEBUGPRINTFX(DBG_ERROR,("Exception when trying to show field fid=%hd: %s[FLUSH]",k,e.what()));
+ SYSYNC_ENDCATCH
+ SYSYNC_CATCH(...)
+ PDEBUGPRINTFX(DBG_ERROR,("Unknown Exception when trying to show field fid=%hd[FLUSH]",k));
+ SYSYNC_ENDCATCH
+ } // for all fields
+ PDEBUGPUTSXX(aDbgMask|DBG_DETAILS|DBG_USERDATA,fshow.c_str(),0,true);
+ }
+ else if (PDEBUGTEST(aDbgMask)) {
+ // single line only
+ StringObjPrintf(fshow,
+ "Item locID='%s', RemID='%s', op=%s",
+ getLocalID(),
+ getRemoteID(),
+ SyncOpNames[getSyncOp()]
+ );
+ if (!PDEBUGTEST(aDbgMask|DBG_USERDATA)) {
+ fshow+=", *** userdata log disabled ***";
+ }
+ else if (
+ getSyncOp()==sop_archive_delete ||
+ getSyncOp()==sop_soft_delete ||
+ getSyncOp()==sop_delete
+ ) {
+ // do not show data for delete
+ }
+ else {
+ fshow+=", Fields: ";
+ for (sInt16 k=0; k<fielddefsP->numFields(); k++) {
+ if (isAssigned(k)) {
+ if (!getField(k)->isEmpty() && !getField(k)->hasProxy()) {
+ getField(k)->StringObjFieldAppend(fshow,20);
+ fshow+=", ";
+ }
+ }
+ }
+ }
+ PDEBUGPUTSX(aDbgMask,fshow.c_str());
+ }
+} // TMultiFieldItem::debugShowItem
+
+#endif
+
+/* end of TMultiFieldItem implementation */
+
+
+
+} // namespace sysync
+
+// eof
diff --git a/src/sysync/multifielditem.h b/src/sysync/multifielditem.h
new file mode 100755
index 0000000..96b6a53
--- /dev/null
+++ b/src/sysync/multifielditem.h
@@ -0,0 +1,371 @@
+/*
+ * File: MultiFieldItem.h
+ *
+ * Author: Lukas Zeller (luz@synthesis.ch)
+ *
+ * TMultiFieldItem
+ * Item consisting of multiple data fields (TItemField objects)
+ *
+ * Copyright (c) 2001-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ * 2001-08-08 : luz : created
+ *
+ */
+#ifndef MultiFieldItem_H
+#define MultiFieldItem_H
+
+// includes
+#include "syncitem.h"
+#include "itemfield.h"
+#include "configelement.h"
+#include "syncappbase.h"
+
+
+
+using namespace sysync;
+
+
+namespace sysync {
+
+
+// undefined field/var index
+// Note: this must be in 8-bit-negative range for current implementation of TScripTContext
+#define VARIDX_UNDEFINED -128
+
+
+// helper macros for getting a field by FID with casting
+// - get dynamically casted field, returns NULL if field has wrong type or does not exist
+#define GETFIELD_DYNAMIC_CAST(ty,tyid,fid,itemP) ( itemP->getField(fid) ? ITEMFIELD_DYNAMIC_CAST_PTR(ty,tyid,itemP->getField(fid)) : NULL )
+// - get statically casted field, field must exist and have that type
+#define GETFIELD_STATIC_CAST(ty,fid,itemP) ( static_cast<ty *>(itemP->getField(fid)) )
+
+// single field definition
+class TFieldDefinition {
+public:
+ // type
+ TItemFieldTypes type;
+ #ifdef ARRAYFIELD_SUPPORT
+ bool array; // set if this is an array field
+ #endif
+ // name
+ TCFG_STRING fieldname;
+ // relevant for equality (used in slow sync and in conflicts)
+ TEqualityMode eqRelevant;
+ // relevant for age sorting (used in case of conflict)
+ bool ageRelevant;
+ // merge options (used whenever one record overwrites other)
+ // - if mergeMode = mem_none, merge is disabled
+ // - if mergeMode = mem_fillempty, empty fields will be filled, but no concatenation is used
+ // - if mergeMode>=0, fields that are non-equal will be accumulated
+ // - if >0 then mergeMode will be used as separation char
+ char mergeMode;
+}; // TFieldDefinition
+
+
+#ifdef HARDCODED_TYPE_SUPPORT
+
+// single field definition
+typedef struct {
+ // type
+ TItemFieldTypes type;
+ //#ifdef ARRAYFIELD_SUPPORT
+ //%%% make all templates equal
+ bool array; // set if this is an array field
+ //#endif
+ // name
+ const char *fieldname;
+ // relevant for equality (used in slow sync and in conflicts)
+ TEqualityMode eqRelevant;
+ // relevant for age sorting (used in case of conflict)
+ bool ageRelevant;
+ // merge options (used whenever one record overwrites other)
+ // - if mergeMode = mem_none, merge is disabled
+ // - if mergeMode = mem_fillempty, empty fields will be filled, but no concatenation is used
+ // - if mergeMode>=0, fields that are non-equal will be accumulated
+ // - if >0 then mergeMode will be used as separation char
+ char mergeMode;
+ // Type limit defaults
+ uInt32 maxSize;
+ bool noTruncate;
+} TFieldDefinitionTemplate;
+
+
+// field definitions template
+typedef struct {
+ // number of fields
+ sInt16 numFields;
+ // sortable by age?
+ bool ageSortable; // %%%% not needed any more, will be set automatically if at least one field is agerelevant
+ // field definitions [0..numfields-1]
+ // note: sort order is 0..n, i.e. fields with lower indexes are more relevant
+ const TFieldDefinitionTemplate *fieldDefs;
+} TFieldDefinitionsTemplate;
+
+#endif
+
+
+// field array
+typedef std::vector<TFieldDefinition> TFieldDefinitionList;
+
+
+class TMultiFieldDatatypesConfig; // forward
+class TMultiFieldItem;
+class TMultiFieldItemType;
+
+// field definition config
+// This object MUST exist only ONCE per
+// MultiField-based type (as assignment compatibility is
+// given by POINTER IDENTITY of the MultiField's TFieldListConfig).
+class TFieldListConfig : public TConfigElement
+{
+ typedef TConfigElement inherited;
+ friend class TMultiFieldDatatypesConfig;
+public:
+ TFieldListConfig(const char* aName, TConfigElement *aParentElement);
+ virtual ~TFieldListConfig();
+ #ifdef HARDCODED_TYPE_SUPPORT
+ const TFieldDefinitionsTemplate *fFieldListTemplateP;
+ void readFieldListTemplate(const TFieldDefinitionsTemplate *aTemplateP);
+ #endif
+ // properties
+ // - sortable by age?
+ bool fAgeSortable;
+ // - field definition array
+ TFieldDefinitionList fFields;
+ sInt16 numFields(void) { return fFields.size(); };
+ sInt16 fieldIndex(const char *aName, size_t aLen=0);
+protected:
+ // check config elements
+ #ifdef CONFIGURABLE_TYPE_SUPPORT
+ virtual bool localStartElement(const char *aElementName, const char **aAttributes, sInt32 aLine);
+ virtual void localResolve(bool aLastPass);
+ #endif
+ virtual void clear();
+}; // TFieldListConfig
+
+
+// profile handler abstract base class
+class TProfileHandler
+{
+public:
+ // constructor/destructor
+ TProfileHandler(TProfileConfig *aProfileCfgP, TMultiFieldItemType *aItemTypeP);
+ ~TProfileHandler();
+ #ifdef OBJECT_FILTERING
+ // filtering
+ // - get field index of given filter expression identifier.
+ virtual sInt16 getFilterIdentifierFieldIndex(const char *aIdentifier, uInt16 aIndex) = 0;
+ // - add keywords and property names to filterCap
+ virtual void addFilterCapPropsAndKeywords(SmlPcdataListPtr_t &aFilterKeywords, SmlPcdataListPtr_t &aFilterProps, TTypeVariantDescriptor aVariantDesc, TSyncItemType *aItemTypeP) = 0;
+ #endif
+ // DevInf
+ // - obtain property list for type, returns NULL if none available
+ virtual SmlDevInfCTDataPropListPtr_t newCTDataPropList(TTypeVariantDescriptor aVariantDescriptor, TSyncItemType *aItemTypeP) { return NULL; };
+ // - Analyze CTCap part of devInf
+ virtual bool analyzeCTCap(SmlDevInfCTCapPtr_t aCTCapP, TSyncItemType *aItemTypeP) { return true; };
+ // set profile options
+ // - mode (for those profiles that have more than one, like MIME-DIR's old/standard)
+ virtual void setProfileMode(sInt32 aMode) { /* nop here */ };
+ // set related datastore (NULL for independent use e.g. from script functions)
+ void setRelatedDatastore(TLocalEngineDS *aRelatedDatastoreP) { fRelatedDatastoreP = aRelatedDatastoreP; };
+ // generate Text Data
+ virtual void generateText(TMultiFieldItem &aItem, string &aString) = 0;
+ // parse Data item
+ virtual bool parseText(const char *aText, stringSize aTextSize, TMultiFieldItem &aItem) = 0;
+ // Debug
+ #ifdef SYDEBUG
+ TDebugLogger *getDbgLogger(void);
+ uInt32 getDbgMask(void);
+ #endif
+protected:
+ TLocalEngineDS *fRelatedDatastoreP; // related datastore, can be NULL
+ TMultiFieldItemType *fItemTypeP; // the item type using this handler
+ // helpers
+ // - get session pointer
+ TSyncSession *getSession(void);
+ // - get session zones pointer
+ GZones *getSessionZones(void);
+ // - check availability (depends on item "supported" flags only in SyncML datastore context)
+ bool isFieldAvailable(TMultiFieldItem &aItem, sInt16 aFieldIndex);
+}; // TProfileHandler
+
+
+// profile config base class (e.g. for MIMEDIR and text profiles)
+// A profile is a config element related to a single fieldlist, but probably to more
+// than one datatype
+class TProfileConfig : public TConfigElement
+{
+ typedef TConfigElement inherited;
+ friend class TMultiFieldDatatypesConfig;
+ friend class TTextTypeConfig;
+public:
+ TProfileConfig(const char* aName, TConfigElement *aParentElement);
+ virtual ~TProfileConfig();
+ // handler factory
+ virtual TProfileHandler *newProfileHandler(TMultiFieldItemType *aItemTypeP) = 0;
+ // properties
+ // - field list config
+ TFieldListConfig *fFieldListP;
+protected:
+ // check config elements
+ virtual void clear();
+}; // TProfileConfig
+
+
+
+// fieldlists list
+typedef std::list<TFieldListConfig *> TFieldListsList;
+// profiles list
+typedef std::list<TProfileConfig *> TProfilesList;
+
+// multi-field based type config registry
+class TMultiFieldDatatypesConfig : public TDatatypesConfig
+{
+ typedef TDatatypesConfig inherited;
+public:
+ TMultiFieldDatatypesConfig(TConfigElement *aParentElement);
+ virtual ~TMultiFieldDatatypesConfig();
+ // properties
+ // - field lists
+ TFieldListsList fFieldLists;
+ TFieldListConfig *getFieldList(const char *aName);
+ // - profiles (fieldlist-related definitions, but probably used in multiple datatypes)
+ TProfilesList fProfiles;
+ TProfileConfig *getProfile(const char *aName);
+protected:
+ // check config elements
+ #ifdef CONFIGURABLE_TYPE_SUPPORT
+ virtual bool localStartElement(const char *aElementName, const char **aAttributes, sInt32 aLine);
+ virtual void localResolve(bool aLastPass);
+ #endif
+ virtual void clear();
+private:
+ void internalClear(void);
+}; // TMultiFieldDatatypesConfig
+
+
+const uInt16 ity_multifield = 100; // must be unique
+
+class TMultiFieldItem: public TSyncItem
+{
+ typedef TSyncItem inherited;
+public:
+ TMultiFieldItem(
+ TMultiFieldItemType *aItemTypeP, // owner's (=source) type
+ TMultiFieldItemType *aTargetItemTypeP // target type (for optimization)
+ );
+ virtual ~TMultiFieldItem();
+ // access to type
+ virtual uInt16 getTypeID(void) const { return ity_multifield; };
+ virtual bool isBasedOn(uInt16 aItemTypeID) const { return aItemTypeID==ity_multifield ? true : TSyncItem::isBasedOn(aItemTypeID); };
+ // assignment (IDs and contents)
+ virtual TSyncItem& operator=(TSyncItem &aSyncItem) { return TSyncItem::operator=(aSyncItem); };
+ // changelog support
+ #if defined(CHECKSUM_CHANGELOG) && !defined(RECORDHASH_FROM_DBAPI)
+ virtual uInt16 getDataCRC(uInt16 crc=0, bool aEQRelevantOnly=false);
+ #endif
+ // update dependencies of fields (such as BLOB proxies) on localID
+ virtual void updateLocalIDDependencies(void);
+ // - access fields by field name or index
+ TItemField *getField(sInt16 aFieldIndex);
+ TItemField *getField(const char *aFieldName);
+ TItemField &getFieldRef(sInt16 aFieldIndex); // get field reference (create if not yet created)
+ // for array support, but is always there to simplify implementations
+ TItemField *getArrayField(const char *aFieldName, sInt16 aIndex, bool aExistingOnly=false);
+ TItemField *getArrayField(sInt16 aFid, sInt16 aIndex, bool aExistingOnly=false);
+ // find index of field (returns FID_NOT_SUPPORTED if field is not a field of this item)
+ sInt16 getIndexOfField(const TItemField *aFieldP);
+ // adjust fid and repeat offset to access array element if
+ // base fid is an array field or to offset fid accordingly
+ // if based fid is NOT an array field
+ // - returns adjusted aFid and aIndex ready to be used with getArrayField()
+ // - returns true if aFid IS an array field
+ bool adjustFidAndIndex(sInt16 &aFid, sInt16 &aIndex);
+ // - check if field is assigned (exists and has a value)
+ bool const isAssigned(sInt16 aFieldIndex);
+ bool const isAssigned(const char *aFieldName);
+ // access field definitions
+ TFieldListConfig *getFieldDefinitions(void) { return fFieldDefinitionsP; };
+ // get associated MultiFieldItemType
+ TMultiFieldItemType *getItemType(void) { return fItemTypeP; };
+ TMultiFieldItemType *getTargetItemType(void) { return fTargetItemTypeP; };
+ // field availability (combined source & target)
+ bool isAvailable(sInt16 aFieldIndex);
+ bool isAvailable(const char *aFieldName);
+ bool knowsRemoteFieldOptions(void);
+ // - instantiate all fields that are available in source and target
+ void assignAvailables(void);
+ // compare abilities
+ virtual bool comparable(TSyncItem &aItem);
+ virtual bool sortable(TSyncItem &aItem);
+ // clear item data
+ virtual void cleardata(void);
+ // replace data contents from specified item
+ // - aAvailable only: only replace contents actually available in aItem, leave rest untouched
+ // - aDetectCutOffs: handle case where aItem could have somehow cut-off data and prevent replacing
+ // complete data with cut-off version (e.g. mobiles like T39m with limited name string capacity)
+ virtual bool replaceDataFrom(TSyncItem &aItem, bool aAvailableOnly=false, bool aDetectCutoffs=false, bool aAssignedOnly=false, bool aTransferUnassigned=false);
+ // check item before processing it
+ virtual bool checkItem(TLocalEngineDS *aDatastoreP);
+ #ifndef SYSYNC_CLIENT
+ // merge this item with specified item.
+ // Notes:
+ // - specified item is treated as loosing item, this item is winning item
+ // - also updates other item to make sure it is equal to the winning after the merge
+ // sets (but does not reset) change status of this and other item.
+ // Note that changes of non-relevant fields are not reported here.
+ virtual void mergeWith(TSyncItem &aItem, bool &aChangedThis, bool &aChangedOther, TLocalEngineDS *aDatastoreP);
+ // standard merge (subset of mergeWith, used if no merge script is defined)
+ void standardMergeWith(TMultiFieldItem &aItem, bool &aChangedThis, bool &aChangedOther);
+ // compare: returns 0 if equal, 1 if this > aItem, -1 if this < aItem
+ // SYSYNC_NOT_COMPARABLE if not equal and no ordering known
+ virtual sInt16 compareWith(
+ TSyncItem &aItem,
+ TEqualityMode aEqMode,
+ TLocalEngineDS *aDatastoreP
+ #ifdef SYDEBUG
+ ,bool aDebugShow=false
+ #endif
+ );
+ // standard compare (subset of compareWith, used if no compare script is defined)
+ sInt16 standardCompareWith(
+ TMultiFieldItem &aItem,
+ TEqualityMode aEqMode,
+ bool aDebugShow
+ );
+ #endif
+ #ifdef SYDEBUG
+ // show item contents for debug
+ virtual void debugShowItem(uInt32 aDbgMask=DBG_DATA);
+ #endif
+ #ifdef OBJECT_FILTERING
+ // New style generic filtering
+ // - check if item passes filter and probably apply some modifications to it
+ virtual bool postFetchFiltering(TLocalEngineDS *aDatastoreP);
+ // Old style object filtering
+ // - test if item passes filter
+ virtual bool testFilter(const char *aFilterString);
+ // - make item pass filter
+ virtual bool makePassFilter(const char *aFilterString);
+ // - actually process filter expression
+ bool processFilter(bool aMakePass, const char *&aPos, const char *aStop=NULL, sInt16 aLastOpPrec=0);
+ #endif
+protected:
+ // associated multifield type items (owner and target)
+ TMultiFieldItemType *fItemTypeP;
+ TMultiFieldItemType *fTargetItemTypeP;
+ // associated field definitions (copied from ItemType for performance)
+ TFieldListConfig *fFieldDefinitionsP;
+ // contents: array of actual fields
+ TItemField **fFieldsP;
+private:
+ // cast pointer to same type, returns NULL if incompatible
+ TMultiFieldItem *castToSameTypeP(TSyncItem *aItemP); // all are compatible TSyncItem
+}; // TMultiFieldItem
+
+
+} // namespace sysync
+
+#endif // MultiFieldItem_H
+
+// eof
diff --git a/src/sysync/multifielditemtype.cpp b/src/sysync/multifielditemtype.cpp
new file mode 100755
index 0000000..9c61095
--- /dev/null
+++ b/src/sysync/multifielditemtype.cpp
@@ -0,0 +1,1007 @@
+/*
+ * File: MultiFieldItemType.cpp
+ *
+ * Author: Lukas Zeller (luz@synthesis.ch)
+ *
+ * TMultiFieldItemType
+ * Type consisting of multiple data fields (TItemField objects)
+ * To be used as base class for field formats like vCard,
+ * vCalendar etc.
+ *
+ * Copyright (c) 2001-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ * 2001-08-13 : luz : created
+ *
+ */
+
+// includes
+#include "prefix_file.h"
+#include "sysync.h"
+#include "multifielditemtype.h"
+
+
+namespace sysync {
+
+// multifield-based datatype config
+
+TMultiFieldTypeConfig::TMultiFieldTypeConfig(const char* aName, TConfigElement *aParentElement) :
+ TDataTypeConfig(aName,aParentElement)
+{
+ clear();
+} // TMultiFieldTypeConfig::TMultiFieldTypeConfig
+
+
+TMultiFieldTypeConfig::~TMultiFieldTypeConfig()
+{
+ clear();
+} // TMultiFieldTypeConfig::~TMultiFieldTypeConfig
+
+
+// init defaults
+void TMultiFieldTypeConfig::clear(void)
+{
+ // clear properties
+ // - remove link to field list
+ fFieldListP=NULL;
+ // - remove link to field list
+ fProfileConfigP=NULL;
+ #ifdef SCRIPT_SUPPORT
+ fInitScript.erase();
+ fIncomingScript.erase();
+ fOutgoingScript.erase();
+ fFilterInitScript.erase();
+ fPostFetchFilterScript.erase();
+ #ifndef SYSYNC_CLIENT
+ fCompareScript.erase();
+ fMergeScript.erase();
+ #endif
+ fProcessItemScript.erase();
+ #endif
+ // clear properties
+ fProfileMode=PROFILEMODE_DEFAULT; // default profile mode
+ // clear inherited
+ inherited::clear();
+} // TMultiFieldTypeConfig::clear
+
+
+#ifdef SCRIPT_SUPPORT
+
+class TMFTypeFuncs {
+public:
+
+ // void SETFILTERALL(integer all)
+ // sets if all records in the syncset need to checked against filter or only those that are new or changed
+ static void func_SetFilterAll(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ static_cast<TMultiFieldItemType *>(aFuncContextP->getCallerContext())->fNeedToFilterAll =
+ aFuncContextP->getLocalVar(0)->getAsBoolean();
+ }; // func_SetFilterAll
+
+
+ #ifdef SYSYNC_TARGET_OPTIONS
+
+ // integer SIZELIMIT()
+ // gets the size limit set for this item
+ static void func_Limit(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ fieldinteger_t i = static_cast<TMultiFieldItemType *>(aFuncContextP->getCallerContext())->fDsP->fItemSizeLimit;
+ if (i<0)
+ aTermP->unAssign();
+ else
+ aTermP->setAsInteger(i);
+ }; // func_Limit
+
+
+ // SETSIZELIMIT(integer limit)
+ // sets size limit used for generating this item for remote
+ static void func_SetLimit(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ static_cast<TMultiFieldItemType *>(aFuncContextP->getCallerContext())->fDsP->fItemSizeLimit =
+ aFuncContextP->getLocalVar(0)->isUnassigned() ? -1 : aFuncContextP->getLocalVar(0)->getAsInteger();
+ }; // func_SetLimit
+
+ #endif
+
+
+
+ #ifndef SYSYNC_CLIENT
+
+ // void ECHOITEM(string syncop)
+ // creates a duplicate of the processed item to be sent back to sender with the specified syncop
+ static void func_EchoItem(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ // convert to syncop
+ string s;
+ aFuncContextP->getLocalVar(0)->getAsString(s);
+ sInt16 sop;
+ StrToEnum(SyncOpNames, numSyncOperations, sop, s.c_str());
+ static_cast<TMultiFieldItemType *>(aFuncContextP->getCallerContext())->fDsP->fEchoItemOp =
+ (TSyncOperation) sop;
+ }; // func_EchoItem
+
+
+ // void CONFLICTSTRATEGY(string strategy)
+ // sets the conflict strategy for this item
+ static void func_ConflictStrategy(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ // convert to conflictstrategy
+ string s;
+ aFuncContextP->getLocalVar(0)->getAsString(s);
+ sInt16 strategy;
+ StrToEnum(conflictStrategyNames,numConflictStrategies, strategy, s.c_str());
+ static_cast<TMultiFieldItemType *>(aFuncContextP->getCallerContext())->fDsP->fItemConflictStrategy =
+ (TConflictResolution) strategy;
+ }; // func_ConflictStrategy
+
+
+ // void FORCECONFLICT()
+ // forces conflict between this item and item from the DB
+ static void func_ForceConflict(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ static_cast<TMultiFieldItemType *>(aFuncContextP->getCallerContext())->fDsP->fForceConflict=true;
+ }; // func_ForceConflict
+
+
+ // void DELETEWINS()
+ // in a replace/delete conflict, delete wins (normally, replace wins)
+ static void func_DeleteWins(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ static_cast<TMultiFieldItemType *>(aFuncContextP->getCallerContext())->fDsP->fDeleteWins=true;
+ }; // func_DeleteWins
+
+
+ // void PREVENTADD()
+ // if set, attempt to add item from remote will cause no add but delete of remote item
+ static void func_PreventAdd(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ static_cast<TMultiFieldItemType *>(aFuncContextP->getCallerContext())->fDsP->fPreventAdd=true;
+ }; // func_PreventAdd
+
+
+ // void IGNOREUPDATE()
+ // if set, attempt to update existing items from remote will be ignored
+ static void func_IgnoreUpdate(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ static_cast<TMultiFieldItemType *>(aFuncContextP->getCallerContext())->fDsP->fIgnoreUpdate=true;
+ }; // func_IgnoreUpdate
+
+
+ // void MERGEFIELDS()
+ static void func_MergeFields(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ TMultiFieldItemType *mfitP = static_cast<TMultiFieldItemType *>(aFuncContextP->getCallerContext());
+ if (mfitP->fFirstItemP)
+ mfitP->fFirstItemP->standardMergeWith(*(mfitP->fSecondItemP),mfitP->fChangedFirst,mfitP->fChangedSecond);
+ }; // func_MergeFields
+
+
+ // integer WINNINGCHANGED()
+ // returns true if winning was changed
+ static void func_WinningChanged(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ TMultiFieldItemType *mfitP = static_cast<TMultiFieldItemType *>(aFuncContextP->getCallerContext());
+ aTermP->setAsInteger(mfitP->fChangedFirst ? 1 : 0);
+ }; // func_WinningChanged
+
+ // integer LOOSINGCHANGED()
+ // returns true if loosing was changed
+ static void func_LoosingChanged(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ TMultiFieldItemType *mfitP = static_cast<TMultiFieldItemType *>(aFuncContextP->getCallerContext());
+ aTermP->setAsInteger(mfitP->fChangedSecond ? 1 : 0);
+ }; // func_LoosingChanged
+
+
+ static void func_SetWinningChanged(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ TMultiFieldItemType *mfitP = static_cast<TMultiFieldItemType *>(aFuncContextP->getCallerContext());
+ mfitP->fChangedFirst =
+ aFuncContextP->getLocalVar(0)->getAsBoolean();
+ }; // func_SetWinningChanged
+
+ static void func_SetLoosingChanged(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ TMultiFieldItemType *mfitP = static_cast<TMultiFieldItemType *>(aFuncContextP->getCallerContext());
+ mfitP->fChangedSecond =
+ aFuncContextP->getLocalVar(0)->getAsBoolean();
+ }; // func_SetLoosingChanged
+
+
+ // integer COMPAREFIELDS()
+ // returns 0 if equal, 1 if first > second, -1 if first < second
+ static void func_CompareFields(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ TMultiFieldItemType *mfitP = static_cast<TMultiFieldItemType *>(aFuncContextP->getCallerContext());
+ if (!mfitP->fFirstItemP) aTermP->setAsInteger(SYSYNC_NOT_COMPARABLE);
+ else {
+ aTermP->setAsInteger(
+ mfitP->fFirstItemP->standardCompareWith(*(mfitP->fSecondItemP),mfitP->fEqMode,DEBUGTEST(DBG_SCRIPTS+DBG_DATA+DBG_MATCH)) ? 1 : 0
+ );
+ }
+ }; // func_CompareFields
+
+ #endif
+
+
+ // string SYNCOP()
+ // returns sync-operation as text
+ static void func_SyncOp(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ aTermP->setAsString(
+ SyncOpNames[static_cast<TMultiFieldItemType *>(aFuncContextP->getCallerContext())->fCurrentSyncOp]
+ );
+ }; // func_SyncOp
+
+
+ // void REJECTITEM(integer statuscode)
+ // causes current item not to be processed, but rejected with status code (0=silently rejected)
+ static void func_RejectItem(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ static_cast<TMultiFieldItemType *>(aFuncContextP->getCallerContext())->fDsP->fRejectStatus=
+ aFuncContextP->getLocalVar(0)->getAsInteger();
+ }; // func_RejectItem
+
+
+ // string REMOTEID()
+ // returns target item's remoteID as text
+ static void func_RemoteID(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ TMultiFieldItemType *mfitP = static_cast<TMultiFieldItemType *>(aFuncContextP->getCallerContext());
+ if (mfitP->fFirstItemP) {
+ aTermP->setAsString(mfitP->fFirstItemP->getRemoteID());
+ }
+ }; // func_RemoteID
+
+
+ // void SETREMOTEID(string remoteid)
+ // sets the target item's remote ID as text
+ static void func_SetRemoteID(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ TMultiFieldItemType *mfitP = static_cast<TMultiFieldItemType *>(aFuncContextP->getCallerContext());
+ if (mfitP->fFirstItemP) {
+ string s;
+ aFuncContextP->getLocalVar(0)->getAsString(s);
+ mfitP->fFirstItemP->setRemoteID(s.c_str());
+ }
+ }; // func_SetRemoteID
+
+
+ // string LOCALID()
+ // returns target item's localID as text
+ static void func_LocalID(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ TMultiFieldItemType *mfitP = static_cast<TMultiFieldItemType *>(aFuncContextP->getCallerContext());
+ if (mfitP->fFirstItemP) {
+ aTermP->setAsString(mfitP->fFirstItemP->getLocalID());
+ }
+ }; // func_LocalID
+
+
+ // void SETLOCALID(string remoteid)
+ // sets the target item's localID as text
+ static void func_SetLocalID(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ TMultiFieldItemType *mfitP = static_cast<TMultiFieldItemType *>(aFuncContextP->getCallerContext());
+ if (mfitP->fFirstItemP) {
+ string s;
+ aFuncContextP->getLocalVar(0)->getAsString(s);
+ mfitP->fFirstItemP->setLocalID(s.c_str());
+ }
+ }; // func_SetLocalID
+
+}; // TMFTypeFuncs
+
+
+// chain function to link datastore-level functions
+static void* DataTypeChainFunc(void *&aNextCallerContext)
+{
+ // caller context for datastore-level functions is the datastore pointer
+ if (aNextCallerContext)
+ aNextCallerContext = static_cast<TMultiFieldItemType *>(aNextCallerContext)->fDsP;
+ // next table is that of the localdatastore
+ return (void *) &DBFuncTable;
+} // DataTypeChainFunc
+
+const uInt8 param_StrArg[] = { VAL(fty_string) };
+const uInt8 param_IntArg[] = { VAL(fty_integer) };
+
+// builtin functions for datastore-context table
+const TBuiltInFuncDef DataTypeFuncDefs[] = {
+ { "SETFILTERALL", TMFTypeFuncs::func_SetFilterAll, fty_none, 1, param_IntArg },
+ #ifdef SYSYNC_TARGET_OPTIONS
+ { "SIZELIMIT", TMFTypeFuncs::func_Limit, fty_integer, 0, NULL },
+ { "SETSIZELIMIT", TMFTypeFuncs::func_SetLimit, fty_none, 1, param_IntArg },
+ #endif
+ #ifndef SYSYNC_CLIENT
+ { "ECHOITEM", TMFTypeFuncs::func_EchoItem, fty_none, 1, param_StrArg },
+ { "CONFLICTSTRATEGY", TMFTypeFuncs::func_ConflictStrategy, fty_none, 1, param_StrArg },
+ { "FORCECONFLICT", TMFTypeFuncs::func_ForceConflict, fty_none, 0, NULL },
+ { "DELETEWINS", TMFTypeFuncs::func_DeleteWins, fty_none, 0, NULL },
+ { "PREVENTADD", TMFTypeFuncs::func_PreventAdd, fty_none, 0, NULL },
+ { "IGNOREUPDATE", TMFTypeFuncs::func_IgnoreUpdate, fty_none, 0, NULL },
+ { "MERGEFIELDS", TMFTypeFuncs::func_MergeFields, fty_none, 0, NULL },
+ { "WINNINGCHANGED", TMFTypeFuncs::func_WinningChanged, fty_integer, 0, NULL },
+ { "LOOSINGCHANGED", TMFTypeFuncs::func_LoosingChanged, fty_integer, 0, NULL },
+ { "SETWINNINGCHANGED", TMFTypeFuncs::func_SetWinningChanged, fty_none, 1, param_IntArg },
+ { "SETLOOSINGCHANGED", TMFTypeFuncs::func_SetLoosingChanged, fty_none, 1, param_IntArg },
+ { "COMPAREFIELDS", TMFTypeFuncs::func_CompareFields, fty_integer, 0, NULL },
+ #endif
+ { "SYNCOP", TMFTypeFuncs::func_SyncOp, fty_string, 0, NULL },
+ { "REJECTITEM", TMFTypeFuncs::func_RejectItem, fty_none, 1, param_IntArg },
+ { "LOCALID", TMFTypeFuncs::func_LocalID, fty_string, 0, NULL },
+ { "SETLOCALID", TMFTypeFuncs::func_SetLocalID, fty_none, 1, param_StrArg },
+ { "REMOTEID", TMFTypeFuncs::func_RemoteID, fty_string, 0, NULL },
+ { "SETREMOTEID", TMFTypeFuncs::func_SetRemoteID, fty_none, 1, param_StrArg },
+};
+
+const TFuncTable DataTypeFuncTable = {
+ sizeof(DataTypeFuncDefs) / sizeof(TBuiltInFuncDef), // size of table
+ DataTypeFuncDefs, // table pointer
+ DataTypeChainFunc // chain to localdatastore funcs
+};
+
+
+#endif
+
+
+#ifdef CONFIGURABLE_TYPE_SUPPORT
+
+// config element parsing
+bool TMultiFieldTypeConfig::localStartElement(const char *aElementName, const char **aAttributes, sInt32 aLine)
+{
+ // checking the elements
+ // Note: derived classes might override this "use" further
+ // Here, we check for using only a file list, or a profile which implies a field list
+ if (strucmp(aElementName,"use")==0) {
+ if (fFieldListP || fProfileConfigP)
+ return fail("'use' cannot be specified more than once");
+ // - get type registry
+ TMultiFieldDatatypesConfig *mufcP;
+ GET_CASTED_PTR(mufcP,TMultiFieldDatatypesConfig,getParentElement(),DEBUGTEXT("TMultiFieldTypeConfig with non-TMultiFieldDatatypesConfig parent","txit2"));
+ // - check what to look for
+ const char *pnam = getAttr(aAttributes,"fieldlist");
+ if (pnam) {
+ // we are addressing a fieldlist
+ fFieldListP=mufcP->getFieldList(pnam);
+ if (!fFieldListP)
+ return fail("unknown field list '%s' specified in 'use'",pnam);
+ }
+ else {
+ // this must be a profile (we may also call it 'mimeprofile' for historical, <=2.1 engine reasons)
+ pnam = getAttr(aAttributes,"profile");
+ if (!pnam)
+ pnam = getAttr(aAttributes,"mimeprofile"); // %%% for compatibility with <=2.1 engine configs
+ if (!pnam)
+ return fail("'use' must have a 'fieldlist' or 'profile' attribute");
+ fProfileConfigP=mufcP->getProfile(pnam);
+ if (!fProfileConfigP)
+ return fail("unknown profile '%s' specified in 'use'",pnam);
+ // - copy field list pointer into TMultiFieldTypeConfig as well
+ fFieldListP = fProfileConfigP->fFieldListP;
+ }
+ expectEmpty();
+ }
+ else if (strucmp(aElementName,"profilemode")==0)
+ expectInt32(fProfileMode); // usually, this is set implicitly by derived types, such as vCard, vCalendar
+ #ifdef SCRIPT_SUPPORT
+ else if (strucmp(aElementName,"initscript")==0)
+ expectScript(fInitScript,aLine,&DataTypeFuncTable);
+ else if (strucmp(aElementName,"incomingscript")==0)
+ expectScript(fIncomingScript,aLine,&DataTypeFuncTable);
+ else if (strucmp(aElementName,"outgoingscript")==0)
+ expectScript(fOutgoingScript,aLine,&DataTypeFuncTable);
+ else if (strucmp(aElementName,"filterinitscript")==0)
+ expectScript(fFilterInitScript,aLine,&DataTypeFuncTable);
+ else if (strucmp(aElementName,"filterscript")==0)
+ expectScript(fPostFetchFilterScript,aLine,&DataTypeFuncTable);
+ #ifndef SYSYNC_CLIENT
+ else if (strucmp(aElementName,"comparescript")==0)
+ expectScript(fCompareScript,aLine,&DataTypeFuncTable);
+ else if (strucmp(aElementName,"mergescript")==0)
+ expectScript(fMergeScript,aLine,&DataTypeFuncTable);
+ #endif
+ else if (strucmp(aElementName,"processitemscript")==0)
+ expectScript(fProcessItemScript,aLine,&DataTypeFuncTable);
+ #endif
+ // - none known here
+ else
+ return TDataTypeConfig::localStartElement(aElementName,aAttributes,aLine);
+ // ok
+ return true;
+} // TMultiFieldTypeConfig::localStartElement
+
+
+// resolve
+void TMultiFieldTypeConfig::localResolve(bool aLastPass)
+{
+ // check
+ if (aLastPass) {
+ if (!fFieldListP)
+ SYSYNC_THROW(TConfigParseException("missing 'use' in datatype"));
+ #ifdef SCRIPT_SUPPORT
+ TScriptContext *sccP = NULL;
+ SYSYNC_TRY {
+ // resolve all scripts in same context
+ // - init script
+ TScriptContext::resolveScript(getSyncAppBase(),fInitScript,sccP,fFieldListP);
+ // - incoming/outgoing scripts
+ TScriptContext::resolveScript(getSyncAppBase(),fIncomingScript,sccP,fFieldListP);
+ TScriptContext::resolveScript(getSyncAppBase(),fOutgoingScript,sccP,fFieldListP);
+ // - filtering scripts
+ TScriptContext::resolveScript(getSyncAppBase(),fFilterInitScript,sccP,fFieldListP);
+ TScriptContext::resolveScript(getSyncAppBase(),fPostFetchFilterScript,sccP,fFieldListP);
+ // - compare and merge scripts
+ #ifndef SYSYNC_CLIENT
+ TScriptContext::resolveScript(getSyncAppBase(),fCompareScript,sccP,fFieldListP);
+ TScriptContext::resolveScript(getSyncAppBase(),fMergeScript,sccP,fFieldListP);
+ #endif
+ // - special processing of incoming items before they are sent to the DB
+ TScriptContext::resolveScript(getSyncAppBase(),fProcessItemScript,sccP,fFieldListP);
+ // - forget this context (rebuild will take place in the datastore's fXXXXTypeScriptContextP)
+ if (sccP) delete sccP;
+ }
+ SYSYNC_CATCH (...)
+ if (sccP) delete sccP;
+ SYSYNC_RETHROW;
+ SYSYNC_ENDCATCH
+ #endif
+ }
+ // resolve inherited
+ inherited::localResolve(aLastPass);
+} // TMultiFieldTypeConfig::localResolve
+
+#endif // CONFIGURABLE_TYPE_SUPPORT
+
+
+#ifdef HARDCODED_TYPE_SUPPORT
+
+void TMultiFieldTypeConfig::setConfig(TFieldListConfig *aFieldList, const char *aTypeName, const char* aTypeVers)
+{
+ // set field list
+ fFieldListP=aFieldList;
+ // set type name/version
+ if (aTypeName) fTypeName=aTypeName;
+ if (aTypeVers) fTypeVersion=aTypeVers;
+} // TMultiFieldTypeConfig::setConfig
+
+#endif // HARDCODED_TYPE_SUPPORT
+
+
+
+/*
+ * Implementation of TMultiFieldItemType
+ */
+
+/* public TMultiFieldItemType members */
+
+
+TMultiFieldItemType::TMultiFieldItemType(
+ TSyncSession *aSessionP,
+ TDataTypeConfig *aTypeConfigP,
+ const char *aCTType,
+ const char *aVerCT,
+ TSyncDataStore *aRelatedDatastoreP,
+ TFieldListConfig *aFieldDefinitions // field definition list
+) :
+ TSyncItemType(aSessionP,aTypeConfigP,aCTType,aVerCT,aRelatedDatastoreP)
+{
+ // save field definition pointer
+ fFieldDefinitionsP = aFieldDefinitions;
+ if (!fFieldDefinitionsP)
+ SYSYNC_THROW(TSyncException(DEBUGTEXT("MultiFieldItemType without fieldDefinitions","mfit1")));
+ // create options array
+ fFieldOptionsP = new TFieldOptions[fFieldDefinitionsP->numFields()];
+ // - init default options
+ for (sInt16 i=0; i<fFieldDefinitionsP->numFields(); i++) {
+ // by default, all fields in the list are available
+ fFieldOptionsP[i].available=true; // available
+ fFieldOptionsP[i].maxsize=
+ aSessionP->fLimitedRemoteFieldLengths ?
+ FIELD_OPT_MAXSIZE_UNKNOWN : // limited, but unknown length
+ FIELD_OPT_MAXSIZE_NONE; // no known limit
+ fFieldOptionsP[i].maxoccur=0; // maximum occurrence count not defined
+ fFieldOptionsP[i].notruncate=false; // allow truncation by default
+ }
+} // TMultiFieldItemType::TMultiFieldItemType
+
+
+TMultiFieldItemType::~TMultiFieldItemType()
+{
+ // remove fields options array
+ delete [] fFieldOptionsP;
+} // TMultiFieldItemType::~TMultiFieldItemType
+
+
+// compatibility (=assignment compatibility between items based on these types)
+bool TMultiFieldItemType::isCompatibleWith(TSyncItemType *aReferenceType)
+{
+ // check compatibility
+ // - reference must be based on TMultiFieldItemType
+ if (!aReferenceType->isBasedOn(ity_multifield)) return false;
+ // - both multifields must be based on same field list
+ return
+ fFieldDefinitionsP == static_cast<TMultiFieldItemType *>(aReferenceType)->fFieldDefinitionsP;
+} // TMultiFieldItemType::isCompatibleWith
+
+
+
+// Initialize use of datatype with a datastore
+// Note: This might not affect any datatype-related members, as datatype can
+// be in use by different datastores at a time. Intitialisation is
+// performed on members of the datastore (such as fXXXXTypeScriptContextP)
+void TMultiFieldItemType::initDataTypeUse(TLocalEngineDS *aDatastoreP, bool aForSending, bool aForReceiving)
+{
+ #ifdef SCRIPT_SUPPORT
+ // Note: identifier situation is equal for all calls of initDataTypeUse, however,
+ // as multiple datastores might use the datatype, several instances of the
+ // context (with separate local var VALUEs) might exist.
+ // Delete old context(s), if any
+ if (aDatastoreP->fSendingTypeScriptContextP && aForSending) {
+ // delete only if actually using this type for sending (otherwise, sending context must be left untouched)
+ delete aDatastoreP->fSendingTypeScriptContextP;
+ aDatastoreP->fSendingTypeScriptContextP=NULL;
+ }
+ if (aDatastoreP->fReceivingTypeScriptContextP && aForReceiving) {
+ // delete only if actually using this type for receiving (otherwise, receiving context must be left untouched)
+ delete aDatastoreP->fReceivingTypeScriptContextP;
+ aDatastoreP->fReceivingTypeScriptContextP=NULL;
+ }
+ // Create contexts in datastore and instantiate variables for scripts
+ TMultiFieldTypeConfig *cfgP = getMultifieldTypeConfig();
+ // Get context
+ // NOTE: if initialized for both sending and receiving, only the sending
+ // context will be used for both receiving and sending
+ TScriptContext **ctxPP;
+ if (aForSending)
+ // sending or both
+ ctxPP = &aDatastoreP->fSendingTypeScriptContextP;
+ else
+ // only receiving
+ ctxPP = &aDatastoreP->fReceivingTypeScriptContextP;
+ // NOTE: always rebuild all scripts, even though
+ // some might remain unused in the sending or receiving context
+ // This is needed because we don't know at ResolveScript() if a
+ // type will be used for input or output only, and rebuild must
+ // be in the same order as Resolve to guarantee same var/field indexes.
+ // - init script (will be executed right after all scripts are resolved)
+ TScriptContext::rebuildContext(cfgP->getSyncAppBase(),cfgP->fInitScript,*ctxPP,fSessionP);
+ // - incoming and outgoing data brush-ups scripts
+ TScriptContext::rebuildContext(cfgP->getSyncAppBase(),cfgP->fIncomingScript,*ctxPP,fSessionP);
+ TScriptContext::rebuildContext(cfgP->getSyncAppBase(),cfgP->fOutgoingScript,*ctxPP,fSessionP);
+ // - filtering scripts
+ TScriptContext::rebuildContext(cfgP->getSyncAppBase(),cfgP->fFilterInitScript,*ctxPP,fSessionP);
+ TScriptContext::rebuildContext(cfgP->getSyncAppBase(),cfgP->fPostFetchFilterScript,*ctxPP,fSessionP);
+ // - compare and merge scripts
+ #ifndef SYSYNC_CLIENT
+ TScriptContext::rebuildContext(cfgP->getSyncAppBase(),cfgP->fCompareScript,*ctxPP,fSessionP);
+ TScriptContext::rebuildContext(cfgP->getSyncAppBase(),cfgP->fMergeScript,*ctxPP,fSessionP);
+ #endif
+ // - special processing of incoming items before they are sent to the DB
+ TScriptContext::rebuildContext(cfgP->getSyncAppBase(),cfgP->fProcessItemScript,*ctxPP,fSessionP,true); // now build vars
+ // Now execute init script
+ // - init the needed member vars
+ fDsP = aDatastoreP;
+ fFirstItemP = NULL;
+ fSecondItemP = NULL;
+ fEqMode = eqm_none;
+ fCurrentSyncOp = sop_none;
+ TScriptContext::execute(
+ *ctxPP, // the context
+ cfgP->fInitScript, // the script
+ &DataTypeFuncTable, // context function table
+ this // context data (myself)
+ );
+ #endif
+} // TMultiFieldItemType::initDataTypeUse
+
+
+
+#ifdef OBJECT_FILTERING
+
+// - check if new-style filtering needed
+void TMultiFieldItemType::initPostFetchFiltering(bool &aNeeded, bool &aNeededForAll, TLocalEngineDS *aDatastoreP)
+{
+ // init filter and determine if we need any filtering
+ #ifndef SCRIPT_SUPPORT
+ aNeeded=false;
+ aNeededForAll=false;
+ #else
+ fNeedToFilterAll=true; // init member that will be acessed by the script
+ fDsP=aDatastoreP; // set for access from script funcs
+ fFirstItemP = NULL;
+ fSecondItemP = NULL;
+ fEqMode = eqm_none;
+ aNeeded=TScriptContext::executeTest(
+ false, // default to no need for any filters
+ aDatastoreP->fSendingTypeScriptContextP,
+ static_cast<TMultiFieldTypeConfig *>(fTypeConfigP)->fFilterInitScript,
+ &DataTypeFuncTable, // context function table
+ this // context data (myself)
+ );
+ // - copy back script output
+ aNeededForAll=fNeedToFilterAll;
+ if (!aNeeded) aNeededForAll=false; // keep this consistent
+ POBJDEBUGPRINTFX(fSessionP,DBG_FILTER,(
+ "Type-specific postfetch filtering %sneeded%s",
+ aNeeded ? "" : "NOT ",
+ aNeeded ? (aNeededForAll ? " and to be applied to all records" : " only for changed records") : ""
+ ));
+ #endif
+} // TMultiFieldItemType::initPostFetchFiltering
+
+
+// called by TMultiField::postFetchFiltering()
+// Checks if to-be-sent item fetched from DB passes all filters to be actually sent to remote
+bool TMultiFieldItemType::postFetchFiltering(TMultiFieldItem *aItemP, TLocalEngineDS *aDatastoreP)
+{
+ #ifndef SCRIPT_SUPPORT
+ return true; // no scripts, always pass
+ #else
+ // if no script, always pass
+ string &script = static_cast<TMultiFieldTypeConfig *>(fTypeConfigP)->fPostFetchFilterScript;
+ if (script.empty()) return true;
+ // call script to test filter
+ fDsP=aDatastoreP; // set for access from script funcs
+ fFirstItemP = NULL;
+ fSecondItemP = NULL;
+ fEqMode = eqm_none;
+ fCurrentSyncOp = aItemP->getSyncOp();
+ return TScriptContext::executeTest(
+ false, // default to not passing if filter script fails or returns no value
+ aDatastoreP->fSendingTypeScriptContextP,
+ script,
+ &DataTypeFuncTable, // context function table
+ this, // context data (myself)
+ aItemP, // target item
+ true // can be written if needed
+ );
+ #endif
+} // TMultiFieldItemType::postFetchFiltering
+
+#endif
+
+
+// check item before processing it
+bool TMultiFieldItemType::checkItem(TMultiFieldItem &aItem, TLocalEngineDS *aDatastoreP)
+{
+ #ifndef SCRIPT_SUPPORT
+ return true; // simply ok
+ #else
+ // execute a script if there is a context for it
+ fDsP=aDatastoreP; // set for access from script funcs
+ fFirstItemP = &aItem;
+ fSecondItemP = NULL;
+ fEqMode = eqm_none;
+ fCurrentSyncOp = aItem.getSyncOp();
+ return TScriptContext::execute(
+ aDatastoreP->fReceivingTypeScriptContextP ?
+ aDatastoreP->fReceivingTypeScriptContextP :
+ aDatastoreP->fSendingTypeScriptContextP,
+ static_cast<TMultiFieldTypeConfig *>(fTypeConfigP)->fProcessItemScript,
+ &DataTypeFuncTable, // context function table
+ this, // context data (myself)
+ &aItem, // the item
+ true // checking might change data
+ );
+ fFirstItemP = NULL;
+ #endif
+} // TMultiFieldItemType::checkItem
+
+
+#ifndef SYSYNC_CLIENT
+
+// compare two items
+sInt16 TMultiFieldItemType::compareItems(
+ TMultiFieldItem &aFirstItem,
+ TMultiFieldItem &aSecondItem,
+ TEqualityMode aEqMode,
+ bool aDebugShow,
+ TLocalEngineDS *aDatastoreP
+)
+{
+ #ifndef SCRIPT_SUPPORT
+ // just do standard compare
+ return aFirstItem.standardCompareWith(aSecondItem,aEqMode,aDebugShow);
+ #else
+ // if no script use standard merging
+ sInt16 cmpres;
+ string &script = static_cast<TMultiFieldTypeConfig *>(fTypeConfigP)->fCompareScript;
+ if (script.empty())
+ return aFirstItem.standardCompareWith(aSecondItem,aEqMode,aDebugShow);
+ // execute script to perform comparison
+ // - set up helpers
+ fDsP = aDatastoreP;
+ fEqMode = aEqMode;
+ fFirstItemP = &aFirstItem;
+ fSecondItemP = &aSecondItem;
+ fCurrentSyncOp = fDsP->fCurrentSyncOp;
+ TItemField *resP=NULL;
+ bool ok=TScriptContext::executeWithResult(
+ resP, // result will be put here if there is one
+ aDatastoreP->fReceivingTypeScriptContextP ?
+ aDatastoreP->fReceivingTypeScriptContextP :
+ aDatastoreP->fSendingTypeScriptContextP,
+ script,
+ &DataTypeFuncTable, // context function table
+ this, // context data (myself)
+ &aFirstItem, // winning item
+ false, // compare is read-only
+ &aSecondItem, // loosing item
+ false, // compare is read-only
+ !PDEBUGTEST(DBG_MATCH+DBG_EXOTIC) // suppress script debug output unless comparison detail display is on
+ );
+ // items are no longer available
+ fFirstItemP = NULL;
+ fSecondItemP = NULL;
+ // get result
+ cmpres=SYSYNC_NOT_COMPARABLE; // default to not comparable (if error or no result)
+ if (ok) {
+ if (resP) cmpres=resP->getAsInteger();
+ }
+ // dispose result field
+ if (resP) delete resP;
+ // return result
+ return cmpres;
+ #endif
+} // TMultiFieldItemType::compareItems
+
+
+// merge two items
+void TMultiFieldItemType::mergeItems(
+ TMultiFieldItem &aWinningItem,
+ TMultiFieldItem &aLoosingItem,
+ bool &aChangedWinning,
+ bool &aChangedLoosing,
+ TLocalEngineDS *aDatastoreP
+)
+{
+ #ifndef SCRIPT_SUPPORT
+ // just do standard merge
+ aWinningItem.standardMergeWith(aLoosingItem,aChangedWinning,aChangedLoosing);
+ return;
+ #else
+ // if no script use standard merging
+ string &script = static_cast<TMultiFieldTypeConfig *>(fTypeConfigP)->fMergeScript;
+ if (script.empty()) {
+ aWinningItem.standardMergeWith(aLoosingItem,aChangedWinning,aChangedLoosing);
+ return;
+ }
+ // execute script to perform merge
+ // - set up helpers
+ fDsP = aDatastoreP;
+ fFirstItemP = &aWinningItem;
+ fSecondItemP = &aLoosingItem;
+ fChangedFirst = aChangedWinning;
+ fChangedSecond = aChangedLoosing;
+ fCurrentSyncOp = fDsP->fCurrentSyncOp;
+ TScriptContext::execute(
+ aDatastoreP->fReceivingTypeScriptContextP ?
+ aDatastoreP->fReceivingTypeScriptContextP :
+ aDatastoreP->fSendingTypeScriptContextP,
+ script,
+ &DataTypeFuncTable, // context function table
+ this, // context data (myself)
+ &aWinningItem, // winning item
+ true, // can be written if needed
+ &aLoosingItem, // loosing item
+ true, // can be written if needed
+ !PDEBUGTEST(DBG_CONFLICT) // script output only if conflict details enabled
+ );
+ // items are no longer available
+ fFirstItemP = NULL;
+ fSecondItemP = NULL;
+ // get change status back
+ aChangedWinning = fChangedFirst;
+ aChangedLoosing = fChangedSecond;
+ #endif
+} // TMultiFieldItemType::mergeItems
+
+#endif // server only
+
+
+// helper to create same-typed instance via base class
+TSyncItemType *TMultiFieldItemType::newCopyForSameType(
+ TSyncSession *aSessionP, // the session
+ TSyncDataStore *aDatastoreP // the datastore
+)
+{
+ // create new itemtype of appropriate derived class type that can handle
+ // this type
+ MP_RETURN_NEW(TMultiFieldItemType,DBG_OBJINST,"TMultiFieldItemType",TMultiFieldItemType(aSessionP,fTypeConfigP,getTypeName(),getTypeVers(),aDatastoreP,fFieldDefinitionsP));
+} // TMultiFieldItemType::newCopyForSameType
+
+
+/// @brief copy CTCap derived info from another SyncItemType
+/// @return false if item not compatible
+/// @note required to create remote type variants from ruleMatch type alternatives
+bool TMultiFieldItemType::copyCTCapInfoFrom(TSyncItemType &aSourceItem)
+{
+ // must be based on same type as myself and have the same fieldlist
+ if (!aSourceItem.isBasedOn(getTypeID()))
+ return false; // not compatible
+ TMultiFieldItemType *itemTypeP = static_cast<TMultiFieldItemType *>(&aSourceItem);
+ if (fFieldDefinitionsP!=itemTypeP->fFieldDefinitionsP)
+ return false; // not compatible
+ // both have the same fFieldDefinitionsP, so both option arrays have the same size
+ // - we can copy the options 1:1
+ for (sInt16 i=0; i<fFieldDefinitionsP->numFields(); i++) {
+ // by default, all fields in the list are available
+ fFieldOptionsP[i] = itemTypeP->fFieldOptionsP[i];
+ }
+ // other CTCap info is in the field options of MultiFieldItemType
+ return inherited::copyCTCapInfoFrom(aSourceItem);
+} // TMultiFieldItemType::copyCTCapInfoFrom
+
+
+
+// apply default limits to type (e.g. from hard-coded template in config)
+void TMultiFieldItemType::addDefaultTypeLimits(void)
+{
+ #ifdef HARDCODED_TYPE_SUPPORT
+ // get default type settings from field list template
+ if (fFieldDefinitionsP && fFieldDefinitionsP->fFieldListTemplateP) {
+ // copy options from field list template
+ for (sInt16 i=0; i<fFieldDefinitionsP->fFieldListTemplateP->numFields; i++) {
+ // get options
+ TFieldOptions *optP = getFieldOptions(i);
+ // transfer limits if not already defined
+ // - max field size
+ if (optP->maxsize==FIELD_OPT_MAXSIZE_NONE)
+ optP->maxsize=fFieldDefinitionsP->fFieldListTemplateP->fieldDefs[i].maxSize;
+ // - notruncate option
+ if (!optP->notruncate)
+ optP->notruncate=fFieldDefinitionsP->fFieldListTemplateP->fieldDefs[i].noTruncate;
+ }
+ }
+ #endif
+} // TMultiFieldItemType::addDefaultTypeLimits
+
+
+
+// access to fields by name (returns FID_NOT_SUPPORTED if field not found)
+sInt16 TMultiFieldItemType::getFieldIndex(const char *aFieldName)
+{
+ for (sInt16 i=0; i<fFieldDefinitionsP->numFields(); i++) {
+ if (strucmp(aFieldName,fFieldDefinitionsP->fFields[i].TCFG_CSTR(fieldname))==0)
+ return i; // found
+ }
+ return FID_NOT_SUPPORTED; // not found
+} // TMultiFieldItemType::getFieldIndex
+
+
+bool TMultiFieldItemType::isFieldIndexValid(sInt16 aFieldIndex)
+{
+ return (aFieldIndex>=0 && aFieldIndex<fFieldDefinitionsP->numFields());
+} // TMultiFieldItemType::isFieldIndexValid
+
+
+// access to field options (returns NULL if field not found)
+TFieldOptions *TMultiFieldItemType::getFieldOptions(sInt16 aFieldIndex)
+{
+ if (!isFieldIndexValid(aFieldIndex)) return NULL;
+ return &(fFieldOptionsP[aFieldIndex]);
+} // TMultiFieldItemType::getFieldOptions
+
+
+// test if item could contain cut-off data (e.g. because of field size restrictions)
+// compared to specified reference item
+// NOTE: if no reference item is specified, any occurrence of a
+// size-limited field will signal possible cutoff
+bool TMultiFieldItemType::mayContainCutOffData(TSyncItemType *aReferenceType)
+{
+ TMultiFieldItemType *refP=NULL;
+ if (aReferenceType->isBasedOn(ity_multifield))
+ refP=static_cast<TMultiFieldItemType *>(aReferenceType);
+ for (sInt16 i=0; i<fFieldDefinitionsP->numFields(); i++) {
+ sInt32 refsize,mysize;
+ mysize = getFieldOptions(i)->maxsize;
+ if (refP)
+ refsize = refP->getFieldOptions(i)->maxsize;
+ else
+ refsize = FIELD_OPT_MAXSIZE_NONE; // assume no limit
+ // now sort out cases: cutOff data can happen if
+ if (
+ // - this field is somehow limited && reference field is unlimited or limited, but unknown
+ (mysize!=FIELD_OPT_MAXSIZE_NONE && (refsize<=0)) ||
+ // - this field has smaller size than reference field
+ (mysize<refsize && mysize>0 && refsize>0)
+ ) {
+ // field could contain cut-off data (when conataining data from reference type)
+ return true;
+ }
+ }
+ // no field limits detected, no cutoff
+ return false;
+} // TMultiFieldItemType::mayContainCutOffData
+
+
+
+// fill in SyncML data (but leaves IDs empty)
+// Note: for MultiFieldItem, this is for post-processing data (FieldFillers)
+bool TMultiFieldItemType::internalFillInData(
+ TSyncItem *aSyncItemP, // SyncItem to be filled with data
+ SmlItemPtr_t aItemP, // SyncML toolkit item Data to be converted into SyncItem (may be NULL if no data, in case of Delete or Map)
+ TLocalEngineDS *aLocalDatastoreP, // local datastore
+ TStatusCommand &aStatusCmd // status command that might be modified in case of error
+)
+{
+ #if defined(SCRIPT_SUPPORT)
+ // check type
+ TMultiFieldItem *itemP;
+ GET_CASTED_PTR(itemP,TMultiFieldItem,aSyncItemP,DEBUGTEXT("TMultiFieldItemType::internalFillInData: incompatible item class","mfit2"));
+ #endif
+ #ifdef SCRIPT_SUPPORT
+ // post-process data: apply incoming script
+ // - init the needed member vars
+ fDsP = aLocalDatastoreP;
+ fFirstItemP = itemP;
+ fSecondItemP = NULL;
+ fEqMode = eqm_none;
+ fCurrentSyncOp = itemP->getSyncOp();
+ TScriptContext::execute(
+ aLocalDatastoreP->fReceivingTypeScriptContextP ?
+ aLocalDatastoreP->fReceivingTypeScriptContextP : // separate context for receiving, use it
+ aLocalDatastoreP->fSendingTypeScriptContextP, // send and receive use same context (sending one)
+ static_cast<TMultiFieldTypeConfig *>(fTypeConfigP)->fIncomingScript,
+ &DataTypeFuncTable,
+ this,
+ itemP,
+ true,
+ NULL,
+ false,
+ !PDEBUGTEST(DBG_PARSE)
+ );
+ fFirstItemP = NULL;
+ #endif
+ // - do NOT call ancestor (it's a dummy for types w/o implementation returning false)
+ // can't go wrong
+ return true;
+} // TMultiFieldItemType::internalFillInData
+
+
+// sets data and meta from SyncItem data, but leaves source & target untouched
+// Note: for MultiFieldItem, this is for pre-processing data (FieldFillers)
+bool TMultiFieldItemType::internalSetItemData(
+ TSyncItem *aSyncItemP, // the syncitem to be represented as SyncML
+ SmlItemPtr_t aItem, // item with NULL meta and NULL data
+ TLocalEngineDS *aLocalDatastoreP // local datastore
+)
+{
+ #if defined(SCRIPT_SUPPORT)
+ // check type
+ TMultiFieldItem *itemP;
+ GET_CASTED_PTR(itemP,TMultiFieldItem,aSyncItemP,DEBUGTEXT("TMultiFieldItemType::internalSetItemData: incompatible item class","mdit8"));
+ // do NOT call ancestor (it's a dummy for types w/o implementation returning false)
+ #endif
+ #ifdef SCRIPT_SUPPORT
+ // pre-process data: apply outgoing script
+ // - init the needed member vars
+ fDsP = aLocalDatastoreP;
+ fFirstItemP = itemP;
+ fSecondItemP = NULL;
+ fEqMode = eqm_none;
+ fCurrentSyncOp = itemP->getSyncOp();
+ return TScriptContext::execute(
+ aLocalDatastoreP->fSendingTypeScriptContextP,
+ static_cast<TMultiFieldTypeConfig *>(fTypeConfigP)->fOutgoingScript,
+ &DataTypeFuncTable,
+ this,
+ itemP,
+ true,
+ NULL,
+ false,
+ !PDEBUGTEST(DBG_GEN)
+ );
+ fFirstItemP = NULL;
+ #else
+ // can't go wrong
+ return true;
+ #endif
+} // TMultiFieldItemType::internalSetItemData
+
+
+} // namespace sysync
+
+/* end of TMultiFieldItemType implementation */
+
+// eof
diff --git a/src/sysync/multifielditemtype.h b/src/sysync/multifielditemtype.h
new file mode 100755
index 0000000..79158c2
--- /dev/null
+++ b/src/sysync/multifielditemtype.h
@@ -0,0 +1,219 @@
+/*
+ * File: MultiFieldItemType.h
+ *
+ * Author: Lukas Zeller (luz@synthesis.ch)
+ *
+ * TMultiFieldItemType
+ * Type consisting of multiple data fields (TItemField objects)
+ * To be used as base class for field formats like vCard,
+ * vCalendar etc.
+ *
+ * Copyright (c) 2001-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ * 2001-08-13 : luz : created
+ *
+ */
+
+#ifndef MultiFieldItemType_H
+#define MultiFieldItemType_H
+
+// includes
+#include "syncitemtype.h"
+#include "multifielditem.h"
+#include "scriptcontext.h"
+
+
+using namespace sysync;
+
+
+namespace sysync {
+
+// special field IDs
+#define FID_NOT_SUPPORTED -1 // no field-ID
+
+// special field offsets
+#define OFFS_NOSTORE -9999 // offset meaning "do not store"
+
+// special repeat counts
+#define REP_REWRITE 0 // unlimited repeating, but later occurrences overwrite previous ones
+#define REP_ARRAY 32767 // virtually unlimited repeating, should be used with array fields only
+
+// default profile mode (devivates might define their own)
+#define PROFILEMODE_DEFAULT 0 // default mode of the profile, usually MIME-DIR
+
+
+
+// type-instance specific options (there can be multiple
+// TSyncItemType instances with the same TFieldListConfig,
+// but different sets of field options, e.g. different
+// versions of the same content type where one has fewer
+// fields implemented, or server and client types with
+// differing sets of available fields)
+#define FIELD_OPT_MAXSIZE_UNKNOWN -1
+#define FIELD_OPT_MAXSIZE_NONE 0
+typedef struct {
+ bool available; // set if field is available in this type
+ sInt32 maxsize; // maximum field length, FIELD_OPT_MAXSIZE_NONE=none, FIELD_OPT_MAXSIZE_UNKNOWN=limited, but unknown
+ bool notruncate; // set if type doesn't want to get truncated values for this field from remote
+ sInt32 maxoccur; // maximum number of repetitions of properties based on this field
+} TFieldOptions;
+
+
+// multi-field based datatype
+class TMultiFieldTypeConfig : public TDataTypeConfig
+{
+ typedef TDataTypeConfig inherited;
+public:
+ TMultiFieldTypeConfig(const char *aElementName, TConfigElement *aParentElementP);
+ virtual ~TMultiFieldTypeConfig();
+ // properties
+ // - associated field list config (normally set from derived type)
+ TFieldListConfig *fFieldListP;
+ // - associated profile
+ TProfileConfig *fProfileConfigP;
+ // - mode for profile
+ sInt32 fProfileMode;
+ #ifdef SCRIPT_SUPPORT
+ // - scripts
+ string fInitScript; // executed once per usage by a datastore
+ string fIncomingScript; // script that is executed after receiving item
+ string fOutgoingScript; // script that is executed just before sending item
+ string fFilterInitScript; // script that is executed once per session when filter params are all available. Must return true if something to filter at all
+ string fPostFetchFilterScript; // script that is executed after item is fetched from DB. Must return true if item passes
+ string fCompareScript; // script that is executed to compare items
+ string fMergeScript; // script that is executed to merge items
+ string fProcessItemScript; // script that is executed to decide what to do with an incoming item
+ #endif
+ #ifdef HARDCODED_TYPE_SUPPORT
+ void setConfig(TFieldListConfig *aFieldList, const char *aTypeName, const char* aTypeVers);
+ #endif
+protected:
+ #ifdef CONFIGURABLE_TYPE_SUPPORT
+ // check config elements
+ virtual bool localStartElement(const char *aElementName, const char **aAttributes, sInt32 aLine);
+ virtual void localResolve(bool aLastPass);
+ #endif
+ virtual void clear();
+}; // TMultiFieldTypeConfig
+
+
+class TMultiFieldItemType: public TSyncItemType
+{
+ typedef TSyncItemType inherited;
+public:
+ TMultiFieldItemType(
+ TSyncSession *aSessionP,
+ TDataTypeConfig *aTypeConfigP,
+ const char *aCTType,
+ const char *aVerCT,
+ TSyncDataStore *aRelatedDatastoreP,
+ TFieldListConfig *aFieldDefinitions // field definition list
+ );
+ virtual ~TMultiFieldItemType();
+ // access to type
+ virtual uInt16 getTypeID(void) const { return ity_multifield; };
+ virtual bool isBasedOn(uInt16 aItemTypeID) const { return aItemTypeID==ity_multifield ? true : TSyncItemType::isBasedOn(aItemTypeID); };
+ // differentiation between implemented and just descriptive TSyncTypeItems
+ virtual bool isImplemented(void) { return true; }; // MultiFields are implemented
+ // compatibility (=assignment compatibility between items based on these types)
+ virtual bool isCompatibleWith(TSyncItemType *aReferenceType);
+ // test if item could contain cut-off data (e.g. because of field size restrictions)
+ // compared to specified reference item
+ virtual bool mayContainCutOffData(TSyncItemType *aReferenceType);
+ // helper to create same-typed instance via base class
+ // MUST BE IMPLEMENTED IN ALL non-virtual DERIVED CLASSES!
+ virtual TSyncItemType *newCopyForSameType(
+ TSyncSession *aSessionP, // the session
+ TSyncDataStore *aDatastoreP // the datastore
+ );
+ /// @brief copy CTCap derived info from another SyncItemType
+ virtual bool copyCTCapInfoFrom(TSyncItemType &aSourceItemP);
+ // apply default limits to type (e.g. from hard-coded template in config)
+ virtual void addDefaultTypeLimits(void);
+ // get config pointer
+ TMultiFieldTypeConfig *getMultifieldTypeConfig(void) { return static_cast<TMultiFieldTypeConfig *>(fTypeConfigP); };
+ // access field definitions
+ TFieldListConfig *getFieldDefinitions(void) { return fFieldDefinitionsP; };
+ // access to fields by name (returns -1 if field not found)
+ sInt16 getFieldIndex(const char *aFieldName);
+ bool isFieldIndexValid(sInt16 aFieldIndex);
+ // access to field options (returns NULL if field not found)
+ TFieldOptions *getFieldOptions(sInt16 aFieldIndex);
+ TFieldOptions *getFieldOptions(const char *aFieldName)
+ { return getFieldOptions(getFieldIndex(aFieldName)); };
+ virtual bool hasReceivedFieldOptions(void) { return false; }; // returns true if field options are based on remote devinf (and not just defaults)
+ // access to field definitions
+ TFieldDefinition *getFieldDefinition(sInt16 aFieldIndex)
+ { if (fFieldDefinitionsP) return &(fFieldDefinitionsP->fFields[aFieldIndex]); else return NULL; }
+ // Prepare datatype for use with a datastore. This might be implemented
+ // in derived classes to initialize the datastore's script context etc.
+ virtual void initDataTypeUse(TLocalEngineDS *aDatastoreP, bool aForSending, bool aForReceiving);
+ #ifdef OBJECT_FILTERING
+ // new style generic filtering
+ // - init new-style filtering, returns flag if needed at all
+ virtual void initPostFetchFiltering(bool &aNeeded, bool &aNeededForAll, TLocalEngineDS *aDatastoreP);
+ // - do the actual filtering
+ bool postFetchFiltering(TMultiFieldItem *aItemP, TLocalEngineDS *aDatastoreP);
+ // old style expression filtering
+ // - get field index of given filter expression identifier. Note that
+ // derived classes might first check for MIME property names etc.
+ virtual sInt16 getFilterIdentifierFieldIndex(const char *aIdentifier, uInt16 aIndex)
+ { return getFieldIndex(aIdentifier); }; // base class just handles field names
+ #endif
+ // comparing and merging
+ sInt16 compareItems(TMultiFieldItem &aFirstItem, TMultiFieldItem &aSecondItem, TEqualityMode aEqMode, bool aDebugShow, TLocalEngineDS *aDatastoreP);
+ void mergeItems(
+ TMultiFieldItem &aWinningItem,
+ TMultiFieldItem &aLoosingItem,
+ bool &aChangedWinning,
+ bool &aChangedLoosing,
+ TLocalEngineDS *aDatastoreP
+ );
+ // check item before processing it
+ bool checkItem(TMultiFieldItem &aItem, TLocalEngineDS *aDatastoreP);
+protected:
+ // Item data management
+ // - create new sync item of proper type and optimization for specified target
+ /* MultiFields are only a base class and lack implementation of
+ the following:
+ virtual TSyncItem *internalNewSyncItem(TSyncItemType *aTargetItemTypeP, TLocalEngineDS *aLocalDatastoreP);
+ */
+ // - fill in SyncML data (but leaves IDs empty)
+ // Note: for MultiFieldItem, this is for post-processing data (FieldFillers)
+ virtual bool internalFillInData(
+ TSyncItem *aSyncItemP, // SyncItem to be filled with data
+ SmlItemPtr_t aItemP, // SyncML toolkit item Data to be converted into SyncItem (may be NULL if no data, in case of Delete or Map)
+ TLocalEngineDS *aLocalDatastoreP, // local datastore
+ TStatusCommand &aStatusCmd // status command that might be modified in case of error
+ );
+ // - sets data and meta from SyncItem data, but leaves source & target untouched
+ // Note: for MultiFieldItem, this is for pre-processing data (FieldFillers)
+ virtual bool internalSetItemData(
+ TSyncItem *aSyncItemP, // the syncitem to be represented as SyncML
+ SmlItemPtr_t aItem, // item with NULL meta and NULL data
+ TLocalEngineDS *aLocalDatastoreP // local datastore
+ );
+ // field definition list pointer
+ TFieldListConfig *fFieldDefinitionsP;
+public:
+ // temporary variables needed by filter script context funcs
+ bool fNeedToFilterAll; // need to filter all records, even not-changed ones (dynamic syncset)
+ #ifdef SCRIPT_SUPPORT
+ TLocalEngineDS *fDsP; // helper for script context funcs
+ TMultiFieldItem *fFirstItemP; // helper for script context funcs
+ TMultiFieldItem *fSecondItemP; // helper for script context funcs
+ bool fChangedFirst; // helper for script context funcs
+ bool fChangedSecond; // helper for script context funcs
+ TEqualityMode fEqMode; // helper for script context funcs
+ TSyncOperation fCurrentSyncOp; // helper for script context funcs
+ #endif
+private:
+ // array of field options
+ TFieldOptions *fFieldOptionsP;
+}; // TMultiFieldItemType
+
+} // namespace sysync
+
+#endif // MultiFieldItemType_H
+
+// eof
diff --git a/src/sysync/remotedatastore.cpp b/src/sysync/remotedatastore.cpp
new file mode 100755
index 0000000..f5c7d91
--- /dev/null
+++ b/src/sysync/remotedatastore.cpp
@@ -0,0 +1,265 @@
+/*
+ * File: RemoteDataStore.cpp
+ *
+ * Author: Lukas Zeller (luz@synthesis.ch)
+ *
+ * TRemoteDataStore
+ * Abstraction of remote data store for SyncML Server
+ * Buffers and forwards incoming remote data store commands for
+ * processing by sync engine.
+ *
+ * Copyright (c) 2001-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ * 2001-06-12 : luz : created
+ *
+ */
+
+// includes
+#include "prefix_file.h"
+#include "sysync.h"
+#include "syncsession.h"
+#include "remotedatastore.h"
+
+#ifndef SYSYNC_CLIENT
+#include "syncserver.h"
+#endif
+
+using namespace sysync;
+
+
+/*
+ * Implementation of TRemoteDataStore
+ */
+
+void TRemoteDataStore::init(void)
+{
+ // nop so far
+} // TSyncDataStore::init
+
+
+void TRemoteDataStore::InternalResetDataStore(void)
+{
+ // for server, get default GUID size (in case remote devInf does not send one)
+ #ifndef SYSYNC_CLIENT
+ fMaxGUIDSize = static_cast<TServerConfig *>(getSession()->getSessionConfig())->fMaxGUIDSizeSent;
+ #endif
+} // TRemoteDataStore::InternalResetDataStore
+
+
+TRemoteDataStore::TRemoteDataStore(TSyncSession *aSessionP) :
+ TSyncDataStore(aSessionP)
+{
+ // nop so far
+} // TSyncDataStore::TSyncDataStore
+
+
+TRemoteDataStore::TRemoteDataStore(
+ TSyncSession *aSessionP, const char *aName,
+ uInt32 aCommonSyncCapMask
+) :
+ TSyncDataStore(aSessionP, aName, aCommonSyncCapMask)
+{
+ // assume full name same as real name, but could be updated at <alert> or <sync>
+ fFullName=aName;
+} // TSyncDataStore::TSyncDataStore
+
+
+
+TRemoteDataStore::~TRemoteDataStore()
+{
+ InternalResetDataStore();
+ // nop so far
+} // TRemoteDataStore::~TRemoteDataStore
+
+
+// - return pure relative (item) URI (removes absolute part or ./ prefix)
+const char *TRemoteDataStore::DatastoreRelativeURI(const char *aURI)
+{
+ return relativeURI(aURI,fSessionP->getRemoteURI());
+} // TRemoteDataStore::DatastoreRelativeURI
+
+
+// check if this remote datastore is accessible with given URI
+// NOTE: URI might include path elements or CGI params that are
+// access options to the database.
+// Remote datastores however only represent devinf, so
+// it counts as name match if start of specified URI matches
+// name and then continues with "/" or "?" (subpaths and CGI ignored)
+uInt16 TRemoteDataStore::isDatastore(const char *aDatastoreURI)
+{
+ // - make pure relative paths
+ const char *nam=DatastoreRelativeURI(fName.c_str());
+ size_t n=strlen(nam);
+ const char *uri=DatastoreRelativeURI(aDatastoreURI);
+ // - compare up to end of local name
+ if (strucmp(nam,uri,n,n)==0) {
+ // beginnig matches.
+ char c=uri[n]; // terminating char
+ // match if full match, or uri continues with '/' or '?'
+ // Note: return number of chars matched, to allow search for best match
+ return (c==0 || c=='/' || c=='?') ? n : 0;
+ }
+ else
+ return 0; // no match
+} // TRemoteDataStore::isDatastore
+
+
+
+// SYNC command bracket start (check credentials if needed)
+bool TRemoteDataStore::remoteProcessSyncCmd(
+ SmlSyncPtr_t aSyncP, // the Sync element
+ TStatusCommand &aStatusCommand, // status that might be modified
+ bool &aQueueForLater // will be set if command must be queued for later (re-)execution
+)
+{
+ // adjust name of datastore (may include subpath or CGI)
+ fFullName = smlSrcTargLocURIToCharP(aSyncP->source);
+ // read meta of Sync Command for remote datastore
+ // NOTE: this will overwrite Max values eventually read from DevInf
+ // as meta is more accurate (actual free bytes/ids, not total)
+ SmlMetInfMetInfPtr_t metaP = smlPCDataToMetInfP(aSyncP->meta);
+ if (metaP) {
+ if (metaP->mem) {
+ // - max free bytes
+ if (metaP->mem->free)
+ if (!StrToLongLong(smlPCDataToCharP(metaP->mem->free),fFreeMemory))
+ return false;
+ // - maximum ID
+ if (metaP->mem->freeid)
+ if (!StrToLongLong(smlPCDataToCharP(metaP->mem->freeid),fFreeID))
+ return false;
+ }
+ PDEBUGPRINTFX(DBG_REMOTEINFO,("Sync Meta provides memory constraints: FreeMem=" PRINTF_LLD ", FreeID=" PRINTF_LLD,PRINTF_LLD_ARG(fFreeMemory),PRINTF_LLD_ARG(fFreeID)));
+ }
+ return true;
+} // TRemoteDataStore::remoteProcessSyncCmd
+
+
+// SYNC command bracket end (but another might follow in next message)
+bool TRemoteDataStore::remoteProcessSyncCmdEnd(void)
+{
+ // %%% nop for now, %%% eventually obsolete
+ return true;
+} // TRemoteDataStore::remoteProcessSyncCmdEnd
+
+
+// end of all sync commands from client
+bool TRemoteDataStore::endOfClientSyncCmds(void)
+{
+ // %%% nop for now, %%% eventually obsolete
+ return true;
+} // TRemoteDataStore::endOfClientSyncCmds
+
+
+// set description structure of datastore
+bool TRemoteDataStore::setDatastoreDevInf(
+ SmlDevInfDatastorePtr_t aDataStoreDevInfP, // the datastore DevInf
+ TSyncItemTypePContainer &aLocalItemTypes, // list to look up local types (for reference)
+ TSyncItemTypePContainer &aNewItemTypes // list to add analyzed types if not already there
+)
+{
+ // get important info out of the structure
+ SYSYNC_TRY {
+ // - name (sourceRef)
+ fName=smlPCDataToCharP(aDataStoreDevInfP->sourceref);
+ #ifndef MINIMAL_CODE
+ // - displayname
+ fDisplayName=smlPCDataToCharP(aDataStoreDevInfP->displayname);
+ #endif
+ // - MaxGUIDsize
+ if (aDataStoreDevInfP->maxguidsize) {
+ if (!StrToLong(smlPCDataToCharP(aDataStoreDevInfP->maxguidsize),fMaxGUIDSize))
+ return false;
+ }
+ else {
+ PDEBUGPRINTFX(DBG_REMOTEINFO,("Datastore DevInf does not specify MaxGUIDSize -> using default"));
+ }
+ PDEBUGPRINTFX(DBG_REMOTEINFO+DBG_HOT,(
+ "Remote Datastore Name='%s', DisplayName='%s', MaxGUIDSize=%ld",
+ getName(),
+ getDisplayName(),
+ (long)fMaxGUIDSize
+ ));
+ PDEBUGPRINTFX(DBG_REMOTEINFO,(
+ "Preferred Rx='%s' version '%s', preferred Tx='%s' version '%s'",
+ smlPCDataToCharP(aDataStoreDevInfP->rxpref->cttype),
+ smlPCDataToCharP(aDataStoreDevInfP->rxpref->verct),
+ smlPCDataToCharP(aDataStoreDevInfP->txpref->cttype),
+ smlPCDataToCharP(aDataStoreDevInfP->txpref->verct)
+ ));
+ // - analyze DS 1.2 style datastore local CTCap
+ if (getSession()->getSyncMLVersion()>=syncml_vers_1_2) {
+ // analyze CTCaps (content type capabilities)
+ SmlDevInfCtcapListPtr_t ctlP = aDataStoreDevInfP->ctcap;
+ // loop through list
+ PDEBUGBLOCKDESC("RemoteTypes", "Analyzing remote types listed in datastore level CTCap");
+ if (getSession()->fIgnoreCTCap) {
+ // ignore CTCap
+ if (ctlP) {
+ PDEBUGPRINTFX(DBG_REMOTEINFO+DBG_HOT,("Remote rule prevents looking at CTCap"));
+ }
+ }
+ else {
+ while (ctlP) {
+ if (ctlP->data) {
+ // create appropriate remote data itemtypes
+ if (TSyncItemType::analyzeCTCapAndCreateItemTypes(
+ getSession(),
+ this, // this is the DS1.2 style where CTCap is related to this datastore
+ ctlP->data, // CTCap
+ aLocalItemTypes, // look up in local types for specialized classes
+ aNewItemTypes // add new item types here
+ )) {
+ // we have CTCap info of at least one remote type
+ getSession()->fRemoteDataTypesKnown=true;
+ }
+ else
+ return false;
+ }
+ // - go to next item
+ ctlP=ctlP->next;
+ } // while
+ }
+ PDEBUGENDBLOCK("RemoteTypes");
+ } // if >=DS1.2
+ // - analyze supported rx types
+ TSyncDataStore *relDsP = getSession()->getSyncMLVersion()>=syncml_vers_1_2 ? this : NULL;
+ fRxPrefItemTypeP=TSyncItemType::registerType(fSessionP,aDataStoreDevInfP->rxpref,aLocalItemTypes,aNewItemTypes,relDsP);
+ fRxItemTypes.push_back(fRxPrefItemTypeP);
+ registerTypes(fRxItemTypes,aDataStoreDevInfP->rx,aLocalItemTypes,aNewItemTypes,relDsP);
+ // - analyze supported tx types
+ fTxPrefItemTypeP=TSyncItemType::registerType(fSessionP,aDataStoreDevInfP->txpref,aLocalItemTypes,aNewItemTypes,relDsP);
+ fTxItemTypes.push_back(fTxPrefItemTypeP);
+ registerTypes(fTxItemTypes,aDataStoreDevInfP->tx,aLocalItemTypes,aNewItemTypes,relDsP);
+ // - Datastore Memory
+ if (aDataStoreDevInfP->dsmem) {
+ // datastore provides memory information
+ // Note: <Sync> command meta could override these with actual free info
+ // - max free bytes
+ if (aDataStoreDevInfP->dsmem->maxmem) {
+ if (!StrToLongLong(smlPCDataToCharP(aDataStoreDevInfP->dsmem->maxmem),fMaxMemory))
+ return false;
+ else
+ fFreeMemory=fMaxMemory; // default for free = max (sync meta might correct this)
+ }
+ // - maximum free ID
+ if (aDataStoreDevInfP->dsmem->maxid) {
+ if (!StrToLongLong(smlPCDataToCharP(aDataStoreDevInfP->dsmem->maxid),fMaxID))
+ return false;
+ else
+ fFreeID=fMaxID; // default for free = max (sync meta might correct this)
+ }
+ PDEBUGPRINTFX(DBG_REMOTEINFO,("DevInf provides DSMem: MaxMem=" PRINTF_LLD ", MaxID=" PRINTF_LLD,PRINTF_LLD_ARG(fFreeMemory),PRINTF_LLD_ARG(fMaxID)));
+ }
+ }
+ SYSYNC_CATCH (...)
+ DEBUGPRINTFX(DBG_ERROR,("******** setDatastoreDevInf caused exception"));
+ return false;
+ SYSYNC_ENDCATCH
+ return true;
+} // TRemoteDataStore::setDatastoreDevInf
+
+
+/* end of TRemoteDataStore implementation */
+
+// eof
diff --git a/src/sysync/remotedatastore.h b/src/sysync/remotedatastore.h
new file mode 100755
index 0000000..efd4887
--- /dev/null
+++ b/src/sysync/remotedatastore.h
@@ -0,0 +1,83 @@
+/*
+ * File: RemoteDataStore.h
+ *
+ * Author: Lukas Zeller (luz@synthesis.ch)
+ *
+ * TRemoteDataStore
+ * Abstraction of remote data store for SyncML Server
+ * Buffers and forwards incoming remote data store commands for
+ * processing by sync engine.
+ *
+ * Copyright (c) 2001-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ * 2001-06-12 : luz : created
+ *
+ */
+
+#ifndef RemoteDataStore_H
+#define RemoteDataStore_H
+
+// includes
+#include "sysync.h"
+#include "syncdatastore.h"
+
+
+namespace sysync {
+
+class TRemoteDataStore: public TSyncDataStore
+{
+ typedef TSyncDataStore inherited;
+ friend class TSyncServer;
+private:
+ void init(void); // internal init
+ void InternalResetDataStore(void); // reset for re-use without re-creation
+public:
+ TRemoteDataStore(TSyncSession *aSessionP);
+ TRemoteDataStore(TSyncSession *aSessionP, const char *aName, uInt32 aCommonSyncCapMask=0);
+ virtual void engResetDataStore(void) { InternalResetDataStore(); inherited::engResetDataStore(); };
+ virtual ~TRemoteDataStore();
+ // Naming
+ // check if this remote datastore is accessible with given URI
+ virtual uInt16 isDatastore(const char *aDatastoreURI);
+ const char *getFullName(void) { return fFullName.c_str(); }
+ void setFullName(const char *aFullName) { fFullName=aFullName; };
+ // - SYNC command bracket start (check credentials if needed)
+ bool remoteProcessSyncCmd(
+ SmlSyncPtr_t aSyncP, // the Sync element
+ TStatusCommand &aStatusCommand, // status that might be modified
+ bool &aQueueForLater // will be set if command must be queued for later (re-)execution
+ );
+ // %%% probably obsolete %%%% process client commands in server case
+ // - SYNC command bracket end (but another might follow in next message)
+ bool remoteProcessSyncCmdEnd(void);
+ // %%% probably obsolete %%%% process client commands in server case
+ // - end of all sync commands from client
+ bool endOfClientSyncCmds(void);
+ // description structure of datastore (NULL if not available)
+ // - set description structure, ownership is passed to TSyncDataStore
+ virtual bool setDatastoreDevInf(
+ SmlDevInfDatastorePtr_t aDataStoreDevInfP, // the datastore DevInf
+ TSyncItemTypePContainer &aLocalItemTypes, // list to look up local types (for reference)
+ TSyncItemTypePContainer &aNewItemTypes // list to add analyzed types if not already there
+ );
+ // helpers
+ // - return display name
+ #ifndef MINIMAL_CODE
+ virtual const char *getDisplayName(void) { return fDisplayName.c_str(); };
+ #endif
+ // - return pure relative (item) URI (removes absolute part or ./ prefix)
+ const char *DatastoreRelativeURI(const char *aURI);
+protected:
+ #ifndef MINIMAL_CODE
+ // Display name of Datastore
+ string fDisplayName;
+ #endif
+private:
+ string fFullName;
+}; // TRemoteDataStore
+
+} // namespace sysync
+
+#endif // RemoteDataStore_H
+
+// eof
diff --git a/src/sysync/rrules.cpp b/src/sysync/rrules.cpp
new file mode 100755
index 0000000..b0bc9d7
--- /dev/null
+++ b/src/sysync/rrules.cpp
@@ -0,0 +1,2364 @@
+/*
+ * File: rrules.cpp
+ *
+ * Author: Lukas Zeller (luz@synthesis.ch)
+ *
+ * Parser/Generator routines for vCalendar RRULES
+ *
+ * Copyright (c) 2001-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ * 2004-11-23 : luz : created from exctracts from vcalendaritemtype.cpp
+ *
+ */
+#include "prefix_file.h"
+
+#include <stdarg.h>
+
+#include "rrules.h"
+
+
+#if defined(SYSYNC_TOOL)
+ #include "syncappbase.h" // for CONSOLEPRINTF
+#endif
+
+using namespace sysync;
+
+namespace sysync {
+
+
+// Names of weekdays in RRULEs
+const char* const RRULE_weekdays[ DaysOfWeek ] = {
+ "SU",
+ "MO",
+ "TU",
+ "WE",
+ "TH",
+ "FR",
+ "SA"
+};
+
+
+// Support for SySync Diagnostic Tool
+#ifdef SYSYNC_TOOL
+
+// convert between RRULE and internal format
+int rruleConv(int argc, const char *argv[])
+{
+ if (argc<0) {
+ // help requested
+ CONSOLEPRINTF((" rrule <input mode> <output mode> <input> ..."));
+ CONSOLEPRINTF((" Convert between RRULE representations:"));
+ CONSOLEPRINTF((" mode \"i\" : internal format: startdate,freq,freqmod,interval,firstmask,lastmask,until"));
+ CONSOLEPRINTF((" (firstmask/lastmask in decimal or hex, until in ISO8601 format)"));
+ CONSOLEPRINTF((" mode \"t\" : output only, like \"i\" but also shows table of first 10 recurrence dates"));
+ CONSOLEPRINTF((" mode \"1\" : startdate,RRULE according to vCalendar 1.0 specs"));
+ CONSOLEPRINTF((" mode \"2\" : startdate,RRULE according to iCalendar (RFC2445) specs"));
+ return EXIT_SUCCESS;
+ }
+
+ // check for argument
+ if (argc!=3) {
+ CONSOLEPRINTF(("3 arguments required"));
+ return EXIT_FAILURE;
+ }
+ // mode
+ char inmode,outmode;
+ // internal representation
+ lineartime_t start;
+ char freq;
+ char freqmod;
+ sInt16 interval;
+ fieldinteger_t firstmask;
+ fieldinteger_t lastmask;
+ lineartime_t until;
+ // time zone stuff
+ timecontext_t rulecontext=TCTX_UNKNOWN;
+ timecontext_t untilcontext;
+ GZones zones;
+ // RRULE
+ char isodate[50];
+ char rulebuf[200];
+ string rrule,iso;
+ // get input mode
+ inmode=tolower(*(argv[0]));
+ if (inmode!='i' && inmode!='1' && inmode!='2') {
+ CONSOLEPRINTF(("invalid input mode, must be i, 1 or 2"));
+ return EXIT_FAILURE;
+ }
+ // get output mode
+ outmode=tolower(*(argv[1]));
+ if (outmode!='i' && outmode!='t' && outmode!='1' && outmode!='2') {
+ CONSOLEPRINTF(("invalid output mode, must be i, t, 1 or 2"));
+ return EXIT_FAILURE;
+ }
+ CONSOLEPRINTF((""));
+ // reset params
+ freq='0'; // frequency = none
+ freqmod=' '; // frequency modifier
+ interval=0; // interval
+ firstmask=0; // day mask counted from the first day of the period
+ lastmask=0; // day mask counted from the last day of the period
+ until= noLinearTime; // last day
+ // get input and convert to internal format (if needed)
+ if (inmode=='i') {
+ if (sscanf(argv[2],
+ "%[^,],%c,%c,%hd,%lld,%lld,%s",
+ rulebuf,
+ &freq,
+ &freqmod,
+ &interval,
+ &firstmask,
+ &lastmask,
+ isodate
+ )!=7) {
+ CONSOLEPRINTF(("invalid internal format input"));
+ return EXIT_FAILURE;
+ }
+ // - convert dates
+ ISO8601StrToTimestamp(rulebuf,start,rulecontext); // Start determines rule context
+ ISO8601StrToTimestamp(isodate,until,untilcontext); // until will be converted to same context
+ TzConvertTimestamp(until,untilcontext,rulecontext,&zones);
+ }
+ else if (inmode=='1') {
+ if (sscanf(argv[2],"%[^,],%[^\n]",isodate,rulebuf)!=2) {
+ CONSOLEPRINTF(("error, expected: startdate,RRULE"));
+ return EXIT_FAILURE;
+ }
+ ISO8601StrToTimestamp(isodate,start,rulecontext);
+ if (!RRULE1toInternal(
+ rulebuf, // RRULE string to be parsed
+ start, // reference date for parsing RRULE
+ rulecontext, // time context of RRULE
+ freq,
+ freqmod,
+ interval,
+ firstmask,
+ lastmask,
+ until,
+ untilcontext,
+ NULL
+ )) {
+ CONSOLEPRINTF(("invalid/unsupported RRULE type 1 specification"));
+ return EXIT_FAILURE;
+ }
+ }
+ else if (inmode=='2') {
+ if (sscanf(argv[2],"%[^,],%[^\n]",isodate,rulebuf)!=2) {
+ CONSOLEPRINTF(("error, expected: startdate,RRULE"));
+ return EXIT_FAILURE;
+ }
+ ISO8601StrToTimestamp(isodate,start,rulecontext);
+ if (!RRULE2toInternal(
+ rulebuf, // RRULE string to be parsed
+ start, // reference date for parsing RRULE
+ rulecontext, // time context of RRULE
+ freq,
+ freqmod,
+ interval,
+ firstmask,
+ lastmask,
+ until,
+ untilcontext,
+ NULL
+ )) {
+ CONSOLEPRINTF(("invalid/unsupported RRULE type 2 specification"));
+ return EXIT_FAILURE;
+ }
+ }
+ // convert to rrule (if needed) and output
+ TimestampToISO8601Str(iso,start,rulecontext,true);
+ if (outmode=='i' || outmode=='t') {
+ TimestampToISO8601Str(rrule,until,TCTX_UTC,true);
+ CONSOLEPRINTF((
+ "Internal format (start,freq,freqmod,interval,firstmask,lastmask,until:\n%s,%c,%c,%hd,0x%llX,0x%llX,%s",
+ iso.c_str(),
+ freq,
+ freqmod,
+ interval,
+ firstmask,
+ lastmask,
+ rrule.c_str()
+ ));
+ // extra info if we have 't' table mode
+ lineartime_t occurrence;
+ if (outmode=='t') {
+ sInt16 cnt;
+ for (cnt=1; cnt<=10; cnt++) {
+ // calculate date of cnt-th recurrence
+ if (!endDateFromCount(
+ occurrence,
+ start,
+ freq, freqmod,
+ interval,
+ firstmask, lastmask,
+ cnt, true, // counting occurrences
+ NULL
+ ))
+ break;
+ // see if limit already reached
+ if (occurrence>until) {
+ cnt=0;
+ break;
+ }
+ // show this recurrence
+ TimestampToISO8601Str(iso,occurrence,rulecontext,true);
+ CONSOLEPRINTF((
+ "%3d. occurrence at %s (%s)",
+ cnt,
+ iso.c_str(),
+ RRULE_weekdays[lineartime2weekday(occurrence)]
+ ));
+ }
+ // show end date if not all recurrences shown already
+ if (cnt>10) {
+ // calculate occurrence count
+ if (countFromEndDate(
+ cnt, true, // counting occurrences
+ start,
+ freq, freqmod,
+ interval,
+ firstmask, lastmask,
+ until,
+ NULL
+ )) {
+ if (cnt==0) {
+ CONSOLEPRINTF((
+ "last occurrence: <none> (repeating infinitely)",
+ cnt,
+ iso.c_str()
+ ));
+ }
+ else {
+ // convert back to date
+ if (endDateFromCount(
+ occurrence,
+ start,
+ freq, freqmod,
+ interval,
+ firstmask, lastmask,
+ cnt, true, // counting occurrences
+ NULL
+ )) {
+ TimestampToISO8601Str(iso,occurrence,rulecontext,true);
+ CONSOLEPRINTF((
+ "%3d./LAST occ. at %s (%s)",
+ cnt,
+ iso.c_str(),
+ RRULE_weekdays[lineartime2weekday(occurrence)]
+ ));
+ }
+ }
+ }
+ }
+ }
+ }
+ else if (outmode=='1') {
+ if (!internalToRRULE1(
+ rrule, // receives RRULE string
+ freq,
+ freqmod,
+ interval,
+ firstmask,
+ lastmask,
+ until,
+ rulecontext,
+ NULL
+ )) {
+ CONSOLEPRINTF(("Cannot show as RRULE type 1"));
+ return EXIT_FAILURE;
+ }
+ CONSOLEPRINTF(("RRULE type 1 format (start,rrule):\n%s,%s",iso.c_str(),rrule.c_str()));
+ }
+ else if (outmode=='2') {
+ if (!internalToRRULE2(
+ rrule, // receives RRULE string
+ freq,
+ freqmod,
+ interval,
+ firstmask,
+ lastmask,
+ until,
+ rulecontext,
+ NULL
+ )) {
+ CONSOLEPRINTF(("Cannot show as RRULE type 2"));
+ return EXIT_FAILURE;
+ }
+ CONSOLEPRINTF(("RRULE type 2 format (start,rrule):\n%s,%s",iso.c_str(),rrule.c_str()));
+ }
+ return EXIT_SUCCESS;
+} // rruleConv
+
+#endif // SYSYNC_TOOL
+
+
+
+/*
+ // sample of a field block:
+
+ Offset=0: <field name="RR_FREQ" type="string" compare="conflict"/>
+ - frequency codes:
+ 0 = none,
+ s = secondly,
+ m = minutely,
+ h = hourly,
+ D = daily,
+ W = weekly,
+ M = monthly,
+ Y = yearly
+ - frequency modifiers
+ space = none
+ s = by second list
+ m = by minute list
+ h = by hour list
+ W = by weekday list
+ D = by monthday list
+ Y = by yearday list
+ N = by weeknumber list
+ M = by monthlist
+ P = by setposlist
+
+ Offset=1: <field name="RR_INTERVAL" type="integer" compare="conflict"/>
+ - interval
+
+ Offset=2: <field name="RR_FMASK" type="integer" compare="conflict"/>
+ - bit-coded list of items according to frequency modifier,
+ relative to the beginning of the interval
+ - for seconds, minutes, hours: Bit0=first, Bit1=second....
+ - for weekly: Bit0=Sun, Bit6=Sat
+ - for monthly by weekday: Bit0=first Sun, Bit7=2nd Sun... Bit35=5th Sun
+ - for monthly by day: Bit0=1st, Bit1=2nd, Bit31=31st.
+ - for yearly by month: Bit0=jan, Bit1=feb,...
+ - for yearly by weeknumber: Bit0=weekno1,....
+
+ Offset=3: <field name="RR_LMASK" type="integer" compare="conflict"/>
+ - bit coded list of items according to frequency modifier,
+ relative to the end of the interval:
+ - Bit0=last, Bit1=second last, Bit2=third last....
+ - for weekly: Bit0=last Sun, Bit1=last Mon,... Bit7=second last Sun,...
+
+ Offset=4: <field name="RR_END" type="timestamp" compare="conflict"/>
+ - end date. This is calculated from DTSTART and count if incoming rule does
+ not specify an end date
+
+ Offset=5: <field name="DTSTART" type="timestamp" compare="always"/>
+ - for reference only
+
+*/
+
+// Converts internal recurrence into vCalendar 1.0 RRULE string
+bool internalToRRULE1(
+ string &aString, // receives RRULE string
+ char freq,
+ char freqmod,
+ sInt16 interval,
+ fieldinteger_t firstmask,
+ fieldinteger_t lastmask,
+ lineartime_t until,
+ timecontext_t untilcontext,
+ TDebugLogger *aLogP
+)
+{
+ // Now do the conversion
+ string s;
+ sInt16 i,j,k;
+ fieldinteger_t m;
+ bool repshown;
+ aString.erase();
+ // frequency and modifier
+ switch (freq) {
+ case '0' : return true; // no repetition
+ case 'D' : aString+='D'; break;
+ case 'W' : aString+='W'; break;
+ case 'M' :
+ aString+='M';
+ if (freqmod=='W') aString+='P'; // by-(weekday)-position
+ else if (freqmod=='D') aString+='D'; // by (month)day
+ else goto incompat;
+ break;
+ case 'Y' :
+ aString+='Y';
+ if (freqmod=='M') aString+='M'; // by month
+ // else if (freqmod=='Y') aString+='D'; // by yearday, %%%% not supported
+ else goto incompat;
+ break;
+ default :
+ goto incompat;
+ } // switch freq
+ // add interval
+ if (interval<1) goto incompat;
+ StringObjAppendPrintf(aString,"%hd",interval);
+ // add modifiers
+ switch (freqmod) {
+ case 'W' :
+ if (freq=='M') {
+ // Monthly by weekday
+ m = firstmask;
+ for (i=0; i<2; i++) {
+ // - from start and from end
+ for (j=0; j<WeeksOfMonth; j++) {
+ // - repetition
+ repshown=false;
+ // - append weekdays for this repetition
+ for (k=0; k<DaysOfWeek; k++) {
+ if (m & ((uInt64)1<<(k+(DaysOfWeek*j)))) {
+ // show repetion before first day item
+ if (!repshown) {
+ repshown=true;
+ StringObjAppendPrintf(aString," %hd",j+1);
+ // - show if relative to beginning or end of month
+ if (i>0) aString+='-'; else aString+='+';
+ }
+ // show day
+ aString+=' '; aString+=RRULE_weekdays[k];
+ }
+ }
+ }
+ // - switch to those that are relative to the end of the month
+ m = lastmask;
+ }
+ }
+ else {
+ // weekly by weekday
+ for (k=0; k<DaysOfWeek; k++) {
+ if (firstmask & ((uInt64)1<<k)) { aString+=' '; aString+=RRULE_weekdays[k]; }
+ }
+ }
+ break;
+ case 'D' :
+ case 'M' :
+ // monthly by day number or
+ // yearly by month number
+ m = firstmask;
+ for (i=0; i<2; i++) {
+ // - show day numbers
+ for (k=0; k<32; k++) {
+ if (m & ((uInt64)1<<k)) {
+ StringObjAppendPrintf(aString," %hd",k+1);
+ // show if relative to the end
+ if (i>0) aString+='-';
+ }
+ }
+ // - switch to those that are relative to the end of the month / year
+ m = lastmask;
+ }
+ break;
+ case ' ' :
+ // no modifiers, that's ok as well
+ break;
+ default :
+ goto incompat;
+ } // switch freqmod
+ // add end date (or #0 if no end date = endless)
+ if (until!=noLinearTime) {
+ // there is an end date, use it
+ aString+=' ';
+ TimestampToISO8601Str(s, until, untilcontext);
+ aString+=s;
+ }
+ else {
+ // there is no end date, repeat forever
+ aString+=" #0";
+ }
+ // genereated a RRULE
+ return true;
+incompat:
+ // incompatible, cannot be shown as vCal 1.0 RRULE
+ aString.erase();
+ return false; // no value generated
+} // internalToRRULE1
+
+
+// Converts internal recurrence into vCalendar 2.0 RRULE string
+bool internalToRRULE2(
+ string &aString, // receives RRULE string
+ char freq,
+ char freqmod,
+ sInt16 interval,
+ fieldinteger_t firstmask,
+ fieldinteger_t lastmask,
+ lineartime_t until,
+ timecontext_t untilcontext,
+ TDebugLogger *aLogP
+)
+{
+ LOGDEBUGPRINTFX(aLogP,DBG_GEN,("InternalToRRULE2() need to analyze freq %c and freqmod %c", freq, freqmod));
+
+ // Now do the conversion
+ string s;
+ sInt16 i,j,k;
+ fieldinteger_t m;
+ bool repshown;
+ cAppCharP sep;
+ aString.erase();
+ // frequency and modifier
+ switch (freq) {
+ case '0' : return true; // no repetition
+ case 'D' : aString+="FREQ=DAILY"; break;
+ case 'W' : aString+="FREQ=WEEKLY"; break;
+ case 'M' : aString+="FREQ=MONTHLY"; break;
+ case 'Y' : aString+="FREQ=YEARLY"; break;
+ default :
+ goto incompat;
+ } // switch freq
+ // add interval
+ if (interval<1) goto incompat;
+ StringObjAppendPrintf(aString,";INTERVAL=%hd",interval);
+ // add modifiers
+ switch (freqmod) {
+ case 'W' :
+ sep=";BYDAY=";
+ if (freq=='M') {
+ // Monthly by weekday
+ m = firstmask;
+ for (i=0; i<2; i++) {
+ // - from start and from end
+ for (j=0; j<WeeksOfMonth; j++) {
+ // - repetition
+ repshown=false;
+ // - append weekdays for this repetition
+ for (k=0; k<DaysOfWeek; k++) {
+ if (m & ((uInt64)1<<(k+(DaysOfWeek*j)))) {
+ // show repetion before first day item
+ aString+=sep;
+ sep=",";
+ if (!repshown) {
+ repshown=true;
+ if (i>0) aString+='-';
+ StringObjAppendPrintf(aString,"%hd",j+1);
+ // - show if relative to beginning or end of month
+ }
+ // show day
+ aString+=RRULE_weekdays[k];
+ }
+ }
+ }
+ // - switch to those that are relative to the end of the month
+ m = lastmask;
+ }
+ }
+ else {
+ // weekly by weekday
+ for (k=0; k<DaysOfWeek; k++) {
+ if (firstmask & ((uInt64)1<<k)) {
+ aString+=sep;
+ sep=",";
+ aString+=RRULE_weekdays[k];
+ }
+ }
+ }
+ break;
+ case 'D' :
+ // monthly by day number
+ appendMaskAsNumbers(";BYMONTHDAY=", aString, firstmask, lastmask);
+ break;
+ case 'Y' :
+ // yearday by day number
+ appendMaskAsNumbers(";BYYEARDAY=", aString, firstmask, lastmask);
+ break;
+ case 'N' :
+ // yearly by week number
+ appendMaskAsNumbers(";BYWEEKNO=", aString, firstmask, lastmask);
+ break;
+ case 'M' :
+ // yearly by month number
+ appendMaskAsNumbers(";BYMONTH=", aString, firstmask, lastmask);
+ break;
+ case ' ' :
+ // no modifiers, that's ok as well
+ break;
+ default :
+ goto incompat;
+ } // switch freqmod
+ // add end date (or #0 if no end date = endless)
+ if (until!=noLinearTime) {
+ // there is an end date, use it
+ aString+=";UNTIL=";
+ TimestampToISO8601Str(s, until, untilcontext);
+ aString+=s;
+ }
+
+ // do output
+ LOGDEBUGPRINTFX(aLogP,DBG_GEN,("generated rrule %s", aString.c_str()));
+
+ // genereated a RRULE
+ return true;
+incompat:
+ // incompatible, cannot be shown as vCal 1.0 RRULE
+ aString.erase();
+ return false; // no value generated
+} // internalToRRULE2
+
+
+// Converts vCalendar 1.0 RRULE string into internal recurrence representation
+bool RRULE1toInternal(
+ const char *aText, // RRULE string to be parsed
+ lineartime_t dtstart, // reference date for parsing RRULE
+ timecontext_t startcontext, // context of reference date
+ char &freq,
+ char &freqmod,
+ sInt16 &interval,
+ fieldinteger_t &firstmask,
+ fieldinteger_t &lastmask,
+ lineartime_t &until,
+ timecontext_t &untilcontext,
+ TDebugLogger *aLogP
+)
+{
+ string s;
+ const char *p;
+ sInt16 startwday;
+ sInt16 startyear,startmonth,startday;
+ bool fromstart;
+ sInt16 cnt;
+
+ // get elements of start point
+ startwday=lineartime2weekday(dtstart); // get starting weekday
+ lineartime2date(dtstart,&startyear,&startmonth,&startday); // year, month, day-in-month
+ // do the conversion here
+ p=aText;
+ char c,c2;
+ sInt16 j,k;
+ fieldinteger_t m;
+ // get frequency and modifier
+ do c=*p++; while(c==' '); // next non-space
+ if (c==0) goto incompat; // no frequency: incompatible -> parse error
+ switch (c) {
+ case 'D' : freq='D'; break;
+ case 'W' : freq='W'; freqmod='W'; break;
+ case 'M' :
+ freq='M';
+ // get modifier
+ c=*p++;
+ if (c=='P') freqmod='W'; // by weekday
+ else if (c=='D') freqmod='D'; // by monthday
+ else goto norep; // unknown modifier: cancel repetition (but no error)
+ break;
+ case 'Y' :
+ freq='Y';
+ // get modifier
+ c=*p++;
+ if (c=='M') freqmod='M'; // by monthlist
+ else if (c=='D') freqmod='Y'; // by day-of-year list %%% not supported
+ else goto norep; // unknown modifier: cancel repetition (but no error)
+ break;
+ default :
+ goto incompat;
+ }
+ // get interval
+ while (isdigit(*p)) { interval=interval*10+((*p++)-'0'); }
+ if (interval==0) goto incompat; // no interval is incompatible -> parse error
+ // get modifier(s) if any
+ switch (freq) {
+ case 'W' :
+ // weekly may or may not have modifiers
+ do {
+ do c=*p++; while(c==' '); // next non-space
+ if (isalpha(c)) {
+ c2=*p++;
+ if (!isalpha(c2)) goto incompat; // bad weekday syntax -> parse error
+ for (k=0;k<DaysOfWeek;k++) if (toupper(c)==RRULE_weekdays[k][0] && toupper(c2)==RRULE_weekdays[k][1]) break;
+ if (k==DaysOfWeek) goto incompat; // bad weekday -> parse error
+ firstmask = firstmask | ((uInt64)1<<k); // add weekday
+ }
+ else {
+ p--;
+ break;
+ }
+ } while(true);
+ // make sure start day is always in mask
+ if (dtstart) firstmask |= ((uInt64)1<<startwday);
+ break;
+ case 'M' :
+ fromstart=true; // default to specification relative to start
+ if (freqmod=='W') {
+ // monthly by weekday (position)
+ j=0; // default to first occurrence until otherwise set
+ do {
+ do c=*p++; while(c==' '); // next non-space
+ if (isdigit(c) && !isdigit(*p)) { // only single digit, otherwise it might be end date
+ // get occurrence specs
+ if (c>'5' || c<'1') goto incompat; // no more than 5 weeks in a month! -> parse error
+ j=c-'1'; // first occurrence=0
+ // check for '+' or '-'
+ if (*p=='+') p++; // simply skip
+ else if (!(fromstart=!(*p=='-'))) p++; // skip
+ }
+ else if (isalpha(c)) {
+ // get weekday spec
+ c2=*p++;
+ if (!isalpha(c2)) goto incompat; // bad weekday syntax -> parse error
+ for (k=0;k<DaysOfWeek;k++) if (toupper(c)==RRULE_weekdays[k][0] && toupper(c2)==RRULE_weekdays[k][1]) break;
+ if (k==DaysOfWeek) goto incompat; // bad weekday -> parse error
+ m=(uInt64)1<<(DaysOfWeek*j+k);
+ if (fromstart) firstmask |= m; else lastmask |= m; // add weekday rep
+ }
+ else {
+ // end of modifiers
+ p--;
+ break;
+ }
+ } while(true);
+ // check if we need defaults
+ if (!(firstmask | lastmask)) {
+ if (!dtstart) goto norep; // cannot set, no start date (but no error)
+ // determine the repetition in the month of this weekday
+ j=(startday-1) / DaysOfWeek;
+ firstmask = ((uInt64)1<<(DaysOfWeek*j+startwday)); // set nth repetition of current weekday
+ }
+ }
+ else {
+ // must be 'D' = monthly by day of month
+ do {
+ fromstart=true;
+ do c=*p++; while(c==' '); // next non-space
+ if (c=='L' && *p=='D') {
+ // special case: "LD" means last day of month
+ lastmask |= 1;
+ }
+ else if (isdigit(c) && (*p) && (!isdigit(*p) || !isdigit(*(p+1)))) { // more than two digits are end date
+ // get day number
+ k=c-'0';
+ if (isdigit(*p)) k=k*10+((*p++)-'0');
+ // check for '+' or '-'
+ if (*p=='+') p++; // simply skip plus
+ if (!(fromstart=!(*p=='-'))) p++; // skip minus and set fromstart to false
+ if (k==0) goto incompat; // 0 is not allowed -> parse error
+ // set mask
+ k--; // bits are 0 based
+ m = (uInt64)1<<k;
+ if (fromstart) firstmask |= m; else lastmask |= m; // add day
+ }
+ else {
+ p--;
+ break;
+ }
+ } while(true);
+ // check if we need defaults
+ if (!(firstmask | lastmask)) {
+ if (!dtstart) goto norep; // cannot set, no start date (no error)
+ // set current day number
+ firstmask = ((uInt64)1<<(startday-1)); // set bit of current day-in-month
+ }
+ } // by day of month
+ // monthly modifiers done
+ break;
+ case 'Y' :
+ if (freqmod=='M') {
+ // by monthlist
+ do {
+ do c=*p++; while (c==' '); // next non-space
+ if (isdigit(c) && (*p) && (!isdigit(*p) || !isdigit(*(p+1)))) { // more than two digits are end date
+ // get month number
+ k=c-'0';
+ if (isdigit(*p)) k=k*10+((*p++)-'0');
+ if (k==0) goto incompat; // 0 is not allowed -> parse error
+ // set mask
+ k--; // bits are 0 based
+ firstmask |= (uInt64)1<<k; // add month
+ }
+ else {
+ p--;
+ break;
+ }
+ } while(true);
+ // check if we need defaults
+ if (!firstmask) {
+ if (!dtstart) goto norep; // cannot set, no start date (but no error)
+ // set current month number
+ firstmask = ((uInt64)1<<(startmonth-1)); // set bit of current day-in-month
+ }
+ }
+ else {
+ // by day of year, only supported if no list follows
+ // - check if list follows
+ do c=*p++; while (c==' '); // next non-space
+ if (isdigit(c)) {
+ if (*p) {
+ if (!isdigit(*p)) goto norep; // single digit, is list, abort (but no error)
+ if (*(p+1)) {
+ if (!isdigit(*(p+1))) goto norep; // dual digit, is list, abort (but no error)
+ if (*(p+2)) {
+ if (!isdigit(*(p+2))) goto norep; // three digit, is list, abort (but no error)
+ }
+ }
+ }
+ }
+ // if we get so far, it's either end date (>= 4 digits) or # or end of string
+ --p; // we have fetched one to many
+ // - make best try to convert to by-monthlist
+ freqmod='M';
+ firstmask = ((uInt64)1<<(startmonth-1)); // set bit of current day-in-month
+ }
+ // yearly modifiers done
+ break;
+ } // switch for modifier reading
+ // get count or end date
+ do c=*p++; while (c==' '); // next non-space
+ cnt=2; // default to repeat once (=applied two times)
+ if (c==0) goto calcenddate;
+ else if (c=='#') {
+ // count specified
+ if (!StrToShort(p,cnt)) goto incompat; // bad count -> parse error
+ calcenddate:
+ if (!endDateFromCount(until,dtstart,freq,freqmod,interval,firstmask,lastmask,cnt,false,aLogP))
+ goto norep;
+ untilcontext = startcontext; // until is in same context as start
+ } // count specified
+ else {
+ // must be end date, or spec is bad
+ p--;
+ if (ISO8601StrToTimestamp(p, until, untilcontext)==0)
+ goto incompat; // bad end date -> parse error
+ } // end date specified
+ // parsed ok, now store it
+ goto store;
+norep:
+ // no repetition (but no parse error generated)
+ freq='0'; // frequency = none
+ freqmod=' '; // frequency modifier
+ interval=0; // interval
+ firstmask=0; // day mask counted from the first day of the period
+ lastmask=0; // day mask counted from the last day of the period
+ until= noLinearTime; // last day
+store:
+ return true; // ok
+incompat:
+ // incompatible, value cannot be parsed usefully
+ return false; // no value generated
+} // RRULE1toInternal
+
+
+// Check if a combined BYDAY and BYMONTH, where BYMONTH is one entry and equal to <startmonth>
+static bool DayMonthCombi( char freq, string byday,
+ string bymonth, sInt16 startmonth )
+{
+ return freq=='Y' && // it is yearly,
+ startmonth>0 && // start month of <dtstart> available ...
+ startmonth==atoi(bymonth.c_str()) && // ... and aequivalent to bymonth item
+ !byday.empty(); // and byday item available as well
+} // DayMonthCombi
+
+
+
+
+/// @brief calculate end date of RRULE when count is specified
+/// @return true if repeating, false if not repeating at all
+/// @note returns until=noLinearTime for endless repeat (count=0)
+bool endDateFromCount(
+ lineartime_t &until,
+ lineartime_t dtstart,
+ char freq, char freqmod,
+ sInt16 interval,
+ fieldinteger_t firstmask,fieldinteger_t lastmask,
+ sInt16 cnt, bool countsoccurrences,
+ TDebugLogger *aLogP
+)
+{
+ // count<=0 means endless
+ if (cnt<=0) {
+ until= noLinearTime; // forever, we don't need a start date for this
+ return true; // ok
+ }
+ // check no-rep cases
+ if (cnt<0) return false; // negative count, no repeat
+ if (dtstart==noLinearTime) return false; // no start date, cannot calc end date -> no rep (but no error)
+ if (interval<=0) return false; // interval=0 means no recurrence (but no error)
+ // default to dtstart
+ until = dtstart;
+ // calculate elements of start point
+ sInt16 startwday;
+ sInt16 startyear,startmonth,startday;
+ lineartime_t starttime;
+ starttime = lineartime2timeonly(dtstart); // start time of day
+ startwday = lineartime2weekday(dtstart); // get starting weekday
+ lineartime2date(dtstart,&startyear,&startmonth,&startday); // year, month, day-in-month
+ // calculate interval repetitions (which is what is needed for daily and RRULE v1 calculation)
+ sInt16 ivrep = (cnt-1)*interval;
+ // check if daily
+ if (freq == 'D') {
+ // Daily recurrence is same for occurrence and interval counts
+ LOGDEBUGPRINTFX(aLogP,DBG_PARSE+DBG_EXOTIC,("RRULE2toInternal() daily calc - same in all cases"));
+ until = dtstart+(ivrep*linearDateToTimeFactor);
+ return true;
+ }
+ else if (!countsoccurrences) {
+ // RRULE v1 interpretation of count (=number of repetitions of interval, not number of occurrences)
+ // - determine end of recurrence interval (which is usually NOT a occurrence precisely)
+ switch (freq)
+ {
+ case 'W':
+ LOGDEBUGPRINTFX(aLogP,DBG_PARSE+DBG_EXOTIC,("RRULE2toInternal() simple weekly calc"));
+ // v1 end date calc, we need to take into account eventual masks, so result must be end of interval, not just start date+interval
+ // weekly: end date is last day of target week
+ until=dtstart+((ivrep*DaysOfWeek-startwday+6)*linearDateToTimeFactor);
+ return true;
+ case 'M':
+ LOGDEBUGPRINTFX(aLogP,DBG_PARSE+DBG_EXOTIC,("RRULE2toInternal() simple monthly calc"));
+ startmonth--; // make 0 based
+ startmonth += ivrep+1; // add number of months plus one (as we want next month, and then go one day back to last day of month)
+ startyear += startmonth / 12; // update years
+ startmonth = startmonth % 12 + 1; // update month and make 1 based again
+ // - calculate last day in month of occurrence
+ until = (date2lineardate(startyear,startmonth,1)-1)*linearDateToTimeFactor+starttime;
+ return true;
+ case 'Y':
+ LOGDEBUGPRINTFX(aLogP,DBG_PARSE+DBG_EXOTIC,("RRULE2toInternal() simple yearly calc"));
+ // yearly: end date is end of end year
+ until = (date2lineardate(startyear+ivrep,12,31))*linearDateToTimeFactor+starttime;
+ return true;
+ }
+ }
+ else {
+ // v2 type counting, means that count specifies number of occurrences, not interval repetitions
+ // requires more elaborate expansion
+ // NOTE: this does not work without masks set, so we need to calculate the default masks if none are explicitly set
+ // - we need the number of days in the month in most cases
+ sInt16 lastday = getMonthDays(lineartime2dateonly(dtstart)); // number of days in this month
+ sInt16 newYearsPassed;
+ switch (freq)
+ {
+ case 'W':
+ LOGDEBUGPRINTFX(aLogP,DBG_PARSE+DBG_EXOTIC,("RRULE2toInternal() full expansion weekly calc"));
+ // - make sure we have a mask
+ if (firstmask==0 && lastmask==0)
+ firstmask = 1<<startwday; // set start day in mask
+ // now calculate
+ while (firstmask) {
+ if (firstmask & ((uInt64)1<<startwday)) {
+ // found an occurrence
+ cnt--; // count it
+ if (cnt<=0) break; // found all
+ }
+ // increment day
+ until+=linearDateToTimeFactor;
+ startwday++;
+ if (startwday>6) {
+ // new week starts
+ startwday=0;
+ // skip part of interval which has no occurrence
+ until+=(interval-1)*7*linearDateToTimeFactor;
+ }
+ }
+ return true;
+ case 'M':
+ if (freqmod=='W') {
+ // monthly by weekday
+ LOGDEBUGPRINTFX(aLogP,DBG_PARSE+DBG_EXOTIC,("RRULE2toInternal() full expansion of monthly by weekday"));
+ // - make sure we have a mask
+ if (firstmask==0 && lastmask==0)
+ firstmask = (uInt64)1<<(startwday+7*((startday-1)/7)); // set start day in mask
+ // - now calculate
+ while (firstmask || lastmask) {
+ // calculate which weeks we are in
+ sInt16 fwk=(startday-1) / 7; // start is nth week of the month
+ sInt16 lwk=(lastday-startday) / 7; // start is nth-last week of the month
+ if (
+ (firstmask & ((uInt64)1<<(startwday+7*fwk))) || // nth occurrence of weekday in month
+ (lastmask & ((uInt64)1<<(startwday+7*lwk))) // nth-last occurrence of weekday in month
+ ) {
+ // found an occurrence
+ cnt--; // count it
+ if (cnt<=0) break; // found all
+ }
+ // increment day
+ until+=linearDateToTimeFactor;
+ startday++; // next day in month
+ startwday++; if (startwday>6) startwday=0; // next day in the week
+ // check for new month
+ if (startday>lastday) {
+ // new month starts
+ sInt16 i=interval;
+ while (true) {
+ lastday = getMonthDays(lineartime2dateonly(until)); // number of days in next month
+ startday = 1; // start at 1st of month again
+ if (--i == 0) break; // done
+ // skip entire next month
+ until+=lastday*linearDateToTimeFactor; // advance by number of days in this month
+ }
+ // now recalculate weekday
+ startwday=lineartime2weekday(until); // calculation continues here
+ }
+ }
+ }
+ else {
+ // everything else, including no modifier, is treated as monthly by monthday
+ LOGDEBUGPRINTFX(aLogP,DBG_PARSE+DBG_EXOTIC,("RRULE2toInternal() full expansion of monthly by monthday"));
+ // - make sure we have a mask
+ if (firstmask==0 && lastmask==0)
+ firstmask = (uInt64)1<<(startday-1); // set start day in mask
+ // - now calculate
+ while (firstmask || lastmask) {
+ if (
+ (firstmask & ((uInt64)1<<(startday-1))) || // nth day in month
+ (lastmask & ((uInt64)1<<(lastday-startday))) // nth-last day in month
+ ) {
+ // found an occurrence
+ cnt--; // count it
+ if (cnt<=0) break; // found all
+ }
+ // increment day
+ until+=linearDateToTimeFactor;
+ startday++; // next day in month
+ if (startday>lastday) {
+ // new month starts
+ sInt16 i=interval;
+ while (true) {
+ lastday = getMonthDays(lineartime2dateonly(until)); // number of days in next month
+ startday = 1; // start at 1st of month again
+ if (--i == 0) break; // done
+ // skip entire next month
+ until+=lastday*linearDateToTimeFactor; // advance by number of days in this month
+ }
+ }
+ }
+ }
+ return true;
+ case 'Y':
+ if (freqmod=='M') {
+ // Yearly by month
+ LOGDEBUGPRINTFX(aLogP,DBG_PARSE+DBG_EXOTIC,("RRULE2toInternal() full expansion of yearly by month"));
+ // - make sure we have a mask
+ if (firstmask==0 && lastmask==0)
+ firstmask = (uInt64)1<<(startmonth-1); // set start month in mask
+ // - do entire calculation on 1st of month such that we can be sure that day exists (unlike a Feb 30th or April 31th)
+ until -= (startday-1)*linearDateToTimeFactor;
+ // - now calculate
+ newYearsPassed=0;
+ // occurrence interval can be at most 4 years in the future (safety abort)
+ while (newYearsPassed<=4) {
+ if (firstmask & ((uInt64)1<<(startmonth-1))) {
+ // eventually found an occurrence
+ // - is an occurrence only if that day exists in the month
+ if (startday<=lastday) {
+ cnt--; // count it
+ newYearsPassed = 0;
+ if (cnt<=0) break; // found all
+ }
+ }
+ // go to same day in next month (and skip months that don't have that day, like an 31st April or 30Feb
+ until+=lastday*linearDateToTimeFactor;
+ startmonth++;
+ if (startmonth>12) {
+ // new year starts
+ startmonth=1;
+ newYearsPassed++;
+ // skip additional years (in month steps)
+ for (sInt16 i=(interval-1)*12; i>0; i--) {
+ lastday = getMonthDays(lineartime2dateonly(until)); // number of days in next month
+ // skip month
+ until+=lastday*linearDateToTimeFactor; // advance by number of days in this month
+ }
+ }
+ // get size of next month to check
+ lastday = getMonthDays(lineartime2dateonly(until)); // number of days in next month
+ }
+ // move back to start day
+ until += (startday-1)*linearDateToTimeFactor;
+ }
+ else {
+ // everything else, including no modifier, is treated as yearly on the same date (multiple occurrences per year not supported)
+ LOGDEBUGPRINTFX(aLogP,DBG_PARSE+DBG_EXOTIC,("RRULE2toInternal() full expansion of yearly by yearday - NOT SUPPORTED with more than one day"));
+ until = (date2lineardate(startyear+ivrep,startmonth,startday))*linearDateToTimeFactor+starttime;
+ }
+ return true;
+ } // switch
+ }
+ // no recurrence
+ return false;
+} // endDateFromCount
+
+
+
+#ifdef DEBUG
+/*
+// %%%% hack debug
+#define SYNTHESIS_UNIT_TEST 1
+#define UNIT_TEST_TITLE(a)
+#define UNIT_TEST_CALL(x,p,t,v)
+*/
+#endif
+
+#ifdef SYNTHESIS_UNIT_TEST
+
+// helper to create lineartime parameters
+static lineartime_t t(char *aTime)
+{
+ if (!aTime || *aTime==0)
+ return noLinearTime;
+ timecontext_t tctx;
+ lineartime_t res;
+ ISO8601StrToTimestamp(aTime, res, tctx);
+ return res;
+}
+
+// helper to show lineartime results
+static char buf[100];
+char *s(lineartime_t aTime)
+{
+ if (aTime==noLinearTime)
+ return "<none>";
+ string str;
+ TimestampToISO8601Str(str, aTime, TCTX_UNKNOWN, true, false);
+ strcpy(buf,str.c_str());
+ return buf;
+}
+
+
+// RRULE expansion tests
+bool test_expand_rrule(void)
+{
+ bool ok=true;
+ lineartime_t lt;
+ TRRuleExpandStatus es;
+
+ {
+ UNIT_TEST_TITLE("Birthday 1");
+ UNIT_TEST_CALL(initRRuleExpansion(es,t("2008-03-31T00:00:00"),'Y','M',1,0x4,0x0,t("2009-03-31T00:00:00"),t("2009-04-01T00:00:00")),("-none-"),true,ok);
+ UNIT_TEST_CALL(lt = getNextOccurrence(es),("lt = %s",s(lt)),lt==t("2009-03-31T00:00:00"),ok);
+ UNIT_TEST_CALL(lt = getNextOccurrence(es),("lt = %s",s(lt)),lt==t(""),ok); // generated occurrences (checked 3)
+
+ UNIT_TEST_TITLE("Birthday 2");
+ UNIT_TEST_CALL(initRRuleExpansion(es,t("1979-03-06T00:00:00"),'Y','M',1,0x4,0x0,t("2009-03-31T00:00:00"),t("2009-04-01T00:00:00")),("-none-"),true,ok);
+ UNIT_TEST_CALL(lt = getNextOccurrence(es),("lt = %s",s(lt)),lt==t(""),ok); // no occurrences (checked 32)
+
+ UNIT_TEST_TITLE("Birthday 3");
+ UNIT_TEST_CALL(initRRuleExpansion(es,t("2006-03-13T00:00:00"),'Y','M',1,0x4,0x0,t("2009-02-23T00:00:00"),t("2009-03-31T00:00:00")),("-none-"),true,ok);
+ UNIT_TEST_CALL(lt = getNextOccurrence(es),("lt = %s",s(lt)),lt==t("2009-03-13T00:00:00"),ok);
+ UNIT_TEST_CALL(lt = getNextOccurrence(es),("lt = %s",s(lt)),lt==t(""),ok); // generated occurrences (checked 5)
+
+ UNIT_TEST_TITLE("Pay day");
+ UNIT_TEST_CALL(initRRuleExpansion(es,t("2006-06-20T00:00:00"),'M','D',1,0x80000,0x0,t("2009-02-23T00:00:00"),t("2009-03-31T00:00:00")),("-none-"),true,ok);
+ UNIT_TEST_CALL(lt = getNextOccurrence(es),("lt = %s",s(lt)),lt==t("2009-03-20T00:00:00"),ok);
+ UNIT_TEST_CALL(lt = getNextOccurrence(es),("lt = %s",s(lt)),lt==t(""),ok); // generated occurrences (checked 35)
+
+ UNIT_TEST_TITLE("St. Patrick's Day");
+ UNIT_TEST_CALL(initRRuleExpansion(es,t("2006-03-17T00:00:00"),'Y','M',1,0x4,0x0,t("2009-02-23T00:00:00"),t("2009-03-31T00:00:00")),("-none-"),true,ok);
+ UNIT_TEST_CALL(lt = getNextOccurrence(es),("lt = %s",s(lt)),lt==t("2009-03-17T00:00:00"),ok);
+ UNIT_TEST_CALL(lt = getNextOccurrence(es),("lt = %s",s(lt)),lt==t(""),ok); // generated occurrences (checked 5)
+
+ // Expanding 'Change fish tank filter':
+ UNIT_TEST_TITLE("Change fish tank filter");
+ UNIT_TEST_CALL(initRRuleExpansion(es,t("2007-10-21T00:00:00"),'W','W',5,0x1,0x0,t("2009-02-23T00:00:00"),t("2009-03-31T00:00:00")),("-none-"),true,ok);
+ UNIT_TEST_CALL(lt = getNextOccurrence(es),("lt = %s",s(lt)),lt==t("2009-03-29T00:00:00"),ok);
+ UNIT_TEST_CALL(lt = getNextOccurrence(es),("lt = %s",s(lt)),lt==t(""),ok); // generated occurrences (checked 17)
+
+ // Expanding 'Start of British Summer Time':
+ UNIT_TEST_TITLE("Start of British Summer Time");
+ UNIT_TEST_CALL(initRRuleExpansion(es,t("2003-03-30T00:00:00"),'M','W',12,0x0,0x1,t("2009-02-23T00:00:00"),t("2009-03-31T00:00:00")),("-none-"),true,ok);
+ UNIT_TEST_CALL(lt = getNextOccurrence(es),("lt = %s",s(lt)),lt==t("2009-03-29T00:00:00"),ok);
+ UNIT_TEST_CALL(lt = getNextOccurrence(es),("lt = %s",s(lt)),lt==t(""),ok); // generated occurrences (checked 8)
+
+ UNIT_TEST_TITLE("Birthday 4");
+ UNIT_TEST_CALL(initRRuleExpansion(es,t("1957-03-14T00:00:00"),'Y','M',1,0x4,0x0,t("2009-02-23T00:00:00"),t("2009-03-31T00:00:00")),("-none-"),true,ok);
+ UNIT_TEST_CALL(lt = getNextOccurrence(es),("lt = %s",s(lt)),lt==t("2009-03-14T00:00:00"),ok);
+ UNIT_TEST_CALL(lt = getNextOccurrence(es),("lt = %s",s(lt)),lt==t(""),ok); // generated occurrences (checked 54)
+
+ UNIT_TEST_TITLE("Buy Lottery tickets");
+ UNIT_TEST_CALL(initRRuleExpansion(es,t("2007-08-11T00:00:00"),'W','W',3,0x40,0x0,t("2009-02-23T00:00:00"),t("2009-03-31T00:00:00")),("-none-"),true,ok);
+ UNIT_TEST_CALL(lt = getNextOccurrence(es),("lt = %s",s(lt)),lt==t("2009-02-28T00:00:00"),ok);
+ UNIT_TEST_CALL(lt = getNextOccurrence(es),("lt = %s",s(lt)),lt==t("2009-03-21T00:00:00"),ok);
+ UNIT_TEST_CALL(lt = getNextOccurrence(es),("lt = %s",s(lt)),lt==t(""),ok); // generated occurrences (checked 30)
+
+ UNIT_TEST_TITLE("Recycling bin collection");
+ UNIT_TEST_CALL(initRRuleExpansion(es,t("2007-08-17T00:00:00"),'W','W',2,0x20,0x0,t("2009-02-23T00:00:00"),t("2009-03-31T00:00:00")),("-none-"),true,ok);
+ UNIT_TEST_CALL(lt = getNextOccurrence(es),("lt = %s",s(lt)),lt==t("2009-02-27T00:00:00"),ok);
+ UNIT_TEST_CALL(lt = getNextOccurrence(es),("lt = %s",s(lt)),lt==t("2009-03-13T00:00:00"),ok);
+ UNIT_TEST_CALL(lt = getNextOccurrence(es),("lt = %s",s(lt)),lt==t("2009-03-27T00:00:00"),ok);
+ UNIT_TEST_CALL(lt = getNextOccurrence(es),("lt = %s",s(lt)),lt==t(""),ok); // generated occurrences (checked 44)
+
+ UNIT_TEST_TITLE("test feb 29th");
+ UNIT_TEST_CALL(initRRuleExpansion(es,t("2008-02-29T14:00:00"),'Y','M',1,0x0,0x0,t("2009-04-01T00:00:00"),t("2019-04-04T00:00:00")),("-none-"),true,ok);
+ UNIT_TEST_CALL(lt = getNextOccurrence(es),("lt = %s",s(lt)),lt==t("2012-02-29T14:00:00"),ok);
+ UNIT_TEST_CALL(lt = getNextOccurrence(es),("lt = %s",s(lt)),lt==t("2016-02-29T14:00:00"),ok);
+ UNIT_TEST_CALL(lt = getNextOccurrence(es),("lt = %s",s(lt)),lt==t(""),ok);
+
+ UNIT_TEST_TITLE("February and March 29th every year");
+ UNIT_TEST_CALL(initRRuleExpansion(es,t("2008-02-29T14:00:00"),'Y','M',1,0x6,0x0,t("2009-04-01T00:00:00"),t("2016-04-04T00:00:00")),("-none-"),true,ok);
+ UNIT_TEST_CALL(lt = getNextOccurrence(es),("lt = %s",s(lt)),lt==t("2010-03-29T14:00:00"),ok);
+ UNIT_TEST_CALL(lt = getNextOccurrence(es),("lt = %s",s(lt)),lt==t("2011-03-29T14:00:00"),ok);
+ UNIT_TEST_CALL(lt = getNextOccurrence(es),("lt = %s",s(lt)),lt==t("2012-02-29T14:00:00"),ok); // leap year
+ UNIT_TEST_CALL(lt = getNextOccurrence(es),("lt = %s",s(lt)),lt==t("2012-03-29T14:00:00"),ok);
+ UNIT_TEST_CALL(lt = getNextOccurrence(es),("lt = %s",s(lt)),lt==t("2013-03-29T14:00:00"),ok);
+ UNIT_TEST_CALL(lt = getNextOccurrence(es),("lt = %s",s(lt)),lt==t("2014-03-29T14:00:00"),ok);
+ UNIT_TEST_CALL(lt = getNextOccurrence(es),("lt = %s",s(lt)),lt==t("2015-03-29T14:00:00"),ok);
+ UNIT_TEST_CALL(lt = getNextOccurrence(es),("lt = %s",s(lt)),lt==t("2016-02-29T14:00:00"),ok); // leap year
+ UNIT_TEST_CALL(lt = getNextOccurrence(es),("lt = %s",s(lt)),lt==t("2016-03-29T14:00:00"),ok);
+ UNIT_TEST_CALL(lt = getNextOccurrence(es),("lt = %s",s(lt)),lt==t(""),ok);
+
+ UNIT_TEST_TITLE("suddenly expanding from start problem");
+ UNIT_TEST_CALL(initRRuleExpansion(es,t("2007-08-17"),'W','W',2,0x20,0x0,t("2009-07-04"),t("2009-08-08")),("-none-"),true,ok);
+ UNIT_TEST_CALL(lt = getNextOccurrence(es),("lt = %s",s(lt)),lt==t("2009-07-17"),ok);
+ UNIT_TEST_CALL(lt = getNextOccurrence(es),("lt = %s",s(lt)),lt==t("2009-07-31"),ok);
+ UNIT_TEST_CALL(lt = getNextOccurrence(es),("lt = %s",s(lt)),lt==t(""),ok);
+
+ }
+ return ok;
+}
+
+#endif // SYNTHESIS_UNIT_TEST
+
+
+
+/// @brief initialize expansion of RRule
+void initRRuleExpansion(
+ TRRuleExpandStatus &es,
+ lineartime_t aDtstart,
+ char aFreq, char aFreqmod,
+ sInt16 aInterval,
+ fieldinteger_t aFirstmask, fieldinteger_t aLastmask,
+ lineartime_t aExpansionStart,
+ lineartime_t aExpansionEnd
+)
+{
+ // %%% hardcoded for now
+ es.weekstart = 0; // starts on sunday for now
+ // save recurrence parameters
+ es.freq = aFreq;
+ es.freqmod = aFreqmod;
+ es.interval = aInterval;
+ es.firstmask = aFirstmask;
+ es.lastmask = aLastmask;
+ // analyze bits
+ es.singleFMaskBit = -1;
+ if (es.lastmask==0 && es.firstmask) {
+ fieldinteger_t m = es.firstmask;
+ es.singleFMaskBit = 0;
+ while((m & 1)==0) { m>>=1; es.singleFMaskBit++; } // calculate bit number
+ if (m & ~1) es.singleFMaskBit = -1; // more bits to come - reset again
+ }
+ // init expansion parameters
+ // - no valid occurrence yet
+ es.started = false;
+ // - elements of start point
+ es.starttime = lineartime2timeonly(aDtstart); // start time of day
+ lineartime2date(aDtstart,&es.startyear,&es.startmonth,&es.startday); // year, month, day-in-month
+ // - cursor, initialized to start point
+ es.cursor = aDtstart;
+ es.cursorMLen = getMonthDays(lineartime2dateonly(es.cursor));
+ es.cursorWDay = lineartime2weekday(es.cursor); // get cursor weekday
+ // save expansion range
+ es.expansionEnd = aExpansionEnd; // can be noLinearTime if no end is set
+ es.expansionStartDayOffset = 0;
+ if (aExpansionStart!=noLinearTime) {
+ // specified start of expansion period, apply if later than beginning of recurrence
+ es.expansionStartDayOffset = (lineartime2dateonly(aExpansionStart)-lineartime2dateonly(es.cursor));
+ if (es.expansionStartDayOffset<0)
+ es.expansionStartDayOffset = 0;
+ }
+ #ifdef SYNTHESIS_UNIT_TEST
+ sInt16 y,m,d,h,mi,s,ms;
+ lineartime2date(aExpansionStart, &y, &m, &d);
+ lineartime2time(aExpansionStart, &h, &mi, &s, &ms);
+ printf(" aExpansionStart = %04hd-%02hd-%02hd %02hd:%02hd:%02hd - es.expansionStartDayOffset=%ld\n",y,m,d,h,mi,s,es.expansionStartDayOffset);
+ #endif
+}
+
+
+// negative aAtLeastDays allowed only with aModulo==1 (because of undefined % operator for negatives - altough it is normally truncate towards zero = remainder)
+static void adjustCursor(TRRuleExpandStatus &es, lineardate_t aAtLeastDays, sInt16 aModulo=1)
+{
+ lineardate_t days = 0;
+ days = aAtLeastDays; // default to simply add days
+ if (aModulo>1) {
+ // we need to make sure remainder is 0 and advance extra
+ lineardate_t extradays = aAtLeastDays % aModulo;
+ if (extradays!=0) {
+ days += aModulo-extradays;
+ }
+ }
+ // now days = number of days to advance
+ es.cursor += days*linearDateToTimeFactor;
+ // adjust weekday
+ es.cursorWDay += days % DaysPerWk;
+ if (es.cursorWDay<0) es.cursorWDay += DaysPerWk;
+ else if (es.cursorWDay>=DaysPerWk) es.cursorWDay -= DaysPerWk;
+ #ifdef SYNTHESIS_UNIT_TEST
+ sInt16 y,m,d,h,mi,s,ms;
+ lineartime2date(es.cursor, &y, &m, &d);
+ lineartime2time(es.cursor, &h, &mi, &s, &ms);
+ printf(" es.cursor = %04hd-%02hd-%02hd %02hd:%02hd:%02hd / Day=%hd, lt=%lld\n",y,m,d,h,mi,s,es.cursorWDay,es.cursor);
+ #endif
+}
+
+// simple and fast cursor increment
+static void incCursor(TRRuleExpandStatus &es)
+{
+ es.cursor += linearDateToTimeFactor;
+ es.cursorWDay += 1;
+ if (es.cursorWDay>=DaysPerWk) es.cursorWDay=0;
+}
+
+
+static bool expansionEnd(TRRuleExpandStatus &es)
+{
+ if (es.expansionEnd && es.cursor>=es.expansionEnd) {
+ // end of expansion, reset cursor
+ es.cursor = noLinearTime;
+ return true;
+ }
+ // not yet end of expansion
+ return false;
+}
+
+
+
+
+
+static sInt16 monthDiff(sInt16 y1, sInt16 m1, sInt16 y2, sInt16 m2)
+{
+ return (y1-y2)*12 + (m1-m2);
+}
+
+
+static void monthAdd(sInt16 &y, sInt16 &m, sInt16 a)
+{
+ m += a;
+ if (m>12) {
+ sInt16 r = (m-1)/12; // years plus
+ y += r; // add the years
+ m -= (r*12); // remove from the months
+ }
+}
+
+
+
+
+/// @brief get next occurrence
+/// @return noLinearTime if no next occurrence exists, lineartime of next occurrence otherwise
+lineartime_t getNextOccurrence(TRRuleExpandStatus &es)
+{
+ if (es.cursor==noLinearTime) return noLinearTime; // already done (or never started properly)
+ if (es.interval<=0) return noLinearTime; // interval=0 means no recurrence (but no error)
+ // if we are already started, we need to advance the cursor first, then check for match
+ bool advanceFirst = es.started;
+ // now calculate
+ if (es.freq=='D') {
+ // Daily: can be calculated directly
+ if (!es.started) {
+ // calculate first occurrence
+ adjustCursor(es, es.expansionStartDayOffset, es.interval); // move cursor to first day
+ }
+ else {
+ // calculate next occurrence
+ adjustCursor(es, es.interval);
+ }
+ } // D
+ else if (es.freq=='W') {
+ // Note: interval of weekly recurrences starts at sunday,
+ // so recurrence starting at a Thu, scheduled for every two weeks on Mo and Thu will have:
+ // 1st week: Thu, 2nd week: nothing, 3rd week: Mo,Thu, 4rd week: nothing, 5th week: Mo,Thu...
+ if (!es.started) {
+ // - make sure we have a mask
+ if (es.firstmask==0)
+ es.firstmask = 1<<es.cursorWDay; // set start day in mask
+ // - advance cursor to first day of expansion period
+ sInt16 woffs = es.cursorWDay-es.weekstart; // how many days back to next week start
+ if (woffs<0) woffs+=DaysPerWk; // we want to go BACK to next week start
+ adjustCursor(es, -woffs, 1); // back to previous start of week
+ adjustCursor(es, es.expansionStartDayOffset, es.interval*DaysPerWk); // advance by intervals
+ adjustCursor(es, woffs, 1); // back to start weekday
+ if (expansionEnd(es)) goto done; // could by beyond current expanding scope due to interval jump
+ }
+ // calculate first/next occurrence (if not first occurrence, do not check initially but advance first)
+ while(advanceFirst || !(
+ es.firstmask & ((uInt64)1<<es.cursorWDay)
+ )) {
+ advanceFirst = false; // next time we need to check
+ // - next day is next candidate
+ incCursor(es); // next day
+ if (es.cursorWDay==es.weekstart) {
+ // a new week has started, we need to skip non-active weeks first
+ adjustCursor(es, (es.interval-1)*DaysPerWk);
+ }
+ }
+ // found occurrence
+ } // W
+ else if (es.freq=='M') {
+ if (!es.started) {
+ // make sure we have a mask
+ if (es.firstmask==0 && es.lastmask==0) {
+ if (es.freqmod=='W') {
+ es.firstmask = (uInt64)1<<(es.cursorWDay+DaysPerWk*((es.startday-1)/DaysPerWk)); // set start weekday in mask
+ }
+ else {
+ es.firstmask = (uInt64)1<<(es.startday-1); // set start monthday in mask
+ }
+ }
+ // For all monthly repeats: find first month to apply freqmod to
+ if (es.expansionStartDayOffset) {
+ // does not necessarily start in cursor month
+ // - move cursor to where we want to begin earliest
+ adjustCursor(es, es.expansionStartDayOffset);
+ // - components of the expansion start
+ sInt16 y,m;
+ lineartime2date(es.cursor,&y,&m,&es.startday); // year, month, day-in-month
+ // - how many months since start
+ sInt16 numMonths = monthDiff(y,m,es.startyear,es.startmonth);
+ sInt16 monthMod = numMonths % es.interval;
+ if (monthMod>0) {
+ // need to go into subsequent month
+ numMonths += es.interval-monthMod; // months to next interval start
+ // entire month is after expansion start, so begin with 1st
+ es.startday = 1;
+ }
+ monthAdd(es.startyear,es.startmonth,numMonths);
+ // startYear/month/day now on first candidate
+ // - calculate linear start
+ es.cursor = date2lineartime(es.startyear, es.startmonth, es.startday);
+ if (expansionEnd(es)) goto done; // could by beyond current expanding scope due to interval jump
+ // - calculate last day in this month
+ es.cursorMLen = getMonthDays(lineartime2dateonly(es.cursor));
+ // - make sure cursor weekday is correct
+ es.cursorWDay = lineartime2weekday(es.cursor);
+ }
+ }
+ // apply freqmod
+ if (es.freqmod=='W') {
+ // monthly by weekday
+ // - calculate first/next occurrence (if not first occurrence, do not check initially but advance first)
+ while(true) {
+ // calculate which weeks we are in
+ sInt16 fwk=(es.startday-1) / DaysPerWk; // start is nth week of the month
+ sInt16 lwk=(es.cursorMLen-es.startday) / DaysPerWk; // start is nth-last week of the month
+ // check if we found an occurrence
+ if (!advanceFirst) {
+ if (
+ (es.firstmask & ((uInt64)1<<(es.cursorWDay+DaysPerWk*fwk))) || // nth occurrence of weekday in month
+ (es.lastmask & ((uInt64)1<<(es.cursorWDay+DaysPerWk*lwk))) // nth-last occurrence of weekday in month
+ )
+ break;
+ }
+ advanceFirst = false; // next time we need to check
+ // - next day-in-month is next candidate
+ es.cursorWDay++; if (es.cursorWDay>=DaysPerWk) es.cursorWDay=0; // next day in the week
+ es.startday++;
+ if (es.startday>es.cursorMLen) {
+ // end of month reached
+ // - goto next relevant month
+ monthAdd(es.startyear,es.startmonth,es.interval);
+ // - first day
+ es.startday=1;
+ // - advance cursor into new month
+ es.cursor = date2lineartime(es.startyear, es.startmonth, es.startday);
+ // - get length of this month
+ es.cursorMLen = getMonthDays(lineartime2dateonly(es.cursor));
+ // - make sure cursor weekday is correct
+ es.cursorWDay = lineartime2weekday(es.cursor);
+ }
+ }
+ // found occurrence, update cursor
+ es.cursor = date2lineartime(es.startyear, es.startmonth, es.startday)+es.starttime;
+ } // MW
+ else {
+ // monthly by monthday list
+ // - calculate first/next occurrence (if not first occurrence, do not check initially but advance first)
+ while(advanceFirst || !(
+ (es.firstmask & ((uInt64)1<<(es.startday-1))) || // nth day in month
+ (es.lastmask & ((uInt64)1<<(es.cursorMLen-es.startday))) // nth-last day in month
+ )) {
+ advanceFirst = false; // next time we need to check
+ if (es.singleFMaskBit>=0) {
+ // - next candidate is indicated by single mask bit
+ if (es.startday<es.singleFMaskBit+1)
+ es.startday = es.singleFMaskBit+1; // jump to start day
+ else
+ es.startday = es.cursorMLen+1; // jump to end of month
+ }
+ else {
+ // - next day-in-month is next candidate
+ es.startday++;
+ }
+ if (es.startday>es.cursorMLen) {
+ // end of month reached
+ // - goto next relevant month
+ monthAdd(es.startyear,es.startmonth,es.interval);
+ // - first day
+ es.startday=1;
+ // - advance cursor into new month
+ es.cursor = date2lineartime(es.startyear, es.startmonth, es.startday);
+ // - get length of this month
+ es.cursorMLen = getMonthDays(lineartime2dateonly(es.cursor));
+ }
+ }
+ // found occurrence, update cursor
+ es.cursor = date2lineartime(es.startyear, es.startmonth, es.startday)+es.starttime;
+ } // MD
+ } // M
+ else if (es.freq=='Y') {
+ if (!es.started) {
+ // Make sure we have a mask or cancel freqmod entirely
+ if (es.firstmask==0 && es.lastmask==0) {
+ // no mask at all, only startmonth counts anyway -> revert to simple direct calculation (cancel freqmod)
+ es.freqmod = 0;
+ }
+ else if (es.singleFMaskBit>=0 && es.singleFMaskBit+1==es.startmonth) {
+ // mask with single bit on start date month -> can revert to simple diect calculation (cancel freqmod)
+ es.freqmod = 0; // no month list parsing needed
+ }
+ // For all yearly repeats: find first year to apply freqmod to
+ if (es.expansionStartDayOffset) {
+ // does not necessarily start in cursor year
+ // - move cursor to where we want to begin earliest
+ adjustCursor(es, es.expansionStartDayOffset);
+ // - year of the expansion start
+ sInt16 y,m,d;
+ lineartime2date(es.cursor,&y,&m,&d); // year, month, day-in-month
+ // - make sure cursor can be a candidate (i.e. is not before start)
+ if (es.freqmod=='M') {
+ if (d>es.startday)
+ monthAdd(y, m, 1); // no occurrence possible in this month, next candidate is in next month
+ }
+ else {
+ if (m>es.startmonth || (m==es.startmonth && d>es.startday))
+ y++; // no occurrence possible this year, next candidate is in next year
+ }
+ // - make sure we are in a year at the beginning of the interval
+ sInt16 numYears = y-es.startyear;
+ sInt16 yearMod = numYears % es.interval;
+ if (yearMod>0) {
+ // need to go into subsequent year
+ numYears += es.interval-yearMod; // months to next interval start
+ m = 1; // start with Jan again (only used below when checking monthlist i.e. freqmod==M)
+ }
+ es.startyear += numYears;
+ if (es.freqmod=='M') {
+ // month-by-month checking
+ es.startmonth = m;
+ }
+ // startYear/month/day now on first candidate
+ // - calculate linear start
+ es.cursor = date2lineartime(es.startyear, es.startmonth, 1)+es.starttime;
+ es.cursorMLen = getMonthDays(lineartime2dateonly(es.cursor));
+ adjustCursor(es, es.startday-1);
+ if (expansionEnd(es)) goto done; // could by beyond current expanding scope due to interval jump
+ // - calculate last day in this month
+ }
+ }
+ // apply freqmod
+ if (es.freqmod=='M') {
+ // yearly by month list (and we really have a month list, not only a single month bit)
+ // - calculate first/next occurrence (if not first occurrence, do not check initially but advance first)
+ int yearsscanned = 0;
+ do {
+ while(advanceFirst || !(
+ (es.firstmask & ((uInt64)1<<(es.startmonth-1))) || // nth month
+ (es.lastmask & ((uInt64)1<<(12-es.startmonth))) // nth-last month
+ )) {
+ advanceFirst = false; // next time we need to check
+ // - next month is next candidate
+ es.startmonth++;
+ if (es.startmonth>12) {
+ // end of year reached
+ // - goto next relevant year
+ es.startyear += es.interval;
+ yearsscanned ++;
+ // - first month again
+ es.startmonth = 1;
+ }
+ }
+ // found possible occurrence (could be a day that does not exist in the month), update cursor
+ es.cursor = date2lineartime(es.startyear, es.startmonth, 1)+es.starttime;
+ es.cursorMLen = getMonthDays(lineartime2dateonly(es.cursor));
+ adjustCursor(es, es.startday-1);
+ // repeat until day actually exists
+ if (es.startday>es.cursorMLen) {
+ // disable checking for match again
+ advanceFirst = true;
+ }
+ } while(advanceFirst && yearsscanned<5);
+ // found occurrence
+ } // YM
+ else {
+ // yearly on same date
+ while (advanceFirst || es.startday>es.cursorMLen ) {
+ advanceFirst = false; // next time we need to check
+ // just calculate next candiate
+ es.startyear += es.interval;
+ // update cursor and calculate
+ es.cursor = date2lineartime(es.startyear, es.startmonth, 1)+es.starttime;
+ es.cursorMLen = getMonthDays(lineartime2dateonly(es.cursor));
+ adjustCursor(es, es.startday-1);
+ }
+ // found occurrence
+ } // Yx
+ } // Y
+ else {
+ // unknown = nonexpandable recurrence
+ // - return start date as first occurrence to make sure it does not get invisible
+ if (es.started) {
+ // subsequent occurrence requested: not available
+ es.cursor = noLinearTime;
+ }
+ }
+ // stop expansion if end of expansion period reached
+ expansionEnd(es);
+done:
+ // done
+ es.started = true;
+ return es.cursor;
+} // getNthNextOccurrence
+
+
+
+
+
+
+
+
+/// @brief calculate number of recurrences from specified end date of RRULE
+/// @return true if count could be calculated, false otherwise
+/// @param[in] dtstart, until : must be in the correct context to evaluate weekday rules
+/// @param[in] countsoccurrences : if true, counting occurrences (rather than intervals)
+/// @note returns until=noLinearTime for endless repeat (count=0)
+bool countFromEndDate(
+ sInt16 &cnt, bool countsoccurrences,
+ lineartime_t dtstart,
+ char freq, char freqmod,
+ sInt16 interval,
+ fieldinteger_t firstmask,fieldinteger_t lastmask,
+ lineartime_t until,
+ TDebugLogger *aLogP
+)
+{
+ if (dtstart==noLinearTime || interval<1) return false; // no start date or interval, cannot calc count
+ if (until==noLinearTime) {
+ // endless repeat
+ cnt=0;
+ return true;
+ }
+ // iterate until end date is reached
+ cnt=1;
+ lineartime_t occurrence=noLinearTime;
+ // break after 500 recurrences
+ while (cnt<500) {
+ if (!endDateFromCount(
+ occurrence,
+ dtstart,
+ freq,freqmod,
+ interval,
+ firstmask,lastmask,
+ cnt,
+ countsoccurrences,
+ aLogP
+ ))
+ return false; // error, cannot calc end date
+ if (occurrence>until) {
+ // no more occurrences
+ cnt--;
+ if (cnt==0) return false; // if not endless, but no occurrence found in range -> not repeating
+ return true; // return count
+ }
+ // next occurrence
+ cnt++;
+ }
+ // 500 and more recurrences count as endless
+ cnt=0;
+ return true;
+} // countFromEndDate
+
+
+
+// Converts vCalendar 2.0 RRULE string into internal recurrence representation
+bool RRULE2toInternal(
+ const char *aText, // RRULE string to be parsed
+ lineartime_t dtstart, // reference date for parsing RRULE
+ timecontext_t startcontext, // context of reference date
+ char &freq,
+ char &freqmod,
+ sInt16 &interval,
+ fieldinteger_t &firstmask,
+ fieldinteger_t &lastmask,
+ lineartime_t &until,
+ timecontext_t &untilcontext,
+ TDebugLogger *aLogP
+)
+{
+ #ifdef SYDEBUG
+ string abc;
+ LOGDEBUGPRINTFX(aLogP,DBG_PARSE+DBG_EXOTIC,("RRULE2toInternal() need to analyze %s", aText));
+ #endif
+
+ string temp;
+ const char *p;
+ char s [50];
+ sInt16 startwday;
+ sInt16 startyear,startmonth,startday;
+
+ string::size_type endIndex = 0;
+ string::size_type startIndex = 0;
+ uInt16 cnt = 0;
+ size_t pos;
+ bool calculateEndDate = false;
+ string key,value,byday,bymonthday,bymonth;
+
+ // get elements of start point
+ startwday=lineartime2weekday(dtstart); // get starting weekday
+ lineartime2date(dtstart,&startyear,&startmonth,&startday); // year, month, day-in-month
+ until= noLinearTime; // initialize end point, if not available
+
+ // do the conversion here
+ p=aText;
+ int start = 0;
+ // get freq
+ if (!getNextDirective(temp, p, start))
+ {
+ // failed
+ goto incompat;
+ }
+ // analyze freq
+ if (temp == "FREQ=YEARLY")
+ {
+ freq='Y';
+ }
+ else if (temp == "FREQ=MONTHLY")
+ {
+ freq='M';
+ }
+ else if (temp == "FREQ=WEEKLY")
+ {
+ freq='W';
+ }
+ else if (temp == "FREQ=DAILY")
+ {
+ freq='D';
+ }
+ else
+ {
+ goto incompat;
+ }
+
+ LOGDEBUGPRINTFX(aLogP,DBG_PARSE+DBG_EXOTIC,("RRULE2toInternal() found frequency %s which maps to freq %c", temp.c_str(), freq));
+
+ // init vars
+ cnt = 0;
+ // indicator that end date calc is required as last step
+ calculateEndDate = false;
+ // set interval to 1
+ interval = 1;
+ freqmod = ' ';
+ firstmask = 0;
+ lastmask = 0;
+ // get next directives
+ while (getNextDirective(temp, p, start))
+ {
+
+ LOGDEBUGPRINTFX(aLogP,DBG_PARSE+DBG_EXOTIC,("RRULE2toInternal() found next directive %s", temp.c_str()));
+
+ // split
+ pos = temp.find("=");
+ if (pos == string::npos || pos == 0 || pos == (temp.length() - 1))
+ {
+ goto incompat;
+ }
+ key = temp.substr(0, pos);
+ value = temp.substr(pos + 1, temp.length() - 1);
+
+ LOGDEBUGPRINTFX(aLogP,DBG_PARSE+DBG_EXOTIC,("RRULE2toInternal() extracted key/value %s/%s", key.c_str(), value.c_str()));
+
+ // check for until
+ if (key == "UNTIL")
+ {
+ if (ISO8601StrToTimestamp(value.c_str(), until, untilcontext)==0)
+ {
+ goto incompat;
+ }
+ calculateEndDate = false;
+ }
+ // check for count
+ else if (key == "COUNT")
+ {
+ // convert count
+ for (int i = 0, ii = value.length(); i < ii; ++i)
+ {
+ if (isdigit(value[i]))
+ {
+ cnt = cnt * 10 + ((value[i]) - '0');
+ }
+ }
+ // check if no count or one only
+ if (cnt <= 1)
+ {
+ goto norep;
+ }
+ // recalc enddate
+ calculateEndDate = true;
+ }
+ // check for interval
+ else if (key == "INTERVAL")
+ {
+ // convert interval
+ interval = 0;
+ for (int i = 0, ii = value.length(); i < ii; ++i)
+ {
+ if (isdigit(value[i]))
+ {
+ interval = interval * 10 + ((value[i]) - '0');
+ }
+ }
+ if (interval == 0)
+ {
+ goto incompat;
+ }
+ }
+ // just copy all supported byxxx rules into vars
+ else if (key == "BYDAY")
+ {
+ byday = value;
+ }
+ else if (key == "BYMONTHDAY")
+ {
+ bymonthday = value;
+ }
+ else if (key == "BYMONTH")
+ {
+ bymonth = value;
+ }
+ // ignore week start
+ else if (key == "WKST")
+ {
+ }
+ else
+ {
+ goto incompat;
+ }
+ } // while
+
+ #ifdef SYDEBUG
+ LOGDEBUGPRINTFX(aLogP,DBG_PARSE+DBG_EXOTIC,("RRULE2toInternal() dtstart weekday is %i", startwday));
+ LOGDEBUGPRINTFX(aLogP,DBG_PARSE+DBG_EXOTIC,("RRULE2toInternal() extracted interval %u", interval));
+ LOGDEBUGPRINTFX(aLogP,DBG_PARSE+DBG_EXOTIC,("RRULE2toInternal() extracted interval (2nd time) %u", interval));
+ LOGDEBUGPRINTFX(aLogP,DBG_PARSE+DBG_EXOTIC,("RRULE2toInternal() extracted count %u", cnt));
+ LOGDEBUGPRINTFX(aLogP,DBG_PARSE+DBG_EXOTIC,("RRULE2toInternal() end date calc required? %s", calculateEndDate ? "true" : "false"));
+ LOGDEBUGPRINTFX(aLogP,DBG_PARSE+DBG_EXOTIC,("RRULE2toInternal() extracted byday %s", byday.c_str()));
+ LOGDEBUGPRINTFX(aLogP,DBG_PARSE+DBG_EXOTIC,("RRULE2toInternal() extracted bymonthday %s", bymonthday.c_str()));
+ LOGDEBUGPRINTFX(aLogP,DBG_PARSE+DBG_EXOTIC,("RRULE2toInternal() extracted bymonth %s", bymonth.c_str()));
+ TimestampToISO8601Str(abc,until,startcontext,false,false);
+ LOGDEBUGPRINTFX(aLogP,DBG_PARSE+DBG_EXOTIC,("RRULE2toInternal() extracted until %s", abc.c_str()));
+ #endif
+
+ if (DayMonthCombi( freq, byday,bymonth,startmonth )) {
+ freq = 'M'; // a different model will be chosen for this case,
+ interval= 12*interval; // which is in fact aequivalent
+ bymonth = "";
+ } // if
+
+ // check freq
+ endIndex = 0;
+ startIndex = 0;
+ switch (freq)
+ {
+ case 'D' :
+ // make weekly if byday and nothing else is set
+ if (!(byday == "") && bymonth == "" && bymonthday == "")
+ {
+ freq='W';
+ freqmod='W';
+ // search separator ','
+ while ((endIndex = byday.find(",", startIndex)) != string::npos)
+ {
+ // set masks for the specific day
+ if (!setWeekday(byday, firstmask, lastmask, startIndex, endIndex, false))
+ {
+ goto incompat;
+ }
+ // set new start
+ startIndex = endIndex + 1;
+ if (startIndex >= byday.length())
+ {
+ break;
+ }
+ }
+ // check if anything is behind endindex
+ if (endIndex == string::npos && startIndex < byday.length() - 1)
+ {
+ endIndex = byday.length();
+ if (!setWeekday(byday, firstmask, lastmask, startIndex, endIndex, false))
+ {
+ goto incompat;
+ }
+ }
+ }
+ else
+ {
+ // we don't support byday and anything else at the same time
+ if (!byday.empty())
+ {
+ goto incompat;
+ }
+ // ok, no mod
+ freqmod = ' ';
+ }
+ break;
+ case 'W' :
+ // we don't support month or monthday within weekly
+ if (!bymonth.empty() || !bymonthday.empty())
+ {
+ goto incompat;
+ }
+ // set weekly and days
+ freqmod='W';
+ if (!byday.empty())
+ {
+ // search separator ','
+ while ((endIndex = byday.find(",", startIndex)) != string::npos)
+ {
+ // set masks for the specific day
+ if (!setWeekday(byday, firstmask, lastmask, startIndex, endIndex, false))
+ {
+ goto incompat;
+ }
+ // set new start
+ startIndex = endIndex + 1;
+ if (startIndex >= byday.length())
+ {
+ break;
+ }
+ }
+ // check if anything is behind endindex
+ if (endIndex == string::npos && startIndex < byday.length() - 1)
+ {
+ endIndex = byday.length();
+ if (!setWeekday(byday, firstmask, lastmask, startIndex, endIndex, false))
+ {
+ goto incompat;
+ }
+ }
+ }
+ break;
+ case 'M' :
+ // we don't support by month in monthly
+ if (!bymonth.empty())
+ {
+ goto incompat;
+ }
+ // we don't support byday and bymonthday at the same time
+ if (!byday.empty() && !bymonthday.empty())
+ {
+ goto incompat;
+ }
+ // check if bymonthday
+ if (!bymonthday.empty())
+ {
+ freqmod = 'D';
+ // search separator ','
+ while ((endIndex = bymonthday.find(",", startIndex)) != string::npos)
+ {
+ // set masks for the specific day
+ if (!setMonthDay(bymonthday, firstmask, lastmask, startIndex, endIndex))
+ {
+ goto incompat;
+ }
+ // set new start
+ startIndex = endIndex + 1;
+ if (startIndex >= bymonthday.length())
+ {
+ break;
+ }
+ }
+ // check if anything is behind endindex
+ if (endIndex == string::npos && startIndex < bymonthday.length() - 1)
+ {
+ endIndex = bymonthday.length();
+ if (!setMonthDay(bymonthday, firstmask, lastmask, startIndex, endIndex))
+ {
+ goto incompat;
+ }
+ }
+ }
+ // or if by weekday
+ else if (!byday.empty())
+ {
+ freqmod = 'W';
+ // search separator ','
+ while ((endIndex = byday.find(",", startIndex)) != string::npos)
+ {
+ // set masks for the specific day
+ if (!setWeekday(byday, firstmask, lastmask, startIndex, endIndex, true))
+ {
+ goto incompat;
+ }
+ // set new start
+ startIndex = endIndex + 1;
+ if (startIndex >= byday.length())
+ {
+ break;
+ }
+ }
+ // check if anything is behind endindex
+ if (endIndex == string::npos && startIndex < byday.length() - 1)
+ {
+ endIndex = byday.length();
+ if (!setWeekday(byday, firstmask, lastmask, startIndex, endIndex, true))
+ {
+ goto incompat;
+ }
+ }
+ }
+ else
+ {
+ // fine, no mod
+ freqmod = ' ';
+ }
+ break;
+ case 'Y' :
+ if (byday == "" ||
+ (byday.length() == 2 && byday[0] == RRULE_weekdays[startwday][0] &&
+ byday[1] == RRULE_weekdays[startwday][1]))
+ {
+ temp.erase();
+ sprintf(s, "%hd", startday);
+ temp.append(s);
+
+ LOGDEBUGPRINTFX(aLogP,DBG_PARSE+DBG_EXOTIC,("RRULE2toInternal() bymonthday checking %s against %s", temp.c_str(), bymonthday.c_str()));
+
+ if (bymonthday == "" || bymonthday == temp)
+ {
+ temp.erase();
+ sprintf(s, "%hd", startmonth);
+ temp.append(s);
+
+ LOGDEBUGPRINTFX(aLogP,DBG_PARSE+DBG_EXOTIC,("RRULE2toInternal() bymonth checking %s against %s", temp.c_str(), bymonth.c_str()));
+
+ if (bymonth == "" || bymonth == temp)
+ {
+ // this should usually be ' ' but the vcard conversion has a bug and requires 'M'
+ freqmod = 'M';
+ lastmask = 0;
+ firstmask = 0;
+ }
+ else
+ {
+ goto incompat;
+ }
+ }
+ else
+ {
+ goto incompat;
+ }
+ }
+ else
+ {
+ goto incompat;
+ }
+ break;
+ default :
+ LOGDEBUGPRINTFX(aLogP,DBG_PARSE+DBG_EXOTIC,("RRULE2toInternal() strange self-set freq %c, rule was %s", freq, aText));
+ break;
+ } // switch
+
+ // calc end date, assumption/make sure: cnt > 1
+ if (calculateEndDate)
+ {
+ LOGDEBUGPRINTFX(aLogP,DBG_PARSE+DBG_EXOTIC,("RRULE2toInternal() calculating end date now"));
+ if (!endDateFromCount(until,dtstart,freq,freqmod,interval,firstmask,lastmask,cnt,true,aLogP))
+ goto norep;
+ untilcontext = startcontext; // until is in same context as start
+ }
+ // parsed ok, now store it
+ goto store;
+norep:
+ // no repetition (but no parse error generated)
+ freq='0'; // frequency = none
+ freqmod=' '; // frequency modifier
+ interval=0; // interval
+ firstmask=0; // day mask counted from the first day of the period
+ lastmask=0; // day mask counted from the last day of the period
+ until= noLinearTime; // last day
+store:
+
+ #ifdef SYDEBUG
+ LOGDEBUGPRINTFX(aLogP,DBG_PARSE+DBG_EXOTIC,("RRULE2toInternal() leaving with freq %c, freqmod %c, interval %hd, firstmask %ld, lastmask %ld", freq, freqmod, interval, (long)firstmask, (long)lastmask));
+ TimestampToISO8601Str(abc,until,untilcontext,false,false);
+ LOGDEBUGPRINTFX(aLogP,DBG_PARSE+DBG_EXOTIC,("RRULE2toInternal() extracted until %s", abc.c_str()));
+ #endif
+
+ return true; // ok
+incompat:
+ // incompatible, value cannot be parsed usefully
+ return false; // no value generated
+} // RRULE2toInternal
+
+
+
+// appends the firstmask and lastmask as numbers to the string.
+// all values are increased by one prior to adding them to string.
+void appendMaskAsNumbers(
+ cAppCharP aPrefix, // if list is not empty, this will be appended in front of the list
+ string &aString, // receives list string
+ fieldinteger_t firstmask,
+ fieldinteger_t lastmask
+)
+{
+ fieldinteger_t m = firstmask;
+ int i,k;
+ for (i=0; i<2; i++) {
+ // - show day numbers
+ for (k=0; k<32; k++) {
+ if (m & ((uInt64)1<<k)) {
+ aString+=aPrefix;
+ aPrefix=","; // prefix for further elements is now colon.
+ if (i>0) aString+='-';
+ StringObjAppendPrintf(aString,"%hd",k+1);
+ }
+ }
+ // - switch to those that are relative to the end of the month / year
+ m = lastmask;
+ }
+} // appendMaskAsNumbers
+
+
+// returns the next directive for the ical format
+bool getNextDirective(
+ string &aString,
+ const char *aText,
+ int &aStart
+)
+{
+ // check
+ if (*aText == 0)
+ {
+ return false;
+ }
+
+ char c;
+ // check start
+ if (aStart > 0)
+ {
+ // skip to start
+ for (int i = 0; (i < aStart) && ((c = *aText) != 0); ++i) aText++;
+ }
+ else
+ {
+ // skip all spaces
+ while ((c = *aText) == ' ')
+ {
+ aText++;
+ }
+ }
+
+ // erase string, set counter
+ aString.erase();
+ uInt16 counter = 0;
+
+ // append text
+ c = *aText++; aStart++;
+ while (c != 0 && c != ';')
+ {
+ aString.append(1, c);
+ ++counter;
+ c = *aText++; aStart++;
+ }
+
+ // empty?
+ if (counter == 0)
+ {
+ return false;
+ }
+
+ // remove trailing whitespace
+ sInt16 i = counter - 1;
+ while (i >= 0 && aString[i] == ' ')
+ {
+ aString.erase(i, 1);
+ --i;
+ }
+
+ // check length
+ if (i == -1)
+ {
+ return false;
+ }
+
+ // fine
+ return true;
+} // getNextDirective
+
+// maps the byday rule into the masks
+bool setWeekday(
+ const string &byday,
+ fieldinteger_t &firstmask,
+ fieldinteger_t &lastmask,
+ string::size_type &startIndex,
+ const string::size_type &endIndex,
+ bool allowSpecial
+)
+{
+ // remove leading spaces
+ while (byday[startIndex] == ' ' && startIndex < endIndex)
+ {
+ ++startIndex;
+ }
+ // check if digit or sign
+ if (isdigit(byday[startIndex]) || byday[startIndex] == '+' || byday[startIndex] == '-')
+ {
+ // check if numbers before week days are allowed
+ if (!allowSpecial)
+ {
+ return false;
+ }
+ // special treatment
+ return setSpecialWeekday(byday, firstmask, lastmask, startIndex, endIndex);
+ }
+
+ // get index of weekday array
+ sInt16 weekdayIndex = getWeekdayIndex(byday, startIndex);
+ if (weekdayIndex == -1)
+ {
+ return false;
+ }
+ // put into mask
+
+ if (!allowSpecial) {
+ firstmask |= ((uInt64)1<<weekdayIndex);
+ return true;
+ }
+
+ for (int i= 0; i<WeeksOfMonth; i++) { // do it for all weeks of the month
+ firstmask |= ((uInt64)1<<weekdayIndex);
+ weekdayIndex+= DaysOfWeek;
+ } // for
+
+ return true;
+} // setWeekday
+
+// maps a special weekday (+/- int WEEKDAY) into the masks
+bool setSpecialWeekday(
+ const string &byday,
+ fieldinteger_t &firstmask,
+ fieldinteger_t &lastmask,
+ string::size_type &startIndex,
+ const string::size_type &endIndex
+)
+{
+ // indicator for negative values
+ bool isNegative;
+ // the value
+ sInt16 val = 0;
+
+ // check sign
+ if (byday[startIndex] == '-')
+ {
+ isNegative = true;
+ ++startIndex;
+ }
+ else if (byday[startIndex] == '+')
+ {
+ isNegative = false;
+ ++startIndex;
+ }
+ else
+ {
+ isNegative = false;
+ }
+ // convert to number
+ while (isdigit(byday[startIndex]))
+ {
+ val = val * 10 + ((byday[startIndex++]) - '0');
+ }
+ // remove leading spaces
+ while (byday[startIndex] == ' ' && startIndex < endIndex)
+ {
+ ++startIndex;
+ }
+
+ // make sure there's enough space
+ if (startIndex >= endIndex - 1)
+ {
+ return false;
+ }
+ // get index for weekday
+ sInt16 index = getWeekdayIndex(byday, startIndex);
+ if (index == -1)
+ {
+ return false;
+ }
+ // put into mask
+ if (isNegative)
+ {
+ lastmask |= (((uInt64)1<<index)<<((val - 1) * DaysOfWeek));
+ }
+ else
+ {
+ firstmask |= (((uInt64)1<<index)<<((val - 1) * DaysOfWeek));
+ }
+
+ return true;
+} // setSpecialWeekday
+
+// returns the index within the rrule weekday array for the next
+// two chars of the supplied string starting at startIndex
+sInt16 getWeekdayIndex(
+ const string &byday,
+ sInt16 startIndex
+)
+{
+ // search
+ for (sInt16 i = 0; i < DaysOfWeek; ++i)
+ {
+ if (RRULE_weekdays[i][0] == byday[startIndex] && RRULE_weekdays[i][1] == byday[startIndex + 1])
+ {
+ return i;
+ }
+ }
+ // not found
+ return -1;
+} // getWeekdayIndex
+
+// set a day in month
+bool setMonthDay(
+ const string &bymonthday,
+ fieldinteger_t &firstmask,
+ fieldinteger_t &lastmask,
+ string::size_type &startIndex,
+ const string::size_type &endIndex
+)
+{
+ // indicator for negative values
+ bool isNegative;
+ // the value
+ sInt16 val=0;
+
+ // check sign
+ if (bymonthday[startIndex] == '-')
+ {
+ isNegative = true;
+ ++startIndex;
+ }
+ else if (bymonthday[startIndex] == '+')
+ {
+ isNegative = false;
+ ++startIndex;
+ }
+ else
+ {
+ isNegative = false;
+ }
+ // convert to number
+ for (string::size_type i = startIndex; i < endIndex; ++i)
+ {
+ if (isdigit(bymonthday[i]))
+ {
+ val = val * 10 + ((bymonthday[i]) - '0');
+ }
+ else
+ {
+ return false;
+ }
+ }
+ // put into mask
+ if (isNegative)
+ {
+ lastmask |= ((uInt64)1<<(val - 1));
+ }
+ else
+ {
+ firstmask |= ((uInt64)1<<(val - 1));
+ }
+
+ return true;
+} // setMonthDay
+
+} // namespace sysync
+
+/* eof */
diff --git a/src/sysync/rrules.h b/src/sysync/rrules.h
new file mode 100755
index 0000000..9350c2b
--- /dev/null
+++ b/src/sysync/rrules.h
@@ -0,0 +1,247 @@
+/*
+ * File: rrules.h
+ *
+ * Author: Lukas Zeller (luz@synthesis.ch)
+ *
+ * Parser/Generator routines for vCalendar RRULES
+ *
+ * Copyright (c) 2001-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ * 2004-11-23 : luz : created from exctracts from vcalendaritemtype.cpp
+ *
+ */
+
+#ifndef RRULES_H
+#define RRULES_H
+
+// includes
+#ifndef FULLY_STANDALONE
+#include "itemfield.h"
+#endif
+#include "debuglogger.h"
+
+
+using namespace sysync;
+
+namespace sysync {
+
+#ifdef FULLY_STANDALONE
+typedef sInt64 fieldinteger_t;
+#endif
+
+#ifdef SYSYNC_TOOL
+
+// convert between RRULE and internal format
+int rruleConv(int argc, const char *argv[]);
+
+#endif
+
+#ifdef MOBOSX
+ // make this usable by the Todo+Cal app
+ #define PUBLIC_ENTRY extern "C"
+ #define PUBLIC_ENTRY_ATTR __attribute__((visibility("default")))
+#else
+ #define PUBLIC_ENTRY
+ #define PUBLIC_ENTRY_ATTR
+#endif
+
+
+
+const int DaysOfWeek = 7;
+const int WeeksOfMonth= 5;
+
+
+#ifdef SYNTHESIS_UNIT_TEST
+// RRULE expansion tests
+bool test_expand_rrule(void);
+#endif
+
+
+// Converts internal recurrence into vCalendar 1.0 RRULE string
+bool internalToRRULE1(
+ string &aString, // receives RRULE string
+ char freq,
+ char freqmod,
+ sInt16 interval,
+ fieldinteger_t firstmask,
+ fieldinteger_t lastmask,
+ lineartime_t until,
+ timecontext_t untilcontext,
+ TDebugLogger *aLogP
+);
+
+
+// Converts internal recurrence into vCalendar 2.0 RRULE string
+bool internalToRRULE2(
+ string &aString, // receives RRULE string
+ char freq,
+ char freqmod,
+ sInt16 interval,
+ fieldinteger_t firstmask,
+ fieldinteger_t lastmask,
+ lineartime_t until,
+ timecontext_t untilcontext,
+ TDebugLogger *aLogP
+);
+
+// Converts vCalendar 1.0 RRULE string into internal recurrence representation
+bool RRULE1toInternal(
+ const char *aText, // RRULE string to be parsed
+ lineartime_t dtstart, // reference date for parsing RRULE
+ timecontext_t startcontext, // context of reference date, until will be in same context
+ char &freq,
+ char &freqmod,
+ sInt16 &interval,
+ fieldinteger_t &firstmask,
+ fieldinteger_t &lastmask,
+ lineartime_t &until,
+ timecontext_t &untilcontext,
+ TDebugLogger *aLogP
+);
+
+// Converts vCalendar 2.0 RRULE string into internal recurrence representation
+bool RRULE2toInternal(
+ const char *aText, // RRULE string to be parsed
+ lineartime_t dtstart, // reference date for parsing RRULE
+ timecontext_t startcontext, // context of reference date, until will be in same context
+ char &freq,
+ char &freqmod,
+ sInt16 &interval,
+ fieldinteger_t &firstmask,
+ fieldinteger_t &lastmask,
+ lineartime_t &until,
+ timecontext_t &untilcontext,
+ TDebugLogger *aLogP
+);
+
+
+
+/// @brief recurrence rule expansion status record
+typedef struct {
+ // recurrence definition parameters
+ char freq;
+ char freqmod;
+ sInt16 interval;
+ fieldinteger_t firstmask;
+ fieldinteger_t lastmask;
+ sInt16 weekstart; // 0=Su, 1=Mo...
+ // bit anaalysis for speedup
+ sInt16 singleFMaskBit; // if >=0, only this bit number is set in firstmask and none in lastmask.
+ // start and end point of recurrence
+ lineardate_t expansionStartDayOffset;
+ lineartime_t expansionEnd;
+ // internal status
+ // - start point
+ sInt16 startyear,startmonth,startday;
+ lineartime_t starttime; // time part for re-adding after expansion
+ // - current point of expansion
+ lineartime_t cursor;
+ sInt16 cursorWDay; // weekday
+ sInt16 cursorMLen; // length of month
+ // - set if started (i.e. advanced cursor to first valid recurrence)
+ bool started;
+} TRRuleExpandStatus;
+
+
+/// @brief initialize expansion of RRule
+PUBLIC_ENTRY void initRRuleExpansion(
+ TRRuleExpandStatus &es,
+ lineartime_t aDtstart,
+ char aFreq, char aFreqmod,
+ sInt16 aInterval,
+ fieldinteger_t aFirstmask, fieldinteger_t aLastmask,
+ lineartime_t aExpansionStart=noLinearTime,
+ lineartime_t aExpansionEnd=noLinearTime
+);
+
+/// @brief get next occurrence
+/// @return noLinearTime if no next occurrence exists, lineartime of next occurrence otherwise
+PUBLIC_ENTRY lineartime_t getNextOccurrence(TRRuleExpandStatus &es);
+
+
+
+/// @brief calculate end date of RRULE when count is specified
+/// @return true if repeating, false if not repeating at all
+/// @note returns until=noLinearTime for endless repeat (count=0)
+/// @note this is made public as TodoZ iPhone app uses it for calendar recurrence expansion
+PUBLIC_ENTRY bool endDateFromCount(
+ lineartime_t &until,
+ lineartime_t dtstart,
+ char freq, char freqmod,
+ sInt16 interval,
+ fieldinteger_t firstmask,fieldinteger_t lastmask,
+ sInt16 cnt, bool countsoccurrences,
+ TDebugLogger *aLogP
+) PUBLIC_ENTRY_ATTR;
+
+
+/// @brief calculate number of recurrences from specified end date of RRULE
+/// @return true if count could be calculated, false otherwise
+/// @note returns until=noLinearTime for endless repeat (count=0)
+bool countFromEndDate(
+ sInt16 &cnt, bool countsoccurrences,
+ lineartime_t dtstart,
+ char freq, char freqmod,
+ sInt16 interval,
+ fieldinteger_t firstmask,fieldinteger_t lastmask,
+ lineartime_t until,
+ TDebugLogger *aLogP
+);
+
+
+// appends the firstmask and lastmask as numbers to the string.
+// all values are increased by one prior to adding them to string.
+void appendMaskAsNumbers(
+ cAppCharP aPrefix, // if list is not empty, this will be appended in front of the list
+ string &aString, // receives list string
+ fieldinteger_t firstmask,
+ fieldinteger_t lastmask
+);
+
+// returns the next directive for the ical format
+bool getNextDirective(
+ string &aString,
+ const char *aText,
+ int &aStart
+);
+
+// maps the byday rule into the masks
+bool setWeekday(
+ const string &byday,
+ fieldinteger_t &firstmask,
+ fieldinteger_t &lastmask,
+ string::size_type &startIndex,
+ const string::size_type &endIndex,
+ bool allowSpecial
+);
+
+// maps a special weekday (+/- int WEEKDAY) into the masks
+bool setSpecialWeekday(
+ const string &byday,
+ fieldinteger_t &firstmask,
+ fieldinteger_t &lastmask,
+ string::size_type &startIndex,
+ const string::size_type &endIndex
+);
+
+// returns the index within the rrule weekday array for the next
+// two chars of the supplied string starting at startIndex
+sInt16 getWeekdayIndex(
+ const string &byday,
+ sInt16 startIndex
+);
+
+// set a day in month
+bool setMonthDay(
+ const string &bymonthday,
+ fieldinteger_t &firstmask,
+ fieldinteger_t &lastmask,
+ string::size_type &startIndex,
+ const string::size_type &endIndex
+);
+
+} // namespace sysync
+
+#endif // RRULES_H
+
+/* eof */
diff --git a/src/sysync/san.cpp b/src/sysync/san.cpp
new file mode 100755
index 0000000..a2e083a
--- /dev/null
+++ b/src/sysync/san.cpp
@@ -0,0 +1,691 @@
+/*
+ * File: san.cpp
+ *
+ * Author: Beat Forster (bfo@synthesis.ch)
+ *
+ * Server Alerted Notification
+ * for OMA DS 1.2
+ *
+ * Copyright (c) 2005-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ *
+ */
+
+/*
+ 128 bit 64 bit + n char
+ +------------+--------------------+--------------------+
+ | | | |
+ | digest | notification-hdr | notification-body |
+ | | | |
+ +------------+--------------------+--------------------+
+ / \ \
+ / \ --------- \
+ / \ \ \
+ -------------------- ------ \ \
+ / \ \ \
+ / \ \ \
++---------+---------+-----------+--------+---------+--------+--------+ +----------+
+| version | ui-mode | initiator | future | session | server | server | | usage |
+| | | | use | id | ident | ident | | specific |
+| | | | | | length | | | |
++---------+---------+-----------+--------+---------+--------+--------+ +----------+
+ 10 bit 2 bit 1 bit 27 bit 16 bit 8 bit n char
+
+
+H = MD5 hashing function
+B64 = Base64 encoding
+digest= H(B64(H(server-identifier:password)):nonce:B64(H(notification)))
+
+
+
+ notification body:
+ +-------+--------+--------+--------+----------+
+ | num | future | sync 1 | sync N | vendor |
+ | syncs | use | | | specific |
+ | | | | | |
+ +-------+--------+--------+--------+----------+
+ 4 bit 4 bit / \ n char
+ / \
+ ---------- -------
+ / \
+ / \
++------+--------+---------+--------+--------+
+| sync | future | content | server | server |
+| type | use | type | URI | URI |
+| | | | length | |
++------+--------+---------+--------+--------+
+ 4 bit 4 bit 24 bit 8 bit n char
+
+*/
+
+
+#include "prefix_file.h"
+#include "san.h"
+#include "sysync_md5.h"
+#include "sysync_b64.h"
+
+#ifndef PUSHALERTER
+#include "sysync_utils.h"
+#endif
+
+
+const uInt16 SyncML12 = 12; // currently supported SyncML version
+
+#pragma options align= packed // allow direct mapping of the structure
+
+
+using namespace sysync;
+
+namespace sysync {
+
+// ---- structure definition ---------------------------------------------
+#define BpB 8 // bits per byte
+#define NBits 7 // bytes of the notification-hdr bits
+#define BBits 4 // bytes of the notification-body bits
+
+#define DB_Full 420 // memory full error
+#define DB_Error 510 // general DB error
+
+struct TPackage {
+ TDigestField digest;
+
+ uInt8 bitField[ NBits ]; // version, ui-mode, initiator, future use, sesion id
+ uInt8 serverID_len;
+}; // TPackage
+
+
+struct TBody {
+ uInt8 bitField[ BBits ]; // sync type, future use, content type
+ uInt8 serverURI_len;
+}; // TBody
+
+
+
+// ---- defined locally for the moment to avoid dependencies ----
+// - sysync_free replaced by free
+//
+// MD5 and B64 given string
+static void MD5B64_Local(const char *aString, sInt32 aLen, string &aMD5B64)
+{
+ // determine input length
+ if (aLen<=0) aLen=strlen(aString);
+ // calc MD5
+ md5::SYSYNC_MD5_CTX context;
+ uInt8 digest[16];
+ md5::Init (&context);
+ md5::Update (&context, (const uInt8 *)aString,aLen);
+ md5::Final (digest, &context);
+ // b64 encode the MD5 digest
+ uInt32 b64md5len;
+ char *b64md5=b64::encode(digest,16,&b64md5len);
+ // assign result
+ aMD5B64.assign(b64md5,b64md5len);
+ // done
+ /*sysync_*/free(b64md5); // return buffer allocated by b64::encode
+} // MD5B64_Local
+
+
+
+// ---- constructor/destructor -------------------------------------------
+SanPackage::SanPackage() // constructor
+{
+ fBody= NULL;
+ CreateEmptyNotificationBody();
+
+ memset( &fDigest, 0, DigestSize );
+ fProtocolVersion= 0;
+ fUI_Mode = UI_not_specified;
+ fInitiator = Initiator_Server;
+ fSessionID = 0;
+
+ fSan = NULL;
+ fSanSize = 0;
+} // constructor
+
+
+SanPackage::~SanPackage() // destructor
+{
+ ReleasePackage();
+ ReleaseNotificationBody();
+} // destructor
+
+
+
+// ---- digest creation --------------------------------------------------
+TDigestField SanPackage::H( string s )
+{
+ TDigestField df;
+
+ // calc MD5
+ md5::SYSYNC_MD5_CTX context;
+ md5::Init ( &context );
+ md5::Update ( &context, (const uInt8 *)s.c_str(), s.length() );
+ md5::Final( df.b, &context );
+ return df;
+} // DigestField
+
+
+string SanPackage::B64_H( string s1, string s2 )
+{
+ if (!s2.empty()) s1+= ":" + s2;
+ MD5B64_Local( s1.c_str(), s1.size(), s1 );
+ return s1;
+} // B64_H
+
+
+string SanPackage::B64_H_Notification( void* san, size_t sanSize )
+{
+ string s;
+ const char* v= (const char*)san + DigestSize;
+ size_t nfySize= sanSize - DigestSize;
+ MD5B64_Local( v, nfySize, s );
+ return s;
+} // B64_H
+
+
+
+/*! Prepare the SAN record */
+void SanPackage::PreparePackage( string aB64_H_srvID_pwd,
+ string aNonce,
+ uInt16 aProtocolVersion,
+ UI_Mode aUI_Mode,
+ Initiator aInitiator,
+ uInt16 aSessionID,
+ string aSrvID )
+{
+ fB64_H_srvID_pwd= aB64_H_srvID_pwd;
+ fNonce = aNonce;
+ fProtocolVersion= aProtocolVersion;
+ fUI_Mode = aUI_Mode;
+ fInitiator = aInitiator;
+ fSessionID = aSessionID;
+ fServerID = aSrvID;
+} // PreparePackage
+
+
+// if only hashes are available
+TSyError SanPackage::CreateDigest( const char* b64_h_serverID_password,
+ const char* aNonce,
+ void* san, size_t sanSize )
+{
+ string s= b64_h_serverID_password;
+ if ( s.empty()) {
+ for (int i= 0; i<DigestSize; i++) { // special case for empty digest
+ fDigest.b[ i ]= 0x00;
+ } // for
+ }
+ else { s+= ":";
+ s+= aNonce; s+= ":";
+ s+= B64_H_Notification( san,sanSize );
+ fDigest= H( s );
+ } // if
+
+ return LOCERR_OK;
+} // CreateDigest
+
+
+TSyError SanPackage::CreateDigest( const char* aServerID,
+ const char* aPassword,
+ const char* aNonce,
+ void* san, size_t sanSize )
+{
+ return CreateDigest( B64_H( aServerID,aPassword ).c_str(),
+ aNonce,
+ san,sanSize );
+} // CreateDigest
+
+
+
+bool SanPackage::DigestOK( void* san )
+{
+ TDigestField* sanD= (TDigestField*)san;
+
+ for (int i= 0; i<DigestSize; i++) {
+ if (fDigest.b[ i ]!=sanD->b[ i ]) return false;
+ } // for
+
+ return true;
+} // DigestOK
+
+
+
+// ---- bit operations ---------------------------------------------------
+void SanPackage::AddBits( void* ptr, int pos, int n, uInt32 value )
+{
+ byte* b= (byte*)ptr;
+ int lim= pos+n;
+ if (lim>BpB*NBits) return; // check if within the field
+ while (lim>BpB) { b++; lim-= BpB; }
+
+ int i;
+ for (i=0; i<n; i++) {
+ uInt8 db= 1<<(BpB-lim);
+
+ if ((value % 2)==1) *b|= db; // add bit
+ else *b&= ~db; // remove bit
+ value= value / 2;
+
+ lim--;
+ if (lim==0) { lim= BpB; b--; }
+ } // for
+} // AddBits
+
+
+uInt32 SanPackage::GetBits( void* ptr, int pos, int n )
+{
+ uInt32 value= 0;
+
+ byte* b= (byte*)ptr;
+ int lim= pos+n;
+ if (lim>BpB*NBits) return 0; // check if within the field
+ while (lim>BpB) { b++; lim-= BpB; }
+
+ int i;
+ for (i=0; i<n; i++) {
+ uInt8 db= 1<<(BpB-lim);
+
+ if ((*b & db)!=0) value|= (1<<n); // check bit and add it to <value>
+ value= value / 2;
+
+ lim--;
+ if (lim==0) { lim= BpB; b--; }
+ } // for
+
+ return value;
+} // GetBits
+
+
+
+// ---- notification body generation -------------------------------------
+void SanPackage::CreateEmptyNotificationBody()
+{
+ ReleaseNotificationBody();
+
+ fEmpty= 0x00; // no sync fields = ALL data stores concerned
+ fBody= &fEmpty;
+ fBodySize= sizeof(fEmpty);
+ fNSync= 0;
+} // CreateEmptyNotificationBody
+
+
+
+TSyError SanPackage::AddSync( int syncType, uInt32 contentType,
+ const char* serverURI )
+{
+ int len= strlen(serverURI);
+ int nLen= BBits + 1 + len; // length of the new part
+ int newLen= fBodySize + nLen; // total length of the new block
+
+ void* fb= malloc( newLen ); // allocate it
+ memcpy( fb, fBody,fBodySize ); // copy existing structure to beginning
+
+ byte* b = (byte*)fb;
+ b+= fBodySize; // get a pointer to the new part
+
+ ReleaseNotificationBody(); // release the old structure
+ fNSync++; // adapt number of available parts
+ fBody = fb; // now the new bigger structure is assigned
+ fBodySize= newLen;
+
+ // fill in new counter value
+ AddBits( fBody, 0, 4, fNSync ); // number of sync datastores
+ AddBits( fBody, 4, 4, 0 ); // future use
+
+ // fill in contents of the nth structure
+ TBody* tb= (TBody*)b;
+ AddBits( tb->bitField, 0, 4, syncType-200 ); // the sync type 206..210
+ AddBits( tb->bitField, 4, 4, 0 ); // future use
+ AddBits( tb->bitField, 8,24, contentType ); // the content tye
+ tb->serverURI_len= len;
+
+ byte* pp= (byte*)(tb+1); // = right after TBody
+ memcpy( (void*) pp, (void*)serverURI, len );
+ return LOCERR_OK;
+} // AddSync
+
+
+
+void SanPackage::ReleaseNotificationBody()
+{
+ if (fBody!=NULL &&
+ fBody!=&fEmpty) { free( fBody ); fBody= NULL; }
+} // ReleaseNotificationBody
+
+
+// not available for pushalerter tool
+#ifndef PUSHALERTER
+// general callback entry for all others
+static Ret_t univ( ... )
+{
+//printf( "callback\n" );
+ return 0;
+} // univ
+
+
+static Ret_t startM( InstanceID_t id, VoidPtr_t userData, SmlSyncHdrPtr_t pContent )
+{
+ cAppCharP Sy= "SyncML/";
+ size_t n = strlen(Sy);
+
+ SanPackage* a= (SanPackage*)userData;
+ string mup = "";
+ string nonce= "";
+
+ uInt16 major=0,minor=0;
+
+ cAppCharP verP = smlPCDataToCharP(pContent->proto);
+ if (strucmp(verP,Sy,n)==0) {
+ n+=StrToUShort(verP+n,major);
+ if (verP[n]=='.') {
+ n++;
+ StrToUShort(verP+n,minor);
+ }
+ }
+
+ sInt32 sessionID;
+ smlPCDataToLong( pContent->sessionID, sessionID );
+
+ string srvID= smlSrcTargLocURIToCharP(pContent->source);
+
+ a->PreparePackage( mup, nonce, 10*major+minor, UI_not_specified, Initiator_Server, sessionID, srvID );
+ a->CreateEmptyNotificationBody();
+ return 0;
+} // startM
+
+
+static Ret_t alertM( InstanceID_t id, VoidPtr_t userData, SmlAlertPtr_t pContent )
+{
+ SanPackage* a= (SanPackage*)userData;
+
+ sInt32 syncType;
+ smlPCDataToLong( pContent->data, syncType );
+ uInt32 contentType= 0; // always 0
+
+ SmlItemListPtr_t el= pContent->itemList;
+
+ while (true) { // can be a chained list of elements
+ string locURI= smlSrcTargLocURIToCharP(el->item->source);
+ a->AddSync( syncType, contentType, locURI.c_str() ); // for each element add one
+
+ if (el->next==NULL) break;
+ el= el->next;
+ } // while
+
+//printf( "alert\n" );
+ return 0;
+} // alert
+
+
+static Ret_t endM( InstanceID_t id, VoidPtr_t userData, Boolean_t final )
+{
+//printf( "end\n" );
+ return 0;
+} // endM
+
+
+// Callback record, most of the routines are not used
+static const SmlCallbacks_t mySmlCallbacks = {
+ /* message callbacks */
+ startM, // smlStartMessageCallback,
+ endM, // smlEndMessageCallback,
+ /* grouping commands */
+ (smlStartSyncFunc) univ, // smlStartSyncCallback,
+ (smlEndSyncFunc) univ, // smlEndSyncCallback,
+ #ifdef ATOMIC_RECEIVE /* these callbacks are NOT included in the Toolkit lite version */
+ univ, // smlStartAtomicCallback,
+ univ, // smlEndAtomicCallback,
+ #endif
+ #ifdef SEQUENCE_RECEIVE
+ univ, // smlStartSequenceCallback,
+ univ, // smlEndSequenceCallback,
+ #endif
+ /* Sync Commands */
+ (smlAddCmdFunc) univ, // smlAddCmdCallback,
+ alertM, // smlAlertCmdCallback,
+ (smlDeleteCmdFunc)univ, // smlDeleteCmdCallback,
+ (smlGetCmdFunc) univ, // smlGetCmdCallback,
+ (smlPutCmdFunc) univ, // smlPutCmdCallback,
+ #ifdef MAP_RECEIVE
+ (smlMapCmdFunc) univ, // smlMapCmdCallback,
+ #endif
+ #ifdef RESULT_RECEIVE
+ (smlResultsCmdFunc)univ, // smlResultsCmdCallback,
+ #endif
+ (smlStatusCmdFunc) univ, // smlStatusCmdCallback,
+ (smlReplaceCmdFunc)univ, // smlReplaceCmdCallback,
+ /* other commands */
+ #ifdef COPY_RECEIVE /* these callbacks are NOT included in the Toolkit lite version */
+ univ, // smlCopyCmdCallback,
+ #endif
+ #ifdef EXEC_RECEIVE
+ univ, // smlExecCmdCallback,
+ #endif
+ #ifdef SEARCH_RECEIVE
+ univ, // smlSearchCmdCallback,
+ #endif
+ smlMoveCmdFunc(univ), // smlMoveCmdCallback,
+ /* Other Callbacks */
+ smlHandleErrorFunc (univ), // smlHandleErrorCallback,
+ smlTransmitChunkFunc(univ) // smlTransmitChunkCallback
+}; /* sml_callbacks struct */
+
+
+
+// Try to convert a 1.1 message
+// - if successful, fill in values into 1.2 fields
+// - if not successful, interpret it as 1.2 structure
+TSyError SanPackage::Check_11( void* san, size_t sanSize )
+{
+ TSyError err;
+ SmlCallbacks_t scb= mySmlCallbacks;
+ SmlInstanceOptions_t sIOpts;
+ InstanceID_t id;
+ Ret_t cer;
+ MemPtr_t wPos;
+ MemSize_t freeSize;
+
+ // struct assignment / 1k buffer
+ sIOpts.encoding = SML_WBXML; // it is always WBXML
+ sIOpts.workspaceSize = 1024; // should be always sufficient
+ sIOpts.maxOutgoingSize= 0; // disabled for now
+
+ err= smlInitInstance( &scb, &sIOpts, this, &id ); if (err) return err;
+
+ do {
+ err= smlLockWriteBuffer ( id, &wPos, &freeSize ); if (err) break;
+
+ memcpy( wPos, san,sanSize ); // now we have a new internal copy
+ err= smlUnlockWriteBuffer( id, sanSize ); if (err) break;
+ err= smlProcessData ( id, SML_ALL_COMMANDS ); if (err) break;
+ } while (false);
+
+ cer= smlTerminateInstance( id ); if (!err) err= cer;
+
+ return err;
+} // Check_11
+#endif
+
+
+TSyError SanPackage::PassSan( void* san, size_t sanSize )
+{
+ TSyError err;
+ bool use_as_12= true;
+
+ ReleasePackage();
+//printf( "here we will have the potential 1.1 -> 1.2 conversion\n" );
+
+ #ifndef PUSHALERTER
+ err= Check_11 ( san,sanSize );
+ if (!err) err= GetPackage( san,sanSize );
+ //use_as_12= err==SML_ERR_XLT_INCOMP_WBXML_VERS;
+ use_as_12= err!=0;
+ //printf( "err=%d\n", err );
+ #endif
+
+ if (use_as_12) {
+ err= DB_Full;
+
+ fSan= malloc( sanSize );
+ if (fSan) {
+ fSanSize= sanSize;
+ memcpy( fSan, san,sanSize ); // now we have a new internal copy
+ err= LOCERR_OK;
+ } // if
+ } // if
+
+ return err;
+} // PassSan
+
+
+TSyError SanPackage::GetSanSize( void* san, size_t &sanSize )
+{
+ TPackage* tp= (TPackage*)san;
+ TBody* tb = NULL;
+
+ byte* b= (byte*)(tp+1);
+ byte* v;
+
+ b+= tp->serverID_len;
+
+ int nth= GetBits( b, 0,4 ); // first not valid = the end
+
+ b++; // start of 1st element
+ int n= nth;
+ while (n>0) {
+ n--;
+ tb= (TBody*)b;
+ b = (byte*)(tb+1);
+
+ if (b > (byte*)san+sanSize && sanSize>0) return DB_Forbidden;
+ v= b + tb->serverURI_len;
+ if (b > (byte*)san+sanSize && sanSize>0) return DB_Forbidden;
+
+ if (n==0) break;
+ b= v;
+ } // while
+
+ b+= tb->serverURI_len; // finally the serverURI length
+
+ size_t rslt= b - (byte*)san;
+ if (sanSize>0 && sanSize<rslt) return DB_Forbidden;
+
+ sanSize= rslt;
+ return LOCERR_OK;
+} // GetSanSize
+
+
+// ---- notification body parsing ----------------------------------------
+TSyError SanPackage::GetNthSync( int nth,
+ int &syncType,
+ uInt32 &contentType,
+ string &serverURI )
+{
+ syncType = 0; // set default values
+ contentType= 0;
+ serverURI = "";
+
+ TPackage* tp= (TPackage*)fSan;
+ TBody* tb;
+
+ fDigest = tp->digest;
+ fProtocolVersion= GetBits( tp->bitField, 0,10 );
+ fUI_Mode = (UI_Mode)GetBits( tp->bitField, 10, 2 );
+ fInitiator = (Initiator)GetBits( tp->bitField, 12, 1 );
+ fSessionID = GetBits( tp->bitField, 40,16 );
+
+ // that's the joke, it's no longer forbidden !
+//if (fProtocolVersion!=SyncML12) return DB_Forbidden;
+
+ byte* b= (byte*)(tp+1);
+ byte* v;
+
+ fServerID.assign( (const char*)b,(unsigned int)tp->serverID_len );
+ b+= tp->serverID_len;
+
+ fNSync= GetBits( b, 0,4 );
+
+ if (nth==0) return LOCERR_OK;
+ if (nth<1 || nth>fNSync ) return DB_NotFound;
+
+ b++; // start of 1st element
+ int n= nth;
+ while (n>0) {
+ n--;
+ tb= (TBody*)b;
+ b = (byte*)(tb+1);
+
+ if (b > (byte*)fSan+fSanSize) return DB_Forbidden; // no access behind the message
+ v= b + tb->serverURI_len;
+ if (v > (byte*)fSan+fSanSize) return DB_Forbidden; // no access behind the message
+
+ if (n==0) break;
+ b= v;
+ } // while
+
+ syncType = 200 + GetBits( tb->bitField, 0, 4 );
+ contentType= GetBits( tb->bitField, 8,24 );
+
+ serverURI.assign( (const char*)b,(unsigned int)tb->serverURI_len );
+
+ return LOCERR_OK;
+} // GetNthSync
+
+
+TSyError SanPackage::GetHeader()
+{
+ int syncType; // these 3 variables are not really used
+ uInt32 contentType;
+ string serverURI;
+
+ return GetNthSync( 0, syncType,contentType,serverURI );
+} // GetHeader
+
+
+
+// ---- package generation -----------------------------------------------
+TSyError SanPackage::GetPackage( void* &san, size_t &sanSize,
+ void* vendorSpecific,
+ size_t vendorSpecificSize )
+{
+ ReleasePackage(); // remove a previous one
+
+ byte len = (byte)fServerID.length(); // calulate the full size
+ sanSize= sizeof(TPackage) + len + fBodySize + vendorSpecificSize;
+ //size_t nfySize= sanSize - DigestSize;
+ fSan = malloc( sanSize );
+ san = fSan;
+ TPackage* tp= (TPackage*)fSan;
+
+ // -------------------
+ AddBits( tp->bitField, 0,10, fProtocolVersion );
+ AddBits( tp->bitField, 10, 2, fUI_Mode );
+ AddBits( tp->bitField, 12, 1, fInitiator );
+ AddBits( tp->bitField, 13,27, 0 ); // future use, must be "0"
+ AddBits( tp->bitField, 40,16, fSessionID );
+ tp->serverID_len= len;
+
+ // copy <fServerID> string at the end of TPackage struct
+ byte* pp= (byte*)(tp+1); // = right after TPackage
+ memcpy( (void*) pp, (void*)fServerID.c_str(), len );
+ memcpy( (void*)(pp+len), fBody, fBodySize );
+
+ if (vendorSpecific!=NULL &&
+ vendorSpecificSize>0)
+ memcpy( (void*)(pp+len+fBodySize), vendorSpecific,vendorSpecificSize );
+
+ CreateDigest( fB64_H_srvID_pwd.c_str(), fNonce.c_str(), san,sanSize );
+ tp->digest= fDigest;
+
+ fSanSize= sanSize;
+ return LOCERR_OK;
+} // GetPackage
+
+
+void SanPackage::ReleasePackage() {
+ if (fSan!=NULL) { free( fSan ); fSan= NULL; }
+} // ReleasePackage
+
+
+} // namespace sysync
+
+// eof
diff --git a/src/sysync/san.h b/src/sysync/san.h
new file mode 100644
index 0000000..6043365
--- /dev/null
+++ b/src/sysync/san.h
@@ -0,0 +1,256 @@
+/*
+ * File: san.h
+ *
+ * Author: Beat Forster (bfo@synthesis.ch)
+ *
+ * Server Alerted Notification
+ * for OMA DS 1.2
+ *
+ * Copyright (c) 2005-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ */
+
+#ifndef San_H
+#define San_H
+
+
+// ---------- standalone definitions ------------------------------
+#include "generic_types.h" // some basic defs, which aren't available
+#include "syerror.h" // error code definitions
+
+#include <cstdio> // used for printf calls
+#include <cstring> // used for strcpy/strlen calls
+
+#ifdef __cplusplus
+ #include <string> // STL includes
+ #include <list>
+
+ using namespace std;
+#endif
+
+typedef unsigned char byte;
+
+// ----------------------------------------------------
+
+
+namespace sysync {
+
+
+// The digest field structure
+#define DigestSize 16
+
+struct TDigestField {
+ uInt8 b[ DigestSize ];
+}; // TDigestField
+
+
+enum UI_Mode {
+ UI_not_specified = 0, // "00"
+ UI_background = 1, // "01"
+ UI_informative = 2, // "10"
+ UI_user_interaction = 3 // "11"
+};
+
+enum Initiator {
+ Initiator_User = 0, // "0"
+ Initiator_Server = 1 // "1"
+};
+
+
+/*!
+ * How to create a SAN package (on server side):
+ * 1) Prepare the SAN package using 'PreparePackage'
+ *
+ * If all datastores need to be notified, skip 2 and 3
+ * If specific datastores need to be notified, call 2 and 3
+ *
+ * 2) Call 'CreateEmptyNotificationBody' (not needed 1st time) and
+ * 3) call 'AddSync' for each datastore to be notified
+ *
+ * 4) Create the SAN package with 'GetPackage'.
+ * A vendor specific record can be added, if required.
+ *
+ *--------------------------------------------------------------------
+ * How to check a SAN package (on client side):
+ * 1) pass the san message with 'PassSan'
+ * 1) create <fDigest> first (using 'CreateDigest')
+ * 2) call 'DigestOK', to verify it
+ *
+ * How to get the <n>th sync message:
+ * call 'GetNthSync( n, .. )'
+ * [ call 'GetHeader ( )' or
+ * GetNthSync( 0, .. )' to get header params only ]
+ *
+ */
+class SanPackage {
+ public:
+ SanPackage(); // constructor
+ ~SanPackage(); // destructor
+
+ /*! Base64 encoded MD5, two strings can be concatenated with ":" */
+ string B64_H( string s1, string s2= "" );
+
+ /*! Base64 encoded MD5 of the notification part of <san>,<sanSize>. */
+ string B64_H_Notification( void* san, size_t sanSize );
+
+
+ /*! Prepare the SAN package */
+ void PreparePackage( string aB64_H_srvID_pwd,
+ string aNonce,
+ uInt16 aProtocolVersion,
+ UI_Mode aUI_Mode,
+ Initiator aInitiator,
+ uInt16 aSessionID,
+ string aSrvID );
+
+
+
+ /*! These variables will be assigned with the 'PreparePackage' call
+ * <fProtocolVersion>: 10*version => max= V102.3 / V1.0 = 10 )
+ * for OMA DS 1.2 use '12'
+ */
+ string fB64_H_srvID_pwd;
+ string fNonce;
+ uInt16 fProtocolVersion; // 10 bit
+ UI_Mode fUI_Mode; // 2 bit
+ Initiator fInitiator; // 1 bit
+ uInt16 fSessionID; // 16 bit
+ string fServerID;
+
+
+ /*! Create an empty notification body */
+ void CreateEmptyNotificationBody();
+
+
+ /*! Add a sync sequence to the notification body
+ *
+ * (in)
+ * @param <syncType> 206..210 (internally less 200: 206 -> 6)
+ * @param <contentType> MIME media content type (24 bit)
+ * @param <serverURI> server's URI
+ */
+ TSyError AddSync( int syncType, uInt32 contentType, const char* serverURI );
+
+
+ /*! Get the SAN package
+ *
+ * (out)
+ * @param <san> get the pointer to the SAN message.
+ * @param <sanSize> get the SAN message size (in bytes).
+ *
+ * (in)
+ * @param <vendorSpecific> reference to vendor specific part
+ * @param <vendorSpecificSize> size (in bytes) of vendor specific part
+ *
+ * @return error code if operation can't be performed
+ *
+ * NOTE: The notification body will be added automatically
+ *
+ */
+ TSyError GetPackage( void* &san, size_t &sanSize,
+ void* vendorSpecific= NULL,
+ size_t vendorSpecificSize= 0 );
+
+
+ /*! Create the digest for the SAN package:
+ * digest= H(B64(H(server-identifier:password)):nonce:B64(H(notification)))
+ * where notification will be calculated from <san>/<sanSize>.
+ */
+ TSyError CreateDigest( const char* aSrvID,
+ const char* aPwd,
+ const char* aNonce,
+ void* san, size_t sanSize );
+
+ /*! overloaded version, if only the B64 hashes are available */
+ TSyError CreateDigest( const char* b64_h_srvID_pwd,
+ const char* aNonce,
+ void* san, size_t sanSize );
+
+
+ /*! Check, if the digest of <san> is correct */
+ bool DigestOK( void* san );
+
+
+ /*! Pass SAN message <san>,<sanSize> to object,
+ * a local copy will be kept then internally
+ */
+ TSyError PassSan( void* san, size_t sanSize );
+
+
+ /*! Get the effective size of an already created <san> message
+ * (without vendor specific part)
+ *
+ * (in)
+ * @param <san> the pointer to the SAN message
+ * @param <sanSize> the max. SAN message size (in bytes)
+ * 0, if unknown.
+ * (out)
+ * @param <sanSize> the effective SAN message size (in bytes)
+ *
+ * @return error code 403, if input <sanSize> is too small
+ */
+ TSyError GetSanSize( void* san, size_t &sanSize );
+
+
+ /*! Get the nth sync info
+ *
+ * (in)
+ * @param <san> the pointer to the SAN message
+ * @param <sanSize> the SAN message size (in bytes)
+ * @param <nth> asks for the <nth> sync info
+ * nth=0 is allowed also, but will only assign
+ * the header variables
+ * (out)
+ * @param <syncType> 206..210 (internally less 200: 206 -> 6)
+ * @param <contentType> MIME media content type (24 bit)
+ * @param <serverURI> server's URI
+ *
+ * @return error code 403, if <sanSize> is too small
+ * 404, if <nth> is out of range
+ */
+ //TSyError GetNthSync( void* san, size_t sanSize, int nth,
+ TSyError GetNthSync( int nth,
+ int &syncType,
+ uInt32 &contentType,
+ string &serverURI );
+
+ /*! Alternative call for GetNthSync( 0, ... ) */
+ //TSyError GetHeader ( void* san, size_t sanSize );
+ TSyError GetHeader ();
+
+
+ TDigestField fDigest; // The digest, created with "CreateDigest"
+ int fNSync; // number of actual sync fields
+
+ private:
+ /*! the internally built notification-body structure */
+ byte fEmpty; // direct reference to empty structure
+ void* fBody; // the body structure ...
+ size_t fBodySize; // .. and its size
+
+ /*! local copies of <san>,<sanSize> */
+ void* fSan;
+ size_t fSanSize;
+
+ /*! MD5 conversion */
+ TDigestField H( string s );
+
+ /* Try to interpret SyncML 1.1 SAN */
+ TSyError Check_11( void* san, size_t sanSize );
+
+ /*! Add <value> into field <b> at <pos>,<n> */
+ void AddBits( void* ptr, int pos, int n, uInt32 value );
+ /*! Get value from field <b> at <pos>,<n> */
+ uInt32 GetBits( void* ptr, int pos, int n );
+
+ /*! Release notification body */
+ void ReleaseNotificationBody();
+
+ /*! Release the SAN package */
+ void ReleasePackage();
+}; // SanPackage
+
+
+} // namespace sysync
+#endif // San_H
+// eof
diff --git a/src/sysync/scriptcontext.cpp b/src/sysync/scriptcontext.cpp
new file mode 100755
index 0000000..773c72b
--- /dev/null
+++ b/src/sysync/scriptcontext.cpp
@@ -0,0 +1,4624 @@
+/*
+ * File: scriptcontext.cpp
+ *
+ * Author: Lukas Zeller (luz@synthesis.ch)
+ *
+ * TScriptContext
+ * Environment to tokenize, prepare and run scripts
+ *
+ * Copyright (c) 2002-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ * 2002-09-11 : luz : created
+ *
+ */
+
+#include "prefix_file.h"
+
+#ifdef SCRIPT_SUPPORT
+
+// includes
+#include "scriptcontext.h"
+
+#include "platform_exec.h" // for SHELLEXECUTE
+#include "rrules.h" // for RECURRENCE_COUNT/DATE
+#include "vtimezone.h" // for SETTIMEZONE
+#include "mimediritemtype.h" // for AlldayCount/MakeAllday
+#ifdef REGEX_SUPPORT
+ #include "pcre.h" // for RegEx functions
+#endif
+
+
+// script debug messages
+#ifdef SYDEBUG
+ #define SCRIPTDBGMSGX(lvl,x) { if (debugon && fSessionP) { POBJDEBUGPRINTFX(fSessionP,lvl,x); } }
+ #define SCRIPTDBGMSG(x) SCRIPTDBGMSGX(DBG_SCRIPTS,x)
+ #define SCRIPTDBGSTART(nam) { if (debugon && fSessionP) { if (fSessionP->getDbgMask() & DBG_SCRIPTS) { fSessionP->PDEBUGBLOCKFMTCOLL(("ScriptExecute","Start executing Script","name=%s",nam)); } else { POBJDEBUGPRINTFX(fSessionP,DBG_DATA,("Executing Script '%s'",nam)); } } }
+ #define SCRIPTDBGEND() { if (debugon && fSessionP && (fSessionP->getDbgMask() & DBG_SCRIPTS)) { fSessionP->PDEBUGENDBLOCK("ScriptExecute"); } }
+ #define SCRIPTDBGTEST (debugon && fSessionP && (fSessionP->getDbgMask() & DBG_SCRIPTS))
+ #define EXPRDBGTEST (debugon && fSessionP && ((fSessionP->getDbgMask() & (DBG_SCRIPTS|DBG_SCRIPTEXPR)) == (DBG_SCRIPTS|DBG_SCRIPTEXPR)))
+ #define DBGSTRINGDEF(s) string s
+ #define DBGVALUESHOW(s,v) dbgValueShow(s,v)
+ #define SHOWVARDEFS(t) showVarDefs(t)
+#else
+ #define SCRIPTDBGMSGX(lvl,x)
+ #define SCRIPTDBGMSG(x)
+ #define SCRIPTDBGSTART(nam)
+ #define SCRIPTDBGEND()
+ #define SCRIPTDBGTEST false
+ #define EXPRDBGTEST false
+ #define DBGSTRINGDEF(s)
+ #define DBGVALUESHOW(s,v)
+ #define SHOWVARDEFS(t)
+#endif
+
+
+namespace sysync {
+
+#ifdef SYDEBUG
+
+// show value of a field
+static void dbgValueShow(string &aString, TItemField *aFieldP)
+{
+ TItemFieldTypes ty;
+ if (aFieldP) {
+ // value
+ aString = "&html;<span class=\"value\">&html;";
+ aFieldP->StringObjFieldAppend(aString,200);
+ aString += "&html;</span>&html;";
+ // add type info
+ ty=aFieldP->getType();
+ aString += " (";
+ aString += ItemFieldTypeNames[ty];
+ aString += ")";
+ }
+ else {
+ aString="<NO FIELD>";
+ }
+} // dbgValueShow
+
+#endif
+
+
+TTokenizeException::TTokenizeException(cAppCharP aScriptName, cAppCharP aMsg1,cAppCharP aScript, uInt16 aIndex, uInt16 aLine)
+ : TConfigParseException("")
+{
+ cAppCharP p2=aScript+aIndex;
+ cAppCharP p1=p2-(aIndex>5 ? 5 : aIndex);
+
+ StringObjPrintf(fMessage,
+ "%s: %s at line %hd: '...%-.5s_%-.10s...'",
+ aScriptName ? aScriptName : "<unnamed script>",
+ aMsg1,
+ aLine,
+ p1,
+ p2
+ );
+} // TTokenizeException::TTokenizeException
+
+
+TScriptErrorException::TScriptErrorException(cAppCharP aMsg1, uInt16 aLine, cAppCharP aIdent)
+ : TConfigParseException("")
+{
+ if (aIdent) {
+ StringObjPrintf(fMessage,aMsg1,aIdent);
+ }
+ else {
+ StringObjPrintf(fMessage,"%s",aMsg1);
+ }
+ StringObjAppendPrintf(
+ fMessage,
+ " in script at line %hd",
+ aLine
+ );
+} // TScriptErrorException::TScriptErrorException
+
+
+
+/*
+ * Implementation of TScriptConfig
+ */
+
+
+// config constructor
+TScriptConfig::TScriptConfig(TConfigElement *aParentElementP) :
+ TConfigElement("scripting",aParentElementP)
+{
+ clear();
+} // TScriptConfig::TScriptConfig
+
+
+// config destructor
+TScriptConfig::~TScriptConfig()
+{
+ clear();
+} // TScriptConfig::~TScriptConfig
+
+
+// init defaults
+void TScriptConfig::clear(void)
+{
+ // delete options
+ fMaxLoopProcessingTime =
+ #if SYDEBUG>1
+ 60; // 1 min for debugging
+ #else
+ 5; // 5 seconds for real execution
+ #endif
+ // delete functions
+ TUserScriptList::iterator pos;
+ for (pos=fFunctionScripts.begin();pos!=fFunctionScripts.end();++pos) {
+ delete (*pos);
+ }
+ fFunctionScripts.clear();
+ fScriptMacros.clear();
+ // clear inherited
+ inherited::clear();
+} // TScriptConfig::clear
+
+
+// server config element parsing
+bool TScriptConfig::localStartElement(const char *aElementName, const char **aAttributes, sInt32 aLine)
+{
+ // checking the elements
+ if (strucmp(aElementName,"looptimeout")==0)
+ expectUInt32(fMaxLoopProcessingTime);
+ else if (strucmp(aElementName,"function")==0) {
+ // create new function definition
+ TUserScriptFunction *funcdefP = new TUserScriptFunction;
+ fFunctionScripts.push_back(funcdefP);
+ expectFunction(*funcdefP,aLine);
+ }
+ else if (strucmp(aElementName,"macro")==0) {
+ const char *macroname = getAttr(aAttributes,"name");
+ if (!macroname)
+ fail("<macro> must have a 'name' attribute");
+ // create new macro
+ expectRawString(fScriptMacros[macroname]);
+ }
+ // - none known here
+ else
+ return inherited::localStartElement(aElementName,aAttributes,aLine);
+ // ok
+ return true;
+} // TScriptConfig::localStartElement
+
+
+// resolve
+void TScriptConfig::localResolve(bool aLastPass)
+{
+ // resolve inherited
+ inherited::localResolve(aLastPass);
+} // TScriptConfig::localResolve
+
+
+// get script text
+string *TScriptConfig::getFunctionScript(sInt16 aFuncIndex)
+{
+ if (aFuncIndex<0 || aFuncIndex>(sInt16)fFunctionScripts.size())
+ return NULL;
+ return &(fFunctionScripts[aFuncIndex]->fFuncDef);
+} // TScriptConfig::getFunctionScript
+
+
+// get index of specific function
+sInt16 TScriptConfig::getFunctionIndex(cAppCharP aName, size_t aLen)
+{
+ TUserScriptList::iterator pos;
+ sInt16 i=0;
+ for (pos=fFunctionScripts.begin();pos!=fFunctionScripts.end();++pos) {
+ if (strucmp((*pos)->fFuncName.c_str(),aName)==0)
+ return i;
+ i++;
+ }
+ // unknown
+ return VARIDX_UNDEFINED;
+} // TScriptConfig::getFunctionIndex
+
+
+// Script variable definition
+
+// create new scrip variable definition
+TScriptVarDef::TScriptVarDef(cAppCharP aName,uInt16 aIdx, TItemFieldTypes aType, bool aIsArray, bool aIsRef, bool aIsOpt)
+{
+ fVarName=aName;
+ fIdx=aIdx;
+ fVarType=aType;
+ fIsArray=aIsArray;
+ fIsRef=aIsRef;
+ fIsOpt=aIsOpt;
+} // TScriptVarDef::TScriptVarDef
+
+
+TScriptVarDef::~TScriptVarDef()
+{
+} // TScriptVarDef::~TScriptVarDef
+
+
+
+/*
+ * builtin function definitions
+ */
+
+
+class TBuiltinStdFuncs {
+public:
+
+ // timestamp NOW()
+ // returns current date/time stamp in UTC with timezone set
+ static void func_Now(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ TTimestampField *tsP = static_cast<TTimestampField *>(aTermP);
+
+ tsP->setTimestampAndContext(
+ getSystemNowAs(TCTX_UTC, tsP->getGZones()),
+ TCTX_UTC
+ );
+ }; // func_Now
+
+
+ // timestamp SYSTEMNOW()
+ // returns current date/time stamp as system time with timezone set
+ static void func_SystemNow(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ TTimestampField *tsP = static_cast<TTimestampField *>(aTermP);
+
+ tsP->setTimestampAndContext(
+ getSystemNowAs(TCTX_SYSTEM, tsP->getGZones()),
+ TCTX_SYSTEM
+ );
+ }; // func_SystemNow
+
+
+ // timestamp DBNOW()
+ // returns database's idea of "now" in UTC with local timezone set
+ static void func_DbNow(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ TTimestampField *tsP = static_cast<TTimestampField *>(aTermP);
+
+ tsP->setTimestampAndContext(
+ aFuncContextP->getSession()->getDatabaseNowAs(TCTX_UTC),
+ TCTX_UTC
+ );
+ }; // func_DbNow
+
+
+ // integer ZONEOFFSET(timestamp atime)
+ // returns zone offset for given timestamp.
+ // New in 3.1: Floating timestamps return unassigned
+ static void func_ZoneOffset(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ TTimestampField *tsP = static_cast<TTimestampField *>(aFuncContextP->getLocalVar(0));
+ sInt16 moffs=0;
+
+ if (tsP->isFloating()) {
+ // floating timestamps do not have an offset
+ aTermP->unAssign();
+ }
+ else {
+ moffs = tsP->getMinuteOffset();
+ aTermP->setAsInteger((sInt32)moffs*SecsPerMin);
+ }
+ }; // func_ZoneOffset
+
+
+ // helper to get a time context from integer seconds offset or string zone name
+ static timecontext_t contextFromSpec(TItemField *aVariantP, TScriptContext *aFuncContextP)
+ {
+ timecontext_t tctx = TCTX_UNKNOWN;
+
+ if (aVariantP->isBasedOn(fty_timestamp)) {
+ // just use context of another timestamp
+ tctx = static_cast<TTimestampField *>(aVariantP)->getTimeContext();
+ }
+ else if (aVariantP->getCalcType()==fty_integer) {
+ // integer specifies a seconds offset
+ tctx = TCTX_MINOFFSET(aVariantP->getAsInteger() / SecsPerMin);
+ }
+ else if (aVariantP->isEmpty()) {
+ // empty non-timestamp and non-integer mean unknown/floating timezone
+ tctx = TCTX_UNKNOWN;
+ }
+ else {
+ // treat as string specifying time zone by name or vTimezone
+ string str;
+ aVariantP->getAsString(str);
+ if (strucmp(str.c_str(),"USERTIMEZONE")==0) {
+ // special case - session's user time zone
+ tctx = aFuncContextP->getSession()->fUserTimeContext;
+ }
+ else if (strucmp(str.c_str(),"SYSTEM")==0) {
+ tctx = TCTX_SYSTEM;
+ }
+ else if (strucmp(str.c_str(),"BEGIN:VTIMEZONE",15)==0) {
+ // is a vTimezone, get it
+ if (!VTIMEZONEtoInternal(str.c_str(), tctx, aFuncContextP->getSession()->getSessionZones()))
+ tctx = TCTX_UNKNOWN;
+ }
+ else {
+ // search for timezone by name
+ if (!TimeZoneNameToContext(str.c_str(), tctx, aFuncContextP->getSession()->getSessionZones())) {
+ // last attempt is parsing it as a ISO8601 offset spec
+ ISO8601StrToContext(str.c_str(), tctx);
+ }
+ }
+ }
+ return tctx;
+ } // contextFromSpec
+
+
+ // helper to represent a time context as string
+ static void zoneStrFromContext(timecontext_t aContext, TItemField *aZoneStrFieldP, TScriptContext *aFuncContextP)
+ {
+ string str;
+
+ if (TCTX_IS_UNKNOWN(aContext)) {
+ // no time zone
+ aZoneStrFieldP->unAssign();
+ }
+ else if (TCTX_IS_TZ(aContext)) {
+ // symbolic time zone, show name
+ TimeZoneContextToName(aContext,str,aFuncContextP->getSession()->getSessionZones());
+ aZoneStrFieldP->setAsString(str);
+ }
+ else {
+ // is non-symbolic minute offset, show it in ISO8601 extended form
+ str.erase();
+ ContextToISO8601StrAppend(str, aContext, true);
+ }
+ } // zoneStrFromContext
+
+
+ // string TIMEZONE(timestamp atime)
+ // returns time zone name
+ static void func_Timezone(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ TTimestampField *tsP = static_cast<TTimestampField *>(aFuncContextP->getLocalVar(0));
+
+ zoneStrFromContext(tsP->getTimeContext(), aTermP, aFuncContextP);
+ }; // func_Timezone
+
+
+ // string VTIMEZONE(timestamp atime)
+ // returns time zone in VTIMEZONE format
+ static void func_VTimezone(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ TTimestampField *tsP = static_cast<TTimestampField *>(aFuncContextP->getLocalVar(0));
+
+ string z;
+ internalToVTIMEZONE(tsP->getTimeContext(),z,aFuncContextP->getSession()->getSessionZones());
+ aTermP->setAsString(z);
+ }; // func_VTimezone
+
+
+ // SETTIMEZONE(timestamp &atime,variant zonespec)
+ // sets time zone for given timestamp field
+ static void func_SetTimezone(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ TTimestampField *tsP = static_cast<TTimestampField *>(aFuncContextP->getLocalVar(0));
+
+ // get context from variant spec
+ timecontext_t tctx = contextFromSpec(aFuncContextP->getLocalVar(1), aFuncContextP);
+ // set it
+ tsP->setTimeContext(tctx);
+ }; // func_SetTimezone
+
+
+ // SETFLOATING(timestamp &atime)
+ // sets given timestamp to floating (no timezone)
+ // this is an efficient shortform for SETTIMEZONE(atime,"FLOATING") or SETTIMEZONE(atime,"")
+ static void func_SetFloating(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ TTimestampField *tsP = static_cast<TTimestampField *>(aFuncContextP->getLocalVar(0));
+ // set it
+ tsP->setTimeContext(TCTX_UNKNOWN);
+ }; // func_SetFloating
+
+
+ // timestamp CONVERTTOZONE(timestamp atime, variant zonespec [,boolean doUnfloat])
+ // returns timestamp converted to specified zone.
+ // - If doUnfloat, floating timestamps will be fixed in the new zone w/o conversion of the timestamp itself.
+ // - timestamps that already have a zone will be converted
+ static void func_ConvertToZone(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ TTimestampField *tsP = static_cast<TTimestampField *>(aFuncContextP->getLocalVar(0));
+
+ // get context from variant spec
+ timecontext_t actual,tctx = contextFromSpec(aFuncContextP->getLocalVar(1), aFuncContextP);
+ // convert and get actually resulting context back (can also be floating)
+ lineartime_t ts = tsP->getTimestampAs(tctx,&actual);
+ // unfloat floats if selected
+ if (aFuncContextP->getLocalVar(2)->getAsBoolean() && TCTX_IS_UNKNOWN(actual)) actual=tctx; // unfloat
+ // assign it to result
+ static_cast<TTimestampField *>(aTermP)->setTimestampAndContext(ts,actual);
+ }; // func_ConvertToZone
+
+
+ // timestamp CONVERTTOUSERZONE(timestamp atime [,boolean doUnfloat])
+ // returns timestamp converted to user time zone.(or floating timestamp as-is)
+ // - this is an efficient shortform for CONVERTTOZONE(atime,"USERTIMEZONE")
+ // - If doUnfloat, floating timestamps will be fixed in the new zone w/o conversion of the timestamp itself.
+ // - timestamps that already have a zone will be converted
+ static void func_ConvertToUserZone(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ TTimestampField *tsP = static_cast<TTimestampField *>(aFuncContextP->getLocalVar(0));
+
+ timecontext_t actual,tctx = aFuncContextP->getSession()->fUserTimeContext;
+ // convert and get actually resulting context back (can also be floating)
+ lineartime_t ts = tsP->getTimestampAs(tctx,&actual);
+ // unfloat floats if selected
+ if (aFuncContextP->getLocalVar(1)->getAsBoolean() && TCTX_IS_UNKNOWN(actual)) actual=tctx; // unfloat
+ // assign it to result
+ static_cast<TTimestampField *>(aTermP)->setTimestampAndContext(ts,actual);
+ }; // func_ConvertToUserZone
+
+
+ // string USERTIMEZONE()
+ // returns session user time zone name
+ static void func_UserTimezone(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ zoneStrFromContext(aFuncContextP->getSession()->fUserTimeContext,aTermP, aFuncContextP);
+ }; // func_UserTimezone
+
+
+ // SETUSERTIMEZONE(variant zonespec)
+ // sets session user time zone
+ static void func_SetUserTimezone(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ // get context from variant spec
+ timecontext_t tctx = contextFromSpec(aFuncContextP->getLocalVar(1), aFuncContextP);
+
+ aFuncContextP->getSession()->fUserTimeContext = tctx;
+ }; // func_SetUserTimezone
+
+
+ // integer ISDATEONLY(timestamp atime)
+ // returns true if given timestamp is a date-only
+ static void func_IsDateOnly(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ TTimestampField *tsP = static_cast<TTimestampField *>(aFuncContextP->getLocalVar(0));
+
+ aTermP->setAsInteger(TCTX_IS_DATEONLY(tsP->getTimeContext()) ? 1 : 0);
+ }; // func_IsDateOnly
+
+
+ // timestamp DATEONLY(timestamp atime)
+ // returns a floating(!) date-only of the given timestamp
+ static void func_DateOnly(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ TTimestampField *tsP = static_cast<TTimestampField *>(aFuncContextP->getLocalVar(0));
+
+ // get timestamp as dateonly
+ timecontext_t tctx;
+ lineartime_t ts;
+ ts = tsP->getTimestampAs(TCTX_UNKNOWN | TCTX_DATEONLY, &tctx);
+ // assign it to result (but do NOT pass in tctx, as it might contain a zone
+ // but we need it as floating to make dates comparable
+ static_cast<TTimestampField *>(aTermP)->setTimestampAndContext(ts,TCTX_DATEONLY|TCTX_UNKNOWN);
+ }; // func_DateOnly
+
+
+ // integer ISDURATION(timestamp atime)
+ // returns true if given timestamp is a duration value
+ static void func_IsDuration(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ TTimestampField *tsP = static_cast<TTimestampField *>(aFuncContextP->getLocalVar(0));
+ aTermP->setAsInteger(tsP->isDuration() ? 1 : 0);
+ }; // func_IsDuration
+
+
+ // timestamp DURATION(timestamp atime)
+ // returns the timestamp as a duration (floating, duration flag set)
+ static void func_Duration(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ TTimestampField *tsP = static_cast<TTimestampField *>(aFuncContextP->getLocalVar(0));
+
+ // get timestamp as-is
+ timecontext_t tctx;
+ lineartime_t ts;
+ ts = tsP->getTimestampAs(TCTX_UNKNOWN, &tctx);
+ // result is floating timestamp with duration flag set (and dateonly/timeonly flags retained)
+ static_cast<TTimestampField *>(aTermP)->setTimestampAndContext(ts,TCTX_UNKNOWN|(tctx&TCTX_RFLAGMASK)|TCTX_DURATION);
+ }; // func_Duration
+
+
+ // timestamp POINTINTIME(timestamp atime)
+ // returns the timestamp as a point in time (i.e. not duration)
+ static void func_PointInTime(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ TTimestampField *tsP = static_cast<TTimestampField *>(aFuncContextP->getLocalVar(0));
+
+ // get timestamp as-is
+ timecontext_t tctx;
+ lineartime_t ts;
+ ts = tsP->getTimestampAs(TCTX_UNKNOWN, &tctx);
+ // assign it to result with duration flag cleared
+ static_cast<TTimestampField *>(aTermP)->setTimestampAndContext(ts,tctx & (~TCTX_DURATION));
+ }; // func_PointInTime
+
+
+
+ // integer ISFLOATING(timestamp atime)
+ // returns true if given timestamp is floating (i.e. not bound to a time zone)
+ static void func_IsFloating(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ TTimestampField *tsP = static_cast<TTimestampField *>(aFuncContextP->getLocalVar(0));
+ aTermP->setAsInteger(tsP->isFloating() ? 1 : 0);
+ }; // func_IsFloating
+
+
+
+
+ // integer ALLDAYCOUNT(timestamp start, timestamp end [, boolean checkinusercontext [, onlyutcinusercontext]])
+ // returns number of days for an all-day event
+ // Note: Timestamps must be in the context in which they are to be checked for midnight, 23:59:xx etc.
+ // except if checkinusercontext ist set (then non-floating timestamps are moved into user context before
+ // checking. onlyutcinusercontext limits moving to user context to UTC timestamps only.
+ static void func_AlldayCount(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ sInt16 c = AlldayCount(
+ aFuncContextP->getLocalVar(0),
+ aFuncContextP->getLocalVar(1),
+ aFuncContextP->getLocalVar(2)->getAsBoolean() ? aFuncContextP->getSession()->fUserTimeContext : TCTX_UNKNOWN,
+ aFuncContextP->getLocalVar(3)->getAsBoolean()
+ );
+ aTermP->setAsInteger(c);
+ }; // func_AlldayCount
+
+
+ // MAKEALLDAY(timestamp &start, timestamp &end [,integer days])
+ // adjusts timestamps for allday representation, makes them floating
+ // Note: Timestamps must already represent local day times
+ static void func_MakeAllday(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ sInt16 days=aFuncContextP->getLocalVar(2)->getAsInteger(); // returns 0 if unassigned
+ MakeAllday(
+ aFuncContextP->getLocalVar(0),
+ aFuncContextP->getLocalVar(1),
+ TCTX_UNKNOWN,
+ days
+ );
+ }; // func_MakeAllday
+
+
+ // integer WEEKDAY(timestamp timestamp)
+ // returns weekday (0=sunday, 1=monday ... 6=saturday) from a timestamp
+ static void func_Weekday(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ lineartime_t lt = static_cast<TTimestampField *>(aFuncContextP->getLocalVar(0))->getTimestampAs(TCTX_UNKNOWN);
+ aTermP->setAsInteger(lineartime2weekday(lt));
+ }; // func_Weekday
+
+
+ // integer SECONDS(integer timeunits)
+ // returns number of seconds from a time unit spec
+ static void func_Seconds(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ fieldinteger_t v=aFuncContextP->getLocalVar(0)->getAsInteger();
+ aTermP->setAsInteger(v/secondToLinearTimeFactor);
+ }; // func_Seconds
+
+
+ // integer MILLISECONDS(integer timeunits)
+ // returns number of milliseconds from a time unit spec
+ static void func_Milliseconds(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ fieldinteger_t v=aFuncContextP->getLocalVar(0)->getAsInteger();
+ if (secondToLinearTimeFactor==1) {
+ v=v*1000; // internal unit is seconds
+ }
+ else if (secondToLinearTimeFactor!=1000) {
+ v=v/secondToLinearTimeFactor; // seconds
+ v=v*1000; // artifical milliseconds
+ }
+ aTermP->setAsInteger(v);
+ }; // func_Milliseconds
+
+
+ // void SLEEPMS(integer milliseconds)
+ // sleeps process (or thread) for specified time
+ static void func_SleepMS(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ fieldinteger_t sl=aFuncContextP->getLocalVar(0)->getAsInteger();
+ // make nanoseconds, then timeunits
+ sl *= 1000000LL;
+ sl /= nanosecondsPerLinearTime;
+ sleepLineartime(sl);
+ }; // func_SleepMS
+
+
+ // integer TIMEUNITS(integer seconds)
+ // returns number of time units from a seconds spec
+ static void func_Timeunits(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ fieldinteger_t v=aFuncContextP->getLocalVar(0)->getAsInteger();
+ aTermP->setAsInteger(v*secondToLinearTimeFactor);
+ }; // func_Timeunits
+
+
+ // integer DAYUNITS(integer days)
+ // returns number of time units from a seconds spec
+ static void func_Dayunits(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ fieldinteger_t v=aFuncContextP->getLocalVar(0)->getAsInteger();
+ aTermP->setAsInteger(v*linearDateToTimeFactor);
+ }; // func_Dayunits
+
+
+ // integer MONTHDAYS(timestamp date)
+ // returns number of days of the month date is in
+ static void func_MonthDays(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ lineartime_t ts = static_cast<TTimestampField *>(aFuncContextP->getLocalVar(0))->getTimestampAs(TCTX_UNKNOWN);
+ aTermP->setAsInteger(getMonthDays(lineartime2dateonly(ts)));
+ }; // func_MonthDays
+
+
+
+ // DEBUGMESSAGE(string msg)
+ // writes debug message to debug log file if debugging is not completely disabled
+ static void func_Debugmessage(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ #ifdef SYDEBUG
+ if (aFuncContextP->getDbgMask()) {
+ // get message
+ string s;
+ aFuncContextP->getLocalVar(0)->getAsString(s);
+ // write it
+ if (aFuncContextP->getSession()) {
+ POBJDEBUGPRINTFX(aFuncContextP->getSession(),DBG_HOT,("Script DEBUGMESSAGE() at Line %hd: %s",aFuncContextP->getScriptLine(),s.c_str()));
+ } else {
+ POBJDEBUGPRINTFX(aFuncContextP->getSyncAppBase(),DBG_HOT,("Script DEBUGMESSAGE() at Line %hd: %s",aFuncContextP->getScriptLine(),s.c_str()));
+ }
+ }
+ #endif
+ }; // func_Debugmessage
+
+
+ // DEBUGSHOWVARS()
+ // shows values of all local script variables
+ static void func_DebugShowVars(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ #ifdef SYDEBUG
+ if (aFuncContextP->getDbgMask() && aFuncContextP->fParentContextP) {
+ POBJDEBUGPRINTFX(aFuncContextP->getSession(),DBG_HOT,("Script DEBUGSHOWVARS() at Line %hd:",aFuncContextP->getScriptLine()));
+ // show all local vars (of PARENT!)
+ TScriptContext *showContextP = aFuncContextP->fParentContextP;
+ string fshow;
+ uInt16 i;
+ for (i=0; i<showContextP->getNumLocals(); i++) {
+ StringObjAppendPrintf(fshow,"- %-20s : ",showContextP->getVarDef(i)->fVarName.c_str());
+ showContextP->getLocalVar(i)->StringObjFieldAppend(fshow,80);
+ fshow += '\n';
+ }
+ // output preformatted
+ POBJDEBUGPUTSXX(aFuncContextP->getSession(),DBG_HOT,fshow.c_str(),0,true);
+ }
+ #endif
+ }; // func_DebugShowVars
+
+
+ // DEBUGSHOWITEM([bool aShowRefItem])
+ // shows all fields and values of current item
+ static void func_DebugShowItem(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ #ifdef SYDEBUG
+ if (aFuncContextP->getDbgMask() && aFuncContextP->fParentContextP) {
+ POBJDEBUGPRINTFX(aFuncContextP->getSession(),DBG_HOT,("Script DEBUGSHOWITEM() at Line %hd:",aFuncContextP->getScriptLine()));
+ // select item
+ TMultiFieldItem *showItemP =
+ aFuncContextP->getLocalVar(0)->getAsBoolean() ? // optional param to select ref item instead of target
+ aFuncContextP->fParentContextP->fReferenceItemP :
+ aFuncContextP->fParentContextP->fTargetItemP;
+ if (showItemP) {
+ showItemP->debugShowItem(DBG_HOT);
+ }
+ else {
+ POBJDEBUGPRINTFX(aFuncContextP->getSession(),DBG_HOT,("- no item to show"));
+ }
+ }
+ #endif
+ }; // func_DebugShowItem
+
+
+
+
+ // SETXMLTRANSLATE(bool yesorno)
+ // enables or disables XML translated SyncML message dumping on a per session basis
+ static void func_SetXMLTranslate(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ #ifdef SYDEBUG
+ if (aFuncContextP->getSession())
+ aFuncContextP->getSession()->fXMLtranslate=aFuncContextP->getLocalVar(0)->getAsBoolean();
+ #endif
+ }; // func_SetXMLTranslate
+
+
+ // SETMSGDUMP(bool yesorno)
+ // enables or disables raw SyncML message dumping on a per session basis
+ static void func_SetMsgDump(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ #ifdef SYDEBUG
+ if (aFuncContextP->getSession())
+ aFuncContextP->getSession()->fMsgDump=aFuncContextP->getLocalVar(0)->getAsBoolean();
+ #endif
+ }; // func_SetMsgDump
+
+
+ // SETDEBUGOPTIONS(string optionname, boolean set)
+ // sets or clears debug option flags (for the currently running session)
+ static void func_SetDebugOptions(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ #ifdef SYDEBUG
+ TDebugLogger *loggerP=aFuncContextP->getDbgLogger();
+ if (loggerP) {
+ // get option string
+ string s;
+ aFuncContextP->getLocalVar(0)->getAsString(s);
+ // convert to bitmask
+ sInt16 k;
+ if (StrToEnum(debugOptionNames,numDebugOptions,k,s.c_str())) {
+ // found mask, modify
+ uInt32 currentmask=loggerP->getRealMask();
+ if (aFuncContextP->getLocalVar(1)->getAsBoolean())
+ currentmask |= debugOptionMasks[k];
+ else
+ currentmask &= ~debugOptionMasks[k];
+ // .. and apply
+ loggerP->setMask(currentmask);
+ }
+ }
+ #endif
+ }; // func_SetDebugOptions
+
+
+ // SETDEBUGMASK(integer mask)
+ // sets the debug mask
+ static void func_SetDebugMask(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ #ifdef SYDEBUG
+ TDebugLogger *loggerP=aFuncContextP->getDbgLogger();
+ if (loggerP) {
+ // get mask value
+ loggerP->setMask(aFuncContextP->getLocalVar(0)->getAsInteger());
+ }
+ #endif
+ }; // func_SetDebugMask
+
+
+ // integer GETDEBUGMASK()
+ // gets the current debug mask
+ static void func_GetDebugMask(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ uInt32 m=0; // without debug, mask is always 0
+ #ifdef SYDEBUG
+ TDebugLogger *loggerP=aFuncContextP->getDbgLogger();
+ if (loggerP) {
+ // get mask value
+ loggerP->getRealMask();
+ }
+ #endif
+ aTermP->setAsInteger(m);
+ }; // func_GetDebugMask
+
+
+ // void REQUESTMAXTIME(integer maxtime_seconds)
+ static void func_RequestMaxTime(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ TSyncSession *sessionP = aFuncContextP->getSession();
+ if (sessionP) {
+ sessionP->fRequestMaxTime=aFuncContextP->getLocalVar(0)->getAsInteger();
+ }
+ } // func_RequestMaxTime
+
+
+ // void REQUESTMINTIME(integer maxtime_seconds)
+ // artificial delay for testing
+ static void func_RequestMinTime(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ TSyncSession *sessionP = aFuncContextP->getSession();
+ if (sessionP) {
+ sessionP->fRequestMinTime=aFuncContextP->getLocalVar(0)->getAsInteger();
+ }
+ } // func_RequestMinTime
+
+
+ // string SYNCMLVERS()
+ // gets the SyncML version of the current session as string like "1.2" or "1.0" etc.
+ static void func_SyncMLVers(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ aTermP->setAsString(SyncMLVersionNames[aFuncContextP->getSession()->getSyncMLVersion()]);
+ }; // func_SyncMLVers
+
+
+ // integer SHELLEXECUTE(string command, string arguments [,boolean inbackground])
+ // executes the command line in a shell, returns the exit code of the command
+ static void func_Shellexecute(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ // get params
+ string cmd,params;
+ bool inbackground;
+ sInt32 exitcode;
+ aFuncContextP->getLocalVar(0)->getAsString(cmd);
+ aFuncContextP->getLocalVar(1)->getAsString(params);
+ // optional param
+ inbackground=aFuncContextP->getLocalVar(2)->getAsBoolean(); // returns false if not assigned -> not in background
+ // execute now
+ exitcode=shellExecCommand(cmd.c_str(),params.c_str(),inbackground);
+ // return result code
+ aTermP->setAsInteger(exitcode);
+ }; // func_Shellexecute
+
+
+ // string REMOTERULENAME()
+ // returns name of applied remote rule, empty if none
+ static void func_Remoterulename(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ #ifndef NO_REMOTE_RULES
+ string r;
+ if (aFuncContextP->getSession()) {
+ // there is a session
+ if (aFuncContextP->getSession()->fAppliedRemoteRuleP) {
+ // there is a rule applied
+ aTermP->setAsString(aFuncContextP->getSession()->fAppliedRemoteRuleP->getName());
+ return;
+ }
+ }
+ #endif
+ // no remote rule applied
+ aTermP->assignEmpty();
+ }; // func_Remoterulename
+
+
+ // TREATASLOCALTIME(integer flag)
+ static void func_SetTreatAsLocaltime(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ TSyncSession *sessionP = aFuncContextP->getSession();
+ if (sessionP) sessionP->fTreatRemoteTimeAsLocal = aFuncContextP->getLocalVar(0)->getAsBoolean();
+ }; // func_SetTreatAsLocaltime
+
+
+ // TREATASUTC(integer flag)
+ static void func_SetTreatAsUTC(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ TSyncSession *sessionP = aFuncContextP->getSession();
+ if (sessionP) sessionP->fTreatRemoteTimeAsUTC = aFuncContextP->getLocalVar(0)->getAsBoolean();
+ }; // func_SetTreatAsUTC
+
+
+ // UPDATECLIENTINSLOWSYNC(integer flag)
+ static void func_SetUpdateClientInSlowSync(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ TSyncSession *sessionP = aFuncContextP->getSession();
+ if (sessionP) sessionP->fUpdateClientDuringSlowsync = aFuncContextP->getLocalVar(0)->getAsBoolean();
+ }; // func_SetUpdateClientInSlowSync
+
+
+ // UPDATESERVERINSLOWSYNC(integer flag)
+ static void func_SetUpdateServerInSlowSync(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ TSyncSession *sessionP = aFuncContextP->getSession();
+ if (sessionP) sessionP->fUpdateServerDuringSlowsync = aFuncContextP->getLocalVar(0)->getAsBoolean();
+ }; // func_SetUpdateServerInSlowSync
+
+
+ // SHOWCTCAPPROPERTIES(bool yesorno)
+ static void func_ShowCTCapProps(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ TSyncSession *sessionP = aFuncContextP->getSession();
+ if (sessionP) sessionP->fShowCTCapProps = aFuncContextP->getLocalVar(0)->getAsBoolean();
+ } // func_ShowCTCapProps
+
+
+ // SHOWTYPESIZEINCTCAP10(bool yesorno)
+ static void func_ShowTypeSizeInCTCap10(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ TSyncSession *sessionP = aFuncContextP->getSession();
+ if (sessionP) sessionP->fShowTypeSzInCTCap10 = aFuncContextP->getLocalVar(0)->getAsBoolean();
+ } // func_ShowTypeSizeInCTCap10
+
+
+ // ENUMDEFAULTPROPPARAMS(bool yesorno)
+ static void func_EnumDefaultPropParams(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ TSyncSession *sessionP = aFuncContextP->getSession();
+ // Note that this is a tristate!
+ if (sessionP) sessionP->fEnumDefaultPropParams =
+ aFuncContextP->getLocalVar(0)->getAsBoolean() ? 1 : 0;
+ } // func_EnumDefaultPropParams
+
+
+ // string LOCALURI()
+ // returns local URI as used by client for starting session
+ static void func_LocalURI(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ string r;
+ if (aFuncContextP->getSession()) {
+ // there is a session
+ aTermP->setAsString(aFuncContextP->getSession()->getInitialLocalURI());
+ return;
+ }
+ // no session??
+ aTermP->assignEmpty();
+ }; // func_LocalURI
+
+
+ // string SUBSTR(string, from [, count])
+ static void func_Substr(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ // get params
+ string s;
+ aFuncContextP->getLocalVar(0)->getAsString(s);
+ string::size_type i=aFuncContextP->getLocalVar(1)->getAsInteger();
+ // optional param
+ string::size_type l=s.size(); // default to entire string
+ if (aFuncContextP->getLocalVar(2)->isAssigned())
+ l=aFuncContextP->getLocalVar(2)->getAsInteger(); // use specified count
+ string r;
+ // adjust params
+ if (i>=s.size()) l=0;
+ else if (i+l>s.size()) l=s.size()-i;
+ // evaluate
+ if (l>0) r.assign(s,i,l);
+ // save result
+ aTermP->setAsString(r);
+ }; // func_Substr
+
+
+ // string EXPLODE(string glue, variant &parts[])
+ static void func_Explode(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ // get params
+ string glue,s;
+ aFuncContextP->getLocalVar(0)->getAsString(glue);
+ TItemField *fldP = aFuncContextP->getLocalVar(1);
+ // concatenate array elements with glue
+ aTermP->assignEmpty();
+ for (uInt16 i=0; i<fldP->arraySize(); i++) {
+ // get array element as string
+ fldP->getArrayField(i)->getAsString(s);
+ // add to output
+ aTermP->appendString(s);
+ if (i+1<fldP->arraySize()) {
+ // we have more elements, add glue
+ aTermP->appendString(glue);
+ }
+ }
+ }; // func_Explode
+
+
+ #ifdef _MSC_VER
+ static long long V_llabs( long long j )
+ {
+ return (j < 0 ? -j : j);
+ }
+ #endif
+
+ // integer ABS(integer val)
+ static void func_Abs(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ #ifdef _MSC_VER
+ aTermP->setAsInteger(V_llabs(aFuncContextP->getLocalVar(0)->getAsInteger()));
+ #else
+ aTermP->setAsInteger(::llabs(aFuncContextP->getLocalVar(0)->getAsInteger()));
+ #endif
+ }; // func_Abs
+
+
+ // integer SIGN(integer val)
+ // i == 0 : 0
+ // i > 0 : 1
+ // i < 0 : -1
+ static void func_Sign(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ fieldinteger_t i = aFuncContextP->getLocalVar(0)->getAsInteger();
+
+ aTermP->setAsInteger(i==0 ? 0 : (i>0 ? 1 : -1));
+ }; // func_Sign
+
+
+ // integer RANDOM(integer range [, integer seed])
+ // generates random number between 0 and range-1. Seed is optional to init random generator
+ static void func_Random(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ // seed if second param there
+ if (aFuncContextP->getLocalVar(1)->isAssigned()) {
+ // seed random gen first
+ srand((unsigned int)aFuncContextP->getLocalVar(1)->getAsInteger());
+ }
+ // now get random value
+ fieldinteger_t r = rand();
+ fieldinteger_t max = RAND_MAX;
+ // scale to specified range
+ aTermP->setAsInteger(r * aFuncContextP->getLocalVar(0)->getAsInteger() / (max+1));
+ }; // func_Random
+
+
+
+ // string NUMFORMAT(integer num, integer digits [,string filler=" " [,boolean opts=""]])
+ static void func_NumFormat(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ // mandatory params
+ fieldinteger_t i = aFuncContextP->getLocalVar(0)->getAsInteger();
+ sInt16 numdigits = aFuncContextP->getLocalVar(1)->getAsInteger(); // negative = left justified
+ // optional params
+ string filler = " "; // default to filling with spaces
+ if (aFuncContextP->getLocalVar(2)->isAssigned())
+ aFuncContextP->getLocalVar(2)->getAsString(filler); // empty: no filling, only truncation
+ string opts;
+ aFuncContextP->getLocalVar(3)->getAsString(opts); // +: show plus sign. space: show space as plus sign
+
+ // generate raw string
+ string s;
+ // - determine hex mode
+ bool hex = opts.find("x")!=string::npos;
+ // - create sign
+ char sign = 0;
+ if (!hex) {
+ if (i<0)
+ sign = '-';
+ else {
+ if (opts.find("+")!=string::npos) sign='+';
+ else if (opts.find(" ")!=string::npos) sign=' ';
+ }
+ }
+ // create raw numeric string
+ if (hex) {
+ StringObjPrintf(s,"%llX",(long long)i);
+ }
+ else {
+ #ifdef _MSC_VER
+ StringObjPrintf(s,"%lld",V_llabs(i));
+ #else
+ StringObjPrintf(s,"%lld",::llabs(i));
+ #endif
+ }
+ // adjust
+ char c = *(filler.c_str()); // NUL or filler char
+ if (c!='0' && sign) {
+ s.insert(0,1,sign); // no zero-padding: insert sign before padding
+ sign=0; // done now
+ }
+ sInt32 n,sz = s.size() + (sign ? 1 : 0); // leave room for sign after zero padding
+ if (numdigits>0) {
+ // right aligned field
+ n = numdigits-sz; // empty space in field
+ if (n<0)
+ s.erase(0,-n); // delete at beginning
+ else if (n>0 && c)
+ s.insert(0,n,c); // insert at beginning
+ }
+ else {
+ // left aligned field
+ n = -numdigits-sz; // empty space in field
+ if (n<0)
+ s.erase(sz-n,-n); // delete at end
+ else if (n>0 && c)
+ s.insert(sz,n,c); // insert at end
+ }
+ // insert plus now if filled with zeroes
+ if (sign)
+ s.insert(0,1,sign); // insert sign after zero padding
+ // return string
+ aTermP->setAsString(s);
+ } // func_NumFormat
+
+
+ // integer LENGTH(string)
+ static void func_Length(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ TItemField *fldP = aFuncContextP->getLocalVar(0);
+ fieldinteger_t siz;
+ if (fldP->isBasedOn(fty_string)) {
+ // don't get value to avoid pulling large strings just for size
+ siz=static_cast<TStringField *>(fldP)->getStringSize();
+ }
+ else {
+ // brute force
+ string s;
+ fldP->getAsString(s);
+ siz=s.size();
+ }
+ // save result
+ aTermP->setAsInteger(siz);
+ }; // func_Length
+
+
+ // integer SIZE(&var)
+ static void func_Size(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ TItemField *fldP = aFuncContextP->getLocalVar(0);
+ fieldinteger_t siz;
+ if (fldP->isArray()) {
+ siz=fldP->arraySize();
+ }
+ else if (fldP->isBasedOn(fty_string)) {
+ // don't get value to avoid pulling large strings just for size
+ siz=static_cast<TStringField *>(fldP)->getStringSize();
+ }
+ else {
+ // brute force
+ string s;
+ fldP->getAsString(s);
+ siz=s.size();
+ }
+ // save result
+ aTermP->setAsInteger(siz);
+ }; // func_Size
+
+
+
+ // integer FIND(string, pattern [, startat])
+ static void func_Find(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ // get params
+ string s,pat;
+ string::size_type p;
+ aFuncContextP->getLocalVar(0)->getAsString(s);
+ aFuncContextP->getLocalVar(1)->getAsString(pat);
+ // optional param
+ uInt32 i=aFuncContextP->getLocalVar(2)->getAsInteger(); // returns 0 if unassigned
+ // find in string
+ if (i<s.size())
+ p=s.find(pat,i);
+ else
+ p=string::npos;
+ // return UNASSIGNED for "not found" and position otherwise
+ if (p==string::npos) aTermP->unAssign();
+ else aTermP->setAsInteger(p);
+ }; // func_Find
+
+
+ // integer RFIND(string, pattern [, startat])
+ static void func_RFind(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ // get params
+ string s,pat;
+ string::size_type p;
+ aFuncContextP->getLocalVar(0)->getAsString(s);
+ aFuncContextP->getLocalVar(1)->getAsString(pat);
+ // optional param
+ uInt32 i=aFuncContextP->getLocalVar(2)->getAsInteger(); // returns 0 if unassigned
+ if (i>s.size()) i=s.size();
+ // find in string
+ p=s.rfind(pat,i);
+ // return UNASSIGNED for "not found" and position otherwise
+ if (p==string::npos) aTermP->unAssign();
+ else aTermP->setAsInteger(p);
+ }; // func_RFind
+
+
+ #ifdef REGEX_SUPPORT
+
+ // run PCRE regexp
+ // Returns: > 0 => success; value is the number of elements filled in
+ // = 0 => success, but offsets is not big enough
+ // -1 => failed to match
+ // -2 => PCRE_ERROR_NULL => did not compile, error reported to aDbgLogger
+ // < -2 => some kind of unexpected problem
+ static int run_pcre(cAppCharP aRegEx, cAppCharP aSubject, stringSize aSubjLen, stringSize aSubjStart, int *aOutVec, int aOVSize, TDebugLogger *aDbgLogger)
+ {
+ string regexpat;
+ // set default options
+ int options=0;
+ // scan input pattern. If it starts with /, we assume /xxx/opt form
+ cAppCharP p = aRegEx;
+ char c=*p;
+ if (c=='/') {
+ // delimiter found
+ p++;
+ // - now search end
+ while (*p) {
+ if (*p=='\\') {
+ // escaped char
+ p++;
+ if (*p) p++;
+ }
+ else {
+ if (*p==c) {
+ // found end of regex
+ size_t n=p-aRegEx-1; // size of plain regExp
+ // - scan options
+ cAppCharP o = p++;
+ while (*o) {
+ switch (*o) {
+ case 'i' : options |= PCRE_CASELESS; break;
+ case 'm' : options |= PCRE_MULTILINE; break;
+ case 's' : options |= PCRE_DOTALL; break;
+ case 'x' : options |= PCRE_EXTENDED; break;
+ case 'U' : options |= PCRE_UNGREEDY; break;
+ }
+ o++;
+ }
+ // - extract regex itself
+ regexpat.assign(aRegEx+1,n);
+ aRegEx = regexpat.c_str();
+ break; // done
+ }
+ p++;
+ }
+ } // while chars in regex
+ } // if regex with delimiter
+ // - compile regex
+ pcre *regex;
+ cAppCharP errMsg=NULL;
+ int errOffs=0;
+ regex = pcre_compile(aRegEx, options | PCRE_UTF8, &errMsg, &errOffs, NULL);
+ if (regex==NULL) {
+ // error, display it in log if script logging is on
+ PLOGDEBUGPRINTFX(aDbgLogger,DBG_SCRIPTS+DBG_ERROR,(
+ "RegEx error at pattern pos %d: %s ",
+ errOffs,
+ errMsg ? errMsg : "<unknown>"
+ ));
+ return PCRE_ERROR_NULL; // -2, regexp did not compile
+ }
+ else {
+ // regExp is ok and can be executed agains subject
+ return pcre_exec(regex, NULL, aSubject, aSubjLen, aSubjStart, 0, aOutVec, aOVSize);
+ }
+ } // run_pcre
+
+
+ // integer REGEX_FIND(string subject, string pattern [, integer startat])
+ static void func_Regex_Find(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ // get params
+ string s,pat;
+ aFuncContextP->getLocalVar(0)->getAsString(s);
+ aFuncContextP->getLocalVar(1)->getAsString(pat);
+ // optional param
+ sInt16 i=aFuncContextP->getLocalVar(2)->getAsInteger(); // returns 0 if unassigned
+ // use PCRE to find
+ const int ovsize=3; // we need no matches
+ int ov[ovsize];
+ int rc = run_pcre(pat.c_str(),s.c_str(),s.size(),i,ov,ovsize,aFuncContextP->getDbgLogger());
+ if (rc>=0) {
+ // return start position
+ aTermP->setAsInteger(ov[0]);
+ }
+ else {
+ // return UNASSIGNED for "not found" and error
+ aTermP->unAssign();
+ }
+ }; // func_Regex_Find
+
+
+ // integer REGEX_MATCH(string subject, string regexp, integer startat, array &matches)
+ static void func_Regex_Match(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ // get params
+ string s,pat;
+ aFuncContextP->getLocalVar(0)->getAsString(s);
+ aFuncContextP->getLocalVar(1)->getAsString(pat);
+ sInt32 i=aFuncContextP->getLocalVar(2)->getAsInteger();
+ TItemField *matchesP = aFuncContextP->getLocalVar(3);
+ string m;
+ // use PCRE to find
+ const int ovsize=54; // max matches (they say this must be a multiple of 3, no idea why; I'd say 2...)
+ int ov[ovsize];
+ int rc = run_pcre(pat.c_str(),s.c_str(),s.size(),i,ov,ovsize,aFuncContextP->getDbgLogger());
+ if (rc>0) {
+ // return start position
+ aTermP->setAsInteger(ov[0]);
+ // return matches
+ int mIdx;
+ TItemField *fldP;
+ for (mIdx=0; mIdx<rc; mIdx++) {
+ // get field to assign match to
+ if (matchesP->isArray())
+ fldP = matchesP->getArrayField(mIdx);
+ else {
+ // non-array specified
+ fldP = matchesP;
+ // - if there are no subpatterns, assign the first match (entire pattern)
+ // - if there are subpatterns, assign the first subpattern match
+ if (rc>1) {
+ // there is at least one subpattern
+ mIdx++; // skip the entire pattern match such that 1st subpattern gets assigned
+ }
+ }
+ // assign match (first is entire pattern)
+ fldP->setAsString(s.c_str()+ov[mIdx*2],ov[mIdx*2+1]-ov[mIdx*2]); // assign substring
+ // end if matches is not an array
+ if (!matchesP->isArray())
+ break;
+ }
+ }
+ else {
+ // return UNASSIGNED for "not found" and all errors
+ aTermP->unAssign();
+ }
+ }; // func_Regex_Match
+
+
+ // integer REGEX_SPLIT(string subject, string separatorregexp, array elements [, boolean emptyElements])
+ // returns number of elements created in elements array
+ static void func_Regex_Split(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ // get params
+ string s,pat;
+ aFuncContextP->getLocalVar(0)->getAsString(s);
+ aFuncContextP->getLocalVar(1)->getAsString(pat);
+ TItemField *elementsP = aFuncContextP->getLocalVar(2);
+ // optional params
+ bool emptyElements = aFuncContextP->getLocalVar(3)->getAsBoolean(); // skip empty elements by default
+ // find all recurrences of separator
+ uInt32 i = 0; // start at beginning
+ sInt32 rIdx = 0; // no results so far
+ while (i<s.size()) {
+ // use PCRE to find
+ const int ovsize=3; // we need no matches
+ int ov[ovsize];
+ int rc = run_pcre(pat.c_str(),s.c_str(),s.size(),i,ov,ovsize,aFuncContextP->getDbgLogger());
+ if (rc<=0) {
+ // no further match found
+ // - simulate match at end of string
+ ov[0]=s.size();
+ ov[1]=ov[0];
+ }
+ // copy element
+ if (uInt32(ov[0])>i || emptyElements) {
+ TItemField *fldP = elementsP->getArrayField(rIdx);
+ if (ov[0]-i>0)
+ fldP->setAsString(s.c_str()+i,ov[0]-i);
+ else
+ fldP->assignEmpty(); // empty element
+ // next element
+ rIdx++;
+ }
+ // skip separator
+ i = ov[1];
+ } // while not at end of string
+ // return number of elements found
+ aTermP->setAsInteger(rIdx);
+ }; // func_Regex_Split
+
+
+ // string REGEX_REPLACE(string,regexp,replacement [,startat [,repeat]])
+ static void func_Regex_Replace(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ // get params
+ string s,pat,reppat,res;
+ aFuncContextP->getLocalVar(0)->getAsString(s);
+ aFuncContextP->getLocalVar(1)->getAsString(pat);
+ aFuncContextP->getLocalVar(2)->getAsString(reppat);
+ // optional params
+ sInt16 i=aFuncContextP->getLocalVar(3)->getAsInteger(); // returns 0 if unassigned -> start at beginning
+ sInt32 c=aFuncContextP->getLocalVar(4)->getAsInteger(); // returns 0 if unassigned -> replace all
+ // use PCRE to find
+ const int ovsize=54; // max matches (they say this must be a multiple of 3, no idea why; I'd say 2...)
+ int ov[ovsize];
+ res.assign(s.c_str(),i); // part of string not searched at all
+ do {
+ int rc = run_pcre(pat.c_str(),s.c_str(),s.size(),i,ov,ovsize,aFuncContextP->getDbgLogger());
+ if (rc<0)
+ break; // error or no more matches found
+ // found an occurrence
+ // - subsititute matches in replacement string
+ cAppCharP p=reppat.c_str();
+ cAppCharP q=p;
+ string rep;
+ rep.erase();
+ while (*p) {
+ if (*p=='\\') {
+ p++;
+ if (*p==0) break;
+ // check for escaped backslash
+ if (*p=='\\') { p++; continue; }
+ // get replacement number
+ if (isdigit(*p)) {
+ // is replacement escape sequence, get index
+ uInt16 mIdx = *(p++)-'0';
+ // append chars before \x
+ rep.append(q,p-q-2);
+ // append match (if there is one)
+ if (mIdx<rc)
+ rep.append(s.c_str(),ov[mIdx*2],ov[mIdx*2+1]-ov[mIdx*2]);
+ // update rest pointer
+ q=p;
+ continue;
+ }
+ }
+ p++; // next
+ }
+ rep.append(q); // copy rest of pattern
+ // - insert replacement string into result
+ res.append(s.c_str(),i,ov[0]-i); // from previous match or beginning of string to current match
+ res.append(rep); // replacement
+ i=ov[1]; // search continues here
+ } while (c<=0 || (--c)>0); // if c==0, replace all, otherwise as many times as c says
+ res.append(s.c_str()+i); // rest
+ // return result
+ aTermP->setAsString(res);
+ }; // func_Regex_Replace
+
+ #endif // REGEX_SUPPORT
+
+
+ // integer COMPARE(value, value)
+ // - returns 0 if equal, 1 if first > second, -1 if first < second,
+ // SYSYNC_NOT_COMPARABLE if not equal and no ordering known or if field
+ // types do not match.
+ static void func_Compare(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ // compare first field with second one
+ aTermP->setAsInteger(
+ aFuncContextP->getLocalVar(0)->compareWith(*(aFuncContextP->getLocalVar(1)))
+ );
+ }; // func_Compare
+
+
+ // integer CONTAINS(&ref, value [,bool caseinsensitive])
+ // - returns 1 if value contained in ref, 0 if not
+ static void func_Contains(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ // check if second is contained in first
+ bool caseinsensitive = aFuncContextP->getLocalVar(2)->getAsBoolean(); // returns false if not specified
+ aTermP->setAsBoolean(
+ aFuncContextP->getLocalVar(0)->contains(*(aFuncContextP->getLocalVar(1)),caseinsensitive)
+ );
+ }; // func_Contains
+
+
+ // APPEND(&ref, value)
+ // - appends value to ref (ref can be array)
+ static void func_Append(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ // append second to first
+ aFuncContextP->getLocalVar(0)->append(*(aFuncContextP->getLocalVar(1)));
+ }; // func_Append
+
+
+
+ // string UPPERCASE(string)
+ static void func_UpperCase(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ string s;
+ TItemField *fldP = aFuncContextP->getLocalVar(0);
+ if (fldP->isAssigned()) {
+ fldP->getAsString(s);
+ StringUpper(s);
+ // save result
+ aTermP->setAsString(s);
+ }
+ else {
+ aTermP->unAssign();
+ }
+ }; // func_UpperCase
+
+
+ // string LOWERCASE(string)
+ static void func_LowerCase(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ string s;
+ TItemField *fldP = aFuncContextP->getLocalVar(0);
+ if (fldP->isAssigned()) {
+ fldP->getAsString(s);
+ StringLower(s);
+ // save result
+ aTermP->setAsString(s);
+ }
+ else {
+ aTermP->unAssign();
+ }
+ }; // func_LowerCase
+
+
+ // string NORMALIZED(variant value)
+ // get as normalized string (trimmed CR/LF/space at both ends, no special chars for telephone numbers, http:// added for URLs w/o protocol spec)
+ static void func_Normalized(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ // get field reference
+ TItemField *fldP = aFuncContextP->getLocalVar(0);
+ if (fldP->isAssigned()) {
+ // get normalized version
+ string s;
+ fldP->getAsNormalizedString(s);
+ // save it
+ aTermP->setAsString(s);
+ }
+ else {
+ aTermP->unAssign();
+ }
+ }; // func_Normalized
+
+
+ // bool ISAVAILABLE(variant &fieldvar)
+ // check if field is available (supported by both ends)
+ // - returns EMPTY if availability is not known
+ // - fieldvar must be a field contained in the primary item of the caller, else function returns UNASSIGNED
+ static void func_IsAvailable(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ if (aFuncContextP->fParentContextP) {
+ // get item to find field in
+ TMultiFieldItem *checkItemP = aFuncContextP->fParentContextP->fTargetItemP;
+ // check if this item's type has actually received availability info
+ if (!checkItemP->knowsRemoteFieldOptions()) {
+ aTermP->assignEmpty(); // nothing known about field availability
+ return;
+ }
+ else {
+ // we have availability info
+ // - get index of field by field pointer (passed by reference)
+ sInt16 fid = checkItemP->getIndexOfField(aFuncContextP->getLocalVar(0));
+ if (fid!=FID_NOT_SUPPORTED) {
+ // field exists, return availability
+ aTermP->setAsBoolean(checkItemP->isAvailable(fid));
+ return;
+ }
+ }
+ }
+ // no parent context or field not found
+ aTermP->unAssign();
+ }; // func_IsAvailable
+
+
+ // string ITEMDATATYPE()
+ // returns the type's internal name (like "vcard21")
+ static void func_ItemDataType(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ if (aFuncContextP->fParentContextP) {
+ // get item of which we want to know the type
+ TMultiFieldItem *checkItemP = aFuncContextP->fParentContextP->fTargetItemP;
+ if (checkItemP) {
+ TMultiFieldItemType *mfitP = static_cast<TMultiFieldItemType *>(checkItemP->getItemType());
+ if (mfitP) {
+ aTermP->setAsString(mfitP->getTypeConfig()->getName());
+ return;
+ }
+ }
+ }
+ // no type associated or no item in current context
+ aTermP->unAssign();
+ }; // func_ItemDataType
+
+
+ // string ITEMTYPENAME()
+ // returns the type's name (like "text/x-vcard")
+ static void func_ItemTypeName(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ if (aFuncContextP->fParentContextP) {
+ // get item of which we want to know the type
+ TMultiFieldItem *checkItemP = aFuncContextP->fParentContextP->fTargetItemP;
+ if (checkItemP) {
+ TMultiFieldItemType *mfitP = static_cast<TMultiFieldItemType *>(checkItemP->getItemType());
+ if (mfitP) {
+ aTermP->setAsString(mfitP->getTypeName());
+ return;
+ }
+ }
+ }
+ // no type associated or no item in current context
+ aTermP->unAssign();
+ }; // func_ItemTypeName
+
+
+ // string ITEMTYPEVERS()
+ // returns the type's version string (like "2.1")
+ static void func_ItemTypeVers(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ if (aFuncContextP->fParentContextP) {
+ // get item of which we want to know the type
+ TMultiFieldItem *checkItemP = aFuncContextP->fParentContextP->fTargetItemP;
+ if (checkItemP) {
+ TMultiFieldItemType *mfitP = static_cast<TMultiFieldItemType *>(checkItemP->getItemType());
+ if (mfitP) {
+ aTermP->setAsString(mfitP->getTypeVers());
+ return;
+ }
+ }
+ }
+ // no type associated or no item in current context
+ aTermP->unAssign();
+ }; // func_ItemTypeVers
+
+
+
+ // void SWAP(untyped1,untyped2)
+ static void func_Swap(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ // get params
+ TItemField *p1 = aFuncContextP->getLocalVar(0);
+ TItemField *p2 = aFuncContextP->getLocalVar(1);
+ TItemField *tempP = newItemField(p1->getType(), aFuncContextP->getSessionZones());
+
+ // swap
+ (*tempP)=(*p1);
+ (*p1)=(*p2);
+ (*p2)=(*tempP);
+ }; // func_Swap
+
+
+ // string TYPENAME(untyped1)
+ static void func_TypeName(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ // get params
+ TItemFieldTypes ty = aFuncContextP->getLocalVar(0)->getType();
+ // return type name
+ aTermP->setAsString(ItemFieldTypeNames[ty]);
+ }; // func_TypeName
+
+
+ // variant SESSIONVAR(string varname)
+ static void func_SessionVar(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ TItemField *sessionVarP;
+ TScriptVarDef *sessionVarDefP;
+ string varname;
+ TScriptContext *sessionContextP=NULL;
+
+ // get name
+ aFuncContextP->getLocalVar(0)->getAsString(varname);
+ // get variable from session
+ if (aFuncContextP->getSession())
+ sessionContextP=aFuncContextP->getSession()->getSessionScriptContext();
+ if (sessionContextP) {
+ // get definition
+ sessionVarDefP = sessionContextP->getVarDef(
+ varname.c_str(),varname.size()
+ );
+ if (sessionVarDefP) {
+ // get variable
+ sessionVarP = sessionContextP->getLocalVar(sessionVarDefP->fIdx);
+ if (sessionVarP) {
+ // create result field of appropriate type
+ aTermP = newItemField(sessionVarP->getType(), aFuncContextP->getSessionZones());
+ // copy value
+ (*aTermP) = (*sessionVarP);
+ }
+ }
+ }
+ if (!aTermP) {
+ // if no such variable found, return unassigned (but not no-value, which would abort script)
+ aTermP=newItemField(fty_none, aFuncContextP->getSessionZones());
+ aTermP->unAssign(); // make it (already is...) unassigned
+ }
+ }; // func_SessionVar
+
+
+ // SETSESSIONVAR(string varname, value)
+ static void func_SetSessionVar(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ TItemField *sessionVarP;
+ TScriptVarDef *sessionVarDefP;
+ string varname;
+ TScriptContext *sessionContextP=NULL;
+
+ // get name
+ aFuncContextP->getLocalVar(0)->getAsString(varname);
+ // get variable from session
+ if (aFuncContextP->getSession()) sessionContextP=aFuncContextP->getSession()->getSessionScriptContext();
+ if (sessionContextP) {
+ // get definition
+ sessionVarDefP = sessionContextP->getVarDef(
+ varname.c_str(),varname.size()
+ );
+ if (sessionVarDefP) {
+ // get variable
+ sessionVarP = sessionContextP->getLocalVar(sessionVarDefP->fIdx);
+ if (sessionVarP) {
+ // store new value
+ (*sessionVarP) = (*(aFuncContextP->getLocalVar(1)));
+ }
+ }
+ }
+ }; // func_SetSessionVar
+
+
+ // void ABORTSESSION(integer statuscode)
+ static void func_AbortSession(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ TSyncSession *sessionP = aFuncContextP->getSession();
+ if (sessionP) {
+ sessionP->AbortSession(aFuncContextP->getLocalVar(0)->getAsInteger(),true); // locally caused
+ }
+ }; // func_AbortSession
+
+
+ // string CONFIGVAR(string varname)
+ static void func_ConfigVar(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ string varname,value;
+
+ // get name
+ aFuncContextP->getLocalVar(0)->getAsString(varname);
+ // get value from syncappbase
+ if (!aFuncContextP->getSyncAppBase()->getConfigVar(varname.c_str(),value))
+ aTermP->setAsString(value);
+ else
+ aTermP->unAssign(); // not found
+ }; // func_ConfigVar
+
+
+ // timestamp RECURRENCE_DATE(
+ // timestamp start,
+ // string rr_freq, integer interval,
+ // integer fmask, integer lmask,
+ // boolean occurrencecount,
+ // integer count
+ // )
+ static void func_Recurrence_Date(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ // get params
+ string rr_freq;
+ TTimestampField *startFldP = static_cast<TTimestampField *>(aFuncContextP->getLocalVar(0));
+ timecontext_t tctx;
+ // - get start timestamp as is along with current context
+ lineartime_t start = startFldP->getTimestampAs(TCTX_UNKNOWN,&tctx);
+ aFuncContextP->getLocalVar(1)->getAsString(rr_freq);
+ char freq = rr_freq.size()>0 ? rr_freq[0] : ' ';
+ char freqmod = rr_freq.size()>1 ? rr_freq[1] : ' ';
+ sInt16 interval = aFuncContextP->getLocalVar(2)->getAsInteger();
+ fieldinteger_t fmask = aFuncContextP->getLocalVar(3)->getAsInteger();
+ fieldinteger_t lmask = aFuncContextP->getLocalVar(4)->getAsInteger();
+ bool occurrencecount = aFuncContextP->getLocalVar(5)->getAsBoolean();
+ uInt16 count = aFuncContextP->getLocalVar(6)->getAsInteger();
+ // now calculate
+ lineartime_t occurrence;
+ if(endDateFromCount(
+ occurrence,
+ start,
+ freq,freqmod,
+ interval,
+ fmask,lmask,
+ count,
+ occurrencecount,
+ aFuncContextP->getDbgLogger()
+ )) {
+ // successful, set timestamp in same context as start timestamp had
+ static_cast<TTimestampField *>(aTermP)->setTimestampAndContext(occurrence,tctx);
+ }
+ else {
+ // unsuccessful
+ aTermP->unAssign();
+ }
+ } // func_Recurrence_Date
+
+
+ // integer RECURRENCE_COUNT(
+ // timestamp start,
+ // string rr_freq, integer interval,
+ // integer fmask, integer lmask,
+ // boolean occurrencecount,
+ // timestamp occurrence
+ // )
+ static void func_Recurrence_Count(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ // get params
+ string rr_freq;
+ timecontext_t tctx;
+ // - start time context is used for rule evaluation
+ lineartime_t start = static_cast<TTimestampField *>(aFuncContextP->getLocalVar(0))->getTimestampAs(TCTX_UNKNOWN,&tctx);
+ aFuncContextP->getLocalVar(1)->getAsString(rr_freq);
+ char freq = rr_freq.size()>0 ? rr_freq[0] : ' ';
+ char freqmod = rr_freq.size()>1 ? rr_freq[1] : ' ';
+ sInt16 interval = aFuncContextP->getLocalVar(2)->getAsInteger();
+ fieldinteger_t fmask = aFuncContextP->getLocalVar(3)->getAsInteger();
+ fieldinteger_t lmask = aFuncContextP->getLocalVar(4)->getAsInteger();
+ bool occurrencecount = aFuncContextP->getLocalVar(5)->getAsBoolean();
+ // - get end date / recurrence to get count for
+ // Note: this is obtained in the same context as start, even if it might be in another context
+ lineartime_t occurrence = static_cast<TTimestampField *>(aFuncContextP->getLocalVar(6))->getTimestampAs(tctx);
+ // now calculate
+ sInt16 count;
+ if(countFromEndDate(
+ count, occurrencecount,
+ start,
+ freq,freqmod,
+ interval,
+ fmask,lmask,
+ occurrence,
+ aFuncContextP->getDbgLogger()
+ )) {
+ // successful
+ aTermP->setAsInteger(count);
+ }
+ else {
+ // unsuccessful
+ aTermP->unAssign();
+ }
+ } // func_Recurrence_Count
+
+
+
+ // string MAKE_RRULE(
+ // boolean rrule2,
+ // string rr_freq, integer interval,
+ // integer fmask, integer lmask,
+ // timestamp until
+ // )
+ static void func_Make_RRULE(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ // get params
+ string rr_freq;
+ timecontext_t untilcontext;
+ // - start time context is used for rule evaluation
+ bool rruleV2 = aFuncContextP->getLocalVar(0)->getAsBoolean();
+ aFuncContextP->getLocalVar(1)->getAsString(rr_freq);
+ char freq = rr_freq.size()>0 ? rr_freq[0] : ' ';
+ char freqmod = rr_freq.size()>1 ? rr_freq[1] : ' ';
+ sInt16 interval = aFuncContextP->getLocalVar(2)->getAsInteger();
+ fieldinteger_t fmask = aFuncContextP->getLocalVar(3)->getAsInteger();
+ fieldinteger_t lmask = aFuncContextP->getLocalVar(4)->getAsInteger();
+ lineartime_t until = static_cast<TTimestampField *>(aFuncContextP->getLocalVar(5))->getTimestampAs(TCTX_UNKNOWN,&untilcontext);
+ // convert to RRULE string
+ string rrule;
+ bool ok;
+ if (rruleV2)
+ ok = internalToRRULE2(rrule,freq,freqmod,interval,fmask,lmask,until,untilcontext,aFuncContextP->getDbgLogger());
+ else
+ ok = internalToRRULE1(rrule,freq,freqmod,interval,fmask,lmask,until,untilcontext,aFuncContextP->getDbgLogger());
+ if (ok) {
+ // successful
+ aTermP->setAsString(rrule);
+ }
+ else {
+ // unsuccessful
+ aTermP->unAssign();
+ }
+ } // func_Make_RRULE
+
+
+ // boolean PARSE_RRULE(
+ // boolean rruleV2,
+ // string rrule,
+ // timestamp start,
+ // string &rr_freq, integer &interval,
+ // integer &fmask, integer &lmask,
+ // timestamp &until
+ // )
+ static void func_Parse_RRULE(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ timecontext_t startcontext;
+ char freq[3] = {' ', ' ', 0};
+ sInt16 interval;
+ fieldinteger_t fmask,lmask;
+ lineartime_t until;
+ timecontext_t untilcontext;
+
+ // get params
+ // - start time context is used for rule evaluation
+ bool rruleV2 = aFuncContextP->getLocalVar(0)->getAsBoolean();
+ string rrule;
+ aFuncContextP->getLocalVar(1)->getAsString(rrule);
+ lineartime_t start = static_cast<TTimestampField *>(aFuncContextP->getLocalVar(2))->getTimestampAs(TCTX_UNKNOWN,&startcontext);
+ bool ok;
+ if (rruleV2)
+ ok = RRULE2toInternal(rrule.c_str(),start,startcontext,freq[0],freq[1],interval,fmask,lmask,until,untilcontext,aFuncContextP->getDbgLogger());
+ else
+ ok = RRULE1toInternal(rrule.c_str(),start,startcontext,freq[0],freq[1],interval,fmask,lmask,until,untilcontext,aFuncContextP->getDbgLogger());
+ if (ok) {
+ // successful
+ // - save return values
+ aFuncContextP->getLocalVar(3)->setAsString(freq);
+ aFuncContextP->getLocalVar(4)->setAsInteger(interval);
+ aFuncContextP->getLocalVar(5)->setAsInteger(fmask);
+ aFuncContextP->getLocalVar(6)->setAsInteger(lmask);
+ static_cast<TTimestampField *>(aFuncContextP->getLocalVar(7))->setTimestampAndContext(until, untilcontext);
+ }
+ aTermP->setAsBoolean(ok);
+ } // func_Parse_RRULE
+
+
+
+ // integer PARSEEMAILSPEC(string emailspec, string &name, string &email)
+ static void func_ParseEmailSpec(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ string spec,name,addr;
+ aFuncContextP->getLocalVar(0)->getAsString(spec);
+ cAppCharP e = parseRFC2822AddrSpec(spec.c_str(),name,addr);
+ aTermP->setAsInteger(e-spec.c_str()); // return number of chars parsed
+ aFuncContextP->getLocalVar(1)->setAsString(name);
+ aFuncContextP->getLocalVar(2)->setAsString(addr);
+ } // func_ParseEmailSpec
+
+
+ // string MAKEEMAILSPEC(string name, string email)
+ static void func_MakeEmailSpec(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ string spec,name,addr;
+ aFuncContextP->getLocalVar(0)->getAsString(name);
+ aFuncContextP->getLocalVar(1)->getAsString(addr);
+ makeRFC2822AddrSpec(name.c_str(),addr.c_str(),spec);
+ aTermP->setAsString(spec); // return RFC2822 email address specification
+ } // func_MakeEmailSpec
+
+
+ // helper to create profile handler by name
+ // - returns NULL if no such profile name or profile's field list does not match the item's fieldlist
+ static TProfileHandler *newProfileHandlerByName(cAppCharP aProfileName, TMultiFieldItem *aItemP)
+ {
+ // get type registry to find profile config in
+ TMultiFieldDatatypesConfig *mufcP;
+ GET_CASTED_PTR(mufcP,TMultiFieldDatatypesConfig,aItemP->getItemType()->getTypeConfig()->getParentElement(),"PARSETEXTWITHPROFILE/MAKETEXTWITHPROFILE used with non-multifield item");
+ // get profile config from type registry
+ TProfileConfig *profileConfig = mufcP->getProfile(aProfileName);
+ if (profileConfig) {
+ // create a profile handler for the item type
+ return profileConfig->newProfileHandler(aItemP->getItemType());
+ }
+ return NULL; // no such profile
+ }
+
+
+ // integer PARSETEXTWITHPROFILE(string textformat, string profileName [, int mode])
+ static void func_ParseTextWithProfile(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ bool ok = false;
+ if (aFuncContextP->fParentContextP) {
+ // get the item to work with
+ TMultiFieldItem *itemP = aFuncContextP->fParentContextP->fTargetItemP;
+ // get a handler by name
+ string s;
+ aFuncContextP->getLocalVar(1)->getAsString(s);
+ TProfileHandler *profileHandlerP = newProfileHandlerByName(s.c_str(), itemP);
+ if (profileHandlerP) {
+ // now we can convert
+ // - set the mode code (none = 0 = default)
+ profileHandlerP->setProfileMode(aFuncContextP->getLocalVar(2)->getAsInteger());
+ profileHandlerP->setRelatedDatastore(NULL); // no datastore in particular is related
+ // - convert
+ aFuncContextP->getLocalVar(0)->getAsString(s);
+ ok = profileHandlerP->parseText(s.c_str(), s.size(), *itemP);
+ // - forget
+ delete profileHandlerP;
+ }
+ }
+ aTermP->setAsBoolean(ok);
+ } // func_ParseTextWithProfile
+
+
+ // string MAKETEXTWITHPROFILE(string profileName [, int mode])
+ static void func_MakeTextWithProfile(TItemField *&aTermP, TScriptContext *aFuncContextP)
+ {
+ if (aFuncContextP->fParentContextP) {
+ // get the item to work with
+ TMultiFieldItem *itemP = aFuncContextP->fParentContextP->fTargetItemP;
+ // get a handler by name
+ string s;
+ aFuncContextP->getLocalVar(0)->getAsString(s);
+ TProfileHandler *profileHandlerP = newProfileHandlerByName(s.c_str(), itemP);
+ if (profileHandlerP) {
+ // now we can convert
+ // - set the mode code (none = 0 = default)
+ profileHandlerP->setProfileMode(aFuncContextP->getLocalVar(1)->getAsInteger());
+ profileHandlerP->setRelatedDatastore(NULL); // no datastore in particular is related
+ // - convert, after clearing the string (some generateText() implementations
+ // append instead of overwriting)
+ s = "";
+ profileHandlerP->generateText(*itemP,s);
+ aTermP->setAsString(s); // return text generated according to profile
+ }
+ }
+ } // func_MakeTextWithProfile
+
+
+
+}; // TBuiltinStdFuncs
+
+
+const uInt8 param_oneTimestamp[] = { VAL(fty_timestamp) };
+const uInt8 param_oneInteger[] = { VAL(fty_integer) };
+const uInt8 param_oneString[] = { VAL(fty_string) };
+const uInt8 param_oneVariant[] = { VAL(fty_none) };
+const uInt8 param_oneOptInteger[] = { OPTVAL(fty_integer) };
+
+const uInt8 param_Random[] = { VAL(fty_integer), OPTVAL(fty_integer) };
+const uInt8 param_SetTimezone[] = { REF(fty_timestamp), VAL(fty_none) };
+const uInt8 param_SetFloating[] = { REF(fty_timestamp) };
+const uInt8 param_ConvertToZone[] = { VAL(fty_timestamp), VAL(fty_none), OPTVAL(fty_integer) };
+const uInt8 param_ConvertToUserZone[] = { VAL(fty_timestamp), OPTVAL(fty_integer) };
+const uInt8 param_Shellexecute[] = { VAL(fty_string), VAL(fty_string), OPTVAL(fty_integer) };
+const uInt8 param_substr[] = { VAL(fty_string), VAL(fty_integer), OPTVAL(fty_integer) };
+const uInt8 param_size[] = { REF(fty_none) };
+const uInt8 param_Normalized[] = { REF(fty_none) };
+const uInt8 param_find[] = { VAL(fty_string), VAL(fty_string), OPTVAL(fty_integer) };
+const uInt8 param_compare[] = { VAL(fty_none), VAL(fty_none) };
+const uInt8 param_contains[] = { REF(fty_none), VAL(fty_none), OPTVAL(fty_integer) };
+const uInt8 param_append[] = { REF(fty_none), VAL(fty_none) };
+const uInt8 param_swap[] = { REF(fty_none), REF(fty_none) };
+const uInt8 param_isAvailable[] = { REF(fty_none) };
+const uInt8 param_typename[] = { VAL(fty_none) };
+const uInt8 param_SetSessionVar[] = { VAL(fty_string), VAL(fty_none) };
+const uInt8 param_SetDebugOptions[] = { VAL(fty_string), VAL(fty_integer) };
+const uInt8 param_Recurrence_Date[] = { VAL(fty_timestamp), VAL(fty_string), VAL(fty_integer), VAL(fty_integer), VAL(fty_integer), VAL(fty_integer), VAL(fty_integer) };
+const uInt8 param_Recurrence_Count[] = { VAL(fty_timestamp), VAL(fty_string), VAL(fty_integer), VAL(fty_integer), VAL(fty_integer), VAL(fty_integer), VAL(fty_timestamp) };
+const uInt8 param_Make_RRULE[] = { VAL(fty_integer), VAL(fty_string), VAL(fty_integer), VAL(fty_integer), VAL(fty_integer), VAL(fty_timestamp) };
+const uInt8 param_Parse_RRULE[] = { VAL(fty_integer), VAL(fty_string), VAL(fty_timestamp), REF(fty_string), REF(fty_integer), REF(fty_integer), REF(fty_integer), REF(fty_timestamp) };
+const uInt8 param_AlldayCount[] = { VAL(fty_timestamp), VAL(fty_timestamp), OPTVAL(fty_integer), OPTVAL(fty_integer) };
+const uInt8 param_MakeAllday[] = { REF(fty_timestamp), REF(fty_timestamp), OPTVAL(fty_integer) };
+const uInt8 param_NumFormat[] = { VAL(fty_integer), VAL(fty_integer), OPTVAL(fty_string), OPTVAL(fty_string) };
+const uInt8 param_Explode[] = { VAL(fty_string), REFARR(fty_none) };
+const uInt8 param_parseEmailSpec[] = { VAL(fty_string), REF(fty_string), REF(fty_string) };
+const uInt8 param_makeEmailSpec[] = { VAL(fty_string), VAL(fty_string) };
+const uInt8 param_parseTextWithProfile[] = { VAL(fty_string), VAL(fty_string), OPTVAL(fty_integer) };
+const uInt8 param_makeTextWithProfile[] = { VAL(fty_string), OPTVAL(fty_integer) };
+
+
+#ifdef REGEX_SUPPORT
+const uInt8 param_regexfind[] = { VAL(fty_string), VAL(fty_string), OPTVAL(fty_integer) };
+const uInt8 param_regexmatch[] = { VAL(fty_string), VAL(fty_string), VAL(fty_integer), REF(fty_none) };
+const uInt8 param_regexreplace[] = { VAL(fty_string), VAL(fty_string), VAL(fty_string), OPTVAL(fty_integer), OPTVAL(fty_integer) };
+const uInt8 param_regexsplit[] = { VAL(fty_string), VAL(fty_string), REFARR(fty_none), OPTVAL(fty_integer) };
+#endif
+
+// builtin function table
+const TBuiltInFuncDef BuiltInFuncDefs[] = {
+ { "ABS", TBuiltinStdFuncs::func_Abs, fty_integer, 1, param_oneInteger },
+ { "SIGN", TBuiltinStdFuncs::func_Sign, fty_integer, 1, param_oneInteger },
+ { "RANDOM", TBuiltinStdFuncs::func_Random, fty_integer, 2, param_Random },
+ { "NUMFORMAT", TBuiltinStdFuncs::func_NumFormat, fty_string, 4, param_NumFormat },
+ { "NORMALIZED", TBuiltinStdFuncs::func_Normalized, fty_string, 1, param_Normalized },
+ { "ISAVAILABLE", TBuiltinStdFuncs::func_IsAvailable, fty_integer, 1, param_isAvailable },
+ { "ITEMDATATYPE", TBuiltinStdFuncs::func_ItemDataType, fty_string, 0, NULL },
+ { "ITEMTYPENAME", TBuiltinStdFuncs::func_ItemTypeName, fty_string, 0, NULL },
+ { "ITEMTYPEVERS", TBuiltinStdFuncs::func_ItemTypeVers, fty_string, 0, NULL },
+ { "EXPLODE", TBuiltinStdFuncs::func_Explode, fty_string, 2, param_Explode },
+ { "SUBSTR", TBuiltinStdFuncs::func_Substr, fty_string, 3, param_substr },
+ { "LENGTH", TBuiltinStdFuncs::func_Length, fty_integer, 1, param_oneString },
+ { "SIZE", TBuiltinStdFuncs::func_Size, fty_integer, 1, param_size },
+ { "FIND", TBuiltinStdFuncs::func_Find, fty_integer, 3, param_find },
+ { "RFIND", TBuiltinStdFuncs::func_RFind, fty_integer, 3, param_find },
+ #ifdef REGEX_SUPPORT
+ { "REGEX_FIND", TBuiltinStdFuncs::func_Regex_Find, fty_integer, 3, param_regexfind },
+ { "REGEX_MATCH", TBuiltinStdFuncs::func_Regex_Match, fty_integer, 4, param_regexmatch },
+ { "REGEX_SPLIT", TBuiltinStdFuncs::func_Regex_Split, fty_integer, 4, param_regexsplit },
+ { "REGEX_REPLACE", TBuiltinStdFuncs::func_Regex_Replace, fty_string, 5, param_regexreplace },
+ #endif
+ { "COMPARE", TBuiltinStdFuncs::func_Compare, fty_integer, 2, param_compare },
+ { "CONTAINS", TBuiltinStdFuncs::func_Contains, fty_integer, 3, param_contains },
+ { "APPEND", TBuiltinStdFuncs::func_Append, fty_none, 2, param_append },
+ { "UPPERCASE", TBuiltinStdFuncs::func_UpperCase, fty_string, 1, param_oneString },
+ { "LOWERCASE", TBuiltinStdFuncs::func_LowerCase, fty_string, 1, param_oneString },
+ { "SWAP", TBuiltinStdFuncs::func_Swap, fty_none, 2, param_swap },
+ { "TYPENAME", TBuiltinStdFuncs::func_TypeName, fty_string, 1, param_oneVariant },
+ { "REMOTERULENAME", TBuiltinStdFuncs::func_Remoterulename, fty_string, 0, NULL },
+ { "LOCALURI", TBuiltinStdFuncs::func_LocalURI, fty_string, 0, NULL },
+ { "NOW", TBuiltinStdFuncs::func_Now, fty_timestamp, 0, NULL },
+ { "SYSTEMNOW", TBuiltinStdFuncs::func_SystemNow, fty_timestamp, 0, NULL },
+ { "DBNOW", TBuiltinStdFuncs::func_DbNow, fty_timestamp, 0, NULL },
+ { "ZONEOFFSET", TBuiltinStdFuncs::func_ZoneOffset, fty_integer, 1, param_oneTimestamp },
+ { "TIMEZONE", TBuiltinStdFuncs::func_Timezone, fty_string, 1, param_oneTimestamp },
+ { "VTIMEZONE", TBuiltinStdFuncs::func_VTimezone, fty_string, 1, param_oneTimestamp },
+ { "SETTIMEZONE", TBuiltinStdFuncs::func_SetTimezone, fty_none, 2, param_SetTimezone },
+ { "SETFLOATING", TBuiltinStdFuncs::func_SetFloating, fty_none, 1, param_SetFloating },
+ { "CONVERTTOZONE", TBuiltinStdFuncs::func_ConvertToZone, fty_timestamp, 3, param_ConvertToZone },
+ { "CONVERTTOUSERZONE", TBuiltinStdFuncs::func_ConvertToUserZone, fty_timestamp, 2, param_ConvertToUserZone },
+ { "USERTIMEZONE", TBuiltinStdFuncs::func_UserTimezone, fty_string, 0, NULL },
+ { "SETUSERTIMEZONE", TBuiltinStdFuncs::func_SetUserTimezone, fty_none, 1, param_oneVariant },
+ { "ISDATEONLY", TBuiltinStdFuncs::func_IsDateOnly, fty_integer, 1, param_oneTimestamp },
+ { "DATEONLY", TBuiltinStdFuncs::func_DateOnly, fty_timestamp, 1, param_oneTimestamp },
+ { "ISDURATION", TBuiltinStdFuncs::func_IsDuration, fty_integer, 1, param_oneTimestamp },
+ { "DURATION", TBuiltinStdFuncs::func_Duration, fty_timestamp, 1, param_oneTimestamp },
+ { "POINTINTIME", TBuiltinStdFuncs::func_PointInTime, fty_timestamp, 1, param_oneTimestamp },
+ { "ISFLOATING", TBuiltinStdFuncs::func_IsFloating, fty_integer, 1, param_oneTimestamp },
+ { "WEEKDAY", TBuiltinStdFuncs::func_Weekday, fty_integer, 1, param_oneTimestamp },
+ { "SECONDS", TBuiltinStdFuncs::func_Seconds, fty_integer, 1, param_oneInteger },
+ { "MILLISECONDS", TBuiltinStdFuncs::func_Milliseconds, fty_integer, 1, param_oneInteger },
+ { "SLEEPMS", TBuiltinStdFuncs::func_SleepMS, fty_none, 1, param_oneInteger },
+ { "TIMEUNITS", TBuiltinStdFuncs::func_Timeunits, fty_integer, 1, param_oneInteger },
+ { "DAYUNITS", TBuiltinStdFuncs::func_Dayunits, fty_integer, 1, param_oneInteger },
+ { "MONTHDAYS", TBuiltinStdFuncs::func_MonthDays, fty_integer, 1, param_oneTimestamp },
+ { "DEBUGMESSAGE", TBuiltinStdFuncs::func_Debugmessage, fty_none, 1, param_oneString },
+ { "DEBUGSHOWVARS", TBuiltinStdFuncs::func_DebugShowVars, fty_none, 0, NULL },
+ { "DEBUGSHOWITEM", TBuiltinStdFuncs::func_DebugShowItem, fty_none, 1, param_oneOptInteger },
+ { "SETDEBUGOPTIONS", TBuiltinStdFuncs::func_SetDebugOptions, fty_none, 2, param_SetDebugOptions },
+ { "SETDEBUGMASK", TBuiltinStdFuncs::func_SetDebugMask, fty_none, 1, param_oneInteger },
+ { "SETXMLTRANSLATE", TBuiltinStdFuncs::func_SetXMLTranslate, fty_none, 1, param_oneInteger },
+ { "SETMSGDUMP", TBuiltinStdFuncs::func_SetMsgDump, fty_none, 1, param_oneInteger },
+ { "GETDEBUGMASK", TBuiltinStdFuncs::func_GetDebugMask, fty_integer, 0, NULL },
+ { "REQUESTMAXTIME", TBuiltinStdFuncs::func_RequestMaxTime, fty_none, 1, param_oneInteger },
+ { "REQUESTMINTIME", TBuiltinStdFuncs::func_RequestMinTime, fty_none, 1, param_oneInteger },
+ { "SHELLEXECUTE", TBuiltinStdFuncs::func_Shellexecute, fty_integer, 3, param_Shellexecute },
+ { "SESSIONVAR", TBuiltinStdFuncs::func_SessionVar, fty_none, 1, param_oneString },
+ { "SETSESSIONVAR", TBuiltinStdFuncs::func_SetSessionVar, fty_none, 2, param_SetSessionVar },
+ { "ABORTSESSION", TBuiltinStdFuncs::func_AbortSession, fty_none, 1, param_oneInteger },
+ { "CONFIGVAR", TBuiltinStdFuncs::func_ConfigVar, fty_string, 1, param_oneString },
+ { "TREATASLOCALTIME", TBuiltinStdFuncs::func_SetTreatAsLocaltime, fty_none, 1, param_oneInteger },
+ { "TREATASUTC", TBuiltinStdFuncs::func_SetTreatAsUTC, fty_none, 1, param_oneInteger },
+ { "UPDATECLIENTINSLOWSYNC", TBuiltinStdFuncs::func_SetUpdateClientInSlowSync, fty_none, 1, param_oneInteger },
+ { "UPDATESERVERINSLOWSYNC", TBuiltinStdFuncs::func_SetUpdateServerInSlowSync, fty_none, 1, param_oneInteger },
+ { "SHOWCTCAPPROPERTIES", TBuiltinStdFuncs::func_ShowCTCapProps, fty_none, 1, param_oneInteger },
+ { "SHOWTYPESIZEINCTCAP10", TBuiltinStdFuncs::func_ShowTypeSizeInCTCap10, fty_none, 1, param_oneInteger },
+ { "ENUMDEFAULTPROPPARAMS", TBuiltinStdFuncs::func_EnumDefaultPropParams, fty_none, 1, param_oneInteger },
+ { "RECURRENCE_DATE", TBuiltinStdFuncs::func_Recurrence_Date, fty_timestamp, 7, param_Recurrence_Date },
+ { "RECURRENCE_COUNT", TBuiltinStdFuncs::func_Recurrence_Count, fty_integer, 7, param_Recurrence_Count },
+ { "MAKE_RRULE", TBuiltinStdFuncs::func_Make_RRULE, fty_string, 6, param_Make_RRULE },
+ { "PARSE_RRULE", TBuiltinStdFuncs::func_Parse_RRULE, fty_integer, 8, param_Parse_RRULE },
+ { "PARSEEMAILSPEC", TBuiltinStdFuncs::func_ParseEmailSpec, fty_integer, 3, param_parseEmailSpec },
+ { "MAKEEMAILSPEC", TBuiltinStdFuncs::func_MakeEmailSpec, fty_string, 2, param_makeEmailSpec },
+ { "PARSETEXTWITHPROFILE", TBuiltinStdFuncs::func_ParseTextWithProfile, fty_integer, 3, param_parseTextWithProfile },
+ { "MAKETEXTWITHPROFILE", TBuiltinStdFuncs::func_MakeTextWithProfile, fty_string, 2, param_makeTextWithProfile },
+ { "SYNCMLVERS", TBuiltinStdFuncs::func_SyncMLVers, fty_string, 0, NULL },
+ { "ALLDAYCOUNT", TBuiltinStdFuncs::func_AlldayCount, fty_integer, 4, param_AlldayCount },
+ { "MAKEALLDAY", TBuiltinStdFuncs::func_MakeAllday, fty_integer, 3, param_MakeAllday },
+};
+
+const TFuncTable BuiltInFuncTable = {
+ sizeof(BuiltInFuncDefs) / sizeof(TBuiltInFuncDef), // size of table
+ BuiltInFuncDefs, // table pointer
+ NULL // no chain func
+};
+
+
+/*
+ * Implementation of TScriptContext
+ */
+
+/* public TScriptContext members */
+
+
+TScriptContext::TScriptContext(TSyncAppBase *aAppBaseP, TSyncSession *aSessionP) :
+ fAppBaseP(aAppBaseP), // save syncappbase link, must always exist
+ fSessionP(aSessionP), // save session, can be NULL
+ fNumVars(0), // number of instantiated vars
+ fNumParams(0),
+ fFieldsP(NULL), // no field contents yet
+ scriptname(NULL), // no script name known yet
+ linesource(NULL),
+ executing(false),
+ debugon(false),
+ fTargetItemP(NULL),
+ fReferenceItemP(NULL),
+ fParentContextP(NULL)
+{
+ fVarDefs.clear();
+} // TScriptContext::TScriptContext
+
+
+TScriptContext::~TScriptContext()
+{
+ clear();
+} // TScriptContext::~TScriptContext
+
+
+// Reset context (clear all variables and definitions)
+void TScriptContext::clear(void)
+{
+ // clear actual fields
+ clearFields();
+ // clear definitions
+ TVarDefs::iterator pos;
+ for (pos=fVarDefs.begin(); pos!=fVarDefs.end(); pos++) {
+ if (*pos) delete (*pos);
+ }
+ fVarDefs.clear();
+} // TScriptContext::clear
+
+
+GZones *TScriptContext::getSessionZones(void)
+{
+ return
+ fSessionP ? fSessionP->getSessionZones() : NULL;
+} // TScriptContext::getSessionZones
+
+
+#ifdef SYDEBUG
+// get debug logger
+TDebugLogger *TScriptContext::getDbgLogger(void)
+{
+ // use session logger if linked to a session
+ if (fSessionP)
+ return fSessionP->getDbgLogger();
+ // otherwise, use global logger
+ return fAppBaseP ? fAppBaseP->getDbgLogger() : NULL;
+} // TScriptContext::getDbgLogger
+
+
+uInt32 TScriptContext::getDbgMask(void)
+{
+ // use session logger if linked to a session
+ if (fSessionP)
+ return fSessionP->getDbgMask();
+ // otherwise, use global logger
+ return fAppBaseP ? fAppBaseP->getDbgMask() : 0;
+} // TScriptContext::getDbgMask
+#endif
+
+
+
+// Reset context (clear all variables and definitions)
+void TScriptContext::clearFields(void)
+{
+ if (fFieldsP) {
+ // clear local vars (fields), but not references
+ TVarDefs::iterator pos;
+ for (pos=fVarDefs.begin(); pos!=fVarDefs.end(); pos++) {
+ sInt16 i=(*pos)->fIdx;
+ if (i>=fNumVars) break; // all instantiated vars done, stop even if more might be defined
+ if (fFieldsP[i]) {
+ if (!(*pos)->fIsRef)
+ delete fFieldsP[i]; // delete field object (but only if not reference
+ fFieldsP[i]=NULL;
+ }
+ }
+ // clear array of field pointers
+ delete [] fFieldsP;
+ fFieldsP=NULL;
+ fNumVars=0;
+ }
+} // TScriptContext::clearFields
+
+
+
+// check for identifier
+static bool isidentchar(appChar c) {
+ return isalnum(c) || c=='_';
+} // isidentchar
+
+
+// adds source line (including source text, if selected) to token stream
+static void addSourceLine(uInt16 aLine, const char *aText, string &aScript, bool aIncludeSource, uInt16 &aLastIncludedLine)
+{
+ aScript+=TK_SOURCELINE; // token
+ #ifdef SYDEBUG
+ sInt16 linelen=0;
+ // line #0 is script/function name and must be included anyway
+ if (aIncludeSource && (aLine>aLastIncludedLine || aLine==0)) {
+ const char *tt=aText;
+ while (*tt && *tt!=0x0D && *tt!=0x0A) ++tt;
+ linelen=tt-aText+1; // room for the terminator
+ if (linelen>250) linelen=250; // limit size (2 chars needed for number, 3 reserved)
+ // Note: even empty line will have at least one char (the terminator)
+ aScript+=(appChar)(2+linelen); // length of additional data
+ }
+ else {
+ // Note: not-included line will have no extra data (not even the terminator), so
+ // it can be distinguished from empty line
+ aIncludeSource=false;
+ aScript+=(appChar)(2); // length of additional data
+ }
+ #else
+ aScript+=(appChar)(2); // length of additional data
+ #endif
+ // add source line number
+ aScript+=(appChar)(aLine>>8); // source line
+ aScript+=(appChar)(aLine & 0xFF);
+ // add source itself
+ #ifdef SYDEBUG
+ if (aIncludeSource) {
+ aScript.append(aText,linelen-1);
+ aScript+=(appChar)(0); // add terminator
+ aLastIncludedLine = aLine;
+ }
+ #endif
+} // addSourceLine
+
+
+// Tokenize input string
+void TScriptContext::Tokenize(TSyncAppBase *aAppBaseP, cAppCharP aScriptName, sInt32 aLine, cAppCharP aScriptText, string &aTScript, const TFuncTable *aContextFuncs, bool aFuncHeader, bool aNoDeclarations)
+{
+ string itm;
+ string macro;
+ appChar c,c2;
+ appChar token,lasttoken=0;
+ cAppCharP text = aScriptText;
+ cAppCharP p;
+ uInt16 line=aLine; // script starts here
+ sInt16 enu;
+
+ // clear output
+ aTScript.erase();
+
+ // debug info if script debugging is enabled in configuration
+ bool includesource =
+ #ifdef SYDEBUG
+ aAppBaseP->getRootConfig()->fDebugConfig.fDebug & DBG_SCRIPTS;
+ #else
+ false;
+ #endif
+ uInt16 lastincludedline = 0;
+
+ if (*text) {
+ #ifdef SYDEBUG
+ // insert script name as line #0 in all but completely empty scripts (or functions)
+ if (aScriptName) addSourceLine(0,aScriptName,aTScript,true,lastincludedline);
+ #endif
+ // insert source line identification token for start of script for all but completely empty scripts
+ addSourceLine(line,text,aTScript,includesource,lastincludedline);
+ }
+ SYSYNC_TRY {
+ // process text
+ while (*text) {
+ // get next token
+ token=0; // none yet
+ // - skip spaces (but not line ends)
+ while (*text==' ' || *text=='\t') text++;
+ // - dispatch different types of tokens
+ c=*text++;
+ if (c==0) break; // done with script
+ // - check input now
+ if (isdigit(c)) {
+ // numeric literal
+ p=text-1; // beginning of literal
+ while (isalnum(*text)) text++;
+ // - p=start, text=past end of numeric literal
+ itm.assign(p,text-p);
+ // code literal into token string
+ aTScript+=TK_NUMERIC_LITERAL; // token
+ aTScript+=(appChar)(itm.size()); // length of additional data
+ aTScript.append(itm);
+ }
+ else if (c=='"') {
+ // string literal, parse
+ itm.erase();
+ while ((c=*text)) {
+ text++;
+ if (c=='"') { break; } // done
+ if (c=='\\') {
+ // escape char
+ c2=*text++;
+ if (!c2) break; // escape without anything following -> done
+ else if (c2=='n') c='\n'; // internal line end
+ else if (c2=='t') c='\t'; // internal tab
+ else if (c2=='x') {
+ // hex char spec
+ uInt16 sh;
+ text+=HexStrToUShort(text,sh,2);
+ c=(appChar)sh;
+ }
+ else c=c2; // simply use char following the escape char
+ }
+ // now add
+ if (c) itm+=c;
+ }
+ // code literal into token string
+ aTScript+=TK_STRING_LITERAL; // token
+ aTScript+=(appChar)(itm.size()); // length of additional data
+ aTScript.append(itm);
+ }
+ else if (isalpha(c)) {
+ // identifier
+ // - get identifier
+ p=text-1;
+ while (isidentchar(*text)) text++;
+ // - now p=start of identified, text=end
+ uInt16 il=text-p;
+ // - skip whitespace following identifier
+ while (*text==' ' || *text=='\t') text++;
+ // - check language keywords
+ if (strucmp(p,"IF",il)==0) token=TK_IF;
+ else if (strucmp(p,"ELSE",il)==0) token=TK_ELSE;
+ else if (strucmp(p,"LOOP",il)==0) token=TK_LOOP;
+ else if (strucmp(p,"WHILE",il)==0) token=TK_WHILE;
+ else if (strucmp(p,"BREAK",il)==0) token=TK_BREAK;
+ else if (strucmp(p,"CONTINUE",il)==0) token=TK_CONTINUE;
+ else if (strucmp(p,"RETURN",il)==0) token=TK_RETURN;
+ // - check special constants
+ else if (strucmp(p,"EMPTY",il)==0) token=TK_EMPTY;
+ else if (strucmp(p,"UNASSIGNED",il)==0) token=TK_UNASSIGNED;
+ else if (strucmp(p,"TRUE",il)==0) token=TK_TRUE;
+ else if (strucmp(p,"FALSE",il)==0) token=TK_FALSE;
+ // - check types
+ else if (StrToEnum(ItemFieldTypeNames,numFieldTypes,enu,p,il)) {
+ // check if declaration and if allowed
+ if (aNoDeclarations && lasttoken!=TK_OPEN_PARANTHESIS)
+ SYSYNC_THROW(TTokenizeException(aScriptName, "no local variable declarations allowed in this script",aScriptText,text-aScriptText,line));
+ // code type into token
+ aTScript+=TK_TYPEDEF; // token
+ aTScript+=1; // length of additional data
+ aTScript+=enu; // type
+ }
+ // - check function calls if in body
+ else if (*text=='(' && !aFuncHeader) {
+ // identifier followed by ( must be function call (if not in header of function itself)
+ // - check for built-in function
+ sInt16 k=0;
+ while (k<BuiltInFuncTable.numFuncs) {
+ if (strucmp(p,BuiltInFuncDefs[k].fFuncName,il)==0) {
+ // built-in
+ aTScript+=TK_FUNCTION; // token
+ aTScript+=1; // length of additional data
+ aTScript+=k; // built-in function index
+ k=-1; // found flag
+ break;
+ }
+ k++;
+ }
+ if (k>=0) {
+ // no built-in base function, could be context-related function
+ k=0; // function index (may span several chain links)
+ // - start with passed functable
+ TFuncTable *functableP = (TFuncTable *)aContextFuncs;
+ while(functableP) {
+ // get the function table properties
+ sInt16 fidx,numfuncs = functableP->numFuncs;
+ const TBuiltInFuncDef *funcs = functableP->funcDefs;
+ // process this func table
+ for (fidx=0; fidx<numfuncs; fidx++) {
+ if (strucmp(p,funcs[fidx].fFuncName,il)==0) {
+ // built-in
+ aTScript+=TK_CONTEXTFUNCTION; // token
+ aTScript+=1; // length of additional data
+ aTScript+=k+fidx; // built-in function index
+ k=-1; // found flag
+ break;
+ }
+ }
+ // exit loop if found
+ if (k<0) break;
+ // not found, try to chain
+ if (functableP->chainFunc) {
+ // obtain next function table (caller context pointer is irrelevant here)
+ k+=numfuncs; // index for next chained table starts at end of indexes for current table
+ void *ctx=NULL;
+ functableP=(TFuncTable *)functableP->chainFunc(ctx);
+ }
+ else {
+ functableP=NULL; // end chaining loop
+ }
+ } // while
+ if (k>=0) {
+ // no built-in nor context-built-in found, assume user-defined
+ aTScript+=TK_USERFUNCTION; // token
+ aTScript+=(appChar)(il+1); // length of additional data
+ aTScript+=VARIDX_UNDEFINED; // no function index defined yet
+ aTScript.append(p,il); // identifier name
+ }
+ }
+ }
+ // - check object qualifiers
+ else if (*text=='.') {
+ // must be qualifier
+ uInt8 objidx=OBJ_AUTO;
+ if (strucmp(p,"LOCAL",il)==0) objidx=OBJ_LOCAL;
+ else if (strucmp(p,"OLD",il)==0) objidx=OBJ_REFERENCE;
+ else if (strucmp(p,"LOOSING",il)==0) objidx=OBJ_REFERENCE;
+ else if (strucmp(p,"REFERENCE",il)==0) objidx=OBJ_REFERENCE;
+ else if (strucmp(p,"NEW",il)==0) objidx=OBJ_TARGET;
+ else if (strucmp(p,"WINNING",il)==0) objidx=OBJ_TARGET;
+ else if (strucmp(p,"TARGET",il)==0) objidx=OBJ_TARGET;
+ else
+ SYSYNC_THROW(TTokenizeException(aScriptName,"unknown object name",aScriptText,text-aScriptText,line));
+ text++; // skip object qualifier
+ aTScript+=TK_OBJECT; // token
+ aTScript+=1; // length of additional data
+ aTScript+=objidx; // object index
+ }
+ else {
+ // generic identifier, must be some kind of variable reference
+ aTScript+=TK_IDENTIFIER; // token
+ aTScript+=(appChar)(il+1); // length of additional data
+ aTScript+=VARIDX_UNDEFINED; // no variable index defined yet
+ aTScript.append(p,il); // identifier name
+ }
+ } // if identifier
+ else {
+ // get next char for double-char tokens
+ c2=*text;
+ // check special single chars
+ switch (c) {
+ // - macro
+ case '$': {
+ // get macro name
+ p=text;
+ while (isidentchar(*text)) text++;
+ if (text==p)
+ SYSYNC_THROW(TTokenizeException(aScriptName,"missing macro name after $",aScriptText,text-aScriptText,line));
+ itm.assign(p,text-p);
+ // see if we have such a macro
+ TScriptConfig *cfgP = aAppBaseP->getRootConfig()->fScriptConfigP;
+ TStringToStringMap::iterator pos = cfgP->fScriptMacros.find(itm);
+ if (pos==cfgP->fScriptMacros.end())
+ SYSYNC_THROW(TTokenizeException(aScriptName,"unknown macro",aScriptText,p-1-aScriptText,line));
+ // continue tokenizing with macro text
+ TScriptContext::Tokenize(
+ aAppBaseP,
+ itm.c_str(), // pass macro name as "script" name
+ 1, // line number relative to beginning of macro
+ (*pos).second.c_str(), // use macro text as script text
+ macro, // produce tokenized macro here
+ aContextFuncs, // same context
+ false, // not in function header
+ aNoDeclarations // same condition
+ );
+ // append tokenized macro to current script
+ aTScript+=macro;
+ // continue with normal text
+ break;
+ }
+ // - grouping
+ case '(': token=TK_OPEN_PARANTHESIS; break; // open subexpression/argument paranthesis
+ case ')': token=TK_CLOSE_PARANTHESIS; break; // close subexpression/argument paranthesis
+ case ',': token=TK_LIST_SEPARATOR; break; // comma for separating arguments
+ case '{': token=TK_BEGIN_BLOCK; aFuncHeader=false; break; // begin block (and start of function body)
+ case '}': token=TK_END_BLOCK; break; // end block
+ case ';': token=TK_END_STATEMENT; break; // end statement
+ case '[': token=TK_OPEN_ARRAY; break; // open array paranthesis
+ case ']': token=TK_CLOSE_ARRAY; break; // close array paranthesis
+ // line ends
+ case 0x0D:
+ if (c2==0x0A) text++; // skip LF of CRLF sequence as well to make sure it is not counted twice
+ // otherwise treat like LF
+ case 0x0A:
+ // new line begins : insert source line identification token (and source of next line, if any)
+ line++;
+ addSourceLine(line,text,aTScript,includesource,lastincludedline);
+ break;
+ // possible multi-char tokens
+ case '/':
+ if (c2=='/') {
+ text++;
+ // end-of-line comment, skip it
+ do { c=*text; if (c==0 || c==0x0D || c==0x0A) break; text++; } while(true);
+ }
+ else if (c2=='*') {
+ // C-style comment, skip until next '*/'
+ text++;
+ do {
+ c=*text;
+ if (c==0) break;
+ text++; // next
+ if (c=='*' && *text=='/') {
+ // end of comment
+ text++; // skip /
+ break; // end of comment
+ }
+ else if (c==0x0D || c==0x0A) {
+ if (*text==0x0A) text++; // skip LF of CRLF sequence as well to make sure it is not counted twice
+ // new line begins : insert source line identification token (and source of next line, if any)
+ line++;
+ addSourceLine(line,text,aTScript,includesource,lastincludedline);
+ }
+ } while(true);
+ }
+ else
+ token=TK_DIVIDE; // simple division
+ break;
+ case '*': token=TK_MULTIPLY; break; // multiply
+ case '%': token=TK_MODULUS; break; // modulus
+ case '+': token=TK_PLUS; break; // add
+ case '-': token=TK_MINUS; break; // subtract/unary minus
+ case '^': token=TK_BITWISEXOR; break; // bitwise XOR
+ case '~': token=TK_BITWISENOT; break; // bitwise not (one's complement)
+ case '!':
+ if (c2=='=') { token=TK_NOTEQUAL; text++; } // !=
+ else token=TK_LOGICALNOT; // !
+ break;
+ case '=':
+ if (c2=='=') { token=TK_EQUAL; text++; } // ==
+ else token=TK_ASSIGN; // =
+ break;
+ case '>':
+ if (c2=='=') { token=TK_GREATEREQUAL; text++; } // >=
+ else if (c2=='>') { token=TK_SHIFTRIGHT; text++; } // >>
+ else token=TK_GREATERTHAN; // >
+ break;
+ case '<':
+ if (c2=='=') { token=TK_LESSEQUAL; text++; } // <=
+ else if (c2=='<') { token=TK_SHIFTLEFT; text++; } // <<
+ else if (c2=='>') { token=TK_NOTEQUAL; text++; } // <>
+ else token=TK_LESSTHAN; // <
+ break;
+ case '&':
+ if (c2=='&') { token=TK_LOGICALAND; text++; } // &&
+ else token=TK_BITWISEAND; // &
+ break;
+ case '|':
+ if (c2=='|') { token=TK_LOGICALOR; text++; } // ||
+ else token=TK_BITWISEOR; // |
+ break;
+ default:
+ SYSYNC_THROW(TTokenizeException(aScriptName,"Syntax Error",aScriptText,text-aScriptText,line));
+ }
+ }
+ // add token if simple token found
+ if (token) aTScript+=token;
+ lasttoken=token; // save for differentiating casts from declarations etc.
+ } // while more script text
+ }
+ SYSYNC_CATCH (...)
+ // make sure that script with errors is not stored
+ aTScript.erase();
+ SYSYNC_RETHROW;
+ SYSYNC_ENDCATCH
+} // TScriptContext::Tokenize
+
+
+// tokenize and resolve user-defined function
+void TScriptContext::TokenizeAndResolveFunction(TSyncAppBase *aAppBaseP, sInt32 aLine, cAppCharP aScriptText, TUserScriptFunction &aFuncDef)
+{
+ TScriptContext *resolvecontextP=NULL;
+
+ Tokenize(aAppBaseP, NULL, aLine,aScriptText,aFuncDef.fFuncDef,NULL,true); // parse as function
+ SYSYNC_TRY {
+ // resolve identifiers
+ resolvecontextP=new TScriptContext(aAppBaseP,NULL);
+ resolvecontextP->ResolveIdentifiers(
+ aFuncDef.fFuncDef,
+ NULL, // no fields
+ false, // not rebuild
+ &aFuncDef.fFuncName // store name here
+ );
+ delete resolvecontextP;
+ }
+ SYSYNC_CATCH (exception &e)
+ delete resolvecontextP;
+ SYSYNC_RETHROW;
+ SYSYNC_ENDCATCH
+} // TScriptContext::TokenizeAndResolveFunction
+
+
+
+// resolve identifiers in a script, if there is a context passed at all
+void TScriptContext::resolveScript(TSyncAppBase *aAppBaseP, string &aTScript,TScriptContext *&aCtxP, TFieldListConfig *aFieldListConfigP)
+{
+ if (aTScript.empty()) return; // no resolving needed
+ if (!aCtxP) {
+ // we need a context, create one
+ aCtxP = new TScriptContext(aAppBaseP, NULL);
+ }
+ aCtxP->ResolveIdentifiers(
+ aTScript,
+ aFieldListConfigP,
+ false
+ );
+} // TScriptContext::ResolveScript
+
+
+// link a script into a context with already instantiated variables.
+// This is for "late bound" scripts (such as rulescript) that cannot be bound at config
+// but are determined only later, when their context is already instantiated.
+void TScriptContext::linkIntoContext(string &aTScript,TScriptContext *aCtxP, TSyncSession *aSessionP)
+{
+ if (aTScript.empty() || !aCtxP) return; // no resolving needed (no script or no context)
+ // resolve identfiers (no new declarations possible)
+ aCtxP->ResolveIdentifiers(
+ aTScript,
+ NULL, // no field list
+ false, // do not rebuild
+ NULL, // no function name
+ false // no declarations allowed
+ );
+} // TScriptContext::linkIntoContext
+
+
+// rebuild a script context for a script, if the script is not empty
+// - Script must already be resolved with ResolveIdentifiers
+// - If context already exists, adds new locals to existing ones
+// - If aBuildVars is set, buildVars() will be called after rebuilding variable definitions
+void TScriptContext::rebuildContext(TSyncAppBase *aAppBaseP, string &aTScript,TScriptContext *&aCtxP, TSyncSession *aSessionP, bool aBuildVars)
+{
+ // Optimization: Nop if script is empty and NOT build var requested for already existing context (=vars from other scripts!)
+ if (aTScript.empty() && !(aBuildVars && aCtxP)) return;
+ // Create context if there isn't one yet
+ if (!aCtxP) {
+ // we need a context, create one
+ aCtxP = new TScriptContext(aAppBaseP,aSessionP);
+ }
+ SYSYNC_TRY {
+ aCtxP->ResolveIdentifiers(
+ aTScript,
+ NULL,
+ true
+ );
+ if (aBuildVars) {
+ // call this one, too
+ buildVars(aCtxP);
+ }
+ }
+ SYSYNC_CATCH (TScriptErrorException &e)
+ // show error in log, but otherwise ignore it
+ POBJDEBUGPRINTFX(aSessionP,DBG_ERROR,("Failed rebuilding script context: %s",e.what()));
+ SYSYNC_ENDCATCH
+ SYSYNC_CATCH (...)
+ SYSYNC_ENDCATCH
+} // TScriptContext::rebuildContext
+
+
+// Builds the local variables according to definitions (clears existing vars first)
+void TScriptContext::buildVars(TScriptContext *&aCtxP)
+{
+ if (aCtxP) {
+ // instantiate the variables
+ aCtxP->PrepareLocals();
+ }
+} // TScriptContext::buildVars
+
+
+// init parsing vars
+void TScriptContext::initParse(const string &aTScript, bool aExecuting)
+{
+ executing=aExecuting;
+ bp=(cUInt8P)aTScript.c_str(); // start of script
+ ep=bp+aTScript.size(); // end of script
+ p=bp; // cursor, start at beginning
+ np=NULL; // no next token yet
+ linesource=NULL; // no source yet
+ scriptname=NULL; // no name yet
+ inComment=false; // not in comment yet
+ // try to get start line
+ if (ep>bp && *p!=TK_SOURCELINE)
+ SYSYNC_THROW(TScriptErrorException(DEBUGTEXT("Script does not start with TK_SOURCELINE","scri2"),line));
+ do {
+ // get script name or first source code line
+ line = (((uInt8)*(p+2))<<8) + (uInt8)(*(p+3));
+ nextline=line; // assume same
+ #ifdef SYDEBUG
+ sInt16 n=(uInt8)*(p+1)-2;
+ if (n>0) {
+ linesource=(const char *)(p+4); // source for this line starts here
+ if (line==0) {
+ // this is the script name
+ scriptname=linesource; // remember
+ linesource=NULL;
+ }
+ }
+ p+=2+2+n;
+ #else
+ p+=2+2; // script starts here
+ #endif
+ if (line==0) {
+ // we have the name
+ continue; // get first line now
+ }
+ break;
+ } while(true);
+} // TScriptContext::initParse
+
+
+#ifdef SYDEBUG
+
+// colorize source line (comments only so far)
+static void colorizeSourceLine(cAppCharP aSource, string &aColorSource, bool &aComment, bool skipping)
+{
+ appChar c;
+ bool start=true;
+ bool incomment=aComment;
+ bool lineendcomment=false;
+
+ aColorSource.erase();
+ while ((c=*aSource++)) {
+ // check for comment start
+ if (!aComment && !skipping) {
+ // not in comment
+ if (c=='/') {
+ if (*aSource=='*')
+ { aComment=true; }
+ else if (*aSource=='/')
+ { aComment=true; lineendcomment=true; }
+ }
+ }
+ // switch to new color if changes at this point
+ if (start || incomment!=aComment) {
+ aColorSource += "&html;";
+ if (!start)
+ aColorSource += "</span>"; // not start
+ aColorSource += "<span class=\"";
+ aColorSource += skipping ? "skipped" : (aComment ? "comment" : "source");
+ aColorSource += "\">&html;";
+ incomment = aComment;
+ }
+ // output current char
+ if (c==' ')
+ aColorSource += "&sp;"; // will convert to &nbsp; in HTML
+ else
+ aColorSource += c;
+ // check for end of comment after this char
+ if (aComment && !lineendcomment && !skipping) {
+ // in C-style comment
+ if (c=='/' && !start && *(aSource-2)=='*') {
+ // this was end of C-comment
+ aComment=false;
+ }
+ }
+ // not start any more
+ start=false;
+ }
+ // end of color
+ aColorSource += "&html;</span>&html;";
+ // end of line terminates //-style comment
+ if (lineendcomment) aComment=false;
+} // colorizeSourceLine
+
+#endif
+
+
+// get token at p if not end of script, updates np to point to next token
+uInt8 TScriptContext::gettoken(void)
+{
+ uInt8 tk;
+ DBGSTRINGDEF(s);
+
+ if (np) p=np; // advance to next token
+ line=nextline; // advance to next line
+ if (p>=ep) return 0; // end of script
+ do {
+ // get token and search next
+ tk=*p; // get token
+ if (tk>TK_MAX_MULTIBYTE) np=p+1; // single byte token, next is next char
+ else np=p+2+*(p+1); // use length to find next token
+ // show current line with source if we have it
+ #ifdef SYDEBUG
+ // delay display of line when processing a end block statement to make sure
+ // end-skipping decision is made BEFORE showing the line
+ if (SCRIPTDBGTEST &&
+ linesource &&
+ executing &&
+ tk!=TK_END_STATEMENT &&
+ tk!=TK_END_BLOCK) {
+ bool sk = skipping>0;
+ colorizeSourceLine(linesource,s,inComment,sk);
+ SCRIPTDBGMSG(("Line %4hd: %s",line,s.c_str()));
+ linesource=NULL; // prevent showing same line twice
+ }
+ #endif
+ // check if it is line token, which would be processed invisibly
+ if (tk==TK_SOURCELINE) {
+ // get source code line number
+ line = (((uInt8)*(p+2))<<8) + (uInt8)(*(p+3));
+ // get source code itself, if present
+ #ifdef SYDEBUG
+ sInt16 n=(uInt8)*(p+1)-2;
+ if (n>0) linesource=(const char *)(p+4); // source for this line starts here
+ #endif
+ // go to next token
+ p=np;
+ continue; // fetch next
+ }
+ nextline=line; // by default, assume next token is on same line
+ do {
+ // check if next is line token, if yes, skip it as well
+ if (*np==TK_SOURCELINE) {
+ // get next token's source code line
+ #ifdef SYDEBUG
+ uInt16 showline = nextline; // current line is next that must be shown
+ #endif
+ nextline = (((uInt8)*(np+2))<<8) + (uInt8)(*(np+3));
+ #ifdef SYDEBUG
+ // - show last line if we'll skip another line
+ if (linesource && executing) {
+ colorizeSourceLine(linesource,s,inComment,skipping>0);
+ SCRIPTDBGMSG(("Line %4hd: %s",showline,s.c_str()));
+ linesource=NULL; // prevent showing same line twice
+ }
+ // - get next line's number and text
+ sInt16 n=(uInt8)*(np+1)-2;
+ if (n>0) linesource=(const char *)(np+4); // source for this line starts here
+ np=np+2+2+n;
+ #else
+ np=np+2+2; // skip token
+ #endif
+ continue; // test again
+ }
+ break;
+ } while(true);
+ break;
+ } while(true);
+ return tk;
+} // TScriptContext::gettoken
+
+
+// re-use last token fetched with gettoken()
+void TScriptContext::reusetoken(void)
+{
+ // set pointer such that next gettoken will fetch the same token again
+ np=p;
+ nextline=line;
+} // TScriptContext::reusetoken
+
+
+
+// Resolve local variable declarations, references and field references
+// Note: does not clear the context, so multiple scripts can
+// share the same context
+// Note: modifies the aTScript passed (inserts identifier IDs)
+void TScriptContext::ResolveIdentifiers(string &aTScript,TFieldListConfig *aFieldListConfigP, bool aRebuild, string *aFuncNameP, bool aNoNewLocals)
+{
+ uInt8 tk; // current token
+
+ TItemFieldTypes ty=fty_none;
+ bool deftype=false;
+ bool refdecl=false;
+ bool funcparams=false;
+ string ident;
+ sInt16 objidx;
+
+ // init parsing
+ initParse(aTScript);
+ objidx=OBJ_AUTO;
+ while ((tk=gettoken())) {
+ // check declaration syntax
+ if (deftype && tk!=TK_IDENTIFIER)
+ SYSYNC_THROW(TScriptErrorException("Bad declaration",line));
+ if (!funcparams && tk==TK_OPEN_PARANTHESIS) {
+ // could be typecast
+ if (*np==TK_TYPEDEF) {
+ // is a typecase
+ gettoken(); // swallow type identifier
+ // next must be closing paranthesis
+ if (gettoken()!=TK_CLOSE_PARANTHESIS)
+ SYSYNC_THROW(TScriptErrorException("Invalid typecast",line));
+ }
+ }
+ // process token
+ else if (tk==TK_TYPEDEF) {
+ // type definition (for variable declaration or function definition)
+ ty = (TItemFieldTypes)(*(p+2));
+ deftype=true; // we are in type definition mode now
+ // check if we are declaring a variable reference
+ if (*np==TK_BITWISEAND) {
+ gettoken();
+ refdecl=true; // defining a reference
+ if (!funcparams)
+ SYSYNC_THROW(TScriptErrorException("Field reference declaration allowed for function parameters",line));
+ }
+ }
+ else if (tk==TK_OBJECT) {
+ objidx=*(p+2);
+ continue; // avoid resetting to OBJ_AUTO at end of loop
+ }
+ else if (tk==TK_CLOSE_PARANTHESIS) {
+ if (funcparams) {
+ // end of function parameters
+ fNumParams=fVarDefs.size(); // locals declared up to here are parameters
+ funcparams=false; // done with parameters
+ }
+ }
+ else if (tk==TK_IDENTIFIER) {
+ // get identifier
+ ident.assign((cAppCharP)(p+3),(size_t)(*(p+1)-1));
+ // check for function definition
+ if (!funcparams && *np==TK_OPEN_PARANTHESIS) {
+ // this is a function declaration
+ if (!aRebuild) {
+ // - check if allowed
+ if (!aFuncNameP)
+ SYSYNC_THROW(TScriptErrorException("cannot declare function here",line));
+ // - save name of function
+ aFuncNameP->assign(ident);
+ }
+ // - determine return type
+ if (deftype)
+ fFuncType=ty; // save function return type
+ else
+ fFuncType=fty_none; // void function
+ // - switch to function param parsing
+ gettoken(); // get opening paranthesis
+ funcparams=true;
+ deftype=false;
+ }
+ else if (deftype) {
+ // defining new local variable or parameter
+ if (objidx!=OBJ_AUTO && objidx!=OBJ_LOCAL)
+ SYSYNC_THROW(TScriptErrorException("cannot declare non-local variable '%s'",line,ident.c_str()));
+ bool arr=false;
+ // define new identifier(s)
+ cUInt8P idp=p; // remember identifier token pointer
+ // - check if this is an array definition
+ if (*np==TK_OPEN_ARRAY) {
+ arr=true;
+ gettoken();
+ if (*np!=TK_CLOSE_ARRAY)
+ SYSYNC_THROW(TScriptErrorException("Invalid array declaration for '%s'",line,ident.c_str()));
+ #ifndef ARRAYFIELD_SUPPORT
+ SYSYNC_THROW(TScriptErrorException("Arrays not available in this version",line));
+ #endif
+ gettoken(); // swallow closing bracket
+ }
+ // - check if variable already defined
+ TScriptVarDef *vardefP=NULL;
+ #if SYDEBUG>1
+ // always get it by name and compare index
+ vardefP = getVarDef(ident.c_str(),ident.size());
+ #else
+ if (!aRebuild || (sInt8)(*(idp+2))==VARIDX_UNDEFINED)
+ // first time build or index not yet set for another reason: get by name
+ vardefP = getVarDef(ident.c_str(),ident.size());
+ else
+ // on rebuild, get definition by already known index
+ vardefP = getVarDef(-((sInt8)(*(idp+2))+1));
+ #endif
+ #ifndef RELEASE_VERSION
+ DEBUGPRINTFX(DBG_SCRIPTS+DBG_EXOTIC,(
+ "ident=%s, vardefP=0x%lX, vardefP->fIdx=%d, stored idx=%d (=vardef idx %d)",
+ ident.c_str(),
+ (long)vardefP,
+ vardefP ? (int)vardefP->fIdx : VARIDX_UNDEFINED,
+ (int)((sInt8)(*(idp+2))),
+ (int)(-((sInt8)(*(idp+2))+1))
+ ));
+ #endif
+ // check for match
+ if (vardefP) {
+ // check if type matching
+ if (ty!=vardefP->fVarType || arr!=vardefP->fIsArray || refdecl!=vardefP->fIsRef)
+ SYSYNC_THROW(TScriptErrorException("Redefined '%s' to different type",line,ident.c_str()));
+ }
+ else {
+ // not existing yet
+ if (aNoNewLocals) {
+ // Note: aNoNewLocals is useful when resolving a script into a context
+ // with already existing variables (such as RuleScript). We cannot
+ // add new variables once the VarDefs have been instantiated!
+ SYSYNC_THROW(TScriptErrorException("Cannot declare variables in this script",line));
+ }
+ else {
+ // create new variable definition
+ vardefP = new TScriptVarDef(ident.c_str(),fVarDefs.size(),ty,arr,refdecl,false);
+ fVarDefs.push_back(vardefP);
+ #ifndef RELEASE_VERSION
+ DEBUGPRINTFX(DBG_SCRIPTS+DBG_EXOTIC,(
+ "created new vardef, ident=%s, type=%s, vardefP=0x%lX, vardefs.size()=%ld",
+ ident.c_str(),
+ ItemFieldTypeNames[ty],
+ (long)vardefP,
+ (long)fVarDefs.size()
+ ));
+ #endif
+ }
+ }
+ if (!aRebuild) {
+ // creating defs for the first time, set index
+ aTScript[idp-bp+2]= -(sInt8)(vardefP->fIdx)-1;
+ }
+ #if SYDEBUG>1
+ else {
+ // check existing index against new one
+ if ((sInt8)(*(idp+2))!=-(sInt8)(vardefP->fIdx)-1)
+ SYSYNC_THROW(TScriptErrorException(DEBUGTEXT("Rebuilding defs gives different index","scri1"),line));
+ }
+ #endif
+ // - check what follows
+ deftype=false; // default to nothing
+ refdecl=false;
+ if (funcparams) {
+ if (*np==TK_LIST_SEPARATOR) gettoken(); // ok, next param
+ }
+ else {
+ if (*np==TK_LIST_SEPARATOR) {
+ deftype=true; // continue with more definitions of same type
+ gettoken(); // consume separator
+ }
+ else if (*np!=TK_END_STATEMENT)
+ SYSYNC_THROW(TScriptErrorException("Missing ';' after declaration of '%s'",line,ident.c_str()));
+ }
+ }
+ else if (!aRebuild) {
+ // refer to identifier
+ sInt16 i=getIdentifierIndex(objidx,aFieldListConfigP,ident.c_str(),ident.size());
+ if (i==VARIDX_UNDEFINED)
+ SYSYNC_THROW(TScriptErrorException("Undefined identifier '%s'",line,ident.c_str()));
+ // save field/var index in script
+ aTScript[p-bp+2]= (sInt8)i;
+ }
+ }
+ else {
+ // Other non-declaration tokens
+ // - if this is function param def, no other tokens are allowed
+ if (funcparams) {
+ SYSYNC_THROW(TScriptErrorException("Invalid function parameter declaration",line));
+ }
+ else {
+ // non-declaration stuff
+ if (tk==TK_USERFUNCTION && !aRebuild) {
+ // resolve function
+ ident.assign((cAppCharP)(p+3),(size_t)(*(p+1)-1));
+ sInt16 i=getSyncAppBase()->getRootConfig()->fScriptConfigP->getFunctionIndex(ident.c_str(),ident.size());
+ if (i==VARIDX_UNDEFINED)
+ SYSYNC_THROW(TScriptErrorException("Undefined function '%s'",line,ident.c_str()));
+ // save function index in script
+ aTScript[p-bp+2]= (sInt8)i;
+ }
+ }
+ }
+ objidx=OBJ_AUTO; // default to auto-select
+ // process next
+ }
+ SHOWVARDEFS(aRebuild ? "Rebuilding" : "Resolving");
+} // TScriptContext::ResolveIdentifiers
+
+
+// create local variable definitions for calling a built-in function
+// This is called for built-in functions before calling PrepareLocals, and
+// builds up local var definitions like ResolveIdentifiers does for
+// script code.
+void TScriptContext::defineBuiltInVars(const TBuiltInFuncDef *aFuncDefP)
+{
+ // get information and save it in the function's context
+ fNumParams=aFuncDefP->fNumParams;
+ fFuncType=aFuncDefP->fReturntype;
+ // get params
+ sInt16 i=0;
+ while (i<fNumParams) {
+ // create parameter definition
+ uInt8 paramdef=aFuncDefP->fParamTypes[i];
+ TItemFieldTypes ty= (TItemFieldTypes)(paramdef & PARAM_TYPEMASK);
+ bool isref = paramdef & PARAM_REF;
+ bool isarr = paramdef & PARAM_ARR;
+ bool isopt = paramdef & PARAM_OPT;
+ TScriptVarDef *vardefP = new TScriptVarDef("", fVarDefs.size(), ty, isarr, isref, isopt);
+ fVarDefs.push_back(vardefP);
+ i++;
+ }
+} // TScriptContext::defineBuiltInVars
+
+
+// execute built-in function
+void TScriptContext::executeBuiltIn(TItemField *&aTermP, const TBuiltInFuncDef *aFuncDefP)
+{
+ TItemField *resultP=NULL;
+
+ // pre-create result field if type is known
+ // Note: multi-typed functions might create result themselves depending on result type
+ if (fFuncType!=fty_none)
+ resultP = newItemField(fFuncType, getSessionZones());
+ // call actual function routine
+ (aFuncDefP->fFuncProc)(resultP,this);
+ // return result
+ if (aTermP) {
+ // copy value to exiting result field
+ if (resultP) {
+ (*aTermP)=(*resultP);
+ delete resultP;
+ } else
+ aTermP=NULL; // no result
+ }
+ else {
+ // just pass back result field (or NULL)
+ aTermP=resultP;
+ }
+} // TScriptContext::executeBuiltIn
+
+
+#ifdef SYDEBUG
+void TScriptContext::showVarDefs(cAppCharP aTxt)
+{
+ if (DEBUGTEST(DBG_SCRIPTS+DBG_EXOTIC)) {
+ // Show var defs
+ DEBUGPRINTFX(DBG_SCRIPTS+DBG_EXOTIC+DBG_HOT,("%s - %s, ctx=0x%lX, VarDefs:",aTxt,scriptname ? scriptname : "<name unknown>",(long)this));
+ TVarDefs::iterator pos;
+ for (pos=fVarDefs.begin(); pos!=fVarDefs.end(); pos++) {
+ DEBUGPRINTFX(DBG_SCRIPTS+DBG_EXOTIC,(
+ "%d: %s %s%s%s",
+ (*pos)->fIdx,
+ ItemFieldTypeNames[(*pos)->fVarType],
+ (*pos)->fIsRef ? "&" : "",
+ (*pos)->fVarName.c_str(),
+ (*pos)->fIsArray ? "[]" : ""
+ ));
+ }
+ }
+} // TScriptContext::showVarDefs
+#endif
+
+
+
+// Prepare local variables (create local fields), according to definitions (re-)built with ResolveIdentifiers()
+// Note: This is called after ResolveIdentifiers() for each script of that context
+// have been called. Normally ResolveIdentifiers() was called already at config parse
+// with an anonymous context that is lost when PrepareLocals needs to be called.
+// Therefore, ResolveIdentifiers(aRebuild=true) can be called to rebuild the definitions from
+// the script(s) involved. Care must be taken to call ResolveIdentifiers() for each script
+// in the same order as at config parse to make sure already resolved indexes will match the variables (again).
+// Note: Field references will have a NULL pointer (which must be filled when the function is
+// called.
+bool TScriptContext::PrepareLocals(void)
+{
+ // make sure old array is cleared
+ SHOWVARDEFS("PrepareLocals");
+ clearFields();
+ // create array of appropriate size for locals
+ fNumVars=fVarDefs.size();
+ SYSYNC_TRY {
+ if (fNumVars) {
+ fFieldsP=new TItemFieldP[fNumVars];
+ if (fFieldsP==NULL) return false;
+ // - init it with null pointers to make sure we can survive when field instantiation fails
+ for (sInt16 i=0; i<fNumVars; i++) fFieldsP[i]=NULL;
+ // - now instantiate local fields (but leave references = NULL)
+ TVarDefs::iterator pos;
+ for (pos=fVarDefs.begin(); pos!=fVarDefs.end(); pos++) {
+ if (!(*pos)->fIsRef && ((*pos)->fVarType!=fty_none)) {
+ // this is not a reference and not an untyped parameter:
+ // instantiate a locally owned field
+ fFieldsP[(*pos)->fIdx]=sysync::newItemField((*pos)->fVarType,getSessionZones(),(*pos)->fIsArray);
+ }
+ }
+ }
+ }
+ SYSYNC_CATCH (...)
+ // failed, remove fields again
+ clearFields();
+ return false;
+ SYSYNC_ENDCATCH
+ return true;
+} // TScriptContext::PrepareLocals
+
+
+// execute a script returning a boolean
+bool TScriptContext::executeTest(
+ bool aDefaultAnswer, // result if no script is there or script returns no value
+ TScriptContext *aCtxP,
+ const string &aTScript,
+ const TFuncTable *aFuncTableP, // context's function table, NULL if none
+ void *aCallerContext, // free pointer eventually having a meaning for context functions and chain function
+ TMultiFieldItem *aTargetItemP, // target (or "loosing") item
+ bool aTargetWritable, // if set, target item may be modified
+ TMultiFieldItem *aReferenceItemP, // reference for source (or "old" or "winning") item
+ bool aRefWritable // if set, reference item may also be written
+)
+{
+ // if no script (no context), return default answer
+ if (!aCtxP) return aDefaultAnswer;
+ // now call and evaluate boolean result
+ TItemField *resP;
+ bool res;
+ res=aCtxP->ExecuteScript(
+ aTScript,
+ &resP, // we want a result
+ false, // not a function
+ aFuncTableP, // context function table
+ aCallerContext, // context data
+ aTargetItemP, // target (or "loosing") item
+ aTargetWritable, // if set, target item may be modified
+ aReferenceItemP, // reference for source (or "old" or "winning") item
+ aRefWritable // if set, reference item may also be written
+ );
+ if (!res) {
+ // script execution failed, do as if there was no script
+ res=aDefaultAnswer;
+ }
+ else {
+ // evaluate result
+ if (resP) {
+ res=resP->getAsBoolean();
+ // get rid of result
+ delete resP;
+ }
+ else {
+ // no result, return default
+ res=aDefaultAnswer;
+ }
+ }
+ // return result
+ return res;
+} // TScriptContext::executeTest
+
+
+// execute a script returning a result if there is a context for it
+bool TScriptContext::executeWithResult(
+ TItemField *&aResultField, // can be default result or NULL, will contain result or NULL if no result
+ TScriptContext *aCtxP,
+ const string &aTScript,
+ const TFuncTable *aFuncTableP, // context's function table, NULL if none
+ void *aCallerContext, // free pointer eventually having a meaning for context functions
+ TMultiFieldItem *aTargetItemP, // target (or "loosing") item
+ bool aTargetWritable, // if set, target item may be modified
+ TMultiFieldItem *aReferenceItemP, // reference for source (or "old" or "winning") item
+ bool aRefWritable, // if set, reference item may also be written
+ bool aNoDebug // if set, debug output is suppressed even if DBG_SCRIPTS is generally on
+)
+{
+ // if no script (no context), return default answer
+ if (!aCtxP) return true; // ok, leave aResultFieldP unmodified
+ // now call and evaluate result
+ return aCtxP->ExecuteScript(
+ aTScript,
+ &aResultField, // we want a result, if we pass a default it will be modified if script has a result
+ false, // not a function
+ aFuncTableP, // context function table
+ aCallerContext, // context data
+ aTargetItemP, // target (or "loosing") item
+ aTargetWritable, // if set, target item may be modified
+ aReferenceItemP, // reference for source (or "old" or "winning") item
+ aRefWritable, // if set, reference item may also be written
+ aNoDebug // if set, debug output is suppressed even if DBG_SCRIPTS is generally on
+ );
+} // TScriptContext::executeTest
+
+
+
+
+// execute a script if there is a context for it
+bool TScriptContext::execute(
+ TScriptContext *aCtxP,
+ const string &aTScript,
+ const TFuncTable *aFuncTableP, // context's function table, NULL if none
+ void *aCallerContext, // free pointer eventually having a meaning for context functions
+ TMultiFieldItem *aTargetItemP, // target (or "loosing") item
+ bool aTargetWritable, // if set, target item may be modified
+ TMultiFieldItem *aReferenceItemP, // reference for source (or "old" or "winning") item
+ bool aRefWritable, // if set, reference item may also be written
+ bool aNoDebug // if set, debug output is suppressed even if DBG_SCRIPTS is generally on
+)
+{
+ if (!aCtxP) return true; // no script, success
+ // execute script in given context
+ SYSYNC_TRY {
+ bool r=aCtxP->ExecuteScript(
+ aTScript,
+ NULL, // we don't need a result
+ false, // not a function
+ aFuncTableP, // context specific function table
+ aCallerContext, // caller's context private data pointer
+ aTargetItemP, // target (or "loosing") item
+ aTargetWritable, // if set, target item may be modified
+ aReferenceItemP, // reference for source (or "old" or "winning") item
+ aRefWritable, // if set, reference item may also be written
+ aNoDebug // if set, debug output is suppressed even if DBG_SCRIPTS is generally on
+ );
+ return r;
+ }
+ SYSYNC_CATCH (...)
+ SYSYNC_RETHROW;
+ SYSYNC_ENDCATCH
+} // TScriptContext::execute
+
+
+void TScriptContext::pushState(TScriptState aNewState, cUInt8P aBegin, uInt16 aLine)
+{
+ if (fStackEntries>=maxstackentries)
+ SYSYNC_THROW(TScriptErrorException("too many IF/ELSE/WHILE/LOOP nested",line));
+ // use defaults
+ if (aBegin==NULL) {
+ aBegin = np; // next token after current token
+ aLine = nextline;
+ }
+ // push current state
+ fScriptstack[fStackEntries].state=aNewState; // new state
+ fScriptstack[fStackEntries].begin=aBegin; // start of block
+ fScriptstack[fStackEntries].line=aLine; // line where block starts
+ fStackEntries++;
+} // TScriptContext::pushState
+
+
+// pop state from flow control state stack
+void TScriptContext::popState(TScriptState aCurrentStateExpected)
+{
+ if (fScriptstack[fStackEntries-1].state!=aCurrentStateExpected)
+ SYSYNC_THROW(TScriptErrorException("bad block/IF/ELSE/WHILE/LOOP nesting",line));
+ // remove entry
+ fStackEntries--; // remove stack entry
+ if (fStackEntries<1)
+ SYSYNC_THROW(TScriptErrorException(DEBUGTEXT("unbalanced control flow stack pop","scri3"),line));
+} // TScriptContext::popState
+
+
+// get variable specification, returns true if writable
+bool TScriptContext::getVarField(TItemField *&aItemFieldP)
+{
+ sInt16 objnum, varidx, arridx;
+ TMultiFieldItem *itemP=NULL;
+ bool writeable=false;
+ bool fidoffs=false;
+ uInt8 tk;
+
+ aItemFieldP=NULL; // none by default
+ tk=gettoken();
+ objnum=OBJ_AUTO; // default to automatic object selection
+ // check for object specifier
+ if (tk==TK_OBJECT) {
+ // object qualifier
+ objnum=*(p+2);
+ tk=gettoken();
+ // must be followed by identifier
+ if (tk!=TK_IDENTIFIER)
+ SYSYNC_THROW(TScriptErrorException("bad variable/field reference",line));
+ }
+ // check for object itself
+ if (tk==TK_IDENTIFIER) {
+ #ifdef SYDEBUG
+ cAppCharP idnam=(cAppCharP)(p+3);
+ int idlen=*(p+1)-1;
+ #endif
+ // get index
+ varidx=(sInt8)(*(p+2));
+ if (varidx==VARIDX_UNDEFINED)
+ SYSYNC_THROW(TScriptErrorException("Undefined identifier",line));
+ // check eventual array index
+ arridx=-1; // default to non-array
+ if (*np==TK_OPEN_ARRAY) {
+ tk=gettoken(); // consume open bracket
+ // check special field-offset access mode
+ if (*np==TK_PLUS) {
+ fidoffs=true;
+ gettoken(); // consume the plus
+ }
+ TItemField *fldP = new TIntegerField;
+ SYSYNC_TRY {
+ evalExpression(fldP,EXPRDBGTEST,NULL); // do not show array index expression evaluation
+ arridx=fldP->getAsInteger();
+ delete fldP;
+ }
+ SYSYNC_CATCH (...)
+ delete fldP;
+ SYSYNC_RETHROW;
+ SYSYNC_ENDCATCH
+ // check closing array bracket
+ tk=gettoken();
+ if (tk!=TK_CLOSE_ARRAY)
+ SYSYNC_THROW(TScriptErrorException("missing ']' for array reference",line));
+ }
+ // now access field
+ #ifdef SYDEBUG
+ sInt16 oai=arridx;
+ #endif
+ // - determine object if not explicitly specified
+ if (objnum==OBJ_AUTO) {
+ if (varidx<0) objnum=OBJ_LOCAL;
+ else objnum=OBJ_TARGET;
+ }
+ // - now access (arridx==-1 if we don't want to access leaf element)
+ if (objnum==OBJ_LOCAL) {
+ if (fidoffs) { varidx-=arridx; arridx=-1; } // use array index as offset within local fields (negative indices!)
+ aItemFieldP=getFieldOrVar(NULL,varidx,arridx);
+ writeable=true; // locals are always writeable
+ }
+ else {
+ // prepare index/arrayindex to access
+ if (fidoffs) { varidx+=arridx; arridx=-1; } // use array index as offset within field list
+ // get item to access
+ if (objnum==OBJ_TARGET) { itemP=fTargetItemP; writeable=fTargetWritable; }
+ else if (objnum==OBJ_REFERENCE) { itemP=fReferenceItemP; writeable=fRefWritable; }
+ if (itemP)
+ aItemFieldP=getFieldOrVar(itemP,varidx,arridx);
+ else
+ SYSYNC_THROW(TScriptErrorException("field not accessible in this context",line));
+ }
+ // error if no field
+ DBGSTRINGDEF(s);
+ DBGSTRINGDEF(sa);
+ DBGVALUESHOW(s,aItemFieldP);
+ if (EXPRDBGTEST) {
+ if (oai>=0) {
+ if (arridx!=-1)
+ StringObjPrintf(sa,"[%hd]",oai);
+ else
+ StringObjPrintf(sa,"[+%hd]",oai);
+ }
+ else {
+ // no array access
+ sa.erase();
+ }
+ SCRIPTDBGMSG((
+ "- %s: %.*s%s = %s",
+ objnum==OBJ_LOCAL ? "Local Variable" :
+ (objnum==OBJ_REFERENCE ? "OLD/LOOSING Field " : "Field"),
+ idlen,idnam,
+ sa.c_str(),
+ s.c_str()
+ ));
+ }
+ if (!aItemFieldP)
+ SYSYNC_THROW(TScriptErrorException("undefined identifier, bad array index or offset",line));
+ // return field pointer we've found, if any
+ return writeable;
+ }
+ else
+ SYSYNC_THROW(TScriptErrorException("expected identifier",line));
+} // TScriptContext::getVarField
+
+
+
+// evaluate function parameters
+void TScriptContext::evalParams(TScriptContext *aFuncContextP)
+{
+ uInt8 tk;
+ sInt16 paramidx;
+ TScriptVarDef *vardefP;
+ TItemField *fldP;
+
+ tk=gettoken();
+ if (tk!=TK_OPEN_PARANTHESIS)
+ SYSYNC_THROW(TScriptErrorException("missing '(' of function parameter list",line));
+ paramidx=0;
+ // special check for function with only optional params
+ if (aFuncContextP->getNumParams()>0 && aFuncContextP->getVarDef((short)0)->fIsOpt) {
+ if (*np==TK_CLOSE_PARANTHESIS) goto noparams; // first is optional already -> ok to see closing paranthesis here
+ }
+ // at least one param
+ while(paramidx<aFuncContextP->getNumParams()) {
+ vardefP=aFuncContextP->getVarDef(paramidx);
+ // check what param is expected next
+ if (vardefP->fIsRef) {
+ // by reference, we must have a writable lvalue here
+ fldP=NULL;
+ if (*np==TK_IDENTIFIER || *np==TK_OBJECT) {
+ // get writable field/var of caller
+ if (!getVarField(fldP))
+ SYSYNC_THROW(TScriptErrorException("expected writable by-reference parameter",line));
+ // type must match (or destination must be untyped)
+ if (vardefP->fVarType!=fldP->getType() && vardefP->fVarType!=fty_none)
+ SYSYNC_THROW(TScriptErrorException("expected by-reference parameter of type '%s'",line,ItemFieldTypeNames[vardefP->fVarType]));
+ // type must match (or destination must be untyped)
+ if (vardefP->fIsArray && !(fldP->isArray()))
+ SYSYNC_THROW(TScriptErrorException("expected array as by-reference parameter",line));
+ // assign field to function context's local var list
+ aFuncContextP->setLocalVar(paramidx,fldP);
+ }
+ else
+ SYSYNC_THROW(TScriptErrorException("expected by-reference parameter",line));
+ }
+ else {
+ // by value
+ // - get field where to assign value
+ if (vardefP->fVarType!=fty_none) {
+ // already prepared typed parameter
+ fldP=aFuncContextP->getLocalVar(paramidx);
+ // - evaluate expression into local var
+ evalExpression(fldP,EXPRDBGTEST); // do not show expression, as we will show param
+ }
+ else {
+ // untyped param, let evaluation create correct type
+ fldP=NULL;
+ evalExpression(fldP,EXPRDBGTEST); // do not show expression, as we will show param
+ // untyped params are not yet instantiated, assign expression result now
+ aFuncContextP->setLocalVar(paramidx,fldP);
+ }
+ }
+ DBGSTRINGDEF(s);
+ DBGVALUESHOW(s,fldP);
+ SCRIPTDBGMSG((
+ "- Parameter #%d (by %s) = %s",
+ paramidx+1,
+ vardefP->fIsRef ? "reference" : "value",
+ s.c_str()
+ ));
+ // got param
+ paramidx++;
+ // check parameter delimiters
+ if (paramidx<aFuncContextP->getNumParams()) {
+ // function potentially has more params
+ tk=gettoken();
+ if (tk!=TK_LIST_SEPARATOR) {
+ // - check if next is an optional parameter
+ if (aFuncContextP->getVarDef(paramidx)->fIsOpt)
+ goto endofparams; // yes, optional -> check for end of parameter list
+ else
+ SYSYNC_THROW(TScriptErrorException("expected ',' (more parameters)",line)); // non-optional parameter missing
+ }
+ }
+ }
+ // check end of parameter list
+noparams:
+ tk=gettoken();
+endofparams:
+ if (tk!=TK_CLOSE_PARANTHESIS)
+ SYSYNC_THROW(TScriptErrorException("missing ')' of function parameter list",line));
+} // TScriptContext::evalParams
+
+
+
+// evaluate term, return caller-owned field containing term data
+TItemField *TScriptContext::evalTerm(TItemFieldTypes aResultType)
+{
+ TItemFieldTypes termtype=aResultType; // default to result type
+ TItemField *termP=NULL;
+ uInt8 tk;
+ TScriptContext *funccontextP;
+ string *funcscript;
+ const char *funcname;
+ uInt16 funcnamelen;
+
+ // Evaluate term. A term is
+ // - a subexpression in paranthesis
+ // - a variable reference
+ // - a builtin function call
+ // - a user defined function call
+ // - a literal, including the special EMPTY and UNASSIGNED ones
+ // A term can be preceeded by a typecast
+ do {
+ tk=gettoken();
+ if (tk==TK_OPEN_PARANTHESIS) {
+ // typecast or subexpression
+ if (*np==TK_TYPEDEF) {
+ // this is a typecast
+ gettoken(); // actually get type
+ termtype=(TItemFieldTypes)(*(p+2)); // set new term target type
+ if (gettoken()!=TK_CLOSE_PARANTHESIS)
+ SYSYNC_THROW(TScriptErrorException("missing ')' after typecast",line));
+ if (aResultType!=fty_none && aResultType!=termtype)
+ SYSYNC_THROW(TScriptErrorException("invalid typecast",line));
+ continue; // now get term
+ }
+ else {
+ // process subexpression into term
+ if (termtype!=fty_none) {
+ // prepare distinct result type field
+ termP=newItemField(termtype, getSessionZones());
+ }
+ // puts result in a caller-owned termP (creates new one ONLY if caller passes NULL)
+ evalExpression(termP,EXPRDBGTEST); // do not show subexpression results
+ if (gettoken()!=TK_CLOSE_PARANTHESIS)
+ SYSYNC_THROW(TScriptErrorException("missing ')' after expression",line));
+ }
+ }
+ else if (tk==TK_FUNCTION || tk==TK_CONTEXTFUNCTION) {
+ // get function definition table reference
+ const TFuncTable *functableP =
+ (tk==TK_FUNCTION) ?
+ &BuiltInFuncTable : // globally available functions
+ fFuncTableP; // context specific functions
+ // get the function index
+ sInt16 funcid=*(p+2);
+ // now find the function definition record in the chain of function tables
+ void *callerContext = fCallerContext; // start with caller context of current script
+ while(functableP && funcid>=functableP->numFuncs) {
+ // function is in a chained table
+ // - adjust id to get offset based on next table
+ funcid-=functableP->numFuncs;
+ // - get next table and next caller context
+ if (functableP->chainFunc)
+ functableP = (TFuncTable *)functableP->chainFunc(callerContext);
+ else
+ functableP = NULL;
+ }
+ if (!functableP)
+ SYSYNC_THROW(TScriptErrorException("undefined context function",line));
+ // found function table and caller context, now get funcdef
+ const TBuiltInFuncDef *funcdefP = &(functableP->funcDefs[funcid]);
+ // execute built-in function call
+ funcname=funcdefP->fFuncName;
+ funcnamelen=100; // enough
+ SCRIPTDBGMSG(("- %s() built-in function call:",funcname));
+ // - get context for built-in function
+ funccontextP=new TScriptContext(fAppBaseP,fSessionP);
+ SYSYNC_TRY {
+ // define built-in parameters of function context
+ funccontextP->defineBuiltInVars(funcdefP);
+ // prepare local variables of function context
+ funccontextP->PrepareLocals();
+ // prepare parameters
+ evalParams(funccontextP);
+ // copy current line for reference (as builtins have no own line number
+ funccontextP->line=line;
+ // copy caller's context pointer (eventually modified by function table chaining)
+ funccontextP->fCallerContext=callerContext;
+ funccontextP->fParentContextP=this; // link to calling script context
+ // copy target and reference item vars
+ funccontextP->fTargetItemP = fTargetItemP;
+ funccontextP->fTargetWritable = fTargetWritable;
+ funccontextP->fReferenceItemP = fReferenceItemP;
+ funccontextP->fRefWritable = fRefWritable;
+ // execute function
+ funccontextP->executeBuiltIn(termP,funcdefP);
+ // show by-ref parameters after call
+ if (SCRIPTDBGTEST) {
+ // show by-ref variables
+ for (uInt16 i=0; i<funcdefP->fNumParams; i++) {
+ if (funcdefP->fParamTypes[i] & PARAM_REF) {
+ // is a parameter passed by reference, possibly changed by function call
+ DBGSTRINGDEF(s);
+ DBGVALUESHOW(s,funccontextP->getLocalVar(i));
+ SCRIPTDBGMSG((
+ "- return value of by-ref parameter #%d = %s",
+ i+1, s.c_str()
+ ));
+ }
+ }
+ }
+ // done
+ delete funccontextP;
+ }
+ SYSYNC_CATCH (...)
+ if (funccontextP) delete funccontextP;
+ SYSYNC_RETHROW;
+ SYSYNC_ENDCATCH
+ goto funcresult;
+ }
+ else if (tk==TK_USERFUNCTION) {
+ // user defined function call
+ funcname=(const char *)p+3;
+ funcnamelen=*(p+1)-1;
+ SCRIPTDBGMSGX(DBG_SCRIPTS+DBG_EXOTIC+DBG_SCRIPTEXPR,("- User-defined function %.*s call:",funcnamelen,funcname));
+ // - get function text
+ funcscript=getSyncAppBase()->getRootConfig()->fScriptConfigP->getFunctionScript(*(p+2));
+ if (!funcscript)
+ SYSYNC_THROW(TSyncException(DEBUGTEXT("invalid user function index","scri7")));
+ // %%% add caching of function contexts here eventually.
+ // Now we rebuild a context for every function call. Not extremely efficient...
+ funccontextP=NULL;
+ rebuildContext(fAppBaseP,*funcscript,funccontextP,fSessionP,true);
+ if (!funccontextP)
+ SYSYNC_THROW(TSyncException(DEBUGTEXT("no context for user-defined function call","scri5")));
+ SYSYNC_TRY {
+ // prepare parameters
+ evalParams(funccontextP);
+ // pass parent context
+ funccontextP->fParentContextP=this; // link to calling script context
+ // process sub-script into term
+ if (termtype!=fty_none)
+ termP=newItemField(termtype, getSessionZones()); // we want a specific type, pre-define the field
+ // execute function
+ if (!funccontextP->ExecuteScript(
+ *funcscript,
+ &termP, // receives result, if any
+ true, // is a function
+ NULL, NULL, // no context functions/datapointer
+ NULL, false, // no target fields
+ NULL, false // no reference fields
+ )) {
+ SCRIPTDBGMSG(("- User-defined function failed to execute"));
+ SYSYNC_THROW(TSyncException("User-defined function failed to execute properly"));
+ }
+ // done
+ if (funccontextP) delete funccontextP;
+ }
+ SYSYNC_CATCH (...)
+ if (funccontextP) delete funccontextP;
+ SYSYNC_RETHROW;
+ SYSYNC_ENDCATCH
+ funcresult:
+ DBGSTRINGDEF(s);
+ DBGVALUESHOW(s,termP);
+ SCRIPTDBGMSG((
+ "- %.*s() function result = %s",
+ funcnamelen,funcname,
+ s.c_str()
+ ));
+ }
+ else if (tk==TK_EMPTY) {
+ termP=newItemField(fty_none, getSessionZones());
+ termP->assignEmpty(); // make it EMPTY (that is, assigned)
+ //SCRIPTDBGMSGX(DBG_SCRIPTS+DBG_EXOTIC+DBG_SCRIPTEXPR,("- Literal: EMPTY"));
+ }
+ else if (tk==TK_UNASSIGNED) {
+ termP=newItemField(fty_none, getSessionZones());
+ termP->unAssign(); // make it (already is...) unassigned
+ //SCRIPTDBGMSGX(DBG_SCRIPTS+DBG_EXOTIC+DBG_SCRIPTEXPR,("- Literal: UNASSIGNED"));
+ }
+ else if (tk==TK_TRUE || tk==TK_FALSE) {
+ termP=newItemField(fty_integer, getSessionZones());
+ termP->setAsInteger(tk==TK_TRUE ? 1 : 0); // set 0 or 1
+ //SCRIPTDBGMSGX(DBG_SCRIPTS+DBG_EXOTIC+DBG_SCRIPTEXPR,("- Literal BOOLEAN: %d",tk==TK_TRUE ? 1 : 0));
+ }
+ else if (tk==TK_NUMERIC_LITERAL) {
+ // %%% add fty_float later eventually
+ // set type to integer if not another type requested
+ //SCRIPTDBGMSGX(DBG_SCRIPTS+DBG_EXOTIC+DBG_SCRIPTEXPR,("- Literal number: %0.*s",*(p+1),p+2));
+ if (termtype==fty_none) termtype=fty_integer;
+ goto literalterm;
+ }
+ else if (tk==TK_STRING_LITERAL) {
+ if (termtype==fty_none) termtype=fty_string;
+ //SCRIPTDBGMSGX(DBG_SCRIPTS+DBG_EXOTIC+DBG_SCRIPTEXPR,("- Literal string: \"%0.*s\"",*(p+1),p+2));
+ literalterm:
+ termP = newItemField(termtype, getSessionZones());
+ termP->setAsString((cAppCharP)(p+2),*(p+1));
+ }
+ else {
+ // must be identifier
+ reusetoken();
+ TItemField *varP=NULL;
+ getVarField(varP);
+ // copy and convert
+ if (termtype==fty_none) termtype=varP->getType();
+ termP=newItemField(termtype, getSessionZones());
+ (*termP)=(*varP); // simply assign (automatically converts if needed)
+ }
+ break;
+ } while(true); // repeat only for typecast
+ return termP; // return caller-owned field
+} // TScriptContext::evalTerm
+
+
+// evaluate expression, creates new or fills passed caller-owned field with result
+void TScriptContext::evalExpression(
+ TItemField *&aResultFieldP, // result (created new if passed NULL, modified and casted if passed a field)
+ bool aShowResult, // if set, this is the main expression (and we want to see the result in DBG)
+ TItemField *aLeftTermP, // if not NULL, chain-evaluate rest of expression according to aBinaryOp and aPreviousOp. WILL BE CONSUMED
+ uInt8 *aBinaryOpP, // operator to be applied between term passed in aLeftTermP and next term, will receive next operator that has same or lower precedence than aPreviousOp
+ uInt8 aPreviousOp // if an operator of same or lower precedence than this is found, expression evaluation ends
+)
+{
+ TItemFieldTypes termtype; // type of term
+ TItemField *termP; // next term
+ TItemField *resultP; // intermediate result
+ string s; // temp string
+ bool retainType=false;
+
+ uInt8 unaryop,binaryop,nextbinaryop;
+
+ fieldinteger_t a,b;
+
+ // defaults
+ binaryop=0; // none by default
+ if (aBinaryOpP) binaryop=*aBinaryOpP; // get one if one was passed
+ termtype=fty_none; // first term can be anything
+
+ // init first term/operation if there is one
+ if (binaryop && !aLeftTermP) binaryop=0; // security
+
+ // process expression
+ #ifdef SYDEBUG
+ // - determine if subexpression
+ if (!binaryop) {
+ // starting new evaluation
+ SCRIPTDBGMSGX(DBG_SCRIPTS+DBG_EXOTIC+DBG_SCRIPTEXPR,("- Starting expression evaluation"));
+ }
+ #endif
+
+ SYSYNC_TRY {
+ // Note: if binaryop!=0, we have a valid aLeftTermP
+ do {
+ resultP=NULL;
+ termP=NULL;
+ // Get next term
+ unaryop=0; // default to none
+ // - check for unaries
+ if (*np==TK_BITWISENOT || *np==TK_LOGICALNOT || *np==TK_MINUS) {
+ unaryop=gettoken();
+ // evaluate to integer, as unary ops all only make sense with integers
+ termtype=fty_integer;
+ }
+ // - get next term
+ termP=evalTerm(termtype);
+ // Apply unaries now
+ if (unaryop) {
+ // all unaries are integer ops
+ if (termP->getCalcType()!=fty_integer)
+ SYSYNC_THROW(TScriptErrorException("unary operator applied to non-integer",line));
+ fieldinteger_t ival = termP->getAsInteger();
+ // apply op
+ switch (unaryop) {
+ case TK_MINUS:
+ ival=-ival;
+ break;
+ case TK_BITWISENOT:
+ ival= ~ival;
+ break;
+ case TK_LOGICALNOT:
+ ival= !termP->getAsBoolean();
+ break;
+ }
+ // store back into term field
+ termP->setAsInteger(ival);
+ }
+ // now we have a new term in termP (or NULL if term had no result)
+ // - check for a next operator
+ nextbinaryop=0;
+ if (*np>=TK_BINOP_MIN && *np <=TK_BINOP_MAX) {
+ // this is a binary operator
+ nextbinaryop=gettoken();
+ }
+ // - check for non-existing term
+ if (!termP && (binaryop || nextbinaryop))
+ SYSYNC_THROW(TScriptErrorException("non-value cannot be used in expression",line));
+ // - check if there is a previous operation pending
+ if (binaryop) {
+ fieldinteger_t intres;
+ // There is an operation to perform between aLeftTermP and current term (or expression with
+ // higher precedence)
+ // - get precedences (make them minus to have lowerprec<higherprec)
+ sInt8 currentprec=-(binaryop & TK_OP_PRECEDENCE_MASK);
+ sInt8 nextprec=-(nextbinaryop & TK_OP_PRECEDENCE_MASK);
+ // - we must evaluate part of the expression BEFORE applying binaryop if next operator
+ // has higher precedence than current one
+ if (nextbinaryop && nextprec>currentprec) {
+ // next operator has higher precedence, evaluate everything up to next operator with same or
+ // lower precedence as current one BEFORE applying binaryop
+ evalExpression(
+ resultP, // we'll receive the result here
+ EXPRDBGTEST, // do not normally show subexpression results
+ termP, // left term of new expression, WILL BE CONSUMED
+ &nextbinaryop, // operator, will be updated to receive next operator to apply
+ binaryop // evaluation stops when expression reaches operator with same or lower precedence
+ );
+ // nextbinaryop now has next operation to perform AFTER doing binaryop
+ termP=resultP; // original termP has been consumed, use result as new termP
+ resultP=NULL;
+ }
+ // Now we can apply binaryop between lasttermP and termP
+ // Note: this must CONSUME termP AND aLeftTermP and CREATE resultP
+ // - check for operators that work with multiple types
+ if (binaryop==TK_PLUS) {
+ if (aLeftTermP->getCalcType()!=fty_integer) {
+ // treat plus as general append (defaults to string append for non-arrays)
+ aLeftTermP->append(*termP);
+ resultP=aLeftTermP; // we can simply pass the pointer
+ aLeftTermP=NULL; // consume
+ goto opdone; // operation done, get rid of termP
+ }
+ }
+ else if (binaryop>=TK_LESSTHAN && binaryop<=TK_NOTEQUAL) {
+ // comparison operators
+ sInt16 cmpres=-3; // will never happen
+ bool neg=false;
+ switch (binaryop) {
+ // - comparison
+ case TK_LESSTHAN : cmpres=-1; break;
+ case TK_GREATERTHAN : cmpres=1; break;
+ case TK_LESSEQUAL : cmpres=1; neg=true; break;
+ case TK_GREATEREQUAL : cmpres=-1; neg=true; break;
+ // - equality
+ case TK_EQUAL : cmpres=0; break;
+ case TK_NOTEQUAL : cmpres=0; neg=true; break;
+ }
+ // do comparison
+ if (termP->getType()==fty_none) {
+ // EMPTY or UNASSIGNED comparison
+ if (termP->isUnassigned()) {
+ intres = aLeftTermP->isUnassigned() ? 0 : 1; // if left is assigned, it is greater
+ }
+ else {
+ intres = aLeftTermP->isEmpty() ? 0 : 1; // if left is not empty, it is greater
+ }
+ }
+ else {
+ // normal comparison
+ intres=aLeftTermP->compareWith(*termP);
+ }
+ if (intres==SYSYNC_NOT_COMPARABLE) intres=0; // false
+ else {
+ intres=cmpres==intres;
+ if (neg) intres=!intres;
+ }
+ retainType= aLeftTermP->getType()==fty_integer;; // comparisons must always have plain integer result
+ goto intresult;
+ }
+ // - integer operators
+ a=aLeftTermP->getAsInteger();
+ b=termP->getAsInteger();
+ // for integer math operators, retain type of left term if it can calculate as integer
+ // (such that expressions like: "timestamp + integer" will have a result type of timestamp)
+ retainType = aLeftTermP->getCalcType()==fty_integer;
+ // now perform integer operation
+ switch (binaryop) {
+ // - multiply, divide
+ case TK_MULTIPLY : intres=a*b; break;
+ case TK_DIVIDE : intres=a/b; break;
+ case TK_MODULUS : intres=a%b; break;
+ // - add, subtract
+ case TK_PLUS : intres=a+b; break;
+ case TK_MINUS : intres=a-b; break;
+ // - shift
+ case TK_SHIFTLEFT : intres=a<<b; break;
+ case TK_SHIFTRIGHT : intres=a>>b; break;
+ // - bitwise AND
+ case TK_BITWISEAND : intres=a&b; break;
+ // - bitwise XOR
+ case TK_BITWISEXOR : intres=a^b; break;
+ // - bitwise OR
+ case TK_BITWISEOR : intres=a|b; break;
+ // - logical AND
+ case TK_LOGICALAND : intres=a&&b; break;
+ // - logical OR
+ case TK_LOGICALOR : intres=a||b; break;
+ default:
+ SYSYNC_THROW(TScriptErrorException("operator not implemented",line));
+ }
+ intresult:
+ // save integer result (optimized, generate new field only if aLeftTermP is not already integer-calc-type)
+ if (retainType)
+ resultP=aLeftTermP; // retain original left-side type (including extra information like time zone context and rendering type)
+ else {
+ // create new integer field
+ resultP = newItemField(fty_integer, getSessionZones());
+ delete aLeftTermP;
+ }
+ aLeftTermP=NULL;
+ resultP->setAsInteger(intres);
+ opdone:
+ // check for special conditions when operating on timestamps
+ if (resultP->isBasedOn(fty_timestamp) && termP->isBasedOn(fty_timestamp)) {
+ // both based on timestamp.
+ if (binaryop==TK_MINUS && !static_cast<TTimestampField *>(resultP)->isDuration() && !static_cast<TTimestampField *>(termP)->isDuration()) {
+ // subtracted two points in time -> result is duration
+ static_cast<TTimestampField *>(resultP)->makeDuration();
+ }
+ }
+ // get rid of termP
+ delete termP;
+ termP=NULL;
+ } // if binary op was pending
+ else {
+ // no binary op pending, simply pass term as result
+ resultP=termP;
+ termP=NULL;
+ }
+ // Now termP and aLeftTermP are consumed, resultP is alive
+ // - determine next operation
+ if (nextbinaryop) {
+ // check precedence, if we can pass back to previous
+ sInt8 lastprec=-(aPreviousOp & TK_OP_PRECEDENCE_MASK);
+ sInt8 thisprec=-(nextbinaryop & TK_OP_PRECEDENCE_MASK);
+ if (aPreviousOp && thisprec<=lastprec) break; // force exit, resultP is assigned
+ }
+ // result is going to be left term for next operation
+ aLeftTermP=resultP;
+ binaryop=nextbinaryop;
+ } while(nextbinaryop); // as long as expression continues
+ // show result
+ #ifdef SYDEBUG
+ // show final result of main expression only
+ if (!nextbinaryop && aShowResult) {
+ DBGVALUESHOW(s,resultP);
+ SCRIPTDBGMSG((
+ "- Expression result: %s",
+ s.c_str()
+ ));
+ }
+ #endif
+ // convert result to desired type
+ if (!aResultFieldP) {
+ // no return field passed, just pass pointer (and ownership) of our resultP
+ aResultFieldP=resultP;
+ }
+ else {
+ // return field already exists, assign value (will convert type if needed)
+ if (resultP) {
+ (*aResultFieldP)=(*resultP);
+ delete resultP; // not passed to caller, so we must get rid of it
+ }
+ else {
+ // no result, unAssign
+ aResultFieldP->unAssign();
+ }
+ }
+ // return nextbinaryop if requested
+ if (aBinaryOpP) *aBinaryOpP=nextbinaryop; // this is how we ended
+ }
+ SYSYNC_CATCH (...)
+ // delete terms
+ if (resultP) delete resultP;
+ if (aLeftTermP) delete aLeftTermP;
+ if (termP) delete termP;
+ SYSYNC_RETHROW;
+ SYSYNC_ENDCATCH
+} // TScriptContext:evalExpression
+
+
+
+// execute script
+// returns false if script execution was not successful
+bool TScriptContext::ExecuteScript(
+ const string &aTScript,
+ TItemField **aResultPP, // if not NULL, a result field will be returned here (must be deleted by caller)
+ bool aAsFunction, // if set, this is a function call
+ const TFuncTable *aFuncTableP, // context's function table, NULL if none
+ void *aCallerContext, // free pointer eventually having a meaning for context functions
+ TMultiFieldItem *aTargetItemP, // target (or "loosing") item
+ bool aTargetWritable, // if set, target item may be modified
+ TMultiFieldItem *aReferenceItemP, // reference for source (or "old" or "winning") item
+ bool aRefWritable, // if set, reference item may also be written
+ bool aNoDebug // if set, debug output is suppressed even if DBG_SCRIPTS is generally on
+)
+{
+ if (aResultPP) *aResultPP=NULL; // no result yet
+ TItemField *resultP = NULL; // none yet
+
+ uInt8 tk; // current token
+ TScriptState sta; // status
+
+ skipping=0; // skipping level
+
+ bool funchdr=aAsFunction; // flag if processing function header first
+ if (funchdr) skipping=1; // skip stuff
+
+ // endless loop protection
+ lineartime_t loopmaxtime=0; // not in a loop
+
+ // save context parameters
+ fTargetItemP=aTargetItemP; // target (or "loosing") item
+ fTargetWritable=aTargetWritable; // if set, target item may be modified
+ fReferenceItemP=aReferenceItemP; // reference for source (or "old" or "winning") item
+ fRefWritable=aRefWritable; // if set, reference item may also be written
+ fFuncTableP=aFuncTableP; // context's function table, NULL if none
+ fCallerContext=aCallerContext;
+
+ #ifdef SYDEBUG
+ debugon = !aNoDebug;
+ #endif
+
+ // init parsing (for execute)
+ initParse(aTScript,true);
+ // test if there's something to execute at all
+ if (ep>bp) {
+ #ifdef SYDEBUG
+ if (aAsFunction) {
+ SCRIPTDBGMSGX(DBG_SCRIPTS+DBG_HOT,("* Starting execution of user-defined function"));
+ } else {
+ SCRIPTDBGSTART(scriptname ? scriptname : "<unnamed>");
+ }
+ #endif
+ SYSYNC_TRY {
+ // init state stack
+ fStackEntries=0; // stack is empty
+ pushState(ssta_statement); // start with statement
+
+ // execute
+ while ((tk=gettoken())) {
+ resultP=NULL;
+ // start of statement
+ // - check for block
+ if (tk==TK_BEGIN_BLOCK) {
+ // push current state to stack and start new block
+ pushState(ssta_block);
+ if (funchdr) {
+ // start of first block = start of function body, stop skipping
+ funchdr=false;
+ skipping=0;
+ }
+ continue; // process next
+ }
+ else if (tk==TK_END_BLOCK) {
+ // pop block start, throw error if none there
+ popState(ssta_block);
+ // treat like end-of-statement, check for IF/ELSE/LOOP etc.
+ goto endstatement;
+ }
+ else if (tk==TK_END_STATEMENT) {
+ goto endstatement;
+ }
+ // - handle skipping of conditionally excluded blocks
+ if (skipping!=0) {
+ // only IF and ELSE are of any relevance
+ if (tk==TK_IF) {
+ pushState(ssta_if); // nested IF
+ skipping++; // skip anyway
+ }
+ else if (tk==TK_ELSE) {
+ // %%% this case probably never happens, as it is pre-fetched at end-of-statement
+ // only push ELSE if this is not a chained ELSE IF
+ if (*np!=TK_IF) pushState(ssta_else); // nested ELSE
+ }
+ // all others are irrelevant during skip
+ continue;
+ }
+ else {
+ // really executing
+ // - check empty statement
+ if (tk==TK_END_STATEMENT) goto endstatement;
+ // - check IF statement
+ else if (tk==TK_IF || tk==TK_WHILE) {
+ // IF or WHILE conditional
+ // - remember for WHILE case as condition must be re-evaluated for every loop
+ cUInt8P condBeg = p;
+ uInt16 condLine = line;
+ // - process boolean expression
+ tk=gettoken();
+ if (tk!=TK_OPEN_PARANTHESIS)
+ SYSYNC_THROW(TScriptErrorException("missing '(' after IF or WHILE",line));
+ SCRIPTDBGMSGX(DBG_SCRIPTS+DBG_EXOTIC+DBG_SCRIPTEXPR,("- IF or WHILE, evaluating condition..."));
+ evalExpression(resultP,EXPRDBGTEST);
+ tk=gettoken();
+ if (tk!=TK_CLOSE_PARANTHESIS)
+ SYSYNC_THROW(TScriptErrorException("missing ')' in IF or WHILE",line));
+ // - determine which branch to process
+ if (!(resultP && resultP->getAsBoolean()))
+ skipping=1; // enter skip level 1
+ SCRIPTDBGMSGX(DBG_SCRIPTS+DBG_EXOTIC,("- %s condition is %s", *condBeg==TK_WHILE ? "WHILE" : "IF", skipping ? "false" : "true"));
+ delete resultP;
+ resultP=NULL;
+ if (*condBeg==TK_WHILE) {
+ if (skipping) {
+ SCRIPTDBGMSGX(DBG_SCRIPTS+DBG_HOT,("- WHILE condition is false -> skipping WHILE body"));
+ }
+ // for every subsequent loop, WHILE must be reprocessed
+ pushState(ssta_while,condBeg,condLine);
+ goto startloop; // limit loop execution time
+ }
+ else {
+ // process statement (block) after IF
+ pushState(ssta_if);
+ }
+ continue;
+ }
+ // Note: ELSE is checked at end of statement only
+ // Check loop beginning
+ else if (tk==TK_LOOP) {
+ // initiate loop
+ pushState(ssta_loop);
+ SCRIPTDBGMSGX(DBG_SCRIPTS+DBG_EXOTIC,("- LOOP entered"));
+ startloop:
+ // - remember entry time of outermost loop
+ if (loopmaxtime==0) {
+ uInt32 loopSecs = getSyncAppBase()->getRootConfig()->fScriptConfigP->fMaxLoopProcessingTime;
+ if (loopSecs>0) {
+ loopmaxtime=
+ getSystemNowAs(TCTX_UTC, getSessionZones())+
+ secondToLinearTimeFactor * loopSecs;
+ }
+ else {
+ loopmaxtime=maxLinearTime; // virtually unlimited time
+ }
+ }
+ continue;
+ }
+ else if (tk==TK_CONTINUE || tk==TK_BREAK) {
+ // see if there is a LOOP or WHILE on the stack
+ bool inloop=false;
+ bool inwhile=false;
+ sInt16 sp=fStackEntries;
+ while (sp>0) {
+ sta = fScriptstack[sp-1].state;
+ if (sta==ssta_loop) { inloop=true; break; } // we are in a LOOP
+ if (sta==ssta_while) { inwhile=true; break; } // we are in a WHILE
+ sp--;
+ }
+ // no loop found, error
+ if (!inloop && !inwhile)
+ SYSYNC_THROW(TScriptErrorException("BREAK or CONTINUE without enclosing LOOP or WHILE",line));
+ if (tk==TK_BREAK) {
+ // make sure we are skipping everything (including any number of nested if/else
+ // and blocks up to reaching next end-of-loop). Note that if a LOOP or WHILE is in the
+ // skipped part, it will not cause troubles because it is not recognized as
+ // such while skipping.
+ skipping=maxstackentries;
+ }
+ else {
+ // continue, remove stack entries down to LOOP or WHILE and jump to beginning of loop
+ // - same as if we had reached the bottom of the loop, but pop
+ // open blocks, if's and else's first
+ // - sta is ssta_loop or ssta_while to decide if we must pop the status or not
+ goto loopcontinue;
+ }
+ }
+ else if (tk==TK_TYPEDEF) {
+ // declaration, skip it
+ do {
+ if (*np!=TK_IDENTIFIER)
+ SYSYNC_THROW(TScriptErrorException("Invalid declaration",line));
+ tk=gettoken(); // swallow identifier
+ if (*np==TK_OPEN_ARRAY) {
+ // must be array declaration
+ tk=gettoken(); // swallow [
+ if (*np==TK_CLOSE_ARRAY)
+ gettoken(); // swallow ]
+ }
+ if (*np!=TK_LIST_SEPARATOR) break;
+ gettoken(); // swallow separator
+ } while(true);
+ // end of statement should follow here, will be checked below.
+ }
+ else if (tk==TK_IDENTIFIER || tk==TK_OBJECT) {
+ SCRIPTDBGMSGX(DBG_SCRIPTS+DBG_EXOTIC+DBG_SCRIPTEXPR,("- Starting assignment/unstored expression"));
+ // could be assignment if identifier is followed by TK_ASSIGN
+ // otherwise, it could be a simple expression evaluation
+ cUInt8P ts=p; // remember start of statement
+ uInt16 tl=line; // remember line as well
+ TItemField *fldP;
+ reusetoken();
+ bool writeable=getVarField(fldP);
+ // now check if this is an assignment
+ if (*np==TK_ASSIGN) {
+ gettoken(); // swallow TK_ASSIGN
+ // must be writeable
+ if (!writeable)
+ SYSYNC_THROW(TScriptErrorException("Not allowed to assign to this field/variable",line));
+ // evaluate expression into given field
+ evalExpression(fldP,false); // we show the result ourselves
+ DBGSTRINGDEF(s);
+ DBGVALUESHOW(s,fldP);
+ SCRIPTDBGMSG(("- Assigned expression result = %s",s.c_str()));
+ }
+ else {
+ // must be plain expression
+ np=ts; // back to start of statement;
+ nextline=tl;
+ evalExpression(resultP,true); // we always want to see the result
+ SCRIPTDBGMSGX(DBG_SCRIPTS+DBG_EXOTIC+DBG_SCRIPTEXPR,("- Evaluated unstored expression"));
+ // - if nothing follows, script ends here with result of expression
+ if (*np==0) break;
+ // - otherwise, forget result
+ delete resultP;
+ resultP=NULL;
+ }
+ }
+ else if (tk==TK_RETURN) {
+ // simply return if no expression follows
+ resultP=NULL;
+ if (*np==TK_END_STATEMENT) {
+ SCRIPTDBGMSGX(DBG_SCRIPTS+DBG_HOT,("- RETURN: ending %s without result",aAsFunction ? "function" : "script"));
+ break;
+ }
+ // evaluate expression first
+ SCRIPTDBGMSGX(DBG_SCRIPTS+DBG_EXOTIC+DBG_SCRIPTEXPR,("- RETURN: evaluating result expression..."));
+ evalExpression(resultP,false); // we always show the result ourselves
+ DBGSTRINGDEF(s);
+ DBGVALUESHOW(s,resultP);
+ SCRIPTDBGMSGX(DBG_SCRIPTS+DBG_HOT,(
+ "- RETURN: ending %s with result = %s",
+ aAsFunction ? "function" : "script",
+ s.c_str()
+ ));
+ // now end script
+ break;
+ }
+ else {
+ // everything else must be plain expression (e.g. function calls etc.)
+ SCRIPTDBGMSGX(DBG_SCRIPTS+DBG_EXOTIC+DBG_SCRIPTEXPR,("- Starting evaluating unstored expression"));
+ reusetoken();
+ evalExpression(resultP,false); // we always show the result ourselves
+ SCRIPTDBGMSGX(DBG_SCRIPTS+DBG_EXOTIC+DBG_SCRIPTEXPR,("- Evaluated unstored expression"));
+ /* %%% wrong, prevents script from returning NOTHING!
+ to return a value, RETURN *must* be used!
+ // - if nothing follows, script ends here with result of expression
+ if (*np==0) {
+ DBGSTRINGDEF(s);
+ DBGVALUESHOW(s,resultP);
+ SCRIPTDBGMSGX(DBG_SCRIPTS+DBG_HOT,(
+ "- Script ends with result = %s",
+ s.c_str()
+ ));
+ break;
+ }
+ */
+ // - otherwise, forget result
+ delete resultP;
+ resultP=NULL;
+ }
+ } // not skipping
+ // Statement executed, next must be end-of-statement
+ tk=gettoken();
+ if (tk!=TK_END_STATEMENT)
+ SYSYNC_THROW(TScriptErrorException("missing ';' after statement",line));
+ // Statement executed and properly terminated
+ endstatement:
+ // check state
+ sta=fScriptstack[fStackEntries-1].state;
+ // check end of IF block
+ if (sta==ssta_if) {
+ SCRIPTDBGMSGX(DBG_SCRIPTS+DBG_EXOTIC,("- End of %s IF",skipping ? "skipped" : "executed"));
+ popState(sta); // end this state
+ if (skipping) {
+ // IF branch was not executed, check for else
+ if (*np==TK_ELSE) {
+ // else follows
+ // - swallow TK_ELSE
+ gettoken();
+ // - check for ELSE IF chain
+ // (in this case, we must NOT push a state, but let chained IF push TK_IF again
+ // and also increment skipping again, after it is decremented by one below)
+ if (*np!=TK_IF) {
+ pushState(ssta_else); // end-of-chain ELSE: enter else state
+ // keep skipping (compensate for decrement below) ONLY if end-of-chain ELSE
+ // is not to be executed due to surrounding skip.
+ if (skipping>1) skipping++;
+ }
+ }
+ // no ELSE, just continue with next statement
+ skipping--; // reduce skip level
+ continue;
+ }
+ else {
+ // IF branch was executed, check for else
+ if (*np==TK_ELSE) {
+ // else follows, skip it (including all chained IFs)
+ skipping=1;
+ gettoken(); // swallow ELSE
+ // - check for ELSE IF chain
+ if (*np==TK_IF) {
+ // chained if while skipping
+ gettoken(); // consume it (avoid regular parsing to see it and increment skipping)
+ pushState(ssta_chainif); // chained IF, make sure nothing is executed up to and including end-of-chain else
+ }
+ else
+ pushState(ssta_else); // process as skipped ELSE part
+ }
+ // no else, simply continue execution
+ continue;
+ }
+ }
+ else if (sta==ssta_chainif) {
+ // only entered while skipping rest of chain
+ SCRIPTDBGMSGX(DBG_SCRIPTS+DBG_EXOTIC,("- End of skipped ELSE IF"));
+ if (*np==TK_ELSE) {
+ gettoken(); // consume it
+ if (*np==TK_IF) {
+ // chained if while skipping
+ gettoken(); // consume it (avoid regular parsing to see it and increment skipping)
+ // stay in ssta_chainif
+ }
+ else {
+ popState(ssta_chainif); // end of ELSE IF chain
+ pushState(ssta_else); // rest is a normal skipped ELSE part
+ }
+ }
+ else {
+ // end of skipped chained ifs
+ popState(ssta_chainif);
+ skipping--;
+ }
+ }
+ else if (sta==ssta_else) {
+ SCRIPTDBGMSGX(DBG_SCRIPTS+DBG_EXOTIC,("- End of %s ELSE",skipping ? "skipped" : "executed"));
+ popState(ssta_else); // end of else state
+ if (skipping) skipping--;
+ }
+ // check end of LOOP block
+ else if (sta==ssta_loop || sta==ssta_while) {
+ // Note: we'll never see that for a completely skipped loop/while, as
+ // no ssta_loop/ssta_while is pushed while skipping.
+ if (!skipping) goto loopcontinue; // not end of while or breaking out of loop, repeat
+ // - end of loop reached
+ popState(sta);
+ skipping=0; // end of loop found after BREAK, continue normal execution
+ SCRIPTDBGMSGX(DBG_SCRIPTS+DBG_EXOTIC,("- End of WHILE or LOOP"));
+ }
+ // nothing special, just execute next statement
+ continue;
+ loopcontinue:
+ // continue at beginning of loop which is at top of state stack
+ if (getSystemNowAs(TCTX_UTC, getSessionZones())>loopmaxtime)
+ SYSYNC_THROW(TScriptErrorException("loop execution aborted because <looptimeout> reached (endless loop?)",line));
+ // go to beginning of loop: restore position of beginning of loop (including condition in case of WHILE)
+ np=fScriptstack[fStackEntries-1].begin; // next token after current token
+ nextline=fScriptstack[fStackEntries-1].line; // line of next token after current token
+ linesource=NULL; // prevent showing source (which is that of NEXT line, not that of jump target's
+ // for while, we must pop the stack entry as it will be recreated by re-excution of the WHILE statement
+ if (sta==ssta_while)
+ popState(sta);
+ SCRIPTDBGMSGX(DBG_SCRIPTS+DBG_HOT,("- Starting next iteration of LOOP/WHILE -> jumping to line %hd",nextline));
+ continue;
+ } // while more tokens
+ } // try
+ SYSYNC_CATCH (exception &e)
+ // make sure field is deleted
+ if (resultP) delete resultP;
+ // show error message
+ SCRIPTDBGMSGX(DBG_ERROR,("Warning: TERMINATING SCRIPT WITH ERROR: %s",e.what()));
+ if (!aAsFunction) SCRIPTDBGEND();
+ return false;
+ SYSYNC_ENDCATCH
+ #ifdef SYDEBUG
+ if (aAsFunction) {
+ SCRIPTDBGMSGX(DBG_SCRIPTS+DBG_HOT,("* Successfully finished execution of user-defined function"));
+ } else {
+ SCRIPTDBGEND();
+ }
+ #endif
+ } // if something to execute at all
+ // return most recent evaluated expression (normally RETURN expression)
+ if (aResultPP) {
+ // caller wants to see result, pass it back
+ if (*aResultPP && resultP) {
+ // script result field already exists, assign value
+ (**aResultPP)=(*resultP);
+ delete resultP;
+ }
+ else {
+ // script result field does not yet exist, just pass result (even if it is NULL)
+ *aResultPP=resultP;
+ }
+ }
+ else if (resultP) delete resultP; // nobody needs the result, delete it
+ // execution successful
+ return true;
+} // TScriptContext::ExecuteScript
+
+
+// get variable definition by name, NULL if none defined yet
+TScriptVarDef *TScriptContext::getVarDef(cAppCharP aVarName,size_t len)
+{
+ TVarDefs::iterator pos;
+ for (pos=fVarDefs.begin(); pos!=fVarDefs.end(); pos++) {
+ if (strucmp((*pos)->fVarName.c_str(),aVarName,0,len)==0) return (*pos);
+ }
+ return NULL;
+} // TScriptContext::getVarDef
+
+
+// get variable definition by index, NULL if none defined yet
+TScriptVarDef *TScriptContext::getVarDef(sInt16 aLocalVarIdx)
+{
+ if (aLocalVarIdx<0 || uInt32(aLocalVarIdx)>=fVarDefs.size()) return NULL;
+ return fVarDefs[aLocalVarIdx];
+} // TScriptContext::getVarDef
+
+
+// - get identifier index (>=0: field index, <0: local var)
+// returns VARIDX_UNDEFINED for unknown identifier
+sInt16 TScriptContext::getIdentifierIndex(sInt16 aObjIndex, TFieldListConfig *aFieldListConfigP, cAppCharP aIdentifier,size_t aLen)
+{
+ // first look for local variable
+ if (aObjIndex==OBJ_AUTO || aObjIndex==OBJ_LOCAL) {
+ TScriptVarDef *vardefP = getVarDef(aIdentifier,aLen);
+ if (vardefP) {
+ return - (sInt16)vardefP->fIdx-1;
+ }
+ }
+ if (aObjIndex==OBJ_AUTO || aObjIndex==OBJ_TARGET || aObjIndex==OBJ_REFERENCE) {
+ // look for field with that name
+ if (!aFieldListConfigP) return VARIDX_UNDEFINED; // no field list available here
+ return aFieldListConfigP->fieldIndex(aIdentifier,aLen);
+ }
+ else return VARIDX_UNDEFINED; // unknown object, unknown index
+} // TScriptContext::getIdentifierIndex
+
+
+// get field by fid, can also be field of aItem, also resolves arrays
+TItemField *TScriptContext::getFieldOrVar(TMultiFieldItem *aItemP, sInt16 aFid, sInt16 aArrIdx)
+{
+ TItemField *fldP = getFieldOrVar(aItemP,aFid);
+ if (!fldP) return NULL;
+ #ifdef ARRAYFIELD_SUPPORT
+ if (aArrIdx>=0)
+ return fldP->getArrayField(aArrIdx);
+ else
+ return fldP; // return field without array resolution
+ #else
+ if (aArrIdx>0) return NULL;
+ return fldP;
+ #endif
+} // TScriptContext::getFieldOrVar
+
+
+// get field by fid, can be field of aItem (positive aFid) or local variable (negative fid)
+TItemField *TScriptContext::getFieldOrVar(TMultiFieldItem *aItemP, sInt16 aFid)
+{
+ if (aFid<0) return getLocalVar(-aFid-1);
+ if (!aItemP) return NULL;
+ return aItemP->getField(aFid);
+} // TScriptContext::getFieldOrVar
+
+
+// get local var by local index
+TItemField *TScriptContext::getLocalVar(sInt16 aVarIdx)
+{
+ if (aVarIdx<0 || aVarIdx>=fNumVars) return NULL;
+ return fFieldsP[aVarIdx];
+} // TScriptContext::getLocalVar
+
+
+// set local var by local index (used for passing references)
+void TScriptContext::setLocalVar(sInt16 aVarIdx, TItemField *aFieldP)
+{
+ if (aVarIdx<0 || aVarIdx>=fNumVars) return;
+ fFieldsP[aVarIdx]=aFieldP; // set new
+} // TScriptContext::setLocalVar
+
+
+/* end of TScriptContext implementation */
+
+
+#ifdef ENGINEINTERFACE_SUPPORT
+
+// TScriptVarKey
+// =============
+
+
+// get FID for specified name
+sInt16 TScriptVarKey::getFidFor(cAppCharP aName, stringSize aNameSz)
+{
+ if (fScriptContext==NULL) return VARIDX_UNDEFINED;
+ // check for iterator commands first
+ if (strucmp(aName,VALNAME_FIRST)==0) {
+ fIterator=0;
+ if (fIterator>=sInt32(fScriptContext->fVarDefs.size())) return VARIDX_UNDEFINED;
+ return -fIterator-1;
+ }
+ else if (strucmp(aName,VALNAME_NEXT)==0) {
+ fIterator++;
+ if (fIterator>=sInt32(fScriptContext->fVarDefs.size())) return VARIDX_UNDEFINED;
+ return -fIterator-1;
+ }
+ else
+ return fScriptContext->getIdentifierIndex(OBJ_LOCAL, NULL, aName, aNameSz);
+} // TScriptVarKey::getFidFor
+
+
+
+// get base field from FID
+TItemField *TScriptVarKey::getBaseFieldFromFid(sInt16 aFid)
+{
+ if (fScriptContext==NULL) return NULL;
+ return fScriptContext->getFieldOrVar(NULL, aFid);
+} // TScriptVarKey::getBaseFieldFromFid
+
+
+// get field name from FID
+bool TScriptVarKey::getFieldNameFromFid(sInt16 aFid, string &aFieldName)
+{
+ if (fScriptContext==NULL) return false;
+ TScriptVarDef *vardefP = fScriptContext->getVarDef(-aFid-1);
+ if (vardefP) {
+ aFieldName = vardefP->fVarName;
+ return true;
+ }
+ // none found
+ return false;
+} // TScriptVarKey::getFieldNameFromFid
+
+
+
+#endif // ENGINEINTERFACE_SUPPORT
+
+
+} // namespace sysync
+
+
+#endif // SCRIPT_SUPPORT
+
+// eof
diff --git a/src/sysync/scriptcontext.h b/src/sysync/scriptcontext.h
new file mode 100755
index 0000000..359f158
--- /dev/null
+++ b/src/sysync/scriptcontext.h
@@ -0,0 +1,483 @@
+/*
+ * File: scriptcontext.cpp
+ *
+ * Author: Lukas Zeller (luz@synthesis.ch)
+ *
+ * TScriptContext
+ * Environment to tokenize, prepare and run scripts
+ *
+ * Copyright (c) 2002-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ * 2002-09-11 : luz : created
+ *
+ *
+ */
+
+#ifdef SCRIPT_SUPPORT
+
+#ifndef SCRIPT_CONTEXT_H
+#define SCRIPT_CONTEXT_H
+
+// includes
+#include "sysync.h"
+
+
+#include "itemfield.h"
+#include "multifielditem.h"
+
+
+using namespace sysync;
+
+namespace sysync {
+
+
+/*
+ * builtin function definitions
+ */
+
+#define PARAM_TYPEMASK 0x1F
+#define PARAM_OPT 0x20
+#define PARAM_REF 0x40
+#define PARAM_ARR 0x80
+
+#define VAL(x) ( (uInt8)x)
+#define OPTVAL(x) (((uInt8)x)+PARAM_OPT)
+#define REF(x) (((uInt8)x)+PARAM_REF)
+#define REFARR(x) (((uInt8)x)+PARAM_REF+PARAM_ARR)
+
+
+// Token definitions
+
+// - Multi-Byte tokens:
+// tldddddddddddddd
+// - t=token ID alphanum.
+// - l=length of additional token data, 0x00..0xFF as one char
+// - dddddd=additional token data
+
+// highest Multibyte token
+#define TK_MAX_MULTIBYTE 0x1F
+
+// - literals, dddd=literal string
+#define TK_NUMERIC_LITERAL 0x10
+#define TK_STRING_LITERAL 0x11
+// - variable identifiers, dddd = idddddd
+// where i=room for field index, dddd=identifier
+#define TK_IDENTIFIER 0x12
+// - script line token, dd = line number 16bit, followed by source text (NUL terminated) if script debug on
+#define TK_SOURCELINE 0x13
+// - object specifier, d=single object index byte
+#define TK_OBJECT 0x14
+// - builtin function identifier, d=function index
+#define TK_FUNCTION 0x15
+// - user defined function identifier, idddddd i=room for function index, ddd=function name
+#define TK_USERFUNCTION 0x16
+// - built-in context-dependent function
+#define TK_CONTEXTFUNCTION 0x17
+
+// - type definition, d=TItemFieldTypes enum
+#define TK_TYPEDEF 0x18
+
+
+// object indices
+#define OBJ_AUTO 0 // not specified, is local or target field
+#define OBJ_LOCAL 1 // local variable
+#define OBJ_TARGET 2 // target field
+#define OBJ_REFERENCE 3 // reference field
+
+
+
+// - language elements, 0x80..0x9F
+#define TK_IF 0x80
+#define TK_ELSE 0x81
+#define TK_LOOP 0x82
+#define TK_BREAK 0x83
+#define TK_CONTINUE 0x84
+#define TK_RETURN 0x85
+#define TK_WHILE 0x86
+
+// - constants
+#define TK_EMPTY 0x90
+#define TK_UNASSIGNED 0x91
+#define TK_TRUE 0x92
+#define TK_FALSE 0x93
+
+// - grouping, in the 0x20..0x3F area
+#define TK_DOMAINSEP '.'
+#define TK_OPEN_PARANTHESIS '('
+#define TK_CLOSE_PARANTHESIS ')'
+#define TK_LIST_SEPARATOR ','
+#define TK_BEGIN_BLOCK 0x30
+#define TK_END_BLOCK 0x31
+#define TK_END_STATEMENT ';'
+#define TK_OPEN_ARRAY 0x32
+#define TK_CLOSE_ARRAY 0x33
+
+// - operators, in the 0x40..0x7F range
+// in order of precedence (upper 6 bits = precedence)
+#define TK_OP_PRECEDENCE_MASK 0xFC
+#define TK_BINOP_MIN 0x44
+#define TK_BINOP_MAX 0x68 // excluding assignment %%%
+
+// Pure unaries
+// - negation
+#define TK_BITWISENOT 0x40
+#define TK_LOGICALNOT 0x41
+
+// Binaries (some of them also used as unaries, like MINUS)
+// - multiply, divide
+#define TK_MULTIPLY 0x44
+#define TK_DIVIDE 0x45
+#define TK_MODULUS 0x46
+// - add, subtract
+#define TK_PLUS 0x48
+#define TK_MINUS 0x49
+// - shift
+#define TK_SHIFTLEFT 0x4C
+#define TK_SHIFTRIGHT 0x4D
+// - comparison
+#define TK_LESSTHAN 0x50
+#define TK_GREATERTHAN 0x51
+#define TK_LESSEQUAL 0x52
+#define TK_GREATEREQUAL 0x53
+// - equality
+#define TK_EQUAL 0x54
+#define TK_NOTEQUAL 0x55
+// - bitwise AND
+#define TK_BITWISEAND 0x58
+// - bitwise XOR
+#define TK_BITWISEXOR 0x5C
+// - bitwise OR
+#define TK_BITWISEOR 0x60
+// - logical AND
+#define TK_LOGICALAND 0x64
+// - logical OR
+#define TK_LOGICALOR 0x68
+
+// Special modifying operator
+// - assignment
+#define TK_ASSIGN 0x6C
+
+
+// local script variable definition
+class TScriptVarDef
+{
+public:
+ TScriptVarDef(cAppCharP aName,uInt16 aIdx, TItemFieldTypes aType, bool aIsArray, bool aIsRef, bool aIsOpt);
+ ~TScriptVarDef();
+ uInt16 fIdx; // index (position in container).
+ string fVarName; // Variable name
+ TItemFieldTypes fVarType; // type of variable
+ bool fIsArray;
+ bool fIsRef; // set if this is a local reference to an existing variable
+ bool fIsOpt; // set if this is a optional parameter
+}; // TScriptVarDef
+
+
+// script variables
+typedef std::vector<TScriptVarDef *> TVarDefs;
+
+
+// user defined script function
+class TUserScriptFunction
+{
+public:
+ string fFuncName;
+ string fFuncDef;
+}; // TUserScriptFunction
+
+typedef std::vector<TUserScriptFunction *> TUserScriptList;
+
+// global script config (such as user-defined functions)
+class TScriptConfig : public TConfigElement
+{
+ typedef TConfigElement inherited;
+public:
+ TScriptConfig(TConfigElement *aParentElementP);
+ virtual ~TScriptConfig();
+ // properties
+ uInt32 fMaxLoopProcessingTime; // seconds
+ // special scripts
+ // user-defined functions
+ TUserScriptList fFunctionScripts;
+ // macros (pure texts used while parsing)
+ TStringToStringMap fScriptMacros;
+ // accessing user defined functions
+ string *getFunctionScript(sInt16 aFuncIndex);
+ sInt16 getFunctionIndex(cAppCharP aName, size_t aLen);
+ virtual void clear();
+ void clearmacros() { fScriptMacros.clear(); }; // called when config is read, as then templates are no longer needed
+protected:
+ // check config elements
+ virtual bool localStartElement(const char *aElementName, const char **aAttributes, sInt32 aLine);
+ virtual void localResolve(bool aLastPass);
+}; // TScriptConfig
+
+
+// script tokenizing error
+class TTokenizeException : public TConfigParseException
+{
+ typedef TConfigParseException inherited;
+public:
+ TTokenizeException(cAppCharP aScriptName, cAppCharP aMsg1,cAppCharP aScript, uInt16 aIndex, uInt16 aLine);
+}; // TTokenizeException
+
+
+// script resolving or execution error
+class TScriptErrorException : public TConfigParseException
+{
+ typedef TConfigParseException inherited;
+public:
+ TScriptErrorException(cAppCharP aMsg1, uInt16 aLine, cAppCharP aIdent=NULL);
+}; // TScriptErrorException
+
+
+
+// script execution state stack
+typedef enum {
+ ssta_statement,
+ ssta_block,
+ ssta_if,
+ ssta_else,
+ ssta_chainif,
+ ssta_loop,
+ ssta_while,
+} TScriptState;
+
+typedef struct {
+ TScriptState state; // state
+ cUInt8P begin; // begin of that state in source
+ uInt16 line; // line number
+} TScriptStackEntry;
+
+// flow control stack depth
+const sInt16 maxstackentries=40;
+
+class TMultiFieldItem;
+
+// script context
+class TScriptContext
+{
+ friend class TScriptVarKey;
+public:
+ TScriptContext(TSyncAppBase *aAppBaseP, TSyncSession *aSessionP);
+ virtual ~TScriptContext();
+ // Tokenizing is available without context
+ static void Tokenize(TSyncAppBase *aAppBaseP, cAppCharP aScriptName, sInt32 aLine, cAppCharP aScriptText, string &aTScript, const TFuncTable *aContextFuncs, bool aFuncHeader=false, bool aNoDeclarations=false);
+ static void TokenizeAndResolveFunction(TSyncAppBase *aAppBaseP, sInt32 aLine, cAppCharP aScriptText, TUserScriptFunction &aFuncDef);
+ // resolve identifiers in a script, if there is a context passed at all
+ static void resolveScript(TSyncAppBase *aAppBaseP, string &aTScript,TScriptContext *&aCtxP, TFieldListConfig *aFieldListConfigP);
+ // rebuild a script context for a script, if the script is not empty
+ // - Script must already be resolved with ResolveIdentifiers
+ // - If context already exists, adds new locals to existing ones
+ // - If aBuildVars is set, buildVars() will be called after rebuilding variable definitions
+ static void rebuildContext(TSyncAppBase *aAppBaseP, string &aTScript,TScriptContext *&aCtxP, TSyncSession *aSessionP=NULL, bool aBuildVars=false);
+ // - link a script into a contect with already instantiated variables. This is e.g. for activating remoterule scripts
+ static void linkIntoContext(string &aTScript,TScriptContext *aCtxP, TSyncSession *aSessionP);
+ // Build the local variables according to definitions (clears existing vars first)
+ static void buildVars(TScriptContext *&aCtxP);
+ // execute a script if there is a context for it
+ bool static execute(
+ TScriptContext *aCtxP,
+ const string &aTScript,
+ const TFuncTable *aFuncTableP, // context's function table, NULL if none
+ void *aCallerContext, // free pointer eventually having a meaning for context functions
+ TMultiFieldItem *aTargetItemP=NULL, // target (or "loosing") item
+ bool aTargetWritable=true, // if set, target item may be modified
+ TMultiFieldItem *aReferenceItemP=NULL, // reference for source (or "old" or "winning") item
+ bool aRefWritable=false, // if set, reference item may also be written
+ bool aNoDebug=false // if set, debug output is suppressed even if DBG_SCRIPTS is generally on
+ );
+ // execute a script returning a boolean if there is a context for it
+ bool static executeTest(
+ bool aDefaultAnswer, // result if no script is there or script returns no value
+ TScriptContext *aCtxP,
+ const string &aTScript,
+ const TFuncTable *aFuncTableP, // context's function table, NULL if none
+ void *aCallerContext, // free pointer eventually having a meaning for context functions
+ TMultiFieldItem *aTargetItemP=NULL, // target (or "loosing") item
+ bool aTargetWritable=true, // if set, target item may be modified
+ TMultiFieldItem *aReferenceItemP=NULL, // reference for source (or "old" or "winning") item
+ bool aRefWritable=false // if set, reference item may also be written
+ );
+ // execute a script returning a result if there is a context for it
+ bool static executeWithResult(
+ TItemField *&aResultField, // can be default result or NULL, will contain result or NULL if no result
+ TScriptContext *aCtxP,
+ const string &aTScript,
+ const TFuncTable *aFuncTableP, // context's function table, NULL if none
+ void *aCallerContext, // free pointer eventually having a meaning for context functions
+ TMultiFieldItem *aTargetItemP, // target (or "loosing") item
+ bool aTargetWritable, // if set, target item may be modified
+ TMultiFieldItem *aReferenceItemP, // reference for source (or "old" or "winning") item
+ bool aRefWritable, // if set, reference item may also be written
+ bool aNoDebug=false // if set, debug output is suppressed even if DBG_SCRIPTS is generally on
+ );
+ // get info
+ uInt16 getScriptLine(void) { return line; };
+ // get context pointers
+ TSyncSession *getSession(void) { return fSessionP; };
+ TSyncAppBase *getSyncAppBase(void) { return fAppBaseP; };
+ GZones *getSessionZones(void);
+ #ifdef SYDEBUG
+ TDebugLogger *getDbgLogger(void);
+ uInt32 getDbgMask(void);
+ #endif
+ // Reset context (clear all variables and definitions)
+ void clear(void);
+ // clear all fields (local variables), but not their definitions
+ void clearFields(void);
+ // Resolve local variable declarations, references and field references
+ void ResolveIdentifiers(
+ string &aTScript,
+ TFieldListConfig *aFieldListConfigP,
+ bool aRebuild=false,
+ string *aFuncNameP=NULL,
+ bool aNoNewLocals=false // if set, declaration of new locals will be suppressed
+ );
+ // Prepare local variables (create local fields), according to definitions (re-)built with ResolveIdentifiers()
+ bool PrepareLocals(void);
+ // Execute script
+ bool ExecuteScript(
+ const string &aTScript,
+ TItemField **aResultPP, // if not NULL, a result field will be returned here (must be deleted by caller)
+ bool aAsFunction, // if set, this is a function call
+ const TFuncTable *aFuncTableP, // context's function table, NULL if none
+ void *aCallerContext, // free pointer eventually having a meaning for context functions
+ TMultiFieldItem *aTargetItemP, // target (or "loosing") item
+ bool aTargetWritable, // if set, target item may be modified
+ TMultiFieldItem *aReferenceItemP, // reference for source (or "old" or "winning") item
+ bool aRefWritable, // if set, reference item may also be written
+ bool aNoDebug=false // if set, debug output is suppressed even if DBG_SCRIPTS is generally on
+ );
+ // context variable access
+ // - get variable definition, NULL if none defined yet
+ TScriptVarDef *getVarDef(cAppCharP aVarName,size_t len=0);
+ TScriptVarDef *getVarDef(sInt16 aLocalVarIdx);
+ // - get identifier index (>=0: field index, <0: local var)
+ // returns VARIDX_UNDEFINED for unknown identifier
+ sInt16 getIdentifierIndex(sInt16 aObjIndex, TFieldListConfig *aFieldListConfigP, cAppCharP aIdentifier,size_t aLen=0);
+ // - get field by fid, can also be field of aItem, also resolves arrays
+ TItemField *getFieldOrVar(TMultiFieldItem *aItemP, sInt16 aFid, sInt16 aArrIdx);
+ // - get field by fid, can also be field of aItem
+ TItemField *getFieldOrVar(TMultiFieldItem *aItemP, sInt16 aFid);
+ // - get local var by local index
+ TItemField *getLocalVar(sInt16 aVarIdx);
+ // - set local var by local index (used for passing references)
+ void setLocalVar(sInt16 aVarIdx, TItemField *aFieldP);
+ sInt16 getNumLocals(void) { return fNumVars; };
+ // - parameters
+ sInt16 getNumParams(void) { return fNumParams; };
+ // - caller's context data pointer (opaque, for use by context functions)
+ void *getCallerContext(void) { return fCallerContext; };
+private:
+ // syncappbase link, must always exist
+ TSyncAppBase *fAppBaseP;
+ // session link, can be NULL
+ TSyncSession *fSessionP;
+ // local variable definitions (used in resolve phase)
+ TVarDefs fVarDefs;
+ // actually instantiated local variable fields (size of fFieldsP array, used at execution)
+ // Note: might differ from fVarDefs when new vars have been defined, but not instantiated yet)
+ uInt16 fNumVars;
+ // function properties (if context is a function context)
+ uInt16 fNumParams; // this determines the number of parameter locals
+ TItemFieldTypes fFuncType; // return type of function
+ // The local variables and field references
+ TItemField **fFieldsP;
+ // script parsing
+ // - variables
+ sInt16 skipping; // skipping
+ uInt16 line,nextline; // line numbers
+ cUInt8P bp; // start of script
+ cUInt8P ep; // end of script
+ cUInt8P p; // cursor, start at beginning
+ cUInt8P np; // next token
+ #ifdef SYDEBUG
+ const char *scriptname; // name of script
+ const char *linesource; // start of embedded source for current line
+ bool executing; // set if executing (not resolving)
+ bool debugon; // set if debug enabled
+ bool inComment; // for colorizer
+ #endif
+ // - helpers
+ void initParse(const string &aTScript, bool aExecuting=false); // init parsing variables
+ uInt8 gettoken(void); // get next token
+ void reusetoken(void); // re-use last token fetched with gettoken()
+ // script execution
+ // - flow control state stack
+ sInt16 fStackEntries;
+ TScriptStackEntry fScriptstack[maxstackentries];
+ // - context information
+public:
+ // Items
+ TMultiFieldItem *fTargetItemP;
+ bool fTargetWritable;
+ TMultiFieldItem *fReferenceItemP;
+ bool fRefWritable;
+ // Link to calling script context (for function contexts)
+ TScriptContext *fParentContextP;
+private:
+ // Function table
+ const TFuncTable *fFuncTableP; // caller context's function table
+ void *fCallerContext; // free pointer eventually having a meaning for context functions
+ // - helpers
+ void pushState(TScriptState aNewState, cUInt8P aBegin=NULL, uInt16 aLine=0);
+ void popState(TScriptState aCurrentStateExpected);
+ bool getVarField(TItemField *&aItemFieldP);
+ void evalParams(TScriptContext *aFuncContextP);
+ TItemField *evalTerm(TItemFieldTypes aResultType);
+ void evalExpression(
+ TItemField *&aResultFieldP, // result (created new if passed NULL, modified and casted if passed a field)
+ bool aShowResult=true, // if set, this is the main expression (and we want to see the result in DBG)
+ TItemField *aLeftTermP=NULL, // if not NULL, chain-evaluate rest of expression according to aBinaryOp and aPreviousOp. WILL BE CONSUMED
+ uInt8 *aBinaryOpP=NULL, // operator to be applied between term passed in aLeftTermP and next term, will receive next operator that has same or lower precedence than aPreviousOp
+ uInt8 aPreviousOp=0 // if an operator of same or lower precedence than this is found, expression evaluation ends
+ );
+ void defineBuiltInVars(const TBuiltInFuncDef *aFuncDefP);
+ void executeBuiltIn(TItemField *&aTermP, const TBuiltInFuncDef *aFuncDefP);
+ #ifdef SYDEBUG
+ void showVarDefs(cAppCharP aTxt);
+ #endif
+}; // TScriptContext
+
+
+#ifdef ENGINEINTERFACE_SUPPORT
+
+
+// key for access to script context variables
+class TScriptVarKey :
+ public TItemFieldKey
+{
+ typedef TItemFieldKey inherited;
+public:
+ TScriptVarKey(TEngineInterface *aEngineInterfaceP, TScriptContext *aScriptContext) :
+ inherited(aEngineInterfaceP),
+ fScriptContext(aScriptContext), // may be NULL, no vars will be accessible then but no crash occurs
+ fIterator(0)
+ {};
+
+protected:
+
+ // methods to actually access a TItemField
+ virtual sInt16 getFidFor(cAppCharP aName, stringSize aNameSz);
+ virtual TItemField *getBaseFieldFromFid(sInt16 aFid);
+ virtual bool getFieldNameFromFid(sInt16 aFid, string &aFieldName);
+
+ // the script context
+ TScriptContext *fScriptContext;
+ // value iterator
+ sInt16 fIterator;
+
+}; // TScriptVarKey
+
+
+#endif // ENGINEINTERFACE_SUPPORT
+
+
+} // namespace sysync
+
+#endif // SCRIPT_CONTEXT_H
+
+#endif // SCRIPT_SUPPORT
+
+/* eof */
diff --git a/src/sysync/simpleitem.cpp b/src/sysync/simpleitem.cpp
new file mode 100755
index 0000000..a087dab
--- /dev/null
+++ b/src/sysync/simpleitem.cpp
@@ -0,0 +1,102 @@
+/*
+ * File: SimpleItem.cpp
+ *
+ * Author: Lukas Zeller (luz@synthesis.ch)
+ *
+ * TSimpleItem
+ * Simple item, no internal structure but just a string
+ *
+ * Copyright (c) 2001-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ * 2001-06-18 : luz : created
+ *
+ */
+
+// includes
+#include "prefix_file.h"
+#include "sysync.h"
+#include "simpleitem.h"
+
+#ifdef CHECKSUM_CHANGELOG
+#include "sysync_crc16.h"
+#endif
+
+
+using namespace sysync;
+
+
+/*
+ * Implementation of TSimpleItem
+ */
+
+
+TSimpleItem::TSimpleItem(TSyncItemType *aItemTypeP) :
+ TSyncItem(aItemTypeP)
+{
+ // save link to type
+ fItemTypeP = aItemTypeP;
+} // TSimpleItem::TSimpleItem
+
+
+TSimpleItem::~TSimpleItem()
+{
+} // TSimpleItem::~TSimpleItem
+
+
+#ifdef CHECKSUM_CHANGELOG
+
+// changelog support: calculate CRC over contents
+uInt16 TSimpleItem::getDataCRC(uInt16 crc, bool aEQRelevantOnly)
+{
+ // CRC of contents string (no change if nothing in it)
+ return sysync_crc16_block(fContents.c_str(),fContents.size(),crc);
+} // TSimpleItem::getDataCRC
+
+#endif
+
+
+// test if comparable (at least for equality)
+bool TSimpleItem::comparable(TSyncItem &aItem)
+{
+ // test if comparable: only SimpleItems can be compared
+ return aItem.getTypeID() == getTypeID();
+} // TSimpleItem::comparable
+
+
+// replace data contents from specified item
+// - aAvailable only: only replace contents actually available in aItem, leave rest untouched
+// - aDetectCutOffs: handle case where aItem could have somhow cut-off data and prevent replacing
+// complete data with cut-off version (e.g. mobiles like T39m with limited name string capacity)
+bool TSimpleItem::replaceDataFrom(TSyncItem &aItem, bool aAvailableOnly, bool aDetectCutoffs, bool aAssignedOnly, bool aTransferUnassigned)
+{
+ // check type
+ if (!aItem.isBasedOn(ity_simple)) return false;
+ // ok, same type, copy data
+ fContents=static_cast<TSimpleItem *>(&aItem)->fContents;
+ return true;
+} // TSimpleItem::replaceDataFrom
+
+
+
+// compare function, returns 0 if equal, 1 if this > aItem, -1 if this < aItem
+sInt16 TSimpleItem::compareWith(
+ TSyncItem &aItem,
+ TEqualityMode aEqMode,
+ TLocalEngineDS *aDatastoreP
+ #ifdef SYDEBUG
+ ,bool aDebugShow
+ #endif
+)
+{
+ // we should test for comparable() before!
+ if (!comparable(aItem)) return SYSYNC_NOT_COMPARABLE;
+ TSimpleItem *simpleitemP = ((TSimpleItem *)&aItem);
+ // compare (equality only, mod date/version is unknown)
+ if (fContents == simpleitemP->fContents) return 0;
+ else return SYSYNC_NOT_COMPARABLE;
+} // TSimpleItem::compareWith
+
+
+/* end of TSimpleItem implementation */
+
+// eof
diff --git a/src/sysync/simpleitem.h b/src/sysync/simpleitem.h
new file mode 100755
index 0000000..d8fa865
--- /dev/null
+++ b/src/sysync/simpleitem.h
@@ -0,0 +1,75 @@
+/*
+ * File: SimpleItem.h
+ *
+ * Author: Lukas Zeller (luz@synthesis.ch)
+ *
+ * TSimpleItem
+ * Simple item, no internal structure but just a string
+ *
+ * Copyright (c) 2001-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ * 2001-06-18 : luz : created
+ *
+ */
+
+#ifndef SimpleItem_H
+#define SimpleItem_H
+
+// includes
+#include "syncitem.h"
+#include "sysync.h"
+
+
+namespace sysync {
+
+const uInt16 ity_simple = 1; // must be unique
+
+class TSimpleItem: public TSyncItem
+{
+ typedef TSyncItem inherited;
+public:
+ TSimpleItem(TSyncItemType *aItemTypeP);
+ virtual ~TSimpleItem();
+ // access to type
+ virtual uInt16 getTypeID(void) const { return ity_simple; };
+ virtual bool isBasedOn(uInt16 aItemTypeID) const { return aItemTypeID==ity_simple ? true : TSyncItem::isBasedOn(aItemTypeID); };
+ // assignment (IDs and contents)
+ virtual TSyncItem& operator=(TSyncItem &aSyncItem) { return TSyncItem::operator=(aSyncItem); };
+ // access to simple item contents
+ const char *getContents(void) { return fContents.c_str(); };
+ void setContents(const char *aContents) { fContents = aContents; };
+ // compare abilities
+ virtual bool comparable(TSyncItem &aItem);
+ virtual bool sortable(TSyncItem &aItem) { return false; }
+ // clear item data
+ virtual void cleardata(void) { fContents.erase(); };
+ // - changelog support
+ #ifdef CHECKSUM_CHANGELOG
+ virtual uInt16 getDataCRC(uInt16 crc=0, bool aEQRelevantOnly=false);
+ #endif
+ // replace data contents from specified item
+ // - aAvailable only: only replace contents actually available in aItem, leave rest untouched
+ // - aDetectCutOffs: handle case where aItem could have somhow cut-off data and prevent replacing
+ // complete data with cut-off version (e.g. mobiles like T39m with limited name string capacity)
+ virtual bool replaceDataFrom(TSyncItem &aItem, bool aAvailableOnly=false, bool aDetectCutoffs=false, bool aAssignedOnly=false, bool aTransferUnassigned=false);
+protected:
+ // compare function, returns 0 if equal, 1 if this > aItem, -1 if this < aItem
+ virtual sInt16 compareWith(
+ TSyncItem &aItem,
+ TEqualityMode aEqMode,
+ TLocalEngineDS *aDatastoreP
+ #ifdef SYDEBUG
+ ,bool aDebugShow=false
+ #endif
+ );
+ // associated type item
+ TSyncItemType *fItemTypeP;
+ // contents: simple string
+ string fContents;
+}; // TSimpleItem
+
+} // namespace sysync
+
+#endif // SimpleItem_H
+
+// eof
diff --git a/src/sysync/smltk_precomp.h b/src/sysync/smltk_precomp.h
new file mode 100755
index 0000000..3f745a4
--- /dev/null
+++ b/src/sysync/smltk_precomp.h
@@ -0,0 +1,40 @@
+/* C Headers that might be available in precompiled form
+ * for SML toolkit compilation.
+ * (standard libraries, SyncML toolkit itself...)
+ */
+
+#ifndef SMLTK_PRECOMP_H
+#define SMLTK_PRECOMP_H
+
+
+/* prerequisites for SML toolkit */
+// - compiler/platform specific defines
+#include "define.h"
+// - special, platform dependent lock library
+// (may include extra platform support headers BEFORE
+// other SML files will include standard platform support)
+#include "liblock.h"
+
+
+/* standard C includes */
+#include <ctype.h>
+#include <string.h>
+#ifdef __PALM_OS__
+ #include <unix_stdarg.h>
+ #include <stdlib.h>
+ #include <time.h>
+#else
+ #include <stdio.h>
+ #include <stdarg.h>
+#endif
+#if !defined(__MC68K__) && !defined(LINUX) && !defined(WINCE) && !defined(MACOSX) && !defined(__EPOC_OS__)
+ #include <extras.h>
+#endif
+
+
+/* SyncML Toolkit includes */
+// - SyncML Toolkit external API
+#include "sml.h"
+#include "smlerr.h"
+
+#endif /* defined SMLTK_PRECOMP_H */
diff --git a/src/sysync/smltk_precomp_xpt.h b/src/sysync/smltk_precomp_xpt.h
new file mode 100755
index 0000000..595108f
--- /dev/null
+++ b/src/sysync/smltk_precomp_xpt.h
@@ -0,0 +1,17 @@
+/* C Headers that might be available in precompiled form
+ * for SML toolkit compilation.
+ * (standard libraries, SyncML toolkit itself...)
+ */
+
+#ifndef SMLTK_PRECOMP_XPT_H
+#define SMLTK_PRECOMP_XPT_H
+
+
+/* SyncML Toolkit includes */
+// standard stuff
+#include "smltk_precomp.h"
+// plus XPT stuff
+#include "xpt.h"
+
+
+#endif /* defined SMLTK_PRECOMP_XPT_H */
diff --git a/src/sysync/stdlogicagent.cpp b/src/sysync/stdlogicagent.cpp
new file mode 100755
index 0000000..0aa19fc
--- /dev/null
+++ b/src/sysync/stdlogicagent.cpp
@@ -0,0 +1,84 @@
+/**
+ * @File stdlogicagent.cpp
+ *
+ * @Author Lukas Zeller (luz@synthesis.ch)
+ *
+ * @brief TStdLogicAgent
+ * Agent (=server or client session) for standard database logic implementations, see @ref TStdLogicDS
+ *
+ * Copyright (c) 2001-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ * @Date 2005-09-23 : luz : created from custdbagent
+ */
+/*
+ */
+
+// includes
+#include "prefix_file.h"
+#include "stdlogicagent.h"
+
+
+/*
+ * Implementation of TStdLogicAgent
+ */
+
+/* public TStdLogicAgent members */
+
+#ifdef SYSYNC_CLIENT
+
+TStdLogicAgent::TStdLogicAgent(TSyncClientBase *aClientBaseP, const char *aSessionID) :
+ TSyncClient(aClientBaseP,aSessionID)
+{
+ // nop
+} // TStdLogicAgent::TStdLogicAgent
+
+#else
+
+TStdLogicAgent::TStdLogicAgent(TSyncAppBase *aAppBaseP, TSyncSessionHandle *aSessionHandleP, const char *aSessionID) :
+ TSyncServer(aAppBaseP, aSessionHandleP, aSessionID)
+{
+ // nop
+ InternalResetSession();
+} // TStdLogicAgent::TStdLogicAgent
+
+#endif
+
+
+// destructor
+TStdLogicAgent::~TStdLogicAgent()
+{
+ // make sure everything is terminated BEFORE destruction of hierarchy begins
+ TerminateSession();
+} // TStdLogicAgent::~TStdLogicAgent
+
+
+// Terminate session
+void TStdLogicAgent::TerminateSession()
+{
+ if (!fTerminated) {
+ InternalResetSession();
+ }
+ inherited::TerminateSession();
+} // TStdLogicAgent::TerminateSession
+
+
+
+// Reset session
+void TStdLogicAgent::InternalResetSession(void)
+{
+} // TStdLogicAgent::InternalResetSession
+
+
+// Virtual version
+void TStdLogicAgent::ResetSession(void)
+{
+ // do my own stuff
+ InternalResetSession();
+ // let ancestor do its stuff
+ inherited::ResetSession();
+} // TStdLogicAgent::ResetSession
+
+
+/* end of TStdLogicAgent implementation */
+
+// eof
diff --git a/src/sysync/stdlogicagent.h b/src/sysync/stdlogicagent.h
new file mode 100755
index 0000000..80a21c3
--- /dev/null
+++ b/src/sysync/stdlogicagent.h
@@ -0,0 +1,70 @@
+/**
+ * @File stdlogicagent.h
+ *
+ * @Author Lukas Zeller (luz@synthesis.ch)
+ *
+ * @brief TStdLogicAgent
+ * Agent (=server or client session) for standard database logic implementations, see @ref TStdLogicDS
+ *
+ * Copyright (c) 2001-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ * @Date 2005-09-23 : luz : created from custdbagent
+ */
+/*
+ */
+
+#ifndef STDLOGICAGENT_H
+#define STDLOGICAGENT_H
+
+// includes
+#include "sysync.h"
+#ifdef SYSYNC_CLIENT
+#include "syncclient.h"
+#else
+#include "syncserver.h"
+#endif
+#include "localengineds.h"
+
+
+using namespace sysync;
+
+namespace sysync {
+
+
+class TStdLogicAgent:
+ #ifdef SYSYNC_CLIENT
+ public TSyncClient
+ #else
+ public TSyncServer
+ #endif
+{
+ #ifdef SYSYNC_CLIENT
+ typedef TSyncClient inherited;
+ #else
+ typedef TSyncServer inherited;
+ #endif
+public:
+ #ifdef SYSYNC_CLIENT
+ TStdLogicAgent(TSyncClientBase *aClientBaseP, const char *aSessionID);
+ #else
+ TStdLogicAgent(TSyncAppBase *aAppBaseP, TSyncSessionHandle *aSessionHandleP, const char *aSessionID);
+ #endif
+ virtual ~TStdLogicAgent();
+ virtual void TerminateSession(void); // Terminate session, like destructor, but without actually destructing object itself
+ virtual void ResetSession(void); // Resets session (but unlike TerminateSession, session might be re-used)
+ void InternalResetSession(void); // static implementation for calling through virtual destructor and virtual ResetSession();
+ // user authentication
+ #ifndef SYSYNC_CLIENT
+ // - server should implement it, so we make it abstract here again (altough there is
+ // an implementation for simpleauth in session.
+ virtual bool SessionLogin(const char *aUserName, const char *aAuthString, TAuthSecretTypes aAuthStringType, const char *aDeviceID) = 0;
+ #endif
+}; // TStdLogicAgent
+
+
+} // namespace sysync
+
+
+#endif // STDLOGICAGENT_H
+
+// eof
diff --git a/src/sysync/stdlogicds.cpp b/src/sysync/stdlogicds.cpp
new file mode 100755
index 0000000..8d43914
--- /dev/null
+++ b/src/sysync/stdlogicds.cpp
@@ -0,0 +1,1452 @@
+/**
+ * @File stdlogicds.cpp
+ *
+ * @Author Lukas Zeller (luz@synthesis.ch)
+ *
+ * @brief TStdLogicDS
+ * Standard database logic implementation, suitable for most (currently all)
+ * actual DS implementations, but takes as few assumptions about datastore
+ * so for vastly different sync patterns, this could be replaced by differnt locic
+ *
+ * Copyright (c) 2001-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ * @Date 2005-09-15 : luz : created from custdbdatastore
+ */
+/*
+ */
+
+// includes
+#include "prefix_file.h"
+
+#include "sysync.h"
+#include "stdlogicds.h"
+#include "multifielditem.h"
+#include "multifielditemtype.h"
+
+using namespace sysync;
+
+namespace sysync {
+
+/*
+ * Implementation of TStdLogicDS
+ */
+
+/* public TStdLogicDS members */
+
+
+TStdLogicDS::TStdLogicDS(
+ TLocalDSConfig *aDSConfigP,
+ sysync::TSyncSession *aSessionP,
+ const char *aName,
+ long aCommonSyncCapMask
+) :
+ TLocalEngineDS(aDSConfigP, aSessionP, aName, aCommonSyncCapMask)
+ #ifdef MULTI_THREAD_DATASTORE
+ ,fStartSyncStatus(aSessionP) // a thread-private status command to store status ocurring during threaded startDataAccessForServer()
+ #endif
+{
+ InternalResetDataStore();
+ fMultiThread= fSessionP->getSessionConfig()->fMultiThread; // get comfort pointer
+} // TStdLogicDS::TStdLogicDS
+
+
+TStdLogicDS::~TStdLogicDS()
+{
+ InternalResetDataStore();
+} // TStdLogicDS::~TStdLogicDS
+
+
+void TStdLogicDS::InternalResetDataStore(void)
+{
+ // reset
+ fFirstTimeSync=false;
+ fWriteStarted=false;
+ fPreviousSyncTime=0;
+ fCurrentSyncTime=0;
+
+ #ifdef MULTI_THREAD_DATASTORE // combined ifdef/flag
+ if (fMultiThread) {
+ // make sure background processing aborts if it is in progress
+ fStartSyncThread.terminate(); // request soft termination of thread
+ if (!fStartSyncThread.waitfor()) {
+ // has not already terminated
+ PDEBUGPRINTFX(DBG_HOT,("******** Waiting for background thread to terminate"));
+ fStartSyncThread.waitfor(-1); // wait forever or until thread really terminates
+ PDEBUGPRINTFX(DBG_HOT,("******** Background thread terminated"));
+ } // if
+ } // if
+ #endif
+ // we are not initializing
+ fInitializing=false;
+ // no start init request yet
+ fStartInit=false;
+ #if !defined(SYSYNC_CLIENT) || defined(CLIENT_USES_SERVER_DB)
+ // remove all items
+ TSyncItemPContainer::iterator pos;
+ for (pos=fItems.begin(); pos!=fItems.end(); ++pos) {
+ delete *pos;
+ }
+ fItems.clear(); // clear list
+ #endif
+ #ifndef SYSYNC_CLIENT
+ fNumRefOnlyItems=0;
+ #endif
+} // TStdLogicDS::InternalResetDataStore
+
+
+// Internal events during sync to access local database
+// ====================================================
+
+
+// called to make admin data ready
+localstatus TStdLogicDS::logicMakeAdminReady(cAppCharP aDataStoreURI, cAppCharP aRemoteDBID)
+{
+ PDEBUGBLOCKFMTCOLL(("MakeAdminReady","Making Admin Data ready to check sync anchors","localDB=%s|remoteDB=%s",aDataStoreURI,aRemoteDBID));
+ // init local anchor strings, because it will not be set on impl level
+ // (impl level only uses timestamps for local anchor)
+ // These will be derived from timestamps below when implMakeAdminReady() is successful
+ fLastLocalAnchor.erase();
+ fNextLocalAnchor.erase();
+ // Updates the following state variables
+ // - from TLocalEngineDS: fLastRemoteAnchor, fLastLocalAnchor, fResumeAlertCode, fFirstTimeSync
+ // - for client: fPendingAddMaps
+ // - for server: fTempGUIDMap
+ // - from TStdLogicDS: fPreviousSyncTime, fCurrentSyncTime
+ // - from derived classes: whatever else belongs to dsSavedAdmin and dsCurrentAdmin state
+ localstatus sta = implMakeAdminReady(
+ fSessionP->getRemoteURI(), // remote device/server URI (device ID)
+ aDataStoreURI, // entire relative URI of local datastore
+ aRemoteDBID // database ID of remote device/server (=path as sent by remote)
+ );
+ if (sta==LOCERR_OK) {
+ // TStdLogicDS requires implementation to store timestamps to make the local anchors
+ // - regenerate last session's local anchor string from timestamp
+ TimestampToISO8601Str(fLastLocalAnchor,fPreviousSyncTime,TCTX_UTC,false,false);
+ // - create this session's local anchor string from timestamp
+ TimestampToISO8601Str(fNextLocalAnchor,fCurrentSyncTime,TCTX_UTC,false,false);
+ // - check if config has changed since last sync
+ if (fSessionP->mustSendDevInf() || fFirstTimeSync || fPreviousSyncTime<=fSessionP->getRootConfig()->fConfigDate) {
+ // remote should see our (probably changed) devInf
+ PDEBUGPRINTFX(DBG_PROTO,("First time sync or config changed since last sync -> remote should see our devinf"));
+ fSessionP->remoteMustSeeDevinf();
+ }
+ // empty saved anchors if first time sync (should be empty anyway, but...)
+ if (fFirstTimeSync) {
+ fLastLocalAnchor.empty();
+ fLastRemoteAnchor.empty();
+ }
+ }
+ PDEBUGENDBLOCK("MakeAdminReady");
+ return sta;
+} // TStdLogicDS::logicMakeAdminReady
+
+
+
+// start writing if not already started
+localstatus TStdLogicDS::startDataWrite()
+{
+ localstatus sta = LOCERR_OK;
+
+ if (!fWriteStarted) {
+ sta=implStartDataWrite();
+ fWriteStarted = sta==LOCERR_OK; // must be here to prevent recursion as startDataWrite might be called implicitly below
+ #if !defined(SYSYNC_CLIENT) || defined(CLIENT_USES_SERVER_DB)
+ // server-type DB needs post-processing to update map entries (client and server case)
+ TSyncItemPContainer::iterator pos;
+ if (sta==LOCERR_OK) {
+ // Now allow post-processing of all reported items
+ for (pos=fItems.begin(); pos!=fItems.end(); ++pos) {
+ localstatus sta2 = implReviewReadItem(**pos);
+ if (sta2!=LOCERR_OK) sta=sta2;
+ }
+ }
+ #endif
+ #if defined(SYSYNC_CLIENT) && defined(CLIENT_USES_SERVER_DB)
+ /// @todo we don't need the items to remain that long at all - not even for CLIENT_USES_SERVER_DB case
+ fItems.clear(); // empty list
+ #endif // client using server DB
+ }
+ DEBUGPRINTFX(DBG_DATA,("startDataWrite called, status=%hd", sta));
+ return sta;
+} // TStdLogicDS::startDataWrite
+
+
+// end writing
+localstatus TStdLogicDS::endDataWrite(void)
+{
+ localstatus sta=LOCERR_OK;
+
+ DEBUGPRINTFX(DBG_DATA,(
+ "endDataWrite called, write %s started",
+ fWriteStarted ? "is" : "not"
+ ));
+ // if we commit a write, the session is ok, and we can clear the resume state
+ if (fWriteStarted) {
+ sta = implEndDataWrite();
+ // ended now
+ fWriteStarted=false;
+ }
+ return sta;
+} // TStdLogicDS::endDataWrite
+
+
+// - read specific item from database
+// Data and missing ID information is filled in from local database
+bool TStdLogicDS::logicRetrieveItemByID(
+ TSyncItem &aSyncItem, // item to be filled with data from server. Local or Remote ID must already be set
+ TStatusCommand &aStatusCommand // status, must be set on error or non-200-status
+)
+{
+ // simply call implementation
+ return implRetrieveItemByID(aSyncItem,aStatusCommand);
+} // TLocalEngineDS::logicRetrieveItemByID
+
+
+/// check is datastore is completely started.
+/// @param[in] aWait if set, call will not return until either started state is reached
+/// or cannot be reached within the maximally allowed request processing time left.
+bool TStdLogicDS::isStarted(bool aWait)
+{
+ #ifndef SYSYNC_CLIENT
+ // only server has threaded datastores so far
+ if (aWait && fInitializing) {
+ localstatus sta = startDataAccessForServer();
+ if (sta!=LOCERR_OK) {
+ SYSYNC_THROW(TSyncException("startDataAccessForServer failed (when called from isStarted)", sta));
+ }
+ }
+ #endif
+ // if initialisation could not be completed in the first startDataAccessForServer() call
+ // we are not started.
+ return !fInitializing && inherited::isStarted(aWait);
+} // TStdLogicDS::isStarted
+
+
+/// called to mark an already generated (but probably not sent or not yet statused) item
+/// as "to-be-resumed", by localID or remoteID (latter only in server case).
+/// @note This must be repeatable without side effects, as server must mark/save suspend state
+/// after every request (and not just at end of session)
+void TStdLogicDS::logicMarkItemForResume(cAppCharP aLocalID, cAppCharP aRemoteID, bool aUnSent)
+{
+ implMarkItemForResume(aLocalID, aRemoteID, aUnSent);
+} // TStdLogicDS::logicMarkItemForResume
+
+
+/// called to mark an already sent item as "to-be-resent", e.g. due to temporary
+/// error status conditions, by localID or remoteID (latter only in server case).
+void TStdLogicDS::logicMarkItemForResend(cAppCharP aLocalID, cAppCharP aRemoteID)
+{
+ implMarkItemForResend(aLocalID, aRemoteID);
+} // TStdLogicDS::logicMarkItemForResend
+
+
+
+#ifndef SYSYNC_CLIENT
+
+// Server case
+// ===========
+
+// Actual start sync actions in DB. If server supports threaded init, this will
+// be called in a sub-thread's context
+localstatus TStdLogicDS::performStartSync(void)
+{
+ localstatus sta = LOCERR_OK;
+ TP_DEFIDX(li);
+ TP_SWITCH(li,fSessionP->fTPInfo,TP_database);
+ sta = implStartDataRead();
+ fNumRefOnlyItems=0;
+ if (sta==LOCERR_OK) {
+ // now get data from DB
+ if (!isRefreshOnly() || (isSlowSync() && isResuming())) {
+ // not only updating from client, so read all items now
+ // Note: for a resumed slow updating from client only, we need the
+ // currently present syncset as well as we need it to detect
+ // re-sent items
+ bool eof;
+ bool changed;
+ PDEBUGBLOCKFMTCOLL(("GetItems","Read items from DB implementation","datastore=%s",getName()));
+ do {
+ // check if external request to terminate loop
+ if (shouldExitStartSync()) {
+ PDEBUGPRINTFX(DBG_ERROR,("performStartSync aborted by external request"));
+ PDEBUGENDBLOCK("GetItems");
+ TP_START(fSessionP->fTPInfo,li);
+ return 510;
+ }
+ // try to read item
+ TSyncItem *myitemP=NULL;
+ // report all items in syncset, not only changes if we need to filter
+ changed=!fFilteringNeededForAll; // let GetItem
+ // now fetch next item
+ sta = implGetItem(eof,changed,myitemP);
+ if (sta!=LOCERR_OK) {
+ implEndDataRead(); // terminate reading
+ PDEBUGENDBLOCK("GetItems");
+ TP_START(fSessionP->fTPInfo,li);
+ return sta;
+ }
+ else {
+ // read successful, test for eof
+ if (eof) break; // reading done
+ if (fSlowSync) changed=true; // all have changed (just in case GetItem does not return clean result here)
+ // NOTE: sop can be sop_reference_only ONLY in case of server resuming a slowsync
+ TSyncOperation sop=myitemP->getSyncOp();
+ // check if we need to do some filtering to determine final syncop
+ // NOTE: call postFetchFiltering even in case we do not actually
+ // need filtering (but we might need making item pass acceptance filter!)
+ if (sop!=sop_delete && sop!=sop_soft_delete && sop!=sop_archive_delete) {
+ // we need to post-fetch filter the item first
+ bool passes=postFetchFiltering(myitemP);
+ if (!passes) {
+ // item does not pass = does not belong to sync set per now
+ if (!fSlowSync && (sop==sop_wants_replace)) {
+ // item already exists on remote but falls out of syncset now: delete
+ // NOTE: This works only if reviewReadItem() is correctly implemented
+ // and checks for items that are deleted after being reported
+ // something else to delete their local map entry
+ sop=sop_delete;
+ myitemP->cleardata(); // also get rid of unneeded data
+ }
+ else sop=sop_none; // ignore all others (especially adds or slowsync replaces)
+ }
+ else {
+ // item passes = belongs to sync set
+ if (sop==sop_wants_replace && !changed && !fSlowSync) {
+ // exists but has not changed since last sync
+ sop=sop_none; // ignore for now
+ }
+ }
+ }
+ // check if we should use that item
+ if (sop==sop_none) {
+ delete myitemP;
+ continue; // try next from DB
+ }
+ // set final sop now
+ myitemP->setSyncOp(sop);
+ // %%% these are just-in-case tests for sloppy db interface
+ // - adjust operation for slowsync
+ if (fSlowSync) {
+ if (sop==sop_delete || sop==sop_soft_delete || sop==sop_archive_delete) {
+ // do not process deleted items during slow sync at all
+ delete myitemP; // forget it
+ continue; // Read next item
+ }
+ else {
+ // must be add or replace, will be an add by default (if unmatched)
+ // - set it to sop_wants_add to signal that this item was not matched yet!
+ myitemP->setRemoteID(""); // forget remote ID, is unknown in slow sync anyway
+ if (sop!=sop_reference_only) // if reference only (resumed slowsync), keep it as is
+ myitemP->setSyncOp(sop_wants_add); // flag it unmatched
+ }
+ }
+ // - now add it to my local list
+ fItems.push_back(myitemP);
+ if (sop==sop_reference_only)
+ fNumRefOnlyItems++; // count these to avoid them being shown in NOC
+ }
+ } while (true); // exit by break
+ PDEBUGENDBLOCK("GetItems");
+ } // not from client only
+ // end reading
+ sta=implEndDataRead();
+ // show items
+ PDEBUGPRINTFX(DBG_HOT,("%s: number of local items involved in %ssync = %ld",getName(), fSlowSync ? "slow " : "",fItems.size()));
+ CONSOLEPRINTF((" %ld local items are new/changed/deleted for this sync",fItems.size()));
+ if (PDEBUGTEST(DBG_DATA+DBG_DETAILS)) {
+ PDEBUGBLOCKFMTCOLL(("SyncSet","Items involved in Sync","datastore=%s",getName()));
+ for (TSyncItemPContainer::iterator pos=fItems.begin();pos!=fItems.end();pos++) {
+ TSyncItem *syncitemP = (*pos);
+ PDEBUGPRINTFX(DBG_DATA,(
+ "SyncOp=%-20s: LocalID=%15s RemoteID=%15s",
+ SyncOpNames[syncitemP->getSyncOp()],
+ syncitemP->getLocalID(),
+ syncitemP->getRemoteID()
+ ));
+ }
+ PDEBUGENDBLOCK("SyncSet");
+ }
+ // initiate writing and cause reviewing of items now,
+ // before any of them can be modified by sync process
+ // Notes:
+ // - startDataWrite() includes zapping the sync set
+ // in slow refresh only sessions (not resumed, see next note)
+ // - In case of resumed slow refresh from remote, the sync set
+ // will not get zapped again, because some items are already there
+ if (sta==LOCERR_OK)
+ sta = startDataWrite(); // private helper
+ // test if ok
+ if (sta != LOCERR_OK) {
+ // failed
+ engAbortDataStoreSync(sta,true);
+ return sta;
+ }
+ TP_START(fSessionP->fTPInfo,li);
+ return sta;
+ } // startDataRead successful
+ else {
+ TP_START(fSessionP->fTPInfo,li);
+ return sta;
+ }
+} // TStdLogicDS::performStartSync
+
+
+#ifdef MULTI_THREAD_DATASTORE
+
+// function executed by thread
+static uInt32 StartSyncThreadFunc(TThreadObject *aThreadObject, uInt32 aParam)
+{
+ // parameter passed is pointer to datastore
+ TStdLogicDS *datastoreP = static_cast<TStdLogicDS *>((void *)aParam);
+ // now call routine that actually performs datastore start
+ uInt32 exitCode = (uInt32) (datastoreP->performStartSync());
+ // thread is about to end (has ended for the Impl and Api levels), inform datastore
+ // and post necessary ThreadMayChangeNow() calls
+ datastoreP->endingThread();
+ // return
+ return exitCode;
+} // StartSyncThreadFunc
+
+
+bool TStdLogicDS::threadedStartSync(void)
+{
+ // do this in a separate thread if requested
+ // Note: ThreadMayChangeNow() has been posted already by startingThread()
+ PDEBUGPRINTFX(DBG_HOT,("******* starting background thread for reading sync set..."));
+ fStartSyncStatus.setStatusCode(200); // assume ok
+ if (!fStartSyncThread.launch(StartSyncThreadFunc,(uInt32)this)) { // pass datastoreP as param
+ // starting thread failed
+ PDEBUGPRINTFX(DBG_ERROR,("******* Failed starting background thread for reading sync set"));
+ return false;
+ }
+ #ifdef SYDEBUG
+ // show link to thread log
+ PDEBUGPRINTFX(DBG_HOT,(
+ "******* started &html;<a href=\"%s_%lu%s\" target=\"_blank\">&html;background thread id=%lu&html;</a>&html; for reading sync set",
+ getDbgLogger()->getDebugFilename(), // href base
+ fStartSyncThread.getid(), // plus thread
+ getDbgLogger()->getDebugExt(), // plus extension
+ fStartSyncThread.getid()
+ ));
+ #endif
+ return true; // started ok
+} // TStdLogicDS::threadedStartSync
+
+
+// can be called to check if performStartSync() should be terminated
+bool TStdLogicDS::shouldExitStartSync(void)
+{
+ // threaded version, check if termination flag was set
+ return fMultiThread && fStartSyncThread.terminationRequested();
+} // TStdLogicDS::shouldExitStartSync
+
+#else
+
+// can be called to check if performStartSync() should be terminated
+bool TStdLogicDS::shouldExitStartSync(void)
+{
+ // nonthread version, is never the case
+ return false;
+} // TStdLogicDS::shouldExitStartSync
+
+#endif
+
+
+// called by dsBeforeStateChange to dssta_dataaccessstarted to make sure datastore is
+// getting ready for being accessed. Also called by isStarted(true) when starting
+// up took longer than one request max time for threaded datastores
+localstatus TStdLogicDS::startDataAccessForServer(void)
+{
+ localstatus sta = LOCERR_OK;
+
+ DEBUGPRINTFX(DBG_HOT,("TStdLogicDS::startDataAccessForServer"));
+ if (!fInitializing) {
+ // The datastore has not started initializing yet
+ // - start initialisation now
+ fWriteStarted=false;
+ // - read all records from DB right now if server data is used at all
+ DEBUGPRINTFX(DBG_DATA,("- number of items in list before StartDataRead = %ld",fItems.size()));
+ // now we can initialize the conflict resolution mode for this session
+ /// @todo move this to localengineds, at point where we get dssta_syncmodestable
+ fSessionConflictStrategy=getConflictStrategy(fSlowSync,fFirstTimeSync);
+ // prepare for read
+ #ifdef SCRIPT_SUPPORT
+ // - call DB init script, which might add extra filters depending on options and remoterule
+ TScriptContext::execute(
+ fDataStoreScriptContextP,
+ getDSConfig()->fDBInitScript,
+ &DBFuncTable, // context's function table
+ this // datastore pointer needed for context
+ );
+ #endif
+ // - init post fetch filtering, sets fFilteringNeededForAll and fFilteringNeeded correctly
+ initPostFetchFiltering();
+ // - now we can start reading (fFilteringNeededForAll can be checked by StartDataRead)
+ fInitializing=true; // we enter the initialisation phase now
+ fStartInit=true; // and we want to start the init
+ }
+ // try starting init now (eventually repeats until it can be done)
+ if (fStartInit) {
+ PDEBUGPRINTFX(DBG_DATA,( "MultiThread %sabled", fMultiThread ? "en":"dis" ));
+ #ifdef MULTI_THREAD_DATASTORE // combined define and flag
+ if (fMultiThread) {
+ // start init thread
+ if (startingThread()) {
+ // we may start a thread here
+ if (threadedStartSync())
+ fStartInit= false; // starting done now
+ } // if
+ } // if
+ #endif
+ if (!fMultiThread) {
+ // Just perform initialisation
+ sta = performStartSync();
+ fStartInit=false; // starting done now
+ fInitializing=false; // initialisation is already complete here
+ } // if
+ }
+
+ #ifdef MULTI_THREAD_DATASTORE // combined define and flag
+ if (fMultiThread) {
+ // wait for started initialisation to finish within time we have left for this request
+ if (!fStartInit && fInitializing) {
+ // initialisation started but not ended so far: wait for it until done or defined request time passed
+ sInt32 t=fSessionP->RemainingRequestTime();
+ if (fStartSyncThread.waitfor(t<0 ? 0 : t * 1000)) {
+ // background thread has terminated
+ sta = fStartSyncThread.exitcode();
+ PDEBUGPRINTFX(DBG_HOT,("******* background thread for startSync() terminated with exit code=%ld, status sta=%hd", fStartSyncThread.exitcode(),sta));
+ // initialisation is now complete
+ fInitializing=false;
+ } // if
+ } // if
+ } // if
+ #endif
+
+ // now complete initialisation if not still initializing in background
+ if (!fStartInit && !fInitializing) {
+ // finished background processing
+ if (sta==LOCERR_OK) {
+ // quick test: if number of items is > than allowed maxid of remote datatstore,
+ // sync is unlikely to succeed
+ if (getRemoteDatastore()->getMaxID()<fItems.size()) {
+ // this will not work, warn (but no longer abort session, as Siemens S55 guys don't like that)
+ CONSOLEPRINTF((
+ "Warning: Synchronisation involves more items (%ld) than client can possibly manage (%ld",
+ (sInt32)fItems.size(),
+ (sInt32)getRemoteDatastore()->getMaxID()
+ ));
+ PDEBUGPRINTFX(DBG_ERROR,(
+ "Warning: Synchronisation involves more items (%ld) than client can possibly manage (%ld)",
+ (sInt32)fItems.size(),
+ (sInt32)getRemoteDatastore()->getMaxID()
+ ));
+ }
+ }
+ // return status of initialisation
+ return sta;
+ }
+ else {
+ // background processing still in progress
+ // - if we are still processing in background, init is ok so far
+ return LOCERR_OK;
+ }
+} // TStdLogicDS::startDataAccessForServer
+
+
+// called to check if conflicting replace or delete command from server exists
+TSyncItem *TStdLogicDS::getConflictingItemByRemoteID(TSyncItem *syncitemP)
+{
+ // search for conflicting item by LUID
+ TSyncItemPContainer::iterator pos;
+ for (pos=fItems.begin(); pos!=fItems.end(); ++pos) {
+ if (strcmp((*pos)->getRemoteID(),syncitemP->getRemoteID())==0) {
+ // same LUID exists in data from server
+ PDEBUGPRINTFX(DBG_DATA+DBG_CONFLICT,(
+ "TStdLogicDS::getConflictingItemByRemoteID, found RemoteID='%s', LocalID='%s', syncop=%s",
+ syncitemP->getRemoteID(),
+ syncitemP->getLocalID(),
+ SyncOpNames[syncitemP->getSyncOp()]
+ ));
+ return (*pos); // return pointer to item in question
+ }
+ }
+ PDEBUGPRINTFX(DBG_DATA+DBG_CONFLICT,("TStdLogicDS::getConflictingItemByRemoteID, no conflicting item"));
+ return NULL;
+} // TStdLogicDS::getConflictingItemByRemoteID
+
+
+// called to check if content-matching item from server exists for slow sync
+TSyncItem *TStdLogicDS::getMatchingItem(TSyncItem *syncitemP, TEqualityMode aEqMode)
+{
+ // search for content matching item
+ TSyncItemPContainer::iterator pos;
+ for (pos=fItems.begin(); pos!=fItems.end(); ++pos) {
+ DEBUGPRINTFX(DBG_DATA+DBG_MATCH+DBG_EXOTIC,(
+ "comparing (this) local item localID='%s' with incoming (other) item remoteID='%s'",
+ (*pos)->getLocalID(),
+ syncitemP->getRemoteID()
+ ));
+ if ((*pos)->compareWith(
+ *syncitemP,aEqMode,this
+ #ifdef SYDEBUG
+ ,PDEBUGTEST(DBG_DATA+DBG_MATCH+DBG_EXOTIC) // only show comparison if exotic AND match is enabled
+ #endif
+ )==0) {
+ // items match in content
+ // - check if item is not already matched
+ if ((*pos)->getSyncOp()!=sop_wants_add && (*pos)->getSyncOp()!=sop_reference_only) {
+ // item has already been matched before, so don't match it again
+ DEBUGPRINTFX(DBG_DATA,(
+ "TStdLogicDS::getMatchingItem, match but already used -> skip it: remoteID='%s' = localID='%s'",
+ syncitemP->getRemoteID(),
+ (*pos)->getLocalID()
+ ));
+ }
+ else {
+ // item has not been matched yet (wannabe add or reference-only), return it now
+ PDEBUGPRINTFX(DBG_DATA+DBG_MATCH+DBG_HOT,(
+ "TStdLogicDS::getMatchingItem, found remoteID='%s' is equal in content with localID='%s'",
+ syncitemP->getRemoteID(),
+ (*pos)->getLocalID()
+ ));
+ return (*pos); // return pointer to item in question
+ }
+ }
+ }
+ PDEBUGPRINTFX(DBG_DATA+DBG_MATCH,("TStdLogicDS::getMatchingItem, no matching item"));
+ return NULL;
+} // TStdLogicDS::getMatchingItem
+
+
+// - called to prevent item to be sent to client in subsequent generateSyncCommands()
+// item in question should be an item that was returned by getConflictingItemByRemoteID() or getMatchingItem()
+void TStdLogicDS::dontSendItemAsServer(TSyncItem *syncitemP)
+{
+ PDEBUGPRINTFX(DBG_DATA+DBG_EXOTIC,("Preventing localID='%s' to be sent to client",syncitemP->getLocalID()));
+ syncitemP->setSyncOp(sop_none); // anyway, set to none
+ // delete from list as we don't need it any more
+ TSyncItemPContainer::iterator pos;
+ for (pos=fItems.begin(); pos!=fItems.end(); ++pos) {
+ if (*pos == syncitemP) {
+ // it is in our list
+ PDEBUGPRINTFX(DBG_DATA+DBG_HOT,("Item with localID='%s' will NOT be sent to client (usually due to slowsync match)",syncitemP->getLocalID()));
+ delete *pos; // delete item itself
+ fItems.erase(pos); // remove from list
+ break;
+ }
+ }
+} // TStdLogicDS::dontSendItemAsServer
+
+
+// - called when a item in the sync set changes its localID (due to local DB internals)
+// Datastore must make sure that eventually cached items get updated
+// - NOTE: derivates must take care of updating map entries as well!
+void TStdLogicDS::dsLocalIdHasChanged(const char *aOldID, const char *aNewID)
+{
+ PDEBUGPRINTFX(DBG_DATA,("TStdLogicDS::dsLocalIdHasChanged"));
+ // update in loaded list of items
+ TSyncItemPContainer::iterator pos;
+ for (pos=fItems.begin(); pos!=fItems.end(); ++pos) {
+ if (strcmp((*pos)->getLocalID(),aOldID)==0) {
+ // found item, change it's local ID now
+ (*pos)->setLocalID(aNewID);
+ // make sure internal dependencies get updated
+ (*pos)->updateLocalIDDependencies();
+ // done
+ break;
+ }
+ }
+ // let base class do what is needed to update the item itself
+ inherited::dsLocalIdHasChanged(aOldID, aNewID);
+} // TStdLogicDS::dsLocalIdHasChanged
+
+
+
+// - called to have additional item sent to remote
+void TStdLogicDS::SendItemAsServer(TSyncItem *aSyncitemP)
+{
+ // add to list of changes
+ fItems.push_back(aSyncitemP);
+} // TStdLogicDS::SendItemAsServer
+
+
+// - end map operation (derived class might want to rollback)
+bool TStdLogicDS::MapFinishAsServer(
+ bool aDoCommit, // if not set, entire map operation must be undone
+ TStatusCommand &aStatusCommand // status, must be set on error or non-200-status
+)
+{
+ // unsuccessful Map will cause rollback of entire datastore transaction
+ if (!aDoCommit) {
+ // bad, abort session
+ engAbortDataStoreSync(510,true); // data store failed, local problem
+ }
+ return true;
+} // TStdLogicDS::MapFinishAsServer
+
+
+// - called for SyncML 1.1 if remote wants number of changes.
+// Must return -1 if no NOC value can be returned
+// NOTE: we implement it here only for server, as it is not really needed
+// for clients normally - if it is needed, client's agent must provide
+// it as CustDBDatastore has no own list it can use to count in client case.
+sInt32 TStdLogicDS::getNumberOfChanges(void)
+{
+ // for server, number of changes is the number of items in the item list
+ // minus those that are for reference only (in a slow sync resume)
+ return fItems.size()-fNumRefOnlyItems;
+} // TStdLogicDS::getNumberOfChanges
+
+
+/// @brief called to have all non-yet-generated sync commands as "to-be-resumed"
+void TStdLogicDS::logicMarkOnlyUngeneratedForResume(void)
+{
+ // we do not maintain the map/bookmark list at this level, so
+ // derived class (ODBC, BinFile etc.) must make sure that their list is
+ // clean (no marks from previous sessions) before calling this inherited version
+ implMarkOnlyUngeneratedForResume();
+ // Now add those that we have already received from the implementation
+ TSyncItemPContainer::iterator pos;
+ for (pos = fItems.begin(); pos != fItems.end(); ++pos) {
+ // let datastore mark these unprocessed
+ TSyncItem *syncitemP = (*pos);
+ // mark it for resume by ID
+ logicMarkItemForResume(syncitemP->getLocalID(),syncitemP->getRemoteID(),true); // these are unsent
+ }
+} // TStdLogicDS::logicMarkOnlyUngeneratedForResume
+
+
+
+// - called to let server generate sync commands for client
+// Returns true if now finished (or aborted) for this datastore
+// also sets fState to dss_syncdone when finished
+bool TStdLogicDS::logicGenerateSyncCommandsAsServer(
+ TSmlCommandPContainer &aNextMessageCommands,
+ TSmlCommand * &aInterruptedCommandP,
+ const char *aLocalIDPrefix
+)
+{
+ bool alldone=false;
+ bool ignoreitem;
+ uInt32 itemcount=0;
+ // send as many as possible from list of local modifications
+ // sop_want_replace can only be sent if state is already dss_syncfinish
+ TSyncItemPContainer::iterator pos;
+ pos = fItems.begin(); // first item
+ TSyncItemType *itemtypeP = getRemoteReceiveType();
+ POINTERTEST(itemtypeP,("TStdLogicDS::logicGenerateSyncCommandsAsServer: fRemoteReceiveFromLocalTypeP undefined"));
+ #ifdef SYDEBUG
+ if (fMaxItemCount> 0) {
+ PDEBUGPRINTFX(DBG_DATA+DBG_HOT,(
+ "Info: Max number of items to be sent in this session is limited to %ld (already sent by now=%ld)",
+ fMaxItemCount,
+ fItemsSent
+ ));
+ }
+ #endif
+ while (
+ !isAborted() && // not aborted
+ (getDSConfig()->fMaxItemsPerMessage==0 || itemcount<getDSConfig()->fMaxItemsPerMessage==0) && // max item count per message not reached or not active
+ !fSessionP->outgoingMessageFull() && // message not full
+ aNextMessageCommands.size()==0 // no commands already queued for next message
+ ) {
+ // get item to process
+ if (pos == fItems.end()) {
+ alldone=true;
+ break;
+ }
+ //
+ TSyncItem *syncitemP = (*pos);
+ // get sync op to perform
+ TSyncOperation syncop=syncitemP->getSyncOp();
+ // check if we can send the item now (for replaces, we need ALWAYS to wait until client has finished sending)
+ // Note: usually sync engine will not start generating before dssta_serverseenclientmods anyway, but...
+ if (syncop==sop_wants_replace && !testState(dssta_serverseenclientmods)) {
+ // cannot be sent now, take next
+ pos++;
+ continue;
+ }
+ // check if we should ignore this item
+ ignoreitem = syncop==sop_reference_only; // ignore anyway if reference only
+ // further check if not already ignored
+ if (!ignoreitem) {
+ // - check if adding is still allowed
+ if (fRemoteAddingStopped && (syncop==sop_wants_add || syncop==sop_add)) {
+ // adding to remote has been stopped, discard add items
+ PDEBUGPRINTFX(DBG_DATA,(
+ "Suppressed add for item localID='%s' (fRemoteAddingStopped)",
+ syncitemP->getLocalID()
+ ));
+ ignoreitem=true;
+ }
+ #ifdef SYNCML_TAF_SUPPORT
+ // check other reasons to prevent further adds
+ if (syncop==sop_wants_add || syncop==sop_add) {
+ // - check if max number of items has already been reached
+ if (fMaxItemCount!=0 && fItemsSent>=fMaxItemCount) {
+ PDEBUGPRINTFX(DBG_DATA,(
+ "Suppressed add for item localID='%s' (max item count=%ld reached)",
+ syncitemP->getLocalID(),
+ fMaxItemCount
+ ));
+ ignoreitem=true;
+ }
+ // - check if item passes eventual TAF
+ /// %%% (do not filter replaces, as these would not get reported again in the next session)
+ /// @todo: the above is no longer true as we can now have them re-sent in next session,
+ /// so this must be changed later!!!
+ if (!(
+ syncitemP->testFilter(fTargetAddressFilter.c_str()) &&
+ syncitemP->testFilter(fIntTargetAddressFilter.c_str())
+ )) {
+ PDEBUGPRINTFX(DBG_DATA+DBG_HOT,(
+ "Item localID='%s' does not pass INCLUSIVE filter (TAF) -> Suppressed adding",
+ syncitemP->getLocalID()
+ ));
+ ignoreitem=true;
+ }
+ }
+ #endif
+ }
+ // Now discard if ignored
+ if (ignoreitem) {
+ // remove item from list
+ TSyncItemPContainer::iterator temp_pos = pos++; // make copy and set iterator to next
+ fItems.erase(temp_pos); // now entry can be deleted (N.M. Josuttis, pg204)
+ // delete item itself
+ delete syncitemP;
+ // test next
+ continue;
+ }
+ // add prefixes to ID
+ if (syncitemP->hasLocalID()) {
+ // make sure GUID (plus prefixes) is not exceeding allowed size
+ adjustLocalIDforSize(syncitemP->fLocalID,getRemoteDatastore()->getMaxGUIDSize(),aLocalIDPrefix ? strlen(aLocalIDPrefix) : 0);
+ // add local ID prefix, if any
+ if (aLocalIDPrefix && *aLocalIDPrefix)
+ syncitemP->fLocalID.insert(0,aLocalIDPrefix);
+ }
+ // create sync op command (may return NULL in case command cannot be created, e.g. for MaxObjSize limitations)
+ TSyncOpCommand *syncopcmdP = newSyncOpCommand(syncitemP,itemtypeP);
+ // erase item from list
+ delete syncitemP;
+ pos = fItems.erase(pos);
+ // issue command now
+ // - Note that when command is split, issuePtr returns true, but we still may NOT generate new commands
+ // as the message is already full now. That's why the while contains a check for message full and aNextMessageCommands size
+ // (was not the case before 2.1.0.2, which could cause that the first chunk of a subsequent command
+ // would be sent before the third..nth chunk of the previous command).
+ TSmlCommand *cmdP = syncopcmdP;
+ syncopcmdP=NULL;
+ // eventually, we have a NULL command here (e.g. in case it could not be generated due to MaxObjSize restrictions)
+ if (cmdP) {
+ if (!fSessionP->issuePtr(cmdP,aNextMessageCommands,aInterruptedCommandP)) {
+ alldone=false; // issue failed (no room in message), not finished so far
+ break;
+ }
+ // count item sent
+ fItemsSent++; // overall counter for statistics
+ itemcount++; // per message counter
+ // send event (but no check for abort)
+ OBJ_PROGRESS_EVENT(fSessionP->getSyncAppBase(),pev_itemsent,getDSConfig(),fItemsSent,getNumberOfChanges(),0);
+ }
+ }; // while not aborted and not message full
+ // we are not done until all aNextMessageCommands are also out
+ // Note: this must be specially checked because we now have SyncML 1.1 chunked commands.
+ // Those issue() fine, but leave a next chunk in the aNextMessageCommands queue.
+ if (alldone && aNextMessageCommands.size()>0) {
+ alldone=false;
+ }
+ // finished when we have done all
+ return (alldone || isAborted());
+} // TStdLogicDS::logicGenerateSyncCommandsAsServer
+
+
+// called for servers when receiving map from client
+localstatus TStdLogicDS::logicProcessMap(cAppCharP aRemoteID, cAppCharP aLocalID)
+{
+ // simply call implementation
+ return implProcessMap(aRemoteID, aLocalID);
+} // TStdLogicDS::logicProcessMap
+
+
+
+#else
+
+// Client Case
+// ===========
+
+// called by dsBeforeStateChange to dssta_dataaccessstarted to make sure datastore is ready for being accessed.
+localstatus TStdLogicDS::startDataAccessForClient(void)
+{
+ DEBUGPRINTFX(DBG_HOT,("TStdLogicDS::startDataAccessForClient"));
+ // init
+ fWriteStarted=false;
+ fEoC=false; // not all changes seen yet
+ // prepare for read
+ #ifdef SCRIPT_SUPPORT
+ // - call DB init script, which might add extra filters depending on options and remoterule
+ TScriptContext::execute(
+ fDataStoreScriptContextP,
+ getDSConfig()->fDBInitScript,
+ &DBFuncTable, // context's function table
+ this // datastore pointer needed for context
+ );
+ #endif
+ // - init post fetch filtering, sets fFilteringNeededForAll and fFilteringNeeded correctly
+ initPostFetchFiltering();
+ // - prepare for read
+ localstatus sta=implStartDataRead();
+ return sta;
+} // TStdLogicDS::startDataAccessForClient
+
+
+/// @brief called to have all non-yet-generated sync commands as "to-be-resumed"
+void TStdLogicDS::logicMarkOnlyUngeneratedForResume(void)
+{
+ // we do not maintain the map/bookmark list at this level, so
+ // derived class (ODBC, BinFile etc.) must make sure that their list is
+ // clean (no marks from previous sessions) before calling this inherited version
+ implMarkOnlyUngeneratedForResume();
+ // in client case, fItems does not contain ungenerated/unprocessed items
+ // so we don't have anything more do here for now
+} // TStdLogicDS::logicMarkOnlyUngeneratedForResume
+
+
+
+// called to generate sync sub-commands as client for remote server
+// @return true if now finished for this datastore
+bool TStdLogicDS::logicGenerateSyncCommandsAsClient(
+ TSmlCommandPContainer &aNextMessageCommands,
+ TSmlCommand * &aInterruptedCommandP,
+ const char *aLocalIDPrefix
+)
+{
+ localstatus sta = LOCERR_OK;
+ bool alldone=true;
+ // send as many changed items as possible
+ TSyncItemType *itemtypeP = getRemoteReceiveType();
+ POINTERTEST(itemtypeP,("TStdLogicDS::logicGenerateSyncCommandsAsClient: fRemoteReceiveFromLocalTypeP undefined"));
+ while (!fEoC && !isAborted() && !fSessionP->outgoingMessageFull() && aNextMessageCommands.size()==0) {
+ // get next item from DB
+ TSyncItem *syncitemP = NULL;
+ #ifdef OBJECT_FILTERING
+ bool changed=!fFilteringNeededForAll; // set if we need all records for later filtering
+ #else
+ bool changed=true; // without filters, always let DB check if modified
+ #endif
+ sta = implGetItem(fEoC,changed,syncitemP);
+ if (sta!=LOCERR_OK) {
+ // fatal error
+ implEndDataRead(); // terminate reading (error does not matter)
+ engAbortDataStoreSync(sta, true); // local problem
+ return false; // not complete
+ }
+ // read successful, test for EoC (end of changes)
+ if (fEoC)
+ break; // reading done
+ // get sync op to perform
+ TSyncOperation syncop=syncitemP->getSyncOp();
+ #ifdef OBJECT_FILTERING
+ // Filtering
+ // - call this anyway (makes sure item is made conformant to remoteAccept filter, even if
+ // fFilteringNeeded is not set)
+ if (syncop!=sop_delete && syncop!=sop_soft_delete && syncop!=sop_archive_delete) {
+ bool passes=postFetchFiltering(syncitemP);
+ if (fFilteringNeeded) {
+ if (!passes) {
+ // item does not pass (current) filter: don't send it.
+ // Note that we DO NOT DELETE items falling out of the sync set by filtering,
+ // as for that we'd need to be able to differentiate adds from replaces.
+ // The use case for client-side filtering is also normally not the "moving-subset-window"
+ // case as for server side filtering, but more static exclusion of certain types of
+ // local entries (e.g. to prevent private stuff going to the server).
+ // - we don't need that sync item
+ delete syncitemP;
+ // - try next
+ continue;
+ }
+ }
+ }
+ #endif
+ // add local ID prefix, if any
+ if (aLocalIDPrefix && *aLocalIDPrefix && syncitemP->hasLocalID())
+ syncitemP->fLocalID.insert(0,aLocalIDPrefix);
+ // create sync op command
+ TSyncOpCommand *syncopcmdP = newSyncOpCommand(syncitemP,itemtypeP);
+ #ifdef CLIENT_USES_SERVER_DB
+ // save item, we need it later for post-processing and Map simulation
+ fItems.push_back(syncitemP);
+ #else
+ // delete item, not used any more
+ delete syncitemP;
+ #endif
+ // issue command now
+ // - Note that when command is split, issuePtr returns true, but we still may NOT generate new commands
+ // as the message is already full now. That's why the while contains a check for message full and aNextMessageCommands size
+ // (was not the case before 2.1.0.2, which could cause that the first chunk of a subsequent command
+ // would be sent before the third..nth chunk of the previous command).
+ TSmlCommand *cmdP = syncopcmdP;
+ syncopcmdP=NULL;
+ // eventually, we have a NULL command here (e.g. in case it could not be generated due to MaxObjSize restrictions)
+ if (cmdP) {
+ if (!fSessionP->issuePtr(cmdP,aNextMessageCommands,aInterruptedCommandP)) {
+ alldone=false; // issue failed (no room in message), not finished so far
+ break;
+ }
+ // count item sent
+ fItemsSent++;
+ // send event and check for abort
+ #ifdef PROGRESS_EVENTS
+ if (!fSessionP->getSyncAppBase()->NotifyProgressEvent(pev_itemsent,getDSConfig(),fItemsSent,getNumberOfChanges())) {
+ implEndDataRead(); // terminate reading
+ fSessionP->AbortSession(500,true,LOCERR_USERABORT);
+ return false; // error
+ }
+ // check for "soft" suspension
+ if (!fSessionP->getSyncAppBase()->NotifyProgressEvent(pev_suspendcheck)) {
+ fSessionP->SuspendSession(LOCERR_USERSUSPEND);
+ }
+ #endif
+ }
+ }; // while not aborted
+ // we are not done until all aNextMessageCommands are also out
+ // Note: this must be specially checked because we now have SyncML 1.1 chunked commands.
+ // Those issue() fine, but leave a next chunk in the aNextMessageCommands queue.
+ if (alldone && aNextMessageCommands.size()>0) {
+ alldone=false;
+ }
+ // done if we are now ready for sync or if aborted
+ return ((alldone && fEoC) || isAborted());
+} // TStdLogicDS::logicGenerateSyncCommandsAsClient
+
+
+#endif // client case
+
+
+// called to process incoming item operation
+// Method takes ownership of syncitemP in all cases
+bool TStdLogicDS::logicProcessRemoteItem(
+ TSyncItem *syncitemP,
+ TStatusCommand &aStatusCommand,
+ bool &aVisibleInSyncset, // on entry: tells if resulting item SHOULD be visible; on exit: set if processed item remains visible in the sync set.
+ string *aGUID // GUID is stored here if not NULL
+) {
+ localstatus sta=LOCERR_OK;
+ bool irregular=false;
+ bool shouldbevisible=aVisibleInSyncset;
+ string datatext;
+
+ TP_DEFIDX(li);
+ TP_SWITCH(li,fSessionP->fTPInfo,TP_database);
+ SYSYNC_TRY {
+ // assume item will stay visible in the syncset after processing
+ aVisibleInSyncset=true;
+ // start writing if not already started
+ sta=startDataWrite();
+ if (sta!=LOCERR_OK) {
+ aStatusCommand.setStatusCode(sta);
+ }
+ else {
+ // show
+ DEBUGPRINTFX(DBG_DATA,(
+ "TStdLogicDS::logicProcessRemoteItem starting, SyncOp=%s, RemoteID='%s', LocalID='%s'",
+ SyncOpNames[syncitemP->getSyncOp()],
+ syncitemP->getRemoteID(),
+ syncitemP->getLocalID()
+ ));
+ // now perform action
+ if (syncitemP->getSyncOp()==sop_replace || syncitemP->getSyncOp()==sop_wants_replace) {
+ // check if we should read before writing
+ TMultiFieldItem *mfiP;
+ GET_CASTED_PTR(mfiP,TMultiFieldItem,syncitemP,"");
+ bool replacewritesallfields = dsReplaceWritesAllDBFields();
+ bool mightcontaincutoff=false;
+ // - see if we would pass sync set filter as is
+ #ifdef OBJECT_FILTERING
+ aVisibleInSyncset = mfiP->testFilter(fSyncSetFilter.c_str());
+ bool invisible =
+ !getDSConfig()->fInvisibleFilter.empty() && // has an invisible filter
+ mfiP->testFilter(getDSConfig()->fInvisibleFilter.c_str()); // and passes it -> invisible
+ bool visibilityok = (aVisibleInSyncset && !invisible) == shouldbevisible; // check if visibility is correct
+ if (visibilityok && !replacewritesallfields && aVisibleInSyncset) // avoid expensive check if we have to read anyway
+ #endif
+ mightcontaincutoff = mfiP->getItemType()->mayContainCutOffData(mfiP->getTargetItemType());
+ // - if not, we must read item from the DB first and then
+ // test again.
+ if (
+ #ifdef OBJECT_FILTERING
+ !visibilityok ||
+ #endif
+ replacewritesallfields ||
+ mightcontaincutoff ||
+ fIgnoreUpdate // if we may not update items, only add them, then we must check first if item exists in DB
+ ) {
+ // the item we are replacing might contain cut-off data or
+ // needs otherwise to be modified based on current contents
+ // we should therefore read item from DB first
+ // - create new empty TMultiFieldItem
+ TMultiFieldItem *refitemP =
+ (TMultiFieldItem *) newItemForRemote(ity_multifield);
+ #ifdef SYSYNC_CLIENT
+ // Client: retrieve by local ID
+ refitemP->clearRemoteID(); // not known
+ refitemP->setLocalID(syncitemP->getLocalID()); // make sure we retrieve by local ID
+ #else
+ // Server: retrieve by remote ID
+ refitemP->clearLocalID(); // make sure we retrieve by remote ID
+ refitemP->setRemoteID(syncitemP->getRemoteID()); // make sure we retrieve by remote ID
+ #endif
+ refitemP->setSyncOp(sop_replace);
+ #ifdef OBJECT_FILTERING
+ PDEBUGPRINTFX(DBG_DATA,(
+ "TStdLogicDS: Need read-modify-write (cause: %s%s%s%s) -> retrieve original item from DB",
+ !visibilityok ? "visibility_not_ok " : "",
+ replacewritesallfields ? "replace_writes_all_fields " : "",
+ mightcontaincutoff ? "might_contain_cutoff_data " : "",
+ fIgnoreUpdate ? "ignoreUpdate " : ""
+ ));
+ #else
+ PDEBUGPRINTFX(DBG_DATA,(
+ "TStdLogicDS: Need read-modify-write (cause: %s%s%s) -> retrieve original item from DB",
+ replacewritesallfields ? "replace_writes_all_fields " : "",
+ mightcontaincutoff ? "might_contain_cutoff_data " : "",
+ fIgnoreUpdate ? "ignoreUpdate " : ""
+ ));
+ #endif
+ if (implRetrieveItemByID(*refitemP,aStatusCommand)) {
+ #ifdef SYDEBUG
+ if (PDEBUGTEST(DBG_DATA)) {
+ PDEBUGPRINTFX(DBG_DATA,("TStdLogicDS: Retrieved item"));
+ if (PDEBUGTEST(DBG_DATA+DBG_DETAILS)) refitemP->debugShowItem(); // show item retrieved
+ }
+ #endif
+ if (fIgnoreUpdate) {
+ // updates may not be executed at all, and be simply ignored
+ aStatusCommand.setStatusCode(200); // fake ok.
+ PDEBUGPRINTFX(DBG_DATA+DBG_HOT,("TStdLogicDS: fIgnoreUpdate set, update command not executed but answered with status 200"));
+ irregular=true;
+ sta=LOCERR_OK;
+ goto processed;
+ }
+ // we got the item to be replaced
+ // - now modify it:
+ // - only modify available fields
+ // - perform cutoff prevention
+ // - do not just copy assigned fields (but all those that are available)
+ // - IF replace can write individual fields, then transfer unassigned status
+ // (also for non-availables!) to avoid that datastore needs to write back
+ // values that are already there.
+ refitemP->replaceDataFrom(*syncitemP,true,true,false,!replacewritesallfields);
+ #ifdef SYDEBUG
+ if (PDEBUGTEST(DBG_DATA)) {
+ PDEBUGPRINTFX(DBG_DATA,("TStdLogicDS: Item updated with contents from remote"));
+ if (PDEBUGTEST(DBG_DATA+DBG_EXOTIC)) refitemP->debugShowItem(); // show item retrieved
+ }
+ #endif
+ #ifdef OBJECT_FILTERING
+ // - make sure item will pass sync set filter NOW
+ makePassSyncSetFilter(refitemP);
+ // - make sure item will be visible NOW
+ makeVisible(refitemP);
+ #ifdef SYDEBUG
+ if (PDEBUGTEST(DBG_DATA)) {
+ PDEBUGPRINTFX(DBG_DATA,("TStdLogicDS: Made visible and pass sync set filter"));
+ if (PDEBUGTEST(DBG_DATA+DBG_EXOTIC)) refitemP->debugShowItem(); // show item retrieved
+ }
+ #endif
+ #endif
+ // - get rid of original
+ delete syncitemP;
+ // - use new item for further processing
+ syncitemP=refitemP;
+ }
+ else {
+ // failed retrieving item: switch to add
+ sta = aStatusCommand.getStatusCode();
+ if (sta==404 || sta==410) {
+ // this is an irregularity for a client (but it's perfectly
+ // normal case for server, as client may use replace for adds+replaces)
+ #ifdef SYSYNC_CLIENT
+ if (!syncitemP->hasRemoteID()) {
+ // - we cannot handle this properly, we have no remoteID, so report error to server
+ PDEBUGPRINTFX(DBG_ERROR,("TStdLogicDS: Item not found, but cannot switch to add because no RemoteID is known, Status=%hd",aStatusCommand.getStatusCode()));
+ }
+ else
+ #endif
+ {
+ if (fPreventAdd) {
+ // prevent implicit add -> return sta as is
+ }
+ else {
+ // - switch to add if remote sent remoteID along so we can properly map
+ syncitemP->setSyncOp(sop_add);
+ PDEBUGPRINTFX(DBG_DATA,("TStdLogicDS: RetrieveItem: not found (Status=%hd) --> adding instead",sta));
+ #ifndef SYSYNC_CLIENT
+ // - make sure we delete the old item from the map table (as we *know* the item is gone - should it reappear under a different localID, it'll be re-added)
+ implProcessMap(syncitemP->getRemoteID(),NULL);
+ #endif
+ // we can handle it, as we know the remoteID
+ #ifdef SYSYNC_CLIENT
+ irregular=true; // irregular only for client
+ #endif
+ sta=LOCERR_OK; // ok for further processing, anyway
+ }
+ }
+ }
+ else {
+ // this is a fatal error, report it
+ PDEBUGPRINTFX(DBG_ERROR,("TStdLogicDS: RetrieveItem failed, Status=%hd",sta));
+ }
+ // get rid of reference item
+ delete refitemP;
+ }
+ }
+ }
+ if (sta==LOCERR_OK) {
+ // make sure that added items will pass sync set filters. If we can't make them pass
+ // for DS 1.2 exclusive filters we must generate a delete so we must remember the itempassed status)
+ #ifdef OBJECT_FILTERING
+ if (syncitemP->getSyncOp()==sop_add || syncitemP->getSyncOp()==sop_wants_add) {
+ // - make sure new item will pass sync set filter when re-read from DB
+ aVisibleInSyncset=makePassSyncSetFilter(syncitemP);
+ // - also make sure new item has correct visibility status
+ if (shouldbevisible)
+ makeVisible(syncitemP); // make visible
+ else
+ aVisibleInSyncset = !makeInvisible(syncitemP); // make invisible
+ }
+ #endif
+ // Now let derived class process the item
+ if (!implProcessItem(syncitemP,aStatusCommand))
+ sta = aStatusCommand.getStatusCode(); // not successful, get error status code
+ }
+ // perform special case handling
+ if (sta!=LOCERR_OK) {
+ // irregular, special case handling
+ switch (syncitemP->getSyncOp()) {
+ case sop_wants_add : // to make sure
+ case sop_add :
+ if (sta==418) {
+ // 418: item already exists, this is kind of a conflict
+ // (should not happen normally, but can happen if aborted session was not
+ // completely rolled back by server, so treat it like
+ // "conflict resolved by client data winning")
+ PDEBUGPRINTFX(DBG_DATA,("to-be-added item already exists, and incomplete rollbacks in server possible -> trying replace (=conflict resolved by client winning)"));
+ // - switch to replace
+ syncitemP->setSyncOp(sop_replace);
+ irregular=true;
+ // - process again
+ if (implProcessItem(syncitemP,aStatusCommand)) {
+ aStatusCommand.setStatusCode(208); // client has won
+ }
+ else {
+ sta = aStatusCommand.getStatusCode();
+ }
+ }
+ break;
+ case sop_replace :
+ if (sta==404 || sta==410) {
+ // this is an irregularity for a client (but it's perfectly
+ // normal case for server, as client may use replace for adds+replaces)
+ #ifdef SYSYNC_CLIENT
+ if (!syncitemP->hasRemoteID()) {
+ // - we cannot handle this properly, we have no remoteID, so report error to server
+ PDEBUGPRINTFX(DBG_ERROR,("to-be-replaced item not found, but cannot switch to add because no RemoteID is known, Status=%hd",sta));
+ }
+ else
+ #endif
+ {
+ if (fPreventAdd) {
+ // prevent implicit add -> return status as is
+ }
+ else {
+ // - switch to add if remote sent remoteID along so we can properly map
+ syncitemP->setSyncOp(sop_add);
+ #ifndef SYSYNC_CLIENT
+ // - make sure we delete the old item from the map table (as we *know* the item is gone - should it reappear under a different localID, it'll be re-added)
+ implProcessMap(syncitemP->getRemoteID(),NULL);
+ #endif
+ // we can handle it, as we know the remoteID
+ #ifdef SYSYNC_CLIENT
+ irregular=true;
+ #endif
+ PDEBUGPRINTFX(DBG_DATA,("to-be-replaced item not found (Status=%hd) --> adding instead",sta));
+ // - process again (note that we are re-using the status command that might
+ // already have a text item with an OS errir if something failed before)
+ sta=LOCERR_OK; // forget previous status
+ if (!implProcessItem(syncitemP,aStatusCommand))
+ sta=aStatusCommand.getStatusCode(); // not successful, get error status code
+ }
+ }
+ }
+ break;
+ case sop_delete :
+ case sop_archive_delete :
+ case sop_soft_delete :
+ if (sta==404 || sta==410) {
+ if (fSessionP->getSessionConfig()->fDeletingGoneOK) {
+ // 404/410: item not found, could be because previous aborted session has
+ // already committed deletion of that item -> behave as if delete was ok
+ PDEBUGPRINTFX(DBG_DATA,("to-be-deleted item was not found, but do NOT report %hd",sta));
+ aStatusCommand.setStatusCode(200);
+ irregular=true;
+ sta = LOCERR_OK; // this is ok, item is deleted already
+ }
+ }
+ break;
+ default :
+ SYSYNC_THROW(TSyncException("Unknown sync op in TStdLogicDS::logicProcessRemoteItem"));
+ } // switch
+ } // if not ok
+ processed:
+ if (sta==LOCERR_OK) {
+ PDEBUGPRINTFX(DBG_DATA,(
+ "- Operation %s performed (%sregular), Remote ID=%s Local ID=%s, status=%hd",
+ SyncOpNames[syncitemP->getSyncOp()],
+ irregular ? "ir" : "",
+ syncitemP->getRemoteID(),
+ syncitemP->getLocalID(),
+ aStatusCommand.getStatusCode()
+ ));
+ // return GUID if string ptr was passed
+ if (aGUID) {
+ (*aGUID)=syncitemP->getLocalID();
+ }
+ }
+ else {
+ PDEBUGPRINTFX(DBG_ERROR,(
+ "- Operation %s failed with SyncML status=%hd",
+ SyncOpNames[syncitemP->getSyncOp()],
+ sta
+ ));
+ }
+ } // if startDataWrite ok
+ // anyway, we are done with this item, delete it now
+ delete syncitemP;
+ TP_START(fSessionP->fTPInfo,li);
+ // done, return regular/irregular status
+ return (sta==LOCERR_OK) && !irregular;
+ }
+ SYSYNC_CATCH (...)
+ // delete the item
+ if (syncitemP) delete syncitemP;
+ TP_START(fSessionP->fTPInfo,li);
+ // re-throw
+ SYSYNC_RETHROW;
+ SYSYNC_ENDCATCH
+ #ifndef __BORLANDC__
+ return false; // BCPPB: unreachable code
+ #endif
+} // TStdLogicDS::logicProcessRemoteItem
+
+
+
+// Abort datastore sync
+void TStdLogicDS::dsAbortDatastoreSync(TSyError aReason, bool aLocalProblem)
+{
+ // call anchestor
+ inherited::dsAbortDatastoreSync(aReason, aLocalProblem);
+} // TStdLogicDS::dsAbortDatastoreSync
+
+
+// inform logic of coming state change
+localstatus TStdLogicDS::dsBeforeStateChange(TLocalEngineDSState aOldState,TLocalEngineDSState aNewState)
+{
+ localstatus sta=LOCERR_OK;
+
+ if (aNewState==dssta_dataaccessstarted) {
+ // start data access
+ #ifdef SYSYNC_CLIENT
+ sta = startDataAccessForClient();
+ #else
+ sta = startDataAccessForServer();
+ #endif
+ }
+ #ifdef SYSYNC_CLIENT
+ if (aNewState==dssta_syncgendone) {
+ // when client has done sync gen, start writing
+ sta = startDataWrite();
+ }
+ #endif
+ if (aNewState==dssta_completed && !isAborted()) {
+ // finish writing data now anyway
+ endDataWrite();
+ // we must save anchors at the moment we shift from any state to dssta_completed
+ PDEBUGPRINTFX(DBG_ADMIN,("TStdLogicDS: successfully completed, save anchors now"));
+ // update our level's state
+ fPreviousSyncTime = fCurrentSyncTime;
+ // let implementation update their state and save it
+ sta=implSaveEndOfSession(true);
+ if (sta!=LOCERR_OK) {
+ PDEBUGPRINTFX(DBG_ERROR,("TStdLogicDS: Could not save session completed status, err=%hd",sta));
+ }
+ }
+ if (aNewState==dssta_idle && aOldState>=dssta_syncsetready) {
+ // again: make sure data is written anyway (if already done this is a NOP)
+ endDataWrite();
+ }
+ // abort on error
+ if (sta!=LOCERR_OK) return sta;
+ // let inherited do its stuff as well
+ return inherited::dsBeforeStateChange(aOldState,aNewState);
+} // TStdLogicDS::dsBeforeStateChange
+
+
+// inform logic of happened state change
+localstatus TStdLogicDS::dsAfterStateChange(TLocalEngineDSState aOldState,TLocalEngineDSState aNewState)
+{
+ localstatus sta=LOCERR_OK;
+
+ if (aNewState==dssta_dataaccessdone) {
+ // finish writing data now
+ endDataWrite();
+ }
+ // abort on error
+ if (sta!=LOCERR_OK) return sta;
+ // let inherited do its stuff as well
+ return inherited::dsAfterStateChange(aOldState,aNewState);
+} // TStdLogicDS::dsAfterStateChange
+
+
+/** @deprecated obsolete, replaced by stuff in dsBeforeStateChange()
+// called at very end of sync session, when all map commands are done, too
+// Note: is also called before deleting a datastore (so aborted sessions
+// can do cleanup and/or statistics display as well)
+void TStdLogicDS::endOfSync(bool aRegular)
+{
+ // save new sync anchor now
+ // NOTE: gets called even if not active
+ if (fState!=dss_idle) {
+ PDEBUGPRINTFX(DBG_ADMIN,("TStdLogicDS::endOfSync, %sregular end of sync session",aRegular ? "" : "ir"));
+ if (!aRegular) fRollback=true; // do not write irregular ends
+ // datastore was active in sync, end it now
+ if (!fRollback) {
+ fRollback=!startWrite();
+ if (!fRollback) {
+ fRollback=!SaveAnchor(fNextRemoteAnchor.c_str());
+ }
+ }
+ #ifdef SYDEBUG
+ if (fRollback)
+ DEBUGPRINTFX(DBG_ERROR,("************** Datastore error, rolling back transaction"));
+ #endif
+ // if session is complete, we can't resume it any more, so clear that status now
+ if (!fRollback) {
+ fResumeAlertCode=0; // no resume
+ }
+ // end writing now, sync is done
+ endWrite();
+ }
+ // let ancestor do its things
+ TLocalEngineDS::endOfSync(aRegular);
+} // TStdLogicDS::endOfSync
+
+*/
+
+} // namespace sysync
+
+/* end of TStdLogicDS implementation */
+
+// eof
diff --git a/src/sysync/stdlogicds.h b/src/sysync/stdlogicds.h
new file mode 100755
index 0000000..5271e7e
--- /dev/null
+++ b/src/sysync/stdlogicds.h
@@ -0,0 +1,357 @@
+/**
+ * @File stdlogicds.h
+ *
+ * @Author Lukas Zeller (luz@synthesis.ch)
+ *
+ * @brief TStdLogicDS
+ * Standard database logic implementation, suitable for most (currently all)
+ * actual DS implementations, but takes as few assumptions about datastore
+ * so for vastly different sync patterns, this could be replaced by differnt locic
+ *
+ * Copyright (c) 2001-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ * @Date 2005-09-15 : luz : created from custdbdatastore
+ */
+/*
+ */
+
+#ifndef TStdLogicDS_H
+#define TStdLogicDS_H
+
+// includes
+#include "sysync.h"
+#include "syncappbase.h"
+#include "localengineds.h"
+
+#ifdef MULTI_THREAD_DATASTORE
+#include "platform_thread.h"
+#endif
+
+using namespace sysync;
+
+namespace sysync {
+
+// container for TSyncItem pointers
+typedef std::list<sysync::TSyncItem *> TSyncItemPContainer; // contains data items
+
+
+/// @brief standard logic datastore
+/// - only called directly by TLocalEngineDS via logicXXXX virtuals.
+/// - to perform actual access to implementation, this class calls its (mostly abstract)
+/// implXXXX virtuals.
+class TStdLogicDS: public TLocalEngineDS
+{
+ typedef TLocalEngineDS inherited;
+private:
+ bool fWriteStarted; ///< set if write has started
+ #ifdef SYSYNC_CLIENT
+ bool fEoC; ///< end of changes
+ #ifdef CLIENT_USES_SERVER_DB
+ TSyncItemPContainer fItems; ///< list of data items, used to simulate maps in server DB
+ #endif
+ #else
+ TSyncItemPContainer fItems; ///< list of data items
+ uInt32 fNumRefOnlyItems;
+ #endif
+ // startSync/threading privates
+ bool fInitializing;
+ bool fStartInit;
+ bool fMultiThread; // copied flag from sessionConfig
+
+protected:
+
+ /// @name dsSavedAdmin administrative data (anchors, timestamps, maps) as saved or to-be-saved
+ /// @Note These will be loaded and saved be derived classes
+ /// @Note Some of these will be updated from resp. @ref dsCurrentAdmin members at distinct events (suspend, session end, etc.)
+ /// @Note Some of these will be updated during the session, but in a way that does NOT affect the anchoring of current/last session
+ //
+ /// @{
+ // - TStdLogicDS is timestamp-based, so we save timestamps of the previous session
+ lineartime_t fPreviousSyncTime; ///< time of previous sync (used to generate local anchor string representing previous sync)
+ /// @}
+
+ /// @name dsCurrentAdmin current session's admin data (anchors, timestamps, maps)
+ /// @Note These will be copied to @ref dsSavedAdmin members ONLY when a session completes successfully/suspends.
+ /// @Note Admin data is NEVER directly saved or loaded from these
+ /// @Note Derivates will update some of these at dssta_adminready with current time/anchor values
+ //
+ /// @{
+ // - TStdLogicDS is timestamp-based, so we get timestamp to anchor this session
+ lineartime_t fCurrentSyncTime; ///< anchoring timestamp of this, currently running sync.
+ /// @}
+
+
+private:
+ /// internally reset for re-use without re-creation
+ void InternalResetDataStore(void);
+public:
+ /// constructor
+ TStdLogicDS(
+ TLocalDSConfig *aDSConfigP,
+ sysync::TSyncSession *aSessionP,
+ const char *aName,
+ long aCommonSyncCapMask=0);
+ virtual ~TStdLogicDS();
+
+public:
+ /// @name dsProperty property and state querying methods
+ /// @{
+ /// check is datastore is completely started.
+ /// @param[in] aWait if set, call will not return until either started state is reached
+ /// or cannot be reached within the maximally allowed request processing time left.
+ virtual bool isStarted(bool aWait);
+ /// @}
+
+
+protected:
+ /// @name dsXXXX (usually abstract) virtuals defining the interface to derived datastore classes (implementation, api)
+ /// These are usually designed such that they should always call inherited::dsXXX to let the entire chain
+ /// of ancestors see the calls
+ /// @{
+ //
+ /// reset datastore to a re-usable, like new-created state.
+ virtual void dsResetDataStore(void) { InternalResetDataStore(); inherited::dsResetDataStore(); };
+ /// abort datastore (no reset yet, everything is just frozen as it is)
+ virtual void dsAbortDatastoreSync(TSyError aStatusCode, bool aLocalProblem);
+ /// inform logic of coming state change
+ virtual localstatus dsBeforeStateChange(TLocalEngineDSState aOldState,TLocalEngineDSState aNewState);
+ /// inform logic of happened state change
+ virtual localstatus dsAfterStateChange(TLocalEngineDSState aOldState,TLocalEngineDSState aNewState);
+ #ifndef SYSYNC_CLIENT
+ // - called when a item in the sync set changes its localID (due to local DB internals)
+ // Datastore must make sure that eventually cached items get updated
+ virtual void dsLocalIdHasChanged(const char *aOldID, const char *aNewID);
+ #endif
+ /// @}
+
+ /// @name logicXXXX methods defining the interface to TLocalEngineDS.
+ /// Only these will be called by TLocalEnginDS
+ /// @Note some of these are virtuals ONLY for being derived by superdatastore, NEVER by locic or other derivates
+ /// We use the SUPERDS_VIRTUAL macro for these, which is empty in case we don't have superdatastores, then
+ /// these can be non-virtual.
+ /// @{
+ //
+ /// called to make admin data ready
+ /// - might be called several times (auth retries at beginning of session)
+ virtual localstatus logicMakeAdminReady(cAppCharP aDataStoreURI, cAppCharP aRemoteDBID);
+ /// called to have all non-yet-generated sync commands as "to-be-resumed"
+ virtual void logicMarkOnlyUngeneratedForResume(void);
+ /// called to mark an already generated (but probably not sent or not yet statused) item
+ /// as "to-be-resumed", by localID or remoteID (latter only in server case).
+ /// @note This must be repeatable without side effects, as server must mark/save suspend state
+ /// after every request (and not just at end of session)
+ virtual void logicMarkItemForResume(cAppCharP aLocalID, cAppCharP aRemoteID, bool aUnSent);
+ /// called to mark an already sent item as "to-be-resent", e.g. due to temporary
+ /// error status conditions, by localID or remoteID (latter only in server case).
+ virtual void logicMarkItemForResend(cAppCharP aLocalID, cAppCharP aRemoteID);
+ /// save status information required to eventually perform a resume (as passed to datastore with
+ /// markOnlyUngeneratedForResume(), markItemForResume() and markItemForResend())
+ /// (or, in case the session is really complete, make sure that no resume state is left)
+ /// @note Must also save tempGUIDs (for server) and pending/unconfirmed maps (for client)
+ virtual localstatus logicSaveResumeMarks(void) { return implSaveResumeMarks(); };
+
+ /// called to process incoming item operation
+ /// @note Method must take ownership of syncitemP in all cases
+ virtual bool logicProcessRemoteItem(
+ TSyncItem *syncitemP,
+ TStatusCommand &aStatusCommand,
+ bool &aVisibleInSyncset, ///< on entry: tells if resulting item SHOULD be visible; on exit: set if processed item remains visible in the sync set.
+ string *aGUID=NULL ///< GUID is stored here if not NULL
+ );
+ /// called to read a specified item from the server DB (not restricted to set of conflicting items)
+ virtual bool logicRetrieveItemByID(
+ TSyncItem &aSyncItem, ///< item to be filled with data from server. Local or Remote ID must already be set
+ TStatusCommand &aStatusCommand ///< status, must be set on error or non-200-status
+ );
+
+ /// @}
+
+
+
+ /// @name implXXXX methods defining the interface to TStdLogicDS.
+ /// Only these will be called by TLocalEnginDS
+ /// @Note some of these are virtuals ONLY for being derived by superdatastore, NEVER by locic or other derivates
+ /// We use the SUPERDS_VIRTUAL macro for these, which is empty in case we don't have superdatastores, then
+ /// these can be non-virtual.
+ /// @{
+ //
+ /// save status information required to eventually perform a resume (as passed to datastore with
+ /// markOnlyUngeneratedForResume() and markItemForResume())
+ /// (or, in case the session is really complete, make sure that no resume state is left)
+ /// @note Must also save tempGUIDs (for server) and pending/unconfirmed maps (for client)
+ virtual localstatus implSaveResumeMarks(void) = 0;
+ // - called to have all non-yet-generated sync commands as "to-be-resumed"
+ virtual void implMarkOnlyUngeneratedForResume(void) = 0;
+ /// called to mark an already generated (but probably not sent or not yet statused) item
+ /// as "to-be-resumed", by localID or remoteID (latter only in server case).
+ /// @note This must be repeatable without side effects, as server must mark/save suspend state
+ /// after every request (and not just at end of session)
+ virtual void implMarkItemForResume(cAppCharP aLocalID, cAppCharP aRemoteID, bool aUnSent) = 0;
+ /// called to mark an already sent item as "to-be-resent", e.g. due to temporary
+ /// error status conditions, by localID or remoteID (latter only in server case).
+ virtual void implMarkItemForResend(cAppCharP aLocalID, cAppCharP aRemoteID) = 0;
+ /// sync login (into this database)
+ /// @note might be called several times (auth retries at beginning of session)
+ /// @note must update the following state variables
+ /// - in TLocalEngineDS: fLastRemoteAnchor, fLastLocalAnchor, fResumeAlertCode, fFirstTimeSync
+ /// - for client: fPendingAddMaps
+ /// - for server: fTempGUIDMap
+ /// - in TStdLogicDS: fPreviousSyncTime, fCurrentSyncTime
+ /// - in derived classes: whatever else belongs to dsSavedAdmin and dsCurrentAdmin state
+ virtual localstatus implMakeAdminReady(
+ const char *aDeviceID, ///< @param[in] remote device URI (device ID)
+ const char *aDatabaseID, ///< @param[in] database ID
+ const char *aRemoteDBID ///< @param[in] database ID of remote device
+ ) = 0;
+ /// start data read
+ /// @note: fSlowSync and fRefreshOnly must be valid before calling this method
+ virtual localstatus implStartDataRead() = 0;
+ /// get item from DB
+ virtual localstatus implGetItem(
+ bool &aEof,
+ bool &aChanged,
+ TSyncItem* &aSyncItemP
+ ) = 0;
+ /// end of read
+ virtual localstatus implEndDataRead(void) = 0;
+ /// start of write
+ virtual localstatus implStartDataWrite(void) = 0;
+ /// review reported entry (allows post-processing such as map deleting)
+ /// MUST be called after implStartDataWrite, before any actual writing,
+ /// for each item obtained in implGetItem
+ virtual localstatus implReviewReadItem(
+ TSyncItem &aItem // the item
+ ) = 0;
+ #ifndef SYSYNC_CLIENT
+ /// called to set maps.
+ /// @note aLocalID or aRemoteID can be NULL - which signifies deletion of a map entry
+ /// @note that this might be needed for clients accessing a server-style database as well
+ virtual localstatus implProcessMap(cAppCharP aRemoteID, cAppCharP aLocalID) = 0;
+ #endif
+ /// called to read a specified item from the server DB (not restricted to set of conflicting items)
+ virtual bool implRetrieveItemByID(
+ TSyncItem &aSyncItem, ///< item to be filled with data from server. Local or Remote ID must already be set
+ TStatusCommand &aStatusCommand ///< status, must be set on error or non-200-status
+ ) = 0;
+ /// process item (according to operation: add/delete/replace/map)
+ virtual bool implProcessItem(
+ TSyncItem *aItemP, ///< the item
+ TStatusCommand &aStatusCommand
+ ) = 0;
+ /// save end of session state
+ virtual localstatus implSaveEndOfSession(bool aUpdateAnchors) = 0;
+ /// end write sequence
+ virtual bool implEndDataWrite(void) = 0;
+ /// @}
+
+
+private:
+ /// @name dsHelpers
+ /// internal, private helper methods
+
+ #ifdef SYSYNC_CLIENT
+ /// called by dsBeforeStateChange to dssta_dataaccessstarted to make sure datastore is ready for being accessed.
+ virtual localstatus startDataAccessForClient(void);
+ #else
+ /// called by dsBeforeStateChange to dssta_dataaccessstarted to make sure datastore is ready for being accessed.
+ virtual localstatus startDataAccessForServer(void);
+ #endif
+
+ /// @}
+
+
+
+ #ifndef SYSYNC_CLIENT
+ // - called to check if conflicting replace or delete command from server exists
+ virtual TSyncItem *getConflictingItemByRemoteID(TSyncItem *syncitemP);
+ // - called to check if content-matching item from server exists
+ virtual TSyncItem *getMatchingItem(TSyncItem *syncitemP, TEqualityMode aEqMode);
+ // - called to prevent item to be sent to client in subsequent logicGenerateSyncCommandsAsServer()
+ // item in question should be an item that was returned by getConflictingItemByRemoteID() or getMatchingItem()
+ virtual void dontSendItemAsServer(TSyncItem *syncitemP);
+ // - called to have additional item sent to remote (DB takes ownership of item)
+ virtual void SendItemAsServer(TSyncItem *aSyncitemP);
+ // - end map operation (rollback if not aDoCommit)
+ virtual bool MapFinishAsServer(
+ bool aDoCommit, // if not set, entire map operation must be undone
+ TStatusCommand &aStatusCommand // status, must be set on error or non-200-status
+ );
+ // - called for SyncML 1.1 if remote wants number of changes.
+ // Must return -1 no NOC value can be returned
+ // NOTE: we implement it here only for server, as it is not really needed
+ // for clients normally - if it is needed, client's agent must provide
+ // it as CustDBDatastore has no own list it can use to count in client case.
+ virtual sInt32 getNumberOfChanges(void);
+ /// called to generate sync sub-commands as client for remote server
+ /// @return true if now finished for this datastore
+ virtual bool logicGenerateSyncCommandsAsServer(
+ TSmlCommandPContainer &aNextMessageCommands,
+ TSmlCommand * &aInterruptedCommandP,
+ const char *aLocalIDPrefix
+ );
+ /// called for servers when receiving map from client
+ /// @note aLocalID or aRemoteID can be NULL - which signifies deletion of a map entry
+ virtual localstatus logicProcessMap(cAppCharP aLocalID, cAppCharP aRemoteID);
+ #else
+ /// called to generate sync sub-commands as server for remote client
+ /// @return true if now finished for this datastore
+ virtual bool logicGenerateSyncCommandsAsClient(
+ TSmlCommandPContainer &aNextMessageCommands,
+ TSmlCommand * &aInterruptedCommandP,
+ const char *aLocalIDPrefix
+ );
+ #endif
+ /** @deprecated obsolete, replaced by stuff in dsBeforeStateChange()
+ // - called at very end of sync session, when everything is done
+ // Note: is also called before deleting a datastore (so aborted sessions
+ // can do cleanup and/or statistics display as well)
+ virtual void endOfSync(bool aRegular);
+ */
+ // - determine if this is a first time sync situation
+ virtual bool isFirstTimeSync(void) { return fFirstTimeSync; };
+
+public:
+ // Simple custom DB access interface methods
+ // - returns true if database implementation can only update all fields of a record at once
+ virtual bool dsReplaceWritesAllDBFields(void) { return false; } // we assume DB is smart enough
+ #ifdef OBJECT_FILTERING
+ // - returns true if DB implementation can filter during database fetch
+ // (otherwise, fetched items must be filtered after being read from DB)
+ virtual bool dsFilteredFetchesFromDB(bool aFilterChanged=false) { return false; } // assume unfiltered data from DB
+ #endif
+
+private:
+ /// internal stdlogic: start writing if not already started
+ localstatus startDataWrite(void);
+ /// internal stdlogic: end writing if not already ended
+ localstatus endDataWrite(void);
+public:
+ // - must be called before starting a thread. If returns false, starting a thread now
+ // is not allowed and must be postponed.
+ virtual bool startingThread(void) { return true; };
+ // - must be called when a thread's activity has ended
+ // BUT THE CALL MUST BE FROM THE ENDING THREAD, not the main thread!
+ virtual void endingThread(void) {};
+ // - should be called before doing DB accesses that might be locked (e.g. because another thread is using the DB resources)
+ virtual bool dbAccessLocked(void) { return false; };
+ // - Actual start sync actions in DB. If server supports threaded init, this will
+ // be called in a sub-thread's context
+ localstatus performStartSync(void);
+ #ifdef MULTI_THREAD_DATASTORE
+ TStatusCommand fStartSyncStatus; // a thread-private status command to store status ocurring during threaded startSync()
+ #endif
+private:
+ // - can be called to check if performStartSync() should be terminated
+ bool shouldExitStartSync(void);
+ #ifdef MULTI_THREAD_DATASTORE
+ bool threadedStartSync(void);
+ TThreadObject fStartSyncThread; // the wrapper object for the startSync thread
+ #endif
+}; // TStdLogicDS
+
+
+} // namespace sysync
+
+#endif // TStdLogicDS_H
+
+// eof
diff --git a/src/sysync/stringutils.cpp b/src/sysync/stringutils.cpp
new file mode 100755
index 0000000..d3cc1a6
--- /dev/null
+++ b/src/sysync/stringutils.cpp
@@ -0,0 +1,856 @@
+/*
+ * File: stringutils.cpp
+ *
+ * Authors: Lukas Zeller (luz@synthesis.ch)
+ * Beat Forster (bfo@synthesis.ch)
+ *
+ * C++ string utils
+ *
+ * Copyright (c) 2001-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ * 2002-05-01 : luz : extracted from sysync_utils
+ */
+
+#include "prefix_file.h"
+#include "stringutils.h"
+
+// Consistent support for Linux, MacOSX CW & XCode
+#if defined __GNUC__ || defined _MSC_VER
+ #include <ctype.h>
+#else
+ #include <ctype>
+#endif
+
+#include <cstdio>
+#include <cstring>
+#include <stdarg.h>
+
+using namespace std;
+
+namespace sysync {
+
+
+
+#ifdef NO_VSNPRINTF
+// dummy (unsafe!!) map to vsprintf
+int vsnprintf(char *s, int sz, const char *formatStr, va_list arg)
+{
+ return vsprintf(s,formatStr,arg);
+} // vsnprintf
+#endif
+
+#ifdef NO_SNPRINTF
+// own implementation (eventually unsafe, if vsnprintf is dummy)
+int snprintf(char *s, int sz, const char *formatStr, ...)
+{
+ va_list args;
+
+ va_start(args, formatStr);
+ // now make the string
+ int n=vsnprintf(s,sz,formatStr,args);
+ va_end(args);
+ return n;
+} // snprintf
+#endif
+
+
+/// @brief Return passed pointer as C string, if pointer is NULL, empty string is returned
+/// @param[in] aStr a C string or NULL
+/// @return if aStr is NULL, returns empty string, aStr otherwise
+const char *alwaysCStr(const char *aStr)
+{
+ return aStr ? aStr : "";
+} // alwaysCStr
+
+
+// Assign char ptr to string object
+// NOTE: NULL is allowed and means empty string
+void AssignString(string &aString, const char *aCharP)
+{
+ if (aCharP)
+ aString=aCharP;
+ else
+ aString.erase();
+} // AssignString
+
+
+// Assign char ptr to char array of size aLen (note that we always leave room for terminator)
+// NOTE: NULL is allowed and means empty string
+void AssignCString(char *aCStr, const char *aCharP, size_t aLen)
+{
+ if (aCharP) {
+ strncpy(aCStr,aCharP,aLen-1);
+ aCStr[aLen-1]=0; // make sure it is terminated
+ }
+ else
+ aCStr[0]=0;
+} // AssignCString
+
+
+// trim leading spaces/ctrls and everything including and after the next space/ctrl
+void AssignTrimmedCString(char *aCStr, const char *aCharP, size_t aLen)
+{
+ if (aCharP) {
+ // not empty, find first non-control/space
+ while (*aCharP && (uInt8)(*aCharP)<=0x20) aCharP++;
+ // now find end
+ const char *e = aCharP;
+ while (*e && (uInt8)(*e)>0x20) e++;
+ // copy valid part of URL
+ size_t n=e-aCharP;
+ if (n>aLen-1) n=aLen-1; // limit to max buffer size
+ strncpy(aCStr,aCharP,n);
+ aCStr[n]=0; // make sure it is terminated
+ }
+ else
+ aCStr[0]=0;
+} // AssignTrimmedCString
+
+
+
+// save mangled into C-string buffer
+uInt16 assignMangledToCString(char *aCString, const char *aCode, uInt16 aMaxBytes, bool aIsName, uInt8 aMangleInc, const char *aSecondKey)
+{
+ uInt8 mangler=aMangleInc;
+ uInt16 b,bytes=0;
+ uInt8 c;
+ const char *kp = aSecondKey && *aSecondKey ? aSecondKey : NULL; // need at least once char of key
+ while(aCode && *aCode && bytes<aMaxBytes-1) {
+ c = *(aCode++);
+ if ((aIsName && (c>=0x20)) || (!aIsName && isalnum(c))) {
+ // save only visible chars and spaces from name and alphanums from code
+ *(aCString++)=
+ c ^ mangler ^ (kp ? *(kp++) : 0); // add mangled
+ if (kp && *kp==0) kp=aSecondKey; // wrap around
+ mangler+=aMangleInc;
+ bytes++;
+ }
+ }
+ // add terminator in all cases
+ *aCString++=mangler ^ (kp ? *(kp++) : 0);
+ if (kp && *kp==0) kp=aSecondKey; // wrap around
+ bytes++;
+ // now add some garbage to disguise length of actual data
+ b=bytes;
+ while (b<aMaxBytes) {
+ *aCString++ = b++ ^ mangler;
+ mangler+=aMangleInc;
+ }
+ // return actual number of bytes (without disguising garbage)
+ return bytes;
+} // assignMangledToCString
+
+
+// get unmangled into string
+void getUnmangled(string &aString, const char *aMangled, uInt16 aMaxChars, uInt8 aMangleInc, const char *aSecondKey)
+{
+ uInt8 mangler=aMangleInc;
+ uInt8 c;
+ const char *kp = aSecondKey && *aSecondKey ? aSecondKey : NULL; // need at least once char of key
+ aString.erase();
+ while (aMaxChars-- > 0 && aMangled && (c=((uInt8)(*aMangled) ^ mangler ^ (kp ? *(kp++) : 0)))) {
+ aString += (char)c;
+ if (kp && *kp==0) kp=aSecondKey; // wrap around
+ aMangled++;
+ mangler+=aMangleInc;
+ }
+} // getUnmangled
+
+
+// get unmangled into buffer
+void getUnmangledAsBuf(appCharP aBuffer, uInt16 aBufSize, cAppCharP aMangled, uInt8 aMangleInc, cAppCharP aSecondKey)
+{
+ uInt8 mangler=aMangleInc;
+ uInt8 c;
+ if (!aBuffer || aBufSize<1) return; // no buffer
+ const char *kp = aSecondKey && *aSecondKey ? aSecondKey : NULL; // need at least once char of key
+ aBuffer[--aBufSize]=0; // ultimate security terminator
+ while (aBufSize-- > 0 && aMangled && (c=((uInt8)(*aMangled) ^ mangler ^ (kp ? *(kp++) : 0)))) {
+ *(aBuffer++) = (char)c;
+ if (kp && *kp==0) kp=aSecondKey; // wrap around
+ aMangled++;
+ mangler+=aMangleInc;
+ }
+ *(aBuffer) = 0; // terminate
+} // getUnmangled
+
+
+
+// case insensitive and whitespace trimming strcmp, NULL allowed as empty string input
+sInt16 strutrimcmp(const char *s1, const char *s2)
+{
+ // skip whitespace on both strings first
+ while (s1 && *s1 && isspace(*s1)) ++s1;
+ while (s2 && *s2 && isspace(*s2)) ++s2;
+ // count number of non-whitespaces
+ sInt32 n1=0; while (s1 && s1[n1] && !isspace(s1[n1])) ++n1;
+ sInt32 n2=0; while (s2 && s2[n2] && !isspace(s2[n2])) ++n2;
+ // now compare
+ return strucmp(s1,s2,n1,n2);
+} // strutrimcmp
+
+
+// case insensitive strcmp which allow wildcards ? and * in s2, NULL allowed as empty string input
+sInt16 strwildcmp(const char *s1, const char *s2, size_t len1, size_t len2)
+{
+ // allow NULL as empty strings
+ if (!s1) s1 = "";
+ if (!s2) s2 = "";
+ // s1>s2 : 1, s1==s2 : 0, s1<s2 : -1
+ size_t i,j; // size_t instead of sInt32: BCPPB
+ // calc number of chars we must compare
+ size_t len = len1==0 ? len2 : (len2==0 ? len1 : (len1>len2 ? len2 : len1));
+ for (i=0; (!len || i<len) && *s1 && *s2; i++) {
+ // while both strings have chars and not len reached
+ if (*s2=='*') {
+ // zero to arbitrary number of chars
+ s2++;
+ j=0;
+ if (*s2==0) return 0; // if asterisk is last in pattern, match is complete
+ while (*s1) {
+ // more pattern follows, recurse to compare the rest
+ if (strwildcmp(s1,s2,len1 ? len1-i-j : 0,len2 ? len2-i-1 : 0)==0) {
+ // rest matches now -> full match
+ return 0;
+ }
+ // next attempt
+ s1++;
+ j++;
+ }
+ // no match if we get this far
+ return 1;
+ }
+ else if (*s2!='?' && toupper(*s1)!=toupper(*s2))
+ return toupper(*s1)>toupper(*s2) ? 1 : -1; // different and no wildcard in pattern at this place
+ // next
+ s1++;
+ s2++;
+ }
+ // equal up to end of shorter string or reached len
+ // - if both reached end or len
+ if (len1 ? i==len1 : *s1==0) {
+ // s1 has reached end, check special case that pattern ends with *, this would be ok
+ if ((len==0 || i<len2) && *s2=='*') { s2++; i++; } // simply consume asterisk, if s2 is now at end as well, that's ok
+ if (len2 ? i==len2 : *s2==0) return 0; // match only if s2 is now at end as well
+ }
+ // - not equal, longer string is larger
+ // (if not reached end of s1 or stopped before len1, s1 is longer)
+ return (len1 ? i<len1 : *s1) ? 1 : -1;
+} // strwildcmp
+
+
+#ifdef SYNTHESIS_UNIT_TEST
+bool test_strwildcmp(void)
+{
+ bool ok=true;
+ int i;
+ UNIT_TEST_CALL(i=strwildcmp("http://test.com/test","http*://test.com/test"),("i=%d",i),i==0,ok);
+ UNIT_TEST_CALL(i=strwildcmp("https://test.com/test","http*://test.com/test"),("i=%d",i),i==0,ok);
+ UNIT_TEST_CALL(i=strwildcmp("htt://test.com/test","http*://test.com/test"),("i=%d",i),i!=0,ok);
+ UNIT_TEST_CALL(i=strwildcmp("http://test.com/test/10000","http*://test.com/test*"),("i=%d",i),i==0,ok);
+ UNIT_TEST_CALL(i=strwildcmp("http://test.com/test","http*://test.com/test*"),("i=%d",i),i==0,ok);
+ UNIT_TEST_CALL(i=strwildcmp("realGARBAGE","real"),("i=%d",i),i!=0,ok);
+ UNIT_TEST_CALL(i=strwildcmp("realGARBAGE","real",4),("i=%d",i),i==0,ok);
+ UNIT_TEST_CALL(i=strwildcmp("realGARBAGE","realTRASH",4,4),("i=%d",i),i==0,ok);
+ UNIT_TEST_CALL(i=strwildcmp("one_two_two2_four_five","one*two2*five"),("i=%d",i),i==0,ok);
+ UNIT_TEST_CALL(i=strwildcmp("one_two_two2_four_five","one*two2*five",12),("i=%d",i),i!=0,ok);
+ UNIT_TEST_CALL(i=strwildcmp("one_two_two2_four_five","one*two2*five",12,8),("i=%d",i),i==0,ok);
+ UNIT_TEST_CALL(i=strwildcmp("one_two_two2_four_five","one*two2*five",12,9),("i=%d",i),i==0,ok);
+ UNIT_TEST_CALL(i=strwildcmp("one_two_two2_four_five","one*two2*five",12,10),("i=%d",i),i!=0,ok);
+ UNIT_TEST_CALL(i=strwildcmp("P800","P?00"),("i=%d",i),i==0,ok);
+ UNIT_TEST_CALL(i=strwildcmp("P810","P?00"),("i=%d",i),i!=0,ok);
+ return ok;
+}
+#endif
+
+
+// case insensitive strpos, returns -1 if not found
+sInt32 strupos(const char *s, const char *pat, size_t slen, size_t patlen)
+{
+ size_t i,k;
+ for (i=0; (!slen || i<slen) && s && *s; i++) {
+ for (k=0; (!patlen || k<patlen) && pat && *pat; k++) {
+ if (*(s+k)==0 || (slen && i+k>=slen))
+ return -1; // pattern too long to possibly match at all
+ if (toupper(*(s+k))!=toupper(*pat))
+ break; // no match at this position
+ pat++;
+ }
+ if (*pat==0 || (patlen && k>=patlen))
+ return i; // pattern end reached and all matches so far -> found string here
+ // try next position
+ s++;
+ }
+ return -1; // not found in any position
+} // strupos
+
+
+// case insensitive strcmp, NULL allowed as empty string input
+sInt16 strucmp(const char *s1, const char *s2, size_t len1, size_t len2)
+{
+ // allow NULL as empty strings
+ if (!s1) s1 = "";
+ if (!s2) s2 = "";
+ // s1>s2 : 1, s1==s2 : 0, s1<s2 : -1
+ size_t i;
+ // calc number of chars we must compare
+ size_t len = len1==0 ? len2 : (len2==0 ? len1 : (len1>len2 ? len2 : len1));
+ for (i=0; (!len || i<len) && *s1 && *s2; i++) {
+ // while both strings have chars and not len reached
+ if (toupper(*s1)!=toupper(*s2))
+ return toupper(*s1)>toupper(*s2) ? 1 : -1; // different
+ // next
+ s1++;
+ s2++;
+ }
+ // equal up to end of shorter string or reached len
+ // - if both reached end or len -> equal
+ if ( ((len1 ? i==len1 : false) || *s1==0) && ((len2 ? i==len2 : false) || *s2==0) ) return 0;
+ // - not equal, longer string is larger
+ // (if not reached end of s1 or stopped before len1, s1 is longer
+ // but note than len1 can be longer than actual length of s1, so we
+ // must check for *s1 to make sure we have really not reached end of s1)
+ return (len1 ? i<len1 && *s1 : *s1) ? 1 : -1;
+} // strucmp
+
+
+// byte by byte strcmp, NULL allowed as empty string input
+// Note: is compatible with standard strncmp if len2 is left unspecified
+sInt16 strnncmp(const char *s1, const char *s2, size_t len1, size_t len2)
+{
+ // allow NULL as empty strings
+ if (!s1) s1 = "";
+ if (!s2) s2 = "";
+ // s1>s2 : 1, s1==s2 : 0, s1<s2 : -1
+ size_t i; // size_t instead of sInt32: BCPPB
+ // calc number of chars we must compare
+ size_t len = len1==0 ? len2 : (len2==0 ? len1 : (len1>len2 ? len2 : len1));
+ for (i=0; (!len || i<len) && *s1 && *s2; i++) {
+ // while both strings have chars and not len reached
+ if (*s1!=*s2)
+ return *s1>*s2 ? 1 : -1; // different
+ // next
+ s1++;
+ s2++;
+ }
+ // equal up to end of shorter string or reached len
+ // - if both reached end or len
+ if ( ((len1 ? i==len1 : false) || *s1==0) && ((len2 ? i==len2 : false) || *s2==0) ) return 0;
+ // - not equal, longer string is larger
+ // (if not reached end of s1 or stopped before len1, s1 is longer
+ // but note than len1 can be longer than actual length of s1, so we
+ // must check for *s1 to make sure we have really not reached end of s1)
+ return (len1 ? i<len1 && *s1 : *s1) ? 1 : -1;
+} // strnncmp
+
+
+// parser for tag="value" type string format
+cAppCharP nextTag(cAppCharP aTagString, string &aTag, string &aTagValue)
+{
+ size_t n;
+ if (!aTagString) return NULL;
+ // find start of next tag
+ while (*aTagString==' ' || *aTagString==',') aTagString++;
+ // find end of tag
+ cAppCharP p = strchr(aTagString,'=');
+ if (!p) return NULL; // no more tags
+ n=p-aTagString; // size of tag
+ // save tag name
+ aTag.assign(aTagString,n);
+ // check for value
+ aTagValue.erase(); // default to none
+ p++; // skip '='
+ if (*p==0) return NULL; // no more tags
+ // must start with single or double quote
+ char q=*p; // quote char
+ if (q!=0x22 && q!='\'') return p; // no value, next tag follows immediately
+ p++; // skip starting quote
+ while (*p) {
+ if (*p=='\\') {
+ // escape next char
+ p++;
+ if (*p==0) break; // no next char, string ends
+ }
+ else if (*p==q) {
+ // end of string (unescaped closing quote)
+ p++; // skip closing quote
+ break;
+ }
+ // just take char as it is
+ aTagValue+=*p++;
+ }
+ if (*p==';') p++; // skip semicolon if there is one
+ if (*p==0) return NULL; // end of string, no more tags
+ // p is now starting point for next tag
+ return p;
+} // nextTag
+
+
+// direct conversion of value (usually from nextTag()) into uInt32
+uInt32 uint32Val(cAppCharP aValue)
+{
+ uInt32 res=0;
+ StrToULong(aValue, res);
+ return res;
+} // uint32Val
+
+
+// direct conversion of value (usually from nextTag()) into uInt16
+uInt16 uint16Val(cAppCharP aValue)
+{
+ uInt16 res=0;
+ StrToUShort(aValue, res);
+ return res;
+} // uint16val
+
+
+// direct conversion of value (usually from nextTag()) into bool
+bool boolVal(cAppCharP aValue)
+{
+ bool res=false;
+ StrToBool(aValue, res);
+ return res;
+} // boolVal
+
+
+
+// returns true on successful conversion of string to bool
+bool StrToBool(const char *aStr, bool &aBool)
+{
+ if (
+ strutrimcmp(aStr,"yes")==0 ||
+ strutrimcmp(aStr,"true")==0 ||
+ strutrimcmp(aStr,"1")==0 ||
+ strutrimcmp(aStr,"on")==0
+ ) {
+ aBool=true;
+ return true;
+ }
+ else if (
+ strutrimcmp(aStr,"no")==0 ||
+ strutrimcmp(aStr,"false")==0 ||
+ strutrimcmp(aStr,"0")==0 ||
+ strutrimcmp(aStr,"off")==0
+ ) {
+ aBool=false;
+ return true;
+ }
+ // error
+ return false;
+} // StrToBool
+
+
+const char *tristateString(sInt8 aTristate)
+{
+ if (aTristate<0) return "default";
+ else if (aTristate>0) return "Yes";
+ else return "No";
+}
+
+
+const char *boolString(bool aBool)
+{
+ if (aBool) return "Yes";
+ else return "No";
+}
+
+
+
+
+// returns true on successful conversion of string to enum
+bool StrToEnum(const char * const aEnumNames[], sInt16 aNumEnums, sInt16 &aShort, const char *aStr, sInt16 aLen)
+{
+ const char *e;
+ for (sInt16 k=0; k<aNumEnums; k++) {
+ e=aEnumNames[k];
+ if (!e) continue; // NULL entries are allowed in aEnumNames but will be skipped
+ if (strucmp(aStr,e,aLen)==0) {
+ aShort=k;
+ return true;
+ }
+ }
+ return false; // not found
+} // StrToEnum
+
+
+// makes hex char out of nibble
+char NibbleToHexDigit(uInt8 aNibble)
+{
+ aNibble &= 0x0F;
+ if (aNibble>9) return 'A'-0x0A+aNibble;
+ else return '0'+aNibble;
+} // NibbleToHexDigit
+
+
+// returns number of successfully converted chars
+sInt16 HexStrToUShort(const char *aStr, uInt16 &aShort, sInt16 aMaxDigits)
+{
+ // our own implementation
+ char c;
+ sInt16 n=0;
+ aShort=0;
+ while (aStr && (c=*aStr++) && (n<aMaxDigits)) {
+ if (!isxdigit(c)) break;
+ aShort<<=4;
+ aShort+=(toupper(c)-0x30);
+ if (!isdigit(c)) aShort-=7;
+ n++;
+ }
+ return n;
+} // HexStrToUShort
+
+
+// returns number of successfully converted chars
+sInt16 HexStrToULong(const char *aStr, uInt32 &aLong, sInt16 aMaxDigits)
+{
+ // our own implementation
+ char c;
+ sInt16 n=0;
+ aLong=0;
+
+ while (aStr && (c=*aStr++) && (n<aMaxDigits)) {
+ if (!isxdigit(c)) break;
+ aLong*= 16;
+ aLong+=(toupper(c)-0x30);
+ if (!isdigit(c)) aLong-=7;
+ n++;
+ } // while
+
+ return n;
+} // HexStrToULong
+
+
+// returns number of successfully converted chars
+sInt16 HexStrToULongLong(const char *aStr, uInt64 &aLongLong, sInt16 aMaxDigits)
+{
+ // our own implementation
+ char c;
+ sInt16 n=0;
+ aLongLong=0;
+
+ while (aStr && (c=*aStr++) && (n<aMaxDigits)) {
+ if (!isxdigit(c)) break;
+ aLongLong*= 16;
+ aLongLong+=(toupper(c)-0x30);
+ if (!isdigit(c)) aLongLong-=7;
+ n++;
+ } // while
+
+ return n;
+} // HexStrToULongLong
+
+sInt16 HexStrToUIntPtr(const char *aStr, uIntPtr &aIntPtr, sInt16 aMaxDigits)
+{
+#if __WORDSIZE == 64
+ return HexStrToULongLong(aStr, aIntPtr, aMaxDigits);
+#else
+ return HexStrToULong(aStr, aIntPtr, aMaxDigits);
+#endif
+} // HexStrToUIntPtr
+
+// returns number of successfully converted chars
+sInt16 StrToUShort(const char *aStr, uInt16 &aShort, sInt16 aMaxDigits)
+{
+ // our own implementation
+ char c;
+ sInt16 n=0;
+ aShort=0;
+ while (aStr && (c=*aStr++) && (n<aMaxDigits)) {
+ if (!isdigit(c)) break;
+ aShort*=10;
+ aShort+=(c-0x30);
+ n++;
+ }
+ return n;
+} // StrToUShort
+
+
+// returns number of successfully converted chars
+sInt16 StrToULong(const char *aStr, uInt32 &aLong, sInt16 aMaxDigits)
+{
+ // our own implementation
+ char c;
+ sInt16 n=0;
+ aLong=0;
+ while (aStr && (c=*aStr++) && (n<aMaxDigits)) {
+ if (!isdigit(c)) break;
+ aLong*=10l;
+ aLong+=(c-0x30);
+ n++;
+ }
+ return n;
+} // StrToULong
+
+
+// returns number of successfully converted chars
+sInt16 StrToULongLong(const char *aStr, uInt64 &aLongLong, sInt16 aMaxDigits)
+{
+ // our own implementation
+ char c;
+ sInt16 n=0;
+ aLongLong=0;
+ while (aStr && (c=*aStr++) && (n<aMaxDigits)) {
+ if (!isdigit(c)) break;
+ aLongLong*=10l;
+ aLongLong+=(c-0x30);
+ n++;
+ }
+ return n;
+} // StrToULongLong
+
+
+// helper
+static bool isMinus(const char *&aStr, sInt16 &aNumRead)
+{
+ if (!aStr) return false; // not minus
+ if (*aStr=='-') {
+ aStr++;
+ aNumRead++;
+ return true; // is minus
+ }
+ else if (*aStr=='+') {
+ aStr++;
+ aNumRead++;
+ }
+ // is plus
+ return false;
+} // isMinus
+
+
+// returns number of successfully converted chars
+sInt16 StrToShort(const char *aStr, sInt16 &aShort, sInt16 aMaxDigits)
+{
+ // our own implementation
+ uInt16 temp;
+ sInt16 n=0;
+ bool neg=isMinus(aStr,n);
+ n+=StrToUShort(aStr,temp,aMaxDigits-n);
+ if (neg) aShort=-temp;
+ else aShort=temp;
+ return n;
+} // StrToShort
+
+
+// returns number of successfully converted chars
+sInt16 StrToLong(const char *aStr, sInt32 &aLong, sInt16 aMaxDigits)
+{
+ // our own implementation
+ uInt32 temp;
+ sInt16 n=0;
+ bool neg=isMinus(aStr,n);
+ n+=StrToULong(aStr,temp,aMaxDigits-n);
+ if (neg) aLong=-(sInt32)temp;
+ else aLong=temp;
+ return n;
+} // StrToLong
+
+
+// returns number of successfully converted chars
+sInt16 StrToLongLong(const char *aStr, sInt64 &aLongLong, sInt16 aMaxDigits)
+{
+ // our own implementation
+ uInt64 temp;
+ sInt16 n=0;
+ bool neg=isMinus(aStr,n);
+ n+=StrToULongLong(aStr,temp,aMaxDigits-n);
+ if (neg) aLongLong=-(sInt64)temp;
+ else aLongLong=temp;
+ return n;
+} // StrToLongLong
+
+
+#ifndef NO_FLOATS
+
+// returns number of successfully converted chars
+sInt16 StrToDouble(const char *aStr, double &aDouble, sInt16 aMaxDigits)
+{
+ // our own implementation
+ char c;
+ sInt16 n=0;
+ bool neg=isMinus(aStr,n);
+ sInt64 fraction=0;
+ aDouble=0;
+ while (aStr && (c=*aStr++) && (n<aMaxDigits)) {
+ if (!isdigit(c)) {
+ if (fraction || c!='.') break;
+ fraction=10; // first digit after decimal point is 1/10
+ n++;
+ continue; // next
+ }
+ c-=0x30; // make 0..9
+ if (fraction) {
+ // within fractional part
+ aDouble+=(double)c/fraction;
+ fraction*=10;
+ }
+ else {
+ // integer part
+ aDouble*=10.0;
+ aDouble+=c;
+ }
+ n++;
+ }
+ if (neg) aDouble=-aDouble;
+ return n;
+} // StrToDouble
+
+#endif // NO_FLOATS
+
+
+// returns number of C-string-escaped chars successfully converted to string
+sInt16 CStrToStrAppend(const char *aStr, string &aString, bool aStopAtQuoteOrCtrl, char ignore)
+{
+ unsigned char c;
+ const char *p=aStr;
+ while ((c=*p++)) {
+ // check for escape
+ if (c=='\\') {
+ // escape, check next
+ c=*p++;
+ if (!c) break; // unfinished escape sequence is ignored
+ switch (c) {
+ case 't' : c=0x09; break; // TAB
+ case 'n' : c=0x0A; break; // line feed
+ case 'r' : c=0x0D; break; // carriage return
+ case '\r':
+ case '\n':
+ // continued line, swallow line end
+ c=0;
+ break;
+ case 'x' :
+ // hex char
+ c=0; uInt16 n=0;
+ while (isxdigit(*p) && n<2) {
+ c=(c<<4) | (*p>'9' ? toupper(*p)-'A'+0x0A : *p-'0');
+ p++; n++;
+ }
+ break;
+ }
+ // c is the char to add
+ }
+ else if (aStopAtQuoteOrCtrl && (c=='"' || c<0x20)) {
+ // terminating char is NOT consumed
+ p--;
+ break; // stop here
+ }
+ // otherwise, ignore any control characters
+ else if (c<0x20 && c!=ignore)
+ continue;
+ // add it to the result
+ if (c) aString+=c;
+ }
+ // return number of converted chars
+ return p-aStr;
+} // CStrToStrAppend
+
+
+
+// add a hex byte to a string
+// Note: this is required because PalmOS sprintf does not correctly
+// work with %02X (always writes 4 digits!)
+void AppendHexByte(string &aString, uInt8 aByte)
+{
+ aString += NibbleToHexDigit(aByte>>4);
+ aString += NibbleToHexDigit(aByte);
+} // AppendHexByte
+
+
+// returns number of C-string-escaped chars successfully converted to string
+sInt16 StrToCStrAppend(const char *aStr, string &aString, bool aAllow8Bit, char ignore)
+{
+ unsigned char c;
+ const char *p=aStr;
+ while ((c=*p++)) {
+ // check for specials
+ if (c==ignore) aString+= c;
+ else if (c==0x09) aString+="\\t";
+ else if (c==0x0A) aString+="\\n";
+ else if (c==0x0D) aString+="\\r";
+ else if (c==0x22) aString+="\\\"";
+ else if (c=='\\') aString+="\\\\"; // escape the backslash as well
+ else if (c<0x20 || c==0x7F || (!aAllow8Bit && c>=0x80)) {
+ aString += "\\x";
+ AppendHexByte(aString,c);
+ }
+ else {
+ // as is
+ aString+=c;
+ }
+ }
+ // return number of converted chars
+ return p-aStr;
+} // CStrToStrAppend
+
+
+
+// old-style C-formatted output into string object
+void vStringObjPrintf(string &aStringObj, const char *aFormat, bool aAppend, va_list aArgs)
+{
+ #ifndef NO_VSNPRINTF
+ const size_t bufsiz=128;
+ #else
+ const size_t bufsiz=2048;
+ #endif
+ size_t actualsize;
+ char buf[bufsiz];
+
+ buf[0]='\0';
+ char *bufP = NULL;
+ if (!aAppend) aStringObj.erase();
+ #ifndef NO_VSNPRINTF
+ // using aArgs in vsnprintf() is destructive, need a copy in
+ // case we call the function a second time
+ va_list args;
+ va_copy(args, aArgs);
+ #endif
+ actualsize = vsnprintf(buf, bufsiz, aFormat, aArgs);
+ #ifndef NO_VSNPRINTF
+ if (actualsize>=bufsiz) {
+ // default buffer was too small, create bigger dynamic buffer
+ bufP = new char[actualsize+1];
+ actualsize = vsnprintf(bufP, actualsize+1, aFormat, args);
+ if (actualsize>0) {
+ aStringObj += bufP;
+ }
+ delete [] bufP;
+ }
+ else
+ #endif
+ {
+ // small default buffer was big enough, add it
+ if (actualsize<0) return; // abort, error
+ aStringObj += buf;
+ }
+ #ifndef NO_VSNPRINTF
+ va_end(args);
+ #endif
+} // vStringObjPrintf
+
+
+// old-style C-formatted output into string object
+void StringObjPrintf(string &aStringObj, const char *aFormat, ...)
+{
+ va_list args;
+
+ va_start(args, aFormat);
+ // now make the string
+ vStringObjPrintf(aStringObj,aFormat,false,args);
+ va_end(args);
+} // StringObjPrintf
+
+
+// old-style C-formatted output appending into string object
+void StringObjAppendPrintf(string &aStringObj, const char *aFormat, ...)
+{
+ va_list args;
+
+ va_start(args, aFormat);
+ // now make the string
+ vStringObjPrintf(aStringObj,aFormat,true,args);
+ va_end(args);
+} // StringObjAppendPrintf
+
+} // namespace sysync
+
+/* eof */
+
diff --git a/src/sysync/stringutils.h b/src/sysync/stringutils.h
new file mode 100755
index 0000000..b581939
--- /dev/null
+++ b/src/sysync/stringutils.h
@@ -0,0 +1,160 @@
+/*
+ * File: stringutils.h
+ *
+ * Authors: Lukas Zeller (luz@synthesis.ch)
+ * Beat Forster (bfo@synthesis.ch)
+ *
+ * C++ string utils
+ *
+ * Copyright (c) 2001-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ * 2002-05-01 : luz : extracted from sysync_utils
+ */
+
+#ifndef STRINGUTILS_H
+#define STRINGUTILS_H
+
+#include <string>
+#ifdef __EPOC_OS__
+ #include <stdio.h>
+ #include <string.h>
+#else
+ #include <cstdio>
+ #include <cstring>
+#endif
+#include <ctype.h>
+
+#include "generic_types.h"
+
+using namespace std;
+
+namespace sysync {
+
+// PALM OS specifics
+#ifdef __PALM_OS__
+ // StrVPrintF handles a subset of ANSI vsprintf, but normal cases are available
+ #define vsprintf StrVPrintF
+ #define sprintf StrPrintF
+ // PalmOS 3.5 does not have strncmp, use our own enhanced strnncmp
+ #define strncmp sysync::strnncmp
+ // PalmOS does not have vnsprintf, we need to simulate them
+ #define NO_VSNPRINTF
+ #define NO_SNPRINTF
+#endif
+
+#if defined(WINCE) || defined(_MSC_VER) || defined(__EPOC_OS__)
+ // WinCE and Symbian do not have vsnprintf, as well as Visual Studio
+ #define NO_VSNPRINTF
+ #define NO_SNPRINTF
+#endif
+
+#ifdef NO_VSNPRINTF
+int vsnprintf(char *s, int sz, const char *formatStr, va_list arg);
+#endif
+#ifdef NO_SNPRINTF
+int snprintf(char *s, int sz, const char *formatStr, ...);
+#endif
+
+
+/// @brief Return passed pointer as C string, if pointer is NULL, empty string is returned
+const char *alwaysCStr(const char *aStr);
+
+// Assign char ptr to string object
+// NOTE: NULL is allowed and means empty string
+void AssignString(string &aString, const char *aCharP);
+// Assign char ptr to char array
+// NOTE: NULL is allowed and means empty string
+void AssignCString(char *aCStr, const char *aCharP, size_t aLen);
+// trim leading spaces/ctrls and everything including and after the next space/ctrl
+// NOTE: NULL is allowed and means empty string
+void AssignTrimmedCString(char *aCStr, const char *aCharP, size_t aLen);
+
+// standard mangling increment
+#define SYSER_NAME_MANGLEINC 43
+// save mangled into C-string buffer
+uInt16 assignMangledToCString(appCharP aCString, cAppCharP aCode, uInt16 aMaxBytes, bool aIsName, uInt8 aMangleInc=SYSER_NAME_MANGLEINC, cAppCharP aSecondKey=NULL);
+// get unmangled into string
+void getUnmangled(string &aString, cAppCharP aMangled, uInt16 aMaxChars=100, uInt8 aMangleInc=SYSER_NAME_MANGLEINC, cAppCharP aSecondKey=NULL);
+// get unmangled into buffer
+void getUnmangledAsBuf(appCharP aBuffer, uInt16 aBufSize, cAppCharP aMangled, uInt8 aMangleInc=SYSER_NAME_MANGLEINC, cAppCharP aSecondKey=NULL);
+
+
+// Note: NULL allowed as empty string input for struXXX
+// case insensitive and whitespace trimming strcmp
+sInt16 strutrimcmp(const char *s1, const char *s2);
+// case insensitive strcmp which allow wildcards ? and * in s2, NULL allowed as empty string input
+sInt16 strwildcmp(const char *s1, const char *s2, size_t len1=0, size_t len2=0);
+// case insensitive strpos, returns -1 if not found
+sInt32 strupos(const char *s, const char *pat, size_t slen=0, size_t patlen=0);
+// case insensitive strcmp
+sInt16 strucmp(const char *s1, const char *s2, size_t len1=0, size_t len2=0);
+// byte by byte strcmp
+// Note: is compatible with standard strncmp if len2 is left unspecified
+sInt16 strnncmp(const char *s1, const char *s2, size_t len1=0, size_t len2=0);
+// testing
+#ifdef SYNTHESIS_UNIT_TEST
+bool test_strwildcmp(void);
+#endif
+
+
+// parser for tag="value" type string format
+cAppCharP nextTag(cAppCharP aTagString, string &aTag, string &aTagValue);
+// direct conversion of value (usually from nextTag()) into uInt16
+uInt16 uint16Val(cAppCharP aValue);
+// direct conversion of value (usually from nextTag()) into uInt32
+uInt32 uint32Val(cAppCharP aValue);
+// direct conversion of value (usually from nextTag()) into bool
+bool boolVal(cAppCharP aValue);
+
+
+// returns true on successful conversion of string to bool
+bool StrToBool(const char *aStr, bool &aBool);
+// returns true on successful conversion of string to enum
+bool StrToEnum(const char * const aEnumNames[], sInt16 aNumEnums, sInt16 &aShort, const char *aStr, sInt16 aLen=0);
+
+const char *tristateString(sInt8 aTristate);
+const char *boolString(bool aBool);
+
+
+// return number of successfully converted chars
+// Note: despite the names these functions take *fixed-size* integers
+sInt16 StrToUShort(const char *aStr, uInt16 &aShort, sInt16 aMaxDigits=100);
+sInt16 StrToShort(const char *aStr, sInt16 &aShort, sInt16 aMaxDigits=100);
+sInt16 StrToULong(const char *aStr, uInt32 &aLong, sInt16 aMaxDigits=100);
+sInt16 StrToLong(const char *aStr, sInt32 &aLong, sInt16 aMaxDigits=100);
+sInt16 StrToULongLong(const char *aStr, uInt64 &aLongLong, sInt16 aMaxDigits=100);
+sInt16 StrToLongLong(const char *aStr, sInt64 &aLongLong, sInt16 aMaxDigits=100);
+sInt16 HexStrToUShort(const char *aStr, uInt16 &aShort, sInt16 aMaxDigits=100);
+sInt16 HexStrToULong(const char *aStr, uInt32 &aLong, sInt16 aMaxDigits=100);
+sInt16 HexStrToULongLong(const char *aStr, uInt64 &aLongLong, sInt16 aMaxDigits=100);
+sInt16 HexStrToUIntPtr(const char *aStr, uIntPtr &aIntPtr, sInt16 aMaxDigits=100);
+#ifndef NO_FLOATS
+sInt16 StrToDouble(const char *aStr, double &aDouble, sInt16 aMaxDigits=100);
+#endif
+
+sInt16 CStrToStrAppend(const char *aStr, string &aString, bool aStopAtQuoteOrCtrl=false, char ignore='\0');
+sInt16 StrToCStrAppend(const char *aStr, string &aString, bool aAllow8Bit=false, char ignore='\0');
+
+// makes hex char out of nibble
+char NibbleToHexDigit(uInt8 aNibble);
+// add a hex byte to a string
+void AppendHexByte(string &aString, uInt8 aByte);
+
+// old-style C-formatted output into string object
+void vStringObjPrintf(string &aStringObj, const char *aFormat, bool aAppend, va_list aArgs);
+
+// old-style C-formatted output into string object
+void StringObjPrintf(string &aStringObj, const char *aFormat, ...)
+#ifdef __GNUC__
+ __attribute__((format(printf, 2, 3)))
+#endif
+ ;
+void StringObjAppendPrintf(string &aStringObj, const char *aFormat, ...)
+#ifdef __GNUC__
+ __attribute__((format(printf, 2, 3)))
+#endif
+ ;
+
+} // namespace sysync
+
+#endif // STRINGUTILS_H
diff --git a/src/sysync/superdatastore.cpp b/src/sysync/superdatastore.cpp
new file mode 100755
index 0000000..c3ec689
--- /dev/null
+++ b/src/sysync/superdatastore.cpp
@@ -0,0 +1,1244 @@
+/*
+ * File: SuperDataStore.h
+ *
+ * Author: Lukas Zeller (luz@synthesis.ch)
+ *
+ * TSuperDataStore
+ * "Virtual" datastore consisting of an union of other
+ * datastores, for example a vCal datastore based on
+ * two separate vEvent and vTodo datastores.
+ *
+ * Copyright (c) 2002-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ * 2002-08-05 : luz : created
+ *
+ */
+
+// includes
+#include "prefix_file.h"
+#include "sysync.h"
+#include "syncappbase.h"
+#include "superdatastore.h"
+
+#ifdef SUPERDATASTORES
+
+
+using namespace sysync;
+
+
+// sub-datastore link config
+// =========================
+
+
+// config constructor
+TSubDSLinkConfig::TSubDSLinkConfig(TLocalDSConfig *aLocalDSConfigP, TConfigElement *aParentElementP) :
+ TConfigElement(aLocalDSConfigP->getName(),aParentElementP)
+{
+ clear();
+ fLinkedDSConfigP=aLocalDSConfigP;
+} // TSubDSLinkConfig::TSubDSLinkConfig
+
+
+// config destructor
+TSubDSLinkConfig::~TSubDSLinkConfig()
+{
+ clear();
+} // TSubDSLinkConfig::~TSubDSLinkConfig
+
+
+// init defaults
+void TSubDSLinkConfig::clear(void)
+{
+ // init defaults
+ fDispatchFilter.erase();
+ fGUIDPrefix.erase();
+ // clear inherited
+ inherited::clear();
+} // TSubDSLinkConfig::clear
+
+
+#ifndef HARDCODED_CONFIG
+
+// remote rule config element parsing
+bool TSubDSLinkConfig::localStartElement(const char *aElementName, const char **aAttributes, sInt32 aLine)
+{
+ // checking the elements
+ // - identification of remote
+ if (strucmp(aElementName,"dispatchfilter")==0)
+ expectString(fDispatchFilter);
+ else if (strucmp(aElementName,"guidprefix")==0)
+ expectString(fGUIDPrefix);
+ // - not known here
+ else
+ return inherited::localStartElement(aElementName,aAttributes,aLine);
+ // ok
+ return true;
+} // TSubDSLinkConfig::localStartElement
+
+#endif
+
+
+// superdatastore config
+// =====================
+
+TSuperDSConfig::TSuperDSConfig(const char* aName, TConfigElement *aParentElement) :
+ TLocalDSConfig(aName,aParentElement)
+{
+ clear();
+} // TSuperDSConfig::TSuperDSConfig
+
+
+TSuperDSConfig::~TSuperDSConfig()
+{
+ clear();
+} // TSuperDSConfig::~TSuperDSConfig
+
+
+// init defaults
+void TSuperDSConfig::clear(void)
+{
+ // init defaults
+ // - no datastore links
+ TSubDSConfigList::iterator pos;
+ for(pos=fSubDatastores.begin();pos!=fSubDatastores.end();pos++)
+ delete *pos;
+ fSubDatastores.clear();
+ // clear inherited
+ inherited::clear();
+} // TSuperDSConfig::clear
+
+
+#ifndef HARDCODED_CONFIG
+
+// config element parsing
+bool TSuperDSConfig::localStartElement(const char *aElementName, const char **aAttributes, sInt32 aLine)
+{
+ // checking the elements
+ // - links to sub-datastores
+ if (strucmp(aElementName,"contains")==0) {
+ // definition of a new datastore
+ const char* nam = getAttr(aAttributes,"datastore");
+ if (!nam)
+ return fail("'contains' missing 'datastore' attribute");
+ // search sub-datastore
+ TLocalDSConfig *subdscfgP =
+ static_cast<TSessionConfig *>(getParentElement())->getLocalDS(nam);
+ if (!subdscfgP)
+ return fail("unknown datastore '%s' specified",nam);
+ // create new datastore link
+ TSubDSLinkConfig *dslinkcfgP =
+ new TSubDSLinkConfig(subdscfgP,this);
+ // - save in list
+ fSubDatastores.push_back(dslinkcfgP);
+ // - let element handle parsing
+ expectChildParsing(*dslinkcfgP);
+ }
+ // - none known here
+ else
+ return TLocalDSConfig::localStartElement(aElementName,aAttributes,aLine);
+ // ok
+ return true;
+} // TSuperDSConfig::localStartElement
+
+#endif
+
+// resolve
+void TSuperDSConfig::localResolve(bool aLastPass)
+{
+ if (aLastPass) {
+ // check for required settings
+ // %%% tbd
+ }
+ // resolve inherited
+ inherited::localResolve(aLastPass);
+} // TSuperDSConfig::localResolve
+
+
+// - create appropriate datastore from config, calls addTypeSupport as well
+TLocalEngineDS *TSuperDSConfig::newLocalDataStore(TSyncSession *aSessionP)
+{
+ // Synccap defaults to normal set supported by the engine by default
+ TSuperDataStore *sdsP =
+ new TSuperDataStore(this,aSessionP,getName(),aSessionP->getSyncCapMask());
+ // add type support
+ addTypes(sdsP,aSessionP);
+ return sdsP;
+} // TLocalDSConfig::newLocalDataStore
+
+
+
+
+
+/*
+ * Implementation of TSuperDataStore
+ */
+
+/* public TSuperDataStore members */
+
+
+TSuperDataStore::TSuperDataStore(TSuperDSConfig *aDSConfigP, TSyncSession *aSessionP, const char *aName, uInt32 aCommonSyncCapMask) :
+ TLocalEngineDS(aDSConfigP, aSessionP, aName, aCommonSyncCapMask)
+{
+ // set config ptr
+ fDSConfigP = aDSConfigP;
+ if (!fDSConfigP)
+ SYSYNC_THROW(TSyncException(DEBUGTEXT("TSuperDataStore::TSuperDataStore called with NULL config","lds1")));
+ // reset first
+ InternalResetDataStore();
+ // create links to subdatastores
+ TSubDSConfigList::iterator pos;
+ TSubDatastoreLink link;
+ for(pos=aDSConfigP->fSubDatastores.begin();pos!=aDSConfigP->fSubDatastores.end();pos++) {
+ // start not yet pending
+ link.fStartPending=false;
+ // set link to subdatastore link config
+ link.fDSLinkConfigP=*pos;
+ // find actual datastore by "handle" (= config pointer)
+ link.fDatastoreLinkP=aSessionP->findLocalDataStore(link.fDSLinkConfigP->fLinkedDSConfigP);
+ // if actual datastore does not yet exist, create it now. This can be
+ // the case for clients, where datastores are only instantiated when
+ // directly addressed by a SyncRequest (which will not happen for
+ // subdatastores normally)
+ if (!link.fDatastoreLinkP) {
+ // add
+ link.fDatastoreLinkP=aSessionP->addLocalDataStore(link.fDSLinkConfigP->fLinkedDSConfigP);
+ }
+ // save link
+ fSubDSLinks.push_back(link);
+ }
+ // Important: We need to get the iterator now again, as the implicit
+ // iterator init via InternalResetDataStore() is invalid because the list was empty then.
+ fCurrentGenDSPos=fSubDSLinks.begin();
+} // TSuperDataStore::TSuperDataStore
+
+
+void TSuperDataStore::InternalResetDataStore(void)
+{
+ // init
+ fFirstTimeSync=false;
+ TSubDSLinkList::iterator pos;
+ // cancel all pending starts
+ for(pos=fSubDSLinks.begin();pos!=fSubDSLinks.end();pos++) {
+ pos->fStartPending=false;
+ }
+ fSuperStartPending = false;
+ // make sure this is set in case startSync() is not called before generateSyncCommands()
+ fCurrentGenDSPos=fSubDSLinks.begin();
+} // TSuperDataStore::InternalResetDataStore
+
+
+TSuperDataStore::~TSuperDataStore()
+{
+ InternalResetDataStore();
+} // TSuperDataStore::~TSuperDataStore
+
+
+
+// Session events, which need some distribution to subdatastores
+// =============================================================
+
+// Methods overriding TLocalEngineDS
+// ----------------------------------
+
+
+// obtain Sync Cap mask, must be lowest common mask of all subdatastores
+uInt32 TSuperDataStore::getSyncCapMask(void)
+{
+ // AND of all subdatastores
+ uInt32 capmask = ~0; // all bits set
+ TSubDSLinkList::iterator pos;
+ for (pos=fSubDSLinks.begin();pos!=fSubDSLinks.end();pos++) {
+ capmask = capmask & pos->fDatastoreLinkP->getSyncCapMask();
+ }
+ return capmask;
+} // TSuperDataStore::getSyncCapMask
+
+
+// process Sync alert from remote party: check if alert code is supported,
+// check if slow sync is needed due to anchor mismatch
+// - server case: also generate appropriate Alert acknowledge command
+TAlertCommand *TSuperDataStore::engProcessSyncAlert(
+ TSuperDataStore *aAsSubDatastoreOf, // if acting as subdatastore
+ uInt16 aAlertCode, // the alert code
+ const char *aLastRemoteAnchor, // last anchor of client
+ const char *aNextRemoteAnchor, // next anchor of client
+ const char *aTargetURI, // target URI as sent by remote, no processing at all
+ const char *aIdentifyingTargetURI, // target URI that was used to identify datastore
+ const char *aTargetURIOptions, // option string contained in target URI
+ SmlFilterPtr_t aTargetFilter, // DS 1.2 filter, NULL if none
+ const char *aSourceURI, // source URI
+ TStatusCommand &aStatusCommand // status that might be modified
+)
+{
+ TAlertCommand *alertcmdP=NULL;
+
+ TAlertCommand *subalertcmdP=NULL;
+ TStatusCommand substatus(fSessionP);
+
+ SYSYNC_TRY {
+ // alert all subdatastores
+ TSubDSLinkList::iterator pos;
+ for (pos=fSubDSLinks.begin();pos!=fSubDSLinks.end();pos++) {
+ subalertcmdP=pos->fDatastoreLinkP->engProcessSyncAlert(
+ this, // as subdatastore of myself
+ aAlertCode, // the alert code
+ aLastRemoteAnchor, // last anchor of client
+ aNextRemoteAnchor, // next anchor of client
+ aTargetURI, // target URI as sent by remote, no processing at all
+ aIdentifyingTargetURI, // target URI (without eventual CGI)
+ aTargetURIOptions, // filtering CGI (NULL or empty if none)
+ aTargetFilter, // DS 1.2 filter, NULL if none
+ aSourceURI, // source URI
+ substatus // status that might be modified
+ );
+ if (subalertcmdP) {
+ // get rid of this, we don't need it
+ delete subalertcmdP;
+ }
+ else {
+ // basic problem with one of the subdatastores
+ // - propagate error code
+ aStatusCommand.setStatusCode(substatus.getStatusCode());
+ // - cancel alert
+ return NULL;
+ }
+ // this one is pending for start
+ pos->fStartPending=true;
+ }
+ // set flag to indicate this subdatastore has init pending
+ // Now all subdatastores should be successfully alerted and have current anchor infos ready,
+ // so we can call inherited (which will obtain combined anchors from our logicInitSyncAnchors)
+ alertcmdP = inherited::engProcessSyncAlert(
+ aAsSubDatastoreOf, // as indicated by caller (normally, superdatastore is not subdatastore of another superdatastore, but...)
+ aAlertCode, // the alert code
+ aLastRemoteAnchor, // last anchor of client
+ aNextRemoteAnchor, // next anchor of client
+ aTargetURI, // target URI as sent by remote, no processing at all
+ aIdentifyingTargetURI, // target URI (without eventual CGI)
+ aTargetURIOptions, // filtering CGI (NULL or empty if none)
+ aTargetFilter, // DS 1.2 filter, NULL if none
+ aSourceURI, // source URI
+ aStatusCommand // status that might be modified
+ );
+ // entire superdatastore is pending for start
+ fSuperStartPending=true;
+ }
+ SYSYNC_CATCH (...)
+ // clean up locally owned objects
+ if (alertcmdP) delete alertcmdP;
+ if (subalertcmdP) delete subalertcmdP;
+ SYSYNC_RETHROW;
+ SYSYNC_ENDCATCH
+ return alertcmdP;
+} // TSuperDataStore::engProcessSyncAlert
+
+
+// process status received for sync alert
+bool TSuperDataStore::engHandleAlertStatus(TSyError aStatusCode)
+{
+ // show it to all subdatastores
+ TSubDSLinkList::iterator pos;
+ for (pos=fSubDSLinks.begin();pos!=fSubDSLinks.end();pos++) {
+ pos->fDatastoreLinkP->engHandleAlertStatus(aStatusCode);
+ }
+ // all subdatastores have seen the alert status, so let superdatastore handle it as well
+ return TLocalEngineDS::engHandleAlertStatus(aStatusCode);
+} // TSuperDataStore::engHandleAlertStatus
+
+
+// Set remote datastore for local
+void TSuperDataStore::engSetRemoteDatastore(
+ TRemoteDataStore *aRemoteDatastoreP // the remote datastore involved
+)
+{
+ // set all subdatastores to (same) remote datastore as superdatastore itself
+ TSubDSLinkList::iterator pos;
+ for (pos=fSubDSLinks.begin();pos!=fSubDSLinks.end();pos++) {
+ pos->fDatastoreLinkP->engSetRemoteDatastore(aRemoteDatastoreP);
+ }
+ // set in superdatastore as well
+ TLocalEngineDS::engSetRemoteDatastore(aRemoteDatastoreP);
+} // TSuperDataStore::engSetRemoteDatastore
+
+
+// set Sync types needed for sending local data to remote DB
+void TSuperDataStore::setSendTypeInfo(
+ TSyncItemType *aLocalSendToRemoteTypeP,
+ TSyncItemType *aRemoteReceiveFromLocalTypeP
+)
+{
+ // set all subdatastores to (same) types as superdatastore itself
+ TSubDSLinkList::iterator pos;
+ for (pos=fSubDSLinks.begin();pos!=fSubDSLinks.end();pos++) {
+ pos->fDatastoreLinkP->setSendTypeInfo(aLocalSendToRemoteTypeP,aRemoteReceiveFromLocalTypeP);
+ }
+ // set in superdatastore as well
+ TLocalEngineDS::setSendTypeInfo(aLocalSendToRemoteTypeP,aRemoteReceiveFromLocalTypeP);
+} // TSuperDataStore::setSendTypeInfo
+
+
+// set Sync types needed for receiving remote data in local DB
+void TSuperDataStore::setReceiveTypeInfo(
+ TSyncItemType *aLocalReceiveFromRemoteTypeP,
+ TSyncItemType *aRemoteSendToLocalTypeP
+)
+{
+ // set all subdatastores to (same) types as superdatastore itself
+ TSubDSLinkList::iterator pos;
+ for (pos=fSubDSLinks.begin();pos!=fSubDSLinks.end();pos++) {
+ pos->fDatastoreLinkP->setReceiveTypeInfo(aLocalReceiveFromRemoteTypeP,aRemoteSendToLocalTypeP);
+ }
+ // set in superdatastore as well
+ TLocalEngineDS::setReceiveTypeInfo(aLocalReceiveFromRemoteTypeP,aRemoteSendToLocalTypeP);
+} // TSuperDataStore::setReceiveTypeInfo
+
+
+// init usage of datatypes set with setSendTypeInfo/setReceiveTypeInfo
+localstatus TSuperDataStore::initDataTypeUse(void)
+{
+ localstatus sta=LOCERR_OK;
+
+ // set all subdatastores to (same) types as superdatastore itself
+ TSubDSLinkList::iterator pos;
+ for (pos=fSubDSLinks.begin();pos!=fSubDSLinks.end();pos++) {
+ sta = pos->fDatastoreLinkP->initDataTypeUse();
+ if (sta!=LOCERR_OK)
+ return sta; // failed
+ }
+ // set in superdatastore as well
+ return TLocalEngineDS::initDataTypeUse();
+} // TSuperDataStore::initDataTypeUse
+
+
+// SYNC command bracket start (check credentials if needed)
+bool TSuperDataStore::engProcessSyncCmd(
+ SmlSyncPtr_t aSyncP, // the Sync element
+ TStatusCommand &aStatusCommand, // status that might be modified
+ bool &aQueueForLater // will be set if command must be queued for later (re-)execution
+)
+{
+ // start sync for all subdatastores
+ bool ok=true;
+ bool doqueue;
+ TSubDSLinkList::iterator pos;
+ for (pos=fSubDSLinks.begin();pos!=fSubDSLinks.end();pos++) {
+ // Note: this will cause subdatastores to call their own startSync,
+ // so we do NOT need to iterate over subdatastores in our startSync!
+ doqueue=false;
+ // if in init phase (entire superdatastore pending to start)
+ // only call subdatastores that are still pending for start, too
+ ok=true;
+ if (!fSuperStartPending || pos->fStartPending) {
+ ok=pos->fDatastoreLinkP->engProcessSyncCmd(aSyncP,aStatusCommand,doqueue);
+ if (!doqueue) {
+ // this one is now initialized. Do not do it again until all others are initialized, too
+ pos->fStartPending=false;
+ }
+ else {
+ // we must queue the entire command for later (but some subdatastores might be excluded then)
+ aQueueForLater=true; // queue if one of the subdatastores needs it
+ }
+ }
+ if (!ok) return false;
+ }
+ // start sync myself
+ ok=TLocalEngineDS::engProcessSyncCmd(aSyncP,aStatusCommand,doqueue);
+ if (doqueue) aQueueForLater=true; // queue if one of the subdatastores needs it
+ // if we reach this w/o queueing, start is no longer pending
+ if (!aQueueForLater) fSuperStartPending=false;
+ // done
+ return ok;
+} // TSuperDataStore::processSyncCmd
+
+
+// SYNC command bracket end (but another might follow in next message)
+bool TSuperDataStore::engProcessSyncCmdEnd(bool &aQueueForLater)
+{
+ // signal sync end to all subdatastores
+ bool ok=true;
+ bool doqueue;
+ TSubDSLinkList::iterator pos;
+ for (pos=fSubDSLinks.begin();pos!=fSubDSLinks.end();pos++) {
+ doqueue=false;
+ ok=pos->fDatastoreLinkP->engProcessSyncCmdEnd(doqueue);
+ if (doqueue) aQueueForLater=true; // queue if one of the subdatastores needs it
+ if (!ok) return false;
+ }
+ // signal it to myself
+ ok=TLocalEngineDS::engProcessSyncCmdEnd(doqueue);
+ if (doqueue) aQueueForLater=true; // queue if one of the subdatastores needs it
+ return ok;
+} // TSuperDataStore::engProcessSyncCmdEnd
+
+
+#ifndef SYSYNC_CLIENT
+
+// process map
+localstatus TSuperDataStore::engProcessMap(cAppCharP aRemoteID, cAppCharP aLocalID)
+{
+ TSubDatastoreLink *linkP = NULL;
+ localstatus sta = LOCERR_OK;
+
+ // item has local ID, we can find datastore by prefix
+ linkP = findSubLinkByLocalID(aLocalID);
+ if (!linkP) {
+ sta = 404; // not found
+ goto done;
+ }
+ // let subdatastore process (and only show subDS part of localID)
+ sta=linkP->fDatastoreLinkP->engProcessMap(
+ aRemoteID,
+ aLocalID+linkP->fDSLinkConfigP->fGUIDPrefix.size()
+ );
+done:
+ return sta;
+} // TSuperDataStore::engProcessMap
+
+#endif
+
+
+// called to process incoming item operation
+// Method takes ownership of syncitemP in all cases
+// - returns true (and unmodified or non-200-successful status) if
+// operation could be processed regularily
+// - returns false (but probably still successful status) if
+// operation was processed with internal irregularities, such as
+// trying to delete non-existant item in datastore with
+// incomplete Rollbacks (which returns status 200 in this case!).
+bool TSuperDataStore::engProcessRemoteItem(
+ TSyncItem *syncitemP,
+ TStatusCommand &aStatusCommand
+)
+{
+ bool regular=true;
+ string datatext;
+ #ifndef SYSYNC_CLIENT
+ TSyncItem *itemcopyP;
+ #endif
+
+ // show
+ PDEBUGBLOCKFMT((
+ "SuperProcessItem", "Processing incoming item in superdatastore",
+ "datastore=%s|SyncOp=%s|RemoteID=%s|LocalID=%s",
+ getName(),
+ SyncOpNames[syncitemP->getSyncOp()],
+ syncitemP->getRemoteID(),
+ syncitemP->getLocalID()
+ ));
+ // let appropriate subdatastore handle the command
+ TSubDatastoreLink *linkP = NULL;
+ TSyncOperation sop=syncitemP->getSyncOp();
+ string remid;
+ TSubDSLinkList::iterator pos;
+ switch (sop) {
+ #ifndef SYSYNC_CLIENT
+ // Server case
+ case sop_wants_replace:
+ case sop_replace:
+ case sop_wants_add:
+ case sop_add:
+ // item has no local ID, we need to apply filters to item data
+ PDEBUGPRINTFX(DBG_DATA,("Checkin subdatastore filters to find where it belongs"));
+ linkP = findSubLinkByData(*syncitemP);
+ if (!linkP) goto nods;
+ PDEBUGPRINTFX(DBG_DATA,(
+ "Found item belongs to subdatastore '%s'",
+ linkP->fDatastoreLinkP->getName()
+ ));
+ // make sure item does not have a local ID (which would be wrong because of prefixes anyway)
+ syncitemP->clearLocalID();
+ // remembert because we might need it below for move-replace
+ remid=syncitemP->getRemoteID();
+ // let subdatastore process
+ regular=linkP->fDatastoreLinkP->engProcessRemoteItem(syncitemP,aStatusCommand);
+ // now check if replace was treated as add, if yes, this indicates
+ // that this might be a move between subdatastores
+ if (
+ (sop==sop_replace || sop==sop_wants_replace) &&
+ !fSlowSync && aStatusCommand.getStatusCode()==201
+ ) {
+ // this is probably a move from another datastore by changing an attribute
+ // that dispatches datastores (such as a vEvent changed to a vToDo)
+ // - so we delete all items with this remote ID in all other datastores
+ PDEBUGPRINTFX(DBG_DATA,("Replace could be a move between subdatastores, trying to delete all items with same remoteID in other subdatastores"));
+ TSubDSLinkList::iterator pos;
+ TStatusCommand substatus(fSessionP);
+ for (pos=fSubDSLinks.begin();pos!=fSubDSLinks.end();pos++) {
+ if (&(*pos) != linkP) {
+ // all but original datastore
+ substatus.setStatusCode(200);
+ itemcopyP = new TSyncItem();
+ // - only remote ID and syncop are relevant, leave everything else empty
+ itemcopyP->setRemoteID(remid.c_str());
+ itemcopyP->setSyncOp(sop_delete);
+ // - now try to delete. This might fail if replace above wasn't a move
+ // itemcopyP is consumed
+ PDEBUGPRINTFX(DBG_DATA+DBG_DETAILS,(
+ "Trying to delete item with remoteID='%s' from subdatastore '%s'",
+ itemcopyP->getRemoteID(),
+ linkP->fDatastoreLinkP->getName()
+ ));
+ regular=pos->fDatastoreLinkP->engProcessRemoteItem(itemcopyP,substatus);
+ #ifdef SYDEBUG
+ if (regular) {
+ // deleted ok
+ PDEBUGPRINTFX(DBG_DATA,(
+ "Found item in '%s', deleted here (and moved to '%s')",
+ pos->fDatastoreLinkP->getName(),
+ linkP->fDatastoreLinkP->getName()
+ ));
+ }
+ #endif
+ }
+ }
+ PDEBUGPRINTFX(DBG_DATA,("End of (possible) move-replace between subdatastores"));
+ regular=true; // fully ok, no matter if delete above has succeeded or not
+ }
+ goto done;
+ case sop_archive_delete:
+ case sop_soft_delete:
+ case sop_delete:
+ case sop_copy:
+ // item has no local ID AND no data, only a remoteID:
+ // we must try to read item from all subdatastores by remoteID until
+ // one is found
+ // get an empty item of correct type to call logicRetrieveItemByID
+ itemcopyP = getLocalReceiveType()->newSyncItem(getRemoteSendType(),this);
+ // - only remote ID is relevant, leave everything else empty
+ itemcopyP->setRemoteID(syncitemP->getRemoteID());
+ // try to read item from all subdatastores
+ for (pos=fSubDSLinks.begin();pos!=fSubDSLinks.end();pos++) {
+ linkP = &(*pos);
+ // always start with 200
+ aStatusCommand.setStatusCode(200);
+ // now try to read
+ PDEBUGPRINTFX(DBG_DATA+DBG_DETAILS,(
+ "Trying to read item by remoteID='%s' from subdatastore '%s' to see if it is there",
+ itemcopyP->getRemoteID(),
+ linkP->fDatastoreLinkP->getName()
+ ));
+ regular=linkP->fDatastoreLinkP->logicRetrieveItemByID(*itemcopyP,aStatusCommand);
+ // must be ok AND not 404 (item not found)
+ if (regular && aStatusCommand.getStatusCode()!=404) {
+ PDEBUGPRINTFX(DBG_DATA,(
+ "Item found in subdatastore '%s', deleting it there",
+ linkP->fDatastoreLinkP->getName()
+ ));
+ // now we can delete or copy, consuming original item
+ regular=linkP->fDatastoreLinkP->engProcessRemoteItem(syncitemP,aStatusCommand);
+ // delete duplicated item as well
+ delete itemcopyP;
+ // done
+ regular=true;
+ goto done;
+ }
+ }
+ // none of the datastores could process this item --> error
+ // - delete duplicated item
+ delete itemcopyP;
+ // - make sure delete reports 200 for incomplete-rollback-datastores
+ if (aStatusCommand.getStatusCode()==404 && sop!=sop_copy) {
+ // not finding an item for delete might be ok for remote...
+ if (fSessionP->getSessionConfig()->fDeletingGoneOK) {
+ // 404/410: item not found, could be because previous aborted session has
+ // already committed deletion of that item -> behave as if delete was ok
+ PDEBUGPRINTFX(DBG_DATA,("to-be-deleted item was not found, but do NOT report %hd",aStatusCommand.getStatusCode()));
+ aStatusCommand.setStatusCode(200);
+ }
+ // ...but it is a internal irregularity, fall thru to return false
+ }
+ // is an internal irregularity
+ regular=false;
+ goto done;
+ #else
+ // Client case
+ case sop_wants_replace:
+ case sop_replace:
+ case sop_archive_delete:
+ case sop_soft_delete:
+ case sop_delete:
+ case sop_copy:
+ // item has local ID, we can find datastore by prefix
+ linkP = findSubLinkByLocalID(syncitemP->getLocalID());
+ if (!linkP) goto nods;
+ // remove prefix before letting subdatastore process it
+ syncitemP->fLocalID.erase(0,linkP->fDSLinkConfigP->fGUIDPrefix.size());
+ // now let subdatastore process
+ regular=linkP->fDatastoreLinkP->engProcessRemoteItem(syncitemP,aStatusCommand);
+ goto done;
+ case sop_wants_add:
+ case sop_add:
+ // item has no local ID, we need to apply filters to item data
+ linkP = findSubLinkByData(*syncitemP);
+ if (!linkP) goto nods;
+ // make sure item does not have a local ID (which would be wrong because of prefixes anyway)
+ syncitemP->clearLocalID();
+ // let subdatastore process
+ regular=linkP->fDatastoreLinkP->engProcessRemoteItem(syncitemP,aStatusCommand);
+ goto done;
+ #endif
+ default:
+ nods:
+ // no datastore or unknown command, general DB error
+ aStatusCommand.setStatusCode(510);
+ PDEBUGPRINTFX(DBG_ERROR,("TSuperDataStore::processRemoteItem Fatal: Item cannot be processed by any subdatastore"));
+ // consume item
+ delete syncitemP;
+ regular=false;
+ goto done;
+ } // switch
+done:
+ PDEBUGENDBLOCK("SuperProcessItem");
+ return regular;
+} // TSuperDataStore::engProcessRemoteItem
+
+
+
+// - must return true if this datastore is finished with <sync>
+// (if all datastores return true,
+// session is allowed to finish sync packet with outgoing message
+bool TSuperDataStore::isSyncDone(void)
+{
+ // check subdatastores
+ bool done=true;
+ TSubDSLinkList::iterator pos;
+ for (pos=fSubDSLinks.begin();pos!=fSubDSLinks.end();pos++) {
+ done=done && pos->fDatastoreLinkP->isSyncDone();
+ }
+ // check myself
+ return done && TLocalEngineDS::isSyncDone();
+} // TSuperDataStore::isSyncDone
+
+
+// abort sync with this super datastore (that is, with all subdatastores as well)
+void TSuperDataStore::engAbortDataStoreSync(TSyError aStatusCode, bool aLocalProblem, bool aResumable)
+{
+ // abort subdatastores
+ TSubDSLinkList::iterator pos;
+ for (pos=fSubDSLinks.begin();pos!=fSubDSLinks.end();pos++) {
+ pos->fDatastoreLinkP->engAbortDataStoreSync(aStatusCode,aLocalProblem,aResumable);
+ }
+ // set code in my own ancestor
+ TLocalEngineDS::engAbortDataStoreSync(aStatusCode,aLocalProblem,aResumable);
+} // TSuperDataStore::engAbortDataStoreSync
+
+
+// - must return true if this datastore is finished with <sync>
+// (if all datastores return true,
+// session is allowed to finish sync packet with outgoing message
+bool TSuperDataStore::isAborted(void)
+{
+ // check subdatastores
+ TSubDSLinkList::iterator pos;
+ for (pos=fSubDSLinks.begin();pos!=fSubDSLinks.end();pos++) {
+ if (pos->fDatastoreLinkP->isAborted()) return true; // one aborted, super aborted as well
+ }
+ // check myself
+ return TLocalEngineDS::isAborted();
+} // TSuperDataStore::isAborted
+
+
+// called at very end of sync session, when everything is done
+// Note: is also called before deleting a datastore (so aborted sessions
+// can do cleanup and/or statistics display as well)
+void TSuperDataStore::engFinishDataStoreSync(localstatus aErrorStatus)
+{
+ // inform all subdatastores
+ TSubDSLinkList::iterator pos;
+ for (pos=fSubDSLinks.begin();pos!=fSubDSLinks.end();pos++) {
+ pos->fDatastoreLinkP->engFinishDataStoreSync(aErrorStatus);
+ }
+ // call inherited
+ inherited::engFinishDataStoreSync(aErrorStatus);
+} // TSuperDataStore::engFinishDataStoreSync
+
+
+// Internal events during sync to access local database
+// ====================================================
+
+// Methods overriding TLocalEngineDS
+// ----------------------------------
+
+
+// Abstracts of TLocalEngineDS
+// ----------------------------
+
+
+// called at sync alert (before generating for client, after receiving for server)
+// - obtains combined anchor from subdatastores
+// - combines them into a common anchor (if possible)
+// - updates fFirstTimeSync as well
+localstatus TSuperDataStore::engInitSyncAnchors(
+ cAppCharP aDatastoreURI, // local datastore URI
+ cAppCharP aRemoteDBID // ID of remote datastore (to find session information in local DB)
+)
+{
+ bool allanchorsequal=true;
+ localstatus sta=LOCERR_OK;
+
+ // superdatastore has no own anchors, so collect data from subdatastores
+ fFirstTimeSync=false;
+ TSubDSLinkList::iterator pos;
+ for (pos=fSubDSLinks.begin();pos!=fSubDSLinks.end();pos++) {
+ if (pos==fSubDSLinks.begin()) {
+ /* not needed, because engInitSyncAnchors() will be called only after all subdatastore's
+ engProcessSyncAlert() was called, which in turn contains a call to engInitSyncAnchors()
+ This means we can safely assume we have the fLastRemoteAnchor/fNextLocalAnchor info
+ ready here.
+ In fact, calling this here AGAIN had the effect that the state of the first
+ subdatastore would be set BACK to dssta_adminready (instead of the required dssta_syncmodestable,
+ and in turn when the first sync command arrived, it would be rejected with "SYNC received too early".
+ // init anchors of subdatastore
+ sta = pos->fDatastoreLinkP->engInitSyncAnchors(aDatastoreURI, aRemoteDBID);
+ if (sta!=LOCERR_OK)
+ break; // exit, we cannot init
+ */
+ // assign references of first datastore
+ // - this must be same from all subdatastores
+ fLastRemoteAnchor=pos->fDatastoreLinkP->fLastRemoteAnchor;
+ // - these are used from the first datastore, and might differ (a few seconds,
+ // that is) for other datastores
+ fLastLocalAnchor=pos->fDatastoreLinkP->fLastLocalAnchor;
+ fNextLocalAnchor=pos->fDatastoreLinkP->fNextLocalAnchor;
+ }
+ else {
+ // see if all are equal
+ allanchorsequal = allanchorsequal &&
+ pos->fDatastoreLinkP->fLastRemoteAnchor == fLastRemoteAnchor;
+ }
+ // also combine firstTimeSync (first time if it's first for any of the subdatastores)
+ fFirstTimeSync = fFirstTimeSync || pos->fDatastoreLinkP->fFirstTimeSync;
+ }
+ // make sure common anchor is valid only if all of the subdatastores have equal anchors
+ if (sta!=LOCERR_OK || fFirstTimeSync || !allanchorsequal) {
+ fLastLocalAnchor.empty();
+ fLastRemoteAnchor.empty();
+ }
+ // superdatastore gets adminready when all subdatastores have successfully done engInitSyncAnchors()
+ if (sta==LOCERR_OK) {
+ changeState(dssta_adminready); // admin data is now ready
+ }
+ // return status
+ return sta;
+} // TSuperDataStore::engInitSyncAnchors
+
+
+// - called at start of first <Sync> command (prepare DB for reading/writing)
+bool TSuperDataStore::startSync(TStatusCommand &aStatusCommand)
+{
+ DEBUGPRINTFX(DBG_HOT,("TSuperDataStore::startSync"));
+ // make sure we start generating with first datastore
+ fCurrentGenDSPos=fSubDSLinks.begin();
+ // NOTE: Do NOT iterate subdatastores, because these were called already
+ // by engProcessSyncCmd (server case) or engProcessSyncAlert (client case)
+ return true; // ok
+} // TSuperDataStore::startSync
+
+
+/// check is datastore is completely started.
+/// @param[in] aWait if set, call will not return until either started state is reached
+/// or cannot be reached within the maximally allowed request processing time left.
+bool TSuperDataStore::engIsStarted(bool aWait)
+{
+ // check subdatastores
+ bool ready=true;
+ TSubDSLinkList::iterator pos;
+ for (pos=fSubDSLinks.begin();pos!=fSubDSLinks.end();pos++) {
+ ready=ready && pos->fDatastoreLinkP->engIsStarted(aWait);
+ }
+ // check myself
+ return ready && inherited::engIsStarted(aWait);
+} // TSuperDataStore::engIsStarted
+
+
+// remove prefix for given subDatastore
+// @param[in] aIDWithPrefix points to ID with prefix
+// @return NULL if either datastore not found or prefix not present in aIDWithPrefix
+// @return pointer to first char in aIDWithPrefix which is not part of the prefix
+cAppCharP TSuperDataStore::removeSubDSPrefix(cAppCharP aIDWithPrefix, TLocalEngineDS *aLocalDatastoreP)
+{
+ if (!aIDWithPrefix) return NULL;
+ TSubDSLinkList::iterator pos;
+ for (pos=fSubDSLinks.begin();pos!=fSubDSLinks.end();pos++) {
+ if (pos->fDatastoreLinkP == aLocalDatastoreP) {
+ // check the prefix
+ if (strnncmp(
+ aIDWithPrefix,
+ pos->fDSLinkConfigP->fGUIDPrefix.c_str(),
+ pos->fDSLinkConfigP->fGUIDPrefix.size()
+ ) ==0)
+ return aIDWithPrefix+pos->fDSLinkConfigP->fGUIDPrefix.size(); // return start of subDS ID
+ else
+ return aIDWithPrefix; // datastore found, but prefix is not there, return unmodified
+ }
+ }
+ return NULL;
+} // TSuperDataStore::removeSubDSPrefix
+
+
+// private helper: find subdatastore which matches prefix of given localID
+TSubDatastoreLink *TSuperDataStore::findSubLinkByLocalID(const char *aLocalID)
+{
+ TSubDSLinkList::iterator pos;
+ for (pos=fSubDSLinks.begin();pos!=fSubDSLinks.end();pos++) {
+ if (strnncmp(
+ aLocalID,
+ pos->fDSLinkConfigP->fGUIDPrefix.c_str(),
+ pos->fDSLinkConfigP->fGUIDPrefix.size()
+ ) ==0) {
+ // found
+ return &(*pos);
+ }
+ }
+ return NULL; // not found
+} // TSuperDataStore::findSubLinkByLocalID
+
+
+// private helper: find subdatastore which can accept item data
+TSubDatastoreLink *TSuperDataStore::findSubLinkByData(TSyncItem &aSyncItem)
+{
+ TSubDSLinkList::iterator pos;
+ for (pos=fSubDSLinks.begin();pos!=fSubDSLinks.end();pos++) {
+ PDEBUGPRINTFX(DBG_DATA+DBG_DETAILS,(
+ "Testing item data against <dispatchfilter> of subdatastore '%s'",
+ pos->fDatastoreLinkP->getName()
+ ));
+ if (aSyncItem.testFilter(pos->fDSLinkConfigP->fDispatchFilter.c_str())) {
+ // found
+ return &(*pos);
+ }
+ }
+ return NULL; // not found
+} // TSuperDataStore::findSubLinkByData
+
+
+// only dummy, creates error if called
+bool TSuperDataStore::logicRetrieveItemByID(
+ TSyncItem &aSyncItem, // item to be filled with data from server. Local or Remote ID must already be set
+ TStatusCommand &aStatusCommand // status, must be set on error or non-200-status
+)
+{
+ aStatusCommand.setStatusCode(500);
+ DEBUGPRINTFX(DBG_ERROR,("TSuperDataStore::logicRetrieveItemByID called, which should never happen!!!!!!"));
+ return false; // not ok
+} // TSuperDataStore::logicRetrieveItemByID
+
+
+// only dummy, creates error if called
+// - Method takes ownership of syncitemP in all cases
+bool TSuperDataStore::logicProcessRemoteItem(
+ TSyncItem *syncitemP,
+ TStatusCommand &aStatusCommand,
+ bool &aVisibleInSyncset, // on entry: tells if resulting item SHOULD be visible; on exit: set if processed item remains visible in the sync set.
+ string *aGUID // GUID is stored here if not NULL
+)
+{
+ delete syncitemP; // consume
+ aStatusCommand.setStatusCode(500);
+ DEBUGPRINTFX(DBG_ERROR,("TSuperDataStore::logicProcessRemoteItem called, which should never happen!!!!!!"));
+ return false; // not ok
+} // TSuperDataStore::logicProcessRemoteItem
+
+
+// - returns true if DB implementation can filter during database fetch
+// (otherwise, fetched items must be filtered after being read from DB)
+bool TSuperDataStore::engFilteredFetchesFromDB(bool aFilterChanged)
+{
+ // only if all subdatastores support it
+ bool yes=true;
+ TSubDSLinkList::iterator pos;
+ for (pos=fSubDSLinks.begin();pos!=fSubDSLinks.end();pos++) {
+ yes = yes && pos->fDatastoreLinkP->engFilteredFetchesFromDB(aFilterChanged);
+ }
+ return yes;
+} // TSuperDataStore::engFilteredFetchesFromDB
+
+
+// - called for SyncML 1.1 if remote wants number of changes.
+// Must return -1 if no NOC value can be returned
+sInt32 TSuperDataStore::getNumberOfChanges(void)
+{
+ sInt32 noc,totalNoc = 0;
+ TSubDSLinkList::iterator pos;
+ for (pos=fSubDSLinks.begin();pos!=fSubDSLinks.end();pos++) {
+ noc = pos->fDatastoreLinkP->getNumberOfChanges();
+ if (noc<0) return -1; // if one of the subdatastores does not know NOC, we can't return a NOC
+ // subdatastore knows its NOC, sum up
+ totalNoc+=noc;
+ }
+ // return sum of all NOCs
+ return totalNoc;
+}; // TSuperDataStore::getNumberOfChanges
+
+
+// show statistics or error of current sync
+void TSuperDataStore::showStatistics(void)
+{
+ // show something in debug log
+ PDEBUGPRINTFX(DBG_HOT,("Superdatastore Sync for '%s' (%s), %s sync status:",
+ getName(),
+ fRemoteViewOfLocalURI.c_str(),
+ fSlowSync ? "slow" : "normal"
+ ));
+ // and on user console
+ CONSOLEPRINTF((""));
+ CONSOLEPRINTF(("- Superdatastore Sync for '%s' (%s), %s sync status:",
+ getName(),
+ fRemoteViewOfLocalURI.c_str(),
+ fSlowSync ? "slow" : "normal"
+ ));
+ // now show results
+ if (isAborted()) {
+ // failed
+ PDEBUGPRINTFX(DBG_ERROR,("Warning: Failed with status code=%hd",fAbortStatusCode));
+ CONSOLEPRINTF((" ************ Failed with status code=%hd",fAbortStatusCode));
+ }
+ else {
+ // successful: show statistics on console
+ PDEBUGPRINTFX(DBG_HOT,("Completed successfully - details see subdatastores"));
+ CONSOLEPRINTF((" Completed successfully - details see subdatastores"));
+ }
+ CONSOLEPRINTF((""));
+} // TSuperDataStore::showStatistics
+
+
+// - returns true if DB implementation of all subdatastores support resume
+bool TSuperDataStore::dsResumeSupportedInDB(void)
+{
+ // yes if all subdatastores support it
+ bool yes=true;
+ TSubDSLinkList::iterator pos;
+ for (pos=fSubDSLinks.begin();pos!=fSubDSLinks.end();pos++) {
+ yes = yes && pos->fDatastoreLinkP->dsResumeSupportedInDB();
+ }
+ return yes;
+} // TSuperDataStore::dsResumeSupportedInDB
+
+
+// helper to save resume state either at end of request or explicitly at reception of a "suspend"
+localstatus TSuperDataStore::engSaveSuspendState(bool aAnyway)
+{
+ // only save here if not aborted already (aborting saves the state immediately)
+ // or explicitly requested
+ if (aAnyway || !isAborted()) {
+ // only save if DS 1.2 and supported by DB
+ if ((fSessionP->getSyncMLVersion()>=syncml_vers_1_2) && dsResumeSupportedInDB()) {
+ PDEBUGBLOCKDESC("SuperSaveSuspendState","Saving state for suspend/resume");
+ // save alert state
+ fResumeAlertCode=fAlertCode;
+ TSubDSLinkList::iterator pos;
+ if (fResumeAlertCode) {
+ // let all subdatastores update partial item and markOnlyUngeneratedForResume()
+ for (pos=fSubDSLinks.begin();pos!=fSubDSLinks.end();pos++) {
+ // save partial state if any
+ if (pos->fDatastoreLinkP->fPartialItemState!=pi_state_save_outgoing) {
+ // ONLY if we have no request for saving an outgoing item state already,
+ // we eventually need to save a pending incoming item
+ // if there is an incompletely received item, let it update Partial Item (fPIxxx) state
+ // (if it is an item of this datastore, that is).
+ if (fSessionP->fIncompleteDataCommandP)
+ fSessionP->fIncompleteDataCommandP->updatePartialItemState(pos->fDatastoreLinkP);
+ }
+ // mark ungenerated
+ pos->fDatastoreLinkP->logicMarkOnlyUngeneratedForResume();
+ }
+ /// @note that already generated items are related to the originating
+ /// localEngineDS, so markPendingForResume() on existing commands will
+ /// directly reach the correct datastore
+ /// @note markItemForResume() will get the localID as presented to
+ /// remote, that is in case of superdatastores prefixed that needs to be removed
+ fSessionP->markPendingForResume(this);
+ }
+ // let all subdatastores logicSaveResumeMarks() to make all this persistent
+ localstatus globErr=LOCERR_OK;
+ for (pos=fSubDSLinks.begin();pos!=fSubDSLinks.end();pos++) {
+ localstatus err=pos->fDatastoreLinkP->logicSaveResumeMarks();
+ if (err!=LOCERR_OK) globErr=err;
+ }
+ PDEBUGENDBLOCK("SuperSaveSuspendState");
+ return globErr;
+ }
+ }
+ return LOCERR_OK;
+} // TSuperDataStore::engSaveSuspendState
+
+
+#ifndef SYSYNC_CLIENT
+
+/// @brief called at end of request processing, should be used to save suspend state
+/// @note subdatastores don't do anything themselves, to make sure superds can make things happen in correct order
+void TSuperDataStore::engRequestEnded(void)
+{
+ // variant for superdatastore - also handles its subdatastores
+ // For DS 1.2: Make sure everything is ready for a resume in case there's an abort (implicit Suspend)
+ // before the next request. Note that the we cannot wait for session timeout, as the resume attempt
+ // from the client probably arrives much earlier.
+ if (testState(dssta_syncmodestable)) {
+ // make sure all unsent items are marked for resume
+ localstatus sta=engSaveSuspendState(false); // only if not already aborted
+ if (sta!=LOCERR_OK) {
+ DEBUGPRINTFX(DBG_ERROR,("Could not save suspend state at end of Request: err=%hd",sta));
+ }
+ }
+ // let datastore prepare for end of request (other than thread change)
+ TSubDSLinkList::iterator pos;
+ for (pos=fSubDSLinks.begin();pos!=fSubDSLinks.end();pos++) {
+ pos->fDatastoreLinkP->dsRequestEnded();
+ }
+ // then let them know that thread may change
+ for (pos=fSubDSLinks.begin();pos!=fSubDSLinks.end();pos++) {
+ pos->fDatastoreLinkP->dsThreadMayChangeNow();
+ }
+} // TSuperDataStore::engRequestEnded
+
+#endif
+
+
+// - called to let server generate sync commands for client
+// Returns true if now finished for this datastore
+// also sets fState to dss_syncdone when finished
+bool TSuperDataStore::engGenerateSyncCommands(
+ TSmlCommandPContainer &aNextMessageCommands,
+ TSmlCommand * &aInterruptedCommandP,
+ const char *aLocalIDPrefix
+)
+{
+ PDEBUGBLOCKFMT(("SuperSyncGen","Now generating sync commands from superdatastore","datastore=%s",getName()));
+ bool finished=false;
+ string prefix;
+
+ while (!isAborted()) {
+ // check for end
+ if (fCurrentGenDSPos==fSubDSLinks.end()) {
+ // done, update status
+ changeState(dssta_syncgendone,true);
+ break;
+ }
+ // create current prefix
+ AssignString(prefix,aLocalIDPrefix);
+ prefix.append(fCurrentGenDSPos->fDSLinkConfigP->fGUIDPrefix);
+ // call subdatastore to generate commands
+ finished=fCurrentGenDSPos->fDatastoreLinkP->engGenerateSyncCommands(
+ aNextMessageCommands,
+ aInterruptedCommandP,
+ prefix.c_str()
+ );
+ // exit if not yet finished with generating commands for this datastore
+ if (!finished) break;
+ // done with this datastore, switch to next if any
+ fCurrentGenDSPos++;
+ } // while not aborted
+ // finished when state is dss_syncdone
+ PDEBUGPRINTFX(DBG_DATA,(
+ "superdatastore's engGenerateSyncCommands ended, state='%s', sync generation %sdone",
+ getDSStateName(),
+ dbgTestState(dssta_syncgendone,true) ? "" : "NOT "
+ ));
+ PDEBUGENDBLOCK("SuperSyncGen");
+ // also finished with this datastore when aborted
+ return (isAborted() || testState(dssta_syncgendone,true));
+} // TSuperDataStore::generateSyncCommands
+
+
+#ifdef SYSYNC_CLIENT
+
+/* %%% not required - engClientStartOfSyncMessage() will be called for all local DS anyway,
+ and superDS will ALWAYS be called after contained subDS, as the superDS's definition
+ is always AFTER the contained subDS's definition in the config
+// called whenever outgoing Message of Sync Package starts
+// - Client will start Sync generation now, which means that subdatastores
+// just set fSyncGenerationStarted=true (without actually creating a <sync>,
+// and superdatastore creates a <sync> which will contain all add/delete/replace
+// from all subdatastores (as generated by generateSyncCommands)
+void TSuperDataStore::engClientStartOfSyncMessage(void)
+{
+ // signal sync start to all subdatastores
+ TSubDSLinkList::iterator pos;
+ for (pos=fSubDSLinks.begin();pos!=fSubDSLinks.end();pos++) {
+ pos->fDatastoreLinkP->engClientStartOfSyncMessage();
+ }
+ // signal it to myself
+ TLocalEngineDS::engClientStartOfSyncMessage();
+} // TSuperDataStore::engClientStartOfSyncMessage
+
+*/
+
+// Client only: returns number of unsent map items
+sInt32 TSuperDataStore::numUnsentMaps(void)
+{
+ // add maps from all subdatastores
+ uInt32 num=0;
+ TSubDSLinkList::iterator pos;
+ for (pos=fSubDSLinks.begin();pos!=fSubDSLinks.end();pos++) {
+ num+=pos->fDatastoreLinkP->numUnsentMaps();
+ }
+ return num;
+} // TSuperDataStore::numUnsentMaps
+
+
+// called to mark maps confirmed, that is, we have received ok status for them
+void TSuperDataStore::engMarkMapConfirmed(cAppCharP aLocalID, cAppCharP aRemoteID)
+{
+ // we must detect the subdatastore by prefix
+ TSubDatastoreLink *linkP = findSubLinkByLocalID(aLocalID);
+ if (linkP) {
+ // pass to subdatastore with prefix removed
+ linkP->engMarkMapConfirmed(aLocalID+linkP->fDSLinkConfigP->fGUIDPrefix.size(),aRemoteID);
+ }
+} // TSuperDataStore::engMarkMapConfirmed
+
+
+// - client only: called to generate Map items
+// Returns true if now finished for this datastore
+// also sets fState to dss_done when finished
+bool TSuperDataStore::engGenerateMapItems(
+ TMapCommand *aMapCommandP,
+ const char *aLocalIDPrefix
+)
+{
+ TSubDSLinkList::iterator pos=fSubDSLinks.begin();
+ bool ok;
+ string prefix;
+
+ PDEBUGBLOCKDESC("SuperMapGenerate","TSuperDataStore: Generating Map items...");
+ do {
+ // check if already done
+ if (pos==fSubDSLinks.end()) break; // done
+ // create current prefix
+ AssignString(prefix,aLocalIDPrefix);
+ prefix.append(fCurrentGenDSPos->fDSLinkConfigP->fGUIDPrefix);
+ // generate Map items
+ ok=pos->fDatastoreLinkP->engGenerateMapItems(TMapCommand *aMapCommandP,prefix.c_str());
+ // exit if not yet finished with generating map items for this datastore
+ if (!ok) {
+ PDEBUGENDBLOCK("MapGenerate");
+ return false; // not all map items generated
+ }
+ // next datastore
+ pos++;
+ } while(true);
+ // done
+ // we are done if state is syncdone (no more sync commands will occur)
+ if (fState==dss_syncsend) {
+ PDEBUGPRINTFX(DBG_PROTO,("TSuperDataStore: Finished sending chached Map items from previous session"))
+ }
+ else if (fState==dss_syncdone) {
+ fState=dss_done;
+ PDEBUGPRINTFX(DBG_PROTO,("TSuperDataStore: Finished generating Map items, server has finished <sync>, we are done now"))
+ }
+ else {
+ PDEBUGPRINTFX(DBG_PROTO,("TSuperDataStore: Finished generating Map items for now, but server still sending <Sync>"))
+ }
+ PDEBUGENDBLOCK("MapGenerate");
+ return true;
+} // TSuperDataStore::engGenerateMapItems
+
+
+#endif
+
+
+/* end of TSuperDataStore implementation */
+
+#endif // SUPERDATASTORES
+
+// eof
diff --git a/src/sysync/superdatastore.h b/src/sysync/superdatastore.h
new file mode 100755
index 0000000..6d1716c
--- /dev/null
+++ b/src/sysync/superdatastore.h
@@ -0,0 +1,322 @@
+/*
+ * File: SuperDataStore.h
+ *
+ * Author: Lukas Zeller (luz@synthesis.ch)
+ *
+ * TSuperDataStore
+ * "Virtual" datastore consisting of an union of other
+ * datastores, for example a vCal datastore based on
+ * two separate vEvent and vTodo datastores.
+ *
+ * Copyright (c) 2002-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ * 2002-08-05 : luz : created
+ *
+ */
+
+#ifndef SuperDataStore_H
+#define SuperDataStore_H
+
+// includes
+#include "configelement.h"
+#include "syncitem.h"
+#include "localengineds.h"
+
+#ifdef SUPERDATASTORES
+
+
+using namespace sysync;
+
+
+namespace sysync {
+
+class TSuperDataStore; // forward
+
+#ifndef OBJECT_FILTERING
+ #error "SUPERDATASTORE needs OBJECT_FILTERING"
+#endif
+
+
+// sub-datastore (contained datastore) link config
+class TSubDSLinkConfig: public TConfigElement
+{
+ typedef TConfigElement inherited;
+public:
+ TSubDSLinkConfig(TLocalDSConfig *aLocalDSConfigP, TConfigElement *aParentElement);
+ virtual ~TSubDSLinkConfig();
+ // properties
+ // - filter applied to test if incoming item is to be processed
+ // by this datastore
+ string fDispatchFilter;
+ // - GUID prefix to create super-GUIDs out of individual datastore GUIDs
+ string fGUIDPrefix;
+ // - linked datastore's config
+ TLocalDSConfig *fLinkedDSConfigP;
+ // - reset config to defaults
+ virtual void clear();
+protected:
+ // check config elements
+ #ifndef HARDCODED_CONFIG
+ virtual bool localStartElement(const char *aElementName, const char **aAttributes, sInt32 aLine);
+ #endif
+}; // TSubDSLinkConfig
+
+
+typedef std::list<TSubDSLinkConfig *> TSubDSConfigList;
+
+
+// sub-datastore link
+typedef struct {
+ TLocalEngineDS *fDatastoreLinkP;
+ TSubDSLinkConfig *fDSLinkConfigP;
+ bool fStartPending;
+} TSubDatastoreLink;
+
+typedef std::list<TSubDatastoreLink> TSubDSLinkList;
+
+
+
+// super datastore config
+class TSuperDSConfig: public TLocalDSConfig
+{
+ typedef TLocalDSConfig inherited;
+public:
+ TSuperDSConfig(const char* aName, TConfigElement *aParentElement);
+ virtual ~TSuperDSConfig();
+ // properties
+ // - contained subdatastores
+ TSubDSConfigList fSubDatastores;
+ // public methods
+ // - create appropriate datastore from config, including creating links to subdatastores
+ virtual TLocalEngineDS *newLocalDataStore(TSyncSession *aSessionP);
+ // - reset config to defaults
+ virtual void clear();
+protected:
+ // check config elements
+ #ifndef HARDCODED_CONFIG
+ virtual bool localStartElement(const char *aElementName, const char **aAttributes, sInt32 aLine);
+ #endif
+ virtual void localResolve(bool aLastPass);
+}; // TSuperDSConfig
+
+
+
+class TSuperDataStore: public TLocalEngineDS
+{
+ typedef TLocalEngineDS inherited;
+private:
+ void InternalResetDataStore(void); // reset for re-use without re-creation
+public:
+ TSuperDataStore(TSuperDSConfig *aDSConfigP, TSyncSession *aSessionP, const char *aName, uInt32 aCommonSyncCapMask=0);
+ virtual ~TSuperDataStore();
+ virtual void dsResetDataStore(void) { InternalResetDataStore(); inherited::dsResetDataStore(); };
+ // abort
+ virtual void engAbortDataStoreSync(TSyError aReason, bool aLocalProblem, bool aResumable=true);
+ virtual bool isAborted(void); // test abort status
+ // Sync operation Methods overriding TLocalEngineDS (which need some distribution to subdatastores)
+ // - process Sync alert for a datastore
+ virtual TAlertCommand *engProcessSyncAlert(
+ TSuperDataStore *aAsSubDatastoreOf, // if acting as subdatastore
+ uInt16 aAlertCode, // the alert code
+ const char *aLastRemoteAnchor, // last anchor of client
+ const char *aNextRemoteAnchor, // next anchor of client
+ const char *aTargetURI, // target URI as sent by remote, no processing at all
+ const char *aIdentifyingTargetURI, // target URI that was used to identify datastore
+ const char *aTargetURIOptions, // option string contained in target URI
+ SmlFilterPtr_t aTargetFilter, ///< DS 1.2 filter, NULL if none
+ const char *aSourceURI, // source URI
+ TStatusCommand &aStatusCommand // status that might be modified
+ );
+ // - process status received for sync alert
+ virtual bool engHandleAlertStatus(TSyError aStatusCode);
+ // - Set remote datastore for local
+ virtual void engSetRemoteDatastore(TRemoteDataStore *aRemoteDatastoreP);
+ // - set Sync types needed for sending local data to remote DB
+ virtual void setSendTypeInfo(
+ TSyncItemType *aLocalSendToRemoteTypeP,
+ TSyncItemType *aRemoteReceiveFromLocalTypeP);
+ // - set Sync types needed for receiving remote data in local DB
+ virtual void setReceiveTypeInfo(
+ TSyncItemType *aLocalReceiveFromRemoteTypeP,
+ TSyncItemType *aRemoteSendToLocalTypeP);
+ // - init usage of datatypes set with setSendTypeInfo/setReceiveTypeInfo
+ virtual localstatus initDataTypeUse(void);
+ // - SYNC command bracket start (check credentials if needed)
+ virtual bool engProcessSyncCmd(SmlSyncPtr_t aSyncP,TStatusCommand &aStatusCommand,bool &aQueueForLater);
+ // - SYNC command bracket end (but another might follow in next message)
+ virtual bool engProcessSyncCmdEnd(bool &aQueueForLater);
+ // - called to process incoming item operation.
+ // Method must take ownership of syncitemP in all cases
+ virtual bool engProcessRemoteItem(
+ TSyncItem *syncitemP,
+ TStatusCommand &aStatusCommand
+ );
+ #ifndef SYSYNC_CLIENT
+ // - called to process map commands from client to server
+ virtual localstatus engProcessMap(cAppCharP aRemoteID, cAppCharP aLocalID);
+ #endif
+ // - must return true if this datastore is finished with <sync>
+ // (if all datastores return true,
+ // session is allowed to finish sync packet with outgoing message
+ virtual bool isSyncDone(void);
+ // - called at very end of sync session, when everything is done
+ virtual void engFinishDataStoreSync(localstatus aErrorStatus=LOCERR_OK);
+
+ // Abstracts of TLocalEngineDS
+protected:
+ // obtain Sync Cap mask, must be lowest common mask of all subdatastores
+ virtual uInt32 getSyncCapMask(void);
+ // Internal events during sync for derived classes
+ // Note: local DB authorisation must be established already before calling these
+ // - prepares for Sync with this datastore
+ // - updates fLastRemoteAnchor,fNextRemoteAnchor,fLastLocalAnchor,fNextLocalAnchor
+ // - called at sync alert, obtains anchor information from local DB
+ // (abstract, must be implemented in derived class)
+ virtual localstatus engInitSyncAnchors(
+ cAppCharP aDatastoreURI, // local datastore URI
+ cAppCharP aRemoteDBID // ID of remote datastore (to find session information in local DB)
+ );
+ // - called at start of first <Sync> command (prepare DB for reading/writing)
+ virtual bool startSync(TStatusCommand &aStatusCommand);
+ /// check is datastore is completely started.
+ /// @param[in] aWait if set, call will not return until either started state is reached
+ /// or cannot be reached within the maximally allowed request processing time left.
+ virtual bool engIsStarted(bool aWait);
+ // - only dummy, creates error if called
+ virtual bool logicRetrieveItemByID(
+ TSyncItem &aSyncItem, // item to be filled with data from server. Local or Remote ID must already be set
+ TStatusCommand &aStatusCommand // status, must be set on error or non-200-status
+ );
+ // - only dummy, creates error if called
+ virtual bool logicProcessRemoteItem(
+ TSyncItem *syncitemP,
+ TStatusCommand &aStatusCommand,
+ bool &aVisibleInSyncset, // on entry: tells if resulting item SHOULD be visible; on exit: set if processed item remains visible in the sync set.
+ string *aGUID=NULL // GUID is stored here if not NULL
+ );
+ // - returns true if DB implementation can filter during database fetch
+ // (otherwise, fetched items will be filtered after being read from DB)
+ virtual bool engFilteredFetchesFromDB(bool aFilterChanged=false);
+ // - called for SyncML 1.1 if remote wants number of changes.
+ // Must return -1 if no NOC value can be returned
+ virtual sInt32 getNumberOfChanges(void);
+ // - called to show sync statistics in debug log and on console
+ SUPERDS_VIRTUAL void showStatistics(void);
+ // - called to generate sync sub-commands
+ // Returns true if now finished for this datastore
+ // also sets fState to dss_syncdone(server)/dss_syncready(client) when finished
+ virtual bool engGenerateSyncCommands(
+ TSmlCommandPContainer &aNextMessageCommands,
+ TSmlCommand * &aInterruptedCommandP,
+ const char *aLocalIDPrefix
+ );
+ // Suspend/Resume
+ // - helper to save resume state either at end of request or explicitly at reception of a "suspend"
+ // (overridden only by superdatastore)
+ virtual localstatus engSaveSuspendState(bool aAnyway);
+ // - returns true if DB implementation supports resume (saving of resume marks, alert code, pending maps, tempGUIDs)
+ virtual bool dsResumeSupportedInDB(void);
+ #ifdef SYSYNC_CLIENT
+ /* %%% not required - these will be called for all local DS anyway,
+ and superDS will ALWAYS be called after contained subDS, as the superDS's definition
+ is always AFTER the contained subDS's definition in the config
+ // client only: called whenever outgoing Message of Sync Package starts
+ // - should start a sync command for all alerted datastores
+ virtual void engClientStartOfSyncMessage(void);
+ // called whenever outgoing Message of Map Package starts
+ virtual void engClientStartOfMapMessage(bool aNotYetInMapPackage);
+ */
+ // Client only: called to generate Map items
+ // - Returns true if now finished for this datastore
+ // - also sets fState to dss_done when finished
+ virtual bool engGenerateMapItems(TMapCommand *aMapCommandP);
+ // Client only: returns number of unsent map items
+ virtual sInt32 numUnsentMaps(void);
+ #ifdef SYSYNC_CLIENT
+ /// client: called to generate sync sub-commands to be sent to server
+ /// Returns true if now finished for this datastore
+ /// also sets fState to dss_syncdone(server)/dss_syncready(client) when finished
+ virtual bool logicGenerateSyncCommandsAsClient(
+ TSmlCommandPContainer &aNextMessageCommands,
+ TSmlCommand * &aInterruptedCommandP,
+ const char *aLocalIDPrefix=NULL
+ ) { return LOCERR_NOTIMP; };
+ // Client only: called to mark maps confirmed, that is, we have received ok status for them
+ virtual void engMarkMapConfirmed(cAppCharP aLocalID, cAppCharP aRemoteID);
+ #endif
+ #else
+ // Server only:
+ /// server: called to generate sync sub-commands to be sent to client
+ /// Returns true if now finished for this datastore
+ /// also sets fState to dss_syncdone(server)/dss_syncready(client) when finished
+ virtual bool logicGenerateSyncCommandsAsServer(
+ TSmlCommandPContainer &aNextMessageCommands,
+ TSmlCommand * &aInterruptedCommandP,
+ const char *aLocalIDPrefix=NULL
+ ) { return LOCERR_NOTIMP; };
+ // - only dummy, creates error if called
+ virtual localstatus logicProcessMap(cAppCharP aRemoteID, cAppCharP aLocalID) { return false; }; // dummy, should never be called
+ // - called at end of request processing in server
+ virtual void engRequestEnded(void);
+ // Dummies, should never be called in Superdatastore, as all DB processing takes
+ // place in subdatastores
+ // - called to check if conflicting replace command from server exists
+ virtual TSyncItem *getConflictingItemByRemoteID(TSyncItem *syncitemP) { return NULL; };
+ // - called to check if content-matching item from server exists
+ virtual TSyncItem *getMatchingItem(TSyncItem *syncitemP, TEqualityMode aEqMode) { return NULL; };
+ // - called to prevent item to be sent to client in subsequent engGenerateSyncCommands()
+ // item in question should be an item that was returned by getConflictingItemByRemoteID() or getMatchingItem()
+ virtual void dontSendItemAsServer(TSyncItem *syncitemP) {};
+ // - called when a item in the sync set changes its localID (due to local DB internals)
+ // NOTE: this is not called for superdatastores, only real datastores, so it's NOP here
+ virtual void dsLocalIdHasChanged(const char *aOldID, const char *aNewID) {};
+ // - called to have additional item sent to remote (DB takes ownership of item)
+ virtual void SendItemAsServer(TSyncItem *aSyncitemP) {};
+ #endif
+ /// called to have all non-yet-generated sync commands as "to-be-resumed"
+ virtual void logicMarkOnlyUngeneratedForResume(void) {};
+ /// called to mark an already generated (but probably not sent or not yet statused) item
+ /// as "to-be-resumed", by localID or remoteID (latter only in server case).
+ /// @note This must be repeatable without side effects, as server must mark/save suspend state
+ /// after every request (and not just at end of session)
+ virtual void logicMarkItemForResume(cAppCharP aLocalID, cAppCharP aRemoteID, bool aUnSent) {};
+ /// called to mark an already generated (but probably not sent or not yet statused) item
+ /// as "to-be-resumed", by localID or remoteID (latter only in server case).
+ /// @note This must be repeatable without side effects, as server must mark/save suspend state
+ /// after every request (and not just at end of session)
+ virtual void logicMarkItemForResend(cAppCharP aLocalID, cAppCharP aRemoteID) {};
+ /// save status information required to eventually perform a resume (as passed to datastore with
+ /// markOnlyUngeneratedForResume() and markItemForResume())
+ /// (or, in case the session is really complete, make sure that no resume state is left)
+ /// @note Must also save tempGUIDs (for server) and pending/unconfirmed maps (for client)
+ virtual localstatus logicSaveResumeMarks(void) { return LOCERR_NOTIMP; }; // must be derived (or avoided, as in superdatastore)
+public:
+ /// remove prefix for given subDatastore
+ /// @param[in] aIDWithPrefix points to ID with prefix
+ /// @return NULL if not found
+ /// @return pointer to first char in aIDWithPrefix which is not part of the prefix
+ cAppCharP removeSubDSPrefix(cAppCharP aIDWithPrefix, TLocalEngineDS *aLocalDatastoreP);
+private:
+ // find subdatastore which matches prefix of given localID
+ TSubDatastoreLink *findSubLinkByLocalID(const char *aLocalID);
+ // find subdatastore which can accept item data
+ TSubDatastoreLink *findSubLinkByData(TSyncItem &aSyncItem);
+ /** @deprecated moved to localEngineDS
+ // combined first-time sync flag (collected from subdatastores)
+ bool fFirstTimeSync;
+ */
+ // this flag is set at alert and cleared after all subdatastores have fully initialized
+ bool fSuperStartPending;
+ // list of subdatastores
+ TSubDSLinkList fSubDSLinks;
+ // currently generating subdatastore
+ TSubDSLinkList::iterator fCurrentGenDSPos;
+}; // TSuperDataStore
+
+} // namespace sysync
+
+#endif // SUPERDATASTORES
+
+#endif // SuperDataStore_H
+
+// eof
diff --git a/src/sysync/syncappbase.cpp b/src/sysync/syncappbase.cpp
new file mode 100755
index 0000000..13c6840
--- /dev/null
+++ b/src/sysync/syncappbase.cpp
@@ -0,0 +1,3545 @@
+/*
+ * TSyncAppBase
+ * Base class for SySync applications, is supposed to exist
+ * as singular object only, manages "global" things such
+ * as config reading and session dispatching
+ *
+ * Copyright (c) 2002-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ * 2002-03-06 : luz : Created
+ */
+
+#include "prefix_file.h"
+
+#include "sysync.h"
+#include "syncappbase.h"
+#include "scriptcontext.h"
+#include "iso8601.h"
+#include "multifielditem.h" // in case we have no scripts...
+
+#ifdef SYSER_REGISTRATION
+#include "syserial.h"
+#endif
+
+#ifdef DIRECT_APPBASE_GLOBALACCESS
+#include "sysync_glob_vars.h"
+#endif
+
+
+
+#include "global_progress.h" // globally accessible progress event posting
+
+#ifndef ENGINE_LIBRARY
+
+// can be called globally to post progress events
+extern "C" int GlobalNotifyProgressEvent (
+ TProgressEventType aEventType,
+ sInt32 aExtra1,
+ sInt32 aExtra2,
+ sInt32 aExtra3
+)
+{
+ #ifdef PROGRESS_EVENTS
+ TSyncAppBase *baseP = getExistingSyncAppBase();
+ if (baseP) {
+ return baseP->NotifyProgressEvent(aEventType,NULL,aExtra1,aExtra2,aExtra3);
+ }
+ #endif
+ return true; // not aborted
+} // GlobalNotifyProgressEvent
+
+#endif // not ENGINE_LIBRARY
+
+
+namespace sysync {
+
+#ifndef HARDCODED_CONFIG
+
+// SyncML encoding names for end user config
+const char * const SyncMLEncodingNames[numSyncMLEncodings] = {
+ "undefined",
+ "wbxml",
+ "xml"
+};
+
+#endif
+
+// SyncML encoding names for MIME type
+const char * const SyncMLEncodingMIMENames[numSyncMLEncodings] = {
+ "undefined",
+ SYNCML_ENCODING_WBXML,
+ SYNCML_ENCODING_XML
+};
+
+
+#if !defined(HARDCODED_CONFIG) || defined(ENGINEINTERFACE_SUPPORT)
+
+// platform string names for accessing them as config variables
+const char * const PlatformStringNames[numPlatformStrings] = {
+ "platformvers", // version string of the current platform
+ "globcfg_path", // global system-wide config path (such as C:\Windows or /etc)
+ "loccfg_path", // local config path (such as exedir or user's dir)
+ "defout_path", // default path to writable directory to write logs and other output by default
+ "temp_path", // path where we can write temp files
+ "exedir_path", // path to directory where executable resides
+ "userdir_path", // path to the user's home directory for user-visible documents and files
+ "appdata_path", // path to the user's preference directory for this application
+ "prefs_path", // path to directory where all application prefs reside (not just mine)
+ "device_uri", // URI of the device (as from getDeviceInfo)
+ "device_name", // Name of the device (as from getDeviceInfo)
+ "user_name", // Name of the currently logged in user
+};
+
+#endif
+
+
+#ifdef ENGINEINTERFACE_SUPPORT
+
+ #ifdef DIRECT_APPBASE_GLOBALACCESS
+ // Only for old-style targets that still use a global anchor
+
+ #ifdef ENGINE_LIBRARY
+ #error "Engine Library may NOT access appBase via global anchor any more!"
+ #endif
+
+
+ // With enginemodulebase support, the global anchor is the enginebase module,
+ // not syncappbase itself.
+
+ #define GET_SYNCAPPBASE (((TEngineInterface *)sysync_glob_anchor())->getSyncAppBase())
+
+ // get access to existing Sync app base object, NULL if none (i.e. if no TEngineInterface)
+ TSyncAppBase *getExistingSyncAppBase(void)
+ {
+ TEngineInterface *eng = (TEngineInterface *)sysync_glob_anchor();
+ return eng ? eng->getSyncAppBase() : NULL;
+ } // getExistingSyncAppBase
+
+ // global function, returns pointer to (singular) app base
+ // object. With EngineInterface, this is ALWAYS a member of
+ // TEngineInterface, so we create the global engineinterface if we don't have it
+ // already.
+ TSyncAppBase *getSyncAppBase(void)
+ {
+ TSyncAppBase *appBase = getExistingSyncAppBase();
+ if (appBase) return appBase;
+ // no appBase yet, we must create and anchor the TEngineInterface
+ ENGINE_IF_CLASS *engine = sysync::newEngine();
+ // we must init the engine to trigger creation of the appbase!
+ if (engine) engine->Init();
+ return GET_SYNCAPPBASE;
+ } // getSyncAppBase
+
+ TEngineInterface *getEngineInterface(void)
+ {
+ return (TEngineInterface *)sysync_glob_anchor();
+ } // getEngineInterface
+
+ // free Sync Session Dispatcher
+ void freeSyncAppBase(void)
+ {
+ TEngineInterface *eng = getEngineInterface();
+ if (eng) {
+ // kills interface, and also kills syncappbase in the process
+ delete eng;
+ }
+ }
+
+ #endif // DIRECT_APPBASE_GLOBALACCESS
+
+
+#else // ENGINEINTERFACE_SUPPORT
+
+ // Old style without enginemodulebase: the global anchor is syncappbase
+
+ #define GET_SYNCAPPBASE ((TSyncAppBase *)sysync_glob_anchor())
+
+
+ // global function, returns pointer to (singular) app base
+ // object. If not yet existing, a new app base is created
+ TSyncAppBase *getSyncAppBase(void)
+ {
+ if (!sysync_glob_anchor()) {
+ // no dispatcher exists, create new one
+ // (using function which will create derived app
+ // specific dispatcher)
+ sysync_glob_setanchor(newSyncAppBase());
+ }
+ // dispatcher now exists, use it
+ return GET_SYNCAPPBASE;
+ } // getSyncAppBase
+
+
+ // get access to existing Sync app base object, NULL if none
+ TSyncAppBase *getExistingSyncAppBase(void)
+ {
+ return GET_SYNCAPPBASE;
+ } // getExistingSyncAppBase
+
+
+ // free Sync Session Dispatcher
+ void freeSyncAppBase(void)
+ {
+ if (sysync_glob_anchor()) {
+ // object exists, kill it now
+ delete GET_SYNCAPPBASE;
+ sysync_glob_setanchor(NULL);
+ }
+ }
+
+#endif // not ENGINEINTERFACE_SUPPORT
+
+
+#ifdef SYDEBUG
+
+// static routines for accessing appbase logs from UI_Call_In/DB_Callback
+
+extern "C" void AppBaseLogDebugPuts(void *aCallbackRef, const char *aText)
+{
+ if (aCallbackRef) {
+ POBJDEBUGPUTSX(static_cast<TSyncAppBase *>(aCallbackRef),DBG_DBAPI+DBG_PLUGIN,aText);
+ }
+} // AppBaseLogDebugPuts
+
+
+extern "C" void AppBaseLogDebugExotic(void *aCallbackRef, const char *aText)
+{
+ if (aCallbackRef) {
+ POBJDEBUGPUTSX(static_cast<TSyncAppBase *>(aCallbackRef),DBG_DBAPI+DBG_PLUGIN+DBG_EXOTIC,aText);
+ }
+} // AppBaseLogDebugExotic
+
+
+extern "C" void AppBaseLogDebugBlock(void *aCallbackRef, const char *aTag, const char *aDesc, const char *aAttrText )
+{
+ if (aCallbackRef) {
+ bool collapsed=false;
+ if (aTag && aTag[0]=='-') { aTag++; collapsed=true; }
+ static_cast<TSyncAppBase *>(aCallbackRef)->getDbgLogger()->DebugOpenBlock(aTag,aDesc,collapsed,"%s",aAttrText);
+ }
+} // AppBaseLogDebugBlock
+
+
+extern "C" void AppBaseLogDebugEndBlock(void *aCallbackRef, const char *aTag)
+{
+ if (aCallbackRef) {
+ if (aTag && aTag[0]=='-') aTag++;
+ static_cast<TSyncAppBase *>(aCallbackRef)->getDbgLogger()->DebugCloseBlock(aTag);
+ }
+} // AppBaseLogDebugEndBlock
+
+
+extern "C" void AppBaseLogDebugEndThread(void *aCallbackRef)
+{
+ if (aCallbackRef) {
+ static_cast<TSyncAppBase *>(aCallbackRef)->getDbgLogger()->DebugThreadOutputDone(true); // remove thread record for global threads
+ }
+} // AppBaseLogDebugEndThread
+
+#endif
+
+
+// root config constructor
+TRootConfig::TRootConfig(TSyncAppBase *aSyncAppBaseP) :
+ TRootConfigElement(aSyncAppBaseP),
+ fCommConfigP(NULL),
+ fAgentConfigP(NULL),
+ fDatatypesConfigP(NULL)
+ #ifdef SCRIPT_SUPPORT
+ , fScriptConfigP(NULL)
+ #endif
+ #ifdef SYDEBUG
+ , fDebugConfig("debug",this) // init static debug config member
+ #endif
+{
+ // config date is unknown so far
+ fConfigDate=0;
+ #ifndef HARDCODED_CONFIG
+ // string for identifying config file in logs
+ fConfigIDString="<none>";
+ #endif
+} // TRootConfig::TRootConfig
+
+
+TRootConfig::~TRootConfig()
+{
+ // clear linked
+ if (fAgentConfigP) delete fAgentConfigP;
+ if (fDatatypesConfigP) delete fDatatypesConfigP;
+ if (fCommConfigP) delete fCommConfigP;
+ #ifdef SCRIPT_SUPPORT
+ if (fScriptConfigP) delete fScriptConfigP;
+ #endif
+} // TRootConfig::~TRootConfig
+
+
+void TRootConfig::clear(void)
+{
+ // root config variables
+ // - init PUT suppression
+ fNeverPutDevinf=false;
+ // - init message size
+ fLocalMaxMsgSize=DEFAULT_MAXMSGSIZE;
+ fLocalMaxObjSize=DEFAULT_MAXOBJSIZE;
+ // - system time zone
+ fSystemTimeContext=TCTX_SYSTEM; // default to automatic detection
+ #ifdef ENGINEINTERFACE_SUPPORT
+ // - default identification
+ fMan.clear();
+ fMod.clear();
+ #endif
+ // - init device limit
+ #ifdef CUSTOMIZABLE_DEVICES_LIMIT
+ fConcurrentDeviceLimit=CONCURRENT_DEVICES_LIMIT;
+ #endif
+ // remove, (re-)create and clear linked config branches (debug last to allow dbg output while clearing)
+ // - global scripting config
+ #ifdef SCRIPT_SUPPORT
+ if (fScriptConfigP) delete fScriptConfigP;
+ fScriptConfigP= new TScriptConfig(this);
+ if (fScriptConfigP) fScriptConfigP->clear();
+ #endif
+ // - communication config
+ if (fCommConfigP) delete fCommConfigP;
+ installCommConfig();
+ if (fCommConfigP) fCommConfigP->clear();
+ // - datatypes registry config
+ if (fDatatypesConfigP) delete fDatatypesConfigP;
+ installDatatypesConfig();
+ if (fDatatypesConfigP) fDatatypesConfigP->clear();
+ // - agent config
+ if (fAgentConfigP) delete fAgentConfigP;
+ installAgentConfig();
+ if (fAgentConfigP) fAgentConfigP->clear();
+ // clear embedded debug config (as the last action)
+ #ifdef SYDEBUG
+ fDebugConfig.clear();
+ #endif
+ // clear inherited
+ inherited::clear();
+} // TRootConfig::clear
+
+
+// save app state (such as settings in datastore configs etc.)
+void TRootConfig::saveAppState(void)
+{
+ if (fAgentConfigP) fAgentConfigP->saveAppState();
+ if (fDatatypesConfigP) fDatatypesConfigP->saveAppState();
+ if (fCommConfigP) fCommConfigP->saveAppState();
+} // TRootConfig::saveAppState
+
+
+// MUST be called after creating config to load (or pre-load) variable parts of config
+// such as binfile profiles. If aDoLoose==false, situations, where existing config
+// is detected but cannot be re-used will return an error. With aDoLoose==true, config
+// files etc. are created even if it means a loss of data.
+localstatus TRootConfig::loadVarConfig(bool aDoLoose)
+{
+ // only agent may load variable config
+ if (fAgentConfigP)
+ return fAgentConfigP->loadVarConfig(aDoLoose);
+ else
+ return LOCERR_NOCFG; // no config yet
+} // TRootConfig::loadVarConfig
+
+
+
+
+#ifndef HARDCODED_CONFIG
+
+// root config element parsing
+bool TRootConfig::localStartElement(const char *aElementName, const char **aAttributes, sInt32 aLine)
+{
+ // debug
+ if (strucmp(aElementName,"debug")==0) {
+ #ifdef SYDEBUG
+ // let static TDebugConfig member handle it
+ expectChildParsing(fDebugConfig);
+ #else
+ ReportError(false,"No debugging features available in this version");
+ expectAll(); // no debug code, simply ignore settings
+ #endif
+ }
+ // config ID/date
+ else if (strucmp(aElementName,"configdate")==0)
+ expectTimestamp(fConfigDate);
+ else if (strucmp(aElementName,"configidstring")==0)
+ expectMacroString(fConfigIDString);
+ else
+ #ifdef ENGINEINTERFACE_SUPPORT
+ // - product identification (in devInf)
+ if (strucmp(aElementName,"manufacturer")==0)
+ expectMacroString(fMan);
+ else if (strucmp(aElementName,"model")==0)
+ expectMacroString(fMod);
+ else if (strucmp(aElementName,"HardwareVersion")==0)
+ expectMacroString(fHwV);
+ else if (strucmp(aElementName,"FirmwareVersion")==0)
+ expectMacroString(fFwV);
+ else if (strucmp(aElementName,"DeviceType")==0)
+ expectMacroString(fDevTyp);
+ else
+ #endif
+ // config variables
+ if (strucmp(aElementName,"configvar")==0) {
+ const char *nam = getAttr(aAttributes,"name");
+ if (!nam)
+ return fail("Missing 'name' attribute in 'configvar'");
+ string val;
+ if (!getAttrExpanded(aAttributes,"value",val,false))
+ return fail("Missing 'value' attribute in 'configvar'");
+ getSyncAppBase()->setConfigVar(nam,val.c_str());
+ expectEmpty();
+ }
+ // options
+ else if (strucmp(aElementName,"neverputdevinf")==0)
+ expectBool(fNeverPutDevinf);
+ else if (strucmp(aElementName,"maxmsgsize")==0)
+ expectUInt32(fLocalMaxMsgSize);
+ else if (strucmp(aElementName,"maxobjsize")==0)
+ expectUInt32(fLocalMaxObjSize);
+ else if (strucmp(aElementName,"maxconcurrentsessions")==0) {
+ #ifdef CUSTOMIZABLE_DEVICES_LIMIT
+ expectInt32(fConcurrentDeviceLimit);
+ #else
+ expectAll(); // no configurable limit, simply ignore contents
+ #endif
+ }
+ // time zones
+ else if (strucmp(aElementName,"definetimezone")==0)
+ expectVTimezone(getSyncAppBase()->getAppZones()); // definition of custom time zone
+ else if (strucmp(aElementName,"systemtimezone")==0)
+ expectTimezone(fSystemTimeContext);
+ // license
+ else if (strucmp(aElementName,"licensename")==0) {
+ #ifdef SYSER_REGISTRATION
+ expectString(fLicenseName);
+ #else
+ expectAll(); // simply ignore contents for versions w/o registration
+ #endif
+ }
+ else if (strucmp(aElementName,"licensecode")==0) {
+ #ifdef SYSER_REGISTRATION
+ expectString(fLicenseCode);
+ #else
+ expectAll(); // simply ignore contents for versions w/o registration
+ #endif
+ }
+ #ifdef SCRIPT_SUPPORT
+ else if (strucmp(aElementName,"scripting")==0) {
+ // let linked TScriptConfig handle it
+ expectChildParsing(*fScriptConfigP);
+ }
+ #endif
+ else
+ // Agent: Server or client
+ #ifdef SYSYNC_CLIENT
+ if (strucmp(aElementName,"client")==0)
+ #else
+ if (strucmp(aElementName,"server")==0)
+ #endif
+ {
+ // Agent config
+ if (!fAgentConfigP) return false;
+ return parseAgentConfig(aAttributes, aLine);
+ }
+ // Transport
+ else if (strucmp(aElementName,"transport")==0) {
+ // transport config
+ if (!fCommConfigP) return false;
+ if (!parseCommConfig(aAttributes, aLine)) {
+ // not a transport we can understand, simply ignore
+ expectAll();
+ }
+ }
+ else if (strucmp(aElementName,"datatypes")==0) {
+ // datatypes (type registry) config
+ if (!fDatatypesConfigP) return false;
+ return parseDatatypesConfig(aAttributes, aLine);
+ }
+ else {
+ // invalid element
+ return false;
+ }
+ // ok
+ return true;
+} // TRootConfig::localStartElement
+#endif
+
+
+// resolve (finish after all data is parsed)
+void TRootConfig::localResolve(bool aLastPass)
+{
+ // make sure static debug element is resolved so
+ // eventual debug information created by resolving other
+ // elements go to the correct locations/files
+ // Note: in XML configs, the debug element is resolved immediately
+ // after parsing (has fResolveImmediately set) and will
+ // not be re-resolved here unless the element was not parsed at all.
+ #if defined(SYDEBUG) && !defined(SYSYNC_TOOL)
+ // - for SysyTool, do not resolve here as we don't want to see all of the
+ // DBG blurb on the screen created by resolving the config
+ fDebugConfig.Resolve(aLastPass);
+ #endif
+ // set zone for system if one was defined explicitly
+ if (!TCTX_IS_SYSTEM(fSystemTimeContext) && !TCTX_IS_UNKNOWN(fSystemTimeContext)) {
+ getSyncAppBase()->getAppZones()->predefinedSysTZ = fSystemTimeContext;
+ getSyncAppBase()->getAppZones()->ResetCache(); // make sure next query for SYSTEM tz will get new set zone
+ }
+
+ // MaxMessagesize must have a reasonable size
+ if (fLocalMaxMsgSize<512) {
+ SYSYNC_THROW(TConfigParseException("<maxmsgsize> must be at least 512 bytes"));
+ }
+ // make sure we have the registration info vars updated
+ #ifdef APP_CAN_EXPIRE
+ getSyncAppBase()->updateAppExpiry();
+ #elif defined(SYSER_REGISTRATION)
+ getSyncAppBase()->isRegistered();
+ #endif
+ // make sure linked elements are resolved
+ #ifdef SCRIPT_SUPPORT
+ if (fScriptConfigP) fScriptConfigP->Resolve(aLastPass);
+ #endif
+ if (fAgentConfigP) fAgentConfigP->Resolve(aLastPass);
+ if (fDatatypesConfigP) fDatatypesConfigP->Resolve(aLastPass);
+ if (fCommConfigP) fCommConfigP->Resolve(aLastPass);
+ // finally, get rid of macros (all scripts are now read)
+ #ifdef SCRIPT_SUPPORT
+ if (fScriptConfigP && aLastPass) fScriptConfigP->clearmacros();
+ #endif
+ #if defined(SYDEBUG) && defined(SYSYNC_TOOL)
+ // - for SysyTool, resolve now, where resolving dbg output has gone /dev/null already
+ fDebugConfig.Resolve(aLastPass);
+ #endif
+} // TRootConfig::localResolve
+
+
+// Base datatype config
+
+// init defaults
+void TDataTypeConfig::clear(void)
+{
+ // clear properties
+ fTypeName.erase(); // no type
+ fTypeVersion.erase(); // no version
+ #ifdef ZIPPED_BINDATA_SUPPORT
+ fZippedBindata=false;
+ fZipCompressionLevel=-1; // valid range is 0-9, invalid value will select Z_DEFAULT_COMPRESSION
+ #endif
+ fBinaryParts=false; // no binary parts
+ fUseUTF16=false; // no UTF-16/Unicode translation
+ fMSBFirst=false; // default to Intel byte order for UTF16
+ // clear inherited
+ inherited::clear();
+} // TMIMEDirTypeConfig::clear
+
+
+#ifdef CONFIGURABLE_TYPE_SUPPORT
+
+// config element parsing
+bool TDataTypeConfig::localStartElement(const char *aElementName, const char **aAttributes, sInt32 aLine)
+{
+ // checking the elements
+ if (strucmp(aElementName,"typestring")==0)
+ expectString(fTypeName);
+ else if (strucmp(aElementName,"versionstring")==0)
+ expectString(fTypeVersion);
+ else if (strucmp(aElementName,"binaryparts")==0)
+ expectBool(fBinaryParts);
+ #ifdef ZIPPED_BINDATA_SUPPORT
+ else if (strucmp(aElementName,"zippedbindata")==0)
+ expectBool(fZippedBindata);
+ else if (strucmp(aElementName,"zipcompressionlevel")==0)
+ expectInt16(fZipCompressionLevel);
+ #endif
+ else if (strucmp(aElementName,"unicodedata")==0)
+ expectBool(fUseUTF16);
+ else if (strucmp(aElementName,"bigendian")==0)
+ expectBool(fMSBFirst);
+ // - none known here
+ else
+ return false; // base class is TConfigElement
+ // ok
+ return true;
+} // TDataTypeConfig::localStartElement
+
+
+// resolve
+void TDataTypeConfig::localResolve(bool aLastPass)
+{
+ // check
+ if (aLastPass) {
+ // Note: type strings might be set explicitly or implicitly by derived classes
+ // Note2: all types must have a version (SCTS will fail without). For example
+ // Starfish's text/plain notes have Version "1.0"
+ if (fTypeName.empty() || fTypeVersion.empty() )
+ SYSYNC_THROW(TConfigParseException("datatype must have non-empty 'typestring' and 'versionstring'"));
+ }
+} // TDataTypeConfig::localResolve
+
+#endif
+
+
+
+// Datatype registry
+
+TDatatypesConfig::TDatatypesConfig(const char* aName, TConfigElement *aParentElement) :
+ TConfigElement(aName,aParentElement)
+{
+ clear();
+} // TDatatypesConfig::TDatatypesConfig
+
+
+TDatatypesConfig::~TDatatypesConfig()
+{
+ clear();
+} // TDatatypesConfig::~TDatatypesConfig
+
+
+// init defaults
+void TDatatypesConfig::clear(void)
+{
+ // remove datatypes
+ TDataTypesList::iterator pos;
+ for(pos=fDataTypesList.begin();pos!=fDataTypesList.end();pos++)
+ delete *pos;
+ fDataTypesList.clear();
+ // clear inherited
+ inherited::clear();
+} // TDatatypesConfig::clear
+
+
+TDataTypeConfig *TDatatypesConfig::getDataType(const char *aName)
+{
+ TDataTypesList::iterator pos;
+ for(pos=fDataTypesList.begin();pos!=fDataTypesList.end();pos++) {
+ if (strucmp((*pos)->getName(),aName)==0) {
+ // found
+ return *pos;
+ }
+ }
+ return NULL; // not found
+} // TDatatypesConfig::getDataType
+
+
+#ifdef CONFIGURABLE_TYPE_SUPPORT
+
+// config element parsing
+bool TDatatypesConfig::localStartElement(const char *aElementName, const char **aAttributes, sInt32 aLine)
+{
+ // checking the elements
+ if (strucmp(aElementName,"datatype")==0) {
+ // definition of a new data type
+ // - get name
+ const char *nam = getAttr(aAttributes,"name");
+ if (!nam)
+ return fail("Missing 'name' attribute in 'datatype'");
+ // - get basetype
+ const char *basetype = getAttr(aAttributes,"basetype");
+ if (!basetype)
+ return fail("Missing 'basetype' attribute in 'datatype'");
+ // - call global function to get correct TDataTypeConfig derived object
+ TDataTypeConfig *datatypeP = getSyncAppBase()->getRootConfig()->newDataTypeConfig(nam,basetype,this);
+ if (!datatypeP)
+ return fail("Unknown basetype '%s'",basetype);
+ // - save in list
+ fDataTypesList.push_back(datatypeP);
+ // - let element handle parsing
+ expectChildParsing(*datatypeP);
+ }
+ // - none known here
+ else
+ return inherited::localStartElement(aElementName,aAttributes,aLine);
+ // ok
+ return true;
+} // TDatatypesConfig::localStartElement
+
+#endif
+
+
+// resolve
+void TDatatypesConfig::localResolve(bool aLastPass)
+{
+ // resolve all types in list
+ TDataTypesList::iterator pos;
+ for(pos=fDataTypesList.begin();pos!=fDataTypesList.end();pos++)
+ (*pos)->Resolve(aLastPass);
+ // resolve inherited
+ inherited::localResolve(aLastPass);
+} // TDatatypesConfig::localResolve
+
+
+
+#ifdef SYDEBUG
+
+// debug config constructor
+TDebugConfig::TDebugConfig(const char *aElementName, TConfigElement *aParentElementP) :
+ TConfigElement(aElementName,aParentElementP)
+{
+ // do not call clear(), because this is virtual!
+} // TDebugConfig::TDebugConfig
+
+
+void TDebugConfig::clear(void)
+{
+ // set defaults
+ fGlobalDbgLoggerOptions.clear(); // set logger options to defaults
+ fSessionDbgLoggerOptions.clear(); // set logger options to defaults
+ fDebug=DEFAULT_DEBUG; // if <>0 (and #defined SYDEBUG), debug output is generated, value is used as mask
+ fMsgDump=DEFAULT_MSGDUMP; // if set (and #defined MSGDUMP), messages sent and received are logged;
+ fSingleGlobLog=false; // create a separate global log per app start
+ fSingleSessionLog=false; // create separate session logs
+ fTimedSessionLogNames=true; // add session start time into file name
+ fXMLtranslate=DEFAULT_XMLTRANSLATE; // if set, communication will be translated to XML and logged
+ fSimMsgRead=DEFAULT_SIMMSGREAD; // if set (and #defined SIMMSGREAD), simulated input with "i_" prefixed incoming messages are supported
+ fGlobalDebugLogs=DEFAULT_GLOBALDEBUGLOGS;
+ fSessionDebugLogs=DEFAULT_SESSIONDEBUGLOGS;
+ if (getPlatformString(pfs_defout_path,fDebugInfoPath))
+ makeOSDirPath(fDebugInfoPath);
+ else
+ fDebugInfoPath.erase(); // none
+ // make sure that defaults are applied a first time NOW, BEFORE reading first config
+ localResolve(true);
+ #ifndef SYSYNC_TOOL
+ // and make sure final resolve takes place early when <debug> element finishes parsing
+ // (but not for SYSYNC_TOOL, where we want no debug output at all during config read & resolve!)
+ fResolveImmediately=true;
+ #endif
+ // clear inherited
+ inherited::clear();
+} // TDebugConfig::clear
+
+
+// resolve (finish after all data is parsed)
+void TDebugConfig::localResolve(bool aLastPass)
+{
+ if (aLastPass) {
+ #ifdef SYDEBUG
+ // we have debug
+ #ifndef HARDCODED_CONFIG
+ // XML config - user options settings are parsed into fSessionDbgLoggerOptions
+ // - by default, global logging has same options as configured for session...
+ fGlobalDbgLoggerOptions = fSessionDbgLoggerOptions;
+ // ...but we have a few hard-coded things for global logging:
+ #ifdef MULTITHREAD_PIPESERVER
+ // - for pipe server, global logs should be per-thread
+ fGlobalDbgLoggerOptions.fFlushMode=dbgflush_flush; // flush every log line
+ fGlobalDbgLoggerOptions.fSubThreadMode=dbgsubthread_separate; // separate per thread
+ fGlobalDbgLoggerOptions.fTimestampForAll=true; // timestamp for every message
+ #else
+ #ifndef SYSYNC_CLIENT
+ // - global log for ISAPI/XPT *Servers* is always in openclose mode (possibly multiple processes accessing it)
+ fGlobalDbgLoggerOptions.fFlushMode=dbgflush_openclose; // open and close for every log line
+ #endif
+ fGlobalDbgLoggerOptions.fSubThreadMode=dbgsubthread_linemix; // mix in one file
+ #ifdef MULTI_THREAD_SUPPORT
+ fGlobalDbgLoggerOptions.fThreadIDForAll=true; // thread ID for each message
+ #endif
+ #endif
+ #endif
+ // initialize global debug logging
+ getSyncAppBase()->fAppLogger.setMask(fDebug); // set initial debug mask from config
+ getSyncAppBase()->fAppLogger.setEnabled(fGlobalDebugLogs); // init from config
+ getSyncAppBase()->fAppLogger.setOptions(&fGlobalDbgLoggerOptions);
+ getSyncAppBase()->fAppLogger.installOutput(getSyncAppBase()->newDbgOutputter(true)); // install the output object (and pass ownership!) // %%% later request this from sessionbase which can return the appropriate platform-adapted type
+ getSyncAppBase()->fAppLogger.setDebugPath(fDebugInfoPath.c_str()); // global log all in one file
+ getSyncAppBase()->fAppLogger.appendToDebugPath(TARGETID);
+ if (fSingleGlobLog) {
+ // One single log - in this case, we MUST append to current log
+ fGlobalDbgLoggerOptions.fAppend=true;
+ }
+ else {
+ // create a new global log for each app start
+ getSyncAppBase()->fAppLogger.appendToDebugPath("_");
+ string t;
+ TimestampToISO8601Str(t, getSyncAppBase()->getSystemNowAs(TCTX_UTC), TCTX_UTC, false, false);
+ getSyncAppBase()->fAppLogger.appendToDebugPath(t.c_str());
+ getSyncAppBase()->fAppLogger.appendToDebugPath("_global");
+ }
+ // define this as the main thread
+ getSyncAppBase()->fAppLogger.DebugDefineMainThread();
+ #endif
+ }
+}; // TDebugConfig::localResolve
+
+
+#ifndef HARDCODED_CONFIG
+
+// debug option (combination) names
+const char * const debugOptionNames[numDebugOptions] = {
+ // current categories
+ "hot",
+ "error",
+ "data",
+ "admin",
+ "syncml",
+ "remoteinfo",
+ "parse",
+ "generate",
+ "rtk_sml",
+ "rtk_xpt",
+ "session",
+ "lock",
+ "objinst",
+ "transp",
+ "scripts",
+ "profiling",
+ "rest",
+
+ // flags mostly (not always) used in combination with some of the basic categories
+ "userdata",
+ "dbapi",
+ "plugin",
+ "filter",
+ "match",
+ "conflict",
+ "details",
+ "exotic",
+ "expressions",
+
+ // useful sets
+ "all",
+ "minimal",
+ "normal",
+ "extended",
+ "maximal",
+ "db",
+ "syncml_rtk",
+
+ // old ones
+ "items",
+ "cmd",
+ "devinf",
+ "dataconf"
+};
+
+const uInt32 debugOptionMasks[numDebugOptions] = {
+ // current categories
+ DBG_HOT,
+ DBG_ERROR,
+ DBG_DATA,
+ DBG_ADMIN,
+ DBG_PROTO,
+ DBG_REMOTEINFO,
+ DBG_PARSE,
+ DBG_GEN,
+ DBG_RTK_SML,
+ DBG_RTK_XPT,
+ DBG_SESSION,
+ DBG_LOCK,
+ DBG_OBJINST,
+ DBG_TRANSP,
+ DBG_SCRIPTS,
+ DBG_PROFILE,
+ DBG_REST,
+
+ // flags mostly (not always) used in combination with some of the basic categories
+ DBG_USERDATA,
+ DBG_DBAPI,
+ DBG_PLUGIN,
+ DBG_FILTER,
+ DBG_MATCH,
+ DBG_CONFLICT,
+ DBG_DETAILS,
+ DBG_EXOTIC,
+ DBG_SCRIPTEXPR,
+
+ // useful sets
+ DBG_ALL,
+ DBG_MINIMAL,
+ DBG_NORMAL,
+ DBG_EXTENDED,
+ DBG_MAXIMAL,
+ DBG_ALLDB,
+ DBG_RTK_SML+DBG_RTK_XPT,
+
+ // old names that are mapped to new masks
+ DBG_DATA, // formerly: DBG_ITEMS
+ DBG_PROTO, // formerly: DBG_CMD
+ DBG_REMOTEINFO, // formerly: DBG_DEVINF
+ DBG_PARSE+DBG_GEN // formerly: DBG_DATACONV
+};
+
+
+
+uInt32 TDebugConfig::str2DebugMask(const char **aAttributes)
+{
+ expectEmpty(); // enable may not have content
+ // process arguments
+ const char* dbgopt = getAttr(aAttributes,"option");
+ if (!dbgopt) {
+ ReportError(false,"debug enable/disable, missing 'option' attribute");
+ }
+ sInt16 k;
+ if (StrToEnum(debugOptionNames,numDebugOptions,k,dbgopt)) {
+ return debugOptionMasks[k];
+ }
+ ReportError(false,"unknown debug option '%s'",dbgopt);
+ return 0;
+} // TDebugConfig::str2DebugMask
+
+
+// debug config element parsing
+bool TDebugConfig::localStartElement(const char *aElementName, const char **aAttributes, sInt32 aLine)
+{
+ // checking the elements
+ if (strucmp(aElementName,"mask")==0)
+ expectUInt32(fDebug);
+ else if (strucmp(aElementName,"enable")==0)
+ fDebug = fDebug | str2DebugMask(aAttributes);
+ else if (strucmp(aElementName,"disable")==0)
+ fDebug = fDebug & ~str2DebugMask(aAttributes);
+ else if (strucmp(aElementName,"logpath")==0)
+ expectPath(fDebugInfoPath);
+ else if (strucmp(aElementName,"msgdump")==0)
+ expectBool(fMsgDump);
+ else if (strucmp(aElementName,"xmltranslate")==0)
+ expectBool(fXMLtranslate);
+ else if (strucmp(aElementName,"simmsgread")==0)
+ expectBool(fSimMsgRead);
+ else if (strucmp(aElementName,"sessionlogs")==0)
+ expectBool(fSessionDebugLogs);
+ else if (strucmp(aElementName,"globallogs")==0)
+ expectBool(fGlobalDebugLogs);
+ // options for TDebugLogger
+ else if (strucmp(aElementName,"logformat")==0)
+ expectEnum(sizeof(fSessionDbgLoggerOptions.fOutputFormat),&fSessionDbgLoggerOptions.fOutputFormat,DbgOutFormatNames,numDbgOutFormats);
+ else if (strucmp(aElementName,"folding")==0)
+ expectEnum(sizeof(fSessionDbgLoggerOptions.fFoldingMode),&fSessionDbgLoggerOptions.fFoldingMode,DbgFoldingModeNames,numDbgFoldingModes);
+ else if (strucmp(aElementName,"indentstring")==0)
+ expectCString(fSessionDbgLoggerOptions.fIndentString);
+ else if (strucmp(aElementName,"fileprefix")==0)
+ expectRawString(fSessionDbgLoggerOptions.fCustomPrefix);
+ else if (strucmp(aElementName,"filesuffix")==0)
+ expectRawString(fSessionDbgLoggerOptions.fCustomSuffix);
+ else if (strucmp(aElementName,"logflushmode")==0)
+ expectEnum(sizeof(fSessionDbgLoggerOptions.fFlushMode),&fSessionDbgLoggerOptions.fFlushMode,DbgFlushModeNames,numDbgFlushModes);
+ else if (strucmp(aElementName,"appendtoexisting")==0)
+ expectBool(fSessionDbgLoggerOptions.fAppend);
+ else if (strucmp(aElementName,"timestamp")==0)
+ expectBool(fSessionDbgLoggerOptions.fTimestampStructure);
+ else if (strucmp(aElementName,"timestampall")==0)
+ expectBool(fSessionDbgLoggerOptions.fTimestampForAll);
+ else if (strucmp(aElementName,"showthreadid")==0)
+ expectBool(fSessionDbgLoggerOptions.fThreadIDForAll);
+ else if (strucmp(aElementName,"subthreadmode")==0)
+ expectEnum(sizeof(fSessionDbgLoggerOptions.fSubThreadMode),&fSessionDbgLoggerOptions.fSubThreadMode,DbgSubthreadModeNames,numDbgSubthreadModes);
+ else if (strucmp(aElementName,"subthreadbuffersize")==0)
+ expectUInt32(fSessionDbgLoggerOptions.fSubThreadBufferMax);
+ else if (strucmp(aElementName,"singlegloballog")==0)
+ expectBool(fSingleGlobLog);
+ else if (strucmp(aElementName,"singlesessionlog")==0)
+ expectBool(fSingleSessionLog);
+ else if (strucmp(aElementName,"timedsessionlognames")==0)
+ expectBool(fTimedSessionLogNames);
+ else
+ return false; // invalid element
+ return true;
+} // TDebugConfig::localStartElement
+
+#endif
+#endif
+
+
+#ifndef ENGINE_LIBRARY
+
+// in engine library, all output must be in context of an engineInterface/appBase
+
+// Debug output routines
+//
+// If a fDebugLogOutputFunc callback is defined,
+// it will be used for output, otherwise global gDebugLogPath
+// file will be written
+
+uInt32 getDbgMask(void)
+{
+ #ifdef SYDEBUG
+ TSyncAppBase *appBase = getExistingSyncAppBase();
+ if (!appBase) return 0; // no appbase -> no debug
+ return appBase->getDbgMask();
+ #else
+ return 0;
+ #endif
+} // getDebugMask
+
+
+TDebugLogger *getDbgLogger(void)
+{
+ #ifdef SYDEBUG
+ TSyncAppBase *appBase = getExistingSyncAppBase();
+ if (!appBase) return NULL; // no appbase -> no debuglogger
+ return appBase->getDbgLogger();
+ #else
+ return NULL;
+ #endif
+} // getDbgLogger
+
+
+// non-class DebugPuts
+void DebugPuts(uInt32 mask, const char *text)
+{
+ #ifdef SYDEBUG
+ // use global debug channel of appBase for non-object-context output
+ TSyncAppBase *appBase = getExistingSyncAppBase();
+ if (!appBase || appBase->getDbgMask()==0) return; // no appbase or debug off -> no output
+ TDebugLogger *dbgLogger = appBase->getDbgLogger();
+ if (!dbgLogger) return;
+ dbgLogger->DebugPuts(mask,text,0,false);
+ #endif
+} // DebugPuts
+
+
+// non-class print to debug channel
+void DebugVPrintf(uInt32 mask, const char *format, va_list args)
+{
+ #ifdef SYDEBUG
+ // use global debug channel of appBase for non-object-context output
+ TSyncAppBase *appBase = getExistingSyncAppBase();
+ if (!appBase || appBase->getDbgMask()==0) return; // no appbase or debug off -> no output
+ TDebugLogger *dbgLogger = appBase->getDbgLogger();
+ if (!dbgLogger) return;
+ dbgLogger->DebugVPrintf(mask,format,args);
+ #endif
+} // DebugVPrintf
+
+
+// non-class print to debug channel
+void DebugPrintf(const char *text, ...)
+{
+ #ifdef SYDEBUG
+ va_list args;
+ if (PDEBUGMASK) {
+ va_start(args, text);
+ DebugVPrintf(DBG_TRANSP, text,args);
+ va_end(args);
+ } // if (PDEBUGMASK)
+ #endif
+} // DebugPrintf
+
+
+void smlLibPrint(const char *text, ...)
+{
+ #ifdef SYDEBUG
+ va_list args;
+ va_start(args, text);
+ DebugVPrintf(DBG_RTK_SML,text,args);
+ va_end(args);
+ #endif
+} // smlLibPrint
+
+
+void smlLibVprintf(const char *format, va_list va)
+{
+ #ifdef SYDEBUG
+ DebugVPrintf(DBG_RTK_SML,format,va);
+ #endif
+} // smlLibVprintf
+
+
+// entry point for SyncML-Toolkit with
+// #define TRACE_TO_STDOUT
+void localOutput(const char *aFormat, va_list aArgs)
+{
+ #ifdef SYDEBUG
+ NCDEBUGVPRINTFX(DBG_RTK_SML,aFormat,aArgs);
+ #endif
+}
+
+
+#endif // not ENGINE_LIBRARY
+
+
+// Console printout. Only enabled when defined(CONSOLEINFO)
+
+// non-class print to console
+void ConsolePrintf(const char *text, ...)
+{
+ #ifdef CONSOLEINFO
+ const sInt16 maxmsglen=1024;
+ char msg[maxmsglen];
+ va_list args;
+
+ msg[0]='\0';
+ va_start(args, text);
+ // assemble the message string
+ vsnprintf(msg, maxmsglen, text, args);
+ va_end(args);
+ // write the string
+ ConsolePuts(msg);
+ #endif
+} // sysyncConsolePrintf
+
+
+// non-class Console output
+void ConsolePuts(const char *text)
+{
+ #ifdef CONSOLEINFO
+ // show on app's console equivalent
+ AppConsolePuts(text);
+ #endif // console enabled
+} // sysyncConsolePuts
+
+
+
+// TSyncAppBase
+// ============
+
+
+/* SyncML toolkit callback function declarations */
+
+extern "C" {
+ /* message callbacks */
+ Ret_t smlStartMessageCallback(InstanceID_t id, VoidPtr_t userData, SmlSyncHdrPtr_t pContent);
+ Ret_t smlEndMessageCallback(InstanceID_t id, VoidPtr_t userData, Boolean_t final);
+ /* grouping commands */
+ Ret_t smlStartSyncCallback(InstanceID_t id, VoidPtr_t userData, SmlSyncPtr_t pContent);
+ Ret_t smlEndSyncCallback(InstanceID_t id, VoidPtr_t userData);
+ #ifdef ATOMIC_RECEIVE /* these callbacks are NOT included in the Toolkit lite version */
+ Ret_t smlStartAtomicCallback(InstanceID_t id, VoidPtr_t userData, SmlAtomicPtr_t pContent);
+ Ret_t smlEndAtomicCallback(InstanceID_t id, VoidPtr_t userData);
+ #endif
+ #ifdef SEQUENCE_RECEIVE
+ Ret_t smlStartSequenceCallback(InstanceID_t id, VoidPtr_t userData, SmlSequencePtr_t pContent);
+ Ret_t smlEndSequenceCallback(InstanceID_t id, VoidPtr_t userData);
+ #endif
+ /* Sync Commands */
+ Ret_t smlAddCmdCallback(InstanceID_t id, VoidPtr_t userData, SmlAddPtr_t pContent);
+ Ret_t smlAlertCmdCallback(InstanceID_t id, VoidPtr_t userData, SmlAlertPtr_t pContent);
+ Ret_t smlDeleteCmdCallback(InstanceID_t id, VoidPtr_t userData, SmlDeletePtr_t pContent);
+ Ret_t smlGetCmdCallback(InstanceID_t id, VoidPtr_t userData, SmlGetPtr_t pContent);
+ Ret_t smlPutCmdCallback(InstanceID_t id, VoidPtr_t userData, SmlPutPtr_t pContent);
+ #ifdef MAP_RECEIVE
+ Ret_t smlMapCmdCallback(InstanceID_t id, VoidPtr_t userData, SmlMapPtr_t pContent);
+ #endif
+ #ifdef RESULT_RECEIVE
+ Ret_t smlResultsCmdCallback(InstanceID_t id, VoidPtr_t userData, SmlResultsPtr_t pContent);
+ #endif
+ Ret_t smlStatusCmdCallback(InstanceID_t id, VoidPtr_t userData, SmlStatusPtr_t pContent);
+ Ret_t smlReplaceCmdCallback(InstanceID_t id, VoidPtr_t userData, SmlReplacePtr_t pContent);
+ /* othe commands */
+ #ifdef COPY_RECEIVE /* these callbacks are NOT included in the Toolkit lite version */
+ Ret_t smlCopyCmdCallback(InstanceID_t id, VoidPtr_t userData, SmlCopyPtr_t param);
+ #endif
+ Ret_t smlMoveCmdCallback(InstanceID_t id, VoidPtr_t userData, SmlMovePtr_t param);
+ #ifdef EXEC_RECEIVE
+ Ret_t smlExecCmdCallback(InstanceID_t id, VoidPtr_t userData, SmlExecPtr_t pContent);
+ #endif
+ #ifdef SEARCH_RECEIVE
+ Ret_t smlSearchCmdCallback(InstanceID_t id, VoidPtr_t userData, SmlSearchPtr_t pContent);
+ #endif
+ /* Other Callbacks */
+ Ret_t smlHandleErrorCallback(InstanceID_t id, VoidPtr_t userData);
+ Ret_t smlTransmitChunkCallback(InstanceID_t id, VoidPtr_t userData);
+
+ /* print callback */
+ void smlPrintCallback(String_t outputString);
+} // extern "C" declaration
+
+
+// constructor
+TSyncAppBase::TSyncAppBase() :
+ fDeleting(false),
+ fConfigP(NULL),
+ fRequestCount(0),
+ #ifdef PROGRESS_EVENTS
+ fProgressEventFunc(NULL),
+ #endif
+ #ifdef ENGINEINTERFACE_SUPPORT
+ fEngineInterfaceP(NULL),
+ #else
+ fMasterPointer(NULL),
+ #endif
+ fApiInterModuleContext(0)
+ // reset all callbacks
+ #ifdef SYDEBUG
+ // app logger
+ ,fAppLogger(&fAppZones)
+ #endif
+{
+ // at the moment of creation, this is now the SyncAppBase
+ // (set it here already to allow getSyncAppBase() from derived constructors)
+ #ifdef ENGINEINTERFACE_SUPPORT
+ #ifdef DIRECT_APPBASE_GLOBALACCESS
+ getEngineInterface()->setSyncAppBase(this); // set the link so getSyncAppBase() via EngineInterface works immediately
+ #endif
+ #else
+ #ifdef ENGINE_LIBRARY
+ #error "DIRECT_APPBASE_GLOBALACCESS is not allowed with ENGINE_LIBRARY"
+ #endif
+ sysync_glob_setanchor(this);
+ #endif
+ // init profiling
+ TP_INIT(fTPInfo);
+ TP_START(fTPInfo,TP_general);
+ #ifdef EXPIRES_AFTER_DATE
+ // - get current time
+ sInt16 y,m,d;
+ lineartime2date(getSystemNowAs(TCTX_UTC),&y,&m,&d);
+ // - calculate scrambled version thereof
+ fScrambledNow=
+ ((sInt32)y-1720l)*12l*42l+
+ ((sInt32)m-1)*42l+
+ ((sInt32)d+7l);
+ /*
+ #define SCRAMBLED_EXPIRY_VALUE \
+ (EXPIRY_DAY+7)+ \
+ (EXPIRY_MONTH-1)*42+ \
+ (EXPIRY_YEAR-1720)*12*42
+ */
+ #endif
+ #ifdef APP_CAN_EXPIRE
+ fAppExpiryStatus=LOCERR_OK; // do not compare here, would be too easy
+ #endif
+ #if defined(SYDEBUG) && defined(HARDCODED_CONFIG)
+ fConfigFilePath="<hardcoded config>";
+ #endif
+ #ifdef SYSER_REGISTRATION
+ // make sure that license is standard (no phone home, no expiry) in case there is NO license at all
+ fRegLicenseType=0;
+ fRelDuration=0;
+ fRegDuration=0;
+ #endif
+
+ // TODO: put this somewhere where the return code can be checked and
+ // reported to the user of TSyncAppBase
+ fAppZones.initialize();
+
+ /* %%%%
+ string zoneName;
+ sInt16 stdBias;
+ sInt16 dstBias;
+ lineartime_t stdStart;
+ lineartime_t dstStart;
+ bool ok = getSystemTimeZone(zoneName, stdBias, dstBias, stdStart, dstStart);
+ printf("getSystemTimeZone: name=%s, stdBias=%hd, dstBias=%hd, stdStart=%lld, dstStart=%lld\n", zoneName.c_str(), stdBias, dstBias, stdStart, dstStart);
+ */
+
+} // TSyncAppBase::TSyncAppBase
+
+
+// destructor
+TSyncAppBase::~TSyncAppBase()
+{
+ fDeleting=true; // flag deletion to block calling critical (virtual) methods
+ #if !defined(ENGINEINTERFACE_SUPPORT) || defined(DIRECT_APPBASE_GLOBALACCESS)
+ sysync_glob_setanchor(NULL);
+ #endif
+ // stop and show profiling info
+ TP_STOP(fTPInfo);
+ #ifdef TIME_PROFILING
+ if (PDEBUGMASK & DBG_PROFILE) {
+ sInt16 i;
+ PDEBUGPRINTFX(DBG_PROFILE,("Non-Session CPU usage statistics: (system/user)"));
+ // sections
+ for (i=0; i<numTPTypes; i++) {
+ PNCDEBUGPRINTFX(DBG_PROFILE,(
+ "- %-20s : (%10ld/%10ld) ms",
+ TP_TypeNames[i],
+ TP_GETSYSTEMMS(fTPInfo,(TTP_Types)i),
+ TP_GETUSERMS(fTPInfo,(TTP_Types)i)
+ ));
+ }
+ // total
+ PNCDEBUGPRINTFX(DBG_PROFILE,(
+ "- %-20s : (%10ld/%10ld) ms",
+ "TOTAL",
+ TP_GETTOTALSYSTEMMS(fTPInfo),
+ TP_GETTOTALUSERMS(fTPInfo)
+ ));
+ }
+ #endif
+ // delete the config now
+ #ifdef SYDEBUG
+ // - but first make sure applogger does not refer to it any more
+ fAppLogger.setOptions(NULL);
+ #endif
+ // - now delete
+ if (fConfigP) delete fConfigP;
+} // TSyncAppBase::~TSyncAppBase
+
+
+
+#ifndef HARDCODED_CONFIG
+
+// get config variables
+bool TSyncAppBase::getConfigVar(cAppCharP aVarName, string &aValue)
+{
+ // look up in user-defined variables first
+ TStringToStringMap::iterator pos = fConfigVars.find(aVarName);
+ if (pos!=fConfigVars.end()) {
+ aValue = (*pos).second;
+ return true; // found in user defined vars
+ }
+ // check some globals
+ if (strucmp(aVarName,"version")==0) {
+ // version
+ aValue = SYSYNC_FULL_VERSION_STRING;
+ return true;
+ }
+ if (strucmp(aVarName,"hexversion")==0) {
+ // string-comparable version as 8-digit hex MMmmrrbb (Major, minor, rev, build)
+ StringObjPrintf(aValue,"%02X%02X%02X%02X",SYSYNC_VERSION_MAJOR,SYSYNC_VERSION_MINOR,SYSYNC_SUBVERSION,SYSYNC_BUILDNUMBER);
+ return true;
+ }
+ if (strucmp(aVarName,"manufacturer")==0) {
+ aValue = getManufacturer();
+ return true;
+ }
+ if (strucmp(aVarName,"model")==0) {
+ aValue = getModel();
+ return true;
+ }
+ if (strucmp(aVarName,"variant")==0) {
+ // variant
+ #if SYSER_VARIANT_CODE==SYSER_VARIANT_STD
+ aValue = "STD";
+ #elif SYSER_VARIANT_CODE==SYSER_VARIANT_PRO
+ aValue ="PRO";
+ #elif SYSER_VARIANT_CODE==SYSER_VARIANT_CUSTOM
+ aValue = "CUSTOM";
+ #elif SYSER_VARIANT_CODE==SYSER_VARIANT_DEMO
+ aValue = "DEMO";
+ #else
+ aValue = "unknown";
+ #endif
+ return true;
+ }
+ if (strucmp(aVarName,"productcode")==0) {
+ #ifdef SYSER_PRODUCT_CODE
+ StringObjPrintf(aValue,"%d",SYSER_PRODUCT_CODE);
+ #else
+ aValue = "unknown";
+ #endif
+ return true;
+ }
+ if (strucmp(aVarName,"extraid")==0) {
+ #ifdef SYSER_PRODUCT_CODE
+ StringObjPrintf(aValue,"%d",SYSER_EXTRA_ID);
+ #else
+ aValue = "unknown";
+ #endif
+ return true;
+ }
+ if (strucmp(aVarName,"platformname")==0) {
+ aValue = SYSYNC_PLATFORM_NAME;
+ return true;
+ }
+ // look up in platform strings
+ int plsId;
+ for (plsId=0; plsId<numPlatformStrings; plsId++) {
+ if (strucmp(aVarName,PlatformStringNames[plsId])==0) {
+ // get platform string
+ return getPlatformString((TPlatformStringID)plsId,aValue);
+ }
+ }
+ // not found
+ return false;
+} // TSyncAppBase::getConfigVar
+
+
+// set config variable
+bool TSyncAppBase::setConfigVar(cAppCharP aVarName, cAppCharP aNewValue)
+{
+ // set user-defined config variable
+ fConfigVars[aVarName] = aNewValue;
+ return true;
+} // TSyncAppBase::setConfigVar
+
+
+// expand config vars in string
+bool TSyncAppBase::expandConfigVars(string &aString, sInt8 aCfgVarExp, TConfigElement *aCfgElement, cAppCharP aElementName)
+{
+ string::size_type n,n2;
+
+ if (aCfgVarExp<=0 || aString.empty()) return true; // no expansion
+ if (aCfgElement && !aElementName) aElementName="*Unknown*";
+ n=0;
+ while(true) {
+ n = aString.find("$(",n);
+ if (n==string::npos)
+ break; // no more macros
+ // found macro name start - now search end
+ n+=2; // position after $( lead-in
+ n2 = aString.find(")",n);
+ if (n2!=string::npos) {
+ // macro name found
+ string vn,vv;
+ vn.assign(aString,n,n2-n); // name
+ if (getConfigVar(vn.c_str(),vv)) {
+ if (aCfgVarExp==2) {
+ // check for recursion loop
+ vn.insert(0,"$("); vn.append(")");
+ if (vv.find(vn,0)!=string::npos) {
+ if (aCfgElement) aCfgElement->ReportError(true,"Recursive config variable $(%s) in <%s>",vn.c_str(),aElementName);
+ n=n2+1; // do not expand
+ continue;
+ }
+ }
+ // found value - substitute
+ n-=2; // substitute beginning with leadin
+ n2+=1; // include closing paranthesis
+ aString.replace(n,n2-n,vv);
+ if (aCfgVarExp<2) {
+ // do not allow recursive macro expansion
+ n+=vv.size(); // continue searching past substituted chars
+ }
+ }
+ else {
+ // not found - leave macro as-is
+ if (aCfgElement) aCfgElement->ReportError(false,"Undefined config variable $(%s) in <%s>",vn.c_str(),aElementName);
+ n=n2+1; // continue search after closing paranthesis
+ }
+ }
+ else {
+ if (aCfgElement) aCfgElement->ReportError(false,"Unterminated $(xxx)-style config variable in <%s>",aElementName);
+ }
+ }
+ return true;
+} // TSyncAppBase::expandConfigVars
+
+#endif // not HARDCODED_CONFIG
+
+
+
+#ifdef HARDCODED_CONFIG
+
+
+localstatus TSyncAppBase::initHardcodedConfig(void)
+{
+ localstatus err;
+
+ // initialize for receiving new config
+ fConfigP->clear();
+ // now call initializer in derived root config
+ err=fConfigP->createHardcodedConfig();
+ if (err!=LOCERR_OK) return err;
+ // make sure it gets all resolved
+ fConfigP->ResolveAll();
+ // is ok now
+ return LOCERR_OK;
+} // TSyncAppBase::initHardcodedConfig
+
+
+#else
+
+
+// report config errors
+void TSyncAppBase::ConferrPrintf(const char *text, ...)
+{
+ const sInt16 maxmsglen=1024;
+ char msg[maxmsglen];
+ va_list args;
+
+ msg[0]='\0';
+ va_start(args, text);
+ // assemble the message string
+ vsnprintf(msg, maxmsglen, text, args);
+ va_end(args);
+ // output config errors
+ ConferrPuts(msg);
+} // TSyncAppBase::ConferrPrintf
+
+
+// report config errors to appropriate channel
+void TSyncAppBase::ConferrPuts(const char *msg)
+{
+ #ifdef ENGINE_LIBRARY
+ // engine variant
+ string filename;
+ // - get config var to see where we should put config errors
+ if (!getConfigVar("conferrpath", filename))
+ return; // no output defined for config errors
+ // a config error path is defined
+ if (strucmp(filename.c_str(),"console")==0) {
+ // put message directly to what is supposed to be the console
+ AppConsolePuts(msg);
+ return; // done
+ }
+ #else // ENGINE_LIBRARY
+ // old variant - output to predefined path
+ #ifdef CONSOLEINFO
+ ConsolePuts(msg);
+ return;
+ #elif defined(__PALM_OS__)
+ return; // PalmOS has no file output
+ #endif
+ // prepare file name
+ string filename;
+ if (!getPlatformString(pfs_defout_path,filename)) return;
+ makeOSDirPath(filename);
+ filename+=CONFERRPREFIX;
+ filename+=TARGETID;
+ filename+=CONFERRSUFFIX;
+ #endif // not ENGINE_LIBRARY
+ #ifndef __PALM_OS__
+ // now write to file
+ FILE * logfile=fopen(filename.c_str(),"a");
+ if (logfile) {
+ string ts;
+ StringObjTimestamp(ts,getSystemNowAs(TCTX_SYSTEM));
+ ts.append(": ");
+ fputs(ts.c_str(),logfile);
+ fputs(msg,logfile);
+ fputs("\n",logfile);
+ fclose(logfile);
+ }
+ #endif // __PALM_OS__
+} // TSyncAppBase::ConferrPuts
+
+
+/* config reading */
+
+// XML parser's "userdata"
+typedef struct {
+ XML_Parser parser;
+ TRootConfigElement *rootconfig;
+} TXMLUserData;
+
+
+// prototypes
+extern "C" void startElement(void *userData, const char *name, const char **atts);
+extern "C" void charData(void *userData, const XML_Char *s, int len);
+extern "C" void endElement(void *userData, const char *name);
+
+
+static localstatus checkErrors(TRootConfigElement *aRootConfigP,XML_Parser aParser)
+{
+ const char *errmsg = aRootConfigP->getErrorMsg();
+ if (errmsg) {
+ aRootConfigP->getSyncAppBase()->ConferrPrintf(
+ "%s at line %ld col %ld",
+ errmsg,
+ (sInt32)XML_GetCurrentLineNumber(aParser),
+ (sInt32)XML_GetCurrentColumnNumber(aParser)
+ );
+ aRootConfigP->resetError();
+ return aRootConfigP->getFatalError(); // return when fatal
+ }
+ else
+ return LOCERR_OK; // no (fatal) error
+} // checkErrors
+
+
+// callback for expat
+extern "C" void startElement(void *userData, const char *name, const char **atts)
+{
+ TRootConfigElement *cfgP = static_cast<TXMLUserData *>(userData)->rootconfig;
+ XML_Parser parser = static_cast<TXMLUserData *>(userData)->parser;
+ SYSYNC_TRY {
+ cfgP->startElement(name,atts,XML_GetCurrentLineNumber(parser));
+ }
+ SYSYNC_CATCH (exception &e)
+ cfgP->ReportError(true,"Exception in StartElement: %s",e.what());
+ SYSYNC_ENDCATCH
+ // check for errors
+ checkErrors(cfgP,parser);
+} // startElement
+
+
+// callback for expat
+extern "C" void charData(void *userData, const XML_Char *s, int len)
+{
+ TRootConfigElement *cfgP = static_cast<TXMLUserData *>(userData)->rootconfig;
+ XML_Parser parser = static_cast<TXMLUserData *>(userData)->parser;
+ SYSYNC_TRY {
+ cfgP->charData(s,len);
+ }
+ SYSYNC_CATCH (exception &e)
+ cfgP->ReportError(true,"Exception in charData: %s",e.what());
+ SYSYNC_ENDCATCH
+ // check for errors
+ checkErrors(cfgP,parser);
+} // charData
+
+
+
+// callback for expat
+extern "C" void endElement(void *userData, const char *name)
+{
+ TRootConfigElement *cfgP = static_cast<TXMLUserData *>(userData)->rootconfig;
+ XML_Parser parser = static_cast<TXMLUserData *>(userData)->parser;
+ SYSYNC_TRY {
+ cfgP->endElement(name);
+ }
+ SYSYNC_CATCH (exception &e)
+ cfgP->ReportError(true,"Exception in endElement: %s",e.what());
+ SYSYNC_ENDCATCH
+ // check for errors
+ checkErrors(cfgP,parser);
+} // endElement
+
+
+// config stream reading
+localstatus TSyncAppBase::readXMLConfigStream(TXMLConfigReadFunc aReaderFunc, void *aContext)
+{
+ localstatus fatalerr;
+
+ // clear (reset to default) all config
+ TP_DEFIDX(last);
+ TP_SWITCH(last,fTPInfo,TP_configread);
+ MP_SHOWCURRENT(DBG_HOT,"start reading config");
+
+ // initialize for new config
+ fConfigP->clear();
+ fConfigP->ResetParsing();
+ // read XML
+ appChar buf[CONFIG_READ_BUFSIZ];
+ sInt16 done;
+ // - create parser
+ XML_Parser parser = XML_ParserCreate(NULL);
+ SYSYNC_TRY {
+ // init user data struct
+ TXMLUserData userdata;
+ userdata.parser=parser;
+ userdata.rootconfig=fConfigP;
+ // pass pointer to root config here
+ XML_SetUserData(parser, &userdata);
+ XML_SetElementHandler(parser, startElement, endElement);
+ XML_SetCharacterDataHandler(parser, charData);
+ do {
+ bufferIndex len=0;
+ // callback reader func
+ if (!(aReaderFunc)(buf,CONFIG_READ_BUFSIZ,&len,aContext)) {
+ fConfigP->setFatalError(LOCERR_CFGREAD); // this is also fatal
+ ConferrPrintf(
+ "Error reading from config"
+ );
+ break;
+ }
+ // %%% changed stop criterium to smooth EngineInterface API
+ //done = len < CONFIG_READ_BUFSIZ;
+ done = len == 0;
+ if (!XML_Parse(parser, buf, len, done)) {
+ fConfigP->setFatalError(LOCERR_CFGPARSE); // this is also fatal
+ ConferrPrintf(
+ "%s at line %ld col %ld",
+ XML_ErrorString(XML_GetErrorCode(parser)),
+ (sInt32)XML_GetCurrentLineNumber(parser),
+ (sInt32)XML_GetCurrentColumnNumber(parser)
+ );
+ break;
+ }
+ else
+ if (fConfigP->getFatalError()) break;
+ } while (!done);
+ XML_ParserFree(parser);
+ if (!fConfigP->getFatalError()) {
+ // now resolve
+ fConfigP->ResolveAll();
+ // display resolve error, if any
+ const char *msg = fConfigP->getErrorMsg();
+ if (msg) {
+ // this is fatal only if error was thrown in Resolve.
+ // Some warnings might be set with ReportError and
+ // will not cause abort.
+ ConferrPrintf(msg);
+ }
+ }
+ // check if ok or not
+ if ((fatalerr=fConfigP->getFatalError())!=LOCERR_OK) {
+ // config failed, reset
+ fConfigP->clear();
+ ConferrPrintf(
+ "Fatal error %hd, no valid configuration could be read from XML file",
+ (sInt16)fatalerr
+ );
+ TP_START(fTPInfo,last);
+ return fatalerr;
+ }
+ }
+ SYSYNC_CATCH (...)
+ XML_ParserFree(parser);
+ fConfigP->clear();
+ fConfigP->setFatalError(LOCERR_CFGPARSE); // this is also fatal
+ ConferrPrintf(
+ "Exception while parsing XML, no valid configuration"
+ );
+ TP_START(fTPInfo,last);
+ return LOCERR_CFGPARSE;
+ SYSYNC_ENDCATCH
+ TP_START(fTPInfo,last);
+ #if defined(APP_CAN_EXPIRE) && defined(RELEASE_YEAR) && defined(SYSER_REGISTRATION)
+ if (fAppExpiryStatus==LOCERR_TOONEW) {
+ ConferrPrintf(
+ "License is invalid for software released on or after %04d-%02d-01",
+ (fRelDuration / 12) + 2000,
+ (fRelDuration) % 12 + 1
+ );
+ }
+ #endif
+ #ifdef APP_CAN_EXPIRE
+ if (fAppExpiryStatus!=LOCERR_OK) {
+ if (fAppExpiryStatus==LOCERR_EXPIRED) {
+ ConferrPrintf("Time-limited License expired");
+ } else {
+ ConferrPrintf("Missing or bad License Information");
+ }
+ ConferrPrintf("Please contact Synthesis AG to obtain new license information");
+ fConfigP->setFatalError(fAppExpiryStatus); // this is also fatal
+ return fAppExpiryStatus;
+ }
+ #endif
+ #ifdef SYSER_REGISTRATION
+ // check if config should have locked sections with a certain CRC value
+ sInt16 daysleft;
+ uInt32 shouldcrc;
+ string s;
+ bool ok=false;
+ // - get restriction string from licensed info
+ ok = getAppEnableInfo(daysleft, NULL, &s)==LOCERR_OK;
+ string restrid,restrval;
+ const char *p = s.c_str(); // start of license info string
+ while (ok && (p=getLicenseRestriction(p,restrid,restrval))!=NULL) {
+ if (restrid=="l") { // lock CRC
+ StrToULong(restrval.c_str(),shouldcrc);
+ ok=shouldcrc==fConfigP->getConfigLockCRC();
+ }
+ }
+ if (!ok) {
+ ConferrPrintf("Locked config sections are not valid");
+ fConfigP->setFatalError(LOCERR_BADREG); // this is also fatal
+ return LOCERR_BADREG;
+ }
+ #endif
+ // ok if done
+ MP_SHOWCURRENT(DBG_HOT,"finished reading config");
+ return LOCERR_OK;
+} // TSyncAppBase::readXMLConfigStream
+
+
+#ifdef CONSTANTXML_CONFIG
+
+// stream reader for reading from compiled-in text constant
+static int _CALLING_ ConstantReader(
+ sysync::appCharP aBuffer,
+ sysync::bufferIndex aMaxSize,
+ sysync::bufferIndex *aReadCharsP,
+ void *aContext // const char **
+)
+{
+ // get cursor
+ const char *readptr = *((const char **)aContext);
+ // read from constant
+ if (!readptr) return false;
+ size_t len = strlen(readptr);
+ if (len>aMaxSize) len=aMaxSize;
+ // - copy
+ if (len>0) strncpy(aBuffer,readptr,len);
+ // - update cursor
+ *((const char **)aContext)=readptr+len;
+ *aReadCharsP=len; // return number of chars actually read
+ return true; // successful
+} // ConstantReader
+
+
+// config file reading from hard-wired Constant
+localstatus TSyncAppBase::readXMLConfigConstant(const char *aConstantXML)
+{
+ const char *aCursor = aConstantXML;
+ return readXMLConfigStream(&ConstantReader, &aCursor);
+ #ifdef SYDEBUG
+ // signal where config came from
+ fConfigFilePath="<XML read from string constant>";
+ #endif
+} // TSyncAppBase::readXMLConfigConstant
+
+
+#endif
+
+
+// stream reader for cfile
+static int _CALLING_ CFileReader(
+ appCharP aBuffer,
+ bufferIndex aMaxSize,
+ bufferIndex *aReadCharsP,
+ void *aContext // FILE *
+)
+{
+ FILE *cfgfile = (FILE *)aContext;
+ // read from file
+ size_t len = fread(aBuffer, 1, aMaxSize, cfgfile);
+ if (len<0) {
+ if (!feof(cfgfile))
+ return appFalse; // not EOF, other error: failed
+ len=0; // nothing read, end of file
+ }
+ *aReadCharsP=len; // return number of chars actually read
+ return appTrue; // successful
+} // CFileReader
+
+
+// config file reading from C file
+localstatus TSyncAppBase::readXMLConfigCFile(FILE *aCfgFile)
+{
+ return readXMLConfigStream(&CFileReader, (void *)aCfgFile);
+} // TSyncAppBase::readXMLConfigCFile
+
+
+// read config from file path
+localstatus TSyncAppBase::readXMLConfigFile(cAppCharP aFilePath)
+{
+ localstatus fatalerr;
+
+ // open file
+ FILE* cfgfile=fopen(aFilePath,"r");
+ if (cfgfile) {
+ // yes, there is a config file. Get its date
+ getRootConfig()->fConfigDate = getFileModificationDate(aFilePath);
+ // now read the config file (and possibly override fConfigDate)
+ if ((fatalerr=readXMLConfigCFile(cfgfile))!=LOCERR_OK) {
+ fclose(cfgfile);
+ return fatalerr; // config file with fatal errors
+ }
+ fclose(cfgfile);
+ #if defined(SYDEBUG) && !defined(SYSYNC_TOOL)
+ string t;
+ StringObjTimestamp(t,getRootConfig()->fConfigDate);
+ // now write settings to log
+ PDEBUGPRINTFX(DBG_HOT,(
+ "==== Config file '%s' read: Last Config Change=%s, Debug=0x%08lX, Lock=%ld",
+ aFilePath,
+ t.c_str(),
+ (long)PDEBUGMASK,
+ (long)fConfigP->getConfigLockCRC()
+ ));
+ PDEBUGPRINTFX(DBG_HOT,(
+ "==== Config file ID = '%s'",
+ getRootConfig()->fConfigIDString.c_str()
+ ));
+ #endif
+ CONSOLEPRINTF(("- Config file read from '%s'",aFilePath));
+ #ifdef SYDEBUG
+ if (getRootConfig()->fDebugConfig.fSessionDebugLogs || getRootConfig()->fDebugConfig.fGlobalDebugLogs)
+ CONSOLEPRINTF(("- Debug log path: %s",getRootConfig()->fDebugConfig.fDebugInfoPath.c_str()));
+ // signal where config came from
+ fConfigFilePath=aFilePath;
+ #endif
+ // config found
+ return LOCERR_OK;
+ }
+ else {
+ DEBUGPRINTFX(DBG_ERROR,("==== No Config file found under '%s'",aFilePath));
+ // reset config to defaults
+ fConfigP->clear();
+ return LOCERR_NOCFGFILE;
+ }
+} // TSyncAppBase::readXMLConfigFile
+
+
+#ifndef ENGINE_LIBRARY
+
+// standard reading of config on predefined paths
+localstatus TSyncAppBase::readXMLConfigStandard(const char *aConfigFileName, bool aOnlyGlobal, bool aAbsolute)
+{
+ localstatus fatalerr;
+
+ SYSYNC_TRY {
+ // - get path where config file should be
+ string cfgfilename;
+ sInt16 attempt= aOnlyGlobal ? 1 : 0; // if only global, don't look in exe dir (e.g. for ISAPI modules)
+ while (true) {
+ // count attempt
+ attempt++;
+ if (aAbsolute) {
+ if (attempt>1) {
+ // no config file found
+ return LOCERR_NOCFGFILE;
+ }
+ // just use path as it is
+ cfgfilename=aConfigFileName;
+ }
+ else {
+ // try paths
+ if (attempt==1) {
+ // first check if there's a local copy
+ if (!getPlatformString(pfs_loccfg_path,cfgfilename))
+ continue; // none found, try next
+ }
+ else if (attempt==2) {
+ // if no local config file, look for a global one
+ if (!getPlatformString(pfs_globcfg_path,cfgfilename))
+ continue; // none found, try next
+ }
+ else {
+ CONSOLEPRINTF(("- No config file found, using default settings"));
+ // reset config to defaults
+ fConfigP->clear();
+ return LOCERR_NOCFGFILE; // no config
+ }
+ // add file name
+ makeOSDirPath(cfgfilename,false);
+ cfgfilename+=aConfigFileName;
+ }
+ fatalerr=readXMLConfigFile(cfgfilename.c_str());
+ if (fatalerr==LOCERR_OK)
+ break; // config found, ok
+ } // attempt loop
+ } // try
+ SYSYNC_CATCH (...)
+ ConferrPrintf("Fatal Error (exception) while reading config file");
+ return LOCERR_CFGREAD;
+ SYSYNC_ENDCATCH
+ return LOCERR_OK; // ok
+} // TSyncAppBase::readXMLConfigStandard
+
+#endif // ENGINE_LIBRARY
+
+#endif // HARDCODED_CONFIG
+
+
+/* progress sevent notification */
+
+#ifdef PROGRESS_EVENTS
+
+// event generator
+bool TSyncAppBase::NotifyProgressEvent(
+ TProgressEventType aEventType,
+ TLocalDSConfig *aDatastoreID,
+ sInt32 aExtra1,
+ sInt32 aExtra2,
+ sInt32 aExtra3
+)
+{
+ TProgressEvent theevent;
+
+ if (fProgressEventFunc) {
+ // there is a progress event callback
+ // - prepare event
+ theevent.eventtype=aEventType;
+ theevent.datastoreID=aDatastoreID;
+ theevent.extra=aExtra1;
+ theevent.extra2=aExtra2;
+ theevent.extra3=aExtra3;
+ // - invoke callback (returns false if aborted)
+ return fProgressEventFunc(
+ theevent,
+ fProgressEventContext
+ );
+ }
+ // if no callback, never abort
+ return true; // ok, no abort
+} // TSyncAppBase::NotifyProgressEvent
+
+#endif
+
+
+
+/* logfile outputs */
+
+
+/* SyncML toolkit callback handlers */
+
+
+Ret_t TSyncAppBase::EndMessage(VoidPtr_t userData, Boolean_t aFinal)
+{
+ if (!userData) return SML_ERR_WRONG_PARAM;
+ // call Session
+ SYSYNC_TRY {
+ return ((TSyncSession *) userData)->EndMessage(aFinal);
+ }
+ SYSYNC_CATCH (exception &e)
+ return HandleDecodingException((TSyncSession *)userData,"EndMessage",&e);
+ SYSYNC_ENDCATCH
+ SYSYNC_CATCH (...)
+ return HandleDecodingException((TSyncSession *)userData,"EndMessage",NULL);
+ SYSYNC_ENDCATCH
+} // TSyncAppBase::EndMessage
+
+
+Ret_t TSyncAppBase::StartSync(VoidPtr_t userData, SmlSyncPtr_t aContentP)
+{
+ if (!userData) return SML_ERR_WRONG_PARAM;
+ // call Session
+ SYSYNC_TRY {
+ return ((TSyncSession *) userData)->StartSync(aContentP);
+ }
+ SYSYNC_CATCH (exception &e)
+ return HandleDecodingException((TSyncSession *)userData,"StartSync",&e);
+ SYSYNC_ENDCATCH
+ SYSYNC_CATCH (...)
+ return HandleDecodingException((TSyncSession *)userData,"StartSync",NULL);
+ SYSYNC_ENDCATCH
+} // TSyncAppBase::StartSync
+
+
+Ret_t TSyncAppBase::EndSync(VoidPtr_t userData)
+{
+ if (!userData) return SML_ERR_WRONG_PARAM;
+ // call Session
+ SYSYNC_TRY {
+ return ((TSyncSession *) userData)->EndSync();
+ }
+ SYSYNC_CATCH (exception &e)
+ return HandleDecodingException((TSyncSession *)userData,"EndSync",&e);
+ SYSYNC_ENDCATCH
+ SYSYNC_CATCH (...)
+ return HandleDecodingException((TSyncSession *)userData,"EndSync",NULL);
+ SYSYNC_ENDCATCH
+} // TSyncAppBase::EndSync
+
+
+#ifdef SEQUENCE_RECEIVE
+Ret_t TSyncAppBase::StartSequence(VoidPtr_t userData, SmlSequencePtr_t aContentP)
+{
+ if (!userData) return SML_ERR_WRONG_PARAM;
+ // call Session
+ SYSYNC_TRY {
+ return ((TSyncSession *) userData)->StartSequence(aContentP);
+ }
+ SYSYNC_CATCH (exception &e)
+ return HandleDecodingException((TSyncSession *)userData,"StartSequence",&e);
+ SYSYNC_ENDCATCH
+ SYSYNC_CATCH (...)
+ return HandleDecodingException((TSyncSession *)userData,"StartSequence",NULL);
+ SYSYNC_ENDCATCH
+} // TSyncAppBase::StartSequence
+
+
+Ret_t TSyncAppBase::EndSequence(VoidPtr_t userData)
+{
+ if (!userData) return SML_ERR_WRONG_PARAM;
+ // call Session
+ SYSYNC_TRY {
+ return ((TSyncSession *) userData)->EndSequence();
+ }
+ SYSYNC_CATCH (exception &e)
+ return HandleDecodingException((TSyncSession *)userData,"EndSequence",&e);
+ SYSYNC_ENDCATCH
+ SYSYNC_CATCH (...)
+ return HandleDecodingException((TSyncSession *)userData,"EndSequence",NULL);
+ SYSYNC_ENDCATCH
+} // TSyncAppBase::EndSequence
+#endif
+
+
+#ifdef ATOMIC_RECEIVE
+Ret_t TSyncAppBase::StartAtomic(VoidPtr_t userData, SmlAtomicPtr_t aContentP)
+{
+ if (!userData) return SML_ERR_WRONG_PARAM;
+ // call Session
+ SYSYNC_TRY {
+ return ((TSyncSession *) userData)->StartAtomic(aContentP);
+ }
+ SYSYNC_CATCH (exception &e)
+ return HandleDecodingException((TSyncSession *)userData,"StartAtomic",&e);
+ SYSYNC_ENDCATCH
+ SYSYNC_CATCH (...)
+ return HandleDecodingException((TSyncSession *)userData,"StartAtomic",NULL);
+ SYSYNC_ENDCATCH
+} // TSyncAppBase::StartAtomic
+
+
+Ret_t TSyncAppBase::EndAtomic(VoidPtr_t userData)
+{
+ if (!userData) return SML_ERR_WRONG_PARAM;
+ // call Session
+ SYSYNC_TRY {
+ return ((TSyncSession *) userData)->EndAtomic();
+ }
+ SYSYNC_CATCH (exception &e)
+ return HandleDecodingException((TSyncSession *)userData,"EndAtomic",&e);
+ SYSYNC_ENDCATCH
+ SYSYNC_CATCH (...)
+ return HandleDecodingException((TSyncSession *)userData,"EndAtomic",NULL);
+ SYSYNC_ENDCATCH
+} // TSyncAppBase::EndAtomic
+#endif
+
+
+Ret_t TSyncAppBase::AddCmd(VoidPtr_t userData, SmlAddPtr_t aContentP)
+{
+ if (!userData) return SML_ERR_WRONG_PARAM;
+ // call Session
+ SYSYNC_TRY {
+ return ((TSyncSession *) userData)->AddCmd(aContentP);
+ }
+ SYSYNC_CATCH (exception &e)
+ return HandleDecodingException((TSyncSession *)userData,"AddCmd",&e);
+ SYSYNC_ENDCATCH
+ SYSYNC_CATCH (...)
+ return HandleDecodingException((TSyncSession *)userData,"AddCmd",NULL);
+ SYSYNC_ENDCATCH
+} // TSyncAppBase::AddCmd
+
+
+Ret_t TSyncAppBase::AlertCmd(VoidPtr_t userData, SmlAlertPtr_t aContentP)
+{
+ if (!userData) return SML_ERR_WRONG_PARAM;
+ // call Session
+ SYSYNC_TRY {
+ return ((TSyncSession *) userData)->AlertCmd(aContentP);
+ }
+ SYSYNC_CATCH (exception &e)
+ return HandleDecodingException((TSyncSession *)userData,"AlertCmd",&e);
+ SYSYNC_ENDCATCH
+ SYSYNC_CATCH (...)
+ return HandleDecodingException((TSyncSession *)userData,"AlertCmd",NULL);
+ SYSYNC_ENDCATCH
+} // TSyncAppBase::AlertCmd
+
+
+Ret_t TSyncAppBase::DeleteCmd(VoidPtr_t userData, SmlDeletePtr_t aContentP)
+{
+ if (!userData) return SML_ERR_WRONG_PARAM;
+ // call Session
+ SYSYNC_TRY {
+ return ((TSyncSession *) userData)->DeleteCmd(aContentP);
+ }
+ SYSYNC_CATCH (exception &e)
+ return HandleDecodingException((TSyncSession *)userData,"DeleteCmd",&e);
+ SYSYNC_ENDCATCH
+ SYSYNC_CATCH (...)
+ return HandleDecodingException((TSyncSession *)userData,"DeleteCmd",NULL);
+ SYSYNC_ENDCATCH
+} // TSyncAppBase::DeleteCmd
+
+
+Ret_t TSyncAppBase::GetCmd(VoidPtr_t userData, SmlGetPtr_t aContentP)
+{
+ if (!userData) return SML_ERR_WRONG_PARAM;
+ // call Session
+ SYSYNC_TRY {
+ return ((TSyncSession *) userData)->GetCmd(aContentP);
+ }
+ SYSYNC_CATCH (exception &e)
+ return HandleDecodingException((TSyncSession *)userData,"GetCmd",&e);
+ SYSYNC_ENDCATCH
+ SYSYNC_CATCH (...)
+ return HandleDecodingException((TSyncSession *)userData,"GetCmd",NULL);
+ SYSYNC_ENDCATCH
+} // TSyncAppBase::GetCmd
+
+
+Ret_t TSyncAppBase::PutCmd(VoidPtr_t userData, SmlPutPtr_t aContentP)
+{
+ if (!userData) return SML_ERR_WRONG_PARAM;
+ // call Session
+ SYSYNC_TRY {
+ return ((TSyncSession *) userData)->PutCmd(aContentP);
+ }
+ SYSYNC_CATCH (exception &e)
+ return HandleDecodingException((TSyncSession *)userData,"PutCmd",&e);
+ SYSYNC_ENDCATCH
+ SYSYNC_CATCH (...)
+ return HandleDecodingException((TSyncSession *)userData,"PutCmd",NULL);
+ SYSYNC_ENDCATCH
+} // TSyncAppBase::PutCmd
+
+
+#ifdef MAP_RECEIVE
+Ret_t TSyncAppBase::MapCmd(VoidPtr_t userData, SmlMapPtr_t aContentP)
+{
+ if (!userData) return SML_ERR_WRONG_PARAM;
+ // call Session
+ SYSYNC_TRY {
+ return ((TSyncSession *) userData)->MapCmd(aContentP);
+ }
+ SYSYNC_CATCH (exception &e)
+ return HandleDecodingException((TSyncSession *)userData,"MapCmd",&e);
+ SYSYNC_ENDCATCH
+ SYSYNC_CATCH (...)
+ return HandleDecodingException((TSyncSession *)userData,"MapCmd",NULL);
+ SYSYNC_ENDCATCH
+} // TSyncAppBase::MapCmd
+#endif
+
+
+#ifdef RESULT_RECEIVE
+Ret_t TSyncAppBase::ResultsCmd(VoidPtr_t userData, SmlResultsPtr_t aContentP)
+{
+ if (!userData) return SML_ERR_WRONG_PARAM;
+ // call Session
+ SYSYNC_TRY {
+ return ((TSyncSession *) userData)->ResultsCmd(aContentP);
+ }
+ SYSYNC_CATCH (exception &e)
+ return HandleDecodingException((TSyncSession *)userData,"ResultsCmd",&e);
+ SYSYNC_ENDCATCH
+ SYSYNC_CATCH (...)
+ return HandleDecodingException((TSyncSession *)userData,"ResultsCmd",NULL);
+ SYSYNC_ENDCATCH
+} // TSyncAppBase::ResultsCmd
+#endif
+
+
+Ret_t TSyncAppBase::StatusCmd(VoidPtr_t userData, SmlStatusPtr_t aContentP)
+{
+ if (!userData) return SML_ERR_WRONG_PARAM;
+ // call Session
+ SYSYNC_TRY {
+ return ((TSyncSession *) userData)->StatusCmd(aContentP);
+ }
+ SYSYNC_CATCH (exception &e)
+ return HandleDecodingException((TSyncSession *)userData,"StatusCmd",&e);
+ SYSYNC_ENDCATCH
+ SYSYNC_CATCH (...)
+ return HandleDecodingException((TSyncSession *)userData,"StatusCmd",NULL);
+ SYSYNC_ENDCATCH
+} // TSyncAppBase::StatusCmd
+
+
+Ret_t TSyncAppBase::ReplaceCmd(VoidPtr_t userData, SmlReplacePtr_t aContentP)
+{
+ if (!userData) return SML_ERR_WRONG_PARAM;
+ // call Session
+ SYSYNC_TRY {
+ return ((TSyncSession *) userData)->ReplaceCmd(aContentP);
+ }
+ SYSYNC_CATCH (exception &e)
+ return HandleDecodingException((TSyncSession *)userData,"ReplaceCmd",&e);
+ SYSYNC_ENDCATCH
+ SYSYNC_CATCH (...)
+ return HandleDecodingException((TSyncSession *)userData,"ReplaceCmd",NULL);
+ SYSYNC_ENDCATCH
+} // TSyncAppBase::ReplaceCmd
+
+
+#ifdef COPY_RECEIVE
+Ret_t TSyncAppBase::CopyCmd(VoidPtr_t userData, SmlCopyPtr_t aContentP)
+{
+ if (!userData) return SML_ERR_WRONG_PARAM;
+ // call Session
+ SYSYNC_TRY {
+ return ((TSyncSession *) userData)->CopyCmd(aContentP);
+ }
+ SYSYNC_CATCH (exception &e)
+ return HandleDecodingException((TSyncSession *)userData,"CopyCmd",&e);
+ SYSYNC_ENDCATCH
+ SYSYNC_CATCH (...)
+ return HandleDecodingException((TSyncSession *)userData,"CopyCmd",NULL);
+ SYSYNC_ENDCATCH
+} // TSyncAppBase::CopyCmd
+#endif
+
+
+Ret_t TSyncAppBase::MoveCmd(VoidPtr_t userData, SmlMovePtr_t aContentP)
+{
+ if (!userData) return SML_ERR_WRONG_PARAM;
+ // call Session
+ SYSYNC_TRY {
+ return ((TSyncSession *) userData)->MoveCmd(aContentP);
+ }
+ SYSYNC_CATCH (exception &e)
+ return HandleDecodingException((TSyncSession *)userData,"MoveCmd",&e);
+ SYSYNC_ENDCATCH
+ SYSYNC_CATCH (...)
+ return HandleDecodingException((TSyncSession *)userData,"MoveCmd",NULL);
+ SYSYNC_ENDCATCH
+} // TSyncAppBase::MoveCmd
+
+
+/* Other Callbacks */
+Ret_t TSyncAppBase::HandleError(VoidPtr_t userData)
+{
+ if (!userData) return SML_ERR_WRONG_PARAM;
+ // call Session
+ SYSYNC_TRY {
+ return ((TSyncSession *) userData)->HandleError();
+ }
+ SYSYNC_CATCH (exception &e)
+ return HandleDecodingException((TSyncSession *)userData,"HandleError",&e);
+ SYSYNC_ENDCATCH
+ SYSYNC_CATCH (...)
+ return HandleDecodingException((TSyncSession *)userData,"HandleError",NULL);
+ SYSYNC_ENDCATCH
+} // TSyncAppBase::HandleError
+
+
+
+// %%%%
+Ret_t TSyncAppBase::DummyHandler(VoidPtr_t userData, const char* msg)
+{
+ if (!userData) return SML_ERR_WRONG_PARAM;
+ if (userData) {
+ // session is attached
+ SYSYNC_TRY {
+ return ((TSyncSession *) userData)->DummyHandler(msg);
+ }
+ SYSYNC_CATCH (exception &e)
+ return HandleDecodingException((TSyncSession *)userData,"DummyHandler",&e);
+ SYSYNC_ENDCATCH
+ SYSYNC_CATCH (...)
+ return HandleDecodingException((TSyncSession *)userData,"DummyHandler",NULL);
+ SYSYNC_ENDCATCH
+ }
+ else {
+ DEBUGPRINTFX(DBG_HOT,("DummyHandler (without session attached): msg=%s",msg));
+ return SML_ERR_OK;
+ }
+} // TSyncAppBase::DummyHandler
+
+
+/* SyncML toolkit callback address table */
+
+static const SmlCallbacks_t mySmlCallbacks = {
+ /* message callbacks */
+ smlStartMessageCallback,
+ smlEndMessageCallback,
+ /* grouping commands */
+ smlStartSyncCallback,
+ smlEndSyncCallback,
+ #ifdef ATOMIC_RECEIVE /* these callbacks are NOT included in the Toolkit lite version */
+ smlStartAtomicCallback,
+ smlEndAtomicCallback,
+ #endif
+ #ifdef SEQUENCE_RECEIVE
+ smlStartSequenceCallback,
+ smlEndSequenceCallback,
+ #endif
+ /* Sync Commands */
+ smlAddCmdCallback,
+ smlAlertCmdCallback,
+ smlDeleteCmdCallback,
+ smlGetCmdCallback,
+ smlPutCmdCallback,
+ #ifdef MAP_RECEIVE
+ smlMapCmdCallback,
+ #endif
+ #ifdef RESULT_RECEIVE
+ smlResultsCmdCallback,
+ #endif
+ smlStatusCmdCallback,
+ smlReplaceCmdCallback,
+ /* other commands */
+ #ifdef COPY_RECEIVE /* these callbacks are NOT included in the Toolkit lite version */
+ smlCopyCmdCallback,
+ #endif
+ #ifdef EXEC_RECEIVE
+ smlExecCmdCallback,
+ #endif
+ #ifdef SEARCH_RECEIVE
+ smlSearchCmdCallback,
+ #endif
+ smlMoveCmdCallback,
+ /* Other Callbacks */
+ smlHandleErrorCallback,
+ smlTransmitChunkCallback
+}; /* sml_callbacks struct */
+
+
+/* Context record to find back to appbase and store userData */
+
+typedef struct {
+ TSyncAppBase *appBaseP;
+ void *userDataP;
+} TSmlContextDataRec;
+
+
+/* SyncML toolkit callback implementations */
+
+// macros to simplify access to contex
+#define GET_APPBASE(x) (((TSmlContextDataRec *)x)->appBaseP)
+#define GET_USERDATA(x) (((TSmlContextDataRec *)x)->userDataP)
+
+/* message callbacks */
+Ret_t smlStartMessageCallback(InstanceID_t id, VoidPtr_t userData, SmlSyncHdrPtr_t pContent) { return GET_APPBASE(userData)->StartMessage(id,GET_USERDATA(userData),pContent); }
+Ret_t smlEndMessageCallback(InstanceID_t id, VoidPtr_t userData, Boolean_t final) { return GET_APPBASE(userData)->EndMessage(GET_USERDATA(userData),final); }
+/* grouping commands */
+Ret_t smlStartSyncCallback(InstanceID_t id, VoidPtr_t userData, SmlSyncPtr_t pContent) { return GET_APPBASE(userData)->StartSync(GET_USERDATA(userData),pContent); }
+Ret_t smlEndSyncCallback(InstanceID_t id, VoidPtr_t userData) { return GET_APPBASE(userData)->EndSync(GET_USERDATA(userData)); }
+#ifdef ATOMIC_RECEIVE /* these callbacks are NOT included in the Toolkit lite version */
+ Ret_t smlStartAtomicCallback(InstanceID_t id, VoidPtr_t userData, SmlAtomicPtr_t pContent) { return GET_APPBASE(userData)->StartAtomic(GET_USERDATA(userData),pContent); }
+ Ret_t smlEndAtomicCallback(InstanceID_t id, VoidPtr_t userData) { return GET_APPBASE(userData)->EndAtomic(GET_USERDATA(userData)); }
+#endif
+#ifdef SEQUENCE_RECEIVE
+ Ret_t smlStartSequenceCallback(InstanceID_t id, VoidPtr_t userData, SmlSequencePtr_t pContent) { return GET_APPBASE(userData)->StartSequence(GET_USERDATA(userData),pContent); }
+ Ret_t smlEndSequenceCallback(InstanceID_t id, VoidPtr_t userData) { return GET_APPBASE(userData)->EndSequence(GET_USERDATA(userData)); }
+#endif
+/* Sync Commands */
+Ret_t smlAddCmdCallback(InstanceID_t id, VoidPtr_t userData, SmlAddPtr_t pContent) { return GET_APPBASE(userData)->AddCmd(GET_USERDATA(userData),pContent); }
+Ret_t smlAlertCmdCallback(InstanceID_t id, VoidPtr_t userData, SmlAlertPtr_t pContent) { return GET_APPBASE(userData)->AlertCmd(GET_USERDATA(userData),pContent); }
+Ret_t smlDeleteCmdCallback(InstanceID_t id, VoidPtr_t userData, SmlDeletePtr_t pContent) { return GET_APPBASE(userData)->DeleteCmd(GET_USERDATA(userData),pContent); }
+Ret_t smlGetCmdCallback(InstanceID_t id, VoidPtr_t userData, SmlGetPtr_t pContent) { return GET_APPBASE(userData)->GetCmd(GET_USERDATA(userData),pContent); }
+Ret_t smlPutCmdCallback(InstanceID_t id, VoidPtr_t userData, SmlPutPtr_t pContent) { return GET_APPBASE(userData)->PutCmd(GET_USERDATA(userData),pContent); }
+#ifdef MAP_RECEIVE
+ Ret_t smlMapCmdCallback(InstanceID_t id, VoidPtr_t userData, SmlMapPtr_t pContent) { return GET_APPBASE(userData)->MapCmd(GET_USERDATA(userData),pContent); }
+#endif
+#ifdef RESULT_RECEIVE
+ Ret_t smlResultsCmdCallback(InstanceID_t id, VoidPtr_t userData, SmlResultsPtr_t pContent) { return GET_APPBASE(userData)->ResultsCmd(GET_USERDATA(userData),pContent); }
+#endif
+Ret_t smlStatusCmdCallback(InstanceID_t id, VoidPtr_t userData, SmlStatusPtr_t pContent) { return GET_APPBASE(userData)->StatusCmd(GET_USERDATA(userData),pContent); }
+Ret_t smlReplaceCmdCallback(InstanceID_t id, VoidPtr_t userData, SmlReplacePtr_t pContent) { return GET_APPBASE(userData)->ReplaceCmd(GET_USERDATA(userData),pContent); }
+/* other commands */
+#ifdef COPY_RECEIVE /* these callbacks are NOT included in the Toolkit lite version */
+ Ret_t smlCopyCmdCallback(InstanceID_t id, VoidPtr_t userData, SmlCopyPtr_t pContent) { return GET_APPBASE(userData)->CopyCmd(GET_USERDATA(userData),pContent); }
+#endif
+Ret_t smlMoveCmdCallback(InstanceID_t id, VoidPtr_t userData, SmlMovePtr_t pContent) { return GET_APPBASE(userData)->MoveCmd(GET_USERDATA(userData),pContent); }
+#ifdef EXEC_RECEIVE
+ Ret_t smlExecCmdCallback(InstanceID_t id, VoidPtr_t userData, SmlExecPtr_t pContent) { /*%%%tbd return GET_APPBASE(userData)->ExecCmd(GET_USERDATA(userData),pContent); */ return SML_ERR_INVALID_OPTIONS; }
+#endif
+#ifdef SEARCH_RECEIVE
+ Ret_t smlSearchCmdCallback(InstanceID_t id, VoidPtr_t userData, SmlSearchPtr_t pContent) { /*%%%tbd return GET_APPBASE(userData)->SearchCmd(GET_USERDATA(userData),pContent); */ return SML_ERR_INVALID_OPTIONS; }
+#endif
+/* Other Callbacks */
+Ret_t smlHandleErrorCallback(InstanceID_t id, VoidPtr_t userData) { return GET_APPBASE(userData)->DummyHandler(GET_USERDATA(userData),"ErrorCallback"); }
+Ret_t smlTransmitChunkCallback(InstanceID_t id, VoidPtr_t userData) { /*%%%tdb return GET_APPBASE(userData)->TransmitChunk(GET_USERDATA(userData),pContent); */ return SML_ERR_INVALID_OPTIONS; }
+
+
+/* end callback implementations */
+
+
+/* RTK interfacing */
+
+
+Ret_t TSyncAppBase::setSmlInstanceUserData(
+ InstanceID_t aInstanceID,
+ void *aUserDataP
+)
+{
+ void *ctxP = NULL;
+ if (smlGetUserData(aInstanceID,&ctxP)==SML_ERR_OK && ctxP) {
+ static_cast<TSmlContextDataRec *>(ctxP)->userDataP=aUserDataP;
+ return SML_ERR_OK;
+ }
+ return SML_ERR_MGR_INVALID_INSTANCE_INFO; // invalid instance (has no TSmlContextDataRec in userData)
+} // TSyncAppBase::setSmlInstanceUserData
+
+
+Ret_t TSyncAppBase::getSmlInstanceUserData(
+ InstanceID_t aInstanceID,
+ void **aUserDataPP
+)
+{
+ void *ctxP = NULL;
+ if (smlGetUserData(aInstanceID,&ctxP)==SML_ERR_OK && ctxP) {
+ *aUserDataPP = static_cast<TSmlContextDataRec *>(ctxP)->userDataP;
+ return SML_ERR_OK;
+ }
+ return SML_ERR_MGR_INVALID_INSTANCE_INFO; // invalid instance (has no TSmlContextDataRec in userData)
+} // TSyncAppBase::setSmlInstanceUserData
+
+
+
+
+// create new SyncML toolkit instance
+bool TSyncAppBase::newSmlInstance(
+ SmlEncoding_t aEncoding,
+ sInt32 aWorkspaceMem,
+ InstanceID_t &aInstanceID
+)
+{
+ SmlInstanceOptions_t myInstanceOptions;
+ Ret_t err;
+
+ #ifndef NOWSM
+ #error "Only NOWSM version is supported any more"
+ #endif
+ // Set options
+ // - encoding
+ myInstanceOptions.encoding=aEncoding;
+ // - total size of instance buffer
+ // (must have room for both incoming and outgoing message if instance
+ // is used for both)
+ myInstanceOptions.workspaceSize=aWorkspaceMem;
+ // - maximum outgoing message size
+ myInstanceOptions.maxOutgoingSize=0; // %%% disabled for now, can be set later with setMaxOutgoingSize()
+ // - create user data record
+ TSmlContextDataRec *smlContextRecP = new TSmlContextDataRec;
+ if (!smlContextRecP) return false;
+ smlContextRecP->appBaseP=this; // pointer to find back to this syncappbase w/o the help of global vars
+ smlContextRecP->userDataP=NULL; // userData will be SySyncSession pointer, but now session is not yet determined
+ // - now instantiate (thread-safe!)
+ err=smlInitInstance(
+ &mySmlCallbacks, // callbacks
+ &myInstanceOptions,
+ smlContextRecP,
+ &aInstanceID // where to store the instance ID
+ );
+ // - return instance or NULL if failed
+ if (err==SML_ERR_OK) {
+ DEBUGPRINTFX(DBG_RTK_SML,("////////////// sml Instance created, id(=instanceInfoPtr)=0x%08lX",(long)aInstanceID));
+ return true; // success
+ }
+ else {
+ DEBUGPRINTFX(DBG_ERROR,("************ smlInitInstance returned 0x%hX",(sInt16)err));
+ aInstanceID=NULL; // none
+ return false; // failed
+ }
+} // TSyncAppBase::newSmlInstance
+
+
+void TSyncAppBase::freeSmlInstance(InstanceID_t aInstance)
+{
+ // forget instance now
+ // - accept no-instance
+ if (aInstance==NULL) return;
+ // - there is an instance
+ // - free context record
+ void *ctxP;
+ if (smlGetUserData(aInstance,&ctxP)==SML_ERR_OK && ctxP)
+ delete static_cast<TSmlContextDataRec *>(ctxP);
+ // - free instance itself
+ Ret_t err=smlTerminateInstance(aInstance);
+ DEBUGPRINTFX(DBG_RTK_SML,("////////////// sml Instance freed, id(=instanceInfoPtr)=0x%08lX, err=0x%hX",(long)aInstance,(sInt16)err));
+ #ifdef SYDEBUG
+ if (err!=SML_ERR_OK) {
+ DEBUGPRINTFX(DBG_ERROR,("smlTerminateInstance returned 0x%hX",(sInt16)err));
+ }
+ #endif
+} // TSyncAppBase::freeSmlInstance
+
+
+// save app state (such as settings in datastore configs etc.)
+void TSyncAppBase::saveAppState(void)
+{
+ if (fConfigP) fConfigP->saveAppState();
+} // TSyncAppBase::saveAppState
+
+
+// manufacturer of overall solution (can be configured, while OEM is fixed to Synthesis)
+string TSyncAppBase::getManufacturer(void)
+{
+ #ifdef ENGINEINTERFACE_SUPPORT
+ if (fConfigP && !(fConfigP->fMan.empty()))
+ return fConfigP->fMan;
+ #endif
+ // if no string configured, return default
+ return CUST_SYNC_MAN;
+} // TSyncAppBase::getManufacturer
+
+
+// model (application name) of overall solution
+string TSyncAppBase::getModel(void) {
+ #ifdef ENGINEINTERFACE_SUPPORT
+ if (fConfigP && !(fConfigP->fMod.empty()))
+ return fConfigP->fMod;
+ #endif
+ // if no string configured, return default
+ return CUST_SYNC_MODEL;
+} // TSyncAppBase::getModel
+
+// hardware version
+string TSyncAppBase::getHardwareVersion(void) {
+ #ifdef ENGINEINTERFACE_SUPPORT
+ if (fConfigP && !(fConfigP->fHwV.empty())) {
+ return fConfigP->fHwV;
+ }
+ #endif
+ string s;
+ // if no string configured, return default
+ getPlatformString(pfs_device_name, s);
+ return s;
+} // TSyncAppBase::getHardwareVersion
+
+// firmware version (depends a lot on the context - OS version?)
+string TSyncAppBase::getFirmwareVersion(void) {
+ #ifdef ENGINEINTERFACE_SUPPORT
+ if (fConfigP && !(fConfigP->fFwV.empty())) {
+ return fConfigP->fFwV;
+ }
+ #endif
+ string s;
+ // if no string configured, return default
+ getPlatformString(pfs_platformvers, s);
+ return s;
+} // TSyncAppBase::getHardwareVersion
+
+// hardware type (PDA, PC, ...)
+string TSyncAppBase::getDevTyp() {
+ #ifdef ENGINEINTERFACE_SUPPORT
+ if (fConfigP && !(fConfigP->fDevTyp.empty())) {
+ return fConfigP->fDevTyp;
+ }
+ #endif
+ // if no string configured, return default
+ return SYNCML_CLIENT_DEVTYP;
+} // TSyncAppBase::getDevTyp
+
+#ifdef APP_CAN_EXPIRE
+
+void TSyncAppBase::updateAppExpiry(void)
+{
+ // this is the basic check. Some other checks
+ // are spread in various files to disguise checking a little
+ #if defined(EXPIRES_AFTER_DAYS) || defined(SYSER_REGISTRATION)
+ // check soft expiry
+ fAppExpiryStatus = appEnableStatus();
+ // check hard expiry only if demo, that is, if no valid license is installed
+ if (fAppExpiryStatus==LOCERR_OK && fDaysLeft>=0 && !fRegOK)
+ #endif
+ {
+ fAppExpiryStatus =
+ #ifdef EXPIRES_AFTER_DATE
+ // check hard expiry date
+ fScrambledNow>SCRAMBLED_EXPIRY_VALUE ?
+ LOCERR_EXPIRED : LOCERR_OK;
+ #else
+ // no hard expiry, just ok if enabled
+ LOCERR_OK;
+ #endif
+ }
+} // TSyncAppBase::updateAppExpiry
+
+#endif
+
+
+
+#ifdef SYSER_REGISTRATION
+
+// checks if registered (must be implemented in base class)
+// returns LOCERR_EXPIRED, LOCERR_TOONEW or LOCERR_BADREG if not registered correctly
+localstatus TSyncAppBase::isRegistered(void)
+{
+ #if !defined(HARDCODED_CONFIG) || defined(ENGINEINTERFACE_SUPPORT)
+ // we have licensing in the config file or using engine interface, check it
+ return fConfigP ? checkRegInfo(fConfigP->fLicenseName.c_str(),fConfigP->fLicenseCode.c_str(),false) : LOCERR_BADREG;
+ #else
+ // no license checking at this level (maybe overriden method provides check)
+ return LOCERR_EXPIRED;
+ #endif
+} // TSyncAppBase::isRegistered
+
+
+// get (entire) registration string
+void TSyncAppBase::getRegString(string &aString)
+{
+ #if !defined(HARDCODED_CONFIG) || defined(ENGINEINTERFACE_SUPPORT)
+ // we have licensing in the config file or set via engine interface, use it
+ if (fConfigP)
+ aString=fConfigP->fLicenseName;
+ else
+ #endif
+ {
+ aString.erase();
+ }
+} // TSyncAppBase::getRegString
+
+
+#endif
+
+
+// safety checks
+#if !defined(APP_CAN_EXPIRE) && defined(RELEASE_VERSION) && !defined(NEVER_EXPIRES_IS_OK)
+ #error "Warning: Release version that never expires!"
+#endif
+
+
+#ifdef APP_CAN_EXPIRE
+
+// make sure we have a valid variant code for the target
+#ifndef SYSER_VARIANT_CODE
+ #error "SYSER_VARIANT_CODE must be defined in target_options.h"
+#endif
+
+// check enable status of application
+localstatus TSyncAppBase::appEnableStatus(void)
+{
+ // safety check - app w/o initialized config is NOT enabled
+ // Note: agentconfig tested here, but all other config sections are created in clear() at the same time
+ if (!fConfigP || !(fConfigP->fAgentConfigP)) {
+ return LOCERR_WRONGUSAGE;
+ }
+ // check registration (which will disable normal expiry)
+ #ifdef SYSER_REGISTRATION
+ localstatus regsta = isRegistered(); // ok if registered
+ #else
+ #ifndef APP_CAN_EXPIRE
+ localstatus regsta = LOCERR_OK; // not registerable, not exprining - just run forever
+ #ifdef RELEASE_VERSION
+ #error "WARNING: Completely unlimited operation w/o license or expiry - is this intended??"
+ #endif
+ #else
+ localstatus regsta = LOCERR_BADREG; // not registerable, assume no license, must be eval which expires
+ #endif
+ #endif
+ localstatus sta = regsta;
+ // check expiry (only if registration has not already defined one)
+ #ifdef APP_CAN_EXPIRE
+ #ifdef NO_LICENSE_UNTIL_HARDEXPIRY
+ // we don't need a valid license code until hard expiry date hits
+ // so if we have a failure now, let the hard expiry decide
+ if (regsta!=LOCERR_OK) {
+ #ifdef SYSER_REGISTRATION
+ fDaysLeft=1; // simulate expiring tomorrow
+ #endif
+ sta = LOCERR_OK; // ok if we find no hard expiry later
+ // then let hard expiry decide
+ #ifndef EXPIRES_AFTER_DATE
+ #error "WARNING: NO_LICENSE_UNTIL_HARDEXPIRY without EXPIRES_AFTER_DATE - running forever w/o license - indended that way?"
+ #endif
+ }
+ #endif
+ sInt32 td = lineartime2dateonly(getSystemNowAs(TCTX_UTC));
+ #if defined(EXPIRES_AFTER_DAYS) || defined(NO_LICENSE_UNTIL_HARDEXPIRY)
+ if (regsta==LOCERR_BADREG || regsta==LOCERR_WRONGPROD) {
+ // not registered (for this product), check if we are in evaluation period
+ #ifdef EXPIRES_AFTER_DATE
+ // check hard expiry first
+ if (fScrambledNow>SCRAMBLED_EXPIRY_VALUE) {
+ #ifdef SYSER_REGISTRATION
+ fDaysLeft=0; // hard-expired
+ #endif
+ sta = LOCERR_EXPIRED;
+ }
+ else
+ #endif
+ {
+ #ifdef EXPIRES_AFTER_DAYS
+ // (bfo found that we need to chec for > demo days too, as
+ // otherwise some clever guys could install with the
+ // clock 20 years in the future and then set the clock back)
+ uInt32 vers;
+ lineardate_t firstuse;
+ getFirstUseInfo(SYSER_VARIANT_CODE,firstuse,vers);
+ sInt32 d = firstuse+EXPIRES_AFTER_DAYS - td;
+ fDaysLeft = d>0 && d<=EXPIRES_AFTER_DAYS ? d : 0;
+ sta = d>0 ? LOCERR_OK : LOCERR_EXPIRED;
+ #else
+ // Do NOT change the status to expired! - just pass on the license error status
+ // which might be ok here in case we have a NO_LICENSE_UNTIL_HARDEXPIRY
+ //sta = LOCERR_EXPIRED;
+ #endif
+ }
+ }
+ #endif
+ #else
+ fDaysLeft=-1; // App cannot expire, no limit
+ #endif
+ return sta;
+} // TSyncAppBase::appEnableStatus
+
+
+// get registration information to display (and check internally)
+localstatus TSyncAppBase::getAppEnableInfo(sInt16 &aDaysLeft, string *aRegnameP, string *aRegInternalsP)
+{
+ #ifndef SYSER_REGISTRATION
+ // no registration
+ if (aRegnameP) aRegnameP->erase();
+ if (aRegInternalsP) aRegInternalsP->erase();
+ aDaysLeft = -1; // no expiring license (altough hardexpiry might still apply
+ return appEnableStatus();
+ #else
+ // we have registration
+ localstatus sta = appEnableStatus();
+ if (sta!=LOCERR_OK) {
+ if (aRegnameP) aRegnameP->erase();
+ if (aRegInternalsP) aRegInternalsP->erase();
+ return sta; // not enabled
+ }
+ // do an extra check here
+ aDaysLeft = fDaysLeft;
+ if (fDaysLeft==0) SYSYNC_THROW(exception()); // exit app, as fDaysLeft must be > 0 or -1 here
+ // get strings if requested
+ if (aRegnameP || aRegInternalsP) {
+ string s;
+ // get entire string
+ getRegString(s);
+ // check for separation into a visible (name) and and invisible (email or license restriction) part
+ size_t n,m;
+ size_t l=s.size();
+ // - first priority: license restrictions in form ::x=something
+ n=s.find("::");
+ if (n!=string::npos) {
+ // found a license restriction
+ m=n; // everthing before special separator belongs to name, rest to internals
+ }
+ else {
+ // - second priority: a simple email address at the end of the string
+ n=s.rfind('@');
+ if (n==string::npos) {
+ n=l; m=l; // no email, entire string is name
+ }
+ else {
+ n=s.rfind(' ',n);
+ if (n==string::npos) {
+ n=l; m=l; // no email, entire string is name
+ }
+ else {
+ m=n+1; // do not return separating space in either name nor internals
+ }
+ }
+ }
+
+ // now remove spaces at the beginning to avoid
+ // invisible registration info
+ int i = s.find_first_not_of(' ');
+ if (i==string::npos) i=0; // no non-space -> start at beginning
+ /*
+ int i;
+ for (i= 0; i<n; i++) {
+ if (s.find( ' ',i )!=i) break; // search for the first char, which is not ' '
+ } // if
+ */
+
+ // now assign
+ if (aRegnameP) aRegnameP->assign(s, i,n);
+ if (aRegInternalsP) {
+ if (m<l) aRegInternalsP->assign(s,m,l-m);
+ else aRegInternalsP->erase();
+ }
+ }
+ return sta;
+ #endif
+} // TSyncAppBase::getAppEnableInfo
+
+#endif // APP_CAN_EXPIRE
+
+
+
+#ifdef EXPIRES_AFTER_DAYS
+
+// make sure we have a valid variant code for the target
+#ifndef SYSER_VERSCHECK_MASK
+ #error "SYSER_VERSCHECK_MASK must be defined in target_options.h"
+#endif
+
+/// @brief update first use info to allow for repeated eval when user installs an all-new version
+/// @return true if update was needed
+/// @param[in] aVariant variant context to perform update
+/// @param[in,out] aFirstUseDate date when this variant and version was used first.
+/// Will be updated
+/// @param[in,out] aFirstUseVers version that relates to aFirstUseDate.
+/// Will be updated to current version if version check allows re-starting demo period.
+bool TSyncAppBase::updateFirstUseInfo(lineardate_t &aFirstUseDate, uInt32 &aFirstUseVers)
+{
+ // update if current version is significantly newer than what we used last time
+ if (
+ aFirstUseDate==0 ||
+ ((aFirstUseVers & SYSER_VERSCHECK_MASK) <
+ (SYSYNC_VERSION_UINT32 & SYSER_VERSCHECK_MASK))
+ ) {
+ // current version is different in a relevant part of the version number, so reset first use
+ aFirstUseDate = getSystemNowAs(TCTX_UTC) / linearDateToTimeFactor;
+ aFirstUseVers = SYSYNC_VERSION_UINT32;
+ return true;
+ }
+ // no updates
+ return false;
+} // TSyncAppBase::updateFirstUseInfo
+
+#endif
+
+
+#ifdef SYSER_REGISTRATION
+
+// helper to get next restriction out of license string
+const char * TSyncAppBase::getLicenseRestriction(const char *aInfo, string &aID, string &aVal)
+{
+ const char *p;
+ char c;
+ bool quotedval=false;
+
+ // if no info or at end of string, return NULL
+ if (!aInfo || *aInfo==0) return NULL;
+ // find next restriction
+ aInfo=strstr(aInfo,"::");
+ if (!aInfo) return NULL; // no more restrictions found
+ // get ID of restriction
+ aInfo+=2; // skip ::
+ p=strchr(aInfo,'=');
+ if (!p) return NULL; // no more restrictions found
+ // - assign ID
+ aID.assign(aInfo,p-aInfo);
+ aInfo=p+1; // skip =
+ // get value of restriction
+ aVal.erase();
+ if (*aInfo==0x22) {
+ // starts with doublequote -> treat as quoted value
+ quotedval=true;
+ aInfo++;
+ }
+ while ((c=*aInfo)) {
+ aInfo++;
+ if (quotedval) {
+ // quoted string ends with " and can contain backslash escapes
+ if (c==0x22) break; // done after closing quote
+ if (c=='\\') {
+ if (*aInfo==0) break; // quote without char following, stop here
+ c=*aInfo++; // get next char
+ }
+ }
+ else {
+ // unquoted string ends with next space
+ if (isspace(c)) break;
+ }
+ // save char
+ aVal+=c;
+ }
+ // return where to continue scanning for next item
+ return aInfo;
+} // TSyncAppBase::getLicenseRestriction
+
+
+// checks registration code for CRC ok and compare with
+// predefined constants for product code etc.
+localstatus TSyncAppBase::checkRegInfo(const char *aRegKey, const char *aRegCode, bool aMangled)
+{
+ uInt32 infocrc;
+
+ // extract information from registration code
+ localstatus regSta = LOCERR_OK;
+ fRelDuration=0;
+ // do not allow registration text less than 12 chars
+ if (strlen(aRegKey)<12)
+ regSta=LOCERR_BADREG; // too short code is a bad code
+ else if (!getSySerialInfo(
+ aRegCode, // code
+ fRegProductFlags,
+ fRegProductCode,
+ fRegLicenseType,
+ fRegQuantity,
+ fRegDuration,
+ fLicCRC, // CRC as included in the license code
+ infocrc,
+ aMangled
+ ))
+ regSta=LOCERR_BADREG; // no code is a bad code
+ if (regSta==LOCERR_OK) {
+ // code is basically ok, check standard info
+ if (!(
+ // product code
+ (
+ (fRegProductCode == SYSER_PRODUCT_CODE_MAIN)
+ #ifdef SYSER_PRODUCT_CODE_ALT1
+ || (fRegProductCode == SYSER_PRODUCT_CODE_ALT1)
+ #endif
+ #ifdef SYSER_PRODUCT_CODE_ALT2
+ || (fRegProductCode == SYSER_PRODUCT_CODE_ALT2)
+ #endif
+ #ifdef SYSER_PRODUCT_CODE_ALT3
+ || (fRegProductCode == SYSER_PRODUCT_CODE_ALT3)
+ #endif
+ #ifdef SYSER_PRODUCT_CODE_ALT4
+ || (fRegProductCode == SYSER_PRODUCT_CODE_ALT4)
+ #endif
+ #ifdef SYSER_PRODUCT_CODE_ALT5
+ || (fRegProductCode == SYSER_PRODUCT_CODE_ALT5)
+ #endif
+ #ifdef SYSER_PRODUCT_CODE_ALT6
+ || (fRegProductCode == SYSER_PRODUCT_CODE_ALT6)
+ #endif
+ #ifdef SYSER_PRODUCT_CODE_ALT7
+ || (fRegProductCode == SYSER_PRODUCT_CODE_ALT7)
+ #endif
+ #ifdef SYSER_PRODUCT_CODE_ALT8
+ || (fRegProductCode == SYSER_PRODUCT_CODE_ALT8)
+ #endif
+ #ifdef SYSER_PRODUCT_CODE_ALT9
+ || (fRegProductCode == SYSER_PRODUCT_CODE_ALT9)
+ #endif
+ )
+ // special SYSER_PRODFLAG_MAXRELDATE product flag
+ && (
+ ((SYSER_NEEDED_PRODUCT_FLAGS & SYSER_PRODFLAG_MAXRELDATE)==0) || // either maxreldate flag is not required...
+ (fRegProductFlags & SYSER_PRODFLAG_MAXRELDATE) || // ..or it is present..
+ (fRegDuration!=0) // ..or there is a time limit, which means that the even if the flag is required, product can run with a timed license without the flag
+ )
+ // test other product flags (all except SYSER_PRODFLAG_MAXRELDATE)
+ && ((fRegProductFlags & (SYSER_NEEDED_PRODUCT_FLAGS & ~SYSER_PRODFLAG_MAXRELDATE)) == (SYSER_NEEDED_PRODUCT_FLAGS & ~SYSER_PRODFLAG_MAXRELDATE))
+ && ((fRegProductFlags & SYSER_FORBIDDEN_PRODUCT_FLAGS) == 0)
+ ))
+ regSta=LOCERR_WRONGPROD;
+ // anyway, check if CRC is ok
+ if (!(fLicCRC == addNameToCRC(infocrc,aRegKey,aMangled))) {
+ regSta=LOCERR_BADREG; // bad CRC is bad registration (and has precedence over wrong product)
+ // make sure we do not enable exotic functionality
+ fRegProductFlags=0;
+ fRegLicenseType=0;
+ fRegQuantity=1;
+ fRegDuration=0;
+ fRelDuration=0;
+ }
+ }
+ // Now if we have OK, app is registered, but maybe limited
+ // by time or max release date.
+ if (regSta==LOCERR_OK) {
+ // - check max release date limit first
+ if (fRegProductFlags & SYSER_PRODFLAG_MAXRELDATE) {
+ // duration is a release duration
+ // - in case this is a max release date limited license, this
+ // excludes time limited - max release date licenses are always permanent
+ fRelDuration = fRegDuration;
+ fRegDuration = 0;
+ // Now check if this build has a hard-coded release date
+ #ifdef RELEASE_YEAR
+ // license valid only up to release date specified, check that
+ // if (fRegDuration > (RELEASE_YEAR-2000)*12+RELEASE_MONTH-1) // plain
+ if (
+ fRelDuration &&
+ (fRelDuration*3+48 <= 3*((RELEASE_YEAR-2000)*12+RELEASE_MONTH-1)+48)
+ ) { // a bit disguised
+ // license is not valid any more for a build as new as this one
+ fRegOK=false;
+ return LOCERR_TOONEW;
+ }
+ #endif
+ }
+ // - check for expired license now
+ // Note: we don't check demo period (days after first use) here
+ if (fRegDuration!=0) {
+ lineardate_t ending = date2lineardate(fRegDuration/12+2000,fRegDuration%12+1,1);
+ sInt32 d=ending-lineartime2dateonly(getSystemNowAs(TCTX_UTC));
+ fDaysLeft = d>0 ? d : 0;
+ if (d<=0) {
+ // license has expired
+ fRegOK=false;
+ return LOCERR_EXPIRED;
+ }
+ }
+ else
+ fDaysLeft=-1; // unlimited by registration
+ }
+ // if it is not ok here, registration code is bad
+ fRegOK=regSta==LOCERR_OK; // save for further reference
+ return regSta; // return status
+} // TSyncAppBase::checkRegInfo
+
+
+// checks if current license is properly activated
+// - note: must be called at a time when internet connection is available
+localstatus TSyncAppBase::checkLicenseActivation(lineardate_t &aLastcheck, uInt32 &aLastcrc)
+{
+ bool ok = true;
+
+ // - check if already activated
+ if (
+ fRegLicenseType && (
+ (aLastcrc != fLicCRC) || // different license than at last activation
+ (aLastcheck==0) || // never checked at all
+ (aLastcheck+180 < lineartime2dateonly(getSystemNowAs(TCTX_UTC))) // not checked in the last half year
+ )
+ ) {
+ // re-check needed
+ ok = checkLicenseType(fRegLicenseType);
+ // update check date even if we fail (but only if we checked once before)
+ // to prevent failed sync sessions when reg server has a problem
+ if (ok || aLastcheck) {
+ aLastcheck = lineartime2dateonly(getSystemNowAs(TCTX_UTC));
+ }
+ // update CRC only if we could validate the new CRC
+ if (ok) {
+ aLastcrc = fLicCRC;
+ }
+ }
+ // show it to user
+ if (!ok) {
+ OBJ_PROGRESS_EVENT(this,pev_error,NULL,LOCERR_BADREG,0,0);
+ }
+ // return status
+ return ok ? LOCERR_OK : LOCERR_BADREG;
+} // TSyncAppBase::checkLicenseActivation
+
+#endif
+
+
+#ifdef CONCURRENT_DEVICES_LIMIT
+
+// check if session count is exceeded
+void TSyncAppBase::checkSessionCount(sInt32 aSessionCount, TSyncSession *aSessionP)
+{
+ // Check if session must be busy (due to licensing restrictions)
+ // - some minor arithmetic to hide actual comparison with limit
+ #ifdef SYSER_REGISTRATION
+ // number of users from license or hardcoded limit, whichever is lower
+ sInt32 scrambledlimit = 3*(CONCURRENT_DEVICES_LIMIT!=0 && CONCURRENT_DEVICES_LIMIT<fRegQuantity ? CONCURRENT_DEVICES_LIMIT : fRegQuantity)+42;
+ #else
+ // only hardcoded limit
+ sInt32 scrambledlimit = 3*CONCURRENT_DEVICES_LIMIT+42;
+ #endif
+ if (
+ // compare with hard limit (if zero, count is unlimited)
+ ((aSessionCount<<1)+aSessionCount+42>=scrambledlimit && scrambledlimit!=42)
+ #ifdef CUSTOMIZABLE_DEVICES_LIMIT
+ // compare with configurable limit, if not set to zero (=unlimited)
+ || (((aSessionCount<<2) > fConfigP->fConcurrentDeviceLimit*4) && fConfigP->fConcurrentDeviceLimit)
+ #endif
+ ) {
+ // make session busy (not really responding)
+ aSessionP->setSessionBusy(true);
+ PDEBUGPRINTFX(DBG_HOT,("***** Limit of concurrent sessions reached, server response is 'busy'"));
+ // info
+ CONSOLEPRINTF(("- concurrent session limit reached, rejecting session with BUSY status"));
+ }
+} // TSyncAppBase::checkSessionCount
+
+#endif
+
+
+// factory methods of Rootconfig
+// =============================
+
+
+// Create datatypes registry - default to Multifield-capable version
+void TRootConfig::installDatatypesConfig(void)
+{
+ fDatatypesConfigP = new TMultiFieldDatatypesConfig(this); // default
+} // TSyncAppBase::newDatatypesConfig
+
+
+#ifndef HARDCODED_CONFIG
+
+bool TRootConfig::parseDatatypesConfig(const char **aAttributes, sInt32 aLine)
+{
+ // currently, only one type of datatype registry exists per app, so we do not
+ // need to check attributes
+ expectChildParsing(*fDatatypesConfigP);
+ return true;
+} // TRootConfig::parseDatatypesConfig
+
+#endif
+
+
+} // namespace sysync
+
+
+
+#ifdef CONFIGURABLE_TYPE_SUPPORT
+
+#ifdef MIMEDIR_SUPPORT
+ #include "vcarditemtype.h"
+ #include "vcalendaritemtype.h"
+#endif
+#ifdef TEXTTYPE_SUPPORT
+ #include "textitemtype.h"
+#endif
+#ifdef DATAOBJ_SUPPORT
+ #include "dataobjtype.h"
+#endif
+
+
+namespace sysync {
+
+// create new datatype config by name
+// returns NULL if none found
+TDataTypeConfig *TRootConfig::newDataTypeConfig(const char *aName, const char *aBaseType, TConfigElement *aParentP)
+{
+ #ifdef MIMEDIR_SUPPORT
+ if (strucmp(aBaseType,"mimedir")==0)
+ return new TMIMEDirTypeConfig(aName,aParentP);
+ else if (strucmp(aBaseType,"vcard")==0)
+ return new TVCardTypeConfig(aName,aParentP);
+ else if (strucmp(aBaseType,"vcalendar")==0)
+ return new TVCalendarTypeConfig(aName,aParentP);
+ else
+ #endif
+ #ifdef TEXTTYPE_SUPPORT
+ if (strucmp(aBaseType,"text")==0)
+ return new TTextTypeConfig(aName,aParentP);
+ else
+ #endif
+ #ifdef DATAOBJ_SUPPORT
+ if (strucmp(aBaseType,"dataobj")==0)
+ return new TDataObjConfig(aName,aParentP);
+ else
+ #endif
+ return NULL; // unknown basetype
+} // TRootConfig::newDataTypeConfig
+
+
+// create new profile config by name
+// returns NULL if none found
+TProfileConfig *TRootConfig::newProfileConfig(const char *aName, const char *aTypeName, TConfigElement *aParentP)
+{
+ // create profiles by name
+ #ifdef MIMEDIR_SUPPORT
+ if (strucmp(aTypeName,"mimeprofile")==0)
+ return new TMIMEProfileConfig(aName,aParentP);
+ else
+ #endif
+ #ifdef TEXTTYPE_SUPPORT
+ if (strucmp(aTypeName,"textprofile")==0)
+ return new TTextProfileConfig(aName,aParentP);
+ else
+ #endif
+ return NULL; // unknown profile
+} // TRootConfig::newProfileConfig
+
+} // namespace sysync
+
+#endif
+
+
+
+
+// only one of XML2GO or SDK/Plugin can be on top of customagent
+#ifdef XML2GO_SUPPORT
+ #include "xml2goapiagent.h"
+#elif defined(SDK_SUPPORT)
+ #include "pluginapiagent.h"
+#endif
+// ODBC can be in-between if selected
+#ifdef SQL_SUPPORT
+ #include "odbcapiagent.h"
+#endif
+
+
+namespace sysync {
+
+// Create agent config - default to customImpl based agent
+void TRootConfig::installAgentConfig(void)
+{
+ #if defined(XML2GO_SUPPORT)
+ fAgentConfigP = new TXml2goAgentConfig(this); // xml2go (eventually on top of ODBC)
+ #elif defined(SDK_SUPPORT)
+ fAgentConfigP = new TPluginAgentConfig(this); // plugin/SDK (eventually on top of ODBC)
+ #elif defined(SQL_SUPPORT)
+ fAgentConfigP = new TOdbcAgentConfig(this); // ODBC only
+ #else
+ fAgentConfigP = NULL; // none
+ #endif
+} // TSyncAppBase::installAgentConfig
+
+
+#ifndef HARDCODED_CONFIG
+
+bool TRootConfig::parseAgentConfig(const char **aAttributes, sInt32 aLine)
+{
+ const char *typenam = getAttr(aAttributes,"type");
+ bool parseit=false;
+
+ #ifdef XML2GO_SUPPORT
+ if (strucmp(typenam,"xml2go")==0) parseit=true;
+ #endif
+ #ifdef SDK_SUPPORT
+ if (strucmp(typenam,"plugin")==0) parseit=true;
+ #endif
+ #ifdef SQL_SUPPORT
+ if (strucmp(typenam,"odbc")==0 || strucmp(typenam,"sql")==0) parseit=true;
+ #endif
+ if (parseit) {
+ expectChildParsing(*fAgentConfigP);
+ }
+ return parseit;
+} // TRootConfig::parseAgentConfig
+
+#endif
+
+
+// Support for SySync Diagnostic Tool
+#ifdef SYSYNC_TOOL
+
+#include <errno.h>
+
+// special RTK instance just for translating WBXML to XML
+
+
+#define GET_XMLOUTINSTANCE(u) ((InstanceID_t)GET_USERDATA(u))
+
+// SyncML toolkit callback implementations for sysytool debug decoder
+
+// userData must be instance_id of XML instance to generate XML message into
+
+static Ret_t sysytoolStartMessageCallback(InstanceID_t id, VoidPtr_t userData, SmlSyncHdrPtr_t pContent)
+{
+ // Note: we must find the SyncML version in advance, as fSyncMLVersion is not yet valid here
+ sInt16 hdrVers;
+ StrToEnum(SyncMLVerDTDNames,numSyncMLVersions,hdrVers,smlPCDataToCharP(pContent->version));
+ smlStartMessageExt(GET_XMLOUTINSTANCE(userData),pContent,SmlVersionCodes[hdrVers]);
+ return SML_ERR_OK;
+}
+
+static Ret_t sysytoolEndMessageCallback(InstanceID_t id, VoidPtr_t userData, Boolean_t final)
+{
+ smlEndMessage(GET_XMLOUTINSTANCE(userData),final);
+ return SML_ERR_OK;
+}
+
+static Ret_t sysytoolStartSyncCallback(InstanceID_t id, VoidPtr_t userData, SmlSyncPtr_t pContent)
+{
+ smlStartSync(GET_XMLOUTINSTANCE(userData),pContent);
+ return SML_ERR_OK;
+}
+
+static Ret_t sysytoolEndSyncCallback(InstanceID_t id, VoidPtr_t userData)
+{
+ smlEndSync(GET_XMLOUTINSTANCE(userData));
+ return SML_ERR_OK;
+}
+
+
+#ifdef ATOMIC_RECEIVE /* these callbacks are NOT included in the Toolkit lite version */
+
+static Ret_t sysytoolStartAtomicCallback(InstanceID_t id, VoidPtr_t userData, SmlAtomicPtr_t pContent)
+{
+ smlStartAtomic(GET_XMLOUTINSTANCE(userData),pContent);
+ return SML_ERR_OK;
+}
+
+static Ret_t sysytoolEndAtomicCallback(InstanceID_t id, VoidPtr_t userData)
+{
+ smlEndAtomic(GET_XMLOUTINSTANCE(userData));
+ return SML_ERR_OK;
+}
+
+#endif
+
+#ifdef SEQUENCE_RECEIVE
+
+static Ret_t sysytoolStartSequenceCallback(InstanceID_t id, VoidPtr_t userData, SmlSequencePtr_t pContent)
+{
+ smlStartSequence(GET_XMLOUTINSTANCE(userData),pContent);
+ return SML_ERR_OK;
+}
+
+static Ret_t sysytoolEndSequenceCallback(InstanceID_t id, VoidPtr_t userData)
+{
+ smlEndSequence(GET_XMLOUTINSTANCE(userData));
+ return SML_ERR_OK;
+}
+
+#endif
+
+static Ret_t sysytoolAddCmdCallback(InstanceID_t id, VoidPtr_t userData, SmlAddPtr_t pContent)
+{
+ smlAddCmd(GET_XMLOUTINSTANCE(userData),pContent);
+ return SML_ERR_OK;
+}
+
+static Ret_t sysytoolAlertCmdCallback(InstanceID_t id, VoidPtr_t userData, SmlAlertPtr_t pContent)
+{
+ smlAlertCmd(GET_XMLOUTINSTANCE(userData),pContent);
+ return SML_ERR_OK;
+}
+
+
+static Ret_t sysytoolDeleteCmdCallback(InstanceID_t id, VoidPtr_t userData, SmlDeletePtr_t pContent)
+{
+ smlDeleteCmd(GET_XMLOUTINSTANCE(userData),pContent);
+ return SML_ERR_OK;
+}
+
+static Ret_t sysytoolGetCmdCallback(InstanceID_t id, VoidPtr_t userData, SmlGetPtr_t pContent)
+{
+ smlGetCmd(GET_XMLOUTINSTANCE(userData),pContent);
+ return SML_ERR_OK;
+}
+
+static Ret_t sysytoolPutCmdCallback(InstanceID_t id, VoidPtr_t userData, SmlPutPtr_t pContent)
+{
+ smlPutCmd(GET_XMLOUTINSTANCE(userData),pContent);
+ return SML_ERR_OK;
+}
+
+#ifdef MAP_RECEIVE
+static Ret_t sysytoolMapCmdCallback(InstanceID_t id, VoidPtr_t userData, SmlMapPtr_t pContent)
+{
+ smlMapCmd(GET_XMLOUTINSTANCE(userData),pContent);
+ return SML_ERR_OK;
+}
+#endif
+
+#ifdef RESULT_RECEIVE
+static Ret_t sysytoolResultsCmdCallback(InstanceID_t id, VoidPtr_t userData, SmlResultsPtr_t pContent)
+{
+ smlResultsCmd(GET_XMLOUTINSTANCE(userData),pContent);
+ return SML_ERR_OK;
+}
+#endif
+
+static Ret_t sysytoolStatusCmdCallback(InstanceID_t id, VoidPtr_t userData, SmlStatusPtr_t pContent)
+{
+ smlStatusCmd(GET_XMLOUTINSTANCE(userData),pContent);
+ return SML_ERR_OK;
+}
+
+static Ret_t sysytoolReplaceCmdCallback(InstanceID_t id, VoidPtr_t userData, SmlReplacePtr_t pContent)
+{
+ smlReplaceCmd(GET_XMLOUTINSTANCE(userData),pContent);
+ return SML_ERR_OK;
+}
+
+#ifdef COPY_RECEIVE
+static Ret_t sysytoolCopyCmdCallback(InstanceID_t id, VoidPtr_t userData, SmlCopyPtr_t pContent)
+{
+ smlCopyCmd(GET_XMLOUTINSTANCE(userData),pContent);
+ return SML_ERR_OK;
+}
+#endif
+
+static Ret_t sysytoolMoveCmdCallback(InstanceID_t id, VoidPtr_t userData, SmlMovePtr_t pContent)
+{
+ smlMoveCmd(GET_XMLOUTINSTANCE(userData),pContent);
+ return SML_ERR_OK;
+}
+
+#ifdef EXEC_RECEIVE
+static Ret_t sysytoolExecCmdCallback(InstanceID_t id, VoidPtr_t userData, SmlExecPtr_t pContent)
+{
+ smlExecCmd(GET_XMLOUTINSTANCE(userData),pContent);
+ return SML_ERR_OK;
+}
+#endif
+
+
+#ifdef SEARCH_RECEIVE
+static Ret_t sysytoolSearchCmdCallback(InstanceID_t id, VoidPtr_t userData, SmlSearchPtr_t pContent)
+{
+ smlSearchCmd(GET_XMLOUTINSTANCE(userData),pContent);
+ return SML_ERR_OK;
+}
+#endif
+
+
+static Ret_t sysytoolHandleErrorCallback(InstanceID_t id, VoidPtr_t userData)
+{
+ return SML_ERR_OK;
+}
+
+static Ret_t sysytoolTransmitChunkCallback(InstanceID_t id, VoidPtr_t userData)
+{
+ return SML_ERR_INVALID_OPTIONS;
+}
+
+
+static const SmlCallbacks_t sysyncToolCallbacks = {
+ /* message callbacks */
+ sysytoolStartMessageCallback,
+ sysytoolEndMessageCallback,
+ /* grouping commands */
+ sysytoolStartSyncCallback,
+ sysytoolEndSyncCallback,
+ #ifdef ATOMIC_RECEIVE /* these callbacks are NOT included in the Toolkit lite version */
+ sysytoolStartAtomicCallback,
+ sysytoolEndAtomicCallback,
+ #endif
+ #ifdef SEQUENCE_RECEIVE
+ sysytoolStartSequenceCallback,
+ sysytoolEndSequenceCallback,
+ #endif
+ /* Sync Commands */
+ sysytoolAddCmdCallback,
+ sysytoolAlertCmdCallback,
+ sysytoolDeleteCmdCallback,
+ sysytoolGetCmdCallback,
+ sysytoolPutCmdCallback,
+ #ifdef MAP_RECEIVE
+ sysytoolMapCmdCallback,
+ #endif
+ #ifdef RESULT_RECEIVE
+ sysytoolResultsCmdCallback,
+ #endif
+ sysytoolStatusCmdCallback,
+ sysytoolReplaceCmdCallback,
+ /* other commands */
+ #ifdef COPY_RECEIVE /* these callbacks are NOT included in the Toolkit lite version */
+ sysytoolCopyCmdCallback,
+ #endif
+ #ifdef EXEC_RECEIVE
+ sysytoolExecCmdCallback,
+ #endif
+ #ifdef SEARCH_RECEIVE
+ sysytoolSearchCmdCallback,
+ #endif
+ sysytoolMoveCmdCallback,
+ /* Other Callbacks */
+ sysytoolHandleErrorCallback,
+ sysytoolTransmitChunkCallback
+}; /* sml_callbacks struct */
+
+
+
+// WBXML to XML conversion
+int wbxmlConv(int argc, const char *argv[])
+{
+ if (argc<0) {
+ // help requested
+ CONSOLEPRINTF((" wbxml2xml <wbxml binary message file> [<xml output file>]"));
+ CONSOLEPRINTF((" Converts to XML using SyncML-Toolkit"));
+ CONSOLEPRINTF((" If no output file is specified, input file name with suffix '.xml' is used"));
+ return EXIT_SUCCESS;
+ }
+
+ // check for argument
+ if (argc<1 || argc>2) {
+ CONSOLEPRINTF(("1 or 2 arguments required"));
+ return EXIT_FAILURE;
+ }
+
+ // open input file
+ FILE *inFile = fopen(argv[0],"rb");
+ if (!inFile) {
+ CONSOLEPRINTF(("Error opening input file '%s', error=%d",argv[0],errno));
+ return EXIT_FAILURE;
+ }
+
+ // prepare a instance for decoding the WBXML
+ InstanceID_t wbxmlInInstance;
+ if (!getSyncAppBase()->newSmlInstance(
+ SML_WBXML,
+ 500*1024, // 500k should be waaaaay enough
+ wbxmlInInstance
+ )) {
+ CONSOLEPRINTF(("Error creating WBXML parser"));
+ return EXIT_FAILURE;
+ }
+ // install the sysytool callbacks (default are the normal appbase ones)
+ smlSetCallbacks(wbxmlInInstance, &sysyncToolCallbacks);
+
+ // prepare instance for generating XML output
+ InstanceID_t xmlOutInstance;
+ if (!getSyncAppBase()->newSmlInstance(
+ SML_XML,
+ 500*1024, // 500k should be waaaaay enough
+ xmlOutInstance
+ )) {
+ CONSOLEPRINTF(("Error creating XML generator"));
+ return EXIT_FAILURE;
+ }
+ // link output instance as userdata into input instance
+ getSyncAppBase()->setSmlInstanceUserData(wbxmlInInstance, xmlOutInstance);
+
+ // read WBXML original file into workspace
+ MemPtr_t wbxmlBufP;
+ MemSize_t wbxmlBufSiz, wbxmlDataSiz;
+ Ret_t rc;
+ rc = smlLockWriteBuffer(wbxmlInInstance, &wbxmlBufP, &wbxmlBufSiz);
+ if (rc!=SML_ERR_OK) {
+ CONSOLEPRINTF(("Error getting WBXML buffer, err=%d",rc));
+ return EXIT_FAILURE;
+ }
+ wbxmlDataSiz = fread(wbxmlBufP,1,wbxmlBufSiz,inFile);
+ if (wbxmlDataSiz==0) {
+ CONSOLEPRINTF(("No data in WBXML input file or error, err=%d",errno));
+ return EXIT_FAILURE;
+ }
+ fclose(inFile);
+ CONSOLEPRINTF(("Read %ld bytes from WBXML input file '%s'",wbxmlDataSiz,argv[0]));
+ smlUnlockWriteBuffer(wbxmlInInstance,wbxmlDataSiz);
+
+ // decode
+ do {
+ rc = smlProcessData(wbxmlInInstance, SML_NEXT_COMMAND);
+ } while (rc==SML_ERR_CONTINUE);
+ if (rc!=SML_ERR_OK) {
+ CONSOLEPRINTF(("Error while decoding WBXML document, rc=%d",rc));
+ }
+ else {
+ CONSOLEPRINTF(("Successfully completed decoding WBXML document"));
+ }
+
+ // write to output file
+ // - determine name
+ string outFileName;
+ outFileName = argv[0];
+ if (argc>1)
+ outFileName = argv[1];
+ else
+ outFileName += ".xml";
+ // save output
+ FILE *outFile = fopen(outFileName.c_str(),"w");
+ if (!outFile) {
+ CONSOLEPRINTF(("Error opening output file '%s', error=%d",outFileName.c_str(),errno));
+ return EXIT_FAILURE;
+ }
+ MemPtr_t xmlBufP;
+ MemSize_t xmlDataSiz;
+ rc = smlLockReadBuffer(xmlOutInstance,&xmlBufP,&xmlDataSiz);
+ if (rc!=SML_ERR_OK) {
+ CONSOLEPRINTF(("Error reading decoded XML from converter WBXML document, rc=%d",rc));
+ return EXIT_FAILURE;
+ }
+ CONSOLEPRINTF(("Writing %ld bytes of XML translation to '%s'",xmlDataSiz,outFileName.c_str()));
+ fwrite(xmlBufP,1,xmlDataSiz,outFile);
+ fclose(outFile);
+ // done
+ return EXIT_SUCCESS;
+} // wbxmlConv
+
+#endif // SYSYNC_TOOL
+
+
+
+} // namespace sysync
+
+// eof
diff --git a/src/sysync/syncappbase.h b/src/sysync/syncappbase.h
new file mode 100755
index 0000000..3112213
--- /dev/null
+++ b/src/sysync/syncappbase.h
@@ -0,0 +1,654 @@
+/*
+ * TSyncAppBase
+ * Base class for SySync applications, is supposed to exist
+ * as singular object only, manages "global" things such
+ * as config reading and session dispatching
+ *
+ * Copyright (c) 2002-2009 by Synthesis AG (www.synthesis.ch)
+ */
+
+#ifndef SYNCAPPBASE_H
+#define SYNCAPPBASE_H
+
+#include "configelement.h"
+#include "profiling.h"
+#include "debuglogger.h"
+#include "syncitemtype.h"
+#include "engineinterface.h"
+
+#include "global_progress.h"
+
+// expat if not hardcoded config
+#ifndef HARDCODED_CONFIG
+#include "xmlparse.h"
+#endif
+
+
+namespace sysync {
+
+
+#ifdef SYSYNC_TOOL
+// WBXML to XML conversion
+int wbxmlConv(int argc, const char *argv[]);
+#endif
+
+// XML config doc name (can be overridden in target_options if needed)
+#ifndef XMLCONFIG_DOCNAME
+ #define XMLCONFIG_DOCNAME "sysync_config"
+#endif
+#ifndef XMLCONFIG_DOCVERSION
+ #define XMLCONFIG_DOCVERSION "1.0"
+#endif
+
+
+// progress event posting macro
+#ifdef PROGRESS_EVENTS
+ #define PROGRESS_EVENT(e,d,x,y,z) NotifyProgressEvent(e,d,x,y,z)
+ #define OBJ_PROGRESS_EVENT(o,e,d,x,y,z) o->NotifyProgressEvent(e,d,x,y,z)
+#else
+ #define PROGRESS_EVENT(e,d,x,y,z) true
+ #define OBJ_PROGRESS_EVENT(o,e,d,x,y,z) true
+#endif
+
+// non-class print to console (#ifdef CONSOLEINFO)
+extern "C" void ConsolePrintf(const char *text, ...);
+extern "C" void ConsolePuts(const char *text);
+
+// direct print to app's console, whatever that is
+// NOTE: implemented in derived xxxx_app.cpp
+void AppConsolePuts(const char *aText);
+
+
+#ifndef HARDCODED_CONFIG
+// debug option (combination) names
+const sInt16 numDebugOptions = 37;
+extern const char * const debugOptionNames[numDebugOptions];
+extern const uInt32 debugOptionMasks[numDebugOptions];
+// non-class print to report config errors
+void ConferrPrintf(const char *text, ...);
+#endif
+// provides additional context info
+void writeDebugContextInfo(FILE *logfile);
+
+extern "C" {
+ /* moved to sysync_debug.h to make them accessible by just importing it
+ // non-class debug output functions
+ void DebugVPrintf(const char *format, va_list args);
+ void DebugPrintf(const char *text, ...);
+ void DebugPuts(const char *text);
+ uInt16 getDebugMask(void);
+ */
+ // entry point for XPT part of SyncML-Toolkit with
+ // #define TRACE_TO_STDOUT
+ // functionally equal to DebugPrintf
+ void localOutput(const char *aFormat, va_list aArgs);
+ #ifdef NOWSM
+ // entry points for SML part of SyncML-Toolkit
+ void smlLibPrint(const char *text, ...);
+ void smlLibVprintf(const char *format, va_list va);
+ #endif
+}
+
+
+// static routines for accessing appbase logs from UI_Call_In/DB_Callback
+#ifdef SYDEBUG
+extern "C" void AppBaseLogDebugPuts(void *aCallbackRef, const char *aText);
+extern "C" void AppBaseLogDebugExotic(void *aCallbackRef, const char *aText);
+extern "C" void AppBaseLogDebugBlock(void *aCallbackRef, const char *aTag, const char *aDesc, const char *aAttrText );
+extern "C" void AppBaseLogDebugEndBlock(void *aCallbackRef, const char *aTag);
+extern "C" void AppBaseLogDebugEndThread(void *aCallbackRef);
+#endif
+
+
+#ifndef HARDCODED_CONFIG
+extern const char * const SyncMLEncodingNames[];
+#endif
+extern const char * const SyncMLEncodingMIMENames[];
+
+
+// forward declarations
+class TSyncSession;
+class TSyncAppBase;
+
+class TAgentConfig;
+class TDatatypesConfig;
+class TSyncDataStore;
+class TCommConfig;
+class TDataTypeConfig;
+class TSyncItemType;
+class TRootConfig;
+
+#ifdef SCRIPT_SUPPORT
+class TScriptConfig;
+#endif
+
+// prototype for dispatcher creation function
+// Note: This function must be implemented in the derived TSyncAppBase
+// class' .cpp file. This allows different SySync Apps to be
+// implemented by simply including the appropriate TSyncAppBase
+// derivate. THIS FUNCTION IS NOT IMPLEMENTED in SyncAppBase.cpp!
+TSyncAppBase *newSyncAppBase(void);
+
+#ifdef ENGINEINTERFACE_SUPPORT
+
+// only as an intermediate legacy solution we still grant appBase direct access
+
+#ifdef DIRECT_APPBASE_GLOBALACCESS
+// get access to Sync app base object, creates new one if none exists
+TSyncAppBase *getSyncAppBase(void);
+// get access to existing Sync app base object, NULL if none
+TSyncAppBase *getExistingSyncAppBase(void);
+// We also need a way to access the engineInterface if we are in old AppBase code
+TEngineInterface *getEngineInterface(void);
+// free Sync app base object - which means that engineInterface is deleted
+void freeSyncAppBase(void);
+#endif
+
+#else
+
+// get access to Sync app base object, creates new one if none exists
+TSyncAppBase *getSyncAppBase(void);
+// get access to existing Sync app base object, NULL if none
+TSyncAppBase *getExistingSyncAppBase(void);
+// free Sync app base object
+void freeSyncAppBase(void);
+
+#endif
+
+
+// communication (transport) config object
+class TCommConfig : public TConfigElement
+{
+private:
+ typedef TConfigElement inherited;
+
+public:
+ TCommConfig(const char *aElementName, TConfigElement *aParentElementP) :
+ TConfigElement(aElementName,aParentElementP) {};
+ // nothing special so far
+}; // TCommConfig
+
+
+// agent configuration (an agent is a server or a client)
+class TAgentConfig : public TConfigElement
+{
+ typedef TConfigElement inherited;
+public:
+ TAgentConfig(const char *aElementName, TConfigElement *aParentElementP) :
+ TConfigElement(aElementName,aParentElementP) {};
+ // - MUST be called after creating config to load (or pre-load) variable parts of config
+ // such as binfile profiles. If aDoLoose==false, situations, where existing config
+ // is detected but cannot be re-used will return an error. With aDoLoose==true, config
+ // files etc. are created even if it means a loss of data.
+ virtual localstatus loadVarConfig(bool aDoLoose=false) { return LOCERR_OK; /* NOP and ok by default */ }
+}; // TAgentConfig
+
+
+
+// single data type configuration abstract class
+// Note: derived instances are created using the global newDataTypeConfig() function
+class TDataTypeConfig : public TConfigElement
+{
+ typedef TConfigElement inherited;
+public:
+ TDataTypeConfig(const char *aElementName, TConfigElement *aParentElementP) :
+ TConfigElement(aElementName,aParentElementP) {};
+ // properties
+ // - type name string
+ string fTypeName;
+ // - type version string
+ string fTypeVersion;
+ #ifdef ZIPPED_BINDATA_SUPPORT
+ // if flag is set, payload data will be
+ bool fZippedBindata;
+ sInt16 fZipCompressionLevel;
+ #endif
+ // if flag is set, we can use binary blocks within text (how this is done depends on actual type)
+ bool fBinaryParts;
+ // Unicode payload settings
+ bool fUseUTF16; // 16-bit unicode rather than UTF-8
+ bool fMSBFirst; // byte order for unicode
+ // create Sync Item Type of appropriate type from config
+ virtual TSyncItemType *newSyncItemType(TSyncSession *aSessionP, TSyncDataStore *aDatastoreP) = 0;
+ // get a descriptor for selecting a variant of a datatype (if any), NULL=no variant with this name
+ virtual TTypeVariantDescriptor getVariantDescriptor(const char *aVariantName) { return NULL; };
+protected:
+ #ifdef CONFIGURABLE_TYPE_SUPPORT
+ // check config elements
+ virtual bool localStartElement(const char *aElementName, const char **aAttributes, sInt32 aLine);
+ virtual void localResolve(bool aLastPass);
+ #endif
+ virtual void clear();
+}; // TDataTypeConfig
+
+
+// datatypes list
+typedef std::list<TDataTypeConfig *> TDataTypesList;
+
+// datatypes registry configuration
+class TDatatypesConfig : public TConfigElement
+{
+ typedef TConfigElement inherited;
+public:
+ TDatatypesConfig(const char *aElementName, TConfigElement *aParentElementP);
+ virtual ~TDatatypesConfig();
+ // properties
+ // - list of registered data types
+ TDataTypesList fDataTypesList;
+ // public methods
+ TDataTypeConfig *getDataType(const char *aName);
+protected:
+ #ifdef CONFIGURABLE_TYPE_SUPPORT
+ // check config elements
+ virtual bool localStartElement(const char *aElementName, const char **aAttributes, sInt32 aLine);
+ #endif
+ // resolve is needed also for hardcoded types!
+ virtual void localResolve(bool aLastPass);
+public:
+ virtual void clear();
+}; // TDatatypesConfig
+
+
+
+#ifdef SYDEBUG
+// debug Config element
+class TDebugConfig : public TConfigElement
+{
+ typedef TConfigElement inherited;
+public:
+ // create root config
+ TDebugConfig(const char *aElementName, TConfigElement *aParentElementP);
+ virtual void clear(void);
+ // the debug info path
+ string fDebugInfoPath;
+ // global template for TDebugLogger options
+ TDbgOptions fGlobalDbgLoggerOptions;
+ TDbgOptions fSessionDbgLoggerOptions;
+ // other global debug options
+ // - if <>0 (and #defined SYDEBUG), debug output is generated, value is used as mask
+ uInt32 fDebug;
+ // if set (and #defined MSGDUMP), messages sent and received are logged;
+ bool fMsgDump;
+ // if set, communication will be translated to XML and logged
+ bool fXMLtranslate;
+ // if set (and #defined SIMMSGREAD), simulated input with "i_" prefixed incoming messages are supported
+ bool fSimMsgRead;
+ // if set, session debug logs are enabled by default (but can be disabled by session later)
+ bool fSessionDebugLogs;
+ // if set, global log file is enabled
+ bool fGlobalDebugLogs;
+ // if set, only one single global log file is created (instead of one for every start of the app)
+ bool fSingleGlobLog;
+ // if set, only one single session log file is created (instead of one for every session)
+ bool fSingleSessionLog;
+ // if set, ISO8601 timestamp will be added as part of the session log filename
+ bool fTimedSessionLogNames;
+protected:
+ #ifndef HARDCODED_CONFIG
+ // parsing
+ virtual bool localStartElement(const char *aElementName, const char **aAttributes, sInt32 aLine);
+ #endif
+ // resolving (finishing)
+ virtual void localResolve(bool aLastPass);
+private:
+ uInt32 str2DebugMask(const char **aAttributes);
+}; // TDebugConfig
+#endif
+
+class TProfileConfig; // forward
+
+// root Config element
+class TRootConfig : public TRootConfigElement
+{
+ typedef TRootConfigElement inherited;
+public:
+ // create root config
+ TRootConfig(TSyncAppBase *aSyncAppBaseP);
+ virtual ~TRootConfig();
+ // - factory methods for main config aspects
+ virtual void installCommConfig(void) = 0;
+ virtual void installDatatypesConfig(void);
+ virtual void installAgentConfig(void);
+ // - parsing of main config aspects
+ #ifndef HARDCODED_CONFIG
+ virtual bool parseCommConfig(const char **aAttributes, sInt32 aLine) = 0;
+ virtual bool parseDatatypesConfig(const char **aAttributes, sInt32 aLine);
+ virtual bool parseAgentConfig(const char **aAttributes, sInt32 aLine);
+ #endif
+ #ifdef CONFIGURABLE_TYPE_SUPPORT
+ // - profile configs (MIME profiles, text profiles, etc.)
+ virtual TProfileConfig *newProfileConfig(const char *aName, const char *aTypeName, TConfigElement *aParentP);
+ virtual TDataTypeConfig *newDataTypeConfig(const char *aName, const char *aBaseType, TConfigElement *aParentP);
+ #endif
+ // - hardcoded types
+ #ifdef HARDCODED_TYPE_SUPPORT
+ virtual void createHardcodedTypes(TDatatypesConfig *aDatatypesConfig) = 0;
+ #ifndef NO_REMOTE_RULES
+ #error "%%%% warning: hardcoded type with remoterule support is not tested yet. Should work with old addProperty() calls propertyGroup is 0 by default which should disable grouping"
+ #endif
+ #endif
+ // - hardcoded other config
+ #ifdef HARDCODED_CONFIG
+ // setup config elements (must be implemented in derived RootConfig class)
+ virtual localstatus createHardcodedConfig(void) = 0;
+ #endif
+ // Clear config
+ virtual void clear(void);
+ // save app state (such as settings in datastore configs etc.)
+ virtual void saveAppState(void);
+ // MUST be called after creating config to load (or pre-load) variable parts of config
+ // such as binfile profiles. If aDoLoose==false, situations, where existing config
+ // is detected but cannot be re-used will return an error. With aDoLoose==true, config
+ // files etc. are created even if it means a loss of data.
+ virtual localstatus loadVarConfig(bool aDoLoose=false);
+ // date when config has last changed
+ lineartime_t fConfigDate;
+ #ifndef HARDCODED_CONFIG
+ // string for identifying config file in logs
+ string fConfigIDString;
+ #endif
+ // flag to suppress sending devinf to clients that do not request it
+ bool fNeverPutDevinf;
+ // transport/environment config
+ TCommConfig *fCommConfigP;
+ // Server or Client config
+ TAgentConfig *fAgentConfigP;
+ // datatypes config
+ TDatatypesConfig *fDatatypesConfigP;
+ #ifdef SCRIPT_SUPPORT
+ // global script definitions
+ TScriptConfig *fScriptConfigP;
+ #endif
+ // embedded debug config
+ #ifdef SYDEBUG
+ TDebugConfig fDebugConfig;
+ #endif
+ #ifdef CUSTOMIZABLE_DEVICES_LIMIT
+ // active session limit
+ sInt32 fConcurrentDeviceLimit;
+ #endif
+ #ifdef ENGINEINTERFACE_SUPPORT
+ // for engine libraries, MAN/MOD/HwV/DevTyp can be configured
+ string fMan;
+ string fMod;
+ string fHwV;
+ string fFwV;
+ string fDevTyp;
+ #endif
+ #if defined(SYSER_REGISTRATION) && (!defined(HARDCODED_CONFIG) || defined(ENGINEINTERFACE_SUPPORT))
+ // licensing via config file or engine interface is possible
+ string fLicenseName;
+ string fLicenseCode;
+ #endif
+ // SyncML encoder/decoder parameters
+ uInt32 fLocalMaxMsgSize; // my own maxmsgsize
+ uInt32 fLocalMaxObjSize; // my own maxobjsize, if 0, large object support is disabled
+ // - System time context (usually TCTX_SYSTEM, but might be explicitly set if system TZ info is not available)
+ timecontext_t fSystemTimeContext;
+protected:
+ #ifndef HARDCODED_CONFIG
+ // parsing
+ virtual bool localStartElement(const char *aElementName, const char **aAttributes, sInt32 aLine);
+ #endif
+ // resolving (finishing)
+ virtual void localResolve(bool aLastPass);
+private:
+}; // TRootConfig
+
+
+} // namespace sysync
+
+// now include rest (some of the config classes are
+// required by these includes already)
+#include "sysync.h"
+#include "syncsession.h"
+
+namespace sysync {
+
+
+/* TSyncAppBase is a global, singular object which is instantiated ONCE
+ * per application.
+ */
+class TSyncAppBase {
+ friend class TDebugConfig;
+public:
+ // constructors/destructors
+ TSyncAppBase();
+ virtual ~TSyncAppBase();
+ // request count
+ sInt32 requestCount(void) { return fRequestCount; };
+ sInt32 incRequestCount(void) { return ++fRequestCount; };
+ // config access
+ TRootConfig *getRootConfig(void) { return fConfigP; };
+ // - combine URI and session ID to make a RespURI according to transport
+ virtual void generateRespURI(
+ string & /* aRespURI */,
+ const char * /* aLocalURI */,
+ const char * /* aSessionID */
+ ) { /* nop, no RespURI */ };
+ #ifdef HARDCODED_CONFIG
+ // create hardcoded config (root config element knows how)
+ localstatus initHardcodedConfig(void);
+ #else
+ // config error printing
+ virtual void ConferrPuts(const char *msg);
+ void ConferrPrintf(const char *text, ...);
+ // config reading
+ localstatus readXMLConfigStream(TXMLConfigReadFunc aReaderFunc, void *aContext);
+ virtual localstatus configStatus(void) { if (!fConfigP) return LOCERR_NOCFG; return fConfigP->getFatalError(); };
+ #ifdef CONSTANTXML_CONFIG
+ localstatus readXMLConfigConstant(const char *aConstantXML);
+ #endif
+ localstatus readXMLConfigCFile(FILE *aCfgFile);
+ localstatus readXMLConfigFile(cAppCharP aFilePath);
+ #ifndef ENGINE_LIBRARY
+ localstatus readXMLConfigStandard(const char *aConfigFileName, bool aOnlyGlobal, bool aAbsolute=false);
+ #endif
+ #endif // HARDCODED_CONFIG
+ #ifdef APP_CAN_EXPIRE
+ localstatus fAppExpiryStatus; // LOCERR_OK = ok, LOCERR_BADREG, LOCERR_TOONEW or LOCERR_EXPIRED = bad
+ #endif
+ #ifndef HARDCODED_CONFIG
+ // access to config variables
+ bool getConfigVar(cAppCharP aVarName, string &aValue);
+ bool setConfigVar(cAppCharP aVarName, cAppCharP aNewValue);
+ bool expandConfigVars(string &aString, sInt8 aCfgVarExp, TConfigElement *aCfgElement=NULL, cAppCharP aElementName=NULL);
+ #endif
+ #ifdef SYDEBUG
+ // path where config came from
+ string fConfigFilePath;
+ // access to logging for session
+ TDebugLogger *getDbgLogger(void) { return &fAppLogger; };
+ uInt32 getDbgMask(void) { return fAppLogger.getMask(); };
+ // factory function for debug output channel
+ #ifndef NO_C_FILES
+ virtual TDbgOut *newDbgOutputter(bool aGlobal) { return new TStdFileDbgOut; };
+ #else
+ virtual TDbgOut *newDbgOutputter(bool aGlobal) = 0;
+ #endif
+ #endif // SYDEBUG
+ // app time zones
+ GZones *getAppZones(void) { return &fAppZones; };
+ // Profiling
+ TP_DEFINFO(fTPInfo)
+ // Handle exception happening while decoding commands for a session
+ virtual Ret_t HandleDecodingException(TSyncSession *aSessionP, const char *aRoutine, exception *aExceptionP=NULL) = 0;
+ // access to SyncML toolkit
+ bool newSmlInstance(SmlEncoding_t aEncoding, sInt32 aWorkspaceMem, InstanceID_t &aInstanceID);
+ Ret_t getSmlInstanceUserData(InstanceID_t aInstanceID, void **aUserDataPP);
+ Ret_t setSmlInstanceUserData(InstanceID_t aInstanceID, void *aUserDataP);
+ void freeSmlInstance(InstanceID_t aInstance);
+ // virtual handlers for SyncML toolkit callbacks, must be separately derived for server/client cases
+ // - Start/End Message: derived method in server case actually creates session
+ virtual Ret_t StartMessage(
+ InstanceID_t aSmlWorkspaceID, // SyncML toolkit workspace instance ID
+ VoidPtr_t aUserData, // user data, should be NULL (as StartMessage is responsible for setting userdata)
+ SmlSyncHdrPtr_t aContentP // SyncML tookit's decoded form of the <SyncHdr> element
+ ) = 0;
+ // test if message buffering is available
+ virtual bool canBufferRetryAnswer(void) { return false; }; // must be set in server bases
+ // non-virtual handlers, are always called with userData containing valid session pointer
+ // - end of message
+ Ret_t EndMessage(VoidPtr_t userData, Boolean_t aFinal);
+ // - grouping commands
+ Ret_t StartSync(VoidPtr_t userData, SmlSyncPtr_t aContentP);
+ Ret_t EndSync(VoidPtr_t userData);
+ Ret_t StartSequence(VoidPtr_t userData, SmlSequencePtr_t aContentP);
+ Ret_t EndSequence(VoidPtr_t userData);
+ Ret_t StartAtomic(VoidPtr_t userData, SmlAtomicPtr_t aContentP);
+ Ret_t EndAtomic(VoidPtr_t userData);
+ // - sync commands
+ Ret_t AddCmd(VoidPtr_t userData, SmlAddPtr_t aContentP);
+ Ret_t AlertCmd(VoidPtr_t userData, SmlAlertPtr_t aContentP);
+ Ret_t DeleteCmd(VoidPtr_t userData, SmlDeletePtr_t aContentP);
+ Ret_t GetCmd(VoidPtr_t userData, SmlGetPtr_t aContentP);
+ Ret_t PutCmd(VoidPtr_t userData, SmlPutPtr_t aContentP);
+ #ifdef MAP_RECEIVE
+ Ret_t MapCmd(VoidPtr_t userData, SmlMapPtr_t aContentP);
+ #endif
+ #ifdef RESULT_RECEIVE
+ Ret_t ResultsCmd(VoidPtr_t userData, SmlResultsPtr_t aContentP);
+ #endif
+ Ret_t StatusCmd(VoidPtr_t userData, SmlStatusPtr_t aContentP);
+ Ret_t ReplaceCmd(VoidPtr_t userData, SmlReplacePtr_t aContentP);
+ Ret_t CopyCmd(VoidPtr_t userData, SmlCopyPtr_t aContentP);
+ Ret_t MoveCmd(VoidPtr_t userData, SmlMovePtr_t aContentP);
+ // - error handling
+ Ret_t HandleError(VoidPtr_t userData);
+ Ret_t DummyHandler(VoidPtr_t userData, const char* msg);
+ // flag to indicated deletion in progess (blocking virtuals for debug info)
+ #ifdef SYSER_REGISTRATION
+ // somewhat scattered within object to make reverse engineering harder
+ uInt16 fRegProductCode; // updated by checkRegInfo
+ uInt8 fRegLicenseType; // updated by checkRegInfo
+ #endif
+ // convenience version for getting time
+ lineartime_t getSystemNowAs(timecontext_t aContext) { return sysync::getSystemNowAs(aContext,getAppZones()); };
+protected:
+ // Application custom time zones
+ GZones fAppZones;
+ // Destruction flag
+ bool fDeleting;
+ // config
+ TRootConfig *fConfigP;
+ #ifndef HARDCODED_CONFIG
+ // user-defined config variables
+ TStringToStringMap fConfigVars;
+ #endif
+ // request count
+ sInt32 fRequestCount; // count of requests
+public:
+ #ifdef SYSER_REGISTRATION
+ // somewhat scattered within object to make reverse engineering harder
+ bool fRegOK; // updated by checkRegInfo, used to disable hard-coded-expiry
+ #endif
+ #ifdef PROGRESS_EVENTS
+ // callback for progress events
+ TProgressEventFunc fProgressEventFunc;
+ void *fProgressEventContext;
+ // event generator
+ bool NotifyProgressEvent(
+ TProgressEventType aEventType,
+ TLocalDSConfig *aDatastoreID=NULL,
+ sInt32 aExtra1=0,
+ sInt32 aExtra2=0,
+ sInt32 aExtra3=0
+ );
+ #endif
+ #ifdef ENGINEINTERFACE_SUPPORT
+ // owning engineInterface
+ TEngineInterface *fEngineInterfaceP;
+ #else
+ // "master" pointer, to allow callbacks to refer to "master" object which creates appbase
+ void *fMasterPointer;
+ #endif
+ // save app state (such as settings in datastore configs etc.)
+ virtual void saveAppState(void);
+public:
+ // - inter-module context (needed for pooling global per-process ressources like Java VM)
+ CContext fApiInterModuleContext;
+ #ifdef SYSER_REGISTRATION
+ // somewhat scattered within object to make reverse engineering harder
+ uInt8 fRegProductFlags; // updated by checkRegInfo
+ uInt16 fRegQuantity; // updated by checkRegInfo
+ sInt16 fDaysLeft; // updated by appEnableStatus() and/or checkRegInfo(). 0=expired, -1=not limited
+ #endif
+ // identification
+ // - identification of the application (constant for monolithic builds, configurable for engine library)
+ string getManufacturer();
+ string getModel();
+ string getHardwareVersion();
+ string getFirmwareVersion();
+ // - device type, only used for clients
+ string getDevTyp();
+ // - hardwired information (cannot change, always identifying Synthesis engine and its version
+ cAppCharP getOEM(void) { return SYSYNC_OEM; } // hardwired, not configurable in target options
+ cAppCharP getSoftwareVersion(void) { return SYSYNC_FULL_VERSION_STRING; } // hardwired to real version number
+ // expiry checking
+ #ifdef EXPIRES_AFTER_DATE
+ sInt32 fScrambledNow; // scrambled now, set at syncappbase creation
+ #endif
+ #if defined(APP_CAN_EXPIRE) || defined(SYSER_REGISTRATION)
+ // check if app is enabled
+ localstatus appEnableStatus(void);
+ // get registration information to display
+ localstatus getAppEnableInfo(sInt16 &aDaysLeft, string *aRegnameP=NULL, string *aRegInternalsP=NULL);
+ #else
+ localstatus appEnableStatus(void) { return LOCERR_OK; } // app is always enabled
+ //#warning "WARNING: non-expiring app - is this intended?"
+ #endif
+ #ifdef APP_CAN_EXPIRE
+ // updates fAppExpiryStatus according to registration and hardcoded expiry date
+ void updateAppExpiry(void);
+ #endif
+ #ifdef SYSER_REGISTRATION
+ // updated by checkRegInfo
+ uInt8 fRegDuration;
+ uInt8 fRelDuration;
+ uInt32 fLicCRC;
+ // checks if registered (must be implemented in base class)
+ // returns LOCERR_EXPIRED, LOCERR_TOONEW or LOCERR_BADREG if not registered correctly
+ virtual localstatus isRegistered(void);
+ // checks and saves registration info passed. Returns true if registration is ok
+ // (note: derived class must implement saving)
+ virtual localstatus checkAndSaveRegInfo(const char *aRegKey, const char *aRegCode) { return LOCERR_BADREG; } /* no implementation */
+ // checks registration code for CRC ok and compare with
+ // predefined constants for product code etc.
+ virtual localstatus checkRegInfo(const char *aRegKey, const char *aRegCode, bool aMangled=false);
+ // extended license type depending check (such as activation)
+ virtual bool checkLicenseType(uInt16 aLicenseType) { return aLicenseType==0; }; // defaults to ok for type==0
+ // checks if current license is properly activated
+ localstatus checkLicenseActivation(lineardate_t &aLastcheck, uInt32 &aLastcrc);
+ // scan for license restrictions
+ static const char * getLicenseRestriction(const char *aInfo, string &aID, string &aVal);
+ // get registration string
+ virtual void getRegString(string &aString);
+ #endif
+ // - check for Feature enabled
+ virtual bool isFeatureEnabled(uInt16 aFeatureNo) { return false; /* none enabled by default */ };
+ #ifdef CONCURRENT_DEVICES_LIMIT
+ // check if session count is exceeded
+ void checkSessionCount(sInt32 aSessionCount, TSyncSession *aSessionP);
+ #endif
+ #ifdef EXPIRES_AFTER_DAYS
+ // gets information of first use for a given variant of the software.
+ virtual void getFirstUseInfo(uInt8 aVariant, lineardate_t &aFirstUseDate, uInt32 &aFirstUseVers)
+ { aFirstUseDate=0; aFirstUseVers=0; }
+ // update first use info to allow for repeated eval when user installs an all-new version
+ bool updateFirstUseInfo(lineardate_t &aFirstUseDate, uInt32 &aFirstUseVers);
+ #endif
+private:
+ // debug logging
+ #ifdef SYDEBUG
+ TDebugLogger fAppLogger; // the logger
+ #endif
+}; // TSyncAppBase
+
+
+} // namespace sysync
+
+
+#endif // SYNCAPPBASE_H
+
+
+// eof
diff --git a/src/sysync/syncclient.cpp b/src/sysync/syncclient.cpp
new file mode 100755
index 0000000..f0f10ff
--- /dev/null
+++ b/src/sysync/syncclient.cpp
@@ -0,0 +1,1805 @@
+/*
+ * File: SyncClient.cpp
+ *
+ * Author: Lukas Zeller (luz@synthesis.ch)
+ *
+ * TSyncClient
+ * Provides functionality required for a Synchronisation
+ * client.
+ *
+ * Copyright (c) 2002-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ * 2002-05-06 : luz : created from old SyncClient.h (which was a
+ * intermediate class between server and session,
+ * as initially we considered a client-server
+ * combined class. Now both SyncClient and
+ * SyncServer are directly derived from SyncSession
+ *
+ */
+
+
+// includes
+#include "prefix_file.h"
+#include "sysync.h"
+#include "syncclient.h"
+#include "syncappbase.h"
+
+#ifdef HARD_CODED_SERVER_URI
+ #include "syserial.h"
+#endif
+
+#ifndef SYSYNC_CLIENT
+ #error "SYSYNC_CLIENT must be defined when compiling syncclient.cpp"
+#endif
+
+
+
+namespace sysync {
+
+
+#ifdef PRECONFIGURED_SYNCREQUESTS
+
+// Implementation of TSyncReqConfig
+// ================================
+
+// config for databases to sync with
+TSyncReqConfig::TSyncReqConfig(TLocalDSConfig *aLocalDSCfg, TConfigElement *aParentElement) :
+ TConfigElement("syncrequest",aParentElement),
+ fLocalDSConfig(aLocalDSCfg)
+{
+ clear();
+} // TSyncReqConfig::TSyncReqConfig
+
+
+TSyncReqConfig::~TSyncReqConfig()
+{
+ // nop so far
+} // TSyncReqConfig::~TSyncReqConfig
+
+
+// init defaults
+void TSyncReqConfig::clear(void)
+{
+ // init defaults
+ // - local client datatstore subselection path or CGI (such as "test" in "contact/test")
+ fLocalPathExtension.erase();
+ // - remote server DB layer auth
+ fDBUser.erase();
+ fDBPassword.erase();
+ // - remote server datastore path
+ fServerDBPath.erase();
+ // - sync mode
+ fSyncMode=smo_twoway;
+ fSlowSync=false; // default to non-slow
+ // - DS 1.2 filtering parameters
+ fRecordFilterQuery.erase();
+ fFilterInclusive=false;
+ // clear inherited
+ inherited::clear();
+} // TSyncReqConfig::clear
+
+
+// config element parsing
+bool TSyncReqConfig::localStartElement(const char *aElementName, const char **aAttributes, sInt32 aLine)
+{
+ // checking the elements
+ if (strucmp(aElementName,"localpathextension")==0)
+ expectString(fLocalPathExtension);
+ else if (strucmp(aElementName,"dbuser")==0)
+ expectString(fDBUser);
+ else if (strucmp(aElementName,"dbpassword")==0)
+ expectString(fDBPassword);
+ else if (strucmp(aElementName,"dbpath")==0)
+ expectString(fServerDBPath);
+ else if (strucmp(aElementName,"syncmode")==0)
+ expectEnum(sizeof(fSyncMode),&fSyncMode,SyncModeNames,numSyncModes);
+ else if (strucmp(aElementName,"slowsync")==0)
+ expectBool(fSlowSync);
+ else if (strucmp(aElementName,"recordfilter")==0)
+ expectString(fRecordFilterQuery);
+ else if (strucmp(aElementName,"filterinclusive")==0)
+ expectBool(fFilterInclusive);
+
+ // - none known here
+ else
+ return inherited::localStartElement(aElementName,aAttributes,aLine);
+ // ok
+ return true;
+} // TSyncReqConfig::localStartElement
+
+
+// resolve
+void TSyncReqConfig::localResolve(bool aLastPass)
+{
+ if (aLastPass) {
+ // check for required settings
+ // %%% tbd
+ }
+ // resolve inherited
+ inherited::localResolve(aLastPass);
+} // TSyncReqConfig::localResolve
+
+
+// create appropriate type of local datastore from config and init sync parameters
+TLocalEngineDS *TSyncReqConfig::initNewLocalDataStore(TSyncSession *aSessionP)
+{
+ // - create appropriate type of localdsP
+ TLocalEngineDS *localdsP = fLocalDSConfig->newLocalDataStore(aSessionP);
+ // - set parameters
+ localdsP->dsSetClientSyncParams(
+ fSyncMode,
+ fSlowSync,
+ fServerDBPath.c_str(),
+ fDBUser.c_str(),
+ fDBPassword.c_str(),
+ fLocalPathExtension.c_str(),
+ fRecordFilterQuery.c_str(),
+ fFilterInclusive
+ );
+ return localdsP;
+} // TSyncReqConfig::initNewLocalDataStore
+
+#endif // PRECONFIGURED_SYNCREQUESTS
+
+
+// Implementation of TClientConfig
+// ===============================
+
+
+TClientConfig::TClientConfig(const char* aName, TConfigElement *aParentElement) :
+ TSessionConfig(aName,aParentElement)
+{
+ clear();
+} // TClientConfig::TClientConfig
+
+
+TClientConfig::~TClientConfig()
+{
+ clear();
+} // TClientConfig::~TClientConfig
+
+
+// init defaults
+void TClientConfig::clear(void)
+{
+ // init auth defaults (note that these MUST correspond with the defaults set by loadRemoteParams() !!!
+ fAssumedServerAuth=auth_none; // start with no auth
+ fAssumedServerAuthEnc=fmt_chr; // start with char encoding
+ fAssumedNonce.erase(); // start with no nonce
+ // auth retry options (mainly for stupid servers like SCTS)
+ #ifdef SCTS_COMPATIBILITY_HACKS
+ fNewSessionForAuthRetry=false;
+ fNoRespURIForAuthRetry=false;
+ #else
+ fNewSessionForAuthRetry=true; // all production Synthesis clients had it hardcoded (ifdeffed) this way until 2.9.8.7
+ fNoRespURIForAuthRetry=true; // all production Synthesis clients had it hardcoded (ifdeffed) this way until 2.9.8.7
+ #endif
+ // other defaults
+ fPutDevInfAtSlowSync=true; // smartner server needs it, and it does not harm so we have it on by default
+ #ifndef NO_LOCAL_DBLOGIN
+ fLocalDBUser.erase();
+ fLocalDBPassword.erase();
+ fNoLocalDBLogin=false;
+ #endif
+ #ifdef PRECONFIGURED_SYNCREQUESTS
+ fEncoding=SML_XML; // default to more readable XML
+ fServerUser.erase();
+ fServerPassword.erase();
+ fServerURI.erase();
+ fTransportUser.erase();
+ fTransportPassword.erase();
+ fSocksHost.erase();
+ fProxyHost.erase();
+ fProxyUser.erase();
+ fProxyPassword.erase();
+ // remove sync db specifications
+ TSyncReqList::iterator pos;
+ for(pos=fSyncRequests.begin();pos!=fSyncRequests.end();pos++)
+ delete *pos;
+ fSyncRequests.clear();
+ #endif
+ // clear inherited
+ TSessionConfig::clear();
+ // modify timeout after inherited sets it
+ fSessionTimeout=DEFAULT_CLIENTSESSIONTIMEOUT;
+ // SyncML version support
+ fAssumedServerVersion=MAX_SYNCML_VERSION; // try with highest version we support
+ fMaxSyncMLVersionSupported=MAX_SYNCML_VERSION; // support what we request (overrides session default)
+} // TClientConfig::clear
+
+
+#ifndef HARDCODED_CONFIG
+
+// config element parsing
+bool TClientConfig::localStartElement(const char *aElementName, const char **aAttributes, sInt32 aLine)
+{
+ // checking the elements
+ // - defaults for starting a session
+ if (strucmp(aElementName,"defaultsyncmlversion")==0)
+ expectEnum(sizeof(fAssumedServerVersion),&fAssumedServerVersion,SyncMLVersionNames,numSyncMLVersions);
+ else if (strucmp(aElementName,"defaultauth")==0)
+ expectEnum(sizeof(fAssumedServerAuth),&fAssumedServerAuth,authTypeNames,numAuthTypes);
+ else if (strucmp(aElementName,"defaultauthencoding")==0)
+ expectEnum(sizeof(fAssumedServerAuthEnc),&fAssumedServerAuthEnc,encodingFmtSyncMLNames,numFmtTypes);
+ else if (strucmp(aElementName,"defaultauthnonce")==0)
+ expectString(fAssumedNonce);
+ else if (strucmp(aElementName,"newsessionforretry")==0)
+ expectBool(fNewSessionForAuthRetry);
+ else if (strucmp(aElementName,"originaluriforretry")==0)
+ expectBool(fNoRespURIForAuthRetry);
+ // - other options
+ else if (strucmp(aElementName,"putdevinfatslowsync")==0)
+ expectBool(fPutDevInfAtSlowSync);
+ else if (strucmp(aElementName,"fakedeviceid")==0)
+ expectString(fFakeDeviceID);
+ else
+ #ifndef NO_LOCAL_DBLOGIN
+ if (strucmp(aElementName,"localdbuser")==0)
+ expectString(fLocalDBUser);
+ else if (strucmp(aElementName,"localdbpassword")==0)
+ expectString(fLocalDBPassword);
+ else if (strucmp(aElementName,"nolocaldblogin")==0)
+ expectBool(fNoLocalDBLogin);
+ else
+ #endif
+ // serverURL is always available to allow define fixed URL in config that can't be overridden in profiles
+ if (strucmp(aElementName,"serverurl")==0)
+ expectString(fServerURI);
+ else
+ #ifdef PRECONFIGURED_SYNCREQUESTS
+ if (strucmp(aElementName,"syncmlencoding")==0)
+ expectEnum(sizeof(fEncoding),&fEncoding,SyncMLEncodingNames,numSyncMLEncodings);
+ else if (strucmp(aElementName,"serveruser")==0)
+ expectString(fServerUser);
+ else if (strucmp(aElementName,"serverpassword")==0)
+ expectString(fServerPassword);
+ else if (strucmp(aElementName,"sockshost")==0)
+ expectString(fSocksHost);
+ else if (strucmp(aElementName,"proxyhost")==0)
+ expectString(fProxyHost);
+ else if (strucmp(aElementName,"proxyuser")==0)
+ expectString(fProxyUser);
+ else if (strucmp(aElementName,"proxypassword")==0)
+ expectString(fProxyPassword);
+ else if (strucmp(aElementName,"transportuser")==0)
+ expectString(fTransportUser);
+ else if (strucmp(aElementName,"transportpassword")==0)
+ expectString(fTransportPassword);
+ // - Sync DB specification
+ else if (strucmp(aElementName,"syncrequest")==0) {
+ // definition of a new datastore
+ const char* nam = getAttr(aAttributes,"datastore");
+ if (!nam) {
+ ReportError(true,"syncrequest missing 'datastore' attribute");
+ }
+ else {
+ // search datastore
+ TLocalDSConfig *localDSCfgP = getLocalDS(nam);
+ if (!localDSCfgP)
+ return fail("unknown local datastore '%s' specified",nam);
+ // create new syncDB config linked to that datastore
+ TSyncReqConfig *syncreqcfgP = new TSyncReqConfig(localDSCfgP,this);
+ // - save in list
+ fSyncRequests.push_back(syncreqcfgP);
+ // - let element handle parsing
+ expectChildParsing(*syncreqcfgP);
+ }
+ }
+ else
+ #endif
+ // - none known here
+ return TSessionConfig::localStartElement(aElementName,aAttributes,aLine);
+ // ok
+ return true;
+} // TClientConfig::localStartElement
+
+#endif
+
+// resolve
+void TClientConfig::localResolve(bool aLastPass)
+{
+ if (aLastPass) {
+ #ifdef PRECONFIGURED_SYNCREQUESTS
+ // - resolve requests
+ TSyncReqList::iterator pos;
+ for(pos=fSyncRequests.begin();pos!=fSyncRequests.end();pos++)
+ (*pos)->Resolve(aLastPass);
+ #endif
+ }
+ // resolve inherited
+ inherited::localResolve(aLastPass);
+} // TClientConfig::localResolve
+
+
+
+// Implementation of TSyncClient
+// =============================
+
+
+// constructor
+TSyncClient::TSyncClient(
+ TSyncClientBase *aSyncClientBaseP,
+ cAppCharP aSessionID // a session ID
+) :
+ TSyncSession(aSyncClientBaseP,aSessionID)
+ #ifdef ENGINEINTERFACE_SUPPORT
+ ,fEngineState(ces_idle)
+ #endif
+{
+ #ifdef HARD_CODED_SERVER_URI
+ fNoCRCPrefixLen=0;
+ #endif
+ // reset session now to get correct initial state
+ InternalResetSession();
+ // restart with session numbering at 1 (incremented before use)
+ fClientSessionNo=0;
+} // TSyncClient::TSyncClient
+
+
+// destructor
+TSyncClient::~TSyncClient()
+{
+ // make sure everything is terminated BEFORE destruction of hierarchy begins
+ TerminateSession();
+} // TSyncClient::~TSyncClient
+
+
+// Terminate session
+void TSyncClient::TerminateSession()
+{
+ if (!fTerminated) {
+ InternalResetSession();
+ #ifdef ENGINEINTERFACE_SUPPORT
+ // switch state to done to prevent any further activity via SessionStep()
+ fEngineState = ces_done;
+ #endif
+ }
+ inherited::TerminateSession();
+} // TSyncClient::TerminateSession
+
+
+
+#ifdef ENGINEINTERFACE_SUPPORT
+
+// Support for EngineModule common interface
+// -----------------------------------------
+
+
+/// @brief Executes next step of the session
+/// @param aStepCmd[in/out] step command (STEPCMD_xxx):
+/// - tells caller to send or receive data or end the session etc.
+/// - instructs engine to suspend or abort the session etc.
+/// @param aInfoP[in] pointer to a TEngineProgressInfo structure, NULL if no progress info needed
+/// @return LOCERR_OK on success, SyncML or LOCERR_xxx error code on failure
+TSyError TSyncClient::SessionStep(uInt16 &aStepCmd, TEngineProgressInfo *aInfoP)
+{
+ uInt16 stepCmdIn = aStepCmd;
+ localstatus sta = LOCERR_WRONGUSAGE;
+
+ // init default response
+ aStepCmd = STEPCMD_ERROR; // error
+ if (aInfoP) {
+ aInfoP->eventtype=PEV_NOP;
+ aInfoP->targetID=0;
+ aInfoP->extra1=0;
+ aInfoP->extra2=0;
+ aInfoP->extra3=0;
+ }
+
+ // if session is already aborted, no more steps are required
+ if (isAborted()) {
+ fEngineState = ces_done; // we are done
+ }
+
+ // handle pre-processed step command according to current engine state
+ switch (fEngineState) {
+
+ // Idle state
+ case ces_done :
+ // session done, nothing happens any more
+ aStepCmd = STEPCMD_DONE;
+ sta = LOCERR_OK;
+ break;
+
+ case ces_idle :
+ // in idle, we can only start a session
+ switch (stepCmdIn) {
+ case STEPCMD_CLIENTSTART:
+ case STEPCMD_CLIENTAUTOSTART:
+ // initialize a new session
+ sta = InitializeSession(fProfileSelectorInternal,stepCmdIn==STEPCMD_CLIENTAUTOSTART);
+ if (sta!=LOCERR_OK) break;
+ // engine is now ready, start generating first request
+ fEngineState = ces_generating;
+ // ok with no status
+ aStepCmd = STEPCMD_OK;
+ break;
+ } // switch stepCmdIn for ces_idle
+ break;
+
+ // Ready for generation steps
+ case ces_generating:
+ switch (stepCmdIn) {
+ case STEPCMD_STEP :
+ sta = generatingStep(aStepCmd,aInfoP);
+ break;
+ } // switch stepCmdIn for ces_generating
+ break;
+
+ // Ready for processing steps
+ case ces_processing:
+ switch (stepCmdIn) {
+ case STEPCMD_STEP :
+ sta = processingStep(aStepCmd,aInfoP);
+ break;
+ } // switch stepCmdIn for ces_processing
+ break;
+
+ // Waiting for SyncML data
+ case ces_needdata:
+ switch (stepCmdIn) {
+ case STEPCMD_GOTDATA :
+ // got data, now start processing it
+ OBJ_PROGRESS_EVENT(getSyncAppBase(),pev_recvend,NULL,0,0,0);
+ fIgnoreMsgErrs=false; // do not ignore errors by default
+ fEngineState = ces_processing;
+ aStepCmd = STEPCMD_OK;
+ sta = LOCERR_OK;
+ break;
+ } // switch stepCmdIn for ces_processing
+ break;
+
+ // Waiting until SyncML data is sent
+ case ces_dataready:
+ switch (stepCmdIn) {
+ case STEPCMD_SENTDATA :
+ // sent data, now request answer data
+ OBJ_PROGRESS_EVENT(getSyncAppBase(),pev_sendend,NULL,0,0,0);
+ fEngineState = ces_needdata;
+ aStepCmd = STEPCMD_NEEDDATA;
+ sta = LOCERR_OK;
+ break;
+ } // switch stepCmdIn for ces_processing
+ break;
+
+ case numClientEngineStates:
+ // invalid
+ break;
+
+ } // switch fEngineState
+
+ // done
+ return sta;
+} // TSyncClient::SessionStep
+
+
+
+// Step that generates SyncML data
+TSyError TSyncClient::generatingStep(uInt16 &aStepCmd, TEngineProgressInfo *aInfoP)
+{
+ localstatus sta = LOCERR_WRONGUSAGE;
+ bool done;
+
+ //%%% at this time, generate next message in one step
+ sta = NextMessage(done);
+ if (done) {
+ // done with session, with or without error
+ fEngineState = ces_done; // blocks any further activity with the session
+ aStepCmd = STEPCMD_DONE;
+ // terminate session to provoke all end-of-session progress events
+ TerminateSession();
+ }
+ else if (sta==LOCERR_OK) {
+ // next is sending request to server
+ fEngineState = ces_dataready;
+ aStepCmd = STEPCMD_SENDDATA;
+ OBJ_PROGRESS_EVENT(getSyncAppBase(),pev_sendstart,NULL,0,0,0);
+ }
+ // return status
+ return sta;
+} // TSyncClient::generatingStep
+
+
+
+// Step that processes SyncML data
+TSyError TSyncClient::processingStep(uInt16 &aStepCmd, TEngineProgressInfo *aInfoP)
+{
+ InstanceID_t myInstance = getSmlWorkspaceID();
+ Ret_t rc;
+ localstatus sta = LOCERR_WRONGUSAGE;
+
+ // now process next command
+ PDEBUGPRINTFX(DBG_EXOTIC,("Calling smlProcessData(NEXT_COMMAND)"));
+ #ifdef SYDEBUG
+ MemPtr_t data = NULL;
+ MemSize_t datasize;
+ smlPeekMessageBuffer(getSmlWorkspaceID(), false, &data, &datasize);
+ #endif
+ rc=smlProcessData(
+ myInstance,
+ SML_NEXT_COMMAND
+ );
+ if (rc==SML_ERR_CONTINUE) {
+ // processed ok, but message not completely processed yet
+ // - engine state remains as is
+ aStepCmd = STEPCMD_OK; // ok w/o progress %%% for now, progress is delivered via queue in next step
+ sta = LOCERR_OK;
+ }
+ else if (rc==SML_ERR_OK) {
+ // message completely processed
+ // - switch engine state to generating next message (if any)
+ aStepCmd = STEPCMD_OK;
+ fEngineState = ces_generating;
+ sta = LOCERR_OK;
+ }
+ else {
+ // processing failed
+ PDEBUGPRINTFX(DBG_ERROR,("===> smlProcessData failed, returned 0x%hX",(sInt16)rc));
+ // dump the message that failed to process
+ #ifdef SYDEBUG
+ if (data) DumpSyncMLBuffer(data,datasize,false,rc);
+ #endif
+ if (!fIgnoreMsgErrs) {
+ // abort the session (causing proper error events to be generated and reported back)
+ AbortSession(LOCERR_PROCESSMSG, true);
+ // session is now done
+ fEngineState = ces_done;
+ }
+ else {
+ // we must ignore errors e.g. because of session restart and go back to generate next message
+ fEngineState = ces_generating;
+ }
+ // anyway, step by itself is ok - let app continue stepping (to restart session or complete abort)
+ aStepCmd = STEPCMD_OK;
+ sta = LOCERR_OK;
+ }
+ // now check if this is a session restart
+ if (sta==LOCERR_OK && isStarting()) {
+ // this is still the beginning of a session, which means
+ // that we are restarting the session and caller should close
+ // eventually open communication with the server before sending the next message
+ aStepCmd = STEPCMD_RESTART;
+ }
+ // done
+ return sta;
+} // // TSyncClient::processingStep
+
+#endif // ENGINEINTERFACE_SUPPORT
+
+
+
+
+void TSyncClient::InternalResetSession(void)
+{
+ // use remote URI as specified to start a session
+ fRespondURI = fRemoteURI;
+ #ifdef HARD_CODED_SERVER_URI
+ #if defined(CUSTOM_URI_SUFFIX) && !defined(HARD_CODED_SERVER_URI_LEN)
+ #error "HARD_CODED_SERVER_URI_LEN must be defined when using CUSTOM_URI_SUFFIX"
+ #endif
+ #ifdef HARD_CODED_SERVER_URI_LEN
+ // only part of URL must match (max HARD_CODED_SERVER_URI_LEN chars will be added, less if URI is shorter)
+ fServerURICRC = addNameToCRC(SYSER_CRC32_SEED, fRemoteURI.c_str()+fNoCRCPrefixLen, false, HARD_CODED_SERVER_URI_LEN);
+ #else
+ // entire URL (except prefix) must match
+ fServerURICRC = addNameToCRC(SYSER_CRC32_SEED, fRemoteURI.c_str()+fNoCRCPrefixLen, false);
+ #endif
+ #endif
+ // set SyncML version
+ // Note: will be overridden with call to loadRemoteParams()
+ fSyncMLVersion = syncml_vers_unknown; // unknown
+ // will be cleared to suppress automatic use of DS 1.2 SINCE/BEFORE filters
+ // (e.g. for date range in func_SetDaysRange())
+ fServerHasSINCEBEFORE = true;
+} // TSyncClient::InternalResetSession
+
+
+// Virtual version
+void TSyncClient::ResetSession(void)
+{
+ // let ancestor do its stuff
+ TSyncSession::ResetSession();
+ // do my own stuff (and probably modify settings of ancestor!)
+ InternalResetSession();
+} // TSyncClient::ResetSession
+
+
+
+// initialize the client session and link it with the SML toolkit
+localstatus TSyncClient::InitializeSession(uInt32 aProfileSelector, bool aAutoSyncSession)
+{
+ localstatus sta;
+
+ // Select profile now (before creating instance, as encoding is dependent on profile)
+ sta=SelectProfile(aProfileSelector, aAutoSyncSession);
+ if (sta) return sta;
+ // Start a SyncML toolkit instance now and set the encoding from config
+ InstanceID_t myInstance;
+ if (!getSyncAppBase()->newSmlInstance(
+ getEncoding(),
+ getRootConfig()->fLocalMaxMsgSize * 2, // twice the message size
+ myInstance
+ )) {
+ return LOCERR_SMLFATAL;
+ }
+ // let toolkit know the session pointer
+ if (getSyncAppBase()->setSmlInstanceUserData(myInstance,this)!=SML_ERR_OK) // toolkit must know session (as userData)
+ return LOCERR_SMLFATAL;
+ // remember the instance myself
+ setSmlWorkspaceID(myInstance); // session must know toolkit workspace
+ // done
+ return LOCERR_OK;
+} // TSyncClient::InitializeSession
+
+
+
+// select a profile (returns false if profile not found)
+// Note: base class just tries to retrieve information from
+// config
+localstatus TSyncClient::SelectProfile(uInt32 aProfileSelector, bool aAutoSyncSession)
+{
+ #ifndef PRECONFIGURED_SYNCREQUESTS
+ // no profile settings in config -> error
+ return LOCERR_NOCFG;
+ #else
+ // get profile settings from config
+ TClientConfig *configP = static_cast<TClientConfig *>(getRootConfig()->fAgentConfigP);
+ // - get encoding
+ fEncoding=configP->fEncoding; // SyncML encoding
+ // - set server access details
+ fRemoteURI=configP->fServerURI; // Remote URI = Server URI
+ fTransportUser=configP->fTransportUser; // transport layer user (e.g. HTTP auth)
+ fTransportPassword=configP->fTransportPassword; // transport layer password (e.g. HTTP auth)
+ fServerUser=configP->fServerUser; // Server layer authentification user name
+ fServerPassword=configP->fServerPassword; // Server layer authentification password
+ #ifndef NO_LOCAL_DBLOGIN
+ fLocalDBUser=configP->fLocalDBUser; // Local DB authentification user name (empty if local DB is single user)
+ fNoLocalDBLogin=configP->fNoLocalDBLogin; // if set, no local DB auth takes place, but fLocalDBUser is used as userkey (depending on DB implementation)
+ fLocalDBPassword=configP->fLocalDBPassword; // Local DB authentification password
+ #endif
+ fProxyHost=configP->fProxyHost; // Proxy host
+ fSocksHost=configP->fSocksHost; // Socks host
+ fProxyUser=configP->fProxyUser;
+ fProxyPassword=configP->fProxyPassword;
+ // Reset session after profile change
+ // and also remove any datastores we might have
+ ResetAndRemoveDatastores();
+ // - create and init datastores needed for this session from config
+ // Note: probably config has no sync requests, but they are created later
+ // programmatically
+ TSyncReqList::iterator pos;
+ for (pos=configP->fSyncRequests.begin(); pos!=configP->fSyncRequests.end(); pos++) {
+ // create and init the datastore
+ fLocalDataStores.push_back(
+ (*pos)->initNewLocalDataStore(this)
+ );
+ }
+ // create "new" session ID (derivates will do this better)
+ fClientSessionNo++;
+ return LOCERR_OK;
+ #endif
+} // TSyncClient::SelectProfile
+
+
+// make sure we are logged in to local datastore
+localstatus TSyncClient::LocalLogin(void)
+{
+ #ifndef NO_LOCAL_DBLOGIN
+ if (!fNoLocalDBLogin && !fLocalDBUser.empty()) {
+ // check authorisation (login to correct user) in local DB
+ if (!SessionLogin(fLocalDBUser.c_str(),fLocalDBPassword.c_str(),sectyp_clearpass,fRemoteURI.c_str())) {
+ return localError(401); // done & error
+ }
+ }
+ #endif
+ return LOCERR_OK;
+} // TSyncClient::LocalLogin
+
+
+// starting with engine version 2.0.8.7 a client's device ID (in devinf) is no longer
+// a constant string, but the device's unique ID
+string TSyncClient::getDeviceID(void)
+{
+ if (fLocalURI.empty())
+ return SYSYNC_CLIENT_DEVID; // return default ID
+ else
+ return fLocalURI;
+} // TSyncClient::getDeviceID
+
+string TSyncClient::getDeviceType(void)
+{
+ // taken from configuration or SYSYNC_CLIENT_DEVTYP
+ return getSyncAppBase()->getDevTyp();
+}
+
+
+
+// process message in the instance buffer
+localstatus TSyncClient::processAnswer(void)
+{
+ InstanceID_t myInstance = getSmlWorkspaceID();
+ Ret_t err;
+
+ // now process data
+ DEBUGPRINTF(("===> now calling smlProcessData"));
+ #ifdef SYDEBUG
+ MemPtr_t data = NULL;
+ MemSize_t datasize;
+ smlPeekMessageBuffer(getSmlWorkspaceID(), false, &data, &datasize);
+ #endif
+ fIgnoreMsgErrs=false;
+ err=smlProcessData(
+ myInstance,
+ SML_ALL_COMMANDS
+ );
+ if (err) {
+ // dump the message that failed to process
+ #ifdef SYDEBUG
+ if (data) DumpSyncMLBuffer(data,datasize,false,err);
+ #endif
+ if (!fIgnoreMsgErrs) {
+ PDEBUGPRINTFX(DBG_ERROR,("===> smlProcessData failed, returned 0x%hX",(sInt16)err));
+ // other problem or already using SyncML 1.0 --> error
+ return LOCERR_PROCESSMSG;
+ }
+ }
+ // now check if this is a session restart
+ if (isStarting()) {
+ // this is still the beginning of a session
+ return LOCERR_SESSIONRST;
+ }
+ return LOCERR_OK;
+} // TSyncClientBase::processAnswer
+
+
+
+// let session produce (or finish producing) next message into
+// SML workspace
+// - returns aDone if no answer needs to be sent (=end of session)
+// - returns 0 if successful
+// - returns SyncML status code if unsucessfully aborted session
+localstatus TSyncClient::NextMessage(bool &aDone)
+{
+ TLocalDataStorePContainer::iterator pos;
+ TSyError status;
+
+ TP_START(fTPInfo,TP_general); // could be new thread
+ // default to not continuing
+ aDone=true;
+ #ifdef PROGRESS_EVENTS
+ // check for user suspend
+ if (!getSyncAppBase()->NotifyProgressEvent(pev_suspendcheck)) {
+ SuspendSession(LOCERR_USERSUSPEND);
+ }
+ #endif
+ // done if session was aborted by last received commands
+ if (isAborted()) return getAbortReasonStatus(); // done & error
+ // check package state
+ if (fOutgoingState==psta_idle) {
+ // if suspended here, we'll just stop - nothing has happened yet
+ if (isSuspending()) {
+ AbortSession(fAbortReasonStatus,true);
+ return getAbortReasonStatus();
+ }
+ // start of an entirely new client session
+ #ifdef HARD_CODED_SERVER_URI
+ // extra check to limit hacking
+ if (fServerURICRC != SERVER_URI_CRC) {
+ // someone has tried to change the URI
+ DEBUGPRINTFX(DBG_ERROR,("hardcoded Server URI CRC mismatch"));
+ return LOCERR_LIMITED; // user will not know what this means, but we will
+ }
+ #endif
+ // - check if we have client requests
+ if (fLocalDataStores.size()<1) {
+ PDEBUGPRINTFX(DBG_ERROR,("No datastores defined to sync with"));
+ return LOCERR_NOCFG;
+ }
+ // %%% later, we could probably load cached info about
+ // server requested auth, devinf etc. here
+ // use remote URI as specified to start a session
+ fRespondURI=fRemoteURI;
+ // get default params for sending first message to remote
+ // Note: may include remote flag settings that influence creation of my own ID below, that's why we do it now here
+ loadRemoteParams();
+ // get info about my own URI, whatever that is
+ #ifndef HARDCODED_CONFIG
+ if (!static_cast<TClientConfig *>(getRootConfig()->fAgentConfigP)->fFakeDeviceID.empty()) {
+ // return fake Device ID if we have one defined in the config file (useful for testing)
+ fLocalURI = static_cast<TClientConfig *>(getRootConfig()->fAgentConfigP)->fFakeDeviceID;
+ }
+ else
+ #endif
+ {
+ if (!getLocalDeviceID(fLocalURI) || devidWithUserHash()) {
+ // Device ID is not really unique, make a hash including user name to make it pseudo-unique
+ // create MD5 hash from non-unique ID and user name
+ // Note: when compiled with GUARANTEED_UNIQUE_DEVICID, devidWithUserHash() is always false.
+ md5::SYSYNC_MD5_CTX context;
+ uInt8 digest[16]; // for MD5 digest
+ md5::Init (&context);
+ // - add what we got for ID
+ md5::Update (&context, (uInt8 *)fLocalURI.c_str(), fLocalURI.size());
+ // - add user name, if any
+ if (fLocalURI.size()>0) md5::Update (&context, (uInt8 *)fServerUser.c_str(), fServerUser.size());
+ // - done
+ md5::Final (digest, &context);
+ // now make hex string of that
+ fLocalURI = devidWithUserHash() ? 'x' : 'X'; // start with X to document this special case (lowercase = forced by remoteFlag)
+ for (int n=0; n<16; n++) {
+ AppendHexByte(fLocalURI,digest[n]);
+ }
+ }
+ }
+ // get my own name (if any)
+ getPlatformString(pfs_device_name,fLocalName);
+ // override some of these if not set by loadRemoteParams()
+ if (fSyncMLVersion==syncml_vers_unknown)
+ fSyncMLVersion=static_cast<TClientConfig *>(getRootConfig()->fAgentConfigP)->fAssumedServerVersion;
+ if (fRemoteRequestedAuth==auth_none)
+ fRemoteRequestedAuth=static_cast<TClientConfig *>(getRootConfig()->fAgentConfigP)->fAssumedServerAuth;
+ if (fRemoteRequestedAuthEnc==fmt_chr)
+ fRemoteRequestedAuthEnc=static_cast<TClientConfig *>(getRootConfig()->fAgentConfigP)->fAssumedServerAuthEnc;
+ if (fRemoteNonce.empty())
+ fRemoteNonce=static_cast<TClientConfig *>(getRootConfig()->fAgentConfigP)->fAssumedNonce;
+
+ // we are not yet authenticated for the entire session
+ fNeedAuth=true;
+ // now ready for init
+ fOutgoingState=psta_init; // %%%% could also set psta_initsync for combined init/sync
+ fIncomingState=psta_idle; // remains idle until first answer SyncHdr with OK status is received
+ fInProgress=true; // assume in progress
+ // set session ID string
+ StringObjPrintf(fSynchdrSessionID,"%hd",(sInt16)fClientSessionNo);
+ // now we have a session id, can now display debug stuff
+ #ifdef SYDEBUG
+ string t;
+ StringObjTimestamp(t,getSystemNowAs(TCTX_SYSTEM));
+ PDEBUGPRINTFX(DBG_HOT,("\n[%s] =================> Starting new client session",t.c_str()));
+ #endif
+ // - make sure we are logged into the local database (if needed)
+ status=LocalLogin();
+ if (status!=LOCERR_OK) return status;
+ // create header for first message no noResp
+ issueHeader(false);
+ }
+ else {
+ // check for proper end of session (caused by MessageEnded analysis)
+ if (!fInProgress) {
+ // end sync in all datastores (save anchors etc.)
+ PDEBUGPRINTFX(DBG_PROTO,("Successful end of session -> calling engFinishDataStoreSync() for datastores now"));
+ for (pos=fLocalDataStores.begin(); pos!=fLocalDataStores.end(); ++pos)
+ (*pos)->engFinishDataStoreSync(); // successful end
+ PDEBUGPRINTFX(DBG_PROTO,("Session not any more in progress: NextMessage() returns OK status=0"));
+ return LOCERR_OK; // done & ok
+ }
+ }
+ // check expired case
+ #ifdef APP_CAN_EXPIRE
+ if (getClientBase()->fAppExpiryStatus!=LOCERR_OK) {
+ PDEBUGPRINTFX(DBG_ERROR,("Evaluation Version expired - Please contact Synthesis AG for release version"));
+ return getClientBase()->fAppExpiryStatus; // payment required, done & error
+ }
+ #endif
+ if (fOutgoingState==psta_init || fOutgoingState==psta_initsync) {
+ // - if suspended in init, nothing substantial has happened already, so just exit
+ if (isSuspending() && fOutgoingState==psta_init) {
+ AbortSession(fAbortReasonStatus,true);
+ return getAbortReasonStatus();
+ }
+ // - prepare Alert(s) for databases to sync
+ bool anyfirstsyncs=false;
+ bool anyslowsyncs=false;
+ TLocalEngineDS *localDS;
+ for (pos=fLocalDataStores.begin(); pos!=fLocalDataStores.end(); ++pos) {
+ // prepare alert
+ localDS = *pos;
+ status=localDS->engPrepareClientSyncAlert(NULL); // not as superdatastore
+ if (status!=LOCERR_OK) {
+ // local database error
+ return localError(status); // not found
+ }
+ if (localDS->fFirstTimeSync) anyfirstsyncs=true;
+ if (localDS->fSlowSync) anyslowsyncs=true;
+ }
+ // create Put command if any datastore is doing first time sync / slow sync or devinf Put is externally requested
+ if (mustSendDevInf() || anyfirstsyncs || (anyslowsyncs && static_cast<TClientConfig *>(getRootConfig()->fAgentConfigP)->fPutDevInfAtSlowSync)) {
+ TDevInfPutCommand *putcmdP = new TDevInfPutCommand(this);
+ issueRootPtr(putcmdP);
+ }
+ // try to load devinf from cache (only if we don't know it already)
+ if (!fRemoteDataStoresKnown || !fRemoteDataTypesKnown) {
+ SmlDevInfDevInfPtr_t devinfP;
+ if (loadRemoteDevInf(getRemoteURI(),devinfP)) {
+ // we have cached devinf, analyze it now
+ analyzeRemoteDevInf(devinfP);
+ }
+ }
+ // GET the server's info if server didn't send it and we haven't cached at least the datastores
+ if (!fRemoteDataStoresKnown) {
+ // if we know datastores here, but not types, this means that remote does not have
+ // CTCap, so it makes no sense to issue a GET again.
+ #ifndef NO_DEVINF_GET
+ PDEBUGPRINTFX(DBG_REMOTEINFO,("Nothing known about server, request DevInf using GET command"));
+ TGetCommand *getcommandP = new TGetCommand(this);
+ getcommandP->addTargetLocItem(SyncMLDevInfNames[fSyncMLVersion]);
+ string devinftype=SYNCML_DEVINF_META_TYPE;
+ addEncoding(devinftype);
+ getcommandP->setMeta(newMetaType(devinftype.c_str()));
+ ISSUE_COMMAND_ROOT(this,getcommandP);
+ #endif
+ }
+ // - create Alert(s) for databases to sync
+ for (pos=fLocalDataStores.begin(); pos!=fLocalDataStores.end(); ++pos) {
+ // create alert for non-subdatastores
+ localDS = *pos;
+ if (!localDS->isSubDatastore()) {
+ TAlertCommand *alertcmdP;
+ status=localDS->engGenerateClientSyncAlert(alertcmdP);
+ if (status!=0) {
+ // local database error
+ return status; // not found
+ }
+ ///%%%% unneeded (probably got here by copy&paste accidentally): if (localDS->fFirstTimeSync) anyfirstsyncs=true;
+ // issue alert
+ issueRootPtr(alertcmdP);
+ }
+ }
+ // append sync phase if we have combined init/sync
+ if (fOutgoingState==psta_initsync) fOutgoingState=psta_sync;
+ }
+ // process sync/syncop/map generating phases after init
+ if (!isSuspending()) {
+ // normal, session continues
+ if (fOutgoingState==psta_sync) {
+ // hold back sync until server has finished first package (init or initsync)
+ if (fIncomingState==psta_sync || fIncomingState==psta_initsync) {
+ // start sync for alerted datastores
+ for (pos=fLocalDataStores.begin(); pos!=fLocalDataStores.end(); ++pos) {
+ // Note: some datastores might be aborted due to unsuccessful alert.
+ if ((*pos)->isActive()) {
+ // prepare engine for sync (%%% new routine in 3.2.0.3, summarizing engInitForSyncOps() and
+ // switching to dssta_dataaccessstarted, i.e. loading sync set), but do in only once
+ if (!((*pos)->testState(dssta_syncsetready))) {
+ // not yet started
+ status = (*pos)->engInitForClientSync();
+ if (status!=LOCERR_OK) {
+ // failed
+ AbortSession(status,true);
+ return getAbortReasonStatus();
+ }
+ }
+ /* %%% old code: engInitForSyncOps was here, but switching to dssta_dataaccessstarted
+ was done earlier when handling alert from server. This caused that types
+ were not ready when loading the sync set, making client-side filtering impossible
+ // initialize datastore for sync
+ TStatusCommand dummystatus(this);
+ status = (*pos)->engInitForSyncOps((*pos)->getRemoteDBPath());
+ if (status!=LOCERR_OK) {
+ // failed
+ AbortSession(status,true);
+ return status;
+ }
+ // init prepared, we can now call datastore to generate sync command
+ */
+ // start or continue (which is largely nop, as continuing works via unfinished sync command)
+ // generating sync items
+ (*pos)->engClientStartOfSyncMessage();
+ }
+ }
+ }
+ }
+ else if (fOutgoingState==psta_map) {
+ // hold back map until server has started sync at least (incominstate >=psta_sync)
+ // NOTE: This is according to the specs, which says that client can begin
+ // with Map/update status package BEFORE sync package from server is
+ // completely received.
+ // NOTE: Starfish server expects this and generates a few 222 alerts
+ // if we wait here, but then goes to map as well
+ // (so (fIncomingState==psta_map)-version works as well here!
+ // %%%% other version: wait until server has started map phase as well
+ // %%%% if (fIncomingState==psta_map) {
+ if (fIncomingState>=psta_sync) {
+ // start map for synced datastores
+ for (pos=fLocalDataStores.begin(); pos!=fLocalDataStores.end(); ++pos) {
+ // Note: some datastores might be aborted due to unsuccessful alert.
+ if ((*pos)->isActive()) {
+ // now call datastore to generate map command if not already done
+ (*pos)->engClientStartOfMapMessage(fIncomingState<psta_map);
+ }
+ }
+ }
+ }
+ else if (fOutgoingState==psta_supplement) {
+ // we are waiting for the server to complete a pending phase altough we are already done
+ // with everything we want to send.
+ // -> just generate a Alert 222 and wait for server to complete
+ PDEBUGPRINTFX(DBG_PROTO+DBG_HOT,("Client finished so far, but needs to wait in supplement outgoing state until server finishes phase"));
+ }
+ else if (fOutgoingState!=psta_init) {
+ // NOTE: can be psta_init because "if" begins again after psta_init checking
+ // to allow psta_init appending psta_sync for combined init/sync
+ // no known state
+ return 9999; // %%%%%
+ }
+ } // if not suspended
+
+ // security only: exit here if session got aborted in between
+ if (isAborted())
+ return getAbortReasonStatus(); // done & error
+ if (!fInProgress)
+ return 9999; // that's fine with us
+ // now, we know that we will (most probably) send a message, so default for aDone is false from now on
+ aDone=false;
+ bool outgoingfinal;
+
+ // check for suspend
+ if (isSuspending()) {
+ // make sure we send a Suspend Alert
+ TAlertCommand *alertCmdP = new TAlertCommand(this,NULL,(uInt16)224);
+ // - we just put local and remote URIs here
+ SmlItemPtr_t itemP = newItem();
+ itemP->target = newLocation(fRemoteURI.c_str());
+ itemP->source = newLocation(fLocalURI.c_str());
+ alertCmdP->addItem(itemP);
+ ISSUE_COMMAND_ROOT(this,alertCmdP);
+ // outgoing message is final, regardless of any session state
+ outgoingfinal=true;
+ }
+ else {
+ // Determine if package can be final and if we need an 222 Alert
+ // NOTE: if any commands were interruped or not sent due to outgoing message
+ // size limits, FinishMessage() will prevent final anyway, so no
+ // separate checking for enOfSync or endOfMap is needed.
+ // - can finalize message when server has at least started answering current package
+ // OR if this is the first message (probably repeatedly) sent
+ outgoingfinal = fIncomingState >= fOutgoingState || fIncomingState==psta_idle;
+ if (outgoingfinal) {
+ // allow early success here in case of nothing to respond, and nothing pending
+ // StarFish server does need this...
+ if (!fNeedToAnswer) {
+ if (hasPendingCommands()) {
+ // we have pending commands, cannot be final message
+ outgoingfinal=false;
+ }
+ else {
+ // no pending commands -> we're done now
+ PDEBUGPRINTFX(DBG_PROTO,("Early end of session (nothing to send to server any more) -> calling engFinishDataStoreSync() for datastores now"));
+ // - end sync in all datastores (save anchors etc.)
+ for (pos=fLocalDataStores.begin(); pos!=fLocalDataStores.end(); ++pos)
+ (*pos)->engFinishDataStoreSync(); // successful end
+ PDEBUGPRINTFX(DBG_PROTO,("Session not any more in progress: NextMessage() returns OK status=0"));
+ // done & ok
+ aDone=true;
+ return LOCERR_OK;
+ }
+ }
+ }
+ }
+ if (!outgoingfinal) {
+ // - send Alert 222 if we need to continue package but have nothing to send
+ // (or ALWAYS_CONTINUE222 defined)
+ #ifndef ALWAYS_CONTINUE222
+ if (!fNeedToAnswer)
+ #endif
+ {
+ // not final, and nothing to answer otherwise: create alert-Command to request more info
+ TAlertCommand *alertCmdP = new TAlertCommand(this,NULL,(uInt16)222);
+ // %%% not clear from spec what has to be in item for 222 alert code
+ // but there MUST be an Item for the Alert command according to SyncML TK
+ // - we just put local and remote URIs here
+ SmlItemPtr_t itemP = newItem();
+ itemP->target = newLocation(fRemoteURI.c_str());
+ itemP->source = newLocation(fLocalURI.c_str());
+ alertCmdP->addItem(itemP);
+ ISSUE_COMMAND_ROOT(this,alertCmdP);
+ }
+ }
+ // send custom end-of session puts
+ if (!isSuspending() && outgoingfinal && fOutgoingState==psta_map) {
+ // End of outgoing map package; let custom PUTs which may transmit some session statistics etc. happen now
+ issueCustomEndPut();
+ }
+ // message complete, now finish it
+ FinishMessage(
+ outgoingfinal, // allowed if possible
+ false // final not prevented
+ );
+ // Note, now fNewOutgoingPackage is set (by FinishMessage())
+ // if next message will be responded to with a new package
+
+ // debug info
+ #ifdef SYDEBUG
+ if (PDEBUGMASK & DBG_SESSION) {
+ PDEBUGPRINTFX(DBG_SESSION,(
+ "---> NextMessage, outgoing state='%s', incoming state='%s'",
+ PackageStateNames[fOutgoingState],
+ PackageStateNames[fIncomingState]
+ ));
+ TLocalDataStorePContainer::iterator pos;
+ for (pos=fLocalDataStores.begin(); pos!=fLocalDataStores.end(); ++pos) {
+ // Show state of local datastores
+ PDEBUGPRINTFX(DBG_SESSION,(
+ "Local Datastore '%s': %sState=%s, %s%s sync, %s%s",
+ (*pos)->getName(),
+ (*pos)->isAborted() ? "ABORTED - " : "",
+ (*pos)->getDSStateName(),
+ (*pos)->isResuming() ? "RESUMED " : "",
+ (*pos)->fSlowSync ? "SLOW" : "normal",
+ SyncModeDescriptions[(*pos)->fSyncMode],
+ (*pos)->fServerAlerted ? ", Server-Alerted" : ""
+ ));
+ }
+ }
+ PDEBUGENDBLOCK("SyncML_Outgoing");
+ if (getLastIncomingMsgID()>0) {
+ // we have already received an incoming message, so we have started an "SyncML_Incoming" blocks sometime
+ PDEBUGENDBLOCK("SyncML_Incoming"); // terminate debug block of previous incoming message as well
+ }
+ #endif
+ // ok
+ return LOCERR_OK; // ok
+} // TSyncClient::NextMessage
+
+
+
+// called after successful decoding of an incoming message
+bool TSyncClient::MessageStarted(SmlSyncHdrPtr_t aContentP, TStatusCommand &aStatusCommand, bool aBad)
+{
+ // message not authorized by default
+ fMessageAuthorized=false;
+
+ // Check information from SyncHdr
+ if (
+ aBad ||
+ (!(fSynchdrSessionID==smlPCDataToCharP(aContentP->sessionID))) ||
+ (!(fLocalURI==smlSrcTargLocURIToCharP(aContentP->target)))
+ ) {
+ // bad response
+ PDEBUGPRINTFX(DBG_ERROR,(
+ "Bad SyncHeader from Server. Syntax %s, SessionID (rcvd/correct) = '%s' / '%s', LocalURI (rcvd/correct) = '%s' / '%s'",
+ aBad ? "ok" : "BAD",
+ smlPCDataToCharP(aContentP->sessionID),
+ fSynchdrSessionID.c_str(),
+ smlSrcTargLocURIToCharP(aContentP->target),
+ fLocalURI.c_str()
+ ));
+ aStatusCommand.setStatusCode(400); // bad response/request
+ AbortSession(400,true);
+ return false;
+ }
+ // check for suspend: if we are suspended at this point, this means that we have sent the Suspend Alert already
+ // in the previous message (due to user suspend request), so we can now terminate the session
+ if (isSuspending()) {
+ AbortSession(514,true,LOCERR_USERSUSPEND);
+ return false;
+ }
+ // - RespURI (remote URI to respond to)
+ if (aContentP->respURI) {
+ fRespondURI=smlPCDataToCharP(aContentP->respURI);
+ DEBUGPRINTFX(DBG_PROTO,("RespURI set to = '%s'",fRespondURI.c_str()));
+ }
+ // authorization check
+ // Note: next message will be started not before status for last one
+ // has been processed. Commands issued before will automatically
+ // be queued by issuePtr()
+ // %%% none for now
+ fSessionAuthorized=true;
+ fMessageAuthorized=true;
+ // returns false on BAD header (but true on wrong/bad/missing cred)
+ return true;
+} // TSyncClient::MessageStarted
+
+
+// determines new package states and sets fInProgress
+void TSyncClient::MessageEnded(bool aIncomingFinal)
+{
+ TLocalDataStorePContainer::iterator pos;
+
+ // show status before processing
+ PDEBUGPRINTFX(DBG_SESSION,(
+ "MessageEnded starts : old outgoing state='%s', old incoming state='%s', %sNeedToAnswer",
+ PackageStateNames[fOutgoingState],
+ PackageStateNames[fIncomingState],
+ fNeedToAnswer ? "" : "NO "
+ ));
+ bool allFromClientOnly=false;
+ // process exceptions
+ if (fAborted) {
+ PDEBUGPRINTFX(DBG_ERROR,("***** Session is flagged 'aborted' -> MessageEnded ends package and session"));
+ fOutgoingState=psta_idle;
+ fIncomingState=psta_idle;
+ fInProgress=false;
+ } // if aborted
+ else if (!fMessageAuthorized) {
+ // not authorized messages will just be ignored, so
+ // nothing changes in states
+ // %%% this will probably not really work, as we would need to repeat the last
+ // message in this (unlikely) case that fMessageAuthorized is not set for
+ // a non-first message (first message case is handled in handleHeaderStatus)
+ DEBUGPRINTFX(DBG_ERROR,("***** received Message not authorized, ignore and DONT end package"));
+ fInProgress=true;
+ }
+ else {
+ fInProgress=true; // assume we need to continue
+ // Note: the map phase will not take place, if all datastores are in
+ // send-to-server-only mode and we are not in non-conformant old
+ // synthesis-compatible fCompleteFromClientOnly mode.
+ if (!fCompleteFromClientOnly) {
+ // let all local datastores know that message has ended
+ allFromClientOnly=true;
+ for (pos=fLocalDataStores.begin(); pos!=fLocalDataStores.end(); ++pos) {
+ // check sync modes
+ if ((*pos)->isActive() && (*pos)->getSyncMode()!=smo_fromclient) {
+ allFromClientOnly=false;
+ break;
+ }
+ }
+ }
+ // new outgoing state is determined by the incomingState of this message
+ // (which is the answer to the <final/> message of the previous outgoing package)
+ if (fNewOutgoingPackage && fIncomingState!=psta_idle) {
+ // last message sent was an end-of-package, so next will be a new package
+ if (fIncomingState==psta_init) {
+ // server has responded (or is still responding) to our finished init,
+ // so client enters sync state now (but holds back sync until server
+ // has finished init)
+ fOutgoingState=psta_sync;
+ }
+ else if (fIncomingState==psta_sync || fIncomingState==psta_initsync) {
+ // server has started (or already finished) sending statuses for our
+ // <sync> or its own <sync>
+ // client can enter map state (but holds back maps until server
+ // has finished sync/initsync). In case of allFromClientOnly, we skip the map phase
+ // but only if there is no need to answer.
+ // Otherwise, this is most probably an old (pre 2.9.8.2) Synthesis server that has
+ // sent an empty <sync> (and the status for it has set fNeedToAnswer), so we still
+ // go to map phase.
+ if (allFromClientOnly && !fNeedToAnswer) {
+ fOutgoingState=psta_supplement; // all datastores are from-client-only, skip map phase
+ PDEBUGPRINTFX(DBG_PROTO+DBG_HOT,("All datastores in from-client-only mode, and no need to answer: skip map phase"));
+ }
+ else {
+ fOutgoingState=psta_map; // Some datastores do from-server-only or twoway, so we need a map phase
+ allFromClientOnly=false; // do not skip map phase
+ }
+ }
+ else {
+ // map is finished as well, we might need extra packages just to
+ // finish getting results for map commands
+ fOutgoingState=psta_supplement;
+ }
+ }
+ // New incoming state is simply derived from the incoming state of
+ // this message
+ if (aIncomingFinal && fIncomingState!=psta_idle) {
+ if (fIncomingState==psta_init) {
+ // switch to sync
+ fIncomingState=psta_sync;
+ }
+ else if (fIncomingState==psta_sync || fIncomingState==psta_initsync) {
+ // check what to do
+ if (allFromClientOnly) {
+ // no need to answer and allFromClientOnly -> this is the end of the session
+ fIncomingState=psta_supplement;
+ fInProgress=false; // normally, at end of map answer, we are done
+ }
+ else {
+ fIncomingState=psta_map;
+ }
+ }
+ else {
+ // end of a map phase - end of session (if no fNeedToAnswer)
+ fIncomingState=psta_supplement;
+ // this only ALLOWS ending the session, but it will continue as long
+ // as more than OK for SyncHdr (fNeedToAnswer) must be sent
+ fInProgress=false; // normally, at end of map answer, we are done
+ }
+ }
+ // continue anyway as long as we need to answer
+ if (fNeedToAnswer) fInProgress=true;
+ }
+ // show states
+ PDEBUGPRINTFX(DBG_HOT,(
+ "MessageEnded finishes : new outgoing state='%s', new incoming state='%s', %sNeedToAnswer",
+ PackageStateNames[fOutgoingState],
+ PackageStateNames[fIncomingState],
+ fNeedToAnswer ? "" : "NO "
+ ));
+ // let all local datastores know that message has ended
+ for (pos=fLocalDataStores.begin(); pos!=fLocalDataStores.end(); ++pos) {
+ // let them know
+ (*pos)->engEndOfMessage();
+ // Show state of local datastores
+ PDEBUGPRINTFX(DBG_HOT,(
+ "Local Datastore '%s': %sState=%s, %s%s sync, %s%s",
+ (*pos)->getName(),
+ (*pos)->isAborted() ? "ABORTED - " : "",
+ (*pos)->getDSStateName(),
+ (*pos)->isResuming() ? "RESUMED " : "",
+ (*pos)->isSlowSync() ? "SLOW" : "normal",
+ SyncModeDescriptions[(*pos)->getSyncMode()],
+ (*pos)->fServerAlerted ? ", Server-Alerted" : ""
+ ));
+ }
+ // thread might end here, so stop profiling
+ TP_STOP(fTPInfo);
+} // TSyncClient::MessageEnded
+
+
+// get credentials/username to authenticate with remote party, NULL if none
+SmlCredPtr_t TSyncClient::newCredentialsForRemote(void)
+{
+ if (fNeedAuth) {
+ // generate cretentials from username/password
+ PDEBUGPRINTFX(DBG_PROTO+DBG_USERDATA,("Authenticating with server as user '%s'",fServerUser.c_str()));
+ // NOTE: can be NULL when fServerRequestedAuth is auth_none
+ return newCredentials(
+ fServerUser.c_str(),
+ fServerPassword.c_str()
+ );
+ }
+ else {
+ // already authorized, no auth needed
+ return NULL;
+ }
+} // TSyncClient::newCredentialsForRemote
+
+
+// get client base
+TSyncClientBase *TSyncClient::getClientBase(void)
+{
+ return static_cast<TSyncClientBase *>(getSyncAppBase());
+} // TSyncClient::getClientBase
+
+
+// called when incoming SyncHdr fails to execute
+bool TSyncClient::syncHdrFailure(bool aTryAgain)
+{
+ // do not try to re-execute the header, just let message processing fail;
+ // this will cause the client's main loop to try using an older protocol
+ return false;
+} // TSyncClient::syncHdrFailure
+
+
+// retry older protocol, returns false if no older protocol to try
+bool TSyncClient::retryOlderProtocol(bool aSameVersionRetry, bool aOldMessageInBuffer)
+{
+ if (fIncomingState==psta_idle) {
+ // if we have not started a session yet and not using oldest protocol already,
+ // we want to retry with next older SyncML version
+ if (aSameVersionRetry) {
+ // just retry same version
+ PDEBUGPRINTFX(DBG_PROTO,("Retrying session start with %s",SyncMLVerProtoNames[fSyncMLVersion]));
+ }
+ else if (fSyncMLVersion>getSessionConfig()->fMinSyncMLVersionSupported) {
+ // next lower
+ fSyncMLVersion=(TSyncMLVersions)(((uInt16)fSyncMLVersion)-1);
+ PDEBUGPRINTFX(DBG_PROTO,("Server does not support our SyncML version, trying with %s",SyncMLVerProtoNames[fSyncMLVersion]));
+ }
+ else {
+ // cannot retry
+ return false;
+ }
+ // retry
+ retryClientSessionStart(aOldMessageInBuffer);
+ return true;
+ }
+ // session already started or no older protocol to try
+ return false;
+} // TSyncClient::retryOlderProtocol
+
+
+// prepares client session such that it will do a retry to start a session
+// (but keeping already received auth/nonce/syncML-Version state)
+void TSyncClient::retryClientSessionStart(bool aOldMessageInBuffer)
+{
+ TClientConfig *configP = static_cast<TClientConfig *>(getRootConfig()->fAgentConfigP);
+
+ // now restarting
+ PDEBUGPRINTFX(DBG_HOT,("=================> Retrying Client Session Start"));
+ #ifdef SYDEBUG
+ /*
+ #define AUTH_RETRY_USES_SAME_CLIENT_SESSION
+ #warning "For debugging mightyphone only!!!!"
+ */
+ #endif
+ if (configP->fNewSessionForAuthRetry) {
+ // Notes:
+ // - must apparently be disabled for SCTS 3.1.2 and eventually Mightyphone
+ // - must be enabled e.g for for Magically Server
+ // Create new session ID
+ StringObjPrintf(fSynchdrSessionID,"%hd",(sInt16)++fClientSessionNo);
+ // restart message counting at 1
+ fIncomingMsgID=0;
+ fOutgoingMsgID=0;
+ // we must terminate the block here when we reset fIncomingMsgID, as NextMessage
+ // only closes the incoming block when fIncomingMsgID>0
+ PDEBUGENDBLOCK("SyncML_Incoming");
+ }
+ if (configP->fNoRespURIForAuthRetry) {
+ // Notes:
+ // - must apparently be switched on for Starfish.
+ // - must apparently be switched off for SCTS 3.1.2.
+ // make sure we send next msg to the original URL
+ fRespondURI=fRemoteURI;
+ }
+ // - make sure status for SyncHdr will not be generated!
+ forgetHeaderWaitCommands();
+ // check if we have already started next outgoing message
+ if (!fOutgoingStarted) {
+ if (aOldMessageInBuffer) {
+ // make sure we start with a fresh output buffer
+ // Note: This usually only occur when we are not currently parsing
+ // part of the buffer. If we are parsing, the remaining incoming
+ // message gets cleared as well.
+ getClientBase()->clrUnreadSmlBufferdata();
+ }
+ // start a new message
+ issueHeader(false);
+ }
+ else {
+ if (aOldMessageInBuffer) {
+ PDEBUGPRINTFX(DBG_ERROR,("Warning - restarting session with old message in output buffer"));
+ }
+ }
+ // - make sure subsequent commands (most probably statuses for Alerts)
+ // don't get processed
+ AbortCommandProcessing(0); // silently discard all further commands
+ // - make sure eventual processing errors do not abort the session
+ fIgnoreMsgErrs = true;
+} // TSyncClient::retryClientSessionStart
+
+
+// handle status received for SyncHdr, returns false if not handled
+bool TSyncClient::handleHeaderStatus(TStatusCommand *aStatusCmdP)
+{
+ bool handled=true;
+ const char *txt;
+ SmlMetInfMetInfPtr_t chalmetaP=NULL;
+ SmlChalPtr_t chalP;
+
+ // first evaluate eventual challenge in header status
+ chalP = aStatusCmdP->getStatusElement()->chal;
+ if (chalP) {
+ chalmetaP = smlPCDataToMetInfP(chalP->meta);
+ if (chalmetaP) {
+ sInt16 ty;
+ // - get auth type
+ if (!chalmetaP->type) AbortSession(401,true); // missing auth, but no type
+ txt = smlPCDataToCharP(chalmetaP->type);
+ PDEBUGPRINTFX(DBG_PROTO,("Remote requests auth type='%s'",txt));
+ if (StrToEnum(authTypeSyncMLNames,numAuthTypes,ty,txt))
+ fRemoteRequestedAuth=(TAuthTypes)ty;
+ else {
+ AbortSession(406,true); // unknown auth type, not supported
+ goto donewithstatus;
+ }
+ // - get auth format
+ if (!smlPCDataToFormat(chalmetaP->format, fRemoteRequestedAuthEnc)) {
+ AbortSession(406,true); // unknown auth format, not supported
+ goto donewithstatus;
+ }
+ // - get next nonce
+ if (chalmetaP->nextnonce) {
+ // decode B64
+ uInt32 l;
+ uInt8 *nonce = b64::decode(smlPCDataToCharP(chalmetaP->nextnonce), 0, &l);
+ fRemoteNonce.assign((char *)nonce,l);
+ sysync_free(nonce);
+ }
+ // - show
+ PDEBUGPRINTFX(DBG_PROTO,(
+ "Next Cred will have type='%s' and format='%s' and use nonce='%s'",
+ authTypeNames[fRemoteRequestedAuth],
+ encodingFmtNames[fRemoteRequestedAuthEnc],
+ fRemoteNonce.c_str()
+ ));
+ }
+ /* %%% do not save here already, we don't know if SyncML version is ok
+ moved to those status code cases below that signal
+ // let descendant eventually save auth params
+ saveRemoteParams();
+ */
+ }
+ // now evaluate status code
+ switch (aStatusCmdP->getStatusCode()) {
+ case 101: // Busy
+ // Abort
+ AbortSession(101,false);
+ break;
+ case 212: // authentication accepted for entire session
+ fNeedAuth=false; // no need for further auth
+ PDEBUGPRINTFX(DBG_PROTO,("Remote accepted authentication for entire session"));
+ case 200: // authentication accepted for this message
+ // if this is the first authorized message we get an OK for the synchdr, this is
+ // also the first incoming message that is really processed as init message
+ if (fIncomingState==psta_idle && fMessageAuthorized) {
+ // first incoming is expected to be same as first outgoing (init or initsync)
+ fIncomingState=fOutgoingState;
+ PDEBUGPRINTFX(DBG_PROTO,("Authenticated successfully with remote server"));
+ }
+ else {
+ PDEBUGPRINTFX(DBG_PROTO,("Authentication with server ok for this message"));
+ }
+ // let descendant eventually save auth params
+ saveRemoteParams();
+ break;
+ case 501: // handle a "command not implemented" for the SyncHdr like 513 (indication that server does not like our header)
+ case 400: // ..and 400 as well (sync4j case, as it seems)
+ case 513: // bad protocol version
+ case 505: // bad DTD version (NextHaus/DeskNow case)
+ // try with next lower protocol
+ PDEBUGENDBLOCK("processStatus"); // done processing status
+ if (!retryOlderProtocol()) {
+ // no older SyncML protocol we can try --> abort
+ AbortSession(513,false); // server does not know any of our SyncML versions
+ }
+ break;
+ case 401: // bad authentication
+ // Bad authorisation
+ if (fAuthRetries==0)
+ // if first attempt is rejected with "bad", we conclude that the
+ // last attempt was carrying auth data and was not a attempt to get challenge
+ // from server. Therefore we count this as two tries (one get chal, one really failing)
+ fAuthRetries=2;
+ else
+ fAuthRetries++; // just count attempt to auth
+ if (fAuthRetries>MAX_AUTH_RETRIES) {
+ AbortSession(401,false); // abort session, too many retries
+ break;
+ }
+ // Treat no nonce like empty nonce to make sure that a server (like SySync old versions...)
+ // that does not send a nonce at all does not get auth with some old, invalid nonce string included.
+ if (chalmetaP && chalmetaP->nextnonce==NULL) fRemoteNonce.erase();
+ // otherwise treat like 407
+ goto authfail;
+ case 407: // authentication required
+ // new since 2.0.4.6: count this as well (normally this happens once when sending
+ // no auth to the server to force it to send us auth chal first).
+ fAuthRetries++;
+ authfail:
+ PDEBUGPRINTFX(DBG_ERROR,("Authentication failed (status=%hd) with remote server",aStatusCmdP->getStatusCode()));
+ // Auth fail after we have received a valid response for the init message indicates protocol messed up
+ if (fIncomingState!=psta_idle) {
+ AbortSession(400,true); // error in protocol handling from remote
+ break;
+ }
+ // Missing or bad authorisation, evaluate chal
+ if (!chalmetaP || fAuthRetries>MAX_AUTH_RETRIES) {
+ #ifdef SYDEBUG
+ if (!chalmetaP) {
+ PDEBUGPRINTFX(DBG_ERROR,("Bad auth but no challenge in response status -> can't work - no retry"));
+ }
+ #endif
+ AbortSession(aStatusCmdP->getStatusCode(),true); // missing auth, but no chal -> can't work this way!
+ break;
+ }
+ // let descendant eventually save auth params
+ saveRemoteParams();
+ // modify session for re-start
+ PDEBUGENDBLOCK("processStatus"); // done processing status
+ retryClientSessionStart(false); // no previously sent message in the buffer
+ break;
+ default:
+ handled=false; // could not handle status
+ } // switch
+donewithstatus:
+ // Anyway, reception of status for header enables generation of next message header
+ // (plus already generated commands such as status for response header)
+ if (!fMsgNoResp && !isAborted()) {
+ // issue header now if not already issued above
+ if (!fOutgoingStarted) {
+ // interrupt status processing block here as issueHeader will do a start-of-message PDEBUGBLOCK
+ PDEBUGENDBLOCK("processStatus");
+ issueHeader(false);
+ PDEBUGBLOCKDESC("processStatus","finishing processing incoming SyncHdr Status");
+ }
+ }
+ // return handled status
+ return handled;
+} // TSyncClient::handleHeaderStatus
+
+
+// - start sync group (called in client or server roles)
+bool TSyncClient::processSyncStart(
+ SmlSyncPtr_t aSyncP, // the Sync element
+ TStatusCommand &aStatusCommand, // pre-set 200 status, can be modified in case of errors
+ bool &aQueueForLater // will be set if command must be queued for later (re-)execution
+)
+{
+ if (fIncomingState!=psta_sync && fIncomingState!=psta_initsync) {
+ aStatusCommand.setStatusCode(403); // forbidden in this context
+ PDEBUGPRINTFX(DBG_ERROR,("Sync command not allowed outside of sync phase (-> 403)"));
+ AbortSession(400,true);
+ return false;
+ }
+ // just find appropriate database, must be already initialized for sync!
+ // determine local database to sync with (target)
+ TLocalEngineDS *datastoreP = findLocalDataStoreByURI(smlSrcTargLocURIToCharP(aSyncP->target));
+ if (!datastoreP) {
+ // no such local datastore
+ PDEBUGPRINTFX(DBG_ERROR,("Sync command for unknown DS locURI '%s' (-> 404)",smlSrcTargLocURIToCharP(aSyncP->target)));
+ aStatusCommand.setStatusCode(404); // not found
+ return false;
+ }
+ else {
+ // save the pointer, will e.g. be used to route subsequent server commands
+ fLocalSyncDatastoreP=datastoreP;
+ // let local datastore know (in server case, this is done in TSyncServer)
+ return fLocalSyncDatastoreP->engProcessSyncCmd(aSyncP,aStatusCommand,aQueueForLater);
+ }
+ return true;
+} // TSyncClient::processSyncStart
+
+
+
+#ifdef ENGINEINTERFACE_SUPPORT
+
+
+// Support for EngineModule common interface
+// =========================================
+
+
+/// @brief Get new session key to access details of this session
+appPointer TSyncClient::newSessionKey(TEngineInterface *aEngineInterfaceP)
+{
+ return new TClientParamsKey(aEngineInterfaceP,this);
+} // TSyncClient::newSessionKey
+
+
+// Client runtime settings key
+// ---------------------------
+
+// Constructor
+TClientParamsKey::TClientParamsKey(TEngineInterface *aEngineInterfaceP, TSyncClient *aClientSessionP) :
+ inherited(aEngineInterfaceP,aClientSessionP),
+ fClientSessionP(aClientSessionP)
+{
+} // TClientParamsKey::TClientParamsKey
+
+
+// - read connection URL
+static TSyError readConnectURI(
+ TStructFieldsKey *aStructFieldsKeyP, const TStructFieldInfo *aFldInfoP,
+ appPointer aBuffer, memSize aBufSize, memSize &aValSize
+)
+{
+ TClientParamsKey *mykeyP = static_cast<TClientParamsKey *>(aStructFieldsKeyP);
+ return TStructFieldsKey::returnString(
+ mykeyP->fClientSessionP->getSendURI(),
+ aBuffer,aBufSize,aValSize
+ );
+} // readConnectURI
+
+
+// - read host part of connection URL
+static TSyError readConnectHost(
+ TStructFieldsKey *aStructFieldsKeyP, const TStructFieldInfo *aFldInfoP,
+ appPointer aBuffer, memSize aBufSize, memSize &aValSize
+)
+{
+ TClientParamsKey *mykeyP = static_cast<TClientParamsKey *>(aStructFieldsKeyP);
+ string host;
+ splitURL(mykeyP->fClientSessionP->getSendURI(),NULL,&host,NULL,NULL,NULL);
+ return TStructFieldsKey::returnString(
+ host.c_str(),
+ aBuffer,aBufSize,aValSize
+ );
+} // readConnectHost
+
+
+// - read document part of connection URL
+static TSyError readConnectDoc(
+ TStructFieldsKey *aStructFieldsKeyP, const TStructFieldInfo *aFldInfoP,
+ appPointer aBuffer, memSize aBufSize, memSize &aValSize
+)
+{
+ TClientParamsKey *mykeyP = static_cast<TClientParamsKey *>(aStructFieldsKeyP);
+ string doc;
+ splitURL(mykeyP->fClientSessionP->getSendURI(),NULL,NULL,&doc,NULL,NULL);
+ return TStructFieldsKey::returnString(
+ doc.c_str(),
+ aBuffer,aBufSize,aValSize
+ );
+} // readConnectDoc
+
+
+// - read content type string
+static TSyError readContentType(
+ TStructFieldsKey *aStructFieldsKeyP, const TStructFieldInfo *aFldInfoP,
+ appPointer aBuffer, memSize aBufSize, memSize &aValSize
+)
+{
+ TClientParamsKey *mykeyP = static_cast<TClientParamsKey *>(aStructFieldsKeyP);
+ string contentType = SYNCML_MIME_TYPE;
+ mykeyP->fClientSessionP->addEncoding(contentType);
+ return TStructFieldsKey::returnString(
+ contentType.c_str(),
+ aBuffer,aBufSize,aValSize
+ );
+} // readContentType
+
+
+// - read local session ID
+static TSyError readLocalSessionID(
+ TStructFieldsKey *aStructFieldsKeyP, const TStructFieldInfo *aFldInfoP,
+ appPointer aBuffer, memSize aBufSize, memSize &aValSize
+)
+{
+ TClientParamsKey *mykeyP = static_cast<TClientParamsKey *>(aStructFieldsKeyP);
+ return TStructFieldsKey::returnString(
+ mykeyP->fClientSessionP->getLocalSessionID(),
+ aBuffer,aBufSize,aValSize
+ );
+} // readLocalSessionID
+
+
+// - write (volatile, write-only) password for running this session
+// (for cases where we don't want to rely on binfile storage for sensitive password data)
+TSyError writeSessionPassword(
+ TStructFieldsKey *aStructFieldsKeyP, const TStructFieldInfo *aFldInfoP,
+ cAppPointer aBuffer, memSize aValSize
+)
+{
+ TClientParamsKey *mykeyP = static_cast<TClientParamsKey *>(aStructFieldsKeyP);
+ mykeyP->fClientSessionP->setServerPassword((cAppCharP)aBuffer, aValSize);
+ return LOCERR_OK;
+} // writeSessionPassword
+
+
+#ifdef ENGINE_LIBRARY
+// - read display alert
+static TSyError readDisplayAlert(
+ TStructFieldsKey *aStructFieldsKeyP, const TStructFieldInfo *aFldInfoP,
+ appPointer aBuffer, memSize aBufSize, memSize &aValSize
+)
+{
+ TClientEngineInterface *clientEngineP =
+ static_cast<TClientEngineInterface *>(aStructFieldsKeyP->getEngineInterface());
+ return TStructFieldsKey::returnString(
+ clientEngineP->fAlertMessage.c_str(),
+ aBuffer,aBufSize,aValSize
+ );
+} // readDisplayAlert
+#endif
+
+
+// accessor table for client params
+static const TStructFieldInfo ClientParamFieldInfos[] =
+{
+ // valName, valType, writable, fieldOffs, valSiz
+ { "connectURI", VALTYPE_TEXT, false, 0, 0, &readConnectURI, NULL },
+ { "connectHost", VALTYPE_TEXT, false, 0, 0, &readConnectHost, NULL },
+ { "connectDoc", VALTYPE_TEXT, false, 0, 0, &readConnectDoc, NULL },
+ { "contenttype", VALTYPE_TEXT, false, 0, 0, &readContentType, NULL },
+ { "localSessionID", VALTYPE_TEXT, false, 0, 0, &readLocalSessionID, NULL },
+ { "sessionPassword", VALTYPE_TEXT, true, 0, 0, NULL, &writeSessionPassword },
+ #ifdef ENGINE_LIBRARY
+ { "displayalert", VALTYPE_TEXT, false, 0, 0, &readDisplayAlert, NULL },
+ #endif
+};
+
+// get table describing the fields in the struct
+const TStructFieldInfo *TClientParamsKey::getFieldsTable(void)
+{
+ return ClientParamFieldInfos;
+} // TClientParamsKey::getFieldsTable
+
+sInt32 TClientParamsKey::numFields(void)
+{
+ return sizeof(ClientParamFieldInfos)/sizeof(TStructFieldInfo);
+} // TClientParamsKey::numFields
+
+// get actual struct base address
+uInt8P TClientParamsKey::getStructAddr(void)
+{
+ // prepared for accessing fields in client session object
+ return (uInt8P)fClientSessionP;
+} // TClientParamsKey::getStructAddr
+
+#endif // ENGINEINTERFACE_SUPPORT
+
+} // namespace sysync
+
+// eof
diff --git a/src/sysync/syncclient.h b/src/sysync/syncclient.h
new file mode 100755
index 0000000..b23e31a
--- /dev/null
+++ b/src/sysync/syncclient.h
@@ -0,0 +1,367 @@
+/*
+ * File: SyncClient.h
+ *
+ * Author: Lukas Zeller (luz@synthesis.ch)
+ *
+ * TSyncClient
+ * Provides functionality required for a Synchronisation
+ * client.
+ *
+ * Copyright (c) 2002-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ * 2002-05-06 : luz : created from old SyncClient.h (which was a
+ * intermediate class between server and session,
+ * as initially we considered a client-server
+ * combined class. Now both SyncClient and
+ * SyncServer are directly derived from SyncSession
+ *
+ */
+
+#ifndef SyncClient_H
+#define SyncClient_H
+
+//%%% we still need this at this time
+#define NON_FULLY_GRANULAR_ENGINE 1
+
+#ifdef NON_FULLY_GRANULAR_ENGINE
+namespace sysync {
+ // queued progress events
+ typedef std::list<TEngineProgressInfo> TEngineProgressInfoList;
+}
+#endif
+
+
+
+// includes
+#include "syncsession.h"
+#include "syncclientbase.h"
+#include "localengineds.h"
+
+#include "engineinterface.h"
+
+
+using namespace sysync;
+
+
+namespace sysync {
+
+
+#ifdef NON_FULLY_GRANULAR_ENGINE
+// queued progress events
+typedef std::list<TEngineProgressInfo> TEngineProgressInfoList;
+#endif
+
+
+/// @brief client engine state
+typedef enum {
+ ces_idle, ///< client engine is idle and can be initialized with STEPCMD_CLIENTSTART
+ ces_generating, ///< ready to perform next STEPCMD_STEP to generate SyncML messages
+ ces_dataready, ///< data is ready to be sent, waiting for STEPCMD_SENTDATA
+ ces_needdata, ///< need response data, waiting for STEPCMD_GOTDATA
+ ces_processing, ///< ready to perform next STEPCMD_STEP to process SyncML messages
+ ces_done, ///< session done
+
+ numClientEngineStates
+} TClientEngineState;
+
+
+
+
+#ifdef PRECONFIGURED_SYNCREQUESTS
+
+// config for databases to sync with
+class TSyncReqConfig: public TConfigElement
+{
+ typedef TConfigElement inherited;
+public:
+ TSyncReqConfig(TLocalDSConfig *aLocalDSCfg, TConfigElement *aParentElement);
+ virtual ~TSyncReqConfig();
+ // General sync db settings
+ // - local client datastore involved
+ TLocalDSConfig *fLocalDSConfig;
+ // - local client datatstore path extension (such as "test" in "contact/test")
+ string fLocalPathExtension;
+ // - remote server DB layer auth
+ string fDBUser;
+ string fDBPassword;
+ // - remote server datastore path
+ string fServerDBPath;
+ // - sync mode
+ TSyncModes fSyncMode;
+ // - slowsync
+ bool fSlowSync;
+ // - DS 1.2 filters
+ string fRecordFilterQuery;
+ bool fFilterInclusive;
+ // public methods
+ // - create appropriate localdatastore from config and init client request params
+ TLocalEngineDS *initNewLocalDataStore(TSyncSession *aSessionP);
+protected:
+ // check config elements
+ #ifndef HARDCODED_CONFIG
+ virtual bool localStartElement(const char *aElementName, const char **aAttributes, sInt32 aLine);
+ #endif
+ virtual void clear();
+ virtual void localResolve(bool aLastPass);
+}; // TSyncReqConfig
+
+
+
+// Sync DB config list
+typedef std::list<TSyncReqConfig *> TSyncReqList;
+
+#endif
+
+// forward
+class TSyncClient;
+
+// client config
+class TClientConfig: public TSessionConfig
+{
+ typedef TSessionConfig inherited;
+public:
+ TClientConfig(const char *aElementName, TConfigElement *aParentElementP);
+ virtual ~TClientConfig();
+ // Local config
+ // - syncml version
+ TSyncMLVersions fAssumedServerVersion; // we use this version for first connect attempt
+ // - PUT devinf at every slow sync?? (smartner server needs this)
+ bool fPutDevInfAtSlowSync;
+ // - default auth params
+ TAuthTypes fAssumedServerAuth;
+ TFmtTypes fAssumedServerAuthEnc; // start with char encoding
+ string fAssumedNonce; // start with no nonce
+ // auth retry options (mainly for stupid servers like SCTS)
+ bool fNewSessionForAuthRetry; // restart session for auth retries
+ bool fNoRespURIForAuthRetry; // send retry to original URI for auth retries
+ #ifndef NO_LOCAL_DBLOGIN
+ // - user/pw to access local DB
+ string fLocalDBUser;
+ string fLocalDBPassword;
+ bool fNoLocalDBLogin; // if set, no local DB login is required, fLocalDBUser is used as userkey
+ #endif
+ // - server URI (used for PRECONFIGURED_SYNCREQUESTS, but also to predefine server URL in config for fixed-server apps)
+ string fServerURI;
+ #ifdef PRECONFIGURED_SYNCREQUESTS
+ // Preconfigured sync request settings
+ // - encoding
+ SmlEncoding_t fEncoding;
+ // - server layer user / pw
+ string fServerUser;
+ string fServerPassword;
+ string fSocksHost;
+ string fProxyHost;
+ string fProxyUser;
+ string fProxyPassword;
+ // - transport layer user / pw
+ string fTransportUser;
+ string fTransportPassword;
+ // - DB sync requests array
+ TSyncReqList fSyncRequests;
+ #endif
+ #ifndef HARDCODED_CONFIG
+ // - for debug only - used to fake a devID in the config file (useful when testing)
+ string fFakeDeviceID;
+ #endif
+protected:
+ // check config elements
+ #ifndef HARDCODED_CONFIG
+ virtual bool localStartElement(const char *aElementName, const char **aAttributes, sInt32 aLine);
+ #endif
+ virtual void clear();
+ virtual void localResolve(bool aLastPass);
+public:
+ // create appropriate session (=agent) for this client
+ virtual TSyncClient *CreateClientSession(cAppCharP aSessionID) = 0;
+}; // TClientConfig
+
+
+
+class TSyncClientBase;
+
+// default profile ID
+#define DEFAULT_PROFILE_ID 0xFFFFFFFF
+
+class TSyncClient: public TSyncSession
+{
+ typedef TSyncSession inherited;
+public:
+ TSyncClient(
+ TSyncClientBase *aSyncClientBaseP,
+ cAppCharP aSessionID // a session ID
+ );
+ virtual ~TSyncClient();
+ virtual void TerminateSession(void); // Terminate session, like destructor, but without actually destructing object itself
+ virtual void ResetSession(void); // Resets session (but unlike TerminateSession, session might be re-used)
+ void InternalResetSession(void); // static implementation for calling through virtual destructor and virtual ResetSession();
+ #ifdef ENGINEINTERFACE_SUPPORT
+ // set profileID to client session before doing first SessionStep
+ virtual void SetProfileSelector(uInt32 aProfileSelector) { fProfileSelectorInternal = aProfileSelector; /* default is just passing it on */ };
+ // Support for EngineModule common interface
+ /// @brief Executes next step of the session
+ /// @param aStepCmd[in/out] step command (STEPCMD_xxx):
+ /// - tells caller to send or receive data or end the session etc.
+ /// - instructs engine to suspend or abort the session etc.
+ /// @param aInfoP[in] pointer to a TEngineProgressInfo structure, NULL if no progress info needed
+ /// @return LOCERR_OK on success, SyncML or LOCERR_xxx error code on failure
+ TSyError SessionStep(uInt16 &aStepCmd, TEngineProgressInfo *aInfoP);
+ /// @brief Get new session key to access details of this session
+ virtual appPointer newSessionKey(TEngineInterface *aEngineInterfaceP);
+ #endif // ENGINEINTERFACE_SUPPORT
+
+
+ // session set-up
+ // - initialize the client session, select the profile and link session with SML instance
+ // for the correct encoding
+ localstatus InitializeSession(uInt32 aProfileSelector, bool aAutoSyncSession=false);
+ // - selects a profile (returns false if profile not found)
+ // Note: This call must create and initialize all datastores that
+ // are to be synced with that profile.
+ virtual localstatus SelectProfile(uInt32 aProfileSelector, bool aAutoSyncSession=false);
+ // - Prepares connection-related stuff. Will be called after all
+ // session init is done, but before first request is sent
+ virtual localstatus PrepareConnect(void) { return LOCERR_OK; };
+ // called when incoming SyncHdr fails to execute
+ virtual bool syncHdrFailure(bool aTryAgain);
+ // retry older protocol, returns false if no older protocol to try
+ bool retryOlderProtocol(bool aSameVersionRetry=false, bool aOldMessageInBuffer=false);
+ // prepares client session such that it will do a retry to start a session
+ // (but keeping already received auth/nonce/syncML-Version state)
+ void retryClientSessionStart(bool aOldMessageInBuffer);
+ // - check if session is just starting (no response yet)
+ bool isStarting(void) { return fIncomingMsgID==0; };
+ // Session and DB management
+ // - perform login to local DB to allow accessing datastores later
+ localstatus LocalLogin(void);
+ // - let session process message in the SML instance buffer
+ localstatus processAnswer(void);
+ // - let session produce (or finish producing) next message into
+ // SML instance buffer
+ // returns aDone if no answer needs to be sent (=end of session)
+ // returns <>0 SyncML error status on error (no SyncML answer could be generated)
+ localstatus NextMessage(bool &aDone);
+ // info about session
+ virtual bool IsServerSession(void) { return false; }; // is client
+ // URI to send outgoing message to
+ virtual cAppCharP getSendURI(void) { return fRespondURI.c_str(); }; // use respondURI
+ // login
+ cAppCharP getServerUser(void) { return fServerUser.c_str(); };
+ cAppCharP getServerPassword(void) { return fServerPassword.c_str(); };
+ void setServerPassword(cAppCharP aPassword, sInt32 aPwSize=-1) { if (aPwSize<0) fServerPassword = aPassword; else fServerPassword.assign(aPassword, aPwSize); };
+ // transport layer login
+ cAppCharP getTransportUser(void) { return fTransportUser.c_str(); };
+ cAppCharP getTransportPassword(void) { return fTransportPassword.c_str(); };
+ // proxy/socks hosts
+ cAppCharP getSocksHost(void) { return fSocksHost.c_str(); };
+ cAppCharP getProxyHost(void) { return fProxyHost.c_str(); };
+ cAppCharP getProxyUser(void) { return fProxyUser.c_str(); };
+ cAppCharP getProxyPassword(void) { return fProxyPassword.c_str(); };
+ // info about needed auth type
+ virtual TAuthTypes requestedAuthType(void) { return auth_none; }; // client does not require auth by default
+ virtual bool isAuthTypeAllowed(TAuthTypes /* aAuthType */) { return true; }; // client accepts any auth by default
+ // special behaviour
+ virtual bool devidWithUserHash(void) { return false; }; // do not include user name to make a hash-based pseudo-device ID by default
+ // session handling
+ // - get client base
+ TSyncClientBase *getClientBase(void);
+ // Session level command handling
+ // - handle status received for SyncHdr, returns false if not handled
+ virtual bool handleHeaderStatus(TStatusCommand *aStatusCmdP);
+ // Sync processing (command group)
+ // - start sync group
+ virtual bool processSyncStart(
+ SmlSyncPtr_t aSyncP, // the Sync element
+ TStatusCommand &aStatusCommand, // pre-set 200 status, can be modified in case of errors
+ bool &aQueueForLater // will be set if command must be queued for later (re-)execution
+ );
+protected:
+ // variables
+ // - socks and proxy hosts if any
+ string fSocksHost;
+ string fProxyHost;
+ string fProxyUser;
+ string fProxyPassword;
+ // - remote SyncML server layer login
+ string fServerUser; // Server layer authentification user name
+ string fServerPassword; // Server layer authentification password
+ // - remote transport layer login
+ string fTransportUser;
+ string fTransportPassword;
+ // - local DB login
+ #ifndef NO_LOCAL_DBLOGIN
+ bool fNoLocalDBLogin;
+ string fLocalDBUser;
+ string fLocalDBPassword;
+ #endif
+ // Authorisation
+ // - get credentials/username to authenticate with remote party, NULL if none
+ virtual SmlCredPtr_t newCredentialsForRemote(void); // get credentials to login to remote server
+ virtual cAppCharP getUsernameForRemote(void) { return fServerUser.c_str(); }; // specified user name
+ // internal processing events
+ // - message start and end
+ virtual bool MessageStarted(SmlSyncHdrPtr_t aContentP, TStatusCommand &aStatusCommand, bool aBad=false);
+ virtual void MessageEnded(bool aIncomingFinal);
+ // device info (uses defaults in this base class, override to customize)
+ virtual string getDeviceType(void);
+ virtual string getDeviceID(void);
+ // if set, SML processing errors will not be reported
+ // (in case session wants to re-try something)
+ bool fIgnoreMsgErrs;
+ // incrementing client session number 0..255
+ uInt8 fClientSessionNo;
+ #ifdef HARD_CODED_SERVER_URI
+ uInt32 fServerURICRC;
+ uInt8 fNoCRCPrefixLen;
+ #endif
+ #ifdef ENGINEINTERFACE_SUPPORT
+ // Engine interface
+ // - Step that generates SyncML data
+ TSyError generatingStep(uInt16 &aStepCmd, TEngineProgressInfo *aInfoP);
+ // - Step that processes SyncML data
+ TSyError processingStep(uInt16 &aStepCmd, TEngineProgressInfo *aInfoP);
+ // - internal profile selector (can be ID or index) determined with
+ // SetProfileSelector(), to be used with SelectProfile()
+ uInt32 fProfileSelectorInternal;
+ // - Client engine state
+ TClientEngineState fEngineState;
+ #endif // ENGINEINTERFACE_SUPPORT
+public:
+ // - can be cleared to suppress automatic use of DS 1.2 SINCE/BEFORE filters
+ // (e.g. for date range in func_SetDaysRange())
+ bool fServerHasSINCEBEFORE;
+}; // TSyncClient
+
+
+#ifdef ENGINEINTERFACE_SUPPORT
+
+// Support for EngineModule common interface
+// =========================================
+
+// client runtime parameters
+class TClientParamsKey :
+ public TSessionKey
+{
+ typedef TSessionKey inherited;
+
+public:
+ TClientParamsKey(TEngineInterface *aEngineInterfaceP, TSyncClient *aClientSessionP);
+ virtual ~TClientParamsKey() {};
+
+protected:
+ // get table describing the fields in the struct
+ virtual const TStructFieldInfo *getFieldsTable(void);
+ virtual sInt32 numFields(void);
+ // get actual struct base address
+ virtual uInt8P getStructAddr(void);
+public:
+ // the associated client session
+ TSyncClient *fClientSessionP;
+}; // TClientParamsKey
+
+#endif // ENGINEINTERFACE_SUPPORT
+
+} // namespace sysync
+
+#endif // SyncClient_H
+
+// eof
diff --git a/src/sysync/syncclientbase.cpp b/src/sysync/syncclientbase.cpp
new file mode 100755
index 0000000..7e4ebc6
--- /dev/null
+++ b/src/sysync/syncclientbase.cpp
@@ -0,0 +1,490 @@
+/*
+ * TSyncClientBase
+ * Abstract baseclass for client
+ *
+ * Copyright (c) 2002-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ */
+
+#include "prefix_file.h"
+
+#include "sysync.h"
+#include "syncclientbase.h"
+#include "syncclient.h"
+
+namespace sysync {
+
+#ifdef ENGINEINTERFACE_SUPPORT
+
+// Support for EngineModule common interface
+// =========================================
+
+#ifndef ENGINE_LIBRARY
+
+#warning "using ENGINEINTERFACE_SUPPORT in old-style appbase-rooted environment. Should be converted to real engine usage later"
+
+// Engine factory function for non-Library case
+ENGINE_IF_CLASS *newEngine(void)
+{
+ // For real engine based targets, newEngine must create a target-specific derivate
+ // of the engine, which then has a suitable newSyncAppBase() method to create the
+ // appBase. For old-style environment, a generic TServerEngineInterface is ok, as this
+ // in turn calls the global newSyncAppBase() which then returns the appropriate
+ // target specific appBase.
+ return new TClientEngineInterface;
+} // newEngine
+
+/// @brief returns a new application base.
+TSyncAppBase *TClientEngineInterface::newSyncAppBase(void)
+{
+ // For not really engine based targets, the appbase factory function is
+ // a global routine (for real engine targets, it is a true virtual of
+ // the engineInterface, implemented in the target's leaf engineInterface derivate.
+ // - for now, use the global appBase creator routine
+ return sysync::newSyncAppBase(); // use global factory function
+} // TClientEngineInterface::newSyncAppBase
+
+#else
+
+// EngineInterface methods
+// -----------------------
+
+// progress callback which queues up progress events for later delivery via SessionStep()
+static bool progressCallback(
+ const TProgressEvent &aEvent,
+ void *aContext
+)
+{
+ TClientEngineInterface *clientEngineP = static_cast<TClientEngineInterface *>(aContext);
+
+ // handle some events specially
+ if (aEvent.eventtype == pev_nop)
+ return true; // just continue
+ else if (aEvent.eventtype == pev_suspendcheck)
+ return !(clientEngineP->fSuspendRequested);
+ else {
+ // create engine progress record
+ TEngineProgressInfo info;
+ info.eventtype = (uInt16)(aEvent.eventtype);
+ // - datastore ID, if any
+ if (aEvent.datastoreID != NULL)
+ info.targetID = (sInt32)(aEvent.datastoreID->fLocalDBTypeID);
+ else
+ info.targetID = 0;
+ // - handle display message event specially
+ if (aEvent.eventtype==pev_display100) {
+ info.extra1 = 0;
+ // extra1 is a pointer to the message text, save it for retrieval via SessionKey
+ clientEngineP->fAlertMessage = (cAppCharP)(aEvent.extra);
+ }
+ else {
+ info.extra1 = aEvent.extra;
+ }
+ info.extra2 = aEvent.extra2;
+ info.extra3 = aEvent.extra3;
+ // append it at the end of the list
+ clientEngineP->fProgressInfoList.push_back(info);
+ }
+ return !(clientEngineP->fAbortRequested);
+} // progressCallback
+
+
+
+
+/// @brief Open a session
+/// @param aNewSessionH[out] receives session handle for all session execution calls
+/// @param aSelector[in] selector, depending on session type. For multi-profile clients: profile ID to use
+/// @param aSessionName[in] a text name/id to identify a session, useage depending on session type.
+/// @return LOCERR_OK on success, SyncML or LOCERR_xxx error code on failure
+TSyError TClientEngineInterface::OpenSessionInternal(SessionH &aNewSessionH, uInt32 aSelector, cAppCharP aSessionName)
+{
+ TSyncClientBase *clientBaseP = static_cast<TSyncClientBase *>(getSyncAppBase());
+
+ // No client session may exist when opening a new one
+ if (clientBaseP->fClientSessionP)
+ return LOCERR_WRONGUSAGE;
+ // check type of session
+ if (aSelector == SESSIONSEL_DBAPI_TUNNEL) {
+ // initiate a DBAPI tunnel session.
+ #ifdef DBAPI_TUNNEL_SUPPORT
+ // Create a new session, sessionName selects datastore
+ fSessionStatus = clientBaseP->CreateTunnelSession(aSessionName);
+ if (fSessionStatus==LOCERR_OK) {
+ // return the session pointer as handle
+ aNewSessionH=clientBaseP->fClientSessionP;
+ }
+ #else
+ return LOCERR_NOTIMP; // tunnel not implemented
+ #endif
+ }
+ else if ((aSelector & ~SESSIONSEL_PROFILEID_MASK) == SESSIONSEL_CLIENT_AS_CHECK) {
+ // special autosync-checking "session"
+ #ifdef AUTOSYNC_SUPPORT
+ // %%% tbi
+ return LOCERR_NOTIMP; // %%% not implemented for now
+ #else
+ return LOCERR_NOTIMP; // no Autosync in this engine
+ #endif
+ }
+ else {
+ #ifdef NON_FULLY_GRANULAR_ENGINE
+ // Install callback to catch progress events and deliver them via SessionStep
+ // - erase the list of queued progress events
+ fProgressInfoList.clear();
+ fPendingStepCmd=0; // none pending
+ // init the flags which are set by STEPCMD_SUSPEND, STEPCMD_ABORT and STEPCMD_TRANSPFAIL
+ fAbortRequested=false;
+ fSuspendRequested=false;
+ // - install the callback for catching all progress events (including those during session creation)
+ clientBaseP->fProgressEventFunc=progressCallback;
+ clientBaseP->fProgressEventContext=this; // pass link to myself
+ #endif // NON_FULLY_GRANULAR_ENGINE
+ // Create a new session
+ fSessionStatus = clientBaseP->CreateSession();
+ // Pass profile ID
+ if (fSessionStatus==LOCERR_OK) {
+ clientBaseP->fClientSessionP->SetProfileSelector(aSelector & ~SESSIONSEL_PROFILEID_MASK);
+ // return the session pointer as handle
+ aNewSessionH=(SessionH)clientBaseP->fClientSessionP;
+ }
+ }
+ // done
+ return fSessionStatus;
+} // TClientEngineInterface::OpenSessionInternal
+
+
+/// @brief open session specific runtime parameter/settings key
+/// @note key handle obtained with this call must be closed BEFORE SESSION IS CLOSED!
+/// @param aNewKeyH[out] receives the opened key's handle on success
+/// @param aSessionH[in] session handle obtained with OpenSession
+/// @param aMode[in] the open mode
+/// @return LOCERR_OK on success, SyncML or LOCERR_xxx error code on failure
+TSyError TClientEngineInterface::OpenSessionKey(SessionH aSessionH, KeyH &aNewKeyH, uInt16 aMode)
+{
+ // %%% add autosync-check session case here
+ // must be current session's handle (for now - however
+ // future engines might allow multiple concurrent client sessions)
+ if (aSessionH != (SessionH)static_cast<TSyncClientBase *>(getSyncAppBase())->fClientSessionP)
+ return LOCERR_WRONGUSAGE; // something wrong with that handle
+ // get client session pointer
+ TSyncClient *clientSessionP = static_cast<TSyncClient *>((void *)aSessionH);
+ // create settings key for the session
+ aNewKeyH = (KeyH)clientSessionP->newSessionKey(this);
+ // done
+ return LOCERR_OK;
+} // TClientEngineInterface::OpenSessionKey
+
+
+/// @brief Close a session
+/// @note It depends on session type if this also destroys the session or if it may persist and can be re-opened.
+/// @param aSessionH[in] session handle obtained with OpenSession
+/// @return LOCERR_OK on success, SyncML or LOCERR_xxx error code on failure
+TSyError TClientEngineInterface::CloseSession(SessionH aSessionH)
+{
+ TSyncClientBase *clientBaseP = static_cast<TSyncClientBase *>(getSyncAppBase());
+
+ // %%% add autosync-check session case here
+ // Nothing to do if no client session exists or none is requested for closing
+ if (!clientBaseP->fClientSessionP || !aSessionH)
+ return LOCERR_OK; // nop
+ // must be current session's handle
+ if (aSessionH != (SessionH)clientBaseP->fClientSessionP)
+ return LOCERR_WRONGUSAGE; // something wrong with that handle
+ // terminate running session (if any)
+ clientBaseP->KillClientSession(LOCERR_USERABORT); // closing while session in progress counts as user abort
+ // - remove the callback
+ clientBaseP->fProgressEventFunc=NULL;
+ clientBaseP->fProgressEventContext=NULL;
+ // done
+ return LOCERR_OK;
+} // TClientEngineInterface::CloseSession
+
+
+/// @brief Executes next step of the session
+/// @param aSessionH[in] session handle obtained with OpenSession
+/// @param aStepCmd[in/out] step command (STEPCMD_xxx):
+/// - tells caller to send or receive data or end the session etc.
+/// - instructs engine to suspend or abort the session etc.
+/// @param aInfoP[in] pointer to a TEngineProgressInfo structure, NULL if no progress info needed
+/// @return LOCERR_OK on success, SyncML or LOCERR_xxx error code on failure
+TSyError TClientEngineInterface::SessionStep(SessionH aSessionH, uInt16 &aStepCmd, TEngineProgressInfo *aInfoP)
+{
+ TSyncClientBase *clientBaseP = static_cast<TSyncClientBase *>(getSyncAppBase());
+
+ // %%% add autosync-check session case here
+ // must be current session's handle (for now - however
+ // future engines might allow multiple concurrent client sessions)
+ if (aSessionH != (SessionH)clientBaseP->fClientSessionP)
+ return LOCERR_WRONGUSAGE; // something wrong with that handle
+ // get client session pointer
+ TSyncClient *clientSessionP = static_cast<TSyncClient *>((void *)aSessionH);
+ #ifdef NON_FULLY_GRANULAR_ENGINE
+ // pre-process setp command and generate pseudo-steps to empty progress event queue
+ // preprocess general step codes
+ switch (aStepCmd) {
+ case STEPCMD_TRANSPFAIL :
+ // directly abort
+ clientSessionP->AbortSession(LOCERR_TRANSPFAIL,true);
+ goto abort;
+ case STEPCMD_ABORT :
+ // directly abort
+ clientSessionP->AbortSession(LOCERR_USERABORT,true);
+ abort:
+ // also set the flag so subsequent progress events will result the abort status
+ fAbortRequested=true;
+ aStepCmd = STEPCMD_STEP; // convert to normal step
+ goto step;
+ case STEPCMD_SUSPEND :
+ // directly suspend
+ clientSessionP->SuspendSession(LOCERR_USERSUSPEND);
+ // also set the flag so subsequent pev_suspendcheck events will result the suspend status
+ fSuspendRequested=true;
+ aStepCmd = STEPCMD_STEP; // convert to normal step
+ goto step;
+ case STEPCMD_STEP :
+ step:
+ // first just return all queued up progress events
+ if (fProgressInfoList.size()>0) {
+ // get first element in list
+ TEngineProgressInfoList::iterator pos = fProgressInfoList.begin();
+ // pass it back to caller if caller is interested
+ if (aInfoP) {
+ *aInfoP = *pos; // copy progress event
+ }
+ // delete progress event from list
+ fProgressInfoList.erase(pos);
+ // that's it for now, engine state does not change, wait for next step
+ aStepCmd = STEPCMD_PROGRESS;
+ return LOCERR_OK;
+ }
+ else if (fPendingStepCmd != 0) {
+ // now return previously generated step command
+ // Note: engine is already in the new state matching fPendingStepCmd
+ aStepCmd = fPendingStepCmd;
+ fSessionStatus = fPendingStatus;
+ fPendingStepCmd=0; // none pending any more
+ fPendingStatus=0;
+ return fSessionStatus; // return pending status now
+ }
+ // all progress events are delivered, now we can do the real work
+ }
+ #endif
+ // let client session handle it (if a session exists at all)
+ fSessionStatus = clientSessionP->SessionStep(aStepCmd, aInfoP);
+ #ifdef NON_FULLY_GRANULAR_ENGINE
+ // make sure caller issues STEPCMD_STEP to get all pending progress events
+ if (fProgressInfoList.size()>0) {
+ // save pending step command for returning later
+ fPendingStepCmd = aStepCmd;
+ fPendingStatus = fSessionStatus;
+ // return request for more steps instead
+ aStepCmd = STEPCMD_OK;
+ fSessionStatus = LOCERR_OK;
+ }
+ #endif
+ // return step status
+ return fSessionStatus;
+} // TClientEngineInterface::SessionStep
+
+
+/// @brief returns the SML instance for a given session handle
+/// (internal helper to allow TEngineInterface to provide the access to the SyncML buffer)
+InstanceID_t TClientEngineInterface::getSmlInstanceOfSession(SessionH aSessionH)
+{
+ TSyncClientBase *clientBaseP = static_cast<TSyncClientBase *>(getSyncAppBase());
+
+ // %%% add autosync-check session case here
+ // must be current session's handle (for now - however
+ // future engines might allow multiple concurrent client sessions)
+ if (aSessionH != (SessionH)clientBaseP->fClientSessionP)
+ return 0; // something wrong with session handle -> no SML instance
+ // get client session pointer
+ TSyncClient *clientSessionP = static_cast<TSyncClient *>((void *)aSessionH);
+ // return SML instance associated with that session
+ return clientSessionP->getSmlWorkspaceID();
+} // TClientEngineInterface::getSmlInstanceOfSession
+
+#endif // ENGINE_LIBRARY
+
+#endif // ENGINEINTERFACE_SUPPORT
+
+
+
+// TSyncClientBase
+// ===============
+
+#ifdef DIRECT_APPBASE_GLOBALACCESS
+// Only for old-style targets that still use a global anchor
+
+TSyncClientBase *getClientBase(void)
+{
+ return static_cast<TSyncClientBase *>(getSyncAppBase());
+} // getClientBase
+
+#endif // DIRECT_APPBASE_GLOBALACCESS
+
+
+// constructor
+TSyncClientBase::TSyncClientBase() :
+ TSyncAppBase(),
+ fClientSessionP(NULL)
+{
+ // nop so far
+} // TSyncClientBase::TSyncClientBase
+
+
+// destructor
+TSyncClientBase::~TSyncClientBase()
+{
+ // kill session, if any
+ KillClientSession();
+ fDeleting=true; // flag deletion to block calling critical (virtual) methods
+ // delete client session, if any
+ SYSYNC_TRY {
+ // %%%%%%%% tdb...
+ }
+ SYSYNC_CATCH (...)
+ SYSYNC_ENDCATCH
+} // TSyncClientBase::~TSyncClientBase
+
+
+// Called from SyncML toolkit when a new SyncML message arrives
+// - dispatches to session's StartMessage
+Ret_t TSyncClientBase::StartMessage(
+ InstanceID_t aSmlWorkspaceID, // SyncML toolkit workspace instance ID
+ VoidPtr_t aUserData, // user data, contains NULL or char* to transport-layer supported session ID
+ SmlSyncHdrPtr_t aContentP // SyncML tookit's decoded form of the <SyncHdr> element
+) {
+ TSyncSession *sessionP = static_cast<TSyncClient *>(aUserData); // the client session
+ SYSYNC_TRY {
+ // let session handle details of StartMessage callback
+ return sessionP->StartMessage(aContentP);
+ }
+ SYSYNC_CATCH (exception &e)
+ return HandleDecodingException(sessionP,"StartMessage",&e);
+ SYSYNC_ENDCATCH
+ SYSYNC_CATCH (...)
+ return HandleDecodingException(sessionP,"StartMessage",NULL);
+ SYSYNC_ENDCATCH
+} // TSyncClientBase::StartMessage
+
+
+
+#ifdef DBAPI_TUNNEL_SUPPORT
+
+// - create a new client DBApi tunnel session
+localstatus TSyncClientBase::CreateTunnelSession(cAppCharP aDatastoreName)
+{
+ localstatus sta;
+
+ // create an ordinary session
+ sta = CreateSession();
+ if (sta==LOCERR_OK) {
+ // determine which datastore to address
+ sta = fClientSessionP->InitializeTunnelSession(aDatastoreName);
+ }
+ // done
+ return sta;
+} // TSyncClientBase::CreateTunnelSession
+
+#endif // DBAPI_TUNNEL_SUPPORT
+
+
+
+// create a new client session
+localstatus TSyncClientBase::CreateSession(void)
+{
+ // remove any eventually existing old session first
+ KillClientSession();
+ // get config
+ //TClientConfig *configP = static_cast<TClientConfig *>(getSyncAppBase()->getRootConfig()->fAgentConfigP);
+ // create a new client session of appropriate type
+ // - use current time as session ID (only for logging purposes)
+ string s;
+ LONGLONGTOSTR(s,(long long)(getSystemNowAs(TCTX_UTC)));
+ fClientSessionP = static_cast<TClientConfig *>(fConfigP->fAgentConfigP)->CreateClientSession(s.c_str());
+ if (!fClientSessionP) return LOCERR_UNDEFINED;
+ // check expiry here
+ return appEnableStatus();
+} // TSyncClientBase::CreateSession
+
+
+// initialize the (already created) client session and link it with the SML toolkit
+localstatus TSyncClientBase::InitializeSession(uInt32 aProfileID, bool aAutoSyncSession)
+{
+ // session must be created before with CreateSession()
+ if (!fClientSessionP)
+ return LOCERR_WRONGUSAGE;
+ // initialitze session
+ return fClientSessionP->InitializeSession(aProfileID, aAutoSyncSession);
+}
+
+
+// create a message into the instance buffer
+localstatus TSyncClientBase::generateRequest(bool &aDone)
+{
+ return getClientSession()->NextMessage(aDone);
+} // TSyncClientBase::generateRequest
+
+
+
+// clear all unprocessed or unsent data from SML workspace
+void TSyncClientBase::clrUnreadSmlBufferdata(void)
+{
+ InstanceID_t myInstance = getClientSession()->getSmlWorkspaceID();
+ MemPtr_t p;
+ MemSize_t s;
+
+ smlLockReadBuffer(myInstance,&p,&s);
+ smlUnlockReadBuffer(myInstance,s);
+} // TSyncClientBase::clrUnreadSmlBufferdata
+
+
+
+// process message in the instance buffer
+localstatus TSyncClientBase::processAnswer(void)
+{
+ return fClientSessionP->processAnswer();
+}
+
+
+// - extract hostname from an URI according to transport
+void TSyncClientBase::extractHostname(const char *aURI, string &aHostName)
+{
+ splitURL(aURI,NULL,&aHostName,NULL,NULL,NULL);
+} // TSyncClientBase::extractHostname
+
+
+// - extract document name from an URI according to transport
+void TSyncClientBase::extractDocumentInfo(const char *aURI, string &aDocName)
+{
+ splitURL(aURI,NULL,NULL,&aDocName,NULL,NULL);
+} // TSyncClientBase::extractDocumentInfo
+
+
+// - extract protocol name from an URI according to transport
+void TSyncClientBase::extractProtocolname(const char *aURI, string &aProtocolName)
+{
+ splitURL(aURI,&aProtocolName,NULL,NULL,NULL,NULL);
+} // TSyncClientBase::extractProtocolname
+
+
+// delete and unlink current session from SML toolkit
+void TSyncClientBase::KillClientSession(localstatus aStatusCode)
+{
+ if (fClientSessionP) {
+ // Abort session
+ if (aStatusCode) fClientSessionP->AbortSession(aStatusCode,true);
+ // remove instance of that session
+ freeSmlInstance(fClientSessionP->getSmlWorkspaceID());
+ fClientSessionP->setSmlWorkspaceID(0); // make sure it isn't set any more
+ // delete session itself
+ TSyncClient *clientP = fClientSessionP;
+ fClientSessionP=NULL;
+ delete clientP;
+ }
+} // TSyncClientBase::KillSession
+
+} // namespace sysync
+
+// eof
diff --git a/src/sysync/syncclientbase.h b/src/sysync/syncclientbase.h
new file mode 100644
index 0000000..c815aaf
--- /dev/null
+++ b/src/sysync/syncclientbase.h
@@ -0,0 +1,190 @@
+/*
+ * TSyncClientBase
+ * Abstract baseclass for client
+ *
+ * Copyright (c) 2002-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ */
+
+#ifndef SYNCCLIENTBASE_H
+#define SYNCCLIENTBASE_H
+
+// general includes (SyncML tookit, windows, Clib)
+#include "sysync.h"
+#include "syncappbase.h"
+#include "syncclient.h"
+
+
+
+namespace sysync {
+
+// forward declarations
+class TSyncSession;
+class TSyncClient;
+class TSyncClientBase;
+
+#ifdef ENGINEINTERFACE_SUPPORT
+
+// Support for EngineModule common interface
+// =========================================
+
+
+// Engine module class
+class TClientEngineInterface:
+ public TEngineInterface
+{
+ typedef TEngineInterface inherited;
+public:
+ // constructor
+ TClientEngineInterface()
+ #ifdef ENGINE_LIBRARY
+ : fSessionStatus(LOCERR_WRONGUSAGE)
+ #ifdef NON_FULLY_GRANULAR_ENGINE
+ ,fPendingStepCmd(0)
+ #endif
+ #endif
+ {};
+
+ #ifndef ENGINE_LIBRARY
+
+ // appbase factory (based on old appbase-root method) is here
+ virtual TSyncAppBase *newSyncAppBase(void);
+
+ #else
+
+ // Running a Client Sync Session
+ // -----------------------------
+
+ /// @brief Open a session
+ /// @param aNewSessionH[out] receives session handle for all session execution calls
+ /// @param aSelector[in] selector, depending on session type. For multi-profile clients: profile ID to use
+ /// @param aSessionName[in] a text name/id to identify a session, useage depending on session type.
+ /// @return LOCERR_OK on success, SyncML or LOCERR_xxx error code on failure
+ virtual TSyError OpenSessionInternal(SessionH &aNewSessionH, uInt32 aSelector, cAppCharP aSessionName);
+
+ /// @brief open session specific runtime parameter/settings key
+ /// @note key handle obtained with this call must be closed BEFORE SESSION IS CLOSED!
+ /// @param aNewKeyH[out] receives the opened key's handle on success
+ /// @param aSessionH[in] session handle obtained with OpenSession
+ /// @param aMode[in] the open mode
+ /// @return LOCERR_OK on success, SyncML or LOCERR_xxx error code on failure
+ virtual TSyError OpenSessionKey(SessionH aSessionH, KeyH &aNewKeyH, uInt16 aMode);
+
+ /// @brief Close a session
+ /// @note It depends on session type if this also destroys the session or if it may persist and can be re-opened.
+ /// @param aSessionH[in] session handle obtained with OpenSession
+ /// @return LOCERR_OK on success, SyncML or LOCERR_xxx error code on failure
+ virtual TSyError CloseSession(SessionH aSessionH);
+
+ /// @brief Executes sync session or other sync related activity step by step
+ /// @param aSessionH[in] session handle obtained with OpenSession
+ /// @param aStepCmd[in/out] step command (STEPCMD_xxx):
+ /// - tells caller to send or receive data or end the session etc.
+ /// - instructs engine to suspend or abort the session etc.
+ /// @param aInfoP[in] pointer to a TEngineProgressInfo structure, NULL if no progress info needed
+ /// @return LOCERR_OK on success, SyncML or LOCERR_xxx error code on failure
+ virtual TSyError SessionStep(SessionH aSessionH, uInt16 &aStepCmd, TEngineProgressInfo *aInfoP = NULL);
+
+protected:
+
+ /// @brief returns the SML instance for a given session handle
+ virtual InstanceID_t getSmlInstanceOfSession(SessionH aSessionH);
+
+private:
+ // status of the session
+ localstatus fSessionStatus;
+
+ #ifdef NON_FULLY_GRANULAR_ENGINE
+public:
+ // progress event queue until engine is fully granular
+ TEngineProgressInfoList fProgressInfoList;
+ // pending step command and status during progress retrieval steps
+ uInt16 fPendingStepCmd;
+ localstatus fPendingStatus;
+ string fAlertMessage;
+ // suspend and abort requests
+ bool fSuspendRequested;
+ bool fAbortRequested;
+ #endif // NON_FULLY_GRANULAR_ENGINE
+
+ #endif // ENGINE_LIBRARY
+
+}; // TClientEngineInterface
+
+#endif // ENGINEINTERFACE_SUPPORT
+
+
+
+#ifdef DIRECT_APPBASE_GLOBALACCESS
+// access to client base
+TSyncClientBase *getClientBase(void);
+#endif
+
+
+/* TSyncClientBase is a singular object that can set-up and
+ * start Sync client sessions.
+ * It also receives calls from SyncML toolkit callbacks and
+ * can route them to the active TSyncSession
+ */
+class TSyncClientBase : public TSyncAppBase
+{
+ typedef TSyncAppBase inherited;
+#ifdef ENGINEINTERFACE_SUPPORT
+friend class TClientEngineInterface;
+#endif
+
+public:
+ // constructors/destructors
+ TSyncClientBase();
+ virtual ~TSyncClientBase();
+ // Session flow entry points
+ // - create a new client session
+ localstatus CreateSession(void);
+ #ifdef DBAPI_TUNNEL_SUPPORT
+ // - create a new client DBApi tunnel session
+ localstatus CreateTunnelSession(cAppCharP aDatastoreName);
+ #endif
+ // - initialize client session and link it with the SML toolkit
+ // (session must be created with CreateSession() before)
+ localstatus InitializeSession(uInt32 aProfileID, bool aAutoSyncSession=false);
+ // - create a message into the instance buffer, returns aDone==true if none needed any more
+ localstatus generateRequest(bool &aDone);
+ // - clear unread data in the instance buffer (received and to-be-sent)
+ void clrUnreadSmlBufferdata(void);
+ // - process message in the instance buffer
+ localstatus processAnswer(void);
+ // - get current session
+ TSyncClient *getClientSession(void) { return fClientSessionP; };
+ // - remove and kill current session
+ void KillClientSession(localstatus aStatusCode=0);
+ // handlers for SyncML toolkit callbacks
+ // - Start/End Message: identifies Session, and creates new or assigns existing session
+ Ret_t StartMessage(
+ InstanceID_t aSmlWorkspaceID, // SyncML toolkit workspace instance ID
+ VoidPtr_t aUserData, // user data, should be NULL (as StartMessage is responsible for setting userdata)
+ SmlSyncHdrPtr_t aContentP // SyncML tookit's decoded form of the <SyncHdr> element
+ );
+ // Session handling
+ // - extract hostname from an URI according to transport
+ virtual void extractHostname(const char *aURI, string &aHostName);
+ // - extract document info (name/auth) from an URI according to transport
+ void extractDocumentInfo(const char *aURI, string &aDocName);
+ // - extract protocol name from an URI according to transport
+ virtual void extractProtocolname(const char *aURI, string &aProtocolName);
+protected:
+ // Handle exception happening while decoding commands for session
+ // (Former KillSession)
+ virtual Ret_t HandleDecodingException(TSyncSession * /* aSessionP */, const char * /* aRoutine */, exception * /* aExceptionP */=NULL) { return SML_ERR_UNSPECIFIC; /* %%%% nop so far */ };
+private:
+ // the current session
+ TSyncClient *fClientSessionP;
+}; // TSyncClientBase
+
+
+} // namespace sysync
+
+
+#endif // SYNCCLIENTBASE_H
+
+
+// eof
diff --git a/src/sysync/synccommand.cpp b/src/sysync/synccommand.cpp
new file mode 100755
index 0000000..81bc1e1
--- /dev/null
+++ b/src/sysync/synccommand.cpp
@@ -0,0 +1,4307 @@
+/*
+ * File: SyncCommand.cpp
+ *
+ * Author: Lukas Zeller (luz@synthesis.ch)
+ *
+ * TSmlCommand, TXXXCmd....
+ * Wrapper classes for SyncML Commands and the associated SyncML
+ * Toolkit mechanics.
+ *
+ * Copyright (c) 2001-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ * 2001-05-30 : luz : created
+ *
+ */
+
+// includes
+#include "prefix_file.h"
+
+#include "synccommand.h"
+#include "syncsession.h"
+
+// %%%% debug hack switch
+//#define DEBUG_XMLRESPONSE_FOR_WBXML 1
+//#undef DEBUG_XMLRESPONSE_FOR_WBXML
+
+// Status for Results is not needed according to SyncML 1.0.1
+// specs but 9210 seems to need it.
+// Note: future versions of the standard will require
+// status for all commands, so this is going to be the default
+#define RESULTS_SENDS_STATUS 1
+
+// Map statuses are sent immediately, even if they will be part
+// of sync-updates-to-client package (instead of map-acknowledge)
+// This seems to be the normal case
+#define MAP_STATUS_IMMEDIATE 1
+
+
+#ifndef SYNCCOMMAND_PART1_EXCLUDE
+
+using namespace sysync;
+
+
+/* command name list, used for cmdRef */
+
+const char * const SyncCommandNames[numSmlCommandTypes] = {
+ "SyncHdr",
+ "Sync",
+ "Sync", // note this is actually SyncEnd, but as we send this as cmdRef, it MUST be named "Sync" as well
+ "Add",
+ "Alert",
+ "Delete",
+ "Get",
+ "Put",
+ "Map",
+ "Results",
+ "Status",
+ "Replace",
+ "Copy",
+ "Move",
+ "Sequence",
+ "Atomic",
+ "[unknown]"
+};
+
+
+/*
+ * Implementation of TSmlCommand
+ */
+
+/* public TSmlCommand members */
+
+
+// base constructor (to be called by all derived constructors)
+TSmlCommand::TSmlCommand(
+ TSmlCommandTypes aCmdType, // the command type
+ bool aOutgoing, // set if this is a outgoing command (to avoid confusion)
+ TSyncSession *aSessionP, // associated session (for callbacks)
+ uInt32 aMsgID // (optional, for receiving only) the Message ID of the command
+)
+{
+ // save info
+ fCmdType=aCmdType;
+ fMsgID=aMsgID;
+ fSessionP=aSessionP;
+ fOutgoing=aOutgoing;
+ // init
+ fWaitingForStatus=0; // not yet waiting for any statuses
+ fCmdID=0; // no ID yet, will be either set at issue() or read at StartProcessing()
+ fNoResp=false; // respond by default
+ fDontSend=false; // send by default
+ fEvalMode=false; // no eval
+ fPrepared=false;
+ fAllowFailure=false;
+ // debug
+ DEBUGPRINTFX(DBG_PROTO,("Created command '%s' (%s)", getName(), fOutgoing ? "outgoing" : "incoming"));
+} // TSmlCommand::TSmlCommand
+
+
+// get name of command
+const char *TSmlCommand::getName(void)
+{
+ return SyncCommandNames[fCmdType];
+} // TSmlCommand::getName
+
+
+#ifdef SYDEBUG
+TDebugLogger *TSmlCommand::getDbgLogger(void)
+{
+ // commands log to session's logger
+ return fSessionP ? fSessionP->getDbgLogger() : NULL;
+} // TSmlCommand::getDbgLogger
+
+uInt32 TSmlCommand::getDbgMask(void)
+{
+ if (!fSessionP) return 0; // no session, no debug
+ return fSessionP->getDbgMask();
+} // TSmlCommand::getDbgMask
+#endif
+
+// get name of certain command
+const char *TSmlCommand::getNameOf(TSmlCommandTypes aCmdType)
+{
+ return SyncCommandNames[aCmdType];
+} // TSmlCommand::getNameOf
+
+
+// start processing a command
+void TSmlCommand::StartProcessing(
+ SmlPcdataPtr_t aCmdID, // ID of command
+ Flag_t aFlags // flags of command
+)
+{
+ // get command ID
+ StrToULong(smlPCDataToCharP(aCmdID),fCmdID);
+ // get noResp state
+ fNoResp=(aFlags & SmlNoResp_f)!=0;
+ PDEBUGPRINTFX(DBG_HOT,("Started processing Command '%s' (incoming MsgID=%ld, CmdID=%ld)%s",getName(),(long)fMsgID,(long)fCmdID,fNoResp ? ", noResp" : ""));
+} // TSmlCommand::StartProcessing
+
+
+
+// - Prepare for issuing, evaluate size of command
+sInt32 TSmlCommand::evalIssue(
+ uInt32 aAsCmdID, // command ID to be used
+ uInt32 aInMsgID, // message ID in which command is being issued
+ bool aNoResp // issue without wanting response
+)
+{
+ MemSize_t res;
+ bool ok;
+
+ fPrepared=false; // force re-prepare
+
+ // start evaluation run
+ smlStartEvaluation(fSessionP->getSmlWorkspaceID());
+
+ fEvalMode=true;
+ SYSYNC_TRY {
+ // - call issue to get size
+ ok=issue(aAsCmdID,aInMsgID,aNoResp);
+ // - back to normal mode
+ fEvalMode=false;
+ smlEndEvaluation(fSessionP->getSmlWorkspaceID(),&res);
+ if (!ok) res=-1; // no room, error
+ /* %%% hack to force unsendable-sized syncop commands:
+ #ifdef RELEASE_VERSION
+ #error "%%%%remove this debuh hack!!!"
+ #endif
+ if (dynamic_cast<TSyncOpCommand *>(this)!=NULL) {
+ // always oversized
+ res=0;
+ }
+ */
+ }
+ SYSYNC_CATCH (...)
+ fEvalMode=false;
+ smlEndEvaluation(fSessionP->getSmlWorkspaceID(),&res);
+ SYSYNC_RETHROW;
+ SYSYNC_ENDCATCH
+
+ // return available space after sending this command
+ return res;
+} // TSmlCommand::evalIssue
+
+
+
+void TSmlCommand::PrepareIssue(
+ SmlPcdataPtr_t *aCmdID, // ID of command
+ Flag_t *aFlags // flags of command
+)
+{
+ if (!fPrepared) {
+ fPrepared=true;
+ // set Command ID
+ if (aCmdID) {
+ // remove old
+ if (*aCmdID) smlFreePcdata(*aCmdID);
+ // create new
+ *aCmdID=newPCDataLong(fCmdID);
+ }
+ // add NoResp flag if requested
+ if (aFlags)
+ *aFlags |= fNoResp ? SmlNoResp_f : 0;
+ }
+ if (!fEvalMode) {
+ #ifdef SYDEBUG
+ if (aCmdID) {
+ if (*aCmdID==NULL)
+ SYSYNC_THROW(TSyncException("No Command ID set at evalIssue() but requested one at issue()"));
+ }
+ #endif
+ // save workspace size immediately before sending to calc message size
+ fBytesbefore=fSessionP->getSmlWorkspaceFreeBytes();
+ #ifdef DEBUG_XMLRESPONSE_FOR_WBXML
+ // %%% debug hack %%% force output to be XML
+ smlSetEncoding(fSessionP->getSmlWorkspaceID(),SML_XML);
+ #endif
+ }
+} // TSmlCommand::PrepareIssue
+
+
+#ifndef USE_SML_EVALUATION
+
+// get (approximated) message size required for sending it
+uInt32 TSmlCommand::messageSize(void)
+{
+ // default, should be enough for most commands
+ return DEFAULTCOMMANDSIZE;
+}
+
+#endif
+
+// finalizes issuing a command (updates message size)
+void TSmlCommand::FinalizeIssue(void)
+{
+ fSessionP->incOutgoingMessageSize(fBytesbefore-fSessionP->getSmlWorkspaceFreeBytes()); // update msg size
+ #ifdef DEBUG_XMLRESPONSE_FOR_WBXML
+ // %%% debug hack %%% force input to be WBXML again
+ smlSetEncoding(fSessionP->getSmlWorkspaceID(),SML_WBXML);
+ #endif
+} // TSmlCommand::FinalizeIssue
+
+
+// finalizes issuing a command (updates message size)
+bool TSmlCommand::queueForResponse(void)
+{
+ return (!fNoResp && !fSessionP->fOutgoingNoResp);
+} // TSmlCommand::queueForStatus
+
+
+// returns true if command must be put to the waiting-for-status queue.
+// If false, command can be deleted
+bool TSmlCommand::issue(
+ uInt32 aAsCmdID, // command ID to be used
+ uInt32 aInMsgID, // message ID in which command is being issued
+ bool aNoResp
+)
+{
+ // set command ID and Message ID (for further compare with incoming statuses)
+ fCmdID=aAsCmdID;
+ fMsgID=aInMsgID;
+ fNoResp=aNoResp;
+ return fEvalMode; // evaluation ok, but base class commands cannot be issued
+} // TSmlCommand::issue
+
+
+// execute command (perform real actions, generate status)
+// returns true if command has executed and can be deleted
+bool TSmlCommand::execute(void)
+{
+ // non-derived execute (such as map received by client): protocol error
+ TStatusCommand *statusCmdP=newStatusCommand(400);
+ ISSUE_COMMAND_ROOT(fSessionP,statusCmdP);
+ // done
+ return false;
+} // TSmlCommand::execute
+
+
+// - test if command matches status
+bool TSmlCommand::matchStatus(TStatusCommand *aStatusCmdP)
+{
+ // match if cmdID and msgID are the same
+ return (
+ (fMsgID==aStatusCmdP->fRefMsgID) &&
+ (fCmdID==aStatusCmdP->fRefCmdID)
+ );
+} // TSmlCommand::matchStatus
+
+
+// handle status received for previously issued command
+// returns true if done, false if command must be kept in the status queue
+bool TSmlCommand::handleStatus(TStatusCommand *aStatusCmdP)
+{
+ // base class just handles common cases
+ TSyError statuscode = aStatusCmdP->getStatusCode();
+ if (statuscode<200) {
+ // informational
+ switch (statuscode) {
+ case 101:
+ // in progress, wait for final status
+ POBJDEBUGPRINTFX(fSessionP,DBG_HOT,("Status: 101: In progress, keep waiting for final status"));
+ return false; // keep in queue
+ //break;
+ default:
+ // unknown
+ POBJDEBUGPRINTFX(fSessionP,DBG_ERROR,("Status: %hd: unknown informational status -> accepted",statuscode));
+ return true;
+ }
+ }
+ else if (statuscode<300) {
+ if (statuscode==202) {
+ // accepted for processing
+ POBJDEBUGPRINTFX(fSessionP,DBG_PROTO,("Status: 202: accepted for processing, keep waiting for final status"));
+ return false; // keep in queue
+ }
+ // successful
+ POBJDEBUGPRINTFX(fSessionP,DBG_PROTO,("Status: %hd: successful --> accept as ok",statuscode));
+ return true; // done with command
+ }
+ else if (statuscode<400) {
+ // redirection
+ // %%% - we cannot handle them, abort for now
+ POBJDEBUGPRINTFX(fSessionP,DBG_ERROR,("Status: %hd: redirected --> we cannot handle this, abort session",statuscode));
+ fSessionP->AbortSession(412,false,statuscode); // other party's fault: incomplete command
+ return true; // done with command
+ }
+ else if (statuscode<500) {
+ // originator exception (we sent some bad stuff)
+ POBJDEBUGPRINTFX(fSessionP,DBG_ERROR,("Status: %hd: originator exception",statuscode));
+ if (!fAllowFailure) fSessionP->AbortSession(500,false,statuscode); // our fault
+ return true; // done with command
+ }
+ else {
+ // must be recipient exception
+ POBJDEBUGPRINTFX(fSessionP,DBG_ERROR,("Status: %hd: recipient exception",statuscode));
+ if (!fAllowFailure) fSessionP->AbortSession(statuscode,false,statuscode); // show other party's reason for error
+ return true; // done with command
+ }
+ // just to make sure
+ return true; // done with command
+} // TSmlCommand::handleStatus
+
+
+
+// generate status depending on fNoResp and session's fMsgNoResp
+TStatusCommand *TSmlCommand::newStatusCommand(TSyError aStatusCode, const char *aStringItem)
+{
+ TStatusCommand *statusCmdP = new TStatusCommand(fSessionP,this,aStatusCode);
+ if (aStringItem)
+ statusCmdP->addItemString(aStringItem);
+ return statusCmdP;
+} // TSmlCommand::newStatusCommand
+
+
+// generate and send status depending on fNoResp and session's fMsgNoResp
+void TSmlCommand::issueStatusCommand(TSyError aStatusCode)
+{
+ // issuePtr can handle NULL in case no status was generated...
+ fSessionP->issueRootPtr(newStatusCommand(aStatusCode));
+} // TSmlCommand::issueStatusCommand
+
+
+
+TSmlCommand::~TSmlCommand()
+{
+ PDEBUGPRINTFX(DBG_PROTO,("Deleted command '%s' (%s MsgID=%ld, CmdID=%ld)",getName(),fOutgoing ? "outgoing" : "incoming", (long)fMsgID,(long)fCmdID));
+} // TSmlCommand::~TSmlCommand
+
+/* end of TSmlCommand implementation */
+
+
+
+/*
+ * Implementation of TSyncHeader
+ */
+
+/* public TSyncHeader members */
+
+
+// constructor for receiving Sync header
+TSyncHeader::TSyncHeader(
+ TSyncSession *aSessionP, // associated session (for callbacks)
+ SmlSyncHdrPtr_t aSyncHdrElementP // associated SyncHdr content element
+) :
+ TSmlCommand(scmd_synchdr,false,aSessionP)
+{
+ // save element
+ fSyncHdrElementP = aSyncHdrElementP;
+} // TSyncHeader::TSyncHeader
+
+
+// constructor for sending SyncHdr
+TSyncHeader::TSyncHeader(
+ TSyncSession *aSessionP, // associated session (for callbacks)
+ bool aOutgoingNoResp // if true, entire message will request no responses
+) :
+ TSmlCommand(scmd_synchdr,true,aSessionP)
+{
+ // prevent two simultaneous messages
+ if (fSessionP->fOutgoingStarted)
+ SYSYNC_THROW(TSyncException("Tried to start new message before finishing previous"));
+ // let session create the structure (using session vars for info)
+ fSyncHdrElementP=fSessionP->NewOutgoingSyncHdr(aOutgoingNoResp);
+ // now outgoing message IS started
+ fSessionP->fOutgoingStarted=true;
+} // TSyncHeader::TSyncHeader
+
+
+// returns true if command must be put to the waiting-for-status queue.
+// If false, command can be deleted
+bool TSyncHeader::issue(
+ uInt32 aAsCmdID, // command ID to be used
+ uInt32 aInMsgID, // message ID in which command is being issued
+ bool aNoResp
+)
+{
+ // prepare basic stuff
+ TSmlCommand::issue(0,aInMsgID,false);
+ // now issue
+ if (fSyncHdrElementP) {
+ // issue command with SyncML toolkit, no CmdID or flags to set here
+ PrepareIssue(NULL,NULL);
+ if (!fEvalMode) {
+ Ret_t err;
+ #ifdef SYDEBUG
+ if (fSessionP->fXMLtranslate && fSessionP->fOutgoingXMLInstance) {
+ err=smlStartMessageExt(fSessionP->fOutgoingXMLInstance,fSyncHdrElementP,SmlVersionCodes[fSessionP->fSyncMLVersion]);
+ if (err!=SML_ERR_OK) {
+ // problem with XML translation
+ PDEBUGPRINTFX(DBG_ERROR,("XML translation disabled due to sml error=%04hX",err));
+ fSessionP->fXMLtranslate=false;
+ }
+ }
+ #endif
+ if ((err=smlStartMessageExt(fSessionP->getSmlWorkspaceID(),fSyncHdrElementP,SmlVersionCodes[fSessionP->fSyncMLVersion]))!=SML_ERR_OK) {
+ SYSYNC_THROW(TSmlException("smlStartMessage",err));
+ }
+ FinalizeIssue();
+ // we don't need the status structure any more, free (and NULL ptr) now
+ FreeSmlElement();
+ }
+ else
+ // just evaluate size
+ return smlStartMessageExt(fSessionP->getSmlWorkspaceID(),fSyncHdrElementP,SmlVersionCodes[fSessionP->fSyncMLVersion])==SML_ERR_OK;
+ }
+ else {
+ DEBUGPRINTFX(DBG_ERROR,("*** Tried to issue NULL synchdr"));
+ }
+ // header will normally receive status
+ return queueForResponse();
+} // TSyncHeader::issue
+
+
+// handle status received for previously issued command
+// returns true if done, false if command must be kept in the status queue
+bool TSyncHeader::handleStatus(TStatusCommand *aStatusCmdP)
+{
+ // status for SyncHdr
+ if (!fSessionP->handleHeaderStatus(aStatusCmdP)) {
+ // session could not handle item
+ return TSmlCommand::handleStatus(aStatusCmdP);
+ }
+ // status handled
+ return true; // done with command
+} // TSyncHeader::handleStatus
+
+
+// execute command (perform real actions, generate status)
+// returns true if command has executed and can be deleted
+// NOTE: synchdr is a special case: if it returns FALSE,
+// session must be reset and synchdr must be re-excecuted
+bool TSyncHeader::execute(void)
+{
+ const char *verDTD;
+ const char *verProto;
+ TStatusCommand *statusCmdP=NULL;
+ sInt16 statuscode=400;
+ bool hdrok=true;
+
+ if (!fSyncHdrElementP) SYSYNC_THROW(TSyncException("empty header"));
+ if (!fSessionP) SYSYNC_THROW(TSyncException("missing fSessionP"));
+ // first get message ID to be able to generate statuses
+ fMsgID=0; // none by default
+ if (StrToULong(smlPCDataToCharP(fSyncHdrElementP->msgID),fMsgID)) {
+ // got msgID, check if ok
+ if (
+ fMsgID==uInt32(fSessionP->fIncomingMsgID-1) &&
+ fSessionP->fAllowMessageRetries // check if we want to allow this (7250 for example seems to retry messages)
+ ) {
+ // this seems to be a transport-level retry of the previous message
+ PDEBUGPRINTFX(DBG_ERROR,("*********** WARNING: Remote resent MsgID %ld",(long)fMsgID));
+ // set back incoming ID
+ fSessionP->fIncomingMsgID = fMsgID;
+ // -> we should resend the previous answer again.
+ if (
+ fSessionP->getSyncAppBase()->canBufferRetryAnswer()
+ ) {
+ // we can resend answers
+ PDEBUGPRINTFX(DBG_ERROR,("last answer buffered -> sending it again"));
+ fSessionP->fMessageRetried=true;
+ return false;
+ }
+ else {
+ // use not foolproof poor man's method
+ // %%% This will fail if the previous message received has
+ // changed datastore/session/package states, but will work if
+ // the retry is within a phase (probable case for long sessions)
+ PDEBUGPRINTFX(DBG_ERROR,("No buffered answer to resend -> just process msg again (WARNING: possibly messes up session state)"));
+ }
+ }
+ if (fMsgID<uInt32(fSessionP->fIncomingMsgID)) {
+ // bad Message ID (lower than previous): forget previous session, start new one
+ PDEBUGPRINTFX(DBG_ERROR,("Bad incoming MsgID %ld, expected >=%ld -> Aborting previous Session, starting new",(long)fMsgID,(long)fSessionP->fIncomingMsgID));
+ return false; // session must be restarted, this command re-executed
+ }
+ }
+ else {
+ // header without session ID is bad
+ statuscode=500;
+ statusCmdP=newStatusCommand(statuscode);
+ // log file entry
+ PDEBUGPRINTFX(DBG_ERROR,("Missing incoming MsgID -> Aborting Session"));
+ hdrok=false; // cannot start
+ }
+ // Assign session-var anyway
+ fSessionP->fIncomingMsgID=fMsgID;
+ // now check header for conformance
+ SYSYNC_TRY {
+ // get info out of SyncHdr
+ if (hdrok) {
+ // get noResp flag (which is valid for the entire message)
+ fNoResp=(fSyncHdrElementP->flags & SmlNoResp_f)!=0;
+ fSessionP->fMsgNoResp=fNoResp; // copy to session flag
+ // test SyncML version compatibility
+ verProto=smlPCDataToCharP(fSyncHdrElementP->proto);
+ verDTD=smlPCDataToCharP(fSyncHdrElementP->version);
+ sInt16 ver;
+ // find version
+ for (ver=1; ver<numSyncMLVersions; ver++) {
+ if (strcmp(verProto,SyncMLVerProtoNames[ver])==0) {
+ // known version found
+ // - set it only for server, client keeps preset version (will change it when
+ // a 513 is detected)
+ if (fSessionP->IsServerSession()) fSessionP->fSyncMLVersion=(TSyncMLVersions)ver;
+ break;
+ }
+ }
+ // - Protocol Version
+ TSyncMLVersions maxver = fSessionP->getSessionConfig()->fMaxSyncMLVersionSupported;
+ TSyncMLVersions minver = fSessionP->getSessionConfig()->fMinSyncMLVersionSupported;
+ if (
+ ver<minver ||
+ ver>=numSyncMLVersions ||
+ ver>maxver ||
+ (fSessionP->IsServerSession() && fSessionP->fSyncMLVersion!=syncml_vers_unknown && fSessionP->fSyncMLVersion!=ver)
+ ) {
+ // unsupported protocol version (or different than in first message): Status 513
+ // - Make sure we have a valid SyncML version
+ if (
+ fSessionP->fSyncMLVersion==syncml_vers_unknown ||
+ fSessionP->fSyncMLVersion>maxver
+ ) {
+ // use highest version we know for the answering message
+ fSessionP->fSyncMLVersion = maxver;
+ }
+ else if (fSessionP->fSyncMLVersion<minver) {
+ // use lowest enabled version for answering message
+ fSessionP->fSyncMLVersion = minver;
+ }
+ // - Set status
+ statuscode=513;
+ statusCmdP=newStatusCommand(statuscode);
+ // - add version(s) we support in data item
+ string vs;
+ for (sInt16 v=minver; v<=maxver; v++) {
+ if (!vs.empty()) vs+=", ";
+ vs+=SyncMLVerProtoNames[v];
+ }
+ statusCmdP->addItemString(vs.c_str());
+ // - log file entry
+ PDEBUGPRINTFX(DBG_ERROR,("Unsupported or changing verProto %s -> Aborting Session",verProto));
+ hdrok=false; // bad header
+ }
+ // protocol version known, check if DTD matches
+ else if (strcmp(verDTD,SyncMLVerDTDNames[ver])!=0) {
+ // wrong DTD version for this protocol version: Status 505
+ statuscode=505;
+ statusCmdP=newStatusCommand(statuscode);
+ statusCmdP->addItemString(SyncMLVerDTDNames[ver]);
+ // log file entry
+ PDEBUGPRINTFX(DBG_ERROR,(
+ "Wrong verDTD %s, expected %s -> Aborting Session",
+ verDTD,
+ SyncMLVerDTDNames[ver]
+ ));
+ hdrok=false; // bad header
+ }
+ // if ok so far, continue header processing
+ if (hdrok) {
+ PDEBUGPRINTFX(DBG_HOT,("Started Processing of message #%ld (%s)",(long)fSessionP->fIncomingMsgID,SyncMLVerProtoNames[ver]));
+ // take a look at SyncHdr Meta
+ SmlMetInfMetInfPtr_t metaP=smlPCDataToMetInfP(fSyncHdrElementP->meta);
+ if (metaP) {
+ // max (outgoing) message size
+ if (metaP->maxmsgsize) {
+ smlPCDataToLong(metaP->maxmsgsize,fSessionP->fMaxOutgoingMsgSize);
+ PDEBUGPRINTFX(DBG_REMOTEINFO,("MaxMsgSize for outgoing msgs set to %ld",(long)fSessionP->fMaxOutgoingMsgSize));
+ }
+ // max (outgoing) object size (SyncML 1.1 only)
+ if (metaP->maxobjsize) {
+ smlPCDataToLong(metaP->maxobjsize,fSessionP->fMaxOutgoingObjSize);
+ PDEBUGPRINTFX(DBG_REMOTEINFO,("MaxObjSize found in SyncHdr: %ld",(long)fSessionP->fMaxOutgoingObjSize));
+ }
+ }
+ // call server/client specific message start in derived classes
+ statusCmdP=newStatusCommand(200); // prepare OK default status (will eventually be modified by MessageStarted())
+ hdrok=fSessionP->MessageStarted(fSyncHdrElementP,*statusCmdP);
+ }
+ else {
+ //#warning "comment the next line to have server respond like pre-1.0.8.29"
+ // call client/server specific message start routine, but with error flag
+ // Note: we already have a status command (containing an error)
+ fSessionP->MessageStarted(fSyncHdrElementP,*statusCmdP,true);
+ }
+ }
+ // complete and send status, if any
+ if (statusCmdP) {
+ statusCmdP->addTargetRef(fSessionP->fLocalURI.c_str());
+ statusCmdP->addSourceRef(fSessionP->fRemoteURI.c_str());
+ // issue as SyncHdr status (if successful status)
+ TStatusCommand *cmdP = statusCmdP; statusCmdP=NULL;
+ statuscode=cmdP->getStatusCode(); // get actual status code
+ fSessionP->issueRootPtr(cmdP,false,statuscode==200);
+ }
+ // set session vars according to header success or failure
+ // - ignore all further incoming commands when header is not ok
+ // NOTE: bad cred will NOT (any longer, SyncFest #5) be flagged as bad header
+ // but instead MessageStarted may have aborted command processing
+ if (!hdrok) fSessionP->AbortSession(400,true); // bad request
+ // free this one now, is not needed any more
+ FreeSmlElement();
+ }
+ SYSYNC_CATCH (...)
+ // make sure owned objects in local scope are deleted
+ if (statusCmdP) delete statusCmdP;
+ // re-throw
+ SYSYNC_RETHROW;
+ SYSYNC_ENDCATCH
+ // done with command, delete it now: return true
+ return true;
+} // TSyncHeader::execute
+
+
+
+void TSyncHeader::FreeSmlElement(void)
+{
+ // remove SyncML toolkit element(s)
+ FREEPROTOELEMENT(fSyncHdrElementP);
+} // TSyncHeader::FreeSmlElement
+
+
+TSyncHeader::~TSyncHeader()
+{
+ // free command elements, if any (use explicit invocation as this is a destructor)
+ TSyncHeader::FreeSmlElement();
+} // TSyncHeader::~TSyncHeader
+
+
+/* end of TSyncHeader implementation */
+
+
+
+/*
+ * Implementation of TSyncCommand
+ */
+
+
+// constructor for sending Sync Command
+TSyncCommand::TSyncCommand(
+ TSyncSession *aSessionP, // associated session (for callbacks)
+ TLocalEngineDS *aLocalDataStoreP, // local datastore
+ TRemoteDataStore *aRemoteDataStoreP // remote datastore
+) :
+ TSmlCommand(scmd_sync,true,aSessionP),
+ fInterruptedCommandP(NULL)
+{
+ // save params
+ fLocalDataStoreP=aLocalDataStoreP;
+ fRemoteDataStoreP=aRemoteDataStoreP;
+ // create internal sync element
+ fSyncElementP = SML_NEW(SmlSync_t);
+ // set proto element type to make it auto-disposable
+ fSyncElementP->elementType=SML_PE_SYNC_START;
+ // Cmd ID is now empty (will be set when issued)
+ fSyncElementP->cmdID=NULL;
+ // default to no flags (noResp is set at issue, if at all)
+ fSyncElementP->flags=0;
+ // set source and target
+ fSyncElementP->target=newLocation(fRemoteDataStoreP->getFullName()); // remote is target for Sync command
+ fSyncElementP->source=newLocation(fLocalDataStoreP->getRemoteViewOfLocalURI()); // local is source of Sync command
+ // no optional elements for now
+ fSyncElementP->cred=NULL; // %%% no database level auth yet at all
+ fSyncElementP->meta=NULL; // %%% no search grammar for now
+ // add number of changes for SyncML 1.1 if remote supports it
+ fSyncElementP->noc=NULL; // default to none
+ if (aSessionP->fRemoteWantsNOC) {
+ sInt32 noc = fLocalDataStoreP->getNumberOfChanges();
+ if (noc>=0) {
+ // we have a valid NOC value, add it
+ fSyncElementP->noc=newPCDataLong(noc);
+ }
+ }
+ // not yet in progress (as not yet issued)
+ fInProgress=false;
+} // TSyncCommand::TSyncCommand
+
+
+// constructor for receiving Sync Command
+TSyncCommand::TSyncCommand(
+ TSyncSession *aSessionP, // associated session (for callbacks)
+ uInt32 aMsgID, // the Message ID of the command
+ SmlSyncPtr_t aSyncElementP // associated Sync content element
+) :
+ TSmlCommand(scmd_sync,false,aSessionP,aMsgID),
+ fInterruptedCommandP(NULL)
+{
+ // save sync element
+ fSyncElementP = aSyncElementP;
+ fInProgress=false; // just in case...
+ // no params
+ fLocalDataStoreP=NULL;
+ fRemoteDataStoreP=NULL;
+} // TSyncCommand::TSyncCommand
+
+
+// handle status received for previously issued command
+// returns true if done, false if command must be kept in the status queue
+bool TSyncCommand::handleStatus(TStatusCommand *aStatusCmdP)
+{
+ // catch those codes that do not abort entire session
+ TSyError statuscode = aStatusCmdP->getStatusCode();
+ bool handled=false;
+ switch (statuscode) {
+ case 404: // datastore not found
+ case 403: // forbidden
+ case 406: // bad mode
+ case 415: // type(s) not supported
+ case 422: // bad CGI
+ case 510: // datastore error
+ case 512: // sync failed
+ case 514: // cancelled
+ if (fLocalDataStoreP) {
+ // there is a local datastore to abort
+ fLocalDataStoreP->engAbortDataStoreSync(statuscode,false); // remote problem
+ }
+ if (fInProgress) {
+ // make sure sync command is finished now
+ fInProgress=false;
+ // Note that setting fInProgress to false
+ // is only completely safe in issue().
+ // Here it is allowed as we KNOW that syncCommand
+ // is root level, so session's handleStatus()
+ // will properly clear the fInterruptedCommandP
+ // (this would not work if this command was nested)
+ }
+ handled = true;
+ break;
+ default:
+ handled=TSmlCommand::handleStatus(aStatusCmdP);
+ break;
+ }
+ return handled;
+} // TSyncCommand::handleStatus
+
+
+// mark any syncitems (or other data) for resume. Called for pending commands
+// when a Suspend alert is received or whenever a resumable state must be saved
+void TSyncCommand::markPendingForResume(TLocalEngineDS *aForDatastoreP, bool aUnsent)
+{
+ // only act if this is for our local datastore and unsent
+ // (sent ones will be in the status wait queue, and will be found there)
+ if (aUnsent && fLocalDataStoreP==aForDatastoreP) {
+ fSessionP->markPendingForResume(
+ fNextMessageCommands,
+ fInterruptedCommandP,
+ fLocalDataStoreP
+ );
+ }
+} // TSyncCommand::markPendingForResume
+
+
+// analyze command (but do not yet execute)
+bool TSyncCommand::analyze(TPackageStates aPackageState)
+{
+ TSmlCommand::analyze(aPackageState);
+ // get Command ID and flags
+ if (fSyncElementP) {
+ StartProcessing(fSyncElementP->cmdID,fSyncElementP->flags);
+ return true;
+ }
+ else return false; // no proto element, bad command
+} // TSyncCommand::analyze
+
+
+// execute command (perform real actions, generate status)
+// returns true if command has executed and can be deleted
+bool TSyncCommand::execute(void)
+{
+ TStatusCommand *statusCmdP=NULL;
+ bool queueforlater;
+
+ if (fSyncElementP)
+ SYSYNC_TRY {
+ // determine database to be synced (target LocURI)
+ // - generate default OK status
+ statusCmdP = newStatusCommand(200);
+ // - add source and target refs from item
+ statusCmdP->addSourceRef(smlSrcTargLocURIToCharP(fSyncElementP->source));
+ statusCmdP->addTargetRef(smlSrcTargLocURIToCharP(fSyncElementP->target));
+ PDEBUGPRINTFX(DBG_HOT,(
+ "Processing Sync, Source='%s', Target='%s'",
+ smlSrcTargLocURIToCharP(fSyncElementP->source),
+ smlSrcTargLocURIToCharP(fSyncElementP->target)
+ ));
+ // - check for MaxObjSize here
+ SmlMetInfMetInfPtr_t metaP=smlPCDataToMetInfP(fSyncElementP->meta);
+ if (metaP && metaP->maxobjsize) {
+ smlPCDataToLong(metaP->maxobjsize,fSessionP->fMaxOutgoingObjSize);
+ PDEBUGPRINTFX(DBG_REMOTEINFO,("MaxObjSize found in Sync command: %ld",(long)fSessionP->fMaxOutgoingObjSize));
+ }
+ // - let session do the processing
+ queueforlater=false;
+ fSessionP->processSyncStart(
+ fSyncElementP,
+ *statusCmdP,
+ queueforlater // will be set if command must be queued for later re-execution
+ );
+ if (queueforlater) {
+ // we don't need the status now
+ delete statusCmdP;
+ }
+ else {
+ // Sync command execution completed, send (or save) status now
+ #ifdef SYNCSTATUS_AT_SYNC_CLOSE
+ // %%% don't send, just save for being sent at </Sync>
+ fSessionP->fSyncCloseStatusCommandP=statusCmdP;
+ #else
+ // - issue status for item
+ ISSUE_COMMAND_ROOT(fSessionP,statusCmdP);
+ #endif
+ /* %%% no need to abort session, failing sync command stops sync with this
+ datastore anyway
+ // make sure session gets aborted when Sync is not successful
+ if (!ok) {
+ fSessionP->AbortSession(500,true);
+ }
+ */
+ // free element
+ FreeSmlElement();
+ }
+ }
+ SYSYNC_CATCH (...)
+ // make sure owned objects in local scope are deleted
+ if (statusCmdP) delete statusCmdP;
+ // re-throw
+ SYSYNC_RETHROW;
+ SYSYNC_ENDCATCH
+ // return true if command has fully executed
+ return !queueforlater;
+} // TSyncCommand::execute
+
+
+// returns true if command must be put to the waiting-for-status queue.
+// If false, command can be deleted
+bool TSyncCommand::issue(
+ uInt32 aAsCmdID, // command ID to be used
+ uInt32 aInMsgID, // message ID in which command is being issued
+ bool aNoResp
+)
+{
+ // prepare basic stuff
+ TSmlCommand::issue(aAsCmdID,aInMsgID,aNoResp);
+ // now issue
+ if (fSyncElementP) {
+ // generate (first) opening (or evaluate)
+ if (fEvalMode) return generateOpen();
+ generateOpen();
+ // generate commands, update fInProgress
+ generateCommandsAndClose();
+ // Make sure this is not counted as command (will be counted in issuePtr(), so
+ // decrement here). Note that this must be done AFTER calling generateCommandsAndClose(),
+ // because generating needs the correct count (and this <sync> is not yet included by now!)
+ fSessionP->fOutgoingCmds--; // we're now one below the real count, but that will be compensated when returning to issuePtr
+ }
+ else {
+ DEBUGPRINTFX(DBG_ERROR,("*** Tried to issue NULL sync"));
+ }
+ // return true if command must be queued for status/result response reception
+ return queueForResponse();
+} // TSyncCommand::issue
+
+
+// - test if completely issued (must be called after issue() and continueIssue())
+bool TSyncCommand::finished(void)
+{
+ // not finished as long there are more syncOps to send;
+ // incoming are always finished after executing
+ return (!fInProgress || !fOutgoing);
+} // TSyncCommand::finished
+
+
+// generate opening <Sync> bracket
+bool TSyncCommand::generateOpen(void)
+{
+ // issue command Start <Sync> with SyncML toolkit
+ fPrepared=false; // force re-preparing in all cases (as this might be continuing a command)
+ PrepareIssue(&fSyncElementP->cmdID,&fSyncElementP->flags);
+ if (!fEvalMode) {
+ #ifdef SYDEBUG
+ if (fSessionP->fXMLtranslate && fSessionP->fOutgoingXMLInstance)
+ smlStartSync(fSessionP->fOutgoingXMLInstance,fSyncElementP);
+ #endif
+ Ret_t err;
+ if ((err=smlStartSync(fSessionP->getSmlWorkspaceID(),fSyncElementP))!=SML_ERR_OK) {
+ SYSYNC_THROW(TSmlException("smlStartSync",err));
+ }
+ FinalizeIssue();
+ // show debug
+ PDEBUGBLOCKFMT(("sync","Opened Sync command bracket",
+ "Reopen=%s|SourceURI=%s|TargetURI=%s|IncomingMsgID=%ld|CmdID=%ld",
+ fInProgress ? "yes" : "no",
+ smlSrcTargLocURIToCharP(fSyncElementP->source),
+ smlSrcTargLocURIToCharP(fSyncElementP->target),
+ (long)fMsgID,
+ (long)fCmdID
+ ));
+ PDEBUGPRINTFX(DBG_HOT,(
+ "%sOpened <Sync> bracket, Source='%s', Target='%s' as (outgoing MsgID=%ld, CmdID=%ld)",
+ fInProgress ? "Re-" : "",
+ smlSrcTargLocURIToCharP(fSyncElementP->source),
+ smlSrcTargLocURIToCharP(fSyncElementP->target),
+ (long)fMsgID,
+ (long)fCmdID
+ ));
+ // calculate (approx) max free space for data
+ fSessionP->fMaxRoomForData =
+ fSessionP->getSmlWorkspaceFreeBytes()-fSessionP->getNotUsableBufferBytes()- // what is free
+ DEFAULTCOMMANDSIZE; // minus a standard command size
+ // now we are in progress
+ fInProgress=true;
+ return true;
+ }
+ else
+ return smlStartSync(fSessionP->getSmlWorkspaceID(),fSyncElementP)==SML_ERR_OK;
+} // TSyncCommand::generateOpen
+
+
+// generate commands for inside of the <Sync> bracket
+void TSyncCommand::generateCommandsAndClose(void)
+{
+ // generate new commands only if message is not full already
+ if (!fSessionP->outgoingMessageFull()) {
+ // check for unassigned fLocalDataStoreP as this seems to happen sometimes in the cmdline client
+ PPOINTERTEST(fLocalDataStoreP,("Warning: fLocalDataStoreP==NULL, cannot generate commands -> empty <sync> command"));
+ if (fLocalDataStoreP) {
+ fInProgress = !(
+ fLocalDataStoreP->engGenerateSyncCommands
+ (
+ fNextMessageCommands,
+ fInterruptedCommandP
+ )
+ );
+ }
+ }
+ // issue command End </Sync> with SyncML toolkit
+ // - save workspace size immediately before sending to calc message size
+ fBytesbefore=fSessionP->getSmlWorkspaceFreeBytes();
+ #ifdef SYDEBUG
+ if (fSessionP->fXMLtranslate && fSessionP->fOutgoingXMLInstance)
+ smlEndSync(fSessionP->fOutgoingXMLInstance);
+ #endif
+ Ret_t err;
+ if ((err=smlEndSync(fSessionP->getSmlWorkspaceID()))!=SML_ERR_OK) {
+ SYSYNC_THROW(TSmlException("smlEndSync",err));
+ }
+ FinalizeIssue();
+ PDEBUGPRINTFX(DBG_HOT,(
+ "Closed </Sync> bracket, %sfinal",
+ fInProgress ? "NOT " : ""
+ ));
+ PDEBUGENDBLOCK("sync");
+} // TSyncCommand::generateCommandsAndClose
+
+
+// - continue issuing command. This is called by session at start of new message
+// when finished() returned false after issue()/continueIssue()
+// (which causes caller of finished() to put command into fInteruptedCommands queue)
+// returns true if command should be queued for status (again?)
+// NOTE: continueIssue must make sure that it gets a new CmdID/MsgID because
+// the command itself is issued multiple times
+bool TSyncCommand::continueIssue(bool &aNewIssue)
+{
+ aNewIssue=false; // never issue anew!
+ if (!fInProgress) return false; // done, don't queue for status again
+ // command is in progress, re-open a <sync> bracket in this message
+ // - get new CmdID/MsgID
+ fCmdID = fSessionP->getNextOutgoingCmdID();
+ fMsgID = fSessionP->getOutgoingMsgID();
+ // - now issue open
+ generateOpen();
+ // first try to execute queued sub-commands that could not be sent in last message
+ fSessionP->ContinuePackage(
+ fNextMessageCommands,
+ fInterruptedCommandP
+ );
+ // then generate more commands if needed (updates fInProgress)
+ generateCommandsAndClose();
+ // new sync command must be queued for status again
+ return true;
+} // TSyncCommand::continueIssue
+
+
+void TSyncCommand::FreeSmlElement(void)
+{
+ // remove SyncML toolkit element(s)
+ FREEPROTOELEMENT(fSyncElementP);
+} // TResultsCommand::FreeSmlElement
+
+
+TSyncCommand::~TSyncCommand()
+{
+ // free command elements, if any (use explicit invocation as this is a destructor)
+ TSyncCommand::FreeSmlElement();
+ // forget any queued sub commands
+ TSmlCommandPContainer::iterator pos;
+ for (pos=fNextMessageCommands.begin(); pos!=fNextMessageCommands.end(); ++pos) {
+ // show that command was not sent
+ DEBUGPRINTFX(DBG_ERROR,("Never sent prepared Sub-Command '%s', (outgoing MsgID=%ld, CmdID=%ld)",
+ (*pos)->getName(),
+ (long)(*pos)->getMsgID(),
+ (long)(*pos)->getCmdID()
+ ));
+ // delete
+ delete *pos;
+ }
+ fNextMessageCommands.clear(); // clear list
+ // - interrupted command
+ // NOTE: interrupted subcommands may NOT exist in any of the main command
+ // queues, because these OWN the commands and will delete them
+ // at ResetSession().
+ if (fInterruptedCommandP) {
+ // show that command was not sent
+ DEBUGPRINTFX(DBG_ERROR,("Never finished interrupted sub-command '%s', (outgoing MsgID=%ld, CmdID=%ld)",
+ fInterruptedCommandP->getName(),
+ (long)fInterruptedCommandP->getMsgID(),
+ (long)fInterruptedCommandP->getCmdID()
+ ));
+ delete fInterruptedCommandP;
+ fInterruptedCommandP=NULL;
+ }
+} // TSyncCommand::~TSyncCommand
+
+
+/* end of TSyncCommand implementation */
+
+
+/*
+ * Implementation of TSyncEndCommand
+ */
+
+
+// constructor for receiving Sync Command
+TSyncEndCommand::TSyncEndCommand(
+ TSyncSession *aSessionP, // associated session (for callbacks)
+ uInt32 aMsgID // the Message ID of the command
+) :
+ TSmlCommand(scmd_syncend,false,aSessionP,aMsgID)
+{
+ // nop so far
+} // TSyncEndCommand::TSyncEndCommand
+
+
+// execute command (perform real actions, generate status)
+// returns true if command has executed and can be deleted
+bool TSyncEndCommand::execute(void)
+{
+ bool queueforlater=false;
+
+ // let session do the appropriate processing
+ fSessionP->processSyncEnd(queueforlater);
+ // return true if command has fully executed
+ return !queueforlater;
+} // TSyncEndCommand::execute
+
+
+TSyncEndCommand::~TSyncEndCommand()
+{
+} // TSyncEndCommand::~TSyncEndCommand
+
+
+/* end of TSyncEndCommand implementation */
+
+
+
+/*
+ * Implementation of TAlertCommand
+ */
+
+
+// constructor for sending Alert
+TAlertCommand::TAlertCommand(
+ TSyncSession *aSessionP, // associated session (for callbacks)
+ TLocalEngineDS *aLocalDataStoreP, // local datastore
+ uInt16 aAlertCode // Alert code to send
+) :
+ TSmlCommand(scmd_alert,true,aSessionP)
+{
+ // save datastore
+ fLocalDataStoreP = aLocalDataStoreP;
+ // save Alert Code
+ fAlertCode = aAlertCode;
+ // create internal alert element
+ fAlertElementP = SML_NEW(SmlAlert_t);
+ // set proto element type to make it auto-disposable
+ fAlertElementP->elementType=SML_PE_ALERT;
+ // Cmd ID is now empty (will be set when issued)
+ fAlertElementP->cmdID=NULL;
+ // default to no flags (noResp is set at issue, if at all)
+ fAlertElementP->flags=0;
+ // data is alert code
+ fAlertElementP->data=newPCDataLong(fAlertCode);
+ // no optional elements for now
+ fAlertElementP->cred=NULL;
+ fAlertElementP->itemList=NULL;
+} // TAlertCommand::TAlertCommand
+
+
+// constructor for receiving Alert
+TAlertCommand::TAlertCommand(
+ TSyncSession *aSessionP, // associated session (for callbacks)
+ uInt32 aMsgID, // the Message ID of the command
+ SmlAlertPtr_t aAlertElementP // associated Alert content element
+) :
+ TSmlCommand(scmd_alert,false,aSessionP,aMsgID)
+{
+ // save alert element
+ fAlertElementP = aAlertElementP;
+ // no params
+ fLocalDataStoreP=NULL;
+} // TAlertCommand::TAlertCommand
+
+
+// analyze command (but do not yet execute)
+bool TAlertCommand::analyze(TPackageStates aPackageState)
+{
+ TSmlCommand::analyze(aPackageState);
+ // get Command ID and flags
+ if (fAlertElementP) {
+ StartProcessing(fAlertElementP->cmdID,fAlertElementP->flags);
+ return true;
+ }
+ else return false; // no proto element, bad command
+} // TAlertCommand::analyze
+
+
+// execute command (perform real actions, generate status)
+// returns true if command has executed and can be deleted
+bool TAlertCommand::execute(void)
+{
+ TStatusCommand *statusCmdP=NULL;
+ TSmlCommand *alertresponsecmdP=NULL;
+
+ SYSYNC_TRY {
+ // get alert code
+ sInt32 temp;
+ if (!smlPCDataToLong(fAlertElementP->data,temp)) {
+ // non-numeric alert
+ PDEBUGPRINTFX(DBG_ERROR,(
+ "Non-Integer Alert Data: '%s', cannot handle",
+ smlPCDataToCharP(fAlertElementP->data)
+ ));
+ statusCmdP = newStatusCommand(400,"Alert Data not understood");
+ }
+ else {
+ fAlertCode=temp;
+ PDEBUGPRINTFX(DBG_HOT,(
+ "Code=%hd. %s Cred. Analyzing Items",
+ fAlertCode,
+ fAlertElementP->cred ? "has" : "No"
+ ));
+ // intercept special codes
+ if (fAlertCode==221) {
+ // %%% tdb
+ PDEBUGPRINTFX(DBG_ERROR,("*********** RESULT ALERT 221 received in bad context"));
+ statusCmdP = newStatusCommand(400,"221 Alert in bad context received");
+ } else if (fAlertCode==222) {
+ // request next message in packet
+ PDEBUGPRINTFX(DBG_HOT,("Next Message in Packet Alert (222): flag sending waiting commands"));
+ // - acknowledge 222 alert
+ statusCmdP = newStatusCommand(200);
+ // - add source and target refs
+ statusCmdP->addTargetRef(fSessionP->getLocalURI());
+ statusCmdP->addSourceRef(fSessionP->getRemoteURI());
+ // - issue status for item (but not being sent alone with SyncHdr status)
+ // (treat it like ok-synchdr-status = NOT to be sent if there's no real command following)
+ { TSmlCommand* p=statusCmdP; statusCmdP=NULL; fSessionP->issueRootPtr(p,false,true); }
+ // - signal occurrence of 222 alert to session
+ fSessionP->nextMessageRequest();
+ } else {
+ // normal alert, walk through items
+ SmlItemListPtr_t nextitemP = fAlertElementP->itemList;
+ while (nextitemP) {
+ if (nextitemP->item) {
+ // - check for MaxObjSize here
+ SmlMetInfMetInfPtr_t metaP=smlPCDataToMetInfP(nextitemP->item->meta);
+ if (metaP && metaP->maxobjsize) {
+ smlPCDataToLong(metaP->maxobjsize,fSessionP->fMaxOutgoingObjSize);
+ PDEBUGPRINTFX(DBG_REMOTEINFO,("MaxObjSize found in Alert command: %ld",(long)fSessionP->fMaxOutgoingObjSize));
+ }
+ // process alert item with common code
+ // - generate default OK status
+ statusCmdP = newStatusCommand(200);
+ // - add source and target refs from item
+ statusCmdP->addSourceRef(smlSrcTargLocURIToCharP(nextitemP->item->source));
+ statusCmdP->addTargetRef(smlSrcTargLocURIToCharP(nextitemP->item->target));
+ PDEBUGPRINTFX(DBG_HOT,(
+ "- Processing Alert Item (code=%hd), Source='%s', Target='%s'",
+ fAlertCode,
+ smlSrcTargLocURIToCharP(nextitemP->item->source),
+ smlSrcTargLocURIToCharP(nextitemP->item->target)
+ ));
+ // - let session process the alert item
+ alertresponsecmdP =
+ fSessionP->processAlertItem(
+ fAlertCode,
+ nextitemP->item,
+ fAlertElementP->cred,
+ *statusCmdP,
+ fLocalDataStoreP // receives involved datastore (Note: if Alert cmd has multiple items, this will finally contain only the datastore of the last item, others might differ)
+ );
+ // - issue status for item
+ ISSUE_COMMAND_ROOT(fSessionP,statusCmdP);
+ // - issue result (e.g. acknowledge alert) for item, if any
+ if (alertresponsecmdP) {
+ fSessionP->queueForIssueRoot(alertresponsecmdP);
+ }
+ }
+ // next
+ nextitemP=nextitemP->next;
+ } // while
+ } // normal alert code with item
+ } // numeric alert code
+ // free element
+ FreeSmlElement();
+ }
+ SYSYNC_CATCH (...)
+ // make sure owned objects in local scope are deleted
+ if (statusCmdP) delete statusCmdP;
+ if (alertresponsecmdP) delete alertresponsecmdP;
+ // re-throw
+ SYSYNC_RETHROW;
+ SYSYNC_ENDCATCH
+ // done with command, delete it now: return true
+ return true;
+} // TAlertCommand::execute
+
+
+// returns true if command must be put to the waiting-for-status queue.
+// If false, command can be deleted
+bool TAlertCommand::issue(
+ uInt32 aAsCmdID, // command ID to be used
+ uInt32 aInMsgID, // message ID in which command is being issued
+ bool aNoResp
+)
+{
+ // prepare basic stuff
+ TSmlCommand::issue(aAsCmdID,aInMsgID,aNoResp);
+ // now issue
+ if (fAlertElementP) {
+ // issue command with SyncML toolkit
+ PrepareIssue(&fAlertElementP->cmdID,&fAlertElementP->flags);
+ if (!fEvalMode) {
+ #ifdef SYDEBUG
+ if (fSessionP->fXMLtranslate && fSessionP->fOutgoingXMLInstance)
+ smlAlertCmd(fSessionP->fOutgoingXMLInstance,fAlertElementP);
+ #endif
+ Ret_t err;
+ if ((err=smlAlertCmd(fSessionP->getSmlWorkspaceID(),fAlertElementP))!=SML_ERR_OK) {
+ SYSYNC_THROW(TSmlException("smlAlertCmd",err));
+ }
+ FinalizeIssue();
+ // show debug
+ #ifdef SYDEBUG
+ PDEBUGPRINTFX(DBG_HOT,("Alert Code %s sent",
+ smlPCDataToCharP(fAlertElementP->data)
+ ));
+ // normal alert, walk through items
+ SmlItemListPtr_t nextitemP = fAlertElementP->itemList;
+ while (nextitemP) {
+ if (nextitemP->item) {
+ // show alert item
+ PDEBUGPRINTFX(DBG_HOT,(
+ "- Alert Item: Source='%s', Target='%s'",
+ smlSrcTargLocURIToCharP(nextitemP->item->source),
+ smlSrcTargLocURIToCharP(nextitemP->item->target)
+ ));
+ }
+ // next
+ nextitemP=nextitemP->next;
+ /// @note %%% we should not send more than one item for now!
+ if (nextitemP)
+ DEBUGPRINTFX(DBG_ERROR,("More than one item - handleStatus is not prepared for that!"));
+ } // while
+ #endif
+ // we don't need the status structure any more, free (and NULL ptr) now
+ FreeSmlElement();
+ }
+ else
+ return smlAlertCmd(fSessionP->getSmlWorkspaceID(),fAlertElementP)==SML_ERR_OK;
+ }
+ else {
+ DEBUGPRINTFX(DBG_ERROR,("*** Tried to issue NULL alert"));
+ }
+ // return true if command must be queued for status/result response reception
+ return queueForResponse();
+} // TAlertCommand::issue
+
+
+// handle status received for previously issued command
+// returns true if done, false if command must be kept in the status queue
+bool TAlertCommand::handleStatus(TStatusCommand *aStatusCmdP)
+{
+ /// @note: This is a simplified implementation which KNOWS that
+ /// we do not send Alerts with more than one item. If we did one
+ /// day, we'd have to extend this implementation.
+ // if this alert command was created by a datastore, let it handle it
+ if (fLocalDataStoreP) {
+ // pass to local datastore
+ if (fLocalDataStoreP->engHandleAlertStatus(aStatusCmdP->getStatusCode()))
+ return true; // datastore handled it
+ }
+ // let SmlCommand handle it
+ return TSmlCommand::handleStatus(aStatusCmdP);
+} // TAlertCommand::handleStatus
+
+
+// add a String Item to the alert
+void TAlertCommand::addItemString(
+ const char *aItemString // item string to be added
+)
+{
+ if (fAlertElementP && aItemString) {
+ addItem(newStringDataItem(aItemString));
+ }
+} // TAlertCommand::addItemString
+
+
+// add an Item to the alert
+void TAlertCommand::addItem(
+ SmlItemPtr_t aItemP // existing item data structure, ownership is passed to Alert
+)
+{
+ if (fAlertElementP)
+ addItemToList(aItemP,&(fAlertElementP->itemList));
+} // TAlertCommand::addItem
+
+
+void TAlertCommand::FreeSmlElement(void)
+{
+ // remove SyncML toolkit element(s)
+ FREEPROTOELEMENT(fAlertElementP);
+} // TResultsCommand::FreeSmlElement
+
+
+TAlertCommand::~TAlertCommand()
+{
+ // free command elements, if any (use explicit invocation as this is a destructor)
+ TAlertCommand::FreeSmlElement();
+} // TAlertCommand::~TAlertCommand
+
+
+/* end of TAlertCommand implementation */
+
+
+
+
+
+/*
+ * Implementation of TUnimplementedCommand
+ */
+
+
+// constructor for receiving command
+TUnimplementedCommand::TUnimplementedCommand(
+ TSyncSession *aSessionP, // associated session (for callbacks)
+ uInt32 aMsgID, // the Message ID of the command
+ SmlPcdataPtr_t aCmdID, // command ID (as contents are unknown an cannot be analyzed)
+ Flag_t aFlags, // flags to get fNoResp
+ TSmlCommandTypes aCmdType, // command type (for name)
+ void *aContentP, // associated command content element
+ TSyError aStatusCode // status code to be returned on execution
+) :
+ TSmlCommand(aCmdType,false,aSessionP,aMsgID)
+{
+ // get command ID
+ StrToULong(smlPCDataToCharP(aCmdID),fCmdID);
+ // save noResp
+ fNoResp=(aFlags & SmlNoResp_f)!=0;
+ // forget element
+ smlFreeProtoElement(aContentP);
+ // save status type
+ fStatusCode=aStatusCode;
+} // TUnimplementedCommand::TUnimplementedCommand
+
+
+// execute command (perform real actions, generate status)
+// returns true if command has executed and can be deleted
+bool TUnimplementedCommand::execute(void)
+{
+ TStatusCommand *statusCmdP=NULL;
+
+ DEBUGPRINTFX(DBG_HOT,("UNIMPLEMENTED, started dummy processing, (incoming MsgID=%ld, CmdID=%ld)",(long)fMsgID,(long)fCmdID));
+ SYSYNC_TRY {
+ statusCmdP = newStatusCommand(fStatusCode);
+ statusCmdP->addItemString("Unimplemented Command");
+ ISSUE_COMMAND_ROOT(fSessionP,statusCmdP);
+ }
+ SYSYNC_CATCH (...)
+ // make sure owned objects in local scope are deleted
+ if (statusCmdP) delete statusCmdP;
+ // re-throw
+ SYSYNC_RETHROW;
+ SYSYNC_ENDCATCH
+ // done with command, delete it now: return true
+ return true;
+} // TUnimplementedCommand::execute
+
+
+/* end of TUnimplementedCommand implementation */
+
+
+
+/*
+ * Implementation of TSyncOpCommand
+ */
+
+// constructor for receiving command
+TSyncOpCommand::TSyncOpCommand(
+ TSyncSession *aSessionP, // associated session (for callbacks)
+ TLocalEngineDS *aDataStoreP, // the local datastore this syncop belongs to
+ uInt32 aMsgID, // the Message ID of the command
+ TSyncOperation aSyncOp, // the Sync operation (sop_xxx)
+ TSmlCommandTypes aCmdType, // the command type (scmd_xxx)
+ SmlGenericCmdPtr_t aSyncOpElementP // associated syncml protocol element
+) :
+ TSmlCommand(aCmdType,false,aSessionP,aMsgID)
+{
+ // save datastore
+ fDataStoreP=aDataStoreP;
+ // save operation type
+ fSyncOp=aSyncOp;
+ // save element
+ fSyncOpElementP = aSyncOpElementP;
+ // no remainder to be sent as next chunk
+ fChunkedItemSize = 0;
+ fIncompleteData = false;
+ // no suspended part of item
+ fStoredSize=0;
+ fUnconfirmedSize=0;
+ fLastChunkSize=0;
+} // TSyncOpCommand::TSyncOpCommand
+
+
+// constructor for sending command
+TSyncOpCommand::TSyncOpCommand(
+ TSyncSession *aSessionP, // associated session (for callbacks)
+ TLocalEngineDS *aDataStoreP, // datastore from which command originates
+ TSyncOperation aSyncOp, // sync operation (=command type)
+ SmlPcdataPtr_t aMetaP // meta for entire command (passing owner)
+) :
+ TSmlCommand(scmd_unknown,true,aSessionP)
+{
+ // save datastore
+ fDataStoreP=aDataStoreP;
+ #ifndef USE_SML_EVALUATION
+ // no items yet
+ fItemSizes=0;
+ #endif
+ // no remainder to be sent as next chunk
+ fChunkedItemSize = 0;
+ fIncompleteData = false;
+ // no suspended part of item
+ fStoredSize=0;
+ fUnconfirmedSize=0;
+ fLastChunkSize=0;
+ // command type not yet determined
+ fSyncOp=aSyncOp;
+ // create internal alert element
+ fSyncOpElementP = SML_NEW(SmlGenericCmd_t);
+ // set default to make sure it is disposable (will be adjusted when items are added)
+ fSyncOpElementP->elementType=SML_PE_GENERIC;
+ // Cmd ID is now empty (will be set when issued)
+ fSyncOpElementP->cmdID=NULL;
+ // default to no flags (noResp is set at issue, if at all)
+ fSyncOpElementP->flags=0;
+ // insert meta, if any
+ fSyncOpElementP->meta=aMetaP;
+ // no optional elements for now
+ fSyncOpElementP->cred=NULL;
+ fSyncOpElementP->itemList=NULL;
+ // determine command type
+ switch (fSyncOp) {
+ case sop_wants_add:
+ case sop_add:
+ fSyncOpElementP->elementType=SML_PE_ADD;
+ fCmdType=scmd_add;
+ break;
+ case sop_copy:
+ fSyncOpElementP->elementType=SML_PE_COPY;
+ fCmdType=scmd_copy;
+ break;
+ case sop_move:
+ fSyncOpElementP->elementType=SML_PE_MOVE;
+ fCmdType=scmd_move;
+ break;
+ case sop_wants_replace:
+ case sop_replace:
+ fSyncOpElementP->elementType=SML_PE_REPLACE;
+ fCmdType=scmd_replace;
+ break;
+ case sop_archive_delete:
+ fSyncOpElementP->flags |= SmlArchive_f;
+ goto dodelete;
+ case sop_soft_delete:
+ fSyncOpElementP->flags |= SmlSftDel_f;
+ case sop_delete:
+ dodelete:
+ fSyncOpElementP->elementType=SML_PE_DELETE;
+ fCmdType=scmd_delete;
+ break;
+ default:
+ SYSYNC_THROW(TSyncException("Invalid SyncOp in TSyncOpCommand"));
+ //break;
+ } // switch
+} // TSyncOpCommand::TSyncOpCommand
+
+
+#ifndef USE_SML_EVALUATION
+
+// get (approximated) message size required for sending it
+uInt32 TSyncOpCommand::messageSize(void)
+{
+ // default, should be enough for most commands
+ return TSmlCommand::messageSize()+fItemSizes;
+}
+
+#endif
+
+
+// handle status received for previously issued command
+// returns true if done, false if command must be kept in the status queue
+bool TSyncOpCommand::handleStatus(TStatusCommand *aStatusCmdP)
+{
+ // check if this is a split command
+ if (fIncompleteData) {
+ // must be 213
+ if (aStatusCmdP->getStatusCode()==213) return true;
+ // something wrong, we did not get a 213
+ PDEBUGPRINTFX(DBG_ERROR,("chunked object received status %hd instead of 213 --> aborting session",aStatusCmdP->getStatusCode()));
+ // reason for aborting is incomplete command (reception)
+ fSessionP->AbortSession(412,true); // local problem
+ }
+ else {
+ // end of non-split SyncOp means that eventually saved
+ // parts of partial outgoing item are now obsolete
+ if (fDataStoreP->fPartialItemState==pi_state_save_outgoing) {
+ fDataStoreP->fPartialItemState=pi_state_none; // not any more
+ PDEBUGPRINTFX(DBG_PROTO+DBG_EXOTIC,("chunked object received final status %hd --> forget partial item data now",aStatusCmdP->getStatusCode()));
+ // - forget stored data in case we have any
+ if (fDataStoreP->fPIStoredDataP) {
+ if (fDataStoreP->fPIStoredDataAllocated)
+ smlLibFree(fDataStoreP->fPIStoredDataP);
+ fDataStoreP->fPIStoredDataP=NULL;
+ fDataStoreP->fPIStoredDataAllocated=false; // not any more
+ }
+ // - clear URIs
+ fDataStoreP->fLastSourceURI.erase();
+ fDataStoreP->fLastTargetURI.erase();
+ // - clear other flags
+ fDataStoreP->fLastItemStatus=0; // none left to send
+ fDataStoreP->fPITotalSize=0;
+ fDataStoreP->fPIUnconfirmedSize=0;
+ fDataStoreP->fPIStoredSize=0;
+ }
+ }
+ // let datastore handle this
+ // Note: as fDataStoreP is linked to the generating datastore, this will never be
+ // a superdatastore. engHandleSyncOpStatus() is prepared to convert
+ // superID prefixed localIDs back to subDS's localID
+ bool handled=false;
+ if (fDataStoreP) {
+ handled=fDataStoreP->engHandleSyncOpStatus(aStatusCmdP,this);
+ }
+ if (!handled) {
+ // let base class handle it
+ handled=TSmlCommand::handleStatus(aStatusCmdP);
+ }
+ return handled;
+} // TSyncOpCommand::handleStatus
+
+
+// mark any syncitems (or other data) for resume. Called for pending commands
+// when a Suspend alert is received or whenever a resumable state must be saved
+void TSyncOpCommand::markPendingForResume(TLocalEngineDS *aForDatastoreP, bool aUnsent)
+{
+ // only act if this is for our local datastore, and only outgoing ones!
+ if (fOutgoing && fDataStoreP==aForDatastoreP && fSyncOpElementP) {
+ // go through all of my items
+ SmlItemListPtr_t itemListP = fSyncOpElementP->itemList;
+ SmlItemPtr_t itemP;
+ while (itemListP) {
+ itemP=itemListP->item;
+ if (itemP) {
+ // call datastore to mark this one for resume
+ fDataStoreP->engMarkItemForResume(
+ smlSrcTargLocURIToCharP(itemP->source), // source for outgoing items is localID
+ smlSrcTargLocURIToCharP(itemP->target), // target for outgoing items is remoteID
+ fIncompleteData ? true : aUnsent // if not completely sent yet, always treat it as unsent!
+ );
+ }
+ itemListP=itemListP->next;
+ }
+ }
+} // TSyncOpCommand::markPendingForResume
+
+
+
+// mark item for resend in next sync session (if possible)
+void TSyncOpCommand::markForResend(void)
+{
+ if (fOutgoing && fDataStoreP) {
+ // go through all of my items
+ SmlItemListPtr_t itemListP = fSyncOpElementP->itemList;
+ SmlItemPtr_t itemP;
+ while (itemListP) {
+ itemP=itemListP->item;
+ if (itemP) {
+ // call datastore to mark this one for resume
+ fDataStoreP->engMarkItemForResend(
+ smlSrcTargLocURIToCharP(itemP->source), // source for outgoing items is localID
+ smlSrcTargLocURIToCharP(itemP->target) // target for outgoing items is remoteID
+ );
+ }
+ itemListP=itemListP->next;
+ }
+ }
+} // TSyncOpCommand::markForResend
+
+
+
+// let item update the partial item state for suspend
+// Note: this may only be called for an item that is actually the partial item
+void TSyncOpCommand::updatePartialItemState(TLocalEngineDS *aForDatastoreP)
+{
+ if (!fOutgoing && fDataStoreP==aForDatastoreP && fDataStoreP->dsResumeChunkedSupportedInDB()) {
+ // save info for only partially received item
+ fDataStoreP->fLastItemStatus = 0; // no status sent yet
+ // pass current item data
+ // - get last item data
+ SmlItemListPtr_t itemnodeP=fSyncOpElementP->itemList;
+ while (itemnodeP) {
+ if (!itemnodeP->next) {
+ // this is the last item
+ fDataStoreP->fPartialItemState=pi_state_save_incoming;
+ // - get total expected size
+ fDataStoreP->fPITotalSize=0;
+ SmlMetInfMetInfPtr_t metaP = smlPCDataToMetInfP(itemnodeP->item->meta);
+ if (!metaP || !metaP->size) {
+ // item has no meta or no meta-size, so size must be in meta of command
+ metaP = smlPCDataToMetInfP(fSyncOpElementP->meta);
+ }
+ if (metaP) smlPCDataToULong(metaP->size, fDataStoreP->fPITotalSize);
+ // - get data
+ if (itemnodeP->item && itemnodeP->item->data) {
+ // dispose current contents if these were separately allocated
+ // (otherwise, we can just overwrite the pointer)
+ if (fDataStoreP->fPIStoredDataP && fDataStoreP->fPIStoredDataAllocated)
+ smlLibFree(fDataStoreP->fPIStoredDataP);
+ // datastore does NOT get owner of the data!
+ fDataStoreP->fPIStoredDataAllocated=false; // we still own that data, session must NOT try to dispose!
+ fDataStoreP->fPIStoredSize=itemnodeP->item->data->length;
+ fDataStoreP->fPIStoredDataP=itemnodeP->item->data->content;
+ }
+ // - get amount of stored data that is unconfirmed
+ // (=the size of the most recently received chunk. This is unconfirmed
+ // until the next chunk arrives, because we don't know before that if the
+ // status 213 has reached the sender)
+ fDataStoreP->fPIUnconfirmedSize=fLastChunkSize;
+ // summarize
+ PDEBUGPRINTFX(DBG_ADMIN,(
+ "State of partially received item: total size=%ld, received=%ld, unconfirmed=%ld",
+ (long)fDataStoreP->fPITotalSize,
+ (long)fDataStoreP->fPIStoredSize,
+ (long)fDataStoreP->fPIUnconfirmedSize
+ ));
+ // done
+ break;
+ }
+ itemnodeP=itemnodeP->next;
+ } // while
+ }
+} // TSyncOpCommand::updatePartialItemState
+
+
+// get source (localID) of sent command
+cAppCharP TSyncOpCommand::getSourceLocalID(void)
+{
+ if (!fSyncOpElementP) return NULL;
+ if (!fSyncOpElementP->itemList) return NULL;
+ if (!fSyncOpElementP->itemList->item) return NULL;
+ return smlSrcTargLocURIToCharP(fSyncOpElementP->itemList->item->source);
+} // TSyncOpCommand::getSourceLocalID
+
+
+// get target (remoteID) of sent command
+cAppCharP TSyncOpCommand::getTargetRemoteID(void)
+{
+ if (!fSyncOpElementP) return NULL;
+ if (!fSyncOpElementP->itemList) return NULL;
+ if (!fSyncOpElementP->itemList->item) return NULL;
+ return smlSrcTargLocURIToCharP(fSyncOpElementP->itemList->item->target);
+} // TSyncOpCommand::getTargetRemoteID
+
+
+// add an Item to the sync op command
+void TSyncOpCommand::addItem(
+ SmlItemPtr_t aItemP // existing item data structure, ownership is passed to SyncOp command
+)
+{
+ // add item
+ if (fSyncOpElementP) {
+ addItemToList(aItemP,&(fSyncOpElementP->itemList));
+ #ifndef USE_SML_EVALUATION
+ fItemSizes +=
+ ITEMOVERHEADSIZE +
+ strlen(smlSrcTargLocURIToCharP(aItemP->target)) +
+ strlen(smlSrcTargLocNameToCharP(aItemP->target)) +
+ strlen(smlSrcTargLocURIToCharP(aItemP->source)) +
+ strlen(smlSrcTargLocNameToCharP(aItemP->source));
+ SmlMetInfMetInfPtr_t metaP = smlPCDataToMetInfP(aItemP->meta);
+ if (metaP) fItemSizes+=strlen(smlPCDataToCharP(metaP->type));
+ if (aItemP->data) fItemSizes+=aItemP->data->length;
+ #endif
+ }
+} // TSyncOpCommand::addItem
+
+
+
+// helper: add dataPos EMI to meta
+static void addDataPos(SmlMetInfMetInfPtr_t aMetaP, uInt32 aDataPos)
+{
+ // - find end of emi list or existing datapos
+ SmlPcdataListPtr_t *emiPP = &(aMetaP->emi);
+ /* version that searches for existing datapos and will replace it
+ while (*emiPP!=NULL) {
+ if (strucmp(smlPCDataToCharP((*emiPP)->data),"datapos=",8)==0) {
+ // found existing datapos
+ break;
+ }
+ emiPP = &((*emiPP)->next);
+ }
+ if (*emiPP==NULL) {
+ // - add new EMI
+ *emiPP=(SmlPcdataListPtr_t)smlLibMalloc(sizeof(SmlPcdataList_t));
+ (*emiPP)->next=NULL;
+ }
+ else {
+ // - replace existing
+ smlFreePcdata((*emiPP)->data);
+ }
+ */
+ while (*emiPP!=NULL) emiPP = &((*emiPP)->next);
+ // - add new EMI list element
+ *emiPP=(SmlPcdataListPtr_t)smlLibMalloc(sizeof(SmlPcdataList_t));
+ (*emiPP)->next=NULL;
+ // - set new data
+ string s;
+ StringObjPrintf(s,"datapos=%ld",(long)aDataPos);
+ (*emiPP)->data = newPCDataString(s.c_str(),s.size());
+} // addDataPos
+
+
+
+void TSyncOpCommand::saveAsPartialItem(SmlItemPtr_t aItemP)
+{
+ // - forget stored data in case we have any
+ if (fDataStoreP->fPIStoredDataP) {
+ if (fDataStoreP->fPIStoredDataAllocated)
+ smlLibFree(fDataStoreP->fPIStoredDataP);
+ fDataStoreP->fPIStoredDataP=NULL;
+ fDataStoreP->fPIStoredDataAllocated=false; // not any more
+ }
+ // save only when we can actually store chunk resume data
+ if (fDataStoreP->dsResumeChunkedSupportedInDB()) {
+ // - save URIs
+ fDataStoreP->fLastSourceURI=smlSrcTargLocURIToCharP(aItemP->source);
+ fDataStoreP->fLastTargetURI=smlSrcTargLocURIToCharP(aItemP->target);
+ // - copy current item's data now (before the split!)
+ fDataStoreP->fPIStoredDataAllocated=true; // separately allocated buffer, not connected with item any more
+ fDataStoreP->fPIStoredSize=aItemP->data->length; // length of unsplitted item's buffer
+ fDataStoreP->fPIStoredDataP=smlLibMalloc(aItemP->data->length);
+ smlLibMemcpy((uInt8 *)fDataStoreP->fPIStoredDataP,aItemP->data->content,aItemP->data->length);
+ // - set other flags
+ fDataStoreP->fLastItemStatus=0; // none left to send
+ fDataStoreP->fPartialItemState=pi_state_save_outgoing;
+ fDataStoreP->fPITotalSize=fChunkedItemSize;
+ fDataStoreP->fPIUnconfirmedSize=fDataStoreP->fPIStoredSize; // just a copy
+ }
+} // TSyncOpCommand::saveAsPartialItem
+
+
+// - eventually substitute data with previous session's buffered left-overs from a chunked transfer
+// for resuming a chunked item transfer.
+bool TSyncOpCommand::checkChunkContinuation(void)
+{
+ if (fChunkedItemSize==0 && fDataStoreP && fDataStoreP->fPartialItemState==pi_state_loaded_outgoing) {
+ // this is a so far unchunked item, and there is
+ // to-be-resent data from the previously suspended session
+ // - check if the command contains the partially sent item
+ SmlItemListPtr_t itemListP = fSyncOpElementP->itemList;
+ while (itemListP) {
+ if (
+ itemListP->item &&
+ strcmp(smlSrcTargLocURIToCharP(itemListP->item->source),fDataStoreP->fLastSourceURI.c_str())==0 &&
+ strcmp(smlSrcTargLocURIToCharP(itemListP->item->target),fDataStoreP->fLastTargetURI.c_str())==0
+ ) {
+ // detected to-be-continued chunked item
+ PDEBUGPRINTFX(DBG_PROTO+DBG_HOT,(
+ "Resuming sending chunked item Source='%s', Target='%s' -> replacing data with rest from suspended session",
+ fDataStoreP->fLastSourceURI.c_str(),
+ fDataStoreP->fLastTargetURI.c_str()
+ ));
+ // Do not send meta size on all but first chunk
+ SmlMetInfMetInfPtr_t metinfP = itemListP->item->meta ? (SmlMetInfMetInfPtr_t)(itemListP->item->meta->content) : NULL;
+ if (metinfP && metinfP->size) {
+ smlFreePcdata(metinfP->size);
+ metinfP->size=NULL;
+ }
+ // But make sure item knows the original total size (prevents splitCommand to add meta size again)
+ fChunkedItemSize = fDataStoreP->fPITotalSize;
+ // now add dataPos as <meta><EMI>: fPITotalSize-fPIStoredSize
+ // - make sure we have meta
+ if (!metinfP) {
+ itemListP->item->meta=newMeta();
+ metinfP=(SmlMetInfMetInfPtr_t)(itemListP->item->meta->content);
+ }
+ // - add it (total - what remains to be sent)
+ addDataPos(metinfP,fDataStoreP->fPITotalSize-fDataStoreP->fPIStoredSize);
+ // update buffered data info
+ if (itemListP->item->data) {
+ // substitute original data with buffered rest of data from last session
+ // (so in case the item has changed in the local DB, we'll still send the original data of
+ // which the receiver already has seen a part)
+ PDEBUGPRINTFX(DBG_PROTO+DBG_DETAILS,(
+ "original data size of item=%ld, total size at time of suspend=%ld, rest being sent now=%ld",
+ (long)itemListP->item->data->length, // current total size
+ (long)fDataStoreP->fPITotalSize, // total size at time of suspend
+ (long)fDataStoreP->fPIStoredSize // what we will send now
+ ));
+ // dispose current data
+ smlLibFree(itemListP->item->data->content);
+ // set new data from last session
+ itemListP->item->data->length=fDataStoreP->fPIStoredSize;
+ itemListP->item->data->content=fDataStoreP->fPIStoredDataP;
+ // data is now owned by item
+ fDataStoreP->fPIStoredDataP=NULL;
+ fDataStoreP->fPIStoredSize=0;
+ fDataStoreP->fPIStoredDataAllocated=false;
+ }
+ else {
+ PDEBUGPRINTFX(DBG_ERROR,("WARNING: internal error: to-be-resumed item contains no data?"));
+ }
+ fDataStoreP->fPartialItemState=pi_state_none; // consumed now
+ fDataStoreP->fPITotalSize=0;
+ fDataStoreP->fPIUnconfirmedSize=0;
+ // substitution of item data done
+ // (don't check other items - we have ONE item only, anyway in the current implementation)
+ return true;
+ }
+ // next item
+ itemListP=itemListP->next;
+ }
+ }
+ // no chunk continuation
+ return false;
+} // TSyncOpCommand::checkChunkContinuation
+
+
+// check if split is possible at all
+bool TSyncOpCommand::canSplit(void)
+{
+ // Sync Op commands can be split when remote supports <moredata/> mechanism
+ return fSessionP->fRemoteSupportsLargeObjects;
+}
+
+
+// - try to split (e.g. by SyncML 1.1 moredata mechanism) command by reducing
+// original command by at least aReduceByBytes and generating a second
+// command containing the rest of the data
+// Returns NULL if split is not possible or not worth trying in the current situation
+// (e.g.: only ridiculously small part would remain)
+TSmlCommand *TSyncOpCommand::splitCommand(sInt32 aReduceByBytes)
+{
+ SmlItemListPtr_t *itemListPP;
+ TSyncOpCommand *remainingDataCmdP=NULL;
+
+ // nothing remaining so far
+ SmlItemListPtr_t remainingItems=NULL;
+ // check other conditions
+ if (!fSyncOpElementP) return NULL;
+ // now find where we should split
+ do {
+ // start of list
+ itemListPP = &(fSyncOpElementP->itemList);
+ // check if any items
+ if (*itemListPP==NULL) break; // no more items
+ // find last item
+ while ((*itemListPP)->next) itemListPP = &((*itemListPP)->next);
+ // get it's data
+ SmlItemPtr_t itemP = (*itemListPP)->item;
+ SmlPcdataPtr_t dataP = itemP->data;
+ // check if we should split this item
+ if (
+ canSplit() && // remote can handle large objects (that is: chunked transfers)
+ dataP && // there is data
+ dataP->contentType!=SML_PCDATA_EXTENSION && // it's not an extension
+ dataP->length > aReduceByBytes+MIN_SPLIT_DATA // remaining length of first part is large enough to make splitting worth doing now
+ ) {
+ // split within this item
+ // Prepare original item
+ // - make sure we have meta
+ if (!itemP->meta)
+ itemP->meta = newMeta(); // create meta as we haven't got one yet
+ SmlMetInfMetInfPtr_t metinfP = (SmlMetInfMetInfPtr_t)(itemP->meta->content);
+ // - set size if this is the first chunk
+ if (metinfP->size) smlFreePcdata(metinfP->size);
+ if (fChunkedItemSize==0) {
+ // first chunk
+ // - set dataPos to 0
+ addDataPos(metinfP,0);
+ // Note: we don't need to buffer when sending the first chunk.
+ // If we get suspended before the second chunk is being generated, the item will
+ // start over from beginning anyway (and we can use data from the DB).
+ fChunkedItemSize=dataP->length;
+ metinfP->size=newPCDataLong(fChunkedItemSize);
+ PDEBUGPRINTFX(DBG_PROTO+DBG_HOT,(
+ "Item data too big (%ld) - must be chunked using <MoreData/>, %ld bytes to be sent later",
+ (long)fChunkedItemSize,
+ (long)aReduceByBytes
+ ));
+ #ifdef SYDEBUG
+ // %%% should not happen, but I'm not sure it really won't
+ if (fDataStoreP->fPartialItemState==pi_state_loaded_outgoing) {
+ PDEBUGPRINTFX(DBG_ERROR,("WARNING - internal error: Splitting new item apparently before previous session's item has been resent"));
+ }
+ #endif
+ }
+ else {
+ // this item is NOT the first chunk -> save stuff we need to
+ PDEBUGPRINTFX(DBG_PROTO+DBG_HOT,(
+ "Remaining data still too big (%ld/%ld) - must be chunked again, %ld bytes to be sent later",
+ (long)dataP->length,
+ (long)fChunkedItemSize,
+ (long)aReduceByBytes
+ ));
+ // Note: in this case, the item already has a dataPos - no need to add one here!
+ // Resume the item in the middle rather than resending it in full.
+ // IMPORTANT: It is essential not to store the fLastSourceURI/fLastTargetURI
+ // on the first chunk, as it then probably still contains info from
+ // the previous session.
+ saveAsPartialItem(itemP);
+ }
+ // - set <moredata>
+ itemP->flags |= SmlMoreData_f;
+ // - create new data for original item (original data is in dataP)
+ itemP->data = newPCDataStringX(
+ (uInt8 *)dataP->content, // original data
+ dataP->contentType==SML_PCDATA_OPAQUE, // opaque if original was opaque
+ dataP->length-aReduceByBytes // reduced length
+ );
+ // Create additional item for next message
+ // - new item
+ SmlItemPtr_t nextItemP = newItem();
+ // - with meta
+ nextItemP->meta = newMeta();
+ SmlMetInfMetInfPtr_t nextMetinfP = (SmlMetInfMetInfPtr_t)(nextItemP->meta->content);
+ // - SyncML 1.1.1: The <Size> element MUST only be specified in the first chunk of the item.
+ nextMetinfP->size=NULL; // no size in remaining items
+ // - copy other meta
+ if (metinfP->maxobjsize) nextMetinfP->format=newPCDataString(smlPCDataToCharP(metinfP->maxobjsize)); // We NEED this one of ZIPPED_BINDATA_SUPPORT!
+ if (metinfP->format) nextMetinfP->format=newPCDataString(smlPCDataToCharP(metinfP->format));
+ if (metinfP->type) nextMetinfP->type=newPCDataString(smlPCDataToCharP(metinfP->type));
+ if (metinfP->mark) nextMetinfP->mark=newPCDataString(smlPCDataToCharP(metinfP->mark));
+ //%%% if we ever decide to carry over EMI here, make sure we don't copy <EMI>dataPos=x</EMI>, as
+ // addDataPos() code relies on no dataPos being present when called.
+ //if (metinfP->emi) nextMetinfP->emi=newPCDataString(smlPCDataToCharP(metinfP->emi)); // %%% not needed yet
+ if (metinfP->version) nextMetinfP->version=newPCDataString(smlPCDataToCharP(metinfP->version));
+ // - copy source and target
+ nextItemP->target=newOptLocation(smlSrcTargLocURIToCharP(itemP->target),smlSrcTargLocNameToCharP(itemP->target));
+ nextItemP->source=newOptLocation(smlSrcTargLocURIToCharP(itemP->source),smlSrcTargLocNameToCharP(itemP->source));
+ // - copy flags except <moredata>
+ nextItemP->flags = itemP->flags & ~SmlMoreData_f;
+ // - now copy rest of data
+ nextItemP->data = newPCDataStringX(
+ (uInt8 *)dataP->content + dataP->length-aReduceByBytes, // original data minus what we have in original item
+ dataP->contentType==SML_PCDATA_OPAQUE, // opaque if original was opaque
+ aReduceByBytes // reduced length = remaining bytes
+ );
+ // - original data can now be disposed
+ smlFreePcdata(dataP);
+ // Add correct data position to the next item now.
+ addDataPos(nextMetinfP,fChunkedItemSize-aReduceByBytes);
+ // Now insert new item containing remaining data to beginning of list for next chunk
+ SmlItemListPtr_t ilP = SML_NEW(SmlItemList_t);
+ ilP->next=remainingItems;
+ ilP->item=nextItemP;
+ remainingItems=ilP;
+ // count reduction
+ aReduceByBytes=0;
+ // done
+ break;
+ }
+ else {
+ // Split between items
+ fChunkedItemSize=0; // no chunking in progress now
+ // - get last itemlist element
+ SmlItemListPtr_t ilP = *itemListPP;
+ // - cut it out of original list
+ *itemListPP=NULL;
+ // - put it at the BEGINNING of the remaining items list
+ ilP->next=remainingItems;
+ remainingItems=ilP;
+ // - count reduction
+ aReduceByBytes -= dataP ? dataP->length : 0;
+ // loop to check second-last item
+ }
+ // repeat as long as reduction goal is not met
+ } while (aReduceByBytes>0);
+ // check if any items left in original command
+ if (fSyncOpElementP->itemList == NULL) {
+ // none left in original list, can't split
+ // - put back remaining list to original item
+ fSyncOpElementP->itemList = remainingItems;
+ // - cannot split
+ return NULL;
+ }
+ // now create new command containing the remaining items
+ if (remainingItems) {
+ if (aReduceByBytes<=0) {
+ // duplicate meta
+ SmlPcdataPtr_t splitMetaP = copyMeta(fSyncOpElementP->meta);
+ // make new command
+ remainingDataCmdP = new TSyncOpCommand(
+ fSessionP, // associated session (for callbacks)
+ fDataStoreP, // datastore from which command originates
+ fSyncOp, // sync operation (=command type)
+ splitMetaP // meta for entire command (passing owner)
+ );
+ // now pass remaining items to new command
+ remainingDataCmdP->fSyncOpElementP->itemList = remainingItems;
+ // next command must know that it part of a chunked transfer
+ remainingDataCmdP->fChunkedItemSize = fChunkedItemSize;
+ // original command must know that it must expect a 213 status
+ fIncompleteData = true;
+ }
+ else {
+ // remaining items not used, delete them
+ smlFreeItemList(remainingItems);
+ }
+ }
+ // return split-off command
+ return remainingDataCmdP;
+} // TSyncOpCommand::splitCommand
+
+
+// - test if completely issued (must be called after issue() and continueIssue())
+bool TSyncOpCommand::finished(void)
+{
+ return
+ (fOutgoing) || // outgoing commands are always finished (new split mechanism actually splits the command in two separate commands)
+ (!fOutgoing && fSessionP->fIncompleteDataCommandP!=this); // not part of an incoming chunked data transfer
+} // TSyncOpCommand::finished
+
+
+// analyze command (but do not yet execute)
+bool TSyncOpCommand::analyze(TPackageStates aPackageState)
+{
+ TSmlCommand::analyze(aPackageState);
+ // get Command ID and flags
+ if (fSyncOpElementP) {
+ StartProcessing(fSyncOpElementP->cmdID,fSyncOpElementP->flags);
+ return true;
+ }
+ else return false; // no proto element, bad command
+} // TSyncOpCommand::analyze
+
+
+// Adds data chunk from specified command to this (incomplete) command
+// includes checking if sync op and item source/target match and
+// checks for size match
+#define MISSING_END_OF_CHUNK_ERROR 223 // End of Data for chunked object not received
+
+localstatus TSyncOpCommand::AddNextChunk(SmlItemPtr_t aNextChunkItem, TSyncOpCommand *aCmdP)
+{
+
+ SmlItemListPtr_t incompletenode;
+ SmlItemPtr_t incompleteitem;
+ SmlPcdataPtr_t newdata;
+ localstatus sta;
+
+ // find last item in this command
+ incompletenode = fSyncOpElementP->itemList;
+ if (!incompletenode) return 412; // incomplete command
+ while (incompletenode && incompletenode->next) incompletenode=incompletenode->next;
+ incompleteitem = incompletenode->item;
+ // check for data integrity
+ if (!incompleteitem) return 412;
+ if (!incompleteitem->data) return 412;
+ if (!aNextChunkItem) return 412;
+ if (!aNextChunkItem->data) return 412;
+ // check for item compatibility
+ // - commands must have same sync op
+ if (getSyncOp() != aCmdP->getSyncOp())
+ goto missingeoc; // not a status - will be converted to alert
+ // - must have same source and target URI
+ if (
+ strcmp(smlSrcTargLocURIToCharP(incompleteitem->source),smlSrcTargLocURIToCharP(aNextChunkItem->source))!=0 ||
+ strcmp(smlSrcTargLocURIToCharP(incompleteitem->target),smlSrcTargLocURIToCharP(aNextChunkItem->target))!=0
+ )
+ goto missingeoc; // not a status - will be converted to alert
+ // - must have same PCData type and not extension
+ if (
+ incompleteitem->data->contentType != aNextChunkItem->data->contentType ||
+ aNextChunkItem->data->contentType == SML_PCDATA_EXTENSION
+ )
+ return 400; // originator error, bad request
+ // append data from next chunk to incomplete item
+ newdata = SML_NEW(SmlPcdata_t);
+ // - same content type as previous data had
+ newdata->contentType = incompleteitem->data->contentType;
+ newdata->contentType = incompleteitem->data->contentType;
+ // - set new size
+ newdata->length = incompleteitem->data->length + aNextChunkItem->data->length;
+ // - remember size of chunk we're adding now in the incomplete command
+ aCmdP->fLastChunkSize = aNextChunkItem->data->length;
+ // - allocate new data for updated item (including an extra NUL terminator)
+ newdata->content=smlLibMalloc(newdata->length+1);
+ if (newdata->content==NULL)
+ return 413; // request entity too large
+ // - copy existing content
+ smlLibMemcpy(newdata->content,incompleteitem->data->content,incompleteitem->data->length);
+ // - add new chunk
+ smlLibMemcpy((uInt8 *)newdata->content+incompleteitem->data->length,aNextChunkItem->data->content,aNextChunkItem->data->length);
+ // - set the NUL terminator in case item is parsed as C string
+ *(((uInt8 *)newdata->content)+newdata->length)=0;
+ // - free old data in item
+ smlFreePcdata(incompleteitem->data);
+ // - insert updated data
+ incompleteitem->data = newdata;
+ // Now check if we are complete
+ if ((aNextChunkItem->flags & SmlMoreData_f) == 0) {
+ // end of chunk, verify size
+ // - get total size from meta (of original item)
+ SmlMetInfMetInfPtr_t metaP = smlPCDataToMetInfP(incompleteitem->meta);
+ if (!metaP || !metaP->size) {
+ // item has no meta or no meta-size, so size must be in meta of command
+ metaP = smlPCDataToMetInfP(fSyncOpElementP->meta);
+ if (!metaP) {
+ PDEBUGPRINTFX(DBG_ERROR,("Chunked item has no meta -> no size found"));
+ return 424; // no meta: Size mismatch
+ }
+ }
+ sInt32 expectedSize;
+ if (!smlPCDataToLong(metaP->size, expectedSize)) {
+ PDEBUGPRINTFX(DBG_ERROR,("Chunked item had no or invalid meta <size> in first chunk"));
+ return 424; // bad size string: size mismatch
+ }
+ // - check for size match
+ if (incompleteitem->data->length != expectedSize) {
+ // check if size mismatch could be due to duplicate transmit of unconfirmed data
+ if (fUnconfirmedSize == uInt32(incompleteitem->data->length - expectedSize)) {
+ // size mismatch is exactly what could be caused by duplicate transmit
+ PDEBUGPRINTFX(DBG_PROTO+DBG_HOT,(
+ "Detected duplicate chunk in reassembled item due to resume without dataPos (%ld bytes at %ld) -> adjusting",
+ (long)fUnconfirmedSize,
+ (long)fStoredSize-fUnconfirmedSize
+ ));
+ // - cut out duplicate
+ smlLibMemmove(
+ ((uInt8 *)incompleteitem->data->content+fStoredSize-fUnconfirmedSize), // to end of confirmed data
+ ((uInt8 *)incompleteitem->data->content+fStoredSize), // from end of stored data
+ incompleteitem->data->length-fStoredSize // everything between end of stored data and end of item
+ );
+ incompleteitem->data->length = expectedSize; // adjust length
+ *((uInt8 *)incompleteitem->data->content+expectedSize)=0; // add new safety terminator
+ fUnconfirmedSize=0; // no data unconfirmed any more
+ }
+ else {
+ PDEBUGPRINTFX(DBG_ERROR,(
+ "Chunked item has wrong size (%ld, expected=%ld) after reassembly",
+ (long)incompleteitem->data->length,
+ (long)expectedSize
+ ));
+ return 424; // size mismatch
+ }
+ }
+ // successfully reassembled
+ sta=0;
+ }
+ else {
+ // not yet completely reassembled
+ sta=213;
+ }
+ // get MsgID and CmdID from added chunk's commnd
+ fCmdID = aCmdP->getCmdID();
+ fMsgID = aCmdP->getMsgID();
+ // copy actual cmdID tag (to make sure we don't get the wrong CmdID when re-processing the command later)
+ smlFreePcdata(fSyncOpElementP->cmdID); // ged rid of current ID
+ fSyncOpElementP->cmdID = smlPcdataDup(aCmdP->fSyncOpElementP->cmdID); // copy ID from last added chunk
+ // return status code
+ return sta;
+missingeoc:
+ // missing end of chunk
+ TAlertCommand *alertCmdP = new TAlertCommand(fSessionP, NULL, (uInt16)223);
+ SmlItemPtr_t alertItemP = newItem();
+ alertItemP->target=newOptLocation(smlSrcTargLocURIToCharP(incompleteitem->source));
+ alertItemP->source=newOptLocation(smlSrcTargLocURIToCharP(incompleteitem->target));
+ alertCmdP->addItem(alertItemP);
+ // issue the alert
+ fSessionP->issueRootPtr(alertCmdP);
+ // signal failure of reassembling chunked item
+ return MISSING_END_OF_CHUNK_ERROR;
+} // TSyncOpCommand::AddNextChunk
+
+
+// execute command (perform real actions, generate status)
+// returns true if command has executed and can be deleted
+bool TSyncOpCommand::execute(void)
+{
+ TStatusCommand *statusCmdP=NULL;
+ SmlItemListPtr_t *itemnodePP, thisitemnode;
+ localstatus sta;
+ TSyncOpCommand *incompleteCmdP;
+ bool queueforlater,processitem;
+ SmlItemListPtr_t tobequeueditems=NULL;
+
+ SYSYNC_TRY {
+ // get datastore pointer if we do not have one yet
+ if (fDataStoreP==NULL) {
+ // in case we did not get the pointer at creation - that is, when the enclosing
+ // <sync> was delayed - we must now get datastore as set by the current <sync> command
+ fDataStoreP = fSessionP->fLocalSyncDatastoreP;
+ if (fDataStoreP==NULL) {
+ statusCmdP=newStatusCommand(404); // no datastore, we can't process items for it
+ ISSUE_COMMAND_ROOT(fSessionP,statusCmdP);
+ return true; // command executed
+ }
+ }
+ // get command meta if any
+ SmlMetInfMetInfPtr_t cmdmetaP=smlPCDataToMetInfP(fSyncOpElementP->meta);
+ // process items
+ DEBUGPRINTFX(DBG_HOT,("command started processing"));
+ itemnodePP=&(fSyncOpElementP->itemList);
+ while (*itemnodePP) {
+ queueforlater=false; // do no queue by default
+ processitem=true; // process by default
+ thisitemnode = *itemnodePP;
+ // no result nor status so far
+ statusCmdP=NULL;
+ // check for NULL item
+ if (!thisitemnode->item) {
+ PDEBUGPRINTFX(DBG_ERROR,("command with NULL item"));
+ statusCmdP=newStatusCommand(400); // protocol error
+ ISSUE_COMMAND_ROOT(fSessionP,statusCmdP);
+ return true; // command executed
+ }
+ // check if we are resuming a partially transmitted item (but MUST have data)
+ if (
+ fSessionP->fIncompleteDataCommandP==NULL &&
+ thisitemnode->item->data && // item must have data
+ fDataStoreP->fPartialItemState==pi_state_loaded_incoming &&
+ strcmp(smlSrcTargLocURIToCharP(thisitemnode->item->source),fDataStoreP->fLastSourceURI.c_str())==0 &&
+ strcmp(smlSrcTargLocURIToCharP(thisitemnode->item->target),fDataStoreP->fLastTargetURI.c_str())==0
+ ) {
+ // this is the last item sent in the previously suspended session
+ if (fDataStoreP->fPIStoredSize==0) {
+ // No item was left only partially received from the suspended session.
+ // But we must handle the special case where the final status of the last item sent
+ // in the suspended session did not reach the recipient, so we now see the last chunk again.
+ // In this case, simply re-issue the status and ignore the contents
+ if (fDataStoreP->fLastItemStatus) {
+ // item already processed, just repeat sending the status
+ PDEBUGPRINTFX(DBG_PROTO,("Received last chunk for already processed item -> just resending status %hd",fDataStoreP->fLastItemStatus));
+ statusCmdP = newStatusCommand(fDataStoreP->fLastItemStatus);
+ processitem=false; // do not further process the item, just status
+ fDataStoreP->fPartialItemState=pi_state_none; // chunked transfer from last session finally done
+ }
+ }
+ else {
+ // we have a partially received item left from the suspended session
+ // continue it
+ PDEBUGPRINTFX(DBG_PROTO+DBG_HOT,(
+ "Resuming receiving chunked item Source='%s', Target='%s'",
+ fDataStoreP->fLastSourceURI.c_str(),
+ fDataStoreP->fLastTargetURI.c_str()
+ ));
+ // simulate a meta size from suspended session's saved item size
+ // - create meta if none there
+ if (!thisitemnode->item->meta)
+ thisitemnode->item->meta = newMeta();
+ // - now check/generate meta size
+ SmlMetInfMetInfPtr_t metinfP = (SmlMetInfMetInfPtr_t)(thisitemnode->item->meta->content);
+ if (metinfP->size) {
+ #ifdef __MWERKS__
+ #warning "%%% maybe use presence of meta size to know that this is NOT resuming, that is as an implicit dataPos=0"
+ #endif
+ /* if I understand correctly, size MUST NOT be present on any but the first chunk.
+ If that's correct, we could discard the saved partial item from a previous session
+ in case we see a size (because this would mean the item is retransmitted as a whole
+ and sender does not support resuming a partial item */
+ // item has meta size, check if correct
+ uInt32 thissize;
+ if (
+ !smlPCDataToULong(metinfP->size, thissize) ||
+ thissize!=fDataStoreP->fPITotalSize
+ )
+ {
+ PDEBUGPRINTFX(DBG_ERROR,(
+ "Resumed item size does not match (expected=%ld, found=%ld)",
+ (long)fDataStoreP->fPITotalSize,
+ (long)thissize
+ ));
+ statusCmdP = newStatusCommand(424);
+ processitem=false; // do not further process the item
+ }
+ }
+ else {
+ // item has no meta size, create it now
+ metinfP->size=newPCDataLong(fDataStoreP->fPITotalSize);
+ }
+ SmlPcdataPtr_t newdata=NULL;
+ uInt32 dataPos=0;
+ if (processitem) {
+ // save sizes confirmed/unconfirmed data in this command object
+ // (for eventual reassembly adjustment when command is complete)
+ fUnconfirmedSize=fDataStoreP->fPIUnconfirmedSize;
+ fStoredSize=fDataStoreP->fPIStoredSize;
+ // Determine Data position
+ // - check if we have it in EMI (Synthesis-Oracle enhancement)
+ // <EMI xmlns='syncml:metinf'>datapos=NUM</EMI>
+ SmlPcdataListPtr_t emiP = metinfP->emi; // start with item meta EMI
+ bool foundDataPos=false;
+ for (uInt16 i=0; i<2; i++) {
+ while (emiP) {
+ if (emiP->data) {
+ const char *p = smlPCDataToCharP(emiP->data);
+ if (strucmp(p,"datapos=",8)==0) {
+ // correct lead-in, get number now
+ if (StrToULong(p+8,dataPos)>0) {
+ PDEBUGPRINTFX(DBG_PROTO+DBG_DETAILS,("found <meta><EMI>datapos=%ld",(long)dataPos));
+ foundDataPos=true; // found dataPos
+ break;
+ }
+ else {
+ PDEBUGPRINTFX(DBG_ERROR,("invalid number in <meta><EMI>datapos, ignoring it"));
+ }
+ }
+ }
+ // check next
+ emiP=emiP->next;
+ }
+ // done if found
+ if (foundDataPos) break;
+ // try with cmd meta
+ if (!cmdmetaP) break; // we don't have a command meta, no point to search
+ emiP = cmdmetaP->emi; // cmd meta EMI
+ }
+ if (foundDataPos) {
+ // we have a dataPos value
+ // - no unconfirmed size any more for this item (but we still need the datastore-level vars
+ // below to do the copying
+ fUnconfirmedSize=0;
+ // - confirmed size is the data position now
+ fStoredSize=dataPos;
+ }
+ else {
+ // without known dataPos, we assume all received data confirmed for now
+ // and will adjust at end of item if needed
+ dataPos = fDataStoreP->fPIStoredSize;
+ }
+ // - check integrity
+ if (dataPos>fDataStoreP->fPIStoredSize) {
+ PDEBUGPRINTFX(DBG_ERROR,(
+ "Data position invalid (max allowed=%ld, found=%ld)",
+ (long)fDataStoreP->fPIStoredSize,
+ (long)dataPos
+ ));
+ statusCmdP = newStatusCommand(412);
+ processitem=false; // do not further process the item
+ }
+ if (processitem) {
+ // - combine new and old data
+ newdata = SML_NEW(SmlPcdata_t);
+ // - same content type
+ newdata->contentType = thisitemnode->item->data->contentType;
+ // - set new size
+ newdata->length = dataPos + thisitemnode->item->data->length;
+ // - allocate new data for updated item (including an extra NUL terminator)
+ newdata->content=smlLibMalloc(newdata->length+1);
+ if (newdata->content==NULL) {
+ statusCmdP = newStatusCommand(413);
+ processitem=false; // do not further process the item
+ }
+ }
+ }
+ if (processitem) {
+ // copy already received content up to dataPos
+ smlLibMemcpy(newdata->content,fDataStoreP->fPIStoredDataP,dataPos);
+ // forget data stored at DS level
+ fDataStoreP->fPIStoredSize=0;
+ if (fDataStoreP->fPIStoredDataP) {
+ // only free if not owned by an item
+ if (fDataStoreP->fPIStoredDataAllocated)
+ smlLibFree(fDataStoreP->fPIStoredDataP);
+ }
+ fDataStoreP->fPIStoredDataP=NULL;
+ // append content just received in this item
+ smlLibMemcpy((uInt8 *)newdata->content+dataPos,thisitemnode->item->data->content,thisitemnode->item->data->length);
+ // - set the NUL terminator in case item is parsed as C string
+ *(((uInt8 *)newdata->content)+newdata->length)=0;
+ // - free old data in item
+ smlFreePcdata(thisitemnode->item->data);
+ // - insert new data
+ thisitemnode->item->data = newdata;
+ }
+ /* No, we need to keep pi_state_loaded_incoming until we get
+ // Finished processing left-overs from last session
+ fDataStoreP->fPartialItemState=pi_state_none;
+ */
+ // Now we can treat this as if it was the first (or only) chunk of a non-resumed item
+ } // if we have partial data received in last session
+ } // if item is the same as last item in suspended session
+ if (processitem) {
+ processitem=false; // only process further if we detect complete data
+ // check if this item is complete (not chunked by <moredata>)
+ if (thisitemnode->item->flags & SmlMoreData_f) {
+ // no, it's only a chunk
+ if (thisitemnode->item->data==NULL) {
+ PDEBUGPRINTFX(DBG_ERROR,("Chunked item has no <data>"));
+ statusCmdP->setStatusCode(412);
+ // do not further process the command, exit item loop
+ break;
+ }
+ // - return appropriate status
+ statusCmdP = newStatusCommand(213); // chunked item accepted and buffered
+ // - no items may follow the chunked one
+ if (thisitemnode->next) {
+ PDEBUGPRINTFX(DBG_ERROR,("Chunked item had additional items after the chunked one"));
+ statusCmdP->setStatusCode(400);
+ // do not further process the command, exit item loop
+ break;
+ }
+ if (fSessionP->fIncompleteDataCommandP==NULL) {
+ // This is the first chunk, save this as the original command (as it contains all meta)
+ fSessionP->fIncompleteDataCommandP = this;
+ // make sure that already executed items will not get saved
+ *itemnodePP=NULL; // disconnect not-yet-executed items from executed ones
+ smlFreeItemList(fSyncOpElementP->itemList); // free executed items
+ fSyncOpElementP->itemList = thisitemnode; // put not-yet-executed, partial item into command
+ // make sure moredata flag is not set on reassembled item
+ thisitemnode->item->flags &= ~SmlMoreData_f;
+ // save size of the chunk
+ fLastChunkSize=thisitemnode->item->data->length;
+ }
+ else {
+ // this is a chunk in the middle, combine its data
+ // with the already buffered incomplete command
+ // and update MsgID/CmdID. If source and target do not match,
+ // statusCmdP will set to appropriate error code
+ sta=fSessionP->fIncompleteDataCommandP->AddNextChunk(thisitemnode->item,this);
+ if (sta) statusCmdP->setStatusCode(sta); // set error if any
+ }
+ PDEBUGPRINTFX(DBG_PROTO+DBG_HOT,("<Moredata/> set: Chunk (%ld bytes) received (plus eventual carry over from suspend) and buffered",(long)fLastChunkSize));
+ }
+ else {
+ // Complete item or end of chunked item
+ processitem=true;
+ incompleteCmdP = fSessionP->fIncompleteDataCommandP;
+ if (incompleteCmdP) {
+ // end of chunked item, add final chunk and check for errors
+ sta=incompleteCmdP->AddNextChunk(thisitemnode->item,this);
+ if (sta==MISSING_END_OF_CHUNK_ERROR) {
+ // command should have been end of chunked data, but wasn't.
+ // Alert 223 is already issued.
+ // - dispose of failed incomplete command
+ delete incompleteCmdP;
+ fSessionP->fIncompleteDataCommandP=NULL;
+ // - process item as new command
+ }
+ else if (sta==0) {
+ // successfully reassembled command, execute it now
+ PDEBUGPRINTFX(DBG_PROTO+DBG_HOT,("Last Chunk received (%ld bytes), item is reassembled",(long)fLastChunkSize));
+ // - first remove global link to it to avoid recursion
+ fSessionP->fIncompleteDataCommandP=NULL;
+ // - execute now (and pass ownership)
+ // issues appropriate statuses
+ fSessionP->process(incompleteCmdP);
+ // - this item is processed now, continue in loop if there are more items
+ processitem=false; // do not process the item normally
+ // Note: statusCmdP is NULL here, so no extra status will be issued for the
+ // item (process() should have already caused appropriate statuses to
+ // be generated.
+ }
+ else {
+ // some error with this item, generate status
+ statusCmdP = newStatusCommand(sta);
+ PDEBUGPRINTFX(DBG_ERROR,("Adding next chunk to item failed with status=%hd",sta));
+ // - dispose of failed incomplete command
+ delete incompleteCmdP;
+ fSessionP->fIncompleteDataCommandP=NULL;
+ // do not process anything any further
+ processitem=false; // do not process the item normally
+ fDataStoreP->fLocalItemsError++; // count this as an error
+ }
+ }
+ else {
+ // save size of the chunk (if item has ANY data at all)
+ fLastChunkSize=thisitemnode->item->data ? thisitemnode->item->data->length : 0;
+ }
+ if (processitem) {
+ // - get remote and local IDs of item
+ const char *remoteID=smlSrcTargLocURIToCharP(thisitemnode->item->source);
+ const char *localID=smlSrcTargLocURIToCharP(thisitemnode->item->target);
+ // prepare OK status for item
+ statusCmdP = newStatusCommand(200);
+ // let session process the item
+ PDEBUGPRINTFX(DBG_PROTO+DBG_HOT,(
+ "Item (syncop: %s) started processing, remoteID='%s', localID='%s'",
+ SyncOpNames[(sInt16)fSyncOp],
+ remoteID,
+ localID
+ ));
+ queueforlater=false;
+ if (fSessionP->processSyncOpItem(
+ fSyncOp, // the operation
+ thisitemnode->item, // the item to be processed
+ cmdmetaP, // command-wide meta, if any
+ fDataStoreP, // related datastore pointer
+ *statusCmdP, // pre-set 200 status, can be modified in case of errors
+ queueforlater // set if processing of item must be done later
+ )) {
+ // fully ok (no internal change of requested operations or
+ // silent acceptance of deleting non-existant item)
+ }
+ else {
+ // Internal processing of items showed some irregularity, but
+ // status to remote peer can still be ok (for example when trying
+ // to delete non-existant item in datastore with incomplete rollbacks,
+ // processSyncOpItem() will return false, but Status=200.
+ PDEBUGPRINTFX(DBG_PROTO,(
+ "Irregularity in execution of item, status=%hd",
+ statusCmdP->getStatusCode()
+ ));
+ }
+ } // if processitem
+ } // if complete item
+ } // if processitem
+ // now generate status or queue for later
+ // - remember as last item for eventual suspend and resume
+ fDataStoreP->fLastSourceURI = smlSrcTargLocURIToCharP(thisitemnode->item->source);
+ fDataStoreP->fLastTargetURI = smlSrcTargLocURIToCharP(thisitemnode->item->target);
+ if (queueforlater) {
+ PDEBUGPRINTFX(DBG_PROTO,(
+ "Item could not be processed completely now -> will be queued for later processing"
+ ));
+ // item processing could not complete, we must queue this and all other items
+ // in this command for later processing. However, re-assembling chunked
+ // items must proceed as normal.
+ fDataStoreP->fLastItemStatus = 0; // no status sent yet
+ // Therefore, we move this item from the original list to a list of to-be-queued items
+ addItemToList(
+ thisitemnode->item, // the item
+ &tobequeueditems // place pointer to next node here
+ );
+ // cut item out of original list to make sure it does not get disposed with it
+ // - itemnodePP points to pointer to list start or a "next" pointer in a node
+ // (which in turn points to thisitemnode). Now let it point to the next item node.
+ // - this also advance processing to the next item
+ *itemnodePP = thisitemnode->next;
+ // - dispose of the item node itself
+ thisitemnode->next=NULL; // disconnect subsequent nodes
+ thisitemnode->item=NULL; // disconnect item (which is now in tobequeueditems list)
+ smlFreeItemList(thisitemnode); // dispose of node
+ }
+ else {
+ // count incoming net data
+ if (thisitemnode->item->data) {
+ fDataStoreP->fIncomingDataBytes+=thisitemnode->item->data->length;
+ }
+ // item processed
+ // - remember status (final only) for eventual suspend and resume
+ sta= statusCmdP ? statusCmdP->getStatusCode() : 0;
+ if (sta!=213) {
+ // final status received, save it for eventual resend
+ fDataStoreP->fLastItemStatus = sta;
+ // but forget data stored at DS level
+ fDataStoreP->fPIStoredSize=0;
+ if (fDataStoreP->fPIStoredDataP) {
+ // only free if not owned by an item
+ if (fDataStoreP->fPIStoredDataAllocated)
+ smlLibFree(fDataStoreP->fPIStoredDataP);
+ }
+ fDataStoreP->fPIStoredDataP=NULL;
+ // make sure it gets saved
+ fDataStoreP->fPartialItemState = pi_state_save_incoming;
+ }
+ // - issue status for it
+ if (statusCmdP) {
+ // add source and target refs of item
+ statusCmdP->addTargetRef(smlSrcTargLocURIToCharP(thisitemnode->item->target)); // add target ref
+ statusCmdP->addSourceRef(smlSrcTargLocURIToCharP(thisitemnode->item->source)); // add source ref
+ // issue
+ ISSUE_COMMAND_ROOT(fSessionP,statusCmdP);
+ }
+ // advance to next item in list
+ itemnodePP = &(thisitemnode->next);
+ }
+ } // item loop
+ // free this one in advance (only if command is finished)
+ if (finished() && !tobequeueditems) FreeSmlElement();
+ // update item list in command for queuing if needed
+ if (tobequeueditems) {
+ // there are to-be-queued items, insert them instead of the original item list
+ // - delete current item list (only contains already processed and statused items)
+ smlFreeItemList(fSyncOpElementP->itemList);
+ // - insert to be queued items instead
+ fSyncOpElementP->itemList=tobequeueditems;
+ }
+ }
+ SYSYNC_CATCH (...)
+ // make sure owned objects in local scope are deleted
+ if (statusCmdP) delete statusCmdP;
+ // re-throw
+ SYSYNC_RETHROW;
+ SYSYNC_ENDCATCH
+ // if not all items could be executed, command must be queued for later re-execution.
+ // (all successfully executed items are already deleted from the item list)
+ return !tobequeueditems;
+} // TSyncOpCommand::execute
+
+
+// returns true if command must be put to the waiting-for-status queue.
+// If false, command does not need to be put into waiting-for-status queue
+// and can be deleted if finished() returns true
+bool TSyncOpCommand::issue(
+ uInt32 aAsCmdID, // command ID to be used
+ uInt32 aInMsgID, // message ID in which command is being issued
+ bool aNoResp // dummy here, because Status has always no response
+)
+{
+ // prepare basic stuff
+ TSmlCommand::issue(aAsCmdID,aInMsgID,aNoResp);
+ // now issue
+ if (fSyncOpElementP) {
+ // issue command with SyncML toolkit (no flags)
+ PrepareIssue(&fSyncOpElementP->cmdID,&fSyncOpElementP->flags); // CmdID and flags
+ Ret_t err;
+ InstanceID_t wspid = fSessionP->getSmlWorkspaceID();
+ switch (fCmdType) {
+ case scmd_add:
+ #ifdef SYDEBUG
+ if (fSessionP->fXMLtranslate && fSessionP->fOutgoingXMLInstance && !fEvalMode)
+ smlAddCmd(fSessionP->fOutgoingXMLInstance,fSyncOpElementP);
+ #endif
+ err=smlAddCmd(wspid,fSyncOpElementP);
+ break;
+ #ifdef COPY_SEND
+ case scmd_copy:
+ #ifdef SYDEBUG
+ if (fSessionP->fXMLtranslate && fSessionP->fOutgoingXMLInstance && !fEvalMode)
+ smlCopyCmd(fSessionP->fOutgoingXMLInstance,fSyncOpElementP);
+ #endif
+ err=smlCopyCmd(wspid,fSyncOpElementP);
+ break;
+ #endif
+ case scmd_move:
+ #ifdef SYDEBUG
+ if (fSessionP->fXMLtranslate && fSessionP->fOutgoingXMLInstance && !fEvalMode)
+ smlMoveCmd(fSessionP->fOutgoingXMLInstance,fSyncOpElementP);
+ #endif
+ err=smlMoveCmd(wspid,fSyncOpElementP);
+ break;
+ case scmd_replace:
+ #ifdef SYDEBUG
+ if (fSessionP->fXMLtranslate && fSessionP->fOutgoingXMLInstance && !fEvalMode)
+ smlReplaceCmd(fSessionP->fOutgoingXMLInstance,fSyncOpElementP);
+ #endif
+ err=smlReplaceCmd(wspid,fSyncOpElementP);
+ break;
+ case scmd_delete:
+ #ifdef SYDEBUG
+ if (fSessionP->fXMLtranslate && fSessionP->fOutgoingXMLInstance && !fEvalMode)
+ smlDeleteCmd(fSessionP->fOutgoingXMLInstance,fSyncOpElementP);
+ #endif
+ err=smlDeleteCmd(wspid,fSyncOpElementP);
+ break;
+ default:
+ SYSYNC_THROW(TSyncException("TSyncOpCommand:issue, bad command type"));
+ }
+ if (!fEvalMode) {
+ if (err!=SML_ERR_OK) {
+ SYSYNC_THROW(TSmlException("smlAdd/Copy/Replace/DeleteCmd",err));
+ }
+ FinalizeIssue();
+ // show items
+ SmlItemListPtr_t itemP = fSyncOpElementP->itemList;
+ while (itemP) {
+ // count net data sent
+ sInt32 itemlen=0;
+ if (itemP->item && itemP->item->data) {
+ itemlen=itemP->item->data->length;
+ fDataStoreP->fOutgoingDataBytes+=itemlen;
+ }
+ // show item source and target
+ PDEBUGPRINTFX(DBG_PROTO,(
+ "Item remoteID='%s', localID='%s', datasize=%ld",
+ smlSrcTargLocURIToCharP(itemP->item->target),
+ smlSrcTargLocURIToCharP(itemP->item->source),
+ (long)itemlen
+ ));
+ if (fChunkedItemSize>0 && !fIncompleteData) {
+ #ifdef SYDEBUG
+ PDEBUGPRINTFX(DBG_PROTO+DBG_HOT,(
+ "Last Chunk (%ld bytes) of large object (%ld total) sent now - retaining it in case of implicit suspend",
+ (long)itemlen,
+ (long)fChunkedItemSize
+ ));
+ #endif
+ if (itemlen>0)
+ saveAsPartialItem(itemP->item);
+ }
+ // next
+ itemP=itemP->next;
+ }
+ // we don't need the data any more, but we should keep the source and target IDs until we have the status
+ // - get rid of data
+ if (fSyncOpElementP->itemList) {
+ // - free eventual extra items (shouldn't be any - we always send single item per command)
+ smlFreeItemList(fSyncOpElementP->itemList->next);
+ fSyncOpElementP->itemList->next=NULL;
+ // - free data and meta part of this item, but not target and source info
+ if (fSyncOpElementP->itemList->item) {
+ smlFreePcdata(fSyncOpElementP->itemList->item->meta);
+ fSyncOpElementP->itemList->item->meta=NULL;
+ smlFreePcdata(fSyncOpElementP->itemList->item->data);
+ fSyncOpElementP->itemList->item->data=NULL;
+ }
+ }
+ }
+ else
+ return err==SML_ERR_OK; // ok if evaluation had no error
+ }
+ else {
+ DEBUGPRINTFX(DBG_ERROR,("*** Tried to issue NULL status"));
+ }
+ // sync op commands do need status
+ return true; // queue for status
+} // TSyncOpCommand::issue
+
+
+void TSyncOpCommand::FreeSmlElement(void)
+{
+ // remove SyncML toolkit element(s)
+ FREEPROTOELEMENT(fSyncOpElementP);
+} // TSyncOpCommand::FreeSmlElement
+
+
+TSyncOpCommand::~TSyncOpCommand()
+{
+ // free command elements, if any (use explicit invocation as this is a destructor)
+ TSyncOpCommand::FreeSmlElement();
+} // TSyncOpCommand::~TSyncOpCommand
+
+
+/* end of TSyncOpCommand implementation */
+
+
+#endif // SYNCCOMMAND_PART1_EXCLUDE
+#ifndef SYNCCOMMAND_PART2_EXCLUDE
+
+/*
+ * Implementation of TMapCommand
+ */
+
+
+// constructor for receiving Map command
+TMapCommand::TMapCommand(
+ TSyncSession *aSessionP, // associated session (for callbacks)
+ uInt32 aMsgID, // the Message ID of the command
+ SmlMapPtr_t aMapElementP // associated syncml protocol element
+) :
+ TSmlCommand(scmd_map,false,aSessionP,aMsgID)
+{
+ // save element
+ fMapElementP = aMapElementP;
+ fInProgress=false; // just in case...
+ // no params
+ fLocalDataStoreP=NULL;
+ fRemoteDataStoreP=NULL;
+} // TMapCommand::TMapCommand
+
+
+#ifndef SYSYNC_CLIENT
+// Server only receives Maps
+
+// analyze command (but do not yet execute)
+bool TMapCommand::analyze(TPackageStates aPackageState)
+{
+ TSmlCommand::analyze(aPackageState);
+ // get Command ID and flags
+ if (fMapElementP) {
+ StartProcessing(fMapElementP->cmdID,0);
+ return true;
+ }
+ else return false; // no proto element, bad command
+} // TMapCommand::analyze
+
+
+// execute command (perform real actions, generate status)
+// returns true if command has executed and can be deleted
+bool TMapCommand::execute(void)
+{
+ TStatusCommand *statusCmdP=NULL;
+ bool queueforlater;
+
+ SYSYNC_TRY {
+ // prepare a status
+ statusCmdP = newStatusCommand(200);
+ // let session actually process the command
+ queueforlater=false;
+ fSessionP->processMapCommand(fMapElementP,*statusCmdP,queueforlater);
+ // check if done or queued for later
+ if (queueforlater) {
+ delete statusCmdP;
+ }
+ else {
+ // now issue status, if any
+ if (statusCmdP) {
+ statusCmdP->addTargetRef(smlSrcTargLocURIToCharP(fMapElementP->target)); // add target ref
+ statusCmdP->addSourceRef(smlSrcTargLocURIToCharP(fMapElementP->source)); // add source ref
+ #ifdef MAP_STATUS_IMMEDIATE
+ // issue status right now (might cause map statuses
+ // returned in sync-updates-to-client package #4 instead of map-confirm #6
+ ISSUE_COMMAND_ROOT(fSessionP,statusCmdP);
+ #else
+ // issue status only if outgoing package is map-response
+ fSessionP->issueNotBeforePackage(psta_map,statusCmdP);
+ #endif
+ }
+ // free this one in advance
+ FreeSmlElement();
+ }
+ }
+ SYSYNC_CATCH (...)
+ // make sure owned objects in local scope are deleted
+ if (statusCmdP) delete statusCmdP;
+ // re-throw
+ SYSYNC_RETHROW;
+ SYSYNC_ENDCATCH
+ // return true if command has fully executed
+ return !queueforlater;
+} // TMapCommand::execute
+
+
+#else
+// Client only sends maps
+
+// constructor for sending MAP Command
+TMapCommand::TMapCommand(
+ TSyncSession *aSessionP, // associated session (for callbacks)
+ TLocalEngineDS *aLocalDataStoreP, // local datastore
+ TRemoteDataStore *aRemoteDataStoreP // remote datastore
+) :
+ TSmlCommand(scmd_map,true,aSessionP)
+{
+ // save params
+ fLocalDataStoreP=aLocalDataStoreP;
+ fRemoteDataStoreP=aRemoteDataStoreP;
+ // create new internal map element
+ fMapElementP=NULL; // none yet
+ generateEmptyMapElement();
+ // not yet in progress (as not yet issued)
+ fInProgress=false;
+} // TMapCommand::TMapCommand
+
+
+// generate empty map element
+void TMapCommand::generateEmptyMapElement(void)
+{
+ // free eventually still existing map element
+ if (fMapElementP) FreeSmlElement();
+ // create internal map element
+ fMapElementP = SML_NEW(SmlMap_t);
+ // set proto element type to make it auto-disposable
+ fMapElementP->elementType=SML_PE_MAP;
+ // Cmd ID is now empty (will be set when issued)
+ fMapElementP->cmdID=NULL;
+ // set source and target
+ fMapElementP->target=newLocation(fRemoteDataStoreP->getFullName()); // remote is target for Map command
+ fMapElementP->source=newLocation(fLocalDataStoreP->getRemoteViewOfLocalURI()); // local is source of Map command
+ // no optional elements for now
+ fMapElementP->cred=NULL; // %%% no database level auth yet at all
+ fMapElementP->meta=NULL; // %%% no meta for now
+ // map itemlist is empty
+ fMapElementP->mapItemList=NULL;
+ #ifndef USE_SML_EVALUATION
+ // no item sizes
+ fItemSizes=0;
+ #endif
+} // TMapCommand::generateEmptyMapElement
+
+
+// returns true if command must be put to the waiting-for-status queue.
+// If false, command can be deleted (except if it is not finished() )
+bool TMapCommand::issue(
+ uInt32 aAsCmdID, // command ID to be used
+ uInt32 aInMsgID, // message ID in which command is being issued
+ bool aNoResp
+)
+{
+ // prepare basic stuff
+ TSmlCommand::issue(aAsCmdID,aInMsgID,aNoResp);
+ // now issue
+ if (fMapElementP) {
+ // add as many Map items as possible, update fInProgress
+ // BUT DONT DO THAT IN EVAL MODE (would be recursive, as generateMapItems()
+ // will call evalIssue() which will call this routine....)
+ if (!fEvalMode) generateMapItems();
+ // issue command, but only if there are any items at all
+ if (fMapElementP->mapItemList) {
+ // now issue map command
+ PrepareIssue(&fMapElementP->cmdID);
+ if (!fEvalMode) {
+ #ifdef SYDEBUG
+ if (fSessionP->fXMLtranslate && fSessionP->fOutgoingXMLInstance)
+ smlMapCmd(fSessionP->fOutgoingXMLInstance,fMapElementP);
+ #endif
+ Ret_t err;
+ if ((err=smlMapCmd(fSessionP->getSmlWorkspaceID(),fMapElementP))!=SML_ERR_OK) {
+ SYSYNC_THROW(TSmlException("smlMapCmd",err));
+ }
+ FinalizeIssue();
+ // show debug
+ DEBUGPRINTFX(DBG_HOT,(
+ "Generated Map command, Source='%s', Target='%s'",
+ smlSrcTargLocURIToCharP(fMapElementP->source),
+ smlSrcTargLocURIToCharP(fMapElementP->target)
+ ));
+ }
+ else
+ return smlMapCmd(fSessionP->getSmlWorkspaceID(),fMapElementP)==SML_ERR_OK;
+ }
+ else {
+ if (fEvalMode) return true; // evaluated nothing, is ok!
+ DEBUGPRINTFX(DBG_PROTO,("Suppressed generating empty map command"));
+ return false; // do not queue, delete command now
+ }
+ // keep smlElement, as we need it to mark confirmed map items
+ // at handleStatus().
+ }
+ else {
+ DEBUGPRINTFX(DBG_ERROR,("*** Tried to issue NULL sync"));
+ }
+ // return true if command must be queued for status/result response reception
+ return queueForResponse();
+} // TMapCommand::issue
+
+
+
+// - test if completely issued (must be called after issue() and continueIssue())
+bool TMapCommand::finished(void)
+{
+ // not finished as long there are more syncOps to send
+ return (!fInProgress || !fOutgoing);
+} // TMapCommand::finished
+
+
+// - continue issuing command. This is called by session at start of new message
+// when finished() returned false after issue()/continueIssue()
+// (which causes caller of finished() to put command into fInteruptedCommands queue)
+// returns true if command should be queued for status (again?)
+// NOTE: continueIssue must make sure that it gets a new CmdID/MsgID because
+// the command itself is issued multiple times
+bool TMapCommand::continueIssue(bool &aNewIssue)
+{
+ aNewIssue=false; // never issue anew!
+ if (!fInProgress) return false; // done, don't queue for status again
+ // we can generate next map command only after we have received status for this one
+ if (isWaitingForStatus()) return true; // still in progress, still waiting for status
+ // command is in progress, continue with another <Map> command in this message
+ // - create new Map command
+ generateEmptyMapElement();
+ // - issue again with new command ID
+ fCmdID = fSessionP->getNextOutgoingCmdID();
+ fMsgID = fSessionP->getOutgoingMsgID();
+ // new map command must be queued for status again
+ // but only if something was actually issued (map command issue can
+ // be nop if there are no map items present)
+ return issue(fCmdID,fMsgID,fNoResp);
+} // TMapCommand::continueIssue
+
+
+// add as many Map items as possible, update fInProgress
+void TMapCommand::generateMapItems(void)
+{
+ // let datastore add Map items (eventually none)
+ fInProgress = !(
+ fLocalDataStoreP->engGenerateMapItems(this)
+ );
+} // TMapCommand::generateMapItems
+
+
+// add a Map Item to the map command
+void TMapCommand::addMapItem(const char *aLocalID, const char *aRemoteID)
+{
+ // make item
+ SmlMapItemPtr_t mapitemP = SML_NEW(SmlMapItem_t);
+ mapitemP->target=newLocation(aRemoteID);
+ mapitemP->source=newLocation(aLocalID);
+ #ifndef USE_SML_EVALUATION
+ // update size
+ fItemSizes +=
+ ITEMOVERHEADSIZE +
+ strlen(aLocalID) +
+ strlen(aRemoteID);
+ #endif
+ // add item to item list
+ SmlMapItemListPtr_t *mapItemListPP = &(fMapElementP->mapItemList);
+ // find last itemlist pointer
+ while (*mapItemListPP) {
+ mapItemListPP=&((*mapItemListPP)->next);
+ }
+ // aItemListPP now points to a NULL pointer which must be replaced by addr of new ItemList entry
+ *mapItemListPP = SML_NEW(SmlMapItemList_t);
+ (*mapItemListPP)->next=NULL;
+ (*mapItemListPP)->mapItem=mapitemP; // insert new item
+} // TMapCommand::addItem
+
+
+// remove last added map item from the command
+void TMapCommand::deleteLastMapItem(void)
+{
+ // make item
+ #ifndef USE_SML_EVALUATION
+ #error "Not implemented for old non-eval method any more"
+ #endif
+ // kill last item in item list
+ SmlMapItemListPtr_t *mapItemListPP = &(fMapElementP->mapItemList);
+ // find last itemlist pointer pointing to an item
+ while (*mapItemListPP && (*mapItemListPP)->next) {
+ mapItemListPP=&((*mapItemListPP)->next);
+ }
+ // aItemListPP now points to the pointer which points to the last item list element (or is NULL if no items there)
+ if (*mapItemListPP) {
+ // delete rest of list, which consists of one item only
+ smlFreeMapItemList(*mapItemListPP);
+ // NULL link in previous item or beginning of list
+ *mapItemListPP=NULL;
+ }
+} // TMapCommand::deleteLastMapItem
+
+
+
+// handle status received for previously issued command
+// returns true if done, false if command must be kept in the status queue
+bool TMapCommand::handleStatus(TStatusCommand *aStatusCmdP)
+{
+ bool handled=false;
+ if (aStatusCmdP->getStatusCode()==200) {
+ handled=true; // is ok
+ // mark maps confirmed
+ if (fLocalDataStoreP) {
+ // go through all of my items
+ SmlMapItemListPtr_t mapItemListP = fMapElementP->mapItemList;
+ SmlMapItemPtr_t mapItemP;
+ while (mapItemListP) {
+ mapItemP=mapItemListP->mapItem;
+ if (mapItemP) {
+ // call datastore to mark this map for pending (need to be sent again in next session / resume)
+ fLocalDataStoreP->engMarkMapConfirmed(
+ smlSrcTargLocURIToCharP(mapItemP->source), // source for outgoing mapitem is localID
+ smlSrcTargLocURIToCharP(mapItemP->target) // target for outgoing mapitem is remoteID
+ );
+ }
+ mapItemListP=mapItemListP->next;
+ }
+ }
+ }
+ // anyway, we can now get rid of the map items in the command
+ if (fMapElementP) FreeSmlElement();
+ // check if base class must handle it
+ if(!handled) {
+ // let base class handle it
+ handled=TSmlCommand::handleStatus(aStatusCmdP);
+ }
+ return handled;
+} // TMapCommand::handleStatus
+
+#endif // client-only map implementation
+
+
+// Common for Client and Server
+
+#ifndef USE_SML_EVALUATION
+
+// get (approximated) message size required for sending it
+uInt32 TMapCommand::messageSize(void)
+{
+ // return size of command so far
+ return TSmlCommand::messageSize()+fItemSizes;
+} // TMapCommand::messageSize
+
+#endif
+
+
+void TMapCommand::FreeSmlElement(void)
+{
+ // remove SyncML toolkit element(s)
+ FREEPROTOELEMENT(fMapElementP);
+} // TMapCommand::FreeSmlElement
+
+
+TMapCommand::~TMapCommand()
+{
+ // free command elements, if any (use explicit invocation as this is a destructor)
+ TMapCommand::FreeSmlElement();
+} // TMapCommand::TMapCommand
+
+
+/* end of TMapCommand implementation */
+
+/*
+ * Implementation of TGetCommand
+ */
+
+// constructor for receiving command
+TGetCommand::TGetCommand(
+ TSyncSession *aSessionP, // associated session (for callbacks)
+ uInt32 aMsgID, // the Message ID of the command
+ SmlGetPtr_t aGetElementP // associated syncml protocol element
+) :
+ TSmlCommand(scmd_get,false,aSessionP,aMsgID)
+{
+ // save element
+ fGetElementP = aGetElementP;
+} // TGetCommand::TGetCommand
+
+
+// constructor for sending command
+TGetCommand::TGetCommand(
+ TSyncSession *aSessionP // associated session (for callbacks)
+) :
+ TSmlCommand(scmd_get,true,aSessionP)
+{
+ // create internal get element
+ fGetElementP = SML_NEW(SmlGet_t);
+ // set proto element type to make it auto-disposable
+ fGetElementP->elementType=SML_PE_GET;
+ // Cmd ID is now empty (will be set when issued)
+ fGetElementP->cmdID=NULL;
+ // default to no flags (noResp is set at issue, if at all)
+ fGetElementP->flags=0;
+ // no optional elements for now
+ fGetElementP->cred=NULL;
+ fGetElementP->lang=NULL;
+ fGetElementP->meta=NULL;
+ fGetElementP->itemList=NULL;
+} // TGetCommand::TGetCommand
+
+
+// set Meta of the get command
+void TGetCommand::setMeta(
+ SmlPcdataPtr_t aMetaP // existing meta data structure, ownership is passed to Get
+)
+{
+ if (fGetElementP)
+ fGetElementP->meta=aMetaP;
+} // TGetCommand::setMeta
+
+
+// add an Item to the get command
+void TGetCommand::addItem(
+ SmlItemPtr_t aItemP // existing item data structure, ownership is passed to Get
+)
+{
+ if (fGetElementP)
+ addItemToList(aItemP,&(fGetElementP->itemList));
+} // TGetCommand::addItem
+
+
+// add a target specification Item to the get command
+void TGetCommand::addTargetLocItem(
+ const char *aTargetURI,
+ const char *aTargetName
+)
+{
+ SmlItemPtr_t itemP = newItem();
+
+ itemP->target = newLocation(aTargetURI,aTargetName);
+ addItem(itemP);
+} // TGetCommand::addTargetLocItem
+
+
+
+// analyze command (but do not yet execute)
+bool TGetCommand::analyze(TPackageStates aPackageState)
+{
+ TSmlCommand::analyze(aPackageState);
+ // get Command ID and flags
+ if (fGetElementP) {
+ StartProcessing(fGetElementP->cmdID,fGetElementP->flags);
+ return true;
+ }
+ else return false; // no proto element, bad command
+} // TGetCommand::analyze
+
+
+// execute command (perform real actions, generate status)
+// returns true if command has executed and can be deleted
+bool TGetCommand::execute(void)
+{
+ TStatusCommand *statusCmdP=NULL;
+ TResultsCommand *resultsCmdP=NULL;
+ SmlItemListPtr_t thisitemnode,nextitemnode;
+
+ SYSYNC_TRY {
+ // process items
+ thisitemnode=fGetElementP->itemList;
+ while (thisitemnode) {
+ nextitemnode = thisitemnode->next;
+ // no result nor status so far
+ statusCmdP=NULL;
+ resultsCmdP=NULL;
+ // get Target LocURI
+ if (!thisitemnode->item) SYSYNC_THROW(TSyncException("Get with NULL item"));
+ string locURI=smlSrcTargLocURIToCharP(thisitemnode->item->target);
+ DEBUGPRINTFX(DBG_HOT,("processing item with locURI=%s",locURI.c_str()));
+ // let session (and descendants) process it
+ // - default to "not found" status
+ statusCmdP = newStatusCommand(404);
+ resultsCmdP=fSessionP->processGetItem(locURI.c_str(),this,thisitemnode->item,*statusCmdP);
+ // now complete and issue status
+ statusCmdP->addTargetRef(locURI.c_str()); // add target ref
+ ISSUE_COMMAND_ROOT(fSessionP,statusCmdP);
+ // issue results, if any
+ if (resultsCmdP) {
+ ISSUE_COMMAND_ROOT(fSessionP,resultsCmdP);
+ }
+ // next item
+ thisitemnode=nextitemnode;
+ }
+ // free this one in advance
+ FreeSmlElement();
+ }
+ SYSYNC_CATCH (...)
+ // make sure owned objects in local scope are deleted
+ if (statusCmdP) delete statusCmdP;
+ if (resultsCmdP) delete resultsCmdP;
+ // re-throw
+ SYSYNC_RETHROW;
+ SYSYNC_ENDCATCH
+ // done with command, delete it now: return true
+ return true;
+} // TGetCommand::execute
+
+
+// returns true if command must be put to the waiting-for-status queue.
+// If false, command can be deleted
+bool TGetCommand::issue(
+ uInt32 aAsCmdID, // command ID to be used
+ uInt32 aInMsgID, // message ID in which command is being issued
+ bool aNoResp
+)
+{
+ // prepare basic stuff
+ TSmlCommand::issue(aAsCmdID,aInMsgID,aNoResp);
+ // now issue
+ if (fGetElementP) {
+ // issue command with SyncML toolkit
+ PrepareIssue(&fGetElementP->cmdID,&fGetElementP->flags);
+ if (!fEvalMode) {
+ #ifdef SYDEBUG
+ if (fSessionP->fXMLtranslate && fSessionP->fOutgoingXMLInstance)
+ smlGetCmd(fSessionP->fOutgoingXMLInstance,fGetElementP);
+ #endif
+ Ret_t err;
+ if ((err=smlGetCmd(fSessionP->getSmlWorkspaceID(),fGetElementP))!=SML_ERR_OK) {
+ SYSYNC_THROW(TSmlException("smlGetCmd",err));
+ }
+ FinalizeIssue();
+ // we don't need the status structure any more, free (and NULL ptr) now
+ FreeSmlElement();
+ }
+ else
+ return smlGetCmd(fSessionP->getSmlWorkspaceID(),fGetElementP)==SML_ERR_OK;
+ }
+ else {
+ DEBUGPRINTFX(DBG_ERROR,("*** Tried to issue NULL get"));
+ }
+ // return true if command must be queued for status/result response reception
+ return queueForResponse();
+} // TGetCommand::issue
+
+
+// handle status received for previously issued command
+// returns true if done, false if command must be kept in the status queue
+bool TGetCommand::handleStatus(TStatusCommand *aStatusCmdP)
+{
+ // base class just handles common cases
+ TSyError statuscode = aStatusCmdP->getStatusCode();
+ if (statuscode>=500) {
+ // %%% for SCTS, which rejects GET with status 500 in some cases
+ POBJDEBUGPRINTFX(fSessionP,DBG_ERROR,("Status: %hd: 5xx for get is passed w/o error",statuscode));
+ return true;
+ }
+ // let ancestor analyze
+ return TSmlCommand::handleStatus(aStatusCmdP);
+} // TGetCommand::handleStatus
+
+
+
+
+void TGetCommand::FreeSmlElement(void)
+{
+ // remove SyncML toolkit element(s)
+ FREEPROTOELEMENT(fGetElementP);
+} // TGetCommand::FreeSmlElement
+
+
+TGetCommand::~TGetCommand()
+{
+ // free command elements, if any (use explicit invocation as this is a destructor)
+ TGetCommand::FreeSmlElement();
+} // TGetCommand::~TGetCommand
+
+
+/* end of TGetCommand implementation */
+
+/*
+ * Implementation of TPutCommand
+ */
+
+
+// constructor for receiving command
+TPutCommand::TPutCommand(
+ TSyncSession *aSessionP, // associated session (for callbacks)
+ uInt32 aMsgID, // the Message ID of the command
+ SmlGetPtr_t aPutElementP // associated syncml protocol element
+) :
+ TSmlCommand(scmd_put,false,aSessionP,aMsgID)
+{
+ // save element
+ fPutElementP = aPutElementP;
+} // TPutCommand::TPutCommand
+
+
+// constructor for sending command
+TPutCommand::TPutCommand(
+ TSyncSession *aSessionP // associated session (for callbacks)
+) :
+ TSmlCommand(scmd_put,true,aSessionP)
+{
+ // create internal get element
+ fPutElementP = SML_NEW(SmlPut_t);
+ // set proto element type to make it auto-disposable
+ fPutElementP->elementType=SML_PE_PUT;
+ // Cmd ID is now empty (will be set when issued)
+ fPutElementP->cmdID=NULL;
+ // default to no flags (noResp is set at issue, if at all)
+ fPutElementP->flags=0;
+ // no optional elements for now
+ fPutElementP->cred=NULL;
+ fPutElementP->lang=NULL;
+ fPutElementP->meta=NULL;
+ fPutElementP->itemList=NULL;
+} // TPutCommand::TPutCommand
+
+
+// add Meta to the get command
+void TPutCommand::setMeta(
+ SmlPcdataPtr_t aMetaP // existing meta data structure, ownership is passed to Get
+)
+{
+ if (fPutElementP)
+ fPutElementP->meta=aMetaP;
+} // TPutCommand::setMeta
+
+
+// add an Item to the get command
+void TPutCommand::addItem(
+ SmlItemPtr_t aItemP // existing item data structure, ownership is passed to Get
+)
+{
+ if (fPutElementP)
+ addItemToList(aItemP,&(fPutElementP->itemList));
+} // TPutCommand::addItem
+
+
+// add a source specification Item to the put command and return a pointer
+// to add data to it
+SmlItemPtr_t TPutCommand::addSourceLocItem(
+ const char *aTargetURI,
+ const char *aTargetName
+)
+{
+ SmlItemPtr_t itemP = newItem();
+
+ itemP->source = newLocation(aTargetURI,aTargetName);
+ addItem(itemP);
+ return itemP;
+} // TPutCommand::addSourceLocItem
+
+
+// analyze command (but do not yet execute)
+bool TPutCommand::analyze(TPackageStates aPackageState)
+{
+ TSmlCommand::analyze(aPackageState);
+ // get Command ID and flags
+ if (fPutElementP) {
+ StartProcessing(fPutElementP->cmdID,fPutElementP->flags);
+ return true;
+ }
+ else return false; // no proto element, bad command
+} // TPutCommand::analyze
+
+
+// execute command (perform real actions, generate status)
+// returns true if command has executed and can be deleted
+bool TPutCommand::execute(void)
+{
+ TStatusCommand *statusCmdP=NULL;
+ SmlItemListPtr_t thisitemnode,nextitemnode;
+
+ SYSYNC_TRY {
+ // process items
+ thisitemnode=fPutElementP->itemList;
+ while (thisitemnode) {
+ nextitemnode = thisitemnode->next;
+ // no result nor status so far
+ statusCmdP=NULL;
+ if (!thisitemnode->item) SYSYNC_THROW(TSyncException("Put with NULL item"));
+ // get Source LocURI
+ string locURI=smlSrcTargLocURIToCharP(thisitemnode->item->source);
+ DEBUGPRINTFX(DBG_HOT,("processing item with locURI=%s",locURI.c_str()));
+ // let session (and descendants) process it
+ // - default to not allowed to put status
+ statusCmdP = newStatusCommand(403);
+ fSessionP->processPutResultItem(true,locURI.c_str(),this,thisitemnode->item,*statusCmdP);
+ // now complete and issue status
+ statusCmdP->addSourceRef(locURI.c_str()); // add source ref
+ ISSUE_COMMAND_ROOT(fSessionP,statusCmdP);
+ // next item
+ thisitemnode=nextitemnode;
+ }
+ // free element
+ FreeSmlElement();
+ }
+ SYSYNC_CATCH (...)
+ // make sure owned objects in local scope are deleted
+ if (statusCmdP) delete statusCmdP;
+ // re-throw
+ SYSYNC_RETHROW;
+ SYSYNC_ENDCATCH
+ // done with command, delete it now: return true
+ return true;
+} // TPutCommand::execute
+
+
+// returns true if command must be put to the waiting-for-status queue.
+// If false, command can be deleted
+bool TPutCommand::issue(
+ uInt32 aAsCmdID, // command ID to be used
+ uInt32 aInMsgID, // message ID in which command is being issued
+ bool aNoResp
+)
+{
+ // prepare basic stuff
+ TSmlCommand::issue(aAsCmdID,aInMsgID,aNoResp);
+ // now issue
+ if (fPutElementP) {
+ // issue command with SyncML toolkit
+ PrepareIssue(&fPutElementP->cmdID,&fPutElementP->flags);
+ if (!fEvalMode) {
+ #ifdef SYDEBUG
+ if (fSessionP->fXMLtranslate && fSessionP->fOutgoingXMLInstance)
+ smlPutCmd(fSessionP->fOutgoingXMLInstance,fPutElementP);
+ #endif
+ Ret_t err;
+ if ((err=smlPutCmd(fSessionP->getSmlWorkspaceID(),fPutElementP))!=SML_ERR_OK) {
+ SYSYNC_THROW(TSmlException("smlPutCmd",err));
+ }
+ FinalizeIssue();
+ // we don't need the status structure any more, free (and NULL ptr) now
+ FreeSmlElement();
+ }
+ else
+ return smlPutCmd(fSessionP->getSmlWorkspaceID(),fPutElementP)==SML_ERR_OK;
+ }
+ else {
+ DEBUGPRINTFX(DBG_ERROR,("*** Tried to issue NULL put"));
+ }
+ // return true if command must be queued for status/result response reception
+ return queueForResponse();
+} // TPutCommand::issue
+
+
+void TPutCommand::FreeSmlElement(void)
+{
+ // remove SyncML toolkit element(s)
+ FREEPROTOELEMENT(fPutElementP);
+} // TPutCommand::FreeSmlElement
+
+
+TPutCommand::~TPutCommand()
+{
+ // free command elements, if any (use explicit invocation as this is a destructor)
+ TPutCommand::FreeSmlElement();
+} // TPutCommand::~TPutCommand
+
+
+/* end of TPutCommand implementation */
+
+
+/*
+ * Implementation of TStatusCommand
+ */
+
+
+// constructor for generating status
+TStatusCommand::TStatusCommand(
+ TSyncSession *aSessionP, // associated session (for callbacks)
+ TSmlCommand *aCommandP, // command this status refers to
+ TSyError aStatusCode // status code
+) :
+ TSmlCommand(scmd_status,true,aSessionP)
+{
+ // save status code
+ fStatusCode=aStatusCode;
+ // get reference info
+ if (aCommandP) {
+ // referring to a command
+ fRefMsgID=aCommandP->getMsgID();
+ fRefCmdID=aCommandP->getCmdID();
+ fRefCmdType=aCommandP->getCmdType();
+ // - set flag to suppress sending this status when NoResp is set
+ fDontSend= aCommandP->getNoResp() || fSessionP->getMsgNoResp();
+ }
+ else {
+ // referring to SyncHdr of current message
+ fRefMsgID=fSessionP->fIncomingMsgID;
+ fRefCmdID=0; // "command" ID of SyncHdr is 0
+ fRefCmdType=scmd_synchdr; // is SyncHdr
+ // - set flag to suppress sending this status when NoResp is set
+ fDontSend=fSessionP->getMsgNoResp();
+ }
+ // create internal status element
+ fStatusElementP = SML_NEW(SmlStatus_t);
+ // set proto element type to make it auto-disposable
+ fStatusElementP->elementType=SML_PE_STATUS;
+ // Cmd ID is now empty (will be set at Issue to ensure correct sequential ordering in case this status is not immediately sent)
+ fStatusElementP->cmdID=NULL;
+ // status refers to message of specified command
+ fStatusElementP->msgRef=newPCDataLong(fRefMsgID);
+ // status refers to ID of specified command
+ fStatusElementP->cmdRef=newPCDataLong(fRefCmdID);
+ // add name
+ fStatusElementP->cmd=newPCDataString(getNameOf(fRefCmdType));
+ // no status code yet (will be added at issue())
+ fStatusElementP->data=NULL;
+ // no optional elements for now
+ fStatusElementP->targetRefList=NULL;
+ fStatusElementP->sourceRefList=NULL;
+ fStatusElementP->cred=NULL;
+ fStatusElementP->chal=NULL;
+ fStatusElementP->itemList=NULL;
+} // TStatusCommand::TStatusCommand
+
+
+// %%%% (later obsolete) constructor for generating status w/o having a command object
+TStatusCommand::TStatusCommand(
+ TSyncSession *aSessionP, // associated session (for callbacks)
+ uInt32 aRefCmdID, // referred-to command ID
+ TSmlCommandTypes aRefCmdType, // referred-to command type (scmd_xxx)
+ bool aNoResp, // set if no-Resp
+ TSyError aStatusCode // status code
+) :
+ TSmlCommand(scmd_status,true,aSessionP)
+{
+ // save status code
+ fStatusCode=aStatusCode;
+ // get reference info
+ fRefMsgID=fSessionP->fIncomingMsgID;
+ fRefCmdID=aRefCmdID;
+ fRefCmdType=aRefCmdType;
+ // - set flag to suppress sending this status when NoResp is set
+ fDontSend=aNoResp;
+ // create internal status element
+ fStatusElementP = SML_NEW(SmlStatus_t);
+ // set proto element type to make it auto-disposable
+ fStatusElementP->elementType=SML_PE_STATUS;
+ // Cmd ID is now empty (will be set at Issue to ensure correct sequential ordering in case this status is not immediately sent)
+ fStatusElementP->cmdID=NULL;
+ // status refers to message of specified command
+ fStatusElementP->msgRef=newPCDataLong(fRefMsgID);
+ // status refers to ID of specified command
+ fStatusElementP->cmdRef=newPCDataLong(fRefCmdID);
+ // add name
+ fStatusElementP->cmd=newPCDataString(getNameOf(fRefCmdType));
+ // no status code yet (will be added at issue())
+ fStatusElementP->data=NULL;
+ // no optional elements for now
+ fStatusElementP->targetRefList=NULL;
+ fStatusElementP->sourceRefList=NULL;
+ fStatusElementP->cred=NULL;
+ fStatusElementP->chal=NULL;
+ fStatusElementP->itemList=NULL;
+} // TStatusCommand::TStatusCommand
+
+
+// constructor for receiving status
+TStatusCommand::TStatusCommand(
+ TSyncSession *aSessionP, // associated session (for callbacks)
+ uInt32 aMsgID, // the Message ID of the command
+ SmlStatusPtr_t aStatusElementP // associated STATUS command content element
+) :
+ TSmlCommand(scmd_status,false,aSessionP,aMsgID)
+{
+ // save element
+ fStatusElementP = aStatusElementP;
+} // TStatusCommand::TStatusCommand
+
+
+// analyze status
+bool TStatusCommand::analyze(TPackageStates aPackageState)
+{
+ TSmlCommand::analyze(aPackageState);
+ // get Command ID and flags
+ if (fStatusElementP) {
+ StartProcessing(fStatusElementP->cmdID,0);
+ // get status code
+ sInt32 temp;
+ if (smlPCDataToLong(fStatusElementP->data,temp)) fStatusCode=temp;
+ else {
+ PDEBUGPRINTFX(DBG_ERROR,("Malformed status code: '%s'",smlPCDataToCharP(fStatusElementP->data)));
+ return false; // bad status
+ }
+ // get message reference
+ if (!fStatusElementP->msgRef) {
+ // no MsgRef -> must assume 1 (according to SyncML specs)
+ fRefMsgID=1;
+ DEBUGPRINTFX(DBG_PROTO,("No MsgRef in Status, assumed 1"));
+ }
+ else if (!smlPCDataToULong(fStatusElementP->msgRef,fRefMsgID)) {
+ PDEBUGPRINTFX(DBG_ERROR,("Malformed message reference: '%s'",smlPCDataToCharP(fStatusElementP->msgRef)));
+ return false; // bad status
+ }
+ // get command reference
+ if (!smlPCDataToULong(fStatusElementP->cmdRef,fRefCmdID)) {
+ PDEBUGPRINTFX(DBG_ERROR,("Malformed command reference: '%s'",smlPCDataToCharP(fStatusElementP->msgRef)));
+ return false; // bad status
+ }
+ #ifdef SYDEBUG
+ // warn if error (don't treat slow sync status or conflict indication as errors)
+ if (fStatusCode>=300 && fStatusCode!=508 && fStatusCode!=419) {
+ PDEBUGPRINTFX(DBG_ERROR,(
+ "WARNING: RECEIVED NON-OK STATUS %hd for &html;<a name=\"SO_%ld_%ld\" href=\"#IO_%ld_%ld\">&html;command '%s'&html;</a>&html; (outgoing MsgID=%ld, CmdID=%ld)",
+ fStatusCode,
+ (long)fRefMsgID,
+ (long)fRefCmdID,
+ (long)fRefMsgID,
+ (long)fRefCmdID,
+ smlPCDataToCharP(fStatusElementP->cmd),
+ (long)fRefMsgID,
+ (long)fRefCmdID
+ ));
+ }
+ else {
+ // show what we have received
+ PDEBUGPRINTFX(DBG_HOT,(
+ "RECEIVED STATUS %hd for for &html;<a name=\"SO_%ld_%ld\" href=\"#IO_%ld_%ld\">&html;command '%s'&html;</a>&html; (outgoing MsgID=%ld, CmdID=%ld)",
+ fStatusCode,
+ (long)fRefMsgID,
+ (long)fRefCmdID,
+ (long)fRefMsgID,
+ (long)fRefCmdID,
+ smlPCDataToCharP(fStatusElementP->cmd),
+ (long)fRefMsgID,
+ (long)fRefCmdID
+ ));
+ }
+ // - source and target refs
+ if (PDEBUGMASK & DBG_HOT) {
+ SmlTargetRefListPtr_t targetrefP = fStatusElementP->targetRefList;
+ while (targetrefP) {
+ // target ref available
+ PDEBUGPRINTFX(DBG_HOT,("- TargetRef (remoteID) = '%s'",smlPCDataToCharP(targetrefP->targetRef)));
+ // next
+ targetrefP=targetrefP->next;
+ }
+ SmlSourceRefListPtr_t sourcerefP = fStatusElementP->sourceRefList;
+ while (sourcerefP) {
+ // target ref available
+ PDEBUGPRINTFX(DBG_HOT,("- SourceRef (localID) = '%s'",smlPCDataToCharP(sourcerefP->sourceRef)));
+ // next
+ sourcerefP=sourcerefP->next;
+ }
+ }
+ // - optional items
+ if (PDEBUGMASK & DBG_ERROR) {
+ SmlItemListPtr_t itemlistP = fStatusElementP->itemList;
+ while (itemlistP) {
+ // item available
+ PDEBUGPRINTFX(DBG_HOT,("- Item data = %s",smlItemDataToCharP(itemlistP->item)));
+ // next
+ itemlistP=itemlistP->next;
+ }
+ }
+ #endif
+ return true; // good status
+ }
+ else return false; // no proto element, bad command
+} // TStatusCommand::analyze
+
+
+
+// returns true if command must be put to the waiting-for-status queue.
+// If false, command can be deleted
+bool TStatusCommand::issue(
+ uInt32 aAsCmdID, // command ID to be used
+ uInt32 aInMsgID, // message ID in which command is being issued
+ bool aNoResp // dummy here, because Status has always no response
+)
+{
+ // now issue
+ if (fStatusElementP) {
+ // prepare basic stuff
+ TSmlCommand::issue(aAsCmdID,aInMsgID,true);
+ // set status code in structure (but only once, in case we are evaluating before issuing)
+ if (fStatusElementP->data==NULL) fStatusElementP->data=newPCDataLong(fStatusCode);
+ // Status is meant to have a CmdID. SyncML 1.0 docs say no, but this was corrected in 1.0.1
+ // issue command with SyncML toolkit (no flags)
+ PrepareIssue(&fStatusElementP->cmdID,NULL); // CmdID (SyncML 1.0.1 conformant)
+ if (!fEvalMode) {
+ // PrepareIssue(NULL,NULL); // no CmdID, no flags
+ #ifdef SYDEBUG
+ if (fSessionP->fXMLtranslate && fSessionP->fOutgoingXMLInstance)
+ smlStatusCmd(fSessionP->fOutgoingXMLInstance,fStatusElementP);
+ #endif
+ Ret_t err;
+ if ((err=smlStatusCmd(fSessionP->getSmlWorkspaceID(),fStatusElementP))!=SML_ERR_OK) {
+ SYSYNC_THROW(TSmlException("smlStatusCmd",err));
+ }
+ FinalizeIssue();
+ // show debug
+ #ifdef SYDEBUG
+ // - warning for non-ok (don't treat slow sync status as error)
+ if (fStatusCode>=300 && fStatusCode!=508) {
+ PDEBUGPRINTFX(DBG_ERROR,("WARNING: Non-OK Status %hd returned to remote!",fStatusCode));
+ }
+ // - what was issued
+ PDEBUGPRINTFX(DBG_HOT,("Status Code %s issued for Cmd=%s, (incoming MsgID=%s, CmdID=%s)",
+ smlPCDataToCharP(fStatusElementP->data),
+ smlPCDataToCharP(fStatusElementP->cmd),
+ smlPCDataToCharP(fStatusElementP->msgRef),
+ smlPCDataToCharP(fStatusElementP->cmdRef)
+ ));
+ // - source and target refs
+ if (PDEBUGTEST(DBG_HOT)) {
+ SmlTargetRefListPtr_t targetrefP = fStatusElementP->targetRefList;
+ while (targetrefP) {
+ // target ref available
+ PDEBUGPRINTFX(DBG_HOT,("- TargetRef (localID) = '%s'",smlPCDataToCharP(targetrefP->targetRef)))
+ // next
+ targetrefP=targetrefP->next;
+ }
+ SmlSourceRefListPtr_t sourcerefP = fStatusElementP->sourceRefList;
+ while (sourcerefP) {
+ // target ref available
+ PDEBUGPRINTFX(DBG_HOT,("- SourceRef (remoteID) = '%s'",smlPCDataToCharP(sourcerefP->sourceRef)))
+ // next
+ sourcerefP=sourcerefP->next;
+ }
+ }
+ #endif
+ // we don't need the status structure any more, free (and NULL ptr) now
+ FreeSmlElement();
+ }
+ else
+ return smlStatusCmd(fSessionP->getSmlWorkspaceID(),fStatusElementP)==SML_ERR_OK;
+ }
+ else {
+ DEBUGPRINTFX(DBG_ERROR,("*** Tried to issue NULL status"));
+ }
+ // status does NOT await any answer, and can be deleted after issuing
+ return false; // don't queue for status
+} // TStatusCommand::issue
+
+
+// add a target Ref to the status (no op if ref is NULL)
+void TStatusCommand::addTargetRef(
+ const char *aTargetRef // Target LocURI of an item this status applies to
+)
+{
+ SmlTargetRefListPtr_t *targetreflistPP;
+ if (fStatusElementP && aTargetRef && *aTargetRef) {
+ // Note: empty refs will not be added
+ targetreflistPP=&(fStatusElementP->targetRefList);
+ // find last targetreflist pointer
+ while (*targetreflistPP) {
+ targetreflistPP=&((*targetreflistPP)->next);
+ }
+ // targetreflistPP now points to a NULL pointer which must be replaced by addr of new targetreflist entry
+ *targetreflistPP = SML_NEW(SmlTargetRefList_t);
+ (*targetreflistPP)->next=NULL;
+ (*targetreflistPP)->targetRef=
+ newPCDataString(aTargetRef);
+ }
+} // TStatusCommand::addTargetRef
+
+
+// add a source Ref to the status (no op if ref is NULL)
+void TStatusCommand::addSourceRef(
+ const char *aSourceRef // Target LocURI of an item this status applies to
+)
+{
+ SmlSourceRefListPtr_t *sourcereflistPP;
+ if (fStatusElementP && aSourceRef && *aSourceRef) {
+ // Note: empty refs will not be added
+ sourcereflistPP=&(fStatusElementP->sourceRefList);
+ // find last sourcereflist pointer
+ while (*sourcereflistPP) {
+ sourcereflistPP=&((*sourcereflistPP)->next);
+ }
+ // sourcereflistPP now points to a NULL pointer which must be replaced by addr of new sourcereflist entry
+ *sourcereflistPP = SML_NEW(SmlSourceRefList_t);
+ (*sourcereflistPP)->next=NULL;
+ (*sourcereflistPP)->sourceRef=
+ newPCDataString(aSourceRef);
+ }
+} // TStatusCommand::addSourceRef
+
+
+// add an Error code string Item to the status
+void TStatusCommand::addErrorCodeString(
+ uInt32 aErrCode,
+ const char *aText // optional descriptive text
+)
+{
+ string msg;
+ if (!aText) aText="Err";
+ StringObjPrintf(msg,"%s = %ld",aText,(long)aErrCode);
+ if (fStatusElementP) {
+ addItem(newStringDataItem(msg.c_str()));
+ }
+} // TStatusCommand::addItemString
+
+
+// add a String Item to the status
+void TStatusCommand::addItemString(
+ const char *aItemString // item string to be added
+)
+{
+ if (fStatusElementP && aItemString) {
+ addItem(newStringDataItem(aItemString));
+ }
+} // TStatusCommand::addItemString
+
+
+// add an Item to the status
+void TStatusCommand::addItem(
+ SmlItemPtr_t aItemP // existing item data structure, ownership is passed to Status
+)
+{
+ if (fStatusElementP)
+ addItemToList(aItemP,&(fStatusElementP->itemList));
+} // TStatusCommand::addItem
+
+
+// move items from another status to this one (passes ownership of items and deletes them from original)
+void TStatusCommand::moveItemsFrom(TStatusCommand *aStatusCommandP)
+{
+ if (fStatusElementP && aStatusCommandP) {
+ // pass item list to new owner
+ fStatusElementP->itemList = aStatusCommandP->fStatusElementP->itemList;
+ // remove items from original
+ aStatusCommandP->fStatusElementP->itemList = NULL;
+ }
+} // TStatusCommand::moveItemsFrom
+
+
+
+// add Challenge to status
+// challenge data structure ownership is passed to Status
+void TStatusCommand::setChallenge(SmlChalPtr_t aChallengeP)
+{
+ if (fStatusElementP) {
+ if (fStatusElementP->chal) {
+ // get rid of old challenge
+ smlFreeChalPtr(fStatusElementP->chal);
+ fStatusElementP->chal=NULL;
+ }
+ // set new
+ fStatusElementP->chal=aChallengeP;
+ }
+} // TStatusCommand::setChallenge
+
+
+// set Status code
+void TStatusCommand::setStatusCode(TSyError aStatusCode)
+{
+ fStatusCode = aStatusCode;
+} // TStatusCommand::setStatusCode
+
+
+void TStatusCommand::FreeSmlElement(void)
+{
+ // remove SyncML toolkit element(s)
+ FREEPROTOELEMENT(fStatusElementP);
+} // TResultsCommand::FreeSmlElement
+
+
+TStatusCommand::~TStatusCommand()
+{
+ // free command elements, if any (use explicit invocation as this is a destructor)
+ TStatusCommand::FreeSmlElement();
+} // TStatusCommand::TStatusCommand
+
+
+/* end of TStatusCommand implementation */
+
+
+/*
+ * Implementation of TResultsCommand
+ */
+
+// constructor for receiving command
+TResultsCommand::TResultsCommand(
+ TSyncSession *aSessionP, // associated session (for callbacks)
+ uInt32 aMsgID, // the Message ID of the command
+ SmlResultsPtr_t aResultsElementP // associated syncml protocol element
+) :
+ TSmlCommand(scmd_results,false,aSessionP,aMsgID)
+{
+ // save element
+ fResultsElementP = aResultsElementP;
+} // TResultsCommand::TResultsCommand
+
+
+// constructor for generating results
+TResultsCommand::TResultsCommand(
+ TSyncSession *aSessionP, // associated session (for callbacks)
+ TSmlCommand *aCommandP, // command these results refer to
+ const char *aTargetRef, // target reference
+ const char *aSourceRef // source reference
+) :
+ TSmlCommand(scmd_results,true,aSessionP)
+{
+ // create internal results element
+ fResultsElementP = SML_NEW(SmlResults_t);
+ // set proto element type to make it auto-disposable
+ fResultsElementP->elementType=SML_PE_RESULTS;
+ // Cmd ID is now empty (will be set when issued)
+ fResultsElementP->cmdID=NULL;
+ // results refer to message of specified command
+ fResultsElementP->msgRef=newPCDataLong(aCommandP->getMsgID());
+ // results refer to ID of specified command
+ fResultsElementP->cmdRef=newPCDataLong(aCommandP->getCmdID());
+ // no meta for now
+ fResultsElementP->meta=NULL;
+ // target reference if specified
+ if (aTargetRef) fResultsElementP->targetRef=newPCDataString(aTargetRef);
+ else fResultsElementP->targetRef=NULL;
+ // source reference if specified
+ if (aSourceRef) fResultsElementP->sourceRef=newPCDataString(aSourceRef);
+ else fResultsElementP->sourceRef=NULL;
+ // no items for now
+ fResultsElementP->itemList=NULL;
+} // TResultsCommand::TResultsCommand
+
+
+
+// returns true if command must be put to the waiting-for-status queue.
+// If false, command can be deleted
+bool TResultsCommand::issue(
+ uInt32 aAsCmdID, // command ID to be used
+ uInt32 aInMsgID, // message ID in which command is being issued
+ bool aNoResp // issue without wanting response
+)
+{
+ // prepare
+ TSmlCommand::issue(aAsCmdID,aInMsgID,aNoResp);
+ // now issue
+ if (fResultsElementP) {
+ // issue command with SyncML toolkit (no flags)
+ PrepareIssue(&fResultsElementP->cmdID,NULL);
+ if (!fEvalMode) {
+ #ifdef SYDEBUG
+ if (fSessionP->fXMLtranslate && fSessionP->fOutgoingXMLInstance)
+ smlResultsCmd(fSessionP->fOutgoingXMLInstance,fResultsElementP);
+ #endif
+ Ret_t err;
+ if ((err=smlResultsCmd(fSessionP->getSmlWorkspaceID(),fResultsElementP))!=SML_ERR_OK) {
+ SYSYNC_THROW(TSmlException("smlResultsCmd",err));
+ }
+ FinalizeIssue();
+ // show debug
+ DEBUGPRINTFX(DBG_HOT,("results issued for (incoming MsgID=%s, CmdID=%s)",
+ smlPCDataToCharP(fResultsElementP->msgRef),
+ smlPCDataToCharP(fResultsElementP->cmdRef)
+ ));
+ // we don't need the results structure any more, free (and NULL ptr) now
+ FreeSmlElement();
+ }
+ else
+ return smlResultsCmd(fSessionP->getSmlWorkspaceID(),fResultsElementP)==SML_ERR_OK;
+ }
+ else {
+ DEBUGPRINTFX(DBG_ERROR,("*** Tried to issue NULL result"));
+ }
+ // SyncML 1.0: result does NOT await any answer, and can be deleted after issuing
+ // SyncML 1.0.1: all commands will receive a status (except Status)
+ return true; // queue for status/result
+} // TResultsCommand::issue
+
+
+// add a String Item to the results
+void TResultsCommand::addItemString(
+ const char *aItemString // item string to be added
+)
+{
+ if (fResultsElementP && aItemString) {
+ addItem(newStringDataItem(aItemString));
+ }
+} // TResultsCommand::addItemString
+
+
+// add an Item to the results
+void TResultsCommand::addItem(
+ SmlItemPtr_t aItemP // existing item data structure, ownership is passed to Results
+)
+{
+ if (fResultsElementP)
+ addItemToList(aItemP,&(fResultsElementP->itemList));
+} // TResultsCommand::addItem
+
+
+// set Meta of the results command
+void TResultsCommand::setMeta(
+ SmlPcdataPtr_t aMetaP // existing meta data structure, ownership is passed to Get
+)
+{
+ if (fResultsElementP)
+ fResultsElementP->meta=aMetaP;
+} // TResultsCommand::setMeta
+
+
+
+
+// analyze command (but do not yet execute)
+bool TResultsCommand::analyze(TPackageStates aPackageState)
+{
+ TSmlCommand::analyze(aPackageState);
+ // get Command ID and flags
+ if (fResultsElementP) {
+ StartProcessing(fResultsElementP->cmdID,0);
+ return true;
+ }
+ else return false; // no proto element, bad command
+} // TResultsCommand::analyze
+
+
+// execute command (perform real actions, generate status)
+// returns true if command has executed and can be deleted
+bool TResultsCommand::execute(void)
+{
+ TStatusCommand *statusCmdP=NULL;
+ SmlItemListPtr_t thisitemnode,nextitemnode;
+
+ // process items
+ thisitemnode=fResultsElementP->itemList;
+ while (thisitemnode) {
+ nextitemnode = thisitemnode->next;
+ if (!thisitemnode->item) SYSYNC_THROW(TSyncException("Results with NULL item"));
+ // get Source LocURI
+ string locURI=smlSrcTargLocURIToCharP(thisitemnode->item->source);
+ DEBUGPRINTFX(DBG_HOT,("processing item with locURI=%s",locURI.c_str()));
+ // let session (and descendants) process it
+ // - default to ok status
+ statusCmdP = newStatusCommand(200);
+ fSessionP->processPutResultItem(false,locURI.c_str(),this,thisitemnode->item,*statusCmdP);
+ #ifdef RESULTS_SENDS_STATUS
+ // now complete and issue status
+ statusCmdP->addSourceRef(locURI.c_str()); // add source ref
+ ISSUE_COMMAND_ROOT(fSessionP,statusCmdP);
+ #else
+ // suppress (forget) status
+ delete statusCmdP;
+ #endif
+ // next item
+ thisitemnode=nextitemnode;
+ }
+ // free element
+ FreeSmlElement();
+ // done with command, delete it now: return true
+ return true;
+} // TResultsCommand::execute
+
+
+void TResultsCommand::FreeSmlElement(void)
+{
+ // remove SyncML toolkit element(s)
+ FREEPROTOELEMENT(fResultsElementP);
+} // TResultsCommand::FreeSmlElement
+
+
+TResultsCommand::~TResultsCommand()
+{
+ // free command elements, if any (use explicit invocation as this is a destructor)
+ TResultsCommand::FreeSmlElement();
+} // TResultsCommand::TResultsCommand
+
+
+/* end of TResultsCommand implementation */
+
+
+/*
+ * Implementation of TDevInfResultsCommand
+ */
+
+
+// constructor for generating devInf results
+TDevInfResultsCommand::TDevInfResultsCommand(
+ TSyncSession *aSessionP, // associated session (for callbacks)
+ TSmlCommand *aCommandP // command these results refer to
+) :
+ /* %%% if strictly following example in SyncML protocol specs
+ * DevInf results don't have a source or target ref, only the item has
+ TResultsCommand(aSessionP,aCommandP,SYNCML_DEVINF_LOCURI,NULL)
+ */
+ TResultsCommand(aSessionP,aCommandP,NULL,NULL)
+{
+ // standard result is now created
+ // - create meta type
+ /* %%% if strictly following example in SyncML protocol specs, meta
+ * must be defined in the result/put command, not the individual item
+ */
+ string metatype=SYNCML_DEVINF_META_TYPE;
+ fSessionP->addEncoding(metatype);
+ fResultsElementP->meta=newMetaType(metatype.c_str());
+ // - add local DevInf item to result
+ // Note: explicit GET always returns entire devInf (all datastores)
+ addItem(fSessionP->getLocalDevInfItem(false,false));
+} // TDevInfResultsCommand::TDevInfResultsCommand
+
+
+// - try to shrink command by at least aReduceByBytes
+// Returns false if shrink is not possible
+bool TDevInfResultsCommand::shrinkCommand(sInt32 aReduceByBytes)
+{
+ // measure current size (with current CmdID/MsgID)
+ sInt32 origfree = evalIssue(fCmdID,fMsgID,fNoResp);
+ // extract original devInf
+ SmlItemPtr_t origDevInf = fResultsElementP->itemList->item;
+ // remove it from the item list
+ fResultsElementP->itemList->item = NULL;
+ smlFreeItemList(fResultsElementP->itemList);
+ fResultsElementP->itemList = NULL;
+ // create reduced version of the devInf
+ // - only alerted datastores if initialisation is complete (i.e. we KNOW which datastores the sync is about)
+ // - no CTCap property lists
+ SmlItemPtr_t reducedDevInf =
+ fSessionP->getLocalDevInfItem(
+ fSessionP->getIncomingState()>psta_init, // alerted only if init complete
+ true // no CTCap property lists
+ );
+ // insert it into command
+ addItem(reducedDevInf);
+ // measure again
+ sInt32 nowfree = evalIssue(fCmdID,fMsgID,fNoResp);
+ // decide if we are successful
+ if (nowfree-origfree>=aReduceByBytes) {
+ // new devinf matches size requirement
+ smlFreeItemPtr(origDevInf); // discard original devInf
+ return true; // shrink successful
+ }
+ else {
+ // new reduced devInf is still too big
+ fResultsElementP->itemList->item = origDevInf; // restore oiginal devInf
+ smlFreeItemPtr(reducedDevInf); // discard reduced one
+ return false; // cannot shrink enough
+ }
+} // TDevInfResultsCommand::shrinkCommand
+
+
+
+// mark devinf as sent when issuing command
+bool TDevInfResultsCommand::issue(
+ uInt32 aAsCmdID, // command ID to be used
+ uInt32 aInMsgID, // message ID in which command is being issued
+ bool aNoResp
+)
+{
+ fSessionP->remoteGotDevinf();
+ return TResultsCommand::issue(aAsCmdID,aInMsgID,aNoResp);
+} // TDevInfResultsCommand::issue
+
+
+void TDevInfResultsCommand::FreeSmlElement(void)
+{
+ // remove SyncML toolkit element(s)
+ // - result might already be freed!
+ if (fResultsElementP) {
+ FREEPROTOELEMENT(fResultsElementP);
+ }
+} // TDevInfResultsCommand::FreeSmlElement
+
+
+TDevInfResultsCommand::~TDevInfResultsCommand()
+{
+ // free command elements, if any (use explicit invocation as this is a destructor)
+ TDevInfResultsCommand::FreeSmlElement();
+ // NOTE: fResultsElementP is NULLed so invocation of base class destuctor does no harm
+} // TDevInfResultsCommand::TDevInfResultsCommand
+
+
+/* end of TDevInfResultsCommand implementation */
+
+
+/*
+ * Implementation of TDevInfResultsCommand
+ */
+
+
+// constructor for generating devinf Put command
+TDevInfPutCommand::TDevInfPutCommand(
+ TSyncSession *aSessionP // associated session (for callbacks)
+) :
+ TPutCommand(aSessionP)
+{
+ // standard put is now created
+ // - create meta type
+ /* %%% if strictly following example in SyncML protocol specs, meta
+ * must be defined in the result/put command, not the individual item
+ */
+ string metatype=SYNCML_DEVINF_META_TYPE;
+ fSessionP->addEncoding(metatype);
+ fPutElementP->meta=newMetaType(metatype.c_str());
+ // - add local DevInf item to result
+ // Note: PUT of server only returns alerted datastore's devInf
+ addItem(fSessionP->getLocalDevInfItem(fSessionP->IsServerSession(),false));
+} // TDevInfPutCommand::TDevInfPutCommand
+
+
+// mark devinf as sent when issuing command
+bool TDevInfPutCommand::issue(
+ uInt32 aAsCmdID, // command ID to be used
+ uInt32 aInMsgID, // message ID in which command is being issued
+ bool aNoResp
+)
+{
+ fSessionP->remoteGotDevinf();
+ return TPutCommand::issue(aAsCmdID,aInMsgID,aNoResp);
+} // TDevInfPutCommand::issue
+
+
+void TDevInfPutCommand::FreeSmlElement(void)
+{
+ // remove SyncML toolkit element(s)
+ // - result might already be freed!
+ if (fPutElementP) {
+ FREEPROTOELEMENT(fPutElementP);
+ }
+} // TDevInfPutCommand::FreeSmlElement
+
+
+TDevInfPutCommand::~TDevInfPutCommand()
+{
+ // free command elements, if any (use explicit invocation as this is a destructor)
+ TDevInfPutCommand::FreeSmlElement();
+ // NOTE: fResultsElementP is NULLed so invocation of base class destuctor does not harm
+} // TDevInfPutCommand::~TDevInfPutCommand
+
+/* end of TDevInfPutCommand implementation */
+
+#endif // SYNCCOMMAND_PART2_EXCLUDE
+
+// eof
diff --git a/src/sysync/synccommand.h b/src/sysync/synccommand.h
new file mode 100755
index 0000000..94f088c
--- /dev/null
+++ b/src/sysync/synccommand.h
@@ -0,0 +1,805 @@
+/*
+ * File: SyncCommand.cpp
+ *
+ * Author: Lukas Zeller (luz@synthesis.ch)
+ *
+ * TSmlCommand, TXXXCmd....
+ * Wrapper classes for SyncML Commands and the associated SyncML
+ * Toolkit mechanics.
+ *
+ * Copyright (c) 2001-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ * 2001-05-30 : luz : created
+ *
+ */
+
+#ifndef SyncCommand_H
+#define SyncCommand_H
+
+// includes
+#include "sysync.h"
+
+
+
+using namespace sysync;
+
+
+namespace sysync {
+
+
+// forward
+class TSyncDataStore;
+class TLocalEngineDS;
+class TRemoteDataStore;
+
+// default command size
+#define DEFAULTCOMMANDSIZE 200;
+
+
+// Command Types (Note: not only strictly commands, but all
+// SyncML protocol items that need command-like processin
+// are wrapped in a command, too, e.g. "SyncHdr"
+typedef enum {
+ scmd_synchdr,
+ scmd_sync,
+ scmd_syncend,
+ scmd_add,
+ scmd_alert,
+ scmd_delete,
+ scmd_get,
+ scmd_put,
+ scmd_map,
+ scmd_results,
+ scmd_status,
+ scmd_replace,
+ scmd_copy,
+ scmd_move,
+ scmd_sequence,
+ scmd_atomic,
+ scmd_unknown,
+ numSmlCommandTypes
+} TSmlCommandTypes;
+
+// forward
+class TStatusCommand;
+class TSyncSession;
+
+
+// abstract base command class
+class TSmlCommand
+{
+public:
+ // constructor for ALL commands
+ TSmlCommand(
+ TSmlCommandTypes aCmdType, // the command type
+ bool aOutgoing, // set if this is a outgoing command (to avoid confusion)
+ TSyncSession *aSessionP, // associated session (for callbacks)
+ uInt32 aMsgID=0 // the Message ID of the command (0=none yet)
+ );
+ virtual ~TSmlCommand();
+ // methods
+ #ifdef SYDEBUG
+ TDebugLogger *getDbgLogger(void);
+ uInt32 getDbgMask(void);
+ #endif
+ #ifndef USE_SML_EVALUATION
+ // - get (approximated) message size required for sending it
+ virtual uInt32 messageSize(void);
+ #endif
+ // - analyze command (but do not yet execute)
+ virtual bool analyze(TPackageStates aPackageState) { fPackageState=aPackageState; return true; }; // returns false if command is bad and cannot be executed
+ // - execute command (perform real actions, generate status)
+ virtual bool execute(void); // returns true if command could execute, false if it must be queued for later finishing (next message)
+ // - get number of bytes that will be still available in the workspace after
+ // sending this command.
+ sInt32 evalIssue(
+ uInt32 aAsCmdID, // command ID to be used
+ uInt32 aInMsgID, // message ID in which command is being issued
+ bool aNoResp=false // issue without wanting response
+ );
+ // - issue (send) command to remote party
+ // returns true if command must be put to the waiting-for-status queue.
+ // If returns false, command can be deleted
+ virtual bool issue(
+ uInt32 aAsCmdID, // command ID to be used
+ uInt32 aInMsgID, // message ID in which command is being issued
+ bool aNoResp=false // issue without wanting response
+ );
+ // - try to split (SyncML 1.1 moredata mechanism) command by reducing
+ // original command by at least aReduceByBytes and generating a second
+ // command containing the rest of the data
+ // Returns NULL if split is not possible
+ virtual TSmlCommand *splitCommand(sInt32 /* aReduceByBytes */) { return NULL; /* normal commands can't be split */ };
+ virtual bool canSplit(void) { return false; /* normal commands can't be split */ };
+ // - try to shrink command by at least aReduceByBytes
+ // Returns false if shrink is not possible
+ virtual bool shrinkCommand(sInt32 /* aReduceByBytes */) { return false; /* normal commands can't be shrunk */ };
+ // - eventually substitute data with previous session's buffered left-overs from a chunked transfer
+ // for resuming a chunked item transfer.
+ virtual bool checkChunkContinuation(void) { return false; /* normal commands can't split and so can't continue */ };
+ // - test if completely issued (must be called after issue() and continueIssue())
+ // or completely executed (must be called after execute() to see if command can be deleted)
+ virtual bool finished(void) { return true; }; // normal commands finish after issuing/executing
+ // - continue issuing command. This is called by session at start of new message
+ // when finished() returned false after issue()/continueIssue()
+ // (which causes caller of finished() to put command into fInteruptedCommands queue)
+ // returns true if command should be queued for status (again?)
+ // NOTE: continueIssue must make sure that it gets a new CmdID/MsgID in case
+ // the command itself was NOT yet issued (normally this does not happen, but...)
+ virtual bool continueIssue(bool & /* aNewIssue */) { return true; }
+ // - test if command matches status
+ bool matchStatus(TStatusCommand *aStatusCmdP);
+ // - handle status received for previously issued command
+ // returns true if done, false if command must be kept in the status queue
+ virtual bool handleStatus(TStatusCommand *aStatusCmdP);
+ // - mark any syncitems (or other data) for resume. Called for pending commands
+ // when a Suspend alert is received or whenever a resumable state must be saved
+ virtual void markPendingForResume(TLocalEngineDS *aForDatastoreP, bool aUnsent) { /* nop */ };
+ // - mark item for resend in next sync session (if possible)
+ virtual void markForResend(void) { /* nop */ };
+ // - generate status depending on fNoResp and session's fMsgNoResp
+ // NOTE: always returns a Status (NoResp is handled by not SENDING status)
+ TStatusCommand *newStatusCommand(TSyError aStatusCode, const char *aStringItem=NULL);
+ // properties
+ bool getNoResp(void) { return fNoResp; }; // noResp condition
+ bool getDontSend(void) { return fDontSend; }; // send inhibited condition
+ void dontSend(void) { fDontSend=true; }; // prevent sending this command
+ void allowFailure(void) { fAllowFailure=true; }; // allow failure (don't abort session if this command returns 4xx or 5xx status)
+ const char * getName(void); // name of command
+ uInt32 getCmdID(void) { return fCmdID; }; // ID of command
+ uInt32 getMsgID(void) { return fMsgID; }; // Message ID
+ TSmlCommandTypes getCmdType(void) { return fCmdType; }; // get command type
+ bool isWaitingForStatus(void) { return fWaitingForStatus>0; };
+ void setWaitingForStatus(bool w) { if (w) fWaitingForStatus++; else if (fWaitingForStatus>0) fWaitingForStatus--; };
+ TPackageStates getPackageState(void) { return fPackageState; };
+ virtual SmlPcdataPtr_t getMeta(void) { return NULL; };
+ virtual bool isSyncOp(void) { return false; };
+ virtual bool neverIgnore(void) { return false; }; // normal commands should be ignored when in fIgnoreIncomingCommands state
+ virtual bool statusEssential(void) { return true; }; // normal commands MUST receive status
+protected:
+ // helper methods for derived classes
+ // - get name of certain command
+ const char *getNameOf(TSmlCommandTypes aCmdType);
+ // - start processing a command
+ void StartProcessing(
+ SmlPcdataPtr_t aCmdID, // ID of command
+ Flag_t aFlags // flags of command
+ );
+ // - prepare elements for issuing
+ void PrepareIssue(
+ SmlPcdataPtr_t *aCmdID=NULL, // ID of command
+ Flag_t *aFlags=NULL // flags of command
+ );
+ void FinalizeIssue(void); // finalizes issuing a command (updates message size)
+ // returns true if command must be queued for status or result response
+ virtual bool queueForResponse(void);
+ // - generate and issue status depending on fNoResp and session's fMsgNoResp
+ void issueStatusCommand(TSyError aStatusCode);
+ // - free smlProtoElement structure (if any). Includes precautions if structure contains
+ // parts that are not owned by the command itself
+ virtual void FreeSmlElement(void) { /* nop in base class */ };
+ // debug
+ #ifdef SYDEBUG
+ // print to (session-related) debug output channel
+ void DebugPrintf(const char *text, ...);
+ #endif
+ // session
+ TSyncSession *fSessionP;
+ // command info
+ TSmlCommandTypes fCmdType;
+ TPackageStates fPackageState; // incoming or outgoing package state of this command (may be different from current state for delayed commands)
+ bool fOutgoing; // set if outgoing command
+ bool fNoResp;
+ uInt32 fCmdID; // command ID
+ uInt32 fMsgID; // message ID
+ // message size calculation
+ uInt32 fBytesbefore;
+ // commands that should not be sent (e.g. a "response" to a NoResp command)
+ bool fDontSend;
+ // set to signal to issue that this is a size evaluation issuing, not a real one
+ bool fEvalMode;
+ // set if PrepareIssue() was already called
+ bool fPrepared;
+ // set if command may fail with status 4xx or 5xx without aborting the session
+ bool fAllowFailure;
+private:
+ uInt16 fWaitingForStatus; // count for how many statuses a command is waiting
+}; // TSmlCommand
+
+
+// Unimplemented command
+class TUnimplementedCommand: public TSmlCommand
+{
+ typedef TSmlCommand inherited;
+public:
+ // constructor for generating devinf results
+ TUnimplementedCommand(
+ TSyncSession *aSessionP, // associated session (for callbacks)
+ uInt32 aMsgID, // the Message ID of the command
+ SmlPcdataPtr_t aCmdID, // command ID (as contents are unknown an cannot be analyzed)
+ Flag_t aFlags, // flags to get fNoResp
+ TSmlCommandTypes aCmdType=scmd_unknown, // command type (for name)
+ void *aContentP=NULL, // associated command content element
+ TSyError aStatusCode=406 // status code to be returned on execution (default = unimplemented option 406)
+ );
+ virtual bool execute(void);
+private:
+ TSyError fStatusCode;
+}; // TUnimplementedCommand
+
+
+// Sync Header "command"
+class TSyncHeader: public TSmlCommand
+{
+ typedef TSmlCommand inherited;
+public:
+ // constructor for receiving SyncHdr
+ TSyncHeader(
+ TSyncSession *aSessionP, // associated session (for callbacks)
+ SmlSyncHdrPtr_t aSyncHdrElementP=NULL // associated SyncHdr content element
+ );
+ // constructor for sending SyncHdr
+ TSyncHeader(
+ TSyncSession *aSessionP, // associated session (for callbacks)
+ bool aOutgoingNoResp=false // if true, entire message will request no responses
+ );
+ virtual ~TSyncHeader();
+ virtual bool execute(void);
+ virtual bool issue(
+ uInt32 aAsCmdID, // command ID to be used (is dummy=0 for SyncHdr)
+ uInt32 aInMsgID, // message ID in which command is being issued
+ bool aNoResp=false // issue without wanting response FOR ENTIRE message
+ ); // returns false, because command can be deleted immediately after issue()
+ virtual bool statusEssential(void) { return false; }; // header status is not essential (per se - of course it is for login)
+ // - handle status received for previously issued command
+ // returns true if done, false if command must be kept in the status queue
+ virtual bool handleStatus(TStatusCommand *aStatusCmdP);
+protected:
+ virtual void FreeSmlElement(void);
+ SmlSyncHdrPtr_t fSyncHdrElementP;
+}; // TSyncHeader
+
+
+// Alert Command
+class TAlertCommand: public TSmlCommand
+{
+ typedef TSmlCommand inherited;
+public:
+ // constructor for receiving Alert
+ TAlertCommand(
+ TSyncSession *aSessionP, // associated session (for callbacks)
+ uInt32 aMsgID, // the Message ID of the command
+ SmlAlertPtr_t aAlertElementP // associated Alert content element
+ );
+ // constructor for sending Alert
+ TAlertCommand(
+ TSyncSession *aSessionP, // associated session (for callbacks)
+ TLocalEngineDS *aLocalDataStoreP, // local datastore
+ uInt16 aAlertCode // Alert code to send
+ );
+ virtual ~TAlertCommand();
+ virtual bool analyze(TPackageStates aPackageState);
+ virtual bool execute(void);
+ virtual bool issue(
+ uInt32 aAsCmdID, // command ID to be used (is dummy=0 for SyncHdr)
+ uInt32 aInMsgID, // message ID in which command is being issued
+ bool aNoResp=false // issue without wanting response
+ ); // returns false, because command can be deleted immediately after issue()
+ // - add a String Item
+ void addItemString(const char *aItemString); // item string to be added
+ // - add an Item
+ void addItem(SmlItemPtr_t aItemP); // existing item data structure, ownership is passed to Command
+ // - handle status received for previously issued command
+ // returns true if done, false if command must be kept in the status queue
+ virtual bool handleStatus(TStatusCommand *aStatusCmdP);
+protected:
+ virtual void FreeSmlElement(void);
+ SmlAlertPtr_t fAlertElementP;
+ uInt16 fAlertCode; // alert code
+ // involved datastore
+ TLocalEngineDS *fLocalDataStoreP;
+}; // TAlertCommand
+
+
+
+// Sync Command
+class TSyncCommand: public TSmlCommand
+{
+ typedef TSmlCommand inherited;
+public:
+ // constructor for receiving Sync
+ TSyncCommand(
+ TSyncSession *aSessionP, // associated session (for callbacks)
+ uInt32 aMsgID, // the Message ID of the command
+ SmlSyncPtr_t aSyncElementP=NULL // associated Sync content element
+ );
+ // constructor for sending Sync
+ TSyncCommand(
+ TSyncSession *aSessionP, // associated session (for callbacks)
+ TLocalEngineDS *aLocalDataStoreP, // local datastore
+ TRemoteDataStore *aRemoteDataStoreP // remote datastore
+ );
+ virtual ~TSyncCommand();
+ virtual bool analyze(TPackageStates aPackageState);
+ virtual bool execute(void);
+ virtual bool issue(
+ uInt32 aAsCmdID, // command ID to be used (is dummy=0 for SyncHdr)
+ uInt32 aInMsgID, // message ID in which command is being issued
+ bool aNoResp=false // issue without wanting response
+ );
+ // - test if completely issued (must be called after issue() and continueIssue())
+ // or completely executed (must be called after execute() to see if command can be deleted)
+ virtual bool finished(void); // normal command finish
+ // - continue issuing command.
+ virtual bool continueIssue(bool &aNewIssue);
+ // - handle status received for previously issued command
+ // returns true if done, false if command must be kept in the status queue
+ virtual bool handleStatus(TStatusCommand *aStatusCmdP);
+ // - mark any syncitems (or other data) for resume. Called for pending commands
+ // when a Suspend alert is received or whenever a resumable state must be saved
+ virtual void markPendingForResume(TLocalEngineDS *aForDatastoreP, bool aUnsent);
+protected:
+ virtual void FreeSmlElement(void);
+ SmlSyncPtr_t fSyncElementP;
+private:
+ // internals
+ bool generateOpen(void);
+ void generateCommandsAndClose(void);
+ bool fInProgress; // set if <Sync> bracket must be re-opened in next message (=not finished)
+ // variables for sync needing more than one message
+ TSmlCommandPContainer fNextMessageCommands;
+ TSmlCommand *fInterruptedCommandP;
+ // involved datastores and mode
+ TLocalEngineDS *fLocalDataStoreP;
+ TRemoteDataStore *fRemoteDataStoreP;
+}; // TSyncCommand
+
+
+// Sync End Command (closing <sync> bracket: </sync>)
+// Note: used for receiving only, when sending, the closing
+// bracket is part of TSyncCommand production process.
+class TSyncEndCommand: public TSmlCommand
+{
+ typedef TSmlCommand inherited;
+public:
+ // constructor for receiving </sync>
+ TSyncEndCommand(
+ TSyncSession *aSessionP, // associated session (for callbacks)
+ uInt32 aMsgID // the Message ID of the command
+ );
+ virtual ~TSyncEndCommand();
+ virtual bool execute(void);
+}; // TSyncEndCommand
+
+
+
+// bytes required for empty item (approx)
+ /*
+<MapItem><Target><LocURI></LocURI></Target><Source><LocURI></LocURI></Source></MapItem>
+ */
+#define ITEMOVERHEADSIZE 90
+
+// minimum size of data for a first split
+#define MIN_SPLIT_DATA 200
+
+// SyncOp (Add/Delete/Replace/Copy/Move) command
+class TSyncOpCommand: public TSmlCommand
+{
+ typedef TSmlCommand inherited;
+public:
+ // constructor for receiving SyncOP command
+ TSyncOpCommand(
+ TSyncSession *aSessionP, // associated session (for callbacks)
+ TLocalEngineDS *aDataStoreP, // the local datastore this syncop belongs to
+ uInt32 aMsgID, // the Message ID of the command
+ TSyncOperation aSyncOp, // the Sync operation (sop_xxx)
+ TSmlCommandTypes aCmdType, // the command type (scmd_xxx)
+ SmlGenericCmdPtr_t aSyncOpElementP=NULL // associated syncml protocol element
+ );
+ // constructor for sending command
+ TSyncOpCommand(
+ TSyncSession *aSessionP, // associated session (for callbacks)
+ TLocalEngineDS *aDataStoreP, // datastore from which command originates
+ TSyncOperation aSyncOp, // sync operation (=command type)
+ SmlPcdataPtr_t aMetaP // meta for entire command (passing owner)
+ );
+ virtual ~TSyncOpCommand();
+ virtual bool isSyncOp() { return true; };
+ virtual bool analyze(TPackageStates aPackageState);
+ virtual bool execute(void);
+ #ifndef USE_SML_EVALUATION
+ // - get (approximated) message size required for sending it
+ virtual uInt32 messageSize(void);
+ #endif
+ // - add an Item to an existing Sync op command
+ void addItem(SmlItemPtr_t aItemP); // existing item data structure, ownership is passed to SyncOpCmd
+ // returns true if command must be put to the waiting-for-status queue.
+ // If false, command can be deleted
+ bool issue(
+ uInt32 aAsCmdID, // command ID to be used
+ uInt32 aInMsgID, // message ID in which command is being issued
+ bool aNoResp // dummy here, because Status has always no response
+ );
+ // - try to split (e.g using SyncML 1.1 moredata mechanism) command by reducing
+ // original command by at least aReduceByBytes and generating a second
+ // command containing the rest of the data
+ // Returns NULL if split is not possible
+ virtual TSmlCommand *splitCommand(sInt32 aReduceByBytes);
+ virtual bool canSplit(void);
+ // - eventually substitute data with previous session's buffered left-overs from a chunked transfer
+ // for resuming a chunked item transfer.
+ virtual bool checkChunkContinuation(void);
+ // - test if completely issued (must be called after issue() and continueIssue())
+ // or completely executed (must be called after execute() to see if command can be deleted)
+ virtual bool finished(void); // normal command finish
+ /* %%% not used any more with new splitCommand
+ // - continue issuing command. This is called by session at start of new message
+ // when finished() returned false after issue()/continueIssue()
+ // (which causes caller of finished() to put command into fInteruptedCommands queue)
+ // returns true if command should be queued for status (again?)
+ // NOTE: continueIssue must make sure that it gets a new CmdID/MsgID in case
+ // the command itself was NOT yet issued (normally this does not happen, but...)
+ virtual bool continueIssue(bool &aNewIssue);
+ */
+ // - handle status received for previously issued command
+ // returns true if done, false if command must be kept in the status queue
+ virtual bool handleStatus(TStatusCommand *aStatusCmdP);
+ // - mark any unstatused mapitems for resume. Called for pending commands
+ // when a Suspend alert is received or whenever a resumable state must be saved
+ virtual void markPendingForResume(TLocalEngineDS *aForDatastoreP, bool aUnsent);
+ // - mark item for resend in next sync session (if possible)
+ virtual void markForResend(void);
+ // - let item update the partial item state for suspend
+ void updatePartialItemState(TLocalEngineDS *aForDatastoreP);
+ // - get sync op
+ TSyncOperation getSyncOp(void) { return fSyncOp; };
+ // - get source (localID) of sent command
+ cAppCharP getSourceLocalID(void);
+ // - get target (remoteID) of sent command
+ cAppCharP getTargetRemoteID(void);
+protected:
+ localstatus AddNextChunk(SmlItemPtr_t aNextChunkItem, TSyncOpCommand *aCmdP);
+ void saveAsPartialItem(SmlItemPtr_t aItemP);
+ virtual void FreeSmlElement(void);
+ SmlGenericCmdPtr_t fSyncOpElementP;
+ #ifndef USE_SML_EVALUATION
+ uInt32 fItemSizes; // accumulated item size
+ #endif
+ TSyncOperation fSyncOp; // Sync operation (sop_xxx)
+ TLocalEngineDS *fDataStoreP;
+ // SyncML 1.1 data segmentation
+ uInt32 fChunkedItemSize; // size of object currently being chunked
+ bool fIncompleteData; // set for commands that do not tranfer the final chunk (and must be answered with 213 status)
+ // SyncML 1.2 resuming a chunked item transmission
+ uInt32 fStoredSize; // from previous session
+ uInt32 fUnconfirmedSize; // from previous session
+ uInt32 fLastChunkSize; // size of last chunk received in THIS session (=probably unconfirmed reception, will probably be sent again from remote)
+}; // TSyncOpCommand
+
+
+
+// Map command
+class TMapCommand: public TSmlCommand
+{
+ typedef TSmlCommand inherited;
+public:
+ // Common for client and server
+ virtual ~TMapCommand();
+ #ifndef USE_SML_EVALUATION
+ // - get (approximated) message size required for sending it
+ virtual uInt32 messageSize(void);
+ #endif
+ // - constructor for receiving MAP command
+ TMapCommand(
+ TSyncSession *aSessionP, // associated session (for callbacks)
+ uInt32 aMsgID, // the Message ID of the command
+ SmlMapPtr_t aMapElementP // associated syncml protocol element
+ );
+ #ifndef SYSYNC_CLIENT
+ // Server implementation
+ virtual bool analyze(TPackageStates aPackageState);
+ virtual bool execute(void);
+ #else
+ // Client implementation
+ // - constructor for sending MAP
+ TMapCommand(
+ TSyncSession *aSessionP, // associated session (for callbacks)
+ TLocalEngineDS *aLocalDataStoreP, // local datastore
+ TRemoteDataStore *aRemoteDataStoreP // remote datastore
+ );
+ virtual bool issue(
+ uInt32 aAsCmdID, // command ID to be used (is dummy=0 for SyncHdr)
+ uInt32 aInMsgID, // message ID in which command is being issued
+ bool aNoResp=false // issue without wanting response
+ );
+ // - test if completely issued (must be called after issue() and continueIssue())
+ // or completely executed (must be called after execute() to see if command can be deleted)
+ virtual bool finished(void); // normal command finish
+ // - continue issuing command.
+ virtual bool continueIssue(bool &aNewIssue);
+ // - add map item
+ void addMapItem(const char *aLocalID, const char *aRemoteID);
+ // - remove last added map item from the command
+ void deleteLastMapItem(void);
+ // - handle status received for previously issued command
+ // returns true if done, false if command must be kept in the status queue
+ virtual bool handleStatus(TStatusCommand *aStatusCmdP);
+ #endif
+protected:
+ virtual void FreeSmlElement(void);
+ SmlMapPtr_t fMapElementP;
+private:
+ // internals
+ #ifdef SYSYNC_CLIENT
+ void generateEmptyMapElement(void);
+ void generateMapItems(void);
+ #endif
+ bool fInProgress; // set if <Map> command must be continued in next message (=not finished)
+ // involved datastores and mode
+ TLocalEngineDS *fLocalDataStoreP;
+ TRemoteDataStore *fRemoteDataStoreP;
+ #ifndef USE_SML_EVALUATION
+ // size
+ uInt32 fItemSizes; // accumulated item size
+ #endif
+}; // TMapCommand
+
+
+
+// Get command
+class TGetCommand: public TSmlCommand
+{
+ typedef TSmlCommand inherited;
+public:
+ // constructor for receiving GET command
+ TGetCommand(
+ TSyncSession *aSessionP, // associated session (for callbacks)
+ uInt32 aMsgID, // the Message ID of the command
+ SmlGetPtr_t aGetElementP=NULL // associated GET command content element
+ );
+ // constructor for sending command
+ TGetCommand(
+ TSyncSession *aSessionP // associated session (for callbacks)
+ );
+ // add an Item to the get command
+ void addItem(
+ SmlItemPtr_t aItemP // existing item data structure, ownership is passed to Get
+ );
+ // set Meta of the get command
+ void setMeta(
+ SmlPcdataPtr_t aMetaP // existing meta data structure, ownership is passed to Get
+ );
+ // get meta of the get command
+ virtual SmlPcdataPtr_t getMeta(void) { return fGetElementP==NULL ? NULL : fGetElementP->meta; };
+ // add a target specification Item to the get command
+ void addTargetLocItem(
+ const char *aTargetURI,
+ const char *aTargetName=NULL
+ );
+ virtual ~TGetCommand();
+ virtual bool analyze(TPackageStates aPackageState);
+ virtual bool execute(void);
+ virtual bool issue(
+ uInt32 aAsCmdID, // command ID to be used
+ uInt32 aInMsgID, // message ID in which command is being issued
+ bool aNoResp=true // status does not want response by default
+ ); // returns true if command must be put to the waiting-for-status queue. If false, command can be deleted
+ // - handle status received for previously issued command
+ // returns true if done, false if command must be kept in the status queue
+ virtual bool handleStatus(TStatusCommand *aStatusCmdP);
+protected:
+ virtual void FreeSmlElement(void);
+ SmlGetPtr_t fGetElementP;
+}; // TGetCommand
+
+
+// Put command
+class TPutCommand: public TSmlCommand
+{
+ typedef TSmlCommand inherited;
+public:
+ // constructor for receiving PUT command
+ TPutCommand(
+ TSyncSession *aSessionP, // associated session (for callbacks)
+ uInt32 aMsgID, // the Message ID of the command
+ SmlGetPtr_t aPutElementP=NULL // associated PUT command content element
+ );
+ // constructor for sending command
+ TPutCommand(
+ TSyncSession *aSessionP // associated session (for callbacks)
+ );
+ // add an Item to the put command
+ void addItem(
+ SmlItemPtr_t aItemP // existing item data structure, ownership is passed to Get
+ );
+ // set Meta of the put command
+ void setMeta(
+ SmlPcdataPtr_t aMetaP // existing meta data structure, ownership is passed to Get
+ );
+ // get meta of the put command
+ virtual SmlPcdataPtr_t getMeta(void) { return fPutElementP==NULL ? NULL : fPutElementP->meta; };
+ // add a source specification + data Item to the Put command
+ SmlItemPtr_t addSourceLocItem(
+ const char *aTargetURI,
+ const char *aTargetName=NULL
+ );
+ virtual ~TPutCommand();
+ virtual bool analyze(TPackageStates aPackageState);
+ virtual bool execute(void);
+ virtual bool issue(
+ uInt32 aAsCmdID, // command ID to be used
+ uInt32 aInMsgID, // message ID in which command is being issued
+ bool aNoResp=true // status does not want response by default
+ ); // returns true if command must be put to the waiting-for-status queue. If false, command can be deleted
+protected:
+ virtual void FreeSmlElement(void);
+ SmlPutPtr_t fPutElementP;
+}; // TPutCommand
+
+
+// Status command
+class TStatusCommand: public TSmlCommand
+{
+ typedef TSmlCommand inherited;
+ friend class TSmlCommand; // command may look into status
+public:
+ // constructor for generating status
+ TStatusCommand(
+ TSyncSession *aSessionP, // associated session (for callbacks)
+ TSmlCommand *aCommandP, // command this status refers to, NULL=response to message header
+ TSyError aStatusCode // status code
+ );
+ // constructor for generating status w/o having a command object (e.g. dummy)
+ TStatusCommand(
+ TSyncSession *aSessionP, // associated session (for callbacks)
+ uInt32 aRefCmdID=0, // referred-to command ID
+ TSmlCommandTypes aRefCmdType=scmd_unknown, // referred-to command type (scmd_xxx)
+ bool aNoResp=true, // set if no-Resp
+ TSyError aStatusCode=0 // status code
+ );
+ // constructor for receiving status
+ TStatusCommand(
+ TSyncSession *aSessionP, // associated session (for callbacks)
+ uInt32 aMsgID, // the Message ID of the command
+ SmlStatusPtr_t aStatusElementP=NULL // associated STATUS command content element
+ );
+ virtual ~TStatusCommand();
+ virtual bool analyze(TPackageStates aPackageState); // analyze status
+ virtual bool execute(void) { return true; }; // cannot be executed
+ #ifndef USE_SML_EVALUATION
+ // - get (approximated) message size required for sending it
+ virtual uInt32 messageSize(void) { return fDontSend ? 0 : TSmlCommand::messageSize(); };
+ #endif
+ virtual bool issue(
+ uInt32 aAsCmdID, // command ID to be used
+ uInt32 aInMsgID, // message ID in which command is being issued
+ bool aNoResp=true // status does not want response by default
+ ); // returns true if command must be put to the waiting-for-status queue. If false, command can be deleted
+ // status building tools
+ // - add a target Ref
+ void addTargetRef(const char *aTargetRef); // Target LocURI of an item this status applies to
+ // - add a source Ref to an existing status
+ void addSourceRef(const char *aSourceRef); // Source LocURI of an item this status applies to
+ // - add a String Item to an existing status
+ void addItemString(const char *aItemString); // item string to be added
+ // - add an (internal) error code message as text item to the status
+ void addErrorCodeString(uInt32 aErrCode, const char *aText=NULL);
+ // - add an Item to an existing status
+ void addItem(SmlItemPtr_t aItemP); // existing item data structure, ownership is passed to Status
+ // - move items from another status to this one (passes ownership of items and deletes them from original)
+ void moveItemsFrom(TStatusCommand *aStatusCommandP);
+ // - set Status code
+ void setStatusCode(TSyError aStatusCode);
+ // - set Challenge of an existing status (overwrites existing one, if any)
+ void setChallenge(SmlChalPtr_t aChallengeP); // existing challenge data structure, ownership is passed to Status
+ // properties
+ // - get status code
+ TSyError getStatusCode(void) { return fStatusCode; };
+ // - get status Sml Element
+ const SmlStatusPtr_t getStatusElement(void) { return fStatusElementP; }
+protected:
+ virtual void FreeSmlElement(void);
+ SmlStatusPtr_t fStatusElementP;
+ // info
+ TSyError fStatusCode; // status code
+ uInt32 fRefMsgID; // referenced message ID
+ uInt32 fRefCmdID; // referenced command ID
+ TSmlCommandTypes fRefCmdType; // referenced command type
+}; // TStatusCommand
+
+
+// Results command
+class TResultsCommand: public TSmlCommand
+{
+ typedef TSmlCommand inherited;
+public:
+ // constructor for generating results
+ TResultsCommand(
+ TSyncSession *aSessionP, // associated session (for callbacks)
+ TSmlCommand *aCommandP, // command this status refers to
+ const char *aTargetRef, // target reference
+ const char *aSourceRef // source reference
+ );
+ // constructor for receiving command
+ TResultsCommand(
+ TSyncSession *aSessionP, // associated session (for callbacks)
+ uInt32 aMsgID, // the Message ID of the command
+ SmlResultsPtr_t aResultsElementP // associated syncml protocol element
+ );
+ virtual ~TResultsCommand();
+ virtual bool neverIgnore() { return true; }; // result for devInf we requested should never be ignored
+ virtual bool analyze(TPackageStates aPackageState);
+ virtual bool execute(void);
+ virtual bool issue(
+ uInt32 aAsCmdID, // command ID to be used
+ uInt32 aInMsgID, // message ID in which command is being issued
+ bool aNoResp=false // issue without wanting response
+ ); // returns true if command must be put to the waiting-for-status queue. If false, command can be deleted
+ // results building tools
+ // - add a String Item to results
+ void addItemString(const char *aItemString); // item string to be added
+ // - add an Item to results
+ void addItem(SmlItemPtr_t aItemP); // existing item data structure, ownership is passed to Status
+ // set Meta of the results command
+ void setMeta(
+ SmlPcdataPtr_t aMetaP // existing meta data structure, ownership is passed to Get
+ );
+ // get meta of the results command
+ virtual SmlPcdataPtr_t getMeta(void) { return fResultsElementP==NULL ? NULL : fResultsElementP->meta; };
+protected:
+ virtual void FreeSmlElement(void);
+ SmlResultsPtr_t fResultsElementP;
+}; // TResultsCommand
+
+
+// DevInf Results command (specialized TResultsCommand derivate for sending devInf)
+class TDevInfResultsCommand: public TResultsCommand
+{
+ typedef TResultsCommand inherited;
+public:
+ // constructor for generating devinf results
+ TDevInfResultsCommand(
+ TSyncSession *aSessionP, // associated session (for callbacks)
+ TSmlCommand *aCommandP // command this status refers to
+ );
+ virtual ~TDevInfResultsCommand();
+ virtual bool issue(
+ uInt32 aAsCmdID, // command ID to be used
+ uInt32 aInMsgID, // message ID in which command is being issued
+ bool aNoResp
+ );
+ virtual bool statusEssential(void) { return false; }; // results status is not essential
+ // - try to shrink command by at least aReduceByBytes
+ // Returns false if shrink is not possible
+ virtual bool shrinkCommand(sInt32 aReduceByBytes);
+protected:
+ virtual void FreeSmlElement(void);
+}; // TDevInfResultsCommand
+
+
+// DevInf Put command (specialized TPutCommand derivate for sending devInf)
+class TDevInfPutCommand: public TPutCommand
+{
+ typedef TPutCommand inherited;
+public:
+ // constructor for generating devinf Put
+ TDevInfPutCommand(
+ TSyncSession *aSessionP // associated session (for callbacks)
+ );
+ virtual ~TDevInfPutCommand();
+ virtual bool issue(
+ uInt32 aAsCmdID, // command ID to be used
+ uInt32 aInMsgID, // message ID in which command is being issued
+ bool aNoResp
+ );
+ virtual bool statusEssential(void) { return false; }; // devInf Put status is not essential
+protected:
+ virtual void FreeSmlElement(void);
+}; // TDevInfPutCommand
+
+
+} // namespace sysync
+
+#endif // SyncCommand_H
+
+// eof
diff --git a/src/sysync/syncdatastore.cpp b/src/sysync/syncdatastore.cpp
new file mode 100755
index 0000000..50fd999
--- /dev/null
+++ b/src/sysync/syncdatastore.cpp
@@ -0,0 +1,333 @@
+/*
+ * File: SyncDataStore.cpp
+ *
+ * Author: Lukas Zeller (luz@synthesis.ch)
+ *
+ * TSyncDataStore
+ * <describe here>
+ *
+ * Copyright (c) 2001-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ * 2001-xx-xx : luz : created
+ *
+ */
+
+// includes
+#include "prefix_file.h"
+
+#include "sysync.h"
+
+#include "syncdatastore.h"
+#include "syncsession.h"
+
+
+
+using namespace sysync;
+
+
+/*
+ * Implementation of TSyncDataStore
+ */
+
+
+void TSyncDataStore::init(TSyncSession *aSessionP)
+{
+ // save link to session
+ fSessionP=aSessionP;
+ // no sync capabilities by default
+ fCommonSyncCapMask=0;
+ // name not known
+ fName="[unknown]";
+ // no types
+ fRxPrefItemTypeP = NULL;
+ fTxPrefItemTypeP = NULL;
+ // no max GUID size yet
+ fMaxGUIDSize = 0;
+ // unlimited (that is, maximum possible with longlong) size and ID
+ #ifdef __BORLANDC__
+ fMaxMemory = numeric_limits<longlong>::max();
+ fFreeMemory = fMaxMemory;
+ fMaxID = numeric_limits<longlong>::max();
+ fFreeID = fMaxID;
+ #elif defined(LINUX) || defined(WINCE) || defined(_MSC_VER) || defined(__EPOC_OS__)
+ fMaxMemory = LONG_MAX;
+ fFreeMemory = fMaxMemory;
+ fMaxID = LONG_MAX;
+ fFreeID = fMaxID;
+ #else
+ fMaxMemory = numeric_limits<__typeof__(fMaxMemory)>::max();
+ fFreeMemory = fMaxMemory;
+ fMaxID = numeric_limits<__typeof__(fMaxID)>::max();
+ fFreeID = fMaxID;
+ #endif
+ // rest ist like reset
+ InternalResetDataStore();
+} // TSyncDataStore::init
+
+
+TSyncDataStore::TSyncDataStore(TSyncSession *aSessionP)
+{
+ init(aSessionP);
+} // TSyncDataStore::TSyncDataStore
+
+
+TSyncDataStore::TSyncDataStore(TSyncSession *aSessionP, const char *aName, uInt32 aCommonSyncCapMask)
+{
+ // basic init
+ init(aSessionP);
+ // make sure we have SyncML minimal Caps, save it
+ fCommonSyncCapMask=aCommonSyncCapMask | SCAP_MASK_MINIMAL;
+ // save name of datastore
+ fName = aName;
+} // TSyncDataStore::TSyncDataStore
+
+
+void TSyncDataStore::setPreferredTypes(TSyncItemType *aRxItemTypeP, TSyncItemType *aTxItemTypeP)
+{
+ // save standard Rx and Tx preferences
+ fRxPrefItemTypeP = aRxItemTypeP;
+ fTxPrefItemTypeP = aTxItemTypeP ? aTxItemTypeP : aRxItemTypeP;
+ // add them to the tx/rx lists
+ fRxItemTypes.push_back(fRxPrefItemTypeP);
+ fTxItemTypes.push_back(fTxPrefItemTypeP);
+} // TSyncDataStore::setPreferredTypes
+
+
+void TSyncDataStore::InternalResetDataStore(void)
+{
+ // empty for now
+} // TSyncDataStore::InternalResetDataStore
+
+
+TSyncDataStore::~TSyncDataStore()
+{
+ InternalResetDataStore();
+} // TSyncDataStore::~TSyncDataStore
+
+
+
+// - returns session zones
+GZones *TSyncDataStore::getSessionZones(void)
+{
+ return fSessionP->getSessionZones();
+} // TSyncDataStore::getSessionZones
+
+
+#ifdef SYDEBUG
+
+TDebugLogger *TSyncDataStore::getDbgLogger(void)
+{
+ // commands log to session's logger
+ return fSessionP ? fSessionP->getDbgLogger() : NULL;
+} // TSyncDataStore::getDbgLogger
+
+uInt32 TSyncDataStore::getDbgMask(void)
+{
+ if (!fSessionP) return 0; // no session, no debug
+ return fSessionP->getDbgMask();
+} // TSyncDataStore::getDbgMask
+
+#endif
+
+
+// check if this datastore is accessible with given URI
+// NOTE: URI might include path elements or CGI params that are
+// access options to the database; derived classes might
+// therefore base identity check on more than simple name match
+uInt16 TSyncDataStore::isDatastore(const char *aDatastoreURI)
+{
+ // base class only implements case insensitive name comparison
+ return strucmp(fName.c_str(),aDatastoreURI)==0 ? fName.size() : 0;
+} // TSyncDataStore::isDatastore
+
+
+/// @brief checks if specified type is used by this datastore
+/// @return true if type is used by this datastore
+/// @param aSyncItemType[in] type to be checked for being used
+/// @param aVariantDescP[out] if not NULL, will receive the variant descriptor associated with that use
+bool TSyncDataStore::doesUseType(TSyncItemType *aSyncItemType, TTypeVariantDescriptor *aVariantDescP)
+{
+ // true if either used for send or for receive
+ TSyncItemType *rx, *tx;
+ rx=getReceiveType(aSyncItemType);
+ tx=getSendType(aSyncItemType);
+ // determine usage variant if needed
+ if (aVariantDescP) {
+ *aVariantDescP = getVariantDescForType(aSyncItemType);
+ }
+ // used if used as rx or tx
+ return (rx!=NULL || tx!=NULL);
+} // TSyncDataStore::doesUseType
+
+
+// - returns type that this datastore can use to send data to specified datastore
+// Note: if possible, use preferred Rx type of specified datastore, if this
+// is not possible, use preferred Tx type of this datastore, else
+// use any matching pair.
+TSyncItemType *TSyncDataStore::getTypesForTxTo(
+ TSyncDataStore *aDatastoreP, // usually remote datastore
+ TSyncItemType **aCorrespondingTypePP
+)
+{
+ TSyncItemTypePContainer::iterator pos;
+ TSyncItemType *corrP;
+ TSyncItemType *txtypeP=NULL, *corrtypeP=NULL; // none found so far
+ TSyncItemType *preftxP=NULL, *preftxcorrP=NULL; // none found so far
+
+ // if no datastore there: none
+ if (!aDatastoreP) return NULL;
+ // get common type for transmitting TO specified datastore
+ // - loop through all send types of myself
+ for (pos=fTxItemTypes.begin(); pos!=fTxItemTypes.end(); ++pos) {
+ // - check if remote has this type
+ if ((corrP=aDatastoreP->getReceiveType(*pos))!=NULL) {
+ // found matching type
+ // - save pointers (we found at least one matching type)
+ txtypeP=*pos; // TSyncItemType of this datastore
+ corrtypeP=corrP; // TSyncItemType of other datastore
+ // - check if preferred by remote
+ if (aDatastoreP->getPreferredRxItemType()==corrtypeP) {
+ // yes, this is best case, save it and stop searching
+ preftxP=txtypeP;
+ preftxcorrP=corrtypeP;
+ break; // done
+ }
+ // - if found, but not preferred by remote, check if its our own preference
+ if (getPreferredTxItemType()==txtypeP) {
+ // remember in case we don't find the remote's preferred type
+ preftxP=txtypeP;
+ preftxcorrP=corrtypeP;
+ // but continue because we might find remote's preferred
+ }
+ }
+ }
+ // Now txtypeP/corrtypeP are NULL if no match found
+ // Now preftxP/=preftxcorrP are NULL if no preferred match found
+ // check if we found a preferred
+ if (preftxP) {
+ // use it instead of last found (or NULL if none found)
+ txtypeP=preftxP;
+ corrtypeP=preftxcorrP;
+ }
+ // return send type
+ if (aCorrespondingTypePP) *aCorrespondingTypePP = corrtypeP; // corresponding TSyncItemType of other datastore
+ return txtypeP;
+} // TSyncDataStore::getTypesForTxTo
+
+
+// - returns type that this datastore can use to receive data from specified datastore
+TSyncItemType *TSyncDataStore::getTypesForRxFrom(
+ TSyncDataStore *aDatastoreP, // usually remote datastore
+ TSyncItemType **aCorrespondingTypePP
+)
+{
+ TSyncItemTypePContainer::iterator pos;
+ TSyncItemType *rxtypeP=NULL, *corrP;
+
+ // get common type for receiving FROM specified datastore
+ for (pos=fRxItemTypes.begin(); pos!=fRxItemTypes.end(); ++pos) {
+ // loop through all receive types of myself
+ if ((corrP=aDatastoreP->getSendType(*pos))!=NULL) {
+ // found
+ rxtypeP=*pos;
+ if (aCorrespondingTypePP) *aCorrespondingTypePP = corrP; // corresponding TSyncItemType of other datastore
+ break;
+ }
+ }
+ // return receive type
+ return rxtypeP;
+} // TSyncDataStore::getTypesForRxFrom
+
+
+
+// get datastore's SyncItemType for receive of specified type, NULL if none
+TSyncItemType *TSyncDataStore::getReceiveType(const char *aType, const char *aVers)
+{
+ return (
+ TSyncItemType::findTypeInList(
+ fRxItemTypes,
+ aType,
+ aVers,
+ false, // version doesn't need to match
+ true, // but we want an implemented type
+ NULL // no datastore link comparison
+ )
+ );
+} // TSyncDataStore::getReceiveType
+
+
+// get datastore's SyncItemType for receive of specified type, NULL if none
+TSyncItemType *TSyncDataStore::getReceiveType(TSyncItemType *aSyncItemTypeP)
+{
+ return (
+ getReceiveType (
+ aSyncItemTypeP->getTypeName(),
+ aSyncItemTypeP->getTypeVers()
+ )
+ );
+} // TSyncDataStore::getReceiveType
+
+
+// get datastore's SyncItemType for send of specified type, NULL if none
+TSyncItemType *TSyncDataStore::getSendType(const char *aType, const char *aVers)
+{
+ return (
+ TSyncItemType::findTypeInList(
+ fTxItemTypes,
+ aType,
+ aVers,
+ false, // version doesn't need to match
+ true, // but we want an implemented type
+ NULL // no datastore link comparison
+ )
+ );
+} // TSyncDataStore::getSendType
+
+
+// get datastore's SyncItemType for send of specified type, NULL if none
+TSyncItemType *TSyncDataStore::getSendType(TSyncItemType *aSyncItemTypeP)
+{
+ return (
+ getSendType(
+ aSyncItemTypeP->getTypeName(),
+ aSyncItemTypeP->getTypeVers()
+ )
+ );
+} // TSyncDataStore::getSendType
+
+
+
+// description structure of datastore (NULL if not available)
+SmlDevInfDatastorePtr_t TSyncDataStore::getDatastoreDevinf(bool aAsServer, bool aWithoutCTCapProps)
+{
+ // get Datastore description (when I am a Server)
+ return newDevInfDatastore(aAsServer, aWithoutCTCapProps);
+} // TSyncDataStore::getDatastoreDevinf
+
+
+// private helper for setDatastoreDevInf
+void TSyncDataStore::registerTypes(
+ TSyncItemTypePContainer &aSupportedXmitTypes,
+ SmlDevInfXmitListPtr_t aXmitTypeListP,
+ TSyncItemTypePContainer &aLocalItemTypes, // list to look up local types (for reference)
+ TSyncItemTypePContainer &aNewItemTypes, // list to add analyzed types if not already there
+ TSyncDataStore *aRelatedDatastoreP
+)
+{
+ while (aXmitTypeListP) {
+ if (aXmitTypeListP->data) {
+ // register
+ TSyncItemType *itemtypeP =
+ TSyncItemType::registerType(fSessionP,aXmitTypeListP->data,aLocalItemTypes,aNewItemTypes,aRelatedDatastoreP);
+ // add to list
+ aSupportedXmitTypes.push_back(itemtypeP);
+ }
+ // next
+ aXmitTypeListP=aXmitTypeListP->next;
+ }
+} // TSyncDataStore::registerTypes
+
+
+/* end of TSyncDataStore implementation */
+
+// eof
diff --git a/src/sysync/syncdatastore.h b/src/sysync/syncdatastore.h
new file mode 100755
index 0000000..d39fc8c
--- /dev/null
+++ b/src/sysync/syncdatastore.h
@@ -0,0 +1,145 @@
+/*
+ * File: SyncDataStore.h
+ *
+ * Author: Lukas Zeller (luz@synthesis.ch)
+ *
+ * TSyncDataStore
+ * <describe here>
+ *
+ * Copyright (c) 2001-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ * 2001-xx-xx : luz : created
+ *
+ */
+
+#ifndef SyncDataStore_H
+#define SyncDataStore_H
+
+#include "syncitemtype.h"
+
+namespace sysync {
+
+
+// forward
+class TSyncItemType;
+class TSyncSession;
+
+
+class TSyncDataStore
+{
+private:
+ void init(TSyncSession *aSessionP); // internal init
+ void InternalResetDataStore(void); // reset for re-use without re-creation
+public:
+ TSyncDataStore(TSyncSession *aSessionP);
+ TSyncDataStore(TSyncSession *aSessionP, const char *aName, uInt32 aCommonSyncCapMask=0);
+ virtual ~TSyncDataStore();
+ virtual void engResetDataStore(void) { InternalResetDataStore(); };
+ virtual void announceAgentDestruction(void) { /* nop */ }; ///< called while agent is still fully ok, so we must clean up such that later call of destructor does NOT access agent any more
+ // set preferred type support
+ void setPreferredTypes(TSyncItemType *aRxItemTypeP, TSyncItemType *aTxItemTypeP=NULL);
+ // access datastore
+ // - get name (as used in path)
+ const char *getName(void) { return fName.c_str(); }
+ // - return display name (descriptive)
+ virtual const char *getDisplayName(void)
+ #ifndef MINIMAL_CODE
+ =0; // implemented in derivates
+ #else
+ { return getName(); }; // just return normal name
+ #endif
+ // - check if this datastore is accessible with given URI
+ // NOTE: URI might include path elements or CGI params that are
+ // access options to the database; derived classes might
+ // therefore base identity check on more than simple name match
+ virtual uInt16 isDatastore(const char *aDatastoreURI);
+ // - returns if specified type is used by this datastore
+ bool doesUseType(TSyncItemType *aSyncItemType, TTypeVariantDescriptor *aVariantDescP=NULL);
+ // - get common types to send to or receive from another datastore
+ TSyncItemType *getTypesForTxTo(TSyncDataStore *aDatastoreP, TSyncItemType **aCorrespondingTypePP=NULL);
+ TSyncItemType *getTypesForRxFrom(TSyncDataStore *aDatastoreP, TSyncItemType **aCorrespondingTypePP=NULL);
+ // - get datastore's type for receive / send of specified type
+ TSyncItemType *getReceiveType(TSyncItemType *aSyncItemTypeP);
+ TSyncItemType *getReceiveType(const char *aType, const char *aVers);
+ TSyncItemType *getSendType(TSyncItemType *aSyncItemTypeP);
+ TSyncItemType *getSendType(const char *aType, const char *aVers);
+ virtual TTypeVariantDescriptor getVariantDescForType(TSyncItemType *aItemTypeP) { return NULL; };
+ // - get preferred Item types
+ TSyncItemType *getPreferredRxItemType(void) { return fRxPrefItemTypeP; };
+ TSyncItemType *getPreferredTxItemType(void) { return fTxPrefItemTypeP; };
+ // - get max GUID size
+ sInt32 getMaxGUIDSize(void) { return fMaxGUIDSize; };
+ // - memory limits
+ sInt64 getFreeMemory(void) { return fFreeMemory; };
+ sInt64 getMaxMemory(void) { return fMaxMemory; };
+ sInt64 getFreeID(void) { return fFreeID; };
+ sInt64 getMaxID(void) { return fMaxID; };
+ // - session
+ TSyncSession *getSession(void) { return fSessionP; };
+ // description structure of datastore (NULL if not available)
+ // - get description structure, but ownership remains at TSyncDataStore
+ SmlDevInfDatastorePtr_t getDatastoreDevinf(bool aAsServer, bool aWithoutCTCapProps);
+ // - set description structure
+ virtual bool setDatastoreDevInf(
+ SmlDevInfDatastorePtr_t /* aDataStoreDevInfP */, // the datastore DevInf
+ TSyncItemTypePContainer & /* aLocalItemTypes */, // list to look up local types (for reference)
+ TSyncItemTypePContainer & /* aNewItemTypes */ // list to add analyzed types if not already there
+ ) { return true; /* nop for base class */ };
+ // helpers
+ // - returns session zones
+ GZones *getSessionZones(void);
+ // - return pure relative (item) URI (removes absolute part or ./ prefix)
+ virtual const char *DatastoreRelativeURI(const char *aURI) = 0;
+ #ifdef SYDEBUG
+ TDebugLogger *getDbgLogger(void);
+ uInt32 getDbgMask(void);
+ #endif
+protected:
+ // obtain new datastore info, returns NULL if none available
+ virtual SmlDevInfDatastorePtr_t newDevInfDatastore(bool /* aAsServer */, bool /* aWithoutCTCapProps */) { return NULL; } // no description in base class
+ SmlDevInfXmitListPtr_t newXMitListDevInf(TSyncItemTypePContainer &aTypeList);
+ // obtain Sync Cap mask, defaults to value passed at creation by session
+ virtual uInt32 getSyncCapMask(void) { return fCommonSyncCapMask; };
+ // SyncML-exposed name (SourceRef in DevInf) of the datastore
+ string fName;
+ // Maximal size of GUIDs sent to that (remote) datastore, so if real local GUID
+ // is longer, temporary GUIDs that meet MaxGUIDSize must be created by the server.
+ sInt32 fMaxGUIDSize;
+ // Information about datastore memory (could be enormous, so provide longlongs here)
+ sInt64 fMaxMemory; // maximum number bytes for datastore
+ sInt64 fFreeMemory; // number of free bytes
+ sInt64 fMaxID; // maximum number of ID
+ sInt64 fFreeID; // free IDs
+public:
+ // Type of items in this datastore (read-only, can be used by multiple Datastores simultaneously)
+ // - receiving types (also used as default item type)
+ TSyncItemType *fRxPrefItemTypeP;
+ TSyncItemTypePContainer fRxItemTypes;
+ // - sending types
+ TSyncItemType *fTxPrefItemTypeP;
+ TSyncItemTypePContainer fTxItemTypes;
+protected:
+ /*
+ // devInf <DataStore> representation
+ SmlDevInfDatastorePtr_t fDataStoreDevInfP;
+ */
+ // session
+ TSyncSession *fSessionP;
+ // common (session) Sync capabilities
+ uInt32 fCommonSyncCapMask;
+ // type registering
+ void registerTypes(
+ TSyncItemTypePContainer &aSupportedXmitTypes,
+ SmlDevInfXmitListPtr_t aXmitTypeList,
+ TSyncItemTypePContainer &aLocalItemTypes, // list to look up local types (for reference)
+ TSyncItemTypePContainer &aNewItemTypes, // list to add analyzed types if not already there
+ TSyncDataStore *aRelatedDatastoreP
+ );
+}; // TSyncDataStore
+
+
+} // namespace sysync
+
+#endif // SyncDataStore_H
+
+// eof
diff --git a/src/sysync/syncexception.cpp b/src/sysync/syncexception.cpp
new file mode 100755
index 0000000..49179d8
--- /dev/null
+++ b/src/sysync/syncexception.cpp
@@ -0,0 +1,72 @@
+/*
+ * File: SyncException.cpp
+ *
+ * Author: Lukas Zeller (luz@synthesis.ch)
+ *
+ * TSyncException...
+ * SySync Exception classes
+ *
+ * Copyright (c) 2001-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ * 2001-05-28 : luz : created
+ *
+ */
+
+// includes
+#include "prefix_file.h"
+
+#include "sysync.h"
+#include "syncexception.h"
+
+
+
+using namespace sysync;
+
+
+
+TSyncException::TSyncException(const char *aMsg1, localstatus aLocalStatus) NOTHROW
+{
+ fMessage = aMsg1;
+ fLocalStatus = aLocalStatus;
+} // TSyncException::TSyncException
+
+
+TSyncException::TSyncException(localstatus aLocalStatus) NOTHROW
+{
+ fLocalStatus = aLocalStatus;
+ fMessage = "Exception due to local error status";
+} // TSyncException::TSyncException
+
+
+TSyncException::~TSyncException() NOTHROW
+{
+} // TSyncException::~TSyncException
+
+
+const char *TSyncException::what() const NOTHROW
+{
+ return fMessage.c_str();
+} // TSyncException::getMessage
+
+
+void TSyncException::setMsg(const char *p)
+{
+ fMessage=p;
+} // TSyncException::setMsg
+
+
+
+
+TSmlException::TSmlException(const char *aMsg, Ret_t aSmlError) NOTHROW
+{
+ const int msgsiz=256;
+ char msg[msgsiz];
+
+ fSmlError=aSmlError;
+ msg[0]=0;
+ snprintf(msg,msgsiz,"SyncML Toolkit error=0x%04hX, %s",(uInt16)fSmlError,aMsg);
+ setMsg(msg);
+} // TSmlException::TSmlException
+
+
+// eof
diff --git a/src/sysync/syncexception.h b/src/sysync/syncexception.h
new file mode 100755
index 0000000..f5463d9
--- /dev/null
+++ b/src/sysync/syncexception.h
@@ -0,0 +1,90 @@
+/*
+ * File: SyncException.cpp
+ *
+ * Author: Lukas Zeller (luz@synthesis.ch)
+ *
+ * TSyncException...
+ * SySync Exception classes
+ *
+ * Copyright (c) 2001-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ * 2001-05-28 : luz : created
+ *
+ */
+
+#ifndef SyncException_H
+#define SyncException_H
+
+using namespace std;
+
+
+namespace sysync {
+
+#if defined(WINCE) || defined(__EPOC_OS__)
+// eVC + EPOC cannot process throw() qualifier
+#define NOTHROW
+// eVC + EPOC has no exception base class
+class exception
+{
+public:
+ exception() {};
+ exception(const exception&) {};
+ exception& operator= (const exception&) {return *this;};
+ virtual ~exception() {};
+ virtual const char* what() const {return "exception";};
+};
+#else
+#define NOTHROW throw()
+#endif
+
+class TSyncException : public exception
+{
+ typedef exception inherited;
+public:
+ TSyncException(const char *aMsg1, localstatus aLocalStatus=LOCERR_EXCEPTION) NOTHROW;
+ TSyncException(localstatus aLocalStatus) NOTHROW;
+ TSyncException() NOTHROW { fLocalStatus=LOCERR_EXCEPTION; };
+ virtual ~TSyncException() NOTHROW;
+ virtual const char * what() const NOTHROW;
+ localstatus status(void) NOTHROW { return fLocalStatus; }
+protected:
+ void setMsg(const char *p);
+ string fMessage;
+private:
+ localstatus fLocalStatus;
+}; // TSyncException
+
+
+class TSmlException : public TSyncException
+{
+ typedef TSyncException inherited;
+public:
+ TSmlException(const char *aMsg, Ret_t aSmlError) NOTHROW;
+ Ret_t getSmlError(void) { return fSmlError; };
+private:
+ Ret_t fSmlError;
+}; // TSmlException
+
+
+
+class TStructException : public TSyncException
+{
+ typedef TSyncException inherited;
+public:
+ TStructException(const char *aMsg) NOTHROW: TSyncException (aMsg) {};
+}; // TStructException
+
+
+class TMemException : public TSyncException
+{
+ typedef TSyncException inherited;
+public:
+ TMemException(const char *aMsg) NOTHROW: TSyncException (aMsg) {};
+}; // TMemException
+
+
+} // namespace sysync
+
+#endif // SyncException_H
+
+// eof
diff --git a/src/sysync/syncitem.cpp b/src/sysync/syncitem.cpp
new file mode 100755
index 0000000..c3ef1d1
--- /dev/null
+++ b/src/sysync/syncitem.cpp
@@ -0,0 +1,98 @@
+/*
+ * File: SyncItem.cpp
+ *
+ * Author: Lukas Zeller (luz@synthesis.ch)
+ *
+ * TSyncItem
+ * Abstract Base class of all data containing items.
+ * TSyncItems always correspond to a TSyncItemType
+ * which holds type information and conversion
+ * features.
+ *
+ * Copyright (c) 2001-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ * 2001-06-18 : luz : created
+ *
+ */
+
+// includes
+#include "prefix_file.h"
+
+#include "sysync.h"
+#include "syncitem.h"
+#include "syncappbase.h"
+
+
+
+using namespace sysync;
+
+
+// equality mode names
+const char * const sysync::compareRelevanceNames[numEQmodes] = {
+ "never", // irrelevant, only for fields with really unimportant data (such as REV)
+ "conflict", // for conflict, all fields that have user data should use at least this
+ "slowsync", // for slow sync, all fields that must match for identifying records in slow sync
+ "always", // always relevant, fields that must always match (first-time sync match set)
+ "n/a"
+};
+
+/*
+ * Implementation of TSyncItem
+ */
+
+/* public TSyncItem members */
+
+
+TSyncItem::TSyncItem(TSyncItemType *aItemType)
+{
+ fSyncOp=sop_none;
+ fSyncItemTypeP=aItemType;
+} // TSyncItem::TSyncItem
+
+
+TSyncItem::~TSyncItem()
+{
+ // NOP
+} // TSyncItem::~TSyncItem
+
+
+// assignment (IDs and contents)
+TSyncItem& TSyncItem::operator=(TSyncItem &aSyncItem)
+{
+ // - IDs
+ setRemoteID(aSyncItem.getRemoteID());
+ setLocalID(aSyncItem.getLocalID());
+ // - syncop
+ setSyncOp(aSyncItem.getSyncOp());
+ // - Contents
+ replaceDataFrom(aSyncItem);
+ // done
+ return *this;
+} // TSyncItem::operator=
+
+
+// get session zones pointer
+GZones *TSyncItem::getSessionZones(void)
+{
+ return getSession() ? getSession()->getSessionZones() : NULL;
+} // TSyncItem::getSessionZones
+
+
+
+#ifdef SYDEBUG
+TDebugLogger *TSyncItem::getDbgLogger(void)
+{
+ // commands log to session's logger
+ return fSyncItemTypeP ? fSyncItemTypeP->getDbgLogger() : NULL;
+} // TSyncItem::getDbgLogger
+
+uInt32 TSyncItem::getDbgMask(void)
+{
+ if (!fSyncItemTypeP) return 0; // no session, no debug
+ return fSyncItemTypeP->getDbgMask();
+} // TSyncItem::getDbgMask
+#endif
+
+/* end of TSyncItem implementation */
+
+// eof
diff --git a/src/sysync/syncitem.h b/src/sysync/syncitem.h
new file mode 100755
index 0000000..a8d359b
--- /dev/null
+++ b/src/sysync/syncitem.h
@@ -0,0 +1,163 @@
+/*
+ * File: SyncItem.h
+ *
+ * Author: Lukas Zeller (luz@synthesis.ch)
+ *
+ * TSyncItem
+ * Abstract Base class of all data containing items.
+ * TSyncItems always correspond to a TSyncItemType
+ * which holds type information and conversion
+ * features.
+ *
+ * Copyright (c) 2001-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ * 2001-06-18 : luz : created
+ *
+ */
+
+#ifndef SyncItem_H
+#define SyncItem_H
+
+// includes
+#include "syncitemtype.h"
+
+
+namespace sysync {
+
+
+// default command size
+#define DEFAULTITEMSIZE 50;
+
+
+// equality relevance / mode
+typedef enum { // RELEVANCE COMPARE MODE
+ eqm_none, // not relevant at all (*) compares ALL fields, even not-relevant ones
+ eqm_conflict, // relevant for conflicts only compares all somehow relevant fields for conflict comparison
+ eqm_slowsync, // relevant for slow sync compares only fields relevant for slow sync
+ eqm_always, // always relevant (e.g firsttime) compares only always-relevant fields (first time slowsync)
+ eqm_nocompare // n/a prevent equality test in compareWith totally
+} TEqualityMode;
+// (*) Note: eqm_none fields will be silently merged in mergeWith(), that is, if no other
+// fields are merged, mergeWith will report "nothing merged" even if eqm_none field might
+// be modified
+const short numEQmodes = eqm_nocompare-eqm_none+1;
+
+extern const char * const compareRelevanceNames[numEQmodes];
+
+
+// merge options
+#define mem_none -1 // do not merge at all
+#define mem_fillempty -2 // fill empty fields of winning item with loosig item's field contents
+#define mem_addunassigned -3 // add fields of loosing item to winning item if it has this field not yet assigned (not just empty)
+#define mem_concat 0 // simply concatenate field contents
+// NOTE: any positive char constant means intelligent merge using given char as separator
+
+// forward
+class TLocalEngineDS;
+
+class TSyncItem
+{
+public:
+ TSyncItem(TSyncItemType *aItemType=NULL);
+ virtual ~TSyncItem();
+ // access to type
+ virtual uInt16 getTypeID(void) const { return ity_syncitem; };
+ virtual bool isBasedOn(uInt16 aItemTypeID) const { return aItemTypeID==ity_syncitem; };
+ // get session pointer
+ TSyncSession *getSession(void) { return fSyncItemTypeP ? fSyncItemTypeP->getSession() : NULL; };
+ // get session zones pointer
+ GZones *getSessionZones(void);
+ // assignment (IDs and contents)
+ virtual TSyncItem& operator=(TSyncItem &aSyncItem);
+ // access to IDs
+ // - remote ID
+ const char *getRemoteID(void) { return fRemoteID.c_str(); };
+ void setRemoteID(const char *aRemoteID) { fRemoteID = aRemoteID; };
+ bool hasRemoteID(void) { return !fRemoteID.empty(); };
+ void clearRemoteID(void) { fRemoteID.erase(); };
+ // - local ID
+ const char *getLocalID(void) { return fLocalID.c_str(); };
+ void setLocalID(const char *aLocalID) { fLocalID = aLocalID; };
+ bool hasLocalID(void) { return !fLocalID.empty(); };
+ void clearLocalID(void) { fLocalID.erase(); };
+ virtual void updateLocalIDDependencies(void) { /* nop here */ }
+ // - changelog support
+ #ifdef CHECKSUM_CHANGELOG
+ virtual uInt16 getDataCRC(uInt16 crc=0, bool /* aEQRelevantOnly */=false) { return crc; /* always empty */ };
+ #endif
+ // access to operation
+ TSyncOperation getSyncOp(void) { return fSyncOp; };
+ void setSyncOp(TSyncOperation aSyncOp) { fSyncOp=aSyncOp; };
+ // access to visibility
+ // object filtering
+ #ifdef OBJECT_FILTERING
+ // New style generic filtering
+ // - check if item passes filter and probably apply some modifications to it
+ virtual bool postFetchFiltering(TLocalEngineDS *aDatastoreP) { return true; } // without filters, just pass everything
+ // Old style filter expressions
+ // - test if item passes filter
+ virtual bool testFilter(const char *aFilterString)
+ { return (!aFilterString || *aFilterString==0); }; // without real test, only empty filterstring pass
+ // - make item pass filter
+ virtual bool makePassFilter(const char *aFilterString)
+ { return (!aFilterString || *aFilterString==0); }; // without real implementation, works only with no filter string
+ #endif
+ // compare abilities
+ // - test if comparable at all (correct types)
+ virtual bool comparable(TSyncItem & /* aItem */) { return false; };
+ // - test if comparison can find out newer (greater) item
+ // NOTE: this should reflect the actually possible sorting state
+ // related to current contents of the item (for example, a vCard
+ // that COULD be sorted if it HAD a REV date may not return true
+ // if the actual item does not have a REV property included.
+ virtual bool sortable(TSyncItem & /* aItem */) { return false; };
+ // clear item data (means not only empty values, but NO VALUES assigned)
+ virtual void cleardata(void) {};
+ // replace data contents from specified item (returns false if not possible)
+ // - aAvailable only: only replace contents actually available in aItem, leave rest untouched
+ // - aDetectCutOffs: handle case where aItem could have somhow cut-off data and prevent replacing
+ // complete data with cut-off version (e.g. mobiles like T39m with limited name string capacity)
+ virtual bool replaceDataFrom(TSyncItem & /* aItem */, bool /* aAvailableOnly */=false, bool /* aDetectCutoffs */=false, bool /* aAssignedOnly */=false, bool /* aTransferUnassigned */=false) { return true; }; // no data -> nop
+ // check item before processing it
+ virtual bool checkItem(TLocalEngineDS * /* aDatastoreP */) { return true; }; // default is: ok
+ // merge this item with specified item.
+ // Notes:
+ // - specified item is treated as loosing item, this item is winning item
+ // - also updates other item to make sure it is equal to the winning after the merge
+ // sets (but does not reset) change status of this and other item.
+ // Note that changes of non-relevant fields are not reported here.
+ virtual void mergeWith(TSyncItem & /* aItem */, bool &aChangedThis, bool &aChangedOther, TLocalEngineDS * /* aDatastoreP */) { aChangedThis=false; aChangedOther=false; }; // nop by default
+ // remote and local ID
+ string fRemoteID; // ID in remote party (if this is a server: LUID, GUID otherwise)
+ string fLocalID; // ID in this party (if this is a server: GUID, LUID otherwise)
+ // compare: returns 0 if equal, 1 if this > aItem, -1 if this < aItem
+ // SYSYNC_NOT_COMPARABLE if not equal and no ordering known
+ virtual sInt16 compareWith(
+ TSyncItem & /* aItem */,
+ TEqualityMode /* aEqMode */,
+ TLocalEngineDS * /* aDatastoreP */
+ #ifdef SYDEBUG
+ ,bool /* aDebugShow */=false
+ #endif
+ ) { return SYSYNC_NOT_COMPARABLE; };
+ #ifdef SYDEBUG
+ // show item contents for debug
+ virtual void debugShowItem(uInt32 aDbgMask=DBG_DATA) { /* nop */ };
+ // get debug channel
+ TDebugLogger *getDbgLogger(void);
+ uInt32 getDbgMask(void);
+ #endif
+protected:
+ // operation to be performed with this item at its destination
+ TSyncOperation fSyncOp;
+ TSyncItemType *fSyncItemTypeP;
+private:
+ // cast pointer to same type, returns NULL if incompatible
+ TSyncItem *castToSameTypeP(TSyncItem *aItemP) { return aItemP; } // all are compatible TSyncItem
+}; // TSyncItem
+
+} // namespace sysync
+
+#endif // SyncItem_H
+
+// eof
diff --git a/src/sysync/syncitemtype.cpp b/src/sysync/syncitemtype.cpp
new file mode 100755
index 0000000..ab509c5
--- /dev/null
+++ b/src/sysync/syncitemtype.cpp
@@ -0,0 +1,907 @@
+/*
+ * File: SyncItemType.cpp
+ *
+ * Author: Lukas Zeller (luz@synthesis.ch)
+ *
+ * TSyncItemType
+ * Type description and converter (template) for TSyncItem.
+ *
+ * Copyright (c) 2001-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ * 2001-05-16 : luz : created
+ *
+ */
+
+// includes
+#include "prefix_file.h"
+
+#include "sysync.h"
+#include "syncitemtype.h"
+#include "syncitem.h"
+#include "synccommand.h"
+#include "syncsession.h"
+
+#ifdef ZIPPED_BINDATA_SUPPORT
+ #include "zlib.h"
+#endif
+
+using namespace sysync;
+
+
+/*
+ * Implementation of TSyncItemType
+ */
+
+/* TSyncItemType members */
+
+void TSyncItemType::init(
+ TSyncSession *aSessionP,
+ TDataTypeConfig *aTypeConfigP,
+ const char *aCTType,
+ const char *aVerCT,
+ TSyncDataStore *aRelatedDatastoreP
+)
+{
+ // link to config
+ fTypeConfigP=aTypeConfigP;
+ // link to session
+ fSessionP=aSessionP;
+ // set type name and vers (if any)
+ fTypeName=aCTType;
+ if (aVerCT) fTypeVers=aVerCT;
+ // set relation to a specific datastore (if any)
+ fRelatedDatastoreP=aRelatedDatastoreP;
+
+ #if defined(ZIPPED_BINDATA_SUPPORT) && defined(SYDEBUG)
+ // data compression accounting
+ fRawDataBytes=0;
+ fZippedDataBytes=0;
+ #endif
+
+} // TSyncItemType::init
+
+
+TSyncItemType::TSyncItemType(
+ TSyncSession *aSessionP,
+ TDataTypeConfig *aTypeConfigP,
+ const char *aCTType,
+ const char *aVerCT,
+ TSyncDataStore *aRelatedDatastoreP
+)
+{
+ // only basic init
+ init(aSessionP,aTypeConfigP,aCTType,aVerCT,aRelatedDatastoreP);
+} // TSyncItemType::TSyncItemType
+
+
+TSyncItemType::~TSyncItemType()
+{
+ #if defined(ZIPPED_BINDATA_SUPPORT) && defined(SYDEBUG)
+ // show compression statistics, if any
+ if (fSessionP && fRawDataBytes && fZippedDataBytes) {
+ POBJDEBUGPRINTFX(fSessionP,DBG_HOT,(
+ "##### Type '%s', zippedbindata send statistics: raw=%ld, compressed=%ld, compressed down to %ld%%",
+ getTypeName(),
+ (long)fRawDataBytes,
+ (long)fZippedDataBytes,
+ (long)(fZippedDataBytes*100/fRawDataBytes)
+ ));
+ }
+ #endif
+} // TSyncItemType::~TSyncItemType
+
+
+
+// get session zones pointer
+GZones *TSyncItemType::getSessionZones(void)
+{
+ return getSession() ? getSession()->getSessionZones() : NULL;
+} // TSyncItemType::getSessionZones
+
+
+#ifdef SYDEBUG
+
+TDebugLogger *TSyncItemType::getDbgLogger(void)
+{
+ // commands log to session's logger
+ return fSessionP ? getSession()->getDbgLogger() : NULL;
+} // TSyncItemType::getDbgLogger
+
+uInt32 TSyncItemType::getDbgMask(void)
+{
+ if (!fSessionP) return 0; // no session, no debug
+ return fSessionP->getDbgMask();
+} // TSyncItemType::getDbgMask
+
+#endif
+
+
+
+
+// helper to create same-typed instance via base class
+TSyncItemType *TSyncItemType::newCopyForSameType(
+ TSyncSession *aSessionP, // the session
+ TSyncDataStore *aDatastoreP // the datastore
+)
+{
+ // create new itemtype of appropriate derived class type that can handle
+ // this type
+ MP_RETURN_NEW(TSyncItemType,DBG_OBJINST,"TSyncItemType",TSyncItemType(aSessionP,fTypeConfigP,getTypeName(),getTypeVers(),aDatastoreP));
+} // TSyncItemType::newCopyForSameType
+
+
+
+// check if type is supported
+// - if version specified is NULL, first name-matching flavour is returned
+// - if type has no version, it matches any versions given
+// - if aVersMustMatch is set, version must match (both w/o version is a match, too)
+bool TSyncItemType::supportsType(const char *aName, const char *aVers, bool aVersMustMatch)
+{
+ if (!aName) return false; // no support for unnamed
+ if (!aVers) aVers=""; // empty version
+ return (
+ strucmp(fTypeName.c_str(),aName)==0 &&
+ ( ((*aVers==0 || fTypeVers.empty()) && !aVersMustMatch) || strucmp(fTypeVers.c_str(),aVers)==0 )
+ );
+} // TSyncItemType::supportsType
+
+
+
+bool TSyncItemType::supportsType(SmlDevInfXmitPtr_t aXmitType, bool aVersMustMatch)
+{
+ if (!aXmitType) return false; // null type not found
+ return (
+ supportsType(
+ smlPCDataToCharP(aXmitType->cttype),
+ smlPCDataToCharP(aXmitType->verct),
+ aVersMustMatch
+ )
+ );
+} // TSyncItemType::supportsType
+
+
+
+/// @brief get CTCap entry
+/// @param aOnlyForDS[in]
+/// - if NULL, CTCap is generated suitable for all datastores
+/// - if not NULL, CTCap is generated specifically for the datastore passed
+const SmlDevInfCTCapPtr_t TSyncItemType::getCTCapDevInf(TLocalEngineDS *aOnlyForDS, TTypeVariantDescriptor aVariantDescriptor, bool aWithoutCTCapProps)
+{
+ SmlDevInfCTCapPtr_t ctcap=NULL;
+ // get item type and property description (part of <CTCap>), if any
+ // - first see if we have (and are allowed to show) property descriptions at all
+ SmlDevInfCTDataPropListPtr_t proplistP = NULL;
+ if (fSessionP->fShowCTCapProps && !aWithoutCTCapProps)
+ proplistP = newCTDataPropList(aVariantDescriptor);
+ // - and if we should report field level replace capability to remote
+ bool acceptsFieldLevel = getSession()->getSyncMLVersion()>=syncml_vers_1_2 && canAcceptFieldLevelUpdates();
+ if (proplistP || acceptsFieldLevel) {
+ // there are properties available for this item, create DevInfCTCap
+ ctcap = SML_NEW(SmlDevInfCTCap_t);
+ // - add type name
+ ctcap->cttype=newPCDataString(getTypeName());
+ // - for DS 1.2, add the VerCT
+ if (getSession()->getSyncMLVersion()>=syncml_vers_1_2)
+ ctcap->verct=newPCDataString(getTypeVers());
+ else
+ ctcap->verct=NULL;
+ // - init the flags
+ ctcap->flags= acceptsFieldLevel ? SmlDevInfFieldLevel_f : 0;
+ // - add property list
+ ctcap->prop=proplistP;
+ }
+ return ctcap;
+} // TSyncItemType::getCTCapDevInf
+
+
+// intended for creating SyncItemTypes for remote databases from
+// transmitted DevInf.
+bool TSyncItemType::analyzeCTCap(SmlDevInfCTCapPtr_t aCTCapP)
+{
+ // Note: derived classes will eventually get some type-related info out of the CTCaps
+ return true;
+} // TSyncItemType::analyzeCTCap
+
+
+/// @brief copy CTCap derived info from another SyncItemType
+/// @return false if item not compatible
+/// @note required to create remote type variants from ruleMatch type alternatives
+bool TSyncItemType::copyCTCapInfoFrom(TSyncItemType &aSourceItem)
+{
+ // no generic CTCap info to copy
+ return true;
+} // TSyncItemType::copyCTCapInfoFrom
+
+
+
+// - static function to search type in a TSyncItemTypePContainer
+TSyncItemType *TSyncItemType::findTypeInList(
+ TSyncItemTypePContainer &aList,
+ const char *aName, const char *aVers,
+ bool aVersMustMatch,
+ bool aMustBeImplemented,
+ TSyncDataStore *aRelatedDatastoreP // if not NULL, type must be specific to this datastore (or unspecific)
+)
+{
+ TSyncItemTypePContainer::iterator pos;
+ // first priority: return type which is specific to aRelatedDatastoreP
+ for (pos=aList.begin(); pos!=aList.end(); ++pos) {
+ if ((*pos)->supportsType(aName,aVers,aVersMustMatch)) {
+ // found, return if implementation is ok and related to the correct datastore (or no relation requested)
+ if (
+ ( !aMustBeImplemented || (*pos)->isImplemented() ) &&
+ ( (aRelatedDatastoreP==NULL) || ((*pos)->getRelatedDatastore()==aRelatedDatastoreP) )
+ )
+ return (*pos); // return it
+ }
+ }
+ if (aRelatedDatastoreP) {
+ // second priority: return type which is expressedly not specific to a datastore
+ for (pos=aList.begin(); pos!=aList.end(); ++pos) {
+ if ((*pos)->supportsType(aName,aVers,aVersMustMatch)) {
+ // found, return if implementation is ok and related to the correct datastore
+ if (
+ (!aMustBeImplemented || (*pos)->isImplemented()) &&
+ ((*pos)->getRelatedDatastore()==NULL)
+ )
+ return (*pos); // return it
+ }
+ }
+ }
+ return NULL; // not found
+} // static TSyncItemType::findTypeInList
+
+
+// - static function to search type in a TSyncItemTypePContainer
+TSyncItemType *TSyncItemType::findTypeInList(
+ TSyncItemTypePContainer &aList,
+ SmlDevInfXmitPtr_t aXmitType, // name and version of type
+ bool aVersMustMatch,
+ bool aMustBeImplemented,
+ TSyncDataStore *aRelatedDatastoreP // if not NULL, type must be specific to THIS datastore (or unspecific)
+)
+{
+ return (
+ findTypeInList(
+ aList,
+ smlPCDataToCharP(aXmitType->cttype),
+ smlPCDataToCharP(aXmitType->verct),
+ aVersMustMatch,
+ aMustBeImplemented,
+ aRelatedDatastoreP
+ )
+ );
+} // static TSyncItemType::findTypeInList
+
+
+// - static function to add new or copied ItemType to passed list
+TSyncItemType *TSyncItemType::registerType(
+ TSyncSession *aSessionP,
+ SmlDevInfXmitPtr_t aXmitTypeP, // name and version of type
+ TSyncItemTypePContainer &aLocalItemTypes, // list to look up local types (for reference)
+ TSyncItemTypePContainer &aNewItemTypes, // list to add analyzed types if not already there
+ TSyncDataStore *aRelatedDatastoreP
+)
+{
+ return (
+ registerType(
+ aSessionP,
+ smlPCDataToCharP(aXmitTypeP->cttype),
+ smlPCDataToCharP(aXmitTypeP->verct),
+ aLocalItemTypes,
+ aNewItemTypes,
+ aRelatedDatastoreP
+ )
+ );
+} // static TSyncItemType::registerType
+
+
+// - static function to add new or copied ItemType to passed list
+// If type is already in aNewItemTypes, nothing will be added
+TSyncItemType *TSyncItemType::registerType(
+ TSyncSession *aSessionP,
+ const char *aName, const char *aVers, // name and version of type
+ TSyncItemTypePContainer &aLocalItemTypes, // list to look up local types (for reference) - aRelatedDatastoreP does NOT relate to the types here, as these are local ones
+ TSyncItemTypePContainer &aNewItemTypes, // list to add analyzed types if not already there
+ TSyncDataStore *aRelatedDatastoreP // if NULL, type is not related to a specific (remote!) datastore
+)
+{
+ if (!aName) SYSYNC_THROW(TSyncException("cannot register type w/o name"));
+ #ifdef SYDEBUG
+ if (aSessionP) {
+ // Show warning if no version
+ if (!aVers || *aVers==0) {
+ POBJDEBUGPRINTFX(aSessionP,DBG_REMOTEINFO,("WARNING: Registering type with no version specification!"));
+ }
+ }
+ #endif
+ // - check if type is already in the list (version must match, but implementation not required
+ TSyncItemType *newitemtypeP = findTypeInList(aNewItemTypes,aName,aVers,true,false,aRelatedDatastoreP);
+ if (newitemtypeP) {
+ POBJDEBUGPRINTFX(aSessionP,DBG_REMOTEINFO+DBG_EXOTIC,(
+ "Type already registered as '%s' Version='%s'",
+ newitemtypeP->getTypeName(),
+ newitemtypeP->hasTypeVers() ? newitemtypeP->getTypeVers() : "[none]"
+ ));
+ }
+ else {
+ // - check if one of the local item types supports this type
+ // Note: SySync engine only has one global list of supported types. It might contain multiple entries for
+ // the same type, when related to different datastores. The following search will find the first with
+ // matching name - this is no problem as long as all types with same name have the same CLASS type
+ // (as the only reason to look up in local types is to create the correct class' instance). When
+ // actually USED later by datastores, each datastore finds it's own INSTANCES via it's tx/rx(pref).
+ // No need for version match, but for implementation
+ TSyncItemType *localitemtypeP = findTypeInList(aLocalItemTypes,aName,aVers,false,true,NULL); // local types are not specific to a datastore
+ if (localitemtypeP) {
+ // %%% test for conforming version if type being registered has no version spec
+ // local itemtype exists that can support this type/vers
+ // - create new item type of same class (type-specialized derivate of TSyncItemType)
+ // - but relate it to the remote datastore (for DS1.2, or none for DS1.1 and earlier)
+ newitemtypeP = localitemtypeP->newCopyForSameType(aSessionP,aRelatedDatastoreP);
+ }
+ else {
+ // no local support of this type
+ // - create base class item just describing the type found
+ // (but without handling abilities)
+ newitemtypeP = new TSyncItemType(aSessionP,NULL,aName,aVers,aRelatedDatastoreP);
+ }
+ // add item to list
+ aNewItemTypes.push_back(newitemtypeP);
+ POBJDEBUGPRINTFX(aSessionP,DBG_REMOTEINFO+DBG_HOT,(
+ "Registered Type '%s' Version='%s', %s%s, related to remote datastore '%s'",
+ newitemtypeP->getTypeName(),
+ newitemtypeP->hasTypeVers() ? newitemtypeP->getTypeVers() : "[none]",
+ newitemtypeP->isImplemented() ? "implemented by local type " : "NOT implemented",
+ newitemtypeP->isImplemented() ? newitemtypeP->getTypeConfig()->getName() : "",
+ aRelatedDatastoreP ? aRelatedDatastoreP->getName() : "<none>"
+ ));
+ // Only show this if we REALLY have a local type-to-DS relation (NEVER so far - that is in 3.1.2.1)
+ if (newitemtypeP->isImplemented() && localitemtypeP && localitemtypeP->getRelatedDatastore()) {
+ POBJDEBUGPRINTFX(aSessionP,DBG_REMOTEINFO,(
+ "- Implementation is exclusively related to local datastore '%s'",
+ localitemtypeP->getRelatedDatastore()->getName()
+ ));
+ }
+ } // if not already in list
+ return newitemtypeP;
+} // static TSyncItemType::registerType
+
+
+/// @brief static function to analyze CTCap and add entries to passed list
+/// @todo %%% to be moved to TMimeDirItemType (as basic TSyncItemType does not really know the VERSION property)
+bool TSyncItemType::analyzeCTCapAndCreateItemTypes(
+ TSyncSession *aSessionP,
+ TRemoteDataStore *aRemoteDataStoreP, ///< if not NULL, this is the datastore to which this type is local (DS 1.2 case)
+ SmlDevInfCTCapPtr_t aCTCapP,
+ TSyncItemTypePContainer &aLocalItemTypes, ///< list to look up local types (for reference)
+ TSyncItemTypePContainer &aNewItemTypes ///< list to add analyzed types if not already there
+)
+{
+ bool versfound = false;
+
+ // create one TSyncItemType for each type contained in this CTCap
+ if (aCTCapP) {
+ // type name
+ const char *name = smlPCDataToCharP(aCTCapP->cttype);
+ // - from DS 1.2 on, we should have the verct without digging into properties
+ const char *vers = smlPCDataToCharP(aCTCapP->verct);
+ if (vers && *vers) {
+ // we have a non-empty version from verct (new DS 1.2 devinf)
+ #ifdef SYDEBUG
+ if (aSessionP) { PLOGDEBUGBLOCKFMTCOLL(aSessionP->getDbgLogger(),("RemoteCTCap", "Registering remote Type/Version from >=DS 1.2 style CTCap", "type=%s|version=%s", name, vers)); }
+ #endif
+ // valenum shows version supported
+ TSyncItemType *newitemtypeP =
+ registerType(
+ aSessionP,
+ name,vers, // name and version of type
+ aLocalItemTypes, // list to look up local types (for reference)
+ aNewItemTypes, // list to add it to
+ aRemoteDataStoreP
+ );
+ // now let new item process the CTCap
+ if (newitemtypeP) newitemtypeP->analyzeCTCap(aCTCapP);
+ #ifdef SYDEBUG
+ if (aSessionP) { PLOGDEBUGENDBLOCK(aSessionP->getDbgLogger(),"RemoteCTCap"); }
+ #endif
+ versfound=true;
+ }
+ else {
+ // - pre-DS 1.2 hack: try to find VERSION property
+ SmlDevInfCTDataPropListPtr_t prlP = aCTCapP->prop;
+ while (prlP) {
+ // get property name
+ const char *n = smlPCDataToCharP(prlP->data->prop->name);
+ if (n && strcmp(n,"VERSION")==0) {
+ // version property found. Now create flavour for each ValEnum
+ SmlPcdataListPtr_t velP = prlP->data->prop->valenum;
+ while (velP) {
+ vers = smlPCDataToCharP(velP->data);
+ if (vers) {
+ versfound=true; // found at least one
+ #ifdef SYDEBUG
+ if (aSessionP) { PLOGDEBUGBLOCKFMTCOLL(aSessionP->getDbgLogger(),("RemoteCTCap", "Registering remote Type/Version from old style CTCap w/o verct", "type=%s|version=%s", name, vers)); }
+ #endif
+ // valenum shows version supported
+ TSyncItemType *newitemtypeP =
+ registerType(
+ aSessionP,
+ name,vers, // name and version of type
+ aLocalItemTypes, // list to look up local types (for reference)
+ aNewItemTypes, // list to add it to
+ aRemoteDataStoreP
+ );
+ // now let new item process the CTCap
+ if (newitemtypeP) newitemtypeP->analyzeCTCap(aCTCapP);
+ #ifdef SYDEBUG
+ if (aSessionP) { PLOGDEBUGENDBLOCK(aSessionP->getDbgLogger(),"RemoteCTCap"); }
+ #endif
+ } // if version is not null
+ // next
+ velP=velP->next;
+ } // while valenums
+ break; // VERSION found, done with properties for now
+ } // while properties
+ prlP=prlP->next;
+ } // while
+ }
+ if (!versfound) {
+ // version is NULL, for a start, create type w/o version
+ #ifdef SYDEBUG
+ if (aSessionP) { PLOGDEBUGBLOCKFMT(aSessionP->getDbgLogger(),("RemoteCTCap", "Registering remote Type w/o version from CTCap", "type=%s|version=[none]", name)); }
+ #endif
+ TSyncItemType *newitemtypeP =
+ registerType(
+ aSessionP,
+ name,NULL, // name, but no version
+ aLocalItemTypes, // list to look up local types (for reference)
+ aNewItemTypes, // list to add it to
+ aRemoteDataStoreP
+ );
+ // now let new item process the CTCap
+ if (newitemtypeP) newitemtypeP->analyzeCTCap(aCTCapP);
+ #ifdef SYDEBUG
+ if (aSessionP) { PLOGDEBUGENDBLOCK(aSessionP->getDbgLogger(),"RemoteCTCap"); }
+ #endif
+ // this is ok, too
+ return true;
+ } // without version
+ }
+ return versfound;
+} // static TSyncItemType::analyzeCTCapAndCreateItemTypes
+
+
+
+// get type as transmit format
+SmlDevInfXmitPtr_t TSyncItemType::newXMitDevInf(void)
+{
+ SmlDevInfXmitPtr_t xmitP;
+
+ xmitP = SML_NEW(SmlDevInfXmit_t);
+ xmitP->cttype=newPCDataString(getTypeName());
+ xmitP->verct=newPCDataString(getTypeVers());
+
+ return xmitP;
+} // TSyncItemType::newXMitDevInf
+
+
+// static helper: get type list as transmit format
+SmlDevInfXmitListPtr_t TSyncItemType::newXMitListDevInf(
+ TSyncItemTypePContainer &aTypeList,
+ TSyncItemType *aDontIncludeP
+)
+{
+ SmlDevInfXmitListPtr_t resultP = NULL;
+ SmlDevInfXmitListPtr_t *listPP = &resultP;
+
+ TSyncItemTypePContainer::iterator pos;
+ for (pos=aTypeList.begin(); pos!=aTypeList.end(); ++pos) {
+ if (aDontIncludeP!=(*pos)) { // only if item in list does not match "except" item
+ // new list item
+ (*listPP) = SML_NEW(SmlDevInfXmitList_t);
+ (*listPP)->next=NULL;
+ // add type
+ (*listPP)->data=(*pos)->newXMitDevInf();
+ // next
+ listPP=&((*listPP)->next);
+ }
+ }
+ return resultP;
+} // TSyncItemType::newXMitListDevInf
+
+
+// create new empty sync item
+TSyncItem *TSyncItemType::newSyncItem(
+ TSyncItemType *aTargetItemTypeP, // the targeted type (for optimizing field lists etc.)
+ TLocalEngineDS *aLocalDataStoreP // the datastore
+)
+{
+ if (!aLocalDataStoreP) {
+ PDEBUGPRINTFX(DBG_ERROR,("Trying to call newSyncItem w/o datastore"));
+ return NULL;
+ }
+ // test if implemented at all
+ if (!isImplemented()) {
+ PDEBUGPRINTFX(DBG_ERROR,(
+ "Tried to create SyncItem for unimplemented SyncItemType '%s' (%s)",
+ fTypeName.c_str(),
+ fTypeVers.c_str()
+ ));
+ return NULL;
+ }
+ // check for compatibility with targeted type
+ if (!isCompatibleWith(aTargetItemTypeP)) {
+ PDEBUGPRINTFX(DBG_ERROR,(
+ "Local type (%s) is not assignment compatible with remote target type (%s) - probably multiple types with same name/version but different fieldlists in config",
+ getTypeConfig()->getName(),aTargetItemTypeP-> getTypeConfig()->getName()
+ ));
+ return NULL;
+ }
+ // get appropriate item
+ TSyncItem *syncitemP = internalNewSyncItem(aTargetItemTypeP,aLocalDataStoreP);
+ // return new item (if any)
+ return syncitemP;
+} // TSyncItemType::newSyncItem
+
+
+// create new sync item from SyncML data
+TSyncItem *TSyncItemType::newSyncItem(
+ SmlItemPtr_t aItemP, // SyncML toolkit item Data to be converted into SyncItem
+ TSyncOperation aSyncOp, // the operation to be performed with this item
+ TFmtTypes aFormat, // the format (normally fmt_chr)
+ TSyncItemType *aTargetItemTypeP, // the targeted type (for optimizing field lists etc.)
+ TLocalEngineDS *aLocalDataStoreP, // local datastore
+ TStatusCommand &aStatusCmd // status command that might be modified in case of error
+)
+{
+ TSyncItem *syncitemP = NULL;
+ // test if implemented at all
+ if (!isImplemented()) {
+ DEBUGPRINTFX(DBG_ERROR,(
+ "Tried to create SyncItem for unimplemented SyncItemType '%s' (%s)",
+ fTypeName.c_str(),
+ fTypeVers.c_str()
+ ));
+ aStatusCmd.setStatusCode(415);
+ ADDDEBUGITEM(aStatusCmd,"Known, but unimplemented Item Type");
+ return NULL;
+ }
+ PDEBUGBLOCKFMT(("Item_Parse","parsing SyncML item",
+ "SyncOp=%s|format=%s|LocalID=%s|RemoteID=%s",
+ SyncOpNames[aSyncOp],
+ encodingFmtNames[aFormat],
+ smlSrcTargLocURIToCharP(aItemP->target),
+ smlSrcTargLocURIToCharP(aItemP->source)
+ ));
+ SYSYNC_TRY {
+ // get appropriate item type
+ syncitemP = internalNewSyncItem(aTargetItemTypeP,aLocalDataStoreP);
+ // get local and remote IDs
+ if (syncitemP) {
+ // set operation type
+ syncitemP->setSyncOp(aSyncOp);
+ // an appropriate syncitem was created, set target and source now
+ // - we have received this item from remote, so target=myself, source=remote party
+ syncitemP->setLocalID(relativeURI(smlSrcTargLocURIToCharP(aItemP->target)));
+ #ifdef DONT_STRIP_PATHPREFIX_FROM_REMOTEIDS
+ syncitemP->setRemoteID(smlSrcTargLocURIToCharP(aItemP->source));
+ #else
+ syncitemP->setRemoteID(relativeURI(smlSrcTargLocURIToCharP(aItemP->source)));
+ #endif
+ PDEBUGPRINTFX(DBG_DATA+DBG_DETAILS,(
+ "Created new item of datatype '%s', localID='%s' remoteID='%s'",
+ getTypeConfig()->getName(),
+ syncitemP->getLocalID(),
+ syncitemP->getRemoteID()
+ ));
+ if (aSyncOp!=sop_delete && aSyncOp!=sop_archive_delete && aSyncOp!=sop_soft_delete && aSyncOp!=sop_copy && aSyncOp!=sop_move) {
+ // Item has data, parse it
+ // - uncompress data first if zippedbindata selected in type
+ #ifdef ZIPPED_BINDATA_SUPPORT
+ if (fTypeConfigP->fZippedBindata && fSessionP->getEncoding()==SML_WBXML && fSessionP->getSyncMLVersion()>=syncml_vers_1_1) {
+ // this type uses zipped bindata and we have WBXML (we cannot use zipped bindata in XML)
+ // - get input data
+ MemPtr_t zipBinPayload = NULL;
+ MemSize_t zipBinSize = 0;
+ MemPtr_t expandedPayload = NULL;
+ sInt32 expandedSize = 0;
+ if (aItemP->data) {
+ if ((zipBinSize=aItemP->data->length)>0) {
+ zipBinPayload = (MemPtr_t) aItemP->data->content;
+ }
+ }
+ // uncompress it, if we have data at all
+ if (zipBinPayload) {
+ // - <item><meta><maxobjsize> contains expanded size of payload for pre-allocating the buffer
+ SmlMetInfMetInfPtr_t metaP = smlPCDataToMetInfP(aItemP->meta);
+ if (metaP && metaP->maxobjsize) {
+ smlPCDataToLong(metaP->maxobjsize, expandedSize);
+ }
+ if (expandedSize>0) {
+ // we have data to expand AND we know how big the output will be (if we did not get MaxObjSize, this
+ // means that the data is not compressed
+ expandedPayload = (MemPtr_t) smlLibMalloc(expandedSize+1); // we need one more for the terminator
+ // uncompress the <data> payload with gzip
+ z_stream zipstream;
+ // - no special alloc
+ zipstream.zalloc=NULL;
+ zipstream.zfree=NULL;
+ zipstream.opaque=NULL;
+ // - no input yet
+ zipstream.next_in=NULL;
+ zipstream.avail_in=0;
+ // - init deflate
+ inflateInit2(&zipstream,15+32); // 15=default window size, +16=sets gzip detect flag (+32: sets gzip+zlib detect flag)
+ // - actually inflate item's <data>...
+ zipstream.next_in=zipBinPayload;
+ zipstream.avail_in=zipBinSize;
+ // - ...into new buffer
+ zipstream.next_out=expandedPayload;
+ zipstream.avail_out=expandedSize;
+ int err = inflate(&zipstream,Z_SYNC_FLUSH);
+ // - replace compressed by expanded data if everything's fine
+ if (err==Z_OK && zipstream.avail_in==0) {
+ // make sure data is null terminated
+ expandedPayload[expandedSize]=0;
+ // set expanded data in place of compressed
+ aItemP->data->length=expandedSize;
+ aItemP->data->content=expandedPayload;
+ // forget compressed data
+ smlLibFree(zipBinPayload);
+ }
+ else {
+ // if failed, get rid of unneeded buffer
+ smlLibFree(expandedPayload);
+ }
+ // clean up zip decompressor
+ inflateEnd(&zipstream);
+ } // if expected data size is known (from maxobjsize), i.e. was sent compressed
+ } // if input data available at all
+ } // if zippedBinData enabled
+ #endif
+ // convert payload (as a whole) from known format encodings
+ if (aFormat==fmt_b64) {
+ MemSize_t origSize = aItemP->data->length;
+ cAppCharP origData = (cAppCharP)aItemP->data->content;
+ if (origSize) {
+ // something to decode, do it and replace original content
+ aItemP->data->content = b64::decode(origData, origSize, (uInt32 *)&(aItemP->data->length));
+ // we don't need the original data any more
+ sysync_free((void *)origData);
+ }
+ }
+ // convert payload (as a whole) from UTF16 (Unicode) to UTF-8
+ if (fTypeConfigP->fUseUTF16) {
+ // get original size
+ MemSize_t origSize = aItemP->data->length;
+ cAppCharP origData = (cAppCharP)aItemP->data->content;
+ if (origSize) {
+ // we usually don't need more memory than the original
+ string utf8Payload;
+ // now convert
+ appendUTF16AsUTF8(
+ (const uInt16 *)origData,
+ origSize/2,
+ fTypeConfigP->fMSBFirst,
+ utf8Payload,
+ false, false
+ );
+ // replace contents
+ if (MemSize_t(utf8Payload.size())<origSize) origSize=utf8Payload.size();
+ // copy into existing contents, as UTF-8 is usually smaller
+ memcpy((void *)aItemP->data->content,utf8Payload.c_str(),origSize+1); // include terminator byte in copy
+ aItemP->data->length=origSize;
+ }
+ }
+ // fill in data, if any (virtual method implemented in descendant)
+ if (!internalFillInData(syncitemP,aItemP,aLocalDataStoreP,aStatusCmd)) {
+ // delete item, as it could not be filled properly
+ delete syncitemP;
+ PDEBUGPRINTFX(DBG_ERROR,("Could not fill item -> immediately deleted, none returned"));
+ syncitemP=NULL; // none any more
+ }
+ }
+ }
+ PDEBUGENDBLOCK("Item_Parse");
+ }
+ SYSYNC_CATCH (...)
+ PDEBUGENDBLOCK("Item_Parse");
+ SYSYNC_RETHROW;
+ SYSYNC_ENDCATCH
+ // return new item (if any)
+ return syncitemP;
+} // TSyncItemType::newSyncItem
+
+
+// - create new SyncML toolkit item from SyncItem
+SmlItemPtr_t TSyncItemType::newSmlItem(
+ TSyncItem *aSyncItemP, // the syncitem to be represented as SyncML
+ TLocalEngineDS *aLocalDatastoreP // local datastore
+)
+{
+ SmlItemPtr_t smlitemP = NULL;
+ PDEBUGBLOCKFMT(("Item_Generate","generating SyncML item",
+ "SyncOp=%s|LocalID=%s|RemoteID=%s",
+ SyncOpNames[aSyncItemP->getSyncOp()],
+ aSyncItemP->getLocalID(),
+ aSyncItemP->getRemoteID()
+ ));
+ SYSYNC_TRY {
+ // allocate an empty smlItem
+ smlitemP = newItem();
+ // data only if not delete, copy or map
+ TSyncOperation syncop = aSyncItemP->getSyncOp();
+ if (syncop!=sop_delete && syncop!=sop_archive_delete && syncop!=sop_soft_delete && syncop!=sop_copy) {
+ // let virtual method implemented in descendant fill in data and, eventually, meta.
+ if (!internalSetItemData(aSyncItemP,smlitemP,aLocalDatastoreP)) {
+ SYSYNC_THROW(TSyncException("newSmlItem: internalSetItemData() failed"));
+ }
+ // convert payload (as a whole) to UTF16 (Unicode)
+ if (fTypeConfigP->fUseUTF16) {
+ string utf16bytestream;
+ appendUTF8ToUTF16ByteString(
+ (cAppCharP)smlitemP->data->content,
+ utf16bytestream,
+ fTypeConfigP->fMSBFirst,
+ lem_none,
+ 0
+ );
+ // dispose old data
+ smlLibFree((appPointer)smlitemP->data->content);
+ // create new data block
+ smlitemP->data->content= (void*)( (const char *)smlLibMalloc(utf16bytestream.size()+2) );
+ smlitemP->data->length = utf16bytestream.size();
+ // copy contents
+ memcpy((appPointer)smlitemP->data->content,utf16bytestream.c_str(),utf16bytestream.size());
+ /*
+ // get original size
+ MemSize_t origSize = smlitemP->data->length;
+ cAppCharP origData = (cAppCharP)smlitemP->data->content;
+ if (origSize) {
+ // we need roughly twice as many bytes (probably less, but not more)
+ MemSize_t utf16size = origSize*2;
+ MemPtr_t utf16Payload = (MemPtr_t)smlLibMalloc(utf16size+2); // one more in case we detect end of buffer only after a surrogate pair
+ if (utf16Payload) {
+ // now convert
+ uInt32 ucs4;
+ uInt16 utf16,utf16_1;
+ appCharP outP = (appCharP)utf16Payload;
+ cAppCharP inP = (cAppCharP)smlitemP->data->content;
+ while (*inP && inP-origData<origSize && outP-(cAppCharP)utf16Payload<utf16size) {
+ inP=UTF8toUCS4(inP, ucs4);
+ if (ucs4==0) break; // error
+ utf16_1 = UCS4toUTF16(ucs4,utf16);
+ if (fTypeConfigP->fMSBFirst) {
+ // Motorola order
+ if (utf16_1) {
+ *(outP++) = (utf16_1 >> 8) & 0xFF;
+ *(outP++) = utf16_1 & 0xFF;
+ }
+ *(outP++) = (utf16 >> 8) & 0xFF;
+ *(outP++) = utf16 & 0xFF;
+ }
+ else {
+ // Intel order
+ if (utf16_1) {
+ *(outP++) = (utf16_1 >> 8) & 0xFF;
+ *(outP++) = utf16_1 & 0xFF;
+ }
+ *(outP++) = utf16 & 0xFF;
+ *(outP++) = (utf16 >> 8) & 0xFF;
+ }
+ }
+ // replace contents
+ smlitemP->data->length=(MemPtr_t)outP-utf16Payload;
+ smlitemP->data->content=utf16Payload;
+ // forget original data
+ smlLibFree((void *)origData);
+ }
+ }
+ */
+ }
+ // compress data if zippedbindata selected in type
+ #ifdef ZIPPED_BINDATA_SUPPORT
+ if (fTypeConfigP->fZippedBindata && fSessionP->getEncoding()==SML_WBXML && fSessionP->getSyncMLVersion()>=syncml_vers_1_1) {
+ // this type uses zipped bindata and we have WBXML (we cannot use zipped bindata in XML)
+ // compress the <data> payload with gzip if there IS any data
+ MemSize_t expandedSize = 0;
+ MemPtr_t expandedPayload = NULL;
+ MemPtr_t zipBinPayload = NULL;
+ // - get expanded data
+ if (smlitemP->data) {
+ if ((expandedSize=smlitemP->data->length)>0) {
+ expandedPayload = (MemPtr_t) smlitemP->data->content;
+ }
+ }
+ if (expandedPayload) {
+ // there is data to send - zip it and send it as binary
+ // - assume output will not be bigger than input
+ zipBinPayload = (MemPtr_t) smlLibMalloc(expandedSize);
+ if (zipBinPayload) {
+ // compress the <data> payload with gzip
+ z_stream zipstream;
+ // - no special alloc
+ zipstream.zalloc=NULL;
+ zipstream.zfree=NULL;
+ zipstream.opaque=NULL;
+ // - no input yet
+ zipstream.next_in=NULL;
+ zipstream.avail_in=0;
+ // - init deflate
+ int comprLevel = fTypeConfigP->fZipCompressionLevel;
+ if (comprLevel>9 || comprLevel<0)
+ comprLevel=Z_DEFAULT_COMPRESSION;
+ deflateInit(&zipstream,comprLevel);
+ // - actually deflate item's <data>...
+ zipstream.next_in=expandedPayload;
+ zipstream.avail_in=expandedSize; // not more than uncompressed version would take
+ // - ...into new buffer
+ zipstream.next_out=zipBinPayload;
+ zipstream.avail_out=expandedSize;
+ int err = deflate(&zipstream,Z_SYNC_FLUSH);
+ // - replace compressed by expanded data if everything's fine
+ if (err==Z_OK && zipstream.avail_in==0) {
+ // compression ok, set compressed data in place of original
+ smlitemP->data->length=zipstream.total_out;
+ smlitemP->data->content=zipBinPayload;
+ // forget original data
+ smlLibFree(expandedPayload);
+ // only if we have succeeded, we will set the maxobjsize and format Otherwise, we'll send uncompressed data
+ SmlMetInfMetInfPtr_t metaP = smlPCDataToMetInfP(smlitemP->meta);
+ if (!metaP) {
+ // we have no meta yet at all, create it first
+ smlitemP->meta = newMeta();
+ metaP = (SmlMetInfMetInfPtr_t)(smlitemP->meta->content);
+ }
+ // - store expanded size in meta maxobjsize
+ if (metaP->maxobjsize)
+ smlFreePcdata(metaP->maxobjsize); // delete if there is already something here
+ metaP->maxobjsize=newPCDataLong(expandedSize);
+ // - set format to "bin" as a flag that contents are compressed
+ if (metaP->format)
+ smlFreePcdata(metaP->format); // delete if there is already something here
+ metaP->format=newPCDataString("bin");
+ }
+ else {
+ // if failed, get rid of unneeded buffer
+ smlLibFree(zipBinPayload);
+ }
+ // update statistics
+ #ifdef SYDEBUG
+ POBJDEBUGPRINTFX(fSessionP,DBG_DATA,(
+ "zippedbindata item statistics: raw=%ld, compressed=%ld, compressed down to %ld%%",
+ expandedSize,
+ smlitemP->data->length,
+ smlitemP->data->length*100/expandedSize
+ ));
+ fRawDataBytes+=expandedSize;
+ fZippedDataBytes+=smlitemP->data->length;
+ #endif
+ // clean up zip compressor
+ deflateEnd(&zipstream);
+ } // can allocate
+ } // if any payload at all
+ }
+ #endif
+ }
+ // set source and target (AFTER setting data, as item data might influence item ID
+ // in some special cases (as for Nokia 9500-style email)
+ // - we will send this item to remote, so target=remote party, source=myself
+ smlitemP->target=newOptLocation(aSyncItemP->getRemoteID());
+ smlitemP->source=newOptLocation(aSyncItemP->getLocalID());
+ PDEBUGENDBLOCK("Item_Generate");
+ }
+ SYSYNC_CATCH (...)
+ PDEBUGENDBLOCK("Item_Generate");
+ SYSYNC_RETHROW;
+ SYSYNC_ENDCATCH
+ // return smlItem
+ return smlitemP;
+} // TSyncItemType::newSmlItem
+
+
+
+/* end of TSyncItemType implementation */
+
+// eof
diff --git a/src/sysync/syncitemtype.h b/src/sysync/syncitemtype.h
new file mode 100755
index 0000000..62a9d52
--- /dev/null
+++ b/src/sysync/syncitemtype.h
@@ -0,0 +1,262 @@
+/*
+ * File: SyncItemType.h
+ *
+ * Author: Lukas Zeller (luz@synthesis.ch)
+ *
+ * TSyncItemType
+ * Type description and converter (template) for TSyncItem.
+ *
+ * Copyright (c) 2001-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ * 2001-05-16 : luz : created
+ *
+ */
+
+#ifndef SyncItemType_H
+#define SyncItemType_H
+
+#include "sysync_globs.h"
+
+namespace sysync {
+
+
+// for hardcoded config, we do not use any C++ strings
+#ifndef CONFIGURABLE_TYPE_SUPPORT
+ // string pointers only
+ #define TCFG_STRING const char *
+ #define TCFG_CSTR(s) s
+ #define TCFG_ASSIGN(s,c) { if (c) s=c; else s=""; }
+ #define TCFG_CLEAR(s) s=""
+ #define TCFG_ISEMPTY(s) (!s || *s==0)
+ #define TCFG_SIZE(s) (s ? strlen(s) : 0)
+#else
+ // C++ strings
+ #define TCFG_STRING string
+ #define TCFG_CSTR(s) s.c_str()
+ #define TCFG_ASSIGN(s,c) { if (c) s=c; else s.erase(); }
+ #define TCFG_CLEAR(s) s.erase()
+ #define TCFG_ISEMPTY(s) s.empty()
+ #define TCFG_SIZE(s) s.size()
+#endif
+
+
+// forward
+class TSyncSession;
+class TSyncItemType;
+class TSyncItem;
+class TStatusCommand;
+class TDataTypeConfig;
+class TLocalEngineDS;
+class TRemoteDataStore;
+class TSyncDataStore;
+
+// container types
+typedef std::list<TSyncItemType*> TSyncItemTypePContainer; // contains item types
+
+// type variant descriptor (for now, a simple pointer, if needed we can make it a class later)
+typedef void * TTypeVariantDescriptor;
+
+const uInt16 ity_syncitem = 0; // must be unique
+
+class TSyncItemType {
+private:
+ void init(
+ TSyncSession *aSessionP,
+ TDataTypeConfig *aTypeConfigP,
+ const char *aCTType,
+ const char *aVerCT,
+ TSyncDataStore *aRelatedDatastoreP
+ );
+public:
+ // constructor
+ TSyncItemType(
+ TSyncSession *aSessionP,
+ TDataTypeConfig *aTypeConfigP,
+ const char *aCTType,
+ const char *aVerCT,
+ TSyncDataStore *aRelatedDatastoreP
+ );
+ // destructor
+ virtual ~TSyncItemType();
+ // access to type
+ virtual uInt16 getTypeID(void) const { return ity_syncitem; };
+ virtual bool isBasedOn(uInt16 aItemTypeID) const { return aItemTypeID==ity_syncitem; };
+ // differentiation between implemented and just descriptive TSyncTypeItems
+ virtual bool isImplemented(void) { return false; }; // base class is descriptive only
+ // compatibility (=assignment compatibility between items based on these types)
+ virtual bool isCompatibleWith(TSyncItemType *aReferenceType) { return this==aReferenceType; } // compatible if same type
+ // get session pointer
+ TSyncSession *getSession(void) { return fSessionP; };
+ // get session zones pointer
+ GZones *getSessionZones(void);
+ // ret related datastore (can be NULL for session-global types like before DS 1.2)
+ TSyncDataStore *getRelatedDatastore(void) { return fRelatedDatastoreP; };
+ // helper to create same-typed instance via base class
+ // MUST BE IMPLEMENTED IN ALL DERIVED CLASSES!
+ virtual TSyncItemType *newCopyForSameType(
+ TSyncSession *aSessionP, // the session
+ TSyncDataStore *aDatastoreP // the datastore
+ );
+ // apply default limits to type (e.g. from hard-coded template in config)
+ virtual void addDefaultTypeLimits(void) { /* nop */ };
+ // Prepare datatype for use with a datastore. This might be implemented
+ // in derived classes to initialize the datastore's script context etc.
+ virtual void initDataTypeUse(TLocalEngineDS * /* aDatastoreP */, bool /* aForSending */, bool /* aForReceiving */) { /* nop */ };
+ // Filtering
+ // - add keywords and property names to filterCap
+ virtual void addFilterCapPropsAndKeywords(SmlPcdataListPtr_t &aFilterKeywords, SmlPcdataListPtr_t &aFilterProps, TTypeVariantDescriptor aVariantDesc) { /* nop */ };
+ // - check for special filter keywords the type might want to handle directly
+ virtual bool checkFilterkeywordTerm(
+ cAppCharP aIdent, bool aAssignToMakeTrue,
+ cAppCharP aOp, bool aCaseInsensitive,
+ cAppCharP aVal, bool aSpecialValue
+ ) { return true; /* we do not handle this specially, handle at DS level or include into filter expression */ };
+ // - init new-style filtering, returns flag if needed at all
+ virtual void initPostFetchFiltering(bool &aNeeded, bool &aNeededForAll, TLocalEngineDS * /* aDatastoreP */) { aNeeded=false; aNeededForAll=false; };
+ // item read and write
+ // - test if item could contain cut-off data (e.g. because of field size restrictions)
+ // compared to specified reference item
+ virtual bool mayContainCutOffData(TSyncItemType * /* aReferenceType */) { return false; }; // generally, no
+ // - try to extract a version string from actual item data, NULL if none
+ virtual bool versionFromData(SmlItemPtr_t /* aItemP */, string & /* aString */) { return false; };
+ #ifdef APP_CAN_EXPIRE
+ // test if modified date of item is hard-expired
+ virtual sInt32 expiryFromData(SmlItemPtr_t /* aItemP */, lineardate_t & /* aDat */) { return 0; }; // default to not expired
+ #endif
+ /// create new empty sync item
+ TSyncItem *newSyncItem(
+ TSyncItemType *aTargetItemTypeP, ///< the targeted type (for optimizing field lists etc.)
+ TLocalEngineDS *aLocalDataStoreP ///< local datastore
+ );
+ /// create new sync item from SyncML data
+ TSyncItem *newSyncItem(
+ SmlItemPtr_t aItemP, ///< SyncML toolkit item Data to be converted into SyncItem
+ TSyncOperation aSyncOp, ///< the operation to be performed with this item
+ TFmtTypes aFormat, ///< the format (normally fmt_chr)
+ TSyncItemType *aTargetItemTypeP, ///< the targeted type (for optimizing field lists etc.)
+ TLocalEngineDS *aLocalDataStoreP, ///< local datastore
+ TStatusCommand &aStatusCmd ///< status command that might be modified in case of error
+ );
+ /// create new SyncML toolkit item from SyncItem
+ SmlItemPtr_t newSmlItem(
+ TSyncItem *aSyncItemP, ///< the syncitem to be represented as SyncML
+ TLocalEngineDS *aLocalDatastoreP ///< local datastore
+ );
+ /// @brief get CTCap entry
+ /// @param aOnlyForDS[in]
+ /// - if NULL, CTCap is generated suitable for all datastores
+ /// - if not NULL, CTCap is generated specifically for the datastore passed
+ const SmlDevInfCTCapPtr_t getCTCapDevInf(TLocalEngineDS *aOnlyForDS, TTypeVariantDescriptor aVariantDescriptor, bool aWithoutCTCapProps);
+ /// @brief analyze CTCap for specific type
+ virtual bool analyzeCTCap(SmlDevInfCTCapPtr_t aCTCapP);
+ /// @brief copy CTCap derived info from another SyncItemType
+ virtual bool copyCTCapInfoFrom(TSyncItemType &aSourceItem);
+ /// @brief returns true if type is able to ACCEPT field level updates
+ virtual bool canAcceptFieldLevelUpdates(void) { return false; }; /* no by default */
+ // - static function to search type in a TSyncItemTypePContainer
+ static TSyncItemType *findTypeInList(
+ TSyncItemTypePContainer &aList,
+ const char *aName, const char *aVers,
+ bool aVersMustMatch,
+ bool aMustBeImplemented,
+ TSyncDataStore *aRelatedDatastoreP
+ );
+ static TSyncItemType *findTypeInList(
+ TSyncItemTypePContainer &aList,
+ SmlDevInfXmitPtr_t aXmitType, // name and version of type
+ bool aVersMustMatch,
+ bool aMustBeImplemented,
+ TSyncDataStore *aRelatedDatastoreP
+ );
+ // - static function to analyze CTCap and add entries to passed list
+ static bool analyzeCTCapAndCreateItemTypes(
+ TSyncSession *aSessionP,
+ TRemoteDataStore *aRemoteDataStoreP, // if not NULL, this is the datastore to which this type is local (DS 1.2 case)
+ SmlDevInfCTCapPtr_t aCTCapP,
+ TSyncItemTypePContainer &aLocalItemTypes, // list to look up local types (for reference)
+ TSyncItemTypePContainer &aNewItemTypes // list to add analyzed types if not already there
+ );
+ // - static function to add new or copied ItemType to passed list
+ static TSyncItemType *registerType(
+ TSyncSession *aSessionP,
+ const char *aName, const char *aVers, // name and version of type
+ TSyncItemTypePContainer &aLocalItemTypes, // list to look up local types (for reference)
+ TSyncItemTypePContainer &aNewItemTypes, // list to add analyzed types if not already there
+ TSyncDataStore *aRelatedDatastoreP
+ );
+ static TSyncItemType *registerType(
+ TSyncSession *aSessionP,
+ SmlDevInfXmitPtr_t aXmitTypeP, // name and version of type
+ TSyncItemTypePContainer &aLocalItemTypes, // list to look up local types (for reference)
+ TSyncItemTypePContainer &aNewItemTypes, // list to add analyzed types if not already there
+ TSyncDataStore *aRelatedDatastoreP
+ );
+ // static helper for creating rx/tx type lists
+ static SmlDevInfXmitListPtr_t newXMitListDevInf(
+ TSyncItemTypePContainer &aTypeList,
+ TSyncItemType *aDontIncludeP
+ );
+ // type support check
+ virtual bool supportsType(const char *aName, const char *aVers, bool aVersMustMatch=false);
+ bool supportsType(SmlDevInfXmitPtr_t aXmitType, bool aVersMustMatch=false);
+ // - get type name / vers
+ virtual cAppCharP getTypeName(sInt32 aMode=0) { return fTypeName.c_str(); };
+ virtual cAppCharP getTypeVers(sInt32 aMode=0) { return fTypeVers.c_str(); };
+ void setTypeVers(const char *aVers) { fTypeVers=aVers; };
+ bool hasTypeVers(void) { return !fTypeVers.empty(); };
+ // - read type as Rx/Tx entry
+ SmlDevInfXmitPtr_t newXMitDevInf(void);
+ // - get config pointer of type
+ TDataTypeConfig *getTypeConfig(void) { return fTypeConfigP; };
+ // - get debug
+ #ifdef SYDEBUG
+ TDebugLogger *getDbgLogger(void);
+ uInt32 getDbgMask(void);
+ #endif
+protected:
+ // methods
+ // obtain property list for type, returns NULL if none available
+ virtual SmlDevInfCTDataPropListPtr_t newCTDataPropList(TTypeVariantDescriptor aVariantDescriptor) { return NULL; } // no properties available
+ // Item data management
+ // - create new sync item of proper type.
+ // NOTE: aTargetItemTypeP is passed to allow creation of optimized items for
+ // reception by a specific target type (e.g. common field list optimization etc.)
+ virtual TSyncItem *internalNewSyncItem(
+ TSyncItemType * /* aTargetItemTypeP */,
+ TLocalEngineDS * /* aLocalDatastoreP */
+ ) { return NULL; } // no op in base class (returns no item)
+ // - fill in SyncML data (but leaves IDs empty)
+ virtual bool internalFillInData(
+ TSyncItem * /* aSyncItemP */, // SyncItem to be filled with data
+ SmlItemPtr_t /* aItemP */, // SyncML toolkit item Data to be converted into SyncItem (may be NULL if no data, in case of Delete or Map)
+ TLocalEngineDS * /* aLocalDatastoreP */, // local datastore
+ TStatusCommand & /* aStatusCmd */ // status command that might be modified in case of error
+ ) { return false; } // no op in base class (cannot fill)
+ // - sets data and meta from SyncItem data, but leaves source & target untouched
+ virtual bool internalSetItemData(
+ TSyncItem * /* aSyncItemP */, // the syncitem to be represented as SyncML
+ SmlItemPtr_t /* aItem */, // item with NULL meta and NULL data
+ TLocalEngineDS * /* aLocalDatastoreP */ // local datastore
+ ) { return false; } // no op in base class (leaves item untouched)
+ // session pointer
+ TSyncSession *fSessionP;
+ // the config for this type
+ TDataTypeConfig *fTypeConfigP;
+private:
+ // the related datastore (for DS 1.2)
+ TSyncDataStore *fRelatedDatastoreP;
+ // the item's type name and version
+ string fTypeName;
+ string fTypeVers;
+ #if defined(ZIPPED_BINDATA_SUPPORT) && defined(SYDEBUG)
+ sInt32 fRawDataBytes;
+ sInt32 fZippedDataBytes;
+ #endif
+}; // TSyncItemType
+
+
+} // namespace sysync
+
+#endif // SyncItemType_H
+
+// eof
diff --git a/src/sysync/syncml_globs.h b/src/sysync/syncml_globs.h
new file mode 100755
index 0000000..f2c003f
--- /dev/null
+++ b/src/sysync/syncml_globs.h
@@ -0,0 +1,77 @@
+/*
+ * File: syncml_globs.h
+ *
+ * Authors: Lukas Zeller (luz@synthesis.ch)
+ * Beat Forster (bfo@synthesis.ch)
+ *
+ * Global SyncML definitions/macros/constants
+ *
+ * Copyright (c) 2001-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ */
+
+#ifndef SYNCML_GLOBS_H
+#define SYNCML_GLOBS_H
+
+#include "generic_types.h"
+
+#ifdef __cplusplus
+
+#if defined _MSC_VER && !defined WINCE
+ #include <windows.h>
+#endif
+
+// Consistent support for Linux, MacOSX CW & XCode & Visual Studio
+#if defined __GNUC__ || defined _MSC_VER
+ #include <ctype.h>
+#else
+ #include <ctype>
+#endif
+
+// we need the namespace std
+using namespace std;
+namespace sysync {
+#endif
+
+// basic SyncML constants
+#define SYNCML_HDRCMDNAME "SyncHdr"
+
+// SyncML Encodings (to be appended to SYNCML_MIME_TYPE and SYNCML_DEVINF_META_TYPE
+#define SYNCML_ENCODING_XML "xml"
+#define SYNCML_ENCODING_WBXML "wbxml"
+// - spearator between MIME-Type and encoding string
+#define SYNCML_ENCODING_SEPARATOR "+"
+
+// SyncML content type constants
+#define SYNCML_MIME_TYPE "application/vnd.syncml" // plus encoding
+
+// SyncML charset
+#define SYNCML_DEFAULT_CHARSET "utf-8"
+
+// prefix for relative URIs
+#define URI_RELPREFIX "./"
+
+// SyncML DEVINF constants
+#define SYNCML_DEVINF_LOCNAME "Device Information"
+#define SYNCML_DEVINF_META_TYPE "application/vnd.syncml-devinf" // plus encoding !
+#define SYNCML_META_VERSION "syncml:metinf"
+
+// SyncML Filter grammars
+#define SYNCML_FILTERTYPE_CGI "syncml:filtertype-cgi"
+#define SYNCML_FILTERTYPE_CGI_VERS "1.0"
+#define SYNCML_FILTERTYPE_INCLUSIVE "INCLUSIVE"
+#define SYNCML_FILTERTYPE_EXCLUSIVE "EXCLUSIVE"
+
+
+// SyncML encodings
+// Note: SmlEncoding_t is defined in the RTK smldef.h
+#define numSyncMLEncodings (SML_XML-SML_UNDEF+1)
+
+#ifdef __cplusplus
+} // namespace sysync
+#endif
+
+#endif // SYNCML_GLOBS_H
+
+// eof
+
diff --git a/src/sysync/syncml_tk.h b/src/sysync/syncml_tk.h
new file mode 100755
index 0000000..4bf4a6f
--- /dev/null
+++ b/src/sysync/syncml_tk.h
@@ -0,0 +1,9 @@
+/* sysync SyncML toolkit includes */
+
+#ifndef SYNCML_TK_HPP
+#define SYNCML_TK_HPP
+
+#include
+
+
+#endif
diff --git a/src/sysync/syncserver.cpp b/src/sysync/syncserver.cpp
new file mode 100755
index 0000000..b055a2b
--- /dev/null
+++ b/src/sysync/syncserver.cpp
@@ -0,0 +1,1440 @@
+/*
+ * File: SyncServer.cpp
+ *
+ * Author: Lukas Zeller (luz@synthesis.ch)
+ *
+ * TSyncServer
+ * <describe here>
+ *
+ * Copyright (c) 2001-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ * 2001-05-07 : luz : Created
+ *
+ */
+
+// includes
+#include <errno.h>
+#include "prefix_file.h"
+
+#include "sysync.h"
+#include "syncserver.h"
+
+#ifdef SYSYNC_CLIENT
+ #error "SYSYNC_CLIENT must NOT be defined when compiling syncserver.cpp"
+#endif
+
+
+// %%% define to prevent GET command sent to Client when
+// package #1 did not contain DEVINF
+//#define NO_DEVINF_GET 1
+
+// %%% define to combine SYNC and MAP as one response package
+// %%%%%%% probably useless and totally wrong, but trying to
+// %%%%%%% get 9210 to work.
+//#define COMBINE_SYNCANDMAP
+
+// define to include a 222 alert in every response to a message
+// that does not include the <Final/> flag (otherwise, 222 Alert
+// will only be generated when no other command is to be sent back)
+//#define ALWAYS_CONTINUE222 1
+
+using namespace sysync;
+
+
+namespace sysync {
+
+// Support for SySync Diagnostic Tool
+#ifdef SYSYNC_TOOL
+
+
+// test login into database
+int testLogin(int argc, const char *argv[])
+{
+ if (argc<0) {
+ // help requested
+ CONSOLEPRINTF((" login [<username> <password>] [<deviceid>]"));
+ CONSOLEPRINTF((" test login to database with syncml user/password and optional deviceid"));
+ return EXIT_SUCCESS;
+ }
+
+ TSyncSession *sessionP = NULL;
+ const char *username = NULL;
+ const char *password = NULL;
+ const char *deviceid = "sysytool_test";
+
+ // check for argument
+ if (argc<2) {
+ // no user/password, test anonymous login
+ if (argc>0) deviceid = argv[0];
+ }
+ else {
+ // login with user/password
+ username = argv[0];
+ password = argv[1];
+ if (argc>2) {
+ // explicit device ID
+ deviceid = argv[2];
+ }
+ }
+
+ // get session to work with
+ sessionP =
+ static_cast<TSyncSessionDispatch *>(getSyncAppBase())->getSySyToolSession();
+
+ bool authok = false;
+
+ // try login
+ if (username) {
+ // real login with user and password
+ authok = sessionP->SessionLogin(username, password, sectyp_clearpass, deviceid);
+ }
+ else {
+ // anonymous - do a "login" with empty credentials
+ authok = sessionP->SessionLogin("anonymous", NULL, sectyp_anonymous, deviceid);
+ }
+
+ if (authok) {
+ CONSOLEPRINTF(("+++++ Successfully authorized"));
+ }
+ else {
+ CONSOLEPRINTF(("----- Authorisation failed"));
+ }
+
+ return authok;
+} // testLogin
+
+
+// convert user data into internal format and back
+int convertData(int argc, const char *argv[])
+{
+ if (argc<0) {
+ // help requested
+ CONSOLEPRINTF((" convert <datastore name> <data file, vcard or vcalendar etc.> [<explicit input type>] [<output type>]"));
+ CONSOLEPRINTF((" Convert data to internal format of specified datastore and back"));
+ return EXIT_SUCCESS;
+ }
+
+ TSyncSession *sessionP = NULL;
+ const char *datastore = NULL;
+ const char *rawfilename = NULL;
+ const char *inputtype = NULL;
+ const char *outputtype = NULL;
+
+ // check for argument
+ if (argc<2) {
+ CONSOLEPRINTF(("required datatype name and raw file name arguments"));
+ return EXIT_FAILURE;
+ }
+ datastore = argv[0];
+ rawfilename = argv[1];
+ if (argc>=3) {
+ // third arg is explicit input type
+ inputtype=argv[2];
+ }
+ outputtype=inputtype; // default to input type
+ if (argc>=4) {
+ // fourth arg is explicit output type
+ outputtype=argv[3];
+ }
+
+ // get session to work with
+ sessionP =
+ static_cast<TSyncSessionDispatch *>(getSyncAppBase())->getSySyToolSession();
+
+
+ // switch mimimal debugging on
+ sessionP->getDbgLogger()->setMask(sessionP->getDbgLogger()->getMask() | (DBG_PARSE+DBG_GEN));
+
+ // find datastore
+ TLocalEngineDS *datastoreP = sessionP->findLocalDataStore(datastore);
+ TSyncItemType *inputtypeP = NULL;
+ TSyncItemType *outputtypeP = NULL;
+ if (!datastoreP) {
+ CONSOLEPRINTF(("datastore type '%s' not found",datastore));
+ return EXIT_FAILURE;
+ }
+
+ // find input type
+ if (inputtype) {
+ // search in datastore
+ inputtypeP=datastoreP->getReceiveType(inputtype,NULL);
+ }
+ else {
+ // use preferred rx type
+ inputtypeP=datastoreP->getPreferredRxItemType();
+ }
+ if (!inputtypeP) {
+ CONSOLEPRINTF(("input type not found"));
+ return EXIT_FAILURE;
+ }
+ // find output type
+ if (outputtype) {
+ // search in datastore
+ outputtypeP=datastoreP->getSendType(outputtype,NULL);
+ }
+ else {
+ // use preferred rx type
+ outputtypeP=datastoreP->getPreferredTxItemType();
+ }
+ if (!outputtypeP) {
+ CONSOLEPRINTF(("output type not found"));
+ return EXIT_FAILURE;
+ }
+ // prepare type usage
+ if (inputtypeP==outputtypeP)
+ inputtypeP->initDataTypeUse(datastoreP, true, true);
+ else {
+ inputtypeP->initDataTypeUse(datastoreP, false, true);
+ outputtypeP->initDataTypeUse(datastoreP, true, false);
+ }
+
+ // now open file and read data item
+ FILE *infile;
+ size_t insize=0;
+ uInt8 *databuffer;
+
+ infile = fopen(rawfilename,"rb");
+ if (!infile) {
+ CONSOLEPRINTF(("Cannot open input file '%s' (%d)",rawfilename,errno));
+ return EXIT_FAILURE;
+ }
+ // - get size of file
+ fseek(infile,0,SEEK_END);
+ insize=ftell(infile);
+ fseek(infile,0,SEEK_SET);
+ // - create buffer of appropriate size
+ databuffer = new uInt8[insize];
+ if (!databuffer) {
+ CONSOLEPRINTF(("Not enough memory to read input file '%s' (%d)",rawfilename,errno));
+ return EXIT_FAILURE;
+ }
+ // - read data
+ if (fread(databuffer,1,insize,infile)<insize) {
+ CONSOLEPRINTF(("Error reading input file '%s' (%d)",rawfilename,errno));
+ return EXIT_FAILURE;
+ }
+ CONSOLEPRINTF(("\nNow converting into internal field representation\n"));
+ // create a sml item
+ TStatusCommand statusCmd(sessionP);
+ SmlItemPtr_t smlitemP = newItem();
+ smlitemP->data=newPCDataStringX(databuffer,true,insize);
+ delete[] databuffer;
+ // create and fill a Sync item
+ TSyncItem *syncitemP = inputtypeP->newSyncItem(
+ smlitemP, // SyncML toolkit item Data to be converted into SyncItem
+ sop_replace, // the operation to be performed with this item
+ fmt_chr, // assume default (char) format
+ inputtypeP, // target myself
+ datastoreP, // local datastore
+ statusCmd // status command that might be modified in case of error
+ );
+ // forget SyncML version
+ smlFreeItemPtr(smlitemP);
+ if (!syncitemP) {
+ CONSOLEPRINTF(("Error converting input file to internal format (SyncML status code=%hd)",statusCmd.getStatusCode()));
+ return EXIT_FAILURE;
+ }
+
+ CONSOLEPRINTF(("\nNow copying item and convert back to transport format\n"));
+
+ // make new for output type
+ TSyncItem *outsyncitemP = outputtypeP->newSyncItem(
+ outputtypeP, // target myself
+ datastoreP // local datastore
+ );
+ // copy data
+ outsyncitemP->replaceDataFrom(*syncitemP);
+ delete syncitemP;
+ // convert back
+ smlitemP=outputtypeP->newSmlItem(
+ outsyncitemP, // the syncitem to be represented as SyncML
+ datastoreP // local datastore
+ );
+ if (!syncitemP) {
+ CONSOLEPRINTF(("Could not convert back item data"));
+ return EXIT_FAILURE;
+ }
+
+ // forget converted back item
+ smlFreeItemPtr(smlitemP);
+
+ return EXIT_SUCCESS;
+} // convertData
+
+#endif // SYSYNC_TOOL
+
+
+
+/*
+ * Implementation of TServerConfig
+ */
+
+
+// config constructor
+TServerConfig::TServerConfig(const char *aElementName, TConfigElement *aParentElementP) :
+ TSessionConfig(aElementName,aParentElementP)
+{
+ clear();
+} // TServerConfig::TServerConfig
+
+
+// config destructor
+TServerConfig::~TServerConfig()
+{
+} // TServerConfig::~TServerConfig
+
+
+// init defaults
+void TServerConfig::clear(void)
+{
+ // init defaults
+ fRequestedAuth=auth_md5;
+ fRequiredAuth=auth_md5;
+ fAutoNonce=true;
+ fConstantNonce.erase();
+ fExternalURL.erase();
+ fMaxGUIDSizeSent=32; // reasonable size, but prevent braindamaged Exchange-size IDs to be sent
+ // clear inherited
+ inherited::clear();
+ // modify timeout after inherited sets it
+ fSessionTimeout=DEFAULT_SERVERSESSIONTIMEOUT;
+} // TServerConfig::clear
+
+
+// server config element parsing
+bool TServerConfig::localStartElement(const char *aElementName, const char **aAttributes, sInt32 aLine)
+{
+ // checking the elements
+ if (strucmp(aElementName,"requestedauth")==0)
+ expectEnum(sizeof(fRequestedAuth),&fRequestedAuth,authTypeNames,numAuthTypes);
+ else if (strucmp(aElementName,"requiredauth")==0)
+ expectEnum(sizeof(fRequiredAuth),&fRequiredAuth,authTypeNames,numAuthTypes);
+ // here to maintain compatibility with old pre 1.0.5.3 config files
+ else if (strucmp(aElementName,"reqiredauth")==0)
+ expectEnum(sizeof(fRequiredAuth),&fRequiredAuth,authTypeNames,numAuthTypes);
+ else if (strucmp(aElementName,"autononce")==0)
+ expectBool(fAutoNonce);
+ else if (strucmp(aElementName,"constantnonce")==0)
+ expectString(fConstantNonce);
+ else if (strucmp(aElementName,"externalurl")==0)
+ expectString(fExternalURL);
+ else if (strucmp(aElementName,"maxguidsizesent")==0)
+ expectUInt16(fMaxGUIDSizeSent);
+ // - none known here
+ else
+ return inherited::localStartElement(aElementName,aAttributes,aLine);
+ // ok
+ return true;
+} // TServerConfig::localStartElement
+
+
+// resolve
+void TServerConfig::localResolve(bool aLastPass)
+{
+ // check
+ if (aLastPass) {
+ if (!fAutoNonce && fConstantNonce.empty())
+ ReportError(false,"Warning: 'constantnonce' should be defined when 'autononce' is not set");
+ }
+ // resolve inherited
+ inherited::localResolve(aLastPass);
+} // TServerConfig::localResolve
+
+
+/*
+ * Implementation of TSyncServer
+ */
+
+
+TSyncServer::TSyncServer(
+ TSyncAppBase *aAppBaseP,
+ TSyncSessionHandle *aSessionHandleP,
+ const char *aSessionID // a session ID
+) :
+ TSyncSession(aAppBaseP,aSessionID)
+{
+ // init answer buffer
+ fBufferedAnswer=NULL;
+ fBufferedAnswerSize=0;
+ // reset data counts
+ fIncomingBytes=0;
+ fOutgoingBytes=0;
+ // init own stuff
+ InternalResetSession();
+ // save session handle
+ fSessionHandleP = aSessionHandleP; // link to handle
+ // create all locally available datastores from config
+ TServerConfig *configP = static_cast<TServerConfig *>(aAppBaseP->getRootConfig()->fAgentConfigP);
+ TLocalDSList::iterator pos;
+ for (pos=configP->fDatastores.begin(); pos!=configP->fDatastores.end(); pos++) {
+ // create the datastore
+ addLocalDataStore(*pos);
+ }
+} // TSyncServer::TSyncServer
+
+
+TSyncServer::~TSyncServer()
+{
+ // forget any buffered answers
+ bufferAnswer(NULL,0);
+ // reset session
+ InternalResetSession();
+ // show session data transfer
+ PDEBUGPRINTFX(DBG_HOT,(
+ "Session data transfer statistics: incoming bytes=%ld, outgoing bytes=%ld",
+ fIncomingBytes,
+ fOutgoingBytes
+ ));
+ // DO NOT remove session from dispatcher here,
+ // this is the task of the dispatcher itself!
+ CONSOLEPRINTF(("Terminated SyncML session (server id=%s)\n",getLocalSessionID()));
+ // show end of session in global level
+ POBJDEBUGPRINTFX(getSyncAppBase(),DBG_HOT,(
+ "TSyncServer::~TSyncServer: Deleted SyncML session (local session id=%s)",
+ getLocalSessionID()
+ ));
+} // TSyncServer::~TSyncServer
+
+
+// Reset session
+void TSyncServer::InternalResetSession(void)
+{
+ // %%% remove this as soon as Server is 1.1 compliant
+ //fSyncMLVersion=syncml_vers_1_0; // only accepts 1.0 for now %%%%
+} // TSyncServer::InternalResetSession
+
+
+// Virtual version
+void TSyncServer::ResetSession(void)
+{
+ // let ancestor do its stuff
+ TSyncSession::ResetSession();
+ // do my own stuff
+ InternalResetSession();
+} // TSyncServer::ResetSession
+
+
+// called when incoming SyncHdr fails to execute
+bool TSyncServer::syncHdrFailure(bool aTryAgain)
+{
+ if (!aTryAgain) {
+ // not already retried executing
+ // special case: header failed to execute, this means that session must be reset
+ // - Reset session (aborts all DB transactions etc.)
+ ResetSession();
+ PDEBUGPRINTFX(DBG_ERROR,("Trying to recover SyncHdr failure: =========== Session restarted ====================="));
+ // - now all session infos are gone except this command which is owned by
+ // this function alone. Execute it again.
+ aTryAgain=true;
+ }
+ else {
+ // special special case: header failed to execute the second time
+ DEBUGPRINTFX(DBG_ERROR,("Fatal internal problem, SyncHdr execution failed twice"));
+ aTryAgain=false; // just to make sure
+ SYSYNC_THROW((TSyncException("SyncHdr fatal execution problem")));
+ }
+ return aTryAgain;
+} // TSyncServer::syncHdrFailure
+
+
+// undefine these only for tests. Introduced to find problem with T68i
+#define USE_RESPURI
+#define RESPURI_ONLY_WHEN_NEEDED
+
+// create a RespURI string. If none needed, return NULL
+SmlPcdataPtr_t TSyncServer::newResponseURIForRemote(void)
+{
+ // do it in a transport-independent way, therefore let dispatcher do it
+ string respURI; // empty string
+ #ifdef USE_RESPURI
+ getSyncAppBase()->generateRespURI(
+ respURI, // remains unaffected if no RespURI could be calculated
+ fInitialLocalURI.c_str(), // initial URI used by remote to send first message
+ fLocalSessionID.c_str() // server generated unique session ID
+ );
+ // Omit RespURI if local URI as seen by client is identical
+ #ifdef RESPURI_ONLY_WHEN_NEEDED
+ // %%% attempt to make T68i work
+ if (respURI==fLocalURI) {
+ respURI.erase();
+ DEBUGPRINTFX(DBG_SESSION,(
+ "Generated RespURI and sourceLocURI are equal (%s)-> RespURI omitted",
+ fLocalURI.c_str()
+ ));
+ }
+ #endif
+ #endif
+ // Note: returns NULL if respURI is empty string
+ return newPCDataOptString(respURI.c_str());
+} // newResponseURIForRemote
+
+
+// called after successful decoding of an incoming message
+bool TSyncServer::MessageStarted(SmlSyncHdrPtr_t aContentP, TStatusCommand &aStatusCommand, bool aBad)
+{
+ Ret_t err=SML_ERR_OK;
+
+ // message not authorized by default
+ fMessageAuthorized=false;
+
+ // Get information from SyncHdr which is needed for answers
+ // - session ID to be used for responses
+ fSynchdrSessionID=smlPCDataToCharP(aContentP->sessionID);
+ // - local URI (as seen by remote client)
+ fLocalURI=smlSrcTargLocURIToCharP(aContentP->target);
+ fLocalName=smlSrcTargLocNameToCharP(aContentP->target);
+ // - also remember URI to which first message was sent
+ // %%% note: incoming ID is not a criteria, because it might be >1 due to
+ // client retrying something which it thinks is for the same session
+ //if (fIncomingMsgID==1) {
+ if (fOutgoingMsgID==0) {
+ // this is the first message, remember first URI used to contact server
+ // (or set preconfigured string from <externalurl>)
+ if (getServerConfig()->fExternalURL.empty())
+ fInitialLocalURI=fLocalURI; // use what client sends to us
+ else
+ fInitialLocalURI=getServerConfig()->fExternalURL; // use preconfigured URL
+ // Many clients, including SCTS send the second login attempt with a MsgID>1,
+ // and depending on how they handle RespURI, they might get a new session for that
+ // -> so, just handle the case that a new session does not start with MsgID=1
+ if (fIncomingMsgID>1) {
+ PDEBUGPRINTFX(DBG_ERROR,(
+ "New session gets first message with MsgID=%ld (should be 1). Might be due to retries, adjusting OutgoingID as well",
+ fIncomingMsgID
+ ));
+ fOutgoingMsgID=fIncomingMsgID-1; // to make it match what client expects
+ }
+ }
+ // - remote URI
+ fRemoteURI=smlSrcTargLocURIToCharP(aContentP->source);
+ fRemoteName=smlSrcTargLocNameToCharP(aContentP->source);
+ // - RespURI (remote URI to respond to, if different from source)
+ fRespondURI.erase();
+ if (aContentP->respURI) {
+ fRespondURI=smlPCDataToCharP(aContentP->respURI);
+ DEBUGPRINTFX(DBG_PROTO,("RespURI specified = '%s'",fRespondURI.c_str()));
+ }
+ if (fRespondURI==fRemoteURI) fRespondURI.erase(); // if specified but equal to remote: act as if not specified
+ // More checking if header was ok
+ if (aBad) {
+ // bad header, only do what is needed to get a status back to client
+ fSessionAuthorized=false;
+ fIncomingState=psta_init;
+ fOutgoingState=psta_init;
+ fNewOutgoingPackage=true;
+ // issue header to make sure status can be sent back to client
+ if (!fMsgNoResp)
+ issueHeader(false); // issue header, do not prevent responses
+ }
+ else {
+ // check busy (or expired) case
+ if (serverBusy()) {
+ #ifdef APP_CAN_EXPIRE
+ if (getSyncAppBase()->fAppExpiryStatus!=LOCERR_OK) {
+ aStatusCommand.setStatusCode(511); // server failure (expired)
+ aStatusCommand.addItemString("License expired or invalid");
+ PDEBUGPRINTFX(DBG_ERROR,("License expired or invalid - Please contact Synthesis AG to obtain license"));
+ }
+ else
+ #endif
+ {
+ aStatusCommand.setStatusCode(101); // busy
+ }
+ issueHeader(false); // issue header, do not prevent responses
+ AbortSession(0,true); // silently discard rest of commands
+ return false; // header not ok
+ }
+ // now check what state we are in
+ if (fIncomingState==psta_idle) {
+ // Initialize
+ // - session-wide authorization not yet there
+ fSessionAuthorized=false;
+ fMapSeen=false;
+ // - session has started, we are processing first incoming
+ // package and generating first outgoing package
+ // (init, eventually changed to combined init/sync by <sync> in this package)
+ fIncomingState=psta_init;
+ fOutgoingState=psta_init;
+ fNewOutgoingPackage=true;
+ }
+ // authorization check
+ if (fIncomingState>=psta_init) {
+ // now check authorization
+ if (!fSessionAuthorized) {
+ // started, but not yet permanently authorized
+ fMessageAuthorized=checkCredentials(
+ smlSrcTargLocNameToCharP(aContentP->source), // user name in clear text according to SyncML 1.0.1
+ aContentP->cred, // actual credentials
+ aStatusCommand
+ );
+ // NOTE: aStatusCommand has now the appropriate status and chal (set by checkCredentials())
+ // if credentials do not match, stop processing commands (but stay with the session)
+ if (!fMessageAuthorized) {
+ AbortCommandProcessing(aStatusCommand.getStatusCode());
+ PDEBUGPRINTFX(DBG_PROTO,("Authorization failed with status %hd, stop command processing",aStatusCommand.getStatusCode()));
+ }
+ // now determine if authorization is permanent or not
+ if (fMessageAuthorized) {
+ fAuthFailures=0; // reset count
+ if (messageAuthRequired()) {
+ // each message needs autorisation again (or no auth at all)
+ // - 200 ok, next message needs authorization again (or again: none)
+ fSessionAuthorized=false; // no permanent authorization
+ aStatusCommand.setStatusCode(200);
+ // - add challenge for next auth (different nonce)
+ aStatusCommand.setChallenge(newSessionChallenge());
+ PDEBUGPRINTFX(DBG_PROTO,("Authorization ok, but required again for subsequent messages: 200 + chal"));
+ }
+ else {
+ // entire session is authorized
+ fSessionAuthorized=true; // permanent authorization
+ // - 212 authentication accepted (or 200 if none is reqired at all)
+ aStatusCommand.setStatusCode(requestedAuthType()==auth_none ? 200 : 212);
+ // - add challenge for next auth (in next session, but as we support carry
+ // forward via using sessionID, we need to send one here as well)
+ aStatusCommand.setChallenge(newSessionChallenge());
+ PDEBUGPRINTFX(DBG_PROTO,("Authorization accepted: 212"));
+ }
+ }
+ } // authorisation check
+ else {
+ // already authorized from previous message
+ PDEBUGPRINTFX(DBG_PROTO,("Authorization ok from previous request: 200"));
+ fMessageAuthorized=true;
+ }
+ // Start response message AFTER auth check, to allow issueHeader
+ // to check auth state and customize the header accordingly (no
+ // RespURI for failed auth for example)
+ if (!fMsgNoResp) {
+ issueHeader(false); // issue header, do not prevent responses
+ }
+ } // if started at least
+ } // if not aBad
+ // return startmessage status
+ // debug info
+ #ifdef SYDEBUG
+ if (PDEBUGMASK & DBG_SESSION) {
+ PDEBUGPRINTFX(DBG_SESSION,(
+ "---> MessageStarted, Message %sauthorized, incoming state='%s', outgoing state='%s'",
+ fMessageAuthorized ? "" : "NOT ",
+ PackageStateNames[fIncomingState],
+ PackageStateNames[fOutgoingState]
+ ));
+ TLocalDataStorePContainer::iterator pos;
+ for (pos=fLocalDataStores.begin(); pos!=fLocalDataStores.end(); ++pos) {
+ // Show state of local datastores
+ PDEBUGPRINTFX(DBG_SESSION,(
+ "Local Datastore '%s': State=%s, %s%s sync, %s%s",
+ (*pos)->getName(),
+ (*pos)->getDSStateName(),
+ (*pos)->isResuming() ? "RESUMED " : "",
+ (*pos)->fSlowSync ? "SLOW" : "normal",
+ SyncModeDescriptions[(*pos)->fSyncMode],
+ (*pos)->fServerAlerted ? ", Server-Alerted" : ""
+ ));
+ }
+ }
+ #endif
+ // final check for too many auth failures
+ if (!fMessageAuthorized) {
+ #ifdef NO_NONCE_OLD_BEAHVIOUR
+ AbortSession(aStatusCommand.getStatusCode(),true); // local error
+ // avoid special treatment of non-authorized message, we have aborted, this is enough
+ fMessageAuthorized=true;
+ #else
+ // Unsuccessful auth, count this
+ fAuthFailures++;
+ PDEBUGPRINTFX(DBG_ERROR,(
+ "Authorization failed %hd. time, (any reason), sending status %hd",
+ fAuthFailures,
+ aStatusCommand.getStatusCode()
+ ));
+ // - abort session after too many auth failures
+ if (fAuthFailures>=MAX_AUTH_ATTEMPTS) {
+ PDEBUGPRINTFX(DBG_ERROR,("Too many (>=%hd) failures, aborting session",MAX_AUTH_ATTEMPTS));
+ AbortSession(400,true);
+ }
+ #endif
+ }
+ // returns false on BAD header (but true on wrong/bad/missing cred)
+ return true;
+} // TSyncServer::MessageStarted
+
+
+void TSyncServer::MessageEnded(bool aIncomingFinal)
+{
+ bool alldone;
+ TPackageStates newoutgoingstate,newincomingstate;
+ TLocalDataStorePContainer::iterator pos;
+ bool allFromClientOnly=false;
+
+ // Incoming message ends here - what is following are commands initiated by the server
+ // not directly related to a incoming command.
+ PDEBUGENDBLOCK("SyncML_Incoming");
+ // assume that outgoing package is NOT finished, so outgoing state does not change
+ newoutgoingstate=fOutgoingState;
+ // new incoming state depends on whether this message is final or not
+ if ((aIncomingFinal || (fIncomingState==psta_supplement)) && fMessageAuthorized) {
+ // Note: in supplement state, incoming final is not relevant (may or may not be present, there is
+ // no next phase anyway
+ // find out if this is a shortened session (no map phase) due to
+ // from-client-only in all datastores
+ if (!fCompleteFromClientOnly) {
+ allFromClientOnly=true;
+ for (pos=fLocalDataStores.begin(); pos!=fLocalDataStores.end(); ++pos) {
+ // check sync modes
+ if ((*pos)->isActive() && (*pos)->getSyncMode()!=smo_fromclient) {
+ allFromClientOnly=false;
+ break;
+ }
+ }
+ }
+ // determine what package comes next
+ switch (fIncomingState) {
+ case psta_init :
+ newincomingstate=psta_sync;
+ break;
+ case psta_sync :
+ case psta_initsync :
+ // end of sync phase means end of session if all datastores are in from-client-only mode
+ if (allFromClientOnly) {
+ PDEBUGPRINTFX(DBG_PROTO+DBG_HOT,("All datastores in from-client-only mode: don't expect map phase from client"));
+ newincomingstate=psta_supplement;
+ }
+ else {
+ newincomingstate=psta_map;
+ }
+ break;
+ case psta_map :
+ case psta_supplement : // supplement state does not exit automatically
+ // after map, eventually some supplement status/alert 222 messages are needed from client
+ newincomingstate=psta_supplement;
+ break;
+ default:
+ // by default, back to idle
+ newincomingstate=psta_idle;
+ break;
+ } // switch
+ }
+ else {
+ // not final or not authorized: no change in state
+ newincomingstate=fIncomingState;
+ }
+ // show status before processing
+ PDEBUGPRINTFX(DBG_SESSION,(
+ "---> MessageEnded starts : old incoming state='%s', old outgoing state='%s', %sNeedToAnswer",
+ PackageStateNames[fIncomingState],
+ PackageStateNames[fOutgoingState],
+ fNeedToAnswer ? "" : "NO "
+ ));
+ // process
+ if (isAborted()) {
+ // actual aborting has already taken place
+ PDEBUGPRINTFX(DBG_ERROR,("***** Session is flagged 'aborted' -> MessageEnded ends package and session"));
+ newoutgoingstate=psta_idle;
+ newincomingstate=psta_idle;
+ fInProgress=false;
+ } // if aborted
+ else if (isSuspending()) {
+ // only flagged for suspend - but datastores are not yet aborted, do it now
+ AbortSession(514,true,getAbortReasonStatus());
+ PDEBUGPRINTFX(DBG_ERROR,("***** Session is flagged 'suspended' -> MessageEnded ends package and session"));
+ newoutgoingstate=psta_idle;
+ newincomingstate=psta_idle;
+ fInProgress=false;
+ }
+ else if (!fMessageAuthorized) {
+ // not authorized messages will just be ignored, no matter if final or not,
+ // so outgoing will NEVER be final on non-authorized messages
+ // %%% before 1.0.4.9, this was fInProgress=true
+ // DEBUGPRINTFX(DBG_ERROR,("***** Message not authorized, ignore and DONT end package, session continues"));
+ // fInProgress=true;
+ PDEBUGPRINTFX(DBG_ERROR,("***** Message not authorized, ignore msg and terminate session"));
+ fInProgress=false;
+ }
+ else {
+ // determine if session continues living or not
+ // - if in other than idle state, session will continue
+ fInProgress =
+ (newincomingstate!=psta_idle) || // if not idle, we'll continue
+ !fMessageAuthorized; // if not authorized, we'll continue as well (retrying auth)
+ // Check if we need to send an Alert 222 to get more messages of this package
+ if (!aIncomingFinal) {
+ // not end of incoming package
+ #ifndef ALWAYS_CONTINUE222
+ if (!fNeedToAnswer)
+ #endif
+ {
+ #ifdef COMBINE_SYNCANDMAP
+ // %%% make sure session gets to an end in case combined sync/map was used
+ if (fMapSeen && fIncomingState==psta_map && fOutgoingState==psta_map) {
+ DEBUGPRINTFX(DBG_HOT,("********** Incoming, non-final message in (combined)map state needs no answer -> force end of outgoing package"));
+ newoutgoingstate=psta_idle;
+ }
+ else
+ #endif
+ {
+ // detected 222-loop on init here: when we have nothing to answer in init
+ // and nothing is alerted -> break session
+ // %%% not sure if this is always ok
+ if (fIncomingState<=psta_init) {
+ PDEBUGPRINTFX(DBG_ERROR,("############## Looks like if we were looping in an init-repeat loop -> force final"));
+ fInProgress=false;
+ fOutgoingState=psta_idle;
+ }
+ else {
+ // not final, and nothing to answer otherwise: create alert-Command to request more info
+ TAlertCommand *alertCmdP = new TAlertCommand(this,NULL,(uInt16)222);
+ // %%% not clear from spec what has to be in item for 222 alert code
+ // but there MUST be an Item for the Alert command according to SyncML TK
+ // - we just put local and remote URIs here
+ SmlItemPtr_t itemP = newItem();
+ itemP->target = newLocation(fRemoteURI.c_str());
+ itemP->source = newLocation(fLocalURI.c_str());
+ alertCmdP->addItem(itemP);
+ ISSUE_COMMAND_ROOT(this,alertCmdP);
+ }
+ }
+ }
+ }
+ else {
+ // end of package, finish processing package
+ if (fIncomingState==psta_init) {
+ // - try to load devinf from cache (only if we don't have both datastores and type info already)
+ if (!fRemoteDataStoresKnown || !fRemoteDataTypesKnown) {
+ SmlDevInfDevInfPtr_t devinfP;
+ TStatusCommand dummystatus(this);
+ if (loadRemoteDevInf(getRemoteURI(),devinfP)) {
+ // we have cached devinf, analyze it now
+ localstatus sta = analyzeRemoteDevInf(devinfP);
+ PDEBUGPRINTFX(DBG_ERROR,("devInf from Cache could not be analyzed: error=%hd",sta));
+ }
+ }
+ // - if no DevInf for remote datastores cached or received yet,
+ // issue GET for it now
+ if (!fRemoteDataStoresKnown) {
+ // if we know datastores here, but not types, this means that remote does not have
+ // CTCap, so it makes no sense to issue a GET again.
+ #ifndef NO_DEVINF_GET
+ // end of initialisation package, but datastores not known yet
+ // (=no DevInf Put received) --> ask for devinf now
+ PDEBUGPRINTFX(DBG_REMOTEINFO,("No DevInf received or cached, request DevInf using GET command"));
+ TGetCommand *getcommandP = new TGetCommand(this);
+ getcommandP->addTargetLocItem(SyncMLDevInfNames[fSyncMLVersion]);
+ string devinftype=SYNCML_DEVINF_META_TYPE;
+ addEncoding(devinftype);
+ getcommandP->setMeta(newMetaType(devinftype.c_str()));
+ ISSUE_COMMAND_ROOT(this,getcommandP);
+ #endif
+ }
+ }
+ }
+ // make sure syncing local datastores get informed of end-of-<Sync>-message
+ if (fIncomingState==psta_sync || fIncomingState==psta_initsync) {
+ // end of an incoming message of the Sync Package
+ // - let all local datastores know, this is now the time to generate
+ // <sync> commands, if needed
+ // Note: if there are SyncEnd commands delayed, this means that this is
+ // not yet the time to start <sync> commands. Instead, when all
+ // queued SyncEnd commands are executed later, engEndOfSyncFromRemote()
+ // will be called with the endOfAllSyncCommands flag true instead
+ // of now.
+ for (pos=fLocalDataStores.begin(); pos!=fLocalDataStores.end(); ++pos) {
+ (*pos)->engEndOfSyncFromRemote(aIncomingFinal && !delayedSyncEndsPending());
+ }
+ }
+ // Detect outgoing package state transitions
+ // - init to sync
+ if (fOutgoingState==psta_init && newincomingstate>psta_init) {
+ // new outgoing state is sync.
+ // Note: In combined init&sync mode, sync command received in init state
+ // will set outgoing state from init to init-sync while processing message,
+ // so no transition needs to be detected here
+ newoutgoingstate=psta_sync;
+ }
+ // - sync to map
+ else if (
+ (fOutgoingState==psta_sync || fOutgoingState==psta_initsync) && // outgoing is sync..
+ (newincomingstate>=psta_initsync) && // ..and incoming has finished sync
+ !allFromClientOnly // ..and this is not a session with all datastores doing from-client-only
+ ) {
+ // outgoing message belongs to Sync package
+ // - ask all local datastores if they are finished with sync command generation
+ alldone=true;
+ for (pos=fLocalDataStores.begin(); pos!=fLocalDataStores.end(); ++pos) {
+ alldone = alldone && (*pos)->isSyncDone();
+ }
+ if (alldone) {
+ // outgoing state changes to map (or supplement if all datastores are from-client-only
+ PDEBUGPRINTFX(DBG_HOT,("All datastores are done with generating <Sync>"));
+ newoutgoingstate=psta_map;
+ #ifdef COMBINE_SYNCANDMAP
+ // %%% it seems as if 9210 needs combined Sync/Map package and
+ if (fMapSeen) {
+ // prevent FINAL to be sent at end of message
+ DEBUGPRINTFX(DBG_HOT,("********** Combining outgoing sync and map-response packages into one"));
+ fOutgoingState=psta_map;
+ }
+ #endif
+ }
+ }
+ // - map (or from-client-only sync) to idle
+ else if (
+ (fOutgoingState==psta_map && newincomingstate==psta_supplement) ||
+ (allFromClientOnly && (fOutgoingState==psta_sync || fOutgoingState==psta_initsync))
+ ) {
+ // we are going back to idle now
+ newoutgoingstate=psta_idle;
+ // session ends if it doesn't need to continue for session-level reasons
+ if (!sessionMustContinue()) {
+ PDEBUGPRINTFX(DBG_HOT,("Session completed, now let datastores terminate all sync operations"));
+ for (pos=fLocalDataStores.begin(); pos!=fLocalDataStores.end(); ++pos) {
+ // finished with Map: end of sync
+ (*pos)->engFinishDataStoreSync(); // successful
+ }
+ // session ends now
+ fInProgress=false;
+ // let custom PUTs which may transmit some session statistics etc. happen now
+ issueCustomEndPut();
+ }
+ }
+ // - if no need to answer (e.g. nothing to send back except OK status for SyncHdr),
+ // session is over now (as well)
+ if (!fNeedToAnswer) fInProgress=false;
+ } // else
+ // Now finish outgoing message
+ #ifdef DONT_FINAL_BAD_AUTH_ATTEMPTS
+ // - PREVENT final flag after failed auth attempts
+ if(FinishMessage(
+ fOutgoingState!=newoutgoingstate || fOutgoingState==psta_idle, // final when state changed or idle
+ !fMessageAuthorized || serverBusy() // busy or unauthorized prevent final flag at any rate
+ ))
+ #else
+ // - DO set final flag after failed auth attempts
+ if(FinishMessage(
+ !fMessageAuthorized || fOutgoingState!=newoutgoingstate || fOutgoingState==psta_idle, // final when state changed or idle
+ serverBusy() // busy prevents final flag at any rate
+ ))
+ #endif
+ {
+ // outgoing state HAS changed
+ fOutgoingState=newoutgoingstate;
+ }
+ // Now update incoming state
+ fIncomingState=newincomingstate;
+ // show states
+ PDEBUGPRINTFX(DBG_HOT,(
+ "---> MessageEnded finishes : new incoming state='%s', new outgoing state='%s', %sNeedToAnswer",
+ PackageStateNames[fIncomingState],
+ PackageStateNames[fOutgoingState],
+ fNeedToAnswer ? "" : "NO "
+ ));
+ // let all local datastores know that message has ended
+ for (pos=fLocalDataStores.begin(); pos!=fLocalDataStores.end(); ++pos) {
+ // let them know
+ (*pos)->engEndOfMessage();
+ // Show state of local datastores
+ PDEBUGPRINTFX(DBG_HOT,(
+ "Local Datastore '%s': State=%s, %s%s sync, %s%s",
+ (*pos)->getName(),
+ (*pos)->getDSStateName(),
+ (*pos)->isResuming() ? "RESUMED " : "",
+ (*pos)->fSlowSync ? "SLOW" : "normal",
+ SyncModeDescriptions[(*pos)->fSyncMode],
+ (*pos)->fServerAlerted ? ", Server-Alerted" : ""
+ ));
+ }
+ // End of outgoing message
+ PDEBUGPRINTFX(DBG_HOT,(
+ "=================> Finished generating outgoing message #%ld, request=%ld",
+ fOutgoingMsgID,
+ getSyncAppBase()->requestCount()
+ ));
+ PDEBUGENDBLOCK("SyncML_Outgoing");
+} // TSyncServer::MessageEnded
+
+
+void TSyncServer::RequestEnded(bool &aHasData)
+{
+ // to make sure, finish any unfinished message
+ FinishMessage(true); // final allowed, as this is an out-of-normal-order case anyway
+ // if we need to answer, we have data
+ // - SyncML specs 1.0.1 says that server must always respond, even if message
+ // contains of a Status for the SyncHdr only
+ aHasData=true;
+ // %%% first drafts of 1.0.1 said that SyncHdr Status only messages must not be sent...
+ // aHasData=fNeedToAnswer; // %%%
+
+ // now let all datastores know that request processing ends here (so they might
+ // prepare for a thread switch)
+ // terminate sync with all datastores
+ TLocalDataStorePContainer::iterator pos;
+ for (pos=fLocalDataStores.begin(); pos!=fLocalDataStores.end(); ++pos) {
+ // now let them know that request has ended
+ (*pos)->engRequestEnded();
+ }
+} // TSyncServer::RequestEnded
+
+
+// Called at end of Request, returns true if session must be deleted
+// returns flag if data to be returned. If response URI was specified
+// different, it is returned in aRespURI, otherwise aRespURI is empty.
+bool TSyncServer::EndRequest(bool &aHasData, string &aRespURI, uInt32 aReqBytes)
+{
+ // count incoming data
+ fIncomingBytes+=aReqBytes;
+ // let client or server do what is needed
+ if (fMessageRetried) {
+ // Message processing cancelled
+ CancelMessageProcessing();
+ // Nothing happened
+ // - but count bytes
+ fOutgoingBytes+=fBufferedAnswerSize;
+ PDEBUGPRINTFX(DBG_HOT,(
+ "========= Finished retried request with re-sending buffered answer (session %sin progress), incoming bytes=%ld, outgoing bytes=%ld",
+ fInProgress ? "" : "NOT ",
+ aReqBytes,
+ fBufferedAnswerSize
+ ));
+ aHasData=false; // we do not have data in the sml instance (but we have/had some in the retry re-send buffer)
+ }
+ else {
+ // end request
+ RequestEnded(aHasData);
+ // count bytes
+ fOutgoingBytes+=getOutgoingMessageSize();
+ PDEBUGPRINTFX(DBG_HOT,(
+ "========= Finished request (session %sin progress), processing time=%ld msec, incoming bytes=%ld, outgoing bytes=%ld",
+ fInProgress ? "" : "NOT ",
+ (sInt32)((getSystemNowAs(TCTX_UTC)-getLastRequestStarted()) * nanosecondsPerLinearTime / 1000000),
+ aReqBytes,
+ getOutgoingMessageSize()
+ ));
+ // return RespURI (is empty if none specified or equal to message source URI)
+ aRespURI = fRespondURI;
+ }
+ if (!fInProgress) {
+ // terminate datastores here already in case we are not in progress any more
+ // here. If any of the datastores are in progress at this point, this is a
+ // protocol violation, and therefore we return a 400.
+ // Note: resetting the session later will also call TerminateDatastores, but then
+ // with a 408 (which is misleading when the session ends here due to protocol
+ // problem.
+ TerminateDatastores(400);
+ }
+ //%%% moved to happen before end of SyncML_Outgoing
+ //PDEBUGENDBLOCK("SyncML_Incoming");
+ if (fRequestMinTime>0) {
+ // make sure we spent enough time with this request, if not, artificially extend time
+ // - get number of seconds already spent
+ sInt32 t =
+ (sInt32)((getSystemNowAs(TCTX_UTC)-getLastRequestStarted()) / (lineartime_t)secondToLinearTimeFactor);
+ // - delay if needed
+ if (t<fRequestMinTime) {
+ PDEBUGPRINTFX(DBG_HOT,(
+ "requestmintime is set to %ld seconds, we have spent only %ld seconds so far -> sleeping %ld seconds",
+ fRequestMinTime,
+ t,
+ fRequestMinTime-t
+ ));
+ CONSOLEPRINTF((" ...delaying response by %ld seconds because requestmintime is set to %ld",fRequestMinTime,fRequestMinTime-t));
+ sleepLineartime((lineartime_t)(fRequestMinTime-t)*secondToLinearTimeFactor);
+ }
+ }
+ // thread might end here, so stop profiling
+ TP_STOP(fTPInfo);
+ #ifdef SYDEBUG
+ // we are not the main thread any longer
+ getDbgLogger()->DebugThreadOutputDone();
+ #endif
+ // return true if session is not in progress any more
+ return(!fInProgress);
+} // TSyncServer::EndRequest
+
+
+// buffer answer in the session's buffer if transport allows it
+Ret_t TSyncServer::bufferAnswer(MemPtr_t aAnswer, MemSize_t aAnswerSize)
+{
+ // get rid of previous buffered answer
+ if (fBufferedAnswer)
+ delete[] fBufferedAnswer;
+ fBufferedAnswer=NULL;
+ fBufferedAnswerSize=0;
+ // save new answer (if not empty)
+ if (aAnswer && aAnswerSize) {
+ // allocate buffer
+ fBufferedAnswer = new unsigned char[aAnswerSize];
+ // copy data
+ if (!fBufferedAnswer) return SML_ERR_NOT_ENOUGH_SPACE;
+ memcpy(fBufferedAnswer,aAnswer,aAnswerSize);
+ // save size
+ fBufferedAnswerSize=aAnswerSize;
+ }
+ return SML_ERR_OK;
+} // TSyncServer::bufferAnswer
+
+
+// get buffered answer from the session's buffer if there is any
+void TSyncServer::getBufferedAnswer(MemPtr_t &aAnswer, MemSize_t &aAnswerSize)
+{
+ aAnswer=fBufferedAnswer;
+ aAnswerSize=fBufferedAnswerSize;
+ PDEBUGPRINTFX(DBG_HOT,(
+ "Buffered answer read from session: %ld bytes",
+ fBufferedAnswerSize
+ ));
+} // TSyncServer::getBufferedAnswer
+
+
+// returns remaining time for request processing [seconds]
+sInt32 TSyncServer::RemainingRequestTime(void)
+{
+ // if no request timeout specified, use session timeout
+ sInt32 t = fRequestMaxTime ? fRequestMaxTime : getSessionConfig()->fSessionTimeout;
+ // calculate number of remaining seconds
+ return
+ t==0 ?
+ 0x7FFFFFFF : // "infinite"
+ t - (sInt32)((getSystemNowAs(TCTX_UTC)-getLastRequestStarted()) / (lineartime_t)secondToLinearTimeFactor);
+} // TSyncServer::RemainingRequestTime
+
+
+
+
+
+// process a Map command in context of server session
+bool TSyncServer::processMapCommand(
+ SmlMapPtr_t aMapCommandP, // the map command contents
+ TStatusCommand &aStatusCommand, // pre-set 200 status, can be modified in case of errors
+ bool &aQueueForLater
+)
+{
+ bool allok=false; // assume not ok
+ localstatus sta;
+
+ // remember that this session has seen a map command already
+ fMapSeen=true;
+ // Detecting a map command in supplement incomin state indicates a
+ // client like funambol that send to many <final/> in pre-map phases
+ // (such as in 222-Alert messages). So we reset the session state back
+ // to incoming/outgoing map to correct this client bug
+ if (fIncomingState==psta_supplement) {
+ // back to map phase, as client apparently IS still in map phase, despite too many
+ // <final/> sent
+ PDEBUGPRINTFX(DBG_ERROR,(
+ "Warning: detected <Map> command after end of Map phase - buggy client sent too many <final/>. Re-entering map phase to compensate"
+ ));
+ fIncomingState=psta_map;
+ fOutgoingState=psta_map;
+ }
+ // find database(s)
+ // - get relative URI of requested database
+ const char *targetdburi = smlSrcTargLocURIToCharP(aMapCommandP->target);
+ TLocalEngineDS *datastoreP = findLocalDataStoreByURI(targetdburi);
+ if (!datastoreP) {
+ // no such local datastore
+ aStatusCommand.setStatusCode(404); // not found
+ }
+ else {
+ // local datastore found
+ // - maps can be processed when we are at least ready for early (chached by client from previous session) maps
+ if (datastoreP->testState(dssta_syncmodestable)) {
+ // datastore is ready
+ PDEBUGBLOCKFMT(("ProcessMap", "Processing items from Map command", "datastore=%s", targetdburi));
+ bool allok=true; // assume all ok
+ SmlMapItemListPtr_t nextnode = aMapCommandP->mapItemList;
+ while (nextnode) {
+ POINTERTEST(nextnode->mapItem,("MapItemList node w/o MapItem"));
+ PDEBUGPRINTFX(DBG_HOT,(
+ "Mapping remoteID='%s' to localID='%s'",
+ smlSrcTargLocURIToCharP(nextnode->mapItem->source),
+ smlSrcTargLocURIToCharP(nextnode->mapItem->target)
+ ));
+ sta = datastoreP->engProcessMap(
+ #ifdef DONT_STRIP_PATHPREFIX_FROM_REMOTEIDS
+ smlSrcTargLocURIToCharP(nextnode->mapItem->source),
+ #else
+ relativeURI(smlSrcTargLocURIToCharP(nextnode->mapItem->source)),
+ #endif
+ relativeURI(smlSrcTargLocURIToCharP(nextnode->mapItem->target))
+ );
+ if (sta!=LOCERR_OK) {
+ PDEBUGPRINTFX(DBG_ERROR,(" Mapping FAILED!"));
+ aStatusCommand.setStatusCode(sta);
+ allok=false;
+ break;
+ }
+ // next mapitem
+ nextnode=nextnode->next;
+ } // while more mapitems
+ // terminate Map command
+ allok=datastoreP->MapFinishAsServer(allok,aStatusCommand);
+ PDEBUGENDBLOCK("ProcessMap");
+ }
+ else {
+ // we must queue the command for later execution
+ aQueueForLater=true;
+ allok=true; // ok for now, we'll re-execute this later
+ }
+ } // database found
+ return allok;
+} // TSyncServer::processMapCommand
+
+
+// - start sync group
+bool TSyncServer::processSyncStart(
+ SmlSyncPtr_t aSyncP, // the Sync element
+ TStatusCommand &aStatusCommand, // pre-set 200 status, can be modified in case of errors
+ bool &aQueueForLater // will be set if command must be queued for later (re-)execution
+)
+{
+ // Init datastores for sync
+ localstatus sta = initSync(
+ smlSrcTargLocURIToCharP(aSyncP->target), // local datastore
+ smlSrcTargLocURIToCharP(aSyncP->source) // remote datastore
+ );
+ if (sta!=LOCERR_OK) {
+ aStatusCommand.setStatusCode(sta);
+ return false;
+ }
+ // let local datastore prepare for sync as server
+ // - let local process sync command
+ bool ok=fLocalSyncDatastoreP->engProcessSyncCmd(aSyncP,aStatusCommand,aQueueForLater);
+ // Note: ok means that the sync command is addressing existing datastores. However,
+ // it does not mean that the actual processing is already executed; aQueueForLater
+ // could be set!
+ // if ok and not queued: update package states
+ if (ok) {
+ if (fIncomingState==psta_init || fIncomingState==psta_initsync) {
+ // detected sync command in init package -> this is combined init/sync
+ #ifdef SYDEBUG
+ if (fIncomingState==psta_init)
+ DEBUGPRINTFX(DBG_HOT,("<Sync> started init package -> switching to combined init/sync"));
+ #endif
+ // - set new incoming state
+ fIncomingState=psta_initsync;
+ // - also update outgoing state, if it is in init package
+ if (fOutgoingState==psta_init)
+ fOutgoingState=psta_initsync;
+ }
+ else if (fCmdIncomingState!=psta_sync) {
+ DEBUGPRINTFX(DBG_ERROR,(
+ "<Sync> found in wrong incoming package state '%s' -> aborting session",
+ PackageStateNames[fCmdIncomingState]
+ ));
+ aStatusCommand.setStatusCode(403); // forbidden
+ fLocalSyncDatastoreP->engAbortDataStoreSync(403,true); // abort, local problem
+ ok=false;
+ }
+ else {
+ // - show sync start
+ DEBUGPRINTFX(DBG_HOT,(
+ "<Sync> started, cmd-incoming state='%s', incoming state='%s', outgoing state='%s'",
+ PackageStateNames[fCmdIncomingState],
+ PackageStateNames[fIncomingState],
+ PackageStateNames[fOutgoingState]
+ ));
+ }
+ }
+ return ok;
+} // TSyncServer::processSyncStart
+
+
+
+// get next nonce string top be sent to remote party for subsequent MD5 auth
+void TSyncServer::getNextNonce(const char *aDeviceID, string &aNextNonce)
+{
+ fLastNonce.erase();
+ if (getServerConfig()->fAutoNonce) {
+ // generate nonce out of source ref and session ID
+ // This scheme can provide nonce carrying forward between
+ // sessions by initializing lastNonce with the srcRef/sessionid-1
+ // assuming client to use nonce from last session.
+ sInt32 sid;
+ // use current day as nonce varying number
+ sid = time(NULL) / 3600 / 24;
+ generateNonce(fLastNonce,aDeviceID,sid);
+ }
+ else {
+ // get constant nonce (if empty, this is NO nonce)
+ fLastNonce=getServerConfig()->fConstantNonce;
+ }
+ // return new nonce
+ DEBUGPRINTFX(DBG_PROTO,("getNextNonce: created nonce='%s'",fLastNonce.c_str()));
+ aNextNonce=fLastNonce;
+} // TSyncServer::getNextNonce
+
+
+// - get nonce string for specified deviceID
+void TSyncServer::getAuthNonce(const char *aDeviceID, string &aAuthNonce)
+{
+ // if no device ID, use session default nonce
+ if (!aDeviceID) {
+ TSyncSession::getAuthNonce(aDeviceID,fLastNonce);
+ }
+ else {
+ // Basic nonce mechanism needing no per-device storage:
+ // - we have no stored last nonce, but we can re-create nonce used
+ // for last session with this device by the used algorithm
+ if (getServerConfig()->fAutoNonce) {
+ if (fLastNonce.empty()) {
+ // none available, produce new one
+ sInt32 sid;
+ // use current day as nonce varying number
+ sid = time(NULL) / 3600 / 24;
+ generateNonce(fLastNonce,aDeviceID,sid);
+ }
+ }
+ else {
+ // return constant nonce
+ fLastNonce=getServerConfig()->fConstantNonce;
+ }
+ }
+ DEBUGPRINTFX(DBG_PROTO,("getAuthNonce: current auth nonce='%s'",fLastNonce.c_str()));
+ aAuthNonce=fLastNonce;
+} // TSyncServer::getAuthNonce
+
+
+
+// info about server status
+bool TSyncServer::serverBusy(void)
+{
+ // return flag (which might have been set by some connection
+ // limit code in sessiondispatch).
+ // When app is expired, all server sessions are busy anyway
+ #ifdef APP_CAN_EXPIRE
+ return fSessionIsBusy || (getSyncAppBase()->fAppExpiryStatus!=LOCERR_OK);
+ #else
+ return fSessionIsBusy;
+ #endif
+} // TSyncServer::serverBusy
+
+
+// access to config
+TServerConfig *TSyncServer::getServerConfig(void)
+{
+ TServerConfig *scP;
+ GET_CASTED_PTR(scP,TServerConfig,getSyncAppBase()->getRootConfig()->fAgentConfigP,DEBUGTEXT("no TServerConfig","ss1"));
+ return scP;
+} // TSyncServer::getServerConfig
+
+
+// info about requested auth type
+TAuthTypes TSyncServer::requestedAuthType(void)
+{
+ return getServerConfig()->fRequestedAuth;
+} // TSyncServer::requestedAuthType
+
+
+// check if auth type is allowed
+bool TSyncServer::isAuthTypeAllowed(TAuthTypes aAuthType)
+{
+ return aAuthType>=getServerConfig()->fRequiredAuth;
+} // TSyncServer::isAuthTypeAllowed
+
+
+#ifdef ENGINEINTERFACE_SUPPORT
+
+
+/// @brief Get new session key to access details of this session
+appPointer TSyncServer::newSessionKey(TEngineInterface *aEngineInterfaceP)
+{
+ return new TServerParamsKey(aEngineInterfaceP,this);
+} // TSyncServer::newSessionKey
+
+
+
+// Support for EngineModule common interface
+// =========================================
+
+
+#ifndef ENGINE_LIBRARY
+
+#ifndef _MSC_VER
+#warning "using ENGINEINTERFACE_SUPPORT in old-style appbase-rooted environment. Should be converted to real engine usage later"
+#endif
+
+// Engine factory function for non-Library case
+ENGINE_IF_CLASS *newEngine(void)
+{
+ // For real engine based targets, newEngine must create a target-specific derivate
+ // of the engine, which then has a suitable newSyncAppBase() method to create the
+ // appBase. For old-style environment, a generic TServerEngineInterface is ok, as this
+ // in turn calls the global newSyncAppBase() which then returns the appropriate
+ // target specific appBase.
+ return new TServerEngineInterface;
+} // newEngine
+
+/// @brief returns a new application base.
+TSyncAppBase *TServerEngineInterface::newSyncAppBase(void)
+{
+ // For not really engine based targets, the appbase factory function is
+ // a global routine (for real engine targets, it is a true virtual of
+ // the engineInterface, implemented in the target's leaf engineInterface derivate.
+ // - for now, use the global appBase creator routine
+ return sysync::newSyncAppBase(); // use global factory function
+} // TServerEngineInterface::newSyncAppBase
+
+#endif // not ENGINE_LIBRARY
+
+
+
+// Server runtime settings key
+// ---------------------------
+
+// Constructor
+TServerParamsKey::TServerParamsKey(TEngineInterface *aEngineInterfaceP, TSyncServer *aServerSessionP) :
+ inherited(aEngineInterfaceP,aServerSessionP),
+ fServerSessionP(aServerSessionP)
+{
+} // TServerParamsKey::TServerParamsKey
+
+
+// - read local session ID
+static TSyError readLocalSessionID(
+ TStructFieldsKey *aStructFieldsKeyP, const TStructFieldInfo *aFldInfoP,
+ appPointer aBuffer, memSize aBufSize, memSize &aValSize
+)
+{
+ TServerParamsKey *mykeyP = static_cast<TServerParamsKey *>(aStructFieldsKeyP);
+ return TStructFieldsKey::returnString(
+ mykeyP->fServerSessionP->getLocalSessionID(),
+ aBuffer,aBufSize,aValSize
+ );
+} // readLocalSessionID
+
+
+// accessor table for server session key
+static const TStructFieldInfo ServerParamFieldInfos[] =
+{
+ // valName, valType, writable, fieldOffs, valSiz
+ { "localSessionID", VALTYPE_TEXT, false, 0, 0, &readLocalSessionID, NULL },
+};
+
+// get table describing the fields in the struct
+const TStructFieldInfo *TServerParamsKey::getFieldsTable(void)
+{
+ return ServerParamFieldInfos;
+} // TServerParamsKey::getFieldsTable
+
+sInt32 TServerParamsKey::numFields(void)
+{
+ return sizeof(ServerParamFieldInfos)/sizeof(TStructFieldInfo);
+} // TServerParamsKey::numFields
+
+// get actual struct base address
+uInt8P TServerParamsKey::getStructAddr(void)
+{
+ // prepared for accessing fields in client session object
+ return (uInt8P)fServerSessionP;
+} // TServerParamsKey::getStructAddr
+
+
+#endif ENGINEINTERFACE_SUPPORT
+
+
+} // namespace sysync
+
+/* end of TSyncServer implementation */
+
+// eof
diff --git a/src/sysync/syncserver.h b/src/sysync/syncserver.h
new file mode 100755
index 0000000..2532849
--- /dev/null
+++ b/src/sysync/syncserver.h
@@ -0,0 +1,252 @@
+/*
+ * File: SyncServer.h
+ *
+ * Author: Lukas Zeller (luz@synthesis.ch)
+ *
+ * TSyncServer
+ * <describe here>
+ *
+ * Copyright (c) 2001-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ * 2001-05-07 : luz : Created
+ *
+ */
+
+#ifndef SyncServer_H
+#define SyncServer_H
+
+// includes
+#include "syncsessiondispatch.h"
+#include "syncsession.h"
+#include "syncdatastore.h"
+#include "remotedatastore.h"
+
+
+using namespace sysync;
+
+
+namespace sysync {
+
+// Support for SySync Diagnostic Tool
+#ifdef SYSYNC_TOOL
+int testLogin(int argc, const char *argv[]);
+int convertData(int argc, const char *argv[]);
+#endif
+
+// forward
+class TSyncSessionDispatch;
+class TSyncSessionHandle;
+class TSyncServer;
+
+
+// server config
+class TServerConfig: public TSessionConfig
+{
+ typedef TSessionConfig inherited;
+public:
+ TServerConfig(const char *aElementName, TConfigElement *aParentElementP);
+ virtual ~TServerConfig();
+ // General server settings
+ // - requested auth type (Auth requested in Chal sent to client)
+ TAuthTypes fRequestedAuth;
+ // - minimally required auth type (lowest auth type that is allowed)
+ TAuthTypes fRequiredAuth;
+ // - use automatic nonce generation for MD5 auth (empty nonce if false)
+ bool fAutoNonce;
+ // - constant nonce string to be used if autononce is off. If empty, no nonce is used
+ string fConstantNonce;
+ // - constant external URL, if set, it is used to generate RespURI (instead of Target LocURI sent by client)
+ string fExternalURL;
+ // - max size of GUID sent if client does not specify a MaxGUIDSize in devInf. 0=unlimited
+ uInt16 fMaxGUIDSizeSent;
+protected:
+ // check config elements
+ #ifndef HARDCODED_CONFIG
+ virtual bool localStartElement(const char *aElementName, const char **aAttributes, sInt32 aLine);
+ #endif
+ virtual void clear();
+ virtual void localResolve(bool aLastPass);
+public:
+ // create appropriate session (=agent) for this server
+ virtual TSyncServer *CreateServerSession(TSyncSessionHandle *aSessionHandle, const char *aSessionID)=0;
+}; // TServerConfig
+
+
+// server session
+class TSyncServer: public TSyncSession
+{
+ typedef TSyncSession inherited;
+public:
+ TSyncServer(
+ TSyncAppBase *aAppBaseP,
+ TSyncSessionHandle *aSessionHandleP,
+ const char *aSessionID // a session ID
+ );
+ virtual ~TSyncServer();
+ virtual void ResetSession(void); // like destructor, but without destructing object itself
+ void InternalResetSession(void); // static implementation for calling through virtual destructor and virtual ResetSession();
+ virtual SmlPcdataPtr_t newResponseURIForRemote(void); // response URI
+ // info about session
+ virtual bool IsServerSession(void) { return true; }; // is server
+ // info about needed auth type
+ virtual TAuthTypes requestedAuthType(void);
+ virtual bool isAuthTypeAllowed(TAuthTypes aAuthType);
+ // Request processing
+ // - called when incoming SyncHdr fails to execute
+ virtual bool syncHdrFailure(bool aTryAgain);
+ // - end of request (to make sure even incomplete SyncML messages get cleaned up properly)
+ bool EndRequest(bool &aHasData, string &aRespURI, uInt32 aReqBytes); // returns true if session must be deleted
+ // - buffer answer in the session's buffer if transport allows it
+ Ret_t bufferAnswer(MemPtr_t aAnswer, MemSize_t aAnswerSize);
+ // - get buffered answer from the session's buffer if there is any
+ void getBufferedAnswer(MemPtr_t &aAnswer, MemSize_t &aAnswerSize);
+ // - get byte statistics
+ virtual uInt32 getIncomingBytes(void) { return fIncomingBytes; };
+ virtual uInt32 getOutgoingBytes(void) { return fOutgoingBytes; };
+ // session handling
+ // - get session Handle pointer
+ TSyncSessionHandle *getSessionHandle(void) { return fSessionHandleP; }
+ // returns remaining time for request processing [seconds]
+ virtual sInt32 RemainingRequestTime(void);
+ // info about server status
+ virtual bool serverBusy(void); // return busy status (set by connection limit or app expiry)
+ // Sync processing (command group)
+ // - start sync group
+ virtual bool processSyncStart(
+ SmlSyncPtr_t aSyncP, // the Sync element
+ TStatusCommand &aStatusCommand, // pre-set 200 status, can be modified in case of errors
+ bool &aQueueForLater // will be set if command must be queued for later (re-)execution
+ );
+ #ifdef ENGINEINTERFACE_SUPPORT
+ /// @brief Get new session key to access details of this session
+ virtual appPointer newSessionKey(TEngineInterface *aEngineInterfaceP);
+ #endif
+protected:
+ // access to config
+ TServerConfig *getServerConfig(void);
+ // internal processing events
+ // - message start and end
+ virtual bool MessageStarted(SmlSyncHdrPtr_t aContentP, TStatusCommand &aStatusCommand, bool aBad=false);
+ virtual void MessageEnded(bool aIncomingFinal);
+ // - request end, called by EndRequest, virtual for descendants
+ virtual void RequestEnded(bool &aHasData);
+ // - map operation
+ virtual bool processMapCommand(
+ SmlMapPtr_t aMapCommandP, // the map command contents
+ TStatusCommand &aStatusCommand, // pre-set 200 status, can be modified in case of errors
+ bool &aQueueForLater
+ );
+ // Session level auth
+ // - get next nonce string top be sent to remote party for subsequent MD5 auth
+ virtual void getNextNonce(const char *aDeviceID, string &aNextNonce);
+ // - get nonce string, which is expected to be used by remote party for MD5 auth.
+ virtual void getAuthNonce(const char *aDeviceID, string &aAuthNonce);
+ // device info (uses defaults for server, override to customize)
+ virtual string getDeviceID(void) { return SYSYNC_SERVER_DEVID; }
+ virtual string getDeviceType(void) { return SYNCML_SERVER_DEVTYP; }
+ // set if map command received in this session
+ bool fMapSeen;
+ // standard nonce generation (without persistent device info)
+ // %%% note: move this to session when we start supporting client auth checking
+ string fLastNonce; // last nonce, will be returned at getAuthNonce()
+ // busy status
+ bool fServerIsBusy;
+ // buffered answer
+ MemPtr_t fBufferedAnswer;
+ MemSize_t fBufferedAnswerSize;
+ // data transfer statistics
+ uInt32 fIncomingBytes;
+ uInt32 fOutgoingBytes;
+ // server session handle
+ TSyncSessionHandle *fSessionHandleP; // the session "handle" (wrapper, containing server specific locking etc.)
+}; // TSyncServer
+
+
+#ifdef ENGINEINTERFACE_SUPPORT
+
+// Support for EngineModule common interface
+// =========================================
+
+
+// Engine module class
+class TServerEngineInterface :
+ public TEngineInterface
+{
+ typedef TEngineInterface inherited;
+public:
+ // constructor
+ TServerEngineInterface() {};
+
+ // appbase factory
+ virtual TSyncAppBase *newSyncAppBase(void);
+
+ #ifdef ENGINE_LIBRARY
+ #error "%%% tbd: Server version of the session running routines must be implemented"
+ // Running a Server Sync Session
+ // -----------------------------
+
+ /// @brief Open a session
+ /// @param aNewSessionH[out] receives session handle for all session execution calls
+ /// @param aSelector[in] selector, depending on session type.
+ /// @param aSessionName[in] a text name/id to identify a session, useage depending on session type.
+ /// @return LOCERR_OK on success, SyncML or LOCERR_xxx error code on failure
+ virtual TSyError OpenSessionInternal(SessionH &aNewSessionH, uInt32 aSelector, cAppCharP aSessionName);
+
+ /// @brief open session specific runtime parameter/settings key
+ /// @note key handle obtained with this call must be closed BEFORE SESSION IS CLOSED!
+ /// @param aNewKeyH[out] receives the opened key's handle on success
+ /// @param aSessionH[in] session handle obtained with OpenSession
+ /// @param aMode[in] the open mode
+ /// @return LOCERR_OK on success, SyncML or LOCERR_xxx error code on failure
+ virtual TSyError OpenSessionKey(SessionH aSessionH, KeyH &aNewKeyH, uInt16 aMode);
+
+ /// @brief Close a session
+ /// @note It depends on session type if this also destroys the session or if it may persist and can be re-opened.
+ /// @param aSessionH[in] session handle obtained with OpenSession
+ /// @return LOCERR_OK on success, SyncML or LOCERR_xxx error code on failure
+ virtual TSyError CloseSession(SessionH aSessionH);
+
+ /// @brief Executes sync session or other sync related activity step by step
+ /// @param aSessionH[in] session handle obtained with OpenSession
+ /// @param aStepCmd[in/out] step command (STEPCMD_xxx):
+ /// - tells caller to send or receive data or end the session etc.
+ /// - instructs engine to suspend or abort the session etc.
+ /// @param aInfoP[in] pointer to a TEngineProgressInfo structure, NULL if no progress info needed
+ /// @return LOCERR_OK on success, SyncML or LOCERR_xxx error code on failure
+ virtual TSyError SessionStep(SessionH aSessionH, uInt16 &aStepCmd, TEngineProgressInfo *aInfoP = NULL);
+ #endif
+
+}; // TServerEngineInterface
+
+
+// server runtime parameters
+class TServerParamsKey :
+ public TSessionKey
+{
+ typedef TSessionKey inherited;
+
+public:
+ TServerParamsKey(TEngineInterface *aEngineInterfaceP, TSyncServer *aServerSessionP);
+
+ virtual ~TServerParamsKey() {};
+
+protected:
+ // get table describing the fields in the struct
+ virtual const TStructFieldInfo *getFieldsTable(void);
+ virtual sInt32 numFields(void);
+ // get actual struct base address
+ virtual uInt8P getStructAddr(void);
+public:
+ // the associated server session
+ TSyncServer *fServerSessionP;
+}; // TServerParamsKey
+
+#endif // ENGINEINTERFACE_SUPPORT
+
+
+} // namespace sysync
+
+#endif // SyncServer_H
+
+// eof
+
diff --git a/src/sysync/syncsession.cpp b/src/sysync/syncsession.cpp
new file mode 100644
index 0000000..60cd6fc
--- /dev/null
+++ b/src/sysync/syncsession.cpp
@@ -0,0 +1,5592 @@
+/*
+ * TSyncSession
+ * Represents an entire Synchronisation Session, possibly consisting
+ * of multiple SyncML-Toolkit "Sessions" (Message composition/de-
+ * composition) as well as multiple database synchronisations.
+ *
+ * Copyright (c) 2001-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ * 2001-05-07 : luz : Created
+ *
+ */
+
+
+#include "prefix_file.h"
+
+#include "sysync.h"
+#include "syncsession.h"
+#ifdef SUPERDATASTORES
+ #include "superdatastore.h"
+#endif
+#ifdef SCRIPT_SUPPORT
+ #include "scriptcontext.h"
+#endif
+#ifdef MULTI_THREAD_SUPPORT
+ #include "platform_thread.h"
+#endif
+
+
+#ifndef SYNCSESSION_PART1_EXCLUDE
+
+namespace sysync {
+
+// enum names
+// ----------
+
+// SyncML version info
+const char * const SyncMLVerProtoNames[numSyncMLVersions] = {
+ "undefined",
+ "SyncML/1.0",
+ "SyncML/1.1",
+ "SyncML/1.2"
+};
+const SmlVersion_t SmlVersionCodes[numSyncMLVersions] = {
+ SML_VERS_UNDEF,
+ SML_VERS_1_0,
+ SML_VERS_1_1,
+ SML_VERS_1_2
+};
+const char * const SyncMLVerDTDNames[numSyncMLVersions] = {
+ "???",
+ "1.0",
+ "1.1",
+ "1.2"
+};
+const char * const SyncMLDevInfNames[numSyncMLVersions] = {
+ NULL,
+ "./devinf10",
+ "./devinf11",
+ "./devinf12"
+};
+#ifndef HARDCODED_CONFIG
+// version for use in config files
+const char * const SyncMLVersionNames[numSyncMLVersions] = {
+ "unknown",
+ "1.0",
+ "1.1",
+ "1.2"
+};
+#endif
+
+
+// auth type names for config
+const char * const authTypeNames[numAuthTypes] = {
+ "none", // no authorisation
+ "basic", // basic (B64 encoded user pw string)
+ "md5", // Md5 encoded user:pw:nonce
+};
+
+
+// sync mode names
+const char * const SyncModeNames[numSyncModes] = {
+ "twoway",
+ "fromserver",
+ "fromclient"
+};
+
+
+
+#ifdef SYDEBUG
+// package state names
+const char * const PackageStateNames[numPackageStates] = {
+ "idle",
+ "init",
+ "sync",
+ "initsync",
+ "map",
+ "supplement"
+};
+
+// sync operations
+const char * const SyncOpNames[numSyncOperations] = {
+ "wants-add",
+ "add",
+ "wants-replace",
+ "replace",
+ "reference-only",
+ "archive+delete",
+ "soft-delete",
+ "delete",
+ "copy",
+ "move",
+ "[none]" // should be last
+};
+
+#endif
+
+// sync mode descriptions
+const char * const SyncModeDescriptions[numSyncModes] = {
+ "two-way",
+ "from server only",
+ "from client only"
+};
+
+
+#ifdef SCRIPT_SUPPORT
+
+// builtin functions for status-handling scripts
+
+// integer STATUS()
+static void func_Status(TItemField *&aTermP, TScriptContext *aFuncContextP)
+{
+ TErrorFuncContext *errctxP = static_cast<TErrorFuncContext *>(aFuncContextP->getCallerContext());
+ aTermP->setAsInteger(
+ errctxP->statuscode
+ );
+} // func_Status
+
+
+// void SETSTATUS(integer statuscode)
+static void func_SetStatus(TItemField *&aTermP, TScriptContext *aFuncContextP)
+{
+ TErrorFuncContext *errctxP = static_cast<TErrorFuncContext *>(aFuncContextP->getCallerContext());
+ errctxP->newstatuscode=
+ aFuncContextP->getLocalVar(0)->getAsInteger();
+} // func_SetStatus
+
+
+// void SETRESEND(boolean doresend)
+static void func_SetResend(TItemField *&aTermP, TScriptContext *aFuncContextP)
+{
+ TErrorFuncContext *errctxP = static_cast<TErrorFuncContext *>(aFuncContextP->getCallerContext());
+ errctxP->resend=
+ aFuncContextP->getLocalVar(0)->getAsBoolean();
+} // func_SetResend
+
+
+// void ABORTDATASTORE(integer statuscode)
+static void func_AbortDatastore(TItemField *&aTermP, TScriptContext *aFuncContextP)
+{
+ TErrorFuncContext *errctxP = static_cast<TErrorFuncContext *>(aFuncContextP->getCallerContext());
+ if (errctxP->datastoreP) {
+ errctxP->datastoreP->engAbortDataStoreSync(aFuncContextP->getLocalVar(0)->getAsInteger(),true); // we cause the abort locally
+ }
+} // func_AbortDatastore
+
+
+// void STOPADDING()
+static void func_StopAdding(TItemField *&aTermP, TScriptContext *aFuncContextP)
+{
+ TErrorFuncContext *errctxP = static_cast<TErrorFuncContext *>(aFuncContextP->getCallerContext());
+ if (errctxP->datastoreP) {
+ errctxP->datastoreP->engStopAddingToRemote();
+ }
+} // func_StopAdding
+
+
+// string SYNCOP()
+// returns sync-operation as text
+static void func_SyncOp(TItemField *&aTermP, TScriptContext *aFuncContextP)
+{
+ TErrorFuncContext *errctxP = static_cast<TErrorFuncContext *>(aFuncContextP->getCallerContext());
+ aTermP->setAsString(
+ SyncOpNames[errctxP->syncop]
+ );
+} // func_SyncOp
+
+
+const uInt8 param_OneInteger[] = { VAL(fty_integer) };
+const uInt8 param_TwoIntegers[] = { VAL(fty_integer), VAL(fty_integer) };
+const uInt8 param_OneString[] = { VAL(fty_string) };
+
+const TBuiltInFuncDef ErrorFuncDefs[] = {
+ { "STATUS", func_Status, fty_integer, 0, NULL },
+ { "SETSTATUS", func_SetStatus, fty_none, 1, param_OneInteger },
+ { "SETRESEND", func_SetResend, fty_none, 1, param_OneInteger },
+ { "ABORTDATASTORE", func_AbortDatastore, fty_none, 1, param_OneInteger },
+ { "STOPADDING", func_StopAdding, fty_none, 0, NULL },
+ { "SYNCOP", func_SyncOp, fty_string, 0, NULL },
+};
+
+const TFuncTable ErrorFuncTable = {
+ sizeof(ErrorFuncDefs) / sizeof(TBuiltInFuncDef), // size of table
+ ErrorFuncDefs, // table pointer
+ NULL // no chain func
+};
+
+
+// void SETSTATUS(integer statuscode)
+static void func_GetPutResSetStatus(TItemField *&aTermP, TScriptContext *aFuncContextP)
+{
+ TGetPutResultFuncContext *gprctxP = static_cast<TGetPutResultFuncContext *>(aFuncContextP->getCallerContext());
+ gprctxP->statuscode=
+ aFuncContextP->getLocalVar(0)->getAsInteger();
+} // func_GetPutResSetStatus
+
+
+// integer ISPUT()
+static void func_IsPut(TItemField *&aTermP, TScriptContext *aFuncContextP)
+{
+ TGetPutResultFuncContext *gprctxP = static_cast<TGetPutResultFuncContext *>(aFuncContextP->getCallerContext());
+ aTermP->setAsBoolean(gprctxP->isPut);
+} // func_IsPut
+
+
+// string ITEMURI()
+static void func_ItemURI(TItemField *&aTermP, TScriptContext *aFuncContextP)
+{
+ TGetPutResultFuncContext *gprctxP = static_cast<TGetPutResultFuncContext *>(aFuncContextP->getCallerContext());
+ aTermP->setAsString(gprctxP->itemURI);
+} // func_ItemURI
+
+
+// void SETITEMURI(string data)
+static void func_SetItemURI(TItemField *&aTermP, TScriptContext *aFuncContextP)
+{
+ TGetPutResultFuncContext *gprctxP = static_cast<TGetPutResultFuncContext *>(aFuncContextP->getCallerContext());
+ aFuncContextP->getLocalVar(0)->getAsString(gprctxP->itemURI);
+} // func_SetItemURI
+
+
+// string ITEMDATA()
+static void func_ItemData(TItemField *&aTermP, TScriptContext *aFuncContextP)
+{
+ TGetPutResultFuncContext *gprctxP = static_cast<TGetPutResultFuncContext *>(aFuncContextP->getCallerContext());
+ aTermP->setAsString(gprctxP->itemData);
+} // func_ItemData
+
+
+// void SETITEMDATA(string data)
+static void func_SetItemData(TItemField *&aTermP, TScriptContext *aFuncContextP)
+{
+ TGetPutResultFuncContext *gprctxP = static_cast<TGetPutResultFuncContext *>(aFuncContextP->getCallerContext());
+ aFuncContextP->getLocalVar(0)->getAsString(gprctxP->itemData);
+} // func_SetItemData
+
+
+// string METATYPE()
+static void func_MetaType(TItemField *&aTermP, TScriptContext *aFuncContextP)
+{
+ TGetPutResultFuncContext *gprctxP = static_cast<TGetPutResultFuncContext *>(aFuncContextP->getCallerContext());
+ aTermP->setAsString(gprctxP->metaType);
+} // func_MetaType
+
+
+// void SETMETATYPE(string data)
+static void func_SetMetaType(TItemField *&aTermP, TScriptContext *aFuncContextP)
+{
+ TGetPutResultFuncContext *gprctxP = static_cast<TGetPutResultFuncContext *>(aFuncContextP->getCallerContext());
+ aFuncContextP->getLocalVar(0)->getAsString(gprctxP->metaType);
+} // func_SetMetaType
+
+
+// void ISSUEPUT(boolean allowFailure, boolean noResp)
+// use ITEMURI, ITEMDATA and METATYPE to issue a PUT command
+static void func_IssuePut(TItemField *&aTermP, TScriptContext *aFuncContextP)
+{
+ TGetPutResultFuncContext *gprctxP = static_cast<TGetPutResultFuncContext *>(aFuncContextP->getCallerContext());
+ if (gprctxP->canIssue) {
+ TPutCommand *putcommandP = new TPutCommand(aFuncContextP->getSession());
+ putcommandP->setMeta(newMetaType(gprctxP->metaType.c_str()));
+ SmlItemPtr_t putItemP = putcommandP->addSourceLocItem(gprctxP->itemURI.c_str());
+ // - add data to item
+ putItemP->data = newPCDataString(gprctxP->itemData);
+ // issue it
+ if (aFuncContextP->getLocalVar(0)->getAsBoolean()) putcommandP->allowFailure(); // allow failure (4xx or 5xx status)
+ aFuncContextP->getSession()->issueRootPtr(putcommandP,aFuncContextP->getLocalVar(1)->getAsBoolean());
+ }
+} // func_IssuePut
+
+
+// void ISSUEGET(boolean allowFailure)
+// use ITEMURI and METATYPE to issue a GET command
+static void func_IssueGet(TItemField *&aTermP, TScriptContext *aFuncContextP)
+{
+ TGetPutResultFuncContext *gprctxP = static_cast<TGetPutResultFuncContext *>(aFuncContextP->getCallerContext());
+ if (gprctxP->canIssue) {
+ TGetCommand *getcommandP = new TGetCommand(aFuncContextP->getSession());
+ getcommandP->addTargetLocItem(gprctxP->itemURI.c_str());
+ getcommandP->setMeta(newMetaType(gprctxP->metaType.c_str()));
+ // issue it
+ if (aFuncContextP->getLocalVar(0)->getAsBoolean()) getcommandP->allowFailure(); // allow failure (4xx or 5xx status)
+ aFuncContextP->getSession()->issueRootPtr(getcommandP,false); // get with noResp does not make sense
+ }
+} // func_IssueGet
+
+
+// void ISSUEALERT(boolean allowFailure, integer alertcode)
+// use ITEMDATA to add an Alert item
+static void func_IssueAlert(TItemField *&aTermP, TScriptContext *aFuncContextP)
+{
+ TGetPutResultFuncContext *gprctxP = static_cast<TGetPutResultFuncContext *>(aFuncContextP->getCallerContext());
+ if (gprctxP->canIssue) {
+ uInt16 alertcode = aFuncContextP->getLocalVar(1)->getAsInteger();
+ TAlertCommand *alertCommandP = new TAlertCommand(aFuncContextP->getSession(),NULL,alertcode);
+ // - add string data item
+ alertCommandP->addItem(newStringDataItem(gprctxP->itemData.c_str()));
+ // issue it
+ if (aFuncContextP->getLocalVar(0)->getAsBoolean()) alertCommandP->allowFailure(); // allow failure (4xx or 5xx status)
+ aFuncContextP->getSession()->issueRootPtr(alertCommandP,false); // Alert with noResp not supported
+ }
+} // func_IssueAlert
+
+
+
+
+const TBuiltInFuncDef GetPutResultFuncDefs[] = {
+ { "SETSTATUS", func_GetPutResSetStatus, fty_none, 1, param_OneInteger },
+ { "ISPUT", func_IsPut, fty_integer, 0, NULL },
+ { "ITEMURI", func_ItemURI, fty_string, 0, NULL },
+ { "SETITEMURI", func_SetItemURI, fty_none, 1, param_OneString },
+ { "ITEMDATA", func_ItemData, fty_string, 0, NULL },
+ { "SETITEMDATA", func_SetItemData, fty_none, 1, param_OneString },
+ { "METATYPE", func_MetaType, fty_string, 0, NULL },
+ { "SETMETATYPE", func_SetMetaType, fty_none, 1, param_OneString },
+ { "ISSUEPUT", func_IssuePut, fty_none, 2, param_TwoIntegers },
+ { "ISSUEGET", func_IssueGet, fty_none, 1, param_OneInteger },
+ { "ISSUEALERT", func_IssueAlert, fty_none, 2, param_TwoIntegers }
+};
+
+const TFuncTable GetPutResultFuncTable = {
+ sizeof(GetPutResultFuncDefs) / sizeof(TBuiltInFuncDef), // size of table
+ GetPutResultFuncDefs, // table pointer
+ NULL // no chain func
+};
+
+
+
+#endif
+
+
+#ifndef NO_REMOTE_RULES
+
+// Remote Rule Config
+// ==================
+
+#define DONT_REJECT 0xFFFF
+
+// config constructor
+TRemoteRuleConfig::TRemoteRuleConfig(const char *aElementName, TConfigElement *aParentElementP) :
+ TConfigElement(aElementName,aParentElementP)
+{
+ clear();
+} // TRemoteRuleConfig::TRemoteRuleConfig
+
+
+// config destructor
+TRemoteRuleConfig::~TRemoteRuleConfig()
+{
+ clear();
+} // TRemoteRuleConfig::~TRemoteRuleConfig
+
+
+// init defaults
+void TRemoteRuleConfig::clear(void)
+{
+ // init defaults
+ // - id
+ fManufacturer.erase();
+ fModel.erase();
+ fOem.erase();
+ fFirmwareVers.erase();
+ fSoftwareVers.erase();
+ fHardwareVers.erase();
+ fDevId.erase();
+ fDevTyp.erase();
+ // - options
+ fRejectStatusCode=DONT_REJECT; // not rejected
+ fLimitedFieldLengths=-1; // set if remote has limited field lengths
+ fDontSendEmptyProperties=-1; // set if remote does not want empty properties
+ fDoQuote8BitContent=-1; // normally, only use QP for contents with EOLNs in vCard 2.1
+ fDoNotFoldContent=-1; // normally, content must be folded in MIME-DIR
+ fNoReplaceInSlowsync=-1; // normally, we are allowed to use Replace (as server) in slow sync
+ fTreatRemoteTimeAsLocal=-1; // do not ignore time zone
+ fTreatRemoteTimeAsUTC=-1; // do not ignore time zone
+ fVCal10EnddatesSameDay=-1; // use default end date rendering
+ fIgnoreDevInfMaxSize=-1; // do not ignore max field size in remote's devInf
+ fIgnoreCTCap=-1; // do not ignore CTCap
+ fDSPathInDevInf=-1; // use actual DS path as used in Alert for creating datastore devInf (needed for newer Nokia clients)
+ fDSCgiInDevInf=-1; // also show CGI as used in Alert for creating datastore devInf (needed for newer Nokia clients)
+ fForceUTC=-1; // automatic decision based on DevInf (SyncML 1.1) or just UTC for SyncML 1.0
+ fForceLocaltime=-1;
+ fTreatCopyAsAdd=-1;
+ fCompleteFromClientOnly=-1;
+ fRequestMaxTime=-1; // not defined
+ fDefaultOutCharset=chs_unknown; // do not set the charset
+ // - options that also have a configurable session default
+ fUpdateClientDuringSlowsync=-1;
+ fUpdateServerDuringSlowsync=-1;
+ fAllowMessageRetries=-1;
+ fStrictExecOrdering=-1;
+ #ifndef MINIMAL_CODE
+ fRemoteDescName.erase();
+ #endif
+ // - rules are final by default
+ fFinalRule = true;
+ // clear inherited
+ inherited::clear();
+} // TRemoteRuleConfig::clear
+
+
+#ifndef HARDCODED_CONFIG
+
+// remote rule config element parsing
+bool TRemoteRuleConfig::localStartElement(const char *aElementName, const char **aAttributes, sInt32 aLine)
+{
+ // checking the elements
+ // - identification of remote
+ if (strucmp(aElementName,"manufacturer")==0)
+ expectString(fManufacturer);
+ else if (strucmp(aElementName,"model")==0)
+ expectString(fModel);
+ else if (strucmp(aElementName,"oem")==0)
+ expectString(fOem);
+ else if (strucmp(aElementName,"firmware")==0)
+ expectString(fFirmwareVers);
+ else if (strucmp(aElementName,"software")==0)
+ expectString(fSoftwareVers);
+ else if (strucmp(aElementName,"hardware")==0)
+ expectString(fHardwareVers);
+ else if (strucmp(aElementName,"deviceid")==0)
+ expectString(fDevId);
+ else if (strucmp(aElementName,"devicetype")==0)
+ expectString(fDevTyp);
+ // - options
+ else if (strucmp(aElementName,"limitedfieldlengths")==0)
+ expectTristate(fLimitedFieldLengths);
+ else if (strucmp(aElementName,"noemptyproperties")==0)
+ expectTristate(fDontSendEmptyProperties);
+ else if (strucmp(aElementName,"quote8bitcontent")==0)
+ expectTristate(fDoQuote8BitContent);
+ else if (strucmp(aElementName,"nocontentfolding")==0)
+ expectTristate(fDoNotFoldContent);
+ else if (strucmp(aElementName,"noreplaceinslowsync")==0)
+ expectTristate(fNoReplaceInSlowsync);
+ else if (strucmp(aElementName,"treataslocaltime")==0)
+ expectTristate(fTreatRemoteTimeAsLocal);
+ else if (strucmp(aElementName,"treatasutc")==0)
+ expectTristate(fTreatRemoteTimeAsUTC);
+ else if (strucmp(aElementName,"autoenddateinclusive")==0)
+ expectTristate(fVCal10EnddatesSameDay);
+ else if (strucmp(aElementName,"ignoredevinfmaxsize")==0)
+ expectTristate(fIgnoreDevInfMaxSize);
+ else if (strucmp(aElementName,"ignorectcap")==0)
+ expectTristate(fIgnoreCTCap);
+ else if (strucmp(aElementName,"dspathindevinf")==0)
+ expectTristate(fDSPathInDevInf);
+ else if (strucmp(aElementName,"dscgiindevinf")==0)
+ expectTristate(fDSCgiInDevInf);
+ else if (strucmp(aElementName,"updateclientinslowsync")==0)
+ expectTristate(fUpdateClientDuringSlowsync);
+ else if (strucmp(aElementName,"updateserverinslowsync")==0)
+ expectTristate(fUpdateServerDuringSlowsync);
+ else if (strucmp(aElementName,"allowmessageretries")==0)
+ expectTristate(fAllowMessageRetries);
+ else if (strucmp(aElementName,"strictexecordering")==0)
+ expectTristate(fStrictExecOrdering);
+ else if (strucmp(aElementName,"treatcopyasadd")==0)
+ expectTristate(fTreatCopyAsAdd);
+ else if (strucmp(aElementName,"completefromclientonly")==0)
+ expectTristate(fCompleteFromClientOnly);
+ else if (strucmp(aElementName,"requestmaxtime")==0)
+ expectInt32(fRequestMaxTime);
+ else if (strucmp(aElementName,"outputcharset")==0)
+ expectEnum(sizeof(fDefaultOutCharset),&fDefaultOutCharset,MIMECharSetNames,numCharSets);
+ else if (strucmp(aElementName,"rejectstatus")==0)
+ expectUInt16(fRejectStatusCode);
+ else if (strucmp(aElementName,"forceutc")==0)
+ expectTristate(fForceUTC);
+ else if (strucmp(aElementName,"forcelocaltime")==0)
+ expectTristate(fForceLocaltime);
+ /*
+ // Some extra tweaking params for unstable devices and connections
+ else if (strucmp(aElementName,"maxobjspersession")==0)
+ expectUInt32(fMaxObjsPerSession); // max number of object add/deletes sent per session
+ else if (strucmp(aElementName,"maxkbspersession")==0)
+ expectUInt32(fMaxKBsPerSession); // max number of kilobytes content data sent per session
+ else if (strucmp(aElementName,"maxmessagesize")==0)
+ expectUInt32(fMaxMessageSize); // do not send larger messages than these (except if otherwise item cannot be sent)
+ */
+ // rule script. Note that this is special, as it is NOT resolved in the config, but
+ // copied to the session first, as it might differ between sessions.
+ #ifdef SCRIPT_SUPPORT
+ else if (strucmp(aElementName,"rulescript")==0)
+ expectScript(fRuleScriptTemplate,aLine,NULL,true); // late binding, no declarations allowed
+ #endif
+ #ifndef MINIMAL_CODE
+ else if (strucmp(aElementName,"descriptivename")==0)
+ expectString(fRemoteDescName);
+ #endif
+ // - final rule?
+ else if (strucmp(aElementName,"finalrule")==0)
+ expectBool(fFinalRule);
+ // - not known here
+ else
+ return inherited::localStartElement(aElementName,aAttributes,aLine);
+ // ok
+ return true;
+} // TRemoteRuleConfig::localStartElement
+
+#endif // HARDCODED_CONFIG
+
+#endif // NO_REMOTE_RULES
+
+
+#endif // not SYNCSESSION_PART1_EXCLUDE
+#ifndef SYNCSESSION_PART2_EXCLUDE
+
+
+// Session Config
+// ==============
+
+
+// config constructor
+TSessionConfig::TSessionConfig(const char *aElementName, TConfigElement *aParentElementP) :
+ TAgentConfig(aElementName,aParentElementP)
+{
+ clear();
+} // TSessionConfig::TSessionConfig
+
+
+// config destructor
+TSessionConfig::~TSessionConfig()
+{
+ clear();
+} // TSessionConfig::~TSessionConfig
+
+
+// init defaults
+void TSessionConfig::clear(void)
+{
+ // init defaults
+ #ifndef NO_REMOTE_RULES
+ // - no remote rules
+ TRemoteRulesList::iterator pos;
+ for(pos=fRemoteRulesList.begin();pos!=fRemoteRulesList.end();pos++)
+ delete *pos;
+ fRemoteRulesList.clear();
+ #endif
+ // remove datastores
+ TLocalDSList::iterator pos2;
+ for(pos2=fDatastores.begin();pos2!=fDatastores.end();pos2++)
+ delete *pos2;
+ fDatastores.clear();
+ // - no simple auth
+ fSimpleAuthUser.erase();
+ fSimpleAuthPassword.erase();
+ // - medium timeout
+ fSessionTimeout=60; // one minute, will be overridden by derived classes
+ // - set default maximum SyncML version enabled
+ fMaxSyncMLVersionSupported=MAX_SYNCML_VERSION;
+ // - minimum is 1.0
+ fMinSyncMLVersionSupported=syncml_vers_1_0;
+ // - accept server-alerted codes by default
+ fAcceptServerAlerted=true;
+ // - defaults for remote-rule configurable behaviour
+ fUpdateClientDuringSlowsync=false; // do not update client records during slowsync (but do it for first sync!)
+ fUpdateServerDuringSlowsync=false; // do not update server records during NON-FIRST-TIME slowsync (but do it for first sync!)
+ fAllowMessageRetries=true; // generally allow retries
+ fCompleteFromClientOnly=false; // default to standard-compliant behaviour.
+ fRequestMaxTime=0; // no limit by default
+ fRequestMinTime=0; // no minimal request processing delay
+ // - default value for flag to send property lists in CTCap
+ fShowCTCapProps=true;
+ // - default value for flag to send type/size in CTCap for SyncML 1.0 (disable as old clients like S55 crash on this)
+ fShowTypeSzInCTCap10=false;
+ #ifdef SYSYNC_CLIENT
+ // - Synthesis clients always behaved like that (sending 23:59:59), so we'll keep it as a default
+ fVCal10EnddatesSameDay = true;
+ #else
+ // - Many modern clients need the exclusive format (start of next day) to detect all-day events properly.
+ // Synthesis clients detect these fine as well, so not using 23:59:59 style by default is more
+ // compatible in general for a server.
+ fVCal10EnddatesSameDay = false;
+ #endif
+ // traditionally Synthesis has folded content
+ fDoNotFoldContent = false;
+ // - default value for flag is "default" (depends on SyncML version)
+ fEnumDefaultPropParams=-1;
+ // - decide, whether multi-threading for the datastores will be used:
+ // As there are some problems with older Linux versions (e.g. Debian 3.0r2 stable) the
+ // default values are set for downwards compatibility Linux=false / all others=true
+ // Multithreading can be switched of either by #define or by <fMultiThread> flag
+ #if defined LINUX || !defined MULTI_THREAD_DATASTORE
+ fMultiThread= false;
+ #else
+ fMultiThread= true;
+ #endif
+ // - do not wait for status of interrupted command by default (note: before 2.1.0.2, this was always true)
+ fWaitForStatusOfInterrupted=false;
+ // - accept delete commands for already deleted items with 200 (rather that 404 or 211)
+ #ifdef SCTS_COMPATIBILITY_HACKS
+ fDeletingGoneOK=false; // SCTS needs that
+ #else
+ fDeletingGoneOK=true; // makes more sense as it avoids unnecessary session aborts
+ #endif
+ // - abort if all items sent to remote fail
+ fAbortOnAllItemsFailed=true; // note: does only apply in slow syncs now!
+ // - default to system time
+ fUserTimeContext=TCTX_SYSTEM;
+ #ifdef SCRIPT_SUPPORT
+ // - session init script
+ fSessionInitScript.erase();
+ // - status handling scripts
+ fSentItemStatusScript.erase();
+ fReceivedItemStatusScript.erase();
+ // - session termination script
+ fSessionFinishScript.erase();
+ // - custom get handler
+ fCustomGetHandlerScript.erase();
+ // - custom get and put generators
+ fCustomGetPutScript.erase();
+ fCustomEndPutScript.erase();
+ // - custom PUT and RESULT handler
+ fCustomPutResultHandlerScript.erase();
+ #endif
+ #ifndef MINIMAL_CODE
+ // - logfile
+ fLogFileName.erase();
+ fLogFileFormat.assign(DEFAULT_LOG_FORMAT);
+ fLogFileLabels.assign(DEFAULT_LOG_LABELS);
+ fLogEnabled=true;
+ fDebugChunkMaxSize=0; // disabled
+ #endif
+ fRelyOnEarlyMaps=true; // we rely on early maps sent by clients for adds from the previous session
+ // clear inherited
+ inherited::clear();
+} // TSessionConfig::clear
+
+
+// get local DS config pointer by database name or dbTypeID
+TLocalDSConfig *TSessionConfig::getLocalDS(const char *aName, uInt32 aDBTypeID)
+{
+ TLocalDSList::iterator pos;
+ for(pos=fDatastores.begin();pos!=fDatastores.end();pos++) {
+ if (aName==NULL) {
+ if ((*pos)->fLocalDBTypeID==aDBTypeID) return *pos; // found by DBTypeID
+ }
+ else {
+ if (strucmp((*pos)->getName(),aName)==0) return *pos; // found by name
+ }
+ }
+ return NULL; // not found
+} // TSessionConfig::getLocalDS
+
+
+#ifndef HARDCODED_CONFIG
+
+// server config element parsing
+bool TSessionConfig::localStartElement(const char *aElementName, const char **aAttributes, sInt32 aLine)
+{
+ // checking the elements
+ #ifndef NO_REMOTE_RULES
+ if (strucmp(aElementName,"remoterule")==0) {
+ // check for optional name attribute
+ const char* nam = getAttr(aAttributes,"name");
+ if (!nam) nam="unnamed";
+ // create rule
+ TRemoteRuleConfig *ruleP = new TRemoteRuleConfig(nam,this);
+ fRemoteRulesList.push_back(ruleP);
+ expectChildParsing(*ruleP);
+ }
+ else
+ #endif
+ if (strucmp(aElementName,"sessiontimeout")==0)
+ expectInt32(fSessionTimeout);
+ else if (strucmp(aElementName,"requestmaxtime")==0)
+ expectUInt32(fRequestMaxTime);
+ else if (strucmp(aElementName,"requestmintime")==0)
+ expectInt32(fRequestMinTime);
+ else if (strucmp(aElementName,"simpleauthuser")==0)
+ expectString(fSimpleAuthUser);
+ else if (strucmp(aElementName,"simpleauthpw")==0)
+ expectString(fSimpleAuthPassword);
+ else if (strucmp(aElementName,"maxsyncmlversion")==0)
+ expectEnum(sizeof(fMaxSyncMLVersionSupported),&fMaxSyncMLVersionSupported,SyncMLVersionNames,numSyncMLVersions);
+ else if (strucmp(aElementName,"minsyncmlversion")==0)
+ expectEnum(sizeof(fMinSyncMLVersionSupported),&fMinSyncMLVersionSupported,SyncMLVersionNames,numSyncMLVersions);
+ else if (strucmp(aElementName,"acceptserveralerted")==0)
+ expectBool(fAcceptServerAlerted);
+ else if (strucmp(aElementName,"updateclientinslowsync")==0)
+ expectBool(fUpdateClientDuringSlowsync);
+ else if (strucmp(aElementName,"updateserverinslowsync")==0)
+ expectBool(fUpdateServerDuringSlowsync);
+ else if (strucmp(aElementName,"completefromclientonly")==0)
+ expectBool(fCompleteFromClientOnly);
+ else if (strucmp(aElementName,"allowmessageretries")==0)
+ expectBool(fAllowMessageRetries);
+ else if (strucmp(aElementName,"multithread")==0)
+ expectBool(fMultiThread);
+ else if (strucmp(aElementName,"waitforstatusofinterrupted")==0)
+ expectBool(fWaitForStatusOfInterrupted);
+ else if (strucmp(aElementName,"deletinggoneok")==0)
+ expectBool(fDeletingGoneOK);
+ else if (strucmp(aElementName,"abortonallitemsfailed")==0)
+ expectBool(fAbortOnAllItemsFailed);
+ else if (strucmp(aElementName,"showctcapproperties")==0)
+ expectBool(fShowCTCapProps);
+ else if (strucmp(aElementName,"showtypesizeinctcap10")==0)
+ expectBool(fShowTypeSzInCTCap10);
+ else if (strucmp(aElementName,"autoenddateinclusive")==0)
+ expectBool(fVCal10EnddatesSameDay);
+ else if (strucmp(aElementName,"donotfoldcontent")==0)
+ expectBool(fDoNotFoldContent);
+ else if (strucmp(aElementName,"enumdefaultpropparams")==0)
+ expectTristate(fEnumDefaultPropParams); // Tristate!!!
+ else if (strucmp(aElementName,"usertimezone")==0)
+ expectTimezone(fUserTimeContext);
+ #ifdef SCRIPT_SUPPORT
+ else if (strucmp(aElementName,"sessioninitscript")==0)
+ expectScript(fSessionInitScript,aLine,NULL);
+ else if (strucmp(aElementName,"sentitemstatusscript")==0)
+ expectScript(fSentItemStatusScript,aLine,&ErrorFuncTable);
+ else if (strucmp(aElementName,"receiveditemstatusscript")==0)
+ expectScript(fReceivedItemStatusScript,aLine,&ErrorFuncTable);
+ else if (strucmp(aElementName,"sessionfinishscript")==0)
+ expectScript(fSessionFinishScript,aLine,NULL);
+ else if (strucmp(aElementName,"customgethandlerscript")==0)
+ expectScript(fCustomGetHandlerScript,aLine,&GetPutResultFuncTable);
+ else if (strucmp(aElementName,"customgetputscript")==0)
+ expectScript(fCustomGetPutScript,aLine,&GetPutResultFuncTable);
+ else if (strucmp(aElementName,"customendputscript")==0)
+ expectScript(fCustomEndPutScript,aLine,&GetPutResultFuncTable);
+ else if (strucmp(aElementName,"customputresulthandlerscript")==0)
+ expectScript(fCustomPutResultHandlerScript,aLine,&GetPutResultFuncTable);
+ #endif
+ #ifndef MINIMAL_CODE
+ // logfile
+ else if (strucmp(aElementName,"logfile")==0)
+ expectMacroString(fLogFileName);
+ else if (strucmp(aElementName,"logformat")==0)
+ expectCString(fLogFileFormat);
+ else if (strucmp(aElementName,"loglabels")==0)
+ expectCString(fLogFileLabels);
+ else if (strucmp(aElementName,"logenabled")==0)
+ expectBool(fLogEnabled);
+ else if (strucmp(aElementName,"debugchunkmaxsize")==0)
+ expectUInt32(fDebugChunkMaxSize);
+ #endif
+ else if (strucmp(aElementName,"relyonearlymaps")==0)
+ expectBool(fRelyOnEarlyMaps);
+ // - local datastores
+ else if (strucmp(aElementName,"datastore")==0) {
+ // definition of a new datastore
+ const char* nam = getAttr(aAttributes,"name");
+ if (!nam) {
+ ReportError(true,"datastore missing 'name' attribute");
+ }
+ else {
+ // get subtype attribute (some versions can have
+ // different datastore types in same agent)
+ const char* subtype = getAttr(aAttributes,"type");
+ // create new named datastore
+ TLocalDSConfig *datastorecfgP = newDatastoreConfig(nam,subtype,this);
+ if (!datastorecfgP)
+ ReportError(true,"datastore has unknown 'type' attribute");
+ else {
+ // - save in list
+ fDatastores.push_back(datastorecfgP);
+ // - let element handle parsing
+ expectChildParsing(*datastorecfgP);
+ }
+ }
+ }
+ #ifdef SUPERDATASTORES
+ // - superdatastore
+ else if (strucmp(aElementName,"superdatastore")==0) {
+ // definition of a new datastore
+ const char* nam = getAttr(aAttributes,"name");
+ if (!nam) {
+ ReportError(true,"datastore missing 'name' attribute");
+ }
+ else {
+ // create new named datastore
+ TLocalDSConfig *datastorecfgP = new TSuperDSConfig(nam,this);
+ // - save in list
+ fDatastores.push_back(datastorecfgP);
+ // - let element handle parsing
+ expectChildParsing(*datastorecfgP);
+ }
+ }
+ #endif
+ // - none known here
+ else
+ return TAgentConfig::localStartElement(aElementName,aAttributes,aLine);
+ // ok
+ return true;
+} // TSessionConfig::localStartElement
+
+#endif
+
+
+// resolve
+void TSessionConfig::localResolve(bool aLastPass)
+{
+ // resolve
+ if (aLastPass) {
+ #ifndef NO_REMOTE_RULES
+ // - resolve rules
+ TRemoteRulesList::iterator pos;
+ for(pos=fRemoteRulesList.begin();pos!=fRemoteRulesList.end();pos++)
+ (*pos)->Resolve(aLastPass);
+ #endif
+ #ifndef HARDCODED_CONFIG
+ // - resolve datastores
+ if (fDatastores.size()==0)
+ SYSYNC_THROW(TConfigParseException("At least one 'datastore' must be defined"));
+ #endif
+ TLocalDSList::iterator pos2;
+ for(pos2=fDatastores.begin();pos2!=fDatastores.end();pos2++)
+ (*pos2)->Resolve(aLastPass);
+ #ifdef SCRIPT_SUPPORT
+ TScriptContext *sccP = NULL;
+ SYSYNC_TRY {
+ // resolve all scripts in same context
+ // - init scripts
+ TScriptContext::resolveScript(getSyncAppBase(),fSessionInitScript,sccP,NULL);
+ TScriptContext::resolveScript(getSyncAppBase(),fSentItemStatusScript,sccP,NULL);
+ TScriptContext::resolveScript(getSyncAppBase(),fReceivedItemStatusScript,sccP,NULL);
+ TScriptContext::resolveScript(getSyncAppBase(),fSessionFinishScript,sccP,NULL);
+ TScriptContext::resolveScript(getSyncAppBase(),fCustomGetHandlerScript,sccP,NULL);
+ TScriptContext::resolveScript(getSyncAppBase(),fCustomGetPutScript,sccP,NULL);
+ TScriptContext::resolveScript(getSyncAppBase(),fCustomEndPutScript,sccP,NULL);
+ TScriptContext::resolveScript(getSyncAppBase(),fCustomPutResultHandlerScript,sccP,NULL);
+ // - forget this context
+ if (sccP) delete sccP;
+ }
+ SYSYNC_CATCH (...)
+ if (sccP) delete sccP;
+ SYSYNC_RETHROW;
+ SYSYNC_ENDCATCH
+ #endif
+ }
+ // resolve inherited
+ inherited::localResolve(aLastPass);
+} // TSessionConfig::localResolve
+
+
+
+
+// TSyncSession
+// ============
+
+// constructor
+TSyncSession::TSyncSession(
+ TSyncAppBase *aSyncAppBaseP, // the owning application base (dispatcher/client base)
+ const char *aSessionID // a session ID
+) :
+ #ifdef SYDEBUG
+ fSessionDebugLogs(0),
+ #endif
+ fTerminated(false),
+ #ifdef SYDEBUG
+ fSessionLogger(&fSessionZones),
+ #endif
+ fSyncAppBaseP(aSyncAppBaseP) // link to owning base (dispatcher/clienbase)
+{
+ // Inherit globally defined time zones
+ // Note: this must be done as very first step as all time output routines will use the
+ // session zones
+ fSessionZones = *(fSyncAppBaseP->getAppZones());
+ // now mark used to avoid early timeout (will be marked again at InternalResetSession())
+ SessionUsed();
+ fLastRequestStarted=getSessionLastUsed(); // set this in case we terminate before StartMessage()
+ fSessionStarted=fLastRequestStarted; // this is also the start of the session
+ // show creation
+ DEBUGPRINTFX(DBG_OBJINST,("++++++++ TSyncSession created"));
+ // assign session ID to have debug ID on correct channel
+ fLocalSessionID.assign(aSessionID);
+ DEBUGPRINTFX(DBG_EXOTIC,("TSyncSession::TSyncSession: Session ID assigned"));
+ // init and start profiling
+ MP_SHOWCURRENT(DBG_PROFILE,"TSyncSession::TSyncSession: TSyncSession created");
+ TP_INIT(fTPInfo);
+ TP_START(fTPInfo,TP_general);
+ DEBUGPRINTFX(DBG_EXOTIC,("TSyncSession::TSyncSession: Profiling initialized"));
+ // set fields
+ fLocalAbortReason = true; // unless set otherwise
+ fAbortReasonStatus = 0;
+ fSessionIsBusy = false; // not busy by default
+ fSmlWorkspaceID = 0; // no SyncML toolkit workspace ID yet
+ fMaxRoomForData = getRootConfig()->fLocalMaxMsgSize; // rough init
+ // other pointers
+ #ifdef SCRIPT_SUPPORT
+ fSessionScriptContextP = NULL;
+ #endif
+ fInterruptedCommandP = NULL;
+ fIncompleteDataCommandP = NULL;
+ #ifdef SYNCSTATUS_AT_SYNC_CLOSE
+ fSyncCloseStatusCommandP=NULL;
+ #endif
+ // we do not know anything about remote datastores yet
+ fRemoteDevInfKnown=false;
+ fRemoteDataStoresKnown=false;
+ fRemoteDataTypesKnown=false;
+ fRemoteDevInfLock=false;
+ // we have not sent any devinf to the remote yet
+ fRemoteGotDevinf=false;
+ fRemoteMustSeeDevinf=false;
+ fCustomGetPutSent=false;
+ // assume normal, full-featured session. Profile config or session progress might set this flag later
+ fLegacyMode = false;
+ #ifdef SYDEBUG
+ // initialize session debug logging
+ fSessionDebugLogs=getRootConfig()->fDebugConfig.fSessionDebugLogs; /// init from config @todo: get rid of this special session level flag, handle it all via session logger's fDebugEnabled / getDbgMask()
+ fSessionLogger.setEnabled(fSessionDebugLogs); // init from session-level flag @todo: get rid of this special session level flag, handle it all via session logger's fDebugEnabled / getDbgMask()
+ fSessionLogger.setMask(getRootConfig()->fDebugConfig.fDebug); // init from config
+ fSessionLogger.setOptions(&(getRootConfig()->fDebugConfig.fSessionDbgLoggerOptions));
+ fSessionLogger.installOutput(getSyncAppBase()->newDbgOutputter(false)); // install the output object (and pass ownership!)
+ fSessionLogger.setDebugPath(getRootConfig()->fDebugConfig.fDebugInfoPath.c_str()); // base path
+ fSessionLogger.appendToDebugPath(TARGETID);
+ if (getRootConfig()->fDebugConfig.fSingleSessionLog) {
+ getRootConfig()->fDebugConfig.fSessionDbgLoggerOptions.fAppend=true; // One single log - in this case, we MUST append to current log
+ fSessionLogger.appendToDebugPath("_session"); // only single session log, always with the same name
+ }
+ else {
+ if (getRootConfig()->fDebugConfig.fTimedSessionLogNames) {
+ fSessionLogger.appendToDebugPath("_");
+ string t;
+ TimestampToISO8601Str(t, getSystemNowAs(TCTX_UTC), TCTX_UTC, false, false);
+ fSessionLogger.appendToDebugPath(t.c_str());
+ }
+ fSessionLogger.appendToDebugPath("_s");
+ fSessionLogger.appendToDebugPath(fLocalSessionID.c_str());
+ }
+ fSessionLogger.DebugDefineMainThread();
+ // initialize session level dump flags
+ fDumpCount=0;
+ fIgnoreIncomingCommands=false;
+ fOutgoingXMLInstance=NULL;
+ fIncomingXMLInstance=NULL;
+ fXMLtranslate=getRootConfig()->fDebugConfig.fXMLtranslate; // initialize from config
+ fMsgDump=getRootConfig()->fDebugConfig.fMsgDump; // initialize from config
+ #endif
+
+ // reset session at creation
+ DEBUGPRINTFX(DBG_EXOTIC,("TSyncSession::TSyncSession: calling InternalResetSession"));
+ InternalResetSessionEx(false);
+ DEBUGPRINTFX(DBG_EXOTIC,("TSyncSession::TSyncSession: InternalResetSession called"));
+ // show starting
+ OBJ_PROGRESS_EVENT(getSyncAppBase(),pev_sessionstart,NULL,0,0,0);
+ #ifdef SYDEBUG
+ if (PDEBUGTEST(DBG_HOT)) {
+ // Show Session Start
+ PDEBUGPRINTFX(DBG_HOT,(
+ "==== Session started with SyncML Engine Version %d.%d.%d.%d",
+ SYSYNC_VERSION_MAJOR,
+ SYSYNC_VERSION_MINOR,
+ SYSYNC_SUBVERSION,
+ SYSYNC_BUILDNUMBER
+ ));
+ // show Product ID string
+ PDEBUGPRINTFX(DBG_HOT,(
+ "---- Hardcoded Product name: " CUST_SYNC_MODEL
+ ));
+ PDEBUGPRINTFX(DBG_HOT,(
+ "---- Configured Model/Manufacturer: %s / %s",
+ getSyncAppBase()->getModel().c_str(), getSyncAppBase()->getManufacturer().c_str()
+ ));
+ // show platform we're on
+ string uri;
+ getPlatformString(pfs_device_uri,uri);
+ PDEBUGPRINTFX(DBG_HOT,(
+ "---- Running on " SYSYNC_PLATFORM_NAME ", URI/deviceID='%s'",
+ uri.c_str()
+ ));
+ // show process and thread ID of the main session thread
+ #ifdef MULTI_THREAD_SUPPORT
+ PDEBUGPRINTFX(DBG_HOT,(
+ "---- Process ID = %lu, Thread ID = %lu",
+ myProcessID(),
+ myThreadID()
+ ));
+ #endif
+ // show platform details
+ string dname,vers;
+ getPlatformString(pfs_device_name,dname);
+ getPlatformString(pfs_platformvers,vers);
+ PDEBUGPRINTFX(DBG_HOT,(
+ "---- Platform Hardware Name/Version = '%s', Firmware/OS Version = '%s'",
+ dname.c_str(),
+ vers.c_str()
+ ));
+ // show time zone infos
+ lineartime_t tim;
+ string z,ts;
+ timecontext_t tctx;
+ sInt16 offs;
+ // - System local time and zone
+ tctx = getRootConfig()->fSystemTimeContext;
+ TzResolveMetaContext(tctx, getSessionZones()); // make non-meta
+ TimeZoneContextToName(tctx, z, getSessionZones());
+ tim = getSystemNowAs(tctx);
+ StringObjTimestamp(ts,tim);
+ TzResolveToOffset(tctx, offs, tim, false, getSessionZones());
+ PDEBUGPRINTFX(DBG_HOT,(
+ "---- System local time : %s (time zone '%s', offset %hd:%02hd hours east of UTC)",
+ ts.c_str(),z.c_str(),
+ (sInt16)(offs / MinsPerHour),
+ (sInt16)abs(offs % MinsPerHour)
+ ));
+ PDEBUGPRINTFX(DBG_EXOTIC,(" Offset in Minutes east of UTC: %hd",offs));
+ // - System time in UTC
+ tim = getSystemNowAs(TCTX_UTC);
+ StringObjTimestamp(ts,tim);
+ PDEBUGPRINTFX(DBG_HOT,("---- System time in UTC : %s",ts.c_str()));
+ // - make a winter and a summer test
+ if (PDEBUGTEST(DBG_EXOTIC)) {
+ sInt16 y,m,d;
+ lineartime2date(tim,&y,&m,&d);
+ d=1;m=2; // February 1st
+ tim=date2lineartime(y,m,d);
+ TzResolveToOffset(TCTX_SYSTEM, offs, tim, true, getSessionZones());
+ PDEBUGPRINTFX(DBG_EXOTIC,(
+ "---- System time zone offset per %04hd-02-01 = %hd:%02hd (=%hd mins)",
+ y, (sInt16)(offs / MinsPerHour), (sInt16)abs(offs % MinsPerHour), offs
+ ));
+ d=1;m=8; // August 1st
+ tim=date2lineartime(y,m,d);
+ TzResolveToOffset(TCTX_SYSTEM, offs, tim, true, getSessionZones());
+ PDEBUGPRINTFX(DBG_EXOTIC,(
+ "---- System time zone offset per %04hd-08-01 = %hd:%02hd (=%hd mins)",
+ y, (sInt16)(offs / MinsPerHour), (sInt16)abs(offs % MinsPerHour), offs
+ ));
+ }
+ }
+ #endif
+ DebugShowCfgInfo();
+} // TSyncSession::TSyncSession
+
+
+// destructor
+TSyncSession::~TSyncSession()
+{
+ // remove user data pointer because session does not exist any longer
+ getSyncAppBase()->setSmlInstanceUserData(fSmlWorkspaceID,NULL);
+ // make sure it is terminated (but normally it is already terminated here)
+ TerminateSession();
+ // debug
+ DEBUGPRINTFX(DBG_OBJINST,("-------- TSyncSession almost destroyed (except implicit member destruction)"));
+ #ifdef SYDEBUG
+ fSessionLogger.DebugThreadOutputDone();
+ #endif
+} // TSyncSession::~TSyncSession
+
+
+/// @brief terminate a session.
+/// @Note: Termination is final - session cannot be restarted by RestartSession() after
+// calling this routine
+void TSyncSession::TerminateSession(void)
+{
+ if (!fTerminated) {
+ // save type of ending (before fAborted gets reset in InternalResetSession())
+ bool normalend = !fAborted;
+ bool allsuccess = isAllSuccess();
+ // do this class' reset stuff
+ DEBUGPRINTFX(DBG_EXOTIC,("TSyncSession::TerminateSession: calling InternalResetSession"));
+ InternalResetSessionEx(true);
+ DEBUGPRINTFX(DBG_EXOTIC,("TSyncSession::TerminateSession: InternalResetSession called"));
+ // remove all local datastores
+ TLocalDataStorePContainer::iterator pos1;
+ int n=fLocalDataStores.size();
+ PDEBUGPRINTFX(DBG_EXOTIC,("Deleting %d datastores",n));
+ for (pos1=fLocalDataStores.begin(); pos1!=fLocalDataStores.end(); ++pos1) {
+ delete *pos1;
+ }
+ fLocalDataStores.clear(); // clear list
+ // remove all local itemtypes
+ TSyncItemTypePContainer::iterator pos2;
+ for (pos2=fLocalItemTypes.begin(); pos2!=fLocalItemTypes.end(); ++pos2) {
+ delete *pos2;
+ }
+ fLocalItemTypes.clear(); // clear list
+ #ifdef SYDEBUG
+ // save half-begun XML translations
+ XMLTranslationOutgoingEnd();
+ XMLTranslationIncomingEnd();
+ #endif
+ // stop and show profiling info
+ TP_STOP(fTPInfo);
+ #ifdef TIME_PROFILING
+ if (getDbgMask() & DBG_PROFILE) {
+ sInt16 i;
+ uInt32 sy,us;
+ PDEBUGPRINTFX(DBG_PROFILE,("Session CPU usage statistics: (system/user/total)"));
+ // sections
+ for (i=0; i<numTPTypes; i++) {
+ sy=TP_GETSYSTEMMS(fTPInfo,(TTP_Types)i);
+ us=TP_GETUSERMS(fTPInfo,(TTP_Types)i);
+ PDEBUGPRINTFX(DBG_PROFILE,(
+ "- %-20s : %10ld /%10ld /%10ld ms",
+ TP_TypeNames[i],
+ sy,
+ us,
+ sy+us
+ ));
+ }
+ // total
+ sy=TP_GETTOTALSYSTEMMS(fTPInfo);
+ us=TP_GETTOTALUSERMS(fTPInfo);
+ PDEBUGPRINTFX(DBG_PROFILE,(
+ "- TOTAL : %10ld /%10ld /%10ld ms",
+ sy,
+ us,
+ sy+us
+ ));
+ // Real time
+ uInt32 rt=TP_GETREALTIME(fTPInfo);
+ PDEBUGPRINTFX(DBG_PROFILE,(
+ "- Real Time : %10ld ms",
+ rt
+ ));
+ // % CPU
+ PDEBUGPRINTFX(DBG_PROFILE,(
+ "- CPU load : %10ld promille",
+ rt ? (sy+us)*1000/rt : 0
+ ));
+ }
+ #endif
+ MP_SHOWCURRENT(DBG_PROFILE,"TSyncSession deleting");
+ // show ending (if not normal, then ending was already shown in AbortSession())
+ if (normalend) {
+ OBJ_PROGRESS_EVENT(getSyncAppBase(),pev_sessionend,NULL, allsuccess ? 0 : LOCERR_INCOMPLETE,0,0);
+ }
+ // is NOW terminated
+ fTerminated = true;
+ PDEBUGPRINTFX(DBG_EXOTIC,("Session is now terminated (but not yet deleted)"));
+ } // if not already terminated
+} // TSyncSession::TerminateSession
+
+
+// Virtual version
+void TSyncSession::ResetSession(void)
+{
+ // terminate sync of datastores
+ TerminateDatastores();
+ // reset internals
+ DEBUGPRINTFX(DBG_EXOTIC,("TSyncSession::ResetSession: calling InternalResetSession"));
+ InternalResetSessionEx(false);
+ DEBUGPRINTFX(DBG_EXOTIC,("TSyncSession::ResetSession: InternalResetSession called"));
+ // no ancestor to call
+} // TSyncSession::ResetSession
+
+
+/// @brief Announce destruction of descendant to all datastores which might have direct links to these descendants and must cancel those
+/// @note must be called by derived class' destructors to allow datastores to detach from agent BEFORE descendant destructor has run
+void TSyncSession::announceDestruction()
+{
+ // terminate sync with all datastores
+ TLocalDataStorePContainer::iterator pos;
+ for (pos=fLocalDataStores.begin(); pos!=fLocalDataStores.end(); ++pos) {
+ // now let datastores cancel eventual direct links to derived TSession
+ (*pos)->announceAgentDestruction();
+ }
+} // TSyncSession::announceDestruction
+
+
+// - terminate all datastores
+void TSyncSession::TerminateDatastores(localstatus aAbortStatusCode)
+{
+ // terminate sync with all datastores
+ TLocalDataStorePContainer::iterator pos;
+ for (pos=fLocalDataStores.begin(); pos!=fLocalDataStores.end(); ++pos) {
+ // terminate
+ (*pos)->engTerminateDatastore(aAbortStatusCode);
+ }
+} // TSyncSession::TerminateDatastores
+
+
+// - resets session and removes all datastores (local and remote)
+void TSyncSession::ResetAndRemoveDatastores(void)
+{
+ // Must reset session before
+ ResetSession();
+ // remove all local datastores
+ TLocalDataStorePContainer::iterator pos;
+ for (pos=fLocalDataStores.begin(); pos!=fLocalDataStores.end(); ++pos) {
+ // now actually delete them
+ TLocalEngineDS *dsP = (*pos);
+ if (dsP) {
+ (*pos) = NULL; // to avoid double deletes
+ delete dsP;
+ }
+ }
+ fLocalDataStores.clear(); // remove pointers
+} // TSyncSession::ResetAndRemoveDatastores
+
+
+
+// reset session to inital state (new)
+// - this is called at creation and destruction but can be called
+// also when an existing session needs to be restarted.
+// - InternalResetSession() must be proof for being called more than
+// once in a row.
+// - InternalResetSession() must also be callable from destructor
+// (care not to call other objects which will refer to the already
+// half-destructed session!)
+void TSyncSession::InternalResetSessionEx(bool terminationCall)
+{
+ #ifdef SCRIPT_SUPPORT
+ // call session termination script (ONCE!)
+ if (terminationCall && !fTerminated) {
+ TScriptContext::execute(
+ fSessionScriptContextP,
+ getSessionConfig()->fSessionFinishScript,
+ NULL, // context's function table
+ NULL // datastore pointer needed for context
+ );
+ }
+ #endif
+
+ // reset sync and datastores
+ // - version not known in advance
+ fSyncMLVersion=syncml_vers_unknown;
+ // - immediately abort SYNC command in progress
+ fLocalSyncDatastoreP = NULL;
+ #ifndef NO_REMOTE_RULES
+ // - no remote rule applied
+ fAppliedRemoteRuleP = NULL;
+ #endif
+ // - set defaults for >=SyncML 1.1 features
+ fRemoteWantsNOC = false; // no, unless requested
+ fRemoteCanHandleUTC = false; // assume remote can not handle UTC time (note that for SyncML 1.0 this will be set to true later)
+ fRemoteSupportsLargeObjects = false; // no large object support by default
+ // - default options
+ fTreatRemoteTimeAsLocal = false; // do not ignore time zone information from remote
+ fTreatRemoteTimeAsUTC = false; // do not ignore time zone information from remote
+ fIgnoreDevInfMaxSize = false; // do not ignore <maxsize> specification in CTCap
+ fIgnoreCTCap = false; // do not ignore CTCap
+ fDSPathInDevInf = true; // newer Nokias need this, as they expect the same path in devInf as they sent in alert
+ fDSCgiInDevInf = true; // newer Nokias need this, as they expect the same path AND CGI in devInf as they sent in alert
+ fReadOnly = false; // always disabled unless set by SessionLogin()
+ // - init user time zone from setting. May be modified later using SETUSERTIMEZONE()
+ fUserTimeContext = getSessionConfig()->fUserTimeContext;
+ #ifdef SYDEBUG
+ fSessionLogger.setEnabled(getRootConfig()->fDebugConfig.fSessionDebugLogs); // get default value
+ #endif
+ #ifndef MINIMAL_CODE
+ fLogEnabled = getSessionConfig()->fLogEnabled;
+ #endif
+ #ifdef SCRIPT_SUPPORT
+ // delete the script context if any
+ if (fSessionScriptContextP) {
+ delete fSessionScriptContextP;
+ fSessionScriptContextP=NULL;
+ }
+ if (!terminationCall && !fTerminated) {
+ // prepare session-level scripts
+ TScriptContext::rebuildContext(getSyncAppBase(),getSessionConfig()->fSessionInitScript,fSessionScriptContextP,this);
+ TScriptContext::rebuildContext(getSyncAppBase(),getSessionConfig()->fSentItemStatusScript,fSessionScriptContextP,this);
+ TScriptContext::rebuildContext(getSyncAppBase(),getSessionConfig()->fReceivedItemStatusScript,fSessionScriptContextP,this);
+ TScriptContext::rebuildContext(getSyncAppBase(),getSessionConfig()->fSessionFinishScript,fSessionScriptContextP,this);
+ TScriptContext::rebuildContext(getSyncAppBase(),getSessionConfig()->fCustomGetHandlerScript,fSessionScriptContextP,this);
+ TScriptContext::rebuildContext(getSyncAppBase(),getSessionConfig()->fCustomGetPutScript,fSessionScriptContextP,this);
+ TScriptContext::rebuildContext(getSyncAppBase(),getSessionConfig()->fCustomEndPutScript,fSessionScriptContextP,this);
+ TScriptContext::rebuildContext(getSyncAppBase(),getSessionConfig()->fCustomPutResultHandlerScript,fSessionScriptContextP,this,true); // now instantiate vars
+ }
+ #endif
+ /* %%% moved to ResetSession() because this might not be called from
+ destructor as datastores might refer to derived session which is
+ already destructed then
+ // - Note: datastores may not be cleared here, only reset
+ TerminateDatastores();
+ */
+ // - remove all remote datastores
+ TRemoteDataStorePContainer::iterator pos1;
+ for (pos1=fRemoteDataStores.begin(); pos1!=fRemoteDataStores.end(); ++pos1) {
+ delete *pos1;
+ }
+ fRemoteDataStores.clear(); // clear list
+ // - remove all remote itemtypes
+ TSyncItemTypePContainer::iterator pos2;
+ for (pos2=fRemoteItemTypes.begin(); pos2!=fRemoteItemTypes.end(); ++pos2) {
+ delete *pos2;
+ }
+ fRemoteItemTypes.clear(); // clear list
+ // reset basics
+ SYSYNC_TRY {
+ // empty command queues
+ TSmlCommandPContainer::iterator pos;
+ // - commands waiting for status
+ for (pos=fStatusWaitCommands.begin(); pos!=fStatusWaitCommands.end(); ++pos) {
+ #ifdef SYDEBUG
+ TSmlCommand *cmdP = *pos;
+ // show that command was not answered
+ PDEBUGPRINTFX(DBG_PROTO,("Never received status for &html;<a name=\"SO_%ld_%ld\" href=\"#IO_%ld_%ld\">&html;command '%s'&html;</a>&html;, (outgoing MsgID=%ld, CmdID=%ld)",
+ (long)cmdP->getMsgID(),
+ (long)cmdP->getCmdID(),
+ (long)cmdP->getMsgID(),
+ (long)cmdP->getCmdID(),
+ cmdP->getName(),
+ (long)cmdP->getMsgID(),
+ (long)cmdP->getCmdID()
+ ));
+ #endif
+ // delete, if this is not the interrupted command.
+ // Note: only the interrupted command can also be in the status queue.
+ // Other queues are exclusive owners of their commands.
+ if (*pos != fInterruptedCommandP)
+ delete *pos;
+ else
+ DEBUGPRINTF(("- prevented deleting because command is interrupted"));
+ }
+ fStatusWaitCommands.clear(); // clear list
+ // - commands waiting for outgoing message to begin
+ forgetHeaderWaitCommands();
+ // - commands to be issued only after all commands in this message have
+ // been processed and answered by a status
+ for (pos=fEndOfMessageCommands.begin(); pos!=fEndOfMessageCommands.end(); ++pos) {
+ // show that command was not sent
+ DEBUGPRINTF(("Never sent end-of-message command '%s', (outgoing MsgID=%ld, CmdID=%ld)",
+ (*pos)->getName(),
+ (long)(*pos)->getMsgID(),
+ (long)(*pos)->getCmdID()
+ ));
+ // delete
+ delete *pos;
+ }
+ fEndOfMessageCommands.clear(); // clear list
+ // - commands to be sent in next message
+ for (pos=fNextMessageCommands.begin(); pos!=fNextMessageCommands.end(); ++pos) {
+ // show that command was not sent
+ DEBUGPRINTF(("Never sent next-message command '%s', (outgoing MsgID=%ld, CmdID=%ld)",
+ (*pos)->getName(),
+ (long)(*pos)->getMsgID(),
+ (long)(*pos)->getCmdID()
+ ));
+ // delete
+ delete *pos;
+ }
+ fNextMessageCommands.clear(); // clear list
+ // - commands to be sent in next package
+ for (pos=fNextPackageCommands.begin(); pos!=fNextPackageCommands.end(); ++pos) {
+ // show that command was not sent
+ DEBUGPRINTF(("Never sent next-package command '%s', (outgoing MsgID=%ld, CmdID=%ld)",
+ (*pos)->getName(),
+ (long)(*pos)->getMsgID(),
+ (long)(*pos)->getCmdID()
+ ));
+ // delete
+ delete *pos;
+ }
+ fNextPackageCommands.clear(); // clear list
+ // - interrupted command
+ if (fInterruptedCommandP) {
+ // show that command was not sent
+ DEBUGPRINTF(("Never finished interrupted command '%s', (outgoing MsgID=%ld, CmdID=%ld)",
+ fInterruptedCommandP->getName(),
+ (long)fInterruptedCommandP->getMsgID(),
+ (long)fInterruptedCommandP->getCmdID()
+ ));
+ delete fInterruptedCommandP;
+ fInterruptedCommandP=NULL;
+ }
+ // - commands to be executed again at beginning of next message
+ fDelayedExecSyncEnds=0;
+ for (pos=fDelayedExecutionCommands.begin(); pos!=fDelayedExecutionCommands.end(); ++pos) {
+ // show that command was not sent
+ DEBUGPRINTF(("Never finished executing command '%s', (incoming MsgID=%ld, CmdID=%ld)",
+ (*pos)->getName(),
+ (long)(*pos)->getMsgID(),
+ (long)(*pos)->getCmdID()
+ ));
+ // delete
+ delete *pos;
+ }
+ fDelayedExecutionCommands.clear(); // clear list
+ #ifdef SYNCSTATUS_AT_SYNC_CLOSE
+ // make sure sync status is disposed
+ if (fSyncCloseStatusCommandP) delete fSyncCloseStatusCommandP;
+ fSyncCloseStatusCommandP=NULL;
+ #endif
+ // remove incomplete data command
+ if (fIncompleteDataCommandP) delete fIncompleteDataCommandP;
+ fIncompleteDataCommandP=NULL;
+ }
+ SYSYNC_CATCH (exception &e)
+ #ifdef SYDEBUG
+ DEBUGPRINTFX(DBG_ERROR,(
+ "WARNING: Exception during InternalResetSession(): %s",
+ e.what()
+ ));
+ #endif
+ SYSYNC_ENDCATCH
+ SYSYNC_CATCH (...)
+ #ifdef SYDEBUG
+ DEBUGPRINTFX(DBG_ERROR,(
+ "WARNING: Unknown Exception during InternalResetSession()"
+ ));
+ #endif
+ SYSYNC_ENDCATCH
+ // remember time of creation or last reset
+ SessionUsed();
+ // reset session status
+ fIncomingState=psta_idle; // no incoming package status yet
+ fCmdIncomingState=psta_idle;
+ fOutgoingState=psta_idle; // no outgoing package status yet
+ fNextMessageRequests=0; // no pending next message requests
+ fFakeFinalFlag=false; // special flag to work around broken resume implementations
+ fNewOutgoingPackage=true; // first message will be first in outgoing package
+ fSessionAuthorized=false; // session not permanently authorized
+ fMessageAuthorized=false; // message not authorized either
+ fAuthFailures=0; // no failed attempts by remote so far
+ fAuthRetries=0; // no failed attempts by myself so far
+ fIncomingMsgID=0; // expected session to start with MsgID=1, so must be 0 now as it will be incremented at StartMessage()
+ fOutgoingMsgID=0; // starting answers with MsgID=1, so must be 0 as it will be incremented before sending a new message
+ fAborted=false; // not yet aborted
+ fSuspended=false; // not being suspended yet
+ fFailedDatastores=0; // none failed
+ fErrorItemDatastores=0; // none generated or detected error items
+ fInProgress=false; // not yet in progress
+ fOutgoingStarted=false; // no outgoing message started yet
+ fSequenceNesting=0; // no sequence command open
+ fMaxOutgoingMsgSize=0; // no limit for outgoing messages so far
+ fMaxOutgoingObjSize=0; // SyncML 1.1: no limit for outgoing objects so far
+ fOutgoingMessageFull=false; // limit not yet reached
+ // init special remote-dependent behaviour
+ fLimitedRemoteFieldLengths=false; // assume remote has not generally limited field lenghts
+ fDontSendEmptyProperties=false; // normally, empty properties will be sent
+ fDoQuote8BitContent=false;
+ fNoReplaceInSlowsync=false;
+ fTreatRemoteTimeAsLocal=false;
+ fTreatRemoteTimeAsUTC=false;
+ fIgnoreDevInfMaxSize=false;
+ fTreatCopyAsAdd=false;
+ fStrictExecOrdering=true; // SyncML standard requires strict ordering (of statuses, but this implies execution of commands, too)
+ fDefaultOutCharset=chs_utf8; // SyncML content is usually UTF-8
+ // defaults for possibly remote-dependent behaviour
+ fCompleteFromClientOnly=getSessionConfig()->fCompleteFromClientOnly; // conform to standard by default
+ fRequestMaxTime=getSessionConfig()->fRequestMaxTime;
+ fRequestMinTime=getSessionConfig()->fRequestMinTime;
+ fUpdateClientDuringSlowsync=getSessionConfig()->fUpdateClientDuringSlowsync;
+ fUpdateServerDuringSlowsync=getSessionConfig()->fUpdateServerDuringSlowsync;
+ fAllowMessageRetries=getSessionConfig()->fAllowMessageRetries;
+ fShowCTCapProps=getSessionConfig()->fShowCTCapProps;
+ fShowTypeSzInCTCap10=getSessionConfig()->fShowTypeSzInCTCap10;
+ fVCal10EnddatesSameDay=getSessionConfig()->fVCal10EnddatesSameDay;
+ fDoNotFoldContent=getSessionConfig()->fDoNotFoldContent;
+ // tristates!!
+ fEnumDefaultPropParams=getSessionConfig()->fEnumDefaultPropParams;
+ #ifdef SCRIPT_SUPPORT
+ // - rule script is empty at start of session
+ fRuleScript.erase();
+ // call session init script
+ if (!terminationCall && !fTerminated) {
+ TScriptContext::execute(
+ fSessionScriptContextP,
+ getSessionConfig()->fSessionInitScript,
+ NULL, // context's function table
+ NULL // datastore pointer needed for context
+ );
+ }
+ #endif
+} // TSyncSession::InternalResetSessionEx
+
+
+
+// get root config pointer
+// NOTE: we have moved this here because Palm linker
+// would have problems accessing it as syncsession.cpp is
+// large enough to have EndMessage >32k away from the
+// original position of this routine.
+TRootConfig *TSyncSession::getRootConfig(void)
+{
+ return fSyncAppBaseP->getRootConfig();
+} // TSyncSession::getRootConfig
+
+
+// forget commands waiting to be sent when header is generated
+void TSyncSession::forgetHeaderWaitCommands(void)
+{
+ // empty command queues
+ TSmlCommandPContainer::iterator pos;
+ // - commands waiting for outgoing message to begin
+ for (pos=fHeaderWaitCommands.begin(); pos!=fHeaderWaitCommands.end(); ++pos) {
+ #if SYDEBUG>1
+ TSmlCommand *cmdP = *pos;
+ // show that command was not answered
+ DEBUGPRINTF(("Never sent command '%s', (outgoing MsgID=%ld, CmdID=%ld) because outgoing message never started",
+ cmdP->getName(),
+ (long)cmdP->getMsgID(),
+ (long)cmdP->getCmdID()
+ ));
+ #endif
+ // delete
+ delete *pos;
+ }
+ fHeaderWaitCommands.clear(); // clear list
+} // TSyncSession::forgetHeaderWaitCommands
+
+
+// abort processing commands in this message
+void TSyncSession::AbortCommandProcessing(TSyError aStatusCode)
+{
+ if (!fIgnoreIncomingCommands) {
+ fIgnoreIncomingCommands=true; // do not process further commands
+ fStatusCodeForIgnored=aStatusCode; // save status code
+ PDEBUGPRINTFX(DBG_HOT,(
+ "--------------- Ignoring all commands in this message (after %ld sec. request processing, %ld sec. total) with Status %hd (0=none) from here on",
+ (long)((getSystemNowAs(TCTX_UTC)-getLastRequestStarted()) / (lineartime_t)secondToLinearTimeFactor),
+ (long)((getSystemNowAs(TCTX_UTC)-getSessionStarted()) / (lineartime_t)secondToLinearTimeFactor),
+ fStatusCodeForIgnored
+ ));
+ }
+} // TSyncSession::AbortCommandProcessing
+
+
+// suspend session (that is: flag abortion)
+void TSyncSession::SuspendSession(TSyError aReason)
+{
+ // first check for suspend
+ if (fSyncMLVersion>=syncml_vers_1_2) {
+ // try suspend, only possible if not already suspended or aborted
+ if (!fSuspended && !fAborted) {
+ fSuspended=true; // trigger suspend
+ fAbortReasonStatus = aReason;
+ fLocalAbortReason = true;
+ AbortCommandProcessing(514); // abort command processing, all subsequent commands will be ignored with "cancelled" status
+ PDEBUGPRINTFX(DBG_ERROR,(
+ "WARNING: Session locally flagged for suspend, reason=%hd",
+ aReason
+ ));
+ CONSOLEPRINTF((
+ "Session will be suspended due to local error code %hd\n",
+ aReason
+ ));
+ }
+ }
+ else {
+ // We can't suspend, we need to abort
+ AbortSession(500,true,aReason);
+ }
+} // TSyncSession::SuspendSession
+
+
+// abort session (that is: flag abortion)
+void TSyncSession::AbortSession(TSyError aStatusCode, bool aLocalProblem, TSyError aReason)
+{
+ // make sure session gets aborted
+ // BUT: do NOT reset yet. Reset would incorrectly abort message answering
+ if (!fAborted && !fTerminated) {
+ fAborted=true; // session aborted
+ fAbortReasonStatus = aReason ? aReason : aStatusCode;
+ fLocalAbortReason = aLocalProblem;
+ fInProgress=false; // not in progress any more (will be deleted after end of request)
+ PDEBUGBLOCKFMT(("SessionAbort","Aborting Session",
+ "Status=%hd|ProblemSource=%s",
+ fAbortReasonStatus,
+ fLocalAbortReason ? "LOCAL" : "REMOTE"
+ ));
+ PDEBUGPRINTFX(DBG_ERROR,(
+ "WARNING: Aborting Session with Reason Status %hd (%s problem) ***",
+ fAbortReasonStatus,
+ fLocalAbortReason ? "LOCAL" : "REMOTE"
+ ));
+ // In SyncML 1.1, we have a special status code to show that commands are not
+ // executed any more due to cancellation of the session
+ if (fSyncMLVersion>=syncml_vers_1_1)
+ aStatusCode=514; // cancelled, command not completed
+ AbortCommandProcessing(aStatusCode);
+ // let all local datastores know
+ TLocalDataStorePContainer::iterator pos;
+ for (pos=fLocalDataStores.begin(); pos!=fLocalDataStores.end(); ++pos) {
+ (*pos)->engAbortDataStoreSync(fAbortReasonStatus, fLocalAbortReason);
+ }
+ CONSOLEPRINTF((
+ "Session aborted because of %s SyncML error code %hd\n",
+ fLocalAbortReason ? "LOCAL" : "REMOTE",
+ fAbortReasonStatus
+ ));
+ OBJ_PROGRESS_EVENT(
+ getSyncAppBase(),
+ pev_sessionend,
+ NULL,
+ getAbortReasonStatus(),
+ 0,0
+ );
+ PDEBUGENDBLOCK("SessionAbort");
+ }
+} // TSyncSession::AbortSession
+
+
+// returns true if session was a complete success
+bool TSyncSession::isAllSuccess(void)
+{
+ return fFailedDatastores==0 && fErrorItemDatastores==0;
+} // TSyncSession::isAllSuccess
+
+
+// let session know that datastore has failed
+void TSyncSession::DatastoreFailed(TSyError aStatusCode, bool aLocalProblem)
+{
+ fFailedDatastores++;
+ // %%% note that this is not perfect for server, as inactive datastores are possible
+ if (fFailedDatastores>=fLocalDataStores.size()) {
+ // all have failed by now, abort the session
+ AbortSession(aStatusCode, aLocalProblem, aStatusCode);
+ }
+} // TSyncSession::DatastoreFailed
+
+
+// set SyncML toolkit workspace ID
+void TSyncSession::setSmlWorkspaceID(InstanceID_t aSmlWorkspaceID)
+{
+ fSmlWorkspaceID=aSmlWorkspaceID;
+} // TSyncSession::setSmlWorkspaceID
+
+
+// show some information about the config
+void TSyncSession::DebugShowCfgInfo(void)
+{
+ #ifdef SYDEBUG
+ string t;
+ StringObjTimestamp(t,getRootConfig()->fConfigDate);
+ // now write settings to log
+ PDEBUGPRINTFX(DBG_HOT,(
+ "==== Config file='%s', Last Change=%s",
+ getSyncAppBase()->fConfigFilePath.c_str(),
+ t.c_str()
+ ));
+ #ifndef HARDCODED_CONFIG
+ PDEBUGPRINTFX(DBG_HOT,(
+ "==== Config ID string='%s'",
+ getRootConfig()->fConfigIDString.c_str()
+ ));
+ #endif
+ #endif
+} // TSyncSession::DebugShowCfgInfo
+
+
+#ifndef MINIMAL_CODE
+
+#include <errno.h>
+
+// write to sync log file
+void TSyncSession::WriteLogLine(const char *aLogline)
+{
+ if (getSessionConfig()->fLogFileName.empty()) return; // do not write without a path
+ // open for append
+ FILE * logfile=fopen(getSessionConfig()->fLogFileName.c_str(),"a");
+ if (!logfile) {
+ PDEBUGPRINTFX(DBG_ERROR,("**** Cannot write to logfile '%s' (errno=%ld)",getSessionConfig()->fLogFileName.c_str(),(long)errno));
+ return; // cannot write
+ }
+ // check if we need to write labels first
+ if (!getSessionConfig()->fLogFileLabels.empty()) {
+ // check file size
+ if (ftell(logfile)==0) {
+ // we are at the beginning, print labels first
+ fputs(getSessionConfig()->fLogFileLabels.c_str(),logfile);
+ }
+ }
+ // now write log line
+ fputs(aLogline,logfile);
+ // close file
+ fclose(logfile);
+} // TSyncSession::WriteLogLine
+
+#endif
+
+
+
+// queue a SyncBody context command for issuing after incoming message has ended
+void TSyncSession::queueForIssueRoot(
+ TSmlCommand * &aSyncCommandP // the command
+)
+{
+ fEndOfMessageCommands.push_back(aSyncCommandP);
+ aSyncCommandP=NULL;
+} // TSyncSession::queueForIssueRoot
+
+
+// queue a SyncBody context command for issuing after incoming message has ended
+void TSyncSession::issueNotBeforePackage(
+ TPackageStates aPackageState,
+ TSmlCommand *aSyncCommandP // the command
+)
+{
+ if (fOutgoingState>=aPackageState) {
+ issueRootPtr(aSyncCommandP);
+ }
+ else {
+ PDEBUGPRINTFX(DBG_SESSION,("%s queued for next package",aSyncCommandP->getName()));
+ fNextPackageCommands.push_back(aSyncCommandP);
+ }
+} // TSyncSession::issueNotBeforePackage
+
+
+// issue a command in SyncBody context (uses session's interruptedCommand/NextMessageCommands)
+bool TSyncSession::issueRootPtr(
+ TSmlCommand *aSyncCommandP, // the command
+ bool aNoResp, // set if no response is wanted
+ bool aIsOKSyncHdrStatus // set if this is sync hdr status
+)
+{
+ // now issue
+ return issuePtr(aSyncCommandP,fNextMessageCommands,fInterruptedCommandP,aNoResp,aIsOKSyncHdrStatus);
+} // TSyncSession::issueRootPtr
+
+
+// issue object passed as pointer (rather than pointer reference)
+// normally used internally only
+bool TSyncSession::issuePtr(
+ TSmlCommand *aSyncCommandP, // the command
+ TSmlCommandPContainer &aNextMessageCommands, // the list to add the command if cannot be issued in this message
+ TSmlCommand * &aInterruptedCommandP, // where to store command ptr if it was interrupted
+ bool aNoResp, // set if no response is wanted
+ bool aIsOKSyncHdrStatus // set if this is sync hdr status
+)
+{
+ bool issued=true; // NULL issue is successful issue
+
+ if (aSyncCommandP) {
+ PDEBUGBLOCKFMT(("issue","issuing command",
+ "Cmd=%s",
+ aSyncCommandP->getName()
+ ));
+ SYSYNC_TRY {
+ // check for not-to-be-sent commands
+ if (aSyncCommandP->getDontSend()) {
+ // command must not be sent, just silently discarded
+ DEBUGPRINTFX(DBG_PROTO,("%s: not sent because fDontSend is set -> just delete",
+ aSyncCommandP->getName()
+ ));
+ delete aSyncCommandP;
+ issued=true; // counts as issued
+ goto endissue;
+ }
+ // check if this commmand now triggers need to answer
+ if (!aIsOKSyncHdrStatus) {
+ fNeedToAnswer=true;
+ }
+ // check if outgoing message has already started. If not, queue command
+ // for sending when message has started (client case, when status for
+ // header must be evaluated before next header can be generated)
+ if (!fOutgoingStarted) {
+ fHeaderWaitCommands.push_back(aSyncCommandP);
+ PDEBUGPRINTFX(DBG_SESSION+DBG_EXOTIC,(
+ "Outgoing message header not yet generated, command '%s' queued",
+ aSyncCommandP->getName()
+ ));
+ issued=true; // act as if issued
+ goto endissue;
+ }
+ // check if this is a continuation of a suspended chunked item transfer
+ // if yes, substitute the full data we currently have in the item (from
+ // the database) with the buffered left-overs from the previous
+ // session.
+ aSyncCommandP->checkChunkContinuation();
+ // check if message size restrictions or local buffer size
+ // will prevent command from being sent now
+ TSmlCommand *splitCmdP = NULL;
+ if (!fOutgoingMessageFull) {
+ #ifndef USE_SML_EVALUATION
+ #error "This Implementation does not work any more without USE_SML_EVALUATION"
+ #endif
+ // check if enough room to send data
+ sInt32 freeaftersend=aSyncCommandP->evalIssue(
+ peekNextOutgoingCmdID(), // this will be the ID
+ getOutgoingMsgID(),
+ aNoResp
+ );
+ // - if room for new commands is smaller than expected message size
+ sInt32 maxfree=getSmlWorkspaceFreeBytes();
+ sInt32 sizetoend=maxfree-freeaftersend;
+ #ifdef SYDEBUG
+ PDEBUGPRINTFX(DBG_SESSION+DBG_EXOTIC,(
+ "Command '%s': is %hd-th counted cmd, cmdsize(+tags needed to end msg)=%ld, available=%ld (maxfree=%ld, freeaftersend=%ld, notUsableBufferBytes()=%ld)",
+ aSyncCommandP->getName(),
+ fOutgoingCmds,
+ (long)sizetoend, // size of command (+tags needed to end msg)
+ (long)(maxfree-getNotUsableBufferBytes()),
+ (long)maxfree,
+ (long)freeaftersend,
+ (long)getNotUsableBufferBytes()
+ ));
+ #endif
+ #ifndef MINIMAL_CODE
+ // - check for artifical debug chunking
+ if (getSessionConfig()->fDebugChunkMaxSize && aSyncCommandP->isSyncOp()) {
+ // always chunk commands over the configured size
+ uInt32 cmdSize=getSmlWorkspaceFreeBytes()-freeaftersend;
+ if (cmdSize>getSessionConfig()->fDebugChunkMaxSize) {
+ // simulate a different free after send to force chunking
+ sInt32 nfas=getNotUsableBufferBytes()-(cmdSize-getSessionConfig()->fDebugChunkMaxSize)+100;
+ if (nfas<freeaftersend) {
+ freeaftersend=nfas;
+ PDEBUGPRINTFX(DBG_ERROR,("Attention: Debug Chunking enabled, freeaftersend adjusted to %ld",(long)freeaftersend));
+ }
+ }
+ }
+ #endif
+ // - check if we can send this
+ if (freeaftersend<=getNotUsableBufferBytes()) {
+ // not enough space in this message
+ PDEBUGPRINTFX(DBG_PROTO,(
+ "command is %ld bytes too big to be sent as-is in this message",
+ (long)(getNotUsableBufferBytes()-freeaftersend)
+ ));
+ bool sendable=false;
+ // - first choice: try to split (available in SyncML 1.1 and later)
+ if (getSyncMLVersion()>=syncml_vers_1_1) {
+ // we can use moredata mechanism, try if it works for this command
+ if (fOutgoingCmds>0 && sizetoend < getMaxOutgoingSize()/4) {
+ // this is a small command, and not in best position
+ // - command will most likely fit into next message, so end this message now
+ fOutgoingMessageFull=true;
+ sendable=true; // kind of "sendable" - as are confident it will be sendable in next message
+ PDEBUGPRINTFX(DBG_PROTO,("command is less than 1/4 of maxmsgsize -> will likely fit into next message, so queue it"));
+ }
+ else {
+ // - try to split
+ // reserve about 200 bytes for Meta Size
+ splitCmdP = aSyncCommandP->splitCommand(getNotUsableBufferBytes()-freeaftersend+200);
+ if (splitCmdP) {
+ sendable=true;
+ }
+ else if (aSyncCommandP->canSplit()) {
+ // command is basically splittable, but not right now - end message now and try in next
+ fOutgoingMessageFull=true;
+ sendable=true; // kind of "sendable" - just not now, but certainly later
+ }
+ }
+ }
+ // - second choice: try to shrink (mainly devInf)
+ if (!sendable) {
+ // gets sendable if we can shrink it to given size
+ sendable = aSyncCommandP->shrinkCommand(getNotUsableBufferBytes()-freeaftersend);
+ PDEBUGPRINTFX(DBG_PROTO+DBG_HOT,(
+ "shrinkCommand could %sshrink command by %ld bytes or more (which is needed to send it now)",
+ sendable ? "" : "NOT ",
+ (long)(getNotUsableBufferBytes()-freeaftersend)
+ ));
+ }
+ // - third choice: send it later as first command
+ if (!sendable) {
+ if (fOutgoingCmds>0 && sizetoend<getMaxOutgoingSize()+300) {
+ // not best position now, and not plain impossible (i.e. cmd<maxmsgsize+300)
+ PDEBUGPRINTFX(DBG_PROTO,("command can fit into one message -> queue and hope we'll be able to send it as only command in subsequent message"));
+ // - command will possibly fit into next message, so end this message now
+ fOutgoingMessageFull=true;
+ sendable=true; // kind of "sendable" - as we are hoping it will be sendable later
+ }
+ else {
+ // this WAS already the best possible position to send or really too big
+ // - we are in trouble, this command is unsendable
+ PDEBUGPRINTFX(DBG_ERROR,("%s: Warning: command is too big to be sent at all -> discarded/mark for resend",
+ aSyncCommandP->getName()
+ ));
+ // - try again in next session (data size might be smaller then, or remote might allow larger data at some time)
+ aSyncCommandP->markForResend();
+ // - just discard it for now
+ delete aSyncCommandP;
+ issued=false; // not issued (but also not queued again).
+ goto endissue;
+ }
+ }
+ // check if command was made sendable by splitting off a part
+ if (splitCmdP) {
+ // - queue command containing remaining data for issuing in next message
+ aNextMessageCommands.push_back(splitCmdP);
+ PDEBUGPRINTFX(DBG_PROTO+DBG_HOT,("Split command, sending a chunk, queued rest of data for sending in next message"));
+ // - Note: fOutgoingMessageFull is not set here (as first part of cmd must be sent first)
+ // but will be set below after issuing first part (due to splitCmdP!=NULL)
+ }
+ } // command too large to be sent now
+ }
+ // Queue if message is full, or if we have a interrupted command and
+ // command to be sent is something other than status. This will make sure
+ // incoming commands get their statuses before message is filled with other
+ // explicitly generated commands
+ if (fOutgoingMessageFull || (aInterruptedCommandP && aSyncCommandP->getCmdType()!=scmd_status)) {
+ // - queue for issuing in next message
+ aNextMessageCommands.push_back(aSyncCommandP);
+ PDEBUGPRINTFX(DBG_HOT,(
+ "No room for issueing in this message, command '%s' queued for next message",
+ aSyncCommandP->getName()
+ ));
+ // - could not be issued, was queued
+ issued=false;
+ }
+ else {
+ // issue the command
+ if (splitCmdP) fOutgoingMessageFull=true; // if we are sending a split command, message IS full after that!
+ bool dodelete=true;
+ if (aSyncCommandP->issue(getNextOutgoingCmdID(),fOutgoingMsgID,aNoResp)) {
+ // command expects status and must be kept in list
+ PDEBUGPRINTFX(DBG_HOT,("%s: issued as (outgoing MsgID=%ld, CmdID=%ld), now queueing for &html;<a name=\"IO_%ld_%ld\" href=\"#SO_%ld_%ld\">&html;status&html;</a>&html;",
+ aSyncCommandP->getName(),
+ (long)aSyncCommandP->getMsgID(),
+ (long)aSyncCommandP->getCmdID(),
+ (long)aSyncCommandP->getMsgID(),
+ (long)aSyncCommandP->getCmdID(),
+ (long)aSyncCommandP->getMsgID(),
+ (long)aSyncCommandP->getCmdID()
+ ));
+ // - queue for status
+ aSyncCommandP->setWaitingForStatus(true); // increment waiting for status count of this command
+ fStatusWaitCommands.push_back(aSyncCommandP);
+ dodelete=false;
+ }
+ else {
+ // command does not expect status and can be deleted if it is finished
+ PDEBUGPRINTFX(DBG_HOT,("%s: issued as (outgoing MsgID=%ld, CmdID=%ld), not waiting for status",
+ aSyncCommandP->getName(),
+ (long)aSyncCommandP->getMsgID(),
+ (long)aSyncCommandP->getCmdID()
+ ));
+ }
+ // Now as it is issued, count all "real" commands
+ if (!aIsOKSyncHdrStatus) {
+ // count outgoing "real" command, that is NOT SyncHdr OK status nor Alert 222 OK statu
+ // Note that issuing a container command such as <sync> will have pre-decremented fOutgoingCmds
+ // to compensate (container should not be counted)
+ fOutgoingCmds++;
+ }
+ // test if finished issuing
+ if (!aSyncCommandP->finished()) {
+ // issuing was interrupted, continue at start of next message
+ aInterruptedCommandP=aSyncCommandP; // remember
+ dodelete=false;
+ issued=false;
+ PDEBUGPRINTFX(DBG_SESSION,("%s: issue not finished -> queued interrupted command",
+ aSyncCommandP->getName()
+ ));
+ }
+ // - delete if not queued
+ if (dodelete) {
+ PDEBUGPRINTFX(DBG_SESSION,("%s: issue finished and not waiting for status -> deleting command",
+ aSyncCommandP->getName()
+ ));
+ delete aSyncCommandP;
+ }
+ // message size
+ PDEBUGPRINTFX(DBG_PROTO,("Outgoing Message size is now %ld bytes",(long)getOutgoingMessageSize()));
+ }
+ }
+ SYSYNC_CATCH (...)
+ // exception during command issuing
+ // - make sure command is deleted (as issue owns it now)
+ delete aSyncCommandP;
+ // make sure session gets aborted
+ AbortSession(500,true); // local problem
+ // close block
+ PDEBUGENDBLOCK("issue");
+ // re-throw
+ SYSYNC_RETHROW;
+ SYSYNC_ENDCATCH
+ endissue:
+ PDEBUGENDBLOCK("issue");
+ } // if something to issue at all
+ else {
+ DEBUGPRINTFX(DBG_SESSION,("issuePtr called with NULL command"));
+ }
+ // return true if completely issued, false if interrupted or not issued at all
+ return issued;
+} // TSyncSession::issuePtr
+
+
+// returns true if given number of bytes are transferable
+// (not exceeding MaxMsgSize (in SyncML 1.0) or MaxObjSize (SyncML 1.1 and later)
+bool TSyncSession::dataSizeTransferable(uInt32 aDataBytes)
+{
+ if (fSyncMLVersion<syncml_vers_1_1 || !fRemoteSupportsLargeObjects) {
+ // SyncML 1.0: Data is transferable only if it is not more
+ // than the room we had when the first syncop command in the message
+ // was being sent
+ return aDataBytes<=uInt32(fMaxRoomForData);
+ }
+ else {
+ // SyncML 1.1 and later: Data is transferable if it is not more than
+ // the MaxObjSize (if defined at all)
+ return aDataBytes<=uInt32(fMaxOutgoingObjSize) || fMaxOutgoingObjSize==0;
+ }
+} // TSyncSession::dataSizeTransferable
+
+
+
+#ifndef USE_SML_EVALUATION
+ #error "This implementation requires USE_SML_EVALUATION"
+#endif
+
+// get how many bytes may not be used in the outgoing message buffer
+// because of maxMsgSize restrictions
+sInt32 TSyncSession::getNotUsableBufferBytes(void)
+{
+ if (fMaxOutgoingMsgSize) {
+ // there is a limit
+ sInt32 leavefree =
+ getSmlWorkspaceFreeBytes() // what is free now
+ + fOutgoingMsgSize // + what is already used = totally available
+ - fMaxOutgoingMsgSize; // - max number to send = what we must leave free
+ // if available space is less than what we may send, there's no need
+ // to leave anything free.
+ return leavefree > 0 ? leavefree : 0;
+ }
+ else {
+ // limited only by workspace itself: no bytes need to be left free
+ return 0;
+ }
+} // TSyncSession::getNotUsableBufferBytes
+
+
+// get max size outgoing message may have (either defined by remote's maxmsgsize or local buffer space)
+sInt32 TSyncSession::getMaxOutgoingSize(void)
+{
+ return fMaxOutgoingMsgSize>0 ? fMaxOutgoingMsgSize : getSmlWorkspaceFreeBytes();
+} // TSyncSession::getMaxOutgoingSize
+
+
+/// @brief mark all pending items for a datastore for resume
+/// (those items that are in a session queue for being issued or getting status)
+void TSyncSession::markPendingForResume(TLocalEngineDS *aForDatastoreP)
+{
+ TSmlCommandPContainer::iterator pos;
+ // - commands not issued yet because header not yet generated
+ for (pos=fHeaderWaitCommands.begin(); pos!=fHeaderWaitCommands.end(); ++pos) {
+ (*pos)->markPendingForResume(aForDatastoreP,true); // unsent
+ }
+ // - commands not issued yet because they belong at the end of the message
+ for (pos=fEndOfMessageCommands.begin(); pos!=fEndOfMessageCommands.end(); ++pos) {
+ (*pos)->markPendingForResume(aForDatastoreP,true); // unsent
+ }
+ // - interrupted and next-message commands
+ markPendingForResume(fNextMessageCommands,fInterruptedCommandP,aForDatastoreP);
+ // - next package commands
+ for (pos=fNextPackageCommands.begin(); pos!=fNextPackageCommands.end(); ++pos) {
+ (*pos)->markPendingForResume(aForDatastoreP,true); // unsent
+ }
+ // - commands waiting for status
+ for (pos=fStatusWaitCommands.begin(); pos!=fStatusWaitCommands.end(); ++pos) {
+ (*pos)->markPendingForResume(aForDatastoreP,false); // these are already sent (except if they are waiting for chunk ok 213 status)!
+ }
+} // TSyncSession::markPendingForResume
+
+
+/// @brief mark all pending items for a datastore for resume
+/// (those items that are in a session queue for being issued or getting status)
+void TSyncSession::markPendingForResume(
+ TSmlCommandPContainer &aNextMessageCommands,
+ TSmlCommand *aInterruptedCommandP,
+ TLocalEngineDS *aForDatastoreP
+)
+{
+ // - all those that are in an interrupted command
+ if (aInterruptedCommandP) {
+ aInterruptedCommandP->markPendingForResume(aForDatastoreP,true); // these are unsent
+ }
+ // - all those pending for next message
+ TSmlCommandPContainer::iterator pos;
+ for (pos=aNextMessageCommands.begin(); pos!=aNextMessageCommands.end(); ++pos) {
+ (*pos)->markPendingForResume(aForDatastoreP,true); // these are unsent
+ }
+} // TSyncSession::markPendingForResume
+
+
+// continue interrupted or prevented issue of root level commands
+void TSyncSession::ContinuePackageRoot(void)
+{
+ // check if we have anything to send, and if so, reset the count
+ if (fNextMessageCommands.size()>0 || fInterruptedCommandP) {
+ #ifdef SYDEBUG
+ if (fNextMessageRequests) {
+ PDEBUGPRINTFX(DBG_PROTO,("Fulfilling %ld Next-Message-Request-Alerts 222 by sending commands now",(long)fNextMessageRequests));
+ }
+ #endif
+ fNextMessageRequests=0; // sent something, request fulfilled
+ }
+ ContinuePackage(fNextMessageCommands,fInterruptedCommandP);
+} // TSyncSession::ContinuePackageRoot
+
+
+// continue interrupted or prevented issue in next package
+void TSyncSession::ContinuePackage(
+ TSmlCommandPContainer &aNextMessageCommands,
+ TSmlCommand * &aInterruptedCommandP
+)
+{
+ TSmlCommand *cmdP = aInterruptedCommandP;
+ // - first restart interrupted command
+ if (cmdP && !isAborted() && !isSuspending()) {
+ // first check if interrupted command has received status
+ if (cmdP->isWaitingForStatus() && getSessionConfig()->fWaitForStatusOfInterrupted) {
+ // Command to be continued has not yet received status, so wait with continuing it
+ PDEBUGPRINTFX(DBG_SESSION,(
+ "Interrupted command '%s' (outgoing MsgID=%ld, CmdID=%ld) is still waiting for status -> do not continue nor send queued commands",
+ cmdP->getName(),
+ (long)cmdP->getMsgID(),
+ (long)cmdP->getCmdID()
+ ));
+ // aInterruptedCommandP is still set, prevents sending of queued commands
+ }
+ else {
+ PDEBUGPRINTFX(DBG_SESSION,("Sending command that was interrupted at end of last message"));
+ // there is an interrupted command, continue it
+ bool dodelete=true;
+ // if an interrupted command must be continued, this can't be an OK for SyncHdr
+ fNeedToAnswer=true;
+ bool newIssue=false;
+ // now continue issuing
+ bool queueForStatus=cmdP->continueIssue(newIssue);
+ // check if complete re-issuing is needed
+ if (newIssue) {
+ // command must be re-issued as if it was a new command
+ PDEBUGPRINTFX(DBG_SESSION,("%s: continuing command by issuing anew",cmdP->getName()));
+ // interruption is over for now, IssuePtr will set it again if interrupted again
+ aInterruptedCommandP=NULL;
+ // IssuePtr will take care of all needed status queueing and deleting command if finished etc.
+ issuePtr(cmdP,aNextMessageCommands,aInterruptedCommandP);
+ // never delete, as issuePtr will have done it if needed
+ dodelete=false;
+ }
+ else {
+ // no complete re-issuing needed, just check if we need to queue for status (again)
+ if (queueForStatus) {
+ // command expects status (again?) and must be kept in list
+ PDEBUGPRINTFX(DBG_SESSION,("%s: continued, now queueing for status (again) as (outgoing MsgID=%ld, CmdID=%ld)",
+ cmdP->getName(),
+ (long)cmdP->getMsgID(),
+ (long)cmdP->getCmdID()
+ ));
+ // - queue for status (note that every SyncML 1.1 chunk wants to see a status 213 in any case!)
+ if (!cmdP->isWaitingForStatus()) {
+ // only put to the queue again if not already waiting there
+ fStatusWaitCommands.push_back(cmdP);
+ cmdP->setWaitingForStatus(true);
+ }
+ else {
+ // already waiting, so it's already in the queue - do not push it again
+ PDEBUGPRINTFX(DBG_SESSION,("%s: continued command was already waiting for status, do not push again", cmdP->getName()));
+ }
+ dodelete=false;
+ }
+ }
+ // test if completely issued now
+ if (!cmdP->finished()) {
+ // issuing was again interrupted, continue at start of next message
+ // - keep aInterruptedCommandP pointer unchanged
+ dodelete=false;
+ PDEBUGPRINTFX(DBG_SESSION,("%s: continueIssue not finished -> queued interrupted command again",
+ cmdP->getName()
+ ));
+ }
+ else {
+ // no interrupted command any more
+ aInterruptedCommandP=NULL;
+ }
+ // delete if not queued
+ if (dodelete) {
+ PDEBUGPRINTFX(DBG_SESSION,("%s: continueIssue finished and not waiting for status -> deleting command",
+ cmdP->getName()
+ ));
+ delete cmdP;
+ }
+ } // if status was received for interrupted command
+ } // if interrupted command
+ // send commands from queue (until interrupted again)
+ #ifdef SYDEBUG
+ if (!isAborted() && !isSuspending() && !aInterruptedCommandP && aNextMessageCommands.size()>0) {
+ PDEBUGPRINTFX(DBG_SESSION,("Sending %ld commands that didn't make it into last message",(long)aNextMessageCommands.size()));
+ }
+ #endif
+ TSmlCommandPContainer::iterator pos;
+ while (!isAborted() && !isSuspending() && !aInterruptedCommandP && !outgoingMessageFull()) {
+ // first in list
+ pos=aNextMessageCommands.begin();
+ if (pos==aNextMessageCommands.end()) break; // done
+ // take command out of the list
+ TSmlCommand *cmdP=(*pos);
+ aNextMessageCommands.erase(pos);
+ // issue it (without luck, might land in the queue again --> %%% endless retry??)
+ if (!issuePtr(cmdP,aNextMessageCommands,aInterruptedCommandP)) break;
+ }
+} // TSyncSession::ContinuePackage
+
+
+
+// create, send and delete SyncHeader "command"
+void TSyncSession::issueHeader(bool aNoResp)
+{
+ #ifdef SYDEBUG
+ // Start output translation before issuing outgoing header
+ XMLTranslationOutgoingStart();
+ #endif
+ #if defined(SYDEBUG) && defined(SYSYNC_CLIENT)
+ // for client, document exchange starts with outgoing message
+ // but for server, SyncML_Outgoing is started before SyncML_Incoming, as SyncML_Incoming ends first
+ PDEBUGBLOCKDESC("SyncML_Outgoing","start of new outgoing message");
+ PDEBUGPRINTFX(DBG_HOT,("=================> Started new outgoing message"));
+ #endif
+ #if defined(EXPIRES_AFTER_DATE) && defined(SYSYNC_CLIENT)
+ // set 1/4 of the date here
+ fCopyOfScrambledNow=((getSyncAppBase()->fScrambledNow)<<2)+503; // scramble again a little
+ #endif
+ // create and send response header
+ TSyncHeader *syncheaderP;
+ MP_NEW(syncheaderP,DBG_OBJINST,"TSyncHeader",TSyncHeader(this,aNoResp));
+ PDEBUGBLOCKFMT(("SyncHdr","SyncHdr generation","SyncMLVers=%s|OutgoingMsgID=%ld",SyncMLVerDTDNames[fSyncMLVersion],(long)fOutgoingMsgID));
+ SYSYNC_TRY {
+ // Note: do not use session's issue(), as this is designed for real commands, not headers
+ if (syncheaderP->issue(0,fOutgoingMsgID)) {
+ // - queue for status
+ PDEBUGPRINTFX(DBG_PROTO,("SyncHdr: issued in MsgID=%ld, now queueing for status",(long)syncheaderP->getMsgID()));
+ syncheaderP->setWaitingForStatus(true);
+ fStatusWaitCommands.push_back(syncheaderP);
+ }
+ else {
+ PDEBUGPRINTFX(DBG_PROTO,("SyncHdr: issued in MsgID=%ld, now deleting",(long)syncheaderP->getMsgID()));
+ delete syncheaderP; // not used any more
+ }
+ DEBUGPRINTFX(DBG_PROTO,("Outgoing Message size is now %ld bytes",(long)getOutgoingMessageSize()));
+ // - init number of commands in message
+ fOutgoingCmds=0;
+ // - now issue all commands that could not yet be sent because outgoing
+ // message was not started
+ #ifdef SYDEBUG
+ if (fHeaderWaitCommands.size()>0) {
+ PDEBUGPRINTFX(DBG_SESSION,("Sending %ld commands that were queued because header was not yet generated",(long)fHeaderWaitCommands.size()));
+ }
+ #endif
+ TSmlCommandPContainer::iterator pos;
+ while (!fAborted) {
+ // first in list
+ pos=fHeaderWaitCommands.begin();
+ if (pos==fHeaderWaitCommands.end()) break; // done
+ // take command out of the list
+ TSmlCommand *cmdP=(*pos);
+ fHeaderWaitCommands.erase(pos);
+ if (!cmdP) {
+ DEBUGPRINTFX(DBG_ERROR,("There was a NULL command in the fHeaderWaitCommands queue?!?"));
+ continue;
+ }
+ // issue now (as if all were OK for synchdr, because fNeedToAnswer may not be affected here!)
+ if (!issuePtr(cmdP,fNextMessageCommands,fInterruptedCommandP,false,true)) {
+ PDEBUGPRINTFX(DBG_SESSION,("Could not issue queued command"));
+ break;
+ }
+ }
+ PDEBUGENDBLOCK("SyncHdr");
+ }
+ SYSYNC_CATCH (...)
+ PDEBUGENDBLOCK("SyncHdr");
+ delete syncheaderP; // not used any more
+ SYSYNC_RETHROW; // rethrow
+ SYSYNC_ENDCATCH
+} // TSyncSession::IssueHeader
+
+
+
+
+// process a command (analyze and execute it).
+// Ownership of command is passed to process() in all cases.
+// Note: This method does not throw exceptions (catches all) and
+// is suitable for being called without further precautions from
+// SyncML toolkit callbacks. Exceptions are translated to
+// smlXXX error codes.
+Ret_t TSyncSession::process(TSmlCommand *aSyncCommandP)
+{
+ if (aSyncCommandP) {
+ SYSYNC_TRY {
+ // first analyze it (before we can open the block, as we need to find cmdID fisrt)
+ if (!aSyncCommandP->analyze(fIncomingState)) {
+ // bad command
+ PDEBUGPRINTFX(DBG_ERROR,("%s: command failed analyze() -> aborting session",aSyncCommandP->getName()));
+ AbortSession(400,true); // local problem
+ delete aSyncCommandP;
+ }
+ else {
+ // command ok so far (has cmdid, so we can refer to it)
+ PDEBUGBLOCKFMT(("processCmd","Processing incoming command",
+ "Cmd=%s|IncomingMsgID=%ld|CmdID=%ld",
+ aSyncCommandP->getName(),
+ (long)aSyncCommandP->getMsgID(),
+ (long)aSyncCommandP->getCmdID()
+ ));
+ SYSYNC_TRY {
+ // - test if processing is enabled
+ if (fIgnoreIncomingCommands && !aSyncCommandP->neverIgnore()) {
+ // commands must be ignored (fatal error in previous command/header)
+ PDEBUGPRINTFX(DBG_ERROR,("%s: IGNORED ",aSyncCommandP->getName()));
+ // - still generate status (except if aborted status is 0, which means silent abort)
+ // but DO NOT generate status for status!
+ if (fStatusCodeForIgnored!=0 && aSyncCommandP->getCmdType()!=scmd_status) {
+ PDEBUGPRINTFX(DBG_ERROR,(" Sending status %hd for ignored command",fStatusCodeForIgnored));
+ TStatusCommand *aStatusCmdP = aSyncCommandP->newStatusCommand(fStatusCodeForIgnored);
+ issueRootPtr(aStatusCmdP);
+ }
+ // - delete unexecuted
+ delete aSyncCommandP;
+ }
+ else if (
+ fStrictExecOrdering &&
+ fDelayedExecutionCommands.size()>0 &&
+ aSyncCommandP->getCmdType()!=scmd_status &&
+ aSyncCommandP->getCmdType()!=scmd_alert
+ ) {
+ // some commands have been delayed already -> delay all non-statuses and alerts as well
+ PDEBUGPRINTFX(DBG_SESSION,("%s: command received after other commands needed to be delayed -> must be delayed, too",aSyncCommandP->getName()));
+ // - put into delayed execution queue
+ delayExecUntilNextRequest(aSyncCommandP);
+ }
+ else {
+ // command is ok, execute it
+ fCmdIncomingState=aSyncCommandP->getPackageState();
+ if (aSyncCommandP->execute()) {
+ // execution finished, can be deleted
+ if (aSyncCommandP->finished()) {
+ PDEBUGPRINTFX(DBG_SESSION,("%s: command finished execution -> deleting",aSyncCommandP->getName()));
+ delete aSyncCommandP;
+ }
+ else {
+ PDEBUGPRINTFX(DBG_SESSION,("%s: command NOT finished execution, NOT deleting now",aSyncCommandP->getName()));
+ }
+ }
+ else {
+ // command has not finished execution, must be retried after next incoming message
+ PDEBUGPRINTFX(DBG_SESSION,("%s: command wants re-execution later -> queueing",aSyncCommandP->getName()));
+ // - put into delayed execution queue
+ delayExecUntilNextRequest(aSyncCommandP);
+ }
+ } // if not ignored
+ // successfully processed
+ PDEBUGENDBLOCK("processCmd");
+ } // try
+ SYSYNC_CATCH (...)
+ PDEBUGENDBLOCK("processCmd");
+ SYSYNC_RETHROW;
+ SYSYNC_ENDCATCH
+ } // if analyzed successfully
+ }
+ SYSYNC_CATCH (TSmlException &e)
+ // Sml error exception somewhere in command processing
+ // - make sure command is deleted (as issue owns it now)
+ delete aSyncCommandP;
+ PDEBUGPRINTFX(DBG_ERROR,("WARNING: process(cmd) SmlException: %s, smlerr=%hd",e.what(),e.getSmlError()));
+ // - return SML error that caused this exception
+ return e.getSmlError();
+ SYSYNC_ENDCATCH
+ SYSYNC_CATCH (TSyncException &e)
+ // sync exception during command processing
+ // - make sure command is deleted (as issue owns it now)
+ delete aSyncCommandP;
+ PDEBUGPRINTFX(DBG_ERROR,("WARNING: process(cmd) SyncException: %s, status=%hd",e.what(),e.status()));
+ // - unspecific SyncML toolkit error, causes session to abort
+ return SML_ERR_UNSPECIFIC;
+ SYSYNC_ENDCATCH
+ SYSYNC_CATCH (exception &e)
+ // C++ exception during command processing
+ // - make sure command is deleted (as issue owns it now)
+ delete aSyncCommandP;
+ PDEBUGPRINTFX(DBG_ERROR,("WARNING: process(cmd) Exception: %s",e.what()));
+ // - unspecific SyncML toolkit error, causes session to abort
+ return SML_ERR_UNSPECIFIC;
+ SYSYNC_ENDCATCH
+ SYSYNC_CATCH (...)
+ // other exception during command processing
+ // - make sure command is deleted (as issue owns it now)
+ delete aSyncCommandP;
+ PDEBUGPRINTFX(DBG_ERROR,("WARNING: process(cmd) unknown exception: -> cmd deleted"));
+ // - unspecific SyncML toolkit error, causes session to abort
+ return SML_ERR_UNSPECIFIC;
+ SYSYNC_ENDCATCH
+ }
+ // successful
+ return SML_ERR_OK;
+} // TSyncSession::process
+
+
+// process synchdr (analyze and execute it).
+// Ownership of command is passed to process() in all cases.
+// Note: May cause session reset if message sequence numbers are not ok.
+Ret_t TSyncSession::processHeader(TSyncHeader *aSyncHdrP)
+{
+ if (aSyncHdrP) {
+ PDEBUGBLOCKDESC("processHdr","Processing incoming SyncHdr");
+ SYSYNC_TRY {
+ // init some session vars
+ // - do not ignore commands by default
+ fIgnoreIncomingCommands=false;
+ // - need to answer raises out of first non-synchdr-status command issue()d
+ fNeedToAnswer=false; // no need yet
+ // - initialize message status
+ fMsgNoResp=false; // default to response for messages
+ fIncomingMsgID++; // count this incoming message
+ // init flag
+ bool tryagain=false;
+ do {
+ // first analyze header
+ if (!aSyncHdrP->analyze(fIncomingState)) {
+ // bad command
+ PDEBUGPRINTFX(DBG_ERROR,("%s: failed analyze() -> deleting",aSyncHdrP->getName()));
+ delete aSyncHdrP;
+ }
+ else {
+ // command is ok, execute it
+ fCmdIncomingState=aSyncHdrP->getPackageState();
+ PDEBUGBLOCKFMT(("SyncHdr","Processing incoming SyncHdr",
+ "IncomingMsgID=%ld",
+ (long)fIncomingMsgID
+ ));
+ SYSYNC_TRY {
+ if (aSyncHdrP->execute()) {
+ // show session info after processing header
+ #ifdef SYDEBUG
+ PDEBUGPRINTFX(DBG_HOT,("Incoming SyncHdr processed, incomingMsgID=%ld, SyncMLVers=%s",(long)fIncomingMsgID,SyncMLVerDTDNames[fSyncMLVersion]));
+ PDEBUGPRINTFX(DBG_HOT,("- Session ID='%s'",fSynchdrSessionID.c_str()));
+ PDEBUGPRINTFX(DBG_HOT,("- Source (Remote party): URI='%s' DisplayName='%s'",fRemoteURI.c_str(),fRemoteName.c_str()));
+ PDEBUGPRINTFX(DBG_HOT,("- Response to be sent to URI='%s'",fRespondURI.empty() ? "[none specified, back to source]" : fRespondURI.c_str()));
+ PDEBUGPRINTFX(DBG_HOT,("- Target (Local party) : URI='%s' DisplayName='%s'",fLocalURI.c_str(),fLocalName.c_str()));
+ #endif
+ CONSOLEPRINTF(("> SyncML message #%ld received from '%s'",fIncomingMsgID,fRemoteURI.c_str()));
+ // - UTC support is implied for SyncML 1.0 (as most devices support it, and
+ // there is was no way to signal it in 1.0).
+ if (!fRemoteDevInfKnown && fSyncMLVersion==syncml_vers_1_0) fRemoteCanHandleUTC=true;
+ // execution finished, can be deleted
+ PDEBUGPRINTFX(DBG_SESSION,("%s: finished execution -> deleting",aSyncHdrP->getName()));
+ delete aSyncHdrP;
+ // now execute delayed commands (before executing new ones)
+ PDEBUGPRINTFX(DBG_SESSION,("New message: Executing %ld delayed commands",(long)fDelayedExecutionCommands.size()));
+ TSmlCommandPContainer::iterator pos=fDelayedExecutionCommands.begin();
+ bool syncEndAfterSyncPackageEnd=false;
+ while (pos!=fDelayedExecutionCommands.end()) {
+ // execute again
+ TSmlCommand *cmdP = (*pos);
+ // command ok so far (has cmdid, so we can refer to it)
+ PDEBUGBLOCKFMT(("executeDelayedCmd","Re-executing command from delayed queue",
+ "Cmd=%s|IncomingMsgID=%ld|CmdID=%ld",
+ cmdP->getName(),
+ (long)cmdP->getMsgID(),
+ (long)cmdP->getCmdID()
+ ));
+ SYSYNC_TRY {
+ fCmdIncomingState=cmdP->getPackageState();
+ if (cmdP->execute()) {
+ // check if this was a syncend which was now executed AFTER the end of the incoming sync package
+ if (cmdP->getCmdType()==scmd_syncend) {
+ fDelayedExecSyncEnds--; // count executed syncend
+ if (cmdP->getPackageState()!=fIncomingState)
+ syncEndAfterSyncPackageEnd=true; // remember that we had at least one
+ }
+ // execution finished, can be deleted
+ PDEBUGPRINTFX(DBG_SESSION,("%s: command finished execution -> deleting",cmdP->getName()));
+ delete cmdP;
+ // delete from queue and get next
+ pos=fDelayedExecutionCommands.erase(pos);
+ }
+ else {
+ // command has not finished execution, must be retried after next incoming message
+ PDEBUGPRINTFX(DBG_SESSION,("%s: command STILL NOT finished execution -> keep it (and all follwoing) in queue ",cmdP->getName()));
+ // keep this and all subsequent commands in the queue
+ PDEBUGENDBLOCK("executeDelayedCmd");
+ break;
+ }
+ PDEBUGENDBLOCK("executeDelayedCmd");
+ } // try
+ SYSYNC_CATCH (...)
+ PDEBUGENDBLOCK("executeDelayedCmd");
+ SYSYNC_RETHROW;
+ SYSYNC_ENDCATCH
+ }
+ // check if all delayed commands are executed now
+ if (fDelayedExecSyncEnds<=0 && syncEndAfterSyncPackageEnd) {
+ // there was at least one queued syncend executed AFTER end of incoming sync package
+ // This means that we must finalize the sync-from-remote phase for the datastores here
+ // (as it was suppressed when the incoming sync package had ended)
+ TLocalDataStorePContainer::iterator pos;
+ for (pos=fLocalDataStores.begin(); pos!=fLocalDataStores.end(); ++pos) {
+ (*pos)->engEndOfSyncFromRemote(true);
+ }
+ }
+ else {
+ PDEBUGPRINTFX(DBG_SESSION,("%ld delayed commands could not yet be executed and are left in the queue for next message",(long)fDelayedExecutionCommands.size()));
+ }
+ // now issue next package commands if any
+ if (fNewOutgoingPackage) {
+ PDEBUGPRINTFX(DBG_SESSION,("New package: Sending %ld commands that were generated earlier for this package",(long)fNextPackageCommands.size()));
+ TSmlCommandPContainer::iterator pos;
+ for (pos=fNextPackageCommands.begin(); pos!=fNextPackageCommands.end(); pos++) {
+ // issue it (might land in NextMessageCommands)
+ issueRootPtr((*pos));
+ }
+ // done sending next package commands
+ fNextPackageCommands.clear(); // clear list
+ }
+ // done, don't try again
+ break;
+ }
+ else {
+ // unexecutable SyncHdr
+ // - could be resent message
+ if (fMessageRetried) {
+ // simply abort processing, let transport handle this
+ PDEBUGENDBLOCK("processHdr");
+ return LOCERR_RETRYMSG; // signal retry has happened
+ }
+ // - let agent decide what to do (and whether to try again executing the command)
+ DEBUGPRINTFX(DBG_SESSION,("%s: Cannot be executed properly, trying to recover",aSyncHdrP->getName()));
+ tryagain = syncHdrFailure(tryagain);
+ } // synchdr not ok
+ } // try
+ SYSYNC_CATCH (...)
+ PDEBUGENDBLOCK("SyncHdr");
+ SYSYNC_RETHROW;
+ SYSYNC_ENDCATCH
+ }
+ } while(tryagain);
+ PDEBUGENDBLOCK("SyncHdr");
+ }
+ SYSYNC_CATCH (TSmlException &e)
+ // Sml error exception somewhere in command processing
+ // - make sure command is deleted (as issue owns it now)
+ delete aSyncHdrP;
+ PDEBUGPRINTFX(DBG_ERROR,("WARNING: synchdr SmlException: %s, smlerr=%hd",e.what(),e.getSmlError()));
+ PDEBUGENDBLOCK("processHdr");
+ // - return SML error that caused this exception
+ return e.getSmlError();
+ SYSYNC_ENDCATCH
+ SYSYNC_CATCH (exception &e)
+ // C++ exception somewhere in command processing
+ // - make sure command is deleted (as issue owns it now)
+ delete aSyncHdrP;
+ PDEBUGPRINTFX(DBG_ERROR,("WARNING: synchdr exception: %s",e.what()));
+ PDEBUGENDBLOCK("processHdr");
+ // - return SML error that caused this exception
+ return SML_ERR_UNSPECIFIC;
+ SYSYNC_ENDCATCH
+ SYSYNC_CATCH (...)
+ // other exception during command processing
+ // - make sure command is deleted (as issue owns it now)
+ delete aSyncHdrP;
+ PDEBUGPRINTFX(DBG_ERROR,("WARNING: synchdr unknown-class exception: -> deleted"));
+ PDEBUGENDBLOCK("processHdr");
+ // - unspecific SyncML toolkit error, causes session to abort
+ return SML_ERR_UNSPECIFIC;
+ SYSYNC_ENDCATCH
+ }
+ // successful
+ PDEBUGENDBLOCK("processHdr");
+ return SML_ERR_OK;
+} // TSyncSession::processHeader
+
+#ifdef __PALM_OS__
+#pragma segment session2
+#endif
+
+// %%% integrate Results command here, too (that, is, make a common
+// ancestor for both TStatusCommand and TResultsCommand which
+// is then handled here in common).
+// handle a status "command"
+// Ownership of status is passed to handleStatus() in all cases.
+// Note: This method does not throw exceptions (catches all) and
+// is suitable for being called without further precautions from
+// SyncML toolkit callbacks. Exceptions are translated to
+// smlXXX error codes.
+Ret_t TSyncSession::handleStatus(TStatusCommand *aStatusCommandP)
+{
+ if (aStatusCommandP) {
+ PDEBUGBLOCKDESC("processStatus","Processing incoming Status");
+ SYSYNC_TRY {
+ // analyze first
+ if (!aStatusCommandP->analyze(fIncomingState)) {
+ // bad command
+ PDEBUGPRINTFX(DBG_SESSION,("%s: status failed analyze() -> deleting",aStatusCommandP->getName()));
+ delete aStatusCommandP;
+ }
+ else {
+ bool found=false;
+ // status is ok, find matching command
+ TSmlCommandPContainer::iterator pos;
+ for (pos=fStatusWaitCommands.begin(); pos!=fStatusWaitCommands.end(); ++pos) {
+ if ((*pos)->matchStatus(aStatusCommandP)) {
+ PDEBUGPRINTFX(DBG_PROTO,("Found matching command '%s' for Status",(*pos)->getName()));
+ (*pos)->setWaitingForStatus(false); // has received status
+ found=true;
+ if (fIgnoreIncomingCommands) {
+ // ignore statuses, but remove waiting command from queue
+ if ((*pos)->finished()) delete (*pos); // unfinished are owned otherwise and must not be deleted
+ fStatusWaitCommands.erase(pos);
+ PDEBUGPRINTFX(DBG_SESSION,("Status ignored, command considered done -> deleted"));
+ }
+ else {
+ // normally process status
+ if ((*pos)->handleStatus(aStatusCommandP)) {
+ PDEBUGPRINTFX(DBG_SESSION,("Status: processed, removed command '%s' from status wait queue",(*pos)->getName()));
+ // done with command, remove from queue
+ if ((*pos)->finished()) {
+ // - if this is an interrupted command, make sure to remove pointer
+ if ((*pos)==fInterruptedCommandP) fInterruptedCommandP=NULL;
+ // - delete command itself
+ // NOTE; if not finished, command is owned otherwise and must
+ // persist
+ PDEBUGPRINTFX(DBG_SESSION,("Status: command '%s' has handled status and allows to be deleted",(*pos)->getName()));
+ delete (*pos);
+ }
+ else {
+ PDEBUGPRINTFX(DBG_SESSION,("Status: command '%s' has handled status, but not finished() -> NOT deleted",(*pos)->getName()));
+ }
+ // - anyway, remove from list
+ fStatusWaitCommands.erase(pos);
+ }
+ else {
+ // command not yet acknowledged, keep in queue
+ (*pos)->setWaitingForStatus(true); // is again waiting for a status
+ PDEBUGPRINTFX(DBG_SESSION,("(intermediate) Status processed, command kept in queue, not deleted"));
+ }
+ } // else normal processing
+ break; // exit for loop (iterator is not ok any more)
+ }
+ } // for
+ if (!found) {
+ // no matching command found
+ PDEBUGPRINTFX(DBG_ERROR,("No command found for status -> ignoring"));
+ }
+ // now delete status
+ delete aStatusCommandP;
+ } // if analyzed successfully
+ }
+ SYSYNC_CATCH (TSmlException &e)
+ // Sml error exception somewhere in command processing
+ // - make sure command is deleted (as issue owns it now)
+ delete aStatusCommandP;
+ PDEBUGPRINTFX(DBG_ERROR,("WARNING: handleStatus SmlException: %s, smlerr=%hd",e.what(),e.getSmlError()));
+ PDEBUGENDBLOCK("processStatus");
+ // - return SML error that caused this exception
+ return e.getSmlError();
+ SYSYNC_ENDCATCH
+ SYSYNC_CATCH (exception &e)
+ // Sml error exception somewhere in command processing
+ // - make sure command is deleted (as issue owns it now)
+ delete aStatusCommandP;
+ PDEBUGPRINTFX(DBG_ERROR,("WARNING: handleStatus exception: %s",e.what()));
+ PDEBUGENDBLOCK("processStatus");
+ // - unspecific SyncML toolkit error, causes session to abort
+ return SML_ERR_UNSPECIFIC;
+ SYSYNC_ENDCATCH
+ SYSYNC_CATCH (...)
+ // other exception during command processing
+ // - make sure command is deleted (as issue owns it now)
+ delete aStatusCommandP;
+ PDEBUGPRINTFX(DBG_ERROR,("WARNING: handleStatus unknown-class exception: -> deleted"));
+ PDEBUGENDBLOCK("processStatus");
+ // - unspecific SyncML toolkit error, causes session to abort
+ return SML_ERR_UNSPECIFIC;
+ SYSYNC_ENDCATCH
+ }
+ // successful
+ PDEBUGENDBLOCK("processStatus");
+ return SML_ERR_OK;
+} // TSyncSession::handleStatus
+
+
+// Session level meta
+SmlPcdataPtr_t TSyncSession::newHeaderMeta(void)
+{
+ SmlPcdataPtr_t metaP = NULL;
+
+ // create meta for initialisation message only
+ #ifdef SEND_MAXMSGSIZE_ON_INIT_ONLY
+ if (fOutgoingState<=psta_init)
+ #endif
+ {
+ metaP=newMeta();
+ SmlMetInfMetInfPtr_t metinfP = smlPCDataToMetInfP(metaP);
+ // - add max message size
+ metinfP->maxmsgsize=newPCDataLong(getRootConfig()->fLocalMaxMsgSize);
+ if (
+ (getRootConfig()->fLocalMaxObjSize>0) &&
+ (fSyncMLVersion>=syncml_vers_1_1)
+ ) {
+ // SyncML 1.1 has object size
+ metinfP->maxobjsize=newPCDataLong(getRootConfig()->fLocalMaxObjSize);
+ }
+ }
+ return metaP;
+} // TSyncSession::newHeaderMeta
+
+
+// create new SyncHdr structure for TSyncHeader command
+// (here because all data for this is in session anyway)
+// Called exclusively from TSyncHeader command
+SmlSyncHdrPtr_t TSyncSession::NewOutgoingSyncHdr(bool aOutgoingNoResp)
+{
+ SmlSyncHdrPtr_t headerP;
+
+ MP_SHOWCURRENT(DBG_PROFILE,"Start of outgoing message");
+ // set response status for entire message
+ fOutgoingNoResp=aOutgoingNoResp;
+ // get new number for this message
+ fOutgoingMsgID++;
+ // reset message size counting
+ fOutgoingMsgSize=0;
+ // reset command ID
+ fOutgoingCmdID=0;
+ // now compose Sync Header from session vars
+ // - create empty header
+ headerP = SML_NEW(SmlSyncHdr_t);
+ #ifdef EXPIRES_AFTER_DATE
+ // prepare for a check, convert back to normal value * 4
+ sInt32 scramblednow4 = (fCopyOfScrambledNow-503);
+ #endif
+ SYSYNC_TRY {
+ // set proto element type to make it auto-disposable
+ headerP->elementType=SML_PE_HEADER;
+ // set version information
+ headerP->version=newPCDataString(SyncMLVerDTDNames[fSyncMLVersion]);
+ headerP->proto=newPCDataString(SyncMLVerProtoNames[fSyncMLVersion]);
+ // set session ID
+ headerP->sessionID=newPCDataString(fSynchdrSessionID);
+ // set new message ID for this message
+ #ifdef APP_CAN_EXPIRE
+ // check for expiry again
+ #ifdef EXPIRES_AFTER_DATE
+ // - has hard expiry date
+ if (
+ (scramblednow4>SCRAMBLED_EXPIRY_VALUE*4)
+ #ifdef SYSER_REGISTRATION
+ && (!getSyncAppBase()->fRegOK) // only abort if no registration (but accept timed registration)
+ #endif
+ )
+ getSyncAppBase()->fAppExpiryStatus=LOCERR_EXPIRED;
+ #else
+ // - no hard expiry date, just check if license is still valid
+ if (
+ !getSyncAppBase()->fRegOK || // no registered at all
+ getSyncAppBase()->fDaysLeft==0 // or expired
+ )
+ getSyncAppBase()->fAppExpiryStatus=LOCERR_EXPIRED;
+ #endif
+ #endif
+ headerP->msgID=newPCDataLong(fOutgoingMsgID);
+ // flags
+ headerP->flags=fOutgoingNoResp ? SmlNoResp_f : 0;
+ // target (URI/Name of Remote party)
+ // Note: Tsutomu Uenoyama (uenoyama@trl.mei.co.jp) sais in syncml feedback
+ // list that server RespURI behaviour should
+ // be reflecting received RespURI in target, so we do it:
+ headerP->target=newLocation(
+ fRespondURI.empty() ? fRemoteURI.c_str() : fRespondURI.c_str(),
+ fRemoteName.c_str()
+ );
+ PDEBUGPRINTFX(DBG_PROTO,("Target (Remote URI) = '%s'",fRespondURI.empty() ? fRemoteURI.c_str() : fRespondURI.c_str()));
+ // source (URI / User-Name of local party)
+ // NOTE: The LocName must contain the name of the user and not
+ // the name of the device (this is a SyncML 1.0.1 correction
+ // to make MD5 auth implementable - we need a clear-text user name)
+ headerP->source=newLocation(
+ fLocalURI.c_str(),
+ getUsernameForRemote() // user name for remote login, NULL if none available
+ );
+ // add respURI if local party cannot be responded to via normal URI
+ // New added for T68i: do not send a RespURI for an aborted session
+ if (!isAborted() && fMessageAuthorized)
+ headerP->respURI=newResponseURIForRemote();
+ else
+ headerP->respURI=NULL;
+ // add credentials if remote needs them
+ headerP->cred=newCredentialsForRemote();
+ // agent-specific meta
+ headerP->meta=newHeaderMeta();
+ }
+ SYSYNC_CATCH (...)
+ // make sure header is disposed
+ smlFreeProtoElement(headerP);
+ SYSYNC_RETHROW; // re-throw
+ SYSYNC_ENDCATCH
+ // return it
+ return headerP;
+} // TSyncSession::NewOutgoingSyncHdr
+
+
+// delay command for execution at beginning of next received message
+void TSyncSession::delayExecUntilNextRequest(TSmlCommand *aCommand)
+{
+ // push into delay queue
+ fDelayedExecutionCommands.push_back(aCommand);
+ // a delayed (=not processed) syncstart must clear the current fLocalSyncDatastoreP,
+ // as it is not yet known for that <sync>. This causes syncops to receive a NULL datastore
+ // at creation, so they must check for that and get it at execute().
+ if (aCommand->getCmdType()==scmd_sync) {
+ // delayed <sync> has no datastore (yet)
+ fLocalSyncDatastoreP=NULL;
+ }
+ else if (aCommand->getCmdType()==scmd_syncend) {
+ // count delayed syncends as they need special care later
+ fDelayedExecSyncEnds++;
+ // and forget current datastore - safety only, should be NULL here anyway
+ fLocalSyncDatastoreP=NULL;
+ }
+} // TSyncSession::delayExecUntilNextRequest
+
+
+// remote party requests next message by Alert 222
+void TSyncSession::nextMessageRequest(void)
+{
+ // count the request
+ fNextMessageRequests++;
+ #ifndef SYSYNC_CLIENT
+ // check if we have seen many requests but could not fulfil them
+ if (fNextMessageRequests>3) {
+ // check for resume that does not send us an empty Sync (Symbian client at TestFest 16)
+ PDEBUGPRINTFX(DBG_ERROR,("Warning: More than 3 consecutive Alert 222 - looks like endless loop, check if we need to work around client implementation issues"));
+ // - check datastores
+ TLocalDataStorePContainer::iterator pos;
+ for (pos=fLocalDataStores.begin(); pos!=fLocalDataStores.end(); ++pos) {
+ // see if it is currently resuming
+ TLocalEngineDS *ldsP = (*pos);
+ if (ldsP->isResuming() && ldsP->getDSState()<dssta_serverseenclientmods) {
+ // fake empty <sync> from client to get things going again
+ // - create it
+ SmlSyncPtr_t fakeSyncCmdP = (SmlSyncPtr_t)smlLibMalloc(sizeof(SmlSync_t));
+ fakeSyncCmdP->elementType = SML_PE_SYNC_START;
+ fakeSyncCmdP->cmdID=NULL; // none needed here
+ fakeSyncCmdP->flags=0; // none
+ fakeSyncCmdP->cred=NULL;
+ fakeSyncCmdP->target=newLocation(ldsP->getName()); // client would target myself
+ fakeSyncCmdP->source=newLocation(ldsP->getRemoteDBPath()); // client would target myself
+ fakeSyncCmdP->meta=NULL; // no meta
+ fakeSyncCmdP->noc=NULL; // no NOC
+ // - have it processed like it was a real command
+ PDEBUGPRINTFX(DBG_HOT+DBG_PROTO,("Probably client expects resume to continue without sending an empty <Sync> -> simulate one"));
+ PDEBUGBLOCKFMT((
+ "Resume_Sim_Sync","Simulated empty sync to get resume going",
+ "datastore=%s",
+ ldsP->getName()
+ ));
+ TStatusCommand *fakeStatusCmdP = new TStatusCommand(this);
+ bool queueforlater=false;
+ bool ok=processSyncStart(
+ fakeSyncCmdP,
+ *fakeStatusCmdP,
+ queueforlater // will be set if command must be queued for later re-execution
+ );
+ if (!queueforlater) {
+ fNextMessageRequests=0; // reset that counter
+ // and make sure we advance the sync session state
+ // - now the real ugly hacking starts - we have to fake receiving a <final/>
+ fFakeFinalFlag=true;
+ }
+ else {
+ PDEBUGPRINTFX(DBG_ERROR,("simulated <Sync> can't be processed now, we'll try again later"));
+ }
+ // now just simulate a </sync>
+ processSyncEnd(queueforlater);
+ // now let this particular datastore "know" that sync-from-client is over now
+ (*pos)->engEndOfSyncFromRemote(true); // fake "final"
+ PDEBUGENDBLOCK("Resume_Sim_Sync");
+ }
+ }
+ }
+ #endif // SYSYNC_CLIENT
+} // TSyncSession::nextMessageRequest
+
+
+
+
+// check if session must continue (for session-level reasons, that
+// is without regarding sync state of server or client)
+bool TSyncSession::sessionMustContinue(void) {
+ // if there are delayed commands not yet executed after this message: session must go on
+ if (!fDelayedExecutionCommands.empty()) {
+ PDEBUGPRINTFX(DBG_SESSION,(
+ "%ld commands in delayed-execution-queue -> session must continue",
+ (long)fDelayedExecutionCommands.size()
+ ));
+ return true; // must continue
+ }
+ // if no status to wait for: session may be deleted now
+ if (fStatusWaitCommands.empty()) return false;
+ TSmlCommandPContainer::iterator pos;
+ // show them
+ #ifdef SYDEBUG
+ for (pos=fStatusWaitCommands.begin(); pos!=fStatusWaitCommands.end(); ++pos) {
+ TSmlCommand *cmdP = *pos;
+ // show that command was not answered
+ PDEBUGPRINTFX(DBG_PROTO,("- Not yet received %sstatus for command '%s', (outgoing MsgID=%ld, CmdID=%ld)",
+ cmdP->statusEssential() ? "REQUIRED " : "",
+ cmdP->getName(),
+ (long)cmdP->getMsgID(),
+ (long)cmdP->getCmdID()
+ ));
+ }
+ #endif
+ // check type of commands we miss status for
+ bool mustgoon=false;
+ for (pos=fStatusWaitCommands.begin(); pos!=fStatusWaitCommands.end(); ++pos) {
+ TSmlCommand *cmdP = *pos;
+ if (cmdP->statusEssential()) {
+ // we need a status for at least one of these
+ mustgoon=true;
+ break;
+ }
+ }
+ // if only one single status to wait for, check if it is
+ // SyncHdr status; if no, session MUST continue
+ // (otherwise, remote will not send status for SyncHdr alone)
+ if (mustgoon) {
+ PDEBUGPRINTFX(DBG_HOT,("SESSION CANNOT END - Not yet received REQUIRED status for some of %ld commands",(long)fStatusWaitCommands.size()));
+ }
+ return mustgoon;
+} // TSyncSession::sessionMustContinue
+
+
+// returns true if session has pending commands
+bool TSyncSession::hasPendingCommands(void)
+{
+ return (!(
+ fNextMessageCommands.size()==0 && // ..no commands to send in next message AND
+ fDelayedExecutionCommands.size()==0 && // ..no commands to process in next message AND
+ fInterruptedCommandP==NULL // ..no interrupted outgoing commands
+ ));
+} // TSyncSession::hasPendingCommands
+
+
+// finish outgoing Message, returns true if final message of package
+bool TSyncSession::FinishMessage(bool aAllowFinal, bool aForceNonFinal)
+{
+ Ret_t err;
+ bool final=true;
+
+ // finish message if any
+ if (fOutgoingStarted) {
+ // there is an unfinished message, finish it
+ // - final only if no commands waiting for next message
+ // and caller allows final
+ final=
+ !aForceNonFinal && (
+ fAborted || // if aborted, this is a final message, OR..
+ (aAllowFinal && !hasPendingCommands()) // ..(if allowed final AND no pending commands)
+ ); // ...THEN this is a final package
+ PDEBUGPRINTFX(DBG_PROTO,(
+ "Ending message with %s%ld next-message/%ld next-package commands: %sFINAL (%sfinal %sallowed by caller)",
+ fInterruptedCommandP ? "interrupted command and " : "",
+ (long)fNextMessageCommands.size(),
+ (long)fNextPackageCommands.size(),
+ final ? "" : "NOT ",
+ fAborted ? "ABORTED, " : "",
+ aAllowFinal ? "" : "not "
+ ));
+ // - close message now
+ sInt32 bytesbeforeissue=getSmlWorkspaceFreeBytes();
+ fOutgoingStarted=false; // done now
+ fOutgoingMessageFull=false; // message finished, not full any more
+ #ifdef SYDEBUG
+ if (fXMLtranslate && fOutgoingXMLInstance)
+ smlEndMessage(fOutgoingXMLInstance,final);
+ // Now dump XML translation of outgoing message
+ XMLTranslationOutgoingEnd();
+ #endif
+ if ((err=smlEndMessage(fSmlWorkspaceID, final))!=SML_ERR_OK) {
+ SYSYNC_THROW(TSmlException("smlEndMessage",err));
+ }
+ incOutgoingMessageSize(bytesbeforeissue-getSmlWorkspaceFreeBytes());
+ PDEBUGPRINTFX(DBG_PROTO,("Entire message size is now %ld Bytes",(long)getOutgoingMessageSize()));
+ // if outgoing message was not final, prevent session end
+ // except if session is aborted (and final flag is forced nonFinal e.g. when ending session because of serverBusy()
+ if (!final && !fAborted) fInProgress=true;
+ CONSOLEPRINTF(("< SyncML message #%ld sent to '%s'",(long)fOutgoingMsgID,fRespondURI.empty() ? fRemoteURI.c_str() : fRespondURI.c_str()));
+ MP_SHOWCURRENT(DBG_PROFILE,"End of outgoing message");
+ // dump it if configured
+ #ifdef SYDEBUG
+ DumpSyncMLMessage(true); // outgoing
+ #endif
+ }
+ // if this message ends with <Final/>, next message will be in new package
+ fNewOutgoingPackage=final;
+ return final;
+} // TSyncSession::FinishMessage
+
+
+// get name of current encoding
+const char *TSyncSession::getEncodingName(void)
+{
+ return SyncMLEncodingMIMENames[fEncoding];
+} // TSyncSession::getEncodingName
+
+
+// add current encoding spec to given (type-)string
+void TSyncSession::addEncoding(string &aString)
+{
+ aString+=SYNCML_ENCODING_SEPARATOR;
+ aString+=getEncodingName();
+} // TSyncSession::addEncoding
+
+
+// find remote datastore by (remote party specified) URI
+TRemoteDataStore *TSyncSession::findRemoteDataStore(const char *aDatastoreURI)
+{
+ TRemoteDataStorePContainer::iterator pos;
+ TRemoteDataStore *bestMatchP=NULL;
+ uInt16 bestNumMatched=0;
+ // search for BEST match (most number of chars matched)
+ for (pos=fRemoteDataStores.begin(); pos!=fRemoteDataStores.end(); ++pos) {
+ // test for match
+ if ((*pos)->isDatastore(aDatastoreURI) > bestNumMatched) {
+ bestMatchP = *pos; // best so far, but check all
+ }
+ }
+ return bestMatchP; // return NULL if no match found or best matching
+} // TSyncSession::findRemoteDataStore
+
+
+// - find local datastore by URI and separate identifying from optional part of URI
+TLocalEngineDS *TSyncSession::findLocalDataStoreByURI(const char *aURI,string *aOptions, string *aIdentifyingURI)
+{
+ string dburi;
+
+ // - get relative URI of requested database
+ const char *dblocuri = SessionRelativeURI(aURI);
+ // In this base class implementation, identification is path, options are CGI
+ // - separate target address and CGI (if any)
+ const char *optionsCGI=(const char *)strchr(dblocuri,'?');
+ if (optionsCGI) {
+ dburi.assign(dblocuri,optionsCGI-dblocuri);
+ optionsCGI++; // skip '?'
+ dblocuri=dburi.c_str();
+ PDEBUGPRINTFX(DBG_PROTO,("Target Address CGI Options: %s",optionsCGI));
+ if (aOptions) aOptions->assign(optionsCGI);
+ }
+ else {
+ // no CGI contained
+ if (aOptions) aOptions->erase();
+ }
+ // assign identifying part of URL now
+ if (aIdentifyingURI) {
+ aIdentifyingURI->assign(dblocuri);
+ }
+ // find datastore now
+ DEBUGPRINTF(("Determined relative, identifying URI (w/o CGI): %s",dblocuri));
+ return findLocalDataStore(dblocuri);
+} // TSyncSession::findLocalDataStorebyURI
+
+
+// find local datastore by (relative) URI
+TLocalEngineDS *TSyncSession::findLocalDataStore(const char *aDatastoreURI)
+{
+ TLocalDataStorePContainer::iterator pos;
+ for (pos=fLocalDataStores.begin(); pos!=fLocalDataStores.end(); ++pos) {
+ // test for match (we do not do best-match search here because these are names
+ // under our own control that do not contain slashes and hence no
+ // mismatch possibilities like "/Calendar" and "/Calendar/Events" as
+ // with Oracle server.
+ if ((*pos)->isDatastore(aDatastoreURI))
+ return (*pos); // found
+ }
+ return NULL; // none found
+} // TSyncSession::findLocalDataStore
+
+
+// - find local datastore by datastore handle (=config pointer)
+TLocalEngineDS *TSyncSession::findLocalDataStore(void *aDSHandle)
+{
+ TLocalDataStorePContainer::iterator pos;
+ for (pos=fLocalDataStores.begin(); pos!=fLocalDataStores.end(); ++pos) {
+ // test for match
+ TLocalEngineDS *ldsP = (*pos);
+ if ((void *)(ldsP->getDSConfig())==aDSHandle)
+ return (ldsP); // found
+ }
+ return NULL; // none found
+} // TSyncSession::findLocalDataStore
+
+
+TLocalEngineDS *TSyncSession::addLocalDataStore(TLocalDSConfig *aLocalDSConfigP)
+{
+ TLocalEngineDS *ldsP=aLocalDSConfigP->newLocalDataStore(this);
+ fLocalDataStores.push_back(ldsP);
+ return ldsP;
+} // TSyncSession::addLocalDataStore
+
+
+
+// - find local datatype by config pointer (used to avoid duplicating types
+// in session if used by more than a single datastore)
+TSyncItemType *TSyncSession::findLocalType(TDataTypeConfig *aDataTypeConfigP)
+{
+ TSyncItemTypePContainer::iterator pos;
+ for (pos=fLocalItemTypes.begin(); pos!=fLocalItemTypes.end(); ++pos) {
+ // test for match
+ if ((void *)((*pos)->getTypeConfig())==aDataTypeConfigP)
+ return (*pos); // found
+ }
+ return NULL; // none found
+} // TSyncSession::findLocalType
+
+
+// - find implemented remote datatype by config pointer (and related datastore, if any)
+TSyncItemType *TSyncSession::findRemoteType(TDataTypeConfig *aDataTypeConfigP, TSyncDataStore *aRelatedRemoteDS)
+{
+ TSyncItemTypePContainer::iterator pos;
+ for (pos=fRemoteItemTypes.begin(); pos!=fRemoteItemTypes.end(); ++pos) {
+ // test for match
+ if ((void *)((*pos)->getTypeConfig())==aDataTypeConfigP) {
+ // match only if related to same datastore or not related
+ if (aRelatedRemoteDS == (*pos)->getRelatedDatastore())
+ return (*pos); // found
+ }
+ }
+ return NULL; // none found
+} // TSyncSession::findRemoteType
+
+
+
+
+
+// get new list of all local datastores
+SmlDevInfDatastoreListPtr_t TSyncSession::newDevInfDataStoreList(bool aAlertedOnly, bool aWithoutCTCapProps)
+{
+ SmlDevInfDatastoreListPtr_t rootP,*insertpos;
+ SmlDevInfDatastorePtr_t datastoreP;
+
+ // no list at beginning
+ rootP=NULL;
+ insertpos = &rootP;
+
+ // go through local datastore list
+ TLocalDataStorePContainer::iterator pos1;
+ for (pos1=fLocalDataStores.begin(); pos1!=fLocalDataStores.end(); ++pos1) {
+ // check if we only want alerted datastore's info (client case)
+ if (aAlertedOnly) {
+ if (!(*pos1)->testState(dssta_clientsentalert))
+ continue; // not alerted, do not show this one
+ }
+ // see if we have info at all
+ datastoreP = (*pos1)->getDatastoreDevinf(IsServerSession(), aWithoutCTCapProps);
+ if (datastoreP) {
+ // create new list item
+ (*insertpos) = SML_NEW(SmlDevInfDatastoreList_t);
+ (*insertpos)->next = NULL;
+ (*insertpos)->data = datastoreP;
+ // set new insert position
+ insertpos = &((*insertpos)->next);
+ }
+ }
+ return rootP;
+} // TSyncSession::newDevInfDataStoreList
+
+
+// get common sync capabilities mask of this session (datastores might modify it)
+uInt32 TSyncSession::getSyncCapMask(void)
+{
+ return
+ SCAP_MASK_NORMAL |
+ (getSessionConfig()->fAcceptServerAlerted ? SCAP_MASK_SERVER_ALERTED : 0);
+} // TSyncSession::getSyncCapMask
+
+
+// get new list of all local item types
+SmlDevInfCtcapListPtr_t TSyncSession::newLocalCTCapList(bool aAlertedOnly, TLocalEngineDS *aOnlyForDS, bool aWithoutCTCapProps)
+{
+ SmlDevInfCtcapListPtr_t rootP,*insertpos;
+ SmlDevInfCTCapPtr_t ctcapP;
+
+ // no list at beginning
+ rootP=NULL;
+ insertpos = &rootP;
+ bool showTy;
+ TTypeVariantDescriptor variantDesc = NULL;
+
+ // go through local datastore list
+ // Note: rulematch types should normally not be shown, but the way to do that
+ // is not some testing here (which is almost impossible), but profiles
+ // defined for rulematch types should be made invisible.
+ TSyncItemTypePContainer::iterator pos2;
+ TLocalDataStorePContainer::iterator pos1;
+ for (pos2=fLocalItemTypes.begin(); pos2!=fLocalItemTypes.end(); ++pos2) {
+ showTy=true;
+ // check for restriction to certain datastore
+ if (aOnlyForDS) {
+ // only show if specified datastore is using the type
+ showTy = aOnlyForDS->doesUseType(*pos2, &variantDesc);
+ }
+ // see if datatype is used by any of the alerted datastores
+ if (showTy && aAlertedOnly) {
+ showTy=false;
+ for (pos1=fLocalDataStores.begin(); pos1!=fLocalDataStores.end(); ++pos1) {
+ // check if we only want alerted datastore's info (client case)
+ if (!(*pos1)->testState(dssta_clientsentalert)) continue; // test next
+ // see if datatype is used by this datastore
+ if ((*pos1)->doesUseType(*pos2)) {
+ showTy=true;
+ break;
+ }
+ }
+ }
+ // now show if selected
+ if (showTy) {
+ // see if we have info at all
+ ctcapP = (*pos2)->getCTCapDevInf(aOnlyForDS, variantDesc, aWithoutCTCapProps);
+ if (ctcapP) {
+ // create new list item
+ (*insertpos) = SML_NEW(SmlDevInfCtcapList_t);
+ (*insertpos)->next = NULL;
+ (*insertpos)->data = ctcapP;
+ // set new insert position
+ insertpos = &((*insertpos)->next);
+ }
+ }
+ }
+ return rootP;
+} // TSyncSession::newLocalCTCapList
+
+
+// build DevInf of this session
+SmlDevInfDevInfPtr_t TSyncSession::newDevInf(bool aAlertedOnly, bool aWithoutCTCapProps)
+{
+ SmlDevInfDevInfPtr_t devinfP;
+
+ // Create empty DevInf
+ devinfP = SML_NEW(SmlDevInfDevInf_t);
+ // Fill in information for current session
+ devinfP->verdtd=newPCDataString(SyncMLVerDTDNames[fSyncMLVersion]);
+ // - identification of this SyncML implementation
+ devinfP->man=newPCDataOptString(getSyncAppBase()->getManufacturer().c_str());
+ devinfP->mod=newPCDataOptString(getSyncAppBase()->getModel().c_str());
+ devinfP->oem=newPCDataOptString(getSyncAppBase()->getOEM());
+ devinfP->swv=newPCDataOptString(getSyncAppBase()->getSoftwareVersion());
+ // - identification of the device ID and type (server/client etc.)
+ devinfP->devid=newPCDataString(getDeviceID().c_str());
+ devinfP->devtyp=newPCDataString(getDeviceType().c_str());
+ // - identification of the platform the software runs on
+ devinfP->hwv=newPCDataOptString(getSyncAppBase()->getHardwareVersion().c_str());
+ devinfP->fwv=newPCDataOptString(getSyncAppBase()->getFirmwareVersion().c_str());
+ // Now get info for content capabilities
+ if (fSyncMLVersion<syncml_vers_1_2) {
+ // CTCap is global, get it without limitation to a datastore
+ devinfP->ctcap=newLocalCTCapList(aAlertedOnly, NULL, aWithoutCTCapProps);
+ }
+ else
+ devinfP->ctcap=NULL; // no global CTCap any more at devInf level
+ // Now get info for datastores
+ devinfP->datastore=newDevInfDataStoreList(aAlertedOnly, aWithoutCTCapProps);
+ // SyncML 1.1 related flags
+ devinfP->flags=0; // no SyncML 1.1 flags by default
+ if (fSyncMLVersion>=syncml_vers_1_1) {
+ // - we can always parse number of changes (whether we can make use of it is irrelevant)
+ devinfP->flags |= SmlDevInfNOfM_f;
+ // - check if we support UTC based time (implementations with no means to obtain time zone might not)
+ if (canHandleUTC())
+ devinfP->flags |= SmlDevInfUTC_f; // we can handle UTC
+ // - we support large object
+ devinfP->flags |= SmlDevInfLargeObject_f;
+ }
+ // Now get extensions info
+ devinfP->ext=NULL;
+ // %%% tdb, optional
+ // return
+ return devinfP;
+} // TSyncSession::newDevInf
+
+
+// get devInf for this session (caller is passed ownership)
+SmlItemPtr_t TSyncSession::getLocalDevInfItem(bool aAlertedOnly, bool aWithoutCTCapProps)
+{
+ // - create item with correct source and Meta information
+ SmlItemPtr_t devinf = newItem();
+ // %%% if strictly following example in SyncML protocol specs,
+ // source should not have a Displayname
+ //fLocalDevInfItemP->source=newLocation(SYNCML_DEVINF_LOCURI,SYNCML_DEVINF_LOCNAME);
+ // %%% if strictly following example in SyncML protocol specs,
+ // source should not have "./" prefix
+ // %%% this is disputable, DCM expects ./, so we send it again now
+ devinf->source=newLocation(SyncMLDevInfNames[fSyncMLVersion]);
+ // - create meta type
+ /* %%% if strictly following example in SyncML protocol specs, meta
+ * must be defined in the result/put command, not the individual item
+ string metatype=SYNCML_DEVINF_META_TYPE;
+ addEncoding(metatype);
+ fLocalDevInfItemP->meta=newMetaType(metatype.c_str());
+ %%% */
+ // - create DevInf PCData
+ devinf->data = SML_NEW(SmlPcdata_t);
+ devinf->data->contentType=SML_PCDATA_EXTENSION;
+ devinf->data->extension=SML_EXT_DEVINF;
+ // - %%% assume length is not relevant for structured content (looks like in mgrutil.c)
+ devinf->data->length=0;
+ // - create and insert DevInf
+ devinf->data->content = newDevInf(aAlertedOnly, aWithoutCTCapProps);
+ // - done
+ return devinf;
+} // TSyncSession::getLocalDevInfItem
+
+
+// analyze remote devinf delivered by Put or Get/Result commands
+// or loaded from cache by loadRemoteDevInf()
+localstatus TSyncSession::analyzeRemoteDevInf(
+ SmlDevInfDevInfPtr_t aDevInfP
+)
+{
+ localstatus sta = LOCERR_OK;
+ PDEBUGBLOCKDESC("DevInf_Analyze","Analyzing remote devInf");
+ if (!aDevInfP) {
+ PDEBUGPRINTFX(DBG_ERROR,("Warning: No DevInf found (possible cause: improperly encoded devInf from remote)"));
+ sta=400; // no devInf
+ goto done;
+ }
+ else {
+ // we have seen the devinf now
+ fRemoteDevInfKnown=true;
+ // analyze what device we have here
+ PDEBUGPRINTFX(DBG_REMOTEINFO+DBG_HOT,(
+ "Device ID='%" FMT_LENGTH(".50") "s', Type='%" FMT_LENGTH(".20") "s', Model='%" FMT_LENGTH(".50") "s'",
+ FMT_LENGTH_LIMITED(50,smlPCDataToCharP(aDevInfP->devid)),
+ FMT_LENGTH_LIMITED(20,smlPCDataToCharP(aDevInfP->devtyp)),
+ FMT_LENGTH_LIMITED(50,smlPCDataToCharP(aDevInfP->mod))
+ ));
+ PDEBUGPRINTFX(DBG_REMOTEINFO+DBG_HOT,(
+ "Manufacturer='%" FMT_LENGTH(".30") "s', OEM='%" FMT_LENGTH(".30") "s'",
+ FMT_LENGTH_LIMITED(30,smlPCDataToCharP(aDevInfP->man)),
+ FMT_LENGTH_LIMITED(30,smlPCDataToCharP(aDevInfP->oem))
+ ));
+ PDEBUGPRINTFX(DBG_REMOTEINFO+DBG_HOT,(
+ "Softwarevers='%" FMT_LENGTH(".30") "s', Firmwarevers='%" FMT_LENGTH(".30") "s', Hardwarevers='%" FMT_LENGTH(".30") "s'",
+ FMT_LENGTH_LIMITED(30,smlPCDataToCharP(aDevInfP->swv)),
+ FMT_LENGTH_LIMITED(30,smlPCDataToCharP(aDevInfP->fwv)),
+ FMT_LENGTH_LIMITED(30,smlPCDataToCharP(aDevInfP->hwv))
+ ));
+ #ifndef MINIMAL_CODE
+ // get the devinf details 1:1
+ fRemoteDevInf_devid=smlPCDataToCharP(aDevInfP->devid);
+ fRemoteDevInf_devtyp=smlPCDataToCharP(aDevInfP->devtyp);
+ fRemoteDevInf_mod=smlPCDataToCharP(aDevInfP->mod);
+ fRemoteDevInf_man=smlPCDataToCharP(aDevInfP->man);
+ fRemoteDevInf_oem=smlPCDataToCharP(aDevInfP->oem);
+ fRemoteDevInf_swv=smlPCDataToCharP(aDevInfP->swv);
+ fRemoteDevInf_fwv=smlPCDataToCharP(aDevInfP->fwv);
+ fRemoteDevInf_hwv=smlPCDataToCharP(aDevInfP->hwv);
+ // get the descriptive name of the device
+ fRemoteDescName.assign(smlPCDataToCharP(aDevInfP->man));
+ if (fRemoteDescName.size()>0) fRemoteDescName+=" ";
+ fRemoteDescName.append(smlPCDataToCharP(aDevInfP->mod));
+ // get extra info: "Type (HWV, FWV, SWV) Oem"
+ fRemoteInfoString+=fRemoteDevInf_devtyp;
+ fRemoteInfoString+=" (";
+ fRemoteInfoString+=fRemoteDevInf_hwv;
+ fRemoteInfoString+=", ";
+ fRemoteInfoString+=fRemoteDevInf_fwv;
+ fRemoteInfoString+=", ";
+ fRemoteInfoString+=fRemoteDevInf_swv;
+ fRemoteInfoString+=") ";
+ fRemoteInfoString+=fRemoteDevInf_oem;
+ #endif
+ // Show SyncML version here (again)
+ PDEBUGPRINTFX(DBG_REMOTEINFO+DBG_HOT,("SyncML Version: %s",SyncMLVerProtoNames[fSyncMLVersion]));
+ // check SyncML 1.1 flags
+ if (fSyncMLVersion>=syncml_vers_1_1) {
+ // - check if remote can receive NOC (number of changes)
+ fRemoteWantsNOC = (aDevInfP->flags & SmlDevInfNOfM_f);
+ // - check if remote can handle UTC time
+ fRemoteCanHandleUTC = (aDevInfP->flags & SmlDevInfUTC_f);
+ // - check if remote supports large objects
+ fRemoteSupportsLargeObjects = (aDevInfP->flags & SmlDevInfLargeObject_f);
+ PDEBUGPRINTFX(DBG_REMOTEINFO+DBG_HOT,(
+ "SyncML capability flags: wantsNOC=%s, canHandleUTC=%s, supportsLargeObjs=%s",
+ aDevInfP->flags & SmlDevInfNOfM_f ? "Yes" : "No",
+ aDevInfP->flags & SmlDevInfUTC_f ? "Yes" : "No",
+ aDevInfP->flags & SmlDevInfLargeObject_f ? "Yes" : "No"
+ ));
+ }
+ // detect remote specific server behaviour if needed
+ sta = checkRemoteSpecifics(aDevInfP);
+ if (sta!=LOCERR_OK) {
+ remoteAnalyzed(); // analyzed to reject
+ goto done;
+ }
+ // Types and datastores may not be changed/added if sync has allready started
+ if (fRemoteDevInfLock) {
+ // Sync already started, in "blind" mode or previously received devInf,
+ // do not confuse things with changing devInf in mid-sync
+ PDEBUGPRINTFX(DBG_ERROR,(
+ "WARNING: Type and Datastore info in DevInf ignored because it came to late"
+ ));
+ }
+ else {
+ if (getSyncMLVersion()<syncml_vers_1_2) {
+ // analyze CTCaps (content type capabilities)
+ SmlDevInfCtcapListPtr_t ctlP = aDevInfP->ctcap;
+ // loop through list
+ PDEBUGBLOCKDESC("RemoteTypes", "Analyzing remote types listed in devInf level CTCap");
+ if (fIgnoreCTCap) {
+ // ignore CTCap
+ if (ctlP) {
+ PDEBUGPRINTFX(DBG_REMOTEINFO+DBG_HOT,("Remote rule prevents looking at CTCap"));
+ }
+ }
+ else {
+ while (ctlP) {
+ if (ctlP->data) {
+ // create appropriate remote data itemtypes
+ if (TSyncItemType::analyzeCTCapAndCreateItemTypes(
+ this,
+ NULL, // this is the pre-DS1.2 style where CTCap is on devInf level
+ ctlP->data, // CTCap
+ fLocalItemTypes, // look up in local types for specialized classes
+ fRemoteItemTypes // add new item types here
+ )) {
+ // we have CTCap info of at least one remote type
+ fRemoteDataTypesKnown=true;
+ }
+ else {
+ PDEBUGPRINTFX(DBG_ERROR,("CTCap could not be used (missing version)"));
+ sta=500;
+ }
+ }
+ // - go to next item
+ ctlP=ctlP->next;
+ } // while
+ }
+ PDEBUGENDBLOCK("RemoteTypes");
+ } // if <DS1.2
+ // now get datastores
+ PDEBUGBLOCKDESC("RemoteDatastores", "Analyzing remote datastores");
+ SmlDevInfDatastoreListPtr_t dslP = aDevInfP->datastore;
+ while(dslP) {
+ if (dslP->data) {
+ // we have DataStore info of remote datastores
+ fRemoteDataStoresKnown=true;
+ // there is a DataStore entry, create RemoteDataStore for it
+ TRemoteDataStore *datastoreP;
+ MP_NEW(datastoreP,DBG_OBJINST,"TRemoteDataStore",TRemoteDataStore(this));
+ PDEBUGBLOCKDESC("RemoteDSDevInf", "Registering remote Datastore from devInf");
+ // let new datastore analyze the devinf data
+ if (!datastoreP->setDatastoreDevInf(
+ dslP->data,
+ fLocalItemTypes, // look up for datatypes here first
+ fRemoteItemTypes // but add types here
+ )) {
+ // invalid CTCap
+ PDEBUGPRINTFX(DBG_ERROR,("Invalid DataStore devInf"));
+ delete datastoreP; // forget invalid data store
+ sta=500; // failed
+ }
+ else {
+ // CTCap set successfully, new type created
+ // - save it in the list of remote types
+ fRemoteDataStores.push_back(datastoreP);
+ }
+ PDEBUGENDBLOCK("RemoteDSDevInf");
+ }
+ // - go to next item
+ dslP=dslP->next;
+ } // while
+ PDEBUGENDBLOCK("RemoteDatastores");
+ } // else sync not started yet
+ }
+ // give descendants possibility to do something with the analyzed data
+ remoteAnalyzed();
+ // ok
+done:
+ PDEBUGENDBLOCK("DevInf_Analyze");
+ return sta;
+} // TSyncSession::analyzeRemoteDevInf
+
+
+#ifndef SYSYNC_CLIENT
+
+// Initialize Sync: set up datastores and types for server sync session
+localstatus TSyncSession::initSync(
+ const char *aLocalDatastoreURI,
+ const char *aRemoteDatastoreURI
+)
+{
+ localstatus sta = LOCERR_OK;
+
+ // search for local datastore first
+ string cgiOptions;
+ // - search for datastore and obtain eventual CGI
+ fLocalSyncDatastoreP = findLocalDataStoreByURI(SessionRelativeURI(aLocalDatastoreURI),&cgiOptions);
+ if (!fLocalSyncDatastoreP) {
+ // no such local datastore
+ return 404;
+ }
+ // Local datastore is known here (fLocalSyncDatastoreP)
+ // - now init for reception of syncops
+ sta = fLocalSyncDatastoreP->engInitForSyncOps(aRemoteDatastoreURI);
+ #ifdef SYNCML_TAF_SUPPORT
+ if (sta==LOCERR_OK) {
+ // - make sure that options are reparsed (TAF *might* change from Sync request to Sync request)
+ sta = fLocalSyncDatastoreP->engParseOptions(
+ cgiOptions.c_str(),
+ true // we are parsing options from <sync> target URI
+ );
+ }
+ #endif
+ #ifdef OBJECT_FILTERING
+ if (sta==LOCERR_OK) {
+ // %%% parse DS 1.2 <filter>
+ #if !defined _MSC_VER || defined WINCE
+ #warning "tbd%%%: parse <filter>"
+ #endif
+ }
+ #endif
+ #ifdef OBJECT_FILTERING
+ // Show filter summary
+ #ifdef SYDEBUG
+ #ifdef SYNCML_TAF_SUPPORT
+ PDEBUGPRINTFX(DBG_FILTER,("TAF (temporary, INCLUSIVE) Filter : %s",fLocalSyncDatastoreP->fTargetAddressFilter.c_str()));
+ #endif // SYNCML_TAF_SUPPORT
+ PDEBUGPRINTFX(DBG_FILTER,("SyncSet (dynamic, EXCLUSIVE) Filter : %s",fLocalSyncDatastoreP->fSyncSetFilter.c_str()));
+ #ifdef SYSYNC_TARGET_OPTIONS
+ string ts;
+ StringObjTimestamp(ts,fLocalSyncDatastoreP->fDateRangeStart);
+ PDEBUGPRINTFX(DBG_FILTER,("Date Range Start : %s",fLocalSyncDatastoreP->fDateRangeStart ? ts.c_str() : "<none>"));
+ StringObjTimestamp(ts,fLocalSyncDatastoreP->fDateRangeEnd);
+ PDEBUGPRINTFX(DBG_FILTER,("Date Range End : %s",fLocalSyncDatastoreP->fDateRangeEnd ? ts.c_str() : "<none>"));
+ #endif // SYSYNC_TARGET_OPTIONS
+ #endif // SYDEBUG
+ #endif // OBJECT_FILTERING
+ // return status
+ return sta;
+} // TSyncSession::initSync
+
+#endif // not SYSYNC_CLIENT
+
+
+
+// end sync group (of client sync commands)
+bool TSyncSession::processSyncEnd(bool &aQueueForLater)
+{
+ bool ok=true;
+
+ // inform local
+ if (fLocalSyncDatastoreP) {
+ // let datastore process it
+ ok=fLocalSyncDatastoreP->engProcessSyncCmdEnd(aQueueForLater);
+ }
+ // end Sync bracket
+ #ifdef SYNCSTATUS_AT_SYNC_CLOSE
+ // %%% status for sync command sent AFTER statuses for contained commands
+ if (fSyncCloseStatusCommandP)
+ issueRoot(fSyncCloseStatusCommandP);
+ #endif
+ // no local datastore active
+ fLocalSyncDatastoreP=NULL;
+ return ok;
+} // TSyncSession::processSyncEnd
+
+
+
+// process generic sync command item within Sync group
+// - returns true (and unmodified or non-200-successful status) if
+// operation could be processed regularily
+// - returns false (but probably still successful status) if
+// operation was processed with internal irregularities, such as
+// trying to delete non-existant item in datastore with
+// incomplete Rollbacks (which returns status 200 in this case!).
+bool TSyncSession::processSyncOpItem(
+ TSyncOperation aSyncOp, // the operation
+ SmlItemPtr_t aItemP, // the item to be processed
+ SmlMetInfMetInfPtr_t aMetaP, // command-wide meta, if any
+ TLocalEngineDS *aLocalSyncDatastore, // the local datastore for this syncop item
+ TStatusCommand &aStatusCommand, // pre-set 200 status, can be modified in case of errors
+ bool &aQueueForLater // must be set if item cannot be processed now, but must be processed later
+)
+{
+ // assign datastore context (%%% note: some day we will get rid of this
+ // "global" pointer to the active datastore by moving it into the <sync> command
+ // object and installing a hierarchical command processor.)
+ fLocalSyncDatastoreP=aLocalSyncDatastore;
+ // Server mode: commands affect datastores currently in sync
+ if (!fLocalSyncDatastoreP) {
+ // sync generic command outside sync bracket -> error
+ aStatusCommand.setStatusCode(403); // forbidden
+ ADDDEBUGITEM(aStatusCommand,"Add/Copy/Replace/Delete unrelated to datastores");
+ PDEBUGPRINTFX(DBG_ERROR,("Add/Copy/Replace/Delete unrelated to datastores"));
+ // no success
+ return false;
+ }
+ // check for aborted datastore
+ if (fLocalSyncDatastoreP->CheckAborted(aStatusCommand)) return false;
+ // check if we can process it now
+ if (!fLocalSyncDatastoreP->engIsStarted(false) || RemainingRequestTime()<0) {
+ aQueueForLater=true; // re-execute later...
+ return true; // ...but otherwise ok
+ }
+ // process Sync operation sent by remote
+ // - show
+ PDEBUGPRINTFX(DBG_DATA,(
+ "Remote sent %s-operation:",
+ SyncOpNames[aSyncOp]
+ ));
+ PDEBUGPRINTFX(DBG_DATA,(
+ "- Source: remoteID ='%s', remoteName='%s'",
+ smlSrcTargLocURIToCharP(aItemP->source),
+ smlSrcTargLocNameToCharP(aItemP->source)
+ ));
+ PDEBUGPRINTFX(DBG_DATA,(
+ "- Target: localID ='%s', remoteName='%s'",
+ smlSrcTargLocURIToCharP(aItemP->target),
+ smlSrcTargLocNameToCharP(aItemP->target)
+ ));
+ // now let datastore handle it
+ bool regular = fLocalSyncDatastoreP->engProcessSyncOpItem(aSyncOp, aItemP, aMetaP, aStatusCommand);
+ #ifdef SCRIPT_SUPPORT
+ // let script check status code
+ TErrorFuncContext errctx;
+ errctx.statuscode = aStatusCommand.getStatusCode();
+ errctx.newstatuscode = errctx.statuscode;
+ errctx.syncop = aSyncOp;
+ errctx.datastoreP = fLocalSyncDatastoreP;
+ // call script
+ regular =
+ TScriptContext::executeTest(
+ regular, // pass through regular status
+ fSessionScriptContextP,
+ getSessionConfig()->fReceivedItemStatusScript,
+ &ErrorFuncTable,
+ &errctx // caller context
+ );
+ // not completely handled, use eventually modified status code
+ #ifdef SYDEBUG
+ if (aStatusCommand.getStatusCode() != errctx.newstatuscode) {
+ PDEBUGPRINTFX(DBG_ERROR,("Status: Session Script changed original status=%hd to %hd (original op was %s)",aStatusCommand.getStatusCode(),errctx.newstatuscode,SyncOpNames[errctx.syncop]));
+ }
+ #endif
+ aStatusCommand.setStatusCode(errctx.newstatuscode);
+ #endif
+ // check status
+ if (!regular) {
+ localstatus sta=aStatusCommand.getStatusCode();
+ if (sta>=300 && sta!=419) { // conflict resolved with server data is not an error
+ PDEBUGPRINTFX(DBG_ERROR,(
+ "processSyncOpItem: Error while processing item, status=%hd",
+ aStatusCommand.getStatusCode()
+ ));
+ fLocalSyncDatastoreP->fLocalItemsError++; // count this as an error, as remote will see it as such
+ }
+ else {
+ PDEBUGPRINTFX(DBG_DATA+DBG_HOT,(
+ "processSyncOpItem: Irregularity while processing item, status=%hd",
+ aStatusCommand.getStatusCode()
+ ));
+ }
+ }
+ // done
+ return regular;
+} // TSyncSession::processSyncOpItem
+
+
+#endif // not SYNCSESSION_PART2_EXCLUDE
+#ifndef SYNCSESSION_PART1_EXCLUDE
+
+
+// generate challenge for session
+SmlChalPtr_t TSyncSession::newSessionChallenge(void)
+{
+ string nonce;
+ getNextNonce(fRemoteURI.c_str(),nonce);
+ PDEBUGPRINTFX(DBG_PROTO,(
+ "Challenge for next auth: AuthType=%s, Nonce='%s', binary %sallowed",
+ authTypeSyncMLNames[requestedAuthType()],
+ nonce.c_str(),
+ getEncoding()==SML_WBXML ? "" : "NOT "
+ ));
+ return newChallenge(requestedAuthType(),nonce,getEncoding()==SML_WBXML);
+} // TSyncSession::newSessionChallenge
+
+
+// generate credentials (based on fRemoteNonce, fRemoteRequestedAuth, fRemoteRequestedAuthEnc)
+SmlCredPtr_t TSyncSession::newCredentials(const char *aUser, const char *aPassword)
+{
+ SmlCredPtr_t credP = NULL;
+ SmlMetInfMetInfPtr_t metinfP = NULL;
+ uInt8 *authdata = NULL;
+ void *tobefreed = NULL;
+ uInt32 authdatalen=0;
+ bool isbinary=false;
+ uInt8 digest[16]; // for MD5 digest
+
+ // create auth data
+ // - build basic user/pw string
+ string userpw;
+ userpw.assign(aUser);
+ userpw+=':';
+ userpw.append(aPassword);
+ // - code auth data
+ switch (fRemoteRequestedAuth) {
+ case auth_basic:
+ // Note: this has been clarified in SyncML 1.1: even if B64 is inherent for
+ // Basic auth, specifying B64 as format does NOT mean that user:pw is B64-ed twice,
+ // but it is just an optional declaration of the inherent B64 format.
+ #ifdef BASIC_AUTH_HAS_INHERENT_B64
+ // %%% seems to be wrong according to SCTS...
+ // Note that the b64 here is PART OF THE BASIC AUTH SCHEME
+ // so format MUST NOT specify b64 again!
+ fRemoteRequestedAuthEnc=fmt_chr;
+ // make b64 of string
+ authdata=(uInt8 *)b64::encode((const uInt8 *)userpw.c_str(), userpw.size(), &authdatalen);
+ tobefreed=(void *)authdata; // remember to free at end of routine
+ #else
+ // basic auth is always b64 encoded
+ fRemoteRequestedAuthEnc=fmt_b64;
+ authdata=(uInt8 *)userpw.c_str();
+ #endif
+ break;
+ case auth_md5:
+ // Note that b64 encoding IS NOT part of the MD5 auth scheme.
+ // Only if remote specifies b64 format in challenge, b64 encoding is applied
+ if (fSyncMLVersion<syncml_vers_1_1) {
+ // before 1.1, nonce was MD5-ed together with user/pw
+ userpw+=':';
+ userpw+=fRemoteNonce; // append nonce string, might contain NULs
+ }
+ // apply MD5
+ md5::SYSYNC_MD5_CTX context;
+ md5::Init (&context);
+ md5::Update (&context, (unsigned const char *)userpw.c_str(), userpw.size());
+ // get result
+ md5::Final (digest, &context);
+ // more if SyncML 1.1 or later
+ if (fSyncMLVersion>=syncml_vers_1_1) {
+ // starting with 1.1, nonce is added to b64ed-MD5 and the MD5ed again
+ // - B64 it
+ authdata=(uInt8*)b64::encode(digest, 16, &authdatalen);
+ // - MD5 it while adding nonce
+ md5::Init (&context);
+ md5::Update (&context, authdata, authdatalen);
+ sysync_free((void *)authdata); // return buffer allocated by b64::encode
+ // - important: add colon as nonce separator
+ md5::Update (&context, (uInt8 *) ":", 1);
+ // - also add nonce that will be used for checking later
+ md5::Update (&context, (uInt8 *) fRemoteNonce.c_str(), fRemoteNonce.size());
+ // - this is the MD5 auth value
+ // according to SyncML 1.1,
+ // "changes_for_syncml_represent_v11_20020215.pdf", Section 2.19
+ md5::Final (digest, &context);
+ // - according to the above mentioned section 2.19, MD5 auth is
+ // always b64 encoded, even in binary transports. This is a
+ // contradiction to discussion in syncml@yahoogroups, particularily
+ // a statement by Peter Thompson who stated that MD5 auth MUST NOT
+ // be b64 encoded in WBXML. Who knows???
+ fRemoteRequestedAuthEnc=fmt_b64;
+ } // syncml 1.1
+ // auth data is 16 byte digest value in binary
+ authdata=(uInt8 *)digest;
+ authdatalen=16;
+ isbinary=true;
+ break;
+ default : break;
+ } // switch
+ if (authdata) {
+ // create cred
+ credP = SML_NEW(SmlCred_t);
+ // now add auth data (format if necessary)
+ // - force b64 anyway if content is binary but transport isn't
+ if (isbinary && getEncoding()==SML_XML) {
+ fRemoteRequestedAuthEnc=fmt_b64;
+ isbinary=false;
+ }
+ // - create formatted version of content. Use Opaque for binary content
+ credP->data=newPCDataFormatted(authdata,authdatalen,fRemoteRequestedAuthEnc,isbinary);
+ // create meta and get pointer
+ credP->meta=newMeta();
+ metinfP = smlPCDataToMetInfP(credP->meta);
+ // add auth type meta
+ metinfP->type=newPCDataString(authTypeSyncMLNames[fRemoteRequestedAuth]);
+ // Note: aEncType==fmt_chr will not add format tag, as fmt_chr is the default
+ metinfP->format=newPCDataFormat(fRemoteRequestedAuthEnc,false); // no format if default of fmt_chr
+ }
+ // free buffer
+ if (tobefreed) sysync_free(tobefreed);
+ // return cred or NULL if none
+ return credP;
+} // TSyncSession::newCredentials
+
+
+// check credentials
+// Note: should be called even if there are no credentials, as we
+// need a Session login BEFORE generating status with next Nonce
+bool TSyncSession::checkCredentials(const char *aUserName, const SmlCredPtr_t aCredP, TStatusCommand &aStatusCommand)
+{
+ TAuthTypes authtype=auth_basic; // default to basic
+ char *tobefreed = NULL;
+ const char *authdata = NULL;
+ TFmtTypes authfmt = fmt_chr;
+ bool authok = false;
+
+ #ifdef EXPIRES_AFTER_DATE
+ // check for hard expiry again
+ if (
+ (fCopyOfScrambledNow>(SCRAMBLED_EXPIRY_VALUE*4+503))
+ #ifdef SYSER_REGISTRATION
+ && (!getSyncAppBase()->fRegOK) // only abort if no registration (but accept timed registration)
+ #endif
+ ) {
+ aStatusCommand.setStatusCode(401); // seems to be hacked
+ return false;
+ }
+ #endif
+ // Check type of credentials
+ if (!aCredP) {
+ // Anonymous login attempt
+ authtype=auth_none;
+ }
+ else {
+ SmlMetInfMetInfPtr_t metaP;
+ if ((metaP=smlPCDataToMetInfP(aCredP->meta))!=NULL) {
+ // look for type
+ if (metaP->type) {
+ // get type (otherwise default to auth-basic)
+ const char *ty = smlPCDataToCharP(metaP->type);
+ sInt16 t;
+ if (StrToEnum(authTypeSyncMLNames,numAuthTypes,t,ty)) {
+ authtype=(TAuthTypes)t;
+ }
+ else {
+ // bad auth schema
+ authtype=auth_none; // disable checking below
+ aStatusCommand.setStatusCode(406); // unsupported optional feature (auth method)
+ aStatusCommand.addItemString(ty); // identify bad auth method
+ }
+ }
+ // look for format
+ // - get format
+ if (!smlPCDataToFormat(metaP->format,authfmt)) {
+ authtype=auth_none; // disable checking below
+ aStatusCommand.setStatusCode(415); // unsupported format
+ aStatusCommand.addItemString(smlPCDataToCharP(metaP->format)); // identify bad format
+ }
+ // - handle format and get auth data according to auth type
+ authdata = smlPCDataToCharP(aCredP->data); // get it as is
+ // Now check auth
+ switch (authtype) {
+ case auth_none:
+ // anonymous login attempt
+ // - no special measure needed
+ break;
+ case auth_basic:
+ // basic is always b64 encoded
+ #ifdef SYDEBUG
+ if (authfmt!=fmt_b64)
+ PDEBUGPRINTFX(DBG_ERROR,("Auth-basic has no <format>b64 spec --> assumed b64 anyway"));
+ #endif
+ authfmt=fmt_b64; // basic is ALWAYS b64
+ break;
+ case auth_md5:
+ // verify that we have the username in clear text for MD5 auth
+ if (!aUserName || *aUserName==0)
+ {
+ // username missing, probably strict (bad) SyncML 1.0 conformance,
+ // we need SyncML 1.0.1 corrected auth (MD5 w/o username is almost
+ // impossible to process)
+ authtype=auth_none; // disable checking below
+ aStatusCommand.setStatusCode(415); // unsupported format
+ ADDDEBUGITEM(aStatusCommand,"Missing clear-text username in Source LocName (SyncML 1.0.1)");
+ PDEBUGPRINTFX(DBG_ERROR,("Missing clear-text username in Source LocName (SyncML 1.0.1)"));
+ break;
+ }
+ // MD5 can come as binary (for WBXML)
+ if (getEncoding()==SML_WBXML) {
+ if (authfmt==fmt_chr || authfmt==fmt_bin) {
+ // assume unencoded MD5 digest (16 bytes binary), make b64
+ uInt32 l;
+ tobefreed=b64::encode((uInt8 *)authdata,16,&l);
+ authdata=tobefreed;
+ // now authdata is b64 as well
+ authfmt=fmt_b64;
+ }
+ }
+ break;
+ case numAuthTypes:
+ // invalid type?!
+ break;
+ }
+ } // if meta
+ }
+ #ifndef MINIMAL_CODE
+ // save user name for later reference
+ if (aUserName) fSyncUserName.assign(aUserName);
+ else fSyncUserName.erase();
+ #endif
+ // check credentials
+ if (authtype==auth_none) {
+ // check if we can login anonymously
+ // NOTE: do it anyway, even if !isAuthTypeAllowed() to make sure
+ // SessionLogin is called
+ authok=checkCredentials(aUserName,NULL,auth_none);
+ if (!authok || !isAuthTypeAllowed(auth_none)) {
+ // anonymous login not possible, request credentials
+ aStatusCommand.setStatusCode(407); // unauthorized, missing credentials
+ PDEBUGPRINTFX(DBG_PROTO,("Authorization required but none found in SyncHdr, sending status 407 + chal"));
+ // - add challenge
+ aStatusCommand.setChallenge(newSessionChallenge());
+ authok=false;
+ }
+ }
+ else {
+ // verify format (must be MD5 by now)
+ if (authfmt!=fmt_b64) {
+ aStatusCommand.setStatusCode(415); // unsupported format
+ }
+ else if (!authdata || !*authdata) {
+ aStatusCommand.setStatusCode(400); // missing data, malformed request
+ }
+ else {
+ // first check credentials
+ // NOTE: This must be done first, to force calling SessionLogin
+ // in all cases
+ authok=checkCredentials(aUserName,authdata,authtype);
+ // now check result
+ if (!isAuthTypeAllowed(authtype)) {
+ aStatusCommand.setStatusCode(401); // we need another auth type, tell client which one
+ authok=false; // anyway, reject
+ PDEBUGPRINTFX(DBG_ERROR,("Authorization failed (wrong type of creds), sending 401 + chal"));
+ }
+ else if (!authok) {
+ // auth type allowed, but auth itself not ok
+ aStatusCommand.setStatusCode(401); // unauthorized, bad credentials
+ PDEBUGPRINTFX(DBG_ERROR,("Authorization failed (invalid credentials) sending 401 + chal"));
+ }
+ if (!authok) {
+ // - add challenge
+ aStatusCommand.setChallenge(newSessionChallenge());
+ }
+ }
+ }
+ // free buffer if any
+ if (tobefreed) sysync_free(tobefreed);
+ // make sure we see what config was used in the log
+ DebugShowCfgInfo();
+ PDEBUGPRINTFX(DBG_HOT,(
+ "==== Authorisation %s with SyncML Engine Version %d.%d.%d.%d",
+ authok ? "successful" : "failed",
+ SYSYNC_VERSION_MAJOR,
+ SYSYNC_VERSION_MINOR,
+ SYSYNC_SUBVERSION,
+ SYSYNC_BUILDNUMBER
+ ));
+ #ifndef SYSYNC_CLIENT
+ PDEBUGPRINTFX(DBG_HOT,(
+ "==== SyncML URL used = '%s', username as sent by remote = '%s'",
+ fInitialLocalURI.c_str(),
+ fSyncUserName.c_str()
+ ));
+ #endif
+ // return result
+ return authok;
+} // TSyncSession::checkCredentials(SmlCredPtr_t...)
+
+
+// check credential string
+bool TSyncSession::checkCredentials(const char *aUserName, const char *aCred, TAuthTypes aAuthType)
+{
+ // now check auth
+ if (aAuthType==auth_basic) {
+ // basic auth allows extracting clear-text password
+ string user,password;
+ getAuthBasicUserPass(aCred,user,password);
+ #ifndef MINIMAL_CODE
+ fSyncUserName = user;
+ #endif
+ if (aUserName && !(user==aUserName)) {
+ // username does not match LocName (should, in SyncML 1.0.1 and later)
+ PDEBUGPRINTFX(DBG_PROTO,(
+ "basic_auth encoded username (%s) does not match LocName username (%s)",
+ user.c_str(),
+ aUserName ? aUserName : "[NULL Username]"
+ ));
+ }
+ // we have the password in clear text
+ return SessionLogin(user.c_str(), password.c_str(), sectyp_clearpass, fRemoteURI.c_str());
+ }
+ else if (aAuthType==auth_md5) {
+ // login user and device to the service
+ // - this is normally implemented in derived classes
+ return SessionLogin(aUserName, aCred, fSyncMLVersion>=syncml_vers_1_1 ? sectyp_md5_V11 : sectyp_md5_V10, fRemoteURI.c_str());
+ }
+ else if (aAuthType==auth_none) {
+ // even if we have no login, do a "login" with empty credentials
+ return SessionLogin("anonymous", NULL, sectyp_anonymous, fRemoteURI.c_str());
+ }
+ else {
+ return false; // unknown auth, is not ok
+ }
+} // TSyncSession::checkCredentials(const char *...)
+
+
+
+// Helper function:
+// check plain user / password / nonce combination
+// against given auth string.
+bool TSyncSession::checkAuthPlain(
+ const char *aUserName, const char *aPassWord, const char *aNonce, // given values
+ const char *aAuthString, TAuthSecretTypes aAuthStringType // check against this
+)
+{
+ string upw;
+
+ #ifdef SYDEBUG
+ PDEBUGPRINTFX(DBG_ADMIN,("Username = %s",aUserName));
+ DEBUGPRINTFX(DBG_USERDATA+DBG_EXOTIC,("Password = %s",aPassWord));
+ #endif
+ if (aAuthStringType==sectyp_anonymous) {
+ return (aPassWord==NULL || *aPassWord==0); // anonymous login ok if no password expected
+ }
+ else if (aAuthStringType==sectyp_clearpass) {
+ return (strcmp(aAuthString,aPassWord)==0); // login ok if password matches
+ }
+ else {
+ // must be MD5
+ // - concatenate user:password
+ upw = aUserName;
+ upw+=':';
+ upw.append(aPassWord);
+ // depends on method
+ if (aAuthStringType==sectyp_md5_V11) {
+ // V1.1 requires MD5b64-ing user/pw before adding nonce
+ MD5B64(upw.c_str(),upw.size(),upw);
+ }
+ // now check result
+ return checkMD5WithNonce(upw.c_str(),aNonce,aAuthString);
+ }
+ // unknown auth secret type
+ return false;
+} // TSyncSession::checkAuthPlain
+
+
+// Helper function:
+// check MD5B64(user:pw) / nonce combination
+// against given auth string.
+bool TSyncSession::checkAuthMD5(
+ const char *aUserName, const char *aMD5B64, const char *aNonce, // given values
+ const char *aAuthString, TAuthSecretTypes aAuthStringType // check against this
+)
+{
+
+ if (aAuthStringType==sectyp_md5_V11) {
+ // we have a V11 authstring, check it against our MD5B64(user:pw)
+ return checkMD5WithNonce(aMD5B64,aNonce,aAuthString);
+ }
+ else if (aAuthStringType==sectyp_clearpass) {
+ // we must generate the MD5B64(user:pw) from clear text
+ string myAuthString = aUserName;
+ myAuthString+=':';
+ myAuthString+=aAuthString;
+ MD5B64(myAuthString.c_str(),myAuthString.size(),myAuthString);
+ #ifdef SYDEBUG
+ PDEBUGPRINTFX(DBG_ADMIN,("MD5B64(user:pw) stored in local DB = %s",aMD5B64));
+ PDEBUGPRINTFX(DBG_ADMIN,("calculated MD5B64(remoteuser:remotepw) = %s",myAuthString.c_str()));
+ #endif
+ // then we can directly compare them
+ return myAuthString==aMD5B64;
+ }
+ else
+ return false; // we cannot auth V1.0 MD5 against MD5B64(user:password)
+} // checkAuthMD5
+
+
+// Helper function:
+// check V1.1 MD5 type auth against known md5userpass
+// (B64 encoded MD5 digest of user:password) and nonce
+// Note: This works only with V1.1-type credentials!!!!
+bool TSyncSession::checkMD5WithNonce(
+ const char *aStringBeforeNonce,
+ const char *aNonce,
+ const char *aMD5B64Creds
+)
+{
+ string pattern; // pattern to match with
+
+ // see if user/pw/nonce matches given MD5
+ // - add nonce to prepared string
+ // For V1.0 this is "user:password"
+ // For >=V1.1 this is MD5B64("user:password")
+ pattern = aStringBeforeNonce;
+ pattern+=':';
+ pattern.append(aNonce);
+ // - MD5 and B64 entire thing (again)
+ MD5B64(pattern.c_str(),pattern.size(),pattern);
+ #ifdef SYDEBUG
+ DEBUGPRINTFX(DBG_ADMIN ,("String before Nonce = %s",aStringBeforeNonce));
+ DEBUGPRINTFX(DBG_ADMIN ,("Nonce used = %s",aNonce));
+ PDEBUGPRINTFX(DBG_ADMIN,("Locally calculated MD5B64 = %s",pattern.c_str()));
+ PDEBUGPRINTFX(DBG_ADMIN,("Received MD5B64 from remote = %s",aMD5B64Creds));
+ #endif
+ // - now compare with given credentials
+ return strnncmp(aMD5B64Creds,pattern.c_str(),pattern.size())==0;
+} // TSyncSession::checkMD5WithNonce
+
+
+// helper: get user/password out of basic credential string, returns false if bad cred
+bool TSyncSession::getAuthBasicUserPass(const char *aBasicCreds, string &aUsername, string &aPassword)
+{
+ // - convert to user/pw string
+ uInt32 userpwlen;
+ uInt8 *userpw=b64::decode(aBasicCreds, 0, &userpwlen);
+ bool ok=false;
+ if (userpw) {
+ // adjust length if already null terminated
+ if (userpw[userpwlen-1]==0) userpwlen=strlen((char *)userpw);
+ // extract plain-text username first
+ const char *p=strchr((const char *)userpw,':');
+ if (p) {
+ // save user name
+ aUsername.assign((const char *)userpw,p-(const char *)userpw);
+ // save password
+ aPassword.assign(p+1);
+ ok=true;
+ }
+ }
+ sysync_free(userpw);
+ return ok;
+} // TSyncSession::getAuthBasicUserPass
+
+
+// check credential string (clear text pw, MD5, etc.)
+// This function is normally derived to provide checking of auth string
+// Notes:
+// - all auth requests are resolved using this function.
+// - For pre-SyncML 1.1 MD5 auth, credentials are checkable only
+// against plain text passwords. It's up to the derived class to decide if
+// this is possible or not.
+bool TSyncSession::SessionLogin(
+ const char *aUserName,
+ const char *aAuthString,
+ TAuthSecretTypes aAuthStringType,
+ const char *aDeviceID
+)
+{
+ string nonce;
+ // get config for session
+ TSessionConfig *scP = getSessionConfig();
+ // anonymous is always ok (because checking if anonymous allowed is done already)
+ if (aAuthStringType==sectyp_anonymous) return true; // ok
+ // check simple auth
+ if (scP->fSimpleAuthUser.empty()) return false; // no simple auth
+ // check user name
+ if (strucmp(scP->fSimpleAuthUser.c_str(),aUserName)!=0) return false; // wrong user name
+ // now check auth string
+ if (aAuthStringType==sectyp_md5_V10 || aAuthStringType==sectyp_md5_V11) {
+ // we need a nonce
+ getAuthNonce(aDeviceID,nonce);
+ }
+ // now check
+ return checkAuthPlain(
+ scP->fSimpleAuthUser.c_str(),
+ scP->fSimpleAuthPassword.c_str(),
+ nonce.c_str(),
+ aAuthString,
+ aAuthStringType
+ );
+} // TSyncSession::SessionLogin
+
+
+
+// check remote devinf to detect special behaviour needed for some clients (or servers). Base class
+// does not do anything on server level (configured rules are handled at session level)
+// - NOTE: aDevInfP can be NULL to specify that remote device has not sent any devInf at all
+// and this is a blind sync attempt (so best-guess workaround settings might apply)
+localstatus TSyncSession::checkRemoteSpecifics(SmlDevInfDevInfPtr_t aDevInfP)
+{
+ #if defined(SYSER_REGISTRATION) || !defined(NO_REMOTE_RULES)
+ localstatus sta = LOCERR_OK;
+ #endif
+
+ // check hard-coded restrictions
+ if (aDevInfP && (
+ false
+ #ifdef REMOTE_RESTR_DEVID
+ || strwildcmp(smlPCDataToCharP(aDevInfP->devid),REMOTE_RESTR_DEVID)!=0
+ #endif
+ #ifdef REMOTE_RESTR_MAN
+ || strwildcmp(smlPCDataToCharP(aDevInfP->man),REMOTE_RESTR_MAN)!=0
+ #endif
+ #ifdef REMOTE_RESTR_MOD
+ || strwildcmp(smlPCDataToCharP(aDevInfP->mod),REMOTE_RESTR_MOD)!=0
+ #endif
+ #ifdef REMOTE_RESTR_OEM
+ || strwildcmp(smlPCDataToCharP(aDevInfP->oem),REMOTE_RESTR_OEM)!=0
+ #endif
+ #ifdef REMOTE_RESTR_URI
+ || strwildcmp(fRemoteURI.c_str(),REMOTE_RESTR_URI)!=0
+ #endif
+ )) {
+ PDEBUGPRINTFX(DBG_ERROR,("Software not allowed syncing with this remote party"));
+ AbortSession(403,true);
+ return 403;
+ }
+
+ // check license restrictions
+ #ifdef SYSER_REGISTRATION
+ sInt16 daysleft;
+ string s;
+
+ // - get restriction string from licensed info
+ sta = getSyncAppBase()->getAppEnableInfo(daysleft, NULL, &s);
+ string restrid,restrval;
+ const char *p = s.c_str(); // start of license info string
+ while (sta==LOCERR_OK && (p=getSyncAppBase()->getLicenseRestriction(p,restrid,restrval))!=NULL) {
+ const char *restr=NULL;
+ if (restrid=="u") { // URL
+ // we can check the remote URL without having devinf
+ restr=fRemoteURI.c_str();
+ }
+ else {
+ if (restrid.size()==1) {
+ // there is a restriction
+ if (!aDevInfP) {
+ // we cannot check these restrictions without having a devInf
+ sta = LOCERR_BADREG;
+ PDEBUGPRINTFX(DBG_ERROR,("License restriction needs devInf from remote but none found -> block sync"));
+ break;
+ }
+ // we have devinf, we can check it
+ switch (restrid[0]) {
+ case 'i' : restr=smlPCDataToCharP(aDevInfP->devid); break;
+ case 'm' : restr=smlPCDataToCharP(aDevInfP->man); break;
+ case 't' : restr=smlPCDataToCharP(aDevInfP->mod); break;
+ case 'o' : restr=smlPCDataToCharP(aDevInfP->oem); break;
+ }
+ }
+ }
+ // - now compare with wildcards allowed if we have anything to compare
+ if (restr) sta = strwildcmp(restr,restrval.c_str())==0 ? LOCERR_OK : LOCERR_BADREG; // service unavailable
+ }
+ // - abort if not ok
+ if (sta!=LOCERR_OK) {
+ PDEBUGPRINTFX(DBG_ERROR,("License does not allow syncing with this remote party, status=%hd",sta));
+ AbortSession(403,true,sta);
+ return sta;
+ }
+ #endif // SYSER_REGISTRATION
+ // check remote rules
+ #ifndef NO_REMOTE_RULES
+ PDEBUGBLOCKDESC("RemoteRules","Checking for remote rules");
+ // get config for session
+ TSessionConfig *scP = getSessionConfig();
+ // look if we have a matching rule for this device
+ TRemoteRulesList::iterator pos;
+ for(pos=scP->fRemoteRulesList.begin();pos!=scP->fRemoteRulesList.end();pos++) {
+ // compare with devinf (or test for default-rule if aDevInfP is NULL
+ if (
+ ((*pos)->fManufacturer.empty() || aDevInfP && strwildcmp(smlPCDataToCharP(aDevInfP->man),(*pos)->fManufacturer.c_str())==0) &&
+ ((*pos)->fModel.empty() || aDevInfP && strwildcmp(smlPCDataToCharP(aDevInfP->mod),(*pos)->fModel.c_str())==0) &&
+ ((*pos)->fOem.empty() || aDevInfP && strwildcmp(smlPCDataToCharP(aDevInfP->oem),(*pos)->fOem.c_str())==0) &&
+ ((*pos)->fFirmwareVers.empty() || aDevInfP && (*pos)->fFirmwareVers==smlPCDataToCharP(aDevInfP->fwv)) &&
+ ((*pos)->fSoftwareVers.empty() || aDevInfP && (*pos)->fSoftwareVers==smlPCDataToCharP(aDevInfP->swv)) &&
+ ((*pos)->fHardwareVers.empty() || aDevInfP && (*pos)->fHardwareVers==smlPCDataToCharP(aDevInfP->hwv)) &&
+ ((*pos)->fDevId.empty() || aDevInfP && (*pos)->fDevId==smlPCDataToCharP(aDevInfP->devid)) &&
+ ((*pos)->fDevTyp.empty() || aDevInfP && (*pos)->fDevTyp==smlPCDataToCharP(aDevInfP->devtyp))
+ ) {
+ // found, apply rules
+ TRemoteRuleConfig *ruleP = *pos;
+ PDEBUGPRINTFX(DBG_HOT,("Found special rule '%s' for remote party, applying",ruleP->getName()));
+ // set options
+ // - only device specific
+ fAppliedRemoteRuleP = ruleP; // save pointer to applied rule
+ // - apply options that have a value
+ if (ruleP->fLimitedFieldLengths>=0) fLimitedRemoteFieldLengths = ruleP->fLimitedFieldLengths;
+ if (ruleP->fDontSendEmptyProperties>=0) fDontSendEmptyProperties = ruleP->fDontSendEmptyProperties;
+ if (ruleP->fDoQuote8BitContent>=0) fDoQuote8BitContent = ruleP->fDoQuote8BitContent;
+ if (ruleP->fDoNotFoldContent>=0) fDoNotFoldContent = ruleP->fDoNotFoldContent;
+ if (ruleP->fNoReplaceInSlowsync>=0) fNoReplaceInSlowsync = ruleP->fNoReplaceInSlowsync;
+ if (ruleP->fTreatRemoteTimeAsLocal>=0) fTreatRemoteTimeAsLocal = ruleP->fTreatRemoteTimeAsLocal;
+ if (ruleP->fTreatRemoteTimeAsUTC>=0) fTreatRemoteTimeAsUTC = ruleP->fTreatRemoteTimeAsUTC;
+ if (ruleP->fVCal10EnddatesSameDay>=0) fVCal10EnddatesSameDay = ruleP->fVCal10EnddatesSameDay;
+ if (ruleP->fIgnoreDevInfMaxSize>=0) fIgnoreDevInfMaxSize = ruleP->fIgnoreDevInfMaxSize;
+ if (ruleP->fIgnoreCTCap>=0) fIgnoreCTCap = ruleP->fIgnoreCTCap;
+ if (ruleP->fDSPathInDevInf>=0) fDSPathInDevInf = ruleP->fDSPathInDevInf;
+ if (ruleP->fDSCgiInDevInf>=0) fDSCgiInDevInf = ruleP->fDSCgiInDevInf;
+ if (ruleP->fUpdateClientDuringSlowsync>=0) fUpdateClientDuringSlowsync = ruleP->fUpdateClientDuringSlowsync;
+ if (ruleP->fUpdateServerDuringSlowsync>=0) fUpdateServerDuringSlowsync = ruleP->fUpdateServerDuringSlowsync;
+ if (ruleP->fAllowMessageRetries>=0) fAllowMessageRetries = ruleP->fAllowMessageRetries;
+ if (ruleP->fStrictExecOrdering>=0) fStrictExecOrdering = ruleP->fStrictExecOrdering;
+ if (ruleP->fTreatCopyAsAdd>=0) fTreatCopyAsAdd = ruleP->fTreatCopyAsAdd;
+ if (ruleP->fCompleteFromClientOnly>=0) fCompleteFromClientOnly = ruleP->fCompleteFromClientOnly;
+ if (ruleP->fRequestMaxTime>=0) fRequestMaxTime = ruleP->fRequestMaxTime;
+ if (ruleP->fDefaultOutCharset!=chs_unknown) fDefaultOutCharset = ruleP->fDefaultOutCharset;
+ // - eventually override decisions that are otherwise made by session
+ // Note: this is not a single option because we had this before rule options were tristates.
+ if (ruleP->fForceUTC>0) fRemoteCanHandleUTC=true;
+ if (ruleP->fForceLocaltime>0) fRemoteCanHandleUTC=false;
+ // - install rule script
+ #ifdef SCRIPT_SUPPORT
+ if (!ruleP->fRuleScriptTemplate.empty()) fRuleScript = (*pos)->fRuleScriptTemplate;
+ #endif
+ // - descriptive name for the device (for log)
+ #ifndef MINIMAL_CODE
+ if (!ruleP->fRemoteDescName.empty()) fRemoteDescName = (*pos)->fRemoteDescName;
+ #endif
+ // - test for rejection
+ if (ruleP->fRejectStatusCode!=DONT_REJECT) {
+ // reject operation with this device
+ sta = (*pos)->fRejectStatusCode;
+ PDEBUGPRINTFX(DBG_ERROR,("remote party rejected by configured 'remoterule', status=%hd",sta));
+ AbortSession(sta,true);
+ return sta;
+ }
+ // done only if this rule is final
+ if (ruleP->fFinalRule) break;
+ }
+ } // for
+ // - resolve and execute rule script
+ #ifdef SCRIPT_SUPPORT
+ if (!fRuleScript.empty()) {
+ // resolve variable references
+ TScriptContext::linkIntoContext(fRuleScript,fSessionScriptContextP,this);
+ // execute now
+ PDEBUGPRINTFX(DBG_HOT,("Executing rulescript."));
+ TScriptContext::execute(
+ fSessionScriptContextP,
+ fRuleScript,
+ NULL, // context's function table
+ NULL // datastore pointer needed for context
+ );
+ }
+ #endif // SCRIPT_SUPPORT
+ PDEBUGENDBLOCK("RemoteRules");
+ #endif // NO_REMOTE_RULES
+ // Final adjustments
+ #ifndef NO_REMOTE_RULES
+ if (!fAppliedRemoteRuleP)
+ #endif
+ {
+ // no remote rule (none found or mechanism excluded by NO_REMOTE_RULES)
+ if (!aDevInfP) {
+ // no devinf -> blind sync attempt: apply best-guess workaround settings
+ // Note that a blind sync attempt means that the remote party is at least partly non-compliant, as we always request a devInf!
+ PDEBUGPRINTFX(DBG_ERROR,("No remote information available -> applying best-guess workaround behaviour options"));
+ // set device description
+ fRemoteDescName = fRemoteName.empty() ? "[unknown remote]" : fRemoteName.c_str();
+ fRemoteDescName += " (no devInf)";
+ // switch on legacy behaviour (conservative preferred types)
+ fLegacyMode = true;
+ #ifdef SYSYNC_CLIENT
+ // Client case
+ fRemoteCanHandleUTC = true; // Assume server can handle UTC (it is very improbable a server can't)
+ #else
+ // Server case
+ fRemoteCanHandleUTC = fSyncMLVersion==syncml_vers_1_0 ? true : false; // Assume client cannot handle UTC (it is likely a client can't, or at least can't properly, so localtime is safer)
+ fLimitedRemoteFieldLengths = true; // assume limited client field length (almost all clients have limited length)
+ #endif
+ }
+ }
+ // show summary
+ PDEBUGPRINTFX(DBG_HOT+DBG_REMOTEINFO,("Summary of all behaviour options (eventually set by remote rule)"));
+ PDEBUGPRINTFX(DBG_HOT+DBG_REMOTEINFO,("- Remote Description : %s",fRemoteDescName.c_str()));
+ PDEBUGPRINTFX(DBG_HOT+DBG_REMOTEINFO,("- Legacy mode : %s",boolString(fLegacyMode)));
+ PDEBUGPRINTFX(DBG_HOT+DBG_REMOTEINFO,("- Limited Field Lengths : %s",boolString(fLimitedRemoteFieldLengths)));
+ PDEBUGPRINTFX(DBG_HOT+DBG_REMOTEINFO,("- Do not send empty props : %s",boolString(fDontSendEmptyProperties)));
+ PDEBUGPRINTFX(DBG_HOT+DBG_REMOTEINFO,("- Quote 8bit content : %s",boolString(fDoQuote8BitContent)));
+ PDEBUGPRINTFX(DBG_HOT+DBG_REMOTEINFO,("- Prevent Content Folding : %s",boolString(fDoNotFoldContent)));
+ PDEBUGPRINTFX(DBG_HOT+DBG_REMOTEINFO,("- No replace in slowsync : %s",boolString(fNoReplaceInSlowsync)));
+ PDEBUGPRINTFX(DBG_HOT+DBG_REMOTEINFO,("- Treat remote TZ as local : %s",boolString(fTreatRemoteTimeAsLocal)));
+ PDEBUGPRINTFX(DBG_HOT+DBG_REMOTEINFO,("- Treat remote TZ as UTC : %s",boolString(fTreatRemoteTimeAsUTC)));
+ PDEBUGPRINTFX(DBG_HOT+DBG_REMOTEINFO,("- Use 23:59:59 end dates : %s",boolString(fVCal10EnddatesSameDay)));
+ PDEBUGPRINTFX(DBG_HOT+DBG_REMOTEINFO,("- Ignore field maxSize : %s",boolString(fIgnoreDevInfMaxSize)));
+ PDEBUGPRINTFX(DBG_HOT+DBG_REMOTEINFO,("- Ignore CTCap : %s",boolString(fIgnoreCTCap)));
+ PDEBUGPRINTFX(DBG_HOT+DBG_REMOTEINFO,("- send DS path in devInf : %s",boolString(fDSPathInDevInf)));
+ PDEBUGPRINTFX(DBG_HOT+DBG_REMOTEINFO,("- send DS CGI in devInf : %s",boolString(fDSCgiInDevInf)));
+ PDEBUGPRINTFX(DBG_HOT+DBG_REMOTEINFO,("- Update Client in slowsync : %s",boolString(fUpdateClientDuringSlowsync)));
+ PDEBUGPRINTFX(DBG_HOT+DBG_REMOTEINFO,("- Update Server in slowsync : %s",boolString(fUpdateServerDuringSlowsync)));
+ PDEBUGPRINTFX(DBG_HOT+DBG_REMOTEINFO,("- Allow message retries : %s",boolString(fAllowMessageRetries)));
+ PDEBUGPRINTFX(DBG_HOT+DBG_REMOTEINFO,("- Strict SyncML exec order : %s",boolString(fStrictExecOrdering)));
+ PDEBUGPRINTFX(DBG_HOT+DBG_REMOTEINFO,("- Treat copy like add : %s",boolString(fTreatCopyAsAdd)));
+ PDEBUGPRINTFX(DBG_HOT+DBG_REMOTEINFO,("- Complete From-Client-Only : %s",boolString(fCompleteFromClientOnly)));
+ PDEBUGPRINTFX(DBG_HOT+DBG_REMOTEINFO,("- Remote can handle UTC : %s",boolString(fRemoteCanHandleUTC)));
+ PDEBUGPRINTFX(DBG_HOT+DBG_REMOTEINFO,("- Max Request time [sec] : %ld",static_cast<long>(fRequestMaxTime)));
+ PDEBUGPRINTFX(DBG_HOT+DBG_REMOTEINFO,("- Content output charset : %s",MIMECharSetNames[fDefaultOutCharset]));
+ // done
+ return LOCERR_OK;
+} // TSyncSession::checkRemoteSpecifics
+
+
+
+// access to config
+TSessionConfig *TSyncSession::getSessionConfig(void)
+{
+ TSessionConfig *scP;
+ GET_CASTED_PTR(scP,TSessionConfig,getSyncAppBase()->getRootConfig()->fAgentConfigP,DEBUGTEXT("no TSessionConfig","sss1"));
+ return scP;
+} // TSyncSession::getSessionConfig
+
+
+
+// process a Map command in context of session
+bool TSyncSession::processMapCommand(
+ SmlMapPtr_t aMapCommandP, // the map command contents
+ TStatusCommand &aStatusCommand, // pre-set 200 status, can be modified in case of errors
+ bool &aQueueForLater
+)
+{
+ // if not overridden, we cannot process Map
+ aStatusCommand.setStatusCode(403);
+ ADDDEBUGITEM(aStatusCommand,"Map command not allowed in this context");
+ return false; // failed
+} // TSyncSession::processMapCommand
+
+
+// called to issue custom get and put commands
+// may issue custom get and put commands
+void TSyncSession::issueCustomGetPut(bool aGotDevInf, bool aSentDevInf)
+{
+ #ifdef SCRIPT_SUPPORT
+ // call script that might issue GETs and PUTs
+ // - set up context
+ TGetPutResultFuncContext ctx;
+ ctx.isPut=false;
+ ctx.canIssue=true;
+ ctx.statuscode=0;
+ ctx.itemURI.erase();
+ ctx.itemData.erase();
+ ctx.metaType.erase();
+ // - execute
+ TScriptContext::execute(
+ fSessionScriptContextP,
+ getSessionConfig()->fCustomGetPutScript,
+ &GetPutResultFuncTable,
+ &ctx // caller context
+ );
+ #endif
+} // TSyncSession::issueCustomGetPut
+
+
+// called to issue custom put commands at end of session
+// may issue custom put commands (gets don't make sense at end of a session)
+void TSyncSession::issueCustomEndPut(void)
+{
+ #ifdef SCRIPT_SUPPORT
+ // call script that might issue GETs and PUTs
+ // - set up context
+ TGetPutResultFuncContext ctx;
+ ctx.isPut=false;
+ ctx.canIssue=true;
+ ctx.statuscode=0;
+ ctx.itemURI.erase();
+ ctx.itemData.erase();
+ ctx.metaType.erase();
+ // - execute
+ TScriptContext::execute(
+ fSessionScriptContextP,
+ getSessionConfig()->fCustomEndPutScript,
+ &GetPutResultFuncTable,
+ &ctx // caller context
+ );
+ #endif
+} // TSyncSession::issueCustomEndPut
+
+
+
+
+
+// called to process unknown get item, may return a Results command. Must set status to non-404 if get could be served
+// (may be overridden by descendants, only called if no descendant can handle an item)
+TResultsCommand *TSyncSession::processGetItem(const char *aLocUri, TGetCommand *aGetCommandP, SmlItemPtr_t aGetItemP, TStatusCommand &aStatusCommand)
+{
+ TResultsCommand *resultsCmdP = NULL;
+ #ifdef SCRIPT_SUPPORT
+ // first check if script handles it
+ // - set up context
+ TGetPutResultFuncContext ctx;
+ ctx.isPut=false;
+ ctx.canIssue=false;
+ ctx.statuscode=aStatusCommand.getStatusCode();
+ ctx.itemURI=aLocUri;
+ ctx.itemData.erase();
+ // - get meta type of item, if any
+ AssignString(ctx.metaType,smlMetaTypeToCharP(smlPCDataToMetInfP(aGetItemP->meta)));
+ if (ctx.metaType.empty()) {
+ // none in item, get from command
+ AssignString(ctx.metaType,smlMetaTypeToCharP(smlPCDataToMetInfP(aGetCommandP->getMeta())));
+ }
+ // - execute
+ bool hasResult =
+ TScriptContext::executeTest(
+ false, // do not assume script handles the GET
+ fSessionScriptContextP,
+ getSessionConfig()->fCustomGetHandlerScript,
+ &GetPutResultFuncTable,
+ &ctx // caller context
+ );
+ // - update status, anyway
+ aStatusCommand.setStatusCode(ctx.statuscode);
+ // - create result, if script decides so
+ if (hasResult) {
+ // script returns true, so it has handled the GET command
+ // - create a result command (%%% currently GET command itself does not carry src/targ URIs, only item does)
+ resultsCmdP = new TResultsCommand(this,aGetCommandP,NULL,NULL);
+ // - create data item
+ SmlItemPtr_t resItemP = newItem();
+ // - source is get item's URI reflected (if not changed by script)
+ resItemP->source=newLocation(ctx.itemURI.c_str());
+ // - data is just string
+ resItemP->data = newPCDataString(ctx.itemData);
+ // - add item to command
+ resultsCmdP->addItem(resItemP);
+ // - set result command meta if not empty string
+ resultsCmdP->setMeta(newMetaType(ctx.metaType.c_str()));
+ // get item handled, return
+ return resultsCmdP;
+ }
+ #endif
+ // look for ./devinf10 special case
+ if (strucmp(aLocUri,SyncMLDevInfNames[getSyncMLVersion()])==0) {
+ // status is ok
+ aStatusCommand.setStatusCode(200);
+ // prepare a devinf10 <Result>
+ resultsCmdP = new TDevInfResultsCommand(this,aGetCommandP);
+ }
+ return resultsCmdP;
+} // TSyncSession::processGetItem
+
+
+// - put and results command processing
+// (may be overridden by descendants, only called if no descendant can handle an item)
+void TSyncSession::processPutResultItem(bool aIsPut, const char *aLocUri, TSmlCommand *aPutResultsCommandP, SmlItemPtr_t aPutResultsItemP, TStatusCommand &aStatusCommand)
+{
+ localstatus sta = aStatusCommand.getStatusCode();
+ #ifdef SCRIPT_SUPPORT
+ // first check if script handles it
+ // - set up context
+ TGetPutResultFuncContext ctx;
+ ctx.isPut=aIsPut;
+ ctx.canIssue=false;
+ ctx.statuscode=sta;
+ ctx.itemURI=aLocUri;
+ // - get data of item, if any
+ smlPCDataToStringObj(aPutResultsItemP->data,ctx.itemData);
+ // - get meta type of item, if any
+ AssignString(ctx.metaType,smlMetaTypeToCharP(smlPCDataToMetInfP(aPutResultsItemP->meta)));
+ if (ctx.metaType.empty()) {
+ // none in item, get from command
+ AssignString(ctx.metaType,smlMetaTypeToCharP(smlPCDataToMetInfP(aPutResultsCommandP->getMeta())));
+ }
+ // - execute
+ bool hasProcessed =
+ TScriptContext::executeTest(
+ false, // do not assume script handles the PUT or RESULT
+ fSessionScriptContextP,
+ getSessionConfig()->fCustomPutResultHandlerScript,
+ &GetPutResultFuncTable,
+ &ctx // caller context
+ );
+ // update status code
+ sta=ctx.statuscode;
+ if (sta!=LOCERR_OK)
+ aStatusCommand.setStatusCode(syncmlError(sta));
+ // if processed, return now
+ if (hasProcessed)
+ return;
+ #endif
+ // check for ./devinfXX
+ if (strucmp(aLocUri,SyncMLDevInfNames[getSyncMLVersion()])==0) {
+ // remote is sending DevInf, receive it
+ SmlDevInfDevInfPtr_t devinfP = smlPCDataToDevInfP(aPutResultsItemP->data);
+ // save received devinf (if database supports it)
+ saveRemoteDevInf(getRemoteURI(),devinfP);
+ // analyze
+ aStatusCommand.setStatusCode(200); // assume ok
+ sta=analyzeRemoteDevInf(devinfP);
+ if (sta!=LOCERR_OK)
+ aStatusCommand.setStatusCode(syncmlError(sta));
+ }
+ else {
+ // unknown
+ PDEBUGPRINTFX(DBG_ERROR,(
+ "Unknown %s-command with URI=%s received, returning default status=%hd",
+ aIsPut ? "PUT" : "RESULTS",
+ aLocUri,
+ sta
+ ));
+ }
+} // TSyncSession::processPutResultItem
+
+
+
+// process an alert item in context of session
+// Most handling takes place in derived classes,
+// this base class only implements basic stuff
+// - returns command to be issued after issuing status, NULL if none
+TSmlCommand *TSyncSession::processAlertItem(
+ uInt16 aAlertCode, // alert code
+ SmlItemPtr_t aItemP, // alert item to be processed (as one alert can have multiple items)
+ SmlCredPtr_t aCredP, // alert cred element, if any
+ TStatusCommand &aStatusCommand, // pre-set 200 status, can be modified in case of errors
+ TLocalEngineDS *&aLocalDataStoreP // receives datastore pointer, if alert affects a datastore
+)
+{
+ // no alert response command by default
+ TSmlCommand *alertresponsecmdP=NULL;
+ TLocalEngineDS *datastoreP;
+ string optionsCGI,identifyingTargetURI;
+
+ // dispatch numeric alerts
+ switch (aAlertCode) {
+ // sync alerts
+ case 200:
+ case 201:
+ case 202:
+ case 203:
+ case 204:
+ case 205:
+ // Sync resume alert
+ case 225:
+ // Synchronisation initialisation alerts
+ // - test if context is ok
+ if (fIncomingState!=psta_init && fIncomingState!=psta_initsync) {
+ // Sync alert only allowed in init package or combined init/sync
+ PDEBUGPRINTFX(DBG_ERROR,(
+ "Sync Alert not allowed with incoming package state='%s'",
+ PackageStateNames[fIncomingState]
+ ));
+ aStatusCommand.setStatusCode(403); // forbidden
+ ADDDEBUGITEM(aStatusCommand,"Sync Alert only allowed in init package");
+ return NULL; // no alert sent back
+ }
+ // find requested database by URI
+ datastoreP = findLocalDataStoreByURI(
+ smlSrcTargLocURIToCharP(aItemP->target), // target as sent from remote
+ &optionsCGI, // options, if any
+ &identifyingTargetURI // identifying part of URI (CGI removed)
+ );
+ if (!datastoreP) {
+ // no such local datastore
+ aStatusCommand.setStatusCode(404); // not found
+ }
+ else {
+ // save alerted datastore pointer (will be returned to caller, which is TAlertCommand)
+ aLocalDataStoreP=datastoreP;
+ // get anchors
+ const char *nextRemoteAnchor = smlMetaNextAnchorToCharP(smlPCDataToMetInfP(aItemP->meta));
+ if (nextRemoteAnchor==NULL) nextRemoteAnchor=""; // some remotes may send NO anchor
+ const char *lastRemoteAnchor = smlMetaLastAnchorToCharP(smlPCDataToMetInfP(aItemP->meta));
+ if (lastRemoteAnchor==NULL) lastRemoteAnchor=""; // some remotes may send NO anchor
+ // get URIs
+ const char *targetURI = smlSrcTargLocURIToCharP(aItemP->target);
+ const char *sourceURI = smlSrcTargLocURIToCharP(aItemP->source);
+ // get Filter
+ SmlFilterPtr_t targetFilter = aItemP->target ? aItemP->target->filter : NULL;
+ // alert datastore of requested sync
+ // - let datastore process alert and generate additional alert if needed
+ // NOTE: this might generate a PUT command if remote needs to see our
+ // devInf (config changed since last sync)
+ alertresponsecmdP=datastoreP->engProcessSyncAlert(
+ NULL, // not as subdatastore
+ aAlertCode, // the alert code
+ lastRemoteAnchor, // last anchor of client
+ nextRemoteAnchor, // next anchor of client
+ targetURI, // target as sent from remote
+ identifyingTargetURI.c_str(), // identifying part of URI (relative, options removed)
+ optionsCGI.c_str(), // extracted options (e.g. filtering) CGI
+ targetFilter, // DS 1.2 filter, if any (can be NULL if none)
+ sourceURI, // source URI
+ aStatusCommand // status that might be modified
+ );
+ // echo next anchor sent with item back in status
+ // %%% specs say that only next anchor must be echoed, SCTS echoes both
+ SmlItemPtr_t aItemP = newItem(); // empty item
+ // NOTE: anchor is MetInf, but is echoed in DATA part of item, not META!
+ aItemP->data = newMetaAnchor(nextRemoteAnchor,NULL); // only next (like specs)
+ aStatusCommand.addItem(aItemP); // add it to status
+ }
+ break;
+ case 224 :
+ // Suspend alert
+ SuspendSession(514);
+ break;
+ case 100 :
+ // DISPLAY
+ PDEBUGPRINTFX(DBG_HOT,(
+ "---------------- DISPLAY ALERT (100): %s",
+ smlPCDataToCharP(aItemP->data)
+ ));
+ // show it on the console
+ CONSOLEPRINTF((
+ "***** Message from Remote: %s",
+ smlPCDataToCharP(aItemP->data)
+ ));
+ // callback to allow GUI clients to display the message
+ if (!OBJ_PROGRESS_EVENT(getSyncAppBase(),pev_display100,NULL,uIntPtr(smlPCDataToCharP(aItemP->data)),0,0)) {
+ // user answered no to our question "continue?"
+ aStatusCommand.setStatusCode(514); // cancelled
+ // Do NOT abort the session, so give the server a chance to do someting more sensible based on the 514 status.
+ aStatusCommand.addItemString("User abort in response to Alert 100 message");
+ PDEBUGPRINTFX(DBG_ERROR,("User abort after seeing Alert 100 message: %s",smlPCDataToCharP(aItemP->data)));
+ }
+ break;
+ case 223:
+ // Chunking error: missing end of chunk
+ aStatusCommand.setStatusCode(223);
+ aStatusCommand.addItemString("Missing end of chunk");
+ PDEBUGPRINTFX(DBG_ERROR,(
+ "Warning: Alert Code 223 -> Missing end of chunk for item localid='%s', remoteid='%s'",
+ smlSrcTargLocURIToCharP(aItemP->target),
+ smlSrcTargLocURIToCharP(aItemP->source)
+ ));
+ break;
+ default :
+ // unknown alert code
+ aStatusCommand.setStatusCode(406);
+ aStatusCommand.addItemString("Unimplemented Alert Code");
+ PDEBUGPRINTFX(DBG_ERROR,("Unimplemented Alert Code %hd -> Status 406",aAlertCode));
+ break;
+ } // switch fAlertCode
+ // return command generated (or NULL if none)
+ return alertresponsecmdP;
+} // TSyncSession::processAlertItem
+
+
+
+#ifdef SYDEBUG
+ #define XML_TRANSLATION_ENABLED
+#else
+ #undef XML_TRANSLATION_ENABLED
+#endif
+
+#ifdef SYDEBUG
+
+void TSyncSession::XMLTranslationIncomingStart(void)
+{
+ // start translation instances
+ #ifdef XML_TRANSLATION_ENABLED
+ if (fXMLtranslate && !getRootConfig()->fDebugConfig.fDebugInfoPath.empty()) {
+ DEBUGPRINTFX(DBG_EXOTIC,("Initializing incoming XML translation instance"))
+ if (!getSyncAppBase()->newSmlInstance(
+ SML_XML,
+ getRootConfig()->fLocalMaxMsgSize * 3, // XML should not be more than 3 times larger than WBXML
+ fIncomingXMLInstance
+ )) {
+ // if instance cannot be created, turn off XML translation to avoid crashes
+ fXMLtranslate=false;
+ PDEBUGPRINTFX(DBG_ERROR,("XML translation disabled because of lacking memory"))
+ }
+ }
+ else
+ fXMLtranslate=false;
+ #endif
+} // TSyncSession::XMLTranslationIncomingStart
+
+
+void TSyncSession::XMLTranslationOutgoingStart(void)
+{
+ #ifdef XML_TRANSLATION_ENABLED
+ // start translation instances
+ if (fXMLtranslate && !getRootConfig()->fDebugConfig.fDebugInfoPath.empty()) {
+ DEBUGPRINTFX(DBG_EXOTIC,("Initializing outgoing XML translation instance"))
+ if (!getSyncAppBase()->newSmlInstance(
+ SML_XML,
+ getRootConfig()->fLocalMaxMsgSize * 3, // XML should not be more than 3 times larger than WBXML
+ fOutgoingXMLInstance
+ )) {
+ // if instance cannot be created, turn off XML translation to avoid crashes
+ fXMLtranslate=false;
+ PDEBUGPRINTFX(DBG_ERROR,("XML translation disabled because of lacking memory"))
+ }
+ }
+ else
+ fXMLtranslate=false;
+ #endif
+} // TSyncSession::XMLTranslationOutgoingStart
+
+
+/// @todo
+/// rewrite this to use a TDbgOut object to write stuff
+// finish and output XML translation of incoming traffic
+void TSyncSession::XMLTranslationIncomingEnd(void)
+{
+ #ifdef XML_TRANSLATION_ENABLED
+ if (fIncomingXMLInstance && fXMLtranslate) {
+ // write XML translation of input and output to files
+ DEBUGPRINTFX(DBG_EXOTIC,("XML translation enabled..."))
+ MemPtr_t XMLtext;
+ MemSize_t XMLsize;
+ string fname;
+ // - incoming
+ // - get XML
+ DEBUGPRINTFX(DBG_EXOTIC,("- Writing incoming XML translation"))
+ XMLtext=NULL; XMLsize=0;
+ if (smlLockReadBuffer(fIncomingXMLInstance,&XMLtext,&XMLsize)==SML_ERR_OK) {
+ // save to file
+ TDbgOut *dbgOutP = getSyncAppBase()->newDbgOutputter(false);
+ if (dbgOutP) {
+ // create base file name (trm = translated message)
+ string dumpfilename;
+ StringObjPrintf(dumpfilename,
+ "%s_trm%03ld_%03ld_incoming",
+ getDbgLogger()->getDebugPath(), // path + session log base name
+ (long)getLastIncomingMsgID(),
+ (long)++fDumpCount // to make sure it is unique even in case of retries
+ );
+ // open file in raw mode
+ if (dbgOutP->openDbg(
+ dumpfilename.c_str(),
+ ".xml",
+ dbgflush_none,
+ false, // append to existing if any
+ true // raw mode
+ )) {
+ // write out the entire message
+ dbgOutP->putRawData(XMLtext, XMLsize);
+ // close the file
+ dbgOutP->closeDbg();
+ // add a link into the session file to immediately get the file
+ PDEBUGPRINTFX(DBG_HOT+DBG_PROTO,(
+ "Incoming %sXML message msgID=%ld &html;<a href=\"%s_trm%03ld_%03ld_incoming.xml\" target=\"_blank\">&html;saved as XML translation&html;</a>&html;",
+ getEncoding()==SML_XML ? "" : "WB",
+ (long)getLastIncomingMsgID(),
+ getDbgLogger()->getDebugFilename(), // session log base name
+ (long)getLastIncomingMsgID(),
+ (long)fDumpCount
+ ));
+ }
+ else {
+ PDEBUGPRINTFX(DBG_ERROR,("Cannot write <xmltranslate> file"));
+ }
+ delete dbgOutP;
+ }
+ }
+ }
+ if (fIncomingXMLInstance) {
+ // finally free instance
+ getSyncAppBase()->freeSmlInstance(fIncomingXMLInstance);
+ fIncomingXMLInstance=NULL;
+ }
+ #endif
+} // TSyncSession::XMLTranslationIncomingEnd
+
+
+// finish and output XML translation of outgoing traffic
+/// @todo
+/// rewrite this to use a TDbgOut object to write stuff
+void TSyncSession::XMLTranslationOutgoingEnd(void)
+{
+ #ifdef XML_TRANSLATION_ENABLED
+ if (fOutgoingXMLInstance && fXMLtranslate) {
+ // write XML translation of input and output to files
+ DEBUGPRINTFX(DBG_EXOTIC,("XML translation enabled..."))
+ MemPtr_t XMLtext;
+ MemSize_t XMLsize;
+ string fname;
+ // - outgoing
+ // write only if session is debug-enabled
+ // - get XML
+ DEBUGPRINTFX(DBG_EXOTIC,("- Writing outgoing XML translation"))
+ XMLtext=NULL; XMLsize=0;
+ if (smlLockReadBuffer(fOutgoingXMLInstance,&XMLtext,&XMLsize)==SML_ERR_OK) {
+ // save to file
+ TDbgOut *dbgOutP = getSyncAppBase()->newDbgOutputter(false);
+ if (dbgOutP) {
+ // create base file name (trm = translated message)
+ string dumpfilename;
+ StringObjPrintf(dumpfilename,
+ "%s_trm%03ld_%03ld_outgoing",
+ getDbgLogger()->getDebugPath(), // path + session log base name
+ (long)getOutgoingMsgID(),
+ (long)++fDumpCount // to make sure it is unique even in case of retries
+ );
+ // open file in raw mode
+ if (dbgOutP->openDbg(
+ dumpfilename.c_str(),
+ ".xml",
+ dbgflush_none,
+ false, // append to existing if any
+ true // raw mode
+ )) {
+ // write out the entire message
+ dbgOutP->putRawData(XMLtext, XMLsize);
+ // close the file
+ dbgOutP->closeDbg();
+ // add a link into the session file to immediately get the file
+ PDEBUGPRINTFX(DBG_HOT+DBG_PROTO,(
+ "Outgoing %sXML message msgID=%ld &html;<a href=\"%s_trm%03ld_%03ld_outgoing.xml\" target=\"_blank\">&html;saved as XML translation&html;</a>&html;",
+ getEncoding()==SML_XML ? "" : "WB",
+ (long)getOutgoingMsgID(),
+ getDbgLogger()->getDebugFilename(), // session log base name
+ (long)getOutgoingMsgID(),
+ (long)fDumpCount
+ ));
+ }
+ else {
+ PDEBUGPRINTFX(DBG_ERROR,("Cannot write <xmltranslate> file"));
+ }
+ delete dbgOutP;
+ }
+ }
+ }
+ if (fOutgoingXMLInstance) {
+ // finally free instance
+ getSyncAppBase()->freeSmlInstance(fOutgoingXMLInstance);
+ fOutgoingXMLInstance=NULL;
+ }
+ #endif
+} // TSyncSession::XMLTranslationOutgoingEnd
+
+
+// dump message from specified buffer
+void TSyncSession::DumpSyncMLBuffer(MemPtr_t aBuffer, MemSize_t aBufSize, bool aOutgoing, Ret_t aDecoderError)
+{
+ #ifdef MSGDUMP
+ // log message currently in SML buffer
+ if (fMsgDump && !getRootConfig()->fDebugConfig.fDebugInfoPath.empty()) {
+ TDbgOut *dbgOutP = getSyncAppBase()->newDbgOutputter(false);
+ if (dbgOutP) {
+ // create base file name
+ string dumpfilename;
+ // regular message,
+ StringObjPrintf(dumpfilename,
+ "%s_msg%03ld_%03ld_%sing",
+ getDbgLogger()->getDebugPath(), // path + session log base name
+ (long)(aOutgoing ? getOutgoingMsgID() : getLastIncomingMsgID()+1), // just generated msgID / expected next incoming ID
+ (long)++fDumpCount, // to make sure it is unique even in case of retries
+ aOutgoing ? "outgo" : "incom"
+ );
+ if (aDecoderError) {
+ // append error code
+ StringObjAppendPrintf(dumpfilename,"_ERR_0x%04X",aDecoderError);
+ }
+ // open file in raw mode
+ if (dbgOutP->openDbg(
+ dumpfilename.c_str(),
+ getEncoding()==SML_XML ? ".xml" : ".wbxml",
+ dbgflush_none,
+ false, // append to existing if any
+ true // raw mode
+ )) {
+ // write out the entire message
+ dbgOutP->putRawData(aBuffer, aBufSize);
+ // close the file
+ dbgOutP->closeDbg();
+ // add a link into the session file to immediately get the file if it is XML
+ if (getEncoding()==SML_XML) {
+ PDEBUGPRINTFX(DBG_HOT+DBG_PROTO,(
+ "%sing XML message msgID=%ld &html;<a href=\"%s_msg%03ld_%03ld_%sing.xml\" target=\"_blank\">&html;dumped to file&html;</a>&html;",
+ aOutgoing ? "Outgo" : "Incom",
+ (long)getOutgoingMsgID(),
+ getDbgLogger()->getDebugFilename(), // session log base name
+ (long)(aOutgoing ? getOutgoingMsgID() : getLastIncomingMsgID()+1), // just generated msgID / expected next incoming ID
+ (long)fDumpCount,
+ aOutgoing ? "outgo" : "incom"
+ ));
+ }
+ }
+ else {
+ PDEBUGPRINTFX(DBG_ERROR,("Cannot write <msgdump> file"));
+ }
+ delete dbgOutP;
+ }
+ }
+ #endif // MSGDUMP
+} // TSyncSession::DumpSyncMLBuffer
+
+
+// Dump message in SML buffer to file
+void TSyncSession::DumpSyncMLMessage(bool aOutgoing)
+{
+ // dump message if needed
+ #ifdef MSGDUMP
+ // log message currently in SML buffer
+ if (fMsgDump && !getRootConfig()->fDebugConfig.fDebugInfoPath.empty()) {
+ // peek into buffer
+ MemPtr_t data;
+ MemSize_t datasize;
+ if (smlPeekMessageBuffer(getSmlWorkspaceID(), aOutgoing, &data, &datasize)==SML_ERR_OK) {
+ DumpSyncMLBuffer(data,datasize,aOutgoing,SML_ERR_OK);
+ }
+ }
+ #endif // MSGDUMP
+} // TSyncSession::DumpSyncMLMessage
+
+
+#endif // SYDEBUG
+
+
+// SyncML Toolkit callback handlers
+// ================================
+
+
+// start of SyncML message
+Ret_t TSyncSession::StartMessage(SmlSyncHdrPtr_t aContentP)
+{
+ #ifdef SYDEBUG
+ fSessionLogger.DebugDefineMainThread();
+ #endif
+ SessionUsed(); // session used
+ fLastRequestStarted = getSystemNowAs(TCTX_UTC); // request started
+ fMessageRetried = false; // we assume no message retry
+ MP_SHOWCURRENT(DBG_PROFILE,"Start of incoming Message");
+ TP_START(fTPInfo,TP_general); // could be new thread
+ #if defined(EXPIRES_AFTER_DATE) && !defined(SYSYNC_CLIENT)
+ // set 1/4 of the date here
+ fCopyOfScrambledNow=((getSyncAppBase()->fScrambledNow)<<2)+503; // scramble again a little
+ #endif
+ // dump it if configured
+ // Note: this must happen here before answer writing to the instance buffer starts, as otherwise
+ // the already consumed part of the buffer might get overwritten (the SyncML message header in this case).
+ #ifdef SYDEBUG
+ DumpSyncMLMessage(false); // incoming
+ #endif
+ #ifndef SYSYNC_CLIENT
+ // for server, SyncML_Outgoing is started here, as SyncML_Incoming ends before SyncML_Outgoing
+ // but for client, document exchange starts with outgoing message
+ PDEBUGBLOCKDESC("SyncML_Outgoing","preparing for response before starting to analyze new incoming message");
+ #endif
+ PDEBUGBLOCKFMT(("SyncML_Incoming","Starting to analyze incoming message",
+ "RequestNo=%ld|SySyncVers=%d.%d.%d.%d",(long)fSyncAppBaseP->requestCount(),
+ SYSYNC_VERSION_MAJOR,
+ SYSYNC_VERSION_MINOR,
+ SYSYNC_SUBVERSION,
+ SYSYNC_BUILDNUMBER
+ ));
+ PDEBUGPRINTFX(DBG_HOT,(
+ "=================> Starting to analyze incoming message, SySync V%d.%d.%d.%d, RequestNo=%ld",
+ SYSYNC_VERSION_MAJOR,
+ SYSYNC_VERSION_MINOR,
+ SYSYNC_SUBVERSION,
+ SYSYNC_BUILDNUMBER,
+ (long)fSyncAppBaseP->requestCount()
+ ));
+ #ifdef SYDEBUG
+ // Start incoming translation before decoding header
+ XMLTranslationIncomingStart();
+ if (fXMLtranslate && fIncomingXMLInstance) {
+ // Note: we must find the SyncML version in advance, as fSyncMLVersion is not yet valid here
+ sInt16 hdrVers;
+ StrToEnum(SyncMLVerDTDNames,numSyncMLVersions,hdrVers,smlPCDataToCharP(aContentP->version));
+ smlStartMessageExt(fIncomingXMLInstance,aContentP,SmlVersionCodes[hdrVers]);
+ }
+ #endif
+ // update encoding
+ smlGetEncoding(fSmlWorkspaceID,&fEncoding);
+ // create command
+ TSyncHeader *syncheaderP;
+ MP_NEW(syncheaderP,DBG_OBJINST,"TSyncHeader",TSyncHeader(this,aContentP));
+ // execute it (special case for header)
+ return processHeader(syncheaderP);
+} // TSyncSession::StartMessage
+
+
+// special entry point to prematurely abort processing of a incoming message
+// and cause the necessary cleanup
+void TSyncSession::CancelMessageProcessing(void)
+{
+ #ifdef SYDEBUG
+ // Now dump XML translation of incoming message (as far as it was processed at all)
+ XMLTranslationIncomingEnd();
+ #endif
+ // Show premature end of input processing
+ PDEBUGPRINTFX(DBG_HOT,(
+ "=================> Aborted processing message #%ld, request=%ld",
+ (long)fIncomingMsgID,
+ (long)fSyncAppBaseP->requestCount()
+ ));
+} // TSyncSession::CancelMessageProcessing
+
+
+Ret_t TSyncSession::EndMessage(Boolean_t final)
+{
+ #ifdef SYDEBUG
+ // generate XML translation
+ if (fXMLtranslate && fIncomingXMLInstance)
+ smlEndMessage(fIncomingXMLInstance,final);
+ // Now dump XML translation of incoming message
+ XMLTranslationIncomingEnd();
+ #endif
+ // End of incoming message
+ PDEBUGPRINTFX(DBG_HOT,(
+ "=================> Finished processing incoming message #%ld (%sfinal), request=%ld",
+ (long)fIncomingMsgID,
+ final ? "" : "not ",
+ (long)fSyncAppBaseP->requestCount()
+ ));
+ // start outgoing message if not already done so
+ // Note: this should NOT happen, as EVERY message from the remote should contain a SyncHdr status which
+ // should have started the message already
+ if (!fOutgoingStarted) {
+ PDEBUGPRINTFX(DBG_ERROR,("Warning: incoming message #%ld did not contain a SyncHdr status (protocol violation)",(long)fIncomingMsgID));
+ // try to continue by simply ignoring - might not always work out (e.g. when authorisation is not yet complete, this will fail)
+ issueHeader(false);
+ }
+ // forget pending continue requests
+ if (final)
+ fNextMessageRequests=0; // no pending next message requests when a message is final
+ // make sure peer gets devInf Put if needed (only if it didn't issue a GET)
+ // Note: do it here because we have processed all commands (alerts) now but
+ // server response alerts are still in the fEndOfMessageCommands queue.
+ // This ensures that clients gets PUT before it gets ALERTs.
+ if (!fRemoteGotDevinf && fRemoteMustSeeDevinf) {
+ // remote has not got devinf and should see it
+ if (!getRootConfig()->fNeverPutDevinf) {
+ // PUT devinf now
+ PDEBUGPRINTFX(DBG_PROTO,("Remote must see our changed devInf -> creating PUT command"));
+ TDevInfPutCommand *putcmdP = new TDevInfPutCommand(this);
+ issueRootPtr(putcmdP);
+ }
+ else {
+ PDEBUGPRINTFX(DBG_PROTO,("Remote should see devinf, but PUT is suppressed: <neverputdevinf>"));
+ }
+ }
+ // hook for placing custom GET and PUT
+ if (!fCustomGetPutSent) {
+ fCustomGetPutSent=true;
+ issueCustomGetPut(fRemoteDevInfKnown,fRemoteGotDevinf);
+ }
+ // now issue all commands that may only be issued AFTER sending statuses for incoming commands
+ TSmlCommandPContainer::iterator pos;
+ while (true) {
+ // first in list
+ pos=fEndOfMessageCommands.begin();
+ if (pos==fEndOfMessageCommands.end()) break; // done
+ // take command out of the list
+ TSmlCommand *cmdP=(*pos);
+ fEndOfMessageCommands.erase(pos);
+ PDEBUGPRINTFX(DBG_SESSION,("<--- Issuing command '%s' from EndOfMessage Queue",cmdP->getName()));
+ // issue it (doesn't matter if cannot be sent with this message,
+ // it will then be moved into the fNextMessageCommands queue)
+ issuePtr(cmdP,fNextMessageCommands,fInterruptedCommandP);
+ }
+ // now continue with package if it was discontinued in last message
+ // %%% if (fNextMessageRequests>0) {
+ // We have received a 222 Alert, so continue package now
+ // %%% always continue, even if we didn't see a 222 alert
+ ContinuePackageRoot();
+ // %%% }
+ // let client or server do what is needed
+ if (fFakeFinalFlag) {
+ PDEBUGPRINTFX(DBG_ERROR,("Warning: heavy workaround active - <final/> simulated to get resume without sync-from-client going"));
+ }
+ MessageEnded(final || fFakeFinalFlag);
+ fFakeFinalFlag=false;
+ #ifdef SYNCSTATUS_AT_SYNC_CLOSE
+ // make sure sync status is disposed
+ if (fSyncCloseStatusCommandP) delete fSyncCloseStatusCommandP;
+ fSyncCloseStatusCommandP=NULL;
+ #endif
+ MP_SHOWCURRENT(DBG_PROFILE,"End of incoming message");
+ // ok if no exception thrown
+ return SML_ERR_OK;
+} // TSyncSession::EndMessage
+
+
+
+Ret_t TSyncSession::StartSync(SmlSyncPtr_t aContentP)
+{
+ #ifdef SYDEBUG
+ // generate XML translation
+ if (fXMLtranslate && fIncomingXMLInstance)
+ smlStartSync(fIncomingXMLInstance,aContentP);
+ #endif
+ // create command object
+ TSyncCommand *commandP = new TSyncCommand(this,fIncomingMsgID,aContentP);
+ // process it
+ return process(commandP);
+} // TSyncSession::StartSync
+
+
+Ret_t TSyncSession::EndSync(void)
+{
+ #ifdef SYDEBUG
+ // generate XML translation
+ if (fXMLtranslate && fIncomingXMLInstance)
+ smlEndSync(fIncomingXMLInstance);
+ #endif
+
+ /* %%% old version: sync end is no command itself,
+ makes queuing <sync> sequences for later processing
+ impossible, so we made it be a separate command
+ // process Sync End
+ // %%% evtl. catch...
+ PDEBUGPRINTFX(DBG_HOT,("End of <Sync> command"));
+ // Note: do not call if previous Sync start might not have been processed
+ if (!fIgnoreIncomingCommands) processSyncEnd();
+ return SML_ERR_OK;
+ */
+ // create command object
+ TSyncEndCommand *commandP = new TSyncEndCommand(this,fIncomingMsgID);
+ // process it
+ return process(commandP);
+} // TSyncSession::EndSync
+
+
+#ifdef ATOMIC_RECEIVE
+Ret_t TSyncSession::StartAtomic(SmlAtomicPtr_t aContentP)
+{
+ #ifdef SYDEBUG
+ // generate XML translation
+ if (fXMLtranslate && fIncomingXMLInstance)
+ smlStartAtomic(fIncomingXMLInstance,aContentP);
+ #endif
+ // NOTE from Specs: Nested Atomic commands are not legal. A nested Atomic
+ // command will generate an error 500 - command failed.
+ // create command object
+ // %%% create DUMMY command for now
+ PDEBUGPRINTFX(DBG_HOT,("Start of Atomic bracket: return Status 406 unimplemented"));
+ TUnimplementedCommand *commandP =
+ new TUnimplementedCommand(
+ this,
+ fIncomingMsgID,
+ aContentP->cmdID,
+ 0,
+ scmd_copy,
+ aContentP,
+ 406); // optional feature not supported
+ // process it
+ return process(commandP);
+} // TSyncSession::StartAtomic
+
+Ret_t TSyncSession::EndAtomic(void)
+{
+ #ifdef SYDEBUG
+ // generate XML translation
+ if (fXMLtranslate && fIncomingXMLInstance)
+ smlEndAtomic(fIncomingXMLInstance);
+ #endif
+ // process Atomic end
+ // %%% not implemented, just accept
+ PDEBUGPRINTFX(DBG_HOT,("End of Atomic bracket"));
+ return SML_ERR_OK;
+} // TSyncSession::EndAtomic
+#endif
+
+
+#ifdef SEQUENCE_RECEIVE
+Ret_t TSyncSession::StartSequence(SmlSequencePtr_t aContentP)
+{
+ #ifdef SYDEBUG
+ // generate XML translation
+ if (fXMLtranslate && fIncomingXMLInstance)
+ smlStartSequence(fIncomingXMLInstance,aContentP);
+ #endif
+ // %%% later, implement a nestable command object and derive Sequence,Atomic and Sync
+ // from it. Similar to nested command creation, maintain a chain of nested commands;
+ // session will have a pointer to most recent nest and ALL commands will have a pointer
+ // to owning command (or NULL if they are on root level).
+ // Sequence is trivial as SySync executes command in sequence anyway
+ // - simply keep track of nesting
+ fSequenceNesting++;
+ PDEBUGPRINTFX(DBG_HOT,("Start of Sequence bracket, nesting level is now %hd",fSequenceNesting));
+ // get cmdid
+ sInt32 cmdid;
+ StrToLong(smlPCDataToCharP(aContentP->cmdID),cmdid);
+ // make status
+ TStatusCommand *statusCmdP = new TStatusCommand(
+ this, // associated session (for callbacks)
+ cmdid, // referred-to command ID
+ scmd_sequence, // referred-to command type (scmd_xxx)
+ (aContentP->flags & SmlNoResp_f)!=0, // set if no-Resp
+ 200 // status code
+ ); // issue ok status
+ // - return status
+ issueRootPtr(statusCmdP);
+ // - ok
+ return SML_ERR_OK;
+} // TSyncSession::StartSequence
+
+
+Ret_t TSyncSession::EndSequence(void)
+{
+ #ifdef SYDEBUG
+ // generate XML translation
+ if (fXMLtranslate && fIncomingXMLInstance)
+ smlEndSequence(fIncomingXMLInstance);
+ #endif
+ // - keep track of nesting
+ if (fSequenceNesting<1) {
+ // error in nesting
+ PDEBUGPRINTFX(DBG_HOT,("End of Sequence bracket, MISSING PRECEEDING SEQUENCE START -> aborting session"));
+ AbortSession(400,true); // bad nesting is severe, abort session
+ }
+ else {
+ // nesting ok
+ fSequenceNesting--;
+ PDEBUGPRINTFX(DBG_HOT,("End of Sequence bracket, nesting level is now %hd",fSequenceNesting));
+ }
+ return SML_ERR_OK;
+} // TSyncSession::EndSequence
+#endif
+
+
+Ret_t TSyncSession::AddCmd(SmlAddPtr_t aContentP)
+{
+ #ifdef SYDEBUG
+ // generate XML translation
+ if (fXMLtranslate && fIncomingXMLInstance)
+ smlAddCmd(fIncomingXMLInstance,aContentP);
+ #endif
+ // create SyncOp command object
+ TSyncOpCommand *commandP = new TSyncOpCommand(
+ this,
+ fLocalSyncDatastoreP,
+ fIncomingMsgID,
+ sop_add,
+ scmd_add,
+ aContentP
+ );
+ // process it
+ return process(commandP);
+} // TSyncSession::AddCmd
+
+
+Ret_t TSyncSession::AlertCmd(SmlAlertPtr_t aContentP)
+{
+ #ifdef SYDEBUG
+ // generate XML translation
+ if (fXMLtranslate && fIncomingXMLInstance)
+ smlAlertCmd(fIncomingXMLInstance,aContentP);
+ #endif
+ // create command object
+ TAlertCommand *commandP = new TAlertCommand(this,fIncomingMsgID,aContentP);
+ // process it
+ return process(commandP);
+} // TSyncSession::AlertCmd
+
+
+Ret_t TSyncSession::DeleteCmd(SmlDeletePtr_t aContentP)
+{
+ #ifdef SYDEBUG
+ // generate XML translation
+ if (fXMLtranslate && fIncomingXMLInstance)
+ smlDeleteCmd(fIncomingXMLInstance,aContentP);
+ #endif
+ // determine type of delete
+ TSyncOperation syncop;
+ if (aContentP->flags & SmlArchive_f) syncop = sop_archive_delete;
+ else if (aContentP->flags & SmlSftDel_f) syncop = sop_soft_delete;
+ else syncop=sop_delete;
+ // create SyncOp command object
+ TSyncOpCommand *commandP = new TSyncOpCommand(
+ this,
+ fLocalSyncDatastoreP, // note that this one might be NULL in case previous sync command was delayed
+ fIncomingMsgID,
+ syncop,
+ scmd_delete,
+ aContentP
+ );
+ // process it
+ return process(commandP);
+} // TSyncSession::DeleteCmd
+
+
+// process GET commands
+Ret_t TSyncSession::GetCmd(SmlGetPtr_t aContentP)
+{
+ #ifdef SYDEBUG
+ // generate XML translation
+ if (fXMLtranslate && fIncomingXMLInstance)
+ smlGetCmd(fIncomingXMLInstance,aContentP);
+ #endif
+ // create command object
+ TGetCommand *commandP = new TGetCommand(this,fIncomingMsgID,aContentP);
+ // process it
+ return process(commandP);
+} // TSyncSession::GetCmd
+
+
+Ret_t TSyncSession::PutCmd(SmlPutPtr_t aContentP)
+{
+ #ifdef SYDEBUG
+ // generate XML translation
+ if (fXMLtranslate && fIncomingXMLInstance)
+ smlPutCmd(fIncomingXMLInstance,aContentP);
+ #endif
+ // create command object
+ TPutCommand *commandP = new TPutCommand(this,fIncomingMsgID,aContentP);
+ // process it
+ return process(commandP);
+} // TSyncSession::PutCmd
+
+
+#ifdef MAP_RECEIVE
+Ret_t TSyncSession::MapCmd(SmlMapPtr_t aContentP)
+{
+ #ifdef SYDEBUG
+ // generate XML translation
+ if (fXMLtranslate && fIncomingXMLInstance)
+ smlMapCmd(fIncomingXMLInstance,aContentP);
+ #endif
+ // create command object
+ TMapCommand *commandP = new TMapCommand(this,fIncomingMsgID,aContentP);
+ // process it
+ return process(commandP);
+} // TSyncSession::MapCmd
+#endif
+
+
+#ifdef RESULT_RECEIVE
+Ret_t TSyncSession::ResultsCmd(SmlResultsPtr_t aContentP)
+{
+ #ifdef SYDEBUG
+ // generate XML translation
+ if (fXMLtranslate && fIncomingXMLInstance)
+ smlResultsCmd(fIncomingXMLInstance,aContentP);
+ #endif
+ // create command object
+ TResultsCommand *commandP = new TResultsCommand(this,fIncomingMsgID,aContentP);
+ // process it
+ return process(commandP);
+} // TSyncSession::ResultsCmd
+#endif
+
+
+Ret_t TSyncSession::StatusCmd(SmlStatusPtr_t aContentP)
+{
+ #ifdef SYDEBUG
+ // generate XML translation
+ if (fXMLtranslate && fIncomingXMLInstance)
+ smlStatusCmd(fIncomingXMLInstance,aContentP);
+ #endif
+ // create command object
+ TStatusCommand *statuscommandP = new TStatusCommand(this,fIncomingMsgID,aContentP);
+ // handle status (search for command that waits for this status)
+ return handleStatus(statuscommandP);
+} // TSyncSession::StatusCmd
+
+
+Ret_t TSyncSession::ReplaceCmd(SmlReplacePtr_t aContentP)
+{
+ #ifdef SYDEBUG
+ // generate XML translation
+ if (fXMLtranslate && fIncomingXMLInstance)
+ smlReplaceCmd(fIncomingXMLInstance,aContentP);
+ #endif
+ // create SyncOp command object
+ TSyncOpCommand *commandP = new TSyncOpCommand(
+ this,
+ fLocalSyncDatastoreP,
+ fIncomingMsgID,
+ sop_replace,
+ scmd_replace,
+ aContentP
+ );
+ // process it
+ return process(commandP);
+} // TSyncSession::ReplaceCmd
+
+
+#ifdef COPY_RECEIVE
+Ret_t TSyncSession::CopyCmd(SmlReplacePtr_t aContentP)
+{
+ #ifdef SYDEBUG
+ #ifdef COPY_SEND
+ // generate XML translation
+ if (fXMLtranslate && fIncomingXMLInstance)
+ smlCopyCmd(fIncomingXMLInstance,aContentP);
+ #else
+ #error "We will have incomplete XML translation when only COPY_RECEIVE is defined"
+ #endif
+ #endif
+ // create SyncOp command object
+ TSyncOpCommand *commandP = new TSyncOpCommand(
+ this,
+ fLocalSyncDatastoreP,
+ fIncomingMsgID,
+ fTreatCopyAsAdd ? sop_add : sop_copy,
+ scmd_copy,
+ aContentP
+ );
+ // process it
+ return process(commandP);
+} // TSyncSession::CopyCmd
+#endif
+
+
+Ret_t TSyncSession::MoveCmd(SmlReplacePtr_t aContentP)
+{
+ #ifdef SYDEBUG
+ // generate XML translation
+ if (fXMLtranslate && fIncomingXMLInstance)
+ smlMoveCmd(fIncomingXMLInstance,aContentP);
+ #endif
+ // create SyncOp command object
+ TSyncOpCommand *commandP = new TSyncOpCommand(
+ this,
+ fLocalSyncDatastoreP,
+ fIncomingMsgID,
+ sop_move,
+ scmd_move,
+ aContentP
+ );
+ // process it
+ return process(commandP);
+} // TSyncSession::MoveCmd
+
+// - error handling
+Ret_t TSyncSession::HandleError(void)
+{
+ // %%% tbd
+ DEBUGPRINTFX(DBG_ERROR,("HandleError reached"));
+ return SML_ERR_OK; // %%%
+} // TSyncSession::HandleError
+
+
+
+Ret_t TSyncSession::DummyHandler(const char* msg)
+{
+ //DEBUGPRINTFX(DBG_ERROR,("DummyHandler: msg=%s",msg));
+ return SML_ERR_OK;
+} // TSyncSession::DummyHandler
+
+
+
+#ifdef ENGINEINTERFACE_SUPPORT
+
+// Support for EngineModule common interface
+// =========================================
+
+// open subkey by name (not by path!)
+// - this is the actual implementation
+TSyError TSessionKey::OpenSubKeyByName(
+ TSettingsKeyImpl *&aSettingsKeyP,
+ cAppCharP aName, stringSize aNameSize,
+ uInt16 aMode
+) {
+ #ifdef SCRIPT_SUPPORT
+ if (strucmp(aName,"sessionvars",aNameSize)==0) {
+ // note: if no session scripts are used, context does not exist and is NULL.
+ // TScriptVarKey does not crash with a NULL, so we can give ok here (but no session vars
+ // will be accessible).
+ aSettingsKeyP = new TScriptVarKey(fEngineInterfaceP,fSessionP->getSessionScriptContext());
+ }
+ else
+ #endif
+ return inherited::OpenSubKeyByName(aSettingsKeyP,aName,aNameSize,aMode);
+ // opened a key
+ return LOCERR_OK;
+} // TSessionKey::OpenSubKeyByName
+
+#endif // ENGINEINTERFACE_SUPPORT
+
+
+} // namespace sysync
+
+
+// factory methods of Session Config
+// =================================
+
+// only one of XML2GO or SDK/Plugin can be on top of customagent
+#ifdef XML2GO_SUPPORT
+ #include "xml2goapids.h"
+#elif defined(SDK_SUPPORT)
+ #include "pluginapids.h"
+#endif
+// ODBC can be in-between if selected
+#ifdef SQL_SUPPORT
+ #include "odbcapids.h"
+#endif
+
+
+namespace sysync {
+
+#ifndef HARDCODED_CONFIG
+
+// create new datastore config by name
+// returns NULL if none found
+TLocalDSConfig *TSessionConfig::newDatastoreConfig(const char *aName, const char *aType, TConfigElement *aParentP)
+{
+ #ifdef XML2GO_SUPPORT
+ if (aType && strucmp(aType,"xml2go")==0) {
+ // xml2go enhanced datastore
+ return new TXml2goDSConfig(aName,aParentP);
+ }
+ else
+ #elif defined(SDK_SUPPORT)
+ if (aType && strucmp(aType,"plugin")==0) {
+ // APIDB enhanced datastore (on top of ODBC if SQL_SUPPORT is on)
+ return new TPluginDSConfig(aName,aParentP);
+ }
+ else
+ #endif
+ #ifdef SQL_SUPPORT
+ if (aType==0 || strucmp(aType,"odbc")==0 || strucmp(aType,"sql")==0) {
+ // ODBC enabled datastore
+ return new TOdbcDSConfig(aName,aParentP);
+ }
+ else
+ #endif
+ return NULL; // unknown datastore
+} // TSessionConfig::newDatastoreConfig
+
+#endif // HARDCODED_CONFIG
+
+} // namespace sysync
+
+#endif // not SYNCSESSION_PART1_EXCLUDE
+
+// eof
diff --git a/src/sysync/syncsession.h b/src/sysync/syncsession.h
new file mode 100755
index 0000000..a04399a
--- /dev/null
+++ b/src/sysync/syncsession.h
@@ -0,0 +1,973 @@
+/*
+ * TSyncSession
+ * Represents an entire Synchronisation Session, possibly consisting
+ * of multiple SyncML-Toolkit "Sessions" (Message composition/de-
+ * composition) as well as multiple database synchronisations.
+ *
+ * Copyright (c) 2001-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ */
+
+#ifndef SYNC_SESSION_H
+#define SYNC_SESSION_H
+
+// general includes (SyncML tookit, windows, Clib)
+#include "sysync.h"
+
+// specific includes
+#include "syncappbase.h"
+#include "localengineds.h"
+#include "remotedatastore.h"
+#include "profiling.h"
+#include "scriptcontext.h"
+
+
+namespace sysync {
+
+extern const char * const SyncMLVerProtoNames[numSyncMLVersions];
+extern const SmlVersion_t SmlVersionCodes[numSyncMLVersions];
+extern const char * const SyncMLVerDTDNames[numSyncMLVersions];
+extern const char * const SyncMLDevInfNames[numSyncMLVersions];
+#ifndef HARDCODED_CONFIG
+extern const char * const SyncMLVersionNames[numSyncMLVersions];
+#endif
+
+extern const char * const authTypeNames[numAuthTypes];
+
+extern const char * const SyncModeNames[numSyncModes];
+
+#ifdef SYDEBUG
+extern const char * const PackageStateNames[numPackageStates];
+extern const char * const SyncOpNames[numSyncOperations];
+#endif
+
+extern const char * const SyncModeDescriptions[numSyncModes];
+
+
+// secret type for SessionLogin
+typedef enum {
+ // Note: changes here will change AUTHTYPE() script func API in ODBC-Agent!
+ sectyp_anonymous, // anonymous
+ sectyp_clearpass, // clear text password
+ sectyp_md5_V10, // SyncML V1.0 MD5
+ sectyp_md5_V11 // SyncML V1.1 MD5
+} TAuthSecretTypes;
+
+
+// minimal free message size required to end message
+// %%% rough approx, should always be enough
+#define SIZEFORMESSAGEEND 200
+
+
+// Container types
+typedef std::list<TRemoteDataStore*> TRemoteDataStorePContainer; // contains data stores
+
+
+
+// forward declaration
+class TSyncAppBase;
+class TSmlCommand;
+class TStatusCommand;
+class TSyncHeader;
+class TLocalEngineDS;
+class TRemoteDataStore;
+class TRootConfig;
+
+
+#ifdef SCRIPT_SUPPORT
+// publish as derivates might need it
+extern const TFuncTable ErrorFuncTable;
+
+typedef struct {
+ TSyError statuscode;
+ TSyError newstatuscode;
+ bool resend;
+ TLocalEngineDS *datastoreP;
+ TSyncOperation syncop;
+} TErrorFuncContext;
+
+typedef struct {
+ bool isPut;
+ bool canIssue;
+ TSyError statuscode;
+ string itemURI;
+ string itemData;
+ string metaType;
+} TGetPutResultFuncContext;
+#endif
+
+#ifndef NO_REMOTE_RULES
+
+// remote party special rule
+class TRemoteRuleConfig: public TConfigElement
+{
+ typedef TConfigElement inherited;
+public:
+ TRemoteRuleConfig(const char *aElementName, TConfigElement *aParentElementP);
+ virtual ~TRemoteRuleConfig();
+ // properties
+ // - identification of remote
+ string fManufacturer;
+ string fModel;
+ string fOem;
+ string fFirmwareVers;
+ string fSoftwareVers;
+ string fHardwareVers;
+ string fDevId;
+ string fDevTyp;
+ // - options specific for that remote party (0=false, 1=true, -1=unspecified)
+ sInt8 fLimitedFieldLengths; // set if remote has limited field lengths
+ sInt8 fDontSendEmptyProperties; // set if remote does not want empty properties
+ sInt8 fDoQuote8BitContent; // set if 8-bit chars should generally be encoded with QP in MIME-DIR
+ sInt8 fDoNotFoldContent; // set if content should not be folded in MIME-DIR
+ sInt8 fNoReplaceInSlowsync; // do not use Replace (as server) in slow sync (this is to COMPLETELY avoid replaces being sent during slowsync, for clients that crash on that such as old 9210)
+ sInt8 fTreatRemoteTimeAsLocal; // treat remote time as localtime even if it carries different time zone information ("Z" suffix or zone spec)
+ sInt8 fTreatRemoteTimeAsUTC; // treat remote time as UTC even if it carries different time zone information (no suffix or zone spec)
+ sInt8 fVCal10EnddatesSameDay; // send end date-only values (like DTEND) as last time unit of previous day (i.e. 23:59:59, inclusive) instead of midnight of next day (exclusive, like in iCalendar 2.0)
+ sInt8 fIgnoreDevInfMaxSize; // ignore <maxsize> specification in CTCap (when device has bad specs like in E90 for example)
+ sInt8 fIgnoreCTCap; // ignore entire ctcap
+ sInt8 fDSPathInDevInf; // use actual DS path as used in Alert for creating datastore devInf (needed for newer Nokia clients)
+ sInt8 fDSCgiInDevInf; // also show CGI as used in Alert for creating datastore devInf (needed for newer Nokia clients)
+ sInt8 fUpdateClientDuringSlowsync; // do not update client records (due to merge) in slowsync (However, updates can still occur in first-time sync and if server wins conflict)
+ sInt8 fUpdateServerDuringSlowsync; // do not update server records during NON-FIRST-TIME slowsync (but do it for first sync!)
+ sInt8 fAllowMessageRetries; // allow that client sends same message ID again (retry attempt)
+ sInt8 fStrictExecOrdering; // requires strict SyncML-standard ordering of status responses
+ sInt8 fTreatCopyAsAdd; // treat COPY like ADD (needed for Calmeno/Weblicon clients)
+ sInt8 fCompleteFromClientOnly; // perform complete from-client-only session (non conformant, Synthesis before 2.9.8.2 style)
+ sInt32 fRequestMaxTime; // max time [seconds] allowed for processing a single request, 0=unlimited, -1=not specified
+ TCharSets fDefaultOutCharset; // default charset for generation
+ TSyError fRejectStatusCode; // if >=0, attempt to connect will always be rejected with given status code
+ sInt8 fForceUTC; // force sending time in UTC (overrides SyncML 1.1 <utc/> devInf flag)
+ sInt8 fForceLocaltime; // force sending time in localtime (overrides SyncML 1.1 <utc/> devInf flag)
+ #ifndef MINIMAL_CODE
+ string fRemoteDescName; // descriptive name of remote
+ #endif
+ #ifdef SCRIPT_SUPPORT
+ string fRuleScriptTemplate; // template for rule script
+ #endif
+ // flag if this is a final rule (if matches, no more rules will be checked)
+ bool fFinalRule;
+protected:
+ // check config elements
+ #ifndef HARDCODED_CONFIG
+ virtual bool localStartElement(const char *aElementName, const char **aAttributes, sInt32 aLine);
+ #endif
+ virtual void clear();
+}; // TRemoteRuleConfig
+
+
+typedef std::list<TRemoteRuleConfig *> TRemoteRulesList;
+
+#endif
+
+// session config
+class TSessionConfig: public TAgentConfig
+{
+ typedef TAgentConfig inherited;
+public:
+ TSessionConfig(const char *aElementName, TConfigElement *aParentElementP);
+ virtual ~TSessionConfig();
+ // Properties
+ // - session timeout (in seconds)
+ sInt32 fSessionTimeout;
+ // - Maximally supported SyncML version
+ TSyncMLVersions fMaxSyncMLVersionSupported;
+ // - Minimally supported SyncML version
+ TSyncMLVersions fMinSyncMLVersionSupported;
+ // - support of server alerted sync codes
+ bool fAcceptServerAlerted;
+ #ifndef NO_REMOTE_RULES
+ // - list of remote rules
+ TRemoteRulesList fRemoteRulesList;
+ #endif
+ // - simple auth
+ string fSimpleAuthUser;
+ string fSimpleAuthPassword;
+ // - local datastores
+ TLocalDSList fDatastores;
+ #ifndef MINIMAL_CODE
+ // - logfile
+ string fLogFileName;
+ string fLogFileFormat;
+ string fLogFileLabels;
+ bool fLogEnabled;
+ uInt32 fDebugChunkMaxSize;
+ #endif
+ bool fRelyOnEarlyMaps; // if set, we rely on early maps sent by clients for adds from the previous session
+ // defaults for remote-rule configurable behaviour
+ bool fUpdateClientDuringSlowsync; // do not update client records (due to merge) in slowsync (However, updates can still occur in first-time sync and if server wins conflict)
+ bool fUpdateServerDuringSlowsync; // do not update server records during NON-FIRST-TIME slowsync (but do it for first sync!)
+ bool fAllowMessageRetries; // allow that client sends same message ID again (retry attempt)
+ uInt32 fRequestMaxTime; // max time [seconds] allowed for processing a single request, 0=unlimited
+ sInt32 fRequestMinTime; // min time [seconds] spent until returning answer (for debug purposes, 0=no minimum time)
+ bool fCompleteFromClientOnly; // perform complete from-client-only session (non conformant, Synthesis before 2.9.8.2 style)
+ // default value for flag to send property lists in CTCap
+ bool fShowCTCapProps;
+ // default value for flag to send type/size in CTCap for SyncML 1.0 (disable as old clients like S55 crash on this)
+ bool fShowTypeSzInCTCap10;
+ // default value for sending end date-only values (like DTEND) as last time unit of previous day (i.e. 23:59:59, inclusive)
+ // instead of midnight of next day (exclusive, like in iCalendar 2.0)
+ bool fVCal10EnddatesSameDay;
+ // instead of folding long lines (as required by the standard) use one line per property
+ bool fDoNotFoldContent;
+ // - set if we should show default parameter in mimo_old types as list of <propparam>s for each value
+ // Note that this is a tristate: 0=no, 1=yes, -1=auto (=yes for <SyncML 1.2, no for >=SyncML 1.2,
+ // thus making it work for Nokia 7610 (1.1) as well as E-Series like E90)
+ sInt8 fEnumDefaultPropParams;
+ // decides whether multi-threading for the datastores will be used
+ bool fMultiThread;
+ // defines if the engine waits with continuing interrupted commands until previous part received status
+ bool fWaitForStatusOfInterrupted;
+ // accept delete commands for already deleted items with 200 (rather that 404 or 211)
+ bool fDeletingGoneOK;
+ // abort if all items sent to remote fail
+ bool fAbortOnAllItemsFailed;
+ // - Session user time context (what time zone the current session's user is in, for clients w/o TZ/UTC support)
+ timecontext_t fUserTimeContext;
+ #ifdef SCRIPT_SUPPORT
+ // session init script
+ string fSessionInitScript;
+ // Error status handling scripts
+ string fSentItemStatusScript;
+ string fReceivedItemStatusScript;
+ // session init script
+ string fSessionFinishScript;
+ // custom GET command handler script
+ string fCustomGetHandlerScript;
+ // custom GET and PUT command generator scripts
+ string fCustomGetPutScript;
+ string fCustomEndPutScript;
+ // custom PUT and RESULT handler script
+ string fCustomPutResultHandlerScript;
+ #endif
+ // public methods
+ TLocalDSConfig *getLocalDS(const char *aName, uInt32 aDBTypeID=0);
+ lineartime_t getSessionTimeout(void) { return fSessionTimeout * secondToLinearTimeFactor; };
+protected:
+ // check config elements
+ #ifndef HARDCODED_CONFIG
+ virtual bool localStartElement(const char *aElementName, const char **aAttributes, sInt32 aLine);
+ virtual TLocalDSConfig *newDatastoreConfig(const char *aName, const char *aType,TConfigElement *aParentP);
+ #endif
+ virtual void clear();
+ virtual void localResolve(bool aLastPass);
+}; // TSessionConfig
+
+
+// forward
+class TLocalEngineDS;
+
+// Container types
+typedef std::list<TLocalEngineDS*> TLocalDataStorePContainer; // contains local data stores
+
+
+// Sync session
+class TSyncSession {
+ friend class TSmlCommand;
+ friend class TSyncHeader;
+ friend class TAlertCommand;
+ friend class TSyncCommand;
+ friend class TStatusCommand;
+ friend class TSyncOpCommand;
+ friend class TRemoteDataStore;
+ friend class TLocalEngineDS;
+ #ifdef SUPERDATASTORES
+ friend class TSuperDataStore;
+ #endif
+public:
+ // constructors/destructors
+ TSyncSession(
+ TSyncAppBase *aSyncAppBaseP, // the owning application base (dispatcher/client base)
+ const char *aSessionID // a session ID
+ );
+ virtual ~TSyncSession();
+ /// @brief terminate a session.
+ /// @Note: Termination is final - session cannot be restarted by RestartSession() after
+ /// calling this routine
+ virtual void TerminateSession(void);
+ // Announce destruction of descendant to all datastores which might have direct links to these descendants and must cancel those
+ void announceDestruction(void); ///< must be called by derived class' destructors to allow datastores to detach from agent BEFORE descendant destructor has run
+ // Reset session
+ virtual void ResetSession(void); ///< resets session as if created totally new. Descendants must rollback any pending database transactions etc.
+ void InternalResetSessionEx(bool terminationCall); // static implementation for calling through virtual destructor and virtual ResetSession();
+ #ifdef DBAPI_TUNNEL_SUPPORT
+ // Initialize a datastore tunnel session
+ virtual localstatus InitializeTunnelSession(cAppCharP aDatastoreName) { return LOCERR_NOTIMP; }; // is usually implemented in customimplagent, as it depends on DBApi architecture
+ #endif
+ // called when incoming SyncHdr fails to execute
+ virtual bool syncHdrFailure(bool aTryAgain) = 0;
+ // Abort session
+ void AbortSession(TSyError aStatusCode, bool aLocalProblem, TSyError aReason=0); // resets session and sets aborted flag to prevent further processing of message
+ // Suspend session
+ void SuspendSession(TSyError aReason);
+ // Session status
+ bool isAborted(void) { return fAborted; }; // test abort status
+ bool isSuspending(void) { return fSuspended; }; // test if flagged for suspend
+ bool isAllSuccess(void); // test if session was completely successful
+ void DatastoreFailed(TSyError aStatusCode, bool aLocalProblem=false); // let session know that datastore has failed
+ void DatastoreHadErrors(void) { fErrorItemDatastores++; }; // let session know that sync was ok, but some items had errors
+ bool outgoingMessageFull(void) { return fOutgoingMessageFull; }; // test if outgoing full
+ bool isInterrupedCmdPending(void) { return fInterruptedCommandP!=NULL; };
+ bool getIncomingState(void) { return fIncomingState; };
+ // stop processing commands in this message
+ void AbortCommandProcessing(TSyError aStatusCode); // all further commands in message will be answered with given status
+ // returns remaining time for request processing [seconds]
+ virtual sInt32 RemainingRequestTime(void) { return 0x7FFFFFFF; }; // quasi infinite
+ // forget commands waiting to be sent when header is generated
+ void forgetHeaderWaitCommands(void);
+ // SyncML toolkit workspace access
+ void setSmlWorkspaceID(InstanceID_t aSmlWorkspaceID);
+ InstanceID_t getSmlWorkspaceID(void) { return fSmlWorkspaceID; };
+ const char *getEncodingName(void); // encoding suffix in MIME type
+ SmlEncoding_t getEncoding(void) { return fEncoding; }; // current encoding
+ void addEncoding(string &aString); // add current encoding spec to given (type-)string
+ sInt32 getSmlWorkspaceFreeBytes(void) { return ((sInt32) smlGetFreeBuffer(fSmlWorkspaceID)); };
+ #ifdef ENGINEINTERFACE_SUPPORT
+ /// @brief Get new session key to access details of this session
+ virtual appPointer newSessionKey(TEngineInterface *aEngineInterfaceP) = 0;
+ #endif
+ // session handling
+ // - get session owner (dispatcher/clientbase)
+ TSyncAppBase *getSyncAppBase(void) { return fSyncAppBaseP; }
+ // - get time when session was last used
+ lineartime_t getSessionLastUsed(void) { return fSessionLastUsed; };
+ // - get time when session was started and ended
+ lineartime_t getSessionStarted(void) { return fSessionStarted; };
+ // - get time when last request started processing
+ lineartime_t getLastRequestStarted(void) { return fLastRequestStarted; };
+ // - update last used time
+ void SessionUsed(void) { fSessionLastUsed=getSystemNowAs(TCTX_UTC); };
+ // - session custom time zones object access
+ GZones *getSessionZones(void) { return &fSessionZones; };
+ // - convenience version for getting time
+ lineartime_t getSystemNowAs(timecontext_t aContext) { return sysync::getSystemNowAs(aContext,getSessionZones()); };
+ // debug and log printing (should NOT be virtual, so that they can be used in destructors)
+ void DebugShowCfgInfo(void); // show some information about the config
+ //%%%void LogPrintf(const char *text, ...);
+ //%%%void LogPuts(const char *text);
+ // properties
+ TSyncMLVersions getSyncMLVersion(void) { return fSyncMLVersion; };
+ const char *getLocalURI(void) { return fLocalURI.c_str(); };
+ const char *getInitialLocalURI(void) { return fInitialLocalURI.c_str(); };
+ const char *getRemoteURI(void) { return fRemoteURI.c_str(); };
+ const char *getSynchdrSessionID(void) { return fSynchdrSessionID.c_str(); };
+ const char *getLocalSessionID(void) { return fLocalSessionID.c_str(); };
+ localstatus getAbortReasonStatus(void) { return fLocalAbortReason ? localError(fAbortReasonStatus) : syncmlError(fAbortReasonStatus); };
+ #ifndef MINIMAL_CODE
+ const char *getRemoteInfoString(void) { return fRemoteInfoString.c_str(); };
+ const char *getRemoteDescName(void) { return fRemoteDescName.c_str(); };
+ const char *getSyncUserName(void) { return fSyncUserName.c_str(); };
+ #endif
+ sInt32 getLastIncomingMsgID(void) { return fIncomingMsgID; };
+ void setSessionBusy(bool aBusy) { fSessionIsBusy=aBusy; }; // make session behave busy generally
+ bool getReadOnly(void) { return fReadOnly; }; // read-only option
+ void setReadOnly(bool aReadOnly) { fReadOnly=aReadOnly; }; // read-only option
+ // info about session
+ virtual bool IsServerSession(void) = 0;
+ // - check if we can handle UTC time (devices without time zone might override this)
+ virtual bool canHandleUTC(void) { return true; }; // assume yes
+ // helpers
+ // - get session relative URI
+ const char *SessionRelativeURI(const char *aURI)
+ { return relativeURI(aURI,getLocalURI()); };
+ // - add local datastore from config
+ TLocalEngineDS *addLocalDataStore(TLocalDSConfig *aLocalDSConfigP);
+ // - add local type
+ void addLocalItemType(TSyncItemType *aItemTypeP)
+ { fLocalItemTypes.push_back(aItemTypeP); };
+ // - find local datatype by config pointer (used to avoid duplicating types
+ // in session if used by more than a single datastore)
+ TSyncItemType *findLocalType(TDataTypeConfig *aDataTypeConfigP);
+ // - find implemented remote datatype by config pointer (and related datastore, if any)
+ TSyncItemType *findRemoteType(TDataTypeConfig *aDataTypeConfigP, TSyncDataStore *aRelatedRemoteDS);
+ // internal processing events implemented in derived classes
+ // - message start and end
+ virtual bool MessageStarted(SmlSyncHdrPtr_t aContentP, TStatusCommand &aStatusCommand, bool aBad=false)=0;
+ virtual void MessageEnded(bool aIncomingFinal)=0;
+ // - get command item processing, may return a Results command. Must set status to non-404 if get could be served
+ virtual TResultsCommand *processGetItem(const char *aLocUri, TGetCommand *aGetCommandP, SmlItemPtr_t aGetItemP, TStatusCommand &aStatusCommand);
+ // - put and results command processing
+ virtual void processPutResultItem(bool aIsPut, const char *aLocUri, TSmlCommand *aPutResultsCommandP, SmlItemPtr_t aPutResultsItemP, TStatusCommand &aStatusCommand);
+ // - alert processing
+ virtual TSmlCommand *processAlertItem(
+ uInt16 aAlertCode, // alert code
+ SmlItemPtr_t aItemP, // alert item to be processed (as one alert can have multiple items)
+ SmlCredPtr_t aCredP, // alert cred element, if any
+ TStatusCommand &aStatusCommand, // pre-set 200 status, can be modified in case of errors
+ TLocalEngineDS *&aLocalDataStoreP // receives datastore pointer, if alert affects a datastore
+ );
+ /* %%% obsolete, now as alert has its own private datastore pointer
+ // - alert status processing
+ virtual bool handleAlertStatus(
+ TSyError aStatusCode, // status code
+ const char *aLocalURI // should be local URI of datastore which sent the alert
+ );
+ */
+ // - handle status received for SyncHdr, returns false if not handled
+ virtual bool handleHeaderStatus(TStatusCommand * /* aStatusCmdP */) { return false; } // no special handling by default
+ // - map operation
+ virtual bool processMapCommand(
+ SmlMapPtr_t aMapCommandP, // the map command contents
+ TStatusCommand &aStatusCommand, // pre-set 200 status, can be modified in case of errors
+ bool &aQueueForLater
+ );
+ // Sync processing (command group)
+ // - start sync group
+ virtual bool processSyncStart(
+ SmlSyncPtr_t aSyncP, // the Sync element
+ TStatusCommand &aStatusCommand, // pre-set 200 status, can be modified in case of errors
+ bool &aQueueForLater // will be set if command must be queued for later (re-)execution
+ ) = 0;
+ // - end sync group
+ virtual bool processSyncEnd(bool &aQueueForLater); // end of sync group
+ // - process generic sync command item within Sync group
+ // - returns true (and unmodified or non-200-successful status) if
+ // operation could be processed regularily
+ // - returns false (but probably still successful status) if
+ // operation was processed with internal irregularities, such as
+ // trying to delete non-existant item in datastore with
+ // incomplete Rollbacks (which returns status 200 in this case!).
+ bool processSyncOpItem(
+ TSyncOperation aSyncOp, // the operation
+ SmlItemPtr_t aItemP, // the item to be processed
+ SmlMetInfMetInfPtr_t aMetaP, // command-wide meta, if any
+ TLocalEngineDS *aLocalSyncDatastore, // the local datastore for this syncop item
+ TStatusCommand &aStatusCommand, // pre-set 200 status, can be modified in case of errors
+ bool &aQueueForLater // must be set if item cannot be processed now, but must be processed later
+ );
+ // message handling
+ // - get current size of message
+ sInt32 getOutgoingMessageSize(void) { return fOutgoingMsgSize; }; // returns currently assembled message size
+ // - get byte statistics (only implemented in server so far)
+ virtual uInt32 getIncomingBytes(void) { return 0; };
+ virtual uInt32 getOutgoingBytes(void) { return 0; };
+ // - get how many bytes may not be used in the outgoing message buffer
+ // because of maxMsgSize restrictions
+ sInt32 getNotUsableBufferBytes(void);
+ // - get max size outgoing message may have (either defined by remote's maxmsgsize or local buffer space)
+ sInt32 getMaxOutgoingSize(void);
+ // - returns true if given number of bytes are transferable
+ // (not exceeding MaxMsgSize (in SyncML 1.0) or MaxObjSize (SyncML 1.1 and later)
+ bool dataSizeTransferable(uInt32 aDataBytes);
+ // - update outgoing message size
+ void incOutgoingMessageSize(sInt32 aIncrement) { fOutgoingMsgSize+=aIncrement; };
+ // - get message-global noResp status
+ bool getMsgNoResp(void) { return fMsgNoResp; }
+ // - get next outgoing command ID
+ sInt32 getNextOutgoingCmdID(void) { return (++fOutgoingCmdID); }
+ // - get next outgoing command ID without actually consuming it
+ sInt32 peekNextOutgoingCmdID(void) { return (fOutgoingCmdID+1); }
+ // - get current outgoing message ID
+ sInt32 getOutgoingMsgID(void) { return fOutgoingMsgID; }
+ // command handling
+ // - issue a command (and put it to status queue if it expects a result)
+ bool issue(TSmlCommand * &aSyncCommandP,
+ TSmlCommandPContainer &aNextMessageCommands,
+ TSmlCommand * &aInterruptedCommandP,
+ bool aNoResp=false, bool aIsOKSyncHdrStatus=false
+ );
+ bool issuePtr(TSmlCommand *aSyncCommandP, TSmlCommandPContainer &aNextMessageCommands,
+ TSmlCommand * &aInterruptedCommandP, bool aNoResp=false, bool aIsOKSyncHdrStatus=false);
+ // - issue a command in SyncBody context (uses session's interruptedCommand/NextMessageCommands)
+ bool issueRoot(TSmlCommand * &aSyncCommandP,
+ bool aNoResp=false, bool aIsOKSyncHdrStatus=false
+ );
+ bool issueRootPtr(TSmlCommand *aSyncCommandP,
+ bool aNoResp=false, bool aIsOKSyncHdrStatus=false
+ );
+ // queue a SyncBody context command for issuing after incoming message
+ // has been processed (and answers generated)
+ void queueForIssueRoot(
+ TSmlCommand * &aSyncCommandP // the command
+ );
+ // issue a command, but queue it if outgoing package has not begun yet
+ void issueNotBeforePackage(
+ TPackageStates aPackageState,
+ TSmlCommand *aSyncCommandP // the command
+ );
+ // - session continuation and status
+ void nextMessageRequest(void);
+ bool sessionMustContinue(void);
+ void delayExecUntilNextRequest(TSmlCommand *aCommand);
+ bool delayedSyncEndsPending(void) { return fDelayedExecSyncEnds>0; };
+ // - continue interrupted or prevented issue in next package
+ void ContinuePackageRoot(void);
+ void ContinuePackage(
+ TSmlCommandPContainer &aNextMessageCommands,
+ TSmlCommand * &aInterruptedCommandP
+ );
+ // - mark all pending items for a datastore for resume
+ // (those items that are in a session queue for being issued or getting status)
+ void markPendingForResume(TLocalEngineDS *aForDatastoreP);
+ void markPendingForResume(
+ TSmlCommandPContainer &aNextMessageCommands,
+ TSmlCommand *aInterruptedCommandP,
+ TLocalEngineDS *aForDatastoreP
+ );
+ // access to session info from commands
+ bool mustSendDevInf(void) { return false; }; // can be overridden to force devinf sending (e.g if list of synced fields has changed since last sync)
+ // - access DevInf (session owned)
+ SmlItemPtr_t getLocalDevInfItem(bool aAlertedOnly, bool aWithoutCTCapProps);
+ // - analyze devinf of remote party (can be derived to add client or server specific analysis)
+ virtual localstatus analyzeRemoteDevInf(
+ SmlDevInfDevInfPtr_t aDevInfP
+ );
+ // - get eventually cached devinf for specified device, passes ownership of
+ // created devinf structure to caller
+ // returns false if no devinf could be loaded
+ virtual bool loadRemoteDevInf(const char * /* aDeviceID */, SmlDevInfDevInfPtr_t & /* aDevInfP */) { return false; };
+ // - save devinf to cache for specified device
+ // return false if devinf cannot be cached
+ virtual bool saveRemoteDevInf(const char * /* aDeviceID */, SmlDevInfDevInfPtr_t /* aDevInfP */) { return false; };
+ // - finish outgoing Message, returns true if final message of package
+ bool FinishMessage(bool aAllowFinal, bool aForceNonFinal=false);
+ // - returns true if session has pending commands
+ bool hasPendingCommands(void);
+ // - incoming message processing aborted (EndMessage will not get called, clean up)
+ void CancelMessageProcessing(void);
+ // entry points for SyncML Toolkit callbacks
+ // - message handling
+ Ret_t StartMessage(SmlSyncHdrPtr_t aContentP);
+ Ret_t EndMessage(Boolean_t final);
+ // - grouping commands
+ Ret_t StartSync(SmlSyncPtr_t aContentP);
+ Ret_t EndSync(void);
+ #ifdef ATOMIC_RECEIVE
+ Ret_t StartAtomic(SmlAtomicPtr_t aContentP);
+ Ret_t EndAtomic(void);
+ #endif
+ #ifdef SEQUENCE_RECEIVE
+ Ret_t StartSequence(SmlSequencePtr_t aContentP);
+ Ret_t EndSequence(void);
+ #endif
+ // - commands
+ Ret_t AddCmd(SmlAddPtr_t aContentP);
+ Ret_t AlertCmd(SmlAlertPtr_t aContentP);
+ Ret_t DeleteCmd(SmlDeletePtr_t aContentP);
+ Ret_t GetCmd(SmlGetPtr_t aContentP);
+ Ret_t PutCmd(SmlPutPtr_t aContentP);
+ #ifdef MAP_RECEIVE
+ Ret_t MapCmd(SmlMapPtr_t aContentP);
+ #endif
+ #ifdef RESULT_RECEIVE
+ Ret_t ResultsCmd(SmlResultsPtr_t aContentP);
+ #endif
+ Ret_t StatusCmd(SmlStatusPtr_t aContentP);
+ Ret_t ReplaceCmd(SmlReplacePtr_t aContentP);
+ #ifdef COPY_RECEIVE
+ Ret_t CopyCmd(SmlReplacePtr_t aContentP);
+ #endif
+ Ret_t MoveCmd(SmlReplacePtr_t aContentP);
+ // - error handling
+ Ret_t HandleError(void);
+ Ret_t DummyHandler(const char* msg);
+ // - Other Callbacks: routed directly to appropriate session, error if none
+ #ifdef SYNCSTATUS_AT_SYNC_CLOSE
+ TStatusCommand *fSyncCloseStatusCommandP;
+ #endif
+ #ifdef SYDEBUG
+ // Message dump
+ void DumpSyncMLMessage(bool aOutgoing);
+ void DumpSyncMLBuffer(MemPtr_t aBuffer, MemSize_t aBufSize, bool aOutgoing, Ret_t aDecoderError);
+ uInt32 fDumpCount;
+ // XML translations of communication
+ // - recoding instances
+ InstanceID_t fOutgoingXMLInstance,fIncomingXMLInstance;
+ // - routines
+ void XMLTranslationIncomingStart(void);
+ void XMLTranslationOutgoingStart(void);
+ void XMLTranslationIncomingEnd(void);
+ void XMLTranslationOutgoingEnd(void);
+ // - flags
+ bool fXMLtranslate; // dump XML translation of SyncML traffic
+ bool fMsgDump; // dump raw SyncML messages
+ #endif
+ // log writing
+ #ifndef MINIMAL_CODE
+ void WriteLogLine(const char *aLogline);
+ bool logEnabled(void) { return fLogEnabled; };
+ #endif
+ // current database date & time (defaults to system time)
+ virtual lineartime_t getDatabaseNowAs(timecontext_t aContext) { return getSystemNowAs(aContext); };
+ // devinf
+ void remoteGotDevinf(void) { fRemoteGotDevinf=true; };
+ void remoteMustSeeDevinf(void) { fRemoteMustSeeDevinf=true; };
+ // config access
+ TRootConfig *getRootConfig(void);
+ // access to logging for session
+ #ifdef SYDEBUG
+ TDebugLogger *getDbgLogger(void) { return &fSessionLogger; };
+ uInt32 getDbgMask(void) { return fSessionDebugLogs ? fSessionLogger.getMask() : 0; };
+ #endif
+ // Remote-specific options, will be set up by checkClient/ServerSpecifics()
+ bool fLimitedRemoteFieldLengths; // if set, all fields will be assumed to have limited, but unknown field length (used for cut-off detection)
+ bool fDontSendEmptyProperties; // if set, no empty properties will be sent to client
+ bool fDoQuote8BitContent; // set if 8-bit chars should generally be encoded with QP in MIME-DIR
+ bool fDoNotFoldContent; // set if content should not be folded in MIME-DIR
+ bool fNoReplaceInSlowsync; // prevent replace commands totally at slow sync
+ bool fTreatRemoteTimeAsLocal; // treat remote time as localtime even if it carries different time zone information ("Z" suffix or zone spec)
+ bool fTreatRemoteTimeAsUTC; // treat remote time as UTC even if it carries different time zone information (no suffix or zone spec)
+ bool fVCal10EnddatesSameDay; // send end date-only values (like DTEND) as last time unit of previous day (i.e. 23:59:59, inclusive) instead of midnight of next day (exclusive, like in iCalendar 2.0)
+ bool fIgnoreDevInfMaxSize; // ignore <maxsize> specification in CTCap (when device has bad specs like in E90 for example)
+ bool fIgnoreCTCap; // ignore entire ctcap
+ bool fDSPathInDevInf; // use actual DS path as used in Alert for creating datastore devInf (needed for newer Nokia clients)
+ bool fDSCgiInDevInf; // also show CGI as used in Alert for creating datastore devInf (needed for newer Nokia clients)
+ bool fUpdateClientDuringSlowsync; // prevent updates of client records during non-first-time slow sync
+ bool fUpdateServerDuringSlowsync; // do not update server records during NON-FIRST-TIME slowsync (but do it for first sync!)
+ bool fAllowMessageRetries; // allow that client sends same message ID again (retry attempt)
+ bool fStrictExecOrdering; // if set (=default, SyncML standard requirement), statuses are sent in order of incoming commands (=execution is ordered)
+ bool fTreatCopyAsAdd; // treat copy commands as if they were adds
+ bool fCompleteFromClientOnly; // perform complete from-client-only session (non conformant, Synthesis before 2.9.8.2 style)
+ sInt32 fRequestMaxTime; // max time [seconds] allowed for processing a single request, 0=unlimited
+ sInt32 fRequestMinTime; // min time [seconds] spent until returning answer (for debug purposes, 0=no minimum time)
+ TCharSets fDefaultOutCharset; // default charset for MIME-DIR generation
+ #ifndef NO_REMOTE_RULES
+ TRemoteRuleConfig *fAppliedRemoteRuleP; // applied remote rule
+ #endif
+ // legacy mode
+ bool fLegacyMode; // if set, remote will see the types marked preferred="legacy" in devInf as preferred types, not the regular preferred ones
+ #ifdef EXPIRES_AFTER_DATE
+ // copy of scrambled now
+ sInt32 fCopyOfScrambledNow;
+ #endif
+ // Sync datastores
+ // - find local datastore by URI and separate identifying from optional part of URI
+ TLocalEngineDS *findLocalDataStoreByURI(const char *aURI,string *aOptions=NULL, string *aIdentifyingURI=NULL);
+ // - find local datastore by relative path (may not contain any CGI)
+ TLocalEngineDS *findLocalDataStore(const char *aDatastoreURI);
+ // - find local datastore by datastore handle (=config pointer)
+ TLocalEngineDS *findLocalDataStore(void *aDSHandle);
+ // - find remote datastore by (remote party specified) URI
+ TRemoteDataStore *findRemoteDataStore(const char *aDatastoreURI);
+ // Profiling
+ TP_DEFINFO(fTPInfo)
+ // access to config
+ TSessionConfig *getSessionConfig(void);
+ #ifdef SCRIPT_SUPPORT
+ // access to session script context
+ TScriptContext *getSessionScriptContext(void) { return fSessionScriptContextP; };
+ #endif
+ // unprotected options
+ // - set if we should send property lists in CTCap
+ bool fShowCTCapProps;
+ // - set if we should send type/size in CTCap for SyncML 1.0 (disabled by default as old clients like S55 crash on this)
+ bool fShowTypeSzInCTCap10;
+ // - set if we should show default parameter in mimo_old types as list of <propparam>s for each value
+ // Note that this is a tristate: 0=no, 1=yes, -1=auto (=yes for <SyncML 1.2, no for >=SyncML 1.2,
+ // thus making it work for Nokia 7610 (1.1) as well as E-Series like E90)
+ sInt8 fEnumDefaultPropParams;
+ #ifdef SYDEBUG
+ /// @todo
+ /// fSessionDebugLogs should be removed (but this needs rewriting of the XML and SML dumpers)
+ // - set if debug log for this session is enabled
+ bool fSessionDebugLogs;
+ #endif
+ #ifndef MINIMAL_CODE
+ // - se if normal log for this session is enabled
+ bool fLogEnabled; // real log file enabled
+ #endif
+ // - remote options (SyncML 1.1)
+ bool fRemoteWantsNOC; // remote wants number-of-changes info
+ bool fRemoteCanHandleUTC; // remote can handle UTC time
+ bool fRemoteSupportsLargeObjects; // remote can handle large object splitting/reassembly
+ // - object size handling
+ sInt16 fOutgoingCmds; // number of outgoing commands in message, but NOT counting SyncHdr status and Alert 222 status (which are ALWAYS there even in an otherwise empty message)
+ sInt32 fMaxRoomForData; // max room for data (free bytes available for data when startin a <sync> command)
+ // - Session user time context
+ timecontext_t fUserTimeContext;
+protected:
+ // Session control
+ // - terminate all datastores
+ void TerminateDatastores(localstatus aAbortStatusCode=408);
+ // - remove all datastores
+ void ResetAndRemoveDatastores(void);
+ // - session layer credential checking
+ bool checkCredentials(const char *aUserName, const SmlCredPtr_t aCredP, TStatusCommand &aStatusCommand);
+ bool checkCredentials(const char *aUserName, const char *aCred, TAuthTypes aAuthType);
+ // - session layer challenge
+ SmlChalPtr_t newSessionChallenge(void);
+ // datastore and type vars
+ // - list of local datastores
+ TLocalDataStorePContainer fLocalDataStores;
+ // - list of remote (client-side) datastores
+ TRemoteDataStorePContainer fRemoteDataStores;
+ // - list of local content types
+ TSyncItemTypePContainer fLocalItemTypes;
+ // - list of remote item types
+ TSyncItemTypePContainer fRemoteItemTypes;
+ // - Local Database currently targeted by a Sync command, NULL if none
+ // Note: This must be set correctly whenever sync commands (and </sync> syncend) are processed
+ // This can be during actual receiving them, OR while processing them from the fDelayedExecutionCommands
+ // queue.
+ TLocalEngineDS *fLocalSyncDatastoreP;
+ // set if we have received DevInf for remote DataStores / CTCap
+ bool fRemoteDevInfKnown; // remote devInf known
+ bool fRemoteDataStoresKnown; // data stores known
+ bool fRemoteDataTypesKnown; // CTCap known
+ bool fRemoteDevInfLock; // set after starting sync according to devInf we had to prevent in-sync devInf changes
+ // see if we have sent or should send DevInf to remote
+ bool fRemoteGotDevinf; // set if we sent a Put or Result containig DevInf
+ bool fRemoteMustSeeDevinf; // set if we should force (Put) devinf to remote
+ bool fCustomGetPutSent; // set if custom get/put has been sent to remote
+ // DevInf
+ // - get new sml list of all datastores (owner of list is transferred, but items are still owned by datastore
+ SmlDevInfDatastoreListPtr_t newDevInfDataStoreList(bool aAlertedOnly, bool aWithoutCTCapProps);
+ SmlDevInfCtcapListPtr_t newLocalCTCapList(bool aAlertedOnly, TLocalEngineDS *aOnlyForDS, bool aWithoutCTCapProps);
+ // - get new DevInf for this session (as Result for GET or item for PUT)
+ virtual SmlDevInfDevInfPtr_t newDevInf(bool aAlertedOnly, bool aWithoutCTCapProps);
+ // - called to issue custom get and put commands
+ virtual void issueCustomGetPut(bool aGotDevInf, bool aSentDevInf);
+ virtual void issueCustomEndPut(void);
+ // Sync processing
+ // - prepare for sending and receiving Sync commands
+ localstatus initSync(
+ const char *aLocalDatastoreURI,
+ const char *aRemoteDatastoreURI
+ );
+ // Command processing
+ // - process a command (analyze and execute it),
+ // exception-free for simple call from smlCallback adaptors
+ Ret_t process(TSmlCommand *aSyncCommandP);
+ // - handle incoming status
+ // exception-free for simple call from smlCallback adaptors
+ Ret_t handleStatus(TStatusCommand *aStatusCommandP);
+ // helpers for derived classes
+ // - create, send and delete SyncHeader "command"
+ void issueHeader(bool aNoResp=false);
+ // - process the SyncHeader "command"
+ Ret_t processHeader(TSyncHeader *aSyncHdrP);
+ // Helpers for commands
+ // - create new SyncHdr structure for TSyncHeader command
+ // (here because all data for this is in session anyway)
+ SmlSyncHdrPtr_t NewOutgoingSyncHdr(bool aOutgoingNoResp=false);
+ // virtuals for overriding in specialized session derivates
+ // - device ID must be handled on session level as it might depend on session runtime conditions
+ // (like special pseudo-unique ID for Oracle servers when basic id is not unique etc.)
+ virtual string getDeviceID(void)=0;
+ virtual string getDeviceType(void)=0; // abstract, must be client or server
+ // - get new response URI to be sent to remote party for subsequent messages TO local party
+ virtual SmlPcdataPtr_t newResponseURIForRemote(void) { return NULL; }; // no RespURI by default
+ // - URI to send outgoing message to
+ virtual const char *getSendURI(void) { return ""; }; // none by default (and server)
+ // Authorisation
+ // - required authentication type and mode
+ virtual TAuthTypes requestedAuthType(void) = 0; // get preferred authentication type for authentication of remote party
+ virtual bool isAuthTypeAllowed(TAuthTypes aAuthType) = 0; // test if auth type is allowed for authentication by remote party
+ virtual bool messageAuthRequired(void) { return false; }; // no message-by-message auth by default
+ // - get credentials/username to authenticate with remote party, NULL if none
+ virtual SmlCredPtr_t newCredentialsForRemote(void) { return NULL; }; // normally (server case), none
+ virtual const char * getUsernameForRemote(void) { return NULL; }; // normally (server case), none
+ // - generate credentials (based on fRemoteNonce, fRemoteRequestedAuth, fRemoteRequestedAuthEnc)
+ SmlCredPtr_t newCredentials(const char *aUser, const char *aPassword);
+public:
+ // - get common sync capabilities mask of this session (datastores might modify it)
+ virtual uInt32 getSyncCapMask(void);
+ // - check credentials, login to server
+ virtual bool SessionLogin(const char *aUserName, const char *aAuthString, TAuthSecretTypes aAuthStringType, const char *aDeviceID);
+protected:
+ // - get next nonce string top be sent to remote party for subsequent MD5 auth
+ virtual void getNextNonce(const char * /* aDeviceID */, string &aNextNonce) { aNextNonce.erase(); }; // empty nonce
+public:
+ // - get nonce string for specified device
+ virtual void getAuthNonce(const char * /* aDeviceID */, string &aAuthNonce) { aAuthNonce.erase(); };
+ // - check auth helpers
+ bool checkAuthPlain(
+ const char *aUserName, const char *aPassWord, const char *aNonce, // given values
+ const char *aAuthString, TAuthSecretTypes aAuthStringType // check against this
+ );
+ bool checkAuthMD5(
+ const char *aUserName, const char *aMD5B64, const char *aNonce, // given values
+ const char *aAuthString, TAuthSecretTypes aAuthStringType // check against this
+ );
+ bool checkMD5WithNonce(
+ const char *aStringBeforeNonce, const char *aNonce, // given input
+ const char *aMD5B64Creds // credential string to check
+ );
+protected:
+ // - helper functions (for use be derived classes)
+ bool getAuthBasicUserPass(const char *aBasicCreds, string &aUsername, string &aPassword);
+ // - load remote connect params (syncml version, type, format and last nonce)
+ // Note: agents that can cache this information between sessions will load
+ // last info here.
+ virtual void loadRemoteParams(void)
+ { fSyncMLVersion=syncml_vers_unknown; fRemoteRequestedAuth=auth_none; fRemoteRequestedAuthEnc=fmt_chr; fRemoteNonce.erase(); }; // static defaults
+ // - save remote connect params for use in next session (if descendant implements it)
+ virtual void saveRemoteParams(void) { /* nop */ };
+ // - Session level meta
+ virtual SmlPcdataPtr_t newHeaderMeta(void);
+ // - check remote devinf to detect special behaviour needed for some clients. Base class
+ // does not do anything on server level (configured rules are handled at session level)
+ virtual localstatus checkRemoteSpecifics(SmlDevInfDevInfPtr_t aDevInfP);
+ // - remote device is analyzed, eventually save status
+ virtual void remoteAnalyzed(void) { /* nop */ };
+ // SyncML Toolkit interface
+ InstanceID_t fSmlWorkspaceID; // SyncML toolkit workspace instance ID
+ SmlEncoding_t fEncoding; // Current encoding type in SyncML toolkit instance
+ // Session custom time zones
+ GZones fSessionZones;
+ // session timing
+ lineartime_t fSessionLastUsed; // time when session was last used
+ lineartime_t fSessionStarted; // time when session was started
+ lineartime_t fLastRequestStarted; // time when last request was received
+ // session busy status (used for session count limiting normally)
+ bool fSessionIsBusy;
+ // session type
+ // - SyncML protocol version
+ TSyncMLVersions fSyncMLVersion;
+ // - outgoing authorisation
+ TAuthTypes fRemoteRequestedAuth; // type of auth requested by the remote
+ TFmtTypes fRemoteRequestedAuthEnc; // type of encoding requested by the remote
+ string fRemoteNonce; // next nonce to be used to authenticate with remote
+ bool fNeedAuth; // set if we need to authorize to remote for next message
+ // session ID vars
+ string fSynchdrSessionID; // SyncML-protocol ID of this sync session (client generated)
+ string fLocalURI; // local party URI
+ string fInitialLocalURI; // local URI used in first message (or preconfigured with <externalurl>)
+ string fLocalName; // local party optional name
+ string fRemoteURI; // remote party URI (remote deviceID or URL)
+ string fRemoteName; // remote party optional name
+ string fRespondURI; // remote party URI to send response to
+ string fLocalSessionID; // locally generated session ID (server generated)
+ #ifndef MINIMAL_CODE
+ string fRemoteDescName; // descriptive name of remote (set from DevInf and probably adjusted by remoterule)
+ string fRemoteInfoString; // remote party information string (from DevInf)
+ string fSyncUserName; // remote user name
+ // 1:1 devInf details
+ string fRemoteDevInf_devid;
+ string fRemoteDevInf_devtyp;
+ string fRemoteDevInf_mod;
+ string fRemoteDevInf_man;
+ string fRemoteDevInf_oem;
+ string fRemoteDevInf_swv;
+ string fRemoteDevInf_fwv;
+ string fRemoteDevInf_hwv;
+ #endif
+ #ifdef SCRIPT_SUPPORT
+ // Session level script context
+ TScriptContext *fSessionScriptContextP;
+ // Active RemoteRule's scipt. Note that this is copied from the config
+ // as it might differ from session to session (and can't be resolved in
+ // the config globally)
+ string fRuleScript; // rule script, copied from active RemoteRule
+ #endif
+ // Session options
+ bool fReadOnly;
+ // Session state vars
+ // - incoming authorisation
+ bool fSessionAuthorized; // session is (permanently) authorized, that is, further messages do not need authorization
+ bool fMessageAuthorized; // this message is authorized
+ sInt16 fAuthFailures; // count of failed authentication attempts by remote in a row (normally, server case), will cause abort if too many
+ sInt16 fAuthRetries; // count of failed authentication attempts by myself at remote (normally, client case)
+ // - session state
+ TPackageStates fIncomingState; // incoming package state
+ TPackageStates fCmdIncomingState; // while executing commands: state when command was received (actual might be different due to queueing)
+ TPackageStates fOutgoingState; // outgoing package state
+ bool fFakeFinalFlag; // special flag to work around broken resume implementations
+ bool fNewOutgoingPackage; // set if first outgoing message in outgoing package
+ bool fNeedToAnswer; // set if an answer to currently processed message is needed (will be set by issuing of first non-synchdr-status)
+ sInt32 fIncomingMsgID; // last incoming message ID (0 if none received yet)
+ sInt32 fOutgoingMsgID; // last outgoing message ID (0 if none sent yet)
+ bool fMessageRetried; // if set (by TSyncHeader::execute()) we have received a retried message and should resend the last answer
+ bool fAborted; // if set, session is being aborted (and will be deleted at EndRequest)
+ bool fSuspended; // if set, session is being suspended (stopped processing commands, will send Suspend Alert to remote at next opportunity)
+ uInt16 fFailedDatastores;
+ uInt16 fErrorItemDatastores;
+ TSyError fAbortReasonStatus; // if fAborted, this contains a status code what command has aborted the session
+ bool fLocalAbortReason; // if aborted, this signals if aborted due to local or remote reason
+ bool fInProgress; // if set, session is in progress and must persist beyond this request
+ // incoming Message status
+ bool fMsgNoResp; // if set, current message MUST not be responded to. Suppresses all status sendig attempts
+ bool fIgnoreIncomingCommands; // if set, commands dispatched will be ignored
+ TSyError fStatusCodeForIgnored; // if fIgnoreIncomingCommands is set, this status code will be used to reply all incoming commands
+ // - incoming data from a <moredata> split data item
+ TSyncOpCommand *fIncompleteDataCommandP;
+ // outgoing message status
+ sInt32 fOutgoingCmdID; // last outgoing command ID (0 if none generated yet)
+ bool fOutgoingStarted; // started preparing an outgoing message
+ bool fOutgoingNoResp; // outgoing message does not want response at all
+ // termination flag - set when TerminateSession() has finished executing
+ bool fTerminated; // session is terminated (finally, not restartable!)
+private:
+ // debug logging
+ #ifdef SYDEBUG
+ TDebugLogger fSessionLogger; // the logger
+ #endif
+ // internal vars
+ TSyncAppBase *fSyncAppBaseP; // the owning application base (dispatcher/client base)
+ /* %%% prepared, to be implemented. Currently constant limits
+ sInt32 fMaxIncomingMsgSize; // limit for incoming message, if<>0, causes MaxMsgSize Meta on outgoing SyncHdr
+ sInt32 fMaxIncomingObjSize; // limit for incoming objects, if<>0, causes MaxObjSize Meta on outgoing SyncHdr
+ */
+ sInt32 fMaxOutgoingMsgSize; // max size of outgoing message, 0 if unlimited
+ sInt32 fMaxOutgoingObjSize; // SyncML 1.1: max size of outgoing object, 0 if unlimited
+ sInt32 fOutgoingMsgSize; // current size of outgoing message
+ bool fOutgoingMessageFull; // outgoing message is full, message must be finished and sent
+ // context-free command queues
+ // - sent commands waiting for status
+ TSmlCommandPContainer fStatusWaitCommands;
+ // - received commands that could not be executed immediately
+ TSmlCommandPContainer fDelayedExecutionCommands;
+ sInt32 fDelayedExecSyncEnds;
+ // - commands that must be queued until SyncHdr is generated
+ TSmlCommandPContainer fHeaderWaitCommands;
+ // SyncBody-context command queues
+ // - commands to be issued only after all commands in this message have
+ // been processed and answered by a status
+ TSmlCommandPContainer fEndOfMessageCommands;
+ // - commands waiting for being sent in next outgoing message
+ TSmlCommandPContainer fNextMessageCommands;
+ // - commands waiting for being sent in next outgoing package
+ TSmlCommandPContainer fNextPackageCommands;
+ // - outgoing command that was interrupted by end of message and must be continued in next message
+ TSmlCommand *fInterruptedCommandP;
+ // - counter that gets incremented once per Alert 222 and decremented when package contents get sent
+ uInt32 fNextMessageRequests;
+ // - sequence nesting level
+ sInt32 fSequenceNesting;
+}; // TSyncSession
+
+
+// macros
+#define ISSUE_COMMAND(sp,c,l1,l2) { TSmlCommand* p=c; c=NULL; sp->issuePtr(p,l1,l2); }
+#define ISSUE_COMMAND_ROOT(sp,c) { TSmlCommand* p=c; c=NULL; sp->issueRootPtr(p); }
+
+
+#ifdef ENGINEINTERFACE_SUPPORT
+
+// Support for EngineModule common interface
+// =========================================
+
+// session runtime parameters (such as access to session script vars)
+class TSessionKey :
+ public TStructFieldsKey
+{
+ typedef TStructFieldsKey inherited;
+public:
+ TSessionKey(TEngineInterface *aEngineInterfaceP, TSyncSession *aSessionP) :
+ inherited(aEngineInterfaceP),
+ fSessionP(aSessionP)
+ {};
+ virtual ~TSessionKey() {};
+
+protected:
+ // open subkey by name (not by path!)
+ // - this is the actual implementation
+ virtual TSyError OpenSubKeyByName(
+ TSettingsKeyImpl *&aSettingsKeyP,
+ cAppCharP aName, stringSize aNameSize,
+ uInt16 aMode
+ );
+
+ // the associated sync session
+ TSyncSession *fSessionP;
+}; // TSessionKey
+
+#endif // ENGINEINTERFACE_SUPPORT
+
+} // namespace sysync
+
+
+#endif // SYNC_SESSION_H
+
+
+// eof
diff --git a/src/sysync/syncsessiondispatch.cpp b/src/sysync/syncsessiondispatch.cpp
new file mode 100755
index 0000000..b0204f7
--- /dev/null
+++ b/src/sysync/syncsessiondispatch.cpp
@@ -0,0 +1,888 @@
+/*
+ * TSyncSessionDispatch
+ * Global object, manages instantiation and removal of
+ * TSyncSession objects, connects requests to sessions,
+ * Interfaces between C-coded SyncML toolkit and SySync
+ * C++ framework.
+ *
+ * Copyright (c) 2001-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ * 2001-05-07 : luz : Created
+ *
+ */
+
+#include "prefix_file.h"
+
+// %%%% Note: CW8.2 has bugs in SEH when optimization is turned on (even if we don't use SEH
+// here, we might call routines with SEH so be careful here)
+#pragma optimization_level 0
+
+#include "sysync.h"
+#include "syncsessiondispatch.h"
+
+
+
+// TSyncSessionHandle
+// ==================
+
+// constructor
+TSyncSessionHandle::TSyncSessionHandle(TSyncAppBase *aAppBaseP) :
+ fAppBaseP(aAppBaseP),
+ fSessionP(NULL),
+ fOutdated(false)
+{
+ OBJDEBUGPRINTFX(fAppBaseP,DBG_OBJINST,("++++++++ TSyncSessionHandle created"));
+ MP_SHOWCURRENT(DBG_OBJINST,"TSyncSessionHandle created");
+ // nop
+} // TSyncSessionHandle::TSyncSessionHandle
+
+
+// destructor
+TSyncSessionHandle::~TSyncSessionHandle()
+{
+ if (fSessionP) {
+ POBJDEBUGPRINTFX(fAppBaseP,DBG_ERROR,("TSyncSessionHandle deleted with unterminated session (deleting now as well...)"));
+ MP_DELETE(DBG_OBJINST,"fSessionP",fSessionP);
+ }
+ fSessionP=NULL;
+ MP_SHOWCURRENT(DBG_OBJINST,"TSyncSessionHandle deleting");
+ OBJDEBUGPRINTFX(fAppBaseP,DBG_OBJINST,("-------- TSyncSessionHandle destroyed"));
+} // TSyncSessionHandle::~TSyncSessionHandle
+
+
+
+// terminate and delete session. Must protect caller from exceptions
+// when termination/deletion fails.
+// NOTE: keeps session Enter()ed, to avoid any other thread to enter.
+bool TSyncSessionHandle::EnterAndTerminateSession(uInt16 aStatusCode)
+{
+ // no locks here, so just call TerminateSession
+ return TerminateSession(aStatusCode);
+} // TSyncSessionHandle::EnterAndTerminateSession
+
+
+
+// terminate and delete session. Must protect caller from exceptions
+// when termination/deletion fails.
+// NOTE: keeps session Enter()ed, to avoid any other thread to enter.
+bool TSyncSessionHandle::TerminateSession(uInt16 aStatusCode)
+{
+ if (fSessionP) {
+ // - Set abort code if not zero (not aborted, regular termination)
+ if (aStatusCode) fSessionP->AbortSession(aStatusCode,true); // assume local cause
+ // - terminate the session, which causes datastores to be terminated as well
+ fSessionP->TerminateSession();
+ // - now actually delete
+ TSyncSession *delP = fSessionP;
+ fSessionP=NULL; // make sure we can't delete again under any circumstance
+ delete delP;
+ POBJDEBUGPRINTFX(fAppBaseP,DBG_TRANSP,("TerminateSession(%hd): deleted session successfully", aStatusCode));
+ }
+ else {
+ POBJDEBUGPRINTFX(fAppBaseP,DBG_TRANSP+DBG_EXOTIC,("TerminateSession(%hd) - no op, session was already terminated/deleted before", aStatusCode));
+ }
+ return true;
+} // TSyncSessionHandle::TerminateSession
+
+
+// TSyncSessionDispatch
+// ====================
+
+
+// Support for SySync Diagnostic Tool
+#ifdef SYSYNC_TOOL
+
+// get or create a session for use with the diagnostic tool
+TSyncServer *TSyncSessionDispatch::getSySyToolSession(void)
+{
+ TSyncServer *sessionP=NULL; // the session (new or existing found in fSessions)
+
+ if (fToolSessionHP) {
+ sessionP = fToolSessionHP->fSessionP;
+ }
+ else {
+ // - create session object with given ID
+ fToolSessionHP = CreateSessionHandle();
+ sessionP = static_cast<TServerConfig *>(fConfigP->fAgentConfigP)->CreateServerSession(fToolSessionHP,"SySyTool");
+ fToolSessionHP->fSessionP=sessionP;
+ }
+ return sessionP;
+}
+
+#endif // SYSYNC_TOOL
+
+
+
+// constructor
+TSyncSessionDispatch::TSyncSessionDispatch() :
+ TSyncAppBase()
+{
+ #ifdef SYSYNC_TOOL
+ fToolSessionHP=NULL; // no tool session yet
+ #endif
+} // TSyncSessionDispatch::TSyncSessionDispatch
+
+
+
+// immediately kill all sessions
+void TSyncSessionDispatch::TerminateAllSessions(uInt16 aStatusCode)
+{
+ try {
+ TSyncSessionHandlePContainer::iterator pos;
+ for (pos=fSessions.begin(); pos!=fSessions.end(); ++pos) {
+ if ((pos->second)->fSessionP) {
+ (pos->second)->fSessionP->AbortSession(aStatusCode,true);
+ (pos->second)->fSessionP->ResetSession(); // reset properly before deleting
+ // delete the session handle including the session
+ MP_DELETE(DBG_OBJINST,"leftover session",(pos->second));
+ }
+ }
+ // delete the session list
+ fSessions.clear();
+ }
+ catch (...)
+ {
+ }
+} // TSyncSessionDispatch::TerminateAllSessions
+
+
+
+
+
+// destructor
+TSyncSessionDispatch::~TSyncSessionDispatch()
+{
+ fDeleting=true; // flag deletion to block calling critical (virtual) methods
+ // delete all session handles
+ // NOTE: fSessionslock is already gone as it is owned by
+ // derived class
+ TerminateAllSessions(500); // server error
+ #ifdef SYSYNC_TOOL
+ // delete tool session
+ if (fToolSessionHP) {
+ fToolSessionHP->fSessionP->ResetSession();
+ MP_DELETE(DBG_OBJINST,"leftover session",(fToolSessionHP));
+ fToolSessionHP=NULL;
+ }
+ #endif
+} // TSyncSessionDispatch::~TSyncSessionDispatch
+
+
+
+// Called from SyncML toolkit when a new SyncML message arrives
+// - finds appropriate session or creates new one
+// - dispatches to session's StartMessage
+Ret_t TSyncSessionDispatch::StartMessage(
+ InstanceID_t aSmlWorkspaceID, // SyncML toolkit workspace instance ID
+ VoidPtr_t aUserData, // user data, contains NULL or char* to transport-layer supported session ID
+ SmlSyncHdrPtr_t aContentP // SyncML tookit's decoded form of the <SyncHdr> element
+) {
+ TSyncServer *sessionP=NULL; // the session (new or existing found in fSessions)
+ TSyncSessionHandle *sessionHP=NULL;
+ Ret_t err;
+
+ try {
+ // obtain session (existing or new)
+ // - lock access to list
+ // NOTE: Basic policy is to keep sessions locked only while executing code
+ // that cannot throw a SE or a C++ exception, as unlocking too much
+ // (in a general exception catcher) makes much troubles. All Code
+ // that might throw should be executed ONLY with session list released!
+ LockSessions();
+ // - now get iterator
+ TSyncSessionHandlePContainer::iterator pos=fSessions.end();
+ // - get SessionID, either from transport (aUserData) or from TargetURI
+ string sessionID;
+ AssignString(sessionID,(const char *)aUserData);
+ if (!sessionID.empty()) {
+ // - transport has provided a sessionID
+ PDEBUGPRINTFX(DBG_PROTO,(
+ "TSyncSessionDispatch::StartMessage called with transport layer session ID='%s'",
+ sessionID.c_str()
+ ));
+ }
+ else {
+ // - try to get sessionID from TargetURI
+ if (extractSessionID(smlSrcTargLocURIToCharP(aContentP->target),sessionID)) {
+ PDEBUGPRINTFX(DBG_PROTO,(
+ "TSyncSessionDispatch::StartMessage found session ID in Target.LocURI='%s'",
+ sessionID.c_str()
+ ));
+ }
+ }
+ // - process session ID
+ if (!sessionID.empty()) {
+ // we seem to have a session ID
+ pos=fSessions.find(sessionID);
+ #ifdef SYDEBUG
+ if (pos!=fSessions.end()) {
+ PDEBUGPRINTFX(DBG_HOT,(
+ "Session found by SessionID=%s",
+ sessionID.c_str()
+ ));
+ }
+ #endif
+ }
+ // now enter existing session or create new one
+ if (pos!=fSessions.end()) {
+ // found existing session:
+ // - get handle
+ sessionHP = (*pos).second;
+ // - get session object pointer
+ sessionP = sessionHP->fSessionP;
+ PDEBUGPRINTFX(DBG_SESSION,("Entering found session..."));
+ // Note: We have the session list locked, so we should not wait here for entering to avoid locking the entire server
+ // - try entering
+ int k=0;
+ while (true) {
+ if (sessionHP->EnterSession(0))
+ break; // successfully entered
+ // could not enter. Temporarily release sessions list and wait 10 seconds
+ ReleaseSessions();
+ if (k>60) {
+ // 60*10 = 600 sec = 10 min waited, give up
+ PDEBUGPRINTFX(DBG_ERROR,("Found session is locked for >%d seconds, giving up -> SML_ERR_UNSPECIFIC",k*10));
+ return SML_ERR_UNSPECIFIC;
+ }
+ PDEBUGPRINTFX(DBG_LOCK,("Session could not be entered after waiting %d seconds, keep trying",k*10));
+ // wait
+ sleepLineartime(10*secondToLinearTimeFactor);
+ // we need to re-find the session here, as we had given others control over the session list
+ LockSessions();
+ pos=fSessions.find(sessionID);
+ if (pos==fSessions.end()) {
+ ReleaseSessions();
+ PDEBUGPRINTFX(DBG_HOT,(
+ "SessionID=%s has disappeared from sessions list while we were waiting to enter it --> SML_ERR_UNSPECIFIC",
+ sessionID.c_str()
+ ));
+ return SML_ERR_UNSPECIFIC;
+ }
+ // - get handle again
+ sessionHP = (*pos).second;
+ k++;
+ }
+ // Session is entered, so we can release the list (no other thread will be able to enter same session)
+ ReleaseSessions();
+ // Show that we entered the session
+ PDEBUGPRINTFX(DBG_SESSION,("Session entered"));
+ } // if session already exists
+ else {
+ // we need a new session
+ // Note: session list is still locked here
+ PDEBUGPRINTFX(DBG_TRANSP+DBG_EXOTIC,("No session found, will need new one"));
+ // - count sessions not belonging to this client
+ sInt32 othersessioncount = 0;
+ for (pos=fSessions.begin();pos!=fSessions.end();pos++) {
+ // - get handle
+ sessionHP = (*pos).second;
+ // - get session
+ sessionP=sessionHP->fSessionP;
+ // check if session is for same device as current request
+ if (strcmp(smlSrcTargLocURIToCharP(aContentP->source),sessionP->getRemoteURI())!=0) {
+ // other device, count if not outdated
+ if (!sessionHP->fOutdated) othersessioncount++;
+ }
+ else {
+ // this device, set outdated flag to prevent inactive sessions from being
+ // counted and limiting sessions
+ sessionHP->fOutdated=true;
+ }
+ } // for
+ PDEBUGPRINTFX(DBG_SESSION+DBG_EXOTIC,(
+ "Found %ld sessions not related to device '%s'",
+ (sInt32)othersessioncount,
+ smlSrcTargLocURIToCharP(aContentP->source)
+ ));
+ // - get sessions that need to be deleted
+ TSyncSessionHandlePList delList;
+ collectTimedOutSessions(delList);
+ // - now we can release the sessions list
+ ReleaseSessions();
+ // - actually delete them while session list is again unlocked
+ deleteListedSessions(delList);
+ // Now create new session
+ sessionHP = CreateAndEnterServerSession(NULL); // have sessionID generated
+ // - get session object pointer
+ sessionP = sessionHP->fSessionP;
+ #ifdef CONCURRENT_DEVICES_LIMIT
+ // - now check session count (makes session busy if licensed session count is exceeded)
+ // Note: othersessioncount does not include our own session
+ checkSessionCount(othersessioncount,sessionP);
+ #endif
+ }
+ // now we should have a sessionP
+ if (sessionP) {
+ // Now let session handle SyncML header (start of message)
+ // - sessionP and sessionHP are valid here
+ // - assign pointers to allow direct
+ // routing and calling between session and SyncML toolkit
+ err=setSmlInstanceUserData(aSmlWorkspaceID,sessionP); // toolkit must know session (as userData)
+ if (err!=SML_ERR_OK) throw(TSmlException("setSmlInstanceUserData",err));
+ // - let session know workspace ID
+ sessionP->setSmlWorkspaceID(aSmlWorkspaceID); // session must know toolkit workspace
+ // let session handle details of StartMessage callback
+ return sessionP->StartMessage(aContentP);
+ }
+ else {
+ PDEBUGPRINTFX(DBG_HOT,(
+ "No session could be created --> SML_ERR_UNSPECIFIC",
+ sessionID.c_str()
+ ));
+ return SML_ERR_UNSPECIFIC;
+ }
+ }
+ catch (exception &e) {
+ return HandleDecodingException(sessionP,"StartMessage",&e);
+ }
+ catch (...) {
+ return HandleDecodingException(sessionP,"StartMessage",NULL);
+ }
+} // TSyncSessionDispatch::StartMessage
+
+
+
+/// @brief Collect timed-out sessions and remove them from the session list
+/// @param aDeletableSessions to-be deleted sessions will be appended to this list
+/// @note session list must be locked before call!
+void TSyncSessionDispatch::collectTimedOutSessions(TSyncSessionHandlePList &aDeletableSessions)
+{
+ TSyncServer *sessionP=NULL; // the session (new or existing found in fSessions)
+ TSyncSessionHandle *sessionHP=NULL;
+ TServerConfig *serverconfigP=NULL;
+
+ // get agent config
+ GET_CASTED_PTR(serverconfigP,TServerConfig,fConfigP->fAgentConfigP,"missing agent (server) config");
+ // - find timed-out sessions and count sessions not belonging to this client
+ TSyncSessionHandlePContainer::iterator pos;
+ for (pos=fSessions.begin();pos!=fSessions.end();pos++) {
+ // - get handle
+ sessionHP = (*pos).second;
+ // - get session
+ sessionP=sessionHP->fSessionP;
+ // - check
+ if (getSystemNowAs(TCTX_UTC) > sessionP->getSessionLastUsed()+serverconfigP->getSessionTimeout()) {
+ // this session is too old, queue it for killing
+ aDeletableSessions.push_back(sessionHP);
+ }
+ } // for
+ // now remove the outdated sessions from the main session list
+ // to make sure no other process can possibly access them
+ // - session list is locked once here
+ TSyncSessionHandlePList::iterator delpos;
+ DEBUGPRINTFX(DBG_SESSION,(
+ "Now removing %ld outdated sessions from the session list",
+ (sInt32)aDeletableSessions.size()
+ ));
+ for (delpos=aDeletableSessions.begin();delpos!=aDeletableSessions.end();delpos++) {
+ // - get handle
+ sessionHP = (*delpos);
+ // - remove session handle from the list
+ // NOTE: session list is already locked here, RemoveSession
+ // MUST be called with locked session list!
+ if (!(sessionHP == RemoveSession(sessionHP->fSessionP)))
+ throw (TSyncException("invalid linked session/sessionhandle pair"));
+ }
+} // collectTimedOutSessions
+
+
+/// @brief Try to enter and delete the sessions passed in one by one
+/// @note
+/// - listed sessions must not be part of the sessions list any more
+/// - on deletion failure, "zombie" sessions will be re-inserted into the session list (to get deleted once again later)
+/// - intended for implementations without a session thread (XPT, ISAPI, not pipe)
+void TSyncSessionDispatch::deleteListedSessions(TSyncSessionHandlePList &aDelSessionList)
+{
+ TSyncServer *sessionP=NULL;
+ TSyncSessionHandle *sessionHP=NULL;
+ TServerConfig *serverconfigP=NULL;
+
+ // get agent config
+ GET_CASTED_PTR(serverconfigP,TServerConfig,fConfigP->fAgentConfigP,"missing agent (server) config");
+
+ TSyncSessionHandlePList::iterator delpos;
+ for (delpos=aDelSessionList.begin();delpos!=aDelSessionList.end();delpos++) {
+ try {
+ // - get handle and session
+ sessionHP = (*delpos);
+ sessionP = sessionHP->fSessionP;
+ if (!sessionP) continue; // seems to be already deleted
+ // - terminate the session
+ #ifdef SYDEBUG
+ string deletedsessionid = sessionP->getLocalSessionID();
+ #endif
+ PDEBUGPRINTFX(DBG_HOT,(
+ "Terminating timed-out (older than %lld milliseconds) session '%s'...",
+ serverconfigP->getSessionTimeout(),
+ sessionP->getLocalSessionID()
+ ));
+ // - now actually delete
+ if (sessionHP->EnterAndTerminateSession(408)) { // request timeout
+ PDEBUGPRINTFX(DBG_PROTO,("Terminated and deleted timed-out session '%s'",deletedsessionid.c_str()));
+ // - delete session handle itself
+ delete sessionHP;
+ } else {
+ PDEBUGPRINTFX(DBG_ERROR,("Could NOT properly terminate/delete timed-out session '%s' now --> becomes a ZOMBIE",deletedsessionid.c_str()));
+ // We cannot safely get rid of this session now, so we must put it back to the queue
+ if (sessionHP->fSessionP) {
+ // make it used again, so it will timeout later again
+ sessionHP->fSessionP->SessionUsed();
+ // re-insert into queue
+ LockSessions();
+ // however, if we now see that there is NO other session running, we'll risk crashing here and delete the session
+ if (fSessions.size()==0) {
+ // we'll do no harm to other sessions because there are none - try to hard-kill the session
+ PDEBUGPRINTFX(DBG_ERROR,("No non-ZOMBIE sessions running -> risking to delete this ZOMBIE",deletedsessionid.c_str()));
+ delete sessionHP;
+ }
+ else {
+ fSessions[deletedsessionid]=sessionHP;
+ PDEBUGPRINTFX(DBG_HOT,(
+ "ZOMBIE session ID='%s' is now again in session list, waiting once more for timeout and hopefully clean deletion later",
+ deletedsessionid.c_str()
+ ));
+ }
+ ReleaseSessions();
+ }
+ else {
+ PDEBUGPRINTFX(DBG_ERROR,("Termination failed, but sessionP gone nevertheless"));
+ }
+ }
+ }
+ catch (...) {
+ PDEBUGPRINTFX(DBG_ERROR,("******** Exception while trying to kill outdated session"));
+ }
+ } // for
+} // deleteListedSessions
+
+
+/// Create new session, clean up timed-out sessions
+/// @param aPredefinedSessionID : predefined sessionID, if NULL, internal ID will be generated
+TSyncSessionHandle *TSyncSessionDispatch::CreateAndEnterServerSession(cAppCharP aPredefinedSessionID)
+{
+ TSyncServer *sessionP=NULL; // the session (new or existing found in fSessions)
+ TSyncSessionHandle *sessionHP=NULL;
+ TServerConfig *serverconfigP=NULL;
+
+ // - create new session instance
+ // NOTE: session list is unlocked here already
+ try {
+ #ifdef APP_CAN_EXPIRE
+ // Check expiry (note that this check is for a server running without restart)
+ // - get current time
+ lineartime_t nw = getSystemNowAs(TCTX_UTC);
+ #ifdef EXPIRES_AFTER_DATE
+ #ifdef SYSER_REGISTRATION
+ if (!fRegOK) // only abort if no registration (but accept timed registration)
+ #endif
+ {
+ sInt16 y,mo,d;
+ lineartime2date(nw,&y,&mo,&d);
+ // - calculate scrambled version thereof
+ fScrambledNow=
+ (y-1720)*12*42+
+ (mo-1)*42+
+ (d+7);
+ if (fScrambledNow>SCRAMBLED_EXPIRY_VALUE)
+ fAppExpiryStatus = LOCERR_EXPIRED; // hard expiry
+ }
+ #endif
+ #ifdef SYSER_REGISTRATION
+ // - check if timed license has expired
+ if (fRegDuration && date2lineartime(fRegDuration/12+2000,fRegDuration%12+1,1)<nw)
+ fAppExpiryStatus = LOCERR_EXPIRED; // timed license expiry
+ #endif
+ #endif
+ // - create a session handle
+ PDEBUGPRINTFX(DBG_PROTO,("Now creating new session"));
+ sessionHP = CreateSessionHandle();
+ // - enter processing for this session (will be left at EndRequest)
+ if (!sessionHP->EnterSession(1000)) {
+ PDEBUGPRINTFX(DBG_ERROR,("New created session cannot be entered"));
+ return NULL;
+ }
+ try {
+ // Determine session ID now
+ string SessionIDString;
+ if (aPredefinedSessionID) {
+ SessionIDString=aPredefinedSessionID;
+ }
+ else {
+ // - create unique server-side session ID
+ // format = aaaabbbbccccdddd
+ // - aaaa = low word of time(NULL) >> 1 (to make sure MSB is cleared)
+ // - dddd = high word of time(NULL)
+ // - bbbbcccc = memory address of session handle
+ uInt64 sid =
+ time(NULL);
+ sid =
+ ((sid >> 16) & 0xFFFF) + ((sid << 47) & 0x7FFF000000000000LL) + // aaaa00000000dddd
+ (((uInt32)sessionHP) << 16); // 0000bbbbcccc0000
+ // - make a string of it
+ StringObjPrintf(SessionIDString,"%lld",sid);
+ }
+ #ifdef CONCURRENT_DEVICES_LIMIT
+ // some minor arithmetic to hide actual comparison with limit
+ #ifdef SYSER_REGISTRATION
+ // number of users from license or hardcoded limit, whichever is lower
+ sInt32 scrambledlimit = 3*(CONCURRENT_DEVICES_LIMIT!=0 && CONCURRENT_DEVICES_LIMIT<fRegQuantity ? CONCURRENT_DEVICES_LIMIT : fRegQuantity)+42;
+ #else
+ // only hardcoded limit
+ sInt32 scrambledlimit = 3*CONCURRENT_DEVICES_LIMIT+42;
+ #endif
+ #endif
+ // - create session object with given ID
+ sessionP = static_cast<TServerConfig *>(fConfigP->fAgentConfigP)->CreateServerSession(sessionHP,SessionIDString.c_str());
+ sessionHP->fSessionP=sessionP;
+ // debug
+ PDEBUGPRINTFX(DBG_HOT,(
+ "Session created: local session ID='%s', not yet in session list",
+ SessionIDString.c_str()
+ ));
+ // info
+ CONSOLEPRINTF(("\nStarted new SyncML session (server id=%s)",SessionIDString.c_str()));
+ // - add it to session map
+ LockSessions();
+ fSessions[SessionIDString]=sessionHP;
+ DEBUGPRINTFX(DBG_HOT,(
+ "Session ID='%s' now in session list, total # of sessions now: %ld",
+ sessionP->getLocalSessionID(),
+ (sInt32)fSessions.size()
+ ));
+ ReleaseSessions();
+ }
+ catch (...) {
+ sessionHP->LeaveSession();
+ delete sessionHP;
+ throw; // re-throw
+ }
+ }
+ // session list is not locked here
+ catch (exception &e) {
+ PDEBUGPRINTFX(DBG_HOT,("******** Exception: Cannot create Session: %s",e.what()));
+ return NULL;
+ }
+ catch (...) {
+ PDEBUGPRINTFX(DBG_HOT,("******** Unknown exception: Cannot create Session"));
+ return NULL;
+ }
+ // return session pointer (session is entered)
+ return sessionHP;
+} // TSyncSessionDispatch::CreateAndEnterServerSession
+
+
+// called by owner of Session dispatcher to signal end of
+// a request. Responsible for finishing sending answers and
+// cleaning up the session if needed
+Ret_t TSyncSessionDispatch::EndRequest(InstanceID_t aSmlWorkspaceID, bool &aHasData, string &aRespURI, bool &aEOSession, uInt32 aReqBytes)
+{
+ TSyncServer *serverSessionP=NULL; // the session
+ Ret_t err;
+
+ // In case of a totally wrong request, this method may be
+ // called when no session is attached to the smlWorkspace
+ aEOSession=true; // default to ending session
+ try {
+ err=getSmlInstanceUserData(aSmlWorkspaceID,(void **)&serverSessionP);
+ if (err==SML_ERR_OK && serverSessionP) {
+ // Important: instance and session must remain attached until session either continues
+ // running or is deleted.
+ // Normal case: there IS a session attached
+ DEBUGPRINTFX(DBG_SESSION,("Request ended with session attached, calling TSyncServer::EndRequest"));
+ if (serverSessionP->EndRequest(aHasData,aRespURI,aReqBytes)) {
+ // TSyncSession::EndRequest returns true when session is done and must be removed
+ PDEBUGPRINTFX(DBG_SESSION,("TSyncServer::EndRequest returned true -> terminating and deleting session now"));
+ // - take session out of session list
+ LockSessions();
+ TSyncSessionHandle *sessionHP = RemoveSession(serverSessionP);
+ ReleaseSessions();
+ // - delete session now (could cause AV in extreme case)
+ if (sessionHP) {
+ // - now actually delete
+ if (sessionHP->EnterAndTerminateSession(0)) { // normal termination of session
+ PDEBUGPRINTFX(DBG_PROTO,("Terminated and deleted session"));
+ } else {
+ PDEBUGPRINTFX(DBG_ERROR,("Could NOT properly terminate/delete session"));
+ }
+ // - delete handle (including session lock, if any)
+ delete sessionHP;
+ }
+ else {
+ PDEBUGPRINTFX(DBG_ERROR,("Very strange case: Session has no sessionhandle any more -> just delete session"));
+ delete serverSessionP;
+ }
+ // safety: session no longer exists, make sure instance has no longer a pointer
+ // (altough deleting the session should already have caused nulling the pointer by now)
+ setSmlInstanceUserData(aSmlWorkspaceID,NULL);
+ }
+ else {
+ // session is not finished, just leave lock as next message might come from another thread
+ PDEBUGPRINTFX(DBG_SESSION,("TSyncServer::EndRequest returned false -> just leave session"));
+ serverSessionP->getSessionHandle()->LeaveSession();
+ aEOSession=false; // session does not end
+ // remove session's reference to this workspace as next request might be decoded in a different workspace
+ // (BUT NOT BEFORE HERE, to make sure the link still exists for the delete case above!)
+ serverSessionP->setSmlWorkspaceID(NULL);
+ // Note: session remains linked from instance's userData
+ }
+ }
+ else {
+ // end of request that could not be assiged a session at all
+ DEBUGPRINTFX(DBG_HOT,("Request ended with no session attached"));
+ }
+ #ifdef SYDEBUG
+ dbgListSessions();
+ #endif
+ return SML_ERR_OK;
+ }
+ catch (exception &e) {
+ aHasData=false;
+ return HandleDecodingException(serverSessionP,"EndRequest",&e);
+ }
+ catch (...) {
+ aHasData=false;
+ return HandleDecodingException(serverSessionP,"EndRequest",NULL);
+ }
+} // TSyncSessionDispatch::EndRequest
+
+
+// list currently active sessions
+void TSyncSessionDispatch::dbgListSessions(void)
+{
+ #ifdef SYDEBUG
+ string ts;
+ TSyncSession *sP;
+ if (PDEBUGTEST(DBG_SESSION)) {
+ LockSessions();
+ try {
+ // View list of active sessions
+ TSyncSessionHandlePContainer::iterator pos;
+ PDEBUGPRINTFX(DBG_SESSION,("-------------------------------------------"));
+ PDEBUGPRINTFX(DBG_SESSION,("Active Sessions (%d):",fSessions.size()));
+ for (pos=fSessions.begin(); pos!=fSessions.end(); pos++) {
+ sP =(*pos).second->fSessionP;
+ if (sP) {
+ // handle has a session
+ StringObjTimestamp(ts,sP->getSessionLastUsed());
+ PDEBUGPRINTFX(DBG_SESSION,(
+ "- %s :Remote URI=%s, Local URI=%s, LastUsed=%s",
+ (*pos).first.c_str(),
+ sP->getRemoteURI(),
+ sP->getLocalURI(),
+ ts.c_str()
+ ));
+ }
+ else {
+ // Strange error: handle without session
+ PDEBUGPRINTFX(DBG_SESSION,(
+ "- %s : <error: session object already deleted>",
+ (*pos).first.c_str()
+ ));
+ }
+ }
+ PDEBUGPRINTFX(DBG_SESSION,("-------------------------------------------"));
+ ReleaseSessions(); // make sure list does not remain blocked
+ }
+ catch (...) {
+ ReleaseSessions(); // make sure list does not remain blocked
+ }
+ }
+ #endif
+} // TSyncSessionDispatch::dbgListSessions
+
+
+// buffer answer in the session's buffer if instance still has a session attached at all
+Ret_t TSyncSessionDispatch::bufferAnswer(InstanceID_t aSmlWorkspaceID, MemPtr_t aAnswer, MemSize_t aAnswerSize)
+{
+ TSyncServer *serverSessionP=NULL; // the session
+ Ret_t err;
+
+ err=getSmlInstanceUserData(aSmlWorkspaceID,(void **)&serverSessionP);
+ if ((err==SML_ERR_OK) && serverSessionP) {
+ // there is a session attached, buffer
+ err=serverSessionP->bufferAnswer(aAnswer,aAnswerSize);
+ }
+ return err;
+} // TSyncSessionDispatch::bufferAnswer
+
+
+// get buffered answer from the session's buffer if there is any
+void TSyncSessionDispatch::getBufferedAnswer(InstanceID_t aSmlWorkspaceID, MemPtr_t &aAnswer, MemSize_t &aAnswerSize)
+{
+ TSyncServer *serverSessionP=NULL; // the session
+ Ret_t err;
+
+ err=getSmlInstanceUserData(aSmlWorkspaceID,(void **)&serverSessionP);
+ if (err==SML_ERR_OK && serverSessionP) {
+ serverSessionP->getBufferedAnswer(aAnswer,aAnswerSize);
+ }
+ else {
+ aAnswer=NULL;
+ aAnswerSize=0;
+ }
+} // TSyncSessionDispatch::getBufferedAnswer
+
+
+
+/* Session abort */
+
+// called by owner or derivate of Session dispatcher when
+// session must be aborted due to error in request processing.
+// Kills session currently assigned to specified workspace
+// Note: may not be called when session list is already locked
+void TSyncSessionDispatch::KillSessionByInstance(InstanceID_t aSmlWorkspaceID, uInt16 aStatusCode, const char *aMsg, uInt32 aErrorCode)
+{
+ TSyncServer *sessionP;
+
+ // In case of a totally bad request, this method may be
+ // called when no session is attached to the smlWorkspace
+ Ret_t err=getSmlInstanceUserData(aSmlWorkspaceID,(void **)&sessionP);
+ if (err==SML_ERR_OK) {
+ // there IS a session attached
+ KillServerSession(sessionP,aStatusCode,aMsg,aErrorCode); // get rid of session
+ }
+ else {
+ // there is no session at all
+ DEBUGPRINTFX(DBG_HOT,("TSyncSessionDispatch::KillSessionByInstance: smlInstance has no session to be killed"));
+ }
+} // TSyncSessionDispatch::KillSessionByInstance
+
+
+// remove and kill session
+// Note: may not be called when session list is already locked
+void TSyncSessionDispatch::KillServerSession(TSyncServer *aSessionP, uInt16 aStatusCode, const char *aMsg, uInt32 aErrorCode)
+{
+ if (aSessionP) {
+ LockSessions();
+ // make sure session log shows the problem
+ #ifdef SYDEBUG
+ OBJDEBUGPRINTFX(aSessionP,DBG_ERROR,(
+ "******* Warning: Terminating Session with Statuscode=%hd because: %s (Errorcode=%ld)",
+ aStatusCode,
+ aMsg ? aMsg : "<unknown>",
+ aErrorCode
+ ));
+ #endif
+ // remove session
+ TSyncSessionHandle *sessionHP = RemoveSession(aSessionP); // remove session from session list
+ ReleaseSessions();
+ // terminate and delete session
+ if (sessionHP) {
+ // - terminate and delete session
+ DEBUGPRINTFX(DBG_SESSION,("TSyncSessionDispatch::KillServerSession: terminating session first..."));
+ if (sessionHP->EnterAndTerminateSession(aStatusCode)) {
+ DEBUGPRINTFX(DBG_SESSION,("TSyncSessionDispatch::KillServerSession: ...terminated, now deleting session handle"));
+ } else {
+ DEBUGPRINTFX(DBG_ERROR,("TSyncSessionDispatch::KillServerSession: ...Session could NOT be deleted, left in memory"));
+ }
+ // - delete session handle itself
+ delete sessionHP;
+ DEBUGPRINTFX(DBG_HOT,("TSyncSessionDispatch::KillServerSession: finished"));
+ }
+ else {
+ // - session not found
+ DEBUGPRINTFX(DBG_ERROR,("TSyncSessionDispatch::KillServerSession: no session found, session pointer seems invalid"));
+ }
+ }
+ else {
+ DEBUGPRINTFX(DBG_ERROR,("TSyncSessionDispatch::KillServerSession: ******* Tried to kill NULL sessionP"));
+ }
+} // TSyncSessionDispatch::KillServerSession
+
+
+// Handle exception happening while decoding commands for a session
+Ret_t TSyncSessionDispatch::HandleDecodingException(TSyncSession *aSessionP, const char *aRoutine, exception *aExceptionP)
+{
+ #ifdef SYDEBUG
+ // determine session name
+ const char *sname = "<unknown>";
+ try {
+ if (aSessionP) {
+ sname = aSessionP->getLocalSessionID();
+ }
+ }
+ catch (...) {
+ sname = "<BAD aSessionP, caused exception>";
+ aSessionP=NULL; // prevent attempt to write to session's log
+ }
+ // determine routine name
+ if (!aRoutine) aRoutine="<unspecified routine>";
+ // show details
+ if (aExceptionP) {
+ // known exception
+ // - show it in global log
+ PDEBUGPRINTFX(DBG_ERROR,(
+ "******** Exception in %s, sessionID=%s: %s",
+ aRoutine,
+ sname,
+ aExceptionP->what()
+ ));
+ // - and also in session log
+ #ifdef SYDEBUG
+ if (aSessionP) {
+ POBJDEBUGPRINTFX(aSessionP,DBG_ERROR,(
+ "******** Warning: Exception in %s: %s",
+ aRoutine,
+ aExceptionP->what()
+ ));
+ }
+ #endif
+ }
+ else {
+ // unknown exception
+ // - show it in global log
+ PDEBUGPRINTFX(DBG_ERROR,(
+ "******** Unknown Exception in %s, sessionID=%s",
+ aRoutine,
+ sname
+ ));
+ // - and also in session log
+ #ifdef SYDEBUG
+ if (aSessionP) {
+ POBJDEBUGPRINTFX(aSessionP,DBG_ERROR,(
+ "******** Warning: Unknown Exception in %s",
+ aRoutine
+ ));
+ }
+ #endif
+ }
+ #endif
+ // try to kill session
+ DEBUGPRINTFX(DBG_SESSION,("******** Exception aborts session: calling KillServerSession"));
+ KillServerSession(static_cast<TSyncServer *>(aSessionP),412,"Decoding Exception");
+ // return error
+ DEBUGPRINTFX(DBG_SESSION,("******** Exception: returning SML_ERR_UNSPECIFIC to abort smlProcessData"));
+ return SML_ERR_UNSPECIFIC;
+} // TSyncSessionDispatch::HandleDecodingException
+
+
+// remove session by pointer from session list. If none found, return NULL
+// NOTES:
+// - must be called with session list locked!!!
+// - does not throw
+TSyncSessionHandle *TSyncSessionDispatch::RemoveSession(TSyncSession *aSessionP) throw()
+{
+ TSyncSessionHandle *foundhandleP = NULL;
+
+ // remove session from Dispatcher
+ // - locate session
+ DEBUGPRINTFX(DBG_SESSION,("TSyncSessionDispatch::RemoveSession called..."));
+ TSyncSessionHandlePContainer::iterator pos;
+ for (pos=fSessions.begin(); pos!=fSessions.end(); ++pos) {
+ TSyncSessionHandle *sessionHP = pos->second;
+ if (sessionHP && (sessionHP->fSessionP == aSessionP)) {
+ // - erase in list
+ fSessions.erase(pos);
+ DEBUGPRINTFX(DBG_SESSION,("...TSyncSessionDispatch::RemoveSession succeeded"));
+ // - return handle
+ foundhandleP=sessionHP;
+ break; // done
+ }
+ }
+ // - unlock access to list
+ return foundhandleP;
+} // TSyncSessionDispatch::RemoveSession
+
+
+
+// eof
diff --git a/src/sysync/syncsessiondispatch.h b/src/sysync/syncsessiondispatch.h
new file mode 100755
index 0000000..303e753
--- /dev/null
+++ b/src/sysync/syncsessiondispatch.h
@@ -0,0 +1,153 @@
+/*
+ * TSyncSessionDispatch
+ * Abstract baseclass for session manager controlling
+ * instantiation and removal of TSySyncSession objects and
+ * re-connecting requests to sessions.
+ *
+ * Copyright (c) 2001-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ */
+
+#ifndef SYNCSESSIONDISPATCH_H
+#define SYNCSESSIONDISPATCH_H
+
+// general includes (SyncML tookit, windows, Clib)
+#include "sysync.h"
+#include "syncappbase.h"
+#include "syncserver.h"
+
+
+
+namespace sysync {
+
+// forward declarations
+class TSyncSession;
+class TSyncServer;
+class TSyncSessionDispatch;
+
+
+// Session handle, includes dispatcher-specific stuff for handling sessions
+// (such as thread IDs, locks, thread termination mechanisms etc.)
+class TSyncSessionHandle {
+public:
+ // the session
+ TSyncServer * fSessionP;
+ // the app base
+ TSyncAppBase * fAppBaseP;
+ // - used for counting session for session limiting
+ bool fOutdated;
+ // - enter session (re-entrance avoidance)
+ virtual bool EnterSession(sInt32 aMaxWaitTime) = 0;
+ // - leave session
+ virtual void LeaveSession(void) = 0;
+ // - Enter, terminate and delete session. Must protect caller from exceptions
+ // when termination/deletion fails.
+ // NOTE: keeps session Enter()ed, to avoid any other thread to enter.
+ virtual bool EnterAndTerminateSession(uInt16 aStatusCode);
+ // - terminate and delete session. Must protect caller from exceptions
+ // when termination/deletion fails.
+ // NOTE: session must be entered already
+ virtual bool TerminateSession(uInt16 aStatusCode);
+ // constructor/destructor
+ TSyncSessionHandle(TSyncAppBase *aAppBaseP);
+ virtual ~TSyncSessionHandle();
+}; // TSyncSessionHandle
+
+
+// Container types
+// - main session list
+typedef std::map<std::string,TSyncSessionHandle*> TSyncSessionHandlePContainer; // contains sync sessions by sessionID-String
+// - deletable sessions list
+typedef std::list<TSyncSessionHandle*> TSyncSessionHandlePList;
+
+
+/* TSyncSessionDispatch manages a list of active sync sessions
+ * and distributes server requests to the appropriate session,
+ * or creates a new session if needed.
+ *
+ * This is a global, singular object which is instantiated ONCE per server
+ * instance. It receives calls from SyncML-Toolkit callbacks.
+ *
+ * The adding an deleting of sessions is done in a thread-safe way
+ * as multiple request might be processed simultaneously in different
+ * threads.
+ */
+class TSyncSessionDispatch : public TSyncAppBase {
+ typedef TSyncAppBase inherited;
+public:
+ // constructors/destructors
+ TSyncSessionDispatch();
+ virtual ~TSyncSessionDispatch();
+ // immediately kill all sessions
+ void TerminateAllSessions(uInt16 aStatusCode);
+ // handlers for SyncML toolkit callbacks
+ // - Start Message: identifies Session, and creates new or assigns existing session
+ Ret_t StartMessage(
+ InstanceID_t aSmlWorkspaceID, // SyncML toolkit workspace instance ID
+ VoidPtr_t aUserData, // user data, should be NULL (as StartMessage is responsible for setting userdata)
+ SmlSyncHdrPtr_t aContentP // SyncML tookit's decoded form of the <SyncHdr> element
+ );
+ // - end of request (to make sure even incomplete SyncML messages get cleaned up properly)
+ Ret_t EndRequest(InstanceID_t aSmlWorkspaceID, bool &aHasData, string &aRespURI, bool &aEOSession, uInt32 aReqBytes);
+ // Answer resending
+ // - buffer answer in the session's buffer if transport allows it
+ Ret_t bufferAnswer(InstanceID_t aSmlWorkspaceID, MemPtr_t aAnswer, MemSize_t aAnswerSize);
+ // - get buffered answer from the session's buffer if there is any
+ void getBufferedAnswer(InstanceID_t aSmlWorkspaceID, MemPtr_t &aAnswer, MemSize_t &aAnswerSize);
+ // Session handling
+ // - Collect timed-out sessions and remove them from the session list
+ void collectTimedOutSessions(TSyncSessionHandlePList &aDeletableSessions);
+ // - Try to enter and delete the sessions passed in one by one
+ void deleteListedSessions(TSyncSessionHandlePList &aDelSessionList);
+ // - create new session
+ TSyncSessionHandle *CreateAndEnterServerSession(cAppCharP aPredefinedSessionID=NULL);
+ // - create new session handle (of correct TSessionHandle derivate for dispatcher used)
+ virtual TSyncSessionHandle *CreateSessionHandle(void) = 0;
+ // - extract sessionID from query string (docname or TargetURI)
+ virtual bool extractSessionID(
+ const char *aQueryString,
+ string &aSessionID
+ ) { return false; /* no sessionID found */ }
+ // list currently active sessions to debug channel
+ void dbgListSessions(void);
+ // - number of sessions
+ sInt32 numSessions(void) { return fSessions.size(); };
+ #ifdef SYSYNC_TOOL
+ // get or create a session for use with the diagnostic tool
+ TSyncServer *getSySyToolSession(void);
+ #endif
+protected:
+ // must be implemented in derived class to make access to
+ // fSessions thread-safe
+ virtual void LockSessions(void) { /* dummy in base class */ };
+ virtual void ReleaseSessions(void) { /* dummy in base class */ };
+ // remove session from internal session list and return it's handle object
+ // Note: session list must be locked while calling RemoveSession
+ TSyncSessionHandle *RemoveSession(TSyncSession *aSessionP) throw();
+ // remove and kill session
+ // Note: may not be called when session list is already locked
+ void KillServerSession(TSyncServer *aSessionP, uInt16 aStatusCode, const char *aMsg=NULL, uInt32 aErrorCode=0); // by pointer
+ void KillSessionByInstance(InstanceID_t aSmlWorkspaceID, uInt16 aStatusCode, const char *aMsg=NULL, uInt32 aErrorCode=0); // by SyncML toolkit instance ID
+ // Handle exception happening while decoding commands for a session
+ virtual Ret_t HandleDecodingException(TSyncSession *aSessionP, const char *aRoutine, exception *aExceptionP=NULL);
+ #ifndef NOWSM
+ // lock to make SyncML toolkit instance creation/freeing thread safe
+ virtual void LockToolkit(void) { /* dummy in base class */ };
+ virtual void ReleaseToolkit(void) { /* dummy in base class */ };
+ #endif
+private:
+ // session map
+ TSyncSessionHandlePContainer fSessions;
+ #ifdef SYSYNC_TOOL
+ TSyncSessionHandle *fToolSessionHP;
+ #endif
+}; // TSyncSessionDispatch
+
+
+} // namespace sysync
+
+
+#endif // SYNCSESSIONDISPATCH_H
+
+
+// eof
diff --git a/src/sysync/syserial.h b/src/sysync/syserial.h
new file mode 100644
index 0000000..467e544
--- /dev/null
+++ b/src/sysync/syserial.h
@@ -0,0 +1,221 @@
+/*
+ * File: syserial.h
+ *
+ * Author: Lukas Zeller (luz@synthesis.ch)
+ *
+ * Serial number generator and checker
+ *
+ * Copyright (c) 2003-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ * 2003-02-11 : luz : created
+ *
+ */
+
+#ifndef SYSERIAL_H
+#define SYSERIAL_H
+
+#include "generic_types.h"
+#include <string>
+
+using namespace std;
+
+namespace sysync {
+
+
+// variant codes
+// =============
+
+#define SYSER_VARIANT_UNKNOWN 0
+#define SYSER_VARIANT_STD 1
+#define SYSER_VARIANT_PRO 2
+#define SYSER_VARIANT_CUSTOM 3
+#define SYSER_VARIANT_DEMO 10
+
+
+// branding codes
+#define SYSER_EXTRA_ID_NONE 0
+#define SYSER_EXTRA_ID_PROTO 1
+#define SYSER_EXTRA_ID_DBG 2
+
+#define SYSER_EXTRA_ID_GOOSYNC 10 // Toffa.com Goosync special version
+#define SYSER_EXTRA_ID_FONELINK 11 // novamedia FoneLink special version
+
+
+
+// product codes
+// =============
+
+// PDA Clients
+// - universal codes for all PDA clients = pocketPC codes
+#define SYSER_PRODCODE_CLIENT_PDA_STD 2 // this is the former PocketPC STD code (most widespread)
+#define SYSER_PRODCODE_CLIENT_PDA_PRO 5 // this is the former PocketPC PRO code (most widespread)
+
+// Old platform specific PDA codes (still valid, but no longer required)
+// - Palm & PPC STD
+#define SYSER_PRODCODE_CLIENT_PALM_STD 1 // for PalmOS
+#define SYSER_PRODCODE_CLIENT_PPC_STD 2 // for PocketPC
+#define SYSER_PRODCODE_CLIENT_PALMPPC_STD 3 // combined for PPC and PalmOS
+// - Palm & PPC PRO
+#define SYSER_PRODCODE_CLIENT_PALM_PRO 4 // for PalmOS
+#define SYSER_PRODCODE_CLIENT_PPC_PRO 5 // for PocketPC
+#define SYSER_PRODCODE_CLIENT_PALMPPC_PRO 6 // combined for PPC and PalmOS
+// - Symbian client
+#define SYSER_PRODCODE_CLIENT_SYMBIAN_STD 18
+#define SYSER_PRODCODE_CLIENT_SYMBIAN_PRO 19
+// - Smartphone
+#define SYSER_PRODCODE_CLIENT_MSSMP_STD 13 // for Microsoft SmartPhone (2003)
+#define SYSER_PRODCODE_CLIENT_MSSMP_PRO 14 // for Microsoft SmartPhone (2003)
+
+
+// - ODBC client STD
+#define SYSER_PRODCODE_CLIENT_ODBC_STD_WIN32 7 // Win32 ODBC PRO
+#define SYSER_PRODCODE_CLIENT_ODBC_STD_MACOSX 8 // for Mac OS X
+#define SYSER_PRODCODE_CLIENT_ODBC_STD_LINUX 9 // for Linux
+// - ODBC client PRO
+#define SYSER_PRODCODE_CLIENT_ODBC_PRO_WIN32 10 // Win32 ODBC PRO
+#define SYSER_PRODCODE_CLIENT_ODBC_PRO_MACOSX 11 // for Mac OS X
+#define SYSER_PRODCODE_CLIENT_ODBC_PRO_LINUX 12 // for Linux
+
+
+// - Demo client
+#define SYSER_PRODCODE_CLIENT_DEMO 15 // Demo Client (Text only)
+
+// - Outlook client
+#define SYSER_PRODCODE_CLIENT_OUTLOOK_STD 16 // Outlook Client STD
+#define SYSER_PRODCODE_CLIENT_OUTLOOK_PRO 17 // Outlook Client PRO (with email)
+
+// - Client Libraries
+#define SYSER_PRODCODE_CLIENT_LIB_WIN32 18 // Win32 ODBC PRO
+#define SYSER_PRODCODE_CLIENT_LIB_MACOSX 19 // for Mac OS X
+#define SYSER_PRODCODE_CLIENT_LIB_LINUX 20 // for Linux
+#define SYSER_PRODCODE_CLIENT_LIB_SYMBIAN 21 // for Symbian
+#define SYSER_PRODCODE_CLIENT_LIB_WM 22 // for Windows Mobile
+#define SYSER_PRODCODE_CLIENT_LIB_PALM 23 // for PALMOS
+#define SYSER_PRODCODE_CLIENT_LIB_IPHONEOS 28 // iPhone OS
+
+
+#define SYSER_PRODCODE_CLIENT_LIB_ALL 24 // ALL Platforms
+#define SYSER_PRODCODE_CLIENT_LIB_MOBILE 25 // ALL Mobile Platforms
+#define SYSER_PRODCODE_CLIENT_LIB_DESK 26 // ALL Desktop Platforms
+
+#define SYSER_PRODCODE_CLIENT_LIB_DEMO 27 // All DEMO Libraries
+
+
+// - Server product flags (no flags -> only XPT version allowed)
+#define SYSER_PRODFLAG_CLIENT_DMU 0x01 // DMU enabled
+
+
+// Servers
+
+// - Server Libraries
+#define SYSER_PRODCODE_SERVER_LIB_WIN32 28 // Win32 ODBC PRO
+#define SYSER_PRODCODE_SERVER_LIB_MACOSX 29 // for Mac OS X
+#define SYSER_PRODCODE_SERVER_LIB_LINUX 30 // for Linux
+#define SYSER_PRODCODE_SERVER_LIB_SYMBIAN 31 // for Symbian
+#define SYSER_PRODCODE_SERVER_LIB_WM 32 // for Windows Mobile
+#define SYSER_PRODCODE_SERVER_LIB_PALM 33 // for PALMOS
+#define SYSER_PRODCODE_SERVER_LIB_IPHONEOS 38 // iPhone OS
+
+
+#define SYSER_PRODCODE_SERVER_LIB_ALL 34 // ALL Platforms
+#define SYSER_PRODCODE_SERVER_LIB_MOBILE 35 // ALL Mobile Platforms
+#define SYSER_PRODCODE_SERVER_LIB_DESK 36 // ALL Desktop Platforms
+
+#define SYSER_PRODCODE_SERVER_LIB_DEMO 37 // All DEMO Libraries
+
+// - Demo
+#define SYSER_PRODCODE_SERVER_DEMO 50 // Demo Server (Text only)
+// - ODBC
+#define SYSER_PRODCODE_SERVER_STD 51 // STD Server (with ODBC)
+#define SYSER_PRODCODE_SERVER_PRO 52 // PRO Server (with ODBC)
+// - XML2GO
+#define SYSER_PRODCODE_SERVER_XML2GO 53 // xml2go Server (with ODBC and XML2GO)
+
+// - Server product flags (no ISAPI or APACHE flags -> only XPT version allowed)
+#define SYSER_PRODFLAG_SERVER_ISAPI 0x01 // ISAPI version
+#define SYSER_PRODFLAG_SERVER_APACHE 0x02 // Apache version
+#define SYSER_PRODFLAG_SERVER_SDKAPI 0x04 // external DB API plugins allowed
+
+
+// special flag: if set, time code in license does not specify when temporary
+// license expires, but for up to what release date (hard-coded into the binary)
+// this code is valid. This allows to issue time unlimited licenses that will allow
+// be used with new releases only up to a defined time period after issuing.
+// If set, the duration bits (encoded absolute month) are no longer the
+// expiry date, but the max release date supported.
+// If this bit is set in SYSER_NEEDED_PRODUCT_FLAGS, this means that the
+// license must either have the bit set, too, or the license must be a
+// time limited license. Only licenses limited neither in time nor in release
+// date will be rejected.
+#define SYSER_PRODFLAG_MAXRELDATE 0x80
+
+
+// license types
+#define SYSER_LTYP_STANDARD 0 // standard license, nothing special
+#define SYSER_LTYP_SYN_REG 1 // requires activation at synthesis
+#define SYSER_LTYP_S2G_REG 2 // requires activation at space2go
+
+
+// registration checking URLs
+#define SYSER_SYN_REG_HOST "www.synthesis.ch"
+#define SYSER_SYN_REG_DOC "/reg/"
+#define SYSER_S2G_REG_HOST "sync.space2go.com"
+#define SYSER_S2G_REG_DOC "/reg/"
+
+// update checking URL
+#define SYSER_SYN_UDC_HOST "www.synthesis.ch"
+#define SYSER_SYN_UDC_DOC "/udc/"
+
+
+
+// Internals
+// =========
+
+// size of serial number
+#define SYSER_SERIALNUM_SIZE 20 // 4*4 chars, plus 3 dashes, plus one terminator = 16+3+1 = 20
+#define SYSER_SERIALNUM_MANGLED_SIZE 17 // dashes are optimized away, so 4*4+1 = 17
+// max size of "name" string (only that much will be stored and tested)
+#define SYSER_NAMESTRING_MAX 80 // should be enough for name and email
+
+
+#ifdef LINUX
+#define SYSER_CRC32_SEED ((uInt32)4119203362LL) // phone Tiefenau :-)
+#else
+#define SYSER_CRC32_SEED 4119203362 // phone Tiefenau :-)
+#endif
+
+// make sure we don't ever include the generator into a product
+#ifndef SYSYNC_VERSION_MAJOR
+
+// generate serial
+void generateSySerial(
+ char *outbuf, // must be able to receive SYSER_SERIALNUM_SIZE chars (including terminator)
+ uInt8 productflags, uInt16 productcode, uInt8 licensetype, uInt16 quantity, uInt8 duration,
+ const char *name,
+ bool aIprevent
+);
+
+#endif
+
+#if defined SYSER_REGISTRATION || !defined(SYSYNC_VERSION_MAJOR)
+
+bool getSySerialInfo(
+ const char *input,
+ uInt8 &productflags, uInt16 &productcode, uInt8 &licensetype, uInt16 &quantity, uInt8 &duration,
+ uInt32 &crc, uInt32 &infocrc,
+ bool aMangled=false
+);
+
+#endif // SYSER_REGISTRATION
+
+
+uInt32 addToCrc(uInt32 aCRC, uInt8 aByte);
+uInt32 addNameToCRC(uInt32 aCRC, const char *aName, bool aMangled=false, uInt16 aMaxChars=32000);
+
+
+
+} // namespace sysync
+
+#endif // SYSERIAL_H
+
+/* eof */
diff --git a/src/sysync/sysync.h b/src/sysync/sysync.h
new file mode 100755
index 0000000..8a48e40
--- /dev/null
+++ b/src/sysync/sysync.h
@@ -0,0 +1,91 @@
+/* sysync generic header file */
+
+#ifndef SYSYNC_H
+#define SYSYNC_H
+
+/* Headers that might be available in precompiled form
+ * (standard libraries)
+ */
+#ifndef SYSYNC_PRECOMP_H
+#include "sysync_precomp.h"
+#endif
+
+#if __MC68K__
+ // Note: STL includes may not be in precompiled headers for CW Palm v9
+ // (STL map crashes), so we have them here for MC68k
+ /* - STL includes */
+ #include <string>
+ #include <vector>
+ #include <map>
+ #include <list>
+#endif
+
+/* SySync headers (not precompiled during SySync development) */
+
+// global constants and settings
+#include "sysync_globs.h"
+
+#ifdef DIRECT_APPBASE_GLOBALACCESS
+ // only in old style environment with global anchors
+ #include "sysync_glob_vars.h"
+#endif
+
+/* SyncML Toolkit includes */
+// - SyncML Toolkit external API
+extern "C" {
+ #include "sml.h"
+ #include "smlerr.h"
+ #include "smldtd.h"
+ #include "smldevinfdtd.h"
+ #include "smlmetinfdtd.h"
+ #include "mgrutil.h" // utilities to work with SmlXXX structs
+ #include "libmem.h" // utilities to allocate/deallocate SML memory
+
+}
+// engine defs (public defines also used in SDK)
+#include "engine_defs.h"
+
+// utilities
+#include "sysync_utils.h"
+#include "stringutils.h"
+#include "lineartime.h"
+#include "iso8601.h"
+#include "debuglogger.h"
+// platform adapters
+#include "configfiles.h"
+
+/* integrated extensions into RTK sources 2002-06-20 %%%
+// Synthesis SyncML toolkit extensions
+extern "C" {
+ #include "smlextensions.h"
+}
+*/
+
+// utility classes without cross-dependencies
+#include "syncexception.h"
+#include "profiling.h"
+
+// base classes
+//#include "syncappbase.h"
+//#include "itemfield.h"
+//#include "syncitemtype.h"
+//#include "syncitem.h"
+//#include "vcarditemtype.h"
+//#include "vcalendaritemtype.h"
+//#include "mimediritemtype.h"
+//#include "syncdatastore.h"
+//#include "localengineds.h"
+//#include "synccommand.h"
+//#include "syncsession.h"
+//#include "syncclient.h"
+//#include "syncserver.h"
+//#include "syncsessiondispatch.h"
+
+// use sysync namespace
+using namespace sysync;
+
+/* globals */
+
+
+#endif // SYSYNC_H
+
diff --git a/src/sysync/sysync_b64.cpp b/src/sysync/sysync_b64.cpp
new file mode 100755
index 0000000..eed6fb2
--- /dev/null
+++ b/src/sysync/sysync_b64.cpp
@@ -0,0 +1,321 @@
+/* b64 encoding/decoding */
+
+#include "prefix_file.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "sysync_b64.h"
+#include "profiling.h"
+
+using namespace b64;
+
+static const char table [64] = {
+
+ 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P',
+
+ 'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f',
+
+ 'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v',
+
+ 'w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/'
+};
+
+char *b64::encode (const uInt8 *instr, uInt32 len, uInt32 *outlenP, sInt16 maxLineLen, bool crLineEnd) {
+
+
+ // no line breaks -- this is just a straight b64 transform
+
+ uInt32 i_off = 0;
+ uInt32 o_off = 0;
+ uInt8 inbuf [3];
+ uInt32 inlen,outlen,inover;
+ uInt32 triples;
+ uInt32 i;
+ sInt16 linechars;
+ char *outstr = NULL;
+
+ if ( (instr == NULL) || (len == 0) ) {
+ return(NULL);
+ }
+
+ inlen = len;
+ inover = inlen%3;
+ triples = ((inlen-inover)/3);
+
+ outlen = 4*triples+1;
+ if (inover) {
+ outlen+=4;
+ }
+ if (maxLineLen) {
+ // make whole number of quads fit on one line
+ maxLineLen &= ~3; // clear bit 0&1
+ // also add room for CRs or CRLFs
+ outlen +=
+ (outlen/maxLineLen+1) << (crLineEnd ? 0 : 1);
+ }
+
+ outstr = (char *)sysync_malloc(outlen*sizeof(char));
+ memset(outstr,0,outlen);
+
+ linechars=0;
+ o_off=0;
+ for (i = 0; i < triples; i++) {
+
+ i_off = i*3;
+ // o_off = i*4; %%% not ok as there might be line ends in between
+
+ inbuf[0] = instr[i_off];
+ inbuf[1] = instr[i_off+1];
+ inbuf[2] = instr[i_off+2];
+
+ outstr[o_off++] = table[(inbuf [0] & 0xFC) >> 2];
+ outstr[o_off++] = table[((inbuf [0] & 0x03) << 4) | ((inbuf [1] & 0xF0) >> 4)];
+ outstr[o_off++] = table[((inbuf [1] & 0x0F) << 2) | ((inbuf [2] & 0xC0) >> 6)];
+ outstr[o_off++] = table[inbuf [2] & 0x3F];
+
+ // check line wrapping
+ linechars+=4;
+ if (
+ maxLineLen && linechars>=maxLineLen && // line limit enabled
+ (i<triples-1 || inover) // and more to come (either full quad or inover padded quad)
+ ) {
+ if (crLineEnd)
+ outstr[o_off++]='\r';
+ else {
+ outstr[o_off++]=0x0D;
+ outstr[o_off++]=0x0A;
+ }
+ linechars=0;
+ }
+ }
+
+ if (inover) {
+ i_off = i*3;
+ // o_off = i*4; %%% not ok as there might be line ends in between
+
+ memset(inbuf,0,3);
+ inbuf[0] = instr[i_off];
+ if (inover > 1 ) {
+ inbuf[1] = instr[i_off+1];
+ }
+ inbuf[2] = 0;
+
+ outstr[o_off] = table[(inbuf [0] & 0xFC) >> 2];
+ outstr[o_off+1] = table[((inbuf [0] & 0x03) << 4) | ((inbuf [1] & 0xF0) >> 4)];
+ outstr[o_off+2] = table[((inbuf [1] & 0x0F) << 2) | ((inbuf [2] & 0xC0) >> 6)];
+ outstr[o_off+3] = table[inbuf [2] & 0x3F];
+
+ // generate b64 strings that are padded with = to
+ // make multiple's of 4 (this is how it must be)
+
+ if (inover < 3 ) {
+ outstr[o_off+3] = '=';
+ if (inover < 2 ) {
+ outstr[o_off+2] = '=';
+ }
+ }
+ }
+
+ // output length is either size of all complete quadruples including line feeds (o_off) or 4 more if
+ // input was not evenly divisible by 3
+ // (%%% luz added case for inover<>0, which produced 4 NULLs at end of string on inover==0)
+ if (outlenP) *outlenP = o_off+ (inover==0 ? 0 : 4);
+
+ return(outstr);
+}
+
+
+uInt8 *b64::decode(const char *instr, uInt32 len, uInt32 *outlenP)
+{
+
+ uInt8 inbuf [4];
+ uInt32 n;
+ sInt16 quadi;
+ bool done;
+ const char *p;
+ char c=0;
+ uInt8 *outstr,*q;
+
+ // get length if not passed as argument
+ if (!instr)
+ len=0;
+ else
+ if (len==0) len=strlen(instr);
+ p = instr;
+
+ // this should always be more than enough len:
+ // 3 times number of quads touched plus one for NUL terminator
+ outstr = (uInt8 *)sysync_malloc(((3*(len/4+1))+1) * sizeof(char));
+ if (!outstr) return NULL;
+ q=outstr;
+
+ // process input string now
+ n=0;
+ quadi=0; // index within quad
+ done=false; // not done yet
+
+ while (!done) {
+ if (n<len) {
+ // init new quad if needed
+ if (quadi==0) {
+ // new quad starting, clear buffer
+ memset(inbuf,0,4);
+ }
+ // get next char
+ c=*p++;
+ n++;
+ // process char
+ if ((c >= 'A') && (c <= 'Z'))
+ c = c - 'A';
+ else if ((c >= 'a') && (c <= 'z'))
+ c = c - 'a' + 26;
+ else if ((c >= '0') && (c <= '9'))
+ c = c - '0' + 52;
+ else if (c == '+')
+ c = 62;
+ else if (c == '/')
+ c = 63;
+ else if (c == '=') {
+ // reaching a "=" is like end of data
+ done=true;
+ }
+ else
+ continue; // ignore all others
+ }
+ else
+ done=true;
+ // save in char
+ if (!done) inbuf[quadi++] = c;
+ // check if done or full quadruple
+ if (done || quadi==4) {
+ // produce data now
+ if (quadi>=2) {
+ // two input bytes, first byte is there for sure
+ *q++ = (inbuf [0] << 2) | ((inbuf [1] & 0x30) >> 4);
+ if (quadi>=3) {
+ // three input bytes, two output bytes are there
+ *q++ = ((inbuf [1] & 0x0F) << 4) | ((inbuf [2] & 0x3C) >> 2);
+ if (quadi==4) {
+ // all 4 bytes there, produce three bytes
+ *q++ = ((inbuf [2] & 0x03) << 6) | (inbuf [3] & 0x3F);
+ }
+ }
+ }
+ // start new quad
+ quadi=0;
+ }
+ } // while
+
+ // return length if requested
+ if (outlenP) *outlenP = q-outstr;
+ // make sure output ends with NUL in case it is interpreted as a c string
+ *q=0;
+ // return string
+ return(outstr);
+
+
+ /*
+ unsigned int i_off, o_off;
+ uInt8 inbuf [4];
+ unsigned int inlen,outlen,inover;
+ unsigned int quads;
+ int i,cnt;
+ uInt8 *outstr = NULL;
+ char ch;
+
+ if (len==0) len=strlen(instr);
+
+ if ( (instr == NULL) || (len==0) ) {
+ return(NULL);
+ }
+
+ inlen = len;
+ inover = inlen%4;
+ quads = ((inlen-inover)/4);
+
+ // this should always be more than enough len
+ outlen = (3*(quads+1))+1;
+
+ outstr = (uInt8 *)sysync_malloc(outlen * sizeof(char));
+ memset(outstr,0,outlen);
+ for (i = 0; i < quads; i++) {
+
+ i_off = i*4;
+ o_off = i*3;
+
+ for (cnt = 0; cnt < 4; cnt++) {
+ ch = instr[i_off+cnt];
+
+ if ((ch >= 'A') && (ch <= 'Z'))
+ ch = ch - 'A';
+
+ else if ((ch >= 'a') && (ch <= 'z'))
+ ch = ch - 'a' + 26;
+
+ else if ((ch >= '0') && (ch <= '9'))
+ ch = ch - '0' + 52;
+
+ else if (ch == '+')
+ ch = 62;
+
+ else if (ch == '=') //no op -- can't ignore this one*
+ ch = 0;
+
+ else if (ch == '/')
+ ch = 63;
+
+ inbuf[cnt] = ch;
+
+ }
+
+ outstr[o_off] = (inbuf [0] << 2) | ((inbuf [1] & 0x30) >> 4);
+ outstr[o_off+1] = ((inbuf [1] & 0x0F) << 4) | ((inbuf [2] & 0x3C) >> 2);
+ outstr[o_off+2] = ((inbuf [2] & 0x03) << 6) | (inbuf [3] & 0x3F);
+ }
+
+ // handle b64 strings that are not padded correctly
+
+ if (inover) {
+ i_off = i*4;
+ o_off = i*3;
+
+ memset(inbuf,0,4);
+
+ for (cnt = 0; cnt < inover; cnt++) {
+ ch = instr[i_off+cnt];
+
+ if ((ch >= 'A') && (ch <= 'Z'))
+ ch = ch - 'A';
+
+ else if ((ch >= 'a') && (ch <= 'z'))
+ ch = ch - 'a' + 26;
+
+ else if ((ch >= '0') && (ch <= '9'))
+ ch = ch - '0' + 52;
+
+ else if (ch == '+')
+ ch = 62;
+
+ else if (ch == '=') //no op -- can't ignore this one*
+ ch = 0;
+
+ else if (ch == '/')
+ ch = 63;
+
+ inbuf[cnt] = ch;
+ }
+
+ outstr[o_off] = (inbuf [0] << 2) | ((inbuf [1] & 0x30) >> 4);
+ outstr[o_off+1] = ((inbuf [1] & 0x0F) << 4) | ((inbuf [2] & 0x3C) >> 2);
+ outstr[o_off+2] = ((inbuf [2] & 0x03) << 6) | (inbuf [3] & 0x3F);
+
+ }
+
+ if (outlenP) *outlenP = o_off+3;
+
+ return(outstr);
+ */
+}
+
+// eof
diff --git a/src/sysync/sysync_b64.h b/src/sysync/sysync_b64.h
new file mode 100755
index 0000000..d10094b
--- /dev/null
+++ b/src/sysync/sysync_b64.h
@@ -0,0 +1,26 @@
+/* b64 encoding/decoding */
+
+#ifndef SYSYNC_B64_H
+#define SYSYNC_B64_H
+
+#include "generic_types.h"
+
+using namespace sysync;
+
+namespace b64 {
+
+// encode data to B64, returns allocated buffer
+// does line breaks if maxLineLen!=0
+char *encode (
+ const uInt8 *instr, uInt32 len, uInt32 *outlenP=NULL,
+ sInt16 maxLineLen=0, bool crLineEnd=false
+);
+
+// decode B64 string to data (len=0 calculates string length automatically)
+uInt8 *decode(const char *instr, uInt32 len=0, uInt32 *outlenP=NULL);
+
+}
+
+#endif /* SYSYNC_B64_H */
+
+/* eof */
diff --git a/src/sysync/sysync_crc16.cpp b/src/sysync/sysync_crc16.cpp
new file mode 100755
index 0000000..0030180
--- /dev/null
+++ b/src/sysync/sysync_crc16.cpp
@@ -0,0 +1,146 @@
+/*
+ * sysync_crc16.cpp
+ * CRC 16 checksumming functions
+ *
+ * Copyright (c) 2002-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ */
+
+#include "prefix_file.h"
+#include "sysync_crc16.h"
+
+namespace sysync {
+
+#define TAB_DRIVEN 1 /* 1 - fast ; 0 - small */
+#define CRC_CCITT 0 /* 1 for CCITT algorithm */
+#define CRC_CRC16 1 /* 1 for CRC16 algorithm */
+
+
+#if TAB_DRIVEN
+
+#if CRC_CCITT
+/* CCITT 16 bit CRC table build using feedback value of 0x8408 */
+static const uInt16 crc16_table[256] = {
+ 0x0000,0x1189,0x2312,0x329b,0x4624,0x57ad,0x6536,0x74bf,
+ 0x8c48,0x9dc1,0xaf5a,0xbed3,0xca6c,0xdbe5,0xe97e,0xf8f7,
+ 0x1891,0x0918,0x3b83,0x2a0a,0x5eb5,0x4f3c,0x7da7,0x6c2e,
+ 0x94d9,0x8550,0xb7cb,0xa642,0xd2fd,0xc374,0xf1ef,0xe066,
+ 0x3122,0x20ab,0x1230,0x03b9,0x7706,0x668f,0x5414,0x459d,
+ 0xbd6a,0xace3,0x9e78,0x8ff1,0xfb4e,0xeac7,0xd85c,0xc9d5,
+ 0x29b3,0x383a,0x0aa1,0x1b28,0x6f97,0x7e1e,0x4c85,0x5d0c,
+ 0xa5fb,0xb472,0x86e9,0x9760,0xe3df,0xf256,0xc0cd,0xd144,
+ 0x6244,0x73cd,0x4156,0x50df,0x2460,0x35e9,0x0772,0x16fb,
+ 0xee0c,0xff85,0xcd1e,0xdc97,0xa828,0xb9a1,0x8b3a,0x9ab3,
+ 0x7ad5,0x6b5c,0x59c7,0x484e,0x3cf1,0x2d78,0x1fe3,0x0e6a,
+ 0xf69d,0xe714,0xd58f,0xc406,0xb0b9,0xa130,0x93ab,0x8222,
+ 0x5366,0x42ef,0x7074,0x61fd,0x1542,0x04cb,0x3650,0x27d9,
+ 0xdf2e,0xcea7,0xfc3c,0xedb5,0x990a,0x8883,0xba18,0xab91,
+ 0x4bf7,0x5a7e,0x68e5,0x796c,0x0dd3,0x1c5a,0x2ec1,0x3f48,
+ 0xc7bf,0xd636,0xe4ad,0xf524,0x819b,0x9012,0xa289,0xb300,
+ 0xc488,0xd501,0xe79a,0xf613,0x82ac,0x9325,0xa1be,0xb037,
+ 0x48c0,0x5949,0x6bd2,0x7a5b,0x0ee4,0x1f6d,0x2df6,0x3c7f,
+ 0xdc19,0xcd90,0xff0b,0xee82,0x9a3d,0x8bb4,0xb92f,0xa8a6,
+ 0x5051,0x41d8,0x7343,0x62ca,0x1675,0x07fc,0x3567,0x24ee,
+ 0xf5aa,0xe423,0xd6b8,0xc731,0xb38e,0xa207,0x909c,0x8115,
+ 0x79e2,0x686b,0x5af0,0x4b79,0x3fc6,0x2e4f,0x1cd4,0x0d5d,
+ 0xed3b,0xfcb2,0xce29,0xdfa0,0xab1f,0xba96,0x880d,0x9984,
+ 0x6173,0x70fa,0x4261,0x53e8,0x2757,0x36de,0x0445,0x15cc,
+ 0xa6cc,0xb745,0x85de,0x9457,0xe0e8,0xf161,0xc3fa,0xd273,
+ 0x2a84,0x3b0d,0x0996,0x181f,0x6ca0,0x7d29,0x4fb2,0x5e3b,
+ 0xbe5d,0xafd4,0x9d4f,0x8cc6,0xf879,0xe9f0,0xdb6b,0xcae2,
+ 0x3215,0x239c,0x1107,0x008e,0x7431,0x65b8,0x5723,0x46aa,
+ 0x97ee,0x8667,0xb4fc,0xa575,0xd1ca,0xc043,0xf2d8,0xe351,
+ 0x1ba6,0x0a2f,0x38b4,0x293d,0x5d82,0x4c0b,0x7e90,0x6f19,
+ 0x8f7f,0x9ef6,0xac6d,0xbde4,0xc95b,0xd8d2,0xea49,0xfbc0,
+ 0x0337,0x12be,0x2025,0x31ac,0x4513,0x549a,0x6601,0x7788
+ };
+#elif defined(CRC_CRC16)
+static const uInt16 crc16_table[256] = {
+ 0x0000,0x1189,0x2312,0x329B,0x4624,0x57AD,0x6536,0x74BF,
+ 0x8C48,0x9DC1,0xAF5A,0xBED3,0xCA6C,0xDBE5,0xE97E,0xF8F7,
+ 0x1081,0x0108,0x3393,0x221A,0x56A5,0x472C,0x75B7,0x643E,
+ 0x9CC9,0x8D40,0xBFDB,0xAE52,0xDAED,0xCB64,0xF9FF,0xE876,
+ 0x2102,0x308B,0x0210,0x1399,0x6726,0x76AF,0x4434,0x55BD,
+ 0xAD4A,0xBCC3,0x8E58,0x9FD1,0xEB6E,0xFAE7,0xC87C,0xD9F5,
+ 0x3183,0x200A,0x1291,0x0318,0x77A7,0x662E,0x54B5,0x453C,
+ 0xBDCB,0xAC42,0x9ED9,0x8F50,0xFBEF,0xEA66,0xD8FD,0xC974,
+ 0x4204,0x538D,0x6116,0x709F,0x0420,0x15A9,0x2732,0x36BB,
+ 0xCE4C,0xDFC5,0xED5E,0xFCD7,0x8868,0x99E1,0xAB7A,0xBAF3,
+ 0x5285,0x430C,0x7197,0x601E,0x14A1,0x0528,0x37B3,0x263A,
+ 0xDECD,0xCF44,0xFDDF,0xEC56,0x98E9,0x8960,0xBBFB,0xAA72,
+ 0x6306,0x728F,0x4014,0x519D,0x2522,0x34AB,0x0630,0x17B9,
+ 0xEF4E,0xFEC7,0xCC5C,0xDDD5,0xA96A,0xB8E3,0x8A78,0x9BF1,
+ 0x7387,0x620E,0x5095,0x411C,0x35A3,0x242A,0x16B1,0x0738,
+ 0xFFCF,0xEE46,0xDCDD,0xCD54,0xB9EB,0xA862,0x9AF9,0x8B70,
+ 0x8408,0x9581,0xA71A,0xB693,0xC22C,0xD3A5,0xE13E,0xF0B7,
+ 0x0840,0x19C9,0x2B52,0x3ADB,0x4E64,0x5FED,0x6D76,0x7CFF,
+ 0x9489,0x8500,0xB79B,0xA612,0xD2AD,0xC324,0xF1BF,0xE036,
+ 0x18C1,0x0948,0x3BD3,0x2A5A,0x5EE5,0x4F6C,0x7DF7,0x6C7E,
+ 0xA50A,0xB483,0x8618,0x9791,0xE32E,0xF2A7,0xC03C,0xD1B5,
+ 0x2942,0x38CB,0x0A50,0x1BD9,0x6F66,0x7EEF,0x4C74,0x5DFD,
+ 0xB58B,0xA402,0x9699,0x8710,0xF3AF,0xE226,0xD0BD,0xC134,
+ 0x39C3,0x284A,0x1AD1,0x0B58,0x7FE7,0x6E6E,0x5CF5,0x4D7C,
+ 0xC60C,0xD785,0xE51E,0xF497,0x8028,0x91A1,0xA33A,0xB2B3,
+ 0x4A44,0x5BCD,0x6956,0x78DF,0x0C60,0x1DE9,0x2F72,0x3EFB,
+ 0xD68D,0xC704,0xF59F,0xE416,0x90A9,0x8120,0xB3BB,0xA232,
+ 0x5AC5,0x4B4C,0x79D7,0x685E,0x1CE1,0x0D68,0x3FF3,0x2E7A,
+ 0xE70E,0xF687,0xC41C,0xD595,0xA12A,0xB0A3,0x8238,0x93B1,
+ 0x6B46,0x7ACF,0x4854,0x59DD,0x2D62,0x3CEB,0x0E70,0x1FF9,
+ 0xF78F,0xE606,0xD49D,0xC514,0xB1AB,0xA022,0x92B9,0x8330,
+ 0x7BC7,0x6A4E,0x58D5,0x495C,0x3DE3,0x2C6A,0x1EF1,0x0F78
+ };
+#else
+ #error "no CRC method defined"
+#endif
+
+/* compute 16 bit CRC using the table */
+uInt16 sysync_crc16(uInt16 crc,uInt8 b)
+{
+ int s, i;
+
+ i = (b ^ crc) & 0xff;
+ s = (crc >> 8) ^ crc16_table[i];
+ return s & 0xffff;
+} // sysync_crc16
+
+#else
+
+#if CRC_CCITT
+/* compute 16 bit CCITT crc on the fly, doesn't use table */
+/* use this one if you are tight on space. */
+uInt16 sysync_crc16(uInt16 crc,uInt8 b)
+{
+ int s;
+
+ /* s = b ^ (crc & 0xff);*/
+ s = (b ^ crc) & 0xff;
+ s = s ^ (s << 4);
+ s = (crc >> 8) ^ (s << 8) ^ (s << 3) ^ (s >> 4);
+
+ return s & 0xffff;
+}
+#endif
+
+#if CRC_CRC16
+/* compute 16 bit CCR16 crc on the fly, doesn't use table */
+/* use this one if you are tight on space. */
+
+#error "this function not implemented ... "
+
+#endif
+
+#endif
+
+// calc CRC over a block of bytes
+uInt16 sysync_crc16_block(const void* dataP, uInt32 len, uInt16 crc)
+{
+ uInt32 i;
+ uInt8 *p=(uInt8 *)dataP;
+
+ for (i=0; i<len; i++) crc = sysync_crc16(crc,*(p++));
+ return crc;
+} // sysync_crc16_block
+
+} // namespace sysync
+
+/* eof */
diff --git a/src/sysync/sysync_crc16.h b/src/sysync/sysync_crc16.h
new file mode 100755
index 0000000..4bb2ae0
--- /dev/null
+++ b/src/sysync/sysync_crc16.h
@@ -0,0 +1,23 @@
+/*
+ * sysync_crc16.h
+ * CRC 16 checksumming functions
+ *
+ * Copyright (c) 2002-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ */
+
+/* compute 16 bit CCITT crc */
+
+#include "generic_types.h"
+
+namespace sysync {
+
+/* add next byte to CRC */
+uInt16 sysync_crc16(uInt16 crc,uInt8 b);
+
+// add next block of bytes to CRC
+uInt16 sysync_crc16_block(const void* dataP, uInt32 len, uInt16 crc);
+
+} // namespace sysync
+
+/* eof */
diff --git a/src/sysync/sysync_debug.h b/src/sysync/sysync_debug.h
new file mode 100755
index 0000000..ca194c3
--- /dev/null
+++ b/src/sysync/sysync_debug.h
@@ -0,0 +1,321 @@
+/*
+ * File: sysync_debug.h
+ *
+ * Author: Lukas Zeller (luz@synthesis.ch)
+ *
+ * Global debug related definitions
+ *
+ * Copyright (c) 2004-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ * 2004-08-31 : luz : created
+ *
+ */
+
+#ifndef SYSYNC_DEBUG_H
+#define SYSYNC_DEBUG_H
+
+#include "generic_types.h"
+
+
+#ifdef __cplusplus
+using namespace std;
+namespace sysync {
+#endif
+
+#ifndef __PALM_OS__
+ #if defined __MACH__ && !defined __GNUC__ /* used for va_list support */
+ #include <mw_stdarg.h>
+ #else
+ #include <stdarg.h>
+ #endif
+#endif
+
+#ifdef __cplusplus
+ #define SYSYCDECL extern "C"
+#else
+ #define SYSYCDECL
+#endif
+
+/* REMARK: do not define it again for C
+/// @todo get rid of all GDEBUG references later
+#warning "get rid of all GDEBUG references later"
+#define GDEBUG getDbgMask() // use accessor, depends on context which one is used
+*/
+
+// non-class debug output functions
+SYSYCDECL void DebugVPrintf(uInt32 mask, const char *format, va_list args);
+SYSYCDECL void DebugPrintf(const char *text, ...);
+SYSYCDECL void DebugPuts(uInt32 mask, const char *text);
+SYSYCDECL uInt32 getDbgMask(void);
+#ifdef __cplusplus
+class TDebugLogger;
+TDebugLogger *getDbgLogger(void);
+#endif
+
+// debug level masks
+// - main categories
+#define DBG_HOT 0x00000001 // hot information
+#define DBG_ERROR 0x00000002 // Error conditions
+// - specialized info categories
+#define DBG_PROTO 0x00000010 // directly SyncML protocol related info
+#define DBG_SESSION 0x00000020 // more internal Session management stuff
+#define DBG_ADMIN 0x00000040 // administrative data management
+#define DBG_DATA 0x00000080 // content data management (but needs DBG_USERDATA to show actual data)
+#define DBG_REMOTEINFO 0x00000100 // information we get about remote party (e.g. devInf or remote rule)
+#define DBG_PARSE 0x00000200 // parsing external formats into internal ones
+#define DBG_GEN 0x00000400 // generate external formats from internal ones
+#define DBG_SCRIPTEXPR 0x00000800 // Script expression details
+#define DBG_SCRIPTS 0x00001000 // Script execution
+#define DBG_TRANSP 0x00002000 // transport debug (outside SySync, e.g. ISAPI)
+#define DBG_REST 0x00008000 // everything not categorized, includes all DEBUGPRINTF
+// - technical insider categories
+#define DBG_LOCK 0x00010000 // Thread lock info
+#define DBG_OBJINST 0x00020000 // object instantiation and deletion
+#define DBG_PROFILE 0x00040000 // Execution time and memory profiling
+// - external library debug info categories
+#define DBG_RTK_SML 0x00100000 // SyncML Toolkit SML messages
+#define DBG_RTK_XPT 0x00200000 // SyncML Toolkit XPT messages
+
+// - flags to select more info, usually combined with other data
+#define DBG_USERDATA 0x01000000 // user content data
+#define DBG_DBAPI 0x02000000 // DB API data (such as SQL statements)
+#define DBG_PLUGIN 0x04000000 // plugin debug messages (all plugins)
+#define DBG_FILTER 0x08000000 // Details on filtering
+#define DBG_MATCH 0x10000000 // Details on content matching (but no user data unless USERDATA is set as well)
+#define DBG_CONFLICT 0x20000000 // Details on conflict resolution, such as data merge
+// - more details
+#define DBG_DETAILS 0x40000000 // show more detailed data
+#define DBG_EXOTIC 0x80000000 // show even most exotic details
+
+// - useful sets
+#define DBG_ALL 0xFFFFFFFF
+#define DBG_MINIMAL (DBG_ERROR+DBG_HOT)
+#define DBG_NORMAL (DBG_MINIMAL +DBG_DATA+DBG_ADMIN+DBG_PROTO+DBG_REMOTEINFO)
+#define DBG_EXTENDED (DBG_NORMAL +DBG_TRANSP+DBG_DBAPI+DBG_PLUGIN+DBG_SESSION+DBG_PARSE+DBG_GEN+DBG_DETAILS+DBG_FILTER+DBG_CONFLICT+DBG_MATCH+DBG_USERDATA+DBG_RTK_SML)
+#define DBG_MAXIMAL ((DBG_EXTENDED +DBG_EXOTIC+DBG_SCRIPTS) & ~DBG_MATCH) // DBG_MATCH explicitly excluded, as it causes O(N^2) output with DBG_EXOTIC
+
+// - special sets (internally used)
+#define DBG_ALLDB (DBG_DATA+DBG_ADMIN+DBG_DBAPI+DBG_PLUGIN) // what used to be "DBG_DB" (more or less)
+#define DBG_TOOL (DBG_HOT+DBG_ERROR+DBG_ALLDB+DBG_SCRIPTS+DBG_TRANSP) // what is shown for sysytest/sysytool in verbose mode
+
+
+// output to console macro
+#ifdef CONSOLEINFO
+ #define CONSOLEPUTS(m) ConsolePuts(m)
+ #define CONSOLEPRINTF(m) ConsolePrintf m
+#else
+ #define CONSOLEPUTS(m)
+ #define CONSOLEPRINTF(m)
+#endif
+
+
+// debug output macros (prevents unnecessary printf argument
+// calculations when SYDEBUG is UNDEFined).
+
+#ifndef DIRECT_APPBASE_GLOBALACCESS
+ // without global access, all NC variants are always disabled
+ #define PNCDEBUGPUTSX(lvl,m)
+ #define PNCDEBUGPUTSXX(lvl,m,s,p)
+ #define PNCDEBUGPRINTFX(lvl,m)
+ #define PNCDEBUGVPRINTFX(lvl,f,a)
+#endif
+
+#ifdef SYDEBUG
+ // "Public" debug info
+ // Debug structure
+ #define PDEBUGBLOCKFMT(m) getDbgLogger()->DebugOpenBlockExpanded m
+ #define PDEBUGBLOCKFMTCOLL(m) getDbgLogger()->DebugOpenBlockCollapsed m
+ #define PDEBUGBLOCKDESC(n,d) getDbgLogger()->DebugOpenBlock(n,d)
+ #define PDEBUGBLOCKDESCCOLL(n,d) getDbgLogger()->DebugOpenBlock(n,d,true)
+ #define PDEBUGBLOCK(n) getDbgLogger()->DebugOpenBlock(n)
+ #define PDEBUGBLOCKCOLL(n) getDbgLogger()->DebugOpenBlock(n,NULL,true)
+ #define PDEBUGENDBLOCK(n) getDbgLogger()->DebugCloseBlock(n)
+ // current-class context debug output
+ #define PDEBUGPUTSX(lvl,m) { if (((lvl) & getDbgMask()) == (lvl)) getDbgLogger()->DebugPuts(lvl,m); }
+ #define PDEBUGPUTSXX(lvl,m,s,p) { if (((lvl) & getDbgMask()) == (lvl)) getDbgLogger()->DebugPuts(lvl,m,s,p); }
+ #define PDEBUGPUTS(m) PDEBUGPUTSX(DBG_REST,m)
+ #define PDEBUGVPRINTFX(lvl,f,a) { if (((lvl) & getDbgMask()) == (lvl)) getDbgLogger()->DebugVPrintf(lvl,f,a); }
+ #define PDEBUGPRINTFX(lvl,m) { if (((lvl) & getDbgMask()) == (lvl)) getDbgLogger()->setNextMask(lvl).DebugPrintfLastMask m; }
+ #define PDEBUGPRINTF(m) PDEBUGPRINTFX(DBG_REST,m)
+ #define PPOINTERTEST(p,m) if (!p) getDbgLogger()->setNextMask(DBG_ERROR).DebugPrintfLastMask m
+ #define PDEBUGTEST(lvl) (((lvl) & getDbgMask()) == (lvl))
+ #define PDEBUGMASK getDbgMask()
+ // direct output to a logger
+ #define PLOGDEBUGBLOCKFMT(lo,m) (lo)->DebugOpenBlockExpanded m
+ #define PLOGDEBUGBLOCKFMTCOLL(lo,m) (lo)->DebugOpenBlockCollapsed m
+ #define PLOGDEBUGBLOCKDESC(lo,n,d) (lo)->DebugOpenBlock(n,d)
+ #define PLOGDEBUGBLOCKDESCCOLL(lo,n,d) (lo)->DebugOpenBlock(n,d,true)
+ #define PLOGDEBUGBLOCK(lo,n) (lo)->DebugOpenBlock(n)
+ #define PLOGDEBUGBLOCKCOLL(lo,n) (lo)->DebugOpenBlock(n,NULL,true)
+ #define PLOGDEBUGENDBLOCK(lo,n) (lo)->DebugCloseBlock(n)
+ #define PLOGDEBUGPUTSX(lo,lvl,m) { if ((lo) &&((lvl) & (lo)->getMask()) == (lvl)) (lo)->DebugPuts(lvl,m); }
+ #define PLOGDEBUGPUTSXX(lo,lvl,m,s,p) { if ((lo) &&((lvl) & (lo)->getMask()) == (lvl)) (lo)->DebugPuts(lvl,m,s,p); }
+ #define PLOGDEBUGVPRINTFX(lo,lvl,f,a) { if ((lo) &&((lvl) & (lo)->getMask()) == (lvl)) (lo)->DebugVPrintf(lvl,f,a); }
+ #define PLOGDEBUGPRINTFX(lo,lvl,m) { if ((lo) &&((lvl) & (lo)->getMask()) == (lvl)) (lo)->setNextMask(lvl).DebugPrintfLastMask m; }
+ // non-class context or C-level debug output
+ #ifdef DIRECT_APPBASE_GLOBALACCESS
+ #ifdef __cplusplus
+ #define PNCDEBUGPUTSX(lvl,m) { if (((lvl) & sysync::getDbgMask()) == (lvl)) sysync::DebugPuts(m); }
+ #define PNCDEBUGPRINTFX(lvl,m) { if (((lvl) & sysync::getDbgMask()) == (lvl)) sysync::DebugPrintf m; }
+ #define PNCDEBUGVPRINTFX(lvl,f,a) { if (((lvl) & sysync::getDbgMask()) == (lvl)) sysync::DebugVPrintf(lvl,f,a); }
+ #else
+ #define PNCDEBUGPUTSX(lvl,m) { if (((lvl) & getDbgMask()) == (lvl)) DebugPuts(m); }
+ #define PNCDEBUGPRINTFX(lvl,m) { if (((lvl) & getDbgMask()) == (lvl)) DebugPrintf m; }
+ #define PNCDEBUGVPRINTFX(lvl,f,a) { if (((lvl) & getDbgMask()) == (lvl)) DebugVPrintf(lvl,f,a); }
+ #endif
+ #endif
+ // specified object-context debug output
+ #define POBJDEBUGPRINTFX(obj,lvl,m) { if ((obj) && (((lvl) & (obj)->getDbgMask()) == (lvl))) (obj)->getDbgLogger()->setNextMask(lvl).DebugPrintfLastMask m; }
+ #define POBJDEBUGPUTSX(obj,lvl,m) { if ((obj) && (((lvl) & (obj)->getDbgMask()) == (lvl))) (obj)->getDbgLogger()->DebugPuts(lvl,m); }
+ #define POBJDEBUGPUTSXX(obj,lvl,m,s,p) { if ((obj) && (((lvl) & (obj)->getDbgMask()) == (lvl))) (obj)->getDbgLogger()->DebugPuts(lvl,m,s,p); }
+ #define POBJDEBUGPRINTF(obj,m) POBJDEBUGPRINTFX(obj,DBG_REST,m)
+ #define POBJDEBUGTEST(obj,lvl) ((obj) && (((lvl) & (obj)->getDbgMask()) == (lvl)))
+ // get current logger
+ #define GETDBGLOGGER (getDbgLogger())
+ #define OBJGETDBGLOGGER(obj) (obj->getDbgLogger())
+
+#else
+ #define PDEBUGBLOCKFMT(m)
+ #define PDEBUGBLOCKFMTCOLL(m)
+ #define PDEBUGBLOCKDESC(n,d)
+ #define PDEBUGBLOCKDESCCOLL(n,d)
+ #define PDEBUGBLOCK(n)
+ #define PDEBUGBLOCKCOLL(n)
+ #define PDEBUGENDBLOCK(n)
+ #define PDEBUGPUTSX(lvl,m)
+ #define PDEBUGPUTSXX(lvl,m,s,p)
+ #define PDEBUGPUTS(m)
+ #define PDEBUGVPRINTFX(lvl,f,a)
+ #define PDEBUGPRINTFX(lvl,m)
+ #define PDEBUGPRINTF(m)
+ #define PPOINTERTEST(p,m)
+ #define PDEBUGTEST(lvl) false
+ #define PDEBUGMASK 0
+ #define PLOGDEBUGBLOCKFMT(lo,m)
+ #define PLOGDEBUGBLOCKFMTCOLL(lo,m)
+ #define PLOGDEBUGBLOCKDESC(lo,n,d)
+ #define PLOGDEBUGBLOCKDESCCOLL(lo,n,d)
+ #define PLOGDEBUGBLOCK(lo,n)
+ #define PLOGDEBUGBLOCKCOLL(lo,n)
+ #define PLOGDEBUGENDBLOCK(lo,n)
+ #define PLOGDEBUGPUTSX(lo,lvl,m)
+ #define PLOGDEBUGPUTSXX(lo,lvl,m,s,p)
+ #define PLOGDEBUGVPRINTFX(lo,lvl,f,a)
+ #define PLOGDEBUGPRINTFX(lo,lvl,m)
+ #ifdef DIRECT_APPBASE_GLOBALACCESS
+ #define PNCDEBUGPUTSX(lvl,m)
+ #define PNCDEBUGPUTSXX(lvl,m,s,p)
+ #define PNCDEBUGPRINTFX(lvl,m)
+ #define PNCDEBUGVPRINTFX(lvl,f,a)
+ #endif
+ //#define PTHREADDEBUGPRINTFX(lvl,m)
+ #define POBJDEBUGPRINTFX(obj,lvl,m)
+ #define POBJDEBUGPUTSX(obj,lvl,m)
+ #define POBJDEBUGPUTSXX(obj,lvl,m,s,p)
+ #define POBJDEBUGPRINTF(obj,m)
+ #define POBJDEBUGTEST(obj,lvl) false
+ // get current logger
+ #define GETDBGLOGGER NULL
+ #define OBJGETDBGLOGGER(obj) NULL
+#endif
+#if SYDEBUG>1
+ // full debugging, including private debug info
+ #define DEBUGPUTSX(lvl,m) PDEBUGPUTSX(lvl,m)
+ #define DEBUGPUTSXX(lvl,m,s,p) PDEBUGPUTSXX(lvl,m,s,p)
+ #define DEBUGPUTS(m) PDEBUGPUTS(m)
+ #define DEBUGVPRINTFX(lvl,f,a) PDEBUGVPRINTFX(lvl,f,a)
+ #define DEBUGPRINTFX(lvl,m) PDEBUGPRINTFX(lvl,m)
+ #define DEBUGPRINTF(m) PDEBUGPRINTF(m)
+ #define POINTERTEST(p,m) PPOINTERTEST(p,m)
+ #define DEBUGTEST(lvl) PDEBUGTEST(lvl)
+ #define DEBUGMASK PDEBUGMASK
+ #define NCDEBUGPUTSX(lvl,m) PNCDEBUGPUTSX(lvl,m)
+ #define NCDEBUGPUTSXX(lvl,m,s,p) PNCDEBUGPUTSXX(lvl,m,s,p)
+ #define NCDEBUGPRINTFX(lvl,m) PNCDEBUGPRINTFX(lvl,m)
+ #define NCDEBUGVPRINTFX(lvl,f,a) PNCDEBUGVPRINTFX(lvl,f,a)
+ #define LOGDEBUGBLOCKFMT(lo,m) PLOGDEBUGBLOCKFMT(lo,m)
+ #define LOGDEBUGBLOCKFMTCOLL(lo,m) PLOGDEBUGBLOCKFMTCOLL(lo,m)
+ #define LOGDEBUGBLOCKDESC(lo,n,d) PLOGDEBUGBLOCKDESC(lo,n,d)
+ #define LOGDEBUGBLOCKDESCCOLL(lo,n,d) PLOGDEBUGBLOCKDESCCOLL(lo,n,d)
+ #define LOGDEBUGBLOCK(lo,n) PLOGDEBUGBLOCK(lo,n)
+ #define LOGDEBUGBLOCKCOLL(lo,n) PLOGDEBUGBLOCKCOLL(lo,n)
+ #define LOGDEBUGENDBLOCK(lo,n) PLOGDEBUGENDBLOCK(lo,n)
+ #define LOGDEBUGPUTSX(lo,lvl,m) PLOGDEBUGPUTSX(lo,lvl,m)
+ #define LOGDEBUGPUTSXX(lo,lvl,m,s,p) PLOGDEBUGPUTSXX(lo,lvl,m,s,p)
+ #define LOGDEBUGVPRINTFX(lo,lvl,f,a) PLOGDEBUGVPRINTFX(lo,lvl,f,a)
+ #define LOGDEBUGPRINTFX(lo,lvl,m) PLOGDEBUGPRINTFX(lo,lvl,m)
+ //#define THREADDEBUGPRINTFX(lvl,m) PTHREADDEBUGPRINTFX(lvl,m)
+ #define OBJDEBUGPRINTFX(obj,lvl,m) POBJDEBUGPRINTFX(obj,lvl,m)
+ #define OBJDEBUGPUTSX(obj,lvl,m) POBJDEBUGPUTSX(obj,lvl,m)
+ #define OBJDEBUGPUTSXX(obj,lvl,m,s,p) POBJDEBUGPUTSXX(obj,lvl,m,s,p)
+ #define OBJDEBUGPRINTF(obj,m) POBJDEBUGPRINTF(obj,m)
+ #define OBJDEBUGTEST(obj,lvl) POBJDEBUGTEST(obj,lvl)
+#else
+ // public or no debugging, none or only P-variants are active
+ #define DEBUGPUTSX(lvl,m)
+ #define DEBUGPUTSXX(lvl,m,s,p)
+ #define DEBUGPUTS(m)
+ #define DEBUGVPRINTFX(lvl,f,a)
+ #define DEBUGPRINTFX(lvl,m)
+ #define DEBUGPRINTF(m)
+ #define POINTERTEST(p,m)
+ #define DEBUGTEST(lvl) false
+ #define DEBUGMASK 0
+ #define NCDEBUGPUTSX(lvl,m)
+ #define NCDEBUGPUTSXX(lvl,m,s,p)
+ #define NCDEBUGPRINTFX(lvl,m)
+ #define NCDEBUGVPRINTFX(lvl,f,a)
+ #define LOGDEBUGBLOCKFMT(lo,m)
+ #define LOGDEBUGBLOCKFMTCOLL(lo,m)
+ #define LOGDEBUGBLOCKDESC(lo,n,d)
+ #define LOGDEBUGBLOCKDESCCOLL(lo,n,d)
+ #define LOGDEBUGBLOCK(lo,n)
+ #define LOGDEBUGBLOCKCOLL(lo,n)
+ #define LOGDEBUGENDBLOCK(lo,n)
+ #define LOGDEBUGPUTSX(lo,lvl,m)
+ #define LOGDEBUGPUTSXX(lo,lvl,m,s,p)
+ #define LOGDEBUGVPRINTFX(lo,lvl,f,a)
+ #define LOGDEBUGPRINTFX(lo,lvl,m)
+ //#define THREADDEBUGPRINTFX(lvl,m)
+ #define OBJDEBUGPRINTFX(obj,lvl,m)
+ #define OBJDEBUGPUTSX(obj,lvl,m)
+ #define OBJDEBUGPUTSXX(obj,lvl,m,s,p)
+ #define OBJDEBUGPRINTF(obj,m)
+ #define OBJDEBUGTEST(obj,lvl) false
+#endif
+
+
+
+// debug String macro (for omitting text from non-debug code)
+#ifdef SHORTDEBUGTEXTS
+#define DEBUGSHORT(l,s) s
+#else
+#define DEBUGSHORT(l,s) l
+#endif
+
+
+
+// debug String macro (for omitting text from non-debug code)
+#ifdef SYDEBUG
+#define DEBUGTEXT(m,c) m
+#else
+#define DEBUGTEXT(m,c) c
+#endif
+
+
+// "enhanced" status generation in debug mode:
+// adds String Item to Status with description
+#ifdef SYDEBUG
+#define ADDDEBUGITEM(s,m) { if (PDEBUGTEST(DBG_HOT)) s.addItemString(m); }
+#else
+#define ADDDEBUGITEM(s,m)
+#endif
+
+#ifdef __cplusplus
+} // namespace sysync
+#endif
+
+#endif // SYSYNC_DEBUG_H
+
+// eof
+
diff --git a/src/sysync/sysync_globs.h b/src/sysync/sysync_globs.h
new file mode 100755
index 0000000..9f9771b
--- /dev/null
+++ b/src/sysync/sysync_globs.h
@@ -0,0 +1,478 @@
+/*
+ * File: sysync_globs.h
+ *
+ * Author: Lukas Zeller (luz@synthesis.ch)
+ *
+ * Global definitions/macros/constants
+ *
+ * Copyright (c) 2001-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ * 2001-xx-xx : luz : created
+ *
+ */
+
+#ifndef SYSYNC_GLOBS_H
+#define SYSYNC_GLOBS_H
+
+#include "generic_types.h"
+
+// global error codes
+#include "syerror.h"
+
+// include sysync-independent part
+#include "syncml_globs.h"
+
+// include debug definitions
+#include "sysync_debug.h"
+
+// include global progress defs
+#include "global_progress.h"
+
+#ifdef __cplusplus
+// we need some STL basics as we define types based on STL constructs
+#include <string>
+#include <list>
+#include <map>
+
+using namespace std;
+namespace sysync {
+#endif
+
+// local status codes (normally generated from SyncML status + LOCAL_STATUS_CODE)
+typedef TSyError localstatus;
+
+
+// configuration switches (DEFAULT_xxx are defaults for corresponding globals)
+
+// - time (in seconds) how long a session will be kept inactive before it times out
+#define DEFAULT_SERVERSESSIONTIMEOUT (60*5) // 5 minutes
+#define DEFAULT_CLIENTSESSIONTIMEOUT 20 // 20 seconds
+
+// - log file format: datastore
+#ifdef SYSYNC_CLIENT
+ #define DEFAULT_LOG_LABELS "SyncEndTime\tUser\tSyncMLVers\tStatus\tSynctype\tSessionID\tRemote ID\tRemote Name\tRemote VersInfo\tDatastore\tLocAdded\tLocUpdated\tLocDeleted\tLocErrors\tRemAdded\tRemUpdated\tRemDeleted\tRemErrors\tBytesOut\tBytesIn\n\n"
+ #define DEFAULT_LOG_FORMAT "%seT\t%U\t%syV\t%sS\t%tS\t%iS\t%iR\t%nR\t%vR\t%nD\t%laI\t%luI\t%ldI\t%leI\t%raI\t%ruI\t%rdI\t%reI\t%doB\t%diB\n"
+#else
+ #define DEFAULT_LOG_LABELS "SyncEndTime\tUser\tSyncMLVers\tStatus\tSynctype\tSessionID\tRemote ID\tRemote Name\tRemote VersInfo\tDatastore\tLocAdded\tLocUpdated\tLocDeleted\tLocErrors\tRemAdded\tRemUpdated\tRemDeleted\tRemErrors\tSlowSyncMatches\tServerWon\tClientWon\tDuplicated\tBytesOut\tBytesIn\tSessionBytesOut\tSessionBytesIn\n\n"
+ #define DEFAULT_LOG_FORMAT "%seT\t%U\t%syV\t%sS\t%tS\t%iS\t%iR\t%nR\t%vR\t%nD\t%laI\t%luI\t%ldI\t%leI\t%raI\t%ruI\t%rdI\t%reI\t%smI\t%scI\t%ccI\t%dcI\t%doB\t%diB\t%toB\t%tiB\n"
+#endif
+
+// - defines debug mask that is active by default
+#define DEFAULT_DEBUG DBG_NORMAL
+// - if 1, enables message dumps (if included)
+#define DEFAULT_MSGDUMP 0
+// - if 1, enables XML translation of messages (independent of MSGDUMP)
+#define DEFAULT_XMLTRANSLATE 0
+// - if 1, enables incoming message simulation (if included)
+#define DEFAULT_SIMMSGREAD 0
+// - if true, global log file is enabled
+#define DEFAULT_GLOBALDEBUGLOGS false
+// - if true, session-spcific log files are enabled by default
+#define DEFAULT_SESSIONDEBUGLOGS true
+// - hard-wired names for debug logs
+#define CONFERRPREFIX "sysync_"
+#define CONFERRSUFFIX "_cfgerr.log"
+#define LOGNAMEPREFIX "sysync_"
+#define LOGSUFFIX ".log"
+#define MSGDUMPPREFIX "sysync_"
+#define MSGDUMPINSUFFIX "_incoming.sml"
+#define MSGDUMPOUTSUFFIX "_outgoing.sml"
+
+
+// Expiry
+#ifdef EXPIRES_AFTER_DATE
+ // gcc does not like line continuations in DOS text files
+ // scalar value representing date
+ #define SCRAMBLED_EXPIRY_VALUE ((EXPIRY_DAY+7l) + (EXPIRY_MONTH-1)*42l + (EXPIRY_YEAR-1720l)*12l*42l)
+#endif
+// define at least the max difference above formula can have to
+// a real difference because of non-smooth increment with time
+#define MAX_EXPIRY_DIFF 15 // 42-28(Feb) = 14
+
+
+#if defined(EXPIRES_AFTER_DATE) || defined(EXPIRES_AFTER_DAYS) || defined(SYSER_REGISTRATION)
+#define APP_CAN_EXPIRE
+#else
+#undef APP_CAN_EXPIRE
+#endif
+
+// Hack switches for testing with difficult clients
+// - if defined, strings will be encoded as SML_PCDATA_OPAQUE
+//#define SML_STRINGS_AS_OPAQUE 1
+// - if defined, status for <Sync> command will be issued after
+// statuses for contained SyncOp commands.
+//#define SYNCSTATUS_AT_SYNC_CLOSE 1
+
+
+// general constants
+// - used by compare functions to signal compare incompatibility
+// (i.e. non-orderable not-equal)
+#define SYSYNC_NOT_COMPARABLE -999
+
+// - maximum auth retries attempted (client) before giving up
+// Note: since 2.0.4.6 this includes the attitional "retry" required to request auth chal from the server
+#define MAX_AUTH_RETRIES 2 // Note: retry only once (plus once for chal request) to avoid strict servers (such as COA-S) to lock accounts
+// - maximum auth attempts allowed (server) before session gets aborted
+#define MAX_AUTH_ATTEMPTS 3
+// - number of message resend retries (client)
+#define MAX_MESSAGE_RESENDS 3
+// - number of message resend retries before trying older protocol
+#define SAME_PROTOCOL_RESENDS 2
+
+
+// - switch on multithread support if we need it
+#if defined(MULTI_THREAD_DATASTORE) || defined(MULTITHREAD_PIPESERVER)
+ #define MULTI_THREAD_SUPPORT 1
+#endif
+
+// - buffer sizes
+#define CONFIG_READ_BUFSIZ 3048 // size of buffer for XML config reading
+
+
+/*
+// - maximum size of SyncML toolkit memory usage
+#ifndef SML_WORKSPACEMEM
+ // if not specified in target_options, use defaults
+ // NOTE: most clients and servers use the defaults, so be careful when changing here!
+ #ifdef SYSYNC_CLIENT
+ // client
+ //#define SML_MAXTOOLKITMEM 1000000 // 1 Megs for now
+ #define SML_WORKSPACEMEM 40000 // 40k now for DS 1.2 (we had 20k before 3.x)
+ #else
+ // server
+ //#define SML_MAXTOOLKITMEM 0 // unlimited for now
+ #define SML_WORKSPACEMEM 100000 // 100k workspace per default
+ #endif
+#endif
+
+// message size and object size constraints (0 for none)
+#ifndef SYNCML_MAXMSGSIZE
+ #define SYNCML_MAXMSGSIZE (SML_WORKSPACEMEM/2) // half of workspace size
+#endif
+#ifndef SYNCML_MAXOBJSIZE
+ #define SYNCML_MAXOBJSIZE 4000000 // 4MB should be enough
+#endif
+#if SYNCML_MAXOBJSIZE<SML_WORKSPACEMEM*2/3
+ #warning "SYNCML_MAXOBJSIZE probably too small (smaller than 2/3 of message)"
+#endif
+#if (SYNCML_MAXMSGSIZE>=SML_WORKSPACEMEM*3/4) && (SML_WORKSPACEMEM-SYNCML_MAXMSGSIZE<10000)
+ #warning "SYNCML_MAXMSGSIZE probably too big"
+#endif
+*/
+
+// Max message size
+#ifndef DEFAULT_MAXMSGSIZE
+ #ifdef SYSYNC_CLIENT
+ // client
+ #define DEFAULT_MAXMSGSIZE 20000 // 20k now for DS 1.2 (we had 10k before 3.x)
+ #else
+ // server
+ #define DEFAULT_MAXMSGSIZE 50000 // 50k should be enough
+ #endif
+#endif
+
+// Max object size
+#ifndef DEFAULT_MAXOBJSIZE
+ #define DEFAULT_MAXOBJSIZE 4000000 // 4MB should be enough
+#endif
+
+
+// default identification strings
+#define SYSYNC_OEM "Synthesis AG"
+#define SYSYNC_SERVER_DEVID "SySync Server"
+#define SYSYNC_CLIENT_DEVID "SySync Client"
+#ifndef SYNCML_SERVER_DEVTYP
+ #define SYNCML_SERVER_DEVTYP "server"; // could also be "workstation"
+#endif
+#ifndef SYNCML_CLIENT_DEVTYP
+ #define SYNCML_CLIENT_DEVTYP "workstation"; // general case, could also be "handheld" or "pda"...
+#endif
+
+// SyncML SyncCap mask bits
+#define SCAP_MASK_TWOWAY 0x0002 // Support of 'two-way sync' = 1
+#define SCAP_MASK_TWOWAY_SLOW 0x0004 // Support of 'slow two-way sync' = 2
+#define SCAP_MASK_ONEWAY_CLIENT 0x0008 // Support of 'one-way sync from client only' = 3
+#define SCAP_MASK_REFRESH_CLIENT 0x0010 // Support of 'refresh sync from client only' = 4
+#define SCAP_MASK_ONEWAY_SERVER 0x0020 // Support of 'one-way sync from server only' = 5
+#define SCAP_MASK_REFRESH_SERVER 0x0040 // Support of 'refresh sync from server only' = 6
+#define SCAP_MASK_SERVER_ALERTED 0x0080 // Support of 'server alerted sync' = 7
+// - minimum needed for conformance
+#define SCAP_MASK_MINIMAL (SCAP_MASK_TWOWAY | SCAP_MASK_TWOWAY_SLOW) // Support of 'server alerted sync' = 7
+// - normal capabilities
+#define SCAP_MASK_NORMAL (SCAP_MASK_MINIMAL | SCAP_MASK_ONEWAY_CLIENT | SCAP_MASK_REFRESH_CLIENT | SCAP_MASK_ONEWAY_SERVER | SCAP_MASK_REFRESH_SERVER)
+
+// DMU / IPP locUris
+#define IPP_PARAMS_LOCURI_BASE "./vendor/synthesis/ipp10/" // URI used to address DMU/IPP settings/subscription
+#define IPP_PARAMS_LOCURI_CFG IPP_PARAMS_LOCURI_BASE "cfg" // IPP config data from server
+#define IPP_PARAMS_LOCURI_REQ IPP_PARAMS_LOCURI_BASE "req" // IPP request from client
+#define IPP_PARAMS_ITEM_METATYPE "text/plain" // meta type for IPP req and cfg PUT data items
+#define IPP_REQ_ACTIVATE "activate" // activate-request
+#define IPP_REQ_SUBSCRIBE "subscribe" // activate-request
+#define IPP_REQ_CHECK "check" // check-request
+
+// Remote provisioning
+#define SETTINGS_LOCURI_BASE "./vendor/synthesis/settings10/" // URI used to address settings
+#define SETTINGS_LOCURI_CFG SETTINGS_LOCURI_BASE "cfg" // settings config data from server
+#define SETTINGS_ITEM_METATYPE "text/plain" // meta type for settings config data
+
+
+// fatal errors
+#ifdef __PALM_OS__
+ #ifndef PlatFormFatalErr
+ #define PlatFormFatalErr { ErrDisplay("PlatFormFatalErr called"); ErrThrow(999); }
+ #endif
+ #define PlatFormFatalThrow(x) { exception *eP=new x; ErrDisplay(eP->what()); ErrThrow(999); }
+ #define PlatFormFatalReThrow { ErrDisplay("C++ re-throw attempted"); ErrThrow(999); }
+#else
+ #ifndef PlatFormFatalErr
+ #define PlatFormFatalErr { printf("PlatFormFatalErr called"); exit(999); }
+ #endif
+ #define PlatFormFatalThrow(x) { exception *eP=new x; printf("C++ exception thrown: %s",eP->what()); exit(999); }
+ #define PlatFormFatalReThrow { printf("C++ re-throw attempted"); exit(999); }
+#endif
+
+// exceptions
+
+#ifndef TARGET_HAS_EXCEPTIONS
+ // define here depending on compiler
+ // if not defined e.g. in target options
+ #ifdef __EPOC_OS__
+ // no exceptions in EPOC
+ #define TARGET_HAS_EXCEPTIONS 0
+ #elif defined(__MWERKS__)
+ // in Coderwarrior it depends on compiler settings
+ #if __option (exceptions)
+ #define TARGET_HAS_EXCEPTIONS 1
+ #else
+ #define TARGET_HAS_EXCEPTIONS 0
+ #endif
+ #elif defined(WINCE)
+ // no exceptions in eVC
+ #define TARGET_HAS_EXCEPTIONS 0
+ #else
+ // otherwise generally assume yes
+ #define TARGET_HAS_EXCEPTIONS 1
+ #endif
+#endif
+
+#if TARGET_HAS_EXCEPTIONS
+ // really throw
+ #define SYSYNC_THROW(x) throw x
+ #define SYSYNC_RETHROW throw
+ #define SYSYNC_TRY try
+ #define SYSYNC_CATCH(x) catch(x) {
+ #define SYSYNC_ENDCATCH }
+#else
+ // global fatal error
+ #define SYSYNC_THROW(x) PlatFormFatalThrow(x)
+ #define SYSYNC_RETHROW PlatFormFatalReThrow
+ #define SYSYNC_TRY
+ #define SYSYNC_CATCH(x) if(false) { TSmlException e("",SML_ERR_UNSPECIFIC);
+ #define SYSYNC_ENDCATCH }
+#endif
+
+
+// checked casts (non-checked when RTTI is not there)
+#if defined(WINCE) || defined(__EPOC_OS__)
+ // eVC + symbian has no RTTI, so we rely on having the right type and
+ // do a static cast here
+ #define GET_CASTED_PTR(dst,ty,src,msg) dst=static_cast<ty *>(src)
+#else
+ // use dynamic cast and check if pointer is not NULL
+ #define GET_CASTED_PTR(dst,ty,src,msg) { dst = dynamic_cast<ty *>(src); if (!dst) { SYSYNC_THROW(TSyncException(msg)); } }
+#endif
+
+
+// global types
+
+// library calling interface
+#ifndef SYSYLIBCI
+ #define SYSYLIBCI // default to compilers default calling interface
+#endif
+
+// extra1 values for pev_debug
+#define PEV_DEBUG_FORCERETRY 1
+
+#ifdef __cplusplus
+
+// internal field types (item field types)
+typedef enum {
+ fty_string,
+ fty_telephone,
+ fty_integer,
+ fty_timestamp,
+ fty_date,
+ fty_url,
+ fty_multiline,
+ fty_blob,
+ fty_none, // note: this one is mostly internal use
+ numFieldTypes
+} TItemFieldTypes;
+
+
+#ifdef SCRIPT_SUPPORT
+
+// built-in function definition
+class TItemField;
+class TScriptContext;
+
+typedef void (*TBuiltinFunc)(TItemField *&aTermP, TScriptContext *aFuncContextP);
+
+typedef void* (*TTableChainFunc) (void *&aNextCallerContext);
+
+typedef struct {
+ // name of function
+ cAppCharP fFuncName;
+ // implementation
+ TBuiltinFunc fFuncProc;
+ // return type of function
+ TItemFieldTypes fReturntype;
+ // parameters
+ sInt16 fNumParams;
+ // list of parameter definition bytes
+ const uInt8 *fParamTypes;
+} TBuiltInFuncDef;
+
+typedef struct {
+ // number of functions
+ uInt16 numFuncs;
+ // actual functions
+ const TBuiltInFuncDef *funcDefs;
+ // chain function
+ TTableChainFunc chainFunc;
+} TFuncTable;
+
+
+#endif // SCRIPT_SUPPORT
+
+// progress event
+class TLocalDSConfig; // forward
+
+typedef struct {
+ TProgressEventType eventtype;
+ TLocalDSConfig *datastoreID; // config pointer is used as ID, NULL if global
+ sInt32 extra; // extra info, such as error code or count for progress or # of added items
+ sInt32 extra2; // extra info, such as total for progress or # of updated items
+ sInt32 extra3; // extra info, such as # of deleted items
+} TProgressEvent;
+
+// Callbacks
+
+/* %%% from old ages, probably obsolete
+// - XML config data reader func type
+typedef int SYSYLIBCI (*TXMLTextReadFunc)(
+ appCharP aBuffer,
+ bufferIndex aMaxSize,
+ bufferIndex *aReadCharsP,
+ void *aContext
+);
+// - text message output
+typedef void SYSYLIBCI (*TTextMsgProc)(cAppCharP aMessage, void *aContext);
+*/
+
+// - progress event notification
+typedef bool SYSYLIBCI (*TProgressEventFunc)(
+ const TProgressEvent &aEvent,
+ void *aContext
+);
+
+#endif // __cplusplus
+
+
+
+// local status codes (normally generated from SyncML status + LOCAL_STATUS_CODE)
+typedef uInt16 localstatus;
+
+// SyncML encodings
+// Note: SmlEncoding_t is defined in the RTK smldef.h
+#define numSyncMLEncodings (SML_XML-SML_UNDEF+1)
+
+
+// SyncML Versions
+typedef enum {
+ syncml_vers_unknown,
+ syncml_vers_1_0,
+ syncml_vers_1_1,
+ syncml_vers_1_2,
+ // number of enums
+ numSyncMLVersions
+} TSyncMLVersions;
+
+
+// conflict resolution strategies
+typedef enum {
+ cr_duplicate, // add conflicting counterpart to both databases
+ cr_newer_wins, // newer version wins (if date/version comparison is possible, like sst_duplicate otherwise)
+ cr_server_wins, // server version wins (and is written to client, with merge if enabled)
+ cr_client_wins, // client version wins (and is written to server, with merge if enabled)
+ // number of enums
+ numConflictStrategies
+} TConflictResolution;
+
+
+// package states
+typedef enum {
+ psta_idle, // not started yet, no package sent or received
+ psta_init, // initialisation package
+ psta_sync, // sync package
+ psta_initsync, // combined initialisation and sync package
+ psta_map, // data update status / map
+ psta_supplement, // extra packages eventually needed at end of session
+ // number of enums
+ numPackageStates
+} TPackageStates;
+
+
+/// Sync operations
+typedef enum {
+ sop_wants_add, ///< like add, but is still available for slowsync match
+ sop_add,
+ sop_wants_replace, ///< like replace, but not yet conflict-checked
+ sop_replace,
+ sop_reference_only, ///< slowsync resume only: like sop_wants_add/sop_wants_replace, but ONLY for comparing with incoming add/replace from client and avoiding add when matching - NEVER send these to client
+ sop_archive_delete,
+ sop_soft_delete,
+ sop_delete,
+ sop_copy,
+ sop_move, // new for DS 1.2
+ sop_none, // should be last
+ // number of enums
+ numSyncOperations
+} TSyncOperation;
+
+
+// Sync modes (note: slow/refresh is a separate flag)
+typedef enum {
+ smo_twoway,
+ smo_fromserver,
+ smo_fromclient,
+ // number of enums
+ numSyncModes
+} TSyncModes;
+
+
+// filter identifiers
+#define isFilterIdent(c) (isalnum(c) || c=='_' || c=='.')
+
+
+#ifdef __cplusplus
+
+// forwards
+class TSmlCommand;
+
+// container for TSmlCommand pointers
+typedef std::list<TSmlCommand*> TSmlCommandPContainer; // contains sync commands
+
+// string to string map
+typedef std::map<string,string> TStringToStringMap; // string to string map
+
+#endif
+
+#ifdef __cplusplus
+} // namespace sysync
+#endif
+
+#endif // SYSYNC_GLOBS_H
+
+// eof
+
diff --git a/src/sysync/sysync_md5.cpp b/src/sysync/sysync_md5.cpp
new file mode 100755
index 0000000..ab65b34
--- /dev/null
+++ b/src/sysync/sysync_md5.cpp
@@ -0,0 +1,400 @@
+/* MD5C.C - RSA Data Security, Inc., MD5 message-digest algorithm
+ */
+
+/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All
+rights reserved.
+
+License to copy and use this software is granted provided that it
+is identified as the "RSA Data Security, Inc. MD5 Message-Digest
+Algorithm" in all material mentioning or referencing this software
+or this function.
+
+License is also granted to make and use derivative works provided
+that such works are identified as "derived from the RSA Data
+Security, Inc. MD5 Message-Digest Algorithm" in all material
+mentioning or referencing the derived work.
+
+RSA Data Security, Inc. makes no representations concerning either
+the merchantability of this software or the suitability of this
+software for any particular purpose. It is provided "as is"
+without express or implied warranty of any kind.
+
+These notices must be retained in any copies of any part of this
+documentation and/or software.
+ */
+
+/* 2001-05-29: adapted to MW-C and ANSI-C by luz */
+/* 2001-08-07: added MD5_MSB_FIRST variant for Motorola order digest output*/
+
+#include "prefix_file.h"
+
+#include "sysync_md5.h"
+
+/* Constants for MD5Transform routine.
+ */
+
+using namespace md5;
+
+
+#define S11 7
+#define S12 12
+#define S13 17
+#define S14 22
+#define S21 5
+#define S22 9
+#define S23 14
+#define S24 20
+#define S31 4
+#define S32 11
+#define S33 16
+#define S34 23
+#define S41 6
+#define S42 10
+#define S43 15
+#define S44 21
+
+/* local prototypes */
+static void MD5Transform (SYSYNC_UINT4 [4], const uInt8 [64]);
+static void Encode (uInt8 *, const SYSYNC_UINT4 *, uInt32);
+#ifdef MD5_MSB_FIRST
+static void EncodeMSBfirst (uInt8 *, const SYSYNC_UINT4 *, uInt32);
+#endif
+static void Decode (SYSYNC_UINT4 *, const uInt8 *, uInt32);
+static void MD5_memcpy (SYSYNC_POINTER, SYSYNC_POINTER, uInt32);
+static void MD5_memset (SYSYNC_POINTER, sInt32, uInt32);
+
+/* moved to SYSYNC_MD5_CTX as statics are not allowed in some environments
+static uInt8 PADDING[64] = {
+ 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+*/
+
+/* F, G, H and I are basic MD5 functions.
+ */
+#define F(x, y, z) (((x) & (y)) | ((~x) & (z)))
+#define G(x, y, z) (((x) & (z)) | ((y) & (~z)))
+#define H(x, y, z) ((x) ^ (y) ^ (z))
+#define I(x, y, z) ((y) ^ ((x) | (~z)))
+
+/* ROTATE_LEFT rotates x left n bits.
+ */
+#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n))))
+
+/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4.
+Rotation is separate from addition to prevent recomputation.
+ */
+#define FF(a, b, c, d, x, s, ac) { (a) += F ((b), (c), (d)) + (x) + (SYSYNC_UINT4)(ac); (a) = ROTATE_LEFT ((a), (s)); (a) += (b); }
+#define GG(a, b, c, d, x, s, ac) { (a) += G ((b), (c), (d)) + (x) + (SYSYNC_UINT4)(ac); (a) = ROTATE_LEFT ((a), (s)); (a) += (b); }
+#define HH(a, b, c, d, x, s, ac) { (a) += H ((b), (c), (d)) + (x) + (SYSYNC_UINT4)(ac); (a) = ROTATE_LEFT ((a), (s)); (a) += (b); }
+#define II(a, b, c, d, x, s, ac) { (a) += I ((b), (c), (d)) + (x) + (SYSYNC_UINT4)(ac); (a) = ROTATE_LEFT ((a), (s)); (a) += (b); }
+
+/* MD5 initialization. Begins an MD5 operation, writing a new context.
+ */
+void md5::Init (SYSYNC_MD5_CTX *context)
+{
+ context->count[0] = context->count[1] = 0;
+ /* Load magic initialization constants.
+*/
+ context->state[0] = 0x67452301;
+ context->state[1] = 0xefcdab89;
+ context->state[2] = 0x98badcfe;
+ context->state[3] = 0x10325476;
+ /* initialize the padding space, don't know if this is necessary */
+ MD5_memset ((SYSYNC_POINTER)context->PADDING, 0, 64);
+ context->PADDING[0]=0x80;
+ // note: this does the same thing as the original:
+ /* moved to SYSYNC_MD5_CTX as statics are not allowed in some environments
+ static uInt8 PADDING[64] = {
+ 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ };
+ */
+}
+
+/* MD5 block update operation. Continues an MD5 message-digest
+ operation, processing another message block, and updating the
+ context.
+ */
+void md5::Update (
+ SYSYNC_MD5_CTX *context, /* context */
+ const uInt8 *input, /* input block */
+ uInt32 inputLen /* length of input block */
+)
+{
+ uInt32 i, index, partLen;
+
+ /* Compute number of bytes mod 64 */
+ index = (uInt32)((context->count[0] >> 3) & 0x3F);
+
+ /* Update number of bits */
+ if ((context->count[0] += ((SYSYNC_UINT4)inputLen << 3))
+ < ((SYSYNC_UINT4)inputLen << 3))
+ context->count[1]++;
+ context->count[1] += ((SYSYNC_UINT4)inputLen >> 29);
+
+ partLen = 64 - index;
+
+ /* Transform as many times as possible.
+ */
+ if (inputLen >= partLen) {
+ MD5_memcpy
+ ((SYSYNC_POINTER)&context->buffer[index], (SYSYNC_POINTER)input, partLen);
+ MD5Transform (context->state, context->buffer);
+
+ for (i = partLen; i + 63 < inputLen; i += 64)
+ MD5Transform (context->state, &input[i]);
+
+ index = 0;
+ }
+ else
+ i = 0;
+
+ /* Buffer remaining input */
+ MD5_memcpy
+ ((SYSYNC_POINTER)&context->buffer[index], (SYSYNC_POINTER)&input[i],
+ inputLen-i);
+}
+
+/* MD5 finalization. Ends an MD5 message-digest operation, writing the
+ the message digest and zeroizing the context.
+ */
+void md5::Final (
+ uInt8 digest[16], /* message digest */
+ SYSYNC_MD5_CTX *context /* context */
+)
+{
+ uInt8 bits[8];
+ uInt32 index, padLen;
+
+ /* Save number of bits */
+ Encode (bits, context->count, 8);
+
+ /* Pad out to 56 mod 64.
+ */
+ index = (uInt32)((context->count[0] >> 3) & 0x3f);
+ padLen = (index < 56) ? (56 - index) : (120 - index);
+ md5::Update (context, context->PADDING, padLen);
+
+ /* Append length (before padding) */
+ md5::Update (context, bits, 8);
+
+ /* Store state in digest */
+ Encode (digest, context->state, 16);
+
+ /* Zeroize sensitive information.
+ */
+ MD5_memset ((SYSYNC_POINTER)context, 0, sizeof (*context));
+}
+
+/* MD5 basic transformation. Transforms state based on block.
+ */
+static void MD5Transform (
+ SYSYNC_UINT4 state[4],
+ const uInt8 block[64]
+)
+{
+ SYSYNC_UINT4 a = state[0], b = state[1], c = state[2], d = state[3], x[16];
+
+ Decode (x, block, 64);
+
+ /* Round 1 */
+ FF (a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */
+ FF (d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */
+ FF (c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */
+ FF (b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */
+ FF (a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */
+ FF (d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */
+ FF (c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */
+ FF (b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */
+ FF (a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */
+ FF (d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */
+ FF (c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */
+ FF (b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */
+ FF (a, b, c, d, x[12], S11, 0x6b901122); /* 13 */
+ FF (d, a, b, c, x[13], S12, 0xfd987193); /* 14 */
+ FF (c, d, a, b, x[14], S13, 0xa679438e); /* 15 */
+ FF (b, c, d, a, x[15], S14, 0x49b40821); /* 16 */
+
+ /* Round 2 */
+ GG (a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */
+ GG (d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */
+ GG (c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */
+ GG (b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */
+ GG (a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */
+ GG (d, a, b, c, x[10], S22, 0x2441453); /* 22 */
+ GG (c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */
+ GG (b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */
+ GG (a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */
+ GG (d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */
+ GG (c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */
+ GG (b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */
+ GG (a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */
+ GG (d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */
+ GG (c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */
+ GG (b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */
+
+ /* Round 3 */
+ HH (a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */
+ HH (d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */
+ HH (c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */
+ HH (b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */
+ HH (a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */
+ HH (d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */
+ HH (c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */
+ HH (b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */
+ HH (a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */
+ HH (d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */
+ HH (c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */
+ HH (b, c, d, a, x[ 6], S34, 0x4881d05); /* 44 */
+ HH (a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */
+ HH (d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */
+ HH (c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */
+ HH (b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */
+
+ /* Round 4 */
+ II (a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */
+ II (d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */
+ II (c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */
+ II (b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */
+ II (a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */
+ II (d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */
+ II (c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */
+ II (b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */
+ II (a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */
+ II (d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */
+ II (c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */
+ II (b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */
+ II (a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */
+ II (d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */
+ II (c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */
+ II (b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */
+
+ state[0] += a;
+ state[1] += b;
+ state[2] += c;
+ state[3] += d;
+
+ /* Zeroize sensitive information.
+ */
+ MD5_memset ((SYSYNC_POINTER)x, 0, sizeof (x));
+}
+
+
+/* Encodes input (SYSYNC_UINT4) into output (uInt8). Assumes len is
+ a multiple of 4.
+ */
+static void Encode (
+ uInt8 *output,
+ const SYSYNC_UINT4 *input,
+ uInt32 len
+)
+{
+ uInt32 i, j;
+
+ for (i = 0, j = 0; j < len; i++, j += 4) {
+ output[j] = (uInt8)(input[i] & 0xff);
+ output[j+1] = (uInt8)((input[i] >> 8) & 0xff);
+ output[j+2] = (uInt8)((input[i] >> 16) & 0xff);
+ output[j+3] = (uInt8)((input[i] >> 24) & 0xff);
+ }
+}
+
+/* Decodes input (uInt8) into output (SYSYNC_UINT4). Assumes len is
+ a multiple of 4.
+ */
+static void Decode (
+ SYSYNC_UINT4 *output,
+ const uInt8 *input,
+ uInt32 len
+)
+{
+ uInt32 i, j;
+
+ for (i = 0, j = 0; j < len; i++, j += 4)
+ output[i] = ((SYSYNC_UINT4)input[j]) | (((SYSYNC_UINT4)input[j+1]) << 8) |
+ (((SYSYNC_UINT4)input[j+2]) << 16) | (((SYSYNC_UINT4)input[j+3]) << 24);
+}
+
+/* Note: Replace "for loop" with standard memcpy if possible.
+ */
+
+static void MD5_memcpy (
+ SYSYNC_POINTER output,
+ SYSYNC_POINTER input,
+ uInt32 len
+)
+{
+ uInt32 i;
+
+ for (i = 0; i < len; i++)
+ output[i] = input[i];
+}
+
+/* Note: Replace "for loop" with standard memset if possible.
+ */
+static void MD5_memset (
+ SYSYNC_POINTER output,
+ sInt32 value,
+ uInt32 len
+)
+{
+ uInt32 i;
+
+ for (i = 0; i < len; i++)
+ ((char *)output)[i] = (char)value;
+}
+
+
+#ifdef MD5_TEST_FUNCS
+/* MD5 Test stuff */
+
+/* Digests a string and prints the result.
+ */
+void md5::String (const char *aString, char *s)
+{
+ SYSYNC_MD5_CTX context;
+ uInt8 digest[16];
+ uInt32 len = strlen (aString);
+
+ Init (&context);
+ Update (&context, (uInt8 *) aString, len);
+ Final (digest, &context);
+
+ sprintf (s,"MD5 (\"%s\") = ", aString); s+=strlen(s);
+ Print (digest,s);
+}
+
+/* Prints a message digest in hexadecimal.
+ */
+void md5::Print (uInt8 *digest, char * &s)
+{
+
+ uInt32 i;
+
+ for (i = 0; i < 16; i++) {
+ sprintf (s,"%02hx", (uInt16)digest[i]);
+ s+=strlen(s);
+ }
+}
+
+void md5::dotest(void)
+{
+ printf("================= MD5 Test Suite ===========================");
+ char buf[1024];
+ md5::String ("",buf); printf("%s (should be: d41d8cd98f00b204e9800998ecf8427e)",buf);
+ md5::String ("a",buf); printf("%s (should be: 0cc175b9c0f1b6a831c399e269772661)",buf);
+ md5::String ("abc",buf); printf("%s (should be: 900150983cd24fb0d6963f7d28e17f72)",buf);
+ md5::String ("message digest",buf); printf("%s (should be: f96b697d7cb7938d525a2f31aaf161d0)",buf);
+ md5::String ("abcdefghijklmnopqrstuvwxyz",buf); printf("%s (should be: c3fcd3d76192e4007dfb496cca67e13b)",buf);
+ md5::String ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",buf); printf("%s (should be: d174ab98d277d9f5a5611c2c9f419d9f)",buf);
+ md5::String ("12345678901234567890123456789012345678901234567890123456789012345678901234567890",buf); printf("%s (should be: 57edf4a22be3c955ac49da2e2107b67a)",buf);
+ md5::String ("luz:gaga",buf); printf("%s (should be: <dontknow>)",buf);
+ printf("================= end MD5 Test Suite ===========================");
+}
+
+#endif
+
+/* eof */
diff --git a/src/sysync/sysync_md5.h b/src/sysync/sysync_md5.h
new file mode 100755
index 0000000..5bf2fb6
--- /dev/null
+++ b/src/sysync/sysync_md5.h
@@ -0,0 +1,78 @@
+/* MD5.H - header file for MD5C.C
+ */
+
+/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All
+rights reserved.
+
+License to copy and use this software is granted provided that it
+is identified as the "RSA Data Security, Inc. MD5 Message-Digest
+Algorithm" in all material mentioning or referencing this software
+or this function.
+
+License is also granted to make and use derivative works provided
+that such works are identified as "derived from the RSA Data
+Security, Inc. MD5 Message-Digest Algorithm" in all material
+mentioning or referencing the derived work.
+
+RSA Data Security, Inc. makes no representations concerning either
+the merchantability of this software or the suitability of this
+software for any particular purpose. It is provided "as is"
+without express or implied warranty of any kind.
+
+These notices must be retained in any copies of any part of this
+documentation and/or software.
+ */
+
+/* 2001-05-29: adapted to MW-C and ANSI-C by luz */
+
+
+/* originally GLOBAL.H - RSAREF types and constants
+ * now moved here to keep everything in namespace
+ */
+
+// define this to add test functions String() and Print()
+// #define MD5_TEST_FUNCS 1
+
+#ifndef SYSYNC_MD5_H
+#define SYSYNC_MD5_H
+
+
+#include "generic_types.h"
+
+using namespace sysync;
+
+namespace md5 {
+
+/* POINTER defines a generic pointer type */
+typedef uInt8 *SYSYNC_POINTER;
+
+/* UINT2 defines a two byte word */
+typedef uInt16 SYSYNC_UINT2;
+
+/* UINT4 defines a four byte word */
+typedef uInt32 SYSYNC_UINT4;
+
+/* MD5 context. */
+typedef struct {
+ SYSYNC_UINT4 state[4]; /* state (ABCD) */
+ SYSYNC_UINT4 count[2]; /* number of bits, modulo 2^64 (lsb first) */
+ uInt8 buffer[64]; /* input buffer */
+ uInt8 PADDING[64]; /* padding space */
+} SYSYNC_MD5_CTX;
+
+/* MD5 functions */
+void Init (SYSYNC_MD5_CTX *);
+void Update (SYSYNC_MD5_CTX *, const uInt8 *, uInt32);
+void Final (uInt8 [16], SYSYNC_MD5_CTX *);
+
+#ifdef MD5_TEST_FUNCS
+/* for test */
+void String (const char *aString, char *s);
+void Print (uInt8 *digest, char * &s);
+#endif
+
+} // end namespace md5
+
+#endif // SYSYNC_MD5_H
+
+/* eof */
diff --git a/src/sysync/sysync_precomp.h b/src/sysync/sysync_precomp.h
new file mode 100755
index 0000000..9e94740
--- /dev/null
+++ b/src/sysync/sysync_precomp.h
@@ -0,0 +1,63 @@
+/* Headers that might be available in precompiled form
+ * (standard libraries, SyncML toolkit...)
+ */
+
+#ifndef SYSYNC_PRECOMP_H
+#define SYSYNC_PRECOMP_H
+
+/* global includes */
+#include "target_options.h"
+
+/* compiler specifics */
+/* %%% probably obsolete
+#ifdef __BORLANDC__
+typedef __int64 longlong;
+typedef __uint64 ulonglong;
+#else if defined(_MSC_VER)
+typedef __int64 longlong;
+typedef unsigned __int64 ulonglong;
+#endif
+*/
+
+/* standard C includes */
+#ifdef __PALM_OS__
+ // don't use the *.h versions! they don't work any more with CW Palm v9
+ #include <ctype>
+ #include <cstdarg>
+ #include <cstdio>
+ #include <cstring>
+ #include <ctime>
+#else
+ #include <ctype.h>
+ #include <stdarg.h>
+ #include <stdio.h>
+ #include <string.h>
+ #if !defined(WINCE) && !defined(MACOSX)
+ #include <time.h> // BCPPB
+ #endif
+#endif
+
+/* C++/STL includes */
+/* - RTTI */
+#if !defined(WINCE) && !defined(__EPOC_OS__)
+ #include <typeinfo>
+#endif
+#if __MC68K__
+ #warning "STL headers excluded from precompiled headers due to problems with CW Palm v9"
+#else
+ /* - STL */
+ #include <string>
+ #include <vector>
+ #include <map>
+ #include <list>
+#endif
+
+#if defined(LINUX) || defined(__EPOC_OS__)
+ // gcc in standard distrs lacks the <limits> STL header
+ #include <limits.h>
+#else
+ #include <limits>
+#endif
+
+
+#endif // defined SYSYNC_PRECOMP_H
diff --git a/src/sysync/sysync_precomp_xpt.h b/src/sysync/sysync_precomp_xpt.h
new file mode 100755
index 0000000..505191a
--- /dev/null
+++ b/src/sysync/sysync_precomp_xpt.h
@@ -0,0 +1,18 @@
+/* Headers that might be available in precompiled form
+ * (standard libraries, SyncML toolkit...)
+ */
+
+#ifndef SYSYNC_PRECOMP_XPT_H
+#define SYSYNC_PRECOMP_XPT_H
+
+
+// include what is needed anyway for sysync...
+#include "sysync_precomp.h"
+
+// ...plus XPT SyncML Toolkit includes
+// - SyncML Toolkit external API
+extern "C" {
+ #include "xpt.h"
+}
+
+#endif // defined SYSYNC_PRECOMP_XPT_H
diff --git a/src/sysync/sysync_utils.cpp b/src/sysync/sysync_utils.cpp
new file mode 100755
index 0000000..b63fcb8
--- /dev/null
+++ b/src/sysync/sysync_utils.cpp
@@ -0,0 +1,2928 @@
+/*
+ * File: sysync_utils.cpp
+ *
+ * Author: Lukas Zeller (luz@synthesis.ch)
+ *
+ * Provides some helper functions interfacing between SyncML Toolkit
+ * and C++
+ *
+ * Copyright (c) 2001-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ * 2001-05-16 : luz : created
+ *
+ */
+
+#include "prefix_file.h"
+
+#include "sysync_utils.h"
+
+#include "libmem.h"
+
+
+#ifdef SYSYNC_TOOL
+ #include "syncappbase.h" // for CONSOLEPRINTF
+ #include "customimplagent.h" // for DBCharSetNames
+#endif
+
+namespace sysync {
+
+// Support for SySync Diagnostic Tool
+#ifdef SYSYNC_TOOL
+
+// parse RFC 2822 addr spec
+int parse2822AddrSpec(int argc, const char *argv[])
+{
+ if (argc<0) {
+ // help requested
+ CONSOLEPRINTF((" addrparse <RFC2822 addr-spec string to parse>"));
+ CONSOLEPRINTF((" Parse name and email address out of a RFC2822-type addr-spec"));
+ return EXIT_SUCCESS;
+ }
+ // check for argument
+ if (argc<1) {
+ CONSOLEPRINTF(("1 argument required"));
+ return EXIT_FAILURE;
+ }
+ // parse
+ string addrname,addremail;
+ const char* p=argv[0];
+ p=parseRFC2822AddrSpec(p,addrname,addremail);
+ // show
+ CONSOLEPRINTF(("Input : %s",argv[0]));
+ CONSOLEPRINTF(("Name : %s",addrname.c_str()));
+ CONSOLEPRINTF(("email : %s",addremail.c_str()));
+ CONSOLEPRINTF(("unparsed rest : %s",p));
+ return EXIT_SUCCESS;
+} // parse2822AddrSpec
+
+
+// convert between character sets
+int charConv(int argc, const char *argv[])
+{
+ if (argc<0) {
+ // help requested
+ CONSOLEPRINTF((" charconv [<input charset>] <output charset> <C-string to convert>"));
+ CONSOLEPRINTF((" Convert from one charset to another. Default input is UTF-8"));
+ return EXIT_SUCCESS;
+ }
+
+ #ifdef __TEST_EQUALITY_OF_CP936_WITH_GB2312__
+ // quick test
+ uInt32 ch_in;
+ for (ch_in=0x8100; ch_in<=0xFFFF; ch_in++) {
+ // convert into internal UTF-8
+ string s_internal,s_in;
+ s_in.erase();
+ if (ch_in>=0x8100) s_in+=(ch_in >> 8) & 0xFF;
+ s_in+=(ch_in & 0xFF);
+ s_internal.erase();
+ appendStringAsUTF8(
+ s_in.c_str(),
+ s_internal,
+ chs_gb2312
+ );
+ // convert into output format
+ string s_out;
+ s_out.erase();
+ appendUTF8ToString(
+ s_internal.c_str(),
+ s_out,
+ chs_cp936
+ );
+ // show differences
+ if (s_in!=s_out && s_out.size()>0 && s_out[0]!=INCONVERTIBLE_PLACEHOLDER) {
+ string s1,s2;
+ s1.erase(); StrToCStrAppend(s_in.c_str(), s1);
+ s2.erase(); StrToCStrAppend(s_out.c_str(), s2);
+ CONSOLEPRINTF(("\"%s\" != \"%s\"",s1.c_str(),s2.c_str()));
+ }
+ }
+ return EXIT_SUCCESS;
+ #endif
+
+ // check for argument
+ if (argc<2) {
+ CONSOLEPRINTF(("2 or 3 arguments required"));
+ return EXIT_FAILURE;
+ }
+ int ochsarg=1;
+ sInt16 enu;
+ // get input charset
+ TCharSets charset_in=chs_utf8;
+ if (argc==3) {
+ // first arg is input charset
+ if (!StrToEnum(DBCharSetNames, numCharSets, enu, argv[0])) {
+ CONSOLEPRINTF(("'%s' is not a valid input charset name",argv[0]));
+ return EXIT_FAILURE;
+ }
+ charset_in = (TCharSets)enu;
+ }
+ else {
+ ochsarg=0; // first arg ist input charset
+ }
+ // get output charset
+ TCharSets charset_out;
+ if (!StrToEnum(DBCharSetNames, numCharSets, enu, argv[ochsarg])) {
+ CONSOLEPRINTF(("'%s' is not a valid output charset name",argv[ochsarg]));
+ return EXIT_FAILURE;
+ }
+ charset_out = (TCharSets)enu;
+ // get string to convert
+ string s_in;
+ s_in.erase();
+ CStrToStrAppend(argv[ochsarg+1], s_in);
+ // convert into internal UTF-8
+ string s_internal;
+ s_internal.erase();
+ appendStringAsUTF8(
+ s_in.c_str(),
+ s_internal,
+ charset_in
+ );
+ // convert into output format
+ string s_out;
+ s_out.erase();
+ appendUTF8ToString(
+ s_internal.c_str(),
+ s_out,
+ charset_out
+ );
+ // show all three
+ string show;
+ // - input
+ show.erase(); StrToCStrAppend(s_in.c_str(), show);
+ CONSOLEPRINTF(("Input : %-20s = \"%s\"",DBCharSetNames[charset_in], show.c_str()));
+ // - internal UTF8
+ show.erase(); StrToCStrAppend(s_internal.c_str(), show);
+ CONSOLEPRINTF(("Internal : %-20s = \"%s\"",DBCharSetNames[chs_utf8], show.c_str()));
+ // - output
+ show.erase(); StrToCStrAppend(s_out.c_str(), show);
+ CONSOLEPRINTF(("Output : %-20s = \"%s\"",DBCharSetNames[charset_out], show.c_str()));
+ return EXIT_SUCCESS;
+} // charConv
+
+#endif // SYSYNC_TOOL
+
+
+// conversion table from ANSI 0x80..0x9F to UCS4
+static const uInt32 Ansi_80_to_9F_to_UCS4[0x20] = {
+ 0x20AC, 0 ,0x201A,0x0192, 0x201E,0x2026,0x2020,0x2021, // 0x80..0x87
+ 0x02C6,0x2030,0x0160,0x2039, 0x0152, 0 ,0x017D, 0 , // 0x88..0x8F
+ 0 ,0x2018,0x2019,0x201C, 0x201D,0x2022,0x2013,0x2014, // 0x90..0x97
+ 0x02DC,0x2122,0x0161,0x203A, 0x0153, 0 ,0x017E,0x0178 // 0x98..0x9F
+};
+
+
+// ASCIIfy table to convert umlauts etc. to nearest plain ASCII
+typedef struct {
+ uInt32 ucs4;
+ uInt8 ascii;
+} TASCIIfyEntry;
+
+static const TASCIIfyEntry ASCIIfyTable[] = {
+ { 0x000000C4, 'A' }, // Adieresis
+ { 0x000000C5, 'A' }, // Aring
+ { 0x000000C7, 'C' }, // Ccedilla
+ { 0x000000C9, 'E' }, // Eacute
+ { 0x000000D1, 'N' }, // Ntilde
+ { 0x000000D6, 'O' }, // Odieresis
+ { 0x000000DC, 'U' }, // Udieresis
+ { 0x000000E1, 'a' }, // aacute
+ { 0x000000E0, 'a' }, // agrave
+ { 0x000000E2, 'a' }, // acircumflex
+ { 0x000000E4, 'a' }, // adieresis
+ { 0x000000E3, 'a' }, // atilde
+ { 0x000000E5, 'a' }, // aring
+ { 0x000000E7, 'c' }, // ccedilla
+ { 0x000000E9, 'e' }, // eacute
+ { 0x000000E8, 'e' }, // egrave
+ { 0x000000EA, 'e' }, // ecircumflex
+ { 0x000000EB, 'e' }, // edieresis
+ { 0x000000ED, 'i' }, // iacute
+ { 0x000000EC, 'i' }, // igrave
+ { 0x000000EE, 'i' }, // icircumflex
+ { 0x000000EF, 'i' }, // idieresis
+ { 0x000000F1, 'n' }, // ntilde
+ { 0x000000F3, 'o' }, // oacute
+ { 0x000000F2, 'o' }, // ograve
+ { 0x000000F4, 'o' }, // ocircumflex
+ { 0x000000F6, 'o' }, // odieresis
+ { 0x000000F5, 'o' }, // otilde
+ { 0x000000FA, 'u' }, // uacute
+ { 0x000000F9, 'u' }, // ugrave
+ { 0x000000FB, 'u' }, // ucircumflex
+ { 0x000000FC, 'u' }, // udieresis
+ { 0x000000DF, 's' }, // germandoubles
+ { 0x000000D8, 'O' }, // Oslash
+ { 0x000000F8, 'o' }, // oslash
+ { 0x000000C0, 'A' }, // Agrave
+ { 0x000000C3, 'A' }, // Atilde
+ { 0x000000D5, 'O' }, // Otilde
+ { 0x00000152, 'O' }, // OE
+ { 0x00000153, 'o' }, // oe
+ { 0x000000C6, 'A' }, // AE
+ { 0x000000E6, 'a' }, // ae
+ { 0x000000C2, 'A' }, // Acircumflex
+ { 0x000000CA, 'E' }, // Ecircumflex
+ { 0x000000C1, 'A' }, // Aacute
+ { 0x000000CB, 'E' }, // Edieresis
+ { 0x000000C8, 'E' }, // Egrave
+ { 0x000000CD, 'I' }, // Iacute
+ { 0x000000CC, 'I' }, // Igrave
+ { 0x000000CE, 'i' }, // Icircumflex
+ { 0x000000CF, 'i' }, // Odieresis
+ { 0x000000D3, 'O' }, // Oacute
+ { 0x000000D2, 'O' }, // Ograve
+ { 0x000000D4, 'O' }, // Ocircumflex
+ // terminator
+ { 0,0 }
+};
+
+
+// line end mode names
+const char * const lineEndModeNames[numLineEndModes] = {
+ "none", // none specified
+ "unix", // 0x0A
+ "mac", // 0x0D
+ "dos", // 0x0D 0x0A
+ "cstr", // as in C strings, '\n' which is 0x0A normally (but might be 0x0D on some platforms)
+ "filemaker" // 0x0B (filemaker tab-separated text format, CR is shown as 0x0B within fields
+};
+
+
+
+// literal quoting mode names
+const char * const quotingModeNames[numQuotingModes] = {
+ "none", // none specified
+ "singlequote", // single quote must be duplicated
+ "doublequote", // double quote must be duplicated
+ "backslash" // C-string-style escapes of CR,LF,TAB,BS,\," and ' (but no full c-string escape with \xXX etc.)
+};
+
+
+// Encoding format names for SyncML
+const char * const encodingFmtSyncMLNames[numFmtTypes] = {
+ "chr", // plain chars
+ "bin", // binary
+ "b64" // base 64 encoding
+};
+// Encoding format names for user
+const char * const encodingFmtNames[numFmtTypes] = {
+ "plain-text", // no encoding (plain text)
+ "binary", // plain binary (in WBXML only)
+ "base64" // base 64 encoding
+};
+
+
+// field (property) data type names
+const char * const propDataTypeNames[numPropDataTypes] = {
+ "chr", // Character
+ "int", // Integer
+ "bool", // Boolean
+ "bin", // Binary
+ "datetime", // Date and time of day
+ "phonenum", // Phone number
+ "text", // plain text
+ "???" // unknown
+};
+
+
+// Auth type names
+const char * const authTypeSyncMLNames[numAuthTypes] = {
+ NULL, // no authorisation
+ "syncml:auth-basic", // basic (B64 encoded user pw string)
+ "syncml:auth-md5" // Md5 encoded user:pw:nonce
+};
+
+
+// MIME encoding types
+const char * const MIMEEncodingNames[numMIMEencodings] = {
+ "",
+ "7BIT",
+ "8BIT",
+ "BINARY",
+ "QUOTED-PRINTABLE",
+ "BASE64",
+ "B"
+};
+
+// Charset names for MIME based strings
+const char * const MIMECharSetNames[numCharSets] = {
+ "unknown",
+ "US-ASCII",
+ "ANSI",
+ "ISO-8859-1",
+ "UTF-8",
+ "UTF-16",
+ #ifdef CHINESE_SUPPORT
+ "GB2312",
+ "CP936",
+ #endif
+};
+
+
+// generate RFC2822-style address specificiation
+// - Common Name will be quoted
+// - recipient will be put in angle brackets
+void makeRFC2822AddrSpec(
+ cAppCharP aCommonName,
+ cAppCharP aRecipient,
+ string &aRFCAddr
+)
+{
+ if (aCommonName && *aCommonName) {
+ aRFCAddr='"';
+ while (*aCommonName) {
+ if (*aCommonName=='"') aRFCAddr += "\\\"";
+ else aRFCAddr += *aCommonName;
+ aCommonName++;
+ }
+ aRFCAddr+="\" <";
+ aRFCAddr+=aRecipient;
+ aRFCAddr+=">";
+ }
+ else {
+ // plain email address
+ aRFCAddr=aRecipient;
+ }
+} // makeRFC2822AddrSpec
+
+
+
+
+// sysytool -f syncserv_odbc.xml addrparse "(Lukas Peter) luz@synthesis.ch (Zeller), gaga"
+
+// Parse RFC2822-style address specificiation
+// - aName will receive name and all (eventual) comments
+// - aRecipient will receive the (first, in case of a group) email address
+cAppCharP parseRFC2822AddrSpec(
+ cAppCharP aText,
+ string &aName,
+ string &aRecipient
+)
+{
+ const char *p;
+ char c;
+
+ enum {
+ pstate_sepspace,
+ pstate_trailing,
+ pstate_text,
+ pstate_comment,
+ pstate_quoted,
+ pstate_email
+ } pstate = pstate_trailing;
+ string text,groupname;
+ bool textcouldbeemail=true;
+ bool atfound=false;
+ aName.erase();
+ aRecipient.erase();
+ p=aText;
+ do {
+ c=*p;
+ // check end of input
+ if (c==0) break; // done with the string
+ // advance to next char
+ p++;
+ // check according to state
+ switch (pstate) {
+ case pstate_sepspace:
+ if (c==' ') {
+ aName+=c;
+ }
+ pstate=pstate_trailing;
+ // otherwise treat like trailing
+ case pstate_trailing:
+ textcouldbeemail=aRecipient.empty();
+ atfound=false;
+ // skip trailing WSP first
+ if (c==' ' || c=='\t' || c=='\n' || c=='\r') break; // simply ignore WSP in trailing mode
+ else pstate=pstate_text;
+ // fall trough to do text analysis
+ case pstate_text:
+ // now check specials
+ if (c==',') { c=0; break; } // end of address, cause exit from loop, next will start after comma
+ else if (c==';') { c=0; break; } // end of group address list, treat it like single address
+ else if (c=='@' && textcouldbeemail) atfound=true; // flag presence of @
+ // check if text could still be a email address by itself
+ if (textcouldbeemail && !isalnum(c) && c!='@' && c!='_' && c!='-' && c!='.') {
+ textcouldbeemail=false;
+ if (atfound) {
+ aRecipient=text;
+ text.erase();
+ }
+ atfound=false;
+ }
+ // now check other specials
+ if (c=='"') { pstate=pstate_quoted; } // start of quoted string
+ else if (c=='(') { pstate=pstate_comment; } // start of comment
+ else if (c=='<') { aRecipient.erase(); pstate=pstate_email; } // start of angle-addr, overrides other recipient texts
+ else if (c==':') {
+ groupname=aRecipient; // what we've probably parsed as recipient
+ groupname+=aName; // plus name so far
+ groupname+=text; // plus additional text
+ text.erase();
+ aName.erase();
+ aRecipient.erase();
+ pstate=pstate_trailing;
+ } // flag presence of a group name (which can be used as name if addr itself does not have one)
+ else {
+ // add other text chars to the text
+ text += c;
+ }
+ break;
+ case pstate_quoted:
+ if (c=='\\') {
+ if (*p) c=*p++; else break; // get next char (if any) and add to result untested
+ }
+ else if (c=='"') {
+ // end of quoted string
+ pstate=pstate_sepspace;
+ aName+=text;
+ text.erase();
+ break;
+ }
+ // add to text
+ text += c;
+ break;
+ case pstate_comment:
+ if (c==')') {
+ // end of comment
+ aName+=text;
+ text.erase();
+ pstate=pstate_sepspace;
+ break;
+ }
+ // add to text
+ text += c;
+ break;
+ case pstate_email:
+ if (!isalnum(c) && c!='@' && c!='_' && c!='-' && c!='.') {
+ // any non-email char terminates email, not only '>', but only '>' is swallowed
+ if (c!='>') p--; // re-evaluate char in next state
+ pstate=pstate_sepspace;
+ break;
+ }
+ // add to email
+ aRecipient += c;
+ break;
+ } // switch
+ } while (c!=0);
+ // handle case of pure email address without name and without < > brackets or :
+ if (aRecipient.empty() && textcouldbeemail && atfound)
+ aRecipient = text;
+ else
+ aName += text;
+ // if name is (now) empty, but we have a group name, use the group name
+ if (aName.empty()) aName=groupname;
+ // remove trailing spaces in aName
+ string::size_type n=aName.find_last_not_of(' ');
+ if (n!=string::npos) aName.resize(n+1);
+ // return where to continue parsing for next addr-spec (if not end of string)
+ return p;
+} // parseRFC2822AddrSpec
+
+
+
+// append internal UTF8 string as RFC2047 style encoding
+const char *appendUTF8AsRFC2047(
+ const char *aText,
+ string &aString
+)
+{
+ const char *p,*q,*r;
+ char c;
+
+ p=aText;
+ do {
+ q=p; // remember start
+ // find chars until next char that must be stored as encoded word
+ do {
+ c=*p;
+ if (c==0 || (c & 0x80) || (c=='=' && *(p+1)=='?')) break;
+ p++;
+ } while(true);
+ // copy chars outside encoded word directly
+ if (p-q>0) aString.append(q,p-q);
+ // check if end of string
+ if (c==0) break;
+ // pack some chars into encoded word
+ // - start word
+ aString.append("=?utf-8?B?"); // 10 chars start (+ 2 chars will be added at end)
+ // - encoded data must be 75-12=63 chars or less
+ // Using B (=b64) encoding, output of 63 chars = 63/4*3 = max 47 chars.
+ // We use 45 max, as this is evenly divisible by 3 and output is 60 chars
+ q=p;
+ while (true) {
+ // find next space
+ while (*q && !isspace(*q) && q-p<45) q++;
+ if (q-p>=45) break; // abort if exhausted already
+ // find next non-space
+ r=q;
+ while (isspace(*r)) r++;
+ // check if next non-space will start a new word
+ if (*r & 0x80) {
+ // we should include the next word as well, if possible without exceeding size
+ if (r-p<45) {
+ q=r;
+ continue;
+ }
+ }
+ break;
+ }
+ // encode binary stream and append to string
+ appendEncoded((const uInt8 *)p,q-p,aString,enc_b);
+ p=q;
+ // - end word
+ aString.append("?=");
+ } while (true);
+ return p;
+} // appendUTF8AsRFC2047
+
+
+// parse character string from RFC2047 style encoding to UTF8 internal string
+const char *appendRFC2047AsUTF8(
+ const char *aRFC2047,
+ stringSize aSize,
+ string &aString,
+ TLineEndModes aLEM
+)
+{
+ const char *p,*q,*r,*w;
+ char c;
+ const char *eot = aRFC2047+aSize;
+
+ p=aRFC2047;
+ w=NULL; // start of last detected word (to avoid re-scanning)
+ while (p<eot) {
+ q=p; // remember start
+ // find chars until next encoded word
+ while (p<eot) {
+ c=*p;
+ if (c==0 || (p!=w && c=='=' && *(p+1)=='?')) break;
+ p++;
+ }
+ // copy chars outside encoded word directly
+ aString.append(q,p-q);
+ // check if end of string
+ if (p>=eot || c==0) break;
+ // try to parse encoded word
+ q=p+2;
+ scanword:
+ // q is now where we start to parse word contents
+ // p is where we would re-start reading normally if current word turns out not to be a word at all
+ // - remember start of word scan (to avoid re-scanning it)
+ w=p;
+ // - get charset
+ r=q;
+ while (q<eot && *q!='?' && isgraph(*q)) q++;
+ if (q>=eot || *q!='?') continue; // is not an encoded word, parse normally
+ sInt16 en;
+ TCharSets charset=chs_unknown;
+ if (StrToEnum(MIMECharSetNames, numCharSets, en, r, q-r)) charset=(TCharSets)en;
+ // - get encoding
+ r=++q; // continue after ? separator
+ while (q<eot && *q!='?' && isgraph(*q)) q++;
+ if (q>=eot || *q!='?') continue; // is not an encoded word, parse normally
+ TEncodingTypes encoding=enc_8bit;
+ if (StrToEnum(MIMEEncodingNames, numMIMEencodings, en, r, q-r)) encoding=(TEncodingTypes)en;
+ // - get data part
+ r=++q;
+ while (q+1<eot && *q && *q!=' ' && !(*q=='?' && *(q+1)=='=')) q++;
+ if (q>=eot || *q!='?') continue; // is not an encoded word, parse normally
+ // - decode
+ string decoded;
+ appendDecoded(r,q-r,decoded,encoding);
+ // - convert to UTF-8
+ appendStringAsUTF8(
+ decoded.c_str(),
+ aString,
+ charset,
+ aLEM
+ );
+ // - skip word terminator
+ p=q+2;
+ // - check for special case of adjacent words
+ q=p;
+ while (q<eot && isspace(*q)) q++;
+ if (q+1<eot && q>p && *q=='=' && *(q+1)=='?') {
+ // adjacent encoded words, only separated by space -> ignore space
+ // p is after previous word
+ q+=2;
+ // q is after lead-in of next word
+ goto scanword;
+ }
+ // p is where we continue reading
+ }
+ return p;
+} // appendRFC2047AsUTF8
+
+
+// decode encoded data and append to string
+const char *appendDecoded(
+ const char *aText,
+ size_t aSize,
+ string &aBinString,
+ TEncodingTypes aEncoding
+)
+{
+ char c;
+ const char *p=aText;
+ uInt32 binsz;
+ uInt8 *binP;
+
+ switch (aEncoding) {
+ case enc_quoted_printable :
+ // decode quoted-printable content
+ while ((c=*p++)) {
+ // char found
+ if (c=='=') {
+ uInt16 code;
+ char hex[2];
+ // check for soft break first
+ if (*p=='\x0D' || *p=='\x0A') {
+ // soft break, swallow
+ if (*p=='\x0D') p++;
+ if (*p=='\x0A') p++;
+ continue;
+ }
+ // decode
+ hex[0]=*p;
+ if (*p) {
+ p++;
+ hex[1]=*p;
+ if (*p) {
+ p++;
+ if (HexStrToUShort(hex,code,2)==2) {
+ c=code; // decoded char
+ }
+ else continue; // simply ignore
+ }
+ else break;
+ }
+ else break;
+ }
+ // append char
+ aBinString+=c;
+ }
+ aText=p;
+ break;
+ case enc_base64:
+ case enc_b:
+ // decode base 64
+ binsz=0;
+ binP = b64::decode(aText, aSize, &binsz);
+ aBinString.append((const char *)binP,binsz);
+ sysync_free(binP);
+ aText+=aSize;
+ break;
+ case enc_7bit:
+ case enc_8bit:
+ // copy no more than size
+ if (aSize>0) aBinString.reserve(aBinString.size()+aSize);
+ while (*p && aSize>0) {
+ aBinString+=*p++;
+ aSize--;
+ }
+ aText=p;
+ break;
+ case enc_none:
+ case enc_binary:
+ // copy bytes
+ aBinString.append(aText,aSize);
+ aText+=aSize;
+ break;
+ case numMIMEencodings:
+ // invalid
+ break;
+ } // quoted printable
+ return aText;
+} // appendDecoded
+
+
+
+// encode binary stream and append to string
+void appendEncoded(
+ const uInt8 *aBinary,
+ size_t aSize,
+ string &aString,
+ TEncodingTypes aEncoding,
+ sInt16 aMaxLineSize,
+ sInt32 aCurrLineSize,
+ bool aSoftBreaksAsCR,
+ bool aEncodeBinary
+)
+{
+ char c;
+ string::size_type linestart;
+ const uInt8 *p;
+ bool softbreak;
+ uInt32 b64len;
+ char *b64;
+ bool processed;
+
+ switch (aEncoding) {
+ case enc_binary :
+ case enc_none :
+ case enc_8bit :
+ case enc_7bit : // assume we have no 8bit chars
+ // just copy 1:1
+ aString.append((const char *)aBinary,aSize);
+ break;
+ case enc_quoted_printable:
+ // quote-printable encoding
+ // - determine start of last line in aString
+ // Note: this is because property text will be folded when lines aMaxLineSize
+ linestart=aString.size()-aCurrLineSize;
+ for (p=aBinary;p<aBinary+aSize;p++) { // '\0' will not terminate the 'for' loop
+ c=*p;
+ if (!aEncodeBinary && !c) break; // still exit at NUL when not encoding real binary data
+ processed=false; // input data in c is not yet processed
+ // make sure we do not go over the limit (if one is set)
+ // - if less than 8 chars (=0D=0A + =\r) are free, soft break the line
+ softbreak= aMaxLineSize && (aString.size()-linestart>=string::size_type(aMaxLineSize)-8);
+ if (!aEncodeBinary) {
+ if (c=='\r') continue; // ignore them
+ if (c=='\b') continue; // ignore them (optional break indicators, not relevant for QP output)
+ if (c=='\n') { // - encode line ends
+ aString.append("=0D=0A"); // special string for Line Ends (CR LF plus soft line break)
+ processed=true; // c is processed now
+ softbreak=true;
+ } // if
+ } // if
+ // - handle soft line break (but only if really doing line breaking)
+ if (softbreak && aMaxLineSize) {
+ if (aSoftBreaksAsCR)
+ aString.append("=\r"); // '\r' signals softbreak for finalizeproperty()
+ else
+ aString.append("=\x0D\x0A"); // break line here
+ // new line starts after softbreak
+ linestart=aString.size();
+ // make sure soft line break is not followed by unencoded space
+ // (which would look like MIME folding)
+ if (c==' ' || (processed && p[1]==' ')) {
+ aString.append("=20");
+ if (processed) p++; // if current char was already processed, we need to explicitly skip the space
+ processed=true; // char is now processed in any case
+ } // if
+ } // if
+ // now encode the char in c if not already processed by now
+ if (!processed) {
+ bool encodeIt=
+ (c=='=') // escape equal sign itself
+ || (c=='<' && aEncodeBinary) // avoid XML mismatch problems
+ || (uInt8)c>0x7F
+ || (uInt8)c<0x20; // '\0' will be encoded as well
+ if (encodeIt) { // encode all non ASCII chars > 0x7F (and control chars as well)
+ aString+="=";
+ aString+=NibbleToHexDigit(c>>4);
+ aString+=NibbleToHexDigit(c);
+ }
+ else
+ aString+=c; // just copy
+ } // if
+ }
+ break;
+ case enc_base64:
+ case enc_b:
+ // use base64 encoding
+ if (aSize>0) {
+ // don't call b64 with size=0!
+ b64 = b64::encode(
+ aBinary,aSize, // what to encode
+ &b64len, // output size
+ aMaxLineSize, // max line size
+ aSoftBreaksAsCR
+ );
+ // append to output, if any
+ if (b64) {
+ aString.append(b64,b64len);
+ // release buffer
+ sysync_free(b64);
+ }
+ if (aEncoding!=enc_b) {
+ // make sure it ends with a newline for "base64" (but NOT for "b" as used in RFC2047)
+ aString+= aSoftBreaksAsCR ? "\r" : "\x0D\x0A";
+ }
+ }
+ break;
+ default:
+ // do nothing
+ break;
+ } // switch
+} // appendEncoded
+
+
+#ifdef CHINESE_SUPPORT
+// the flatBinTree tables for converting to and from GB2312
+#include "gb2312_tables_inc.cpp"
+// the flatBinTree tables for converting to and from CP936
+#include "cp936_tables_inc.cpp"
+#endif
+
+
+// add char (possibly multi-byte) as UTF8 to value and apply charset translation if needed
+// - returns > 0 if aNumChars was not correct number of bytes needed to convert an entire character;
+// return value is number of bytes needed to generate one output character. If return value
+// is<>0, no char has been appended to aVal.
+uInt16 appendCharsAsUTF8(const char *aChars, string &aVal, TCharSets aCharSet, uInt16 aNumChars)
+{
+ uInt32 ucs4;
+ // first char
+ uInt8 c=*aChars;
+ // this is a 8-bit char
+ switch(aCharSet) {
+ case chs_utf8 :
+ // UTF8 is native charset of the application, simply add
+ aVal+=c;
+ break;
+ case chs_ansi :
+ case chs_iso_8859_1 :
+ // do poor man's conversion to UCS4
+ // - most ANSI chars are 1:1 mapped
+ ucs4 = ((uInt8)c & 0xFF);
+ // - except 0x80..0x9F, use table for these
+ if (ucs4>=0x80 && ucs4<=0x9F)
+ ucs4=Ansi_80_to_9F_to_UCS4[ucs4-0x80];
+ // - convert to UTF8
+ UCS4toUTF8(ucs4,aVal);
+ break;
+ #ifdef CHINESE_SUPPORT
+ case chs_gb2312 : // simplified Chinese GB-2312 charset
+ // all below 0x80 are passed as-is
+ if (c<0x80)
+ aVal+=c; // simply append
+ else {
+ // 16-bit GB2312 char
+ if (aNumChars!=2)
+ return 2; // we need 2 chars for a successful GB-2312
+ // we have 2 bytes, convert them
+ ucs4 = searchFlatBintree(gb2312_to_ucs2, (c<<8) + (uInt8)aChars[1], INCONVERTIBLE_PLACEHOLDER);
+ // - convert to UTF8
+ UCS4toUTF8(ucs4,aVal);
+ }
+ break;
+ case chs_cp936: // simplified chinese Windows codepage CP936
+ if (c<0x80)
+ aVal+=c; // simply append
+ else {
+ // 0x0080 (euro sign) or 2-byte CP936
+ if (c==0x80)
+ ucs4=searchFlatBintree(cp936_to_ucs2, 0x0080, INCONVERTIBLE_PLACEHOLDER);
+ else {
+ // 16-bit GB2312 char
+ if (aNumChars!=2)
+ return 2; // we need 2 chars for a successful CP936
+ // we have 2 bytes, convert them
+ ucs4 = searchFlatBintree(cp936_to_ucs2, (c<<8) + (uInt8)aChars[1], INCONVERTIBLE_PLACEHOLDER);
+ }
+ // - convert to UTF8
+ UCS4toUTF8(ucs4,aVal);
+ }
+ break;
+ #endif
+ case chs_ascii : // plain 7-bit ASCII
+ default : // unknown
+ // only 7-bit allowed
+ if (c & 0x80)
+ aVal+=INCONVERTIBLE_PLACEHOLDER;
+ else
+ aVal+=c;
+ break;
+ } // switch
+ return 0; // ok, converted aNumChars
+} // appendCharsAsUTF8
+
+
+
+
+// add string as UTF8 to value and apply charset translation if needed
+// - if lineEndMode is not lem_none, all sorts of line ends will be converted
+// to the specified mode.
+void appendStringAsUTF8(const char *s, string &aVal, TCharSets aCharSet, TLineEndModes aLEM, bool aAllowFilemakerCR)
+{
+ char c;
+ const char *start=s;
+ if (s) {
+ while ((c=*s++)!=0) {
+ if (aLEM!=lem_none) {
+ // line end handling enabled
+ if (c==0x0D) {
+ // could be mac (0x0D) or DOS (0x0D/0x0A)
+ if (*s==0x0A) {
+ // this is DOS-type line end
+ // - consume the 0x0A as well
+ s++;
+ // - check for 0x0D 0x0D 0x0A special case (caused by
+ // DOS-text-file conversion of non-DOS strings)
+ if (s>=start+3) {
+ if (*(s-3)==0x0D) {
+ // char before the DOS-CRLF was a 0x0D as well (and
+ // has already produced a newline in the output
+ // --> completely ignore this CRLF
+ continue;
+ }
+ }
+ }
+ // is a line end, convert it to platform-lineend
+ c='\n'; // platform
+ }
+ else if (c==0x0A) {
+ // 0x0A without preceeding 0x0D = unix
+ c='\n'; // platform
+ }
+ else if (c==0x0B && aAllowFilemakerCR) {
+ // 0x0B is used as lineend in filemaker export and achilformat
+ c='\n';
+ }
+ // line end converted to platform
+ if (c=='\n' && aLEM!=lem_cstr) {
+ // produce specified line end
+ switch (aLEM) {
+ case lem_mac : c=0x0D; break;
+ case lem_unix : c=0x0A; break;
+ case lem_filemaker : c=0x0B; break;
+ case lem_dos :
+ c=0x0A; // LF will be added later
+ aVal+=0x0D; // add CR
+ break;
+ default: break;
+ }
+ }
+ } // line end handling enabled
+ // normal add
+ uInt16 i,seqlen=1; // assume logical char consists of single byte
+ do {
+ seqlen=appendCharsAsUTF8(s-seqlen,aVal,aCharSet,seqlen); // add char (eventually with UTF8 expansion) to aVal
+ if (seqlen<=1) break; // done
+ for (i=1;i<seqlen;i++) { if (*s==0) break; else s++; }
+ if (i<seqlen) break; // not enough bytes
+ } while(true);
+ }
+ }
+} // appendStringAsUTF8
+
+
+
+// same as appendUTF8ToString, but output string is cleared first
+bool storeUTF8ToString(
+ cAppCharP aUTF8, string &aVal,
+ TCharSets aCharSet,
+ TLineEndModes aLEM,
+ TQuotingModes aQuotingMode,
+ size_t aMaxBytes
+)
+{
+ aVal.erase();
+ return appendUTF8ToString(aUTF8,aVal,aCharSet,aLEM,aQuotingMode,aMaxBytes);
+} // storeUTF8ToString
+
+
+
+// helper for adding chars
+static void appendCharToString(
+ char c,
+ string &aVal,
+ TQuotingModes aQuotingMode
+) {
+ if (aQuotingMode==qm_none) {
+ aVal+=c;
+ }
+ else if (aQuotingMode==qm_backslash) {
+ // treat CR, LF, BS, TAB, single/doublequote and backslash specially
+ if (c==0x0D)
+ aVal+="\\r";
+ else if (c==0x0A)
+ aVal+="\\n";
+ else if (c==0x08)
+ aVal+="\\b";
+ else if (c==0x09)
+ aVal+="\\t";
+ else if (c=='"')
+ aVal+="\\\"";
+ else if (c=='\'')
+ aVal+="\\'";
+ else if (c=='\\')
+ aVal+="\\\\";
+ else
+ aVal+=c;
+ }
+ else if (aQuotingMode==qm_duplsingle) {
+ if (c=='\'') aVal+=c; // duplicate
+ aVal+=c; // normal append
+ }
+ else if (aQuotingMode==qm_dupldouble) {
+ if (c=='"') aVal+=c; // duplicate
+ aVal+=c; // normal append
+ }
+} // appendCharToString
+
+
+// add UTF8 string to value in custom charset
+// - if aLEM is not lem_none, occurrence of any type of Linefeeds
+// (LF,CR,CRLF and even CRCRLF) in input string will be
+// replaced by the specified line end type
+// - aQuotingMode specifies what quoting (for ODBC literals for example) should be used
+// - output is clipped after aMaxBytes bytes (if not 0)
+// - returns true if all input could be converted, false if output is clipped
+bool appendUTF8ToString(
+ cAppCharP aUTF8,
+ string &aVal,
+ TCharSets aCharSet,
+ TLineEndModes aLEM,
+ TQuotingModes aQuotingMode,
+ size_t aMaxBytes
+)
+{
+ uInt32 ucs4;
+ uInt8 c;
+ size_t n=0;
+ cAppCharP p=aUTF8;
+ cAppCharP start=aUTF8;
+
+ if (!aUTF8) return true; // nothing to copy, copied everything of that!
+ if (aCharSet==chs_utf8 && aLEM==lem_none && aQuotingMode==qm_none) {
+ // shortcut: simply append entire string
+ if (aMaxBytes==0)
+ aVal+=aUTF8;
+ else
+ aVal.append(aUTF8,aMaxBytes);
+ // advance "processed" pointer behind consumed part of string
+ p=aUTF8+aVal.size();
+ }
+ else {
+ // process char by char
+ while((c=*aUTF8)!=0 && (aMaxBytes==0 || n<aMaxBytes)) {
+ p=aUTF8;
+ // check for linefeed conversion
+ if (aLEM!=lem_none && (c==0x0D || c==0x0A)) {
+ aUTF8++;
+ // line end, handling enabled
+ if (c==0x0D) {
+ // could be mac (0x0D) or DOS (0x0D/0x0A)
+ if (*aUTF8==0x0A) {
+ // this is DOS-type line end
+ // - consume the 0x0A as well
+ aUTF8++;
+ // - check for 0x0D 0x0D 0x0A special case (caused by
+ // DOS-text-file conversion of non-DOS strings)
+ if (aUTF8>=start+3) {
+ if (*(aUTF8-3)==0x0D) {
+ // char before the DOS-CRLF was a 0x0D as well (and
+ // has already produced a newline in the output
+ // --> completely ignore this CRLF
+ continue;
+ }
+ }
+ }
+ // is a line end, convert it to platform-lineend
+ c='\n'; // platform
+ }
+ else { // must be 0x0A
+ // 0x0A without preceeding 0x0D = unix
+ c='\n'; // platform
+ }
+ // line end converted to platform
+ if (aLEM!=lem_cstr) {
+ // produce specified line end
+ switch (aLEM) {
+ case lem_mac : c=0x0D; break;
+ case lem_filemaker : c=0x0B; break;
+ case lem_unix : c=0x0A; break;
+ case lem_dos :
+ c=0x0A; // LF will be added later
+ n++; // count it extra
+ if (aMaxBytes && n>=aMaxBytes)
+ goto stringfull; // no room to complete it, ignore it
+ appendCharToString(0x0D,aVal,aQuotingMode);
+ break;
+ default: break;
+ }
+ }
+ appendCharToString(c,aVal,aQuotingMode);
+ n++; // count it
+ } // line end, handling enabled
+ else {
+ // non lineend (or lineend not handled specially)
+ if (aCharSet==chs_utf8) {
+ aUTF8++;
+ // - simply add char
+ appendCharToString(c,aVal,aQuotingMode);
+ n++;
+ }
+ else {
+ // - make UCS4
+ p=aUTF8; // save previous position to detect if we have processed all
+ aUTF8=UTF8toUCS4(aUTF8,ucs4);
+ // now we have UCS4
+ if (ucs4==0) {
+ // UTF8 resulting in UCS4 null char is not allowed
+ ucs4=INCONVERTIBLE_PLACEHOLDER;
+ }
+ else {
+ // convert to specified charset
+ switch (aCharSet) {
+ case chs_ansi:
+ case chs_iso_8859_1:
+ if ((ucs4<=0xFF && ucs4>=0xA0) || ucs4<0x80)
+ // 00..7F and A0..FF directly map to ANSI
+ appendCharToString(ucs4,aVal,aQuotingMode);
+ else {
+ // search for matching ANSI in table
+ uInt8 k;
+ for (k=0; k<0x20; k++) {
+ if (ucs4==Ansi_80_to_9F_to_UCS4[k]) {
+ // found in table
+ break;
+ }
+ }
+ if (k<0x20)
+ // conversion found
+ aVal+=k+0x80;
+ else
+ // no conversion found in table
+ aVal+=INCONVERTIBLE_PLACEHOLDER;
+ } // not in 1:1 range 0..7F, A0..FF
+ n++;
+ break;
+ #ifdef CHINESE_SUPPORT
+ case chs_gb2312 : // simplified Chinese GB-2312 charset
+ // all below 0x80 are passed as-is
+ if (ucs4<0x80) {
+ appendCharToString(ucs4,aVal,aQuotingMode); // simply append ASCII codes
+ n++;
+ }
+ else {
+ // convert to 16-bit GB2312 char
+ uInt16 gb = searchFlatBintree(ucs2_to_gb2312, ucs4, INCONVERTIBLE_PLACEHOLDER);
+ // check if we have space
+ if (aMaxBytes!=0 && n+2>aMaxBytes)
+ goto stringfull;
+ // append as two bytes to output string
+ aVal+=gb >> 8;
+ aVal+=gb & 0xFF;
+ n+=2;
+ }
+ break;
+ case chs_cp936 : // simplified Chinese CP936 windows codepage
+ // all below 0x80 are passed as-is
+ if (ucs4<0x80) {
+ appendCharToString(ucs4,aVal,aQuotingMode); // simply append ASCII codes
+ n++;
+ }
+ else {
+ // convert to CP936 16-bit representation
+ uInt16 twobytes = searchFlatBintree(ucs2_to_cp936, ucs4, INCONVERTIBLE_PLACEHOLDER);
+ // append as two bytes to output string, but only this is a CP936 two-byte at all
+ if (twobytes>0x0080) {
+ // check if we have space
+ if (aMaxBytes!=0 && n+2>aMaxBytes)
+ goto stringfull;
+ aVal+=twobytes >> 8; // sub-page lead in
+ n++;
+ }
+ aVal+=twobytes & 0xFF; // sub-page code
+ n++;
+ }
+ break;
+ #endif
+ case chs_ascii:
+ // explicit ASCII: convert some special chars to plain ASCII
+ if ((ucs4 & 0xFFFFFF80) !=0) {
+ // search in ASCIIfy table
+ uInt16 k=0;
+ while (ASCIIfyTable[k].ucs4!=0) {
+ if (ucs4==ASCIIfyTable[k].ucs4) {
+ // found, fetch ASCII-equivalent
+ ucs4=ASCIIfyTable[k].ascii;
+ break; // use it
+ }
+ k++;
+ }
+ }
+ // fall through to default, which does not know ANY non-ASCII
+ default:
+ // only 7 bit ASCII is allowed
+ if ((ucs4 & 0xFFFFFF80) !=0)
+ aVal+=INCONVERTIBLE_PLACEHOLDER;
+ else
+ appendCharToString(ucs4,aVal,aQuotingMode); // simply append ASCII codes
+ n++;
+ break;
+ } // switch
+ } // valid UCS4
+ } // not already UTF8
+ } // if not lineend
+ // processed until here
+ p=aUTF8;
+ } // while not end of input string
+ } // not already UTF8
+ // return true if input string completely consumed
+stringfull:
+ return (*p==0);
+} // appendUTF8ToString
+
+
+// convert UTF8 to UCS4
+// - returns pointer to next char
+// - returns UCS4=0 on error (no char, bad sequence, sequence not complete)
+const char *UTF8toUCS4(const char *aUTF8, uInt32 &aUCS4)
+{
+ uInt8 c;
+ sInt16 morechars;
+
+ if ((c=*aUTF8)!=0) {
+ aUTF8++;
+ // there is a char
+ morechars=0;
+ // decode UTF8 lead-in
+ if ((c & 0x80) == 0) {
+ // single byte
+ aUCS4=c;
+ morechars=0;
+ }
+ else if ((c & 0xE0) == 0xC0) {
+ // two bytes
+ aUCS4=c & 0x1F;
+ morechars=1;
+ }
+ else if ((c & 0xF0) == 0xE0) {
+ aUCS4=c & 0x0F;
+ morechars=2;
+ }
+ else if ((c & 0xF8) == 0xF0) {
+ aUCS4=c & 0x07;
+ morechars=3;
+ }
+ else if ((c & 0xFC) == 0xF8) {
+ aUCS4=c & 0x03;
+ morechars=4;
+ }
+ else if ((c & 0xFE) == 0xFC) {
+ aUCS4=c & 0x01;
+ morechars=5;
+ }
+ else {
+ // bad char
+ aUCS4=0;
+ }
+ // process additional chars
+ while(morechars--) {
+ if ((c=*aUTF8)==0) {
+ // unfinished sequence
+ aUCS4=0;
+ break;
+ }
+ aUTF8++;
+ if ((c & 0xC0) != 0x80) {
+ // bad additional char
+ aUCS4=0;
+ break;
+ }
+ // each additional char adds 6 new bits
+ aUCS4 = aUCS4 << 6; // shift existing bits
+ aUCS4 |= (c & 0x3F); // add new bits
+ }
+ }
+ else {
+ // no char
+ aUCS4=0;
+ }
+ // return pointer to next char
+ return aUTF8;
+} // UTF8toUCS4
+
+
+// convert UCS4 to UTF8 (0 char is not allowed and will be ignored!)
+void UCS4toUTF8(uInt32 aUCS4, string &aUTF8)
+{
+ uInt8 c;
+
+ // ignore null char
+ if (aUCS4==0) return;
+ // create UTF8 lead-in
+ sInt16 morechars=0;
+ if (aUCS4<0x00000080) {
+ // one byte
+ c=aUCS4;
+ }
+ else if (aUCS4<0x00000800) {
+ // two bytes
+ c=0xC0 | ((aUCS4 >> 6) & 0x1F);
+ morechars=1;
+ }
+ else if (aUCS4<0x00010000) {
+ // three bytes
+ c=0xE0 | ((aUCS4 >> 12) & 0x0F);
+ morechars=2;
+ }
+ else if (aUCS4<0x00200000) {
+ // four bytes
+ c=0xF0 | ((aUCS4 >> 18) & 0x07);
+ morechars=3;
+ }
+ else if (aUCS4<0x04000000) {
+ // five bytes
+ c=0xF8 | ((aUCS4 >> 24) & 0x03);
+ morechars=4;
+ }
+ else {
+ // six bytes
+ c=0xFC | ((aUCS4 >> 30) & 0x01);
+ morechars=5;
+ }
+ // add lead-in
+ aUTF8+=c;
+ // add rest of sequence
+ while (morechars--) {
+ c= 0x80 | ((aUCS4 >> (morechars * 6)) & 0x3F);
+ aUTF8+=c;
+ }
+} // UCS4toUTF8
+
+
+/* Encoding UTF-16 (excerpt from RFC 2781, paragraph 2.1)
+
+ Encoding of a single character from an ISO 10646 character value to
+ UTF-16 proceeds as follows. Let U be the character number, no greater
+ than 0x10FFFF.
+
+ 1) If U < 0x10000, encode U as a 16-bit unsigned integer and
+ terminate.
+
+ 2) Let U' = U - 0x10000. Because U is less than or equal to 0x10FFFF,
+ U' must be less than or equal to 0xFFFFF. That is, U' can be
+ represented in 20 bits.
+
+ 3) Initialize two 16-bit unsigned integers, W1 and W2, to 0xD800 and
+ 0xDC00, respectively. These integers each have 10 bits free to
+ encode the character value, for a total of 20 bits.
+
+ 4) Assign the 10 high-order bits of the 20-bit U' to the 10 low-order
+ bits of W1 and the 10 low-order bits of U' to the 10 low-order
+ bits of W2. Terminate.
+
+ Graphically, steps 2 through 4 look like:
+ U' = yyyyyyyyyyxxxxxxxxxx
+ W1 = 110110yyyyyyyyyy
+ W2 = 110111xxxxxxxxxx
+*/
+
+// convert UCS4 to UTF-16
+// - returns 0 for UNICODE range UCS4 and first word of UTF-16 for non UNICODE
+uInt16 UCS4toUTF16(uInt32 aUCS4, uInt16 &aUTF16)
+{
+ if (aUCS4<0x10000) {
+ // in unicode range: single UNICODE char
+ aUTF16=aUCS4;
+ return 0; // no second char
+ }
+ else {
+ // out of UNICODE range
+ aUCS4-=0x10000;
+ if (aUCS4>0xFFFF) {
+ // inconvertible
+ aUTF16=INCONVERTIBLE_PLACEHOLDER;
+ return 0;
+ }
+ else {
+ // convert to two-word UNICODE / UCS-2
+ aUTF16=0xD800+(aUCS4>>10);
+ return 0xDC00+(aUCS4 & 0x03FF);
+ }
+ }
+} // UCS4toUTF16
+
+
+
+/* Decoding UTF-16
+
+ Decoding of a single character from UTF-16 to an ISO 10646 character
+ value proceeds as follows. Let W1 be the next 16-bit integer in the
+ sequence of integers representing the text. Let W2 be the (eventual)
+ next integer following W1.
+
+ 1) If W1 < 0xD800 or W1 > 0xDFFF, the character value U is the value
+ of W1. Terminate.
+
+ 2) Determine if W1 is between 0xD800 and 0xDBFF. If not, the sequence
+ is in error and no valid character can be obtained using W1.
+ Terminate.
+
+ 3) If there is no W2 (that is, the sequence ends with W1), or if W2
+ is not between 0xDC00 and 0xDFFF, the sequence is in error.
+ Terminate.
+
+ 4) Construct a 20-bit unsigned integer U', taking the 10 low-order
+ bits of W1 as its 10 high-order bits and the 10 low-order bits of
+ W2 as its 10 low-order bits.
+
+ 5) Add 0x10000 to U' to obtain the character value U. Terminate.
+
+ Note that steps 2 and 3 indicate errors. Error recovery is not
+ specified by this document. When terminating with an error in steps 2
+ and 3, it may be wise to set U to the value of W1 to help the caller
+ diagnose the error and not lose information. Also note that a string
+ decoding algorithm, as opposed to the single-character decoding
+ described above, need not terminate upon detection of an error, if
+ proper error reporting and/or recovery is provided.
+
+*/
+
+// convert UTF-16 to UCS4
+// - returns pointer to next char
+// - returns UCS4=0 on error (no char, bad sequence, sequence not complete)
+const uInt16 *UTF16toUCS4(const uInt16 *aUTF16P, uInt32 &aUCS4)
+{
+ uInt16 utf16=*aUTF16P++;
+
+ if (utf16<0xD800 || utf16>0xDFFF) {
+ // single char unicode
+ aUCS4=utf16;
+ }
+ else {
+ // could be two-char
+ if (utf16<=0xDBFF) {
+ // valid first char: check second char
+ uInt16 utf16_2 = *aUTF16P; // next
+ if (utf16_2 && utf16_2>=0xDC00 && utf16_2<=0xDFFF) {
+ // second char exists and is valid
+ aUTF16P++; // advance now
+ aUCS4 =
+ ((utf16 & 0x3FF) << 10) +
+ (utf16_2 & 0x3FF);
+ }
+ else
+ aUCS4=0; // no char
+ }
+ else {
+ aUCS4=0; // no char
+ }
+ }
+ // return advanced pointer
+ return aUTF16P;
+} // UCS4toUTF16
+
+
+
+
+
+
+// add UTF8 string as UTF-16 byte stream to 8-bit string
+// - if aLEM is not lem_none, occurrence of any type of Linefeeds
+// (LF,CR,CRLF and even CRCRLF) in input string will be
+// replaced by the specified line end type
+// - output is clipped after ByteString reaches aMaxBytes size (if not 0), = approx half as many Unicode chars
+// - returns true if all input could be converted, false if output is clipped
+bool appendUTF8ToUTF16ByteString(
+ cAppCharP aUTF8,
+ string &aUTF16ByteString,
+ bool aBigEndian,
+ TLineEndModes aLEM,
+ uInt32 aMaxBytes
+)
+{
+ uInt32 ucs4;
+ uInt16 utf16=0,utf16_1;
+ cAppCharP p;
+
+ while (aUTF8 && *aUTF8) {
+ // convert next UTF8 char to UCS4
+ p=UTF8toUCS4(aUTF8, ucs4);
+ if (ucs4==0) break; // error in UTF8 encoding, exit
+ // convert line ends
+ if (ucs4 == '\n' && aLEM!=lem_none && aLEM!=lem_cstr) {
+ // produce specified line end
+ utf16_1=0;
+ switch (aLEM) {
+ case lem_mac : utf16=0x0D; break;
+ case lem_filemaker : utf16=0x0B; break;
+ case lem_unix : utf16=0x0A; break;
+ case lem_dos :
+ utf16_1=0x0D; // CR..
+ utf16=0x0A; // ..then LF
+ break;
+ default: break;
+ }
+ }
+ else {
+ // ordinary char, use UTF16 encoding
+ utf16_1 = UCS4toUTF16(ucs4,utf16);
+ }
+ // check if appending UTF16 would exceed max size specified
+ if (aMaxBytes!=0 && aUTF16ByteString.size() + (utf16_1 ? 4 : 2) > aMaxBytes)
+ break;
+ // we can append, advance input pointer
+ aUTF8 = p;
+ // now append
+ if (aBigEndian) {
+ // Big end first, Motorola order
+ if (utf16_1) {
+ aUTF16ByteString += (char)((utf16_1 >> 8) & 0xFF);
+ aUTF16ByteString += (char)(utf16_1 & 0xFF);
+ }
+ aUTF16ByteString += (char)((utf16 >> 8) & 0xFF);
+ aUTF16ByteString += (char)(utf16 & 0xFF);
+ }
+ else {
+ // Little end first, Intel order
+ if (utf16_1) {
+ aUTF16ByteString += (char)((utf16_1 >> 8) & 0xFF);
+ aUTF16ByteString += (char)(utf16_1 & 0xFF);
+ }
+ aUTF16ByteString += (char)(utf16 & 0xFF);
+ aUTF16ByteString += (char)((utf16 >> 8) & 0xFF);
+ }
+ } // while
+ // true if all input consumed
+ return (aUTF8==NULL) || (*aUTF8==0);
+} // appendUTF8ToUTF16ByteString
+
+
+// add UTF16 byte string as UTF8 to value
+void appendUTF16AsUTF8(
+ const uInt16 *aUTF16,
+ uInt32 aNumUTF16Chars,
+ bool aBigEndian,
+ string &aVal,
+ bool aConvertLineEnds,
+ bool aAllowFilemakerCR
+)
+{
+ uInt32 ucs4;
+ uInt16 utf16pair[2];
+ cAppCharP inP = (cAppCharP)aUTF16;
+ bool lastWasCR=false;
+
+ while (inP && !(*inP==0 && *(inP+1)==0) && aNumUTF16Chars>0) {
+ // get two words (in case of surrogate pair)
+ if (aBigEndian) {
+ // Motorola order
+ utf16pair[0]=((*(inP) & 0xFF)<<8) + (*(inP+1) & 0xFF);
+ if (aNumUTF16Chars>1) utf16pair[1]=((*(inP+2) & 0xFF)<<8) + (*(inP+3) & 0xFF);
+ }
+ else {
+ // Intel order
+ utf16pair[0]=((*(inP+1) & 0xFF)<<8) + (*(inP) & 0xFF);
+ if (aNumUTF16Chars>1) utf16pair[1]=((*(inP+3) & 0xFF)<<8) + (*(inP+2) & 0xFF);
+ }
+ cAppCharP hP = (cAppCharP)UTF16toUCS4(utf16pair, ucs4);
+ /*
+ PDEBUGPRINTFX(DBG_PARSE+DBG_EXOTIC,(
+ "Parsed %ld bytes: *(inP)=0x%02hX, *(inP+1)=0x%02hX, *(inP+2)=0x%02hX, *(inP+3)=0x%02hX, utf16pair[0]=0x%04hX, utf16pair[1]=0x%04hX, ucs4=0x%04lX",
+ (uInt32)(hP-(cAppCharP)utf16pair),
+ (uInt16)*(inP), (uInt16)*(inP+1), (uInt16)*(inP+2), (uInt16)*(inP+3),
+ (uInt16)utf16pair[0], (uInt16)utf16pair[1],
+ (uInt32)ucs4
+ ));
+ */
+ uInt32 bytes=hP-(cAppCharP)utf16pair;
+ inP+=bytes; // next UTF16 to check
+ aNumUTF16Chars-=bytes/2; // count down UTF16 chars
+ // convert line ends if selected
+ if (aConvertLineEnds) {
+ if (ucs4 == 0x0D) {
+ lastWasCR=true;
+ continue;
+ }
+ else {
+ if (ucs4 == 0x0A || (aAllowFilemakerCR && ucs4 == 0x0B))
+ ucs4 = '\n'; // convert to LineEnd
+ else if (lastWasCR)
+ aVal += '\n'; // insert a LineEnd
+ lastWasCR=false;
+ }
+ }
+ // append to UTF-8 string
+ UCS4toUTF8(ucs4, aVal);
+ }
+ if (lastWasCR)
+ aVal += '\n'; // input string ended on CR, must be shown in output
+} // appendUTF16AsUTF8
+
+
+
+
+
+
+#ifdef BINTREE_GENERATOR
+
+// add a key/value pair to the binary tree
+void addToBinTree(TBinTreeNode *&aBinTree, treeval_t aMinKey, treeval_t aMaxKey, treeval_t aKey, treeval_t aValue)
+{
+ // start at root
+ TBinTreeNode **nextPP = &aBinTree;
+ treeval_t cmpval;
+ do {
+ // create the new decision value from max and min
+ cmpval = aMinKey+((aMaxKey-aMinKey) >> 1);
+ // create the node if not already there
+ if (*nextPP==NULL) {
+ *nextPP = new TBinTreeNode;
+ (*nextPP)->key = cmpval;
+ (*nextPP)->nextHigher=NULL;
+ (*nextPP)->nextLowerOrEqual=NULL;
+ (*nextPP)->value=0;
+ }
+ // check if the node CREATED is a leaf node
+ // this is the case if max==min
+ if (aMaxKey==aMinKey) {
+ // save leaf value (possibly overwriting existing leaf value for same code)
+ (*nextPP)->value=aValue;
+ break;
+ }
+ // decide which way to go
+ if (aKey>cmpval) {
+ // go to the "higher" side
+ nextPP = &((*nextPP)->nextHigher);
+ // determine new minimum
+ aMinKey = cmpval+1; // minimum must be higher than cmpval
+ }
+ else {
+ // go to the "lower or equal" side
+ nextPP = &((*nextPP)->nextLowerOrEqual);
+ // determine new maximum
+ aMaxKey = cmpval; // maximum must be lower or equal than cmpval
+ }
+ } while(true);
+} // addToBinTree
+
+
+// dispose a bintree
+void disposeBinTree(TBinTreeNode *&aBinTree)
+{
+ if (!aBinTree) return;
+ if (aBinTree->nextHigher)
+ disposeBinTree(aBinTree->nextHigher);
+ if (aBinTree->nextLowerOrEqual)
+ disposeBinTree(aBinTree->nextLowerOrEqual);
+ delete aBinTree;
+ aBinTree=NULL;
+} // disposeBinTree
+
+
+// convert key to value using a flat bintree
+treeval_t searchBintree(TBinTreeNode *aBinTree, treeval_t aKey, treeval_t aUndefValue, treeval_t aMinKey, treeval_t aMaxKey)
+{
+ treeval_t cmpval;
+ while(aBinTree) {
+ // create the new decision value from max and min
+ cmpval = aMinKey+((aMaxKey-aMinKey) >> 1);
+ // must match stored cmpval
+ if (cmpval!=aBinTree->key)
+ return aUndefValue;
+ // check if next node must be leaf if the tree contains our key,
+ // this is the case if max==min
+ if (aMaxKey==aMinKey) {
+ if (aBinTree->nextHigher!=NULL || aBinTree->nextLowerOrEqual!=NULL) {
+ // no leaf value here, should not be the case ever (we should have
+ // encountered a node with no left or right link before this!)
+ return aUndefValue;
+ }
+ else {
+ // found a leaf value here
+ return aBinTree->value;
+ }
+ }
+ // decide which way to go
+ if (aKey>cmpval) {
+ // go to the "higher" side = just next element in array, except if we have the special marker here
+ if (aBinTree->nextHigher == NULL)
+ return aUndefValue; // we should go higher-side, but can't -> unknown key
+ aBinTree=aBinTree->nextHigher;
+ // determine new minimum
+ aMinKey = cmpval+1; // minimum must be higher than cmpval
+ }
+ else {
+ // go to the "lower" side = element at index indicated by current element, except if we have the special marker here
+ if (aBinTree->nextLowerOrEqual == NULL)
+ return aUndefValue; // we should go lower-or-equal-side, but can't -> unknown key
+ aBinTree=aBinTree->nextLowerOrEqual;
+ // determine new maximum
+ aMaxKey = cmpval; // maximum must be lower or equal than cmpval
+ }
+ }
+ // if we reach the end of the array, key is not in the tree
+ return aUndefValue;
+} // searchBintree
+
+
+
+
+// make a flat form representation of the bintree in a one-dimensional array
+// - higher-side links are implicit (nodes following each other),
+// lower-or-equal-side links are explicit
+static bool flatBinTreeRecursion(
+ TBinTreeNode *aBinTree, size_t &aIndex, treeval_t *aFlatArray, size_t aArrSize, treeval_t aLinksStart, treeval_t aLinksEnd
+)
+{
+ // check if array is full
+ if (aIndex>=aArrSize)
+ return false;
+ // examine node to flatten
+ if (aBinTree->nextHigher==NULL && aBinTree->nextLowerOrEqual==NULL) {
+ // this is a leaf node, containing only the value
+ if (aBinTree->value>=aLinksStart && aBinTree->value<=aLinksEnd)
+ return false; // link space and value space overlap
+ aFlatArray[aIndex]=aBinTree->value;
+ aIndex++;
+ }
+ else if (aBinTree->nextHigher==NULL) {
+ // lower-side-only node: set special mark to specify that lower-or-equal side
+ // implicitly follows (instead of higher-side)
+ aFlatArray[aIndex]=aLinksStart + 1; // no node points to the immediately following node explicitly, so 1 can be used as special marker
+ aIndex++;
+ // - recurse to generate it
+ if (!flatBinTreeRecursion(aBinTree->nextLowerOrEqual,aIndex,aFlatArray,aArrSize,aLinksStart,aLinksEnd))
+ return false;
+ }
+ else {
+ // this is a branch
+ // - lower-or-equal side is represented as an index in the array
+ aFlatArray[aIndex]=aLinksStart + 0; // default to not-existing (no node points to itself, so 0 can be used as NIL index value)
+ // - higher side branch follows immediately
+ size_t linkindex = aIndex++;
+ // - recurse to generate it
+ if (!flatBinTreeRecursion(aBinTree->nextHigher,aIndex,aFlatArray,aArrSize,aLinksStart,aLinksEnd))
+ return false;
+ // - now we have the index where we must insert the lower-or-equal side
+ if (aBinTree->nextLowerOrEqual!=NULL) {
+ // there is a lower-or-equal side
+ // - place relative link from original node
+ uInt32 rellink=aIndex-linkindex;
+ if ((uInt32)aLinksStart+rellink>(uInt32)aLinksEnd-1L) {
+ // we need a long link
+ // - move generated higher side branch one up
+ for (size_t k=aIndex-1; k>linkindex; k--) aFlatArray[k+1]=aFlatArray[k];
+ aIndex++; // we've eaten up one extra entry now
+ // - now set long link
+ aFlatArray[linkindex]=aLinksEnd-1; // long link marker
+ if (rellink>0xFFFF)
+ return false; // cannot jump more than 64k
+ aFlatArray[linkindex+1]=rellink; // long link
+ }
+ else {
+ // short link is ok
+ aFlatArray[linkindex]=aLinksStart+rellink;
+ }
+ // - now create the lower-or-equal side
+ if (!flatBinTreeRecursion(aBinTree->nextLowerOrEqual,aIndex,aFlatArray,aArrSize,aLinksStart,aLinksEnd))
+ return false;
+ }
+ }
+ return true;
+} // flatBinTreeRecursion
+
+
+// make a flat form representation of the bintree in a one-dimensional array
+// - higher-side links are implicit (nodes following each other),
+// lower-or-equal-side links are explicit
+bool flatBinTree(
+ TBinTreeNode *aBinTree, TConvFlatTree &aFlatTree, size_t aArrSize,
+ treeval_t aMinKey, treeval_t aMaxKey, treeval_t aLinksStart, treeval_t aLinksEnd
+)
+{
+ // save tree params
+ aFlatTree.numelems=0;
+ aFlatTree.minkey=aMinKey;
+ aFlatTree.maxkey=aMaxKey;
+ aFlatTree.linksstart=aLinksStart;
+ aFlatTree.linksend=aLinksEnd;
+ // now create actual tree
+ size_t index=0;
+ if (!flatBinTreeRecursion(aBinTree,index,aFlatTree.elements,aArrSize,aLinksStart,aLinksEnd))
+ return false;
+ aFlatTree.numelems=index; // actual length of array
+ return true;
+} // flatBinTree
+
+
+
+
+#endif
+
+
+// convert key to value using a flat bintree
+treeval_t searchFlatBintree(const TConvFlatTree &aFlatTree, treeval_t aKey, treeval_t aUndefValue)
+{
+ treeval_t cmpval,thisnode;
+ size_t index=0;
+ // get start min and max
+ treeval_t minKey = aFlatTree.minkey;
+ treeval_t maxKey = aFlatTree.maxkey;
+ // reject out-of-bounds keys immediately
+ if (aKey<minKey || aKey>maxKey)
+ return aUndefValue;
+ do {
+ // create the new decision value from max and min
+ cmpval = minKey+((maxKey-minKey) >> 1);
+ thisnode = aFlatTree.elements[index];
+ // check if next node must be leaf if the tree contains our key,
+ // this is the case if max==min
+ if (maxKey==minKey) {
+ #ifdef BINTREE_GENERATOR
+ if (thisnode>=aFlatTree.linksstart && thisnode<=aFlatTree.linksend) {
+ // no leaf value here, should not be the case ever (we should have
+ // encountered a node with no left or right link before this!)
+ return aUndefValue;
+ }
+ else
+ #endif
+ {
+ // found a leaf value here
+ return (treeval_t) thisnode;
+ }
+ }
+ // decide which way to go
+ if (aKey>cmpval) {
+ // go to the "higher" side = just next element in array, except if we have the special marker here
+ if (thisnode == aFlatTree.linksstart+1)
+ return aUndefValue; // we should go higher-side, but can't -> unknown key
+ // next node is next index (or one more in case this is a long link)
+ if (thisnode == aFlatTree.linksend-1)
+ index++;
+ index++;
+ // determine new minimum
+ minKey = cmpval+1; // minimum must be higher than cmpval
+ }
+ else {
+ // go to the "lower" side = element at index indicated by current element, except if we have the special marker here
+ if (thisnode == aFlatTree.linksstart+1)
+ index++; // special case, "lower" side is immediately following because there is no "higher" side
+ else {
+ #ifdef BINTREE_GENERATOR
+ // if node contains a leaf value instead of a link, something is wrong
+ if (thisnode<aFlatTree.linksstart || thisnode>aFlatTree.linksend)
+ return aUndefValue; // no leaf expected here
+ #endif
+ if (thisnode==aFlatTree.linksend-1) {
+ // long link
+ index++; // skip long link marker
+ thisnode = aFlatTree.elements[index]; // get link value
+ index = index+thisnode; // jump by link value
+ }
+ else {
+ // short link
+ index = index+(thisnode-aFlatTree.linksstart); // get index of next node (relative branch)
+ }
+ if (index==0)
+ return aUndefValue; // there is no link
+ }
+ // determine new maximum
+ maxKey = cmpval; // maximum must be lower or equal than cmpval
+ }
+ } while(index<aFlatTree.numelems);
+ // if we reach the end of the array, key is not in the tree
+ return aUndefValue;
+} // searchFlatBintree
+
+
+// allocate memory via SyncML toolkit allocation function, but throw
+// exception if it fails. Used by SML
+void *_smlMalloc(MemSize_t size)
+{
+ void *p;
+
+ p=smlLibMalloc(size);
+ if (!p) SYSYNC_THROW(TMemException("smlLibMalloc() failed"));
+ return p;
+} // _smlMalloc
+
+
+
+// MD5 and B64 given string
+void MD5B64(const char *aString, sInt32 aLen, string &aMD5B64)
+{
+ // determine input length
+ if (aLen<=0) aLen=strlen(aString);
+ // calc MD5
+ md5::SYSYNC_MD5_CTX context;
+ uInt8 digest[16];
+ md5::Init (&context);
+ md5::Update (&context, (const uInt8 *)aString,aLen);
+ md5::Final (digest, &context);
+ // b64 encode the MD5 digest
+ uInt32 b64md5len;
+ char *b64md5=b64::encode(digest,16,&b64md5len);
+ // assign result
+ aMD5B64.assign(b64md5,b64md5len);
+ // done
+ sysync_free(b64md5); // return buffer allocated by b64::encode
+} // MD5B64
+
+
+// format as Timestamp for use in debug logs
+void StringObjTimestamp(string &aStringObj, lineartime_t aTimer)
+{
+ // format the time
+ sInt16 y,mo,d,h,mi,s,ms;
+ lineartime2date(aTimer,&y,&mo,&d);
+ lineartime2time(aTimer,&h,&mi,&s,&ms);
+ StringObjPrintf(
+ aStringObj,
+ "%04d-%02d-%02d %02d:%02d:%02d.%03d",
+ y,mo,d,h,mi,s,ms
+ );
+} // StringObjTimestamp
+
+
+// format as hex string
+void StringObjHexString(string &aStringObj, const uInt8 *aBinary, uInt32 aBinSz)
+{
+ aStringObj.erase();
+ if (!aBinary) return;
+ while (aBinSz>0) {
+ AppendHexByte(aStringObj,*aBinary++);
+ aBinSz--;
+ }
+} // StringObjHexString
+
+
+// add CGI to existing URL string
+bool addCGItoString(string &aStringObj, cAppCharP aCGI, bool noduplicate)
+{
+ if (!noduplicate || aStringObj.find(aCGI)==string::npos) {
+ // - Add CGI separator if and only if none exists already
+ if (aStringObj.find("?")==string::npos)
+ aStringObj += '?';
+ aStringObj += aCGI;
+ return true; // added
+ }
+ return false; // nothing added
+}
+
+
+// Count bits
+int countbits(uInt32 aMask)
+{
+ int bits=0;
+ uInt32 mask=0x0000001;
+ while (mask) {
+ if (aMask & mask) bits++;
+ mask=mask << 1;
+ }
+ return bits;
+} // countbits
+
+
+// make uppercase
+void StringUpper(string &aString)
+{
+ for(uInt32 k=0; k<aString.size(); k++) aString[k]=toupper(aString[k]);
+} // StringUpper
+
+
+// make lowercase
+void StringLower(string &aString)
+{
+ for(uInt32 k=0; k<aString.size(); k++) aString[k]=tolower(aString[k]);
+} // StringLower
+
+
+
+// Substitute occurences of pattern with replacement in string
+void StringSubst(
+ string &aString, const char *aPattern, const string &aReplacement,
+ sInt32 aPatternLen,
+ TCharSets aCharSet, TLineEndModes aLEM,
+ TQuotingModes aQuotingMode
+)
+{
+ StringSubst(
+ aString, aPattern,
+ aReplacement.c_str(),
+ aPatternLen,
+ aReplacement.size(),
+ aCharSet, aLEM, aQuotingMode
+ );
+} // StringSubst
+
+
+// Substitute occurences of pattern with replacement in string
+void StringSubst(
+ string &aString, const char *aPattern, const char *aReplacement,
+ sInt32 aPatternLen, sInt32 aReplacementLen,
+ TCharSets aCharSet, TLineEndModes aLEM,
+ TQuotingModes aQuotingMode
+)
+{
+ string::size_type i;
+ string s;
+ i=0;
+ if (aPatternLen<0) aPatternLen=strlen(aPattern);
+ // convert if needed
+ if (!aReplacement) {
+ aReplacement=""; // empty string if not specified
+ aReplacementLen=0;
+ }
+ if (aCharSet!=chs_unknown) {
+ appendUTF8ToString(aReplacement,s,aCharSet,aLEM,aQuotingMode);
+ aReplacement=s.c_str();
+ aReplacementLen=s.size();
+ }
+ else {
+ if (aReplacementLen<0) aReplacementLen=strlen(aReplacement);
+ }
+ // now replace
+ while((i=aString.find(aPattern,i))!=string::npos) {
+ aString.replace(i,aPatternLen,aReplacement);
+ i+=aReplacementLen;
+ }
+} // StringSubst
+
+
+// Substitute occurences of pattern with replacement in string
+void StringSubst(string &aString, const char *aPattern, const string &aReplacement, sInt32 aPatternLen)
+{
+ StringSubst(aString,aPattern,aReplacement.c_str(),aPatternLen,aReplacement.size());
+} // StringSubst
+
+
+// Substitute occurences of pattern with integer number in string
+void StringSubst(string &aString, const char *aPattern, sInt32 aNumber, sInt32 aPatternLen)
+{
+ string s;
+ StringObjPrintf(s,"%ld",(long)aNumber);
+ StringSubst(aString,aPattern,s,aPatternLen);
+} // StringSubst
+
+
+
+// copy PCdata contents into std::string object
+void smlPCDataToStringObj(const SmlPcdataPtr_t aPcdataP, string &aStringObj)
+{
+ if (!aPcdataP || !aPcdataP->content) {
+ // no content at all
+ aStringObj.erase();
+ }
+ else if (
+ // NOTE: Opaque works only with modified syncML toolkit which
+ // makes sure opaque content is ALSO TERMINATED LIKE A C-STRING
+ aPcdataP->contentType == SML_PCDATA_STRING ||
+ aPcdataP->contentType == SML_PCDATA_OPAQUE
+ ) {
+ // string or opaque type
+ aStringObj.assign((char *)aPcdataP->content, aPcdataP->length);
+ }
+ else if (aPcdataP->contentType == SML_PCDATA_EXTENSION) {
+ // extension type
+ StringObjPrintf(aStringObj,"[PCDATA_EXTENSION Type=%hd]",(sInt16)aPcdataP->extension);
+ }
+ else {
+ // other type
+ StringObjPrintf(aStringObj,"[PCDATA Type=%hd]",(sInt16)aPcdataP->contentType);
+ }
+} // smlPCDataToStringObj
+
+
+// returns pointer to PCdata contents or null string. If aSizeP!=NULL, length will be stored in *aSize
+const char *smlPCDataToCharP(const SmlPcdataPtr_t aPcdataP, stringSize *aSizeP)
+{
+ const char *str = smlPCDataOptToCharP(aPcdataP, aSizeP);
+ if (str) return str;
+ return "";
+} // smlPCDataToCharP
+
+
+// returns pointer to PCdata contents if existing, NULL otherwise.
+// If aSizeP!=NULL, length will be stored in *aSize
+const char *smlPCDataOptToCharP(const SmlPcdataPtr_t aPcdataP, stringSize *aSizeP)
+{
+ if (!aPcdataP || !aPcdataP->content) {
+ return NULL; // we have no value, it could be empty howevert
+ if (aSizeP) *aSizeP=0;
+ }
+ if (aPcdataP->length==0) {
+ // empty content
+ if (aSizeP) *aSizeP=0;
+ return ""; // return empty string
+ }
+ else if (
+ // NOTE: Opaque works only with modified syncML toolkit which
+ // makes sure opaque content is ALSO TERMINATED LIKE A C-STRING
+ aPcdataP->contentType == SML_PCDATA_STRING ||
+ aPcdataP->contentType == SML_PCDATA_CDATA || // XML only
+ aPcdataP->contentType == SML_PCDATA_OPAQUE // WBXML only
+ ) {
+ // return pointer to content
+ if (aSizeP) *aSizeP=aPcdataP->length;
+ return (char *) aPcdataP->content;
+ }
+ else {
+ // no string
+ if (aSizeP) *aSizeP=11;
+ return "[no string]";
+ }
+} // smlPCDataOptToCharP
+
+
+// returns item string or empty string (NEVER NULL)
+const char *smlItemDataToCharP(const SmlItemPtr_t aItemP)
+{
+ if (!aItemP) return "";
+ return smlPCDataToCharP(aItemP->data);
+} // smlItemDataToCharP
+
+
+// returns first item string or empty string (NEVER NULL)
+const char *smlFirstItemDataToCharP(const SmlItemListPtr_t aItemListP)
+{
+ if (!aItemListP) return "";
+ return smlItemDataToCharP(aItemListP->item);
+} // smlFirstItemDataToCharP
+
+
+
+// returns pointer to source or target LocURI
+const char *smlSrcTargLocURIToCharP(const SmlTargetPtr_t aSrcTargP)
+{
+ if (!aSrcTargP || !aSrcTargP->locURI) {
+ return ""; // empty string
+ }
+ else {
+ // return PCdata string contents
+ return smlPCDataToCharP(aSrcTargP->locURI);
+ }
+} // smlSrcTargLocURIToCharP
+
+
+// returns pointer to source or target LocName
+const char *smlSrcTargLocNameToCharP(const SmlTargetPtr_t aSrcTargP)
+{
+ if (!aSrcTargP || !aSrcTargP->locName) {
+ return ""; // empty string
+ }
+ else {
+ // return PCdata string contents
+ return smlPCDataToCharP(aSrcTargP->locName);
+ }
+} // smlSrcTargLocNameToCharP
+
+
+// returns error code made ready for SyncML sending (that is, remove offset
+// of 10000 if present, and make generic error 500 for non-SyncML errors,
+// and return LOCERR_OK as 200)
+localstatus syncmlError(localstatus aErr)
+{
+ if (aErr==LOCERR_OK) return 200; // SyncML ok code
+ if (aErr<999) return aErr; // return as is
+ if (aErr>=LOCAL_STATUS_CODE+100 && aErr<=999)
+ return aErr-LOCAL_STATUS_CODE; // return with offset removed
+ // no suitable conversion
+ return 500; // return generic "bad"
+} // localError
+
+
+// returns error code made local (that is, offset by 10000 in case aErr is a
+// SyncML status code <10000, and convert 200 into LOCERR_OK)
+localstatus localError(localstatus aErr)
+{
+ if (aErr==200) return LOCERR_OK;
+ if (aErr<LOCAL_STATUS_CODE) return aErr+LOCAL_STATUS_CODE;
+ return aErr;
+} // localError
+
+
+// returns pure relative URI, if specified relative or absolute to
+// given server URI
+const char *relativeURI(const char *aURI,const char *aServerURI)
+{
+ // check for "./" type relative URI
+ if (strnncmp(aURI,URI_RELPREFIX,2)==0) {
+ // relative URI prefixed with "./", just zap the relative part
+ return aURI+2;
+ }
+ else if (aServerURI) {
+ // test if absolute URI specifying the right server
+ uInt32 n=strlen(aServerURI);
+ if (strnncmp(aURI,aServerURI,n)==0) {
+ // beginning of URI matches server's URI
+ const char *p=aURI+n;
+ // skip delimiter, if any
+ if (*p=='/') p++;
+ // return relative part of URI
+ return p;
+ }
+ }
+ // just return unmodified
+ return aURI;
+} // relativeURI
+
+
+// split Hostname into address and port parts
+void splitHostname(const char *aHost,string *aAddr,string *aPort)
+{
+ const char *p,*q;
+ p=aHost;
+ q=strchr(p,':');
+ if (q) {
+ // port spec found
+ if (aAddr) aAddr->assign(p,q-p);
+ if (aPort) aPort->assign(q+1);
+ }
+ else {
+ // no prot spec
+ if (aAddr) aAddr->assign(p);
+ if (aPort) aPort->erase();
+ }
+} // splitHostname
+
+
+// split URL into protocol, hostname, document name and auth-info (user, password)
+void splitURL(const char *aURI,string *aProtocol,string *aHost,string *aDoc,string *aUser, string *aPasswd)
+{
+ const char *p,*q,*r;
+
+ p=aURI;
+ // extract protocol
+ q=strchr(p,':');
+ if (q) {
+ // protocol found
+ if (aProtocol) aProtocol->assign(p,q-p);
+ p=q+1; // past colon
+ while (*p=='/') p++; // past trailing slashes
+ // if protocol specified, check for auth info
+ q=strchr(p,'@');
+ r=strchr(p,':');
+ if (q && r && q>r) {
+ // auth exists
+ if (aUser) aUser->assign(p,r-p);
+ if (aPasswd) aPasswd->assign(r+1,q-r-1);
+ p=q+1; // past "@"
+ }
+ else {
+ // no auth found
+ if (aUser) aUser->erase();
+ if (aPasswd) aPasswd->erase();
+ }
+ }
+ else {
+ // no protocol found
+ if (aProtocol) aProtocol->erase();
+ // no protocol, no auth
+ if (aUser) aUser->erase();
+ if (aPasswd) aPasswd->erase();
+ }
+ // separate hostname and document
+ // - assume path
+ q=strchr(p,'/');
+ // - if no path, check if there is a CGI param directly after the host name
+ if (!q) {
+ q=strchr(p,'?');
+ // in case of no docpath, but CGI, put '?' into docname
+ r=q;
+ }
+ else {
+ // in case of '/', do not put slash into docname
+ // except if docname would be empty otherwise
+ r=q+1; // exclude slash
+ if (*r==0) r=q; // nothing follows, include the slash
+ }
+ if (q) {
+ // document exists
+ if (aDoc) {
+ aDoc->erase();
+ if (*q=='?') (*aDoc)+='/'; // if doc starts with CGI, we are at root
+ aDoc->append(r); // till end of string
+ }
+ if (aHost) aHost->assign(p,q-p); // assign host (all up to / or ?)
+ }
+ else {
+ if (aDoc) aDoc->erase(); // empty document name
+ if (aHost) aHost->assign(p); // entire string is host
+ }
+} // splitURL
+
+
+
+// returns type from meta
+const char *smlMetaTypeToCharP(SmlMetInfMetInfPtr_t aMetaP)
+{
+ if (!aMetaP) return NULL; // no meta at all
+ return smlPCDataToCharP(aMetaP->type);
+} // smlMetaTypeToCharP
+
+
+
+// returns Next Anchor from meta
+const char *smlMetaNextAnchorToCharP(SmlMetInfMetInfPtr_t aMetaP)
+{
+ if (!aMetaP) return NULL; // no meta at all
+ if (!aMetaP->anchor) return NULL; // no anchor at all
+ return smlPCDataToCharP(aMetaP->anchor->next);
+} // smlMetaAnchorToCharP
+
+
+// returns Last Anchor from meta
+const char *smlMetaLastAnchorToCharP(SmlMetInfMetInfPtr_t aMetaP)
+{
+ if (!aMetaP) return NULL; // no meta at all
+ if (!aMetaP->anchor) return NULL; // no anchor at all
+ return smlPCDataToCharP(aMetaP->anchor->last);
+} // smlMetaLastAnchorToCharP
+
+
+// returns DevInf pointer if any in specified PCData, NULL otherwise
+SmlDevInfDevInfPtr_t smlPCDataToDevInfP(const SmlPcdataPtr_t aPCDataP)
+{
+ if (!aPCDataP) return NULL;
+ if (aPCDataP->contentType!=SML_PCDATA_EXTENSION) return NULL;
+ if (aPCDataP->extension!=SML_EXT_DEVINF) return NULL;
+ return (SmlDevInfDevInfPtr_t)(aPCDataP->content);
+} // smlPCDataToDevInfP
+
+
+// returns MetInf pointer if any in specified PCData, NULL otherwise
+SmlMetInfMetInfPtr_t smlPCDataToMetInfP(const SmlPcdataPtr_t aPCDataP)
+{
+ if (!aPCDataP) return NULL;
+ if (aPCDataP->contentType!=SML_PCDATA_EXTENSION) return NULL;
+ if (aPCDataP->extension!=SML_EXT_METINF) return NULL;
+ return (SmlMetInfMetInfPtr_t)(aPCDataP->content);
+} // smlPCDataToMetInfP
+
+
+
+// returns true on successful conversion of PCData string to sInt32
+bool smlPCDataToLong(const SmlPcdataPtr_t aPCDataP, sInt32 &aLong)
+{
+ return StrToLong(smlPCDataToCharP(aPCDataP),aLong);
+} // smlPCDataToLong
+
+
+// returns true on successful conversion of PCData string to sInt32
+bool smlPCDataToULong(const SmlPcdataPtr_t aPCDataP, uInt32 &aLong)
+{
+ return StrToULong(smlPCDataToCharP(aPCDataP),aLong);
+} // smlPCDataToLong
+
+
+// returns true on successful conversion of PCData string to format
+bool smlPCDataToFormat(const SmlPcdataPtr_t aPCDataP, TFmtTypes &aFmt)
+{
+ const char *fmt = smlPCDataToCharP(aPCDataP);
+ sInt16 sh;
+ if (*fmt) {
+ if (!StrToEnum(encodingFmtSyncMLNames,numFmtTypes,sh,fmt))
+ return false; // unknown format
+ aFmt=(TFmtTypes)sh;
+ }
+ else {
+ aFmt=fmt_chr; // no spec = chr
+ }
+ return true;
+} // smlPCDataToFormat
+
+
+// build Meta anchor
+SmlPcdataPtr_t newMetaAnchor(const char *aNextAnchor, const char *aLastAnchor)
+{
+ SmlPcdataPtr_t metaP;
+ SmlMetInfAnchorPtr_t anchorP;
+
+ // - create empty meta
+ metaP=newMeta();
+ // - create new anchor
+ anchorP=SML_NEW(SmlMetInfAnchor_t);
+ // - set anchor contents
+//%%% anchorP->last=newPCDataOptEmptyString(aLastAnchor); // optional, but omitted only if string is NULL (not if only empty)
+ anchorP->last=newPCDataOptString(aLastAnchor); // optional
+ anchorP->next=newPCDataString(aNextAnchor); // mandatory
+ // - set anchor
+ ((SmlMetInfMetInfPtr_t)(metaP->content))->anchor=anchorP;
+ // return
+ return metaP;
+} // newMetaAnchor
+
+
+// build Meta type
+SmlPcdataPtr_t newMetaType(const char *aMetaType)
+{
+ SmlPcdataPtr_t metaP;
+
+ // - if not type, we don't create a meta at all
+ if (aMetaType==NULL || *aMetaType==0) return NULL;
+ // - create empty meta
+ metaP=newMeta();
+ // - set type
+ ((SmlMetInfMetInfPtr_t)(metaP->content))->type=newPCDataString(aMetaType);
+ // return
+ return metaP;
+} // newMetaType
+
+
+// build empty Meta
+SmlPcdataPtr_t newMeta(void)
+{
+ SmlPcdataPtr_t metaP;
+ SmlMetInfMetInfPtr_t metinfP;
+
+ // - create empty PCData
+ metaP = SML_NEW(SmlPcdata_t);
+ metaP->contentType=SML_PCDATA_EXTENSION;
+ metaP->extension=SML_EXT_METINF;
+ // - %%% assume length is not relevant for structured content (looks like in mgrutil.c)
+ metaP->length=0;
+ // - create empty meta
+ metinfP = SML_NEW(SmlMetInfMetInf_t);
+ metaP->content=metinfP; // link to PCdata
+ // - init meta options
+ metinfP->version=NULL;
+ metinfP->format=NULL;
+ metinfP->type=NULL;
+ metinfP->mark=NULL;
+ metinfP->size=NULL;
+ metinfP->nextnonce=NULL;
+ metinfP->maxmsgsize=NULL;
+ metinfP->mem=NULL;
+ metinfP->emi=NULL; // PCData list
+ metinfP->anchor=NULL;
+ // - SyncML 1.1
+ metinfP->maxobjsize=NULL;
+ // - SyncML 1.2
+ metinfP->flags=0;
+ // return
+ return metaP;
+} // newMeta
+
+
+// copy meta from existing meta (for data items only
+// anchor, mem, emi, nonce are not copied!)
+// Note however that we copy maxobjsize, as we (mis-)use it for ZIPPED_BINDATA_SUPPORT
+SmlPcdataPtr_t copyMeta(SmlPcdataPtr_t aOldMetaP)
+{
+ if (!aOldMetaP) return NULL;
+ SmlPcdataPtr_t newmetaP=newMeta();
+ if (!newmetaP) return NULL;
+ SmlMetInfMetInfPtr_t oldmetinfP = smlPCDataToMetInfP(aOldMetaP);
+ if (!oldmetinfP) return NULL;
+ SmlMetInfMetInfPtr_t newmetInfP = smlPCDataToMetInfP(newmetaP);
+ // - copy meta
+ newmetInfP->version = smlPcdataDup(oldmetinfP->version);
+ newmetInfP->format = smlPcdataDup(oldmetinfP->format);
+ newmetInfP->type = smlPcdataDup(oldmetinfP->type);
+ newmetInfP->mark = smlPcdataDup(oldmetinfP->mark);
+ newmetInfP->size = smlPcdataDup(oldmetinfP->size);
+ newmetInfP->maxobjsize = smlPcdataDup(oldmetinfP->maxobjsize);
+ // return
+ return newmetaP;
+} // copyMeta
+
+
+
+
+// add an item to an item list
+SmlItemListPtr_t *addItemToList(
+ SmlItemPtr_t aItemP, // existing item data structure, ownership is passed to list
+ SmlItemListPtr_t *aItemListPP // adress of pointer to existing item list or NULL
+)
+{
+ if (aItemListPP && aItemP) {
+ // find last itemlist pointer
+ while (*aItemListPP) {
+ aItemListPP=&((*aItemListPP)->next);
+ }
+ // aItemListPP now points to a NULL pointer which must be replaced by addr of new ItemList entry
+ *aItemListPP = SML_NEW(SmlItemList_t);
+ (*aItemListPP)->next=NULL;
+ (*aItemListPP)->item=aItemP; // insert new item
+ // return pointer to pointer to next element (which is now NULL).
+ // Can be passed in to addPCDataToList() again to append more elements without searching
+ // for end-of-list
+ return &((*aItemListPP)->next);
+ }
+ // nop, return pointer unmodified
+ return aItemListPP;
+} // addItemToList
+
+
+// add a CTData item to a CTDataList
+SmlDevInfCTDataListPtr_t *addCTDataToList(
+ SmlDevInfCTDataPtr_t aCTDataP, // existing CTData item data structure, ownership is passed to list
+ SmlDevInfCTDataListPtr_t *aCTDataListPP // adress of pointer to existing item list or NULL
+)
+{
+ if (aCTDataListPP && aCTDataP) {
+ // find last itemlist pointer
+ while (*aCTDataListPP) {
+ aCTDataListPP=&((*aCTDataListPP)->next);
+ }
+ // aItemListPP now points to a NULL pointer which must be replaced by addr of new ItemList entry
+ *aCTDataListPP = SML_NEW(SmlDevInfCTDataList_t);
+ (*aCTDataListPP)->next=NULL;
+ (*aCTDataListPP)->data=aCTDataP; // insert new data
+ // return pointer to pointer to next element (which is now NULL).
+ // Can be passed in to addPCDataToList() again to append more elements without searching
+ // for end-of-list
+ return &((*aCTDataListPP)->next);
+ }
+ // nop, return pointer unmodified
+ return aCTDataListPP;
+} // addCTDataToList
+
+
+// add a CTDataProp item to a CTDataPropList
+SmlDevInfCTDataPropListPtr_t *addCTDataPropToList(
+ SmlDevInfCTDataPropPtr_t aCTDataPropP, // existing CTDataProp item data structure, ownership is passed to list
+ SmlDevInfCTDataPropListPtr_t *aCTDataPropListPP // adress of pointer to existing item list or NULL
+)
+{
+ if (aCTDataPropListPP && aCTDataPropP) {
+ // find last itemlist pointer
+ while (*aCTDataPropListPP) {
+ aCTDataPropListPP=&((*aCTDataPropListPP)->next);
+ }
+ // aItemListPP now points to a NULL pointer which must be replaced by addr of new ItemList entry
+ *aCTDataPropListPP = SML_NEW(SmlDevInfCTDataPropList_t);
+ (*aCTDataPropListPP)->next=NULL;
+ (*aCTDataPropListPP)->data=aCTDataPropP; // insert new data
+ // return pointer to pointer to next element (which is now NULL).
+ // Can be passed in to addPCDataToList() again to append more elements without searching
+ // for end-of-list
+ return &((*aCTDataPropListPP)->next);
+ }
+ // nop, return pointer unmodified
+ return aCTDataPropListPP;
+} // addCTDataPropToList
+
+
+// add a CTData describing a property (as returned by newDevInfCTData())
+// as a new property without parameters to a CTDataPropList
+SmlDevInfCTDataPropListPtr_t *addNewPropToList(
+ SmlDevInfCTDataPtr_t aPropCTData, // CTData describing property
+ SmlDevInfCTDataPropListPtr_t *aCTDataPropListPP // adress of pointer to existing item list or NULL
+)
+{
+ SmlDevInfCTDataPropPtr_t propdataP = SML_NEW(SmlDevInfCTDataProp_t);
+ propdataP->param = NULL; // no params
+ propdataP->prop = aPropCTData;
+ return addCTDataPropToList(propdataP, aCTDataPropListPP);
+} // addNewPropToList
+
+
+
+// add PCData element to a PCData list
+SmlPcdataListPtr_t *addPCDataToList(
+ SmlPcdataPtr_t aPCDataP, // Existing PCData element to be added, ownership is passed to list
+ SmlPcdataListPtr_t *aPCDataListPP // adress of pointer to existing PCData list or NULL
+)
+{
+ if (aPCDataListPP) {
+ // find last PCDataList pointer
+ while (*aPCDataListPP) {
+ aPCDataListPP=&((*aPCDataListPP)->next);
+ }
+ // aItemListPP now points to a NULL pointer which must be replaced by addr of new PCDataList entry
+ *aPCDataListPP = SML_NEW(SmlPcdataList_t);
+ (*aPCDataListPP)->next=NULL;
+ (*aPCDataListPP)->data=aPCDataP; // insert new item
+ // return pointer to pointer to next element (which is now NULL).
+ // Can be passed in to addPCDataToList() again to append more elements without searching
+ // for end-of-list
+ return &((*aPCDataListPP)->next);
+ }
+ return NULL;
+} // addPCDataToList
+
+
+// add PCData string to a PCData list
+SmlPcdataListPtr_t *addPCDataStringToList(
+ const char *aString, // String to be added
+ SmlPcdataListPtr_t *aPCDataListPP // adress of pointer to existing PCData list or NULL
+)
+{
+ return addPCDataToList(newPCDataString(aString),aPCDataListPP);
+} // addPCDataStringToList
+
+
+// create new optional location (source or target)
+// Returns NULL if URI specified is NULL or empty
+SmlSourcePtr_t newOptLocation(
+ const char *aLocURI,
+ const char *aLocName
+)
+{
+ if (!aLocURI || *aLocURI==0) return NULL;
+ else return newLocation(aLocURI,aLocName);
+} // newOptLocation
+
+
+// create new location (source or target)
+// always returns location, even if URI and/or name are empty
+// If name is NULL or empty, only URI is generated
+SmlSourcePtr_t newLocation(
+ const char *aLocURI,
+ const char *aLocName
+)
+{
+ SmlSourcePtr_t locP;
+
+ locP = SML_NEW(SmlSource_t);
+ // URI is always present (might be empty, though)
+ locP->locURI=newPCDataString(aLocURI);
+ // name only if not empty
+ if (aLocName && *aLocName!=0)
+ locP->locName=newPCDataString(aLocName);
+ else
+ locP->locName=NULL;
+ // filter defaults to NULL
+ locP->filter=NULL;
+ return locP;
+} // newLocation
+
+
+// create new empty Item
+SmlItemPtr_t newItem(void)
+{
+ SmlItemPtr_t itemP;
+
+ itemP = SML_NEW(SmlItem_t);
+ itemP->target=NULL;
+ itemP->source=NULL;
+ itemP->meta=NULL;
+ itemP->data=NULL;
+ // SyncML 1.1, no MoreData set
+ itemP->flags=0;
+ // SyncML 1.2
+ itemP->targetParent=NULL;
+ itemP->sourceParent=NULL;
+ return itemP;
+} // newItem
+
+
+// create new Item with string-type data
+SmlItemPtr_t newStringDataItem(
+ const char *aString
+)
+{
+ SmlItemPtr_t itemP=newItem();
+ itemP->data=newPCDataString(aString);
+ return itemP;
+} // newStringDataItem
+
+
+// create meta-format PCData
+SmlPcdataPtr_t newPCDataFormat(
+ TFmtTypes aFmtType,
+ bool aShowDefault
+)
+{
+ if (aFmtType==fmt_chr && !aShowDefault)
+ return NULL; // default
+ else
+ return newPCDataString(encodingFmtSyncMLNames[aFmtType]); // show format type
+} // newPCDataFormat
+
+
+// create new string-type PCData, if NULL or empty string is passed for aData,
+// NULL is returned (optional info not there)
+SmlPcdataPtr_t newPCDataFormatted(
+ const uInt8 *aData, // data
+ sInt32 aLength, // length of data, if<=0 then string length is calculated
+ TFmtTypes aFmtType, // encoding Format
+ bool aNeedsOpaque // set opaque needed (string that could confuse XML parsing or even binary)
+)
+{
+ if (!aData) return NULL; // no data
+ if (aLength==0) aLength=strlen((const char *)aData);
+ if (aLength==0) return NULL; // no data
+ // encode input string if needed
+ SmlPcdataPtr_t pcdataP;
+ char *b64data;
+ uInt32 b64len;
+ switch (aFmtType) {
+ case fmt_b64:
+ // convert to b64
+ b64len=0;
+ b64data=b64::encode(aData, aLength, &b64len);
+ pcdataP = newPCDataString(b64data,b64len);
+ sysync_free(b64data);
+ return pcdataP;
+ default:
+ // just copy into string or opaque/C_DATA string
+ return newPCDataStringX(aData, aNeedsOpaque, aLength);
+ }
+} // newPCDataEncoded
+
+
+// create new string-type PCData, if NULL or empty string is passed for aString,
+// NULL is returned (optional info not there)
+SmlPcdataPtr_t newPCDataOptString(
+ const char *aString,
+ sInt32 aLength // length of string, if<0 then length is calculated
+)
+{
+ if (aString && (*aString!=0))
+ return newPCDataString(aString,aLength);
+ else
+ return NULL;
+} // newPCDataOptString
+
+
+// create new string-type PCData, if NULL is passed for aString,
+// NULL is returned (optional info not there)
+// if empty string is passed, PCData with empty contents will be created
+SmlPcdataPtr_t newPCDataOptEmptyString(
+ const char *aString,
+ sInt32 aLength // length of string, if<0 then length is calculated
+)
+{
+ if (aString)
+ return newPCDataString(aString,aLength);
+ else
+ return NULL;
+} // newPCDataOptEmptyString
+
+
+// create new string-type PCData, if NULL is passed for aString,
+// an empty string is created (that is, a PCData with string terminator as
+// content only, length=0)
+SmlPcdataPtr_t newPCDataString(
+ const char *aString,
+ sInt32 aLength // length of string, if<0 then length is calculated
+)
+{
+ return newPCDataStringX((const uInt8 *)aString,false,aLength);
+} // newPCDataString
+
+
+// create new PCData, aOpaque can be used to generate non-string data
+// Note: empty strings are always coded as non-opaque, even if aOpaque is set
+SmlPcdataPtr_t newPCDataStringX(
+ const uInt8 *aString,
+ bool aOpaque, // if set, an opaque method (OPAQUE or CDATA) is used
+ sInt32 aLength // length of string, if<0 then length is calculated
+)
+{
+ SmlPcdataPtr_t pcdataP;
+
+ pcdataP = SML_NEW(SmlPcdata_t);
+
+ // determine length
+ if (aLength>=0 && aString)
+ pcdataP->length = aLength; // as specified, and string argument not NULL
+ else
+ pcdataP->length = aString ? strlen((const char *)aString) : 0; // from argument, if NULL -> length=0
+ // determine type
+ if (aOpaque && aLength!=0) {
+ // Note: due to modification in RTK, this generates
+ // OPAQUE in WBXML and CDATA in XML
+ pcdataP->contentType=SML_PCDATA_OPAQUE;
+ }
+ else {
+ // non-critical string
+ #ifdef SML_STRINGS_AS_OPAQUE
+ pcdataP->contentType=SML_PCDATA_OPAQUE;
+ #else
+ pcdataP->contentType=SML_PCDATA_STRING;
+ #endif
+ }
+ pcdataP->extension=SML_EXT_UNDEFINED;
+ // - allocate data space (ALWAYS with room for a terminator, even if Opaque or empty string)
+ pcdataP->content=smlLibMalloc(pcdataP->length+1); // +1 for terminator, see below
+ // copy data (if any)
+ if (pcdataP->length>0) {
+ // - copy string
+ smlLibMemcpy(pcdataP->content,aString,pcdataP->length);
+ }
+ // set terminator
+ ((char *)(pcdataP->content))[pcdataP->length]=0; // terminate C string
+ // return
+ return pcdataP;
+} // newPCDataStringX
+
+
+// create new string-type PCData from C++ string
+SmlPcdataPtr_t newPCDataString(
+ const string &aString
+)
+{
+ return newPCDataString(aString.c_str(),aString.length());
+} // newPCDataString(string&)
+
+
+// create new decimal string representation of sInt32 as PCData
+SmlPcdataPtr_t newPCDataLong(
+ sInt32 aLong
+)
+{
+ const int ssiz=20;
+ char s[ssiz];
+
+ snprintf(s,ssiz,"%ld",(long)aLong);
+ return newPCDataString(s);
+} // newPCDataLong
+
+
+// Nonce generator allowing last-session nonce to be correctly re-generated in next session
+void generateNonce(string &aNonce, const char *aDevStaticString, sInt32 aSessionStaticID)
+{
+ md5::SYSYNC_MD5_CTX context;
+ uInt8 digest[16];
+ md5::Init (&context);
+ // - add in static device string
+ md5::Update (&context, (const uInt8 *)aDevStaticString, strlen(aDevStaticString));
+ // - add in session static ID in binary format
+ md5::Update (&context, (const uInt8 *)&aSessionStaticID, sizeof(sInt32));
+ // - done
+ md5::Final (digest, &context);
+ // - make string of first 48 bit of MD5: 48 bits, use 6 bits per char = 8 chars
+ uInt64 dig48 = *((uInt32 *)(digest));
+ aNonce.erase();
+ for (sInt16 k=0; k<8; k++) {
+ aNonce+=((dig48 & 0x03F) + 0x21);
+ dig48 = dig48 >> 6;
+ }
+} // generateNonce
+
+
+// create challenge of requested type
+SmlChalPtr_t newChallenge(TAuthTypes aAuthType, const string &aNextNonce, bool aBinaryAllowed)
+{
+ SmlChalPtr_t chalP=NULL;
+ SmlMetInfMetInfPtr_t metaP;
+
+ if (aAuthType!=auth_none) {
+ // new challenge record
+ chalP = SML_NEW(SmlChal_t);
+ // add empty meta
+ chalP->meta=newMeta();
+ metaP=(SmlMetInfMetInfPtr_t)(chalP->meta->content);
+ // add type and format
+ // - type
+ metaP->type=newPCDataString(authTypeSyncMLNames[aAuthType]);
+ // - format
+ const char *fmt = NULL;
+ switch (aAuthType) {
+ case auth_basic:
+ // always request b64
+ fmt=encodingFmtSyncMLNames[fmt_b64];
+ break;
+ case auth_md5:
+ // request b64 only for non-binary capable encoding (that is, XML)
+ /* %%% dont do that, Nokia9210 miserably fails when we do that,
+ * it sends its data B64 encoded, but obviously with bad
+ * data in it. Ericsson T39m seems to do it correctly however.
+ if (!aBinaryAllowed)
+ fmt=encodingFmtSyncMLNames[fmt_b64];
+ */
+ // always request b64 for now, seems to be safer with not fully compatible clients
+ fmt=encodingFmtSyncMLNames[fmt_b64];
+ break;
+ default: break;
+ }
+ metaP->format=newPCDataOptString(fmt); // set format, but not empty
+ // - add nonce if needed
+ if (aAuthType==auth_md5) {
+ // MD5 also might need nonce
+ if (!aNextNonce.empty()) {
+ // add base64 encoded nonce string
+ uInt32 b64len;
+ char *b64=b64::encode((const uInt8 *)aNextNonce.c_str(),aNextNonce.size(),&b64len);
+ metaP->nextnonce=newPCDataString(b64,b64len);
+ sysync_free(b64); // return buffer allocated by b64_encode
+ }
+ }
+ }
+ return chalP;
+} // newChallenge
+
+
+// create new property or param descriptor for CTCap
+SmlDevInfCTDataPtr_t newDevInfCTData(cAppCharP aName,uInt32 aSize, bool aNoTruncate, uInt32 aMaxOccur, cAppCharP aDataType)
+{
+ SmlDevInfCTDataPtr_t result = SML_NEW(SmlDevInfCTData_t);
+ // fill descriptor
+ // - name if property or param
+ result->name=newPCDataString(aName);
+ // - no display name so far
+ result->dname=NULL; // no display name
+ // - datatype (optional)
+ result->datatype=newPCDataOptString(aDataType);
+ // - max size
+ if (aSize==0)
+ result->maxsize=NULL; // no size
+ else
+ result->maxsize=newPCDataLong(aSize); // set size
+ // - no valenum here, will be added later if any
+ result->valenum=NULL; // no valenum
+ // SyncML 1.2
+ if (aMaxOccur==0)
+ result->maxoccur=NULL; // no maxoccur
+ else
+ result->maxoccur=newPCDataLong(aMaxOccur); // set maxoccur
+ result->flags = aNoTruncate ? SmlDevInfNoTruncate_f : 0; // notruncate flag or none
+ return result;
+} // newDevInfCTData
+
+
+// frees prototype element and sets calling pointer to NULL
+void FreeProtoElement(void * &aVoidP)
+{
+ if (aVoidP) smlFreeProtoElement(aVoidP);
+ aVoidP=NULL;
+} // FreeProtoElement
+
+} // namespace sysync
+
+// eof
diff --git a/src/sysync/sysync_utils.h b/src/sysync/sysync_utils.h
new file mode 100755
index 0000000..dd9d37a
--- /dev/null
+++ b/src/sysync/sysync_utils.h
@@ -0,0 +1,618 @@
+/*
+ * File: sysync_utils.cpp
+ *
+ * Author: Lukas Zeller (luz@synthesis.ch)
+ *
+ * Provides some helper functions interfacing between SyncML Toolkit
+ * and C++ plus other utilities
+ *
+ * Copyright (c) 2001-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ * 2001-05-16 : luz : created
+ *
+ */
+
+#ifndef SYSYNC_UTILS_H
+#define SYSYNC_UTILS_H
+
+#include "sysync_globs.h"
+// include external utils in separate files
+#include "sysync_b64.h"
+#include "sysync_md5.h"
+#include "stringutils.h"
+#include "lineartime.h"
+#include "iso8601.h"
+
+#ifndef FULLY_STANDALONE
+#include "sysync.h"
+#endif
+
+#include "sml.h"
+#include "smldevinfdtd.h"
+#include "smlmetinfdtd.h"
+
+namespace sysync {
+
+#ifdef SYSYNC_TOOL
+
+// convert between character sets
+int charConv(int argc, const char *argv[]);
+
+// parse RFC 2822 addr spec
+int parse2822AddrSpec(int argc, const char *argv[]);
+
+#endif
+
+
+// max line size for MIME content (used while encoding and folding)
+#define MIME_MAXLINESIZE 75
+
+
+// supported charsets
+typedef enum {
+ chs_unknown, // invalid
+ chs_ascii, // 7 bit ASCII-only, with nearest char conversion for umlauts etc.
+ chs_ansi,
+ chs_iso_8859_1,
+ chs_utf8,
+ chs_utf16,
+ #ifdef CHINESE_SUPPORT
+ chs_gb2312,
+ chs_cp936,
+ #endif
+ numCharSets
+} TCharSets;
+// Note: Char set names are defined after this enum in other files,
+// such as MimeDirItemType
+
+
+// supported MIME encoding types
+typedef enum {
+ enc_none,
+ enc_7bit,
+ enc_8bit,
+ enc_binary,
+ enc_quoted_printable,
+ enc_base64, // b64 including terminating with CRLF at end
+ enc_b, // b64 without termination (as needed in RFC2047)
+ numMIMEencodings
+} TEncodingTypes;
+
+
+
+// line end modes
+typedef enum {
+ lem_none, // none specified
+ lem_unix, // 0x0A
+ lem_mac, // 0x0D
+ lem_dos, // 0x0D 0x0A
+ lem_cstr, // as in C strings, '\n' which is 0x0A normally (but might be 0x0D on some platforms)
+ lem_filemaker, // 0x0B (filemaker tab-separated text format, CR is shown as 0x0B within fields
+ numLineEndModes
+} TLineEndModes;
+
+extern const char * const lineEndModeNames[numLineEndModes];
+
+
+// literal quoting modes
+typedef enum {
+ qm_none, // none specified
+ qm_duplsingle, // single quote must be duplicated
+ qm_dupldouble, // double quote must be duplicated
+ qm_backslash, // C-string-style escapes of CR,LF,TAB,BS,\," and ' (but no full c-string escape with \xXX etc.)
+ numQuotingModes
+} TQuotingModes;
+
+extern const char * const quotingModeNames[numQuotingModes];
+
+
+
+/*
+The value of this element SHOULD BE one of bin, bool, b64, chr, int,
+node, null or xml. If the element type is missing, the default value is chr. If the value is
+bin, then the format of the content is binary data. If the value is bool, then the format of
+the content is either true or false. If the value is b64, then the format of the content
+information is binary data that has been character encoded using the Base64 transfer
+encoding defined by [RFC2045]. If the value is chr, then the format of the content
+information is clear-text in the character set specified on either the transport protocol, the
+MIME content type header or the XML prolog. If the value is int, then the format of the
+content information is numeric text representing an unsigned integer between zero and
+2**32-1. If the value is node, then the content represents an interior object in the
+management tree. If the value is null, then there is no content information. This value is
+used by some synchronization data models to delete the content, but not the presence of
+the property. If the value is xml, then the format of the content information is XML
+structured mark-up data.
+*/
+
+// format types
+typedef enum {
+ fmt_chr, // default
+ fmt_bin,
+ fmt_b64,
+ numFmtTypes
+} TFmtTypes;
+
+extern const char * const encodingFmtNames[numFmtTypes];
+extern const char * const encodingFmtSyncMLNames[numFmtTypes];
+
+extern const char * const MIMEEncodingNames[numMIMEencodings];
+extern const char * const MIMECharSetNames[numCharSets];
+
+
+// field (property) data types
+typedef enum {
+ proptype_chr, // Character
+ proptype_int, // Integer
+ proptype_bool, // Boolean
+ proptype_bin, // Binary
+ proptype_datetime, // Date and time of day
+ proptype_phonenum, // Phone number
+ proptype_text, // plain text
+ proptype_unknown, // unknown
+ numPropDataTypes
+} TPropDataTypes;
+
+extern const char * const propDataTypeNames[numPropDataTypes];
+
+
+// Authorization types
+typedef enum {
+ auth_none,
+ auth_basic,
+ auth_md5,
+ numAuthTypes
+} TAuthTypes;
+
+extern const char * const authTypeSyncMLNames[numAuthTypes];
+//extern const char * const authFormatNames[numAuthTypes];
+
+
+// char that is used for non-convertible chars
+#define INCONVERTIBLE_PLACEHOLDER '_'
+
+
+// encoding functions
+
+// encode binary stream and append to string
+void appendEncoded(
+ const uInt8 *aBinary,
+ size_t aSize,
+ string &aString,
+ TEncodingTypes aEncoding,
+ sInt16 aMaxLineSize=76,
+ sInt32 aCurrLineSize=0, // how may chars are on the first line
+ bool aSoftBreaksAsCR=false, // if set, soft breaks are not added as CRLF, but only indicated as CR
+ bool aEncodeBinary=false // quoted printable: binary coding: both CR and LF will be
+ // always replaced by "=0D" and "=0A"
+);
+
+// decode encoded data and append to string
+const char *appendDecoded(
+ const char *aText,
+ size_t aSize,
+ string &aBinString,
+ TEncodingTypes aEncoding
+);
+
+
+
+// generate RFC2822-style address specificiation
+// - Common Name will be quoted
+// - recipient will be put in angle brackets
+void makeRFC2822AddrSpec(
+ cAppCharP aCommonName,
+ cAppCharP aRecipient,
+ string &aRFCAddr
+);
+
+
+// Parse RFC2822-style address specificiation
+// - aName will receive name and all (eventual) comments
+// - aRecipient will receive the (first, in case of a group) email address
+cAppCharP parseRFC2822AddrSpec(
+ cAppCharP aText,
+ string &aName,
+ string &aRecipient
+);
+
+
+// RFC2047 encoding
+
+// append internal UTF8 string as RFC2047 style encoding
+const char *appendUTF8AsRFC2047(
+ const char *aText,
+ string &aString
+);
+
+// parse character string from RFC2047 style encoding to UTF8 internal string
+const char *appendRFC2047AsUTF8(
+ const char *aRFC2047,
+ stringSize aSize, // max number of chars to look at
+ string &aString,
+ TLineEndModes aLEM=lem_none
+);
+
+
+
+// charset conversion functions
+
+// generic bintree-based conversion functions
+typedef uInt16 treeval_t;
+
+typedef struct {
+ treeval_t minkey;
+ treeval_t maxkey;
+ treeval_t linksstart;
+ treeval_t linksend;
+ size_t numelems;
+ treeval_t *elements;
+} TConvFlatTree;
+
+
+#ifdef BINTREE_GENERATOR
+
+typedef struct TBinTreeNode {
+ treeval_t key;
+ struct TBinTreeNode *nextHigher;
+ struct TBinTreeNode *nextLowerOrEqual;
+ treeval_t value; // valid only if links are both NULL
+} TBinTreeNode;
+
+
+// add a key/value pair to the binary tree
+void addToBinTree(TBinTreeNode *&aBinTree, treeval_t aMinKey, treeval_t aMaxKey, treeval_t aKey, treeval_t aValue);
+// dispose a bintree
+void disposeBinTree(TBinTreeNode *&aBinTree);
+// search directly in bintree
+treeval_t searchBintree(TBinTreeNode *aBinTree, treeval_t aKey, treeval_t aUndefValue, treeval_t aMinKey, treeval_t aMaxKey);
+
+
+// make a flat form representation of the bintree in a one-dimensional array
+bool flatBinTree(
+ TBinTreeNode *aBinTree, TConvFlatTree &aFlatTree, size_t aArrSize,
+ treeval_t aMinKey, treeval_t aMaxKey, treeval_t aLinksStart, treeval_t aLinksEnd
+);
+#endif
+
+// search flattened bintree for a specific key value
+treeval_t searchFlatBintree(const TConvFlatTree &aFlatTree, treeval_t aKey, treeval_t aUndefValue);
+
+// add byte char as UTF8 to string value and apply charset translation if needed
+//void appendCharAsUTF8(char c, string &aVal, TCharSets aCharSet);
+uInt16 appendCharsAsUTF8(const char *aChars, string &aVal, TCharSets aCharSet, uInt16 aNumChars=1);
+
+// add string as UTF8 to value and apply charset translation if needed
+// - if aLineEndChar is specified, occurrence of this will be replaced
+// by '\n', occurrence of non matching LF/CR will be ignored
+void appendStringAsUTF8(
+ const char *s, string &aVal,
+ TCharSets aCharSet,
+ TLineEndModes aLEM=lem_cstr,
+ bool aAllowFilemakerCR=false // if set, 0x0B is interpreted as line end as well
+);
+// add UTF8 string to value in custom charset
+// - aLEM specifies line ends to be used
+// - aQuotingMode specifies what quoting (for ODBC literals for example) should be used
+// - output is clipped after aMaxBytes bytes (if not 0)
+// - returns true if all input could be converted, false if output is clipped
+bool appendUTF8ToString(
+ cAppCharP aUTF8, string &aVal,
+ TCharSets aCharSet,
+ TLineEndModes aLEM=lem_none,
+ TQuotingModes aQuotingMode=qm_none,
+ size_t aMaxBytes=0
+);
+// same, but output string is cleared first
+bool storeUTF8ToString(
+ cAppCharP aUTF8, string &aVal,
+ TCharSets aCharSet,
+ TLineEndModes aLEM=lem_none,
+ TQuotingModes aQuotingMode=qm_none,
+ size_t aMaxBytes=0
+);
+
+
+// convert UTF8 to UCS4
+// - returns pointer to next char
+// - returns UCS4=0 on error (no char, bad sequence, sequence not complete)
+const char *UTF8toUCS4(const char *aUTF8, uInt32 &aUCS4);
+// convert UCS4 to UTF8 (0 char is not allowed and will be ignored!)
+void UCS4toUTF8(uInt32 aUCS4, string &aUTF8);
+
+
+// convert UTF-16 to UCS4
+// - returns pointer to next char
+// - returns UCS4=0 on error (no char, bad sequence, sequence not complete)
+const uInt16 *UTF16toUCS4(const uInt16 *aUTF16P, uInt32 &aUCS4);
+// convert UCS4 to UTF-16
+// - returns 0 for UNICODE range UCS4 and first word of UTF-16 for non UNICODE
+uInt16 UCS4toUTF16(uInt32 aUCS4, uInt16 &aUTF16);
+
+
+
+// add UTF8 string as UTF-16 byte stream to 8-bit string
+// - if aLEM is not lem_none, occurrence of any type of Linefeeds
+// (LF,CR,CRLF and even CRCRLF) in input string will be
+// replaced by the specified line end type
+// - output is clipped after ByteString reaches aMaxBytes size (if not 0), = approx half as many Unicode chars
+// - returns true if all input could be converted, false if output is clipped
+bool appendUTF8ToUTF16ByteString(
+ cAppCharP aUTF8,
+ string &aUTF16ByteString,
+ bool aBigEndian,
+ TLineEndModes aLEM=lem_none,
+ uInt32 aMaxBytes=0
+);
+
+// add UTF16 byte string as UTF8 to value
+void appendUTF16AsUTF8(
+ const uInt16 *aUTF16,
+ uInt32 aNumUTF16Chars,
+ bool aBigEndian,
+ string &aVal,
+ bool aConvertLineEnds=false,
+ bool aAllowFilemakerCR=false
+);
+
+
+// MD5 and B64 given string
+void MD5B64(const char *aString, sInt32 aLen, string &aMD5B64);
+
+// format as Timestamp text, usually for logfiles
+void StringObjTimestamp(string &aStringObj, lineartime_t aTimer);
+// format as hex byte string
+void StringObjHexString(string &aStringObj, const uInt8 *aBinary, uInt32 aBinSz);
+
+// add CGI to existing URL string
+bool addCGItoString(string &aStringObj, cAppCharP aCGI, bool noduplicate=true);
+
+// Count bits
+int countbits(uInt32 aMask);
+
+// make uppercase
+void StringUpper(string &aString);
+// make lowercase
+void StringLower(string &aString);
+
+// Substitute occurences of pattern with replacement in string
+void StringSubst(
+ string &aString, const char *aPattern, const string &aReplacement,
+ sInt32 aPatternLen,
+ TCharSets aCharSet, TLineEndModes aLEM,
+ TQuotingModes aQuotingMode
+);
+void StringSubst(
+ string &aString, const char *aPattern, const char *aReplacement,
+ sInt32 aPatternLen, sInt32 aReplacementLen,
+ TCharSets aCharSet=chs_unknown, TLineEndModes aLEM=lem_none,
+ TQuotingModes aQuotingMode=qm_none
+);
+void StringSubst(string &aString, const char *aPattern, const string &aReplacement, sInt32 aPatternLen=-1);
+void StringSubst(string &aString, const char *aPattern, sInt32 aNumber, sInt32 aPatternLen=-1);
+/* subst regexp
+i\=0\; *while\(\(i\=([^.]+)\.find\(\"([^"]+)\",i\)\)\!\=string::npos\) *\{ *[^.]+\.replace\(i,([0-9]+),(.+)\)\; i\+\=.*$
+StringSubst(\1,"\2",\4,\3);
+*/
+
+
+// helper macro for allocation of SyncML Toolkit structures from C++ code
+#define SML_NEW(ty) ((ty*) _smlMalloc(sizeof(ty)))
+#define SML_FREE(m) smlLibFree(m)
+
+// allocate memory via SyncML toolkit allocation function, but throw
+// exception if it fails. Used by SML
+void *_smlMalloc(MemSize_t size);
+
+// copy PCdata contents into std::string object
+void smlPCDataToStringObj(const SmlPcdataPtr_t aPcdataP, string &aStringObj);
+
+// returns pointer to PCdata contents or null string. If aSizeP!=NULL, length will be stored in *aSize
+const char *smlPCDataToCharP(const SmlPcdataPtr_t aPcdata, stringSize *aSizeP=NULL);
+
+// returns pointer to PCdata contents if existing, NULL otherwise.
+// If aSizeP!=NULL, length will be stored in *aSize
+const char *smlPCDataOptToCharP(const SmlPcdataPtr_t aPcdataP, stringSize *aSizeP=NULL);
+
+// returns item string or empty string (NEVER NULL)
+const char *smlItemDataToCharP(const SmlItemPtr_t aItemP);
+
+// returns first item string or empty string (NEVER NULL)
+const char *smlFirstItemDataToCharP(const SmlItemListPtr_t aItemListP);
+
+// split Hostname into address and port parts
+void splitHostname(const char *aHost,string *aAddr,string *aPort);
+
+// split URL into protocol, hostname, document name and auth-info (user, password)
+void splitURL(const char *aURI,string *aProtocol,string *aHost,string *aDoc,string *aUser, string *aPasswd);
+
+// returns error code made ready for SyncML sending (that is, remove offset
+// of 10000 if present, and make generic error 500 for non-SyncML errors,
+// and return LOCERR_OK as 200)
+localstatus syncmlError(localstatus aErr);
+
+// returns error code made local (that is, offset by 10000 in case aErr is a
+// SyncML status code <10000, and convert 200 into LOCERR_OK)
+localstatus localError(localstatus aErr);
+
+// returns pure relative URI, if specified relative or absolute to
+// optionally given server URI
+const char *relativeURI(const char *aURI,const char *aServerURI=NULL);
+
+// returns pointer to source or target LocURI
+const char *smlSrcTargLocURIToCharP(const SmlTargetPtr_t aSrcTargP);
+
+// returns pointer to source or target LocName
+const char *smlSrcTargLocNameToCharP(const SmlTargetPtr_t aSrcTargP);
+
+// returns DevInf pointer if any in specified PCData, NULL otherwise
+SmlDevInfDevInfPtr_t smlPCDataToDevInfP(const SmlPcdataPtr_t aPCDataP);
+
+// returns MetInf pointer if any in specified PCData, NULL otherwise
+SmlMetInfMetInfPtr_t smlPCDataToMetInfP(const SmlPcdataPtr_t aPCDataP);
+
+// returns true on successful conversion of PCData string to format
+bool smlPCDataToFormat(const SmlPcdataPtr_t aPCDataP, TFmtTypes &aFmt);
+
+
+// returns type from meta
+const char *smlMetaTypeToCharP(SmlMetInfMetInfPtr_t aMetaP);
+
+// returns Next Anchor from meta
+const char *smlMetaNextAnchorToCharP(SmlMetInfMetInfPtr_t aMetaP);
+
+// returns Last Anchor from meta
+const char *smlMetaLastAnchorToCharP(SmlMetInfMetInfPtr_t aMetaP);
+
+
+// build Meta anchor
+SmlPcdataPtr_t newMetaAnchor(const char *aNextAnchor, const char *aLastAnchor=NULL);
+
+// build Meta type
+SmlPcdataPtr_t newMetaType(const char *aMetaType);
+
+// build empty Meta
+SmlPcdataPtr_t newMeta(void);
+
+// copy meta from existing meta (for data items only
+// anchor, mem, emi, maxobjsize, nonce are not copied!)
+SmlPcdataPtr_t copyMeta(SmlPcdataPtr_t aOldMetaP);
+
+
+// add an item to an item list
+SmlItemListPtr_t *addItemToList(
+ SmlItemPtr_t aItemP, // existing item data structure, ownership is passed to list
+ SmlItemListPtr_t *aItemListPP // adress of pointer to existing item list or NULL
+);
+
+// add a CTData item to a CTDataList
+SmlDevInfCTDataListPtr_t *addCTDataToList(
+ SmlDevInfCTDataPtr_t aCTDataP, // existing CTData item data structure, ownership is passed to list
+ SmlDevInfCTDataListPtr_t *aCTDataListPP // adress of pointer to existing item list or NULL
+);
+
+// add a CTDataProp item to a CTDataPropList
+SmlDevInfCTDataPropListPtr_t *addCTDataPropToList(
+ SmlDevInfCTDataPropPtr_t aCTDataPropP, // existing CTDataProp item data structure, ownership is passed to list
+ SmlDevInfCTDataPropListPtr_t *aCTDataPropListPP // adress of pointer to existing item list or NULL
+);
+
+// add a CTData describing a property (as returned by newDevInfCTData())
+// as a new property without parameters to a CTDataPropList
+SmlDevInfCTDataPropListPtr_t *addNewPropToList(
+ SmlDevInfCTDataPtr_t aPropCTData, // CTData describing property
+ SmlDevInfCTDataPropListPtr_t *aCTDataPropListPP // adress of pointer to existing item list or NULL
+);
+
+// add PCData element to a PCData list
+SmlPcdataListPtr_t *addPCDataToList(
+ SmlPcdataPtr_t aPCDataP, // Existing PCData element to be added, ownership is passed to list
+ SmlPcdataListPtr_t *aPCDataListPP // adress of pointer to existing PCData list or NULL
+);
+
+// add PCData string to a PCData list
+SmlPcdataListPtr_t *addPCDataStringToList(
+ const char *aString, // String to be added
+ SmlPcdataListPtr_t *aPCDataListPP // adress of pointer to existing PCData list or NULL
+);
+
+// create new optional location (source or target)
+SmlSourcePtr_t newOptLocation(
+ const char *aLocURI,
+ const char *aLocName=NULL
+);
+
+// create new location (source or target)
+SmlSourcePtr_t newLocation(
+ const char *aLocURI,
+ const char *aLocName=NULL
+);
+
+// create new empty Item
+SmlItemPtr_t newItem(void);
+
+// create new Item with string-type data
+SmlItemPtr_t newStringDataItem(
+ const char *aString
+);
+
+
+// create format PCData
+SmlPcdataPtr_t newPCDataFormat(
+ TFmtTypes aFmtType,
+ bool aShowDefault
+);
+
+// create new string-type PCData, if NULL or empty string is passed for aData,
+// NULL is returned (optional info not there)
+SmlPcdataPtr_t newPCDataFormatted(
+ const uInt8 *aData, // data
+ sInt32 aLength, // length of data, if<0 then string length is calculated
+ TFmtTypes aEncType, // encoding
+ bool aNeedsOpaque // set opaque needed (string that could confuse XML parsing or even binary)
+);
+
+// create new string-type PCData, if NULL is passed for aString, NULL is returned (optional info not there)
+SmlPcdataPtr_t newPCDataOptString(
+ const char *aString,
+ sInt32 aLength=-1 // length of string, if<0 then length is calculated
+);
+
+// create new string-type PCData, if NULL is passed for aString,
+// NULL is returned (optional info not there)
+// if empty string is passed, PCData with empty contents will be created
+SmlPcdataPtr_t newPCDataOptEmptyString(
+ const char *aString,
+ sInt32 aLength=-1 // length of string, if<0 then length is calculated
+);
+
+// create new string-type PCData, if NULL is passed for aString, an empty string is created
+SmlPcdataPtr_t newPCDataString(
+ const char *aString,
+ sInt32 aLength=-1 // length of string, if<0 then length is calculate
+);
+
+// create new PCData, aOpaque can be used to generate non-string data
+SmlPcdataPtr_t newPCDataStringX(
+ const uInt8 *aString,
+ bool aOpaque=false, // if set, an opaque method (OPAQUE or CDATA) is used
+ sInt32 aLength=-1 // length of string, if<0 then length is calculate
+);
+
+
+// create new string-type PCData from C++ string
+SmlPcdataPtr_t newPCDataString(const string &aString);
+
+// create new decimal string representation of long as PCData
+SmlPcdataPtr_t newPCDataLong(sInt32 aLong);
+
+// returns true on successful conversion of PCData string to long
+bool smlPCDataToLong(const SmlPcdataPtr_t aPCDataP, sInt32 &aLong);
+bool smlPCDataToULong(const SmlPcdataPtr_t aPCDataP, uInt32 &aLong);
+
+
+// create challenge of requested type
+SmlChalPtr_t newChallenge(TAuthTypes aAuthType, const string &aNextNonce, bool aBinaryAllowed);
+
+// Nonce generator allowing last-session nonce to be correctly re-generated in next session
+void generateNonce(string &aNonce, const char *aDevStaticString, sInt32 aSessionStaticID);
+
+// create new property or param descriptor for CTCap
+SmlDevInfCTDataPtr_t newDevInfCTData(cAppCharP aName,uInt32 aSize=0, bool aNoTruncate=false, uInt32 aMaxOccur=0, cAppCharP aDataType=NULL);
+
+// frees prototype element and sets calling pointer to NULL
+void FreeProtoElement(void * &aVoidP);
+// macro to overcome pointer reference conversion constraints
+#ifdef PREFER_MACROS
+#define FREEPROTOELEMENT(p) FreeProtoElement((void *&)p)
+#else
+template <class T> void FREEPROTOELEMENT(T *&p)
+{
+ smlFreeProtoElement(static_cast<void *>(p));
+ p = NULL;
+}
+#endif
+
+} // namespace sysync
+
+#endif
+// eof
diff --git a/src/sysync/textitemtype.cpp b/src/sysync/textitemtype.cpp
new file mode 100755
index 0000000..32f4912
--- /dev/null
+++ b/src/sysync/textitemtype.cpp
@@ -0,0 +1,325 @@
+/*
+ * File: TextItemType.cpp
+ *
+ * Author: Lukas Zeller (luz@synthesis.ch)
+ *
+ * TTextItemType
+ * base class for plain text based items (notes, emails...)
+ *
+ * Copyright (c) 2002-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ * 2002-05-29 : luz : created from MimeDirItemType
+ *
+ */
+
+// includes
+#include "prefix_file.h"
+
+#include "sysync.h"
+#include "textitemtype.h"
+
+
+using namespace sysync;
+
+
+// Config
+// ======
+
+
+// text-based datatype config
+
+TTextTypeConfig::TTextTypeConfig(const char* aName, TConfigElement *aParentElement) :
+ TMultiFieldTypeConfig(aName,aParentElement)
+{
+ clear();
+} // TTextTypeConfig::TTextTypeConfig
+
+
+TTextTypeConfig::~TTextTypeConfig()
+{
+ // make sure everything is deleted (was missing long time and caused mem leaks!)
+ clear();
+} // TTextTypeConfig::~TTextTypeConfig
+
+
+// init defaults
+void TTextTypeConfig::clear(void)
+{
+ // clear inherited
+ inherited::clear();
+} // TTextTypeConfig::clear
+
+
+// create Sync Item Type of appropriate type from config
+TSyncItemType *TTextTypeConfig::newSyncItemType(TSyncSession *aSessionP, TSyncDataStore *aDatastoreP)
+{
+ if (!fFieldListP)
+ SYSYNC_THROW(TSyncException(DEBUGTEXT("TTextTypeConfig::newSyncItemType: no fFieldListP","txit1")));
+ return
+ new TTextItemType(
+ aSessionP,
+ this,
+ fTypeName.c_str(),
+ fTypeVersion.c_str(),
+ aDatastoreP,
+ fFieldListP
+ );
+} // TTextTypeConfig::newSyncItemType
+
+
+#ifdef CONFIGURABLE_TYPE_SUPPORT
+
+
+// config element parsing
+bool TTextTypeConfig::localStartElement(const char *aElementName, const char **aAttributes, sInt32 aLine)
+{
+ // For >2.1 engines, the text item does not have linemaps etc. in the datatype, but uses
+ // profiles that can be shared among datatypes. To maintain compatibility with old config
+ // files, we implicitly create and link a profile if we see profile definions at this
+ // level
+ // - first, check textItem specifics
+ // %%% none
+ // - then, check ancestor
+ if (TMultiFieldTypeConfig::localStartElement(aElementName,aAttributes,aLine))
+ return true; // ancestor could parse the tag
+ else {
+ // ancestor did not know the tag, could be implicit profile
+ if (!fProfileConfigP) {
+ // there is no profile config yet, create an implicit one
+ // - create an implicit profile with the same name as the datatype itself
+ if (!fFieldListP)
+ return fail("%s must have a 'use' tag before referencing fields");
+ TMultiFieldDatatypesConfig *mfdtcfgP = static_cast<TMultiFieldDatatypesConfig *>(getParentElement());
+ fProfileConfigP=new TTextProfileConfig(getName(),mfdtcfgP);
+ if (fProfileConfigP) {
+ fProfileConfigP->fFieldListP = fFieldListP; // assign field list
+ mfdtcfgP->fProfiles.push_back(fProfileConfigP); // add to profiles
+ }
+ // - warn
+ ReportError(false,
+ "Warning: old-style config - '%s' directly in datatype '%s', should be moved to textprofile",
+ aElementName,
+ getName()
+ );
+ }
+ // now try to parse
+ if (fProfileConfigP) {
+ // let the profile parse as if it was inside a "textprofile"
+ return delegateParsingTo(fProfileConfigP,aElementName,aAttributes,aLine);
+ }
+ else
+ return false; // cannot parse
+ }
+} // TTextTypeConfig::localStartElement
+
+
+// resolve
+void TTextTypeConfig::localResolve(bool aLastPass)
+{
+ // check correct type of profile
+ if (dynamic_cast<TTextProfileConfig *>(fProfileConfigP)==NULL)
+ SYSYNC_THROW(TConfigParseException("missing 'use' of a 'textprofile' in datatype"));
+ // resolve inherited
+ inherited::localResolve(aLastPass);
+} // TTextTypeConfig::localResolve
+
+#endif
+
+
+/*
+ * Implementation of TTextItemType
+ */
+
+
+TTextItemType::TTextItemType(
+ TSyncSession *aSessionP,
+ TDataTypeConfig *aTypeCfgP, // type config
+ const char *aCTType,
+ const char *aVerCT,
+ TSyncDataStore *aRelatedDatastoreP,
+ TFieldListConfig *aFieldDefinitions // field definitions
+) :
+ TMultiFieldItemType(aSessionP,aTypeCfgP,aCTType,aVerCT,aRelatedDatastoreP,aFieldDefinitions),
+ fProfileHandlerP(NULL)
+{
+ // save typed config pointer again
+ fTypeCfgP = static_cast<TTextTypeConfig *>(aTypeCfgP);
+ // create the profile handler
+ fProfileHandlerP = static_cast<TTextTypeConfig *>(aTypeCfgP)->fProfileConfigP->newProfileHandler(this);
+ // set profile mode
+ fProfileHandlerP->setProfileMode(fTypeCfgP->fProfileMode);
+} // TTextItemType::TTextItemType
+
+
+TTextItemType::~TTextItemType()
+{
+ if (fProfileHandlerP)
+ delete fProfileHandlerP;
+} // TTextItemType::~TTextItemType
+
+
+#ifdef OBJECT_FILTERING
+
+// get field index of given filter expression identifier.
+sInt16 TTextItemType::getFilterIdentifierFieldIndex(const char *aIdentifier, uInt16 aIndex)
+{
+ // check if explicit field level identifier
+ if (strucmp(aIdentifier,"F.",2)==0) {
+ // explicit field identifier, skip property lookup
+ aIdentifier+=2;
+ }
+ else if (fProfileHandlerP) {
+ // let profile search for fields by profile-defined alternative names
+ sInt16 fid = fProfileHandlerP->getFilterIdentifierFieldIndex(aIdentifier, aIndex);
+ if (fid!=FID_NOT_SUPPORTED)
+ return fid;
+ }
+ // if no field ID found so far, look up in field list
+ return TMultiFieldItemType::getFilterIdentifierFieldIndex(aIdentifier, aIndex);
+} // TTextItemType::getFilterIdentifierFieldIndex
+
+#endif
+
+
+// helper to create same-typed instance via base class
+TSyncItemType *TTextItemType::newCopyForSameType(
+ TSyncSession *aSessionP, // the session
+ TSyncDataStore *aDatastoreP // the datastore
+)
+{
+ // create new itemtype of appropriate derived class type that can handle
+ // this type
+ MP_RETURN_NEW(TTextItemType,DBG_OBJINST,"TTextItemType",TTextItemType(
+ aSessionP,
+ fTypeConfigP,
+ getTypeName(),
+ getTypeVers(),
+ aDatastoreP,
+ fFieldDefinitionsP
+ ));
+} // TTextItemType::newCopyForSameType
+
+
+// create new sync item of proper type and optimization for specified target
+TSyncItem *TTextItemType::internalNewSyncItem(TSyncItemType *aTargetItemTypeP, TLocalEngineDS *aLocalDatastoreP)
+{
+ // All TextItems are stored in MultiFieldItems
+ if (!aTargetItemTypeP->isBasedOn(ity_multifield))
+ SYSYNC_THROW(TSyncException(DEBUGTEXT("TTextItemType::internalNewSyncItem with bad-typed target","txit3")));
+ TMultiFieldItemType *targetitemtypeP =
+ static_cast<TMultiFieldItemType *> (aTargetItemTypeP);
+ return new TMultiFieldItem(this,targetitemtypeP);
+} // TTextItemType::internalNewSyncItem
+
+
+// fill in SyncML data (but leaves IDs empty)
+bool TTextItemType::internalFillInData(
+ TSyncItem *aSyncItemP, // SyncItem to be filled with data
+ SmlItemPtr_t aItemP, // SyncML toolkit item Data to be converted into SyncItem (may be NULL if no data, in case of Delete or Map)
+ TLocalEngineDS *aLocalDatastoreP, // local datastore
+ TStatusCommand &aStatusCmd // status command that might be modified in case of error
+)
+{
+ // check type
+ if (!aSyncItemP->isBasedOn(ity_multifield) || !fProfileHandlerP)
+ SYSYNC_THROW(TSyncException(DEBUGTEXT("TTextItemType::internalFillInData: incompatible item class","txit4")));
+ TMultiFieldItem *itemP = static_cast<TMultiFieldItem *> (aSyncItemP);
+ // process data if any
+ if (aItemP->data) {
+ // set related datastore so handler can access session specific datastore state
+ fProfileHandlerP->setRelatedDatastore(aLocalDatastoreP);
+ // for text items, the profile handler does all the parsing
+ stringSize sz;
+ cAppCharP t = smlPCDataToCharP(aItemP->data,&sz);
+ if (!fProfileHandlerP->parseText(t,sz,*itemP)) {
+ // format error
+ aStatusCmd.setStatusCode(415); // Unsupported media type or format
+ ADDDEBUGITEM(aStatusCmd,"Error parsing Text content");
+ return false;
+ }
+ }
+ else {
+ // no data
+ aStatusCmd.setStatusCode(412); // incomplete command
+ ADDDEBUGITEM(aStatusCmd,"No data found in item");
+ return false;
+ }
+ // ok, let ancestor process data as well
+ return TMultiFieldItemType::internalFillInData(aSyncItemP,aItemP,aLocalDatastoreP,aStatusCmd);
+} // TTextItemType::internalFillInData
+
+
+// sets data and meta from SyncItem data, but leaves source & target untouched
+bool TTextItemType::internalSetItemData(
+ TSyncItem *aSyncItemP, // the syncitem to be represented as SyncML
+ SmlItemPtr_t aItem, // item with NULL meta and NULL data
+ TLocalEngineDS *aLocalDatastoreP // local datastore
+)
+{
+ // check type
+ if (!aSyncItemP->isBasedOn(ity_multifield) || !fProfileHandlerP)
+ SYSYNC_THROW(TSyncException(DEBUGTEXT("TTextItemType::internalSetItemData: incompatible item class","txit4")));
+ TMultiFieldItem *itemP = static_cast<TMultiFieldItem *> (aSyncItemP);
+ // let ancestor prepare first
+ if (!TMultiFieldItemType::internalSetItemData(aSyncItemP,aItem,aLocalDatastoreP)) return false;
+ // set related datastore so handler can access session specific datastore state
+ fProfileHandlerP->setRelatedDatastore(aLocalDatastoreP);
+ // generate data item
+ string dataitem;
+ fProfileHandlerP->generateText(*itemP,dataitem);
+ // put data item into opaque/cdata PCData (note that dataitem could be BINARY string, so we need to pass size!)
+ aItem->data=newPCDataStringX((const uInt8 *)dataitem.c_str(),true,dataitem.size());
+ // can't go wrong
+ return true;
+} // TTextItemType::internalSetItemData
+
+
+// generates SyncML-Devinf property list for type
+SmlDevInfCTDataPropListPtr_t TTextItemType::newCTDataPropList(TTypeVariantDescriptor aVariantDescriptor)
+{
+ // no properties here
+ return NULL;
+} // TTextItemType::newCTDataPropList
+
+
+// Filtering: add keywords and property names to filterCap
+void TTextItemType::addFilterCapPropsAndKeywords(SmlPcdataListPtr_t &aFilterKeywords, SmlPcdataListPtr_t &aFilterProps, TTypeVariantDescriptor aVariantDescriptor)
+{
+ #ifdef OBJECT_FILTERING
+ // let profile add the keywords
+ if (fProfileHandlerP) {
+ fProfileHandlerP->addFilterCapPropsAndKeywords(aFilterKeywords, aFilterProps, aVariantDescriptor, this);
+ }
+ // let base class add own keywords/props
+ inherited::addFilterCapPropsAndKeywords(aFilterKeywords, aFilterProps, aVariantDescriptor);
+ #endif
+} // TTextItemType::addFilterCapPropsAndKeywords
+
+
+
+// intended for creating SyncItemTypes for remote databases from
+// transmitted DevInf.
+// SyncItemType MUST NOT take ownership of devinf structure passed
+// (because multiple types might be created from a single CTCap entry)
+bool TTextItemType::analyzeCTCap(SmlDevInfCTCapPtr_t aCTCapP)
+{
+ // just let parent handle
+ return inherited::analyzeCTCap(aCTCapP);
+} // TTextItemType::analyzeCTCap
+
+
+/// @brief copy CTCap derived info from another SyncItemType
+/// @return false if item not compatible
+/// @note required to create remote type variants from ruleMatch type alternatives
+bool TTextItemType::copyCTCapInfoFrom(TSyncItemType &aSourceItem)
+{
+ // just let parent handle
+ return inherited::copyCTCapInfoFrom(aSourceItem);
+} // TTextItemType::copyCTCapInfoFrom
+
+
+
+
+/* end of TTextItemType implementation */
+
+// eof
diff --git a/src/sysync/textitemtype.h b/src/sysync/textitemtype.h
new file mode 100755
index 0000000..e4e1c12
--- /dev/null
+++ b/src/sysync/textitemtype.h
@@ -0,0 +1,115 @@
+/*
+ * File: TextItemType.h
+ *
+ * Author: Lukas Zeller (luz@synthesis.ch)
+ *
+ * TTextItemType
+ * base class for plain text based items (notes, emails...)
+ *
+ * Copyright (c) 2002-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ * 2002-05-29 : luz : created from MimeDirItemType
+ *
+ */
+
+#ifndef TextItemType_H
+#define TextItemType_H
+
+// includes
+#include "syncitemtype.h"
+#include "multifielditemtype.h"
+#include "textprofile.h"
+
+
+namespace sysync {
+
+// Text based datatype
+class TTextTypeConfig : public TMultiFieldTypeConfig
+{
+ typedef TMultiFieldTypeConfig inherited;
+public:
+ TTextTypeConfig(const char *aElementName, TConfigElement *aParentElementP);
+ virtual ~TTextTypeConfig();
+ // properties
+ // public functions
+ // - create Sync Item Type of appropriate type from config
+ virtual TSyncItemType *newSyncItemType(TSyncSession *aSessionP, TSyncDataStore *aDatastoreP);
+protected:
+ #ifdef CONFIGURABLE_TYPE_SUPPORT
+ // check config elements
+ virtual bool localStartElement(const char *aElementName, const char **aAttributes, sInt32 aLine);
+ virtual void localResolve(bool aLastPass);
+ #endif
+ virtual void clear();
+}; // TTextTypeConfig
+
+const uInt16 ity_text=102; // must be unique
+
+class TTextItemType: public TMultiFieldItemType
+{
+ typedef TMultiFieldItemType inherited;
+public:
+ // constructor
+ TTextItemType(
+ TSyncSession *aSessionP,
+ TDataTypeConfig *aTypeCfgP, // type config
+ const char *aCTType,
+ const char *aVerCT,
+ TSyncDataStore *aRelatedDatastoreP,
+ TFieldListConfig *aFieldDefinitions // field definitions
+ );
+ // destructor
+ virtual ~TTextItemType();
+ // access to type
+ virtual uInt16 getTypeID(void) const { return ity_text; };
+ virtual bool isBasedOn(uInt16 aItemTypeID) const { return aItemTypeID==ity_text ? true : TMultiFieldItemType::isBasedOn(aItemTypeID); };
+ // differentiation between implemented and just descriptive TSyncTypeItems
+ virtual bool isImplemented(void) { return true; }; // MIME-DIR is an implementation
+ // helper to create same-typed instance via base class
+ // MUST BE IMPLEMENTED IN ALL DERIVED CLASSES!
+ virtual TSyncItemType *newCopyForSameType(
+ TSyncSession *aSessionP, // the session
+ TSyncDataStore *aDatastoreP // the datastore
+ );
+ /// @brief copy CTCap derived info from another SyncItemType
+ virtual bool copyCTCapInfoFrom(TSyncItemType &aSourceItem);
+ #ifdef OBJECT_FILTERING
+ // filtering
+ // - get field index of given filter expression identifier.
+ virtual sInt16 getFilterIdentifierFieldIndex(const char *aIdentifier, uInt16 aIndex);
+ #endif
+protected:
+ // - analyze CTCap for specific type
+ virtual bool analyzeCTCap(SmlDevInfCTCapPtr_t aCTCapP);
+ // - obtain property list for type, returns NULL if none available
+ virtual SmlDevInfCTDataPropListPtr_t newCTDataPropList(TTypeVariantDescriptor aVariantDescriptor);
+ // - Filtering: add keywords and property names to filterCap
+ virtual void addFilterCapPropsAndKeywords(SmlPcdataListPtr_t &aFilterKeywords, SmlPcdataListPtr_t &aFilterProps, TTypeVariantDescriptor aVariantDesc);
+ // Item data management
+ // - create new sync item of proper type and optimization for specified target
+ virtual TSyncItem *internalNewSyncItem(TSyncItemType *aTargetItemTypeP, TLocalEngineDS *aLocalDatastoreP);
+ // - fill in SyncML data (but leaves IDs empty)
+ virtual bool internalFillInData(
+ TSyncItem *aSyncItemP, // SyncItem to be filled with data
+ SmlItemPtr_t aItemP, // SyncML toolkit item Data to be converted into SyncItem (may be NULL if no data, in case of Delete or Map)
+ TLocalEngineDS *aLocalDatastoreP, // local datastore
+ TStatusCommand &aStatusCmd // status command that might be modified in case of error
+ );
+ // - sets data and meta from SyncItem data, but leaves source & target untouched
+ virtual bool internalSetItemData(
+ TSyncItem *aSyncItemP, // the syncitem to be represented as SyncML
+ SmlItemPtr_t aItem, // item with NULL meta and NULL data
+ TLocalEngineDS *aLocalDatastoreP // local datastore
+ );
+private:
+ // member fields
+ TProfileHandler *fProfileHandlerP;
+ TTextTypeConfig *fTypeCfgP; // the text type config element
+}; // TTextItemType
+
+
+} // namespace sysync
+
+#endif // TextItemType_H
+
+// eof
diff --git a/src/sysync/textprofile.cpp b/src/sysync/textprofile.cpp
new file mode 100755
index 0000000..37421c9
--- /dev/null
+++ b/src/sysync/textprofile.cpp
@@ -0,0 +1,1681 @@
+/*
+ * File: textprofile.cpp
+ *
+ * Author: Lukas Zeller (luz@synthesis.ch)
+ *
+ * TTextProfile
+ * utility class to parse line-by-line type text including RFC822 emails
+ *
+ * Copyright (c) 2002-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ * 2005-07-26 : luz : extracted from textitemtype
+ *
+ */
+
+
+// includes
+#include "prefix_file.h"
+
+#include "sysync.h"
+#include "textprofile.h"
+
+
+using namespace sysync;
+
+
+// Config
+// ======
+
+#define X_LIMIT_HEADER_NAME "X-Sync-Message-Limit"
+
+// line map config
+
+TLineMapDefinition::TLineMapDefinition(TConfigElement *aParentElementP, sInt16 aFid) :
+ TConfigElement("lm",aParentElementP)
+{
+ // save field ID
+ fFid=aFid;
+ // init others
+ clear();
+} // TLineMapDefinition::TLineMapDefinition
+
+
+
+void TLineMapDefinition::clear(void)
+{
+ // clear
+ // - default: all text
+ fNumLines=0;
+ fInHeader=false; // not restricted to header
+ fAllowEmpty=false; // no empty ones
+ // - no tagged header
+ TCFG_CLEAR(fHeaderTag);
+ // - no RFC822 specials
+ fValueType=vt822_plain;
+ fListSeparator=0;
+ fMaxRepeat=1;
+ fRepeatInc=1;
+ #ifdef OBJECT_FILTERING
+ // - no filterkeyword
+ TCFG_CLEAR(fFilterKeyword);
+ #endif
+} // TLineMapDefinition::clear
+
+
+#ifdef CONFIGURABLE_TYPE_SUPPORT
+
+// Conversion names for RFC(2)822 parsing
+const char * const RFC822ValueTypeNames[num822ValueTypes] = {
+ "text",
+ "date",
+ "body",
+ "rfc2047"
+};
+
+
+
+// server config element parsing
+bool TLineMapDefinition::localStartElement(const char *aElementName, const char **aAttributes, sInt32 aLine)
+{
+ // checking the elements
+ if (strucmp(aElementName,"numlines")==0)
+ expectInt16(fNumLines);
+ else if (strucmp(aElementName,"inheader")==0)
+ expectBool(fInHeader);
+ else if (strucmp(aElementName,"allowempty")==0)
+ expectBool(fAllowEmpty);
+ else if (strucmp(aElementName,"headertag")==0)
+ expectString(fHeaderTag);
+ // filtering
+ #ifdef OBJECT_FILTERING
+ else if (strucmp(aElementName,"filterkeyword")==0)
+ expectString(fFilterKeyword);
+ #endif
+ // RFC(2)822 parsing options
+ else if (strucmp(aElementName,"valuetype")==0)
+ expectEnum(sizeof(fValueType),&fValueType,RFC822ValueTypeNames,num822ValueTypes);
+ else if (strucmp(aElementName,"list")==0) {
+ // list spec
+ // - separator
+ const char *attr = getAttr(aAttributes,"separator");
+ if (!attr)
+ fail("list needs 'separator'");
+ fListSeparator=*attr;
+ // - max repetitions
+ fMaxRepeat=1;
+ attr = getAttr(aAttributes,"repeat");
+ if (attr) {
+ #ifdef ARRAYFIELD_SUPPORT
+ if (strucmp(attr,"array")==0) fMaxRepeat=REP_ARRAY;
+ else
+ #endif
+ if (!StrToShort(attr,fMaxRepeat))
+ return !fail("expected number or 'array' in 'repeat'");
+ }
+ fRepeatInc=1;
+ if (!getAttrShort(aAttributes,"increment",fRepeatInc,true))
+ return !fail("number expected in 'increment'");
+ }
+ // - none known here
+ else
+ return inherited::localStartElement(aElementName,aAttributes,aLine);
+ // ok
+ return true;
+} // TLineMapDefinition::localStartElement
+
+#endif
+
+
+TLineMapDefinition::~TLineMapDefinition()
+{
+ // nop so far
+} // TLineMapDefinition::~TLineMapDefinition
+
+
+
+// text-based datatype config
+
+TTextProfileConfig::TTextProfileConfig(const char *aElementName, TConfigElement *aParentElementP) :
+ TProfileConfig(aElementName,aParentElementP)
+{
+ clear();
+} // TTextProfileConfig::TTextProfileConfig
+
+
+TTextProfileConfig::~TTextProfileConfig()
+{
+ // make sure everything is deleted (was missing long time and caused mem leaks!)
+ clear();
+} // TTextProfileConfig::~TTextProfileConfig
+
+
+// init defaults
+void TTextProfileConfig::clear(void)
+{
+ // clear properties
+ // - init
+ fFieldListP=NULL;
+ #ifdef EMAIL_FORMAT_SUPPORT
+ fMIMEMail=false;
+ fBodyMIMETypesFid=VARIDX_UNDEFINED;
+ fSizeLimitField=VARIDX_UNDEFINED;
+ fBodyCountFid=VARIDX_UNDEFINED;
+ #ifdef EMAIL_ATTACHMENT_SUPPORT
+ // - no limit
+ fMaxAttachments=29999; // enough
+ // - fields not yet known
+ fAttachmentCountFid=VARIDX_UNDEFINED;
+ fAttachmentMIMETypesFid=VARIDX_UNDEFINED;
+ fAttachmentContentsFid=VARIDX_UNDEFINED;
+ fAttachmentSizesFid=VARIDX_UNDEFINED;
+ fAttachmentNamesFid=VARIDX_UNDEFINED;
+ #endif
+ #endif
+ // - remove line maps
+ TLineMapList::iterator pos;
+ for(pos=fLineMaps.begin();pos!=fLineMaps.end();pos++)
+ delete *pos;
+ fLineMaps.clear();
+ // clear inherited
+ inherited::clear();
+} // TTextProfileConfig::clear
+
+
+#ifdef CONFIGURABLE_TYPE_SUPPORT
+
+
+// config element parsing
+bool TTextProfileConfig::localStartElement(const char *aElementName, const char **aAttributes, sInt32 aLine)
+{
+ /* %%% this must be enabled if we want to reference script vars
+ #ifdef SCRIPT_SUPPORT
+ // early resolve basic multifield scripts so we can refer to local vars
+ if (!fScriptsResolved) %%% resolve resolvecontext;
+ #endif
+ */
+ // checking the elements
+ if (strucmp(aElementName,"linemap")==0) {
+ // <linemap field="SUBJECT">
+ const char* nam = getAttr(aAttributes,"field");
+ if (!fFieldListP)
+ return fail("'use' must be specified before first <linemap>");
+ // search field
+ // %%% add context here if we have any
+ sInt16 fid = TConfigElement::getFieldIndex(nam,fFieldListP);
+ if (fid==VARIDX_UNDEFINED)
+ return fail("'field' references unknown field '%s'",nam);
+ // create new linemap
+ TLineMapDefinition *linemapP = new TLineMapDefinition(this,fid);
+ // - save in list
+ fLineMaps.push_back(linemapP);
+ // - let element handle parsing
+ expectChildParsing(*linemapP);
+ }
+ #ifdef EMAIL_FORMAT_SUPPORT
+ else if (strucmp(aElementName,"mimemail")==0)
+ expectBool(fMIMEMail);
+ else if (strucmp(aElementName,"sizelimitfield")==0)
+ expectFieldID(fSizeLimitField,fFieldListP);
+ else if (strucmp(aElementName,"bodymimetypesfield")==0)
+ expectFieldID(fBodyMIMETypesFid,fFieldListP);
+ else if (strucmp(aElementName,"bodycountfield")==0)
+ expectFieldID(fBodyCountFid,fFieldListP);
+ #ifdef EMAIL_ATTACHMENT_SUPPORT
+ else if (strucmp(aElementName,"maxattachments")==0)
+ expectInt16(fMaxAttachments);
+ else if (strucmp(aElementName,"attachmentcountfield")==0)
+ expectFieldID(fAttachmentCountFid,fFieldListP);
+ else if (strucmp(aElementName,"attachmentmimetypesfield")==0)
+ expectFieldID(fAttachmentMIMETypesFid,fFieldListP);
+ else if (strucmp(aElementName,"attachmentsfield")==0)
+ expectFieldID(fAttachmentContentsFid,fFieldListP);
+ else if (strucmp(aElementName,"attachmentsizesfield")==0)
+ expectFieldID(fAttachmentSizesFid,fFieldListP);
+ else if (strucmp(aElementName,"attachmentnamesfield")==0)
+ expectFieldID(fAttachmentNamesFid,fFieldListP);
+ #endif
+ #endif
+ // - none known here
+ else
+ return TProfileConfig::localStartElement(aElementName,aAttributes,aLine);
+ // ok
+ return true;
+} // TTextProfileConfig::localStartElement
+
+
+// resolve
+void TTextProfileConfig::localResolve(bool aLastPass)
+{
+ // nop
+ // resolve inherited
+ inherited::localResolve(aLastPass);
+} // TTextProfileConfig::localResolve
+
+#endif
+
+
+#ifdef HARDCODED_TYPE_SUPPORT
+
+TLineMapDefinition *TTextProfileConfig::addLineMap(
+ sInt16 aFid, sInt16 aNumLines, bool aAllowEmpty,
+ bool aInHeader, const char* aHeaderTag,
+ T822ValueType aValueType,
+ char aListSeparator, sInt16 aMaxRepeat, sInt16 aRepeatInc
+)
+{
+ // create new linemap
+ TLineMapDefinition *linemapP = new TLineMapDefinition(this,aFid);
+ // set properties
+ linemapP->fNumLines=aNumLines;
+ linemapP->fAllowEmpty=aAllowEmpty;
+ linemapP->fInHeader=aInHeader;
+ TCFG_ASSIGN(linemapP->fHeaderTag,aHeaderTag);
+ // save email options
+ linemapP->fValueType=aValueType;
+ linemapP->fListSeparator=aListSeparator;
+ linemapP->fListSeparator=aMaxRepeat;
+ linemapP->fListSeparator=aRepeatInc;
+ // save in list
+ fLineMaps.push_back(linemapP);
+ // return pointer
+ return linemapP;
+} // TTextProfileConfig::addLineMap
+
+#endif
+
+
+// handler factory
+TProfileHandler *TTextProfileConfig::newProfileHandler(TMultiFieldItemType *aItemTypeP)
+{
+ // check if fieldlists match as they should
+ if (aItemTypeP->getFieldDefinitions()!=fFieldListP) {
+ // profile is for another field list, cannot be used for this item type
+ return NULL;
+ }
+ // our handler is the text profile handler
+ return (TProfileHandler *)(new TTextProfileHandler(this,aItemTypeP));
+}
+
+
+
+/*
+ * Implementation of TTextProfileHandler
+ */
+
+
+TTextProfileHandler::TTextProfileHandler(
+ TTextProfileConfig *aTextProfileCfgP,
+ TMultiFieldItemType *aItemTypeP
+) : TProfileHandler(aTextProfileCfgP, aItemTypeP)
+{
+ // save profile config pointer
+ fProfileCfgP = aTextProfileCfgP;
+ // datastore settable options defaults
+ fNoAttachments = false;
+ fItemSizeLimit = -1;
+} // TTextProfileHandler::TTextProfileHandler
+
+
+TTextProfileHandler::~TTextProfileHandler()
+{
+ // nop for now
+} // TTextProfileHandler::~TTextProfileHandler
+
+
+#ifdef OBJECT_FILTERING
+
+// Filtering: add keywords and property names to filterCap
+void TTextProfileHandler::addFilterCapPropsAndKeywords(SmlPcdataListPtr_t &aFilterKeywords, SmlPcdataListPtr_t &aFilterProps, TTypeVariantDescriptor aVariantDescriptor, TSyncItemType *aItemTypeP)
+{
+ // search linemaps for fields with keyword
+ TLineMapList::iterator pos;
+ for(pos=fProfileCfgP->fLineMaps.begin();pos!=fProfileCfgP->fLineMaps.end();pos++) {
+ // first priority: compare with explicit filterkeyword, if any
+ if (!TCFG_ISEMPTY((*pos)->fFilterKeyword)) {
+ // has a filterkeyword, show it
+ addPCDataStringToList(TCFG_CSTR((*pos)->fFilterKeyword), &aFilterKeywords);
+ }
+ }
+} // TTextProfileHandler::addFilterCapPropsAndKeywords
+
+
+
+// get field index of given filter expression identifier.
+sInt16 TTextProfileHandler::getFilterIdentifierFieldIndex(const char *aIdentifier, uInt16 aIndex)
+{
+ // search linemaps for tagged fields
+ TLineMapList::iterator pos;
+ for(pos=fProfileCfgP->fLineMaps.begin();pos!=fProfileCfgP->fLineMaps.end();pos++) {
+ // first priority: compare with explicit filterkeyword, if any
+ if (!TCFG_ISEMPTY((*pos)->fFilterKeyword)) {
+ // compare with filterkeyword
+ if (strucmp(TCFG_CSTR((*pos)->fFilterKeyword),aIdentifier)==0)
+ return (*pos)->fFid;
+ }
+ else if (TCFG_SIZE((*pos)->fHeaderTag)>1) { // one tag char and a separator at least
+ // if no filterkeyword defined, compare to header without separator
+ if (strucmp(TCFG_CSTR((*pos)->fHeaderTag),aIdentifier,TCFG_SIZE((*pos)->fHeaderTag)-1)==0)
+ return (*pos)->fFid;
+ }
+ }
+ // no field ID found in profile
+ return FID_NOT_SUPPORTED;
+} // TTextProfileHandler::getFilterIdentifierFieldIndex
+
+#endif
+
+
+#ifdef EMAIL_FORMAT_SUPPORT
+
+
+// find token in header data
+static cAppCharP findToken(cAppCharP aText, cAppCharP aTextEnd, char aStopChar, cAppCharP &aTokenStart, stringSize &aTokenLen)
+{
+ // skip trailing whitespace
+ cAppCharP e=aTextEnd;
+ if (!e)
+ e=aText+strlen(aText);
+ while (aText<e && isspace(*aText)) aText++;
+ bool quoted = (*aText=='"'); // " to fool buggy colorizer
+ if (quoted) {
+ aText++;
+ }
+ // token starts here
+ aTokenStart=aText;
+ // find end
+ while (aText<e) {
+ if (quoted) {
+ if (*aText=='"') break; // " to fool buggy colorizer
+ if (*aText=='\\') {
+ aText++;
+ if (*aText)
+ aText++; // do not interpret next
+ }
+ }
+ else {
+ if (*aText==aStopChar || isspace(*aText)) break;
+ }
+ aText++;
+ }
+ aTokenLen=aText-aTokenStart;
+ // skip ending quote
+ if (aText<e && quoted && *aText=='"') aText++; // " to fool buggy colorizer
+ // advance to next non-space
+ while (aText<e && isspace(*aText)) aText++;
+ // return position
+ return aText;
+} // findToken
+
+
+// check if line contains a MIME header
+static bool checkMimeHeaders(cAppCharP aLine, stringSize aSize, string &aContentType, TEncodingTypes &aEncoding, uInt32 &aContentLen, TCharSets &aCharSet, string &aBoundary, string *aFileNameP)
+{
+ stringSize toksz,valtoksz;
+ cAppCharP tok,valtok,p,e;
+ sInt16 en;
+ e=aLine+aSize;
+ // search header name
+ p=aLine;
+ p=findToken(p,e,':',tok,toksz);
+ if (*p!=':') return false; // no header
+ ++p;
+ // compare header name
+ if (strucmp(tok,"Content-Type",toksz)==0) {
+ // get content type
+ p=findToken(p,e,';',tok,toksz);
+ aContentType.assign(tok,toksz);
+ // get parameters
+ while (*p==';') {
+ p++;
+ p=findToken(p,e,'=',tok,toksz);
+ if (*p=='=') {
+ p++;
+ // get value
+ p=findToken(p,e,';',valtok,valtoksz);
+ if (strucmp(tok,"charset",toksz)==0) {
+ // charset
+ if (StrToEnum(MIMECharSetNames, numCharSets, en, valtok, valtoksz))
+ aCharSet=(TCharSets)en;
+ else
+ aCharSet=chs_unknown;
+ }
+ else if (strucmp(tok,"boundary",toksz)==0) {
+ // boundary
+ aBoundary.assign(valtok,valtoksz);
+ }
+ }
+ } // while params
+ }
+ else if (strucmp(tok,"Content-Transfer-Encoding",toksz)==0) {
+ // get encoding
+ p=findToken(p,e,';',tok,toksz);
+ if (StrToEnum(MIMEEncodingNames, numMIMEencodings, en, tok, toksz))
+ aEncoding = (TEncodingTypes)en;
+ else
+ aEncoding = enc_none;
+ }
+ else if (strucmp(tok,"Content-Length",toksz)==0) {
+ // get content length (not MIME, this is Synthesis' own invention, only valid for encoding=binary!!)
+ p=findToken(p,e,';',tok,toksz);
+ StrToULong(tok, aContentLen, toksz);
+ }
+ else if (aFileNameP && strucmp(tok,"Content-Disposition",toksz)==0) {
+ // get disposition
+ p=findToken(p,e,';',tok,toksz);
+ aFileNameP->erase();
+ if (strucmp(tok,"attachment",toksz)==0 || strucmp(tok,"inline",toksz)==0) {
+ while (*p==';') {
+ p++;
+ p=findToken(p,e,'=',tok,toksz);
+ if (strucmp(tok,"filename",toksz)==0) {
+ // filename, get it
+ if (*p=='=') {
+ p++;
+ p=findToken(p,e,';',tok,toksz);
+ aFileNameP->assign(tok,toksz);
+ }
+ }
+ else {
+ // other param, skip it
+ if (*p=='=') {
+ p++;
+ p=findToken(p,e,';',tok,toksz);
+ }
+ }
+ }
+ }
+ }
+ else return false; // no MIME header
+ // found MIME header
+ return true;
+} // checkMimeHeaders
+
+
+// parse body into appropriate field(s)
+cAppCharP TTextProfileHandler::parseBody(
+ cAppCharP aText, // body text to parse
+ stringSize aTextSize, // max text to parse
+ cAppCharP aType, TEncodingTypes aEncoding, uInt32 aContentLen, TCharSets aCharSet,
+ TMultiFieldItem &aItem, TLineMapDefinition *aLineMapP,
+ cAppCharP aBoundary // boundary, NULL if none
+)
+{
+ // empty boundary is no boundary
+ if (aBoundary && *aBoundary==0) aBoundary=NULL;
+ POBJDEBUGPRINTFX(fItemTypeP->getSession(),DBG_PARSE,(
+ "Parsing body (part) with Content-Type='%s', Encoding=%s, contentLen=%ld, Charset=%s, Boundary='%s'",
+ aType,
+ MIMEEncodingNames[aEncoding],
+ (long)aContentLen,
+ MIMECharSetNames[aCharSet],
+ aBoundary ? aBoundary : "<none>"
+ ));
+ // params for parts
+ string contentType=aType; // content type
+ string boundary; // main boundary
+ string filename; // attachment
+ TEncodingTypes encoding=aEncoding; // main encoding
+ uInt32 contentlen=aContentLen; // content len in case encoding is binary
+ TCharSets charset=aCharSet; // main charset
+ // helpers
+ string decoded;
+ string converted;
+ TItemField *fldP;
+ // check if alternative
+ bool alternative = strucmp(aType,"multipart/alternative")==0;
+ bool foundBody=false;
+ // search for first boundary occurrence if this is multipart
+ cAppCharP toBeParsed=NULL; // pointer to previous part to be processed
+ size_t previousSize=0;
+ cAppCharP prevStart=NULL;
+ cAppCharP p=aText;
+ cAppCharP eot=p+aTextSize;
+ bool startpart=false;
+ // process parts
+ do {
+ startpart=false;
+ // search for a boundary occurrence if this is multipart
+ if (aBoundary) {
+ size_t bl=strlen(aBoundary);
+ prevStart=p;
+ // check for special case: if we have something to parse, and encoding is binary,
+ // use the content-length header to skip to next boundary
+ if (toBeParsed && encoding==enc_binary) {
+ // we simply KNOW how much to skip until next boundary starts
+ p=toBeParsed+contentlen;
+ // p now points to the next boundary indicator
+ }
+ // now detect boundary indicator
+ do {
+ if (p+2+bl<=eot && strucmp(p,"--",2)==0) {
+ p+=2;
+ if (strucmp(p,aBoundary,bl)==0) {
+ // start of line matches boundary
+ startpart=true;
+ // calc how much we skipped until finding this boundary
+ previousSize=p-2-prevStart;
+ // A boundary has to be preceeded by a CRLF according to rfc2046, and
+ // this CRLF belongs to the boundary and is NOT considered to be content of the preceeding part
+ // Therefore, the following code is only needed for error tolerance reasons - for
+ // correctly formatted message it's just a -=2.
+ if (previousSize>2) {
+ if (*(p-3)==0x0A || *(p-3)==0x0D) previousSize--;
+ if (*(p-4)==0x0A || *(p-4)==0x0D) previousSize--;
+ }
+ // Note: in case of binary encoding, previousSize should be contentlen here!
+ p+=bl;
+ }
+ }
+ // skip until end of line
+ while (p<eot && *p && *p!='\x0A') p++;
+ if (p<eot && *p) p++; // skip LF as well
+ } while (p<eot && *p && !startpart);
+ // if we exit here and have no startpart, body does not contain expected parts
+ // so exit here
+ if (!startpart)
+ return p;
+ else {
+ // altough previousSize should be equal to contentlen here, trust contentlen
+ if (toBeParsed && encoding==enc_binary)
+ previousSize=contentlen;
+ }
+ }
+ // p is now start of space after found boundary if startpart is set
+ // previousSize is now size of space between where we've started and
+ // start of next boundary. If toBeParsed, this is a part, otherwise it's a preamble
+ if (toBeParsed) {
+ if (previousSize>0) {
+ // parse leaf part (DO NOT CHANGE p HERE, it points to next part)
+ // decide if this is body or attachment
+ bool isText = contentType.empty() || (strucmp(contentType.c_str(),"text/",5)==0);
+ if (isText && filename.empty()) {
+ // this is a part of the body
+ // - check if we can store multiple bodies
+ bool multibody = aItem.getField(aLineMapP->fFid)->isArray();
+ if (!foundBody || multibody) {
+ sInt16 bodyIndex=VARIDX_UNDEFINED;
+ // not found plain text body yet (or we can store multiple bodies)
+ if (!alternative || strucmp(contentType.c_str(),"text/plain")==0) {
+ // strictly plain text or not alternative, store it in the first body array element
+ bodyIndex=0;
+ // found plain text body, discard other alternatives if we have no other body variants
+ if (alternative) foundBody=true;
+ }
+ else if (multibody) {
+ // text, but not plain - and we can store alternatives, do it!
+ bodyIndex=++fBodyAlternatives;
+ }
+ // if we shall store, do it now (otherwise, fldP is NULL)
+ if (bodyIndex!=VARIDX_UNDEFINED) {
+ // - get MIME-type field
+ fldP = aItem.getArrayField(fProfileCfgP->fBodyMIMETypesFid,bodyIndex);
+ if (fldP) {
+ fldP->setAsString(contentType); // store MIME type
+ }
+ // - get content field
+ fldP = aItem.getArrayField(aLineMapP->fFid,bodyIndex);
+ if (fldP) {
+ // - decode
+ decoded.erase();
+ decoded.reserve(previousSize); // we need approx this sizee -> reserve to speed up
+ appendDecoded(
+ toBeParsed,
+ previousSize,
+ decoded,
+ encoding
+ );
+ // - convert charset
+ converted.erase();
+ converted.reserve(decoded.size()); // we need approx this size -> reserve to speed up
+ appendStringAsUTF8(
+ decoded.c_str(),
+ converted,
+ charset,
+ lem_cstr
+ );
+ decoded.erase(); // we do not need this any more
+ // add to field
+ fldP->appendString(converted.c_str(), converted.size());
+ }
+ }
+ }
+ }
+ else {
+ // this is an attachment, ignore it if we have no attachment support
+ #ifdef EMAIL_ATTACHMENT_SUPPORT
+ if (filename.empty()) {
+ // create filename
+ StringObjPrintf(filename,"attachment%hd.bin",fExtraParts);
+ }
+ // save filename
+ if (fProfileCfgP->fAttachmentNamesFid!=VARIDX_UNDEFINED) {
+ fldP=aItem.getArrayField(fProfileCfgP->fAttachmentNamesFid,fExtraParts);
+ if (fldP) {
+ fldP->setAsString(filename.c_str());
+ }
+ }
+ // save type
+ if (fProfileCfgP->fAttachmentMIMETypesFid!=VARIDX_UNDEFINED) {
+ fldP=aItem.getArrayField(fProfileCfgP->fAttachmentMIMETypesFid,fExtraParts);
+ if (fldP) {
+ fldP->setAsString(contentType.c_str());
+ }
+ }
+ // now decode attachment itself
+ decoded.erase();
+ decoded.reserve(previousSize); // we need approx this sizee -> reserve to speed up
+ appendDecoded(
+ toBeParsed,
+ previousSize,
+ decoded,
+ encoding
+ );
+ // save attachment data
+ if (fProfileCfgP->fAttachmentContentsFid!=VARIDX_UNDEFINED) {
+ fldP=aItem.getArrayField(fProfileCfgP->fAttachmentContentsFid,fExtraParts);
+ if (fldP) {
+ fldP->setAsString(decoded.c_str(),decoded.size());
+ }
+ }
+ // save size in extra field
+ if (fProfileCfgP->fAttachmentSizesFid!=VARIDX_UNDEFINED) {
+ fldP=aItem.getArrayField(fProfileCfgP->fAttachmentSizesFid,fExtraParts);
+ if (fldP) {
+ fldP->setAsInteger(decoded.size());
+ }
+ }
+ decoded.erase(); // we do not need this any more
+ // done, found one extra part
+ fExtraParts++;
+ #else
+ // Attachment cannot be processed
+ // - get body field
+ fldP = aItem.getArrayField(aLineMapP->fFid,0);
+ // - append attachment replacement message
+ string msg;
+ if (filename.empty()) filename="<unnamed>";
+ StringObjPrintf(msg,"\n\nAttachment not stored: %s\n\n",filename.c_str());
+ fldP->appendString(msg.c_str());
+ #endif
+ }
+ } // if not empty part
+ // done
+ toBeParsed=NULL;
+ // if parsing single part, we're done
+ if (!aBoundary) return p;
+ }
+ // check for more parts or exit if this was the closing boundary
+ if (startpart) {
+ // we have found a boundary above, p=space following it
+ // part starts here
+ bool isPart=false;
+ // - get headers
+ while (p<eot && *p && *p!='\x0D' && *p!='\x0A' && *p!='-') {
+ // not end or empty line or start of other boundary, find end of next header line
+ char c;
+ cAppCharP q=p;
+ while (q<eot && (c=*q++)) {
+ if (c=='\x0D' || c=='\x0A') {
+ if (c=='\x0D' && *q=='\x0A') {
+ // CRLF sequence, do not count CR
+ }
+ else {
+ // end of line, see if folded
+ if (*q=='\x0D' || *q=='\x0A' || !isspace(*q)) {
+ // not folded, end of line
+ break;
+ }
+ }
+ }
+ }
+ // check headers
+ if (checkMimeHeaders(p,q-p,contentType,encoding,contentlen,charset,boundary,&filename))
+ isPart=true; // at least one relevant header found, this is a part
+ // next line
+ p=q;
+ }
+ // now we have all the headers
+ // - skip end of headers (CR)LF if any
+ if (p<eot && *p=='\x0D') p++;
+ if (p<eot && *p=='\x0A') p++;
+ // - check for end of this multipart
+ if (!isPart) {
+ // this is not a part, end current multipart scanning
+ // - return start of next boundary or end of text
+ return p;
+ }
+ // we have now the valid headers for the next part
+ // - if this is a multipart, recurse
+ if (strucmp(contentType.c_str(),"multipart/",10)==0) {
+ // recurse
+ if (boundary.empty()) return NULL; // multipart w/o boundary is bad
+ p = parseBody(
+ p, // body text to parse
+ eot-p, // max size to parse
+ contentType.c_str(),encoding,contentlen,charset,
+ aItem, aLineMapP,
+ boundary.c_str() // boundary for nested parts
+ );
+ }
+ else {
+ // single leaf part, process it when we've found the next boundary
+ toBeParsed=p;
+ }
+ } // if startpart
+ else {
+ // no start of a part by boundary
+ if (!aBoundary) {
+ // not part of a multipart, parse as is
+ toBeParsed=p;
+ previousSize=eot-p;
+ }
+ }
+ } while(true);
+} // parseBody
+
+#endif
+
+// parse header fields
+
+
+// parse value into appropriate field(s)
+bool TTextProfileHandler::parseContent(const char *aValue, stringSize aValSize, TMultiFieldItem &aItem, TLineMapDefinition *aLineMapP)
+{
+ // get field
+ TItemField *fieldP = aItem.getField(aLineMapP->fFid);
+ // use appropriate translator
+ string s;
+ switch (aLineMapP->fValueType) {
+ case vt822_timestamp:
+ #ifdef EMAIL_FORMAT_SUPPORT
+ // rfc822 timestamp
+ if (!fieldP->isBasedOn(fty_timestamp)) break;
+ s.assign(aValue,aValSize);
+ if (!(static_cast<TTimestampField *>(fieldP)->setAsRFC822date(s.c_str(),aItem.getSession()->fUserTimeContext,false)))
+ fieldP->assignEmpty();
+ break;
+ #endif
+ case vt822_body:
+ #ifdef EMAIL_FORMAT_SUPPORT
+ // falls through to plain if email support is switched off
+ if (fProfileCfgP->fMIMEMail) {
+ // clear body text field
+ fieldP->assignEmpty();
+ fExtraParts=0;
+ fBodyAlternatives=0;
+ // now parse body (we should already have parsed the content headers
+ parseBody(
+ aValue, // body text to parse
+ aValSize,
+ fContentType.c_str(),fEncoding,fContentLen,fCharSet,
+ aItem, aLineMapP,
+ fBoundary.c_str() // boundary, NULL or empty if none
+ );
+ // save number of bodies
+ if (fProfileCfgP->fBodyCountFid!=VARIDX_UNDEFINED) {
+ TItemField *fldP=aItem.getField(fProfileCfgP->fBodyCountFid);
+ if (fldP) {
+ fldP->setAsInteger(fBodyAlternatives+1);
+ }
+ }
+ // save number of attachments
+ #ifdef EMAIL_ATTACHMENT_SUPPORT
+ if (fProfileCfgP->fAttachmentCountFid!=VARIDX_UNDEFINED) {
+ TItemField *fldP=aItem.getField(fProfileCfgP->fAttachmentCountFid);
+ if (fldP) {
+ fldP->setAsInteger(fExtraParts);
+ }
+ }
+ #endif
+ break;
+ }
+ #endif
+ goto standardfield;
+ case vt822_rfc2047:
+ #ifdef EMAIL_FORMAT_SUPPORT
+ // text field encoded according to RFC2047
+ s.erase();
+ appendRFC2047AsUTF8(aValue,aValSize,s);
+ fieldP->setAsString(s.c_str());
+ break;
+ #else
+ goto standardfield;
+ #endif
+
+ standardfield:
+ case vt822_plain:
+ // plain text
+ default:
+ // assign as string
+ fieldP->setAsString(aValue,aValSize);
+ break;
+ }
+ return true;
+} // TTextProfileHandler::parseContent
+
+
+
+#ifdef EMAIL_FORMAT_SUPPORT
+
+
+// add a MIME-Boundary
+static void addBoundary(string &aString, sInt16 aLevel, bool aForHeader=false)
+{
+ if (!aForHeader) aString+="\x0D\x0A--";
+ StringObjAppendPrintf(aString,"--==========_=_nextpart_%03hd_42503735617.XE======",aLevel);
+ if (!aForHeader) aString+="\x0D\x0A";
+} // TTextProfileHandler::addBoundary
+
+
+// add a body content-type header
+static void addBodyTypeHeader(sInt16 aBodyTypeFid, sInt16 aBodyIndex, TMultiFieldItem &aItem, string &aString)
+{
+ string bodytype;
+ TItemField *fldP = aItem.getArrayField(aBodyTypeFid,aBodyIndex,true);
+ if (fldP && !fldP->isEmpty())
+ fldP->getAsString(bodytype);
+ else
+ bodytype="text/plain";
+ aString+="Content-Type: ";
+ aString+=bodytype;
+ aString+="; charset=\"UTF-8\"\x0D\x0A";
+} // addBodyTypeHeader
+
+
+// generate body (multipart/alternative if there is more than one)
+// if aLevel==0, content type was already set before
+bool TTextProfileHandler::generateBody(sInt16 aLevel, TMultiFieldItem &aItem, TLineMapDefinition *aLineMapP, string &aString)
+{
+ // check if we need to add headers
+ if (aLevel>0) {
+ #ifdef EMAIL_ATTACHMENT_SUPPORT
+ if (fBodyAlternatives>0) {
+ // multiple bodies, nested multipart
+ aString+="Content-Type: multipart/alternative;\x0D\x0A boundary=\"";
+ addBoundary(aString,aLevel,true); // nested boundary
+ aString+="\"\x0D\x0A";
+ }
+ else
+ #endif
+ {
+ // single body, add body type
+ addBodyTypeHeader(fProfileCfgP->fBodyMIMETypesFid, 0, aItem, aString);
+ // - end part header
+ aString+="\x0D\x0A";
+ }
+ }
+ // now add body/bodies
+ // - get size limit for this item
+ sInt16 bodyindex=0;
+ do {
+ #ifdef EMAIL_ATTACHMENT_SUPPORT
+ if (fBodyAlternatives>0 && fItemSizeLimit!=0) {
+ // - opening boundary
+ addBoundary(aString,aLevel); // nested
+ // - content type
+ addBodyTypeHeader(fProfileCfgP->fBodyMIMETypesFid, bodyindex, aItem, aString);
+ // - end part header
+ aString+="\x0D\x0A";
+ }
+ #endif
+ // get body field
+ TItemField *fieldP = aItem.getArrayField(aLineMapP->fFid,bodyindex);
+ if (!fieldP)
+ break; // should not happen
+ // now generate body contents
+ if (fItemSizeLimit==0) {
+ // limited to nothing, just don't send anything
+ fLimited=true;
+ break;
+ }
+ else if (fieldP->isBasedOn(fty_string)) {
+ TStringField *sfP = static_cast<TStringField *>(fieldP);
+ if (fItemSizeLimit>0) {
+ // limited string field
+ // - check if we can add more
+ if (fItemSizeLimit<=fGeneratedBytes) {
+ // already exhausted, suppress body completely
+ fLimited=true;
+ break;
+ }
+ // add limited number of body bytes
+ // - determine number of bytes to send
+ #ifdef STREAMFIELD_SUPPORT
+ sInt32 bodysize=sfP->getStreamSize();
+ if (bodysize+fGeneratedBytes > fItemSizeLimit) {
+ bodysize=fItemSizeLimit-fGeneratedBytes;
+ fLimited=true;
+ }
+ // - get appropriate number of bytes
+ char *bodyP = new char[bodysize+1];
+ sfP->resetStream();
+ bodysize = sfP->readStream(bodyP,bodysize);
+ bodyP[bodysize]=0;
+ // - append to content string
+ aString.reserve(aString.size()+bodysize); // reserve what we need approximately
+ appendUTF8ToString(
+ bodyP,
+ aString,
+ chs_utf8, // always UTF8 for body
+ lem_dos // CRLFs for email
+ );
+ // approximately, UTF-8 conversion and CRLF might cause slightly more chars
+ fGeneratedBytes+=bodysize;
+ // - get rid of buffer
+ delete bodyP;
+ #else
+ // simply get it
+ fieldP->appendToString(aString,fItemSizeLimit);
+ fGeneratedBytes+=fieldP->getStringSize();
+ #endif
+ }
+ else {
+ // no limit, simply append to content string
+ appendUTF8ToString(
+ sfP->getCStr(),
+ aString,
+ chs_utf8, // always UTF8 for body
+ lem_dos // CRLFs for email
+ );
+ // approximately, UTF-8 conversion and CRLF might cause slightly more chars
+ fGeneratedBytes+=sfP->getStringSize();
+ }
+ }
+ else {
+ // no string field, just append string representation
+ fieldP->appendToString(aString);
+ fGeneratedBytes+=fieldP->getStringSize();
+ }
+ // done one body
+ bodyindex++;
+ // repeat until all done
+ } while (bodyindex<=fBodyAlternatives && (fItemSizeLimit<0 || fGeneratedBytes<fItemSizeLimit));
+ // now add final boundary if we had alternatives
+ #ifdef EMAIL_ATTACHMENT_SUPPORT
+ if (fBodyAlternatives>0 && fItemSizeLimit!=0) {
+ // - closing boundary for last part
+ addBoundary(aString,aLevel); // nested
+ }
+ #endif
+ return true;
+} // TTextProfileHandler::generateBody
+
+#endif
+
+
+// generate contents of a header or body
+// returns true if tagging and folding is needed on output,
+// false if output can simply be appended to text (such as: no output at all)
+bool TTextProfileHandler::generateContent(TMultiFieldItem &aItem, TLineMapDefinition *aLineMapP, string &aString)
+{
+ aString.erase(); // nothing by default
+ bool needsfolding=true;
+ string s;
+
+ // %%% missing repeats
+ TItemField *fieldP=aItem.getField(aLineMapP->fFid);
+ if (!fieldP) return false; // no field contents, do not even show the tag
+ switch (aLineMapP->fValueType) {
+ case vt822_body:
+ // body with size restriction
+ #ifdef EMAIL_FORMAT_SUPPORT
+ #ifdef EMAIL_ATTACHMENT_SUPPORT
+ // - multipart is supported
+ if (fExtraParts>0) {
+ // add body as first part
+ // - opening boundary
+ addBoundary(aString,0);
+ // - add body on level 1 (means that it must add its own headers)
+ generateBody(1,aItem,aLineMapP,aString);
+ // - add attachments
+ sInt16 attIdx;
+ TItemField *fldP;
+ for (attIdx=0; attIdx<fExtraParts; attIdx++) {
+ // - get size of next attachment
+ sInt32 sizebefore=aString.size(); // remember size before this attachment
+ sInt32 attachsize=0;
+ if (fProfileCfgP->fAttachmentSizesFid!=VARIDX_UNDEFINED) {
+ fldP=aItem.getArrayField(fProfileCfgP->fAttachmentSizesFid,attIdx,true);
+ if (!fldP) continue;
+ // get it from separate field
+ attachsize=fldP->getAsInteger();
+ }
+ else {
+ // get it from attachment itself (will probably pull proxy)
+ fldP=aItem.getArrayField(fProfileCfgP->fAttachmentContentsFid,attIdx,true);
+ if (!fldP) continue;
+ attachsize=fldP->getStringSize();
+ }
+ // - check if we have data for the attachment
+ if (attachsize==0) continue;
+ // Prepare attachment
+ TItemField *attfldP = NULL; // none yet
+ string attachMsg;
+ attachMsg.erase(); // no message
+ // - get content type
+ bool isText=false;
+ fldP = aItem.getArrayField(fProfileCfgP->fAttachmentMIMETypesFid,attIdx,true);
+ string contenttype;
+ if (fldP && !fldP->isEmpty()) {
+ fldP->getAsString(contenttype);
+ }
+ else {
+ contenttype="application/octet-stream";
+ }
+ // - check for text/xxxx contents
+ if (strucmp(contenttype.c_str(),"text/",5)==0) isText=true;
+ // - check if attachment has enough room
+ if (
+ (fItemSizeLimit>=0 && fGeneratedBytes+attachsize>fItemSizeLimit) || // limit specified by client
+ !fItemTypeP->getSession()->dataSizeTransferable(fGeneratedBytes+attachsize*(isText ? 3 : 4)/3) // physical limit as set by maxMsgSize in SyncML 1.0 and maxObjSize in SyncML 1.1
+ ) {
+ // no room for attachment, include a text message instead
+ fldP = aItem.getArrayField(fProfileCfgP->fAttachmentNamesFid,attIdx,true);
+ string attnam;
+ if (fldP)
+ fldP->getAsString(attnam);
+ else
+ attnam="unnamed";
+ StringObjPrintf(attachMsg,
+ "\x0D\x0A" "Attachment suppressed: '%s' (%ld KBytes)\x0D\x0A",
+ attnam.empty() ? "<unnamed>" : attnam.c_str(),
+ long(attachsize/1024)
+ );
+ // set type
+ contenttype="text/plain";
+ isText=true; // force in-line
+ // signal incomplete message
+ // NOTE: other attachments that are smaller may still be included
+ fLimited=true;
+ }
+ else {
+ // we can send the attachment
+ attfldP = aItem.getArrayField(fProfileCfgP->fAttachmentContentsFid,attIdx,true);
+ if (!attfldP) continue; // cannot generate this attachment
+ }
+ // - opening boundary for attachment
+ addBoundary(aString,0);
+ // - add disposition
+ aString+="Content-Disposition: ";
+ if (isText) {
+ // text is always in-line
+ aString+="inline";
+ }
+ else {
+ // non-text is attachment if it has a filename
+ fldP = aItem.getArrayField(fProfileCfgP->fAttachmentNamesFid,attIdx,true);
+ if (fldP && !fldP->isEmpty()) {
+ // has a filename, make attachment
+ aString+="attachment; filename=\"";
+ fldP->appendToString(aString);
+ aString+="\"";
+ }
+ else {
+ // has no filename, show inline
+ aString+="inline";
+ }
+ }
+ aString+="\x0D\x0A";
+ // - start content type (but no charset yet)
+ aString+="Content-Type: ";
+ aString+=contenttype;
+ // - check attachment mode
+ if (attfldP && attfldP->isBasedOn(fty_blob)) {
+ // Attachment is a BLOB, so it may contain binary data
+ TBlobField *blobP = static_cast<TBlobField *>(attfldP);
+ // make sure charset/encoding are valid
+ blobP->makeContentsValid();
+ // - get charset from the BLOB
+ if (blobP->fCharset!=chs_unknown) {
+ aString+="; charset=\"";
+ aString+=MIMECharSetNames[blobP->fCharset];
+ aString+='"';
+ }
+ aString+="\x0D\x0A";
+ // - if known, use originally requested encoding
+ TEncodingTypes enc = blobP->fWantsEncoding;
+ TEncodingTypes hasenc = blobP->fHasEncoding;
+ // - make sure we use a valid encoding that is ok for sending as text
+ if (enc==enc_b || enc==enc_none || enc==enc_binary) {
+ enc = isText ? enc_8bit : enc_base64;
+ }
+ // - see if we should transmit the existing encoding
+ if (isText) {
+ if (hasenc==enc_7bit && hasenc==enc_8bit && hasenc==enc_quoted_printable)
+ enc=hasenc; // already encoded
+ }
+ else {
+ if (hasenc==enc_base64 || hasenc==enc_b)
+ enc=hasenc; // already encoded
+ }
+ // when we are in Synthesis-special mode, and encoding is WBXML, we can use plain binary encoding
+ // (specifying the length with a Content-Length: header)
+ if (fItemTypeP->getTypeConfig()->fBinaryParts && fItemTypeP->getSession()->getEncoding()==SML_WBXML && (enc==enc_b || enc==enc_base64)) {
+ // switch to 1:1 binary
+ enc=enc_binary;
+ StringObjAppendPrintf(aString,"Content-Length: %ld\x0D\x0A", long(blobP->getStringSize()));
+ }
+ // - set transfer encoding from the BLOB
+ aString+="Content-Transfer-Encoding: ";
+ aString+=MIMEEncodingNames[enc];
+ aString+="\x0D\x0A";
+ // - end of part headers
+ aString+="\x0D\x0A";
+ // - now add contents as-is (this pulls the proxy now)
+ appendEncoded(
+ (const uInt8 *)blobP->getCStr(), // input
+ blobP->getStringSize(),
+ aString, // append output here
+ enc==hasenc ? enc_none : enc, // desired encoding if not already encoded
+ MIME_MAXLINESIZE, // limit to standard MIME-linesize
+ 0, // current line size
+ false // insert CRLFs for line breaks
+ );
+ }
+ else {
+ // Attachment isn't a BLOB, but a string. Transmit as 8bit, UTF-8
+ aString+="; charset=\"";
+ aString+=MIMECharSetNames[chs_utf8];
+ aString+="\"\x0D\x0A";
+ // - content encoding is 8bit
+ aString+="Content-Transfer-Encoding: ";
+ aString+=MIMEEncodingNames[enc_8bit];
+ aString+="\x0D\x0A";
+ // - end of part headers
+ aString+="\x0D\x0A";
+ // - simply append string
+ if (attfldP) {
+ string s;
+ attfldP->getAsString(s);
+ appendUTF8ToString(
+ s.c_str(),
+ aString,
+ chs_utf8, // always UTF8 for body
+ lem_dos // CRLFs for email
+ );
+ }
+ else {
+ // append attachment suppression message
+ aString+=attachMsg; // no attachment field, append replacement text instead
+ }
+ }
+ // count added bytes
+ fGeneratedBytes+=(aString.size()-sizebefore);
+ } // for all attachments
+ // - closing boundary
+ addBoundary(aString,0);
+ }
+ else
+ #endif
+ {
+ // message consists only of a body (which might have alternatives)
+ // - add body on level 0 (means that it must not have own headers)
+ generateBody(0,aItem,aLineMapP,aString);
+ }
+ // Body does not need any folding
+ needsfolding=false;
+ break;
+ #else
+ // no EMAIL FORMAT support
+ goto standardfield;
+ #endif
+ case vt822_rfc2047:
+ // text field encoded according to RFC2047
+ #ifdef EMAIL_FORMAT_SUPPORT
+ if (fieldP->isUnassigned()) return false; // field not assigned, do not even show the tag
+ fieldP->getAsString(s);
+ appendUTF8AsRFC2047(s.c_str(),aString);
+ break;
+ #else
+ goto standardfield;
+ #endif
+ case vt822_timestamp:
+ if (fieldP->isUnassigned()) return false; // field not assigned, do not even show the tag
+ #ifdef EMAIL_FORMAT_SUPPORT
+ if (!fieldP->isBasedOn(fty_timestamp)) break;
+ static_cast<TTimestampField *>(fieldP)->getAsRFC822date(aString,aItem.getSession()->fUserTimeContext,true);
+ break;
+ #endif
+
+#ifndef EMAIL_FORMAT_SUPPORT
+ standardfield:
+#endif
+ case vt822_plain:
+ // plain text
+ if (fieldP->isUnassigned()) return false; // field not assigned, do not even show the tag
+ fieldP->getAsString(aString);
+ break;
+ case num822ValueTypes:
+ // not handled?
+ break;
+ }
+ return needsfolding;
+} // TTextProfileHandler::generateContent
+
+
+// generate Data item (includes header and footer)
+void TTextProfileHandler::generateText(TMultiFieldItem &aItem, string &aString)
+{
+ TLineMapList::iterator pos;
+
+ // reset byte counter
+ fGeneratedBytes=0;
+ fLimited=false;
+ #ifdef EMAIL_ATTACHMENT_SUPPORT
+ fExtraParts=0;
+ #endif
+ #ifdef EMAIL_FORMAT_SUPPORT
+ fBodyAlternatives=0;
+ bool multipart=false;
+ #endif
+
+ #ifdef SYDEBUG
+ POBJDEBUGPRINTFX(fItemTypeP->getSession(),DBG_GEN+DBG_HOT,("Generating...."));
+ aItem.debugShowItem(DBG_DATA+DBG_GEN);
+ #endif
+
+ // init attachment limit
+ // - get from datastore if one is related
+ if (fRelatedDatastoreP) {
+ fItemSizeLimit = fRelatedDatastoreP->getItemSizeLimit();
+ fNoAttachments = fRelatedDatastoreP->getNoAttachments();
+ }
+ // - if size limit is zero or attachments explicitly disabled,
+ // attachments are not allowed for this item
+ #ifdef EMAIL_ATTACHMENT_SUPPORT
+ if (fItemSizeLimit==0 || fNoAttachments)
+ fAttachmentLimit=0; // no attachments
+ else
+ fAttachmentLimit=fProfileCfgP->fMaxAttachments; // use limit from datatype config
+ #endif
+ // generate according to linemaps
+ bool header = (*fProfileCfgP->fLineMaps.begin())->fInHeader; // we are in header if first is in header
+ for (pos=fProfileCfgP->fLineMaps.begin();pos!=fProfileCfgP->fLineMaps.end();pos++) {
+ // get linemap config
+ TLineMapDefinition *linemapP = *pos;
+ // separate body
+ if (header && !linemapP->fInHeader) {
+ // add special email headers
+ #ifdef EMAIL_FORMAT_SUPPORT
+ if (fProfileCfgP->fMIMEMail) {
+ // basic support
+ TItemField *cntFldP;
+ #ifdef EMAIL_ATTACHMENT_SUPPORT
+ // attachments allowed, get number
+ cntFldP = aItem.getField(fProfileCfgP->fAttachmentCountFid);
+ if (cntFldP && !cntFldP->isEmpty()) {
+ // we have a count field, get it's value
+ fExtraParts=cntFldP->getAsInteger();
+ }
+ else {
+ // determine exta part number by counting attachments
+ if (fProfileCfgP->fAttachmentContentsFid) {
+ fExtraParts=aItem.getField(fProfileCfgP->fAttachmentContentsFid)->arraySize();
+ }
+ }
+ // limit to what is allowed
+ if (fExtraParts > fProfileCfgP->fMaxAttachments) fExtraParts=fProfileCfgP->fMaxAttachments;
+ if (fExtraParts > fAttachmentLimit) { fExtraParts=fAttachmentLimit; fLimited=true; }
+ // check if we have body alternatives
+ cntFldP = aItem.getField(fProfileCfgP->fBodyCountFid);
+ if (cntFldP && !cntFldP->isEmpty()) {
+ // we have a count field, get it's value
+ fBodyAlternatives=cntFldP->getAsInteger()-1;
+ }
+ else {
+ fBodyAlternatives = aItem.getField(linemapP->fFid)->arraySize()-1;
+ }
+ if (fBodyAlternatives<0) fBodyAlternatives=0;
+ // now add multipart content header if we have extra parts to send
+ if (fExtraParts>0) {
+ // we have attachments, this will be a multipart/mixed
+ aString+="Content-Type: multipart/mixed;\x0D\x0A boundary=\"";
+ addBoundary(aString,0,true);
+ aString+="\"\x0D\x0A";
+ multipart=true;
+ }
+ else if (fBodyAlternatives) {
+ // no attachments, but multiple bodies
+ aString+="Content-Type: multipart/alternative;\x0D\x0A boundary=\"";
+ addBoundary(aString,0,true);
+ aString+="\"\x0D\x0A";
+ multipart=true;
+ }
+ else {
+ // no attachments and no body alternatives, single body
+ // - set type header for first and only part
+ addBodyTypeHeader(fProfileCfgP->fBodyMIMETypesFid, 0,aItem,aString);
+ }
+ #else
+ // only single body supported, always UTF-8
+ addBodyTypeHeader(fProfileCfgP->fBodyMIMETypesFid, 0,aItem,aString);
+ #endif
+ // now add encoding header, always 8-bit
+ aString+="Content-Transfer-Encoding: 8BIT\x0D\x0A";
+ }
+ else {
+ // no mail format, end headers here
+ aString.append("\x0D\x0A"); // extra empty line
+ }
+ #else
+ // end headers
+ aString.append("\x0D\x0A"); // extra empty line
+ #endif
+ }
+ // generate value
+ string fval;
+ bool tagandfold=generateContent(aItem,linemapP,fval);
+ // prevent empty ones if selected
+ if (fval.empty() && !linemapP->fAllowEmpty) continue;
+ // prefix with tag if any
+ bool tagged=!TCFG_ISEMPTY(linemapP->fHeaderTag);
+ // add field contents now
+ // generate contents from field
+ if (!tagandfold) {
+ // no folding necessary
+ #ifdef EMAIL_FORMAT_SUPPORT
+ // - add updated limit header here if message is really limited
+ if (fProfileCfgP->fMIMEMail) {
+ if (fProfileCfgP->fSizeLimitField!=VARIDX_UNDEFINED) {
+ TItemField *fldP = aItem.getField(fProfileCfgP->fSizeLimitField);
+ fieldinteger_t limit = fItemSizeLimit;
+ // now update its value
+ if (!fLimited) limit=-1;
+ fldP->setAsInteger(limit); // limited to specified size
+ // now add the updated limit header (we have no linemap for it)
+ aString+=X_LIMIT_HEADER_NAME ": ";
+ fldP->appendToString(aString);
+ aString+="\x0D\x0A"; // end of header
+ }
+ // terminate header not before here
+ if (header && !linemapP->fInHeader) {
+ // end headers
+ aString.append("\x0D\x0A"); // extra empty line
+ }
+ }
+ #endif
+ // - fVal includes everything needed INCLUDING tag AND CRLF at end
+ // or fVal is empty meaning that the value does not need to be added at all
+ aString.append(fval);
+ }
+ else {
+ // add tag if tagged line
+ if (tagged) {
+ aString.append(linemapP->fHeaderTag);
+ aString+=' '; // this extra space is common usage in RFC822 mails
+ }
+ // add with folding
+ const char *p = fval.c_str();
+ sInt16 n=(*pos)->fNumLines;
+ sInt16 i=0;
+ sInt16 cnt=0; // char counter
+ sInt16 lastLWSP=-1; // no linear whitespace found yet
+ char c;
+ // add multi-line field contents
+ while ((c=*p++)) {
+ if (c=='\r') continue; // ignore CRs
+ if (tagged) {
+ // apply RFC822 folding (65 recommened for old terminals, 72 max)
+ if (cnt>=65) {
+ // check where we can fold
+ if (lastLWSP>=0) {
+ // this is the last LWSP
+ // - new size of line is string beginning with lastLWSP up to end of string
+ cnt=aString.size()-lastLWSP;
+ // - insert a CRLF before the last LWSP
+ aString.insert(lastLWSP,"\x0D\x0A");
+ // - this one is now invalid
+ lastLWSP=-1; // invalidate again
+ }
+ }
+ if (isspace(c)) {
+ // remember possible position for folding
+ lastLWSP=aString.size(); // index of this LWSP
+ }
+ }
+ if (c=='\n') {
+ // line break in data
+ if (tagged) {
+ // for tagged fields, line break in data is used as recommended folding
+ // position, so just fold NOW
+ aString.append("\x0D\x0A ");
+ lastLWSP=-1;
+ cnt=1; // the space is already here
+ }
+ else {
+ // for non-tagged fields, we might cut writing data here if no more lines allowed
+ // - check if more lines allowed
+ if (i<n || n==0) {
+ // one more line allowed
+ aString.append("\x0D\x0A");
+ i++; // count the line
+ }
+ }
+ }
+ else {
+ aString+=c; // append char as is
+ cnt++;
+ }
+ }
+ // one line at least
+ aString.append("\x0D\x0A");
+ }
+ }
+ #ifdef SYDEBUG
+ POBJDEBUGPRINTFX(fItemTypeP->getSession(),DBG_GEN,("Generated:"));
+ if (fItemTypeP->getDbgMask() & DBG_GEN) {
+ // note, do not use debugprintf because string is too long
+ POBJDEBUGPUTSXX(fItemTypeP->getSession(),DBG_GEN+DBG_USERDATA,aString.c_str(),0,true);
+ }
+ #endif
+} // TTextProfileHandler::generateText
+
+
+// parse Data item (includes header and footer)
+bool TTextProfileHandler::parseText(const char *aText, stringSize aTextSize, TMultiFieldItem &aItem)
+{
+ TLineMapList::iterator pos;
+
+ // get options from datastore if one is related
+ if (fRelatedDatastoreP) {
+ fItemSizeLimit = fRelatedDatastoreP->getItemSizeLimit();
+ fNoAttachments = fRelatedDatastoreP->getNoAttachments();
+ }
+ // parse according to linemaps
+ pos=fProfileCfgP->fLineMaps.begin();
+ if (pos==fProfileCfgP->fLineMaps.end()) return true; // simply return, no mappings defined
+ // - we are in header if first is in header
+ bool header=(*pos)->fInHeader;
+ #ifdef EMAIL_FORMAT_SUPPORT
+ fContentType.erase(); // no known type
+ fBoundary.erase(); // no boundary yet
+ fEncoding=enc_8bit; // 8 bit
+ fContentLen=0; // not defined until we have Synthesis-style binary encoding in parts
+ fCharSet=chs_utf8; // UFT-8 is SyncML default
+ fContentType.erase(); // no main content type
+ #endif
+ // - header has tagged fields if first has a tag
+ bool tagged=!TCFG_ISEMPTY((*pos)->fHeaderTag);
+ const char *p = aText;
+ const char *eot = aText+aTextSize;
+ // if we are starting in body, simulate a preceeding EOLN
+ bool lastwaseoln=!header;
+ while (pos!=fProfileCfgP->fLineMaps.end()) {
+ // check special case of this linemap eating all of the remaining body text
+ if (!header && (*pos)->fNumLines==0) {
+ // Optimization: this linemap will receive the entire remainder of the message
+ parseContent(p, eot-p, aItem, *pos);
+ // and we are done
+ goto parsed;
+ }
+ // scan input data
+ string fval;
+ fval.erase();
+ char c=0;
+ sInt16 i=0,n=0;
+ bool assignnow=false;
+ bool fielddone=false;
+ while (p<=eot) {
+ // get char, simulate a NUL if we are at end of text
+ c = (p==eot) ? 0 : *p;
+ p++; // make sure we're over eot now
+ // convert all types of line ends: 0A, 0D0A and 0D are allowed
+ // special 0D0D0A that sometimes happens (CRLF saved through a DOS
+ // linefeed expander) is also detected correctly
+ if (c==0x0D) {
+ // CR, discard LF if one follows
+ if (p<eot && *p==0x0A) {
+ p++; // discard
+ // check if previous char was 0D as well
+ if (lastwaseoln && *(p-3)==0x0D) {
+ // this is the famous 0D0D0A sequence
+ continue; // simply completely ignore it, as previous CR was already detected as line end
+ }
+ }
+ c='\n'; // internal line end char
+ }
+ else if (c==0x0A)
+ c='\n'; // single LF is treated as line end char as well
+ // process now
+ if (c==0 || c=='\n') {
+ // end of input line, if tagged headers, process line but ONLY if it's not an empty line
+ if (header && tagged && !lastwaseoln) {
+ // check if we have the entire line already
+ if (p>=eot || c==0 || (*p!=' ' && *p!='\t')) {
+ // end of text or next line does not begin with space or TAB -> end of header
+ #ifdef EMAIL_FORMAT_SUPPORT
+ if (fProfileCfgP->fMIMEMail) {
+ // check for MIME-content relevant mail headers first
+ if (!checkMimeHeaders(fval.c_str(),fval.size(),fContentType,fEncoding,fContentLen,fCharSet,fBoundary,NULL)) {
+ // check for X-Sync-Limit special header
+ const char *h = fval.c_str();
+ const char *e = h+fval.size();
+ const char *tok;
+ stringSize toksz;
+ h=findToken(h,e,':',tok,toksz);
+ if (*h==':') {
+ // header field
+ ++h;
+ // check name
+ if (strucmp(tok,X_LIMIT_HEADER_NAME,toksz)==0) {
+ // X-Sync-Limit special header
+ h=findToken(h,e,':',tok,toksz);
+ TItemField *limfldP = aItem.getField(fProfileCfgP->fSizeLimitField);
+ if (limfldP)
+ limfldP->setAsString(tok,toksz);
+ }
+ }
+ }
+ }
+ // note that mime headers can still be mapped to fields, so fall through
+ #endif
+ // - search by tag for matching linemap now
+ TLineMapList::iterator tagpos;
+ for (tagpos=fProfileCfgP->fLineMaps.begin();tagpos!=fProfileCfgP->fLineMaps.end();tagpos++) {
+ TCFG_STRING &s = (*tagpos)->fHeaderTag;
+ if ((*tagpos)->fInHeader && !TCFG_ISEMPTY(s)) {
+ if (strucmp(fval.c_str(),TCFG_CSTR(s),TCFG_SIZE(s))==0) {
+ // tag matches, set position to matching linemap
+ pos=tagpos;
+ // remove tag from input data
+ fval.erase(0,TCFG_SIZE(s));
+ // remove leading spaces
+ size_t j=0;
+ while (fval.size()>j && isspace(fval[j])) j++;
+ if (j>0) fval.erase(0,j);
+ // assign value now
+ assignnow=true;
+ break; // break for loop
+ }
+ }
+ } // search for correct map
+ // assignnow is set if we have found a map now, otherwise, header will be ignored
+ fielddone=true; // cause loop exit, but first check for transition from header to body and set lastwaseoln
+ }
+ else {
+ // process eventual folding
+ if (p<eot && c!=0 && isspace(*p)) {
+ // this is a lineend because of folding -> ignore line end and just keep LWSP
+ fval+=*p++; // keep the LWSP
+ lastwaseoln=false; // last was LWSP, not EOLN :-)
+ continue; // just check next one
+ }
+ }
+ }
+ // - check for switch from header to body
+ if (header && lastwaseoln) {
+ // two line ends in succession = end of header
+ header=false;
+ if (tagged) {
+ // find first non-header linemap (pos can be anywhere within header linemaps here
+ while (pos!=fProfileCfgP->fLineMaps.end() && (*pos)->fInHeader) pos++;
+ // but no need to store, as tagged headers have stored already
+ }
+ else {
+ // end of untagged headers
+ // assign what is already accumulated
+ assignnow=true;
+ }
+ tagged=false;
+ // just stop here if no more linemaps
+ if (pos==fProfileCfgP->fLineMaps.end()) {
+ goto parsed; // do not spend time and memory with parsing unneeded data
+ }
+ // important optimization: if the last linemap does not have a line
+ // count restriction, process rest of text without filling it into a string var
+ if ((*pos)->fNumLines==0) {
+ // this linemap will receive the entire remainder of the message
+ parseContent(p, eot-p, aItem, *pos);
+ // and we are done
+ goto parsed;
+ }
+ break;
+ }
+ lastwaseoln=true;
+ // end of input line
+ if (!tagged) {
+ i++; // count line
+ n=(*pos)->fNumLines;
+ if ((i>=n && n!=0) || c==0) {
+ // line count exhausted or end of input text, assign to field now
+ assignnow=true; // assign fval to field now
+ break;
+ }
+ else
+ if (c) fval+='\n'; // multi-line field, eoln if not eostring
+ }
+ } // if end of input line
+ else {
+ // not end of input line
+ lastwaseoln=false;
+ // add to value
+ fval+=c;
+ }
+ if (!c || fielddone) break;
+ }
+ // assign accumulated value
+ if (assignnow) {
+ // assign according to linemap
+ parseContent(fval.c_str(), fval.size(), aItem, *pos);
+ // advance to next map if not in tagged header mode
+ if (!tagged) pos++;
+ }
+ // all parsed, rest of definitions is not relevant, p is invalid
+ if (c==0) break;
+ } // while
+parsed:
+ #ifdef SYDEBUG
+ POBJDEBUGPRINTFX(fItemTypeP->getSession(),DBG_PARSE,("Successfully parsed: "));
+ if (fItemTypeP->getDbgMask() & DBG_PARSE) {
+ // very detailed
+ POBJDEBUGPUTSXX(fItemTypeP->getSession(),DBG_PARSE+DBG_USERDATA+DBG_EXOTIC,aText,0,true);
+ }
+ aItem.debugShowItem(DBG_DATA+DBG_PARSE);
+ #endif
+ return true;
+} // TTextProfileHandler::parseData
+
+
+/* end of TTextProfileHandler implementation */
+
+// eof
diff --git a/src/sysync/textprofile.h b/src/sysync/textprofile.h
new file mode 100755
index 0000000..5569976
--- /dev/null
+++ b/src/sysync/textprofile.h
@@ -0,0 +1,197 @@
+/*
+ * File: textprofile.h
+ *
+ * Author: Lukas Zeller (luz@synthesis.ch)
+ *
+ * TTextProfile
+ * utility class to parse line-by-line type text including RFC822 emails
+ *
+ * Copyright (c) 2002-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ * 2005-07-26 : luz : extracted from textitemtype
+ *
+ */
+
+#ifndef TextProfile_H
+#define TextProfile_H
+
+// includes
+#include "syncitemtype.h"
+#include "multifielditemtype.h"
+
+
+namespace sysync {
+
+typedef enum {
+ vt822_plain, // no conversion at all
+ vt822_timestamp, // RFC2822 timestamp value
+ vt822_body, // email body
+ vt822_rfc2047, // text with RFC2047 MIME message header extensions
+ num822ValueTypes
+} T822ValueType;
+
+
+// line map definition
+class TLineMapDefinition : public TConfigElement {
+ typedef TConfigElement inherited;
+public:
+ // constructor/destructor
+ TLineMapDefinition(TConfigElement *aParentElementP, sInt16 aFid);
+ virtual ~TLineMapDefinition();
+ // properties
+ // - number of lines (0=all)
+ sInt16 fNumLines;
+ // - allow empty
+ bool fAllowEmpty;
+ // - only header lines
+ bool fInHeader;
+ // - tagged header
+ TCFG_STRING fHeaderTag; // must include the separator char (for RFC822 emails, a colon)
+ // - field id to put text into
+ sInt16 fFid;
+ // RFC2822 parsing properties
+ // - type of value
+ T822ValueType fValueType;
+ // - list separator (0=no list)
+ char fListSeparator;
+ // - max number of repeats (items in list), REP_ARRAY=unlimited, for array fields
+ sInt16 fMaxRepeat;
+ sInt16 fRepeatInc;
+ #ifdef OBJECT_FILTERING
+ // - no filterkeyword
+ TCFG_STRING fFilterKeyword;
+ #endif
+ #ifdef CONFIGURABLE_TYPE_SUPPORT
+ // check config elements
+ virtual bool localStartElement(const char *aElementName, const char **aAttributes, sInt32 aLine);
+ #endif
+ virtual void clear();
+}; // TLineMapDefinition
+
+
+// line mapping definitions
+typedef std::list<TLineMapDefinition *> TLineMapList;
+
+// Text based datatype
+class TTextProfileConfig : public TProfileConfig
+{
+ typedef TProfileConfig inherited;
+public:
+ TTextProfileConfig(const char *aElementName, TConfigElement *aParentElementP);
+ virtual ~TTextProfileConfig();
+ // handler factory
+ virtual TProfileHandler *newProfileHandler(TMultiFieldItemType *aItemTypeP);
+ // properties
+ // - Note, field list is parsed here, but is a property of TMultiFieldTypeConfig
+ // - text-line based field definitions
+ TLineMapList fLineMaps;
+ #ifdef EMAIL_FORMAT_SUPPORT
+ // email-specific properties
+ // - if set, MIME mail extra feature according to RFC 2822/2045/2046 will be used
+ bool fMIMEMail;
+ // - field containing body type(s) (for body alternatives, if undefined defaults to text/plain)
+ sInt16 fBodyMIMETypesFid;
+ // - field that contains body count
+ sInt16 fBodyCountFid;
+ // - overall body size limit field
+ sInt16 fSizeLimitField;
+ #ifdef EMAIL_ATTACHMENT_SUPPORT
+ // - max number of attachments (for non-arrays)
+ sInt16 fMaxAttachments;
+ // - field that contains attachment count
+ sInt16 fAttachmentCountFid;
+ // - field base offsets (or array field ID) for attachment components
+ // - attachment MIME types
+ sInt16 fAttachmentMIMETypesFid;
+ // - attachment contents
+ sInt16 fAttachmentContentsFid;
+ // - attachment sizes (in bytes)
+ sInt16 fAttachmentSizesFid;
+ // - attachment (file)names
+ sInt16 fAttachmentNamesFid;
+ #endif
+ #endif
+ // public functions
+ #ifdef HARDCODED_TYPE_SUPPORT
+ TLineMapDefinition *addLineMap(
+ sInt16 aFid, sInt16 aNumLines, bool aAllowEmpty,
+ bool aInHeader=false, const char* aHeaderTag=NULL,
+ T822ValueType aValueType=vt822_plain,
+ char aListSeparator=0, sInt16 aMaxRepeat=1, sInt16 aRepeatInc=1
+ );
+ #endif
+protected:
+ #ifdef CONFIGURABLE_TYPE_SUPPORT
+ // check config elements
+public:
+ virtual bool localStartElement(const char *aElementName, const char **aAttributes, sInt32 aLine);
+protected:
+ virtual void localResolve(bool aLastPass);
+ #endif
+ virtual void clear();
+}; // TTextProfileConfig
+
+
+class TTextProfileHandler : public TProfileHandler
+{
+ typedef TProfileHandler inherited;
+public:
+ // constructor
+ TTextProfileHandler(
+ TTextProfileConfig *aTextProfileCfgP,
+ TMultiFieldItemType *aItemTypeP
+ );
+ // destructor
+ virtual ~TTextProfileHandler();
+ #ifdef OBJECT_FILTERING
+ // filtering
+ // - get field index of given filter expression identifier.
+ virtual sInt16 getFilterIdentifierFieldIndex(const char *aIdentifier, uInt16 aIndex);
+ // - add keywords and property names to filterCap
+ virtual void addFilterCapPropsAndKeywords(SmlPcdataListPtr_t &aFilterKeywords, SmlPcdataListPtr_t &aFilterProps, TTypeVariantDescriptor aVariantDesc, TSyncItemType *aItemTypeP);
+ #endif
+ // generate Text Data (includes header and footer)
+ virtual void generateText(TMultiFieldItem &aItem, string &aString);
+ // parse Data item (includes header and footer)
+ virtual bool parseText(const char *aText, stringSize aTextSize, TMultiFieldItem &aItem);
+private:
+ // Internal routines
+ #ifdef EMAIL_FORMAT_SUPPORT
+ bool generateBody(sInt16 aLevel, TMultiFieldItem &aItem, TLineMapDefinition *aLineMapP, string &aString);
+ cAppCharP parseBody(
+ cAppCharP aText, // body text to parse
+ stringSize aTextSize, // max text to parse
+ cAppCharP aType, TEncodingTypes aEncoding, uInt32 aContentLen, TCharSets aCharSet,
+ TMultiFieldItem &aItem, TLineMapDefinition *aLineMapP,
+ cAppCharP aBoundary // boundary, NULL if none
+ );
+ #endif
+ bool generateContent(TMultiFieldItem &aItem, TLineMapDefinition *aLineMapP, string &aString);
+ bool parseContent(const char *aValue, stringSize aValSize, TMultiFieldItem &aItem, TLineMapDefinition *aLineMapP);
+ // vars
+ TTextProfileConfig *fProfileCfgP; // the text profile config element
+ fieldinteger_t fGeneratedBytes; // number of bytes already generated (for limit checks)
+ bool fLimited; // set if size was actually limited
+ // externally set options
+ bool fNoAttachments;
+ fieldinteger_t fItemSizeLimit;
+ #ifdef EMAIL_FORMAT_SUPPORT
+ sInt16 fExtraParts; // number of extra parts in addition to body
+ sInt16 fBodyAlternatives; // number of body alternatives
+ string fContentType; // main content type
+ uInt32 fContentLen; // content len for Synthesis-style binary parts
+ string fBoundary; // main boundary
+ TEncodingTypes fEncoding; // main encoding
+ TCharSets fCharSet; // main charset
+ #ifdef EMAIL_ATTACHMENT_SUPPORT
+ sInt16 fAttachmentLimit; // limit for attachments in this message
+ #endif
+ #endif
+}; // TTextProfileHandler
+
+
+} // namespace sysync
+
+#endif // TextProfile_H
+
+// eof
diff --git a/src/sysync/timezones.cpp b/src/sysync/timezones.cpp
new file mode 100755
index 0000000..5c5fff7
--- /dev/null
+++ b/src/sysync/timezones.cpp
@@ -0,0 +1,1381 @@
+/*
+ * File: timezones.cpp
+ *
+ * Author: Beat Forster
+ *
+ * Timezones conversion from/to linear time scale.
+ *
+ * Copyright (c) 2004-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ * 2004-04-18 : bfo : initial version
+ *
+ */
+
+
+/* ToDo / open issues
+ *
+ * 1) done 04/04/18 <aISOstr> does not contain TZ info, how to bring it in ? -> new param
+ * 2) 04/04/18 <aOffsetSecs> not yet limited to one day
+ * 3) done 04/04/18 Is 20040101Z a valid 8601 ? (yes it is)
+ * 4) done 04/04/20 Fill in all valid time zones
+ * 5) done 04/04/20 Adapt straight forward conversions Enum <=> Name
+ * 6) done 04/06/14 System time zone calculation for Linux
+ * 7) done 04/06/14 InsertTZ / RemoveTZ implementation
+ * 8) 06/04/12 Offset calculation for more complicated vTZ ("$") records
+ * 9) done 07/02/26 Multi year list support
+ */
+
+// must be first in file, everything above is ignored by MVC compilers
+#include "prefix_file.h"
+
+#define TIMEZONES_INTERNAL 1
+
+// do not import the whole thing to make life easier for standalone apps
+#ifdef FULLY_STANDALONE
+ #include "sysync_globs.h"
+ #include "sysync_debug.h"
+#else
+ #include "sysync.h"
+#endif
+
+#include "lineartime.h"
+#include "timezones.h"
+#include "iso8601.h"
+#include "stringutils.h"
+#include "vtimezone.h"
+
+namespace sysync {
+
+static bool tzcmp( const tz_entry &t, const tz_entry &tzi );
+static bool YearFit( const tz_entry &t, const tz_entry &tzi, GZones* g );
+
+// ---- global structure -----------------------------------------------------------
+
+// %%% this is now a global variable, which is BAD. Unlike a const struct array, which can be put into the
+// code section, this "const" is a run-time generated structure (won't work e.g. for Symbian)
+// We should avoid this and create that object in the app level GZones, and let further GZone objects
+// allow using it (without copying!) - similar mechanism as the old CopyCustomTZFrom() had
+// (see 8742d15b65fecf02db54ce9b7447cee09f913ad0 commit which removed it)
+
+const class tzdata : public std::vector<tz_entry>
+{
+ public:
+ tzdata() {
+ #ifndef NO_BUILTIN_TZ
+ // add the global entries
+ reserve(tctx_numtimezones);
+ for (int i=0; i<tctx_numtimezones; i++) {
+ const tbl_tz_entry &t = tbl_tz[i];
+ if (i > 0 && back().name == t.name) {
+ // the previous entry wasn't really the last of its group,
+ // fix that
+ back().groupEnd = false;
+ }
+ // add new entry, assuming that it terminates its group
+ push_back(tz_entry(
+ t.name, t.bias, t.biasDST, t.ident, t.dynYear,
+ tChange(t.dst.wMonth, t.dst.wDayOfWeek, t.dst.wNth, t.dst.wHour, t.dst.wMinute),
+ tChange(t.std.wMonth, t.std.wDayOfWeek, t.std.wNth, t.std.wHour, t.std.wMinute),
+ true // groupEnd
+ ));
+ }
+ //push_back(tz_entry("unknown", 0, 0, "x", "", tChange( 0, 0,0, 0,0), tChange( 0, 0,0, 0,0))); // 0
+ #endif
+ }
+} tz;
+
+
+// ---------------------------------------------------------------------------------
+// GZones
+
+bool GZones::initialize()
+{
+ bool ok = true;
+ // load system wide definitions.
+ bool nobuiltin = loadSystemZoneDefinitions(this);
+ // %%%% later, we'll load system zones not into each GZones, but only once into
+ // a global list. Then, the return value of loadSystemZoneDefinitions() will
+ // determine if zones from the built-in list should be added or not
+ // %%%% for now, we can't do that yet, built-in zones are always active
+ if (!nobuiltin) {
+ //%%% add entries from tz_table
+ }
+ return ok;
+}
+
+
+bool GZones::matchTZ(const tz_entry &aTZ, timecontext_t &aContext)
+{
+ // keeps track of best match while iterating
+ class comparison : public visitor {
+ /** best solution so far has matching rules */
+ bool fRuleMatch;
+ /** best solution has matching location */
+ bool fLocationMatch;
+ /** best solution so far */
+ timecontext_t fContext;
+
+ /** the time zone we try to match */
+ tz_entry fTZ;
+ /** the TZID we try to match */
+ string fTZID;
+
+ /** the last entry without dynYear, i.e., the main entry of a group */
+ timecontext_t fLeadContext;
+
+ /** time zones */
+ GZones *fG;
+
+ public:
+ comparison(const tz_entry &aTZ, GZones *g) :
+ fRuleMatch(false),
+ fLocationMatch(false),
+ fContext(TCTX_UNKNOWN),
+ fTZID(aTZ.name),
+ fLeadContext(TCTX_UNKNOWN),
+ fG(g)
+ {
+ // prepare information for tzcmp() and YearFit()
+ fTZ.bias = aTZ.bias;
+ fTZ.biasDST = aTZ.biasDST;
+ fTZ.std = aTZ.std;
+ fTZ.dst = aTZ.dst;
+ // setting the year here instead of 'CUR' avoids repeated calls
+ // to MyYear() inside YearFit()
+ int year = MyYear(g);
+ StringObjPrintf(fTZ.dynYear, "%d", year);
+ }
+
+ bool visit(const tz_entry &aTZ, timecontext_t aContext)
+ {
+ bool rule_match = tzcmp(fTZ, aTZ) && YearFit(fTZ, aTZ, fG);
+
+ if (aTZ.dynYear.empty())
+ fLeadContext = aContext;
+
+ if (rule_match &&
+ fTZ.name == aTZ.name) {
+ // name AND rule match => best possible match, return early
+ fContext = fLeadContext;
+ return true;
+ }
+
+ if (!aTZ.location.empty() &&
+ fTZID.find(aTZ.location) != fTZID.npos) {
+ // location name is part of the TZID we try to match
+ if (!fLocationMatch ||
+ (!fRuleMatch && rule_match)) {
+ // previous match did not match location or
+ // not the rules and we do, so this match is better
+ fLocationMatch = true;
+ fRuleMatch = rule_match;
+ fContext = fLeadContext;
+ return false;
+ }
+ }
+
+ // a rule match with no better match yet?
+ if (!fLocationMatch &&
+ rule_match &&
+ !fRuleMatch) {
+ fContext = fLeadContext;
+ fRuleMatch = true;
+ }
+
+ return false;
+ }
+
+ bool result(timecontext_t &aContext)
+ {
+ if (fContext != TCTX_UNKNOWN) {
+ aContext = fContext;
+ return true;
+ } else {
+ return false;
+ }
+ }
+ } c(aTZ, this);
+
+ foreachTZ(c);
+
+ return c.result(aContext);
+}
+
+bool GZones::foreachTZ(visitor &v)
+{
+ int i; // visit hard coded elements first
+ for (i= 1; i<(int)tctx_numtimezones; i++) {
+ if (v.visit(tz[i], TCTX_ENUMCONTEXT(i))) {
+ return true;
+ }
+ }
+
+ bool result = false;
+#ifdef MUTEX_SUPPORT
+ lockMutex(muP);
+#endif
+
+ for (TZList::iterator pos= tzP.begin();
+ pos!=tzP.end();
+ pos++, i++) {
+ if (v.visit(*pos, TCTX_ENUMCONTEXT(i)))
+ break;
+ }
+
+#ifdef MUTEX_SUPPORT
+ unlockMutex(muP);
+#endif
+ return result;
+}
+
+// ---------------------------------------------------------------------------------
+
+// Get signed minute offset
+sInt16 TCTX_MINOFFSET( timecontext_t tctx ) {
+ return (sInt16)(tctx & TCTX_OFFSETMASK );
+} // TCTX_MINOFFSET
+
+
+// Get time zone enum
+TTimeZones TCTX_TZENUM( timecontext_t tctx ) {
+ return (TTimeZones)(tctx & TCTX_OFFSETMASK );
+} // TCTX_TZENUM
+
+
+// Check if <tctx> is a symbolic TZ info
+bool TCTX_IS_TZ( timecontext_t tctx ) {
+ return (tctx & TCTX_SYMBOLIC_TZ)!=0;
+} // TCTX_IS_TZ
+
+
+// Check if <tctx> is a built-in symbolic TZ info
+bool TCTX_IS_BUILTIN( timecontext_t tctx ) {
+ return
+ (tctx & TCTX_SYMBOLIC_TZ) && // is a symbolic time zone
+ (TCTX_TZENUM(tctx)<tctx_numtimezones); // and is in the internal list
+} // TCTX_IS_BUILTIN
+
+
+// Check if <tctx> has TCTX_DATEONLY mode set (but not TIMEONLY - both set means same as both not set)
+bool TCTX_IS_DATEONLY( timecontext_t tctx ) {
+ return (tctx & TCTX_DATEONLY) && !(tctx & TCTX_TIMEONLY);
+} // TCTX_IS_DATEONLY
+
+
+// Check if <tctx> has TCTX_TIMEONLY mode set (but not DATEONLY - both set means same as both not set)
+bool TCTX_IS_TIMEONLY( timecontext_t tctx ) {
+ return (tctx & TCTX_TIMEONLY) && !(tctx & TCTX_DATEONLY);
+} // TCTX_IS_TIMEONLY
+
+
+// Check if <tctx> has TCTX_DURATION mode set
+bool TCTX_IS_DURATION( timecontext_t tctx ) {
+ return ( tctx & TCTX_DURATION )!=0;
+} // TCTX_IS_DURATION
+
+
+// Check if <tctx> is a unknown time zone
+bool TCTX_IS_UNKNOWN( timecontext_t tctx ) {
+ return TCTX_IS_TZ( tctx ) && ( TCTX_TZENUM( tctx )==tctx_tz_unknown );
+} // TCTX_IS_UNKNOWN
+
+
+// Check if <tctx> is the system time zone
+bool TCTX_IS_SYSTEM ( timecontext_t tctx ) {
+ return TCTX_IS_TZ( tctx ) && ( TCTX_TZENUM( tctx )==tctx_tz_system );
+} // TCTX_IS_SYSTEM
+
+
+// Check if <tctx> is the UTC time zone
+bool TCTX_IS_UTC ( timecontext_t tctx ) {
+ return TCTX_IS_TZ( tctx ) && ( TCTX_TZENUM( tctx )==tctx_tz_UTC );
+} // TCTX_IS_SYSTEM
+
+
+//! result is render flags from <aRFlagContext> with zone info from <aZoneContext>
+timecontext_t TCTX_JOIN_RFLAGS_TZ ( timecontext_t aRFlagContext, timecontext_t aZoneContext )
+{
+ return
+ (aRFlagContext & TCTX_RFLAGMASK) |
+ (aZoneContext & ~TCTX_RFLAGMASK);
+} // TCTX_JOIN_RFLAGS_TZ
+
+
+
+// ---- utility functions --------------------------------------------------------------
+// Specific bias string. Unit: hours
+string BiasStr( int bias )
+{
+ char hrs[ 80 ];
+ float f= (float)bias/MinsPerHour;
+
+ if (bias % MinsPerHour == 0) sprintf( hrs,"%3.0f ", f ); // not with .0
+ else if (abs(bias % MinsPerHour)==30) sprintf( hrs,"%5.1f ", f );
+ else sprintf( hrs,"%6.2f ", f );
+
+ return hrs;
+} // BiasStr
+
+
+// Clear the DST info of <t>
+void ClrDST( tz_entry &t )
+{
+ memset( &t.dst, 0, sizeof(t.dst) );
+ memset( &t.std, 0, sizeof(t.std) );
+} // ClrDST
+
+
+// -------------------------------------------------------------------------------------
+// Special cases: <year> = 0, the one w/o dynYear
+// = -1, direct index
+bool GetTZ( timecontext_t aContext, tz_entry &t, GZones* g, int year )
+{
+ if (!TCTX_IS_TZ( aContext )) return false;
+
+ int aTZ= aContext & TCTX_OFFSETMASK;
+ if (aTZ>=0 && aTZ<tctx_numtimezones) {
+ while (true) {
+ t= tz[ aTZ ]; if (t.dynYear.empty() || year==-1) break; // replace it by a non-dynYear
+ if (aTZ==0) break;
+ aTZ--;
+ } // while
+
+ if (year<=0) return true;
+
+ int nx= aTZ+1;
+ while (nx<tctx_numtimezones) { // search for the dynamic year
+ if (t.name != tz[ nx ].name) break; // no or no more
+ t= tz[ nx ];
+ if (year<=atoi( t.dynYear.c_str() )) break; // this is the year line we are looking for
+ nx++;
+ } // while
+
+ return true; // this is it !!
+ } // if
+
+ t= tz[ tctx_tz_unknown ]; // default, if not yet ok
+ if (g==NULL) return false; // If there is no <g>, it is definitely false
+
+ // -----------------------
+ bool ok= false;
+//if (g==NULL) g= gz(); // either <g> or global list
+
+ #ifdef MUTEX_SUPPORT
+ lockMutex( g->muP );
+ #endif
+
+ int i = tctx_numtimezones;
+ TZList::iterator pos;
+ for (pos= g->tzP.begin(); // go thru the additional list
+ pos!=g->tzP.end(); pos++) {
+ if (aTZ==i && // no removed elements !!
+ !(pos->ident=="-")) {
+ t = *pos;
+ ok= true;
+ if (year<=0) break; // pass unlock now
+
+ pos++;
+ while (pos!=g->tzP.end()) { // search for the dynamic year
+ if (t.name != pos->name) break; // no or no more
+ t=*pos;
+ if (year<=atoi( t.dynYear.c_str() )) break; // this is the year line we are looking for
+ pos++;
+ } // while
+
+ break; // pass unlock now
+ } // if
+
+ i++;
+ } // for
+
+ #ifdef MUTEX_SUPPORT
+ unlockMutex( g->muP );
+ #endif
+ // -----------------------
+
+ return ok;
+} /* GetTZ */
+
+
+
+void Get_tChange( lineartime_t tim, tChange &v, bool asDate )
+{
+ sInt16 y, day, d, sec, ms;
+
+ lineartime2date( tim, &y, &v.wMonth, &day );
+ lineartime2time( tim, &v.wHour, &v.wMinute, &sec, &ms );
+
+ if (asDate) {
+ v.wDayOfWeek= -1; // use it as date directly
+ v.wNth = day;
+ }
+ else {
+ v.wDayOfWeek= lineartime2weekday( tim );
+ v.wNth = ( day-1 ) / DaysPerWk + 1;
+
+ if (v.wNth==4) { // the last one within month ?
+ d= day + DaysPerWk;
+ AdjustDay( d, v.wMonth, y );
+ if ( d==day ) v.wNth= 5;
+ } // if
+ } // if
+} // Get_tChange
+
+
+
+static bool Fill_tChange( string iso8601, int bias, int biasDST, tChange &tc, bool isDST )
+{
+ lineartime_t l;
+ timecontext_t c;
+//sInt16 y, day, d, sec, ms;
+
+ string::size_type rslt= ISO8601StrToTimestamp( iso8601.c_str(), l, c );
+ if (rslt!=iso8601.length()) return false;
+
+ int bMins = bias;
+ if (!isDST) bMins+= biasDST;
+ l+= seconds2lineartime( bMins*SecsPerMin );
+
+ Get_tChange( l, tc );
+
+ /*
+ lineartime2date( l, &y, &tc.wMonth, &day );
+ lineartime2time( l, &tc.wHour, &tc.wMinute, &sec, &ms );
+ tc.wDayOfWeek= lineartime2weekday( l );
+ tc.wNth = ( day-1 ) / DaysPerWk + 1;
+
+ if (tc.wNth==4) { // the last one within month ?
+ d= day + DaysPerWk;
+ AdjustDay( d, tc.wMonth, y );
+ if ( d==day ) tc.wNth= 5;
+ } // if
+ */
+
+ return true;
+} // Fill_tChange
+
+
+bool GetTZ( string std, string dst, int bias, int biasDST, tz_entry &t, GZones* g )
+{
+ t.name = "";
+ t.bias = bias;
+ t.biasDST= biasDST;
+ t.ident = "";
+ t.dynYear= "";
+
+ return Fill_tChange( std, bias,biasDST, t.std, false ) &&
+ Fill_tChange( dst, bias,biasDST, t.dst, true );
+} // GetTZ
+
+
+static bool Same_tChange( const tChange &tCh1, const tChange &tCh2 )
+{
+ return tCh1.wMonth ==tCh2.wMonth &&
+ tCh1.wDayOfWeek==tCh2.wDayOfWeek &&
+ tCh1.wNth ==tCh2.wNth &&
+ tCh1.wHour ==tCh2.wHour &&
+ tCh1.wMinute ==tCh2.wMinute;
+} // Same_tChange
+
+
+/*! Compare time zone information */
+static bool tzcmp( const tz_entry &t, const tz_entry &tzi )
+{
+ if (!t.name.empty() &&
+ strucmp( t.name.c_str(), tzi.name.c_str() )!=0) return false;
+
+ bool idN= t.ident.empty();
+ if (!idN && t.ident == "?" && // search for the name only
+ strucmp( t.name.c_str(), tzi.name.c_str() )==0) return true;
+
+ if (!idN && t.ident == "$" &&
+ t.ident == tzi.ident &&
+ strucmp( t.name.c_str(), tzi.name.c_str() )==0) return true;
+
+ /*
+ PNCDEBUGPRINTFX( DBG_SESSION,( "tS m=%d dw=%d n=%d h=%d M=%d\n", t.std.wMonth, t.std.wDayOfWeek, t.std.wNth,
+ t.std.wHour, t.std.wMinute ) );
+ PNCDEBUGPRINTFX( DBG_SESSION,( "tD m=%d dw=%d n=%d h=%d M=%d\n", t.dst.wMonth, t.dst.wDayOfWeek, t.dst.wNth,
+ t.dst.wHour, t.dst.wMinute ) );
+ PNCDEBUGPRINTFX( DBG_SESSION,( "tziS m=%d dw=%d n=%d h=%d M=%d\n", tzi.std.wMonth, tzi.std.wDayOfWeek, tzi.std.wNth,
+ tzi.std.wHour, tzi.std.wMinute ) );
+ PNCDEBUGPRINTFX( DBG_SESSION,( "tziD m=%d dw=%d n=%d h=%d M=%d\n", tzi.dst.wMonth, tzi.dst.wDayOfWeek, tzi.dst.wNth,
+ tzi.dst.wHour, tzi.dst.wMinute ) );
+
+ PNCDEBUGPRINTFX( DBG_SESSION,( "t bs=%d bd=%d\n", t.bias, t.biasDST ) );
+ PNCDEBUGPRINTFX( DBG_SESSION,( "tzi bs=%d bd=%d\n", tzi.bias, tzi.biasDST ) );
+ */
+
+ if (t.bias!=tzi.bias) return false; // bias must be identical
+
+ if (t.ident == "o") {
+ // stop comparing, return result of last check
+ return t.biasDST == tzi.biasDST;
+ }
+
+ bool tIsDst= DSTCond( t );
+ bool tziIsDst= DSTCond( tzi );
+ if (tIsDst!=tziIsDst) return false; // DST cond must be on or off for both
+
+ if (tIsDst) {
+ //if (memcmp(&t.dst, &tzi.dst, sizeof(t.dst))!=0 ||
+ // memcmp(&t.std, &tzi.std, sizeof(t.std))!=0 ||
+ if (!Same_tChange( t.dst, tzi.dst ) ||
+ !Same_tChange( t.std, tzi.std ) ||
+ t.biasDST!=tzi.biasDST) return false;
+ } // if
+
+ return tzi.ident != "-" && // not removed
+ ( idN ||
+ t.ident.empty() ||
+ t.ident == tzi.ident );
+
+ /*
+ return memcmp(&t.dst, &tzi.dst, sizeof(t.dst))==0 &&
+ memcmp(&t.std, &tzi.std, sizeof(t.std))==0 &&
+ t.bias ==tzi.bias &&
+ t.biasDST==tzi.biasDST && //%%% luz: this must be compared as well
+ strcmp( "-", tzi.ident )!=0 && // removed
+ ( idN ||
+ strcmp( t.ident, "" )==0 ||
+ strcmp( t.ident, tzi.ident )==0 );
+ */
+} // tzcmp
+
+
+
+sInt16 MyYear( GZones* g )
+{
+ sInt16 y, m, d;
+
+ lineartime_t t= getSystemNowAs( TCTX_UTC, g, true );
+ lineartime2date( t, &y,&m,&d );
+ return y;
+} // MyYear
+
+
+static bool YearFit( const tz_entry &t, const tz_entry &tzi, GZones* g )
+{
+ int yearS;
+ if (t.dynYear == "CUR") yearS= MyYear( g );
+ else yearS= atoi( t.dynYear.c_str() );
+ if (yearS==0) return true;
+
+ int yearI= atoi( tzi.dynYear.c_str() );
+ if (yearI==0) return true;
+
+ if (tzi.groupEnd) return yearS>=yearI;
+ else return yearS<=yearI;
+} // YearFit
+
+
+
+/* Returns true, if the given TZ is existing already
+ * <t> tz_entry to search for:
+ * If <t.name> == "" search for any entry with these values.
+ * <t.name> != "" name must fit
+ * If <t.ident>== "" search for any entry with these values
+ * <t.ident>!= "" name must fit
+ * <aName> is <t.name> of the found record
+ * <aContext> is the assigned context
+ * <createIt> create an entry, if not yet existing / default: false
+ * <searchOffset> says, where to start searching / default: at the beginning
+ *
+ * supported <t.ident> values:
+ * "" any (to search)
+ * "?" name only (to search)
+ * "o" compare offsets, but not changes (to search); name is compared if set
+ *
+ * "x" unknown/system
+ * "m" military zones
+ * "s" standard zones
+ * "d" daylight zones
+ * "-" removed zones
+ * "$" not converted zones (pure text)
+ * " " all others
+ */
+bool FoundTZ( const tz_entry &tc,
+ string &aName,
+ timecontext_t &aContext, GZones* g, bool createIt,
+ timecontext_t searchOffset )
+{
+ aName = "";
+ aContext = TCTX_UNKNOWN;
+ int offs = TCTX_OFFSCONTEXT( searchOffset );
+ bool ok = false;
+ tz_entry t = tc;
+
+ if (!t.ident.empty() && // specific items will not contain more info
+ t.ident != " ") {
+ ClrDST ( t );
+ } // if
+
+ int i; // search hard coded elements first
+ for (i= offs+1; i<(int)tctx_numtimezones; i++) {
+ const tz_entry &tzi = tz[ i ];
+ if (tzcmp ( t, tzi ) &&
+ YearFit( t, tzi, g )) {
+ aName= tzi.name;
+ ok = true; break;
+ } // if
+ } // for
+
+ // don't go thru the mutex, if not really needed
+ if (!ok && g!=NULL) {
+ // -------------------------------------------
+ //if (g==NULL) g= gz();
+
+ #ifdef MUTEX_SUPPORT
+ lockMutex( g->muP );
+ #endif
+
+ TZList::iterator pos;
+ int j= offs-(int)tctx_numtimezones; // remaining gap to be skipped
+
+ // Search for all not removed elements first
+ for (pos= g->tzP.begin();
+ pos!=g->tzP.end(); pos++) {
+ if (j<0 &&
+ pos->ident != "-" && // element must not be removed
+ tzcmp( t, *pos )) {
+ aName= pos->name;
+ ok = true; break;
+ } // if
+
+ i++;
+ j--;
+ } // for
+
+ // now check, if an already removed element can be reactivated
+ if (createIt && !ok) {
+ i= (int)tctx_numtimezones;
+ j= offs-(int)tctx_numtimezones; // remaining gap to be skipped
+
+ for (pos= g->tzP.begin();
+ pos!=g->tzP.end(); pos++) {
+ if (j<0 &&
+ pos->ident == "-" && // removed element ?
+ tzcmp( t, *pos )) {
+ pos->ident= t.ident; // reactivate the identifier
+ aName = pos->name; // should be the same
+ ok = true; break;
+ } // if
+
+ i++;
+ j--;
+ } // for
+ } // if
+
+ // no such element => must be created
+ if (createIt && !ok) { // create it, if not yet ok
+ g->tzP.push_back( t );
+ ok= true;
+ } // if
+
+ #ifdef MUTEX_SUPPORT
+ unlockMutex( g->muP );
+ #endif
+ // -------------------------------------------
+ } // if
+
+ if (ok) aContext= TCTX_ENUMCONTEXT( i );
+ return aContext!=TCTX_UNKNOWN;
+} /* FoundTZ */
+
+
+
+/* Remove a time zone definition, if already existing
+ * NOTE: Currently only dynamic entries can be removed
+ */
+bool RemoveTZ( const tz_entry &t, GZones* g )
+{
+//printf( "RemoveTZ '%s' %d\n", t.name, t.bias );
+ bool ok= false;
+
+ // ---------------------------
+ if (g==NULL) return ok;
+//if (g==NULL) g= gz();
+
+ #ifdef MUTEX_SUPPORT
+ lockMutex( g->muP );
+ #endif
+
+ TZList::iterator pos;
+ for (pos= g->tzP.begin();
+ pos!=g->tzP.end(); pos++) {
+ if (pos->ident != "-" && // element must not be removed
+ tzcmp( t, *pos )) {
+ pos->ident = "-";
+ //gz()->tzP.erase( pos ); // do not remove it, keep it persistent
+ ok= true; break;
+ } // if
+ } // for
+
+ #ifdef MUTEX_SUPPORT
+ unlockMutex( g->muP );
+ #endif
+ // ---------------------------
+
+ return ok;
+} /* RemoveTZ */
+
+
+
+bool TimeZoneNameToContext( cAppCharP aName, timecontext_t &aContext, GZones* g )
+{
+ // check some special cases
+ if (strucmp(aName,"DATE")==0) {
+ aContext = TCTX_UNKNOWN|TCTX_DATEONLY;
+ return true;
+ }
+ else if (strucmp(aName,"DURATION")==0) {
+ aContext = TCTX_UNKNOWN|TCTX_DURATION;
+ return true;
+ }
+ else if (*aName==0 || strucmp(aName,"FLOATING")==0) {
+ aContext = TCTX_UNKNOWN;
+ return true;
+ }
+
+ const char* GMT= "GMT";
+ const char* UTC= "UTC";
+
+ int v, n;
+ char* q;
+ tz_entry t;
+
+ t.name = aName; // prepare searching
+ t.ident = "?";
+ t.dynYear= ""; // luz: must be initialized!
+
+ string tName;
+ if (FoundTZ( t, tName, aContext, g )) return true;
+
+ /*
+ int i; aContext= TCTX_UNKNOWN;
+ for (i=0; i<(int)tctx_numtimezones; i++) {
+ if (strucmp( tz[i].name,aName )==0) { aContext= TCTX_ENUMCONTEXT(i); break; }
+ } // for
+
+ if (aContext!=TCTX_UNKNOWN) return true;
+ */
+
+ // calculate the UTC offset
+ n= 1; q= (char *)strstr( aName,UTC );
+ if (q==NULL) q= (char *)strstr( aName,GMT );
+ if (q!=NULL && strlen( q )>strlen( UTC )) {
+ q+= strlen( UTC );
+ n= MinsPerHour;
+ }
+ else {
+ q= (char*)aName;
+ } // if
+
+ v= atoi( q );
+ if (v!=0 || strcmp( q, "0" )==0
+ || strcmp( q,"+0" )==0
+ || strcmp( q,"-0" )==0) {
+ aContext= TCTX_OFFSCONTEXT(v*n); return true;
+ } // if
+
+ return false;
+} /* TimeZoneNameToContext */
+
+
+
+bool TimeZoneContextToName( timecontext_t aContext, string &aName, GZones* g,
+ cAppCharP aPrefIdent )
+{
+ // check some special cases
+ if (TCTX_IS_UNKNOWN(aContext)) {
+ aName = TCTX_IS_DURATION(aContext) ? "DURATION" : (TCTX_IS_DATEONLY(aContext) ? "DATE" : "FLOATING");
+ return true;
+ }
+
+ tz_entry t;
+ aName= "UNKNOWN";
+
+ // if aPrefIndent contains "o", this means we'd like to see olson name, if possible
+ // %%% for now, we can return olson for the built-ins only
+ if (TCTX_IS_BUILTIN(aContext) && aPrefIdent && strchr(aPrefIdent, 'o')!=NULL) {
+ #ifndef NO_BUILTIN_TZ
+ // look up in hardcoded table
+ cAppCharP oname = tbl_tz[TCTX_TZENUM(aContext)].olsonName;
+ if (oname) {
+ // we have an olson name for this entry, return it
+ aName = oname;
+ return true;
+ }
+ #endif
+ }
+
+ // %%% <aPrefIdent> has not yet any influence
+ if (TCTX_IS_TZ( aContext ) &&
+ GetTZ( aContext, t, g )) {
+ if (t.ident == "$") return false; // unchanged elements are not yet supported
+ aName= t.name;
+ } // if
+
+ return true;
+} /* TimeZoneContextToName */
+
+
+
+/*! Is it a DST time zone ? (both months must be defined for DST mode) */
+bool DSTCond( const tz_entry &t ) {
+ return (t.dst.wMonth!=0 &&
+ t.std.wMonth!=0);
+} // DSTCond
+
+
+
+/* adjust to day number within month <m>, %% valid till year <y> 2099 */
+void AdjustDay( sInt16 &d, sInt16 m, sInt16 y )
+{
+ while (d>31) d-= DaysPerWk;
+ if (d>30 && (m==4 || m==6 || m==9 || m==11)) d-= DaysPerWk;
+ if (d>29 && m==2) d-= DaysPerWk;
+ if (d>28 && m==2 && (y % 4)!=0) d-= DaysPerWk;
+} // AdjustDay
+
+
+/* Get the day where STD <=> DST switch will be done */
+static sInt16 DaySwitch( lineartime_t aTime, const tChange* c )
+{
+ sInt16 y, m, d, ds;
+ lineartime2date( aTime, &y, &m, &d );
+
+ if (c->wDayOfWeek==-1) {
+ ds= c->wNth;
+ }
+ else {
+ sInt16 wkDay= lineartime2weekday( aTime );
+ ds = ( wkDay + 5*DaysPerWk - ( d-1 ) ) % DaysPerWk; /* wkday of 1st */
+ ds = ( DaysPerWk - ds + c->wDayOfWeek ) % DaysPerWk + 1; /* 1st occurance */
+ ds+= ( c->wNth-1 )*DaysPerWk;
+
+ AdjustDay( ds, c->wMonth, y );
+ } // if
+
+ return ds;
+} // DaySwitch
+
+
+/*! Get lineartime_t of <t> for a given <year>, either from std <toDST> or vice versa */
+lineartime_t DST_Switch( const tz_entry &t, int bias, sInt16 aYear, bool toDST )
+{
+ sInt16 ds;
+
+ const tChange* c;
+ if (toDST) c= &t.dst;
+ else c= &t.std;
+
+ lineartime_t tim= date2lineartime( aYear, c->wMonth, 1 );
+
+ ds= DaySwitch( tim, c );
+
+ sInt32 bMins = t.bias;
+ if (!toDST) bMins+= t.biasDST;
+
+ return date2lineartime( aYear, c->wMonth, ds )
+ + time2lineartime( c->wHour, c->wMinute, 0,0 )
+ - seconds2lineartime( bMins*SecsPerMin );
+} /* DST_Switch */
+
+
+
+/* Check whether <aValue> of time zone <t> is DST based */
+static bool IsDST( lineartime_t aTime, const tz_entry &t )
+{
+ bool ok;
+ sInt16 y, m, d, h, min, ds;
+
+ if (!DSTCond( t )) return false;
+
+ lineartime2date( aTime, &y, &m, &d );
+ lineartime2time( aTime, &h, &min, NULL, NULL );
+
+ const tChange* c= NULL;
+ if (m==t.std.wMonth) c= &t.std;
+ if (m==t.dst.wMonth) c= &t.dst;
+
+ /* calculation is a little bit more tricky within the two switching months */
+ if (c!=NULL) {
+ /*
+ if (c->wDayOfWeek==-1) {
+ ds= c->wNth;
+ }
+ else {
+ sInt16 wkDay= lineartime2weekday( aTime );
+ ds = ( wkDay + 5*DaysPerWk - (d-1) ) % DaysPerWk; // wkday of 1st
+ ds = ( DaysPerWk - ds + c->wDayOfWeek ) % DaysPerWk + 1; // 1st occurance
+ ds+= ( c->wNth-1 )*DaysPerWk;
+
+ AdjustDay( ds, m, y );
+ } // if
+ */
+
+ /* <ds> is the day when dst<=>std takes place */
+ ds= DaySwitch( aTime, c );
+ if (ds < d ) return c==&t.dst;
+ if (ds > d ) return c==&t.std;
+
+ /* day correct => compare hours */
+ if (c->wHour < h ) return c==&t.dst;
+ if (c->wHour > h ) return c==&t.std;
+
+ /* hour correct => compare minutes */
+ if (c->wMinute< min ) return c==&t.dst;
+ if (c->wMinute> min ) return c==&t.std;
+
+ /* decide for the margin, if identical */
+ return c==&t.dst;
+ } /* if */
+
+ /* northern and southern hemnisphere supported */
+ if (t.dst.wMonth<t.std.wMonth)
+ ok= m>t.dst.wMonth && m<t.std.wMonth;
+ else ok= m<t.std.wMonth || m>t.dst.wMonth;
+ return ok;
+} /* IsDST */
+
+
+
+static sInt32 DST_Offs( lineartime_t aValue, const tz_entry &t, bool backwards )
+{
+ if (backwards) aValue+= (lineartime_t)(t.bias*SecsPerMin)*secondToLinearTimeFactor;
+ if (IsDST( aValue,t )) return t.bias + t.biasDST;
+ else return t.bias;
+} /* DST_Offs */
+
+
+
+/* get offset in minutes */
+static bool TimeZoneToOffs( lineartime_t aValue, timecontext_t aContext,
+ bool backwards, sInt32 &offs, GZones* g )
+{
+ tz_entry t;
+
+ if (TCTX_IS_TZ( aContext )) {
+ offs= 0; // default
+
+ sInt16 year;
+ lineartime2date( aValue, &year, NULL, NULL );
+ if (GetTZ( aContext, t, g, year )) {
+ if (t.ident == "$") return false; // unchanged elements are not yet supported
+ offs= DST_Offs( aValue, t, backwards );
+ } // if
+ }
+ else {
+ offs= TCTX_MINOFFSET(aContext);
+ } // if
+
+ return true;
+} /* TimeZoneToOffs */
+
+
+
+
+timecontext_t SelectTZ( TDaylightSavingZone zone, int bias, int biasDST, lineartime_t tNow, bool isDbg )
+{
+ bool dst, ok;
+ bool withDST= zone!=EDstNone;
+ timecontext_t t= tctx_tz_unknown;
+ bool special= false; // eventually needed true for NGage
+
+ int i; // go thru the whole list of time zones
+ for (i=(int)tctx_tz_system+1; i<(int)tctx_numtimezones; i++) {
+ bool tCond= DSTCond( tz[ i ] ); // are there any DST rules ?
+ if (tCond==withDST) {
+ if (withDST) dst= IsDST( tNow, tz[ i ] ); // check, if now in DST
+ else dst= false;
+
+ int b= bias;
+ if (dst && special) b= bias - biasDST;
+ if (tz[ i ].bias==b) { // the bias must fit exactly
+ switch (zone) {
+ case EDstEuropean :
+ case EDstNorthern : ok= tz[ i ].dst.wMonth<tz[ i ].std.wMonth; break;
+ case EDstSouthern : ok= tz[ i ].dst.wMonth>tz[ i ].std.wMonth; break;
+ default : ok= true;
+ } // switch
+
+ if (isDbg) PNCDEBUGPRINTFX( DBG_SESSION,( " %d %d dst=%d cond=%d '%s'",
+ i, bias, dst, tCond, tz[ i ].name.c_str() ));
+ if (ok) { // check, if european time zone
+ ok= (zone==EDstEuropean) ==
+ (tz[ i ].name.find("Europe") != string::npos ||
+ tz[ i ].name == "CET/CEST" ||
+ tz[ i ].name == "Romance" ||
+ tz[ i ].name == "GMT");
+ if (ok) { t= i; break; }
+ } // if
+ } // if
+ } // if
+ } // for
+
+ if (isDbg) PNCDEBUGPRINTFX( DBG_SESSION,( "SelectTZ: zone=%s bias=%d",
+ tz[ t ].name.c_str(), bias ) );
+ return TCTX_SYMBOLIC_TZ+t;
+} // SelectTZ
+
+
+
+/* %%% luz 2009-04-02 seems of no relevance except for Symbian/Epoc -> Epoc code moved to Symbian/platform_timezones.cpp.
+
+ Note: added a replacement using MyContext which should return the same result (but not
+ the debug output, as SystemTZ is still being called from sysytest.
+
+timecontext_t SystemTZ( GZones *g, bool isDbg )
+{
+ TDaylightSavingZone zone= EDstNone;
+ int bias= 0;
+ lineartime_t tNow = 0;
+
+ #ifdef __EPOC_OS__
+ TLocale tl;
+ zone= tl.HomeDaylightSavingZone();
+ TTimeIntervalSeconds o= tl.UniversalTimeOffset();
+ bias= o.Int() / SecsPerMin; // bias is based on minutes
+ bool isDst= tl.QueryHomeHasDaylightSavingOn();
+ isDbg= true;
+
+ const char* zs;
+ switch (zone) {
+ case EDstHome : zs= "EDstHome"; break;
+ case EDstNone : zs= "EDstNone"; break;
+ case EDstEuropean : zs= "EDstEuropean"; break;
+ case EDstNorthern : zs= "EDstNorthern"; break;
+ case EDstSouthern : zs= "EDstSouthern"; break;
+ default : zs= "???"; break;
+ } // switch
+
+ PNCDEBUGPRINTFX( DBG_SESSION,( "SystemTZ (EPOC): %s %d dst=%s", zs,bias, isDst ? "on":"off" ));
+
+ #ifdef _WIN32
+ PNCDEBUGPRINTFX( DBG_SESSION,( "SystemTZ (EPOC): on emulator" ));
+ #endif
+ #endif
+
+ #if defined _WIN32 && !defined __EPOC_OS__
+ TIME_ZONE_INFORMATION tzi;
+ DWORD rslt= GetTimeZoneInformation( &tzi );
+ zone= EDstEuropean;
+ bias= -tzi.Bias; // as negative value
+ PNCDEBUGPRINTFX( DBG_SESSION,( "SystemTZ (WIN): %s %d", "", bias ) );
+ #endif
+
+ // is only dependendent on current time/date, if any DST zone
+ if (zone!=EDstNone) tNow= getSystemNowAs(TCTX_SYSTEM, g);
+ return SelectTZ( zone,bias,tNow, isDbg );
+} // SystemTZ
+*/
+
+
+// Get int value <i> as string
+static string IntStr( sInt32 i )
+{
+ const int FLen= 15; /* max length of (internal) item name */
+ char f[ FLen ];
+ // cheating: this printf format assumes that sInt32 == int
+ sprintf ( f, "%d", int(i) );
+ string s= f;
+ return s;
+} // IntStr
+
+
+/* get system's time zone */
+static bool MyContext( timecontext_t &aContext, GZones* g )
+{
+ bool isDbg= false;
+ // check for cached system time zone, return it if available
+ if (g) {
+ isDbg= g->isDbg;
+ // luz: added safety here to avoid that incorrectly initialized GZone
+ // (with tctx_tz_unknown instead of TCTX_UNKNOWN, as it was in timezones.h)
+ // does not cause that system timezone is assumed known (as UTC) and returned WRONG!)
+ // Note: rearranged to make non-locked writing to g->sysTZ is safe (for hacks needed pre-3.1.2.x!)
+ timecontext_t curSysTZ= g->sysTZ;
+ if (!(TCTX_IS_UNKNOWN( curSysTZ ) ||
+ TCTX_IS_SYSTEM ( curSysTZ )
+ )) { aContext= curSysTZ; return true; }
+ } // if
+
+ // there is no system time zone cached, we need to determine it from the operating system
+ // - call platform specific routine
+ bool ok = getSystemTimeZoneContext( aContext, g );
+
+ if (isDbg) PNCDEBUGPRINTFX( DBG_SESSION, ( "MyContext: %08X ok=%d", aContext, ok ));
+
+ // update cached system context
+ if (g && ok) g->sysTZ= aContext; // assign the system context
+ return ok;
+} /* MyContext */
+
+
+/* %%% luz 2009-04-02 seems of no relevance except for Symbian/Epoc -> Epoc code moved to Symbian/platform_timezones.cpp
+ added a replacement here as it is still used from sysytest, which should return the same result (but not
+ the debug output */
+timecontext_t SystemTZ( GZones *g, bool isDbg )
+{
+ timecontext_t tctx;
+ if (MyContext(tctx, g))
+ return tctx;
+ else
+ return TCTX_UNKNOWN;
+}
+
+
+
+bool ContextForEntry( timecontext_t &aContext, tz_entry &t, bool chkNameFirst, GZones* g )
+{
+ string s;
+ string sName = t.name;
+ bool ok = true;
+ do {
+ if (chkNameFirst && !sName.empty() &&
+ TimeZoneNameToContext( sName.c_str(),aContext, g )) break;
+
+ // if there are no rules defined => switch it off
+ if (t.dst.wMonth==0 ||
+ t.std.wMonth==0 ||
+ Same_tChange( t.dst, t.std )) {
+ ClrDST( t );
+ } // if
+
+ t.name = (char*)sName.c_str();
+ t.ident = "";
+ t.dynYear= ""; // MUST be set as this is assigned unchecked to a string
+ // (which crashes when assigned NULL or low number)
+ if (FoundTZ( t, s, aContext, g )) break; // with this name
+
+ t.name = "";
+ t.dynYear= "CUR";
+ if (FoundTZ( t, s, aContext, g )) break; // with different name
+
+ int i= 0;
+ if (sName.empty()) sName= "unassigned";
+
+ string v;
+ while (true) {
+ v = sName.c_str();
+ if (i>0) v+= "_" + IntStr( i );
+ if (!TimeZoneNameToContext( v.c_str(),aContext, g )) break; // A timezone with this name must not exist
+ i++;
+ } // while
+
+ t.name= (char*)v.c_str();
+
+ string tz_Name;
+ FoundTZ( t, tz_Name, aContext, g, true ); // create it
+ if (TimeZoneNameToContext( v.c_str(),aContext, g )) break;
+ ok= false;
+
+ /*
+ t.name= (char*)sName.c_str();
+
+ string tzName;
+ FoundTZ( t, tzName, aContext, g, true ); // create it
+ if (TimeZoneNameToContext( sName.c_str(),aContext, g )) break;
+ ok= false;
+ */
+ } while (false);
+ return ok;
+} /* ContextFromNameAndEntry */
+
+
+
+/* returns true, if the context rules are identical */
+static bool IdenticalRules( timecontext_t aSourceContext,
+ timecontext_t aTargetContext, GZones* g )
+{
+ tz_entry ts, tt;
+
+ /* only performed in TZ mode */
+ if (!GetTZ( aSourceContext, ts, g, 0 ) ||
+ !GetTZ( aTargetContext, tt, g, 0 )) return false;
+
+ TTimeZones asTZ= TCTX_TZENUM( aSourceContext );
+ TTimeZones atTZ= TCTX_TZENUM( aTargetContext );
+ if (asTZ==atTZ) return true; // identical
+
+ if (ts.ident == "$" ||
+ tt.ident == "$") return false;
+
+ if (ts.bias==tt.bias &&
+ // memcmp( &ts.dst, &tt.dst, sizeof(tChange))==0 &&
+ // memcmp( &ts.std, &tt.std, sizeof(tChange))==0) return true;
+ Same_tChange( ts.dst, tt.dst ) &&
+ Same_tChange( ts.std, tt.std )) return true;
+
+ /*
+ if (!TCTX_IS_TZ( aSourceContext ) ||
+ !TCTX_IS_TZ( aTargetContext )) return false;
+
+ // get the time zones for calculation
+ TTimeZones asTZ= TCTX_TZENUM(aSourceContext);
+ TTimeZones atTZ= TCTX_TZENUM(aTargetContext);
+
+ if (asTZ==atTZ) return true;
+
+ if ( tz[ asTZ ].bias==tz[ atTZ ].bias &&
+ memcmp( &tz[ asTZ ].dst, &tz[ atTZ ].dst, sizeof(tChange))==0 &&
+ memcmp( &tz[ asTZ ].std, &tz[ atTZ ].std, sizeof(tChange))==0) {
+ return true;
+ } // if
+ */
+
+ return false;
+} /* IdenticalRules */
+
+
+
+/*! get system's time zone context (i.e. resolve the TCTX_SYSTEM meta-context)
+ * @param[in,out] aContext : context will be made non-meta, that is, if input is TCTX_SYSTEM,
+ * actual time zone will be determined.
+ */
+bool TzResolveMetaContext( timecontext_t &aContext, GZones* g )
+{
+ if (!TCTX_IS_SYSTEM(aContext))
+ return true; // no meta zone, just return unmodified
+ // is meta-context TCTX_SYSTEM, determine actual symbolic context
+ return MyContext(aContext,g);
+} // TzResolveMetaContext
+
+
+/* make time context non-symbolic (= calculate minute offset east of UTC for aRefTime) */
+bool TzResolveContext( timecontext_t &aContext, lineartime_t aRefTime, bool aRefTimeUTC, GZones* g )
+{
+ sInt32 offs;
+ // resolve eventual meta context (TCTX_SYSTEM at this time)
+ if (!TzResolveMetaContext(aContext,g)) return false;
+ // check if already an offset (non-symbolic)
+ if (!TCTX_IS_TZ(aContext))
+ return true; // yes, no conversion needed
+ // is symbolic, needs conversion to offset
+ bool ok = TimeZoneToOffs(
+ aRefTime, // reference time for which we want to know the offset
+ aContext, // context
+ aRefTimeUTC, // "backwards" means refTime is UTC and we want know offset to go back to local
+ offs, // here we get the offset
+ g
+ );
+ if (ok) {
+ aContext = TCTX_JOIN_RFLAGS_TZ(
+ aContext, // join original rendering flags...
+ TCTX_OFFSCONTEXT(offs) // ...with new offset based context
+ );
+ }
+ return ok;
+} // TzResolveContext
+
+
+/*! calculate minute offset east of UTC for aRefTime
+ * @param[in] aContext : time context to resolve
+ * @param[out] aMinuteOffset : receives minute offset east of UTC
+ * @param[in] aRefTime : reference time point for resolving the offset
+ * @param[in] aRefTimeUTC : if set, reference time must be UTC,
+ * otherwise, reference time must be in context of aContext
+ */
+bool TzResolveToOffset( timecontext_t aContext, sInt16 &aMinuteOffset, lineartime_t aRefTime, bool aRefTimeUTC, GZones* g )
+{
+ bool ok = TzResolveContext(aContext, aRefTime, aRefTimeUTC, g);
+ if (ok) {
+ aMinuteOffset = TCTX_MINOFFSET(aContext);
+ }
+ return ok;
+} // TzResolveToOffset
+
+
+
+/*! Offset between two contexts (in seconds)
+ * Complex time zones (type "$") can't be currently resolved, they return false
+ * @param[in] aSourceValue : reference time for which the offset should be calculated
+ * @param[in] aSourceContext : source time zone context
+ * @param[in] aTargetContext : source time zone context
+ * @param[out] sDiff : receives offset east of UTC in seconds
+ */
+bool TzOffsetSeconds( lineartime_t aSourceValue, timecontext_t aSourceContext,
+ timecontext_t aTargetContext,
+ sInt32 &sDiff, GZones* g,
+ timecontext_t aDefaultContext )
+{
+ bool sSys, tSys,
+ sUnk, tUnk;
+ sInt32 sOffs= 0, tOffs= 0; // initialize them for sure
+ lineartime_t aTargetValue;
+
+ bool ok= true;
+ bool isDbg= false;
+ if (g) isDbg= g->isDbg;
+
+ // set default context for unknown zones
+ if (TCTX_IS_UNKNOWN(aSourceContext)) aSourceContext=aDefaultContext;
+ if (TCTX_IS_UNKNOWN(aTargetContext)) aTargetContext=aDefaultContext;
+
+ do {
+ sDiff= 0;
+ if (aSourceContext==aTargetContext) break; /* no conversion, if identical context */
+
+ /* both unknown or system is still ok */
+ if (TCTX_IS_UNKNOWN( aSourceContext ) &&
+ TCTX_IS_UNKNOWN( aTargetContext )) break;
+ sSys= TCTX_IS_SYSTEM ( aSourceContext );
+ tSys= TCTX_IS_SYSTEM ( aTargetContext ); if (sSys && tSys) break;
+
+ /* calculate specifically for the system's time zone */
+ if (sSys) MyContext( aSourceContext, g );
+ if (tSys) MyContext( aTargetContext, g );
+
+ /* if both are unknown now, then it's good as well */
+ sUnk= TCTX_IS_UNKNOWN( aSourceContext );
+ tUnk= TCTX_IS_UNKNOWN( aTargetContext ); if (sUnk && tUnk) break;
+ /* this case can't be resolved: */ if (sUnk || tUnk) { ok= false; break; }
+
+ if (IdenticalRules( aSourceContext,aTargetContext, g )) break;
+
+ /* now do the "hard" things */
+ if (!TimeZoneToOffs( aSourceValue, aSourceContext, false, sOffs, g )) return false;
+ aTargetValue= aSourceValue
+ - (lineartime_t)(sOffs*SecsPerMin)*secondToLinearTimeFactor;
+ if (!TimeZoneToOffs( aTargetValue, aTargetContext, true, tOffs, g )) return false;
+
+ sDiff= ( tOffs - sOffs )*SecsPerMin;
+ } while (false);
+
+ if (isDbg) {
+ PNCDEBUGPRINTFX( DBG_SESSION,( "sSys=%d tSys=%d / sUnk=%d tUnk=%d / sOffs=%d tOffs=%d",
+ sSys,tSys, sUnk,tUnk, sOffs,tOffs ));
+ PNCDEBUGPRINTFX( DBG_SESSION,( "TzOffsetSeconds=%d", sDiff ));
+ } // if
+
+ return ok;
+} /* TzOffsetSeconds */
+
+
+/*! Converts timestamp value from one zone to another
+ * Complex time zones (type "$") can't be currently resolved, they return false
+ * @param[in/out] aValue : will be converted from source context to target context. If==noLinearTime==0, no conversion is done
+ * @param[in] aSourceContext : source time zone context
+ * @param[in] aTargetContext : source time zone context
+ * @param[in] aDefaultContext: default context to use if source or target is TCTX_UNKNOWN
+ */
+bool TzConvertTimestamp( lineartime_t &aValue, timecontext_t aSourceContext,
+ timecontext_t aTargetContext,
+ GZones* g,
+ timecontext_t aDefaultContext )
+{
+ sInt32 sdiff;
+ if (aValue==noLinearTime) return true; // no time, don't convert
+ bool ok = TzOffsetSeconds( aValue, aSourceContext, aTargetContext, sdiff, g, aDefaultContext );
+ if (ok)
+ aValue += (lineartime_t)(sdiff)*secondToLinearTimeFactor;
+ return ok;
+} /* TzConvertTimestamp */
+
+
+} // namespace sysync
+
+
+/* eof */
+
diff --git a/src/sysync/timezones.h b/src/sysync/timezones.h
new file mode 100755
index 0000000..f74c5a5
--- /dev/null
+++ b/src/sysync/timezones.h
@@ -0,0 +1,464 @@
+/*
+ * File: timezones.h
+ *
+ * Author: Beat Forster
+ *
+ * Timezones conversion from/to linear time scale.
+ *
+ * Copyright (c) 2004-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ * 2004-04-18 : bfo : initial version
+ *
+ */
+
+#ifndef TIMEZONES_H
+#define TIMEZONES_H
+
+#include <string>
+#include <list>
+
+#include "lineartime.h"
+#include "debuglogger.h"
+
+#ifdef MUTEX_SUPPORT
+ #include "platform_mutex.h"
+#endif
+
+
+using namespace std; // for string and list
+
+namespace sysync {
+
+// include the built-in time zone table
+#ifndef NO_BUILTIN_TZ
+ #include "tz_table.h"
+#endif
+
+/// time zone table entry definition
+typedef struct tChangeStruct {
+ short wMonth;
+ short wDayOfWeek;
+ short wNth; // nth occurance
+ short wHour;
+ short wMinute;
+
+ tChangeStruct() :
+ wMonth(0),
+ wDayOfWeek(0),
+ wNth(0),
+ wHour(0),
+ wMinute(0)
+ {}
+
+ tChangeStruct(short aMonth,
+ short aDayOfWeek,
+ short aNth,
+ short aHour,
+ short aMinute) :
+ wMonth(aMonth),
+ wDayOfWeek(aDayOfWeek),
+ wNth(aNth),
+ wHour(aHour),
+ wMinute(aMinute)
+ {}
+ } tChange;
+
+
+class tz_entry {
+ public:
+ std::string name; /**< name, same as TZID in VTIMEZONE, e.g. CET/CEST or /softwarestudio.org/Tzfile/Europe/Berlin */
+ std::string location; /**< location string as used in Olson TZID, e.g. Europe/Berlin */
+ short bias; /**< minutes difference to UTC (west negative, east positive */
+ short biasDST; /**< minutes difference to bias (not UTC!) */
+ std::string ident; /**< see FoundTZ() */
+ std::string dynYear; /**< if this time zone rule is assigned to a specific year range */
+ tChange dst; /**< describes when daylight saving time will become active */
+ tChange std; /**< describes when standard time will become active */
+ bool groupEnd; /**< true if the entry which follows this one belongs to a different zone or last entry */
+
+ tz_entry() :
+ bias(0),
+ biasDST(0),
+ groupEnd(true)
+ {}
+
+ tz_entry( const std::string &aName,
+ short aBias,
+ short aBiasDST,
+ const std::string &aIdent,
+ const std::string &aDynYear,
+ const tChange &aDst,
+ const tChange &aStd,
+ bool aGroupEnd ) :
+ name(aName),
+ bias(aBias),
+ biasDST(aBiasDST),
+ ident(aIdent),
+ dynYear(aDynYear),
+ dst(aDst),
+ std(aStd),
+ groupEnd(aGroupEnd)
+ {}
+};
+
+
+
+
+
+//const int DST_Bias= 60; // General DST offset (in minutes)
+
+// Symbian timezone categories
+// visible for all other systems as well
+#ifndef __EPOC_OS__
+ enum TDaylightSavingZone {
+ EDstHome =0x40000000,
+ EDstNone =0,
+ EDstEuropean=1,
+ EDstNorthern=2,
+ EDstSouthern=4
+ };
+#endif
+
+
+// Offset mask
+const uInt32 TCTX_OFFSETMASK = 0x0000FFFF;
+
+// time context flags
+// - symbolic zone flag
+const uInt32 TCTX_SYMBOLIC_TZ= 0x00010000;
+// - rendering flags
+const uInt32 TCTX_DATEONLY = 0x00020000;
+const uInt32 TCTX_TIMEONLY = 0x00040000;
+const uInt32 TCTX_DURATION = 0x00080000;
+
+const uInt32 TCTX_RFLAGMASK = TCTX_DATEONLY+TCTX_TIMEONLY+TCTX_DURATION;
+
+//! Get signed minute offset
+sInt16 TCTX_MINOFFSET( timecontext_t tctx );
+
+//! Get time zone enum
+TTimeZones TCTX_TZENUM( timecontext_t tctx );
+
+
+// macro to get time zone context
+#define TCTX_ENUMCONTEXT( tzenum ) ((timecontext_t) ((tzenum) | TCTX_SYMBOLIC_TZ))
+#define TCTX_UNKNOWN TCTX_ENUMCONTEXT( tctx_tz_unknown )
+#define TCTX_SYSTEM TCTX_ENUMCONTEXT( tctx_tz_system )
+#define TCTX_UTC TCTX_ENUMCONTEXT( tctx_tz_UTC )
+#define TCTX_OFFSCONTEXT( offs ) ((timecontext_t)((sInt16)(offs) & TCTX_OFFSETMASK ))
+
+
+/*
+// macro to get time zone and other flags
+#define TCTX_IS_TZ( tctx ) ((bool)(tctx & TCTX_SYMBOLIC_TZ))
+#define TCTX_IS_DATEONLY( tctx ) ((bool)(tctx & TCTX_DATEONLY ))
+*/
+
+//! Check if <tctx> is a symbolic TZ info
+bool TCTX_IS_TZ ( timecontext_t tctx );
+
+// Check if <tctx> is a built-in symbolic TZ info
+bool TCTX_IS_BUILTIN ( timecontext_t tctx );
+
+//! Check if <tctx> has TCTX_DATEONLY mode set
+bool TCTX_IS_DATEONLY( timecontext_t tctx );
+
+//! Check if <tctx> has TCTX_TIMEONLY mode set
+bool TCTX_IS_TIMEONLY( timecontext_t tctx );
+
+//! Check if <tctx> has TCTX_DURATION mode set
+bool TCTX_IS_DURATION( timecontext_t tctx );
+
+//! Check if <tctx> is a unknown time zone
+bool TCTX_IS_UNKNOWN ( timecontext_t tctx );
+
+//! Check if <tctx> is the system time zone
+bool TCTX_IS_SYSTEM ( timecontext_t tctx );
+
+//! Check if <tctx> is the UTC time zone
+bool TCTX_IS_UTC ( timecontext_t tctx );
+
+//! result is render flags from <aRFlagContext> with zone info from <aZoneContext>
+timecontext_t TCTX_JOIN_RFLAGS_TZ ( timecontext_t aRFlagContext, timecontext_t aZoneContext );
+
+
+
+// ---- utility functions -------------------------------------------------------------
+/*! Specific bias string. Unit: hours */
+string BiasStr( int bias );
+
+/*! Clear the DST info of <t> */
+void ClrDST( tz_entry &t );
+
+
+typedef std::list<tz_entry> TZList;
+
+class GZones {
+ public:
+ GZones() {
+ #ifdef MUTEX_SUPPORT
+ muP= newMutex();
+ #endif
+
+ predefinedSysTZ= TCTX_UNKNOWN; // no predefined system time zone
+ sysTZ= predefinedSysTZ; // default to predefined zone, if none, this will be obtained from OS APIs
+ isDbg= false; // !!! IMPORTANT: do NOT enable this except for test targets, as it leads to recursions (debugPrintf calls time routines!)
+
+ #ifdef SYDEBUG
+ getDbgMask = 0;
+ getDbgLogger= NULL;
+ #endif
+ } // constructor
+
+ /*! @brief populate GZones with system information
+ *
+ * Sets predefinedSysTZ, sysTZ and adds time zones
+ * to tzP, if that information can be found on the
+ * system.
+ *
+ * Returns false in case of a fatal error.
+ */
+ bool initialize();
+
+ /*! @brief find a matching time zone
+ *
+ * This returns the best match, with "better" defined as (best
+ * match first):
+ * - exact name AND exact rule set match
+ * - existing entry has a location (currently only the case for
+ * time zones imported from libical) AND that location is part of the
+ * name being searched for AND the rule matches
+ * - as before, but with the rule match
+ * - exact rule set match
+ *
+ * If there are multiple entries which are equally good, the first one
+ * is returned.
+ *
+ * When doing rule matching, the dynYear value of an existing
+ * entry has to match the current year as implemented by the
+ * FitYear() function in timezone.cpp. In most (all?) cases this
+ * has the effect that historic rules are skipped. Likewise,
+ * VTIMEZONE information sent out is based on the current rules
+ * for the zone, regardless of when the event itself takes place.
+ *
+ * This is approach is intentional: it helps avoid mismatches
+ * when historic rules of one zone match the current rules of
+ * another. Furthermore, it helps peers which do rule-based
+ * matching and only know the current rules of each zone.
+ *
+ * @param aTZ entry with rules and name set; ident is ignored
+ * @retval aContext the matching time zone context ID; it always
+ * refers to the tz_entry without a dynYear
+ * @return true if match found
+ */
+ bool matchTZ(const tz_entry &aTZ, timecontext_t &aContext);
+
+ class visitor {
+ public:
+ /** @return true to stop iterating */
+ virtual bool visit(const tz_entry &aTZ, timecontext_t aContext) = 0;
+ };
+
+ /*! @brief invoke visitor once for each time zone
+ * @return true if the visitor returned true
+ */
+ bool foreachTZ(visitor &v);
+
+ void ResetCache(void) {
+ sysTZ= predefinedSysTZ; // reset cached system time zone to make sure it is re-evaluated
+ }
+
+ #ifdef MUTEX_SUPPORT
+ #endif
+
+ TZList tzP; // the list of additional time zones
+ timecontext_t predefinedSysTZ; // can be set to a specific zone to override zone returned by OS API
+ timecontext_t sysTZ; // the system's time zone, will be calculated,
+ // if set to tctx_tz_unknown
+ bool isDbg; // write debug information
+
+ #ifdef SYDEBUG
+ uInt32 getDbgMask; // allow debugging in a specific context
+ TDebugLogger* getDbgLogger;
+ #endif
+}; // GZones
+
+
+
+// visible for debugging only
+timecontext_t SystemTZ( GZones *g, bool isDbg= false );
+timecontext_t SelectTZ( TDaylightSavingZone zone, int bias, int biasDST, lineartime_t tNow,
+ bool isDbg= false );
+
+// visible for platform_timezones.cpp/.mm
+bool ContextForEntry( timecontext_t &aContext, tz_entry &t, bool chkNameFirst, GZones* g );
+void Get_tChange( lineartime_t tim, tChange &v, bool asDate= false );
+
+
+
+/*! Get <tz_entry> from <aContext>
+ * or <std>/<dst>
+ */
+bool GetTZ( timecontext_t aContext, tz_entry &t, GZones* g, int year= 0 );
+bool GetTZ( string std, string dst, int bias, int biasDST, tz_entry &t, GZones* g );
+
+/*! Get the current year
+ */
+sInt16 MyYear( GZones* g );
+
+
+/*! Returns true, if the given TZ is existing already
+ * <t> tz_entry to search for:
+ * If <t.name> == "" search for any entry with these values.
+ * <t.name> != "" name must fit
+ * If <t.ident>== "" search for any entry with these values
+ * <t.ident>!= "" ident must fit
+ * <t.ident>== "?" search for the name <t.name> only.
+ *
+ * <aName> is <t.name> of the found record
+ * <aContext> is the assigned context
+ * <g> global list of additional time zones
+ * <createIt> create an entry, if not yet existing / default: false
+ * <searchOffset> says, where to start searching / default: at the beginning
+ */
+bool FoundTZ( const tz_entry &t,
+ string &aName,
+ timecontext_t &aContext,
+ GZones* g,
+ bool createIt = false,
+ timecontext_t searchOffset= tctx_tz_unknown );
+
+
+/*! Remove an existing entry
+ * Currently, elements of the hard coded list can't be removed
+ */
+bool RemoveTZ( const tz_entry &t, GZones* g );
+
+
+/*! Is it a DST time zone ? (both months must be defined for DST mode) */
+bool DSTCond( const tz_entry &t );
+
+/*! Adjust to day number within month <m>, %% valid till year <y> 2099 */
+void AdjustDay( sInt16 &d, sInt16 m, sInt16 y );
+
+/*! Get lineartime_t of <t> for a given <year>, either from std <toDST> or vice versa */
+lineartime_t DST_Switch( const tz_entry &t, int bias, sInt16 aYear, bool toDST );
+
+/*! Convert time zone name into context
+ * @param[in] aName : context name to resolve
+ * @param[out] aContext : context for this aName
+ * @param[in] g : global list of additional time zones
+ *
+ */
+bool TimeZoneNameToContext( cAppCharP aName, timecontext_t &aContext, GZones* g );
+
+/*! Convert context into time zone name, a preferred name can be given
+ * @param[in] aContext : time context to resolve
+ * @param[out] aName : context name for this aContext
+ * @param[in] g : global list of additional time zones
+ * @param[in] aPrefIdent : preferred name, if more than one is fitting
+ *
+ */
+bool TimeZoneContextToName( timecontext_t aContext, string &aName, GZones* g, cAppCharP aPrefIdent= "" );
+
+
+
+/*! get system's time zone context (i.e. resolve the TCTX_SYSTEM meta-context)
+ * @param[in,out] aContext : context will be made non-meta, that is, if input is TCTX_SYSTEM,
+ * actual time zone will be determined.
+ * @param[in] g : global list of additional time zones
+ */
+bool TzResolveMetaContext( timecontext_t &aContext, GZones* g );
+
+/*! make time context non-symbolic (= calculate minute offset east of UTC for aRefTime)
+ * but retain other time context flags in aContext
+ * @param[in,out] aContext : context will be made non-symbolic, that is resolved to minute offset east of UTC
+ * @param[in] aRefTime : reference time point for resolving the offset
+ * @param[in] aRefTimeUTC : if set, reference time must be UTC,
+ * otherwise, reference time must be in context of aContext
+ * @param[in] g : global list of additional time zones
+ */
+bool TzResolveContext( timecontext_t &aContext, lineartime_t aRefTime, bool aRefTimeUTC, GZones* g );
+
+/*! calculate minute offset east of UTC for aRefTime
+ * @param[in] aContext : time context to resolve
+ * @param[out] aMinuteOffset : receives minute offset east of UTC
+ * @param[in] aRefTime : reference time point for resolving the offset
+ * @param[in] aRefTimeUTC : if set, reference time must be UTC,
+ * otherwise, reference time must be in context of aContext
+ * @param[in] g : global list of additional time zones
+ */
+bool TzResolveToOffset( timecontext_t aContext, sInt16 &aMinuteOffset,
+ lineartime_t aRefTime,
+ bool aRefTimeUTC,
+ GZones* g );
+
+
+/*! Offset between two contexts (in seconds)
+ * Complex time zones (type "$") can't be currently resolved, they return false
+ * @param[in] aSourceValue : reference time for which the offset should be calculated
+ * @param[in] aSourceContext : source time zone context
+ * @param[in] aTargetContext : source time zone context
+ * @param[out] sDiff : receives offset east of UTC in seconds
+ * @param[in] g : global list of additional time zones
+ * @param[in] aDefaultContext: default context to use if source or target is TCTX_UNKNOWN
+ */
+bool TzOffsetSeconds( lineartime_t aSourceValue, timecontext_t aSourceContext,
+ timecontext_t aTargetContext,
+ sInt32 &sDiff,
+ GZones* g,
+ timecontext_t aDefaultContext= TCTX_UNKNOWN);
+
+
+
+/*! Converts timestamp value from one zone to another
+ * Complex time zones (type "$") can't be currently resolved, they return false
+ * @param[in/out] aValue : will be converted from source context to target context
+ * @param[in] aSourceContext : source time zone context
+ * @param[in] aTargetContext : source time zone context
+ * @param[in] g : global list of additional time zones
+ * @param[in] aDefaultContext: default context to use if source or target is TCTX_UNKNOWN
+ */
+bool TzConvertTimestamp( lineartime_t &aValue, timecontext_t aSourceContext,
+ timecontext_t aTargetContext,
+ GZones* g,
+ timecontext_t aDefaultContext = TCTX_UNKNOWN);
+
+
+/*! Prototypes for platform-specific implementation of time-zone-related routines
+ * which are implemented in platform_time.cpp
+ */
+
+/*! @brief get system real time
+ * @return system's real time in lineartime_t scale, in specified time zone context
+ * @param[in] aTimeContext : desired output time zone
+ * @param[in] g : global list of additional time zones
+ * @param[in] aNoOffset : no offset calculation, if true (to avoid recursive calls)
+ */
+lineartime_t getSystemNowAs( timecontext_t aTimeContext, GZones* g, bool aNoOffset= false );
+
+
+/*! Prototypes for platform-specific implementation of time-zone-related routines
+ * which are implemented in platform_timezones.cpp/.mm
+ */
+
+/*! @brief platform specific loading of time zone definitions
+ * @return true if this list is considered complete (i.e. no built-in zones should be used additionally)
+ * @param[in/out] aGZones : the GZones object where system zones should be loaded into
+ */
+bool loadSystemZoneDefinitions( GZones* aGZones );
+
+
+/*! @brief get current system time zone
+ * @return true if successful
+ * @param[out] aContext : the time zone context representing the current system time zone.
+ * @param[in] aGZones : the GZones object.
+ */
+bool getSystemTimeZoneContext( timecontext_t &aContext, GZones* aGZones );
+
+
+
+} // namespace sysync
+
+#endif
+/* eof */
diff --git a/src/sysync/tz_table.h b/src/sysync/tz_table.h
new file mode 100755
index 0000000..218594f
--- /dev/null
+++ b/src/sysync/tz_table.h
@@ -0,0 +1,573 @@
+/*
+ * File: tz_table.h
+ *
+ * Author: Beat Forster
+ *
+ * Time zone information.
+ * (CURRENTLY NOT) Automatically generated with 'read_tzi'
+ * Conversion date: ??/??/??
+ *
+ * Copyright (c) 2004-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ */
+
+
+
+#ifndef TZ_TABLE_H
+#define TZ_TABLE_H
+
+#include <string>
+#include <vector>
+
+typedef enum {
+ // special time zones
+ tctx_tz_unknown, // 0
+ tctx_tz_system, // 0
+
+ // list of all Registry time zones
+ tctx_tz_Afghanistan, // 4.5 Kabul
+ tctx_tz_AKST_AKDT, // -9 Alaska
+ tctx_tz_AKST_AKDT_2006, // -9 Alaska
+ tctx_tz_AKST_AKDT_2007, // -9 Alaska
+ tctx_tz_HNY_NAY, // -9 Alaska
+ tctx_tz_HNY_NAY_2006, // -9 Alaska
+ tctx_tz_HNY_NAY_2007, // -9 Alaska
+ tctx_tz_Alaskan, // -9 Alaska
+ tctx_tz_Alaskan_2006, // -9 Alaska
+ tctx_tz_Alaskan_2007, // -9 Alaska
+ tctx_tz_Arab, // 3 Kuwait, Er Riad
+ tctx_tz_Arabian, // 4 Abu Dhabi, Muskat
+ tctx_tz_Arabic, // 3 Bagdad
+ tctx_tz_AST_ADT, // -4 Atlantik (Kanada)
+ tctx_tz_AST_ADT_2006, // -4 Atlantik (Kanada)
+ tctx_tz_AST_ADT_2007, // -4 Atlantik (Kanada)
+ tctx_tz_HNA_HAA, // -4 Atlantik (Kanada)
+ tctx_tz_HNA_HAA_2006, // -4 Atlantik (Kanada)
+ tctx_tz_HNA_HAA_2007, // -4 Atlantik (Kanada)
+ tctx_tz_Atlantic, // -4 Atlantik (Kanada)
+ tctx_tz_Atlantic_2006, // -4 Atlantik (Kanada)
+ tctx_tz_Atlantic_2007, // -4 Atlantik (Kanada)
+ tctx_tz_AUS_Central, // 9.5 Darwin
+ tctx_tz_AUS_Eastern, // 10 Canberra, Melbourne, Sydney
+ tctx_tz_Azerbaijan, // 4 Baku
+ tctx_tz_Azores, // -1 Azoren
+ tctx_tz_Canada_Central, // -6 Saskatchewan
+ tctx_tz_Cape_Verde, // -1 Kapverdische Inseln
+ tctx_tz_Caucasus, // 4 Eriwan
+ tctx_tz_ACST_ACDT, // 9.5 Adelaide
+ tctx_tz_Central_Australia, // 9.5 Adelaide
+ tctx_tz_Central_America, // -6 Zentralamerika
+ tctx_tz_Central_Asia, // 6 Astana, Dhaka
+ tctx_tz_Central_Brazilian, // -4 Manaus
+ tctx_tz_Central_Brazilian_2006, // -4 Manaus
+ tctx_tz_Central_Brazilian_2007, // -4 Manaus
+ tctx_tz_CET_CEST, // 1 Belgrad, Bratislava, Budapest, Ljubljana, Prag
+ tctx_tz_MEZ_MESZ, // 1 Belgrad, Bratislava, Budapest, Ljubljana, Prag
+ tctx_tz_Central_Europe, // 1 Belgrad, Bratislava, Budapest, Ljubljana, Prag
+ tctx_tz_Central_European, // 1 Sarajevo, Skopje, Warschau, Zagreb
+ tctx_tz_Central_Pacific, // 11 Magadan, Salomonen, Neukaledonien
+ tctx_tz_CST_CDT, // -6 Chicago, Dallas, Kansas City, Winnipeg
+ tctx_tz_CST_CDT_2006, // -6 Chicago, Dallas, Kansas City, Winnipeg
+ tctx_tz_CST_CDT_2007, // -6 Chicago, Dallas, Kansas City, Winnipeg
+ tctx_tz_HNC_HAC, // -6 Chicago, Dallas, Kansas City, Winnipeg
+ tctx_tz_HNC_HAC_2006, // -6 Chicago, Dallas, Kansas City, Winnipeg
+ tctx_tz_HNC_HAC_2007, // -6 Chicago, Dallas, Kansas City, Winnipeg
+ tctx_tz_Central, // -6 Chicago, Dallas, Kansas City, Winnipeg
+ tctx_tz_Central_2006, // -6 Chicago, Dallas, Kansas City, Winnipeg
+ tctx_tz_Central_2007, // -6 Chicago, Dallas, Kansas City, Winnipeg
+ tctx_tz_Central_Mexico, // -6 Guadalajara, Mexiko-Stadt, Monterrey - neu
+ tctx_tz_China, // 8 Peking, Chongqing, Hongkong, Urumchi
+ tctx_tz_Dateline, // -12 Internationale Datumsgrenze (Westen)
+ tctx_tz_East_Africa, // 3 Nairobi
+ tctx_tz_AEST_AEDT, // 10 Brisbane
+ tctx_tz_East_Australia, // 10 Brisbane
+ tctx_tz_EET_EEST, // 2 Minsk
+ tctx_tz_East_Europe, // 2 Minsk
+ tctx_tz_East_South_America, // -3 Brasilia
+ tctx_tz_East_South_America_2006, // -3 Brasilia
+ tctx_tz_East_South_America_2007, // -3 Brasilia
+ tctx_tz_EST_EDT, // -5 New York, Miami, Atlanta, Detroit, Toronto
+ tctx_tz_EST_EDT_2006, // -5 New York, Miami, Atlanta, Detroit, Toronto
+ tctx_tz_EST_EDT_2007, // -5 New York, Miami, Atlanta, Detroit, Toronto
+ tctx_tz_HNE_HAE, // -5 New York, Miami, Atlanta, Detroit, Toronto
+ tctx_tz_HNE_HAE_2006, // -5 New York, Miami, Atlanta, Detroit, Toronto
+ tctx_tz_HNE_HAE_2007, // -5 New York, Miami, Atlanta, Detroit, Toronto
+ tctx_tz_Eastern, // -5 New York, Miami, Atlanta, Detroit, Toronto
+ tctx_tz_Eastern_2006, // -5 New York, Miami, Atlanta, Detroit, Toronto
+ tctx_tz_Eastern_2007, // -5 New York, Miami, Atlanta, Detroit, Toronto
+ tctx_tz_Egypt, // 2 Kairo
+ tctx_tz_Ekaterinburg, // 5 Jekaterinburg
+ tctx_tz_Fiji, // 12 Fidschi, Kamtschatka, Marshall-Inseln
+ tctx_tz_FLE, // 2 Helsinki, Kiew, Riga, Sofia, Tallinn, Wilna
+ tctx_tz_Georgian, // 3 Tiflis
+ tctx_tz_GMT, // 0 Dublin, Edinburgh, Lissabon, London
+ tctx_tz_Greenland, // -3 Groenland
+ tctx_tz_Greenwich, // 0 Casablanca, Monrovia, Reykjavik
+ tctx_tz_GTB, // 2 Athen, Bukarest, Istanbul
+ tctx_tz_HAST_HADT, // -10 Hawaii
+ tctx_tz_Hawaiian, // -10 Hawaii
+ tctx_tz_India, // 5.5 Chennai, Kolkata, Mumbai, Neu-Delhi
+ tctx_tz_Iran, // 3.5 Teheran
+ tctx_tz_Iran_2005, // 3.5 Teheran
+ tctx_tz_Iran_2006, // 3.5 Teheran
+ tctx_tz_Israel, // 2 Jerusalem
+ tctx_tz_Israel_2004, // 2 Jerusalem
+ tctx_tz_Israel_2005, // 2 Jerusalem
+ tctx_tz_Israel_2006, // 2 Jerusalem
+ tctx_tz_Israel_2007, // 2 Jerusalem
+ tctx_tz_Israel_2008, // 2 Jerusalem
+ tctx_tz_Israel_2009, // 2 Jerusalem
+ tctx_tz_Israel_2010, // 2 Jerusalem
+ tctx_tz_Israel_2011, // 2 Jerusalem
+ tctx_tz_Israel_2012, // 2 Jerusalem
+ tctx_tz_Israel_2013, // 2 Jerusalem
+ tctx_tz_Israel_2014, // 2 Jerusalem
+ tctx_tz_Israel_2015, // 2 Jerusalem
+ tctx_tz_Israel_2016, // 2 Jerusalem
+ tctx_tz_Israel_2017, // 2 Jerusalem
+ tctx_tz_Israel_2018, // 2 Jerusalem
+ tctx_tz_Israel_2019, // 2 Jerusalem
+ tctx_tz_Israel_2020, // 2 Jerusalem
+ tctx_tz_Israel_2021, // 2 Jerusalem
+ tctx_tz_Israel_2022, // 2 Jerusalem
+ tctx_tz_Israel_2023, // 2 Jerusalem
+ tctx_tz_Jordan, // 2 Amman
+ tctx_tz_Korea, // 9 Seoul
+ tctx_tz_Mexico, // -6 Guadalajara, Mexiko-Stadt, Monterrey - alt
+ tctx_tz_Mexico_2, // -7 Chihuahua, La Paz, Mazatlan - alt
+ tctx_tz_Mid_Atlantic, // -2 Mittelatlantik
+ tctx_tz_Middle_East, // 2 Beirut
+ tctx_tz_Montevideo, // -3 Montevideo
+ tctx_tz_MST_MDT, // -7 Denver, Salt Lake City, Calgary
+ tctx_tz_MST_MDT_2006, // -7 Denver, Salt Lake City, Calgary
+ tctx_tz_MST_MDT_2007, // -7 Denver, Salt Lake City, Calgary
+ tctx_tz_HNR_HAR, // -7 Denver, Salt Lake City, Calgary
+ tctx_tz_HNR_HAR_2006, // -7 Denver, Salt Lake City, Calgary
+ tctx_tz_HNR_HAR_2007, // -7 Denver, Salt Lake City, Calgary
+ tctx_tz_Mountain, // -7 Denver, Salt Lake City, Calgary
+ tctx_tz_Mountain_2006, // -7 Denver, Salt Lake City, Calgary
+ tctx_tz_Mountain_2007, // -7 Denver, Salt Lake City, Calgary
+ tctx_tz_Mountain_Mexico, // -7 Chihuahua, La Paz, Mazatlan - neu
+ tctx_tz_Myanmar, // 6.5 Yangon (Rangun)
+ tctx_tz_North_Central_Asia, // 6 Almaty, Nowosibirsk
+ tctx_tz_Namibia, // 2 Windhuk
+ tctx_tz_Nepal, // 5.75 Katmandu
+ tctx_tz_New_Zealand, // 12 Auckland, Wellington
+ tctx_tz_NST_NDT, // -3.5 Neufundland
+ tctx_tz_NST_NDT_2006, // -3.5 Neufundland
+ tctx_tz_NST_NDT_2007, // -3.5 Neufundland
+ tctx_tz_HNT_HAT, // -3.5 Neufundland
+ tctx_tz_HNT_HAT_2006, // -3.5 Neufundland
+ tctx_tz_HNT_HAT_2007, // -3.5 Neufundland
+ tctx_tz_Newfoundland, // -3.5 Neufundland
+ tctx_tz_Newfoundland_2006, // -3.5 Neufundland
+ tctx_tz_Newfoundland_2007, // -3.5 Neufundland
+ tctx_tz_North_Asia_East, // 8 Irkutsk, Ulan Bator
+ tctx_tz_North_Asia, // 7 Krasnojarsk
+ tctx_tz_Pacific_SA, // -4 Santiago
+ tctx_tz_PST_PDT, // -8 Pacific (USA, Kanada)
+ tctx_tz_PST_PDT_2006, // -8 Pacific (USA, Kanada)
+ tctx_tz_PST_PDT_2007, // -8 Pacific (USA, Kanada)
+ tctx_tz_HNP_HAP, // -8 Pacific (USA, Kanada)
+ tctx_tz_HNP_HAP_2006, // -8 Pacific (USA, Kanada)
+ tctx_tz_HNP_HAP_2007, // -8 Pacific (USA, Kanada)
+ tctx_tz_Pacific, // -8 Pacific (USA, Kanada)
+ tctx_tz_Pacific_2006, // -8 Pacific (USA, Kanada)
+ tctx_tz_Pacific_2007, // -8 Pacific (USA, Kanada)
+ tctx_tz_Pacific_Mexico, // -8 Tijuana, Niederkalifornien (Mexiko)
+ tctx_tz_Romance, // 1 Bruessel, Kopenhagen, Madrid, Paris
+ tctx_tz_Russian, // 3 Moskau, St. Petersburg, Wolgograd
+ tctx_tz_SA_Eastern, // -3 Buenos Aires, Georgetown
+ tctx_tz_SA_Pacific, // -5 Bogota, Lima, Quito, Rio Branco
+ tctx_tz_SA_Western, // -4 Caracas, La Paz
+ tctx_tz_Samoa, // -11 Midway-Inseln, Samoa
+ tctx_tz_SE_Asia, // 7 Bangkok, Hanoi, Jakarta
+ tctx_tz_Singapore, // 8 Kuala Lumpur, Singapur
+ tctx_tz_South_Africa, // 2 Harare, Praetoria
+ tctx_tz_Sri_Lanka, // 5.5 Sri Jayawardenepura
+ tctx_tz_Taipei, // 8 Taipeh
+ tctx_tz_Tasmania, // 10 Hobart
+ tctx_tz_Tokyo, // 9 Osaka, Sapporo, Tokio
+ tctx_tz_Tonga, // 13 Nuku'alofa
+ tctx_tz_US_Eastern, // -5 Indiana (Ost)
+ tctx_tz_US_Mountain, // -7 Arizona
+ tctx_tz_Vladivostok, // 10 Wladiwostok
+ tctx_tz_West_Australia, // 8 Perth
+ tctx_tz_West_Australia_2005, // 8 Perth
+ tctx_tz_West_Australia_2006, // 8 Perth
+ tctx_tz_West_Australia_2007, // 8 Perth
+ tctx_tz_West_Central_Africa, // 1 West-Zentralafrika
+ tctx_tz_WET_WEST, // 1 Amsterdam, Berlin, Bern, Rom, Stockholm, Wien
+ tctx_tz_West_Europe, // 1 Amsterdam, Berlin, Bern, Rom, Stockholm, Wien
+ tctx_tz_West_Asia, // 5 Islamabad, Karatschi, Taschkent
+ tctx_tz_West_Pacific, // 10 Guam, Port Moresby
+ tctx_tz_Yakutsk, // 9 Jakutsk
+
+ // http://www.timeanddate.com/library/abbreviations/timezones
+ tctx_tz_A, // 1 Alpha Time Zone <Military>
+ tctx_tz_ACDT, // 10.5 Australian Central Daylight Time Australia
+ tctx_tz_ACST, // 9.5 Australian Central Standard Time Australia
+ tctx_tz_ADT, // -3 Atlantic Daylight Time North America
+ tctx_tz_AEDT, // 11 Australian Eastern Daylight Time Australia
+ tctx_tz_AEST, // 10 Australian Eastern Standard Time Australia
+ tctx_tz_AKDT, // -8 Alaska Daylight Time North America
+ tctx_tz_AKST, // -9 Alaska Standard Time North America
+ tctx_tz_AST, // -4 Atlantic Standard Time North America
+ tctx_tz_AWST, // 8 Australian Western Standard Time Australia
+ tctx_tz_B, // 2 Bravo Time Zone <Military>
+ tctx_tz_BST, // 1 British Summer Time Europe
+ tctx_tz_C, // 3 Charlie Time Zone <Military>
+ tctx_tz_CDT, // -5 Central Daylight Time North America
+ tctx_tz_CEST, // 2 Central European Summer Time Europe
+ tctx_tz_CET, // 1 Central European Time Europe
+ tctx_tz_CST, // -6 Central Standard Time North America
+ tctx_tz_CXT, // 7 Christmas Island Time Australia
+ tctx_tz_D, // 4 Delta Time Zone <Military>
+ tctx_tz_E, // 5 Echo Time Zone <Military>
+ tctx_tz_EDT, // -4 Eastern Daylight Time North America
+ tctx_tz_EEST, // 3 Eastern European Summer Time Europe
+ tctx_tz_EET, // 2 Eastern European Time Europe
+ tctx_tz_EST, // -5 Eastern Standard Time North America
+ tctx_tz_F, // 6 Foxtrot Time Zone <Military>
+ tctx_tz_G, // 7 Golf Time Zone <Military>
+ tctx_tz_H, // 8 Hotel Time Zone <Military>
+ tctx_tz_HAA, // -3 Heure Avancee de l'Atlantique North America
+ tctx_tz_HAC, // -5 Heure Avancee du Centre North America
+ tctx_tz_HADT, // -9 Hawaii-Aleutian Daylight Time North America
+ tctx_tz_HAE, // -4 Heure Avancee de l'Est North America
+ tctx_tz_HAP, // -7 Heure Avancee du Pacifique North America
+ tctx_tz_HAR, // -6 Heure Avancee des Rocheuses North America
+ tctx_tz_HAST, // -10 Hawaii-Aleutian Standard Time North America
+ tctx_tz_HAT, // -1.5 Heure Avancee de Terre-Neuve North America
+ tctx_tz_HAY, // -8 Heure Avancee du Yukon North America
+ tctx_tz_HNA, // -4 Heure Normale de l'Atlantique North America
+ tctx_tz_HNC, // -6 Heure Normale du Centre North America
+ tctx_tz_HNE, // -5 Heure Normale de l'Est North America
+ tctx_tz_HNP, // -8 Heure Normale du Pacifique North America
+ tctx_tz_HNR, // -7 Heure Normale des Rocheuses North America
+ tctx_tz_HNT, // -2.5 Heure Normale de Terre-Neuve North America
+ tctx_tz_HNY, // -9 Heure Normale du Yukon North America
+ tctx_tz_I, // 9 India Time Zone <Military>
+ tctx_tz_IST, // 1 Irish Summer Time Europe
+ tctx_tz_K, // 10 Kilo Time Zone <Military>
+ tctx_tz_L, // 11 Lima Time Zone <Military>
+ tctx_tz_M, // 12 Mike Time Zone <Military>
+ tctx_tz_MDT, // -6 Mountain Daylight Time North America
+ tctx_tz_MESZ, // 2 Mitteleuropaeische Sommerzeit Europe
+ tctx_tz_MEZ, // 1 Mitteleuropaeische Zeit Europe
+ tctx_tz_MST, // -7 Mountain Standard Time North America
+ tctx_tz_N, // -1 November Time Zone <Military>
+ tctx_tz_NDT, // -1.5 Newfoundland Daylight Time North America
+ tctx_tz_NFT, // 11.5 Norfolk (Island) Time Australia
+ tctx_tz_NST, // -2.5 Newfoundland Standard Time North America
+ tctx_tz_O, // -2 Oscar Time Zone <Military>
+ tctx_tz_P, // -3 Papa Time Zone <Military>
+ tctx_tz_PDT, // -7 Pacific Daylight Time North America
+ tctx_tz_PST, // -8 Pacific Standard Time North America
+ tctx_tz_Q, // -4 Quebec Time Zone <Military>
+ tctx_tz_R, // -5 Romeo Time Zone <Military>
+ tctx_tz_S, // -6 Sierra Time Zone <Military>
+ tctx_tz_T, // -7 Tango Time Zone <Military>
+ tctx_tz_U, // -8 Uniform Time Zone <Military>
+ tctx_tz_UTC, // 0 Coordinated Universal Time Europe
+ tctx_tz_V, // -9 Victor Time Zone <Military>
+ tctx_tz_W, // -10 Whiskey Time Zone <Military>
+ tctx_tz_WEST, // 1 Western European Summer Time Europe
+ tctx_tz_WET, // 0 Western European Time Europe
+ tctx_tz_WST, // 8 Western Standard Time Australia
+ tctx_tz_X, // -11 X-ray Time Zone <Military>
+ tctx_tz_Y, // -12 Yankee Time Zone <Military>
+ tctx_tz_Z, // 0 Zulu Time Zone <Military>
+
+ // size of enum
+ tctx_numtimezones
+} TTimeZones;
+
+
+
+#ifdef TIMEZONES_INTERNAL
+
+typedef struct {
+ short wMonth;
+ short wDayOfWeek;
+ short wNth; // nth occurance
+ short wHour;
+ short wMinute;
+ } tbl_tChange;
+
+
+typedef struct {
+ const char* name;
+ const char* olsonName;
+ short bias;
+ short biasDST;
+ const char* ident;
+ const char* dynYear;
+ tbl_tChange dst;
+ tbl_tChange std;
+ } tbl_tz_entry;
+
+
+
+const tbl_tz_entry tbl_tz[tctx_numtimezones] =
+{
+ { "unknown", NULL, 0, 0, "x", "", { 0, 0,0, 0,0 }, { 0, 0,0, 0,0 } }, // 0
+ { "system", NULL, 0, 0, "x", "", { 0, 0,0, 0,0 }, { 0, 0,0, 0,0 } }, // 0
+
+ { "Afghanistan", "Asia/Kabul", 270, 0, " ", "", { 0, 0,0, 0,0 }, { 0, 0,0, 0,0 } }, // 4.5
+ { "AKST/AKDT", "America/Anchorage", -540, 60, " ", "", { 3, 0,2, 2,0 }, { 11, 0,1, 2,0 } }, // -9
+ { "AKST/AKDT", "America/Anchorage", -540, 60, " ","2006", { 4, 0,1, 2,0 }, { 10, 0,5, 2,0 } }, // -9
+ { "AKST/AKDT", "America/Anchorage", -540, 60, " ","2007", { 3, 0,2, 2,0 }, { 11, 0,1, 2,0 } }, // -9
+ { "HNY/NAY", "America/Anchorage", -540, 60, " ", "", { 3, 0,2, 2,0 }, { 11, 0,1, 2,0 } }, // -9
+ { "HNY/NAY", "America/Anchorage", -540, 60, " ","2006", { 4, 0,1, 2,0 }, { 10, 0,5, 2,0 } }, // -9
+ { "HNY/NAY", "America/Anchorage", -540, 60, " ","2007", { 3, 0,2, 2,0 }, { 11, 0,1, 2,0 } }, // -9
+ { "Alaskan", "America/Anchorage", -540, 60, " ", "", { 3, 0,2, 2,0 }, { 11, 0,1, 2,0 } }, // -9
+ { "Alaskan", "America/Anchorage", -540, 60, " ","2006", { 4, 0,1, 2,0 }, { 10, 0,5, 2,0 } }, // -9
+ { "Alaskan", "America/Anchorage", -540, 60, " ","2007", { 3, 0,2, 2,0 }, { 11, 0,1, 2,0 } }, // -9
+ { "Arab", "Asia/Riyadh", 180, 0, " ", "", { 0, 0,0, 0,0 }, { 0, 0,0, 0,0 } }, // 3
+ { "Arabian", "Asia/Dubai", 240, 0, " ", "", { 0, 0,0, 0,0 }, { 0, 0,0, 0,0 } }, // 4
+ { "Arabic", "Asia/Baghdad", 180, 60, " ", "", { 4, 0,1, 3,0 }, { 10, 0,1, 4,0 } }, // 3
+ { "AST/ADT", "America/Halifax", -240, 60, " ", "", { 3, 0,2, 2,0 }, { 11, 0,1, 2,0 } }, // -4
+ { "AST/ADT", "America/Halifax", -240, 60, " ","2006", { 4, 0,1, 2,0 }, { 10, 0,5, 2,0 } }, // -4
+ { "AST/ADT", "America/Halifax", -240, 60, " ","2007", { 3, 0,2, 2,0 }, { 11, 0,1, 2,0 } }, // -4
+ { "HNA/HAA", "America/Halifax", -240, 60, " ", "", { 3, 0,2, 2,0 }, { 11, 0,1, 2,0 } }, // -4
+ { "HNA/HAA", "America/Halifax", -240, 60, " ","2006", { 4, 0,1, 2,0 }, { 10, 0,5, 2,0 } }, // -4
+ { "HNA/HAA", "America/Halifax", -240, 60, " ","2007", { 3, 0,2, 2,0 }, { 11, 0,1, 2,0 } }, // -4
+ { "Atlantic", "America/Halifax", -240, 60, " ", "", { 3, 0,2, 2,0 }, { 11, 0,1, 2,0 } }, // -4
+ { "Atlantic", "America/Halifax", -240, 60, " ","2006", { 4, 0,1, 2,0 }, { 10, 0,5, 2,0 } }, // -4
+ { "Atlantic", "America/Halifax", -240, 60, " ","2007", { 3, 0,2, 2,0 }, { 11, 0,1, 2,0 } }, // -4
+ { "AUS_Central", "Australia/Darwin", 570, 0, " ", "", { 0, 0,0, 0,0 }, { 0, 0,0, 0,0 } }, // 9.5
+ { "AUS_Eastern", "Australia/Sydney", 600, 60, " ", "", { 10, 0,5, 2,0 }, { 3, 0,5, 3,0 } }, // 10
+ { "Azerbaijan", "Asia/Baku", 240, 60, " ", "", { 3, 0,5, 4,0 }, { 10, 0,5, 5,0 } }, // 4
+ { "Azores", "Atlantic/Azores", -60, 60, " ", "", { 3, 0,5, 2,0 }, { 10, 0,5, 3,0 } }, // -1
+ { "Canada_Central", "America/Regina", -360, 0, " ", "", { 0, 0,0, 0,0 }, { 0, 0,0, 0,0 } }, // -6
+ { "Cape_Verde", "Atlantic/Cape_Verde", -60, 0, " ", "", { 0, 0,0, 0,0 }, { 0, 0,0, 0,0 } }, // -1
+ { "Caucasus", "Asia/Tbilisi", 240, 60, " ", "", { 3, 0,5, 2,0 }, { 10, 0,5, 3,0 } }, // 4
+ { "ACST/ACDT", "Australia/Adelaide", 570, 60, " ", "", { 10, 0,5, 2,0 }, { 3, 0,5, 3,0 } }, // 9.5
+ { "Central_Australia", "Australia/Adelaide", 570, 60, " ", "", { 10, 0,5, 2,0 }, { 3, 0,5, 3,0 } }, // 9.5
+ { "Central_America", "America/Guatemala", -360, 0, " ", "", { 0, 0,0, 0,0 }, { 0, 0,0, 0,0 } }, // -6
+ { "Central_Asia", "Asia/Dhaka", 360, 0, " ", "", { 0, 0,0, 0,0 }, { 0, 0,0, 0,0 } }, // 6
+ { "Central_Brazilian", "America/Manaus", -240, 60, " ", "", { 11, 0,1, 0,0 }, { 2, 0,5, 0,0 } }, // -4
+ { "Central_Brazilian", "America/Manaus", -240, 60, " ","2006", { 11, 0,1, 0,0 }, { 2, 0,2, 2,0 } }, // -4
+ { "Central_Brazilian", "America/Manaus", -240, 60, " ","2007", { 11, 0,1, 0,0 }, { 2, 0,5, 0,0 } }, // -4
+ { "CET/CEST", "Europe/Zurich", 60, 60, " ", "", { 3, 0,5, 2,0 }, { 10, 0,5, 3,0 } }, // 1
+ { "MEZ/MESZ", "Europe/Berlin", 60, 60, " ", "", { 3, 0,5, 2,0 }, { 10, 0,5, 3,0 } }, // 1
+ { "Central_Europe", "Europe/Budapest", 60, 60, " ", "", { 3, 0,5, 2,0 }, { 10, 0,5, 3,0 } }, // 1
+ { "Central_European", "Europe/Warsaw", 60, 60, " ", "", { 3, 0,5, 2,0 }, { 10, 0,5, 3,0 } }, // 1
+ { "Central_Pacific", "Pacific/Guadalcanal", 660, 0, " ", "", { 0, 0,0, 0,0 }, { 0, 0,0, 0,0 } }, // 11
+ { "CST/CDT", "America/Chicago", -360, 60, " ", "", { 3, 0,2, 2,0 }, { 11, 0,1, 2,0 } }, // -6
+ { "CST/CDT", "America/Chicago", -360, 60, " ","2006", { 4, 0,1, 2,0 }, { 10, 0,5, 2,0 } }, // -6
+ { "CST/CDT", "America/Chicago", -360, 60, " ","2007", { 3, 0,2, 2,0 }, { 11, 0,1, 2,0 } }, // -6
+ { "HNC/HAC", "America/Chicago", -360, 60, " ", "", { 3, 0,2, 2,0 }, { 11, 0,1, 2,0 } }, // -6
+ { "HNC/HAC", "America/Chicago", -360, 60, " ","2006", { 4, 0,1, 2,0 }, { 10, 0,5, 2,0 } }, // -6
+ { "HNC/HAC", "America/Chicago", -360, 60, " ","2007", { 3, 0,2, 2,0 }, { 11, 0,1, 2,0 } }, // -6
+ { "Central", "America/Chicago", -360, 60, " ", "", { 3, 0,2, 2,0 }, { 11, 0,1, 2,0 } }, // -6
+ { "Central", "America/Chicago", -360, 60, " ","2006", { 4, 0,1, 2,0 }, { 10, 0,5, 2,0 } }, // -6
+ { "Central", "America/Chicago", -360, 60, " ","2007", { 3, 0,2, 2,0 }, { 11, 0,1, 2,0 } }, // -6
+ { "Central_Mexico", "America/Mexico_City", -360, 60, " ", "", { 4, 0,1, 2,0 }, { 10, 0,5, 2,0 } }, // -6
+ { "China", "Asia/Shanghai", 480, 0, " ", "", { 0, 0,0, 0,0 }, { 0, 0,0, 0,0 } }, // 8
+ { "Dateline", "Etc/GMT+12", -720, 0, " ", "", { 0, 0,0, 0,0 }, { 0, 0,0, 0,0 } }, // -12
+ { "East_Africa", "Africa/Nairobi", 180, 0, " ", "", { 0, 0,0, 0,0 }, { 0, 0,0, 0,0 } }, // 3
+ { "AEST/AEDT", "Australia/Brisbane", 600, 0, " ", "", { 0, 0,0, 0,0 }, { 0, 0,0, 0,0 } }, // 10
+ { "East_Australia", "Australia/Brisbane", 600, 0, " ", "", { 0, 0,0, 0,0 }, { 0, 0,0, 0,0 } }, // 10
+ { "EET/EEST", "Europe/Minsk", 120, 60, " ", "", { 3, 0,5, 2,0 }, { 10, 0,5, 3,0 } }, // 2
+ { "East_Europe", "Europe/Minsk", 120, 60, " ", "", { 3, 0,5, 2,0 }, { 10, 0,5, 3,0 } }, // 2
+ { "East_South_America","America/Sao_Paulo", -180, 60, " ", "", { 11, 0,1, 0,0 }, { 2, 0,5, 0,0 } }, // -3
+ { "East_South_America","America/Sao_Paulo", -180, 60, " ","2006", { 11, 0,1, 0,0 }, { 2, 0,2, 2,0 } }, // -3
+ { "East_South_America","America/Sao_Paulo", -180, 60, " ","2007", { 11, 0,1, 0,0 }, { 2, 0,5, 0,0 } }, // -3
+ { "EST/EDT", "America/New_York", -300, 60, " ", "", { 3, 0,2, 2,0 }, { 11, 0,1, 2,0 } }, // -5
+ { "EST/EDT", "America/New_York", -300, 60, " ","2006", { 4, 0,1, 2,0 }, { 10, 0,5, 2,0 } }, // -5
+ { "EST/EDT", "America/New_York", -300, 60, " ","2007", { 3, 0,2, 2,0 }, { 11, 0,1, 2,0 } }, // -5
+ { "HNE/HAE", "America/New_York", -300, 60, " ", "", { 3, 0,2, 2,0 }, { 11, 0,1, 2,0 } }, // -5
+ { "HNE/HAE", "America/New_York", -300, 60, " ","2006", { 4, 0,1, 2,0 }, { 10, 0,5, 2,0 } }, // -5
+ { "HNE/HAE", "America/New_York", -300, 60, " ","2007", { 3, 0,2, 2,0 }, { 11, 0,1, 2,0 } }, // -5
+ { "Eastern", "America/New_York", -300, 60, " ", "", { 3, 0,2, 2,0 }, { 11, 0,1, 2,0 } }, // -5
+ { "Eastern", "America/New_York", -300, 60, " ","2006", { 4, 0,1, 2,0 }, { 10, 0,5, 2,0 } }, // -5
+ { "Eastern", "America/New_York", -300, 60, " ","2007", { 3, 0,2, 2,0 }, { 11, 0,1, 2,0 } }, // -5
+ { "Egypt", "Africa/Cairo", 120, 60, " ", "", { 4, 4,5,23,59 }, { 9, 4,5,23,59 }}, // 2
+ { "Ekaterinburg", "Asia/Yekaterinburg", 300, 60, " ", "", { 3, 0,5, 2,0 }, { 10, 0,5, 3,0 } }, // 5
+ { "Fiji", "Pacific/Fiji", 720, 0, " ", "", { 0, 0,0, 0,0 }, { 0, 0,0, 0,0 } }, // 12
+ { "FLE", "Europe/Kiev", 120, 60, " ", "", { 3, 0,5, 3,0 }, { 10, 0,5, 4,0 } }, // 2
+ { "Georgian", "Etc/GMT-3", 180, 0, " ", "", { 0, 0,0, 0,0 }, { 0, 0,0, 0,0 } }, // 3
+ { "GMT", "Europe/London", 0, 60, " ", "", { 3, 0,5, 1,0 }, { 10, 0,5, 2,0 } }, // 0
+ { "Greenland", "America/Godthab", -180, 60, " ", "", { 4, 0,1, 2,0 }, { 10, 0,5, 2,0 } }, // -3
+ { "Greenwich", "Africa/Casablanca", 0, 0, " ", "", { 0, 0,0, 0,0 }, { 0, 0,0, 0,0 } }, // 0
+ { "GTB", "Europe/Istanbul", 120, 60, " ", "", { 3, 0,5, 3,0 }, { 10, 0,5, 4,0 } }, // 2
+ { "HAST/HADT", "Pacific/Honolulu", -600, 0, " ", "", { 0, 0,0, 0,0 }, { 0, 0,0, 0,0 } }, // -10
+ { "Hawaiian", "Pacific/Honolulu", -600, 0, " ", "", { 0, 0,0, 0,0 }, { 0, 0,0, 0,0 } }, // -10
+ { "India", "Asia/Calcutta", 330, 0, " ", "", { 0, 0,0, 0,0 }, { 0, 0,0, 0,0 } }, // 5.5
+ { "Iran", "Asia/Tehran", 210, 0, " ", "", { 0, 0,0, 0,0 }, { 0, 0,0, 0,0 } }, // 3.5
+ { "Iran", "Asia/Tehran", 210, 60, " ","2005", { 3, 0,1, 2,0 }, { 9, 2,4, 2,0 } }, // 3.5
+ { "Iran", "Asia/Tehran", 210, 0, " ","2006", { 0, 0,0, 0,0 }, { 0, 0,0, 0,0 } }, // 3.5
+ { "Israel", "Asia/Jerusalem", 120, 60, " ", "", { 3, 5,5, 2,0 }, { 9, 0,3, 2,0 } }, // 2
+ { "Israel", "Asia/Jerusalem", 120, 0, " ","2004", { 0, 0,0, 0,0 }, { 0, 0,0, 0,0 } }, // 2
+ { "Israel", "Asia/Jerusalem", 120, 60, " ","2005", { 4,-1,1, 2,0 }, { 10,-1,9, 2,0 } }, // 2
+ { "Israel", "Asia/Jerusalem", 120, 60, " ","2006", { 3,-1,31,2,0 }, { 10,-1,1, 2,0 } }, // 2
+ { "Israel", "Asia/Jerusalem", 120, 60, " ","2007", { 3,-1,30,2,0 }, { 9,-1,16,2,0 } }, // 2
+ { "Israel", "Asia/Jerusalem", 120, 60, " ","2008", { 3,-1,28,2,0 }, { 10,-1,5, 2,0 } }, // 2
+ { "Israel", "Asia/Jerusalem", 120, 60, " ","2009", { 3,-1,27,2,0 }, { 9,-1,27,2,0 } }, // 2
+ { "Israel", "Asia/Jerusalem", 120, 60, " ","2010", { 3,-1,26,2,0 }, { 9,-1,12,2,0 } }, // 2
+ { "Israel", "Asia/Jerusalem", 120, 60, " ","2011", { 4,-1,1, 2,0 }, { 10,-1,2, 2,0 } }, // 2
+ { "Israel", "Asia/Jerusalem", 120, 60, " ","2012", { 3,-1,30,2,0 }, { 9,-1,23,2,0 } }, // 2
+ { "Israel", "Asia/Jerusalem", 120, 60, " ","2013", { 3,-1,29,2,0 }, { 9,-1,8, 2,0 } }, // 2
+ { "Israel", "Asia/Jerusalem", 120, 60, " ","2014", { 3,-1,28,2,0 }, { 9,-1,28,2,0 } }, // 2
+ { "Israel", "Asia/Jerusalem", 120, 60, " ","2015", { 3,-1,27,2,0 }, { 9,-1,20,2,0 } }, // 2
+ { "Israel", "Asia/Jerusalem", 120, 60, " ","2016", { 4,-1,1, 2,0 }, { 10,-1,9, 2,0 } }, // 2
+ { "Israel", "Asia/Jerusalem", 120, 60, " ","2017", { 3,-1,31,2,0 }, { 9,-1,24,2,0 } }, // 2
+ { "Israel", "Asia/Jerusalem", 120, 60, " ","2018", { 3,-1,30,2,0 }, { 9,-1,16,2,0 } }, // 2
+ { "Israel", "Asia/Jerusalem", 120, 60, " ","2019", { 3,-1,29,2,0 }, { 10,-1,6, 2,0 } }, // 2
+ { "Israel", "Asia/Jerusalem", 120, 60, " ","2020", { 3,-1,27,2,0 }, { 9,-1,27,2,0 } }, // 2
+ { "Israel", "Asia/Jerusalem", 120, 60, " ","2021", { 3,-1,26,2,0 }, { 9,-1,12,2,0 } }, // 2
+ { "Israel", "Asia/Jerusalem", 120, 60, " ","2022", { 4,-1,1, 2,0 }, { 10,-1,2, 2,0 } }, // 2
+ { "Israel", "Asia/Jerusalem", 120, 0, " ","2023", { 0, 0,0, 0,0 }, { 0, 0,0, 0,0 } }, // 2
+ { "Jordan", "Asia/Amman", 120, 60, " ", "", { 3, 4,5, 0,0 }, { 9, 5,5, 1,0 } }, // 2
+ { "Korea", "Asia/Seoul", 540, 0, " ", "", { 0, 0,0, 0,0 }, { 0, 0,0, 0,0 } }, // 9
+ { "Mexico", "America/Mexico_City", -360, 60, " ", "", { 4, 0,1, 2,0 }, { 10, 0,5, 2,0 } }, // -6
+ { "Mexico_2", "America/Chihuahua", -420, 60, " ", "", { 4, 0,1, 2,0 }, { 10, 0,5, 2,0 } }, // -7
+ { "Mid_Atlantic", "Atlantic/South_Georgia", -120, 60, " ", "", { 3, 0,5, 2,0 }, { 9, 0,5, 2,0 } }, // -2
+ { "Middle_East", "Asia/Beirut", 120, 60, " ", "", { 3, 0,5, 0,0 }, { 10, 6,5,23,59 }}, // 2
+ { "Montevideo", "America/Montevideo", -180, 60, " ", "", { 10, 0,1, 2,0 }, { 3, 0,2, 2,0 } }, // -3
+ { "MST/MDT", "America/Denver", -420, 60, " ", "", { 3, 0,2, 2,0 }, { 11, 0,1, 2,0 } }, // -7
+ { "MST/MDT", "America/Denver", -420, 60, " ","2006", { 4, 0,1, 2,0 }, { 10, 0,5, 2,0 } }, // -7
+ { "MST/MDT", "America/Denver", -420, 60, " ","2007", { 3, 0,2, 2,0 }, { 11, 0,1, 2,0 } }, // -7
+ { "HNR/HAR", "America/Denver", -420, 60, " ", "", { 3, 0,2, 2,0 }, { 11, 0,1, 2,0 } }, // -7
+ { "HNR/HAR", "America/Denver", -420, 60, " ","2006", { 4, 0,1, 2,0 }, { 10, 0,5, 2,0 } }, // -7
+ { "HNR/HAR", "America/Denver", -420, 60, " ","2007", { 3, 0,2, 2,0 }, { 11, 0,1, 2,0 } }, // -7
+ { "Mountain", "America/Denver", -420, 60, " ", "", { 3, 0,2, 2,0 }, { 11, 0,1, 2,0 } }, // -7
+ { "Mountain", "America/Denver", -420, 60, " ","2006", { 4, 0,1, 2,0 }, { 10, 0,5, 2,0 } }, // -7
+ { "Mountain", "America/Denver", -420, 60, " ","2007", { 3, 0,2, 2,0 }, { 11, 0,1, 2,0 } }, // -7
+ { "Mountain_Mexico", "America/Chihuahua", -420, 60, " ", "", { 4, 0,1, 2,0 }, { 10, 0,5, 2,0 } }, // -7
+ { "Myanmar", "Asia/Rangoon", 390, 0, " ", "", { 0, 0,0, 0,0 }, { 0, 0,0, 0,0 } }, // 6.5
+ { "North_Central_Asia","Asia/Novosibirsk", 360, 60, " ", "", { 3, 0,5, 2,0 }, { 10, 0,5, 3,0 } }, // 6
+ { "Namibia", "Africa/Windhoek", 120,-60, " ", "", { 4, 0,1, 2,0 }, { 9, 0,1, 2,0 } }, // 2
+ { "Nepal", "Asia/Katmandu", 345, 0, " ", "", { 0, 0,0, 0,0 }, { 0, 0,0, 0,0 } }, // 5.75
+ { "New_Zealand", "Pacific/Auckland", 720, 60, " ", "", { 10, 0,1, 2,0 }, { 3, 0,3, 3,0 } }, // 12
+ { "NST/NDT", "America/St_Johns", -210, 60, " ", "", { 3, 0,2, 0,1 }, { 11, 0,1, 0,1 } }, // -3.5
+ { "NST/NDT", "America/St_Johns", -210, 60, " ","2006", { 4, 0,1, 0,1 }, { 10, 0,5, 0,0 } }, // -3.5
+ { "NST/NDT", "America/St_Johns", -210, 60, " ","2007", { 3, 0,2, 0,1 }, { 11, 0,1, 0,0 } }, // -3.5
+ { "HNT/HAT", "America/St_Johns", -210, 60, " ", "", { 3, 0,2, 0,1 }, { 11, 0,1, 0,1 } }, // -3.5
+ { "HNT/HAT", "America/St_Johns", -210, 60, " ","2006", { 4, 0,1, 0,1 }, { 10, 0,5, 0,0 } }, // -3.5
+ { "HNT/HAT", "America/St_Johns", -210, 60, " ","2007", { 3, 0,2, 0,1 }, { 11, 0,1, 0,0 } }, // -3.5
+ { "Newfoundland", "America/St_Johns", -210, 60, " ", "", { 3, 0,2, 0,1 }, { 11, 0,1, 0,1 } }, // -3.5
+ { "Newfoundland", "America/St_Johns", -210, 60, " ","2006", { 4, 0,1, 0,1 }, { 10, 0,5, 0,0 } }, // -3.5
+ { "Newfoundland", "America/St_Johns", -210, 60, " ","2007", { 3, 0,2, 0,1 }, { 11, 0,1, 0,0 } }, // -3.5
+ { "North_Asia_East", "Asia/Irkutsk", 480, 60, " ", "", { 3, 0,5, 2,0 }, { 10, 0,5, 3,0 } }, // 8
+ { "North_Asia", "Asia/Krasnoyarsk", 420, 60, " ", "", { 3, 0,5, 2,0 }, { 10, 0,5, 3,0 } }, // 7
+ { "Pacific_SA", "America/Santiago", -240, 60, " ", "", { 10, 6,2,23,59 }, { 3, 6,2,23,59 }}, // -4
+ { "PST/PDT", "America/Los_Angeles", -480, 60, " ", "", { 3, 0,2, 2,0 }, { 11, 0,1, 2,0 } }, // -8
+ { "PST/PDT", "America/Los_Angeles", -480, 60, " ","2006", { 4, 0,1, 2,0 }, { 10, 0,5, 2,0 } }, // -8
+ { "PST/PDT", "America/Los_Angeles", -480, 60, " ","2007", { 3, 0,2, 2,0 }, { 11, 0,1, 2,0 } }, // -8
+ { "HNP/HAP", "America/Los_Angeles", -480, 60, " ", "", { 3, 0,2, 2,0 }, { 11, 0,1, 2,0 } }, // -8
+ { "HNP/HAP", "America/Los_Angeles", -480, 60, " ","2006", { 4, 0,1, 2,0 }, { 10, 0,5, 2,0 } }, // -8
+ { "HNP/HAP", "America/Los_Angeles", -480, 60, " ","2007", { 3, 0,2, 2,0 }, { 11, 0,1, 2,0 } }, // -8
+ { "Pacific", "America/Los_Angeles", -480, 60, " ", "", { 3, 0,2, 2,0 }, { 11, 0,1, 2,0 } }, // -8
+ { "Pacific", "America/Los_Angeles", -480, 60, " ","2006", { 4, 0,1, 2,0 }, { 10, 0,5, 2,0 } }, // -8
+ { "Pacific", "America/Los_Angeles", -480, 60, " ","2007", { 3, 0,2, 2,0 }, { 11, 0,1, 2,0 } }, // -8
+ { "Pacific_Mexico", "America/Tijuana", -480, 60, " ", "", { 4, 0,1, 2,0 }, { 10, 0,5, 2,0 } }, // -8
+ { "Romance", "Europe/Paris", 60, 60, " ", "", { 3, 0,5, 2,0 }, { 10, 0,5, 3,0 } }, // 1
+ { "Russian", "Europe/Moscow", 180, 60, " ", "", { 3, 0,5, 2,0 }, { 10, 0,5, 3,0 } }, // 3
+ { "SA_Eastern", "Etc/GMT+3", -180, 0, " ", "", { 0, 0,0, 0,0 }, { 0, 0,0, 0,0 } }, // -3
+ { "SA_Pacific", "America/Bogota", -300, 0, " ", "", { 0, 0,0, 0,0 }, { 0, 0,0, 0,0 } }, // -5
+ { "SA_Western", "America/La_Paz", -240, 0, " ", "", { 0, 0,0, 0,0 }, { 0, 0,0, 0,0 } }, // -4
+ { "Samoa", "Pacific/Apia", -660, 0, " ", "", { 0, 0,0, 0,0 }, { 0, 0,0, 0,0 } }, // -11
+ { "SE_Asia", "Asia/Bangkok", 420, 0, " ", "", { 0, 0,0, 0,0 }, { 0, 0,0, 0,0 } }, // 7
+ { "Singapore", "Asia/Singapore", 480, 0, " ", "", { 0, 0,0, 0,0 }, { 0, 0,0, 0,0 } }, // 8
+ { "South_Africa", "Africa/Johannesburg", 120, 0, " ", "", { 0, 0,0, 0,0 }, { 0, 0,0, 0,0 } }, // 2
+ { "Sri_Lanka", "Asia/Colombo", 330, 0, " ", "", { 0, 0,0, 0,0 }, { 0, 0,0, 0,0 } }, // 5.5
+ { "Taipei", "Asia/Taipei", 480, 0, " ", "", { 0, 0,0, 0,0 }, { 0, 0,0, 0,0 } }, // 8
+ { "Tasmania", "Australia/Hobart", 600, 60, " ", "", { 10, 0,1, 2,0 }, { 3, 0,5, 3,0 } }, // 10
+ { "Tokyo", "Asia/Tokyo", 540, 0, " ", "", { 0, 0,0, 0,0 }, { 0, 0,0, 0,0 } }, // 9
+ { "Tonga", "Pacific/Tongatapu", 780, 0, " ", "", { 0, 0,0, 0,0 }, { 0, 0,0, 0,0 } }, // 13
+ { "US_Eastern", "Etc/GMT+5", -300, 0, " ", "", { 0, 0,0, 0,0 }, { 0, 0,0, 0,0 } }, // -5
+ { "US_Mountain", "America/Phoenix", -420, 0, " ", "", { 0, 0,0, 0,0 }, { 0, 0,0, 0,0 } }, // -7
+ { "Vladivostok", "Asia/Vladivostok", 600, 60, " ", "", { 3, 0,5, 2,0 }, { 10, 0,5, 3,0 } }, // 10
+ { "West_Australia", "Australia/Perth", 480, 60, " ", "", { 10, 0,5, 2,0 }, { 3, 0,5, 3,0 } }, // 8
+ { "West_Australia", "Australia/Perth", 480, 0, " ","2005", { 0, 0,0, 0,0 }, { 0, 0,0, 0,0 } }, // 8
+ { "West_Australia", "Australia/Perth", 480, 60, " ","2006", { 12,-1,1, 2,0 }, { 1,-1,1, 0,0 } }, // 8
+ { "West_Australia", "Australia/Perth", 480, 60, " ","2007", { 10, 0,5, 2,0 }, { 3, 0,5, 3,0 } }, // 8
+ { "West_Central_Africa","Africa/Lagos", 60, 0, " ", "", { 0, 0,0, 0,0 }, { 0, 0,0, 0,0 } }, // 1
+ { "WET/WEST", "Europe/Berlin", 60, 60, " ", "", { 3, 0,5, 2,0 }, { 10, 0,5, 3,0 } }, // 1
+ { "West_Europe", "Europe/Berlin", 60, 60, " ", "", { 3, 0,5, 2,0 }, { 10, 0,5, 3,0 } }, // 1
+ { "West_Asia", "Asia/Karachi", 300, 0, " ", "", { 0, 0,0, 0,0 }, { 0, 0,0, 0,0 } }, // 5
+ { "West_Pacific", "Pacific/Port_Moresby", 600, 0, " ", "", { 0, 0,0, 0,0 }, { 0, 0,0, 0,0 } }, // 10
+ { "Yakutsk", "Asia/Yakutsk", 540, 60, " ", "", { 3, 0,5, 2,0 }, { 10, 0,5, 3,0 } }, // 9
+ { "A", NULL, 60, 0, "m", "", { 0, 0,0, 0,0 }, { 0, 0,0, 0,0 } }, // 1
+ { "ACDT", NULL, 630, 0, "d", "", { 0, 0,0, 0,0 }, { 0, 0,0, 0,0 } }, // 10.5
+ { "ACST", NULL, 570, 0, "s", "", { 0, 0,0, 0,0 }, { 0, 0,0, 0,0 } }, // 9.5
+ { "ADT", NULL, -180, 0, "d", "", { 0, 0,0, 0,0 }, { 0, 0,0, 0,0 } }, // -3
+ { "AEDT", NULL, 660, 0, "d", "", { 0, 0,0, 0,0 }, { 0, 0,0, 0,0 } }, // 11
+ { "AEST", NULL, 600, 0, "s", "", { 0, 0,0, 0,0 }, { 0, 0,0, 0,0 } }, // 10
+ { "AKDT", NULL, -480, 0, "d", "", { 0, 0,0, 0,0 }, { 0, 0,0, 0,0 } }, // -8
+ { "AKST", NULL, -540, 0, "s", "", { 0, 0,0, 0,0 }, { 0, 0,0, 0,0 } }, // -9
+ { "AST", NULL, -240, 0, "s", "", { 0, 0,0, 0,0 }, { 0, 0,0, 0,0 } }, // -4
+ { "AWST", NULL, 480, 0, " ", "", { 0, 0,0, 0,0 }, { 0, 0,0, 0,0 } }, // 8
+ { "B", NULL, 120, 0, "m", "", { 0, 0,0, 0,0 }, { 0, 0,0, 0,0 } }, // 2
+ { "BST", NULL, 60, 0, " ", "", { 0, 0,0, 0,0 }, { 0, 0,0, 0,0 } }, // 1
+ { "C", NULL, 180, 0, "m", "", { 0, 0,0, 0,0 }, { 0, 0,0, 0,0 } }, // 3
+ { "CDT", NULL, -300, 0, "d", "", { 0, 0,0, 0,0 }, { 0, 0,0, 0,0 } }, // -5
+ { "CEST", NULL, 120, 0, "d", "", { 0, 0,0, 0,0 }, { 0, 0,0, 0,0 } }, // 2
+ { "CET", NULL, 60, 0, "s", "", { 0, 0,0, 0,0 }, { 0, 0,0, 0,0 } }, // 1
+ { "CST", NULL, -360, 0, "s", "", { 0, 0,0, 0,0 }, { 0, 0,0, 0,0 } }, // -6
+ { "CXT", NULL, 420, 0, " ", "", { 0, 0,0, 0,0 }, { 0, 0,0, 0,0 } }, // 7
+ { "D", NULL, 240, 0, "m", "", { 0, 0,0, 0,0 }, { 0, 0,0, 0,0 } }, // 4
+ { "E", NULL, 300, 0, "m", "", { 0, 0,0, 0,0 }, { 0, 0,0, 0,0 } }, // 5
+ { "EDT", NULL, -240, 0, "d", "", { 0, 0,0, 0,0 }, { 0, 0,0, 0,0 } }, // -4
+ { "EEST", NULL, 180, 0, "d", "", { 0, 0,0, 0,0 }, { 0, 0,0, 0,0 } }, // 3
+ { "EET", NULL, 120, 0, "s", "", { 0, 0,0, 0,0 }, { 0, 0,0, 0,0 } }, // 2
+ { "EST", NULL, -300, 0, "s", "", { 0, 0,0, 0,0 }, { 0, 0,0, 0,0 } }, // -5
+ { "F", NULL, 360, 0, "m", "", { 0, 0,0, 0,0 }, { 0, 0,0, 0,0 } }, // 6
+ { "G", NULL, 420, 0, "m", "", { 0, 0,0, 0,0 }, { 0, 0,0, 0,0 } }, // 7
+ { "H", NULL, 480, 0, "m", "", { 0, 0,0, 0,0 }, { 0, 0,0, 0,0 } }, // 8
+ { "HAA", NULL, -180, 0, "d", "", { 0, 0,0, 0,0 }, { 0, 0,0, 0,0 } }, // -3
+ { "HAC", NULL, -300, 0, "d", "", { 0, 0,0, 0,0 }, { 0, 0,0, 0,0 } }, // -5
+ { "HADT", NULL, -540, 0, "d", "", { 0, 0,0, 0,0 }, { 0, 0,0, 0,0 } }, // -9
+ { "HAE", NULL, -240, 0, "d", "", { 0, 0,0, 0,0 }, { 0, 0,0, 0,0 } }, // -4
+ { "HAP", NULL, -420, 0, "d", "", { 0, 0,0, 0,0 }, { 0, 0,0, 0,0 } }, // -7
+ { "HAR", NULL, -360, 0, "d", "", { 0, 0,0, 0,0 }, { 0, 0,0, 0,0 } }, // -6
+ { "HAST", NULL, -600, 0, "s", "", { 0, 0,0, 0,0 }, { 0, 0,0, 0,0 } }, // -10
+ { "HAT", NULL, -90, 0, "d", "", { 0, 0,0, 0,0 }, { 0, 0,0, 0,0 } }, // -1.5
+ { "HAY", NULL, -480, 0, "d", "", { 0, 0,0, 0,0 }, { 0, 0,0, 0,0 } }, // -8
+ { "HNA", NULL, -240, 0, "s", "", { 0, 0,0, 0,0 }, { 0, 0,0, 0,0 } }, // -4
+ { "HNC", NULL, -360, 0, "s", "", { 0, 0,0, 0,0 }, { 0, 0,0, 0,0 } }, // -6
+ { "HNE", NULL, -300, 0, "s", "", { 0, 0,0, 0,0 }, { 0, 0,0, 0,0 } }, // -5
+ { "HNP", NULL, -480, 0, "s", "", { 0, 0,0, 0,0 }, { 0, 0,0, 0,0 } }, // -8
+ { "HNR", NULL, -420, 0, "s", "", { 0, 0,0, 0,0 }, { 0, 0,0, 0,0 } }, // -7
+ { "HNT", NULL, -150, 0, "s", "", { 0, 0,0, 0,0 }, { 0, 0,0, 0,0 } }, // -2.5
+ { "HNY", NULL, -540, 0, "s", "", { 0, 0,0, 0,0 }, { 0, 0,0, 0,0 } }, // -9
+ { "I", NULL, 540, 0, "m", "", { 0, 0,0, 0,0 }, { 0, 0,0, 0,0 } }, // 9
+ { "IST", NULL, 60, 0, " ", "", { 0, 0,0, 0,0 }, { 0, 0,0, 0,0 } }, // 1
+ { "K", NULL, 600, 0, "m", "", { 0, 0,0, 0,0 }, { 0, 0,0, 0,0 } }, // 10
+ { "L", NULL, 660, 0, "m", "", { 0, 0,0, 0,0 }, { 0, 0,0, 0,0 } }, // 11
+ { "M", NULL, 720, 0, "m", "", { 0, 0,0, 0,0 }, { 0, 0,0, 0,0 } }, // 12
+ { "MDT", NULL, -360, 0, "d", "", { 0, 0,0, 0,0 }, { 0, 0,0, 0,0 } }, // -6
+ { "MESZ", NULL, 120, 0, "d", "", { 0, 0,0, 0,0 }, { 0, 0,0, 0,0 } }, // 2
+ { "MEZ", NULL, 60, 0, "s", "", { 0, 0,0, 0,0 }, { 0, 0,0, 0,0 } }, // 1
+ { "MST", NULL, -420, 0, "s", "", { 0, 0,0, 0,0 }, { 0, 0,0, 0,0 } }, // -7
+ { "N", NULL, -60, 0, "m", "", { 0, 0,0, 0,0 }, { 0, 0,0, 0,0 } }, // -1
+ { "NDT", NULL, -90, 0, "d", "", { 0, 0,0, 0,0 }, { 0, 0,0, 0,0 } }, // -1.5
+ { "NFT", NULL, 690, 0, " ", "", { 0, 0,0, 0,0 }, { 0, 0,0, 0,0 } }, // 11.5
+ { "NST", NULL, -150, 0, "s", "", { 0, 0,0, 0,0 }, { 0, 0,0, 0,0 } }, // -2.5
+ { "O", NULL, -120, 0, "m", "", { 0, 0,0, 0,0 }, { 0, 0,0, 0,0 } }, // -2
+ { "P", NULL, -180, 0, "m", "", { 0, 0,0, 0,0 }, { 0, 0,0, 0,0 } }, // -3
+ { "PDT", NULL, -420, 0, "d", "", { 0, 0,0, 0,0 }, { 0, 0,0, 0,0 } }, // -7
+ { "PST", NULL, -480, 0, "s", "", { 0, 0,0, 0,0 }, { 0, 0,0, 0,0 } }, // -8
+ { "Q", NULL, -240, 0, "m", "", { 0, 0,0, 0,0 }, { 0, 0,0, 0,0 } }, // -4
+ { "R", NULL, -300, 0, "m", "", { 0, 0,0, 0,0 }, { 0, 0,0, 0,0 } }, // -5
+ { "S", NULL, -360, 0, "m", "", { 0, 0,0, 0,0 }, { 0, 0,0, 0,0 } }, // -6
+ { "T", NULL, -420, 0, "m", "", { 0, 0,0, 0,0 }, { 0, 0,0, 0,0 } }, // -7
+ { "U", NULL, -480, 0, "m", "", { 0, 0,0, 0,0 }, { 0, 0,0, 0,0 } }, // -8
+ { "UTC", NULL, 0, 0, " ", "", { 0, 0,0, 0,0 }, { 0, 0,0, 0,0 } }, // 0
+ { "V", NULL, -540, 0, "m", "", { 0, 0,0, 0,0 }, { 0, 0,0, 0,0 } }, // -9
+ { "W", NULL, -600, 0, "m", "", { 0, 0,0, 0,0 }, { 0, 0,0, 0,0 } }, // -10
+ { "WEST", NULL, 60, 0, "d", "", { 0, 0,0, 0,0 }, { 0, 0,0, 0,0 } }, // 1
+ { "WET", NULL, 0, 0, "s", "", { 0, 0,0, 0,0 }, { 0, 0,0, 0,0 } }, // 0
+ { "WST", NULL, 480, 0, " ", "", { 0, 0,0, 0,0 }, { 0, 0,0, 0,0 } }, // 8
+ { "X", NULL, -660, 0, "m", "", { 0, 0,0, 0,0 }, { 0, 0,0, 0,0 } }, // -11
+ { "Y", NULL, -720, 0, "m", "", { 0, 0,0, 0,0 }, { 0, 0,0, 0,0 } }, // -12
+ { "Z", NULL, 0, 0, "m", "", { 0, 0,0, 0,0 }, { 0, 0,0, 0,0 } }, // 0
+};
+
+#endif // TIMEZONES_INTERNAL
+
+#endif // TZ_TABLE_H
+/* eof */
diff --git a/src/sysync/uiapi.cpp b/src/sysync/uiapi.cpp
new file mode 100755
index 0000000..1c6a7ec
--- /dev/null
+++ b/src/sysync/uiapi.cpp
@@ -0,0 +1,131 @@
+/*
+ * File: uiapi.cpp
+ *
+ * Author: Beat Forster (bfo@synthesis.ch)
+ *
+ * TUI_Api class
+ * Bridge to user programmable interface
+ *
+ * Copyright (c) 2007-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ */
+
+/*
+ * The "TUI_Api" class acts as a standard interface between
+ * the SySync Server and a (user programmable) module "sync_uiapi".
+ *
+ * It is possible to have more than one (identical) interface module,
+ * either packed into a DLL or directly linked to the server
+ * (or combined).
+ *
+ */
+
+
+#include "uiapi.h"
+#include "sync_uiapi.h"
+
+
+namespace sysync {
+
+
+/* ---- "DB_Api_Config" implementation ------------------------------------- */
+TUI_Api:: TUI_Api() { fConnected= false; uCreated= false; } // constructor
+TUI_Api::~TUI_Api() { /* empty */ } // destructor
+
+
+
+/* Create a context */
+TSyError TUI_Api::CreateContext( cAppCharP uiName, TDB_Api_Config &config )
+{
+ typedef TSyError (*CreateU_Func)( CContext *uContext,
+ cAppCharP uiName,
+ DB_Callback uCB );
+
+ if (uCreated) return DB_Forbidden;
+ uName= uiName; // make a local copy
+
+ dm= &config.m; // assign reference to the methods
+ CreateU_Func p= (CreateU_Func)dm->ui.UI_CreateContext;
+
+ DB_Callback uCB= &fCB.Callback;
+ CContext* uc= &uCB->mContext; // store it at a temporary var
+ *uc= 0; // set it to check later, if changed
+ uCB->cContext= config.mContext; // inherit info
+ uContext= 0; uCB->mContext= config.fCB.Callback.mContext;
+ TSyError err= p( &uContext, uName.c_str(), uCB );
+ if (!err) {
+ uCreated= true;
+ if (*uc==0) *uc= uContext; // assign for datastores, but only if not assigned in plug-in module
+ } // if
+
+ return err;
+} // CreateContext
+
+
+
+// Run the UI API
+TSyError TUI_Api::RunContext()
+{
+ if (!uCreated) return DB_Forbidden;
+
+ Context_Func p= (Context_Func)dm->ui.UI_RunContext;
+ TSyError err= p( uContext );
+ return err;
+} // RunContext
+
+
+
+TSyError TUI_Api::DeleteContext()
+{
+ if (!uCreated) return DB_Forbidden;
+
+ Context_Func p= (Context_Func)dm->ui.UI_DeleteContext;
+ TSyError err= p( uContext );
+ if (!err) uCreated= false;
+ return err;
+} // DeleteContext
+
+
+
+// Connect the UI API
+TSyError TUI_Api::Connect( cAppCharP moduleName,
+ cAppCharP mContextName, bool aIsLib, bool allowDLL )
+{
+ m= new TDB_Api_Config;
+ m->fCB= fCB;
+
+ CContext gContext= 0;
+ TSyError err= m->Connect( moduleName, gContext, mContextName, aIsLib,allowDLL );
+
+ if (!err) {
+ err= CreateContext( moduleName, *m );
+ } // if
+
+ if (err) delete m;
+ else fConnected= true;
+
+
+ printf( "TUI connected err=%d\n", err );
+ return err;
+} // Connect
+
+
+// Disconnect the UI API
+TSyError TUI_Api::Disconnect()
+{
+ if (!fConnected) return DB_Forbidden;
+
+ TSyError err= DeleteContext();
+ if (!err) {
+ err= m->Disconnect();
+ if (!err) { delete m; fConnected= false; }
+ } // if
+
+ printf( "TUI disconnected err=%d\n", err );
+ return err;
+} // Disconnect
+
+
+
+} // namespace
+/* eof */
diff --git a/src/sysync/uiapi.h b/src/sysync/uiapi.h
new file mode 100644
index 0000000..dedd364
--- /dev/null
+++ b/src/sysync/uiapi.h
@@ -0,0 +1,70 @@
+/*
+ * File: uiapi.h
+ *
+ * Author: Beat Forster (bfo@synthesis.ch)
+ *
+ * TUI_Api class
+ * Bridge to user programmable interface
+ *
+ * Copyright (c) 2007-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ */
+
+/*
+ * The "TUI_Api" class acts as a standard interface between
+ * the SySync Server and a (user programmable) module "sync_uiapi".
+ *
+ * It is possible to have more than one (identical) interface module,
+ * either packed into a DLL or directly linked to the server
+ * (or combined).
+ *
+ */
+
+
+#ifndef UI_API_H
+#define UI_API_H
+
+// access to the definitions of the interface
+#include "sync_dbapidef.h"
+#include "dbapi.h"
+
+
+namespace sysync {
+
+
+class TUI_Api
+{
+ public:
+ TUI_Api(); // constructor
+ ~TUI_Api(); // destructor
+
+ // Connect to the plug-in <moduleName> with <mContextName>
+ TSyError Connect( const char* moduleName, const char* mContextName= "", bool aIsLib= false, bool allowDLL= true );
+ bool Connected() { return fConnected; } // read status of <fConnected>
+
+ // Run the UI API
+ TSyError RunContext();
+
+ // Disconnect the UI API
+ TSyError Disconnect();
+
+ TDB_Api_Callback fCB; // Callback wrapper
+
+ private:
+ TSyError CreateContext( const char* uiName, TDB_Api_Config &config );
+ TSyError DeleteContext();
+
+ bool fConnected; // if successfully connected to module
+ TDB_Api_Config* m; // the module behind
+ API_Methods* dm; // local reference to the API methods
+
+ CContext uContext; // The UI context
+ bool uCreated; // if successfully connected to ui context
+ string uName; // local copy of <uiName>
+}; // class TUI_Api
+
+
+
+} // namespace
+#endif // UI_API_H
+/* eof */
diff --git a/src/sysync/vcalendaritemtype.cpp b/src/sysync/vcalendaritemtype.cpp
new file mode 100755
index 0000000..bb1a6e5
--- /dev/null
+++ b/src/sysync/vcalendaritemtype.cpp
@@ -0,0 +1,263 @@
+/*
+ * File: VCalendarItemType.cpp
+ *
+ * Author: Lukas Zeller (luz@synthesis.ch)
+ *
+ * TVCalendarItemType
+ * vCalendar item type, based on MIME-DIR Item Type, uses
+ * TMultiFieldItem as data item.
+ *
+ * Copyright (c) 2001-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ * 2001-09-25 : luz : created
+ *
+ */
+
+// includes
+#include "prefix_file.h"
+
+#include "sysync.h"
+#include "vcalendaritemtype.h"
+
+#include "rrules.h"
+
+using namespace sysync;
+
+namespace sysync {
+
+/* version info table */
+const struct {
+ sInt32 profilemode;
+ const char* typetext;
+ const char* versiontext;
+} VCalendarVersionInfo[numVCalendarVersions-1] = {
+ // vCalendar 1.0
+ { PROFILEMODE_OLD, "text/x-vcalendar","1.0" },
+ // iCalendar 2.0
+ { PROFILEMODE_MIMEDIR, "text/calendar","2.0" }
+};
+
+
+// available vCard versions
+const char * const vCalendarVersionNames[numVCalendarVersions] = {
+ "1.0",
+ "2.0",
+ "none"
+};
+
+
+// VCalendar config
+
+// init defaults
+void TVCalendarTypeConfig::clear(void)
+{
+ // clear properties
+ fVCalendarVersion=vcalendar_vers_none;
+ // clear inherited
+ inherited::clear();
+} // TVCalendarTypeConfig::clear
+
+
+// create Sync Item Type of appropriate type from config
+TSyncItemType *TVCalendarTypeConfig::newSyncItemType(TSyncSession *aSessionP, TSyncDataStore *aDatastoreP)
+{
+ return
+ new TVCalendarItemType(
+ aSessionP,
+ this,
+ fTypeName.c_str(),
+ fTypeVersion.c_str(),
+ aDatastoreP,
+ fMIMEProfileP->fFieldListP
+ );
+} // TVCalendarTypeConfig::newSyncItemType
+
+
+// resolve (note: needed even if not configurable!)
+void TVCalendarTypeConfig::localResolve(bool aLastPass)
+{
+ // pre-set profile mode if a predefined vCard mode is selected
+ if (fVCalendarVersion!=vcalendar_vers_none) {
+ fProfileMode = VCalendarVersionInfo[fVCalendarVersion].profilemode;
+ // also set these, but getTypeName()/getTypeVers() do not use them (but required for syntax check)
+ fTypeName = VCalendarVersionInfo[fVCalendarVersion].typetext;
+ fTypeVersion = VCalendarVersionInfo[fVCalendarVersion].versiontext;
+ }
+ // resolve inherited
+ inherited::localResolve(aLastPass);
+} // TVCalendarTypeConfig::localResolve
+
+
+#ifdef CONFIGURABLE_TYPE_SUPPORT
+
+
+// config element parsing
+bool TVCalendarTypeConfig::localStartElement(const char *aElementName, const char **aAttributes, sInt32 aLine)
+{
+ // checking the elements
+ if (strucmp(aElementName,"version")==0)
+ expectEnum(sizeof(fVCalendarVersion),&fVCalendarVersion,vCalendarVersionNames,numVCalendarVersions);
+ else
+ return TMIMEDirTypeConfig::localStartElement(aElementName,aAttributes,aLine);
+ // ok
+ return true;
+} // TVCalendarTypeConfig::localStartElement
+
+
+#endif
+
+
+/*
+ * Implementation of TVCalendarItemType
+ */
+
+/* public TVCalendarItemType members */
+
+
+// private helper
+TVCalendarVersion TVCalendarItemType::getVCalenderVersionByMode(sInt32 aMode)
+{
+ if (aMode!=PROFILEMODE_DEFAULT) {
+ for (int v=vcalendar_vers_1_0; v<numVCalendarVersions-1; v++) {
+ if (VCalendarVersionInfo[v].profilemode==aMode)
+ return (TVCalendarVersion)v;
+ }
+ }
+ // not found or default mode
+ return fVCalendarVersion; // return the configured version
+} // TVCalendarItemType::getVCalenderVersionByMode
+
+
+// - get type name / vers
+cAppCharP TVCalendarItemType::getTypeName(sInt32 aMode)
+{
+ TVCalendarVersion v = getVCalenderVersionByMode(aMode);
+ if (v!=vcalendar_vers_none)
+ return VCalendarVersionInfo[v].typetext;
+ else
+ return inherited::getTypeName(aMode);
+} // TVCalendarItemType::getTypeName
+
+
+cAppCharP TVCalendarItemType::getTypeVers(sInt32 aMode)
+{
+ TVCalendarVersion v = getVCalenderVersionByMode(aMode);
+ if (v!=vcalendar_vers_none)
+ return VCalendarVersionInfo[v].versiontext;
+ else
+ return inherited::getTypeVers(aMode);
+} // TVCalendarItemType::getTypeVers
+
+
+// relaxed type comparison, taking into account common errors in real-world implementations
+bool TVCalendarItemType::supportsType(const char *aName, const char *aVers, bool aVersMustMatch)
+{
+ bool match = inherited::supportsType(aName,aVers,aVersMustMatch);
+ if (!match) {
+ // no exact match, but also check for unambiguos misstyped variants
+ if (aVers && strstr(aName,"calendar")!=NULL) {
+ // if "calendar" is in type name, version alone is sufficient to identify the vCalendar version,
+ // even if the type string is wrong, like mixing "x-vcalendar" and "calendar"
+ match = strucmp(getTypeVers(),aVers)==0;
+ if (match) {
+ PDEBUGPRINTFX(DBG_ERROR,(
+ "Warning: fuzzy type match: probably misspelt %s version %s detected as correct type %s version %s",
+ aName,aVers,
+ getTypeName(),getTypeVers()
+ ));
+ }
+ }
+ }
+ // return result now
+ return match;
+} // TVCalendarItemType::supportsType
+
+
+// try to extract a version string from actual item data, NULL if none
+bool TVCalendarItemType::versionFromData(SmlItemPtr_t aItemP, string &aString)
+{
+ // try to extract version
+ return parseForProperty(aItemP,"VERSION",aString);
+}
+
+
+#ifdef APP_CAN_EXPIRE
+
+// try to extract a version string from actual item data, NULL if none
+sInt32 TVCalendarItemType::expiryFromData(SmlItemPtr_t aItemP, lineardate_t &aDat)
+{
+ string rev;
+ aDat=0; // default to no date
+ // try to extract version
+ if (!parseForProperty(aItemP,"LAST-MODIFIED",rev)) return 0; // no date, is ok
+ lineartime_t t;
+ timecontext_t tctx;
+ if (ISO8601StrToTimestamp(rev.c_str(),t,tctx)==0) return 0; // bad format, is ok;
+ aDat=t/linearDateToTimeFactor; // mod date
+ #ifdef EXPIRES_AFTER_DATE
+ sInt16 y,mo,d;
+ lineardate2date(aDat,&y,&mo,&d);
+ // check if this is after expiry date
+ long v=SCRAMBLED_EXPIRY_VALUE;
+ v =
+ (y-1720)*12*42+
+ (mo-1)*42+
+ (d+7);
+ v=v-SCRAMBLED_EXPIRY_VALUE;
+ return (v>0 ? v : 0);
+ #else
+ return 0; // not expired
+ #endif
+} // TVCalendarItemType::expiryFromData
+
+#endif // APP_CAN_EXPIRE
+
+
+#ifdef OBJECT_FILTERING
+
+// get field index of given filter expression identifier.
+sInt16 TVCalendarItemType::getFilterIdentifierFieldIndex(const char *aIdentifier, uInt16 aIndex)
+{
+ // check if explicit field level identifier
+ if (strucmp(aIdentifier,"F.",2)==0) {
+ // explicit field identifier, skip property lookup
+ return TMultiFieldItemType::getFilterIdentifierFieldIndex(aIdentifier+2,aIndex);
+ }
+ else {
+ // translate SyncML-defined abstracts
+ if (strucmp(aIdentifier,"START")==0)
+ return inherited::getFilterIdentifierFieldIndex("DTSTART",0);
+ else if (strucmp(aIdentifier,"END")==0)
+ return inherited::getFilterIdentifierFieldIndex("DTEND",0);
+ }
+ // simply search for matching property names
+ return inherited::getFilterIdentifierFieldIndex(aIdentifier,aIndex);
+} // TVCalendarItemType::getFilterIdentifierFieldIndex
+
+#endif
+
+
+// helper to create same-typed instance via base class
+TSyncItemType *TVCalendarItemType::newCopyForSameType(
+ TSyncSession *aSessionP, // the session
+ TSyncDataStore *aDatastoreP // the datastore
+)
+{
+ // create new itemtype of appropriate derived class type that can handle
+ // this type
+ MP_RETURN_NEW(TVCalendarItemType,DBG_OBJINST,"TVCalendarItemType",TVCalendarItemType(
+ aSessionP,
+ fTypeConfigP,
+ getTypeName(),
+ getTypeVers(),
+ aDatastoreP,
+ fFieldDefinitionsP
+ ));
+} // TVCalendarItemType::newCopyForSameType
+
+
+} // namespace sysync
+
+/* end of TVCalendarItemType implementation */
+
+// eof
diff --git a/src/sysync/vcalendaritemtype.h b/src/sysync/vcalendaritemtype.h
new file mode 100755
index 0000000..69d92cf
--- /dev/null
+++ b/src/sysync/vcalendaritemtype.h
@@ -0,0 +1,122 @@
+/*
+ * File: VCalendarItemType.h
+ *
+ * Author: Lukas Zeller (luz@synthesis.ch)
+ *
+ * TVCalendarItemType
+ * vCalendar item type, based on MIME-DIR Item Type, uses
+ * TMultiFieldItem as data item.
+ *
+ * Copyright (c) 2001-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ * 2001-09-25 : luz : created
+ *
+ */
+
+#ifndef VCalendarItemType_H
+#define VCalendarItemType_H
+
+// includes
+#include "mimediritemtype.h"
+
+using namespace sysync;
+
+namespace sysync {
+
+// define local conversion mode for MIME-derivate
+#define CONVMODE_RRULE CONVMODE_MIME_DERIVATES+0
+
+
+// vCalendar variants
+typedef enum {
+ vcalendar_vers_1_0,
+ vcalendar_vers_2_0,
+ vcalendar_vers_none,
+ numVCalendarVersions
+} TVCalendarVersion;
+
+
+// Vcalendar based datatype
+class TVCalendarTypeConfig : public TMIMEDirTypeConfig
+{
+ typedef TMIMEDirTypeConfig inherited;
+public:
+ TVCalendarTypeConfig(const char *aElementName, TConfigElement *aParentElementP) :
+ TMIMEDirTypeConfig(aElementName,aParentElementP) {};
+ // properties
+ TVCalendarVersion fVCalendarVersion;
+ // public functions
+ // - create Sync Item Type of appropriate type from config
+ virtual TSyncItemType *newSyncItemType(TSyncSession *aSessionP, TSyncDataStore *aDatastoreP);
+protected:
+ #ifdef CONFIGURABLE_TYPE_SUPPORT
+ // check config elements
+ virtual bool localStartElement(const char *aElementName, const char **aAttributes, sInt32 aLine);
+ #endif
+ virtual void localResolve(bool aLastPass);
+ virtual void clear();
+}; // TVCalendarTypeConfig
+
+
+const uInt16 ity_vcalendar=103; // must be unique
+
+class TVCalendarItemType: public TMimeDirItemType
+{
+ typedef TMimeDirItemType inherited;
+public:
+ // constructor
+ TVCalendarItemType(
+ TSyncSession *aSessionP,
+ TDataTypeConfig *aTypeConfigP,
+ const char *aCTType,
+ const char *aVerCT,
+ TSyncDataStore *aRelatedDatastoreP,
+ TFieldListConfig *aFieldDefinitions // field definitions
+ ) : TMimeDirItemType(
+ aSessionP,
+ aTypeConfigP,
+ aCTType,
+ aVerCT,
+ aRelatedDatastoreP,
+ aFieldDefinitions
+ ) { fVCalendarVersion = static_cast<TVCalendarTypeConfig *>(aTypeConfigP)->fVCalendarVersion; };
+ // destructor
+ virtual ~TVCalendarItemType() {};
+ // access to type
+ virtual uInt16 getTypeID(void) const { return ity_vcalendar; };
+ virtual bool isBasedOn(uInt16 aItemTypeID) const { return aItemTypeID==ity_vcalendar ? true : TMimeDirItemType::isBasedOn(aItemTypeID); };
+ // get type name / vers
+ virtual cAppCharP getTypeName(sInt32 aMode=0);
+ virtual cAppCharP getTypeVers(sInt32 aMode=0);
+ // differentiation between implemented and just descriptive TSyncTypeItems
+ virtual bool isImplemented(void) { return true; }; // vCard is an implemented data type
+ // relaxed type comparison, taking into account common errors in real-world implementations
+ virtual bool supportsType(const char *aName, const char *aVers, bool aVersMustMatch=false);
+ // try to extract a version string from actual item data, false if none
+ virtual bool versionFromData(SmlItemPtr_t aItemP, string &aString);
+ #ifdef APP_CAN_EXPIRE
+ // test if modified date of item is expired
+ virtual sInt32 expiryFromData(SmlItemPtr_t aItemP, lineardate_t &aDat);
+ #endif
+ // helper to create same-typed instance via base class
+ // MUST BE IMPLEMENTED IN ALL DERIVED CLASSES!
+ virtual TSyncItemType *newCopyForSameType(
+ TSyncSession *aSessionP, // the session
+ TSyncDataStore *aDatastoreP // the datastore
+ );
+ #ifdef OBJECT_FILTERING
+ // find field index for filter identifier
+ virtual sInt16 getFilterIdentifierFieldIndex(const char *aIdentifier, uInt16 aIndex);
+ #endif
+private:
+ TVCalendarVersion getVCalenderVersionByMode(sInt32 aMode);
+ // vCard version
+ TVCalendarVersion fVCalendarVersion;
+}; // TVCalendarItemType
+
+
+} // namespace sysync
+
+#endif // VCalendarItemType_H
+
+// eof
diff --git a/src/sysync/vcarditemtype.cpp b/src/sysync/vcarditemtype.cpp
new file mode 100755
index 0000000..9108304
--- /dev/null
+++ b/src/sysync/vcarditemtype.cpp
@@ -0,0 +1,288 @@
+/*
+ * File: VCardItemType.cpp
+ *
+ * Author: Lukas Zeller (luz@synthesis.ch)
+ *
+ * TVCardItemType
+ * vCard item type, based on MIME-DIR Item Type, uses
+ * TMultiFieldItem as data item.
+ *
+ * Copyright (c) 2001-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ * 2001-08-20 : luz : created
+ *
+ */
+
+// includes
+#include "prefix_file.h"
+
+#include "sysync.h"
+#include "vcarditemtype.h"
+
+
+using namespace sysync;
+
+
+/* version info table */
+const struct {
+ sInt32 profilemode;
+ const char* typetext;
+ const char* versiontext;
+} VCardVersionInfo[numVCardVersions-1] = {
+ // vCard 2.1
+ { PROFILEMODE_OLD, "text/x-vcard","2.1" },
+ // vCard 3.0
+ { PROFILEMODE_MIMEDIR, "text/vcard","3.0" }
+};
+
+
+// available vCard versions
+const char * const vCardVersionNames[numVCardVersions] = {
+ "2.1",
+ "3.0",
+ "none"
+};
+
+
+// VCard config
+
+// init defaults
+void TVCardTypeConfig::clear(void)
+{
+ // clear properties
+ fVCardVersion=vcard_vers_none;
+ // clear inherited
+ inherited::clear();
+} // TVCardTypeConfig::clear
+
+
+// create Sync Item Type of appropriate type from config
+TSyncItemType *TVCardTypeConfig::newSyncItemType(TSyncSession *aSessionP, TSyncDataStore *aDatastoreP)
+{
+ return
+ new TVCardItemType(
+ aSessionP,
+ this,
+ fTypeName.c_str(),
+ fTypeVersion.c_str(),
+ aDatastoreP,
+ fMIMEProfileP->fFieldListP
+ );
+} // TVCardTypeConfig::newSyncItemType
+
+
+// resolve (note: needed even if not configurable!)
+void TVCardTypeConfig::localResolve(bool aLastPass)
+{
+ // pre-set profile mode if a predefined vCard mode is selected
+ if (fVCardVersion!=vcard_vers_none) {
+ fProfileMode=VCardVersionInfo[fVCardVersion].profilemode;
+ // also set these, but getTypeName()/getTypeVers() do not use them (but required for syntax check)
+ fTypeName = VCardVersionInfo[fVCardVersion].typetext;
+ fTypeVersion = VCardVersionInfo[fVCardVersion].versiontext;
+ }
+ // resolve inherited
+ inherited::localResolve(aLastPass);
+} // TVCardTypeConfig::localResolve
+
+
+#ifdef CONFIGURABLE_TYPE_SUPPORT
+
+// config element parsing
+bool TVCardTypeConfig::localStartElement(const char *aElementName, const char **aAttributes, sInt32 aLine)
+{
+ // checking the elements
+ if (strucmp(aElementName,"version")==0)
+ expectEnum(sizeof(fVCardVersion),&fVCardVersion,vCardVersionNames,numVCardVersions);
+ else
+ return TMIMEDirTypeConfig::localStartElement(aElementName,aAttributes,aLine);
+ // ok
+ return true;
+} // TVCardTypeConfig::localStartElement
+
+#endif
+
+
+
+
+/*
+ * Implementation of TVCardItemType
+ */
+
+
+// private helper
+TVCardVersion TVCardItemType::getVCardVersionByMode(sInt32 aMode)
+{
+ if (aMode!=PROFILEMODE_DEFAULT) {
+ for (int v=vcard_vers_2_1; v<numVCardVersions-1; v++) {
+ if (VCardVersionInfo[v].profilemode==aMode)
+ return (TVCardVersion)v;
+ }
+ }
+ // not found or default mode
+ return fVCardVersion; // return the configured version
+} // TVCardItemType::getVCalenderVersionByMode
+
+
+// - get type name / vers
+cAppCharP TVCardItemType::getTypeName(sInt32 aMode)
+{
+ TVCardVersion v = getVCardVersionByMode(aMode);
+ if (v!=vcard_vers_none)
+ return VCardVersionInfo[v].typetext;
+ else
+ return inherited::getTypeName(aMode);
+} // TVCardItemType::getTypeName
+
+
+cAppCharP TVCardItemType::getTypeVers(sInt32 aMode)
+{
+ TVCardVersion v = getVCardVersionByMode(aMode);
+ if (v!=vcard_vers_none)
+ return VCardVersionInfo[v].versiontext;
+ else
+ return inherited::getTypeVers(aMode);
+} // TVCardItemType::getTypeVers
+
+
+
+// relaxed type comparison, taking into account common errors in real-world implementations
+bool TVCardItemType::supportsType(const char *aName, const char *aVers, bool aVersMustMatch)
+{
+ bool match = inherited::supportsType(aName,aVers,aVersMustMatch);
+ if (!match) {
+ // no exact match, but also check for unambiguos misstyped variants
+ if (aVers && strstr(aName,"vcard")!=NULL) {
+ // if "vcard" is in type name, version alone is sufficient to identify the vcard version,
+ // even if the type string is wrong, like mixing "x-vcard" and "vcard"
+ match = strucmp(getTypeVers(),aVers)==0;
+ if (match) {
+ PDEBUGPRINTFX(DBG_ERROR,(
+ "Warning: fuzzy type match: probably misspelt %s version %s detected as correct type %s version %s",
+ aName,aVers,
+ getTypeName(),getTypeVers()
+ ));
+ }
+ }
+ }
+ // return result now
+ return match;
+} // TVCardItemType::supportsType
+
+
+// try to extract a version string from actual item data, NULL if none
+bool TVCardItemType::versionFromData(SmlItemPtr_t aItemP, string &aString)
+{
+ // try to extract version
+ return parseForProperty(aItemP,"VERSION",aString);
+} // TVCardItemType::versionFromData
+
+
+#ifdef APP_CAN_EXPIRE
+
+// try to extract a version string from actual item data, NULL if none
+sInt32 TVCardItemType::expiryFromData(SmlItemPtr_t aItemP, lineardate_t &aDat)
+{
+ string rev;
+ aDat=0; // default to no date
+ // try to extract version
+ if (!parseForProperty(aItemP,"REV",rev)) return 0; // no REV, is ok
+ lineartime_t t;
+ timecontext_t tctx;
+ if (ISO8601StrToTimestamp(rev.c_str(),t,tctx)==0) return 0; // bad format, is ok;
+ aDat=t/linearDateToTimeFactor; // mod date
+ #ifdef EXPIRES_AFTER_DATE
+ sInt16 y,mo,d;
+ lineardate2date(aDat,&y,&mo,&d);
+ // check if this is after expiry date
+ long v=SCRAMBLED_EXPIRY_VALUE;
+ v =
+ (y-1720)*12*42+
+ (mo-1)*42+
+ (d+7);
+ v=v-SCRAMBLED_EXPIRY_VALUE;
+ return (v>0 ? v : 0);
+ #else
+ return 0; // not expired
+ #endif
+} // TVCardItemType::expiryFromData
+
+#endif // APP_CAN_EXPIRE
+
+
+#ifdef OBJECT_FILTERING
+
+// get field index of given filter expression identifier.
+sInt16 TVCardItemType::getFilterIdentifierFieldIndex(const char *aIdentifier, uInt16 aIndex)
+{
+ // check if explicit field level identifier
+ if (strucmp(aIdentifier,"F.",2)==0) {
+ // explicit field identifier, skip property lookup
+ return TMultiFieldItemType::getFilterIdentifierFieldIndex(aIdentifier+2,aIndex);
+ }
+ else {
+ // translate SyncML-defined abstracts
+ if (strucmp(aIdentifier,"FAMILY")==0) {
+ TPropertyDefinition *propP = fProfileHandlerP->getProfileDefinition()->getPropertyDef("N");
+ if (propP) {
+ // value with convdef index 0 is family (last) name
+ if (propP->numValues>0)
+ return propP->convdefs[0].fieldid;
+ }
+ return VARIDX_UNDEFINED;
+ }
+ else if (strucmp(aIdentifier,"GIVEN")==0) {
+ TPropertyDefinition *propP = fProfileHandlerP->getProfileDefinition()->getPropertyDef("N");
+ if (propP) {
+ // value with convdef index 1 is given (first) name
+ if (propP->numValues>1)
+ return propP->convdefs[1].fieldid;
+ }
+ return VARIDX_UNDEFINED;
+ }
+ else if (strucmp(aIdentifier,"GROUP")==0) {
+ return inherited::getFilterIdentifierFieldIndex("CATEGORIES",0);
+ }
+ }
+ // simply search for matching property names
+ return inherited::getFilterIdentifierFieldIndex(aIdentifier,aIndex);
+} // TVCardItemType::getFilterIdentifierFieldIndex
+
+
+void TVCardItemType::addFilterCapPropsAndKeywords(SmlPcdataListPtr_t &aFilterKeywords, SmlPcdataListPtr_t &aFilterProps, TTypeVariantDescriptor aVariantDesc)
+{
+ // add my own properties
+ addPCDataStringToList("GROUP", &aFilterKeywords);
+ // add basics
+ inherited::addFilterCapPropsAndKeywords(aFilterKeywords,aFilterProps,aVariantDesc);
+} // TVCardItemType::addFilterCapPropsAndKeywords
+
+
+
+#endif
+
+
+// helper to create same-typed instance via base class
+TSyncItemType *TVCardItemType::newCopyForSameType(
+ TSyncSession *aSessionP, // the session
+ TSyncDataStore *aDatastoreP // the datastore
+)
+{
+ // create new itemtype of appropriate derived class type that can handle
+ // this type
+ MP_RETURN_NEW(TVCardItemType,DBG_OBJINST,"TVCardItemType",TVCardItemType(
+ aSessionP,
+ fTypeConfigP,
+ getTypeName(),
+ getTypeVers(),
+ aDatastoreP,
+ fFieldDefinitionsP
+ ));
+} // TVCardItemType::newCopyForSameType
+
+
+
+
+/* end of TVCardItemType implementation */
+
+// eof
diff --git a/src/sysync/vcarditemtype.h b/src/sysync/vcarditemtype.h
new file mode 100755
index 0000000..c7873c1
--- /dev/null
+++ b/src/sysync/vcarditemtype.h
@@ -0,0 +1,124 @@
+/*
+ * File: VCardItemType.h
+ *
+ * Author: Lukas Zeller (luz@synthesis.ch)
+ *
+ * TVCardItemType
+ * vCard item type, based on MIME-DIR Item Type, uses
+ * TMultiFieldItem as data item.
+ *
+ * Copyright (c) 2001-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ * 2001-08-20 : luz : created
+ *
+ */
+
+#ifndef VCardItemType_H
+#define VCardItemType_H
+
+// includes
+#include "mimediritemtype.h"
+
+
+
+using namespace sysync;
+
+namespace sysync {
+
+
+// vCard variants
+typedef enum {
+ vcard_vers_2_1,
+ vcard_vers_3_0,
+ vcard_vers_none, // must be last as version table has no entry for that
+ numVCardVersions
+} TVCardVersion;
+
+
+
+// Vcard based datatype
+class TVCardTypeConfig : public TMIMEDirTypeConfig
+{
+ typedef TMIMEDirTypeConfig inherited;
+public:
+ TVCardTypeConfig(const char *aElementName, TConfigElement *aParentElementP) :
+ TMIMEDirTypeConfig(aElementName,aParentElementP) {};
+ // properties
+ TVCardVersion fVCardVersion;
+ // public functions
+ // - create Sync Item Type of appropriate type from config
+ virtual TSyncItemType *newSyncItemType(TSyncSession *aSessionP, TSyncDataStore *aDatastoreP);
+protected:
+ #ifdef CONFIGURABLE_TYPE_SUPPORT
+ // check config elements
+ virtual bool localStartElement(const char *aElementName, const char **aAttributes, sInt32 aLine);
+ #endif
+ virtual void localResolve(bool aLastPass);
+ virtual void clear();
+}; // TVCardTypeConfig
+
+
+const uInt16 ity_vcard=104; // must be unique
+
+class TVCardItemType: public TMimeDirItemType
+{
+ typedef TMimeDirItemType inherited;
+public:
+ // constructor
+ TVCardItemType(
+ TSyncSession *aSessionP,
+ TDataTypeConfig *aTypeConfigP,
+ const char *aCTType,
+ const char *aVerCT,
+ TSyncDataStore *aRelatedDatastoreP,
+ TFieldListConfig *aFieldDefinitions // field definitions
+ ) : TMimeDirItemType(
+ aSessionP,
+ aTypeConfigP,
+ aCTType,
+ aVerCT,
+ aRelatedDatastoreP,
+ aFieldDefinitions
+ ) { fVCardVersion = static_cast<TVCardTypeConfig *>(aTypeConfigP)->fVCardVersion; };
+ // destructor
+ virtual ~TVCardItemType() {};
+ // access to type
+ virtual uInt16 getTypeID(void) const { return ity_vcard; };
+ virtual bool isBasedOn(uInt16 aItemTypeID) const { return aItemTypeID==ity_vcard ? true : TMimeDirItemType::isBasedOn(aItemTypeID); };
+ // get type name / vers
+ virtual cAppCharP getTypeName(sInt32 aMode=0);
+ virtual cAppCharP getTypeVers(sInt32 aMode=0);
+ // differentiation between implemented and just descriptive TSyncTypeItems
+ virtual bool isImplemented(void) { return true; }; // vCard is an implemented data type
+ // relaxed type comparison, taking into account common errors in real-world implementations
+ virtual bool supportsType(const char *aName, const char *aVers, bool aVersMustMatch=false);
+ // try to extract a version string from actual item data, false if none
+ virtual bool versionFromData(SmlItemPtr_t aItemP, string &aString);
+ #ifdef APP_CAN_EXPIRE
+ // test if modified date of item is expired
+ virtual sInt32 expiryFromData(SmlItemPtr_t aItemP, lineardate_t &aDat);
+ #endif
+ // helper to create same-typed instance via base class
+ // MUST BE IMPLEMENTED IN ALL DERIVED CLASSES!
+ virtual TSyncItemType *newCopyForSameType(
+ TSyncSession *aSessionP, // the session
+ TSyncDataStore *aDatastoreP // the datastore
+ );
+ #ifdef OBJECT_FILTERING
+ // add extra keywords and property names to filterCap
+ virtual void addFilterCapPropsAndKeywords(SmlPcdataListPtr_t &aFilterKeywords, SmlPcdataListPtr_t &aFilterProps, TTypeVariantDescriptor aVariantDesc);
+ // find field index for filter identifier
+ virtual sInt16 getFilterIdentifierFieldIndex(const char *aIdentifier, uInt16 aIndex);
+ #endif
+private:
+ TVCardVersion getVCardVersionByMode(sInt32 aMode);
+ // vCard version
+ TVCardVersion fVCardVersion;
+}; // TVCardItemType
+
+
+} // namespace sysync
+
+#endif // VCardItemType_H
+
+// eof
diff --git a/src/sysync/vtimezone.cpp b/src/sysync/vtimezone.cpp
new file mode 100644
index 0000000..0cd013e
--- /dev/null
+++ b/src/sysync/vtimezone.cpp
@@ -0,0 +1,838 @@
+/*
+ * File: vtimezone.cpp
+ *
+ * Author: Beat Forster (bfo@synthesis.ch)
+ *
+ * Parser/Generator routines for vTimezone
+ *
+ * Copyright (c) 2006-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ * 2006-03-06 : bfo : created from exctracts from rrules.cpp
+ *
+ */
+
+
+// includes
+#include "prefix_file.h"
+
+// do not import the whole thing to make life easier for standalone apps
+#ifdef FULLY_STANDALONE
+ #include "sysync_globs.h"
+ #include "sysync_debug.h"
+#else
+ #include "sysync.h"
+#endif
+
+//#ifdef SYSYNC_TOOL
+// #include "SDK_support.h"
+//#endif
+
+#include "vtimezone.h"
+#include "rrules.h"
+#include "lineartime.h"
+#include "timezones.h"
+#include "iso8601.h"
+#include "stringutils.h"
+
+
+typedef unsigned long ulong;
+
+
+using namespace sysync;
+
+namespace sysync {
+
+
+struct RType { // RRule block definition
+ char freq;
+ char freqmod;
+ sInt16 interval;
+ fieldinteger_t firstmask;
+ fieldinteger_t lastmask;
+ lineartime_t until;
+ }; // RType
+
+#ifdef SYSYNC_TOOL
+/*! Get boolean as const char string */
+static cAppCharP Bo( bool b )
+{
+ if (b) return "true";
+ else return "false";
+} // Bo
+#endif
+
+
+
+/*! Logging RType info */
+static void RTypeInfo( RType &r, bool ok, TDebugLogger* aLogP )
+{
+ string runt;
+ TimestampToISO8601Str( runt, r.until, TCTX_UTC); // get end time as string
+
+ #ifdef SYSYNC_TOOL
+ ulong* f1= (ulong*)&r.firstmask; // prepare for longlong disp
+ ulong* f2= f1++;
+ ulong* l1= (ulong*)&r.lastmask;
+ ulong* l2= l1++;
+
+ LOGDEBUGPRINTFX( aLogP, DBG_GEN,( "RType ok=%s freq=%c freqmod=%c iv=%d",
+ Bo( ok ), r.freq, r.freqmod, r.interval ) );
+ LOGDEBUGPRINTFX( aLogP, DBG_GEN,( "RType 1st=%08X %08X last=%08X %08X '%s'",
+ *f1,*f2, *l1,*l2, runt.c_str()) );
+ #endif
+} // RTypeInfo
+
+
+
+/*! VTIMEZONE keywords */
+const char* VTZ_ID = "TZID";
+const char* VTZ_BEGIN = "BEGIN";
+const char* VTZ_END = "END";
+const char* VTZ_STD = "STANDARD";
+const char* VTZ_DST = "DAYLIGHT";
+const char* VTZ_RR = "RRULE";
+const char* VTZ_START = "DTSTART";
+const char* VTZ_OFROM = "TZOFFSETFROM";
+const char* VTZ_OTO = "TZOFFSETTO";
+const char* VTZ_NAME = "TZNAME";
+
+
+
+/*! RRULE2toInternal, using RType
+ * Converts vCalendar 2.0 RRULE string into internal recurrence representation
+ */
+static bool RRULE2toInternalR( const char* aText, // RRULE string to be parsed
+ lineartime_t dtstart, // reference date for parsing RRULE
+ RType &r,
+ TDebugLogger* aLogP )
+{
+ timecontext_t untilcontext = TCTX_UNKNOWN;
+ bool ok= RRULE2toInternal( aText, dtstart, TCTX_UNKNOWN,
+ r.freq,r.freqmod,r.interval,r.firstmask,r.lastmask,r.until,untilcontext,
+ aLogP );
+ if (aLogP) RTypeInfo( r, ok, aLogP );
+ return ok;
+} // RRULE2toInternalR
+
+
+/*! internalToRRULE2, using RType
+ * Converts internal recurrence into vCalendar 2.0 RRULE string
+ */
+static bool internalRToRRULE2( string &aString, // receives RRULE string
+ RType r,
+ bool asUTC,
+ TDebugLogger* aLogP )
+{
+ bool ok= internalToRRULE2( aString,
+ r.freq,r.freqmod,r.interval,r.firstmask,r.lastmask,r.until,
+ asUTC, aLogP );
+ #ifdef SYSYNC_TOOL
+ if (aLogP)
+ LOGDEBUGPRINTFX( aLogP, DBG_GEN,( "RType ok=%s aString='%s'", Bo( ok ),
+ aString.c_str() ) );
+ #endif
+
+ return ok;
+} // internalRtoRRULE2
+
+
+
+// ----------------------------------------------------------------------------------------
+/*! Convert mask values into <aDayOfWeek>,<aNth> */
+static void R_to_WD( RType &r, short &aDayOfWeek, short &aNth )
+{
+ int i,j;
+
+ aDayOfWeek= -1;
+ aNth = -1;
+
+ for (i= 0; i<WeeksOfMonth; i++) {
+ for (j= 0; j<DaysOfWeek; j++) {
+ sInt16 index= i*DaysOfWeek + j;
+ if (r.firstmask & ((uInt64)1<<index)) { aDayOfWeek= j; aNth= i+1; return; }
+ } // for
+ } // for
+
+ for (i= 0; i<WeeksOfMonth; i++) {
+ for (j= 0; j<DaysOfWeek; j++) {
+ sInt16 index= i*DaysOfWeek + j;
+ if (r.lastmask & ((uInt64)1<<index)) { aDayOfWeek= j; aNth= WeeksOfMonth-i; return; }
+ } // for
+ } // for
+} // R_to_WD
+
+
+/*! Convert <r>,<tm> into tChange <c> */
+static void Rtm_to_tChange( RType &r, lineartime_t tim, tChange &c )
+{
+ sInt16 mo, da, ho, mi;
+ lineartime2date(tim, NULL, &mo, &da);
+ lineartime2time(tim, &ho, &mi, NULL, NULL);
+
+ c.wMonth = mo;
+ c.wHour = ho;
+ c.wMinute= mi;
+ R_to_WD( r, c.wDayOfWeek, c.wNth );
+ if (c.wDayOfWeek==-1) c.wNth= da;
+} // Rtm_to_tChange
+
+
+
+// ----------------------------------------------------------------------------------------
+/*! Convert <aDayOfWeek>,<aNth> into mask values */
+static void WD_to_R( int aDayOfWeek, int aNth, RType &r )
+{
+ r.firstmask = 0; // default values
+ r.lastmask = 0;
+ sInt16 index= aDayOfWeek % DaysOfWeek;
+
+ bool last= aNth==WeeksOfMonth; // 5 is the last week
+ if (last) r.lastmask = (uInt64)1<< index;
+ else r.firstmask= (uInt64)1<<( index + (aNth-1)*DaysOfWeek );
+} // WD_to_R
+
+
+static void AdaptDay( lineartime_t &tim, tChange c )
+{
+ sInt16 yr, mo, dowk;
+
+ lineartime2date( tim, &yr ,&mo, NULL );
+ tim = date2lineartime( yr, mo, 1 ); // make calculation with the 1st of month
+ dowk= lineartime2weekday ( tim ); // get weekday
+
+ sInt16 da= c.wDayOfWeek - dowk + 1;
+ if (da<1) da+= DaysOfWeek; // this is the 1st occurance of the given weekday
+ da+= (c.wNth-1)*DaysOfWeek;
+
+ AdjustDay( da,mo,yr ); // and ajust <d> within the month
+ tim= date2lineartime( yr,mo,da );
+} // AdaptDay
+
+
+/*! Convert <r>,<tm> into tChange <c> */
+static void tChange_to_Rtm( tChange c, int year, RType &r, lineartime_t &tim )
+{
+ bool noDOW = c.wDayOfWeek==-1;
+ bool noCalc= c.wMonth==0;
+ if (noCalc) c.wMonth= 1;
+
+ sInt16 da= 1;
+ if (noDOW) da= c.wNth;
+
+ tim = date2lineartime( year, c.wMonth, da );
+ lineartime_t timT= time2lineartime( c.wHour, c.wMinute, 0,0 );
+
+ if (!(noCalc || noDOW)) {
+ AdaptDay( tim, c ); // get wDayOfWeek/wNth, fitting for <year>
+ WD_to_R ( c.wDayOfWeek, c.wNth, r );
+ } // if
+
+ tim+= timT; // don't forget time
+} // tChange_to_Rtm
+
+
+
+// ----------------------------------------------------------------------------------------
+/*! Get the <bias> value from TZOFFSFROM/TZOFFSTO strings */
+static bool Get_Bias( string of, string ot, short &bias )
+{
+ bool negative= ot.find( "-",0 )==0;
+ bool positive= ot.find( "+",0 )==0; // needed because positive TZOFFSxxx should have it as per specs
+ if (negative || positive) ot= ot.substr( 1,ot.length()-1);
+
+ string h= ot.substr( 0,2 );
+ string m= ot.substr( 2,2 );
+
+ bias= atoi( h.c_str() )*MinsPerHour + atoi( m.c_str() );
+ if (negative) bias= -bias;
+ return true;
+} // Get_Bias
+
+
+/*! Fill in the TZ info */
+static bool GetTZInfo( cAppCharP aText,
+ cAppCharP aIdent,
+ tChange &c,
+ short &cBias,
+ string &cName,
+ sInt32 aNth, // take nth occurance, -1: take last
+ TDebugLogger* aLogP )
+{
+ RType r;
+ timecontext_t tctx;
+ lineartime_t dtstart, dtH= 0;
+ string a, st;
+
+ if (aNth==-1) { // search for the last (in time)
+ sInt32 i= 1;
+
+ while (true) {
+ a= VStr( aText, aIdent, i ); if (a=="") break;
+ st= VValue( a, VTZ_START ); // - start time
+ if (ISO8601StrToTimestamp( st.c_str(), dtstart, tctx )==0) break;
+ if (dtH<dtstart) { dtH= dtstart; aNth= i; } // the latest one with index
+
+ i++;
+ } // while
+ } // if
+ a= VStr( aText, aIdent, aNth );
+ string rr= VValue( a, VTZ_RR ); // sub items: - RRULE
+ st= VValue( a, VTZ_START ); // - start time
+ string of= VValue( a, VTZ_OFROM ); // - tz offset from
+ string ot= VValue( a, VTZ_OTO ); // - tz offset to
+ cName= VValue( a, VTZ_NAME ); // - tz name
+
+ if (ISO8601StrToTimestamp( st.c_str(), dtstart, tctx )==0) return false;
+ if (!RRULE2toInternalR ( rr.c_str(), dtstart, r, aLogP )) return false;
+ if (!Get_Bias( of,ot, cBias )) return false;
+
+ string vvv;
+ internalRToRRULE2( vvv, r, false, aLogP );
+
+ Rtm_to_tChange( r, dtstart, c );
+ return true;
+} // GetTZInfo
+
+
+/*! Create a property string */
+static string Property( string propertyName, string value ) {
+ return propertyName + ":" + value + "\n";
+} // Property
+
+
+/*! Check, if "BEGIN:value" is available only once */
+static int PMulti( string &aText, string value )
+{
+ string p= Property( VTZ_BEGIN, value );
+ string::size_type
+ n= aText.find( p, 0 ); if (n==string::npos) return 0;
+ n= aText.find( p, n+1 ); if (n==string::npos) return 1;
+ /* else */ return 2;
+} // PMulti
+
+
+/* vTimezones with more than one STANDARD or DAYLIGHT sequence can't be resolved */
+static void MultipleSeq( string aText, int &s, int &d )
+{
+ s= PMulti( aText,VTZ_STD );
+ d= PMulti( aText,VTZ_DST );
+} // MultipleSeq
+
+
+bool VTIMEZONEtoTZEntry( const char* aText, // VTIMEZONE string to be parsed
+ tz_entry &t,
+ string &aStdName,
+ string &aDstName,
+ TDebugLogger* aLogP)
+{
+ t.name = "";
+ t.ident = "";
+ t.dynYear= "CUR";
+ GetTZInfo( aText,VTZ_STD, t.std, t.bias, aStdName, -1, aLogP );
+ GetTZInfo( aText,VTZ_DST, t.dst, t.biasDST, aDstName, -1, aLogP );
+
+ if (t.bias==t.biasDST) ClrDST( t ); // no DST ?
+ else {
+ //%%% luz: t.biasDST is not defined here. Intent of comparison not clear, usually fails and prevents parsing
+ //if (t.bias + t.biasDST!=biasD) ok= false;
+ //%%% luz: instead I think t.biasDST must be CALCULATED here
+ t.biasDST -= t.bias;
+ } // if
+
+ // get TZID as found in VTIMEZONE
+ t.name = VValue( aText, VTZ_ID );
+
+ return true;
+} // VTIMEZONEtoTZEntry
+
+
+/*! Convert VTIMEZONE string ito internal context value */
+bool VTIMEZONEtoInternal( const char* aText, // VTIMEZONE string to be parsed
+ timecontext_t &aContext,
+ GZones* g,
+ TDebugLogger* aLogP,
+ string* aTzidP ) ///< if not NULL, receives TZID as found in VTIMEZONE
+{
+ aContext= tctx_tz_unknown;
+
+ tz_entry t;
+ string stdName,
+ dstName,
+ lName;
+ timecontext_t lContext;
+
+ VTIMEZONEtoTZEntry( aText, t, stdName, dstName, aLogP );
+ if (aTzidP) *aTzidP = t.name; // return the original TZID as found, needed to match with TZID occurences in rest of vCalendar
+
+ bool sC= stdName!="";
+ bool dC= dstName!="";
+
+ string tName = t.name;
+ if (sC || dC) tName = ""; // TZID will be replaced in case of unknown
+ if (sC) tName+= stdName;
+ if (sC && dC) tName+= "/";
+ if (dC) tName+= dstName;
+
+ bool ok = true;
+ bool okM= true;
+ int s,d;
+ MultipleSeq( aText, s,d );
+
+ if (s==0 && d==0) return false;
+ okM= s<=1 && d<=1; // currently not more than one section each is supported
+
+ // find best match for VTIMEZONE: checks name and rules
+ // allows multiple timezone, if last is ok !
+ ok= g->matchTZ(t, aContext);
+
+ if (!ok && !okM) { // store it "as is" if both is not ok
+ ClrDST( t );
+ t.name = aText;
+ t.ident= "$";
+ t.bias = 0;
+ return FoundTZ( t, lName, aContext, g, true );
+ } // if
+
+ // find best match for VTIMEZONE: checks name and rules
+//ok= g->matchTZ(t, aContext);
+
+ #ifdef SYSYNC_TOOL
+ if (ok) {
+ string existing_name;
+ TimeZoneContextToName(aContext, existing_name, g);
+ LOGDEBUGPRINTFX( aLogP, DBG_PARSE,( "found matching time zone with name='%s' tx=%08X %d",
+ existing_name.c_str(), aContext,
+ TCTX_OFFSCONTEXT( aContext ) ) );
+ }
+ #endif
+
+ // if not found, then try again with name and add
+ // the entry; doing a full comparison again is
+ // redundant here, but there is no other way to
+ // add the entry
+ string new_name;
+ t.name = tName;
+ if (!ok) ok= FoundTZ( t, new_name, aContext, g, true );
+
+ if (ok && t.std.wMonth!=0 &&
+ t.dst.wMonth!=0) {
+ tz_entry std;
+ std.name = stdName;
+ std.ident= "s"; // standard
+ std.bias = t.bias;
+ FoundTZ( std, lName,lContext, g, true );
+
+ tz_entry dst;
+ dst.name = dstName;
+ dst.ident= "d"; // daylight saving
+ dst.bias = t.bias + t.biasDST;
+ FoundTZ( dst, lName,lContext, g, true );
+ } // if
+
+ return ok;
+} // VTIMEZONEtoInternal
+
+
+// -----------------------------------------------------------------------------------------
+/*! Create a string with <txt> in-between BEGIN:<value> .. END:<value> */
+static string Encapsuled( string value, string txt )
+{
+ return Property( VTZ_BEGIN, value ) + txt +
+ Property( VTZ_END, value );
+} // Encapsuled
+
+
+/*! Get the hour/minute string of <bias> */
+static string HourMinStr( int bias )
+{
+ const char* form;
+ if (bias>=0) form= "%+03d%02d";
+ else form= "%03d%02d"; // for negative values
+
+ char s[ 10 ];
+ sprintf( s, form, bias / MinsPerHour,
+ bias % MinsPerHour );
+ return s;
+} // HourMinStr
+
+
+/*! Get TZOFFSETFROM/TO strings */
+static string FromTo( int biasFrom, int biasTo )
+{
+ return Property( VTZ_OFROM, HourMinStr( biasFrom ) ) +
+ Property( VTZ_OTO, HourMinStr( biasTo ) );
+} // FromTo
+
+
+/*! Generate a TZ info:
+ * - <t> : the whole tz entry
+ * - <value> : STANDARD/DAYLIGHT
+ * - <aIdent> : "s"/"d"
+ * - <c> : std / dst info
+ * - <y> : staring year
+ * - <aFrom> : bias before changing
+ * - <aTo> : " after "
+ * - <aLogP> : the debug logger
+ */
+static string GenerateTZInfo( tz_entry t,
+ const char* value, const char* aIdent,
+ tChange c, int y,
+ int aFrom, int aTo,
+ GZones *g,
+ TDebugLogger* aLogP )
+{
+ string rTxt, dtStart;
+ RType r;
+ lineartime_t tim;
+ string aName;
+ timecontext_t aContext;
+ bool withDST= strcmp( aIdent," " )!=0;
+
+ r.freq = 'M'; // these are the fixed parameters for TZ
+ r.freqmod = 'W';
+ r.interval = 12;
+ r.until = noLinearTime; // last day
+ r.firstmask= 0; // default values
+ r.lastmask = 0;
+
+ tChange_to_Rtm( c,y, r, tim );
+ TimestampToISO8601Str( dtStart, tim, TCTX_UNKNOWN); // with time, but no offset
+
+ if (withDST) internalRToRRULE2( rTxt, r, false, aLogP );
+
+ t.name = "";
+ t.ident= aIdent;
+ t.bias = aTo; // this is the bias to search for STD/DST name
+ if (!FoundTZ( t, aName, aContext, g )) aName= "";
+
+ string s = Property( VTZ_START, dtStart );
+ if (withDST) s+= Property( VTZ_RR, rTxt );
+ s+= FromTo ( aFrom,aTo );
+ if (withDST &&
+ !aName.empty()) s+= Property( VTZ_NAME, aName );
+
+ return Encapsuled( value, s );
+} // GenerateTZInfo
+
+
+/*! Convert internal context value into VTIMEZONE */
+bool internalToVTIMEZONE( timecontext_t aContext,
+ string &aText, // receives VTIMEZONE string
+ GZones* g,
+ TDebugLogger* aLogP,
+ sInt32 testYear,
+ sInt32 untilYear,
+ cAppCharP aPrefIdent )
+{
+ // %%% note: untilYear needs to be implemented, is without functionality so far
+
+ sInt16 yy= testYear;
+ if (testYear==0) yy= MyYear( g );
+ TzResolveMetaContext( aContext, g ); // we need actual zone, not meta-context
+
+ tz_entry t, tp;
+ GetTZ( aContext, t, g, yy );
+ bool withDST= t.std.wMonth!=0 &&
+ t.dst.wMonth!=0;
+ if (t.ident == "$") { aText= t.name; return true; } // just give it back
+
+ int t_plus = t.bias; const char* id= " ";
+ if (withDST) { t_plus+= t.biasDST; id= "s"; } // there is only an offset with DST
+
+ // time zone start year
+ int y_std= 1967; // this info gets lost in the TZ_Entry system
+ int y_dst= 1987; // hard coded, because there is currently no field to store it
+
+ // modify it, if dyn year is later
+ if (!t.dynYear.empty()) {
+ timecontext_t aDJ= aContext+1;
+ GetTZ ( aDJ, tp, g, -1 );
+
+ // but only if not the first entry of dynYear
+ if (t.name == tp.name &&
+ t.dynYear != tp.dynYear) {
+ y_std= atoi( t.dynYear.c_str() );
+ y_dst= y_std;
+ } // if
+ } // if
+
+ // make sure we get the right TZID string according to aPrefIdent
+ string tzn = t.name;
+ if (aPrefIdent) {
+ TimeZoneContextToName(aContext, tzn, g, aPrefIdent);
+ }
+
+ // at least one STANDARD or DAYLIGHT info is mandatory
+ aText= Property ( VTZ_ID, tzn ) +
+ GenerateTZInfo( t, VTZ_STD, id, t.std, y_std, t_plus,t.bias, g, aLogP );
+
+ if (withDST)
+ aText+= GenerateTZInfo( t, VTZ_DST,"d", t.dst, y_dst, t.bias,t_plus, g, aLogP );
+
+ return false;
+} // internalToVTIMEZONE
+
+
+
+static bool NextStr( string &s, string &nx )
+{
+ string::size_type i= s.find( ";", 0 );
+ if (i==string::npos ) return false; // mismatch
+ nx= s.substr ( 0, i );
+ s = s.substr ( i+1, s.length()-i-1 );
+ return true;
+} // NextStr
+
+
+/*! Convert TZ/DAYLIGHT string into internal context value */
+bool TzDaylightToContext( const char* aText, ///< DAYLIGHT property value to be parsed
+ timecontext_t aStdOffs, ///< Standard (non-DST) offset obtained from TZ
+ timecontext_t &aContext, ///< receives context
+ GZones* g,
+ timecontext_t aPreferredCtx, // preferred context, if rule matches more than one context
+ TDebugLogger* aLog )
+{
+ TzResolveMetaContext( aPreferredCtx, g ); // we need actual zone, not meta-context
+ string s= aText;
+ string hrs, dst, std, l, r, rslt;
+ string::size_type i;
+ tz_entry t, tCopy;
+ timecontext_t cc, ccFirst, ccSlash;
+
+ aContext= aStdOffs; // as default, convert it into a enum TZ
+
+ do {
+ if (s=="FALSE" ) { s= ""; break; } // no DST
+
+ /*
+ i= s.find( ";", 0 ); // TRUE
+ if (i==string::npos ) return false; // mismatch
+ l= s.substr( 0, i );
+ */
+ if (!NextStr( s, l )) return false; // mismatch
+
+ if (l=="FALSE" ) { s= ""; break; } // no DST
+ if (l!="TRUE" ) return false; // either "TRUE" or "FALSE"
+
+ if (!NextStr( s, hrs )) return false; // mismatch
+ if (!NextStr( s, dst )) return false; // mismatch
+ if (!NextStr( s, std )) return false; // mismatch
+
+ int minsDST= MinsPerHour; // %%% not yet perfect for Namibia !!
+
+ /*
+ i= s.find( ";", 0 ); // +XX
+ i= s.find( ";", i+1 ); // DST time
+ i= s.find( ";", i+1 ); // STD time
+
+ if (i==string::npos ) return false; // parsing error
+
+ s= s.substr( i+1, s.length()-i-1 );
+ */
+
+ ISO8601StrToContext( hrs.c_str(), cc );
+ int mins= TCTX_MINOFFSET( cc )-minsDST;
+
+ i= s.find ( ";", 0 );
+ l= s.substr( 0, i );
+ r= s.substr( i+1, s.length()-i-1 );
+ if (l==r) s= l; // twice the same => take it once
+ StringSubst( s, ";", "/" );
+
+ TimeZoneNameToContext( s.c_str(), aContext, g );
+
+ // if it perfectly fits to a named zone, take it
+ if (GetTZ( std,dst, mins,minsDST, t, g )) { ccFirst= TCTX_UNKNOWN; // start with these defaults
+ ccSlash= TCTX_UNKNOWN;
+ cc = TCTX_SYSTEM;
+ while (FoundTZ( t, rslt, cc, g, false, cc )) {
+ if (s==rslt) { aContext= cc; break; }
+ if (s.empty()) {
+ if (ccFirst==TCTX_UNKNOWN) ccFirst= cc;
+ if (ccSlash==TCTX_UNKNOWN) {
+ i= rslt.find( "/",0 ); // a slash TZ would be the best choice
+ if (i!=0 && i!=string::npos) ccSlash= cc;
+ } // if
+ } // if
+ } // while
+
+ if (ccSlash!=TCTX_UNKNOWN) aContext= ccSlash;
+ else if (ccFirst!=TCTX_UNKNOWN) aContext= ccFirst;
+ } // if
+ } while (false);
+
+ // check if it fits to <aPreferredCtx>
+ bool pUnk = TCTX_IS_UNKNOWN ( aPreferredCtx );
+ timecontext_t t_Greenwich= TCTX_ENUMCONTEXT( tctx_tz_Greenwich );
+
+ ccFirst= TCTX_UNKNOWN; // start with these defaults
+ cc = TCTX_SYSTEM;
+
+ if (GetTZ( aContext, t, g )) {
+ if (FoundTZ( t, s, cc, g, false, cc )) { // search by correct name first
+ if (!(cc!=aPreferredCtx && cc==t_Greenwich)) { // take UTC for this case
+ if (pUnk || cc==aPreferredCtx) { aContext= cc; return true; }
+ if (ccFirst==TCTX_UNKNOWN) ccFirst= cc; // keep it, just in case
+ } // if
+ } // if
+ }
+ else {
+ t.bias = TCTX_MINOFFSET( aContext );
+ t.biasDST= 0; // no DST offset
+ t.dynYear= "";
+ ClrDST( t );
+ } // if
+
+ tCopy= t;
+ tCopy.name = ""; // make more generic comparison
+ tCopy.ident= ""; cc= TCTX_SYSTEM;
+ while (FoundTZ( tCopy, s, cc, g, false, cc )) {
+ if (!(cc!=aPreferredCtx && cc==t_Greenwich)) { // take UTC for this case
+ if (pUnk || cc==aPreferredCtx) { aContext= cc; return true; }
+ if (ccFirst==TCTX_UNKNOWN) ccFirst= cc; // keep it, just in case
+ } // if
+ } // while
+
+ aContext= ccFirst;
+ return true;
+} // TzDaylightToContext
+
+
+
+/*! Create DAYLIGHT string from context for a given sample time(year) */
+bool ContextToTzDaylight( timecontext_t aContext,
+ lineartime_t aSampleTime, ///< specifies the time after which we search DST
+ string &aText, ///< receives DAYLIGHT string
+ timecontext_t &aStdOffs, ///< receives standard (non-DST) offset for TZ
+ GZones* g,
+ TDebugLogger* aLog )
+{
+//#ifdef RELEASE_VERSION
+ //#error "%%%missing actual implementation - this is just a q&d dummy for testing"
+//#endif
+ TzResolveMetaContext( aContext, g ); // we need actual zone, not meta-context
+
+ sInt16 year, month, day;
+ lineartime2date( aSampleTime, &year,&month,&day ); // we need the active year
+ aStdOffs= 0; // default
+
+ tz_entry t, tCopy;
+ bool found= false;
+ bool dDone= false;
+ bool sDone= false;
+ lineartime_t dt = 0, st = 0;
+ string s;
+
+ do {
+ bool ok= GetTZ( aContext, t, g );
+ if (!ok) return false;
+
+ aStdOffs= TCTX_OFFSCONTEXT( t.bias ); // need this here in case of no DST
+
+ ok= DSTCond( t );
+ if (!ok) { aText= "FALSE"; return true; }
+
+ const int FLen= 15;
+ char f[ FLen ];
+ sprintf ( f, "%d", year );
+ string yy= f;
+ t.dynYear= yy.c_str();
+
+ // go further only in case of DST available
+ s= t.name;
+ if (s.find( "/",0 )==string::npos) { // search for a time zone with slash in it
+ tCopy= t;
+ tCopy.name = ""; // make more generic comparison
+ tCopy.ident= "";
+
+ timecontext_t cc= TCTX_UNKNOWN;
+ while (FoundTZ( tCopy, s, cc, g, false, cc )) {
+ if (s.find( "/",0 )!=string::npos) { // search for a time zone with slash in it
+ found= true; break;
+ } // if
+ } // while
+
+ if (!found) {
+ s = t.name; // create a <x>;<x> string
+ s+= ";";
+ s+= t.name;
+ } // if
+ } // if
+
+ if (!dDone) dt= DST_Switch( t, t.bias, year, true ); // get the switch time/date for DST
+ if (!sDone) st= DST_Switch( t, t.bias, year, false ); // get the switch time/date for STD
+
+ // search into future, <dt> must be earlier than <st>
+ if (dt>aSampleTime ) dDone= true;
+ if (st>aSampleTime && st>dt) sDone= true;
+
+ year++; // search in next year
+ } while (!dDone || !sDone);
+
+//lineartime_t lt= seconds2lineartime( t.bias * SecsPerMin );
+ string offs;
+ timecontext_t tc= TCTX_OFFSCONTEXT( t.bias+t.biasDST );
+ ContextToISO8601StrAppend( offs, tc, false );
+
+ // now concatenate the result string
+ aText= "TRUE;";
+ StringObjAppendPrintf( aText, "%s;", offs.c_str() );
+//StringObjAppendPrintf( aText, "%+03d;", t.bias / MinsPerHour );
+
+ string dstStr;
+ TimestampToISO8601Str( dstStr, dt, TCTX_UTC );
+ string stdStr;
+ TimestampToISO8601Str( stdStr, st, TCTX_UTC );
+
+ StringSubst ( s, "/", ";" );
+ aText+= dstStr + ";" + stdStr + ";" + s;
+
+ return true;
+} // ContextToTzDaylight
+
+
+
+
+// -----------------------------------------------------------------------------------------
+// Get sequence between <bv> and <ev>
+static string PeeledStr( string aStr, string bv, string ev, sInt32 aNth )
+{
+ string::size_type bp= 0;
+
+ if (aNth==-1) {
+ bp= aStr.rfind( bv, aStr.length() ); if (bp==string::npos) return "";
+ }
+ else {
+ sInt32 i= 1;
+
+ while (true) {
+ bp= aStr.find( bv, bp ); if (bp==string::npos) return "";
+ if (i>=aNth) break;
+ i++; bp++;
+ } // while
+ } // if
+
+//string::size_type bp= aStr.find( bv, 0 ); if (bp==string::npos) return "";
+ string::size_type ep= aStr.find( ev,bp ); if (ep==string::npos) return "";
+
+ string::size_type bpl= bp + bv.length();
+ return aStr.substr( bpl, ep - bpl );
+} // PeeledStr
+
+
+// Get the string between "BEGIN:<value>\n" and "END:<value>\n"
+string VStr( string aStr, string value, sInt32 aNth ) {
+ return PeeledStr( aStr, Property( VTZ_BEGIN, value ),
+ Property( VTZ_END, value ), aNth );
+} // VStr
+
+
+// Get the value string between "<field>:" and "\n"
+string VValue( string aStr, string key ) {
+ return PeeledStr( aStr, key + ":", "\n", 1 );
+} // VValue
+
+
+} // namespace sysync
+
+/* eof */
diff --git a/src/sysync/vtimezone.h b/src/sysync/vtimezone.h
new file mode 100755
index 0000000..da61cac
--- /dev/null
+++ b/src/sysync/vtimezone.h
@@ -0,0 +1,92 @@
+/*
+ * File: vtimezone.h
+ *
+ * Author: Beat Forster (bfo@synthesis.ch)
+ *
+ * Parser/Generator routines for VTIMEZONE
+ *
+ * Copyright (c) 2006-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ * 2006-03-06 : bfo : created from exctracts of "rrules.h"
+ *
+ */
+
+#ifndef VTIMEZONE_H
+#define VTIMEZONE_H
+
+
+// includes
+#include "timezones.h"
+#include "lineartime.h"
+#include "debuglogger.h"
+
+
+using namespace sysync;
+
+namespace sysync {
+
+// forward
+class TDebugLogger;
+
+bool VTIMEZONEtoTZEntry( const char* aText, // VTIMEZONE string to be parsed
+ tz_entry &t,
+ string &aStdName,
+ string &aDstName,
+ TDebugLogger* aLogP);
+
+/*! Convert VTIMEZONE string into internal context value */
+bool VTIMEZONEtoInternal( const char* aText, ///< VTIMEZONE string to be parsed
+ timecontext_t &aContext,
+ GZones* g,
+ TDebugLogger* aLog= NULL,
+ string* aTzidP= NULL ); ///< if not NULL, receives TZID as found in VTIMEZONE
+
+
+/*! Convert internal context value into VTIMEZONE */
+bool internalToVTIMEZONE( timecontext_t aContext,
+ string &aText, ///< receives VTIMEZONE string
+ GZones* g,
+ TDebugLogger* aLog= NULL,
+ sInt32 testYear= 0, // starting year
+ sInt32 untilYear= 0, // ending year
+ cAppCharP aPrefIdent= NULL ); // preferred type of TZID
+
+
+/*! Convert TZ/DAYLIGHT string into internal context value */
+bool TzDaylightToContext( const char* aText, ///< DAYLIGHT property value to be parsed
+ timecontext_t aStdOffs, ///< Standard (non-DST) offset obtained from TZ
+ timecontext_t &aContext, ///< receives context
+ GZones* g,
+ timecontext_t preferredCtx = TCTX_UNKNOWN, // preferred context, if rule matches more than one context
+ TDebugLogger* aLog= NULL );
+
+
+/*! Create DAYLIGHT string from context for a given sample time(year) */
+bool ContextToTzDaylight( timecontext_t aContext,
+ lineartime_t aSampleTime, ///< specifies year for which we want to see a sample
+ string &aText, ///< receives DAYLIGHT string
+ timecontext_t &aStdOffs, ///< receives standard (non-DST) offset for TZ
+ GZones* g,
+ TDebugLogger* aLog= NULL );
+
+
+// ---- utility functions ---------------------------------------------------------
+/*! <aStr> parsing:
+ * Get the string between "BEGIN:<value>\n" and "END:<value>\n"
+ * Default: First occurance
+ */
+string VStr( string aStr, string value, sInt32 aNth= 1 );
+
+
+/*! <aStr> parsing:
+ * Get the value between "<key>:" and "\n"
+ */
+string VValue( string aStr, string key );
+
+
+
+} // namespace sysync
+
+#endif // VTIMEZONE_H
+
+/* eof */
diff --git a/src/sysync_SDK/DB_Interfaces/demo/DLL/target_options.h b/src/sysync_SDK/DB_Interfaces/demo/DLL/target_options.h
new file mode 100644
index 0000000..13e2034
--- /dev/null
+++ b/src/sysync_SDK/DB_Interfaces/demo/DLL/target_options.h
@@ -0,0 +1,44 @@
+/*
+ * File: target_options.h
+ *
+ * Author: Beat Forster (bfo@synthesis.ch)
+ *
+ * Programming interface between Synthesis SyncML engine
+ * and a database structure.
+ *
+ * Copyright (c) 2004-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ */
+
+#ifndef TARGET_OPTIONS_H
+#define TARGET_OPTIONS_H
+
+
+/* - find out target platform */
+#ifdef __MACH__
+ #define MACOSX
+#else
+ #if defined __MWERKS__ || defined _MSC_VER
+ #ifndef _WIN32
+ #define _WIN32
+ #endif
+ #else
+ #define LINUX
+ #endif
+#endif
+
+
+/* code is running within DLL */
+#define SDK_DLL 1
+
+/* - we are not at the SyncML engine's side here */
+/* - and we need an extern "C" interface for the DLL */
+#undef SYSYNC_ENGINE
+
+
+/* activate debug output */
+#define SYDEBUG 2
+
+
+#endif /* TARGET_OPTIONS_H */
+/* eof */
diff --git a/src/sysync_SDK/DB_Interfaces/demo/sync_dbapi_demo.c b/src/sysync_SDK/DB_Interfaces/demo/sync_dbapi_demo.c
new file mode 100644
index 0000000..28e97e4
--- /dev/null
+++ b/src/sysync_SDK/DB_Interfaces/demo/sync_dbapi_demo.c
@@ -0,0 +1,941 @@
+/*
+ * File: sync_dbapi_demo.c
+ *
+ * Author: Beat Forster (bfo@synthesis.ch)
+ *
+ *
+ * Programming interface between Synthesis SyncML engine
+ * and a database structure.
+ *
+ * Copyright (c) 2004-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ *
+ * E X A M P L E C O D E
+ * ========================
+ * (To be used as starting point for SDK development.
+ * It can be adapted and extended by programmer)
+ *
+ */
+
+#include "sync_include.h" /* include general SDK definitions */
+#include "sync_dbapidef.h" /* include the interface file and utilities */
+#include "SDK_util.h" /* include SDK utilities */
+
+/* C/C++ support */
+#ifdef __cplusplus /* this allows to have more than one of these modules in C++ */
+ namespace SDK_demodb {
+ using sysync::SDK_Interface_Struct;
+ using sysync::DB_Callback;
+ using sysync::ItemID;
+ using sysync::MapID;
+
+ using sysync::LOCERR_OK;
+ using sysync::LOCERR_NOTIMP;
+ using sysync::DB_Full;
+ using sysync::DB_NotFound;
+ using sysync::DB_Forbidden;
+
+ using sysync::ReadNextItem_Changed;
+ using sysync::ReadNextItem_Unchanged;
+ using sysync::ReadNextItem_EOF;
+
+ using sysync::Password_ClrText_IN;
+#endif
+
+#include "sync_dbapi.h" /* include the interface file and utilities */
+
+#define BuildNumber 0 /* User defined build number, can be 0..255 */
+#define MyDB "DemoDB" /* example debug name */
+#define MY_ID 42 /* example datastore context */
+
+#define STRLEN 80 /* Max length of local string copies */
+
+#if defined MACOSX && defined __MWERKS__
+#pragma export on
+#endif
+
+
+/* -- MODULE -------------------------------------------------------------------- */
+/* this is an example, how a context could be structured */
+/**** CAN BE ADAPTED BY USER ****/
+#ifdef __cplusplus
+ class ModuleContext {
+ public:
+ ModuleContext() { fCB= NULL; }
+
+ DB_Callback fCB; /* callback structure */
+ char fModuleName[ STRLEN ]; /* the module's name */
+ };
+#else
+ typedef struct {
+ DB_Callback fCB; /* callback structure */
+ char fModuleName[ STRLEN ]; /* the module's name */
+ } ModuleContext;
+#endif
+
+
+/* <mContext> will be casted to the ModuleContext* structure */
+static ModuleContext* MoC( CContext mContext ) { return (ModuleContext*)mContext; }
+
+
+
+TSyError Module_CreateContext( CContext *mContext, cAppCharP moduleName,
+ cAppCharP subName,
+ cAppCharP mContextName,
+ DB_Callback mCB )
+{
+ cAppCharP sep= "";
+
+ ModuleContext* mc;
+
+ #ifdef __cplusplus
+ mc= new ModuleContext;
+ #else
+ mc= (ModuleContext*)malloc( sizeof(ModuleContext) );
+ #endif
+
+ if (mc==NULL) return DB_Full;
+
+ if (subName!=NULL &&
+ *subName!='\0') sep= "!"; /* Notation: <moduleName>"!"<subName> */
+
+ strncpy ( mc->fModuleName, moduleName, STRLEN );
+ mc->fCB = mCB;
+ DEBUG_DB( mc->fCB, MyDB,Mo_CC, "'%s%s%s' (%s)", mc->fModuleName,sep,subName,
+ mContextName );
+
+ *mContext= (CContext)mc; /* return the created context structure */
+ return LOCERR_OK;
+} /* Module_CreateContext */
+
+
+
+/* Get the plug-in's version number */
+CVersion Module_Version( CContext mContext )
+{
+ CVersion v= Plugin_Version( BuildNumber ); /* The current plugin's SDK version is expected here */
+
+ if ( mContext )
+ DEBUG_DB( MoC( mContext )->fCB, MyDB,Mo_Ve, "%08X", v );
+
+ return v;
+} /* Module_Version */
+
+
+
+/* Get the plug-in's capabilities */
+TSyError Module_Capabilities( CContext mContext, appCharP *mCapabilities )
+{
+ char s[ 256 ]; /* copy it into a local string */
+ char* p= s;
+
+ /* return value of sprintf contains the total string length: increment will concatenate */
+ p+= sprintf( p, "%s\n", MyPlatform() );
+ p+= sprintf( p, "%s\n", DLL_Info );
+ p+= sprintf( p, "%s:%s\n", CA_MinVersion, "V1.0.6.0" ); /* must not be changed */
+ p+= sprintf( p, "%s:%s\n", CA_Manufacturer, "Synthesis AG" ); /**** SHOULD BE ADAPTED BY USER ****/
+ p+= sprintf( p, "%s:%s", CA_Description, "Demo Example Module" ); /**** SHOULD BE ADAPTED BY USER ****/
+
+ *mCapabilities= StrAlloc( s );
+
+ DEBUG_DB( MoC( mContext )->fCB, MyDB,Mo_Ca, "'%s'", *mCapabilities );
+ return LOCERR_OK;
+} /* Module_Capabilities */
+
+
+
+TSyError Module_PluginParams( CContext mContext, cAppCharP mConfigParams, CVersion engineVersion )
+{
+ ModuleContext* mc= MoC( mContext );
+ DEBUG_DB ( mc->fCB, MyDB,Mo_PP, " Engine=%08X", engineVersion );
+ DEBUG_DB ( mc->fCB, MyDB,Mo_PP, "'%s'", mConfigParams );
+
+/*return LOCERR_CFGPARSE;*/ /* if there are unsupported params */
+ return LOCERR_OK;
+} /* Module_PluginParams */
+
+
+
+/* Dispose the memory of the module context */
+void Module_DisposeObj( CContext mContext, void* memory )
+{
+ DEBUG_Exotic_DB( MoC( mContext )->fCB, MyDB,Mo_DO, "free at %08X '%s'", memory,memory );
+ StrDispose( memory );
+} /* Module_DisposeObj */
+
+
+
+TSyError Module_DeleteContext( CContext mContext )
+{
+ ModuleContext* mc= MoC( mContext );
+ DEBUG_DB ( mc->fCB, MyDB,Mo_DC, "'%s'", mc->fModuleName );
+
+ #ifdef __cplusplus
+ delete mc;
+ #else
+ free ( mc );
+ #endif
+
+ return LOCERR_OK;
+} /* Module_DeleteContext */
+
+
+
+
+/* ---------------------- session handling --------------------- */
+/* this is an example, how a context could be structured */
+/**** CAN BE ADAPTED BY USER ****/
+#ifdef __cplusplus
+ class SessionContext {
+ public:
+ SessionContext() { fCB= NULL; }
+
+ int fID; /* a reference number. */
+ DB_Callback fCB; /* callback structure */
+ int fPMode; /* The login password mode */
+ };
+#else
+ typedef struct {
+ int fID; /* a reference number. */
+ DB_Callback fCB; /* callback structure */
+ int fPMode; /* The login password mode */
+ } SessionContext;
+#endif
+
+
+
+/* <sContext> will be casted to the SessionContext* structure */
+static SessionContext* SeC( CContext sContext ) { return (SessionContext*)sContext; }
+
+
+
+/* Create a context for a new session */
+TSyError Session_CreateContext( CContext *sContext, cAppCharP sessionName, DB_Callback sCB )
+{
+ SessionContext* sc;
+
+/*return DB_Error;*/ /* added for test */
+
+ #ifdef __cplusplus
+ sc= new SessionContext;
+ #else
+ sc= (SessionContext*)malloc( sizeof(SessionContext) );
+ #endif
+
+ if (sc==NULL) return DB_Full;
+
+ /**** CAN BE ADAPTED BY USER ****/
+ sc->fID= 333; /* as an example */
+ sc->fCB= sCB;
+ sc->fPMode= Password_ClrText_IN; /* take this mode ... */
+ /* sc->fPMode= Password_ClrText_OUT; */ /* ... or this */
+ DEBUG_DB( sc->fCB, MyDB,Se_CC, "%d '%s'", sc->fID,sessionName );
+
+ *sContext= (CContext)sc; /* return the created context structure */
+ return LOCERR_OK;
+} /* Session_CreateContext */
+
+
+
+/* ----- "script-like" ADAPT --------- */
+TSyError Session_AdaptItem( CContext sContext, appCharP *sItemData1,
+ appCharP *sItemData2,
+ appCharP *sLocalVars,
+ uInt32 sIdentifier )
+{
+ /**** CAN BE ADAPTED BY USER ****/
+ SessionContext* sc= SeC( sContext );
+ DEBUG_DB ( sc->fCB, MyDB,"Session_AdaptItem", "'%s' '%s' '%s' id=%d",
+ *sItemData1,*sItemData2,*sLocalVars, sIdentifier );
+ return LOCERR_OK;
+} /* Session_AdaptItem */
+
+
+
+/* Check the database entry of <deviceID> and return its nonce string */
+TSyError Session_CheckDevice( CContext sContext, cAppCharP aDeviceID, appCharP *sDevKey,
+ appCharP *nonce )
+{
+ /**** CAN BE ADAPTED BY USER ****/
+ SessionContext* sc= SeC( sContext );
+
+ *sDevKey= StrAlloc( aDeviceID );
+ *nonce = StrAlloc( "xyz_last" );
+ DEBUG_DB( sc->fCB, MyDB,Se_CD, "%d dev='%s' nonce='%s'", sc->fID, *sDevKey,*nonce );
+ return LOCERR_OK;
+} /* Session_CheckDevice */
+
+
+
+/* Get a new nonce from the database. If this returns an error, the SyncML engine
+ * will create its own nonce.
+ */
+TSyError Session_GetNonce( CContext sContext, appCharP *nonce )
+{
+ /**** CAN BE ADAPTED BY USER ****/
+ SessionContext* sc= SeC( sContext );
+ DEBUG_DB ( sc->fCB, MyDB,Se_GN, "%d (not supported)", sc->fID );
+ *nonce= NULL;
+ return DB_NotFound;
+} /* Session_GetNonce */
+
+
+
+/* Save the new nonce (which will be expected to be returned
+ * in the next session for this device
+ */
+TSyError Session_SaveNonce( CContext sContext, cAppCharP nonce )
+{
+ /**** CAN BE ADAPTED BY USER ****/
+ SessionContext* sc= SeC( sContext );
+ DEBUG_DB ( sc->fCB, MyDB,Se_SN, "%d nonce='%s'", sc->fID, nonce );
+ return LOCERR_OK;
+} /* Session_SaveNonce */
+
+
+
+/* Save the device info of <sContext> */
+TSyError Session_SaveDeviceInfo( CContext sContext, cAppCharP aDeviceInfo )
+{
+ /**** CAN BE ADAPTED BY USER ****/
+ SessionContext* sc= SeC( sContext );
+ DEBUG_DB ( sc->fCB, MyDB,Se_SD, "%d info='%s'", sc->fID, aDeviceInfo );
+ return LOCERR_OK;
+} /* Session_SaveDeviceInfo */
+
+
+
+/* Get the plugin's DB time */
+TSyError Session_GetDBTime( CContext sContext, appCharP *currentDBTime )
+{
+ /**** CAN BE ADAPTED BY USER ****/
+ SessionContext* sc= SeC( sContext );
+ DEBUG_DB ( sc->fCB, MyDB,Se_GT, "%d", sc->fID );
+ *currentDBTime= NULL;
+ return DB_NotFound;
+} /* Session_GetDBTime */
+
+
+
+/* Return: Password_ClrText_IN 'SessionLogin' will get clear text password
+ * Password_ClrText_OUT " must return clear text password
+ * Password_MD5_OUT " must return MD5 coded password
+ * Password_MD5_Nonce_IN " will get MD5B64(MD5B64(user:pwd):nonce)
+ */
+sInt32 Session_PasswordMode( CContext sContext )
+{
+ /**** CAN BE ADAPTED BY USER ****/
+ SessionContext* sc= SeC( sContext );
+ DEBUG_DB ( sc->fCB, MyDB,Se_PM, "%d mode=%d", sc->fID, sc->fPMode );
+ return sc->fPMode;
+} /* Session_PasswordMode */
+
+
+
+/* Make login */
+TSyError Session_Login( CContext sContext, cAppCharP sUsername, appCharP *sPassword,
+ appCharP *sUsrKey )
+{
+ /**** CAN BE ADAPTED BY USER ****/
+ SessionContext* sc= SeC( sContext );
+ TSyError err= DB_Forbidden; /* default */
+
+ /* different modes, choose one for the plugin */
+ if (sc->fPMode==Password_ClrText_IN) {
+ if (strcmp( sUsername,"super" )==0 &&
+ strcmp( *sPassword,"user" )==0) { *sUsrKey = StrAlloc( "1234" ); err= LOCERR_OK; }
+ }
+ else { /* Password will be returned */
+ if (strcmp( sUsername,"super" )==0) { *sPassword= StrAlloc( "user" );
+ *sUsrKey = StrAlloc( "1234" ); err= LOCERR_OK; }
+ } /* if */
+
+ if (err) { DEBUG_DB( sc->fCB, MyDB,Se_LI, "%d usr='%s' err=%d",
+ sc->fID,sUsername, err ); }
+ else DEBUG_DB( sc->fCB, MyDB,Se_LI, "%d usr='%s' pwd='%s' => key='%s'",
+ sc->fID,sUsername,*sPassword, *sUsrKey );
+ return err;
+} /* Session_Login */
+
+
+
+/* Make logout */
+TSyError Session_Logout( CContext sContext )
+{
+ /**** CAN BE ADAPTED BY USER ****/
+ SessionContext* sc= SeC( sContext );
+ DEBUG_DB ( sc->fCB, MyDB,Se_LO, "%d",sc->fID );
+ return LOCERR_OK;
+} /* Session_Logout */
+
+
+
+void Session_DisposeObj( CContext sContext, void* memory )
+{
+ /**** CAN BE ADAPTED BY USER ****/
+ SessionContext* sc= SeC( sContext );
+ DEBUG_Exotic_DB( sc->fCB, MyDB,Se_DO, "%d free at %08X '%s'",
+ sc->fID, memory,memory );
+ StrDispose ( memory );
+} /* Session_DisposeObj */
+
+
+
+/* Can be implemented empty, if no action is required */
+void Session_ThreadMayChangeNow( CContext sContext )
+{
+ /**** CAN BE ADAPTED BY USER ****/
+ SessionContext* sc= SeC( sContext );
+ DEBUG_Exotic_DB( sc->fCB, MyDB,Se_TC, "%d",sc->fID );
+} /* Session_ThreadMayChangeNow */
+
+
+
+/* This routine is implemented for debug purposes only and will NOT BE CALLED by the
+ * SyncML engine. Can be implemented empty, if not needed
+ */
+void Session_DispItems( CContext sContext, bool allFields, cAppCharP specificItem )
+{
+ /**** CAN BE ADAPTED BY USER ****/
+ SessionContext* sc= SeC( sContext );
+ DEBUG_DB ( sc->fCB, MyDB,Se_DI, "%d %d '%s'",
+ sc->fID, allFields,specificItem );
+} /* Session_DispItems */
+
+
+
+/* Delete a session context */
+TSyError Session_DeleteContext( CContext sContext )
+{
+ /**** CAN BE ADAPTED BY USER ****/
+ SessionContext* sc= SeC( sContext );
+ DEBUG_DB ( sc->fCB, MyDB,Se_DC, "%d",sc->fID );
+
+ #ifdef __cplusplus
+ delete sc;
+ #else
+ free ( sc );
+ #endif
+
+ return LOCERR_OK;
+} /* Session_DeleteContext */
+
+
+
+
+/* ----------------------------------------------------------------- */
+/* This is an example, how a context could be structured */
+/**** CAN BE ADAPTED BY USER ****/
+#ifdef __cplusplus
+ class TDBContext {
+ public:
+ TDBContext() { fCB= NULL; }
+
+ DB_Callback fCB; /* debug logging callback */
+
+ int contextID; /* context identifier */
+ int nthItem; /* for 'ReadNextItem' */
+
+ char fDevKey[ STRLEN ];
+ char fUsrKey[ STRLEN ];
+ };
+#else
+ typedef struct {
+ DB_Callback fCB; /* debug logging callback */
+
+ int contextID; /* context identifier */
+ int nthItem; /* for 'ReadNextItem' */
+
+ char fDevKey[ STRLEN ];
+ char fUsrKey[ STRLEN ];
+ } TDBContext;
+#endif
+
+typedef TDBContext* ContextP;
+
+
+/* <aContext> will be casted to the ContextP structure */
+static ContextP DBC( CContext aContext ) { return (ContextP)aContext; }
+
+
+
+/* -- OPEN ----------------------------------------------------------------------- */
+TSyError CreateContext( CContext *aContext, cAppCharP aContextName, DB_Callback aCB,
+ cAppCharP sDevKey,
+ cAppCharP sUsrKey )
+{
+ ContextP ac;
+
+ #ifdef __cplusplus
+ ac= new TDBContext;
+ #else
+ ac= (ContextP)malloc( sizeof(TDBContext) );
+ #endif
+
+ if (ac==NULL) return DB_Full;
+
+ /**** CAN BE ADAPTED BY USER ****/
+ ac->fCB = aCB; /* debug logging callback */
+ ac->contextID= MY_ID; /* example context */
+ ac->nthItem = 0; /* reset counter */
+ strncpy ( ac->fDevKey, sDevKey, STRLEN ); /* local copies */
+ strncpy ( ac->fUsrKey, sUsrKey, STRLEN );
+
+ DEBUG_DB( ac->fCB, MyDB,Da_CC, "%d '%s' dev='%s' usr='%s'",
+ ac->contextID, aContextName, ac->fDevKey, ac->fUsrKey );
+
+ *aContext= (CContext)ac; /* return the created context structure */
+ return LOCERR_OK;
+} /* CreateContext */
+
+
+
+uInt32 ContextSupport( CContext aContext, cAppCharP aContextRules )
+{
+ /**** CAN BE ADAPTED BY USER ****/
+ ContextP ac= DBC( aContext );
+ DEBUG_DB( ac->fCB, MyDB,Da_CS, "%d '%s'", ac->contextID,aContextRules );
+ return 0;
+} /* ContextSupport */
+
+
+
+uInt32 FilterSupport( CContext aContext, cAppCharP aFilterRules )
+{
+ /**** CAN BE ADAPTED BY USER ****/
+ ContextP ac= DBC( aContext );
+ DEBUG_DB( ac->fCB, MyDB,Da_FS, "%d '%s'", ac->contextID,aFilterRules );
+ return 0;
+} /* FilterSupport */
+
+
+
+/* -- ADMINISTRATION ------------------------------------------------------------ */
+TSyError LoadAdminData( CContext aContext, cAppCharP aLocDB,
+ cAppCharP aRemDB, appCharP *adminData )
+{
+ /**** CAN BE ADAPTED BY USER ****/
+ ContextP ac= DBC( aContext );
+ DEBUG_DB( ac->fCB, MyDB,Da_LA, "%d '%s' '%s' '%s' '%s'",
+ ac->contextID, ac->fDevKey, ac->fUsrKey, aLocDB, aRemDB );
+ *adminData= NULL;
+ return DB_Forbidden; /* not yet implemented */
+} /* LoadAdminData */
+
+
+
+TSyError SaveAdminData( CContext aContext, cAppCharP adminData )
+{
+ /**** CAN BE ADAPTED BY USER ****/
+ ContextP ac= DBC( aContext );
+ DEBUG_DB( ac->fCB, MyDB,Da_SA, "%d '%s'", ac->contextID, adminData );
+ return DB_Forbidden; /* not yet implemented */
+} /* SaveAdminData */
+
+
+
+bool ReadNextMapItem( CContext aContext, MapID mID, bool aFirst )
+{
+ /**** CAN BE ADAPTED BY USER ****/
+ ContextP ac= DBC( aContext );
+ DEBUG_DB( ac->fCB, MyDB, Da_RM, "%d %08X first=%d (EOF)", ac->contextID, mID, aFirst );
+ return false; /* not yet implemented */
+} /* ReadNextMapItem */
+
+
+
+TSyError InsertMapItem( CContext aContext, cMapID mID )
+{
+ /**** CAN BE ADAPTED BY USER ****/
+ ContextP ac= DBC( aContext );
+ DEBUG_DB( ac->fCB, MyDB,Da_IM, "%d %08X: '%s' '%s' %04X %d",
+ ac->contextID, mID, mID->localID, mID->remoteID, mID->flags, mID->ident );
+ return DB_Forbidden; /* not yet implemented */
+} /* InsertMapItem */
+
+
+
+TSyError UpdateMapItem( CContext aContext, cMapID mID )
+{
+ /**** CAN BE ADAPTED BY USER ****/
+ ContextP ac= DBC( aContext );
+ DEBUG_DB( ac->fCB, MyDB,Da_UM, "%d %08X: '%s' '%s' %04X %d",
+ ac->contextID, mID, mID->localID, mID->remoteID, mID->flags, mID->ident );
+ return DB_Forbidden; /* not yet implemented */
+} /* UpdateMapItem */
+
+
+
+TSyError DeleteMapItem( CContext aContext, cMapID mID )
+{
+ /**** CAN BE ADAPTED BY USER ****/
+ ContextP ac= DBC( aContext );
+ DEBUG_DB( ac->fCB, MyDB,Da_DM, "%d %08X: '%s' '%s' %04X %d",
+ ac->contextID, mID, mID->localID, mID->remoteID, mID->flags, mID->ident );
+ return DB_Forbidden; /* not yet implemented */
+} /* DeleteMapItem */
+
+
+
+
+/* -- GENERAL -------------------------------------------------------------------- */
+void DisposeObj( CContext aContext, void* memory )
+{
+ /**** CAN BE ADAPTED BY USER ****/
+ ContextP ac= DBC( aContext );
+ DEBUG_Exotic_DB( ac->fCB, MyDB,Da_DO, "%d free at %08X", ac->contextID,memory );
+ free( memory );
+} /* DisposeObj */
+
+
+
+void ThreadMayChangeNow( CContext aContext )
+{
+ /**** CAN BE ADAPTED BY USER ****/
+ /* can be implemented empty, if no action is required */
+ ContextP ac= DBC( aContext );
+ DEBUG_Exotic_DB( ac->fCB, MyDB,Da_TC, "%d", ac->contextID );
+} /* ThreadMayChangeNow */
+
+
+
+void WriteLogData( CContext aContext, cAppCharP logData )
+{
+ /**** CAN BE ADAPTED BY USER ****/
+ ContextP ac= DBC( aContext );
+ DEBUG_DB( ac->fCB, MyDB, Da_WL, "%d (BEGIN)\n%s", ac->contextID, logData );
+ DEBUG_DB( ac->fCB, MyDB, Da_WL, "%d (END)", ac->contextID );
+} /* WriteLogData */
+
+
+
+/* This routine is implemented for debug purposes only and will NOT BE CALLED by the
+ * SyncML engine. Can be implemented empty, if not needed
+ */
+void DispItems( CContext aContext, bool allFields, cAppCharP specificItem )
+{
+ /**** CAN BE ADAPTED BY USER ****/
+ ContextP ac= DBC( aContext );
+ DEBUG_DB( ac->fCB, MyDB,Da_DI, "%d %d '%s'", ac->contextID,allFields,specificItem );
+} /* DispItems */
+
+
+
+/* ----- "script-like" ADAPT --------- */
+TSyError AdaptItem( CContext aContext, appCharP *aItemData1,
+ appCharP *aItemData2,
+ appCharP *aLocalVars,
+ uInt32 aIdentifier )
+{
+ /**** CAN BE ADAPTED BY USER ****/
+ ContextP ac= DBC( aContext );
+ DEBUG_DB( ac->fCB, MyDB,"AdaptItem", "'%s' '%s' '%s' id=%d",
+ *aItemData1,*aItemData2,*aLocalVars, aIdentifier );
+ return LOCERR_OK;
+} /* AdaptItem */
+
+
+
+/* -- READ ---------------------------------------------------------------------- */
+TSyError StartDataRead( CContext aContext, cAppCharP lastToken,
+ cAppCharP resumeToken )
+{
+ /**** CAN BE ADAPTED BY USER ****/
+ ContextP ac= DBC( aContext );
+ DEBUG_DB( ac->fCB, MyDB,Da_SR, "%d last='%s' resume='%s'",
+ ac->contextID, lastToken,resumeToken );
+ ac->nthItem= 0; /* reset counter */
+ return LOCERR_OK;
+} /* StartDataRead */
+
+
+
+TSyError ReadNextItem( CContext aContext, ItemID aID, appCharP *aItemData, sInt32 *aStatus, bool aFirst )
+{
+ /**** CAN BE ADAPTED BY USER ****/
+ ContextP ac= DBC( aContext );
+ if (aFirst) ac->nthItem= 0;
+
+ /* Show all visible items to the SyncML engine here */
+ /* "ReadNextItem" will be called subsequently until ReadNextItem_EOF is returned */
+ if (ac->nthItem>0) {
+ *aStatus= ReadNextItem_EOF;
+ DEBUG_DB( ac->fCB, MyDB,Da_RN, "%d aStatus=%d", ac->contextID, *aStatus );
+ return LOCERR_OK;
+ } /* if */
+
+ /* This example just shows one hard coded element */
+ aID->item = StrAlloc( "demo_ID" );
+ aID->parent= StrAlloc( "demo_parent" );
+ *aItemData = StrAlloc( "demo_data" );
+ *aStatus = ReadNextItem_Changed; /* comparison not implemented here */
+
+ ac->nthItem++;
+ DEBUG_DB( ac->fCB, MyDB,Da_RN, "%d aStatus=%d aItemData='%s' aID=(%s,%s)",
+ ac->contextID, *aStatus, *aItemData, aID->item,aID->parent );
+ return LOCERR_OK;
+} /* ReadNextItem */
+
+
+TSyError ReadNextItemAsKey( CContext aContext, ItemID aID, KeyH aItemKey,
+ sInt32* aStatus, bool aFirst )
+{
+ /**** CAN BE ADAPTED BY USER ****/
+ ContextP ac= DBC( aContext );
+ if (aFirst) ac->nthItem= 0;
+
+ /* Show all visible items to the SyncML engine here */
+ /* "ReadNextItemAsKey" will be called subsequently until ReadNextItem_EOF is returned */
+ if (ac->nthItem>0) {
+ *aStatus= ReadNextItem_EOF;
+ DEBUG_DB( ac->fCB, MyDB,Da_RNK, "%d aStatus=%d", ac->contextID, *aStatus );
+ return LOCERR_OK;
+ } /* if */
+
+ /* This example just shows one hard coded element */
+ aID->item = StrAlloc( "demo_ID" );
+ aID->parent= StrAlloc( "demo_parent" );
+ *aStatus = ReadNextItem_Changed; /* comparison not implemented here */
+
+ ac->nthItem++;
+ DEBUG_DB( ac->fCB, MyDB,Da_RNK, "%d aStatus=%d aItemKey=%08X aID=(%s,%s)",
+ ac->contextID, *aStatus, aItemKey, aID->item,aID->parent );
+ return LOCERR_OK;
+} /* ReadNextItemAsKey */
+
+
+
+TSyError ReadItem( CContext aContext, cItemID aID, appCharP *aItemData )
+{
+ /**** CAN BE ADAPTED BY USER ****/
+ ContextP ac= DBC( aContext );
+
+ *aItemData= StrAlloc( "demo_data" );
+ DEBUG_DB( ac->fCB, MyDB,Da_RI, "%d aItemData='%s' aID=(%s,%s)",
+ ac->contextID,*aItemData, aID->item,aID->parent );
+ return LOCERR_OK;
+} /* ReadItem */
+
+
+
+TSyError ReadItemAsKey( CContext aContext, cItemID aID, KeyH aItemKey )
+{
+ /**** CAN BE ADAPTED BY USER ****/
+ ContextP ac= DBC( aContext );
+ DEBUG_DB( ac->fCB, MyDB,Da_RIK, "%d aItemKey=%08X aID=(%s,%s)",
+ ac->contextID, aItemKey, aID->item,aID->parent );
+ return LOCERR_OK;
+} /* ReadItemAsKey */
+
+
+
+TSyError ReadBlob( CContext aContext, cItemID aID, cAppCharP aBlobID,
+ appPointer *aBlkPtr, memSize *aBlkSize,
+ memSize *aTotSize,
+ bool aFirst, bool *aLast )
+{
+ /**** CAN BE ADAPTED BY USER ****/
+ ContextP ac= DBC( aContext );
+
+ const memSize sz= sizeof(int);
+ int* ip = (int*)malloc( sz ); /* example BLOB structure for test (=4 bytes) */
+ *ip = 231;
+
+ *aBlkPtr = (appPointer)ip; if (*aBlkSize==0 || *aBlkSize>=sz) *aBlkSize= sz;
+ *aTotSize= *aBlkSize;
+ *aLast = true;
+
+ DEBUG_DB( ac->fCB, MyDB,Da_RB, "%d aID=(%s,%s) aBlobID=(%s)",
+ ac->contextID, aID->item,aID->parent, aBlobID );
+ DEBUG_DB( ac->fCB, MyDB,"", "aBlkPtr=%08X aBlkSize=%d aTotSize=%d aFirst=%s aLast=%s",
+ *aBlkPtr, *aBlkSize, *aTotSize,
+ aFirst?"true":"false", *aLast?"true":"false" );
+ return LOCERR_OK;
+} /* ReadBlob */
+
+
+
+TSyError EndDataRead( CContext aContext )
+{
+ /**** CAN BE ADAPTED BY USER ****/
+ ContextP ac= DBC( aContext );
+ DEBUG_DB( ac->fCB, MyDB,Da_ER, "%d", ac->contextID );
+ return LOCERR_OK;
+} /* EndDataRead */
+
+
+
+
+/* -- WRITE --------------------------------------------------------------------- */
+TSyError StartDataWrite( CContext aContext )
+{
+ /**** CAN BE ADAPTED BY USER ****/
+ ContextP ac= DBC( aContext );
+ DEBUG_DB( ac->fCB, MyDB,Da_SW, "%d", ac->contextID );
+ return LOCERR_OK;
+} /* StartDataWrite */
+
+
+
+TSyError InsertItem( CContext aContext, cAppCharP aItemData, ItemID newID )
+{
+ /**** CAN BE ADAPTED BY USER ****/
+ ContextP ac= DBC( aContext );
+ newID->item = StrAlloc( "hello" );
+ DEBUG_DB( ac->fCB, MyDB,Da_II, "%d '%s'\nnewID=(%s,%s)",
+ ac->contextID, aItemData, newID->item,newID->parent );
+ return LOCERR_OK;
+} /* InsertItem */
+
+
+TSyError InsertItemAsKey( CContext aContext, KeyH aItemKey, ItemID newID )
+{
+ /**** CAN BE ADAPTED BY USER ****/
+ ContextP ac= DBC( aContext );
+ DEBUG_DB( ac->fCB, MyDB,Da_IIK, "%d %08X\n", ac->contextID, aItemKey );
+ newID= NULL;
+ return LOCERR_NOTIMP;
+} /* InsertItemAsKey */
+
+
+
+TSyError UpdateItem( CContext aContext, cAppCharP aItemData, cItemID aID,
+ ItemID updID )
+{
+ /**** CAN BE ADAPTED BY USER ****/
+ ContextP ac= DBC( aContext );
+ if (strcmp( aID->item,"example" )!=0) updID->item = StrAlloc( "example" );
+
+ DEBUG_DB( ac->fCB, MyDB,Da_UI, "%d '%s'\naID=(%s,%s)",
+ ac->contextID, aItemData, aID->item,aID->parent );
+
+ if (updID->item==NULL) { DEBUG_DB( ac->fCB, MyDB,"", "NULL" ); }
+ else { DEBUG_DB( ac->fCB, MyDB,"", "updID=(%s,%s)",
+ updID->item,updID->parent ); }
+
+ return LOCERR_OK;
+} /* UpdateItem */
+
+
+TSyError UpdateItemAsKey( CContext aContext, KeyH aItemKey, cItemID aID,
+ ItemID updID )
+{
+ /**** CAN BE ADAPTED BY USER ****/
+ ContextP ac= DBC( aContext );
+ DEBUG_DB( ac->fCB, MyDB,Da_UIK, "%d %08X\naID=(%s,%s)",
+ ac->contextID, aItemKey, aID->item,aID->parent );
+ updID= NULL;
+ return LOCERR_NOTIMP;
+} /* UpdateItemAsKey */
+
+
+
+TSyError MoveItem( CContext aContext, cItemID aID, cAppCharP newParID )
+{
+ /**** CAN BE ADAPTED BY USER ****/
+ ContextP ac= DBC( aContext );
+ DEBUG_DB( ac->fCB, MyDB,Da_MvI, "%d aID=(%s,%s) => (%s,%s)",
+ ac->contextID, aID->item,aID->parent,
+ aID->item,newParID );
+ return LOCERR_OK;
+} /* MoveItem */
+
+
+
+TSyError DeleteItem( CContext aContext, cItemID aID )
+{
+ /**** CAN BE ADAPTED BY USER ****/
+ ContextP ac= DBC( aContext );
+ DEBUG_DB( ac->fCB, MyDB,Da_DeI, "%d aID=(%s,%s)", ac->contextID,aID->item,aID->parent );
+ return LOCERR_OK;
+} /* DeleteItem */
+
+
+
+TSyError FinalizeLocalID( CContext aContext, cItemID aID, ItemID updID )
+{
+ /**** CAN BE ADAPTED BY USER ****/
+ ContextP ac= DBC( aContext );
+ DEBUG_DB( ac->fCB, MyDB,Da_FLI, "%d aID=(%s,%s)", ac->contextID,aID->item,aID->parent );
+ updID= NULL;
+ return LOCERR_NOTIMP;
+} /* FinalizeLocalID */
+
+
+
+TSyError DeleteSyncSet( CContext aContext )
+{
+ /**** CAN BE ADAPTED BY USER ****/
+ ContextP ac= DBC( aContext );
+ DEBUG_DB( ac->fCB, MyDB,Da_DSS, "%d", ac->contextID );
+ return LOCERR_NOTIMP;
+} /* DeleteSyncSet */
+
+
+
+TSyError WriteBlob( CContext aContext, cItemID aID, cAppCharP aBlobID,
+ appPointer aBlkPtr, memSize aBlkSize,
+ memSize aTotSize,
+ bool aFirst, bool aLast )
+{
+ /**** CAN BE ADAPTED BY USER ****/
+ ContextP ac= DBC( aContext );
+ DEBUG_DB( ac->fCB, MyDB,Da_WB, "%d aID=(%s,%s) aBlobID=(%s)",
+ ac->contextID, aID->item,aID->parent, aBlobID );
+ DEBUG_DB( ac->fCB, MyDB,"", "aBlkPtr=%08X aBlkSize=%d aTotSize=%d aFirst=%s aLast=%s",
+ aBlkPtr, aBlkSize, aTotSize,
+ aFirst?"true":"false", aLast ?"true":"false" );
+ return LOCERR_OK;
+} /* WriteBlob */
+
+
+
+TSyError DeleteBlob( CContext aContext, cItemID aID, cAppCharP aBlobID )
+{
+ /**** CAN BE ADAPTED BY USER ****/
+ ContextP ac= DBC( aContext );
+ DEBUG_DB( ac->fCB, MyDB,Da_DB, "%d aID=(%s,%s) aBlobID=(%s)",
+ ac->contextID, aID->item,aID->parent, aBlobID );
+ return LOCERR_OK;
+} /* DeleteBlob */
+
+
+
+TSyError EndDataWrite( CContext aContext, bool success, appCharP *newToken )
+{
+ /**** CAN BE ADAPTED BY USER ****/
+ ContextP ac= DBC( aContext );
+
+ #define TokenExample "20041110T230000Z"
+ *newToken= StrAlloc( TokenExample );
+
+ DEBUG_DB( ac->fCB, MyDB,Da_EW, "%d %s '%s'",
+ ac->contextID, success ? "COMMIT":"ROLLBACK", *newToken );
+ return LOCERR_OK;
+} /* EndDataWrite */
+
+
+
+/* ----------------------------------- */
+TSyError DeleteContext( CContext aContext )
+{
+ /**** CAN BE ADAPTED BY USER ****/
+ ContextP ac= DBC( aContext );
+ DEBUG_DB( ac->fCB, MyDB,Da_DC, "%d", ac->contextID );
+
+ #ifdef __cplusplus
+ delete ac;
+ #else
+ free ( ac ); /* release the structure itself */
+ #endif
+
+ return LOCERR_OK;
+} /* DeleteContext */
+
+
+
+/* C/C++ support */
+#ifdef __cplusplus
+} /* namespace */
+#endif
+/* eof */
diff --git a/src/sysync_SDK/DB_Interfaces/snowwhite/DLL/target_options.h b/src/sysync_SDK/DB_Interfaces/snowwhite/DLL/target_options.h
new file mode 100644
index 0000000..ef98ea1
--- /dev/null
+++ b/src/sysync_SDK/DB_Interfaces/snowwhite/DLL/target_options.h
@@ -0,0 +1,53 @@
+/*
+ * File: target_options.h
+ *
+ * Author: Beat Forster (bfo@synthesis.ch)
+ *
+ * Programming interface between Synthesis SyncML engine
+ * and a database structure.
+ *
+ * Copyright (c) 2004-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ */
+
+#ifndef TARGET_OPTIONS_H
+#define TARGET_OPTIONS_H
+
+
+/* - find out target platform */
+#ifdef __MACH__
+ #define MACOSX
+#else
+ #if defined __MWERKS__ || defined _MSC_VER
+ #ifndef _WIN32
+ #define _WIN32
+ #endif
+
+ #ifndef WIN32
+ #define WIN32
+ #endif
+ #else
+ #define LINUX
+ #endif
+#endif
+
+
+/* code is running within DLL */
+#define SDK_DLL 1
+
+/* - we are not at the SyncML engine's side here */
+/* - and we need an extern "C" interface for the DLL */
+#undef SYSYNC_ENGINE
+
+
+/* activate debug output */
+#define SYDEBUG 2
+
+
+/* use internal BLOB and ADMIN implementation */
+//#define DISABLE_PLUGIN_DATASTOREADMIN 1
+//#define DISABLE_PLUGIN_BLOBS 1
+
+
+#endif /* TARGET_OPTIONS_H */
+/* eof */
diff --git a/src/sysync_SDK/DB_Interfaces/snowwhite/myadapter.h b/src/sysync_SDK/DB_Interfaces/snowwhite/myadapter.h
new file mode 100644
index 0000000..d154e94
--- /dev/null
+++ b/src/sysync_SDK/DB_Interfaces/snowwhite/myadapter.h
@@ -0,0 +1,28 @@
+/*
+ * File: myadapter.h
+ *
+ * Author: Beat Forster (bfo@synthesis.ch)
+ *
+ * This bridge allows a OceanBlue module
+ * which needn't to be changed for different
+ * DBApi database adapters
+ *
+ * Copyright (c) 2008-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ */
+
+#ifndef MYADAPTER_H
+#define MYADAPTER_H
+
+
+//*** defs used for unified OceanBlue ***
+#define MyAdapter_Name "SnowWhite"
+#define MyAdapter_Module SnowWhite_Module
+#define MyAdapter_Session SnowWhite_Session
+#define MyAdapter SnowWhite
+
+#include "snowwhite.h"
+
+
+#endif // MYADAPTER_H
+/* eof */
diff --git a/src/sysync_SDK/DB_Interfaces/snowwhite/oceanblue.cpp b/src/sysync_SDK/DB_Interfaces/snowwhite/oceanblue.cpp
new file mode 100644
index 0000000..5b3c740
--- /dev/null
+++ b/src/sysync_SDK/DB_Interfaces/snowwhite/oceanblue.cpp
@@ -0,0 +1,757 @@
+/*
+ * File: oceanblue.cpp
+ *
+ * Author: Beat Forster (bfo@synthesis.ch)
+ *
+ * datastore plugin interface and
+ * Base class for datastore plugins in C++
+ *
+ * Copyright (c) 2008-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ */
+
+#include "myadapter.h"
+
+namespace oceanblue {
+ using sysync::SDK_Interface_Struct;
+ using sysync::DB_Callback;
+ using sysync::ItemID;
+ using sysync::MapID;
+
+ using sysync::LOCERR_OK;
+ using sysync::LOCERR_NOTIMP;
+ using sysync::LOCERR_TRUNCATED;
+ using sysync::DB_Forbidden;
+ using sysync::DB_NotFound;
+ using sysync::DB_Full;
+ using sysync::Password_Mode_Undefined;
+ using sysync::ReadNextItem_EOF;
+ using sysync::VP_GlobMulti;
+ using sysync::VALTYPE_TEXT;
+ using sysync::VALTYPE_INT16;
+ using sysync::VALTYPE_INT32;
+
+ using sysync::Manufacturer;
+ using sysync::Description;
+ using sysync::MinVersion;
+ using sysync::YesField;
+ using sysync::IsAdmin;
+
+
+#include "sync_dbapi.h" // include the interface file and utilities
+#define MyDB "OceanBlue" // example debug name
+
+
+
+/* ----- OceanBlue MODULE --------------------------------------------------------- */
+TSyError OceanBlue_Module::CreateContext( cAppCharP subName )
+{
+ DEBUG_DB( fCB, MyDB,Mo_CC, "'%s' mod='%s' sub='%s'", fContextName.c_str(), fModName.c_str(), subName );
+ return LOCERR_OK;
+} // CreateContext
+
+CVersion OceanBlue_Module::Version() { return Plugin_Version( 0 ); }
+
+TSyError OceanBlue_Module::Capabilities( appCharP &capa )
+{
+ string s = MyPlatform();
+ s+= '\n'; s+= DLL_Info;
+ Manufacturer ( s, "Synthesis AG" );
+ Description ( s, "OceanBlue Adapter" );
+ MinVersion ( s, VP_GlobMulti ); // at least V1.5.1
+ YesField ( s, CA_ADMIN_Info ); // show "ADMIN" info
+ capa= StrAlloc( s.c_str() );
+ return LOCERR_OK;
+} // Capabilities
+
+TSyError OceanBlue_Module::PluginParams ( cAppCharP /* mConfigParams */, // ignore
+ CVersion /* engineVersion */ ) { return LOCERR_OK; }
+
+void OceanBlue_Module::DisposeObj( void* memory ) { StrDispose( memory ); }
+
+TSyError OceanBlue_Module::DeleteContext() {
+ DEBUG_DB( fCB, MyDB,Mo_DC, "'%s'", fContextName.c_str() ); return LOCERR_OK;
+} // DeleteContext
+
+
+
+/* ----- OceanBlue SESSION -------------------------------------------------------- */
+TSyError OceanBlue_Session::CreateContext() {
+ DEBUG_DB( fCB, MyDB,Se_CC, "session='%s'", fSessionName.c_str() ); return LOCERR_OK;
+} // CreateContext
+
+// -- device
+TSyError OceanBlue_Session::CheckDevice( cAppCharP /* aDeviceID */,
+ appCharP &sDevKey,
+ appCharP &nonce )
+{
+ sDevKey= StrAlloc( "key" );
+ nonce = StrAlloc( "nonce" ); return LOCERR_OK;
+} // CheckDevice
+
+TSyError OceanBlue_Session::GetNonce ( appCharP& /* nonce */ ) { return DB_NotFound; }
+TSyError OceanBlue_Session::SaveNonce ( cAppCharP /* nonce */ ) { return LOCERR_OK; }
+TSyError OceanBlue_Session::SaveDeviceInfo( cAppCharP /* aDeviceInfo */ ) { return LOCERR_OK; }
+TSyError OceanBlue_Session::GetDBTime ( appCharP& /* currDBTime */ ) { return DB_NotFound; }
+
+// -- login
+sInt32 OceanBlue_Session::PasswordMode() { return Password_Mode_Undefined; }
+
+TSyError OceanBlue_Session::Login ( cAppCharP /* sUsername */,
+ appCharP& /* sPassword */,
+ appCharP& /* sUsrKey */ ) { return DB_Forbidden; }
+
+TSyError OceanBlue_Session::Logout() { return LOCERR_OK; }
+
+// -- general
+void OceanBlue_Session::DisposeObj( void* memory ) { StrDispose( memory ); }
+void OceanBlue_Session::ThreadMayChangeNow() { }
+void OceanBlue_Session::DispItems( bool /* allFields */, cAppCharP /* specificItem */ ) { }
+
+TSyError OceanBlue_Session::Adapt_Item( appCharP& /* sItemData1 */,
+ appCharP& /* sItemData2 */,
+ appCharP& /* sLocalVars */,
+ uInt32 /* sIdentifier */ ) { return LOCERR_OK; }
+
+TSyError OceanBlue_Session::DeleteContext() {
+ DEBUG_DB( fCB, MyDB,Se_DC, "session='%s'", fSessionName.c_str() ); return LOCERR_OK;
+} // DeleteContext
+
+
+
+/* ----- OceanBlue DATASTORE ------------------------------------------------------ */
+TSyError OceanBlue::CreateContext ( cAppCharP sDevKey, cAppCharP sUsrKey )
+{
+ DEBUG_DB( fCB, MyDB,Da_CC, "'%s' (%s) dev='%s' usr='%s'",
+ fContextName.c_str(), fAsAdmin?"admin":"data", sDevKey,sUsrKey );
+
+ #ifndef DISABLE_PLUGIN_DATASTOREADMIN
+ fAdmin.Init( fCB, MyDB, "", fContextName, sDevKey,sUsrKey );
+ #endif
+ #ifndef DISABLE_PLUGIN_BLOBS
+ fBlob.Init ( fCB, MyDB, "", fContextName, sDevKey,sUsrKey );
+ #endif
+
+ fNthItem= 0; // initialize it
+ return LOCERR_OK;
+} // CreateContext
+
+
+uInt32 OceanBlue::ContextSupport( cAppCharP /* aContextRules */ ) { return 0; }
+uInt32 OceanBlue::FilterSupport ( cAppCharP /* aFilterRules */ ) { return 0; }
+
+// -- admin
+#ifndef DISABLE_PLUGIN_DATASTOREADMIN
+ TSyError OceanBlue::LoadAdminData( cAppCharP aLocDB, cAppCharP aRemDB, appCharP &adminData ) {
+ return fAdmin.LoadAdminData ( aLocDB, aRemDB, &adminData );
+ } // LoadAdminData
+
+ TSyError OceanBlue::SaveAdminData( cAppCharP adminData ) {
+ return fAdmin.SaveAdminData ( adminData );
+ } // SaveAdminData
+
+ bool OceanBlue::ReadNextMapItem ( MapID mID, bool aFirst ) {
+ return fAdmin.ReadNextMapItem ( mID, aFirst );
+ } // ReadNextMapItem
+
+ TSyError OceanBlue::InsertMapItem ( cMapID mID ) {
+ return fAdmin.InsertMapItem ( mID );
+ } // InsertMapItem
+
+ TSyError OceanBlue::UpdateMapItem ( cMapID mID ) {
+ return fAdmin.UpdateMapItem ( mID );
+ } // UpdateMapItem
+
+ TSyError OceanBlue::DeleteMapItem ( cMapID mID ) {
+ return fAdmin.DeleteMapItem ( mID );
+ } // DeleteMapItem
+#else
+ TSyError OceanBlue::LoadAdminData( cAppCharP /* aLocDB */,
+ cAppCharP /* aRemDB */, appCharP& /* adminData */ ) { return DB_Forbidden; }
+ TSyError OceanBlue::SaveAdminData ( cAppCharP /* adminData */ ) { return DB_Forbidden; }
+ bool OceanBlue::ReadNextMapItem ( MapID /* mID */, bool /* aFirst */ ) { return false; }
+ TSyError OceanBlue::InsertMapItem ( cMapID /* mID */ ) { return DB_Forbidden; }
+ TSyError OceanBlue::UpdateMapItem ( cMapID /* mID */ ) { return DB_Forbidden; }
+ TSyError OceanBlue::DeleteMapItem ( cMapID /* mID */ ) { return DB_Forbidden; }
+#endif
+
+
+// -- read
+TSyError OceanBlue::StartDataRead ( cAppCharP /* lastToken */,
+ cAppCharP /* resumeToken */ ) { return LOCERR_OK; }
+
+TSyError OceanBlue::ReadNextItem ( ItemID /* aID */, appCharP& /* aItemData */,
+ sInt32 &aStatus, bool /* aFirst */ )
+ { aStatus= ReadNextItem_EOF; return LOCERR_OK; }
+TSyError OceanBlue::ReadNextItemAsKey( ItemID /* aID */, KeyH /* aItemKey */,
+ sInt32 &aStatus, bool /* aFirst */ )
+ { aStatus= ReadNextItem_EOF; return LOCERR_OK; }
+
+TSyError OceanBlue::ReadItem ( cItemID /* aID */, appCharP& /* aItemData */ ) { return LOCERR_NOTIMP; }
+TSyError OceanBlue::ReadItemAsKey ( cItemID /* aID */, KeyH /* aItemKey */ ) { return LOCERR_NOTIMP; }
+TSyError OceanBlue::EndDataRead() { return LOCERR_OK; }
+
+// -- write
+TSyError OceanBlue::StartDataWrite() { return LOCERR_OK; }
+TSyError OceanBlue::InsertItem ( cAppCharP /* aItemData */, ItemID /* newID */ ) { return LOCERR_NOTIMP; }
+TSyError OceanBlue::InsertItemAsKey ( KeyH /* aItemKey */, ItemID /* newID */ ) { return LOCERR_NOTIMP; }
+TSyError OceanBlue::UpdateItem ( cAppCharP /* aItemData */, cItemID /* aID */,
+ ItemID /* updID */ ) { return LOCERR_NOTIMP; }
+TSyError OceanBlue::UpdateItemAsKey ( KeyH /* aItemKey */, cItemID /* aID */,
+ ItemID /* updID */ ) { return LOCERR_NOTIMP; }
+
+TSyError OceanBlue::MoveItem ( cItemID /* aID */, cAppCharP /* newParID */ ) { return LOCERR_NOTIMP; }
+TSyError OceanBlue::DeleteItem ( cItemID /* aID */ ) { return LOCERR_NOTIMP; }
+TSyError OceanBlue::FinalizeLocalID ( cItemID /* aID */, ItemID /* updID */ ) { return LOCERR_NOTIMP; }
+TSyError OceanBlue::DeleteSyncSet() { return LOCERR_NOTIMP; }
+TSyError OceanBlue::EndDataWrite ( bool /* success */, appCharP& /* newToken */ ) { return LOCERR_OK; }
+
+
+// -- blobs
+TSyError OceanBlue::ReadBlob ( cItemID aID, cAppCharP aBlobID,
+ appPointer &aBlkPtr, memSize &aBlkSize, memSize &aTotSize,
+ bool aFirst, bool &aLast )
+{
+ #ifndef DISABLE_PLUGIN_BLOBS
+ return fBlob.ReadBlob ( aID,aBlobID, &aBlkPtr,&aBlkSize,&aTotSize, aFirst,&aLast );
+ #endif
+ return LOCERR_NOTIMP;
+} // ReadBlob
+
+TSyError OceanBlue::WriteBlob ( cItemID aID, cAppCharP aBlobID,
+ appPointer aBlkPtr, memSize aBlkSize, memSize aTotSize,
+ bool aFirst, bool aLast )
+{
+ #ifndef DISABLE_PLUGIN_BLOBS
+ return fBlob.WriteBlob ( aID,aBlobID, aBlkPtr,aBlkSize,aTotSize, aFirst,aLast );
+ #endif
+ return LOCERR_NOTIMP;
+} // WriteBlob
+
+TSyError OceanBlue::DeleteBlob( cItemID aID, cAppCharP aBlobID )
+{
+ #ifndef DISABLE_PLUGIN_BLOBS
+ return fBlob.DeleteBlob( aID,aBlobID );
+ #endif
+ return LOCERR_NOTIMP;
+} // DeleteBlob
+
+
+// -- general
+void OceanBlue::WriteLogData ( cAppCharP /* logData */ ) { }
+void OceanBlue::DisposeObj ( void* /* memory */ ) { }
+void OceanBlue::ThreadMayChangeNow() { }
+void OceanBlue::DispItems ( bool /* allFields */,
+ cAppCharP /* specificItem */ ) { }
+
+TSyError OceanBlue::Adapt_Item ( appCharP& /* aItemData1 */,
+ appCharP& /* aItemData2 */,
+ appCharP& /* aLocalVars */,
+ uInt32 /* aIdentifier */ ) { return LOCERR_OK; }
+
+TSyError OceanBlue::DeleteContext() {
+ DEBUG_DB( fCB, MyDB,Da_DC, "'%s'", fContextName.c_str() ); return LOCERR_OK;
+} // DeleteContext
+
+
+// ---- call-in functions ----
+sInt32 OceanBlue::GetValueID ( KeyH aItemKey, string aValName, string suff )
+{
+ if (!fCB->ui.GetValueID) return -1;
+ aValName+= suff;
+ return fCB->ui.GetValueID ( fCB, aItemKey, aValName.c_str() );
+} // GetValueID
+
+TSyError OceanBlue::GetValue ( KeyH aItemKey, string aValName, uInt16 aValType,
+ appPointer aBuffer, memSize aBufSize, memSize &aValSize, string suff )
+{
+ if (!fCB->ui.GetValue) return LOCERR_NOTIMP;
+ aValName+= suff;
+ return fCB->ui.GetValue ( fCB, aItemKey, aValName.c_str(), aValType, aBuffer, aBufSize, &aValSize );
+} // GetValue
+
+TSyError OceanBlue::GetValueByID( KeyH aItemKey, sInt32 aID, sInt32 arrIndex,
+ uInt16 aValType,
+ appPointer aBuffer, memSize aBufSize, memSize &aValSize, string suff )
+{
+ if (!fCB->ui.GetValueByID) return LOCERR_NOTIMP;
+ if (suff!="") aID+= GetValueID ( aItemKey, VALNAME_FLAG, suff );
+ return fCB->ui.GetValueByID( fCB, aItemKey, aID, arrIndex, aValType, aBuffer, aBufSize, &aValSize );
+} // GetValueByID
+
+TSyError OceanBlue::SetValue ( KeyH aItemKey, string aValName, uInt16 aValType,
+ appPointer aBuffer, memSize aValSize, string suff )
+{
+ if (!fCB->ui.SetValue) return LOCERR_NOTIMP;
+ aValName+= suff;
+ return fCB->ui.SetValue ( fCB, aItemKey, aValName.c_str(), aValType, aBuffer, aValSize );
+} // SetValue
+
+TSyError OceanBlue::SetValueByID( KeyH aItemKey, sInt32 aID, sInt32 arrIndex,
+ uInt16 aValType,
+ appPointer aBuffer, memSize aValSize, string suff )
+{
+ if (!fCB->ui.SetValueByID) return LOCERR_NOTIMP;
+ if (suff!="") aID+= GetValueID ( aItemKey, VALNAME_FLAG, suff );
+ return fCB->ui.SetValueByID( fCB, aItemKey, aID, arrIndex, aValType, aBuffer, aValSize );
+} // SetValueByID
+
+
+
+// ---- convenience routines to access Get/SetValue for specific types ----
+// string
+TSyError OceanBlue::GetStr( KeyH aItemKey, string aValName, string &aText, string suff )
+{
+ TSyError err;
+ const uInt16 ty= VALTYPE_TEXT;
+
+ const int txtFSz= 80; // a readonable string length to catch it at once
+ char txt[ txtFSz ];
+ memSize txtSz;
+
+ // try directly to fit in a short string first
+ appCharP f= (char*)&txt;
+ err= GetValue( aItemKey, aValName, ty, f, txtFSz, txtSz, suff );
+
+ bool tooShort= err==LOCERR_TRUNCATED;
+ if (tooShort) {
+ err= GetValue( aItemKey, aValName, ty, f, 0, txtSz, suff ); // get size
+ f= (appCharP)malloc( txtSz+1 ); // plus NUL termination
+ err= GetValue( aItemKey, aValName, ty, f, txtSz+1, txtSz, suff ); // no error anymore
+ } // if
+
+ if (err) aText= "";
+ else aText= f; // assign it
+
+ if (tooShort)
+ free( f ); // and deallocate again, if used dynamically
+
+ return err;
+} // GetStr
+
+
+TSyError OceanBlue::GetStrByID( KeyH aItemKey, sInt32 aID, string &aText,
+ sInt32 arrIndex, string suff )
+{
+ TSyError err;
+ const uInt16 ty= VALTYPE_TEXT;
+
+ const int txtFSz= 80; // a readonable string length to catch it at once
+ char txt[ txtFSz ];
+ memSize txtSz;
+
+ // try directly to fit in a short string first
+ appCharP f= (char*)&txt;
+ err= GetValueByID( aItemKey, aID,arrIndex, ty, f, txtFSz, txtSz, suff );
+
+ bool tooShort= err==LOCERR_TRUNCATED;
+ if (tooShort) {
+ err= GetValueByID( aItemKey, aID,arrIndex, ty, f, 0, txtSz, suff ); // get size
+ f= (appCharP)malloc( txtSz+1 ); // plus NUL termination
+ err= GetValueByID( aItemKey, aID,arrIndex, ty, f, txtSz+1, txtSz, suff ); // no error anymore
+ } // if
+
+ if (err) aText= "";
+ else aText= f; // assign it
+
+ if (tooShort)
+ free( f ); // and deallocate again, if used dynamically
+
+ return err;
+} // GetStrByID
+
+
+TSyError OceanBlue::SetStr ( KeyH aItemKey, string aValName, string aText, string suff ) {
+ // -1 automatically calculate length from null-terminated string
+ return SetValue ( aItemKey, aValName, VALTYPE_TEXT, (appPointer)aText.c_str(), (memSize)-1, suff );
+} // SetStr
+
+
+TSyError OceanBlue::SetStrByID( KeyH aItemKey, sInt32 aID, string aText,
+ sInt32 arrIndex, string suff ) {
+ // -1 automatically calculate length from null-terminated string
+ return SetValueByID( aItemKey, aID,arrIndex, VALTYPE_TEXT, (appPointer)aText.c_str(), (memSize)-1, suff );
+} // SetStrByID
+
+
+
+/* sInt16 / uInt16 */
+TSyError OceanBlue::GetInt16 ( KeyH aItemKey, string aValName, sInt16 &aValue,
+ string suff ) {
+ memSize vSize;
+ return GetValue ( aItemKey, aValName, VALTYPE_INT16,&aValue, sizeof(aValue),vSize, suff );
+} // GetInt16
+TSyError OceanBlue::GetInt16 ( KeyH aItemKey, string aValName, uInt16 &aValue,
+ string suff ) {
+ memSize vSize;
+ return GetValue ( aItemKey, aValName, VALTYPE_INT16,&aValue, sizeof(aValue),vSize, suff );
+} // GetInt16
+
+TSyError OceanBlue::GetInt16ByID( KeyH aItemKey, sInt32 aID, sInt16 &aValue,
+ sInt32 arrIndex, string suff ) {
+ memSize vSize;
+ return GetValueByID( aItemKey, aID,arrIndex, VALTYPE_INT16,&aValue, sizeof(aValue),vSize, suff );
+} // GetInt16ByID
+TSyError OceanBlue::GetInt16ByID( KeyH aItemKey, sInt32 aID, uInt16 &aValue,
+ sInt32 arrIndex, string suff ) {
+ memSize vSize;
+ return GetValueByID( aItemKey, aID,arrIndex, VALTYPE_INT16,&aValue, sizeof(aValue),vSize, suff );
+} // GetInt16ByID
+
+
+TSyError OceanBlue::SetInt16 ( KeyH aItemKey, string aValName, sInt16 aValue,
+ string suff ) {
+ return SetValue ( aItemKey, aValName, VALTYPE_INT16,&aValue, sizeof(aValue), suff );
+} // SetInt16
+
+TSyError OceanBlue::SetInt16ByID( KeyH aItemKey, sInt32 aID, sInt16 aValue,
+ sInt32 arrIndex, string suff ) {
+ return SetValueByID( aItemKey, aID,arrIndex, VALTYPE_INT16,&aValue, sizeof(aValue), suff );
+} // SetInt16ByID
+
+
+
+/* sInt32 / uInt32 */
+TSyError OceanBlue::GetInt32 ( KeyH aItemKey, string aValName, sInt32 &aValue,
+ string suff ) {
+ memSize vSize;
+ return GetValue ( aItemKey, aValName, VALTYPE_INT32,&aValue, sizeof(aValue),vSize, suff );
+} // GetInt32
+TSyError OceanBlue::GetInt32 ( KeyH aItemKey, string aValName, uInt32 &aValue,
+ string suff ) {
+ memSize vSize;
+ return GetValue ( aItemKey, aValName, VALTYPE_INT32,&aValue, sizeof(aValue),vSize, suff );
+} // GetInt32
+
+TSyError OceanBlue::GetInt32ByID( KeyH aItemKey, sInt32 aID, sInt32 &aValue,
+ sInt32 arrIndex, string suff ) {
+ memSize vSize;
+ return GetValueByID( aItemKey, aID,arrIndex, VALTYPE_INT32,&aValue, sizeof(aValue),vSize, suff );
+} // GetInt32ByID
+TSyError OceanBlue::GetInt32ByID( KeyH aItemKey, sInt32 aID, uInt32 &aValue,
+ sInt32 arrIndex, string suff ) {
+ memSize vSize;
+ return GetValueByID( aItemKey, aID,arrIndex, VALTYPE_INT32,&aValue, sizeof(aValue),vSize, suff );
+} // GetInt32ByID
+
+
+TSyError OceanBlue::SetInt32 ( KeyH aItemKey, string aValName, sInt32 aValue,
+ string suff ) {
+ return SetValue ( aItemKey, aValName, VALTYPE_INT32,&aValue, sizeof(aValue), suff );
+} // SetInt32
+
+TSyError OceanBlue::SetInt32ByID( KeyH aItemKey, sInt32 aID, sInt32 aValue,
+ sInt32 arrIndex, string suff ) {
+ return SetValueByID( aItemKey, aID,arrIndex, VALTYPE_INT32,&aValue, sizeof(aValue), suff );
+} // SetInt32ByID
+
+
+
+/* -------------------------------------------------------------------------------- */
+#if defined MACOSX && defined __MWERKS__
+#pragma export on
+#endif
+
+/* ---- MODULE -------------------------------------------------------------------- */
+static MyAdapter_Module* SW_Mo( CContext mc ) { return (MyAdapter_Module*)mc; }
+TSyError Module_CreateContext( CContext *mc, cAppCharP moduleName,
+ cAppCharP subName,
+ cAppCharP mContextName, DB_Callback mCB )
+{
+ MyAdapter_Module* swm= new MyAdapter_Module; if (swm==NULL) return DB_Full;
+ swm->fCB = mCB;
+ swm->fModName = moduleName;
+ swm->fContextName= mContextName;
+ TSyError err= swm->CreateContext ( subName );
+ if (err)delete swm;
+ else *mc= (CContext)swm;
+ return err;
+} // Module_CreateContext
+
+CVersion Module_Version ( CContext mc ) {
+ if (!mc ) return Plugin_Version( 0 );
+ return SW_Mo( mc )->Version();
+} // Module_Version
+
+TSyError Module_Capabilities( CContext mc, appCharP *capa ) {
+ return SW_Mo( mc )->Capabilities ( *capa );
+} // Module_Capabilities
+
+TSyError Module_PluginParams( CContext mc, cAppCharP mConfigParams, CVersion engineVersion ) {
+ return SW_Mo( mc )->PluginParams ( mConfigParams, engineVersion );
+} // Module_PluginParams
+
+void Module_DisposeObj ( CContext mc, void* memory ) {
+ SW_Mo( mc )->DisposeObj ( memory );
+} // Module_DisposeObj
+
+TSyError Module_DeleteContext( CContext mc )
+{
+ MyAdapter_Module* swm= SW_Mo( mc );
+ TSyError err= swm->DeleteContext();
+ delete swm;
+ return err;
+} // Module_DeleteContext
+
+
+
+
+/* ---- SESSION ------------------------------------------------------------------- */
+static MyAdapter_Session* SW_Se( CContext sc ) { return (MyAdapter_Session*)sc; }
+TSyError Session_CreateContext ( CContext *sc, cAppCharP sessionName, DB_Callback sCB )
+{
+ MyAdapter_Session* sws= new MyAdapter_Session; if (sws==NULL) return DB_Full;
+ sws->fCB = sCB;
+ sws->fSessionName= sessionName;
+ TSyError err= sws->CreateContext();
+ if (err)delete sws;
+ else *sc= (CContext)sws;
+ return err;
+} // Session_CreateContext
+
+
+// -- device
+TSyError Session_CheckDevice ( CContext sc, cAppCharP aDeviceID, appCharP *sDevKey,
+ appCharP *nonce ) {
+ return SW_Se( sc )->CheckDevice ( aDeviceID, *sDevKey,*nonce );
+} // Session_CheckDevice
+
+TSyError Session_GetNonce ( CContext sc, appCharP *nonce ) {
+ return SW_Se( sc )->GetNonce ( *nonce );
+} // Session_GetNonce
+
+TSyError Session_SaveNonce ( CContext sc, cAppCharP nonce ) {
+ return SW_Se( sc )->SaveNonce ( nonce );
+} // Session_SaveNonce
+
+TSyError Session_SaveDeviceInfo( CContext sc, cAppCharP aDeviceInfo ) {
+ return SW_Se( sc )->SaveDeviceInfo ( aDeviceInfo );
+} // Session_SaveDeviceInfo
+
+TSyError Session_GetDBTime ( CContext sc, appCharP *currentDBTime ) {
+ return SW_Se( sc )->GetDBTime ( *currentDBTime );
+} // Session_GetDBTime
+
+
+// -- login
+sInt32 Session_PasswordMode( CContext sc ) {
+ return SW_Se( sc )->PasswordMode();
+} // Session_PasswordMode
+
+TSyError Session_Login ( CContext sc, cAppCharP sUsername, appCharP *sPassword, appCharP *sUsrKey ) {
+ return SW_Se( sc )->Login ( sUsername, *sPassword, *sUsrKey );
+} // Session_Login
+
+TSyError Session_Logout( CContext sc ) {
+ return SW_Se( sc )->Logout();
+} // Session_Logout
+
+
+// -- general
+void Session_DisposeObj( CContext sc, void* memory ) {
+ SW_Se( sc )->DisposeObj ( memory );
+} // Session_DisposeObj
+
+void Session_ThreadMayChangeNow( CContext sc ) {
+ SW_Se( sc )->ThreadMayChangeNow();
+} // Session_ThreadMayChangeNow
+
+void Session_DispItems ( CContext sc, bool allFields, cAppCharP specificItem ) {
+ SW_Se( sc )->DispItems ( allFields, specificItem );
+} // Session_DispItems
+
+TSyError Session_AdaptItem ( CContext sc, appCharP *sItemData1,
+ appCharP *sItemData2,
+ appCharP *sLocalVars, uInt32 sIdentifier ) {
+ return SW_Se( sc )->Adapt_Item( *sItemData1, *sItemData2, *sLocalVars, sIdentifier );
+} // Session_AdaptItem
+
+TSyError Session_DeleteContext( CContext sc )
+{
+ MyAdapter_Session* sws= SW_Se( sc );
+ TSyError err= sws->DeleteContext();
+ delete sws;
+ return err;
+} // Session_DeleteContext
+
+
+
+
+/* ---- DATASTORE ----------------------------------------------------------------- */
+static MyAdapter* SW( CContext ac ) { return (MyAdapter*)ac; }
+TSyError CreateContext( CContext *ac, cAppCharP aContextName, DB_Callback aCB,
+ cAppCharP sDevKey,
+ cAppCharP sUsrKey )
+{
+ MyAdapter* sw= new MyAdapter; if (sw==NULL) return DB_Full;
+ sw->fCB = aCB;
+ sw->fContextName= aContextName;
+ sw->fAsAdmin= IsAdmin( sw->fContextName );
+ TSyError err= sw->CreateContext( sDevKey,sUsrKey );
+ if (err) delete sw;
+ else *ac= (CContext)sw;
+ return err;
+} // CreateContext
+
+uInt32 ContextSupport( CContext ac, cAppCharP aContextRules ) {
+ return SW( ac )->ContextSupport ( aContextRules );
+} // ContextSupport
+
+uInt32 FilterSupport ( CContext ac, cAppCharP aFilterRules ) {
+ return SW( ac )->FilterSupport ( aFilterRules );
+} // FilterSupport
+
+
+
+// -- admin
+TSyError LoadAdminData ( CContext ac, cAppCharP aLocDB, cAppCharP aRemDB, appCharP *adminData ) {
+ return SW( ac )->LoadAdminData ( aLocDB, aRemDB, *adminData );
+} // LoadAdminData
+
+TSyError SaveAdminData ( CContext ac, cAppCharP adminData ) {
+ return SW( ac )->SaveAdminData ( adminData );
+} // SaveAdminData
+
+bool ReadNextMapItem( CContext ac, MapID mID, bool aFirst ) {
+ return SW( ac )->ReadNextMapItem ( mID, aFirst );
+} // ReadNextMapItem
+
+TSyError InsertMapItem ( CContext ac, cMapID mID ) {
+ return SW( ac )->InsertMapItem ( mID );
+} // InsertMapItem
+
+TSyError UpdateMapItem ( CContext ac, cMapID mID ) {
+ return SW( ac )->UpdateMapItem ( mID );
+} // UpdateMapItem
+
+TSyError DeleteMapItem ( CContext ac, cMapID mID ) {
+ return SW( ac )->DeleteMapItem ( mID );
+} // DeleteMapItem
+
+
+
+// -- read
+TSyError StartDataRead ( CContext ac, cAppCharP lastToken, cAppCharP resumeToken ) {
+ return SW( ac )->StartDataRead ( lastToken, resumeToken );
+} // StartDataRead
+
+TSyError ReadNextItem ( CContext ac, ItemID aID, appCharP *aItemData, sInt32 *aStatus, bool aFirst ) {
+ return SW( ac )->ReadNextItem ( aID, *aItemData, *aStatus, aFirst );
+} // ReadNextItem
+
+TSyError ReadNextItemAsKey( CContext ac, ItemID aID, KeyH aItemKey, sInt32 *aStatus, bool aFirst ) {
+ return SW( ac )->ReadNextItemAsKey ( aID, aItemKey, *aStatus, aFirst );
+} // ReadNextItemAsKey
+
+TSyError ReadItem ( CContext ac, cItemID aID, appCharP *aItemData ) {
+ return SW( ac )->ReadItem ( aID, *aItemData );
+} // ReadItem
+
+TSyError ReadItemAsKey ( CContext ac, cItemID aID, KeyH aItemKey ) {
+ return SW( ac )->ReadItemAsKey ( aID, aItemKey );
+} // ReadItemAsKey
+
+TSyError EndDataRead ( CContext ac ) {
+ return SW( ac )->EndDataRead();
+} // EndDataRead
+
+
+
+// -- write
+TSyError StartDataWrite ( CContext ac ) {
+ return SW( ac )->StartDataWrite();
+} // StartDataWrite
+
+TSyError InsertItem ( CContext ac, cAppCharP aItemData, ItemID newID ) {
+ return SW( ac )->InsertItem ( aItemData, newID );
+} // InsertItem
+
+TSyError InsertItemAsKey( CContext ac, KeyH aItemKey, ItemID newID ) {
+ return SW( ac )->InsertItemAsKey ( aItemKey, newID );
+} // InsertItemAsKey
+
+TSyError UpdateItem ( CContext ac, cAppCharP aItemData, cItemID aID, ItemID updID ) {
+ return SW( ac )->UpdateItem ( aItemData, aID, updID );
+} // UpdateItem
+
+TSyError UpdateItemAsKey( CContext ac, KeyH aItemKey, cItemID aID, ItemID updID ) {
+ return SW( ac )->UpdateItemAsKey ( aItemKey, aID, updID );
+} // UpdateItemAsKey
+
+TSyError MoveItem ( CContext ac, cItemID aID, cAppCharP newParID ) {
+ return SW( ac )->MoveItem ( aID, newParID );
+} // MoveItem
+
+TSyError DeleteItem ( CContext ac, cItemID aID ) {
+ return SW( ac )->DeleteItem ( aID );
+} // DeleteItem
+
+TSyError FinalizeLocalID( CContext ac, cItemID aID, ItemID updID ) {
+ return SW( ac )->FinalizeLocalID ( aID, updID );
+} // FinalizeLocalID
+
+TSyError DeleteSyncSet ( CContext ac ) {
+ return SW( ac )->DeleteSyncSet();
+} // DeleteSyncSet
+
+TSyError EndDataWrite ( CContext ac, bool success, appCharP *newToken ) {
+ return SW( ac )->EndDataWrite ( success, *newToken );
+} // EndDataWrite
+
+
+
+// -- blobs
+TSyError ReadBlob ( CContext ac, cItemID aID, cAppCharP aBlobID,
+ appPointer *aBlkPtr, memSize *aBlkSize,
+ memSize *aTotSize,
+ bool aFirst, bool *aLast ) {
+ return SW( ac )->ReadBlob ( aID,aBlobID, *aBlkPtr,*aBlkSize,*aTotSize, aFirst,*aLast );
+} // ReadBlob
+
+TSyError WriteBlob ( CContext ac, cItemID aID, cAppCharP aBlobID,
+ appPointer aBlkPtr, memSize aBlkSize,
+ memSize aTotSize,
+ bool aFirst, bool aLast ) {
+ return SW( ac )->WriteBlob ( aID,aBlobID, aBlkPtr, aBlkSize, aTotSize, aFirst, aLast );
+} // WriteBlob
+
+TSyError DeleteBlob( CContext ac, cItemID aID, cAppCharP aBlobID ) {
+ return SW( ac )->DeleteBlob( aID,aBlobID );
+} // DeleteBlob
+
+
+
+// -- general
+void WriteLogData( CContext ac, cAppCharP logData ) {
+ SW( ac )->WriteLogData ( logData );
+} // WriteLogData
+
+void DisposeObj ( CContext ac, void* memory ) {
+ SW( ac )->DisposeObj ( memory );
+} // DisposeObj
+
+void ThreadMayChangeNow( CContext ac ) {
+ SW( ac )->ThreadMayChangeNow();
+} // ThreadMayChangeNow
+
+void DispItems ( CContext ac, bool allFields, cAppCharP specificItem ) {
+ SW( ac )->DispItems ( allFields, specificItem );
+} // DispItems
+
+TSyError AdaptItem ( CContext ac, appCharP *aItemData1,
+ appCharP *aItemData2,
+ appCharP *aLocalVars, uInt32 aIdentifier ) {
+ return SW( ac )->Adapt_Item( *aItemData1, *aItemData2, *aLocalVars, aIdentifier );
+} // AdaptItem
+
+TSyError DeleteContext( CContext ac )
+{
+ MyAdapter* sw= SW( ac );
+ TSyError err= sw->DeleteContext();
+ delete sw;
+ return err;
+} // DeleteContext
+
+
+} // namespace
+/* eof */
diff --git a/src/sysync_SDK/DB_Interfaces/snowwhite/oceanblue.h b/src/sysync_SDK/DB_Interfaces/snowwhite/oceanblue.h
new file mode 100644
index 0000000..67bfc19
--- /dev/null
+++ b/src/sysync_SDK/DB_Interfaces/snowwhite/oceanblue.h
@@ -0,0 +1,268 @@
+/*
+ * File: oceanblue.h
+ *
+ * Author: Beat Forster (bfo@synthesis.ch)
+ *
+ * Base class for datastore plugins in C++
+ *
+ * Copyright (c) 2008-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ */
+
+#ifndef OCEANBLUE_H
+#define OCEANBLUE_H
+
+#include "sync_include.h" // include general SDK definitions
+#include "sync_dbapidef.h" // include the interface file and utilities
+#include "SDK_util.h" // include SDK utilities
+#include "SDK_support.h" // include more SDK support
+
+// There are reference implementations for admin data handling and for BLOBs
+// But they needn't to be used. The "target_options.h" defines whether to use them
+#ifndef DISABLE_PLUGIN_DATASTOREADMIN
+ #include "admindata.h"
+#endif
+#ifndef DISABLE_PLUGIN_BLOBS
+ #include "blobs.h"
+#endif
+
+// Use them in two different namespaces
+namespace oceanblue {
+ using sysync::sInt16;
+ using sysync::uInt16;
+ using sysync::sInt32;
+ using sysync::uInt32;
+ using sysync::appPointer;
+ using sysync::memSize;
+
+ using sysync::DB_Callback;
+ using sysync::MapID;
+ using sysync::cMapID;
+ using sysync::ItemID;
+ using sysync::cItemID;
+ using sysync::KeyH;
+
+#ifndef DISABLE_PLUGIN_DATASTOREADMIN
+ using sysync::TAdminData;
+#endif
+#ifndef DISABLE_PLUGIN_BLOBS
+ using sysync::TBlob;
+#endif
+
+
+// The three classes "OceanBlue_Module", "OceanBlue_Session" and "OceanBlue" should
+// be derived as shown in the "snowwhite" adapter example.
+// It is intended not to change this module, but only the derived adapter.
+// The name of the derived adapter is defined "myadapter.h"
+
+
+// Module access
+class OceanBlue_Module
+{
+ public:
+ virtual ~OceanBlue_Module() { }; // virtual destructor
+
+ DB_Callback fCB; // local callback reference
+ string fModName; // the module name
+ string fContextName; // the context name
+
+ virtual TSyError CreateContext ( cAppCharP subName );
+ virtual CVersion Version();
+ virtual TSyError Capabilities ( appCharP &mCapabilities );
+ virtual TSyError PluginParams ( cAppCharP mConfigParams, CVersion engineVersion );
+ virtual void DisposeObj ( void* memory );
+ virtual TSyError DeleteContext();
+}; // OceanBlue_Module
+
+
+// Session access
+class OceanBlue_Session
+{
+ public:
+ virtual ~OceanBlue_Session() { }; // virtual destructor
+
+ DB_Callback fCB; // local callback reference
+ string fSessionName; // the session name
+
+ virtual TSyError CreateContext();
+
+ // -- device
+ virtual TSyError CheckDevice ( cAppCharP aDeviceID,
+ appCharP &sDevKey,
+ appCharP &nonce );
+ virtual TSyError GetNonce ( appCharP &nonce );
+ virtual TSyError SaveNonce ( cAppCharP nonce );
+ virtual TSyError SaveDeviceInfo ( cAppCharP aDeviceInfo );
+ virtual TSyError GetDBTime ( appCharP &currentDBTime );
+
+ // -- login
+ virtual sInt32 PasswordMode();
+ virtual TSyError Login ( cAppCharP sUsername,
+ appCharP &sPassword, appCharP &sUsrKey );
+ virtual TSyError Logout();
+
+ // -- general
+ virtual void DisposeObj ( void* memory );
+ virtual void ThreadMayChangeNow();
+ virtual void DispItems ( bool allFields,
+ cAppCharP specificItem );
+
+ virtual TSyError Adapt_Item ( appCharP &sItemData1,
+ appCharP &sItemData2,
+ appCharP &sLocalVars, uInt32 sIdentifier );
+ virtual TSyError DeleteContext();
+}; // OceanBlue_Session
+
+
+// Datastore access
+class OceanBlue
+{
+ public:
+ virtual ~OceanBlue() { }; // virtual destructor
+
+ DB_Callback fCB; // local callback reference
+ string fContextName; // the context name
+ bool fAsAdmin; // true, if it is admin context
+ uInt32 fNthItem;
+
+ #ifndef DISABLE_PLUGIN_DATASTOREADMIN
+ TAdminData fAdmin; // an internal ADMIN implementation can be used
+ #endif
+ #ifndef DISABLE_PLUGIN_BLOBS
+ TBlob fBlob; // an internal BLOB implementation can be used
+ #endif
+
+
+ virtual TSyError CreateContext ( cAppCharP sDevKey, cAppCharP sUsrKey );
+ virtual uInt32 ContextSupport ( cAppCharP aContextRules );
+ virtual uInt32 FilterSupport ( cAppCharP aFilterRules );
+
+ // -- admin
+ virtual TSyError LoadAdminData ( cAppCharP aLocDB,
+ cAppCharP aRemDB,
+ appCharP &adminData );
+ virtual TSyError SaveAdminData ( cAppCharP adminData );
+
+ virtual bool ReadNextMapItem ( MapID mID, bool aFirst );
+ virtual TSyError InsertMapItem ( cMapID mID );
+ virtual TSyError UpdateMapItem ( cMapID mID );
+ virtual TSyError DeleteMapItem ( cMapID mID );
+
+ // -- read
+ virtual TSyError StartDataRead ( cAppCharP lastToken, cAppCharP resumeToken );
+
+ virtual TSyError ReadNextItem ( ItemID aID, appCharP &aItemData,
+ sInt32 &aStatus, bool aFirst );
+ virtual TSyError ReadNextItemAsKey ( ItemID aID, KeyH aItemKey,
+ sInt32 &aStatus, bool aFirst );
+
+ virtual TSyError ReadItem ( cItemID aID, appCharP &aItemData );
+ virtual TSyError ReadItemAsKey ( cItemID aID, KeyH aItemKey );
+ virtual TSyError EndDataRead();
+
+ // -- write
+ virtual TSyError StartDataWrite();
+ virtual TSyError InsertItem ( cAppCharP aItemData, ItemID newID );
+ virtual TSyError InsertItemAsKey( KeyH aItemKey, ItemID newID );
+ virtual TSyError UpdateItem ( cAppCharP aItemData, cItemID aID, ItemID updID );
+ virtual TSyError UpdateItemAsKey( KeyH aItemKey, cItemID aID, ItemID updID );
+
+ virtual TSyError MoveItem ( cItemID aID, cAppCharP newParID );
+ virtual TSyError DeleteItem ( cItemID aID );
+ virtual TSyError FinalizeLocalID ( cItemID aID, ItemID updID );
+ virtual TSyError DeleteSyncSet();
+ virtual TSyError EndDataWrite ( bool success, appCharP &newToken );
+
+ // -- blobs
+ virtual TSyError ReadBlob ( cItemID aID, cAppCharP aBlobID,
+ appPointer &aBlkPtr, memSize &aBlkSize,
+ memSize &aTotSize,
+ bool aFirst, bool &aLast );
+ virtual TSyError WriteBlob ( cItemID aID, cAppCharP aBlobID,
+ appPointer aBlkPtr, memSize aBlkSize,
+ memSize aTotSize,
+ bool aFirst, bool aLast );
+
+ virtual TSyError DeleteBlob ( cItemID aID, cAppCharP aBlobID );
+
+ // -- general
+ virtual void WriteLogData ( cAppCharP logData );
+ virtual void DisposeObj ( void* memory );
+ virtual void ThreadMayChangeNow();
+ virtual void DispItems ( bool allFields,
+ cAppCharP specificItem );
+
+ virtual TSyError Adapt_Item ( appCharP &aItemData1,
+ appCharP &aItemData2,
+ appCharP &aLocalVars, uInt32 aIdentifier );
+ virtual TSyError DeleteContext();
+
+
+
+ // ---- call-in functions ----
+ virtual sInt32 GetValueID ( KeyH aItemKey, string aValName, string suff="" );
+
+ virtual TSyError GetValue ( KeyH aItemKey, string aValName, uInt16 aValType,
+ appPointer aBuffer, memSize aBufSize, memSize &aValSize,
+ string suff="" );
+ virtual TSyError GetValueByID( KeyH aItemKey, sInt32 aID, sInt32 arrIndex,
+ uInt16 aValType,
+ appPointer aBuffer, memSize aBufSize, memSize &aValSize,
+ string suff="" );
+
+ virtual TSyError SetValue ( KeyH aItemKey, string aValName, uInt16 aValType,
+ appPointer aBuffer, memSize aValSize, string suff="" );
+ virtual TSyError SetValueByID( KeyH aItemKey, sInt32 aID, sInt32 arrIndex,
+ uInt16 aValType,
+ appPointer aBuffer, memSize aValSize, string suff="" );
+
+ // ---- convenience routines to access Get/SetValue for specific types ----
+ /* string */
+ virtual TSyError GetStr ( KeyH aItemKey, string aValName, string &aText,
+ string suff="" );
+ virtual TSyError GetStrByID ( KeyH aItemKey, sInt32 aID, string &aText,
+ sInt32 arrIndex= 0, string suff="" );
+ virtual TSyError SetStr ( KeyH aItemKey, string aValName, string aText,
+ string suff="" );
+ virtual TSyError SetStrByID ( KeyH aItemKey, sInt32 aID, string aText,
+ sInt32 arrIndex= 0, string suff="" );
+
+
+ /* sInt16 / uInt16 */
+ virtual TSyError GetInt16 ( KeyH aItemKey, string aValName, sInt16 &aValue,
+ string suff="" );
+ virtual TSyError GetInt16 ( KeyH aItemKey, string aValName, uInt16 &aValue,
+ string suff="" );
+
+ virtual TSyError GetInt16ByID( KeyH aItemKey, sInt32 aID, sInt16 &aValue,
+ sInt32 arrIndex= 0, string suff="" );
+ virtual TSyError GetInt16ByID( KeyH aItemKey, sInt32 aID, uInt16 &aValue,
+ sInt32 arrIndex= 0, string suff="" );
+
+ virtual TSyError SetInt16 ( KeyH aItemKey, string aValName, sInt16 aValue,
+ string suff="" );
+ virtual TSyError SetInt16ByID( KeyH aItemKey, sInt32 aID, sInt16 aValue,
+ sInt32 arrIndex= 0, string suff="" );
+
+ /* sInt32 / uInt32 */
+ virtual TSyError GetInt32 ( KeyH aItemKey, string aValName, sInt32 &aValue,
+ string suff="" );
+ virtual TSyError GetInt32 ( KeyH aItemKey, string aValName, uInt32 &aValue,
+ string suff="" );
+
+ virtual TSyError GetInt32ByID( KeyH aItemKey, sInt32 aID, sInt32 &aValue,
+ sInt32 arrIndex= 0, string suff="" );
+ virtual TSyError GetInt32ByID( KeyH aItemKey, sInt32 aID, uInt32 &aValue,
+ sInt32 arrIndex= 0, string suff="" );
+
+ virtual TSyError SetInt32 ( KeyH aItemKey, string aValName, sInt32 aValue,
+ string suff="" );
+
+ virtual TSyError SetInt32ByID( KeyH aItemKey, sInt32 aID, sInt32 aValue,
+ sInt32 arrIndex= 0, string suff="" );
+}; // OceanBlue
+
+
+} // namespace
+#endif // OCEANBLUE_H
+/* eof */
diff --git a/src/sysync_SDK/DB_Interfaces/snowwhite/snowwhite.cpp b/src/sysync_SDK/DB_Interfaces/snowwhite/snowwhite.cpp
new file mode 100644
index 0000000..59972c6
--- /dev/null
+++ b/src/sysync_SDK/DB_Interfaces/snowwhite/snowwhite.cpp
@@ -0,0 +1,337 @@
+/*
+ * File: snowwhite.cpp
+ *
+ * Author: Beat Forster (bfo@synthesis.ch)
+ *
+ *
+ * Programming interface between Synthesis SyncML engine
+ * and a database structure.
+ *
+ * Copyright (c) 2008-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ *
+ * E X A M P L E C O D E
+ * ========================
+ * (To be used as starting point for SDK development.
+ * It can be adapted and extended by programmer)
+ *
+ */
+
+#include "myadapter.h"
+
+namespace oceanblue {
+ using sysync::memSize;
+ using sysync::stringSize;
+ using sysync::appChar;
+
+ using sysync::LOCERR_OK;
+ using sysync::DB_Forbidden;
+ using sysync::DB_NotFound;
+ using sysync::Password_MD5_OUT;
+ using sysync::ReadNextItem_Unchanged;
+ using sysync::ReadNextItem_EOF;
+ using sysync::VP_GlobMulti;
+ using sysync::VALTYPE_TIME64;
+
+ using sysync::MinVersion;
+ using sysync::Manufacturer;
+ using sysync::Description;
+ using sysync::YesField;
+ using sysync::IntStr;
+ using sysync::Parans;
+
+
+#define BuildNumber 1 // user defined build number, can be 0..255
+#define MyDB MyAdapter_Name // plugin module's name
+
+#define Example_UsrKey "5678"
+
+#define ItemRdOffs 12000 // examples: first valid item for reading
+#define ItemOffs 15000 // first valid item
+
+
+
+// ---- module -----------------------------------------------------------------------------
+// If your implementation has a higher build number, override it as done here
+CVersion MyAdapter_Module::Version()
+{
+ CVersion v= Plugin_Version( BuildNumber ); // an individual build number
+ DEBUG_DB( fCB, MyDB,Mo_Ve, "%08X", v ); // NOTE: do not change the version number itself
+ return v; // as it used by the engine to
+} // Version // guarantee up/downwards compatibility
+
+
+// If your implementation has special capabilities, override them here
+// Your company's name and the plugin descriptiom should be added here
+TSyError MyAdapter_Module::Capabilities( appCharP &capa )
+{
+ string s = MyPlatform(); // some standard identifiers
+ s+= '\n'; s+= DLL_Info;
+
+ Manufacturer ( s, "Plugin GmbH" ); // **** your company's name ****
+
+ string desc = MyAdapter_Name;
+ desc+= " Plugin";
+ Description ( s, desc ); // **** your plugin's description ****
+
+ MinVersion ( s, VP_GlobMulti ); // at least V1.5.1
+ YesField ( s, CA_ADMIN_Info ); // show "ADMIN" info
+ YesField ( s, CA_ItemAsKey ); // use the <asKey> routines
+ capa= StrAlloc( s.c_str() );
+ return LOCERR_OK;
+} // Capabilities
+
+
+
+// ---- session ----------------------------------------------------------------------------
+// Example password mode
+sInt32 MyAdapter_Session::PasswordMode()
+{
+ int mode= Password_MD5_OUT;
+ return mode;
+} /* Session_PasswordMode */
+
+
+
+// Example login allowed for super:user MD5 login, resulting in <sUsrKey> = "5678"
+TSyError MyAdapter_Session::Login( cAppCharP sUsername,
+ appCharP &sPassword, appCharP &sUsrKey )
+{
+ cAppCharP MD5_OUT_SU= "11JrMX94iTR1ob5KZFtEwQ=="; // the MD5B64 example for "super:user"
+ TSyError err= DB_Forbidden; // default
+
+ if (strcmp( sUsername,"super" )==0) { // an example account ("super"), hard coded here
+ err = LOCERR_OK;
+ sPassword= StrAlloc( MD5_OUT_SU );
+ sUsrKey = StrAlloc( Example_UsrKey ); // <sUsrKey> allows to access the datastore later
+ } // if
+
+ return err;
+} // Login
+
+
+
+// ---- utility routines -------------------------------------------------------------------
+// set some contacts field as example
+void MyAdapter::SetContactFields( KeyH aItemKey, cAppCharP aFirst,
+ cAppCharP aLast,
+ cAppCharP aCity )
+{
+ SetInt32( aItemKey, "SYNCLVL", 10 ); // set some fields
+ SetStr ( aItemKey, "N_FIRST", aFirst );
+ SetStr ( aItemKey, "N_LAST", aLast );
+ SetStr ( aItemKey, "ADR_W_CITY", aCity );
+} // SetContactFields
+
+
+// set some events field as example
+void MyAdapter::SetEventFields ( KeyH aItemKey, cAppCharP aSum,
+ cAppCharP aLoc,
+ cAppCharP aMod,
+ cAppCharP aStart,
+ cAppCharP aEnd, bool allDay )
+{
+ SetInt32( aItemKey, "SYNCLVL", 10 ); // set some fields
+ SetStr ( aItemKey, "KIND", "EVENT" );
+ SetStr ( aItemKey, "SUMMARY", aSum );
+ SetStr ( aItemKey, "LOCATION", aLoc );
+ SetStr ( aItemKey, "DMODIFIED", aMod );
+ SetStr ( aItemKey, "DTSTART", aStart );
+ SetStr ( aItemKey, "DTEND", aEnd );
+ SetInt32( aItemKey, "ALLDAY", allDay );
+
+ if (allDay) {
+ sInt32 id= GetValueID( aItemKey, "DTSTART" );
+ SetStrByID( aItemKey,id, "DATE", 0,VALSUFF_TZNAME ); // timezone name
+ } // if
+
+ if (allDay) {
+ sInt32 id= GetValueID( aItemKey, "DTEND" );
+ SetStrByID( aItemKey,id, "DATE", 0,VALSUFF_TZNAME ); // timezone name
+ } // if
+} // SetEventFields
+
+
+
+// Get values: name, type, array size, timezone name of <aID>
+TSyError MyAdapter::GetFieldKey( KeyH aItemKey, sInt32 aID, string &aKey,
+ uInt16 &aType,
+ sInt32 &nFields,
+ string &aTZ )
+{
+ TSyError err;
+
+ aTZ= "";
+ err= GetStrByID ( aItemKey,aID, aKey, 0,VALSUFF_NAME ); if (err) return err; // field name
+ err= GetInt16ByID( aItemKey,aID, aType, 0,VALSUFF_TYPE ); if (err) return err; // field type
+ err= GetInt32ByID( aItemKey,aID, nFields, 0,VALSUFF_ARRSZ ); if (err) nFields= -1; // array size
+
+ if (aType==VALTYPE_TIME64) {
+ err= GetStrByID( aItemKey,aID, aTZ, 0,VALSUFF_TZNAME ); // timezone name
+ } // if
+
+ return LOCERR_OK;
+} // GetFieldKey
+
+
+// Display available fields of <aItemKey>
+void MyAdapter::DisplayFields( KeyH aItemKey )
+{
+ cAppCharP DF= "Display_Fields";
+ TSyError err;
+ sInt32 id;
+ string vKey, vTZ, vName;
+ uInt16 vType;
+ sInt32 nFields;
+
+ cAppCharP step= VALNAME_FIRST;
+ while (true) {
+ id = GetValueID ( aItemKey, step );
+ err= GetFieldKey( aItemKey, id, vKey,vType,nFields,vTZ ); if (err) break;
+
+ bool isArr= nFields>=0; // check array condition
+ if (isArr) DEBUG_DB( fCB, MyDB,DF, " (%2d) %s: **ARRAY** %d",
+ vType, vKey.c_str(), nFields );
+ else nFields= 1; // not an array -> 1 element
+
+ if (vTZ!="") vTZ= " " + Parans( "TZNAME=" + vTZ );
+
+ // for each array element do ...
+ for (int i= 0; i<nFields; i++) {
+ err= GetStrByID( aItemKey, id, vName, i ); if (err) break;
+ switch (err) {
+ case 0: DEBUG_DB( fCB, MyDB,DF, " (%2d) %s:%s%s",
+ vType, vKey.c_str(),
+ vName.c_str(),
+ vTZ.c_str() ); break;
+ case 204:
+ case 404: break; // do nothing
+
+ default : DEBUG_DB( fCB, MyDB,DF, " err=%d" );
+ } // switch
+ } // for
+
+ step= VALNAME_NEXT; // next element
+ } // loop
+} // DisplayFields
+
+
+
+// ---- datastore --------------------------------------------------------------------------
+TSyError MyAdapter::CreateContext( cAppCharP sDevKey, cAppCharP sUsrKey )
+{
+ string s= sUsrKey;
+ if (s!=Example_UsrKey) return DB_Forbidden; // Check the example <sUsrKey>
+
+ // just return 15000,15001,... as valid item ids
+ // please choose a persistent solution for a real implementation
+ fCreItem= ItemOffs;
+
+ return inherited::CreateContext( sDevKey,sUsrKey ); // and call the base method
+} // CreateContext
+
+
+
+// Example routine: Return 4 valid items 12000 .. 12003
+TSyError MyAdapter::ReadNextItemAsKey( ItemID aID, KeyH /* aItemKey */,
+ sInt32 &aStatus, bool aFirst )
+{
+ // Example: Constant 4 valid contact items or 2 event items for this example
+ // See "ReadItemAsKey" for details of this example
+ uInt32 n= 0;
+ if (fContextName=="contacts") n= 4;
+ if (fContextName=="events" ) n= 2;
+
+ if (aFirst) fNthItem= 0; // re-initialize it
+ if (fNthItem>=n) { aStatus= ReadNextItem_EOF; return LOCERR_OK; }
+
+ // Create items 12000 ..
+ aID->item= StrAlloc( IntStr( ItemRdOffs+fNthItem ).c_str() );
+ fNthItem++; // next
+ aStatus= ReadNextItem_Unchanged;
+ return LOCERR_OK;
+} // ReadNextItemAsKey
+
+
+
+// Example routine: Return 4 valid items 12000..12003 for contacts
+// 2 valid items 12000..12001 for events
+TSyError MyAdapter::ReadItemAsKey( cItemID aID, KeyH aItemKey )
+{
+ TSyError err= LOCERR_OK;
+ string s = aID->item;
+
+ do {
+ if (fContextName=="contacts") {
+ if (s=="12000") { SetContactFields( aItemKey, "Johann Sebastian","Bach", "Leipzig" ); break; }
+ if (s=="12001") { SetContactFields( aItemKey, "Wolfgang Amadeus","Mozart", "Salzburg" ); break; }
+ if (s=="12002") { SetContactFields( aItemKey, "Richard", "Wagner", "Bayreuth" ); break; }
+ if (s=="12003") { SetContactFields( aItemKey, "Giacomo", "Puccini","Lucca" ); break; }
+ } // if
+
+ if (fContextName=="events") { // can have the same IDs as contacts, because they are independent
+ if (s=="12000") { SetEventFields ( aItemKey, "Sechsel\xC3\xA4uten",
+ "Bellevue", "20081110T140000Z",
+ "20090420", "20090421" ); break; }
+ if (s=="12001") { SetEventFields ( aItemKey, "Knabenschiessen",
+ "Albisg\xC3\xBC\x65tli", "20081110T140000",
+ "20090914T041500", "20090915T17" ); break; }
+ } // if
+
+ // not found
+ err= DB_NotFound;
+ } while (false);
+
+ return err;
+} // ReadItemAsKey
+
+
+
+// Example routine: Debug display of the item contents.
+// Return item names 15000 ...
+TSyError MyAdapter::InsertItemAsKey( KeyH aItemKey, ItemID newID )
+{
+ // **** implementation for inserting an item must be added here ****
+ TSyError err;
+ sInt32 id;
+
+ // ---- as an example, first and last name will be read ----
+ string vFirst, vLast;
+ err= GetStr ( aItemKey, "N_FIRST", vFirst ); // get it directly
+
+ id = GetValueID( aItemKey, "N_LAST" ); // or get it via id
+ err= GetStrByID( aItemKey, id, vLast );
+
+ DEBUG_DB( fCB, MyDB,Da_IIK, "%08X %s %s", aItemKey, vFirst.c_str(),vLast.c_str() );
+
+ // ---- this is a simple loop to show the contents of each field of an item ----
+ DisplayFields( aItemKey );
+
+ // just return 15000, 15001, ... as valid item ids
+ newID->item= StrAlloc( IntStr( fCreItem++ ).c_str() );
+ return LOCERR_OK;
+} // InsertItemAsKey
+
+
+
+TSyError MyAdapter::UpdateItemAsKey( KeyH aItemKey, cItemID /* aID */, ItemID /* updID */ )
+{
+ // **** implementation for updating an item must be added here ****
+ DEBUG_DB( fCB, MyDB,Da_UIK, "%08X", aItemKey );
+ DisplayFields( aItemKey );
+
+ return LOCERR_OK;
+} // UpdateItemAsKey
+
+
+
+TSyError MyAdapter::DeleteItem( cItemID /* aID */ )
+{
+ // **** implementation for deleting an item must be added here ****
+ return DB_NotFound;
+} // DeleteItem
+
+
+
+} // namespace
+/* eof */
diff --git a/src/sysync_SDK/DB_Interfaces/snowwhite/snowwhite.h b/src/sysync_SDK/DB_Interfaces/snowwhite/snowwhite.h
new file mode 100644
index 0000000..af7a8fd
--- /dev/null
+++ b/src/sysync_SDK/DB_Interfaces/snowwhite/snowwhite.h
@@ -0,0 +1,90 @@
+/*
+ * File: snowwhite.h
+ *
+ * Author: Beat Forster (bfo@synthesis.ch)
+ *
+ * Datastore plugin classes which is derived
+ * from the "oceanblue" classes.
+ *
+ * Copyright (c) 2008-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ */
+
+#ifndef SNOWWHITE_H
+#define SNOWWHITE_H
+
+#include "myadapter.h"
+#include "sync_include.h"
+
+#include "oceanblue.h"
+namespace oceanblue {
+
+
+
+class MyAdapter_Module : public OceanBlue_Module
+{
+ typedef OceanBlue_Module inherited;
+
+ public:
+ /**** any of the OceanBlue_Module methods can be overriden ****/
+ CVersion Version();
+ TSyError Capabilities( appCharP &capa );
+}; // MyAdapter_Module
+
+
+
+class MyAdapter_Session : public OceanBlue_Session
+{
+ typedef OceanBlue_Session inherited;
+
+ public:
+ /**** any of the OceanBlue_Session methods can be overriden ****/
+ sInt32 PasswordMode();
+ TSyError Login( cAppCharP sUsername, appCharP &sPassword, appCharP &sUsrKey );
+}; // MyAdapter_Session
+
+
+
+class MyAdapter : public OceanBlue
+{
+ typedef OceanBlue inherited;
+
+ private:
+ uInt32 fCreItem; // an internal number for new item creation
+
+ // Fill example items
+ void SetContactFields( KeyH aItemKey, cAppCharP aFirst,
+ cAppCharP aLast,
+ cAppCharP aCity );
+ void SetEventFields ( KeyH aItemKey, cAppCharP aSum,
+ cAppCharP aLoc,
+ cAppCharP aMod,
+ cAppCharP aStart,
+ cAppCharP aEnd, bool allday= true );
+ // Get field information
+ TSyError GetFieldKey ( KeyH aItemKey, sInt32 aID, string &aKey,
+ uInt16 &aType,
+ sInt32 &nFields,
+ string &aTZ );
+ // Display fields of the work item
+ void DisplayFields ( KeyH aItemKey );
+
+ public:
+ /**** any of the OceanBlue methods can be overriden ****/
+ TSyError CreateContext ( cAppCharP sDevKey, cAppCharP sUsrKey );
+
+ // for this example, the <asKey> methods will be used
+ TSyError ReadNextItemAsKey ( ItemID aID, KeyH aItemKey,
+ sInt32 &aStatus, bool aFirst );
+ TSyError ReadItemAsKey ( cItemID aID, KeyH aItemKey );
+
+ TSyError InsertItemAsKey ( KeyH aItemKey, ItemID newID );
+ TSyError UpdateItemAsKey ( KeyH aItemKey, cItemID aID, ItemID updID );
+
+ TSyError DeleteItem ( cItemID aID );
+}; // MyAdapter
+
+
+} // namespace
+#endif // SNOWWHITE_H
+/* eof */
diff --git a/src/sysync_SDK/DB_Interfaces/text_db/DLL/target_options.h b/src/sysync_SDK/DB_Interfaces/text_db/DLL/target_options.h
new file mode 100644
index 0000000..5693edd
--- /dev/null
+++ b/src/sysync_SDK/DB_Interfaces/text_db/DLL/target_options.h
@@ -0,0 +1,46 @@
+/*
+ * File: target_options.h
+ *
+ * Author: Beat Forster (bfo@synthesis.ch)
+ *
+ * Programming interface between Synthesis SyncML engine
+ * and a database structure.
+ *
+ * Copyright (c) 2004-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ */
+
+#ifndef TARGET_OPTIONS_H
+#define TARGET_OPTIONS_H
+
+
+/* - find out target platform */
+#ifdef __MACH__
+ #define MACOSX
+#else
+ #if defined __MWERKS__ || defined _MSC_VER
+ #ifndef _WIN32
+ #define _WIN32
+ #endif
+ #else
+ #define LINUX
+ #endif
+#endif
+
+
+/* code is running within DLL */
+#define SDK_DLL 1
+
+/* - we are not at the SyncML engine's side here */
+/* - and we need an extern "C" interface for the DLL */
+#undef SYSYNC_ENGINE
+
+/* activate debug output */
+#define SYDEBUG 2
+
+//#define DISABLE_PLUGIN_DATASTOREADMIN 1
+//#define DISABLE_PLUGIN_BLOBS 1
+
+
+#endif /* TARGET_OPTIONS_H */
+/* eof */
diff --git a/src/sysync_SDK/DB_Interfaces/text_db/sync_dbapi_text.cpp b/src/sysync_SDK/DB_Interfaces/text_db/sync_dbapi_text.cpp
new file mode 100644
index 0000000..3f5c66f
--- /dev/null
+++ b/src/sysync_SDK/DB_Interfaces/text_db/sync_dbapi_text.cpp
@@ -0,0 +1,1347 @@
+/*
+ * File: sync_dbapi_text.cpp
+ *
+ * Author: Beat Forster (bfo@synthesis.ch)
+ *
+ *
+ * Example DBApi database adapter plugin
+ * Written in C++
+ *
+ * Similar behaviour as the former "text_db".
+ * (with extensions for ARRAY and BLOB access)
+ *
+ * For newer engine versions, this sample code
+ * is 1:1 used as datastore connector for the
+ * so called demo server/client.
+ *
+ * Copyright (c) 2005-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ *
+ * E X A M P L E C O D E
+ * (text_db interface)
+ *
+ */
+
+#include "sync_include.h" // include general SDK definitions
+#include "sync_dbapidef.h" // include the interface file and utilities
+#include "SDK_util.h" // include SDK utilities
+#include "SDK_support.h" // and some C++ support functionality
+
+#ifndef SYSYNC_ENGINE // outside the SyncML engine we need additional stuff:
+ #include "stringutil.h" // local implementation for CStr <=> Str conversions
+ #include <ctype.h> // isalnum
+#endif
+
+#include "admindata.h" // TAdminData class
+#include "blobs.h" // TBlob class
+#include "dbitem.h" // TDBItem class
+
+namespace SDK_textdb { // the plugin runs within a namespace
+ using sysync::DB_Callback; // common definitions for both namespaces
+ using sysync::ItemID_Struct;
+ using sysync::ItemID;
+ using sysync::cItemID;
+ using sysync::MapID;
+ using sysync::KeyH;
+ using sysync::TDBItem;
+ using sysync::TDBItemField;
+ using sysync::TAdminData;
+ using sysync::TBlob;
+
+ using sysync::LOCERR_OK;
+ using sysync::LOCERR_TOOOLD;
+ using sysync::LOCERR_NOTIMP;
+ using sysync::DB_Forbidden;
+ using sysync::DB_NotFound;
+ using sysync::DB_Full;
+ using sysync::DB_Error;
+ using sysync::Password_ClrText_IN;
+ using sysync::Password_ClrText_OUT;
+ using sysync::Password_MD5_Nonce_IN;
+ using sysync::Password_MD5_OUT;
+ using sysync::ReadNextItem_Unchanged;
+ using sysync::ReadNextItem_Changed;
+ using sysync::ReadNextItem_Resumed;
+ using sysync::ReadNextItem_EOF;
+ using sysync::VP_GlobMulti;
+
+ using sysync::Manufacturer;
+ using sysync::Description;
+ using sysync::GContext;
+ using sysync::MinVersion;
+ using sysync::NoField;
+ using sysync::YesField;
+ using sysync::IsAdmin;
+
+ using sysync::GlobContext;
+ using sysync::GlobContextFound;
+ using sysync::Apo;
+ using sysync::IntStr;
+ using sysync::VersionStr;
+ using sysync::ConcatNames;
+ using sysync::ConcatPaths;
+ using sysync::CurrentTime;
+ using sysync::CompareTokens;
+ using sysync::CStrToStrAppend;
+
+#include "sync_dbapi.h" // include the interface file within the namespace
+
+#define BuildNumber 0 // User defined build number, initial value is 0
+#define MyDB "TextDB" // textdb example debug name
+
+#if defined MACOSX && defined __MWERKS__ // export the items for Mac OS X as well
+#pragma export on // (for Windows, a ".def" file is required)
+#endif
+
+
+/* ------------------------------------------------------------------------------ */
+/* File name prefixes for the textdb implementation */
+#define P_Device "DEV_" // device info
+#define P_Data "TDB_" // textdb data
+#define P_NewItem "NID_" // new item id (for a persistent approach)
+
+
+
+/* -- MODULE -------------------------------------------------------------------- */
+/* All the plugin's common variables are defined here
+ * Normally there is just one such context per module.
+ * These variables could be defined as global vars as well.
+ *
+ * The example here shows, how to handle a common AND a
+ * module context in parallel. A module context will be
+ * opened once for the Session environment and twice for
+ * each Datastore (for the data and the admin part).
+ * The idea is to have the session's plugin params as
+ * default values for the datastore plugin params. These
+ * default values (for gDataPath/gBlobPath and gMapPath)
+ * will be stored at the common context.
+ */
+class CommonContext {
+ public:
+ CommonContext() { cInstalled = false;
+ cEngineVersion= 0; // engine's version not yet available here
+ cGlob = NULL; }
+
+ bool cInstalled; // The module is already installed
+ long cEngineVersion; // the engine's version
+ GlobContext* cGlob; // structure for reaching the common context
+
+ // --- local copies of ...
+ // NOTE: These are default values if datatore specific
+ // paths are not defined
+ string cDataPath; // directory where to store data
+ string cBlobPath; // " " " " BLOBs
+ string cMapPath; // " " " " maps and admin data
+
+ /* other elements can be defined here */
+ /* ... */
+}; // CommonContext
+
+
+class ModuleContext {
+ public:
+ ModuleContext() { fCB= NULL;
+ fCC= NULL; }
+
+ // --- local copies of ...
+ DB_Callback fCB; // the callback structure
+ CommonContext* fCC; // reference to the common context
+
+ string fModuleName; // the module's name
+ string fSubName; // the sub path name (not used for textdb implementation)
+ string fContextName; // the context name
+
+ string fDataPath; // directory where to store data
+ string fBlobPath; // " " " " BLOBs
+ string fMapPath; // " " " " maps and admin data
+
+ /* other elements can be defined here */
+ /* ... */
+}; // ModuleContext
+
+/* <mContext> will be casted to the ModuleContext* structure */
+static ModuleContext* MoC( CContext mContext ) { return (ModuleContext*)mContext; }
+
+
+
+TSyError Module_CreateContext( CContext *mContext, cAppCharP moduleName,
+ cAppCharP subName,
+ cAppCharP mContextName, DB_Callback mCB )
+{
+ DEBUG_DB( mCB, MyDB,Mo_CC, "'%s' (%s)", ConcatNames( moduleName,subName ).c_str(),
+ mContextName );
+ if (!CB_gContext( mCB )) return LOCERR_TOOOLD; // the new <gContext> system is required !
+
+ ModuleContext* mc= new ModuleContext; // get the (unique) module context
+ *mContext= (CContext)mc; // return the context variable
+
+ mc->fModuleName = moduleName; // local copy of module's name
+ mc->fSubName = subName; // " " " module's sub name
+ mc->fContextName= mContextName; // " " " the contexts name (e.g. "contacts")
+ mc->fCB = mCB; // " " " the callback reference
+
+ string db= MyDB;
+ #ifdef _MSC_VER
+ db+= "_visual";
+ #endif
+ #if defined __MACH__ && !defined __MWERKS__
+ db+= "_universal";
+ #endif
+
+ GlobContext* g= (GlobContext*)mCB->gContext;
+ if (GlobContextFound( db, g ))
+ mc->fCC= (CommonContext*)g->ref; // connect the structure
+ else { mc->fCC= new CommonContext;
+ mc->fCC->cGlob= g;
+ g->ref= mc->fCC; // connect the structure
+ } // if
+ g->cnt++;
+
+ DEBUG_DB( mCB, MyDB,Mo_CC, "mContext=%08X gContext=%08X g->ref=%08X",
+ *mContext, mCB->gContext, g->ref );
+ return LOCERR_OK;
+} // Module_CreateContext
+
+
+
+/* -- VERSION/CAPABILITIES ---- */
+/* Get the plug-in's version number */
+CVersion Module_Version( CContext mContext )
+{
+ /* The current plugin's SDK version is expected here */
+ CVersion v= Plugin_Version( BuildNumber );
+
+ if ( mContext )
+ DEBUG_DB( MoC( mContext )->fCB, MyDB,Mo_Ve, "%s", VersionStr( v ).c_str() );
+ return v;
+} /* Module_Version */
+
+
+
+/* Get the plug-in's capabilities */
+TSyError Module_Capabilities( CContext mContext, appCharP *mCapabilities )
+{
+ if (!mContext) return DB_Forbidden;
+ ModuleContext* mc= MoC( mContext );
+
+ string s = MyPlatform();
+ s+= '\n'; s+= DLL_Info;
+ Manufacturer( s, "Synthesis AG" ); // **** can be adapted ***
+ Description ( s, "Text database module. Writes data directly to TDB_*.txt file" );
+ YesField ( s, CA_ADMIN_Info );
+ MinVersion ( s, VP_GlobMulti ); // at least V1.5.1
+
+ if (mc->fCC) {
+ GContext( s, mc->fCC->cGlob );
+ } // if
+
+ // Parts of the plug-in can be disabled (will not be connected and therefore not be used)
+ // These settings are expected ath the "target_options.h" of the plugin
+ #ifdef DISABLE_PLUGIN_SESSIONAUTH
+ NoField( s, Plugin_SE_Auth );
+ #endif
+ #ifdef DISABLE_PLUGIN_DEVICEADMIN
+ NoField( s, Plugin_DV_Admin );
+ #endif
+ #ifdef DISABLE_PLUGIN_DATASTOREADMIN
+ NoField( s, Plugin_DS_Admin );
+ #endif
+ #ifdef DISABLE_PLUGIN_DATASTOREDATA
+ NoField( s, Plugin_DS_Data );
+ #endif
+
+ *mCapabilities= StrAlloc( s.c_str() );
+ DEBUG_DB( mc->fCB, MyDB,Mo_Ca, "'%s'", *mCapabilities );
+ return LOCERR_OK;
+} // Module_Capabilities
+
+
+
+TSyError Module_PluginParams( CContext mContext, cAppCharP mConfigParams,
+ CVersion engineVersion )
+{
+ /* Decide, which data path to take */
+ cAppCharP DataFilePath= "datafilepath"; // TDB_
+ cAppCharP BlobFilePath= "blobfilepath"; // BLB_
+ cAppCharP MapFilePath= "mapfilepath"; // DEV_ / ADM_ / MAP_
+
+ // ---- legacy, please use <datafilepath> and <mapfilepath> for newer implementations
+ #if defined LINUX || defined MACOSX
+ cAppCharP DataPath= "unixpath";
+ #else
+ cAppCharP DataPath= "winpath";
+ #endif
+ // ---- END legacy ----
+
+ ModuleContext* mc= MoC( mContext );
+ DEBUG_DB ( mc->fCB, MyDB,Mo_PP, "EngineVersion: %s",
+ VersionStr( engineVersion ).c_str() );
+ DEBUG_DB ( mc->fCB, MyDB,Mo_PP, "'%s'", mConfigParams );
+
+ // ------------------------------------------------------
+ // Get the data path for the textDB files
+ char* vv;
+ mc->fDataPath= mc->fCC->cDataPath; // as default
+
+ // ---- legacy data path
+ if (Field( mConfigParams, DataPath, &vv )) {
+ mc->fDataPath= "";
+ CStrToStrAppend( vv, mc->fDataPath );
+ StrDispose ( vv );
+ } // if
+ // ---- END legacy ----
+
+ // *** DATA PATH ***
+ // overwrite it, if using new definition
+ if (Field( mConfigParams, DataFilePath, &vv )) {
+ mc->fDataPath= "";
+ CStrToStrAppend( vv, mc->fDataPath );
+ StrDispose ( vv );
+ } // if
+
+ // *** BLOB PATH ***
+ // the same for the BLOB files
+ // take this as the default
+ mc->fBlobPath= mc->fCC->cBlobPath; // take the global one
+ if (mc->fBlobPath.empty()) mc->fBlobPath= mc->fDataPath; // or take the local data path
+ if (mc->fBlobPath.empty()) mc->fBlobPath= mc->fCC->cDataPath; // or even more globally
+
+ if (Field( mConfigParams, BlobFilePath, &vv )) {
+ mc->fBlobPath= "";
+ CStrToStrAppend( vv, mc->fBlobPath );
+ StrDispose ( vv );
+ } // if
+
+ // *** MAP PATH ***
+ // and the same for the map/admin files
+ // take this as the default
+ mc->fMapPath= mc->fCC->cMapPath; // take the global one
+ if (mc->fMapPath.empty()) mc->fMapPath= mc->fDataPath; // or take the local data path
+ if (mc->fMapPath.empty()) mc->fMapPath= mc->fCC->cDataPath; // or even more globally
+
+ if (Field( mConfigParams, MapFilePath, &vv )) {
+ mc->fMapPath= "";
+ CStrToStrAppend( vv, mc->fMapPath );
+ StrDispose ( vv );
+ } // if
+
+ // get global defaults when called for session context
+ // Indication: <mc->fContextName> is empty.
+ if (!mc->fCC->cInstalled && mc->fContextName.empty()) {
+ mc->fCC->cDataPath = mc->fDataPath;
+ mc->fCC->cBlobPath = mc->fBlobPath;
+ mc->fCC->cMapPath = mc->fMapPath;
+ } // if
+
+ if (mc->fCC->cEngineVersion==0) // set it once
+ mc->fCC->cEngineVersion= engineVersion;
+
+ mc->fCC->cInstalled= true; // now it's really installed
+ return LOCERR_OK;
+} // Module_PluginParams
+
+
+
+/* Dispose the memory of the module context */
+void Module_DisposeObj( CContext mContext, void* memory )
+{
+ ModuleContext* mc= MoC( mContext );
+ DEBUG_Exotic_DB( mc->fCB, MyDB,Mo_DO, "%d free at %08X '%s'", mc, memory,memory );
+ StrDispose ( memory );
+} // Module_DisposeObj
+
+
+
+TSyError Module_DeleteContext( CContext mContext )
+{
+ ModuleContext* mc= MoC( mContext );
+//printf( "DEL: '%s' %08X\n", mc->fModuleName.c_str(), mc->fCB->gContext );
+ DEBUG_DB ( mc->fCB, MyDB,Mo_DC, "'%s'", mc->fModuleName.c_str() );
+
+ GlobContext* g= mc->fCC->cGlob;
+ g->cnt--;
+ if (g->cnt==0) {
+ CommonContext* cc= (CommonContext*)g->ref;
+ delete cc; g->ref= NULL;
+ } // if
+
+ delete mc;
+ return LOCERR_OK;
+} // Module_DeleteContext
+
+
+
+// ---------------------- session handling ---------------------
+#if !defined DISABLE_PLUGIN_DEVICEADMIN || !defined DISABLE_PLUGIN_DEVICEADMIN
+
+/*! Each session requires a session context, which will be created with
+ * 'Session_CreateContext' and deleted with 'Session_DeleteContext'.
+ */
+class SessionContext {
+ public:
+ DB_Callback fCB;
+
+ TDBItem fDevList; // The current device list
+ TDBItem* fDev; // reference
+ string fDeviceID; // stored <aDeviceID> value of 'CheckDevice'
+
+ /* other elements can be defined here */
+ /* ... */
+}; // SessionContext
+
+/* <sContext> will be casted to the SessionContext* structure */
+static SessionContext* SeC( CContext sContext ) { return (SessionContext*)sContext; }
+
+
+
+/* Create a context for a new session */
+TSyError Session_CreateContext( CContext *sContext, cAppCharP sessionName, DB_Callback sCB )
+{
+ SessionContext* sc= new SessionContext;
+ if (sc==NULL) return DB_Full;
+
+ sc->fDev= NULL;
+ sc->fCB = sCB;
+ DEBUG_DB ( sc->fCB, MyDB,Se_CC, "%d '%s'", sc,sessionName );
+ *sContext= (CContext)sc; // return the context variable
+ return LOCERR_OK;
+} // Session_CreateContext
+
+
+/* ----- "script-like" ADAPT --------- */
+TSyError Session_AdaptItem( CContext sContext, appCharP *sItemData1,
+ appCharP *sItemData2,
+ appCharP *sLocalVars, uInt32 sIdentifier )
+{
+ /**** CAN BE ADAPTED BY USER ****/
+ SessionContext* sc= SeC( sContext );
+ DEBUG_DB ( sc->fCB, MyDB,"AdaptItem", "'%s' '%s' '%s' id=%d",
+ *sItemData1,*sItemData2,*sLocalVars, sIdentifier );
+ return LOCERR_OK;
+} /* Session_AdaptItem */
+
+
+/*! Create a alphanumerical string out of <aName> (ignore all other chars)
+ * This is an internal utility proc.
+ */
+static string AlphaNum( cAppCharP aName )
+{
+ cAppCharP p= aName;
+ string s;
+
+ while (*p!='\0') {
+ if (isalnum( *p )) s+= tolower( *p );
+ p++;
+ } // while
+
+ return s;
+} // AlphaNum
+
+
+#ifndef DISABLE_PLUGIN_DEVICEADMIN
+/* Check the database entry of <deviceID> and return its nonce string */
+TSyError Session_CheckDevice( CContext sContext, cAppCharP aDeviceID, appCharP *sDevKey,
+ appCharP *nonce )
+{
+ SessionContext* sc= SeC ( sContext );
+ ModuleContext* mc= MoC( sc->fCB->cContext );
+
+ TSyError err= LOCERR_OK;
+ sc->fDeviceID= AlphaNum( aDeviceID );
+ string s= P_Device + sc->fDeviceID + ".txt";
+
+ sc->fDevList.fFileName= ConcatPaths( mc->fMapPath, s );
+ err= sc->fDevList.LoadDB( true, "DEV", sc->fCB ); // load the DEV file info
+
+ if (!sc->fDev) {
+ /* get the device */ sc->fDev= &sc->fDevList;
+ bool found= ListNext( sc->fDev );
+ if (!found) { /* not found -> create such an element */
+ err= sc->fDevList.CreateEmptyItem( s, sc->fDev ); if (err) return err;
+ } // if
+ } // if
+
+ *sDevKey= StrAlloc( sc->fDeviceID.c_str() ); // 1:1 assigned at the moment
+ *nonce = StrAlloc( sc->fDev->fToken.c_str() );
+ DEBUG_DB ( sc->fCB, MyDB,Se_CD,
+ "devKey='%s' nonce='%s' err=%d", *sDevKey, *nonce, err );
+ return LOCERR_OK;
+} /* Session_CheckDevice */
+
+
+
+/* Get a new nonce from the database.
+ * If this function returns an error (as done for this implementiation here),
+ * the SyncML engine has to create its own nonce.
+ */
+TSyError Session_GetNonce( CContext sContext, appCharP * /* nonce */ )
+{
+ SessionContext* sc= SeC( sContext );
+ DEBUG_DB ( sc->fCB, MyDB,Se_GN, "%d (not supported)", sc );
+ return DB_NotFound;
+} /* Session_GetNonce */
+
+
+
+/* Save the new nonce (which will be expected to be returned
+ * in the next session for this device
+ */
+TSyError Session_SaveNonce( CContext sContext, cAppCharP nonce )
+{
+ TSyError err= DB_NotFound;
+ SessionContext* sc= SeC( sContext );
+ DEBUG_DB ( sc->fCB, MyDB, Se_SN, "%d '%s'", sc,nonce );
+
+ if (sc->fDev) { sc->fDev->fToken= nonce;
+ err= sc->fDevList.SaveDB( true, sc->fCB ); // save it
+ } // if
+
+ return err;
+} /* Session_SaveNonce */
+
+
+
+/* Save the device info for <sContext> */
+TSyError Session_SaveDeviceInfo( CContext sContext, cAppCharP aDeviceInfo )
+{
+ SessionContext* sc= SeC ( sContext );
+ ModuleContext* mc= MoC( sc->fCB->cContext );
+ TSyError err= DB_NotFound;
+
+ if (!sc->fDev) {
+ string s= P_Device + sc->fDeviceID + ".txt";
+ sc->fDevList.fFileName= ConcatPaths( mc->fMapPath, s );
+
+ /* get the device */ sc->fDev= &sc->fDevList;
+ bool found= ListNext( sc->fDev );
+ if (!found) { /* not found -> create such an element */
+ err= sc->fDevList.CreateEmptyItem( s, sc->fDev ); if (err) return err;
+ } // if
+ } // if
+
+ if (sc->fDev) {
+ err= sc->fDevList.UpdateFields( sc->fCB, aDeviceInfo, sc->fDev, true );
+ if (!err) err= sc->fDevList.SaveDB( true, sc->fCB );
+ } // if
+
+ DEBUG_DB( sc->fCB, MyDB,Se_SD, "%d err=%d", sc, err );
+ sc->fDevList.Disp_Items( sc->fCB, Se_SD );
+
+ return err;
+} /* Session_SaveDeviceInfo */
+
+
+TSyError Session_GetDBTime( CContext /* sContext */, appCharP *currentDBTime )
+{
+ string iso8601_str= CurrentTime();
+ *currentDBTime= StrAlloc( iso8601_str.c_str() );
+ return LOCERR_OK;
+} /* Session_GetDBTime */
+#endif // DISABLE_PLUGIN_DEVICEADMIN
+
+
+
+#ifndef DISABLE_PLUGIN_SESSIONAUTH
+/* There are currently 4 different password modes supported:
+ * Return: Password_ClrText_IN 'SessionLogin' will get clear text password
+ * Password_ClrText_OUT " must return clear text password
+ * Password_MD5_OUT " must return MD5 coded password
+ * Password_MD5_Nonce_IN " will get MD5B64(MD5B64(user:pwd):nonce)
+ */
+sInt32 Session_PasswordMode( CContext sContext )
+{
+//int mode= Password_ClrText_IN;
+//int mode= Password_ClrText_OUT;
+//int mode= Password_MD5_Nonce_IN;
+ int mode= Password_MD5_OUT;
+
+ SessionContext* sc= SeC( sContext );
+ DEBUG_DB ( sc->fCB, MyDB,Se_PM, "%d mode=%d", sc, mode );
+ return mode;
+} /* Session_PasswordMode */
+
+
+
+/* Make login */
+/* This example here shows how the different modes are working */
+/* In practice, it's sufficient to support one of theses modes */
+/* The chosen mode must be returned with "Session_PasswordMode" */
+TSyError Session_Login( CContext sContext, cAppCharP sUsername, appCharP *sPassword,
+ appCharP *sUsrKey )
+{
+ cAppCharP MD5_OUT_SU = "11JrMX94iTR1ob5KZFtEwQ=="; // the MD5B64 example for "super:user"
+ cAppCharP MD5_OUT_TT = "xPlhtDgO4k2YL+127za+nw=="; // the MD5B64 example for "test:test"
+
+ SessionContext* sc= SeC( sContext );
+ TSyError err= DB_Forbidden; // default
+ sInt32 mode= Session_PasswordMode( sContext );
+
+ if (strcmp( sUsername,"super" )==0) { // an example account ("super"), hard coded here
+ switch (mode) {
+ case Password_ClrText_IN : if (strcmp( *sPassword,"user" )==0) err= LOCERR_OK; break;
+ case Password_ClrText_OUT : *sPassword= StrAlloc( "user" ); err= LOCERR_OK; break;
+ case Password_MD5_OUT : *sPassword= StrAlloc( MD5_OUT_SU ); err= LOCERR_OK; break;
+ case Password_MD5_Nonce_IN: break; // currently not supported for SDK_textdb
+ } // switch
+
+ if (!err) *sUsrKey= StrAlloc( "5678" ); // <sUsrKey> allows to access the datastore later
+ } // if
+
+ if (strcmp( sUsername,"test" )==0) { // an example account ("test"), hard coded here
+ switch (mode) {
+ case Password_ClrText_IN : if (strcmp( *sPassword,"test" )==0) err= LOCERR_OK; break;
+ case Password_ClrText_OUT : *sPassword= StrAlloc( "test" ); err= LOCERR_OK; break;
+ case Password_MD5_OUT : *sPassword= StrAlloc( MD5_OUT_TT ); err= LOCERR_OK; break;
+ case Password_MD5_Nonce_IN: break; // currently not supported for SDK_textdb
+ } // switch
+
+ if (!err) *sUsrKey= StrAlloc( "test" );
+ } // if
+
+ appCharP pw= *sPassword;
+ if (err && (mode==Password_ClrText_OUT ||
+ mode==Password_MD5_OUT )) pw= "";
+
+ appCharP uk= *sUsrKey;
+ if (err) uk= "";
+
+ DEBUG_DB( sc->fCB, MyDB,Se_LI,"%d usr='%s' pwd='%s' => key='%s' err=%d",
+ sc, sUsername, pw,uk, err );
+ return err;
+} // Session_Login
+
+
+
+/* Make logout */
+TSyError Session_Logout( CContext sContext )
+{
+ SessionContext* sc= SeC( sContext );
+ DEBUG_DB ( sc->fCB, MyDB,Se_LO, "%d", sc );
+ return LOCERR_OK; /* do nothing */
+} // Session_Logout
+#endif // DISABLE_PLUGIN_SESSIONAUTH
+
+
+
+void Session_DisposeObj( CContext sContext, void* memory )
+{
+ SessionContext* sc= SeC( sContext );
+ DEBUG_Exotic_DB( sc->fCB, MyDB,Se_DO, "%d free at %08X '%s'", sc, memory,memory );
+ StrDispose ( memory );
+} /* Session_DisposeObj */
+
+
+
+void Session_ThreadMayChangeNow( CContext sContext )
+{
+ SessionContext* sc= SeC( sContext );
+ DEBUG_Exotic_DB( sc->fCB, MyDB,Se_TC, "%d", sc );
+} /* Session_ThreadMayChangeNow */
+
+
+
+/* For Debugging only !
+ * Will not be called by the SyncML engine
+ * Can be implemented empty, if not needed
+ */
+void Session_DispItems( CContext sContext, bool allFields, cAppCharP specificItem )
+{
+ SessionContext* sc= SeC( sContext );
+ DEBUG_DB ( sc->fCB, MyDB,Se_DI, "%d", sc );
+ sc->fDevList.Disp_Items( sc->fCB, "", allFields, specificItem );
+} /* Session_DispItems */
+
+
+
+/* Delete the session context; there will be no subsequent calls
+ * to <sContext>. All 'Session_DisposeObj' calls will be done before
+ */
+TSyError Session_DeleteContext( CContext sContext )
+{
+ SessionContext* sc= SeC( sContext );
+ DEBUG_DB ( sc->fCB, MyDB,Se_DC, "%d", sc );
+ delete sc;
+ return LOCERR_OK;
+} // Session_DeleteContext
+
+#endif
+// ---- end session ------------------------------------------
+
+
+
+// ---------- context class ----------------------------------
+/*! Each datastore access requires a context, which will be created with
+ * 'CreateContext' and deleted with 'DeleteContext'.
+ */
+typedef struct {
+ bool allfields; // false (default): 'ReadNextItem' will return ID only /
+ // true: returns ID + data
+
+ /* other elements can be defined here */
+ /* ... */
+} SupportType;
+
+
+class TDBContext {
+ public:
+ TDBContext( cAppCharP aContextName, DB_Callback aCB, cAppCharP sDevKey, cAppCharP sUsrKey );
+ ~TDBContext();
+
+ DB_Callback fCB; // callback structure for logging
+
+ string fContextName; // local copy of the context name
+ string fUserName; // unique key of usr-local
+ string fCombiName; // unique key of dev-usr-local
+ TDBItem fLogList; // log info
+
+ TDBItem* fCurrent; // for 'ReadNextItem'
+ int fNth; // and according counter
+
+ TDBItem fNewItem;
+ string fNewID;
+ TDBItem fItemList;
+
+ #ifndef DISABLE_PLUGIN_DATASTOREADMIN
+ TAdminData fAdmin; // admin data handling
+ #endif
+
+ #ifndef DISABLE_PLUGIN_BLOBS
+ TBlob fBlob; // current blob info
+ #endif
+
+ TDBItem fSupportList;
+ SupportType fSupport; // fast readable form
+
+ TDBItem fFilterList;
+
+ string fLastToken; // the <newToken> of the last session
+ string fResumeToken; // suspend/resume support
+ string fNewToken; // the <newToken> of this session
+
+ void ResetCounter();
+ TSyError RemoveAll ( TDBItem* hdK );
+ TSyError UpdateSupport( TDBItem* hdK, int &n );
+ void KeyAndField ( cAppCharP sKey, cAppCharP sField, string &s );
+ void KeyAndField ( TDBItemField* actKey, TDBItemField* actField, string &s );
+
+ /* other elements can be defined here */
+ /* ... */
+}; // DBContext
+typedef TDBContext* ContextP;
+
+
+/* <aContext> will be casted to the ContextP structure */
+static ContextP DBC( CContext aContext ) { return (ContextP)aContext; }
+
+
+/* the constructor */
+TDBContext::TDBContext( cAppCharP aContextName, DB_Callback aCB, cAppCharP sDevKey,
+ cAppCharP sUsrKey )
+{
+ fContextName= aContextName; // make a local copy
+ bool asAdmin= IsAdmin( fContextName );
+
+ fCB = aCB;
+ DEBUG_DB( fCB, MyDB,Da_CC, "'%s' dev='%s' usr='%s' type=%s",
+ fContextName.c_str(), sDevKey,sUsrKey,
+ asAdmin ? "ADMIN" : "DATA" );
+
+ fUserName = ConcatNames( sUsrKey, fContextName, "_" );
+ fCombiName= ConcatNames( sDevKey, fUserName, "_" );
+
+ ModuleContext* mc= MoC( aCB->cContext );
+ #ifndef DISABLE_PLUGIN_DATASTOREADMIN
+ if (asAdmin)
+ fAdmin.Init( aCB, MyDB, mc->fMapPath, fContextName, sDevKey,sUsrKey );
+ #endif
+
+ #ifndef DISABLE_PLUGIN_BLOBS
+ fBlob.Init ( aCB, MyDB, mc->fBlobPath, fContextName, sDevKey,sUsrKey );
+ #endif
+
+ ResetCounter();
+ fNewItem.init ( "newItem", aCB );
+ fItemList.init ( "itemID","parentID", aCB );
+ fSupportList.init( "support", aCB, new TDBItem );
+ fSupport.allfields= false;
+ fFilterList.init ( "filter", aCB, new TDBItem );
+ fLogList.init ( "log", aCB, new TDBItem );
+} // constructor
+
+
+TDBContext::~TDBContext() {
+ DEBUG_DB( fCB, MyDB,Da_DC, "" );
+} // destructor
+
+
+
+/*! Reset the counter for 'ReadNextItem' */
+void TDBContext::ResetCounter() {
+ fCurrent= &fItemList;
+ fNth= 0;
+} // ResetCounter
+
+
+TSyError TDBContext::UpdateSupport( TDBItem* hdK, int &n )
+// Update the specific field <fKey> with the new value <fVal>.
+// Create the whole bunch of missing keys, if not yet available.
+{
+ TDBItem* hdI = hdK; ListNext( hdI );
+ TDBItemField* actK= &hdK->item; // the key
+ TDBItemField* actI= &hdI->item;
+
+//printf( " v allfields=%s\n",
+// fSupport.allfields?"true":"false" );
+
+ n= 0; // init nr of valid contexts
+ int i= 0; // init nr of total contexts
+ while (ListNext( actK,actI ) && i==n) { // still all ?
+ if (strcmp( actK->field.c_str(), Da_RN )==0) {
+ fSupport.allfields= strcmp( actI->field.c_str(),"allfields" )==0;
+ n++;
+ } // if
+
+ i++;
+
+ //printf( " i=%d n=%d '%s' '%s'\n",
+ // i, n, actK->field.c_str(), actI->field.c_str() );
+ } // while
+
+//printf( " n allfields=%s\n",
+// fSupport.allfields?"true":"false" );
+ return LOCERR_OK;
+} // UpdateSupport
+
+
+TSyError TDBContext::RemoveAll( TDBItem* hdK )
+// Remove all elements of the list
+{
+ TDBItem* hdI = hdK; ListNext( hdI );
+ TDBItemField* prvK= &hdK->item; // the key
+ TDBItemField* prvI= &hdI->item;
+
+ while (true) {
+ TDBItemField* actK= prvK; // the key
+ TDBItemField* actI= prvI;
+
+ if (!ListNext( actK,actI )) break;
+
+ prvK->next= actK->next;
+ actK->next= NULL; // avoid destroying the whole chain
+ delete actK;
+
+ prvI->next= actI->next;
+ actI->next= NULL; // avoid destroying the whole chain
+ delete actI;
+ } // while
+
+ return LOCERR_OK;
+} // RemoveAll
+
+
+
+
+/* -- OPEN ----------------------------------------------------------------------- */
+TSyError CreateContext( CContext *aContext, cAppCharP aContextName, DB_Callback aCB,
+ cAppCharP sDevKey,
+ cAppCharP sUsrKey )
+{
+//long v= MoC( aCB->cContext )->fCC->cEngineVersion;
+
+ ContextP ac= new TDBContext( aContextName, aCB, sDevKey,sUsrKey );
+ if (ac==NULL) return DB_Full; // this is really fatal
+ *aContext= (CContext)ac;
+ return LOCERR_OK;
+} /* CreateContext */
+
+
+
+/* -- GENERAL -------------------------------------------------------------------- */
+#if !defined DISABLE_PLUGIN_DATASTOREADMIN || !defined DISABLE_PLUGIN_DATASTOREDATA
+uInt32 ContextSupport( CContext aContext, cAppCharP aSupportRules )
+{
+ ContextP ac= DBC( aContext );
+ DEBUG_DB( ac->fCB, MyDB,Da_CS, "'%s'", aSupportRules );
+ ac->RemoveAll( &ac->fSupportList );
+ ac->fSupportList.UpdateFields( ac->fCB, aSupportRules );
+
+ int n;
+ ac->UpdateSupport( &ac->fSupportList, n );
+ DEBUG_DB( ac->fCB, MyDB,Da_CS, "allfields=%d", ac->fSupport.allfields );
+ return n; // number of supported contexts
+} // ContextSupport
+
+
+uInt32 FilterSupport( CContext aContext, cAppCharP aFilterRules )
+{
+ ContextP ac= DBC( aContext );
+ DEBUG_DB( ac->fCB, MyDB,Da_FS, "'%s'", aFilterRules );
+ ac->fFilterList.UpdateFields( ac->fCB, aFilterRules );
+ return 0; // none supported until now
+} // FilterSupport
+
+
+void ThreadMayChangeNow( CContext aContext )
+{
+ ContextP ac= DBC( aContext );
+ DEBUG_Exotic_DB( ac->fCB, MyDB,Da_TC, "(%08X)", ac );
+} /* ThreadMayChangeNow */
+
+
+void WriteLogData( CContext aContext, cAppCharP logData )
+{
+ ContextP ac= DBC( aContext );
+ ac->fLogList.UpdateFields( ac->fCB, logData );
+ ac->fLogList.Disp_Items ( ac->fCB, Da_WL );
+} /* WriteLogData */
+
+
+/* ---- display database contents, for debugging only ---- */
+/* Writes the context of items to dbg output path
+ * This routine is implemented for debug purposes only and will NOT BE CALLED by the
+ * SyncML engine.
+ */
+void DispItems( CContext aContext, bool allFields, cAppCharP specificItem )
+{
+ ContextP ac= DBC( aContext );
+ DEBUG_DB( ac->fCB, MyDB,Da_DI, "BEGIN" );
+
+ TDBItem* actB= &ac->fItemList;
+ TDBItemField* actK;
+
+ ac->fSupportList.Disp_Items( NULL, "fSupport", allFields,specificItem );
+ ac->fFilterList.Disp_Items ( NULL, "fFilter", allFields,specificItem );
+ DEBUG_( ac->fCB, "---" );
+
+ if (!actB->next) { // key titles only
+ DEBUG_( ac->fCB, "%s", actB->c_str() );
+
+ actK= &actB->item; // the key identifier
+ while (ListNext( actK ))
+ DEBUG_( ac->fCB, "%s: -", actK->field.c_str() );
+ }
+ else ac->fItemList.Disp_Items( NULL, "fItem", allFields,specificItem );
+
+ DEBUG_DB( ac->fCB, MyDB,Da_DI, "END" );
+} // DispItems
+#endif
+// ----- end general ----------------------------------------
+
+
+
+#ifndef DISABLE_PLUGIN_DATASTOREADMIN
+/* -- ADMINISTRATION ------------------------------------------------------------ */
+/* (will be passed to the "admindata" module) */
+TSyError LoadAdminData( CContext aContext, cAppCharP aLocDB,
+ cAppCharP aRemDB, appCharP *adminData ) {
+ return DBC( aContext )->fAdmin.LoadAdminData ( aLocDB,aRemDB, adminData );
+} /* LoadAdminData */
+
+TSyError SaveAdminData( CContext aContext, cAppCharP adminData ) {
+ return DBC( aContext )->fAdmin.SaveAdminData ( adminData );
+} /* SaveAdminData */
+
+bool ReadNextMapItem( CContext aContext, MapID mID, bool aFirst ) {
+ return DBC( aContext )->fAdmin.ReadNextMapItem( mID, aFirst );
+} /* ReadNextMapItem */
+
+TSyError InsertMapItem( CContext aContext, cMapID mID ) {
+ return DBC( aContext )->fAdmin.InsertMapItem ( mID );
+} /* InsertMapItem */
+
+TSyError UpdateMapItem( CContext aContext, cMapID mID ) {
+ return DBC( aContext )->fAdmin.UpdateMapItem ( mID );
+} /* UpdateMapItem */
+
+TSyError DeleteMapItem( CContext aContext, cMapID mID ) {
+ return DBC( aContext )->fAdmin.DeleteMapItem ( mID );
+} /* DeleteMapItem */
+#endif // DISABLE_PLUGIN_DATASTOREADMIN
+
+
+
+
+#ifndef DISABLE_PLUGIN_DATASTOREDATA
+/* -- READ ---------------------------------------------------------------------- */
+TSyError StartDataRead( CContext aContext, cAppCharP lastToken, cAppCharP resumeToken )
+{
+ ContextP ac= DBC ( aContext );
+ ModuleContext* mc= MoC( ac->fCB->cContext );
+
+ ac->fLastToken = lastToken; // store it in case of no success
+ ac->fResumeToken= resumeToken; // used for OMA DS 1.2 suspend/resume support
+ ac->fNewToken = CurrentTime( -1 ); // make sure that all changes will be seen
+
+ DEBUG_DB( ac->fCB, MyDB,Da_SR, "last='%s' resume='%s' new='%s'",
+ lastToken,resumeToken, ac->fNewToken.c_str() );
+
+ ac->ResetCounter(); // start with the first element
+
+ string s= P_Data + ac->fUserName + ".txt";
+ if (mc) s= ConcatPaths( mc->fDataPath,s );
+
+ ac->fItemList.fFileName= s; // load the database into memory
+ ac->fItemList.LoadDB( false ); // implementation could be changed to "by name" here
+
+ // -------------------
+ s= P_NewItem + ac->fUserName + ".txt";
+ if (mc) s= ConcatPaths( mc->fDataPath,s );
+
+ ac->fNewItem.fFileName= s; // load the database into memory
+ ac->fNewItem.LoadDB ( false ); // implementation could be changed to "by name" here
+
+ TDBItem* act= &ac->fNewItem;
+ ac->fNewID= F_First;
+ if (ListNext( act )) ac->fNewID= act->itemID;
+
+ return LOCERR_OK;
+} /* StartDataRead */
+
+
+
+TSyError ReadNextItem( CContext aContext, ItemID aID, appCharP *aItemData,
+ sInt32 *aStatus, bool aFirst )
+{
+ ContextP ac= DBC( aContext );
+ if (aFirst) ac->ResetCounter();
+ *aItemData= NULL; // safety setting, if no value returning
+ cAppCharP mode= "Unused"; // default
+ string s;
+
+ ac->fNth++;
+ if (ListNext( ac->fCurrent )) {
+ aID->item = StrAlloc( ac->fCurrent->itemID.c_str() );
+ aID->parent= StrAlloc( ac->fCurrent->parentID.c_str() );
+
+ if (ac->fSupport.allfields) ReadItem( aContext, aID, aItemData );
+
+ // assume, there are two yyyymmddThhmmssZ strings, which can be
+ // compared directly w/o conversion into lineartime
+ *aStatus= CompareTokens( ac->fCurrent->fToken, ac->fLastToken, ac->fResumeToken );
+ switch (*aStatus) {
+ case ReadNextItem_Unchanged: mode= "Unchanged"; s= Apo(ac->fLastToken ) + " >= "; break;
+ case ReadNextItem_Changed : mode= "Changed"; s= Apo(ac->fLastToken ) + " < " ; break;
+ case ReadNextItem_Resumed : mode= "Resumed"; s= Apo(ac->fResumeToken) + " < " ; break;
+ } // switch
+ s+= Apo( ac->fCurrent->fToken );
+
+ DEBUG_DB( ac->fCB, MyDB,Da_RN, "(no=%d) %s (%s): %s",
+ ac->fNth, mode, ac->fCurrent->c_str(), s.c_str() );
+ return LOCERR_OK;
+ } // if
+
+ DEBUG_DB( ac->fCB, MyDB,Da_RN, "(no=%d) EOF", ac->fNth );
+ *aStatus= ReadNextItem_EOF;
+ return LOCERR_OK;
+} /* ReadNextItem */
+
+
+// The <asKey> variant is not supported for textdb
+TSyError ReadNextItemAsKey( CContext /* aContext */, ItemID /* aID */, KeyH /* aItemKey */,
+ sInt32* /* aStatus */, bool /* aFirst */ ) {
+ return LOCERR_NOTIMP;
+} /* ReadNextItemAsKey */
+
+
+
+static bool ItemFound( cItemID aID, TDBItem* &actL )
+{
+ appCharP p= aID->parent;
+ if (!p) p= "";
+
+ while (ListNext( actL )) {
+ if (strcmp( aID->item, actL->itemID.c_str() )==0 && // search for item <aID>
+ strcmp( p, actL->parentID.c_str() )==0) return true;
+ } // while
+
+ return false;
+} // ItemFound
+
+
+TSyError ReadItem( CContext aContext, cItemID aID, appCharP *aItemData )
+{
+ ContextP ac = DBC( aContext );
+ TSyError err= DB_NotFound;
+ TDBItem* actL;
+ TDBItemField* actK;
+ TDBItemField* actF;
+ int a= 0;
+ string s, dat;
+
+ *aItemData= ""; actL= &ac->fItemList;
+ if (ItemFound( aID, actL )) {
+ actK = &ac->fItemList.item; // now concatenate <aItemData>
+ int n= 0; actF= &actL->item;
+ while (ListNext( actK,actF )) {
+ s= KeyAndField( actK,actF );
+
+ if (n>0) dat+= '\n';
+ n++; dat+= s;
+ } // while
+
+ *aItemData= StrAlloc( dat.c_str() );
+ err= LOCERR_OK;
+ } // if
+
+ s= ItemID_Info( aID );
+
+ if (!err) a= strlen( *aItemData );
+ DEBUG_DB( ac->fCB, MyDB,Da_RI, "%s '%s' len=%d err=%d",
+ s.c_str(), *aItemData, a, err );
+ return err;
+} /* ReadItem */
+
+
+// The <asKey> variant is not supported for textdb
+TSyError ReadItemAsKey( CContext /* aContext */, cItemID /* aID */, KeyH /* aItemKey */ ) {
+ return LOCERR_NOTIMP;
+} /* ReadItemAsKey */
+
+
+
+TSyError EndDataRead( CContext aContext )
+{
+ ContextP ac= DBC( aContext ); // no additional actions needed at this point
+ DEBUG_DB( ac->fCB, MyDB,Da_ER, "(%08X)", ac ); // just logging
+ return LOCERR_OK;
+} /* EndDataRead */
+
+
+
+
+/* -- WRITE --------------------------------------------------------------------- */
+TSyError StartDataWrite( CContext aContext )
+{
+ ContextP ac= DBC( aContext ); // no additional actions needed at this point
+ DEBUG_DB( ac->fCB, MyDB,Da_SW, "(%08X)", ac ); // just logging
+ return LOCERR_OK;
+} /* StartDataWrite */
+
+
+
+TSyError InsertItem( CContext aContext, cAppCharP aItemData, ItemID newID )
+{
+ ContextP ac= DBC( aContext );
+ string newItemID;
+ TDBItem* act;
+
+ ItemID_Struct a; a.item = "";
+ a.parent= newID->parent; if (!a.parent) a.parent= "";
+
+ TSyError err= ac->fItemList.CreateEmptyItem( &a, newItemID, act, ac->fNewID );
+
+ if (err) a.item= "???"; // undefined
+ else a.item= (char*)newItemID.c_str();
+ string s= ItemID_Info( &a );
+
+ DEBUG_DB( ac->fCB, MyDB,Da_II, "%s '%s' err=%d", s.c_str(), aItemData, err );
+
+ if (!err) { // the newly created item will now be updated => filled with content
+ ItemID_Struct u; u.item = NULL; // no change of item
+ u.parent= NULL;
+ err= UpdateItem( aContext, aItemData, &a,&u );
+ } // if
+
+ if (err) {
+ DeleteItem( aContext, &a ); // remove it again in case of an error
+ newID->item= NULL;
+ }
+ else {
+ newID->item= StrAlloc ( newItemID.c_str() );
+ ac->fNewID = IntStr( atoi( newID->item )+1 );
+ } // if
+
+ return err;
+} /* InsertItem */
+
+
+// The <asKey> variant is not supported for textdb
+TSyError InsertItemAsKey( CContext /* aContext */, KeyH /* aItemKey */, ItemID /* newID */ ) {
+ return LOCERR_NOTIMP;
+} /* InsertItemAsKey */
+
+
+
+TSyError UpdateItem( CContext aContext, cAppCharP aItemData, cItemID aID,
+ ItemID /* updID */ )
+// This implementation will not create a new <updID>
+{
+ ContextP ac = DBC( aContext );
+ TSyError err= DB_NotFound;
+ TDBItem* actI;
+ err= ac->fItemList.GetItem( aID, actI ); // the item must exist already
+ if (!err) err= ac->fItemList.UpdateFields( ac->fCB, aItemData, actI, false,
+ ac->fNewToken.c_str() );
+
+ string s= ItemID_Info( aID );
+ DEBUG_DB( ac->fCB, MyDB,Da_UI, "%s '%s' err=%d", s.c_str(), aItemData, err );
+ return err;
+} /* UpdateItem */
+
+
+// The <asKey> variant is not supported for textdb
+TSyError UpdateItemAsKey( CContext /* aContext */, KeyH /* aItemKey */, cItemID /* aID */,
+ ItemID /* updID */ ) {
+ return LOCERR_NOTIMP;
+} /* UpdateItemAsKey */
+
+
+
+TSyError MoveItem( CContext aContext, cItemID aID, cAppCharP newParID )
+{
+ ContextP ac = DBC( aContext );
+ TSyError err= DB_NotFound;
+ TDBItem* actL;
+
+ ItemID_Struct nID; nID.item = aID->item;
+ nID.parent= (appCharP)newParID;
+ err= ac->fItemList.ParentExist( aID->item, newParID );
+
+ if (!err) err= ac->fItemList.GetItem( aID, actL );
+ if (!err) actL->parentID= newParID;
+
+ string s= ItemID_Info( aID );
+ string n= ItemID_Info( &nID );
+ DEBUG_DB( ac->fCB, MyDB,Da_MvI, "%s => %s err=%d", s.c_str(),n.c_str(), err );
+ return err;
+} /* MoveItem */
+
+
+
+TSyError DeleteItem( CContext aContext, cItemID aID )
+{
+ ContextP ac= DBC( aContext );
+
+ TDBItem* act;
+ TSyError err= ac->fItemList.GetItem( aID, act ); if (err) return err; // must exist already
+ if (ac->fCurrent==act) ListBack( ac->fCurrent, &ac->fItemList );
+
+ err= ac->fItemList.DeleteItem( aID );
+ DEBUG_DB( ac->fCB, MyDB,Da_DeI, "%s err=%d", ItemID_Info( aID ).c_str(), err );
+ return err;
+} /* DeleteItem */
+
+
+
+TSyError FinalizeLocalID( CContext /* aContext */, cItemID aID, ItemID updID )
+{
+ updID->item= StrAlloc( aID->item );
+ return LOCERR_OK;
+
+//return LOCERR_NOTIMP;
+} /* FinalizeLocalID */
+
+
+
+TSyError DeleteSyncSet( CContext aContext )
+{
+ TSyError err;
+ ContextP ac= DBC( aContext );
+ ItemID_Struct iID;
+ appCharP itemData;
+ sInt32 status;
+
+ while (true) {
+ err= ReadNextItem( aContext, &iID, &itemData, &status, true );
+ if (err || status==ReadNextItem_EOF) break;
+
+ err= DeleteItem( aContext, &iID );
+ DEBUG_DB( ac->fCB, MyDB,Da_DSS, "%s: err=%d\n", ItemID_Info( &iID ).c_str(), err );
+ if (err) break;
+
+ DisposeObj( aContext, iID.item );
+ DisposeObj( aContext, iID.parent );
+ DisposeObj( aContext, itemData );
+ } // while
+
+ return err;
+} /* DeleteSyncSet */
+
+
+
+/* -- BLOBs --------------------------------------------------------------------- */
+#ifndef DISABLE_PLUGIN_BLOBS
+TSyError ReadBlob ( CContext aContext, cItemID aID, cAppCharP aBlobID,
+ appPointer *aBlkPtr, memSize *aBlkSize,
+ memSize *aTotSize,
+ bool aFirst, bool *aLast ) {
+ return DBC( aContext )->fBlob.ReadBlob ( aID, aBlobID,
+ aBlkPtr,aBlkSize, aTotSize, aFirst,aLast );
+} // ReadBlob
+
+TSyError WriteBlob( CContext aContext, cItemID aID, cAppCharP aBlobID,
+ appPointer aBlkPtr, memSize aBlkSize,
+ memSize aTotSize,
+ bool aFirst, bool aLast ) {
+ return DBC( aContext )->fBlob.WriteBlob ( aID, aBlobID,
+ aBlkPtr,aBlkSize, aTotSize, aFirst,aLast );
+} // WriteBlob;
+
+TSyError DeleteBlob( CContext aContext, cItemID aID, cAppCharP aBlobID ) {
+ return DBC( aContext )->fBlob.DeleteBlob ( aID, aBlobID );
+} // DeleteBlob
+#endif
+/* ------------------------------------------------------------------------------ */
+
+
+
+/* If all operations for this datastore access are successful, the current
+ * itemlist can be written to a text file and the <newToken> will be returned
+ */
+TSyError EndDataWrite( CContext aContext, bool success, appCharP *newToken )
+{
+ ContextP ac= DBC( aContext );
+ DEBUG_DB( ac->fCB, MyDB,Da_EW, "=> %s", success ? "COMMIT":"ROLLBACK" );
+
+ TDBItem* act= &ac->fNewItem;
+ ac->fNewItem.CreateItem( ac->fNewID,"", act );
+
+ TSyError err= DB_Error;
+ ac->fNewItem.SaveDB ( false );
+ if (success) err= ac->fItemList.SaveDB( false );
+ if (err) ac->fNewToken= ac->fLastToken;
+ *newToken= StrAlloc( ac->fNewToken.c_str() );
+
+ DEBUG_DB( ac->fCB, MyDB,Da_EW, "err=%d newToken='%s'", err, *newToken );
+ return err;
+} /* EndDataWrite */
+#endif // DISABLE_PLUGIN_DATASTOREDATA
+
+
+
+/* ----- "script-like" ADAPT --------- */
+TSyError AdaptItem( CContext aContext, appCharP *aItemData1,
+ appCharP *aItemData2,
+ appCharP *aLocalVars, uInt32 aIdentifier )
+{
+ /**** CAN BE ADAPTED BY USER ****/
+ // NOTE: will not yet be called by the SyncML engine
+ ContextP ac= DBC( aContext );
+ DEBUG_DB( ac->fCB, MyDB,"AdaptItem", "'%s' '%s' '%s' id=%d",
+ *aItemData1,*aItemData2,*aLocalVars, aIdentifier );
+ *aItemData1 = StrAlloc( *aItemData1 );
+ *aItemData2 = StrAlloc( "4:just_a_test\n" );
+ return LOCERR_OK;
+} /* AdaptItem */
+
+
+
+/* ----------------------------------- */
+void DisposeObj( CContext aContext, void* memory )
+{
+ ContextP ac= DBC( aContext );
+ DEBUG_Exotic_DB( ac->fCB, MyDB,Da_DO, "%d free at %08X '%s'", ac, memory,memory );
+ StrDispose ( memory );
+} /* DisposeObj */
+
+
+TSyError DeleteContext( CContext aContext )
+{
+ ContextP ac= DBC( aContext );
+ delete ac; // release the structure itself
+ return LOCERR_OK;
+} /* DeleteContext */
+
+
+} /* namespace */
+/* eof */
diff --git a/src/sysync_SDK/Sources/SDK_support.cpp b/src/sysync_SDK/Sources/SDK_support.cpp
new file mode 100755
index 0000000..4000aba
--- /dev/null
+++ b/src/sysync_SDK/Sources/SDK_support.cpp
@@ -0,0 +1,967 @@
+/*
+ * File: SDK_support.cpp
+ *
+ * Author: Beat Forster (bfo@synthesis.ch)
+ *
+ * Some SDK support utility functions for C++
+ *
+ *
+ * Copyright (c) 2005-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ */
+
+#include "sync_include.h" // import some global things
+#include "sync_dbapidef.h"
+#include "SDK_util.h"
+#include "SDK_support.h"
+
+
+#ifdef __cplusplus
+ namespace sysync {
+#endif
+
+#define MyDB "SDK" /* local debug name */
+#define FLen 30 /* max length of (internal) item name */
+
+
+
+// --- plugin name handling ----------------------------------------
+//! recognize separation
+static bool SepFound( string name, string::size_type &pos, string sep= "!", bool backwards= false )
+{
+ if (backwards) pos= name.rfind( sep, name.length()-1 );
+ else pos= name.find ( sep, 0 );
+ return pos!=string::npos;
+} // SepFound
+
+
+
+// returns true if in brackets: "[aaa]"
+bool InBracks( string name )
+{
+ string::size_type o, c;
+ return SepFound( name, o, "[" ) && o==0
+ && SepFound( name, c, "]", true ) && c==name.length()-1;
+} // InBracks
+
+
+
+// returns true if twice in brackets: "[[aaa]]"
+static bool InDBracks( string name )
+{
+ string::size_type o, c;
+ return SepFound( name, o, "[[" ) && o==0
+ && SepFound( name, c, "]]", true ) && c==name.length()-2;
+} // InDBracks
+
+
+
+bool CutBracks( string &name )
+{
+ bool ok= InBracks( name );
+ if (ok) name= name.substr( 1, name.length()-2 ); // cut the brackets
+ return ok;
+} // CutBracks
+
+
+
+/*! Returns true, if <name> belongs to an internal LIB.
+ * Returns false for DLL access
+ * The following notations are recognized as LIB:
+ * ""
+ * "[aaa]"
+ * "[aaa!bbb]"
+ * "[aaa x]"
+ * "[aaa x!bbb]"
+ *
+ * "[aaa]!bbb"
+ * "[aaa x]!bbb"
+ *
+ * "[aaa] x"
+ * "[aaa] x!bbb"
+ */
+bool IsLib( string name )
+{
+ if (name.empty() || InBracks( name )) return true; // empty or embraced with '[' ']'
+
+ string::size_type o, c;
+ return SepFound( name, o, "[" ) && o==0
+ && SepFound( name, c, "]",true ) && ( name[ c+1 ]=='!' ||
+ name[ c+1 ]==' ' );
+} // IsLib
+
+
+
+// Cut the brackets at begin/end of <name>, if available
+string NoBracks( string name )
+{
+ string p;
+
+ string::size_type pos;
+ if (SepFound( name, pos )) { // check the case "[aaa]!bbb" first
+ p= Plugin_MainName( name );
+ if (IsLib( p )) {
+ p= NoBracks( p ) + "!" + Plugin_SubName ( name );
+ return p;
+ } // if
+ } // if
+
+ if (SepFound( name, pos, " " )) { // check the case "[aaa] xx"
+ if (name[ 0 ]=='[' &&
+ name[ pos-1 ]==']' &&
+ NextToken( name, p )) {
+ p= NoBracks( p ) + " " + name;
+ return p;
+ } // if
+ } // if
+
+ // check now for "[aaa]" or "[aaa!bbb]"
+ CutBracks( name );
+ return name;
+} // NoBracks
+
+
+
+/*! Add brackets at begin/end of <name>, if not yet there */
+string AddBracks( string name )
+{
+ if (!name.empty() && !InBracks( name )) name= Bracks( name );
+ return name;
+} // AddBracks
+
+
+
+static string Plugin_PartName( string name, bool asSub )
+{
+ string::size_type pos, o, c;
+ if (!SepFound( name, pos )) { // at least one '!' must be there
+ if (asSub) return "";
+ else return name;
+ } // if
+
+ int nBridge= 0;
+ if (SepFound( name, o, "[" ) && o==0) {
+ for (c= pos+1; c<name.length(); c++) {
+ if (name[ c ]=='[') nBridge--;
+ if (name[ c ]==']') nBridge++;
+ } // for
+
+ if (nBridge<0) nBridge= 0; // adjustment problems
+ } // if
+
+ if (asSub) {
+ name= name.substr( pos+1, name.length()-pos-1 );
+ while (nBridge-- > 0) name= '[' + name;
+ }
+ else {
+ name= name.substr( 0, pos );
+ while (nBridge-- > 0) name= name + ']';
+ } // if
+
+ while (InDBracks( name )) CutBracks( name );
+ return name;
+} // Plugin_PartName
+
+
+
+//! the part before and after '!'
+string Plugin_MainName( string name ) { return Plugin_PartName( name, false ); }
+string Plugin_SubName ( string name ) { return Plugin_PartName( name, true ); }
+
+bool WithSubSystem( string name, string &sMain, string &sSub )
+{
+ sMain= Plugin_MainName( name );
+ sSub = Plugin_SubName ( name );
+ return !sSub.empty();
+} // WithSubSystem
+
+
+bool IsAdmin( string &aContextName )
+{
+ uInt32 fLen= aContextName.length();
+ uInt32 fPos= fLen - strlen( ADMIN_Ident );
+
+ bool asAdmin= aContextName.rfind( ADMIN_Ident,fLen )==fPos;
+ if (asAdmin) aContextName= aContextName.substr( 0, fPos );
+ return asAdmin;
+} // IsAdmin
+
+
+
+// ------ command line utility functions -------------------------------------
+// assign command line arguments
+void CLine::InitOptions( int argc, char* argv[] )
+{
+ fArgc= argc; // get copies
+ fArgv= argv;
+} // InitOptions
+
+
+// Get <n>th option string of <opt>
+void CLine::StrOpt( char opt, int n, string &value )
+{
+ int p= 0;
+ for (int i=1; i<fArgc; i++) {
+ if (i+n >= fArgc) return;
+ char* s= fArgv[ i+p ];
+
+ if (*s=='-') { // search for options
+ do { s++;
+ if (*s==opt) { // yes, that's the one we're looking for
+ bool ok= true;
+ int act= i+p+1;
+ int lim= i+n;
+
+ for (int j=act; j<=lim; j++) {
+ s= fArgv[ j ];
+ if (*s=='-') { // no other options allowed
+ s++;
+ if (*s<'0' || *s>'9') { // negative numbers ARE allowed
+ ok= false; break;
+ } // if
+ } // if
+
+ p++;
+ } // for
+
+ if (ok) {
+ value= s; return; // it's ok -> assign
+ } // if
+ } // if
+ } while (*s!='\0' && *s!=' ');
+ } // if
+ } // for
+} // StrOpt;
+
+
+bool CLine::NextOpt( char opt, int &n, string &value )
+{
+ value= "";
+ StrOpt( opt, n+1, value );
+
+ if (value.empty()) return false;
+ else { n++; return true; }
+} // NextOpt
+
+
+void CLine::IntOpt( char opt, int n, uInt32 &value )
+{
+ string s= IntStr( value );
+ StrOpt( opt,n, s );
+ value= atoi( s.c_str() );
+} // IntOpt
+
+
+void CLine::IntOpt( char opt, int n, sInt32 &value )
+{
+ string s= IntStr( value );
+ StrOpt( opt,n, s );
+ value= atoi( s.c_str() );
+} // IntOpt
+
+
+void CLine::IntOpt( char opt, int n, uInt16 &value )
+{
+ string s= IntStr( value );
+ StrOpt( opt,n, s );
+ value= atoi( s.c_str() );
+} // IntOpt
+
+
+void CLine::IntOpt( char opt, int n, sInt16 &value )
+{
+ string s= IntStr( value );
+ StrOpt( opt,n, s );
+ value= atoi( s.c_str() );
+} // IntOpt
+
+
+// check for a specific option
+bool CLine::Opt_Found( char opt )
+{
+ for (int n=1; n<fArgc; n++) {
+ char* s= fArgv[ n ];
+ if (*s=='-') {
+ do { s++;
+ if (*s==opt) return true;
+ } while (*s!='\0' && *s!=' ');
+ } // if
+ } // for
+
+ return false;
+} // Opt_Found
+
+
+
+
+// ---- Capabilities support --------------------------------------------------------
+static void AddCapa( string &s, string fieldName, string fieldValue )
+{
+ if (fieldValue.empty()) return; // only with a valid <fieldValue>
+
+ if (!s.empty()) s+= '\n'; // add a separator (in-between)
+
+ s+= fieldName; // and the new identifier
+ s+= ':' + fieldValue;
+} // AddCapa
+
+
+// Add a fieldname with "no" attribute
+void NoField( string &s, string fieldName ) { AddCapa( s, fieldName, "no" ); }
+
+// Add a fieldname with "yes" attribute
+void YesField( string &s, string fieldName ) { AddCapa( s, fieldName, "yes" ); }
+
+// Add a fieldname with "both" attribute
+void BothField( string &s, string fieldName ) { AddCapa( s, fieldName,"both" ); }
+
+
+// Add the minimum supported version for the plugin to <s>:
+// ===> <resumeToken> MUST be sent at StartDataRead ==> V1.0.6.X
+void MinVersion( string &s, CVersion internalMinVersion )
+{
+ CVersion minV= VP_ResumeToken; // At least V1.0.6.X for all ...
+ if (minV<=internalMinVersion) // ... or even higher
+ minV= internalMinVersion;
+
+ string value;
+ GetField( s, CA_MinVersion, value );
+
+ if (!value.empty()) { // already a min version defined ?
+ if (VersionNr( value )>=minV) return; // it's perfectly fine already -> done
+
+ RemoveField( s, CA_MinVersion, value ); // <value> must be replaced, remove it
+ } // if
+
+ AddCapa( s, CA_MinVersion, VersionStr( minV ) );
+} // MinVersion
+
+
+// Add sub system's version, if smaller than internal
+void SubVersion( string &s, CVersion subSysVersion )
+{
+ if (Plugin_Version( 0 )<subSysVersion)
+ AddCapa( s, CA_SubVersion, VersionStr ( subSysVersion ) );
+} // SubVersion
+
+
+// Separator for the sub system
+void SubSystem ( string &s, string subName ) {
+ AddCapa( s, CA_SubSystem, NoBracks( subName ) );
+} // SubSystem
+
+
+// Compose capability string
+void Manufacturer( string &s, string name ) { AddCapa( s, CA_Manufacturer, name ); }
+void Description ( string &s, string desc ) { AddCapa( s, CA_Description, desc ); }
+void GuidStr ( string &s, string guidStr ) { AddCapa( s, CA_GUID, guidStr ); }
+
+
+// Add global context <gContext> information to <s>
+void GContext( string &s, GlobContext* gContext ) {
+ if (gContext) AddCapa( s, CA_GlobContext, RefStr( gContext ) );
+} // CContext
+
+
+// Add capability error, if <err>
+void CapaError( string &s, TSyError err ) {
+ if (err) AddCapa( s, CA_Error, IntStr(err) );
+} // CapaError
+
+
+
+// ---- utility functions ----------------------------------------------------------
+// Allocate local memory for an item
+void MapAlloc( MapID rslt, cMapID mID )
+{
+ rslt->localID = StrAlloc( mID->localID );
+ rslt->remoteID= StrAlloc( mID->remoteID );
+ rslt->flags = mID->flags;
+ rslt->ident = mID->ident;
+} // MapAlloc
+
+// Allocate local memory for an item
+void ItemAlloc( ItemID rslt, cItemID aID )
+{
+ rslt->item = StrAlloc( aID->item );
+ rslt->parent= StrAlloc( aID->parent );
+} // ItemAlloc
+
+void BlockAlloc( appPointer *newPtr, appPointer bPtr, uInt32 bSize )
+{
+ *newPtr= malloc( bSize );
+ memcpy( *newPtr, bPtr, bSize );
+} // BlockAlloc
+
+
+
+// -----------------------------------------------------------
+// Get int value <i> as string
+string IntStr( sInt32 i )
+{
+ char f[ FLen ];
+ sprintf ( f, "%d", (int)i );
+ string s= f;
+ return s;
+} // IntStr
+
+
+string RefStr( void* a, bool asHex )
+{
+ char f[ FLen ];
+
+ #if __WORDSIZE == 64
+ if (asHex) sprintf( f, "%016llX", (unsigned long long)a );
+ else sprintf( f, "%lld", (long long int )a );
+ #else
+ if (asHex) sprintf( f, "%08X", (unsigned int)a );
+ else sprintf( f, "%d", (int)a );
+ #endif
+
+ string s= f;
+ return s;
+} // RefStr
+
+
+static string HexConv( uInt32 h, cAppCharP format )
+{
+ char f[ FLen ];
+ sprintf ( f, format, h );
+ string s= f;
+ return s;
+} // HexConv
+
+/*! Get byte/hex/longhex value <h> as string */
+string ByteStr( uInt8 h ) { return HexConv( h, "%02X" ); }
+string HexStr( uInt16 h ) { return HexConv( h, "%04X" ); }
+string LHexStr( uInt32 h ) { return HexConv( h, "%08X" ); }
+
+
+/*! Get ch as int value */
+int HexNr( char ch )
+{
+ int i= 0;
+ if ( ch>='0' && ch<='9' ) i= ch-'0';
+ if ( ch>='a' && ch<='f' ) i= ch-'a'+10;
+ if ( ch>='A' && ch<='F' ) i= ch-'A'+10;
+ return i;
+} // HexNr
+
+
+/*! Get string as hex value */
+void* LHexRef( string v )
+{
+ #if __WORDSIZE == 64
+ long long a= 0;
+ const int mx= 16;
+ #else
+ long a= 0;
+ const int mx= 8;
+ #endif
+
+ int len= v.length();
+ if (len>mx) len= mx;
+
+ for (int i= 0; i<len; i++) {
+ a= 16*a + HexNr( v[ i ] );
+ } // for
+
+ return (void*)a;
+} // LHexRef
+
+
+/*! Get string as hex value */
+long LHex( string v )
+{
+ long a= 0;
+ const int mx= 8;
+
+ int len= v.length();
+ if (len>mx) len= mx;
+
+ for (int i= 0; i<len; i++) {
+ a= 16*a + HexNr( v[ i ] );
+ } // for
+
+ return a;
+} // LHex
+
+
+/*! Get boolean as const char string */
+cAppCharP Bo( bool b )
+{
+ if (b) return "true";
+ else return "false";
+} // Bo
+
+/*! Get boolean as short const char string "v"=true / "o"=false */
+cAppCharP BoS( bool b )
+{
+ if (b) return "v";
+ else return "o";
+} // BoS
+
+
+// Get <v> as version string Va.b.c.d
+string VersionStr( CVersion v )
+{
+ if (v==VP_BadVersion) return "<unknown>";
+ if (v==0) return "--";
+
+ string s;
+ for (int i= 0; i<= 3; i++) {
+ if (!s.empty()) s= '.' + s;
+ s= IntStr( v % 256 ) + s;
+ v= v / 256;
+ } // for
+
+ return 'V' + s;
+} // VersionStr
+
+
+
+// Get <v> as version long 0x0a0b0c0d
+CVersion VersionNr( string s )
+{
+ long v= 0x00000000;
+ string a;
+
+ int j= s.find( 'V',0 );
+ if (j==0) s= s.substr( 1, s.length()-1 );
+
+ if (s.length()==8 && // direct conversion
+ s.find( '.',0 )==string::npos) {
+ return LHex( s );
+ } // if
+
+ for (int i= 0; i<= 3; i++) {
+ a= s;
+ j= a.find( '.',1 );
+ if (j>=1) {
+ a= a.substr( 0,j );
+ s= s.substr( j+1, s.length()-j-1 );
+ }
+ else
+ s= "";
+ // if
+
+ j= atoi( a.c_str() );
+ v= 256*v + j;
+ } // for
+
+ return v;
+} // VersionStr
+
+
+
+bool SameBegin( string s, string cmp )
+{
+ uInt32 w= s.length()-cmp.length();
+ return w>=0 && s.find ( cmp, 0 )==0;
+} // SameBegin
+
+
+bool SameEnd ( string s, string cmp )
+{
+ uInt32 w= s.length()-cmp.length();
+ return w>=0 && s.find ( cmp, w )==w;
+} // SameEnd
+
+
+
+// Replace 0A -> 0B, TAB -> ' ' when saving
+void ReplaceSave( cAppCharP str, string &rslt )
+{
+ unsigned char* s= (unsigned char*)str; // support UTF8 as well
+ char ch= '\0';
+ rslt= "";
+
+ while (*s!='\0') {
+ bool ok= true;
+
+ switch (*s) {
+ case 0x0A : ch= 0x0B; break;
+ case 0x09 : ch= ' '; break; // TAB -> SP
+ case 0x1D : ch= *s ; break; // do not touch ARR Sep
+
+ default : if (*s>=0x20 &&
+ *s!=0x7F) ch= *s;
+ else ok= false; // ignore others
+ } // switch
+
+ s++;
+ if (ok) rslt+= ch;
+ } // while
+} // ReplaceSave
+
+
+
+// Replace 0B -> 0A, when loading
+void ReplaceLoad( cAppCharP str, string &rslt )
+{
+ unsigned char* s= (unsigned char*)str; // support UTF8 as well
+ char ch= '\0';
+ rslt= "";
+
+ while (*s!='\0') {
+ bool ok= true;
+
+ switch (*s) {
+ case 0x0B : ch= 0x0A; break;
+ case 0x09 :
+ case 0x1D : ch= *s ; break; // do not touch ARR Sep
+ default : if (*s>=0x20 &&
+ *s!=0x7F) ch= *s;
+ else ok= false; // ignore others
+ } // switch
+
+ s++;
+ if (ok) rslt+= ch;
+ } // while
+} // ReplaceLoad
+
+
+
+// ----------------------------------------------------------------------
+string Apo ( string s ) { return "'" + s + "'"; } // Get string within single quotes
+string Parans( string s ) { return "(" + s + ")"; } // Get string within parans
+string Bracks( string s ) { return "[" + s + "]"; } // Get string within brackets
+
+void CutCh ( string &s ) { s= s.substr( 1, s.length()-1 ); } // Cut 1st char
+void CutLSP( string &s ) { while (s.find( " ",0 )==0) CutCh( s ); } // Cut all leading SP
+
+
+// Token separation
+bool NextToken( string &s, string &nx, string sep )
+{
+ nx= "";
+ CutLSP ( s );
+ if ( s.empty()) return false;
+
+ string::size_type len= s.length();
+ string::size_type pos= s.find( sep, 0 ); // search for the last separator
+ if (pos==string::npos) {
+ nx= s; // no separation possible, result is all
+ s = "";
+ }
+ else {
+ int sepLen= sep.length();
+ nx= s.substr( 0, pos ); // separate, if found
+ s = s.substr( pos+sepLen,len-pos-sepLen+1 );
+ CutLSP( s );
+ } // if
+
+ return true;
+} // NextToken
+
+
+
+string LineConv( string str, uInt32 maxLen, bool visibleN )
+{
+ cAppCharP BN = "\\n";
+ string s, nx, value;
+ bool first= true;
+ bool b= false;
+ bool e= false;
+
+ if (visibleN) {
+ string::size_type len= str.length();
+ if (len>0) {
+ b= str.find ( "\n", 0 )==0;
+ e= str.rfind( "\n", len-1 )==len-1;
+ } // if
+ } // if
+
+ while (NextToken( str,nx, "\n" )) {
+ if (first && str.empty()) return nx;
+
+ string v= nx;
+ if (NextToken( nx,value, ":" ) && !nx.empty()) {
+ if (!s.empty()) {
+ if (visibleN) s+= BN;
+ else s+= " ";
+ } // if
+
+ s+= v;
+ } // if
+
+ first= false;
+ } // while
+
+ if (b) s= BN + s;
+ if (e) s= s + BN;
+
+ if (maxLen>0 &&
+ maxLen<s.length()) { s= s.substr( 0,maxLen-2 ) + ".."; }
+
+ return s;
+} // LineConv
+
+
+
+// Get <aID.item>,<aID.parent> string
+string ItemID_Info( cItemID aID, string aName )
+{
+ string id;
+
+ if (aID) {
+ if (aID->item) id= aID->item;
+ if (aID->parent) {
+ string p= aID->parent;
+ if (!p.empty()) id+= "," + p;
+ } // if
+ } // if
+ id= Parans( id );
+ if (!aName.empty()) id= aName + "=" + id;
+ return id;
+} // ItemID_Info
+
+
+// Get <mID.localID> <mID.remoteID> <mID.flags> <mID.ident> string
+string MapID_Info( cMapID mID )
+{
+ string id;
+
+ if (mID) {
+ if (mID->localID) id+= Apo( mID->localID ) + " ";
+ if (mID->remoteID) id+= Apo( mID->remoteID ) + " ";
+
+ id+= HexStr( mID->flags ) + " ";
+ id+= IntStr( mID->ident );
+ } // if
+
+ id= "map=" + Parans( id );
+ return id;
+} // MapID_Info
+
+
+
+// Concat <path1> and <path2>
+string ConcatPaths( string path1, string path2, bool isJava )
+{
+ if (path2.empty()) return path1;
+ if (path1.empty()) return path2;
+
+ if (isJava) path1+= '/';
+ else {
+ #ifdef _WIN32
+ path1+= '\\';
+ #else
+ path1+= '/';
+ #endif
+ } // if
+
+ return path1 + path2;
+} // ConcatPaths
+
+
+// Concat <path1>, <path2> and <path3>
+string ConcatPaths( string path1, string path2, string path3, bool isJava ) {
+ return ConcatPaths( ConcatPaths ( path1, path2, isJava ), path3, isJava );
+} // ConcatPaths
+
+
+
+/*! Notation: <name1>"!"<name2> */
+string ConcatNames( string name1, string name2, string sep )
+{
+ if (!name1.empty() &&
+ !name2.empty()) name1+= sep;
+ name1+= name2;
+ return name1;
+} // ConcatNames
+
+
+
+static bool Chk( string s, string &aDat, cAppCharP &q, cAppCharP &qV, int offs )
+{
+ string::size_type pos= aDat.find( s, 0 );
+
+ do {
+ // either at the beginning ...
+ if (pos==0) break;
+
+ // ... or right after a '\n'
+ pos= aDat.find( "\n" + s, 0 );
+ if (pos==string::npos) return false;
+ pos++; // without \n at the beginning
+ } while (false);
+
+ q = aDat.c_str() + pos;
+ qV= q + s.length() + offs;
+ return true;
+} // Chk
+
+
+
+static bool GetField_R( string &aDat, string aKey, string &value, bool removeIt )
+{
+ cAppCharP q;
+ cAppCharP qV;
+ cAppCharP qN;
+ cAppCharP qR;
+
+ value= ""; // in case of not found
+
+ if (!Chk( aKey + StdPattern, aDat, q,qV, 0 ) && // is it one of these types ?
+ !Chk( aKey + ArrayPattern, aDat, q,qV,-1 ) &&
+ !Chk( aKey + ";", aDat, q,qV,-1 )) return false;
+
+ qN= strstr( q,"\n" ); // search for next separator
+ bool last= qN==NULL;
+ if (last) qN= (cAppCharP)q + strlen( q ); // it's the last field ?
+ qR= qN-1;
+ if (*qR!='\r') qR= qN; // ignore \r
+ value.assign ( qV, qR-qV ); // get the snippet
+
+ if (removeIt) {
+ cAppCharP qE= qN; if (*qE) qE++;
+
+ if (last) { // if it is the last one, cut line break before as well
+ while (q>aDat.c_str()) {
+ q--;
+ if (*q!='\n' &&
+ *q!='\r') { q++; break; }
+ } // while
+ } // if
+
+ string::size_type pos= q-aDat.c_str(); // offset to <q> at <aDat>
+ string::size_type n = qE-q; // size of the field
+
+ aDat= aDat.erase( pos, n );
+ } // if
+
+ return true;
+} // GetField_R
+
+
+
+bool GetField( string aDat, string aKey, string &value ) {
+ return GetField_R ( aDat, aKey, value, false );
+} // GetField
+
+
+bool RemoveField( string &aDat, string aKey, string &removedValue ) {
+ return GetField_R ( aDat, aKey, removedValue, true )
+ && !removedValue.empty();
+} // RemoveField
+
+
+
+bool FlagOK( string aDat, string aKey, bool isYes )
+{
+ string value;
+ GetField( aDat, aKey, value );
+ bool ok, emp= aKey.empty();
+
+ if (isYes) { ok= !emp && ( value=="yes"
+ || value=="true"
+ || value=="both" ); }
+ else { ok= emp || ( value!="no"
+ && value!="false" ); }
+
+//printf( "value=%-10s isYes=%-5d ok=%-5d aEmp=%-5d aKey='%s'\n",
+// value.c_str(), isYes, ok, aDat.empty(), aKey.c_str() );
+ return ok;
+} // FlagOK
+
+
+
+bool FlagBoth( string aDat, string aKey )
+{
+ string value;
+ GetField( aDat, aKey, value );
+ return value=="both";
+} // FlagBoth
+
+
+
+void FilterFields( string &aDat, string aFilter )
+{
+ string s, removedValue;
+
+ cAppCharP qN;
+ cAppCharP qR;
+ cAppCharP q= aFilter.c_str();
+
+ while (*q) {
+ qN= strstr( q,"\n" );
+ if (!qN) qN= (cAppCharP)q + strlen( q );
+ qR= qN-1;
+ if (*qR!='\r') qR= qN;
+
+ s.assign( q, (unsigned int)( qR-q ) );
+ q= qN; if (*q) q++;
+
+ RemoveField( aDat, s, removedValue );
+ } // while
+} // FilterFields
+
+
+
+/* ---------- global context handling ------------------------ */
+bool GlobContextFound( string dbName, GlobContext* &g )
+{
+ while (g!=NULL) {
+ if (dbName==g->refName) break; // work is done
+
+ if (strcmp( g->refName,"" )==0) {
+ strcpy( g->refName, dbName.c_str() );
+ return false;
+ } // if
+
+ if (g->next==NULL) {
+ return false;
+ } // if
+
+ g= g->next;
+ } // while
+
+//printf( "ContextFound: %08X '%s'\n", g, dbName.c_str() );
+ return g!=NULL && g->ref!=NULL;
+} // GlobContextFound
+
+
+
+/* ---------- UI callback ------------------------------------ */
+/* Check, if <aCB> structure supports at least extended UI callback */
+static bool CB_UIX( void* aCB ) { return CB_OK( aCB,8 ); }
+
+
+TSyError UI_OpenKeyByPath( void* aCB, KeyH *aKeyH,
+ KeyH aParentKeyH, cAppCharP aPath, uInt16 aMode )
+{
+ TSyError err= LOCERR_NOTIMP;
+
+ DB_Callback cb= (DB_Callback)aCB;
+ if (CB_UIX( aCB )) {
+ err= cb->ui.OpenKeyByPath( cb->callbackRef, aKeyH, aParentKeyH, aPath, aMode );
+ DEBUG_DB( aCB, MyDB, "OpenKeyByPath", "%08X path='%s' mode=%04X err=%d",
+ *aKeyH, aPath, aMode, err );
+ } // if
+
+ return err;
+} // UI_OpenKeyByPath
+
+
+TSyError UI_CloseKey( void* aCB, KeyH aKeyH )
+{
+ TSyError err= LOCERR_NOTIMP;
+
+ DB_Callback cb= (DB_Callback)aCB;
+ if (CB_UIX( aCB )) {
+ err= cb->ui.CloseKey( cb->callbackRef, aKeyH );
+ DEBUG_DB( aCB, MyDB, "CloseKey", "%08X err=%d", aKeyH, err );
+ } // if
+
+ return err;
+} // UI_CloseKey
+
+
+
+#if defined __cplusplus
+ } // namespace */
+#endif
+
+
+/* eof */
diff --git a/src/sysync_SDK/Sources/SDK_support.h b/src/sysync_SDK/Sources/SDK_support.h
new file mode 100755
index 0000000..65a074f
--- /dev/null
+++ b/src/sysync_SDK/Sources/SDK_support.h
@@ -0,0 +1,238 @@
+/*
+ * File: SDK_support.h
+ *
+ * Author: Beat Forster (bfo@synthesis.ch)
+ *
+ * Some SDK support utility functions for C++
+ *
+ *
+ * Copyright (c) 2005-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ */
+
+#ifndef SDK_SUPPORT_H
+#define SDK_SUPPORT_H
+
+#include "sync_include.h" // import some global things
+#include <string>
+
+namespace sysync {
+
+
+// ---- Bracket (LIB) support
+/*! Returns true, if <name> belongs to an internal LIB.
+ * Returns false for DLL or JNI (Java Native Interface) access
+ */
+bool IsLib( string name );
+
+/*! Returns true, if <name> is within brackets: "[aaa]" */
+bool InBracks( string name );
+
+/*! Cut brackets, if at beginning and end
+ * Returns true, if <name> is within brackets: "[aaa]"
+ */
+bool CutBracks( string &name );
+
+/*! Cut the brackets at beginning/end of <name> */
+string NoBracks( string name );
+
+/*! Add brackets at beginning/end of <name>
+ * if not yet there and if not empty string
+ */
+string AddBracks( string name );
+
+
+/*! Get plug-in's main path of <name>, e.g. "aaa!bbb" ==> "aaa" */
+string Plugin_MainName( string name );
+
+/*! Get plug-in's sub path of <name>, e.g. "aaa!bbb" ==> "bbb" */
+string Plugin_SubName ( string name );
+
+/*! Get the two path's <sMain> and <sSub>
+ * Returns true, if sSub is not empty ""
+ */
+bool WithSubSystem ( string name, string &sMain, string &sSub );
+
+/*! Returns true if <aContext> names contains " ADMIN" at the end
+ * The " ADMIN" part will be ut, if available
+ */
+bool IsAdmin( string &aContextName );
+
+
+
+// ------ command line utility functions -------------------------------------
+class CLine
+{
+ public:
+ CLine() { fArgc= 0; }
+
+ void InitOptions( int argc, char* argv[] ); // assign command line arguments
+ void StrOpt ( char opt, int n, string &value ); // Get <n>th option string of <opt>
+ bool NextOpt ( char opt, int &n, string &value ); // Get next option, if available
+
+ void IntOpt ( char opt, int n, uInt32 &value );
+ void IntOpt ( char opt, int n, sInt32 &value );
+ void IntOpt ( char opt, int n, uInt16 &value );
+ void IntOpt ( char opt, int n, sInt16 &value );
+
+ bool Opt_Found ( char opt ); // check for a specific option
+
+ private:
+ int fArgc;
+ char** fArgv;
+}; // Cline
+
+
+
+// ---- Capabilities support --------------------------------------------------------
+/*! Add a fieldname with "no", "yes" or "both" attribute */
+void NoField( string &s, string fieldName );
+void YesField( string &s, string fieldName );
+void BothField( string &s, string fieldName );
+
+/*! Add the minimum supported version for the plugin to <s>
+ * NOTE: If \<internalMinVersion> is not specified or lower than the system's
+ * minimum version, the system's minimum version will be taken.
+ * If \<internalMinVersion> is higher, it will be directly returned.
+ */
+void MinVersion ( string &s, CVersion internalMinVersion= 0 );
+
+/*! Add sub system's version, if smaller than internal */
+void SubVersion ( string &s, CVersion subSysVersion );
+
+/*! Sub system's name; additionally this is a capability separator
+ * So all following capabilities belong to the sub system
+ */
+void SubSystem ( string &s, string subSysName );
+
+/*! Add the manufacturer's <name> to \<s> */
+void Manufacturer( string &s, string name );
+
+/*! Add a short description <desc>, what the module is doing to <s> */
+void Description ( string &s, string desc );
+
+/*! Add a <guid> string to <s> */
+void GuidStr ( string &s, string guidStr );
+
+/*! Add global context <gContext> information to <s> */
+void GContext ( string &s, GlobContext* gContext );
+
+/*! Add capability error */
+void CapaError ( string &s, TSyError err );
+
+
+
+// ---- utility functions ----------------------------------------------------------
+/* Allocate local memory for an item */
+void MapAlloc ( MapID rslt, cMapID mID );
+void ItemAlloc ( ItemID rslt, cItemID aID );
+void BlockAlloc( appPointer *newPtr, appPointer bPtr, uInt32 bSize );
+
+/*! Convert \<str> into \<rslt> string for Save/Load operations */
+void ReplaceSave( cAppCharP str, string &rslt );
+void ReplaceLoad( cAppCharP str, string &rslt );
+
+/*! Get int value <i> as string */
+string IntStr( sInt32 i );
+
+/*! Get ptr address value <a> as string */
+string RefStr( void* a, bool asHex= true );
+
+/*! Get byte/hex/longhex value <h> as string */
+string ByteStr( uInt8 h );
+string HexStr( uInt16 h );
+string LHexStr( uInt32 h );
+
+/*! Get <ch> as int value */
+int HexNr( char ch );
+
+/*! Get string as hex value */
+void* LHexRef( string v );
+long LHex ( string v );
+
+/*! Get boolean as const char string */
+cAppCharP Bo ( bool b );
+
+/*! Get boolean as short const char string "v"=true / "o"=false */
+cAppCharP BoS( bool b );
+
+/*! Get <v> as version string Va.b.c.d */
+string VersionStr( CVersion v );
+
+/*! Get <v> as version long 0x0a0b0c0d */
+CVersion VersionNr ( string v );
+
+
+/* true, if <s> starts/ends with <cmp> */
+bool SameBegin( string s, string cmp );
+bool SameEnd ( string s, string cmp );
+
+
+/*! Get a string within single quotes */
+string Apo ( string s );
+
+/*! Get a string within parans */
+string Parans ( string s );
+
+/*! Get a string within brackets */
+string Bracks ( string s );
+
+/*! Token separation */
+void CutCh ( string &s );
+void CutLSP ( string &s );
+bool NextToken( string &s, string &nx, string sep=" " );
+
+
+/*! Display a line with <maxLen> and removed '\n' */
+string LineConv( string str, uInt32 maxLen= 0, bool visibleN= false );
+
+
+/*! Get (\<mID.localID>,\<mID.remoteID> \<mID.flags> \<mID.ident>) string */
+string MapID_Info ( cMapID mID );
+
+/*! Get (\<aID.item>,\<aID.parent>) string */
+string ItemID_Info( cItemID aID, string aName= "" );
+
+/*! Concatenate <path1>,<path2>,[<path3>] with OS specific separator in-between */
+string ConcatPaths( string path1, string path2, bool isJava= false );
+string ConcatPaths( string path1, string path2, string path3, bool isJava= false );
+
+/*! Notation: <name1>"!"<name2> */
+string ConcatNames( string name, string subName, string sep= "!" );
+
+
+
+// -----------------------------------------------------------------------------------
+/*! Get <aKey>/<aNth> field from <aDat>. <value> will be returned */
+bool GetField ( string aDat, string aKey, string &value );
+
+/*! Remove <aKey> field from <aDat>. <removedValue> will be returned */
+bool RemoveField( string &aDat, string aKey, string &removedValue );
+
+/*! Check if <aKey> is - "yes"/"true/"both" ( <isYes> = true )
+ * - not available or NOT "no"/"false" ( else )
+ */
+bool FlagOK ( string aDat, string aKey, bool isYes= false );
+
+/*! Check if <aKey> is "both" */
+bool FlagBoth( string aDat, string aKey );
+
+/*! filter out <aFilter> item fields from <aItemData>. Result is <aDat> */
+void FilterFields( string &aDat, string aFilter );
+
+
+
+/* ---------- global context handling ------------------------ */
+bool GlobContextFound( string dbName, GlobContext* &g );
+
+
+
+/* ---------- UI callback ------------------------------------ */
+TSyError UI_OpenKeyByPath( void* aCB, KeyH *aKeyH,
+ KeyH aParentKeyH, cAppCharP aPath, uInt16 aMode );
+TSyError UI_CloseKey ( void* aCB, KeyH aKeyH );
+
+
+} // namespace
+#endif /* SDK_SUPPORT */
+/* eof */
diff --git a/src/sysync_SDK/Sources/SDK_util.c b/src/sysync_SDK/Sources/SDK_util.c
new file mode 100755
index 0000000..a7c8e6f
--- /dev/null
+++ b/src/sysync_SDK/Sources/SDK_util.c
@@ -0,0 +1,781 @@
+/*
+ * File: SDK_util.c
+ *
+ * Authors: Beat Forster
+ *
+ *
+ * SDK utility functions for
+ * - version handling
+ * - string alloc/dispose
+ * - debug/callback
+ *
+ * written in Std C, can be used in C++ as well
+ *
+ * Copyright (c) 2004-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ */
+
+
+#include "sync_dbapidef.h"
+#include "SDK_util.h"
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifndef __MACH__
+#include <malloc.h>
+#endif
+
+#define MyDB "SDK" /* local debug name */
+#define SDKversionMask 0xffff00ff /* Old mask for version comparison: Omit OS identifier */
+#define maxmsglen 1024 /* Maximum string length for callback string */
+
+
+
+/* This StdC module is independent from namespace "sysync" */
+
+/* Get the plug-in's version/subversion number
+ * The plugin will contain a user defined <buildNumber> 0..255
+ * This version number is defined by Synthesis and should not be changed:
+ * The engine contains also a version number, and makes some comparisons.
+ */
+CVersion Plugin_Version( short buildNumber )
+{
+ #define P 256
+ long v;
+
+ #define SDK_VERSION_MAJOR 1 /* Release: V1.5.2, change this if you need troubles */
+ #define SDK_VERSION_MINOR 5
+ #define SDK_SUBVERSION 2
+
+ /* allowed range for the local build number */
+ if (buildNumber< 0) buildNumber= 0;
+ if (buildNumber>255) buildNumber= 255;
+
+ v= ((SDK_VERSION_MAJOR *P +
+ SDK_VERSION_MINOR)*P +
+ SDK_SUBVERSION )*P +
+ buildNumber;
+
+ return v;
+} /* Plugin_Version */
+
+
+
+/* Check, if <version_feature> is supported in <currentVersion>
+ * NOTE: For the SyncML engine internally everything is supported
+ * This will be reflected with the fact that for 2.1.1.X
+ * the engine's version was always much much higher.
+ * Today the engine's version is equivalent to the SDK version.
+ */
+bool Feature_Supported ( CVersion versionFeature, CVersion currentVersion )
+{
+ CVersion v= currentVersion;
+ if (v<VP_NewBuildNumber) v= v & SDKversionMask; /* avoid OS identifier comparison */
+ return v>=versionFeature;
+} /* FeatureSupported */
+
+
+/* Check, if <version_feature> is equivalent to <currentVersion>
+ */
+bool Feature_SupportedEq( CVersion versionFeature, CVersion currentVersion )
+{
+ CVersion v= currentVersion;
+ if (v<VP_NewBuildNumber) v= v & SDKversionMask; /* avoid OS identifier comparison */
+ return v==versionFeature;
+} /* FeatureSupportedEq */
+
+
+
+/* Get platform identifier */
+cAppCharP MyPlatform( void )
+{
+ cAppCharP p;
+
+ #if defined MACOSX
+ #if defined IPHONEOS
+ p= "PLATFORM:iPhone"; /* there is ONLY XCode, no need to mention that here */
+ #elif defined __MWERKS__
+ p= "PLATFORM:Mac (CodeWarrior)";
+ #elif defined __GNUC__
+ p= "PLATFORM:Mac (XCode)";
+ #else
+ p= "PLATFORM:Mac";
+ #endif
+
+ #elif defined _WIN32
+ #ifdef __MWERKS__
+ p= "PLATFORM:Windows (CodeWarrior)";
+ #elif defined _MSC_VER
+ p= "PLATFORM:Windows (VisualStudio)";
+ #else
+ p= "PLATFORM:Windows";
+ #endif
+
+ #elif defined LINUX
+ p= "PLATFORM:Linux";
+
+/* elif **** for JAVA defined at the Java code ******** */
+/* p= "PLATFORM:Java" */
+
+/* elif **** for .net defined at the C# code ******** */
+/* p= "PLATFORM:C#" */
+
+ #else
+ p= "PLATFORM:unknown";
+ #endif
+
+ return p;
+} /* MyPlatform */
+
+
+
+/* -------------------- string allocation/deallocation -------- */
+/* Allocate local memory for a string */
+appCharP StrAllocN( cAppCharP s, int n, bool fullSize )
+{
+ char* cp;
+ int len;
+
+ if (!fullSize) {
+ len= strlen( s );
+ if (n>len) n= len; /* never more than the length of <s> */
+ } /* if */
+
+ cp= (char*)malloc( n+1 );
+ strncpy( cp, s, n ); /* not yet NUL terminated !! */
+ cp[ n ]= '\0';
+ return cp;
+} /* StrAllocN */
+
+
+/* Allocate local memory for a string */
+appCharP StrAlloc ( cAppCharP s ) {
+ return StrAllocN( s, strlen( s ), true );
+} /* StrAlloc */
+
+
+/* !Dispose a string which has been allocated with 'StrAlloc' */
+void StrDispose( void* s ) {
+ if (s!=NULL) free( s );
+} /* StrDispose */
+
+
+
+/* ------------------ field operations ------------------------- */
+bool Field( cAppCharP item, cAppCharP key, char** field )
+{
+ char* b;
+ char* e;
+ char* t= (char*)item;
+
+ *field= NULL;
+ while (true) {
+ b= strstr( t,key ); if (!b) break; /* <key> available ? */
+ e= strstr( b,"\r" );
+ if (!e) e= strstr( b,"\n" ); /* get the end of this field */
+
+ if (b==item || *(b-1)=='\r'
+ || *(b-1)=='\n') {
+ b+= strlen( key )+1;
+
+ if (*(b-1)==':') { /* correctly separated ? */
+ if (!e) *field= StrAlloc ( b ); /* either the rest */
+ else *field= StrAllocN( b, e-b, false ); /* or till next field */
+ return true;
+ } /* if */
+
+ if (!e) break;
+ } /* if */
+
+ t= e+1; /* go to the next field */
+ } /* loop */
+
+ return false;
+} /* Field */
+
+
+
+bool SameField( cAppCharP item, cAppCharP key, cAppCharP field )
+{
+ char* vv;
+ bool ok= Field( item, key, &vv ); /* get the <key> field */
+ if (ok) {
+ ok= strcmp( field,vv )==0; /* and compare it with <field> */
+ StrDispose ( vv ); /* <vv> is no longer used now */
+ } /* if */
+
+ return ok;
+} /* SameField */
+
+
+
+/* ------------------ callback system -------------------------------------- */
+/* Initialize to safe defaults, useable for Std C as well
+ * NOTE: The current = latest available <callbackVersion> will be taken
+ */
+static bool BadCB( void* aCallbackRef )
+{
+ DB_Callback cb= aCallbackRef;
+
+ if (cb==NULL) {
+ printf( "bad cb==NULL\n" ); return true;
+ } /* if */
+
+ if (cb->callbackRef!=cb) {
+ printf( "bad callbackRef %08lX <> %08lX\n",
+ (unsigned long)cb->callbackRef,
+ (unsigned long)cb ); return true;
+ } /* if */
+
+ return false; /* false= it's ok */
+} /* BadCB */
+
+
+void NBlk( void* aCallbackRef )
+{
+ int i;
+ DB_Callback cb= aCallbackRef;
+ if (!CB_OK( cb,2 )) return;
+/*if ( BadCB( aCallbackRef )) return;*/
+
+ for (i=0; i<cb->lCount; i++) printf( " " );
+} /* NBlk */
+
+
+
+static void BeginBlk( void* aCallbackRef, cAppCharP aTag,
+ cAppCharP aDesc,
+ cAppCharP aAttrText )
+{
+ DB_Callback cb= aCallbackRef;
+ if ( BadCB( aCallbackRef )) return;
+
+ NBlk( cb ); printf( "<%s> %s %s\n", aTag, aDesc, aAttrText );
+ cb->lCount++;
+} /* BeginBlk */
+
+
+
+static void EndBlk( void* aCallbackRef, cAppCharP aTag )
+{
+ DB_Callback cb= aCallbackRef;
+ if (!CB_OK( cb,2 )) return;
+/*if ( BadCB( aCallbackRef )) return;*/
+
+ cb->lCount--;
+ NBlk( cb ); printf( "</%s>\n", aTag );
+} /* EndBlk */
+
+
+
+static void EndThread( void* aCallbackRef )
+{
+ DB_Callback cb= aCallbackRef;
+ if (!CB_OK( cb,2 )) return;
+/*if ( BadCB( aCallbackRef )) return;*/
+
+ NBlk( cb ); printf( "=EndThread=\n" );
+} /* EndThread */
+
+
+
+/* -------------------------------------------------------------------- */
+void InitCallback( void* aCB, uInt16 aVersion, void* aRoutine, void* aExoticRoutine )
+{
+ DB_Callback cb= aCB;
+ if (!cb) return;
+
+ cb->callbackVersion= aVersion;
+ cb->callbackRef = cb; /* the callback pointer is the cb itself here */
+
+ if (CB_OK( cb,1 )) {
+ cb->debugFlags = 0; /* debug disable so far */
+ cb->DB_DebugPuts = aRoutine;
+ if (aRoutine) {
+ if (aExoticRoutine) cb->debugFlags= DBG_PLUGIN_ALL;
+ else cb->debugFlags= DBG_PLUGIN_INT + DBG_PLUGIN_DB;
+ } /* if */
+
+ cb->cContext= 0; /* contexts */
+ cb->mContext= 0;
+ cb->sContext= 0;
+ } /* if */
+
+ if (CB_OK( cb,2 )) {
+ cb->DB_DebugBlock = BeginBlk; /* level 2 */
+ cb->DB_DebugEndBlock = EndBlk;
+ cb->DB_DebugEndThread= EndThread;
+ cb->lCount = 0;
+ } /* if */
+
+ if (CB_OK( cb,3 ))
+ cb->DB_DebugExotic = aExoticRoutine; /* level 3 */
+
+ if (CB_OK( cb,4 ))
+ cb->allow_DLL_legacy= 0; /* false */ /* level 4 */
+
+ if (CB_OK( cb,5 ))
+ cb->allow_DLL = 0; /* false */ /* level 5 */
+
+ if (CB_OK( cb,6 )) {
+ cb->reserved1= 0; /* level 6 */
+ cb->reserved2= 0;
+ cb->reserved3= 0;
+ cb->reserved4= 0;
+ } /* if */
+
+ if (CB_OK( cb,7 )) {
+ cb->thisCB = cb; /* level 7 */
+ cb->logCount= 0;
+ } /* if */
+
+ if (CB_OK( cb,8 )) {
+ cb->thisBase = NULL; /* level 8 */
+ cb->jRef = NULL;
+ cb->gContext = 0;
+
+ cb->ui.SetStringMode = NULL;
+ cb->ui.InitEngineXML = NULL;
+ cb->ui.InitEngineFile = NULL;
+ cb->ui.InitEngineCB = NULL;
+
+ cb->ui.OpenSession = NULL;
+ cb->ui.OpenSessionKey = NULL;
+ cb->ui.SessionStep = NULL;
+ cb->ui.GetSyncMLBuffer = NULL;
+ cb->ui.RetSyncMLBuffer = NULL;
+ cb->ui.ReadSyncMLBuffer = NULL;
+ cb->ui.WriteSyncMLBuffer= NULL;
+ cb->ui.CloseSession = NULL;
+
+ cb->ui.OpenKeyByPath = NULL;
+ cb->ui.OpenSubkey = NULL;
+ cb->ui.DeleteSubkey = NULL;
+ cb->ui.GetKeyID = NULL;
+ cb->ui.SetTextMode = NULL;
+ cb->ui.SetTimeMode = NULL;
+ cb->ui.CloseKey = NULL;
+
+ cb->ui.GetValue = NULL;
+ cb->ui.GetValueByID = NULL;
+ cb->ui.GetValueID = NULL;
+ cb->ui.SetValue = NULL;
+ cb->ui.SetValueByID = NULL;
+ } /* if */
+
+ if (CB_OK( cb,9 )) {
+ cb->SDK_Interface_size= sizeof(SDK_Interface_Struct); /* level 9 */
+
+ cb->dt.StartDataRead = NULL; /* tunnel callback functions, for internal use only */
+ cb->dt.ReadNextItem = NULL;
+ cb->dt.ReadItem = NULL;
+ cb->dt.EndDataRead = NULL;
+
+ cb->dt.StartDataWrite = NULL;
+ cb->dt.InsertItem = NULL;
+ cb->dt.UpdateItem = NULL;
+ cb->dt.MoveItem = NULL;
+ cb->dt.DeleteItem = NULL;
+ cb->dt.EndDataWrite = NULL;
+ } /* if */
+
+ if (CB_OK( cb,11 )) {
+ cb->dt.DisposeObj = NULL;
+
+ cb->dt.ReadNextItemAsKey= NULL;
+ cb->dt.ReadItemAsKey = NULL;
+ cb->dt.InsertItemAsKey = NULL;
+ cb->dt.UpdateItemAsKey = NULL;
+ } /* if */
+} /* InitCallback */
+
+
+
+/* Initialize to safe defaults and "CB_PurePrintf", usable for Std C as well */
+/* Normal debug output */
+void InitCallback_Pure ( void* aCB, uInt16 aVersion ) {
+ InitCallback ( aCB, aVersion, CB_PurePrintf, NULL );
+} /* InitCallback_Pure */
+
+
+/* Initialize to safe defaults and "CB_PurePrintf", usable for Std C as well */
+/* Normal and exotic debug output */
+void InitCallback_Exotic( void* aCB, uInt16 aVersion ) {
+ InitCallback ( aCB, aVersion, CB_PurePrintf, CB_PurePrintf );
+} /* InitCallback_Exotic */
+
+
+
+/* The <aRoutine> for 'InitCallback', when a simple "printf" is
+ * requested for the callback.
+ *
+ * Routine must be defined as
+ * typedef void (*DB_DebugPuts_Func)( void *aCallbackRef, cAppCharP aText );
+ */
+void CB_PurePrintf( void* aCB, cAppCharP aText )
+{
+ DB_Callback cb= aCB;
+ if (!CB_OK( cb,2 )) return;
+/*if ( BadCB( aCB )) return;*/
+
+ NBlk( cb ); printf( "%s\n", aText );
+} /* CB_PurePrintf */
+
+
+
+/* Get the size of the SDK_Interface_Struct to be copied */
+TSyError SDK_Size( void* aCB, uInt32 *sSize )
+{
+ TSyError err= LOCERR_OK;
+ DB_Callback cb = aCB;
+
+ do {
+ /* try to get the SDK_Interface_Struct <sSize> of the calling engine */
+ if (cb->callbackVersion< SDK_Interface_Struct_V8) { err= LOCERR_TOOOLD; break; }
+ if (cb->callbackVersion==SDK_Interface_Struct_V8) *sSize= SDK_Interface_Struct_V8_Size;
+ else *sSize= cb->SDK_Interface_size;
+
+ /* but it must neither be smaller than version 8 size nor larger than the own size */
+ if (*sSize< SDK_Interface_Struct_V8_Size) { err= LOCERR_TOOOLD; break; }
+ if (*sSize>sizeof(SDK_Interface_Struct)) *sSize= sizeof(SDK_Interface_Struct);
+ } while (false);
+
+/*printf( "sizeof=%d sSize=%d err=%d\n", sizeof(SDK_Interface_Struct), *sSize, err );*/
+ return err;
+} /* SDK_Size */
+
+
+
+/* ---------- debug output ----------------------------------- */
+/* prints directly to the screen */
+static void ConsolePuts( char* msg ) { printf( "%s\n", msg ); }
+
+#if !defined(SYSYNC_ENGINE) && !defined(UIAPI_LINKED)
+ /* the Synthesis SyncML engine has its own implementation */
+ /* => use this code in SDK only */
+ void ConsoleVPrintf( cAppCharP format, va_list args )
+ {
+ #ifdef SYDEBUG
+ char msg[ maxmsglen ];
+ msg[ 0 ]= '\0'; /* start with an empty <msg> */
+ vsnprintf ( msg, maxmsglen,format,args ); /* assemble the message string */
+ ConsolePuts( msg ); /* write the string */
+ #endif
+ } /* ConsoleVPrintf */
+
+ void ConsolePrintf( cAppCharP text, ... )
+ {
+ va_list args;
+ va_start ( args,text );
+ ConsoleVPrintf( text,args );
+ va_end ( args );
+ } /* ConsolePrintf */
+#endif
+
+
+/* Get the callback version of <aCB> */
+uInt16 CB_Version( void* aCB )
+{
+ DB_Callback cb= aCB;
+ if (cb) return cb->callbackVersion;
+ else return 0;
+} /* CB_Version */
+
+
+/* Check, if <aCB> structure is at least <minVersion> */
+bool CB_OK( void* aCB, uInt16 minVersion ) { return CB_Version( aCB )>=minVersion; }
+
+/* Check, if <aCB> structure supports UI callback (cbVersion>=6) */
+/* static bool CB_UI( void* aCB ) { return CB_OK( aCB,6 ); } */
+
+/* Check, if <aCB> structure supports <gContext> (cbVersion>=8) and <gContext> <> 0 */
+bool CB_gContext ( void* aCB )
+{
+ DB_Callback cb= aCB;
+ return CB_OK ( aCB,8 ) && cb->gContext!=0;
+} /* CB_gContext */
+
+
+/* Check, if <aCB> structure supports CA_SubSytem (cbVersion>=10) */
+bool CB_SubSystem( void* aCB ) { return CB_OK( aCB,10 ); }
+
+
+/* Check, if <aCB> structure is at least <minVersion> and DBG_PLUGIN_DB is set */
+bool DB_OK( void* aCB, uInt16 minVersion )
+{
+ DB_Callback cb= aCB; /* at least one flag must be set */
+ return CB_OK( aCB,minVersion ) && (DBG_PLUGIN_DB & cb->debugFlags)!=DBG_PLUGIN_NONE;
+} /* DB_OK */
+
+
+/* Check, if <aCB> structure is at least <minVersion> and one of <debugFlags> is set */
+bool Callback_OK( void* aCB, uInt16 minVersion, uInt16 debugFlags )
+{
+ DB_Callback cb= aCB; /* at least one flag must be set */
+ return CB_OK( aCB,minVersion ) && (debugFlags & cb->debugFlags)!=DBG_PLUGIN_NONE;
+} /* Callback_OK */
+
+
+
+/* Normal callback output of <text> */
+void CallbackPuts( void* aCB, cAppCharP text )
+{
+ DB_Callback cb= aCB;
+ if (!Callback_OK( aCB, 1,DBG_PLUGIN_ALL )) return;
+
+ if (cb->DB_DebugPuts)
+ cb->DB_DebugPuts( cb->callbackRef, text );
+} /* CallbackPuts */
+
+
+
+/* Exotic callback output of <text> */
+static void CallbackExotic( void* aCB, cAppCharP text )
+{
+ DB_Callback cb= aCB;
+ if (!Callback_OK( aCB, 3,DBG_PLUGIN_ALL )) { CallbackPuts( aCB,text ); return; }
+
+ if (cb->DB_DebugExotic)
+ cb->DB_DebugExotic( cb->callbackRef, text );
+} /* CallbackExotic */
+
+
+
+static void CallbackVPrintf( DB_Callback aCB, cAppCharP format, va_list args, uInt16 outputMode )
+{
+ #ifdef SYDEBUG
+ char message[ maxmsglen ];
+ message[ 0 ]= '\0'; /* start with an empty <msg> */
+ vsnprintf( message, maxmsglen, format,args ); /* assemble the message string */
+
+ switch (outputMode) {
+ case OutputNorm : CallbackPuts ( aCB, message ); break;
+ /* case OutputExoticBefore: */
+ /* case OutputExoticAfter : */
+ case OutputExotic : CallbackExotic( aCB, message ); break;
+ case OutputConsole :
+ case OutputExoticBefore:
+ case OutputExoticAfter :
+ case OutputBefore :
+ case OutputAfter : NBlk ( (void*)aCB );
+ ConsolePuts ( message ); break;
+ } /* switch */
+ #endif
+} /* CallbackVPrintf */
+
+
+
+void DEBUG_( void* aCB, cAppCharP text, ... )
+{
+ #ifdef SYDEBUG
+ va_list args;
+
+ if (Callback_OK( aCB, 1,DBG_PLUGIN_ALL )) {
+ va_start ( args,text );
+ CallbackVPrintf( aCB, text,args, false );
+ va_end ( args );
+ } /* if (gDebug) */
+ #endif
+} /* DEBUG_ */
+
+
+
+#ifdef SYDEBUG
+void DoDEBUG( void* aCB, uInt16 outputMode, bool withIntro,
+ cAppCharP ident,
+ cAppCharP routine, va_list args,
+ cAppCharP text )
+{
+ cAppCharP dbIntro= "";
+ cAppCharP id = "";
+ cAppCharP isX = "";
+ cAppCharP p = "";
+ char* s;
+ int size;
+
+ if (*routine!='\0') {
+ if (withIntro) { dbIntro= "##### ";
+ if (outputMode==OutputBefore ||
+ outputMode==OutputExoticBefore) dbIntro= ">>>>> ";
+ if (outputMode==OutputAfter ||
+ outputMode==OutputExoticAfter) dbIntro= "<<<<< ";
+ } /* if */
+
+ id= ident;
+
+ #if defined SDK_LIB
+ isX= ": ";
+ #elif defined SDK_DLL
+ isX= " (DLL): ";
+ #else
+ isX= " (LNK): ";
+ #endif
+
+ if (*text!='\0') p= ": ";
+ } /* if */
+
+ size= strlen(dbIntro) + strlen(id) +
+ strlen(isX) + strlen(routine) +
+ strlen(p) + strlen(text) + 1;
+
+ s= (char*)malloc( size );
+ sprintf( s, "%s%s%s%s%s%s", dbIntro,id,isX,routine,p,text );
+
+ CallbackVPrintf( aCB, s,args, outputMode );
+
+ free ( s );
+} /* DoDEBUG */
+#endif
+
+
+
+/* ------------------------------------------------------------------------------------- */
+void DEBUG_Call( void* aCB, uInt16 debugFlags,
+ cAppCharP ident,
+ cAppCharP routine,
+ cAppCharP text, ... )
+{
+ #ifdef SYDEBUG
+ va_list args;
+
+ if (Callback_OK( aCB, 1,debugFlags )) {
+ va_start ( args,text );
+ DoDEBUG( aCB, false,true, ident,routine, args,text );
+ va_end ( args );
+ } /* if */
+ #endif
+} /* DEBUG_Call */
+
+
+
+void DEBUG_INT( void* aCB, cAppCharP ident,
+ cAppCharP routine,
+ cAppCharP text, ... )
+{
+ #ifdef SYDEBUG
+ va_list args;
+ if (Callback_OK( aCB, 1,DBG_PLUGIN_INT )) {
+ va_start ( args,text );
+ DoDEBUG( aCB, false,true, ident,routine, args,text );
+ va_end ( args );
+ } /* if */
+ #endif
+} /* DEBUG_INT */
+
+
+
+void DEBUG_DB( void* aCB, cAppCharP ident,
+ cAppCharP routine,
+ cAppCharP text, ... )
+{
+ #ifdef SYDEBUG
+ va_list args;
+ if (Callback_OK( aCB, 1,DBG_PLUGIN_DB )) {
+ va_start ( args,text );
+ DoDEBUG( aCB, false,true, ident,routine, args,text );
+ va_end ( args );
+ } /* if */
+ #endif
+} /* DEBUG_DB */
+
+
+
+/* ------------------------------------------------------------------------------------- */
+void DEBUG_Exotic_INT( void* aCB, cAppCharP ident,
+ cAppCharP routine,
+ cAppCharP text, ... )
+{
+ #ifdef SYDEBUG
+ va_list args;
+ if (Callback_OK( aCB, 1,DBG_PLUGIN_INT ) &&
+ Callback_OK( aCB, 1,DBG_PLUGIN_EXOT )) {
+ va_start ( args,text );
+ DoDEBUG( aCB, true,true, ident,routine, args,text );
+ va_end ( args );
+ } /* if */
+ #endif
+} /* DEBUG_Exotic_INT */
+
+
+
+void DEBUG_Exotic_DB( void* aCB, cAppCharP ident,
+ cAppCharP routine,
+ cAppCharP text, ... )
+{
+ #ifdef SYDEBUG
+ va_list args;
+ if (Callback_OK( aCB, 1,DBG_PLUGIN_EXOT )) {
+ va_start ( args,text );
+ DoDEBUG( aCB, true,true, ident,routine, args,text );
+ va_end ( args );
+ } /* if */
+ #endif
+} /* DEBUG_Exotic_DB */
+
+
+
+void DEBUG_Exotic_DBW( void* aCB, cAppCharP ident,
+ cAppCharP routine,
+ cAppCharP text, ... )
+{
+ #ifdef SYDEBUG
+ va_list args;
+ if (Callback_OK( aCB, 1,DBG_PLUGIN_EXOT )) {
+ va_start ( args,text );
+ DoDEBUG( aCB, true,false,ident,routine, args,text );
+ va_end ( args );
+ } /* if */
+ #endif
+} /* DEBUG_Exotic_DBW */
+
+
+
+/* ------------------------------------------------------------------------------------- */
+/* Start of sub block */
+void DEBUG_Block( void* aCB, cAppCharP aTag,
+ cAppCharP aDesc,
+ cAppCharP aAttrText )
+{
+ /* callbackVersion >= 2 support for blocks */
+ #ifdef SYDEBUG
+ DB_Callback cb= aCB;
+ if (Callback_OK( aCB, 2,DBG_PLUGIN_ALL ) &&
+ cb->DB_DebugBlock)
+ cb->DB_DebugBlock( cb->callbackRef, aTag,aDesc,aAttrText );
+ else /* old */
+ DEBUG_DB( aCB, MyDB, aTag, "%s (%s) BEGIN", aAttrText,aDesc );
+ #endif
+} /* DEBUG_Block */
+
+
+
+/* End of sub block */
+void DEBUG_EndBlock( void* aCB, cAppCharP aTag )
+{
+ #ifdef SYDEBUG
+ DB_Callback cb= aCB;
+ if (Callback_OK( aCB, 2,DBG_PLUGIN_ALL ) &&
+ cb->DB_DebugEndBlock)
+ cb->DB_DebugEndBlock( cb->callbackRef, aTag );
+ else /* old */
+ DEBUG_DB( aCB, MyDB, aTag, "END" );
+ #endif
+} /* DEBUG_EndBlock */
+
+
+
+void DEBUG_EndThread( void* aCB )
+{
+ #ifdef SYDEBUG
+ DB_Callback cb= aCB;
+ if (Callback_OK( aCB, 2,DBG_PLUGIN_ALL ) &&
+ cb->DB_DebugEndThread)
+ cb->DB_DebugEndThread( cb->callbackRef );
+ else /* old */
+ DEBUG_DB( aCB, MyDB, "THREAD", "END" );
+ #endif
+} /* DEBUG_EndThread */
+
+
+/* eof */
diff --git a/src/sysync_SDK/Sources/SDK_util.h b/src/sysync_SDK/Sources/SDK_util.h
new file mode 100755
index 0000000..5efd33a
--- /dev/null
+++ b/src/sysync_SDK/Sources/SDK_util.h
@@ -0,0 +1,197 @@
+/*
+ * File: SDK_util.h
+ *
+ * Authors: Beat Forster
+ *
+ *
+ * Useful SDK utility functions
+ *
+ * Copyright (c) 2004-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ */
+
+
+#ifndef SDK_UTIL_H
+#define SDK_UTIL_H
+
+
+#include "sync_include.h"
+
+
+/* C/C++ and DLL/library support */
+#ifdef __cplusplus
+ extern "C" {
+
+ using sysync::uInt16;
+ using sysync::uInt32;
+ using sysync::appCharP;
+ using sysync::cAppCharP;
+ using sysync::appPointer;
+ using sysync::CVersion;
+ using sysync::TSyError;
+#endif
+
+#if defined SYSYNC_ENGINE || defined PLUGIN_INFO
+ #define DLL_Info "DLL:false"
+#else
+ #define DLL_Info "DLL:true"
+#endif
+
+
+/*! Get the current version of the plug-in module.
+ * The returned value will be incremented in future releases of the SDK.
+ * Not to be changed by plug-in developer.
+ * A customer defined <buildNumber> can be set (0..255).
+ */
+CVersion Plugin_Version( short buildNumber );
+
+
+/*! Check, if <version_feature> is supported in <currentVersion>
+ * NOTE: For the SyncML engine internally everything is supported
+ * This will be reflected with the fact that for 2.1.1.X
+ * the engine's version was always much much higher.
+ * Today the engine's version is equivalent to the SDK version.
+ */
+bool Feature_Supported ( CVersion versionFeature, CVersion currentVersion );
+
+/*! Check, if <version_feature> is equivalent to <currentVersion> */
+bool Feature_SupportedEq( CVersion versionFeature, CVersion currentVersion );
+
+
+/*! Get the "PLATFORM:xxx" string as const definition */
+cAppCharP MyPlatform( void );
+
+
+/* ---------- allocate/dispose local memory for a string ----- */
+/*! Allocate a string locally, with length \<n> */
+appCharP StrAllocN ( cAppCharP s, int n, bool fullSize );
+
+/*! Allocate a string locally */
+appCharP StrAlloc ( cAppCharP s );
+
+/* !Dispose a string which has been allocated with 'StrAlloc' */
+void StrDispose( void* s );
+
+
+
+
+/* ---------------- field operations ------------------------- */
+/*! Gets the <field> of <key> at <item>, if available */
+bool Field ( cAppCharP item, cAppCharP key, appCharP *field );
+
+/*! Returns true, if <item>'s <key> field is <field>. */
+bool SameField( cAppCharP item, cAppCharP key, cAppCharP field );
+
+
+
+/* ---------- callback setup (for internal use only) --------- */
+/*! Initialize the callback structure for debug output
+ * For internal use only !!
+ */
+void NBlk ( void* aCB );
+void InitCallback ( void* aCB, uInt16 aVersion, appPointer aRoutine,
+ appPointer aExoticRoutine );
+
+/*! Using 'CB_PurePrintf' as <aRoutine>; no overloading possible with Std C */
+void InitCallback_Pure ( void* aCB, uInt16 aVersion );
+void InitCallback_Exotic( void* aCB, uInt16 aVersion ); /* plus exotic output */
+
+
+/*! If simple "printf" is requested for the callback.
+ * 'CB_PurePrintf' can be given as <aRoutine> for 'InitCallback.
+ * For internal use only !!
+ */
+void CB_PurePrintf( void* aRef, cAppCharP aTxt );
+
+
+/* Get the size of the SDK_Interface_Struct to be copied */
+TSyError SDK_Size ( void* aCB, uInt32 *sSize );
+
+
+
+/* ---------- debug output ----------------------------------- */
+#ifndef SYSYNC_ENGINE
+ /* if it is running standalone as DLL */
+ /* the Synthesis SyncML engine has its own implementation */
+ /* => use this code in SDK only */
+ void ConsoleVPrintf( cAppCharP format, va_list args );
+ void ConsolePrintf ( cAppCharP text, ... );
+#endif
+
+/* Output modes */
+#define OutputNorm 0
+#define OutputExotic 1
+#define OutputConsole 2
+#define OutputBefore 3
+#define OutputAfter 4
+#define OutputExoticBefore 5
+#define OutputExoticAfter 6
+
+
+/* Get the callback version of <aCB> */
+uInt16 CB_Version( void* aCB );
+
+/* Check, if <aCB> structure is at least <minVersion> */
+bool CB_OK ( void* aCB, uInt16 minVersion );
+
+/* Check, if <aCB> structure supports <gContext> (cbVersion>=8) and <gContext> <> 0 */
+bool CB_gContext ( void* aCB );
+
+/* Check, if <aCB> structure supports CA_SubSytem (cbVersion>=10) */
+bool CB_SubSystem( void* aCB );
+
+/* Check, if <aCB> structure is at least <minVersion> and DBG_PLUGIN_DB is set */
+bool DB_OK ( void* aCB, uInt16 minVersion );
+
+/* Check, if <aCB> structure is at least <minVersion> and one of <debugFlags> is set */
+bool Callback_OK ( void* aCB, uInt16 minVersion, uInt16 debugFlags );
+
+void CallbackPuts( void* aCB, cAppCharP text );
+void DEBUG_ ( void* aCB, cAppCharP text, ... );
+
+void DoDEBUG ( void* aCB, uInt16 outputMode, bool withIdent,
+ cAppCharP ident,
+ cAppCharP routine, va_list args, cAppCharP text );
+
+
+/*! The debug logging callback call
+ * The SyncML engine will write the text to the context assigned log file
+ *
+ * Input: <aCB> : Callback variable of the context. The SyncML engine will
+ * pass such a reference, when creating the module, session
+ * or datastore context. The plug-in must store it within its
+ * context for later use.
+ * <debugFlags>: Debug logging flags, bits0/1 are reserved, all others can be
+ * defined plugin specific
+ * <ident> : Normally the name of the plug-in module, usually defined as 'MyDB'
+ * <routine>: The name of the current routine for reference
+ * <text> : The logging text in "printf" format.
+ * ... : The parameters, defined at \<text> with %s, %d, %X ...
+ */
+void DEBUG_Call ( void* aCB, uInt16 debugFlags,
+ cAppCharP ident, cAppCharP routine, cAppCharP text, ... );
+
+/*! The same as DEBUG_Call, but only when specific DBG_PLUGIN_INT/DBG_PLUGIN_DB flag is set */
+void DEBUG_INT ( void* aCB, cAppCharP ident, cAppCharP routine, cAppCharP text, ... );
+void DEBUG_DB ( void* aCB, cAppCharP ident, cAppCharP routine, cAppCharP text, ... );
+
+/*! The same as DEBUG_Call/DEBUG_INT/DEBUG/DEBUG_DB, but only, if exotic flag is set */
+void DEBUG_Exotic_INT ( void* aCB, cAppCharP ident, cAppCharP routine, cAppCharP text, ... );
+void DEBUG_Exotic_DB ( void* aCB, cAppCharP ident, cAppCharP routine, cAppCharP text, ... );
+void DEBUG_Exotic_DBW ( void* aCB, cAppCharP ident, cAppCharP routine, cAppCharP text, ... );
+
+/*! Special block and thread markers
+ * If \<aTag> starts with "-", this block will be displayed collapsed by default
+ */
+void DEBUG_Block ( void* aCB, cAppCharP aTag, cAppCharP aDesc, cAppCharP aAttrText );
+void DEBUG_EndBlock ( void* aCB, cAppCharP aTag );
+void DEBUG_EndThread ( void* aCB );
+
+
+/* C/C++ and DLL/library support */
+#if defined __cplusplus
+ } // end extern "C"
+#endif
+
+#endif /* SDK_UTIL_H */
+/* eof */
diff --git a/src/sysync_SDK/Sources/UI_util.cpp b/src/sysync_SDK/Sources/UI_util.cpp
new file mode 100644
index 0000000..8adad13
--- /dev/null
+++ b/src/sysync_SDK/Sources/UI_util.cpp
@@ -0,0 +1,249 @@
+/*
+ * File: UI_util.cpp
+ *
+ * Author: Beat Forster
+ *
+ * Programming interface between a user application
+ * and the Synthesis SyncML client engine.
+ *
+ * Copyright (c) 2007-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ */
+
+
+#include "prefix_file.h"
+#include "UI_util.h"
+#include "SDK_util.h"
+#include "SDK_support.h"
+
+// ---- include DLL functionality ----
+#if defined _WIN32
+ #include <windows.h>
+ #define DLL_Suffix ".dll"
+#elif defined MACOSX
+ #include <dlfcn.h>
+ #define DLL_Suffix ".dylib"
+ #define RFlags (RTLD_NOW + RTLD_GLOBAL)
+#elif defined LINUX
+ #include <dlfcn.h>
+ #define DLL_Suffix ".so"
+ #define RFlags RTLD_LAZY
+#else
+ #define DLL_Suffix ""
+#endif
+// -----------------------------------
+
+
+#define MyMod "UI_util" // debug name
+
+
+
+namespace sysync {
+
+
+static TSyError NotFnd( appPointer aRef, cAppCharP /* aName */ )
+{
+ TSyError err= LOCERR_OK;
+
+ if (aRef==NULL) {
+ err= DB_NotFound;
+ //printf( "Not found: '%s' err=%d\n", aName, err );
+ } // if
+
+ return err;
+} // NotFnd
+
+
+// Connect to DLL
+static TSyError ConnectDLL( cAppCharP aDLLname, appPointer &aDLL )
+{
+ #if defined _WIN32
+ aDLL= LoadLibrary( aDLLname );
+ #elif defined MACOSX || defined LINUX
+ aDLL= dlopen( aDLLname, RFlags );
+ //printf( "'%s' %s\n", aDLLname, dlerror() );
+ #else
+ aDLL= NULL;
+ #endif
+
+ return NotFnd( aDLL, aDLLname );
+} // ConnectDLL
+
+
+
+// Get <aFunc> of <aFuncName> at <aDLL>
+static TSyError DLL_Func( appPointer aDLL, cAppCharP aFuncName, appPointer &aFunc )
+{
+ #if defined _WIN32
+ aFunc= (appPointer)GetProcAddress( (HINSTANCE)aDLL, aFuncName );
+ #elif defined MACOSX || defined LINUX
+ aFunc= dlsym( aDLL, aFuncName );
+ #else
+ aFunc= NULL;
+ #endif
+
+ return NotFnd( aFunc, aFuncName );
+} // DLL_Func
+
+
+
+static bool IsLib( cAppCharP name )
+{
+ int len= strlen(name);
+ return len==0 || (name[ 0 ]=='[' &&
+ name[ len-1 ]==']'); // empty or embraced with "[" "]"
+} // IsLib
+
+
+// Connect SyncML engine
+TSyError UI_Connect( UI_Call_In &aCI, appPointer &aDLL, cAppCharP aEngineName,
+ CVersion aPrgVersion,
+ uInt16 aDebugFlags )
+{
+ // Always search for BOTH names, independently of environment
+ cAppCharP SyFName= "SySync_ConnectEngine";
+ cAppCharP FName= "ConnectEngine";
+ string name= aEngineName;
+ TSyError err= 0;
+ bool dbg= ( aDebugFlags & DBG_PLUGIN_DIRECT )!=0;
+
+ CVersion engVersion;
+ appPointer fFunc;
+
+ typedef TSyError (*GetCEProc)( UI_Call_In* aCI, CVersion *aEngVersion,
+ CVersion aPrgVersion,
+ uInt16 aDebugFlags );
+ GetCEProc fConnectEngine= NULL;
+
+ do {
+ aCI = NULL; // no such structure available at the beginning
+ aDLL= NULL;
+ if (dbg) printf( "name='%s' err=%d\n", name.c_str(), err );
+
+ if (name.empty()) {
+ // not yet fully implemented: Take default settings
+ aCI= new SDK_Interface_Struct;
+ InitCallback_Pure( aCI, DB_Callback_Version );
+ aCI->debugFlags= aDebugFlags;
+ break;
+ } // if
+
+ if (IsLib( name.c_str() )) {
+ #ifdef DBAPI_LINKED
+ fConnectEngine= SYSYNC_EXTERNAL(ConnectEngine);
+ #endif
+
+ break;
+ } // if
+ name+= DLL_Suffix;
+ err= ConnectDLL( name.c_str(), aDLL ); // try with suffix first
+ if (dbg) printf( "modu='%s' err=%d\n", name.c_str(), err );
+
+ if (err) {
+ name= aEngineName;
+ err= ConnectDLL( name.c_str(), aDLL ); // then try directly
+ } // if
+
+ if (dbg) printf( "modu='%s' err=%d\n", name.c_str(), err );
+ if (err) break;
+
+ cAppCharP fN= SyFName;
+ err= DLL_Func( aDLL, fN, fFunc );
+ fConnectEngine= (GetCEProc)fFunc;
+ if (dbg) printf( "func err=%d '%s' %s\n", err, fN, RefStr( (void*)fConnectEngine ).c_str() );
+
+ if (!fConnectEngine) { fN= FName;
+ err= DLL_Func( aDLL, fN, fFunc );
+ fConnectEngine= (GetCEProc)fFunc;
+ if (dbg) printf( "func err=%d '%s' %s\n", err, fN, RefStr( (void*)fConnectEngine ).c_str() );
+ } // if
+
+ } while (false);
+
+ if (fConnectEngine)
+ err= fConnectEngine( &aCI, &engVersion, aPrgVersion, aDebugFlags );
+ if (dbg) printf( "call err=%d\n", err );
+
+//DEBUG_DB ( aCI, MyMod, "ConnectEngine", "aCB=%08X eng=%08X prg=%08X aDebugFlags=%04X err=%d",
+// aCB, engVersion, aPrgVersion, aDebugFlags, err );
+ if (fConnectEngine && err) return err;
+ return NotFnd( aCI, "ConnectEngine" );
+} // UI_Connect
+
+
+TSyError UI_Disconnect( UI_Call_In aCI, appPointer aDLL )
+{
+ // Always search for BOTH names, independently of environment
+ cAppCharP SyFName= "SySync_DisconnectEngine";
+ cAppCharP FName= "DisconnectEngine";
+ TSyError err= 0;
+ appPointer fFunc;
+
+ typedef TSyError (*GetDEProc)( UI_Call_In aCI );
+ GetDEProc fDisconnectEngine= NULL;
+
+ do {
+ if (aDLL==NULL) {
+ #ifdef DBAPI_LINKED
+ fDisconnectEngine= SYSYNC_EXTERNAL(DisconnectEngine);
+ #endif
+
+ break;
+ } // if
+
+ cAppCharP fN= SyFName;
+ err= DLL_Func( aDLL, fN, fFunc );
+ fDisconnectEngine= (GetDEProc)fFunc;
+
+ if (!fDisconnectEngine) { fN= FName;
+ err= DLL_Func( aDLL, fN, fFunc );
+ fDisconnectEngine= (GetDEProc)fFunc;
+ } // if
+
+ //printf( "func err=%d %08X\n", err, fConnectEngine );
+ } while (false);
+
+ if (fDisconnectEngine)
+ err= fDisconnectEngine( aCI );
+//printf( "call err=%d\n", err );
+
+//DEBUG_DB ( aCI, MyMod, "ConnectEngine", "aCB=%08X eng=%08X prg=%08X aDebugFlags=%04X err=%d",
+// aCB, engVersion, aPrgVersion, aDebugFlags, err );
+ if (fDisconnectEngine && err) return err;
+ return NotFnd( aCI, "DisconnectEngine" );
+} // UI_Disconnect
+
+
+
+// <uContext> will be casted to the UIContext* structure
+UIContext* UiC( CContext uContext ) { return (UIContext*)uContext; }
+
+
+// Create a UI context
+TSyError UI_CreateContext( CContext &uContext, cAppCharP aEngineName,
+ CVersion aPrgVersion,
+ uInt16 aDebugFlags )
+{
+ TSyError err;
+ UIContext* uc= new UIContext;
+ err= UI_Connect( uc->uCI, uc->uDLL, aEngineName, aPrgVersion, aDebugFlags );
+ uc->uName= aEngineName;
+ DEBUG_DB ( uc->uCI, MyMod,"UI_CreateContext", "'%s'", uc->uName.c_str() );
+ uContext= (CContext)uc;
+ return err;
+} // UI_CreateContext
+
+
+
+// Delete a UI context
+TSyError UI_DeleteContext( CContext uContext )
+{
+ UIContext* uc= UiC( uContext );
+ DEBUG_DB ( uc->uCI, MyMod,"UI_DeleteContext", "'%s'", uc->uName.c_str() );
+ delete uc; // delete context
+ return LOCERR_OK;
+} // UI_DeleteContext
+
+
+} // namespace sysync
+/* eof */
diff --git a/src/sysync_SDK/Sources/UI_util.h b/src/sysync_SDK/Sources/UI_util.h
new file mode 100644
index 0000000..f55aea7
--- /dev/null
+++ b/src/sysync_SDK/Sources/UI_util.h
@@ -0,0 +1,48 @@
+/*
+ * File: UI_util.h
+ *
+ * Author: Beat Forster
+ *
+ * Programming interface between a user application
+ * and the Synthesis SyncML client engine.
+ *
+ * Copyright (c) 2007-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ *
+ */
+
+#include <string>
+#include "sync_dbapidef.h"
+
+
+namespace sysync {
+
+
+class UIContext {
+ public:
+ UI_Call_In uCI;
+ appPointer uDLL;
+ string uName;
+}; // UIContext
+
+// <uContext> will be casted to the UIContext* structure
+UIContext* UiC( CContext uContext );
+
+
+/* Function definitions */
+TSyError UI_Connect ( UI_Call_In &aCI,
+ appPointer &aDLL, cAppCharP aEngineName,
+ CVersion aPrgVersion,
+ uInt16 aDebugFlags );
+TSyError UI_Disconnect ( UI_Call_In aCI,
+ appPointer aDLL );
+
+
+TSyError UI_CreateContext( CContext &uContext, cAppCharP aEngineName,
+ CVersion aPrgVersion,
+ uInt16 aDebugFlags );
+TSyError UI_DeleteContext( CContext uContext );
+
+
+} // namespace sysync
+/* eof */
diff --git a/src/sysync_SDK/Sources/admindata.cpp b/src/sysync_SDK/Sources/admindata.cpp
new file mode 100644
index 0000000..3767411
--- /dev/null
+++ b/src/sysync_SDK/Sources/admindata.cpp
@@ -0,0 +1,217 @@
+/*
+ * File: admindata.cpp
+ *
+ * Author: Beat Forster (bfo@synthesis.ch)
+ *
+ *
+ * DBApi database adapter
+ * Admin data handling
+ *
+ * Copyright (c) 2005-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ * E X A M P L E C O D E
+ * (text_db interface)
+ *
+ */
+
+
+#include "admindata.h"
+#include "SDK_util.h" // include SDK utilities
+#include "SDK_support.h"
+
+
+namespace sysync { // the TAdminData class is part of sysync namespace
+
+#define P_Admin "ADM_" // admin table
+#define P_Map "MAP_" // map table
+
+
+// ---------------------------------------------------------------------------
+void TAdminData::Init( void* aCB, cAppCharP aDBName, string aMapPath,
+ string aContextName,
+ string sDevKey,
+ string sUsrKey )
+{
+ fCB = aCB; // the callback for debug purposes
+ fDBName = aDBName; // DBName, used for debug purposes
+ myDB = fDBName.c_str(); // access the same way as in the textdb module
+
+ fMapPath = aMapPath;
+ fContextName= aContextName;
+ fDevKey = sDevKey;
+ fUsrKey = sUsrKey;
+} // Init
+
+
+/*! Reset the map counter for 'ReadNextMapItem' */
+void TAdminData::ResetMapCounter() {
+ fMap= &fMapList;
+} // ResetMapCounter
+
+
+// Get <mID.flags> string
+string TAdminData::MapID_Flag_Str( cMapID mID, bool asHex )
+{
+ if (asHex) return HexStr( mID->flags );
+ else return IntStr( mID->flags ); // default is integer
+} // MapID_Flag_Str
+
+
+// Get <mID.localID>,<mID.remoteID> <mID.flags> string
+string TAdminData::MapID_Str( cMapID mID )
+{
+ string id= mID->localID;
+ if (*mID->remoteID!=0) { id+= ","; id+= mID->remoteID; } // if
+
+ id+= " flags=" + MapID_Flag_Str( mID, true );
+ id+= " ident=" + IntStr ( (sInt32)mID->ident );
+ return id;
+} // MapID_Str
+
+
+
+// ---------------------------------------------------------------------------
+TSyError TAdminData::LoadAdminData( string aLocDB, string aRemDB, appCharP *adminData )
+{
+ TSyError err;
+ TDBItem* actI;
+
+ string combi = ConcatNames( fUsrKey, fContextName, "_" );
+ combi = ConcatNames( fDevKey, combi, "_" );
+ string admName= ConcatPaths( fMapPath, P_Admin + combi + ".txt" );
+ string mapName= ConcatPaths( fMapPath, P_Map + combi + ".txt" );
+
+ fAdmList.fFileName= admName;
+ err= fAdmList.LoadDB( true, "ADM", fCB );
+
+ // Get all the elements of the list as a \n separated string <s>
+ string s= ""; actI= &fAdmList;
+ bool found= ListNext( actI );
+ if (found) {
+ TDBItemField* actK= &fAdmList.item; // the key identifier
+ TDBItemField* actF= &actI->item; // the item current field
+ while (ListNext( actK,actF )) {
+ cAppCharP k= actK->field.c_str();
+ cAppCharP f= actF->field.c_str();
+
+ s+= KeyAndField( k,f ) + '\n';
+ } // while
+ }
+ else { // no such element: create a new one with empty admin data
+ string newItemID;
+ fAdmList.CreateEmptyItem( newItemID, actI );
+ DEBUG_DB( fCB, myDB, Da_LA, "newItemID='%s'", newItemID.c_str() );
+ } // if
+
+ *adminData= StrAlloc( s.c_str() );
+ fAdm= actI;
+ fAdmList.Disp_Items( fCB );
+ fMapList.fFileName= mapName;
+
+ if (!mapName.empty()) {
+ err= fMapList.LoadDB( false, "", fCB );
+ DEBUG_DB( fCB, myDB, Da_LA, "mapFile='%s' err=%d", fMapList.fFileName.c_str(), err );
+ } // if
+
+ ResetMapCounter(); // <aLocDB> and <aRemDB> are not yes use in this implementation
+ DEBUG_DB( fCB, myDB, Da_LA, "'%s' '%s'", aLocDB.c_str(),aRemDB.c_str() );
+
+ if (found) return LOCERR_OK;
+ else return DB_NotFound;
+} /* LoadAdminData */
+
+
+TSyError TAdminData::SaveAdminData( cAppCharP adminData )
+{
+ fAdmList.UpdateFields( fCB, adminData, fAdm, true );
+ fAdmList.Disp_Items ( fCB,Da_SA );
+ TSyError err= fAdmList.SaveDB( true, fCB );
+ if (!err)
+ err= fMapList.SaveDB( false,fCB );
+ return err;
+} /* SaveAdminData */
+
+
+
+// ---------------------------------------------------------------------------
+bool TAdminData::ReadNextMapItem( MapID mID, bool aFirst )
+{
+ if (aFirst) ResetMapCounter();
+ string s= "(EOF)";
+ bool ok= ListNext( fMap );
+ if ( ok ) {
+ TDBItem* mpL= &fMapList;
+ TDBItem* act= fMap;
+ mID->localID = StrAlloc( act->itemID.c_str() );
+ mpL->Field( "0",act, s ); mID->remoteID= StrAlloc( s.c_str() );
+ mpL->Field( "1",act, s ); mID->flags = atoi ( s.c_str() );
+ mpL->Field( "2",act, s ); mID->ident = atoi ( s.c_str() );
+ s= MapID_Str( mID );
+ } // if
+
+ DEBUG_DB( fCB, myDB,Da_RM, "%s", s.c_str() );
+ return ok;
+} // ReadNextMapItem
+
+
+
+TSyError TAdminData::GetMapItem( cMapID mID, TDBItem* &act )
+{
+ TDBItem* mpL= &fMapList;
+ return mpL->GetItem_2( mID->localID, IntStr( (sInt32)mID->ident ).c_str(), mpL, act );
+} // GetMapItem
+
+
+TSyError TAdminData::UpdMapItem( cMapID mID, TDBItem* act )
+{
+ TSyError err= fMapList.UpdateField( fCB, "0", mID->remoteID, act, false );
+ if (!err) err= fMapList.UpdateField( fCB, "1", MapID_Flag_Str( mID ).c_str(), act, false );
+ if (!err) err= fMapList.UpdateField( fCB, "2", IntStr( (sInt32)mID->ident ).c_str(), act, false );
+ return err;
+} // UpdMapItem
+
+
+
+TSyError TAdminData::InsertMapItem( cMapID mID )
+{
+ TDBItem* act;
+ TSyError err= GetMapItem( mID, act );
+ if (!err) err= DB_Error; // Element already exists
+ else {
+ ItemID_Struct a; a.item = mID->localID;
+ a.parent= const_cast<char *>(""); // map items are not hierarchical
+
+ string newItemID;
+ err= fMapList.CreateEmptyItem( &a, newItemID, act );
+ if (!err) err= UpdMapItem ( mID, act );
+ } // if
+
+ DEBUG_DB( fCB, myDB,Da_IM, "%s err=%d", MapID_Str( mID ).c_str(), err );
+ return err;
+} // InsertMapItem
+
+
+TSyError TAdminData::UpdateMapItem( cMapID mID )
+{
+ TDBItem* act;
+ TSyError err= GetMapItem( mID, act ); // Element does not yet exist ?
+ if (!err) err= UpdMapItem( mID, act );
+
+ DEBUG_DB( fCB, myDB,Da_UM, "%s err=%d", MapID_Str( mID ).c_str(), err );
+ return err;
+} // UpdateMapItem
+
+
+TSyError TAdminData::DeleteMapItem( cMapID mID )
+{
+ TDBItem* act;
+ TSyError err= GetMapItem( mID, act );
+ if (!err) err= fMapList.DeleteItem( act );
+
+ DEBUG_DB( fCB, myDB,Da_DM, "%s err=%d", MapID_Str( mID ).c_str(), err );
+ return err;
+} // DeleteMapItem
+
+
+} /* namespace */
+/* eof */
diff --git a/src/sysync_SDK/Sources/admindata.h b/src/sysync_SDK/Sources/admindata.h
new file mode 100644
index 0000000..8543eaa
--- /dev/null
+++ b/src/sysync_SDK/Sources/admindata.h
@@ -0,0 +1,73 @@
+/*
+ * File: admindata.h
+ *
+ * Author: Beat Forster (bfo@synthesis.ch)
+ *
+ *
+ * DBApi database adapter
+ * Admin data handling
+ *
+ * Copyright (c) 2005-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ * E X A M P L E C O D E
+ * (text_db interface)
+ *
+ */
+
+#ifndef ADMINDATA_H
+#define ADMINDATA_H
+
+#include "sync_include.h" // import global things
+#include "sync_dbapidef.h" // get some definitions
+#include "SDK_util.h" // include SDK utilities
+#include "dbitem.h"
+#include <string>
+
+
+namespace sysync {
+
+
+class TAdminData {
+ public:
+ void Init( void* aCB, cAppCharP aDBName, string aAdminPath,
+ string aContextName,
+ string sDevKey,
+ string sUsrKey );
+
+ TSyError LoadAdminData ( string aLocDB,
+ string aRemDB,
+ appCharP *adminData );
+ TSyError SaveAdminData ( cAppCharP adminData );
+
+ bool ReadNextMapItem( MapID mID, bool aFirst );
+ TSyError InsertMapItem( cMapID mID );
+ TSyError UpdateMapItem( cMapID mID );
+ TSyError DeleteMapItem( cMapID mID );
+
+ private:
+ void ResetMapCounter();
+ TSyError GetMapItem ( cMapID mID, TDBItem* &act );
+ TSyError UpdMapItem ( cMapID mID, TDBItem* act );
+
+ string MapID_Flag_Str ( cMapID mID, bool asHex= false );
+ string MapID_Str ( cMapID mID );
+
+ void* fCB; // callback structure, for debug logs
+ string fDBName; // database name, for debug logs
+ cAppCharP myDB; // fDBName.c_str()
+
+ string fMapPath;
+ string fContextName;
+ string fDevKey;
+ string fUsrKey;
+
+ TDBItem fAdmList; // admin structure
+ TDBItem* fAdm; // admin item
+ TDBItem fMapList; // map structure
+ TDBItem* fMap; // map item
+}; // TAdminData
+
+
+} /* namespace */
+#endif /* ADMINDATA_H */
+/* eof */
diff --git a/src/sysync_SDK/Sources/blobs.cpp b/src/sysync_SDK/Sources/blobs.cpp
new file mode 100644
index 0000000..faf1cc1
--- /dev/null
+++ b/src/sysync_SDK/Sources/blobs.cpp
@@ -0,0 +1,343 @@
+/*
+ * File: blobs.cpp
+ *
+ * Author: Beat Forster (bfo@synthesis.ch)
+ *
+ *
+ * DBApi database adapter
+ * BLOB (Binary Large Object) access
+ *
+ * Copyright (c) 2005-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ *
+ * E X A M P L E C O D E
+ * (text_db interface)
+ *
+ */
+
+
+#include "blobs.h"
+#include "SDK_util.h" // include SDK utilities
+#include "SDK_support.h"
+
+
+namespace sysync { // the BLOB class is part of sysync namespace
+
+
+
+#define P_Blob "BLB_" // BLOB data
+
+
+// BLOB constructor
+TBlob::TBlob()
+{
+ fOpened= false;
+
+ #ifdef PLATFORM_FILE
+ fAttrActive= false; // the BLOB can have file dates and attributes
+ fDateActive= false;
+ #endif
+} // constructor
+
+
+// BLOB destructor
+TBlob::~TBlob() {
+ CloseBlob();
+} // destructor
+
+
+
+void TBlob::Init( void* aCB, cAppCharP aDBName, string aBlobPath,
+ string aContextName,
+ string sDevKey,
+ string sUsrKey )
+{
+ fCB = aCB; // the callback for debug purposes only
+ fDBName = aDBName; // DBName, used for debug purposes only
+
+ fBlobPath = aBlobPath;
+ fContextName= aContextName;
+ fDevKey = sDevKey;
+ fUsrKey = sUsrKey;
+} // Init
+
+
+
+// -------------------------------------------------------------------------
+// Get the BLOB's name
+string TBlob::BlobName( cItemID aID, cAppCharP aBlobID )
+{
+ // if it is not an item, but e.g. an admin element => take the device key
+ string v= aID->item; if (v.empty()) v= fDevKey;
+ string s= ConcatNames( fUsrKey, fContextName, "_" );
+ s= P_Blob + ConcatNames( v, s, "_" ) + "_";
+ s+= aBlobID;
+ s= ConcatPaths( fBlobPath, s ); // take a specific file path
+ return s;
+} // BlobName
+
+
+
+// Get the BLOB's size. For this implementation the BLOB size is
+// identical to the file size.
+// Operation allowed also for open BLOBs
+size_t TBlob::BlobSize( string aBlobName )
+{
+ TSyError err;
+ fpos_t tmp;
+
+ fName= aBlobName;
+ if (fName.empty()) return 0; // A BLOB w/o a name has size 0
+
+ bool opn= fOpened; // if not yet opened, open it temporary
+ if (!opn) { err= OpenBlob( "rb" ); if (err) return 0; }
+ else fgetpos( fFile, &tmp ); // save the current pos
+
+ fseek ( fFile, 0,SEEK_END );
+ size_t size= ftell( fFile );
+
+ if (!opn) { err= CloseBlob(); if (err) return 0; }
+ else fsetpos( fFile, &tmp ); // restore the current pos
+
+ return size;
+} // BlobSize
+
+
+
+// BLOB will be stored as separate files in this implementation
+// This is the routine to open them for read or write
+TSyError TBlob::OpenBlob( cAppCharP mode )
+{
+ CloseBlob();
+
+ fFile= fopen( fName.c_str(), mode );
+ if (!fFile) return DB_NotFound;
+
+ fOpened= true;
+ fseek ( fFile, 0, SEEK_END );
+ fCurPos= 0;
+ fSize = ftell( fFile ); // get total size
+ rewind ( fFile );
+ return LOCERR_OK;
+} // OpenBlob
+
+
+// BLOB will be stored as separate files in this implementation
+// This is the routine to close the current one.
+TSyError TBlob::CloseBlob()
+{
+ if (fOpened) { fclose( fFile ); fOpened= false; }
+ fCurPos= 0;
+ fSize = 0;
+ return LOCERR_OK;
+} // CloseBlob
+
+
+
+// Attribute and date handling routines
+#ifdef PLATFORM_FILE
+ TSyError TBlob::GetAttr( string aBlobName, TAttr &aAttr, bool &isFolder )
+ {
+ TSyError err= Get_FileAttr( aBlobName, fAttr, isFolder );
+ //if (!err && isFolder) err= DB_Forbidden;
+
+ DEBUG_Exotic_DB( fCB, fDBName.c_str(),"GetAttr", "'%s': hsadwrx=%d%d%d%d%d%d%d folder=%d err=%d",
+ aBlobName.c_str(), fAttr.h,fAttr.s,fAttr.a,fAttr.d,
+ fAttr.w,fAttr.r,fAttr.x, isFolder, err );
+ aAttr= fAttr;
+ return err;
+ } // GetAttr
+
+
+ TSyError TBlob::SetAttr( TAttr aAttr )
+ {
+ fAttr = aAttr;
+ fAttrActive= true;
+ DEBUG_Exotic_DB( fCB, fDBName.c_str(),"SetAttr", "hsadwrx=%d%d%d%d%d%d%d",
+ fAttr.h,fAttr.s,fAttr.a,fAttr.d,
+ fAttr.w,fAttr.r,fAttr.x );
+ return LOCERR_OK; // no error here
+ } // SetAttr
+
+
+ // -----------------------------------------------------------------------------------
+ TSyError TBlob::GetDates( string aBlobName, TDates &aDate )
+ {
+ TSyError err= Get_FileDate( aBlobName, fDate );
+ DEBUG_Exotic_DB( fCB, fDBName.c_str(),"GetDates", "'%s': cre=%s mod=%s acc=%s err=%d",
+ aBlobName.c_str(), fDate.created.c_str(),
+ fDate.modified.c_str(),
+ fDate.accessed.c_str(), err );
+ aDate= fDate;
+ return err;
+ } // GetDates
+
+
+ TSyError TBlob::SetDates( string aBlobName, TDates aDate )
+ {
+ fDate = aDate;
+ fDateActive = true;
+ TSyError err= Set_FileDate( aBlobName, fDate );
+ DEBUG_Exotic_DB( fCB, fDBName.c_str(),"SetDates", "cre=%s mod=%s acc=%s err=%d",
+ fDate.created.c_str(),
+ fDate.modified.c_str(),
+ fDate.accessed.c_str(), err );
+ return LOCERR_OK; // no error here
+ } // SetDates
+#endif // PLATFORM_FILE
+
+
+
+// -----------------------------------------------------------------------------------
+// BLOB will be stored as separate files in this implementation
+// This is the routine to read the current one or a part of it
+TSyError TBlob::ReadBlob( string aBlobName, appPointer *blkPtr,
+ memSize *blkSize, memSize *totSize,
+ bool aFirst, bool *aLast )
+{
+ // This example shows two values of influence:
+ // - the maximum value of returned <size> is BlobBlk
+ // - for blob sizes < BlobChk, the <totSize> will be returned, else 0.
+ #define BlobBlk 2048
+ #define BlobMax 2048 // checking limit for returning <totSize> = 0
+//#define BlobMax 10000 // checking limit for returning <totSize> = 0
+
+ TSyError err= LOCERR_OK;
+ *blkPtr = NULL; // initialize
+ *totSize= 0;
+ *aLast = true;
+ if (aFirst) fName= aBlobName;
+
+ DEBUG_DB( fCB, fDBName.c_str(), Da_RB, "blobName='%s' size=%d 1st=%s",
+ fName.c_str(), *blkSize, Bo( aFirst ) );
+
+ do { // exit part
+ if (fName.empty()) { err= DB_NotFound; break; }
+
+ if (aFirst) { // special treatement for <first>: open the file
+ err= OpenBlob( "rb" );
+ if (err) {
+ if (err==DB_NotFound) { // a not existing BLOB is not an error
+ err= LOCERR_OK; *blkSize= 0;
+ } // if
+
+ break;
+ } // if
+ } // if
+ if (!fOpened) { err= DB_Forbidden; break; } // now it must be open !
+
+ // adapt the default size
+ if (*blkSize==0 ||
+ *blkSize>BlobBlk) *blkSize= BlobBlk;
+
+ *blkPtr= malloc( *blkSize );
+ uInt32 rslt= fread( *blkPtr, 1, *blkSize, fFile );
+ *aLast= rslt!=*blkSize;
+
+ // check if already finished
+ fCurPos+= rslt;
+ if (fCurPos>=fSize) *aLast = true; // yes, it's done
+ if (fSize<BlobMax) *totSize= fSize;
+
+ // special treatement for <last>: close the file
+ if (*aLast) {
+ *blkSize= rslt;
+ err= CloseBlob();
+ } // if
+ } while (false); // end exit part
+
+ DEBUG_DB( fCB, fDBName.c_str(), Da_RB, "blk(%08X,%d) tot=%d last=%s err=%d",
+ *blkPtr, *blkSize, *totSize, Bo( *aLast ), err );
+ return err;
+} // ReadBlob
+
+TSyError TBlob::ReadBlob( cItemID aID, cAppCharP aBlobID,
+ appPointer *blkPtr, memSize *blkSize, memSize *totSize,
+ bool aFirst, bool *aLast ) {
+ return ReadBlob( BlobName( aID, aBlobID ),
+ blkPtr,blkSize,totSize, aFirst,aLast );
+} // ReadBlob
+
+
+
+// BLOB will be stored as separate files in this implementation
+// This is the routine to write the current one or a part of it
+TSyError TBlob::WriteBlob( string aBlobName, appPointer blkPtr,
+ memSize blkSize, memSize totSize,
+ bool aFirst, bool aLast )
+{
+ TSyError err= LOCERR_OK;
+ if (aFirst) { // empty blobs needn't to be written
+ if (aLast && blkSize==0) return DeleteBlob( aBlobName );
+ fName= aBlobName;
+ } // if
+
+ do {
+ if (fName.empty()) { err= DB_NotFound; break; }
+
+ if (aFirst) { // special treatement for <first>: open the file
+ err= OpenBlob( "wb" ); if (err) break;
+ } // if
+ if (!fOpened) { err= DB_Forbidden; break; } // now it must be open !
+
+ uInt32 rslt= fwrite( blkPtr, 1,blkSize, fFile );
+ if (rslt!=blkSize) err= DB_Error;
+ fCurPos+= rslt;
+
+ // special treatement for <last>: close the file
+ if (aLast || err) {
+ CloseBlob();
+
+ #ifdef PLATFORM_FILE
+ if (fAttrActive) Set_FileAttr( fName, fAttr );
+ if (fDateActive) Set_FileDate( fName, fDate );
+
+ fAttrActive= false; // it's written now
+ fDateActive= false;
+ #endif
+ } // if
+ } while (false);
+
+ DEBUG_DB( fCB, fDBName.c_str(),Da_WB, "blobName='%s' blk(%08X,%d) tot=%d",
+ fName.c_str(), blkPtr, blkSize, totSize );
+ DEBUG_DB( fCB, fDBName.c_str(),Da_WB, "1st=%s last=%s err=%d",
+ Bo( aFirst ), Bo( aLast ), err );
+ return err;
+} // WriteBlob
+
+TSyError TBlob::WriteBlob( cItemID aID, cAppCharP aBlobID,
+ appPointer blkPtr, memSize blkSize, memSize totSize,
+ bool aFirst, bool aLast ) {
+ return WriteBlob( BlobName( aID, aBlobID ),
+ blkPtr,blkSize,totSize, aFirst,aLast );
+} // WriteBlob
+
+
+
+// BLOB will be stored as separate files in this implementation
+// This is the routine to delete the current one.
+TSyError TBlob::DeleteBlob( string aBlobName )
+{
+ CloseBlob(); // close potential old one
+
+ TSyError err= LOCERR_OK;
+ fName= aBlobName;
+
+ do { // exit part
+ if (fName.empty()) { err= DB_NotFound; break; }
+ if (fOpened) { err= DB_Forbidden; break; }
+
+ remove( fName.c_str() ); // not existing BLOB is not an error
+ } while (false); // end exit part
+
+ DEBUG_DB( fCB, fDBName.c_str(),Da_DB, "blobName='%s' err=%d", fName.c_str(), err );
+ return err;
+} // DeleteBlob
+
+TSyError TBlob::DeleteBlob( cItemID aID, cAppCharP aBlobID ) {
+ return DeleteBlob( BlobName( aID, aBlobID ) );
+} // DeleteBlob
+
+
+} /* namespace */
+/* eof */
diff --git a/src/sysync_SDK/Sources/blobs.h b/src/sysync_SDK/Sources/blobs.h
new file mode 100644
index 0000000..e717d6f
--- /dev/null
+++ b/src/sysync_SDK/Sources/blobs.h
@@ -0,0 +1,108 @@
+/*
+ * File: blobs.h
+ *
+ * Author: Beat Forster (bfo@synthesis.ch)
+ *
+ *
+ * DBApi database adapter
+ * BLOB (Binary Large Object) access
+ *
+ * Copyright (c) 2005-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ *
+ * E X A M P L E C O D E
+ *
+ */
+
+#ifndef BLOBS_H
+#define BLOBS_H
+
+#include "sync_dbapidef.h" // get some definitions
+#ifdef PLATFORM_FILE
+ #include "platform_file.h"
+#endif
+
+#include <string>
+
+namespace sysync {
+
+
+
+/* NOTE: A BLOB object can be used for different BLOBs,
+ * but only for one BLOB simultaneously.
+ * If a new BLOB will be opened, the old one will
+ * be closed automatically, if still opened.
+ * read/write with <aFirst> = true will open a new one
+ * " " " " = false will ignore <aBlobName>
+ */
+class TBlob {
+ public:
+ TBlob();
+ ~TBlob();
+
+ void Init( void* aCB, cAppCharP aDBName, string aBlobPath,
+ string aContextName,
+ string sDevKey,
+ string sUsrKey );
+
+ string BlobName ( cItemID aID, cAppCharP aBlobID );
+
+ size_t BlobSize ( string aBlobName ); // allowed also for open BLOBs
+
+ TSyError ReadBlob ( string aBlobName,
+ appPointer *blkPtr, memSize *blkSize, memSize *totSize,
+ bool aFirst, bool *aLast );
+ TSyError ReadBlob ( cItemID aID, cAppCharP aBlobID,
+ appPointer *blkPtr, memSize *blkSize, memSize *totSize,
+ bool aFirst, bool *aLast );
+
+ TSyError WriteBlob ( string aBlobName,
+ appPointer blkPtr, memSize blkSize, memSize totSize,
+ bool aFirst, bool aLast );
+ TSyError WriteBlob ( cItemID aID, cAppCharP aBlobID,
+ appPointer blkPtr, memSize blkSize, memSize totSize,
+ bool aFirst, bool aLast );
+
+ TSyError DeleteBlob( string aBlobName );
+ TSyError DeleteBlob( cItemID aID, cAppCharP aBlobID );
+
+ #ifdef PLATFORM_FILE
+ TSyError GetAttr ( string aBlobName, TAttr &aAttr, bool &isFolder ); // get BLOB's file attributes
+ TSyError SetAttr ( TAttr aAttr ); // set " " "
+
+ TSyError GetDates( string aBlobName, TDates &aDate ); // get BLOB's file dates
+ TSyError SetDates( string aBlobName, TDates aDate ); // set " " "
+ #endif
+
+ private:
+ void* fCB; // callback structure, for debug logs
+ string fDBName; // database name, for debug logs
+
+ string fBlobPath; // params for creating BLOB's name
+ string fContextName;
+ string fDevKey;
+ string fUsrKey;
+
+ FILE* fFile; // assigned file
+ bool fOpened; // Is it currently opened ?
+ ulong fCurPos; // current position
+ ulong fSize; // BLOB's size
+
+ string fName; // BLOB's file name
+
+ #ifdef PLATFORM_FILE
+ TAttr fAttr; // BLOB's file attributes
+ bool fAttrActive;
+
+ TDates fDate; // BLOB's file dates
+ bool fDateActive;
+ #endif
+
+ TSyError OpenBlob( const char* mode );
+ TSyError CloseBlob();
+}; // TBlob
+
+
+} /* namespace */
+#endif /* BLOBS_H */
+/* eof */
diff --git a/src/sysync_SDK/Sources/dataconversion.h b/src/sysync_SDK/Sources/dataconversion.h
new file mode 100644
index 0000000..5acd42e
--- /dev/null
+++ b/src/sysync_SDK/Sources/dataconversion.h
@@ -0,0 +1,29 @@
+#ifndef DataConversion_H
+#define DataConversion_H
+
+#include "sync_declarations.h"
+
+#ifdef __cplusplus
+
+#include <string>
+using namespace sysync;
+namespace sysync {
+
+/**
+ * convert data from one format into another
+ *
+ * @param aSession an active session handle
+ * @param aFromTypeName the original datatype name, like "vCard30"
+ * @param aToTypeName the generated datatype name
+ * @retval aItemData original data, replaced with data in new format
+ * @return true for success
+ */
+bool DataConversion(SessionH aSession,
+ const char *aFromTypeName,
+ const char *aToTypeName,
+ std::string &aItemData);
+} // namespace sysync
+
+#endif /* __cplusplus */
+
+#endif /* DataConversion_H */
diff --git a/src/sysync_SDK/Sources/dbitem.cpp b/src/sysync_SDK/Sources/dbitem.cpp
new file mode 100644
index 0000000..d17964a
--- /dev/null
+++ b/src/sysync_SDK/Sources/dbitem.cpp
@@ -0,0 +1,1003 @@
+/*
+ * File: dbitem.cpp
+ *
+ * Author: Beat Forster (bfo@synthesis.ch)
+ *
+ *
+ * Example DBApi database adapter.
+ * TDBItem class for item handling
+ *
+ * Copyright (c) 2005-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ */
+
+#include "sync_include.h" // import some global things
+#include "sync_dbapidef.h"
+
+#include "SDK_util.h" // include SDK utilities
+#include "SDK_support.h"
+#include "dbitem.h"
+
+#ifndef SYSYNC_ENGINE
+ #include "stringutil.h" // local implementation of CStr <=> Str conversions
+ #include "timeutil.h" // local implementation for time routines
+#endif
+
+
+namespace sysync {
+
+#define MyDB "DBItem" // 'DBItem' debug name
+
+
+#define FFirst 10000 // start number for item name (will be incremented)
+#define ARR_Sep '\x1D' // Array element separator within textdb file
+#define ARR_SepS "\x1D" // ... the same as string
+
+
+
+// ---- utility functions ----------------------------------------------------------
+// Calculate the length of <fVal>'s array expansion
+static int ArrLen( cAppCharP fKey, cAppCharP fVal )
+{
+ cAppCharP z= "[0]";
+ cAppCharP x= "[x]:";
+ int len= strlen( fVal );
+
+ // some additional space is needed for array elements, calculate it first
+ int j= 0;
+ int i;
+ for (i= 0; i<len; i++) {
+ if (fVal[ i ]==ARR_Sep) j++;
+ } // for
+
+ // yes, there are such elements => increase <newLen>
+ // don't forget element "[0]"
+ len = 0;
+ if (j>0) { len+= strlen( z ) + j*(strlen( fKey )+strlen( x )); // "yy[x]:"
+ if (j>10) len+= j-10; // "[xx]"
+ } // if
+
+ return len;
+} // ArrLen
+
+
+/*! Create an item field */
+void MakeField( TDBItemField* &act, cAppCharP field,
+ cAppCharP fieldEnd, int &len, bool convert )
+{
+ MakeObj ( act );
+ if (fieldEnd) act->field.assign( field, (unsigned int)(fieldEnd-field) );
+ else act->field.assign( field );
+
+ if (convert) { string s= ""; // don't touch array separator
+ StrToCStrAppend( act->field.c_str(), s, true, ARR_Sep );
+ act->field= s;
+ } // if
+
+//printf( "len=%3d field='%s'\n", len, act->field.c_str() );
+ len+= act->field.length()+1;
+} // MakeField
+
+
+// Get a token string
+static string Token_Str( string aToken )
+{
+ if (aToken.empty()) return "";
+ else return "[" + aToken + "]";
+} // Token_Str;
+
+
+// create a UTC token for time comparisons
+string CurrentTime( int secsOffs, bool dateOnly )
+{
+ string isoStr;
+ lineartime_t f= secondToLinearTimeFactor * secsOffs;
+
+ #ifdef SYSYNC_ENGINE
+ timecontext_t tctx= TCTX_UTC;
+ if (dateOnly) tctx= TCTX_DATEONLY;
+
+ //lineartime_t timestamp= getSystemNowAs( TCTX_UTC, (GZones*)aGZones );
+ lineartime_t timestamp= getSystemNowAs( TCTX_UTC, NULL );
+ TimestampToISO8601Str( isoStr, timestamp + f, tctx, false, false );
+ #else
+ lineartime_t timestamp= utcNowAsLineartime();
+ timeStampToISO8601 ( timestamp + f, isoStr, dateOnly );
+ #endif
+
+ return isoStr;
+} // CurrentTime
+
+
+
+// Compare time of ISO8601 <aToken> with <aLastToken>/<aResumeToken>
+int CompareTokens( string aToken, string aLastToken, string aResumeToken )
+{
+ bool chg= aLastToken.empty() || // calculate newer condition
+ aLastToken < aToken;
+ bool res= !aResumeToken.empty() && // calculate resume condition
+ aResumeToken < aToken &&
+ aResumeToken > aLastToken;
+
+ int aStatus= ReadNextItem_Unchanged;
+ if (chg) aStatus= ReadNextItem_Changed;
+ if (res) aStatus= ReadNextItem_Resumed;
+ return aStatus;
+} // CompareTokens
+
+
+// -------------------------------------------------------------------------
+/* Combines <sKey> and <sField> into string <s>
+ * support for blobs, tz names and array fields
+ */
+string KeyAndField( cAppCharP sKey, cAppCharP sField )
+{
+ cAppCharP fs= strstr( sField, ";" ); // semicolon pattern: BLOB, TZNAME, ...
+ cAppCharP q = sField;
+ cAppCharP qE;
+
+ string s = sKey;
+ if (fs) { s+= fs; return s; } // with semicolon
+
+ int i= 0;
+ while (true) {
+ qE= strstr( q, ARR_SepS );
+ if (!qE && i==0) break;
+
+ s+= '[' + IntStr( i ) + ']'; if (!qE) break;
+
+ string tmp;
+ tmp.assign( q, (unsigned int)( qE-q ) );
+ s+= ':' + tmp + '\n';
+ i++;
+ q= qE+1;
+
+ s+= sKey;
+ } // loop
+
+ s+= ":";
+ s+= q;
+ return s;
+} // KeyAndField
+
+
+string KeyAndField( TDBItemField* actKey,
+ TDBItemField* actField ) {
+ return KeyAndField( actKey->field.c_str(),
+ actField->field.c_str() );
+} // KeyAndField
+
+
+void AddFields( string &aDat, string aAdd, cAppCharP param1,
+ cAppCharP param2,
+ cAppCharP param3,
+ cAppCharP param4,
+ cAppCharP param5 )
+{
+ string p;
+
+ if (aAdd.empty()) return;
+
+ int i= 1;
+ while (true) {
+ switch ( i ) {
+ case 1 : p= param1; break;
+ case 2 : p= param2; break;
+ case 3 : p= param3; break;
+ case 4 : p= param4; break;
+ case 5 : p= param5; break;
+ default: p= "";
+ } // switch
+
+ string chk= "%" + IntStr( i++ );
+ string::size_type pos= aAdd.find( chk,0 );
+ if (pos==string::npos) break;
+
+ aAdd.replace( pos,chk.length(), p );
+ } // loop
+
+ if (!aDat.empty()) {
+ cAppCharP c= aDat.c_str() + aDat.length()-1; // last char
+ if (*c!='\n') aDat+= '\n';
+ } // if
+
+ aDat+= aAdd;
+} // AddFields
+
+
+/* ------------------------------------------------------------------------ */
+//! assign ID and callback
+void TDBItem::init( cAppCharP aItemID, cAppCharP aParentID, void* aCB, TDBItem* aNext )
+{
+ itemID = aItemID;
+ parentID= aParentID;
+ fCB = aCB;
+ next = aNext;
+} // init
+
+
+//! assign ID and callback (overloaded)
+void TDBItem::init( cAppCharP aItemID, void* aCB, TDBItem* aNext ) {
+ init( aItemID,"", aCB, aNext );
+} // init
+
+
+// the combined item/parent string
+cAppCharP TDBItem::c_str()
+{
+ fID= itemID;
+ if (!parentID.empty()) { fID+= ","; fID+= parentID; }
+ return fID.c_str();
+} // c_str
+
+
+
+/* Get a specific item <actL>, which represents <itemID>,<parentID>.
+ * Returns error, if not found.
+ * If <first> is true (default), start at the beginning of the list,
+ * else continue
+ */
+TSyError TDBItem::GetItem( cItemID aID, TDBItem* &actL, bool first )
+{
+ char* p= aID->parent; if (!p) p= const_cast<char *>("");
+
+ if (first) actL= this;
+ while (ListNext( actL )) {
+ if (strcmp( aID->item,actL->itemID.c_str() )==0 &&
+ strcmp( p, actL->parentID.c_str() )==0) return LOCERR_OK;
+ } // while
+
+ actL= NULL;
+ return DB_NotFound;
+} // GetItem
+
+
+/* Overloaded for <aItemID> only w/o parent ID
+ * Get a specific item <actL>, which represents <itemID>
+ * Returns error, if not found.
+ */
+TSyError TDBItem::GetItem( cAppCharP aItemID, TDBItem* &actL, bool first )
+{
+ ItemID_Struct a; a.item = (char*)aItemID;
+ a.parent= NULL;
+ return GetItem( &a, actL, first );
+} // GetItem
+
+
+/* Overloaded for <aItemID> only w/o parent ID
+ * Get a specific item <actL>, which represents <itemID>
+ * Returns error, if not found.
+ */
+TSyError TDBItem::GetItem_2( cAppCharP aItemID, cAppCharP aField2,
+ TDBItem* mpL, TDBItem* &act )
+{
+ TSyError err= LOCERR_OK;
+ ItemID_Struct a; a.item = (char*)aItemID;
+ a.parent= NULL;
+
+ bool first= true;
+ string s;
+
+ while (true) {
+ err= GetItem( &a, act, first ); if (err) break; // not found
+ mpL->Field ( "2",act, s );
+ if (strcmp( aField2,s.c_str() )==0) break; // found
+ first= false;
+ } // loop
+
+ return err;
+} // GetItem_2
+
+
+
+TSyError TDBItem::DeleteItem( TDBItem* actL )
+{
+ TDBItem* prvL= actL;
+ ListBack( prvL, this ); // search for the previous element
+
+ prvL->next= actL->next;
+ actL->next= NULL; // avoid destroying the whole chain
+ delete actL;
+ return LOCERR_OK;
+} /* DeleteItem */
+
+
+
+TSyError TDBItem::DeleteItem( cItemID aID )
+{
+ TSyError err= DB_NotFound;
+
+ TDBItem* actI;
+ err= GetItem( aID, actI ); if (err) return err;
+
+ // item must be not available as parent element
+ TDBItem* actL= this;
+ while (ListNext( actL )) {
+ if (strcmp( aID->item,actL->parentID.c_str() )==0) return DB_Forbidden;
+ } // while
+
+ return DeleteItem( actI );
+} /* DeleteItem */
+
+
+
+TSyError TDBItem::DeleteItem( cAppCharP aItemID )
+{
+ ItemID_Struct a; a.item = (char*)aItemID;
+ a.parent= const_cast<char *>("");
+ return DeleteItem( &a );
+} // DeleteItem
+
+
+
+// parent element must be "" or exist
+TSyError TDBItem::ParentExist( cAppCharP aItemID, cAppCharP aParentID )
+{
+ if (*aParentID==0) return LOCERR_OK;
+ if (strcmp( aItemID,aParentID )==0) return DB_Forbidden; // no recursion allowed
+
+ TDBItem* actL= this;
+ while (ListNext( actL )) {
+ if (strcmp( aParentID,actL->itemID.c_str() )==0) return LOCERR_OK;
+ } // while
+
+ return DB_NotFound;
+} // ParentExists
+
+
+
+void TDBItem::CreateItem( string newItemID, string parentID, TDBItem* &actL )
+{
+ TDBItemField* actK;
+ TDBItemField* actF;
+
+ TDBItem* newL= new TDBItem; // create item root
+ newL->fCB= fCB; // inherit callback
+ actF= &newL->item;
+ newL->itemID = newItemID;
+ newL->parentID= parentID;
+ actL->next= newL;
+ ListNext( actL );
+
+ int n= 0; actK= &item;
+ while (ListNext( actK )) { // number of elements according to the key
+ MakeField ( actF, "", NULL, actL->len );
+ n++;
+ } // while
+
+ actL->len--; // termination is included already
+} // CreateItem
+
+
+
+/* The strategy is simple: Take ID="10000" as start, go thru the whole list.
+ * if found, increment. Insert new element at the end.
+ */
+TSyError TDBItem::CreateEmptyItem( ItemID aID, string &newItemID, TDBItem* &actL,
+ string newID )
+{
+ TSyError err;
+ int itemID= atoi( newID.c_str() );
+ if (itemID==0) itemID= atoi( F_First );
+
+ TDBItem* last;
+//TDBItem* newL;
+//TDBItemField* actK;
+//TDBItemField* actF;
+
+ // parent element must be "" or exist
+ err= ParentExist( "", aID->parent ); if (err) return err;
+
+ newItemID= aID->item;
+ if (newItemID.empty()) {
+ actL= this;
+ while (true) { last= actL;
+ if (!ListNext( actL )) break;
+
+ int itemNum= atoi( actL->itemID.c_str() );
+ if (itemNum>=itemID) itemID= itemNum+1; // assign largest unique number
+ } // while
+
+ newItemID= IntStr( itemID );
+ actL = last;
+ }
+ else {
+ actL= LastElem( this ); // get the last element
+ } // if
+
+
+ CreateItem( newItemID, aID->parent, actL );
+
+ /*
+ // create item root
+ newL= new TDBItem;
+ newL->fCB= fCB; // inherit callback
+ actF= &newL->item;
+ newL->itemID = newItemID;
+ newL->parentID= aID->parent;
+ actL->next= newL;
+ ListNext( actL );
+
+ int n= 0; actK= &item;
+ while (ListNext( actK )) { // number of elements according to the key
+ MakeField ( actF, "", NULL, actL->len );
+ n++;
+ } // while
+
+ actL->len--; // termination is included already
+ */
+
+ return LOCERR_OK;
+} // CreateEmptyItem
+
+
+TSyError TDBItem::CreateEmptyItem( cAppCharP aParentID, string &newItemID, TDBItem* &actL )
+{
+ ItemID_Struct a; a.item = const_cast<char *>("");
+ a.parent= (char*)aParentID;
+ return CreateEmptyItem ( &a, newItemID, actL );
+} // CreateEmptyItem
+
+
+TSyError TDBItem::CreateEmptyItem( string &newItemID, TDBItem* &actL ) {
+ return CreateEmptyItem( "", newItemID, actL );
+} // CreateEmptyItem
+
+
+/*!
+ * Check, if <aKey> can be interpreted as number
+ */
+static bool NumKey( cAppCharP aKey, int &iKey )
+{
+ iKey= atoi( aKey );
+ return iKey!=0 || strcmp( aKey,"0")==0;
+} // NumKey
+
+
+/* Update the specific field <fKey> with the new value <fVal>.
+ * Create the whole bunch of missing keys, if not yet available.
+ */
+TSyError TDBItem::UpdateField( void* aCB, cAppCharP fKey,
+ cAppCharP fVal, TDBItem* hdI, bool asName )
+{
+ TSyError err= DB_NotFound;
+ char* ts;
+ string s;
+ int iKey= 0, iLast= -1; // start value if data base is empty
+
+ TDBItemField* actK= &item; // the key
+ TDBItemField* lastK;
+ TDBItemField* actI= &hdI->item;
+ TDBItemField* lastI;
+
+ if (aCB==NULL) aCB= fCB; // get the default callback, if not overridden by <aCB>
+
+ // check, if the specific element is already existing
+ while (true) {
+ lastK= actK;
+ if (!ListNext( actK )) break;
+
+ lastI= actI; // a not existing field will be created (empty)
+ if (!ListNext( actI )) {
+ actI= lastI;
+ MakeField ( actI, "", NULL, hdI->len );
+ } // if
+
+ if (strcmp( fKey,actK->field.c_str())==0) {
+ int oldLen= actI->field.length();
+ int newLen= strlen( fVal ) + ArrLen( fKey,fVal );
+
+ hdI->len += newLen-oldLen; // adapt the whole length
+ actI->field= fVal; // and assign value of the new field
+ err= LOCERR_OK; // the new value is assigned, everything is ok
+ break;
+ } // if
+ } // while
+
+ // -----------------------------------------------------
+ if (err && !asName) {
+ if (!lastK->field.empty()) iLast= atoi( lastK->field.c_str() );
+ if (!NumKey( fKey,iKey )) err= DB_NotFound; // not a valid key
+ } // if
+
+ if (err!=DB_NotFound) {
+ s= KeyAndField( fKey,fVal );
+
+ // create name info
+ string vv= hdI->c_str();
+ if (!vv.empty()) vv= " " + Parans( vv ) + " ";
+
+ // create debug info <eInfo>
+ string eInfo;
+ if (err) eInfo= " err=" + IntStr( err );
+
+ DEBUG_Exotic_DB( aCB, MyDB,"UpdateField", "%s'%s'%s",
+ vv.c_str(), s.c_str(), eInfo.c_str() );
+ return err;
+ } // if
+
+ if (asName) iKey= iLast+1; // create just one element
+
+ // now add the missing key fields
+ int i;
+ for (i= iLast+1; i<=iKey; i++) {
+ string tmp= IntStr( i );
+ if (asName) ts= (char*)fKey;
+ else ts= (char*)tmp.c_str();
+ MakeField( lastK, ts, NULL, len ); // create a new key element
+ } // for
+
+ // and the same for each item
+ TDBItem* actV= this;
+ while (ListNext( actV )) {
+ actI= &actV->item;
+ while (true) {
+ lastI= actI;
+ if (!ListNext( actI )) {
+ for (i= iLast+1; i<=iKey; i++) MakeField( lastI, "", NULL, actV->len );
+ break;
+ } // if
+ } // loop
+ } // while
+
+ // the task is not yet done => do it again (recursively)
+ return UpdateField( aCB, fKey, fVal, hdI, asName );
+} // UpdateField
+
+
+
+TSyError TDBItem::Field( cAppCharP fKey, TDBItem* hdI, string &s )
+{
+ TDBItemField* actK= &item;
+ TDBItemField* actF= &hdI->item;
+
+ while (ListNext( actK,actF )) {
+ if (strcmp( actK->field.c_str(),fKey )==0) {
+ s= actF->field.c_str();
+ return LOCERR_OK;
+ } // if
+ } // while
+
+ return DB_NotFound;
+} // Field
+
+
+static void ReplaceArrElem( string &s, int n, string arrElem )
+{
+ int pos, v= 0;
+ bool fnd;
+
+ int i= n;
+ while (true) { pos= v;
+ v= s.find( ARR_Sep, pos );
+ fnd= v!=(int)string::npos;
+ if (!fnd) v= s.length();
+
+ if (i<=0) break;
+
+ if (!fnd) {
+ if (arrElem.empty()) return; // adding not needed
+ s+= ARR_Sep;
+ } // if
+
+ v++; i--;
+ } // while
+
+ s= s.substr( 0, pos ) + arrElem + s.substr( v, s.length()-v );
+} // ReplaceArrElem
+
+
+
+static void CutEmptyEnd( string &s )
+{
+ while (!s.empty()) {
+ uInt32 last= s.length()-1;
+ if (s.rfind( ARR_Sep, last )!=last) break; // last char ?
+ s= s.substr ( 0, last ); // if yes, cut it
+ } // while
+} // CutEmptyEnd
+
+
+static int ArrIndex( cAppCharP q, cAppCharP qN )
+{
+ int n= 0;
+
+ if (qN) {
+ string s;
+ s.assign( q, (unsigned int)( qN-q ) );
+ CutBracks( s );
+ n= atoi ( s.c_str() );
+ } // if
+
+ return n;
+} // ArrIndex
+
+
+void TDBItem::Array_TDB( cAppCharP &q, cAppCharP aKey, TDBItem* hdI, string &aVal )
+{
+ cAppCharP qR;
+ cAppCharP qN;
+ cAppCharP qA;
+ bool firstElem= true; // true during first element handling
+ string s; // temporary local string
+ int n;
+
+//aVal= ""; // init string
+//string org;
+ Field( aKey, hdI, aVal ); // as it is now
+
+ while (*q!='\0') {
+ qN= strstr( q,":" );
+ qA= strstr( q,"[" );
+
+ if (!qN) break; // no more regular elements
+ if (!qA || qN<qA) break; // this is not an array element
+
+ if (!firstElem) {
+ s.assign( q, (unsigned int)( qA-q ) );
+ if (s!=aKey) break;
+
+ //if (strcmp( aKey, s.c_str() )!=0) break; // not the same key
+ } // if
+
+ n= ArrIndex( qA,qN ); // get the index of this field
+ if (qN) q= qN+1;
+
+ // get the string in-between ':' and '\r' or '\n'
+ qN= strstr( q,"\n" );
+ if (!qN) qN= (cAppCharP)q + strlen( q );
+ qR= qN-1;
+ if (*qR!='\r') qR= qN;
+
+ s.assign( q, (unsigned int)( qR-q ) );
+ /*
+ if (!firstElem) aVal+= ARR_Sep; // separator between elements, even if elements = ""
+ aVal+= s;
+ */
+
+ ReplaceArrElem( aVal, n, s );
+ CutEmptyEnd ( aVal );
+
+ q= qN; if (*q!='\0') q++;
+ firstElem= false;
+ } // while
+} // Array_TDB
+
+
+
+TSyError TDBItem::UpdateFields( void* aCB, cAppCharP aItemData, TDBItem* hdI, bool asName,
+ cAppCharP aNewToken )
+{
+ TSyError err= LOCERR_OK;
+ cAppCharP q = aItemData;
+ cAppCharP qR;
+ string fKey, fVal;
+ int iKey;
+
+ if (!hdI) { hdI= this; ListNext( hdI ); } // special case, if only one item
+ if (!asName) hdI->fToken= aNewToken;
+
+ while (*q!='\0') { // break the string <key>':'<value>'\n' into key and value
+ cAppCharP qN= strstr( q, StdPattern ); // normal notation
+ cAppCharP qA= strstr( q,ArrayPattern ); // try array notation
+ //cAppCharP qB= strstr( q, BlobPattern ); // try blob notation
+ //cAppCharP qT= strstr( q, TZPattern ); // try tzname notation
+ cAppCharP qS= strstr( q, ";" ); // semicolon notation
+
+ bool isArr = qA && (!qN || qA<qN); // available and before ":"
+ if (isArr) qN= qA; if (!qN) { err= DB_Fatal; break; }
+
+ //bool isBlob= qB && (!qN || qB<qN); // available and before ":"
+ //if (isBlob) qN= qB; if (!qN) { err= DB_Fatal; break; }
+
+ //bool isTZ = qT && (!qN || qT<qN); // available and before ":"
+ //if (isTZ) qN= qT; if (!qN) { err= DB_Fatal; break; }
+
+ bool isSemi= qS && (!qN || qS<qN); // available and before ":"
+ if (isSemi) qN= qS; if (!qN) { err= DB_Fatal; break; }
+
+ fKey.assign( q, (unsigned int)( qN-q ) );
+ //q= qN; if (!isBlob && !isArr && !isTZ) q++;
+ q= qN; if (!isArr && !isSemi) q++;
+
+ if (isArr) {
+ Array_TDB( q, fKey.c_str(), hdI, fVal );
+ }
+ else { qN= strstr ( q,"\n" ); // allow "\r\n" (what we expect), and also "\n"
+ if (!qN) qN= (cAppCharP)q + strlen( q );
+ qR= qN-1;
+ if (*qR!='\r') qR= qN;
+ fVal.assign( q, (unsigned int)( qR-q ) );
+ q= qN; if (*q!='\0') q++;
+ } // if
+
+ asName|= !NumKey( fKey.c_str(), iKey ); // must interpret them as names !!
+ err= UpdateField( aCB, fKey.c_str(),fVal.c_str(), hdI, asName );
+ if (err) break;
+ } // while
+
+ return err;
+} // UpdateFields
+
+
+
+bool TDBItem::SameField( cAppCharP fKey,
+ cAppCharP fVal, TDBItem* hdI )
+{
+ string s;
+ TSyError err= Field( fKey, hdI, s );
+ return !err && strcmp( s.c_str(), fVal )==0;
+} // SameField
+
+
+
+void TDBItem::Disp_ItemData( void* aCB, cAppCharP title, cAppCharP aTxt, cAppCharP aItemData )
+{
+ DEBUG_Block ( aCB, title, "", aTxt );
+ DEBUG_ ( aCB, "%s", aItemData );
+ DEBUG_EndBlock( aCB, title );
+} // Disp_ItemData
+
+
+void TDBItem::Disp_Items( void* aCB, cAppCharP txt, bool allFields,
+ cAppCharP specificItem )
+{
+ cAppCharP DIT= "-Disp_Items"; // collapsed display with '-' at the beginning
+
+ string s= txt;
+ if (!s.empty()) s= "call=" + s;
+
+ if (aCB==NULL) aCB= fCB; // get the default callback, if not overridden by <aCB>
+ DEBUG_Block ( aCB, DIT, itemID.c_str(), s.c_str() ); // hierarchical log
+
+ TDBItem* actI= this;
+ while (ListNext( actI )) { // for each item do ...
+ if (*specificItem!=0 &&
+ strcmp( specificItem,actI->itemID.c_str() )!=0 ) continue;
+
+ s= Token_Str( actI->fToken );
+ DEBUG_( aCB, "%s= (%s) %s", this->c_str(), actI->c_str(), s.c_str() );
+
+ TDBItemField* actK= &item; // the key identifier
+ TDBItemField* actF= &actI->item; // the item current field
+
+ cAppCharP f;
+ while (ListNext( actK )) { // for each element do ...
+ if (ListNext( actF )) f= actF->field.c_str();
+ else f= ""; // "<???>";
+
+ if (allFields || !actF->field.empty()) { // display only under conditions
+ s= KeyAndField( actK->field.c_str(), f );
+ DEBUG_( aCB, "%s", s.c_str() );
+ } // if
+ } // while
+ } // while
+
+ DEBUG_EndBlock( aCB, DIT );
+} // Disp_Items
+
+
+
+static void AddKey( void* aCB, bool withKey, cAppCharP qA,
+ cAppCharP qR,
+ int n, TDBItemField* &actF, int &len )
+{
+ if (withKey)
+ MakeField( actF, qA,qR, len, true );
+ else { // just numbering, starting with "0"
+ string tmp= IntStr( n-2 );
+ MakeField( actF, tmp.c_str(),NULL, len, true );
+ DEBUG_Exotic_DB( aCB, MyDB,"AddKey", "'%s'", tmp.c_str() );
+ } // if
+} // AddKey
+
+
+TSyError TDBItem::LoadDB( bool withKey, cAppCharP aPrefix, void* aCB )
+{
+ cAppCharP LDB= "-LoadDB"; // collapsed display with '-' at the beginning
+ TSyError err= LOCERR_OK;
+
+ char ch, prv= '\0';
+ char* q;
+ char* qA;
+ char* qR;
+ char* qC;
+
+ // create root structure
+ TDBItem* actL= this;
+ TDBItem* actI= NULL;
+ TDBItem* newL= NULL;
+ TDBItemField* actF;
+ TDBItemField* actT;
+ bool first= true;
+ int n= 0, nMax= 0;
+
+ if (fLoaded) return LOCERR_OK;
+ if (withKey) init( aPrefix, aCB );
+
+ if (aCB==NULL) aCB= fCB; // get the default callback, if not overridden by <aCB>
+
+ FILE* f= fopen( fFileName.c_str(),"rb" );
+ if (!f) err= DB_NotFound; /* empty DB */
+
+ string s= "err=" + IntStr( err );
+ string rslt;
+ DEBUG_Block( aCB, LDB, fFileName.c_str(), s.c_str() ); // hierarchical log
+
+ if (!err) {
+ while (true) { // loop ...
+ bool is0D= false; // last line ended with 0D ?
+
+ // get lines, make it compatible for Windows, Mac and Linux
+ q= &ch;
+ s= "";
+ while (true) {
+ if (fread( q, 1,1, f ) != 1)
+ ; // error ignored
+
+ if (feof( f )) break;
+
+ if (*q=='\x0D') { is0D= true; break; }
+ if (*q=='\x0A') {
+ if (!is0D) break;
+ *q= prv; is0D= false;
+ } // if
+
+ s += ch;
+ prv= ch;
+ } // while
+ if (s.empty() && feof( f )) break; // .. until end of file
+
+ q= (char*)s.c_str();
+ // remove eventual UTF-8 lead-in
+ if ((q[0] & 0xFF) == 0xEF &&
+ (q[1] & 0xFF) == 0xBB &&
+ (q[2] & 0xFF) == 0xBF) {
+ q+=3; // skip UTF-8 lead-in
+ DEBUG_Exotic_DB( aCB, MyDB,"", "UTF-8 lead-in skipped" );
+ } // if
+
+ if (*q=='\0') continue; // empty line ?
+ ReplaceLoad( q, rslt ); // textdb specific => normal
+ q= (char*)rslt.c_str();
+
+ DEBUG_Exotic_DB( aCB, MyDB,"", "line='%s'", q );
+
+ // create index tree
+ if (first) {
+ actT= &actL->item;
+
+ n= 0; qA= q;
+ while ( *qA!='\0') {
+ qR= strstr( qA,"\t" );
+ if (!qR) qR= qA + strlen( qA ); // no tab to skip
+
+ if (n>1) // the index itself is not a field
+ AddKey( aCB, withKey, qA,qR, n, actT, actL->len );
+
+ n++;
+ if (*qR=='\0') break;
+ qA= qR+1;
+ } // while
+
+ nMax= n; // keep it for later appending of additional elements
+ actI= actL; // save it for later use
+ } // if
+
+ // create item elements
+ if (!first || !withKey) {
+ n= 0; qA= q;
+ while ( *qA!='\0') {
+ qR= strstr( qA,"\t" );
+ if (!qR) qR= qA + strlen( qA ); // no tab to skip
+
+ if (n==0) { newL= new TDBItem; // create new item root
+ newL->fCB= fCB; // inherit callback
+ actF= &newL->item;
+ qC= strstr( qA,"," );
+ if (!qC || qC>qR) qC= qR;
+
+ /* separate itemID/parentID, if comma separator available */
+ newL->itemID.assign ( qA, (unsigned int)(qC-qA) );
+ if (qC!=qR) qC++;
+ newL->parentID.assign( qC, (unsigned int)(qR-qC) );
+ actL->next= newL;
+ ListNext ( actL );
+ }
+ else if (n==1) newL->fToken.assign( qA, (unsigned int)(qR-qA) );
+ else {
+ MakeField( actF, qA,qR, actL->len, true );
+ //printf( "A: %3d\n", actL->len );
+ actL->len+= ArrLen( "xx", actF->field.c_str() );
+ //printf( "B: %3d\n", actL->len );
+ } // if
+
+ if (n>=nMax && actI!=NULL) {
+ if (n>1) {
+ //printf( "indexLenV=%d\n", actI->len );
+ AddKey( aCB, withKey, "???",NULL, n, actT, actI->len );
+ //printf( "indexLenN=%d\n", actI->len );
+ } // if
+
+ nMax++; // adapt the new limit
+ } // if
+
+ if (*qR=='\0') break;
+ qA= qR+1; n++;
+ } // while
+ } // if
+
+ actL->len--; // termination is included
+ //printf( "C: %3d\n", actL->len );
+ //printf( "D: '%s'\n", q );
+ first= false; // first run is done
+ } // while
+
+ fclose( f );
+ fLoaded= true;
+ } // if
+
+ DEBUG_EndBlock( aCB, LDB );
+ return err;
+} // LoadDB
+
+
+TSyError TDBItem::SaveDB( bool withKey, void* aCB )
+{
+ cAppCharP SDB= "-SaveDB"; // collapsed display with '-' at the beginning
+ TSyError err= LOCERR_OK;
+
+ if (fFileName.empty()) return DB_NotFound;
+
+ if (aCB==NULL) aCB= fCB; // get the default callback, if not overridden by <aCB>
+
+ FILE* f= fopen( fFileName.c_str(),"wb" );
+ if (!f) err= DB_NotFound;
+
+ string s= "err=" + IntStr( err );
+ string rslt;
+ DEBUG_Block( aCB, SDB, fFileName.c_str(), s.c_str() ); // hierarchical log
+
+ if (!err) {
+ fputs( "\xEF\xBB\xBF", f ); // UTF-8 lead-in
+ DEBUG_Exotic_DB( aCB, MyDB,"","UTF-8 lead-in written" );
+
+ string line;
+ TDBItem* actI= this;
+ while (withKey || ListNext( actI )) {
+ line = actI->c_str(); // starting with the <itemID>[","<parentID>]
+ line+= "\t";
+ line+= actI->fToken.c_str(); // append the timestamp
+
+ int n= 0;
+ TDBItemField* actF= &actI->item; // the item current field
+ while (ListNext( actF )) { s= ""; // tab separated
+ CStrToStrAppend( actF->field.c_str(), s, false, ARR_Sep ); // don't touch array separator
+ ReplaceSave ( s.c_str(), rslt ); // normal => textdb specific
+
+ line+= "\t" + rslt;
+ n++;
+ } // while
+
+ int ii= line.length(); // remove empty items at the end
+ while (ii>0) {
+ if (line[ --ii ]!='\t') break;
+ }
+ line= line.substr( 0, ii+1 );
+
+ // now log and write the whole line
+ DEBUG_DB( aCB, MyDB,"", "line='%s'", line.c_str() );
+ fprintf( f, "%s\r\n", line.c_str() );
+ withKey= false;
+ } // while
+
+ fclose( f );
+ } // if
+
+ DEBUG_EndBlock( aCB, SDB );
+ return err;
+} // SaveDB
+
+
+} /* namespace */
+/* eof */
diff --git a/src/sysync_SDK/Sources/dbitem.h b/src/sysync_SDK/Sources/dbitem.h
new file mode 100644
index 0000000..108c050
--- /dev/null
+++ b/src/sysync_SDK/Sources/dbitem.h
@@ -0,0 +1,214 @@
+/*
+ * File: dbitem.h
+ *
+ * Author: Beat Forster (bfo@synthesis.ch)
+ *
+ *
+ * Example DBApi database adapter.
+ * TDBItem class for item handling
+ *
+ * Copyright (c) 2005-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ */
+
+#ifndef DBITEM_H
+#define DBITEM_H
+
+#include "sync_include.h" // import some global things
+
+
+#ifndef SYSYNC_ENGINE
+ #include "stringutil.h" // local implementation of CStr <=> Str conversions
+#endif
+
+
+#define F_First "10000" // start number for item name (will be incremented)
+
+
+
+namespace sysync {
+// ---------- list templates ----------------------------------
+//! go to the next element of <act>. Returns true, if available
+template <class T> bool ListNext( T* &act )
+{
+ if (act) act= static_cast< T* >( act->next );
+ return act!=NULL;
+} // ListNext
+
+
+//! go to the next element of <act>. Returns true, if available
+// the same with 2 parameters
+template <class T1, class T2> bool ListNext( T1* &act1, T2* &act2 ) {
+ return ListNext( act1 ) && ListNext( act2 );
+} // ListNext
+
+
+template <class T> bool ListBack( T* &act, T* li )
+// go to the previous element of <act>. Returns true, if available
+{
+ T* last;
+ if (act==li) return false;
+
+ do { last= li;
+ } while (ListNext( li ) && li!=act);
+
+ act= last; return true;
+} // ListBack
+
+
+template <class T> T* LastElem( T* li )
+// go to the last element of <act>.
+{ T* act= NULL;
+ ListBack( act,li );
+ return act;
+} // LastElem
+
+
+template <class T> void DeleteNext( T* &next, void* aCB= NULL,
+ const char* s= "", bool dbg= false )
+// delete the rest of the list <act>
+{
+ if (dbg) DEBUG_Exotic_DB( aCB, "","Delete", "%s (%s)", typeid( T ).name(), s );
+ if (next) {
+ delete next;
+ next= NULL; // don't let it undefined
+ } // if
+} // DeleteNext
+
+
+template <class T> void MakeObj( T* &act )
+// create an element at position <act> of the given list
+{
+ T* sv = static_cast< T* >( act->next ); // save it for linking later again
+ act->next= static_cast< T* >( new T ); // create an element of the given type
+ ListNext ( act );
+ act->next= sv;
+} // MakeObj
+
+
+
+// ---------- list class --------------------------------------
+class TList
+{
+ public:
+ TList() { next= NULL; } // constructor
+ virtual ~TList() { DeleteNext( next ); } // destructor (if not yet otherwise deleted)
+ TList* next; // reference to the next element
+}; // TList
+
+
+// ---------- item field class --------------------------------
+class TDBItemField : public TList
+{
+ typedef TList inherited;
+ public:
+ TDBItemField() { next= NULL; } // constructor
+ virtual ~TDBItemField() { TDBItemField** act= (TDBItemField**)&next; // destructor
+ DeleteNext ( *act ); }
+
+ string field; // the string field
+}; // TDBItemField
+
+
+// ---------- item class -------------------------------------
+class TDBItem : public TDBItemField
+{
+ typedef TDBItemField inherited;
+ public:
+ TDBItem( void* aCB= NULL ) { len= 1; fCB= aCB; fLoaded= false; } // constructor
+ virtual ~TDBItem() { TDBItem** act= (TDBItem**)&next; // destructor
+ DeleteNext( *act, fCB, c_str(), true ); }
+
+ TDBItemField item; // the header element
+ int len; // length of all fields of this item
+ bool fLoaded; // indicates, if already loaded
+
+ string itemID;
+ string parentID;
+ const char* c_str(); // <itemID>[","<parentID>]
+
+ void init( cAppCharP aItemID, cAppCharP aParentID, void* aCB= NULL, TDBItem* aNext= NULL ); // initalize
+ void init( cAppCharP aItemID, void* aCB= NULL, TDBItem* aNext= NULL );
+
+ string fFileName; // the (full) file name for LoadDB/SaveDB
+
+ private:
+ string fID; // internal memory space for c_str()
+ void Array_TDB ( cAppCharP &q, cAppCharP aKey, TDBItem* hdI, string &aVal );
+
+ public:
+ string fToken; // the timestamp token (as string)
+ void* fCB; // callback structure
+
+ // two different versions, overloaded
+ TSyError GetItem ( cItemID aID, TDBItem* &actL, bool first= true ); // get a specific item
+ TSyError GetItem ( cAppCharP aItemID, TDBItem* &actL, bool first= true );
+
+ TSyError GetItem_2 ( cAppCharP aItemID,
+ cAppCharP aField2, TDBItem* mpL,
+ TDBItem* &actL ); // get item with specific field 0
+
+ // three different versions, overloaded
+ TSyError DeleteItem ( cItemID aID );
+ TSyError DeleteItem ( cAppCharP aItemID );
+ TSyError DeleteItem ( TDBItem* actL );
+
+ TSyError ParentExist ( cAppCharP aItemID, cAppCharP aParentID );
+
+ void CreateItem ( string newItemID, string parentID, TDBItem* &actL );
+ // three different versions, overloaded
+ TSyError CreateEmptyItem( ItemID aID, string &newItemID, TDBItem* &actL, string itemID= F_First );
+ TSyError CreateEmptyItem( cAppCharP aParentID, string &newItemID, TDBItem* &actL );
+ TSyError CreateEmptyItem ( string &newItemID, TDBItem* &actL );
+
+ TSyError UpdateField ( void* aCB, cAppCharP fKey,
+ cAppCharP fVal, TDBItem* hdI= NULL, bool asName= true );
+ TSyError UpdateFields ( void* aCB, cAppCharP aItemData, TDBItem* hdI= NULL, bool asName= true,
+ cAppCharP aNewToken= "" );
+
+ TSyError Field ( cAppCharP fKey, TDBItem* hdI, string &s );
+ bool SameField ( cAppCharP fKey, cAppCharP fVal, TDBItem* hdI );
+ void Disp_ItemData ( void* aCB, cAppCharP title, cAppCharP attrTxt,
+ cAppCharP aItemData );
+ void Disp_Items ( void* aCB= NULL, cAppCharP txt= "",
+ bool allFields= true, cAppCharP specificItem= "" );
+
+ // load and save the database from/to file
+ TSyError LoadDB ( bool withKey, cAppCharP aPrefix= "", void* aCB= NULL );
+ TSyError SaveDB ( bool withKey, void* aCB= NULL );
+}; // TDBItem
+
+
+// ---- utility functions ----------------------------------------------------------
+/*! Create an item field */
+void MakeField( TDBItemField* &act, cAppCharP field,
+ cAppCharP fieldEnd, int &len, bool convert= false );
+
+
+/*! Create a UTC token for time comparisons */
+string CurrentTime( int secsOffs= 0, bool dateOnly= false );
+
+/*! Compare time of ISO8601 <aToken> with <aLastToken>/<aResumeToken> */
+int CompareTokens( string aToken, string aLastToken, string aResumeToken );
+
+
+/*! combine key and field in a string */
+string KeyAndField( cAppCharP sKey,
+ cAppCharP sField );
+string KeyAndField( TDBItemField* actKey,
+ TDBItemField* actField );
+
+/*! add some <aAdd> item fields to the beginning of <aItemData>. Result is <aDat>.
+ * If <aAdd> contains %1, it will be replaced by <param1>.
+ * " " " %2, " " " " " <param2>
+ */
+void AddFields( string &aDat, string aAdd, cAppCharP param1= "",
+ cAppCharP param2= "",
+ cAppCharP param3= "",
+ cAppCharP param4= "",
+ cAppCharP param5= "" );
+
+
+} /* namespace */
+#endif /* DBITEM_H */
+/* eof */
diff --git a/src/sysync_SDK/Sources/engine_defs.h b/src/sysync_SDK/Sources/engine_defs.h
new file mode 100644
index 0000000..c90ae37
--- /dev/null
+++ b/src/sysync_SDK/Sources/engine_defs.h
@@ -0,0 +1,415 @@
+/**
+ * @File engine_defs.h
+ *
+ * @Author Lukas Zeller (luz@synthesis.ch)
+ * Beat Forster (bfo@synthesis.ch)
+ *
+ * @brief Definitions needed for the common engine interface
+ *
+ * Copyright (c) 2007-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ */
+
+#ifndef ENGINE_DEFS_H
+#define ENGINE_DEFS_H
+
+#include "generic_types.h"
+
+#ifdef __cplusplus
+namespace sysync {
+#endif
+
+
+/* ---- supported charsets ---- */
+enum TCharsetEnum {
+ /** invalid */
+ CHS_UNKNOWN = 0,
+ /** 7 bit ASCII-only, with nearest char conversion for umlauts etc. */
+ CHS_ASCII = 1,
+ CHS_ANSI = 2,
+ CHS_ISO_8859_1 = 3,
+ CHS_UTF8 = 4,
+ CHS_UTF16 = 5,
+ CHS_GB2312 = 6,
+ CHS_CP936 = 7,
+};
+
+/* ---- line end modes ---- */
+enum TLineendEnum {
+ /** none specified */
+ LEM_NONE = 0,
+ /** 0x0A */
+ LEM_UNIX = 1,
+ /** 0x0D */
+ LEM_MAC = 2,
+ /** 0x0D 0x0A */
+ LEM_DOS = 3,
+ /** as in C strings, '\n' which is 0x0A normally
+ (but might be 0x0D on some platforms) */
+ LEM_CSTR = 4,
+ /** 0x0B (filemaker tab-separated text format,
+ CR is shown as 0x0B within fields */
+ LEM_FILEMAKER = 5
+};
+
+/* ---- literal quoting modes ---- */
+enum TQuotingEnum {
+ /** none specified */
+ QM_NONE = 0,
+ /** single quote must be duplicated */
+ QM_DUPLSINGLE = 1,
+ /** double quote must be duplicated */
+ QM_DUPLDOUBLE = 2,
+ /** C-string-style escapes of CR,LF,TAB,BS,\," and '
+ (but no full c-string escape with \xXX etc.) */
+ QM_BACKSLASH = 3
+};
+
+/* ---- time formats ---- */
+enum TTimeModeEnum {
+ /** SySync lineartime (64 bit milliseconds) */
+ TMODE_LINEARTIME = 0,
+ /** SySync lineardate (32 bit days) */
+ TMODE_LINEARDATE = 1,
+ /** Unix epoch time (32 bit seconds) */
+ TMODE_UNIXTIME = 2,
+ /** Unix epoch time (64 bit milliseconds) */
+ TMODE_UNIXTIME_MS = 3,
+ /** Flag that can be added for time in UTC */
+ TMODE_FLAG_UTC = 0x8000,
+ /** Flag that can be added for time as-is */
+ TMODE_FLAG_FLOATING = 0x4000,
+ /** Mask to separate time mode */
+ TMODE_MODEMASK = 0x003F
+};
+
+
+/* ---- path separator for settings key paths ---- */
+#define SETTINGSKEY_PATH_SEPARATOR '/'
+
+/* ---- key/value special IDs ---- */
+enum TKeyValEnum {
+ /** special ID to signal unknown subkey or value */
+ KEYVAL_ID_UNKNOWN = -1,
+ /** special ID to open first subkey */
+ KEYVAL_ID_FIRST = -2,
+ /** special ID to open next subkey */
+ KEYVAL_ID_NEXT = -3,
+ /** special ID to create new subkey (empty values) */
+ KEYVAL_ID_NEW = -4,
+ /** special ID to create new subkey and initialize with
+ (hard-coded or engine-config-predefined) default values */
+ KEYVAL_ID_NEW_DEFAULT = -5,
+ /** special ID to create new subkey using previously opened
+ subkey as template for initializing values */
+ KEYVAL_ID_NEW_DUP = -6,
+ /** special ID for values that cannot be accessed by ID AT ALL */
+ KEYVAL_NO_ID = -7,
+ /** special ID to specify entire contents of a container */
+ KEYVAL_ID_ALL = -8
+};
+
+/* ---- special value names ---- */
+#define VALNAME_FIRST ".FIRST" /* for GetValueID(): get first value, for keys that support value iteration */
+#define VALNAME_NEXT ".NEXT" /* for GetValueID(): get next value, for keys that support value iteration */
+#define VALNAME_FLAG ".FLAG" /* for GetValueID(): get flag mask that can be added to value ID to get special attributes of a value instead of main value */
+/* ---- special value name suffixes (not available for all settings keys, but primarily for item keys) ---- */
+#define VALSUFF_NAME ".VALNAME" /* return field/value name as VALTYPE_TEXT */
+#define VALSUFF_TYPE ".VALTYPE" /* return value's native type (VALTYPE_xxx) name as VALTYPE_INT16 */
+#define VALSUFF_ARRSZ ".ARRAYSIZE" /* return size of array as VALTYPE_INT16. Returns DB_NotFound for non-array values */
+#define VALSUFF_TZNAME ".TZNAME" /* if value is a time stamp, returns name of the time zone as VALTYPE_TEXT (empty=floating) */
+#define VALSUFF_TZOFFS ".TZOFFS" /* if value is a time stamp, returns minute offset east of GMT as VALTYPE_INT16, DB_NotFound for floating timestamps */
+#define VALSUFF_NORM ".NORM" /* return normalized value of field */
+
+
+
+/* ---- value types ---- */
+enum TValTypeEnum {
+ /** Unknown */
+ VALTYPE_UNKNOWN = 0,
+ /** 8-bit integer */
+ VALTYPE_INT8 = 1,
+ /** 16-bit integer */
+ VALTYPE_INT16 = 2,
+ /** 32-bit integer */
+ VALTYPE_INT32 = 3,
+ /** 64-bit integer */
+ VALTYPE_INT64 = 4,
+ /** enum (for the SDK user, this is equivalent to VALTYPE_INT8) */
+ VALTYPE_ENUM = 5,
+ /** raw buffer, binary (for texts, will return application charset,
+ null terminated) */
+ VALTYPE_BUF = 10,
+ /** text in charset / line-end mode as specified with SetTextMode() */
+ VALTYPE_TEXT = 20,
+ /** internal use only: text, internally saved in obfuscated format */
+ VALTYPE_TEXT_OBFUS = 21,
+ /** time specification 64 bit in format specified with SetTimeMode() */
+ VALTYPE_TIME64 = 30,
+ /** time specification 32 bit in format specified with SetTimeMode() */
+ VALTYPE_TIME32 = 31,
+};
+
+/* ---- application feature numbers ---- */
+enum TAppFeatureEnum {
+ /** client autosync */
+ APP_FTR_AUTOSYNC = 1,
+ /** client IPP/DMU */
+ APP_FTR_IPP = 2,
+ /** client settings for event date range */
+ APP_FTR_EVENTRANGE = 3,
+ /** client settings for email date and maxsize range */
+ APP_FTR_EMAILRANGE = 4
+};
+
+/* ---- danger flags ---- */
+enum TDangerEnum {
+ /** sync session will zap client data */
+ DANGERFLAG_WILLZAPCLIENT = 1,
+ /** sync session will zap server data */
+ DANGERFLAG_WILLZAPSERVER = 2
+};
+
+
+/* ---- read-only indicators for certain settings fields --- */
+enum TReadOnlyEnum {
+ /** server URI is read-only */
+ RDONLY_URI = 1,
+ /** URI path suffix is read-only */
+ RDONLY_URIPATH = 2,
+ /** protocol (transport http/https flag as well as SyncML version) are read-only */
+ RDONLY_PROTOCOL = 4,
+ /** DB path names */
+ RDONLY_DBPATH = 8,
+ /** profile may not be deleted or renamed (but non-readonly options can be set) */
+ RDONLY_PROFILE = 16,
+ /** proxy settings (except proxy auth settings) are read-only */
+ RDONLY_PROXY = 32
+};
+
+/**
+ * Profile modes (MIME DIR mode).
+ *
+ * These are the values also used as parameter of the
+ * MAKETEXTWITHPROFILE/PARSETEXTWITHPROFILE macros.
+ */
+enum ProfileModeEnum {
+ PROFILEMODE_DEFAULT = 0, /**< default mode of profile */
+ PROFILEMODE_OLD = 1, /**< vCard 2.1 and vCalendar 1.0 style */
+ PROFILEMODE_MIMEDIR = 2 /**< MIME-DIR = vCard 3.0, iCalendar 2.0 style */
+};
+
+
+/* ---- bit definitons for "profileFlags" profile field --- */
+enum TProfileFlagsEnum {
+ /** we should log the next session (not handled in engine!) */
+ PROFILEFLAG_LOGNEXTSYNC = 0x00000001,
+ /** run session in legacy mode */
+ PROFILEFLAG_LEGACYMODE = 0x00000002
+};
+
+
+/* ---- bit definitons for "transpFlags" profile field --- */
+enum TTranspFlagsEnum {
+ /** SSL certificate verification/expiration errors should be ignored (not handled in engine!) */
+ TRANSPFLAG_SSLIGNORECERTFAIL = 0x00000001
+};
+
+
+
+
+/* ---- step commands ---- */
+/* - input to engine */
+enum TStepCmdEnum {
+ /** start a new client session */
+ STEPCMD_CLIENTSTART = 1,
+ /** start a autosync session */
+ STEPCMD_CLIENTAUTOSTART = 2,
+
+ /** just run next step */
+ STEPCMD_STEP = 10,
+ /** run next step (after receiving STEPCMD_NEEDDATA and putting received
+ SyncML data into buffer using GetSyncMLBuffer/ReturnSyncMLBuffer) */
+ STEPCMD_GOTDATA = 11,
+ /** run next step (after receiving STEPCMD_SENDDATA and sending SyncML
+ data from buffer using GetSyncMLBuffer/ReturnSyncMLBuffer) */
+ STEPCMD_SENTDATA = 12,
+
+ /** suspend the session */
+ STEPCMD_SUSPEND = 20,
+ /** abort the session */
+ STEPCMD_ABORT = 21,
+ /** transport failure causes aborting the session */
+ STEPCMD_TRANSPFAIL = 22,
+
+ /** process SAN in SyncML buffer */
+ STEPCMD_SAN_CHECK = 40,
+ /** check for periodic autosync */
+ STEPCMD_AUTOSYNC_CHECK = 41,
+/* - output from engine */
+ /** engine returns to caller w/o progress info, and should be called
+ again ASAP with STEPCMD_STEP */
+ STEPCMD_OK = 100,
+ /** engine returns to caller to show progress, and should be called
+ again ASAP with STEPCMD_STEP */
+ STEPCMD_PROGRESS = 101,
+ /** error (see return value of SessionStep call) */
+ STEPCMD_ERROR = 102,
+ /** engine has new data to send to remote, use GetSyncMLBuffer() to get
+ access to the engine buffer containing the message */
+ STEPCMD_SENDDATA = 110,
+ /** engine needs new data, use GetSyncMLBuffer() to get access to the
+ empty buffer where to put data */
+ STEPCMD_NEEDDATA = 111,
+ /** session done, SAN processed, Autosync checked etc.
+ No further action required */
+ STEPCMD_DONE = 120,
+ /** communication session ends here (close connection!) but SyncML session continues */
+ STEPCMD_RESTART = 121,
+ /** STEPCMD_AUTOSYNC_CHECK or STEPCMD_SAN_CHECK has detected need
+ for performing a sync session */
+ STEPCMD_NEEDSYNC = 130
+};
+
+/* ---- session type selectors */
+enum TSessionSelectorEnum {
+ /* - binfile client engine selectors */
+ /** normal client session base selector value, add profile ID to select profile */
+ SESSIONSEL_CLIENT_NORMAL = 0x00000000,
+ /** mask to extract profile ID from selector */
+ SESSIONSEL_PROFILEID_MASK = 0x0FFFFFFF,
+ /** Autosync (timed, IPP, SAN) checking session */
+ SESSIONSEL_CLIENT_AS_CHECK = 0x10000000,
+ /* - other special session selectors */
+ /** test session to access a datastore using DBAPI-like calls */
+ SESSIONSEL_DBAPI_TUNNEL = 0x80000001
+};
+
+
+/* ---- XML config data reader func type ---- */
+#if defined _WIN32 && !defined _MSC_VER
+ #define _CALLING_ __stdcall
+#else
+ #define _CALLING_
+#endif
+
+typedef int _CALLING_ (*TXMLConfigReadFunc)(
+ appCharP aBuffer,
+ bufferIndex aMaxSize,
+ bufferIndex *aReadCharsP,
+ void *aContext
+);
+
+
+/* ---- Engine progress information ---- */
+/* - progress event types */
+enum TProgressEventEnum {
+ /* global */
+
+ /** some fatal aborting error */
+ PEV_ERROR = 0,
+ /** extra messages */
+ PEV_MESSAGE = 1,
+ /** extra error code */
+ PEV_ERRCODE = 2,
+ /** no extra message, just called to allow aborting */
+ PEV_NOP = 3,
+ /** called to signal main program, that caller would want to
+ wait for extra1 milliseconds */
+ PEV_WAIT = 4,
+ /** called to allow debug interactions, extra1=code */
+ PEV_DEBUG = 5,
+
+ /* transport-related */
+
+ PEV_SENDSTART = 6,
+ PEV_SENDEND = 7,
+ PEV_RECVSTART = 8,
+ PEV_RECVEND = 9,
+ /** expired */
+ PEV_SSL_EXPIRED = 10,
+ /** not completely trusted */
+ PEV_SSL_NOTRUST = 11,
+ /** sent periodically when waiting for network,
+ allows application to check connection */
+ PEV_CONNCHECK = 12,
+ /** sent when client could initiate a explicit suspend */
+ PEV_SUSPENDCHECK = 13,
+
+ /* general */
+
+ /** alert 100 received from remote, SessionKey's "displayalert" value contains message */
+ PEV_DISPLAY100 = 14,
+
+ /* session-related */
+
+ PEV_SESSIONSTART = 15,
+ /** session ended, probably with error in extra */
+ PEV_SESSIONEND = 16,
+ /* datastore-related */
+ /** preparing (e.g. preflight in some clients), extra1=progress, extra2=total */
+ PEV_PREPARING = 17,
+ /** deleting (zapping datastore), extra1=progress, extra2=total */
+ PEV_DELETING = 18,
+ /** datastore alerted (extra1=0 for normal, 1 for slow, 2 for first time slow,
+ extra2=1 for resumed session, extra3=syncmode: 0=twoway, 1=fromserver, 2=fromclient) */
+ PEV_ALERTED = 19,
+ /** sync started */
+ PEV_SYNCSTART = 20,
+ /** item received, extra1=current item count,
+ extra2=number of expected changes (if >= 0) */
+ PEV_ITEMRECEIVED = 21,
+ /** item sent, extra1=current item count,
+ extra2=number of expected items to be sent (if >=0) */
+ PEV_ITEMSENT = 22,
+ /** item locally processed, extra1=# added,
+ extra2=# updated,
+ extra3=# deleted */
+ PEV_ITEMPROCESSED = 23,
+ /** sync finished, probably with error in extra1 (0=ok),
+ syncmode in extra2 (0=normal, 1=slow, 2=first time),
+ extra3=1 for resumed session) */
+ PEV_SYNCEND = 24,
+ /** datastore statistics for local (extra1=# added,
+ extra2=# updated,
+ extra3=# deleted) */
+ PEV_DSSTATS_L = 25,
+ /** datastore statistics for remote (extra1=# added,
+ extra2=# updated,
+ extra3=# deleted) */
+ PEV_DSSTATS_R = 26,
+ /** datastore statistics for local/remote rejects (extra1=# locally rejected,
+ extra2=# remotely rejected) */
+ PEV_DSSTATS_E = 27,
+ /** datastore statistics for server slowsync (extra1=# slowsync matches) */
+ PEV_DSSTATS_S = 28,
+ /** datastore statistics for server conflicts (extra1=# server won,
+ extra2=# client won,
+ extra3=# duplicated) */
+ PEV_DSSTATS_C = 29,
+ /** datastore statistics for data volume (extra1=outgoing bytes,
+ extra2=incoming bytes) */
+ PEV_DSSTATS_D = 30,
+ /** engine is in process of suspending */
+ PEV_SUSPENDING = 31
+};
+
+
+/* - progress event structure */
+typedef struct TEngineProgressType {
+ uInt16 eventtype; /* PEV_XXX definition */
+ sInt32 targetID; /* target ID if event is specific to a target, KEYVAL_ID_UNKNOWN if session global */
+ sIntPtr extra1; /* extra info, such as error code or count for progress or # of added items */
+ sIntPtr extra2; /* extra info, such as total for progress or # of updated items */
+ sIntPtr extra3; /* extra info, such as # of deleted items */
+} TEngineProgressInfo;
+
+
+
+#ifdef __cplusplus
+} // namespace
+#endif
+
+#endif /* ENGINE_DEFS_H */
+
+/* eof */
diff --git a/src/sysync_SDK/Sources/enginemodulebase.cpp b/src/sysync_SDK/Sources/enginemodulebase.cpp
new file mode 100755
index 0000000..1d0f391
--- /dev/null
+++ b/src/sysync_SDK/Sources/enginemodulebase.cpp
@@ -0,0 +1,191 @@
+/**
+ * @File enginemodulebase.cpp
+ *
+ * @Author Beat Forster (bfo@synthesis.ch)
+ *
+ * @brief TEngineModuleBase
+ * engine bus bar class
+ *
+ * Copyright (c) 2007-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ */
+
+
+#include "prefix_file.h"
+#include "enginemodulebase.h"
+#include "SDK_util.h"
+
+#if defined SYSYNC_ENGINE || defined SYSYNC_ENGINE_TEST
+ #include "engineentry.h"
+#endif
+
+#if !defined(SYSYNC_ENGINE) || defined(ENGINEINTERFACE_SUPPORT)
+
+namespace sysync {
+
+
+
+// TEngineModuleBase
+// =================
+
+// local name
+#define MyName "enginemodulebase"
+
+// constructor
+TEngineModuleBase::TEngineModuleBase()
+{
+ fCI = NULL;
+ fEngineName= ""; // none
+ fPrgVersion= 0;
+ fDebugFlags= 0;
+ fCIisStatic= false;
+} // TEngineModuleBase
+
+
+// destructor
+TEngineModuleBase::~TEngineModuleBase()
+{
+ #if defined SYSYNC_ENGINE || defined SYSYNC_ENGINE_TEST
+ if (fCI && !fCIisStatic) delete fCI;
+ #endif
+} // ~TEngineModuleBase
+
+
+
+// --- Initial connection ------------------------
+TSyError TEngineModuleBase::Connect( string aEngineName,
+ CVersion aPrgVersion,
+ uInt16 aDebugFlags)
+{
+ TSyError err= LOCERR_OK;
+ fEngineName = aEngineName;
+ fPrgVersion = aPrgVersion;
+ fDebugFlags = aDebugFlags;
+
+ #if defined SYSYNC_ENGINE || defined SYSYNC_ENGINE_TEST
+ if (fCI==NULL) fCI= new SDK_Interface_Struct;
+ InitCallback_Exotic( fCI, DB_Callback_Version );
+ fCI->thisBase= this; // get <this> later for callback calls
+ CB_Connect ( fCI );
+ #endif
+
+ if (fPrgVersion==0) fPrgVersion= Plugin_Version( 0 ); // take the internal one
+ if (fPrgVersion<VP_UIApp) err= LOCERR_NOTIMP;
+
+ if (fCI) fCI->debugFlags= fDebugFlags;
+ if (!err) err= Init();
+ DEBUG_DB( fCI, MyName, "Connect","version=%08X flags=%04X err=%d", fPrgVersion, fDebugFlags, err );
+ return err;
+} // Connect
+
+
+TSyError TEngineModuleBase::Disconnect()
+{
+ if (!fCI) return LOCERR_OK;
+ DEBUG_DB( fCI, MyName, "Disconnect","" );
+
+ TSyError err= Term();
+ return err;
+} // Disconnect
+
+
+// ----------------------------------------------------------------------------------------------
+TSyError TEngineModuleBase::GetStrValue( KeyH aKeyH, cAppCharP aValName, string &aText )
+{
+ TSyError err;
+
+ const int txtFieldSize= 80; // a readonable string length to catch it at once
+ char txtField[ txtFieldSize ];
+ memSize txtSize;
+ char* f= (char*)&txtField;
+
+ // try directly to fit in a short string first
+ err= GetValue( aKeyH, aValName, VALTYPE_TEXT, f, txtFieldSize, txtSize );
+
+ bool tooShort= err==LOCERR_TRUNCATED;
+ if (tooShort) {
+ err= GetValue( aKeyH, aValName, VALTYPE_TEXT, f, 0, txtSize ); // get size
+ f= (char*)malloc( txtSize+1 ); // plus NUL termination
+ err= GetValue( aKeyH, aValName, VALTYPE_TEXT, f, txtSize+1, txtSize ); // no error anymore
+ } // if
+
+ aText= f; // assign it
+
+ if (tooShort)
+ free( f ); // and deallocate again, if used dynamically
+
+ return err;
+} // GetStrValue;
+
+
+TSyError TEngineModuleBase::SetStrValue( KeyH aKeyH, cAppCharP aValName, string aText )
+{
+ // -1 automatically calculate length from null-terminated string
+ return SetValue( aKeyH, aValName, VALTYPE_TEXT, aText.c_str(), (memSize)-1 );
+} // SetStrValue
+
+
+// ----------------------------------------------------------------------------------------------
+TSyError TEngineModuleBase::GetInt8Value( KeyH aKeyH, cAppCharP aValName, sInt8 &aValue )
+{
+ memSize vSize;
+ return GetValue( aKeyH, aValName, VALTYPE_INT8, &aValue, sizeof(aValue), vSize );
+} // GetInt8Value
+
+TSyError TEngineModuleBase::GetInt8Value( KeyH aKeyH, cAppCharP aValName, uInt8 &aValue )
+{
+ memSize vSize;
+ return GetValue( aKeyH, aValName, VALTYPE_INT8, &aValue, sizeof(aValue), vSize );
+} // GetInt8Value
+
+TSyError TEngineModuleBase::SetInt8Value( KeyH aKeyH, cAppCharP aValName, uInt8 aValue )
+{
+ return SetValue( aKeyH, aValName, VALTYPE_INT8, &aValue, sizeof(aValue) );
+} // SetInt8Value
+
+
+// ----------------------------------------------------------------------------------------------
+TSyError TEngineModuleBase::GetInt16Value( KeyH aKeyH, cAppCharP aValName, sInt16 &aValue )
+{
+ memSize vSize;
+ return GetValue( aKeyH, aValName, VALTYPE_INT16, &aValue, sizeof(aValue), vSize );
+} // GetInt16Value
+
+TSyError TEngineModuleBase::GetInt16Value( KeyH aKeyH, cAppCharP aValName, uInt16 &aValue )
+{
+ memSize vSize;
+ return GetValue( aKeyH, aValName, VALTYPE_INT16, &aValue, sizeof(aValue), vSize );
+} // GetInt16Value
+
+TSyError TEngineModuleBase::SetInt16Value( KeyH aKeyH, cAppCharP aValName, uInt16 aValue )
+{
+ return SetValue( aKeyH, aValName, VALTYPE_INT16, &aValue, sizeof(aValue) );
+} // SetInt16Value
+
+
+// ----------------------------------------------------------------------------------------------
+TSyError TEngineModuleBase::GetInt32Value( KeyH aKeyH, cAppCharP aValName, sInt32 &aValue )
+{
+ memSize vSize;
+ return GetValue( aKeyH, aValName, VALTYPE_INT32, &aValue, sizeof(aValue), vSize );
+} // GetInt32Value
+
+TSyError TEngineModuleBase::GetInt32Value( KeyH aKeyH, cAppCharP aValName, uInt32 &aValue )
+{
+ memSize vSize;
+ return GetValue( aKeyH, aValName, VALTYPE_INT32, &aValue, sizeof(aValue), vSize );
+} // GetInt32Value
+
+TSyError TEngineModuleBase::SetInt32Value( KeyH aKeyH, cAppCharP aValName, uInt32 aValue )
+{
+ return SetValue( aKeyH, aValName, VALTYPE_INT32, &aValue, sizeof(aValue) );
+} // SetInt32Value
+
+
+
+} // namespace sysync
+/* end of TEngineModuleBase implementation */
+
+#endif // not used in engine or engine with ENGINEINTERFACE_SUPPORT
+
+// eof
diff --git a/src/sysync_SDK/Sources/enginemodulebase.h b/src/sysync_SDK/Sources/enginemodulebase.h
new file mode 100755
index 0000000..2865e7b
--- /dev/null
+++ b/src/sysync_SDK/Sources/enginemodulebase.h
@@ -0,0 +1,379 @@
+/**
+ * @File enginemodulebase.h
+ *
+ * @Author Beat Forster (bfo@synthesis.ch)
+ *
+ * @brief TEngineModuleBase
+ * engine bus bar class
+ *
+ * Copyright (c) 2007-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ */
+
+#ifndef ENGINEMODULEBASE_H
+#define ENGINEMODULEBASE_H
+
+#include "generic_types.h"
+#include "sync_dbapidef.h"
+
+// we need STL strings
+#include <string>
+using namespace std;
+
+
+
+namespace sysync {
+
+// Engine module base
+class TEngineModuleBase
+{
+ public:
+ TEngineModuleBase(); // constructor
+ virtual ~TEngineModuleBase(); // destructor
+
+ UI_Call_In fCI; // call in structure
+ string fEngineName; // name of the SyncML engine to be connected
+ CVersion fPrgVersion; // program's SDK version
+ uInt16 fDebugFlags; // debug flags to be used
+ bool fCIisStatic; // call in structure is statically allocated, must to be deleted
+
+ TSyError Connect( string aEngineName, // connect the SyncML engine
+ CVersion aPrgVersion= 0,
+ uInt16 aDebugFlags= DBG_PLUGIN_NONE );
+
+ TSyError Disconnect(); // disconnect the SyncML engine
+
+ virtual TSyError Init() = 0;
+ virtual TSyError Term() = 0;
+
+
+ // Engine init
+ // -----------
+
+ /// @brief Set the global mode for string paramaters (when never called, default params are UTF-8 with C-style line ends)
+ /// @param aCharSet[in] charset
+ /// @param aLineEndMode[in] line end mode (default is C-lineends of the platform (almost always LF))
+ /// @param aBigEndian[in] determines endianness of UTF16 text (defaults to little endian = intel order)
+ /// @return LOCERR_OK on success, SyncML or LOCERR_xxx error code on failure
+ virtual TSyError SetStringMode ( uInt16 aCharSet,
+ uInt16 aLineEndMode= LEM_CSTR,
+ bool aBigEndian = false ) = 0;
+
+
+ /// @brief init object, optionally passing XML config text in memory
+ /// @param aConfigXML[in] NULL or empty string if no external config needed, config text otherwise
+ /// @return LOCERR_OK on success, SyncML or LOCERR_xxx error code on failure
+ virtual TSyError InitEngineXML ( cAppCharP aConfigXML ) = 0;
+
+ /// @brief init object, optionally passing a open FILE for reading config
+ /// @param aConfigFilePath[in] path to config file
+ /// @return LOCERR_OK on success, SyncML or LOCERR_xxx error code on failure
+ virtual TSyError InitEngineFile ( cAppCharP aConfigFilePath ) = 0;
+
+ /// @brief init object, optionally passing a callback for reading config
+ /// @param aReaderFunc[in] callback function which can deliver next chunk of XML config data
+ /// @param aContext[in] free context pointer passed back with callback
+ /// @return LOCERR_OK on success, SyncML or LOCERR_xxx error code on failure
+ virtual TSyError InitEngineCB ( TXMLConfigReadFunc aReaderFunc, void *aContext ) = 0;
+
+
+ // Running a Sync Session
+ // ----------------------
+
+ /// @brief Open a session
+ /// @param aNewSessionH[out] receives session handle for all session execution calls
+ /// @param aSelector[in] selector, depending on session type. For multi-profile clients: profile ID to use
+ /// @param aSessionName[in] a text name/id to identify a session, useage depending on session type.
+ /// @return LOCERR_OK on success, SyncML or LOCERR_xxx error code on failure
+ virtual TSyError OpenSession( SessionH &aNewSessionH, uInt32 aSelector=0, cAppCharP aSessionName=NULL ) = 0;
+
+ /// @brief open session specific runtime parameter/settings key
+ /// @note key handle obtained with this call must be closed BEFORE SESSION IS CLOSED!
+ /// @param aNewKeyH[out] receives the opened key's handle on success
+ /// @param aSessionH[in] session handle obtained with OpenSession.
+ /// When used as callback from DBApi, this parameter is irrelevant and
+ /// must be set to NULL as a callback from DBApi has an implicit session context
+ /// automatically.
+ /// @param aMode[in] the open mode
+ /// @return LOCERR_OK on success, SyncML or LOCERR_xxx error code on failure
+ virtual TSyError OpenSessionKey ( SessionH aSessionH, KeyH &aNewKeyH, uInt16 aMode ) = 0;
+
+ /// @brief Executes sync session or other sync related activity step by step
+ /// @param aSessionH[in] session handle obtained with OpenSession
+ /// @param aStepCmd[in/out] step command (STEPCMD_xxx):
+ /// - tells caller to send or receive data or end the session etc.
+ /// - instructs engine to suspend or abort the session etc.
+ /// @param aInfoP[in] pointer to a TEngineProgressInfo structure, NULL if no progress info needed
+ /// @return LOCERR_OK on success, SyncML or LOCERR_xxx error code on failure
+ virtual TSyError SessionStep( SessionH aSessionH, uInt16 &aStepCmd, TEngineProgressInfo *aInfoP = NULL ) = 0;
+
+ /// @brief Get access to SyncML message buffer
+ /// @param aSessionH[in] session handle obtained with OpenSession
+ /// @param aForSend[in] direction send/receive
+ /// @param aBuffer[out] receives pointer to buffer (empty for receive, full for send)
+ /// @param aBufSize[out] receives size of empty or full buffer
+ /// @return LOCERR_OK on success, SyncML or LOCERR_xxx error code on failure
+ virtual TSyError GetSyncMLBuffer( SessionH aSessionH, bool aForSend, appPointer &aBuffer, memSize &aBufSize ) = 0;
+
+ /// @brief Return SyncML message buffer to engine
+ /// @param aSessionH[in] session handle obtained with OpenSession
+ /// @param aForSend[in] direction send/receive
+ /// @param aProcessed[in] number of bytes put into or read from the buffer
+ /// @return LOCERR_OK on success, SyncML or LOCERR_xxx error code on failure
+ virtual TSyError RetSyncMLBuffer( SessionH aSessionH, bool aForSend, memSize aProcessed ) = 0;
+
+ /// @brief Read data from SyncML message buffer
+ /// @param aSessionH[in] session handle obtained with OpenSession
+ /// @param aBuffer[in] pointer to buffer
+ /// @param aBufSize[in] size of buffer, maximum to be read
+ /// @param aMsgSize[out] size of data available in the buffer for read INCLUDING just returned data.
+ /// @note If the aBufSize is too small to return all available data LOCERR_TRUNCATED will be returned, and the
+ /// caller can repeat calls to ReadSyncMLBuffer to get the next chunk.
+ /// @return LOCERR_OK on success, SyncML or LOCERR_xxx error code on failure
+ virtual TSyError ReadSyncMLBuffer ( SessionH aSessionH, appPointer aBuffer, memSize aBufSize, memSize &aMsgSize ) = 0;
+
+ /// @brief Write data to SyncML message buffer
+ /// @param aSessionH[in] session handle obtained with OpenSession
+ /// @param aBuffer[in] pointer to buffer
+ /// @param aMsgSize[in] size of message to write to the buffer
+ /// @return LOCERR_OK on success, SyncML or LOCERR_xxx error code on failure
+ virtual TSyError WriteSyncMLBuffer( SessionH aSessionH, appPointer aBuffer, memSize aMsgSize ) = 0;
+
+ /// @brief Close a session
+ /// @note It depends on session type if this also destroys the session or if it may persist and can be re-opened.
+ /// @param aSessionH[in] session handle obtained with OpenSession
+ /// @return LOCERR_OK on success, SyncML or LOCERR_xxx error code on failure
+ virtual TSyError CloseSession( SessionH aSessionH ) = 0;
+
+
+
+ // Settings access
+ // ---------------
+
+ /// @brief open Settings key by path specification
+ /// @param aNewKeyH[out] receives the opened key's handle on success
+ /// @param aParentKeyH[in] NULL if path is absolute from root, handle to an open key for relative access
+ /// @param aPath[in] the path specification as null terminated string
+ /// @param aMode[in] the open mode
+ /// @return LOCERR_OK on success, SyncML or LOCERR_xxx error code on failure
+ virtual TSyError OpenKeyByPath ( KeyH &aNewKeyH,
+ KeyH aParentKeyH,
+ cAppCharP aPath, uInt16 aMode ) = 0;
+
+
+ /// @brief open Settings subkey key by ID or iterating over all subkeys
+ /// @param aNewKeyH[out] receives the opened key's handle on success
+ /// @param aParentKeyH[in] handle to the parent key
+ /// @param aID[in] the ID of the subkey to open,
+ /// or KEYVAL_ID_FIRST/KEYVAL_ID_NEXT to iterate over existing subkeys
+ /// or KEYVAL_ID_NEW to create a new subkey
+ /// @param aMode[in] the open mode
+ /// @return LOCERR_OK on success, DB_NoContent when no more subkeys are found with
+ /// KEYVAL_ID_FIRST/KEYVAL_ID_NEXT
+ /// or any other SyncML or LOCERR_xxx error code on failure
+ virtual TSyError OpenSubkey ( KeyH &aNewKeyH,
+ KeyH aParentKeyH,
+ sInt32 aID, uInt16 aMode ) = 0;
+
+
+ /// @brief delete Settings subkey key by ID
+ /// @param aParentKeyH[in] handle to the parent key
+ /// @param aID[in] the ID of the subkey to delete
+ /// @return LOCERR_OK on success
+ /// or any other SyncML or LOCERR_xxx error code on failure
+ virtual TSyError DeleteSubkey ( KeyH aParentKeyH, sInt32 aID ) = 0;
+
+ /// @brief Get key ID of currently open key. Note that the Key ID is only locally unique within
+ /// the parent key.
+ /// @param aKeyH[in] an open key handle
+ /// @param aID[out] receives the ID of the open key, which can be used to re-access the
+ /// key within its parent using OpenSubkey()
+ /// @return LOCERR_OK on success, SyncML or LOCERR_xxx error code on failure
+ virtual TSyError GetKeyID ( KeyH aKeyH, sInt32 &aID ) = 0;
+
+
+ /// @brief Set text format parameters (when never called, default params are those set with global SetStringMode())
+ /// @param aKeyH[in] an open key handle
+ /// @param aCharSet[in] charset
+ /// @param aLineEndMode[in] line end mode (defaults to C-lineends of the platform (almost always LF))
+ /// @param aBigEndian[in] determines endianness of UTF16 text (defaults to little endian = intel order)
+ /// @return LOCERR_OK on success, SyncML or LOCERR_xxx error code on failure
+ virtual TSyError SetTextMode ( KeyH aKeyH, uInt16 aCharSet,
+ uInt16 aLineEndMode= LEM_CSTR,
+ bool aBigEndian = false ) = 0;
+
+ /// @brief Set time format parameters
+ /// @param aKeyH[in] an open key handle
+ /// @param aTimeMode[in] time mode, see TMODE_xxx (default is platform's lineratime_t when SetTimeMode() is not used)
+ /// @return LOCERR_OK on success, SyncML or LOCERR_xxx error code on failure
+ virtual TSyError SetTimeMode ( KeyH aKeyH, uInt16 aTimeMode ) = 0;
+
+
+ /// @brief Closes a key opened by OpenKeyByPath() or OpenSubKey()
+ /// @param aKeyH[in] an open key handle. Will be invalid when call returns with LOCERR_OK. Do not re-use!
+ /// @return LOCERR_OK on success, SyncML or LOCERR_xxx error code on failure
+ virtual TSyError CloseKey ( KeyH aKeyH ) = 0;
+
+
+ /// @brief Reads a named value in specified format into passed memory buffer
+ /// @param aKeyH[in] an open key handle
+ /// @param aValueName[in] name of the value to read. Some keys offer special ".XXX"
+ /// suffixes to value names, which return alternate values (like a timestamp field's
+ /// time zone name with ".TZNAME").
+ /// @param aValType[in] desired return type, see VALTYPE_xxxx
+ /// @param aBuffer[in/out] buffer where to store the data
+ /// @param aBufSize[in] size of buffer in bytes (ALWAYS in bytes, even if value is Unicode string)
+ /// Note: to get only size of a value (useful especially for strings), pass 0 as buffer size.
+ /// @param aValSize[out] actual size of value.
+ /// For VALTYPE_TEXT, size is string length (IN BYTES) excluding NULL terminator
+ /// Note that this will be set also when return value is LOCERR_BUFTOOSMALL,
+ /// to indicate the required buffer size
+ /// For values that can be truncated (strings), LOCERR_TRUNCATED will be returned
+ /// when returned value is not entire value - aValSize is truncated size then.
+ /// @return LOCERR_OK on success, SyncML or LOCERR_xxx error code on failure
+ virtual TSyError GetValue ( KeyH aKeyH, cAppCharP aValName, uInt16 aValType,
+ appPointer aBuffer, memSize aBufSize, memSize &aValSize ) = 0;
+
+ /// @brief get value's ID for use with Get/SetValueByID()
+ /// @param aKeyH[in] an open key handle
+ /// @param aName[in] name of the value to write. Some keys offer special ".FLAG.XXX"
+ /// values, which return flag bits which can be added to the regular ID to obtain
+ /// alternate values (like the value name with ".FLAG.VALNAME", useful when iterating
+ /// over values).
+ /// @return KEYVAL_ID_UNKNOWN when no ID available for name, ID of value otherwise
+ virtual sInt32 GetValueID ( KeyH aKeyH, cAppCharP aName ) = 0;
+
+
+ /// @brief Reads a named value in specified format into passed memory buffer
+ /// @param aKeyH[in] an open key handle
+ /// @param aID[in] ID of the value to read
+ /// @param arrIndex[in] 0-based array element index for array values.
+ /// @param aValType[in] desired return type, see VALTYPE_xxxx
+ /// @param aBuffer[in/out] buffer where to store the data
+ /// @param aBufSize[in] size of buffer in bytes (ALWAYS in bytes, even if value is Unicode string)
+ /// Note: to get only size of a value (useful especially for strings), pass 0 as buffer size.
+ /// @param aValSize[out] actual size of value.
+ /// For VALTYPE_TEXT, size is string length (IN BYTES) excluding NULL terminator
+ /// Note that this will be set also when return value is LOCERR_BUFTOOSMALL,
+ /// to indicate the required buffer size
+ /// For values that can be truncated (strings), LOCERR_TRUNCATED will be returned
+ /// when returned value is not entire value - aValSize is truncated size then.
+ /// @return LOCERR_OK on success, LOCERR_OUTOFRANGE when array index is out of range
+ /// SyncML or LOCERR_xxx error code on other failure
+ virtual TSyError GetValueByID ( KeyH aKeyH, sInt32 aID, sInt32 arrIndex,
+ uInt16 aValType,
+ appPointer aBuffer, memSize aBufSize, memSize &aValSize ) = 0;
+
+
+ /// @brief Writes a named value in specified format passed in memory buffer
+ /// @param aKeyH[in] an open key handle
+ /// @param aValueName[in] name of the value to write. Some keys offer special ".XXX"
+ /// suffixes to value names, which are used to set alternate values (like a
+ /// timestamp field's time zone name with ".TZNAME").
+ /// @param aValType[in] type of value passed in, see VALTYPE_xxxx
+ /// @param aBuffer[in] buffer containing the data
+ /// @param aValSize[in] size of value. For VALTYPE_TEXT, size can be passed as -1 if string is null terminated
+ /// @return LOCERR_OK on success, SyncML or LOCERR_xxx error code on failure. If value
+ /// buffer passed in is too small for aValType (such as only 2 bytes for a VALTYPE_INT32,
+ /// LOCERR_BUFTOOSMALL will be returned and nothing stored. If buffer passed in is too
+ /// long (e.g. for strings) to be entirely stored, only the beginning is stored and
+ /// LOCERR_TRUNCATED is returned.
+ virtual TSyError SetValue ( KeyH aKeyH, cAppCharP aValName, uInt16 aValType,
+ cAppPointer aBuffer, memSize aValSize ) = 0;
+
+
+ /// @brief Writes a named value in specified format passed in memory buffer
+ /// @param aKeyH[in] an open key handle
+ /// @param aID[in] ID of the value to read
+ /// @param arrIndex[in] 0-based array element index for array values.
+ /// @param aValType[in] type of value passed in, see VALTYPE_xxxx
+ /// @param aBuffer[in] buffer containing the data
+ /// @param aValSize[in] size of value. For VALTYPE_TEXT, size can be passed as -1 if string is null terminated
+ /// @return LOCERR_OK on success, SyncML or LOCERR_xxx error code on failure. If value
+ /// buffer passed in is too small for aValType (such as only 2 bytes for a VALTYPE_INT32,
+ /// LOCERR_BUFTOOSMALL will be returned and nothing stored. If buffer passed in is too
+ /// long (e.g. for strings) to be entirely stored, only the beginning is stored and
+ /// LOCERR_TRUNCATED is returned.
+ virtual TSyError SetValueByID ( KeyH aKeyH, sInt32 aID, sInt32 arrIndex,
+ uInt16 aValType,
+ cAppPointer aBuffer, memSize aValSize ) = 0;
+
+
+ // Convenience routines to close and NULL a handle in one step
+ // -----------------------------------------------------------
+
+ /// @brief Closes a key and nulls the handle
+ /// @param aKeyH[in/out] an open key handle. Will be set to NULL on exit (to make sure it is not re-used)
+ /// @return LOCERR_OK on success, SyncML or LOCERR_xxx error code on failure
+ TSyError CloseKeyAndNULL ( KeyH &aKeyH )
+ {
+ TSyError sta = CloseKey(aKeyH);
+ if (sta==LOCERR_OK) aKeyH=NULL;
+ return sta;
+ };
+
+ /// @brief Closes a session and nulls the handle
+ /// @param aSessionH[in] session handle obtained with OpenSession
+ /// @return LOCERR_OK on success, SyncML or LOCERR_xxx error code on failure
+ TSyError CloseSessionAndNULL ( SessionH &aSessionH )
+ {
+ TSyError sta = CloseSession(aSessionH);
+ if (sta==LOCERR_OK) aSessionH=NULL;
+ return sta;
+ };
+
+
+ // Convenience routines to access Get/SetValue for specific types
+ // --------------------------------------------------------------
+ virtual TSyError GetStrValue ( KeyH aKeyH, cAppCharP aValName, string &aText );
+ virtual TSyError SetStrValue ( KeyH aKeyH, cAppCharP aValName, string aText );
+
+ virtual TSyError GetInt8Value ( KeyH aKeyH, cAppCharP aValName, sInt8 &aValue );
+ virtual TSyError GetInt8Value ( KeyH aKeyH, cAppCharP aValName, uInt8 &aValue );
+ virtual TSyError SetInt8Value ( KeyH aKeyH, cAppCharP aValName, uInt8 aValue );
+
+ virtual TSyError GetInt16Value ( KeyH aKeyH, cAppCharP aValName, sInt16 &aValue );
+ virtual TSyError GetInt16Value ( KeyH aKeyH, cAppCharP aValName, uInt16 &aValue );
+ virtual TSyError SetInt16Value ( KeyH aKeyH, cAppCharP aValName, uInt16 aValue );
+
+ virtual TSyError GetInt32Value ( KeyH aKeyH, cAppCharP aValName, sInt32 &aValue );
+ virtual TSyError GetInt32Value ( KeyH aKeyH, cAppCharP aValName, uInt32 &aValue );
+ virtual TSyError SetInt32Value ( KeyH aKeyH, cAppCharP aValName, uInt32 aValue );
+
+
+ // Tunnel Interface Methods ---------------------------------------------------------------------
+ virtual TSyError StartDataRead ( CContext ac, cAppCharP lastToken,
+ cAppCharP resumeToken ) = 0;
+ virtual TSyError ReadNextItem ( CContext ac, ItemID aID, appCharP *aItemData,
+ sInt32 *aStatus, bool aFirst ) = 0;
+ virtual TSyError ReadItem ( CContext ac, cItemID aID, appCharP *aItemData ) = 0;
+ virtual TSyError EndDataRead ( CContext ac ) = 0;
+ virtual TSyError StartDataWrite ( CContext ac ) = 0;
+ virtual TSyError InsertItem ( CContext ac, cAppCharP aItemData, cItemID aID )= 0;
+ virtual TSyError UpdateItem ( CContext ac, cAppCharP aItemData, cItemID aID,
+ ItemID updID )= 0;
+
+ virtual TSyError MoveItem ( CContext ac, cItemID aID, cAppCharP newParID )= 0;
+ virtual TSyError DeleteItem ( CContext ac, cItemID aID ) = 0;
+ virtual TSyError EndDataWrite ( CContext ac, bool success, appCharP *newToken )= 0;
+ virtual void DisposeObj ( CContext ac, void* memory ) = 0;
+
+ // -- asKey --
+ virtual TSyError ReadNextItemAsKey( CContext ac, ItemID aID, KeyH aItemKey,
+ sInt32 *aStatus, bool aFirst )= 0;
+ virtual TSyError ReadItemAsKey ( CContext ac, cItemID aID, KeyH aItemKey )= 0;
+
+ virtual TSyError InsertItemAsKey ( CContext ac, KeyH aItemKey, cItemID aID )= 0;
+ virtual TSyError UpdateItemAsKey ( CContext ac, KeyH aItemKey, cItemID aID,
+ ItemID updID )= 0;
+}; // TEngineModuleBase
+
+
+// factory function declaration - must be implemented in the source file of the leaf derivate of TEngineInterface
+TEngineModuleBase *newEngine(void);
+
+} // namespace sysync
+#endif // ENGINEMODULEBASE_H
+
+
+// eof
diff --git a/src/sysync_SDK/Sources/enginemodulebridge.cpp b/src/sysync_SDK/Sources/enginemodulebridge.cpp
new file mode 100644
index 0000000..37e51b6
--- /dev/null
+++ b/src/sysync_SDK/Sources/enginemodulebridge.cpp
@@ -0,0 +1,368 @@
+/*
+ * File: enginemodulebridge.cpp
+ *
+ * Author: Beat Forster (bfo@synthesis.ch)
+ *
+ *
+ * Synthesis SyncML client test connector
+ *
+ * Copyright (c) 2007-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ *
+ */
+
+
+#include "enginemodulebridge.h" // include the interface file and utilities
+#include "SDK_util.h"
+#include "UI_util.h"
+
+
+namespace sysync {
+
+
+// local name
+#define MyName "enginemodulebridge"
+
+
+
+// --------------------------------------------------------------
+TEngineModuleBridge::TEngineModuleBridge() {
+ fDLL= NULL;
+} // constructor
+
+
+TEngineModuleBridge::~TEngineModuleBridge() {
+ Term();
+} // destructor
+
+
+
+// --------------------------------------------------------------
+TSyError TEngineModuleBridge::Init()
+{
+ TSyError err= UI_Connect( fCI, fDLL, fEngineName.c_str(), fPrgVersion, fDebugFlags );
+ return err;
+} // Init
+
+
+TSyError TEngineModuleBridge::Term()
+{
+ TSyError err= LOCERR_OK;
+ if (fCI) { err= UI_Disconnect( fCI, fDLL ); fCI= NULL; }
+ return err;
+} // Term
+
+
+// --------------------------------------------------------------
+TSyError TEngineModuleBridge::SetStringMode( uInt16 aCharSet,
+ uInt16 aLineEndMode, bool aBigEndian )
+{
+ SetStringMode_Func p= fCI->ui.SetStringMode; if (!p) return LOCERR_NOTIMP;
+ return p( fCI, aCharSet, aLineEndMode, aBigEndian );
+} // SetStringMode
+
+
+TSyError TEngineModuleBridge::InitEngineXML( cAppCharP aConfigXML )
+{
+ InitEngineXML_Func p= fCI->ui.InitEngineXML; if (!p) return LOCERR_NOTIMP;
+ return p( fCI, aConfigXML );
+} // InitEngineXML
+
+
+TSyError TEngineModuleBridge::InitEngineFile( cAppCharP aConfigFilePath )
+{
+ InitEngineFile_Func p= fCI->ui.InitEngineFile; if (!p) return LOCERR_NOTIMP;
+ return p( fCI, aConfigFilePath );
+} // InitEngineFile
+
+
+TSyError TEngineModuleBridge::InitEngineCB( TXMLConfigReadFunc aReaderFunc, void* aContext )
+{
+ InitEngineCB_Func p= fCI->ui.InitEngineCB; if (!p) return LOCERR_NOTIMP;
+ return p( fCI, aReaderFunc, aContext );
+} // InitEngineCB
+
+
+
+// --------------------------------------------------------------
+TSyError TEngineModuleBridge::OpenSession( SessionH &aSessionH, uInt32 aSelector,
+ cAppCharP aSessionName )
+{
+ OpenSession_Func p= fCI->ui.OpenSession; if (!p) return LOCERR_NOTIMP;
+ return p( fCI, &aSessionH, aSelector, aSessionName );
+} // OpenSession
+
+
+TSyError TEngineModuleBridge::OpenSessionKey( SessionH aSessionH,
+ KeyH &aKeyH, uInt16 aMode )
+{
+ OpenSessionKey_Func p= fCI->ui.OpenSessionKey; if (!p) return LOCERR_NOTIMP;
+ return p( fCI, aSessionH, &aKeyH, aMode );
+} // OpenSessionKey
+
+
+TSyError TEngineModuleBridge::SessionStep( SessionH aSessionH, uInt16 &aStepCmd,
+ TEngineProgressInfo *aInfoP )
+{
+ SessionStep_Func p= fCI->ui.SessionStep; if (!p) return LOCERR_NOTIMP;
+ return p( fCI, aSessionH, &aStepCmd, aInfoP );
+} // SessionStep
+
+
+TSyError TEngineModuleBridge::GetSyncMLBuffer( SessionH aSessionH, bool aForSend,
+ appPointer &aBuffer, memSize &aBufSize )
+{
+ GetSyncMLBuffer_Func p= fCI->ui.GetSyncMLBuffer; if (!p) return LOCERR_NOTIMP;
+ return p( fCI, aSessionH, aForSend, &aBuffer, &aBufSize );
+} // GetSyncMLBuffer
+
+
+TSyError TEngineModuleBridge::RetSyncMLBuffer( SessionH aSessionH, bool aForSend,
+ memSize aRetSize )
+{
+ RetSyncMLBuffer_Func p= fCI->ui.RetSyncMLBuffer; if (!p) return LOCERR_NOTIMP;
+ return p( fCI, aSessionH, aForSend, aRetSize );
+} // RetSyncMLBuffer
+
+
+TSyError TEngineModuleBridge::ReadSyncMLBuffer( SessionH aSessionH,
+ appPointer aBuffer, memSize aBufSize,
+ memSize &aValSize )
+{
+ ReadSyncMLBuffer_Func p= fCI->ui.ReadSyncMLBuffer; if (!p) return LOCERR_NOTIMP;
+ return p( fCI, aSessionH, aBuffer, aBufSize, &aValSize );
+} // ReadSyncMLBuffer
+
+
+TSyError TEngineModuleBridge::WriteSyncMLBuffer( SessionH aSessionH,
+ appPointer aBuffer, memSize aValSize )
+{
+ WriteSyncMLBuffer_Func p= fCI->ui.WriteSyncMLBuffer; if (!p) return LOCERR_NOTIMP;
+ return p( fCI, aSessionH, aBuffer, aValSize );
+} // WriteSyncMLBuffer
+
+
+TSyError TEngineModuleBridge::CloseSession( SessionH aSessionH )
+{
+ CloseSession_Func p= fCI->ui.CloseSession; if (!p) return LOCERR_NOTIMP;
+ return p( fCI, aSessionH );
+} // CloseSession
+
+
+
+// --------------------------------------------------------------
+TSyError TEngineModuleBridge::OpenKeyByPath( KeyH &aKeyH,
+ KeyH aParentKeyH, cAppCharP aPath,
+ uInt16 aMode )
+{
+ OpenKeyByPath_Func p= fCI->ui.OpenKeyByPath; if (!p) return LOCERR_NOTIMP;
+ return p( fCI, &aKeyH, aParentKeyH, aPath, aMode );
+} // OpenKeyByPath
+
+
+TSyError TEngineModuleBridge::OpenSubkey( KeyH &aKeyH,
+ KeyH aParentKeyH, sInt32 aID,
+ uInt16 aMode )
+{
+ OpenSubkey_Func p= fCI->ui.OpenSubkey; if (!p) return LOCERR_NOTIMP;
+ return p( fCI, &aKeyH, aParentKeyH, aID, aMode );
+} // OpenSubKey
+
+
+TSyError TEngineModuleBridge::DeleteSubkey( KeyH aParentKeyH, sInt32 aID )
+{
+ DeleteSubkey_Func p= fCI->ui.DeleteSubkey; if (!p) return LOCERR_NOTIMP;
+ return p( fCI, aParentKeyH, aID );
+} // DeleteSubkey
+
+
+TSyError TEngineModuleBridge::GetKeyID( KeyH aKeyH, sInt32 &aID )
+{
+ GetKeyID_Func p= fCI->ui.GetKeyID; if (!p) return LOCERR_NOTIMP;
+ return p( fCI, aKeyH, &aID );
+} // GetKeyID
+
+
+TSyError TEngineModuleBridge::SetTextMode( KeyH aKeyH, uInt16 aCharSet, uInt16 aLineEndMode,
+ bool aBigEndian )
+{
+ SetTextMode_Func p= fCI->ui.SetTextMode; if (!p) return LOCERR_NOTIMP;
+ return p( fCI, aKeyH, aCharSet, aLineEndMode, aBigEndian );
+} // SetTextMode
+
+
+TSyError TEngineModuleBridge::SetTimeMode( KeyH aKeyH, uInt16 aTimeMode )
+{
+ SetTimeMode_Func p= fCI->ui.SetTimeMode; if (!p) return LOCERR_NOTIMP;
+ return p( fCI, aKeyH, aTimeMode );
+} // SetTimeMode
+
+
+TSyError TEngineModuleBridge::CloseKey( KeyH aKeyH )
+{
+ CloseKey_Func p= fCI->ui.CloseKey; if (!p) return LOCERR_NOTIMP;
+ return p( fCI, aKeyH );
+} // CloseKey
+
+
+
+// --------------------------------------------------------------
+TSyError TEngineModuleBridge::GetValue( KeyH aKeyH, cAppCharP aValName, uInt16 aValType,
+ appPointer aBuffer, memSize aBufSize, memSize &aValSize )
+{
+ GetValue_Func p= fCI->ui.GetValue; if (!p) return LOCERR_NOTIMP;
+ return p( fCI, aKeyH, aValName, aValType, aBuffer,aBufSize,&aValSize );
+} // GetValue
+
+
+TSyError TEngineModuleBridge::GetValueByID( KeyH aKeyH, sInt32 aID, sInt32 arrIndex,
+ uInt16 aValType,
+ appPointer aBuffer, memSize aBufSize, memSize &aValSize )
+{
+ GetValueByID_Func p= fCI->ui.GetValueByID; if (!p) return LOCERR_NOTIMP;
+ return p( fCI, aKeyH, aID, arrIndex, aValType, aBuffer,aBufSize,&aValSize );
+} // GetValueByID
+
+
+sInt32 TEngineModuleBridge::GetValueID( KeyH aKeyH, cAppCharP aName )
+{
+ GetValueID_Func p= fCI->ui.GetValueID; if (!p) return 0;
+ return p( fCI, aKeyH, aName );
+} // GetValueID
+
+
+TSyError TEngineModuleBridge::SetValue( KeyH aKeyH, cAppCharP aValName, uInt16 aValType,
+ cAppPointer aBuffer, memSize aValSize )
+{
+ SetValue_Func p= fCI->ui.SetValue; if (!p) return LOCERR_NOTIMP;
+ return p( fCI, aKeyH, aValName, aValType, aBuffer,aValSize );
+} // SetValue
+
+
+TSyError TEngineModuleBridge::SetValueByID( KeyH aKeyH, sInt32 aID, sInt32 arrIndex,
+ uInt16 aValType,
+ cAppPointer aBuffer, memSize aValSize )
+{
+ SetValueByID_Func p= fCI->ui.SetValueByID; if (!p) return LOCERR_NOTIMP;
+ return p( fCI, aKeyH, aID, arrIndex, aValType, aBuffer,aValSize );
+} // SetValueByID
+
+
+
+// ---- tunnel functions -----------------------------------------------------------------
+TSyError TEngineModuleBridge::StartDataRead( CContext aContext, cAppCharP lastToken,
+ cAppCharP resumeToken )
+{
+ SDR_Func p= fCI->dt.StartDataRead; if (!p) return LOCERR_NOTIMP;
+ return p( aContext, lastToken, resumeToken );
+} // StartDataRead
+
+
+TSyError TEngineModuleBridge::ReadNextItem( CContext aContext, ItemID aID, appCharP *aItemData,
+ sInt32 *aStatus, bool aFirst )
+{
+ RdNItemSFunc p= fCI->dt.ReadNextItem; if (!p) return LOCERR_NOTIMP;
+ return p( aContext, aID, aItemData, aStatus, aFirst );
+} // ReadNextItem
+
+
+TSyError TEngineModuleBridge::ReadItem( CContext aContext, cItemID aID, appCharP *aItemData )
+{
+ Rd_ItemSFunc p= fCI->dt.ReadItem; if (!p) return LOCERR_NOTIMP;
+ return p( aContext, aID, aItemData );
+} // ReadItem
+
+
+TSyError TEngineModuleBridge::EndDataRead( CContext aContext )
+{
+ EDR_Func p= fCI->dt.EndDataRead; if (!p) return LOCERR_NOTIMP;
+ return p( aContext );
+} // EndDataRead
+
+
+TSyError TEngineModuleBridge::StartDataWrite( CContext aContext )
+{
+ SDW_Func p= fCI->dt.StartDataWrite; if (!p) return LOCERR_NOTIMP;
+ return p( aContext );
+} // StartDataWrite
+
+
+TSyError TEngineModuleBridge::InsertItem( CContext aContext, cAppCharP aItemData, cItemID aID )
+{
+ InsItemSFunc p= fCI->dt.InsertItem; if (!p) return LOCERR_NOTIMP;
+ return p( aContext, aItemData, aID );
+} // InsertItem
+
+
+TSyError TEngineModuleBridge::UpdateItem( CContext aContext, cAppCharP aItemData, cItemID aID,
+ ItemID updID )
+{
+ UpdItemSFunc p= fCI->dt.UpdateItem; if (!p) return LOCERR_NOTIMP;
+ return p( aContext, aItemData, aID, updID );
+} // UpdateItem
+
+
+TSyError TEngineModuleBridge::MoveItem( CContext aContext, cItemID aID, cAppCharP newParID )
+{
+ MovItem_Func p= fCI->dt.MoveItem; if (!p) return LOCERR_NOTIMP;
+ return p( aContext, aID, newParID );
+} // MoveItem
+
+
+TSyError TEngineModuleBridge::DeleteItem( CContext aContext, cItemID aID )
+{
+ DelItem_Func p= fCI->dt.DeleteItem; if (!p) return LOCERR_NOTIMP;
+ return p( aContext, aID );
+} // DeleteItem
+
+
+TSyError TEngineModuleBridge::EndDataWrite( CContext aContext, bool success, appCharP *newToken )
+{
+ EDW_Func p= fCI->dt.EndDataWrite; if (!p) return LOCERR_NOTIMP;
+ return p( aContext, success, newToken );
+} // EndDataWrite
+
+
+void TEngineModuleBridge::DisposeObj( CContext aContext, void* memory )
+{
+ DisposeProc p= fCI->dt.DisposeObj; if (!p) return;
+ p( aContext, memory );
+} // DisposeObj
+
+
+// --- asKey ---
+TSyError TEngineModuleBridge::ReadNextItemAsKey( CContext aContext, ItemID aID, KeyH aItemKey,
+ sInt32 *aStatus, bool aFirst )
+{
+ RdNItemKFunc p= fCI->dt.ReadNextItemAsKey; if (!p) return LOCERR_NOTIMP;
+ return p( aContext, aID, aItemKey, aStatus, aFirst );
+} // ReadNextItemAsKey
+
+
+TSyError TEngineModuleBridge::ReadItemAsKey( CContext aContext, cItemID aID, KeyH aItemKey )
+{
+ Rd_ItemKFunc p= fCI->dt.ReadItemAsKey; if (!p) return LOCERR_NOTIMP;
+ return p( aContext, aID, aItemKey );
+} // ReadItemAsKey
+
+
+TSyError TEngineModuleBridge::InsertItemAsKey( CContext aContext, KeyH aItemKey, cItemID aID )
+{
+ InsItemKFunc p= fCI->dt.InsertItemAsKey; if (!p) return LOCERR_NOTIMP;
+ return p( aContext, aItemKey, aID );
+} // InsertItemAsKey
+
+
+TSyError TEngineModuleBridge::UpdateItemAsKey( CContext aContext, KeyH aItemKey, cItemID aID,
+ ItemID updID )
+{
+ UpdItemKFunc p= fCI->dt.UpdateItemAsKey; if (!p) return LOCERR_NOTIMP;
+ return p( aContext, aItemKey, aID, updID );
+} // UpdateItemAsKey
+
+
+
+
+
+} // namespace sysync
+/* eof */
diff --git a/src/sysync_SDK/Sources/enginemodulebridge.h b/src/sysync_SDK/Sources/enginemodulebridge.h
new file mode 100644
index 0000000..9834b23
--- /dev/null
+++ b/src/sysync_SDK/Sources/enginemodulebridge.h
@@ -0,0 +1,110 @@
+/*
+ * File: enginemodulebridge.h
+ *
+ * Author: Beat Forster (bfo@synthesis.ch)
+ *
+ *
+ * Synthesis SyncML client bridge connector
+ *
+ * Copyright (c) 2007-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ *
+ */
+
+
+#ifndef ENGINEMODULEBRIDGE_H
+#define ENGINEMODULEBRIDGE_H
+
+// inherited from ...
+#include "enginemodulebase.h"
+
+
+namespace sysync {
+
+
+class TEngineModuleBridge : public TEngineModuleBase
+{
+ typedef TEngineModuleBase inherited;
+
+ public:
+ TEngineModuleBridge();
+ virtual ~TEngineModuleBridge();
+
+ appPointer fDLL;
+
+ virtual TSyError Init();
+ virtual TSyError Term();
+
+ // ---- Engine init -------------------------------------------------------------------------
+ virtual TSyError SetStringMode ( uInt16 aCharSet,
+ uInt16 aLineEndMode= LEM_CSTR, bool aBigEndian= false );
+
+ virtual TSyError InitEngineXML ( cAppCharP aConfigXML );
+ virtual TSyError InitEngineFile ( cAppCharP aConfigFilePath );
+ virtual TSyError InitEngineCB ( TXMLConfigReadFunc aReaderFunc, void* aContext );
+
+ // ---- Running a Sync Session --------------------------------------------------------------
+ virtual TSyError OpenSession ( SessionH &aSessionH, uInt32 aSelector = 0,
+ cAppCharP aSessionName = NULL );
+ virtual TSyError OpenSessionKey ( SessionH aSessionH, KeyH &aKeyH, uInt16 aMode );
+ virtual TSyError SessionStep ( SessionH aSessionH, uInt16 &aStepCmd, TEngineProgressInfo *aInfoP= NULL );
+ virtual TSyError GetSyncMLBuffer ( SessionH aSessionH, bool aForSend,
+ appPointer &aBuffer, memSize &aBufSize );
+ virtual TSyError RetSyncMLBuffer ( SessionH aSessionH, bool aForSend, memSize aRetSize );
+ virtual TSyError ReadSyncMLBuffer ( SessionH aSessionH,
+ appPointer aBuffer, memSize aBufSize, memSize &aValSize );
+ virtual TSyError WriteSyncMLBuffer( SessionH aSessionH,
+ appPointer aBuffer, memSize aValSize );
+ virtual TSyError CloseSession ( SessionH aSessionH );
+
+ // ---- Settings access ---------------------------------------------------------------------
+ virtual TSyError OpenKeyByPath ( KeyH &aKeyH,
+ KeyH aParentKeyH, cAppCharP aPath, uInt16 aMode );
+ virtual TSyError OpenSubkey ( KeyH &aKeyH,
+ KeyH aParentKeyH, sInt32 aID, uInt16 aMode );
+ virtual TSyError DeleteSubkey ( KeyH aParentKeyH, sInt32 aID );
+ virtual TSyError GetKeyID ( KeyH aKeyH, sInt32 &aID );
+ virtual TSyError SetTextMode ( KeyH aKeyH, uInt16 aCharSet, uInt16 aLineEndMode= LEM_CSTR,
+ bool aBigEndian = false );
+ virtual TSyError SetTimeMode ( KeyH aKeyH, uInt16 aTimeMode );
+ virtual TSyError CloseKey ( KeyH aKeyH );
+
+ virtual TSyError GetValue ( KeyH aKeyH, cAppCharP aValName, uInt16 aValType,
+ appPointer aBuffer, memSize aBufSize, memSize &aValSize );
+ virtual TSyError GetValueByID ( KeyH aKeyH, sInt32 aID, sInt32 arrIndex,
+ uInt16 aValType,
+ appPointer aBuffer, memSize aBufSize, memSize &aValSize );
+ virtual sInt32 GetValueID ( KeyH aKeyH, cAppCharP aName );
+
+ virtual TSyError SetValue ( KeyH aKeyH, cAppCharP aValName, uInt16 aValType,
+ cAppPointer aBuffer, memSize aValSize );
+ virtual TSyError SetValueByID ( KeyH aKeyH, sInt32 aID, sInt32 arrIndex,
+ uInt16 aValType,
+ cAppPointer aBuffer, memSize aValSize );
+
+ // ---- Tunnel methods ----------------------------------------------------------------------
+ virtual TSyError StartDataRead ( CContext ac, cAppCharP lastToken,
+ cAppCharP resumeToken );
+ virtual TSyError ReadNextItem ( CContext ac, ItemID aID, appCharP *aItemData,
+ sInt32 *aStatus, bool aFirst );
+ virtual TSyError ReadItem ( CContext ac, cItemID aID, appCharP *aItemData );
+ virtual TSyError EndDataRead ( CContext ac );
+ virtual TSyError StartDataWrite ( CContext ac );
+ virtual TSyError InsertItem ( CContext ac, cAppCharP aItemData, cItemID aID );
+ virtual TSyError UpdateItem ( CContext ac, cAppCharP aItemData, cItemID aID, ItemID updID );
+ virtual TSyError MoveItem ( CContext ac, cItemID aID, cAppCharP newParID );
+ virtual TSyError DeleteItem ( CContext ac, cItemID aID );
+ virtual TSyError EndDataWrite ( CContext ac, bool success, appCharP *newToken );
+ virtual void DisposeObj ( CContext ac, void* memory );
+
+ // ---- asKey ----
+ virtual TSyError ReadNextItemAsKey( CContext ac, ItemID aID, KeyH aItemKey,
+ sInt32 *aStatus, bool aFirst );
+ virtual TSyError ReadItemAsKey ( CContext ac, cItemID aID, KeyH aItemKey );
+ virtual TSyError InsertItemAsKey ( CContext ac, KeyH aItemKey, cItemID aID );
+ virtual TSyError UpdateItemAsKey ( CContext ac, KeyH aItemKey, cItemID aID, ItemID updID );
+}; // TEngineModuleBridge
+
+} // namespace
+#endif // ENGINEMODULEBRIDGE
+/* eof */
diff --git a/src/sysync_SDK/Sources/generic_types.h b/src/sysync_SDK/Sources/generic_types.h
new file mode 100755
index 0000000..ebc4439
--- /dev/null
+++ b/src/sysync_SDK/Sources/generic_types.h
@@ -0,0 +1,139 @@
+/* generic types to avoid int size dependencies */
+/* ============================================ */
+
+/* NOTE: this file is part of the mod_sysync source files. */
+
+/* NOTE: As this file should work for all kind of plain C compilers
+ * comments with double slashes "//" must be avoided !!
+ */
+
+#ifndef GENERIC_TYPES_H
+#define GENERIC_TYPES_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#if defined(HAVE_STDINT_H)
+# include <stdint.h>
+#endif
+
+#ifdef __cplusplus
+ namespace sysync {
+#endif
+
+#if defined(HAVE_STDINT_H)
+typedef uint64_t uInt64;
+typedef int64_t sInt64;
+typedef uint32_t uInt32;
+typedef int32_t sInt32;
+typedef uint16_t uInt16;
+typedef int16_t sInt16;
+typedef uint8_t uInt8;
+typedef int8_t sInt8;
+
+typedef uintptr_t uIntPtr;
+typedef intptr_t sIntPtr;
+typedef uintptr_t bufferIndex; /* index into app buffers (small platforms may have 16bit here) */
+typedef uintptr_t stringIndex; /* index into string (small platforms may have 16bit here) */
+typedef uintptr_t stringSize; /* size of a string object */
+typedef uintptr_t memSize; /* size of a memory buffer */
+
+#else
+
+/* defined size types */
+/* - integers */
+#ifdef _MSC_VER
+typedef unsigned __int64 uInt64;
+typedef signed __int64 sInt64;
+#else
+typedef unsigned long long uInt64;
+typedef signed long long sInt64;
+#endif
+
+/*
+#ifdef __PALM_OS__
+*/
+typedef unsigned long uInt32;
+typedef signed long sInt32;
+/*
+#else
+ typedef unsigned int uInt32; // according to the ILP32/LP64 std for all other platforms
+ typedef signed int sInt32; // %%%% requires some type cast fixing first %%%%
+#endif
+*/
+
+typedef unsigned short uInt16;
+typedef signed short sInt16;
+typedef unsigned char uInt8;
+typedef signed char sInt8;
+
+#if __WORDSIZE == 64
+typedef unsigned long long uIntPtr;
+typedef signed long long sIntPtr;
+#else
+typedef unsigned long uIntPtr;
+typedef signed long sIntPtr;
+#endif
+
+/* - application integers */
+typedef uInt32 bufferIndex; /* index into app buffers (small platforms may have 16bit here) */
+typedef uInt32 stringIndex; /* index into string (small platforms may have 16bit here) */
+typedef uInt32 stringSize; /* size of a string object */
+typedef uInt32 memSize; /* size of a memory buffer */
+
+#endif /* HAVE_STDINT_H */
+
+#ifndef __WORDSIZE
+/* 64 platforms should have this defined, so assume that */
+/* platforms without the define are 32 bit. */
+# define __WORDSIZE 32
+#endif
+
+/* undefined size types */
+/* - application chars & pointers */
+typedef char appChar;
+typedef char *appCharP;
+typedef const char *cAppCharP;
+/* - application bool */
+#ifdef __cplusplus
+ typedef bool appBool;
+ /* Just define application-specific true and false constants */
+ #define appFalse false
+ #define appTrue true
+#else
+ /* we have no appBool in C, as C++/C mixture could cause bad */
+ /* surprises if types were differently defined in C and C++ */
+ /* Just define application-specific true and false constants */
+ #define appFalse 0
+ #define appTrue 1
+#endif
+
+/* - application pointers */
+typedef void * appPointer;
+typedef const void * cAppPointer;
+
+/* - bytes, byte pointers */
+/* Note: gcc does not like using "typedef uInt8 *UInt8P" for some strange reason */
+typedef uInt8 *uInt8P;
+typedef const uInt8 *cUInt8P;
+
+/* - Context/Version variables */
+typedef struct ContextType *CContext;
+typedef unsigned long CVersion;
+
+/* - ssize_t is not predefined for Windows CW */
+#if defined _WIN32 && !defined _MSC_VER
+typedef sInt32 ssize_t;
+#endif
+
+
+#ifdef __cplusplus
+ } // namespace
+#endif
+
+#endif /* GENERIC_TYPES_H */
+
+/* eof */
+
+
diff --git a/src/sysync_SDK/Sources/prefix_file.h b/src/sysync_SDK/Sources/prefix_file.h
new file mode 100644
index 0000000..1d5c665
--- /dev/null
+++ b/src/sysync_SDK/Sources/prefix_file.h
@@ -0,0 +1,12 @@
+/*
+ * File: prefix_file.h
+ *
+ * Author: Beat Forster (bfo@synthesis.ch)
+ *
+ *
+ * Copyright (c) 2006-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ */
+
+
+/* intentionally left blank */
diff --git a/src/sysync_SDK/Sources/stringutil.cpp b/src/sysync_SDK/Sources/stringutil.cpp
new file mode 100644
index 0000000..6cd9959
--- /dev/null
+++ b/src/sysync_SDK/Sources/stringutil.cpp
@@ -0,0 +1,244 @@
+/*
+ * File: stringutil.cpp
+ *
+ * Authors: Lukas Zeller (luz@synthesis.ch)
+ * Beat Forster (bfo@synthesis.ch)
+ *
+ * C++ string utils
+ *
+ * Copyright (c) 2001-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ */
+
+#include "target_options.h"
+
+
+#if defined __MACH__ && !defined __GNUC__ /* used for va_list support */
+ #include <mw_stdarg.h>
+#else
+ #include <stdarg.h>
+#endif
+
+#ifdef __GNUC__
+ #include <stdio.h>
+#else
+ #ifndef _MSC_VER
+ #include <ctype>
+ #endif
+#endif
+
+#include "stringutil.h"
+
+#ifdef __cplusplus
+ namespace sysync {
+#endif
+
+
+// case insensitive strcmp, NULL allowed as empty string input
+sInt16 strucmp( cAppCharP s1, cAppCharP s2, size_t len1, size_t len2 )
+{
+ // allow NULL as empty strings
+ if (!s1) s1 = "";
+ if (!s2) s2 = "";
+ // s1>s2 : 1, s1==s2 : 0, s1<s2 : -1
+ size_t i;
+ // calc number of chars we must compare
+ size_t len = len1==0 ? len2 : (len2==0 ? len1 : (len1>len2 ? len2 : len1));
+ for (i=0; (!len || i<len) && *s1 && *s2; i++) {
+ // while both strings have chars and not len reached
+ if (toupper(*s1)!=toupper(*s2))
+ return toupper(*s1)>toupper(*s2) ? 1 : -1; // different
+ // next
+ s1++;
+ s2++;
+ }
+ // equal up to end of shorter string or reached len
+ // - if both reached end or len -> equal
+ if ( ((len1 ? i==len1 : false) || *s1==0) && ((len2 ? i==len2 : false) || *s2==0) ) return 0;
+ // - not equal, longer string is larger
+ // (if not reached end of s1 or stopped before len1, s1 is longer
+ // but note than len1 can be longer than actual length of s1, so we
+ // must check for *s1 to make sure we have really not reached end of s1)
+ return (len1 ? i<len1 && *s1 : *s1) ? 1 : -1;
+} // strucmp
+
+
+// returns number of C-string-escaped chars successfully converted to string
+sInt16 CStrToStrAppend( cAppCharP aStr, string &aString, bool aStopAtQuoteOrCtrl, char ignore )
+{
+ unsigned char c;
+ const char *p=aStr;
+ while ((c=*p++)) {
+ // check for escape
+ if (c=='\\') {
+ // escape, check next
+ c=*p++;
+ if (!c) break; // unfinished escape sequence is ignored
+ switch (c) {
+ case 't' : c=0x09; break; // TAB
+ case 'n' : c=0x0A; break; // line feed
+ case 'r' : c=0x0D; break; // carriage return
+ case '\r':
+ case '\n':
+ // continued line, swallow line end
+ c=0;
+ break;
+ case 'x' :
+ // hex char
+ c=0; uInt16 n=0;
+ while (isxdigit(*p) && n<2) {
+ c=(c<<4) | (*p>'9' ? toupper(*p)-'A'+0x0A : *p-'0');
+ p++; n++;
+ }
+ break;
+ }
+ // c is the char to add
+ }
+ else if (aStopAtQuoteOrCtrl && (c=='"' || c<0x20)) {
+ // terminating char is NOT consumed
+ p--;
+ break; // stop here
+ }
+ // otherwise, ignore any control characters
+ else if (c<0x20 && c!=ignore)
+ continue;
+ // add it to the result
+ if (c) aString+=c;
+ }
+ // return number of converted chars
+ return p-aStr;
+} // CStrToStrAppend
+
+
+// returns number of string-escaped chars successfully converted to C-string
+sInt16 StrToCStrAppend( cAppCharP aStr, string &aString, bool aAllow8Bit, char ignore )
+{
+ unsigned char c;
+ const char *p=aStr;
+ while ((c=*p++)) {
+ // check for specials
+ if (c==ignore) aString+= c;
+ else if (c==0x09) aString+="\\t";
+ else if (c==0x0A) aString+="\\n";
+ else if (c==0x0D) aString+="\\r";
+ else if (c==0x22) aString+="\\\"";
+ else if (c=='\\') aString+="\\\\"; // escape the backslash as well
+ else if (c<0x20 || c==0x7F || (!aAllow8Bit && c>=0x80)) {
+ StringObjAppendPrintf(aString,"\\x%02hX",(uInt16)c);
+ }
+ else {
+ // as is
+ aString+=c;
+ }
+ }
+ // return number of converted chars
+ return p-aStr;
+} // StrToCStrAppend
+
+
+
+// old-style C-formatted output into string object
+static void vStringObjPrintf( string &aStringObj, cAppCharP aFormat, bool aAppend, va_list aArgs )
+{
+ #ifndef NO_VSNPRINTF
+ const size_t bufsiz=128;
+ #else
+ const size_t bufsiz=2048;
+ #endif
+ int actualsize;
+ char buf[bufsiz];
+
+ buf[0]='\0';
+ char *bufP = NULL;
+ if (!aAppend) aStringObj.erase();
+ actualsize = vsnprintf(buf, bufsiz, aFormat, aArgs);
+
+ #ifndef NO_VSNPRINTF
+ if (actualsize>=(int)bufsiz) {
+ // default buffer was too small, create bigger dynamic buffer
+ bufP = new char[actualsize+1];
+ actualsize = vsnprintf(bufP, actualsize+1, aFormat, aArgs);
+ if (actualsize>0) {
+ aStringObj += bufP;
+ }
+ delete bufP;
+ }
+ else
+ #endif
+
+ {
+ // small default buffer was big enough, add it
+ if (actualsize<0) return; // abort, error
+ aStringObj += buf;
+ }
+} // vStringObjPrintf
+
+
+// old-style C-formatted output appending into string object
+void StringObjAppendPrintf( string &aStringObj, cAppCharP aFormat, ... )
+{
+ va_list args;
+
+ va_start(args, aFormat);
+ // now make the string
+ vStringObjPrintf(aStringObj,aFormat,true,args);
+ va_end(args);
+} // StringObjAppendPrintf
+
+
+// returns number of successfully converted chars
+sInt16 HexStrToULong( cAppCharP aStr, uInt32 &aLong, sInt16 aMaxDigits )
+{
+ // our own implementation
+ char c;
+ sInt16 n=0;
+ aLong=0;
+
+ while (aStr && (c=*aStr++) && (n<aMaxDigits)) {
+ if (!isxdigit(c)) break;
+ aLong*= 16;
+ aLong+=(toupper(c)-0x30);
+ if (!isdigit(c)) aLong-=7;
+ n++;
+ } // while
+
+ return n;
+} // HexStrToUShort
+
+
+// returns number of successfully converted chars
+sInt16 HexStrToULongLong( cAppCharP aStr, uInt64 &aLongLong, sInt16 aMaxDigits )
+{
+ // our own implementation
+ char c;
+ sInt16 n=0;
+ aLongLong=0;
+
+ while (aStr && (c=*aStr++) && (n<aMaxDigits)) {
+ if (!isxdigit(c)) break;
+ aLongLong*= 16;
+ aLongLong+=(toupper(c)-0x30);
+ if (!isdigit(c)) aLongLong-=7;
+ n++;
+ } // while
+
+ return n;
+} // HexStrToULongLong
+
+
+sInt16 HexStrToUIntPtr( cAppCharP aStr, uIntPtr &aIntPtr, sInt16 aMaxDigits )
+{
+ #if __WORDSIZE == 64
+ return HexStrToULongLong( aStr, aIntPtr, aMaxDigits );
+ #else
+ return HexStrToULong ( aStr, aIntPtr, aMaxDigits );
+ #endif
+} // HexStrToUIntPtr
+
+
+#ifdef __cplusplus
+ } // namespace
+#endif
+
+/* eof */
+
diff --git a/src/sysync_SDK/Sources/stringutil.h b/src/sysync_SDK/Sources/stringutil.h
new file mode 100644
index 0000000..74dbc00
--- /dev/null
+++ b/src/sysync_SDK/Sources/stringutil.h
@@ -0,0 +1,51 @@
+/*
+ * File: stringutil.h
+ *
+ * Authors: Lukas Zeller (luz@synthesis.ch)
+ * Beat Forster (bfo@synthesis.ch)
+ *
+ * C++ string utils
+ *
+ * Copyright (c) 2001-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ */
+
+#ifndef STRINGUTIL_H
+#define STRINGUTIL_H
+
+#include <string>
+#include "target_options.h"
+#include "generic_types.h"
+
+using namespace std;
+
+#ifdef __cplusplus
+ namespace sysync {
+#endif
+
+
+// case insensitive strcmp, NULL allowed as empty string input
+sInt16 strucmp( cAppCharP s1, cAppCharP s2, size_t len1=0, size_t len2=0 );
+
+// returns number of C-string-escaped chars successfully converted to string
+sInt16 CStrToStrAppend( cAppCharP aStr, string &aString, bool aStopAtQuoteOrCtrl=false,
+ char ignore='\0' );
+
+// returns number of string-escaped chars successfully converted to C-string
+sInt16 StrToCStrAppend( cAppCharP aStr, string &aString, bool aAllow8Bit=false,
+ char ignore='\0' );
+
+// old-style C-formatted output into string object
+void StringObjAppendPrintf( string &aStringObj, cAppCharP aFormat, ... );
+
+sInt16 StrToULong ( cAppCharP aStr, uInt32 &aLong, sInt16 aMaxDigits= 100 );
+sInt16 HexStrToULong ( cAppCharP aStr, uInt32 &aLong, sInt16 aMaxDigits= 100 );
+sInt16 HexStrToULongLong( cAppCharP aStr, uInt64 &aLongLong, sInt16 aMaxDigits= 100 );
+sInt16 HexStrToUIntPtr ( cAppCharP aStr, uIntPtr &aIntPtr, sInt16 aMaxDigits= 100 );
+
+
+#ifdef __cplusplus
+ } // namespace
+#endif
+
+#endif // STRINGUTIL_H
diff --git a/src/sysync_SDK/Sources/syerror.h b/src/sysync_SDK/Sources/syerror.h
new file mode 100644
index 0000000..a59a068
--- /dev/null
+++ b/src/sysync_SDK/Sources/syerror.h
@@ -0,0 +1,181 @@
+/*
+ * File: syerror.h
+ *
+ * Author: luz@synthesis.ch / bfo@synthesis.ch
+ *
+ * Synthesis SyncML engine error code definitions
+ *
+ * Copyright (c) 2001-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ *
+ */
+
+#ifndef SYERROR_H
+#define SYERROR_H
+
+#include "generic_types.h"
+
+#ifdef __cplusplus
+ namespace sysync {
+#endif
+
+/**
+ * global type for error codes
+ *
+ * Using an integer simplifies some arithmetic,
+ * allows assigning HTTP error codes to it,
+ * and most importantly, ensures that the ABI
+ * does not accidentally change.
+ */
+typedef uInt16 TSyError;
+
+/**
+ * Local error codes
+ */
+enum TSyErrorEnum {
+ /** ok */
+ LOCERR_OK = 0,
+
+ /** no content / end of file / end of iteration / empty/NULL value */
+ DB_NoContent = 204,
+ /** external data has been merged */
+ DB_DataMerged = 207,
+
+ /** not authorized */
+ DB_Unauthorized = 401,
+ /** forbidden / access denied */
+ DB_Forbidden = 403,
+ /** object not found / unassigned field */
+ DB_NotFound = 404,
+ /** command not allowed (eventually: at this time)*/
+ DB_NotAllowed = 405,
+
+ /** proxy authentication required */
+ DB_ProxyAuth = 407,
+
+ /** command failed / fatal DB error */
+ DB_Fatal = 500,
+ /** general DB error */
+ DB_Error = 510,
+ /** database / memory full error */
+ DB_Full = 420,
+
+ /** bad or unknown protocol */
+ LOCERR_BADPROTO = 20001,
+ /** fatal problem with SML init/setuserdata etc. */
+ LOCERR_SMLFATAL = 20002,
+ /** cannot open communication (TCP level) */
+ LOCERR_COMMOPEN = 20003,
+ /** cannot send data */
+ LOCERR_SENDDATA = 20004,
+ /** cannot receive data */
+ LOCERR_RECVDATA = 20005,
+ /** Bad content in response (i.e. non-SyncML, exceeding buffer or HTTP status not 200) */
+ LOCERR_BADCONTENT = 20006,
+ /** SML (or SAN) error processing incoming message */
+ LOCERR_PROCESSMSG = 20007,
+ /** cannot close communication */
+ LOCERR_COMMCLOSE = 20008,
+ /** transport layer authorisation (e.g. HTTP auth) failed */
+ LOCERR_AUTHFAIL = 20009,
+ /** error parsing config file */
+ LOCERR_CFGPARSE = 20010,
+ /** error reading config file */
+ LOCERR_CFGREAD = 20011,
+ /** no config/profile found at all, or not enough for requested op (client session start) */
+ LOCERR_NOCFG = 20012,
+ /** config file could not be found */
+ LOCERR_NOCFGFILE = 20013,
+ /** expired */
+ LOCERR_EXPIRED = 20014,
+ /** bad usage (e.g. wrong order of library calls) */
+ LOCERR_WRONGUSAGE = 20015,
+ /** bad handle (e.g. datastore) */
+ LOCERR_BADHANDLE = 20016,
+ /** aborted by user */
+ LOCERR_USERABORT = 20017,
+ /** bad registration (not valid generally or for current product) */
+ LOCERR_BADREG = 20018,
+ /** limited trial version */
+ LOCERR_LIMITED = 20019,
+ /** connection timeout */
+ LOCERR_TIMEOUT = 20020,
+ /** connection SSL certificate expired */
+ LOCERR_CERT_EXPIRED = 20021,
+ /** connection SSL certificate invalid */
+ LOCERR_CERT_INVALID = 20022,
+ /** incomplete sync session (some datastores or items have failed) */
+ LOCERR_INCOMPLETE = 20023,
+ /** internal code signalling that client should retry sending message (instance buffer still contains the message) */
+ LOCERR_RETRYMSG = 20024,
+ /** out of memory */
+ LOCERR_OUTOFMEM = 20025,
+ /** we have no means to open a connection (such as phone flight mode etc.) */
+ LOCERR_NOCONN = 20026,
+ /** connection (not TCP, but underlying PPP/GPRS/BT or whatever) cannot be established */
+ LOCERR_CONN = 20027,
+ /** element is already installed */
+ LOCERR_ALREADY = 20028,
+ /** this build is too new for this license (need upgrading license) */
+ LOCERR_TOONEW = 20029,
+ /** function not implemented */
+ LOCERR_NOTIMP = 20030,
+ /** this license code is valid, but not for this product */
+ LOCERR_WRONGPROD = 20031,
+ /** explicitly suspended by user */
+ LOCERR_USERSUSPEND = 20032,
+ /** this build is too old for this SDK/plugin */
+ LOCERR_TOOOLD = 20033,
+ /** unknown subsystem */
+ LOCERR_UNKSUBSYSTEM = 20034,
+ /** internal code signalling that next message will be a session restart (client should disconnect transport) */
+ LOCERR_SESSIONRST = 20035,
+ /** local datastore is not ready (used to pop up alert) */
+ LOCERR_LOCDBNOTRDY = 20036,
+ /** session should be restarted from scratch */
+ LOCERR_RESTART = 20037,
+ /** internal pipe communication problem */
+ LOCERR_PIPECOMM = 20038,
+ /** buffer too small for requested value */
+ LOCERR_BUFTOOSMALL = 20039,
+ /** value truncated to fit into field or buffer */
+ LOCERR_TRUNCATED = 20040,
+ /** bad parameter */
+ LOCERR_BADPARAM = 20041,
+ /** out of range */
+ LOCERR_OUTOFRANGE = 20042,
+ /** external transport failure (no details known in engine) */
+ LOCERR_TRANSPFAIL = 20043,
+ /** class not registered */
+ LOCERR_CLASSNOTREG = 20044,
+ /** interface not registered */
+ LOCERR_IIDNOTREG = 20045,
+ /** bad URL */
+ LOCERR_BADURL = 20046,
+ /** server not found */
+ LOCERR_SRVNOTFOUND = 20047,
+
+ /** cURL error code */
+ LOCERR_CURL = 21000,
+
+ /** base code for linux signals (SIGXXX). SIGXXX enum value will be added to LOCERR_SIGNAL */
+ LOCERR_SIGNAL = 20500,
+
+ /** TSyncException without specific error code set */
+ LOCERR_EXCEPTION = 20998,
+ /** undefined error message */
+ LOCERR_UNDEFINED = 20999,
+
+ /** local codes signalling SyncML status are shown as LOCAL_STATUS_CODE+<SyncML-Statuscode> */
+ LOCAL_STATUS_CODE = 10000
+};
+
+#ifdef __cplusplus
+ } // namespace
+#endif
+
+
+#endif /* SYERROR_H */
+
+/* eof */
+
diff --git a/src/sysync_SDK/Sources/sync_dbapi.h b/src/sysync_SDK/Sources/sync_dbapi.h
new file mode 100755
index 0000000..2a4f6d5
--- /dev/null
+++ b/src/sysync_SDK/Sources/sync_dbapi.h
@@ -0,0 +1,1118 @@
+/*
+ * File: sync_dbapi.h
+ *
+ * Author: Beat Forster (bfo@synthesis.ch)
+ *
+ * C/C++ Programming interface between the
+ * Synthesis SyncML engine and
+ * the database layer for plug-ins
+ *
+ * Copyright (c) 2005-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ */
+
+/*
+ * This is the calling interface between the Synthesis SyncML engine
+ * and a customized module "sync_dbapi".
+ * The same interface can be used either for Standard C or for C++.
+ * And there is a equivalent interface for JNI (Java Native Interface).
+ * Normally the customized module will be compiled as DLL and will
+ * be called by the SyncML engine. A linkable library is available
+ * (for C++) as well.
+ *
+ * The flow for accessing the datastores is always the same:
+ * The SyncML engine will call these routines step by step
+ *
+ * DATASTORE ACCESS FLOW
+ * =====================
+ *
+ * 1) CreateContext
+ * 2) [ContextSupport] (optional)
+ * 3) [FilterSupport] (optional)
+ * 4) [Read Admin Data] (optional)
+ *
+ * 5) StartDataRead
+ * 6) do ReadNextItem while (aStatus!=ReadNextItem_EOF);
+ * 7) any number of random calls to:
+ * - ReadItem
+ * - ReadBlob
+ * - [AdaptItem] (optional)
+ * 8) EndDataRead
+ *
+ * 9) StartDataWrite
+ * 10) any number of random calls to:
+ * - InsertItem
+ * - FinalizeLocalID
+ * - UpdateItem
+ * - DeleteItem
+ * - DeleteSyncSet
+ * - ReadItem
+ * - WriteBlob
+ * - ReadBlob
+ * - DeleteBlob
+ * - [AdaptItem] (optional)
+ * 11) EndDataWrite
+ *
+ * 12) [Write Admin Data] (optional)
+ * 13) DeleteContext
+ *
+ *
+ * NOTE: 'DisposeObj' calls can occur anywhere between any statement
+ * of the flow above (but not before 'CreateContext' and not after
+ * 'DeleteContext'). The SyncML engine is responsible for
+ * disposing all <aItemData> and <aItemID> objects after use.
+ *
+ * NOTE: Returned errors (value > 0) will influence the flow
+ * Error at 1) will not call any further step.
+ * " " 2)..3) these functions do not return errors
+ * " " 5)..10) will cause an 'EndDataWrite' call with
+ * <success> = false, then 'DeleteContext'.
+ * " " 4)
+ * 11)..12) 'DeleteContext' will be called afterwards.
+ *
+ * The module must be able to handle several contexts in parallel.
+ * All routines will have an <aContext> parameter (assigned by
+ * 'CreateContext'), which allows to identify the correct context.
+ * Routines can be called from different threads, but they are
+ * always sequential for each context. If the thread changes,
+ * 'ThreadMayChangeNow' will notify the module about this issue.
+ * This routine can left empty and ignored, if not used.
+ *
+ * NOTE: As the SyncML engine calls the plug-in multithreaded,
+ * all global structure accesses must be thread save.
+ */
+
+
+#ifndef SYNC_DBAPI_H
+#define SYNC_DBAPI_H
+
+/* Global declarations */
+#include "sync_dbapidef.h"
+
+
+#if defined __cplusplus
+ /* combine the definitions of different namespaces */
+ using sysync::sInt32;
+ using sysync::uInt32;
+ using sysync::CContext;
+ using sysync::CVersion;
+ using sysync::TSyError;
+ using sysync::ItemID;
+ using sysync::MapID;
+ using sysync::cItemID;
+ using sysync::cMapID;
+ using sysync::appCharP;
+ using sysync::cAppCharP;
+ using sysync::appPointer;
+ using sysync::memSize;
+ using sysync::KeyH;
+ using sysync::SDK_Interface_Struct;
+ using sysync::DB_Callback;
+ using sysync::UI_Call_In;
+#endif
+
+
+/* C/C++ and DLL/library support
+ * SYSYNC_ENGINE : true ( within the engine itself ) / false ( outside )
+ * SYSYNC_ENGINE_TEST: true ( within a test engine module ) / false ( outside )
+ * DBAPI_LINKED : true ( at standalone APP, e.g. "helloX" ) / false ( within engine/within DLL )
+ * PLUGIN_INFO : true ( within "plugin_info" program ) / false ( everywhere else )
+ */
+#if !defined SYSYNC_ENGINE && !defined SYSYNC_ENGINE_TEST && !defined DBAPI_LINKED && !defined PLUGIN_INFO && !defined DLL_EXPORT
+ #define DLL_EXPORT 1
+#endif
+
+#undef _ENTRY_ /* could be defined already here */
+#ifdef DLL_EXPORT
+ #define _ENTRY_ ENGINE_ENTRY
+#else
+ #define _ENTRY_
+#endif
+
+
+/* -- MODULE -------------------------------------------------------------------- */
+/*! Create a module context \<mContext>
+ * This routine will be called as the 2nd call, when the module will be connected.
+ * (The 1st call is a 'Module_Version( 0 )' call outside any context).
+ *
+ * It will be called not only once, but once for each session and datastore context,
+ * as defined at the XML config file. This routine can return error 20028 (LOCERR_ALREADY),
+ * if already created. This will be treated not as an error. For this case, it must
+ * return the same \<mContext> as for the former call(s).
+ *
+ * NOTE: The module context can exist once and can be shared for all plug-in accesses.
+ * This can either be done with an allocated global variable at the plug-in or
+ * even better using the "GlobContext" structure provided by the SyncML engine.
+ * Please note, that write access to such a common module context structure must
+ * be thread-safe, when accessed from the session or datastore context.
+ * All the 'Module_CreateContext' calls for this module will be called
+ * sequentially by one thread. The plug-in programmer is responsible not to
+ * re-initialize the context for subsequent calls.
+ *
+ * If the module name at the XML config file is defined as "aaa!bbb!ccc" it will be passed
+ * as "aaa" to \<moduleName> and "bbb!ccc" to \<subName>. This mechanism can be used to
+ * cascade plug-in modules, where the next module gets "bbb" as \<moduleName> and "ccc" as
+ * \<subName>. The JNI plug-in for Java is using this structure to address the JNI plug-in
+ * and its assigned Java class. Error 20034 (LOCERR_UNKSUBSYSTEM) should be returned in case
+ * the subsystem does not exist, no error if no subsystem has been chosen at all.
+ *
+ *
+ * @param <mContext> Returns a value, which allows to identify this module context.
+ * Allowed values: Anything except 0, which is reserved for no context.
+ * @param <moduleName> Name of this plug-in
+ * @param <subName> Name of sub module (if available)
+ * @param <mContextName> Name of the (datastore) context, e.g. "contacts";
+ * this string is empty for calll concerning the session.
+ * @param <mCB> DB_Callback structure for module logging
+ *
+ * @return error code if context could not be created (e.g. not enough memory)
+ * LOCERR_ALREADY if global module context already exists (not treated as error)
+ * 0 if context successfully created.
+ */
+_ENTRY_ TSyError Module_CreateContext( CContext *mContext, cAppCharP moduleName,
+ cAppCharP subName,
+ cAppCharP mContextName,
+ DB_Callback mCB );
+
+
+
+/*! Get the module's version.
+ *
+ * NOTE: The SyncML will take decisions depending on this version number, so
+ * the plug-in developer should not change the values at the delivered sample code.
+ * Plugin_Version( short buildNumber ) of 'SDK_util' should be used.
+ * The \<buildNumber> can be defined by the user.
+ *
+ * NOTE: This function can be called by the engine outside any context with
+ * \<mContext> = 0. For this case, any callback is not permitted (as no DB_Callback
+ * is available).
+ *
+ * @param <mContext> The module context ( 0, if none ).
+ * @return current version as SDK_VERSION_MAJOR | SDK_VERSION_MINOR)
+ * | SDK_SUBVERSION | buildNumber
+ */
+_ENTRY_ CVersion Module_Version( CContext mContext );
+
+
+
+/*! Get the module's capabilities
+ * Currently the SyncML engine currently understands and supports:
+ * - "plugin_sessionauth"
+ * - "plugin_deviceadmin"
+ * - "plugin_datastoreadmin"
+ * - "plugin_datastore"
+ *
+ * If one of these identifiers will be defined as "no" ( e.g. "plugin_sessionauth:no" ),
+ * the according routines will not be connected and used.
+ *
+ * NOTE: The \<mCapabilities> can be allocated with "StrAlloc" (SDK_util.h) for C/C++
+ *
+ * @param <mContext> The module context.
+ * @param <mCapabilities> Returns the module's capabilities as multiline
+ * aa:bb\<CRLF>cc:dd[\<CRLF>]
+ * @return error code
+ */
+_ENTRY_ TSyError Module_Capabilities( CContext mContext, appCharP *mCapabilities );
+
+
+
+/*! The module's config params will be sent to the plug-in.
+ * It can be used for access path definitions or other things.
+ * The \<plugin_params> can be defined individually for each session and datastore.
+ * The SyncML engine checks the syntax, but not the content.
+ * This routine should return an error 20010 (LOCERR_CFGPARSE), if one of
+ * these parameters is not supported.
+ *
+ * EXAMPLE: Definition at XML config file:\n
+ * \<plugin_params>\n
+ * \<datapath>/var/log/sysync\</datapath>\n
+ * \<ultimate_answer>42\</ultimate_answer>\n
+ * \</plugin_params>
+ *
+ * will be passed as:\n
+ * "datapath:/var/log/sysync\n
+ * ultimate_answer:42"
+ *
+ * NOTE: Module_PluginParams will be called ALWAYS for each module context,
+ * even if no plug-in parameter is defined. This allows to react consistently
+ * on parameters, which are not always available.
+ *
+ * @param <mContext> The module context.
+ * @param <mConfigParams> The plugin params as multiline aa:bb\<CRLF>cc:dd[\<CRLF>]
+ * @param <engineVersion> The SyncML engine's version
+ * @return error code
+ */
+_ENTRY_ TSyError Module_PluginParams( CContext mContext, cAppCharP mConfigParams,
+ CVersion engineVersion );
+
+
+
+/*! Disposes memory, which has been allocated within the module context.
+ * (At the moment this is only the capabilities string).
+ * 'Module_DisposeObj' can occur at any time within \<mContext>.
+ *
+ * NOTE: - If \<mCapabilities> has been allocated with "StrAlloc", use "Str_Dispose"
+ * (SDK_util.h) to release the memory again.
+ * - If it is defined as const within the plugin module (the module
+ * itself knows about !), this routine can be implemented empty.
+ *
+ * @param <mContext> The module context.
+ * @param <memory> Dispose allocated memory.
+ * @return -
+ */
+_ENTRY_ void Module_DisposeObj( CContext mContext, void* memory );
+
+
+
+/*! This routine will be called as the last call, before this module is disconnected.
+ * The SyncML engine will call 'Module_DisposeObj' (if required) before this call
+ *
+ * NOTE: This routine will be called ONLY, if the server stops in a controlled way.
+ * Its good programming practice not to wait for this 'DeleteContext' call.
+ *
+ * @param <mContext> The module context.
+ * @return error code
+ */
+_ENTRY_ TSyError Module_DeleteContext( CContext mContext );
+
+
+
+
+/* -- SESSION ------------------------------------------------------------------- */
+/*! By default the session context will be handled by the ODBC interface.
+ * The session context of this plug-in module will be used only,
+ * if \<server type="plugin"> and \<plugin_module> is defined
+ * ( \<plugin_module>name_of_the_plugin\</plugin_module> ).
+ * \<plugin_params> can be defined individually.
+ *
+ * @param <sContext> Returns a value, which allows to identify this session context.
+ * @param <sessionName> Name of this session
+ * @param <sCB> DB_Callback structure for session logging
+ *
+ * @result error code, if context could not be created (e.g. not enough memory)
+ * 0 if context successfully created,
+ *
+ * Flags (at the XML config file):
+ * - \<plugin_deviceadmin>yes\</plugin_deviceadmin>: "Session_CheckDevice", "Session_GetNonce"
+ * "Session_SaveNonce" and
+ * "Session_SaveDeviceInfo" will be used.
+ *
+ * - \<plugin_sessionauth>yes\</plugin_sessionauth>: "Session_PasswordMode",
+ * "Session_Login" and "Session_Logout" will be used.
+ */
+_ENTRY_ TSyError Session_CreateContext( CContext *sContext, cAppCharP sessionName,
+ DB_Callback sCB );
+
+
+
+/*! This function adapts itemData
+ *
+ * @param <sContext> The session context
+ * @param <sItemData1> The 1st item's data
+ * @param <sItemData2> The 2nd item's data
+ * @param <sLocalVars> The local vars
+ * @param <sIdentifier> To identify, where it is called
+ *
+ * @return error code
+ *
+ * NOTE: The memory for adapted strings must be allocated locally.
+ * The SyncML engine will call 'DisposeObj' later, to release again its memory.
+ * One or more strings can be returned unchanged as well.
+ */
+_ENTRY_ TSyError Session_AdaptItem( CContext sContext, appCharP *sItemData1,
+ appCharP *sItemData2,
+ appCharP *sLocalVars,
+ uInt32 sIdentifier );
+
+
+/*! Check the database entry of \<aDeviceID> and return its \<nonce> string.
+ * If \<aDeviceID> is not yet available at the plug-in, return "" for \<nonce>
+ *
+ * @param <sContext> The session context
+ * @param <aDeviceID> The assigned device ID string
+ * @param <sDevKey> The device key string (will be used for datastore accesses later)
+ * @param <nonce> The nonce string of the last session
+ * If \<aDeviceID> is not yet available, return "" for \<nonce>
+ * and error code 0.
+ *
+ * @result error code 403 (Forbidden), if plugin_deviceadmin is not supported;
+ * 0, if successful
+ *
+ * USED ONLY WITH \<plugin_deviceadmin>
+ */
+_ENTRY_ TSyError Session_CheckDevice( CContext sContext, cAppCharP aDeviceID,
+ appCharP *sDevKey,
+ appCharP *nonce );
+
+
+/*! Get a new nonce from the database. If this routine returns an error,
+ * the SyncML engine will create its own nonce.
+ *
+ * @param <sContext> The session context
+ * @param <nonce> A valid new nonce value (for the assigned device ID).
+ *
+ * @return error code 404 (NotFound), if no \<nonce> has been generated;
+ * 0, if a valid \<nonce> has been generated
+ *
+ * USED ONLY WITH \<plugin_deviceadmin>
+ */
+_ENTRY_ TSyError Session_GetNonce( CContext sContext, appCharP *nonce );
+
+
+
+/*! Save the new nonce (which will be expected to be returned in the
+ * next session for this device ID.
+ *
+ * @param <sContext> The session context
+ * @param <nonce> New \<nonce> for the next session (of the assigned device ID)
+ *
+ * @result error code 403 (Forbidden), if plugin_deviceadmin is not supported;
+ * 0, if successful
+ *
+ * USED ONLY WITH \<plugin_deviceadmin>
+ */
+_ENTRY_ TSyError Session_SaveNonce( CContext sContext, cAppCharP nonce );
+
+
+
+/*! Save the device info for \<sContext>
+ *
+ * @param <sContext> The session context
+ * @param <aDeviceInfo> More information about the assigned device (for DB and logging)
+ *
+ * @result error code 403 (Forbidden), if plugin_deviceadmin is not supported;
+ * 0, if successful
+ *
+ * USED ONLY WITH \<plugin_deviceadmin>
+ */
+_ENTRY_ TSyError Session_SaveDeviceInfo( CContext sContext, cAppCharP aDeviceInfo );
+
+
+
+/*! Get the current DB time of \<sContext>
+ *
+ * @param <sContext> The session context
+ * @param <currentDBTime> The current time of the plugin's DB (as ISO8601 format).
+ *
+ * @result error code 403 (Forbidden), if plugin_deviceadmin is not supported;
+ * 404 (NotFound), if not available -> the engine creates its own time
+ * 0, if successful
+ */
+_ENTRY_ TSyError Session_GetDBTime( CContext sContext, appCharP *currentDBTime );
+
+
+
+/*--------------------------------------------------------------------------------*/
+/*! Get the password mode.
+ * There are currently 4 different password modes supported.
+ *
+ * @param <sContext> The session context
+ *
+ * @result
+ * - Password_ClrText_IN : 'SessionLogin' will get clear text password
+ * - Password_ClrText_OUT : " must return clear text password
+ * - Password_MD5_OUT : " must return MD5 coded password
+ * - Password_MD5_Nonce_IN: " will get MD5B64(MD5B64(user:pwd):nonce)
+ *
+ * USED ONLY WITH \<plugin_sessionauth>
+ */
+_ENTRY_ sInt32 Session_PasswordMode( CContext sContext );
+
+
+/*! Get \<sUsrKey> of \<sUsername>,\<sPassword> in the session context.
+ *
+ * @param <sContext> The session context
+ * @param <sUsername> The user name ...
+ * @param <sPassword> ... and the password. \<sPassword> is an input parameter
+ for 'Password_ClrTxt_IN' mode and an output parameter for
+ * 'Password_ClrText_OUT' and 'Password_MD5_OUT' modes.
+ * @param <sUsrKey> Returns the internal reference key, which will be passed to
+ * to the datastore contexts later.
+ *
+ * @result error code 403 (Forbidden), if plugin_sessionauth is not supported;
+ * 0, if successful
+ *
+ * USED ONLY WITH \<plugin_sessionauth>
+ */
+_ENTRY_ TSyError Session_Login( CContext sContext, cAppCharP sUsername,
+ appCharP *sPassword,
+ appCharP *sUsrKey );
+
+
+/*! Logout for this session context
+ *
+ * @param <sContext> The session context
+ *
+ * @result error code 403 (Forbidden), if plugin_sessionauth is not supported;
+ * 0, if successful
+ *
+ * USED ONLY WITH \<plugin_sessionauth>
+ */
+_ENTRY_ TSyError Session_Logout( CContext sContext );
+
+
+
+/*! Disposes memory, which has been allocated within the session context.
+ * 'Session_DisposeObj' can occur at any time within \<sContext>.
+ *
+ * @param <sContext> The session context.
+ * @param <memory> Dispose allocated memory.
+ *
+ * @return -
+ */
+_ENTRY_ void Session_DisposeObj( CContext sContext, void* memory );
+
+
+
+/*! Due to the architecture of the SyncML engine, the system may run in a multithread
+ * environment. The consequence is that each routine of this plugin module can be
+ * called by a different thread. Normally this is not a problem, nevertheless
+ * this routine notifies about thread changes in \<sContext>.
+ * It can be ignored ( =implemented empty), if not really needed.
+ *
+ * @param <sContext> The session context
+ * @return -
+ */
+_ENTRY_ void Session_ThreadMayChangeNow( CContext sContext );
+
+
+
+/*! Writes the context of all items to dbg output path
+ * This routine is implemented for debug purposes only and will NOT BE CALLED by the
+ * SyncML engine. Can be implemented empty
+ *
+ * @param <sContext> The session context
+ * @param <allFields> true : all fields, also empty ones, will be displayed;
+ * false: only fields <> "" will be shown
+ * @param <specificItem> "" : all items will be shown;
+ * else shows the \<specificItem>
+ *
+ * @return -
+ */
+_ENTRY_ void Session_DispItems( CContext sContext, bool allFields, cAppCharP specificItem );
+
+
+
+/*! Delete a session context.
+ * No access to \<sContext> will be done after this call
+ *
+ * @param <sContext> The session context
+ * @return error code, if context could not be deleted.
+ */
+_ENTRY_ TSyError Session_DeleteContext( CContext sContext );
+
+
+
+
+/* -- OPEN ---------------------------------------------------------------------- */
+/*! This routine is called to create a new context for a datastore access.
+ * It must allocate all resources for this context and initialize the \<aContext>
+ * parameter with a value that allows re-identifying the context.
+ * \<aContext> can either be a pointer to the local context structure or any key
+ * value which allows to re-identify the context later.
+ * Subsequent calls related to this context will pass the \<aContext> value as returned
+ * from CreateContext. The context must be valid until 'DeleteContext' is called.
+ * \<plugin_params> can be defined individually.
+ *
+ * NOTE: The SyncML engine treats \<aContext> simply as a key. The only condition
+ * is uniqueness for all datastore contexts. Even \<aContext> = 0 can be used.
+ *
+ * @param <aContext> Returns a value, which allows to identify this datastore context.
+ * @param <aContextName> Allows to identify the context, if more than one must be
+ * handled. \<contextName> is defined at the XML configuration.
+ * @param <aCB> DB_Callback structure for datatstore logging.
+ * @param <sDevKey> The result of 'Session_CheckDevice' comes in here.
+ * @param <sUsrKey> The result of 'Session_Login' comes in here.
+ *
+ * @return error code, if context could not be created (e.g. not enough memory),
+ * 0 if context successfully created.
+ */
+_ENTRY_ TSyError CreateContext( CContext *aContext, cAppCharP aContextName, DB_Callback aCB,
+ cAppCharP sDevKey,
+ cAppCharP sUsrKey );
+
+
+/*! This function asks for specific context configurations
+ *
+ * @param <aContext> The datastore context
+ * @param <aSupportRules> The SyncML sends a list of support rules.
+ * This function has to reply, up to which rule,
+ * contexts are supported (and switched on now).
+ * Data is formatted as multiline aa:bb\<CRLF>cc:dd[\<CRLF>]
+ *
+ * @return Up to \<n> fields are supported (and switched on) for this context.
+ * If 0 will be returned, no field of \<aSupportRules> is supported.
+ *
+ */
+_ENTRY_ uInt32 ContextSupport( CContext aContext, cAppCharP aSupportRules );
+
+
+
+/*! This function asks for filter support.
+ *
+ * @param <aContext> The datastore context
+ * @param <aFilterRules> The SyncML sends a list of filter rules.
+ * This function has to reply, up to which rule,
+ * filters are supported (and switched on now).
+ * Data is formatted as multiline aa:bb\<CRLF>cc:dd[\<CRLF>]
+ *
+ * @return Up to \<n> filters are supported (and switched on) for this context
+ * If 0 will be returned, no field of \<aFilterRules> are supported.
+ */
+_ENTRY_ uInt32 FilterSupport( CContext aContext, cAppCharP aFilterRules );
+
+
+
+/* -- GENERAL ------------------------------------------------------------------- */
+/*! Due to the architecture of the SyncML engine, the system may run in a multithread
+ * environment. The consequence is that each routine of this API module can be
+ * called by a different thread. Normally this is not a problem, nevertheless
+ * this routine notifies about thread changes in \<aContext>.
+ * It can be ignored ( =implemented empty), if not really needed.
+ *
+ * @param <aContext> The datastore context.
+ *
+ * @result -
+ */
+_ENTRY_ void ThreadMayChangeNow( CContext aContext );
+
+
+
+/*! This functions writes \<logData> for this context
+ * Can be implemented empty, if not needed.
+ *
+ * @param <aContext> The datastore context.
+ * @param <logData> Logging information, formatted as multiline aa:bb\<CRLF>cc:dd[\<CRLF>]
+ *
+ * @result -
+ */
+_ENTRY_ void WriteLogData( CContext aContext, cAppCharP logData );
+
+
+
+/*! Writes the context of all items to dbg output path
+ * This routine is implemented for debug purposes only and will NOT BE CALLED by the
+ * SyncML engine. Can be implemented empty, if not needed.
+ *
+ * @param <aContext> The datastore context.
+ * @param <allFields>
+ * - true : all fields, also empty ones, will be displayed;
+ * - false: only fields <> "" will be shown
+ * @param <specificItem>
+ * - "" : all items will be shown;
+ * - else : shows the \<specificItem>
+ *
+ * @return -
+ */
+_ENTRY_ void DispItems( CContext aContext, bool allFields, cAppCharP specificItem );
+
+
+
+/* -- ADMINISTRATION ------------------------------------------------------------ */
+/* This section contains the 'admin read' and 'admin write' routines. */
+
+/*! This function gets the stored information about the record with the four paramters:
+ * \<sDevKey>, \<sUsrKey>, \<aLocDB>, \<aRemDB>.
+ *
+ * - \<plugin_deviceadmin>yes\</plugin_deviceadmin>: Admin/Map routines will be used.
+ *
+ * @param <aContext> The datastore context
+ * @param <aLocDB> Name of the local DB
+ * @param <aRemDB> Name of the remote DB
+ * @param <adminData> The data, saved with the last 'SaveAdminData' call
+ *
+ * @return error code 404 (NotFound), if record is not (yet) available,
+ * 0 (no error) if admin data found
+ *
+ * NOTE: \<sDevKey> and \<sUsrKey> have been passed with 'CreateContext' already.
+ * The plug-in module must have stored them within the datastore context.
+ *
+ * USED ONLY WITH \<plugin_datastoredadmin>
+ */
+_ENTRY_ TSyError LoadAdminData( CContext aContext, cAppCharP aLocDB,
+ cAppCharP aRemDB, appCharP *adminData );
+
+
+
+/*! This functions stores the new \<adminData> for this context
+ *
+ * @param <aContext> The datastore context
+ * @param <adminData> The new set of admin data to be stored, will be loaded again
+ * with the next 'LoadAdminData' call.
+ *
+ * @result error code, if data could not be saved (e.g. not enough memory);
+ * 0 if successfully created.
+ *
+ * USED ONLY WITH \<plugin_datastoredadmin>
+ */
+_ENTRY_ TSyError SaveAdminData( CContext aContext, cAppCharP adminData );
+
+
+
+/*! Map table handling: Get the next map item of this context.
+ * If \<aFirst> is true, the routine must start to return the first element
+ *
+ * @param <aContext> The datastore context
+ * @param <mID> MapID ( with \<localID>,\<remoteID>, <flags> and \<ident> ).
+ * @param <aFirst> Starting with the first MapID. When creating a context,
+ * the first call will get the first MapID, even if \<aFirst>
+ * is false.
+ *
+ * @return
+ * - true: as long as there is a MapID available, which must be assigned to <mID>
+ * - false: if there is no more MapID. Nothing must be assigned to <mID>
+ *
+ * USED ONLY WITH \<plugin_datastoredadmin>
+ */
+_ENTRY_ bool ReadNextMapItem( CContext aContext, MapID mID, bool aFirst );
+
+
+/*! Map table handling: Insert a map item of this context
+ *
+ * @param <aContext> The datastore context
+ * @param <mID> MapID ( with \<localID>,\<remoteID>, <flags> and \<ident> ).
+ * A new item with <localID> will be added.
+ *
+ * @return error code, if this MapID can't be inserted, or if already existing
+ *
+ * USED ONLY WITH \<plugin_datastoredadmin>
+ */
+_ENTRY_ TSyError InsertMapItem( CContext aContext, cMapID mID );
+
+
+/*! Map table handling: Update a map item of this context
+ *
+ * @param <aContext> The datastore context
+ * @param <mID> MapID ( with \<localID>,\<remoteID>, <flags> and \<ident> ).
+ * If there is already a MapID element with localID, it
+ * will be update, else created.
+ *
+ * @return error code, if this MapID can't be updated (e.g. not yet existing).
+ *
+ * USED ONLY WITH \<plugin_datastoredadmin>
+ */
+_ENTRY_ TSyError UpdateMapItem( CContext aContext, cMapID mID );
+
+
+/*! Map table handling: Delete a map item of this context
+ *
+ * @param <aContext> The datastore context
+ * @param <mID> MapID ( with \<localID>,\<remoteID>, <flags> and \<ident> ).
+ *
+ * @return error code, if this MapID can't be deleted,
+ * or if this MapID does not exist.
+ *
+ * USED ONLY WITH \<plugin_datastoredadmin>
+ */
+_ENTRY_ TSyError DeleteMapItem( CContext aContext, cMapID mID );
+
+
+
+
+/* -- READ ---------------------------------------------------------------------- */
+/*! This routine initializes reading from the database
+ * StartDataRead must prepare the database to return the objects of this context.
+ *
+ * @param <aContext> The datastore context.
+ * @param <lastToken> The value which has been returned by this module
+ * at the last "EndDataWrite" call will be given.
+ * It will be "", when called the first time.
+ * Normally this token is an ISO8601 formatted string
+ * which represents the module's current time (at the
+ * beginning of a session). It will be used to decide at
+ * 'ReadNextItem' whether a record has been changed.
+ * @param <resumeToken> Token for Suspend/Resume mode.
+ *
+ * @return error code
+ */
+_ENTRY_ TSyError StartDataRead( CContext aContext, cAppCharP lastToken,
+ cAppCharP resumeToken );
+
+
+
+/*! This routine reads the next ItemID from the database.
+ * \<allfields> of 'ContextSupport' ( "ReadNextItem:allfields" ) and
+ * \<aFilterRules> of 'FilterSupport' must be considered.
+ * If \<aFirst> is true, the routine must return the first element (again).
+ *
+ * @param <aContext> The datastore context.
+ * @param <aID> The assigned ItemID in the database;
+ * will be ignored by the SyncML engine, if \<aStatus> = 0
+ * @param <aItemData> The data, formatted as multiline aa:bb\<CRLF>cc:dd[\<CRLF>];
+ * will be ignored by the SyncML engine, if \<aStatus> = 0
+ * @param <aStatus>
+ * - ReadItem_EOF ( =0 ) for none ( =eof ),
+ * - ReadItem_Changed ( =1 ) for a changed item,
+ * - ReadItem_Unchanged ( =2 ) for unchanged item.
+ * - ReadItem_Resumed ( =3 ) for a changed item (since resumed)
+ * @param <aFirst>
+ * - true: the routine must return the first element
+ * - false: the routine must return the next element
+ *
+ * @return error code, if not ok. No datasets found is a success as well !
+ *
+ *
+ * NOTE: The memory for \<aID> and \<aItemData> must be allocated locally.
+ * The SyncML engine will call 'DisposeObj' later for these objects to
+ * release the memory again. It needn't to be allocated, if \<aStatus>
+ * is ReadItem_EOF.
+ *
+ * NOTE: By default, the SyncML engine asks for \<aID> only.
+ * \<aItemData> can be returned, if anyway available or
+ * \<aItemData> must be returned, if the engine asks for it
+ * (when calling "ReadNextItem:allfields" at 'ContextSupport' with \<allfields>).
+ */
+_ENTRY_ TSyError ReadNextItem ( CContext aContext, ItemID aID, appCharP *aItemData,
+ sInt32 *aStatus, bool aFirst );
+
+/*! This is the equivalent to 'ReadNextItem', but using a key instead of a data string */
+_ENTRY_ TSyError ReadNextItemAsKey( CContext aContext, ItemID aID, KeyH aItemKey,
+ sInt32 *aStatus, bool aFirst );
+
+
+
+/*! This routine reads the contents of a specific ItemID \<aID> from the database.
+ *
+ * @param <aContext> The datastore context.
+ * @param <aID> The assigned ItemID in the database
+ * @param <aItemData> Returns the data, formatted as multiline aa:bb\<CRLF>cc:dd[\<CRLF>]
+ *
+ * @return error code, if not ok ( e.g. invalid \<aItemID> )
+ *
+ *
+ * NOTE: The memory for \<aItemData> must be allocated locally.
+ * The SyncML engine will call 'DisposeObj' later for \<aItemData>,
+ * to release again its memory.
+ */
+_ENTRY_ TSyError ReadItem ( CContext aContext, cItemID aID, appCharP *aItemData );
+
+/*! This is the equivalent to 'ReadItem', but using a key instead of a data string */
+_ENTRY_ TSyError ReadItemAsKey( CContext aContext, cItemID aID, KeyH aItemKey );
+
+
+
+/*! This routine reads the specific binary logic block \<aID>,\<aBlobID>
+ * from the database.
+ *
+ * @param <aContext> The datastore context.
+ * @param <aID> ItemID ( with \<item>,\<parent> ).
+ * @param <aBlobID> The assigned ID of the blob.
+ *
+ * @param <aBlkPtr> Position and size (in bytes) of the blob block.
+ * @param <aBlkSize>
+ * - Input: Maximum size (in bytes) of the blob block to be read.
+ * If \<blkSize> is 0, the result size is not limited.
+ * - Output: Size (in bytes) of the blob block.
+ * \<blkSize> must not be larger than its input value.
+ * @param <aTotSize> Total size of the blob (in bytes), can be also 0,
+ * if not available, e.g. for a stream.
+ *
+ * @param <aFirst> (Input)
+ * - true : Engine asks for the first block of this blob.
+ * - false: Engine asks for the next block of this blob.
+ *
+ * @param <aLast> (Output)
+ * - true : This is the last part (or the whole) blob.
+ * - false: More blocks will follow.
+ *
+ * @return error code, if not ok ( e.g. invalid \<aItemID>,\<aBlobID> )
+ *
+ *
+ * NOTE 1) The memory at \<blkPtr>,\<blkSize> must be allocated locally.
+ * The SyncML engine will call 'DisposeObj' later for \<blkPtr>,
+ * to release the memory.
+ *
+ * NOTE 2) Empty blobs are allowed, \<blkSize> and \<totSize> must be set to 0,
+ * \<blkPtr> can be undefined, \<aLast> must be true.
+ * No 'DisposeObj' call is required for this case.
+ *
+ * NOTE 3) The SyncML engine can change to read another blob before
+ * having read the whole blob. It will never resume reading of this
+ * incomplete blob, but start reading again with \<aFirst> = true.
+ */
+_ENTRY_ TSyError ReadBlob( CContext aContext, cItemID aID, cAppCharP aBlobID,
+ appPointer *aBlkPtr, memSize *aBlkSize,
+ memSize *aTotSize,
+ bool aFirst, bool *aLast );
+
+
+
+/*! This routine terminates the read from database phase
+ * It can be used e.g. for termination of a transaction.
+ * In standard case it can be implemented empty, returning simply a value LOCERR_OK = 0.
+ *
+ * @param <aContext> The datastore context.
+ * @return error code
+ */
+_ENTRY_ TSyError EndDataRead( CContext aContext );
+
+
+
+
+/* -- WRITE --------------------------------------------------------------------- */
+/*! This routine initializes writing to the database
+ *
+ * @param <aContext> The datastore context.
+ * @return error code, if not ok (e.g. invalid select options)
+ */
+_ENTRY_ TSyError StartDataWrite( CContext aContext );
+
+
+
+/*! This routine inserts a new dataset to the database. The assigned
+ * new ItemID \<aId> will be returned.
+ *
+ * @param <aContext> The datastore context.
+ * @param <aItemData> The data, formatted as multiline aa:bb\<CRLF>cc:dd[\<CRLF>]
+ * @param <aID> Database key of the new dataset.
+ *
+ * @return error code
+ * - LOCERR_OK ( =0 ), if successful
+ * - DB_DataMerged ( =207 ), if successful, but "ReadItem" requested to
+ * inform about updates
+ * - DB_Forbidden ( =403 ), if \<aItemData> can't be resolved
+ * - DB_Full ( =420 ), if not enough space in the DB
+ * - ... or any other SyncML error code, see Reference Manual
+ *
+ * NOTE: The memory for \<aItemID> must be allocated locally.
+ * The SyncML engine will call 'DisposeObj' later for \<aItemID>,
+ * to release the memory
+ *
+ */
+_ENTRY_ TSyError InsertItem ( CContext aContext, cAppCharP aItemData, ItemID aID );
+
+/*! This is the equivalent to 'InsertItem', but using a key instead of a data string */
+_ENTRY_ TSyError InsertItemAsKey( CContext aContext, KeyH aItemKey, ItemID aID );
+
+
+
+/*! This routine updates an existing dataset of the database
+ *
+ * @param <aContext> The datastore context.
+ * @param <aItemData> The data, formatted as multiline aa:bb\<CRLF>cc:dd[\<CRLF>]
+ * @param <aID> Database key of dataset to be updated
+ * @param <updID>
+ * - Input: NULL is assigned as default value to
+ * \<updID.item> and \<updID.parent>.
+ * - Output: The updated database key for \<aID>.
+ * Can be NULL, if the same as \<aID>
+ *
+ * @return error code
+ * - LOCERR_OK ( =0 ), if successful
+ * - DB_Forbidden ( =403 ), if \<aItemData> can't be resolved
+ * - DB_NotFound ( =404 ), if unknown \<aID>
+ * - DB_Full ( =420 ), if not enough space in the DB
+ * - ... or any other SyncML error code, see Reference Manual
+ *
+ *
+ * NOTE: \<updID> must either contain NULL references ( if the same as \<aID> ),
+ * or the memory for \<updID.item>,\<updID.parent> must be allocated locally.
+ * The SyncML engine will call 'DisposeObj' later for \<updID.item> and
+ * \<updID.parent> to release the memory.
+ * \<updID.parent> can be NULL, if the hierarchical model is not supported.
+ */
+_ENTRY_ TSyError UpdateItem ( CContext aContext, cAppCharP aItemData, cItemID aID,
+ ItemID updID );
+
+/*! This is the equivalent to 'UpdateItem', but using a key instead of a data string */
+_ENTRY_ TSyError UpdateItemAsKey( CContext aContext, KeyH aItemKey, cItemID aID,
+ ItemID updID );
+
+
+
+/*! This routine moves \<aID.item> from \<aID.parent> to \<newParID>
+ *
+ * @param <aContext> The datastore context.
+ * @param <aID> ItemID ( with \<item>,\<parent> ) to be moved.
+ * @param <newParID> New parent ID for \<aID>
+ *
+ * @return error code
+ * - LOCERR_OK ( =0 ), if successful
+ * - DB_NotFound ( =404 ), if unknown \<newParID>
+ * - DB_Full ( =420 ), if not enough space in the DB
+ * - ... or any other SyncML error code, see Reference Manual
+ */
+_ENTRY_ TSyError MoveItem( CContext aContext, cItemID aID, cAppCharP newParID );
+
+
+
+/*! This routine deletes a dataset from the database
+ *
+ * @param <aContext> The datastore context.
+ * @param <aID> ItemID ( with \<item>,\<parent> ) to be deleted.
+ *
+ * @return error code
+ * - LOCERR_OK ( =0 ), if successful
+ * - DB_NotFound ( =404 ), if unknown \<aItemID>
+ * - ... or any other SyncML error code, see Reference Manual
+ */
+_ENTRY_ TSyError DeleteItem( CContext aContext, cItemID aID );
+
+
+
+
+/*! This routine updates a temporary <aID> to an <updID> at the end
+ * For cached systems which assign IDs at the end of a run.
+ *
+ * @param <aContext> The datastore context.
+ * @param <aID> Database key of dataset to be updated
+ * @param <updID>
+ * - Input: NULL is assigned as default value to
+ * \<updID.item> and \<updID.parent>.
+ * - Output: The updated database key for \<aID>.
+ * Can be NULL, if the same as \<aID>
+ *
+ * @return error code
+ * - LOCERR_OK ( =0 ), if successful
+ * - DB_Forbidden ( =403 ), if \<aItemData> can't be resolved
+ * - DB_NotFound ( =404 ), if unknown \<aID>
+ * - LOCERR_NOTIMP ( =20030 ), if no finalizing is needed at all
+ * - ... or any other SyncML error code, see Reference Manual
+ *
+ * NOTE: \<updID> must either contain NULL references ( if the same as \<aID> ),
+ * or the memory for \<updID.item> must be allocated locally.
+ * The SyncML engine will call 'DisposeObj' later for \<updID.item>
+ * to release the memory. \<updID.parent>should be always NULL.
+ */
+_ENTRY_ TSyError FinalizeLocalID( CContext aContext, cItemID aID, ItemID updID );
+
+
+
+
+/*! This routine deletes all datasets from the database
+ *
+ * @param <aContext> The datastore context.
+ *
+ * @return error code
+ * - LOCERR_OK ( =0 ), if successful
+ * - LOCERR_NOTIMP ( =20030 ). For this case, the engine removes all items directly
+ * - ... or any other SyncML error code, see Reference Manual
+ */
+_ENTRY_ TSyError DeleteSyncSet( CContext aContext );
+
+
+
+
+/*! This routine writes the specific binary logic block \<blobID> to the database.
+ *
+ * @param <aContext> The datastore context.
+ * @param <aID> ItemID ( with \<item>,\<parent> ).
+ * @param <aBlobID> The assigned ID of the blob.
+ *
+ * @param <aBlkPtr>
+ * @param <aBlkSize> Position and size (in bytes) of the blob block.
+ * @param <aTotSize> Total size of the blob (in bytes),
+ * Can be also 0, if not available, e.g. for a stream.
+ *
+ * @param <aFirst>
+ * - true : this is the first block of the blob.
+ * - false: this is the next block.
+ * @param <aLast>
+ * - true : this is the last block.
+ * - false: more blocks will follow.
+ *
+ * @return error code, if not ok ( e.g. invalid \<aID>,\<aBlobID> )
+ *
+ * NOTE: Empty blobs are possible, \<blkSize> and \<totSize> will be set to 0,
+ * \<blkPtr> will be NULL, \<aFirst> and \<aLast> will be true.
+ */
+_ENTRY_ TSyError WriteBlob( CContext aContext, cItemID aID, cAppCharP aBlobID,
+ appPointer aBlkPtr, memSize aBlkSize,
+ memSize aTotSize,
+ bool aFirst, bool aLast );
+
+
+/*! This routine deletes the specific binary logic block \<blobID> at the database.
+ *
+ * @param <aContext> The datastore context.
+ * @param <aID> ItemID ( with \<item>,\<parent> ).
+ * @param <aBlobID> The assigned ID of the blob.
+ *
+ * @return error code, if not ok ( e.g. invalid \<aID>,\<aBlobID> )
+ */
+_ENTRY_ TSyError DeleteBlob( CContext aContext, cItemID aID, cAppCharP aBlobID );
+
+
+/*! Advises the database to finsish the running transaction
+ *
+ * @param <aContext> The datastore context.
+ * @param <success>
+ * - true: All former actions were successful,
+ * so the database can commit
+ * - false: The transaction was not successful,
+ * so the database may rollback or ignore the transaction.
+ *
+ * @param <newToken> An internally generated string value, which
+ * will be used to identify changed database records.
+ * It is normally an ISO8601 formatted string, which
+ * represents the module's current time (at the
+ * time the 'StartDataRead' of this context has been
+ * called). All changed records of the currrent context
+ * must get this token as timestamp as as well.
+ * The SyncML engine will return this value with the
+ * 'StartDataRead' call within the next session.
+ * It must return NULL in case of no \<success>.
+ *
+ * @return error code, if operation can't be performed. No \<success> is not an error.
+ *
+ *
+ * NOTE: By default, the SyncML engine expects an ISO8601 string for \<newToken>.
+ * But the SyncML engine can be configured to treat this value completely
+ * opaque, if implemented in a different way.
+ *
+ * The \<newToken> must be allocated locally and will be
+ * disposed with a 'DisposeObj' call later by the SyncML engine.
+ */
+_ENTRY_ TSyError EndDataWrite( CContext aContext, bool success, appCharP *newToken );
+
+
+
+/* ---- ADAPT ITEM -------------------------------------------------------------- */
+/*! This function adapts aItemData
+ *
+ * @param <aContext> The datastore context
+ * @param <aItemData1> The 1st item's data
+ * @param <aItemData2> The 2nd item's data
+ * @param <aLocalVars> The local vars
+ * @param <aIdentifier> To identify, where it is called
+ *
+ * @return error code
+ *
+ * NOTE: The memory for adapted strings must be allocated locally.
+ * The SyncML engine will call 'DisposeObj' later, to release again its memory.
+ * One or more strings can be returned unchanged as well.
+ */
+_ENTRY_ TSyError AdaptItem( CContext aContext, appCharP *aItemData1,
+ appCharP *aItemData2,
+ appCharP *aLocalVars,
+ uInt32 aIdentifier );
+
+
+/* -- DISPOSE / CLOSE ----------------------------------------------------------- */
+/*! Disposes memory, which has been allocated within the datastore context.
+ * 'DisposeObj' can occur at any time within \<aContext>.
+ *
+ * @param <aContext> The datastore context.
+ * @param <memory> Dispose allocated memory.
+ *
+ * @return -
+ *
+ */
+_ENTRY_ void DisposeObj( CContext aContext, void* memory );
+
+
+
+/*! This routine is called to delete a context, that was previously created with
+ * 'CreateContext'. The DB Module must free all resources related to this context.
+ * No calls with \<aContext> will be done after calling this routine, so the
+ * assigned structure, allocated at 'CreateContext' can be released here.
+ *
+ * @param <aContext> The datastore context.
+ *
+ * @result error code, if context could not be deleted ( e.g. not existing \<aContext> ).
+ */
+_ENTRY_ TSyError DeleteContext( CContext aContext );
+
+
+#endif
+/* eof */
diff --git a/src/sysync_SDK/Sources/sync_dbapidef.h b/src/sysync_SDK/Sources/sync_dbapidef.h
new file mode 100755
index 0000000..9ed9a95
--- /dev/null
+++ b/src/sysync_SDK/Sources/sync_dbapidef.h
@@ -0,0 +1,649 @@
+/*
+ * File: sync_dbapidef.h
+ *
+ * Author: Beat Forster (bfo@synthesis.ch)
+ *
+ * C/C++ Programming interface between
+ * the Synthesis SyncML engine
+ * and the database layer
+ *
+ * Copyright (c) 2004-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ *
+ * These are the definitions for the calling interface between the
+ * Synthesis SyncML engine and a customized module "sync_dbapi".
+ * The same interface can be used either for Standard C or for C++.
+ * Normally the customized module will be compiled as DLL and will
+ * be called by the SyncML engine. A linkable library is available
+ * (for C++) as well.
+ *
+ */
+
+/* NOTE: As this file should work for all kind of plain C compilers
+ * comments with double slashes "//" must be avoided !!
+ */
+
+#ifndef SYNC_DBAPIDEF_H
+#define SYNC_DBAPIDEF_H
+
+#include "sync_include.h"
+#include "sync_declarations.h"
+
+#ifdef JNI_SUPPORT
+ #ifdef MACOSX
+ #include <JavaVM/jni.h>
+ #else
+ #include <jni.h>
+ #endif
+#endif
+
+/* export prefix and suffix is dependent on platform and source language */
+#ifdef __cplusplus
+ #define ENTRY_C extern "C"
+#else
+ #define ENTRY_C
+#endif
+
+#if defined MACOSX
+ #define ENGINE_ENTRY ENTRY_C
+
+ #ifdef __GNUC__
+ #define ENTRY_ATTR __attribute__((visibility("default")))
+ #else
+ #define ENTRY_ATTR
+ #endif
+#elif defined _MSC_VER
+ /* Visual Studio 2005 requires a specific entry point definition */
+ /* This definition is empty for all other platforms */
+ #define ENGINE_ENTRY ENTRY_C _declspec(dllexport)
+ #define ENTRY_ATTR
+#else
+ #define ENGINE_ENTRY ENTRY_C
+ #define ENTRY_ATTR
+#endif
+
+/* compose name of external symbols with C binding:
+ * by default use parameter as-is (backwards compatibility),
+ * can be changed in any file that is included before
+ * this file
+ */
+#ifndef SYSYNC_EXTERNAL
+# define SYSYNC_EXTERNAL(_x) _x
+# define SYSYNC_PREFIX ""
+#endif
+
+#ifdef __cplusplus
+ namespace sysync {
+#endif
+
+
+/* ---- Plugin versions ------------------------------------------------------------------------------- */
+/* To make the SyncML engine and the Plugin SDK upwards/downwards compatible to each other,
+ * some version info must be checked and compared: VP_XXX is the min version of the plugin to
+ * support a certain feature, VE_XXX is the min version of the engine to support a feature.
+ * The plugin will get the engine's version with "Plugin_Params".
+ * The version number for engine AND plugin is defined as SDK_VERSION/SUBVERSION at "SDK_util.c"
+ * Normally, the version number of the engine must be equal or higher than the plugin's version.
+ */
+
+/* ---- Plugin support ---- */
+/* For the first versions, "N" is the platform identifier */
+/* VP_Init 0x01000000 * V1.0.N.0 : Initial version */
+/* VP_1st 0x01000002 * V1.0.N.2 : 1st delivered version */
+/* VP_Session_Login 0x01000003 * V1.0.N.3 : VAR_String for Session_Login: this version only */
+
+enum Version {
+ /** V1.0.N.4 : <engineVersion> param for "Module_PluginParams" */
+ VP_EngineVersionParam = 0x01000004,
+ /** V1.0.N.5 : Callback version >= 2 supported */
+ VP_CB_Version2 = 0x01000005,
+ /** V1.0.N.6 : With new function "DeleteBlob" */
+ VP_DeleteBlob = 0x01000006,
+ /** V1.0.N.7 : With new "scripting" function "AdaptItemData" */
+ VP_AdaptItemData = 0x01000007,
+ /** V1.0.N.8 : Supports "InsertMapItem" */
+ VP_InsertMapItem = 0x01000008,
+
+/* From now on, the platform info can be found at he capabilities, "X" is a user defined build nr */
+ /** V1.0.5.X : "X": With customer defined plugin build number */
+ VP_NewBuildNumber = 0x01000500,
+ /** V1.0.6.X : Suspend/Resume extension for 'StartDataRead' */
+ VP_ResumeToken = 0x01000600,
+ /** V1.0.8.X : "Session_GetDBTime" supported */
+ VP_GetDBTime = 0x01000800,
+ /** V1.0.9.X : Callback version >= 3 supported: exotic */
+ VP_CB_Version3 = 0x01000900,
+ /** V1.0.10.X: Callback version >= 4 supported: allowDLL */
+ VP_CB_Version4 = 0x01000a00,
+ /** V1.0.10.X: With enhanced "scripting" function "AdaptItem" */
+ VP_AdaptItem = 0x01000a00,
+
+/* The new SyncML engine V2.9.X/V3.X.X runs best with versions from here onwards */
+ /** V1.1.0.X : Enhanced Plugin Info support: Server > 2.9.5.0 */
+ VP_Plugin_Info = 0x01010000,
+ /** V1.2.1.X : With additional password mode + JNI adaption */
+ VP_MD5_Nonce_IN = 0x01020100,
+/* VP_061012 0x01020200 * V1.2.2.X : Released 12-Oct-06 */
+/* VP_061207 0x01030000 * V1.3.0.X : Released 07-Dec-06, Visual Studio support */
+ /** V1.3.1.X : With UI function support */
+ VP_UI_Support = 0x01030100,
+/* VP_070131 0x01030200 * V1.3.2.X : Released 31-Jan-07 */
+/* VP_070201 0x01030300 * V1.3.3.X : Released 01-Feb-07 */
+ /** V1.3.4.X : Java Callback with <cContext> */
+ VP_Call_cContext = 0x01030400,
+ /** V1.3.4.X : Callback version >= 7 supported: UI / thisCB */
+ VP_CB_Version7 = 0x01030400,
+/* VP_070212 0x01030600 * V1.3.6.X : Released 12-Feb-07 */
+ /** V1.3.8.X : Support for GlobContext structure */
+ VP_GlobContext = 0x01030800,
+ /** V1.3.9.X : Callback version >= 8 supported: extended UI */
+ VP_CB_Version8 = 0x01030900,
+ /** V1.4.0.X : With UI application support / Beta release */
+ VP_UIApp = 0x01040000,
+ /** V1.4.7.X : Callback version >= 9 supported: dbapi tunnel */
+ VP_CB_Version9 = 0x01040700,
+ /** V1.4.8.X : "FinalizeLocalID"/"DeleteSyncSet" support. */
+ VP_FLI_DSS = 0x01040800,
+ /** V1.5.0.X : Released version Aug 2008 */
+ VP_Release_1_5_0 = 0x01050000,
+ /** V1.5.1.X : Multiple global contexts supported */
+ VP_GlobMulti = 0x01050100,
+ /** V1.5.2.X : Callback version >= 11 supported: dbapi tunnel */
+ VP_CB_Version11 = 0x01050200,
+ /** V1.5.2.X : Current version, use 'Plugin_Version()' */
+ VP_CurrentVersion = 0x01050200,
+/* * */
+ /** -------- : Bad/undefined version */
+ VP_BadVersion = 0xffffffff,
+
+
+/* ---- Engine support ---- */
+ /** V1.0.7.0 : Engine supports "InsertMapItem" */
+ VE_InsertMapItem = 0x01000700
+};
+
+
+
+/* ---- Function names -------------------------------------------------------------------------------- */
+/* ---- module ---- */
+#define Mo_CC "Module_CreateContext"
+#define Mo_Ve "Module_Version"
+#define Mo_Ca "Module_Capabilities"
+#define Mo_PP "Module_PluginParams"
+
+#define Mo_DO "Module_DisposeObj"
+#define Mo_DC "Module_DeleteContext"
+
+/* ---- session ---- */
+#define Se_CC "Session_CreateContext"
+#define Se_TC "Session_ThreadMayChangeNow"
+#define Se_DI "Session_DispItems"
+
+#define Se_CD "Session_CheckDevice"
+#define Se_GN "Session_GetNonce"
+#define Se_SN "Session_SaveNonce"
+#define Se_SD "Session_SaveDeviceInfo"
+#define Se_GT "Session_GetDBTime"
+
+#define Se_PM "Session_PasswordMode"
+#define Se_LI "Session_Login"
+#define Se_LO "Session_Logout"
+
+#define Se_DO "Session_DisposeObj"
+#define Se_DC "Session_DeleteContext"
+
+/* ---- datastore ---- */
+#define Da_CC "CreateContext"
+#define Da_CS "ContextSupport"
+#define Da_FS "FilterSupport"
+
+#define Da_LA "LoadAdminData"
+#define Da_SA "SaveAdminData"
+#define Da_RM "ReadNextMapItem"
+#define Da_IM "InsertMapItem"
+#define Da_UM "UpdateMapItem"
+#define Da_DM "DeleteMapItem"
+
+#define Da_TC "ThreadMayChangeNow"
+#define Da_DI "DispItems"
+#define Da_WL "WriteLogData"
+
+#define Da_SR "StartDataRead"
+#define Da_RN "ReadNextItem"
+#define Da_RNK "ReadNextItemAsKey"
+#define Da_RI "ReadItem"
+#define Da_RIK "ReadItemAsKey"
+#define Da_RB "ReadBlob"
+#define Da_ER "EndDataRead"
+
+#define Da_SW "StartDataWrite"
+#define Da_II "InsertItem"
+#define Da_IIK "InsertItemAsKey"
+#define Da_UI "UpdateItem"
+#define Da_UIK "UpdateItemAsKey"
+#define Da_MvI "MoveItem"
+#define Da_DeI "DeleteItem"
+#define Da_FLI "FinalizeLocalID"
+#define Da_DSS "DeleteSyncSet"
+#define Da_WB "WriteBlob"
+#define Da_DB "DeleteBlob"
+#define Da_EW "EndDataWrite"
+
+#define Da_DO "DisposeObj"
+#define Da_DC "DeleteContext"
+
+
+
+/* ---- Predefined capability names ------------------------------------------------------------------- */
+#define CA_MinVersion "MINVERSION" /* Minimum requested engine version by the plugin */
+#define CA_SubVersion "SUBVERSION" /* version of the sub system */
+#define CA_SubSystem "SUBSYSTEM" /* Name of the sub system, used as capability separator */
+#define CA_Manufacturer "MANUFACTURER" /* Name of the plugin manufacturer */
+#define CA_Description "DESCRIPTION" /* A short description, what the plugin module is doing */
+#define CA_Platform "PLATFORM" /* The software platform, e.g. "Windows", "Java", ... */
+#define CA_DLL "DLL" /* Indicates, if plugin is a DLL */
+#define CA_JNI "JNI" /* Indicates, if plugin is based on JNI */
+#define CA_CSHARP "C#" /* Indicates, if plugin is based on C# */
+#define CA_GUID "GUID" /* GUID */
+#define CA_GlobContext "GlobContext" /* The global context, if available */
+#define CA_ADMIN_Info "ADMIN_Info" /* Get ADMIN info as <name><SP>"ADMIN" with 'CreateContext' */
+ /* (supported for V1.3.7 and higher) */
+#define CA_ItemAsKey "ITEM_AS_KEY" /* Supports the ItemAsKey" mode */
+#define CA_Error "ERROR" /* Capability error */
+
+/* Predefined identifiers */
+#define ADMIN_Ident " ADMIN" /* Appended identifier for admin datastore recognition */
+ /* Activated with capability CA_ADMIN_Info */
+
+
+
+/* ---- Allow to switch off certain parts of the plug-in module --------------------------------------- */
+#define Plugin_Start "plugin_start"
+#define Plugin_Param "plugin_param"
+
+#define Plugin_Session "plugin_se"
+#define Plugin_SE_Adapt "plugin_sessionadapt"
+#define Plugin_SE_Auth "plugin_sessionauth"
+#define Plugin_DV_Admin "plugin_deviceadmin"
+#define Plugin_DV_DBTime "plugin_dbtime"
+
+#define Plugin_Datastore "plugin_ds"
+#define Plugin_DS_General "plugin_datageneral"
+#define Plugin_DS_Admin "plugin_datastoreadmin"
+#define Plugin_DS_Data "plugin_datastore"
+#define Plugin_DS_Data_Str "plugin_datastore_str"
+#define Plugin_DS_Data_Key "plugin_datastore_key"
+#define Plugin_DS_Blob "plugin_datablob"
+#define Plugin_DS_Adapt "plugin_dataadapt"
+
+#define Plugin_UI "plugin_ui"
+
+/* Compatibility to older versions */
+#define Plugin_Param_OLD "plugin_param_OLD"
+#define Plugin_SE_Auth_OLD "plugin_sessionauth_OLD"
+#define Plugin_DS_Data_OLD1 "plugin_datastore_OLD1"
+#define Plugin_DS_Data_OLD2 "plugin_datastore_OLD2"
+#define Plugin_DS_Admin_OLD "plugin_datastoreadmin_OLD"
+#define Plugin_DS_Blob_OLD "plugin_datablob_OLD"
+
+
+
+/* ---------------------------------------------------------------------------------------------------- */
+/*! Item and its special definitions */
+#define StdPattern ":" /* std identifier */
+#define ArrayPattern "[" /* array identifier */
+#define BlobPattern ";BLOBID=" /* blob identifier */
+#define TZNamePattern ";TZNAME=" /* tzname identifier */
+#define TZOffsPattern ";TZOFFS=" /* tzoffs identifier */
+
+
+/*! Result value of Session_PasswordMode */
+enum PasswordMode {
+ Password_Mode_Undefined = -1, /* undefined mode */
+ Password_ClrText_IN = 0,
+ Password_ClrText_OUT = 1,
+ Password_MD5_OUT = 2, /* MD5B64(user:pwd) */
+ Password_MD5_Nonce_IN = 3, /* MD5B64(MD5B64(user:pwd):nonce) */
+};
+
+/*! Result status of ReadNextItem */
+enum ReadNextItemResult {
+ ReadNextItem_EOF = 0, /* no more items to read */
+ ReadNextItem_Changed = 1,
+ ReadNextItem_Unchanged = 2,
+ ReadNextItem_Resumed = 3, /* OMA DS 1.2 suspend/resume support */
+};
+
+
+/*! Structure of hierarchical item ID */
+struct ItemIDType {
+ appCharP item;
+ appCharP parent;
+};
+
+/*! Structure of map ID */
+struct MapIDType {
+ appCharP localID;
+ appCharP remoteID;
+ uInt16 flags;
+ uInt8 ident;
+};
+
+/*! Structure of GlobContext */
+struct GlobContext {
+ void* ref; /* reference field */
+ struct GlobContext* next; /* reference to the next GlobContext structure */
+ uInt32 cnt; /* link count */
+ char refName[ 80 ]; /* the reference's name, length restricted */
+};
+
+#define GlobContext_JavaVM "JavaVM"
+
+
+/*! Wrapper for tunnel context callback */
+struct TunnelWrapper {
+ void* tCB;
+ CContext tContext;
+};
+
+
+/*! Undefined function for place holder at 'ConnectFunctions', must have pointer size */
+#define XX (char *)-1
+
+
+
+/* ---- CALLBACK / CALL-IN -------------------------------------------------------- */
+/*! The callback structure allows logging of debug information within the
+ * files of the Synthesis SyncML engine. 'CreateContext' for module, session
+ * and datastore will pass a reference to such a struct.
+ * The same structure contains all call-in functions for the UI Api.
+ */
+
+/* <debugFlags>: Reserved plugin specific bit masks */
+enum DebugFlags {
+ /** No debugging */
+ DBG_PLUGIN_NONE = 0x0000,
+ /** Engine internal calls for plugin interface */
+ DBG_PLUGIN_INT = 0x0001,
+ /** DB access calls, standard for all plugin examples */
+ DBG_PLUGIN_DB = 0x0002,
+ /** DB access calls, exotic calls as well */
+ DBG_PLUGIN_EXOT = 0x0004,
+ /** direct printf calls for test */
+ DBG_PLUGIN_DIRECT= 0x0008,
+ /** Default mask: all bits set */
+ DBG_PLUGIN_ALL = 0xffff,
+};
+
+
+/*! Function prototypes for Debug calls */
+typedef void (*DB_DebugPuts_Func) ( void* aCallbackRef, cAppCharP aText );
+typedef void (*DB_DebugExotic_Func) ( void* aCallbackRef, cAppCharP aText );
+typedef void (*DB_DebugBlock_Func) ( void* aCallbackRef, cAppCharP aTag,
+ cAppCharP aDesc,
+ cAppCharP aAttrText );
+typedef void (*DB_DebugEndBlock_Func) ( void* aCallbackRef, cAppCharP aTag );
+typedef void (*DB_DebugEndThread_Func)( void* aCallbackRef );
+
+
+/* Function prototypes for UI call-in */
+/* ---- Engine init ---- */
+typedef TSyError (*SetStringMode_Func) ( void* aCB, uInt16 aCharSet,
+ uInt16 aLineEndMode,
+ bool aBigEndian );
+typedef TSyError (*InitEngineXML_Func) ( void* aCB, cAppCharP aConfigXML );
+typedef TSyError (*InitEngineFile_Func) ( void* aCB, cAppCharP aConfigFilePath );
+typedef TSyError (*InitEngineCB_Func) ( void* aCB, TXMLConfigReadFunc aReaderFunc, void* aContext );
+
+/* ---- Running a Sync Session ---- */
+typedef TSyError (*OpenSession_Func) ( void* aCB, SessionH *aSessionH, uInt32 aSelector,
+ cAppCharP aSessionName );
+typedef TSyError (*OpenSessionKey_Func) ( void* aCB, SessionH aSessionH,
+ KeyH *aKeyH, uInt16 aMode );
+typedef TSyError (*SessionStep_Func) ( void* aCB, SessionH aSessionH, uInt16 *aStepCmd,
+ TEngineProgressInfo *aInfoP );
+typedef TSyError (*GetSyncMLBuffer_Func) ( void* aCB, SessionH aSessionH, bool aForSend,
+ appPointer *aBuffer, memSize *aBufSize );
+typedef TSyError (*RetSyncMLBuffer_Func) ( void* aCB, SessionH aSessionH, bool aForSend,
+ memSize aRetSize );
+typedef TSyError (*ReadSyncMLBuffer_Func) ( void* aCB, SessionH aSessionH,
+ appPointer aBuffer, memSize aBufSize,
+ memSize *aValSize );
+typedef TSyError (*WriteSyncMLBuffer_Func)( void* aCB, SessionH aSessionH,
+ appPointer aBuffer, memSize aValSize );
+typedef TSyError (*CloseSession_Func) ( void* aCB, SessionH aSessionH );
+
+/* ---- Settings access ---- */
+typedef TSyError (*OpenKeyByPath_Func) ( void* aCB, KeyH *aKeyH,
+ KeyH aParentKeyH, cAppCharP aPath, uInt16 aMode );
+typedef TSyError (*OpenSubkey_Func) ( void* aCB, KeyH *aKeyH,
+ KeyH aParentKeyH, sInt32 aID, uInt16 aMode );
+typedef TSyError (*DeleteSubkey_Func) ( void* aCB, KeyH aParentKeyH, sInt32 aID );
+typedef TSyError (*GetKeyID_Func) ( void* aCB, KeyH aKeyH, sInt32 *aID );
+typedef TSyError (*SetTextMode_Func) ( void* aCB, KeyH aKeyH, uInt16 aCharSet,
+ uInt16 aLineEndMode, bool aBigEndian );
+typedef TSyError (*SetTimeMode_Func) ( void* aCB, KeyH aKeyH, uInt16 aTimeMode );
+typedef TSyError (*CloseKey_Func) ( void* aCB, KeyH aKeyH );
+
+typedef TSyError (*GetValue_Func) ( void* aCB, KeyH aKeyH, cAppCharP aValName, uInt16 aValType,
+ appPointer aBuffer, memSize aBufSize, memSize *aValSize );
+typedef TSyError (*GetValueByID_Func) ( void* aCB, KeyH aKeyH, sInt32 aID, sInt32 arrIndex,
+ uInt16 aValType,
+ appPointer aBuffer, memSize aBufSize, memSize *aValSize );
+typedef sInt32 (*GetValueID_Func) ( void* aCB, KeyH aKeyH, cAppCharP aName );
+
+typedef TSyError (*SetValue_Func) ( void* aCB, KeyH aKeyH, cAppCharP aValName, uInt16 aValType,
+ cAppPointer aBuffer, memSize aValSize );
+typedef TSyError (*SetValueByID_Func) ( void* aCB, KeyH aKeyH, sInt32 aID, sInt32 arrIndex,
+ uInt16 aValType,
+ cAppPointer aBuffer, memSize aValSize );
+
+/* ---- Function prototypes for DBApi ------------------------------------------------------------------- */
+typedef TSyError (*SDR_Func)( CContext ac, cAppCharP lastToken, cAppCharP resumeToken );
+typedef TSyError (*RdNItemSFunc)( CContext ac, ItemID aID, appCharP *aItemData,
+ sInt32 *aStatus, bool aFirst );
+typedef TSyError (*RdNItemKFunc)( CContext ac, ItemID aID, KeyH aItemKey,
+ sInt32 *aStatus, bool aFirst );
+typedef TSyError (*Rd_ItemSFunc)( CContext ac, cItemID aID, appCharP *aItemData );
+typedef TSyError (*Rd_ItemKFunc)( CContext ac, cItemID aID, KeyH aItemKey );
+typedef TSyError (*EDR_Func)( CContext ac );
+
+typedef TSyError (*SDW_Func)( CContext ac );
+typedef TSyError (*InsItemSFunc)( CContext ac, cAppCharP aItemData, cItemID aID );
+typedef TSyError (*InsItemKFunc)( CContext ac, KeyH aItemKey, cItemID aID );
+typedef TSyError (*UpdItemSFunc)( CContext ac, cAppCharP aItemData, cItemID aID, ItemID updID );
+typedef TSyError (*UpdItemKFunc)( CContext ac, KeyH aItemKey, cItemID aID, ItemID updID );
+typedef TSyError (*MovItem_Func)( CContext ac, cItemID aID, cAppCharP newParID );
+typedef TSyError (*DelItem_Func)( CContext ac, cItemID aID );
+typedef TSyError (*FLI_Func)( CContext ac, cItemID aID, ItemID updID );
+typedef TSyError (*DelSS_Func)( CContext ac );
+typedef TSyError (*EDW_Func)( CContext ac, bool success, appCharP *newToken );
+
+typedef void (*DisposeProc)( CContext xContext, void* memory );
+
+
+
+/* ------------------------------------------------------------------------------------------------------ */
+/*!< UI Api methods
+ * NOTE: As this struct is part of the SDK_Interface_Struct, it is frozen and not directly extendable
+ */
+typedef struct {
+ SetStringMode_Func SetStringMode;
+ InitEngineXML_Func InitEngineXML;
+ InitEngineFile_Func InitEngineFile;
+ InitEngineCB_Func InitEngineCB;
+
+ OpenSession_Func OpenSession;
+ OpenSessionKey_Func OpenSessionKey;
+ SessionStep_Func SessionStep;
+ GetSyncMLBuffer_Func GetSyncMLBuffer;
+ RetSyncMLBuffer_Func RetSyncMLBuffer;
+ ReadSyncMLBuffer_Func ReadSyncMLBuffer;
+ WriteSyncMLBuffer_Func WriteSyncMLBuffer;
+ CloseSession_Func CloseSession;
+
+ OpenKeyByPath_Func OpenKeyByPath;
+ OpenSubkey_Func OpenSubkey;
+ DeleteSubkey_Func DeleteSubkey;
+ GetKeyID_Func GetKeyID;
+ SetTextMode_Func SetTextMode;
+ SetTimeMode_Func SetTimeMode;
+ CloseKey_Func CloseKey;
+
+ GetValue_Func GetValue;
+ GetValueByID_Func GetValueByID;
+ GetValueID_Func GetValueID;
+ SetValue_Func SetValue;
+ SetValueByID_Func SetValueByID;
+} SDK_UI_Struct;
+
+
+/*! The SDK dbapi tunnel structure (for internal use only)
+ * NOTE: As this struct is part of the SDK_Interface_Struct, it is frozen and not directly extendable
+ */
+typedef struct {
+ SDR_Func StartDataRead;
+ RdNItemSFunc ReadNextItem;
+ Rd_ItemSFunc ReadItem;
+ EDR_Func EndDataRead;
+
+ SDW_Func StartDataWrite;
+ InsItemSFunc InsertItem;
+ UpdItemSFunc UpdateItem;
+ MovItem_Func MoveItem;
+ DelItem_Func DeleteItem;
+ EDW_Func EndDataWrite;
+
+ DisposeProc DisposeObj;
+
+ /* --- asKey functions */
+ RdNItemKFunc ReadNextItemAsKey;
+ Rd_ItemKFunc ReadItemAsKey;
+ InsItemKFunc InsertItemAsKey;
+ UpdItemKFunc UpdateItemAsKey;
+} SDK_Tunnel_Struct;
+
+
+
+enum {
+ /** Current callback version */
+ DB_Callback_Version = 11
+};
+
+/*! The SDK interface structure for callback and call-in */
+typedef struct SDK_InterfaceType {
+ uInt16 callbackVersion; /*!< The version of this callback struct */
+ appPointer callbackRef; /*!< The opaque pointer that must be returned in
+ * the \<callbackRef> argument of all callbacks
+ */
+
+ /* ---- callbackVersion>=1 fields ---- */
+ uInt16 debugFlags; /*!< Debug control flags: if<>0, debug is enabled */
+ DB_DebugPuts_Func DB_DebugPuts; /*!< Output prodedure to add things to the debug log */
+
+ CContext cContext; /*!< Callback C/C++ context, normally identical with
+ * \<mContext>; different e.g. for JNI
+ */
+
+ CContext mContext; /*!< Module context */
+ CContext sContext; /*!< Session context */
+
+ /* ---- callbackVersion>=2 fields ---- */
+ DB_DebugBlock_Func DB_DebugBlock; /*!< Output proc to open a named (=aTag) structure block in the log */
+ DB_DebugEndBlock_Func DB_DebugEndBlock; /*!< Output proc to close a prev. opened named structure block in the log */
+ DB_DebugEndThread_Func DB_DebugEndThread; /*!< Output proc to signal debug logging system that no more debug output
+ * will come from the calling thread
+ */
+ uInt32 lCount; /*!< Level counter, for internal use only */
+
+ /* ---- callbackVersion>=3 fields ---- */
+ DB_DebugExotic_Func DB_DebugExotic; /*!< Output prodedure to add exotic things to the debug log */
+
+ /* ---- callbackVersion>=4 fields ---- */
+ uInt16 allow_DLL_legacy; /*!< Legacy, because of alignment problems
+ * (as bool) on some platforms,
+ * Use <allow_DLL> for new applications.
+ */
+
+ /* ---- callbackVersion>=5 fields ---- */
+ uInt16 allow_DLL; /*!< DLLs can be used */
+
+ /* ---- callbackVersion>=6 fields ---- */
+ uInt32 reserved1; /*!< (Legacy UI_* funcs, no longer used) */
+ uInt32 reserved2;
+ uInt32 reserved3;
+ uInt32 reserved4;
+
+ /* ---- callbackVersion>=7 fields ---- */
+ appPointer thisCB; /*!< a reference to this structure directly */
+ uInt32 logCount; /*!< Incremental counter for logs */
+
+ /* ---- callbackVersion>=8 fields ---- */
+ appPointer thisBase; /*!< <this> base for abstract method calling */
+ appPointer jRef; /*!< <this> base for java access */
+ CContext gContext; /*!< common global context */
+ SDK_UI_Struct ui; /*!< UI Api methods */
+
+ /* ---- callbackVersion>=9 fields ---- */
+ memSize SDK_Interface_size; /* size of this structure */
+ SDK_Tunnel_Struct dt; /* dbapi tunnel callback functions, for internal use only */
+
+ /* ---- callbackVersion>=10 fields ---- */
+ /* no additional fields, just information that SUBSYSTEM (CA_SubSystem) is supported */
+
+ /* ---- callbackVersion>=11 fields ---- */
+ /* SDK_Tunnel_Struct extended (at the end) with "asKey" functions */
+
+ /* ---- callbackVersion>=12 fields will go here ---- */
+ /* ... */
+} SDK_Interface_Struct, *DB_Callback, *UI_Call_In;
+
+
+/* special case for <callbackVersion> = 8, higher versions contain a size field */
+#define SDK_Interface_Struct_V8 8
+#define SDK_Interface_Struct_V8_Size 184
+
+
+
+/* -------------------------------------------------------------------------------------- */
+/*! Function prototypes for engine connection/disconnection */
+typedef TSyError (*ConnectEngine_Func) ( UI_Call_In *aCI,
+ CVersion *aEngVersion,
+ CVersion aPrgVersion,
+ uInt16 aDebugFlags );
+
+typedef TSyError (*ConnectEngineS_Func) ( UI_Call_In aCI,
+ uInt16 aCallbackVersion,
+ CVersion *aEngVersion,
+ CVersion aPrgVersion,
+ uInt16 aDebugFlags );
+
+typedef TSyError (*DisconnectEngine_Func) ( UI_Call_In aCI );
+
+
+
+/* Entry point for connecting the SyncML engine from outside */
+ ENGINE_ENTRY TSyError SYSYNC_EXTERNAL(ConnectEngine)
+ ( UI_Call_In *aCI,
+ CVersion *aEngVersion,
+ CVersion aPrgVersion,
+ uInt16 aDebugFlags ) ENTRY_ATTR;
+
+ ENGINE_ENTRY TSyError SYSYNC_EXTERNAL(ConnectEngineS)
+ ( UI_Call_In aCI,
+ uInt16 aCallbackVersion,
+ CVersion *aEngVersion,
+ CVersion aPrgVersion,
+ uInt16 aDebugFlags ) ENTRY_ATTR;
+
+/* Entry point for disconnecting the engine at the end */
+ ENGINE_ENTRY TSyError SYSYNC_EXTERNAL(DisconnectEngine)
+ ( UI_Call_In aCI ) ENTRY_ATTR;
+
+
+#ifdef __cplusplus
+ } // namespace
+#endif
+
+#endif
+/* eof */
diff --git a/src/sysync_SDK/Sources/sync_declarations.h b/src/sysync_SDK/Sources/sync_declarations.h
new file mode 100644
index 0000000..15db8c5
--- /dev/null
+++ b/src/sysync_SDK/Sources/sync_declarations.h
@@ -0,0 +1,56 @@
+/*
+ * File: sync_declarations.h
+ *
+ * Author: Patrick Ohly <patrick.ohly@intel.com>
+ *
+ * C/C++ Programming interface between
+ * the Synthesis SyncML engine
+ * and the database layer
+ *
+ * Copyright (c) 2004-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ * These are the declarations for the calling interface between the
+ * Synthesis SyncML engine and a UI and/or DB plugin. Use this
+ * header file when you only need the declaration (= names) of the
+ * various items defined in sync_dbapidef.h. For definitions of
+ * enums include engine_defs.h.
+ *
+ * The following naming convention is used:
+ * - struct fooType = struct or class name
+ * - typedef struct { } foo_Struct = struct itself (in both C++ and C!)
+ * - typedef struct { } *foo = pointer to struct
+ *
+ * This convention has evolved over time and is not used completely
+ * consistently.
+ *
+ * Also note that some types refer to structs which have no real
+ * definition anywhere: they are only introduced to make interfaces
+ * type-safe. These pointer types use fooH as naming scheme.
+ */
+
+#ifndef SYNC_DECLARATIONS_H
+#define SYNC_DECLARATIONS_H
+
+#ifdef __cplusplus
+namespace sysync {
+#endif
+
+ struct SDK_InterfaceType;
+ struct ItemIDType;
+ typedef struct ItemIDType ItemID_Struct, *ItemID;
+ typedef const struct ItemIDType *cItemID;
+ struct MapIDType;
+ typedef struct MapIDType MapID_Struct, *MapID;
+ typedef const struct MapIDType *cMapID;
+ struct TEngineProgressType;
+ typedef struct SessionType *SessionH;
+ typedef struct KeyType *KeyH;
+
+ /* @TODO: typedef const MapID cMapID: a const pointer or a pointer to const struct?! */
+
+#ifdef __cplusplus
+} /* namespace sysync */
+#endif
+
+
+#endif /* SYNC_DECLARATIONS_H */
diff --git a/src/sysync_SDK/Sources/sync_include.h b/src/sysync_SDK/Sources/sync_include.h
new file mode 100644
index 0000000..60ee9ce
--- /dev/null
+++ b/src/sysync_SDK/Sources/sync_include.h
@@ -0,0 +1,93 @@
+/*
+ * File: sync_include.h
+ *
+ * Authors: Beat Forster (bfo@synthesis.ch)
+ *
+ *
+ * SDK include definitions
+ *
+ * Copyright (c) 2004-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ */
+
+
+#ifndef SYNC_INCLUDE_H
+#define SYNC_INCLUDE_H
+
+#include "target_options.h"
+#include "generic_types.h" /* some basic defs, which aren't available */
+#include "engine_defs.h"
+#include "syerror.h" /* "syerror.h" uses "generic_types.h" */
+
+#if !defined(__cplusplus) && !defined(__OBJC__)
+ #if !defined(SYSYNC_ENGINE) || !defined(__MACH__) || defined(__GNUC__)
+ typedef unsigned char bool;
+ #define false 0
+ #define true 1
+ #endif
+#endif
+
+#if !defined SYSYNC_ENGINE || !defined LINUX || defined __MACH__
+ typedef unsigned long ulong;
+#endif
+
+#ifdef SYSYNC_ENGINE
+ /* ==> if it is running within the SyncML engine */
+ #ifdef __cplusplus
+ #include "sysync.h"
+ #include "platform_file.h"
+ #else
+ #if defined _MSC_VER
+ #define NULL 0
+ #endif
+
+ #include "sysync_debug.h"
+ #endif
+#else
+ /* ==> if running standalone, e.g. at a plug-in module */
+ #include <stdio.h> /* used for printf calls */
+ #include <stdlib.h> /* used for the malloc/free calls */
+ #include <string.h> /* used for strcpy/strlen calls */
+
+ #ifdef __cplusplus
+ #ifndef __MACH__ /* MACH: IOFBF/RAND_MAX duplicate problem */
+ #include <string> /* STL includes */
+ #include <list>
+ #endif
+
+ #ifdef __GNUC__
+ #include <typeinfo>
+ #else
+ #include <typeinfo.h> /* type_info class */
+ #endif
+
+ using namespace std;
+ #endif
+
+ #if defined __MACH__ && !defined __GNUC__ /* used for va_list support */
+ #include <mw_stdarg.h>
+ #else
+ #include <stdarg.h>
+ #endif
+#endif
+
+#ifdef DBAPI_FILEOBJ
+ #ifndef FILEOBJ_SUPPORT
+ #define FILEOBJ_SUPPORT 1
+ #endif
+
+ #define PLATFORM_FILE 1
+#endif
+
+/* JAVA native interface JNI */
+#ifdef JNI_SUPPORT
+ #ifdef MACOSX
+ #include <JavaVM/jni.h>
+ #else
+ #include <jni.h>
+ #endif
+#endif
+
+
+#endif /* SYNC_INCLUDE_H */
+/* eof */
diff --git a/src/sysync_SDK/Sources/sync_uiapi.h b/src/sysync_SDK/Sources/sync_uiapi.h
new file mode 100755
index 0000000..5d8ab76
--- /dev/null
+++ b/src/sysync_SDK/Sources/sync_uiapi.h
@@ -0,0 +1,94 @@
+/*
+ * File: sync_uiapi.h
+ *
+ * Author: Beat Forster (bfo@synthesis.ch)
+ *
+ * C/C++ Programming interface between the
+ * Synthesis SyncML engine and
+ * the user interface layer for plug-ins
+ *
+ * Copyright (c) 2007-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ */
+
+/*
+ * This is the calling interface between the Synthesis SyncML engine
+ * and a customized module "sync_uiapi".
+ * The same interface can be used either for Standard C or for C++.
+ * And there is a equivalent interface for JNI (Java Native Interface).
+ * Normally the customized module will be compiled as DLL and will
+ * be called by the SyncML engine. A linkable library is available
+ * (for C++) as well.
+ *
+ * NOTE: The plugin's module context is currently defined at
+ * "sync_dbapi.h" as it is the same structure as for the
+ * data base plugin.
+ *
+ * NOTE: As the SyncML engine calls the plug-in multithreaded,
+ * all global structure accesses must be thread save.
+ */
+
+
+#ifndef SYNC_UIAPI_H
+#define SYNC_UIAPI_H
+
+/*
+// Global declarations
+#if defined __cplusplus
+ // combine the definitions of different namespaces
+ #ifdef SYSYNC_ENGINE
+ using sysync::TSyError;
+ #endif
+#endif
+*/
+
+/* C/C++ and DLL/library support
+ * SYSYNC_ENGINE: true ( within the engine itself ) / false ( outside )
+ * PLUGIN_INFO : true ( within "plugin_info" program ) / false ( everywhere else )
+ */
+#if !defined SYSYNC_ENGINE && !defined SYSYNC_ENGINE_TEST && !defined DBAPI_LINKED && !defined PLUGIN_INFO && !defined DLL_EXPORT
+ #define DLL_EXPORT
+#endif
+
+/* Visual Studio 2005 requires a specific entry point definition */
+/* This definition is empty for all other platforms */
+#undef _ENTRY_
+#ifdef DLL_EXPORT
+ #define _ENTRY_ ENGINE_ENTRY
+#else
+ #define _ENTRY_
+#endif
+
+
+/* -- UI interface -------------------------------------------------------------- */
+/*! Create a user interface context \<uContext>
+ *
+ * @param <uContext> Returns a value, which allows to identify this UI context.
+ * @param <uiName> Name of this user interface context
+ * @param <uCB> UI_Callback structure for module logging
+ *
+ * @return error code, if context could not be created (e.g. not enough memory),
+ * 0 if context successfully created.
+ */
+_ENTRY_ TSyError UI_CreateContext( CContext *uContext, cAppCharP uiName, UI_Call_In uCI );
+
+
+/*! Start interaction with user interface at \<uContext>.
+ */
+_ENTRY_ TSyError UI_RunContext( CContext uContext );
+
+
+/*! This routine will be called as the last call, before this module is disconnected.
+ * The SyncML engine will call 'UI_DisposeObj' (if required) before this call
+ *
+ * NOTE: This routine will be called ONLY, if the server stops in a controlled way.
+ * Its good programming practice not to wait for this 'DeleteContext' call.
+ *
+ * @param <uContext> The module context.
+ * @return error code
+ */
+_ENTRY_ TSyError UI_DeleteContext( CContext uContext );
+
+
+#endif
+/* eof */
diff --git a/src/sysync_SDK/Sources/timeutil.cpp b/src/sysync_SDK/Sources/timeutil.cpp
new file mode 100644
index 0000000..30d08ec
--- /dev/null
+++ b/src/sysync_SDK/Sources/timeutil.cpp
@@ -0,0 +1,157 @@
+/*
+ * File: timeutil.cpp
+ *
+ * Authors: Lukas Zeller (luz@synthesis.ch)
+ * Beat Forster (bfo@synthesis.ch)
+ *
+ * ISO8601 / Lineartime functions
+ *
+ * Copyright (c) 2001-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ */
+
+//#include "target_options.h"
+#include "sync_include.h"
+
+#if defined LINUX || defined MACOSX
+ // problems with "trunc"
+ #define _ISOC99_SOURCE 1
+ #include <math.h>
+#endif
+
+#ifdef _MSC_VER
+ static int trunc( double d ) {
+ return (int)( d-0.5 );
+ }
+#endif
+
+#ifdef MACOSX
+ #include <ctime>
+#endif
+
+#include <time.h>
+#include "timeutil.h"
+#include "stringutil.h"
+
+using namespace std;
+
+#ifdef __cplusplus
+ namespace sysync {
+#endif
+
+typedef struct tm struct_tm;
+typedef sInt32 lineardate_t;
+
+const lineartime_t linearDateToTimeFactor= (secondToLinearTimeFactor*60*60*24);
+
+// offset between algorithm base and 1970-01-01
+const lineartime_t UnixToLineartimeOffset= 2440588*linearDateToTimeFactor;
+
+
+// static version of time zone calculation
+// should be replaced by a full functioning version, when used
+static lineartime_t zoneOffsetAsSeconds()
+{
+ return (1)*60*60; // for CET
+//return (1+1)*60*60; // for CEST
+} // zoneOffsetAsSeconds
+
+
+// get system current date/time
+// this very simple implementation is rather local time than UTC
+lineartime_t utcNowAsLineartime()
+{
+ lineartime_t secs= time(NULL) - zoneOffsetAsSeconds();
+ return secs*secondToLinearTimeFactor + UnixToLineartimeOffset;
+} // utcNowAsLineartime
+
+
+
+static void lineardate2date( lineardate_t julDat,sInt16 *year, sInt16 *month, sInt16 *day )
+{
+ double C,E;
+ sInt32 B,D,F;
+
+ if (julDat<2299161) {
+ B=0;
+ C=julDat+1524;
+ }
+ else {
+ B= (sInt32)(trunc((julDat-1867216.25)/36524.25));
+ C=julDat+(B-trunc((double)B/4))+1525.0;
+ } // if
+
+ D=(sInt32)(trunc((C-122.1)/365.25));
+ E= 365.0*D+trunc((double)D/4);
+ F=(sInt32)(trunc((C-E)/30.6001));
+
+ // return date
+ *day = (sInt16)(trunc(C-E+0.5)-trunc(30.6001*F));
+ *month= (sInt16)(F-1-12*trunc((double)F/14));
+ *year = (sInt16)(D-4715-trunc((double)(7+*month)/10.0));
+} // lineardate2date
+
+
+
+// convert lineartime to h,m,s,ms
+static void lineartime2time( lineartime_t aTim, sInt16 *hour, sInt16 *min, sInt16 *sec, sInt16 *ms )
+{
+ // we have sub-seconds
+ *ms = aTim % secondToLinearTimeFactor;
+ aTim /= secondToLinearTimeFactor;
+
+ *sec = aTim % 60; aTim /= 60;
+ *min = aTim % 60; aTim /= 60;
+ *hour= aTim % 24; // to make sure we don't convert date part
+} // lineartime2time
+
+
+
+// convert struct tm to linear time (in milliseconds)
+static void lineartime2tm( lineartime_t aLt, struct_tm *tim )
+{
+ sInt16 y,mo,d, h,m,s, ms;
+
+ // date
+ lineardate2date( aLt/linearDateToTimeFactor, &y,&mo,&d );
+ tim->tm_year= y-1900;
+ tim->tm_mon = mo-1;
+ tim->tm_mday= d;
+
+ // time
+ lineartime2time( aLt, &h,&m,&s, &ms );
+ tim->tm_hour= h;
+ tim->tm_min = m;
+ tim->tm_sec = s;
+} // tm2lineartime
+
+
+
+// convert UTC lineartimestamp to ISO8601 string representation
+// (and return how long expired)
+sInt32 timeStampToISO8601( lineartime_t aTimeStamp, string &aString, bool dateOnly )
+{
+ aString.erase();
+ if (aTimeStamp==0) return 0;
+
+ struct_tm t;
+ lineartime2tm( aTimeStamp,&t );
+
+ // format as ISO8601 UTC
+ StringObjAppendPrintf ( aString, "%04d%02d%02d", t.tm_year+1900, t.tm_mon+1, t.tm_mday );
+
+ if (!dateOnly) {
+ StringObjAppendPrintf( aString, "T%02hd%02hd%02hd", t.tm_hour, t.tm_min, t.tm_sec );
+ aString+= 'Z'; // add UTC designator
+ } // if
+
+ return 0;
+} // timeStampToISO8601
+
+
+#ifdef __cplusplus
+ } // namespace
+#endif
+
+/* eof */
+
diff --git a/src/sysync_SDK/Sources/timeutil.h b/src/sysync_SDK/Sources/timeutil.h
new file mode 100644
index 0000000..a8dc751
--- /dev/null
+++ b/src/sysync_SDK/Sources/timeutil.h
@@ -0,0 +1,52 @@
+/*
+ * File: timeutil.h
+ *
+ * Authors: Lukas Zeller (luz@synthesis.ch)
+ * Beat Forster (bfo@synthesis.ch)
+ *
+ * ISO8601 / Lineartime functions
+ *
+ * Copyright (c) 2001-2009 by Synthesis AG (www.synthesis.ch)
+ *
+ */
+
+#ifndef TIMEUTIL_H
+#define TIMEUTIL_H
+
+#include "sync_dbapidef.h"
+
+#ifdef __cplusplus
+ #include <string>
+ using namespace std;
+#endif
+
+
+#ifdef __cplusplus
+ namespace sysync {
+#endif
+
+
+#if defined MACOSX || defined _WIN32 || defined LINUX
+ typedef sInt64 lineartime_t;
+#else
+ #error unknown platform
+#endif
+
+
+#ifdef __cplusplus
+ const lineartime_t secondToLinearTimeFactor = 1000; // unit is milliseconds
+
+ // get system current date/time
+ lineartime_t utcNowAsLineartime();
+
+ // convert timestamp to ISO8601 string representation
+ sInt32 timeStampToISO8601( lineartime_t aTimeStamp, string &aString, bool dateOnly= false );
+#endif
+
+
+#ifdef __cplusplus
+ } // namespace
+#endif
+
+#endif /* TIMEUTIL_H */
+/* eof */
diff --git a/src/sysync_SDK/configs/sunbird_client.xml b/src/sysync_SDK/configs/sunbird_client.xml
new file mode 100755
index 0000000..f991b8c
--- /dev/null
+++ b/src/sysync_SDK/configs/sunbird_client.xml
@@ -0,0 +1,821 @@
+<?xml version="1.0"?>
+<!-- SYNTHESIS SYNC CLIENT Version 3.1 Configuration file -->
+<!-- (works for V3.1.1.0 and later) -->
+
+<sysync_config version="1.0">
+
+ <configvar platform="win32" name="logpath" value="$(exedir_path)\sysynclogs"/>
+ <configvar platform="macosx" name="logpath" value="$(userdir_path)/sysynclogs"/>
+
+ <configvar platform="macosx" name="targetdb" value="sunbird"/> <!-- %%% mac does not have that yet -->
+
+ <!-- enter your license details here. Not all versions need a
+ license (for example the demo runs without one).
+ You should have received license code information
+ along with the purchase or evaluation package.
+ If not, please contact sales@synthesis.ch
+ <licensename>your license text here</licensename>
+ <licensecode>YOUR-LICE-NSES-CODE</licensecode>
+ -->
+
+ <!-- this string is output to every session debug logfile to identify the config in use -->
+ <configidstring>Sunbird/Lightning 0.5 client demo using SQLite direct access</configidstring>
+
+ <debug>
+ <!-- path where logfiles are stored -->
+ <logpath>$(logpath)</logpath>
+ <logflushmode>flush</logflushmode> <!-- buffered is fastest mode, but may loose data on process abort. Other options: "flush" (after every line) or "openclose" (safest, slowest, like in 2.x server) -->
+ <!-- per session log -->
+ <sessionlogs>yes</sessionlogs> <!-- by default, create a session log file for every sync session (might be disabled for special users/devices in scripts) -->
+ <!-- debug format options -->
+ <logformat>html</logformat> <!-- html is nicely colored and easily viewable with a web browser. Other options: "xml", "text" -->
+ <timestamp>yes</timestamp> <!-- show timestamps for structure elements in log -->
+ <timestampall>no</timestampall> <!-- don't show timestamp for every log line -->
+ <timedsessionlognames>yes</timedsessionlognames> <!-- session logs also have the session start timestamp in the filename - makes them more easily sortable -->
+ <!-- thread logging mode -->
+ <subthreadmode>separate</subthreadmode> <!-- write log info from subthreads into separate log files. Other options: "suppress" -->
+ <!-- basic debug level selection -->
+ <enable option="extended"/> <!-- "extended" is a good choice for start testing. For production, use "normal" or "minimal" -->
+ <!-- <enable option="normal"/> --> <!-- "normal" provides rich debug info, but still in reasonable size -->
+ <!-- <enable option="minimal"/> --> <!-- "minimal" just shows basic flow and error. Not suitable for debugging -->
+ <!-- <enable option="maximal"/> --> <!-- "maximal" can create VERY LARGE logs and cause HEAVY SLOWDOWN. Only for detail debugging -->
+ <!-- <enable option="all"/> --> <!-- "all" shows EVERYTHING possible, and way too much for any normal situation. For hardcore debugging ONLY! -->
+ <!-- additional debug info switches -->
+ <enable option="userdata"/> <!-- Make this <disable ...> if you don't want user data in the logs -->
+ <enable option="scripts"/> <!-- Make this <enable ...> to show script execution in logs -->
+ <disable option="exotic"/> <!-- Make this <enable ...> to include very in-detail info. CAN PRODUCE ENORMOUS LOGS and HEAVILY IMPACT PERFORMANCE for large slow syncs - use with care! -->
+ <!-- see manual for more debug info switches -->
+ <!-- global log options -->
+ <globallogs>yes</globallogs> <!-- by default, do not log global session dispatching, creation etc. (not useful in multi-user operation) -->
+ <singlegloballog>no</singlegloballog> <!-- a new global log will be started for every start of the server/application -->
+ <!-- SyncML message dumping options -->
+ <msgdump>no</msgdump> <!-- do not dump syncml traffic 1:1 to files -->
+ <xmltranslate>no</xmltranslate> <!-- do not try to translate syncml traffic into XML (DO NOT SET THIS OPTION IN PRODUCTIVE SERVERS!) -->
+ </debug>
+
+ <!-- Note: since 2.1.1.5, clients have a <transport> section to specify <keepconnection> behaviour -->
+ <transport type="xpt">
+ <!-- allow HTTP 1.1 kepp-alive (multiple request-answer-exchanges in single TCP connection) -->
+ <keepconnection>true</keepconnection>
+ </transport>
+
+ <scripting>
+ <looptimeout>5</looptimeout>
+ </scripting>
+
+
+ <datatypes>
+
+
+ <!-- list of internal fields representing vCalendar data
+ (for both events (vEVENT) and tasks (vTODO)) -->
+ <fieldlist name="calendar">
+ <field name="SYNCLVL" type="integer" compare="never"/>
+
+ <!-- this field defines if the data item is an EVENT or a TODO (task) -->
+ <field name="KIND" type="string" compare="always"/>
+
+ <field name="DMODIFIED" type="timestamp" compare="never" age="yes"/>
+ <field name="DCREATED" type="timestamp" compare="never"/>
+
+ <field name="CATEGORIES" type="multiline" compare="conflict" merge="fillempty"/>
+ <field name="CLASS" type="string" compare="conflict" merge="fillempty"/>
+
+ <field name="SUMMARY" type="multiline" compare="always"/>
+ <field name="DESCRIPTION" type="multiline" compare="slowsync" merge="lines"/>
+
+ <field name="ATTENDEES" array="yes" type="multiline" compare="conflict" merge="fillempty"/>
+
+ <field name="LOCATION" type="multiline" compare="conflict" merge="fillempty"/>
+
+ <!-- alarm -->
+ <field name="ALARM_TIME" type="timestamp" compare="conflict"/>
+ <field name="ALARM_SNOOZE" type="string" compare="conflict"/>
+ <field name="ALARM_REPEAT" type="string" compare="conflict"/>
+ <field name="ALARM_MSG" type="string" compare="conflict"/>
+
+ <!-- recurrence rule block, fields must be in that order, including
+ DTSTART as last field !! -->
+ <field name="RR_FREQ" type="string" compare="conflict"/>
+ <field name="RR_INTERVAL" type="integer" compare="conflict"/>
+ <field name="RR_FMASK" type="integer" compare="conflict"/>
+ <field name="RR_LMASK" type="integer" compare="conflict"/>
+ <field name="RR_END" type="timestamp" compare="conflict"/>
+
+ <!-- Note: DTSTART/DTEND are compared in the <comparescript>,
+ therefore compare is set no "never" here -->
+ <field name="DTSTART" type="timestamp" compare="never"/> <!-- also used as DUE in vTODO -->
+
+ <field name="DTEND" type="timestamp" compare="never"/>
+
+ <field name="ALLDAY" type="integer" compare="never"/>
+
+ <!-- exception dates to the recurrence rule -->
+ <field name="EXDATES" array="yes" type="timestamp" compare="conflict"/>
+
+ <!-- for tasks only -->
+ <field name="PRIORITY" type="integer" compare="conflict"/>
+ <field name="STATUS" type="string" compare="conflict" merge="fillempty"/>
+
+ </fieldlist>
+
+ <!-- vCalendar profile, including subprofiles for
+ both events (vEVENT) and tasks (vTODO) -->
+ <mimeprofile name="vcalendar" fieldlist="calendar">
+
+ <profile name="VCALENDAR" nummandatory="1">
+
+ <property name="VERSION" mandatory="yes">
+ <value conversion="version"/>
+ </property>
+
+ <property name="TZ" show="true" suppressempty="yes">
+ <value field="DTSTART" conversion="tz"/>
+ </property>
+
+ <property name="DAYLIGHT" mode="daylight" show="true" suppressempty="yes">
+ <value field="DTSTART" conversion="daylight"/>
+ </property>
+
+ <!-- sub-profile for events -->
+ <subprofile name="VEVENT" nummandatory="1" field="KIND" value="EVENT" showprops="true">
+
+ <property name="LAST-MODIFIED" delayedparsing="1">
+ <value field="DMODIFIED"/>
+ </property>
+
+ <property name="DCREATED" suppressempty="yes">
+ <value field="DCREATED"/>
+ </property>
+
+ <property name="CATEGORIES" suppressempty="yes">
+ <value field="CATEGORIES"/>
+ </property>
+
+ <property name="CLASS" suppressempty="yes">
+ <value field="CLASS"/>
+ </property>
+
+ <property name="SUMMARY" mandatory="yes">
+ <value field="SUMMARY"/>
+ </property>
+
+ <property name="DESCRIPTION" mandatory="yes">
+ <value field="DESCRIPTION"/>
+ </property>
+
+ <property name="LOCATION" suppressempty="yes">
+ <value field="LOCATION"/>
+ </property>
+
+ <property name="DTSTART" suppressempty="yes">
+ <value field="DTSTART"/>
+ <!--
+ <parameter name="TZID" default="no" show="yes">
+ <value field="DTSTART" conversion="TZID"/>
+ </parameter>
+ -->
+ </property>
+
+ <property name="DTEND" suppressempty="yes">
+ <value field="DTEND"/>
+ <!--
+ <parameter name="TZID" default="no" show="yes">
+ <value field="DTEND" conversion="TZID"/>
+ </parameter>
+ -->
+ </property>
+
+ <property name="ATTENDEE" suppressempty="yes">
+ <value field="ATTENDEES"/>
+ <position field="ATTENDEES" repeat="ARRAY" increment="1" minshow="0"/>
+ </property>
+
+ <!-- recurrence rule (with delayed parsing, as it is dependent on DTSTART) -->
+ <property name="RRULE" suppressempty="yes" delayedparsing="1">
+ <!-- Note: RR_FREQ is the beginning of a block of fields
+ suitable for the "rrule" conversion mode -->
+ <value field="RR_FREQ" conversion="rrule"/>
+ </property>
+
+ <!-- up to five exceptions for recurrence rule -->
+ <property name="EXDATE" values="list" suppressempty="yes">
+ <value field="EXDATES"/>
+ <position field="EXDATES" repeat="ARRAY" increment="1" minshow="0"/>
+ </property>
+
+ <!-- AALARM and DALARM both use the same fields -->
+ <property name="AALARM" values="4" suppressempty="yes">
+ <value index="0" field="ALARM_TIME" conversion="emptyonly"/>
+ <value index="1" field="ALARM_SNOOZE" conversion="emptyonly"/>
+ <value index="2" field="ALARM_REPEAT" conversion="emptyonly"/>
+ <value index="3" field="ALARM_MSG" conversion="emptyonly"/>
+ </property>
+ <property name="DALARM" values="4" suppressempty="yes">
+ <value index="0" field="ALARM_TIME" conversion="emptyonly"/>
+ <value index="1" field="ALARM_SNOOZE" conversion="emptyonly"/>
+ <value index="2" field="ALARM_REPEAT" conversion="emptyonly"/>
+ <value index="3" field="ALARM_MSG" conversion="emptyonly"/>
+ </property>
+
+ </subprofile>
+
+ <!-- sub-profile for tasks (todo's) -->
+ <subprofile name="VTODO" nummandatory="1" field="KIND" value="TODO" showprops="true">
+
+ <property name="LAST-MODIFIED" show="false">
+ <value field="DMODIFIED"/>
+ </property>
+
+ <property name="DCREATED" suppressempty="yes" show="false">
+ <value field="DCREATED"/>
+ </property>
+
+ <property name="CATEGORIES" suppressempty="yes" show="false">
+ <value field="CATEGORIES"/>
+ </property>
+
+ <property name="CLASS" suppressempty="yes" show="false">
+ <value field="CLASS"/>
+ </property>
+
+ <property name="SUMMARY" mandatory="yes" show="false">
+ <value field="SUMMARY"/>
+ </property>
+
+ <property name="DESCRIPTION" mandatory="yes" show="false">
+ <value field="DESCRIPTION"/>
+ </property>
+
+ <property name="LOCATION" suppressempty="yes" show="false">
+ <value field="LOCATION"/>
+ </property>
+
+
+ <property name="DUE" suppressempty="yes" show="true">
+ <value field="DTSTART"/> <!-- we use DTSTART field for DUE property in tasks -->
+ </property>
+
+ <property name="ATTENDEE" suppressempty="yes" show="false">
+ <value field="ATTENDEES"/>
+ <position field="ATTENDEES" repeat="ARRAY" increment="1" minshow="0"/>
+ </property>
+
+ <property name="PRIORITY" suppressempty="yes" show="true">
+
+ <value field="PRIORITY"/>
+ </property>
+
+ <property name="STATUS" suppressempty="yes" show="true">
+ <value field="STATUS" conversion="emptyonly"/>
+ </property>
+
+ <!-- AALARM and DALARM both use the same fields -->
+ <property name="AALARM" values="4" suppressempty="yes" show="false">
+ <value index="0" field="ALARM_TIME" conversion="emptyonly"/>
+ <value index="1" field="ALARM_SNOOZE" conversion="emptyonly"/>
+ <value index="2" field="ALARM_REPEAT" conversion="emptyonly"/>
+ <value index="3" field="ALARM_MSG" conversion="emptyonly"/>
+ </property>
+ <property name="DALARM" values="4" suppressempty="yes" show="false">
+ <value index="0" field="ALARM_TIME" conversion="emptyonly"/>
+ <value index="1" field="ALARM_SNOOZE" conversion="emptyonly"/>
+ <value index="2" field="ALARM_REPEAT" conversion="emptyonly"/>
+ <value index="3" field="ALARM_MSG" conversion="emptyonly"/>
+ </property>
+
+ </subprofile>
+
+ <subprofile name="VTIMEZONE" mode="vtimezones" showprops="true"/>
+
+ </profile>
+ </mimeprofile>
+
+
+ <!-- vCalendar 1.0 datatype, using vCalendar profile defined
+ above -->
+ <datatype name="vcalendar10" basetype="vcalendar">
+ <version>1.0</version>
+ <use mimeprofile="vcalendar"/>
+
+ <!-- Scripting allows sophisticated data adjustment:
+ make sure we have always have a SUMMARY, but avoid
+ SUMMARY and DESCRIPTION being identical,
+ as well as always having a DTSTART
+ and an alarm message -->
+ <incomingscript><![CDATA[
+ // make sure we have all trailing and leading spaces eliminated
+ DESCRIPTION=NORMALIZED(DESCRIPTION);
+ SUMMARY=NORMALIZED(SUMMARY);
+ // make sure we have at least a summary
+ if (SUMMARY==EMPTY) SUMMARY=DESCRIPTION; // use description if we don't have a summary
+ if (SUMMARY==EMPTY) SUMMARY="unnamed"; // set dummy summary if we still don't have one
+ if (DESCRIPTION==SUMMARY) DESCRIPTION=EMPTY; // do not save twice
+ // make sure we have DTSTART
+ if (DTSTART==EMPTY) DTSTART=DTEND;
+ ]]></incomingscript>
+
+ <outgoingscript><![CDATA[
+ // make sure we have all trailing and leading spaces eliminated
+ DESCRIPTION=NORMALIZED(DESCRIPTION);
+ SUMMARY=NORMALIZED(SUMMARY);
+ // make sure we have both description AND summary
+ if (DESCRIPTION==EMPTY) DESCRIPTION=SUMMARY; // use summary text to send
+ if (SUMMARY==EMPTY) SUMMARY="unnamed";
+ if (DESCRIPTION==EMPTY) DESCRIPTION=SUMMARY; // use summary text to send
+ // make sure we have an alarm message
+ if (ALARM_TIME!=EMPTY && ALARM_MSG==EMPTY) ALARM_MSG="alarm";
+ // make sure we have DTSTART
+ if (DTSTART==EMPTY) DTSTART=DTEND;
+ ]]></outgoingscript>
+
+ </datatype>
+
+
+ </datatypes>
+
+
+ <client type="sql">
+
+ <binfilespath platform="win32">$(prefs_path)\synthesis.ch\syncml_client_$(targetdb)</binfilespath>
+
+ <sessioninitscript><![CDATA[
+ string s,res;
+ string matches[];
+ integer n;
+ integer h,m;
+
+ // %%% testing NUMFORMAT
+ h=2;
+ m=30;
+ DEBUGMESSAGE (NUMFORMAT(h,3,"0","+") + ":" + NUMFORMAT(m,2,"0"));
+ h=-2;
+ m=30;
+ DEBUGMESSAGE (NUMFORMAT(h,3,"0","+") + ":" + NUMFORMAT(m,2,"0"));
+ h=11;
+ m=30;
+ DEBUGMESSAGE (NUMFORMAT(h,3,"0","+") + ":" + NUMFORMAT(m,2,"$"));
+ h=11;
+ m=3;
+ DEBUGMESSAGE (NUMFORMAT(h,3,"0","+") + ":" + NUMFORMAT(m,-2,"x"));
+
+ // %%% testing regexps
+ s = "the quick brown fox faxes fudi fast";
+ /*
+ n = REGEX_FIND(s,"/QUICK/i",0);
+ n = REGEX_FIND(s,"/QUICK (.*) fox/i",0);
+ n = REGEX_FIND(s,"/(BroWN) (.*)/i",0);
+
+ n = REGEX_MATCH(s,"/(QUICK) (.*) fox/i",0,matches);
+ DEBUGMESSAGE(matches[0]);
+ DEBUGMESSAGE(matches[1]);
+ DEBUGMESSAGE(matches[2]);
+
+ res = REGEX_REPLACE(s,"/f(.)/","[F]\\1(\\2)",0,2); // 2 recurrences
+ DEBUGMESSAGE(res);
+ */
+
+ res = REGEX_REPLACE(s,"/f(.)/","[F]\\1(\\2)",0,0); // all
+ DEBUGMESSAGE(res);
+
+ ]]></sessioninitscript>
+
+
+ <!-- IMPORTANT: Note that the following charset, lineend and quoting settings are relevant for session-level data access only.
+ Most data access takes place in individual <datastores>, which each has the same three settings
+ again locally (see below in the <datastore> sections). -->
+
+ <!-- text db plugin is designed for UTF-8, make sure data is passed as UTF-8 (and not the ISO-8859-1 default) -->
+ <datacharset>UTF-8</datacharset>
+ <!-- text db plugin is designed for Unix linefeeds (\n, 0x0A), make data is passed with unix linefeeds (and not the DOS CRLF = 0x0D,0x0A default) -->
+ <datalineends>unix</datalineends>
+
+ <!-- If you want a text logfile, specify its full path here: -->
+ <logfile platform="win32">$(logpath)\$(targetdb)_synclog.txt</logfile>
+
+ <!-- %%% mac does not have $(targetdb) yet -->
+ <logfile platform="macosx">$(logpath)/sunbirdsynclog.txt</logfile>
+
+ <logenabled>yes</logenabled> <!-- log enabled by default (session login scripts might disable it for special users/devices) -->
+
+ <!-- the logfile format can be customized with <loglabels> and <logformat>
+ but if these are not specified, a default format will be used
+ -->
+ <!-- This sample produces a simplified logfile:
+ <loglabels>SyncEndTime\tUser\tStatus\tSynctype\tRemoteName\tDatabase\tLocAdded\tLocUpdated\tLocDeleted\n\n</loglabels>
+ <logformat>%seT\t%U\t%sS\t%tS\t%nR\t%lD\t%laI\t%luI\t%ldI\n</logformat>
+ -->
+
+
+ <!-- timeout for unfinished sessions in seconds -->
+ <sessiontimeout>20</sessiontimeout>
+
+
+ <datastore name="events" type="sql">
+
+ <alertprepscript><![CDATA[
+ if (targetsetting("extras") & 1 == 1) {
+ setdaysrange(
+ targetsetting("limit1"),
+ targetsetting("limit2")
+ );
+ }
+ ]]></alertprepscript>
+
+ <dbtypeid>1001</dbtypeid> <!-- %%% DBID_MAPI_CONTACTS for now -->
+
+ <sqlitefile platform="win32">$(mozillaprofile)\storage.sdb</sqlitefile>
+
+ <!-- %%% mac does not have lightning support yet -->
+ <sqlitefile platform="macosx">$(prefs_path)/Sunbird/Profiles/$(sunbirdprofile)/storage.sdb</sqlitefile>
+
+ <lastmodfieldtype>unixtime_us</lastmodfieldtype>
+
+ <!-- text db plugin is designed for UTF-8, make sure data is passed as UTF-8 (and not the ISO-8859-1 default) -->
+ <datacharset>UTF-8</datacharset>
+ <!-- text db plugin is designed for Unix linefeeds (\n, 0x0A), make data is passed with unix linefeeds (and not the DOS CRLF = 0x0D,0x0A default) -->
+ <datalineends>unix</datalineends>
+
+ <!-- specifies the time zone for timestamps stored in the database (UTC, SYSTEM, ...) -->
+ <datatimezone>UTC</datatimezone>
+
+ <!-- if set to yet, all timestamps read from the DB are converted to user time zone (from DB time zone) before sending to remote -->
+ <userzoneoutput>yes</userzoneoutput>
+
+ <!-- enable this one if the database needs all fields re-written for every UPDATE
+ operation (exotic Triggers etc.). Normally, this is not needed
+ (SyncML server will update only changed fields)
+ -->
+ <updateallfields>no</updateallfields>
+
+ <!-- If this is set to yes, the server will try to convert
+ the filter expressions to a WHERE clause which then can be used with %AF or %WF
+ in the <selectidandmodifiedsql> statement. In this sample it is set to "yes"
+ as it allows for efficient visibility control. It can be turned off if filters
+ are not suitable for being included in SQL -->
+ <dbcanfilter>yes</dbcanfilter>
+
+ <!-- statement that retrieves KEY and MODIFIED timestamp for all records to be synced.
+ Note that %AF or %WF must be included to make filtering work
+ -->
+ <selectidandmodifiedsql>SELECT id || coalesce(recurrence_id,'') as fullid,last_modified FROM cal_events %WF</selectidandmodifiedsql>
+
+ <!-- We use our own IDs as ROWIDs are not persistent in SunBird -->
+ <obtainidafterinsert>no</obtainidafterinsert>
+
+ <localidscript><![CDATA[
+ // create new ID from random plus time
+ return "SySync:" + NUMFORMAT(RANDOM(10000),4,"0") + "_" + (STRING)MILLISECONDS(NOW());
+ ]]></localidscript>
+
+ <!-- statements to read, insert, update and delete single records in the database
+ %N represents the name list of all mapped fields
+ %v represents the value list of all mapped fields
+ %V represents the name=value list of all mapped fields
+ %k represents the record key (CONTACTS_KEY, EVENTS_KEY...)
+ %f represents the folder key (see <folderkeysql> above)
+ -->
+ <selectdatasql>SELECT %N FROM cal_events WHERE id || coalesce(recurrence_id,'')='%k'</selectdatasql>
+ <insertdatasql>INSERT INTO cal_events (id,last_modified,time_created,%N,cal_id,priority,event_start_tz,event_end_tz) VALUES ('%k',%M,%M,%v,0,0,'/mozilla.org/20070129_1/Africa/Ceuta','/mozilla.org/20070129_1/Africa/Ceuta')</insertdatasql>
+ <updatedatasql>UPDATE cal_events SET last_modified=%M,%V WHERE id || coalesce(recurrence_id,'')='%k'</updatedatasql>
+ <deletedatasql>DELETE FROM cal_events WHERE id || coalesce(recurrence_id,'')='%k'</deletedatasql>
+
+ <!-- Database has combined Date/time field for saving time of last modification. -->
+ <modtimestamp>yes</modtimestamp>
+
+ <!-- filter to let only suitable vCalendar items pass -->
+ <acceptfilter>F.KIND:=EVENT</acceptfilter>
+
+ <!-- if set, SyncML DS 1.2 filtering is possible and will be indicated in server devInf -->
+ <ds12filters>yes</ds12filters>
+
+ <!-- if set, datastore supports filtering by a date range specification (SINCE,BEFORE or /dr())
+ Note: if this is set to yes, actual range filtering must be implemented in the datatype
+ and/or the datastore, usually with scripting. See <filterscript> for vCalendar above -->
+ <daterangesupport>yes</daterangesupport>
+
+
+ <!-- Mapping of the fields in SYNC_EVENTS to the common fieldlist "calendar"
+ which is shared between events and tasks -->
+ <fieldmap fieldlist="calendar">
+
+ <initscript><![CDATA[
+ // general vars
+ integer ok, version, daylist[], numelems, i, wd, rc, rcidx;
+ string s, dateslist[];
+ // flags
+ integer flags; // 0x08 = allday, 0x04 = must always be present
+ // recurrence params
+ string recur_type;
+ integer interval,count;
+ timestamp end_date,ts;
+ string day; // comma separated list of weekday as 1=Su, 2=Mo ... 7=Sa
+ // plus for monthly: 8*occurrence in month
+ // (e.g: 37 = 0x25 = 4th thursday, -13 = 0x0D = last thursday)
+ string monthday; // comma separated list of days in month
+ string dates; // for exception dates
+
+ // init random seed
+ RANDOM(1,NOW());
+
+ // init flags
+ flags = 4;
+
+ // check for correct schema version
+ SQLEXECUTE("select version from cal_calendar_schema_version");
+ ok = SQLFETCHROW();
+ if (ok) {
+ SQLGETCOLUMN(1,version,"numeric");
+ ok = version == 7; // this configuration is for SunBird 0.5 with schema version 7
+ }
+ SQLCOMMIT();
+ if (!ok) {
+ DEBUGMESSAGE("SunBird SQLite database has none or wrong schema version");
+ ABORTSESSION(510);
+ RETURN false; // fail the script
+ }
+ ]]></initscript>
+
+
+ <beforewritescript><![CDATA[
+ // create flags
+ flags = 0x04; // we need Bit2 always
+ // check for allay (non-floating timestamps checked in user time zone)
+ i = ALLDAYCOUNT(DTSTART,DTEND,true);
+ if (i>0) {
+ // all day - make dates span full day (required by SunBird)
+ flags = flags | 0x08; // set allday bit
+ DTSTART = CONVERTTOUSERZONE(DTSTART); // convert non-floating to user zone
+ SETFLOATING(DTSTART); // make floating
+ DTSTART = DATEONLY(DTSTART); // set to beginning of day
+ DTEND = DTSTART + DAYUNITS(i); // make end exact number of days after start
+ }
+ // check for recurrence
+ if (NORMALIZED(RR_FREQ)!=EMPTY)
+ flags = flags | 0x10; // set recurrence bit
+ ]]></beforewritescript>
+
+
+ <afterwritescript><![CDATA[
+ // update separately stored properties
+ // - delete all associated properties
+ SQLEXECUTE("DELETE FROM cal_properties WHERE (key='LOCATION' OR key='DESCRIPTION') AND item_id='%k'");
+ if (!DELETING()) {
+ // (re-)add properties
+ // - location
+ if (LOCATION!=EMPTY) {
+ SQLEXECUTE(
+ "INSERT INTO cal_properties (key,item_id,value) VALUES ('LOCATION','%k'," +
+ DBLITERAL(LOCATION,"string") + ")"
+ );
+ }
+ // - description
+ if (DESCRIPTION!=EMPTY) {
+ SQLEXECUTE(
+ "INSERT INTO cal_properties (key,item_id,value) VALUES ('DESCRIPTION','%k'," +
+ DBLITERAL(DESCRIPTION,"string") + ")"
+ );
+ }
+ }
+ SQLCOMMIT();
+ // update recurrence
+ // - delete all associated recurrence info
+ SQLEXECUTE("DELETE FROM cal_recurrence WHERE item_id='%k'");
+ if (!DELETING()) {
+ // (re-)add recurrence info
+ // - recurrence itself
+ interval = RR_INTERVAL;
+ end_date = RR_END;
+ day = "";
+ monthday = "";
+ daylist = EMPTY;
+ recur_type = EMPTY;
+ // - check type of recurrence
+ if (SUBSTR(RR_FREQ,0,1)=="D") {
+ recur_type = "DAILY";
+ }
+ else if (RR_FREQ=="WW") {
+ recur_type = "WEEKLY";
+ // create list of days 1..7 = SU..SA
+ i=0;
+ while (i<7) {
+ if (RR_FMASK & (1<<i))
+ daylist[SIZE(daylist)]=i+1;
+ i=i+1;
+ }
+ day = EXPLODE(",",daylist);
+ }
+ else if (RR_FREQ=="MD") {
+ recur_type = "MONTHLY";
+ // create list of monthdays 1..31
+ i=0;
+ while (i<31) {
+ if (RR_FMASK & (1<<i))
+ daylist[SIZE(daylist)] = i+1;
+ if (RR_LMASK & (1<<i))
+ daylist[SIZE(daylist)] = -(i+1);
+ i=i+1;
+ }
+ monthday = EXPLODE(",",daylist);
+ }
+ else if (RR_FREQ=="MW") {
+ recur_type = "MONTHLY";
+ // create list of nth weekday in month as (weekday 1..7) + (nTh * 8)
+ i=0;
+ while (i<35) {
+ if (RR_FMASK & (1<<i))
+ daylist[SIZE(daylist)] = (i%7)+1 + 8*(i/7+1);
+ if (RR_LMASK & (1<<i))
+ daylist[SIZE(daylist)] = -((i%7)+1 + 8*(i/7+1));
+ i=i+1;
+ }
+ day = EXPLODE(",",daylist);
+ }
+ else if (SUBSTR(RR_FREQ,0,1)=="Y") {
+ recur_type="YEARLY";
+ }
+ // - now write recurrence, if any
+ if (NORMALIZED(RR_FREQ) != EMPTY) {
+ // - write
+ rcidx = 0;
+ SQLEXECUTE(
+ "INSERT INTO cal_recurrence (item_id, recur_index, recur_type, interval, end_date, day, monthday) VALUES ('%k', " +
+ DBLITERAL(rcidx,"numeric") + "," +
+ DBLITERAL(recur_type,"string") + "," +
+ DBLITERAL(interval,"numeric") + "," +
+ DBLITERAL(end_date,"unixtime_us") + "," +
+ DBLITERAL(day,"string") + "," +
+ DBLITERAL(monthday,"string") + ")"
+ );
+ rcidx=rcidx+1;
+ // - exceptions
+ i=0;
+ WHILE(i<SIZE(EXDATES)) {
+ dates = "ZT" + (string)(TIMESTAMPTODBINT(CONVERTTODATAZONE(EXDATES[i]),"unixtime_us")) + ":/mozilla.org/20070129_1/Africa/Ceuta";
+ // create x-date exception record
+ SQLEXECUTE(
+ "INSERT INTO cal_recurrence (item_id, recur_index, recur_type, is_negative, dates) VALUES ('%k', " +
+ DBLITERAL(rcidx,"numeric") + "," +
+ "'x-date',1," +
+ DBLITERAL(dates,"string") + ")"
+ );
+ rcidx=rcidx+1;
+ i=i+1;
+ }
+ // done
+ SQLCOMMIT();
+ }
+ } // not DELETING()
+ ]]></afterwritescript>
+
+
+ <afterreadscript><![CDATA[
+ // apply flags
+ if (flags & 8) {
+ // all day, adjust time and make timestamps floating
+ MAKEALLDAY(DTSTART,DTEND);
+ SETFLOATING(RR_END);
+ }
+ // get separately stored properties
+ // - location
+ SQLEXECUTE("SELECT value FROM cal_properties WHERE key='LOCATION' AND item_id='%k'");
+ IF (SQLFETCHROW()) {
+ SQLGETCOLUMN(1,LOCATION,"string");
+ }
+ // - description
+ SQLEXECUTE("SELECT value FROM cal_properties WHERE key='DESCRIPTION' AND item_id='%k'");
+ IF (SQLFETCHROW()) {
+ SQLGETCOLUMN(1,DESCRIPTION,"string");
+ }
+ SQLCOMMIT();
+ // - recurrence
+ SQLEXECUTE("SELECT recur_type, count, interval, end_date, day, monthday, dates FROM cal_recurrence WHERE item_id='%k'");
+ WHILE (SQLFETCHROW()) {
+ // get fields
+ SQLGETCOLUMN(1,recur_type,"string");
+ SQLGETCOLUMN(2,count,"numeric");
+ SQLGETCOLUMN(3,interval,"numeric");
+ SQLGETCOLUMN(4,end_date,"unixtime_us");
+ SQLGETCOLUMN(5,day,"string");
+ SQLGETCOLUMN(6,monthday,"string");
+ SQLGETCOLUMN(7,dates,"string");
+ DEBUGSHOWVARS();
+ // calculate internal fields
+ if (recur_type=="x-date") {
+ // evaluate date(s) and make exceptions out of it
+ numelems = REGEX_SPLIT(dates,"/[\s,]+/",dateslist);
+ i=0;
+ WHILE(i<numelems) {
+ REGEX_MATCH(dateslist[i],"/(?<=ZT)\\d+/",0,rc);
+ ts = DBINTTOTIMESTAMP(rc,"unixtime_us");
+ EXDATES[SIZE(EXDATES)] = CONVERTTODATAZONE(ts,true); // unfloat!
+ i = i+1;
+ }
+ }
+ else {
+ // normal recurrence
+ RR_FMASK = 0;
+ RR_LMASK = 0;
+ RR_INTERVAL = interval;
+ RR_END = end_date;
+ RR_FREQ = EMPTY;
+ if (recur_type=="DAILY") {
+ // daily
+ RR_FREQ = "D ";
+ }
+ else if (recur_type=="WEEKLY") {
+ // weekly by weekday list
+ RR_FREQ = "WW";
+ numelems = REGEX_SPLIT(day,"/[\s,]+/",daylist);
+ i=0;
+ while (i<numelems) {
+ RR_FMASK = RR_FMASK | (1<<(daylist[i]-1));
+ i = i+1;
+ }
+ }
+ else if (recur_type=="MONTHLY") {
+ if (monthday!=EMPTY) {
+ // monthly by monthday
+ RR_FREQ = "MD";
+ numelems = REGEX_SPLIT(monthday,"/[\s,]+/",daylist);
+ i=0;
+ while (i<numelems) {
+ rc = abs(daylist[i])-1;
+ if (daylist[i]>0)
+ RR_FMASK = RR_FMASK | (1<<rc);
+ else
+ RR_LMASK = RR_LMASK | (1<<rc);
+ i = i+1;
+ }
+ }
+ else {
+ // monthly by weekday
+ RR_FREQ = "MW";
+ numelems = REGEX_SPLIT(day,"/[\s,]+/",daylist);
+ i=0;
+ while (i<numelems) {
+ // format: Bit0..2 = weekday with offset 1, Bit 3,4 = occurrence in month with offset 1
+ rc = abs(daylist[i]);
+ wd = (rc & 0x07)-1;
+ rc = (rc/8-1)*7 + wd; // bit number for internal masks
+ if (daylist[i]>0) {
+ // counting from beginning
+ RR_FMASK = RR_FMASK | (1<<rc);
+ }
+ else {
+ // counting from end
+ RR_LMASK = RR_LMASK | (1<<rc);
+ }
+ i = i+1;
+ }
+ }
+ }
+ else if (recur_type=="YEARLY") {
+ RR_FREQ = "Y ";
+ }
+ // make sure start date is first recurrence
+ DTSTART = RECURRENCE_DATE(
+ DTSTART,RR_FREQ,RR_INTERVAL,RR_FMASK,RR_LMASK,
+ true,1
+ );
+ // calculate end date if not specified
+ if (RR_END==EMPTY) {
+ // get date of count-th. recurrence = end date
+ RR_END = RECURRENCE_DATE(
+ DTSTART,RR_FREQ,RR_INTERVAL,RR_FMASK,RR_LMASK,
+ true,count
+ );
+ }
+ }
+ }
+ SQLCOMMIT();
+ DEBUGSHOWITEM();
+ ]]></afterreadscript>
+
+
+ <map name="last_modified" references="DMODIFIED" type="unixtime_us" mode="r"/>
+ <map name="time_created" references="DCREATED" type="unixtime_us" mode="r"/>
+
+ <map name="privacy" references="CLASS" type="string" mode="rw" size="63"/>
+
+ <map name="title" references="SUMMARY" type="string" mode="rw" size="254"/>
+
+ <map name="flags" references="flags" type="string" mode="rw"/>
+
+ <map name="event_start" references="DTSTART" type="unixtime_us" mode="rw"/>
+ <map name="event_end" references="DTEND" type="unixtime_us" mode="rw"/>
+
+ <map name="alarm_time" references="ALARM_TIME" type="unixtime_us" mode="rw"/>
+ </fieldmap>
+
+ <!-- datatypes supported by this datastore -->
+ <typesupport>
+ <use datatype="vcalendar10" mode="rw" preferred="yes" variant="VEVENT"/>
+ </typesupport>
+
+ </datastore>
+
+
+ </client>
+
+</sysync_config>
diff --git a/src/sysync_SDK/configs/syncclient_sample_config.xml b/src/sysync_SDK/configs/syncclient_sample_config.xml
new file mode 100644
index 0000000..a0453f0
--- /dev/null
+++ b/src/sysync_SDK/configs/syncclient_sample_config.xml
@@ -0,0 +1,1399 @@
+<?xml version="1.0"?>
+<!-- SYNTHESIS SYNCML CLIENT Version 3.2 Configuration file -->
+
+<sysync_config version="1.0">
+
+ <!-- this string is output to every session debug logfile to identify the config in use -->
+ <configidstring>Synthesis SyncML Client Engine 3.2 sample config</configidstring>
+
+ <debug>
+ <!-- path where logfiles are stored -->
+ <!-- <logpath platform="linux">/your/log/directory</logpath> -->
+ <logflushmode>buffered</logflushmode> <!-- buffered is fastest mode, but may loose data on process abort. Other options: "flush" (after every line) or "openclose" (safest, slowest, like in 2.x server) -->
+ <!-- per session log -->
+ <sessionlogs>yes</sessionlogs> <!-- by default, create a session log file for every sync session (might be disabled for special users/devices in scripts) -->
+ <!-- debug format options -->
+ <logformat>html</logformat> <!-- html is nicely colored and easily viewable with a web browser. Other options: "xml", "text" -->
+ <timestamp>yes</timestamp> <!-- show timestamps for structure elements in log -->
+ <timestampall>no</timestampall> <!-- don't show timestamp for every log line -->
+ <timedsessionlognames>yes</timedsessionlognames> <!-- session logs also have the session start timestamp in the filename - makes them more easily sortable -->
+ <!-- thread logging mode -->
+ <subthreadmode>separate</subthreadmode> <!-- write log info from subthreads into separate log files. Other options: "suppress" -->
+ <!-- basic debug level selection -->
+ <enable option="extended"/> <!-- "extended" is a good choice for start testing. For production, use "normal" or "minimal" -->
+ <!-- <enable option="normal"/> --> <!-- "normal" provides rich debug info, but still in reasonable size -->
+ <!-- <enable option="minimal"/> --> <!-- "minimal" just shows basic flow and error. Not suitable for debugging -->
+ <!-- <enable option="maximal"/> --> <!-- "maximal" can create VERY LARGE logs and cause HEAVY SLOWDOWN. Only for detail debugging -->
+ <!-- <enable option="all"/> --> <!-- "all" shows EVERYTHING possible, and way too much for any normal situation. For hardcore debugging ONLY! -->
+ <!-- additional debug info switches -->
+ <enable option="userdata"/> <!-- Make this <disable ...> if you don't want user data in the logs -->
+ <disable option="scripts"/> <!-- Make this <enable ...> to show script execution in logs -->
+ <disable option="exotic"/> <!-- Make this <enable ...> to include very in-detail info. CAN PRODUCE ENORMOUS LOGS and HEAVILY IMPACT PERFORMANCE for large slow syncs - use with care! -->
+ <!-- see manual for more debug info switches -->
+ <!-- global log options -->
+ <globallogs>no</globallogs> <!-- by default, do not log global session dispatching, creation etc. (not useful in multi-user operation) -->
+ <singlegloballog>no</singlegloballog> <!-- a new global log will be started for every start of the server/application -->
+ <!-- SyncML message dumping options -->
+ <msgdump>no</msgdump> <!-- do not dump syncml traffic 1:1 to files -->
+ <xmltranslate>no</xmltranslate> <!-- do not try to translate syncml traffic into XML (DO NOT SET THIS OPTION IN PRODUCTIVE SERVERS!) -->
+ </debug>
+
+ <transport type="xpt">
+ <!-- allow HTTP 1.1 kepp-alive (multiple request-answer-exchanges in single TCP connection) -->
+ <keepconnection>true</keepconnection>
+ </transport>
+
+
+ <scripting>
+ <looptimeout>5</looptimeout>
+
+ <function><![CDATA[
+ // create a UID
+ string newuid() {
+ return "syuid" + NUMFORMAT(RANDOM(1000000),6,"0") + "." + (string)MILLISECONDS(NOW());
+ }
+ ]]></function>
+
+
+ <!-- define script macros for scripts that are used by both vCalendar 1.0 and iCalendar 2.0 -->
+
+ <macro name="VCALENDAR_INCOMING_SCRIPT"><![CDATA[
+ STRING MATCHES[];
+ STRING CAT,CN,EM;
+ INTEGER i;
+ // make sure we have all trailing and leading spaces eliminated
+ DESCRIPTION=NORMALIZED(DESCRIPTION);
+ SUMMARY=NORMALIZED(SUMMARY);
+ // eliminate description that is the same as summary
+ if (DESCRIPTION==SUMMARY) DESCRIPTION=EMPTY;
+ // calendar or todo
+ if (ISEVENT) {
+ // VEVENT
+ // - handle duration cases
+ if (ISDURATION(DURATION)) {
+ if (DTEND==EMPTY) DTEND = DTSTART + DURATION;
+ if (DTSTART==EMPTY) DTSTART = DTEND - DURATION;
+ }
+ // - detect alldays in vCalendar 1.0 (0:00-0:00 or 23:59 localtime)
+ i = ALLDAYCOUNT(DTSTART,DTEND,TRUE);
+ if (ITEMDATATYPE()=="vtodoz10" && i>0) {
+ // DTSTART and DTEND represent allday event, make them date-only values
+ // - convert start to user zone (or floating) so it represents midnight
+ DTSTART = CONVERTTOUSERZONE(DTSTART);
+ MAKEALLDAY(DTSTART,DTEND,i);
+ }
+ // - shape attendees (and make sure ATTENDEES[] is assigned even for empty email addresses)
+ i=0;
+ while(i<SIZE(ATTENDEES) || i<SIZE(ATTENDEE_CNS)) {
+ PARSEEMAILSPEC(ATTENDEES[i], CN, EM);
+ ATTENDEES[i] = EM; // pure email address
+ // in case we have no specific common name, use the one extracted from the email
+ // This catches the vCalendar 1.0 case and eventually ill-formed iCalendar 2.0 as well
+ if (ATTENDEE_CNS[i]==EMPTY)
+ ATTENDEE_CNS[i]=CN;
+ // default participation status to needs-action
+ if (ATTENDEE_PARTSTATS[i]==EMPTY)
+ ATTENDEE_PARTSTATS[i]=1; // 1=needs action
+ i=i+1;
+ }
+ // - shape organizer
+ PARSEEMAILSPEC(ORGANIZER, CN, EM);
+ ORGANIZER = EM; // pure email address
+ if (ORGANIZER_CN==EMPTY)
+ ORGANIZER_CN=CN;
+ }
+ else {
+ // VTODO
+ // - make sure we have at least a summary
+ if (SUMMARY==EMPTY) SUMMARY=DESCRIPTION; // use description if we don't have a summary
+ if (SUMMARY==EMPTY) SUMMARY="unnamed"; // set dummy summary if we still don't have one
+ // due shaping for non-iCalendar 2.0
+ if (ITEMDATATYPE()=="vCalendar10" && ALLDAYCOUNT(DUE,DUE,TRUE,TRUE)>0) {
+ DUE = DATEONLY(DUE);
+ }
+ }
+ // Common alarm handling
+ // - handle relative alarm time (as possible with VALARM TRIGGER)
+ if (ISDURATION(ALARM_TIME)) {
+ if (ALARM_REL==2)
+ ALARM_TIME = DTEND+ALARM_TIME; // relative to end
+ else {
+ if (ISEVENT)
+ ALARM_TIME = DTSTART+ALARM_TIME; // relative to start for events
+ else
+ ALARM_TIME = DUE+ALARM_TIME; // relative to due for todos
+ }
+ }
+ ]]></macro>
+
+
+ <macro name="VCALENDAR_OUTGOING_SCRIPT"><![CDATA[
+ // set UTC time of generation for iCalendar 2.0 DTSTAMP
+ DGENERATED = NOW();
+ // make sure we have all trailing and leading spaces eliminated
+ DESCRIPTION=NORMALIZED(DESCRIPTION);
+ SUMMARY=NORMALIZED(SUMMARY);
+ if (ISEVENT) {
+ // VEVENT
+ // - combine attendee email address and common name into single string for vCalendar 1.0
+ if (ITEMDATATYPE()=="vCalendar10") {
+ i=0;
+ while(i<SIZE(ATTENDEES)) {
+ ATTENDEES[i] = MAKEEMAILSPEC(ATTENDEE_CNS[i], ATTENDEES[i]);
+ i=i+1;
+ }
+ ORGANIZER = MAKEEMAILSPEC(ORGANIZER_CN, ORGANIZER);
+ }
+ }
+ else {
+ // VTODO
+ // - Nothing special so far
+ }
+ // make sure we have at least a summary
+ if (SUMMARY==EMPTY) SUMMARY=SUBSTR(DESCRIPTION,0,32); // derive from description
+ if (SUMMARY==EMPTY) SUMMARY="unnamed"; // in case description is empty as well
+ // do NOT send duration (some servers crash when doing so)
+ DURATION = UNASSIGNED;
+ // shape alarm
+ if (ALARM_TIME!=EMPTY) {
+ if (ITEMDATATYPE()=="iCalendar20") {
+ if (ALARM_ACTION==EMPTY) ALARM_ACTION = "AUDIO";
+ ALARM_TIME = CONVERTTOUSERZONE(ALARM_TIME,TRUE); // unfloat into user (system) zone, in case it is floating
+ ALARM_TIME = CONVERTTOZONE(ALARM_TIME,"UTC"); // must always be UTC by iCalendar 2.0 specs
+ // send as duration if we have non-empty non-date DTSTART
+ if (DTSTART!=EMPTY && !ISDATEONLY(DTSTART)) {
+ // make a duration (unfloat DTSTART into system zone in case it is floating first!)
+ ALARM_TIME = ALARM_TIME-CONVERTTOZONE(CONVERTTOUSERZONE(DTSTART,TRUE),"UTC");
+ ALARM_REL = 1; // relative to start
+ }
+ }
+ else {
+ if (ALARM_MSG==EMPTY) ALARM_MSG="alarm";
+ }
+ }
+ ]]></macro>
+
+ </scripting>
+
+
+ <datatypes>
+
+ <!-- list of internal fields representing vCard data -->
+ <fieldlist name="contacts">
+ <field name="REV" type="timestamp" compare="never" age="yes"/>
+
+ <!-- Name elements -->
+ <field name="N_LAST" type="string" compare="always"/>
+ <field name="N_FIRST" type="string" compare="always"/>
+ <field name="N_MIDDLE" type="string" compare="always"/>
+ <field name="N_PREFIX" type="string" compare="conflict"/>
+ <field name="N_SUFFIX" type="string" compare="conflict"/>
+ <field name="NICKNAME" type="string" compare="conflict"/>
+ <field name="TITLE" type="string" compare="conflict" merge="fillempty"/>
+
+ <field name="FN" type="string" compare="conflict" merge="fillempty"/>
+
+ <!-- categories and classification -->
+ <field name="CATEGORIES" array="yes" type="string" compare="conflict"/>
+
+ <!-- organisation -->
+ <field name="ORG_NAME" type="string" compare="slowsync" merge="fillempty"/>
+ <field name="ORG_DIVISION" type="string" compare="conflict" merge="fillempty"/>
+
+ <!-- birthday -->
+ <field name="BDAY" type="date" compare="conflict" merge="fillempty"/>
+
+ <!-- telephone numbers -->
+ <field name="TEL" array="yes" type="telephone" compare="conflict"/>
+ <field name="TEL_FLAGS" array="yes" type="integer" compare="conflict"/> <!-- offset 0 -->
+ <field name="TEL_LABEL" array="yes" type="string" compare="conflict"/> <!-- offset 1 -->
+ <field name="TEL_ID" array="yes" type="integer" compare="conflict"/> <!-- offset 2 -->
+
+ <!-- emails -->
+ <field name="EMAIL" array="yes" type="multiline" compare="conflict"/>
+ <field name="EMAIL_FLAGS" array="yes" type="integer" compare="conflict"/> <!-- offset 0 -->
+ <field name="EMAIL_LABEL" array="yes" type="string" compare="conflict"/> <!-- offset 1 -->
+ <field name="EMAIL_ID" array="yes" type="integer" compare="conflict"/> <!-- offset 2 -->
+
+ <!-- web addresses -->
+ <field name="WEB" array="yes" type="url" compare="conflict"/>
+ <field name="WEB_FLAGS" array="yes" type="integer" compare="conflict"/> <!-- offset 0 -->
+ <field name="WEB_LABEL" array="yes" type="string" compare="conflict"/> <!-- offset 1 -->
+ <field name="WEB_ID" array="yes" type="integer" compare="conflict"/> <!-- offset 2 -->
+
+ <!-- home address -->
+ <field name="ADR_STREET" array="yes" type="multiline" compare="conflict"/>
+ <field name="ADR_ADDTL" array="yes" type="multiline" compare="conflict"/>
+ <field name="ADR_STREET_FLAGS" array="yes" type="integer" compare="conflict"/> <!-- offset 0 (from ADR_STREET_FLAGS) -->
+ <field name="ADR_STREET_LABEL" array="yes" type="string" compare="conflict"/> <!-- offset 1 -->
+ <field name="ADR_STREET_ID" array="yes" type="integer" compare="conflict"/> <!-- offset 2 -->
+ <field name="ADR_POBOX" array="yes" type="multiline" compare="conflict"/>
+ <field name="ADR_CITY" array="yes" type="multiline" compare="conflict"/>
+ <field name="ADR_REG" array="yes" type="multiline" compare="conflict"/>
+ <field name="ADR_ZIP" array="yes" type="multiline" compare="conflict"/>
+ <field name="ADR_COUNTRY" array="yes" type="multiline" compare="conflict"/>
+
+ <!-- Note -->
+ <field name="NOTE" type="multiline" compare="conflict" merge="lines"/>
+
+ <!-- Photo -->
+ <field name="PHOTO" type="blob" compare="never" merge="fillempty"/>
+ <field name="PHOTO_TYPE" type="integer" compare="never" merge="fillempty"/>
+
+ </fieldlist>
+
+ <!-- vCard profile -->
+ <mimeprofile name="vCard" fieldlist="contacts">
+
+ <profile name="VCARD" nummandatory="0"> <!-- we allow records without "N" as Address book can store them -->
+ <property name="VERSION">
+ <value conversion="version"/>
+ </property>
+
+ <property onlyformode="standard" name="PRODID" mandatory="no">
+ <value conversion="prodid"/>
+ </property>
+
+ <property name="REV">
+ <value field="REV"/>
+ </property>
+
+ <property name="N" values="5" mandatory="yes"> <!-- Note: makes N parse and generate even if not in remote's CTCap -->
+ <value index="0" field="N_LAST"/>
+ <value index="1" field="N_FIRST"/>
+ <value index="2" field="N_MIDDLE"/>
+ <value index="3" field="N_PREFIX"/>
+ <value index="4" field="N_SUFFIX"/>
+ </property>
+
+ <property name="FN">
+ <value field="FN"/>
+ </property>
+
+ <property name="NICKNAME" onlyformode="standard">
+ <value field="NICKNAME"/>
+ </property>
+
+ <property name="TITLE">
+ <value field="TITLE"/>
+ </property>
+
+ <property name="CATEGORIES" values="list" valueseparator="," altvalueseparator=";" > <!-- non-standard, but 1:1 as in vCard 3.0 (NOT like in vCalendar 1.0, where separator is ";") -->
+ <value field="CATEGORIES"/>
+ <position field="CATEGORIES" repeat="array" increment="1" minshow="0"/>
+ </property>
+
+ <property name="ORG" values="2">
+ <value index="0" field="ORG_NAME"/>
+ <value index="1" field="ORG_DIVISION"/>
+ </property>
+
+ <property name="TEL">
+ <value field="TEL"/>
+ <position field="TEL" repeat="array" increment="1" minshow="1"/>
+ <parameter name="TYPE" default="yes" positional="no" show="yes">
+ <value field="TEL_FLAGS" conversion="multimix" combine=",">
+ <enum name="HOME" value="B0"/>
+ <enum name="WORK" value="B1"/>
+ <enum mode="ignore" value="B2"/> <!-- OTHER -->
+ <enum name="VOICE" value="B3"/>
+ <enum name="CELL" value="B4"/>
+ <enum name="FAX" value="B5"/>
+ <enum name="PAGER" value="B6"/>
+ <enum name="PREF" value="B7"/>
+
+ <enum mode="prefix" name="X-CustomLabel-" value="1.L"/>
+ <enum mode="prefix" name="X-Synthesis-Ref" value="2.L"/>
+ </value>
+ </parameter>
+ </property>
+
+ <property name="EMAIL">
+ <value field="EMAIL"/>
+ <position field="EMAIL" repeat="array" increment="1" minshow="1"/>
+ <parameter name="TYPE" default="yes" positional="no" show="yes">
+ <value field="EMAIL_FLAGS" conversion="multimix" combine=",">
+ <enum name="HOME" value="B0"/>
+ <enum name="WORK" value="B1"/>
+ <enum mode="ignore" value="B2"/> <!-- OTHER -->
+ <enum name="INTERNET" value="B3"/>
+
+ <enum mode="prefix" name="X-CustomLabel-" value="1.L"/>
+ <enum mode="prefix" name="X-Synthesis-Ref" value="2.L"/>
+ </value>
+ </parameter>
+ </property>
+
+ <property name="URL">
+ <value field="WEB"/>
+ <position field="WEB" repeat="array" increment="1" minshow="1"/>
+ <parameter name="TYPE" default="yes" positional="no" show="yes">
+ <value field="WEB_FLAGS" conversion="multimix" combine=",">
+ <enum name="HOME" value="B0"/>
+ <enum name="WORK" value="B1"/>
+ <enum mode="ignore" value="B2"/> <!-- OTHER -->
+ <enum name="PREF" value="B3"/>
+
+ <enum mode="prefix" name="X-CustomLabel-" value="1.L"/>
+ <enum mode="prefix" name="X-Synthesis-Ref" value="2.L"/>
+ </value>
+ </parameter>
+ </property>
+
+ <property name="ADR" values="7">
+ <value index="0" field="ADR_POBOX"/>
+ <value index="1" field="ADR_ADDTL"/>
+ <value index="2" field="ADR_STREET"/>
+ <value index="3" field="ADR_CITY"/>
+ <value index="4" field="ADR_REG"/>
+ <value index="5" field="ADR_ZIP"/>
+ <value index="6" field="ADR_COUNTRY"/>
+ <position field="ADR_POBOX" repeat="array" increment="1" minshow="1"/>
+ <parameter name="TYPE" default="yes" positional="no" show="yes">
+ <value field="ADR_STREET_FLAGS" conversion="multimix" combine=",">
+ <enum name="HOME" value="B0"/>
+ <enum name="WORK" value="B1"/>
+ <enum mode="ignore" value="B2"/> <!-- OTHER -->
+
+ <enum mode="prefix" name="X-CustomLabel-" value="1.L"/>
+ <enum mode="prefix" name="X-Synthesis-Ref" value="2.L"/>
+ </value>
+ </parameter>
+ </property>
+
+ <property name="BDAY">
+ <value field="BDAY"/>
+ </property>
+
+ <property name="NOTE" filter="no">
+ <value field="NOTE"/>
+ </property>
+
+ <property name="PHOTO" filter="no">
+ <value field="PHOTO" conversion="BLOB_B64"/>
+ <parameter name="TYPE" default="no" show="yes">
+ <value field="PHOTO_TYPE">
+ <enum name="JPEG" value="0"/>
+ </value>
+ </parameter>
+ </property>
+
+ </profile>
+ </mimeprofile>
+
+ <!-- vCard 2.1 datatype, using vCard profile defined above -->
+ <datatype name="vCard21" basetype="vcard">
+ <version>2.1</version>
+ <use mimeprofile="vCard"/>
+ </datatype>
+
+ <!-- vCard 3.0 datatype, using vCard profile defined above -->
+ <datatype name="vCard30" basetype="vcard">
+ <version>3.0</version>
+ <use mimeprofile="vCard"/>
+ </datatype>
+
+
+ <!-- common field list for events and todos (both represented by vCalendar/iCalendar) -->
+ <fieldlist name="calendar">
+ <field name="ISEVENT" type="integer" compare="always"/>
+
+ <field name="DMODIFIED" type="timestamp" compare="never" age="yes"/>
+ <field name="DCREATED" type="timestamp" compare="never"/>
+
+ <field name="DGENERATED" type="timestamp" compare="never"/>
+
+ <field name="UID" type="string" compare="never"/>
+
+ <field name="CATEGORIES" array="yes" type="string" compare="conflict" merge="fillempty"/>
+ <field name="CLASS" type="integer" compare="conflict" merge="fillempty"/>
+ <field name="TRANSP" type="integer" compare="conflict" merge="fillempty"/>
+
+ <field name="SUMMARY" type="multiline" compare="always"/>
+ <field name="DESCRIPTION" type="multiline" compare="slowsync" merge="lines"/>
+ <field name="LOCATION" type="multiline" compare="slowsync" merge="lines"/>
+
+ <!-- recurrence rule block, fields must be in that order, including
+ DTSTART as last field !! -->
+ <field name="RR_FREQ" type="string" compare="conflict"/>
+ <field name="RR_INTERVAL" type="integer" compare="conflict"/>
+ <field name="RR_FMASK" type="integer" compare="conflict"/>
+ <field name="RR_LMASK" type="integer" compare="conflict"/>
+ <field name="RR_END" type="timestamp" compare="conflict"/>
+
+ <!-- Note: DTSTART/DTEND are compared in the <comparescript>,
+ therefore compare is set no "never" here -->
+ <field name="DTSTART" type="timestamp" compare="never"/>
+ <field name="DTEND" type="timestamp" compare="never"/>
+ <field name="DURATION" type="timestamp" compare="never"/>
+ <field name="COMPLETED" type="timestamp" compare="never"/>
+ <field name="DUE" type="timestamp" compare="never"/>
+
+ <field name="GEO_LAT" type="string" compare="never"/>
+ <field name="GEO_LONG" type="string" compare="never"/>
+
+ <field name="PRIORITY" type="integer" compare="conflict"/>
+ <field name="STATUS" type="integer" compare="conflict" merge="fillempty"/>
+
+ <field name="ALARM_TIME" type="timestamp" compare="conflict"/>
+ <field name="ALARM_SNOOZE" type="string" compare="conflict"/>
+ <field name="ALARM_REPEAT" type="string" compare="conflict"/>
+ <field name="ALARM_MSG" type="string" compare="conflict"/>
+ <field name="ALARM_ACTION" type="string" compare="conflict"/>
+ <field name="ALARM_REL" type="integer" compare="never"/>
+
+ <!-- non-standard -->
+ <field name="PARENT_UID" type="string" compare="never"/>
+
+ <!-- for events -->
+ <field name="EXDATES" array="yes" type="timestamp" compare="never"/>
+
+ <field name="ORIGSTART" array="no" type="timestamp" compare="never"/>
+ <field name="SEQNO" array="no" type="integer" compare="never"/>
+
+ <field name="ATTENDEES" array="yes" type="string" compare="never"/>
+ <field name="ATTENDEE_CNS" array="yes" type="string" compare="never"/>
+ <field name="ATTENDEE_PARTSTATS" array="yes" type="integer" compare="never"/>
+ <field name="ORGANIZER" array="no" type="string" compare="never"/>
+ <field name="ORGANIZER_CN" array="no" type="string" compare="never"/>
+
+ </fieldlist>
+
+
+ <!-- vCalendar with VTODO and VEVENT variants -->
+ <mimeprofile name="vCalendar" fieldlist="calendar">
+
+ <vtimezonegenmode>current</vtimezonegenmode>
+ <tzidgenmode>olson</tzidgenmode>
+
+ <profile name="VCALENDAR" nummandatory="1">
+
+ <property name="VERSION" mandatory="yes">
+ <value conversion="version"/>
+ </property>
+
+ <property onlyformode="standard" name="PRODID" mandatory="no">
+ <value conversion="prodid"/>
+ </property>
+
+ <property onlyformode="old" name="TZ" filter="false" suppressempty="yes">
+ <value field="DTSTART" conversion="tz"/>
+ </property>
+
+ <property onlyformode="old" name="DAYLIGHT" mode="daylight" filter="false" suppressempty="yes">
+ <value field="DTSTART" conversion="daylight"/>
+ </property>
+
+ <property name="GEO" values="2" suppressempty="yes" onlyformode="old" valueseparator=",">
+ <!-- LON,LAT in vCalendar 1.0 -->
+ <value index="0" field="GEO_LAT"/>
+ <value index="1" field="GEO_LONG"/>
+ </property>
+
+ <subprofile onlyformode="standard" name="VTIMEZONE" mode="vtimezones"/>
+
+ <!-- sub-profile for todoz -->
+ <subprofile name="VTODO" nummandatory="1" showifselectedonly="yes" field="ISEVENT" value="0">
+
+ <property name="LAST-MODIFIED">
+ <value field="DMODIFIED"/>
+ </property>
+
+ <property name="DTSTAMP" suppressempty="yes" onlyformode="standard">
+ <value field="DGENERATED"/>
+ </property>
+
+ <property name="DCREATED" suppressempty="yes" onlyformode="old">
+ <value field="DCREATED"/>
+ </property>
+ <property name="CREATED" suppressempty="yes" onlyformode="standard">
+ <value field="DCREATED"/>
+ </property>
+
+ <property name="UID" suppressempty="yes">
+ <value field="UID"/>
+ </property>
+
+ <property name="SEQUENCE" suppressempty="yes">
+ <value field="SEQNO"/>
+ </property>
+
+ <property name="GEO" values="2" suppressempty="yes" onlyformode="standard" valueseparator=";">
+ <!-- LAT;LON in iCalendar 2.0 -->
+ <value index="0" field="GEO_LONG"/>
+ <value index="1" field="GEO_LAT"/>
+ </property>
+
+ <property onlyformode="standard" name="CATEGORIES" values="list" valueseparator="," suppressempty="yes">
+ <value field="CATEGORIES" />
+ <position field="CATEGORIES" repeat="array" minshow="0"/>
+ </property>
+
+ <property onlyformode="old" name="CATEGORIES" values="list" valueseparator=";" altvalueseparator="," suppressempty="yes">
+ <value field="CATEGORIES" />
+ <position field="CATEGORIES" repeat="array" minshow="0"/>
+ </property>
+
+ <property name="CLASS" suppressempty="yes">
+ <value field="CLASS">
+ <enum name="PUBLIC" value="0"/>
+ <enum name="PRIVATE" value="1"/>
+ <enum name="CONFIDENTIAL" value="2"/>
+ </value>
+ </property>
+
+ <property name="SUMMARY" mandatory="yes">
+ <value field="SUMMARY"/>
+ </property>
+
+ <property name="DESCRIPTION" mandatory="yes">
+ <value field="DESCRIPTION"/>
+ </property>
+
+ <property name="LOCATION" mandatory="no">
+ <value field="LOCATION"/>
+ </property>
+
+ <property name="DTSTART" suppressempty="yes" delayedparsing="1">
+ <value field="DTSTART" conversion="autodate"/>
+ <parameter onlyformode="standard" name="TZID" default="no" show="yes">
+ <value field="DTSTART" conversion="TZID"/>
+ </parameter>
+ <parameter onlyformode="standard" name="VALUE" default="no" show="yes">
+ <value field="DTSTART" conversion="VALUETYPE"/>
+ </parameter>
+ </property>
+
+ <property name="COMPLETED" suppressempty="yes" delayedparsing="1">
+ <value field="COMPLETED" conversion="autoenddate"/>
+ <parameter onlyformode="standard" name="TZID" default="no" show="yes">
+ <value field="COMPLETED" conversion="TZID"/>
+ </parameter>
+ <parameter onlyformode="standard" name="VALUE" default="no" show="yes">
+ <value field="COMPLETED" conversion="VALUETYPE"/>
+ </parameter>
+ </property>
+
+ <property name="DUE" suppressempty="yes" delayedparsing="1">
+ <value field="DUE" conversion="autodate"/>
+ <parameter onlyformode="standard" name="TZID" default="no" show="yes">
+ <value field="DUE" conversion="TZID"/>
+ </parameter>
+ <parameter onlyformode="standard" name="VALUE" default="no" show="yes">
+ <value field="DUE" conversion="VALUETYPE"/>
+ </parameter>
+ </property>
+
+ <property name="PRIORITY" suppressempty="yes">
+ <value field="PRIORITY"/>
+ </property>
+
+ <property name="STATUS" onlyformode="standard" suppressempty="yes">
+ <value field="STATUS" conversion="emptyonly">
+ <enum name="COMPLETED" value="0"/>
+ <enum name="NEEDS-ACTION" value="1"/>
+ <enum name="IN-PROCESS" value="2"/>
+ <enum name="CANCELLED" value="3"/>
+ <enum name="ACCEPTED" value="4"/>
+ <enum name="TENTATIVE" value="5"/>
+ <enum name="DELEGATED" value="6"/>
+ <enum name="DECLINED" value="7"/>
+ <enum name="SENT" value="8"/>
+ <enum name="CONFIRMED" value="9"/>
+ <enum name="DRAFT" value="10"/>
+ <enum name="FINAL" value="11"/>
+ </value>
+ </property>
+
+ <property name="STATUS" onlyformode="old" suppressempty="yes">
+ <value field="STATUS" conversion="emptyonly">
+ <enum name="COMPLETED" value="0"/>
+ <enum name="NEEDS ACTION" value="1"/>
+ <enum mode="defaultvalue" value="1"/> <!-- catch unknown, but also non-conformant NEEDS_ACTION -->
+ <enum name="IN PROCESS" value="2"/>
+ <enum name="CANCELLED" value="3"/>
+ <enum name="ACCEPTED" value="4"/>
+ <enum name="TENTATIVE" value="5"/>
+ <enum name="DELEGATED" value="6"/>
+ <enum name="DECLINED" value="7"/>
+ <enum name="SENT" value="8"/>
+ <enum name="CONFIRMED" value="9"/>
+ <enum name="DRAFT" value="10"/>
+ <enum name="FINAL" value="11"/>
+ </value>
+ </property>
+
+
+ <!-- AALARM and DALARM both use the same fields -->
+ <property name="AALARM" onlyformode="old" values="4" suppressempty="yes">
+ <value index="0" field="ALARM_TIME" conversion="emptyonly"/>
+ <value index="1" field="ALARM_SNOOZE" conversion="emptyonly"/>
+ <value index="2" field="ALARM_REPEAT" conversion="emptyonly"/>
+ <value index="3" field="ALARM_MSG" conversion="emptyonly"/>
+ </property>
+ <property name="DALARM" onlyformode="old" values="4" suppressempty="yes">
+ <value index="0" field="ALARM_TIME" conversion="emptyonly"/>
+ <value index="1" field="ALARM_SNOOZE" conversion="emptyonly"/>
+ <value index="2" field="ALARM_REPEAT" conversion="emptyonly"/>
+ <value index="3" field="ALARM_MSG" conversion="emptyonly"/>
+ </property>
+
+ <subprofile onlyformode="standard" name="VALARM" nummandatory="1" field="ALARM_TIME">
+ <property name="TRIGGER" suppressempty="no" mandatory="yes">
+ <value field="ALARM_TIME"/>
+ <parameter name="VALUE" default="no" show="yes">
+ <value field="ALARM_TIME" conversion="FULLVALUETYPE"/>
+ </parameter>
+ <parameter name="RELATED" default="no" show="yes">
+ <value field="ALARM_REL">
+ <enum mode="ignore" value="0"/>
+ <enum name="START" value="1"/>
+ <enum name="END" value="2"/>
+ </value>
+ </parameter>
+ </property>
+ <property name="ACTION" suppressempty="yes" mandatory="yes">
+ <value field="ALARM_ACTION"/>
+ </property>
+ <property name="DESCRIPTION" suppressempty="yes">
+ <value field="ALARM_MSG"/>
+ </property>
+ <property name="REPEAT" suppressempty="yes">
+ <value field="ALARM_REPEAT"/>
+ </property>
+ </subprofile>
+
+ <property onlyformode="old" name="RELATED-TO" suppressempty="yes">
+ <value field="PARENT_UID"/>
+ </property>
+
+ <property onlyformode="standard" name="RELATED-TO" suppressempty="yes">
+ <value field="PARENT_UID"/>
+ <parameter onlyformode="standard" name="RELTYPE" default="no" positional="yes" show="yes">
+ <value>
+ <enum name="PARENT"/>
+ <enum mode="defaultvalue" name="other"/>
+ </value>
+ <position hasnot="other" shows="PARENT" field="PARENT_UID"/>
+ </parameter>
+ </property>
+
+ </subprofile>
+
+ <!-- sub-profile for event -->
+ <subprofile name="VEVENT" nummandatory="1" showifselectedonly="yes" field="ISEVENT" value="1">
+
+ <property name="LAST-MODIFIED">
+ <value field="DMODIFIED"/>
+ </property>
+
+ <property name="DTSTAMP" suppressempty="yes" onlyformode="standard">
+ <value field="DGENERATED"/>
+ </property>
+
+ <property name="DCREATED" suppressempty="yes" onlyformode="old">
+ <value field="DCREATED"/>
+ </property>
+ <property name="CREATED" suppressempty="yes" onlyformode="standard">
+ <value field="DCREATED"/>
+ </property>
+
+
+ <property name="UID" suppressempty="yes">
+ <value field="UID"/>
+ </property>
+
+ <property name="SEQUENCE" suppressempty="yes">
+ <value field="SEQNO"/>
+ </property>
+
+ <property name="GEO" values="2" suppressempty="yes" onlyformode="standard" valueseparator=";">
+ <!-- LAT;LON in iCalendar 2.0 -->
+ <value index="0" field="GEO_LONG"/>
+ <value index="1" field="GEO_LAT"/>
+ </property>
+
+ <property onlyformode="standard" name="CATEGORIES" values="list" valueseparator="," suppressempty="yes">
+ <value field="CATEGORIES" />
+ <position field="CATEGORIES" repeat="array" minshow="0"/>
+ </property>
+
+ <property onlyformode="old" name="CATEGORIES" values="list" valueseparator=";" altvalueseparator="," suppressempty="yes">
+ <value field="CATEGORIES" />
+ <position field="CATEGORIES" repeat="array" minshow="0"/>
+ </property>
+
+ <property name="CLASS" suppressempty="yes">
+ <value field="CLASS">
+ <enum name="PUBLIC" value="0"/>
+ <enum name="PRIVATE" value="1"/>
+ <enum name="CONFIDENTIAL" value="2"/>
+ </value>
+ </property>
+
+
+ <property name="TRANSP" suppressempty="yes" onlyformode="standard">
+ <value field="TRANSP">
+ <enum name="OPAQUE" value="0"/>
+ <enum name="TRANSPARENT" value="1"/>
+ <enum name="TENTATIVE" value="2"/> <!-- according to Oracle de facto usage in vCalendar 1.0 -->
+ <enum name="OUT_OF_OFFICE" value="3"/> <!-- according to Oracle de facto usage in vCalendar 1.0 -->
+ <enum mode="defaultvalue" value="0"/>
+ </value>
+ </property>
+ <property name="TRANSP" suppressempty="yes" onlyformode="old">
+ <value field="TRANSP"/> <!-- directly numeric in vCalendar 1.0 -->
+ </property>
+
+
+ <property name="PRIORITY" suppressempty="yes">
+ <value field="PRIORITY"/>
+ </property>
+
+ <property name="SUMMARY" mandatory="yes">
+ <value field="SUMMARY"/>
+ </property>
+
+ <property name="DESCRIPTION" mandatory="yes">
+ <value field="DESCRIPTION"/>
+ </property>
+
+ <property name="LOCATION" mandatory="no">
+ <value field="LOCATION"/>
+ </property>
+
+ <property name="DTSTART" suppressempty="yes" delayedparsing="1">
+ <value field="DTSTART" conversion="autodate"/>
+ <parameter onlyformode="standard" name="TZID" default="no" show="yes">
+ <value field="DTSTART" conversion="TZID"/>
+ </parameter>
+ <parameter onlyformode="standard" name="VALUE" default="no" show="yes">
+ <value field="DTSTART" conversion="VALUETYPE"/>
+ </parameter>
+ </property>
+
+ <!-- recurrence rule (with delayed parsing, as it is dependent on DTSTART) -->
+ <property name="RRULE" suppressempty="yes" delayedparsing="2">
+ <!-- Note: RR_FREQ is the beginning of a block of fields
+ suitable for the "rrule" conversion mode -->
+ <value field="RR_FREQ" conversion="rrule"/>
+ </property>
+
+ <!-- Symbian uses this, so it might make the client work with symbian-prepared servers better -->
+ <property name="X-RECURRENCE-ID" suppressempty="yes" onlyformode="old">
+ <value field="ORIGSTART" conversion="autodate"/>
+ </property>
+
+ <property name="RECURRENCE-ID" suppressempty="yes" onlyformode="standard" delayedparsing="1">
+ <value field="ORIGSTART" conversion="autodate"/>
+ <parameter name="TZID" default="no" show="yes">
+ <value field="ORIGSTART" conversion="TZID"/>
+ </parameter>
+ <parameter name="VALUE" default="no" show="yes">
+ <value field="ORIGSTART" conversion="VALUETYPE"/>
+ </parameter>
+ </property>
+
+ <property name="EXDATE" values="list" suppressempty="yes" delayedparsing="1">
+ <value field="EXDATES"/>
+ <position field="EXDATES" repeat="array" increment="1" minshow="0"/>
+ <parameter onlyformode="standard" name="TZID" default="no" show="yes">
+ <value field="EXDATES" conversion="TZID"/>
+ </parameter>
+ </property>
+
+ <property name="DTEND" suppressempty="yes" delayedparsing="1">
+ <value field="DTEND" conversion="autoenddate"/>
+ <parameter onlyformode="standard" name="TZID" default="no" show="yes">
+ <value field="DTEND" conversion="TZID"/>
+ </parameter>
+ <parameter onlyformode="standard" name="VALUE" default="no" show="yes">
+ <value field="DTEND" conversion="VALUETYPE"/>
+ </parameter>
+ </property>
+
+ <property name="DURATION" suppressempty="yes" delayedparsing="1" onlyformode="standard">
+ <value field="DURATION"/>
+ <parameter onlyformode="standard" name="VALUE" default="no" show="no">
+ <value field="DURATION" conversion="VALUETYPE"/>
+ </parameter>
+ </property>
+
+ <property name="ATTENDEE" suppressempty="yes" onlyformode="old">
+ <value field="ATTENDEES"/>
+ <parameter name="ROLE" default="no" positional="yes" show="yes">
+ <value>
+ <enum name="ORGANIZER"/>
+ </value>
+ <position has="ORGANIZER" field="ORGANIZER" overwriteempty="yes"/>
+ <position hasnot="ORGANIZER" field="ATTENDEES" repeat="array" increment="1" overwriteempty="yes"/>
+ </parameter>
+ <parameter name="STATUS" default="no" show="yes">
+ <value field="ATTENDEE_PARTSTATS">
+ <enum name="NEEDS ACTION" value="1"/>
+ <enum mode="defaultvalue" value="1"/> <!-- catch unknown, but also non-conformant NEEDS_ACTION -->
+ <enum name="ACCEPTED" value="4"/>
+ <enum name="DECLINED" value="7"/>
+ <enum name="TENTATIVE" value="5"/>
+ <enum name="DELEGATED" value="6"/>
+ </value>
+ </parameter>
+ </property>
+
+ <property name="ATTENDEE" suppressempty="yes" onlyformode="standard">
+ <value field="ATTENDEES" conversion="mailto"/>
+ <position field="ATTENDEES" repeat="array" increment="1" minshow="0"/>
+ <parameter name="CN" default="no" show="yes" shownonempty="yes">
+ <value field="ATTENDEE_CNS"/>
+ </parameter>
+ <parameter name="PARTSTAT" default="no" show="yes">
+ <value field="ATTENDEE_PARTSTATS">
+ <enum name="NEEDS-ACTION" value="1"/>
+ <enum mode="defaultvalue" value="1"/> <!-- catch unknown, but also non-conformant NEEDS_ACTION -->
+ <enum name="ACCEPTED" value="4"/>
+ <enum name="DECLINED" value="7"/>
+ <enum name="TENTATIVE" value="5"/>
+ <enum name="DELEGATED" value="6"/>
+ </value>
+ </parameter>
+ </property>
+
+ <property name="ORGANIZER" suppressempty="yes" onlyformode="standard">
+ <value field="ORGANIZER" conversion="mailto"/>
+ <parameter name="CN" default="no" show="yes">
+ <value field="ORGANIZER_CN"/>
+ </parameter>
+ </property>
+
+
+ <!-- AALARM and DALARM both use the same fields -->
+ <property name="AALARM" onlyformode="old" values="4" suppressempty="yes">
+ <value index="0" field="ALARM_TIME" conversion="emptyonly"/>
+ <value index="1" field="ALARM_SNOOZE" conversion="emptyonly"/>
+ <value index="2" field="ALARM_REPEAT" conversion="emptyonly"/>
+ <value index="3" field="ALARM_MSG" conversion="emptyonly"/>
+ </property>
+ <property name="DALARM" onlyformode="old" values="4" suppressempty="yes">
+ <value index="0" field="ALARM_TIME" conversion="emptyonly"/>
+ <value index="1" field="ALARM_SNOOZE" conversion="emptyonly"/>
+ <value index="2" field="ALARM_REPEAT" conversion="emptyonly"/>
+ <value index="3" field="ALARM_MSG" conversion="emptyonly"/>
+ </property>
+
+ <subprofile onlyformode="standard" name="VALARM" nummandatory="1" field="ALARM_TIME">
+ <property name="TRIGGER" suppressempty="no" mandatory="yes">
+ <value field="ALARM_TIME"/>
+ <parameter name="VALUE" default="no" show="yes">
+ <value field="ALARM_TIME" conversion="FULLVALUETYPE"/>
+ </parameter>
+ <parameter name="RELATED" default="no" show="yes">
+ <value field="ALARM_REL">
+ <enum mode="ignore" value="0"/>
+ <enum name="START" value="1"/>
+ <enum name="END" value="2"/>
+ </value>
+ </parameter>
+ </property>
+ <property name="ACTION" suppressempty="yes" mandatory="yes">
+ <value field="ALARM_ACTION"/>
+ </property>
+ <property name="DESCRIPTION" suppressempty="yes">
+ <value field="ALARM_MSG"/>
+ </property>
+ <property name="REPEAT" suppressempty="yes">
+ <value field="ALARM_REPEAT"/>
+ </property>
+ </subprofile>
+
+ </subprofile>
+
+ </profile>
+ </mimeprofile>
+
+
+ <!-- vCalendar 1.0 datatype, using vCalendar profile defined above -->
+ <datatype name="vCalendar10" basetype="vcalendar">
+ <version>1.0</version>
+ <use mimeprofile="vCalendar"/>
+
+ <incomingscript><![CDATA[
+ $VCALENDAR_INCOMING_SCRIPT
+ ]]></incomingscript>
+
+ <outgoingscript><![CDATA[
+ $VCALENDAR_OUTGOING_SCRIPT
+ ]]></outgoingscript>
+
+ </datatype>
+
+
+ <!-- iCalendar 2.0 datatype, using vCalendar profile defined above -->
+ <datatype name="iCalendar20" basetype="vcalendar">
+ <version>2.0</version>
+ <use mimeprofile="vCalendar"/>
+
+ <incomingscript><![CDATA[
+ $VCALENDAR_INCOMING_SCRIPT
+ ]]></incomingscript>
+
+ <outgoingscript><![CDATA[
+ $VCALENDAR_OUTGOING_SCRIPT
+ ]]></outgoingscript>
+
+ </datatype>
+
+
+ <!-- list of internal fields representing plain text note data -->
+ <fieldlist name="Note">
+ <field name="SYNCLVL" type="integer" compare="never"/>
+ <field name="SUBJECT" type="multiline" compare="always"/>
+ <field name="TEXT" type="multiline" compare="conflict" merge="lines"/>
+ </fieldlist>
+
+ <textprofile name="Note" fieldlist="Note">
+ <linemap field="SUBJECT">
+ <numlines>1</numlines>
+ <inheader>false</inheader>
+ <allowempty>true</allowempty>
+ <filterkeyword>SUBJECT</filterkeyword>
+ </linemap>
+ <linemap field="TEXT">
+ <numlines>0</numlines>
+ <inheader>false</inheader>
+ <allowempty>true</allowempty>
+ </linemap>
+ </textprofile>
+
+ <datatype name="note10" basetype="text">
+ <use profile="Note"/>
+ <typestring>text/plain</typestring>
+ <versionstring>1.0</versionstring>
+ </datatype>
+
+ <datatype name="note11" basetype="text">
+ <use profile="Note"/>
+ <typestring>text/plain</typestring>
+ <versionstring>1.1</versionstring>
+ </datatype>
+
+
+ <!-- list of internal fields representing vBookmark data -->
+ <fieldlist name="bookmarks">
+ <field name="REV" type="timestamp" compare="never" age="yes"/>
+ <field name="SYNCLVL" type="integer" compare="never"/>
+
+ <!-- Name -->
+ <field name="TITLE" type="string" compare="always"/>
+
+ <!-- categories and classification -->
+ <field name="CATEGORIES" type="string" compare="conflict" merge="fillempty"/>
+ <field name="CLASS" type="string" compare="conflict" merge="fillempty"/>
+
+ <!-- web addresses -->
+ <field name="URL" type="url" compare="slowsync" merge="fillempty"/>
+
+ <!-- Note -->
+ <field name="NOTE" type="multiline" compare="conflict" merge="lines"/>
+
+ </fieldlist>
+
+ <!-- vBookmark profile -->
+ <mimeprofile name="vBookmark" fieldlist="bookmarks">
+
+ <profile name="VBKM" nummandatory="0">
+ <property name="VERSION">
+ <value conversion="version"/>
+ </property>
+
+ <property name="X-LAST-MODIFIED">
+ <value field="REV"/>
+ </property>
+
+ <property name="TITLE">
+ <value field="TITLE"/>
+ </property>
+
+ <property name="URL">
+ <value field="URL"/>
+ </property>
+
+ <!-- non-standard properties -->
+
+ <property name="CATEGORIES">
+ <value field="CATEGORIES"/>
+ </property>
+
+ <property name="CLASS" suppressempty="yes">
+ <value field="CLASS"/>
+ </property>
+
+ <property name="NOTE" filter="no">
+ <value field="NOTE"/>
+ </property>
+
+ </profile>
+ </mimeprofile>
+
+ <!-- vBookmark datatype, using vBookmark profile defined above -->
+ <datatype name="vBookmark10" basetype="mimedir">
+ <typestring>text/x-vbookmark</typestring>
+ <versionstring>1.0</versionstring>
+ <use profile="vBookmark"/>
+ </datatype>
+
+
+ </datatypes>
+
+
+ <client type="plugin">
+ <plugin_module>[SDK_textdb]</plugin_module>
+ <plugin_sessionauth>yes</plugin_sessionauth>
+ <plugin_deviceadmin>yes</plugin_deviceadmin>
+
+ <!-- this is how the client identifies itself to the LOCAL database. SDK_textdb
+ sample has a hard-coded user test/test so the following works out of the box -->
+ <localdbuser>test</localdbuser>
+ <localdbpassword>test</localdbpassword>
+ <!-- we need the local DB login for SDK_textdb to work properly, so we don't disable it here -->
+ <nolocaldblogin>false</nolocaldblogin>
+
+
+ <!-- IMPORTANT: Note that the following charset, lineend and quoting settings are relevant for session-level data access only.
+ Most data access takes place in individual <datastores>, which each has the same three settings
+ again locally (see below in the <datastore> sections). -->
+
+ <!-- text db plugin is designed for UTF-8, make sure data is passed as UTF-8 (and not the ISO-8859-1 default) -->
+ <datacharset>UTF-8</datacharset>
+ <!-- text db plugin is designed for Unix linefeeds (\n, 0x0A), make data is passed with unix linefeeds (and not the DOS CRLF = 0x0D,0x0A default) -->
+ <datalineends>unix</datalineends>
+
+ <!-- If you want a text logfile, specify its full path here: -->
+ <!-- <logfile platform="win32">D:\your\log\directory\yourlogfile.txt</logfile> -->
+ <!-- <logfile platform="linux">/your/log/directory/yourlogfile.txt</logfile> -->
+ <!-- <logfile platform="macosx">/your/log/directory/yourlogfile.txt</logfile> -->
+
+ <logenabled>yes</logenabled> <!-- log enabled by default (session login scripts might disable it for special users/devices) -->
+
+ <!-- the logfile format can be customized with <loglabels> and <logformat>
+ but if these are not specified, a default format will be used
+ -->
+ <!-- This sample produces a simplified logfile:
+ <loglabels>SyncEndTime\tUser\tStatus\tSynctype\tRemoteName\tDatabase\tLocAdded\tLocUpdated\tLocDeleted\n\n</loglabels>
+ <logformat>%seT\t%U\t%sS\t%tS\t%nR\t%lD\t%laI\t%luI\t%ldI\n</logformat>
+ -->
+
+
+ <!-- timeout for unfinished sessions in seconds -->
+ <sessiontimeout>20</sessiontimeout>
+
+
+ <datastore name="contacts" type="plugin">
+ <plugin_module>[SDK_textdb]</plugin_module>
+ <plugin_datastoreadmin>yes</plugin_datastoreadmin>
+
+
+ <!-- text db plugin is designed for UTF-8, make sure data is passed as UTF-8 (and not the ISO-8859-1 default) -->
+ <datacharset>UTF-8</datacharset>
+ <!-- text db plugin is designed for Unix linefeeds (\n, 0x0A), make data is passed with unix linefeeds (and not the DOS CRLF = 0x0D,0x0A default) -->
+ <datalineends>unix</datalineends>
+
+ <!-- plugin DB may have its own identifiers to determine the point in time of changes, so
+ we must make sure this identifier is stored (and not only the sync time) -->
+ <storesyncidentifiers>yes</storesyncidentifiers>
+
+ <!-- TextDB plugin supports resume and resuming partial items, so enable these here -->
+ <resumesupport>yes</resumesupport>
+ <resumeitemsupport>yes</resumeitemsupport>
+
+
+ <!-- Mapping of the fields to the fieldlist "contacts" -->
+ <fieldmap fieldlist="contacts">
+ <automap indexasname="true"/>
+ </fieldmap>
+
+ <!-- datatypes supported by this datastore -->
+ <typesupport>
+ <use datatype="vCard30" mode="rw" preferred="yes"/>
+ <use datatype="vCard21" mode="rw" preferred="legacy"/>
+ </typesupport>
+
+ </datastore>
+
+
+ <datastore name="events" type="plugin">
+ <plugin_module>[SDK_textdb]</plugin_module>
+ <plugin_datastoreadmin>yes</plugin_datastoreadmin>
+
+
+
+ <!-- text db plugin is designed for UTF-8, make sure data is passed as UTF-8 (and not the ISO-8859-1 default) -->
+ <datacharset>UTF-8</datacharset>
+ <!-- text db plugin is designed for Unix linefeeds (\n, 0x0A), make data is passed with unix linefeeds (and not the DOS CRLF = 0x0D,0x0A default) -->
+ <datalineends>unix</datalineends>
+
+ <!-- plugin DB may have its own identifiers to determine the point in time of changes, so
+ we must make sure this identifier is stored (and not only the sync time) -->
+ <storesyncidentifiers>yes</storesyncidentifiers>
+
+ <!-- TextDB plugin supports resume and resuming partial items, so enable these here -->
+ <resumesupport>yes</resumesupport>
+ <resumeitemsupport>yes</resumeitemsupport>
+
+
+ <!-- Mapping of the fields to the fieldlist "calendar" -->
+ <fieldmap fieldlist="calendar">
+ <automap indexasname="true"/>
+ </fieldmap>
+
+ <!-- datatypes supported by this datastore -->
+ <typesupport>
+ <use datatype="iCalendar20" mode="rw" variant="VEVENT" preferred="yes"/>
+ <use datatype="vCalendar10" mode="rw" variant="VEVENT" preferred="legacy"/>
+ </typesupport>
+
+ </datastore>
+
+
+ <datastore name="tasks" type="plugin">
+ <plugin_module>[SDK_textdb]</plugin_module>
+ <plugin_datastoreadmin>yes</plugin_datastoreadmin>
+
+
+
+ <!-- text db plugin is designed for UTF-8, make sure data is passed as UTF-8 (and not the ISO-8859-1 default) -->
+ <datacharset>UTF-8</datacharset>
+ <!-- text db plugin is designed for Unix linefeeds (\n, 0x0A), make data is passed with unix linefeeds (and not the DOS CRLF = 0x0D,0x0A default) -->
+ <datalineends>unix</datalineends>
+
+ <!-- plugin DB may have its own identifiers to determine the point in time of changes, so
+ we must make sure this identifier is stored (and not only the sync time) -->
+ <storesyncidentifiers>yes</storesyncidentifiers>
+
+ <!-- TextDB plugin supports resume and resuming partial items, so enable these here -->
+ <resumesupport>yes</resumesupport>
+ <resumeitemsupport>yes</resumeitemsupport>
+
+
+ <!-- Mapping of the fields to the fieldlist "calendar" -->
+ <fieldmap fieldlist="calendar">
+ <automap indexasname="true"/>
+ </fieldmap>
+
+ <!-- datatypes supported by this datastore -->
+ <typesupport>
+ <use datatype="iCalendar20" mode="rw" variant="VTODO" preferred="yes"/>
+ <use datatype="vCalendar10" mode="rw" variant="VTODO" preferred="legacy"/>
+ </typesupport>
+
+ </datastore>
+
+
+ <datastore name="notes" type="plugin">
+ <plugin_module>[SDK_textdb]</plugin_module>
+ <plugin_datastoreadmin>yes</plugin_datastoreadmin>
+
+
+ <!-- text db plugin is designed for UTF-8, make sure data is passed as UTF-8 (and not the ISO-8859-1 default) -->
+ <datacharset>UTF-8</datacharset>
+ <!-- text db plugin is designed for Unix linefeeds (\n, 0x0A), make data is passed with unix linefeeds (and not the DOS CRLF = 0x0D,0x0A default) -->
+ <datalineends>unix</datalineends>
+
+ <!-- plugin DB may have its own identifiers to determine the point in time of changes, so
+ we must make sure this identifier is stored (and not only the sync time) -->
+ <storesyncidentifiers>yes</storesyncidentifiers>
+
+ <!-- TextDB plugin supports resume and resuming partial items, so enable these here -->
+ <resumesupport>yes</resumesupport>
+ <resumeitemsupport>yes</resumeitemsupport>
+
+
+ <!-- Mapping of the fields to the fieldlist "notes" -->
+ <fieldmap fieldlist="Note">
+ <automap indexasname="true"/>
+ </fieldmap>
+
+ <typesupport>
+ <use datatype="note10" mode="rw" preferred="yes"/>
+ <use datatype="note11" mode="rw"/>
+ </typesupport>
+
+ </datastore>
+
+
+ <datastore name="bookmarks" type="plugin">
+ <plugin_module>[SDK_textdb]</plugin_module>
+ <plugin_datastoreadmin>yes</plugin_datastoreadmin>
+
+
+
+ <!-- text db plugin is designed for UTF-8, make sure data is passed as UTF-8 (and not the ISO-8859-1 default) -->
+ <datacharset>UTF-8</datacharset>
+ <!-- text db plugin is designed for Unix linefeeds (\n, 0x0A), make data is passed with unix linefeeds (and not the DOS CRLF = 0x0D,0x0A default) -->
+ <datalineends>unix</datalineends>
+
+ <!-- plugin DB may have its own identifiers to determine the point in time of changes, so
+ we must make sure this identifier is stored (and not only the sync time) -->
+ <storesyncidentifiers>yes</storesyncidentifiers>
+
+ <!-- TextDB plugin supports resume and resuming partial items, so enable these here -->
+ <resumesupport>yes</resumesupport>
+ <resumeitemsupport>yes</resumeitemsupport>
+
+
+
+ <!-- Mapping of the fields to the fieldlist "bookmarks" -->
+ <fieldmap fieldlist="bookmarks">
+ <automap indexasname="true"/>
+ </fieldmap>
+
+ <!-- datatypes supported by this datastore -->
+ <typesupport>
+ <use datatype="vBookmark10" mode="rw" preferred="yes"/>
+ </typesupport>
+
+ </datastore>
+
+ <!-- Configuration of Sync Requests -->
+ <!-- ============================== -->
+
+
+
+ <!-- choose default SyncML version : 1.2, 1.1 or 1.0
+ Note: client will try with this default version first,
+ if server does not accept this version, the client
+ re-tries with the next lower version -->
+ <defaultsyncmlversion>1.2</defaultsyncmlversion>
+
+ <!-- choose encoding for SyncML messages: xml or wbxml -->
+ <syncmlencoding>wbxml</syncmlencoding>
+
+ <!-- Preconfigured sync requests. The following configuration specifies
+ what SyncML server is connected and which datatypes are synced
+ when the client application is started -->
+
+
+ <!-- Enter the full server URL
+ - if your server is http, specify something like "http://yourserver.com/path"
+ - if your server is OBEX over infrared (win200 only), specify "obex:irda"
+ - if your server is OBEX over TCP, specify "obex:yourserver" -->
+ <serverurl>http://www.synthesis.ch/synciot</serverurl>
+ <serveruser>test</serveruser>
+ <serverpassword>4test2</serverpassword>
+
+ <!-- Enable the following if your SyncML server can be reached via a HTTP proxy server only:
+ <proxyhost>proxy.in.my.lan:3128</proxyhost>
+ -->
+ <!-- If the proxy needs an authorization as well, specify it here:
+ <proxyuser>my_proxy_user</proxyuser>
+ <proxypassword>my_proxy_pw</proxypassword>
+ -->
+
+ <!-- Enable the following if you need to go via a SOCKS proxy:
+ <sockshost>socks.in.my.lan</sockshost>
+ -->
+
+
+ <!-- For each datastore you want to sync, there must be a <syncrequest> tag
+ Just uncomment those that you need. Enter the server's datastore
+ path in the <dbpath>. Note that <dbpath> is preconfigured
+ for use with a Synthesis SyncML server 1.0.8 or newer. -->
+
+
+
+ <syncrequest datastore="contacts">
+ <localpathextension></localpathextension>
+
+ <dbpath>contacts</dbpath>
+ <dbuser></dbuser>
+ <dbpassword></dbpassword>
+
+ <!--
+ <recordfilter><![CDATA[
+ N[1]&iCON;]]
+ ]]></recordfilter>
+ <filterinclusive>yes</filterinclusive>
+ -->
+
+ <syncmode>twoway</syncmode>
+ <slowsync>false</slowsync>
+ </syncrequest>
+
+
+ <syncrequest datastore="events">
+ <localpathextension></localpathextension>
+
+ <dbpath>events</dbpath>
+ <dbuser></dbuser>
+ <dbpassword></dbpassword>
+
+ <syncmode>twoway</syncmode>
+ <slowsync>false</slowsync>
+ </syncrequest>
+ <!-- For DS 1.2 servers with Filter support (e.g. Synthesis SyncML Server 3.0)
+ you might want to use filters. If so, insert the following lines
+ into the SyncRequest above
+ and adapt filter expressions as needed. See config reference or SyncML DS 1.2
+ standard for filter syntax.
+ The sample expression below shows a filter that only syncs events for the year 2006 -->
+ <!--
+ <recordfilter><![CDATA[
+ SINCE&EQ;20060101&AND;BEFORE&EQ;20061231
+ ]]></recordfilter>
+ <filterinclusive>no</filterinclusive>
+ -->
+
+
+ <syncrequest datastore="tasks">
+ <localpathextension></localpathextension>
+
+ <dbpath>tasks</dbpath>
+ <dbuser></dbuser>
+ <dbpassword></dbpassword>
+
+ <syncmode>twoway</syncmode>
+ <slowsync>false</slowsync>
+ </syncrequest>
+ <!-- For DS 1.2 servers with Filter support (e.g. Synthesis SyncML Server 3.0)
+ you might want to use filters. If so, insert the following lines
+ into the SyncRequest above
+ and adapt filter expressions as needed. See config reference or SyncML DS 1.2
+ standard for filter syntax.
+ The sample expression below shows a filter that only syncs tasks for the year 2006 -->
+ <!--
+ <recordfilter><![CDATA[
+ SINCE&EQ;20060101&AND;BEFORE&EQ;20061231
+ ]]></recordfilter>
+ <filterinclusive>no</filterinclusive>
+ -->
+
+
+ <!--
+ <syncrequest datastore="notes">
+ <localpathextension></localpathextension>
+
+ <dbpath>notes</dbpath>
+ <dbuser></dbuser>
+ <dbpassword></dbpassword>
+
+ <syncmode>twoway</syncmode>
+ <slowsync>false</slowsync>
+ </syncrequest>
+ -->
+
+
+ <!--
+ <syncrequest datastore="bookmarks">
+ <localpathextension></localpathextension>
+
+ <dbpath>bookmarks</dbpath>
+ <dbuser></dbuser>
+ <dbpassword></dbpassword>
+
+ <syncmode>twoway</syncmode>
+ <slowsync>false</slowsync>
+ </syncrequest>
+ -->
+
+ </client>
+
+</sysync_config>
diff --git a/src/zlib/ChangeLog b/src/zlib/ChangeLog
new file mode 100755
index 0000000..09283b0
--- /dev/null
+++ b/src/zlib/ChangeLog
@@ -0,0 +1,722 @@
+
+ ChangeLog file for zlib
+
+Changes in 1.2.1 (17 November 2003)
+- Remove a tab in contrib/gzappend/gzappend.c
+- Update some interfaces in contrib for new zlib functions
+- Update zlib version number in some contrib entries
+- Add Windows CE definition for ptrdiff_t in zutil.h [Mai, Truta]
+- Support shared libraries on Hurd and KFreeBSD [Brown]
+- Fix error in NO_DIVIDE option of adler32.c
+
+Changes in 1.2.0.8 (4 November 2003)
+- Update version in contrib/delphi/ZLib.pas and contrib/pascal/zlibpas.pas
+- Add experimental NO_DIVIDE #define in adler32.c
+ - Possibly faster on some processors (let me know if it is)
+- Correct Z_BLOCK to not return on first inflate call if no wrap
+- Fix strm->data_type on inflate() return to correctly indicate EOB
+- Add deflatePrime() function for appending in the middle of a byte
+- Add contrib/gzappend for an example of appending to a stream
+- Update win32/DLL_FAQ.txt [Truta]
+- Delete Turbo C comment in README [Truta]
+- Improve some indentation in zconf.h [Truta]
+- Fix infinite loop on bad input in configure script [Church]
+- Fix gzeof() for concatenated gzip files [Johnson]
+- Add example to contrib/visual-basic.txt [Michael B.]
+- Add -p to mkdir's in Makefile.in [vda]
+- Fix configure to properly detect presence or lack of printf functions
+- Add AS400 support [Monnerat]
+- Add a little Cygwin support [Wilson]
+
+Changes in 1.2.0.7 (21 September 2003)
+- Correct some debug formats in contrib/infback9
+- Cast a type in a debug statement in trees.c
+- Change search and replace delimiter in configure from % to # [Beebe]
+- Update contrib/untgz to 0.2 with various fixes [Truta]
+- Add build support for Amiga [Nikl]
+- Remove some directories in old that have been updated to 1.2
+- Add dylib building for Mac OS X in configure and Makefile.in
+- Remove old distribution stuff from Makefile
+- Update README to point to DLL_FAQ.txt, and add comment on Mac OS X
+- Update links in README
+
+Changes in 1.2.0.6 (13 September 2003)
+- Minor FAQ updates
+- Update contrib/minizip to 1.00 [Vollant]
+- Remove test of gz functions in example.c when GZ_COMPRESS defined [Truta]
+- Update POSTINC comment for 68060 [Nikl]
+- Add contrib/infback9 with deflate64 decoding (unsupported)
+- For MVS define NO_vsnprintf and undefine FAR [van Burik]
+- Add pragma for fdopen on MVS [van Burik]
+
+Changes in 1.2.0.5 (8 September 2003)
+- Add OF to inflateBackEnd() declaration in zlib.h
+- Remember start when using gzdopen in the middle of a file
+- Use internal off_t counters in gz* functions to properly handle seeks
+- Perform more rigorous check for distance-too-far in inffast.c
+- Add Z_BLOCK flush option to return from inflate at block boundary
+- Set strm->data_type on return from inflate
+ - Indicate bits unused, if at block boundary, and if in last block
+- Replace size_t with ptrdiff_t in crc32.c, and check for correct size
+- Add condition so old NO_DEFLATE define still works for compatibility
+- FAQ update regarding the Windows DLL [Truta]
+- INDEX update: add qnx entry, remove aix entry [Truta]
+- Install zlib.3 into mandir [Wilson]
+- Move contrib/zlib_dll_FAQ.txt to win32/DLL_FAQ.txt; update [Truta]
+- Adapt the zlib interface to the new DLL convention guidelines [Truta]
+- Introduce ZLIB_WINAPI macro to allow the export of functions using
+ the WINAPI calling convention, for Visual Basic [Vollant, Truta]
+- Update msdos and win32 scripts and makefiles [Truta]
+- Export symbols by name, not by ordinal, in win32/zlib.def [Truta]
+- Add contrib/ada [Anisimkov]
+- Move asm files from contrib/vstudio/vc70_32 to contrib/asm386 [Truta]
+- Rename contrib/asm386 to contrib/masmx86 [Truta, Vollant]
+- Add contrib/masm686 [Truta]
+- Fix offsets in contrib/inflate86 and contrib/masmx86/inffas32.asm
+ [Truta, Vollant]
+- Update contrib/delphi; rename to contrib/pascal; add example [Truta]
+- Remove contrib/delphi2; add a new contrib/delphi [Truta]
+- Avoid inclusion of the nonstandard <memory.h> in contrib/iostream,
+ and fix some method prototypes [Truta]
+- Fix the ZCR_SEED2 constant to avoid warnings in contrib/minizip
+ [Truta]
+- Avoid the use of backslash (\) in contrib/minizip [Vollant]
+- Fix file time handling in contrib/untgz; update makefiles [Truta]
+- Update contrib/vstudio/vc70_32 to comply with the new DLL guidelines
+ [Vollant]
+- Remove contrib/vstudio/vc15_16 [Vollant]
+- Rename contrib/vstudio/vc70_32 to contrib/vstudio/vc7 [Truta]
+- Update README.contrib [Truta]
+- Invert the assignment order of match_head and s->prev[...] in
+ INSERT_STRING [Truta]
+- Compare TOO_FAR with 32767 instead of 32768, to avoid 16-bit warnings
+ [Truta]
+- Compare function pointers with 0, not with NULL or Z_NULL [Truta]
+- Fix prototype of syncsearch in inflate.c [Truta]
+- Introduce ASMINF macro to be enabled when using an ASM implementation
+ of inflate_fast [Truta]
+- Change NO_DEFLATE to NO_GZCOMPRESS [Truta]
+- Modify test_gzio in example.c to take a single file name as a
+ parameter [Truta]
+- Exit the example.c program if gzopen fails [Truta]
+- Add type casts around strlen in example.c [Truta]
+- Remove casting to sizeof in minigzip.c; give a proper type
+ to the variable compared with SUFFIX_LEN [Truta]
+- Update definitions of STDC and STDC99 in zconf.h [Truta]
+- Synchronize zconf.h with the new Windows DLL interface [Truta]
+- Use SYS16BIT instead of __32BIT__ to distinguish between
+ 16- and 32-bit platforms [Truta]
+- Use far memory allocators in small 16-bit memory models for
+ Turbo C [Truta]
+- Add info about the use of ASMV, ASMINF and ZLIB_WINAPI in
+ zlibCompileFlags [Truta]
+- Cygwin has vsnprintf [Wilson]
+- In Windows16, OS_CODE is 0, as in MSDOS [Truta]
+- In Cygwin, OS_CODE is 3 (Unix), not 11 (Windows32) [Wilson]
+
+Changes in 1.2.0.4 (10 August 2003)
+- Minor FAQ updates
+- Be more strict when checking inflateInit2's windowBits parameter
+- Change NO_GUNZIP compile option to NO_GZIP to cover deflate as well
+- Add gzip wrapper option to deflateInit2 using windowBits
+- Add updated QNX rule in configure and qnx directory [Bonnefoy]
+- Make inflate distance-too-far checks more rigorous
+- Clean up FAR usage in inflate
+- Add casting to sizeof() in gzio.c and minigzip.c
+
+Changes in 1.2.0.3 (19 July 2003)
+- Fix silly error in gzungetc() implementation [Vollant]
+- Update contrib/minizip and contrib/vstudio [Vollant]
+- Fix printf format in example.c
+- Correct cdecl support in zconf.in.h [Anisimkov]
+- Minor FAQ updates
+
+Changes in 1.2.0.2 (13 July 2003)
+- Add ZLIB_VERNUM in zlib.h for numerical preprocessor comparisons
+- Attempt to avoid warnings in crc32.c for pointer-int conversion
+- Add AIX to configure, remove aix directory [Bakker]
+- Add some casts to minigzip.c
+- Improve checking after insecure sprintf() or vsprintf() calls
+- Remove #elif's from crc32.c
+- Change leave label to inf_leave in inflate.c and infback.c to avoid
+ library conflicts
+- Remove inflate gzip decoding by default--only enable gzip decoding by
+ special request for stricter backward compatibility
+- Add zlibCompileFlags() function to return compilation information
+- More typecasting in deflate.c to avoid warnings
+- Remove leading underscore from _Capital #defines [Truta]
+- Fix configure to link shared library when testing
+- Add some Windows CE target adjustments [Mai]
+- Remove #define ZLIB_DLL in zconf.h [Vollant]
+- Add zlib.3 [Rodgers]
+- Update RFC URL in deflate.c and algorithm.txt [Mai]
+- Add zlib_dll_FAQ.txt to contrib [Truta]
+- Add UL to some constants [Truta]
+- Update minizip and vstudio [Vollant]
+- Remove vestigial NEED_DUMMY_RETURN from zconf.in.h
+- Expand use of NO_DUMMY_DECL to avoid all dummy structures
+- Added iostream3 to contrib [Schwardt]
+- Replace rewind() with fseek() for WinCE [Truta]
+- Improve setting of zlib format compression level flags
+ - Report 0 for huffman and rle strategies and for level == 0 or 1
+ - Report 2 only for level == 6
+- Only deal with 64K limit when necessary at compile time [Truta]
+- Allow TOO_FAR check to be turned off at compile time [Truta]
+- Add gzclearerr() function [Souza]
+- Add gzungetc() function
+
+Changes in 1.2.0.1 (17 March 2003)
+- Add Z_RLE strategy for run-length encoding [Truta]
+ - When Z_RLE requested, restrict matches to distance one
+ - Update zlib.h, minigzip.c, gzopen(), gzdopen() for Z_RLE
+- Correct FASTEST compilation to allow level == 0
+- Clean up what gets compiled for FASTEST
+- Incorporate changes to zconf.in.h [Vollant]
+ - Refine detection of Turbo C need for dummy returns
+ - Refine ZLIB_DLL compilation
+ - Include additional header file on VMS for off_t typedef
+- Try to use _vsnprintf where it supplants vsprintf [Vollant]
+- Add some casts in inffast.c
+- Enchance comments in zlib.h on what happens if gzprintf() tries to
+ write more than 4095 bytes before compression
+- Remove unused state from inflateBackEnd()
+- Remove exit(0) from minigzip.c, example.c
+- Get rid of all those darn tabs
+- Add "check" target to Makefile.in that does the same thing as "test"
+- Add "mostlyclean" and "maintainer-clean" targets to Makefile.in
+- Update contrib/inflate86 [Anderson]
+- Update contrib/testzlib, contrib/vstudio, contrib/minizip [Vollant]
+- Add msdos and win32 directories with makefiles [Truta]
+- More additions and improvements to the FAQ
+
+Changes in 1.2.0 (9 March 2003)
+- New and improved inflate code
+ - About 20% faster
+ - Does not allocate 32K window unless and until needed
+ - Automatically detects and decompresses gzip streams
+ - Raw inflate no longer needs an extra dummy byte at end
+ - Added inflateBack functions using a callback interface--even faster
+ than inflate, useful for file utilities (gzip, zip)
+ - Added inflateCopy() function to record state for random access on
+ externally generated deflate streams (e.g. in gzip files)
+ - More readable code (I hope)
+- New and improved crc32()
+ - About 50% faster, thanks to suggestions from Rodney Brown
+- Add deflateBound() and compressBound() functions
+- Fix memory leak in deflateInit2()
+- Permit setting dictionary for raw deflate (for parallel deflate)
+- Fix const declaration for gzwrite()
+- Check for some malloc() failures in gzio.c
+- Fix bug in gzopen() on single-byte file 0x1f
+- Fix bug in gzread() on concatenated file with 0x1f at end of buffer
+ and next buffer doesn't start with 0x8b
+- Fix uncompress() to return Z_DATA_ERROR on truncated input
+- Free memory at end of example.c
+- Remove MAX #define in trees.c (conflicted with some libraries)
+- Fix static const's in deflate.c, gzio.c, and zutil.[ch]
+- Declare malloc() and free() in gzio.c if STDC not defined
+- Use malloc() instead of calloc() in zutil.c if int big enough
+- Define STDC for AIX
+- Add aix/ with approach for compiling shared library on AIX
+- Add HP-UX support for shared libraries in configure
+- Add OpenUNIX support for shared libraries in configure
+- Use $cc instead of gcc to build shared library
+- Make prefix directory if needed when installing
+- Correct Macintosh avoidance of typedef Byte in zconf.h
+- Correct Turbo C memory allocation when under Linux
+- Use libz.a instead of -lz in Makefile (assure use of compiled library)
+- Update configure to check for snprintf or vsnprintf functions and their
+ return value, warn during make if using an insecure function
+- Fix configure problem with compile-time knowledge of HAVE_UNISTD_H that
+ is lost when library is used--resolution is to build new zconf.h
+- Documentation improvements (in zlib.h):
+ - Document raw deflate and inflate
+ - Update RFCs URL
+ - Point out that zlib and gzip formats are different
+ - Note that Z_BUF_ERROR is not fatal
+ - Document string limit for gzprintf() and possible buffer overflow
+ - Note requirement on avail_out when flushing
+ - Note permitted values of flush parameter of inflate()
+- Add some FAQs (and even answers) to the FAQ
+- Add contrib/inflate86/ for x86 faster inflate
+- Add contrib/blast/ for PKWare Data Compression Library decompression
+- Add contrib/puff/ simple inflate for deflate format description
+
+Changes in 1.1.4 (11 March 2002)
+- ZFREE was repeated on same allocation on some error conditions.
+ This creates a security problem described in
+ http://www.zlib.org/advisory-2002-03-11.txt
+- Returned incorrect error (Z_MEM_ERROR) on some invalid data
+- Avoid accesses before window for invalid distances with inflate window
+ less than 32K.
+- force windowBits > 8 to avoid a bug in the encoder for a window size
+ of 256 bytes. (A complete fix will be available in 1.1.5).
+
+Changes in 1.1.3 (9 July 1998)
+- fix "an inflate input buffer bug that shows up on rare but persistent
+ occasions" (Mark)
+- fix gzread and gztell for concatenated .gz files (Didier Le Botlan)
+- fix gzseek(..., SEEK_SET) in write mode
+- fix crc check after a gzeek (Frank Faubert)
+- fix miniunzip when the last entry in a zip file is itself a zip file
+ (J Lillge)
+- add contrib/asm586 and contrib/asm686 (Brian Raiter)
+ See http://www.muppetlabs.com/~breadbox/software/assembly.html
+- add support for Delphi 3 in contrib/delphi (Bob Dellaca)
+- add support for C++Builder 3 and Delphi 3 in contrib/delphi2 (Davide Moretti)
+- do not exit prematurely in untgz if 0 at start of block (Magnus Holmgren)
+- use macro EXTERN instead of extern to support DLL for BeOS (Sander Stoks)
+- added a FAQ file
+
+- Support gzdopen on Mac with Metrowerks (Jason Linhart)
+- Do not redefine Byte on Mac (Brad Pettit & Jason Linhart)
+- define SEEK_END too if SEEK_SET is not defined (Albert Chin-A-Young)
+- avoid some warnings with Borland C (Tom Tanner)
+- fix a problem in contrib/minizip/zip.c for 16-bit MSDOS (Gilles Vollant)
+- emulate utime() for WIN32 in contrib/untgz (Gilles Vollant)
+- allow several arguments to configure (Tim Mooney, Frodo Looijaard)
+- use libdir and includedir in Makefile.in (Tim Mooney)
+- support shared libraries on OSF1 V4 (Tim Mooney)
+- remove so_locations in "make clean" (Tim Mooney)
+- fix maketree.c compilation error (Glenn, Mark)
+- Python interface to zlib now in Python 1.5 (Jeremy Hylton)
+- new Makefile.riscos (Rich Walker)
+- initialize static descriptors in trees.c for embedded targets (Nick Smith)
+- use "foo-gz" in example.c for RISCOS and VMS (Nick Smith)
+- add the OS/2 files in Makefile.in too (Andrew Zabolotny)
+- fix fdopen and halloc macros for Microsoft C 6.0 (Tom Lane)
+- fix maketree.c to allow clean compilation of inffixed.h (Mark)
+- fix parameter check in deflateCopy (Gunther Nikl)
+- cleanup trees.c, use compressed_len only in debug mode (Christian Spieler)
+- Many portability patches by Christian Spieler:
+ . zutil.c, zutil.h: added "const" for zmem*
+ . Make_vms.com: fixed some typos
+ . Make_vms.com: msdos/Makefile.*: removed zutil.h from some dependency lists
+ . msdos/Makefile.msc: remove "default rtl link library" info from obj files
+ . msdos/Makefile.*: use model-dependent name for the built zlib library
+ . msdos/Makefile.emx, nt/Makefile.emx, nt/Makefile.gcc:
+ new makefiles, for emx (DOS/OS2), emx&rsxnt and mingw32 (Windows 9x / NT)
+- use define instead of typedef for Bytef also for MSC small/medium (Tom Lane)
+- replace __far with _far for better portability (Christian Spieler, Tom Lane)
+- fix test for errno.h in configure (Tim Newsham)
+
+Changes in 1.1.2 (19 March 98)
+- added contrib/minzip, mini zip and unzip based on zlib (Gilles Vollant)
+ See http://www.winimage.com/zLibDll/unzip.html
+- preinitialize the inflate tables for fixed codes, to make the code
+ completely thread safe (Mark)
+- some simplifications and slight speed-up to the inflate code (Mark)
+- fix gzeof on non-compressed files (Allan Schrum)
+- add -std1 option in configure for OSF1 to fix gzprintf (Martin Mokrejs)
+- use default value of 4K for Z_BUFSIZE for 16-bit MSDOS (Tim Wegner + Glenn)
+- added os2/Makefile.def and os2/zlib.def (Andrew Zabolotny)
+- add shared lib support for UNIX_SV4.2MP (MATSUURA Takanori)
+- do not wrap extern "C" around system includes (Tom Lane)
+- mention zlib binding for TCL in README (Andreas Kupries)
+- added amiga/Makefile.pup for Amiga powerUP SAS/C PPC (Andreas Kleinert)
+- allow "make install prefix=..." even after configure (Glenn Randers-Pehrson)
+- allow "configure --prefix $HOME" (Tim Mooney)
+- remove warnings in example.c and gzio.c (Glenn Randers-Pehrson)
+- move Makefile.sas to amiga/Makefile.sas
+
+Changes in 1.1.1 (27 Feb 98)
+- fix macros _tr_tally_* in deflate.h for debug mode (Glenn Randers-Pehrson)
+- remove block truncation heuristic which had very marginal effect for zlib
+ (smaller lit_bufsize than in gzip 1.2.4) and degraded a little the
+ compression ratio on some files. This also allows inlining _tr_tally for
+ matches in deflate_slow.
+- added msdos/Makefile.w32 for WIN32 Microsoft Visual C++ (Bob Frazier)
+
+Changes in 1.1.0 (24 Feb 98)
+- do not return STREAM_END prematurely in inflate (John Bowler)
+- revert to the zlib 1.0.8 inflate to avoid the gcc 2.8.0 bug (Jeremy Buhler)
+- compile with -DFASTEST to get compression code optimized for speed only
+- in minigzip, try mmap'ing the input file first (Miguel Albrecht)
+- increase size of I/O buffers in minigzip.c and gzio.c (not a big gain
+ on Sun but significant on HP)
+
+- add a pointer to experimental unzip library in README (Gilles Vollant)
+- initialize variable gcc in configure (Chris Herborth)
+
+Changes in 1.0.9 (17 Feb 1998)
+- added gzputs and gzgets functions
+- do not clear eof flag in gzseek (Mark Diekhans)
+- fix gzseek for files in transparent mode (Mark Diekhans)
+- do not assume that vsprintf returns the number of bytes written (Jens Krinke)
+- replace EXPORT with ZEXPORT to avoid conflict with other programs
+- added compress2 in zconf.h, zlib.def, zlib.dnt
+- new asm code from Gilles Vollant in contrib/asm386
+- simplify the inflate code (Mark):
+ . Replace ZALLOC's in huft_build() with single ZALLOC in inflate_blocks_new()
+ . ZALLOC the length list in inflate_trees_fixed() instead of using stack
+ . ZALLOC the value area for huft_build() instead of using stack
+ . Simplify Z_FINISH check in inflate()
+
+- Avoid gcc 2.8.0 comparison bug a little differently than zlib 1.0.8
+- in inftrees.c, avoid cc -O bug on HP (Farshid Elahi)
+- in zconf.h move the ZLIB_DLL stuff earlier to avoid problems with
+ the declaration of FAR (Gilles VOllant)
+- install libz.so* with mode 755 (executable) instead of 644 (Marc Lehmann)
+- read_buf buf parameter of type Bytef* instead of charf*
+- zmemcpy parameters are of type Bytef*, not charf* (Joseph Strout)
+- do not redeclare unlink in minigzip.c for WIN32 (John Bowler)
+- fix check for presence of directories in "make install" (Ian Willis)
+
+Changes in 1.0.8 (27 Jan 1998)
+- fixed offsets in contrib/asm386/gvmat32.asm (Gilles Vollant)
+- fix gzgetc and gzputc for big endian systems (Markus Oberhumer)
+- added compress2() to allow setting the compression level
+- include sys/types.h to get off_t on some systems (Marc Lehmann & QingLong)
+- use constant arrays for the static trees in trees.c instead of computing
+ them at run time (thanks to Ken Raeburn for this suggestion). To create
+ trees.h, compile with GEN_TREES_H and run "make test".
+- check return code of example in "make test" and display result
+- pass minigzip command line options to file_compress
+- simplifying code of inflateSync to avoid gcc 2.8 bug
+
+- support CC="gcc -Wall" in configure -s (QingLong)
+- avoid a flush caused by ftell in gzopen for write mode (Ken Raeburn)
+- fix test for shared library support to avoid compiler warnings
+- zlib.lib -> zlib.dll in msdos/zlib.rc (Gilles Vollant)
+- check for TARGET_OS_MAC in addition to MACOS (Brad Pettit)
+- do not use fdopen for Metrowerks on Mac (Brad Pettit))
+- add checks for gzputc and gzputc in example.c
+- avoid warnings in gzio.c and deflate.c (Andreas Kleinert)
+- use const for the CRC table (Ken Raeburn)
+- fixed "make uninstall" for shared libraries
+- use Tracev instead of Trace in infblock.c
+- in example.c use correct compressed length for test_sync
+- suppress +vnocompatwarnings in configure for HPUX (not always supported)
+
+Changes in 1.0.7 (20 Jan 1998)
+- fix gzseek which was broken in write mode
+- return error for gzseek to negative absolute position
+- fix configure for Linux (Chun-Chung Chen)
+- increase stack space for MSC (Tim Wegner)
+- get_crc_table and inflateSyncPoint are EXPORTed (Gilles Vollant)
+- define EXPORTVA for gzprintf (Gilles Vollant)
+- added man page zlib.3 (Rick Rodgers)
+- for contrib/untgz, fix makedir() and improve Makefile
+
+- check gzseek in write mode in example.c
+- allocate extra buffer for seeks only if gzseek is actually called
+- avoid signed/unsigned comparisons (Tim Wegner, Gilles Vollant)
+- add inflateSyncPoint in zconf.h
+- fix list of exported functions in nt/zlib.dnt and mdsos/zlib.def
+
+Changes in 1.0.6 (19 Jan 1998)
+- add functions gzprintf, gzputc, gzgetc, gztell, gzeof, gzseek, gzrewind and
+ gzsetparams (thanks to Roland Giersig and Kevin Ruland for some of this code)
+- Fix a deflate bug occuring only with compression level 0 (thanks to
+ Andy Buckler for finding this one).
+- In minigzip, pass transparently also the first byte for .Z files.
+- return Z_BUF_ERROR instead of Z_OK if output buffer full in uncompress()
+- check Z_FINISH in inflate (thanks to Marc Schluper)
+- Implement deflateCopy (thanks to Adam Costello)
+- make static libraries by default in configure, add --shared option.
+- move MSDOS or Windows specific files to directory msdos
+- suppress the notion of partial flush to simplify the interface
+ (but the symbol Z_PARTIAL_FLUSH is kept for compatibility with 1.0.4)
+- suppress history buffer provided by application to simplify the interface
+ (this feature was not implemented anyway in 1.0.4)
+- next_in and avail_in must be initialized before calling inflateInit or
+ inflateInit2
+- add EXPORT in all exported functions (for Windows DLL)
+- added Makefile.nt (thanks to Stephen Williams)
+- added the unsupported "contrib" directory:
+ contrib/asm386/ by Gilles Vollant <info@winimage.com>
+ 386 asm code replacing longest_match().
+ contrib/iostream/ by Kevin Ruland <kevin@rodin.wustl.edu>
+ A C++ I/O streams interface to the zlib gz* functions
+ contrib/iostream2/ by Tyge Løvset <Tyge.Lovset@cmr.no>
+ Another C++ I/O streams interface
+ contrib/untgz/ by "Pedro A. Aranda Guti\irrez" <paag@tid.es>
+ A very simple tar.gz file extractor using zlib
+ contrib/visual-basic.txt by Carlos Rios <c_rios@sonda.cl>
+ How to use compress(), uncompress() and the gz* functions from VB.
+- pass params -f (filtered data), -h (huffman only), -1 to -9 (compression
+ level) in minigzip (thanks to Tom Lane)
+
+- use const for rommable constants in deflate
+- added test for gzseek and gztell in example.c
+- add undocumented function inflateSyncPoint() (hack for Paul Mackerras)
+- add undocumented function zError to convert error code to string
+ (for Tim Smithers)
+- Allow compilation of gzio with -DNO_DEFLATE to avoid the compression code.
+- Use default memcpy for Symantec MSDOS compiler.
+- Add EXPORT keyword for check_func (needed for Windows DLL)
+- add current directory to LD_LIBRARY_PATH for "make test"
+- create also a link for libz.so.1
+- added support for FUJITSU UXP/DS (thanks to Toshiaki Nomura)
+- use $(SHAREDLIB) instead of libz.so in Makefile.in (for HPUX)
+- added -soname for Linux in configure (Chun-Chung Chen,
+- assign numbers to the exported functions in zlib.def (for Windows DLL)
+- add advice in zlib.h for best usage of deflateSetDictionary
+- work around compiler bug on Atari (cast Z_NULL in call of s->checkfn)
+- allow compilation with ANSI keywords only enabled for TurboC in large model
+- avoid "versionString"[0] (Borland bug)
+- add NEED_DUMMY_RETURN for Borland
+- use variable z_verbose for tracing in debug mode (L. Peter Deutsch).
+- allow compilation with CC
+- defined STDC for OS/2 (David Charlap)
+- limit external names to 8 chars for MVS (Thomas Lund)
+- in minigzip.c, use static buffers only for 16-bit systems
+- fix suffix check for "minigzip -d foo.gz"
+- do not return an error for the 2nd of two consecutive gzflush() (Felix Lee)
+- use _fdopen instead of fdopen for MSC >= 6.0 (Thomas Fanslau)
+- added makelcc.bat for lcc-win32 (Tom St Denis)
+- in Makefile.dj2, use copy and del instead of install and rm (Frank Donahoe)
+- Avoid expanded $ Id$. Use "rcs -kb" or "cvs admin -kb" to avoid Id expansion.
+- check for unistd.h in configure (for off_t)
+- remove useless check parameter in inflate_blocks_free
+- avoid useless assignment of s->check to itself in inflate_blocks_new
+- do not flush twice in gzclose (thanks to Ken Raeburn)
+- rename FOPEN as F_OPEN to avoid clash with /usr/include/sys/file.h
+- use NO_ERRNO_H instead of enumeration of operating systems with errno.h
+- work around buggy fclose on pipes for HP/UX
+- support zlib DLL with BORLAND C++ 5.0 (thanks to Glenn Randers-Pehrson)
+- fix configure if CC is already equal to gcc
+
+Changes in 1.0.5 (3 Jan 98)
+- Fix inflate to terminate gracefully when fed corrupted or invalid data
+- Use const for rommable constants in inflate
+- Eliminate memory leaks on error conditions in inflate
+- Removed some vestigial code in inflate
+- Update web address in README
+
+Changes in 1.0.4 (24 Jul 96)
+- In very rare conditions, deflate(s, Z_FINISH) could fail to produce an EOF
+ bit, so the decompressor could decompress all the correct data but went
+ on to attempt decompressing extra garbage data. This affected minigzip too.
+- zlibVersion and gzerror return const char* (needed for DLL)
+- port to RISCOS (no fdopen, no multiple dots, no unlink, no fileno)
+- use z_error only for DEBUG (avoid problem with DLLs)
+
+Changes in 1.0.3 (2 Jul 96)
+- use z_streamp instead of z_stream *, which is now a far pointer in MSDOS
+ small and medium models; this makes the library incompatible with previous
+ versions for these models. (No effect in large model or on other systems.)
+- return OK instead of BUF_ERROR if previous deflate call returned with
+ avail_out as zero but there is nothing to do
+- added memcmp for non STDC compilers
+- define NO_DUMMY_DECL for more Mac compilers (.h files merged incorrectly)
+- define __32BIT__ if __386__ or i386 is defined (pb. with Watcom and SCO)
+- better check for 16-bit mode MSC (avoids problem with Symantec)
+
+Changes in 1.0.2 (23 May 96)
+- added Windows DLL support
+- added a function zlibVersion (for the DLL support)
+- fixed declarations using Bytef in infutil.c (pb with MSDOS medium model)
+- Bytef is define's instead of typedef'd only for Borland C
+- avoid reading uninitialized memory in example.c
+- mention in README that the zlib format is now RFC1950
+- updated Makefile.dj2
+- added algorithm.doc
+
+Changes in 1.0.1 (20 May 96) [1.0 skipped to avoid confusion]
+- fix array overlay in deflate.c which sometimes caused bad compressed data
+- fix inflate bug with empty stored block
+- fix MSDOS medium model which was broken in 0.99
+- fix deflateParams() which could generated bad compressed data.
+- Bytef is define'd instead of typedef'ed (work around Borland bug)
+- added an INDEX file
+- new makefiles for DJGPP (Makefile.dj2), 32-bit Borland (Makefile.b32),
+ Watcom (Makefile.wat), Amiga SAS/C (Makefile.sas)
+- speed up adler32 for modern machines without auto-increment
+- added -ansi for IRIX in configure
+- static_init_done in trees.c is an int
+- define unlink as delete for VMS
+- fix configure for QNX
+- add configure branch for SCO and HPUX
+- avoid many warnings (unused variables, dead assignments, etc...)
+- no fdopen for BeOS
+- fix the Watcom fix for 32 bit mode (define FAR as empty)
+- removed redefinition of Byte for MKWERKS
+- work around an MWKERKS bug (incorrect merge of all .h files)
+
+Changes in 0.99 (27 Jan 96)
+- allow preset dictionary shared between compressor and decompressor
+- allow compression level 0 (no compression)
+- add deflateParams in zlib.h: allow dynamic change of compression level
+ and compression strategy.
+- test large buffers and deflateParams in example.c
+- add optional "configure" to build zlib as a shared library
+- suppress Makefile.qnx, use configure instead
+- fixed deflate for 64-bit systems (detected on Cray)
+- fixed inflate_blocks for 64-bit systems (detected on Alpha)
+- declare Z_DEFLATED in zlib.h (possible parameter for deflateInit2)
+- always return Z_BUF_ERROR when deflate() has nothing to do
+- deflateInit and inflateInit are now macros to allow version checking
+- prefix all global functions and types with z_ with -DZ_PREFIX
+- make falloc completely reentrant (inftrees.c)
+- fixed very unlikely race condition in ct_static_init
+- free in reverse order of allocation to help memory manager
+- use zlib-1.0/* instead of zlib/* inside the tar.gz
+- make zlib warning-free with "gcc -O3 -Wall -Wwrite-strings -Wpointer-arith
+ -Wconversion -Wstrict-prototypes -Wmissing-prototypes"
+- allow gzread on concatenated .gz files
+- deflateEnd now returns Z_DATA_ERROR if it was premature
+- deflate is finally (?) fully deterministic (no matches beyond end of input)
+- Document Z_SYNC_FLUSH
+- add uninstall in Makefile
+- Check for __cpluplus in zlib.h
+- Better test in ct_align for partial flush
+- avoid harmless warnings for Borland C++
+- initialize hash_head in deflate.c
+- avoid warning on fdopen (gzio.c) for HP cc -Aa
+- include stdlib.h for STDC compilers
+- include errno.h for Cray
+- ignore error if ranlib doesn't exist
+- call ranlib twice for NeXTSTEP
+- use exec_prefix instead of prefix for libz.a
+- renamed ct_* as _tr_* to avoid conflict with applications
+- clear z->msg in inflateInit2 before any error return
+- initialize opaque in example.c, gzio.c, deflate.c and inflate.c
+- fixed typo in zconf.h (_GNUC__ => __GNUC__)
+- check for WIN32 in zconf.h and zutil.c (avoid farmalloc in 32-bit mode)
+- fix typo in Make_vms.com (f$trnlnm -> f$getsyi)
+- in fcalloc, normalize pointer if size > 65520 bytes
+- don't use special fcalloc for 32 bit Borland C++
+- use STDC instead of __GO32__ to avoid redeclaring exit, calloc, etc...
+- use Z_BINARY instead of BINARY
+- document that gzclose after gzdopen will close the file
+- allow "a" as mode in gzopen.
+- fix error checking in gzread
+- allow skipping .gz extra-field on pipes
+- added reference to Perl interface in README
+- put the crc table in FAR data (I dislike more and more the medium model :)
+- added get_crc_table
+- added a dimension to all arrays (Borland C can't count).
+- workaround Borland C bug in declaration of inflate_codes_new & inflate_fast
+- guard against multiple inclusion of *.h (for precompiled header on Mac)
+- Watcom C pretends to be Microsoft C small model even in 32 bit mode.
+- don't use unsized arrays to avoid silly warnings by Visual C++:
+ warning C4746: 'inflate_mask' : unsized array treated as '__far'
+ (what's wrong with far data in far model?).
+- define enum out of inflate_blocks_state to allow compilation with C++
+
+Changes in 0.95 (16 Aug 95)
+- fix MSDOS small and medium model (now easier to adapt to any compiler)
+- inlined send_bits
+- fix the final (:-) bug for deflate with flush (output was correct but
+ not completely flushed in rare occasions).
+- default window size is same for compression and decompression
+ (it's now sufficient to set MAX_WBITS in zconf.h).
+- voidp -> voidpf and voidnp -> voidp (for consistency with other
+ typedefs and because voidnp was not near in large model).
+
+Changes in 0.94 (13 Aug 95)
+- support MSDOS medium model
+- fix deflate with flush (could sometimes generate bad output)
+- fix deflateReset (zlib header was incorrectly suppressed)
+- added support for VMS
+- allow a compression level in gzopen()
+- gzflush now calls fflush
+- For deflate with flush, flush even if no more input is provided.
+- rename libgz.a as libz.a
+- avoid complex expression in infcodes.c triggering Turbo C bug
+- work around a problem with gcc on Alpha (in INSERT_STRING)
+- don't use inline functions (problem with some gcc versions)
+- allow renaming of Byte, uInt, etc... with #define.
+- avoid warning about (unused) pointer before start of array in deflate.c
+- avoid various warnings in gzio.c, example.c, infblock.c, adler32.c, zutil.c
+- avoid reserved word 'new' in trees.c
+
+Changes in 0.93 (25 June 95)
+- temporarily disable inline functions
+- make deflate deterministic
+- give enough lookahead for PARTIAL_FLUSH
+- Set binary mode for stdin/stdout in minigzip.c for OS/2
+- don't even use signed char in inflate (not portable enough)
+- fix inflate memory leak for segmented architectures
+
+Changes in 0.92 (3 May 95)
+- don't assume that char is signed (problem on SGI)
+- Clear bit buffer when starting a stored block
+- no memcpy on Pyramid
+- suppressed inftest.c
+- optimized fill_window, put longest_match inline for gcc
+- optimized inflate on stored blocks.
+- untabify all sources to simplify patches
+
+Changes in 0.91 (2 May 95)
+- Default MEM_LEVEL is 8 (not 9 for Unix) as documented in zlib.h
+- Document the memory requirements in zconf.h
+- added "make install"
+- fix sync search logic in inflateSync
+- deflate(Z_FULL_FLUSH) now works even if output buffer too short
+- after inflateSync, don't scare people with just "lo world"
+- added support for DJGPP
+
+Changes in 0.9 (1 May 95)
+- don't assume that zalloc clears the allocated memory (the TurboC bug
+ was Mark's bug after all :)
+- let again gzread copy uncompressed data unchanged (was working in 0.71)
+- deflate(Z_FULL_FLUSH), inflateReset and inflateSync are now fully implemented
+- added a test of inflateSync in example.c
+- moved MAX_WBITS to zconf.h because users might want to change that.
+- document explicitly that zalloc(64K) on MSDOS must return a normalized
+ pointer (zero offset)
+- added Makefiles for Microsoft C, Turbo C, Borland C++
+- faster crc32()
+
+Changes in 0.8 (29 April 95)
+- added fast inflate (inffast.c)
+- deflate(Z_FINISH) now returns Z_STREAM_END when done. Warning: this
+ is incompatible with previous versions of zlib which returned Z_OK.
+- work around a TurboC compiler bug (bad code for b << 0, see infutil.h)
+ (actually that was not a compiler bug, see 0.81 above)
+- gzread no longer reads one extra byte in certain cases
+- In gzio destroy(), don't reference a freed structure
+- avoid many warnings for MSDOS
+- avoid the ERROR symbol which is used by MS Windows
+
+Changes in 0.71 (14 April 95)
+- Fixed more MSDOS compilation problems :( There is still a bug with
+ TurboC large model.
+
+Changes in 0.7 (14 April 95)
+- Added full inflate support.
+- Simplified the crc32() interface. The pre- and post-conditioning
+ (one's complement) is now done inside crc32(). WARNING: this is
+ incompatible with previous versions; see zlib.h for the new usage.
+
+Changes in 0.61 (12 April 95)
+- workaround for a bug in TurboC. example and minigzip now work on MSDOS.
+
+Changes in 0.6 (11 April 95)
+- added minigzip.c
+- added gzdopen to reopen a file descriptor as gzFile
+- added transparent reading of non-gziped files in gzread.
+- fixed bug in gzread (don't read crc as data)
+- fixed bug in destroy (gzio.c) (don't return Z_STREAM_END for gzclose).
+- don't allocate big arrays in the stack (for MSDOS)
+- fix some MSDOS compilation problems
+
+Changes in 0.5:
+- do real compression in deflate.c. Z_PARTIAL_FLUSH is supported but
+ not yet Z_FULL_FLUSH.
+- support decompression but only in a single step (forced Z_FINISH)
+- added opaque object for zalloc and zfree.
+- added deflateReset and inflateReset
+- added a variable zlib_version for consistency checking.
+- renamed the 'filter' parameter of deflateInit2 as 'strategy'.
+ Added Z_FILTERED and Z_HUFFMAN_ONLY constants.
+
+Changes in 0.4:
+- avoid "zip" everywhere, use zlib instead of ziplib.
+- suppress Z_BLOCK_FLUSH, interpret Z_PARTIAL_FLUSH as block flush
+ if compression method == 8.
+- added adler32 and crc32
+- renamed deflateOptions as deflateInit2, call one or the other but not both
+- added the method parameter for deflateInit2.
+- added inflateInit2
+- simplied considerably deflateInit and inflateInit by not supporting
+ user-provided history buffer. This is supported only in deflateInit2
+ and inflateInit2.
+
+Changes in 0.3:
+- prefix all macro names with Z_
+- use Z_FINISH instead of deflateEnd to finish compression.
+- added Z_HUFFMAN_ONLY
+- added gzerror()
diff --git a/src/zlib/FAQ b/src/zlib/FAQ
new file mode 100755
index 0000000..7115ec3
--- /dev/null
+++ b/src/zlib/FAQ
@@ -0,0 +1,315 @@
+
+ Frequently Asked Questions about zlib
+
+
+If your question is not there, please check the zlib home page
+http://www.zlib.org which may have more recent information.
+The lastest zlib FAQ is at http://www.gzip.org/zlib/zlib_faq.html
+
+
+ 1. Is zlib Y2K-compliant?
+
+ Yes. zlib doesn't handle dates.
+
+ 2. Where can I get a Windows DLL version?
+
+ The zlib sources can be compiled without change to produce a DLL.
+ See the file win32/DLL_FAQ.txt in the zlib distribution.
+ Pointers to the precompiled DLL are found in the zlib web site at
+ http://www.zlib.org.
+
+ 3. Where can I get a Visual Basic interface to zlib?
+
+ See
+ * http://www.winimage.com/zLibDll/
+ * http://www.dogma.net/markn/articles/zlibtool/zlibtool.htm
+ * contrib/visual-basic.txt in the zlib distribution
+
+ 4. compress() returns Z_BUF_ERROR
+
+ Make sure that before the call of compress, the length of the compressed
+ buffer is equal to the total size of the compressed buffer and not
+ zero. For Visual Basic, check that this parameter is passed by reference
+ ("as any"), not by value ("as long").
+
+ 5. deflate() or inflate() returns Z_BUF_ERROR
+
+ Before making the call, make sure that avail_in and avail_out are not
+ zero. When setting the parameter flush equal to Z_FINISH, also make sure
+ that avail_out is big enough to allow processing all pending input.
+ Note that a Z_BUF_ERROR is not fatal--another call to deflate() or
+ inflate() can be made with more input or output space. A Z_BUF_ERROR
+ may in fact be unavoidable depending on how the functions are used, since
+ it is not possible to tell whether or not there is more output pending
+ when strm.avail_out returns with zero.
+
+ 6. Where's the zlib documentation (man pages, etc.)?
+
+ It's in zlib.h for the moment, and Francis S. Lin has converted it to a
+ web page zlib.html. Volunteers to transform this to Unix-style man pages,
+ please contact Jean-loup Gailly (jloup@gzip.org). Examples of zlib usage
+ are in the files example.c and minigzip.c.
+
+ 7. Why don't you use GNU autoconf or libtool or ...?
+
+ Because we would like to keep zlib as a very small and simple
+ package. zlib is rather portable and doesn't need much configuration.
+
+ 8. I found a bug in zlib.
+
+ Most of the time, such problems are due to an incorrect usage of
+ zlib. Please try to reproduce the problem with a small program and send
+ the corresponding source to us at zlib@gzip.org . Do not send
+ multi-megabyte data files without prior agreement.
+
+ 9. Why do I get "undefined reference to gzputc"?
+
+ If "make test" produces something like
+
+ example.o(.text+0x154): undefined reference to `gzputc'
+
+ check that you don't have old files libz.* in /usr/lib, /usr/local/lib or
+ /usr/X11R6/lib. Remove any old versions, then do "make install".
+
+10. I need a Delphi interface to zlib.
+
+ See the contrib/delphi directory in the zlib distribution.
+
+11. Can zlib handle .zip archives?
+
+ See the directory contrib/minizip in the zlib distribution.
+
+12. Can zlib handle .Z files?
+
+ No, sorry. You have to spawn an uncompress or gunzip subprocess, or adapt
+ the code of uncompress on your own.
+
+13. How can I make a Unix shared library?
+
+ make clean
+ ./configure -s
+ make
+
+14. How do I install a shared zlib library on Unix?
+
+ make install
+
+ However, many flavors of Unix come with a shared zlib already installed.
+ Before going to the trouble of compiling a shared version of zlib and
+ trying to install it, you may want to check if it's already there! If you
+ can #include <zlib.h>, it's there. The -lz option will probably link to it.
+
+15. I have a question about OttoPDF
+
+ We are not the authors of OttoPDF. The real author is on the OttoPDF web
+ site Joel Hainley jhainley@myndkryme.com.
+
+16. Why does gzip give an error on a file I make with compress/deflate?
+
+ The compress and deflate functions produce data in the zlib format, which
+ is different and incompatible with the gzip format. The gz* functions in
+ zlib on the other hand use the gzip format. Both the zlib and gzip
+ formats use the same compressed data format internally, but have different
+ headers and trailers around the compressed data.
+
+17. Ok, so why are there two different formats?
+
+ The gzip format was designed to retain the directory information about
+ a single file, such as the name and last modification date. The zlib
+ format on the other hand was designed for in-memory and communication
+ channel applications, and has a much more compact header and trailer and
+ uses a faster integrity check than gzip.
+
+18. Well that's nice, but how do I make a gzip file in memory?
+
+ You can request that deflate write the gzip format instead of the zlib
+ format using deflateInit2(). You can also request that inflate decode
+ the gzip format using inflateInit2(). Read zlib.h for more details.
+
+ Note that you cannot specify special gzip header contents (e.g. a file
+ name or modification date), nor will inflate tell you what was in the
+ gzip header. If you need to customize the header or see what's in it,
+ you can use the raw deflate and inflate operations and the crc32()
+ function and roll your own gzip encoding and decoding. Read the gzip
+ RFC 1952 for details of the header and trailer format.
+
+19. Is zlib thread-safe?
+
+ Yes. However any library routines that zlib uses and any application-
+ provided memory allocation routines must also be thread-safe. zlib's gz*
+ functions use stdio library routines, and most of zlib's functions use the
+ library memory allocation routines by default. zlib's Init functions allow
+ for the application to provide custom memory allocation routines.
+
+ Of course, you should only operate on any given zlib or gzip stream from a
+ single thread at a time.
+
+20. Can I use zlib in my commercial application?
+
+ Yes. Please read the license in zlib.h.
+
+21. Is zlib under the GNU license?
+
+ No. Please read the license in zlib.h.
+
+22. The license says that altered source versions must be "plainly marked". So
+ what exactly do I need to do to meet that requirement?
+
+ You need to change the ZLIB_VERSION and ZLIB_VERNUM #defines in zlib.h. In
+ particular, the final version number needs to be changed to "f", and an
+ identification string should be appended to ZLIB_VERSION. Version numbers
+ x.x.x.f are reserved for modifications to zlib by others than the zlib
+ maintainers. For example, if the version of the base zlib you are altering
+ is "1.2.3.4", then in zlib.h you should change ZLIB_VERNUM to 0x123f, and
+ ZLIB_VERSION to something like "1.2.3.f-zachary-mods-v3". You can also
+ update the version strings in deflate.c and inftrees.c.
+
+ For altered source distributions, you should also note the origin and
+ nature of the changes in zlib.h, as well as in ChangeLog and README, along
+ with the dates of the alterations. The origin should include at least your
+ name (or your company's name), and an email address to contact for help or
+ issues with the library.
+
+ Note that distributing a compiled zlib library along with zlib.h and
+ zconf.h is also a source distribution, and so you should change
+ ZLIB_VERSION and ZLIB_VERNUM and note the origin and nature of the changes
+ in zlib.h as you would for a full source distribution.
+
+23. Will zlib work on a big-endian or little-endian architecture, and can I
+ exchange compressed data between them?
+
+ Yes and yes.
+
+24. Will zlib work on a 64-bit machine?
+
+ It should. It has been tested on 64-bit machines, and has no dependence
+ on any data types being limited to 32-bits in length. If you have any
+ difficulties, please provide a complete problem report to zlib@gzip.org
+
+25. Will zlib decompress data from the PKWare Data Compression Library?
+
+ No. The PKWare DCL uses a completely different compressed data format
+ than does PKZIP and zlib. However, you can look in zlib's contrib/blast
+ directory for a possible solution to your problem.
+
+26. Can I access data randomly in a compressed stream?
+
+ No, not without some preparation. If when compressing you periodically
+ use Z_FULL_FLUSH, carefully write all the pending data at those points,
+ and keep an index of those locations, then you can start decompression
+ at those points. You have to be careful to not use Z_FULL_FLUSH too
+ often, since it can significantly degrade compression.
+
+27. Does zlib work on MVS, OS/390, CICS, etc.?
+
+ We don't know for sure. We have heard occasional reports of success on
+ these systems. If you do use it on one of these, please provide us with
+ a report, instructions, and patches that we can reference when we get
+ these questions. Thanks.
+
+28. Is there some simpler, easier to read version of inflate I can look at
+ to understand the deflate format?
+
+ First off, you should read RFC 1951. Second, yes. Look in zlib's
+ contrib/puff directory.
+
+29. Does zlib infringe on any patents?
+
+ As far as we know, no. In fact, that was originally the whole point behind
+ zlib. Look here for some more information:
+
+ http://www.gzip.org/#faq11
+
+30. Can zlib work with greater than 4 GB of data?
+
+ Yes. inflate() and deflate() will process any amount of data correctly.
+ Each call of inflate() or deflate() is limited to input and output chunks
+ of the maximum value that can be stored in the compiler's "unsigned int"
+ type, but there is no limit to the number of chunks. Note however that the
+ strm.total_in and strm_total_out counters may be limited to 4 GB. These
+ counters are provided as a convenience and are not used internally by
+ inflate() or deflate(). The application can easily set up its own counters
+ updated after each call of inflate() or deflate() to count beyond 4 GB.
+ compress() and uncompress() may be limited to 4 GB, since they operate in a
+ single call. gzseek() and gztell() may be limited to 4 GB depending on how
+ zlib is compiled. See the zlibCompileFlags() function in zlib.h.
+
+ The word "may" appears several times above since there is a 4 GB limit
+ only if the compiler's "long" type is 32 bits. If the compiler's "long"
+ type is 64 bits, then the limit is 16 exabytes.
+
+31. Does zlib have any security vulnerabilities?
+
+ The only one that we are aware of is potentially in gzprintf(). If zlib
+ is compiled to use sprintf() or vsprintf(), then there is no protection
+ against a buffer overflow of a 4K string space, other than the caller of
+ gzprintf() assuring that the output will not exceed 4K. On the other
+ hand, if zlib is compiled to use snprintf() or vsnprintf(), which should
+ normally be the case, then there is no vulnerability. The ./configure
+ script will display warnings if an insecure variation of sprintf() will
+ be used by gzprintf(). Also the zlibCompileFlags() function will return
+ information on what variant of sprintf() is used by gzprintf().
+
+ If you don't have snprintf() or vsnprintf() and would like one, you can
+ find a portable implementation here:
+
+ http://www.ijs.si/software/snprintf/
+
+ Note that you should be using the most recent version of zlib. Versions
+ 1.1.3 and before were subject to a double-free vulnerability.
+
+32. Is there a Java version of zlib?
+
+ Probably what you want is to use zlib in Java. zlib is already included
+ as part of the Java SDK in the java.util.zip package. If you really want
+ a version of zlib written in the Java language, look on the zlib home
+ page for links: http://www.zlib.org/
+
+33. I get this or that compiler or source-code scanner warning when I crank it
+ up to maximally-pendantic. Can't you guys write proper code?
+
+ Many years ago, we gave up attempting to avoid warnings on every compiler
+ in the universe. It just got to be a waste of time, and some compilers
+ were downright silly. So now, we simply make sure that the code always
+ works.
+
+34. Will zlib read the (insert any ancient or arcane format here) compressed
+ data format?
+
+ Probably not. Look in the comp.compression FAQ for pointers to various
+ formats and associated software.
+
+35. How can I encrypt/decrypt zip files with zlib?
+
+ zlib doesn't support encryption. The original PKZIP encryption is very weak
+ and can be broken with freely available programs. To get strong encryption,
+ use gpg ( http://www.gnupg.org/ ) which already includes zlib compression.
+ For PKZIP compatible "encryption", look at http://www.info-zip.org/
+
+36. What's the difference between the "gzip" and "deflate" HTTP 1.1 encodings?
+
+ "gzip" is the gzip format, and "deflate" is the zlib format. They should
+ probably have called the second one "zlib" instead to avoid confusion
+ with the raw deflate compressed data format. While the HTTP 1.1 RFC 2616
+ correctly points to the zlib specification in RFC 1950 for the "deflate"
+ transfer encoding, there have been reports of servers and browsers that
+ incorrectly produce or expect raw deflate data per the deflate
+ specficiation in RFC 1951, most notably Microsoft. So even though the
+ "deflate" transfer encoding using the zlib format would be the more
+ efficient approach (and in fact exactly what the zlib format was designed
+ for), using the "gzip" transfer encoding is probably more reliable due to
+ an unfortunate choice of name on the part of the HTTP 1.1 authors.
+
+ Bottom line: use the gzip format for HTTP 1.1 encoding.
+
+37. Does zlib support the new "Deflate64" format introduced by PKWare?
+
+ No. PKWare has apparently decided to keep that format proprietary, since
+ they have not documented it as they have previous compression formats.
+ In any case, the compression improvements are so modest compared to other
+ more modern approaches, that it's not worth the effort to implement.
+
+38. Can you please sign these lengthy legal documents and fax them back to us
+ so that we can use your software in our product?
+
+ No. Go away. Shoo.
diff --git a/src/zlib/INDEX b/src/zlib/INDEX
new file mode 100755
index 0000000..a9de784
--- /dev/null
+++ b/src/zlib/INDEX
@@ -0,0 +1,48 @@
+ChangeLog history of changes
+FAQ Frequently Asked Questions about zlib
+INDEX this file
+Makefile makefile for Unix (generated by configure)
+Makefile.in makefile for Unix (template for configure)
+README guess what
+algorithm.txt description of the (de)compression algorithm
+configure configure script for Unix
+zconf.in.h template for zconf.h (used by configure)
+
+msdos/ makefiles for MSDOS
+old/ makefiles for various architectures and zlib documentation
+ files that have not yet been updated for zlib 1.2.x
+qnx/ makefiles for QNX
+win32/ makefiles for Windows
+
+ zlib public header files (must be kept):
+zconf.h
+zlib.h
+
+ private source files used to build the zlib library:
+adler32.c
+compress.c
+crc32.c
+crc32.h
+deflate.c
+deflate.h
+gzio.c
+infback.c
+inffast.c
+inffast.h
+inffixed.h
+inflate.c
+inflate.h
+inftrees.c
+inftrees.h
+trees.c
+trees.h
+uncompr.c
+zutil.c
+zutil.h
+
+ source files for sample programs:
+example.c
+minigzip.c
+
+ unsupported contribution by third parties
+See contrib/README.contrib
diff --git a/src/zlib/README b/src/zlib/README
new file mode 100755
index 0000000..0f12054
--- /dev/null
+++ b/src/zlib/README
@@ -0,0 +1,126 @@
+ZLIB DATA COMPRESSION LIBRARY
+
+zlib 1.2.1 is a general purpose data compression library. All the code is
+thread safe. The data format used by the zlib library is described by RFCs
+(Request for Comments) 1950 to 1952 in the files
+http://www.ietf.org/rfc/rfc1950.txt (zlib format), rfc1951.txt (deflate format)
+and rfc1952.txt (gzip format). These documents are also available in other
+formats from ftp://ftp.uu.net/graphics/png/documents/zlib/zdoc-index.html
+
+All functions of the compression library are documented in the file zlib.h
+(volunteer to write man pages welcome, contact zlib@gzip.org). A usage example
+of the library is given in the file example.c which also tests that the library
+is working correctly. Another example is given in the file minigzip.c. The
+compression library itself is composed of all source files except example.c and
+minigzip.c.
+
+To compile all files and run the test program, follow the instructions given at
+the top of Makefile. In short "make test; make install" should work for most
+machines. For Unix: "./configure; make test; make install" For MSDOS, use one
+of the special makefiles such as Makefile.msc. For VMS, use Make_vms.com or
+descrip.mms.
+
+Questions about zlib should be sent to <zlib@gzip.org>, or to Gilles Vollant
+<info@winimage.com> for the Windows DLL version. The zlib home page is
+http://www.zlib.org or http://www.gzip.org/zlib/ Before reporting a problem,
+please check this site to verify that you have the latest version of zlib;
+otherwise get the latest version and check whether the problem still exists or
+not.
+
+PLEASE read the zlib FAQ http://www.gzip.org/zlib/zlib_faq.html before asking
+for help.
+
+Mark Nelson <markn@ieee.org> wrote an article about zlib for the Jan. 1997
+issue of Dr. Dobb's Journal; a copy of the article is available in
+http://dogma.net/markn/articles/zlibtool/zlibtool.htm
+
+The changes made in version 1.2.1 are documented in the file ChangeLog.
+
+Unsupported third party contributions are provided in directory "contrib".
+
+A Java implementation of zlib is available in the Java Development Kit
+http://java.sun.com/j2se/1.4.2/docs/api/java/util/zip/package-summary.html
+See the zlib home page http://www.zlib.org for details.
+
+A Perl interface to zlib written by Paul Marquess <pmqs@cpan.org> is in the
+CPAN (Comprehensive Perl Archive Network) sites
+http://www.cpan.org/modules/by-module/Compress/
+
+A Python interface to zlib written by A.M. Kuchling <amk@magnet.com> is
+available in Python 1.5 and later versions, see
+http://www.python.org/doc/lib/module-zlib.html
+
+A zlib binding for TCL written by Andreas Kupries <a.kupries@westend.com> is
+availlable at http://www.oche.de/~akupries/soft/trf/trf_zip.html
+
+An experimental package to read and write files in .zip format, written on top
+of zlib by Gilles Vollant <info@winimage.com>, is available in the
+contrib/minizip directory of zlib.
+
+
+Notes for some targets:
+
+- For Windows DLL versions, please see win32/DLL_FAQ.txt
+
+- For 64-bit Irix, deflate.c must be compiled without any optimization. With
+ -O, one libpng test fails. The test works in 32 bit mode (with the -n32
+ compiler flag). The compiler bug has been reported to SGI.
+
+- zlib doesn't work with gcc 2.6.3 on a DEC 3000/300LX under OSF/1 2.1 it works
+ when compiled with cc.
+
+- On Digital Unix 4.0D (formely OSF/1) on AlphaServer, the cc option -std1 is
+ necessary to get gzprintf working correctly. This is done by configure.
+
+- zlib doesn't work on HP-UX 9.05 with some versions of /bin/cc. It works with
+ other compilers. Use "make test" to check your compiler.
+
+- gzdopen is not supported on RISCOS, BEOS and by some Mac compilers.
+
+- For PalmOs, see http://palmzlib.sourceforge.net/
+
+- When building a shared, i.e. dynamic library on Mac OS X, the library must be
+ installed before testing (do "make install" before "make test"), since the
+ library location is specified in the library.
+
+
+Acknowledgments:
+
+ The deflate format used by zlib was defined by Phil Katz. The deflate
+ and zlib specifications were written by L. Peter Deutsch. Thanks to all the
+ people who reported problems and suggested various improvements in zlib;
+ they are too numerous to cite here.
+
+Copyright notice:
+
+ (C) 1995-2003 Jean-loup Gailly and Mark Adler
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+ 3. This notice may not be removed or altered from any source distribution.
+
+ Jean-loup Gailly Mark Adler
+ jloup@gzip.org madler@alumni.caltech.edu
+
+If you use the zlib library in a product, we would appreciate *not*
+receiving lengthy legal documents to sign. The sources are provided
+for free but without warranty of any kind. The library has been
+entirely written by Jean-loup Gailly and Mark Adler; it does not
+include third-party code.
+
+If you redistribute modified sources, we would appreciate that you include
+in the file ChangeLog history information documenting your changes. Please
+read the FAQ for more information on the distribution of modified source
+versions.
diff --git a/src/zlib/adler32.c b/src/zlib/adler32.c
new file mode 100755
index 0000000..f726519
--- /dev/null
+++ b/src/zlib/adler32.c
@@ -0,0 +1,75 @@
+/* adler32.c -- compute the Adler-32 checksum of a data stream
+ * Copyright (C) 1995-2003 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* @(#) $ Id: adler32.c,v 1.1 2004/10/12 16:03:51 luz Exp $ */
+
+
+#define ZLIB_INTERNAL
+#include "zlib.h"
+
+#define BASE 65521UL /* largest prime smaller than 65536 */
+#define NMAX 5552
+/* NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */
+
+#define DO1(buf,i) {s1 += buf[i]; s2 += s1;}
+#define DO2(buf,i) DO1(buf,i); DO1(buf,i+1);
+#define DO4(buf,i) DO2(buf,i); DO2(buf,i+2);
+#define DO8(buf,i) DO4(buf,i); DO4(buf,i+4);
+#define DO16(buf) DO8(buf,0); DO8(buf,8);
+
+#ifdef NO_DIVIDE
+# define MOD(a) \
+ do { \
+ if (a >= (BASE << 16)) a -= (BASE << 16); \
+ if (a >= (BASE << 15)) a -= (BASE << 15); \
+ if (a >= (BASE << 14)) a -= (BASE << 14); \
+ if (a >= (BASE << 13)) a -= (BASE << 13); \
+ if (a >= (BASE << 12)) a -= (BASE << 12); \
+ if (a >= (BASE << 11)) a -= (BASE << 11); \
+ if (a >= (BASE << 10)) a -= (BASE << 10); \
+ if (a >= (BASE << 9)) a -= (BASE << 9); \
+ if (a >= (BASE << 8)) a -= (BASE << 8); \
+ if (a >= (BASE << 7)) a -= (BASE << 7); \
+ if (a >= (BASE << 6)) a -= (BASE << 6); \
+ if (a >= (BASE << 5)) a -= (BASE << 5); \
+ if (a >= (BASE << 4)) a -= (BASE << 4); \
+ if (a >= (BASE << 3)) a -= (BASE << 3); \
+ if (a >= (BASE << 2)) a -= (BASE << 2); \
+ if (a >= (BASE << 1)) a -= (BASE << 1); \
+ if (a >= BASE) a -= BASE; \
+ } while (0)
+#else
+# define MOD(a) a %= BASE
+#endif
+
+/* ========================================================================= */
+uLong ZEXPORT adler32(adler, buf, len)
+ uLong adler;
+ const Bytef *buf;
+ uInt len;
+{
+ unsigned long s1 = adler & 0xffff;
+ unsigned long s2 = (adler >> 16) & 0xffff;
+ int k;
+
+ if (buf == Z_NULL) return 1L;
+
+ while (len > 0) {
+ k = len < NMAX ? (int)len : NMAX;
+ len -= k;
+ while (k >= 16) {
+ DO16(buf);
+ buf += 16;
+ k -= 16;
+ }
+ if (k != 0) do {
+ s1 += *buf++;
+ s2 += s1;
+ } while (--k);
+ MOD(s1);
+ MOD(s2);
+ }
+ return (s2 << 16) | s1;
+}
diff --git a/src/zlib/algorithm.txt b/src/zlib/algorithm.txt
new file mode 100755
index 0000000..b022dde
--- /dev/null
+++ b/src/zlib/algorithm.txt
@@ -0,0 +1,209 @@
+1. Compression algorithm (deflate)
+
+The deflation algorithm used by gzip (also zip and zlib) is a variation of
+LZ77 (Lempel-Ziv 1977, see reference below). It finds duplicated strings in
+the input data. The second occurrence of a string is replaced by a
+pointer to the previous string, in the form of a pair (distance,
+length). Distances are limited to 32K bytes, and lengths are limited
+to 258 bytes. When a string does not occur anywhere in the previous
+32K bytes, it is emitted as a sequence of literal bytes. (In this
+description, `string' must be taken as an arbitrary sequence of bytes,
+and is not restricted to printable characters.)
+
+Literals or match lengths are compressed with one Huffman tree, and
+match distances are compressed with another tree. The trees are stored
+in a compact form at the start of each block. The blocks can have any
+size (except that the compressed data for one block must fit in
+available memory). A block is terminated when deflate() determines that
+it would be useful to start another block with fresh trees. (This is
+somewhat similar to the behavior of LZW-based _compress_.)
+
+Duplicated strings are found using a hash table. All input strings of
+length 3 are inserted in the hash table. A hash index is computed for
+the next 3 bytes. If the hash chain for this index is not empty, all
+strings in the chain are compared with the current input string, and
+the longest match is selected.
+
+The hash chains are searched starting with the most recent strings, to
+favor small distances and thus take advantage of the Huffman encoding.
+The hash chains are singly linked. There are no deletions from the
+hash chains, the algorithm simply discards matches that are too old.
+
+To avoid a worst-case situation, very long hash chains are arbitrarily
+truncated at a certain length, determined by a runtime option (level
+parameter of deflateInit). So deflate() does not always find the longest
+possible match but generally finds a match which is long enough.
+
+deflate() also defers the selection of matches with a lazy evaluation
+mechanism. After a match of length N has been found, deflate() searches for
+a longer match at the next input byte. If a longer match is found, the
+previous match is truncated to a length of one (thus producing a single
+literal byte) and the process of lazy evaluation begins again. Otherwise,
+the original match is kept, and the next match search is attempted only N
+steps later.
+
+The lazy match evaluation is also subject to a runtime parameter. If
+the current match is long enough, deflate() reduces the search for a longer
+match, thus speeding up the whole process. If compression ratio is more
+important than speed, deflate() attempts a complete second search even if
+the first match is already long enough.
+
+The lazy match evaluation is not performed for the fastest compression
+modes (level parameter 1 to 3). For these fast modes, new strings
+are inserted in the hash table only when no match was found, or
+when the match is not too long. This degrades the compression ratio
+but saves time since there are both fewer insertions and fewer searches.
+
+
+2. Decompression algorithm (inflate)
+
+2.1 Introduction
+
+The key question is how to represent a Huffman code (or any prefix code) so
+that you can decode fast. The most important characteristic is that shorter
+codes are much more common than longer codes, so pay attention to decoding the
+short codes fast, and let the long codes take longer to decode.
+
+inflate() sets up a first level table that covers some number of bits of
+input less than the length of longest code. It gets that many bits from the
+stream, and looks it up in the table. The table will tell if the next
+code is that many bits or less and how many, and if it is, it will tell
+the value, else it will point to the next level table for which inflate()
+grabs more bits and tries to decode a longer code.
+
+How many bits to make the first lookup is a tradeoff between the time it
+takes to decode and the time it takes to build the table. If building the
+table took no time (and if you had infinite memory), then there would only
+be a first level table to cover all the way to the longest code. However,
+building the table ends up taking a lot longer for more bits since short
+codes are replicated many times in such a table. What inflate() does is
+simply to make the number of bits in the first table a variable, and then
+to set that variable for the maximum speed.
+
+For inflate, which has 286 possible codes for the literal/length tree, the size
+of the first table is nine bits. Also the distance trees have 30 possible
+values, and the size of the first table is six bits. Note that for each of
+those cases, the table ended up one bit longer than the ``average'' code
+length, i.e. the code length of an approximately flat code which would be a
+little more than eight bits for 286 symbols and a little less than five bits
+for 30 symbols.
+
+
+2.2 More details on the inflate table lookup
+
+Ok, you want to know what this cleverly obfuscated inflate tree actually
+looks like. You are correct that it's not a Huffman tree. It is simply a
+lookup table for the first, let's say, nine bits of a Huffman symbol. The
+symbol could be as short as one bit or as long as 15 bits. If a particular
+symbol is shorter than nine bits, then that symbol's translation is duplicated
+in all those entries that start with that symbol's bits. For example, if the
+symbol is four bits, then it's duplicated 32 times in a nine-bit table. If a
+symbol is nine bits long, it appears in the table once.
+
+If the symbol is longer than nine bits, then that entry in the table points
+to another similar table for the remaining bits. Again, there are duplicated
+entries as needed. The idea is that most of the time the symbol will be short
+and there will only be one table look up. (That's whole idea behind data
+compression in the first place.) For the less frequent long symbols, there
+will be two lookups. If you had a compression method with really long
+symbols, you could have as many levels of lookups as is efficient. For
+inflate, two is enough.
+
+So a table entry either points to another table (in which case nine bits in
+the above example are gobbled), or it contains the translation for the symbol
+and the number of bits to gobble. Then you start again with the next
+ungobbled bit.
+
+You may wonder: why not just have one lookup table for how ever many bits the
+longest symbol is? The reason is that if you do that, you end up spending
+more time filling in duplicate symbol entries than you do actually decoding.
+At least for deflate's output that generates new trees every several 10's of
+kbytes. You can imagine that filling in a 2^15 entry table for a 15-bit code
+would take too long if you're only decoding several thousand symbols. At the
+other extreme, you could make a new table for every bit in the code. In fact,
+that's essentially a Huffman tree. But then you spend two much time
+traversing the tree while decoding, even for short symbols.
+
+So the number of bits for the first lookup table is a trade of the time to
+fill out the table vs. the time spent looking at the second level and above of
+the table.
+
+Here is an example, scaled down:
+
+The code being decoded, with 10 symbols, from 1 to 6 bits long:
+
+A: 0
+B: 10
+C: 1100
+D: 11010
+E: 11011
+F: 11100
+G: 11101
+H: 11110
+I: 111110
+J: 111111
+
+Let's make the first table three bits long (eight entries):
+
+000: A,1
+001: A,1
+010: A,1
+011: A,1
+100: B,2
+101: B,2
+110: -> table X (gobble 3 bits)
+111: -> table Y (gobble 3 bits)
+
+Each entry is what the bits decode as and how many bits that is, i.e. how
+many bits to gobble. Or the entry points to another table, with the number of
+bits to gobble implicit in the size of the table.
+
+Table X is two bits long since the longest code starting with 110 is five bits
+long:
+
+00: C,1
+01: C,1
+10: D,2
+11: E,2
+
+Table Y is three bits long since the longest code starting with 111 is six
+bits long:
+
+000: F,2
+001: F,2
+010: G,2
+011: G,2
+100: H,2
+101: H,2
+110: I,3
+111: J,3
+
+So what we have here are three tables with a total of 20 entries that had to
+be constructed. That's compared to 64 entries for a single table. Or
+compared to 16 entries for a Huffman tree (six two entry tables and one four
+entry table). Assuming that the code ideally represents the probability of
+the symbols, it takes on the average 1.25 lookups per symbol. That's compared
+to one lookup for the single table, or 1.66 lookups per symbol for the
+Huffman tree.
+
+There, I think that gives you a picture of what's going on. For inflate, the
+meaning of a particular symbol is often more than just a letter. It can be a
+byte (a "literal"), or it can be either a length or a distance which
+indicates a base value and a number of bits to fetch after the code that is
+added to the base value. Or it might be the special end-of-block code. The
+data structures created in inftrees.c try to encode all that information
+compactly in the tables.
+
+
+Jean-loup Gailly Mark Adler
+jloup@gzip.org madler@alumni.caltech.edu
+
+
+References:
+
+[LZ77] Ziv J., Lempel A., ``A Universal Algorithm for Sequential Data
+Compression,'' IEEE Transactions on Information Theory, Vol. 23, No. 3,
+pp. 337-343.
+
+``DEFLATE Compressed Data Format Specification'' available in
+http://www.ietf.org/rfc/rfc1951.txt
diff --git a/src/zlib/compress.c b/src/zlib/compress.c
new file mode 100755
index 0000000..d339fec
--- /dev/null
+++ b/src/zlib/compress.c
@@ -0,0 +1,79 @@
+/* compress.c -- compress a memory buffer
+ * Copyright (C) 1995-2002 Jean-loup Gailly.
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* @(#) $ Id$ */
+
+#define ZLIB_INTERNAL
+#include "zlib.h"
+
+/* ===========================================================================
+ Compresses the source buffer into the destination buffer. The level
+ parameter has the same meaning as in deflateInit. sourceLen is the byte
+ length of the source buffer. Upon entry, destLen is the total size of the
+ destination buffer, which must be at least 0.1% larger than sourceLen plus
+ 12 bytes. Upon exit, destLen is the actual size of the compressed buffer.
+
+ compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough
+ memory, Z_BUF_ERROR if there was not enough room in the output buffer,
+ Z_STREAM_ERROR if the level parameter is invalid.
+*/
+int ZEXPORT compress2 (dest, destLen, source, sourceLen, level)
+ Bytef *dest;
+ uLongf *destLen;
+ const Bytef *source;
+ uLong sourceLen;
+ int level;
+{
+ z_stream stream;
+ int err;
+
+ stream.next_in = (Bytef*)source;
+ stream.avail_in = (uInt)sourceLen;
+#ifdef MAXSEG_64K
+ /* Check for source > 64K on 16-bit machine: */
+ if ((uLong)stream.avail_in != sourceLen) return Z_BUF_ERROR;
+#endif
+ stream.next_out = dest;
+ stream.avail_out = (uInt)*destLen;
+ if ((uLong)stream.avail_out != *destLen) return Z_BUF_ERROR;
+
+ stream.zalloc = (alloc_func)0;
+ stream.zfree = (free_func)0;
+ stream.opaque = (voidpf)0;
+
+ err = deflateInit(&stream, level);
+ if (err != Z_OK) return err;
+
+ err = deflate(&stream, Z_FINISH);
+ if (err != Z_STREAM_END) {
+ deflateEnd(&stream);
+ return err == Z_OK ? Z_BUF_ERROR : err;
+ }
+ *destLen = stream.total_out;
+
+ err = deflateEnd(&stream);
+ return err;
+}
+
+/* ===========================================================================
+ */
+int ZEXPORT compress (dest, destLen, source, sourceLen)
+ Bytef *dest;
+ uLongf *destLen;
+ const Bytef *source;
+ uLong sourceLen;
+{
+ return compress2(dest, destLen, source, sourceLen, Z_DEFAULT_COMPRESSION);
+}
+
+/* ===========================================================================
+ If the default memLevel or windowBits for deflateInit() is changed, then
+ this function needs to be updated.
+ */
+uLong ZEXPORT compressBound (sourceLen)
+ uLong sourceLen;
+{
+ return sourceLen + (sourceLen >> 12) + (sourceLen >> 14) + 11;
+}
diff --git a/src/zlib/crc32.c b/src/zlib/crc32.c
new file mode 100755
index 0000000..9654cf7
--- /dev/null
+++ b/src/zlib/crc32.c
@@ -0,0 +1,311 @@
+/* crc32.c -- compute the CRC-32 of a data stream
+ * Copyright (C) 1995-2003 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ *
+ * Thanks to Rodney Brown <rbrown64@csc.com.au> for his contribution of faster
+ * CRC methods: exclusive-oring 32 bits of data at a time, and pre-computing
+ * tables for updating the shift register in one step with three exclusive-ors
+ * instead of four steps with four exclusive-ors. This results about a factor
+ * of two increase in speed on a Power PC G4 (PPC7455) using gcc -O3.
+ */
+
+/* @(#) $ Id$ */
+
+#ifdef MAKECRCH
+# include <stdio.h>
+# ifndef DYNAMIC_CRC_TABLE
+# define DYNAMIC_CRC_TABLE
+# endif /* !DYNAMIC_CRC_TABLE */
+#endif /* MAKECRCH */
+
+#include "zutil.h" /* for STDC and FAR definitions */
+
+#define local static
+
+/* Find a four-byte integer type for crc32_little() and crc32_big(). */
+#ifndef NOBYFOUR
+# ifdef STDC /* need ANSI C limits.h to determine sizes */
+# include <limits.h>
+# define BYFOUR
+# if (UINT_MAX == 0xffffffffUL)
+ typedef unsigned int u4;
+# else
+# if (ULONG_MAX == 0xffffffffUL)
+ typedef unsigned long u4;
+# else
+# if (USHRT_MAX == 0xffffffffUL)
+ typedef unsigned short u4;
+# else
+# undef BYFOUR /* can't find a four-byte integer type! */
+# endif
+# endif
+# endif
+# endif /* STDC */
+#endif /* !NOBYFOUR */
+
+/* Definitions for doing the crc four data bytes at a time. */
+#ifdef BYFOUR
+# define REV(w) (((w)>>24)+(((w)>>8)&0xff00)+ \
+ (((w)&0xff00)<<8)+(((w)&0xff)<<24))
+ local unsigned long crc32_little OF((unsigned long,
+ const unsigned char FAR *, unsigned));
+ local unsigned long crc32_big OF((unsigned long,
+ const unsigned char FAR *, unsigned));
+# define TBLS 8
+#else
+# define TBLS 1
+#endif /* BYFOUR */
+
+#ifdef DYNAMIC_CRC_TABLE
+
+local int crc_table_empty = 1;
+local unsigned long FAR crc_table[TBLS][256];
+local void make_crc_table OF((void));
+#ifdef MAKECRCH
+ local void write_table OF((FILE *, const unsigned long FAR *));
+#endif /* MAKECRCH */
+
+/*
+ Generate tables for a byte-wise 32-bit CRC calculation on the polynomial:
+ x^32+x^26+x^23+x^22+x^16+x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x+1.
+
+ Polynomials over GF(2) are represented in binary, one bit per coefficient,
+ with the lowest powers in the most significant bit. Then adding polynomials
+ is just exclusive-or, and multiplying a polynomial by x is a right shift by
+ one. If we call the above polynomial p, and represent a byte as the
+ polynomial q, also with the lowest power in the most significant bit (so the
+ byte 0xb1 is the polynomial x^7+x^3+x+1), then the CRC is (q*x^32) mod p,
+ where a mod b means the remainder after dividing a by b.
+
+ This calculation is done using the shift-register method of multiplying and
+ taking the remainder. The register is initialized to zero, and for each
+ incoming bit, x^32 is added mod p to the register if the bit is a one (where
+ x^32 mod p is p+x^32 = x^26+...+1), and the register is multiplied mod p by
+ x (which is shifting right by one and adding x^32 mod p if the bit shifted
+ out is a one). We start with the highest power (least significant bit) of
+ q and repeat for all eight bits of q.
+
+ The first table is simply the CRC of all possible eight bit values. This is
+ all the information needed to generate CRCs on data a byte at a time for all
+ combinations of CRC register values and incoming bytes. The remaining tables
+ allow for word-at-a-time CRC calculation for both big-endian and little-
+ endian machines, where a word is four bytes.
+*/
+local void make_crc_table()
+{
+ unsigned long c;
+ int n, k;
+ unsigned long poly; /* polynomial exclusive-or pattern */
+ /* terms of polynomial defining this crc (except x^32): */
+ static const unsigned char p[] = {0,1,2,4,5,7,8,10,11,12,16,22,23,26};
+
+ /* make exclusive-or pattern from polynomial (0xedb88320UL) */
+ poly = 0UL;
+ for (n = 0; n < sizeof(p)/sizeof(unsigned char); n++)
+ poly |= 1UL << (31 - p[n]);
+
+ /* generate a crc for every 8-bit value */
+ for (n = 0; n < 256; n++) {
+ c = (unsigned long)n;
+ for (k = 0; k < 8; k++)
+ c = c & 1 ? poly ^ (c >> 1) : c >> 1;
+ crc_table[0][n] = c;
+ }
+
+#ifdef BYFOUR
+ /* generate crc for each value followed by one, two, and three zeros, and
+ then the byte reversal of those as well as the first table */
+ for (n = 0; n < 256; n++) {
+ c = crc_table[0][n];
+ crc_table[4][n] = REV(c);
+ for (k = 1; k < 4; k++) {
+ c = crc_table[0][c & 0xff] ^ (c >> 8);
+ crc_table[k][n] = c;
+ crc_table[k + 4][n] = REV(c);
+ }
+ }
+#endif /* BYFOUR */
+
+ crc_table_empty = 0;
+
+#ifdef MAKECRCH
+ /* write out CRC tables to crc32.h */
+ {
+ FILE *out;
+
+ out = fopen("crc32.h", "w");
+ if (out == NULL) return;
+ fprintf(out, "/* crc32.h -- tables for rapid CRC calculation\n");
+ fprintf(out, " * Generated automatically by crc32.c\n */\n\n");
+ fprintf(out, "local const unsigned long FAR ");
+ fprintf(out, "crc_table[TBLS][256] =\n{\n {\n");
+ write_table(out, crc_table[0]);
+# ifdef BYFOUR
+ fprintf(out, "#ifdef BYFOUR\n");
+ for (k = 1; k < 8; k++) {
+ fprintf(out, " },\n {\n");
+ write_table(out, crc_table[k]);
+ }
+ fprintf(out, "#endif\n");
+# endif /* BYFOUR */
+ fprintf(out, " }\n};\n");
+ fclose(out);
+ }
+#endif /* MAKECRCH */
+}
+
+#ifdef MAKECRCH
+local void write_table(out, table)
+ FILE *out;
+ const unsigned long FAR *table;
+{
+ int n;
+
+ for (n = 0; n < 256; n++)
+ fprintf(out, "%s0x%08lxUL%s", n % 5 ? "" : " ", table[n],
+ n == 255 ? "\n" : (n % 5 == 4 ? ",\n" : ", "));
+}
+#endif /* MAKECRCH */
+
+#else /* !DYNAMIC_CRC_TABLE */
+/* ========================================================================
+ * Tables of CRC-32s of all single-byte values, made by make_crc_table().
+ */
+#include "crc32.h"
+#endif /* DYNAMIC_CRC_TABLE */
+
+/* =========================================================================
+ * This function can be used by asm versions of crc32()
+ */
+const unsigned long FAR * ZEXPORT get_crc_table()
+{
+#ifdef DYNAMIC_CRC_TABLE
+ if (crc_table_empty) make_crc_table();
+#endif /* DYNAMIC_CRC_TABLE */
+ return (const unsigned long FAR *)crc_table;
+}
+
+/* ========================================================================= */
+#define DO1 crc = crc_table[0][((int)crc ^ (*buf++)) & 0xff] ^ (crc >> 8)
+#define DO8 DO1; DO1; DO1; DO1; DO1; DO1; DO1; DO1
+
+/* ========================================================================= */
+unsigned long ZEXPORT crc32(crc, buf, len)
+ unsigned long crc;
+ const unsigned char FAR *buf;
+ unsigned len;
+{
+ if (buf == Z_NULL) return 0UL;
+
+#ifdef DYNAMIC_CRC_TABLE
+ if (crc_table_empty)
+ make_crc_table();
+#endif /* DYNAMIC_CRC_TABLE */
+
+#ifdef BYFOUR
+ if (sizeof(void *) == sizeof(ptrdiff_t)) {
+ u4 endian;
+
+ endian = 1;
+ if (*((unsigned char *)(&endian)))
+ return crc32_little(crc, buf, len);
+ else
+ return crc32_big(crc, buf, len);
+ }
+#endif /* BYFOUR */
+ crc = crc ^ 0xffffffffUL;
+ while (len >= 8) {
+ DO8;
+ len -= 8;
+ }
+ if (len) do {
+ DO1;
+ } while (--len);
+ return crc ^ 0xffffffffUL;
+}
+
+#ifdef BYFOUR
+
+/* ========================================================================= */
+#define DOLIT4 c ^= *buf4++; \
+ c = crc_table[3][c & 0xff] ^ crc_table[2][(c >> 8) & 0xff] ^ \
+ crc_table[1][(c >> 16) & 0xff] ^ crc_table[0][c >> 24]
+#define DOLIT32 DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4
+
+/* ========================================================================= */
+local unsigned long crc32_little(crc, buf, len)
+ unsigned long crc;
+ const unsigned char FAR *buf;
+ unsigned len;
+{
+ register u4 c;
+ register const u4 FAR *buf4;
+
+ c = (u4)crc;
+ c = ~c;
+ while (len && ((ptrdiff_t)buf & 3)) {
+ c = crc_table[0][(c ^ *buf++) & 0xff] ^ (c >> 8);
+ len--;
+ }
+
+ buf4 = (const u4 FAR *)buf;
+ while (len >= 32) {
+ DOLIT32;
+ len -= 32;
+ }
+ while (len >= 4) {
+ DOLIT4;
+ len -= 4;
+ }
+ buf = (const unsigned char FAR *)buf4;
+
+ if (len) do {
+ c = crc_table[0][(c ^ *buf++) & 0xff] ^ (c >> 8);
+ } while (--len);
+ c = ~c;
+ return (unsigned long)c;
+}
+
+/* ========================================================================= */
+#define DOBIG4 c ^= *++buf4; \
+ c = crc_table[4][c & 0xff] ^ crc_table[5][(c >> 8) & 0xff] ^ \
+ crc_table[6][(c >> 16) & 0xff] ^ crc_table[7][c >> 24]
+#define DOBIG32 DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4
+
+/* ========================================================================= */
+local unsigned long crc32_big(crc, buf, len)
+ unsigned long crc;
+ const unsigned char FAR *buf;
+ unsigned len;
+{
+ register u4 c;
+ register const u4 FAR *buf4;
+
+ c = REV((u4)crc);
+ c = ~c;
+ while (len && ((ptrdiff_t)buf & 3)) {
+ c = crc_table[4][(c >> 24) ^ *buf++] ^ (c << 8);
+ len--;
+ }
+
+ buf4 = (const u4 FAR *)buf;
+ buf4--;
+ while (len >= 32) {
+ DOBIG32;
+ len -= 32;
+ }
+ while (len >= 4) {
+ DOBIG4;
+ len -= 4;
+ }
+ buf4++;
+ buf = (const unsigned char FAR *)buf4;
+
+ if (len) do {
+ c = crc_table[4][(c >> 24) ^ *buf++] ^ (c << 8);
+ } while (--len);
+ c = ~c;
+ return (unsigned long)(REV(c));
+}
+
+#endif /* BYFOUR */
diff --git a/src/zlib/crc32.h b/src/zlib/crc32.h
new file mode 100755
index 0000000..8053b61
--- /dev/null
+++ b/src/zlib/crc32.h
@@ -0,0 +1,441 @@
+/* crc32.h -- tables for rapid CRC calculation
+ * Generated automatically by crc32.c
+ */
+
+local const unsigned long FAR crc_table[TBLS][256] =
+{
+ {
+ 0x00000000UL, 0x77073096UL, 0xee0e612cUL, 0x990951baUL, 0x076dc419UL,
+ 0x706af48fUL, 0xe963a535UL, 0x9e6495a3UL, 0x0edb8832UL, 0x79dcb8a4UL,
+ 0xe0d5e91eUL, 0x97d2d988UL, 0x09b64c2bUL, 0x7eb17cbdUL, 0xe7b82d07UL,
+ 0x90bf1d91UL, 0x1db71064UL, 0x6ab020f2UL, 0xf3b97148UL, 0x84be41deUL,
+ 0x1adad47dUL, 0x6ddde4ebUL, 0xf4d4b551UL, 0x83d385c7UL, 0x136c9856UL,
+ 0x646ba8c0UL, 0xfd62f97aUL, 0x8a65c9ecUL, 0x14015c4fUL, 0x63066cd9UL,
+ 0xfa0f3d63UL, 0x8d080df5UL, 0x3b6e20c8UL, 0x4c69105eUL, 0xd56041e4UL,
+ 0xa2677172UL, 0x3c03e4d1UL, 0x4b04d447UL, 0xd20d85fdUL, 0xa50ab56bUL,
+ 0x35b5a8faUL, 0x42b2986cUL, 0xdbbbc9d6UL, 0xacbcf940UL, 0x32d86ce3UL,
+ 0x45df5c75UL, 0xdcd60dcfUL, 0xabd13d59UL, 0x26d930acUL, 0x51de003aUL,
+ 0xc8d75180UL, 0xbfd06116UL, 0x21b4f4b5UL, 0x56b3c423UL, 0xcfba9599UL,
+ 0xb8bda50fUL, 0x2802b89eUL, 0x5f058808UL, 0xc60cd9b2UL, 0xb10be924UL,
+ 0x2f6f7c87UL, 0x58684c11UL, 0xc1611dabUL, 0xb6662d3dUL, 0x76dc4190UL,
+ 0x01db7106UL, 0x98d220bcUL, 0xefd5102aUL, 0x71b18589UL, 0x06b6b51fUL,
+ 0x9fbfe4a5UL, 0xe8b8d433UL, 0x7807c9a2UL, 0x0f00f934UL, 0x9609a88eUL,
+ 0xe10e9818UL, 0x7f6a0dbbUL, 0x086d3d2dUL, 0x91646c97UL, 0xe6635c01UL,
+ 0x6b6b51f4UL, 0x1c6c6162UL, 0x856530d8UL, 0xf262004eUL, 0x6c0695edUL,
+ 0x1b01a57bUL, 0x8208f4c1UL, 0xf50fc457UL, 0x65b0d9c6UL, 0x12b7e950UL,
+ 0x8bbeb8eaUL, 0xfcb9887cUL, 0x62dd1ddfUL, 0x15da2d49UL, 0x8cd37cf3UL,
+ 0xfbd44c65UL, 0x4db26158UL, 0x3ab551ceUL, 0xa3bc0074UL, 0xd4bb30e2UL,
+ 0x4adfa541UL, 0x3dd895d7UL, 0xa4d1c46dUL, 0xd3d6f4fbUL, 0x4369e96aUL,
+ 0x346ed9fcUL, 0xad678846UL, 0xda60b8d0UL, 0x44042d73UL, 0x33031de5UL,
+ 0xaa0a4c5fUL, 0xdd0d7cc9UL, 0x5005713cUL, 0x270241aaUL, 0xbe0b1010UL,
+ 0xc90c2086UL, 0x5768b525UL, 0x206f85b3UL, 0xb966d409UL, 0xce61e49fUL,
+ 0x5edef90eUL, 0x29d9c998UL, 0xb0d09822UL, 0xc7d7a8b4UL, 0x59b33d17UL,
+ 0x2eb40d81UL, 0xb7bd5c3bUL, 0xc0ba6cadUL, 0xedb88320UL, 0x9abfb3b6UL,
+ 0x03b6e20cUL, 0x74b1d29aUL, 0xead54739UL, 0x9dd277afUL, 0x04db2615UL,
+ 0x73dc1683UL, 0xe3630b12UL, 0x94643b84UL, 0x0d6d6a3eUL, 0x7a6a5aa8UL,
+ 0xe40ecf0bUL, 0x9309ff9dUL, 0x0a00ae27UL, 0x7d079eb1UL, 0xf00f9344UL,
+ 0x8708a3d2UL, 0x1e01f268UL, 0x6906c2feUL, 0xf762575dUL, 0x806567cbUL,
+ 0x196c3671UL, 0x6e6b06e7UL, 0xfed41b76UL, 0x89d32be0UL, 0x10da7a5aUL,
+ 0x67dd4accUL, 0xf9b9df6fUL, 0x8ebeeff9UL, 0x17b7be43UL, 0x60b08ed5UL,
+ 0xd6d6a3e8UL, 0xa1d1937eUL, 0x38d8c2c4UL, 0x4fdff252UL, 0xd1bb67f1UL,
+ 0xa6bc5767UL, 0x3fb506ddUL, 0x48b2364bUL, 0xd80d2bdaUL, 0xaf0a1b4cUL,
+ 0x36034af6UL, 0x41047a60UL, 0xdf60efc3UL, 0xa867df55UL, 0x316e8eefUL,
+ 0x4669be79UL, 0xcb61b38cUL, 0xbc66831aUL, 0x256fd2a0UL, 0x5268e236UL,
+ 0xcc0c7795UL, 0xbb0b4703UL, 0x220216b9UL, 0x5505262fUL, 0xc5ba3bbeUL,
+ 0xb2bd0b28UL, 0x2bb45a92UL, 0x5cb36a04UL, 0xc2d7ffa7UL, 0xb5d0cf31UL,
+ 0x2cd99e8bUL, 0x5bdeae1dUL, 0x9b64c2b0UL, 0xec63f226UL, 0x756aa39cUL,
+ 0x026d930aUL, 0x9c0906a9UL, 0xeb0e363fUL, 0x72076785UL, 0x05005713UL,
+ 0x95bf4a82UL, 0xe2b87a14UL, 0x7bb12baeUL, 0x0cb61b38UL, 0x92d28e9bUL,
+ 0xe5d5be0dUL, 0x7cdcefb7UL, 0x0bdbdf21UL, 0x86d3d2d4UL, 0xf1d4e242UL,
+ 0x68ddb3f8UL, 0x1fda836eUL, 0x81be16cdUL, 0xf6b9265bUL, 0x6fb077e1UL,
+ 0x18b74777UL, 0x88085ae6UL, 0xff0f6a70UL, 0x66063bcaUL, 0x11010b5cUL,
+ 0x8f659effUL, 0xf862ae69UL, 0x616bffd3UL, 0x166ccf45UL, 0xa00ae278UL,
+ 0xd70dd2eeUL, 0x4e048354UL, 0x3903b3c2UL, 0xa7672661UL, 0xd06016f7UL,
+ 0x4969474dUL, 0x3e6e77dbUL, 0xaed16a4aUL, 0xd9d65adcUL, 0x40df0b66UL,
+ 0x37d83bf0UL, 0xa9bcae53UL, 0xdebb9ec5UL, 0x47b2cf7fUL, 0x30b5ffe9UL,
+ 0xbdbdf21cUL, 0xcabac28aUL, 0x53b39330UL, 0x24b4a3a6UL, 0xbad03605UL,
+ 0xcdd70693UL, 0x54de5729UL, 0x23d967bfUL, 0xb3667a2eUL, 0xc4614ab8UL,
+ 0x5d681b02UL, 0x2a6f2b94UL, 0xb40bbe37UL, 0xc30c8ea1UL, 0x5a05df1bUL,
+ 0x2d02ef8dUL
+#ifdef BYFOUR
+ },
+ {
+ 0x00000000UL, 0x191b3141UL, 0x32366282UL, 0x2b2d53c3UL, 0x646cc504UL,
+ 0x7d77f445UL, 0x565aa786UL, 0x4f4196c7UL, 0xc8d98a08UL, 0xd1c2bb49UL,
+ 0xfaefe88aUL, 0xe3f4d9cbUL, 0xacb54f0cUL, 0xb5ae7e4dUL, 0x9e832d8eUL,
+ 0x87981ccfUL, 0x4ac21251UL, 0x53d92310UL, 0x78f470d3UL, 0x61ef4192UL,
+ 0x2eaed755UL, 0x37b5e614UL, 0x1c98b5d7UL, 0x05838496UL, 0x821b9859UL,
+ 0x9b00a918UL, 0xb02dfadbUL, 0xa936cb9aUL, 0xe6775d5dUL, 0xff6c6c1cUL,
+ 0xd4413fdfUL, 0xcd5a0e9eUL, 0x958424a2UL, 0x8c9f15e3UL, 0xa7b24620UL,
+ 0xbea97761UL, 0xf1e8e1a6UL, 0xe8f3d0e7UL, 0xc3de8324UL, 0xdac5b265UL,
+ 0x5d5daeaaUL, 0x44469febUL, 0x6f6bcc28UL, 0x7670fd69UL, 0x39316baeUL,
+ 0x202a5aefUL, 0x0b07092cUL, 0x121c386dUL, 0xdf4636f3UL, 0xc65d07b2UL,
+ 0xed705471UL, 0xf46b6530UL, 0xbb2af3f7UL, 0xa231c2b6UL, 0x891c9175UL,
+ 0x9007a034UL, 0x179fbcfbUL, 0x0e848dbaUL, 0x25a9de79UL, 0x3cb2ef38UL,
+ 0x73f379ffUL, 0x6ae848beUL, 0x41c51b7dUL, 0x58de2a3cUL, 0xf0794f05UL,
+ 0xe9627e44UL, 0xc24f2d87UL, 0xdb541cc6UL, 0x94158a01UL, 0x8d0ebb40UL,
+ 0xa623e883UL, 0xbf38d9c2UL, 0x38a0c50dUL, 0x21bbf44cUL, 0x0a96a78fUL,
+ 0x138d96ceUL, 0x5ccc0009UL, 0x45d73148UL, 0x6efa628bUL, 0x77e153caUL,
+ 0xbabb5d54UL, 0xa3a06c15UL, 0x888d3fd6UL, 0x91960e97UL, 0xded79850UL,
+ 0xc7cca911UL, 0xece1fad2UL, 0xf5facb93UL, 0x7262d75cUL, 0x6b79e61dUL,
+ 0x4054b5deUL, 0x594f849fUL, 0x160e1258UL, 0x0f152319UL, 0x243870daUL,
+ 0x3d23419bUL, 0x65fd6ba7UL, 0x7ce65ae6UL, 0x57cb0925UL, 0x4ed03864UL,
+ 0x0191aea3UL, 0x188a9fe2UL, 0x33a7cc21UL, 0x2abcfd60UL, 0xad24e1afUL,
+ 0xb43fd0eeUL, 0x9f12832dUL, 0x8609b26cUL, 0xc94824abUL, 0xd05315eaUL,
+ 0xfb7e4629UL, 0xe2657768UL, 0x2f3f79f6UL, 0x362448b7UL, 0x1d091b74UL,
+ 0x04122a35UL, 0x4b53bcf2UL, 0x52488db3UL, 0x7965de70UL, 0x607eef31UL,
+ 0xe7e6f3feUL, 0xfefdc2bfUL, 0xd5d0917cUL, 0xcccba03dUL, 0x838a36faUL,
+ 0x9a9107bbUL, 0xb1bc5478UL, 0xa8a76539UL, 0x3b83984bUL, 0x2298a90aUL,
+ 0x09b5fac9UL, 0x10aecb88UL, 0x5fef5d4fUL, 0x46f46c0eUL, 0x6dd93fcdUL,
+ 0x74c20e8cUL, 0xf35a1243UL, 0xea412302UL, 0xc16c70c1UL, 0xd8774180UL,
+ 0x9736d747UL, 0x8e2de606UL, 0xa500b5c5UL, 0xbc1b8484UL, 0x71418a1aUL,
+ 0x685abb5bUL, 0x4377e898UL, 0x5a6cd9d9UL, 0x152d4f1eUL, 0x0c367e5fUL,
+ 0x271b2d9cUL, 0x3e001cddUL, 0xb9980012UL, 0xa0833153UL, 0x8bae6290UL,
+ 0x92b553d1UL, 0xddf4c516UL, 0xc4eff457UL, 0xefc2a794UL, 0xf6d996d5UL,
+ 0xae07bce9UL, 0xb71c8da8UL, 0x9c31de6bUL, 0x852aef2aUL, 0xca6b79edUL,
+ 0xd37048acUL, 0xf85d1b6fUL, 0xe1462a2eUL, 0x66de36e1UL, 0x7fc507a0UL,
+ 0x54e85463UL, 0x4df36522UL, 0x02b2f3e5UL, 0x1ba9c2a4UL, 0x30849167UL,
+ 0x299fa026UL, 0xe4c5aeb8UL, 0xfdde9ff9UL, 0xd6f3cc3aUL, 0xcfe8fd7bUL,
+ 0x80a96bbcUL, 0x99b25afdUL, 0xb29f093eUL, 0xab84387fUL, 0x2c1c24b0UL,
+ 0x350715f1UL, 0x1e2a4632UL, 0x07317773UL, 0x4870e1b4UL, 0x516bd0f5UL,
+ 0x7a468336UL, 0x635db277UL, 0xcbfad74eUL, 0xd2e1e60fUL, 0xf9ccb5ccUL,
+ 0xe0d7848dUL, 0xaf96124aUL, 0xb68d230bUL, 0x9da070c8UL, 0x84bb4189UL,
+ 0x03235d46UL, 0x1a386c07UL, 0x31153fc4UL, 0x280e0e85UL, 0x674f9842UL,
+ 0x7e54a903UL, 0x5579fac0UL, 0x4c62cb81UL, 0x8138c51fUL, 0x9823f45eUL,
+ 0xb30ea79dUL, 0xaa1596dcUL, 0xe554001bUL, 0xfc4f315aUL, 0xd7626299UL,
+ 0xce7953d8UL, 0x49e14f17UL, 0x50fa7e56UL, 0x7bd72d95UL, 0x62cc1cd4UL,
+ 0x2d8d8a13UL, 0x3496bb52UL, 0x1fbbe891UL, 0x06a0d9d0UL, 0x5e7ef3ecUL,
+ 0x4765c2adUL, 0x6c48916eUL, 0x7553a02fUL, 0x3a1236e8UL, 0x230907a9UL,
+ 0x0824546aUL, 0x113f652bUL, 0x96a779e4UL, 0x8fbc48a5UL, 0xa4911b66UL,
+ 0xbd8a2a27UL, 0xf2cbbce0UL, 0xebd08da1UL, 0xc0fdde62UL, 0xd9e6ef23UL,
+ 0x14bce1bdUL, 0x0da7d0fcUL, 0x268a833fUL, 0x3f91b27eUL, 0x70d024b9UL,
+ 0x69cb15f8UL, 0x42e6463bUL, 0x5bfd777aUL, 0xdc656bb5UL, 0xc57e5af4UL,
+ 0xee530937UL, 0xf7483876UL, 0xb809aeb1UL, 0xa1129ff0UL, 0x8a3fcc33UL,
+ 0x9324fd72UL
+ },
+ {
+ 0x00000000UL, 0x01c26a37UL, 0x0384d46eUL, 0x0246be59UL, 0x0709a8dcUL,
+ 0x06cbc2ebUL, 0x048d7cb2UL, 0x054f1685UL, 0x0e1351b8UL, 0x0fd13b8fUL,
+ 0x0d9785d6UL, 0x0c55efe1UL, 0x091af964UL, 0x08d89353UL, 0x0a9e2d0aUL,
+ 0x0b5c473dUL, 0x1c26a370UL, 0x1de4c947UL, 0x1fa2771eUL, 0x1e601d29UL,
+ 0x1b2f0bacUL, 0x1aed619bUL, 0x18abdfc2UL, 0x1969b5f5UL, 0x1235f2c8UL,
+ 0x13f798ffUL, 0x11b126a6UL, 0x10734c91UL, 0x153c5a14UL, 0x14fe3023UL,
+ 0x16b88e7aUL, 0x177ae44dUL, 0x384d46e0UL, 0x398f2cd7UL, 0x3bc9928eUL,
+ 0x3a0bf8b9UL, 0x3f44ee3cUL, 0x3e86840bUL, 0x3cc03a52UL, 0x3d025065UL,
+ 0x365e1758UL, 0x379c7d6fUL, 0x35dac336UL, 0x3418a901UL, 0x3157bf84UL,
+ 0x3095d5b3UL, 0x32d36beaUL, 0x331101ddUL, 0x246be590UL, 0x25a98fa7UL,
+ 0x27ef31feUL, 0x262d5bc9UL, 0x23624d4cUL, 0x22a0277bUL, 0x20e69922UL,
+ 0x2124f315UL, 0x2a78b428UL, 0x2bbade1fUL, 0x29fc6046UL, 0x283e0a71UL,
+ 0x2d711cf4UL, 0x2cb376c3UL, 0x2ef5c89aUL, 0x2f37a2adUL, 0x709a8dc0UL,
+ 0x7158e7f7UL, 0x731e59aeUL, 0x72dc3399UL, 0x7793251cUL, 0x76514f2bUL,
+ 0x7417f172UL, 0x75d59b45UL, 0x7e89dc78UL, 0x7f4bb64fUL, 0x7d0d0816UL,
+ 0x7ccf6221UL, 0x798074a4UL, 0x78421e93UL, 0x7a04a0caUL, 0x7bc6cafdUL,
+ 0x6cbc2eb0UL, 0x6d7e4487UL, 0x6f38fadeUL, 0x6efa90e9UL, 0x6bb5866cUL,
+ 0x6a77ec5bUL, 0x68315202UL, 0x69f33835UL, 0x62af7f08UL, 0x636d153fUL,
+ 0x612bab66UL, 0x60e9c151UL, 0x65a6d7d4UL, 0x6464bde3UL, 0x662203baUL,
+ 0x67e0698dUL, 0x48d7cb20UL, 0x4915a117UL, 0x4b531f4eUL, 0x4a917579UL,
+ 0x4fde63fcUL, 0x4e1c09cbUL, 0x4c5ab792UL, 0x4d98dda5UL, 0x46c49a98UL,
+ 0x4706f0afUL, 0x45404ef6UL, 0x448224c1UL, 0x41cd3244UL, 0x400f5873UL,
+ 0x4249e62aUL, 0x438b8c1dUL, 0x54f16850UL, 0x55330267UL, 0x5775bc3eUL,
+ 0x56b7d609UL, 0x53f8c08cUL, 0x523aaabbUL, 0x507c14e2UL, 0x51be7ed5UL,
+ 0x5ae239e8UL, 0x5b2053dfUL, 0x5966ed86UL, 0x58a487b1UL, 0x5deb9134UL,
+ 0x5c29fb03UL, 0x5e6f455aUL, 0x5fad2f6dUL, 0xe1351b80UL, 0xe0f771b7UL,
+ 0xe2b1cfeeUL, 0xe373a5d9UL, 0xe63cb35cUL, 0xe7fed96bUL, 0xe5b86732UL,
+ 0xe47a0d05UL, 0xef264a38UL, 0xeee4200fUL, 0xeca29e56UL, 0xed60f461UL,
+ 0xe82fe2e4UL, 0xe9ed88d3UL, 0xebab368aUL, 0xea695cbdUL, 0xfd13b8f0UL,
+ 0xfcd1d2c7UL, 0xfe976c9eUL, 0xff5506a9UL, 0xfa1a102cUL, 0xfbd87a1bUL,
+ 0xf99ec442UL, 0xf85cae75UL, 0xf300e948UL, 0xf2c2837fUL, 0xf0843d26UL,
+ 0xf1465711UL, 0xf4094194UL, 0xf5cb2ba3UL, 0xf78d95faUL, 0xf64fffcdUL,
+ 0xd9785d60UL, 0xd8ba3757UL, 0xdafc890eUL, 0xdb3ee339UL, 0xde71f5bcUL,
+ 0xdfb39f8bUL, 0xddf521d2UL, 0xdc374be5UL, 0xd76b0cd8UL, 0xd6a966efUL,
+ 0xd4efd8b6UL, 0xd52db281UL, 0xd062a404UL, 0xd1a0ce33UL, 0xd3e6706aUL,
+ 0xd2241a5dUL, 0xc55efe10UL, 0xc49c9427UL, 0xc6da2a7eUL, 0xc7184049UL,
+ 0xc25756ccUL, 0xc3953cfbUL, 0xc1d382a2UL, 0xc011e895UL, 0xcb4dafa8UL,
+ 0xca8fc59fUL, 0xc8c97bc6UL, 0xc90b11f1UL, 0xcc440774UL, 0xcd866d43UL,
+ 0xcfc0d31aUL, 0xce02b92dUL, 0x91af9640UL, 0x906dfc77UL, 0x922b422eUL,
+ 0x93e92819UL, 0x96a63e9cUL, 0x976454abUL, 0x9522eaf2UL, 0x94e080c5UL,
+ 0x9fbcc7f8UL, 0x9e7eadcfUL, 0x9c381396UL, 0x9dfa79a1UL, 0x98b56f24UL,
+ 0x99770513UL, 0x9b31bb4aUL, 0x9af3d17dUL, 0x8d893530UL, 0x8c4b5f07UL,
+ 0x8e0de15eUL, 0x8fcf8b69UL, 0x8a809decUL, 0x8b42f7dbUL, 0x89044982UL,
+ 0x88c623b5UL, 0x839a6488UL, 0x82580ebfUL, 0x801eb0e6UL, 0x81dcdad1UL,
+ 0x8493cc54UL, 0x8551a663UL, 0x8717183aUL, 0x86d5720dUL, 0xa9e2d0a0UL,
+ 0xa820ba97UL, 0xaa6604ceUL, 0xaba46ef9UL, 0xaeeb787cUL, 0xaf29124bUL,
+ 0xad6fac12UL, 0xacadc625UL, 0xa7f18118UL, 0xa633eb2fUL, 0xa4755576UL,
+ 0xa5b73f41UL, 0xa0f829c4UL, 0xa13a43f3UL, 0xa37cfdaaUL, 0xa2be979dUL,
+ 0xb5c473d0UL, 0xb40619e7UL, 0xb640a7beUL, 0xb782cd89UL, 0xb2cddb0cUL,
+ 0xb30fb13bUL, 0xb1490f62UL, 0xb08b6555UL, 0xbbd72268UL, 0xba15485fUL,
+ 0xb853f606UL, 0xb9919c31UL, 0xbcde8ab4UL, 0xbd1ce083UL, 0xbf5a5edaUL,
+ 0xbe9834edUL
+ },
+ {
+ 0x00000000UL, 0xb8bc6765UL, 0xaa09c88bUL, 0x12b5afeeUL, 0x8f629757UL,
+ 0x37def032UL, 0x256b5fdcUL, 0x9dd738b9UL, 0xc5b428efUL, 0x7d084f8aUL,
+ 0x6fbde064UL, 0xd7018701UL, 0x4ad6bfb8UL, 0xf26ad8ddUL, 0xe0df7733UL,
+ 0x58631056UL, 0x5019579fUL, 0xe8a530faUL, 0xfa109f14UL, 0x42acf871UL,
+ 0xdf7bc0c8UL, 0x67c7a7adUL, 0x75720843UL, 0xcdce6f26UL, 0x95ad7f70UL,
+ 0x2d111815UL, 0x3fa4b7fbUL, 0x8718d09eUL, 0x1acfe827UL, 0xa2738f42UL,
+ 0xb0c620acUL, 0x087a47c9UL, 0xa032af3eUL, 0x188ec85bUL, 0x0a3b67b5UL,
+ 0xb28700d0UL, 0x2f503869UL, 0x97ec5f0cUL, 0x8559f0e2UL, 0x3de59787UL,
+ 0x658687d1UL, 0xdd3ae0b4UL, 0xcf8f4f5aUL, 0x7733283fUL, 0xeae41086UL,
+ 0x525877e3UL, 0x40edd80dUL, 0xf851bf68UL, 0xf02bf8a1UL, 0x48979fc4UL,
+ 0x5a22302aUL, 0xe29e574fUL, 0x7f496ff6UL, 0xc7f50893UL, 0xd540a77dUL,
+ 0x6dfcc018UL, 0x359fd04eUL, 0x8d23b72bUL, 0x9f9618c5UL, 0x272a7fa0UL,
+ 0xbafd4719UL, 0x0241207cUL, 0x10f48f92UL, 0xa848e8f7UL, 0x9b14583dUL,
+ 0x23a83f58UL, 0x311d90b6UL, 0x89a1f7d3UL, 0x1476cf6aUL, 0xaccaa80fUL,
+ 0xbe7f07e1UL, 0x06c36084UL, 0x5ea070d2UL, 0xe61c17b7UL, 0xf4a9b859UL,
+ 0x4c15df3cUL, 0xd1c2e785UL, 0x697e80e0UL, 0x7bcb2f0eUL, 0xc377486bUL,
+ 0xcb0d0fa2UL, 0x73b168c7UL, 0x6104c729UL, 0xd9b8a04cUL, 0x446f98f5UL,
+ 0xfcd3ff90UL, 0xee66507eUL, 0x56da371bUL, 0x0eb9274dUL, 0xb6054028UL,
+ 0xa4b0efc6UL, 0x1c0c88a3UL, 0x81dbb01aUL, 0x3967d77fUL, 0x2bd27891UL,
+ 0x936e1ff4UL, 0x3b26f703UL, 0x839a9066UL, 0x912f3f88UL, 0x299358edUL,
+ 0xb4446054UL, 0x0cf80731UL, 0x1e4da8dfUL, 0xa6f1cfbaUL, 0xfe92dfecUL,
+ 0x462eb889UL, 0x549b1767UL, 0xec277002UL, 0x71f048bbUL, 0xc94c2fdeUL,
+ 0xdbf98030UL, 0x6345e755UL, 0x6b3fa09cUL, 0xd383c7f9UL, 0xc1366817UL,
+ 0x798a0f72UL, 0xe45d37cbUL, 0x5ce150aeUL, 0x4e54ff40UL, 0xf6e89825UL,
+ 0xae8b8873UL, 0x1637ef16UL, 0x048240f8UL, 0xbc3e279dUL, 0x21e91f24UL,
+ 0x99557841UL, 0x8be0d7afUL, 0x335cb0caUL, 0xed59b63bUL, 0x55e5d15eUL,
+ 0x47507eb0UL, 0xffec19d5UL, 0x623b216cUL, 0xda874609UL, 0xc832e9e7UL,
+ 0x708e8e82UL, 0x28ed9ed4UL, 0x9051f9b1UL, 0x82e4565fUL, 0x3a58313aUL,
+ 0xa78f0983UL, 0x1f336ee6UL, 0x0d86c108UL, 0xb53aa66dUL, 0xbd40e1a4UL,
+ 0x05fc86c1UL, 0x1749292fUL, 0xaff54e4aUL, 0x322276f3UL, 0x8a9e1196UL,
+ 0x982bbe78UL, 0x2097d91dUL, 0x78f4c94bUL, 0xc048ae2eUL, 0xd2fd01c0UL,
+ 0x6a4166a5UL, 0xf7965e1cUL, 0x4f2a3979UL, 0x5d9f9697UL, 0xe523f1f2UL,
+ 0x4d6b1905UL, 0xf5d77e60UL, 0xe762d18eUL, 0x5fdeb6ebUL, 0xc2098e52UL,
+ 0x7ab5e937UL, 0x680046d9UL, 0xd0bc21bcUL, 0x88df31eaUL, 0x3063568fUL,
+ 0x22d6f961UL, 0x9a6a9e04UL, 0x07bda6bdUL, 0xbf01c1d8UL, 0xadb46e36UL,
+ 0x15080953UL, 0x1d724e9aUL, 0xa5ce29ffUL, 0xb77b8611UL, 0x0fc7e174UL,
+ 0x9210d9cdUL, 0x2aacbea8UL, 0x38191146UL, 0x80a57623UL, 0xd8c66675UL,
+ 0x607a0110UL, 0x72cfaefeUL, 0xca73c99bUL, 0x57a4f122UL, 0xef189647UL,
+ 0xfdad39a9UL, 0x45115eccUL, 0x764dee06UL, 0xcef18963UL, 0xdc44268dUL,
+ 0x64f841e8UL, 0xf92f7951UL, 0x41931e34UL, 0x5326b1daUL, 0xeb9ad6bfUL,
+ 0xb3f9c6e9UL, 0x0b45a18cUL, 0x19f00e62UL, 0xa14c6907UL, 0x3c9b51beUL,
+ 0x842736dbUL, 0x96929935UL, 0x2e2efe50UL, 0x2654b999UL, 0x9ee8defcUL,
+ 0x8c5d7112UL, 0x34e11677UL, 0xa9362eceUL, 0x118a49abUL, 0x033fe645UL,
+ 0xbb838120UL, 0xe3e09176UL, 0x5b5cf613UL, 0x49e959fdUL, 0xf1553e98UL,
+ 0x6c820621UL, 0xd43e6144UL, 0xc68bceaaUL, 0x7e37a9cfUL, 0xd67f4138UL,
+ 0x6ec3265dUL, 0x7c7689b3UL, 0xc4caeed6UL, 0x591dd66fUL, 0xe1a1b10aUL,
+ 0xf3141ee4UL, 0x4ba87981UL, 0x13cb69d7UL, 0xab770eb2UL, 0xb9c2a15cUL,
+ 0x017ec639UL, 0x9ca9fe80UL, 0x241599e5UL, 0x36a0360bUL, 0x8e1c516eUL,
+ 0x866616a7UL, 0x3eda71c2UL, 0x2c6fde2cUL, 0x94d3b949UL, 0x090481f0UL,
+ 0xb1b8e695UL, 0xa30d497bUL, 0x1bb12e1eUL, 0x43d23e48UL, 0xfb6e592dUL,
+ 0xe9dbf6c3UL, 0x516791a6UL, 0xccb0a91fUL, 0x740cce7aUL, 0x66b96194UL,
+ 0xde0506f1UL
+ },
+ {
+ 0x00000000UL, 0x96300777UL, 0x2c610eeeUL, 0xba510999UL, 0x19c46d07UL,
+ 0x8ff46a70UL, 0x35a563e9UL, 0xa395649eUL, 0x3288db0eUL, 0xa4b8dc79UL,
+ 0x1ee9d5e0UL, 0x88d9d297UL, 0x2b4cb609UL, 0xbd7cb17eUL, 0x072db8e7UL,
+ 0x911dbf90UL, 0x6410b71dUL, 0xf220b06aUL, 0x4871b9f3UL, 0xde41be84UL,
+ 0x7dd4da1aUL, 0xebe4dd6dUL, 0x51b5d4f4UL, 0xc785d383UL, 0x56986c13UL,
+ 0xc0a86b64UL, 0x7af962fdUL, 0xecc9658aUL, 0x4f5c0114UL, 0xd96c0663UL,
+ 0x633d0ffaUL, 0xf50d088dUL, 0xc8206e3bUL, 0x5e10694cUL, 0xe44160d5UL,
+ 0x727167a2UL, 0xd1e4033cUL, 0x47d4044bUL, 0xfd850dd2UL, 0x6bb50aa5UL,
+ 0xfaa8b535UL, 0x6c98b242UL, 0xd6c9bbdbUL, 0x40f9bcacUL, 0xe36cd832UL,
+ 0x755cdf45UL, 0xcf0dd6dcUL, 0x593dd1abUL, 0xac30d926UL, 0x3a00de51UL,
+ 0x8051d7c8UL, 0x1661d0bfUL, 0xb5f4b421UL, 0x23c4b356UL, 0x9995bacfUL,
+ 0x0fa5bdb8UL, 0x9eb80228UL, 0x0888055fUL, 0xb2d90cc6UL, 0x24e90bb1UL,
+ 0x877c6f2fUL, 0x114c6858UL, 0xab1d61c1UL, 0x3d2d66b6UL, 0x9041dc76UL,
+ 0x0671db01UL, 0xbc20d298UL, 0x2a10d5efUL, 0x8985b171UL, 0x1fb5b606UL,
+ 0xa5e4bf9fUL, 0x33d4b8e8UL, 0xa2c90778UL, 0x34f9000fUL, 0x8ea80996UL,
+ 0x18980ee1UL, 0xbb0d6a7fUL, 0x2d3d6d08UL, 0x976c6491UL, 0x015c63e6UL,
+ 0xf4516b6bUL, 0x62616c1cUL, 0xd8306585UL, 0x4e0062f2UL, 0xed95066cUL,
+ 0x7ba5011bUL, 0xc1f40882UL, 0x57c40ff5UL, 0xc6d9b065UL, 0x50e9b712UL,
+ 0xeab8be8bUL, 0x7c88b9fcUL, 0xdf1ddd62UL, 0x492dda15UL, 0xf37cd38cUL,
+ 0x654cd4fbUL, 0x5861b24dUL, 0xce51b53aUL, 0x7400bca3UL, 0xe230bbd4UL,
+ 0x41a5df4aUL, 0xd795d83dUL, 0x6dc4d1a4UL, 0xfbf4d6d3UL, 0x6ae96943UL,
+ 0xfcd96e34UL, 0x468867adUL, 0xd0b860daUL, 0x732d0444UL, 0xe51d0333UL,
+ 0x5f4c0aaaUL, 0xc97c0dddUL, 0x3c710550UL, 0xaa410227UL, 0x10100bbeUL,
+ 0x86200cc9UL, 0x25b56857UL, 0xb3856f20UL, 0x09d466b9UL, 0x9fe461ceUL,
+ 0x0ef9de5eUL, 0x98c9d929UL, 0x2298d0b0UL, 0xb4a8d7c7UL, 0x173db359UL,
+ 0x810db42eUL, 0x3b5cbdb7UL, 0xad6cbac0UL, 0x2083b8edUL, 0xb6b3bf9aUL,
+ 0x0ce2b603UL, 0x9ad2b174UL, 0x3947d5eaUL, 0xaf77d29dUL, 0x1526db04UL,
+ 0x8316dc73UL, 0x120b63e3UL, 0x843b6494UL, 0x3e6a6d0dUL, 0xa85a6a7aUL,
+ 0x0bcf0ee4UL, 0x9dff0993UL, 0x27ae000aUL, 0xb19e077dUL, 0x44930ff0UL,
+ 0xd2a30887UL, 0x68f2011eUL, 0xfec20669UL, 0x5d5762f7UL, 0xcb676580UL,
+ 0x71366c19UL, 0xe7066b6eUL, 0x761bd4feUL, 0xe02bd389UL, 0x5a7ada10UL,
+ 0xcc4add67UL, 0x6fdfb9f9UL, 0xf9efbe8eUL, 0x43beb717UL, 0xd58eb060UL,
+ 0xe8a3d6d6UL, 0x7e93d1a1UL, 0xc4c2d838UL, 0x52f2df4fUL, 0xf167bbd1UL,
+ 0x6757bca6UL, 0xdd06b53fUL, 0x4b36b248UL, 0xda2b0dd8UL, 0x4c1b0aafUL,
+ 0xf64a0336UL, 0x607a0441UL, 0xc3ef60dfUL, 0x55df67a8UL, 0xef8e6e31UL,
+ 0x79be6946UL, 0x8cb361cbUL, 0x1a8366bcUL, 0xa0d26f25UL, 0x36e26852UL,
+ 0x95770cccUL, 0x03470bbbUL, 0xb9160222UL, 0x2f260555UL, 0xbe3bbac5UL,
+ 0x280bbdb2UL, 0x925ab42bUL, 0x046ab35cUL, 0xa7ffd7c2UL, 0x31cfd0b5UL,
+ 0x8b9ed92cUL, 0x1daede5bUL, 0xb0c2649bUL, 0x26f263ecUL, 0x9ca36a75UL,
+ 0x0a936d02UL, 0xa906099cUL, 0x3f360eebUL, 0x85670772UL, 0x13570005UL,
+ 0x824abf95UL, 0x147ab8e2UL, 0xae2bb17bUL, 0x381bb60cUL, 0x9b8ed292UL,
+ 0x0dbed5e5UL, 0xb7efdc7cUL, 0x21dfdb0bUL, 0xd4d2d386UL, 0x42e2d4f1UL,
+ 0xf8b3dd68UL, 0x6e83da1fUL, 0xcd16be81UL, 0x5b26b9f6UL, 0xe177b06fUL,
+ 0x7747b718UL, 0xe65a0888UL, 0x706a0fffUL, 0xca3b0666UL, 0x5c0b0111UL,
+ 0xff9e658fUL, 0x69ae62f8UL, 0xd3ff6b61UL, 0x45cf6c16UL, 0x78e20aa0UL,
+ 0xeed20dd7UL, 0x5483044eUL, 0xc2b30339UL, 0x612667a7UL, 0xf71660d0UL,
+ 0x4d476949UL, 0xdb776e3eUL, 0x4a6ad1aeUL, 0xdc5ad6d9UL, 0x660bdf40UL,
+ 0xf03bd837UL, 0x53aebca9UL, 0xc59ebbdeUL, 0x7fcfb247UL, 0xe9ffb530UL,
+ 0x1cf2bdbdUL, 0x8ac2bacaUL, 0x3093b353UL, 0xa6a3b424UL, 0x0536d0baUL,
+ 0x9306d7cdUL, 0x2957de54UL, 0xbf67d923UL, 0x2e7a66b3UL, 0xb84a61c4UL,
+ 0x021b685dUL, 0x942b6f2aUL, 0x37be0bb4UL, 0xa18e0cc3UL, 0x1bdf055aUL,
+ 0x8def022dUL
+ },
+ {
+ 0x00000000UL, 0x41311b19UL, 0x82623632UL, 0xc3532d2bUL, 0x04c56c64UL,
+ 0x45f4777dUL, 0x86a75a56UL, 0xc796414fUL, 0x088ad9c8UL, 0x49bbc2d1UL,
+ 0x8ae8effaUL, 0xcbd9f4e3UL, 0x0c4fb5acUL, 0x4d7eaeb5UL, 0x8e2d839eUL,
+ 0xcf1c9887UL, 0x5112c24aUL, 0x1023d953UL, 0xd370f478UL, 0x9241ef61UL,
+ 0x55d7ae2eUL, 0x14e6b537UL, 0xd7b5981cUL, 0x96848305UL, 0x59981b82UL,
+ 0x18a9009bUL, 0xdbfa2db0UL, 0x9acb36a9UL, 0x5d5d77e6UL, 0x1c6c6cffUL,
+ 0xdf3f41d4UL, 0x9e0e5acdUL, 0xa2248495UL, 0xe3159f8cUL, 0x2046b2a7UL,
+ 0x6177a9beUL, 0xa6e1e8f1UL, 0xe7d0f3e8UL, 0x2483dec3UL, 0x65b2c5daUL,
+ 0xaaae5d5dUL, 0xeb9f4644UL, 0x28cc6b6fUL, 0x69fd7076UL, 0xae6b3139UL,
+ 0xef5a2a20UL, 0x2c09070bUL, 0x6d381c12UL, 0xf33646dfUL, 0xb2075dc6UL,
+ 0x715470edUL, 0x30656bf4UL, 0xf7f32abbUL, 0xb6c231a2UL, 0x75911c89UL,
+ 0x34a00790UL, 0xfbbc9f17UL, 0xba8d840eUL, 0x79dea925UL, 0x38efb23cUL,
+ 0xff79f373UL, 0xbe48e86aUL, 0x7d1bc541UL, 0x3c2ade58UL, 0x054f79f0UL,
+ 0x447e62e9UL, 0x872d4fc2UL, 0xc61c54dbUL, 0x018a1594UL, 0x40bb0e8dUL,
+ 0x83e823a6UL, 0xc2d938bfUL, 0x0dc5a038UL, 0x4cf4bb21UL, 0x8fa7960aUL,
+ 0xce968d13UL, 0x0900cc5cUL, 0x4831d745UL, 0x8b62fa6eUL, 0xca53e177UL,
+ 0x545dbbbaUL, 0x156ca0a3UL, 0xd63f8d88UL, 0x970e9691UL, 0x5098d7deUL,
+ 0x11a9ccc7UL, 0xd2fae1ecUL, 0x93cbfaf5UL, 0x5cd76272UL, 0x1de6796bUL,
+ 0xdeb55440UL, 0x9f844f59UL, 0x58120e16UL, 0x1923150fUL, 0xda703824UL,
+ 0x9b41233dUL, 0xa76bfd65UL, 0xe65ae67cUL, 0x2509cb57UL, 0x6438d04eUL,
+ 0xa3ae9101UL, 0xe29f8a18UL, 0x21cca733UL, 0x60fdbc2aUL, 0xafe124adUL,
+ 0xeed03fb4UL, 0x2d83129fUL, 0x6cb20986UL, 0xab2448c9UL, 0xea1553d0UL,
+ 0x29467efbUL, 0x687765e2UL, 0xf6793f2fUL, 0xb7482436UL, 0x741b091dUL,
+ 0x352a1204UL, 0xf2bc534bUL, 0xb38d4852UL, 0x70de6579UL, 0x31ef7e60UL,
+ 0xfef3e6e7UL, 0xbfc2fdfeUL, 0x7c91d0d5UL, 0x3da0cbccUL, 0xfa368a83UL,
+ 0xbb07919aUL, 0x7854bcb1UL, 0x3965a7a8UL, 0x4b98833bUL, 0x0aa99822UL,
+ 0xc9fab509UL, 0x88cbae10UL, 0x4f5def5fUL, 0x0e6cf446UL, 0xcd3fd96dUL,
+ 0x8c0ec274UL, 0x43125af3UL, 0x022341eaUL, 0xc1706cc1UL, 0x804177d8UL,
+ 0x47d73697UL, 0x06e62d8eUL, 0xc5b500a5UL, 0x84841bbcUL, 0x1a8a4171UL,
+ 0x5bbb5a68UL, 0x98e87743UL, 0xd9d96c5aUL, 0x1e4f2d15UL, 0x5f7e360cUL,
+ 0x9c2d1b27UL, 0xdd1c003eUL, 0x120098b9UL, 0x533183a0UL, 0x9062ae8bUL,
+ 0xd153b592UL, 0x16c5f4ddUL, 0x57f4efc4UL, 0x94a7c2efUL, 0xd596d9f6UL,
+ 0xe9bc07aeUL, 0xa88d1cb7UL, 0x6bde319cUL, 0x2aef2a85UL, 0xed796bcaUL,
+ 0xac4870d3UL, 0x6f1b5df8UL, 0x2e2a46e1UL, 0xe136de66UL, 0xa007c57fUL,
+ 0x6354e854UL, 0x2265f34dUL, 0xe5f3b202UL, 0xa4c2a91bUL, 0x67918430UL,
+ 0x26a09f29UL, 0xb8aec5e4UL, 0xf99fdefdUL, 0x3accf3d6UL, 0x7bfde8cfUL,
+ 0xbc6ba980UL, 0xfd5ab299UL, 0x3e099fb2UL, 0x7f3884abUL, 0xb0241c2cUL,
+ 0xf1150735UL, 0x32462a1eUL, 0x73773107UL, 0xb4e17048UL, 0xf5d06b51UL,
+ 0x3683467aUL, 0x77b25d63UL, 0x4ed7facbUL, 0x0fe6e1d2UL, 0xccb5ccf9UL,
+ 0x8d84d7e0UL, 0x4a1296afUL, 0x0b238db6UL, 0xc870a09dUL, 0x8941bb84UL,
+ 0x465d2303UL, 0x076c381aUL, 0xc43f1531UL, 0x850e0e28UL, 0x42984f67UL,
+ 0x03a9547eUL, 0xc0fa7955UL, 0x81cb624cUL, 0x1fc53881UL, 0x5ef42398UL,
+ 0x9da70eb3UL, 0xdc9615aaUL, 0x1b0054e5UL, 0x5a314ffcUL, 0x996262d7UL,
+ 0xd85379ceUL, 0x174fe149UL, 0x567efa50UL, 0x952dd77bUL, 0xd41ccc62UL,
+ 0x138a8d2dUL, 0x52bb9634UL, 0x91e8bb1fUL, 0xd0d9a006UL, 0xecf37e5eUL,
+ 0xadc26547UL, 0x6e91486cUL, 0x2fa05375UL, 0xe836123aUL, 0xa9070923UL,
+ 0x6a542408UL, 0x2b653f11UL, 0xe479a796UL, 0xa548bc8fUL, 0x661b91a4UL,
+ 0x272a8abdUL, 0xe0bccbf2UL, 0xa18dd0ebUL, 0x62defdc0UL, 0x23efe6d9UL,
+ 0xbde1bc14UL, 0xfcd0a70dUL, 0x3f838a26UL, 0x7eb2913fUL, 0xb924d070UL,
+ 0xf815cb69UL, 0x3b46e642UL, 0x7a77fd5bUL, 0xb56b65dcUL, 0xf45a7ec5UL,
+ 0x370953eeUL, 0x763848f7UL, 0xb1ae09b8UL, 0xf09f12a1UL, 0x33cc3f8aUL,
+ 0x72fd2493UL
+ },
+ {
+ 0x00000000UL, 0x376ac201UL, 0x6ed48403UL, 0x59be4602UL, 0xdca80907UL,
+ 0xebc2cb06UL, 0xb27c8d04UL, 0x85164f05UL, 0xb851130eUL, 0x8f3bd10fUL,
+ 0xd685970dUL, 0xe1ef550cUL, 0x64f91a09UL, 0x5393d808UL, 0x0a2d9e0aUL,
+ 0x3d475c0bUL, 0x70a3261cUL, 0x47c9e41dUL, 0x1e77a21fUL, 0x291d601eUL,
+ 0xac0b2f1bUL, 0x9b61ed1aUL, 0xc2dfab18UL, 0xf5b56919UL, 0xc8f23512UL,
+ 0xff98f713UL, 0xa626b111UL, 0x914c7310UL, 0x145a3c15UL, 0x2330fe14UL,
+ 0x7a8eb816UL, 0x4de47a17UL, 0xe0464d38UL, 0xd72c8f39UL, 0x8e92c93bUL,
+ 0xb9f80b3aUL, 0x3cee443fUL, 0x0b84863eUL, 0x523ac03cUL, 0x6550023dUL,
+ 0x58175e36UL, 0x6f7d9c37UL, 0x36c3da35UL, 0x01a91834UL, 0x84bf5731UL,
+ 0xb3d59530UL, 0xea6bd332UL, 0xdd011133UL, 0x90e56b24UL, 0xa78fa925UL,
+ 0xfe31ef27UL, 0xc95b2d26UL, 0x4c4d6223UL, 0x7b27a022UL, 0x2299e620UL,
+ 0x15f32421UL, 0x28b4782aUL, 0x1fdeba2bUL, 0x4660fc29UL, 0x710a3e28UL,
+ 0xf41c712dUL, 0xc376b32cUL, 0x9ac8f52eUL, 0xada2372fUL, 0xc08d9a70UL,
+ 0xf7e75871UL, 0xae591e73UL, 0x9933dc72UL, 0x1c259377UL, 0x2b4f5176UL,
+ 0x72f11774UL, 0x459bd575UL, 0x78dc897eUL, 0x4fb64b7fUL, 0x16080d7dUL,
+ 0x2162cf7cUL, 0xa4748079UL, 0x931e4278UL, 0xcaa0047aUL, 0xfdcac67bUL,
+ 0xb02ebc6cUL, 0x87447e6dUL, 0xdefa386fUL, 0xe990fa6eUL, 0x6c86b56bUL,
+ 0x5bec776aUL, 0x02523168UL, 0x3538f369UL, 0x087faf62UL, 0x3f156d63UL,
+ 0x66ab2b61UL, 0x51c1e960UL, 0xd4d7a665UL, 0xe3bd6464UL, 0xba032266UL,
+ 0x8d69e067UL, 0x20cbd748UL, 0x17a11549UL, 0x4e1f534bUL, 0x7975914aUL,
+ 0xfc63de4fUL, 0xcb091c4eUL, 0x92b75a4cUL, 0xa5dd984dUL, 0x989ac446UL,
+ 0xaff00647UL, 0xf64e4045UL, 0xc1248244UL, 0x4432cd41UL, 0x73580f40UL,
+ 0x2ae64942UL, 0x1d8c8b43UL, 0x5068f154UL, 0x67023355UL, 0x3ebc7557UL,
+ 0x09d6b756UL, 0x8cc0f853UL, 0xbbaa3a52UL, 0xe2147c50UL, 0xd57ebe51UL,
+ 0xe839e25aUL, 0xdf53205bUL, 0x86ed6659UL, 0xb187a458UL, 0x3491eb5dUL,
+ 0x03fb295cUL, 0x5a456f5eUL, 0x6d2fad5fUL, 0x801b35e1UL, 0xb771f7e0UL,
+ 0xeecfb1e2UL, 0xd9a573e3UL, 0x5cb33ce6UL, 0x6bd9fee7UL, 0x3267b8e5UL,
+ 0x050d7ae4UL, 0x384a26efUL, 0x0f20e4eeUL, 0x569ea2ecUL, 0x61f460edUL,
+ 0xe4e22fe8UL, 0xd388ede9UL, 0x8a36abebUL, 0xbd5c69eaUL, 0xf0b813fdUL,
+ 0xc7d2d1fcUL, 0x9e6c97feUL, 0xa90655ffUL, 0x2c101afaUL, 0x1b7ad8fbUL,
+ 0x42c49ef9UL, 0x75ae5cf8UL, 0x48e900f3UL, 0x7f83c2f2UL, 0x263d84f0UL,
+ 0x115746f1UL, 0x944109f4UL, 0xa32bcbf5UL, 0xfa958df7UL, 0xcdff4ff6UL,
+ 0x605d78d9UL, 0x5737bad8UL, 0x0e89fcdaUL, 0x39e33edbUL, 0xbcf571deUL,
+ 0x8b9fb3dfUL, 0xd221f5ddUL, 0xe54b37dcUL, 0xd80c6bd7UL, 0xef66a9d6UL,
+ 0xb6d8efd4UL, 0x81b22dd5UL, 0x04a462d0UL, 0x33cea0d1UL, 0x6a70e6d3UL,
+ 0x5d1a24d2UL, 0x10fe5ec5UL, 0x27949cc4UL, 0x7e2adac6UL, 0x494018c7UL,
+ 0xcc5657c2UL, 0xfb3c95c3UL, 0xa282d3c1UL, 0x95e811c0UL, 0xa8af4dcbUL,
+ 0x9fc58fcaUL, 0xc67bc9c8UL, 0xf1110bc9UL, 0x740744ccUL, 0x436d86cdUL,
+ 0x1ad3c0cfUL, 0x2db902ceUL, 0x4096af91UL, 0x77fc6d90UL, 0x2e422b92UL,
+ 0x1928e993UL, 0x9c3ea696UL, 0xab546497UL, 0xf2ea2295UL, 0xc580e094UL,
+ 0xf8c7bc9fUL, 0xcfad7e9eUL, 0x9613389cUL, 0xa179fa9dUL, 0x246fb598UL,
+ 0x13057799UL, 0x4abb319bUL, 0x7dd1f39aUL, 0x3035898dUL, 0x075f4b8cUL,
+ 0x5ee10d8eUL, 0x698bcf8fUL, 0xec9d808aUL, 0xdbf7428bUL, 0x82490489UL,
+ 0xb523c688UL, 0x88649a83UL, 0xbf0e5882UL, 0xe6b01e80UL, 0xd1dadc81UL,
+ 0x54cc9384UL, 0x63a65185UL, 0x3a181787UL, 0x0d72d586UL, 0xa0d0e2a9UL,
+ 0x97ba20a8UL, 0xce0466aaUL, 0xf96ea4abUL, 0x7c78ebaeUL, 0x4b1229afUL,
+ 0x12ac6fadUL, 0x25c6adacUL, 0x1881f1a7UL, 0x2feb33a6UL, 0x765575a4UL,
+ 0x413fb7a5UL, 0xc429f8a0UL, 0xf3433aa1UL, 0xaafd7ca3UL, 0x9d97bea2UL,
+ 0xd073c4b5UL, 0xe71906b4UL, 0xbea740b6UL, 0x89cd82b7UL, 0x0cdbcdb2UL,
+ 0x3bb10fb3UL, 0x620f49b1UL, 0x55658bb0UL, 0x6822d7bbUL, 0x5f4815baUL,
+ 0x06f653b8UL, 0x319c91b9UL, 0xb48adebcUL, 0x83e01cbdUL, 0xda5e5abfUL,
+ 0xed3498beUL
+ },
+ {
+ 0x00000000UL, 0x6567bcb8UL, 0x8bc809aaUL, 0xeeafb512UL, 0x5797628fUL,
+ 0x32f0de37UL, 0xdc5f6b25UL, 0xb938d79dUL, 0xef28b4c5UL, 0x8a4f087dUL,
+ 0x64e0bd6fUL, 0x018701d7UL, 0xb8bfd64aUL, 0xddd86af2UL, 0x3377dfe0UL,
+ 0x56106358UL, 0x9f571950UL, 0xfa30a5e8UL, 0x149f10faUL, 0x71f8ac42UL,
+ 0xc8c07bdfUL, 0xada7c767UL, 0x43087275UL, 0x266fcecdUL, 0x707fad95UL,
+ 0x1518112dUL, 0xfbb7a43fUL, 0x9ed01887UL, 0x27e8cf1aUL, 0x428f73a2UL,
+ 0xac20c6b0UL, 0xc9477a08UL, 0x3eaf32a0UL, 0x5bc88e18UL, 0xb5673b0aUL,
+ 0xd00087b2UL, 0x6938502fUL, 0x0c5fec97UL, 0xe2f05985UL, 0x8797e53dUL,
+ 0xd1878665UL, 0xb4e03addUL, 0x5a4f8fcfUL, 0x3f283377UL, 0x8610e4eaUL,
+ 0xe3775852UL, 0x0dd8ed40UL, 0x68bf51f8UL, 0xa1f82bf0UL, 0xc49f9748UL,
+ 0x2a30225aUL, 0x4f579ee2UL, 0xf66f497fUL, 0x9308f5c7UL, 0x7da740d5UL,
+ 0x18c0fc6dUL, 0x4ed09f35UL, 0x2bb7238dUL, 0xc518969fUL, 0xa07f2a27UL,
+ 0x1947fdbaUL, 0x7c204102UL, 0x928ff410UL, 0xf7e848a8UL, 0x3d58149bUL,
+ 0x583fa823UL, 0xb6901d31UL, 0xd3f7a189UL, 0x6acf7614UL, 0x0fa8caacUL,
+ 0xe1077fbeUL, 0x8460c306UL, 0xd270a05eUL, 0xb7171ce6UL, 0x59b8a9f4UL,
+ 0x3cdf154cUL, 0x85e7c2d1UL, 0xe0807e69UL, 0x0e2fcb7bUL, 0x6b4877c3UL,
+ 0xa20f0dcbUL, 0xc768b173UL, 0x29c70461UL, 0x4ca0b8d9UL, 0xf5986f44UL,
+ 0x90ffd3fcUL, 0x7e5066eeUL, 0x1b37da56UL, 0x4d27b90eUL, 0x284005b6UL,
+ 0xc6efb0a4UL, 0xa3880c1cUL, 0x1ab0db81UL, 0x7fd76739UL, 0x9178d22bUL,
+ 0xf41f6e93UL, 0x03f7263bUL, 0x66909a83UL, 0x883f2f91UL, 0xed589329UL,
+ 0x546044b4UL, 0x3107f80cUL, 0xdfa84d1eUL, 0xbacff1a6UL, 0xecdf92feUL,
+ 0x89b82e46UL, 0x67179b54UL, 0x027027ecUL, 0xbb48f071UL, 0xde2f4cc9UL,
+ 0x3080f9dbUL, 0x55e74563UL, 0x9ca03f6bUL, 0xf9c783d3UL, 0x176836c1UL,
+ 0x720f8a79UL, 0xcb375de4UL, 0xae50e15cUL, 0x40ff544eUL, 0x2598e8f6UL,
+ 0x73888baeUL, 0x16ef3716UL, 0xf8408204UL, 0x9d273ebcUL, 0x241fe921UL,
+ 0x41785599UL, 0xafd7e08bUL, 0xcab05c33UL, 0x3bb659edUL, 0x5ed1e555UL,
+ 0xb07e5047UL, 0xd519ecffUL, 0x6c213b62UL, 0x094687daUL, 0xe7e932c8UL,
+ 0x828e8e70UL, 0xd49eed28UL, 0xb1f95190UL, 0x5f56e482UL, 0x3a31583aUL,
+ 0x83098fa7UL, 0xe66e331fUL, 0x08c1860dUL, 0x6da63ab5UL, 0xa4e140bdUL,
+ 0xc186fc05UL, 0x2f294917UL, 0x4a4ef5afUL, 0xf3762232UL, 0x96119e8aUL,
+ 0x78be2b98UL, 0x1dd99720UL, 0x4bc9f478UL, 0x2eae48c0UL, 0xc001fdd2UL,
+ 0xa566416aUL, 0x1c5e96f7UL, 0x79392a4fUL, 0x97969f5dUL, 0xf2f123e5UL,
+ 0x05196b4dUL, 0x607ed7f5UL, 0x8ed162e7UL, 0xebb6de5fUL, 0x528e09c2UL,
+ 0x37e9b57aUL, 0xd9460068UL, 0xbc21bcd0UL, 0xea31df88UL, 0x8f566330UL,
+ 0x61f9d622UL, 0x049e6a9aUL, 0xbda6bd07UL, 0xd8c101bfUL, 0x366eb4adUL,
+ 0x53090815UL, 0x9a4e721dUL, 0xff29cea5UL, 0x11867bb7UL, 0x74e1c70fUL,
+ 0xcdd91092UL, 0xa8beac2aUL, 0x46111938UL, 0x2376a580UL, 0x7566c6d8UL,
+ 0x10017a60UL, 0xfeaecf72UL, 0x9bc973caUL, 0x22f1a457UL, 0x479618efUL,
+ 0xa939adfdUL, 0xcc5e1145UL, 0x06ee4d76UL, 0x6389f1ceUL, 0x8d2644dcUL,
+ 0xe841f864UL, 0x51792ff9UL, 0x341e9341UL, 0xdab12653UL, 0xbfd69aebUL,
+ 0xe9c6f9b3UL, 0x8ca1450bUL, 0x620ef019UL, 0x07694ca1UL, 0xbe519b3cUL,
+ 0xdb362784UL, 0x35999296UL, 0x50fe2e2eUL, 0x99b95426UL, 0xfcdee89eUL,
+ 0x12715d8cUL, 0x7716e134UL, 0xce2e36a9UL, 0xab498a11UL, 0x45e63f03UL,
+ 0x208183bbUL, 0x7691e0e3UL, 0x13f65c5bUL, 0xfd59e949UL, 0x983e55f1UL,
+ 0x2106826cUL, 0x44613ed4UL, 0xaace8bc6UL, 0xcfa9377eUL, 0x38417fd6UL,
+ 0x5d26c36eUL, 0xb389767cUL, 0xd6eecac4UL, 0x6fd61d59UL, 0x0ab1a1e1UL,
+ 0xe41e14f3UL, 0x8179a84bUL, 0xd769cb13UL, 0xb20e77abUL, 0x5ca1c2b9UL,
+ 0x39c67e01UL, 0x80fea99cUL, 0xe5991524UL, 0x0b36a036UL, 0x6e511c8eUL,
+ 0xa7166686UL, 0xc271da3eUL, 0x2cde6f2cUL, 0x49b9d394UL, 0xf0810409UL,
+ 0x95e6b8b1UL, 0x7b490da3UL, 0x1e2eb11bUL, 0x483ed243UL, 0x2d596efbUL,
+ 0xc3f6dbe9UL, 0xa6916751UL, 0x1fa9b0ccUL, 0x7ace0c74UL, 0x9461b966UL,
+ 0xf10605deUL
+#endif
+ }
+};
diff --git a/src/zlib/deflate.c b/src/zlib/deflate.c
new file mode 100755
index 0000000..ff0581d
--- /dev/null
+++ b/src/zlib/deflate.c
@@ -0,0 +1,1502 @@
+/* deflate.c -- compress data using the deflation algorithm
+ * Copyright (C) 1995-2003 Jean-loup Gailly.
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/*
+ * ALGORITHM
+ *
+ * The "deflation" process depends on being able to identify portions
+ * of the input text which are identical to earlier input (within a
+ * sliding window trailing behind the input currently being processed).
+ *
+ * The most straightforward technique turns out to be the fastest for
+ * most input files: try all possible matches and select the longest.
+ * The key feature of this algorithm is that insertions into the string
+ * dictionary are very simple and thus fast, and deletions are avoided
+ * completely. Insertions are performed at each input character, whereas
+ * string matches are performed only when the previous match ends. So it
+ * is preferable to spend more time in matches to allow very fast string
+ * insertions and avoid deletions. The matching algorithm for small
+ * strings is inspired from that of Rabin & Karp. A brute force approach
+ * is used to find longer strings when a small match has been found.
+ * A similar algorithm is used in comic (by Jan-Mark Wams) and freeze
+ * (by Leonid Broukhis).
+ * A previous version of this file used a more sophisticated algorithm
+ * (by Fiala and Greene) which is guaranteed to run in linear amortized
+ * time, but has a larger average cost, uses more memory and is patented.
+ * However the F&G algorithm may be faster for some highly redundant
+ * files if the parameter max_chain_length (described below) is too large.
+ *
+ * ACKNOWLEDGEMENTS
+ *
+ * The idea of lazy evaluation of matches is due to Jan-Mark Wams, and
+ * I found it in 'freeze' written by Leonid Broukhis.
+ * Thanks to many people for bug reports and testing.
+ *
+ * REFERENCES
+ *
+ * Deutsch, L.P.,"DEFLATE Compressed Data Format Specification".
+ * Available in http://www.ietf.org/rfc/rfc1951.txt
+ *
+ * A description of the Rabin and Karp algorithm is given in the book
+ * "Algorithms" by R. Sedgewick, Addison-Wesley, p252.
+ *
+ * Fiala,E.R., and Greene,D.H.
+ * Data Compression with Finite Windows, Comm.ACM, 32,4 (1989) 490-595
+ *
+ */
+
+/* @(#) $ Id$ */
+
+#include "deflate.h"
+
+const char deflate_copyright[] =
+ " deflate 1.2.1 Copyright 1995-2003 Jean-loup Gailly ";
+/*
+ If you use the zlib library in a product, an acknowledgment is welcome
+ in the documentation of your product. If for some reason you cannot
+ include such an acknowledgment, I would appreciate that you keep this
+ copyright string in the executable of your product.
+ */
+
+/* ===========================================================================
+ * Function prototypes.
+ */
+typedef enum {
+ need_more, /* block not completed, need more input or more output */
+ block_done, /* block flush performed */
+ finish_started, /* finish started, need only more output at next deflate */
+ finish_done /* finish done, accept no more input or output */
+} block_state;
+
+typedef block_state (*compress_func) OF((deflate_state *s, int flush));
+/* Compression function. Returns the block state after the call. */
+
+local void fill_window OF((deflate_state *s));
+local block_state deflate_stored OF((deflate_state *s, int flush));
+local block_state deflate_fast OF((deflate_state *s, int flush));
+#ifndef FASTEST
+local block_state deflate_slow OF((deflate_state *s, int flush));
+#endif
+local void lm_init OF((deflate_state *s));
+local void putShortMSB OF((deflate_state *s, uInt b));
+local void flush_pending OF((z_streamp strm));
+local int read_buf OF((z_streamp strm, Bytef *buf, unsigned size));
+#ifndef FASTEST
+#ifdef ASMV
+ void match_init OF((void)); /* asm code initialization */
+ uInt longest_match OF((deflate_state *s, IPos cur_match));
+#else
+local uInt longest_match OF((deflate_state *s, IPos cur_match));
+#endif
+#endif
+local uInt longest_match_fast OF((deflate_state *s, IPos cur_match));
+
+#ifdef DEBUG
+local void check_match OF((deflate_state *s, IPos start, IPos match,
+ int length));
+#endif
+
+/* ===========================================================================
+ * Local data
+ */
+
+#define NIL 0
+/* Tail of hash chains */
+
+#ifndef TOO_FAR
+# define TOO_FAR 4096
+#endif
+/* Matches of length 3 are discarded if their distance exceeds TOO_FAR */
+
+#define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1)
+/* Minimum amount of lookahead, except at the end of the input file.
+ * See deflate.c for comments about the MIN_MATCH+1.
+ */
+
+/* Values for max_lazy_match, good_match and max_chain_length, depending on
+ * the desired pack level (0..9). The values given below have been tuned to
+ * exclude worst case performance for pathological files. Better values may be
+ * found for specific files.
+ */
+typedef struct config_s {
+ ush good_length; /* reduce lazy search above this match length */
+ ush max_lazy; /* do not perform lazy search above this match length */
+ ush nice_length; /* quit search above this match length */
+ ush max_chain;
+ compress_func func;
+} config;
+
+#ifdef FASTEST
+local const config configuration_table[2] = {
+/* good lazy nice chain */
+/* 0 */ {0, 0, 0, 0, deflate_stored}, /* store only */
+/* 1 */ {4, 4, 8, 4, deflate_fast}}; /* max speed, no lazy matches */
+#else
+local const config configuration_table[10] = {
+/* good lazy nice chain */
+/* 0 */ {0, 0, 0, 0, deflate_stored}, /* store only */
+/* 1 */ {4, 4, 8, 4, deflate_fast}, /* max speed, no lazy matches */
+/* 2 */ {4, 5, 16, 8, deflate_fast},
+/* 3 */ {4, 6, 32, 32, deflate_fast},
+
+/* 4 */ {4, 4, 16, 16, deflate_slow}, /* lazy matches */
+/* 5 */ {8, 16, 32, 32, deflate_slow},
+/* 6 */ {8, 16, 128, 128, deflate_slow},
+/* 7 */ {8, 32, 128, 256, deflate_slow},
+/* 8 */ {32, 128, 258, 1024, deflate_slow},
+/* 9 */ {32, 258, 258, 4096, deflate_slow}}; /* max compression */
+#endif
+
+/* Note: the deflate() code requires max_lazy >= MIN_MATCH and max_chain >= 4
+ * For deflate_fast() (levels <= 3) good is ignored and lazy has a different
+ * meaning.
+ */
+
+#define EQUAL 0
+/* result of memcmp for equal strings */
+
+#ifndef NO_DUMMY_DECL
+struct static_tree_desc_s {int dummy;}; /* for buggy compilers */
+#endif
+
+/* ===========================================================================
+ * Update a hash value with the given input byte
+ * IN assertion: all calls to to UPDATE_HASH are made with consecutive
+ * input characters, so that a running hash key can be computed from the
+ * previous key instead of complete recalculation each time.
+ */
+#define UPDATE_HASH(s,h,c) (h = (((h)<<s->hash_shift) ^ (c)) & s->hash_mask)
+
+
+/* ===========================================================================
+ * Insert string str in the dictionary and set match_head to the previous head
+ * of the hash chain (the most recent string with same hash key). Return
+ * the previous length of the hash chain.
+ * If this file is compiled with -DFASTEST, the compression level is forced
+ * to 1, and no hash chains are maintained.
+ * IN assertion: all calls to to INSERT_STRING are made with consecutive
+ * input characters and the first MIN_MATCH bytes of str are valid
+ * (except for the last MIN_MATCH-1 bytes of the input file).
+ */
+#ifdef FASTEST
+#define INSERT_STRING(s, str, match_head) \
+ (UPDATE_HASH(s, s->ins_h, s->window[(str) + (MIN_MATCH-1)]), \
+ match_head = s->head[s->ins_h], \
+ s->head[s->ins_h] = (Pos)(str))
+#else
+#define INSERT_STRING(s, str, match_head) \
+ (UPDATE_HASH(s, s->ins_h, s->window[(str) + (MIN_MATCH-1)]), \
+ match_head = s->prev[(str) & s->w_mask] = s->head[s->ins_h], \
+ s->head[s->ins_h] = (Pos)(str))
+#endif
+
+/* ===========================================================================
+ * Initialize the hash table (avoiding 64K overflow for 16 bit systems).
+ * prev[] will be initialized on the fly.
+ */
+#define CLEAR_HASH(s) \
+ s->head[s->hash_size-1] = NIL; \
+ zmemzero((Bytef *)s->head, (unsigned)(s->hash_size-1)*sizeof(*s->head));
+
+/* ========================================================================= */
+int ZEXPORT deflateInit_(strm, level, version, stream_size)
+ z_streamp strm;
+ int level;
+ const char *version;
+ int stream_size;
+{
+ return deflateInit2_(strm, level, Z_DEFLATED, MAX_WBITS, DEF_MEM_LEVEL,
+ Z_DEFAULT_STRATEGY, version, stream_size);
+ /* To do: ignore strm->next_in if we use it as window */
+}
+
+/* ========================================================================= */
+int ZEXPORT deflateInit2_(strm, level, method, windowBits, memLevel, strategy,
+ version, stream_size)
+ z_streamp strm;
+ int level;
+ int method;
+ int windowBits;
+ int memLevel;
+ int strategy;
+ const char *version;
+ int stream_size;
+{
+ deflate_state *s;
+ int wrap = 1;
+ static const char my_version[] = ZLIB_VERSION;
+
+ ushf *overlay;
+ /* We overlay pending_buf and d_buf+l_buf. This works since the average
+ * output size for (length,distance) codes is <= 24 bits.
+ */
+
+ if (version == Z_NULL || version[0] != my_version[0] ||
+ stream_size != sizeof(z_stream)) {
+ return Z_VERSION_ERROR;
+ }
+ if (strm == Z_NULL) return Z_STREAM_ERROR;
+
+ strm->msg = Z_NULL;
+ if (strm->zalloc == (alloc_func)0) {
+ strm->zalloc = zcalloc;
+ strm->opaque = (voidpf)0;
+ }
+ if (strm->zfree == (free_func)0) strm->zfree = zcfree;
+
+#ifdef FASTEST
+ if (level != 0) level = 1;
+#else
+ if (level == Z_DEFAULT_COMPRESSION) level = 6;
+#endif
+
+ if (windowBits < 0) { /* suppress zlib wrapper */
+ wrap = 0;
+ windowBits = -windowBits;
+ }
+#ifdef GZIP
+ else if (windowBits > 15) {
+ wrap = 2; /* write gzip wrapper instead */
+ windowBits -= 16;
+ }
+#endif
+ if (memLevel < 1 || memLevel > MAX_MEM_LEVEL || method != Z_DEFLATED ||
+ windowBits < 8 || windowBits > 15 || level < 0 || level > 9 ||
+ strategy < 0 || strategy > Z_RLE) {
+ return Z_STREAM_ERROR;
+ }
+ if (windowBits == 8) windowBits = 9; /* until 256-byte window bug fixed */
+ s = (deflate_state *) ZALLOC(strm, 1, sizeof(deflate_state));
+ if (s == Z_NULL) return Z_MEM_ERROR;
+ strm->state = (struct internal_state FAR *)s;
+ s->strm = strm;
+
+ s->wrap = wrap;
+ s->w_bits = windowBits;
+ s->w_size = 1 << s->w_bits;
+ s->w_mask = s->w_size - 1;
+
+ s->hash_bits = memLevel + 7;
+ s->hash_size = 1 << s->hash_bits;
+ s->hash_mask = s->hash_size - 1;
+ s->hash_shift = ((s->hash_bits+MIN_MATCH-1)/MIN_MATCH);
+
+ s->window = (Bytef *) ZALLOC(strm, s->w_size, 2*sizeof(Byte));
+ s->prev = (Posf *) ZALLOC(strm, s->w_size, sizeof(Pos));
+ s->head = (Posf *) ZALLOC(strm, s->hash_size, sizeof(Pos));
+
+ s->lit_bufsize = 1 << (memLevel + 6); /* 16K elements by default */
+
+ overlay = (ushf *) ZALLOC(strm, s->lit_bufsize, sizeof(ush)+2);
+ s->pending_buf = (uchf *) overlay;
+ s->pending_buf_size = (ulg)s->lit_bufsize * (sizeof(ush)+2L);
+
+ if (s->window == Z_NULL || s->prev == Z_NULL || s->head == Z_NULL ||
+ s->pending_buf == Z_NULL) {
+ s->status = FINISH_STATE;
+ strm->msg = (char*)ERR_MSG(Z_MEM_ERROR);
+ deflateEnd (strm);
+ return Z_MEM_ERROR;
+ }
+ s->d_buf = overlay + s->lit_bufsize/sizeof(ush);
+ s->l_buf = s->pending_buf + (1+sizeof(ush))*s->lit_bufsize;
+
+ s->level = level;
+ s->strategy = strategy;
+ s->method = (Byte)method;
+
+ return deflateReset(strm);
+}
+
+/* ========================================================================= */
+int ZEXPORT deflateSetDictionary (strm, dictionary, dictLength)
+ z_streamp strm;
+ const Bytef *dictionary;
+ uInt dictLength;
+{
+ deflate_state *s;
+ uInt length = dictLength;
+ uInt n;
+ IPos hash_head = 0;
+
+ if (strm == Z_NULL || strm->state == Z_NULL || dictionary == Z_NULL ||
+ strm->state->wrap == 2 ||
+ (strm->state->wrap == 1 && strm->state->status != INIT_STATE))
+ return Z_STREAM_ERROR;
+
+ s = strm->state;
+ if (s->wrap)
+ strm->adler = adler32(strm->adler, dictionary, dictLength);
+
+ if (length < MIN_MATCH) return Z_OK;
+ if (length > MAX_DIST(s)) {
+ length = MAX_DIST(s);
+#ifndef USE_DICT_HEAD
+ dictionary += dictLength - length; /* use the tail of the dictionary */
+#endif
+ }
+ zmemcpy(s->window, dictionary, length);
+ s->strstart = length;
+ s->block_start = (long)length;
+
+ /* Insert all strings in the hash table (except for the last two bytes).
+ * s->lookahead stays null, so s->ins_h will be recomputed at the next
+ * call of fill_window.
+ */
+ s->ins_h = s->window[0];
+ UPDATE_HASH(s, s->ins_h, s->window[1]);
+ for (n = 0; n <= length - MIN_MATCH; n++) {
+ INSERT_STRING(s, n, hash_head);
+ }
+ if (hash_head) hash_head = 0; /* to make compiler happy */
+ return Z_OK;
+}
+
+/* ========================================================================= */
+int ZEXPORT deflateReset (strm)
+ z_streamp strm;
+{
+ deflate_state *s;
+
+ if (strm == Z_NULL || strm->state == Z_NULL ||
+ strm->zalloc == (alloc_func)0 || strm->zfree == (free_func)0) {
+ return Z_STREAM_ERROR;
+ }
+
+ strm->total_in = strm->total_out = 0;
+ strm->msg = Z_NULL; /* use zfree if we ever allocate msg dynamically */
+ strm->data_type = Z_UNKNOWN;
+
+ s = (deflate_state *)strm->state;
+ s->pending = 0;
+ s->pending_out = s->pending_buf;
+
+ if (s->wrap < 0) {
+ s->wrap = -s->wrap; /* was made negative by deflate(..., Z_FINISH); */
+ }
+ s->status = s->wrap ? INIT_STATE : BUSY_STATE;
+ strm->adler =
+#ifdef GZIP
+ s->wrap == 2 ? crc32(0L, Z_NULL, 0) :
+#endif
+ adler32(0L, Z_NULL, 0);
+ s->last_flush = Z_NO_FLUSH;
+
+ _tr_init(s);
+ lm_init(s);
+
+ return Z_OK;
+}
+
+/* ========================================================================= */
+int ZEXPORT deflatePrime (strm, bits, value)
+ z_streamp strm;
+ int bits;
+ int value;
+{
+ if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR;
+ strm->state->bi_valid = bits;
+ strm->state->bi_buf = (ush)(value & ((1 << bits) - 1));
+ return Z_OK;
+}
+
+/* ========================================================================= */
+int ZEXPORT deflateParams(strm, level, strategy)
+ z_streamp strm;
+ int level;
+ int strategy;
+{
+ deflate_state *s;
+ compress_func func;
+ int err = Z_OK;
+
+ if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR;
+ s = strm->state;
+
+#ifdef FASTEST
+ if (level != 0) level = 1;
+#else
+ if (level == Z_DEFAULT_COMPRESSION) level = 6;
+#endif
+ if (level < 0 || level > 9 || strategy < 0 || strategy > Z_RLE) {
+ return Z_STREAM_ERROR;
+ }
+ func = configuration_table[s->level].func;
+
+ if (func != configuration_table[level].func && strm->total_in != 0) {
+ /* Flush the last buffer: */
+ err = deflate(strm, Z_PARTIAL_FLUSH);
+ }
+ if (s->level != level) {
+ s->level = level;
+ s->max_lazy_match = configuration_table[level].max_lazy;
+ s->good_match = configuration_table[level].good_length;
+ s->nice_match = configuration_table[level].nice_length;
+ s->max_chain_length = configuration_table[level].max_chain;
+ }
+ s->strategy = strategy;
+ return err;
+}
+
+/* =========================================================================
+ * For the default windowBits of 15 and memLevel of 8, this function returns
+ * a close to exact, as well as small, upper bound on the compressed size.
+ * They are coded as constants here for a reason--if the #define's are
+ * changed, then this function needs to be changed as well. The return
+ * value for 15 and 8 only works for those exact settings.
+ *
+ * For any setting other than those defaults for windowBits and memLevel,
+ * the value returned is a conservative worst case for the maximum expansion
+ * resulting from using fixed blocks instead of stored blocks, which deflate
+ * can emit on compressed data for some combinations of the parameters.
+ *
+ * This function could be more sophisticated to provide closer upper bounds
+ * for every combination of windowBits and memLevel, as well as wrap.
+ * But even the conservative upper bound of about 14% expansion does not
+ * seem onerous for output buffer allocation.
+ */
+uLong ZEXPORT deflateBound(strm, sourceLen)
+ z_streamp strm;
+ uLong sourceLen;
+{
+ deflate_state *s;
+ uLong destLen;
+
+ /* conservative upper bound */
+ destLen = sourceLen +
+ ((sourceLen + 7) >> 3) + ((sourceLen + 63) >> 6) + 11;
+
+ /* if can't get parameters, return conservative bound */
+ if (strm == Z_NULL || strm->state == Z_NULL)
+ return destLen;
+
+ /* if not default parameters, return conservative bound */
+ s = strm->state;
+ if (s->w_bits != 15 || s->hash_bits != 8 + 7)
+ return destLen;
+
+ /* default settings: return tight bound for that case */
+ return compressBound(sourceLen);
+}
+
+/* =========================================================================
+ * Put a short in the pending buffer. The 16-bit value is put in MSB order.
+ * IN assertion: the stream state is correct and there is enough room in
+ * pending_buf.
+ */
+local void putShortMSB (s, b)
+ deflate_state *s;
+ uInt b;
+{
+ put_byte(s, (Byte)(b >> 8));
+ put_byte(s, (Byte)(b & 0xff));
+}
+
+/* =========================================================================
+ * Flush as much pending output as possible. All deflate() output goes
+ * through this function so some applications may wish to modify it
+ * to avoid allocating a large strm->next_out buffer and copying into it.
+ * (See also read_buf()).
+ */
+local void flush_pending(strm)
+ z_streamp strm;
+{
+ unsigned len = strm->state->pending;
+
+ if (len > strm->avail_out) len = strm->avail_out;
+ if (len == 0) return;
+
+ zmemcpy(strm->next_out, strm->state->pending_out, len);
+ strm->next_out += len;
+ strm->state->pending_out += len;
+ strm->total_out += len;
+ strm->avail_out -= len;
+ strm->state->pending -= len;
+ if (strm->state->pending == 0) {
+ strm->state->pending_out = strm->state->pending_buf;
+ }
+}
+
+/* ========================================================================= */
+int ZEXPORT deflate (strm, flush)
+ z_streamp strm;
+ int flush;
+{
+ int old_flush; /* value of flush param for previous deflate call */
+ deflate_state *s;
+
+ if (strm == Z_NULL || strm->state == Z_NULL ||
+ flush > Z_FINISH || flush < 0) {
+ return Z_STREAM_ERROR;
+ }
+ s = strm->state;
+
+ if (strm->next_out == Z_NULL ||
+ (strm->next_in == Z_NULL && strm->avail_in != 0) ||
+ (s->status == FINISH_STATE && flush != Z_FINISH)) {
+ ERR_RETURN(strm, Z_STREAM_ERROR);
+ }
+ if (strm->avail_out == 0) ERR_RETURN(strm, Z_BUF_ERROR);
+
+ s->strm = strm; /* just in case */
+ old_flush = s->last_flush;
+ s->last_flush = flush;
+
+ /* Write the header */
+ if (s->status == INIT_STATE) {
+#ifdef GZIP
+ if (s->wrap == 2) {
+ put_byte(s, 31);
+ put_byte(s, 139);
+ put_byte(s, 8);
+ put_byte(s, 0);
+ put_byte(s, 0);
+ put_byte(s, 0);
+ put_byte(s, 0);
+ put_byte(s, 0);
+ put_byte(s, s->level == 9 ? 2 :
+ (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2 ?
+ 4 : 0));
+ put_byte(s, 255);
+ s->status = BUSY_STATE;
+ strm->adler = crc32(0L, Z_NULL, 0);
+ }
+ else
+#endif
+ {
+ uInt header = (Z_DEFLATED + ((s->w_bits-8)<<4)) << 8;
+ uInt level_flags;
+
+ if (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2)
+ level_flags = 0;
+ else if (s->level < 6)
+ level_flags = 1;
+ else if (s->level == 6)
+ level_flags = 2;
+ else
+ level_flags = 3;
+ header |= (level_flags << 6);
+ if (s->strstart != 0) header |= PRESET_DICT;
+ header += 31 - (header % 31);
+
+ s->status = BUSY_STATE;
+ putShortMSB(s, header);
+
+ /* Save the adler32 of the preset dictionary: */
+ if (s->strstart != 0) {
+ putShortMSB(s, (uInt)(strm->adler >> 16));
+ putShortMSB(s, (uInt)(strm->adler & 0xffff));
+ }
+ strm->adler = adler32(0L, Z_NULL, 0);
+ }
+ }
+
+ /* Flush as much pending output as possible */
+ if (s->pending != 0) {
+ flush_pending(strm);
+ if (strm->avail_out == 0) {
+ /* Since avail_out is 0, deflate will be called again with
+ * more output space, but possibly with both pending and
+ * avail_in equal to zero. There won't be anything to do,
+ * but this is not an error situation so make sure we
+ * return OK instead of BUF_ERROR at next call of deflate:
+ */
+ s->last_flush = -1;
+ return Z_OK;
+ }
+
+ /* Make sure there is something to do and avoid duplicate consecutive
+ * flushes. For repeated and useless calls with Z_FINISH, we keep
+ * returning Z_STREAM_END instead of Z_BUF_ERROR.
+ */
+ } else if (strm->avail_in == 0 && flush <= old_flush &&
+ flush != Z_FINISH) {
+ ERR_RETURN(strm, Z_BUF_ERROR);
+ }
+
+ /* User must not provide more input after the first FINISH: */
+ if (s->status == FINISH_STATE && strm->avail_in != 0) {
+ ERR_RETURN(strm, Z_BUF_ERROR);
+ }
+
+ /* Start a new block or continue the current one.
+ */
+ if (strm->avail_in != 0 || s->lookahead != 0 ||
+ (flush != Z_NO_FLUSH && s->status != FINISH_STATE)) {
+ block_state bstate;
+
+ bstate = (*(configuration_table[s->level].func))(s, flush);
+
+ if (bstate == finish_started || bstate == finish_done) {
+ s->status = FINISH_STATE;
+ }
+ if (bstate == need_more || bstate == finish_started) {
+ if (strm->avail_out == 0) {
+ s->last_flush = -1; /* avoid BUF_ERROR next call, see above */
+ }
+ return Z_OK;
+ /* If flush != Z_NO_FLUSH && avail_out == 0, the next call
+ * of deflate should use the same flush parameter to make sure
+ * that the flush is complete. So we don't have to output an
+ * empty block here, this will be done at next call. This also
+ * ensures that for a very small output buffer, we emit at most
+ * one empty block.
+ */
+ }
+ if (bstate == block_done) {
+ if (flush == Z_PARTIAL_FLUSH) {
+ _tr_align(s);
+ } else { /* FULL_FLUSH or SYNC_FLUSH */
+ _tr_stored_block(s, (char*)0, 0L, 0);
+ /* For a full flush, this empty block will be recognized
+ * as a special marker by inflate_sync().
+ */
+ if (flush == Z_FULL_FLUSH) {
+ CLEAR_HASH(s); /* forget history */
+ }
+ }
+ flush_pending(strm);
+ if (strm->avail_out == 0) {
+ s->last_flush = -1; /* avoid BUF_ERROR at next call, see above */
+ return Z_OK;
+ }
+ }
+ }
+ Assert(strm->avail_out > 0, "bug2");
+
+ if (flush != Z_FINISH) return Z_OK;
+ if (s->wrap <= 0) return Z_STREAM_END;
+
+ /* Write the trailer */
+#ifdef GZIP
+ if (s->wrap == 2) {
+ put_byte(s, (Byte)(strm->adler & 0xff));
+ put_byte(s, (Byte)((strm->adler >> 8) & 0xff));
+ put_byte(s, (Byte)((strm->adler >> 16) & 0xff));
+ put_byte(s, (Byte)((strm->adler >> 24) & 0xff));
+ put_byte(s, (Byte)(strm->total_in & 0xff));
+ put_byte(s, (Byte)((strm->total_in >> 8) & 0xff));
+ put_byte(s, (Byte)((strm->total_in >> 16) & 0xff));
+ put_byte(s, (Byte)((strm->total_in >> 24) & 0xff));
+ }
+ else
+#endif
+ {
+ putShortMSB(s, (uInt)(strm->adler >> 16));
+ putShortMSB(s, (uInt)(strm->adler & 0xffff));
+ }
+ flush_pending(strm);
+ /* If avail_out is zero, the application will call deflate again
+ * to flush the rest.
+ */
+ if (s->wrap > 0) s->wrap = -s->wrap; /* write the trailer only once! */
+ return s->pending != 0 ? Z_OK : Z_STREAM_END;
+}
+
+/* ========================================================================= */
+int ZEXPORT deflateEnd (strm)
+ z_streamp strm;
+{
+ int status;
+
+ if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR;
+
+ status = strm->state->status;
+ if (status != INIT_STATE && status != BUSY_STATE &&
+ status != FINISH_STATE) {
+ return Z_STREAM_ERROR;
+ }
+
+ /* Deallocate in reverse order of allocations: */
+ TRY_FREE(strm, strm->state->pending_buf);
+ TRY_FREE(strm, strm->state->head);
+ TRY_FREE(strm, strm->state->prev);
+ TRY_FREE(strm, strm->state->window);
+
+ ZFREE(strm, strm->state);
+ strm->state = Z_NULL;
+
+ return status == BUSY_STATE ? Z_DATA_ERROR : Z_OK;
+}
+
+/* =========================================================================
+ * Copy the source state to the destination state.
+ * To simplify the source, this is not supported for 16-bit MSDOS (which
+ * doesn't have enough memory anyway to duplicate compression states).
+ */
+int ZEXPORT deflateCopy (dest, source)
+ z_streamp dest;
+ z_streamp source;
+{
+#ifdef MAXSEG_64K
+ return Z_STREAM_ERROR;
+#else
+ deflate_state *ds;
+ deflate_state *ss;
+ ushf *overlay;
+
+
+ if (source == Z_NULL || dest == Z_NULL || source->state == Z_NULL) {
+ return Z_STREAM_ERROR;
+ }
+
+ ss = source->state;
+
+ *dest = *source;
+
+ ds = (deflate_state *) ZALLOC(dest, 1, sizeof(deflate_state));
+ if (ds == Z_NULL) return Z_MEM_ERROR;
+ dest->state = (struct internal_state FAR *) ds;
+ *ds = *ss;
+ ds->strm = dest;
+
+ ds->window = (Bytef *) ZALLOC(dest, ds->w_size, 2*sizeof(Byte));
+ ds->prev = (Posf *) ZALLOC(dest, ds->w_size, sizeof(Pos));
+ ds->head = (Posf *) ZALLOC(dest, ds->hash_size, sizeof(Pos));
+ overlay = (ushf *) ZALLOC(dest, ds->lit_bufsize, sizeof(ush)+2);
+ ds->pending_buf = (uchf *) overlay;
+
+ if (ds->window == Z_NULL || ds->prev == Z_NULL || ds->head == Z_NULL ||
+ ds->pending_buf == Z_NULL) {
+ deflateEnd (dest);
+ return Z_MEM_ERROR;
+ }
+ /* following zmemcpy do not work for 16-bit MSDOS */
+ zmemcpy(ds->window, ss->window, ds->w_size * 2 * sizeof(Byte));
+ zmemcpy(ds->prev, ss->prev, ds->w_size * sizeof(Pos));
+ zmemcpy(ds->head, ss->head, ds->hash_size * sizeof(Pos));
+ zmemcpy(ds->pending_buf, ss->pending_buf, (uInt)ds->pending_buf_size);
+
+ ds->pending_out = ds->pending_buf + (ss->pending_out - ss->pending_buf);
+ ds->d_buf = overlay + ds->lit_bufsize/sizeof(ush);
+ ds->l_buf = ds->pending_buf + (1+sizeof(ush))*ds->lit_bufsize;
+
+ ds->l_desc.dyn_tree = ds->dyn_ltree;
+ ds->d_desc.dyn_tree = ds->dyn_dtree;
+ ds->bl_desc.dyn_tree = ds->bl_tree;
+
+ return Z_OK;
+#endif /* MAXSEG_64K */
+}
+
+/* ===========================================================================
+ * Read a new buffer from the current input stream, update the adler32
+ * and total number of bytes read. All deflate() input goes through
+ * this function so some applications may wish to modify it to avoid
+ * allocating a large strm->next_in buffer and copying from it.
+ * (See also flush_pending()).
+ */
+local int read_buf(strm, buf, size)
+ z_streamp strm;
+ Bytef *buf;
+ unsigned size;
+{
+ unsigned len = strm->avail_in;
+
+ if (len > size) len = size;
+ if (len == 0) return 0;
+
+ strm->avail_in -= len;
+
+ if (strm->state->wrap == 1) {
+ strm->adler = adler32(strm->adler, strm->next_in, len);
+ }
+#ifdef GZIP
+ else if (strm->state->wrap == 2) {
+ strm->adler = crc32(strm->adler, strm->next_in, len);
+ }
+#endif
+ zmemcpy(buf, strm->next_in, len);
+ strm->next_in += len;
+ strm->total_in += len;
+
+ return (int)len;
+}
+
+/* ===========================================================================
+ * Initialize the "longest match" routines for a new zlib stream
+ */
+local void lm_init (s)
+ deflate_state *s;
+{
+ s->window_size = (ulg)2L*s->w_size;
+
+ CLEAR_HASH(s);
+
+ /* Set the default configuration parameters:
+ */
+ s->max_lazy_match = configuration_table[s->level].max_lazy;
+ s->good_match = configuration_table[s->level].good_length;
+ s->nice_match = configuration_table[s->level].nice_length;
+ s->max_chain_length = configuration_table[s->level].max_chain;
+
+ s->strstart = 0;
+ s->block_start = 0L;
+ s->lookahead = 0;
+ s->match_length = s->prev_length = MIN_MATCH-1;
+ s->match_available = 0;
+ s->ins_h = 0;
+#ifdef ASMV
+ match_init(); /* initialize the asm code */
+#endif
+}
+
+#ifndef FASTEST
+/* ===========================================================================
+ * Set match_start to the longest match starting at the given string and
+ * return its length. Matches shorter or equal to prev_length are discarded,
+ * in which case the result is equal to prev_length and match_start is
+ * garbage.
+ * IN assertions: cur_match is the head of the hash chain for the current
+ * string (strstart) and its distance is <= MAX_DIST, and prev_length >= 1
+ * OUT assertion: the match length is not greater than s->lookahead.
+ */
+#ifndef ASMV
+/* For 80x86 and 680x0, an optimized version will be provided in match.asm or
+ * match.S. The code will be functionally equivalent.
+ */
+local uInt longest_match(s, cur_match)
+ deflate_state *s;
+ IPos cur_match; /* current match */
+{
+ unsigned chain_length = s->max_chain_length;/* max hash chain length */
+ register Bytef *scan = s->window + s->strstart; /* current string */
+ register Bytef *match; /* matched string */
+ register int len; /* length of current match */
+ int best_len = s->prev_length; /* best match length so far */
+ int nice_match = s->nice_match; /* stop if match long enough */
+ IPos limit = s->strstart > (IPos)MAX_DIST(s) ?
+ s->strstart - (IPos)MAX_DIST(s) : NIL;
+ /* Stop when cur_match becomes <= limit. To simplify the code,
+ * we prevent matches with the string of window index 0.
+ */
+ Posf *prev = s->prev;
+ uInt wmask = s->w_mask;
+
+#ifdef UNALIGNED_OK
+ /* Compare two bytes at a time. Note: this is not always beneficial.
+ * Try with and without -DUNALIGNED_OK to check.
+ */
+ register Bytef *strend = s->window + s->strstart + MAX_MATCH - 1;
+ register ush scan_start = *(ushf*)scan;
+ register ush scan_end = *(ushf*)(scan+best_len-1);
+#else
+ register Bytef *strend = s->window + s->strstart + MAX_MATCH;
+ register Byte scan_end1 = scan[best_len-1];
+ register Byte scan_end = scan[best_len];
+#endif
+
+ /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16.
+ * It is easy to get rid of this optimization if necessary.
+ */
+ Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever");
+
+ /* Do not waste too much time if we already have a good match: */
+ if (s->prev_length >= s->good_match) {
+ chain_length >>= 2;
+ }
+ /* Do not look for matches beyond the end of the input. This is necessary
+ * to make deflate deterministic.
+ */
+ if ((uInt)nice_match > s->lookahead) nice_match = s->lookahead;
+
+ Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead");
+
+ do {
+ Assert(cur_match < s->strstart, "no future");
+ match = s->window + cur_match;
+
+ /* Skip to next match if the match length cannot increase
+ * or if the match length is less than 2:
+ */
+#if (defined(UNALIGNED_OK) && MAX_MATCH == 258)
+ /* This code assumes sizeof(unsigned short) == 2. Do not use
+ * UNALIGNED_OK if your compiler uses a different size.
+ */
+ if (*(ushf*)(match+best_len-1) != scan_end ||
+ *(ushf*)match != scan_start) continue;
+
+ /* It is not necessary to compare scan[2] and match[2] since they are
+ * always equal when the other bytes match, given that the hash keys
+ * are equal and that HASH_BITS >= 8. Compare 2 bytes at a time at
+ * strstart+3, +5, ... up to strstart+257. We check for insufficient
+ * lookahead only every 4th comparison; the 128th check will be made
+ * at strstart+257. If MAX_MATCH-2 is not a multiple of 8, it is
+ * necessary to put more guard bytes at the end of the window, or
+ * to check more often for insufficient lookahead.
+ */
+ Assert(scan[2] == match[2], "scan[2]?");
+ scan++, match++;
+ do {
+ } while (*(ushf*)(scan+=2) == *(ushf*)(match+=2) &&
+ *(ushf*)(scan+=2) == *(ushf*)(match+=2) &&
+ *(ushf*)(scan+=2) == *(ushf*)(match+=2) &&
+ *(ushf*)(scan+=2) == *(ushf*)(match+=2) &&
+ scan < strend);
+ /* The funny "do {}" generates better code on most compilers */
+
+ /* Here, scan <= window+strstart+257 */
+ Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan");
+ if (*scan == *match) scan++;
+
+ len = (MAX_MATCH - 1) - (int)(strend-scan);
+ scan = strend - (MAX_MATCH-1);
+
+#else /* UNALIGNED_OK */
+
+ if (match[best_len] != scan_end ||
+ match[best_len-1] != scan_end1 ||
+ *match != *scan ||
+ *++match != scan[1]) continue;
+
+ /* The check at best_len-1 can be removed because it will be made
+ * again later. (This heuristic is not always a win.)
+ * It is not necessary to compare scan[2] and match[2] since they
+ * are always equal when the other bytes match, given that
+ * the hash keys are equal and that HASH_BITS >= 8.
+ */
+ scan += 2, match++;
+ Assert(*scan == *match, "match[2]?");
+
+ /* We check for insufficient lookahead only every 8th comparison;
+ * the 256th check will be made at strstart+258.
+ */
+ do {
+ } while (*++scan == *++match && *++scan == *++match &&
+ *++scan == *++match && *++scan == *++match &&
+ *++scan == *++match && *++scan == *++match &&
+ *++scan == *++match && *++scan == *++match &&
+ scan < strend);
+
+ Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan");
+
+ len = MAX_MATCH - (int)(strend - scan);
+ scan = strend - MAX_MATCH;
+
+#endif /* UNALIGNED_OK */
+
+ if (len > best_len) {
+ s->match_start = cur_match;
+ best_len = len;
+ if (len >= nice_match) break;
+#ifdef UNALIGNED_OK
+ scan_end = *(ushf*)(scan+best_len-1);
+#else
+ scan_end1 = scan[best_len-1];
+ scan_end = scan[best_len];
+#endif
+ }
+ } while ((cur_match = prev[cur_match & wmask]) > limit
+ && --chain_length != 0);
+
+ if ((uInt)best_len <= s->lookahead) return (uInt)best_len;
+ return s->lookahead;
+}
+#endif /* ASMV */
+#endif /* FASTEST */
+
+/* ---------------------------------------------------------------------------
+ * Optimized version for level == 1 or strategy == Z_RLE only
+ */
+local uInt longest_match_fast(s, cur_match)
+ deflate_state *s;
+ IPos cur_match; /* current match */
+{
+ register Bytef *scan = s->window + s->strstart; /* current string */
+ register Bytef *match; /* matched string */
+ register int len; /* length of current match */
+ register Bytef *strend = s->window + s->strstart + MAX_MATCH;
+
+ /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16.
+ * It is easy to get rid of this optimization if necessary.
+ */
+ Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever");
+
+ Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead");
+
+ Assert(cur_match < s->strstart, "no future");
+
+ match = s->window + cur_match;
+
+ /* Return failure if the match length is less than 2:
+ */
+ if (match[0] != scan[0] || match[1] != scan[1]) return MIN_MATCH-1;
+
+ /* The check at best_len-1 can be removed because it will be made
+ * again later. (This heuristic is not always a win.)
+ * It is not necessary to compare scan[2] and match[2] since they
+ * are always equal when the other bytes match, given that
+ * the hash keys are equal and that HASH_BITS >= 8.
+ */
+ scan += 2, match += 2;
+ Assert(*scan == *match, "match[2]?");
+
+ /* We check for insufficient lookahead only every 8th comparison;
+ * the 256th check will be made at strstart+258.
+ */
+ do {
+ } while (*++scan == *++match && *++scan == *++match &&
+ *++scan == *++match && *++scan == *++match &&
+ *++scan == *++match && *++scan == *++match &&
+ *++scan == *++match && *++scan == *++match &&
+ scan < strend);
+
+ Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan");
+
+ len = MAX_MATCH - (int)(strend - scan);
+
+ if (len < MIN_MATCH) return MIN_MATCH - 1;
+
+ s->match_start = cur_match;
+ return (uInt)len <= s->lookahead ? (uInt)len : s->lookahead;
+}
+
+#ifdef DEBUG
+/* ===========================================================================
+ * Check that the match at match_start is indeed a match.
+ */
+local void check_match(s, start, match, length)
+ deflate_state *s;
+ IPos start, match;
+ int length;
+{
+ /* check that the match is indeed a match */
+ if (zmemcmp(s->window + match,
+ s->window + start, length) != EQUAL) {
+ fprintf(stderr, " start %u, match %u, length %d\n",
+ start, match, length);
+ do {
+ fprintf(stderr, "%c%c", s->window[match++], s->window[start++]);
+ } while (--length != 0);
+ z_error("invalid match");
+ }
+ if (z_verbose > 1) {
+ fprintf(stderr,"\\[%d,%d]", start-match, length);
+ do { putc(s->window[start++], stderr); } while (--length != 0);
+ }
+}
+#else
+# define check_match(s, start, match, length)
+#endif /* DEBUG */
+
+/* ===========================================================================
+ * Fill the window when the lookahead becomes insufficient.
+ * Updates strstart and lookahead.
+ *
+ * IN assertion: lookahead < MIN_LOOKAHEAD
+ * OUT assertions: strstart <= window_size-MIN_LOOKAHEAD
+ * At least one byte has been read, or avail_in == 0; reads are
+ * performed for at least two bytes (required for the zip translate_eol
+ * option -- not supported here).
+ */
+local void fill_window(s)
+ deflate_state *s;
+{
+ register unsigned n, m;
+ register Posf *p;
+ unsigned more; /* Amount of free space at the end of the window. */
+ uInt wsize = s->w_size;
+
+ do {
+ more = (unsigned)(s->window_size -(ulg)s->lookahead -(ulg)s->strstart);
+
+ /* Deal with !@#$% 64K limit: */
+ if (sizeof(int) <= 2) {
+ if (more == 0 && s->strstart == 0 && s->lookahead == 0) {
+ more = wsize;
+
+ } else if (more == (unsigned)(-1)) {
+ /* Very unlikely, but possible on 16 bit machine if
+ * strstart == 0 && lookahead == 1 (input done a byte at time)
+ */
+ more--;
+ }
+ }
+
+ /* If the window is almost full and there is insufficient lookahead,
+ * move the upper half to the lower one to make room in the upper half.
+ */
+ if (s->strstart >= wsize+MAX_DIST(s)) {
+
+ zmemcpy(s->window, s->window+wsize, (unsigned)wsize);
+ s->match_start -= wsize;
+ s->strstart -= wsize; /* we now have strstart >= MAX_DIST */
+ s->block_start -= (long) wsize;
+
+ /* Slide the hash table (could be avoided with 32 bit values
+ at the expense of memory usage). We slide even when level == 0
+ to keep the hash table consistent if we switch back to level > 0
+ later. (Using level 0 permanently is not an optimal usage of
+ zlib, so we don't care about this pathological case.)
+ */
+ n = s->hash_size;
+ p = &s->head[n];
+ do {
+ m = *--p;
+ *p = (Pos)(m >= wsize ? m-wsize : NIL);
+ } while (--n);
+
+ n = wsize;
+#ifndef FASTEST
+ p = &s->prev[n];
+ do {
+ m = *--p;
+ *p = (Pos)(m >= wsize ? m-wsize : NIL);
+ /* If n is not on any hash chain, prev[n] is garbage but
+ * its value will never be used.
+ */
+ } while (--n);
+#endif
+ more += wsize;
+ }
+ if (s->strm->avail_in == 0) return;
+
+ /* If there was no sliding:
+ * strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 &&
+ * more == window_size - lookahead - strstart
+ * => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1)
+ * => more >= window_size - 2*WSIZE + 2
+ * In the BIG_MEM or MMAP case (not yet supported),
+ * window_size == input_size + MIN_LOOKAHEAD &&
+ * strstart + s->lookahead <= input_size => more >= MIN_LOOKAHEAD.
+ * Otherwise, window_size == 2*WSIZE so more >= 2.
+ * If there was sliding, more >= WSIZE. So in all cases, more >= 2.
+ */
+ Assert(more >= 2, "more < 2");
+
+ n = read_buf(s->strm, s->window + s->strstart + s->lookahead, more);
+ s->lookahead += n;
+
+ /* Initialize the hash value now that we have some input: */
+ if (s->lookahead >= MIN_MATCH) {
+ s->ins_h = s->window[s->strstart];
+ UPDATE_HASH(s, s->ins_h, s->window[s->strstart+1]);
+#if MIN_MATCH != 3
+ Call UPDATE_HASH() MIN_MATCH-3 more times
+#endif
+ }
+ /* If the whole input has less than MIN_MATCH bytes, ins_h is garbage,
+ * but this is not important since only literal bytes will be emitted.
+ */
+
+ } while (s->lookahead < MIN_LOOKAHEAD && s->strm->avail_in != 0);
+}
+
+/* ===========================================================================
+ * Flush the current block, with given end-of-file flag.
+ * IN assertion: strstart is set to the end of the current match.
+ */
+#define FLUSH_BLOCK_ONLY(s, eof) { \
+ _tr_flush_block(s, (s->block_start >= 0L ? \
+ (charf *)&s->window[(unsigned)s->block_start] : \
+ (charf *)Z_NULL), \
+ (ulg)((long)s->strstart - s->block_start), \
+ (eof)); \
+ s->block_start = s->strstart; \
+ flush_pending(s->strm); \
+ Tracev((stderr,"[FLUSH]")); \
+}
+
+/* Same but force premature exit if necessary. */
+#define FLUSH_BLOCK(s, eof) { \
+ FLUSH_BLOCK_ONLY(s, eof); \
+ if (s->strm->avail_out == 0) return (eof) ? finish_started : need_more; \
+}
+
+/* ===========================================================================
+ * Copy without compression as much as possible from the input stream, return
+ * the current block state.
+ * This function does not insert new strings in the dictionary since
+ * uncompressible data is probably not useful. This function is used
+ * only for the level=0 compression option.
+ * NOTE: this function should be optimized to avoid extra copying from
+ * window to pending_buf.
+ */
+local block_state deflate_stored(s, flush)
+ deflate_state *s;
+ int flush;
+{
+ /* Stored blocks are limited to 0xffff bytes, pending_buf is limited
+ * to pending_buf_size, and each stored block has a 5 byte header:
+ */
+ ulg max_block_size = 0xffff;
+ ulg max_start;
+
+ if (max_block_size > s->pending_buf_size - 5) {
+ max_block_size = s->pending_buf_size - 5;
+ }
+
+ /* Copy as much as possible from input to output: */
+ for (;;) {
+ /* Fill the window as much as possible: */
+ if (s->lookahead <= 1) {
+
+ Assert(s->strstart < s->w_size+MAX_DIST(s) ||
+ s->block_start >= (long)s->w_size, "slide too late");
+
+ fill_window(s);
+ if (s->lookahead == 0 && flush == Z_NO_FLUSH) return need_more;
+
+ if (s->lookahead == 0) break; /* flush the current block */
+ }
+ Assert(s->block_start >= 0L, "block gone");
+
+ s->strstart += s->lookahead;
+ s->lookahead = 0;
+
+ /* Emit a stored block if pending_buf will be full: */
+ max_start = s->block_start + max_block_size;
+ if (s->strstart == 0 || (ulg)s->strstart >= max_start) {
+ /* strstart == 0 is possible when wraparound on 16-bit machine */
+ s->lookahead = (uInt)(s->strstart - max_start);
+ s->strstart = (uInt)max_start;
+ FLUSH_BLOCK(s, 0);
+ }
+ /* Flush if we may have to slide, otherwise block_start may become
+ * negative and the data will be gone:
+ */
+ if (s->strstart - (uInt)s->block_start >= MAX_DIST(s)) {
+ FLUSH_BLOCK(s, 0);
+ }
+ }
+ FLUSH_BLOCK(s, flush == Z_FINISH);
+ return flush == Z_FINISH ? finish_done : block_done;
+}
+
+/* ===========================================================================
+ * Compress as much as possible from the input stream, return the current
+ * block state.
+ * This function does not perform lazy evaluation of matches and inserts
+ * new strings in the dictionary only for unmatched strings or for short
+ * matches. It is used only for the fast compression options.
+ */
+local block_state deflate_fast(s, flush)
+ deflate_state *s;
+ int flush;
+{
+ IPos hash_head = NIL; /* head of the hash chain */
+ int bflush; /* set if current block must be flushed */
+
+ for (;;) {
+ /* Make sure that we always have enough lookahead, except
+ * at the end of the input file. We need MAX_MATCH bytes
+ * for the next match, plus MIN_MATCH bytes to insert the
+ * string following the next match.
+ */
+ if (s->lookahead < MIN_LOOKAHEAD) {
+ fill_window(s);
+ if (s->lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) {
+ return need_more;
+ }
+ if (s->lookahead == 0) break; /* flush the current block */
+ }
+
+ /* Insert the string window[strstart .. strstart+2] in the
+ * dictionary, and set hash_head to the head of the hash chain:
+ */
+ if (s->lookahead >= MIN_MATCH) {
+ INSERT_STRING(s, s->strstart, hash_head);
+ }
+
+ /* Find the longest match, discarding those <= prev_length.
+ * At this point we have always match_length < MIN_MATCH
+ */
+ if (hash_head != NIL && s->strstart - hash_head <= MAX_DIST(s)) {
+ /* To simplify the code, we prevent matches with the string
+ * of window index 0 (in particular we have to avoid a match
+ * of the string with itself at the start of the input file).
+ */
+#ifdef FASTEST
+ if ((s->strategy < Z_HUFFMAN_ONLY) ||
+ (s->strategy == Z_RLE && s->strstart - hash_head == 1)) {
+ s->match_length = longest_match_fast (s, hash_head);
+ }
+#else
+ if (s->strategy < Z_HUFFMAN_ONLY) {
+ s->match_length = longest_match (s, hash_head);
+ } else if (s->strategy == Z_RLE && s->strstart - hash_head == 1) {
+ s->match_length = longest_match_fast (s, hash_head);
+ }
+#endif
+ /* longest_match() or longest_match_fast() sets match_start */
+ }
+ if (s->match_length >= MIN_MATCH) {
+ check_match(s, s->strstart, s->match_start, s->match_length);
+
+ _tr_tally_dist(s, s->strstart - s->match_start,
+ s->match_length - MIN_MATCH, bflush);
+
+ s->lookahead -= s->match_length;
+
+ /* Insert new strings in the hash table only if the match length
+ * is not too large. This saves time but degrades compression.
+ */
+#ifndef FASTEST
+ if (s->match_length <= s->max_insert_length &&
+ s->lookahead >= MIN_MATCH) {
+ s->match_length--; /* string at strstart already in table */
+ do {
+ s->strstart++;
+ INSERT_STRING(s, s->strstart, hash_head);
+ /* strstart never exceeds WSIZE-MAX_MATCH, so there are
+ * always MIN_MATCH bytes ahead.
+ */
+ } while (--s->match_length != 0);
+ s->strstart++;
+ } else
+#endif
+ {
+ s->strstart += s->match_length;
+ s->match_length = 0;
+ s->ins_h = s->window[s->strstart];
+ UPDATE_HASH(s, s->ins_h, s->window[s->strstart+1]);
+#if MIN_MATCH != 3
+ Call UPDATE_HASH() MIN_MATCH-3 more times
+#endif
+ /* If lookahead < MIN_MATCH, ins_h is garbage, but it does not
+ * matter since it will be recomputed at next deflate call.
+ */
+ }
+ } else {
+ /* No match, output a literal byte */
+ Tracevv((stderr,"%c", s->window[s->strstart]));
+ _tr_tally_lit (s, s->window[s->strstart], bflush);
+ s->lookahead--;
+ s->strstart++;
+ }
+ if (bflush) FLUSH_BLOCK(s, 0);
+ }
+ FLUSH_BLOCK(s, flush == Z_FINISH);
+ return flush == Z_FINISH ? finish_done : block_done;
+}
+
+#ifndef FASTEST
+/* ===========================================================================
+ * Same as above, but achieves better compression. We use a lazy
+ * evaluation for matches: a match is finally adopted only if there is
+ * no better match at the next window position.
+ */
+local block_state deflate_slow(s, flush)
+ deflate_state *s;
+ int flush;
+{
+ IPos hash_head = NIL; /* head of hash chain */
+ int bflush; /* set if current block must be flushed */
+
+ /* Process the input block. */
+ for (;;) {
+ /* Make sure that we always have enough lookahead, except
+ * at the end of the input file. We need MAX_MATCH bytes
+ * for the next match, plus MIN_MATCH bytes to insert the
+ * string following the next match.
+ */
+ if (s->lookahead < MIN_LOOKAHEAD) {
+ fill_window(s);
+ if (s->lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) {
+ return need_more;
+ }
+ if (s->lookahead == 0) break; /* flush the current block */
+ }
+
+ /* Insert the string window[strstart .. strstart+2] in the
+ * dictionary, and set hash_head to the head of the hash chain:
+ */
+ if (s->lookahead >= MIN_MATCH) {
+ INSERT_STRING(s, s->strstart, hash_head);
+ }
+
+ /* Find the longest match, discarding those <= prev_length.
+ */
+ s->prev_length = s->match_length, s->prev_match = s->match_start;
+ s->match_length = MIN_MATCH-1;
+
+ if (hash_head != NIL && s->prev_length < s->max_lazy_match &&
+ s->strstart - hash_head <= MAX_DIST(s)) {
+ /* To simplify the code, we prevent matches with the string
+ * of window index 0 (in particular we have to avoid a match
+ * of the string with itself at the start of the input file).
+ */
+ if (s->strategy < Z_HUFFMAN_ONLY) {
+ s->match_length = longest_match (s, hash_head);
+ } else if (s->strategy == Z_RLE && s->strstart - hash_head == 1) {
+ s->match_length = longest_match_fast (s, hash_head);
+ }
+ /* longest_match() or longest_match_fast() sets match_start */
+
+ if (s->match_length <= 5 && (s->strategy == Z_FILTERED
+#if TOO_FAR <= 32767
+ || (s->match_length == MIN_MATCH &&
+ s->strstart - s->match_start > TOO_FAR)
+#endif
+ )) {
+
+ /* If prev_match is also MIN_MATCH, match_start is garbage
+ * but we will ignore the current match anyway.
+ */
+ s->match_length = MIN_MATCH-1;
+ }
+ }
+ /* If there was a match at the previous step and the current
+ * match is not better, output the previous match:
+ */
+ if (s->prev_length >= MIN_MATCH && s->match_length <= s->prev_length) {
+ uInt max_insert = s->strstart + s->lookahead - MIN_MATCH;
+ /* Do not insert strings in hash table beyond this. */
+
+ check_match(s, s->strstart-1, s->prev_match, s->prev_length);
+
+ _tr_tally_dist(s, s->strstart -1 - s->prev_match,
+ s->prev_length - MIN_MATCH, bflush);
+
+ /* Insert in hash table all strings up to the end of the match.
+ * strstart-1 and strstart are already inserted. If there is not
+ * enough lookahead, the last two strings are not inserted in
+ * the hash table.
+ */
+ s->lookahead -= s->prev_length-1;
+ s->prev_length -= 2;
+ do {
+ if (++s->strstart <= max_insert) {
+ INSERT_STRING(s, s->strstart, hash_head);
+ }
+ } while (--s->prev_length != 0);
+ s->match_available = 0;
+ s->match_length = MIN_MATCH-1;
+ s->strstart++;
+
+ if (bflush) FLUSH_BLOCK(s, 0);
+
+ } else if (s->match_available) {
+ /* If there was no match at the previous position, output a
+ * single literal. If there was a match but the current match
+ * is longer, truncate the previous match to a single literal.
+ */
+ Tracevv((stderr,"%c", s->window[s->strstart-1]));
+ _tr_tally_lit(s, s->window[s->strstart-1], bflush);
+ if (bflush) {
+ FLUSH_BLOCK_ONLY(s, 0);
+ }
+ s->strstart++;
+ s->lookahead--;
+ if (s->strm->avail_out == 0) return need_more;
+ } else {
+ /* There is no previous match to compare with, wait for
+ * the next step to decide.
+ */
+ s->match_available = 1;
+ s->strstart++;
+ s->lookahead--;
+ }
+ }
+ Assert (flush != Z_NO_FLUSH, "no flush?");
+ if (s->match_available) {
+ Tracevv((stderr,"%c", s->window[s->strstart-1]));
+ _tr_tally_lit(s, s->window[s->strstart-1], bflush);
+ s->match_available = 0;
+ }
+ FLUSH_BLOCK(s, flush == Z_FINISH);
+ return flush == Z_FINISH ? finish_done : block_done;
+}
+#endif /* FASTEST */
diff --git a/src/zlib/deflate.h b/src/zlib/deflate.h
new file mode 100755
index 0000000..1040171
--- /dev/null
+++ b/src/zlib/deflate.h
@@ -0,0 +1,326 @@
+/* deflate.h -- internal compression state
+ * Copyright (C) 1995-2002 Jean-loup Gailly
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* WARNING: this file should *not* be used by applications. It is
+ part of the implementation of the compression library and is
+ subject to change. Applications should only use zlib.h.
+ */
+
+/* @(#) $ Id$ */
+
+#ifndef DEFLATE_H
+#define DEFLATE_H
+
+#include "zutil.h"
+
+/* define NO_GZIP when compiling if you want to disable gzip header and
+ trailer creation by deflate(). NO_GZIP would be used to avoid linking in
+ the crc code when it is not needed. For shared libraries, gzip encoding
+ should be left enabled. */
+#ifndef NO_GZIP
+# define GZIP
+#endif
+
+/* ===========================================================================
+ * Internal compression state.
+ */
+
+#define LENGTH_CODES 29
+/* number of length codes, not counting the special END_BLOCK code */
+
+#define LITERALS 256
+/* number of literal bytes 0..255 */
+
+#define L_CODES (LITERALS+1+LENGTH_CODES)
+/* number of Literal or Length codes, including the END_BLOCK code */
+
+#define D_CODES 30
+/* number of distance codes */
+
+#define BL_CODES 19
+/* number of codes used to transfer the bit lengths */
+
+#define HEAP_SIZE (2*L_CODES+1)
+/* maximum heap size */
+
+#define MAX_BITS 15
+/* All codes must not exceed MAX_BITS bits */
+
+#define INIT_STATE 42
+#define BUSY_STATE 113
+#define FINISH_STATE 666
+/* Stream status */
+
+
+/* Data structure describing a single value and its code string. */
+typedef struct ct_data_s {
+ union {
+ ush freq; /* frequency count */
+ ush code; /* bit string */
+ } fc;
+ union {
+ ush dad; /* father node in Huffman tree */
+ ush len; /* length of bit string */
+ } dl;
+} FAR ct_data;
+
+#define Freq fc.freq
+#define Code fc.code
+#define Dad dl.dad
+#define Len dl.len
+
+typedef struct static_tree_desc_s static_tree_desc;
+
+typedef struct tree_desc_s {
+ ct_data *dyn_tree; /* the dynamic tree */
+ int max_code; /* largest code with non zero frequency */
+ static_tree_desc *stat_desc; /* the corresponding static tree */
+} FAR tree_desc;
+
+typedef ush Pos;
+typedef Pos FAR Posf;
+typedef unsigned IPos;
+
+/* A Pos is an index in the character window. We use short instead of int to
+ * save space in the various tables. IPos is used only for parameter passing.
+ */
+
+typedef struct internal_state {
+ z_streamp strm; /* pointer back to this zlib stream */
+ int status; /* as the name implies */
+ Bytef *pending_buf; /* output still pending */
+ ulg pending_buf_size; /* size of pending_buf */
+ Bytef *pending_out; /* next pending byte to output to the stream */
+ int pending; /* nb of bytes in the pending buffer */
+ int wrap; /* bit 0 true for zlib, bit 1 true for gzip */
+ Byte data_type; /* UNKNOWN, BINARY or ASCII */
+ Byte method; /* STORED (for zip only) or DEFLATED */
+ int last_flush; /* value of flush param for previous deflate call */
+
+ /* used by deflate.c: */
+
+ uInt w_size; /* LZ77 window size (32K by default) */
+ uInt w_bits; /* log2(w_size) (8..16) */
+ uInt w_mask; /* w_size - 1 */
+
+ Bytef *window;
+ /* Sliding window. Input bytes are read into the second half of the window,
+ * and move to the first half later to keep a dictionary of at least wSize
+ * bytes. With this organization, matches are limited to a distance of
+ * wSize-MAX_MATCH bytes, but this ensures that IO is always
+ * performed with a length multiple of the block size. Also, it limits
+ * the window size to 64K, which is quite useful on MSDOS.
+ * To do: use the user input buffer as sliding window.
+ */
+
+ ulg window_size;
+ /* Actual size of window: 2*wSize, except when the user input buffer
+ * is directly used as sliding window.
+ */
+
+ Posf *prev;
+ /* Link to older string with same hash index. To limit the size of this
+ * array to 64K, this link is maintained only for the last 32K strings.
+ * An index in this array is thus a window index modulo 32K.
+ */
+
+ Posf *head; /* Heads of the hash chains or NIL. */
+
+ uInt ins_h; /* hash index of string to be inserted */
+ uInt hash_size; /* number of elements in hash table */
+ uInt hash_bits; /* log2(hash_size) */
+ uInt hash_mask; /* hash_size-1 */
+
+ uInt hash_shift;
+ /* Number of bits by which ins_h must be shifted at each input
+ * step. It must be such that after MIN_MATCH steps, the oldest
+ * byte no longer takes part in the hash key, that is:
+ * hash_shift * MIN_MATCH >= hash_bits
+ */
+
+ long block_start;
+ /* Window position at the beginning of the current output block. Gets
+ * negative when the window is moved backwards.
+ */
+
+ uInt match_length; /* length of best match */
+ IPos prev_match; /* previous match */
+ int match_available; /* set if previous match exists */
+ uInt strstart; /* start of string to insert */
+ uInt match_start; /* start of matching string */
+ uInt lookahead; /* number of valid bytes ahead in window */
+
+ uInt prev_length;
+ /* Length of the best match at previous step. Matches not greater than this
+ * are discarded. This is used in the lazy match evaluation.
+ */
+
+ uInt max_chain_length;
+ /* To speed up deflation, hash chains are never searched beyond this
+ * length. A higher limit improves compression ratio but degrades the
+ * speed.
+ */
+
+ uInt max_lazy_match;
+ /* Attempt to find a better match only when the current match is strictly
+ * smaller than this value. This mechanism is used only for compression
+ * levels >= 4.
+ */
+# define max_insert_length max_lazy_match
+ /* Insert new strings in the hash table only if the match length is not
+ * greater than this length. This saves time but degrades compression.
+ * max_insert_length is used only for compression levels <= 3.
+ */
+
+ int level; /* compression level (1..9) */
+ int strategy; /* favor or force Huffman coding*/
+
+ uInt good_match;
+ /* Use a faster search when the previous match is longer than this */
+
+ int nice_match; /* Stop searching when current match exceeds this */
+
+ /* used by trees.c: */
+ /* Didn't use ct_data typedef below to supress compiler warning */
+ struct ct_data_s dyn_ltree[HEAP_SIZE]; /* literal and length tree */
+ struct ct_data_s dyn_dtree[2*D_CODES+1]; /* distance tree */
+ struct ct_data_s bl_tree[2*BL_CODES+1]; /* Huffman tree for bit lengths */
+
+ struct tree_desc_s l_desc; /* desc. for literal tree */
+ struct tree_desc_s d_desc; /* desc. for distance tree */
+ struct tree_desc_s bl_desc; /* desc. for bit length tree */
+
+ ush bl_count[MAX_BITS+1];
+ /* number of codes at each bit length for an optimal tree */
+
+ int heap[2*L_CODES+1]; /* heap used to build the Huffman trees */
+ int heap_len; /* number of elements in the heap */
+ int heap_max; /* element of largest frequency */
+ /* The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used.
+ * The same heap array is used to build all trees.
+ */
+
+ uch depth[2*L_CODES+1];
+ /* Depth of each subtree used as tie breaker for trees of equal frequency
+ */
+
+ uchf *l_buf; /* buffer for literals or lengths */
+
+ uInt lit_bufsize;
+ /* Size of match buffer for literals/lengths. There are 4 reasons for
+ * limiting lit_bufsize to 64K:
+ * - frequencies can be kept in 16 bit counters
+ * - if compression is not successful for the first block, all input
+ * data is still in the window so we can still emit a stored block even
+ * when input comes from standard input. (This can also be done for
+ * all blocks if lit_bufsize is not greater than 32K.)
+ * - if compression is not successful for a file smaller than 64K, we can
+ * even emit a stored file instead of a stored block (saving 5 bytes).
+ * This is applicable only for zip (not gzip or zlib).
+ * - creating new Huffman trees less frequently may not provide fast
+ * adaptation to changes in the input data statistics. (Take for
+ * example a binary file with poorly compressible code followed by
+ * a highly compressible string table.) Smaller buffer sizes give
+ * fast adaptation but have of course the overhead of transmitting
+ * trees more frequently.
+ * - I can't count above 4
+ */
+
+ uInt last_lit; /* running index in l_buf */
+
+ ushf *d_buf;
+ /* Buffer for distances. To simplify the code, d_buf and l_buf have
+ * the same number of elements. To use different lengths, an extra flag
+ * array would be necessary.
+ */
+
+ ulg opt_len; /* bit length of current block with optimal trees */
+ ulg static_len; /* bit length of current block with static trees */
+ uInt matches; /* number of string matches in current block */
+ int last_eob_len; /* bit length of EOB code for last block */
+
+#ifdef DEBUG
+ ulg compressed_len; /* total bit length of compressed file mod 2^32 */
+ ulg bits_sent; /* bit length of compressed data sent mod 2^32 */
+#endif
+
+ ush bi_buf;
+ /* Output buffer. bits are inserted starting at the bottom (least
+ * significant bits).
+ */
+ int bi_valid;
+ /* Number of valid bits in bi_buf. All bits above the last valid bit
+ * are always zero.
+ */
+
+} FAR deflate_state;
+
+/* Output a byte on the stream.
+ * IN assertion: there is enough room in pending_buf.
+ */
+#define put_byte(s, c) {s->pending_buf[s->pending++] = (c);}
+
+
+#define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1)
+/* Minimum amount of lookahead, except at the end of the input file.
+ * See deflate.c for comments about the MIN_MATCH+1.
+ */
+
+#define MAX_DIST(s) ((s)->w_size-MIN_LOOKAHEAD)
+/* In order to simplify the code, particularly on 16 bit machines, match
+ * distances are limited to MAX_DIST instead of WSIZE.
+ */
+
+ /* in trees.c */
+void _tr_init OF((deflate_state *s));
+int _tr_tally OF((deflate_state *s, unsigned dist, unsigned lc));
+void _tr_flush_block OF((deflate_state *s, charf *buf, ulg stored_len,
+ int eof));
+void _tr_align OF((deflate_state *s));
+void _tr_stored_block OF((deflate_state *s, charf *buf, ulg stored_len,
+ int eof));
+
+#define d_code(dist) \
+ ((dist) < 256 ? _dist_code[dist] : _dist_code[256+((dist)>>7)])
+/* Mapping from a distance to a distance code. dist is the distance - 1 and
+ * must not have side effects. _dist_code[256] and _dist_code[257] are never
+ * used.
+ */
+
+#ifndef DEBUG
+/* Inline versions of _tr_tally for speed: */
+
+#if defined(GEN_TREES_H) || !defined(STDC)
+ extern uch _length_code[];
+ extern uch _dist_code[];
+#else
+ extern const uch _length_code[];
+ extern const uch _dist_code[];
+#endif
+
+# define _tr_tally_lit(s, c, flush) \
+ { uch cc = (c); \
+ s->d_buf[s->last_lit] = 0; \
+ s->l_buf[s->last_lit++] = cc; \
+ s->dyn_ltree[cc].Freq++; \
+ flush = (s->last_lit == s->lit_bufsize-1); \
+ }
+# define _tr_tally_dist(s, distance, length, flush) \
+ { uch len = (length); \
+ ush dist = (distance); \
+ s->d_buf[s->last_lit] = dist; \
+ s->l_buf[s->last_lit++] = len; \
+ dist--; \
+ s->dyn_ltree[_length_code[len]+LITERALS+1].Freq++; \
+ s->dyn_dtree[d_code(dist)].Freq++; \
+ flush = (s->last_lit == s->lit_bufsize-1); \
+ }
+#else
+# define _tr_tally_lit(s, c, flush) flush = _tr_tally(s, 0, c)
+# define _tr_tally_dist(s, distance, length, flush) \
+ flush = _tr_tally(s, distance, length)
+#endif
+
+#endif /* DEFLATE_H */
diff --git a/src/zlib/example.c b/src/zlib/example.c
new file mode 100755
index 0000000..a1ab11e
--- /dev/null
+++ b/src/zlib/example.c
@@ -0,0 +1,567 @@
+/* example.c -- usage example of the zlib compression library
+ * Copyright (C) 1995-2003 Jean-loup Gailly.
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* @(#) $ Id$ */
+
+#include <stdio.h>
+#include "zlib.h"
+
+#ifdef STDC
+# include <string.h>
+# include <stdlib.h>
+#else
+ extern void exit OF((int));
+#endif
+
+#if defined(VMS) || defined(RISCOS)
+# define TESTFILE "foo-gz"
+#else
+# define TESTFILE "foo.gz"
+#endif
+
+#define CHECK_ERR(err, msg) { \
+ if (err != Z_OK) { \
+ fprintf(stderr, "%s error: %d\n", msg, err); \
+ exit(1); \
+ } \
+}
+
+const char hello[] = "hello, hello!";
+/* "hello world" would be more standard, but the repeated "hello"
+ * stresses the compression code better, sorry...
+ */
+
+const char dictionary[] = "hello";
+uLong dictId; /* Adler32 value of the dictionary */
+
+void test_compress OF((Byte *compr, uLong comprLen,
+ Byte *uncompr, uLong uncomprLen));
+void test_gzio OF((const char *fname,
+ Byte *uncompr, uLong uncomprLen));
+void test_deflate OF((Byte *compr, uLong comprLen));
+void test_inflate OF((Byte *compr, uLong comprLen,
+ Byte *uncompr, uLong uncomprLen));
+void test_large_deflate OF((Byte *compr, uLong comprLen,
+ Byte *uncompr, uLong uncomprLen));
+void test_large_inflate OF((Byte *compr, uLong comprLen,
+ Byte *uncompr, uLong uncomprLen));
+void test_flush OF((Byte *compr, uLong *comprLen));
+void test_sync OF((Byte *compr, uLong comprLen,
+ Byte *uncompr, uLong uncomprLen));
+void test_dict_deflate OF((Byte *compr, uLong comprLen));
+void test_dict_inflate OF((Byte *compr, uLong comprLen,
+ Byte *uncompr, uLong uncomprLen));
+int main OF((int argc, char *argv[]));
+
+/* ===========================================================================
+ * Test compress() and uncompress()
+ */
+void test_compress(compr, comprLen, uncompr, uncomprLen)
+ Byte *compr, *uncompr;
+ uLong comprLen, uncomprLen;
+{
+ int err;
+ uLong len = (uLong)strlen(hello)+1;
+
+ err = compress(compr, &comprLen, (const Bytef*)hello, len);
+ CHECK_ERR(err, "compress");
+
+ strcpy((char*)uncompr, "garbage");
+
+ err = uncompress(uncompr, &uncomprLen, compr, comprLen);
+ CHECK_ERR(err, "uncompress");
+
+ if (strcmp((char*)uncompr, hello)) {
+ fprintf(stderr, "bad uncompress\n");
+ exit(1);
+ } else {
+ printf("uncompress(): %s\n", (char *)uncompr);
+ }
+}
+
+/* ===========================================================================
+ * Test read/write of .gz files
+ */
+void test_gzio(fname, uncompr, uncomprLen)
+ const char *fname; /* compressed file name */
+ Byte *uncompr;
+ uLong uncomprLen;
+{
+#ifdef NO_GZCOMPRESS
+ fprintf(stderr, "NO_GZCOMPRESS -- gz* functions cannot compress\n");
+#else
+ int err;
+ int len = (int)strlen(hello)+1;
+ gzFile file;
+ z_off_t pos;
+
+ file = gzopen(fname, "wb");
+ if (file == NULL) {
+ fprintf(stderr, "gzopen error\n");
+ exit(1);
+ }
+ gzputc(file, 'h');
+ if (gzputs(file, "ello") != 4) {
+ fprintf(stderr, "gzputs err: %s\n", gzerror(file, &err));
+ exit(1);
+ }
+ if (gzprintf(file, ", %s!", "hello") != 8) {
+ fprintf(stderr, "gzprintf err: %s\n", gzerror(file, &err));
+ exit(1);
+ }
+ gzseek(file, 1L, SEEK_CUR); /* add one zero byte */
+ gzclose(file);
+
+ file = gzopen(fname, "rb");
+ if (file == NULL) {
+ fprintf(stderr, "gzopen error\n");
+ exit(1);
+ }
+ strcpy((char*)uncompr, "garbage");
+
+ if (gzread(file, uncompr, (unsigned)uncomprLen) != len) {
+ fprintf(stderr, "gzread err: %s\n", gzerror(file, &err));
+ exit(1);
+ }
+ if (strcmp((char*)uncompr, hello)) {
+ fprintf(stderr, "bad gzread: %s\n", (char*)uncompr);
+ exit(1);
+ } else {
+ printf("gzread(): %s\n", (char*)uncompr);
+ }
+
+ pos = gzseek(file, -8L, SEEK_CUR);
+ if (pos != 6 || gztell(file) != pos) {
+ fprintf(stderr, "gzseek error, pos=%ld, gztell=%ld\n",
+ (long)pos, (long)gztell(file));
+ exit(1);
+ }
+
+ if (gzgetc(file) != ' ') {
+ fprintf(stderr, "gzgetc error\n");
+ exit(1);
+ }
+
+ if (gzungetc(' ', file) != ' ') {
+ fprintf(stderr, "gzungetc error\n");
+ exit(1);
+ }
+
+ gzgets(file, (char*)uncompr, (int)uncomprLen);
+ if (strlen((char*)uncompr) != 7) { /* " hello!" */
+ fprintf(stderr, "gzgets err after gzseek: %s\n", gzerror(file, &err));
+ exit(1);
+ }
+ if (strcmp((char*)uncompr, hello + 6)) {
+ fprintf(stderr, "bad gzgets after gzseek\n");
+ exit(1);
+ } else {
+ printf("gzgets() after gzseek: %s\n", (char*)uncompr);
+ }
+
+ gzclose(file);
+#endif
+}
+
+/* ===========================================================================
+ * Test deflate() with small buffers
+ */
+void test_deflate(compr, comprLen)
+ Byte *compr;
+ uLong comprLen;
+{
+ z_stream c_stream; /* compression stream */
+ int err;
+ uLong len = (uLong)strlen(hello)+1;
+
+ c_stream.zalloc = (alloc_func)0;
+ c_stream.zfree = (free_func)0;
+ c_stream.opaque = (voidpf)0;
+
+ err = deflateInit(&c_stream, Z_DEFAULT_COMPRESSION);
+ CHECK_ERR(err, "deflateInit");
+
+ c_stream.next_in = (Bytef*)hello;
+ c_stream.next_out = compr;
+
+ while (c_stream.total_in != len && c_stream.total_out < comprLen) {
+ c_stream.avail_in = c_stream.avail_out = 1; /* force small buffers */
+ err = deflate(&c_stream, Z_NO_FLUSH);
+ CHECK_ERR(err, "deflate");
+ }
+ /* Finish the stream, still forcing small buffers: */
+ for (;;) {
+ c_stream.avail_out = 1;
+ err = deflate(&c_stream, Z_FINISH);
+ if (err == Z_STREAM_END) break;
+ CHECK_ERR(err, "deflate");
+ }
+
+ err = deflateEnd(&c_stream);
+ CHECK_ERR(err, "deflateEnd");
+}
+
+/* ===========================================================================
+ * Test inflate() with small buffers
+ */
+void test_inflate(compr, comprLen, uncompr, uncomprLen)
+ Byte *compr, *uncompr;
+ uLong comprLen, uncomprLen;
+{
+ int err;
+ z_stream d_stream; /* decompression stream */
+
+ strcpy((char*)uncompr, "garbage");
+
+ d_stream.zalloc = (alloc_func)0;
+ d_stream.zfree = (free_func)0;
+ d_stream.opaque = (voidpf)0;
+
+ d_stream.next_in = compr;
+ d_stream.avail_in = 0;
+ d_stream.next_out = uncompr;
+
+ err = inflateInit(&d_stream);
+ CHECK_ERR(err, "inflateInit");
+
+ while (d_stream.total_out < uncomprLen && d_stream.total_in < comprLen) {
+ d_stream.avail_in = d_stream.avail_out = 1; /* force small buffers */
+ err = inflate(&d_stream, Z_NO_FLUSH);
+ if (err == Z_STREAM_END) break;
+ CHECK_ERR(err, "inflate");
+ }
+
+ err = inflateEnd(&d_stream);
+ CHECK_ERR(err, "inflateEnd");
+
+ if (strcmp((char*)uncompr, hello)) {
+ fprintf(stderr, "bad inflate\n");
+ exit(1);
+ } else {
+ printf("inflate(): %s\n", (char *)uncompr);
+ }
+}
+
+/* ===========================================================================
+ * Test deflate() with large buffers and dynamic change of compression level
+ */
+void test_large_deflate(compr, comprLen, uncompr, uncomprLen)
+ Byte *compr, *uncompr;
+ uLong comprLen, uncomprLen;
+{
+ z_stream c_stream; /* compression stream */
+ int err;
+
+ c_stream.zalloc = (alloc_func)0;
+ c_stream.zfree = (free_func)0;
+ c_stream.opaque = (voidpf)0;
+
+ err = deflateInit(&c_stream, Z_BEST_SPEED);
+ CHECK_ERR(err, "deflateInit");
+
+ c_stream.next_out = compr;
+ c_stream.avail_out = (uInt)comprLen;
+
+ /* At this point, uncompr is still mostly zeroes, so it should compress
+ * very well:
+ */
+ c_stream.next_in = uncompr;
+ c_stream.avail_in = (uInt)uncomprLen;
+ err = deflate(&c_stream, Z_NO_FLUSH);
+ CHECK_ERR(err, "deflate");
+ if (c_stream.avail_in != 0) {
+ fprintf(stderr, "deflate not greedy\n");
+ exit(1);
+ }
+
+ /* Feed in already compressed data and switch to no compression: */
+ deflateParams(&c_stream, Z_NO_COMPRESSION, Z_DEFAULT_STRATEGY);
+ c_stream.next_in = compr;
+ c_stream.avail_in = (uInt)comprLen/2;
+ err = deflate(&c_stream, Z_NO_FLUSH);
+ CHECK_ERR(err, "deflate");
+
+ /* Switch back to compressing mode: */
+ deflateParams(&c_stream, Z_BEST_COMPRESSION, Z_FILTERED);
+ c_stream.next_in = uncompr;
+ c_stream.avail_in = (uInt)uncomprLen;
+ err = deflate(&c_stream, Z_NO_FLUSH);
+ CHECK_ERR(err, "deflate");
+
+ err = deflate(&c_stream, Z_FINISH);
+ if (err != Z_STREAM_END) {
+ fprintf(stderr, "deflate should report Z_STREAM_END\n");
+ exit(1);
+ }
+ err = deflateEnd(&c_stream);
+ CHECK_ERR(err, "deflateEnd");
+}
+
+/* ===========================================================================
+ * Test inflate() with large buffers
+ */
+void test_large_inflate(compr, comprLen, uncompr, uncomprLen)
+ Byte *compr, *uncompr;
+ uLong comprLen, uncomprLen;
+{
+ int err;
+ z_stream d_stream; /* decompression stream */
+
+ strcpy((char*)uncompr, "garbage");
+
+ d_stream.zalloc = (alloc_func)0;
+ d_stream.zfree = (free_func)0;
+ d_stream.opaque = (voidpf)0;
+
+ d_stream.next_in = compr;
+ d_stream.avail_in = (uInt)comprLen;
+
+ err = inflateInit(&d_stream);
+ CHECK_ERR(err, "inflateInit");
+
+ for (;;) {
+ d_stream.next_out = uncompr; /* discard the output */
+ d_stream.avail_out = (uInt)uncomprLen;
+ err = inflate(&d_stream, Z_NO_FLUSH);
+ if (err == Z_STREAM_END) break;
+ CHECK_ERR(err, "large inflate");
+ }
+
+ err = inflateEnd(&d_stream);
+ CHECK_ERR(err, "inflateEnd");
+
+ if (d_stream.total_out != 2*uncomprLen + comprLen/2) {
+ fprintf(stderr, "bad large inflate: %ld\n", d_stream.total_out);
+ exit(1);
+ } else {
+ printf("large_inflate(): OK\n");
+ }
+}
+
+/* ===========================================================================
+ * Test deflate() with full flush
+ */
+void test_flush(compr, comprLen)
+ Byte *compr;
+ uLong *comprLen;
+{
+ z_stream c_stream; /* compression stream */
+ int err;
+ uInt len = (uInt)strlen(hello)+1;
+
+ c_stream.zalloc = (alloc_func)0;
+ c_stream.zfree = (free_func)0;
+ c_stream.opaque = (voidpf)0;
+
+ err = deflateInit(&c_stream, Z_DEFAULT_COMPRESSION);
+ CHECK_ERR(err, "deflateInit");
+
+ c_stream.next_in = (Bytef*)hello;
+ c_stream.next_out = compr;
+ c_stream.avail_in = 3;
+ c_stream.avail_out = (uInt)*comprLen;
+ err = deflate(&c_stream, Z_FULL_FLUSH);
+ CHECK_ERR(err, "deflate");
+
+ compr[3]++; /* force an error in first compressed block */
+ c_stream.avail_in = len - 3;
+
+ err = deflate(&c_stream, Z_FINISH);
+ if (err != Z_STREAM_END) {
+ CHECK_ERR(err, "deflate");
+ }
+ err = deflateEnd(&c_stream);
+ CHECK_ERR(err, "deflateEnd");
+
+ *comprLen = c_stream.total_out;
+}
+
+/* ===========================================================================
+ * Test inflateSync()
+ */
+void test_sync(compr, comprLen, uncompr, uncomprLen)
+ Byte *compr, *uncompr;
+ uLong comprLen, uncomprLen;
+{
+ int err;
+ z_stream d_stream; /* decompression stream */
+
+ strcpy((char*)uncompr, "garbage");
+
+ d_stream.zalloc = (alloc_func)0;
+ d_stream.zfree = (free_func)0;
+ d_stream.opaque = (voidpf)0;
+
+ d_stream.next_in = compr;
+ d_stream.avail_in = 2; /* just read the zlib header */
+
+ err = inflateInit(&d_stream);
+ CHECK_ERR(err, "inflateInit");
+
+ d_stream.next_out = uncompr;
+ d_stream.avail_out = (uInt)uncomprLen;
+
+ inflate(&d_stream, Z_NO_FLUSH);
+ CHECK_ERR(err, "inflate");
+
+ d_stream.avail_in = (uInt)comprLen-2; /* read all compressed data */
+ err = inflateSync(&d_stream); /* but skip the damaged part */
+ CHECK_ERR(err, "inflateSync");
+
+ err = inflate(&d_stream, Z_FINISH);
+ if (err != Z_DATA_ERROR) {
+ fprintf(stderr, "inflate should report DATA_ERROR\n");
+ /* Because of incorrect adler32 */
+ exit(1);
+ }
+ err = inflateEnd(&d_stream);
+ CHECK_ERR(err, "inflateEnd");
+
+ printf("after inflateSync(): hel%s\n", (char *)uncompr);
+}
+
+/* ===========================================================================
+ * Test deflate() with preset dictionary
+ */
+void test_dict_deflate(compr, comprLen)
+ Byte *compr;
+ uLong comprLen;
+{
+ z_stream c_stream; /* compression stream */
+ int err;
+
+ c_stream.zalloc = (alloc_func)0;
+ c_stream.zfree = (free_func)0;
+ c_stream.opaque = (voidpf)0;
+
+ err = deflateInit(&c_stream, Z_BEST_COMPRESSION);
+ CHECK_ERR(err, "deflateInit");
+
+ err = deflateSetDictionary(&c_stream,
+ (const Bytef*)dictionary, sizeof(dictionary));
+ CHECK_ERR(err, "deflateSetDictionary");
+
+ dictId = c_stream.adler;
+ c_stream.next_out = compr;
+ c_stream.avail_out = (uInt)comprLen;
+
+ c_stream.next_in = (Bytef*)hello;
+ c_stream.avail_in = (uInt)strlen(hello)+1;
+
+ err = deflate(&c_stream, Z_FINISH);
+ if (err != Z_STREAM_END) {
+ fprintf(stderr, "deflate should report Z_STREAM_END\n");
+ exit(1);
+ }
+ err = deflateEnd(&c_stream);
+ CHECK_ERR(err, "deflateEnd");
+}
+
+/* ===========================================================================
+ * Test inflate() with a preset dictionary
+ */
+void test_dict_inflate(compr, comprLen, uncompr, uncomprLen)
+ Byte *compr, *uncompr;
+ uLong comprLen, uncomprLen;
+{
+ int err;
+ z_stream d_stream; /* decompression stream */
+
+ strcpy((char*)uncompr, "garbage");
+
+ d_stream.zalloc = (alloc_func)0;
+ d_stream.zfree = (free_func)0;
+ d_stream.opaque = (voidpf)0;
+
+ d_stream.next_in = compr;
+ d_stream.avail_in = (uInt)comprLen;
+
+ err = inflateInit(&d_stream);
+ CHECK_ERR(err, "inflateInit");
+
+ d_stream.next_out = uncompr;
+ d_stream.avail_out = (uInt)uncomprLen;
+
+ for (;;) {
+ err = inflate(&d_stream, Z_NO_FLUSH);
+ if (err == Z_STREAM_END) break;
+ if (err == Z_NEED_DICT) {
+ if (d_stream.adler != dictId) {
+ fprintf(stderr, "unexpected dictionary");
+ exit(1);
+ }
+ err = inflateSetDictionary(&d_stream, (const Bytef*)dictionary,
+ sizeof(dictionary));
+ }
+ CHECK_ERR(err, "inflate with dict");
+ }
+
+ err = inflateEnd(&d_stream);
+ CHECK_ERR(err, "inflateEnd");
+
+ if (strcmp((char*)uncompr, hello)) {
+ fprintf(stderr, "bad inflate with dict\n");
+ exit(1);
+ } else {
+ printf("inflate with dictionary: %s\n", (char *)uncompr);
+ }
+}
+
+/* ===========================================================================
+ * Usage: example [output.gz [input.gz]]
+ */
+
+int main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ Byte *compr, *uncompr;
+ uLong comprLen = 10000*sizeof(int); /* don't overflow on MSDOS */
+ uLong uncomprLen = comprLen;
+ static const char* myVersion = ZLIB_VERSION;
+
+ if (zlibVersion()[0] != myVersion[0]) {
+ fprintf(stderr, "incompatible zlib version\n");
+ exit(1);
+
+ } else if (strcmp(zlibVersion(), ZLIB_VERSION) != 0) {
+ fprintf(stderr, "warning: different zlib version\n");
+ }
+
+ printf("zlib version %s = 0x%04x, compile flags = 0x%lx\n",
+ ZLIB_VERSION, ZLIB_VERNUM, zlibCompileFlags());
+
+ compr = (Byte*)calloc((uInt)comprLen, 1);
+ uncompr = (Byte*)calloc((uInt)uncomprLen, 1);
+ /* compr and uncompr are cleared to avoid reading uninitialized
+ * data and to ensure that uncompr compresses well.
+ */
+ if (compr == Z_NULL || uncompr == Z_NULL) {
+ printf("out of memory\n");
+ exit(1);
+ }
+ test_compress(compr, comprLen, uncompr, uncomprLen);
+
+ test_gzio((argc > 1 ? argv[1] : TESTFILE),
+ uncompr, uncomprLen);
+
+ test_deflate(compr, comprLen);
+ test_inflate(compr, comprLen, uncompr, uncomprLen);
+
+ test_large_deflate(compr, comprLen, uncompr, uncomprLen);
+ test_large_inflate(compr, comprLen, uncompr, uncomprLen);
+
+ test_flush(compr, &comprLen);
+ test_sync(compr, comprLen, uncompr, uncomprLen);
+ comprLen = uncomprLen;
+
+ test_dict_deflate(compr, comprLen);
+ test_dict_inflate(compr, comprLen, uncompr, uncomprLen);
+
+ free(compr);
+ free(uncompr);
+
+ return 0;
+}
diff --git a/src/zlib/gzio.c b/src/zlib/gzio.c
new file mode 100755
index 0000000..14cf7b1
--- /dev/null
+++ b/src/zlib/gzio.c
@@ -0,0 +1,1005 @@
+/* gzio.c -- IO on .gz files
+ * Copyright (C) 1995-2003 Jean-loup Gailly.
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ *
+ * Compile this file with -DNO_GZCOMPRESS to avoid the compression code.
+ */
+
+/* @(#) $ Id$ */
+
+#include <stdio.h>
+
+#include "zutil.h"
+
+#ifdef NO_DEFLATE /* for compatiblity with old definition */
+# define NO_GZCOMPRESS
+#endif
+
+#ifndef NO_DUMMY_DECL
+struct internal_state {int dummy;}; /* for buggy compilers */
+#endif
+
+#ifndef Z_BUFSIZE
+# ifdef MAXSEG_64K
+# define Z_BUFSIZE 4096 /* minimize memory usage for 16-bit DOS */
+# else
+# define Z_BUFSIZE 16384
+# endif
+#endif
+#ifndef Z_PRINTF_BUFSIZE
+# define Z_PRINTF_BUFSIZE 4096
+#endif
+
+#ifdef __MVS__
+# pragma map (fdopen , "\174\174FDOPEN")
+ FILE *fdopen(int, const char *);
+#endif
+
+#ifndef STDC
+extern voidp malloc OF((uInt size));
+extern void free OF((voidpf ptr));
+#endif
+
+#define ALLOC(size) malloc(size)
+#define TRYFREE(p) {if (p) free(p);}
+
+static int const gz_magic[2] = {0x1f, 0x8b}; /* gzip magic header */
+
+/* gzip flag byte */
+#define ASCII_FLAG 0x01 /* bit 0 set: file probably ascii text */
+#define HEAD_CRC 0x02 /* bit 1 set: header CRC present */
+#define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */
+#define ORIG_NAME 0x08 /* bit 3 set: original file name present */
+#define COMMENT 0x10 /* bit 4 set: file comment present */
+#define RESERVED 0xE0 /* bits 5..7: reserved */
+
+typedef struct gz_stream {
+ z_stream stream;
+ int z_err; /* error code for last stream operation */
+ int z_eof; /* set if end of input file */
+ FILE *file; /* .gz file */
+ Byte *inbuf; /* input buffer */
+ Byte *outbuf; /* output buffer */
+ uLong crc; /* crc32 of uncompressed data */
+ char *msg; /* error message */
+ char *path; /* path name for debugging only */
+ int transparent; /* 1 if input file is not a .gz file */
+ char mode; /* 'w' or 'r' */
+ z_off_t start; /* start of compressed data in file (header skipped) */
+ z_off_t in; /* bytes into deflate or inflate */
+ z_off_t out; /* bytes out of deflate or inflate */
+ int back; /* one character push-back */
+ int last; /* true if push-back is last character */
+} gz_stream;
+
+
+local gzFile gz_open OF((const char *path, const char *mode, int fd));
+local int do_flush OF((gzFile file, int flush));
+local int get_byte OF((gz_stream *s));
+local void check_header OF((gz_stream *s));
+local int destroy OF((gz_stream *s));
+local void putLong OF((FILE *file, uLong x));
+local uLong getLong OF((gz_stream *s));
+
+/* ===========================================================================
+ Opens a gzip (.gz) file for reading or writing. The mode parameter
+ is as in fopen ("rb" or "wb"). The file is given either by file descriptor
+ or path name (if fd == -1).
+ gz_open returns NULL if the file could not be opened or if there was
+ insufficient memory to allocate the (de)compression state; errno
+ can be checked to distinguish the two cases (if errno is zero, the
+ zlib error is Z_MEM_ERROR).
+*/
+local gzFile gz_open (path, mode, fd)
+ const char *path;
+ const char *mode;
+ int fd;
+{
+ int err;
+ int level = Z_DEFAULT_COMPRESSION; /* compression level */
+ int strategy = Z_DEFAULT_STRATEGY; /* compression strategy */
+ char *p = (char*)mode;
+ gz_stream *s;
+ char fmode[80]; /* copy of mode, without the compression level */
+ char *m = fmode;
+
+ if (!path || !mode) return Z_NULL;
+
+ s = (gz_stream *)ALLOC(sizeof(gz_stream));
+ if (!s) return Z_NULL;
+
+ s->stream.zalloc = (alloc_func)0;
+ s->stream.zfree = (free_func)0;
+ s->stream.opaque = (voidpf)0;
+ s->stream.next_in = s->inbuf = Z_NULL;
+ s->stream.next_out = s->outbuf = Z_NULL;
+ s->stream.avail_in = s->stream.avail_out = 0;
+ s->file = NULL;
+ s->z_err = Z_OK;
+ s->z_eof = 0;
+ s->in = 0;
+ s->out = 0;
+ s->back = EOF;
+ s->crc = crc32(0L, Z_NULL, 0);
+ s->msg = NULL;
+ s->transparent = 0;
+
+ s->path = (char*)ALLOC(strlen(path)+1);
+ if (s->path == NULL) {
+ return destroy(s), (gzFile)Z_NULL;
+ }
+ strcpy(s->path, path); /* do this early for debugging */
+
+ s->mode = '\0';
+ do {
+ if (*p == 'r') s->mode = 'r';
+ if (*p == 'w' || *p == 'a') s->mode = 'w';
+ if (*p >= '0' && *p <= '9') {
+ level = *p - '0';
+ } else if (*p == 'f') {
+ strategy = Z_FILTERED;
+ } else if (*p == 'h') {
+ strategy = Z_HUFFMAN_ONLY;
+ } else if (*p == 'R') {
+ strategy = Z_RLE;
+ } else {
+ *m++ = *p; /* copy the mode */
+ }
+ } while (*p++ && m != fmode + sizeof(fmode));
+ if (s->mode == '\0') return destroy(s), (gzFile)Z_NULL;
+
+ if (s->mode == 'w') {
+#ifdef NO_GZCOMPRESS
+ err = Z_STREAM_ERROR;
+#else
+ err = deflateInit2(&(s->stream), level,
+ Z_DEFLATED, -MAX_WBITS, DEF_MEM_LEVEL, strategy);
+ /* windowBits is passed < 0 to suppress zlib header */
+
+ s->stream.next_out = s->outbuf = (Byte*)ALLOC(Z_BUFSIZE);
+#endif
+ if (err != Z_OK || s->outbuf == Z_NULL) {
+ return destroy(s), (gzFile)Z_NULL;
+ }
+ } else {
+ s->stream.next_in = s->inbuf = (Byte*)ALLOC(Z_BUFSIZE);
+
+ err = inflateInit2(&(s->stream), -MAX_WBITS);
+ /* windowBits is passed < 0 to tell that there is no zlib header.
+ * Note that in this case inflate *requires* an extra "dummy" byte
+ * after the compressed stream in order to complete decompression and
+ * return Z_STREAM_END. Here the gzip CRC32 ensures that 4 bytes are
+ * present after the compressed stream.
+ */
+ if (err != Z_OK || s->inbuf == Z_NULL) {
+ return destroy(s), (gzFile)Z_NULL;
+ }
+ }
+ s->stream.avail_out = Z_BUFSIZE;
+
+ errno = 0;
+ s->file = fd < 0 ? F_OPEN(path, fmode) : (FILE*)fdopen(fd, fmode);
+
+ if (s->file == NULL) {
+ return destroy(s), (gzFile)Z_NULL;
+ }
+ if (s->mode == 'w') {
+ /* Write a very simple .gz header:
+ */
+ fprintf(s->file, "%c%c%c%c%c%c%c%c%c%c", gz_magic[0], gz_magic[1],
+ Z_DEFLATED, 0 /*flags*/, 0,0,0,0 /*time*/, 0 /*xflags*/, OS_CODE);
+ s->start = 10L;
+ /* We use 10L instead of ftell(s->file) to because ftell causes an
+ * fflush on some systems. This version of the library doesn't use
+ * start anyway in write mode, so this initialization is not
+ * necessary.
+ */
+ } else {
+ check_header(s); /* skip the .gz header */
+ s->start = ftell(s->file) - s->stream.avail_in;
+ }
+
+ return (gzFile)s;
+}
+
+/* ===========================================================================
+ Opens a gzip (.gz) file for reading or writing.
+*/
+gzFile ZEXPORT gzopen (path, mode)
+ const char *path;
+ const char *mode;
+{
+ return gz_open (path, mode, -1);
+}
+
+/* ===========================================================================
+ Associate a gzFile with the file descriptor fd. fd is not dup'ed here
+ to mimic the behavio(u)r of fdopen.
+*/
+gzFile ZEXPORT gzdopen (fd, mode)
+ int fd;
+ const char *mode;
+{
+ char name[20];
+
+ if (fd < 0) return (gzFile)Z_NULL;
+ sprintf(name, "<fd:%d>", fd); /* for debugging */
+
+ return gz_open (name, mode, fd);
+}
+
+/* ===========================================================================
+ * Update the compression level and strategy
+ */
+int ZEXPORT gzsetparams (file, level, strategy)
+ gzFile file;
+ int level;
+ int strategy;
+{
+ gz_stream *s = (gz_stream*)file;
+
+ if (s == NULL || s->mode != 'w') return Z_STREAM_ERROR;
+
+ /* Make room to allow flushing */
+ if (s->stream.avail_out == 0) {
+
+ s->stream.next_out = s->outbuf;
+ if (fwrite(s->outbuf, 1, Z_BUFSIZE, s->file) != Z_BUFSIZE) {
+ s->z_err = Z_ERRNO;
+ }
+ s->stream.avail_out = Z_BUFSIZE;
+ }
+
+ return deflateParams (&(s->stream), level, strategy);
+}
+
+/* ===========================================================================
+ Read a byte from a gz_stream; update next_in and avail_in. Return EOF
+ for end of file.
+ IN assertion: the stream s has been sucessfully opened for reading.
+*/
+local int get_byte(s)
+ gz_stream *s;
+{
+ if (s->z_eof) return EOF;
+ if (s->stream.avail_in == 0) {
+ errno = 0;
+ s->stream.avail_in = fread(s->inbuf, 1, Z_BUFSIZE, s->file);
+ if (s->stream.avail_in == 0) {
+ s->z_eof = 1;
+ if (ferror(s->file)) s->z_err = Z_ERRNO;
+ return EOF;
+ }
+ s->stream.next_in = s->inbuf;
+ }
+ s->stream.avail_in--;
+ return *(s->stream.next_in)++;
+}
+
+/* ===========================================================================
+ Check the gzip header of a gz_stream opened for reading. Set the stream
+ mode to transparent if the gzip magic header is not present; set s->err
+ to Z_DATA_ERROR if the magic header is present but the rest of the header
+ is incorrect.
+ IN assertion: the stream s has already been created sucessfully;
+ s->stream.avail_in is zero for the first time, but may be non-zero
+ for concatenated .gz files.
+*/
+local void check_header(s)
+ gz_stream *s;
+{
+ int method; /* method byte */
+ int flags; /* flags byte */
+ uInt len;
+ int c;
+
+ /* Assure two bytes in the buffer so we can peek ahead -- handle case
+ where first byte of header is at the end of the buffer after the last
+ gzip segment */
+ len = s->stream.avail_in;
+ if (len < 2) {
+ if (len) s->inbuf[0] = s->stream.next_in[0];
+ errno = 0;
+ len = fread(s->inbuf + len, 1, Z_BUFSIZE >> len, s->file);
+ if (len == 0 && ferror(s->file)) s->z_err = Z_ERRNO;
+ s->stream.avail_in += len;
+ s->stream.next_in = s->inbuf;
+ if (s->stream.avail_in < 2) {
+ s->transparent = s->stream.avail_in;
+ return;
+ }
+ }
+
+ /* Peek ahead to check the gzip magic header */
+ if (s->stream.next_in[0] != gz_magic[0] ||
+ s->stream.next_in[1] != gz_magic[1]) {
+ s->transparent = 1;
+ return;
+ }
+ s->stream.avail_in -= 2;
+ s->stream.next_in += 2;
+
+ /* Check the rest of the gzip header */
+ method = get_byte(s);
+ flags = get_byte(s);
+ if (method != Z_DEFLATED || (flags & RESERVED) != 0) {
+ s->z_err = Z_DATA_ERROR;
+ return;
+ }
+
+ /* Discard time, xflags and OS code: */
+ for (len = 0; len < 6; len++) (void)get_byte(s);
+
+ if ((flags & EXTRA_FIELD) != 0) { /* skip the extra field */
+ len = (uInt)get_byte(s);
+ len += ((uInt)get_byte(s))<<8;
+ /* len is garbage if EOF but the loop below will quit anyway */
+ while (len-- != 0 && get_byte(s) != EOF) ;
+ }
+ if ((flags & ORIG_NAME) != 0) { /* skip the original file name */
+ while ((c = get_byte(s)) != 0 && c != EOF) ;
+ }
+ if ((flags & COMMENT) != 0) { /* skip the .gz file comment */
+ while ((c = get_byte(s)) != 0 && c != EOF) ;
+ }
+ if ((flags & HEAD_CRC) != 0) { /* skip the header crc */
+ for (len = 0; len < 2; len++) (void)get_byte(s);
+ }
+ s->z_err = s->z_eof ? Z_DATA_ERROR : Z_OK;
+}
+
+ /* ===========================================================================
+ * Cleanup then free the given gz_stream. Return a zlib error code.
+ Try freeing in the reverse order of allocations.
+ */
+local int destroy (s)
+ gz_stream *s;
+{
+ int err = Z_OK;
+
+ if (!s) return Z_STREAM_ERROR;
+
+ TRYFREE(s->msg);
+
+ if (s->stream.state != NULL) {
+ if (s->mode == 'w') {
+#ifdef NO_GZCOMPRESS
+ err = Z_STREAM_ERROR;
+#else
+ err = deflateEnd(&(s->stream));
+#endif
+ } else if (s->mode == 'r') {
+ err = inflateEnd(&(s->stream));
+ }
+ }
+ if (s->file != NULL && fclose(s->file)) {
+#ifdef ESPIPE
+ if (errno != ESPIPE) /* fclose is broken for pipes in HP/UX */
+#endif
+ err = Z_ERRNO;
+ }
+ if (s->z_err < 0) err = s->z_err;
+
+ TRYFREE(s->inbuf);
+ TRYFREE(s->outbuf);
+ TRYFREE(s->path);
+ TRYFREE(s);
+ return err;
+}
+
+/* ===========================================================================
+ Reads the given number of uncompressed bytes from the compressed file.
+ gzread returns the number of bytes actually read (0 for end of file).
+*/
+int ZEXPORT gzread (file, buf, len)
+ gzFile file;
+ voidp buf;
+ unsigned len;
+{
+ gz_stream *s = (gz_stream*)file;
+ Bytef *start = (Bytef*)buf; /* starting point for crc computation */
+ Byte *next_out; /* == stream.next_out but not forced far (for MSDOS) */
+
+ if (s == NULL || s->mode != 'r') return Z_STREAM_ERROR;
+
+ if (s->z_err == Z_DATA_ERROR || s->z_err == Z_ERRNO) return -1;
+ if (s->z_err == Z_STREAM_END) return 0; /* EOF */
+
+ next_out = (Byte*)buf;
+ s->stream.next_out = (Bytef*)buf;
+ s->stream.avail_out = len;
+
+ if (s->stream.avail_out && s->back != EOF) {
+ *next_out++ = s->back;
+ s->stream.next_out++;
+ s->stream.avail_out--;
+ s->back = EOF;
+ s->out++;
+ if (s->last) {
+ s->z_err = Z_STREAM_END;
+ return 1;
+ }
+ }
+
+ while (s->stream.avail_out != 0) {
+
+ if (s->transparent) {
+ /* Copy first the lookahead bytes: */
+ uInt n = s->stream.avail_in;
+ if (n > s->stream.avail_out) n = s->stream.avail_out;
+ if (n > 0) {
+ zmemcpy(s->stream.next_out, s->stream.next_in, n);
+ next_out += n;
+ s->stream.next_out = next_out;
+ s->stream.next_in += n;
+ s->stream.avail_out -= n;
+ s->stream.avail_in -= n;
+ }
+ if (s->stream.avail_out > 0) {
+ s->stream.avail_out -= fread(next_out, 1, s->stream.avail_out,
+ s->file);
+ }
+ len -= s->stream.avail_out;
+ s->in += len;
+ s->out += len;
+ if (len == 0) s->z_eof = 1;
+ return (int)len;
+ }
+ if (s->stream.avail_in == 0 && !s->z_eof) {
+
+ errno = 0;
+ s->stream.avail_in = fread(s->inbuf, 1, Z_BUFSIZE, s->file);
+ if (s->stream.avail_in == 0) {
+ s->z_eof = 1;
+ if (ferror(s->file)) {
+ s->z_err = Z_ERRNO;
+ break;
+ }
+ }
+ s->stream.next_in = s->inbuf;
+ }
+ s->in += s->stream.avail_in;
+ s->out += s->stream.avail_out;
+ s->z_err = inflate(&(s->stream), Z_NO_FLUSH);
+ s->in -= s->stream.avail_in;
+ s->out -= s->stream.avail_out;
+
+ if (s->z_err == Z_STREAM_END) {
+ /* Check CRC and original size */
+ s->crc = crc32(s->crc, start, (uInt)(s->stream.next_out - start));
+ start = s->stream.next_out;
+
+ if (getLong(s) != s->crc) {
+ s->z_err = Z_DATA_ERROR;
+ } else {
+ (void)getLong(s);
+ /* The uncompressed length returned by above getlong() may be
+ * different from s->out in case of concatenated .gz files.
+ * Check for such files:
+ */
+ check_header(s);
+ if (s->z_err == Z_OK) {
+ inflateReset(&(s->stream));
+ s->crc = crc32(0L, Z_NULL, 0);
+ }
+ }
+ }
+ if (s->z_err != Z_OK || s->z_eof) break;
+ }
+ s->crc = crc32(s->crc, start, (uInt)(s->stream.next_out - start));
+
+ return (int)(len - s->stream.avail_out);
+}
+
+
+/* ===========================================================================
+ Reads one byte from the compressed file. gzgetc returns this byte
+ or -1 in case of end of file or error.
+*/
+int ZEXPORT gzgetc(file)
+ gzFile file;
+{
+ unsigned char c;
+
+ return gzread(file, &c, 1) == 1 ? c : -1;
+}
+
+
+/* ===========================================================================
+ Push one byte back onto the stream.
+*/
+int ZEXPORT gzungetc(c, file)
+ int c;
+ gzFile file;
+{
+ gz_stream *s = (gz_stream*)file;
+
+ if (s == NULL || s->mode != 'r' || c == EOF || s->back != EOF) return EOF;
+ s->back = c;
+ s->out--;
+ s->last = (s->z_err == Z_STREAM_END);
+ if (s->last) s->z_err = Z_OK;
+ s->z_eof = 0;
+ return c;
+}
+
+
+/* ===========================================================================
+ Reads bytes from the compressed file until len-1 characters are
+ read, or a newline character is read and transferred to buf, or an
+ end-of-file condition is encountered. The string is then terminated
+ with a null character.
+ gzgets returns buf, or Z_NULL in case of error.
+
+ The current implementation is not optimized at all.
+*/
+char * ZEXPORT gzgets(file, buf, len)
+ gzFile file;
+ char *buf;
+ int len;
+{
+ char *b = buf;
+ if (buf == Z_NULL || len <= 0) return Z_NULL;
+
+ while (--len > 0 && gzread(file, buf, 1) == 1 && *buf++ != '\n') ;
+ *buf = '\0';
+ return b == buf && len > 0 ? Z_NULL : b;
+}
+
+
+#ifndef NO_GZCOMPRESS
+/* ===========================================================================
+ Writes the given number of uncompressed bytes into the compressed file.
+ gzwrite returns the number of bytes actually written (0 in case of error).
+*/
+int ZEXPORT gzwrite (file, buf, len)
+ gzFile file;
+ voidpc buf;
+ unsigned len;
+{
+ gz_stream *s = (gz_stream*)file;
+
+ if (s == NULL || s->mode != 'w') return Z_STREAM_ERROR;
+
+ s->stream.next_in = (Bytef*)buf;
+ s->stream.avail_in = len;
+
+ while (s->stream.avail_in != 0) {
+
+ if (s->stream.avail_out == 0) {
+
+ s->stream.next_out = s->outbuf;
+ if (fwrite(s->outbuf, 1, Z_BUFSIZE, s->file) != Z_BUFSIZE) {
+ s->z_err = Z_ERRNO;
+ break;
+ }
+ s->stream.avail_out = Z_BUFSIZE;
+ }
+ s->in += s->stream.avail_in;
+ s->out += s->stream.avail_out;
+ s->z_err = deflate(&(s->stream), Z_NO_FLUSH);
+ s->in -= s->stream.avail_in;
+ s->out -= s->stream.avail_out;
+ if (s->z_err != Z_OK) break;
+ }
+ s->crc = crc32(s->crc, (const Bytef *)buf, len);
+
+ return (int)(len - s->stream.avail_in);
+}
+
+
+/* ===========================================================================
+ Converts, formats, and writes the args to the compressed file under
+ control of the format string, as in fprintf. gzprintf returns the number of
+ uncompressed bytes actually written (0 in case of error).
+*/
+#ifdef STDC
+#include <stdarg.h>
+
+int ZEXPORTVA gzprintf (gzFile file, const char *format, /* args */ ...)
+{
+ char buf[Z_PRINTF_BUFSIZE];
+ va_list va;
+ int len;
+
+ buf[sizeof(buf) - 1] = 0;
+ va_start(va, format);
+#ifdef NO_vsnprintf
+# ifdef HAS_vsprintf_void
+ (void)vsprintf(buf, format, va);
+ va_end(va);
+ for (len = 0; len < sizeof(buf); len++)
+ if (buf[len] == 0) break;
+# else
+ len = vsprintf(buf, format, va);
+ va_end(va);
+# endif
+#else
+# ifdef HAS_vsnprintf_void
+ (void)vsnprintf(buf, sizeof(buf), format, va);
+ va_end(va);
+ len = strlen(buf);
+# else
+ len = vsnprintf(buf, sizeof(buf), format, va);
+ va_end(va);
+# endif
+#endif
+ if (len <= 0 || len >= (int)sizeof(buf) || buf[sizeof(buf) - 1] != 0)
+ return 0;
+ return gzwrite(file, buf, (unsigned)len);
+}
+#else /* not ANSI C */
+
+int ZEXPORTVA gzprintf (file, format, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10,
+ a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
+ gzFile file;
+ const char *format;
+ int a1, a2, a3, a4, a5, a6, a7, a8, a9, a10,
+ a11, a12, a13, a14, a15, a16, a17, a18, a19, a20;
+{
+ char buf[Z_PRINTF_BUFSIZE];
+ int len;
+
+ buf[sizeof(buf) - 1] = 0;
+#ifdef NO_snprintf
+# ifdef HAS_sprintf_void
+ sprintf(buf, format, a1, a2, a3, a4, a5, a6, a7, a8,
+ a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20);
+ for (len = 0; len < sizeof(buf); len++)
+ if (buf[len] == 0) break;
+# else
+ len = sprintf(buf, format, a1, a2, a3, a4, a5, a6, a7, a8,
+ a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20);
+# endif
+#else
+# ifdef HAS_snprintf_void
+ snprintf(buf, sizeof(buf), format, a1, a2, a3, a4, a5, a6, a7, a8,
+ a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20);
+ len = strlen(buf);
+# else
+ len = snprintf(buf, sizeof(buf), format, a1, a2, a3, a4, a5, a6, a7, a8,
+ a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20);
+# endif
+#endif
+ if (len <= 0 || len >= sizeof(buf) || buf[sizeof(buf) - 1] != 0)
+ return 0;
+ return gzwrite(file, buf, len);
+}
+#endif
+
+/* ===========================================================================
+ Writes c, converted to an unsigned char, into the compressed file.
+ gzputc returns the value that was written, or -1 in case of error.
+*/
+int ZEXPORT gzputc(file, c)
+ gzFile file;
+ int c;
+{
+ unsigned char cc = (unsigned char) c; /* required for big endian systems */
+
+ return gzwrite(file, &cc, 1) == 1 ? (int)cc : -1;
+}
+
+
+/* ===========================================================================
+ Writes the given null-terminated string to the compressed file, excluding
+ the terminating null character.
+ gzputs returns the number of characters written, or -1 in case of error.
+*/
+int ZEXPORT gzputs(file, s)
+ gzFile file;
+ const char *s;
+{
+ return gzwrite(file, (char*)s, (unsigned)strlen(s));
+}
+
+
+/* ===========================================================================
+ Flushes all pending output into the compressed file. The parameter
+ flush is as in the deflate() function.
+*/
+local int do_flush (file, flush)
+ gzFile file;
+ int flush;
+{
+ uInt len;
+ int done = 0;
+ gz_stream *s = (gz_stream*)file;
+
+ if (s == NULL || s->mode != 'w') return Z_STREAM_ERROR;
+
+ s->stream.avail_in = 0; /* should be zero already anyway */
+
+ for (;;) {
+ len = Z_BUFSIZE - s->stream.avail_out;
+
+ if (len != 0) {
+ if ((uInt)fwrite(s->outbuf, 1, len, s->file) != len) {
+ s->z_err = Z_ERRNO;
+ return Z_ERRNO;
+ }
+ s->stream.next_out = s->outbuf;
+ s->stream.avail_out = Z_BUFSIZE;
+ }
+ if (done) break;
+ s->out += s->stream.avail_out;
+ s->z_err = deflate(&(s->stream), flush);
+ s->out -= s->stream.avail_out;
+
+ /* Ignore the second of two consecutive flushes: */
+ if (len == 0 && s->z_err == Z_BUF_ERROR) s->z_err = Z_OK;
+
+ /* deflate has finished flushing only when it hasn't used up
+ * all the available space in the output buffer:
+ */
+ done = (s->stream.avail_out != 0 || s->z_err == Z_STREAM_END);
+
+ if (s->z_err != Z_OK && s->z_err != Z_STREAM_END) break;
+ }
+ return s->z_err == Z_STREAM_END ? Z_OK : s->z_err;
+}
+
+int ZEXPORT gzflush (file, flush)
+ gzFile file;
+ int flush;
+{
+ gz_stream *s = (gz_stream*)file;
+ int err = do_flush (file, flush);
+
+ if (err) return err;
+ fflush(s->file);
+ return s->z_err == Z_STREAM_END ? Z_OK : s->z_err;
+}
+#endif /* NO_GZCOMPRESS */
+
+/* ===========================================================================
+ Sets the starting position for the next gzread or gzwrite on the given
+ compressed file. The offset represents a number of bytes in the
+ gzseek returns the resulting offset location as measured in bytes from
+ the beginning of the uncompressed stream, or -1 in case of error.
+ SEEK_END is not implemented, returns error.
+ In this version of the library, gzseek can be extremely slow.
+*/
+z_off_t ZEXPORT gzseek (file, offset, whence)
+ gzFile file;
+ z_off_t offset;
+ int whence;
+{
+ gz_stream *s = (gz_stream*)file;
+
+ if (s == NULL || whence == SEEK_END ||
+ s->z_err == Z_ERRNO || s->z_err == Z_DATA_ERROR) {
+ return -1L;
+ }
+
+ if (s->mode == 'w') {
+#ifdef NO_GZCOMPRESS
+ return -1L;
+#else
+ if (whence == SEEK_SET) {
+ offset -= s->in;
+ }
+ if (offset < 0) return -1L;
+
+ /* At this point, offset is the number of zero bytes to write. */
+ if (s->inbuf == Z_NULL) {
+ s->inbuf = (Byte*)ALLOC(Z_BUFSIZE); /* for seeking */
+ if (s->inbuf == Z_NULL) return -1L;
+ zmemzero(s->inbuf, Z_BUFSIZE);
+ }
+ while (offset > 0) {
+ uInt size = Z_BUFSIZE;
+ if (offset < Z_BUFSIZE) size = (uInt)offset;
+
+ size = gzwrite(file, s->inbuf, size);
+ if (size == 0) return -1L;
+
+ offset -= size;
+ }
+ return s->in;
+#endif
+ }
+ /* Rest of function is for reading only */
+
+ /* compute absolute position */
+ if (whence == SEEK_CUR) {
+ offset += s->out;
+ }
+ if (offset < 0) return -1L;
+
+ if (s->transparent) {
+ /* map to fseek */
+ s->back = EOF;
+ s->stream.avail_in = 0;
+ s->stream.next_in = s->inbuf;
+ if (fseek(s->file, offset, SEEK_SET) < 0) return -1L;
+
+ s->in = s->out = offset;
+ return offset;
+ }
+
+ /* For a negative seek, rewind and use positive seek */
+ if (offset >= s->out) {
+ offset -= s->out;
+ } else if (gzrewind(file) < 0) {
+ return -1L;
+ }
+ /* offset is now the number of bytes to skip. */
+
+ if (offset != 0 && s->outbuf == Z_NULL) {
+ s->outbuf = (Byte*)ALLOC(Z_BUFSIZE);
+ if (s->outbuf == Z_NULL) return -1L;
+ }
+ if (offset && s->back != EOF) {
+ s->back = EOF;
+ s->out++;
+ offset--;
+ if (s->last) s->z_err = Z_STREAM_END;
+ }
+ while (offset > 0) {
+ int size = Z_BUFSIZE;
+ if (offset < Z_BUFSIZE) size = (int)offset;
+
+ size = gzread(file, s->outbuf, (uInt)size);
+ if (size <= 0) return -1L;
+ offset -= size;
+ }
+ return s->out;
+}
+
+/* ===========================================================================
+ Rewinds input file.
+*/
+int ZEXPORT gzrewind (file)
+ gzFile file;
+{
+ gz_stream *s = (gz_stream*)file;
+
+ if (s == NULL || s->mode != 'r') return -1;
+
+ s->z_err = Z_OK;
+ s->z_eof = 0;
+ s->back = EOF;
+ s->stream.avail_in = 0;
+ s->stream.next_in = s->inbuf;
+ s->crc = crc32(0L, Z_NULL, 0);
+ if (!s->transparent) (void)inflateReset(&s->stream);
+ s->in = 0;
+ s->out = 0;
+ return fseek(s->file, s->start, SEEK_SET);
+}
+
+/* ===========================================================================
+ Returns the starting position for the next gzread or gzwrite on the
+ given compressed file. This position represents a number of bytes in the
+ uncompressed data stream.
+*/
+z_off_t ZEXPORT gztell (file)
+ gzFile file;
+{
+ return gzseek(file, 0L, SEEK_CUR);
+}
+
+/* ===========================================================================
+ Returns 1 when EOF has previously been detected reading the given
+ input stream, otherwise zero.
+*/
+int ZEXPORT gzeof (file)
+ gzFile file;
+{
+ gz_stream *s = (gz_stream*)file;
+
+ /* With concatenated compressed files that can have embedded
+ * crc trailers, z_eof is no longer the only/best indicator of EOF
+ * on a gz_stream. Handle end-of-stream error explicitly here.
+ */
+ if (s == NULL || s->mode != 'r') return 0;
+ if (s->z_eof) return 1;
+ return s->z_err == Z_STREAM_END;
+}
+
+/* ===========================================================================
+ Outputs a long in LSB order to the given file
+*/
+local void putLong (file, x)
+ FILE *file;
+ uLong x;
+{
+ int n;
+ for (n = 0; n < 4; n++) {
+ fputc((int)(x & 0xff), file);
+ x >>= 8;
+ }
+}
+
+/* ===========================================================================
+ Reads a long in LSB order from the given gz_stream. Sets z_err in case
+ of error.
+*/
+local uLong getLong (s)
+ gz_stream *s;
+{
+ uLong x = (uLong)get_byte(s);
+ int c;
+
+ x += ((uLong)get_byte(s))<<8;
+ x += ((uLong)get_byte(s))<<16;
+ c = get_byte(s);
+ if (c == EOF) s->z_err = Z_DATA_ERROR;
+ x += ((uLong)c)<<24;
+ return x;
+}
+
+/* ===========================================================================
+ Flushes all pending output if necessary, closes the compressed file
+ and deallocates all the (de)compression state.
+*/
+int ZEXPORT gzclose (file)
+ gzFile file;
+{
+ int err;
+ gz_stream *s = (gz_stream*)file;
+
+ if (s == NULL) return Z_STREAM_ERROR;
+
+ if (s->mode == 'w') {
+#ifdef NO_GZCOMPRESS
+ return Z_STREAM_ERROR;
+#else
+ err = do_flush (file, Z_FINISH);
+ if (err != Z_OK) return destroy((gz_stream*)file);
+
+ putLong (s->file, s->crc);
+ putLong (s->file, (uLong)(s->in & 0xffffffff));
+#endif
+ }
+ return destroy((gz_stream*)file);
+}
+
+/* ===========================================================================
+ Returns the error message for the last error which occured on the
+ given compressed file. errnum is set to zlib error number. If an
+ error occured in the file system and not in the compression library,
+ errnum is set to Z_ERRNO and the application may consult errno
+ to get the exact error code.
+*/
+const char * ZEXPORT gzerror (file, errnum)
+ gzFile file;
+ int *errnum;
+{
+ char *m;
+ gz_stream *s = (gz_stream*)file;
+
+ if (s == NULL) {
+ *errnum = Z_STREAM_ERROR;
+ return (const char*)ERR_MSG(Z_STREAM_ERROR);
+ }
+ *errnum = s->z_err;
+ if (*errnum == Z_OK) return (const char*)"";
+
+ m = (char*)(*errnum == Z_ERRNO ? zstrerror(errno) : s->stream.msg);
+
+ if (m == NULL || *m == '\0') m = (char*)ERR_MSG(s->z_err);
+
+ TRYFREE(s->msg);
+ s->msg = (char*)ALLOC(strlen(s->path) + strlen(m) + 3);
+ if (s->msg == Z_NULL) return (const char*)ERR_MSG(Z_MEM_ERROR);
+ strcpy(s->msg, s->path);
+ strcat(s->msg, ": ");
+ strcat(s->msg, m);
+ return (const char*)s->msg;
+}
+
+/* ===========================================================================
+ Clear the error and end-of-file flags, and do the same for the real file.
+*/
+void ZEXPORT gzclearerr (file)
+ gzFile file;
+{
+ gz_stream *s = (gz_stream*)file;
+
+ if (s == NULL) return;
+ if (s->z_err != Z_STREAM_END) s->z_err = Z_OK;
+ s->z_eof = 0;
+ clearerr(s->file);
+}
diff --git a/src/zlib/infback.c b/src/zlib/infback.c
new file mode 100755
index 0000000..110b03b
--- /dev/null
+++ b/src/zlib/infback.c
@@ -0,0 +1,619 @@
+/* infback.c -- inflate using a call-back interface
+ * Copyright (C) 1995-2003 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/*
+ This code is largely copied from inflate.c. Normally either infback.o or
+ inflate.o would be linked into an application--not both. The interface
+ with inffast.c is retained so that optimized assembler-coded versions of
+ inflate_fast() can be used with either inflate.c or infback.c.
+ */
+
+#include "zutil.h"
+#include "inftrees.h"
+#include "inflate.h"
+#include "inffast.h"
+
+/* function prototypes */
+local void fixedtables OF((struct inflate_state FAR *state));
+
+/*
+ strm provides memory allocation functions in zalloc and zfree, or
+ Z_NULL to use the library memory allocation functions.
+
+ windowBits is in the range 8..15, and window is a user-supplied
+ window and output buffer that is 2**windowBits bytes.
+ */
+int ZEXPORT inflateBackInit_(strm, windowBits, window, version, stream_size)
+z_stream FAR *strm;
+int windowBits;
+unsigned char FAR *window;
+const char *version;
+int stream_size;
+{
+ struct inflate_state FAR *state;
+
+ if (version == Z_NULL || version[0] != ZLIB_VERSION[0] ||
+ stream_size != (int)(sizeof(z_stream)))
+ return Z_VERSION_ERROR;
+ if (strm == Z_NULL || window == Z_NULL ||
+ windowBits < 8 || windowBits > 15)
+ return Z_STREAM_ERROR;
+ strm->msg = Z_NULL; /* in case we return an error */
+ if (strm->zalloc == (alloc_func)0) {
+ strm->zalloc = zcalloc;
+ strm->opaque = (voidpf)0;
+ }
+ if (strm->zfree == (free_func)0) strm->zfree = zcfree;
+ state = (struct inflate_state FAR *)ZALLOC(strm, 1,
+ sizeof(struct inflate_state));
+ if (state == Z_NULL) return Z_MEM_ERROR;
+ Tracev((stderr, "inflate: allocated\n"));
+ strm->state = (voidpf)state;
+ state->wbits = windowBits;
+ state->wsize = 1U << windowBits;
+ state->window = window;
+ state->write = 0;
+ state->whave = 0;
+ return Z_OK;
+}
+
+/*
+ Return state with length and distance decoding tables and index sizes set to
+ fixed code decoding. Normally this returns fixed tables from inffixed.h.
+ If BUILDFIXED is defined, then instead this routine builds the tables the
+ first time it's called, and returns those tables the first time and
+ thereafter. This reduces the size of the code by about 2K bytes, in
+ exchange for a little execution time. However, BUILDFIXED should not be
+ used for threaded applications, since the rewriting of the tables and virgin
+ may not be thread-safe.
+ */
+local void fixedtables(state)
+struct inflate_state FAR *state;
+{
+#ifdef BUILDFIXED
+ static int virgin = 1;
+ static code *lenfix, *distfix;
+ static code fixed[544];
+
+ /* build fixed huffman tables if first call (may not be thread safe) */
+ if (virgin) {
+ unsigned sym, bits;
+ static code *next;
+
+ /* literal/length table */
+ sym = 0;
+ while (sym < 144) state->lens[sym++] = 8;
+ while (sym < 256) state->lens[sym++] = 9;
+ while (sym < 280) state->lens[sym++] = 7;
+ while (sym < 288) state->lens[sym++] = 8;
+ next = fixed;
+ lenfix = next;
+ bits = 9;
+ inflate_table(LENS, state->lens, 288, &(next), &(bits), state->work);
+
+ /* distance table */
+ sym = 0;
+ while (sym < 32) state->lens[sym++] = 5;
+ distfix = next;
+ bits = 5;
+ inflate_table(DISTS, state->lens, 32, &(next), &(bits), state->work);
+
+ /* do this just once */
+ virgin = 0;
+ }
+#else /* !BUILDFIXED */
+# include "inffixed.h"
+#endif /* BUILDFIXED */
+ state->lencode = lenfix;
+ state->lenbits = 9;
+ state->distcode = distfix;
+ state->distbits = 5;
+}
+
+/* Macros for inflateBack(): */
+
+/* Load returned state from inflate_fast() */
+#define LOAD() \
+ do { \
+ put = strm->next_out; \
+ left = strm->avail_out; \
+ next = strm->next_in; \
+ have = strm->avail_in; \
+ hold = state->hold; \
+ bits = state->bits; \
+ } while (0)
+
+/* Set state from registers for inflate_fast() */
+#define RESTORE() \
+ do { \
+ strm->next_out = put; \
+ strm->avail_out = left; \
+ strm->next_in = next; \
+ strm->avail_in = have; \
+ state->hold = hold; \
+ state->bits = bits; \
+ } while (0)
+
+/* Clear the input bit accumulator */
+#define INITBITS() \
+ do { \
+ hold = 0; \
+ bits = 0; \
+ } while (0)
+
+/* Assure that some input is available. If input is requested, but denied,
+ then return a Z_BUF_ERROR from inflateBack(). */
+#define PULL() \
+ do { \
+ if (have == 0) { \
+ have = in(in_desc, &next); \
+ if (have == 0) { \
+ next = Z_NULL; \
+ ret = Z_BUF_ERROR; \
+ goto inf_leave; \
+ } \
+ } \
+ } while (0)
+
+/* Get a byte of input into the bit accumulator, or return from inflateBack()
+ with an error if there is no input available. */
+#define PULLBYTE() \
+ do { \
+ PULL(); \
+ have--; \
+ hold += (unsigned long)(*next++) << bits; \
+ bits += 8; \
+ } while (0)
+
+/* Assure that there are at least n bits in the bit accumulator. If there is
+ not enough available input to do that, then return from inflateBack() with
+ an error. */
+#define NEEDBITS(n) \
+ do { \
+ while (bits < (unsigned)(n)) \
+ PULLBYTE(); \
+ } while (0)
+
+/* Return the low n bits of the bit accumulator (n < 16) */
+#define BITS(n) \
+ ((unsigned)hold & ((1U << (n)) - 1))
+
+/* Remove n bits from the bit accumulator */
+#define DROPBITS(n) \
+ do { \
+ hold >>= (n); \
+ bits -= (unsigned)(n); \
+ } while (0)
+
+/* Remove zero to seven bits as needed to go to a byte boundary */
+#define BYTEBITS() \
+ do { \
+ hold >>= bits & 7; \
+ bits -= bits & 7; \
+ } while (0)
+
+/* Assure that some output space is available, by writing out the window
+ if it's full. If the write fails, return from inflateBack() with a
+ Z_BUF_ERROR. */
+#define ROOM() \
+ do { \
+ if (left == 0) { \
+ put = state->window; \
+ left = state->wsize; \
+ state->whave = left; \
+ if (out(out_desc, put, left)) { \
+ ret = Z_BUF_ERROR; \
+ goto inf_leave; \
+ } \
+ } \
+ } while (0)
+
+/*
+ strm provides the memory allocation functions and window buffer on input,
+ and provides information on the unused input on return. For Z_DATA_ERROR
+ returns, strm will also provide an error message.
+
+ in() and out() are the call-back input and output functions. When
+ inflateBack() needs more input, it calls in(). When inflateBack() has
+ filled the window with output, or when it completes with data in the
+ window, it calls out() to write out the data. The application must not
+ change the provided input until in() is called again or inflateBack()
+ returns. The application must not change the window/output buffer until
+ inflateBack() returns.
+
+ in() and out() are called with a descriptor parameter provided in the
+ inflateBack() call. This parameter can be a structure that provides the
+ information required to do the read or write, as well as accumulated
+ information on the input and output such as totals and check values.
+
+ in() should return zero on failure. out() should return non-zero on
+ failure. If either in() or out() fails, than inflateBack() returns a
+ Z_BUF_ERROR. strm->next_in can be checked for Z_NULL to see whether it
+ was in() or out() that caused in the error. Otherwise, inflateBack()
+ returns Z_STREAM_END on success, Z_DATA_ERROR for an deflate format
+ error, or Z_MEM_ERROR if it could not allocate memory for the state.
+ inflateBack() can also return Z_STREAM_ERROR if the input parameters
+ are not correct, i.e. strm is Z_NULL or the state was not initialized.
+ */
+int ZEXPORT inflateBack(strm, in, in_desc, out, out_desc)
+z_stream FAR *strm;
+in_func in;
+void FAR *in_desc;
+out_func out;
+void FAR *out_desc;
+{
+ struct inflate_state FAR *state;
+ unsigned char FAR *next; /* next input */
+ unsigned char FAR *put; /* next output */
+ unsigned have, left; /* available input and output */
+ unsigned long hold; /* bit buffer */
+ unsigned bits; /* bits in bit buffer */
+ unsigned copy; /* number of stored or match bytes to copy */
+ unsigned char FAR *from; /* where to copy match bytes from */
+ code this; /* current decoding table entry */
+ code last; /* parent table entry */
+ unsigned len; /* length to copy for repeats, bits to drop */
+ int ret; /* return code */
+ static const unsigned short order[19] = /* permutation of code lengths */
+ {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15};
+
+ /* Check that the strm exists and that the state was initialized */
+ if (strm == Z_NULL || strm->state == Z_NULL)
+ return Z_STREAM_ERROR;
+ state = (struct inflate_state FAR *)strm->state;
+
+ /* Reset the state */
+ strm->msg = Z_NULL;
+ state->mode = TYPE;
+ state->last = 0;
+ state->whave = 0;
+ next = strm->next_in;
+ have = next != Z_NULL ? strm->avail_in : 0;
+ hold = 0;
+ bits = 0;
+ put = state->window;
+ left = state->wsize;
+
+ /* Inflate until end of block marked as last */
+ for (;;)
+ switch (state->mode) {
+ case TYPE:
+ /* determine and dispatch block type */
+ if (state->last) {
+ BYTEBITS();
+ state->mode = DONE;
+ break;
+ }
+ NEEDBITS(3);
+ state->last = BITS(1);
+ DROPBITS(1);
+ switch (BITS(2)) {
+ case 0: /* stored block */
+ Tracev((stderr, "inflate: stored block%s\n",
+ state->last ? " (last)" : ""));
+ state->mode = STORED;
+ break;
+ case 1: /* fixed block */
+ fixedtables(state);
+ Tracev((stderr, "inflate: fixed codes block%s\n",
+ state->last ? " (last)" : ""));
+ state->mode = LEN; /* decode codes */
+ break;
+ case 2: /* dynamic block */
+ Tracev((stderr, "inflate: dynamic codes block%s\n",
+ state->last ? " (last)" : ""));
+ state->mode = TABLE;
+ break;
+ case 3:
+ strm->msg = (char *)"invalid block type";
+ state->mode = BAD;
+ }
+ DROPBITS(2);
+ break;
+
+ case STORED:
+ /* get and verify stored block length */
+ BYTEBITS(); /* go to byte boundary */
+ NEEDBITS(32);
+ if ((hold & 0xffff) != ((hold >> 16) ^ 0xffff)) {
+ strm->msg = (char *)"invalid stored block lengths";
+ state->mode = BAD;
+ break;
+ }
+ state->length = (unsigned)hold & 0xffff;
+ Tracev((stderr, "inflate: stored length %u\n",
+ state->length));
+ INITBITS();
+
+ /* copy stored block from input to output */
+ while (state->length != 0) {
+ copy = state->length;
+ PULL();
+ ROOM();
+ if (copy > have) copy = have;
+ if (copy > left) copy = left;
+ zmemcpy(put, next, copy);
+ have -= copy;
+ next += copy;
+ left -= copy;
+ put += copy;
+ state->length -= copy;
+ }
+ Tracev((stderr, "inflate: stored end\n"));
+ state->mode = TYPE;
+ break;
+
+ case TABLE:
+ /* get dynamic table entries descriptor */
+ NEEDBITS(14);
+ state->nlen = BITS(5) + 257;
+ DROPBITS(5);
+ state->ndist = BITS(5) + 1;
+ DROPBITS(5);
+ state->ncode = BITS(4) + 4;
+ DROPBITS(4);
+#ifndef PKZIP_BUG_WORKAROUND
+ if (state->nlen > 286 || state->ndist > 30) {
+ strm->msg = (char *)"too many length or distance symbols";
+ state->mode = BAD;
+ break;
+ }
+#endif
+ Tracev((stderr, "inflate: table sizes ok\n"));
+
+ /* get code length code lengths (not a typo) */
+ state->have = 0;
+ while (state->have < state->ncode) {
+ NEEDBITS(3);
+ state->lens[order[state->have++]] = (unsigned short)BITS(3);
+ DROPBITS(3);
+ }
+ while (state->have < 19)
+ state->lens[order[state->have++]] = 0;
+ state->next = state->codes;
+ state->lencode = (code const FAR *)(state->next);
+ state->lenbits = 7;
+ ret = inflate_table(CODES, state->lens, 19, &(state->next),
+ &(state->lenbits), state->work);
+ if (ret) {
+ strm->msg = (char *)"invalid code lengths set";
+ state->mode = BAD;
+ break;
+ }
+ Tracev((stderr, "inflate: code lengths ok\n"));
+
+ /* get length and distance code code lengths */
+ state->have = 0;
+ while (state->have < state->nlen + state->ndist) {
+ for (;;) {
+ this = state->lencode[BITS(state->lenbits)];
+ if ((unsigned)(this.bits) <= bits) break;
+ PULLBYTE();
+ }
+ if (this.val < 16) {
+ NEEDBITS(this.bits);
+ DROPBITS(this.bits);
+ state->lens[state->have++] = this.val;
+ }
+ else {
+ if (this.val == 16) {
+ NEEDBITS(this.bits + 2);
+ DROPBITS(this.bits);
+ if (state->have == 0) {
+ strm->msg = (char *)"invalid bit length repeat";
+ state->mode = BAD;
+ break;
+ }
+ len = (unsigned)(state->lens[state->have - 1]);
+ copy = 3 + BITS(2);
+ DROPBITS(2);
+ }
+ else if (this.val == 17) {
+ NEEDBITS(this.bits + 3);
+ DROPBITS(this.bits);
+ len = 0;
+ copy = 3 + BITS(3);
+ DROPBITS(3);
+ }
+ else {
+ NEEDBITS(this.bits + 7);
+ DROPBITS(this.bits);
+ len = 0;
+ copy = 11 + BITS(7);
+ DROPBITS(7);
+ }
+ if (state->have + copy > state->nlen + state->ndist) {
+ strm->msg = (char *)"invalid bit length repeat";
+ state->mode = BAD;
+ break;
+ }
+ while (copy--)
+ state->lens[state->have++] = (unsigned short)len;
+ }
+ }
+
+ /* build code tables */
+ state->next = state->codes;
+ state->lencode = (code const FAR *)(state->next);
+ state->lenbits = 9;
+ ret = inflate_table(LENS, state->lens, state->nlen, &(state->next),
+ &(state->lenbits), state->work);
+ if (ret) {
+ strm->msg = (char *)"invalid literal/lengths set";
+ state->mode = BAD;
+ break;
+ }
+ state->distcode = (code const FAR *)(state->next);
+ state->distbits = 6;
+ ret = inflate_table(DISTS, state->lens + state->nlen, state->ndist,
+ &(state->next), &(state->distbits), state->work);
+ if (ret) {
+ strm->msg = (char *)"invalid distances set";
+ state->mode = BAD;
+ break;
+ }
+ Tracev((stderr, "inflate: codes ok\n"));
+ state->mode = LEN;
+
+ case LEN:
+ /* use inflate_fast() if we have enough input and output */
+ if (have >= 6 && left >= 258) {
+ RESTORE();
+ if (state->whave < state->wsize)
+ state->whave = state->wsize - left;
+ inflate_fast(strm, state->wsize);
+ LOAD();
+ break;
+ }
+
+ /* get a literal, length, or end-of-block code */
+ for (;;) {
+ this = state->lencode[BITS(state->lenbits)];
+ if ((unsigned)(this.bits) <= bits) break;
+ PULLBYTE();
+ }
+ if (this.op && (this.op & 0xf0) == 0) {
+ last = this;
+ for (;;) {
+ this = state->lencode[last.val +
+ (BITS(last.bits + last.op) >> last.bits)];
+ if ((unsigned)(last.bits + this.bits) <= bits) break;
+ PULLBYTE();
+ }
+ DROPBITS(last.bits);
+ }
+ DROPBITS(this.bits);
+ state->length = (unsigned)this.val;
+
+ /* process literal */
+ if (this.op == 0) {
+ Tracevv((stderr, this.val >= 0x20 && this.val < 0x7f ?
+ "inflate: literal '%c'\n" :
+ "inflate: literal 0x%02x\n", this.val));
+ ROOM();
+ *put++ = (unsigned char)(state->length);
+ left--;
+ state->mode = LEN;
+ break;
+ }
+
+ /* process end of block */
+ if (this.op & 32) {
+ Tracevv((stderr, "inflate: end of block\n"));
+ state->mode = TYPE;
+ break;
+ }
+
+ /* invalid code */
+ if (this.op & 64) {
+ strm->msg = (char *)"invalid literal/length code";
+ state->mode = BAD;
+ break;
+ }
+
+ /* length code -- get extra bits, if any */
+ state->extra = (unsigned)(this.op) & 15;
+ if (state->extra != 0) {
+ NEEDBITS(state->extra);
+ state->length += BITS(state->extra);
+ DROPBITS(state->extra);
+ }
+ Tracevv((stderr, "inflate: length %u\n", state->length));
+
+ /* get distance code */
+ for (;;) {
+ this = state->distcode[BITS(state->distbits)];
+ if ((unsigned)(this.bits) <= bits) break;
+ PULLBYTE();
+ }
+ if ((this.op & 0xf0) == 0) {
+ last = this;
+ for (;;) {
+ this = state->distcode[last.val +
+ (BITS(last.bits + last.op) >> last.bits)];
+ if ((unsigned)(last.bits + this.bits) <= bits) break;
+ PULLBYTE();
+ }
+ DROPBITS(last.bits);
+ }
+ DROPBITS(this.bits);
+ if (this.op & 64) {
+ strm->msg = (char *)"invalid distance code";
+ state->mode = BAD;
+ break;
+ }
+ state->offset = (unsigned)this.val;
+
+ /* get distance extra bits, if any */
+ state->extra = (unsigned)(this.op) & 15;
+ if (state->extra != 0) {
+ NEEDBITS(state->extra);
+ state->offset += BITS(state->extra);
+ DROPBITS(state->extra);
+ }
+ if (state->offset > state->wsize - (state->whave < state->wsize ?
+ left : 0)) {
+ strm->msg = (char *)"invalid distance too far back";
+ state->mode = BAD;
+ break;
+ }
+ Tracevv((stderr, "inflate: distance %u\n", state->offset));
+
+ /* copy match from window to output */
+ do {
+ ROOM();
+ copy = state->wsize - state->offset;
+ if (copy < left) {
+ from = put + copy;
+ copy = left - copy;
+ }
+ else {
+ from = put - state->offset;
+ copy = left;
+ }
+ if (copy > state->length) copy = state->length;
+ state->length -= copy;
+ left -= copy;
+ do {
+ *put++ = *from++;
+ } while (--copy);
+ } while (state->length != 0);
+ break;
+
+ case DONE:
+ /* inflate stream terminated properly -- write leftover output */
+ ret = Z_STREAM_END;
+ if (left < state->wsize) {
+ if (out(out_desc, state->window, state->wsize - left))
+ ret = Z_BUF_ERROR;
+ }
+ goto inf_leave;
+
+ case BAD:
+ ret = Z_DATA_ERROR;
+ goto inf_leave;
+
+ default: /* can't happen, but makes compilers happy */
+ ret = Z_STREAM_ERROR;
+ goto inf_leave;
+ }
+
+ /* Return unused input */
+ inf_leave:
+ strm->next_in = next;
+ strm->avail_in = have;
+ return ret;
+}
+
+int ZEXPORT inflateBackEnd(strm)
+z_stream FAR *strm;
+{
+ if (strm == Z_NULL || strm->state == Z_NULL || strm->zfree == (free_func)0)
+ return Z_STREAM_ERROR;
+ ZFREE(strm, strm->state);
+ strm->state = Z_NULL;
+ Tracev((stderr, "inflate: end\n"));
+ return Z_OK;
+}
diff --git a/src/zlib/inffast.c b/src/zlib/inffast.c
new file mode 100755
index 0000000..c716440
--- /dev/null
+++ b/src/zlib/inffast.c
@@ -0,0 +1,305 @@
+/* inffast.c -- fast decoding
+ * Copyright (C) 1995-2003 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+#include "zutil.h"
+#include "inftrees.h"
+#include "inflate.h"
+#include "inffast.h"
+
+#ifndef ASMINF
+
+/* Allow machine dependent optimization for post-increment or pre-increment.
+ Based on testing to date,
+ Pre-increment preferred for:
+ - PowerPC G3 (Adler)
+ - MIPS R5000 (Randers-Pehrson)
+ Post-increment preferred for:
+ - none
+ No measurable difference:
+ - Pentium III (Anderson)
+ - 68060 (Nikl)
+ */
+#ifdef POSTINC
+# define OFF 0
+# define PUP(a) *(a)++
+#else
+# define OFF 1
+# define PUP(a) *++(a)
+#endif
+
+/*
+ Decode literal, length, and distance codes and write out the resulting
+ literal and match bytes until either not enough input or output is
+ available, an end-of-block is encountered, or a data error is encountered.
+ When large enough input and output buffers are supplied to inflate(), for
+ example, a 16K input buffer and a 64K output buffer, more than 95% of the
+ inflate execution time is spent in this routine.
+
+ Entry assumptions:
+
+ state->mode == LEN
+ strm->avail_in >= 6
+ strm->avail_out >= 258
+ start >= strm->avail_out
+ state->bits < 8
+
+ On return, state->mode is one of:
+
+ LEN -- ran out of enough output space or enough available input
+ TYPE -- reached end of block code, inflate() to interpret next block
+ BAD -- error in block data
+
+ Notes:
+
+ - The maximum input bits used by a length/distance pair is 15 bits for the
+ length code, 5 bits for the length extra, 15 bits for the distance code,
+ and 13 bits for the distance extra. This totals 48 bits, or six bytes.
+ Therefore if strm->avail_in >= 6, then there is enough input to avoid
+ checking for available input while decoding.
+
+ - The maximum bytes that a single length/distance pair can output is 258
+ bytes, which is the maximum length that can be coded. inflate_fast()
+ requires strm->avail_out >= 258 for each loop to avoid checking for
+ output space.
+ */
+void inflate_fast(strm, start)
+z_streamp strm;
+unsigned start; /* inflate()'s starting value for strm->avail_out */
+{
+ struct inflate_state FAR *state;
+ unsigned char FAR *in; /* local strm->next_in */
+ unsigned char FAR *last; /* while in < last, enough input available */
+ unsigned char FAR *out; /* local strm->next_out */
+ unsigned char FAR *beg; /* inflate()'s initial strm->next_out */
+ unsigned char FAR *end; /* while out < end, enough space available */
+ unsigned wsize; /* window size or zero if not using window */
+ unsigned whave; /* valid bytes in the window */
+ unsigned write; /* window write index */
+ unsigned char FAR *window; /* allocated sliding window, if wsize != 0 */
+ unsigned long hold; /* local strm->hold */
+ unsigned bits; /* local strm->bits */
+ code const FAR *lcode; /* local strm->lencode */
+ code const FAR *dcode; /* local strm->distcode */
+ unsigned lmask; /* mask for first level of length codes */
+ unsigned dmask; /* mask for first level of distance codes */
+ code this; /* retrieved table entry */
+ unsigned op; /* code bits, operation, extra bits, or */
+ /* window position, window bytes to copy */
+ unsigned len; /* match length, unused bytes */
+ unsigned dist; /* match distance */
+ unsigned char FAR *from; /* where to copy match from */
+
+ /* copy state to local variables */
+ state = (struct inflate_state FAR *)strm->state;
+ in = strm->next_in - OFF;
+ last = in + (strm->avail_in - 5);
+ out = strm->next_out - OFF;
+ beg = out - (start - strm->avail_out);
+ end = out + (strm->avail_out - 257);
+ wsize = state->wsize;
+ whave = state->whave;
+ write = state->write;
+ window = state->window;
+ hold = state->hold;
+ bits = state->bits;
+ lcode = state->lencode;
+ dcode = state->distcode;
+ lmask = (1U << state->lenbits) - 1;
+ dmask = (1U << state->distbits) - 1;
+
+ /* decode literals and length/distances until end-of-block or not enough
+ input data or output space */
+ do {
+ if (bits < 15) {
+ hold += (unsigned long)(PUP(in)) << bits;
+ bits += 8;
+ hold += (unsigned long)(PUP(in)) << bits;
+ bits += 8;
+ }
+ this = lcode[hold & lmask];
+ dolen:
+ op = (unsigned)(this.bits);
+ hold >>= op;
+ bits -= op;
+ op = (unsigned)(this.op);
+ if (op == 0) { /* literal */
+ Tracevv((stderr, this.val >= 0x20 && this.val < 0x7f ?
+ "inflate: literal '%c'\n" :
+ "inflate: literal 0x%02x\n", this.val));
+ PUP(out) = (unsigned char)(this.val);
+ }
+ else if (op & 16) { /* length base */
+ len = (unsigned)(this.val);
+ op &= 15; /* number of extra bits */
+ if (op) {
+ if (bits < op) {
+ hold += (unsigned long)(PUP(in)) << bits;
+ bits += 8;
+ }
+ len += (unsigned)hold & ((1U << op) - 1);
+ hold >>= op;
+ bits -= op;
+ }
+ Tracevv((stderr, "inflate: length %u\n", len));
+ if (bits < 15) {
+ hold += (unsigned long)(PUP(in)) << bits;
+ bits += 8;
+ hold += (unsigned long)(PUP(in)) << bits;
+ bits += 8;
+ }
+ this = dcode[hold & dmask];
+ dodist:
+ op = (unsigned)(this.bits);
+ hold >>= op;
+ bits -= op;
+ op = (unsigned)(this.op);
+ if (op & 16) { /* distance base */
+ dist = (unsigned)(this.val);
+ op &= 15; /* number of extra bits */
+ if (bits < op) {
+ hold += (unsigned long)(PUP(in)) << bits;
+ bits += 8;
+ if (bits < op) {
+ hold += (unsigned long)(PUP(in)) << bits;
+ bits += 8;
+ }
+ }
+ dist += (unsigned)hold & ((1U << op) - 1);
+ hold >>= op;
+ bits -= op;
+ Tracevv((stderr, "inflate: distance %u\n", dist));
+ op = (unsigned)(out - beg); /* max distance in output */
+ if (dist > op) { /* see if copy from window */
+ op = dist - op; /* distance back in window */
+ if (op > whave) {
+ strm->msg = (char *)"invalid distance too far back";
+ state->mode = BAD;
+ break;
+ }
+ from = window - OFF;
+ if (write == 0) { /* very common case */
+ from += wsize - op;
+ if (op < len) { /* some from window */
+ len -= op;
+ do {
+ PUP(out) = PUP(from);
+ } while (--op);
+ from = out - dist; /* rest from output */
+ }
+ }
+ else if (write < op) { /* wrap around window */
+ from += wsize + write - op;
+ op -= write;
+ if (op < len) { /* some from end of window */
+ len -= op;
+ do {
+ PUP(out) = PUP(from);
+ } while (--op);
+ from = window - OFF;
+ if (write < len) { /* some from start of window */
+ op = write;
+ len -= op;
+ do {
+ PUP(out) = PUP(from);
+ } while (--op);
+ from = out - dist; /* rest from output */
+ }
+ }
+ }
+ else { /* contiguous in window */
+ from += write - op;
+ if (op < len) { /* some from window */
+ len -= op;
+ do {
+ PUP(out) = PUP(from);
+ } while (--op);
+ from = out - dist; /* rest from output */
+ }
+ }
+ while (len > 2) {
+ PUP(out) = PUP(from);
+ PUP(out) = PUP(from);
+ PUP(out) = PUP(from);
+ len -= 3;
+ }
+ if (len) {
+ PUP(out) = PUP(from);
+ if (len > 1)
+ PUP(out) = PUP(from);
+ }
+ }
+ else {
+ from = out - dist; /* copy direct from output */
+ do { /* minimum length is three */
+ PUP(out) = PUP(from);
+ PUP(out) = PUP(from);
+ PUP(out) = PUP(from);
+ len -= 3;
+ } while (len > 2);
+ if (len) {
+ PUP(out) = PUP(from);
+ if (len > 1)
+ PUP(out) = PUP(from);
+ }
+ }
+ }
+ else if ((op & 64) == 0) { /* 2nd level distance code */
+ this = dcode[this.val + (hold & ((1U << op) - 1))];
+ goto dodist;
+ }
+ else {
+ strm->msg = (char *)"invalid distance code";
+ state->mode = BAD;
+ break;
+ }
+ }
+ else if ((op & 64) == 0) { /* 2nd level length code */
+ this = lcode[this.val + (hold & ((1U << op) - 1))];
+ goto dolen;
+ }
+ else if (op & 32) { /* end-of-block */
+ Tracevv((stderr, "inflate: end of block\n"));
+ state->mode = TYPE;
+ break;
+ }
+ else {
+ strm->msg = (char *)"invalid literal/length code";
+ state->mode = BAD;
+ break;
+ }
+ } while (in < last && out < end);
+
+ /* return unused bytes (on entry, bits < 8, so in won't go too far back) */
+ len = bits >> 3;
+ in -= len;
+ bits -= len << 3;
+ hold &= (1U << bits) - 1;
+
+ /* update state and return */
+ strm->next_in = in + OFF;
+ strm->next_out = out + OFF;
+ strm->avail_in = (unsigned)(in < last ? 5 + (last - in) : 5 - (in - last));
+ strm->avail_out = (unsigned)(out < end ?
+ 257 + (end - out) : 257 - (out - end));
+ state->hold = hold;
+ state->bits = bits;
+ return;
+}
+
+/*
+ inflate_fast() speedups that turned out slower (on a PowerPC G3 750CXe):
+ - Using bit fields for code structure
+ - Different op definition to avoid & for extra bits (do & for table bits)
+ - Three separate decoding do-loops for direct, window, and write == 0
+ - Special case for distance > 1 copies to do overlapped load and store copy
+ - Explicit branch predictions (based on measured branch probabilities)
+ - Deferring match copy and interspersed it with decoding subsequent codes
+ - Swapping literal/length else
+ - Swapping window/direct else
+ - Larger unrolled copy loops (three is about right)
+ - Moving len -= 3 statement into middle of loop
+ */
+
+#endif /* !ASMINF */
diff --git a/src/zlib/inffast.h b/src/zlib/inffast.h
new file mode 100755
index 0000000..1e88d2d
--- /dev/null
+++ b/src/zlib/inffast.h
@@ -0,0 +1,11 @@
+/* inffast.h -- header to use inffast.c
+ * Copyright (C) 1995-2003 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* WARNING: this file should *not* be used by applications. It is
+ part of the implementation of the compression library and is
+ subject to change. Applications should only use zlib.h.
+ */
+
+void inflate_fast OF((z_streamp strm, unsigned start));
diff --git a/src/zlib/inffixed.h b/src/zlib/inffixed.h
new file mode 100755
index 0000000..75ed4b5
--- /dev/null
+++ b/src/zlib/inffixed.h
@@ -0,0 +1,94 @@
+ /* inffixed.h -- table for decoding fixed codes
+ * Generated automatically by makefixed().
+ */
+
+ /* WARNING: this file should *not* be used by applications. It
+ is part of the implementation of the compression library and
+ is subject to change. Applications should only use zlib.h.
+ */
+
+ static const code lenfix[512] = {
+ {96,7,0},{0,8,80},{0,8,16},{20,8,115},{18,7,31},{0,8,112},{0,8,48},
+ {0,9,192},{16,7,10},{0,8,96},{0,8,32},{0,9,160},{0,8,0},{0,8,128},
+ {0,8,64},{0,9,224},{16,7,6},{0,8,88},{0,8,24},{0,9,144},{19,7,59},
+ {0,8,120},{0,8,56},{0,9,208},{17,7,17},{0,8,104},{0,8,40},{0,9,176},
+ {0,8,8},{0,8,136},{0,8,72},{0,9,240},{16,7,4},{0,8,84},{0,8,20},
+ {21,8,227},{19,7,43},{0,8,116},{0,8,52},{0,9,200},{17,7,13},{0,8,100},
+ {0,8,36},{0,9,168},{0,8,4},{0,8,132},{0,8,68},{0,9,232},{16,7,8},
+ {0,8,92},{0,8,28},{0,9,152},{20,7,83},{0,8,124},{0,8,60},{0,9,216},
+ {18,7,23},{0,8,108},{0,8,44},{0,9,184},{0,8,12},{0,8,140},{0,8,76},
+ {0,9,248},{16,7,3},{0,8,82},{0,8,18},{21,8,163},{19,7,35},{0,8,114},
+ {0,8,50},{0,9,196},{17,7,11},{0,8,98},{0,8,34},{0,9,164},{0,8,2},
+ {0,8,130},{0,8,66},{0,9,228},{16,7,7},{0,8,90},{0,8,26},{0,9,148},
+ {20,7,67},{0,8,122},{0,8,58},{0,9,212},{18,7,19},{0,8,106},{0,8,42},
+ {0,9,180},{0,8,10},{0,8,138},{0,8,74},{0,9,244},{16,7,5},{0,8,86},
+ {0,8,22},{64,8,0},{19,7,51},{0,8,118},{0,8,54},{0,9,204},{17,7,15},
+ {0,8,102},{0,8,38},{0,9,172},{0,8,6},{0,8,134},{0,8,70},{0,9,236},
+ {16,7,9},{0,8,94},{0,8,30},{0,9,156},{20,7,99},{0,8,126},{0,8,62},
+ {0,9,220},{18,7,27},{0,8,110},{0,8,46},{0,9,188},{0,8,14},{0,8,142},
+ {0,8,78},{0,9,252},{96,7,0},{0,8,81},{0,8,17},{21,8,131},{18,7,31},
+ {0,8,113},{0,8,49},{0,9,194},{16,7,10},{0,8,97},{0,8,33},{0,9,162},
+ {0,8,1},{0,8,129},{0,8,65},{0,9,226},{16,7,6},{0,8,89},{0,8,25},
+ {0,9,146},{19,7,59},{0,8,121},{0,8,57},{0,9,210},{17,7,17},{0,8,105},
+ {0,8,41},{0,9,178},{0,8,9},{0,8,137},{0,8,73},{0,9,242},{16,7,4},
+ {0,8,85},{0,8,21},{16,8,258},{19,7,43},{0,8,117},{0,8,53},{0,9,202},
+ {17,7,13},{0,8,101},{0,8,37},{0,9,170},{0,8,5},{0,8,133},{0,8,69},
+ {0,9,234},{16,7,8},{0,8,93},{0,8,29},{0,9,154},{20,7,83},{0,8,125},
+ {0,8,61},{0,9,218},{18,7,23},{0,8,109},{0,8,45},{0,9,186},{0,8,13},
+ {0,8,141},{0,8,77},{0,9,250},{16,7,3},{0,8,83},{0,8,19},{21,8,195},
+ {19,7,35},{0,8,115},{0,8,51},{0,9,198},{17,7,11},{0,8,99},{0,8,35},
+ {0,9,166},{0,8,3},{0,8,131},{0,8,67},{0,9,230},{16,7,7},{0,8,91},
+ {0,8,27},{0,9,150},{20,7,67},{0,8,123},{0,8,59},{0,9,214},{18,7,19},
+ {0,8,107},{0,8,43},{0,9,182},{0,8,11},{0,8,139},{0,8,75},{0,9,246},
+ {16,7,5},{0,8,87},{0,8,23},{64,8,0},{19,7,51},{0,8,119},{0,8,55},
+ {0,9,206},{17,7,15},{0,8,103},{0,8,39},{0,9,174},{0,8,7},{0,8,135},
+ {0,8,71},{0,9,238},{16,7,9},{0,8,95},{0,8,31},{0,9,158},{20,7,99},
+ {0,8,127},{0,8,63},{0,9,222},{18,7,27},{0,8,111},{0,8,47},{0,9,190},
+ {0,8,15},{0,8,143},{0,8,79},{0,9,254},{96,7,0},{0,8,80},{0,8,16},
+ {20,8,115},{18,7,31},{0,8,112},{0,8,48},{0,9,193},{16,7,10},{0,8,96},
+ {0,8,32},{0,9,161},{0,8,0},{0,8,128},{0,8,64},{0,9,225},{16,7,6},
+ {0,8,88},{0,8,24},{0,9,145},{19,7,59},{0,8,120},{0,8,56},{0,9,209},
+ {17,7,17},{0,8,104},{0,8,40},{0,9,177},{0,8,8},{0,8,136},{0,8,72},
+ {0,9,241},{16,7,4},{0,8,84},{0,8,20},{21,8,227},{19,7,43},{0,8,116},
+ {0,8,52},{0,9,201},{17,7,13},{0,8,100},{0,8,36},{0,9,169},{0,8,4},
+ {0,8,132},{0,8,68},{0,9,233},{16,7,8},{0,8,92},{0,8,28},{0,9,153},
+ {20,7,83},{0,8,124},{0,8,60},{0,9,217},{18,7,23},{0,8,108},{0,8,44},
+ {0,9,185},{0,8,12},{0,8,140},{0,8,76},{0,9,249},{16,7,3},{0,8,82},
+ {0,8,18},{21,8,163},{19,7,35},{0,8,114},{0,8,50},{0,9,197},{17,7,11},
+ {0,8,98},{0,8,34},{0,9,165},{0,8,2},{0,8,130},{0,8,66},{0,9,229},
+ {16,7,7},{0,8,90},{0,8,26},{0,9,149},{20,7,67},{0,8,122},{0,8,58},
+ {0,9,213},{18,7,19},{0,8,106},{0,8,42},{0,9,181},{0,8,10},{0,8,138},
+ {0,8,74},{0,9,245},{16,7,5},{0,8,86},{0,8,22},{64,8,0},{19,7,51},
+ {0,8,118},{0,8,54},{0,9,205},{17,7,15},{0,8,102},{0,8,38},{0,9,173},
+ {0,8,6},{0,8,134},{0,8,70},{0,9,237},{16,7,9},{0,8,94},{0,8,30},
+ {0,9,157},{20,7,99},{0,8,126},{0,8,62},{0,9,221},{18,7,27},{0,8,110},
+ {0,8,46},{0,9,189},{0,8,14},{0,8,142},{0,8,78},{0,9,253},{96,7,0},
+ {0,8,81},{0,8,17},{21,8,131},{18,7,31},{0,8,113},{0,8,49},{0,9,195},
+ {16,7,10},{0,8,97},{0,8,33},{0,9,163},{0,8,1},{0,8,129},{0,8,65},
+ {0,9,227},{16,7,6},{0,8,89},{0,8,25},{0,9,147},{19,7,59},{0,8,121},
+ {0,8,57},{0,9,211},{17,7,17},{0,8,105},{0,8,41},{0,9,179},{0,8,9},
+ {0,8,137},{0,8,73},{0,9,243},{16,7,4},{0,8,85},{0,8,21},{16,8,258},
+ {19,7,43},{0,8,117},{0,8,53},{0,9,203},{17,7,13},{0,8,101},{0,8,37},
+ {0,9,171},{0,8,5},{0,8,133},{0,8,69},{0,9,235},{16,7,8},{0,8,93},
+ {0,8,29},{0,9,155},{20,7,83},{0,8,125},{0,8,61},{0,9,219},{18,7,23},
+ {0,8,109},{0,8,45},{0,9,187},{0,8,13},{0,8,141},{0,8,77},{0,9,251},
+ {16,7,3},{0,8,83},{0,8,19},{21,8,195},{19,7,35},{0,8,115},{0,8,51},
+ {0,9,199},{17,7,11},{0,8,99},{0,8,35},{0,9,167},{0,8,3},{0,8,131},
+ {0,8,67},{0,9,231},{16,7,7},{0,8,91},{0,8,27},{0,9,151},{20,7,67},
+ {0,8,123},{0,8,59},{0,9,215},{18,7,19},{0,8,107},{0,8,43},{0,9,183},
+ {0,8,11},{0,8,139},{0,8,75},{0,9,247},{16,7,5},{0,8,87},{0,8,23},
+ {64,8,0},{19,7,51},{0,8,119},{0,8,55},{0,9,207},{17,7,15},{0,8,103},
+ {0,8,39},{0,9,175},{0,8,7},{0,8,135},{0,8,71},{0,9,239},{16,7,9},
+ {0,8,95},{0,8,31},{0,9,159},{20,7,99},{0,8,127},{0,8,63},{0,9,223},
+ {18,7,27},{0,8,111},{0,8,47},{0,9,191},{0,8,15},{0,8,143},{0,8,79},
+ {0,9,255}
+ };
+
+ static const code distfix[32] = {
+ {16,5,1},{23,5,257},{19,5,17},{27,5,4097},{17,5,5},{25,5,1025},
+ {21,5,65},{29,5,16385},{16,5,3},{24,5,513},{20,5,33},{28,5,8193},
+ {18,5,9},{26,5,2049},{22,5,129},{64,5,0},{16,5,2},{23,5,385},
+ {19,5,25},{27,5,6145},{17,5,7},{25,5,1537},{21,5,97},{29,5,24577},
+ {16,5,4},{24,5,769},{20,5,49},{28,5,12289},{18,5,13},{26,5,3073},
+ {22,5,193},{64,5,0}
+ };
diff --git a/src/zlib/inflate.c b/src/zlib/inflate.c
new file mode 100755
index 0000000..a53b5c7
--- /dev/null
+++ b/src/zlib/inflate.c
@@ -0,0 +1,1270 @@
+/* inflate.c -- zlib decompression
+ * Copyright (C) 1995-2003 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/*
+ * Change history:
+ *
+ * 1.2.beta0 24 Nov 2002
+ * - First version -- complete rewrite of inflate to simplify code, avoid
+ * creation of window when not needed, minimize use of window when it is
+ * needed, make inffast.c even faster, implement gzip decoding, and to
+ * improve code readability and style over the previous zlib inflate code
+ *
+ * 1.2.beta1 25 Nov 2002
+ * - Use pointers for available input and output checking in inffast.c
+ * - Remove input and output counters in inffast.c
+ * - Change inffast.c entry and loop from avail_in >= 7 to >= 6
+ * - Remove unnecessary second byte pull from length extra in inffast.c
+ * - Unroll direct copy to three copies per loop in inffast.c
+ *
+ * 1.2.beta2 4 Dec 2002
+ * - Change external routine names to reduce potential conflicts
+ * - Correct filename to inffixed.h for fixed tables in inflate.c
+ * - Make hbuf[] unsigned char to match parameter type in inflate.c
+ * - Change strm->next_out[-state->offset] to *(strm->next_out - state->offset)
+ * to avoid negation problem on Alphas (64 bit) in inflate.c
+ *
+ * 1.2.beta3 22 Dec 2002
+ * - Add comments on state->bits assertion in inffast.c
+ * - Add comments on op field in inftrees.h
+ * - Fix bug in reuse of allocated window after inflateReset()
+ * - Remove bit fields--back to byte structure for speed
+ * - Remove distance extra == 0 check in inflate_fast()--only helps for lengths
+ * - Change post-increments to pre-increments in inflate_fast(), PPC biased?
+ * - Add compile time option, POSTINC, to use post-increments instead (Intel?)
+ * - Make MATCH copy in inflate() much faster for when inflate_fast() not used
+ * - Use local copies of stream next and avail values, as well as local bit
+ * buffer and bit count in inflate()--for speed when inflate_fast() not used
+ *
+ * 1.2.beta4 1 Jan 2003
+ * - Split ptr - 257 statements in inflate_table() to avoid compiler warnings
+ * - Move a comment on output buffer sizes from inffast.c to inflate.c
+ * - Add comments in inffast.c to introduce the inflate_fast() routine
+ * - Rearrange window copies in inflate_fast() for speed and simplification
+ * - Unroll last copy for window match in inflate_fast()
+ * - Use local copies of window variables in inflate_fast() for speed
+ * - Pull out common write == 0 case for speed in inflate_fast()
+ * - Make op and len in inflate_fast() unsigned for consistency
+ * - Add FAR to lcode and dcode declarations in inflate_fast()
+ * - Simplified bad distance check in inflate_fast()
+ * - Added inflateBackInit(), inflateBack(), and inflateBackEnd() in new
+ * source file infback.c to provide a call-back interface to inflate for
+ * programs like gzip and unzip -- uses window as output buffer to avoid
+ * window copying
+ *
+ * 1.2.beta5 1 Jan 2003
+ * - Improved inflateBack() interface to allow the caller to provide initial
+ * input in strm.
+ * - Fixed stored blocks bug in inflateBack()
+ *
+ * 1.2.beta6 4 Jan 2003
+ * - Added comments in inffast.c on effectiveness of POSTINC
+ * - Typecasting all around to reduce compiler warnings
+ * - Changed loops from while (1) or do {} while (1) to for (;;), again to
+ * make compilers happy
+ * - Changed type of window in inflateBackInit() to unsigned char *
+ *
+ * 1.2.beta7 27 Jan 2003
+ * - Changed many types to unsigned or unsigned short to avoid warnings
+ * - Added inflateCopy() function
+ *
+ * 1.2.0 9 Mar 2003
+ * - Changed inflateBack() interface to provide separate opaque descriptors
+ * for the in() and out() functions
+ * - Changed inflateBack() argument and in_func typedef to swap the length
+ * and buffer address return values for the input function
+ * - Check next_in and next_out for Z_NULL on entry to inflate()
+ *
+ * The history for versions after 1.2.0 are in ChangeLog in zlib distribution.
+ */
+
+#include "zutil.h"
+#include "inftrees.h"
+#include "inflate.h"
+#include "inffast.h"
+
+#ifdef MAKEFIXED
+# ifndef BUILDFIXED
+# define BUILDFIXED
+# endif
+#endif
+
+/* function prototypes */
+local void fixedtables OF((struct inflate_state FAR *state));
+local int updatewindow OF((z_streamp strm, unsigned out));
+#ifdef BUILDFIXED
+ void makefixed OF((void));
+#endif
+local unsigned syncsearch OF((unsigned FAR *have, unsigned char FAR *buf,
+ unsigned len));
+
+int ZEXPORT inflateReset(strm)
+z_streamp strm;
+{
+ struct inflate_state FAR *state;
+
+ if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR;
+ state = (struct inflate_state FAR *)strm->state;
+ strm->total_in = strm->total_out = state->total = 0;
+ strm->msg = Z_NULL;
+ state->mode = HEAD;
+ state->last = 0;
+ state->havedict = 0;
+ state->wsize = 0;
+ state->whave = 0;
+ state->hold = 0;
+ state->bits = 0;
+ state->lencode = state->distcode = state->next = state->codes;
+ Tracev((stderr, "inflate: reset\n"));
+ return Z_OK;
+}
+
+int ZEXPORT inflateInit2_(strm, windowBits, version, stream_size)
+z_streamp strm;
+int windowBits;
+const char *version;
+int stream_size;
+{
+ struct inflate_state FAR *state;
+
+ if (version == Z_NULL || version[0] != ZLIB_VERSION[0] ||
+ stream_size != (int)(sizeof(z_stream)))
+ return Z_VERSION_ERROR;
+ if (strm == Z_NULL) return Z_STREAM_ERROR;
+ strm->msg = Z_NULL; /* in case we return an error */
+ if (strm->zalloc == (alloc_func)0) {
+ strm->zalloc = zcalloc;
+ strm->opaque = (voidpf)0;
+ }
+ if (strm->zfree == (free_func)0) strm->zfree = zcfree;
+ state = (struct inflate_state FAR *)
+ ZALLOC(strm, 1, sizeof(struct inflate_state));
+ if (state == Z_NULL) return Z_MEM_ERROR;
+ Tracev((stderr, "inflate: allocated\n"));
+ strm->state = (voidpf)state;
+ if (windowBits < 0) {
+ state->wrap = 0;
+ windowBits = -windowBits;
+ }
+ else {
+ state->wrap = (windowBits >> 4) + 1;
+#ifdef GUNZIP
+ if (windowBits < 48) windowBits &= 15;
+#endif
+ }
+ if (windowBits < 8 || windowBits > 15) {
+ ZFREE(strm, state);
+ strm->state = Z_NULL;
+ return Z_STREAM_ERROR;
+ }
+ state->wbits = (unsigned)windowBits;
+ state->window = Z_NULL;
+ return inflateReset(strm);
+}
+
+int ZEXPORT inflateInit_(strm, version, stream_size)
+z_streamp strm;
+const char *version;
+int stream_size;
+{
+ return inflateInit2_(strm, DEF_WBITS, version, stream_size);
+}
+
+/*
+ Return state with length and distance decoding tables and index sizes set to
+ fixed code decoding. Normally this returns fixed tables from inffixed.h.
+ If BUILDFIXED is defined, then instead this routine builds the tables the
+ first time it's called, and returns those tables the first time and
+ thereafter. This reduces the size of the code by about 2K bytes, in
+ exchange for a little execution time. However, BUILDFIXED should not be
+ used for threaded applications, since the rewriting of the tables and virgin
+ may not be thread-safe.
+ */
+local void fixedtables(state)
+struct inflate_state FAR *state;
+{
+#ifdef BUILDFIXED
+ static int virgin = 1;
+ static code *lenfix, *distfix;
+ static code fixed[544];
+
+ /* build fixed huffman tables if first call (may not be thread safe) */
+ if (virgin) {
+ unsigned sym, bits;
+ static code *next;
+
+ /* literal/length table */
+ sym = 0;
+ while (sym < 144) state->lens[sym++] = 8;
+ while (sym < 256) state->lens[sym++] = 9;
+ while (sym < 280) state->lens[sym++] = 7;
+ while (sym < 288) state->lens[sym++] = 8;
+ next = fixed;
+ lenfix = next;
+ bits = 9;
+ inflate_table(LENS, state->lens, 288, &(next), &(bits), state->work);
+
+ /* distance table */
+ sym = 0;
+ while (sym < 32) state->lens[sym++] = 5;
+ distfix = next;
+ bits = 5;
+ inflate_table(DISTS, state->lens, 32, &(next), &(bits), state->work);
+
+ /* do this just once */
+ virgin = 0;
+ }
+#else /* !BUILDFIXED */
+# include "inffixed.h"
+#endif /* BUILDFIXED */
+ state->lencode = lenfix;
+ state->lenbits = 9;
+ state->distcode = distfix;
+ state->distbits = 5;
+}
+
+#ifdef MAKEFIXED
+#include <stdio.h>
+
+/*
+ Write out the inffixed.h that is #include'd above. Defining MAKEFIXED also
+ defines BUILDFIXED, so the tables are built on the fly. makefixed() writes
+ those tables to stdout, which would be piped to inffixed.h. A small program
+ can simply call makefixed to do this:
+
+ void makefixed(void);
+
+ int main(void)
+ {
+ makefixed();
+ return 0;
+ }
+
+ Then that can be linked with zlib built with MAKEFIXED defined and run:
+
+ a.out > inffixed.h
+ */
+void makefixed()
+{
+ unsigned low, size;
+ struct inflate_state state;
+
+ fixedtables(&state);
+ puts(" /* inffixed.h -- table for decoding fixed codes");
+ puts(" * Generated automatically by makefixed().");
+ puts(" */");
+ puts("");
+ puts(" /* WARNING: this file should *not* be used by applications.");
+ puts(" It is part of the implementation of this library and is");
+ puts(" subject to change. Applications should only use zlib.h.");
+ puts(" */");
+ puts("");
+ size = 1U << 9;
+ printf(" static const code lenfix[%u] = {", size);
+ low = 0;
+ for (;;) {
+ if ((low % 7) == 0) printf("\n ");
+ printf("{%u,%u,%d}", state.lencode[low].op, state.lencode[low].bits,
+ state.lencode[low].val);
+ if (++low == size) break;
+ putchar(',');
+ }
+ puts("\n };");
+ size = 1U << 5;
+ printf("\n static const code distfix[%u] = {", size);
+ low = 0;
+ for (;;) {
+ if ((low % 6) == 0) printf("\n ");
+ printf("{%u,%u,%d}", state.distcode[low].op, state.distcode[low].bits,
+ state.distcode[low].val);
+ if (++low == size) break;
+ putchar(',');
+ }
+ puts("\n };");
+}
+#endif /* MAKEFIXED */
+
+/*
+ Update the window with the last wsize (normally 32K) bytes written before
+ returning. If window does not exist yet, create it. This is only called
+ when a window is already in use, or when output has been written during this
+ inflate call, but the end of the deflate stream has not been reached yet.
+ It is also called to create a window for dictionary data when a dictionary
+ is loaded.
+
+ Providing output buffers larger than 32K to inflate() should provide a speed
+ advantage, since only the last 32K of output is copied to the sliding window
+ upon return from inflate(), and since all distances after the first 32K of
+ output will fall in the output data, making match copies simpler and faster.
+ The advantage may be dependent on the size of the processor's data caches.
+ */
+local int updatewindow(strm, out)
+z_streamp strm;
+unsigned out;
+{
+ struct inflate_state FAR *state;
+ unsigned copy, dist;
+
+ state = (struct inflate_state FAR *)strm->state;
+
+ /* if it hasn't been done already, allocate space for the window */
+ if (state->window == Z_NULL) {
+ state->window = (unsigned char FAR *)
+ ZALLOC(strm, 1U << state->wbits,
+ sizeof(unsigned char));
+ if (state->window == Z_NULL) return 1;
+ }
+
+ /* if window not in use yet, initialize */
+ if (state->wsize == 0) {
+ state->wsize = 1U << state->wbits;
+ state->write = 0;
+ state->whave = 0;
+ }
+
+ /* copy state->wsize or less output bytes into the circular window */
+ copy = out - strm->avail_out;
+ if (copy >= state->wsize) {
+ zmemcpy(state->window, strm->next_out - state->wsize, state->wsize);
+ state->write = 0;
+ state->whave = state->wsize;
+ }
+ else {
+ dist = state->wsize - state->write;
+ if (dist > copy) dist = copy;
+ zmemcpy(state->window + state->write, strm->next_out - copy, dist);
+ copy -= dist;
+ if (copy) {
+ zmemcpy(state->window, strm->next_out - copy, copy);
+ state->write = copy;
+ state->whave = state->wsize;
+ }
+ else {
+ state->write += dist;
+ if (state->write == state->wsize) state->write = 0;
+ if (state->whave < state->wsize) state->whave += dist;
+ }
+ }
+ return 0;
+}
+
+/* Macros for inflate(): */
+
+/* check function to use adler32() for zlib or crc32() for gzip */
+#ifdef GUNZIP
+# define UPDATE(check, buf, len) \
+ (state->flags ? crc32(check, buf, len) : adler32(check, buf, len))
+#else
+# define UPDATE(check, buf, len) adler32(check, buf, len)
+#endif
+
+/* check macros for header crc */
+#ifdef GUNZIP
+# define CRC2(check, word) \
+ do { \
+ hbuf[0] = (unsigned char)(word); \
+ hbuf[1] = (unsigned char)((word) >> 8); \
+ check = crc32(check, hbuf, 2); \
+ } while (0)
+
+# define CRC4(check, word) \
+ do { \
+ hbuf[0] = (unsigned char)(word); \
+ hbuf[1] = (unsigned char)((word) >> 8); \
+ hbuf[2] = (unsigned char)((word) >> 16); \
+ hbuf[3] = (unsigned char)((word) >> 24); \
+ check = crc32(check, hbuf, 4); \
+ } while (0)
+#endif
+
+/* Load registers with state in inflate() for speed */
+#define LOAD() \
+ do { \
+ put = strm->next_out; \
+ left = strm->avail_out; \
+ next = strm->next_in; \
+ have = strm->avail_in; \
+ hold = state->hold; \
+ bits = state->bits; \
+ } while (0)
+
+/* Restore state from registers in inflate() */
+#define RESTORE() \
+ do { \
+ strm->next_out = put; \
+ strm->avail_out = left; \
+ strm->next_in = next; \
+ strm->avail_in = have; \
+ state->hold = hold; \
+ state->bits = bits; \
+ } while (0)
+
+/* Clear the input bit accumulator */
+#define INITBITS() \
+ do { \
+ hold = 0; \
+ bits = 0; \
+ } while (0)
+
+/* Get a byte of input into the bit accumulator, or return from inflate()
+ if there is no input available. */
+#define PULLBYTE() \
+ do { \
+ if (have == 0) goto inf_leave; \
+ have--; \
+ hold += (unsigned long)(*next++) << bits; \
+ bits += 8; \
+ } while (0)
+
+/* Assure that there are at least n bits in the bit accumulator. If there is
+ not enough available input to do that, then return from inflate(). */
+#define NEEDBITS(n) \
+ do { \
+ while (bits < (unsigned)(n)) \
+ PULLBYTE(); \
+ } while (0)
+
+/* Return the low n bits of the bit accumulator (n < 16) */
+#define BITS(n) \
+ ((unsigned)hold & ((1U << (n)) - 1))
+
+/* Remove n bits from the bit accumulator */
+#define DROPBITS(n) \
+ do { \
+ hold >>= (n); \
+ bits -= (unsigned)(n); \
+ } while (0)
+
+/* Remove zero to seven bits as needed to go to a byte boundary */
+#define BYTEBITS() \
+ do { \
+ hold >>= bits & 7; \
+ bits -= bits & 7; \
+ } while (0)
+
+/* Reverse the bytes in a 32-bit value */
+#define REVERSE(q) \
+ ((((q) >> 24) & 0xff) + (((q) >> 8) & 0xff00) + \
+ (((q) & 0xff00) << 8) + (((q) & 0xff) << 24))
+
+/*
+ inflate() uses a state machine to process as much input data and generate as
+ much output data as possible before returning. The state machine is
+ structured roughly as follows:
+
+ for (;;) switch (state) {
+ ...
+ case STATEn:
+ if (not enough input data or output space to make progress)
+ return;
+ ... make progress ...
+ state = STATEm;
+ break;
+ ...
+ }
+
+ so when inflate() is called again, the same case is attempted again, and
+ if the appropriate resources are provided, the machine proceeds to the
+ next state. The NEEDBITS() macro is usually the way the state evaluates
+ whether it can proceed or should return. NEEDBITS() does the return if
+ the requested bits are not available. The typical use of the BITS macros
+ is:
+
+ NEEDBITS(n);
+ ... do something with BITS(n) ...
+ DROPBITS(n);
+
+ where NEEDBITS(n) either returns from inflate() if there isn't enough
+ input left to load n bits into the accumulator, or it continues. BITS(n)
+ gives the low n bits in the accumulator. When done, DROPBITS(n) drops
+ the low n bits off the accumulator. INITBITS() clears the accumulator
+ and sets the number of available bits to zero. BYTEBITS() discards just
+ enough bits to put the accumulator on a byte boundary. After BYTEBITS()
+ and a NEEDBITS(8), then BITS(8) would return the next byte in the stream.
+
+ NEEDBITS(n) uses PULLBYTE() to get an available byte of input, or to return
+ if there is no input available. The decoding of variable length codes uses
+ PULLBYTE() directly in order to pull just enough bytes to decode the next
+ code, and no more.
+
+ Some states loop until they get enough input, making sure that enough
+ state information is maintained to continue the loop where it left off
+ if NEEDBITS() returns in the loop. For example, want, need, and keep
+ would all have to actually be part of the saved state in case NEEDBITS()
+ returns:
+
+ case STATEw:
+ while (want < need) {
+ NEEDBITS(n);
+ keep[want++] = BITS(n);
+ DROPBITS(n);
+ }
+ state = STATEx;
+ case STATEx:
+
+ As shown above, if the next state is also the next case, then the break
+ is omitted.
+
+ A state may also return if there is not enough output space available to
+ complete that state. Those states are copying stored data, writing a
+ literal byte, and copying a matching string.
+
+ When returning, a "goto inf_leave" is used to update the total counters,
+ update the check value, and determine whether any progress has been made
+ during that inflate() call in order to return the proper return code.
+ Progress is defined as a change in either strm->avail_in or strm->avail_out.
+ When there is a window, goto inf_leave will update the window with the last
+ output written. If a goto inf_leave occurs in the middle of decompression
+ and there is no window currently, goto inf_leave will create one and copy
+ output to the window for the next call of inflate().
+
+ In this implementation, the flush parameter of inflate() only affects the
+ return code (per zlib.h). inflate() always writes as much as possible to
+ strm->next_out, given the space available and the provided input--the effect
+ documented in zlib.h of Z_SYNC_FLUSH. Furthermore, inflate() always defers
+ the allocation of and copying into a sliding window until necessary, which
+ provides the effect documented in zlib.h for Z_FINISH when the entire input
+ stream available. So the only thing the flush parameter actually does is:
+ when flush is set to Z_FINISH, inflate() cannot return Z_OK. Instead it
+ will return Z_BUF_ERROR if it has not reached the end of the stream.
+ */
+
+int ZEXPORT inflate(strm, flush)
+z_streamp strm;
+int flush;
+{
+ struct inflate_state FAR *state;
+ unsigned char FAR *next; /* next input */
+ unsigned char FAR *put; /* next output */
+ unsigned have, left; /* available input and output */
+ unsigned long hold; /* bit buffer */
+ unsigned bits; /* bits in bit buffer */
+ unsigned in, out; /* save starting available input and output */
+ unsigned copy; /* number of stored or match bytes to copy */
+ unsigned char FAR *from; /* where to copy match bytes from */
+ code this; /* current decoding table entry */
+ code last; /* parent table entry */
+ unsigned len; /* length to copy for repeats, bits to drop */
+ int ret; /* return code */
+#ifdef GUNZIP
+ unsigned char hbuf[4]; /* buffer for gzip header crc calculation */
+#endif
+ static const unsigned short order[19] = /* permutation of code lengths */
+ {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15};
+
+ if (strm == Z_NULL || strm->state == Z_NULL || strm->next_out == Z_NULL ||
+ (strm->next_in == Z_NULL && strm->avail_in != 0))
+ return Z_STREAM_ERROR;
+
+ state = (struct inflate_state FAR *)strm->state;
+ if (state->mode == TYPE) state->mode = TYPEDO; /* skip check */
+ LOAD();
+ in = have;
+ out = left;
+ ret = Z_OK;
+ for (;;)
+ switch (state->mode) {
+ case HEAD:
+ if (state->wrap == 0) {
+ state->mode = TYPEDO;
+ break;
+ }
+ NEEDBITS(16);
+#ifdef GUNZIP
+ if ((state->wrap & 2) && hold == 0x8b1f) { /* gzip header */
+ state->check = crc32(0L, Z_NULL, 0);
+ CRC2(state->check, hold);
+ INITBITS();
+ state->mode = FLAGS;
+ break;
+ }
+ state->flags = 0; /* expect zlib header */
+ if (!(state->wrap & 1) || /* check if zlib header allowed */
+#else
+ if (
+#endif
+ ((BITS(8) << 8) + (hold >> 8)) % 31) {
+ strm->msg = (char *)"incorrect header check";
+ state->mode = BAD;
+ break;
+ }
+ if (BITS(4) != Z_DEFLATED) {
+ strm->msg = (char *)"unknown compression method";
+ state->mode = BAD;
+ break;
+ }
+ DROPBITS(4);
+ if (BITS(4) + 8 > state->wbits) {
+ strm->msg = (char *)"invalid window size";
+ state->mode = BAD;
+ break;
+ }
+ Tracev((stderr, "inflate: zlib header ok\n"));
+ strm->adler = state->check = adler32(0L, Z_NULL, 0);
+ state->mode = hold & 0x200 ? DICTID : TYPE;
+ INITBITS();
+ break;
+#ifdef GUNZIP
+ case FLAGS:
+ NEEDBITS(16);
+ state->flags = (int)(hold);
+ if ((state->flags & 0xff) != Z_DEFLATED) {
+ strm->msg = (char *)"unknown compression method";
+ state->mode = BAD;
+ break;
+ }
+ if (state->flags & 0xe000) {
+ strm->msg = (char *)"unknown header flags set";
+ state->mode = BAD;
+ break;
+ }
+ if (state->flags & 0x0200) CRC2(state->check, hold);
+ INITBITS();
+ state->mode = TIME;
+ case TIME:
+ NEEDBITS(32);
+ if (state->flags & 0x0200) CRC4(state->check, hold);
+ INITBITS();
+ state->mode = OS;
+ case OS:
+ NEEDBITS(16);
+ if (state->flags & 0x0200) CRC2(state->check, hold);
+ INITBITS();
+ state->mode = EXLEN;
+ case EXLEN:
+ if (state->flags & 0x0400) {
+ NEEDBITS(16);
+ state->length = (unsigned)(hold);
+ if (state->flags & 0x0200) CRC2(state->check, hold);
+ INITBITS();
+ }
+ state->mode = EXTRA;
+ case EXTRA:
+ if (state->flags & 0x0400) {
+ copy = state->length;
+ if (copy > have) copy = have;
+ if (copy) {
+ if (state->flags & 0x0200)
+ state->check = crc32(state->check, next, copy);
+ have -= copy;
+ next += copy;
+ state->length -= copy;
+ }
+ if (state->length) goto inf_leave;
+ }
+ state->mode = NAME;
+ case NAME:
+ if (state->flags & 0x0800) {
+ if (have == 0) goto inf_leave;
+ copy = 0;
+ do {
+ len = (unsigned)(next[copy++]);
+ } while (len && copy < have);
+ if (state->flags & 0x02000)
+ state->check = crc32(state->check, next, copy);
+ have -= copy;
+ next += copy;
+ if (len) goto inf_leave;
+ }
+ state->mode = COMMENT;
+ case COMMENT:
+ if (state->flags & 0x1000) {
+ if (have == 0) goto inf_leave;
+ copy = 0;
+ do {
+ len = (unsigned)(next[copy++]);
+ } while (len && copy < have);
+ if (state->flags & 0x02000)
+ state->check = crc32(state->check, next, copy);
+ have -= copy;
+ next += copy;
+ if (len) goto inf_leave;
+ }
+ state->mode = HCRC;
+ case HCRC:
+ if (state->flags & 0x0200) {
+ NEEDBITS(16);
+ if (hold != (state->check & 0xffff)) {
+ strm->msg = (char *)"header crc mismatch";
+ state->mode = BAD;
+ break;
+ }
+ INITBITS();
+ }
+ strm->adler = state->check = crc32(0L, Z_NULL, 0);
+ state->mode = TYPE;
+ break;
+#endif
+ case DICTID:
+ NEEDBITS(32);
+ strm->adler = state->check = REVERSE(hold);
+ INITBITS();
+ state->mode = DICT;
+ case DICT:
+ if (state->havedict == 0) {
+ RESTORE();
+ return Z_NEED_DICT;
+ }
+ strm->adler = state->check = adler32(0L, Z_NULL, 0);
+ state->mode = TYPE;
+ case TYPE:
+ if (flush == Z_BLOCK) goto inf_leave;
+ case TYPEDO:
+ if (state->last) {
+ BYTEBITS();
+ state->mode = CHECK;
+ break;
+ }
+ NEEDBITS(3);
+ state->last = BITS(1);
+ DROPBITS(1);
+ switch (BITS(2)) {
+ case 0: /* stored block */
+ Tracev((stderr, "inflate: stored block%s\n",
+ state->last ? " (last)" : ""));
+ state->mode = STORED;
+ break;
+ case 1: /* fixed block */
+ fixedtables(state);
+ Tracev((stderr, "inflate: fixed codes block%s\n",
+ state->last ? " (last)" : ""));
+ state->mode = LEN; /* decode codes */
+ break;
+ case 2: /* dynamic block */
+ Tracev((stderr, "inflate: dynamic codes block%s\n",
+ state->last ? " (last)" : ""));
+ state->mode = TABLE;
+ break;
+ case 3:
+ strm->msg = (char *)"invalid block type";
+ state->mode = BAD;
+ }
+ DROPBITS(2);
+ break;
+ case STORED:
+ BYTEBITS(); /* go to byte boundary */
+ NEEDBITS(32);
+ if ((hold & 0xffff) != ((hold >> 16) ^ 0xffff)) {
+ strm->msg = (char *)"invalid stored block lengths";
+ state->mode = BAD;
+ break;
+ }
+ state->length = (unsigned)hold & 0xffff;
+ Tracev((stderr, "inflate: stored length %u\n",
+ state->length));
+ INITBITS();
+ state->mode = COPY;
+ case COPY:
+ copy = state->length;
+ if (copy) {
+ if (copy > have) copy = have;
+ if (copy > left) copy = left;
+ if (copy == 0) goto inf_leave;
+ zmemcpy(put, next, copy);
+ have -= copy;
+ next += copy;
+ left -= copy;
+ put += copy;
+ state->length -= copy;
+ break;
+ }
+ Tracev((stderr, "inflate: stored end\n"));
+ state->mode = TYPE;
+ break;
+ case TABLE:
+ NEEDBITS(14);
+ state->nlen = BITS(5) + 257;
+ DROPBITS(5);
+ state->ndist = BITS(5) + 1;
+ DROPBITS(5);
+ state->ncode = BITS(4) + 4;
+ DROPBITS(4);
+#ifndef PKZIP_BUG_WORKAROUND
+ if (state->nlen > 286 || state->ndist > 30) {
+ strm->msg = (char *)"too many length or distance symbols";
+ state->mode = BAD;
+ break;
+ }
+#endif
+ Tracev((stderr, "inflate: table sizes ok\n"));
+ state->have = 0;
+ state->mode = LENLENS;
+ case LENLENS:
+ while (state->have < state->ncode) {
+ NEEDBITS(3);
+ state->lens[order[state->have++]] = (unsigned short)BITS(3);
+ DROPBITS(3);
+ }
+ while (state->have < 19)
+ state->lens[order[state->have++]] = 0;
+ state->next = state->codes;
+ state->lencode = (code const FAR *)(state->next);
+ state->lenbits = 7;
+ ret = inflate_table(CODES, state->lens, 19, &(state->next),
+ &(state->lenbits), state->work);
+ if (ret) {
+ strm->msg = (char *)"invalid code lengths set";
+ state->mode = BAD;
+ break;
+ }
+ Tracev((stderr, "inflate: code lengths ok\n"));
+ state->have = 0;
+ state->mode = CODELENS;
+ case CODELENS:
+ while (state->have < state->nlen + state->ndist) {
+ for (;;) {
+ this = state->lencode[BITS(state->lenbits)];
+ if ((unsigned)(this.bits) <= bits) break;
+ PULLBYTE();
+ }
+ if (this.val < 16) {
+ NEEDBITS(this.bits);
+ DROPBITS(this.bits);
+ state->lens[state->have++] = this.val;
+ }
+ else {
+ if (this.val == 16) {
+ NEEDBITS(this.bits + 2);
+ DROPBITS(this.bits);
+ if (state->have == 0) {
+ strm->msg = (char *)"invalid bit length repeat";
+ state->mode = BAD;
+ break;
+ }
+ len = state->lens[state->have - 1];
+ copy = 3 + BITS(2);
+ DROPBITS(2);
+ }
+ else if (this.val == 17) {
+ NEEDBITS(this.bits + 3);
+ DROPBITS(this.bits);
+ len = 0;
+ copy = 3 + BITS(3);
+ DROPBITS(3);
+ }
+ else {
+ NEEDBITS(this.bits + 7);
+ DROPBITS(this.bits);
+ len = 0;
+ copy = 11 + BITS(7);
+ DROPBITS(7);
+ }
+ if (state->have + copy > state->nlen + state->ndist) {
+ strm->msg = (char *)"invalid bit length repeat";
+ state->mode = BAD;
+ break;
+ }
+ while (copy--)
+ state->lens[state->have++] = (unsigned short)len;
+ }
+ }
+
+ /* build code tables */
+ state->next = state->codes;
+ state->lencode = (code const FAR *)(state->next);
+ state->lenbits = 9;
+ ret = inflate_table(LENS, state->lens, state->nlen, &(state->next),
+ &(state->lenbits), state->work);
+ if (ret) {
+ strm->msg = (char *)"invalid literal/lengths set";
+ state->mode = BAD;
+ break;
+ }
+ state->distcode = (code const FAR *)(state->next);
+ state->distbits = 6;
+ ret = inflate_table(DISTS, state->lens + state->nlen, state->ndist,
+ &(state->next), &(state->distbits), state->work);
+ if (ret) {
+ strm->msg = (char *)"invalid distances set";
+ state->mode = BAD;
+ break;
+ }
+ Tracev((stderr, "inflate: codes ok\n"));
+ state->mode = LEN;
+ case LEN:
+ if (have >= 6 && left >= 258) {
+ RESTORE();
+ inflate_fast(strm, out);
+ LOAD();
+ break;
+ }
+ for (;;) {
+ this = state->lencode[BITS(state->lenbits)];
+ if ((unsigned)(this.bits) <= bits) break;
+ PULLBYTE();
+ }
+ if (this.op && (this.op & 0xf0) == 0) {
+ last = this;
+ for (;;) {
+ this = state->lencode[last.val +
+ (BITS(last.bits + last.op) >> last.bits)];
+ if ((unsigned)(last.bits + this.bits) <= bits) break;
+ PULLBYTE();
+ }
+ DROPBITS(last.bits);
+ }
+ DROPBITS(this.bits);
+ state->length = (unsigned)this.val;
+ if ((int)(this.op) == 0) {
+ Tracevv((stderr, this.val >= 0x20 && this.val < 0x7f ?
+ "inflate: literal '%c'\n" :
+ "inflate: literal 0x%02x\n", this.val));
+ state->mode = LIT;
+ break;
+ }
+ if (this.op & 32) {
+ Tracevv((stderr, "inflate: end of block\n"));
+ state->mode = TYPE;
+ break;
+ }
+ if (this.op & 64) {
+ strm->msg = (char *)"invalid literal/length code";
+ state->mode = BAD;
+ break;
+ }
+ state->extra = (unsigned)(this.op) & 15;
+ state->mode = LENEXT;
+ case LENEXT:
+ if (state->extra) {
+ NEEDBITS(state->extra);
+ state->length += BITS(state->extra);
+ DROPBITS(state->extra);
+ }
+ Tracevv((stderr, "inflate: length %u\n", state->length));
+ state->mode = DIST;
+ case DIST:
+ for (;;) {
+ this = state->distcode[BITS(state->distbits)];
+ if ((unsigned)(this.bits) <= bits) break;
+ PULLBYTE();
+ }
+ if ((this.op & 0xf0) == 0) {
+ last = this;
+ for (;;) {
+ this = state->distcode[last.val +
+ (BITS(last.bits + last.op) >> last.bits)];
+ if ((unsigned)(last.bits + this.bits) <= bits) break;
+ PULLBYTE();
+ }
+ DROPBITS(last.bits);
+ }
+ DROPBITS(this.bits);
+ if (this.op & 64) {
+ strm->msg = (char *)"invalid distance code";
+ state->mode = BAD;
+ break;
+ }
+ state->offset = (unsigned)this.val;
+ state->extra = (unsigned)(this.op) & 15;
+ state->mode = DISTEXT;
+ case DISTEXT:
+ if (state->extra) {
+ NEEDBITS(state->extra);
+ state->offset += BITS(state->extra);
+ DROPBITS(state->extra);
+ }
+ if (state->offset > state->whave + out - left) {
+ strm->msg = (char *)"invalid distance too far back";
+ state->mode = BAD;
+ break;
+ }
+ Tracevv((stderr, "inflate: distance %u\n", state->offset));
+ state->mode = MATCH;
+ case MATCH:
+ if (left == 0) goto inf_leave;
+ copy = out - left;
+ if (state->offset > copy) { /* copy from window */
+ copy = state->offset - copy;
+ if (copy > state->write) {
+ copy -= state->write;
+ from = state->window + (state->wsize - copy);
+ }
+ else
+ from = state->window + (state->write - copy);
+ if (copy > state->length) copy = state->length;
+ }
+ else { /* copy from output */
+ from = put - state->offset;
+ copy = state->length;
+ }
+ if (copy > left) copy = left;
+ left -= copy;
+ state->length -= copy;
+ do {
+ *put++ = *from++;
+ } while (--copy);
+ if (state->length == 0) state->mode = LEN;
+ break;
+ case LIT:
+ if (left == 0) goto inf_leave;
+ *put++ = (unsigned char)(state->length);
+ left--;
+ state->mode = LEN;
+ break;
+ case CHECK:
+ if (state->wrap) {
+ NEEDBITS(32);
+ out -= left;
+ strm->total_out += out;
+ state->total += out;
+ if (out)
+ strm->adler = state->check =
+ UPDATE(state->check, put - out, out);
+ out = left;
+ if ((
+#ifdef GUNZIP
+ state->flags ? hold :
+#endif
+ REVERSE(hold)) != state->check) {
+ strm->msg = (char *)"incorrect data check";
+ state->mode = BAD;
+ break;
+ }
+ INITBITS();
+ Tracev((stderr, "inflate: check matches trailer\n"));
+ }
+#ifdef GUNZIP
+ state->mode = LENGTH;
+ case LENGTH:
+ if (state->wrap && state->flags) {
+ NEEDBITS(32);
+ if (hold != (state->total & 0xffffffffUL)) {
+ strm->msg = (char *)"incorrect length check";
+ state->mode = BAD;
+ break;
+ }
+ INITBITS();
+ Tracev((stderr, "inflate: length matches trailer\n"));
+ }
+#endif
+ state->mode = DONE;
+ case DONE:
+ ret = Z_STREAM_END;
+ goto inf_leave;
+ case BAD:
+ ret = Z_DATA_ERROR;
+ goto inf_leave;
+ case MEM:
+ return Z_MEM_ERROR;
+ case SYNC:
+ default:
+ return Z_STREAM_ERROR;
+ }
+
+ /*
+ Return from inflate(), updating the total counts and the check value.
+ If there was no progress during the inflate() call, return a buffer
+ error. Call updatewindow() to create and/or update the window state.
+ Note: a memory error from inflate() is non-recoverable.
+ */
+ inf_leave:
+ RESTORE();
+ if (state->wsize || (state->mode < CHECK && out != strm->avail_out))
+ if (updatewindow(strm, out)) {
+ state->mode = MEM;
+ return Z_MEM_ERROR;
+ }
+ in -= strm->avail_in;
+ out -= strm->avail_out;
+ strm->total_in += in;
+ strm->total_out += out;
+ state->total += out;
+ if (state->wrap && out)
+ strm->adler = state->check =
+ UPDATE(state->check, strm->next_out - out, out);
+ strm->data_type = state->bits + (state->last ? 64 : 0) +
+ (state->mode == TYPE ? 128 : 0);
+ if (((in == 0 && out == 0) || flush == Z_FINISH) && ret == Z_OK)
+ ret = Z_BUF_ERROR;
+ return ret;
+}
+
+int ZEXPORT inflateEnd(strm)
+z_streamp strm;
+{
+ struct inflate_state FAR *state;
+ if (strm == Z_NULL || strm->state == Z_NULL || strm->zfree == (free_func)0)
+ return Z_STREAM_ERROR;
+ state = (struct inflate_state FAR *)strm->state;
+ if (state->window != Z_NULL) ZFREE(strm, state->window);
+ ZFREE(strm, strm->state);
+ strm->state = Z_NULL;
+ Tracev((stderr, "inflate: end\n"));
+ return Z_OK;
+}
+
+int ZEXPORT inflateSetDictionary(strm, dictionary, dictLength)
+z_streamp strm;
+const Bytef *dictionary;
+uInt dictLength;
+{
+ struct inflate_state FAR *state;
+ unsigned long id;
+
+ /* check state */
+ if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR;
+ state = (struct inflate_state FAR *)strm->state;
+ if (state->mode != DICT) return Z_STREAM_ERROR;
+
+ /* check for correct dictionary id */
+ id = adler32(0L, Z_NULL, 0);
+ id = adler32(id, dictionary, dictLength);
+ if (id != state->check) return Z_DATA_ERROR;
+
+ /* copy dictionary to window */
+ if (updatewindow(strm, strm->avail_out)) {
+ state->mode = MEM;
+ return Z_MEM_ERROR;
+ }
+ if (dictLength > state->wsize) {
+ zmemcpy(state->window, dictionary + dictLength - state->wsize,
+ state->wsize);
+ state->whave = state->wsize;
+ }
+ else {
+ zmemcpy(state->window + state->wsize - dictLength, dictionary,
+ dictLength);
+ state->whave = dictLength;
+ }
+ state->havedict = 1;
+ Tracev((stderr, "inflate: dictionary set\n"));
+ return Z_OK;
+}
+
+/*
+ Search buf[0..len-1] for the pattern: 0, 0, 0xff, 0xff. Return when found
+ or when out of input. When called, *have is the number of pattern bytes
+ found in order so far, in 0..3. On return *have is updated to the new
+ state. If on return *have equals four, then the pattern was found and the
+ return value is how many bytes were read including the last byte of the
+ pattern. If *have is less than four, then the pattern has not been found
+ yet and the return value is len. In the latter case, syncsearch() can be
+ called again with more data and the *have state. *have is initialized to
+ zero for the first call.
+ */
+local unsigned syncsearch(have, buf, len)
+unsigned FAR *have;
+unsigned char FAR *buf;
+unsigned len;
+{
+ unsigned got;
+ unsigned next;
+
+ got = *have;
+ next = 0;
+ while (next < len && got < 4) {
+ if ((int)(buf[next]) == (got < 2 ? 0 : 0xff))
+ got++;
+ else if (buf[next])
+ got = 0;
+ else
+ got = 4 - got;
+ next++;
+ }
+ *have = got;
+ return next;
+}
+
+int ZEXPORT inflateSync(strm)
+z_streamp strm;
+{
+ unsigned len; /* number of bytes to look at or looked at */
+ unsigned long in, out; /* temporary to save total_in and total_out */
+ unsigned char buf[4]; /* to restore bit buffer to byte string */
+ struct inflate_state FAR *state;
+
+ /* check parameters */
+ if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR;
+ state = (struct inflate_state FAR *)strm->state;
+ if (strm->avail_in == 0 && state->bits < 8) return Z_BUF_ERROR;
+
+ /* if first time, start search in bit buffer */
+ if (state->mode != SYNC) {
+ state->mode = SYNC;
+ state->hold <<= state->bits & 7;
+ state->bits -= state->bits & 7;
+ len = 0;
+ while (state->bits >= 8) {
+ buf[len++] = (unsigned char)(state->hold);
+ state->hold >>= 8;
+ state->bits -= 8;
+ }
+ state->have = 0;
+ syncsearch(&(state->have), buf, len);
+ }
+
+ /* search available input */
+ len = syncsearch(&(state->have), strm->next_in, strm->avail_in);
+ strm->avail_in -= len;
+ strm->next_in += len;
+ strm->total_in += len;
+
+ /* return no joy or set up to restart inflate() on a new block */
+ if (state->have != 4) return Z_DATA_ERROR;
+ in = strm->total_in; out = strm->total_out;
+ inflateReset(strm);
+ strm->total_in = in; strm->total_out = out;
+ state->mode = TYPE;
+ return Z_OK;
+}
+
+/*
+ Returns true if inflate is currently at the end of a block generated by
+ Z_SYNC_FLUSH or Z_FULL_FLUSH. This function is used by one PPP
+ implementation to provide an additional safety check. PPP uses
+ Z_SYNC_FLUSH but removes the length bytes of the resulting empty stored
+ block. When decompressing, PPP checks that at the end of input packet,
+ inflate is waiting for these length bytes.
+ */
+int ZEXPORT inflateSyncPoint(strm)
+z_streamp strm;
+{
+ struct inflate_state FAR *state;
+
+ if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR;
+ state = (struct inflate_state FAR *)strm->state;
+ return state->mode == STORED && state->bits == 0;
+}
+
+int ZEXPORT inflateCopy(dest, source)
+z_streamp dest;
+z_streamp source;
+{
+ struct inflate_state FAR *state;
+ struct inflate_state FAR *copy;
+ unsigned char FAR *window;
+
+ /* check input */
+ if (dest == Z_NULL || source == Z_NULL || source->state == Z_NULL ||
+ source->zalloc == (alloc_func)0 || source->zfree == (free_func)0)
+ return Z_STREAM_ERROR;
+ state = (struct inflate_state FAR *)source->state;
+
+ /* allocate space */
+ copy = (struct inflate_state FAR *)
+ ZALLOC(source, 1, sizeof(struct inflate_state));
+ if (copy == Z_NULL) return Z_MEM_ERROR;
+ window = Z_NULL;
+ if (state->window != Z_NULL) {
+ window = (unsigned char FAR *)
+ ZALLOC(source, 1U << state->wbits, sizeof(unsigned char));
+ if (window == Z_NULL) {
+ ZFREE(source, copy);
+ return Z_MEM_ERROR;
+ }
+ }
+
+ /* copy state */
+ *dest = *source;
+ *copy = *state;
+ copy->lencode = copy->codes + (state->lencode - state->codes);
+ copy->distcode = copy->codes + (state->distcode - state->codes);
+ copy->next = copy->codes + (state->next - state->codes);
+ if (window != Z_NULL)
+ zmemcpy(window, state->window, 1U << state->wbits);
+ copy->window = window;
+ dest->state = (voidpf)copy;
+ return Z_OK;
+}
diff --git a/src/zlib/inflate.h b/src/zlib/inflate.h
new file mode 100755
index 0000000..9a12c8f
--- /dev/null
+++ b/src/zlib/inflate.h
@@ -0,0 +1,117 @@
+/* inflate.h -- internal inflate state definition
+ * Copyright (C) 1995-2003 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* WARNING: this file should *not* be used by applications. It is
+ part of the implementation of the compression library and is
+ subject to change. Applications should only use zlib.h.
+ */
+
+/* define NO_GZIP when compiling if you want to disable gzip header and
+ trailer decoding by inflate(). NO_GZIP would be used to avoid linking in
+ the crc code when it is not needed. For shared libraries, gzip decoding
+ should be left enabled. */
+#ifndef NO_GZIP
+# define GUNZIP
+#endif
+
+/* Possible inflate modes between inflate() calls */
+typedef enum {
+ HEAD, /* i: waiting for magic header */
+#ifdef GUNZIP
+ FLAGS, /* i: waiting for method and flags (gzip) */
+ TIME, /* i: waiting for modification time (gzip) */
+ OS, /* i: waiting for extra flags and operating system (gzip) */
+ EXLEN, /* i: waiting for extra length (gzip) */
+ EXTRA, /* i: waiting for extra bytes (gzip) */
+ NAME, /* i: waiting for end of file name (gzip) */
+ COMMENT, /* i: waiting for end of comment (gzip) */
+ HCRC, /* i: waiting for header crc (gzip) */
+#endif
+ DICTID, /* i: waiting for dictionary check value */
+ DICT, /* waiting for inflateSetDictionary() call */
+ TYPE, /* i: waiting for type bits, including last-flag bit */
+ TYPEDO, /* i: same, but skip check to exit inflate on new block */
+ STORED, /* i: waiting for stored size (length and complement) */
+ COPY, /* i/o: waiting for input or output to copy stored block */
+ TABLE, /* i: waiting for dynamic block table lengths */
+ LENLENS, /* i: waiting for code length code lengths */
+ CODELENS, /* i: waiting for length/lit and distance code lengths */
+ LEN, /* i: waiting for length/lit code */
+ LENEXT, /* i: waiting for length extra bits */
+ DIST, /* i: waiting for distance code */
+ DISTEXT, /* i: waiting for distance extra bits */
+ MATCH, /* o: waiting for output space to copy string */
+ LIT, /* o: waiting for output space to write literal */
+ CHECK, /* i: waiting for 32-bit check value */
+#ifdef GUNZIP
+ LENGTH, /* i: waiting for 32-bit length (gzip) */
+#endif
+ DONE, /* finished check, done -- remain here until reset */
+ BAD, /* got a data error -- remain here until reset */
+ MEM, /* got an inflate() memory error -- remain here until reset */
+ SYNC /* looking for synchronization bytes to restart inflate() */
+} inflate_mode;
+
+/*
+ State transitions between above modes -
+
+ (most modes can go to the BAD or MEM mode -- not shown for clarity)
+
+ Process header:
+ HEAD -> (gzip) or (zlib)
+ (gzip) -> FLAGS -> TIME -> OS -> EXLEN -> EXTRA -> NAME
+ NAME -> COMMENT -> HCRC -> TYPE
+ (zlib) -> DICTID or TYPE
+ DICTID -> DICT -> TYPE
+ Read deflate blocks:
+ TYPE -> STORED or TABLE or LEN or CHECK
+ STORED -> COPY -> TYPE
+ TABLE -> LENLENS -> CODELENS -> LEN
+ Read deflate codes:
+ LEN -> LENEXT or LIT or TYPE
+ LENEXT -> DIST -> DISTEXT -> MATCH -> LEN
+ LIT -> LEN
+ Process trailer:
+ CHECK -> LENGTH -> DONE
+ */
+
+/* state maintained between inflate() calls. Approximately 7K bytes. */
+struct inflate_state {
+ inflate_mode mode; /* current inflate mode */
+ int last; /* true if processing last block */
+ int wrap; /* bit 0 true for zlib, bit 1 true for gzip */
+ int havedict; /* true if dictionary provided */
+ int flags; /* gzip header method and flags (0 if zlib) */
+ unsigned long check; /* protected copy of check value */
+ unsigned long total; /* protected copy of output count */
+ /* sliding window */
+ unsigned wbits; /* log base 2 of requested window size */
+ unsigned wsize; /* window size or zero if not using window */
+ unsigned whave; /* valid bytes in the window */
+ unsigned write; /* window write index */
+ unsigned char FAR *window; /* allocated sliding window, if needed */
+ /* bit accumulator */
+ unsigned long hold; /* input bit accumulator */
+ unsigned bits; /* number of bits in "in" */
+ /* for string and stored block copying */
+ unsigned length; /* literal or length of data to copy */
+ unsigned offset; /* distance back to copy string from */
+ /* for table and code decoding */
+ unsigned extra; /* extra bits needed */
+ /* fixed and dynamic code tables */
+ code const FAR *lencode; /* starting table for length/literal codes */
+ code const FAR *distcode; /* starting table for distance codes */
+ unsigned lenbits; /* index bits for lencode */
+ unsigned distbits; /* index bits for distcode */
+ /* dynamic table building */
+ unsigned ncode; /* number of code length code lengths */
+ unsigned nlen; /* number of length code lengths */
+ unsigned ndist; /* number of distance code lengths */
+ unsigned have; /* number of code lengths in lens[] */
+ code FAR *next; /* next available space in codes[] */
+ unsigned short lens[320]; /* temporary storage for code lengths */
+ unsigned short work[288]; /* work area for code table building */
+ code codes[ENOUGH]; /* space for code tables */
+};
diff --git a/src/zlib/inftrees.c b/src/zlib/inftrees.c
new file mode 100755
index 0000000..3bb5639
--- /dev/null
+++ b/src/zlib/inftrees.c
@@ -0,0 +1,321 @@
+/* inftrees.c -- generate Huffman trees for efficient decoding
+ * Copyright (C) 1995-2003 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+#include "zutil.h"
+#include "inftrees.h"
+
+#define MAXBITS 15
+
+const char inflate_copyright[] =
+ " inflate 1.2.1 Copyright 1995-2003 Mark Adler ";
+/*
+ If you use the zlib library in a product, an acknowledgment is welcome
+ in the documentation of your product. If for some reason you cannot
+ include such an acknowledgment, I would appreciate that you keep this
+ copyright string in the executable of your product.
+ */
+
+/*
+ Build a set of tables to decode the provided canonical Huffman code.
+ The code lengths are lens[0..codes-1]. The result starts at *table,
+ whose indices are 0..2^bits-1. work is a writable array of at least
+ lens shorts, which is used as a work area. type is the type of code
+ to be generated, CODES, LENS, or DISTS. On return, zero is success,
+ -1 is an invalid code, and +1 means that ENOUGH isn't enough. table
+ on return points to the next available entry's address. bits is the
+ requested root table index bits, and on return it is the actual root
+ table index bits. It will differ if the request is greater than the
+ longest code or if it is less than the shortest code.
+ */
+int inflate_table(type, lens, codes, table, bits, work)
+codetype type;
+unsigned short FAR *lens;
+unsigned codes;
+code FAR * FAR *table;
+unsigned FAR *bits;
+unsigned short FAR *work;
+{
+ unsigned len; /* a code's length in bits */
+ unsigned sym; /* index of code symbols */
+ unsigned min, max; /* minimum and maximum code lengths */
+ unsigned root; /* number of index bits for root table */
+ unsigned curr; /* number of index bits for current table */
+ unsigned drop; /* code bits to drop for sub-table */
+ int left; /* number of prefix codes available */
+ unsigned used; /* code entries in table used */
+ unsigned huff; /* Huffman code */
+ unsigned incr; /* for incrementing code, index */
+ unsigned fill; /* index for replicating entries */
+ unsigned low; /* low bits for current root entry */
+ unsigned mask; /* mask for low root bits */
+ code this; /* table entry for duplication */
+ code FAR *next; /* next available space in table */
+ const unsigned short FAR *base; /* base value table to use */
+ const unsigned short FAR *extra; /* extra bits table to use */
+ int end; /* use base and extra for symbol > end */
+ unsigned short count[MAXBITS+1]; /* number of codes of each length */
+ unsigned short offs[MAXBITS+1]; /* offsets in table for each length */
+ static const unsigned short lbase[31] = { /* Length codes 257..285 base */
+ 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31,
+ 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0};
+ static const unsigned short lext[31] = { /* Length codes 257..285 extra */
+ 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18,
+ 19, 19, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 16, 76, 66};
+ static const unsigned short dbase[32] = { /* Distance codes 0..29 base */
+ 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193,
+ 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145,
+ 8193, 12289, 16385, 24577, 0, 0};
+ static const unsigned short dext[32] = { /* Distance codes 0..29 extra */
+ 16, 16, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22,
+ 23, 23, 24, 24, 25, 25, 26, 26, 27, 27,
+ 28, 28, 29, 29, 64, 64};
+
+ /*
+ Process a set of code lengths to create a canonical Huffman code. The
+ code lengths are lens[0..codes-1]. Each length corresponds to the
+ symbols 0..codes-1. The Huffman code is generated by first sorting the
+ symbols by length from short to long, and retaining the symbol order
+ for codes with equal lengths. Then the code starts with all zero bits
+ for the first code of the shortest length, and the codes are integer
+ increments for the same length, and zeros are appended as the length
+ increases. For the deflate format, these bits are stored backwards
+ from their more natural integer increment ordering, and so when the
+ decoding tables are built in the large loop below, the integer codes
+ are incremented backwards.
+
+ This routine assumes, but does not check, that all of the entries in
+ lens[] are in the range 0..MAXBITS. The caller must assure this.
+ 1..MAXBITS is interpreted as that code length. zero means that that
+ symbol does not occur in this code.
+
+ The codes are sorted by computing a count of codes for each length,
+ creating from that a table of starting indices for each length in the
+ sorted table, and then entering the symbols in order in the sorted
+ table. The sorted table is work[], with that space being provided by
+ the caller.
+
+ The length counts are used for other purposes as well, i.e. finding
+ the minimum and maximum length codes, determining if there are any
+ codes at all, checking for a valid set of lengths, and looking ahead
+ at length counts to determine sub-table sizes when building the
+ decoding tables.
+ */
+
+ /* accumulate lengths for codes (assumes lens[] all in 0..MAXBITS) */
+ for (len = 0; len <= MAXBITS; len++)
+ count[len] = 0;
+ for (sym = 0; sym < codes; sym++)
+ count[lens[sym]]++;
+
+ /* bound code lengths, force root to be within code lengths */
+ root = *bits;
+ for (max = MAXBITS; max >= 1; max--)
+ if (count[max] != 0) break;
+ if (root > max) root = max;
+ if (max == 0) return -1; /* no codes! */
+ for (min = 1; min <= MAXBITS; min++)
+ if (count[min] != 0) break;
+ if (root < min) root = min;
+
+ /* check for an over-subscribed or incomplete set of lengths */
+ left = 1;
+ for (len = 1; len <= MAXBITS; len++) {
+ left <<= 1;
+ left -= count[len];
+ if (left < 0) return -1; /* over-subscribed */
+ }
+ if (left > 0 && (type == CODES || (codes - count[0] != 1)))
+ return -1; /* incomplete set */
+
+ /* generate offsets into symbol table for each length for sorting */
+ offs[1] = 0;
+ for (len = 1; len < MAXBITS; len++)
+ offs[len + 1] = offs[len] + count[len];
+
+ /* sort symbols by length, by symbol order within each length */
+ for (sym = 0; sym < codes; sym++)
+ if (lens[sym] != 0) work[offs[lens[sym]]++] = (unsigned short)sym;
+
+ /*
+ Create and fill in decoding tables. In this loop, the table being
+ filled is at next and has curr index bits. The code being used is huff
+ with length len. That code is converted to an index by dropping drop
+ bits off of the bottom. For codes where len is less than drop + curr,
+ those top drop + curr - len bits are incremented through all values to
+ fill the table with replicated entries.
+
+ root is the number of index bits for the root table. When len exceeds
+ root, sub-tables are created pointed to by the root entry with an index
+ of the low root bits of huff. This is saved in low to check for when a
+ new sub-table should be started. drop is zero when the root table is
+ being filled, and drop is root when sub-tables are being filled.
+
+ When a new sub-table is needed, it is necessary to look ahead in the
+ code lengths to determine what size sub-table is needed. The length
+ counts are used for this, and so count[] is decremented as codes are
+ entered in the tables.
+
+ used keeps track of how many table entries have been allocated from the
+ provided *table space. It is checked when a LENS table is being made
+ against the space in *table, ENOUGH, minus the maximum space needed by
+ the worst case distance code, MAXD. This should never happen, but the
+ sufficiency of ENOUGH has not been proven exhaustively, hence the check.
+ This assumes that when type == LENS, bits == 9.
+
+ sym increments through all symbols, and the loop terminates when
+ all codes of length max, i.e. all codes, have been processed. This
+ routine permits incomplete codes, so another loop after this one fills
+ in the rest of the decoding tables with invalid code markers.
+ */
+
+ /* set up for code type */
+ switch (type) {
+ case CODES:
+ base = extra = work; /* dummy value--not used */
+ end = 19;
+ break;
+ case LENS:
+ base = lbase;
+ base -= 257;
+ extra = lext;
+ extra -= 257;
+ end = 256;
+ break;
+ default: /* DISTS */
+ base = dbase;
+ extra = dext;
+ end = -1;
+ }
+
+ /* initialize state for loop */
+ huff = 0; /* starting code */
+ sym = 0; /* starting code symbol */
+ len = min; /* starting code length */
+ next = *table; /* current table to fill in */
+ curr = root; /* current table index bits */
+ drop = 0; /* current bits to drop from code for index */
+ low = (unsigned)(-1); /* trigger new sub-table when len > root */
+ used = 1U << root; /* use root table entries */
+ mask = used - 1; /* mask for comparing low */
+
+ /* check available table space */
+ if (type == LENS && used >= ENOUGH - MAXD)
+ return 1;
+
+ /* process all codes and make table entries */
+ for (;;) {
+ /* create table entry */
+ this.bits = (unsigned char)(len - drop);
+ if ((int)(work[sym]) < end) {
+ this.op = (unsigned char)0;
+ this.val = work[sym];
+ }
+ else if ((int)(work[sym]) > end) {
+ this.op = (unsigned char)(extra[work[sym]]);
+ this.val = base[work[sym]];
+ }
+ else {
+ this.op = (unsigned char)(32 + 64); /* end of block */
+ this.val = 0;
+ }
+
+ /* replicate for those indices with low len bits equal to huff */
+ incr = 1U << (len - drop);
+ fill = 1U << curr;
+ do {
+ fill -= incr;
+ next[(huff >> drop) + fill] = this;
+ } while (fill != 0);
+
+ /* backwards increment the len-bit code huff */
+ incr = 1U << (len - 1);
+ while (huff & incr)
+ incr >>= 1;
+ if (incr != 0) {
+ huff &= incr - 1;
+ huff += incr;
+ }
+ else
+ huff = 0;
+
+ /* go to next symbol, update count, len */
+ sym++;
+ if (--(count[len]) == 0) {
+ if (len == max) break;
+ len = lens[work[sym]];
+ }
+
+ /* create new sub-table if needed */
+ if (len > root && (huff & mask) != low) {
+ /* if first time, transition to sub-tables */
+ if (drop == 0)
+ drop = root;
+
+ /* increment past last table */
+ next += 1U << curr;
+
+ /* determine length of next table */
+ curr = len - drop;
+ left = (int)(1 << curr);
+ while (curr + drop < max) {
+ left -= count[curr + drop];
+ if (left <= 0) break;
+ curr++;
+ left <<= 1;
+ }
+
+ /* check for enough space */
+ used += 1U << curr;
+ if (type == LENS && used >= ENOUGH - MAXD)
+ return 1;
+
+ /* point entry in root table to sub-table */
+ low = huff & mask;
+ (*table)[low].op = (unsigned char)curr;
+ (*table)[low].bits = (unsigned char)root;
+ (*table)[low].val = (unsigned short)(next - *table);
+ }
+ }
+
+ /*
+ Fill in rest of table for incomplete codes. This loop is similar to the
+ loop above in incrementing huff for table indices. It is assumed that
+ len is equal to curr + drop, so there is no loop needed to increment
+ through high index bits. When the current sub-table is filled, the loop
+ drops back to the root table to fill in any remaining entries there.
+ */
+ this.op = (unsigned char)64; /* invalid code marker */
+ this.bits = (unsigned char)(len - drop);
+ this.val = (unsigned short)0;
+ while (huff != 0) {
+ /* when done with sub-table, drop back to root table */
+ if (drop != 0 && (huff & mask) != low) {
+ drop = 0;
+ len = root;
+ next = *table;
+ curr = root;
+ this.bits = (unsigned char)len;
+ }
+
+ /* put invalid code marker in table */
+ next[huff >> drop] = this;
+
+ /* backwards increment the len-bit code huff */
+ incr = 1U << (len - 1);
+ while (huff & incr)
+ incr >>= 1;
+ if (incr != 0) {
+ huff &= incr - 1;
+ huff += incr;
+ }
+ else
+ huff = 0;
+ }
+
+ /* set return parameters */
+ *table += used;
+ *bits = root;
+ return 0;
+}
diff --git a/src/zlib/inftrees.h b/src/zlib/inftrees.h
new file mode 100755
index 0000000..82d365a
--- /dev/null
+++ b/src/zlib/inftrees.h
@@ -0,0 +1,55 @@
+/* inftrees.h -- header to use inftrees.c
+ * Copyright (C) 1995-2003 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* WARNING: this file should *not* be used by applications. It is
+ part of the implementation of the compression library and is
+ subject to change. Applications should only use zlib.h.
+ */
+
+/* Structure for decoding tables. Each entry provides either the
+ information needed to do the operation requested by the code that
+ indexed that table entry, or it provides a pointer to another
+ table that indexes more bits of the code. op indicates whether
+ the entry is a pointer to another table, a literal, a length or
+ distance, an end-of-block, or an invalid code. For a table
+ pointer, the low four bits of op is the number of index bits of
+ that table. For a length or distance, the low four bits of op
+ is the number of extra bits to get after the code. bits is
+ the number of bits in this code or part of the code to drop off
+ of the bit buffer. val is the actual byte to output in the case
+ of a literal, the base length or distance, or the offset from
+ the current table to the next table. Each entry is four bytes. */
+typedef struct {
+ unsigned char op; /* operation, extra bits, table bits */
+ unsigned char bits; /* bits in this part of the code */
+ unsigned short val; /* offset in table or code value */
+} code;
+
+/* op values as set by inflate_table():
+ 00000000 - literal
+ 0000tttt - table link, tttt != 0 is the number of table index bits
+ 0001eeee - length or distance, eeee is the number of extra bits
+ 01100000 - end of block
+ 01000000 - invalid code
+ */
+
+/* Maximum size of dynamic tree. The maximum found in a long but non-
+ exhaustive search was 1004 code structures (850 for length/literals
+ and 154 for distances, the latter actually the result of an
+ exhaustive search). The true maximum is not known, but the value
+ below is more than safe. */
+#define ENOUGH 1440
+#define MAXD 154
+
+/* Type of code to build for inftable() */
+typedef enum {
+ CODES,
+ LENS,
+ DISTS
+} codetype;
+
+extern int inflate_table OF((codetype type, unsigned short FAR *lens,
+ unsigned codes, code FAR * FAR *table,
+ unsigned FAR *bits, unsigned short FAR *work));
diff --git a/src/zlib/minigzip.c b/src/zlib/minigzip.c
new file mode 100755
index 0000000..4d726c2
--- /dev/null
+++ b/src/zlib/minigzip.c
@@ -0,0 +1,322 @@
+/* minigzip.c -- simulate gzip using the zlib compression library
+ * Copyright (C) 1995-2002 Jean-loup Gailly.
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/*
+ * minigzip is a minimal implementation of the gzip utility. This is
+ * only an example of using zlib and isn't meant to replace the
+ * full-featured gzip. No attempt is made to deal with file systems
+ * limiting names to 14 or 8+3 characters, etc... Error checking is
+ * very limited. So use minigzip only for testing; use gzip for the
+ * real thing. On MSDOS, use only on file names without extension
+ * or in pipe mode.
+ */
+
+/* @(#) $ Id$ */
+
+#include <stdio.h>
+#include "zlib.h"
+
+#ifdef STDC
+# include <string.h>
+# include <stdlib.h>
+#else
+ extern void exit OF((int));
+#endif
+
+#ifdef USE_MMAP
+# include <sys/types.h>
+# include <sys/mman.h>
+# include <sys/stat.h>
+#endif
+
+#if defined(MSDOS) || defined(OS2) || defined(WIN32) || defined(__CYGWIN__)
+# include <fcntl.h>
+# include <io.h>
+# define SET_BINARY_MODE(file) setmode(fileno(file), O_BINARY)
+#else
+# define SET_BINARY_MODE(file)
+#endif
+
+#ifdef VMS
+# define unlink delete
+# define GZ_SUFFIX "-gz"
+#endif
+#ifdef RISCOS
+# define unlink remove
+# define GZ_SUFFIX "-gz"
+# define fileno(file) file->__file
+#endif
+#if defined(__MWERKS__) && __dest_os != __be_os && __dest_os != __win32_os
+# include <unix.h> /* for fileno */
+#endif
+
+#ifndef WIN32 /* unlink already in stdio.h for WIN32 */
+ extern int unlink OF((const char *));
+#endif
+
+#ifndef GZ_SUFFIX
+# define GZ_SUFFIX ".gz"
+#endif
+#define SUFFIX_LEN (sizeof(GZ_SUFFIX)-1)
+
+#define BUFLEN 16384
+#define MAX_NAME_LEN 1024
+
+#ifdef MAXSEG_64K
+# define local static
+ /* Needed for systems with limitation on stack size. */
+#else
+# define local
+#endif
+
+char *prog;
+
+void error OF((const char *msg));
+void gz_compress OF((FILE *in, gzFile out));
+#ifdef USE_MMAP
+int gz_compress_mmap OF((FILE *in, gzFile out));
+#endif
+void gz_uncompress OF((gzFile in, FILE *out));
+void file_compress OF((char *file, char *mode));
+void file_uncompress OF((char *file));
+int main OF((int argc, char *argv[]));
+
+/* ===========================================================================
+ * Display error message and exit
+ */
+void error(msg)
+ const char *msg;
+{
+ fprintf(stderr, "%s: %s\n", prog, msg);
+ exit(1);
+}
+
+/* ===========================================================================
+ * Compress input to output then close both files.
+ */
+
+void gz_compress(in, out)
+ FILE *in;
+ gzFile out;
+{
+ local char buf[BUFLEN];
+ int len;
+ int err;
+
+#ifdef USE_MMAP
+ /* Try first compressing with mmap. If mmap fails (minigzip used in a
+ * pipe), use the normal fread loop.
+ */
+ if (gz_compress_mmap(in, out) == Z_OK) return;
+#endif
+ for (;;) {
+ len = (int)fread(buf, 1, sizeof(buf), in);
+ if (ferror(in)) {
+ perror("fread");
+ exit(1);
+ }
+ if (len == 0) break;
+
+ if (gzwrite(out, buf, (unsigned)len) != len) error(gzerror(out, &err));
+ }
+ fclose(in);
+ if (gzclose(out) != Z_OK) error("failed gzclose");
+}
+
+#ifdef USE_MMAP /* MMAP version, Miguel Albrecht <malbrech@eso.org> */
+
+/* Try compressing the input file at once using mmap. Return Z_OK if
+ * if success, Z_ERRNO otherwise.
+ */
+int gz_compress_mmap(in, out)
+ FILE *in;
+ gzFile out;
+{
+ int len;
+ int err;
+ int ifd = fileno(in);
+ caddr_t buf; /* mmap'ed buffer for the entire input file */
+ off_t buf_len; /* length of the input file */
+ struct stat sb;
+
+ /* Determine the size of the file, needed for mmap: */
+ if (fstat(ifd, &sb) < 0) return Z_ERRNO;
+ buf_len = sb.st_size;
+ if (buf_len <= 0) return Z_ERRNO;
+
+ /* Now do the actual mmap: */
+ buf = mmap((caddr_t) 0, buf_len, PROT_READ, MAP_SHARED, ifd, (off_t)0);
+ if (buf == (caddr_t)(-1)) return Z_ERRNO;
+
+ /* Compress the whole file at once: */
+ len = gzwrite(out, (char *)buf, (unsigned)buf_len);
+
+ if (len != (int)buf_len) error(gzerror(out, &err));
+
+ munmap(buf, buf_len);
+ fclose(in);
+ if (gzclose(out) != Z_OK) error("failed gzclose");
+ return Z_OK;
+}
+#endif /* USE_MMAP */
+
+/* ===========================================================================
+ * Uncompress input to output then close both files.
+ */
+void gz_uncompress(in, out)
+ gzFile in;
+ FILE *out;
+{
+ local char buf[BUFLEN];
+ int len;
+ int err;
+
+ for (;;) {
+ len = gzread(in, buf, sizeof(buf));
+ if (len < 0) error (gzerror(in, &err));
+ if (len == 0) break;
+
+ if ((int)fwrite(buf, 1, (unsigned)len, out) != len) {
+ error("failed fwrite");
+ }
+ }
+ if (fclose(out)) error("failed fclose");
+
+ if (gzclose(in) != Z_OK) error("failed gzclose");
+}
+
+
+/* ===========================================================================
+ * Compress the given file: create a corresponding .gz file and remove the
+ * original.
+ */
+void file_compress(file, mode)
+ char *file;
+ char *mode;
+{
+ local char outfile[MAX_NAME_LEN];
+ FILE *in;
+ gzFile out;
+
+ strcpy(outfile, file);
+ strcat(outfile, GZ_SUFFIX);
+
+ in = fopen(file, "rb");
+ if (in == NULL) {
+ perror(file);
+ exit(1);
+ }
+ out = gzopen(outfile, mode);
+ if (out == NULL) {
+ fprintf(stderr, "%s: can't gzopen %s\n", prog, outfile);
+ exit(1);
+ }
+ gz_compress(in, out);
+
+ unlink(file);
+}
+
+
+/* ===========================================================================
+ * Uncompress the given file and remove the original.
+ */
+void file_uncompress(file)
+ char *file;
+{
+ local char buf[MAX_NAME_LEN];
+ char *infile, *outfile;
+ FILE *out;
+ gzFile in;
+ uInt len = (uInt)strlen(file);
+
+ strcpy(buf, file);
+
+ if (len > SUFFIX_LEN && strcmp(file+len-SUFFIX_LEN, GZ_SUFFIX) == 0) {
+ infile = file;
+ outfile = buf;
+ outfile[len-3] = '\0';
+ } else {
+ outfile = file;
+ infile = buf;
+ strcat(infile, GZ_SUFFIX);
+ }
+ in = gzopen(infile, "rb");
+ if (in == NULL) {
+ fprintf(stderr, "%s: can't gzopen %s\n", prog, infile);
+ exit(1);
+ }
+ out = fopen(outfile, "wb");
+ if (out == NULL) {
+ perror(file);
+ exit(1);
+ }
+
+ gz_uncompress(in, out);
+
+ unlink(infile);
+}
+
+
+/* ===========================================================================
+ * Usage: minigzip [-d] [-f] [-h] [-r] [-1 to -9] [files...]
+ * -d : decompress
+ * -f : compress with Z_FILTERED
+ * -h : compress with Z_HUFFMAN_ONLY
+ * -r : compress with Z_RLE
+ * -1 to -9 : compression level
+ */
+
+int main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int uncompr = 0;
+ gzFile file;
+ char outmode[20];
+
+ strcpy(outmode, "wb6 ");
+
+ prog = argv[0];
+ argc--, argv++;
+
+ while (argc > 0) {
+ if (strcmp(*argv, "-d") == 0)
+ uncompr = 1;
+ else if (strcmp(*argv, "-f") == 0)
+ outmode[3] = 'f';
+ else if (strcmp(*argv, "-h") == 0)
+ outmode[3] = 'h';
+ else if (strcmp(*argv, "-r") == 0)
+ outmode[3] = 'R';
+ else if ((*argv)[0] == '-' && (*argv)[1] >= '1' && (*argv)[1] <= '9' &&
+ (*argv)[2] == 0)
+ outmode[2] = (*argv)[1];
+ else
+ break;
+ argc--, argv++;
+ }
+ if (argc == 0) {
+ SET_BINARY_MODE(stdin);
+ SET_BINARY_MODE(stdout);
+ if (uncompr) {
+ file = gzdopen(fileno(stdin), "rb");
+ if (file == NULL) error("can't gzdopen stdin");
+ gz_uncompress(file, stdout);
+ } else {
+ file = gzdopen(fileno(stdout), outmode);
+ if (file == NULL) error("can't gzdopen stdout");
+ gz_compress(stdin, file);
+ }
+ } else {
+ do {
+ if (uncompr) {
+ file_uncompress(*argv);
+ } else {
+ file_compress(*argv, outmode);
+ }
+ } while (argv++, --argc);
+ }
+ return 0;
+}
diff --git a/src/zlib/trees.c b/src/zlib/trees.c
new file mode 100755
index 0000000..159f969
--- /dev/null
+++ b/src/zlib/trees.c
@@ -0,0 +1,1215 @@
+/* trees.c -- output deflated data using Huffman coding
+ * Copyright (C) 1995-2003 Jean-loup Gailly
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/*
+ * ALGORITHM
+ *
+ * The "deflation" process uses several Huffman trees. The more
+ * common source values are represented by shorter bit sequences.
+ *
+ * Each code tree is stored in a compressed form which is itself
+ * a Huffman encoding of the lengths of all the code strings (in
+ * ascending order by source values). The actual code strings are
+ * reconstructed from the lengths in the inflate process, as described
+ * in the deflate specification.
+ *
+ * REFERENCES
+ *
+ * Deutsch, L.P.,"'Deflate' Compressed Data Format Specification".
+ * Available in ftp.uu.net:/pub/archiving/zip/doc/deflate-1.1.doc
+ *
+ * Storer, James A.
+ * Data Compression: Methods and Theory, pp. 49-50.
+ * Computer Science Press, 1988. ISBN 0-7167-8156-5.
+ *
+ * Sedgewick, R.
+ * Algorithms, p290.
+ * Addison-Wesley, 1983. ISBN 0-201-06672-6.
+ */
+
+/* @(#) $ Id$ */
+
+/* #define GEN_TREES_H */
+
+#include "deflate.h"
+
+#ifdef DEBUG
+# include <ctype.h>
+#endif
+
+/* ===========================================================================
+ * Constants
+ */
+
+#define MAX_BL_BITS 7
+/* Bit length codes must not exceed MAX_BL_BITS bits */
+
+#define END_BLOCK 256
+/* end of block literal code */
+
+#define REP_3_6 16
+/* repeat previous bit length 3-6 times (2 bits of repeat count) */
+
+#define REPZ_3_10 17
+/* repeat a zero length 3-10 times (3 bits of repeat count) */
+
+#define REPZ_11_138 18
+/* repeat a zero length 11-138 times (7 bits of repeat count) */
+
+local const int extra_lbits[LENGTH_CODES] /* extra bits for each length code */
+ = {0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0};
+
+local const int extra_dbits[D_CODES] /* extra bits for each distance code */
+ = {0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13};
+
+local const int extra_blbits[BL_CODES]/* extra bits for each bit length code */
+ = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7};
+
+local const uch bl_order[BL_CODES]
+ = {16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15};
+/* The lengths of the bit length codes are sent in order of decreasing
+ * probability, to avoid transmitting the lengths for unused bit length codes.
+ */
+
+#define Buf_size (8 * 2*sizeof(char))
+/* Number of bits used within bi_buf. (bi_buf might be implemented on
+ * more than 16 bits on some systems.)
+ */
+
+/* ===========================================================================
+ * Local data. These are initialized only once.
+ */
+
+#define DIST_CODE_LEN 512 /* see definition of array dist_code below */
+
+#if defined(GEN_TREES_H) || !defined(STDC)
+/* non ANSI compilers may not accept trees.h */
+
+local ct_data static_ltree[L_CODES+2];
+/* The static literal tree. Since the bit lengths are imposed, there is no
+ * need for the L_CODES extra codes used during heap construction. However
+ * The codes 286 and 287 are needed to build a canonical tree (see _tr_init
+ * below).
+ */
+
+local ct_data static_dtree[D_CODES];
+/* The static distance tree. (Actually a trivial tree since all codes use
+ * 5 bits.)
+ */
+
+uch _dist_code[DIST_CODE_LEN];
+/* Distance codes. The first 256 values correspond to the distances
+ * 3 .. 258, the last 256 values correspond to the top 8 bits of
+ * the 15 bit distances.
+ */
+
+uch _length_code[MAX_MATCH-MIN_MATCH+1];
+/* length code for each normalized match length (0 == MIN_MATCH) */
+
+local int base_length[LENGTH_CODES];
+/* First normalized length for each code (0 = MIN_MATCH) */
+
+local int base_dist[D_CODES];
+/* First normalized distance for each code (0 = distance of 1) */
+
+#else
+# include "trees.h"
+#endif /* GEN_TREES_H */
+
+struct static_tree_desc_s {
+ const ct_data *static_tree; /* static tree or NULL */
+ const intf *extra_bits; /* extra bits for each code or NULL */
+ int extra_base; /* base index for extra_bits */
+ int elems; /* max number of elements in the tree */
+ int max_length; /* max bit length for the codes */
+};
+
+local const static_tree_desc static_l_desc =
+{static_ltree, extra_lbits, LITERALS+1, L_CODES, MAX_BITS};
+
+local const static_tree_desc static_d_desc =
+{static_dtree, extra_dbits, 0, D_CODES, MAX_BITS};
+
+local const static_tree_desc static_bl_desc =
+{(const ct_data *)0, extra_blbits, 0, BL_CODES, MAX_BL_BITS};
+
+/* ===========================================================================
+ * Local (static) routines in this file.
+ */
+
+local void tr_static_init OF((void));
+local void init_block OF((deflate_state *s));
+local void pqdownheap OF((deflate_state *s, ct_data *tree, int k));
+local void gen_bitlen OF((deflate_state *s, tree_desc *desc));
+local void gen_codes OF((ct_data *tree, int max_code, ushf *bl_count));
+local void build_tree OF((deflate_state *s, tree_desc *desc));
+local void scan_tree OF((deflate_state *s, ct_data *tree, int max_code));
+local void send_tree OF((deflate_state *s, ct_data *tree, int max_code));
+local int build_bl_tree OF((deflate_state *s));
+local void send_all_trees OF((deflate_state *s, int lcodes, int dcodes,
+ int blcodes));
+local void compress_block OF((deflate_state *s, ct_data *ltree,
+ ct_data *dtree));
+local void set_data_type OF((deflate_state *s));
+local unsigned bi_reverse OF((unsigned value, int length));
+local void bi_windup OF((deflate_state *s));
+local void bi_flush OF((deflate_state *s));
+local void copy_block OF((deflate_state *s, charf *buf, unsigned len,
+ int header));
+
+#ifdef GEN_TREES_H
+local void gen_trees_header OF((void));
+#endif
+
+#ifndef DEBUG
+# define send_code(s, c, tree) send_bits(s, tree[c].Code, tree[c].Len)
+ /* Send a code of the given tree. c and tree must not have side effects */
+
+#else /* DEBUG */
+# define send_code(s, c, tree) \
+ { if (z_verbose>2) fprintf(stderr,"\ncd %3d ",(c)); \
+ send_bits(s, tree[c].Code, tree[c].Len); }
+#endif
+
+/* ===========================================================================
+ * Output a short LSB first on the stream.
+ * IN assertion: there is enough room in pendingBuf.
+ */
+#define put_short(s, w) { \
+ put_byte(s, (uch)((w) & 0xff)); \
+ put_byte(s, (uch)((ush)(w) >> 8)); \
+}
+
+/* ===========================================================================
+ * Send a value on a given number of bits.
+ * IN assertion: length <= 16 and value fits in length bits.
+ */
+#ifdef DEBUG
+local void send_bits OF((deflate_state *s, int value, int length));
+
+local void send_bits(s, value, length)
+ deflate_state *s;
+ int value; /* value to send */
+ int length; /* number of bits */
+{
+ Tracevv((stderr," l %2d v %4x ", length, value));
+ Assert(length > 0 && length <= 15, "invalid length");
+ s->bits_sent += (ulg)length;
+
+ /* If not enough room in bi_buf, use (valid) bits from bi_buf and
+ * (16 - bi_valid) bits from value, leaving (width - (16-bi_valid))
+ * unused bits in value.
+ */
+ if (s->bi_valid > (int)Buf_size - length) {
+ s->bi_buf |= (value << s->bi_valid);
+ put_short(s, s->bi_buf);
+ s->bi_buf = (ush)value >> (Buf_size - s->bi_valid);
+ s->bi_valid += length - Buf_size;
+ } else {
+ s->bi_buf |= value << s->bi_valid;
+ s->bi_valid += length;
+ }
+}
+#else /* !DEBUG */
+
+#define send_bits(s, value, length) \
+{ int len = length;\
+ if (s->bi_valid > (int)Buf_size - len) {\
+ int val = value;\
+ s->bi_buf |= (val << s->bi_valid);\
+ put_short(s, s->bi_buf);\
+ s->bi_buf = (ush)val >> (Buf_size - s->bi_valid);\
+ s->bi_valid += len - Buf_size;\
+ } else {\
+ s->bi_buf |= (value) << s->bi_valid;\
+ s->bi_valid += len;\
+ }\
+}
+#endif /* DEBUG */
+
+
+/* the arguments must not have side effects */
+
+/* ===========================================================================
+ * Initialize the various 'constant' tables.
+ */
+local void tr_static_init()
+{
+#if defined(GEN_TREES_H) || !defined(STDC)
+ static int static_init_done = 0;
+ int n; /* iterates over tree elements */
+ int bits; /* bit counter */
+ int length; /* length value */
+ int code; /* code value */
+ int dist; /* distance index */
+ ush bl_count[MAX_BITS+1];
+ /* number of codes at each bit length for an optimal tree */
+
+ if (static_init_done) return;
+
+ /* For some embedded targets, global variables are not initialized: */
+ static_l_desc.static_tree = static_ltree;
+ static_l_desc.extra_bits = extra_lbits;
+ static_d_desc.static_tree = static_dtree;
+ static_d_desc.extra_bits = extra_dbits;
+ static_bl_desc.extra_bits = extra_blbits;
+
+ /* Initialize the mapping length (0..255) -> length code (0..28) */
+ length = 0;
+ for (code = 0; code < LENGTH_CODES-1; code++) {
+ base_length[code] = length;
+ for (n = 0; n < (1<<extra_lbits[code]); n++) {
+ _length_code[length++] = (uch)code;
+ }
+ }
+ Assert (length == 256, "tr_static_init: length != 256");
+ /* Note that the length 255 (match length 258) can be represented
+ * in two different ways: code 284 + 5 bits or code 285, so we
+ * overwrite length_code[255] to use the best encoding:
+ */
+ _length_code[length-1] = (uch)code;
+
+ /* Initialize the mapping dist (0..32K) -> dist code (0..29) */
+ dist = 0;
+ for (code = 0 ; code < 16; code++) {
+ base_dist[code] = dist;
+ for (n = 0; n < (1<<extra_dbits[code]); n++) {
+ _dist_code[dist++] = (uch)code;
+ }
+ }
+ Assert (dist == 256, "tr_static_init: dist != 256");
+ dist >>= 7; /* from now on, all distances are divided by 128 */
+ for ( ; code < D_CODES; code++) {
+ base_dist[code] = dist << 7;
+ for (n = 0; n < (1<<(extra_dbits[code]-7)); n++) {
+ _dist_code[256 + dist++] = (uch)code;
+ }
+ }
+ Assert (dist == 256, "tr_static_init: 256+dist != 512");
+
+ /* Construct the codes of the static literal tree */
+ for (bits = 0; bits <= MAX_BITS; bits++) bl_count[bits] = 0;
+ n = 0;
+ while (n <= 143) static_ltree[n++].Len = 8, bl_count[8]++;
+ while (n <= 255) static_ltree[n++].Len = 9, bl_count[9]++;
+ while (n <= 279) static_ltree[n++].Len = 7, bl_count[7]++;
+ while (n <= 287) static_ltree[n++].Len = 8, bl_count[8]++;
+ /* Codes 286 and 287 do not exist, but we must include them in the
+ * tree construction to get a canonical Huffman tree (longest code
+ * all ones)
+ */
+ gen_codes((ct_data *)static_ltree, L_CODES+1, bl_count);
+
+ /* The static distance tree is trivial: */
+ for (n = 0; n < D_CODES; n++) {
+ static_dtree[n].Len = 5;
+ static_dtree[n].Code = bi_reverse((unsigned)n, 5);
+ }
+ static_init_done = 1;
+
+# ifdef GEN_TREES_H
+ gen_trees_header();
+# endif
+#endif /* defined(GEN_TREES_H) || !defined(STDC) */
+}
+
+/* ===========================================================================
+ * Genererate the file trees.h describing the static trees.
+ */
+#ifdef GEN_TREES_H
+# ifndef DEBUG
+# include <stdio.h>
+# endif
+
+# define SEPARATOR(i, last, width) \
+ ((i) == (last)? "\n};\n\n" : \
+ ((i) % (width) == (width)-1 ? ",\n" : ", "))
+
+void gen_trees_header()
+{
+ FILE *header = fopen("trees.h", "w");
+ int i;
+
+ Assert (header != NULL, "Can't open trees.h");
+ fprintf(header,
+ "/* header created automatically with -DGEN_TREES_H */\n\n");
+
+ fprintf(header, "local const ct_data static_ltree[L_CODES+2] = {\n");
+ for (i = 0; i < L_CODES+2; i++) {
+ fprintf(header, "{{%3u},{%3u}}%s", static_ltree[i].Code,
+ static_ltree[i].Len, SEPARATOR(i, L_CODES+1, 5));
+ }
+
+ fprintf(header, "local const ct_data static_dtree[D_CODES] = {\n");
+ for (i = 0; i < D_CODES; i++) {
+ fprintf(header, "{{%2u},{%2u}}%s", static_dtree[i].Code,
+ static_dtree[i].Len, SEPARATOR(i, D_CODES-1, 5));
+ }
+
+ fprintf(header, "const uch _dist_code[DIST_CODE_LEN] = {\n");
+ for (i = 0; i < DIST_CODE_LEN; i++) {
+ fprintf(header, "%2u%s", _dist_code[i],
+ SEPARATOR(i, DIST_CODE_LEN-1, 20));
+ }
+
+ fprintf(header, "const uch _length_code[MAX_MATCH-MIN_MATCH+1]= {\n");
+ for (i = 0; i < MAX_MATCH-MIN_MATCH+1; i++) {
+ fprintf(header, "%2u%s", _length_code[i],
+ SEPARATOR(i, MAX_MATCH-MIN_MATCH, 20));
+ }
+
+ fprintf(header, "local const int base_length[LENGTH_CODES] = {\n");
+ for (i = 0; i < LENGTH_CODES; i++) {
+ fprintf(header, "%1u%s", base_length[i],
+ SEPARATOR(i, LENGTH_CODES-1, 20));
+ }
+
+ fprintf(header, "local const int base_dist[D_CODES] = {\n");
+ for (i = 0; i < D_CODES; i++) {
+ fprintf(header, "%5u%s", base_dist[i],
+ SEPARATOR(i, D_CODES-1, 10));
+ }
+
+ fclose(header);
+}
+#endif /* GEN_TREES_H */
+
+/* ===========================================================================
+ * Initialize the tree data structures for a new zlib stream.
+ */
+void _tr_init(s)
+ deflate_state *s;
+{
+ tr_static_init();
+
+ s->l_desc.dyn_tree = s->dyn_ltree;
+ s->l_desc.stat_desc = (struct static_tree_desc_s *)&static_l_desc;
+
+ s->d_desc.dyn_tree = s->dyn_dtree;
+ s->d_desc.stat_desc = (struct static_tree_desc_s *)&static_d_desc;
+
+ s->bl_desc.dyn_tree = s->bl_tree;
+ s->bl_desc.stat_desc = (struct static_tree_desc_s *)&static_bl_desc;
+
+ s->bi_buf = 0;
+ s->bi_valid = 0;
+ s->last_eob_len = 8; /* enough lookahead for inflate */
+#ifdef DEBUG
+ s->compressed_len = 0L;
+ s->bits_sent = 0L;
+#endif
+
+ /* Initialize the first block of the first file: */
+ init_block(s);
+}
+
+/* ===========================================================================
+ * Initialize a new block.
+ */
+local void init_block(s)
+ deflate_state *s;
+{
+ int n; /* iterates over tree elements */
+
+ /* Initialize the trees. */
+ for (n = 0; n < L_CODES; n++) s->dyn_ltree[n].Freq = 0;
+ for (n = 0; n < D_CODES; n++) s->dyn_dtree[n].Freq = 0;
+ for (n = 0; n < BL_CODES; n++) s->bl_tree[n].Freq = 0;
+
+ s->dyn_ltree[END_BLOCK].Freq = 1;
+ s->opt_len = s->static_len = 0L;
+ s->last_lit = s->matches = 0;
+}
+
+#define SMALLEST 1
+/* Index within the heap array of least frequent node in the Huffman tree */
+
+
+/* ===========================================================================
+ * Remove the smallest element from the heap and recreate the heap with
+ * one less element. Updates heap and heap_len.
+ */
+#define pqremove(s, tree, top) \
+{\
+ top = s->heap[SMALLEST]; \
+ s->heap[SMALLEST] = s->heap[s->heap_len--]; \
+ pqdownheap(s, tree, SMALLEST); \
+}
+
+/* ===========================================================================
+ * Compares to subtrees, using the tree depth as tie breaker when
+ * the subtrees have equal frequency. This minimizes the worst case length.
+ */
+#define smaller(tree, n, m, depth) \
+ (tree[n].Freq < tree[m].Freq || \
+ (tree[n].Freq == tree[m].Freq && depth[n] <= depth[m]))
+
+/* ===========================================================================
+ * Restore the heap property by moving down the tree starting at node k,
+ * exchanging a node with the smallest of its two sons if necessary, stopping
+ * when the heap property is re-established (each father smaller than its
+ * two sons).
+ */
+local void pqdownheap(s, tree, k)
+ deflate_state *s;
+ ct_data *tree; /* the tree to restore */
+ int k; /* node to move down */
+{
+ int v = s->heap[k];
+ int j = k << 1; /* left son of k */
+ while (j <= s->heap_len) {
+ /* Set j to the smallest of the two sons: */
+ if (j < s->heap_len &&
+ smaller(tree, s->heap[j+1], s->heap[j], s->depth)) {
+ j++;
+ }
+ /* Exit if v is smaller than both sons */
+ if (smaller(tree, v, s->heap[j], s->depth)) break;
+
+ /* Exchange v with the smallest son */
+ s->heap[k] = s->heap[j]; k = j;
+
+ /* And continue down the tree, setting j to the left son of k */
+ j <<= 1;
+ }
+ s->heap[k] = v;
+}
+
+/* ===========================================================================
+ * Compute the optimal bit lengths for a tree and update the total bit length
+ * for the current block.
+ * IN assertion: the fields freq and dad are set, heap[heap_max] and
+ * above are the tree nodes sorted by increasing frequency.
+ * OUT assertions: the field len is set to the optimal bit length, the
+ * array bl_count contains the frequencies for each bit length.
+ * The length opt_len is updated; static_len is also updated if stree is
+ * not null.
+ */
+local void gen_bitlen(s, desc)
+ deflate_state *s;
+ tree_desc *desc; /* the tree descriptor */
+{
+ ct_data *tree = desc->dyn_tree;
+ int max_code = desc->max_code;
+ const ct_data *stree = desc->stat_desc->static_tree;
+ const intf *extra = desc->stat_desc->extra_bits;
+ int base = desc->stat_desc->extra_base;
+ int max_length = desc->stat_desc->max_length;
+ int h; /* heap index */
+ int n, m; /* iterate over the tree elements */
+ int bits; /* bit length */
+ int xbits; /* extra bits */
+ ush f; /* frequency */
+ int overflow = 0; /* number of elements with bit length too large */
+
+ for (bits = 0; bits <= MAX_BITS; bits++) s->bl_count[bits] = 0;
+
+ /* In a first pass, compute the optimal bit lengths (which may
+ * overflow in the case of the bit length tree).
+ */
+ tree[s->heap[s->heap_max]].Len = 0; /* root of the heap */
+
+ for (h = s->heap_max+1; h < HEAP_SIZE; h++) {
+ n = s->heap[h];
+ bits = tree[tree[n].Dad].Len + 1;
+ if (bits > max_length) bits = max_length, overflow++;
+ tree[n].Len = (ush)bits;
+ /* We overwrite tree[n].Dad which is no longer needed */
+
+ if (n > max_code) continue; /* not a leaf node */
+
+ s->bl_count[bits]++;
+ xbits = 0;
+ if (n >= base) xbits = extra[n-base];
+ f = tree[n].Freq;
+ s->opt_len += (ulg)f * (bits + xbits);
+ if (stree) s->static_len += (ulg)f * (stree[n].Len + xbits);
+ }
+ if (overflow == 0) return;
+
+ Trace((stderr,"\nbit length overflow\n"));
+ /* This happens for example on obj2 and pic of the Calgary corpus */
+
+ /* Find the first bit length which could increase: */
+ do {
+ bits = max_length-1;
+ while (s->bl_count[bits] == 0) bits--;
+ s->bl_count[bits]--; /* move one leaf down the tree */
+ s->bl_count[bits+1] += 2; /* move one overflow item as its brother */
+ s->bl_count[max_length]--;
+ /* The brother of the overflow item also moves one step up,
+ * but this does not affect bl_count[max_length]
+ */
+ overflow -= 2;
+ } while (overflow > 0);
+
+ /* Now recompute all bit lengths, scanning in increasing frequency.
+ * h is still equal to HEAP_SIZE. (It is simpler to reconstruct all
+ * lengths instead of fixing only the wrong ones. This idea is taken
+ * from 'ar' written by Haruhiko Okumura.)
+ */
+ for (bits = max_length; bits != 0; bits--) {
+ n = s->bl_count[bits];
+ while (n != 0) {
+ m = s->heap[--h];
+ if (m > max_code) continue;
+ if (tree[m].Len != (unsigned) bits) {
+ Trace((stderr,"code %d bits %d->%d\n", m, tree[m].Len, bits));
+ s->opt_len += ((long)bits - (long)tree[m].Len)
+ *(long)tree[m].Freq;
+ tree[m].Len = (ush)bits;
+ }
+ n--;
+ }
+ }
+}
+
+/* ===========================================================================
+ * Generate the codes for a given tree and bit counts (which need not be
+ * optimal).
+ * IN assertion: the array bl_count contains the bit length statistics for
+ * the given tree and the field len is set for all tree elements.
+ * OUT assertion: the field code is set for all tree elements of non
+ * zero code length.
+ */
+local void gen_codes (tree, max_code, bl_count)
+ ct_data *tree; /* the tree to decorate */
+ int max_code; /* largest code with non zero frequency */
+ ushf *bl_count; /* number of codes at each bit length */
+{
+ ush next_code[MAX_BITS+1]; /* next code value for each bit length */
+ ush code = 0; /* running code value */
+ int bits; /* bit index */
+ int n; /* code index */
+
+ /* The distribution counts are first used to generate the code values
+ * without bit reversal.
+ */
+ for (bits = 1; bits <= MAX_BITS; bits++) {
+ next_code[bits] = code = (code + bl_count[bits-1]) << 1;
+ }
+ /* Check that the bit counts in bl_count are consistent. The last code
+ * must be all ones.
+ */
+ Assert (code + bl_count[MAX_BITS]-1 == (1<<MAX_BITS)-1,
+ "inconsistent bit counts");
+ Tracev((stderr,"\ngen_codes: max_code %d ", max_code));
+
+ for (n = 0; n <= max_code; n++) {
+ int len = tree[n].Len;
+ if (len == 0) continue;
+ /* Now reverse the bits */
+ tree[n].Code = bi_reverse(next_code[len]++, len);
+
+ Tracecv(tree != static_ltree, (stderr,"\nn %3d %c l %2d c %4x (%x) ",
+ n, (isgraph(n) ? n : ' '), len, tree[n].Code, next_code[len]-1));
+ }
+}
+
+/* ===========================================================================
+ * Construct one Huffman tree and assigns the code bit strings and lengths.
+ * Update the total bit length for the current block.
+ * IN assertion: the field freq is set for all tree elements.
+ * OUT assertions: the fields len and code are set to the optimal bit length
+ * and corresponding code. The length opt_len is updated; static_len is
+ * also updated if stree is not null. The field max_code is set.
+ */
+local void build_tree(s, desc)
+ deflate_state *s;
+ tree_desc *desc; /* the tree descriptor */
+{
+ ct_data *tree = desc->dyn_tree;
+ const ct_data *stree = desc->stat_desc->static_tree;
+ int elems = desc->stat_desc->elems;
+ int n, m; /* iterate over heap elements */
+ int max_code = -1; /* largest code with non zero frequency */
+ int node; /* new node being created */
+
+ /* Construct the initial heap, with least frequent element in
+ * heap[SMALLEST]. The sons of heap[n] are heap[2*n] and heap[2*n+1].
+ * heap[0] is not used.
+ */
+ s->heap_len = 0, s->heap_max = HEAP_SIZE;
+
+ for (n = 0; n < elems; n++) {
+ if (tree[n].Freq != 0) {
+ s->heap[++(s->heap_len)] = max_code = n;
+ s->depth[n] = 0;
+ } else {
+ tree[n].Len = 0;
+ }
+ }
+
+ /* The pkzip format requires that at least one distance code exists,
+ * and that at least one bit should be sent even if there is only one
+ * possible code. So to avoid special checks later on we force at least
+ * two codes of non zero frequency.
+ */
+ while (s->heap_len < 2) {
+ node = s->heap[++(s->heap_len)] = (max_code < 2 ? ++max_code : 0);
+ tree[node].Freq = 1;
+ s->depth[node] = 0;
+ s->opt_len--; if (stree) s->static_len -= stree[node].Len;
+ /* node is 0 or 1 so it does not have extra bits */
+ }
+ desc->max_code = max_code;
+
+ /* The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree,
+ * establish sub-heaps of increasing lengths:
+ */
+ for (n = s->heap_len/2; n >= 1; n--) pqdownheap(s, tree, n);
+
+ /* Construct the Huffman tree by repeatedly combining the least two
+ * frequent nodes.
+ */
+ node = elems; /* next internal node of the tree */
+ do {
+ pqremove(s, tree, n); /* n = node of least frequency */
+ m = s->heap[SMALLEST]; /* m = node of next least frequency */
+
+ s->heap[--(s->heap_max)] = n; /* keep the nodes sorted by frequency */
+ s->heap[--(s->heap_max)] = m;
+
+ /* Create a new node father of n and m */
+ tree[node].Freq = tree[n].Freq + tree[m].Freq;
+ s->depth[node] = (uch)((s->depth[n] >= s->depth[m] ?
+ s->depth[n] : s->depth[m]) + 1);
+ tree[n].Dad = tree[m].Dad = (ush)node;
+#ifdef DUMP_BL_TREE
+ if (tree == s->bl_tree) {
+ fprintf(stderr,"\nnode %d(%d), sons %d(%d) %d(%d)",
+ node, tree[node].Freq, n, tree[n].Freq, m, tree[m].Freq);
+ }
+#endif
+ /* and insert the new node in the heap */
+ s->heap[SMALLEST] = node++;
+ pqdownheap(s, tree, SMALLEST);
+
+ } while (s->heap_len >= 2);
+
+ s->heap[--(s->heap_max)] = s->heap[SMALLEST];
+
+ /* At this point, the fields freq and dad are set. We can now
+ * generate the bit lengths.
+ */
+ gen_bitlen(s, (tree_desc *)desc);
+
+ /* The field len is now set, we can generate the bit codes */
+ gen_codes ((ct_data *)tree, max_code, s->bl_count);
+}
+
+/* ===========================================================================
+ * Scan a literal or distance tree to determine the frequencies of the codes
+ * in the bit length tree.
+ */
+local void scan_tree (s, tree, max_code)
+ deflate_state *s;
+ ct_data *tree; /* the tree to be scanned */
+ int max_code; /* and its largest code of non zero frequency */
+{
+ int n; /* iterates over all tree elements */
+ int prevlen = -1; /* last emitted length */
+ int curlen; /* length of current code */
+ int nextlen = tree[0].Len; /* length of next code */
+ int count = 0; /* repeat count of the current code */
+ int max_count = 7; /* max repeat count */
+ int min_count = 4; /* min repeat count */
+
+ if (nextlen == 0) max_count = 138, min_count = 3;
+ tree[max_code+1].Len = (ush)0xffff; /* guard */
+
+ for (n = 0; n <= max_code; n++) {
+ curlen = nextlen; nextlen = tree[n+1].Len;
+ if (++count < max_count && curlen == nextlen) {
+ continue;
+ } else if (count < min_count) {
+ s->bl_tree[curlen].Freq += count;
+ } else if (curlen != 0) {
+ if (curlen != prevlen) s->bl_tree[curlen].Freq++;
+ s->bl_tree[REP_3_6].Freq++;
+ } else if (count <= 10) {
+ s->bl_tree[REPZ_3_10].Freq++;
+ } else {
+ s->bl_tree[REPZ_11_138].Freq++;
+ }
+ count = 0; prevlen = curlen;
+ if (nextlen == 0) {
+ max_count = 138, min_count = 3;
+ } else if (curlen == nextlen) {
+ max_count = 6, min_count = 3;
+ } else {
+ max_count = 7, min_count = 4;
+ }
+ }
+}
+
+/* ===========================================================================
+ * Send a literal or distance tree in compressed form, using the codes in
+ * bl_tree.
+ */
+local void send_tree (s, tree, max_code)
+ deflate_state *s;
+ ct_data *tree; /* the tree to be scanned */
+ int max_code; /* and its largest code of non zero frequency */
+{
+ int n; /* iterates over all tree elements */
+ int prevlen = -1; /* last emitted length */
+ int curlen; /* length of current code */
+ int nextlen = tree[0].Len; /* length of next code */
+ int count = 0; /* repeat count of the current code */
+ int max_count = 7; /* max repeat count */
+ int min_count = 4; /* min repeat count */
+
+ /* tree[max_code+1].Len = -1; */ /* guard already set */
+ if (nextlen == 0) max_count = 138, min_count = 3;
+
+ for (n = 0; n <= max_code; n++) {
+ curlen = nextlen; nextlen = tree[n+1].Len;
+ if (++count < max_count && curlen == nextlen) {
+ continue;
+ } else if (count < min_count) {
+ do { send_code(s, curlen, s->bl_tree); } while (--count != 0);
+
+ } else if (curlen != 0) {
+ if (curlen != prevlen) {
+ send_code(s, curlen, s->bl_tree); count--;
+ }
+ Assert(count >= 3 && count <= 6, " 3_6?");
+ send_code(s, REP_3_6, s->bl_tree); send_bits(s, count-3, 2);
+
+ } else if (count <= 10) {
+ send_code(s, REPZ_3_10, s->bl_tree); send_bits(s, count-3, 3);
+
+ } else {
+ send_code(s, REPZ_11_138, s->bl_tree); send_bits(s, count-11, 7);
+ }
+ count = 0; prevlen = curlen;
+ if (nextlen == 0) {
+ max_count = 138, min_count = 3;
+ } else if (curlen == nextlen) {
+ max_count = 6, min_count = 3;
+ } else {
+ max_count = 7, min_count = 4;
+ }
+ }
+}
+
+/* ===========================================================================
+ * Construct the Huffman tree for the bit lengths and return the index in
+ * bl_order of the last bit length code to send.
+ */
+local int build_bl_tree(s)
+ deflate_state *s;
+{
+ int max_blindex; /* index of last bit length code of non zero freq */
+
+ /* Determine the bit length frequencies for literal and distance trees */
+ scan_tree(s, (ct_data *)s->dyn_ltree, s->l_desc.max_code);
+ scan_tree(s, (ct_data *)s->dyn_dtree, s->d_desc.max_code);
+
+ /* Build the bit length tree: */
+ build_tree(s, (tree_desc *)(&(s->bl_desc)));
+ /* opt_len now includes the length of the tree representations, except
+ * the lengths of the bit lengths codes and the 5+5+4 bits for the counts.
+ */
+
+ /* Determine the number of bit length codes to send. The pkzip format
+ * requires that at least 4 bit length codes be sent. (appnote.txt says
+ * 3 but the actual value used is 4.)
+ */
+ for (max_blindex = BL_CODES-1; max_blindex >= 3; max_blindex--) {
+ if (s->bl_tree[bl_order[max_blindex]].Len != 0) break;
+ }
+ /* Update opt_len to include the bit length tree and counts */
+ s->opt_len += 3*(max_blindex+1) + 5+5+4;
+ Tracev((stderr, "\ndyn trees: dyn %ld, stat %ld",
+ s->opt_len, s->static_len));
+
+ return max_blindex;
+}
+
+/* ===========================================================================
+ * Send the header for a block using dynamic Huffman trees: the counts, the
+ * lengths of the bit length codes, the literal tree and the distance tree.
+ * IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4.
+ */
+local void send_all_trees(s, lcodes, dcodes, blcodes)
+ deflate_state *s;
+ int lcodes, dcodes, blcodes; /* number of codes for each tree */
+{
+ int rank; /* index in bl_order */
+
+ Assert (lcodes >= 257 && dcodes >= 1 && blcodes >= 4, "not enough codes");
+ Assert (lcodes <= L_CODES && dcodes <= D_CODES && blcodes <= BL_CODES,
+ "too many codes");
+ Tracev((stderr, "\nbl counts: "));
+ send_bits(s, lcodes-257, 5); /* not +255 as stated in appnote.txt */
+ send_bits(s, dcodes-1, 5);
+ send_bits(s, blcodes-4, 4); /* not -3 as stated in appnote.txt */
+ for (rank = 0; rank < blcodes; rank++) {
+ Tracev((stderr, "\nbl code %2d ", bl_order[rank]));
+ send_bits(s, s->bl_tree[bl_order[rank]].Len, 3);
+ }
+ Tracev((stderr, "\nbl tree: sent %ld", s->bits_sent));
+
+ send_tree(s, (ct_data *)s->dyn_ltree, lcodes-1); /* literal tree */
+ Tracev((stderr, "\nlit tree: sent %ld", s->bits_sent));
+
+ send_tree(s, (ct_data *)s->dyn_dtree, dcodes-1); /* distance tree */
+ Tracev((stderr, "\ndist tree: sent %ld", s->bits_sent));
+}
+
+/* ===========================================================================
+ * Send a stored block
+ */
+void _tr_stored_block(s, buf, stored_len, eof)
+ deflate_state *s;
+ charf *buf; /* input block */
+ ulg stored_len; /* length of input block */
+ int eof; /* true if this is the last block for a file */
+{
+ send_bits(s, (STORED_BLOCK<<1)+eof, 3); /* send block type */
+#ifdef DEBUG
+ s->compressed_len = (s->compressed_len + 3 + 7) & (ulg)~7L;
+ s->compressed_len += (stored_len + 4) << 3;
+#endif
+ copy_block(s, buf, (unsigned)stored_len, 1); /* with header */
+}
+
+/* ===========================================================================
+ * Send one empty static block to give enough lookahead for inflate.
+ * This takes 10 bits, of which 7 may remain in the bit buffer.
+ * The current inflate code requires 9 bits of lookahead. If the
+ * last two codes for the previous block (real code plus EOB) were coded
+ * on 5 bits or less, inflate may have only 5+3 bits of lookahead to decode
+ * the last real code. In this case we send two empty static blocks instead
+ * of one. (There are no problems if the previous block is stored or fixed.)
+ * To simplify the code, we assume the worst case of last real code encoded
+ * on one bit only.
+ */
+void _tr_align(s)
+ deflate_state *s;
+{
+ send_bits(s, STATIC_TREES<<1, 3);
+ send_code(s, END_BLOCK, static_ltree);
+#ifdef DEBUG
+ s->compressed_len += 10L; /* 3 for block type, 7 for EOB */
+#endif
+ bi_flush(s);
+ /* Of the 10 bits for the empty block, we have already sent
+ * (10 - bi_valid) bits. The lookahead for the last real code (before
+ * the EOB of the previous block) was thus at least one plus the length
+ * of the EOB plus what we have just sent of the empty static block.
+ */
+ if (1 + s->last_eob_len + 10 - s->bi_valid < 9) {
+ send_bits(s, STATIC_TREES<<1, 3);
+ send_code(s, END_BLOCK, static_ltree);
+#ifdef DEBUG
+ s->compressed_len += 10L;
+#endif
+ bi_flush(s);
+ }
+ s->last_eob_len = 7;
+}
+
+/* ===========================================================================
+ * Determine the best encoding for the current block: dynamic trees, static
+ * trees or store, and output the encoded block to the zip file.
+ */
+void _tr_flush_block(s, buf, stored_len, eof)
+ deflate_state *s;
+ charf *buf; /* input block, or NULL if too old */
+ ulg stored_len; /* length of input block */
+ int eof; /* true if this is the last block for a file */
+{
+ ulg opt_lenb, static_lenb; /* opt_len and static_len in bytes */
+ int max_blindex = 0; /* index of last bit length code of non zero freq */
+
+ /* Build the Huffman trees unless a stored block is forced */
+ if (s->level > 0) {
+
+ /* Check if the file is ascii or binary */
+ if (s->data_type == Z_UNKNOWN) set_data_type(s);
+
+ /* Construct the literal and distance trees */
+ build_tree(s, (tree_desc *)(&(s->l_desc)));
+ Tracev((stderr, "\nlit data: dyn %ld, stat %ld", s->opt_len,
+ s->static_len));
+
+ build_tree(s, (tree_desc *)(&(s->d_desc)));
+ Tracev((stderr, "\ndist data: dyn %ld, stat %ld", s->opt_len,
+ s->static_len));
+ /* At this point, opt_len and static_len are the total bit lengths of
+ * the compressed block data, excluding the tree representations.
+ */
+
+ /* Build the bit length tree for the above two trees, and get the index
+ * in bl_order of the last bit length code to send.
+ */
+ max_blindex = build_bl_tree(s);
+
+ /* Determine the best encoding. Compute the block lengths in bytes. */
+ opt_lenb = (s->opt_len+3+7)>>3;
+ static_lenb = (s->static_len+3+7)>>3;
+
+ Tracev((stderr, "\nopt %lu(%lu) stat %lu(%lu) stored %lu lit %u ",
+ opt_lenb, s->opt_len, static_lenb, s->static_len, stored_len,
+ s->last_lit));
+
+ if (static_lenb <= opt_lenb) opt_lenb = static_lenb;
+
+ } else {
+ Assert(buf != (char*)0, "lost buf");
+ opt_lenb = static_lenb = stored_len + 5; /* force a stored block */
+ }
+
+#ifdef FORCE_STORED
+ if (buf != (char*)0) { /* force stored block */
+#else
+ if (stored_len+4 <= opt_lenb && buf != (char*)0) {
+ /* 4: two words for the lengths */
+#endif
+ /* The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE.
+ * Otherwise we can't have processed more than WSIZE input bytes since
+ * the last block flush, because compression would have been
+ * successful. If LIT_BUFSIZE <= WSIZE, it is never too late to
+ * transform a block into a stored block.
+ */
+ _tr_stored_block(s, buf, stored_len, eof);
+
+#ifdef FORCE_STATIC
+ } else if (static_lenb >= 0) { /* force static trees */
+#else
+ } else if (static_lenb == opt_lenb) {
+#endif
+ send_bits(s, (STATIC_TREES<<1)+eof, 3);
+ compress_block(s, (ct_data *)static_ltree, (ct_data *)static_dtree);
+#ifdef DEBUG
+ s->compressed_len += 3 + s->static_len;
+#endif
+ } else {
+ send_bits(s, (DYN_TREES<<1)+eof, 3);
+ send_all_trees(s, s->l_desc.max_code+1, s->d_desc.max_code+1,
+ max_blindex+1);
+ compress_block(s, (ct_data *)s->dyn_ltree, (ct_data *)s->dyn_dtree);
+#ifdef DEBUG
+ s->compressed_len += 3 + s->opt_len;
+#endif
+ }
+ Assert (s->compressed_len == s->bits_sent, "bad compressed size");
+ /* The above check is made mod 2^32, for files larger than 512 MB
+ * and uLong implemented on 32 bits.
+ */
+ init_block(s);
+
+ if (eof) {
+ bi_windup(s);
+#ifdef DEBUG
+ s->compressed_len += 7; /* align on byte boundary */
+#endif
+ }
+ Tracev((stderr,"\ncomprlen %lu(%lu) ", s->compressed_len>>3,
+ s->compressed_len-7*eof));
+}
+
+/* ===========================================================================
+ * Save the match info and tally the frequency counts. Return true if
+ * the current block must be flushed.
+ */
+int _tr_tally (s, dist, lc)
+ deflate_state *s;
+ unsigned dist; /* distance of matched string */
+ unsigned lc; /* match length-MIN_MATCH or unmatched char (if dist==0) */
+{
+ s->d_buf[s->last_lit] = (ush)dist;
+ s->l_buf[s->last_lit++] = (uch)lc;
+ if (dist == 0) {
+ /* lc is the unmatched char */
+ s->dyn_ltree[lc].Freq++;
+ } else {
+ s->matches++;
+ /* Here, lc is the match length - MIN_MATCH */
+ dist--; /* dist = match distance - 1 */
+ Assert((ush)dist < (ush)MAX_DIST(s) &&
+ (ush)lc <= (ush)(MAX_MATCH-MIN_MATCH) &&
+ (ush)d_code(dist) < (ush)D_CODES, "_tr_tally: bad match");
+
+ s->dyn_ltree[_length_code[lc]+LITERALS+1].Freq++;
+ s->dyn_dtree[d_code(dist)].Freq++;
+ }
+
+#ifdef TRUNCATE_BLOCK
+ /* Try to guess if it is profitable to stop the current block here */
+ if ((s->last_lit & 0x1fff) == 0 && s->level > 2) {
+ /* Compute an upper bound for the compressed length */
+ ulg out_length = (ulg)s->last_lit*8L;
+ ulg in_length = (ulg)((long)s->strstart - s->block_start);
+ int dcode;
+ for (dcode = 0; dcode < D_CODES; dcode++) {
+ out_length += (ulg)s->dyn_dtree[dcode].Freq *
+ (5L+extra_dbits[dcode]);
+ }
+ out_length >>= 3;
+ Tracev((stderr,"\nlast_lit %u, in %ld, out ~%ld(%ld%%) ",
+ s->last_lit, in_length, out_length,
+ 100L - out_length*100L/in_length));
+ if (s->matches < s->last_lit/2 && out_length < in_length/2) return 1;
+ }
+#endif
+ return (s->last_lit == s->lit_bufsize-1);
+ /* We avoid equality with lit_bufsize because of wraparound at 64K
+ * on 16 bit machines and because stored blocks are restricted to
+ * 64K-1 bytes.
+ */
+}
+
+/* ===========================================================================
+ * Send the block data compressed using the given Huffman trees
+ */
+local void compress_block(s, ltree, dtree)
+ deflate_state *s;
+ ct_data *ltree; /* literal tree */
+ ct_data *dtree; /* distance tree */
+{
+ unsigned dist; /* distance of matched string */
+ int lc; /* match length or unmatched char (if dist == 0) */
+ unsigned lx = 0; /* running index in l_buf */
+ unsigned code; /* the code to send */
+ int extra; /* number of extra bits to send */
+
+ if (s->last_lit != 0) do {
+ dist = s->d_buf[lx];
+ lc = s->l_buf[lx++];
+ if (dist == 0) {
+ send_code(s, lc, ltree); /* send a literal byte */
+ Tracecv(isgraph(lc), (stderr," '%c' ", lc));
+ } else {
+ /* Here, lc is the match length - MIN_MATCH */
+ code = _length_code[lc];
+ send_code(s, code+LITERALS+1, ltree); /* send the length code */
+ extra = extra_lbits[code];
+ if (extra != 0) {
+ lc -= base_length[code];
+ send_bits(s, lc, extra); /* send the extra length bits */
+ }
+ dist--; /* dist is now the match distance - 1 */
+ code = d_code(dist);
+ Assert (code < D_CODES, "bad d_code");
+
+ send_code(s, code, dtree); /* send the distance code */
+ extra = extra_dbits[code];
+ if (extra != 0) {
+ dist -= base_dist[code];
+ send_bits(s, dist, extra); /* send the extra distance bits */
+ }
+ } /* literal or match pair ? */
+
+ /* Check that the overlay between pending_buf and d_buf+l_buf is ok: */
+ Assert((uInt)(s->pending) < s->lit_bufsize + 2*lx,
+ "pendingBuf overflow");
+
+ } while (lx < s->last_lit);
+
+ send_code(s, END_BLOCK, ltree);
+ s->last_eob_len = ltree[END_BLOCK].Len;
+}
+
+/* ===========================================================================
+ * Set the data type to ASCII or BINARY, using a crude approximation:
+ * binary if more than 20% of the bytes are <= 6 or >= 128, ascii otherwise.
+ * IN assertion: the fields freq of dyn_ltree are set and the total of all
+ * frequencies does not exceed 64K (to fit in an int on 16 bit machines).
+ */
+local void set_data_type(s)
+ deflate_state *s;
+{
+ int n = 0;
+ unsigned ascii_freq = 0;
+ unsigned bin_freq = 0;
+ while (n < 7) bin_freq += s->dyn_ltree[n++].Freq;
+ while (n < 128) ascii_freq += s->dyn_ltree[n++].Freq;
+ while (n < LITERALS) bin_freq += s->dyn_ltree[n++].Freq;
+ s->data_type = (Byte)(bin_freq > (ascii_freq >> 2) ? Z_BINARY : Z_ASCII);
+}
+
+/* ===========================================================================
+ * Reverse the first len bits of a code, using straightforward code (a faster
+ * method would use a table)
+ * IN assertion: 1 <= len <= 15
+ */
+local unsigned bi_reverse(code, len)
+ unsigned code; /* the value to invert */
+ int len; /* its bit length */
+{
+ register unsigned res = 0;
+ do {
+ res |= code & 1;
+ code >>= 1, res <<= 1;
+ } while (--len > 0);
+ return res >> 1;
+}
+
+/* ===========================================================================
+ * Flush the bit buffer, keeping at most 7 bits in it.
+ */
+local void bi_flush(s)
+ deflate_state *s;
+{
+ if (s->bi_valid == 16) {
+ put_short(s, s->bi_buf);
+ s->bi_buf = 0;
+ s->bi_valid = 0;
+ } else if (s->bi_valid >= 8) {
+ put_byte(s, (Byte)s->bi_buf);
+ s->bi_buf >>= 8;
+ s->bi_valid -= 8;
+ }
+}
+
+/* ===========================================================================
+ * Flush the bit buffer and align the output on a byte boundary
+ */
+local void bi_windup(s)
+ deflate_state *s;
+{
+ if (s->bi_valid > 8) {
+ put_short(s, s->bi_buf);
+ } else if (s->bi_valid > 0) {
+ put_byte(s, (Byte)s->bi_buf);
+ }
+ s->bi_buf = 0;
+ s->bi_valid = 0;
+#ifdef DEBUG
+ s->bits_sent = (s->bits_sent+7) & ~7;
+#endif
+}
+
+/* ===========================================================================
+ * Copy a stored block, storing first the length and its
+ * one's complement if requested.
+ */
+local void copy_block(s, buf, len, header)
+ deflate_state *s;
+ charf *buf; /* the input data */
+ unsigned len; /* its length */
+ int header; /* true if block header must be written */
+{
+ bi_windup(s); /* align on byte boundary */
+ s->last_eob_len = 8; /* enough lookahead for inflate */
+
+ if (header) {
+ put_short(s, (ush)len);
+ put_short(s, (ush)~len);
+#ifdef DEBUG
+ s->bits_sent += 2*16;
+#endif
+ }
+#ifdef DEBUG
+ s->bits_sent += (ulg)len<<3;
+#endif
+ while (len--) {
+ put_byte(s, *buf++);
+ }
+}
diff --git a/src/zlib/trees.h b/src/zlib/trees.h
new file mode 100755
index 0000000..72facf9
--- /dev/null
+++ b/src/zlib/trees.h
@@ -0,0 +1,128 @@
+/* header created automatically with -DGEN_TREES_H */
+
+local const ct_data static_ltree[L_CODES+2] = {
+{{ 12},{ 8}}, {{140},{ 8}}, {{ 76},{ 8}}, {{204},{ 8}}, {{ 44},{ 8}},
+{{172},{ 8}}, {{108},{ 8}}, {{236},{ 8}}, {{ 28},{ 8}}, {{156},{ 8}},
+{{ 92},{ 8}}, {{220},{ 8}}, {{ 60},{ 8}}, {{188},{ 8}}, {{124},{ 8}},
+{{252},{ 8}}, {{ 2},{ 8}}, {{130},{ 8}}, {{ 66},{ 8}}, {{194},{ 8}},
+{{ 34},{ 8}}, {{162},{ 8}}, {{ 98},{ 8}}, {{226},{ 8}}, {{ 18},{ 8}},
+{{146},{ 8}}, {{ 82},{ 8}}, {{210},{ 8}}, {{ 50},{ 8}}, {{178},{ 8}},
+{{114},{ 8}}, {{242},{ 8}}, {{ 10},{ 8}}, {{138},{ 8}}, {{ 74},{ 8}},
+{{202},{ 8}}, {{ 42},{ 8}}, {{170},{ 8}}, {{106},{ 8}}, {{234},{ 8}},
+{{ 26},{ 8}}, {{154},{ 8}}, {{ 90},{ 8}}, {{218},{ 8}}, {{ 58},{ 8}},
+{{186},{ 8}}, {{122},{ 8}}, {{250},{ 8}}, {{ 6},{ 8}}, {{134},{ 8}},
+{{ 70},{ 8}}, {{198},{ 8}}, {{ 38},{ 8}}, {{166},{ 8}}, {{102},{ 8}},
+{{230},{ 8}}, {{ 22},{ 8}}, {{150},{ 8}}, {{ 86},{ 8}}, {{214},{ 8}},
+{{ 54},{ 8}}, {{182},{ 8}}, {{118},{ 8}}, {{246},{ 8}}, {{ 14},{ 8}},
+{{142},{ 8}}, {{ 78},{ 8}}, {{206},{ 8}}, {{ 46},{ 8}}, {{174},{ 8}},
+{{110},{ 8}}, {{238},{ 8}}, {{ 30},{ 8}}, {{158},{ 8}}, {{ 94},{ 8}},
+{{222},{ 8}}, {{ 62},{ 8}}, {{190},{ 8}}, {{126},{ 8}}, {{254},{ 8}},
+{{ 1},{ 8}}, {{129},{ 8}}, {{ 65},{ 8}}, {{193},{ 8}}, {{ 33},{ 8}},
+{{161},{ 8}}, {{ 97},{ 8}}, {{225},{ 8}}, {{ 17},{ 8}}, {{145},{ 8}},
+{{ 81},{ 8}}, {{209},{ 8}}, {{ 49},{ 8}}, {{177},{ 8}}, {{113},{ 8}},
+{{241},{ 8}}, {{ 9},{ 8}}, {{137},{ 8}}, {{ 73},{ 8}}, {{201},{ 8}},
+{{ 41},{ 8}}, {{169},{ 8}}, {{105},{ 8}}, {{233},{ 8}}, {{ 25},{ 8}},
+{{153},{ 8}}, {{ 89},{ 8}}, {{217},{ 8}}, {{ 57},{ 8}}, {{185},{ 8}},
+{{121},{ 8}}, {{249},{ 8}}, {{ 5},{ 8}}, {{133},{ 8}}, {{ 69},{ 8}},
+{{197},{ 8}}, {{ 37},{ 8}}, {{165},{ 8}}, {{101},{ 8}}, {{229},{ 8}},
+{{ 21},{ 8}}, {{149},{ 8}}, {{ 85},{ 8}}, {{213},{ 8}}, {{ 53},{ 8}},
+{{181},{ 8}}, {{117},{ 8}}, {{245},{ 8}}, {{ 13},{ 8}}, {{141},{ 8}},
+{{ 77},{ 8}}, {{205},{ 8}}, {{ 45},{ 8}}, {{173},{ 8}}, {{109},{ 8}},
+{{237},{ 8}}, {{ 29},{ 8}}, {{157},{ 8}}, {{ 93},{ 8}}, {{221},{ 8}},
+{{ 61},{ 8}}, {{189},{ 8}}, {{125},{ 8}}, {{253},{ 8}}, {{ 19},{ 9}},
+{{275},{ 9}}, {{147},{ 9}}, {{403},{ 9}}, {{ 83},{ 9}}, {{339},{ 9}},
+{{211},{ 9}}, {{467},{ 9}}, {{ 51},{ 9}}, {{307},{ 9}}, {{179},{ 9}},
+{{435},{ 9}}, {{115},{ 9}}, {{371},{ 9}}, {{243},{ 9}}, {{499},{ 9}},
+{{ 11},{ 9}}, {{267},{ 9}}, {{139},{ 9}}, {{395},{ 9}}, {{ 75},{ 9}},
+{{331},{ 9}}, {{203},{ 9}}, {{459},{ 9}}, {{ 43},{ 9}}, {{299},{ 9}},
+{{171},{ 9}}, {{427},{ 9}}, {{107},{ 9}}, {{363},{ 9}}, {{235},{ 9}},
+{{491},{ 9}}, {{ 27},{ 9}}, {{283},{ 9}}, {{155},{ 9}}, {{411},{ 9}},
+{{ 91},{ 9}}, {{347},{ 9}}, {{219},{ 9}}, {{475},{ 9}}, {{ 59},{ 9}},
+{{315},{ 9}}, {{187},{ 9}}, {{443},{ 9}}, {{123},{ 9}}, {{379},{ 9}},
+{{251},{ 9}}, {{507},{ 9}}, {{ 7},{ 9}}, {{263},{ 9}}, {{135},{ 9}},
+{{391},{ 9}}, {{ 71},{ 9}}, {{327},{ 9}}, {{199},{ 9}}, {{455},{ 9}},
+{{ 39},{ 9}}, {{295},{ 9}}, {{167},{ 9}}, {{423},{ 9}}, {{103},{ 9}},
+{{359},{ 9}}, {{231},{ 9}}, {{487},{ 9}}, {{ 23},{ 9}}, {{279},{ 9}},
+{{151},{ 9}}, {{407},{ 9}}, {{ 87},{ 9}}, {{343},{ 9}}, {{215},{ 9}},
+{{471},{ 9}}, {{ 55},{ 9}}, {{311},{ 9}}, {{183},{ 9}}, {{439},{ 9}},
+{{119},{ 9}}, {{375},{ 9}}, {{247},{ 9}}, {{503},{ 9}}, {{ 15},{ 9}},
+{{271},{ 9}}, {{143},{ 9}}, {{399},{ 9}}, {{ 79},{ 9}}, {{335},{ 9}},
+{{207},{ 9}}, {{463},{ 9}}, {{ 47},{ 9}}, {{303},{ 9}}, {{175},{ 9}},
+{{431},{ 9}}, {{111},{ 9}}, {{367},{ 9}}, {{239},{ 9}}, {{495},{ 9}},
+{{ 31},{ 9}}, {{287},{ 9}}, {{159},{ 9}}, {{415},{ 9}}, {{ 95},{ 9}},
+{{351},{ 9}}, {{223},{ 9}}, {{479},{ 9}}, {{ 63},{ 9}}, {{319},{ 9}},
+{{191},{ 9}}, {{447},{ 9}}, {{127},{ 9}}, {{383},{ 9}}, {{255},{ 9}},
+{{511},{ 9}}, {{ 0},{ 7}}, {{ 64},{ 7}}, {{ 32},{ 7}}, {{ 96},{ 7}},
+{{ 16},{ 7}}, {{ 80},{ 7}}, {{ 48},{ 7}}, {{112},{ 7}}, {{ 8},{ 7}},
+{{ 72},{ 7}}, {{ 40},{ 7}}, {{104},{ 7}}, {{ 24},{ 7}}, {{ 88},{ 7}},
+{{ 56},{ 7}}, {{120},{ 7}}, {{ 4},{ 7}}, {{ 68},{ 7}}, {{ 36},{ 7}},
+{{100},{ 7}}, {{ 20},{ 7}}, {{ 84},{ 7}}, {{ 52},{ 7}}, {{116},{ 7}},
+{{ 3},{ 8}}, {{131},{ 8}}, {{ 67},{ 8}}, {{195},{ 8}}, {{ 35},{ 8}},
+{{163},{ 8}}, {{ 99},{ 8}}, {{227},{ 8}}
+};
+
+local const ct_data static_dtree[D_CODES] = {
+{{ 0},{ 5}}, {{16},{ 5}}, {{ 8},{ 5}}, {{24},{ 5}}, {{ 4},{ 5}},
+{{20},{ 5}}, {{12},{ 5}}, {{28},{ 5}}, {{ 2},{ 5}}, {{18},{ 5}},
+{{10},{ 5}}, {{26},{ 5}}, {{ 6},{ 5}}, {{22},{ 5}}, {{14},{ 5}},
+{{30},{ 5}}, {{ 1},{ 5}}, {{17},{ 5}}, {{ 9},{ 5}}, {{25},{ 5}},
+{{ 5},{ 5}}, {{21},{ 5}}, {{13},{ 5}}, {{29},{ 5}}, {{ 3},{ 5}},
+{{19},{ 5}}, {{11},{ 5}}, {{27},{ 5}}, {{ 7},{ 5}}, {{23},{ 5}}
+};
+
+const uch _dist_code[DIST_CODE_LEN] = {
+ 0, 1, 2, 3, 4, 4, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8,
+ 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10,
+10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
+11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12,
+12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13,
+13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
+14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
+14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
+14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15,
+15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 0, 16, 17,
+18, 18, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 22,
+23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
+24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
+26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
+26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27,
+27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27,
+27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
+28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
+28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
+28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
+29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
+29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
+29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29
+};
+
+const uch _length_code[MAX_MATCH-MIN_MATCH+1]= {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 12, 12,
+13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16,
+17, 17, 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19,
+19, 19, 19, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,
+21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22,
+22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 23, 23, 23,
+23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
+24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
+25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
+25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26,
+26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
+26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27,
+27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28
+};
+
+local const int base_length[LENGTH_CODES] = {
+0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56,
+64, 80, 96, 112, 128, 160, 192, 224, 0
+};
+
+local const int base_dist[D_CODES] = {
+ 0, 1, 2, 3, 4, 6, 8, 12, 16, 24,
+ 32, 48, 64, 96, 128, 192, 256, 384, 512, 768,
+ 1024, 1536, 2048, 3072, 4096, 6144, 8192, 12288, 16384, 24576
+};
+
diff --git a/src/zlib/uncompr.c b/src/zlib/uncompr.c
new file mode 100755
index 0000000..d3ca00a
--- /dev/null
+++ b/src/zlib/uncompr.c
@@ -0,0 +1,61 @@
+/* uncompr.c -- decompress a memory buffer
+ * Copyright (C) 1995-2003 Jean-loup Gailly.
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* @(#) $ Id$ */
+
+#define ZLIB_INTERNAL
+#include "zlib.h"
+
+/* ===========================================================================
+ Decompresses the source buffer into the destination buffer. sourceLen is
+ the byte length of the source buffer. Upon entry, destLen is the total
+ size of the destination buffer, which must be large enough to hold the
+ entire uncompressed data. (The size of the uncompressed data must have
+ been saved previously by the compressor and transmitted to the decompressor
+ by some mechanism outside the scope of this compression library.)
+ Upon exit, destLen is the actual size of the compressed buffer.
+ This function can be used to decompress a whole file at once if the
+ input file is mmap'ed.
+
+ uncompress returns Z_OK if success, Z_MEM_ERROR if there was not
+ enough memory, Z_BUF_ERROR if there was not enough room in the output
+ buffer, or Z_DATA_ERROR if the input data was corrupted.
+*/
+int ZEXPORT uncompress (dest, destLen, source, sourceLen)
+ Bytef *dest;
+ uLongf *destLen;
+ const Bytef *source;
+ uLong sourceLen;
+{
+ z_stream stream;
+ int err;
+
+ stream.next_in = (Bytef*)source;
+ stream.avail_in = (uInt)sourceLen;
+ /* Check for source > 64K on 16-bit machine: */
+ if ((uLong)stream.avail_in != sourceLen) return Z_BUF_ERROR;
+
+ stream.next_out = dest;
+ stream.avail_out = (uInt)*destLen;
+ if ((uLong)stream.avail_out != *destLen) return Z_BUF_ERROR;
+
+ stream.zalloc = (alloc_func)0;
+ stream.zfree = (free_func)0;
+
+ err = inflateInit(&stream);
+ if (err != Z_OK) return err;
+
+ err = inflate(&stream, Z_FINISH);
+ if (err != Z_STREAM_END) {
+ inflateEnd(&stream);
+ if (err == Z_NEED_DICT || (err == Z_BUF_ERROR && stream.avail_in == 0))
+ return Z_DATA_ERROR;
+ return err;
+ }
+ *destLen = stream.total_out;
+
+ err = inflateEnd(&stream);
+ return err;
+}
diff --git a/src/zlib/zconf.h b/src/zlib/zconf.h
new file mode 100755
index 0000000..18642b1
--- /dev/null
+++ b/src/zlib/zconf.h
@@ -0,0 +1,329 @@
+/* zconf.h -- configuration of the zlib compression library
+ * Copyright (C) 1995-2003 Jean-loup Gailly.
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* @(#) $ Id$ */
+
+#ifndef ZCONF_H
+#define ZCONF_H
+
+/*
+ * If you *really* need a unique prefix for all types and library functions,
+ * compile with -DZ_PREFIX. The "standard" zlib should be compiled without it.
+ */
+#ifdef Z_PREFIX
+# define deflateInit_ z_deflateInit_
+# define deflate z_deflate
+# define deflateEnd z_deflateEnd
+# define inflateInit_ z_inflateInit_
+# define inflate z_inflate
+# define inflateEnd z_inflateEnd
+# define deflateInit2_ z_deflateInit2_
+# define deflateSetDictionary z_deflateSetDictionary
+# define deflateCopy z_deflateCopy
+# define deflateReset z_deflateReset
+# define deflatePrime z_deflatePrime
+# define deflateParams z_deflateParams
+# define deflateBound z_deflateBound
+# define inflateInit2_ z_inflateInit2_
+# define inflateSetDictionary z_inflateSetDictionary
+# define inflateSync z_inflateSync
+# define inflateSyncPoint z_inflateSyncPoint
+# define inflateCopy z_inflateCopy
+# define inflateReset z_inflateReset
+# define compress z_compress
+# define compress2 z_compress2
+# define compressBound z_compressBound
+# define uncompress z_uncompress
+# define adler32 z_adler32
+# define crc32 z_crc32
+# define get_crc_table z_get_crc_table
+
+# define Byte z_Byte
+# define uInt z_uInt
+# define uLong z_uLong
+# define Bytef z_Bytef
+# define charf z_charf
+# define intf z_intf
+# define uIntf z_uIntf
+# define uLongf z_uLongf
+# define voidpf z_voidpf
+# define voidp z_voidp
+#endif
+
+#if defined(__MSDOS__) && !defined(MSDOS)
+# define MSDOS
+#endif
+#if (defined(OS_2) || defined(__OS2__)) && !defined(OS2)
+# define OS2
+#endif
+#if defined(_WINDOWS) && !defined(WINDOWS)
+# define WINDOWS
+#endif
+#if (defined(_WIN32) || defined(__WIN32__)) && !defined(WIN32)
+# define WIN32
+#endif
+#if (defined(MSDOS) || defined(OS2) || defined(WINDOWS)) && !defined(WIN32)
+# if !defined(__GNUC__) && !defined(__FLAT__) && !defined(__386__)
+# ifndef SYS16BIT
+# define SYS16BIT
+# endif
+# endif
+#endif
+
+/*
+ * Compile with -DMAXSEG_64K if the alloc function cannot allocate more
+ * than 64k bytes at a time (needed on systems with 16-bit int).
+ */
+#ifdef SYS16BIT
+# define MAXSEG_64K
+#endif
+#ifdef MSDOS
+# define UNALIGNED_OK
+#endif
+
+#ifdef __STDC_VERSION__
+# ifndef STDC
+# define STDC
+# endif
+# if __STDC_VERSION__ >= 199901L
+# ifndef STDC99
+# define STDC99
+# endif
+# endif
+#endif
+#if !defined(STDC) && (defined(__STDC__) || defined(__cplusplus))
+# define STDC
+#endif
+#if !defined(STDC) && (defined(__GNUC__) || defined(__BORLANDC__))
+# define STDC
+#endif
+#if !defined(STDC) && (defined(MSDOS) || defined(WINDOWS) || defined(WIN32))
+# define STDC
+#endif
+#if !defined(STDC) && (defined(OS2) || defined(__HOS_AIX__))
+# define STDC
+#endif
+
+#if defined(__OS400__) && !defined(STDC) /* iSeries (formerly AS/400). */
+# define STDC
+#endif
+
+#ifndef STDC
+# ifndef const /* cannot use !defined(STDC) && !defined(const) on Mac */
+# define const /* note: need a more gentle solution here */
+# endif
+#endif
+
+/* Some Mac compilers merge all .h files incorrectly: */
+#if defined(__MWERKS__)||defined(applec)||defined(THINK_C)||defined(__SC__)
+# define NO_DUMMY_DECL
+#endif
+
+
+/* Windows CE does not have errno.h file */
+#if defined(_WIN32_WCE) && !defined(NO_ERRNO_H)
+# define NO_ERRNO_H
+#endif
+
+/* Maximum value for memLevel in deflateInit2 */
+#ifndef MAX_MEM_LEVEL
+# ifdef MAXSEG_64K
+# define MAX_MEM_LEVEL 8
+# else
+# define MAX_MEM_LEVEL 9
+# endif
+#endif
+
+/* Maximum value for windowBits in deflateInit2 and inflateInit2.
+ * WARNING: reducing MAX_WBITS makes minigzip unable to extract .gz files
+ * created by gzip. (Files created by minigzip can still be extracted by
+ * gzip.)
+ */
+#ifndef MAX_WBITS
+# define MAX_WBITS 15 /* 32K LZ77 window */
+#endif
+
+/* The memory requirements for deflate are (in bytes):
+ (1 << (windowBits+2)) + (1 << (memLevel+9))
+ that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values)
+ plus a few kilobytes for small objects. For example, if you want to reduce
+ the default memory requirements from 256K to 128K, compile with
+ make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7"
+ Of course this will generally degrade compression (there's no free lunch).
+
+ The memory requirements for inflate are (in bytes) 1 << windowBits
+ that is, 32K for windowBits=15 (default value) plus a few kilobytes
+ for small objects.
+*/
+
+ /* Type declarations */
+
+#ifndef OF /* function prototypes */
+# ifdef STDC
+# define OF(args) args
+# else
+# define OF(args) ()
+# endif
+#endif
+
+/* The following definitions for FAR are needed only for MSDOS mixed
+ * model programming (small or medium model with some far allocations).
+ * This was tested only with MSC; for other MSDOS compilers you may have
+ * to define NO_MEMCPY in zutil.h. If you don't need the mixed model,
+ * just define FAR to be empty.
+ */
+#ifdef SYS16BIT
+# if defined(M_I86SM) || defined(M_I86MM)
+ /* MSC small or medium model */
+# define SMALL_MEDIUM
+# ifdef _MSC_VER
+# define FAR _far
+# else
+# define FAR far
+# endif
+# endif
+# if (defined(__SMALL__) || defined(__MEDIUM__))
+ /* Turbo C small or medium model */
+# define SMALL_MEDIUM
+# ifdef __BORLANDC__
+# define FAR _far
+# else
+# define FAR far
+# endif
+# endif
+#endif
+
+#if defined(WINDOWS) || defined(WIN32)
+ /* If building or using zlib as a DLL, define ZLIB_DLL.
+ * This is not mandatory, but it offers a little performance increase.
+ */
+# ifdef ZLIB_DLL
+# if defined(WIN32) && (!defined(__BORLANDC__) || (__BORLANDC__ >= 0x500))
+# ifdef ZLIB_INTERNAL
+# define ZEXTERN extern __declspec(dllexport)
+# else
+# define ZEXTERN extern __declspec(dllimport)
+# endif
+# endif
+# endif /* ZLIB_DLL */
+ /* If building or using zlib with the WINAPI/WINAPIV calling convention,
+ * define ZLIB_WINAPI.
+ * Caution: the standard ZLIB1.DLL is NOT compiled using ZLIB_WINAPI.
+ */
+# ifdef ZLIB_WINAPI
+# ifdef FAR
+# undef FAR
+# endif
+# include <windows.h>
+ /* No need for _export, use ZLIB.DEF instead. */
+ /* For complete Windows compatibility, use WINAPI, not __stdcall. */
+# define ZEXPORT WINAPI
+# ifdef WIN32
+# define ZEXPORTVA WINAPIV
+# else
+# define ZEXPORTVA FAR CDECL
+# endif
+# endif
+#endif
+
+#if defined (__BEOS__)
+# ifdef ZLIB_DLL
+# ifdef ZLIB_INTERNAL
+# define ZEXPORT __declspec(dllexport)
+# define ZEXPORTVA __declspec(dllexport)
+# else
+# define ZEXPORT __declspec(dllimport)
+# define ZEXPORTVA __declspec(dllimport)
+# endif
+# endif
+#endif
+
+#ifndef ZEXTERN
+# define ZEXTERN extern
+#endif
+#ifndef ZEXPORT
+# define ZEXPORT
+#endif
+#ifndef ZEXPORTVA
+# define ZEXPORTVA
+#endif
+
+#ifndef FAR
+# define FAR
+#endif
+
+#if !defined(__MACTYPES__)
+typedef unsigned char Byte; /* 8 bits */
+#endif
+typedef unsigned int uInt; /* 16 bits or more */
+typedef unsigned long uLong; /* 32 bits or more */
+
+#ifdef SMALL_MEDIUM
+ /* Borland C/C++ and some old MSC versions ignore FAR inside typedef */
+# define Bytef Byte FAR
+#else
+ typedef Byte FAR Bytef;
+#endif
+typedef char FAR charf;
+typedef int FAR intf;
+typedef uInt FAR uIntf;
+typedef uLong FAR uLongf;
+
+#ifdef STDC
+ typedef void const *voidpc;
+ typedef void FAR *voidpf;
+ typedef void *voidp;
+#else
+ typedef Byte const *voidpc;
+ typedef Byte FAR *voidpf;
+ typedef Byte *voidp;
+#endif
+
+#if 0 /* HAVE_UNISTD_H -- this line is updated by ./configure */
+# include <sys/types.h> /* for off_t */
+# include <unistd.h> /* for SEEK_* and off_t */
+# ifdef VMS
+# include <unixio.h> /* for off_t */
+# endif
+# define z_off_t off_t
+#endif
+#ifndef SEEK_SET
+# define SEEK_SET 0 /* Seek from beginning of file. */
+# define SEEK_CUR 1 /* Seek from current position. */
+# define SEEK_END 2 /* Set file pointer to EOF plus "offset" */
+#endif
+#ifndef z_off_t
+# define z_off_t long
+#endif
+
+#if defined(__OS400__)
+#define NO_vsnprintf
+#endif
+
+#if defined(__MVS__)
+# define NO_vsnprintf
+# ifdef FAR
+# undef FAR
+# endif
+#endif
+
+/* MVS linker does not support external names larger than 8 bytes */
+#if defined(__MVS__)
+# pragma map(deflateInit_,"DEIN")
+# pragma map(deflateInit2_,"DEIN2")
+# pragma map(deflateEnd,"DEEND")
+# pragma map(deflateBound,"DEBND")
+# pragma map(inflateInit_,"ININ")
+# pragma map(inflateInit2_,"ININ2")
+# pragma map(inflateEnd,"INEND")
+# pragma map(inflateSync,"INSY")
+# pragma map(inflateSetDictionary,"INSEDI")
+# pragma map(compressBound,"CMBND")
+# pragma map(inflate_table,"INTABL")
+# pragma map(inflate_fast,"INFA")
+# pragma map(inflate_copyright,"INCOPY")
+#endif
+
+#endif /* ZCONF_H */
diff --git a/src/zlib/zconf.in.h b/src/zlib/zconf.in.h
new file mode 100755
index 0000000..e874223
--- /dev/null
+++ b/src/zlib/zconf.in.h
@@ -0,0 +1,323 @@
+/* zconf.h -- configuration of the zlib compression library
+ * Copyright (C) 1995-2003 Jean-loup Gailly.
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* @(#) $ Id$ */
+
+#ifndef ZCONF_H
+#define ZCONF_H
+
+/*
+ * If you *really* need a unique prefix for all types and library functions,
+ * compile with -DZ_PREFIX. The "standard" zlib should be compiled without it.
+ */
+#ifdef Z_PREFIX
+# define deflateInit_ z_deflateInit_
+# define deflate z_deflate
+# define deflateEnd z_deflateEnd
+# define inflateInit_ z_inflateInit_
+# define inflate z_inflate
+# define inflateEnd z_inflateEnd
+# define deflateInit2_ z_deflateInit2_
+# define deflateSetDictionary z_deflateSetDictionary
+# define deflateCopy z_deflateCopy
+# define deflateReset z_deflateReset
+# define deflatePrime z_deflatePrime
+# define deflateParams z_deflateParams
+# define deflateBound z_deflateBound
+# define inflateInit2_ z_inflateInit2_
+# define inflateSetDictionary z_inflateSetDictionary
+# define inflateSync z_inflateSync
+# define inflateSyncPoint z_inflateSyncPoint
+# define inflateCopy z_inflateCopy
+# define inflateReset z_inflateReset
+# define compress z_compress
+# define compress2 z_compress2
+# define compressBound z_compressBound
+# define uncompress z_uncompress
+# define adler32 z_adler32
+# define crc32 z_crc32
+# define get_crc_table z_get_crc_table
+
+# define Byte z_Byte
+# define uInt z_uInt
+# define uLong z_uLong
+# define Bytef z_Bytef
+# define charf z_charf
+# define intf z_intf
+# define uIntf z_uIntf
+# define uLongf z_uLongf
+# define voidpf z_voidpf
+# define voidp z_voidp
+#endif
+
+#if defined(__MSDOS__) && !defined(MSDOS)
+# define MSDOS
+#endif
+#if (defined(OS_2) || defined(__OS2__)) && !defined(OS2)
+# define OS2
+#endif
+#if defined(_WINDOWS) && !defined(WINDOWS)
+# define WINDOWS
+#endif
+#if (defined(_WIN32) || defined(__WIN32__)) && !defined(WIN32)
+# define WIN32
+#endif
+#if (defined(MSDOS) || defined(OS2) || defined(WINDOWS)) && !defined(WIN32)
+# if !defined(__GNUC__) && !defined(__FLAT__) && !defined(__386__)
+# ifndef SYS16BIT
+# define SYS16BIT
+# endif
+# endif
+#endif
+
+/*
+ * Compile with -DMAXSEG_64K if the alloc function cannot allocate more
+ * than 64k bytes at a time (needed on systems with 16-bit int).
+ */
+#ifdef SYS16BIT
+# define MAXSEG_64K
+#endif
+#ifdef MSDOS
+# define UNALIGNED_OK
+#endif
+
+#ifdef __STDC_VERSION__
+# ifndef STDC
+# define STDC
+# endif
+# if __STDC_VERSION__ >= 199901L
+# ifndef STDC99
+# define STDC99
+# endif
+# endif
+#endif
+#if !defined(STDC) && (defined(__STDC__) || defined(__cplusplus))
+# define STDC
+#endif
+#if !defined(STDC) && (defined(__GNUC__) || defined(__BORLANDC__))
+# define STDC
+#endif
+#if !defined(STDC) && (defined(MSDOS) || defined(WINDOWS) || defined(WIN32))
+# define STDC
+#endif
+#if !defined(STDC) && (defined(OS2) || defined(__HOS_AIX__))
+# define STDC
+#endif
+
+#if defined(__OS400__) && !defined(STDC) /* iSeries (formerly AS/400). */
+# define STDC
+#endif
+
+#ifndef STDC
+# ifndef const /* cannot use !defined(STDC) && !defined(const) on Mac */
+# define const /* note: need a more gentle solution here */
+# endif
+#endif
+
+/* Some Mac compilers merge all .h files incorrectly: */
+#if defined(__MWERKS__)||defined(applec)||defined(THINK_C)||defined(__SC__)
+# define NO_DUMMY_DECL
+#endif
+
+/* Maximum value for memLevel in deflateInit2 */
+#ifndef MAX_MEM_LEVEL
+# ifdef MAXSEG_64K
+# define MAX_MEM_LEVEL 8
+# else
+# define MAX_MEM_LEVEL 9
+# endif
+#endif
+
+/* Maximum value for windowBits in deflateInit2 and inflateInit2.
+ * WARNING: reducing MAX_WBITS makes minigzip unable to extract .gz files
+ * created by gzip. (Files created by minigzip can still be extracted by
+ * gzip.)
+ */
+#ifndef MAX_WBITS
+# define MAX_WBITS 15 /* 32K LZ77 window */
+#endif
+
+/* The memory requirements for deflate are (in bytes):
+ (1 << (windowBits+2)) + (1 << (memLevel+9))
+ that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values)
+ plus a few kilobytes for small objects. For example, if you want to reduce
+ the default memory requirements from 256K to 128K, compile with
+ make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7"
+ Of course this will generally degrade compression (there's no free lunch).
+
+ The memory requirements for inflate are (in bytes) 1 << windowBits
+ that is, 32K for windowBits=15 (default value) plus a few kilobytes
+ for small objects.
+*/
+
+ /* Type declarations */
+
+#ifndef OF /* function prototypes */
+# ifdef STDC
+# define OF(args) args
+# else
+# define OF(args) ()
+# endif
+#endif
+
+/* The following definitions for FAR are needed only for MSDOS mixed
+ * model programming (small or medium model with some far allocations).
+ * This was tested only with MSC; for other MSDOS compilers you may have
+ * to define NO_MEMCPY in zutil.h. If you don't need the mixed model,
+ * just define FAR to be empty.
+ */
+#ifdef SYS16BIT
+# if defined(M_I86SM) || defined(M_I86MM)
+ /* MSC small or medium model */
+# define SMALL_MEDIUM
+# ifdef _MSC_VER
+# define FAR _far
+# else
+# define FAR far
+# endif
+# endif
+# if (defined(__SMALL__) || defined(__MEDIUM__))
+ /* Turbo C small or medium model */
+# define SMALL_MEDIUM
+# ifdef __BORLANDC__
+# define FAR _far
+# else
+# define FAR far
+# endif
+# endif
+#endif
+
+#if defined(WINDOWS) || defined(WIN32)
+ /* If building or using zlib as a DLL, define ZLIB_DLL.
+ * This is not mandatory, but it offers a little performance increase.
+ */
+# ifdef ZLIB_DLL
+# if defined(WIN32) && (!defined(__BORLANDC__) || (__BORLANDC__ >= 0x500))
+# ifdef ZLIB_INTERNAL
+# define ZEXTERN extern __declspec(dllexport)
+# else
+# define ZEXTERN extern __declspec(dllimport)
+# endif
+# endif
+# endif /* ZLIB_DLL */
+ /* If building or using zlib with the WINAPI/WINAPIV calling convention,
+ * define ZLIB_WINAPI.
+ * Caution: the standard ZLIB1.DLL is NOT compiled using ZLIB_WINAPI.
+ */
+# ifdef ZLIB_WINAPI
+# ifdef FAR
+# undef FAR
+# endif
+# include <windows.h>
+ /* No need for _export, use ZLIB.DEF instead. */
+ /* For complete Windows compatibility, use WINAPI, not __stdcall. */
+# define ZEXPORT WINAPI
+# ifdef WIN32
+# define ZEXPORTVA WINAPIV
+# else
+# define ZEXPORTVA FAR CDECL
+# endif
+# endif
+#endif
+
+#if defined (__BEOS__)
+# ifdef ZLIB_DLL
+# ifdef ZLIB_INTERNAL
+# define ZEXPORT __declspec(dllexport)
+# define ZEXPORTVA __declspec(dllexport)
+# else
+# define ZEXPORT __declspec(dllimport)
+# define ZEXPORTVA __declspec(dllimport)
+# endif
+# endif
+#endif
+
+#ifndef ZEXTERN
+# define ZEXTERN extern
+#endif
+#ifndef ZEXPORT
+# define ZEXPORT
+#endif
+#ifndef ZEXPORTVA
+# define ZEXPORTVA
+#endif
+
+#ifndef FAR
+# define FAR
+#endif
+
+#if !defined(__MACTYPES__)
+typedef unsigned char Byte; /* 8 bits */
+#endif
+typedef unsigned int uInt; /* 16 bits or more */
+typedef unsigned long uLong; /* 32 bits or more */
+
+#ifdef SMALL_MEDIUM
+ /* Borland C/C++ and some old MSC versions ignore FAR inside typedef */
+# define Bytef Byte FAR
+#else
+ typedef Byte FAR Bytef;
+#endif
+typedef char FAR charf;
+typedef int FAR intf;
+typedef uInt FAR uIntf;
+typedef uLong FAR uLongf;
+
+#ifdef STDC
+ typedef void const *voidpc;
+ typedef void FAR *voidpf;
+ typedef void *voidp;
+#else
+ typedef Byte const *voidpc;
+ typedef Byte FAR *voidpf;
+ typedef Byte *voidp;
+#endif
+
+#if 0 /* HAVE_UNISTD_H -- this line is updated by ./configure */
+# include <sys/types.h> /* for off_t */
+# include <unistd.h> /* for SEEK_* and off_t */
+# ifdef VMS
+# include <unixio.h> /* for off_t */
+# endif
+# define z_off_t off_t
+#endif
+#ifndef SEEK_SET
+# define SEEK_SET 0 /* Seek from beginning of file. */
+# define SEEK_CUR 1 /* Seek from current position. */
+# define SEEK_END 2 /* Set file pointer to EOF plus "offset" */
+#endif
+#ifndef z_off_t
+# define z_off_t long
+#endif
+
+#if defined(__OS400__)
+#define NO_vsnprintf
+#endif
+
+#if defined(__MVS__)
+# define NO_vsnprintf
+# ifdef FAR
+# undef FAR
+# endif
+#endif
+
+/* MVS linker does not support external names larger than 8 bytes */
+#if defined(__MVS__)
+# pragma map(deflateInit_,"DEIN")
+# pragma map(deflateInit2_,"DEIN2")
+# pragma map(deflateEnd,"DEEND")
+# pragma map(deflateBound,"DEBND")
+# pragma map(inflateInit_,"ININ")
+# pragma map(inflateInit2_,"ININ2")
+# pragma map(inflateEnd,"INEND")
+# pragma map(inflateSync,"INSY")
+# pragma map(inflateSetDictionary,"INSEDI")
+# pragma map(compressBound,"CMBND")
+# pragma map(inflate_table,"INTABL")
+# pragma map(inflate_fast,"INFA")
+# pragma map(inflate_copyright,"INCOPY")
+#endif
+
+#endif /* ZCONF_H */
diff --git a/src/zlib/zlib.3 b/src/zlib/zlib.3
new file mode 100755
index 0000000..8900984
--- /dev/null
+++ b/src/zlib/zlib.3
@@ -0,0 +1,159 @@
+.TH ZLIB 3 "17 November 2003"
+.SH NAME
+zlib \- compression/decompression library
+.SH SYNOPSIS
+[see
+.I zlib.h
+for full description]
+.SH DESCRIPTION
+The
+.I zlib
+library is a general purpose data compression library.
+The code is thread safe.
+It provides in-memory compression and decompression functions,
+including integrity checks of the uncompressed data.
+This version of the library supports only one compression method (deflation)
+but other algorithms will be added later
+and will have the same stream interface.
+.LP
+Compression can be done in a single step if the buffers are large enough
+(for example if an input file is mmap'ed),
+or can be done by repeated calls of the compression function.
+In the latter case,
+the application must provide more input and/or consume the output
+(providing more output space) before each call.
+.LP
+The library also supports reading and writing files in
+.IR gzip (1)
+(.gz) format
+with an interface similar to that of stdio.
+.LP
+The library does not install any signal handler.
+The decoder checks the consistency of the compressed data,
+so the library should never crash even in case of corrupted input.
+.LP
+All functions of the compression library are documented in the file
+.IR zlib.h .
+The distribution source includes examples of use of the library
+in the files
+.I example.c
+and
+.IR minigzip.c .
+.LP
+Changes to this version are documented in the file
+.I ChangeLog
+that accompanies the source,
+and are concerned primarily with bug fixes and portability enhancements.
+.LP
+A Java implementation of
+.I zlib
+is available in the Java Development Kit 1.1:
+.IP
+http://www.javasoft.com/products/JDK/1.1/docs/api/Package-java.util.zip.html
+.LP
+A Perl interface to
+.IR zlib ,
+written by Paul Marquess (pmqs@cpan.org),
+is available at CPAN (Comprehensive Perl Archive Network) sites,
+including:
+.IP
+http://www.cpan.org/modules/by-module/Compress/
+.LP
+A Python interface to
+.IR zlib ,
+written by A.M. Kuchling (amk@magnet.com),
+is available in Python 1.5 and later versions:
+.IP
+http://www.python.org/doc/lib/module-zlib.html
+.LP
+A
+.I zlib
+binding for
+.IR tcl (1),
+written by Andreas Kupries (a.kupries@westend.com),
+is availlable at:
+.IP
+http://www.westend.com/~kupries/doc/trf/man/man.html
+.LP
+An experimental package to read and write files in .zip format,
+written on top of
+.I zlib
+by Gilles Vollant (info@winimage.com),
+is available at:
+.IP
+http://www.winimage.com/zLibDll/unzip.html
+and also in the
+.I contrib/minizip
+directory of the main
+.I zlib
+web site.
+.SH "SEE ALSO"
+The
+.I zlib
+web site can be found at either of these locations:
+.IP
+http://www.zlib.org
+.br
+http://www.gzip.org/zlib/
+.LP
+The data format used by the zlib library is described by RFC
+(Request for Comments) 1950 to 1952 in the files:
+.IP
+http://www.ietf.org/rfc/rfc1950.txt (concerning zlib format)
+.br
+http://www.ietf.org/rfc/rfc1951.txt (concerning deflate format)
+.br
+http://www.ietf.org/rfc/rfc1952.txt (concerning gzip format)
+.LP
+These documents are also available in other formats from:
+.IP
+ftp://ftp.uu.net/graphics/png/documents/zlib/zdoc-index.html
+.LP
+Mark Nelson (markn@ieee.org) wrote an article about
+.I zlib
+for the Jan. 1997 issue of Dr. Dobb's Journal;
+a copy of the article is available at:
+.IP
+http://dogma.net/markn/articles/zlibtool/zlibtool.htm
+.SH "REPORTING PROBLEMS"
+Before reporting a problem,
+please check the
+.I zlib
+web site to verify that you have the latest version of
+.IR zlib ;
+otherwise,
+obtain the latest version and see if the problem still exists.
+Please read the
+.I zlib
+FAQ at:
+.IP
+http://www.gzip.org/zlib/zlib_faq.html
+.LP
+before asking for help.
+Send questions and/or comments to zlib@gzip.org,
+or (for the Windows DLL version) to Gilles Vollant (info@winimage.com).
+.SH AUTHORS
+Version 1.2.1
+Copyright (C) 1995-2003 Jean-loup Gailly (jloup@gzip.org)
+and Mark Adler (madler@alumni.caltech.edu).
+.LP
+This software is provided "as-is,"
+without any express or implied warranty.
+In no event will the authors be held liable for any damages
+arising from the use of this software.
+See the distribution directory with respect to requirements
+governing redistribution.
+The deflate format used by
+.I zlib
+was defined by Phil Katz.
+The deflate and
+.I zlib
+specifications were written by L. Peter Deutsch.
+Thanks to all the people who reported problems and suggested various
+improvements in
+.IR zlib ;
+who are too numerous to cite here.
+.LP
+UNIX manual page by R. P. C. Rodgers,
+U.S. National Library of Medicine (rodgers@nlm.nih.gov).
+.\" end of man page
diff --git a/src/zlib/zlib.h b/src/zlib/zlib.h
new file mode 100755
index 0000000..92edf96
--- /dev/null
+++ b/src/zlib/zlib.h
@@ -0,0 +1,1200 @@
+/* zlib.h -- interface of the 'zlib' general purpose compression library
+ version 1.2.1, November 17th, 2003
+
+ Copyright (C) 1995-2003 Jean-loup Gailly and Mark Adler
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+ 3. This notice may not be removed or altered from any source distribution.
+
+ Jean-loup Gailly Mark Adler
+ jloup@gzip.org madler@alumni.caltech.edu
+
+
+ The data format used by the zlib library is described by RFCs (Request for
+ Comments) 1950 to 1952 in the files http://www.ietf.org/rfc/rfc1950.txt
+ (zlib format), rfc1951.txt (deflate format) and rfc1952.txt (gzip format).
+*/
+
+#ifndef ZLIB_H
+#define ZLIB_H
+
+#include "zconf.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define ZLIB_VERSION "1.2.1"
+#define ZLIB_VERNUM 0x1210
+
+/*
+ The 'zlib' compression library provides in-memory compression and
+ decompression functions, including integrity checks of the uncompressed
+ data. This version of the library supports only one compression method
+ (deflation) but other algorithms will be added later and will have the same
+ stream interface.
+
+ Compression can be done in a single step if the buffers are large
+ enough (for example if an input file is mmap'ed), or can be done by
+ repeated calls of the compression function. In the latter case, the
+ application must provide more input and/or consume the output
+ (providing more output space) before each call.
+
+ The compressed data format used by the in-memory functions is the zlib
+ format, which is a zlib wrapper documented in RFC 1950, wrapped around a
+ deflate stream, which is itself documented in RFC 1951.
+
+ The library also supports reading and writing files in gzip (.gz) format
+ with an interface similar to that of stdio using the functions that start
+ with "gz". The gzip format is different from the zlib format. gzip is a
+ gzip wrapper, documented in RFC 1952, wrapped around a deflate stream.
+
+ The zlib format was designed to be compact and fast for use in memory
+ and on communications channels. The gzip format was designed for single-
+ file compression on file systems, has a larger header than zlib to maintain
+ directory information, and uses a different, slower check method than zlib.
+
+ This library does not provide any functions to write gzip files in memory.
+ However such functions could be easily written using zlib's deflate function,
+ the documentation in the gzip RFC, and the examples in gzio.c.
+
+ The library does not install any signal handler. The decoder checks
+ the consistency of the compressed data, so the library should never
+ crash even in case of corrupted input.
+*/
+
+typedef voidpf (*alloc_func) OF((voidpf opaque, uInt items, uInt size));
+typedef void (*free_func) OF((voidpf opaque, voidpf address));
+
+struct internal_state;
+
+typedef struct z_stream_s {
+ Bytef *next_in; /* next input byte */
+ uInt avail_in; /* number of bytes available at next_in */
+ uLong total_in; /* total nb of input bytes read so far */
+
+ Bytef *next_out; /* next output byte should be put there */
+ uInt avail_out; /* remaining free space at next_out */
+ uLong total_out; /* total nb of bytes output so far */
+
+ char *msg; /* last error message, NULL if no error */
+ struct internal_state FAR *state; /* not visible by applications */
+
+ alloc_func zalloc; /* used to allocate the internal state */
+ free_func zfree; /* used to free the internal state */
+ voidpf opaque; /* private data object passed to zalloc and zfree */
+
+ int data_type; /* best guess about the data type: ascii or binary */
+ uLong adler; /* adler32 value of the uncompressed data */
+ uLong reserved; /* reserved for future use */
+} z_stream;
+
+typedef z_stream FAR *z_streamp;
+
+/*
+ The application must update next_in and avail_in when avail_in has
+ dropped to zero. It must update next_out and avail_out when avail_out
+ has dropped to zero. The application must initialize zalloc, zfree and
+ opaque before calling the init function. All other fields are set by the
+ compression library and must not be updated by the application.
+
+ The opaque value provided by the application will be passed as the first
+ parameter for calls of zalloc and zfree. This can be useful for custom
+ memory management. The compression library attaches no meaning to the
+ opaque value.
+
+ zalloc must return Z_NULL if there is not enough memory for the object.
+ If zlib is used in a multi-threaded application, zalloc and zfree must be
+ thread safe.
+
+ On 16-bit systems, the functions zalloc and zfree must be able to allocate
+ exactly 65536 bytes, but will not be required to allocate more than this
+ if the symbol MAXSEG_64K is defined (see zconf.h). WARNING: On MSDOS,
+ pointers returned by zalloc for objects of exactly 65536 bytes *must*
+ have their offset normalized to zero. The default allocation function
+ provided by this library ensures this (see zutil.c). To reduce memory
+ requirements and avoid any allocation of 64K objects, at the expense of
+ compression ratio, compile the library with -DMAX_WBITS=14 (see zconf.h).
+
+ The fields total_in and total_out can be used for statistics or
+ progress reports. After compression, total_in holds the total size of
+ the uncompressed data and may be saved for use in the decompressor
+ (particularly if the decompressor wants to decompress everything in
+ a single step).
+*/
+
+ /* constants */
+
+#define Z_NO_FLUSH 0
+#define Z_PARTIAL_FLUSH 1 /* will be removed, use Z_SYNC_FLUSH instead */
+#define Z_SYNC_FLUSH 2
+#define Z_FULL_FLUSH 3
+#define Z_FINISH 4
+#define Z_BLOCK 5
+/* Allowed flush values; see deflate() and inflate() below for details */
+
+#define Z_OK 0
+#define Z_STREAM_END 1
+#define Z_NEED_DICT 2
+#define Z_ERRNO (-1)
+#define Z_STREAM_ERROR (-2)
+#define Z_DATA_ERROR (-3)
+#define Z_MEM_ERROR (-4)
+#define Z_BUF_ERROR (-5)
+#define Z_VERSION_ERROR (-6)
+/* Return codes for the compression/decompression functions. Negative
+ * values are errors, positive values are used for special but normal events.
+ */
+
+#define Z_NO_COMPRESSION 0
+#define Z_BEST_SPEED 1
+#define Z_BEST_COMPRESSION 9
+#define Z_DEFAULT_COMPRESSION (-1)
+/* compression levels */
+
+#define Z_FILTERED 1
+#define Z_HUFFMAN_ONLY 2
+#define Z_RLE 3
+#define Z_DEFAULT_STRATEGY 0
+/* compression strategy; see deflateInit2() below for details */
+
+#define Z_BINARY 0
+#define Z_ASCII 1
+#define Z_UNKNOWN 2
+/* Possible values of the data_type field (though see inflate()) */
+
+#define Z_DEFLATED 8
+/* The deflate compression method (the only one supported in this version) */
+
+#define Z_NULL 0 /* for initializing zalloc, zfree, opaque */
+
+#define zlib_version zlibVersion()
+/* for compatibility with versions < 1.0.2 */
+
+ /* basic functions */
+
+ZEXTERN const char * ZEXPORT zlibVersion OF((void));
+/* The application can compare zlibVersion and ZLIB_VERSION for consistency.
+ If the first character differs, the library code actually used is
+ not compatible with the zlib.h header file used by the application.
+ This check is automatically made by deflateInit and inflateInit.
+ */
+
+/*
+ZEXTERN int ZEXPORT deflateInit OF((z_streamp strm, int level));
+
+ Initializes the internal stream state for compression. The fields
+ zalloc, zfree and opaque must be initialized before by the caller.
+ If zalloc and zfree are set to Z_NULL, deflateInit updates them to
+ use default allocation functions.
+
+ The compression level must be Z_DEFAULT_COMPRESSION, or between 0 and 9:
+ 1 gives best speed, 9 gives best compression, 0 gives no compression at
+ all (the input data is simply copied a block at a time).
+ Z_DEFAULT_COMPRESSION requests a default compromise between speed and
+ compression (currently equivalent to level 6).
+
+ deflateInit returns Z_OK if success, Z_MEM_ERROR if there was not
+ enough memory, Z_STREAM_ERROR if level is not a valid compression level,
+ Z_VERSION_ERROR if the zlib library version (zlib_version) is incompatible
+ with the version assumed by the caller (ZLIB_VERSION).
+ msg is set to null if there is no error message. deflateInit does not
+ perform any compression: this will be done by deflate().
+*/
+
+
+ZEXTERN int ZEXPORT deflate OF((z_streamp strm, int flush));
+/*
+ deflate compresses as much data as possible, and stops when the input
+ buffer becomes empty or the output buffer becomes full. It may introduce some
+ output latency (reading input without producing any output) except when
+ forced to flush.
+
+ The detailed semantics are as follows. deflate performs one or both of the
+ following actions:
+
+ - Compress more input starting at next_in and update next_in and avail_in
+ accordingly. If not all input can be processed (because there is not
+ enough room in the output buffer), next_in and avail_in are updated and
+ processing will resume at this point for the next call of deflate().
+
+ - Provide more output starting at next_out and update next_out and avail_out
+ accordingly. This action is forced if the parameter flush is non zero.
+ Forcing flush frequently degrades the compression ratio, so this parameter
+ should be set only when necessary (in interactive applications).
+ Some output may be provided even if flush is not set.
+
+ Before the call of deflate(), the application should ensure that at least
+ one of the actions is possible, by providing more input and/or consuming
+ more output, and updating avail_in or avail_out accordingly; avail_out
+ should never be zero before the call. The application can consume the
+ compressed output when it wants, for example when the output buffer is full
+ (avail_out == 0), or after each call of deflate(). If deflate returns Z_OK
+ and with zero avail_out, it must be called again after making room in the
+ output buffer because there might be more output pending.
+
+ If the parameter flush is set to Z_SYNC_FLUSH, all pending output is
+ flushed to the output buffer and the output is aligned on a byte boundary, so
+ that the decompressor can get all input data available so far. (In particular
+ avail_in is zero after the call if enough output space has been provided
+ before the call.) Flushing may degrade compression for some compression
+ algorithms and so it should be used only when necessary.
+
+ If flush is set to Z_FULL_FLUSH, all output is flushed as with
+ Z_SYNC_FLUSH, and the compression state is reset so that decompression can
+ restart from this point if previous compressed data has been damaged or if
+ random access is desired. Using Z_FULL_FLUSH too often can seriously degrade
+ the compression.
+
+ If deflate returns with avail_out == 0, this function must be called again
+ with the same value of the flush parameter and more output space (updated
+ avail_out), until the flush is complete (deflate returns with non-zero
+ avail_out). In the case of a Z_FULL_FLUSH or Z_SYNC_FLUSH, make sure that
+ avail_out is greater than six to avoid repeated flush markers due to
+ avail_out == 0 on return.
+
+ If the parameter flush is set to Z_FINISH, pending input is processed,
+ pending output is flushed and deflate returns with Z_STREAM_END if there
+ was enough output space; if deflate returns with Z_OK, this function must be
+ called again with Z_FINISH and more output space (updated avail_out) but no
+ more input data, until it returns with Z_STREAM_END or an error. After
+ deflate has returned Z_STREAM_END, the only possible operations on the
+ stream are deflateReset or deflateEnd.
+
+ Z_FINISH can be used immediately after deflateInit if all the compression
+ is to be done in a single step. In this case, avail_out must be at least
+ the value returned by deflateBound (see below). If deflate does not return
+ Z_STREAM_END, then it must be called again as described above.
+
+ deflate() sets strm->adler to the adler32 checksum of all input read
+ so far (that is, total_in bytes).
+
+ deflate() may update data_type if it can make a good guess about
+ the input data type (Z_ASCII or Z_BINARY). In doubt, the data is considered
+ binary. This field is only for information purposes and does not affect
+ the compression algorithm in any manner.
+
+ deflate() returns Z_OK if some progress has been made (more input
+ processed or more output produced), Z_STREAM_END if all input has been
+ consumed and all output has been produced (only when flush is set to
+ Z_FINISH), Z_STREAM_ERROR if the stream state was inconsistent (for example
+ if next_in or next_out was NULL), Z_BUF_ERROR if no progress is possible
+ (for example avail_in or avail_out was zero). Note that Z_BUF_ERROR is not
+ fatal, and deflate() can be called again with more input and more output
+ space to continue compressing.
+*/
+
+
+ZEXTERN int ZEXPORT deflateEnd OF((z_streamp strm));
+/*
+ All dynamically allocated data structures for this stream are freed.
+ This function discards any unprocessed input and does not flush any
+ pending output.
+
+ deflateEnd returns Z_OK if success, Z_STREAM_ERROR if the
+ stream state was inconsistent, Z_DATA_ERROR if the stream was freed
+ prematurely (some input or output was discarded). In the error case,
+ msg may be set but then points to a static string (which must not be
+ deallocated).
+*/
+
+
+/*
+ZEXTERN int ZEXPORT inflateInit OF((z_streamp strm));
+
+ Initializes the internal stream state for decompression. The fields
+ next_in, avail_in, zalloc, zfree and opaque must be initialized before by
+ the caller. If next_in is not Z_NULL and avail_in is large enough (the exact
+ value depends on the compression method), inflateInit determines the
+ compression method from the zlib header and allocates all data structures
+ accordingly; otherwise the allocation will be deferred to the first call of
+ inflate. If zalloc and zfree are set to Z_NULL, inflateInit updates them to
+ use default allocation functions.
+
+ inflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough
+ memory, Z_VERSION_ERROR if the zlib library version is incompatible with the
+ version assumed by the caller. msg is set to null if there is no error
+ message. inflateInit does not perform any decompression apart from reading
+ the zlib header if present: this will be done by inflate(). (So next_in and
+ avail_in may be modified, but next_out and avail_out are unchanged.)
+*/
+
+
+ZEXTERN int ZEXPORT inflate OF((z_streamp strm, int flush));
+/*
+ inflate decompresses as much data as possible, and stops when the input
+ buffer becomes empty or the output buffer becomes full. It may introduce
+ some output latency (reading input without producing any output) except when
+ forced to flush.
+
+ The detailed semantics are as follows. inflate performs one or both of the
+ following actions:
+
+ - Decompress more input starting at next_in and update next_in and avail_in
+ accordingly. If not all input can be processed (because there is not
+ enough room in the output buffer), next_in is updated and processing
+ will resume at this point for the next call of inflate().
+
+ - Provide more output starting at next_out and update next_out and avail_out
+ accordingly. inflate() provides as much output as possible, until there
+ is no more input data or no more space in the output buffer (see below
+ about the flush parameter).
+
+ Before the call of inflate(), the application should ensure that at least
+ one of the actions is possible, by providing more input and/or consuming
+ more output, and updating the next_* and avail_* values accordingly.
+ The application can consume the uncompressed output when it wants, for
+ example when the output buffer is full (avail_out == 0), or after each
+ call of inflate(). If inflate returns Z_OK and with zero avail_out, it
+ must be called again after making room in the output buffer because there
+ might be more output pending.
+
+ The flush parameter of inflate() can be Z_NO_FLUSH, Z_SYNC_FLUSH,
+ Z_FINISH, or Z_BLOCK. Z_SYNC_FLUSH requests that inflate() flush as much
+ output as possible to the output buffer. Z_BLOCK requests that inflate() stop
+ if and when it get to the next deflate block boundary. When decoding the zlib
+ or gzip format, this will cause inflate() to return immediately after the
+ header and before the first block. When doing a raw inflate, inflate() will
+ go ahead and process the first block, and will return when it gets to the end
+ of that block, or when it runs out of data.
+
+ The Z_BLOCK option assists in appending to or combining deflate streams.
+ Also to assist in this, on return inflate() will set strm->data_type to the
+ number of unused bits in the last byte taken from strm->next_in, plus 64
+ if inflate() is currently decoding the last block in the deflate stream,
+ plus 128 if inflate() returned immediately after decoding an end-of-block
+ code or decoding the complete header up to just before the first byte of the
+ deflate stream. The end-of-block will not be indicated until all of the
+ uncompressed data from that block has been written to strm->next_out. The
+ number of unused bits may in general be greater than seven, except when
+ bit 7 of data_type is set, in which case the number of unused bits will be
+ less than eight.
+
+ inflate() should normally be called until it returns Z_STREAM_END or an
+ error. However if all decompression is to be performed in a single step
+ (a single call of inflate), the parameter flush should be set to
+ Z_FINISH. In this case all pending input is processed and all pending
+ output is flushed; avail_out must be large enough to hold all the
+ uncompressed data. (The size of the uncompressed data may have been saved
+ by the compressor for this purpose.) The next operation on this stream must
+ be inflateEnd to deallocate the decompression state. The use of Z_FINISH
+ is never required, but can be used to inform inflate that a faster approach
+ may be used for the single inflate() call.
+
+ In this implementation, inflate() always flushes as much output as
+ possible to the output buffer, and always uses the faster approach on the
+ first call. So the only effect of the flush parameter in this implementation
+ is on the return value of inflate(), as noted below, or when it returns early
+ because Z_BLOCK is used.
+
+ If a preset dictionary is needed after this call (see inflateSetDictionary
+ below), inflate sets strm-adler to the adler32 checksum of the dictionary
+ chosen by the compressor and returns Z_NEED_DICT; otherwise it sets
+ strm->adler to the adler32 checksum of all output produced so far (that is,
+ total_out bytes) and returns Z_OK, Z_STREAM_END or an error code as described
+ below. At the end of the stream, inflate() checks that its computed adler32
+ checksum is equal to that saved by the compressor and returns Z_STREAM_END
+ only if the checksum is correct.
+
+ inflate() will decompress and check either zlib-wrapped or gzip-wrapped
+ deflate data. The header type is detected automatically. Any information
+ contained in the gzip header is not retained, so applications that need that
+ information should instead use raw inflate, see inflateInit2() below, or
+ inflateBack() and perform their own processing of the gzip header and
+ trailer.
+
+ inflate() returns Z_OK if some progress has been made (more input processed
+ or more output produced), Z_STREAM_END if the end of the compressed data has
+ been reached and all uncompressed output has been produced, Z_NEED_DICT if a
+ preset dictionary is needed at this point, Z_DATA_ERROR if the input data was
+ corrupted (input stream not conforming to the zlib format or incorrect check
+ value), Z_STREAM_ERROR if the stream structure was inconsistent (for example
+ if next_in or next_out was NULL), Z_MEM_ERROR if there was not enough memory,
+ Z_BUF_ERROR if no progress is possible or if there was not enough room in the
+ output buffer when Z_FINISH is used. Note that Z_BUF_ERROR is not fatal, and
+ inflate() can be called again with more input and more output space to
+ continue decompressing. If Z_DATA_ERROR is returned, the application may then
+ call inflateSync() to look for a good compression block if a partial recovery
+ of the data is desired.
+*/
+
+
+ZEXTERN int ZEXPORT inflateEnd OF((z_streamp strm));
+/*
+ All dynamically allocated data structures for this stream are freed.
+ This function discards any unprocessed input and does not flush any
+ pending output.
+
+ inflateEnd returns Z_OK if success, Z_STREAM_ERROR if the stream state
+ was inconsistent. In the error case, msg may be set but then points to a
+ static string (which must not be deallocated).
+*/
+
+ /* Advanced functions */
+
+/*
+ The following functions are needed only in some special applications.
+*/
+
+/*
+ZEXTERN int ZEXPORT deflateInit2 OF((z_streamp strm,
+ int level,
+ int method,
+ int windowBits,
+ int memLevel,
+ int strategy));
+
+ This is another version of deflateInit with more compression options. The
+ fields next_in, zalloc, zfree and opaque must be initialized before by
+ the caller.
+
+ The method parameter is the compression method. It must be Z_DEFLATED in
+ this version of the library.
+
+ The windowBits parameter is the base two logarithm of the window size
+ (the size of the history buffer). It should be in the range 8..15 for this
+ version of the library. Larger values of this parameter result in better
+ compression at the expense of memory usage. The default value is 15 if
+ deflateInit is used instead.
+
+ windowBits can also be -8..-15 for raw deflate. In this case, -windowBits
+ determines the window size. deflate() will then generate raw deflate data
+ with no zlib header or trailer, and will not compute an adler32 check value.
+
+ windowBits can also be greater than 15 for optional gzip encoding. Add
+ 16 to windowBits to write a simple gzip header and trailer around the
+ compressed data instead of a zlib wrapper. The gzip header will have no
+ file name, no extra data, no comment, no modification time (set to zero),
+ no header crc, and the operating system will be set to 255 (unknown).
+
+ The memLevel parameter specifies how much memory should be allocated
+ for the internal compression state. memLevel=1 uses minimum memory but
+ is slow and reduces compression ratio; memLevel=9 uses maximum memory
+ for optimal speed. The default value is 8. See zconf.h for total memory
+ usage as a function of windowBits and memLevel.
+
+ The strategy parameter is used to tune the compression algorithm. Use the
+ value Z_DEFAULT_STRATEGY for normal data, Z_FILTERED for data produced by a
+ filter (or predictor), Z_HUFFMAN_ONLY to force Huffman encoding only (no
+ string match), or Z_RLE to limit match distances to one (run-length
+ encoding). Filtered data consists mostly of small values with a somewhat
+ random distribution. In this case, the compression algorithm is tuned to
+ compress them better. The effect of Z_FILTERED is to force more Huffman
+ coding and less string matching; it is somewhat intermediate between
+ Z_DEFAULT and Z_HUFFMAN_ONLY. Z_RLE is designed to be almost as fast as
+ Z_HUFFMAN_ONLY, but give better compression for PNG image data. The strategy
+ parameter only affects the compression ratio but not the correctness of the
+ compressed output even if it is not set appropriately.
+
+ deflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough
+ memory, Z_STREAM_ERROR if a parameter is invalid (such as an invalid
+ method). msg is set to null if there is no error message. deflateInit2 does
+ not perform any compression: this will be done by deflate().
+*/
+
+ZEXTERN int ZEXPORT deflateSetDictionary OF((z_streamp strm,
+ const Bytef *dictionary,
+ uInt dictLength));
+/*
+ Initializes the compression dictionary from the given byte sequence
+ without producing any compressed output. This function must be called
+ immediately after deflateInit, deflateInit2 or deflateReset, before any
+ call of deflate. The compressor and decompressor must use exactly the same
+ dictionary (see inflateSetDictionary).
+
+ The dictionary should consist of strings (byte sequences) that are likely
+ to be encountered later in the data to be compressed, with the most commonly
+ used strings preferably put towards the end of the dictionary. Using a
+ dictionary is most useful when the data to be compressed is short and can be
+ predicted with good accuracy; the data can then be compressed better than
+ with the default empty dictionary.
+
+ Depending on the size of the compression data structures selected by
+ deflateInit or deflateInit2, a part of the dictionary may in effect be
+ discarded, for example if the dictionary is larger than the window size in
+ deflate or deflate2. Thus the strings most likely to be useful should be
+ put at the end of the dictionary, not at the front.
+
+ Upon return of this function, strm->adler is set to the adler32 value
+ of the dictionary; the decompressor may later use this value to determine
+ which dictionary has been used by the compressor. (The adler32 value
+ applies to the whole dictionary even if only a subset of the dictionary is
+ actually used by the compressor.) If a raw deflate was requested, then the
+ adler32 value is not computed and strm->adler is not set.
+
+ deflateSetDictionary returns Z_OK if success, or Z_STREAM_ERROR if a
+ parameter is invalid (such as NULL dictionary) or the stream state is
+ inconsistent (for example if deflate has already been called for this stream
+ or if the compression method is bsort). deflateSetDictionary does not
+ perform any compression: this will be done by deflate().
+*/
+
+ZEXTERN int ZEXPORT deflateCopy OF((z_streamp dest,
+ z_streamp source));
+/*
+ Sets the destination stream as a complete copy of the source stream.
+
+ This function can be useful when several compression strategies will be
+ tried, for example when there are several ways of pre-processing the input
+ data with a filter. The streams that will be discarded should then be freed
+ by calling deflateEnd. Note that deflateCopy duplicates the internal
+ compression state which can be quite large, so this strategy is slow and
+ can consume lots of memory.
+
+ deflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not
+ enough memory, Z_STREAM_ERROR if the source stream state was inconsistent
+ (such as zalloc being NULL). msg is left unchanged in both source and
+ destination.
+*/
+
+ZEXTERN int ZEXPORT deflateReset OF((z_streamp strm));
+/*
+ This function is equivalent to deflateEnd followed by deflateInit,
+ but does not free and reallocate all the internal compression state.
+ The stream will keep the same compression level and any other attributes
+ that may have been set by deflateInit2.
+
+ deflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source
+ stream state was inconsistent (such as zalloc or state being NULL).
+*/
+
+ZEXTERN int ZEXPORT deflateParams OF((z_streamp strm,
+ int level,
+ int strategy));
+/*
+ Dynamically update the compression level and compression strategy. The
+ interpretation of level and strategy is as in deflateInit2. This can be
+ used to switch between compression and straight copy of the input data, or
+ to switch to a different kind of input data requiring a different
+ strategy. If the compression level is changed, the input available so far
+ is compressed with the old level (and may be flushed); the new level will
+ take effect only at the next call of deflate().
+
+ Before the call of deflateParams, the stream state must be set as for
+ a call of deflate(), since the currently available input may have to
+ be compressed and flushed. In particular, strm->avail_out must be non-zero.
+
+ deflateParams returns Z_OK if success, Z_STREAM_ERROR if the source
+ stream state was inconsistent or if a parameter was invalid, Z_BUF_ERROR
+ if strm->avail_out was zero.
+*/
+
+ZEXTERN uLong ZEXPORT deflateBound OF((z_streamp strm,
+ uLong sourceLen));
+/*
+ deflateBound() returns an upper bound on the compressed size after
+ deflation of sourceLen bytes. It must be called after deflateInit()
+ or deflateInit2(). This would be used to allocate an output buffer
+ for deflation in a single pass, and so would be called before deflate().
+*/
+
+ZEXTERN int ZEXPORT deflatePrime OF((z_streamp strm,
+ int bits,
+ int value));
+/*
+ deflatePrime() inserts bits in the deflate output stream. The intent
+ is that this function is used to start off the deflate output with the
+ bits leftover from a previous deflate stream when appending to it. As such,
+ this function can only be used for raw deflate, and must be used before the
+ first deflate() call after a deflateInit2() or deflateReset(). bits must be
+ less than or equal to 16, and that many of the least significant bits of
+ value will be inserted in the output.
+
+ deflatePrime returns Z_OK if success, or Z_STREAM_ERROR if the source
+ stream state was inconsistent.
+*/
+
+/*
+ZEXTERN int ZEXPORT inflateInit2 OF((z_streamp strm,
+ int windowBits));
+
+ This is another version of inflateInit with an extra parameter. The
+ fields next_in, avail_in, zalloc, zfree and opaque must be initialized
+ before by the caller.
+
+ The windowBits parameter is the base two logarithm of the maximum window
+ size (the size of the history buffer). It should be in the range 8..15 for
+ this version of the library. The default value is 15 if inflateInit is used
+ instead. windowBits must be greater than or equal to the windowBits value
+ provided to deflateInit2() while compressing, or it must be equal to 15 if
+ deflateInit2() was not used. If a compressed stream with a larger window
+ size is given as input, inflate() will return with the error code
+ Z_DATA_ERROR instead of trying to allocate a larger window.
+
+ windowBits can also be -8..-15 for raw inflate. In this case, -windowBits
+ determines the window size. inflate() will then process raw deflate data,
+ not looking for a zlib or gzip header, not generating a check value, and not
+ looking for any check values for comparison at the end of the stream. This
+ is for use with other formats that use the deflate compressed data format
+ such as zip. Those formats provide their own check values. If a custom
+ format is developed using the raw deflate format for compressed data, it is
+ recommended that a check value such as an adler32 or a crc32 be applied to
+ the uncompressed data as is done in the zlib, gzip, and zip formats. For
+ most applications, the zlib format should be used as is. Note that comments
+ above on the use in deflateInit2() applies to the magnitude of windowBits.
+
+ windowBits can also be greater than 15 for optional gzip decoding. Add
+ 32 to windowBits to enable zlib and gzip decoding with automatic header
+ detection, or add 16 to decode only the gzip format (the zlib format will
+ return a Z_DATA_ERROR).
+
+ inflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough
+ memory, Z_STREAM_ERROR if a parameter is invalid (such as a negative
+ memLevel). msg is set to null if there is no error message. inflateInit2
+ does not perform any decompression apart from reading the zlib header if
+ present: this will be done by inflate(). (So next_in and avail_in may be
+ modified, but next_out and avail_out are unchanged.)
+*/
+
+ZEXTERN int ZEXPORT inflateSetDictionary OF((z_streamp strm,
+ const Bytef *dictionary,
+ uInt dictLength));
+/*
+ Initializes the decompression dictionary from the given uncompressed byte
+ sequence. This function must be called immediately after a call of inflate
+ if this call returned Z_NEED_DICT. The dictionary chosen by the compressor
+ can be determined from the adler32 value returned by this call of
+ inflate. The compressor and decompressor must use exactly the same
+ dictionary (see deflateSetDictionary).
+
+ inflateSetDictionary returns Z_OK if success, Z_STREAM_ERROR if a
+ parameter is invalid (such as NULL dictionary) or the stream state is
+ inconsistent, Z_DATA_ERROR if the given dictionary doesn't match the
+ expected one (incorrect adler32 value). inflateSetDictionary does not
+ perform any decompression: this will be done by subsequent calls of
+ inflate().
+*/
+
+ZEXTERN int ZEXPORT inflateSync OF((z_streamp strm));
+/*
+ Skips invalid compressed data until a full flush point (see above the
+ description of deflate with Z_FULL_FLUSH) can be found, or until all
+ available input is skipped. No output is provided.
+
+ inflateSync returns Z_OK if a full flush point has been found, Z_BUF_ERROR
+ if no more input was provided, Z_DATA_ERROR if no flush point has been found,
+ or Z_STREAM_ERROR if the stream structure was inconsistent. In the success
+ case, the application may save the current current value of total_in which
+ indicates where valid compressed data was found. In the error case, the
+ application may repeatedly call inflateSync, providing more input each time,
+ until success or end of the input data.
+*/
+
+ZEXTERN int ZEXPORT inflateCopy OF((z_streamp dest,
+ z_streamp source));
+/*
+ Sets the destination stream as a complete copy of the source stream.
+
+ This function can be useful when randomly accessing a large stream. The
+ first pass through the stream can periodically record the inflate state,
+ allowing restarting inflate at those points when randomly accessing the
+ stream.
+
+ inflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not
+ enough memory, Z_STREAM_ERROR if the source stream state was inconsistent
+ (such as zalloc being NULL). msg is left unchanged in both source and
+ destination.
+*/
+
+ZEXTERN int ZEXPORT inflateReset OF((z_streamp strm));
+/*
+ This function is equivalent to inflateEnd followed by inflateInit,
+ but does not free and reallocate all the internal decompression state.
+ The stream will keep attributes that may have been set by inflateInit2.
+
+ inflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source
+ stream state was inconsistent (such as zalloc or state being NULL).
+*/
+
+/*
+ZEXTERN int ZEXPORT inflateBackInit OF((z_stream FAR *strm, int windowBits,
+ unsigned char FAR *window));
+
+ Initialize the internal stream state for decompression using inflateBack()
+ calls. The fields zalloc, zfree and opaque in strm must be initialized
+ before the call. If zalloc and zfree are Z_NULL, then the default library-
+ derived memory allocation routines are used. windowBits is the base two
+ logarithm of the window size, in the range 8..15. window is a caller
+ supplied buffer of that size. Except for special applications where it is
+ assured that deflate was used with small window sizes, windowBits must be 15
+ and a 32K byte window must be supplied to be able to decompress general
+ deflate streams.
+
+ See inflateBack() for the usage of these routines.
+
+ inflateBackInit will return Z_OK on success, Z_STREAM_ERROR if any of
+ the paramaters are invalid, Z_MEM_ERROR if the internal state could not
+ be allocated, or Z_VERSION_ERROR if the version of the library does not
+ match the version of the header file.
+*/
+
+typedef unsigned (*in_func) OF((void FAR *, unsigned char FAR * FAR *));
+typedef int (*out_func) OF((void FAR *, unsigned char FAR *, unsigned));
+
+ZEXTERN int ZEXPORT inflateBack OF((z_stream FAR *strm,
+ in_func in, void FAR *in_desc,
+ out_func out, void FAR *out_desc));
+/*
+ inflateBack() does a raw inflate with a single call using a call-back
+ interface for input and output. This is more efficient than inflate() for
+ file i/o applications in that it avoids copying between the output and the
+ sliding window by simply making the window itself the output buffer. This
+ function trusts the application to not change the output buffer passed by
+ the output function, at least until inflateBack() returns.
+
+ inflateBackInit() must be called first to allocate the internal state
+ and to initialize the state with the user-provided window buffer.
+ inflateBack() may then be used multiple times to inflate a complete, raw
+ deflate stream with each call. inflateBackEnd() is then called to free
+ the allocated state.
+
+ A raw deflate stream is one with no zlib or gzip header or trailer.
+ This routine would normally be used in a utility that reads zip or gzip
+ files and writes out uncompressed files. The utility would decode the
+ header and process the trailer on its own, hence this routine expects
+ only the raw deflate stream to decompress. This is different from the
+ normal behavior of inflate(), which expects either a zlib or gzip header and
+ trailer around the deflate stream.
+
+ inflateBack() uses two subroutines supplied by the caller that are then
+ called by inflateBack() for input and output. inflateBack() calls those
+ routines until it reads a complete deflate stream and writes out all of the
+ uncompressed data, or until it encounters an error. The function's
+ parameters and return types are defined above in the in_func and out_func
+ typedefs. inflateBack() will call in(in_desc, &buf) which should return the
+ number of bytes of provided input, and a pointer to that input in buf. If
+ there is no input available, in() must return zero--buf is ignored in that
+ case--and inflateBack() will return a buffer error. inflateBack() will call
+ out(out_desc, buf, len) to write the uncompressed data buf[0..len-1]. out()
+ should return zero on success, or non-zero on failure. If out() returns
+ non-zero, inflateBack() will return with an error. Neither in() nor out()
+ are permitted to change the contents of the window provided to
+ inflateBackInit(), which is also the buffer that out() uses to write from.
+ The length written by out() will be at most the window size. Any non-zero
+ amount of input may be provided by in().
+
+ For convenience, inflateBack() can be provided input on the first call by
+ setting strm->next_in and strm->avail_in. If that input is exhausted, then
+ in() will be called. Therefore strm->next_in must be initialized before
+ calling inflateBack(). If strm->next_in is Z_NULL, then in() will be called
+ immediately for input. If strm->next_in is not Z_NULL, then strm->avail_in
+ must also be initialized, and then if strm->avail_in is not zero, input will
+ initially be taken from strm->next_in[0 .. strm->avail_in - 1].
+
+ The in_desc and out_desc parameters of inflateBack() is passed as the
+ first parameter of in() and out() respectively when they are called. These
+ descriptors can be optionally used to pass any information that the caller-
+ supplied in() and out() functions need to do their job.
+
+ On return, inflateBack() will set strm->next_in and strm->avail_in to
+ pass back any unused input that was provided by the last in() call. The
+ return values of inflateBack() can be Z_STREAM_END on success, Z_BUF_ERROR
+ if in() or out() returned an error, Z_DATA_ERROR if there was a format
+ error in the deflate stream (in which case strm->msg is set to indicate the
+ nature of the error), or Z_STREAM_ERROR if the stream was not properly
+ initialized. In the case of Z_BUF_ERROR, an input or output error can be
+ distinguished using strm->next_in which will be Z_NULL only if in() returned
+ an error. If strm->next is not Z_NULL, then the Z_BUF_ERROR was due to
+ out() returning non-zero. (in() will always be called before out(), so
+ strm->next_in is assured to be defined if out() returns non-zero.) Note
+ that inflateBack() cannot return Z_OK.
+*/
+
+ZEXTERN int ZEXPORT inflateBackEnd OF((z_stream FAR *strm));
+/*
+ All memory allocated by inflateBackInit() is freed.
+
+ inflateBackEnd() returns Z_OK on success, or Z_STREAM_ERROR if the stream
+ state was inconsistent.
+*/
+
+ZEXTERN uLong ZEXPORT zlibCompileFlags OF((void));
+/* Return flags indicating compile-time options.
+
+ Type sizes, two bits each, 00 = 16 bits, 01 = 32, 10 = 64, 11 = other:
+ 1.0: size of uInt
+ 3.2: size of uLong
+ 5.4: size of voidpf (pointer)
+ 7.6: size of z_off_t
+
+ Compiler, assembler, and debug options:
+ 8: DEBUG
+ 9: ASMV or ASMINF -- use ASM code
+ 10: ZLIB_WINAPI -- exported functions use the WINAPI calling convention
+ 11: 0 (reserved)
+
+ One-time table building (smaller code, but not thread-safe if true):
+ 12: BUILDFIXED -- build static block decoding tables when needed
+ 13: DYNAMIC_CRC_TABLE -- build CRC calculation tables when needed
+ 14,15: 0 (reserved)
+
+ Library content (indicates missing functionality):
+ 16: NO_GZCOMPRESS -- gz* functions cannot compress (to avoid linking
+ deflate code when not needed)
+ 17: NO_GZIP -- deflate can't write gzip streams, and inflate can't detect
+ and decode gzip streams (to avoid linking crc code)
+ 18-19: 0 (reserved)
+
+ Operation variations (changes in library functionality):
+ 20: PKZIP_BUG_WORKAROUND -- slightly more permissive inflate
+ 21: FASTEST -- deflate algorithm with only one, lowest compression level
+ 22,23: 0 (reserved)
+
+ The sprintf variant used by gzprintf (zero is best):
+ 24: 0 = vs*, 1 = s* -- 1 means limited to 20 arguments after the format
+ 25: 0 = *nprintf, 1 = *printf -- 1 means gzprintf() not secure!
+ 26: 0 = returns value, 1 = void -- 1 means inferred string length returned
+
+ Remainder:
+ 27-31: 0 (reserved)
+ */
+
+
+ /* utility functions */
+
+/*
+ The following utility functions are implemented on top of the
+ basic stream-oriented functions. To simplify the interface, some
+ default options are assumed (compression level and memory usage,
+ standard memory allocation functions). The source code of these
+ utility functions can easily be modified if you need special options.
+*/
+
+ZEXTERN int ZEXPORT compress OF((Bytef *dest, uLongf *destLen,
+ const Bytef *source, uLong sourceLen));
+/*
+ Compresses the source buffer into the destination buffer. sourceLen is
+ the byte length of the source buffer. Upon entry, destLen is the total
+ size of the destination buffer, which must be at least the value returned
+ by compressBound(sourceLen). Upon exit, destLen is the actual size of the
+ compressed buffer.
+ This function can be used to compress a whole file at once if the
+ input file is mmap'ed.
+ compress returns Z_OK if success, Z_MEM_ERROR if there was not
+ enough memory, Z_BUF_ERROR if there was not enough room in the output
+ buffer.
+*/
+
+ZEXTERN int ZEXPORT compress2 OF((Bytef *dest, uLongf *destLen,
+ const Bytef *source, uLong sourceLen,
+ int level));
+/*
+ Compresses the source buffer into the destination buffer. The level
+ parameter has the same meaning as in deflateInit. sourceLen is the byte
+ length of the source buffer. Upon entry, destLen is the total size of the
+ destination buffer, which must be at least the value returned by
+ compressBound(sourceLen). Upon exit, destLen is the actual size of the
+ compressed buffer.
+
+ compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough
+ memory, Z_BUF_ERROR if there was not enough room in the output buffer,
+ Z_STREAM_ERROR if the level parameter is invalid.
+*/
+
+ZEXTERN uLong ZEXPORT compressBound OF((uLong sourceLen));
+/*
+ compressBound() returns an upper bound on the compressed size after
+ compress() or compress2() on sourceLen bytes. It would be used before
+ a compress() or compress2() call to allocate the destination buffer.
+*/
+
+ZEXTERN int ZEXPORT uncompress OF((Bytef *dest, uLongf *destLen,
+ const Bytef *source, uLong sourceLen));
+/*
+ Decompresses the source buffer into the destination buffer. sourceLen is
+ the byte length of the source buffer. Upon entry, destLen is the total
+ size of the destination buffer, which must be large enough to hold the
+ entire uncompressed data. (The size of the uncompressed data must have
+ been saved previously by the compressor and transmitted to the decompressor
+ by some mechanism outside the scope of this compression library.)
+ Upon exit, destLen is the actual size of the compressed buffer.
+ This function can be used to decompress a whole file at once if the
+ input file is mmap'ed.
+
+ uncompress returns Z_OK if success, Z_MEM_ERROR if there was not
+ enough memory, Z_BUF_ERROR if there was not enough room in the output
+ buffer, or Z_DATA_ERROR if the input data was corrupted or incomplete.
+*/
+
+
+typedef voidp gzFile;
+
+ZEXTERN gzFile ZEXPORT gzopen OF((const char *path, const char *mode));
+/*
+ Opens a gzip (.gz) file for reading or writing. The mode parameter
+ is as in fopen ("rb" or "wb") but can also include a compression level
+ ("wb9") or a strategy: 'f' for filtered data as in "wb6f", 'h' for
+ Huffman only compression as in "wb1h", or 'R' for run-length encoding
+ as in "wb1R". (See the description of deflateInit2 for more information
+ about the strategy parameter.)
+
+ gzopen can be used to read a file which is not in gzip format; in this
+ case gzread will directly read from the file without decompression.
+
+ gzopen returns NULL if the file could not be opened or if there was
+ insufficient memory to allocate the (de)compression state; errno
+ can be checked to distinguish the two cases (if errno is zero, the
+ zlib error is Z_MEM_ERROR). */
+
+ZEXTERN gzFile ZEXPORT gzdopen OF((int fd, const char *mode));
+/*
+ gzdopen() associates a gzFile with the file descriptor fd. File
+ descriptors are obtained from calls like open, dup, creat, pipe or
+ fileno (in the file has been previously opened with fopen).
+ The mode parameter is as in gzopen.
+ The next call of gzclose on the returned gzFile will also close the
+ file descriptor fd, just like fclose(fdopen(fd), mode) closes the file
+ descriptor fd. If you want to keep fd open, use gzdopen(dup(fd), mode).
+ gzdopen returns NULL if there was insufficient memory to allocate
+ the (de)compression state.
+*/
+
+ZEXTERN int ZEXPORT gzsetparams OF((gzFile file, int level, int strategy));
+/*
+ Dynamically update the compression level or strategy. See the description
+ of deflateInit2 for the meaning of these parameters.
+ gzsetparams returns Z_OK if success, or Z_STREAM_ERROR if the file was not
+ opened for writing.
+*/
+
+ZEXTERN int ZEXPORT gzread OF((gzFile file, voidp buf, unsigned len));
+/*
+ Reads the given number of uncompressed bytes from the compressed file.
+ If the input file was not in gzip format, gzread copies the given number
+ of bytes into the buffer.
+ gzread returns the number of uncompressed bytes actually read (0 for
+ end of file, -1 for error). */
+
+ZEXTERN int ZEXPORT gzwrite OF((gzFile file,
+ voidpc buf, unsigned len));
+/*
+ Writes the given number of uncompressed bytes into the compressed file.
+ gzwrite returns the number of uncompressed bytes actually written
+ (0 in case of error).
+*/
+
+ZEXTERN int ZEXPORTVA gzprintf OF((gzFile file, const char *format, ...));
+/*
+ Converts, formats, and writes the args to the compressed file under
+ control of the format string, as in fprintf. gzprintf returns the number of
+ uncompressed bytes actually written (0 in case of error). The number of
+ uncompressed bytes written is limited to 4095. The caller should assure that
+ this limit is not exceeded. If it is exceeded, then gzprintf() will return
+ return an error (0) with nothing written. In this case, there may also be a
+ buffer overflow with unpredictable consequences, which is possible only if
+ zlib was compiled with the insecure functions sprintf() or vsprintf()
+ because the secure snprintf() or vsnprintf() functions were not available.
+*/
+
+ZEXTERN int ZEXPORT gzputs OF((gzFile file, const char *s));
+/*
+ Writes the given null-terminated string to the compressed file, excluding
+ the terminating null character.
+ gzputs returns the number of characters written, or -1 in case of error.
+*/
+
+ZEXTERN char * ZEXPORT gzgets OF((gzFile file, char *buf, int len));
+/*
+ Reads bytes from the compressed file until len-1 characters are read, or
+ a newline character is read and transferred to buf, or an end-of-file
+ condition is encountered. The string is then terminated with a null
+ character.
+ gzgets returns buf, or Z_NULL in case of error.
+*/
+
+ZEXTERN int ZEXPORT gzputc OF((gzFile file, int c));
+/*
+ Writes c, converted to an unsigned char, into the compressed file.
+ gzputc returns the value that was written, or -1 in case of error.
+*/
+
+ZEXTERN int ZEXPORT gzgetc OF((gzFile file));
+/*
+ Reads one byte from the compressed file. gzgetc returns this byte
+ or -1 in case of end of file or error.
+*/
+
+ZEXTERN int ZEXPORT gzungetc OF((int c, gzFile file));
+/*
+ Push one character back onto the stream to be read again later.
+ Only one character of push-back is allowed. gzungetc() returns the
+ character pushed, or -1 on failure. gzungetc() will fail if a
+ character has been pushed but not read yet, or if c is -1. The pushed
+ character will be discarded if the stream is repositioned with gzseek()
+ or gzrewind().
+*/
+
+ZEXTERN int ZEXPORT gzflush OF((gzFile file, int flush));
+/*
+ Flushes all pending output into the compressed file. The parameter
+ flush is as in the deflate() function. The return value is the zlib
+ error number (see function gzerror below). gzflush returns Z_OK if
+ the flush parameter is Z_FINISH and all output could be flushed.
+ gzflush should be called only when strictly necessary because it can
+ degrade compression.
+*/
+
+ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile file,
+ z_off_t offset, int whence));
+/*
+ Sets the starting position for the next gzread or gzwrite on the
+ given compressed file. The offset represents a number of bytes in the
+ uncompressed data stream. The whence parameter is defined as in lseek(2);
+ the value SEEK_END is not supported.
+ If the file is opened for reading, this function is emulated but can be
+ extremely slow. If the file is opened for writing, only forward seeks are
+ supported; gzseek then compresses a sequence of zeroes up to the new
+ starting position.
+
+ gzseek returns the resulting offset location as measured in bytes from
+ the beginning of the uncompressed stream, or -1 in case of error, in
+ particular if the file is opened for writing and the new starting position
+ would be before the current position.
+*/
+
+ZEXTERN int ZEXPORT gzrewind OF((gzFile file));
+/*
+ Rewinds the given file. This function is supported only for reading.
+
+ gzrewind(file) is equivalent to (int)gzseek(file, 0L, SEEK_SET)
+*/
+
+ZEXTERN z_off_t ZEXPORT gztell OF((gzFile file));
+/*
+ Returns the starting position for the next gzread or gzwrite on the
+ given compressed file. This position represents a number of bytes in the
+ uncompressed data stream.
+
+ gztell(file) is equivalent to gzseek(file, 0L, SEEK_CUR)
+*/
+
+ZEXTERN int ZEXPORT gzeof OF((gzFile file));
+/*
+ Returns 1 when EOF has previously been detected reading the given
+ input stream, otherwise zero.
+*/
+
+ZEXTERN int ZEXPORT gzclose OF((gzFile file));
+/*
+ Flushes all pending output if necessary, closes the compressed file
+ and deallocates all the (de)compression state. The return value is the zlib
+ error number (see function gzerror below).
+*/
+
+ZEXTERN const char * ZEXPORT gzerror OF((gzFile file, int *errnum));
+/*
+ Returns the error message for the last error which occurred on the
+ given compressed file. errnum is set to zlib error number. If an
+ error occurred in the file system and not in the compression library,
+ errnum is set to Z_ERRNO and the application may consult errno
+ to get the exact error code.
+*/
+
+ZEXTERN void ZEXPORT gzclearerr OF((gzFile file));
+/*
+ Clears the error and end-of-file flags for file. This is analogous to the
+ clearerr() function in stdio. This is useful for continuing to read a gzip
+ file that is being written concurrently.
+*/
+
+ /* checksum functions */
+
+/*
+ These functions are not related to compression but are exported
+ anyway because they might be useful in applications using the
+ compression library.
+*/
+
+ZEXTERN uLong ZEXPORT adler32 OF((uLong adler, const Bytef *buf, uInt len));
+
+/*
+ Update a running Adler-32 checksum with the bytes buf[0..len-1] and
+ return the updated checksum. If buf is NULL, this function returns
+ the required initial value for the checksum.
+ An Adler-32 checksum is almost as reliable as a CRC32 but can be computed
+ much faster. Usage example:
+
+ uLong adler = adler32(0L, Z_NULL, 0);
+
+ while (read_buffer(buffer, length) != EOF) {
+ adler = adler32(adler, buffer, length);
+ }
+ if (adler != original_adler) error();
+*/
+
+ZEXTERN uLong ZEXPORT crc32 OF((uLong crc, const Bytef *buf, uInt len));
+/*
+ Update a running crc with the bytes buf[0..len-1] and return the updated
+ crc. If buf is NULL, this function returns the required initial value
+ for the crc. Pre- and post-conditioning (one's complement) is performed
+ within this function so it shouldn't be done by the application.
+ Usage example:
+
+ uLong crc = crc32(0L, Z_NULL, 0);
+
+ while (read_buffer(buffer, length) != EOF) {
+ crc = crc32(crc, buffer, length);
+ }
+ if (crc != original_crc) error();
+*/
+
+
+ /* various hacks, don't look :) */
+
+/* deflateInit and inflateInit are macros to allow checking the zlib version
+ * and the compiler's view of z_stream:
+ */
+ZEXTERN int ZEXPORT deflateInit_ OF((z_streamp strm, int level,
+ const char *version, int stream_size));
+ZEXTERN int ZEXPORT inflateInit_ OF((z_streamp strm,
+ const char *version, int stream_size));
+ZEXTERN int ZEXPORT deflateInit2_ OF((z_streamp strm, int level, int method,
+ int windowBits, int memLevel,
+ int strategy, const char *version,
+ int stream_size));
+ZEXTERN int ZEXPORT inflateInit2_ OF((z_streamp strm, int windowBits,
+ const char *version, int stream_size));
+ZEXTERN int ZEXPORT inflateBackInit_ OF((z_stream FAR *strm, int windowBits,
+ unsigned char FAR *window,
+ const char *version,
+ int stream_size));
+#define deflateInit(strm, level) \
+ deflateInit_((strm), (level), ZLIB_VERSION, sizeof(z_stream))
+#define inflateInit(strm) \
+ inflateInit_((strm), ZLIB_VERSION, sizeof(z_stream))
+#define deflateInit2(strm, level, method, windowBits, memLevel, strategy) \
+ deflateInit2_((strm),(level),(method),(windowBits),(memLevel),\
+ (strategy), ZLIB_VERSION, sizeof(z_stream))
+#define inflateInit2(strm, windowBits) \
+ inflateInit2_((strm), (windowBits), ZLIB_VERSION, sizeof(z_stream))
+#define inflateBackInit(strm, windowBits, window) \
+ inflateBackInit_((strm), (windowBits), (window), \
+ ZLIB_VERSION, sizeof(z_stream))
+
+
+#if !defined(ZUTIL_H) && !defined(NO_DUMMY_DECL)
+ struct internal_state {int dummy;}; /* hack for buggy compilers */
+#endif
+
+ZEXTERN const char * ZEXPORT zError OF((int err));
+ZEXTERN int ZEXPORT inflateSyncPoint OF((z_streamp z));
+ZEXTERN const uLongf * ZEXPORT get_crc_table OF((void));
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ZLIB_H */
diff --git a/src/zlib/zutil.c b/src/zlib/zutil.c
new file mode 100755
index 0000000..32a3b2b
--- /dev/null
+++ b/src/zlib/zutil.c
@@ -0,0 +1,319 @@
+/* zutil.c -- target dependent utility functions for the compression library
+ * Copyright (C) 1995-2003 Jean-loup Gailly.
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* @(#) $ Id$ */
+
+#include "zutil.h"
+
+#ifndef NO_DUMMY_DECL
+struct internal_state {int dummy;}; /* for buggy compilers */
+#endif
+
+#ifndef STDC
+extern void exit OF((int));
+#endif
+
+const char * const z_errmsg[10] = {
+"need dictionary", /* Z_NEED_DICT 2 */
+"stream end", /* Z_STREAM_END 1 */
+"", /* Z_OK 0 */
+"file error", /* Z_ERRNO (-1) */
+"stream error", /* Z_STREAM_ERROR (-2) */
+"data error", /* Z_DATA_ERROR (-3) */
+"insufficient memory", /* Z_MEM_ERROR (-4) */
+"buffer error", /* Z_BUF_ERROR (-5) */
+"incompatible version",/* Z_VERSION_ERROR (-6) */
+""};
+
+
+const char * ZEXPORT zlibVersion()
+{
+ return ZLIB_VERSION;
+}
+
+uLong ZEXPORT zlibCompileFlags()
+{
+ uLong flags;
+
+ flags = 0;
+ switch (sizeof(uInt)) {
+ case 2: break;
+ case 4: flags += 1; break;
+ case 8: flags += 2; break;
+ default: flags += 3;
+ }
+ switch (sizeof(uLong)) {
+ case 2: break;
+ case 4: flags += 1 << 2; break;
+ case 8: flags += 2 << 2; break;
+ default: flags += 3 << 2;
+ }
+ switch (sizeof(voidpf)) {
+ case 2: break;
+ case 4: flags += 1 << 4; break;
+ case 8: flags += 2 << 4; break;
+ default: flags += 3 << 4;
+ }
+ switch (sizeof(z_off_t)) {
+ case 2: break;
+ case 4: flags += 1 << 6; break;
+ case 8: flags += 2 << 6; break;
+ default: flags += 3 << 6;
+ }
+#ifdef DEBUG
+ flags += 1 << 8;
+#endif
+#if defined(ASMV) || defined(ASMINF)
+ flags += 1 << 9;
+#endif
+#ifdef ZLIB_WINAPI
+ flags += 1 << 10;
+#endif
+#ifdef BUILDFIXED
+ flags += 1 << 12;
+#endif
+#ifdef DYNAMIC_CRC_TABLE
+ flags += 1 << 13;
+#endif
+#ifdef NO_GZCOMPRESS
+ flags += 1 << 16;
+#endif
+#ifdef NO_GZIP
+ flags += 1 << 17;
+#endif
+#ifdef PKZIP_BUG_WORKAROUND
+ flags += 1 << 20;
+#endif
+#ifdef FASTEST
+ flags += 1 << 21;
+#endif
+#ifdef STDC
+# ifdef NO_vsnprintf
+ flags += 1 << 25;
+# ifdef HAS_vsprintf_void
+ flags += 1 << 26;
+# endif
+# else
+# ifdef HAS_vsnprintf_void
+ flags += 1 << 26;
+# endif
+# endif
+#else
+ flags += 1 << 24;
+# ifdef NO_snprintf
+ flags += 1 << 25;
+# ifdef HAS_sprintf_void
+ flags += 1 << 26;
+# endif
+# else
+# ifdef HAS_snprintf_void
+ flags += 1 << 26;
+# endif
+# endif
+#endif
+ return flags;
+}
+
+#ifdef DEBUG
+
+# ifndef verbose
+# define verbose 0
+# endif
+int z_verbose = verbose;
+
+void z_error (m)
+ char *m;
+{
+ fprintf(stderr, "%s\n", m);
+ exit(1);
+}
+#endif
+
+/* exported to allow conversion of error code to string for compress() and
+ * uncompress()
+ */
+const char * ZEXPORT zError(err)
+ int err;
+{
+ return ERR_MSG(err);
+}
+
+#if defined(_WIN32_WCE)
+ /* does not exist on WCE */
+ int errno = 0;
+#endif
+
+#ifndef HAVE_MEMCPY
+
+void zmemcpy(dest, source, len)
+ Bytef* dest;
+ const Bytef* source;
+ uInt len;
+{
+ if (len == 0) return;
+ do {
+ *dest++ = *source++; /* ??? to be unrolled */
+ } while (--len != 0);
+}
+
+int zmemcmp(s1, s2, len)
+ const Bytef* s1;
+ const Bytef* s2;
+ uInt len;
+{
+ uInt j;
+
+ for (j = 0; j < len; j++) {
+ if (s1[j] != s2[j]) return 2*(s1[j] > s2[j])-1;
+ }
+ return 0;
+}
+
+void zmemzero(dest, len)
+ Bytef* dest;
+ uInt len;
+{
+ if (len == 0) return;
+ do {
+ *dest++ = 0; /* ??? to be unrolled */
+ } while (--len != 0);
+}
+#endif
+
+
+#ifdef SYS16BIT
+
+#ifdef __TURBOC__
+/* Turbo C in 16-bit mode */
+
+# define MY_ZCALLOC
+
+/* Turbo C malloc() does not allow dynamic allocation of 64K bytes
+ * and farmalloc(64K) returns a pointer with an offset of 8, so we
+ * must fix the pointer. Warning: the pointer must be put back to its
+ * original form in order to free it, use zcfree().
+ */
+
+#define MAX_PTR 10
+/* 10*64K = 640K */
+
+local int next_ptr = 0;
+
+typedef struct ptr_table_s {
+ voidpf org_ptr;
+ voidpf new_ptr;
+} ptr_table;
+
+local ptr_table table[MAX_PTR];
+/* This table is used to remember the original form of pointers
+ * to large buffers (64K). Such pointers are normalized with a zero offset.
+ * Since MSDOS is not a preemptive multitasking OS, this table is not
+ * protected from concurrent access. This hack doesn't work anyway on
+ * a protected system like OS/2. Use Microsoft C instead.
+ */
+
+voidpf zcalloc (voidpf opaque, unsigned items, unsigned size)
+{
+ voidpf buf = opaque; /* just to make some compilers happy */
+ ulg bsize = (ulg)items*size;
+
+ /* If we allocate less than 65520 bytes, we assume that farmalloc
+ * will return a usable pointer which doesn't have to be normalized.
+ */
+ if (bsize < 65520L) {
+ buf = farmalloc(bsize);
+ if (*(ush*)&buf != 0) return buf;
+ } else {
+ buf = farmalloc(bsize + 16L);
+ }
+ if (buf == NULL || next_ptr >= MAX_PTR) return NULL;
+ table[next_ptr].org_ptr = buf;
+
+ /* Normalize the pointer to seg:0 */
+ *((ush*)&buf+1) += ((ush)((uch*)buf-0) + 15) >> 4;
+ *(ush*)&buf = 0;
+ table[next_ptr++].new_ptr = buf;
+ return buf;
+}
+
+void zcfree (voidpf opaque, voidpf ptr)
+{
+ int n;
+ if (*(ush*)&ptr != 0) { /* object < 64K */
+ farfree(ptr);
+ return;
+ }
+ /* Find the original pointer */
+ for (n = 0; n < next_ptr; n++) {
+ if (ptr != table[n].new_ptr) continue;
+
+ farfree(table[n].org_ptr);
+ while (++n < next_ptr) {
+ table[n-1] = table[n];
+ }
+ next_ptr--;
+ return;
+ }
+ ptr = opaque; /* just to make some compilers happy */
+ Assert(0, "zcfree: ptr not found");
+}
+
+#endif /* __TURBOC__ */
+
+
+#ifdef M_I86
+/* Microsoft C in 16-bit mode */
+
+# define MY_ZCALLOC
+
+#if (!defined(_MSC_VER) || (_MSC_VER <= 600))
+# define _halloc halloc
+# define _hfree hfree
+#endif
+
+voidpf zcalloc (voidpf opaque, unsigned items, unsigned size)
+{
+ if (opaque) opaque = 0; /* to make compiler happy */
+ return _halloc((long)items, size);
+}
+
+void zcfree (voidpf opaque, voidpf ptr)
+{
+ if (opaque) opaque = 0; /* to make compiler happy */
+ _hfree(ptr);
+}
+
+#endif /* M_I86 */
+
+#endif /* SYS16BIT */
+
+
+#ifndef MY_ZCALLOC /* Any system without a special alloc function */
+
+#ifndef STDC
+extern voidp malloc OF((uInt size));
+extern voidp calloc OF((uInt items, uInt size));
+extern void free OF((voidpf ptr));
+#endif
+
+voidpf zcalloc (opaque, items, size)
+ voidpf opaque;
+ unsigned items;
+ unsigned size;
+{
+ if (opaque) items += size - size; /* make compiler happy */
+ return sizeof(uInt) > 2 ? (voidpf)malloc(items * size) :
+ (voidpf)calloc(items, size);
+}
+
+void zcfree (opaque, ptr)
+ voidpf opaque;
+ voidpf ptr;
+{
+ free(ptr);
+ if (opaque) return; /* make compiler happy */
+}
+
+#endif /* MY_ZCALLOC */
diff --git a/src/zlib/zutil.h b/src/zlib/zutil.h
new file mode 100755
index 0000000..e682954
--- /dev/null
+++ b/src/zlib/zutil.h
@@ -0,0 +1,258 @@
+/* zutil.h -- internal interface and configuration of the compression library
+ * Copyright (C) 1995-2003 Jean-loup Gailly.
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* WARNING: this file should *not* be used by applications. It is
+ part of the implementation of the compression library and is
+ subject to change. Applications should only use zlib.h.
+ */
+
+/* @(#) $ Id$ */
+
+#ifndef ZUTIL_H
+#define ZUTIL_H
+
+#define ZLIB_INTERNAL
+#include "zlib.h"
+
+#ifdef STDC
+# include <stddef.h>
+# include <string.h>
+# include <stdlib.h>
+#endif
+#ifdef NO_ERRNO_H
+ extern int errno;
+#else
+# include <errno.h>
+#endif
+
+#ifndef local
+# define local static
+#endif
+/* compile with -Dlocal if your debugger can't find static symbols */
+
+typedef unsigned char uch;
+typedef uch FAR uchf;
+typedef unsigned short ush;
+typedef ush FAR ushf;
+typedef unsigned long ulg;
+
+extern const char * const z_errmsg[10]; /* indexed by 2-zlib_error */
+/* (size given to avoid silly warnings with Visual C++) */
+
+#define ERR_MSG(err) z_errmsg[Z_NEED_DICT-(err)]
+
+#define ERR_RETURN(strm,err) \
+ return (strm->msg = (char*)ERR_MSG(err), (err))
+/* To be used only when the state is known to be valid */
+
+ /* common constants */
+
+#ifndef DEF_WBITS
+# define DEF_WBITS MAX_WBITS
+#endif
+/* default windowBits for decompression. MAX_WBITS is for compression only */
+
+#if MAX_MEM_LEVEL >= 8
+# define DEF_MEM_LEVEL 8
+#else
+# define DEF_MEM_LEVEL MAX_MEM_LEVEL
+#endif
+/* default memLevel */
+
+#define STORED_BLOCK 0
+#define STATIC_TREES 1
+#define DYN_TREES 2
+/* The three kinds of block type */
+
+#define MIN_MATCH 3
+#define MAX_MATCH 258
+/* The minimum and maximum match lengths */
+
+#define PRESET_DICT 0x20 /* preset dictionary flag in zlib header */
+
+ /* target dependencies */
+
+#if defined(MSDOS) || (defined(WINDOWS) && !defined(WIN32))
+# define OS_CODE 0x00
+# if defined(__TURBOC__) || defined(__BORLANDC__)
+# if(__STDC__ == 1) && (defined(__LARGE__) || defined(__COMPACT__))
+ /* Allow compilation with ANSI keywords only enabled */
+ void _Cdecl farfree( void *block );
+ void *_Cdecl farmalloc( unsigned long nbytes );
+# else
+# include <alloc.h>
+# endif
+# else /* MSC or DJGPP */
+# include <malloc.h>
+# endif
+#endif
+
+#ifdef AMIGA
+# define OS_CODE 0x01
+#endif
+
+#if defined(VAXC) || defined(VMS)
+# define OS_CODE 0x02
+# define F_OPEN(name, mode) \
+ fopen((name), (mode), "mbc=60", "ctx=stm", "rfm=fix", "mrs=512")
+#endif
+
+#if defined(ATARI) || defined(atarist)
+# define OS_CODE 0x05
+#endif
+
+#ifdef OS2
+# define OS_CODE 0x06
+#endif
+
+#if defined(MACOS) || defined(TARGET_OS_MAC)
+# define OS_CODE 0x07
+# if defined(__MWERKS__) && __dest_os != __be_os && __dest_os != __win32_os
+# include <unix.h> /* for fdopen */
+# else
+# ifndef fdopen
+# define fdopen(fd,mode) NULL /* No fdopen() */
+# endif
+# endif
+#endif
+
+#ifdef TOPS20
+# define OS_CODE 0x0a
+#endif
+
+#ifdef WIN32
+# ifndef __CYGWIN__ /* Cygwin is Unix, not Win32 */
+# define OS_CODE 0x0b
+# endif
+#endif
+
+#ifdef __50SERIES /* Prime/PRIMOS */
+# define OS_CODE 0x0f
+#endif
+
+#if defined(_BEOS_) || defined(RISCOS)
+# define fdopen(fd,mode) NULL /* No fdopen() */
+#endif
+
+#if (defined(_MSC_VER) && (_MSC_VER > 600))
+# if defined(_WIN32_WCE)
+# define fdopen(fd,mode) NULL /* No fdopen() */
+# ifndef _PTRDIFF_T_DEFINED
+ typedef int ptrdiff_t;
+# define _PTRDIFF_T_DEFINED
+# endif
+# else
+# define fdopen(fd,type) _fdopen(fd,type)
+# endif
+#endif
+
+ /* common defaults */
+
+#ifndef OS_CODE
+# define OS_CODE 0x03 /* assume Unix */
+#endif
+
+#ifndef F_OPEN
+# define F_OPEN(name, mode) fopen((name), (mode))
+#endif
+
+ /* functions */
+
+#if defined(STDC99) || (defined(__TURBOC__) && __TURBOC__ >= 0x550)
+# ifndef HAVE_VSNPRINTF
+# define HAVE_VSNPRINTF
+# endif
+#endif
+#if defined(__CYGWIN__)
+# ifndef HAVE_VSNPRINTF
+# define HAVE_VSNPRINTF
+# endif
+#endif
+#ifndef HAVE_VSNPRINTF
+# ifdef MSDOS
+ /* vsnprintf may exist on some MS-DOS compilers (DJGPP?),
+ but for now we just assume it doesn't. */
+# define NO_vsnprintf
+# endif
+# ifdef __TURBOC__
+# define NO_vsnprintf
+# endif
+# ifdef WIN32
+ /* In Win32, vsnprintf is available as the "non-ANSI" _vsnprintf. */
+# if !defined(vsnprintf) && !defined(NO_vsnprintf)
+# define vsnprintf _vsnprintf
+# endif
+# endif
+# ifdef __SASC
+# define NO_vsnprintf
+# endif
+#endif
+
+#ifdef HAVE_STRERROR
+ extern char *strerror OF((int));
+# define zstrerror(errnum) strerror(errnum)
+#else
+# define zstrerror(errnum) ""
+#endif
+
+#if defined(pyr)
+# define NO_MEMCPY
+#endif
+#if defined(SMALL_MEDIUM) && !defined(_MSC_VER) && !defined(__SC__)
+ /* Use our own functions for small and medium model with MSC <= 5.0.
+ * You may have to use the same strategy for Borland C (untested).
+ * The __SC__ check is for Symantec.
+ */
+# define NO_MEMCPY
+#endif
+#if defined(STDC) && !defined(HAVE_MEMCPY) && !defined(NO_MEMCPY)
+# define HAVE_MEMCPY
+#endif
+#ifdef HAVE_MEMCPY
+# ifdef SMALL_MEDIUM /* MSDOS small or medium model */
+# define zmemcpy _fmemcpy
+# define zmemcmp _fmemcmp
+# define zmemzero(dest, len) _fmemset(dest, 0, len)
+# else
+# define zmemcpy memcpy
+# define zmemcmp memcmp
+# define zmemzero(dest, len) memset(dest, 0, len)
+# endif
+#else
+ extern void zmemcpy OF((Bytef* dest, const Bytef* source, uInt len));
+ extern int zmemcmp OF((const Bytef* s1, const Bytef* s2, uInt len));
+ extern void zmemzero OF((Bytef* dest, uInt len));
+#endif
+
+/* Diagnostic functions */
+#ifdef DEBUG
+# include <stdio.h>
+ extern int z_verbose;
+ extern void z_error OF((char *m));
+# define Assert(cond,msg) {if(!(cond)) z_error(msg);}
+# define Trace(x) {if (z_verbose>=0) fprintf x ;}
+# define Tracev(x) {if (z_verbose>0) fprintf x ;}
+# define Tracevv(x) {if (z_verbose>1) fprintf x ;}
+# define Tracec(c,x) {if (z_verbose>0 && (c)) fprintf x ;}
+# define Tracecv(c,x) {if (z_verbose>1 && (c)) fprintf x ;}
+#else
+# define Assert(cond,msg)
+# define Trace(x)
+# define Tracev(x)
+# define Tracevv(x)
+# define Tracec(c,x)
+# define Tracecv(c,x)
+#endif
+
+
+voidpf zcalloc OF((voidpf opaque, unsigned items, unsigned size));
+void zcfree OF((voidpf opaque, voidpf ptr));
+
+#define ZALLOC(strm, items, size) \
+ (*((strm)->zalloc))((strm)->opaque, (items), (size))
+#define ZFREE(strm, addr) (*((strm)->zfree))((strm)->opaque, (voidpf)(addr))
+#define TRY_FREE(s, p) {if (p) ZFREE(s, p);}
+
+#endif /* ZUTIL_H */